about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.envrc17
-rw-r--r--.gcroots/.skip-subtree1
-rw-r--r--.git-blame-ignore-revs3
-rw-r--r--.gitignore6
-rw-r--r--.rustfmt.toml22
-rw-r--r--LICENSE2
-rw-r--r--OWNERS13
-rw-r--r--README.md17
-rwxr-xr-xbin/__dispatch.sh81
l---------bin/age1
l---------bin/age-keygen1
l---------bin/depot-build1
l---------bin/depot-nixpkgs-update1
l---------bin/depotfmt1
l---------bin/gerrit1
l---------bin/gerrit-update1
l---------bin/hash-password1
l---------bin/kontemplate1
l---------bin/meson1
l---------bin/mg1
l---------bin/ninja1
l---------bin/nint1
l---------bin/perf-flamegraph1
l---------bin/rebuild-system1
l---------bin/rink1
l---------bin/stern1
l---------bin/tf-glesys1
l---------bin/tf-keycloak1
-rw-r--r--buf.gen.yaml8
-rw-r--r--buf.yaml11
-rw-r--r--corp/LICENSE2
-rw-r--r--corp/OWNERS6
-rw-r--r--corp/ops/.envrc4
-rw-r--r--corp/ops/.gitignore4
-rw-r--r--corp/ops/default.nix37
-rw-r--r--corp/ops/modules/.skip-tree1
-rw-r--r--corp/ops/yandex/creds.fish5
-rw-r--r--corp/ops/yandex/encrypted-state-secret.keybin0 -> 121 bytes
-rw-r--r--corp/ops/yandex/main.tf70
-rw-r--r--corp/ops/yandex/rih.tf307
-rw-r--r--corp/rih/.gitignore3
-rw-r--r--corp/rih/README.md3
-rw-r--r--corp/rih/backend/Cargo.lock1315
-rw-r--r--corp/rih/backend/Cargo.toml25
-rw-r--r--corp/rih/backend/default.nix16
-rw-r--r--corp/rih/backend/src/main.rs168
-rw-r--r--corp/rih/backend/src/yandex_log.rs47
-rw-r--r--corp/rih/frontend/Cargo.lock1825
-rw-r--r--corp/rih/frontend/Cargo.toml42
-rw-r--r--corp/rih/frontend/default.nix52
-rw-r--r--corp/rih/frontend/fonts/IdealistSans.eotbin0 -> 229248 bytes
-rw-r--r--corp/rih/frontend/fonts/IdealistSans.svg10438
-rw-r--r--corp/rih/frontend/fonts/IdealistSans.ttfbin0 -> 229072 bytes
-rw-r--r--corp/rih/frontend/fonts/IdealistSans.woffbin0 -> 93520 bytes
-rw-r--r--corp/rih/frontend/fonts/IdealistSans.woff2bin0 -> 65404 bytes
-rw-r--r--corp/rih/frontend/fonts/futurabookc.eotbin0 -> 22434 bytes
-rw-r--r--corp/rih/frontend/fonts/futurabookc.svg992
-rw-r--r--corp/rih/frontend/fonts/futurabookc.ttfbin0 -> 22196 bytes
-rw-r--r--corp/rih/frontend/fonts/futurabookc.woffbin0 -> 13092 bytes
-rw-r--r--corp/rih/frontend/fonts/futurabookc.woff2bin0 -> 9552 bytes
-rw-r--r--corp/rih/frontend/img/fon.pngbin0 -> 95122 bytes
-rw-r--r--corp/rih/frontend/img/it.pngbin0 -> 397645 bytes
-rw-r--r--corp/rih/frontend/img/mat.pngbin0 -> 296697 bytes
-rw-r--r--corp/rih/frontend/img/mat2.pngbin0 -> 882101 bytes
-rw-r--r--corp/rih/frontend/img/rus.pngbin0 -> 51952 bytes
-rw-r--r--corp/rih/frontend/img/work.pngbin0 -> 343895 bytes
-rw-r--r--corp/rih/frontend/index.css248
-rw-r--r--corp/rih/frontend/index.html21
-rw-r--r--corp/rih/frontend/privacy-policy.html202
-rw-r--r--corp/rih/frontend/rih-logo.pngbin0 -> 420956 bytes
-rw-r--r--corp/rih/frontend/src/home.html289
-rw-r--r--corp/rih/frontend/src/main.rs512
-rw-r--r--corp/rih/frontend/src/privacy-policy.md100
-rw-r--r--corp/rih/frontend/static-markdown/Cargo.toml12
-rw-r--r--corp/rih/frontend/static-markdown/src/lib.rs27
-rw-r--r--corp/russian/README.md9
-rw-r--r--corp/russian/data-import/.gitignore2
-rw-r--r--corp/russian/data-import/Cargo.lock499
-rw-r--r--corp/russian/data-import/Cargo.toml18
-rw-r--r--corp/russian/data-import/default.nix55
-rw-r--r--corp/russian/data-import/src/db_setup.rs298
-rw-r--r--corp/russian/data-import/src/main.rs298
-rw-r--r--corp/russian/data-import/src/mappings.rs185
-rw-r--r--corp/russian/data-import/src/oc_parser.rs470
-rw-r--r--corp/russian/data-import/src/or_parser.rs105
-rw-r--r--corp/russian/predlozhnik/.gitignore3
-rw-r--r--corp/russian/predlozhnik/Cargo.lock471
-rw-r--r--corp/russian/predlozhnik/Cargo.toml12
-rw-r--r--corp/russian/predlozhnik/default.nix52
-rw-r--r--corp/russian/predlozhnik/index.css29
-rw-r--r--corp/russian/predlozhnik/index.html24
-rw-r--r--corp/russian/predlozhnik/src/main.rs345
-rw-r--r--corp/website/content-en.md94
-rw-r--r--corp/website/content-ru.md98
-rw-r--r--corp/website/content.md26
-rw-r--r--corp/website/default.nix41
-rw-r--r--default.nix62
-rw-r--r--docs/CONTRIBUTING.md6
-rw-r--r--docs/REVIEWS.md89
-rw-r--r--docs/designs/SPARSE_CHECKOUTS.md6
-rw-r--r--fun/defer_rs/examples/defer-with-error.rs12
-rw-r--r--fun/defer_rs/examples/defer.rs6
-rw-r--r--fun/defer_rs/examples/undefer.rs4
-rw-r--r--fun/gemma/default.nix10
-rw-r--r--fun/idual/default.nix11
-rw-r--r--fun/owothia/default.nix5
-rw-r--r--fun/owothia/pkg.nix26
-rw-r--r--fun/owothia/shell.nix2
-rw-r--r--fun/paroxysm/Cargo.lock767
-rw-r--r--fun/paroxysm/Cargo.nix6064
-rw-r--r--fun/paroxysm/Cargo.toml2
-rw-r--r--fun/paroxysm/OWNERS4
-rw-r--r--fun/paroxysm/default.nix20
-rw-r--r--fun/paroxysm/src/keyword.rs3
-rw-r--r--fun/paroxysm/src/main.rs10
-rw-r--r--fun/streamTVL/default.nix19
-rw-r--r--fun/tvl-ebooks/OWNERS4
-rw-r--r--fun/tvl-ebooks/default.nix2
-rw-r--r--fun/uggc/default.nix3
-rw-r--r--fun/πŸ•°οΈ/OWNERS4
-rw-r--r--fun/πŸ•°οΈ/default.nix3
-rw-r--r--lisp/klatre/OWNERS4
-rw-r--r--net/alcoholic_jwt/Cargo.lock112
-rw-r--r--net/alcoholic_jwt/Cargo.toml9
-rw-r--r--net/alcoholic_jwt/README.md14
-rw-r--r--net/alcoholic_jwt/default.nix2
-rw-r--r--net/alcoholic_jwt/src/lib.rs110
-rw-r--r--net/alcoholic_jwt/src/tests.rs24
-rw-r--r--net/crimp/Cargo.lock195
-rw-r--r--net/crimp/Cargo.toml8
-rw-r--r--net/crimp/README.md13
-rw-r--r--net/crimp/default.nix2
-rw-r--r--net/crimp/src/lib.rs150
-rw-r--r--net/crimp/src/tests.rs93
-rw-r--r--nix/OWNERS4
-rw-r--r--nix/binify/default.nix2
-rw-r--r--nix/bufCheck/default.nix31
-rw-r--r--nix/buildGo/README.md27
-rw-r--r--nix/buildGo/default.nix154
-rw-r--r--nix/buildGo/example/default.nix13
-rw-r--r--nix/buildGo/example/thing.proto10
-rw-r--r--nix/buildGo/external/default.nix55
-rw-r--r--nix/buildGo/external/main.go7
-rw-r--r--nix/buildGo/proto.nix86
-rw-r--r--nix/buildLisp/README.md2
-rw-r--r--nix/buildLisp/default.nix293
-rw-r--r--nix/buildLisp/example/default.nix19
-rw-r--r--nix/buildLisp/tests/argv0.nix58
-rw-r--r--nix/buildManPages/OWNERS4
-rw-r--r--nix/buildManPages/default.nix77
-rw-r--r--nix/buildkite/default.nix548
-rwxr-xr-xnix/buildkite/fetch-parent-targets.sh64
-rw-r--r--nix/dependency-analyzer/default.nix263
-rw-r--r--nix/dependency-analyzer/examples/ci-targets.nix12
-rw-r--r--nix/dependency-analyzer/examples/lisp.nix5
-rw-r--r--nix/dependency-analyzer/tests/default.nix36
-rw-r--r--nix/drvSeqL/default.nix26
-rw-r--r--nix/emptyDerivation/OWNERS4
-rw-r--r--nix/emptyDerivation/default.nix6
-rw-r--r--nix/emptyDerivation/emptyDerivation.nix18
-rw-r--r--nix/emptyDerivation/tests.nix18
-rw-r--r--nix/escapeExecline/default.nix19
-rw-r--r--nix/getBins/default.nix21
-rw-r--r--nix/getBins/tests.nix20
-rw-r--r--nix/lazy-deps/default.nix91
-rw-r--r--nix/mergePatch/default.nix140
-rw-r--r--nix/netstring/attrsToKeyValList.nix4
-rw-r--r--nix/nint/OWNERS4
-rw-r--r--nix/nint/default.nix14
-rw-r--r--nix/nint/nint.rs55
-rw-r--r--nix/nix-1p/README.md648
-rw-r--r--nix/nix-1p/default.nix16
-rw-r--r--nix/readTree/README.md13
-rw-r--r--nix/readTree/default.nix247
-rw-r--r--nix/readTree/tests/default.nix28
-rw-r--r--nix/readTree/tests/test-marker/directory-marked/default.nix2
-rw-r--r--nix/readTree/tests/test-marker/directory-marked/nested/default.nix2
-rw-r--r--nix/readTree/tests/test-marker/file-children/one.nix2
-rw-r--r--nix/readTree/tests/test-marker/file-children/two.nix2
-rw-r--r--nix/readTree/tests/test-tree-traversal/skip-tree/a/default.nix1
-rw-r--r--nix/readTree/tests/test-tree-traversal/skip-tree/b/.skip-tree1
-rw-r--r--nix/readTree/tests/test-tree-traversal/skip-tree/b/default.nix1
-rw-r--r--nix/renderMarkdown/default.nix19
-rw-r--r--nix/runExecline/default.nix5
-rw-r--r--nix/runExecline/runExecline.nix64
-rw-r--r--nix/runExecline/tests.nix121
-rw-r--r--nix/runTestsuite/default.nix76
-rw-r--r--nix/sparseTree/OWNERS4
-rw-r--r--nix/sparseTree/default.nix58
-rw-r--r--nix/stateMonad/default.nix76
-rw-r--r--nix/stateMonad/tests/default.nix110
-rw-r--r--nix/tag/default.nix84
-rw-r--r--nix/tag/tests.nix49
-rw-r--r--nix/tailscale/default.nix3
-rw-r--r--nix/utils/OWNERS4
-rw-r--r--nix/utils/default.nix59
-rw-r--r--nix/utils/tests/default.nix69
-rw-r--r--nix/writeElispBin/default.nix12
-rw-r--r--nix/writeExecline/default.nix17
-rw-r--r--nix/writeScript/default.nix20
-rw-r--r--nix/writeTree/OWNERS1
-rw-r--r--nix/writeTree/default.nix43
-rw-r--r--nix/writeTree/tests/default.nix93
-rw-r--r--nix/writers/default.nix126
-rw-r--r--nix/writers/tests/rust.nix40
-rw-r--r--nix/yants/README.md6
-rw-r--r--nix/yants/default.nix301
-rw-r--r--nix/yants/tests/default.nix34
-rw-r--r--ops/besadii/main.go20
-rw-r--r--ops/buildkite/.gitignore2
-rw-r--r--ops/buildkite/README.md24
-rw-r--r--ops/buildkite/default.nix14
-rw-r--r--ops/buildkite/steps-depot.yml6
-rw-r--r--ops/buildkite/steps-tvix.yml4
-rw-r--r--ops/buildkite/steps-tvl-kit.yml4
-rw-r--r--ops/buildkite/tvl.tf48
-rw-r--r--ops/dns/default.nix5
-rw-r--r--ops/gerrit-autosubmit/.gitignore (renamed from users/grfn/xanthous/server/.gitignore)0
-rw-r--r--ops/gerrit-autosubmit/Cargo.lock302
-rw-r--r--ops/gerrit-autosubmit/Cargo.toml12
-rw-r--r--ops/gerrit-autosubmit/default.nix7
-rw-r--r--ops/gerrit-autosubmit/src/main.rs194
-rw-r--r--ops/gerrit-tvl/static/tvl.js8
-rw-r--r--ops/glesys/default.nix13
-rw-r--r--ops/glesys/dns-nixery-dev.tf9
-rw-r--r--ops/glesys/dns-tvix-dev.tf54
-rw-r--r--ops/glesys/dns-tvl-fyi.tf24
-rw-r--r--ops/glesys/dns-tvl-su.tf81
-rw-r--r--ops/glesys/main.tf46
-rw-r--r--ops/journaldriver/Cargo.lock832
-rw-r--r--ops/journaldriver/Cargo.toml22
-rw-r--r--ops/journaldriver/build.rs3
-rw-r--r--ops/journaldriver/default.nix4
-rw-r--r--ops/journaldriver/src/main.rs171
-rw-r--r--ops/journaldriver/src/tests.rs56
-rw-r--r--ops/keycloak/README.md2
-rw-r--r--ops/keycloak/clients.tf17
-rw-r--r--ops/keycloak/default.nix12
-rw-r--r--ops/keycloak/main.tf12
-rw-r--r--ops/keycloak/user_sources.tf23
-rw-r--r--ops/kontemplate/release.nix22
-rw-r--r--ops/machines/all-systems.nix9
-rw-r--r--ops/machines/nixery-01/default.nix40
-rw-r--r--ops/machines/sanduny/default.nix138
-rw-r--r--ops/machines/whitby/OWNERS7
-rw-r--r--ops/machines/whitby/default.nix435
-rw-r--r--ops/modules/atward.nix3
-rw-r--r--ops/modules/auto-deploy.nix3
-rw-r--r--ops/modules/automatic-gc.nix8
-rw-r--r--ops/modules/btrfs-auto-scrub.nix25
-rw-r--r--ops/modules/cgit.nix55
-rw-r--r--ops/modules/clbot.nix11
-rw-r--r--ops/modules/default.nix2
-rw-r--r--ops/modules/depot-inbox.nix148
-rw-r--r--ops/modules/depot-replica.nix45
-rw-r--r--ops/modules/gerrit-autosubmit.nix43
-rw-r--r--ops/modules/gerrit-queue.nix51
-rw-r--r--ops/modules/git-serving.nix53
-rw-r--r--ops/modules/irccat.nix7
-rw-r--r--ops/modules/josh.nix33
-rw-r--r--ops/modules/journaldriver.nix26
-rw-r--r--ops/modules/known-hosts.nix21
-rw-r--r--ops/modules/livegrep.nix106
-rw-r--r--ops/modules/monorepo-gerrit.nix41
-rw-r--r--ops/modules/nixery.nix10
-rw-r--r--ops/modules/oauth2_proxy.nix52
-rw-r--r--ops/modules/open_eid.nix54
-rw-r--r--ops/modules/owothia.nix5
-rw-r--r--ops/modules/panettone.nix53
-rw-r--r--ops/modules/paroxysm.nix3
-rw-r--r--ops/modules/quassel.nix13
-rw-r--r--ops/modules/restic.nix3
-rw-r--r--ops/modules/smtprelay.nix7
-rw-r--r--ops/modules/sourcegraph.nix8
-rw-r--r--ops/modules/tvl-buildkite.nix71
-rw-r--r--ops/modules/tvl-cache.nix6
-rw-r--r--ops/modules/tvl-headscale.nix62
-rw-r--r--ops/modules/tvl-slapd/default.nix9
-rw-r--r--ops/modules/tvl-users.nix83
-rw-r--r--ops/modules/v4l2loopback.nix12
-rw-r--r--ops/modules/www/auth.tvl.fyi.nix6
-rw-r--r--ops/modules/www/base.nix45
-rw-r--r--ops/modules/www/cl.tvl.fyi.nix4
-rw-r--r--ops/modules/www/code.tvl.fyi.nix47
-rw-r--r--ops/modules/www/grep.tvl.fyi.nix19
-rw-r--r--ops/modules/www/images.tvl.fyi.nix22
-rw-r--r--ops/modules/www/inbox.tvl.su.nix31
-rw-r--r--ops/modules/www/self-redirect.nix27
-rw-r--r--ops/modules/www/signup.tvl.fyi.nix19
-rw-r--r--ops/modules/www/status.tvl.su.nix2
-rw-r--r--ops/modules/www/tazj.in.nix14
-rw-r--r--ops/modules/www/tvix.dev.nix46
-rw-r--r--ops/modules/www/tvl.fyi.nix6
-rw-r--r--ops/modules/www/volgasprint.org.nix15
-rw-r--r--ops/modules/www/wigglydonke.rs.nix2
-rw-r--r--ops/modules/yandex-cloud.nix78
-rw-r--r--ops/mq_cli/Cargo.lock139
-rw-r--r--ops/mq_cli/Cargo.toml16
-rw-r--r--ops/mq_cli/README.md11
-rw-r--r--ops/mq_cli/src/main.rs76
-rw-r--r--ops/nixos.nix25
-rw-r--r--ops/pipelines/depot.nix17
-rw-r--r--ops/pipelines/static-pipeline.yaml35
-rw-r--r--ops/posix_mq.rs/Cargo.lock57
-rw-r--r--ops/posix_mq.rs/Cargo.toml9
-rw-r--r--ops/posix_mq.rs/README.md13
-rw-r--r--ops/posix_mq.rs/src/error.rs80
-rw-r--r--ops/posix_mq.rs/src/lib.rs62
-rw-r--r--ops/posix_mq.rs/src/tests.rs3
-rw-r--r--ops/secrets/besadii.agebin1050 -> 1186 bytes
-rw-r--r--ops/secrets/buildkite-agent-token.agebin710 -> 743 bytes
-rw-r--r--ops/secrets/buildkite-graphql-token.age29
-rw-r--r--ops/secrets/buildkite-ssh-private-key.agebin0 -> 1194 bytes
-rw-r--r--ops/secrets/clbot-ssh.agebin990 -> 1162 bytes
-rw-r--r--ops/secrets/clbot.age27
-rw-r--r--ops/secrets/depot-inbox-imap.age15
-rw-r--r--ops/secrets/depot-replica-key.agebin0 -> 1208 bytes
-rw-r--r--ops/secrets/gerrit-autosubmit.agebin0 -> 853 bytes
-rw-r--r--ops/secrets/gerrit-queue.agebin803 -> 0 bytes
-rw-r--r--ops/secrets/gerrit-secrets.agebin828 -> 913 bytes
-rw-r--r--ops/secrets/grafana.age27
-rw-r--r--ops/secrets/irccat.agebin673 -> 825 bytes
-rw-r--r--ops/secrets/journaldriver.agebin0 -> 3202 bytes
-rw-r--r--ops/secrets/keycloak-db.agebin589 -> 710 bytes
-rw-r--r--ops/secrets/mkSecrets.nix6
-rw-r--r--ops/secrets/nix-cache-priv.agebin732 -> 786 bytes
-rw-r--r--ops/secrets/nix-cache-pub.age27
-rw-r--r--ops/secrets/oauth2_proxy.age14
-rw-r--r--ops/secrets/owothia.age29
-rw-r--r--ops/secrets/panettone.age29
-rw-r--r--ops/secrets/secrets.nix59
-rw-r--r--ops/secrets/smtprelay.age28
-rw-r--r--ops/secrets/tf-buildkite.agebin0 -> 943 bytes
-rw-r--r--ops/secrets/tf-glesys.agebin822 -> 959 bytes
-rw-r--r--ops/secrets/tf-keycloak.agebin842 -> 962 bytes
-rw-r--r--ops/secrets/tvl-alerts-bot-telegram-token.age15
-rw-r--r--ops/terraform/README.md5
-rw-r--r--ops/terraform/deploy-nixos/README.md50
-rw-r--r--ops/terraform/deploy-nixos/main.tf113
-rwxr-xr-xops/terraform/deploy-nixos/nix-eval.sh47
-rwxr-xr-xops/terraform/deploy-nixos/nixos-copy.sh32
-rw-r--r--ops/users/default.nix80
-rw-r--r--ops/yandex-base-image/default.nix9
-rw-r--r--ops/yandex-cloud-rs/.gitignore5
-rw-r--r--ops/yandex-cloud-rs/Cargo.lock1368
-rw-r--r--ops/yandex-cloud-rs/Cargo.toml24
-rw-r--r--ops/yandex-cloud-rs/README.md49
-rw-r--r--ops/yandex-cloud-rs/build.rs43
-rw-r--r--ops/yandex-cloud-rs/default.nix22
-rw-r--r--ops/yandex-cloud-rs/examples/log-write.rs37
-rw-r--r--ops/yandex-cloud-rs/src/lib.rs108
-rw-r--r--rustfmt.toml1
-rw-r--r--third_party/abseil_cpp/.clang-format4
-rw-r--r--third_party/abseil_cpp/.github/ISSUE_TEMPLATE/00-bug_report.md41
-rw-r--r--third_party/abseil_cpp/.github/ISSUE_TEMPLATE/90-question.md7
-rw-r--r--third_party/abseil_cpp/.github/ISSUE_TEMPLATE/config.yml1
-rw-r--r--third_party/abseil_cpp/.gitignore15
-rw-r--r--third_party/abseil_cpp/.skip-subtree1
-rw-r--r--third_party/abseil_cpp/ABSEIL_ISSUE_TEMPLATE.md22
-rw-r--r--third_party/abseil_cpp/AUTHORS6
-rw-r--r--third_party/abseil_cpp/BUILD.bazel25
-rw-r--r--third_party/abseil_cpp/CMake/AbseilDll.cmake514
-rw-r--r--third_party/abseil_cpp/CMake/AbseilHelpers.cmake402
-rw-r--r--third_party/abseil_cpp/CMake/AbseilInstallDirs.cmake20
-rw-r--r--third_party/abseil_cpp/CMake/Googletest/CMakeLists.txt.in14
-rw-r--r--third_party/abseil_cpp/CMake/Googletest/DownloadGTest.cmake41
-rw-r--r--third_party/abseil_cpp/CMake/README.md101
-rw-r--r--third_party/abseil_cpp/CMake/abslConfig.cmake.in8
-rw-r--r--third_party/abseil_cpp/CMake/install_test_project/CMakeLists.txt27
-rw-r--r--third_party/abseil_cpp/CMake/install_test_project/simple.cc23
-rwxr-xr-xthird_party/abseil_cpp/CMake/install_test_project/test.sh162
-rw-r--r--third_party/abseil_cpp/CMakeLists.txt200
-rw-r--r--third_party/abseil_cpp/CONTRIBUTING.md141
-rw-r--r--third_party/abseil_cpp/FAQ.md164
-rw-r--r--third_party/abseil_cpp/LICENSE203
-rw-r--r--third_party/abseil_cpp/LTS.md16
-rw-r--r--third_party/abseil_cpp/README.md114
-rw-r--r--third_party/abseil_cpp/UPGRADES.md17
-rw-r--r--third_party/abseil_cpp/WORKSPACE45
-rw-r--r--third_party/abseil_cpp/absl/BUILD.bazel65
-rw-r--r--third_party/abseil_cpp/absl/CMakeLists.txt37
-rwxr-xr-xthird_party/abseil_cpp/absl/abseil.podspec.gen.py229
-rw-r--r--third_party/abseil_cpp/absl/algorithm/BUILD.bazel91
-rw-r--r--third_party/abseil_cpp/absl/algorithm/CMakeLists.txt69
-rw-r--r--third_party/abseil_cpp/absl/algorithm/algorithm.h159
-rw-r--r--third_party/abseil_cpp/absl/algorithm/algorithm_test.cc182
-rw-r--r--third_party/abseil_cpp/absl/algorithm/container.h1764
-rw-r--r--third_party/abseil_cpp/absl/algorithm/container_test.cc1124
-rw-r--r--third_party/abseil_cpp/absl/algorithm/equal_benchmark.cc126
-rw-r--r--third_party/abseil_cpp/absl/base/BUILD.bazel818
-rw-r--r--third_party/abseil_cpp/absl/base/CMakeLists.txt717
-rw-r--r--third_party/abseil_cpp/absl/base/attributes.h683
-rw-r--r--third_party/abseil_cpp/absl/base/bit_cast_test.cc109
-rw-r--r--third_party/abseil_cpp/absl/base/call_once.h226
-rw-r--r--third_party/abseil_cpp/absl/base/call_once_test.cc107
-rw-r--r--third_party/abseil_cpp/absl/base/casts.h187
-rw-r--r--third_party/abseil_cpp/absl/base/config.h714
-rw-r--r--third_party/abseil_cpp/absl/base/config_test.cc60
-rw-r--r--third_party/abseil_cpp/absl/base/const_init.h76
-rw-r--r--third_party/abseil_cpp/absl/base/dynamic_annotations.h482
-rw-r--r--third_party/abseil_cpp/absl/base/exception_safety_testing_test.cc956
-rw-r--r--third_party/abseil_cpp/absl/base/inline_variable_test.cc64
-rw-r--r--third_party/abseil_cpp/absl/base/inline_variable_test_a.cc27
-rw-r--r--third_party/abseil_cpp/absl/base/inline_variable_test_b.cc27
-rw-r--r--third_party/abseil_cpp/absl/base/internal/atomic_hook.h200
-rw-r--r--third_party/abseil_cpp/absl/base/internal/atomic_hook_test.cc97
-rw-r--r--third_party/abseil_cpp/absl/base/internal/atomic_hook_test_helper.cc32
-rw-r--r--third_party/abseil_cpp/absl/base/internal/atomic_hook_test_helper.h34
-rw-r--r--third_party/abseil_cpp/absl/base/internal/bits.h219
-rw-r--r--third_party/abseil_cpp/absl/base/internal/bits_test.cc97
-rw-r--r--third_party/abseil_cpp/absl/base/internal/cmake_thread_test.cc22
-rw-r--r--third_party/abseil_cpp/absl/base/internal/cycleclock.cc107
-rw-r--r--third_party/abseil_cpp/absl/base/internal/cycleclock.h94
-rw-r--r--third_party/abseil_cpp/absl/base/internal/direct_mmap.h166
-rw-r--r--third_party/abseil_cpp/absl/base/internal/dynamic_annotations.h398
-rw-r--r--third_party/abseil_cpp/absl/base/internal/endian.h266
-rw-r--r--third_party/abseil_cpp/absl/base/internal/endian_test.cc263
-rw-r--r--third_party/abseil_cpp/absl/base/internal/errno_saver.h43
-rw-r--r--third_party/abseil_cpp/absl/base/internal/errno_saver_test.cc45
-rw-r--r--third_party/abseil_cpp/absl/base/internal/exception_safety_testing.cc79
-rw-r--r--third_party/abseil_cpp/absl/base/internal/exception_safety_testing.h1101
-rw-r--r--third_party/abseil_cpp/absl/base/internal/exception_testing.h42
-rw-r--r--third_party/abseil_cpp/absl/base/internal/exponential_biased.cc93
-rw-r--r--third_party/abseil_cpp/absl/base/internal/exponential_biased.h130
-rw-r--r--third_party/abseil_cpp/absl/base/internal/exponential_biased_test.cc199
-rw-r--r--third_party/abseil_cpp/absl/base/internal/fast_type_id.h48
-rw-r--r--third_party/abseil_cpp/absl/base/internal/fast_type_id_test.cc123
-rw-r--r--third_party/abseil_cpp/absl/base/internal/hide_ptr.h51
-rw-r--r--third_party/abseil_cpp/absl/base/internal/identity.h37
-rw-r--r--third_party/abseil_cpp/absl/base/internal/inline_variable.h107
-rw-r--r--third_party/abseil_cpp/absl/base/internal/inline_variable_testing.h46
-rw-r--r--third_party/abseil_cpp/absl/base/internal/invoke.h187
-rw-r--r--third_party/abseil_cpp/absl/base/internal/low_level_alloc.cc620
-rw-r--r--third_party/abseil_cpp/absl/base/internal/low_level_alloc.h126
-rw-r--r--third_party/abseil_cpp/absl/base/internal/low_level_alloc_test.cc162
-rw-r--r--third_party/abseil_cpp/absl/base/internal/low_level_scheduling.h134
-rw-r--r--third_party/abseil_cpp/absl/base/internal/per_thread_tls.h52
-rw-r--r--third_party/abseil_cpp/absl/base/internal/periodic_sampler.cc53
-rw-r--r--third_party/abseil_cpp/absl/base/internal/periodic_sampler.h211
-rw-r--r--third_party/abseil_cpp/absl/base/internal/periodic_sampler_benchmark.cc79
-rw-r--r--third_party/abseil_cpp/absl/base/internal/periodic_sampler_test.cc177
-rw-r--r--third_party/abseil_cpp/absl/base/internal/pretty_function.h33
-rw-r--r--third_party/abseil_cpp/absl/base/internal/raw_logging.cc240
-rw-r--r--third_party/abseil_cpp/absl/base/internal/raw_logging.h187
-rw-r--r--third_party/abseil_cpp/absl/base/internal/scheduling_mode.h58
-rw-r--r--third_party/abseil_cpp/absl/base/internal/scoped_set_env.cc81
-rw-r--r--third_party/abseil_cpp/absl/base/internal/scoped_set_env.h45
-rw-r--r--third_party/abseil_cpp/absl/base/internal/scoped_set_env_test.cc99
-rw-r--r--third_party/abseil_cpp/absl/base/internal/spinlock.cc220
-rw-r--r--third_party/abseil_cpp/absl/base/internal/spinlock.h237
-rw-r--r--third_party/abseil_cpp/absl/base/internal/spinlock_akaros.inc35
-rw-r--r--third_party/abseil_cpp/absl/base/internal/spinlock_benchmark.cc52
-rw-r--r--third_party/abseil_cpp/absl/base/internal/spinlock_linux.inc74
-rw-r--r--third_party/abseil_cpp/absl/base/internal/spinlock_posix.inc46
-rw-r--r--third_party/abseil_cpp/absl/base/internal/spinlock_wait.cc81
-rw-r--r--third_party/abseil_cpp/absl/base/internal/spinlock_wait.h93
-rw-r--r--third_party/abseil_cpp/absl/base/internal/spinlock_win32.inc37
-rw-r--r--third_party/abseil_cpp/absl/base/internal/strerror.cc88
-rw-r--r--third_party/abseil_cpp/absl/base/internal/strerror.h39
-rw-r--r--third_party/abseil_cpp/absl/base/internal/strerror_benchmark.cc29
-rw-r--r--third_party/abseil_cpp/absl/base/internal/strerror_test.cc86
-rw-r--r--third_party/abseil_cpp/absl/base/internal/sysinfo.cc439
-rw-r--r--third_party/abseil_cpp/absl/base/internal/sysinfo.h74
-rw-r--r--third_party/abseil_cpp/absl/base/internal/sysinfo_test.cc100
-rw-r--r--third_party/abseil_cpp/absl/base/internal/thread_annotations.h271
-rw-r--r--third_party/abseil_cpp/absl/base/internal/thread_identity.cc155
-rw-r--r--third_party/abseil_cpp/absl/base/internal/thread_identity.h260
-rw-r--r--third_party/abseil_cpp/absl/base/internal/thread_identity_benchmark.cc38
-rw-r--r--third_party/abseil_cpp/absl/base/internal/thread_identity_test.cc129
-rw-r--r--third_party/abseil_cpp/absl/base/internal/throw_delegate.cc212
-rw-r--r--third_party/abseil_cpp/absl/base/internal/throw_delegate.h75
-rw-r--r--third_party/abseil_cpp/absl/base/internal/tsan_mutex_interface.h68
-rw-r--r--third_party/abseil_cpp/absl/base/internal/unaligned_access.h82
-rw-r--r--third_party/abseil_cpp/absl/base/internal/unique_small_name_test.cc77
-rw-r--r--third_party/abseil_cpp/absl/base/internal/unscaledcycleclock.cc138
-rw-r--r--third_party/abseil_cpp/absl/base/internal/unscaledcycleclock.h124
-rw-r--r--third_party/abseil_cpp/absl/base/invoke_test.cc229
-rw-r--r--third_party/abseil_cpp/absl/base/log_severity.cc27
-rw-r--r--third_party/abseil_cpp/absl/base/log_severity.h121
-rw-r--r--third_party/abseil_cpp/absl/base/log_severity_test.cc204
-rw-r--r--third_party/abseil_cpp/absl/base/macros.h158
-rw-r--r--third_party/abseil_cpp/absl/base/optimization.h241
-rw-r--r--third_party/abseil_cpp/absl/base/optimization_test.cc129
-rw-r--r--third_party/abseil_cpp/absl/base/options.h238
-rw-r--r--third_party/abseil_cpp/absl/base/policy_checks.h111
-rw-r--r--third_party/abseil_cpp/absl/base/port.h26
-rw-r--r--third_party/abseil_cpp/absl/base/raw_logging_test.cc79
-rw-r--r--third_party/abseil_cpp/absl/base/spinlock_test_common.cc271
-rw-r--r--third_party/abseil_cpp/absl/base/thread_annotations.h335
-rw-r--r--third_party/abseil_cpp/absl/base/throw_delegate_test.cc107
-rw-r--r--third_party/abseil_cpp/absl/container/BUILD.bazel921
-rw-r--r--third_party/abseil_cpp/absl/container/CMakeLists.txt905
-rw-r--r--third_party/abseil_cpp/absl/container/btree_benchmark.cc735
-rw-r--r--third_party/abseil_cpp/absl/container/btree_map.h769
-rw-r--r--third_party/abseil_cpp/absl/container/btree_set.h683
-rw-r--r--third_party/abseil_cpp/absl/container/btree_test.cc2827
-rw-r--r--third_party/abseil_cpp/absl/container/btree_test.h166
-rw-r--r--third_party/abseil_cpp/absl/container/fixed_array.h532
-rw-r--r--third_party/abseil_cpp/absl/container/fixed_array_benchmark.cc67
-rw-r--r--third_party/abseil_cpp/absl/container/fixed_array_exception_safety_test.cc201
-rw-r--r--third_party/abseil_cpp/absl/container/fixed_array_test.cc837
-rw-r--r--third_party/abseil_cpp/absl/container/flat_hash_map.h606
-rw-r--r--third_party/abseil_cpp/absl/container/flat_hash_map_test.cc288
-rw-r--r--third_party/abseil_cpp/absl/container/flat_hash_set.h504
-rw-r--r--third_party/abseil_cpp/absl/container/flat_hash_set_test.cc178
-rw-r--r--third_party/abseil_cpp/absl/container/inlined_vector.h845
-rw-r--r--third_party/abseil_cpp/absl/container/inlined_vector_benchmark.cc807
-rw-r--r--third_party/abseil_cpp/absl/container/inlined_vector_exception_safety_test.cc508
-rw-r--r--third_party/abseil_cpp/absl/container/inlined_vector_test.cc1815
-rw-r--r--third_party/abseil_cpp/absl/container/internal/btree.h2587
-rw-r--r--third_party/abseil_cpp/absl/container/internal/btree_container.h683
-rw-r--r--third_party/abseil_cpp/absl/container/internal/common.h206
-rw-r--r--third_party/abseil_cpp/absl/container/internal/compressed_tuple.h290
-rw-r--r--third_party/abseil_cpp/absl/container/internal/compressed_tuple_test.cc409
-rw-r--r--third_party/abseil_cpp/absl/container/internal/container_memory.h460
-rw-r--r--third_party/abseil_cpp/absl/container/internal/container_memory_test.cc256
-rw-r--r--third_party/abseil_cpp/absl/container/internal/counting_allocator.h114
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hash_function_defaults.h161
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hash_function_defaults_test.cc383
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hash_generator_testing.cc76
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hash_generator_testing.h161
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hash_policy_testing.h184
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hash_policy_testing_test.cc45
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hash_policy_traits.h208
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hash_policy_traits_test.cc144
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hashtable_debug.h110
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hashtable_debug_hooks.h85
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hashtablez_sampler.cc270
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hashtablez_sampler.h321
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc30
-rw-r--r--third_party/abseil_cpp/absl/container/internal/hashtablez_sampler_test.cc371
-rw-r--r--third_party/abseil_cpp/absl/container/internal/have_sse.h50
-rw-r--r--third_party/abseil_cpp/absl/container/internal/inlined_vector.h895
-rw-r--r--third_party/abseil_cpp/absl/container/internal/layout.h743
-rw-r--r--third_party/abseil_cpp/absl/container/internal/layout_test.cc1635
-rw-r--r--third_party/abseil_cpp/absl/container/internal/node_hash_policy.h92
-rw-r--r--third_party/abseil_cpp/absl/container/internal/node_hash_policy_test.cc69
-rw-r--r--third_party/abseil_cpp/absl/container/internal/raw_hash_map.h197
-rw-r--r--third_party/abseil_cpp/absl/container/internal/raw_hash_set.cc61
-rw-r--r--third_party/abseil_cpp/absl/container/internal/raw_hash_set.h1903
-rw-r--r--third_party/abseil_cpp/absl/container/internal/raw_hash_set_allocator_test.cc505
-rw-r--r--third_party/abseil_cpp/absl/container/internal/raw_hash_set_test.cc1893
-rw-r--r--third_party/abseil_cpp/absl/container/internal/test_instance_tracker.cc29
-rw-r--r--third_party/abseil_cpp/absl/container/internal/test_instance_tracker.h274
-rw-r--r--third_party/abseil_cpp/absl/container/internal/test_instance_tracker_test.cc184
-rw-r--r--third_party/abseil_cpp/absl/container/internal/tracked.h83
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_map_constructor_test.h489
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_map_lookup_test.h117
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_map_members_test.h87
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_map_modifiers_test.h318
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_map_test.cc50
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_set_constructor_test.h496
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_set_lookup_test.h91
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_set_members_test.h86
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_set_modifiers_test.h190
-rw-r--r--third_party/abseil_cpp/absl/container/internal/unordered_set_test.cc41
-rw-r--r--third_party/abseil_cpp/absl/container/node_hash_map.h597
-rw-r--r--third_party/abseil_cpp/absl/container/node_hash_map_test.cc275
-rw-r--r--third_party/abseil_cpp/absl/container/node_hash_set.h493
-rw-r--r--third_party/abseil_cpp/absl/container/node_hash_set_test.cc143
-rw-r--r--third_party/abseil_cpp/absl/copts/AbseilConfigureCopts.cmake67
-rw-r--r--third_party/abseil_cpp/absl/copts/GENERATED_AbseilCopts.cmake162
-rw-r--r--third_party/abseil_cpp/absl/copts/GENERATED_copts.bzl163
-rw-r--r--third_party/abseil_cpp/absl/copts/configure_copts.bzl76
-rw-r--r--third_party/abseil_cpp/absl/copts/copts.py162
-rwxr-xr-xthird_party/abseil_cpp/absl/copts/generate_copts.py109
-rw-r--r--third_party/abseil_cpp/absl/debugging/BUILD.bazel347
-rw-r--r--third_party/abseil_cpp/absl/debugging/CMakeLists.txt338
-rw-r--r--third_party/abseil_cpp/absl/debugging/failure_signal_handler.cc370
-rw-r--r--third_party/abseil_cpp/absl/debugging/failure_signal_handler.h121
-rw-r--r--third_party/abseil_cpp/absl/debugging/failure_signal_handler_test.cc159
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/address_is_readable.cc139
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/address_is_readable.h32
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/demangle.cc1945
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/demangle.h71
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/demangle_test.cc197
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/elf_mem_image.cc382
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/elf_mem_image.h134
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/examine_stack.cc187
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/examine_stack.h42
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stack_consumption.cc185
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stack_consumption.h50
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stack_consumption_test.cc50
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc199
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stacktrace_arm-inl.inc134
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stacktrace_config.h89
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stacktrace_generic-inl.inc108
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc248
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stacktrace_unimplemented-inl.inc24
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stacktrace_win32-inl.inc93
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/stacktrace_x86-inl.inc346
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/symbolize.h147
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/vdso_support.cc173
-rw-r--r--third_party/abseil_cpp/absl/debugging/internal/vdso_support.h158
-rw-r--r--third_party/abseil_cpp/absl/debugging/leak_check.cc53
-rw-r--r--third_party/abseil_cpp/absl/debugging/leak_check.h113
-rw-r--r--third_party/abseil_cpp/absl/debugging/leak_check_disable.cc20
-rw-r--r--third_party/abseil_cpp/absl/debugging/leak_check_fail_test.cc41
-rw-r--r--third_party/abseil_cpp/absl/debugging/leak_check_test.cc42
-rw-r--r--third_party/abseil_cpp/absl/debugging/stacktrace.cc140
-rw-r--r--third_party/abseil_cpp/absl/debugging/stacktrace.h231
-rw-r--r--third_party/abseil_cpp/absl/debugging/symbolize.cc36
-rw-r--r--third_party/abseil_cpp/absl/debugging/symbolize.h99
-rw-r--r--third_party/abseil_cpp/absl/debugging/symbolize_darwin.inc101
-rw-r--r--third_party/abseil_cpp/absl/debugging/symbolize_elf.inc1560
-rw-r--r--third_party/abseil_cpp/absl/debugging/symbolize_test.cc557
-rw-r--r--third_party/abseil_cpp/absl/debugging/symbolize_unimplemented.inc40
-rw-r--r--third_party/abseil_cpp/absl/debugging/symbolize_win32.inc81
-rw-r--r--third_party/abseil_cpp/absl/flags/BUILD.bazel514
-rw-r--r--third_party/abseil_cpp/absl/flags/CMakeLists.txt451
-rw-r--r--third_party/abseil_cpp/absl/flags/commandlineflag.cc34
-rw-r--r--third_party/abseil_cpp/absl/flags/commandlineflag.h200
-rw-r--r--third_party/abseil_cpp/absl/flags/commandlineflag_test.cc231
-rw-r--r--third_party/abseil_cpp/absl/flags/config.h87
-rw-r--r--third_party/abseil_cpp/absl/flags/config_test.cc61
-rw-r--r--third_party/abseil_cpp/absl/flags/declare.h65
-rw-r--r--third_party/abseil_cpp/absl/flags/flag.cc38
-rw-r--r--third_party/abseil_cpp/absl/flags/flag.h396
-rw-r--r--third_party/abseil_cpp/absl/flags/flag_benchmark.cc159
-rw-r--r--third_party/abseil_cpp/absl/flags/flag_test.cc906
-rw-r--r--third_party/abseil_cpp/absl/flags/flag_test_defs.cc24
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/commandlineflag.cc26
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/commandlineflag.h68
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/flag.cc568
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/flag.h775
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/parse.h59
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/path_util.h62
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/path_util_test.cc46
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/private_handle_accessor.cc65
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/private_handle_accessor.h61
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/program_name.cc60
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/program_name.h50
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/program_name_test.cc61
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/registry.h97
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/usage.cc524
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/usage.h104
-rw-r--r--third_party/abseil_cpp/absl/flags/internal/usage_test.cc493
-rw-r--r--third_party/abseil_cpp/absl/flags/marshalling.cc241
-rw-r--r--third_party/abseil_cpp/absl/flags/marshalling.h264
-rw-r--r--third_party/abseil_cpp/absl/flags/marshalling_test.cc904
-rw-r--r--third_party/abseil_cpp/absl/flags/parse.cc823
-rw-r--r--third_party/abseil_cpp/absl/flags/parse.h60
-rw-r--r--third_party/abseil_cpp/absl/flags/parse_test.cc929
-rw-r--r--third_party/abseil_cpp/absl/flags/reflection.cc337
-rw-r--r--third_party/abseil_cpp/absl/flags/reflection.h90
-rw-r--r--third_party/abseil_cpp/absl/flags/reflection_test.cc267
-rw-r--r--third_party/abseil_cpp/absl/flags/usage.cc65
-rw-r--r--third_party/abseil_cpp/absl/flags/usage.h43
-rw-r--r--third_party/abseil_cpp/absl/flags/usage_config.cc164
-rw-r--r--third_party/abseil_cpp/absl/flags/usage_config.h134
-rw-r--r--third_party/abseil_cpp/absl/flags/usage_config_test.cc205
-rw-r--r--third_party/abseil_cpp/absl/functional/BUILD.bazel93
-rw-r--r--third_party/abseil_cpp/absl/functional/CMakeLists.txt72
-rw-r--r--third_party/abseil_cpp/absl/functional/bind_front.h184
-rw-r--r--third_party/abseil_cpp/absl/functional/bind_front_test.cc231
-rw-r--r--third_party/abseil_cpp/absl/functional/function_ref.h139
-rw-r--r--third_party/abseil_cpp/absl/functional/function_ref_benchmark.cc142
-rw-r--r--third_party/abseil_cpp/absl/functional/function_ref_test.cc257
-rw-r--r--third_party/abseil_cpp/absl/functional/internal/front_binder.h95
-rw-r--r--third_party/abseil_cpp/absl/functional/internal/function_ref.h106
-rw-r--r--third_party/abseil_cpp/absl/hash/BUILD.bazel122
-rw-r--r--third_party/abseil_cpp/absl/hash/CMakeLists.txt116
-rw-r--r--third_party/abseil_cpp/absl/hash/hash.h325
-rw-r--r--third_party/abseil_cpp/absl/hash/hash_test.cc976
-rw-r--r--third_party/abseil_cpp/absl/hash/hash_testing.h378
-rw-r--r--third_party/abseil_cpp/absl/hash/internal/city.cc349
-rw-r--r--third_party/abseil_cpp/absl/hash/internal/city.h78
-rw-r--r--third_party/abseil_cpp/absl/hash/internal/city_test.cc595
-rw-r--r--third_party/abseil_cpp/absl/hash/internal/hash.cc55
-rw-r--r--third_party/abseil_cpp/absl/hash/internal/hash.h1003
-rw-r--r--third_party/abseil_cpp/absl/hash/internal/print_hash_of.cc23
-rw-r--r--third_party/abseil_cpp/absl/hash/internal/spy_hash_state.h231
-rw-r--r--third_party/abseil_cpp/absl/memory/BUILD.bazel65
-rw-r--r--third_party/abseil_cpp/absl/memory/CMakeLists.txt55
-rw-r--r--third_party/abseil_cpp/absl/memory/memory.h699
-rw-r--r--third_party/abseil_cpp/absl/memory/memory_exception_safety_test.cc57
-rw-r--r--third_party/abseil_cpp/absl/memory/memory_test.cc650
-rw-r--r--third_party/abseil_cpp/absl/meta/BUILD.bazel48
-rw-r--r--third_party/abseil_cpp/absl/meta/CMakeLists.txt50
-rw-r--r--third_party/abseil_cpp/absl/meta/type_traits.h767
-rw-r--r--third_party/abseil_cpp/absl/meta/type_traits_test.cc1368
-rw-r--r--third_party/abseil_cpp/absl/numeric/BUILD.bazel74
-rw-r--r--third_party/abseil_cpp/absl/numeric/CMakeLists.txt61
-rw-r--r--third_party/abseil_cpp/absl/numeric/int128.cc390
-rw-r--r--third_party/abseil_cpp/absl/numeric/int128.h1092
-rw-r--r--third_party/abseil_cpp/absl/numeric/int128_benchmark.cc282
-rw-r--r--third_party/abseil_cpp/absl/numeric/int128_have_intrinsic.inc302
-rw-r--r--third_party/abseil_cpp/absl/numeric/int128_no_intrinsic.inc308
-rw-r--r--third_party/abseil_cpp/absl/numeric/int128_stream_test.cc1395
-rw-r--r--third_party/abseil_cpp/absl/numeric/int128_test.cc1225
-rw-r--r--third_party/abseil_cpp/absl/random/BUILD.bazel509
-rw-r--r--third_party/abseil_cpp/absl/random/CMakeLists.txt1207
-rw-r--r--third_party/abseil_cpp/absl/random/benchmarks.cc383
-rw-r--r--third_party/abseil_cpp/absl/random/bernoulli_distribution.h200
-rw-r--r--third_party/abseil_cpp/absl/random/bernoulli_distribution_test.cc217
-rw-r--r--third_party/abseil_cpp/absl/random/beta_distribution.h427
-rw-r--r--third_party/abseil_cpp/absl/random/beta_distribution_test.cc619
-rw-r--r--third_party/abseil_cpp/absl/random/bit_gen_ref.h181
-rw-r--r--third_party/abseil_cpp/absl/random/bit_gen_ref_test.cc102
-rw-r--r--third_party/abseil_cpp/absl/random/discrete_distribution.cc98
-rw-r--r--third_party/abseil_cpp/absl/random/discrete_distribution.h247
-rw-r--r--third_party/abseil_cpp/absl/random/discrete_distribution_test.cc250
-rw-r--r--third_party/abseil_cpp/absl/random/distributions.h452
-rw-r--r--third_party/abseil_cpp/absl/random/distributions_test.cc455
-rw-r--r--third_party/abseil_cpp/absl/random/examples_test.cc99
-rw-r--r--third_party/abseil_cpp/absl/random/exponential_distribution.h165
-rw-r--r--third_party/abseil_cpp/absl/random/exponential_distribution_test.cc430
-rw-r--r--third_party/abseil_cpp/absl/random/gaussian_distribution.cc104
-rw-r--r--third_party/abseil_cpp/absl/random/gaussian_distribution.h275
-rw-r--r--third_party/abseil_cpp/absl/random/gaussian_distribution_test.cc579
-rw-r--r--third_party/abseil_cpp/absl/random/generators_test.cc179
-rw-r--r--third_party/abseil_cpp/absl/random/internal/BUILD.bazel730
-rw-r--r--third_party/abseil_cpp/absl/random/internal/chi_square.cc232
-rw-r--r--third_party/abseil_cpp/absl/random/internal/chi_square.h89
-rw-r--r--third_party/abseil_cpp/absl/random/internal/chi_square_test.cc365
-rw-r--r--third_party/abseil_cpp/absl/random/internal/distribution_caller.h92
-rw-r--r--third_party/abseil_cpp/absl/random/internal/distribution_test_util.cc418
-rw-r--r--third_party/abseil_cpp/absl/random/internal/distribution_test_util.h113
-rw-r--r--third_party/abseil_cpp/absl/random/internal/distribution_test_util_test.cc193
-rw-r--r--third_party/abseil_cpp/absl/random/internal/explicit_seed_seq.h91
-rw-r--r--third_party/abseil_cpp/absl/random/internal/explicit_seed_seq_test.cc204
-rw-r--r--third_party/abseil_cpp/absl/random/internal/fast_uniform_bits.h268
-rw-r--r--third_party/abseil_cpp/absl/random/internal/fast_uniform_bits_test.cc336
-rw-r--r--third_party/abseil_cpp/absl/random/internal/fastmath.h74
-rw-r--r--third_party/abseil_cpp/absl/random/internal/fastmath_test.cc110
-rw-r--r--third_party/abseil_cpp/absl/random/internal/gaussian_distribution_gentables.cc143
-rw-r--r--third_party/abseil_cpp/absl/random/internal/generate_real.h146
-rw-r--r--third_party/abseil_cpp/absl/random/internal/generate_real_test.cc497
-rw-r--r--third_party/abseil_cpp/absl/random/internal/iostream_state_saver.h245
-rw-r--r--third_party/abseil_cpp/absl/random/internal/iostream_state_saver_test.cc371
-rw-r--r--third_party/abseil_cpp/absl/random/internal/mock_helpers.h127
-rw-r--r--third_party/abseil_cpp/absl/random/internal/mock_overload_set.h97
-rw-r--r--third_party/abseil_cpp/absl/random/internal/nanobenchmark.cc804
-rw-r--r--third_party/abseil_cpp/absl/random/internal/nanobenchmark.h172
-rw-r--r--third_party/abseil_cpp/absl/random/internal/nanobenchmark_test.cc77
-rw-r--r--third_party/abseil_cpp/absl/random/internal/nonsecure_base.h150
-rw-r--r--third_party/abseil_cpp/absl/random/internal/nonsecure_base_test.cc245
-rw-r--r--third_party/abseil_cpp/absl/random/internal/pcg_engine.h307
-rw-r--r--third_party/abseil_cpp/absl/random/internal/pcg_engine_test.cc638
-rw-r--r--third_party/abseil_cpp/absl/random/internal/platform.h171
-rw-r--r--third_party/abseil_cpp/absl/random/internal/pool_urbg.cc254
-rw-r--r--third_party/abseil_cpp/absl/random/internal/pool_urbg.h131
-rw-r--r--third_party/abseil_cpp/absl/random/internal/pool_urbg_test.cc182
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen.cc91
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen.h102
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_benchmarks.cc174
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_detect.cc221
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_detect.h33
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_engine.h230
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_engine_test.cc656
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_hwaes.cc573
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_hwaes.h50
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_hwaes_test.cc104
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_round_keys.cc462
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_slow.cc457
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_slow.h40
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_slow_test.cc63
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_test.cc70
-rw-r--r--third_party/abseil_cpp/absl/random/internal/randen_traits.h88
-rw-r--r--third_party/abseil_cpp/absl/random/internal/salted_seed_seq.h167
-rw-r--r--third_party/abseil_cpp/absl/random/internal/salted_seed_seq_test.cc168
-rw-r--r--third_party/abseil_cpp/absl/random/internal/seed_material.cc219
-rw-r--r--third_party/abseil_cpp/absl/random/internal/seed_material.h104
-rw-r--r--third_party/abseil_cpp/absl/random/internal/seed_material_test.cc202
-rw-r--r--third_party/abseil_cpp/absl/random/internal/sequence_urbg.h60
-rw-r--r--third_party/abseil_cpp/absl/random/internal/traits.h101
-rw-r--r--third_party/abseil_cpp/absl/random/internal/traits_test.cc126
-rw-r--r--third_party/abseil_cpp/absl/random/internal/uniform_helper.h244
-rw-r--r--third_party/abseil_cpp/absl/random/internal/uniform_helper_test.cc279
-rw-r--r--third_party/abseil_cpp/absl/random/internal/wide_multiply.h111
-rw-r--r--third_party/abseil_cpp/absl/random/internal/wide_multiply_test.cc66
-rw-r--r--third_party/abseil_cpp/absl/random/log_uniform_int_distribution.h254
-rw-r--r--third_party/abseil_cpp/absl/random/log_uniform_int_distribution_test.cc280
-rw-r--r--third_party/abseil_cpp/absl/random/mock_distributions.h266
-rw-r--r--third_party/abseil_cpp/absl/random/mock_distributions_test.cc72
-rw-r--r--third_party/abseil_cpp/absl/random/mocking_bit_gen.h228
-rw-r--r--third_party/abseil_cpp/absl/random/mocking_bit_gen_test.cc347
-rw-r--r--third_party/abseil_cpp/absl/random/poisson_distribution.h258
-rw-r--r--third_party/abseil_cpp/absl/random/poisson_distribution_test.cc573
-rw-r--r--third_party/abseil_cpp/absl/random/random.h189
-rw-r--r--third_party/abseil_cpp/absl/random/seed_gen_exception.cc46
-rw-r--r--third_party/abseil_cpp/absl/random/seed_gen_exception.h55
-rw-r--r--third_party/abseil_cpp/absl/random/seed_sequences.cc29
-rw-r--r--third_party/abseil_cpp/absl/random/seed_sequences.h110
-rw-r--r--third_party/abseil_cpp/absl/random/seed_sequences_test.cc126
-rw-r--r--third_party/abseil_cpp/absl/random/uniform_int_distribution.h275
-rw-r--r--third_party/abseil_cpp/absl/random/uniform_int_distribution_test.cc259
-rw-r--r--third_party/abseil_cpp/absl/random/uniform_real_distribution.h202
-rw-r--r--third_party/abseil_cpp/absl/random/uniform_real_distribution_test.cc343
-rw-r--r--third_party/abseil_cpp/absl/random/zipf_distribution.h271
-rw-r--r--third_party/abseil_cpp/absl/random/zipf_distribution_test.cc427
-rw-r--r--third_party/abseil_cpp/absl/status/BUILD.bazel103
-rw-r--r--third_party/abseil_cpp/absl/status/CMakeLists.txt88
-rw-r--r--third_party/abseil_cpp/absl/status/internal/status_internal.h58
-rw-r--r--third_party/abseil_cpp/absl/status/internal/statusor_internal.h396
-rw-r--r--third_party/abseil_cpp/absl/status/status.cc442
-rw-r--r--third_party/abseil_cpp/absl/status/status.h817
-rw-r--r--third_party/abseil_cpp/absl/status/status_payload_printer.cc38
-rw-r--r--third_party/abseil_cpp/absl/status/status_payload_printer.h51
-rw-r--r--third_party/abseil_cpp/absl/status/status_test.cc458
-rw-r--r--third_party/abseil_cpp/absl/status/statusor.cc71
-rw-r--r--third_party/abseil_cpp/absl/status/statusor.h760
-rw-r--r--third_party/abseil_cpp/absl/status/statusor_internals.h250
-rw-r--r--third_party/abseil_cpp/absl/status/statusor_test.cc1811
-rw-r--r--third_party/abseil_cpp/absl/strings/BUILD.bazel788
-rw-r--r--third_party/abseil_cpp/absl/strings/CMakeLists.txt609
-rw-r--r--third_party/abseil_cpp/absl/strings/ascii.cc200
-rw-r--r--third_party/abseil_cpp/absl/strings/ascii.h242
-rw-r--r--third_party/abseil_cpp/absl/strings/ascii_benchmark.cc120
-rw-r--r--third_party/abseil_cpp/absl/strings/ascii_test.cc361
-rw-r--r--third_party/abseil_cpp/absl/strings/charconv.cc984
-rw-r--r--third_party/abseil_cpp/absl/strings/charconv.h119
-rw-r--r--third_party/abseil_cpp/absl/strings/charconv_benchmark.cc204
-rw-r--r--third_party/abseil_cpp/absl/strings/charconv_test.cc780
-rw-r--r--third_party/abseil_cpp/absl/strings/cord.cc1995
-rw-r--r--third_party/abseil_cpp/absl/strings/cord.h1299
-rw-r--r--third_party/abseil_cpp/absl/strings/cord_test.cc1711
-rw-r--r--third_party/abseil_cpp/absl/strings/cord_test_helpers.h60
-rw-r--r--third_party/abseil_cpp/absl/strings/escaping.cc949
-rw-r--r--third_party/abseil_cpp/absl/strings/escaping.h164
-rw-r--r--third_party/abseil_cpp/absl/strings/escaping_benchmark.cc94
-rw-r--r--third_party/abseil_cpp/absl/strings/escaping_test.cc664
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/char_map.h156
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/char_map_benchmark.cc61
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/char_map_test.cc172
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/charconv_bigint.cc359
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/charconv_bigint.h423
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/charconv_bigint_test.cc260
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/charconv_parse.cc504
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/charconv_parse.h99
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/charconv_parse_test.cc357
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/cord_internal.h270
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/escaping.cc180
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/escaping.h58
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/escaping_test_common.h133
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/memutil.cc112
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/memutil.h148
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/memutil_benchmark.cc323
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/memutil_test.cc179
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/numbers_test_common.h184
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/ostringstream.cc36
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/ostringstream.h89
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/ostringstream_benchmark.cc106
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/ostringstream_test.cc102
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/pow10_helper.cc122
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/pow10_helper.h40
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/pow10_helper_test.cc122
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/resize_uninitialized.h73
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/resize_uninitialized_test.cc82
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/stl_type_traits.h248
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/arg.cc488
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/arg.h518
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/arg_test.cc130
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/bind.cc259
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/bind.h217
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/bind_test.cc157
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/checker.h333
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/checker_test.cc170
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/convert_test.cc1242
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/extension.cc75
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/extension.h427
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/extension_test.cc98
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.cc1419
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.h37
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/output.cc72
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/output.h96
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/output_test.cc79
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/parser.cc350
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/parser.h349
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_format/parser_test.cc427
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_join_internal.h314
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/str_split_internal.h430
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/string_constant.h70
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/string_constant_test.cc60
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/utf8.cc53
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/utf8.h50
-rw-r--r--third_party/abseil_cpp/absl/strings/internal/utf8_test.cc66
-rw-r--r--third_party/abseil_cpp/absl/strings/match.cc40
-rw-r--r--third_party/abseil_cpp/absl/strings/match.h90
-rw-r--r--third_party/abseil_cpp/absl/strings/match_test.cc110
-rw-r--r--third_party/abseil_cpp/absl/strings/numbers.cc1083
-rw-r--r--third_party/abseil_cpp/absl/strings/numbers.h273
-rw-r--r--third_party/abseil_cpp/absl/strings/numbers_benchmark.cc286
-rw-r--r--third_party/abseil_cpp/absl/strings/numbers_test.cc1356
-rw-r--r--third_party/abseil_cpp/absl/strings/str_cat.cc246
-rw-r--r--third_party/abseil_cpp/absl/strings/str_cat.h408
-rw-r--r--third_party/abseil_cpp/absl/strings/str_cat_benchmark.cc187
-rw-r--r--third_party/abseil_cpp/absl/strings/str_cat_test.cc610
-rw-r--r--third_party/abseil_cpp/absl/strings/str_format.h813
-rw-r--r--third_party/abseil_cpp/absl/strings/str_format_test.cc774
-rw-r--r--third_party/abseil_cpp/absl/strings/str_join.h293
-rw-r--r--third_party/abseil_cpp/absl/strings/str_join_benchmark.cc97
-rw-r--r--third_party/abseil_cpp/absl/strings/str_join_test.cc474
-rw-r--r--third_party/abseil_cpp/absl/strings/str_replace.cc82
-rw-r--r--third_party/abseil_cpp/absl/strings/str_replace.h219
-rw-r--r--third_party/abseil_cpp/absl/strings/str_replace_benchmark.cc122
-rw-r--r--third_party/abseil_cpp/absl/strings/str_replace_test.cc341
-rw-r--r--third_party/abseil_cpp/absl/strings/str_split.cc139
-rw-r--r--third_party/abseil_cpp/absl/strings/str_split.h548
-rw-r--r--third_party/abseil_cpp/absl/strings/str_split_benchmark.cc180
-rw-r--r--third_party/abseil_cpp/absl/strings/str_split_test.cc953
-rw-r--r--third_party/abseil_cpp/absl/strings/string_view.cc235
-rw-r--r--third_party/abseil_cpp/absl/strings/string_view.h629
-rw-r--r--third_party/abseil_cpp/absl/strings/string_view_benchmark.cc381
-rw-r--r--third_party/abseil_cpp/absl/strings/string_view_test.cc1264
-rw-r--r--third_party/abseil_cpp/absl/strings/strip.h91
-rw-r--r--third_party/abseil_cpp/absl/strings/strip_test.cc198
-rw-r--r--third_party/abseil_cpp/absl/strings/substitute.cc171
-rw-r--r--third_party/abseil_cpp/absl/strings/substitute.h696
-rw-r--r--third_party/abseil_cpp/absl/strings/substitute_test.cc204
-rw-r--r--third_party/abseil_cpp/absl/synchronization/BUILD.bazel288
-rw-r--r--third_party/abseil_cpp/absl/synchronization/CMakeLists.txt216
-rw-r--r--third_party/abseil_cpp/absl/synchronization/barrier.cc52
-rw-r--r--third_party/abseil_cpp/absl/synchronization/barrier.h79
-rw-r--r--third_party/abseil_cpp/absl/synchronization/barrier_test.cc75
-rw-r--r--third_party/abseil_cpp/absl/synchronization/blocking_counter.cc57
-rw-r--r--third_party/abseil_cpp/absl/synchronization/blocking_counter.h99
-rw-r--r--third_party/abseil_cpp/absl/synchronization/blocking_counter_test.cc68
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/create_thread_identity.cc140
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/create_thread_identity.h60
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/futex.h154
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/graphcycles.cc698
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/graphcycles.h141
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/graphcycles_benchmark.cc44
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/graphcycles_test.cc464
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/kernel_timeout.h156
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem.cc106
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem.h115
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem_test.cc181
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/thread_pool.h93
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/waiter.cc428
-rw-r--r--third_party/abseil_cpp/absl/synchronization/internal/waiter.h155
-rw-r--r--third_party/abseil_cpp/absl/synchronization/lifetime_test.cc181
-rw-r--r--third_party/abseil_cpp/absl/synchronization/mutex.cc2740
-rw-r--r--third_party/abseil_cpp/absl/synchronization/mutex.h1084
-rw-r--r--third_party/abseil_cpp/absl/synchronization/mutex_benchmark.cc224
-rw-r--r--third_party/abseil_cpp/absl/synchronization/mutex_test.cc1706
-rw-r--r--third_party/abseil_cpp/absl/synchronization/notification.cc78
-rw-r--r--third_party/abseil_cpp/absl/synchronization/notification.h123
-rw-r--r--third_party/abseil_cpp/absl/synchronization/notification_test.cc133
-rw-r--r--third_party/abseil_cpp/absl/time/BUILD.bazel125
-rw-r--r--third_party/abseil_cpp/absl/time/CMakeLists.txt128
-rw-r--r--third_party/abseil_cpp/absl/time/civil_time.cc175
-rw-r--r--third_party/abseil_cpp/absl/time/civil_time.h538
-rw-r--r--third_party/abseil_cpp/absl/time/civil_time_benchmark.cc127
-rw-r--r--third_party/abseil_cpp/absl/time/civil_time_test.cc1243
-rw-r--r--third_party/abseil_cpp/absl/time/clock.cc567
-rw-r--r--third_party/abseil_cpp/absl/time/clock.h74
-rw-r--r--third_party/abseil_cpp/absl/time/clock_benchmark.cc74
-rw-r--r--third_party/abseil_cpp/absl/time/clock_test.cc118
-rw-r--r--third_party/abseil_cpp/absl/time/duration.cc954
-rw-r--r--third_party/abseil_cpp/absl/time/duration_benchmark.cc428
-rw-r--r--third_party/abseil_cpp/absl/time/duration_test.cc1808
-rw-r--r--third_party/abseil_cpp/absl/time/format.cc160
-rw-r--r--third_party/abseil_cpp/absl/time/format_benchmark.cc64
-rw-r--r--third_party/abseil_cpp/absl/time/format_test.cc441
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/BUILD.bazel171
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time.h332
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h628
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/time_zone.h386
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h102
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/cctz_benchmark.cc1030
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_detail.cc94
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_test.cc1066
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.cc140
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.h52
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format.cc1029
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format_test.cc1603
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.cc45
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.h76
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.cc113
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.h93
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.cc965
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.h137
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.cc315
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.h55
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup.cc187
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc1442
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.cc159
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.h132
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/tzfile.h122
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/src/zone_info_source.cc116
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/README.zoneinfo37
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/version1
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjanbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accrabin556 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Abababin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiersbin470 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmarabin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmerabin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamakobin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banguibin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjulbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissaubin149 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyrebin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzavillebin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumburabin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairobin1276 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablancabin1919 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceutabin562 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakrybin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakarbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaambin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djiboutibin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Doualabin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiunbin1830 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetownbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaboronebin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Hararebin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburgbin190 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Jubabin449 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampalabin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoumbin458 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigalibin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasabin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagosbin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Librevillebin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lomebin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luandabin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashibin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusakabin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabobin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputobin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maserubin190 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabanebin190 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishubin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monroviabin164 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobibin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamenabin160 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niameybin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchottbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougoubin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novobin131 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tomebin173 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktubin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripolibin431 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunisbin449 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoekbin638 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adakbin969 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchoragebin977 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anguillabin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Antiguabin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Araguainabin592 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Airesbin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarcabin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadaviabin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordobabin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuybin690 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Riojabin717 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendozabin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegosbin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Saltabin690 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juanbin717 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luisbin717 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucumanbin726 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaiabin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Arubabin151 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncionbin884 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokanbin224 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atkabin969 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahiabin682 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderasbin530 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Barbadosbin231 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belembin394 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belizebin638 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablonbin205 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vistabin430 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bogotabin179 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boisebin999 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Airesbin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Baybin768 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grandebin952 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cancunbin529 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Caracasbin190 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarcabin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayennebin151 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Caymanbin149 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chicagobin1754 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahuabin340 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbourbin224 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cordobabin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Ricabin232 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Crestonbin158 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiababin934 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Curacaobin151 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavnbin447 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawsonbin1029 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creekbin683 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Denverbin1042 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Detroitbin899 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dominicabin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Edmontonbin970 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepebin436 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvadorbin176 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenadabin1025 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelsonbin1448 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Waynebin531 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fortalezabin484 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Baybin880 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Godthabbin465 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Baybin1580 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turkbin862 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grenadabin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupebin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemalabin212 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquilbin179 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guyanabin172 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Halifaxbin1672 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Havanabin1117 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillobin286 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolisbin531 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knoxbin1016 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengobin567 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburgbin683 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_Citybin522 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevaybin369 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennesbin558 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamacbin612 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolisbin531 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvikbin701 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluitbin740 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaicabin339 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuybin690 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Juneaubin966 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisvillebin1242 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticellobin972 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_INbin1016 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijkbin151 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/La_Pazbin170 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Limabin283 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angelesbin1294 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Louisvillebin1242 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princesbin151 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Maceiobin502 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Managuabin295 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Manausbin412 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Marigotbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Martiniquebin178 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Matamorosbin437 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlanbin367 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mendozabin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Menomineebin917 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Meridabin303 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatlabin595 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_Citybin412 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelonbin550 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Monctonbin1493 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Monterreybin293 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideobin969 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montrealbin1717 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montserratbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nassaubin941 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/New_Yorkbin1744 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigonbin835 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nomebin975 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Noronhabin484 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulahbin1043 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Centerbin990 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salembin990 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nuukbin465 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinagabin484 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Panamabin149 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtungbin769 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribobin187 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenixbin240 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Princebin565 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spainbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acrebin418 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velhobin394 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Ricobin177 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenasbin1209 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_Riverbin835 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inletbin692 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Recifebin484 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Reginabin638 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Resolutebin692 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Brancobin418 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rosariobin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabelbin1025 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santarembin409 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santiagobin1282 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingobin317 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulobin952 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysundbin479 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprockbin1042 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sitkabin956 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemybin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johnsbin1878 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kittsbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Luciabin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomasbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincentbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Currentbin368 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpabin194 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thulebin455 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Baybin881 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuanabin1025 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Torontobin1717 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tortolabin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouverbin1330 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Virginbin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorsebin1029 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipegbin1294 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutatbin946 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknifebin729 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Caseybin243 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davisbin197 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrvillebin152 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquariebin976 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawsonbin152 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdobin1043 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmerbin887 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rotherabin132 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Polebin1043 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowabin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Trollbin177 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostokbin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyenbin676 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Adenbin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almatybin609 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ammanbin787 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyrbin743 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtaubin606 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobebin615 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabatbin375 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabadbin375 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyraubin616 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdadbin630 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrainbin152 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bakubin744 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkokbin152 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaulbin753 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirutbin732 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkekbin618 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bruneibin154 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcuttabin220 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chitabin750 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsanbin619 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqingbin393 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungkingbin393 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombobin247 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Daccabin231 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascusbin1047 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhakabin231 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dilibin170 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubaibin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbebin366 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagustabin940 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gazabin1195 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbinbin393 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebronbin1213 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minhbin236 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kongbin775 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovdbin594 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutskbin760 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbulbin1200 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakartabin248 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapurabin171 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalembin1056 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabulbin159 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatkabin727 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachibin266 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgarbin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandubin161 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandubin161 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandygabin775 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkatabin220 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarskbin741 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpurbin256 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuchingbin320 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwaitbin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macaobin791 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macaubin791 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadanbin751 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassarbin190 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manilabin238 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscatbin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosiabin597 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetskbin726 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirskbin753 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omskbin741 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oralbin625 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penhbin152 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianakbin247 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyangbin183 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatarbin152 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanaybin615 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylordabin624 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoonbin187 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadhbin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigonbin236 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalinbin755 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkandbin366 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoulbin415 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghaibin393 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singaporebin256 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymskbin742 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipeibin511 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkentbin366 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisibin629 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehranbin2004 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Avivbin1056 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbubin154 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphubin154 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyobin213 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomskbin753 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandangbin190 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatarbin594 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Batorbin594 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqibin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nerabin771 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientianebin152 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostokbin742 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutskbin741 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangonbin187 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburgbin760 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevanbin708 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azoresbin1435 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermudabin761 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canarybin478 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verdebin175 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroebin441 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroebin441 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayenbin676 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeirabin1435 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavikbin753 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgiabin132 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helenabin130 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanleybin789 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACTbin904 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaidebin921 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbanebin289 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hillbin941 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberrabin904 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Curriebin895 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwinbin234 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Euclabin314 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobartbin967 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHIbin692 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindemanbin325 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howebin692 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbournebin904 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSWbin904 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Northbin234 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perthbin306 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queenslandbin289 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Southbin921 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydneybin904 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmaniabin967 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoriabin904 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Westbin306 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinnabin941 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acrebin418 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronhabin484 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Eastbin952 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Westbin412 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/CETbin621 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/CST6CDTbin951 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlanticbin1672 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Centralbin1294 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Easternbin1717 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountainbin970 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundlandbin1878 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacificbin1330 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewanbin638 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukonbin1029 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continentalbin1282 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIslandbin1102 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Cubabin1117 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EETbin497 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/ESTbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EST5EDTbin951 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Egyptbin1276 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Eirebin1496 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMTbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0bin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1bin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2bin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3bin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4bin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5bin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6bin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7bin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8bin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9bin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0bin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10bin115 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11bin115 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12bin115 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13bin115 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14bin115 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9bin114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0bin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwichbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCTbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTCbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universalbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulubin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdambin1071 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorrabin389 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhanbin726 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athensbin682 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfastbin1599 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgradebin478 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlinbin705 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislavabin723 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brusselsbin1103 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharestbin661 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapestbin766 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingenbin497 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinaubin755 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagenbin623 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublinbin1496 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltarbin1220 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernseybin1599 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinkibin481 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Manbin1599 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbulbin1200 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jerseybin1599 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningradbin904 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kievbin549 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirovbin717 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbonbin1436 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljanabin478 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Londonbin1599 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourgbin1087 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madridbin897 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Maltabin928 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamnbin481 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minskbin808 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monacobin1114 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscowbin908 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosiabin597 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslobin676 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Parisbin1105 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgoricabin478 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Praguebin723 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rigabin694 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Romebin947 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samarabin732 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marinobin947 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevobin478 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratovbin726 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopolbin865 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopjebin478 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofiabin592 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholmbin497 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinnbin675 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiranebin604 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspolbin755 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovskbin760 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorodbin530 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduzbin497 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaticanbin947 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Viennabin658 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilniusbin676 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgogradbin726 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsawbin923 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagrebbin478 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhyebin560 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurichbin497 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Factorybin113 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GBbin1599 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GB-Eirebin1599 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMTbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT+0bin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT-0bin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT0bin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Greenwichbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/HSTbin112 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Hongkongbin775 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Icelandbin753 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivobin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagosbin152 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmasbin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocosbin140 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comorobin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelenbin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahebin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldivesbin152 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritiusbin179 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayottebin182 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunionbin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Iranbin2004 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Israelbin1056 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Jamaicabin339 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Japanbin213 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Kwajaleinbin219 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Libyabin431 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/METbin621 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MSTbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MST7MDTbin951 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNortebin1025 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSurbin367 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/Generalbin412 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/NZbin1043 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHATbin808 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Navajobin1042 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/PRCbin393 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/PST8PDTbin951 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apiabin268 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Aucklandbin1043 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainvillebin201 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chathambin808 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuukbin195 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easterbin1102 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efatebin324 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderburybin172 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofobin153 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fijibin419 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafutibin134 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagosbin175 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambierbin132 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanalbin134 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guambin350 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulubin221 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnstonbin221 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimatibin174 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosraebin242 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajaleinbin219 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majurobin218 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesasbin139 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midwaybin146 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Naurubin183 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niuebin175 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolkbin247 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumeabin198 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pagobin146 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palaubin148 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairnbin153 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpeibin214 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponapebin214 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresbybin154 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotongabin391 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipanbin350 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoabin146 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahitibin133 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawabin134 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapubin237 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Trukbin195 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wakebin134 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallisbin134 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yapbin195 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Polandbin923 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Portugalbin1436 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/ROCbin511 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/ROKbin415 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Singaporebin256 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Turkeybin1200 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/UCTbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Alaskabin977 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutianbin969 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Arizonabin240 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Centralbin1754 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indianabin531 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Easternbin1744 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaiibin221 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starkebin1016 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Michiganbin899 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Mountainbin1042 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Pacificbin1294 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Samoabin146 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/UTCbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Universalbin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/W-SUbin908 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/WETbin494 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Zulubin111 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab274
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/localtimebin148 -> 0 bytes
-rw-r--r--third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab384
-rw-r--r--third_party/abseil_cpp/absl/time/internal/get_current_time_chrono.inc31
-rw-r--r--third_party/abseil_cpp/absl/time/internal/get_current_time_posix.inc24
-rw-r--r--third_party/abseil_cpp/absl/time/internal/test_util.cc131
-rw-r--r--third_party/abseil_cpp/absl/time/internal/test_util.h33
-rw-r--r--third_party/abseil_cpp/absl/time/internal/zoneinfo.inc729
-rw-r--r--third_party/abseil_cpp/absl/time/time.cc500
-rw-r--r--third_party/abseil_cpp/absl/time/time.h1581
-rw-r--r--third_party/abseil_cpp/absl/time/time_benchmark.cc316
-rw-r--r--third_party/abseil_cpp/absl/time/time_test.cc1280
-rw-r--r--third_party/abseil_cpp/absl/time/time_zone_test.cc97
-rw-r--r--third_party/abseil_cpp/absl/types/BUILD.bazel336
-rw-r--r--third_party/abseil_cpp/absl/types/CMakeLists.txt373
-rw-r--r--third_party/abseil_cpp/absl/types/any.h528
-rw-r--r--third_party/abseil_cpp/absl/types/any_exception_safety_test.cc173
-rw-r--r--third_party/abseil_cpp/absl/types/any_test.cc781
-rw-r--r--third_party/abseil_cpp/absl/types/bad_any_cast.cc46
-rw-r--r--third_party/abseil_cpp/absl/types/bad_any_cast.h75
-rw-r--r--third_party/abseil_cpp/absl/types/bad_optional_access.cc48
-rw-r--r--third_party/abseil_cpp/absl/types/bad_optional_access.h78
-rw-r--r--third_party/abseil_cpp/absl/types/bad_variant_access.cc64
-rw-r--r--third_party/abseil_cpp/absl/types/bad_variant_access.h82
-rw-r--r--third_party/abseil_cpp/absl/types/compare.h600
-rw-r--r--third_party/abseil_cpp/absl/types/compare_test.cc389
-rw-r--r--third_party/abseil_cpp/absl/types/internal/conformance_aliases.h447
-rw-r--r--third_party/abseil_cpp/absl/types/internal/conformance_archetype.h978
-rw-r--r--third_party/abseil_cpp/absl/types/internal/conformance_profile.h931
-rw-r--r--third_party/abseil_cpp/absl/types/internal/conformance_testing.h1386
-rw-r--r--third_party/abseil_cpp/absl/types/internal/conformance_testing_helpers.h391
-rw-r--r--third_party/abseil_cpp/absl/types/internal/conformance_testing_test.cc1556
-rw-r--r--third_party/abseil_cpp/absl/types/internal/optional.h396
-rw-r--r--third_party/abseil_cpp/absl/types/internal/parentheses.h34
-rw-r--r--third_party/abseil_cpp/absl/types/internal/span.h128
-rw-r--r--third_party/abseil_cpp/absl/types/internal/transform_args.h246
-rw-r--r--third_party/abseil_cpp/absl/types/internal/variant.h1646
-rw-r--r--third_party/abseil_cpp/absl/types/optional.h776
-rw-r--r--third_party/abseil_cpp/absl/types/optional_exception_safety_test.cc292
-rw-r--r--third_party/abseil_cpp/absl/types/optional_test.cc1659
-rw-r--r--third_party/abseil_cpp/absl/types/span.h726
-rw-r--r--third_party/abseil_cpp/absl/types/span_test.cc846
-rw-r--r--third_party/abseil_cpp/absl/types/variant.h866
-rw-r--r--third_party/abseil_cpp/absl/types/variant_benchmark.cc222
-rw-r--r--third_party/abseil_cpp/absl/types/variant_exception_safety_test.cc532
-rw-r--r--third_party/abseil_cpp/absl/types/variant_test.cc2718
-rw-r--r--third_party/abseil_cpp/absl/utility/BUILD.bazel55
-rw-r--r--third_party/abseil_cpp/absl/utility/CMakeLists.txt44
-rw-r--r--third_party/abseil_cpp/absl/utility/utility.h350
-rw-r--r--third_party/abseil_cpp/absl/utility/utility_test.cc376
-rw-r--r--third_party/abseil_cpp/ci/absl_alternate_options.h29
-rw-r--r--third_party/abseil_cpp/ci/cmake_common.sh25
-rwxr-xr-xthird_party/abseil_cpp/ci/cmake_install_test.sh37
-rwxr-xr-xthird_party/abseil_cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh99
-rwxr-xr-xthird_party/abseil_cpp/ci/linux_clang-latest_libcxx_bazel.sh97
-rwxr-xr-xthird_party/abseil_cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh95
-rwxr-xr-xthird_party/abseil_cpp/ci/linux_clang-latest_libstdcxx_bazel.sh91
-rw-r--r--third_party/abseil_cpp/ci/linux_docker_containers.sh21
-rwxr-xr-xthird_party/abseil_cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh89
-rwxr-xr-xthird_party/abseil_cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh95
-rwxr-xr-xthird_party/abseil_cpp/ci/linux_gcc-latest_libstdcxx_cmake.sh65
-rwxr-xr-xthird_party/abseil_cpp/ci/linux_gcc_alpine_cmake.sh64
-rwxr-xr-xthird_party/abseil_cpp/ci/macos_xcode_bazel.sh54
-rwxr-xr-xthird_party/abseil_cpp/ci/macos_xcode_cmake.sh56
-rwxr-xr-xthird_party/abseil_cpp/conanfile.py51
-rw-r--r--third_party/abseil_cpp/default.nix31
-rw-r--r--third_party/agenix/default.nix14
-rw-r--r--third_party/alsi/OWNERS3
-rw-r--r--third_party/alsi/default.nix25
-rw-r--r--third_party/arion/OWNERS3
-rw-r--r--third_party/arion/default.nix8
-rw-r--r--third_party/bat_syntaxes/default.nix5
-rw-r--r--third_party/bufbuild/default.nix29
-rw-r--r--third_party/buzz/default.nix30
-rw-r--r--third_party/cgit/AUTHORS2
-rw-r--r--third_party/cgit/Makefile6
-rw-r--r--third_party/cgit/README31
-rw-r--r--third_party/cgit/cache.c52
-rw-r--r--third_party/cgit/cgit.c23
-rw-r--r--third_party/cgit/cgit.css40
-rw-r--r--third_party/cgit/cgit.h29
-rw-r--r--third_party/cgit/cgit.mk27
-rw-r--r--third_party/cgit/cgitrc.5.txt46
-rw-r--r--third_party/cgit/default.nix26
-rw-r--r--third_party/cgit/favicon.icobin1078 -> 0 bytes
-rw-r--r--third_party/cgit/filter.c239
-rw-r--r--third_party/cgit/filters/email-gravatar.lua35
-rwxr-xr-xthird_party/cgit/filters/email-gravatar.py3
-rw-r--r--third_party/cgit/filters/email-libravatar.lua36
-rw-r--r--third_party/cgit/filters/file-authentication.lua359
-rw-r--r--third_party/cgit/filters/gentoo-ldap-authentication.lua360
-rw-r--r--third_party/cgit/filters/owner-example.lua17
-rw-r--r--third_party/cgit/filters/simple-authentication.lua314
-rw-r--r--third_party/cgit/html.c4
-rw-r--r--third_party/cgit/parsing.c2
-rw-r--r--third_party/cgit/robots.txt1
-rw-r--r--third_party/cgit/scan-tree.c4
-rw-r--r--third_party/cgit/shared.c29
-rw-r--r--third_party/cgit/tests/filters/dump.lua17
-rwxr-xr-xthird_party/cgit/tests/setup.sh19
-rwxr-xr-xthird_party/cgit/tests/t0105-commit.sh6
-rwxr-xr-xthird_party/cgit/tests/t0106-diff.sh4
-rwxr-xr-xthird_party/cgit/tests/t0111-filter.sh3
-rw-r--r--third_party/cgit/ui-atom.c36
-rw-r--r--third_party/cgit/ui-blame.c19
-rw-r--r--third_party/cgit/ui-blob.c10
-rw-r--r--third_party/cgit/ui-commit.c8
-rw-r--r--third_party/cgit/ui-diff.c44
-rw-r--r--third_party/cgit/ui-log.c31
-rw-r--r--third_party/cgit/ui-patch.c6
-rw-r--r--third_party/cgit/ui-plain.c6
-rw-r--r--third_party/cgit/ui-repolist.c4
-rw-r--r--third_party/cgit/ui-shared.c41
-rw-r--r--third_party/cgit/ui-snapshot.c17
-rw-r--r--third_party/cgit/ui-stats.c5
-rw-r--r--third_party/cgit/ui-summary.c3
-rw-r--r--third_party/cgit/ui-tag.c6
-rw-r--r--third_party/cgit/ui-tree.c20
-rw-r--r--third_party/clj2nix/OWNERS4
-rw-r--r--third_party/clj2nix/default.nix3
-rw-r--r--third_party/ddclient/default.nix12
-rw-r--r--third_party/ddclient/module.nix230
-rw-r--r--third_party/ddclient/pkg.nix45
-rw-r--r--third_party/default.nix57
-rw-r--r--third_party/dfmt/default.nix36
-rw-r--r--third_party/dhall/OWNERS3
-rw-r--r--third_party/dhall/default.nix22
-rw-r--r--third_party/elmPackages_0_18/default.nix18
-rw-r--r--third_party/exwm/.elpaignore1
-rw-r--r--third_party/exwm/LICENSE674
-rw-r--r--third_party/exwm/default.nix14
-rw-r--r--third_party/exwm/exwm-background.el199
-rw-r--r--third_party/exwm/exwm-cm.el50
-rw-r--r--third_party/exwm/exwm-config.el2
-rw-r--r--third_party/exwm/exwm-core.el60
-rw-r--r--third_party/exwm/exwm-floating.el23
-rw-r--r--third_party/exwm/exwm-input.el245
-rw-r--r--third_party/exwm/exwm-layout.el63
-rw-r--r--third_party/exwm/exwm-manage.el115
-rw-r--r--third_party/exwm/exwm-randr.el12
-rw-r--r--third_party/exwm/exwm-systemtray.el276
-rw-r--r--third_party/exwm/exwm-workspace.el486
-rw-r--r--third_party/exwm/exwm-xim.el11
-rw-r--r--third_party/exwm/exwm-xsettings.el336
-rw-r--r--third_party/exwm/exwm.el194
-rw-r--r--third_party/farmhash/default.nix13
-rw-r--r--third_party/geesefs/default.nix25
-rwxr-xr-xthird_party/gerrit-queue/.buildkite/build.sh4
-rw-r--r--third_party/gerrit-queue/.buildkite/pipeline.yml13
-rw-r--r--third_party/gerrit-queue/.gitignore4
-rw-r--r--third_party/gerrit-queue/LICENSE201
-rw-r--r--third_party/gerrit-queue/README.md80
-rw-r--r--third_party/gerrit-queue/default.nix14
-rw-r--r--third_party/gerrit-queue/frontend/frontend.go113
-rw-r--r--third_party/gerrit-queue/frontend/templates/changeset.tmpl.html15
-rw-r--r--third_party/gerrit-queue/frontend/templates/index.tmpl.html76
-rw-r--r--third_party/gerrit-queue/frontend/templates/serie.tmpl.html19
-rw-r--r--third_party/gerrit-queue/gerrit/changeset.go117
-rw-r--r--third_party/gerrit-queue/gerrit/client.go220
-rw-r--r--third_party/gerrit-queue/gerrit/serie.go112
-rw-r--r--third_party/gerrit-queue/gerrit/series.go126
-rw-r--r--third_party/gerrit-queue/go.mod10
-rw-r--r--third_party/gerrit-queue/go.sum69
-rw-r--r--third_party/gerrit-queue/main.go137
-rw-r--r--third_party/gerrit-queue/misc/rotatingloghandler.go34
-rw-r--r--third_party/gerrit-queue/submitqueue/runner.go220
-rw-r--r--third_party/gerrit/0001-Syntax-highlight-nix.patch37
-rw-r--r--third_party/gerrit/0001-Use-detzip-in-download_bower.py.patch25
-rw-r--r--third_party/gerrit/0002-Syntax-highlight-nix.patch24
-rw-r--r--third_party/gerrit/0002-Syntax-highlight-rules.pl.patch37
-rw-r--r--third_party/gerrit/0003-Add-titles-to-CLs-over-HTTP.patch215
-rw-r--r--third_party/gerrit/0003-Syntax-highlight-rules.pl.patch46
-rw-r--r--third_party/gerrit/0004-Add-titles-to-CLs-over-HTTP.patch217
-rw-r--r--third_party/gerrit/0005-When-using-local-fonts-always-assume-Gerrit-is-mount.patch26
-rw-r--r--third_party/gerrit/0006-Always-use-Google-Fonts.patch28
-rw-r--r--third_party/gerrit/default.nix58
-rw-r--r--third_party/gerrit_plugins/builder.nix64
-rw-r--r--third_party/gerrit_plugins/code-owners/default.nix17
-rw-r--r--third_party/gerrit_plugins/code-owners/using-usernames.patch472
-rw-r--r--third_party/gerrit_plugins/default.nix22
-rw-r--r--third_party/gerrit_plugins/oauth/cas-6x.patch69
-rw-r--r--third_party/gerrit_plugins/oauth/default.nix17
-rw-r--r--third_party/git/0001-feat-third_party-git-date-add-dottime-format.patch50
-rw-r--r--third_party/git/default.nix4
-rw-r--r--third_party/gitignoreSource/default.nix17
-rw-r--r--third_party/glog/.bazelci/presubmit.yml25
-rw-r--r--third_party/glog/.gitignore12
-rwxr-xr-xthird_party/glog/.travis.ubuntu.sh18
-rw-r--r--third_party/glog/.travis.yml119
-rw-r--r--third_party/glog/AUTHORS26
-rw-r--r--third_party/glog/BUILD5
-rw-r--r--third_party/glog/CMakeLists.txt848
-rw-r--r--third_party/glog/CONTRIBUTING.md58
-rw-r--r--third_party/glog/CONTRIBUTORS48
-rw-r--r--third_party/glog/COPYING65
-rw-r--r--third_party/glog/ChangeLog99
-rw-r--r--third_party/glog/Dockerfile.ubuntu.template14
-rw-r--r--third_party/glog/INSTALL313
-rw-r--r--third_party/glog/Makefile.am259
-rw-r--r--third_party/glog/README.md10
-rw-r--r--third_party/glog/README.windows17
-rw-r--r--third_party/glog/WORKSPACE10
-rw-r--r--third_party/glog/appveyor.yml71
-rwxr-xr-xthird_party/glog/autogen.sh5
-rw-r--r--third_party/glog/bazel/example/BUILD8
-rw-r--r--third_party/glog/bazel/example/main.cc22
-rw-r--r--third_party/glog/bazel/glog.bzl219
-rw-r--r--third_party/glog/cmake/DetermineGflagsNamespace.cmake69
-rw-r--r--third_party/glog/cmake/FindUnwind.cmake78
-rw-r--r--third_party/glog/cmake/GetCacheVariables.cmake63
-rw-r--r--third_party/glog/cmake/INSTALL.md81
-rw-r--r--third_party/glog/cmake/TestInitPackageConfig.cmake12
-rw-r--r--third_party/glog/cmake/TestPackageConfig.cmake33
-rw-r--r--third_party/glog/configure.ac246
-rw-r--r--third_party/glog/default.nix21
-rw-r--r--third_party/glog/doc/designstyle.css115
-rw-r--r--third_party/glog/doc/glog.html631
-rw-r--r--third_party/glog/glog-config.cmake.in13
-rw-r--r--third_party/glog/glog-modules.cmake.in18
-rw-r--r--third_party/glog/libglog.pc.in10
-rw-r--r--third_party/glog/m4/ac_have_attribute.m416
-rw-r--r--third_party/glog/m4/ac_have_builtin_expect.m414
-rw-r--r--third_party/glog/m4/ac_have_sync_val_compare_and_swap.m414
-rw-r--r--third_party/glog/m4/ac_rwlock.m431
-rw-r--r--third_party/glog/m4/acx_pthread.m4363
-rw-r--r--third_party/glog/m4/google_namespace.m436
-rw-r--r--third_party/glog/m4/ltsugar.m4123
-rw-r--r--third_party/glog/m4/lt~obsolete.m498
-rw-r--r--third_party/glog/m4/namespaces.m415
-rw-r--r--third_party/glog/m4/pc_from_ucontext.m471
-rw-r--r--third_party/glog/m4/stl_namespace.m425
-rw-r--r--third_party/glog/m4/using_operator.m415
-rwxr-xr-xthird_party/glog/packages/deb.sh73
-rw-r--r--third_party/glog/packages/deb/README7
-rw-r--r--third_party/glog/packages/deb/changelog71
-rw-r--r--third_party/glog/packages/deb/compat1
-rw-r--r--third_party/glog/packages/deb/control23
-rw-r--r--third_party/glog/packages/deb/copyright35
-rw-r--r--third_party/glog/packages/deb/docs7
-rw-r--r--third_party/glog/packages/deb/libgoogle-glog-dev.dirs4
-rw-r--r--third_party/glog/packages/deb/libgoogle-glog-dev.install10
-rw-r--r--third_party/glog/packages/deb/libgoogle-glog0.dirs1
-rw-r--r--third_party/glog/packages/deb/libgoogle-glog0.install2
-rwxr-xr-xthird_party/glog/packages/deb/rules117
-rwxr-xr-xthird_party/glog/packages/rpm.sh75
-rw-r--r--third_party/glog/packages/rpm/rpm.spec72
-rw-r--r--third_party/glog/src/base/commandlineflags.h133
-rw-r--r--third_party/glog/src/base/googleinit.h51
-rw-r--r--third_party/glog/src/base/mutex.h333
-rw-r--r--third_party/glog/src/config.h.cmake.in219
-rw-r--r--third_party/glog/src/config_for_unittests.h66
-rw-r--r--third_party/glog/src/demangle.cc1356
-rw-r--r--third_party/glog/src/demangle.h85
-rw-r--r--third_party/glog/src/demangle_unittest.cc168
-rwxr-xr-xthird_party/glog/src/demangle_unittest.sh95
-rw-r--r--third_party/glog/src/demangle_unittest.txt145
-rw-r--r--third_party/glog/src/glog/log_severity.h107
-rw-r--r--third_party/glog/src/glog/logging.h.in1689
-rw-r--r--third_party/glog/src/glog/raw_logging.h.in180
-rw-r--r--third_party/glog/src/glog/stl_logging.h.in220
-rw-r--r--third_party/glog/src/glog/vlog_is_on.h.in129
-rw-r--r--third_party/glog/src/googletest.h611
-rw-r--r--third_party/glog/src/logging.cc2344
-rwxr-xr-xthird_party/glog/src/logging_striplog_test.sh79
-rw-r--r--third_party/glog/src/logging_striptest10.cc35
-rw-r--r--third_party/glog/src/logging_striptest2.cc35
-rw-r--r--third_party/glog/src/logging_striptest_main.cc73
-rw-r--r--third_party/glog/src/logging_unittest.cc1348
-rw-r--r--third_party/glog/src/logging_unittest.err307
-rw-r--r--third_party/glog/src/mock-log.h156
-rw-r--r--third_party/glog/src/mock-log_test.cc106
-rw-r--r--third_party/glog/src/package_config_unittest/working_config/CMakeLists.txt8
-rw-r--r--third_party/glog/src/package_config_unittest/working_config/glog_package_config.cc6
-rw-r--r--third_party/glog/src/raw_logging.cc158
-rw-r--r--third_party/glog/src/signalhandler.cc403
-rw-r--r--third_party/glog/src/signalhandler_unittest.cc112
-rwxr-xr-xthird_party/glog/src/signalhandler_unittest.sh131
-rw-r--r--third_party/glog/src/stacktrace.h61
-rw-r--r--third_party/glog/src/stacktrace_generic-inl.h59
-rw-r--r--third_party/glog/src/stacktrace_libunwind-inl.h90
-rw-r--r--third_party/glog/src/stacktrace_powerpc-inl.h130
-rw-r--r--third_party/glog/src/stacktrace_unittest.cc217
-rw-r--r--third_party/glog/src/stacktrace_windows-inl.h50
-rw-r--r--third_party/glog/src/stacktrace_x86-inl.h146
-rw-r--r--third_party/glog/src/stacktrace_x86_64-inl.h105
-rw-r--r--third_party/glog/src/stl_logging_unittest.cc197
-rw-r--r--third_party/glog/src/symbolize.cc966
-rw-r--r--third_party/glog/src/symbolize.h158
-rw-r--r--third_party/glog/src/symbolize_unittest.cc425
-rw-r--r--third_party/glog/src/utilities.cc374
-rw-r--r--third_party/glog/src/utilities.h238
-rw-r--r--third_party/glog/src/utilities_unittest.cc58
-rw-r--r--third_party/glog/src/vlog_is_on.cc257
-rwxr-xr-xthird_party/glog/src/windows/config.h21
-rw-r--r--third_party/glog/src/windows/dirent.h1160
-rw-r--r--third_party/glog/src/windows/glog/log_severity.h96
-rwxr-xr-xthird_party/glog/src/windows/glog/logging.h1690
-rwxr-xr-xthird_party/glog/src/windows/glog/raw_logging.h184
-rwxr-xr-xthird_party/glog/src/windows/glog/stl_logging.h224
-rwxr-xr-xthird_party/glog/src/windows/glog/vlog_is_on.h133
-rwxr-xr-xthird_party/glog/src/windows/port.cc65
-rwxr-xr-xthird_party/glog/src/windows/port.h174
-rwxr-xr-xthird_party/glog/src/windows/preprocess.sh119
-rw-r--r--third_party/glog/toolchains/.gitignore2
-rw-r--r--third_party/glog/toolchains/clang-cxx17.cmake13
-rw-r--r--third_party/glog/toolchains/gcc-cxx11.cmake13
-rw-r--r--third_party/glog/toolchains/gcc-cxx17.cmake13
-rw-r--r--third_party/glog/toolchains/gcc-cxx98.cmake13
-rw-r--r--third_party/glog/toolchains/gcc-gnuxx11.cmake13
-rw-r--r--third_party/glog/toolchains/linux-mingw-w64-cxx11.cmake33
-rw-r--r--third_party/glog/toolchains/linux-mingw-w64-cxx17.cmake33
-rw-r--r--third_party/glog/toolchains/linux-mingw-w64-gnuxx11.cmake33
-rw-r--r--third_party/glog/toolchains/mingw-cxx11.cmake13
-rw-r--r--third_party/glog/toolchains/mingw-cxx17.cmake13
-rw-r--r--third_party/glog/toolchains/mingw-gnuxx11.cmake13
-rw-r--r--third_party/glog/toolchains/vs-14-2015-sdk-8-1.cmake2
-rw-r--r--third_party/glog/toolchains/vs-14-2015-win64.cmake1
-rw-r--r--third_party/glog/toolchains/vs-15-2017-win64-cxx17.cmake2
-rw-r--r--third_party/glog/toolchains/vs-15-2017-win64.cmake1
-rw-r--r--third_party/gopkgs/github.com/charmbracelet/bubbletea/default.nix10
-rw-r--r--third_party/grpc/default.nix13
-rw-r--r--third_party/gtest/default.nix12
-rw-r--r--third_party/hii/OWNERS4
-rw-r--r--third_party/impermanence/default.nix12
-rw-r--r--third_party/irccat/default.nix2
-rw-r--r--third_party/josh/default.nix25
-rw-r--r--third_party/lisp/OWNERS7
-rw-r--r--third_party/lisp/bordeaux-threads.nix3
-rw-r--r--third_party/lisp/cl-change-case.nix22
-rw-r--r--third_party/lisp/cl-fad.nix4
-rw-r--r--third_party/lisp/cl-json.nix53
-rw-r--r--third_party/lisp/cl-plus-ssl.nix14
-rw-r--r--third_party/lisp/cl-ppcre.nix12
-rw-r--r--third_party/lisp/cl-unicode.nix6
-rw-r--r--third_party/lisp/cl-yacc.nix6
-rw-r--r--third_party/lisp/closure-common.nix11
-rw-r--r--third_party/lisp/easy-routes.nix3
-rw-r--r--third_party/lisp/flexi-streams.nix2
-rw-r--r--third_party/lisp/global-vars.nix2
-rw-r--r--third_party/lisp/hunchentoot.nix5
-rw-r--r--third_party/lisp/ironclad.nix5
-rw-r--r--third_party/lisp/lass.nix3
-rw-r--r--third_party/lisp/lisp-binary.nix20
-rw-r--r--third_party/lisp/local-time.nix3
-rw-r--r--third_party/lisp/mime4cl/OWNERS4
-rw-r--r--third_party/lisp/mime4cl/README7
-rw-r--r--third_party/lisp/mime4cl/README.md27
-rw-r--r--third_party/lisp/mime4cl/address.lisp34
-rw-r--r--third_party/lisp/mime4cl/default.nix12
-rw-r--r--third_party/lisp/mime4cl/endec.lisp146
-rw-r--r--third_party/lisp/mime4cl/ex-sclf.lisp329
-rw-r--r--third_party/lisp/mime4cl/mime.lisp188
-rw-r--r--third_party/lisp/mime4cl/package.lisp18
-rw-r--r--third_party/lisp/mime4cl/streams.lisp323
-rw-r--r--third_party/lisp/mime4cl/test/endec.lisp30
-rw-r--r--third_party/lisp/mime4cl/test/mime.lisp39
-rw-r--r--third_party/lisp/mime4cl/test/package.lisp2
-rw-r--r--third_party/lisp/mime4cl/test/rt.lisp20
-rw-r--r--third_party/lisp/mime4cl/test/samples/sample1.msg (renamed from third_party/lisp/mime4cl/test/sample1.msg)0
-rw-r--r--third_party/lisp/mime4cl/test/temp-file.lisp72
-rw-r--r--third_party/lisp/nibbles.nix3
-rw-r--r--third_party/lisp/npg/OWNERS4
-rw-r--r--third_party/lisp/postmodern.nix3
-rw-r--r--third_party/lisp/qbase64/coreutils-base64.patch13
-rw-r--r--third_party/lisp/qbase64/default.nix57
-rw-r--r--third_party/lisp/routes.nix3
-rw-r--r--third_party/lisp/s-xml/default.nix18
-rw-r--r--third_party/lisp/sclf/.skip-subtree1
-rw-r--r--third_party/lisp/sclf/OWNERS3
-rw-r--r--third_party/lisp/sclf/README6
-rw-r--r--third_party/lisp/sclf/default.nix28
-rw-r--r--third_party/lisp/sclf/directory.lisp404
-rw-r--r--third_party/lisp/sclf/lazy.lisp134
-rw-r--r--third_party/lisp/sclf/mp/README6
-rw-r--r--third_party/lisp/sclf/mp/cmu.lisp115
-rw-r--r--third_party/lisp/sclf/mp/sbcl.lisp235
-rw-r--r--third_party/lisp/sclf/package.lisp258
-rw-r--r--third_party/lisp/sclf/sclf.asd58
-rw-r--r--third_party/lisp/sclf/sclf.lisp1717
-rw-r--r--third_party/lisp/sclf/serial.lisp62
-rw-r--r--third_party/lisp/sclf/sysproc.lisp295
-rw-r--r--third_party/lisp/sclf/time.lisp311
-rw-r--r--third_party/lisp/str.nix49
-rw-r--r--third_party/lisp/trivial-ldap.nix6
-rw-r--r--third_party/lisp/trivial-mimes.nix5
-rw-r--r--third_party/lisp/uax-15.nix5
-rw-r--r--third_party/lisp/unix-opts.nix2
-rw-r--r--third_party/lisp/usocket-server.nix3
-rw-r--r--third_party/lisp/usocket.nix3
-rw-r--r--third_party/naersk/default.nix9
-rw-r--r--third_party/napalm/default.nix7
-rw-r--r--third_party/nix/.clang-format11
-rw-r--r--third_party/nix/.clang-tidy4
-rw-r--r--third_party/nix/.dir-locals.el1
-rw-r--r--third_party/nix/.github/ISSUE_TEMPLATE.md27
-rw-r--r--third_party/nix/.gitignore119
-rw-r--r--third_party/nix/.skip-subtree1
-rw-r--r--third_party/nix/.travis.yml2
-rw-r--r--third_party/nix/.version1
-rw-r--r--third_party/nix/CMakeLists.txt77
-rw-r--r--third_party/nix/COPYING504
-rw-r--r--third_party/nix/OWNERS5
-rw-r--r--third_party/nix/README.md179
-rw-r--r--third_party/nix/clangd.nix30
-rw-r--r--third_party/nix/config.h.in130
-rwxr-xr-xthird_party/nix/config/config.sub1818
-rwxr-xr-xthird_party/nix/config/install-sh527
-rwxr-xr-xthird_party/nix/contrib/stack-collapse.py38
-rw-r--r--third_party/nix/corepkgs/buildenv.nix25
-rw-r--r--third_party/nix/corepkgs/config.nix.in29
-rw-r--r--third_party/nix/corepkgs/derivation.nix27
-rw-r--r--third_party/nix/corepkgs/fetchurl.nix41
-rw-r--r--third_party/nix/corepkgs/imported-drv-to-derivation.nix21
-rw-r--r--third_party/nix/corepkgs/unpack-channel.nix39
-rw-r--r--third_party/nix/default.nix237
-rw-r--r--third_party/nix/doc/manual/advanced-topics/advanced-topics.xml14
-rw-r--r--third_party/nix/doc/manual/advanced-topics/cores-vs-jobs.xml121
-rw-r--r--third_party/nix/doc/manual/advanced-topics/diff-hook.xml205
-rw-r--r--third_party/nix/doc/manual/advanced-topics/distributed-builds.xml190
-rw-r--r--third_party/nix/doc/manual/advanced-topics/post-build-hook.xml160
-rw-r--r--third_party/nix/doc/manual/command-ref/command-ref.xml20
-rw-r--r--third_party/nix/doc/manual/command-ref/conf-file.xml1202
-rw-r--r--third_party/nix/doc/manual/command-ref/env-common.xml202
-rw-r--r--third_party/nix/doc/manual/command-ref/files.xml14
-rw-r--r--third_party/nix/doc/manual/command-ref/main-commands.xml17
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-build.xml190
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-channel.xml178
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-collect-garbage.xml63
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-copy-closure.xml169
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-daemon.xml51
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-env.xml1505
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-hash.xml176
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-instantiate.xml278
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-prefetch-url.xml131
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-shell.xml397
-rw-r--r--third_party/nix/doc/manual/command-ref/nix-store.xml1525
-rw-r--r--third_party/nix/doc/manual/command-ref/opt-common-syn.xml64
-rw-r--r--third_party/nix/doc/manual/command-ref/opt-common.xml366
-rw-r--r--third_party/nix/doc/manual/command-ref/opt-inst-syn.xml22
-rw-r--r--third_party/nix/doc/manual/command-ref/utilities.xml20
-rw-r--r--third_party/nix/doc/manual/expressions/advanced-attributes.xml340
-rw-r--r--third_party/nix/doc/manual/expressions/arguments-variables.xml121
-rw-r--r--third_party/nix/doc/manual/expressions/build-script.xml119
-rw-r--r--third_party/nix/doc/manual/expressions/builder-syntax.xml119
-rw-r--r--third_party/nix/doc/manual/expressions/builtins.xml1658
-rw-r--r--third_party/nix/doc/manual/expressions/derivations.xml211
-rw-r--r--third_party/nix/doc/manual/expressions/expression-language.xml30
-rw-r--r--third_party/nix/doc/manual/expressions/expression-syntax.xml148
-rw-r--r--third_party/nix/doc/manual/expressions/generic-builder.xml98
-rw-r--r--third_party/nix/doc/manual/expressions/language-constructs.xml409
-rw-r--r--third_party/nix/doc/manual/expressions/language-operators.xml222
-rw-r--r--third_party/nix/doc/manual/expressions/language-values.xml313
-rw-r--r--third_party/nix/doc/manual/expressions/simple-building-testing.xml84
-rw-r--r--third_party/nix/doc/manual/expressions/simple-expression.xml47
-rw-r--r--third_party/nix/doc/manual/expressions/writing-nix-expressions.xml26
-rw-r--r--third_party/nix/doc/manual/figures/user-environments.pngbin85031 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/figures/user-environments.sxdbin8412 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/glossary/glossary.xml199
-rw-r--r--third_party/nix/doc/manual/hacking.xml41
-rw-r--r--third_party/nix/doc/manual/images/callouts/1.gifbin889 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/10.gifbin929 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/11.gifbin202 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/12.gifbin210 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/13.gifbin209 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/14.gifbin205 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/15.gifbin210 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/2.gifbin907 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/3.gifbin914 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/4.gifbin907 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/5.gifbin916 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/6.gifbin218 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/7.gifbin907 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/8.gifbin918 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/images/callouts/9.gifbin923 -> 0 bytes
-rw-r--r--third_party/nix/doc/manual/installation/building-source.xml49
-rw-r--r--third_party/nix/doc/manual/installation/env-variables.xml89
-rw-r--r--third_party/nix/doc/manual/installation/installation.xml34
-rw-r--r--third_party/nix/doc/manual/installation/installing-binary.xml190
-rw-r--r--third_party/nix/doc/manual/installation/installing-source.xml16
-rw-r--r--third_party/nix/doc/manual/installation/multi-user.xml107
-rw-r--r--third_party/nix/doc/manual/installation/nix-security.xml27
-rw-r--r--third_party/nix/doc/manual/installation/obtaining-source.xml30
-rw-r--r--third_party/nix/doc/manual/installation/prerequisites-source.xml105
-rw-r--r--third_party/nix/doc/manual/installation/single-user.xml21
-rw-r--r--third_party/nix/doc/manual/installation/supported-platforms.xml36
-rw-r--r--third_party/nix/doc/manual/installation/upgrading.xml22
-rw-r--r--third_party/nix/doc/manual/introduction/about-nix.xml268
-rw-r--r--third_party/nix/doc/manual/introduction/introduction.xml12
-rw-r--r--third_party/nix/doc/manual/introduction/quick-start.xml124
-rw-r--r--third_party/nix/doc/manual/manual.xml52
-rw-r--r--third_party/nix/doc/manual/nix-lang-ref.xml182
-rw-r--r--third_party/nix/doc/manual/packages/basic-package-mgmt.xml194
-rw-r--r--third_party/nix/doc/manual/packages/binary-cache-substituter.xml70
-rw-r--r--third_party/nix/doc/manual/packages/channels.xml57
-rw-r--r--third_party/nix/doc/manual/packages/copy-closure.xml50
-rw-r--r--third_party/nix/doc/manual/packages/garbage-collection.xml86
-rw-r--r--third_party/nix/doc/manual/packages/garbage-collector-roots.xml29
-rw-r--r--third_party/nix/doc/manual/packages/package-management.xml23
-rw-r--r--third_party/nix/doc/manual/packages/profiles.xml158
-rw-r--r--third_party/nix/doc/manual/packages/s3-substituter.xml182
-rw-r--r--third_party/nix/doc/manual/packages/sharing-packages.xml20
-rw-r--r--third_party/nix/doc/manual/packages/ssh-substituter.xml73
-rw-r--r--third_party/nix/doc/manual/quote-literals.xsl40
-rw-r--r--third_party/nix/doc/manual/release-notes/release-notes.xml51
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.10.1.xml13
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.10.xml323
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.11.xml261
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.12.xml175
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.13.xml106
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.14.xml46
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.15.xml14
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.16.xml55
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.5.xml11
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.6.xml122
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.7.xml35
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.8.1.xml21
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.8.xml246
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.9.1.xml13
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.9.2.xml28
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-0.9.xml98
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.0.xml119
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.1.xml100
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.10.xml64
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.11.10.xml31
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.11.xml141
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.2.xml157
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.3.xml19
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.4.xml39
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.5.1.xml12
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.5.2.xml12
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.5.xml12
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.6.1.xml69
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.6.xml127
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.7.xml263
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.8.xml123
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-1.9.xml216
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-2.0.xml1012
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-2.1.xml133
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-2.2.xml143
-rw-r--r--third_party/nix/doc/manual/release-notes/rl-2.3.xml91
-rw-r--r--third_party/nix/doc/manual/schemas.xml4
-rw-r--r--third_party/nix/misc/systemd/nix-daemon.service.in12
-rw-r--r--third_party/nix/misc/systemd/nix-daemon.socket.in11
-rwxr-xr-xthird_party/nix/scripts/build.sh24
-rwxr-xr-xthird_party/nix/scripts/daemon.sh24
-rwxr-xr-xthird_party/nix/scripts/eval.sh23
-rw-r--r--third_party/nix/scripts/install-darwin-multi-user.sh144
-rw-r--r--third_party/nix/scripts/install-multi-user.sh798
-rw-r--r--third_party/nix/scripts/install-nix-from-closure.sh180
-rwxr-xr-xthird_party/nix/scripts/install-systemd-multi-user.sh188
-rw-r--r--third_party/nix/scripts/install.in66
-rwxr-xr-xthird_party/nix/scripts/nix-http-export.cgi.in51
-rw-r--r--third_party/nix/scripts/nix-profile-daemon.sh.in29
-rw-r--r--third_party/nix/scripts/nix-profile.sh.in39
-rwxr-xr-xthird_party/nix/scripts/nix-reduce-build.in171
-rwxr-xr-xthird_party/nix/scripts/repl.sh23
-rwxr-xr-xthird_party/nix/scripts/setup_store.sh11
-rw-r--r--third_party/nix/src/CMakeLists.txt85
-rw-r--r--third_party/nix/src/build-remote/build-remote.cc274
-rw-r--r--third_party/nix/src/cpptoml/LICENSE18
-rw-r--r--third_party/nix/src/cpptoml/cpptoml.h3668
-rw-r--r--third_party/nix/src/libexpr/CMakeLists.txt85
-rw-r--r--third_party/nix/src/libexpr/attr-path.cc109
-rw-r--r--third_party/nix/src/libexpr/attr-path.hh13
-rw-r--r--third_party/nix/src/libexpr/attr-set.cc111
-rw-r--r--third_party/nix/src/libexpr/attr-set.hh69
-rw-r--r--third_party/nix/src/libexpr/common-eval-args.cc72
-rw-r--r--third_party/nix/src/libexpr/common-eval-args.hh26
-rw-r--r--third_party/nix/src/libexpr/eval-inline.hh90
-rw-r--r--third_party/nix/src/libexpr/eval.cc1878
-rw-r--r--third_party/nix/src/libexpr/eval.hh365
-rw-r--r--third_party/nix/src/libexpr/function-trace.cc19
-rw-r--r--third_party/nix/src/libexpr/function-trace.hh14
-rw-r--r--third_party/nix/src/libexpr/get-drvs.cc446
-rw-r--r--third_party/nix/src/libexpr/get-drvs.hh83
-rw-r--r--third_party/nix/src/libexpr/json-to-value.cc152
-rw-r--r--third_party/nix/src/libexpr/json-to-value.hh13
-rw-r--r--third_party/nix/src/libexpr/lexer.l193
-rw-r--r--third_party/nix/src/libexpr/names.cc121
-rw-r--r--third_party/nix/src/libexpr/names.hh31
-rw-r--r--third_party/nix/src/libexpr/nix-expr.pc.in10
-rw-r--r--third_party/nix/src/libexpr/nixexpr.cc414
-rw-r--r--third_party/nix/src/libexpr/nixexpr.hh361
-rw-r--r--third_party/nix/src/libexpr/parser.cc332
-rw-r--r--third_party/nix/src/libexpr/parser.hh100
-rw-r--r--third_party/nix/src/libexpr/parser.y359
-rw-r--r--third_party/nix/src/libexpr/primops.cc2336
-rw-r--r--third_party/nix/src/libexpr/primops.hh17
-rw-r--r--third_party/nix/src/libexpr/primops/context.cc202
-rw-r--r--third_party/nix/src/libexpr/primops/fetchGit.cc277
-rw-r--r--third_party/nix/src/libexpr/primops/fetchMercurial.cc246
-rw-r--r--third_party/nix/src/libexpr/primops/fromTOML.cc94
-rw-r--r--third_party/nix/src/libexpr/symbol-table.cc24
-rw-r--r--third_party/nix/src/libexpr/symbol-table.hh69
-rw-r--r--third_party/nix/src/libexpr/value-to-json.cc91
-rw-r--r--third_party/nix/src/libexpr/value-to-json.hh19
-rw-r--r--third_party/nix/src/libexpr/value-to-xml.cc184
-rw-r--r--third_party/nix/src/libexpr/value-to-xml.hh14
-rw-r--r--third_party/nix/src/libexpr/value.cc121
-rw-r--r--third_party/nix/src/libexpr/value.hh191
-rw-r--r--third_party/nix/src/libmain/CMakeLists.txt33
-rw-r--r--third_party/nix/src/libmain/common-args.cc56
-rw-r--r--third_party/nix/src/libmain/common-args.hh27
-rw-r--r--third_party/nix/src/libmain/nix-main.pc.in9
-rw-r--r--third_party/nix/src/libmain/shared.cc386
-rw-r--r--third_party/nix/src/libmain/shared.hh134
-rw-r--r--third_party/nix/src/libmain/stack.cc75
-rw-r--r--third_party/nix/src/libstore/CMakeLists.txt127
-rw-r--r--third_party/nix/src/libstore/binary-cache-store.cc396
-rw-r--r--third_party/nix/src/libstore/binary-cache-store.hh115
-rw-r--r--third_party/nix/src/libstore/build.cc4820
-rw-r--r--third_party/nix/src/libstore/builtins.hh11
-rw-r--r--third_party/nix/src/libstore/builtins/buildenv.cc240
-rw-r--r--third_party/nix/src/libstore/builtins/fetchurl.cc93
-rw-r--r--third_party/nix/src/libstore/crypto.cc138
-rw-r--r--third_party/nix/src/libstore/crypto.hh49
-rw-r--r--third_party/nix/src/libstore/derivations.cc520
-rw-r--r--third_party/nix/src/libstore/derivations.hh130
-rw-r--r--third_party/nix/src/libstore/download.cc1024
-rw-r--r--third_party/nix/src/libstore/download.hh133
-rw-r--r--third_party/nix/src/libstore/export-import.cc111
-rw-r--r--third_party/nix/src/libstore/fs-accessor.hh31
-rw-r--r--third_party/nix/src/libstore/gc.cc997
-rw-r--r--third_party/nix/src/libstore/globals.cc178
-rw-r--r--third_party/nix/src/libstore/globals.hh464
-rw-r--r--third_party/nix/src/libstore/http-binary-cache-store.cc171
-rw-r--r--third_party/nix/src/libstore/legacy-ssh-store.cc282
-rw-r--r--third_party/nix/src/libstore/local-binary-cache-store.cc93
-rw-r--r--third_party/nix/src/libstore/local-fs-store.cc123
-rw-r--r--third_party/nix/src/libstore/local-store.cc1519
-rw-r--r--third_party/nix/src/libstore/local-store.hh319
-rw-r--r--third_party/nix/src/libstore/machines.cc114
-rw-r--r--third_party/nix/src/libstore/machines.hh36
-rw-r--r--third_party/nix/src/libstore/misc.cc331
-rw-r--r--third_party/nix/src/libstore/mock-binary-cache-store.cc91
-rw-r--r--third_party/nix/src/libstore/mock-binary-cache-store.hh59
-rw-r--r--third_party/nix/src/libstore/nar-accessor.cc268
-rw-r--r--third_party/nix/src/libstore/nar-accessor.hh29
-rw-r--r--third_party/nix/src/libstore/nar-info-disk-cache.cc295
-rw-r--r--third_party/nix/src/libstore/nar-info-disk-cache.hh30
-rw-r--r--third_party/nix/src/libstore/nar-info.cc142
-rw-r--r--third_party/nix/src/libstore/nar-info.hh23
-rw-r--r--third_party/nix/src/libstore/nix-store.pc.in9
-rw-r--r--third_party/nix/src/libstore/optimise-store.cc296
-rw-r--r--third_party/nix/src/libstore/parsed-derivations.cc128
-rw-r--r--third_party/nix/src/libstore/parsed-derivations.hh34
-rw-r--r--third_party/nix/src/libstore/pathlocks.cc172
-rw-r--r--third_party/nix/src/libstore/pathlocks.hh35
-rw-r--r--third_party/nix/src/libstore/profiles.cc252
-rw-r--r--third_party/nix/src/libstore/profiles.hh61
-rw-r--r--third_party/nix/src/libstore/references.cc126
-rw-r--r--third_party/nix/src/libstore/references.hh11
-rw-r--r--third_party/nix/src/libstore/remote-fs-accessor.cc133
-rw-r--r--third_party/nix/src/libstore/remote-fs-accessor.hh38
-rw-r--r--third_party/nix/src/libstore/remote-store.cc686
-rw-r--r--third_party/nix/src/libstore/remote-store.hh141
-rw-r--r--third_party/nix/src/libstore/rpc-store.cc549
-rw-r--r--third_party/nix/src/libstore/rpc-store.hh129
-rw-r--r--third_party/nix/src/libstore/s3-binary-cache-store.cc431
-rw-r--r--third_party/nix/src/libstore/s3-binary-cache-store.hh27
-rw-r--r--third_party/nix/src/libstore/s3.hh42
-rw-r--r--third_party/nix/src/libstore/sandbox-defaults.sb87
-rw-r--r--third_party/nix/src/libstore/sandbox-minimal.sb5
-rw-r--r--third_party/nix/src/libstore/sandbox-network.sb16
-rw-r--r--third_party/nix/src/libstore/schema.sql42
-rw-r--r--third_party/nix/src/libstore/serve-protocol.hh24
-rw-r--r--third_party/nix/src/libstore/sqlite.cc195
-rw-r--r--third_party/nix/src/libstore/sqlite.hh109
-rw-r--r--third_party/nix/src/libstore/ssh-store.cc89
-rw-r--r--third_party/nix/src/libstore/ssh.cc160
-rw-r--r--third_party/nix/src/libstore/ssh.hh41
-rw-r--r--third_party/nix/src/libstore/store-api.cc1167
-rw-r--r--third_party/nix/src/libstore/store-api.hh816
-rw-r--r--third_party/nix/src/libstore/worker-protocol.hh68
-rw-r--r--third_party/nix/src/libutil/CMakeLists.txt68
-rw-r--r--third_party/nix/src/libutil/affinity.cc60
-rw-r--r--third_party/nix/src/libutil/affinity.hh9
-rw-r--r--third_party/nix/src/libutil/archive.cc398
-rw-r--r--third_party/nix/src/libutil/archive.hh77
-rw-r--r--third_party/nix/src/libutil/args.cc219
-rw-r--r--third_party/nix/src/libutil/args.hh221
-rw-r--r--third_party/nix/src/libutil/compression.cc400
-rw-r--r--third_party/nix/src/libutil/compression.hh31
-rw-r--r--third_party/nix/src/libutil/config.cc370
-rw-r--r--third_party/nix/src/libutil/config.hh228
-rw-r--r--third_party/nix/src/libutil/finally.hh13
-rw-r--r--third_party/nix/src/libutil/hash.cc484
-rw-r--r--third_party/nix/src/libutil/hash.hh147
-rw-r--r--third_party/nix/src/libutil/istringstream_nocopy.hh85
-rw-r--r--third_party/nix/src/libutil/json.cc198
-rw-r--r--third_party/nix/src/libutil/json.hh144
-rw-r--r--third_party/nix/src/libutil/lazy.hh45
-rw-r--r--third_party/nix/src/libutil/lru-cache.hh90
-rw-r--r--third_party/nix/src/libutil/monitor-fd.hh57
-rw-r--r--third_party/nix/src/libutil/pool.hh176
-rw-r--r--third_party/nix/src/libutil/proto.hh174
-rw-r--r--third_party/nix/src/libutil/ref.hh65
-rw-r--r--third_party/nix/src/libutil/serialise.cc311
-rw-r--r--third_party/nix/src/libutil/serialise.hh289
-rw-r--r--third_party/nix/src/libutil/status.hh17
-rw-r--r--third_party/nix/src/libutil/sync.hh84
-rw-r--r--third_party/nix/src/libutil/thread-pool.cc163
-rw-r--r--third_party/nix/src/libutil/thread-pool.hh140
-rw-r--r--third_party/nix/src/libutil/types.hh118
-rw-r--r--third_party/nix/src/libutil/util.cc1426
-rw-r--r--third_party/nix/src/libutil/util.hh476
-rw-r--r--third_party/nix/src/libutil/visitor.hh19
-rw-r--r--third_party/nix/src/libutil/xml-writer.cc93
-rw-r--r--third_party/nix/src/libutil/xml-writer.hh52
-rw-r--r--third_party/nix/src/nix-build/nix-build.cc581
-rw-r--r--third_party/nix/src/nix-channel/nix-channel.cc275
-rw-r--r--third_party/nix/src/nix-collect-garbage/nix-collect-garbage.cc103
-rw-r--r--third_party/nix/src/nix-copy-closure/nix-copy-closure.cc73
-rw-r--r--third_party/nix/src/nix-daemon/CMakeLists.txt29
-rw-r--r--third_party/nix/src/nix-daemon/nix-daemon-legacy.cc1185
-rw-r--r--third_party/nix/src/nix-daemon/nix-daemon-proto.cc799
-rw-r--r--third_party/nix/src/nix-daemon/nix-daemon-proto.hh12
-rw-r--r--third_party/nix/src/nix-daemon/nix-daemon.cc201
-rw-r--r--third_party/nix/src/nix-env/nix-env.cc1543
-rw-r--r--third_party/nix/src/nix-env/user-env.cc169
-rw-r--r--third_party/nix/src/nix-env/user-env.hh12
-rw-r--r--third_party/nix/src/nix-instantiate/nix-instantiate.cc219
-rw-r--r--third_party/nix/src/nix-prefetch-url/nix-prefetch-url.cc253
-rw-r--r--third_party/nix/src/nix-store/dotgraph.cc141
-rw-r--r--third_party/nix/src/nix-store/dotgraph.hh11
-rw-r--r--third_party/nix/src/nix-store/graphml.cc80
-rw-r--r--third_party/nix/src/nix-store/graphml.hh11
-rw-r--r--third_party/nix/src/nix-store/nix-store.cc1302
-rw-r--r--third_party/nix/src/nix/add-to-store.cc51
-rw-r--r--third_party/nix/src/nix/build.cc68
-rw-r--r--third_party/nix/src/nix/cat.cc56
-rw-r--r--third_party/nix/src/nix/command.cc156
-rw-r--r--third_party/nix/src/nix/command.hh194
-rw-r--r--third_party/nix/src/nix/copy.cc86
-rw-r--r--third_party/nix/src/nix/doctor.cc142
-rw-r--r--third_party/nix/src/nix/dump-path.cc28
-rw-r--r--third_party/nix/src/nix/edit.cc75
-rw-r--r--third_party/nix/src/nix/eval.cc56
-rw-r--r--third_party/nix/src/nix/hash.cc152
-rw-r--r--third_party/nix/src/nix/installables.cc349
-rw-r--r--third_party/nix/src/nix/legacy.cc7
-rw-r--r--third_party/nix/src/nix/legacy.hh23
-rw-r--r--third_party/nix/src/nix/log.cc63
-rw-r--r--third_party/nix/src/nix/ls.cc137
-rw-r--r--third_party/nix/src/nix/main.cc185
-rw-r--r--third_party/nix/src/nix/optimise-store.cc27
-rw-r--r--third_party/nix/src/nix/path-info.cc133
-rw-r--r--third_party/nix/src/nix/ping-store.cc25
-rw-r--r--third_party/nix/src/nix/repl.cc819
-rw-r--r--third_party/nix/src/nix/run.cc283
-rw-r--r--third_party/nix/src/nix/search.cc276
-rw-r--r--third_party/nix/src/nix/show-config.cc31
-rw-r--r--third_party/nix/src/nix/show-derivation.cc113
-rw-r--r--third_party/nix/src/nix/sigs.cc146
-rw-r--r--third_party/nix/src/nix/upgrade-nix.cc167
-rw-r--r--third_party/nix/src/nix/verify.cc171
-rw-r--r--third_party/nix/src/nix/why-depends.cc269
-rw-r--r--third_party/nix/src/nlohmann/json.hpp20406
-rw-r--r--third_party/nix/src/proto/CMakeLists.txt37
-rw-r--r--third_party/nix/src/proto/worker.proto374
-rw-r--r--third_party/nix/src/tests/CMakeLists.txt78
-rw-r--r--third_party/nix/src/tests/arbitrary.hh176
-rw-r--r--third_party/nix/src/tests/attr-set.cc71
-rw-r--r--third_party/nix/src/tests/derivations_test.cc109
-rw-r--r--third_party/nix/src/tests/dummy-store.hh48
-rw-r--r--third_party/nix/src/tests/hash_test.cc101
-rw-r--r--third_party/nix/src/tests/lang/disabled/README.txt7
-rw-r--r--third_party/nix/src/tests/lang/disabled/eval-okay-search-path.nix11
-rw-r--r--third_party/nix/src/tests/lang/disabled/eval-okay-xml.exp52
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-autoargs.flags1
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-fromjson.nix36
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-functionargs.exp1
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-functionargs.nix89
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-getenv.nix1
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-import.nix11
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-ind-string.exp1
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-replacestrings.exp1
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-sort.exp1
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-sort.nix8
-rw-r--r--third_party/nix/src/tests/lang/eval-okay-types.exp1
-rw-r--r--third_party/nix/src/tests/lang/evalstore-okay-context-introspection.nix24
-rw-r--r--third_party/nix/src/tests/lang/evalstore-okay-context.exp1
-rw-r--r--third_party/nix/src/tests/lang/evalstore-okay-context.nix6
-rw-r--r--third_party/nix/src/tests/lang/parse-fail-dup-attrs-1.nix5
-rw-r--r--third_party/nix/src/tests/language-tests.cc290
-rw-r--r--third_party/nix/src/tests/references_test.cc74
-rw-r--r--third_party/nix/src/tests/status_helpers.h83
-rw-r--r--third_party/nix/src/tests/store-api-test.cc28
-rw-r--r--third_party/nix/src/tests/store-util.hh76
-rw-r--r--third_party/nix/src/tests/store_tests.cc122
-rw-r--r--third_party/nix/src/tests/value-to-json.cc257
-rw-r--r--third_party/nix/test-vm.nix19
-rw-r--r--third_party/nix/tests/add.sh28
-rw-r--r--third_party/nix/tests/binary-cache.sh170
-rw-r--r--third_party/nix/tests/brotli.sh21
-rw-r--r--third_party/nix/tests/build-dry.sh52
-rw-r--r--third_party/nix/tests/build-hook.nix23
-rw-r--r--third_party/nix/tests/build-remote.sh24
-rw-r--r--third_party/nix/tests/case-hack.sh19
-rw-r--r--third_party/nix/tests/case.narbin2416 -> 0 bytes
-rw-r--r--third_party/nix/tests/check-refs.nix70
-rw-r--r--third_party/nix/tests/check-refs.sh42
-rw-r--r--third_party/nix/tests/check-reqs.nix57
-rw-r--r--third_party/nix/tests/check-reqs.sh16
-rw-r--r--third_party/nix/tests/check.nix22
-rw-r--r--third_party/nix/tests/check.sh47
-rw-r--r--third_party/nix/tests/common.sh.in118
-rw-r--r--third_party/nix/tests/config.nix20
-rw-r--r--third_party/nix/tests/dependencies.builder0.sh16
-rw-r--r--third_party/nix/tests/dependencies.builder1.sh2
-rw-r--r--third_party/nix/tests/dependencies.builder2.sh2
-rw-r--r--third_party/nix/tests/dependencies.nix24
-rw-r--r--third_party/nix/tests/dependencies.sh52
-rw-r--r--third_party/nix/tests/dump-db.sh20
-rw-r--r--third_party/nix/tests/export-graph.nix29
-rw-r--r--third_party/nix/tests/export-graph.sh30
-rw-r--r--third_party/nix/tests/export.sh36
-rw-r--r--third_party/nix/tests/fetchGit.sh141
-rw-r--r--third_party/nix/tests/fetchMercurial.sh93
-rw-r--r--third_party/nix/tests/fetchurl.sh78
-rw-r--r--third_party/nix/tests/filter-source.nix12
-rw-r--r--third_party/nix/tests/filter-source.sh19
-rw-r--r--third_party/nix/tests/fixed.builder1.sh3
-rw-r--r--third_party/nix/tests/fixed.builder2.sh6
-rw-r--r--third_party/nix/tests/fixed.nix50
-rw-r--r--third_party/nix/tests/fixed.sh56
-rwxr-xr-xthird_party/nix/tests/function-trace.sh85
-rw-r--r--third_party/nix/tests/gc-auto.sh70
-rw-r--r--third_party/nix/tests/gc-concurrent.builder.sh13
-rw-r--r--third_party/nix/tests/gc-concurrent.nix27
-rw-r--r--third_party/nix/tests/gc-concurrent.sh58
-rw-r--r--third_party/nix/tests/gc-concurrent2.builder.sh7
-rw-r--r--third_party/nix/tests/gc-runtime.nix17
-rw-r--r--third_party/nix/tests/gc-runtime.sh38
-rw-r--r--third_party/nix/tests/gc.sh40
-rw-r--r--third_party/nix/tests/hash-check.nix29
-rw-r--r--third_party/nix/tests/hash.sh87
-rw-r--r--third_party/nix/tests/import-derivation.nix26
-rw-r--r--third_party/nix/tests/import-derivation.sh12
-rw-r--r--third_party/nix/tests/init.sh34
-rwxr-xr-xthird_party/nix/tests/install-darwin.sh96
-rw-r--r--third_party/nix/tests/lang.sh68
-rw-r--r--third_party/nix/tests/linux-sandbox.sh30
-rw-r--r--third_party/nix/tests/logging.sh15
-rw-r--r--third_party/nix/tests/misc.sh19
-rw-r--r--third_party/nix/tests/multiple-outputs.nix68
-rw-r--r--third_party/nix/tests/multiple-outputs.sh76
-rw-r--r--third_party/nix/tests/nar-access.nix23
-rw-r--r--third_party/nix/tests/nar-access.sh44
-rw-r--r--third_party/nix/tests/nix-build.sh25
-rw-r--r--third_party/nix/tests/nix-channel.sh59
-rw-r--r--third_party/nix/tests/nix-copy-closure.nix64
-rw-r--r--third_party/nix/tests/nix-copy-ssh.sh20
-rw-r--r--third_party/nix/tests/nix-profile.sh9
-rw-r--r--third_party/nix/tests/nix-shell.sh57
-rw-r--r--third_party/nix/tests/optimise-store.sh43
-rw-r--r--third_party/nix/tests/parallel.builder.sh29
-rw-r--r--third_party/nix/tests/parallel.nix19
-rw-r--r--third_party/nix/tests/parallel.sh56
-rw-r--r--third_party/nix/tests/pass-as-file.sh18
-rw-r--r--third_party/nix/tests/placeholders.sh20
-rw-r--r--third_party/nix/tests/post-hook.sh15
-rw-r--r--third_party/nix/tests/pure-eval.nix3
-rw-r--r--third_party/nix/tests/pure-eval.sh18
-rwxr-xr-xthird_party/nix/tests/push-to-store.sh4
-rw-r--r--third_party/nix/tests/referrers.sh36
-rw-r--r--third_party/nix/tests/remote-builds.nix108
-rw-r--r--third_party/nix/tests/remote-store.sh19
-rw-r--r--third_party/nix/tests/repair.sh77
-rw-r--r--third_party/nix/tests/restricted.nix1
-rw-r--r--third_party/nix/tests/restricted.sh51
-rw-r--r--third_party/nix/tests/run.nix17
-rw-r--r--third_party/nix/tests/run.sh28
-rw-r--r--third_party/nix/tests/search.nix25
-rw-r--r--third_party/nix/tests/search.sh43
-rw-r--r--third_party/nix/tests/secure-drv-outputs.nix23
-rw-r--r--third_party/nix/tests/secure-drv-outputs.sh36
-rw-r--r--third_party/nix/tests/setuid.nix108
-rw-r--r--third_party/nix/tests/shell.nix56
-rw-r--r--third_party/nix/tests/shell.shebang.rb7
-rwxr-xr-xthird_party/nix/tests/shell.shebang.sh4
-rw-r--r--third_party/nix/tests/signing.sh105
-rw-r--r--third_party/nix/tests/simple.builder.sh11
-rw-r--r--third_party/nix/tests/simple.nix8
-rw-r--r--third_party/nix/tests/simple.sh25
-rw-r--r--third_party/nix/tests/structured-attrs.nix66
-rw-r--r--third_party/nix/tests/structured-attrs.sh7
-rw-r--r--third_party/nix/tests/tarball.sh28
-rw-r--r--third_party/nix/tests/timeout.nix31
-rw-r--r--third_party/nix/tests/timeout.sh40
-rw-r--r--third_party/nix/tests/user-envs.builder.sh5
-rw-r--r--third_party/nix/tests/user-envs.nix29
-rw-r--r--third_party/nix/tests/user-envs.sh181
-rw-r--r--third_party/nixery/default.nix18
-rw-r--r--third_party/nixpkgs/default.nix81
-rw-r--r--third_party/overlays/dhall/OWNERS1
-rw-r--r--third_party/overlays/dhall/default.nix30
-rw-r--r--third_party/overlays/ecl-static.nix9
-rw-r--r--third_party/overlays/emacs.nix11
-rw-r--r--third_party/overlays/haskell/.skip-subtree1
-rw-r--r--third_party/overlays/haskell/OWNERS2
-rw-r--r--third_party/overlays/haskell/default.nix69
-rw-r--r--third_party/overlays/haskell/extra-pkgs/brick-0.73.nix70
-rw-r--r--third_party/overlays/haskell/extra-pkgs/pa-error-tree-0.1.0.0.nix10
-rw-r--r--third_party/overlays/haskell/extra-pkgs/pa-field-parser.nix39
-rw-r--r--third_party/overlays/haskell/extra-pkgs/pa-json.nix43
-rw-r--r--third_party/overlays/haskell/extra-pkgs/pa-label.nix10
-rw-r--r--third_party/overlays/haskell/extra-pkgs/pa-prelude.nix43
-rw-r--r--third_party/overlays/haskell/extra-pkgs/pa-pretty-0.1.1.0.nix29
-rw-r--r--third_party/overlays/haskell/extra-pkgs/pa-run-command-0.1.0.0.nix25
-rw-r--r--third_party/overlays/haskell/extra-pkgs/random-fu-0.2.nix41
-rw-r--r--third_party/overlays/haskell/extra-pkgs/rvar-0.2.nix25
-rw-r--r--third_party/overlays/patches/.skip-tree1
-rw-r--r--third_party/overlays/patches/0001-configure-ac-version.patch13
-rw-r--r--third_party/overlays/patches/buf-tests-dont-use-file-transport.patch64
-rw-r--r--third_party/overlays/patches/cbtemulator-uds.patch140
-rw-r--r--third_party/overlays/patches/clickhouse-support-reading-arrow-LargeListArray.patch106
-rw-r--r--third_party/overlays/patches/crate2nix-run-tests-in-build-source.patch69
-rw-r--r--third_party/overlays/patches/crate2nix-tests-debug.patch12
-rw-r--r--third_party/overlays/patches/evans-add-support-for-unix-domain-sockets.patch39
-rw-r--r--third_party/overlays/patches/tpm2-pkcs11-190-dbupgrade.patch29
-rw-r--r--third_party/overlays/tvl.nix113
-rw-r--r--third_party/prometheus-fail2ban-exporter/default.nix5
-rw-r--r--third_party/protobuf/default.nix12
-rw-r--r--third_party/public-inbox/0001-feat-always-set-the-List-ID-header-even-in-watch.patch30
-rw-r--r--third_party/public-inbox/default.nix9
-rw-r--r--third_party/python/broadlink/default.nix3
-rw-r--r--third_party/rapidcheck/default.nix21
-rw-r--r--third_party/re2/default.nix5
-rw-r--r--third_party/rust-crates/OWNERS8
-rw-r--r--third_party/rust-crates/default.nix96
-rw-r--r--third_party/rustsec-advisory-db/default.nix27
-rw-r--r--third_party/rustsec-advisory-db/pin.json11
-rw-r--r--third_party/smtprelay/default.nix2
-rw-r--r--third_party/sources/default.nix151
-rw-r--r--third_party/sources/sources.json110
-rw-r--r--third_party/terraform-provider-glesys/default.nix7
-rw-r--r--tools/checks/default.nix38
-rw-r--r--tools/cheddar/Cargo.lock1107
-rw-r--r--tools/cheddar/Cargo.toml8
-rw-r--r--tools/cheddar/build.rs9
-rw-r--r--tools/cheddar/default.nix12
-rw-r--r--tools/cheddar/src/bin/cheddar.rs19
-rw-r--r--tools/cheddar/src/lib.rs41
-rw-r--r--tools/cheddar/src/tests.rs7
-rw-r--r--tools/crate2nix-generate.nix8
-rw-r--r--tools/crfo-approve.nix52
-rw-r--r--tools/depot-build.nix8
-rw-r--r--tools/depot-deps.nix35
-rw-r--r--tools/depot-nixpkgs-update.nix44
-rw-r--r--tools/depot-scanner/OWNERS3
-rw-r--r--tools/depot-scanner/default.nix16
-rw-r--r--tools/depot-scanner/depot_scanner.proto46
-rw-r--r--tools/depot-scanner/go.mod3
-rw-r--r--tools/depot-scanner/main.go227
-rw-r--r--tools/depotfmt.nix20
-rw-r--r--tools/emacs-pkgs/FSF_OWNERS6
-rw-r--r--tools/emacs-pkgs/buildEmacsPackage.nix30
-rw-r--r--tools/emacs-pkgs/notable/OWNERS3
-rw-r--r--tools/emacs-pkgs/notable/default.nix4
-rw-r--r--tools/emacs-pkgs/passively/OWNERS4
-rw-r--r--tools/emacs-pkgs/passively/README.md2
-rw-r--r--tools/emacs-pkgs/term-switcher/term-switcher.el40
-rw-r--r--tools/emacs-pkgs/treecrumbs/OWNERS2
-rw-r--r--tools/emacs-pkgs/treecrumbs/default.nix7
-rw-r--r--tools/emacs-pkgs/treecrumbs/treecrumbs.el202
-rw-r--r--tools/emacs-pkgs/tvl/OWNERS4
-rw-r--r--tools/emacs-pkgs/tvl/tvl.el34
-rw-r--r--tools/eprintf.nix12
-rw-r--r--tools/fetch-depot-inbox.nix49
-rw-r--r--tools/git-r.nix138
-rw-r--r--tools/hash-password.nix16
-rw-r--r--tools/magrathea/default.nix26
-rw-r--r--tools/magrathea/mg.scm159
-rw-r--r--tools/nixery/.gitignore12
-rw-r--r--tools/nixery/.skip-subtree1
-rw-r--r--tools/nixery/LICENSE202
-rw-r--r--tools/nixery/README.md156
-rw-r--r--tools/nixery/builder/archive.go104
-rw-r--r--tools/nixery/builder/builder.go527
-rw-r--r--tools/nixery/builder/builder_test.go112
-rw-r--r--tools/nixery/builder/cache.go225
-rw-r--r--tools/nixery/cmd/server/main.go283
-rw-r--r--tools/nixery/config/config.go73
-rw-r--r--tools/nixery/config/pkgsource.go148
-rw-r--r--tools/nixery/default.nix129
-rw-r--r--tools/nixery/go.mod14
-rw-r--r--tools/nixery/go.sum708
-rw-r--r--tools/nixery/layers/layers.go354
-rw-r--r--tools/nixery/logs/logs.go108
-rw-r--r--tools/nixery/manifest/manifest.go135
-rw-r--r--tools/nixery/popcount/README.md39
-rw-r--r--tools/nixery/popcount/default.nix13
-rw-r--r--tools/nixery/popcount/popcount.go280
-rw-r--r--tools/nixery/prepare-image/default.nix18
-rw-r--r--tools/nixery/prepare-image/load-pkgs.nix36
-rw-r--r--tools/nixery/prepare-image/prepare-image.nix198
-rwxr-xr-xtools/nixery/scripts/integration-test.sh59
-rw-r--r--tools/nixery/shell.nix13
-rw-r--r--tools/nixery/storage/filesystem.go99
-rw-r--r--tools/nixery/storage/gcs.go231
-rw-r--r--tools/nixery/storage/storage.go40
-rw-r--r--tools/nixery/web/index.html166
-rw-r--r--tools/nixery/web/nixery-logo.pngbin0 -> 194098 bytes
-rw-r--r--tools/nsfv-setup/default.nix3
-rw-r--r--tools/releases/default.nix37
-rw-r--r--tools/rust-crates-advisory/OWNERS5
-rw-r--r--tools/rust-crates-advisory/check-security-advisory.rs67
-rw-r--r--tools/rust-crates-advisory/default.nix185
-rw-r--r--tools/rust-crates-advisory/format-audit-result.jq75
-rw-r--r--tools/tvlc/OWNERS3
-rw-r--r--tools/tvlc/common.sh33
-rw-r--r--tools/tvlc/default.nix50
-rwxr-xr-xtools/tvlc/tvlc-new103
-rw-r--r--tvix/.envrc10
-rw-r--r--tvix/.gitignore3
-rw-r--r--tvix/.vscode/extensions.json2
-rw-r--r--tvix/Cargo.lock4976
-rw-r--r--tvix/Cargo.nix17645
-rw-r--r--tvix/Cargo.toml46
-rw-r--r--tvix/OWNERS12
-rw-r--r--tvix/README.md111
-rw-r--r--tvix/boot/README.md136
-rw-r--r--tvix/boot/default.nix113
-rw-r--r--tvix/boot/tests/default.nix138
-rw-r--r--tvix/boot/tvix-init.go138
-rw-r--r--tvix/build-go/LICENSE21
-rw-r--r--tvix/build-go/README.md10
-rw-r--r--tvix/build-go/build.pb.go670
-rw-r--r--tvix/build-go/default.nix31
-rw-r--r--tvix/build-go/go.mod19
-rw-r--r--tvix/build-go/go.sum88
-rw-r--r--tvix/build-go/rpc_build.pb.go80
-rw-r--r--tvix/build-go/rpc_build_grpc.pb.go112
-rw-r--r--tvix/build/Cargo.toml33
-rw-r--r--tvix/build/build.rs38
-rw-r--r--tvix/build/default.nix5
-rw-r--r--tvix/build/protos/LICENSE21
-rw-r--r--tvix/build/protos/build.proto163
-rw-r--r--tvix/build/protos/default.nix56
-rw-r--r--tvix/build/protos/rpc_build.proto13
-rw-r--r--tvix/build/src/bin/tvix-build.rs132
-rw-r--r--tvix/build/src/buildservice/dummy.rs19
-rw-r--r--tvix/build/src/buildservice/from_addr.rs90
-rw-r--r--tvix/build/src/buildservice/grpc.rs28
-rw-r--r--tvix/build/src/buildservice/mod.rs16
-rw-r--r--tvix/build/src/lib.rs2
-rw-r--r--tvix/build/src/proto/grpc_buildservice_wrapper.rs35
-rw-r--r--tvix/build/src/proto/mod.rs264
-rw-r--r--tvix/buildkite.yml10
-rw-r--r--tvix/castore-go/LICENSE21
-rw-r--r--tvix/castore-go/README.md10
-rw-r--r--tvix/castore-go/castore.go212
-rw-r--r--tvix/castore-go/castore.pb.go580
-rw-r--r--tvix/castore-go/castore_test.go298
-rw-r--r--tvix/castore-go/default.nix31
-rw-r--r--tvix/castore-go/go.mod22
-rw-r--r--tvix/castore-go/go.sum99
-rw-r--r--tvix/castore-go/rename_node.go38
-rw-r--r--tvix/castore-go/rpc_blobstore.pb.go538
-rw-r--r--tvix/castore-go/rpc_blobstore_grpc.pb.go288
-rw-r--r--tvix/castore-go/rpc_directory.pb.go272
-rw-r--r--tvix/castore-go/rpc_directory_grpc.pb.go248
-rw-r--r--tvix/castore/Cargo.toml118
-rw-r--r--tvix/castore/build.rs39
-rw-r--r--tvix/castore/default.nix23
-rw-r--r--tvix/castore/docs/blobstore-chunking.md147
-rw-r--r--tvix/castore/docs/blobstore-protocol.md104
-rw-r--r--tvix/castore/docs/data-model.md50
-rw-r--r--tvix/castore/docs/why-not-git-trees.md57
-rw-r--r--tvix/castore/protos/LICENSE21
-rw-r--r--tvix/castore/protos/castore.proto71
-rw-r--r--tvix/castore/protos/default.nix54
-rw-r--r--tvix/castore/protos/rpc_blobstore.proto85
-rw-r--r--tvix/castore/protos/rpc_directory.proto53
-rw-r--r--tvix/castore/src/blobservice/chunked_reader.rs496
-rw-r--r--tvix/castore/src/blobservice/combinator.rs132
-rw-r--r--tvix/castore/src/blobservice/from_addr.rs152
-rw-r--r--tvix/castore/src/blobservice/grpc.rs349
-rw-r--r--tvix/castore/src/blobservice/memory.rs137
-rw-r--r--tvix/castore/src/blobservice/mod.rs105
-rw-r--r--tvix/castore/src/blobservice/naive_seeker.rs265
-rw-r--r--tvix/castore/src/blobservice/object_store.rs546
-rw-r--r--tvix/castore/src/blobservice/sled.rs150
-rw-r--r--tvix/castore/src/blobservice/tests/mod.rs254
-rw-r--r--tvix/castore/src/blobservice/tests/utils.rs41
-rw-r--r--tvix/castore/src/digests.rs86
-rw-r--r--tvix/castore/src/directoryservice/bigtable.rs357
-rw-r--r--tvix/castore/src/directoryservice/closure_validator.rs268
-rw-r--r--tvix/castore/src/directoryservice/from_addr.rs174
-rw-r--r--tvix/castore/src/directoryservice/grpc.rs345
-rw-r--r--tvix/castore/src/directoryservice/memory.rs86
-rw-r--r--tvix/castore/src/directoryservice/mod.rs122
-rw-r--r--tvix/castore/src/directoryservice/simple_putter.rs75
-rw-r--r--tvix/castore/src/directoryservice/sled.rs168
-rw-r--r--tvix/castore/src/directoryservice/tests/mod.rs226
-rw-r--r--tvix/castore/src/directoryservice/tests/utils.rs46
-rw-r--r--tvix/castore/src/directoryservice/traverse.rs197
-rw-r--r--tvix/castore/src/directoryservice/utils.rs82
-rw-r--r--tvix/castore/src/errors.rs61
-rw-r--r--tvix/castore/src/fixtures.rs88
-rw-r--r--tvix/castore/src/fs/file_attr.rs29
-rw-r--r--tvix/castore/src/fs/fuse.rs120
-rw-r--r--tvix/castore/src/fs/inode_tracker.rs207
-rw-r--r--tvix/castore/src/fs/inodes.rs96
-rw-r--r--tvix/castore/src/fs/mod.rs877
-rw-r--r--tvix/castore/src/fs/root_nodes.rs37
-rw-r--r--tvix/castore/src/fs/tests.rs1244
-rw-r--r--tvix/castore/src/fs/virtiofs.rs238
-rw-r--r--tvix/castore/src/hashing_reader.rs89
-rw-r--r--tvix/castore/src/import/archive.rs458
-rw-r--r--tvix/castore/src/import/error.rs20
-rw-r--r--tvix/castore/src/import/fs.rs185
-rw-r--r--tvix/castore/src/import/mod.rs197
-rw-r--r--tvix/castore/src/lib.rs30
-rw-r--r--tvix/castore/src/path.rs446
-rw-r--r--tvix/castore/src/proto/grpc_blobservice_wrapper.rs175
-rw-r--r--tvix/castore/src/proto/grpc_directoryservice_wrapper.rs114
-rw-r--r--tvix/castore/src/proto/mod.rs471
-rw-r--r--tvix/castore/src/proto/tests/directory.rs452
-rw-r--r--tvix/castore/src/proto/tests/directory_nodes_iterator.rs78
-rw-r--r--tvix/castore/src/proto/tests/mod.rs2
-rw-r--r--tvix/castore/src/tests/import.rs129
-rw-r--r--tvix/castore/src/tests/mod.rs1
-rw-r--r--tvix/castore/src/tonic.rs122
-rw-r--r--tvix/cli/Cargo.toml27
-rw-r--r--tvix/cli/default.nix95
-rw-r--r--tvix/cli/src/main.rs337
-rw-r--r--tvix/clippy.toml6
-rw-r--r--tvix/crate-hashes.json4
-rw-r--r--tvix/default.nix240
-rw-r--r--tvix/docs/.gitignore4
-rw-r--r--tvix/docs/Makefile12
-rw-r--r--tvix/docs/book.toml11
-rw-r--r--tvix/docs/component-flow.puml74
-rw-r--r--tvix/docs/components.md114
-rw-r--r--tvix/docs/default.nix43
-rw-r--r--tvix/docs/src/SUMMARY.md10
-rw-r--r--tvix/docs/src/TODO.md129
-rw-r--r--tvix/docs/src/architecture.md147
-rw-r--r--tvix/docs/src/figures/component-flow.puml60
-rw-r--r--tvix/docs/src/lang-version.md62
-rw-r--r--tvix/docs/src/language-spec.md (renamed from tvix/docs/language-spec.md)13
-rw-r--r--tvix/docs/src/value-pointer-equality.md338
-rw-r--r--tvix/docs/theme/highlight.js590
-rw-r--r--tvix/eval/.skip-subtree2
-rw-r--r--tvix/eval/Cargo.toml61
-rw-r--r--tvix/eval/README.md92
-rw-r--r--tvix/eval/benches/eval.rs36
-rw-r--r--tvix/eval/build.rs9
-rw-r--r--tvix/eval/builtin-macros/.gitignore1
-rw-r--r--tvix/eval/builtin-macros/Cargo.toml16
-rw-r--r--tvix/eval/builtin-macros/src/lib.rs357
-rw-r--r--tvix/eval/builtin-macros/tests/tests.rs45
-rw-r--r--tvix/eval/default.nix9
-rw-r--r--tvix/eval/docs/abandoned/thread-local-vm.md233
-rw-r--r--tvix/eval/docs/bindings.md133
-rw-r--r--tvix/eval/docs/build-references.md254
-rw-r--r--tvix/eval/docs/builtins.md138
-rw-r--r--tvix/eval/docs/catchable-errors.md131
-rw-r--r--tvix/eval/docs/known-optimisation-potential.md162
-rw-r--r--tvix/eval/docs/language-issues.md46
-rw-r--r--tvix/eval/docs/opcodes-attrsets.md122
-rw-r--r--tvix/eval/docs/recursive-attrs.md68
-rw-r--r--tvix/eval/docs/vm-loop.md315
-rw-r--r--tvix/eval/proptest-regressions/value/mod.txt10
-rw-r--r--tvix/eval/src/builtins/hash.rs29
-rw-r--r--tvix/eval/src/builtins/impure.rs110
-rw-r--r--tvix/eval/src/builtins/mod.rs1728
-rw-r--r--tvix/eval/src/builtins/to_xml.rs154
-rw-r--r--tvix/eval/src/builtins/versions.rs163
-rw-r--r--tvix/eval/src/chunk.rs289
-rw-r--r--tvix/eval/src/compiler/bindings.rs826
-rw-r--r--tvix/eval/src/compiler/import.rs120
-rw-r--r--tvix/eval/src/compiler/mod.rs1684
-rw-r--r--tvix/eval/src/compiler/optimiser.rs125
-rw-r--r--tvix/eval/src/compiler/scope.rs378
-rw-r--r--tvix/eval/src/errors.rs1109
-rw-r--r--tvix/eval/src/io.rs164
-rw-r--r--tvix/eval/src/lib.rs394
-rw-r--r--tvix/eval/src/nix_search_path.rs256
-rw-r--r--tvix/eval/src/observer.rs318
-rw-r--r--tvix/eval/src/opcode.rs284
-rw-r--r--tvix/eval/src/pretty_ast.rs468
-rw-r--r--tvix/eval/src/properties.rs164
-rw-r--r--tvix/eval/src/source.rs65
-rw-r--r--tvix/eval/src/spans.rs109
-rw-r--r--tvix/eval/src/systems.rs351
-rw-r--r--tvix/eval/src/test_utils.rs8
-rw-r--r--tvix/eval/src/tests/mod.rs203
-rw-r--r--tvix/eval/src/tests/nix_tests/README.md8
-rw-r--r--tvix/eval/src/tests/nix_tests/binary-data (renamed from third_party/nix/src/tests/lang/binary-data)bin1024 -> 1024 bytes
-rw-r--r--tvix/eval/src/tests/nix_tests/data (renamed from third_party/nix/src/tests/lang/data)0
-rw-r--r--tvix/eval/src/tests/nix_tests/dir1/a.nix (renamed from third_party/nix/src/tests/lang/dir1/a.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/dir2/a.nix (renamed from third_party/nix/src/tests/lang/dir2/a.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/dir2/b.nix (renamed from third_party/nix/src/tests/lang/dir2/b.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/dir3/a.nix (renamed from third_party/nix/src/tests/lang/dir3/a.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/dir3/b.nix (renamed from third_party/nix/src/tests/lang/dir3/b.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/dir3/c.nix (renamed from third_party/nix/src/tests/lang/dir3/c.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/dir4/a.nix (renamed from third_party/nix/src/tests/lang/dir4/a.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/dir4/c.nix (renamed from third_party/nix/src/tests/lang/dir4/c.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-abort.nix (renamed from third_party/nix/src/tests/lang/eval-fail-abort.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-assert.nix (renamed from third_party/nix/src/tests/lang/eval-fail-assert.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-bad-antiquote-1.nix (renamed from third_party/nix/src/tests/lang/eval-fail-bad-antiquote-1.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-bad-antiquote-3.nix (renamed from third_party/nix/src/tests/lang/eval-fail-bad-antiquote-3.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-blackhole.nix (renamed from third_party/nix/src/tests/lang/eval-fail-blackhole.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-deepseq.nix (renamed from third_party/nix/src/tests/lang/eval-fail-deepseq.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-foldlStrict-strict-op-application.nix5
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-hashfile-missing.nix (renamed from third_party/nix/src/tests/lang/eval-fail-hashfile-missing.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-missing-arg.nix (renamed from third_party/nix/src/tests/lang/eval-fail-missing-arg.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-path-slash.nix (renamed from third_party/nix/src/tests/lang/parse-fail-path-slash.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-remove.nix (renamed from third_party/nix/src/tests/lang/eval-fail-remove.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-seq.nix (renamed from third_party/nix/src/tests/lang/eval-fail-seq.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-substring.nix (renamed from third_party/nix/src/tests/lang/eval-fail-substring.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-fail-to-path.nix (renamed from third_party/nix/src/tests/lang/eval-fail-to-path.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-any-all.exp (renamed from third_party/nix/src/tests/lang/eval-okay-any-all.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-any-all.nix (renamed from third_party/nix/src/tests/lang/eval-okay-any-all.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-arithmetic.exp (renamed from third_party/nix/src/tests/lang/eval-okay-arithmetic.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-arithmetic.nix (renamed from third_party/nix/src/tests/lang/eval-okay-arithmetic.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrnames.exp (renamed from third_party/nix/src/tests/lang/eval-okay-attrnames.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrnames.nix (renamed from third_party/nix/src/tests/lang/eval-okay-attrnames.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs.exp (renamed from third_party/nix/src/tests/lang/eval-okay-attrs.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs.nix (renamed from third_party/nix/src/tests/lang/eval-okay-attrs.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs2.exp (renamed from third_party/nix/src/tests/lang/eval-okay-attrs2.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs2.nix (renamed from third_party/nix/src/tests/lang/eval-okay-attrs2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs3.exp (renamed from third_party/nix/src/tests/lang/eval-okay-attrs3.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs3.nix (renamed from third_party/nix/src/tests/lang/eval-okay-attrs3.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs4.exp (renamed from third_party/nix/src/tests/lang/eval-okay-attrs4.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs4.nix (renamed from third_party/nix/src/tests/lang/eval-okay-attrs4.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs5.exp (renamed from third_party/nix/src/tests/lang/eval-okay-attrs5.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-attrs5.nix (renamed from third_party/nix/src/tests/lang/eval-okay-attrs5.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-1.exp (renamed from third_party/nix/src/tests/lang/eval-okay-backslash-newline-1.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-1.nix (renamed from third_party/nix/src/tests/lang/eval-okay-backslash-newline-1.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-2.exp (renamed from third_party/nix/src/tests/lang/eval-okay-backslash-newline-2.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-2.nix (renamed from third_party/nix/src/tests/lang/eval-okay-backslash-newline-2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-builtins-add.exp (renamed from third_party/nix/src/tests/lang/eval-okay-builtins-add.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-builtins-add.nix (renamed from third_party/nix/src/tests/lang/eval-okay-builtins-add.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-builtins.exp (renamed from third_party/nix/src/tests/lang/eval-okay-builtins.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-builtins.nix (renamed from third_party/nix/src/tests/lang/eval-okay-builtins.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-callable-attrs.exp (renamed from third_party/nix/src/tests/lang/eval-okay-callable-attrs.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-callable-attrs.nix (renamed from third_party/nix/src/tests/lang/eval-okay-callable-attrs.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-catattrs.exp (renamed from third_party/nix/src/tests/lang/eval-okay-catattrs.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-catattrs.nix (renamed from third_party/nix/src/tests/lang/eval-okay-catattrs.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-closure.exp (renamed from third_party/nix/src/tests/lang/eval-okay-closure.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-closure.exp.xml343
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-closure.nix (renamed from third_party/nix/src/tests/lang/eval-okay-closure.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-comments.exp (renamed from third_party/nix/src/tests/lang/eval-okay-comments.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-comments.nix (renamed from third_party/nix/src/tests/lang/eval-okay-comments.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-concat.exp (renamed from third_party/nix/src/tests/lang/eval-okay-concat.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-concat.nix (renamed from third_party/nix/src/tests/lang/eval-okay-concat.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-concatmap.exp (renamed from third_party/nix/src/tests/lang/eval-okay-concatmap.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-concatmap.nix (renamed from third_party/nix/src/tests/lang/eval-okay-concatmap.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-concatstringssep.exp (renamed from third_party/nix/src/tests/lang/eval-okay-concatstringssep.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-concatstringssep.nix (renamed from third_party/nix/src/tests/lang/eval-okay-concatstringssep.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-deepseq.exp (renamed from third_party/nix/src/tests/lang/eval-okay-deepseq.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-deepseq.nix (renamed from third_party/nix/src/tests/lang/eval-okay-deepseq.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-delayed-with-inherit.exp (renamed from third_party/nix/src/tests/lang/eval-okay-delayed-with-inherit.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-delayed-with-inherit.nix (renamed from third_party/nix/src/tests/lang/eval-okay-delayed-with-inherit.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-2.exp (renamed from third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-2.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-2.nix (renamed from third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-bare.exp (renamed from third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-bare.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-bare.nix (renamed from third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-bare.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs.exp (renamed from third_party/nix/src/tests/lang/eval-okay-dynamic-attrs.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs.nix (renamed from third_party/nix/src/tests/lang/eval-okay-dynamic-attrs.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-elem.exp (renamed from third_party/nix/src/tests/lang/eval-okay-elem.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-elem.nix (renamed from third_party/nix/src/tests/lang/eval-okay-elem.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-empty-args.exp (renamed from third_party/nix/src/tests/lang/eval-okay-empty-args.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-empty-args.nix (renamed from third_party/nix/src/tests/lang/eval-okay-empty-args.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-eq.exp (renamed from third_party/nix/src/tests/lang/eval-okay-eq.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-eq.nix (renamed from third_party/nix/src/tests/lang/eval-okay-eq.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-filter.exp (renamed from third_party/nix/src/tests/lang/eval-okay-filter.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-filter.nix (renamed from third_party/nix/src/tests/lang/eval-okay-filter.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-flatten.exp (renamed from third_party/nix/src/tests/lang/eval-okay-flatten.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-flatten.nix (renamed from third_party/nix/src/tests/lang/eval-okay-flatten.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-float.exp (renamed from third_party/nix/src/tests/lang/eval-okay-float.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-float.nix (renamed from third_party/nix/src/tests/lang/eval-okay-float.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-elements.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-elements.nix9
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.nix6
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict.nix3
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-fromTOML.exp (renamed from third_party/nix/src/tests/lang/eval-okay-fromTOML.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-fromTOML.nix (renamed from third_party/nix/src/tests/lang/eval-okay-fromTOML.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-fromjson-escapes.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-fromjson-escapes.nix3
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-fromjson.exp (renamed from third_party/nix/src/tests/lang/eval-okay-fromjson.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-fromjson.nix35
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-functionargs.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-functionargs.exp.xml15
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-functionargs.nix80
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-getenv.exp (renamed from third_party/nix/src/tests/lang/eval-okay-getenv.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-getenv.nix1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix5
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-hash.exp (renamed from third_party/nix/src/tests/lang/eval-okay-hash.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-hashfile.exp (renamed from third_party/nix/src/tests/lang/eval-okay-hashfile.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-hashfile.nix (renamed from third_party/nix/src/tests/lang/eval-okay-hashfile.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-hashstring.exp (renamed from third_party/nix/src/tests/lang/eval-okay-hashstring.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-hashstring.nix (renamed from third_party/nix/src/tests/lang/eval-okay-hashstring.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-if.exp (renamed from third_party/nix/src/tests/lang/eval-okay-if.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-if.nix (renamed from third_party/nix/src/tests/lang/eval-okay-if.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-ind-string.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-ind-string.nix (renamed from third_party/nix/src/tests/lang/eval-okay-ind-string.nix)2
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-intersectAttrs.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-intersectAttrs.nix50
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-let.exp (renamed from third_party/nix/src/tests/lang/eval-okay-let.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-let.nix (renamed from third_party/nix/src/tests/lang/eval-okay-let.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-list.exp (renamed from third_party/nix/src/tests/lang/eval-okay-list.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-list.nix (renamed from third_party/nix/src/tests/lang/eval-okay-list.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-listtoattrs.exp (renamed from third_party/nix/src/tests/lang/eval-okay-listtoattrs.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-listtoattrs.nix (renamed from third_party/nix/src/tests/lang/eval-okay-listtoattrs.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-logic.exp (renamed from third_party/nix/src/tests/lang/eval-okay-logic.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-logic.nix (renamed from third_party/nix/src/tests/lang/eval-okay-logic.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-map.exp (renamed from third_party/nix/src/tests/lang/eval-okay-map.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-map.nix (renamed from third_party/nix/src/tests/lang/eval-okay-map.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-mapattrs.exp (renamed from third_party/nix/src/tests/lang/eval-okay-mapattrs.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-mapattrs.nix (renamed from third_party/nix/src/tests/lang/eval-okay-mapattrs.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-nested-with.exp (renamed from third_party/nix/src/tests/lang/eval-okay-nested-with.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-nested-with.nix (renamed from third_party/nix/src/tests/lang/eval-okay-nested-with.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-new-let.exp (renamed from third_party/nix/src/tests/lang/eval-okay-new-let.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-new-let.nix (renamed from third_party/nix/src/tests/lang/eval-okay-new-let.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-null-dynamic-attrs.exp (renamed from third_party/nix/src/tests/lang/eval-okay-null-dynamic-attrs.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-null-dynamic-attrs.nix (renamed from third_party/nix/src/tests/lang/eval-okay-null-dynamic-attrs.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-partition.exp (renamed from third_party/nix/src/tests/lang/eval-okay-partition.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-partition.nix (renamed from third_party/nix/src/tests/lang/eval-okay-partition.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-path.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-pathexists.exp (renamed from third_party/nix/src/tests/lang/eval-okay-pathexists.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-pathexists.nix (renamed from third_party/nix/src/tests/lang/eval-okay-pathexists.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-patterns.exp (renamed from third_party/nix/src/tests/lang/eval-okay-patterns.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-patterns.nix (renamed from third_party/nix/src/tests/lang/eval-okay-patterns.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-readfile.exp (renamed from third_party/nix/src/tests/lang/eval-okay-readfile.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-readfile.nix (renamed from third_party/nix/src/tests/lang/eval-okay-readfile.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-redefine-builtin.exp (renamed from third_party/nix/src/tests/lang/eval-okay-redefine-builtin.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-redefine-builtin.nix (renamed from third_party/nix/src/tests/lang/eval-okay-redefine-builtin.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-regex-match.exp (renamed from third_party/nix/src/tests/lang/eval-okay-regex-match.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-regex-match.nix (renamed from third_party/nix/src/tests/lang/eval-okay-regex-match.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-regex-split.exp (renamed from third_party/nix/src/tests/lang/eval-okay-regex-split.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-regex-split.nix (renamed from third_party/nix/src/tests/lang/eval-okay-regex-split.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-regression-20220122.exp (renamed from third_party/nix/src/tests/lang/eval-okay-scope-1.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-regression-20220122.nix1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-regression-20220125.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-regression-20220125.nix2
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-remove.exp (renamed from third_party/nix/src/tests/lang/eval-okay-remove.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-remove.nix (renamed from third_party/nix/src/tests/lang/eval-okay-remove.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-1.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-1.nix (renamed from third_party/nix/src/tests/lang/eval-okay-scope-1.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-2.exp (renamed from third_party/nix/src/tests/lang/eval-okay-scope-2.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-2.nix (renamed from third_party/nix/src/tests/lang/eval-okay-scope-2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-3.exp (renamed from third_party/nix/src/tests/lang/eval-okay-scope-3.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-3.nix (renamed from third_party/nix/src/tests/lang/eval-okay-scope-3.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-4.exp (renamed from third_party/nix/src/tests/lang/eval-okay-scope-4.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-4.nix (renamed from third_party/nix/src/tests/lang/eval-okay-scope-4.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-6.exp (renamed from third_party/nix/src/tests/lang/eval-okay-scope-6.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-6.nix (renamed from third_party/nix/src/tests/lang/eval-okay-scope-6.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-7.exp (renamed from third_party/nix/src/tests/lang/eval-okay-scope-7.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-scope-7.nix (renamed from third_party/nix/src/tests/lang/eval-okay-scope-7.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-seq.exp (renamed from third_party/nix/src/tests/lang/eval-okay-seq.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-seq.nix (renamed from third_party/nix/src/tests/lang/eval-okay-seq.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-sort.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-sort.nix20
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-splitversion.exp (renamed from third_party/nix/src/tests/lang/eval-okay-splitversion.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-splitversion.nix (renamed from third_party/nix/src/tests/lang/eval-okay-splitversion.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-string.exp (renamed from third_party/nix/src/tests/lang/eval-okay-string.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-string.nix (renamed from third_party/nix/src/tests/lang/eval-okay-string.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-strings-as-attrs-names.exp (renamed from third_party/nix/src/tests/lang/eval-okay-strings-as-attrs-names.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-strings-as-attrs-names.nix (renamed from third_party/nix/src/tests/lang/eval-okay-strings-as-attrs-names.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-substring.exp (renamed from third_party/nix/src/tests/lang/eval-okay-substring.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-substring.nix (renamed from third_party/nix/src/tests/lang/eval-okay-substring.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-tail-call-1.exp (renamed from third_party/nix/src/tests/lang/eval-okay-tail-call-1.exp-disabled)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-tail-call-1.nix (renamed from third_party/nix/src/tests/lang/disabled/eval-okay-tail-call-1.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-tojson.exp (renamed from third_party/nix/src/tests/lang/eval-okay-tojson.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-tojson.nix (renamed from third_party/nix/src/tests/lang/eval-okay-tojson.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-toxml.exp (renamed from third_party/nix/src/tests/lang/evalstore-okay-toxml.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-toxml.nix (renamed from third_party/nix/src/tests/lang/evalstore-okay-toxml.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-toxml2.exp (renamed from third_party/nix/src/tests/lang/eval-okay-toxml2.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-toxml2.nix (renamed from third_party/nix/src/tests/lang/eval-okay-toxml2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-tryeval.exp (renamed from third_party/nix/src/tests/lang/eval-okay-tryeval.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-tryeval.nix (renamed from third_party/nix/src/tests/lang/eval-okay-tryeval.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-types.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-types.nix (renamed from third_party/nix/src/tests/lang/eval-okay-types.nix)1
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-versions.exp (renamed from third_party/nix/src/tests/lang/eval-okay-versions.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-versions.nix (renamed from third_party/nix/src/tests/lang/eval-okay-versions.nix)3
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-with.exp (renamed from third_party/nix/src/tests/lang/eval-okay-with.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-with.nix (renamed from third_party/nix/src/tests/lang/eval-okay-with.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-xml.exp.xml52
-rw-r--r--tvix/eval/src/tests/nix_tests/eval-okay-xml.nix (renamed from third_party/nix/src/tests/lang/disabled/eval-okay-xml.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/imported.nix (renamed from third_party/nix/src/tests/lang/imported.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/imported2.nix (renamed from third_party/nix/src/tests/lang/imported2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/lib.nix (renamed from third_party/nix/src/tests/lang/lib.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-bad-antiquote-2.nix (renamed from third_party/nix/src/tests/lang/eval-fail-bad-antiquote-2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-fromTOML-timestamps.nix130
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-nonexist-path.nix (renamed from third_party/nix/src/tests/lang/eval-fail-antiquoted-path.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-scope-5.nix (renamed from third_party/nix/src/tests/lang/eval-fail-scope-5.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-undeclared-arg.nix (renamed from third_party/nix/src/tests/lang/eval-fail-undeclared-arg.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-attrs6.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-attrs6.nix4
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.exp (renamed from third_party/nix/src/tests/lang/evalstore-okay-autoargs.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.flags1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.nix (renamed from third_party/nix/src/tests/lang/evalargs-okay-autoargs.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context-introspection.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context-introspection.nix41
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context.nix6
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-curpos.exp (renamed from third_party/nix/src/tests/lang/eval-okay-curpos.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-curpos.nix (renamed from third_party/nix/src/tests/lang/eval-okay-curpos.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-delayed-with.exp (renamed from third_party/nix/src/tests/lang/eval-okay-delayed-with.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-delayed-with.nix (renamed from third_party/nix/src/tests/lang/eval-okay-delayed-with.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-eq-derivations.exp (renamed from third_party/nix/src/tests/lang/eval-okay-eq-derivations.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-eq-derivations.nix (renamed from third_party/nix/src/tests/lang/eval-okay-eq-derivations.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-floor-ceil.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-floor-ceil.nix9
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.flags1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.nix130
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-functionargs.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-functionargs.nix4
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-undefined.exp (renamed from third_party/nix/src/tests/lang/eval-okay-getattrpos-undefined.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-undefined.nix (renamed from third_party/nix/src/tests/lang/eval-okay-getattrpos-undefined.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos.exp (renamed from third_party/nix/src/tests/lang/eval-okay-getattrpos.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos.nix (renamed from third_party/nix/src/tests/lang/eval-okay-getattrpos.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-import.exp (renamed from third_party/nix/src/tests/lang/eval-okay-import.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-import.nix11
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-overrides.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-overrides.nix9
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path-antiquotation.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path-antiquotation.nix12
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path.nix (renamed from third_party/nix/src/tests/lang/disabled/eval-okay-path.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readDir.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readDir.nix.disabled (renamed from third_party/nix/src/tests/lang/eval-okay-readDir.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readFileType.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readFileType.nix6
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-replacestrings.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-replacestrings.nix (renamed from third_party/nix/src/tests/lang/eval-okay-replacestrings.nix)1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.exp (renamed from third_party/nix/src/tests/lang/disabled/eval-okay-search-path.exp)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.flags (renamed from third_party/nix/src/tests/lang/disabled/eval-okay-search-path.flags)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.nix10
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-zipAttrsWith.exp1
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-zipAttrsWith.nix9
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/readDir/bar (renamed from third_party/nix/src/tests/lang/readDir/bar)0
-rw-r--r--tvix/eval/src/tests/nix_tests/notyetpassing/readDir/foo/git-hates-directories (renamed from third_party/nix/src/tests/lang/readDir/foo/git-hates-directories)0
l---------tvix/eval/src/tests/nix_tests/notyetpassing/readDir/ldir1
l---------tvix/eval/src/tests/nix_tests/notyetpassing/readDir/linked1
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-1.nix4
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-2.nix (renamed from third_party/nix/src/tests/lang/parse-fail-dup-attrs-2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-3.nix (renamed from third_party/nix/src/tests/lang/parse-fail-dup-attrs-3.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-4.nix (renamed from third_party/nix/src/tests/lang/parse-fail-dup-attrs-4.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-7.nix (renamed from third_party/nix/src/tests/lang/parse-fail-dup-attrs-7.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-dup-formals.nix (renamed from third_party/nix/src/tests/lang/parse-fail-dup-formals.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-eof-in-string.nix3
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-mixed-nested-attrs1.nix (renamed from third_party/nix/src/tests/lang/parse-fail-mixed-nested-attrs1.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-mixed-nested-attrs2.nix (renamed from third_party/nix/src/tests/lang/parse-fail-mixed-nested-attrs2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-patterns-1.nix (renamed from third_party/nix/src/tests/lang/parse-fail-patterns-1.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-regression-20060610.nix (renamed from third_party/nix/src/tests/lang/parse-fail-regression-20060610.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-uft8.nix (renamed from third_party/nix/src/tests/lang/parse-fail-uft8.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-undef-var-2.nix (renamed from third_party/nix/src/tests/lang/parse-fail-undef-var-2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-fail-undef-var.nix (renamed from third_party/nix/src/tests/lang/parse-fail-undef-var.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-1.nix (renamed from third_party/nix/src/tests/lang/parse-okay-1.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-crlf.nix (renamed from third_party/nix/src/tests/lang/parse-okay-crlf.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-dup-attrs-5.nix (renamed from third_party/nix/src/tests/lang/parse-okay-dup-attrs-5.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-dup-attrs-6.nix (renamed from third_party/nix/src/tests/lang/parse-okay-dup-attrs-6.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-mixed-nested-attrs-1.nix (renamed from third_party/nix/src/tests/lang/parse-okay-mixed-nested-attrs-1.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-mixed-nested-attrs-2.nix (renamed from third_party/nix/src/tests/lang/parse-okay-mixed-nested-attrs-2.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-mixed-nested-attrs-3.nix (renamed from third_party/nix/src/tests/lang/parse-okay-mixed-nested-attrs-3.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-regression-20041027.nix (renamed from third_party/nix/src/tests/lang/parse-okay-regression-20041027.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-regression-751.nix (renamed from third_party/nix/src/tests/lang/parse-okay-regression-751.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-subversion.nix (renamed from third_party/nix/src/tests/lang/parse-okay-subversion.nix)0
-rw-r--r--tvix/eval/src/tests/nix_tests/parse-okay-url.nix (renamed from third_party/nix/src/tests/lang/parse-okay-url.nix)1
-rw-r--r--tvix/eval/src/tests/one_offs.rs36
-rw-r--r--tvix/eval/src/tests/tvix_tests/README.md19
-rw-r--r--tvix/eval/src/tests/tvix_tests/directory/default.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-builtins-substring-negative-start.nix3
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-builtins-thunk-error.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-builtins-tojson-tostring-notcallable.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-builtins-tojson-tostring-strong.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-closed-formals.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-deep-forced-thunk-error.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-deepseq.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-division-by-zero-float.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-division-by-zero-int.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-foldlStrict-strict-op-application.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-force-before-value-pointer-equality.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-function-formals-typecheck.nix2
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-getEnv-coercion.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-infinite-recursion.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-outer-value-never-pointer-equal.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-parsedrvname-coerce.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-remove.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-seq.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-fail-throw-abort-cannot-be-caught.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-abort-throw-can-be-caught.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-abort-throw-can-be-caught.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-access-strange-identifier.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-access-strange-identifier.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-float.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-float.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-int.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-int.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-assert-thunk-condition.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-assert-thunk-condition.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attempt-to-call-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attempt-to-call-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attr-key-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attr-key-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-inherit-literal.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-inherit-literal.nix2
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-simple-inherit.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-simple-inherit.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-lhs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-lhs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-rhs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-rhs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-kv-lhs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-kv-lhs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-basenameof-propagate-catchables.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-basenameof-propagate-catchables.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-basenameof.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-basenameof.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-add.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-add.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all.nix15
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any.nix15
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrnames.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrnames.nix13
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitand.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitand.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitor.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitor.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitxor.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitxor.nix12
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.exp (renamed from third_party/nix/src/tests/lang/evalstore-okay-context-introspection.exp)0
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catAttrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catAttrs.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catattrs-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catattrs-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-compareVersions.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-compareVersions.nix46
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-map-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-map-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-strings-sep-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-strings-sep-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-div.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-div.nix34
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemAt-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemAt-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemat.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemat.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter.nix13
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-foldl-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-foldl-propagate-catchable.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-from-json-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-from-json-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-function-args-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-function-args-propagate-catchable.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-gen-list-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-gen-list-propagate-catchable.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genList-function-strictness.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genList-function-strictness.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-pointer-equality.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-pointer-equality.nix15
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getAttr-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getAttr-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getContext-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getContext-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getattr.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getattr.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-group-by-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-group-by-propagate-catchable.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-groupby-thunk.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-groupby-thunk.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasContext-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasContext-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasattr.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasattr.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head-propagate-catchable.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-isType-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-isType-propagate-catchable.nix14
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.nix15
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-list-to-attrs-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-list-to-attrs-propagate-catchable.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-function-strictness.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-function-strictness.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-propagate-catchable.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.nix19
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mapAttrs-function-strictness.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mapAttrs-function-strictness.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-match-propagate-catchables.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-match-propagate-catchables.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mul.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mul.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-parse-drv-name-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-parse-drv-name-propagate-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition-propagate-catchable.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition.nix13
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-remove-attrs-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-remove-attrs-propagate-catchable.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replace-strings-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replace-strings-propagate-catchable.nix16
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replaceStrings.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replaceStrings.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sort-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sort-propagate-catchable.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-split-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-split-propagate-catchable.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitVersion.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitVersion.nix13
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitversion-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitversion-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length-propagate-catchable.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sub.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sub.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-coerce.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-coerce.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-negative-length.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-negative-length.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring.nix18
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail-propagate-catchable.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-thunked-function-calls.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-thunked-function-calls.nix31
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-json-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-json-propagate-catchable.nix14
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-path-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-path-propagate-catchable.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-string-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-string-propagate-catchable.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-xml-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-xml-propagate-catchable.nix15
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-toString.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-toString.nix28
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-literals.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-literals.nix11
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath-nested.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath-nested.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-thunks.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-thunks.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-tostring.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-tostring.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of-propagate-catchable.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of.nix22
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-predicates.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-predicates.nix34
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-unsafe-discard-string-context-propagate-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-unsafe-discard-string-context-propagate-catchable.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-double-throw.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-double-throw.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-attrNames.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-attrNames.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-inequality.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-inequality.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-intersectattrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-intersectattrs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-string-interpolation.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-string-interpolation.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-update-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-update-attrs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-passed-to-function-with-formals.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-catchable-passed-to-function-with-formals.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-ceil.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-ceil.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-closure-pointer-compare.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-closure-pointer-compare.nix14
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-closure-self.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-closure-self.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-closure-with-shadowing.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-closure-with-shadowing.nix14
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-coerce-opadd.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-coerce-opadd.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-compare-lists.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-compare-lists.nix17
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-compare-ordering-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-compare-ordering-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-concat-lists.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-concat-lists.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-concat-strings.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-concat-strings.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-concatmap.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-concatmap.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-concatstringssep.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-concatstringssep.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-contains-nested-non-set.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-contains-nested-non-set.nix3
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-contains-non-set.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-contains-non-set.nix3
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-attrs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with-closure.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with-closure.nix21
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deepseq.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deepseq.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deferred-unary-formals.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deferred-unary-formals.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deferred-with.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-deferred-with.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-dirof.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-dirof.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-elem.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-elem.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-empty-rec-inherit.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-empty-rec-inherit.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-eq-float.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-eq-float.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-eq-int.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-eq-int.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-eq-nested-list.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-eq-nested-list.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-equality-tolerate-catchable-in-type-field.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-equality-tolerate-catchable-in-type-field.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-escape-string-correct-char-boundaries.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-escape-string-correct-char-boundaries.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-fib.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-fib.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-fix.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-fix.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-float-repr.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-float-repr.nix2
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-floor.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-floor.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-elements.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-elements.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-formals-miscompilation-b-261-regression.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-formals-miscompilation-b-261-regression.nix20
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-fromjson-escapes.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-fromjson-escapes.nix3
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-fromjson.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-fromjson.nix24
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-functionargs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-functionargs.nix83
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-functor-call.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-functor-call.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-genlist.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-genlist.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-hasattr-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-hasattr-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-identifier-formatting.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-identifier-formatting.nix42
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-import-display.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-import-display.nix2
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-import.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-import.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-inherit-string-ident.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-inherit-string-ident.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals-deferred.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals-deferred.nix3
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals.nix3
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-intersectattrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-intersectattrs.nix3
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lambda-identity.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lambda-identity.nix2
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-late-binding-closure.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-late-binding-closure.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-late-binding.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-late-binding.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lazy-assert.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lazy-assert.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lazy-equality.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lazy-equality.nix16
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with-nested.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with-nested.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-fix.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-fix.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-in-with.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-in-with.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-identifiers.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-identifiers.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit-from-later-bound.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit-from-later-bound.nix13
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.nix13
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-sibling-access.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-sibling-access.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit-mixed.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit-mixed.nix20
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-list-comparison.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-list-comparison.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-listtoattrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-listtoattrs.nix16
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-logical-and-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-logical-and-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-logical-or-catchable.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-logical-or-catchable.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-manual-rec.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-manual-rec.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-attrs.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-rec-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-rec-attrs.nix12
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-multiline-string.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-multiline-string.nix2
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-multiple-nested-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-multiple-nested-attrs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-mutually-recursive-let-binding.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-mutually-recursive-let-binding.nix14
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-ne-int.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-ne-int.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-ne-string.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-ne-string.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-assertions.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-assertions.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-closure.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-closure.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-deferred-upvalue.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-deferred-upvalue.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-has-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-has-attrs.nix26
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-let.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-let.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-rec.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-rec.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-let-slots.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-let-slots.nix22
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-let.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-let.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-poisoning.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-poisoning.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-set-thunks.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-set-thunks.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-siblings.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-siblings.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-thunks.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-thunks.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-with.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nested-with.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nix-version-cmp.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-nix-version-cmp.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-observable-eval-cache.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-observable-eval-cache.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-observe-infinite-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-observe-infinite-attrs.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-optimised-bools.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-optimised-bools.nix21
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-default.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-default.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested-default.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested-default.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-non-set.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-non-set.nix2
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-or-operator.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-overlapping-nested-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-overlapping-nested-attrs.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-parsedrvname.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-parsedrvname.nix11
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-pathexists.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-pathexists.nix2
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-poisoned-scopes.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-poisoned-scopes.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-readDir.exp (renamed from third_party/nix/src/tests/lang/eval-okay-readDir.exp)0
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix.disabled1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-readfile.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-readfile.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-rec-nested-access.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-rec-nested-access.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-recursive-attrs-all-features.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-recursive-attrs-all-features.nix13
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-regex-match.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-regex-match.nix29
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-remove.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-remove.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-repeated-list-to-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-repeated-list-to-attrs.nix14
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-seq.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-seq.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-closure.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-closure.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-interpol.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-interpol.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-let.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-let.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-nested-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-nested-attrs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-recursive-attrs.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-recursive-attrs.nix4
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-with.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-simple-with.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-stable-sort.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-stable-sort.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-substring-propagate-catchables.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-substring-propagate-catchables.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-default-args.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-default-args.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-implications.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-implications.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-thunked-functor.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-thunked-functor.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-thunked-if.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-thunked-if.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-thunked-with.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-thunked-with.nix8
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.nix11
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-tryeval-thunk-twice.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-tryeval-thunk-twice.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-tryeval.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-tryeval.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-unpoison-scope.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-unpoison-scope.nix10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-useless-inherit-with.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-useless-inherit-with.nix16
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-value-display.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-value-display.nix16
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-compare.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-compare.nix6
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-equality.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-equality.nix46
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-with-closure.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-with-closure.nix5
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-with-in-dynamic-key.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-with-in-dynamic-key.nix13
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-with-in-list.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-with-in-list.nix14
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-xml.exp.xml41
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-xml.nix7
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-bool-false.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-bool-true.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-dollar-escape.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-empty-attrs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-empty-list.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-flat-attrs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-float.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-heterogeneous-list.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-homogeneous-float-list.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-homogeneous-int-list.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-homogeneous-string-list.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-int.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-kv-attrs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-nested-attrs.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-null.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-assert.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-else.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-if.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-in.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-inherit.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-let.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-rec.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-then.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-with.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-signed-float.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-signed-int.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/identity-string.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/lib.nix64
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-fail-builtins-genericClosure-uncomparable-keys.nix9
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-fail-builtins-genericClosure-uncomparable-keys2.nix12
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-builtins-set-pointer-equality.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-builtins-set-pointer-equality.nix25
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-cycle-display-cpp-nix-2.13.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-cycle-display-cpp-nix-2.13.nix34
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-minimal-2.3-builtins.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-minimal-2.3-builtins.nix122
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-non-identifier-pointer-inequality.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-non-identifier-pointer-inequality.nix28
-rw-r--r--tvix/eval/src/tests/tvix_tests/observable-eval-cache1.nix1
l---------tvix/eval/src/tests/tvix_tests/observable-eval-cache2.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/observable-eval-cache3.nix1
-rw-r--r--tvix/eval/src/tests/tvix_tests/readDir/bar (renamed from users/grfn/system/home/.skip-subtree)0
-rw-r--r--tvix/eval/src/tests/tvix_tests/readDir/foo/.keep (renamed from users/grfn/system/system/.skip-subtree)0
-rw-r--r--tvix/eval/src/upvalues.rs86
-rw-r--r--tvix/eval/src/value/arbitrary.rs106
-rw-r--r--tvix/eval/src/value/attrs.rs621
-rw-r--r--tvix/eval/src/value/attrs/tests.rs106
-rw-r--r--tvix/eval/src/value/builtin.rs137
-rw-r--r--tvix/eval/src/value/function.rs112
-rw-r--r--tvix/eval/src/value/json.rs154
-rw-r--r--tvix/eval/src/value/list.rs102
-rw-r--r--tvix/eval/src/value/mod.rs1073
-rw-r--r--tvix/eval/src/value/path.rs14
-rw-r--r--tvix/eval/src/value/string.rs873
-rw-r--r--tvix/eval/src/value/thunk.rs434
-rw-r--r--tvix/eval/src/vm/generators.rs809
-rw-r--r--tvix/eval/src/vm/macros.rs93
-rw-r--r--tvix/eval/src/vm/mod.rs1368
-rw-r--r--tvix/eval/src/warnings.rs152
-rw-r--r--tvix/eval/tests/nix_oracle.rs171
-rw-r--r--tvix/glue/Cargo.toml57
-rw-r--r--tvix/glue/benches/eval.rs77
-rw-r--r--tvix/glue/default.nix8
-rw-r--r--tvix/glue/src/.skip-subtree1
-rw-r--r--tvix/glue/src/builtins/derivation.nix36
-rw-r--r--tvix/glue/src/builtins/derivation.rs552
-rw-r--r--tvix/glue/src/builtins/errors.rs76
-rw-r--r--tvix/glue/src/builtins/fetchers.rs186
-rw-r--r--tvix/glue/src/builtins/import.rs287
-rw-r--r--tvix/glue/src/builtins/mod.rs797
-rw-r--r--tvix/glue/src/builtins/utils.rs36
-rw-r--r--tvix/glue/src/fetchers/decompression.rs222
-rw-r--r--tvix/glue/src/fetchers/mod.rs445
-rw-r--r--tvix/glue/src/fetchurl.nix53
-rw-r--r--tvix/glue/src/known_paths.rs289
-rw-r--r--tvix/glue/src/lib.rs23
-rw-r--r--tvix/glue/src/refscan.rs115
-rw-r--r--tvix/glue/src/tests/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv1
-rw-r--r--tvix/glue/src/tests/blob.tar.bz2bin0 -> 116 bytes
-rw-r--r--tvix/glue/src/tests/blob.tar.gzbin0 -> 116 bytes
-rw-r--r--tvix/glue/src/tests/blob.tar.xzbin0 -> 172 bytes
-rw-r--r--tvix/glue/src/tests/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv1
-rw-r--r--tvix/glue/src/tests/mod.rs153
-rw-r--r--tvix/glue/src/tests/nix_tests/eval-okay-context-introspection.exp1
-rw-r--r--tvix/glue/src/tests/nix_tests/eval-okay-context-introspection.nix42
-rw-r--r--tvix/glue/src/tests/nix_tests/eval-okay-context.exp1
-rw-r--r--tvix/glue/src/tests/nix_tests/eval-okay-context.nix6
-rw-r--r--tvix/glue/src/tests/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv1
-rw-r--r--tvix/glue/src/tests/tvix_tests/eval-okay-context-introspection.exp1
-rw-r--r--tvix/glue/src/tests/tvix_tests/eval-okay-context-introspection.nix83
-rw-r--r--tvix/glue/src/tests/tvix_tests/eval-okay-context-propagation.exp1
-rw-r--r--tvix/glue/src/tests/tvix_tests/eval-okay-context-propagation.nix119
-rw-r--r--tvix/glue/src/tests/tvix_tests/eval-okay-fetchtarball.exp1
-rw-r--r--tvix/glue/src/tests/tvix_tests/eval-okay-fetchtarball.nix42
-rw-r--r--tvix/glue/src/tests/tvix_tests/eval-okay-fetchurl.exp1
-rw-r--r--tvix/glue/src/tests/tvix_tests/eval-okay-fetchurl.nix25
-rw-r--r--tvix/glue/src/tvix_build.rs439
-rw-r--r--tvix/glue/src/tvix_io.rs66
-rw-r--r--tvix/glue/src/tvix_store_io.rs717
-rw-r--r--tvix/logo.webpbin0 -> 82366 bytes
-rw-r--r--tvix/nar-bridge/.gitignore2
-rw-r--r--tvix/nar-bridge/README.md7
-rw-r--r--tvix/nar-bridge/cmd/nar-bridge-http/main.go93
-rw-r--r--tvix/nar-bridge/cmd/nar-bridge-http/otel.go87
-rw-r--r--tvix/nar-bridge/default.nix10
-rw-r--r--tvix/nar-bridge/go.mod54
-rw-r--r--tvix/nar-bridge/go.sum120
-rw-r--r--tvix/nar-bridge/pkg/http/nar_get.go197
-rw-r--r--tvix/nar-bridge/pkg/http/nar_put.go141
-rw-r--r--tvix/nar-bridge/pkg/http/narinfo.go51
-rw-r--r--tvix/nar-bridge/pkg/http/narinfo_get.go137
-rw-r--r--tvix/nar-bridge/pkg/http/narinfo_put.go103
-rw-r--r--tvix/nar-bridge/pkg/http/server.go119
-rw-r--r--tvix/nar-bridge/pkg/http/util.go24
-rw-r--r--tvix/nar-bridge/pkg/importer/blob_upload.go71
-rw-r--r--tvix/nar-bridge/pkg/importer/counting_writer.go21
-rw-r--r--tvix/nar-bridge/pkg/importer/directory_upload.go88
-rw-r--r--tvix/nar-bridge/pkg/importer/gen_pathinfo.go62
-rw-r--r--tvix/nar-bridge/pkg/importer/importer.go303
-rw-r--r--tvix/nar-bridge/pkg/importer/importer_test.go537
-rw-r--r--tvix/nar-bridge/pkg/importer/roundtrip_test.go85
-rw-r--r--tvix/nar-bridge/pkg/importer/util_test.go34
-rw-r--r--tvix/nar-bridge/testdata/emptydirectory.narbin0 -> 96 bytes
-rw-r--r--tvix/nar-bridge/testdata/nar_1094wph9z4nwlgvsd53abfz8i117ykiv5dwnq9nnhz846s7xqd7d.narbin0 -> 464152 bytes
-rw-r--r--tvix/nar-bridge/testdata/onebyteexecutable.narbin0 -> 152 bytes
-rw-r--r--tvix/nar-bridge/testdata/onebyteregular.narbin0 -> 120 bytes
-rw-r--r--tvix/nar-bridge/testdata/popdirectories.narbin0 -> 600 bytes
-rw-r--r--tvix/nar-bridge/testdata/symlink.narbin0 -> 136 bytes
-rw-r--r--tvix/nix-compat/Cargo.toml56
-rw-r--r--tvix/nix-compat/benches/derivation_parse_aterm.rs31
-rw-r--r--tvix/nix-compat/benches/narinfo_parse.rs69
-rw-r--r--tvix/nix-compat/default.nix7
-rw-r--r--tvix/nix-compat/src/aterm/escape.rs28
-rw-r--r--tvix/nix-compat/src/aterm/mod.rs7
-rw-r--r--tvix/nix-compat/src/aterm/parser.rs125
-rw-r--r--tvix/nix-compat/src/bin/drvfmt.rs42
-rw-r--r--tvix/nix-compat/src/derivation/errors.rs60
-rw-r--r--tvix/nix-compat/src/derivation/mod.rs305
-rw-r--r--tvix/nix-compat/src/derivation/output.rs189
-rw-r--r--tvix/nix-compat/src/derivation/parse_error.rs87
-rw-r--r--tvix/nix-compat/src/derivation/parser.rs585
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/duplicate.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv.json23
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/292w8yzv5nn7nhdpxcs8b7vby2p27s09-nested-json.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/292w8yzv5nn7nhdpxcs8b7vby2p27s09-nested-json.drv.json19
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv.json23
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv.json19
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv.json16
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv.json23
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv.json23
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv.json21
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv.json23
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv1
-rw-r--r--tvix/nix-compat/src/derivation/tests/derivation_tests/ok/x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv.json21
-rw-r--r--tvix/nix-compat/src/derivation/tests/mod.rs436
-rw-r--r--tvix/nix-compat/src/derivation/validate.rs141
-rw-r--r--tvix/nix-compat/src/derivation/write.rs257
-rw-r--r--tvix/nix-compat/src/lib.rs18
-rw-r--r--tvix/nix-compat/src/nar/mod.rs4
-rw-r--r--tvix/nix-compat/src/nar/reader/mod.rs479
-rw-r--r--tvix/nix-compat/src/nar/reader/read.rs109
-rw-r--r--tvix/nix-compat/src/nar/reader/test.rs278
-rw-r--r--tvix/nix-compat/src/nar/tests/complicated.narbin0 -> 840 bytes
-rw-r--r--tvix/nix-compat/src/nar/tests/helloworld.narbin0 -> 128 bytes
-rw-r--r--tvix/nix-compat/src/nar/tests/symlink.narbin0 -> 136 bytes
-rw-r--r--tvix/nix-compat/src/nar/wire/mod.rs133
-rw-r--r--tvix/nix-compat/src/nar/wire/tag.rs166
-rw-r--r--tvix/nix-compat/src/nar/writer/async.rs235
-rw-r--r--tvix/nix-compat/src/nar/writer/mod.rs9
-rw-r--r--tvix/nix-compat/src/nar/writer/sync.rs224
-rw-r--r--tvix/nix-compat/src/nar/writer/test.rs128
-rw-r--r--tvix/nix-compat/src/narinfo/fingerprint.rs50
-rw-r--r--tvix/nix-compat/src/narinfo/mod.rs527
-rw-r--r--tvix/nix-compat/src/narinfo/public_keys.rs152
-rw-r--r--tvix/nix-compat/src/narinfo/signature.rs184
-rw-r--r--tvix/nix-compat/src/nix_daemon/mod.rs4
-rw-r--r--tvix/nix-compat/src/nix_daemon/protocol_version.rs123
-rw-r--r--tvix/nix-compat/src/nix_daemon/worker_protocol.rs434
-rw-r--r--tvix/nix-compat/src/nixbase32.rs206
-rw-r--r--tvix/nix-compat/src/nixhash/algos.rs75
-rw-r--r--tvix/nix-compat/src/nixhash/ca_hash.rs343
-rw-r--r--tvix/nix-compat/src/nixhash/mod.rs602
-rw-r--r--tvix/nix-compat/src/path_info.rs121
-rw-r--r--tvix/nix-compat/src/store_path/mod.rs635
-rw-r--r--tvix/nix-compat/src/store_path/utils.rs293
-rw-r--r--tvix/nix-compat/src/wire/bytes/mod.rs229
-rw-r--r--tvix/nix-compat/src/wire/bytes/reader/mod.rs427
-rw-r--r--tvix/nix-compat/src/wire/bytes/reader/trailer.rs197
-rw-r--r--tvix/nix-compat/src/wire/bytes/writer.rs538
-rw-r--r--tvix/nix-compat/src/wire/mod.rs5
-rw-r--r--tvix/nix-compat/testdata/narinfo.zstbin0 -> 975945 bytes
-rw-r--r--tvix/nix-lang-test-suite/README.md140
-rw-r--r--tvix/proto/LICENSE21
-rw-r--r--tvix/proto/default.nix9
-rw-r--r--tvix/proto/evaluator.proto144
-rwxr-xr-xtvix/scripts/bench-windtunnel.sh27
-rw-r--r--tvix/serde/.skip-subtree1
-rw-r--r--tvix/serde/Cargo.toml9
-rw-r--r--tvix/serde/default.nix5
-rw-r--r--tvix/serde/examples/cfg-demo.rs35
-rw-r--r--tvix/serde/examples/foods.nix22
-rw-r--r--tvix/serde/examples/nixpkgs.rs34
-rw-r--r--tvix/serde/src/de.rs475
-rw-r--r--tvix/serde/src/de_tests.rs245
-rw-r--r--tvix/serde/src/error.rs99
-rw-r--r--tvix/serde/src/lib.rs12
-rw-r--r--tvix/shell.nix64
-rw-r--r--tvix/src/bin/nix-store.rs103
-rw-r--r--tvix/src/main.rs3
-rw-r--r--tvix/store-go/LICENSE21
-rw-r--r--tvix/store-go/README.md10
-rw-r--r--tvix/store-go/default.nix31
-rw-r--r--tvix/store-go/export.go273
-rw-r--r--tvix/store-go/export_test.go134
-rw-r--r--tvix/store-go/go.mod25
-rw-r--r--tvix/store-go/go.sum47
-rw-r--r--tvix/store-go/pathinfo.go99
-rw-r--r--tvix/store-go/pathinfo.pb.go657
-rw-r--r--tvix/store-go/pathinfo_test.go149
-rw-r--r--tvix/store-go/pick_next_node_test.go51
-rw-r--r--tvix/store-go/rpc_pathinfo.pb.go347
-rw-r--r--tvix/store-go/rpc_pathinfo_grpc.pb.go308
-rw-r--r--tvix/store-go/testdata/emptydirectory.narbin0 -> 96 bytes
-rw-r--r--tvix/store-go/testdata/onebyteregular.narbin0 -> 120 bytes
-rw-r--r--tvix/store-go/testdata/symlink.narbin0 -> 136 bytes
-rw-r--r--tvix/store/Cargo.toml80
-rw-r--r--tvix/store/README.md63
-rw-r--r--tvix/store/build.rs38
-rw-r--r--tvix/store/default.nix52
-rw-r--r--tvix/store/docs/api.md288
-rw-r--r--tvix/store/protos/LICENSE21
-rw-r--r--tvix/store/protos/default.nix55
-rw-r--r--tvix/store/protos/pathinfo.proto128
-rw-r--r--tvix/store/protos/rpc_pathinfo.proto76
-rw-r--r--tvix/store/src/bin/tvix-store.rs563
-rw-r--r--tvix/store/src/import.rs180
-rw-r--r--tvix/store/src/lib.rs14
-rw-r--r--tvix/store/src/nar/import.rs355
-rw-r--r--tvix/store/src/nar/mod.rs26
-rw-r--r--tvix/store/src/nar/renderer.rs185
-rw-r--r--tvix/store/src/pathinfoservice/bigtable.rs401
-rw-r--r--tvix/store/src/pathinfoservice/from_addr.rs236
-rw-r--r--tvix/store/src/pathinfoservice/fs/mod.rs81
-rw-r--r--tvix/store/src/pathinfoservice/grpc.rs216
-rw-r--r--tvix/store/src/pathinfoservice/memory.rs84
-rw-r--r--tvix/store/src/pathinfoservice/mod.rs85
-rw-r--r--tvix/store/src/pathinfoservice/nix_http.rs314
-rw-r--r--tvix/store/src/pathinfoservice/sled.rs125
-rw-r--r--tvix/store/src/pathinfoservice/tests/mod.rs113
-rw-r--r--tvix/store/src/pathinfoservice/tests/utils.rs60
-rw-r--r--tvix/store/src/proto/grpc_pathinfoservice_wrapper.rs121
-rw-r--r--tvix/store/src/proto/mod.rs374
-rw-r--r--tvix/store/src/proto/tests/mod.rs1
-rw-r--r--tvix/store/src/proto/tests/pathinfo.rs431
-rw-r--r--tvix/store/src/tests/fixtures.rs142
-rw-r--r--tvix/store/src/tests/mod.rs2
-rw-r--r--tvix/store/src/tests/nar_renderer.rs228
-rw-r--r--tvix/store/src/utils.rs65
-rw-r--r--tvix/tools/crunch-v2/.gitignore1
-rw-r--r--tvix/tools/crunch-v2/Cargo.lock3214
-rw-r--r--tvix/tools/crunch-v2/Cargo.nix11881
-rw-r--r--tvix/tools/crunch-v2/Cargo.toml39
-rw-r--r--tvix/tools/crunch-v2/OWNERS1
-rw-r--r--tvix/tools/crunch-v2/build.rs6
-rw-r--r--tvix/tools/crunch-v2/default.nix15
-rw-r--r--tvix/tools/crunch-v2/protos/flatstore.proto38
-rw-r--r--tvix/tools/crunch-v2/src/bin/extract.rs155
-rw-r--r--tvix/tools/crunch-v2/src/lib.rs3
-rw-r--r--tvix/tools/crunch-v2/src/main.rs309
-rw-r--r--tvix/tools/crunch-v2/src/remote.rs211
-rw-r--r--tvix/tools/narinfo2parquet/Cargo.lock2144
-rw-r--r--tvix/tools/narinfo2parquet/Cargo.nix8528
-rw-r--r--tvix/tools/narinfo2parquet/Cargo.toml25
-rw-r--r--tvix/tools/narinfo2parquet/OWNERS1
-rw-r--r--tvix/tools/narinfo2parquet/default.nix3
-rw-r--r--tvix/tools/narinfo2parquet/src/main.rs264
-rw-r--r--tvix/tools/turbofetch/Cargo.lock1715
-rw-r--r--tvix/tools/turbofetch/Cargo.nix6466
-rw-r--r--tvix/tools/turbofetch/Cargo.toml28
-rw-r--r--tvix/tools/turbofetch/OWNERS1
-rw-r--r--tvix/tools/turbofetch/default.nix12
-rwxr-xr-xtvix/tools/turbofetch/deploy.sh5
-rw-r--r--tvix/tools/turbofetch/src/buffer.rs83
-rw-r--r--tvix/tools/turbofetch/src/lib.rs103
-rw-r--r--tvix/tools/turbofetch/src/main.rs220
-rw-r--r--tvix/tools/weave/Cargo.lock2179
-rw-r--r--tvix/tools/weave/Cargo.nix8809
-rw-r--r--tvix/tools/weave/Cargo.toml20
-rw-r--r--tvix/tools/weave/OWNERS1
-rw-r--r--tvix/tools/weave/default.nix3
-rw-r--r--tvix/tools/weave/src/bin/swizzle.rs114
-rw-r--r--tvix/tools/weave/src/bytes.rs27
-rw-r--r--tvix/tools/weave/src/lib.rs106
-rw-r--r--tvix/tools/weave/src/main.rs216
-rw-r--r--tvix/verify-lang-tests/default.nix226
-rw-r--r--tvix/website/default.nix46
-rw-r--r--tvix/website/landing-en.md42
-rw-r--r--users/Profpatsch/.envrc5
-rw-r--r--users/Profpatsch/.gitignore1
-rw-r--r--users/Profpatsch/.hlint.yaml357
-rw-r--r--users/Profpatsch/.vscode/launch.json18
-rw-r--r--users/Profpatsch/.vscode/settings.json25
-rw-r--r--users/Profpatsch/OWNERS8
-rw-r--r--users/Profpatsch/README.md10
-rw-r--r--users/Profpatsch/alacritty.nix27
-rw-r--r--users/Profpatsch/aliases.nix88
-rw-r--r--users/Profpatsch/arglib/ArglibNetencode.hs22
-rw-r--r--users/Profpatsch/arglib/arglib-netencode.cabal65
-rw-r--r--users/Profpatsch/arglib/netencode.nix99
-rw-r--r--users/Profpatsch/atomically-write.nix3
-rw-r--r--users/Profpatsch/blog/README.md7
-rw-r--r--users/Profpatsch/blog/default.nix366
-rw-r--r--users/Profpatsch/blog/notes/an-idealized-conflang.md298
-rw-r--r--users/Profpatsch/blog/notes/private-trackers-are-markets.md46
-rw-r--r--users/Profpatsch/cabal.project14
-rw-r--r--users/Profpatsch/cas-serve/CasServe.hs247
-rw-r--r--users/Profpatsch/cas-serve/cas-serve.cabal73
-rw-r--r--users/Profpatsch/cas-serve/default.nix38
-rw-r--r--users/Profpatsch/cas-serve/schema.sql38
-rw-r--r--users/Profpatsch/cas-serve/wordlist.json1
-rw-r--r--users/Profpatsch/cas-serve/wordlist.sqlitebin0 -> 36864 bytes
-rw-r--r--users/Profpatsch/cdb.nix20
-rw-r--r--users/Profpatsch/declib/.eslintrc.json14
-rw-r--r--users/Profpatsch/declib/.gitignore6
-rw-r--r--users/Profpatsch/declib/.prettierrc8
-rw-r--r--users/Profpatsch/declib/README.md4
-rw-r--r--users/Profpatsch/declib/build.ninja16
-rw-r--r--users/Profpatsch/declib/index.ts245
-rw-r--r--users/Profpatsch/declib/package.json25
-rw-r--r--users/Profpatsch/declib/tsconfig.json25
-rw-r--r--users/Profpatsch/dhall/lib.dhall84
-rw-r--r--users/Profpatsch/emacs-tree-sitter-move/README.md5
-rw-r--r--users/Profpatsch/emacs-tree-sitter-move/default.nix4
-rw-r--r--users/Profpatsch/emacs-tree-sitter-move/shell.nix7
-rw-r--r--users/Profpatsch/exactSource.nix90
-rw-r--r--users/Profpatsch/execline/ExecHelpers.hs48
-rw-r--r--users/Profpatsch/execline/default.nix59
-rw-r--r--users/Profpatsch/execline/exec-helpers.cabal14
-rw-r--r--users/Profpatsch/execline/exec-helpers/Cargo.lock7
-rw-r--r--users/Profpatsch/execline/exec-helpers/Cargo.toml8
-rw-r--r--users/Profpatsch/execline/exec-helpers/default.nix6
-rw-r--r--users/Profpatsch/execline/exec-helpers/exec_helpers.rs149
-rw-r--r--users/Profpatsch/execline/exec_helpers.rs113
-rw-r--r--users/Profpatsch/fafo.jpgbin0 -> 26139 bytes
-rw-r--r--users/Profpatsch/git-db/default.nix6
-rw-r--r--users/Profpatsch/git-db/git-db.rs39
-rw-r--r--users/Profpatsch/haskell-module-deps/README.md5
-rw-r--r--users/Profpatsch/haskell-module-deps/default.nix55
-rw-r--r--users/Profpatsch/haskell-module-deps/example-output-dhall-haskell.pngbin0 -> 415873 bytes
-rw-r--r--users/Profpatsch/hie.yaml36
-rw-r--r--users/Profpatsch/htmx-experiment/Main.hs4
-rw-r--r--users/Profpatsch/htmx-experiment/default.nix46
-rw-r--r--users/Profpatsch/htmx-experiment/htmx-experiment.cabal89
-rw-r--r--users/Profpatsch/htmx-experiment/src/HtmxExperiment.hs377
-rw-r--r--users/Profpatsch/htmx-experiment/src/ServerErrors.hs244
-rw-r--r--users/Profpatsch/htmx-experiment/src/ValidationParseT.hs40
-rw-r--r--users/Profpatsch/httzip/Httzip.hs66
-rw-r--r--users/Profpatsch/httzip/default.nix40
-rw-r--r--users/Profpatsch/httzip/httzip.cabal73
-rw-r--r--users/Profpatsch/ical-smolify/IcalSmolify.hs124
-rw-r--r--users/Profpatsch/ical-smolify/README.md5
-rw-r--r--users/Profpatsch/ical-smolify/default.nix23
-rw-r--r--users/Profpatsch/ical-smolify/ical-smolify.cabal18
-rw-r--r--users/Profpatsch/imap-idle.nix23
-rw-r--r--users/Profpatsch/imap-idle.rs40
-rw-r--r--users/Profpatsch/importDhall.nix93
-rw-r--r--users/Profpatsch/ini/default.nix6
-rw-r--r--users/Profpatsch/ini/ini.dhall36
-rw-r--r--users/Profpatsch/jaeger.nix46
-rw-r--r--users/Profpatsch/jbovlaste-sqlite/JbovlasteSqlite.hs389
-rw-r--r--users/Profpatsch/jbovlaste-sqlite/default.nix33
-rw-r--r--users/Profpatsch/jbovlaste-sqlite/jbovlaste-sqlite.cabal76
-rw-r--r--users/Profpatsch/lens.nix7
-rw-r--r--users/Profpatsch/lib.nix79
-rw-r--r--users/Profpatsch/lorri-wait-for-eval/LorriWaitForEval.hs173
-rw-r--r--users/Profpatsch/lorri-wait-for-eval/README.md7
-rw-r--r--users/Profpatsch/lorri-wait-for-eval/default.nix20
-rw-r--r--users/Profpatsch/mailbox-org/MailboxOrg.hs523
-rw-r--r--users/Profpatsch/mailbox-org/README.md7
-rw-r--r--users/Profpatsch/mailbox-org/default.nix38
-rw-r--r--users/Profpatsch/mailbox-org/mailbox-org.cabal95
-rw-r--r--users/Profpatsch/mailbox-org/src/AesonQQ.hs24
-rw-r--r--users/Profpatsch/my-prelude/README.md42
-rw-r--r--users/Profpatsch/my-prelude/default.nix51
-rw-r--r--users/Profpatsch/my-prelude/my-prelude.cabal120
-rw-r--r--users/Profpatsch/my-prelude/src/Aeson.hs176
-rw-r--r--users/Profpatsch/my-prelude/src/AtLeast.hs51
-rw-r--r--users/Profpatsch/my-prelude/src/MyPrelude.hs776
-rw-r--r--users/Profpatsch/my-prelude/src/Parse.hs174
-rw-r--r--users/Profpatsch/my-prelude/src/Postgres/Decoder.hs94
-rw-r--r--users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs760
-rw-r--r--users/Profpatsch/my-prelude/src/Pretty.hs108
-rw-r--r--users/Profpatsch/my-prelude/src/Seconds.hs55
-rw-r--r--users/Profpatsch/my-prelude/src/Test.hs115
-rw-r--r--users/Profpatsch/my-prelude/src/Tool.hs75
-rw-r--r--users/Profpatsch/my-prelude/src/ValidationParseT.hs16
-rw-r--r--users/Profpatsch/my-webstuff/default.nix27
-rw-r--r--users/Profpatsch/my-webstuff/my-webstuff.cabal72
-rw-r--r--users/Profpatsch/my-webstuff/src/Multipart2.hs220
-rw-r--r--users/Profpatsch/my-xmonad/Xmonad.hs127
-rw-r--r--users/Profpatsch/my-xmonad/default.nix25
-rw-r--r--users/Profpatsch/my-xmonad/my-xmonad.cabal62
-rw-r--r--users/Profpatsch/netencode/Netencode.hs433
-rw-r--r--users/Profpatsch/netencode/Netencode/Parse.hs102
-rw-r--r--users/Profpatsch/netencode/README.md26
-rw-r--r--users/Profpatsch/netencode/default.nix149
-rw-r--r--users/Profpatsch/netencode/gen.nix40
-rw-r--r--users/Profpatsch/netencode/netencode-mustache.rs29
-rw-r--r--users/Profpatsch/netencode/netencode.cabal74
-rw-r--r--users/Profpatsch/netencode/netencode.rs617
-rw-r--r--users/Profpatsch/netencode/pretty.rs73
-rw-r--r--users/Profpatsch/netstring/default.nix28
-rw-r--r--users/Profpatsch/netstring/tests/default.nix29
-rw-r--r--users/Profpatsch/nix-home/README.md7
-rw-r--r--users/Profpatsch/nix-home/default.nix263
-rw-r--r--users/Profpatsch/nix-tools.nix159
-rw-r--r--users/Profpatsch/nixpkgs-rewriter/MetaStdenvLib.hs80
-rw-r--r--users/Profpatsch/nixpkgs-rewriter/default.nix112
-rw-r--r--users/Profpatsch/openlab-tools/Main.hs6
-rw-r--r--users/Profpatsch/openlab-tools/default.nix69
-rw-r--r--users/Profpatsch/openlab-tools/openlab-tools.cabal111
-rw-r--r--users/Profpatsch/openlab-tools/src/OpenlabTools.hs551
-rw-r--r--users/Profpatsch/read-http.nix25
-rw-r--r--users/Profpatsch/read-http.rs154
-rw-r--r--users/Profpatsch/reverse-haskell-deps.hs72
-rw-r--r--users/Profpatsch/reverse-haskell-deps.nix26
-rw-r--r--users/Profpatsch/reverse-haskell-deps/README.md3
-rw-r--r--users/Profpatsch/reverse-haskell-deps/ReverseHaskellDeps.hs76
-rw-r--r--users/Profpatsch/reverse-haskell-deps/default.nix32
-rw-r--r--users/Profpatsch/reverse-haskell-deps/reverse-haskell-deps.cabal16
-rw-r--r--users/Profpatsch/shell.nix110
-rw-r--r--users/Profpatsch/shortcuttable/default.nix172
-rw-r--r--users/Profpatsch/struct-edit/default.nix13
-rw-r--r--users/Profpatsch/struct-edit/main.go431
-rw-r--r--users/Profpatsch/sync-abfall-ics-aichach-friedberg/README.md3
-rw-r--r--users/Profpatsch/sync-abfall-ics-aichach-friedberg/default.nix31
-rw-r--r--users/Profpatsch/sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall139
-rw-r--r--users/Profpatsch/sync-abfall-ics-aichach-friedberg/sync-ics-to-dir.py133
-rw-r--r--users/Profpatsch/tagtime/README.md18
-rw-r--r--users/Profpatsch/toINI.nix79
-rw-r--r--users/Profpatsch/tree-sitter.nix176
-rw-r--r--users/Profpatsch/whatcd-resolver/Main.hs6
-rw-r--r--users/Profpatsch/whatcd-resolver/README.md21
-rw-r--r--users/Profpatsch/whatcd-resolver/build.ninja20
-rw-r--r--users/Profpatsch/whatcd-resolver/default.nix76
-rw-r--r--users/Profpatsch/whatcd-resolver/notes.org48
-rw-r--r--users/Profpatsch/whatcd-resolver/server-notes.org2
-rw-r--r--users/Profpatsch/whatcd-resolver/services/.gitignore3
-rwxr-xr-xusers/Profpatsch/whatcd-resolver/services/jaeger/run3
-rwxr-xr-xusers/Profpatsch/whatcd-resolver/services/reverse-proxy/run2
-rw-r--r--users/Profpatsch/whatcd-resolver/src/AppT.hs151
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Html.hs69
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Http.hs129
-rw-r--r--users/Profpatsch/whatcd-resolver/src/JsonLd.hs138
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Optional.hs18
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Redacted.hs537
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Transmission.hs306
-rw-r--r--users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs698
-rw-r--r--users/Profpatsch/whatcd-resolver/whatcd-resolver.cabal121
-rw-r--r--users/Profpatsch/writers/default.nix140
-rw-r--r--users/Profpatsch/writers/tests/default.nix42
-rw-r--r--users/Profpatsch/ytextr/README.md5
-rw-r--r--users/Profpatsch/ytextr/create-symlink-farm.nix13
-rw-r--r--users/Profpatsch/ytextr/default.nix93
-rw-r--r--users/aaqaishtyaq/OWNERS3
-rw-r--r--users/aspen/OWNERS3
-rw-r--r--users/aspen/achilles/.envrc2
-rw-r--r--users/aspen/achilles/.gitignore (renamed from users/grfn/achilles/.gitignore)0
-rw-r--r--users/aspen/achilles/Cargo.lock885
-rw-r--r--users/aspen/achilles/Cargo.toml (renamed from users/grfn/achilles/Cargo.toml)0
-rw-r--r--users/aspen/achilles/ach/.gitignore (renamed from users/grfn/achilles/ach/.gitignore)0
-rw-r--r--users/aspen/achilles/ach/Makefile (renamed from users/grfn/achilles/ach/Makefile)0
-rw-r--r--users/aspen/achilles/ach/externs.ach (renamed from users/grfn/achilles/ach/externs.ach)0
-rw-r--r--users/aspen/achilles/ach/functions.ach (renamed from users/grfn/achilles/ach/functions.ach)0
-rw-r--r--users/aspen/achilles/ach/simple.ach (renamed from users/grfn/achilles/ach/simple.ach)0
-rw-r--r--users/aspen/achilles/ach/units.ach (renamed from users/grfn/achilles/ach/units.ach)0
-rw-r--r--users/aspen/achilles/default.nix27
-rw-r--r--users/aspen/achilles/shell.nix18
-rw-r--r--users/aspen/achilles/src/ast/hir.rs (renamed from users/grfn/achilles/src/ast/hir.rs)0
-rw-r--r--users/aspen/achilles/src/ast/mod.rs (renamed from users/grfn/achilles/src/ast/mod.rs)0
-rw-r--r--users/aspen/achilles/src/codegen/llvm.rs (renamed from users/grfn/achilles/src/codegen/llvm.rs)0
-rw-r--r--users/aspen/achilles/src/codegen/mod.rs (renamed from users/grfn/achilles/src/codegen/mod.rs)0
-rw-r--r--users/aspen/achilles/src/commands/check.rs (renamed from users/grfn/achilles/src/commands/check.rs)0
-rw-r--r--users/aspen/achilles/src/commands/compile.rs (renamed from users/grfn/achilles/src/commands/compile.rs)0
-rw-r--r--users/aspen/achilles/src/commands/eval.rs28
-rw-r--r--users/aspen/achilles/src/commands/mod.rs (renamed from users/grfn/achilles/src/commands/mod.rs)0
-rw-r--r--users/aspen/achilles/src/common/env.rs (renamed from users/grfn/achilles/src/common/env.rs)0
-rw-r--r--users/aspen/achilles/src/common/error.rs (renamed from users/grfn/achilles/src/common/error.rs)0
-rw-r--r--users/aspen/achilles/src/common/mod.rs (renamed from users/grfn/achilles/src/common/mod.rs)0
-rw-r--r--users/aspen/achilles/src/common/namer.rs (renamed from users/grfn/achilles/src/common/namer.rs)0
-rw-r--r--users/aspen/achilles/src/compiler.rs (renamed from users/grfn/achilles/src/compiler.rs)0
-rw-r--r--users/aspen/achilles/src/interpreter/error.rs (renamed from users/grfn/achilles/src/interpreter/error.rs)0
-rw-r--r--users/aspen/achilles/src/interpreter/mod.rs (renamed from users/grfn/achilles/src/interpreter/mod.rs)0
-rw-r--r--users/aspen/achilles/src/interpreter/value.rs (renamed from users/grfn/achilles/src/interpreter/value.rs)0
-rw-r--r--users/aspen/achilles/src/main.rs (renamed from users/grfn/achilles/src/main.rs)0
-rw-r--r--users/aspen/achilles/src/parser/expr.rs (renamed from users/grfn/achilles/src/parser/expr.rs)3
-rw-r--r--users/aspen/achilles/src/parser/macros.rs (renamed from users/grfn/achilles/src/parser/macros.rs)0
-rw-r--r--users/aspen/achilles/src/parser/mod.rs (renamed from users/grfn/achilles/src/parser/mod.rs)0
-rw-r--r--users/aspen/achilles/src/parser/type_.rs (renamed from users/grfn/achilles/src/parser/type_.rs)0
-rw-r--r--users/aspen/achilles/src/parser/util.rs (renamed from users/grfn/achilles/src/parser/util.rs)0
-rw-r--r--users/aspen/achilles/src/passes/hir/mod.rs (renamed from users/grfn/achilles/src/passes/hir/mod.rs)0
-rw-r--r--users/aspen/achilles/src/passes/hir/monomorphize.rs (renamed from users/grfn/achilles/src/passes/hir/monomorphize.rs)0
-rw-r--r--users/aspen/achilles/src/passes/hir/strip_positive_units.rs (renamed from users/grfn/achilles/src/passes/hir/strip_positive_units.rs)0
-rw-r--r--users/aspen/achilles/src/passes/mod.rs (renamed from users/grfn/achilles/src/passes/mod.rs)0
-rw-r--r--users/aspen/achilles/src/tc/mod.rs (renamed from users/grfn/achilles/src/tc/mod.rs)0
-rw-r--r--users/aspen/achilles/tests/compile.rs (renamed from users/grfn/achilles/tests/compile.rs)0
-rw-r--r--users/aspen/bbbg/.clj-kondo/config.edn (renamed from users/grfn/bbbg/.clj-kondo/config.edn)0
-rw-r--r--users/aspen/bbbg/.envrc (renamed from users/grfn/achilles/.envrc)0
-rw-r--r--users/aspen/bbbg/.gitignore (renamed from users/grfn/bbbg/.gitignore)0
-rw-r--r--users/aspen/bbbg/Makefile (renamed from users/grfn/bbbg/Makefile)0
-rw-r--r--users/aspen/bbbg/README.md129
-rw-r--r--users/aspen/bbbg/arion-compose.nix (renamed from users/grfn/bbbg/arion-compose.nix)0
-rw-r--r--users/aspen/bbbg/arion-pkgs.nix2
-rw-r--r--users/aspen/bbbg/default.nix82
-rw-r--r--users/aspen/bbbg/deps.edn (renamed from users/grfn/bbbg/deps.edn)0
-rw-r--r--users/aspen/bbbg/deps.nix1494
-rw-r--r--users/aspen/bbbg/env/dev/bbbg-signup/env.clj (renamed from users/grfn/bbbg/env/dev/bbbg-signup/env.clj)0
-rw-r--r--users/aspen/bbbg/env/dev/logback.xml (renamed from users/grfn/bbbg/env/dev/logback.xml)0
-rw-r--r--users/aspen/bbbg/env/prod/bbbg-signup/env.clj (renamed from users/grfn/bbbg/env/prod/bbbg-signup/env.clj)0
-rw-r--r--users/aspen/bbbg/env/prod/logback.xml (renamed from users/grfn/bbbg/env/prod/logback.xml)0
-rw-r--r--users/aspen/bbbg/env/test/bbbg-signup/env.clj (renamed from users/grfn/bbbg/env/test/bbbg-signup/env.clj)0
-rw-r--r--users/aspen/bbbg/env/test/logback.xml (renamed from users/grfn/bbbg/env/test/logback.xml)0
-rw-r--r--users/aspen/bbbg/module.nix137
-rw-r--r--users/aspen/bbbg/pom.xml (renamed from users/grfn/bbbg/pom.xml)0
-rw-r--r--users/aspen/bbbg/resources/base.css (renamed from users/grfn/bbbg/resources/base.css)0
-rw-r--r--users/aspen/bbbg/resources/migrations/20211212165646-init-schema.down.sql (renamed from users/grfn/bbbg/resources/migrations/20211212165646-init-schema.down.sql)0
-rw-r--r--users/aspen/bbbg/resources/migrations/20211212165646-init-schema.up.sql (renamed from users/grfn/bbbg/resources/migrations/20211212165646-init-schema.up.sql)0
-rw-r--r--users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql (renamed from users/grfn/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql)0
-rw-r--r--users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql (renamed from users/grfn/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql)0
-rw-r--r--users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql (renamed from users/grfn/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql)0
-rw-r--r--users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql (renamed from users/grfn/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql)0
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff)bin23576 -> 23576 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2 (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2)bin19272 -> 19272 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff)bin24056 -> 24056 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2 (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2)bin19624 -> 19624 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff)bin23628 -> 23628 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2 (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2)bin19264 -> 19264 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff)bin23872 -> 23872 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2 (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2)bin19440 -> 19440 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff)bin24404 -> 24404 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2 (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2)bin19836 -> 19836 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff)bin24012 -> 24012 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2 (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2)bin19660 -> 19660 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff)bin23480 -> 23480 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2 (renamed from users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2)bin19172 -> 19172 bytes
-rw-r--r--users/aspen/bbbg/resources/public/main.js (renamed from users/grfn/bbbg/resources/public/main.js)0
-rw-r--r--users/aspen/bbbg/resources/public/robots.txt (renamed from users/grfn/bbbg/resources/public/robots.txt)0
-rw-r--r--users/aspen/bbbg/shell.nix29
-rw-r--r--users/aspen/bbbg/src/bbbg/attendee.clj (renamed from users/grfn/bbbg/src/bbbg/attendee.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/attendee_check.clj (renamed from users/grfn/bbbg/src/bbbg/attendee_check.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/core.clj69
-rw-r--r--users/aspen/bbbg/src/bbbg/db.clj (renamed from users/grfn/bbbg/src/bbbg/db.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/db/attendee.clj (renamed from users/grfn/bbbg/src/bbbg/db/attendee.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/db/attendee_check.clj (renamed from users/grfn/bbbg/src/bbbg/db/attendee_check.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/db/event.clj (renamed from users/grfn/bbbg/src/bbbg/db/event.clj)27
-rw-r--r--users/aspen/bbbg/src/bbbg/db/event_attendee.clj (renamed from users/grfn/bbbg/src/bbbg/db/event_attendee.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/db/user.clj (renamed from users/grfn/bbbg/src/bbbg/db/user.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/discord.clj (renamed from users/grfn/bbbg/src/bbbg/discord.clj)9
-rw-r--r--users/aspen/bbbg/src/bbbg/discord/auth.clj (renamed from users/grfn/bbbg/src/bbbg/discord/auth.clj)8
-rw-r--r--users/aspen/bbbg/src/bbbg/event.clj (renamed from users/grfn/bbbg/src/bbbg/event.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/event_attendee.clj (renamed from users/grfn/bbbg/src/bbbg/event_attendee.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/attendee_checks.clj (renamed from users/grfn/bbbg/src/bbbg/handlers/attendee_checks.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/attendees.clj (renamed from users/grfn/bbbg/src/bbbg/handlers/attendees.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/core.clj (renamed from users/grfn/bbbg/src/bbbg/handlers/core.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/events.clj (renamed from users/grfn/bbbg/src/bbbg/handlers/events.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/home.clj (renamed from users/grfn/bbbg/src/bbbg/handlers/home.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/signup_form.clj (renamed from users/grfn/bbbg/src/bbbg/handlers/signup_form.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/meetup/import.clj (renamed from users/grfn/bbbg/src/bbbg/meetup/import.clj)2
-rw-r--r--users/aspen/bbbg/src/bbbg/meetup_user.clj (renamed from users/grfn/bbbg/src/bbbg/meetup_user.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/styles.clj (renamed from users/grfn/bbbg/src/bbbg/styles.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/user.clj (renamed from users/grfn/bbbg/src/bbbg/user.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/util/core.clj (renamed from users/grfn/bbbg/src/bbbg/util/core.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/util/dev_secrets.clj59
-rw-r--r--users/aspen/bbbg/src/bbbg/util/display.clj (renamed from users/grfn/bbbg/src/bbbg/util/display.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/util/spec.clj (renamed from users/grfn/bbbg/src/bbbg/util/spec.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/util/sql.clj (renamed from users/grfn/bbbg/src/bbbg/util/sql.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/util/time.clj (renamed from users/grfn/bbbg/src/bbbg/util/time.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/views/flash.clj (renamed from users/grfn/bbbg/src/bbbg/views/flash.clj)0
-rw-r--r--users/aspen/bbbg/src/bbbg/web.clj (renamed from users/grfn/bbbg/src/bbbg/web.clj)0
-rw-r--r--users/aspen/bbbg/test/bbbg/meetup/import_test.clj (renamed from users/grfn/bbbg/test/bbbg/meetup/import_test.clj)0
-rw-r--r--users/aspen/bbbg/tf.nix (renamed from users/grfn/bbbg/tf.nix)19
-rw-r--r--users/aspen/emacs.d/+bindings.el (renamed from users/grfn/emacs.d/+bindings.el)15
-rw-r--r--users/aspen/emacs.d/+commands.el (renamed from users/grfn/emacs.d/+commands.el)0
-rw-r--r--users/aspen/emacs.d/+private.el.gpg (renamed from users/grfn/emacs.d/+private.el.gpg)bin1115 -> 1115 bytes
-rw-r--r--users/aspen/emacs.d/.gitignore (renamed from users/grfn/emacs.d/.gitignore)0
-rw-r--r--users/aspen/emacs.d/autoload/evil.el (renamed from users/grfn/emacs.d/autoload/evil.el)0
-rw-r--r--users/aspen/emacs.d/autoload/hlissner.el (renamed from users/grfn/emacs.d/autoload/hlissner.el)0
-rw-r--r--users/aspen/emacs.d/clocked-in-elt.el (renamed from users/grfn/emacs.d/clocked-in-elt.el)0
-rw-r--r--users/aspen/emacs.d/clojure.el (renamed from users/grfn/emacs.d/clojure.el)0
-rw-r--r--users/aspen/emacs.d/company-sql.el (renamed from users/grfn/emacs.d/company-sql.el)0
-rw-r--r--users/aspen/emacs.d/config.el (renamed from users/grfn/emacs.d/config.el)92
-rw-r--r--users/aspen/emacs.d/cpp.el39
-rw-r--r--users/aspen/emacs.d/email.el (renamed from users/grfn/emacs.d/email.el)11
-rw-r--r--users/aspen/emacs.d/github-org.el (renamed from users/grfn/emacs.d/github-org.el)0
-rw-r--r--users/aspen/emacs.d/google-c-style.el (renamed from users/grfn/emacs.d/google-c-style.el)0
-rw-r--r--users/aspen/emacs.d/grid.el (renamed from users/grfn/emacs.d/grid.el)0
-rw-r--r--users/aspen/emacs.d/init.el175
-rw-r--r--users/aspen/emacs.d/irc.el (renamed from users/grfn/emacs.d/irc.el)0
-rw-r--r--users/aspen/emacs.d/lisp.el (renamed from users/grfn/emacs.d/lisp.el)0
-rwxr-xr-xusers/aspen/emacs.d/nix-clangd.sh (renamed from users/grfn/emacs.d/nix-clangd.sh)0
-rw-r--r--users/aspen/emacs.d/nix.el (renamed from users/grfn/emacs.d/nix.el)0
-rw-r--r--users/aspen/emacs.d/org-alerts.el (renamed from users/grfn/emacs.d/org-alerts.el)0
-rw-r--r--users/aspen/emacs.d/org-config.el191
-rw-r--r--users/aspen/emacs.d/org-gcal.el (renamed from users/grfn/emacs.d/org-gcal.el)0
-rw-r--r--users/aspen/emacs.d/org-query.el (renamed from users/grfn/emacs.d/org-query.el)27
-rw-r--r--users/aspen/emacs.d/packages.el154
-rw-r--r--users/aspen/emacs.d/rust.el (renamed from users/grfn/emacs.d/rust.el)13
-rw-r--r--users/aspen/emacs.d/show-matching-paren.el (renamed from users/grfn/emacs.d/show-matching-paren.el)0
-rw-r--r--users/aspen/emacs.d/slack-snippets.el (renamed from users/grfn/emacs.d/slack-snippets.el)0
-rw-r--r--users/aspen/emacs.d/slack.el (renamed from users/grfn/emacs.d/slack.el)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/annotation (renamed from users/grfn/emacs.d/snippets/haskell-mode/annotation)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/benchmark-module (renamed from users/grfn/emacs.d/snippets/haskell-mode/benchmark-module)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/header (renamed from users/grfn/emacs.d/snippets/haskell-mode/header)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/hedgehog-generator (renamed from users/grfn/emacs.d/snippets/haskell-mode/hedgehog-generator)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/hedgehog-property (renamed from users/grfn/emacs.d/snippets/haskell-mode/hedgehog-property)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/hlint8
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/import-i (renamed from users/grfn/emacs.d/snippets/haskell-mode/import-i)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/inl (renamed from users/grfn/emacs.d/snippets/haskell-mode/inl)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/inline (renamed from users/grfn/emacs.d/snippets/haskell-mode/inline)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/language pragma (renamed from users/grfn/emacs.d/snippets/haskell-mode/language pragma)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/lens.field (renamed from users/grfn/emacs.d/snippets/haskell-mode/lens.field)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/module (renamed from users/grfn/emacs.d/snippets/haskell-mode/module)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/shut up, hlint (renamed from users/grfn/emacs.d/snippets/haskell-mode/shut up, hlint)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/test-group9
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/test-module (renamed from users/grfn/emacs.d/snippets/haskell-mode/test-module)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/undefined (renamed from users/grfn/emacs.d/snippets/haskell-mode/undefined)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/action-type (renamed from users/grfn/emacs.d/snippets/js2-mode/action-type)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/before (renamed from users/grfn/emacs.d/snippets/js2-mode/before)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/context (renamed from users/grfn/emacs.d/snippets/js2-mode/context)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/describe (renamed from users/grfn/emacs.d/snippets/js2-mode/describe)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/expect (renamed from users/grfn/emacs.d/snippets/js2-mode/expect)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/function (renamed from users/grfn/emacs.d/snippets/js2-mode/function)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/header (renamed from users/grfn/emacs.d/snippets/js2-mode/header)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/it (renamed from users/grfn/emacs.d/snippets/js2-mode/it)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/it-pending (renamed from users/grfn/emacs.d/snippets/js2-mode/it-pending)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/module (renamed from users/grfn/emacs.d/snippets/js2-mode/module)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/record (renamed from users/grfn/emacs.d/snippets/js2-mode/record)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/test (renamed from users/grfn/emacs.d/snippets/js2-mode/test)0
-rw-r--r--users/aspen/emacs.d/snippets/nix-mode/fetchFromGitHub12
-rw-r--r--users/aspen/emacs.d/snippets/nix-mode/pythonPackage (renamed from users/grfn/emacs.d/snippets/nix-mode/pythonPackage)0
-rw-r--r--users/aspen/emacs.d/snippets/nix-mode/sha2567
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/SQL source block (renamed from users/grfn/emacs.d/snippets/org-mode/SQL source block)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/combat13
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/date (renamed from users/grfn/emacs.d/snippets/org-mode/date)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/date-time (renamed from users/grfn/emacs.d/snippets/org-mode/date-time)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/description (renamed from users/grfn/emacs.d/snippets/org-mode/description)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/nologdone (renamed from users/grfn/emacs.d/snippets/org-mode/nologdone)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/python source block (renamed from users/grfn/emacs.d/snippets/org-mode/python source block)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/reveal (renamed from users/grfn/emacs.d/snippets/org-mode/reveal)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/transaction (renamed from users/grfn/emacs.d/snippets/org-mode/transaction)0
-rw-r--r--users/aspen/emacs.d/snippets/prolog-mode/use-module7
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/add_column (renamed from users/grfn/emacs.d/snippets/python-mode/add_column)0
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/decorate15
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/dunder7
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/name7
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/op.get_bind.execute (renamed from users/grfn/emacs.d/snippets/python-mode/op.get_bind.execute)0
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/pdb7
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/#[macro_use] (renamed from users/grfn/emacs.d/snippets/rust-mode/#[macro_use])0
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/async test10
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/benchmark10
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/proptest10
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/test-module11
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/tests (renamed from users/grfn/emacs.d/snippets/rust-mode/tests)0
-rw-r--r--users/aspen/emacs.d/snippets/snippet-mode/indent (renamed from users/grfn/emacs.d/snippets/snippet-mode/indent)0
-rw-r--r--users/aspen/emacs.d/snippets/sql-mode/count(*) group by (renamed from users/grfn/emacs.d/snippets/sql-mode/count(*) group by)0
-rw-r--r--users/aspen/emacs.d/snippets/terraform-mode/variable11
-rw-r--r--users/aspen/emacs.d/snippets/text-mode/date (renamed from users/grfn/emacs.d/snippets/text-mode/date)0
-rw-r--r--users/aspen/emacs.d/splitjoin.el (renamed from users/grfn/emacs.d/splitjoin.el)0
-rw-r--r--users/aspen/emacs.d/sql-strings.el (renamed from users/grfn/emacs.d/sql-strings.el)0
-rw-r--r--users/aspen/emacs.d/terraform.el (renamed from users/grfn/emacs.d/terraform.el)0
-rw-r--r--users/aspen/emacs.d/tests/splitjoin_test.el (renamed from users/grfn/emacs.d/tests/splitjoin_test.el)0
-rw-r--r--users/aspen/emacs.d/themes/grfn-solarized-light-theme.el (renamed from users/grfn/emacs.d/themes/grfn-solarized-light-theme.el)0
-rw-r--r--users/aspen/emacs.d/utils.el (renamed from users/grfn/emacs.d/utils.el)0
-rw-r--r--users/aspen/emacs.d/vterm.el (renamed from users/grfn/emacs.d/vterm.el)0
-rw-r--r--users/aspen/emacs/.gitignore2
-rw-r--r--users/aspen/emacs/config.org1393
-rw-r--r--users/aspen/emacs/init.el199
-rw-r--r--users/aspen/emacs/org-config.el141
-rw-r--r--users/aspen/emacs/packages.el14
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/annotation5
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/benchmark-module26
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/header5
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/hedgehog-generator8
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/hedgehog-property9
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/hlint8
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/import-i4
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/inl6
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/inline5
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/language pragma6
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/lens.field7
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/module32
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/shut up, hlint6
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/test-group9
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/test-module27
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/undefined6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/action-type4
-rw-r--r--users/aspen/emacs/snippets/js2-mode/before7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/context7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/describe6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/expect5
-rw-r--r--users/aspen/emacs/snippets/js2-mode/function6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/header6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/it7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/it-pending5
-rw-r--r--users/aspen/emacs/snippets/js2-mode/module12
-rw-r--r--users/aspen/emacs/snippets/js2-mode/record7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/test7
-rw-r--r--users/aspen/emacs/snippets/nix-mode/fetchFromGitHub12
-rw-r--r--users/aspen/emacs/snippets/nix-mode/pythonPackage16
-rw-r--r--users/aspen/emacs/snippets/nix-mode/sha2567
-rw-r--r--users/aspen/emacs/snippets/org-mode/SQL source block6
-rw-r--r--users/aspen/emacs/snippets/org-mode/combat13
-rw-r--r--users/aspen/emacs/snippets/org-mode/date5
-rw-r--r--users/aspen/emacs/snippets/org-mode/date-time5
-rw-r--r--users/aspen/emacs/snippets/org-mode/description7
-rw-r--r--users/aspen/emacs/snippets/org-mode/nologdone5
-rw-r--r--users/aspen/emacs/snippets/org-mode/python source block6
-rw-r--r--users/aspen/emacs/snippets/org-mode/reveal6
-rw-r--r--users/aspen/emacs/snippets/org-mode/transaction7
-rw-r--r--users/aspen/emacs/snippets/prolog-mode/tests11
-rw-r--r--users/aspen/emacs/snippets/prolog-mode/use-module7
-rw-r--r--users/aspen/emacs/snippets/python-mode/add_column5
-rw-r--r--users/aspen/emacs/snippets/python-mode/decorate15
-rw-r--r--users/aspen/emacs/snippets/python-mode/dunder7
-rw-r--r--users/aspen/emacs/snippets/python-mode/name7
-rw-r--r--users/aspen/emacs/snippets/python-mode/op.get_bind.execute7
-rw-r--r--users/aspen/emacs/snippets/python-mode/pdb7
-rw-r--r--users/aspen/emacs/snippets/rust-mode/#[macro_use]5
-rw-r--r--users/aspen/emacs/snippets/rust-mode/async test10
-rw-r--r--users/aspen/emacs/snippets/rust-mode/benchmark10
-rw-r--r--users/aspen/emacs/snippets/rust-mode/proptest10
-rw-r--r--users/aspen/emacs/snippets/rust-mode/test-module11
-rw-r--r--users/aspen/emacs/snippets/rust-mode/tests9
-rw-r--r--users/aspen/emacs/snippets/snippet-mode/indent5
-rw-r--r--users/aspen/emacs/snippets/sql-mode/count(*) group by5
-rw-r--r--users/aspen/emacs/snippets/terraform-mode/variable11
-rw-r--r--users/aspen/emacs/snippets/text-mode/date5
-rw-r--r--users/aspen/emacs/snippets/tuareg-mode/expect-test9
-rw-r--r--users/aspen/emacs/snippets/tuareg-mode/module9
-rw-r--r--users/aspen/emacs/snippets/tuareg-mode/test-module10
-rw-r--r--users/aspen/goodcry-band/flower-icon.svg1
-rw-r--r--users/aspen/goodcry-band/flower-leaves.svg1
-rw-r--r--users/aspen/goodcry-band/flower.pngbin0 -> 285590 bytes
-rw-r--r--users/aspen/goodcry-band/index.css170
-rw-r--r--users/aspen/goodcry-band/index.html68
-rw-r--r--users/aspen/goodcry-band/reset.css45
-rw-r--r--users/aspen/keyboard/.gitignore (renamed from users/grfn/keyboard/.gitignore)0
-rw-r--r--users/aspen/keyboard/README.org (renamed from users/grfn/keyboard/README.org)0
-rw-r--r--users/aspen/keyboard/default.nix73
-rwxr-xr-xusers/aspen/keyboard/flash2
-rw-r--r--users/aspen/keyboard/increase-tapping-delay.patch (renamed from users/grfn/keyboard/increase-tapping-delay.patch)0
-rw-r--r--users/aspen/keyboard/keymap.c206
-rw-r--r--users/aspen/keys.nix (renamed from users/grfn/keys.nix)0
-rw-r--r--users/aspen/org-clubhouse/.gitignore (renamed from users/grfn/org-clubhouse/.gitignore)0
-rw-r--r--users/aspen/org-clubhouse/CODE_OF_CONDUCT.org (renamed from users/grfn/org-clubhouse/CODE_OF_CONDUCT.org)0
-rw-r--r--users/aspen/org-clubhouse/LICENSE (renamed from users/grfn/org-clubhouse/LICENSE)0
-rw-r--r--users/aspen/org-clubhouse/README.org (renamed from users/grfn/org-clubhouse/README.org)0
-rw-r--r--users/aspen/org-clubhouse/org-clubhouse.el (renamed from users/grfn/org-clubhouse/org-clubhouse.el)0
-rw-r--r--users/aspen/pkgs/cargo-hakari.nix27
-rw-r--r--users/aspen/pkgs/cargo-nextest.nix27
-rw-r--r--users/aspen/pkgs/notmuch-extract-patch.nix18
-rw-r--r--users/aspen/pkgs/py3status.nix12
-rw-r--r--users/aspen/resume/chimera.png (renamed from users/grfn/resume/chimera.png)bin40602 -> 40602 bytes
-rw-r--r--users/aspen/resume/collection.sty (renamed from users/grfn/resume/collection.sty)0
-rw-r--r--users/aspen/resume/default.nix40
-rw-r--r--users/aspen/resume/helvetica.sty (renamed from users/grfn/resume/helvetica.sty)0
-rw-r--r--users/aspen/resume/moderncv.cls (renamed from users/grfn/resume/moderncv.cls)3
-rw-r--r--users/aspen/resume/moderncvcolorblack.sty (renamed from users/grfn/resume/moderncvcolorblack.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorblue.sty (renamed from users/grfn/resume/moderncvcolorblue.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorgreen.sty (renamed from users/grfn/resume/moderncvcolorgreen.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorgrey.sty (renamed from users/grfn/resume/moderncvcolorgrey.sty)0
-rw-r--r--users/aspen/resume/moderncvcolororange.sty (renamed from users/grfn/resume/moderncvcolororange.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorpurple.sty (renamed from users/grfn/resume/moderncvcolorpurple.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorred.sty (renamed from users/grfn/resume/moderncvcolorred.sty)0
-rw-r--r--users/aspen/resume/moderncvcompatibility.sty (renamed from users/grfn/resume/moderncvcompatibility.sty)0
-rw-r--r--users/aspen/resume/moderncviconsletters.sty (renamed from users/grfn/resume/moderncviconsletters.sty)0
-rw-r--r--users/aspen/resume/moderncviconsmarvosym.sty (renamed from users/grfn/resume/moderncviconsmarvosym.sty)0
-rw-r--r--users/aspen/resume/moderncvstylebanking.sty (renamed from users/grfn/resume/moderncvstylebanking.sty)0
-rw-r--r--users/aspen/resume/moderncvstylecasual.sty (renamed from users/grfn/resume/moderncvstylecasual.sty)3
-rw-r--r--users/aspen/resume/moderncvstyleclassic.sty (renamed from users/grfn/resume/moderncvstyleclassic.sty)0
-rw-r--r--users/aspen/resume/moderncvstyleempty.sty (renamed from users/grfn/resume/moderncvstyleempty.sty)0
-rw-r--r--users/aspen/resume/moderncvstyleoldstyle.sty (renamed from users/grfn/resume/moderncvstyleoldstyle.sty)0
-rw-r--r--users/aspen/resume/picture.png (renamed from users/grfn/resume/picture.png)bin14848 -> 14848 bytes
-rw-r--r--users/aspen/resume/resume.tex252
-rw-r--r--users/aspen/resume/tweaklist.sty (renamed from users/grfn/resume/tweaklist.sty)0
-rw-r--r--users/aspen/secrets/.envrc (renamed from users/grfn/bbbg/.envrc)0
-rw-r--r--users/aspen/secrets/bbbg.agebin0 -> 733 bytes
-rw-r--r--users/aspen/secrets/buildkite-ssh-key.agebin0 -> 3883 bytes
-rw-r--r--users/aspen/secrets/buildkite-token.agebin0 -> 623 bytes
-rw-r--r--users/aspen/secrets/cloudflare.age9
-rw-r--r--users/aspen/secrets/ddclient-password.agebin0 -> 429 bytes
-rw-r--r--users/aspen/secrets/default.nix (renamed from users/grfn/secrets/default.nix)0
-rw-r--r--users/aspen/secrets/secrets.nix15
-rw-r--r--users/aspen/secrets/shell.nix8
-rw-r--r--users/aspen/secrets/windtunnel-bot-github-token.age11
-rw-r--r--users/aspen/system/.gitignore (renamed from users/grfn/system/.gitignore)0
-rw-r--r--users/aspen/system/home/.skip-subtree (renamed from users/riking/dotfiles/regolith/flags/first-time-setup-r1-4-1)0
-rw-r--r--users/aspen/system/home/common/solarized.nix18
-rw-r--r--users/aspen/system/home/default.nix42
-rw-r--r--users/aspen/system/home/home.nix (renamed from users/grfn/system/home/home.nix)0
-rw-r--r--users/aspen/system/home/machines/dobharchu.nix20
-rw-r--r--users/aspen/system/home/machines/lusca.nix34
-rw-r--r--users/aspen/system/home/machines/ogopogo.nix78
-rw-r--r--users/aspen/system/home/machines/roswell.nix63
-rw-r--r--users/aspen/system/home/machines/yeren.nix77
-rw-r--r--users/aspen/system/home/modules/.gitignore1
-rw-r--r--users/aspen/system/home/modules/alacritty.nix56
-rw-r--r--users/aspen/system/home/modules/common.nix88
-rw-r--r--users/aspen/system/home/modules/desktop.nix40
-rw-r--r--users/aspen/system/home/modules/development.nix217
-rw-r--r--users/aspen/system/home/modules/development/agda.nix (renamed from users/grfn/system/home/modules/development/agda.nix)6
-rw-r--r--users/aspen/system/home/modules/development/kube.nix (renamed from users/grfn/system/home/modules/development/kube.nix)2
-rw-r--r--users/aspen/system/home/modules/development/ocaml.nix17
-rw-r--r--users/aspen/system/home/modules/development/readyset.nix39
-rw-r--r--users/aspen/system/home/modules/development/rust.nix49
-rw-r--r--users/aspen/system/home/modules/emacs.nix108
-rw-r--r--users/aspen/system/home/modules/email.nix86
-rw-r--r--users/aspen/system/home/modules/firefox.nix (renamed from users/grfn/system/home/modules/firefox.nix)0
-rw-r--r--users/aspen/system/home/modules/games.nix51
-rw-r--r--users/aspen/system/home/modules/i3.nix397
-rw-r--r--users/aspen/system/home/modules/lib/cloneRepo.nix76
-rw-r--r--users/aspen/system/home/modules/lib/zshFunctions.nix23
-rw-r--r--users/aspen/system/home/modules/obs.nix18
-rw-r--r--users/aspen/system/home/modules/ptt.nix (renamed from users/grfn/system/home/modules/ptt.nix)0
-rwxr-xr-xusers/aspen/system/home/modules/pure.zsh-theme (renamed from users/grfn/system/home/modules/pure.zsh-theme)4
-rw-r--r--users/aspen/system/home/modules/rtlsdr.nix23
-rw-r--r--users/aspen/system/home/modules/shell.nix166
-rw-r--r--users/aspen/system/home/modules/tarsnap.nix64
-rw-r--r--users/aspen/system/home/modules/tmux.nix42
-rw-r--r--users/aspen/system/home/modules/twitter.nix (renamed from users/grfn/system/home/modules/twitter.nix)4
-rw-r--r--users/aspen/system/home/modules/vim.nix (renamed from users/grfn/system/home/modules/vim.nix)0
-rw-r--r--users/aspen/system/home/modules/vimrc (renamed from users/grfn/system/home/modules/vimrc)2
-rw-r--r--users/aspen/system/home/modules/zshrc (renamed from users/grfn/system/home/modules/zshrc)0
-rw-r--r--users/aspen/system/home/platforms/darwin.nix (renamed from users/grfn/system/home/platforms/darwin.nix)2
-rw-r--r--users/aspen/system/home/platforms/linux.nix78
-rwxr-xr-xusers/aspen/system/install (renamed from users/grfn/system/install)0
-rw-r--r--users/aspen/system/system/.skip-subtree (renamed from users/riking/dotfiles/regolith/flags/show-shortcuts)0
-rw-r--r--users/aspen/system/system/configuration.nix (renamed from users/grfn/system/system/configuration.nix)0
-rw-r--r--users/aspen/system/system/default.nix48
-rw-r--r--users/aspen/system/system/iso.nix22
-rw-r--r--users/aspen/system/system/machines/bumblebee.nix (renamed from users/grfn/system/system/machines/bumblebee.nix)2
-rw-r--r--users/aspen/system/system/machines/lusca.nix142
-rw-r--r--users/aspen/system/system/machines/mugwump.nix306
-rw-r--r--users/aspen/system/system/machines/ogopogo.nix107
-rw-r--r--users/aspen/system/system/machines/roswell.nix27
-rw-r--r--users/aspen/system/system/machines/yeren.nix132
-rw-r--r--users/aspen/system/system/modules/common.nix97
-rw-r--r--users/aspen/system/system/modules/desktop.nix19
-rw-r--r--users/aspen/system/system/modules/development.nix15
-rw-r--r--users/aspen/system/system/modules/fcitx.nix (renamed from users/grfn/system/system/modules/fcitx.nix)0
-rw-r--r--users/aspen/system/system/modules/fonts.nix13
-rw-r--r--users/aspen/system/system/modules/laptop.nix23
-rw-r--r--users/aspen/system/system/modules/reusable/README.org (renamed from users/grfn/system/system/modules/reusable/README.org)0
-rw-r--r--users/aspen/system/system/modules/rtlsdr.nix (renamed from users/grfn/system/system/modules/rtlsdr.nix)0
-rw-r--r--users/aspen/system/system/modules/sound.nix (renamed from users/grfn/system/system/modules/sound.nix)0
-rw-r--r--users/aspen/system/system/modules/tvl.nix35
-rw-r--r--users/aspen/system/system/modules/wireshark.nix9
-rw-r--r--users/aspen/system/system/modules/xserver.nix (renamed from users/grfn/system/system/modules/xserver.nix)2
-rw-r--r--users/aspen/terraform/globals.nix27
-rw-r--r--users/aspen/terraform/nixosMachine.nix (renamed from users/grfn/terraform/nixosMachine.nix)73
-rw-r--r--users/aspen/terraform/workspace.nix (renamed from users/grfn/terraform/workspace.nix)29
-rw-r--r--users/aspen/web/.envrc2
-rw-r--r--users/aspen/web/.gitignore (renamed from users/grfn/gws.fyi/.gitignore)0
-rw-r--r--users/aspen/web/Makefile40
-rw-r--r--users/aspen/web/config.el (renamed from users/grfn/gws.fyi/config.el)0
-rw-r--r--users/aspen/web/default.nix62
-rw-r--r--users/aspen/web/index.org40
-rw-r--r--users/aspen/web/main.css (renamed from users/grfn/gws.fyi/main.css)0
-rw-r--r--users/aspen/web/orgExportHTML.nix67
-rw-r--r--users/aspen/web/pubkey.gpg206
-rw-r--r--users/aspen/web/recipes/tomato-sauce.org (renamed from users/grfn/gws.fyi/recipes/tomato-sauce.org)4
-rw-r--r--users/aspen/web/shell.nix9
-rw-r--r--users/aspen/web/site.nix (renamed from users/grfn/gws.fyi/site.nix)2
-rw-r--r--users/aspen/wigglydonke.rs/.well-known/cf-2fa-verify.txt1
-rw-r--r--users/aspen/wigglydonke.rs/index.html (renamed from users/grfn/wigglydonke.rs/index.html)0
-rw-r--r--users/aspen/wigglydonke.rs/wd.png (renamed from users/grfn/wigglydonke.rs/wd.png)bin1624030 -> 1624030 bytes
-rw-r--r--users/aspen/xanthous/.envrc (renamed from users/grfn/gws.fyi/.envrc)0
-rw-r--r--users/aspen/xanthous/.github/actions/nix-build/Dockerfile (renamed from users/grfn/xanthous/.github/actions/nix-build/Dockerfile)0
-rwxr-xr-xusers/aspen/xanthous/.github/actions/nix-build/entrypoint.sh (renamed from users/grfn/xanthous/.github/actions/nix-build/entrypoint.sh)0
-rw-r--r--users/aspen/xanthous/.github/workflows/haskell.yml (renamed from users/grfn/xanthous/.github/workflows/haskell.yml)0
-rw-r--r--users/aspen/xanthous/.gitignore (renamed from users/grfn/xanthous/.gitignore)0
-rw-r--r--users/aspen/xanthous/LICENSE (renamed from users/grfn/xanthous/LICENSE)0
-rw-r--r--users/aspen/xanthous/README.org (renamed from users/grfn/xanthous/README.org)0
-rw-r--r--users/aspen/xanthous/Setup.hs (renamed from users/grfn/xanthous/Setup.hs)0
-rw-r--r--users/aspen/xanthous/app/Main.hs (renamed from users/grfn/xanthous/app/Main.hs)0
-rw-r--r--users/aspen/xanthous/bench/Bench.hs (renamed from users/grfn/xanthous/bench/Bench.hs)0
-rw-r--r--users/aspen/xanthous/bench/Bench/Prelude.hs (renamed from users/grfn/xanthous/bench/Bench/Prelude.hs)0
-rw-r--r--users/aspen/xanthous/bench/Xanthous/Generators/UtilBench.hs (renamed from users/grfn/xanthous/bench/Xanthous/Generators/UtilBench.hs)0
-rw-r--r--users/aspen/xanthous/bench/Xanthous/RandomBench.hs (renamed from users/grfn/xanthous/bench/Xanthous/RandomBench.hs)0
-rw-r--r--users/aspen/xanthous/build/generic-arbitrary-export-garbitrary.patch (renamed from third_party/overlays/haskell/patches/generic-arbitrary-export-garbitrary.patch)0
-rw-r--r--users/aspen/xanthous/build/hgeometry-fix-haddock.patch (renamed from users/grfn/xanthous/build/hgeometry-fix-haddock.patch)0
-rw-r--r--users/aspen/xanthous/build/update-comonad-extras.patch (renamed from users/grfn/xanthous/build/update-comonad-extras.patch)0
-rw-r--r--users/aspen/xanthous/default.nix27
-rw-r--r--users/aspen/xanthous/docs/raw-types.org (renamed from users/grfn/xanthous/docs/raw-types.org)0
-rw-r--r--users/aspen/xanthous/hie.yaml (renamed from users/grfn/xanthous/hie.yaml)0
-rw-r--r--users/aspen/xanthous/nixpkgs.nix (renamed from users/grfn/xanthous/nixpkgs.nix)0
-rw-r--r--users/aspen/xanthous/package.yaml (renamed from users/grfn/xanthous/package.yaml)1
-rw-r--r--users/aspen/xanthous/pkg.nix349
-rw-r--r--users/aspen/xanthous/server/.envrc (renamed from users/grfn/secrets/.envrc)0
-rw-r--r--users/aspen/xanthous/server/.gitignore1
-rw-r--r--users/aspen/xanthous/server/Cargo.lock1874
-rw-r--r--users/aspen/xanthous/server/Cargo.toml29
-rw-r--r--users/aspen/xanthous/server/default.nix24
-rw-r--r--users/aspen/xanthous/server/docker.nix21
-rw-r--r--users/aspen/xanthous/server/module.nix49
-rw-r--r--users/aspen/xanthous/server/shell.nix11
-rw-r--r--users/aspen/xanthous/server/src/main.rs385
-rw-r--r--users/aspen/xanthous/server/src/metrics.rs (renamed from users/grfn/xanthous/server/src/metrics.rs)0
-rw-r--r--users/aspen/xanthous/server/src/pty.rs (renamed from users/grfn/xanthous/server/src/pty.rs)3
-rw-r--r--users/aspen/xanthous/shell.nix23
-rw-r--r--users/aspen/xanthous/src/Data/Aeson/Generic/DerivingVia.hs (renamed from users/grfn/xanthous/src/Data/Aeson/Generic/DerivingVia.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/AI/Gormlak.hs (renamed from users/grfn/xanthous/src/Xanthous/AI/Gormlak.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/App.hs647
-rw-r--r--users/aspen/xanthous/src/Xanthous/App/Autocommands.hs (renamed from users/grfn/xanthous/src/Xanthous/App/Autocommands.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/App/Common.hs (renamed from users/grfn/xanthous/src/Xanthous/App/Common.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/App/Prompt.hs (renamed from users/grfn/xanthous/src/Xanthous/App/Prompt.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/App/Time.hs (renamed from users/grfn/xanthous/src/Xanthous/App/Time.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Command.hs145
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data.hs (renamed from users/grfn/xanthous/src/Xanthous/Data.hs)14
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/App.hs47
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/Entities.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/Entities.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/EntityChar.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/EntityChar.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/EntityMap.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/EntityMap.hs)1
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/Levels.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/Levels.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/Memo.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/Memo.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/NestedMap.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/NestedMap.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/VectorBag.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/VectorBag.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Character.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Character.hs)5
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Common.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Common.hs)72
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Creature.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Creature.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs)1
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Draw/Util.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Draw/Util.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Entities.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Entities.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Entities.hs-boot (renamed from users/grfn/xanthous/src/Xanthous/Entities/Entities.hs-boot)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Environment.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Environment.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Item.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Item.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Marker.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Marker.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/RawTypes.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/RawTypes.hs)9
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml)4
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/husk.yaml (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws/husk.yaml)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/noodles.yaml (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws/noodles.yaml)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/ooze.yaml (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws/ooze.yaml)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/rock.yaml10
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/stick.yaml (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws/stick.yaml)6
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game.hs (renamed from users/grfn/xanthous/src/Xanthous/Game.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Arbitrary.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Arbitrary.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Draw.hs224
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Env.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Env.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Lenses.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Lenses.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Memo.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Memo.hs)2
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Prompt.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Prompt.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/State.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/State.hs)1
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/Dungeon.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/Dungeon.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/LevelContents.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/LevelContents.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/Util.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/Util.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/Village.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/Village.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Speech.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Speech.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Messages.hs (renamed from users/grfn/xanthous/src/Xanthous/Messages.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Messages/Template.hs (renamed from users/grfn/xanthous/src/Xanthous/Messages/Template.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Monad.hs (renamed from users/grfn/xanthous/src/Xanthous/Monad.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Orphans.hs (renamed from users/grfn/xanthous/src/Xanthous/Orphans.hs)18
-rw-r--r--users/aspen/xanthous/src/Xanthous/Physics.hs (renamed from users/grfn/xanthous/src/Xanthous/Physics.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Prelude.hs (renamed from users/grfn/xanthous/src/Xanthous/Prelude.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Random.hs (renamed from users/grfn/xanthous/src/Xanthous/Random.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util.hs (renamed from users/grfn/xanthous/src/Xanthous/Util.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Comonad.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Comonad.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Graph.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Graph.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Graphics.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Graphics.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Inflection.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Inflection.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/JSON.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/JSON.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Optparse.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Optparse.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/QuickCheck.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/QuickCheck.hs)10
-rw-r--r--users/aspen/xanthous/src/Xanthous/keybindings.yaml22
-rw-r--r--users/aspen/xanthous/src/Xanthous/messages.yaml (renamed from users/grfn/xanthous/src/Xanthous/messages.yaml)6
-rw-r--r--users/aspen/xanthous/test/Spec.hs (renamed from users/grfn/xanthous/test/Spec.hs)4
-rw-r--r--users/aspen/xanthous/test/Test/Prelude.hs (renamed from users/grfn/xanthous/test/Test/Prelude.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/CommandSpec.hs40
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/EntitiesSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/EntitiesSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/EntityCharSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/EntityCharSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/EntityMapSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/EntityMapSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/LevelsSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/LevelsSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/MemoSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/MemoSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/NestedMapSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/NestedMapSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/DataSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/DataSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Entities/CharacterSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Entities/CharacterSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Entities/CommonSpec.hs65
-rw-r--r--users/aspen/xanthous/test/Xanthous/Entities/RawTypesSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Entities/RawTypesSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Entities/RawsSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Entities/RawsSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Game/PromptSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Game/PromptSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Game/StateSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Game/StateSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/GameSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/GameSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/MessageSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/MessageSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Messages/TemplateSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Messages/TemplateSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/OrphansSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/OrphansSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/RandomSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/RandomSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Util/GraphSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Util/GraphSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Util/GraphicsSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Util/GraphicsSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Util/InflectionSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Util/InflectionSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/UtilSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/UtilSpec.hs)0
-rw-r--r--users/aspen/xanthous/xanthous.cabal (renamed from users/grfn/xanthous/xanthous.cabal)13
-rw-r--r--users/cynthia/OWNERS6
-rw-r--r--users/cynthia/keys.nix2
-rw-r--r--users/edef/OWNERS6
-rw-r--r--users/edef/depot-scan/wrap.nix7
-rw-r--r--users/edef/refscan/.gitignore5
-rw-r--r--users/edef/refscan/Cargo.lock7
-rw-r--r--users/edef/refscan/Cargo.lock.license3
-rw-r--r--users/edef/refscan/Cargo.toml10
-rw-r--r--users/edef/refscan/LICENSES/CC0-1.0.txt121
-rw-r--r--users/edef/refscan/LICENSES/MPL-2.0.txt373
-rw-r--r--users/edef/refscan/src/lib.rs154
-rw-r--r--users/edef/refscan/src/main.rs58
-rw-r--r--users/edef/refscan/testdata/.gitignore6
-rwxr-xr-xusers/edef/refscan/testdata/generate.sh8
-rw-r--r--users/ericvolp12/OWNERS6
-rw-r--r--users/espes/OWNERS1
-rw-r--r--users/eta/OWNERS6
-rw-r--r--users/eta/keys.nix2
-rw-r--r--users/firefly/OWNERS6
-rw-r--r--users/flokli/OWNERS6
-rw-r--r--users/flokli/archeology/OWNERS1
-rw-r--r--users/flokli/archeology/README.md5
-rw-r--r--users/flokli/archeology/default.nix51
-rw-r--r--users/flokli/archeology/parse_bucket_logs.rs42
-rw-r--r--users/flokli/archivist/OWNERS1
-rw-r--r--users/flokli/archivist/default.nix28
-rw-r--r--users/flokli/ipu6-softisp/README.md29
-rw-r--r--users/flokli/ipu6-softisp/config.nix74
-rw-r--r--users/flokli/ipu6-softisp/default.nix57
-rw-r--r--users/flokli/ipu6-softisp/kernel/.skip-tree (renamed from users/riking/dotfiles/regolith/flags/term-profile)0
-rw-r--r--users/flokli/ipu6-softisp/kernel/softisp.patch18077
-rw-r--r--users/flokli/ipu6-softisp/libcamera/.skip-tree0
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch82
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch350
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch169
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch69
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch403
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch523
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch255
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch825
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch506
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch507
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch240
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch302
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch203
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch234
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch127
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch237
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch132
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch396
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch239
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch95
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch42
-rw-r--r--users/flokli/keyboards/dilemma/default.nix45
-rw-r--r--users/flokli/keyboards/dilemma/enable-taps.patch24
-rw-r--r--users/flokli/keyboards/dilemma/keymap.c220
-rw-r--r--users/flokli/keyboards/dilemma/rules.mk2
-rw-r--r--users/flokli/keyboards/k6_pro/default.nix39
-rw-r--r--users/flokli/keyboards/k6_pro/keymap.c76
-rw-r--r--users/flokli/keyboards/k6_pro/rules.mk2
-rw-r--r--users/flokli/nixos/.envrc1
-rw-r--r--users/flokli/nixos/.skip-subtree0
-rw-r--r--users/flokli/nixos/archeology-ec2/OWNERS1
-rw-r--r--users/flokli/nixos/archeology-ec2/configuration.nix35
-rw-r--r--users/flokli/nixos/archeology-ec2/hardware-configuration.nix36
-rw-r--r--users/flokli/nixos/archeology-ec2/parse-bucket-logs-continuously.py62
-rw-r--r--users/flokli/nixos/default.nix32
-rw-r--r--users/flokli/nixos/profiles/archeology.nix37
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/.gitignore1
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/architecture.dot5
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/cppnix-example-lexer.cpp13
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/crate-deps.dot19
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/default.nix37
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/presentation.md294
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/tvix-store-graph.svg17
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/tvixbolt.webmbin0 -> 983213 bytes
-rw-r--r--users/flokli/presentations/2023-09-13-asg-tvix-store/default.nix32
-rw-r--r--users/flokli/presentations/2023-09-13-asg-tvix-store/presentation.md138
-rw-r--r--users/flokli/presentations/2023-09-13-asg-tvix-store/tvix-store-graph-blob-directory.svg17
-rw-r--r--users/fogti/.gitignore (renamed from users/zseri/.gitignore)0
-rw-r--r--users/fogti/OWNERS3
-rw-r--r--users/fogti/dbwospof.md (renamed from users/zseri/dbwospof.md)0
-rw-r--r--users/fogti/store-ref-scanner/.gitignore (renamed from users/zseri/store-ref-scanner/.gitignore)0
-rw-r--r--users/fogti/store-ref-scanner/Cargo.toml11
-rw-r--r--users/fogti/store-ref-scanner/default.nix (renamed from users/zseri/store-ref-scanner/default.nix)0
-rw-r--r--users/fogti/store-ref-scanner/fuzz/.gitignore (renamed from users/zseri/store-ref-scanner/fuzz/.gitignore)0
-rw-r--r--users/fogti/store-ref-scanner/fuzz/Cargo.lock (renamed from users/zseri/store-ref-scanner/fuzz/Cargo.lock)0
-rw-r--r--users/fogti/store-ref-scanner/fuzz/Cargo.toml (renamed from users/zseri/store-ref-scanner/fuzz/Cargo.toml)0
-rw-r--r--users/fogti/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs (renamed from users/zseri/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs)0
-rw-r--r--users/fogti/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs (renamed from users/zseri/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs)0
-rw-r--r--users/fogti/store-ref-scanner/src/hbm.rs (renamed from users/zseri/store-ref-scanner/src/hbm.rs)0
-rw-r--r--users/fogti/store-ref-scanner/src/lib.rs (renamed from users/zseri/store-ref-scanner/src/lib.rs)0
-rw-r--r--users/fogti/store-ref-scanner/src/spec.rs (renamed from users/zseri/store-ref-scanner/src/spec.rs)0
-rw-r--r--users/grfn/OWNERS3
-rw-r--r--users/grfn/achilles/Cargo.lock868
-rw-r--r--users/grfn/achilles/default.nix24
-rw-r--r--users/grfn/achilles/shell.nix18
-rw-r--r--users/grfn/achilles/src/commands/eval.rs32
-rw-r--r--users/grfn/bbbg/arion-pkgs.nix2
-rw-r--r--users/grfn/bbbg/default.nix81
-rw-r--r--users/grfn/bbbg/deps.nix1489
-rw-r--r--users/grfn/bbbg/module.nix136
-rw-r--r--users/grfn/bbbg/shell.nix29
-rw-r--r--users/grfn/bbbg/src/bbbg/core.clj58
-rw-r--r--users/grfn/emacs.d/cpp.el39
-rw-r--r--users/grfn/emacs.d/init.el172
-rw-r--r--users/grfn/emacs.d/org-config.el193
-rw-r--r--users/grfn/emacs.d/packages.el152
-rw-r--r--users/grfn/emacs.d/snippets/haskell-mode/hlint8
-rw-r--r--users/grfn/emacs.d/snippets/haskell-mode/test-group9
-rw-r--r--users/grfn/emacs.d/snippets/nix-mode/fetchFromGitHub12
-rw-r--r--users/grfn/emacs.d/snippets/nix-mode/sha2567
-rw-r--r--users/grfn/emacs.d/snippets/org-mode/combat13
-rw-r--r--users/grfn/emacs.d/snippets/prolog-mode/use-module7
-rw-r--r--users/grfn/emacs.d/snippets/python-mode/decorate15
-rw-r--r--users/grfn/emacs.d/snippets/python-mode/dunder7
-rw-r--r--users/grfn/emacs.d/snippets/python-mode/name7
-rw-r--r--users/grfn/emacs.d/snippets/python-mode/pdb7
-rw-r--r--users/grfn/emacs.d/snippets/rust-mode/async test10
-rw-r--r--users/grfn/emacs.d/snippets/rust-mode/benchmark10
-rw-r--r--users/grfn/emacs.d/snippets/rust-mode/proptest10
-rw-r--r--users/grfn/emacs.d/snippets/terraform-mode/variable11
-rw-r--r--users/grfn/gws.fyi/Makefile31
-rw-r--r--users/grfn/gws.fyi/default.nix35
-rw-r--r--users/grfn/gws.fyi/index.org39
-rw-r--r--users/grfn/gws.fyi/orgExportHTML.nix70
-rw-r--r--users/grfn/gws.fyi/shell.nix9
-rw-r--r--users/grfn/keyboard/default.nix63
-rwxr-xr-xusers/grfn/keyboard/flash2
-rw-r--r--users/grfn/keyboard/keymap.c206
-rw-r--r--users/grfn/resume/default.nix37
-rw-r--r--users/grfn/resume/resume.tex212
-rw-r--r--users/grfn/secrets/bbbg.age12
-rw-r--r--users/grfn/secrets/buildkite-ssh-key.agebin3853 -> 0 bytes
-rw-r--r--users/grfn/secrets/buildkite-token.age12
-rw-r--r--users/grfn/secrets/cloudflare.age9
-rw-r--r--users/grfn/secrets/ddclient-password.age9
-rw-r--r--users/grfn/secrets/secrets.nix13
-rw-r--r--users/grfn/secrets/shell.nix8
-rw-r--r--users/grfn/system/home/common/solarized.nix18
-rw-r--r--users/grfn/system/home/default.nix32
-rw-r--r--users/grfn/system/home/machines/dobharchu.nix17
-rw-r--r--users/grfn/system/home/machines/roswell.nix54
-rw-r--r--users/grfn/system/home/machines/yeren.nix77
-rw-r--r--users/grfn/system/home/modules/alacritty.nix56
-rw-r--r--users/grfn/system/home/modules/alsi.nix58
-rw-r--r--users/grfn/system/home/modules/common.nix105
-rw-r--r--users/grfn/system/home/modules/development.nix213
-rw-r--r--users/grfn/system/home/modules/development/readyset.nix16
-rw-r--r--users/grfn/system/home/modules/development/rust.nix34
-rw-r--r--users/grfn/system/home/modules/emacs.nix108
-rw-r--r--users/grfn/system/home/modules/email.nix87
-rw-r--r--users/grfn/system/home/modules/games.nix59
-rw-r--r--users/grfn/system/home/modules/i3.nix366
-rw-r--r--users/grfn/system/home/modules/lib/cloneRepo.nix67
-rw-r--r--users/grfn/system/home/modules/lib/zshFunctions.nix21
-rw-r--r--users/grfn/system/home/modules/nixos-logo.txt26
-rw-r--r--users/grfn/system/home/modules/obs.nix66
-rw-r--r--users/grfn/system/home/modules/rtlsdr.nix21
-rw-r--r--users/grfn/system/home/modules/shell.nix184
-rw-r--r--users/grfn/system/home/modules/tarsnap.nix64
-rw-r--r--users/grfn/system/home/platforms/linux.nix92
-rw-r--r--users/grfn/system/system/default.nix38
-rw-r--r--users/grfn/system/system/iso.nix17
-rw-r--r--users/grfn/system/system/machines/mugwump.nix279
-rw-r--r--users/grfn/system/system/machines/roswell.nix17
-rw-r--r--users/grfn/system/system/machines/yeren.nix132
-rw-r--r--users/grfn/system/system/modules/common.nix87
-rw-r--r--users/grfn/system/system/modules/desktop.nix19
-rw-r--r--users/grfn/system/system/modules/development.nix6
-rw-r--r--users/grfn/system/system/modules/fonts.nix12
-rw-r--r--users/grfn/system/system/modules/laptop.nix15
-rw-r--r--users/grfn/system/system/modules/reusable/battery.nix32
-rw-r--r--users/grfn/system/system/modules/tvl.nix37
-rw-r--r--users/grfn/system/system/modules/work/kolide.debbin25094998 -> 0 bytes
-rw-r--r--users/grfn/system/system/modules/work/kolide.nix49
-rw-r--r--users/grfn/terraform/globals.nix24
-rw-r--r--users/grfn/xanthous/.envrc1
-rw-r--r--users/grfn/xanthous/build/generic-arbitrary-export-garbitrary.patch12
-rw-r--r--users/grfn/xanthous/default.nix26
-rw-r--r--users/grfn/xanthous/pkg.nix80
-rw-r--r--users/grfn/xanthous/server/.envrc1
-rw-r--r--users/grfn/xanthous/server/Cargo.lock1937
-rw-r--r--users/grfn/xanthous/server/Cargo.toml29
-rw-r--r--users/grfn/xanthous/server/default.nix14
-rw-r--r--users/grfn/xanthous/server/docker.nix19
-rw-r--r--users/grfn/xanthous/server/module.nix48
-rw-r--r--users/grfn/xanthous/server/shell.nix11
-rw-r--r--users/grfn/xanthous/server/src/main.rs388
-rw-r--r--users/grfn/xanthous/shell.nix23
-rw-r--r--users/grfn/xanthous/src/Xanthous/App.hs607
-rw-r--r--users/grfn/xanthous/src/Xanthous/Command.hs84
-rw-r--r--users/grfn/xanthous/src/Xanthous/Data/App.hs45
-rw-r--r--users/grfn/xanthous/src/Xanthous/Entities/Raws/rock.yaml10
-rw-r--r--users/grfn/xanthous/src/Xanthous/Game/Draw.hs151
-rw-r--r--users/grfn/xanthous/test/Xanthous/Entities/CommonSpec.hs32
-rw-r--r--users/isomer/OWNERS3
-rw-r--r--users/isomer/keys.nix7
-rw-r--r--users/j4m3s/OWNERS3
-rw-r--r--users/j4m3s/keys.nix7
-rw-r--r--users/lukegb/OWNERS6
-rw-r--r--users/lukegb/keys.nix5
-rw-r--r--users/padraic-o-mhuiris/OWNERS3
-rw-r--r--users/picnoir/tvix-daemon/.gitignore1
-rw-r--r--users/picnoir/tvix-daemon/Cargo.lock1541
-rw-r--r--users/picnoir/tvix-daemon/Cargo.nix5754
-rw-r--r--users/picnoir/tvix-daemon/Cargo.toml15
-rw-r--r--users/picnoir/tvix-daemon/README.md16
-rw-r--r--users/picnoir/tvix-daemon/default.nix43
-rw-r--r--users/picnoir/tvix-daemon/shell.nix21
-rw-r--r--users/picnoir/tvix-daemon/src/main.rs116
-rw-r--r--users/picnoir/tvix-daemon/vm-test/README.md5
-rw-r--r--users/picnoir/tvix-daemon/vm-test/default.nix28
-rw-r--r--users/qyliss/OWNERS6
-rw-r--r--users/riking/OWNERS3
-rw-r--r--users/riking/adventofcode-2020/.gitignore2
-rw-r--r--users/riking/adventofcode-2020/day01/Cargo.lock14
-rw-r--r--users/riking/adventofcode-2020/day01/Cargo.toml10
-rw-r--r--users/riking/adventofcode-2020/day01/default.nix10
-rw-r--r--users/riking/adventofcode-2020/day01/src/main.rs85
-rw-r--r--users/riking/dotfiles/.mybashrc53
-rw-r--r--users/riking/dotfiles/fish/conf.d/nix-env.fish141
-rw-r--r--users/riking/dotfiles/fish/config.fish8
-rw-r--r--users/riking/dotfiles/fish/fish_variables32
-rw-r--r--users/riking/dotfiles/fish/functions/ddate.fish3
-rw-r--r--users/riking/dotfiles/fish/functions/gh-clone.fish18
-rw-r--r--users/riking/dotfiles/fish/functions/prodaccess.fish6
-rw-r--r--users/riking/dotfiles/fish/functions/reset-audio.fish4
-rw-r--r--users/riking/dotfiles/fish/functions/tvl-push.fish3
-rw-r--r--users/riking/dotfiles/regolith/Xresources5
-rw-r--r--users/riking/dotfiles/regolith/flags/ui-fingerprint1
-rwxr-xr-xusers/riking/dotfiles/regolith/initrc3
-rw-r--r--users/riking/dotfiles/tmux.conf6
-rw-r--r--users/riking/keys.nix20
-rw-r--r--users/sterni/OWNERS6
-rw-r--r--users/sterni/clhs-lookup/default.nix2
-rw-r--r--users/sterni/dot-time-man-pages/OWNERS4
-rw-r--r--users/sterni/dot-time-man-pages/default.nix12
-rw-r--r--users/sterni/emacs/default.nix99
-rw-r--r--users/sterni/emacs/init.el178
-rw-r--r--users/sterni/emacs/subscriptions.el149
-rw-r--r--users/sterni/exercises/aoc/.gitignore3
-rw-r--r--users/sterni/exercises/aoc/2021/default.nix2
-rwxr-xr-xusers/sterni/exercises/aoc/2021/solutions.bqn12
-rw-r--r--users/sterni/exercises/aoc/2022/.skip-subtree1
-rw-r--r--users/sterni/exercises/aoc/2022/01/1.bqn7
-rw-r--r--users/sterni/exercises/aoc/2022/01/1.k1
-rw-r--r--users/sterni/exercises/aoc/2022/02/2.bqn7
-rw-r--r--users/sterni/exercises/aoc/2022/02/2.k1
-rw-r--r--users/sterni/exercises/aoc/2022/03/3.bqn8
-rw-r--r--users/sterni/exercises/aoc/2022/03/3.k1
-rw-r--r--users/sterni/exercises/aoc/2022/04/4.bqn11
-rw-r--r--users/sterni/exercises/aoc/2022/05/5.bqn18
-rw-r--r--users/sterni/exercises/aoc/2022/06/6.bqn4
-rw-r--r--users/sterni/exercises/aoc/2022/06/6.k1
-rw-r--r--users/sterni/exercises/aoc/2022/07/7.bqn24
-rw-r--r--users/sterni/exercises/aoc/2022/08/8.bqn15
-rw-r--r--users/sterni/exercises/aoc/2022/09/9.bqn17
-rw-r--r--users/sterni/exercises/aoc/2022/10/10.bqn25
-rw-r--r--users/sterni/exercises/aoc/2022/11/11.bqn41
-rw-r--r--users/sterni/exercises/aoc/2022/12/12.bqn16
-rw-r--r--users/sterni/exercises/aoc/2022/13/13.bqn14
-rw-r--r--users/sterni/exercises/aoc/2022/15/15.bqn18
-rw-r--r--users/sterni/exercises/aoc/2022/16/16.k21
-rw-r--r--users/sterni/exercises/aoc/2022/17/17.bqn51
-rw-r--r--users/sterni/exercises/aoc/2022/18/18.bqn14
-rw-r--r--users/sterni/exercises/aoc/2022/20/20.bqn13
-rw-r--r--users/sterni/exercises/aoc/2022/21/21.bqn25
-rw-r--r--users/sterni/exercises/aoc/2022/25/25.bqn4
-rw-r--r--users/sterni/exercises/aoc/2022/25/25.k1
-rw-r--r--users/sterni/exercises/aoc/2022/README.md8
-rw-r--r--users/sterni/exercises/aoc/2022/default.nix53
-rw-r--r--users/sterni/exercises/aoc/lib.bqn18
-rw-r--r--users/sterni/external/flipdot-gschichtler.nix9
-rw-r--r--users/sterni/external/likely-music.nix11
-rw-r--r--users/sterni/external/sources.json26
-rw-r--r--users/sterni/external/sources.nix197
-rw-r--r--users/sterni/htmlman/default.nix188
-rw-r--r--users/sterni/keys.nix1
-rw-r--r--users/sterni/lv/gopher/default.nix8
-rw-r--r--users/sterni/machines/.skip-subtree1
-rw-r--r--users/sterni/machines/default.nix81
-rw-r--r--users/sterni/machines/edwin/default.nix19
-rw-r--r--users/sterni/machines/edwin/hardware.nix63
-rw-r--r--users/sterni/machines/edwin/network.nix62
-rw-r--r--users/sterni/machines/ingeborg/default.nix33
-rw-r--r--users/sterni/machines/ingeborg/gopher.nix19
-rw-r--r--users/sterni/machines/ingeborg/hardware.nix76
-rw-r--r--users/sterni/machines/ingeborg/http/code.sterni.lv.nix263
-rw-r--r--users/sterni/machines/ingeborg/http/fcgiwrap.nix15
-rw-r--r--users/sterni/machines/ingeborg/http/flipdot.openlab-augsburg.de.nix36
-rw-r--r--users/sterni/machines/ingeborg/http/likely-music.sterni.lv.nix23
-rw-r--r--users/sterni/machines/ingeborg/http/nginx.nix30
-rw-r--r--users/sterni/machines/ingeborg/http/sterni.lv.nix34
-rw-r--r--users/sterni/machines/ingeborg/irccat.nix23
-rw-r--r--users/sterni/machines/ingeborg/minecraft.nix125
-rw-r--r--users/sterni/machines/ingeborg/monitoring.nix152
-rw-r--r--users/sterni/machines/ingeborg/network.nix62
-rw-r--r--users/sterni/machines/ingeborg/quassel.nix18
-rw-r--r--users/sterni/machines/ingeborg/tv.nix13
-rw-r--r--users/sterni/mblog/.gitignore5
-rw-r--r--users/sterni/mblog/LICENSE674
-rw-r--r--users/sterni/mblog/cli.lisp84
-rw-r--r--users/sterni/mblog/config.lisp31
-rw-r--r--users/sterni/mblog/default.nix24
-rw-r--r--users/sterni/mblog/maildir.lisp20
-rw-r--r--users/sterni/mblog/mblog.lisp147
-rw-r--r--users/sterni/mblog/note.lisp118
-rw-r--r--users/sterni/mblog/packages.lisp59
-rw-r--r--users/sterni/mblog/transformer.lisp17
-rw-r--r--users/sterni/modules/backup-minecraft-fabric.nix125
-rw-r--r--users/sterni/modules/common.nix79
-rw-r--r--users/sterni/modules/default.nix2
-rw-r--r--users/sterni/modules/minecraft-fabric.nix532
-rw-r--r--users/sterni/nix/build/buildGopherHole/default.nix109
-rw-r--r--users/sterni/nix/char/default.nix32
-rw-r--r--users/sterni/nix/char/tests/default.nix12
-rw-r--r--users/sterni/nix/float/default.nix23
-rw-r--r--users/sterni/nix/float/tests/default.nix49
-rw-r--r--users/sterni/nix/flow/default.nix9
-rw-r--r--users/sterni/nix/flow/tests/default.nix8
-rw-r--r--users/sterni/nix/fun/default.nix196
-rw-r--r--users/sterni/nix/fun/tests/default.nix59
-rw-r--r--users/sterni/nix/html/default.nix19
-rw-r--r--users/sterni/nix/html/tests/default.nix73
-rw-r--r--users/sterni/nix/int/default.nix56
-rw-r--r--users/sterni/nix/int/tests/default.nix402
-rw-r--r--users/sterni/nix/list/default.nix30
-rw-r--r--users/sterni/nix/misc/default.nix18
l---------users/sterni/nix/misc/guinea-pig1
-rw-r--r--users/sterni/nix/num/default.nix17
-rw-r--r--users/sterni/nix/num/tests/default.nix26
-rw-r--r--users/sterni/nix/string/default.nix39
-rw-r--r--users/sterni/nix/string/tests/default.nix14
-rw-r--r--users/sterni/nix/url/default.nix41
-rw-r--r--users/sterni/nix/url/tests/default.nix18
-rw-r--r--users/sterni/nix/utf8/default.nix203
-rw-r--r--users/sterni/nix/utf8/tests/default.nix57
-rw-r--r--users/sterni/nixpkgs-crate-holes/default.nix228
-rw-r--r--users/sterni/nixpkgs-crate-holes/format-audit-result.jq61
-rw-r--r--users/sterni/secrets/default.nix3
-rw-r--r--users/sterni/secrets/minecraft-rcon.agebin0 -> 388 bytes
-rw-r--r--users/sterni/secrets/secrets.nix15
-rw-r--r--users/sterni/secrets/warteraum-salt.agebin0 -> 587 bytes
-rw-r--r--users/sterni/secrets/warteraum-tokens.age11
-rw-r--r--users/tazjin/OWNERS6
-rw-r--r--users/tazjin/aoc2019/default.nix22
-rw-r--r--users/tazjin/aoc2020/default.nix16
-rw-r--r--users/tazjin/aoc2022/day1.rs27
-rw-r--r--users/tazjin/aoc2023/day1.el52
-rw-r--r--users/tazjin/aoc2023/day2.el64
-rw-r--r--users/tazjin/aoc2023/day3.el110
-rw-r--r--users/tazjin/blog/default.nix25
-rw-r--r--users/tazjin/blog/posts.nix23
-rw-r--r--users/tazjin/blog/posts/best-tools.md84
-rw-r--r--users/tazjin/blog/posts/nixery-layers.md6
-rw-r--r--users/tazjin/blog/posts/reliably-switch-buffers.md18
-rw-r--r--users/tazjin/blog/posts/reversing-watchguard-vpn.md2
-rw-r--r--users/tazjin/blog/posts/thoughts.md142
-rw-r--r--users/tazjin/blog/posts/tvix-eval-talk-2023.md19
-rw-r--r--users/tazjin/chase-geese/default.nix13
-rw-r--r--users/tazjin/default.nix30
-rw-r--r--users/tazjin/dns/default.nix7
-rw-r--r--users/tazjin/docs/install-zfs.md116
-rw-r--r--users/tazjin/elisp-deps/deps.el83
-rw-r--r--users/tazjin/emacs/config/bindings.el65
-rw-r--r--users/tazjin/emacs/config/custom.el6
-rw-r--r--users/tazjin/emacs/config/desktop.el257
-rw-r--r--users/tazjin/emacs/config/functions.el195
-rw-r--r--users/tazjin/emacs/config/init.el154
-rw-r--r--users/tazjin/emacs/config/look-and-feel.el102
-rw-r--r--users/tazjin/emacs/config/mail-setup.el10
-rw-r--r--users/tazjin/emacs/config/modes.el37
-rw-r--r--users/tazjin/emacs/config/settings.el41
-rw-r--r--users/tazjin/emacs/default.nix318
-rw-r--r--users/tazjin/finito/default.nix4
-rw-r--r--users/tazjin/finito/finito-core/src/lib.rs41
-rw-r--r--users/tazjin/finito/finito-door/src/lib.rs34
-rw-r--r--users/tazjin/finito/finito-postgres/src/error.rs26
-rw-r--r--users/tazjin/finito/finito-postgres/src/lib.rs191
-rw-r--r--users/tazjin/finito/finito-postgres/src/tests.rs11
-rw-r--r--users/tazjin/generator-example/.gitignore1
-rw-r--r--users/tazjin/generator-example/Cargo.lock124
-rw-r--r--users/tazjin/generator-example/Cargo.toml9
-rw-r--r--users/tazjin/generator-example/README.md11
-rw-r--r--users/tazjin/generator-example/src/main.rs115
-rw-r--r--users/tazjin/gio-list-apps/.gitignore1
-rw-r--r--users/tazjin/gio-list-apps/Cargo.lock616
-rw-r--r--users/tazjin/gio-list-apps/Cargo.toml11
-rw-r--r--users/tazjin/gio-list-apps/default.nix14
-rw-r--r--users/tazjin/gio-list-apps/src/lib.rs31
-rw-r--r--users/tazjin/home/khamovnik.nix10
-rw-r--r--users/tazjin/home/persistence.nix42
-rw-r--r--users/tazjin/home/shared.nix91
-rw-r--r--users/tazjin/home/tverskoy.nix18
-rw-r--r--users/tazjin/home/zamalek.nix11
-rw-r--r--users/tazjin/homepage/default.nix62
-rw-r--r--users/tazjin/homepage/entries.nix96
-rw-r--r--users/tazjin/homepage/feed.nix7
-rw-r--r--users/tazjin/homepage/header.html21
-rw-r--r--users/tazjin/homepage/static/tazjin.css57
-rw-r--r--users/tazjin/keys.nix9
-rw-r--r--users/tazjin/keys/default.nix12
-rw-r--r--users/tazjin/kinesis/README.md10
-rwxr-xr-xusers/tazjin/kinesis/advantage2/qwerty.txt6
-rw-r--r--users/tazjin/nix.svg4
-rw-r--r--users/tazjin/nixos/.gitignore1
-rw-r--r--users/tazjin/nixos/camden/default.nix84
-rw-r--r--users/tazjin/nixos/default.nix8
-rw-r--r--users/tazjin/nixos/frog/default.nix35
-rw-r--r--users/tazjin/nixos/khamovnik/default.nix133
-rw-r--r--users/tazjin/nixos/koptevo/default.nix187
-rw-r--r--users/tazjin/nixos/modules/airsonic.nix32
-rw-r--r--users/tazjin/nixos/modules/chromium.nix30
-rw-r--r--users/tazjin/nixos/modules/default.nix2
-rw-r--r--users/tazjin/nixos/modules/desktop.nix55
-rw-r--r--users/tazjin/nixos/modules/fonts.nix24
-rw-r--r--users/tazjin/nixos/modules/geesefs.nix38
-rw-r--r--users/tazjin/nixos/modules/hidpi.nix19
-rw-r--r--users/tazjin/nixos/modules/home-config.nix19
-rw-r--r--users/tazjin/nixos/modules/laptop.nix15
-rw-r--r--users/tazjin/nixos/modules/miniflux.nix22
-rw-r--r--users/tazjin/nixos/modules/persistence.nix26
-rw-r--r--users/tazjin/nixos/modules/physical.nix105
-rw-r--r--users/tazjin/nixos/modules/predlozhnik.nix10
-rw-r--r--users/tazjin/nixos/modules/tgsa.nix29
-rw-r--r--users/tazjin/nixos/tverskoy/default.nix356
-rw-r--r--users/tazjin/nixos/zamalek/default.nix88
-rw-r--r--users/tazjin/presentations/bootstrapping-2018/default.nix38
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/README.md12
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/cppnix-example-lexer.cpp13
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/cppnix-example-smuggling.cpp12
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/default.nix63
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/presentation.pdfpc98
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/presentation.tex148
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/.gitignore2
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.lock899
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.toml7
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/index.html7
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/src/main.rs41
-rw-r--r--users/tazjin/rlox/src/bytecode/compiler.rs89
-rw-r--r--users/tazjin/rlox/src/bytecode/mod.rs5
-rw-r--r--users/tazjin/rlox/src/bytecode/vm.rs33
-rw-r--r--users/tazjin/rlox/src/main.rs17
-rw-r--r--users/tazjin/rlox/src/scanner.rs13
-rw-r--r--users/tazjin/rlox/src/treewalk/interpreter.rs106
-rw-r--r--users/tazjin/rlox/src/treewalk/parser.rs122
-rw-r--r--users/tazjin/rlox/src/treewalk/resolver.rs43
-rw-r--r--users/tazjin/rustfmt.toml22
-rw-r--r--users/tazjin/secrets/default.nix3
-rw-r--r--users/tazjin/secrets/geesefs-tazjins-files.age18
-rw-r--r--users/tazjin/secrets/miniflux.age14
-rw-r--r--users/tazjin/secrets/secrets.nix16
-rw-r--r--users/tazjin/secrets/tgsa-yandex.agebin0 -> 3082 bytes
-rw-r--r--users/tazjin/tgsa/.gitignore3
-rw-r--r--users/tazjin/tgsa/Cargo.lock1567
-rw-r--r--users/tazjin/tgsa/Cargo.toml18
-rw-r--r--users/tazjin/tgsa/default.nix17
-rw-r--r--users/tazjin/tgsa/src/main.rs403
-rw-r--r--users/tazjin/tgsa/src/translate.rs191
-rw-r--r--users/tazjin/tvix-eval-value.d298
-rw-r--r--users/tazjin/yddns/.gitignore1
-rw-r--r--users/tazjin/yddns/Cargo.lock1425
-rw-r--r--users/tazjin/yddns/Cargo.toml12
-rw-r--r--users/tazjin/yddns/default.nix9
-rw-r--r--users/tazjin/yddns/src/main.rs142
-rw-r--r--users/wpcarro/.envrc2
-rw-r--r--users/wpcarro/.gitignore3
-rw-r--r--users/wpcarro/Makefile17
-rw-r--r--users/wpcarro/OWNERS4
-rw-r--r--users/wpcarro/README.md17
-rw-r--r--users/wpcarro/assessments/brilliant/default.nix2
-rwxr-xr-xusers/wpcarro/bin/__dispatch.sh30
l---------users/wpcarro/bin/deploy-diogenes1
l---------users/wpcarro/bin/export-gpg1
l---------users/wpcarro/bin/import-gpg1
-rw-r--r--users/wpcarro/buildHaskell/default.nix40
-rw-r--r--users/wpcarro/ci/pipelines/post-receive.nix18
-rw-r--r--users/wpcarro/ci/pipelines/script.el44
-rw-r--r--users/wpcarro/clients/monsterpoker/default.nix6
-rw-r--r--users/wpcarro/clients/monsterpoker/index.html24
-rw-r--r--users/wpcarro/common.nix32
-rw-r--r--users/wpcarro/configs/.gitconfig3
-rwxr-xr-xusers/wpcarro/configs/.gnupg/export.sh29
-rwxr-xr-xusers/wpcarro/configs/.gnupg/import.sh28
-rw-r--r--users/wpcarro/configs/default.nix3
-rw-r--r--users/wpcarro/dotfiles/config.fish22
-rw-r--r--users/wpcarro/dotfiles/gitconfig9
-rw-r--r--users/wpcarro/dotfiles/prompt.fish (renamed from users/wpcarro/configs/.config/fish/prompt.fish)0
-rw-r--r--users/wpcarro/emacs/.emacs.d/init.el9
-rw-r--r--users/wpcarro/emacs/.emacs.d/vendor/dired+.el13696
-rw-r--r--users/wpcarro/emacs/.emacs.d/vendor/reason-indent.el304
-rw-r--r--users/wpcarro/emacs/.emacs.d/vendor/reason-interaction.el216
-rw-r--r--users/wpcarro/emacs/.emacs.d/vendor/reason-mode.el242
-rw-r--r--users/wpcarro/emacs/.emacs.d/vendor/refmt.el231
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/bag.el70
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/bookmark.el89
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/buffer.el5
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/bytes.el112
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/cache.el88
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/clipboard.el3
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/colorscheme.el85
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/constants.el5
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/device.el62
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/dotted.el57
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/fonts.el92
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/fs.el69
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/functions.el46
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/graph.el94
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/irc.el170
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/ivy-clipmenu.el137
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/keybindings.el144
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/keyboard.el29
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/laptop-battery.el63
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/list.el221
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/macros.el63
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/maybe.el78
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/number.el142
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/random.el80
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/scope.el106
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/sequence.el108
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/series.el92
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/set.el174
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/ssh.el9
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/stack.el101
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/struct.el85
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/symbol.el48
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/timestring.el77
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/tree.el199
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/vector.el84
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/window-manager.el54
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/window.el40
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-dired.el3
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-dotnet.el16
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-javascript.el5
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-language-support.el36
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-lisp.el8
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-misc.el26
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-rust.el27
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-ui.el53
-rw-r--r--users/wpcarro/emacs/AppIcon.icnsbin0 -> 449758 bytes
-rw-r--r--users/wpcarro/emacs/README.md2
-rw-r--r--users/wpcarro/emacs/ci.el44
-rw-r--r--users/wpcarro/emacs/default.nix306
-rw-r--r--users/wpcarro/emacs/pkgs/al/al.el (renamed from users/wpcarro/emacs/.emacs.d/wpc/al.el)45
-rw-r--r--users/wpcarro/emacs/pkgs/al/default.nix28
-rw-r--r--users/wpcarro/emacs/pkgs/al/tests.el53
-rw-r--r--users/wpcarro/emacs/pkgs/bag/bag.el78
-rw-r--r--users/wpcarro/emacs/pkgs/bag/default.nix26
-rw-r--r--users/wpcarro/emacs/pkgs/bag/tests.el32
-rw-r--r--users/wpcarro/emacs/pkgs/bookmark/bookmark.el50
-rw-r--r--users/wpcarro/emacs/pkgs/bookmark/default.nix13
-rw-r--r--users/wpcarro/emacs/pkgs/bytes/bytes.el94
-rw-r--r--users/wpcarro/emacs/pkgs/bytes/default.nix25
-rw-r--r--users/wpcarro/emacs/pkgs/bytes/tests.el18
-rw-r--r--users/wpcarro/emacs/pkgs/cycle/README.md7
-rw-r--r--users/wpcarro/emacs/pkgs/cycle/cycle.el (renamed from users/wpcarro/emacs/.emacs.d/wpc/cycle.el)126
-rw-r--r--users/wpcarro/emacs/pkgs/cycle/default.nix36
-rw-r--r--users/wpcarro/emacs/pkgs/cycle/tests.el79
-rw-r--r--users/wpcarro/emacs/pkgs/fs/default.nix29
-rw-r--r--users/wpcarro/emacs/pkgs/fs/fs.el47
-rw-r--r--users/wpcarro/emacs/pkgs/fs/tests.el26
-rw-r--r--users/wpcarro/emacs/pkgs/list/README.md19
-rw-r--r--users/wpcarro/emacs/pkgs/list/default.nix26
-rw-r--r--users/wpcarro/emacs/pkgs/list/list.el219
-rw-r--r--users/wpcarro/emacs/pkgs/list/tests.el107
-rw-r--r--users/wpcarro/emacs/pkgs/macros/default.nix10
-rw-r--r--users/wpcarro/emacs/pkgs/macros/macros.el45
-rw-r--r--users/wpcarro/emacs/pkgs/math/default.nix30
-rw-r--r--users/wpcarro/emacs/pkgs/math/math.el (renamed from users/wpcarro/emacs/.emacs.d/wpc/math.el)9
-rw-r--r--users/wpcarro/emacs/pkgs/math/tests.el25
-rw-r--r--users/wpcarro/emacs/pkgs/maybe/default.nix24
-rw-r--r--users/wpcarro/emacs/pkgs/maybe/maybe.el54
-rw-r--r--users/wpcarro/emacs/pkgs/maybe/tests.el25
-rw-r--r--users/wpcarro/emacs/pkgs/passage/README.md12
-rw-r--r--users/wpcarro/emacs/pkgs/passage/default.nix12
-rw-r--r--users/wpcarro/emacs/pkgs/passage/passage.el65
-rw-r--r--users/wpcarro/emacs/pkgs/set/default.nix32
-rw-r--r--users/wpcarro/emacs/pkgs/set/set.el116
-rw-r--r--users/wpcarro/emacs/pkgs/set/tests.el69
-rw-r--r--users/wpcarro/emacs/pkgs/string/default.nix27
-rw-r--r--users/wpcarro/emacs/pkgs/string/string.el (renamed from users/wpcarro/emacs/.emacs.d/wpc/string.el)26
-rw-r--r--users/wpcarro/emacs/pkgs/string/tests.el22
-rw-r--r--users/wpcarro/emacs/pkgs/struct/README.md6
-rw-r--r--users/wpcarro/emacs/pkgs/struct/default.nix29
-rw-r--r--users/wpcarro/emacs/pkgs/struct/struct.el65
-rw-r--r--users/wpcarro/emacs/pkgs/struct/tests.el44
-rw-r--r--users/wpcarro/emacs/pkgs/symbol/default.nix24
-rw-r--r--users/wpcarro/emacs/pkgs/symbol/symbol.el38
-rw-r--r--users/wpcarro/emacs/pkgs/symbol/tests.el22
-rw-r--r--users/wpcarro/emacs/pkgs/theme/default.nix14
-rw-r--r--users/wpcarro/emacs/pkgs/theme/theme.el78
-rw-r--r--users/wpcarro/emacs/pkgs/tuple/default.nix10
-rw-r--r--users/wpcarro/emacs/pkgs/tuple/tuple.el (renamed from users/wpcarro/emacs/.emacs.d/wpc/tuple.el)0
-rw-r--r--users/wpcarro/emacs/pkgs/vector/default.nix21
-rw-r--r--users/wpcarro/emacs/pkgs/vector/tests.el20
-rw-r--r--users/wpcarro/emacs/pkgs/vector/vector.el58
-rw-r--r--users/wpcarro/emacs/pkgs/vterm-mgt/README.md17
-rw-r--r--users/wpcarro/emacs/pkgs/vterm-mgt/default.nix19
-rw-r--r--users/wpcarro/emacs/pkgs/vterm-mgt/vterm-mgt.el (renamed from users/wpcarro/emacs/.emacs.d/wpc/vterm-mgt.el)36
-rw-r--r--users/wpcarro/emacs/pkgs/zle/default.nix10
-rw-r--r--users/wpcarro/emacs/pkgs/zle/zle.el (renamed from users/wpcarro/emacs/.emacs.d/wpc/zle.el)11
-rw-r--r--users/wpcarro/emacs/workspace.josh0
-rw-r--r--users/wpcarro/haskell-file/shell.nix2
-rw-r--r--users/wpcarro/keys.nix16
-rw-r--r--users/wpcarro/lib/default.nix5
-rw-r--r--users/wpcarro/nixos/ava/ava.el61
-rw-r--r--users/wpcarro/nixos/ava/default.nix150
-rw-r--r--users/wpcarro/nixos/default.nix36
-rw-r--r--users/wpcarro/nixos/diogenes/README.md13
-rw-r--r--users/wpcarro/nixos/diogenes/default.nix178
-rw-r--r--users/wpcarro/nixos/kyoko/default.nix153
-rw-r--r--users/wpcarro/nixos/kyoko/kyoko.el61
-rw-r--r--users/wpcarro/nixos/marcus/default.nix41
-rw-r--r--users/wpcarro/nixos/marcus/hardware.nix14
-rw-r--r--users/wpcarro/nixos/marcus/marcus.el40
-rw-r--r--users/wpcarro/nixos/modules/.skip-subtree1
-rw-r--r--users/wpcarro/nixos/modules/hadrian-cache.nix17
-rw-r--r--users/wpcarro/nixos/modules/hardware/dell-emc-egw-5200.nix47
-rw-r--r--users/wpcarro/nixos/modules/hardware/nopn.nix53
-rw-r--r--users/wpcarro/nixos/modules/laptop.nix15
-rw-r--r--users/wpcarro/nixos/modules/nginx.nix45
-rw-r--r--users/wpcarro/nixos/tarasco/default.nix144
-rw-r--r--users/wpcarro/nixos/tarasco/tarasco.el61
-rw-r--r--users/wpcarro/playbooks/first-of-the-month.org13
-rw-r--r--users/wpcarro/playbooks/habits.org7
-rw-r--r--users/wpcarro/playbooks/nix_gcr/cloud_run.nix2
-rw-r--r--users/wpcarro/scratch/blockchain/default.nix3
-rw-r--r--users/wpcarro/scratch/compiler/.envrc3
-rw-r--r--users/wpcarro/scratch/compiler/.gitignore5
-rw-r--r--users/wpcarro/scratch/compiler/debug.ml66
-rw-r--r--users/wpcarro/scratch/compiler/expr_parser.ml187
-rw-r--r--users/wpcarro/scratch/compiler/inference.ml183
-rw-r--r--users/wpcarro/scratch/compiler/parser.ml47
-rw-r--r--users/wpcarro/scratch/compiler/prettify.ml9
-rw-r--r--users/wpcarro/scratch/compiler/register_vm.ml129
-rw-r--r--users/wpcarro/scratch/compiler/register_vm.py161
-rw-r--r--users/wpcarro/scratch/compiler/shell.nix9
-rw-r--r--users/wpcarro/scratch/compiler/tests.ml43
-rw-r--r--users/wpcarro/scratch/compiler/type_parser.ml104
-rw-r--r--users/wpcarro/scratch/compiler/types.ml31
-rw-r--r--users/wpcarro/scratch/compiler/vec.ml127
-rw-r--r--users/wpcarro/scratch/groceries/shell.nix2
-rw-r--r--users/wpcarro/scratch/picoctf/challenge_166/shell.nix5
-rw-r--r--users/wpcarro/scratch/rust/.gitignore1
-rw-r--r--users/wpcarro/scratch/rust/Cargo.lock89
-rw-r--r--users/wpcarro/scratch/rust/Cargo.toml8
-rw-r--r--users/wpcarro/scratch/rust/README.md11
-rw-r--r--users/wpcarro/scratch/rust/shell.nix7
-rw-r--r--users/wpcarro/scratch/rust/src/display/mod.rs13
-rw-r--r--users/wpcarro/scratch/rust/src/json/mod.rs81
-rw-r--r--users/wpcarro/scratch/rust/src/main.rs15
-rw-r--r--users/wpcarro/scratch/rust/src/rc/mod.rs12
-rw-r--r--users/wpcarro/scratch/rust/src/stdin/mod.rs22
-rw-r--r--users/wpcarro/scratch/simple-select/README.md71
-rw-r--r--users/wpcarro/scratch/simple-select/main.py262
-rw-r--r--users/wpcarro/scratch/simple-select/parser.py31
-rw-r--r--users/wpcarro/scratch/simple-select/scanner.py27
-rw-r--r--users/wpcarro/slx.js/.gitignore3
-rw-r--r--users/wpcarro/slx.js/README.md55
-rw-r--r--users/wpcarro/slx.js/default.nix11
-rw-r--r--users/wpcarro/slx.js/index.html13
-rw-r--r--users/wpcarro/slx.js/index.js455
-rw-r--r--users/wpcarro/slx.js/package.json14
-rw-r--r--users/wpcarro/slx.js/tests.js68
-rw-r--r--users/wpcarro/slx.js/yarn.lock1495
-rw-r--r--users/wpcarro/terraform/default.nix341
-rw-r--r--users/wpcarro/todo-lists/cta-curriculum.csv108
-rw-r--r--users/wpcarro/tools/monzo_ynab/.envrc2
-rw-r--r--users/wpcarro/tools/monzo_ynab/.skip-subtree2
-rw-r--r--users/wpcarro/tools/monzo_ynab/job.nix14
-rw-r--r--users/wpcarro/tools/monzo_ynab/main.go10
-rw-r--r--users/wpcarro/tools/monzo_ynab/monzo/client.go4
-rw-r--r--users/wpcarro/tools/monzo_ynab/monzo/serde.go12
-rw-r--r--users/wpcarro/tools/monzo_ynab/shell.nix9
-rw-r--r--users/wpcarro/tools/monzo_ynab/tokens.go36
-rw-r--r--users/wpcarro/tools/monzo_ynab/tokens.nix25
-rw-r--r--users/wpcarro/tools/monzo_ynab/ynab/client.go25
-rw-r--r--users/wpcarro/tools/monzo_ynab/ynab/serde.go10
-rw-r--r--users/wpcarro/tools/rfcToKindle/default.nix2
-rw-r--r--users/wpcarro/tools/symlinkManager/default.nix3
-rw-r--r--users/wpcarro/tools/systemd-shell/default.nix8
-rw-r--r--users/wpcarro/tools/systemd-shell/setup.py9
-rw-r--r--users/wpcarro/tools/systemd-shell/systemd-shell36
-rw-r--r--users/wpcarro/tools/url-blocker/default.nix13
-rw-r--r--users/wpcarro/tools/wpcarro-deps.nix8
-rw-r--r--users/wpcarro/utils/builder.nix3
-rw-r--r--users/wpcarro/utils/default.nix5
-rw-r--r--users/wpcarro/utils/fs.nix9
-rw-r--r--users/wpcarro/website/blog/default.nix8
-rw-r--r--users/wpcarro/website/blog/fragments/post.html4
-rw-r--r--users/wpcarro/website/blog/posts.nix91
-rw-r--r--users/wpcarro/website/blog/posts/auto-reboot-nixos.md40
-rw-r--r--users/wpcarro/website/blog/posts/cell-phone-experiment.md6
-rw-r--r--users/wpcarro/website/blog/posts/csharp-unused-variables.md38
-rw-r--r--users/wpcarro/website/blog/posts/git-filter-repo-note.md59
-rw-r--r--users/wpcarro/website/blog/posts/git-rev-refs.md85
-rw-r--r--users/wpcarro/website/blog/posts/importing-subtrees.md147
-rw-r--r--users/wpcarro/website/blog/posts/nginx-curl-note.md5
-rw-r--r--users/wpcarro/website/blog/posts/nix-env-note.md33
-rw-r--r--users/wpcarro/website/blog/posts/nix-shell-note.md50
-rw-r--r--users/wpcarro/website/blog/posts/nixos-disk-full-note.md113
-rw-r--r--users/wpcarro/website/blog/posts/restic.md91
-rw-r--r--users/wpcarro/website/blog/posts/ssh-oddities.md59
-rw-r--r--users/wpcarro/website/blog/posts/tcp-tunneling-note.md63
-rw-r--r--users/wpcarro/website/blog/posts/tee-time.md16
-rw-r--r--users/wpcarro/website/default.nix49
-rw-r--r--users/wpcarro/website/fragments/homepage.html4
-rw-r--r--users/wpcarro/website/fragments/template.html9
-rw-r--r--users/wpcarro/website/habit-screens/default.nix41
-rw-r--r--users/wpcarro/website/habit-screens/elm-srcs.nix120
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/default.nix41
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/elm-srcs.nix104
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/App.hs2
-rw-r--r--users/wpcarro/ynabsql/dataviz/.gitignore5
-rw-r--r--users/wpcarro/ynabsql/dataviz/.parcelrc3
l---------users/wpcarro/ynabsql/dataviz/cdn1
-rw-r--r--users/wpcarro/ynabsql/dataviz/components.jsx1256
-rw-r--r--users/wpcarro/ynabsql/dataviz/index.html19
-rw-r--r--users/wpcarro/ynabsql/dataviz/package.json15
-rw-r--r--users/wpcarro/ynabsql/dataviz/yarn.lock1540
-rw-r--r--users/wpcarro/zoo/Main.hs2
-rw-r--r--users/zseri/OWNERS3
-rw-r--r--users/zseri/store-ref-scanner/Cargo.toml6
-rw-r--r--users/zseri/store-ref-scanner/tests.nix32
-rw-r--r--views/README.md27
-rw-r--r--views/default.nix26
-rw-r--r--views/kit/README.md4
-rw-r--r--views/kit/buildkite.yml8
-rw-r--r--views/kit/default.nix37
-rw-r--r--views/kit/workspace.josh10
-rw-r--r--views/tvix/workspace.josh9
-rw-r--r--web/atom-feed/default.nix7
-rw-r--r--web/atward/Cargo.lock588
-rw-r--r--web/atward/build.rs9
-rw-r--r--web/atward/src/main.rs194
-rw-r--r--web/atward/src/tests.rs187
-rw-r--r--web/blog/default.nix12
-rw-r--r--web/blog/fragments.nix56
-rw-r--r--web/bubblegum/OWNERS4
-rw-r--r--web/bubblegum/default.nix105
-rw-r--r--web/bubblegum/examples/blog.nix43
-rw-r--r--web/bubblegum/examples/default.nix62
-rw-r--r--web/bubblegum/examples/derivation-svg.nix11
-rw-r--r--web/bubblegum/examples/hello.nix8
-rw-r--r--web/cgit-taz/default.nix74
-rw-r--r--web/cgit-tvl/default.nix73
-rw-r--r--web/cgit-tvl/thttpd_cgi_idx.patch (renamed from web/cgit-taz/thttpd_cgi_idx.patch)0
-rw-r--r--web/converse/Cargo.lock1323
-rw-r--r--web/converse/default.nix2
-rw-r--r--web/converse/src/db.rs50
-rw-r--r--web/converse/src/errors.rs17
-rw-r--r--web/converse/src/handlers.rs178
-rw-r--r--web/converse/src/main.rs73
-rw-r--r--web/converse/src/models.rs10
-rw-r--r--web/converse/src/oidc.rs45
-rw-r--r--web/converse/src/render.rs43
-rw-r--r--web/converse/src/schema.rs7
-rw-r--r--web/inbox.nix81
-rw-r--r--web/panettone/OWNERS8
-rw-r--r--web/panettone/default.nix14
-rw-r--r--web/panettone/docker-compose.yml4
-rw-r--r--web/panettone/shell.nix4
-rw-r--r--web/panettone/src/authentication.lisp210
-rw-r--r--web/panettone/src/css.lisp32
-rw-r--r--web/panettone/src/email.lisp5
-rw-r--r--web/panettone/src/inline-markdown.lisp3
-rw-r--r--web/panettone/src/migrations/1-init-schema.lisp23
-rw-r--r--web/panettone/src/migrations/3920286378-add-issue-tsv.lisp5
-rw-r--r--web/panettone/src/migrations/3921488651-create-users-table.lisp6
-rw-r--r--web/panettone/src/model.lisp306
-rw-r--r--web/panettone/src/packages.lisp31
-rw-r--r--web/panettone/src/panettone.lisp206
-rw-r--r--web/panettone/src/static/search.pngbin0 -> 711 bytes
-rw-r--r--web/panettone/src/util.lisp32
-rw-r--r--web/panettone/test/util_test.lisp9
-rw-r--r--web/pwcrypt/.gitignore2
-rw-r--r--web/pwcrypt/Cargo.lock999
-rw-r--r--web/pwcrypt/Cargo.toml13
-rw-r--r--web/pwcrypt/default.nix51
-rw-r--r--web/pwcrypt/index.html26
-rw-r--r--web/pwcrypt/src/main.html48
-rw-r--r--web/pwcrypt/src/main.rs160
-rw-r--r--web/static/default.nix4
-rw-r--r--web/static/terminal.min.css1
-rw-r--r--web/static/tvl.css67
-rw-r--r--web/todolist/default.nix31
-rw-r--r--web/tvixbolt/.gitignore2
-rw-r--r--web/tvixbolt/Cargo.lock1199
-rw-r--r--web/tvixbolt/Cargo.toml26
-rw-r--r--web/tvixbolt/LICENSE661
-rw-r--r--web/tvixbolt/default.nix74
-rw-r--r--web/tvixbolt/index.css7
-rw-r--r--web/tvixbolt/index.html11
-rw-r--r--web/tvixbolt/src/main.rs315
-rw-r--r--web/tvl/blog/2024-02-tvix-update.md333
-rw-r--r--web/tvl/blog/default.nix21
-rw-r--r--web/tvl/blog/tvix-status-202209.md165
-rw-r--r--web/tvl/default.nix38
-rw-r--r--web/tvl/footer/default.nix2
-rw-r--r--web/tvl/logo/default.nix42
-rw-r--r--web/tvl/template/default.nix18
-rw-r--r--web/tvl/tvl.dot47
-rw-r--r--web/volgasprint/README.md3
-rw-r--r--web/volgasprint/default.nix23
-rw-r--r--web/volgasprint/docs/assets/baumana.webpbin0 -> 371346 bytes
-rw-r--r--web/volgasprint/docs/assets/kazan_overview.webpbin0 -> 193440 bytes
-rw-r--r--web/volgasprint/docs/assets/kazan_tree.webpbin0 -> 630720 bytes
-rw-r--r--web/volgasprint/docs/assets/logos/nixos.svg459
-rw-r--r--web/volgasprint/docs/assets/logos/volgasprint_nix.svg28
-rw-r--r--web/volgasprint/docs/assets/logos/volgasprint_ru.svg28
-rw-r--r--web/volgasprint/docs/index.md121
-rw-r--r--web/volgasprint/mkdocs.yml41
-rw-r--r--web/volgasprint/requirements.txt4
5407 files changed, 281617 insertions, 393655 deletions
diff --git a/.envrc b/.envrc
index 162f060fc4..1f15539fb2 100644
--- a/.envrc
+++ b/.envrc
@@ -1,5 +1,18 @@
+# Create a gcroot that keeps all third_party.sources alive
+nix-build --out-link .gcroots/sources -E '
+with import ./. {};
+third_party.nixpkgs.writeText "depot-3p-sources.txt" (
+  toString (
+    builtins.map (s: s.outPath or null) (
+      builtins.attrValues third_party.sources
+    )
+  )
+)'
+
 # Configure the local PATH to contain tools which are fetched ad-hoc
 # from Nix.
+out=$(nix-build -A tools.depot-deps --out-link .gcroots/depot-deps)
+PATH_add "$out/bin"
 
-export PATH="${PWD}/bin:${PATH}"
-export REPO_ROOT="${PWD}"
+watch_file tools/depot-deps.nix
+watch_file third_party/sources/sources.json
diff --git a/.gcroots/.skip-subtree b/.gcroots/.skip-subtree
new file mode 100644
index 0000000000..8c3e8d06db
--- /dev/null
+++ b/.gcroots/.skip-subtree
@@ -0,0 +1 @@
+these are just symlinks to prevent Nix from gc-ing paths we'd like to keep
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 572b385fef..ca28d690e8 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -21,3 +21,6 @@ c758de9d22506eb279c5abe61f621e5c8f61af95
 
 # style(3p/nix): Final act in the brace-wrapping saga
 39087321811e81e26a1a47d6967df1088dcf0e95
+
+# style: format entire depot with nixpkgs-fmt
+aa122cbae78ce97d60c0c98ba14df753d97e40b1
diff --git a/.gitignore b/.gitignore
index 0b135e7034..8cdaa738f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,12 @@
 # trash locally that might be valuable in the future.
 garbage/
 
+# Nix gcroot symlinks created by .envrc
+/.gcroots/*
+
 # Ignore Nix result symlinks
 result
 result-*
+# Nix result symlink used by //nix/buildkite
+/nix-buildkite-extra-step-command-script
+/nix-buildkite-extra-step-command-script-*
diff --git a/.rustfmt.toml b/.rustfmt.toml
deleted file mode 100644
index 6ad0d5341a..0000000000
--- a/.rustfmt.toml
+++ /dev/null
@@ -1,22 +0,0 @@
-# This configuration file changes some defaults from the ones
-# documented on https://rust-lang.github.io/rustfmt/
-#
-# All other settings are left at the defaults.
-
-edition = "2018"
-newline_style = "Unix"
-use_try_shorthand = true
-
-# Unstable settings that we want in the future, once they are
-# available:
-#
-# combine_control_expr = false
-# comment_width = 100
-# condense_wildcard_suffixes = true
-# format_code_in_doc_comments = true
-# inline_attribute_width = 100
-# match_block_trailing_comma = true
-# merge_imports = true
-# normalize_comments = true
-# overflow_delimited_expr = true
-# wrap_comments = true
diff --git a/LICENSE b/LICENSE
index bdc72a2e03..af4c6ae3cf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
 The MIT License (MIT)
 
 Copyright (c) 2019 Vincent Ambo
-Copyright (c) 2020-2021 The TVL Authors
+Copyright (c) 2020-2023 The TVL Authors
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/OWNERS b/OWNERS
index cc2aa26cf2..26b409f49b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,7 @@
-inherited: false
-owners:
-  - tazjin
-  - lukegb
-  - grfn
-  - sterni
+set noparent
+
+tazjin
+lukegb
+aspen
+sterni
+flokli
diff --git a/README.md b/README.md
index 1bb29f29e0..f58f937cef 100644
--- a/README.md
+++ b/README.md
@@ -17,8 +17,8 @@ partially see this as [an experiment][] in tooling for monorepos.
 
 * Source code is available primarily through Sourcegraph on
   [cs.tvl.fyi](https://cs.tvl.fyi), where it is searchable and even semantically
-  indexed. A lower-tech view of the repository is also available via cgit on
-  [code.tvl.fyi](https://code.tvl.fyi).
+  indexed. A lower-tech view of the repository is also available via cgit-pink
+  on [code.tvl.fyi](https://code.tvl.fyi).
 
   The repository can be cloned using `git` from `https://cl.tvl.fyi/depot`.
 
@@ -49,6 +49,9 @@ configuration is tracked in `//ops/{modules,machines}`.
 * [`//nix/readTree`](https://cs.tvl.fyi/depot/-/blob/nix/readTree/README.md)
   contains the Nix code which automatically registers projects in our Nix
   attribute hierarchy based on their in-tree location
+* [`//tools/nixery`](https://cs.tvl.fyi/depot/-/tree/tools/nixery)
+  contains the source code of [Nixery][], a container registry that
+  can build images ad-hoc from Nix packages
 * `//nix/yants` contains **Y**et **A**nother **N**ix **T**ype **S**ystem, which
   we use for a variety of things throughout the repository
 * `//nix/buildGo` implements a Nix library that can build Go software in the
@@ -57,10 +60,12 @@ configuration is tracked in `//ops/{modules,machines}`.
 * `//nix/buildLisp` implements a Nix library that can build Common Lisp
   software. Currently only SBCL is supported. Lisp programs in this repository
   are built using this library.
+* `//web/blog` and `//web/atom-feed`: A Nix-based static site generator which
+  generates the web page and Atom feed for [tazj.in](https://tazj.in)
+  (`//users/tazjin/homepage`) and [tvl.fyi](https://tvl.fyi) (`//web/tvl`)
 * `//web/bubblegum` contains a CGI-based web framework written in Nix.
 * `//nix/nint`: A shebang-compatible interpreter wrapper for Nix.
 * `//tvix` contains initial work towards a modular architecture for Nix.
-* `//third_party/nix` contains [our fork][tvix] of the Nix package manager.
 
 We have a variety of other tools and libraries in the `//nix` folder which may
 be of interest.
@@ -84,10 +89,7 @@ personal or experimental code that does not require review.
 
 Some examples:
 
-* `//users/tazjin/homepage` && `//users/tazjin/blog`: A Nix-based static site
-  generator which generates the web page and Atom feed for
-  [tazj.in](https://tazj.in)
-* `//users/grfn/xanthous`: A (WIP) TUI RPG, written in Haskell.
+* `//users/aspen/xanthous`: A (WIP) TUI RPG, written in Haskell.
 * `//users/tazjin/emacs`: tazjin's Emacs & EXWM configuration
 * `//users/tazjin/finito`: A persistent finite-state machine library for Rust.
 
@@ -119,3 +121,4 @@ Hackint also provide a [web chat][tvl-webchat].
 [hackint-xmpp]: https://hackint.org/transport/xmpp
 [tvl-xmpp]: xmpp:#tvl@irc.hackint.org?join
 [tvl-webchat]: https://webirc.hackint.org/#ircs://irc.hackint.org/#tvl
+[Nixery]: https://nixery.dev
diff --git a/bin/__dispatch.sh b/bin/__dispatch.sh
deleted file mode 100755
index a6a945ad19..0000000000
--- a/bin/__dispatch.sh
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/env bash
-# This script dispatches invocations transparently to programs instantiated from
-# Nix.
-#
-# To add a new tool, insert it into the case statement below by setting `attr`
-# to the key in nixpkgs which represents the program you want to run.
-set -ueo pipefail
-
-readonly REPO_ROOT=$(dirname "$0")/..
-TARGET_TOOL=$(basename "$0")
-
-case "${TARGET_TOOL}" in
-  age)
-    attr="third_party.nixpkgs.age"
-    ;;
-  age-keygen)
-    attr="third_party.nixpkgs.age"
-    ;;
-  depot-build)
-    attr="tools.depot-build"
-    ;;
-  depot-nixpkgs-update)
-    attr="tools.depot-nixpkgs-update"
-    ;;
-  gerrit)
-    attr="tools.gerrit-cli"
-    ;;
-  gerrit-update)
-    attr="tools.gerrit-update"
-    ;;
-  hash-password)
-    attr="tools.hash-password"
-    ;;
-  kontemplate)
-    attr="ops.kontemplate"
-    ;;
-  meson)
-    attr="third_party.nixpkgs.meson"
-    ;;
-  mg)
-    attr="tools.magrathea"
-    ;;
-  ninja)
-    attr="third_party.nixpkgs.ninja"
-    ;;
-  nint)
-    attr="nix.nint"
-    ;;
-  perf-flamegraph)
-    attr="tools.perf-flamegraph"
-    ;;
-  rebuild-system)
-    attr="ops.nixos.rebuild-system"
-    ;;
-  rink)
-    attr="third_party.nixpkgs.rink"
-    ;;
-  stern)
-    attr="third_party.nixpkgs.stern"
-    ;;
-  depotfmt)
-    attr="tools.depotfmt"
-    ;;
-  tf-glesys)
-    TARGET_TOOL="terraform"
-    attr="ops.glesys.terraform"
-    ;;
-  tf-keycloak)
-    TARGET_TOOL="terraform"
-    attr="ops.keycloak.terraform"
-    ;;
-  *)
-    echo "The tool '${TARGET_TOOL}' is currently not installed in this repository."
-    exit 1
-    ;;
-esac
-
-result=$(nix-build --no-out-link --attr "${attr}" "${REPO_ROOT}")
-PATH="${result}/bin:$PATH"
-
-exec "${TARGET_TOOL}" "${@}"
diff --git a/bin/age b/bin/age
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/age
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/age-keygen b/bin/age-keygen
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/age-keygen
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/depot-build b/bin/depot-build
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/depot-build
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/depot-nixpkgs-update b/bin/depot-nixpkgs-update
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/depot-nixpkgs-update
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/depotfmt b/bin/depotfmt
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/depotfmt
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/gerrit b/bin/gerrit
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/gerrit
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/gerrit-update b/bin/gerrit-update
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/gerrit-update
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/hash-password b/bin/hash-password
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/hash-password
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/kontemplate b/bin/kontemplate
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/kontemplate
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/meson b/bin/meson
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/meson
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/mg b/bin/mg
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/mg
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/ninja b/bin/ninja
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/ninja
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/nint b/bin/nint
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/nint
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/perf-flamegraph b/bin/perf-flamegraph
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/perf-flamegraph
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/rebuild-system b/bin/rebuild-system
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/rebuild-system
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/rink b/bin/rink
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/rink
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/stern b/bin/stern
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/stern
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/tf-glesys b/bin/tf-glesys
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/tf-glesys
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/bin/tf-keycloak b/bin/tf-keycloak
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/bin/tf-keycloak
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/buf.gen.yaml b/buf.gen.yaml
new file mode 100644
index 0000000000..4ed18be22e
--- /dev/null
+++ b/buf.gen.yaml
@@ -0,0 +1,8 @@
+version: v1
+plugins:
+ - name: go
+   out: .
+   opt: paths=source_relative
+ - name: go-grpc
+   out: .
+   opt: paths=source_relative
\ No newline at end of file
diff --git a/buf.yaml b/buf.yaml
index 42c769f2e4..ab2028f40e 100644
--- a/buf.yaml
+++ b/buf.yaml
@@ -1,13 +1,12 @@
-build:
-  roots:
-    #- proto
-    - third_party
+version: v1
+
 lint:
-  ignore:
-    - nix/buildGo
+  allow_comment_ignores: true
   use:
     - BASIC
     - FILE_LOWER_SNAKE_CASE
   except:
     - ENUM_VALUE_UPPER_SNAKE_CASE
     - PACKAGE_DIRECTORY_MATCH
+  ignore:
+    - nix/buildGo
diff --git a/corp/LICENSE b/corp/LICENSE
index f29fc84038..6ae339dea3 100644
--- a/corp/LICENSE
+++ b/corp/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2021 ООО Π’Π’Π›
+Copyright 2021-2023 ООО Π’Π’Π›
 
 Code under this folder may be redistributed as part of the TVL depot
 repository. All other usage rights for this code are reserved.
diff --git a/corp/OWNERS b/corp/OWNERS
index 4bc08d35f6..e99d7151f3 100644
--- a/corp/OWNERS
+++ b/corp/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - tvl-employees
+set noparent
+
+group:tvl-employees
diff --git a/corp/ops/.envrc b/corp/ops/.envrc
new file mode 100644
index 0000000000..26049cf426
--- /dev/null
+++ b/corp/ops/.envrc
@@ -0,0 +1,4 @@
+out=$(nix-build ../.. -A corp.ops.deps --out-link ../../.gcroots/corp-deps)
+PATH_add "$out/bin"
+
+watch_file default.nix
diff --git a/corp/ops/.gitignore b/corp/ops/.gitignore
new file mode 100644
index 0000000000..5def054d76
--- /dev/null
+++ b/corp/ops/.gitignore
@@ -0,0 +1,4 @@
+.terraform
+.terraform.lock.hcl
+terraform.tfstate
+terraform.tfstate.backup
diff --git a/corp/ops/default.nix b/corp/ops/default.nix
new file mode 100644
index 0000000000..c88b3bdc1c
--- /dev/null
+++ b/corp/ops/default.nix
@@ -0,0 +1,37 @@
+{ depot, lib, pkgs, ... }:
+
+depot.nix.readTree.drvTargets rec {
+  # Provide a Terraform wrapper with Yandex Cloud support.
+  terraform = pkgs.terraform.withPlugins (p: [
+    p.yandex
+  ]);
+
+  validate = depot.tools.checks.validateTerraform {
+    inherit terraform;
+    name = "corp";
+    src = lib.cleanSource ./.;
+  };
+
+  # Yandex Cloud CLI
+  yc-cli = pkgs.stdenv.mkDerivation rec {
+    pname = "yc-cli";
+    version = "0.106.0";
+
+    src = pkgs.fetchurl {
+      url = "https://storage.yandexcloud.net/yandexcloud-yc/release/${version}/linux/amd64/yc";
+      sha256 = "sha256:1f7fq9rlihz91ld1vdjj9vq9ssq1ls031jin4zisxv75rcdpslh3";
+    };
+
+    phases = [ "installPhase" ];
+    installPhase = "install -D $src $out/bin/yc";
+  };
+
+  deps = depot.tools.depot-deps.overrideDeps {
+    tf-yandex = {
+      attr = "corp.ops.terraform";
+      cmd = "terraform";
+    };
+
+    yc.attr = "corp.ops.yc-cli";
+  };
+}
diff --git a/corp/ops/modules/.skip-tree b/corp/ops/modules/.skip-tree
new file mode 100644
index 0000000000..a6f528167f
--- /dev/null
+++ b/corp/ops/modules/.skip-tree
@@ -0,0 +1 @@
+Only NixOS modules here.
diff --git a/corp/ops/yandex/creds.fish b/corp/ops/yandex/creds.fish
new file mode 100644
index 0000000000..2985b28808
--- /dev/null
+++ b/corp/ops/yandex/creds.fish
@@ -0,0 +1,5 @@
+export YC_TOKEN=(yc iam create-token)
+export YC_CLOUD_ID=(yc config get cloud-id)
+export YC_FOLDER_ID=(yc config get folder-id)
+export AWS_ACCESS_KEY_ID="YCAJE6eRLY8Az-9kveNRtz4sh"
+export AWS_SECRET_ACCESS_KEY=(yc kms symmetric-crypto decrypt --name tvl-credentials --cloud-id b1ggu5m1btue982app12 --folder-name default --ciphertext-file encrypted-state-secret.key --plaintext-file /dev/stdout | head -n1)
diff --git a/corp/ops/yandex/encrypted-state-secret.key b/corp/ops/yandex/encrypted-state-secret.key
new file mode 100644
index 0000000000..0d07158f2f
--- /dev/null
+++ b/corp/ops/yandex/encrypted-state-secret.key
Binary files differdiff --git a/corp/ops/yandex/main.tf b/corp/ops/yandex/main.tf
new file mode 100644
index 0000000000..cd8fa6e4cc
--- /dev/null
+++ b/corp/ops/yandex/main.tf
@@ -0,0 +1,70 @@
+# Terraform configuration for TVL corp infrastructure (on Yandex
+# Cloud).
+
+terraform {
+  required_providers {
+    yandex = {
+      source = "yandex-cloud/yandex"
+    }
+  }
+
+  # Credentials need to be sourced from creds.fish
+  backend "s3" {
+    endpoint = "storage.yandexcloud.net"
+    bucket   = "su-tvl-terraform-state"
+    region   = "ru-central1"
+    key      = "corp/ops/terraform.tfstate"
+
+    skip_region_validation      = true
+    skip_credentials_validation = true
+  }
+}
+
+provider "yandex" {
+  zone = "ru-central1-b"
+}
+
+locals {
+  tvl_cloud_id  = "b1ggu5m1btue982app12"
+  tvl_folder_id = "b1gmbeqt9o5kbl7rclln"
+  rih_cloud_id  = "b1glccvcqggi2ruibgvt"
+  rih_folder_id = "b1gsavcrsjn059d1sbh9"
+}
+
+# Storage state bucket configuration
+
+resource "yandex_iam_service_account" "tf_state_sa" {
+  folder_id = local.tvl_folder_id
+  name      = "terraform-state"
+}
+
+resource "yandex_resourcemanager_folder_iam_member" "tf_state_sa_storage" {
+  folder_id = local.tvl_folder_id
+  role      = "storage.editor"
+  member    = "serviceAccount:${yandex_iam_service_account.tf_state_sa.id}"
+}
+
+resource "yandex_iam_service_account_static_access_key" "tf_state_sa_key" {
+  service_account_id = yandex_iam_service_account.tf_state_sa.id
+  description        = "Static access key for Terraform state"
+}
+
+resource "yandex_storage_bucket" "tf_state" {
+  access_key = yandex_iam_service_account_static_access_key.tf_state_sa_key.access_key
+  secret_key = yandex_iam_service_account_static_access_key.tf_state_sa_key.secret_key
+  bucket     = "su-tvl-terraform-state"
+}
+
+# Secret management configuration
+
+resource "yandex_kms_symmetric_key" "tvl_credentials_key" {
+  name              = "tvl-credentials"
+  folder_id         = local.tvl_folder_id
+  default_algorithm = "AES_256"
+  rotation_period   = "2160h" # 90 days
+}
+
+resource "yandex_kms_secret_ciphertext" "tf_state_key" {
+  key_id    = yandex_kms_symmetric_key.tvl_credentials_key.id
+  plaintext = yandex_iam_service_account_static_access_key.tf_state_sa_key.secret_key
+}
diff --git a/corp/ops/yandex/rih.tf b/corp/ops/yandex/rih.tf
new file mode 100644
index 0000000000..104a61f864
--- /dev/null
+++ b/corp/ops/yandex/rih.tf
@@ -0,0 +1,307 @@
+# Deployment configuration for russiaishiring.com
+#
+# The frontend of the page is served from a storage bucket, the
+# backend runs in a container.
+
+resource "yandex_dns_zone" "russiaishiring_com" {
+  name      = "russiaishiring-com"
+  zone      = "russiaishiring.com."
+  public    = true
+  folder_id = local.rih_folder_id
+}
+
+resource "yandex_iam_service_account" "rih_storage_sa" {
+  name      = "rih-storage-sa"
+  folder_id = local.rih_folder_id
+}
+
+resource "yandex_resourcemanager_folder_iam_member" "rih_sa_storage_editor" {
+  folder_id = local.rih_folder_id
+  role      = "storage.admin"
+  member    = "serviceAccount:${yandex_iam_service_account.rih_storage_sa.id}"
+}
+
+resource "yandex_iam_service_account_static_access_key" "rih_sa_static_key" {
+  service_account_id = yandex_iam_service_account.rih_storage_sa.id
+  description        = "RIH bucket access key"
+}
+
+resource "yandex_storage_bucket" "rih_storage_bucket" {
+  access_key = yandex_iam_service_account_static_access_key.rih_sa_static_key.access_key
+  secret_key = yandex_iam_service_account_static_access_key.rih_sa_static_key.secret_key
+  bucket     = "russiaishiring.com"
+  folder_id  = local.rih_folder_id
+  acl        = "public-read"
+
+  https {
+    certificate_id = yandex_cm_certificate.russiaishiring_com.id
+  }
+
+  website {
+    index_document = "index.html"
+  }
+}
+
+resource "yandex_cm_certificate" "russiaishiring_com" {
+  folder_id = local.rih_folder_id
+  name      = "russiaishiring-com"
+  domains   = ["russiaishiring.com"]
+
+  managed {
+    challenge_type = "DNS_CNAME"
+  }
+}
+
+resource "yandex_dns_recordset" "acme_russiaishiring_com" {
+  zone_id = yandex_dns_zone.russiaishiring_com.id
+  name    = yandex_cm_certificate.russiaishiring_com.challenges[0].dns_name
+  type    = yandex_cm_certificate.russiaishiring_com.challenges[0].dns_type
+  data    = [yandex_cm_certificate.russiaishiring_com.challenges[0].dns_value]
+  ttl     = 3600
+}
+
+resource "yandex_dns_recordset" "yandex_txt_russiaishiring_com" {
+  zone_id = yandex_dns_zone.russiaishiring_com.id
+  name    = "@"
+  type    = "TXT"
+  ttl     = 21600
+
+  data = [
+    "\"yandex-verification: b42768b04ab10b58\"",
+    "\"v=spf1 redirect=_spf.yandex.net\""
+  ]
+}
+
+resource "yandex_dns_recordset" "yandex_mx_russiaishiring_com" {
+  zone_id = yandex_dns_zone.russiaishiring_com.id
+  name    = "@"
+  type    = "MX"
+  data    = ["10 mx.yandex.net."]
+  ttl     = 21600
+}
+
+resource "yandex_dns_recordset" "yandex_dkim_russiaishiring_com" {
+  zone_id = yandex_dns_zone.russiaishiring_com.id
+  name    = "mail._domainkey"
+  type    = "TXT"
+  data    = ["\"v=DKIM1; k=rsa; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgRfKnq+PZS3RFcHUnsKAvnBs2HCH5zSFjiZZ8/oyaC4va6I506/88HkZbME2xxfivpFmkKc6eBBpSzg6TVws0R3hAmb02u3qUQpX29+lEossq9j2fYvYBBZDf557ioxfQrE0+bIpsqV7+LXIsybq61+egbH+MKbxhda6fr4oPqwIDAQAB\""]
+  ttl     = 21600
+}
+
+resource "yandex_dns_recordset" "aname_russiaishiring_com" {
+  zone_id = yandex_dns_zone.russiaishiring_com.id
+  name    = "russiaishiring.com."
+  type    = "ANAME"
+  data    = ["russiaishiring.com.website.yandexcloud.net"]
+  ttl     = 3600
+}
+
+resource "yandex_container_registry" "rih_registry" {
+  name      = "rih-registry"
+  folder_id = local.rih_folder_id
+}
+
+resource "yandex_iam_service_account" "rih_backend" {
+  name      = "rih-backend"
+  folder_id = local.rih_folder_id
+}
+
+resource "yandex_resourcemanager_folder_iam_member" "rih_backend_image_pull" {
+  folder_id = local.rih_folder_id
+  role      = "container-registry.images.puller"
+  member    = "serviceAccount:${yandex_iam_service_account.rih_backend.id}"
+}
+
+resource "yandex_serverless_container" "rih_backend" {
+  name               = "rih-backend"
+  folder_id          = local.rih_folder_id
+  memory             = 128
+  execution_timeout  = "10s"
+  cores              = 1
+  core_fraction      = 100
+  service_account_id = yandex_iam_service_account.rih_backend.id
+
+  image {
+    url = "cr.yandex/crpkcq65tn6bhq6puq2o/rih-backend:q8kfd6kwc7p4wphzw1pj916y9m6icl9q"
+  }
+
+  secrets {
+    id                   = yandex_lockbox_secret.rih_backend_storage_key.id
+    version_id           = yandex_lockbox_secret_version.rih_backend_storage_secret.id
+    key                  = "access_key"
+    environment_variable = "AWS_ACCESS_KEY_ID"
+  }
+
+  secrets {
+    id                   = yandex_lockbox_secret.rih_backend_storage_key.id
+    version_id           = yandex_lockbox_secret_version.rih_backend_storage_secret.id
+    key                  = "secret_key"
+    environment_variable = "AWS_SECRET_ACCESS_KEY"
+  }
+
+  secrets {
+    id                   = data.yandex_lockbox_secret.rih_captcha_prod_key.id
+    version_id           = data.yandex_lockbox_secret.rih_captcha_prod_key.current_version[0].id
+    key                  = "key"
+    environment_variable = "YANDEX_SMARTCAPTCHA_KEY"
+  }
+}
+
+resource "yandex_api_gateway" "rih_gateway" {
+  name      = "rih-gateway"
+  folder_id = local.rih_folder_id
+
+  custom_domains {
+    fqdn           = "api.russiaishiring.com"
+    certificate_id = yandex_cm_certificate.api_russiaishiring_com.id
+  }
+
+  depends_on = [
+    yandex_cm_certificate.api_russiaishiring_com,
+    yandex_dns_recordset.acme_api_russiaishiring_com,
+  ]
+
+  spec = <<-EOT
+    openapi: "3.0.0"
+    info:
+      version: 1.0.0
+      title: RIH API
+    x-yc-apigateway:
+      cors:
+        origin: 'https://russiaishiring.com'
+        methods: '*'
+        allowedHeaders: '*'
+    paths:
+      /{proxy+}:
+        x-yc-apigateway-any-method:
+          x-yc-apigateway-integration:
+            type: serverless_containers
+            container_id: ${yandex_serverless_container.rih_backend.id}
+            service_account_id: ${yandex_iam_service_account.rih_backend.id}
+          parameters:
+          - explode: false
+            in: path
+            name: proxy
+            required: false
+            schema:
+              default: '-'
+              type: string
+            style: simple
+  EOT
+}
+
+resource "yandex_cm_certificate" "api_russiaishiring_com" {
+  folder_id = local.rih_folder_id
+  name      = "api-russiaishiring-com"
+  domains   = ["api.russiaishiring.com"]
+
+  managed {
+    challenge_type = "DNS_CNAME"
+  }
+}
+
+resource "yandex_dns_recordset" "acme_api_russiaishiring_com" {
+  zone_id = yandex_dns_zone.russiaishiring_com.id
+  name    = yandex_cm_certificate.api_russiaishiring_com.challenges[0].dns_name
+  type    = yandex_cm_certificate.api_russiaishiring_com.challenges[0].dns_type
+  data    = [yandex_cm_certificate.api_russiaishiring_com.challenges[0].dns_value]
+  ttl     = 60
+}
+
+resource "yandex_dns_recordset" "cname_api_russiaishiring_com" {
+  zone_id = yandex_dns_zone.russiaishiring_com.id
+  name    = "api.russiaishiring.com."
+  type    = "CNAME"
+  data    = [yandex_api_gateway.rih_gateway.domain]
+  ttl     = 600
+}
+
+# Bucket setup for data receival bucket
+#
+# The bucket is set up and controlled by the default storage account,
+# but a separate key is set up for the rih-backend IAM account which
+# can only access the information in this bucket.
+
+resource "yandex_kms_symmetric_key" "backend_data_key" {
+  name              = "rih-backend-data-key"
+  default_algorithm = "AES_128"
+  rotation_period   = "4380h" # ~6 months
+
+  lifecycle {
+    prevent_destroy = true
+  }
+}
+
+resource "yandex_kms_symmetric_key_iam_binding" "rih_encryption_access" {
+  symmetric_key_id = yandex_kms_symmetric_key.backend_data_key.id
+  role             = "kms.keys.encrypter"
+
+  members = [
+    "serviceAccount:${yandex_iam_service_account.rih_backend.id}"
+  ]
+}
+
+resource "yandex_storage_bucket" "rih_backend_data" {
+  access_key = yandex_iam_service_account_static_access_key.rih_sa_static_key.access_key
+  secret_key = yandex_iam_service_account_static_access_key.rih_sa_static_key.secret_key
+  bucket     = "rih-backend-data"
+  folder_id  = local.rih_folder_id
+  acl        = "private"
+
+  versioning {
+    enabled = true
+  }
+
+  server_side_encryption_configuration {
+    rule {
+      apply_server_side_encryption_by_default {
+        kms_master_key_id = yandex_kms_symmetric_key.backend_data_key.id
+        sse_algorithm     = "aws:kms"
+      }
+    }
+  }
+
+  lifecycle {
+    prevent_destroy = true
+  }
+}
+
+resource "yandex_iam_service_account_static_access_key" "rih_backend_static_key" {
+  service_account_id = yandex_iam_service_account.rih_backend.id
+  description        = "RIH backend bucket access key"
+}
+
+resource "yandex_lockbox_secret" "rih_backend_storage_key" {
+  name      = "rih-backend-storage-key"
+  folder_id = local.rih_folder_id
+}
+
+resource "yandex_lockbox_secret_version" "rih_backend_storage_secret" {
+  secret_id = yandex_lockbox_secret.rih_backend_storage_key.id
+
+  entries {
+    key        = "access_key"
+    text_value = yandex_iam_service_account_static_access_key.rih_backend_static_key.access_key
+  }
+
+  entries {
+    key        = "secret_key"
+    text_value = yandex_iam_service_account_static_access_key.rih_backend_static_key.secret_key
+  }
+}
+
+# TODO(tazjin): automate if tf-yandex gains support for captcha resources
+data "yandex_lockbox_secret" "rih_captcha_prod_key" {
+  secret_id = "e6qloc8913tnracefb8f"
+}
+
+# TODO(tazjin): needs provider update
+#
+# resource "yandex_lockbox_secret_iam_binding" "viewer" {
+#   secret_id = yandex_lockbox_secret.rih_backend_storage_key.id
+#   role = "viewer"
+
+#   members = [
+#     "serviceAccount:${yandex_iam_service_account.rih_backend.id}"
+#   ]
+# }
diff --git a/corp/rih/.gitignore b/corp/rih/.gitignore
new file mode 100644
index 0000000000..64476c05e0
--- /dev/null
+++ b/corp/rih/.gitignore
@@ -0,0 +1,3 @@
+result
+target
+dist
diff --git a/corp/rih/README.md b/corp/rih/README.md
new file mode 100644
index 0000000000..e44d0f2a58
--- /dev/null
+++ b/corp/rih/README.md
@@ -0,0 +1,3 @@
+Implementation of russiaishiring.com.
+
+This is a corporate TVL project, see `//corp/LICENSE`.
diff --git a/corp/rih/backend/Cargo.lock b/corp/rih/backend/Cargo.lock
new file mode 100644
index 0000000000..afbe6fbc0b
--- /dev/null
+++ b/corp/rih/backend/Cargo.lock
@@ -0,0 +1,1315 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+
+[[package]]
+name = "ascii"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
+
+[[package]]
+name = "async-trait"
+version = "0.1.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "attohttpc"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7"
+dependencies = [
+ "http",
+ "log",
+ "rustls",
+ "serde",
+ "serde_json",
+ "url",
+ "webpki",
+ "webpki-roots",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "aws-creds"
+version = "0.34.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3776743bb68d4ad02ba30ba8f64373f1be4e082fe47651767171ce75bb2f6cf5"
+dependencies = [
+ "attohttpc",
+ "dirs",
+ "log",
+ "quick-xml",
+ "rust-ini",
+ "serde",
+ "thiserror",
+ "time",
+ "url",
+]
+
+[[package]]
+name = "aws-region"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "056557a61427d0e5ba29dd931031c8ffed4ee7a550e7cd55692a9d8deb0a9dba"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "buf_redux"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
+dependencies = [
+ "memchr",
+ "safemem",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "winapi",
+]
+
+[[package]]
+name = "chunked_transfer"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "dirs"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dlv-list"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
+
+[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "filetime"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.2.16",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "idna"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
+name = "js-sys"
+version = "0.3.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.144"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "log"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
+
+[[package]]
+name = "maybe-async"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "md5"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "multipart"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182"
+dependencies = [
+ "buf_redux",
+ "httparse",
+ "log",
+ "mime",
+ "mime_guess",
+ "quick-error",
+ "rand",
+ "safemem",
+ "tempfile",
+ "twoway",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+dependencies = [
+ "hermit-abi 0.2.6",
+ "libc",
+]
+
+[[package]]
+name = "num_threads"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "ordered-multimap"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
+dependencies = [
+ "dlv-list",
+ "hashbrown",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quick-xml"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom",
+ "redox_syscall 0.2.16",
+ "thiserror",
+]
+
+[[package]]
+name = "rih-backend"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "attohttpc",
+ "log",
+ "rouille",
+ "rust-s3",
+ "serde",
+ "serde_json",
+ "uuid",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "rouille"
+version = "3.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921"
+dependencies = [
+ "base64",
+ "chrono",
+ "filetime",
+ "multipart",
+ "percent-encoding",
+ "rand",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "sha1_smol",
+ "threadpool",
+ "time",
+ "tiny_http",
+ "url",
+]
+
+[[package]]
+name = "rust-ini"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
+dependencies = [
+ "cfg-if",
+ "ordered-multimap",
+]
+
+[[package]]
+name = "rust-s3"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b2ac5ff6acfbe74226fa701b5ef793aaa054055c13ebb7060ad36942956e027"
+dependencies = [
+ "async-trait",
+ "attohttpc",
+ "aws-creds",
+ "aws-region",
+ "base64",
+ "bytes",
+ "cfg-if",
+ "hex",
+ "hmac",
+ "http",
+ "log",
+ "maybe-async",
+ "md5",
+ "percent-encoding",
+ "quick-xml",
+ "serde",
+ "serde_derive",
+ "sha2",
+ "thiserror",
+ "time",
+ "url",
+]
+
+[[package]]
+name = "rustix"
+version = "0.37.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
+dependencies = [
+ "log",
+ "ring",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "safemem"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
+
+[[package]]
+name = "sct"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.163"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.163"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+
+[[package]]
+name = "sha2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall 0.3.5",
+ "rustix",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "time"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
+dependencies = [
+ "itoa",
+ "libc",
+ "num_threads",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
+
+[[package]]
+name = "time-macros"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
+dependencies = [
+ "time-core",
+]
+
+[[package]]
+name = "tiny_http"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
+dependencies = [
+ "ascii",
+ "chunked_transfer",
+ "httpdate",
+ "log",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "twoway"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "typenum"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+
+[[package]]
+name = "unicase"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "url"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "uuid"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
+dependencies = [
+ "getrandom",
+ "serde",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
+
+[[package]]
+name = "web-sys"
+version = "0.3.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.22.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
+dependencies = [
+ "webpki",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+dependencies = [
+ "windows-targets 0.48.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/corp/rih/backend/Cargo.toml b/corp/rih/backend/Cargo.toml
new file mode 100644
index 0000000000..97d4821e3b
--- /dev/null
+++ b/corp/rih/backend/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "rih-backend"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0"
+log = "0.4"
+serde = { version = "1.0", features = [ "derive" ] }
+serde_json = "1.0"
+uuid = { version = "1.3.3", features = ["v4", "serde"] }
+
+[dependencies.attohttpc]
+version = "0.22"
+default-features = false
+features = [ "tls-rustls" ]
+
+[dependencies.rouille]
+version = "3.6"
+default-features = false
+
+[dependencies.rust-s3]
+version = "0.33"
+default-features = false
+features = [ "sync-rustls-tls" ]
diff --git a/corp/rih/backend/default.nix b/corp/rih/backend/default.nix
new file mode 100644
index 0000000000..802479e509
--- /dev/null
+++ b/corp/rih/backend/default.nix
@@ -0,0 +1,16 @@
+{ depot, pkgs, ... }:
+
+depot.nix.readTree.drvTargets rec {
+  binary = depot.third_party.naersk.buildPackage {
+    src = ./.;
+  };
+
+  image = pkgs.dockerTools.buildLayeredImage {
+    name = "rih-backend";
+    config.Cmd = [ "${binary}/bin/rih-backend" ];
+
+    contents = [
+      binary
+    ];
+  };
+}
diff --git a/corp/rih/backend/src/main.rs b/corp/rih/backend/src/main.rs
new file mode 100644
index 0000000000..208e0367c6
--- /dev/null
+++ b/corp/rih/backend/src/main.rs
@@ -0,0 +1,168 @@
+use anyhow::{bail, Context, Result};
+use log::{debug, error, info, warn, LevelFilter};
+use rouille::{Request, Response};
+use serde::{Deserialize, Serialize};
+use std::collections::BTreeSet;
+use std::env;
+use std::net::SocketAddr;
+use std::time::{SystemTime, UNIX_EPOCH};
+use uuid::Uuid;
+
+mod yandex_log;
+
+/// Represents the request sent by the frontend application.
+#[derive(Debug, Deserialize)]
+struct FrontendReq {
+    captcha_token: String,
+    record: Record,
+}
+
+/// Represents a single record as filled in by a user. This is the
+/// primary data structure we want to populate and persist somewhere.
+#[derive(Debug, Deserialize, Serialize)]
+struct Record {
+    // Record-specific metadata
+    uuid: Uuid,
+
+    // Personal information
+    name: String,
+    email: String,
+    citizenship: String, // TODO
+    personal_details: String,
+
+    // Job information
+    position: String,
+    technologies: BTreeSet<String>,
+    job_details: String,
+    work_background: String,
+}
+
+impl Record {
+    fn validate(&self) -> bool {
+        true
+    }
+}
+
+fn validate_captcha(token: &str) -> Result<()> {
+    // TODO(tazjin): pass `ip` parameter
+    let url = "https://smartcaptcha.yandexcloud.net/validate";
+    let backend_key =
+        env::var("YANDEX_SMARTCAPTCHA_KEY").context("captcha verification key not provided")?;
+
+    #[derive(Deserialize)]
+    struct CaptchaResponse {
+        status: String,
+        message: String,
+    }
+
+    let response: CaptchaResponse = attohttpc::get(url)
+        .param("secret", backend_key)
+        .param("token", token)
+        .send()
+        .context("failed to send captcha verification request")?
+        .error_for_status()
+        .context("captcha verification request failed")?
+        .json()
+        .context("failed to deserialize captcha verification response")?;
+
+    if response.status != "ok" {
+        warn!(
+            "invalid captcha: {} ({})",
+            response.message, response.status
+        );
+    }
+
+    info!("captcha token was valid");
+
+    Ok(())
+}
+
+fn persist_record(ip: &SocketAddr, record: &Record) -> Result<()> {
+    let bucket_name = "rih-backend-data";
+    let credentials =
+        s3::creds::Credentials::from_env().context("failed to initialise storage credentials")?;
+
+    let yandex_region: s3::Region = s3::Region::Custom {
+        region: "ru-central1".to_string(),
+        endpoint: "storage.yandexcloud.net".to_string(),
+    };
+
+    let bucket = s3::Bucket::new(bucket_name, yandex_region, credentials)
+        .context("failed to initialise storage client")?;
+
+    let path_uuid = Uuid::new_v4();
+    let epoch = SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .context("failed to get current time")?
+        .as_secs();
+
+    let path = format!("/records/{}-{}.json", epoch, path_uuid);
+
+    info!("writing record to '{}'", path);
+
+    let data = serde_json::json!({
+        "ip": ip.to_string(),
+        "record": record,
+    });
+
+    let response = bucket
+        .put_object(path, data.to_string().as_bytes())
+        .context("failed to persist storage object")?;
+
+    debug!(
+        "Object Storage response: ({}) {}",
+        response.status_code(),
+        response.as_str().unwrap_or("<unprintable>")
+    );
+
+    Ok(())
+}
+
+fn handle_submit(req: &Request) -> Result<Response> {
+    let submitted: FrontendReq =
+        rouille::input::json::json_input(req).context("failed to deserialise frontend request")?;
+
+    validate_captcha(&submitted.captcha_token)?;
+
+    if !submitted.record.validate() {
+        bail!("invalid record: {:?}", submitted.record);
+    }
+
+    persist_record(req.remote_addr(), &submitted.record).context("failed to persist record")?;
+
+    Ok(Response::text("success"))
+}
+
+fn main() -> Result<()> {
+    log::set_logger(&yandex_log::YANDEX_CLOUD_LOGGER)
+        .map(|()| log::set_max_level(LevelFilter::Info))
+        .expect("log configuration must succeed");
+    let port = env::var("PORT").unwrap_or_else(|_| /* rihb = */ "7442".to_string());
+    let listen = format!("0.0.0.0:{port}");
+
+    info!("launching rih-backend on: {}", listen);
+
+    rouille::start_server(&listen, move |request| {
+        if request.method() == "POST" && request.url() == "/submit" {
+            info!("handling submit request from {}", request.remote_addr());
+            match handle_submit(request) {
+                Ok(response) => {
+                    info!("submit handled successfully");
+                    response
+                }
+                Err(err) => {
+                    error!("failed to handle submit: {}", err);
+                    Response::empty_400()
+                }
+            }
+        } else {
+            warn!(
+                "no matching route for request: {} {}",
+                request.method(),
+                request.url()
+            );
+
+            Response::empty_404()
+        }
+    });
+}
diff --git a/corp/rih/backend/src/yandex_log.rs b/corp/rih/backend/src/yandex_log.rs
new file mode 100644
index 0000000000..64bb4ff97d
--- /dev/null
+++ b/corp/rih/backend/src/yandex_log.rs
@@ -0,0 +1,47 @@
+//! Implements a `log::Log` logger that adheres to the structure
+//! expected by Yandex Cloud Serverless logs.
+//!
+//! https://cloud.yandex.ru/docs/serverless-containers/concepts/logs
+
+use log::{Level, Log};
+use serde_json::json;
+
+pub struct YandexCloudLogger;
+
+pub const YANDEX_CLOUD_LOGGER: YandexCloudLogger = YandexCloudLogger;
+
+fn level_map(level: &Level) -> &'static str {
+    match level {
+        Level::Error => "ERROR",
+        Level::Warn => "WARN",
+        Level::Info => "INFO",
+        Level::Debug => "DEBUG",
+        Level::Trace => "TRACE",
+    }
+}
+
+impl Log for YandexCloudLogger {
+    fn enabled(&self, _: &log::Metadata<'_>) -> bool {
+        true
+    }
+
+    fn log(&self, record: &log::Record<'_>) {
+        if !self.enabled(record.metadata()) {
+            return;
+        }
+
+        eprintln!(
+            "{}",
+            json!({
+                "level": level_map(&record.level()),
+                "message": record.args().to_string(),
+                "target": record.target(),
+                "module": record.module_path(),
+                "file": record.file(),
+                "line": record.line(),
+            })
+        );
+    }
+
+    fn flush(&self) {}
+}
diff --git a/corp/rih/frontend/Cargo.lock b/corp/rih/frontend/Cargo.lock
new file mode 100644
index 0000000000..2d2f5ea84b
--- /dev/null
+++ b/corp/rih/frontend/Cargo.lock
@@ -0,0 +1,1825 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "anymap2"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "base64"
+version = "0.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "boolinator"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
+
+[[package]]
+name = "bumpalo"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+ "once_cell",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "bitflags",
+ "clap_lex",
+ "strsim",
+ "terminal_size",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "comrak"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "482aa5695bca086022be453c700a40c02893f1ba7098a2c88351de55341ae894"
+dependencies = [
+ "clap",
+ "entities",
+ "memchr",
+ "once_cell",
+ "regex",
+ "shell-words",
+ "slug",
+ "syntect",
+ "typed-arena",
+ "unicode_categories",
+ "xdg",
+]
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "csv"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad"
+dependencies = [
+ "csv-core",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "deunicode"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690"
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "encode_unicode"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
+
+[[package]]
+name = "entities"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
+
+[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fancy-regex"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf"
+dependencies = [
+ "bit-set",
+ "regex",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-io"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "fuzzy-matcher"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
+dependencies = [
+ "thread_local",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a4bef6b277b3ab073253d4bca60761240cf8d6998f4bd142211957b69a61b20"
+dependencies = [
+ "gloo-console",
+ "gloo-dialogs",
+ "gloo-events",
+ "gloo-file",
+ "gloo-history",
+ "gloo-net",
+ "gloo-render",
+ "gloo-storage",
+ "gloo-timers",
+ "gloo-utils",
+ "gloo-worker",
+]
+
+[[package]]
+name = "gloo-console"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-dialogs"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-events"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-file"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7"
+dependencies = [
+ "futures-channel",
+ "gloo-events",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-history"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd451019e0b7a2b8a7a7b23e74916601abf1135c54664e57ff71dcc26dfcdeb7"
+dependencies = [
+ "gloo-events",
+ "gloo-utils",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_urlencoded",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-net"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-sink",
+ "gloo-utils",
+ "js-sys",
+ "pin-project",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-render"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-storage"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-worker"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a"
+dependencies = [
+ "anymap2",
+ "bincode",
+ "gloo-console",
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
+name = "home"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "implicit-clone"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7"
+dependencies = [
+ "indexmap",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
+name = "js-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.141"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
+
+[[package]]
+name = "line-wrap"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
+dependencies = [
+ "safemem",
+]
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+dependencies = [
+ "hermit-abi 0.2.6",
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+
+[[package]]
+name = "onig"
+version = "6.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f"
+dependencies = [
+ "bitflags",
+ "libc",
+ "once_cell",
+ "onig_sys",
+]
+
+[[package]]
+name = "onig_sys"
+version = "69.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+
+[[package]]
+name = "phf"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
+dependencies = [
+ "phf_shared",
+ "rand",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pinned"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b"
+dependencies = [
+ "futures",
+ "rustversion",
+ "thiserror",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "plist"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590"
+dependencies = [
+ "base64",
+ "indexmap",
+ "line-wrap",
+ "quick-xml",
+ "serde",
+ "time",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "prettyplease"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+dependencies = [
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "prettytable-rs"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a"
+dependencies = [
+ "csv",
+ "encode_unicode",
+ "is-terminal",
+ "lazy_static",
+ "term",
+ "unicode-width",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prokio"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488"
+dependencies = [
+ "futures",
+ "gloo",
+ "num_cpus",
+ "once_cell",
+ "pin-project",
+ "pinned",
+ "tokio",
+ "tokio-stream",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.28.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom",
+ "redox_syscall",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.7.2",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+
+[[package]]
+name = "rih"
+version = "0.1.0"
+dependencies = [
+ "fuzzy-matcher",
+ "getrandom",
+ "gloo",
+ "js-sys",
+ "rand",
+ "rust_iso3166",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "static_markdown",
+ "uuid",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "yew",
+ "yew-router",
+]
+
+[[package]]
+name = "route-recognizer"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
+
+[[package]]
+name = "rust_iso3166"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e19f23f46e5d2f3f1e917ecf5cc988e23ba7fc2a2ce3ef09cb17033842d0ef8"
+dependencies = [
+ "js-sys",
+ "phf",
+ "prettytable-rs",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "rustix"
+version = "0.37.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
+
+[[package]]
+name = "ryu"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "safemem"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.160"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.160"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "shell-words"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
+
+[[package]]
+name = "siphasher"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
+
+[[package]]
+name = "slab"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "slug"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373"
+dependencies = [
+ "deunicode",
+]
+
+[[package]]
+name = "static_markdown"
+version = "1.0.0"
+dependencies = [
+ "comrak",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syntect"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6c454c27d9d7d9a84c7803aaa3c50cd088d2906fe3c6e42da3209aa623576a8"
+dependencies = [
+ "bincode",
+ "bitflags",
+ "fancy-regex",
+ "flate2",
+ "fnv",
+ "lazy_static",
+ "once_cell",
+ "onig",
+ "plist",
+ "regex-syntax 0.6.29",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "thiserror",
+ "walkdir",
+ "yaml-rust",
+]
+
+[[package]]
+name = "term"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
+dependencies = [
+ "dirs-next",
+ "rustversion",
+ "winapi",
+]
+
+[[package]]
+name = "terminal_size"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
+dependencies = [
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "time"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
+dependencies = [
+ "itoa",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
+
+[[package]]
+name = "time-macros"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
+dependencies = [
+ "time-core",
+]
+
+[[package]]
+name = "tokio"
+version = "1.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001"
+dependencies = [
+ "autocfg",
+ "pin-project-lite",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "typed-arena"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "unicode_categories"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "uuid"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
+dependencies = [
+ "getrandom",
+ "serde",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "walkdir"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
+
+[[package]]
+name = "web-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "xdg"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "688597db5a750e9cad4511cb94729a078e274308099a0382b5b8203bbc767fee"
+dependencies = [
+ "home",
+]
+
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
+
+[[package]]
+name = "yew"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc"
+dependencies = [
+ "console_error_panic_hook",
+ "futures",
+ "gloo",
+ "implicit-clone",
+ "indexmap",
+ "js-sys",
+ "prokio",
+ "rustversion",
+ "serde",
+ "slab",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "yew-macro",
+]
+
+[[package]]
+name = "yew-macro"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b64c253c1d401f1ea868ca9988db63958cfa15a69f739101f338d6f05eea8301"
+dependencies = [
+ "boolinator",
+ "once_cell",
+ "prettyplease",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "yew-router"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "426ee0486d2572a6c5e39fbdbc48b58d59bb555f3326f54631025266cf04146e"
+dependencies = [
+ "gloo",
+ "js-sys",
+ "route-recognizer",
+ "serde",
+ "serde_urlencoded",
+ "tracing",
+ "wasm-bindgen",
+ "web-sys",
+ "yew",
+ "yew-router-macro",
+]
+
+[[package]]
+name = "yew-router-macro"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b249cdb39e0cddaf0644dedc781854524374664793479fdc01e6a65d6e6ae3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
diff --git a/corp/rih/frontend/Cargo.toml b/corp/rih/frontend/Cargo.toml
new file mode 100644
index 0000000000..f01378c7e6
--- /dev/null
+++ b/corp/rih/frontend/Cargo.toml
@@ -0,0 +1,42 @@
+[package]
+version = "0.1.0"
+name = "rih"
+authors = [ "Vincent Ambo <tazjin@tvl.su>" ]
+license = "Proprietary"
+edition = "2021"
+
+[dependencies]
+fuzzy-matcher = "0.3.7"
+getrandom = { version = "0.2", features = ["js"] }
+gloo = "0.8"
+js-sys = "0.3"
+rand = "0.8"
+rust_iso3166 = "0.1.10"
+serde_json = "1.0"
+serde_urlencoded = "*" # pinned by yew
+yew = { version = "0.20", features = ["csr"] }
+yew-router = "0.17"
+wasm-bindgen-futures = "0.4"
+
+# needs to be in sync with nixpkgs
+wasm-bindgen = "= 0.2.91"
+uuid = { version = "1.3.3", features = ["v4", "serde"] }
+
+[dependencies.serde]
+version = "*" # pinned by yew
+features = [ "derive" ]
+
+[dependencies.web-sys]
+version = "*" # pinned by yew
+features = [ "HtmlDetailsElement" ]
+
+[dependencies.static_markdown]
+path = "./static-markdown"
+
+[profile.release]
+lto = true
+opt-level = 'z'
+codegen-units = 1
+
+[package.metadata.wasm-pack.profile.release]
+wasm-opt = ['-Os']
diff --git a/corp/rih/frontend/default.nix b/corp/rih/frontend/default.nix
new file mode 100644
index 0000000000..24bbde09b9
--- /dev/null
+++ b/corp/rih/frontend/default.nix
@@ -0,0 +1,52 @@
+{ lib, pkgs, ... }:
+
+let
+  wasmRust = pkgs.rust-bin.stable.latest.default.override {
+    targets = [ "wasm32-unknown-unknown" ];
+  };
+
+  cargoToml = with builtins; fromTOML (readFile ./Cargo.toml);
+
+  wasmBindgenMatch =
+    cargoToml.dependencies.wasm-bindgen == "= ${pkgs.wasm-bindgen-cli.version}";
+
+  assertWasmBindgen = assert (lib.assertMsg wasmBindgenMatch ''
+    Due to instability in the Rust WASM ecosystem, the trunk build
+    tool enforces that the Cargo-dependency version of `wasm-bindgen`
+    MUST match the version of the CLI supplied in the environment.
+
+    This can get out of sync when nixpkgs is updated. To resolve it,
+    wasm-bindgen must be bumped in the Cargo.toml file and cargo needs
+    to be run to resolve the dependencies.
+
+    Versions of `wasm-bindgen` in Cargo.toml:
+
+      Expected: '= ${pkgs.wasm-bindgen-cli.version}'
+      Actual:   '${cargoToml.dependencies.wasm-bindgen}'
+  ''); pkgs.wasm-bindgen-cli;
+
+  deps = with pkgs; [
+    binaryen
+    sass
+    wasmRust
+    trunk
+    assertWasmBindgen
+  ];
+
+in
+pkgs.rustPlatform.buildRustPackage rec {
+  pname = "rih-frontend";
+  version = "canon";
+  src = lib.cleanSource ./.;
+  cargoLock.lockFile = ./Cargo.lock;
+
+  buildPhase = ''
+    export PATH=${lib.makeBinPath deps}:$PATH
+    mkdir home
+    export HOME=$PWD/.home
+    env
+    trunk build --release -d $out
+  '';
+
+  dontInstall = true;
+}
diff --git a/corp/rih/frontend/fonts/IdealistSans.eot b/corp/rih/frontend/fonts/IdealistSans.eot
new file mode 100644
index 0000000000..de1dfa2ed8
--- /dev/null
+++ b/corp/rih/frontend/fonts/IdealistSans.eot
Binary files differdiff --git a/corp/rih/frontend/fonts/IdealistSans.svg b/corp/rih/frontend/fonts/IdealistSans.svg
new file mode 100644
index 0000000000..01134450ef
--- /dev/null
+++ b/corp/rih/frontend/fonts/IdealistSans.svg
@@ -0,0 +1,10438 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
+<metadata>
+Created by FontForge 20190801 at Thu Jan 17 11:47:14 2013
+ By www-data
+Copyright (c) 2012 by Glenjan. All rights reserved.
+</metadata>
+<defs>
+<font id="IdealistSans" horiz-adv-x="590" >
+  <font-face 
+    font-family="IdealistSans"
+    font-weight="400"
+    font-stretch="normal"
+    units-per-em="1000"
+    panose-1="2 0 0 0 0 0 0 0 0 0"
+    ascent="750"
+    descent="-250"
+    x-height="500"
+    cap-height="650"
+    bbox="-79 -277 6315 1009"
+    underline-thickness="50"
+    underline-position="-100"
+    unicode-range="U+0020-25CA"
+  />
+<missing-glyph horiz-adv-x="510" 
+d="M433 400l-328 -284l109 190l-109 94h328z" />
+    <glyph glyph-name="germandbls" unicode="&#x17f;s" horiz-adv-x="635" 
+d="M65 541q0 41 18 76t49 60t73.5 39t92.5 14q52 0 88 -13.5t58 -35.5t32 -50.5t10 -58.5q0 -53 -27 -86.5t-73 -52.5q-35 -14 -49 -28t-14 -38q0 -29 25 -44.5t67 -23.5q97 -18 142.5 -54t45.5 -102q0 -39 -16.5 -68t-45.5 -47.5t-69 -28t-87 -9.5q-27 0 -48.5 1.5t-41 5.5
+t-38 10.5t-39.5 16.5l24 76q73 -35 143 -35q72 0 101 19t29 60q0 30 -31.5 46t-94.5 28q-34 6 -62.5 17.5t-49 29t-32 42t-11.5 57.5q0 29 8.5 49.5t22.5 35.5t32 25t38 18q37 15 53 35t16 46q0 21 -8 35.5t-22 24t-33 14t-41 4.5q-38 0 -65 -9t-44 -24.5t-25 -37t-8 -46.5
+v-534h-93v541z" />
+    <glyph glyph-name="fb.liga" unicode="fb" horiz-adv-x="999" 
+d="M107 500v33q0 43 15 79t42 62.5t64.5 41t82.5 14.5q63 0 112 -17.5t95 -52.5v-194q38 23 79.5 33.5t92.5 10.5q122 0 185.5 -64t63.5 -191q0 -60 -22.5 -109t-62 -83.5t-93.5 -53.5t-118 -19q-35 0 -64 3t-54.5 9.5t-49.5 16.5t-50 24v573q-26 19 -52 26.5t-60 7.5
+q-62 0 -87.5 -31t-25.5 -83v-36h152l-4 -77l-148 5v-428h-93v423l-87 12v65h87zM518 95q17 -8 31.5 -13t28.5 -8t30 -4.5t36 -1.5q92 0 146.5 47.5t54.5 140.5q0 90 -39 134.5t-124 44.5q-48 0 -88.5 -11t-75.5 -36v-293z" />
+    <glyph glyph-name="ff.liga" unicode="ff" horiz-adv-x="671" 
+d="M107 500v33q0 43 13.5 79t39 62.5t62 41t81.5 14.5q56 0 95 -11.5t77 -36.5q25 23 58.5 35.5t75.5 12.5q50 0 87 -10t74 -33l-45 -73q-26 18 -49 27t-55 9q-63 0 -88.5 -30.5t-25.5 -83.5v-36h153l-4 -77l-149 5v-428h-93v421l-214 8v-429h-93v423l-87 12v65h87zM414 500
+v33q0 21 3 41t10 37q-50 39 -114 39q-62 0 -87.5 -31t-25.5 -83v-36h214z" />
+    <glyph glyph-name="ffi.liga" unicode="ffi" horiz-adv-x="855" 
+d="M697 422l-190 7v-429h-93v421l-214 8v-429h-93v423l-87 12v65h87v33q0 43 13.5 79t39 62.5t62 41t81.5 14.5q56 0 95 -11.5t77 -36.5q25 23 58.5 35.5t75.5 12.5q50 0 87 -9.5t74 -33.5l-45 -73q-26 18 -49 27t-55 9q-63 0 -88.5 -30.5t-25.5 -83.5v-36h283v-500h-93v422
+zM414 500v33q0 21 3 41t10 37q-50 39 -114 39q-62 0 -87.5 -31t-25.5 -83v-36h214z" />
+    <glyph glyph-name="ffj.liga" unicode="ffj" horiz-adv-x="855" 
+d="M107 500v33q0 43 13.5 79t39 62.5t62 41t81.5 14.5q56 0 95 -11.5t77 -36.5q25 23 58.5 35.5t75.5 12.5q50 0 87 -9.5t74 -33.5l-45 -72q-26 17 -49 26t-55 9q-63 0 -88.5 -30.5t-25.5 -83.5v-36h283v-529q0 -101 -52 -156.5t-149 -57.5l-7 79q59 4 86.5 32t27.5 86v468
+l-189 7v-429h-93v421l-214 8v-429h-93v423l-87 12v65h87zM414 500v33q0 21 3 41t10 37q-50 39 -114 39q-62 0 -87.5 -31t-25.5 -83v-36h214z" />
+    <glyph glyph-name="fh.liga" unicode="fh" horiz-adv-x="966" 
+d="M107 500v33q0 43 15 79t42 62.5t64.5 41t82.5 14.5q63 0 112 -17.5t95 -52.5v-232q35 44 82.5 63t98.5 19q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-56 0 -100.5 -22t-80.5 -66v-346h-93v616q-26 19 -52 26.5t-60 7.5q-62 0 -87.5 -31
+t-25.5 -83v-36h152l-4 -77l-148 5v-428h-93v423l-87 12v65h87z" />
+    <glyph glyph-name="fi.liga" unicode="fi" horiz-adv-x="547" 
+d="M107 500v33q0 43 13.5 79t38.5 62.5t61 41t81 14.5q50 0 87 -9.5t74 -33.5l-45 -73q-26 18 -49.5 27t-54.5 9q-62 0 -87.5 -31t-25.5 -83v-36h282v-500h-93v422l-189 7v-429h-93v423l-87 12v65h87z" />
+    <glyph glyph-name="fj.liga" unicode="fj" horiz-adv-x="547" 
+d="M107 500v33q0 43 13.5 79t38.5 62.5t61 41t81 14.5q48 0 86 -9t75 -34l-45 -71q-26 17 -49 25.5t-55 8.5q-62 0 -87.5 -31t-25.5 -83v-36h282v-529q0 -101 -51.5 -156.5t-148.5 -57.5l-7 79q59 4 86.5 32t27.5 86v468l-189 7v-429h-93v423l-87 12v65h87z" />
+    <glyph glyph-name="fk.liga" unicode="fk" horiz-adv-x="940" 
+d="M518 292l80 53l174 155h109l2 -10l-217 -189l227 -293l-3 -8h-105l-190 255l-77 -43v-212h-92v616q-26 19 -52.5 26.5t-60.5 7.5q-62 0 -87.5 -31t-25.5 -83v-36h152l-4 -77l-148 5v-428h-93v423l-87 12v65h87v33q0 43 15 79t42 62.5t64.5 41t82.5 14.5q63 0 112 -17.5
+t95 -52.5v-368z" />
+    <glyph glyph-name="fl.liga" unicode="fl" horiz-adv-x="602" 
+d="M107 500v33q0 43 15 79t42 62.5t64.5 41t82.5 14.5q63 0 112 -17.5t95 -52.5v-516q0 -39 14.5 -51.5t59.5 -12.5v-85q-6 -1 -12 -1h-12q-40 0 -67 11.5t-44 32.5t-24.5 51.5t-7.5 68.5v458q-26 19 -52 26.5t-60 7.5q-62 0 -87.5 -31t-25.5 -83v-36h152l-4 -77l-148 5
+v-428h-93v423l-87 12v65h87z" />
+    <glyph glyph-name="ft.liga" unicode="ft" horiz-adv-x="730" 
+d="M680 423l-196 8v-262q0 -57 26 -81t67 -24q33 0 59.5 9t54.5 26l30 -67q-31 -22 -67.5 -32t-86.5 -10q-86 0 -131.5 45t-45.5 133v254l-190 7v-429h-93v423l-87 12v65h87v33q0 43 13.5 79t38.5 62.5t61 41t81 14.5q56 0 98.5 -14.5t84.5 -49.5v-166h199zM338 500l34 13
+l47 83q-41 54 -106 54q-62 0 -87.5 -31t-25.5 -83v-36h138z" />
+    <glyph glyph-name="ii.liga" unicode="i&#x457;" horiz-adv-x="446" 
+d="M158 500v-500h-93v500h93zM223 691q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -42.5t-45.5 -17.5q-28 0 -45 17.5t-17 42.5q0 26 17 43t45 17zM416 691q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -42.5t-45.5 -17.5q-28 0 -45 17.5t-17 42.5q0 26 17 43t45 17zM381 500v-500h-93
+v500h93zM30 691q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -42.5t-45.5 -17.5q-28 0 -45 17.5t-17 42.5q0 26 17 43t45 17z" />
+    <glyph glyph-name="lsb.liga" unicode="&#x17f;b" horiz-adv-x="901" 
+d="M65 535q0 43 13.5 79t39 61.5t62 40t82.5 14.5q50 0 85.5 -10t72.5 -33v-221q38 23 79.5 33.5t92.5 10.5q122 0 185.5 -64t63.5 -191q0 -60 -22.5 -109t-62 -83.5t-93.5 -53.5t-118 -19q-35 0 -64 3t-54.5 9.5t-49.5 16.5t-50 24v600q-24 7 -56 7q-62 0 -87.5 -31
+t-25.5 -83v-536h-93v535zM420 95q17 -8 31.5 -13t28.5 -8t30 -4.5t36 -1.5q92 0 146.5 47.5t54.5 140.5q0 90 -39 134.5t-124 44.5q-48 0 -88.5 -11t-75.5 -36v-293z" />
+    <glyph glyph-name="lsh.liga" unicode="&#x17f;h" horiz-adv-x="868" 
+d="M65 535q0 43 13.5 79t39 61.5t62 40t82.5 14.5q50 0 85.5 -10t72.5 -33v-259q35 44 82.5 63t98.5 19q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-56 0 -100.5 -22t-80.5 -66v-346h-93v643q-24 7 -56 7q-62 0 -87.5 -31t-25.5 -83v-536h-93v535z
+" />
+    <glyph glyph-name="lsi.liga" unicode="&#x17f;i" horiz-adv-x="454" 
+d="M296 500h93v-500h-93v500zM65 533q0 43 13.5 79t39 62.5t61.5 41t81 14.5q50 0 87 -10t74 -33l-45 -71q-23 17 -47.5 25.5t-56.5 8.5q-63 0 -88.5 -30.5t-25.5 -83.5v-536h-93v533z" />
+    <glyph glyph-name="lsj.liga" unicode="&#x17f;j" horiz-adv-x="454" 
+d="M65 533q0 43 13.5 79t39 62.5t61.5 41t81 14.5q50 0 87 -10t74 -33l-45 -74q-24 19 -52 28t-55 9q-62 0 -86.5 -31t-24.5 -83v-536h-93v533zM389 500v-529q0 -101 -52 -156.5t-149 -57.5l-7 79q59 4 86.5 32t27.5 86v546h94z" />
+    <glyph glyph-name="lsk.liga" unicode="&#x17f;k" horiz-adv-x="842" 
+d="M783 500l2 -10l-217 -189l227 -293l-3 -8h-105l-190 255l-77 -43v-212h-92v643q-13 4 -27.5 5.5t-29.5 1.5q-62 0 -87.5 -31t-25.5 -83v-536h-93v535q0 43 13.5 79t39 61.5t62 40t82.5 14.5q50 0 85.5 -10t72.5 -33v-395l80 53l174 155h109z" />
+    <glyph glyph-name="lsl.liga" unicode="&#x17f;l" horiz-adv-x="503" 
+d="M65 535q0 43 13.5 79t39 61.5t62 40t82.5 14.5q50 0 85.5 -10t72.5 -33v-543q0 -39 14 -51.5t59 -12.5v-84q-6 -1 -12 -1.5t-12 -0.5q-40 0 -67 11.5t-43.5 32.5t-23.5 51.5t-7 68.5v485q-24 7 -57 7q-62 0 -87.5 -31t-25.5 -83v-536h-93v535z" />
+    <glyph glyph-name="lsls.liga" unicode="&#x17f;&#x17f;" horiz-adv-x="505" 
+d="M65 533q0 45 14 81t40.5 62t63 40t81.5 14q42 0 71.5 -6.5t58.5 -23.5q47 30 111 30q50 0 87 -10t74 -33l-45 -70q-26 17 -48.5 25t-55.5 8q-63 0 -88.5 -30.5t-25.5 -83.5v-536h-93v533q0 57 25 104q-31 13 -63 13q-63 0 -88.5 -30.5t-25.5 -83.5v-536h-93v533z" />
+    <glyph glyph-name="lslsi.liga" unicode="&#x17f;&#x17f;i" horiz-adv-x="709" 
+d="M65 533q0 45 14 81t40.5 62t63 40t81.5 14q42 0 71.5 -6.5t58.5 -23.5q47 30 111 30q50 0 87 -10t74 -33l-45 -70q-24 18 -48 25.5t-56 7.5q-63 0 -88.5 -30.5t-25.5 -83.5v-536h-93v533q0 57 25 104q-31 13 -63 13q-63 0 -88.5 -30.5t-25.5 -83.5v-536h-93v533zM551 500
+h93v-500h-93v500z" />
+    <glyph glyph-name="lslsj.liga" unicode="&#x17f;&#x17f;j" horiz-adv-x="709" 
+d="M65 533q0 45 14 81t40.5 62t63 40t81.5 14q42 0 71.5 -6.5t58.5 -23.5q47 30 111 30q50 0 87 -10t74 -33l-35 -74q-26 20 -52.5 28.5t-61.5 8.5q-63 0 -88.5 -30.5t-25.5 -83.5v-536h-93v533q0 57 25 104q-31 13 -63 13q-63 0 -88.5 -30.5t-25.5 -83.5v-536h-93v533z
+M644 500v-529q0 -101 -52 -156.5t-149 -57.5l-7 79q59 4 86.5 32t27.5 86v546h94z" />
+    <glyph glyph-name="lst.liga" unicode="&#x17f;t" horiz-adv-x="666" 
+d="M616 423l-196 8v-262q0 -57 26 -81t67 -24q33 0 59.5 9t54.5 26l30 -67q-31 -22 -67.5 -32t-86.5 -10q-86 0 -131.5 45t-45.5 133v255l-78 11v35l68 43l45 88q-20 21 -45.5 35.5t-54.5 14.5q-55 0 -79 -30.5t-24 -83.5v-536h-93v535q0 43 13.5 79t38 61.5t59 40
+t76.5 14.5q50 0 90.5 -17t77.5 -50v-163h199z" />
+    <glyph glyph-name=".notdef" horiz-adv-x="510" 
+d="M433 400l-328 -284l109 190l-109 94h328z" />
+    <glyph glyph-name=".null" horiz-adv-x="0" 
+ />
+    <glyph glyph-name="nonmarkingreturn" horiz-adv-x="333" 
+ />
+    <glyph glyph-name="space" unicode=" " horiz-adv-x="286" 
+ />
+    <glyph glyph-name="exclam" unicode="!" horiz-adv-x="225" 
+d="M160 679l-13 -509h-69l-13 509h95zM112 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="quotedbl" unicode="&#x22;" horiz-adv-x="298" 
+d="M45 650h86l-20 -183l-65 6zM167 650h86l-20 -193l-65 6z" />
+    <glyph glyph-name="numbersign" unicode="#" horiz-adv-x="524" 
+d="M238 524l-10 -99h77l11 105l72 -6l-10 -99h106l-3 -65h-109l-7 -73h110l-3 -64h-112l-11 -97l-71 5l9 92h-76l-11 -97l-72 5l9 92h-97l3 64h100l8 73h-102l3 65h103l12 105zM216 287h77l7 73h-77z" />
+    <glyph glyph-name="dollar" unicode="$" horiz-adv-x="543" 
+d="M316 673v-68q39 -4 72 -15t68 -34l-37 -69q-38 24 -75.5 34.5t-82.5 10.5q-34 0 -55 -6.5t-33 -17.5t-16 -25t-4 -29q0 -32 21 -49.5t57 -28.5l90 -27q76 -23 123 -68t47 -119q0 -67 -45 -113t-130 -58v-74h-85v71q-53 2 -97.5 14t-90.5 38l25 79q52 -29 97.5 -39.5
+t93.5 -10.5q77 0 109 22.5t32 64.5q0 24 -6 41t-19.5 29.5t-34.5 21.5t-50 17l-97 27q-60 17 -94.5 59t-34.5 101q0 66 43 106.5t124 47.5v67h85z" />
+    <glyph glyph-name="uni0025" unicode="%" horiz-adv-x="717" 
+d="M557 474l-348 -496l-57 37l349 497zM179 231q-30 0 -57.5 10.5t-47.5 29t-32 44t-12 55.5t11.5 55.5t31.5 44.5t47 29.5t59 10.5q30 0 57.5 -11t47.5 -30t32 -44.5t12 -54.5q0 -30 -12 -55.5t-32.5 -44t-47.5 -29t-57 -10.5zM179 294q32 0 55 20t23 56q0 35 -21.5 56
+t-56.5 21q-37 0 -58 -21.5t-21 -55.5t22 -55t57 -21zM538 -10q-30 0 -57.5 10.5t-47.5 29t-32 44t-12 55.5t11.5 55.5t31.5 44.5t47 29.5t59 10.5q30 0 57.5 -11t47.5 -30t32 -44.5t12 -54.5q0 -30 -12 -55.5t-32.5 -44t-47.5 -29t-57 -10.5zM538 53q32 0 55 20t23 56
+q0 35 -21.5 56t-56.5 21q-37 0 -58 -21.5t-21 -55.5t22 -55t57 -21z" />
+    <glyph glyph-name="ampersand" unicode="&#x26;" horiz-adv-x="714" 
+d="M658 431l5 -71h-51q0 -39 -3 -70t-9 -57.5t-16 -51t-25 -49.5q58 -52 126 -61l-26 -79q-27 4 -47.5 9.5t-38 14t-33.5 20t-34 28.5q-47 -41 -96 -59t-115 -18q-54 0 -101 15t-81 42.5t-53.5 67t-19.5 88.5q0 60 32.5 106t83.5 72q-52 43 -52 115q0 30 15 56t42.5 45
+t65 30t82.5 11q51 0 98 -11.5t95 -40.5l-39 -69q-66 46 -154 46q-59 0 -88 -19.5t-29 -51.5q0 -39 32 -68l267 -237q10 21 17 40t10.5 39t5 41.5t1.5 47.5l-90 15l6 64h217zM205 328q-74 -36 -74 -122q0 -32 12.5 -58t34 -45.5t50.5 -30t63 -10.5q52 0 87.5 13t66.5 42z" />
+    <glyph glyph-name="quotesingle" unicode="'" horiz-adv-x="174" 
+d="M45 650h89l-20 -193l-68 6z" />
+    <glyph glyph-name="parenleft" unicode="(" horiz-adv-x="247" 
+d="M217 675q-42 -86 -62.5 -174t-20.5 -188t20.5 -188t62.5 -174l-68 -33q-54 75 -81.5 176t-27.5 219t27.5 219t81.5 176z" />
+    <glyph glyph-name="parenright" unicode=")" horiz-adv-x="247" 
+d="M98 708q54 -75 81.5 -176t27.5 -219t-27.5 -219t-81.5 -176l-68 33q42 86 62.5 174t20.5 188t-20.5 188t-62.5 174z" />
+    <glyph glyph-name="asterisk" unicode="*" horiz-adv-x="309" 
+d="M186 670q14 -3 29 -11t26 -21l-42 -70l81 -12q0 -16 -4 -32t-12 -27l-73 14l10 -84q-8 -3 -18 -4.5t-19 -1.5q-14 0 -24 3l-10 82l-75 -39q-13 17 -18 32t-5 26l71 31l-59 59q14 24 47 39l56 -58z" />
+    <glyph glyph-name="plus" unicode="+" horiz-adv-x="422" 
+d="M252 497v-130h130v-76h-130v-130h-82v130h-130v76h130v130h82z" />
+    <glyph glyph-name="comma" unicode="," horiz-adv-x="231" 
+d="M42 -70q31 12 48 29.5t17 40.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="hyphen" unicode="-" horiz-adv-x="381" 
+d="M341 303v-76h-301v76h301z" />
+    <glyph glyph-name="period" unicode="." horiz-adv-x="233" 
+d="M116 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="slash" unicode="/" horiz-adv-x="302" 
+d="M243 686l77 -25l-243 -697l-75 25z" />
+    <glyph glyph-name="zero" unicode="0" horiz-adv-x="720" 
+d="M680 325q0 -75 -24 -137t-66.5 -107t-101 -69.5t-128.5 -24.5t-128.5 24.5t-101 69.5t-66.5 107t-24 137t24 137t66.5 107t101 69.5t128.5 24.5t128.5 -24.5t101 -69.5t66.5 -107t24 -137zM136 325q0 -62 17 -110t47 -81t71 -50t89 -17t89 17t71 50t47 81t17 110t-17 110
+t-47 81t-71 50.5t-89 17.5t-89 -17.5t-71 -50.5t-47 -81t-17 -110z" />
+    <glyph glyph-name="one" unicode="1" horiz-adv-x="315" 
+d="M250 650v-650h-93v545l-103 -52l-25 69l154 89z" />
+    <glyph glyph-name="two" unicode="2" horiz-adv-x="548" 
+d="M70 76l224 219q51 50 79 88.5t28 85.5q0 56 -35 84t-91 28q-53 0 -93.5 -13.5t-80.5 -45.5l-45 71q53 39 104.5 54.5t119.5 15.5q48 0 86.5 -13t66.5 -38t43 -60.5t15 -80.5q0 -57 -33 -112t-90 -110l-169 -160v-4h299v-85h-428v76z" />
+    <glyph glyph-name="three" unicode="3" horiz-adv-x="545" 
+d="M263 395q48 0 76 27.5t28 69.5q0 46 -32.5 69.5t-77.5 23.5q-50 0 -85.5 -11.5t-72.5 -41.5l-41 65q47 37 94.5 51.5t107.5 14.5q44 0 80.5 -12t63 -33.5t41 -51t14.5 -65.5q0 -40 -18.5 -76.5t-59.5 -54.5v-4q51 -18 82.5 -59.5t31.5 -104.5q0 -44 -16.5 -83t-47 -68.5
+t-75.5 -46.5t-102 -17q-75 0 -128 18t-96 59l44 77q44 -41 83.5 -57t90.5 -16q33 0 61.5 9t49 26.5t32.5 42t12 54.5q0 63 -38.5 90.5t-96.5 27.5h-105v77h100z" />
+    <glyph glyph-name="four" unicode="4" horiz-adv-x="580" 
+d="M473 650v-425h74l-10 -83h-64v-142h-93v142h-345v68l327 446zM380 536l-7 1l-226 -312h233v311z" />
+    <glyph glyph-name="five" unicode="5" horiz-adv-x="559" 
+d="M469 650v-81h-297v-167q29 6 50.5 8.5t53.5 2.5q50 0 92 -14.5t72 -41.5t47 -65.5t17 -87.5q0 -45 -16.5 -84.5t-49 -69t-80.5 -46.5t-110 -17q-34 0 -62.5 3.5t-53.5 12t-48.5 21.5t-48.5 33l42 76q22 -18 41 -30.5t38.5 -20t41 -10.5t47.5 -3q87 0 126.5 36.5
+t39.5 98.5q0 69 -41.5 99t-119.5 30q-44 0 -79 -6.5t-75 -20.5l-13 5v339h386z" />
+    <glyph glyph-name="six" unicode="6" horiz-adv-x="561" 
+d="M448 582q-62 0 -118 -13.5t-99 -42t-68 -73t-25 -106.5q34 30 74.5 46t95.5 16q44 0 82 -14.5t66 -41.5t44 -66t16 -87q0 -58 -21 -98.5t-54.5 -66t-75.5 -37t-85 -11.5q-54 0 -97.5 19.5t-74 59.5t-47.5 100.5t-17 142.5q0 90 31.5 156.5t85.5 110t126 65t153 21.5z
+M140 272q0 -54 11 -93t31.5 -63.5t49 -36t63.5 -11.5q61 0 94 33t33 99q0 63 -32.5 97t-95.5 34q-51 0 -86.5 -15t-67.5 -44z" />
+    <glyph glyph-name="seven" unicode="7" horiz-adv-x="511" 
+d="M471 650v-58l-256 -592h-103l254 566h-321v84h426z" />
+    <glyph glyph-name="eight" unicode="8" horiz-adv-x="567" 
+d="M157 353q-32 17 -55 51.5t-23 79.5q0 40 15 73t42 56.5t64.5 36.5t83.5 13q41 0 78 -11.5t65 -33.5t44.5 -54t16.5 -73q0 -25 -7.5 -47t-18.5 -39.5t-25 -30.5t-27 -19v-4q52 -20 82 -61.5t30 -98.5q0 -49 -17 -87t-48 -64t-75 -39.5t-98 -13.5q-63 0 -108.5 15
+t-74.5 41.5t-42.5 63t-13.5 79.5q0 57 30.5 100.5t81.5 62.5v4zM284 383q56 0 83.5 28t27.5 75q0 43 -25.5 74t-85.5 31q-30 0 -51 -8.5t-34.5 -22.5t-20 -33t-6.5 -41q0 -21 7 -39.5t21 -32.5t35 -22.5t49 -8.5zM284 311q-68 0 -106.5 -32t-38.5 -93q0 -26 7.5 -48.5
+t25 -39.5t45 -26.5t67.5 -9.5q73 0 108 36t35 88q0 60 -36 92.5t-107 32.5z" />
+    <glyph glyph-name="nine" unicode="9" horiz-adv-x="561" 
+d="M102 68q65 0 123 14.5t101.5 44t69.5 72.5t28 100q-34 -30 -75.5 -44.5t-96.5 -14.5q-44 0 -82 14.5t-66 41.5t-44 66t-16 87q0 57 19.5 97.5t52 66t74 37.5t85.5 12q54 0 98.5 -18t76.5 -56.5t49.5 -99.5t17.5 -148q0 -90 -31 -156.5t-86 -110t-129 -65t-161 -21.5z
+M422 381q0 100 -39 152t-114 52q-63 0 -97 -33t-34 -101q0 -63 32.5 -96.5t95.5 -33.5q51 0 87.5 15t68.5 45z" />
+    <glyph glyph-name="colon" unicode=":" horiz-adv-x="233" 
+d="M116 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19zM116 472q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="semicolon" unicode=";" horiz-adv-x="233" 
+d="M42 -70q31 12 48 29.5t17 40.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5zM116 472q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z
+" />
+    <glyph glyph-name="less" unicode="&#x3c;" horiz-adv-x="332" 
+d="M292 456l-160 -133l160 -132l-58 -48l-199 161v38l199 161z" />
+    <glyph glyph-name="equal" unicode="=" horiz-adv-x="383" 
+d="M343 298v-74h-303v74h303zM343 440v-74h-303v74h303z" />
+    <glyph glyph-name="greater" unicode="&#x3e;" horiz-adv-x="332" 
+d="M292 342v-38l-199 -161l-58 47l160 133l-160 132l58 48z" />
+    <glyph glyph-name="question" unicode="?" horiz-adv-x="537" 
+d="M203 170q-14 28 -14 67q0 29 11 51t28.5 37.5t39 26.5t41.5 19q47 18 73 44.5t26 75.5q0 51 -39 82.5t-112 31.5q-51 0 -100 -20t-84 -56l-48 63q45 44 105.5 67t133.5 23q51 0 93.5 -12.5t73 -37.5t48 -63t17.5 -88q0 -46 -25.5 -87.5t-76.5 -67.5q-23 -12 -45.5 -21
+t-40 -20.5t-28.5 -28.5t-11 -45v-41h-66zM236 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="at" unicode="@" horiz-adv-x="769" 
+d="M496 136q-47 -59 -128 -59q-78 0 -121 48t-43 124q0 33 13 63.5t36.5 53.5t56.5 36.5t73 13.5q33 0 57.5 -8.5t47.5 -25.5l24 24l35 -7v-187q0 -30 12.5 -45t35.5 -15q30 0 43.5 30t13.5 81q0 57 -19.5 101.5t-53.5 75t-80 46.5t-99 16q-58 0 -108.5 -17t-88 -50t-59 -80
+t-21.5 -107q0 -59 19 -106t53 -79t81.5 -49t103.5 -17q64 0 116.5 11t95.5 35l24 -70q-53 -23 -110 -35.5t-126 -12.5t-131 20.5t-108.5 61t-73.5 101t-27 140.5q0 74 28 134.5t77 103t114.5 66t140.5 23.5q67 0 127.5 -21.5t106 -61.5t72.5 -98t27 -131q0 -81 -37.5 -127.5
+t-106.5 -46.5q-67 0 -93 47zM474 326q-19 15 -39.5 20t-46.5 5q-50 0 -77 -27.5t-27 -75.5t22.5 -74.5t67.5 -26.5q64 0 100 45v134z" />
+    <glyph glyph-name="A" unicode="A" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9z" />
+    <glyph glyph-name="B" unicode="B" horiz-adv-x="601" 
+d="M292 650q45 0 85.5 -9.5t70.5 -30.5t48 -54t18 -79q0 -38 -15.5 -69t-50.5 -51v-4q111 -56 111 -168q0 -45 -19 -79.5t-50.5 -58t-73.5 -35.5t-87 -12h-264v650h227zM159 77h161q28 0 54.5 5.5t46.5 18.5t32 34t12 52q0 60 -38 89.5t-99 29.5h-169v-229zM159 380h148
+q55 0 84.5 25.5t29.5 68.5q0 51 -32.5 75.5t-86.5 24.5h-143v-194z" />
+    <glyph glyph-name="C" unicode="C" horiz-adv-x="660" 
+d="M584 518q-48 35 -93.5 51t-99.5 16q-58 0 -105.5 -19.5t-81 -54t-51.5 -82t-18 -103.5q0 -63 20 -110.5t54.5 -80t81 -48.5t99.5 -16q30 0 56 3t50 10.5t48 19.5t50 30l28 -82q-54 -37 -110.5 -51t-131.5 -14q-65 0 -126.5 20.5t-109 62t-76 105.5t-28.5 151
+q0 75 28 137.5t76 106.5t112 68.5t135 24.5q73 0 126.5 -17t104.5 -55z" />
+    <glyph glyph-name="D" unicode="D" horiz-adv-x="700" 
+d="M306 650q82 0 147.5 -22.5t111.5 -64.5t70.5 -103t24.5 -139q0 -80 -28 -140t-77.5 -100t-118.5 -60.5t-150 -20.5h-221v650h241zM160 79h134q58 0 108 13t86 42t56.5 75t20.5 112q0 67 -18.5 114.5t-52.5 78t-82 44.5t-106 14h-146v-493z" />
+    <glyph glyph-name="E" unicode="E" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650z" />
+    <glyph glyph-name="F" unicode="F" horiz-adv-x="539" 
+d="M484 650v-77h-323v-199h290v-77h-290v-297h-96v650h419z" />
+    <glyph glyph-name="G" unicode="G" horiz-adv-x="700" 
+d="M645 347v-280q-57 -42 -123 -61t-145 -19q-69 0 -130.5 22t-107 64.5t-72.5 105.5t-27 146q0 75 27.5 137.5t75 107t110.5 69t135 24.5q73 0 132.5 -17t111.5 -55l-38 -73q-48 35 -100 50.5t-106 15.5q-58 0 -105 -19.5t-80 -54t-50.5 -82t-17.5 -103.5
+q0 -59 19.5 -106.5t53.5 -80.5t80 -51t99 -18q51 0 90 8.5t76 28.5v166h-170v75h262z" />
+    <glyph glyph-name="H" unicode="H" horiz-adv-x="669" 
+d="M604 650v-650h-95v303h-349v-303h-95v650h95v-270h349v270h95z" />
+    <glyph glyph-name="I" unicode="I" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95z" />
+    <glyph glyph-name="J" unicode="J" horiz-adv-x="244" 
+d="M179 650v-632q0 -67 -17 -113.5t-47.5 -77t-72.5 -47t-91 -23.5l-30 78q40 6 71 16.5t51.5 30.5t31 53.5t10.5 85.5v629h94z" />
+    <glyph glyph-name="K" unicode="K" horiz-adv-x="635" 
+d="M161 650v-341l313 341h119l-252 -265l279 -385h-113l-229 320l-117 -111v-209h-96v650h96z" />
+    <glyph glyph-name="L" unicode="L" horiz-adv-x="504" 
+d="M467 80v-80h-402v650h96v-570h306z" />
+    <glyph glyph-name="M" unicode="M" horiz-adv-x="761" 
+d="M182 650l198 -296l202 296h114v-650h-92v368l2 164h-9l-199 -291h-39l-197 291h-8l1 -164v-368h-90v650h117z" />
+    <glyph glyph-name="N" unicode="N" horiz-adv-x="734" 
+d="M164 650l417 -531l-3 170v361h91v-650h-102l-418 533l6 -198v-335h-90v650h99z" />
+    <glyph glyph-name="O" unicode="O" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110z" />
+    <glyph glyph-name="P" unicode="P" horiz-adv-x="594" 
+d="M291 650q55 0 102 -14t81.5 -40t54 -64t19.5 -87q0 -50 -19 -88t-53 -64t-81.5 -39.5t-103.5 -13.5h-131v-240h-95v650h226zM160 317h118q85 0 130 29t45 99q0 65 -45 96.5t-130 31.5h-118v-256z" />
+    <glyph glyph-name="Q" unicode="Q" horiz-adv-x="780" 
+d="M698 -119l-393 112q-57 16 -105.5 46t-84 72.5t-55.5 96t-20 117.5q0 75 27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137q0 -57 -17 -106.5t-47.5 -90t-72 -70.5t-90.5 -47l209 -44zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5
+t98 -17.5t98 17.5t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110z" />
+    <glyph glyph-name="R" unicode="R" horiz-adv-x="631" 
+d="M330 650q49 0 90.5 -12.5t71.5 -36.5t47 -58t17 -77q0 -58 -36 -104.5t-107 -65.5l188 -296h-111l-174 279h-156v-279h-95v650h265zM160 356h156q64 0 104.5 28t40.5 82t-39 80.5t-107 26.5h-155v-217z" />
+    <glyph glyph-name="S" unicode="S" horiz-adv-x="574" 
+d="M453 531q-41 29 -81 41.5t-91 12.5q-38 0 -62 -8.5t-38 -21.5t-19.5 -30t-5.5 -33q0 -36 23.5 -60t79.5 -35l93 -19q82 -17 130.5 -66t48.5 -132q0 -42 -17.5 -77t-50.5 -61t-80.5 -40.5t-107.5 -14.5q-36 0 -65.5 3t-56.5 10t-53.5 18t-55.5 27l32 87q56 -35 101 -48
+t96 -13q83 0 124.5 31t41.5 77q0 52 -27.5 78.5t-92.5 38.5l-102 19q-72 14 -111.5 62.5t-39.5 110.5q0 39 14 71t41.5 55t67.5 36t92 13q64 0 113 -12t99 -45z" />
+    <glyph glyph-name="T" unicode="T" horiz-adv-x="544" 
+d="M30 650h484v-76h-194v-574h-96v574h-194v76z" />
+    <glyph glyph-name="U" unicode="U" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95z" />
+    <glyph glyph-name="V" unicode="V" horiz-adv-x="679" 
+d="M282 0l-257 650h105l211 -562h4l208 562h101l-256 -650h-116z" />
+    <glyph glyph-name="W" unicode="W" horiz-adv-x="1009" 
+d="M231 0l-206 650h103l169 -555h4l162 555h85l171 -555h4l165 555h96l-205 -650h-128l-144 483h-4l-145 -483h-127z" />
+    <glyph glyph-name="X" unicode="X" horiz-adv-x="682" 
+d="M162 650l178 -257l182 257h107l-226 -307l254 -343h-115l-205 289l-204 -289h-108l249 343l-226 307h114z" />
+    <glyph glyph-name="Y" unicode="Y" horiz-adv-x="646" 
+d="M131 650l192 -313h4l190 313h104l-250 -401v-249h-94v247l-252 403h106z" />
+    <glyph glyph-name="Z" unicode="Z" horiz-adv-x="617" 
+d="M573 650v-68l-417 -503h425v-79h-544v65l420 508h-393v77h509z" />
+    <glyph glyph-name="bracketleft" unicode="[" horiz-adv-x="253" 
+d="M144 -18h74v-73h-163v826h163v-72h-74v-681z" />
+    <glyph glyph-name="backslash" unicode="\" horiz-adv-x="302" 
+d="M302 -11l-77 -25l-241 697l75 25z" />
+    <glyph glyph-name="bracketright" unicode="]" horiz-adv-x="253" 
+d="M129 663h-74v72h163v-826h-163v73h74v681z" />
+    <glyph glyph-name="asciicircum" unicode="^" horiz-adv-x="375" 
+d="M200 663l140 -150l-28 -63l-125 119l-125 -118l-27 62l141 150h24z" />
+    <glyph glyph-name="underscore" unicode="_" horiz-adv-x="363" 
+d="M363 -37v-71h-363v71h363z" />
+    <glyph glyph-name="grave" unicode="`" horiz-adv-x="267" 
+d="M217 563h-64l-103 136l73 47z" />
+    <glyph glyph-name="a" unicode="a" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106z" />
+    <glyph glyph-name="b" unicode="b" horiz-adv-x="619" 
+d="M158 730v-264q38 23 79.5 33.5t92.5 10.5q122 0 185.5 -64t63.5 -191q0 -60 -22.5 -109t-62 -83.5t-93.5 -53.5t-118 -19q-35 0 -64 3t-54.5 9.5t-49.5 16.5t-50 24v687h93zM158 95q17 -8 31.5 -13t28.5 -8t30 -4.5t36 -1.5q92 0 146.5 47.5t54.5 140.5q0 90 -39 134.5
+t-124 44.5q-48 0 -88.5 -11t-75.5 -36v-293z" />
+    <glyph glyph-name="c" unicode="c" horiz-adv-x="548" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19t102.5 -9t80.5 -29z" />
+    <glyph glyph-name="d" unicode="d" horiz-adv-x="619" 
+d="M554 730v-712q-30 -8 -57.5 -13.5t-56.5 -8.5t-57 -4.5t-70 -1.5q-70 0 -118.5 16.5t-83 49.5t-53 82t-18.5 114q0 58 19.5 106t54.5 81.5t83.5 52t106.5 18.5q52 0 87.5 -8t69.5 -30v258h93zM461 387q-63 48 -149 48q-38 0 -70.5 -11.5t-56.5 -34t-37.5 -56.5t-13.5 -80
+q0 -48 14 -83.5t39.5 -58.5t60 -34.5t75.5 -11.5q42 0 72.5 3.5t65.5 12.5v306z" />
+    <glyph glyph-name="e" unicode="e" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323z" />
+    <glyph glyph-name="f" unicode="f" horiz-adv-x="364" 
+d="M107 500v33q0 43 13.5 79t39 62.5t61.5 41t81 14.5q50 0 87 -10t74 -33l-39 -70q-26 17 -52 25t-58 8q-63 0 -88.5 -30.5t-25.5 -83.5v-36h153l-4 -77l-149 5v-428h-93v423l-87 12v65h87z" />
+    <glyph glyph-name="g" unicode="g" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q46 0 85 -4t81 -15q44 -50 44 -117q0 -55 -28 -90.5t-70.5 -56.5t-94 -29t-97.5 -8q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q25 0 59 4.5t64.5 16.5t51.5 33t21 54q0 14 -4 28t-12 26q-28 6 -51 7.5t-51 1.5
+h-120zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80z" />
+    <glyph glyph-name="h" unicode="h" horiz-adv-x="610" 
+d="M158 730v-302q35 44 82.5 63t98.5 19q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-56 0 -100.5 -22t-80.5 -66v-346h-93v730h93z" />
+    <glyph glyph-name="i" unicode="i" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM112 716q31 0 48 -18.5t17 -44.5q0 -25 -17 -44t-48 -19t-48 19t-17 44q0 26 17 44.5t48 18.5z" />
+    <glyph glyph-name="j" unicode="j" horiz-adv-x="233" 
+d="M168 500v-529q0 -101 -52 -156.5t-149 -57.5l-7 79q59 4 86.5 32t27.5 86v546h94zM121 716q31 0 48 -18.5t17 -44.5q0 -25 -17 -44t-48 -19t-48 19t-17 44q0 26 17 44.5t48 18.5z" />
+    <glyph glyph-name="k" unicode="k" horiz-adv-x="559" 
+d="M157 730v-438l80 53l174 155h109l2 -10l-217 -189l227 -293l-3 -8h-105l-190 255l-77 -43v-212h-92v730h92z" />
+    <glyph glyph-name="l" unicode="l" horiz-adv-x="240" 
+d="M157 730v-582q0 -37 15.5 -52.5t57.5 -15.5v-84q-6 -1 -12 -1.5t-12 -0.5q-40 0 -67 11.5t-43.5 32.5t-23.5 51.5t-7 68.5v572h92z" />
+    <glyph glyph-name="m" unicode="m" horiz-adv-x="961" 
+d="M146 500l6 -79q36 48 79.5 68.5t97.5 20.5q129 0 175 -90q36 43 84 66.5t106 23.5q104 0 153 -56.5t49 -162.5v-291h-93v292q0 33 -5 59t-17.5 44.5t-33.5 28.5t-53 10q-57 0 -95.5 -21.5t-72.5 -63.5q2 -14 3.5 -28t1.5 -30v-291h-93v292q0 33 -5 59t-17.5 44.5
+t-33.5 28.5t-53 10q-59 0 -97.5 -22t-73.5 -67v-345h-93v500h81z" />
+    <glyph glyph-name="n" unicode="n" horiz-adv-x="611" 
+d="M146 500l6 -79q36 48 84.5 68.5t102.5 20.5q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-59 0 -102.5 -22t-78.5 -67v-345h-93v500h81z" />
+    <glyph glyph-name="o" unicode="o" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137z" />
+    <glyph glyph-name="p" unicode="p" horiz-adv-x="609" 
+d="M65 481q29 7 55.5 12.5t53.5 9t56 5.5t62 2q135 0 206 -69t71 -192q0 -57 -16 -104.5t-47.5 -82t-80 -53.5t-112.5 -19q-51 0 -86 10.5t-69 36.5v-267h-93v711zM158 123q36 -31 73 -43t82 -12q76 0 119.5 47.5t43.5 133.5q0 94 -48.5 141t-135.5 47q-23 0 -40.5 -1
+t-33 -3.5t-30 -6t-30.5 -8.5v-295z" />
+    <glyph glyph-name="q" unicode="q" horiz-adv-x="614" 
+d="M456 -230v264q-38 -24 -78 -34t-91 -10q-122 0 -184.5 67t-62.5 194q0 60 23 108t62.5 81.5t92.5 51.5t113 18q65 0 115.5 -12.5t102.5 -40.5v-687h-93zM456 407q-16 8 -30 13t-28.5 8t-31 4.5t-36.5 1.5q-92 0 -144.5 -44.5t-52.5 -137.5q0 -88 38.5 -135t123.5 -47
+q48 0 87 10.5t74 33.5v293z" />
+    <glyph glyph-name="r" unicode="r" horiz-adv-x="491" 
+d="M146 500l6 -79q36 48 78 68.5t96 20.5q52 0 87 -15.5t67 -56.5l-63 -57q-21 26 -47 38.5t-54 12.5q-56 0 -91 -22t-67 -67v-343h-93v500h81z" />
+    <glyph glyph-name="s" unicode="s" horiz-adv-x="502" 
+d="M406 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -37 33.5 -50.5t99.5 -20.5q94 -10 145.5 -45.5t51.5 -112.5q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 19 -7 32t-23 21.5t-42 14t-65 9.5
+q-98 10 -146 43t-48 101q0 30 13 56.5t39.5 46t67 31t94.5 11.5q27 0 49.5 -1.5t43.5 -5t42 -9.5t44 -15z" />
+    <glyph glyph-name="t" unicode="t" horiz-adv-x="434" 
+d="M15 469l68 43l73 113l31 -5v-120h199l-3 -77l-196 9v-263q0 -57 26 -81t67 -24q33 0 59.5 9t54.5 26l30 -67q-31 -22 -67.5 -32t-86.5 -10q-86 0 -131.5 45t-45.5 133v255l-78 11v35z" />
+    <glyph glyph-name="u" unicode="u" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93z" />
+    <glyph glyph-name="v" unicode="v" horiz-adv-x="605" 
+d="M126 500l174 -421h4l180 421h96l-228 -500h-108l-219 500h101z" />
+    <glyph glyph-name="w" unicode="w" horiz-adv-x="862" 
+d="M25 500h96l134 -413h8l133 413h76l137 -413h8l127 413h93l-168 -500h-121l-112 344h-6l-113 -344h-124z" />
+    <glyph glyph-name="x" unicode="x" horiz-adv-x="588" 
+d="M155 500l135 -200h4l145 200h103l-176 -238l192 -262h-110l-154 220h-4l-156 -220h-104l193 261l-177 239h109z" />
+    <glyph glyph-name="y" unicode="y" horiz-adv-x="599" 
+d="M128 500l193 -402h4l149 402h100l-246 -596q-20 -48 -63 -75.5t-94 -27.5q-19 0 -33 3t-33 10l14 72q22 -5 39 -5q35 0 60.5 14.5t42.5 55.5l18 45l-254 504h103z" />
+    <glyph glyph-name="z" unicode="z" horiz-adv-x="543" 
+d="M495 500v-55l-335 -373h337v-72h-455v56l332 372h-321v72h442z" />
+    <glyph glyph-name="braceleft" unicode="{" horiz-adv-x="301" 
+d="M271 663h-33q-50 0 -50 -55v-185q0 -33 -13 -61t-38 -38v-4q23 -9 37 -36t14 -59v-189q0 -54 50 -54h33v-73h-42q-59 0 -94.5 33.5t-35.5 96.5v206q0 22 -11 31t-29 9h-29v74h29q18 0 29 9.5t11 30.5v206q0 63 35 96.5t95 33.5h42v-72z" />
+    <glyph glyph-name="bar" unicode="|" horiz-adv-x="237" 
+d="M162 686v-796h-87v796h87z" />
+    <glyph glyph-name="braceright" unicode="}" horiz-adv-x="301" 
+d="M30 -16h33q50 0 50 54v185q0 33 13 61t38 38v4q-23 9 -37 36t-14 59v189q0 55 -50 55h-33v72h42q59 0 94.5 -33.5t35.5 -96.5v-206q0 -22 11 -31t29 -9h29v-74h-29q-18 0 -29 -9.5t-11 -30.5v-206q0 -63 -35 -96.5t-95 -33.5h-42v73z" />
+    <glyph glyph-name="asciitilde" unicode="~" horiz-adv-x="445" 
+d="M30 335q23 25 51.5 37.5t67.5 12.5q26 0 44.5 -6t34.5 -13.5t32.5 -13.5t37.5 -6q23 0 39.5 7t33.5 24l41 -50q-26 -31 -51.5 -44.5t-63.5 -13.5q-22 0 -41.5 6.5t-37.5 15t-36.5 15t-38.5 6.5q-29 0 -49.5 -9.5t-35.5 -29.5z" />
+    <glyph glyph-name="exclamdown" unicode="&#xa1;" horiz-adv-x="225" 
+d="M65.0098 -28.959l12.999 508.97h68.9961l12.999 -508.97h-94.9941zM113.007 529.008q-31.998 0 -49.4971 18.998q-17.499 18.999 -17.499 46.9971q0 28.999 17.499 45.998t49.4971 16.999t48.9971 -16.999t16.999 -45.998q0 -27.998 -16.999 -46.9971
+q-16.999 -18.998 -48.9971 -18.998z" />
+    <glyph glyph-name="cent" unicode="&#xa2;" horiz-adv-x="548" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-38 -20 -76 -30t-81 -13v-75h-84v75q-54 6 -97.5 27.5t-74 55t-47 77.5t-16.5 95q0 52 18 96t49.5 77.5t74.5 55.5t93 30v71h84v-68
+q47 -2 82.5 -11t68.5 -26z" />
+    <glyph glyph-name="currency" unicode="&#xa4;" horiz-adv-x="448" 
+d="M137 410q37 22 86 22t86 -22l48 51l56 -54l-50 -47q12 -20 18.5 -39t6.5 -44q0 -24 -6.5 -43t-18.5 -39l49 -47l-55 -55l-47 52q-37 -23 -87 -23q-49 0 -87 22l-47 -52l-54 56l49 46q-12 20 -18.5 39t-6.5 44t6.5 44.5t18.5 39.5l-49 48l55 53zM311 277q0 38 -26.5 61.5
+t-61.5 23.5q-17 0 -33 -6t-28 -17.5t-19 -27t-7 -34.5t7.5 -35t19.5 -27t27.5 -17.5t32.5 -6.5t33 6.5t28 17.5t19.5 27t7.5 35z" />
+    <glyph glyph-name="yen" unicode="&#xa5;" horiz-adv-x="630" 
+d="M130 595l183 -291h7l182 291h103l-193 -287h71v-65h-332v65h69l-195 287h105zM483 206v-65h-120v-141h-94v141h-118v65h332z" />
+    <glyph glyph-name="brokenbar" unicode="&#xa6;" horiz-adv-x="237" 
+d="M162 686v-363h-87v363h87zM162 263v-373h-87v373h87z" />
+    <glyph glyph-name="section" unicode="&#xa7;" horiz-adv-x="516" 
+d="M388 579q-30 20 -59.5 28t-64.5 8q-46 0 -71.5 -14t-25.5 -50q0 -17 7.5 -27.5t20 -17.5t28 -11t31.5 -8l63 -15q67 -16 110.5 -51t43.5 -97q0 -40 -20 -74.5t-57 -54.5q23 -19 34 -45.5t11 -56.5q0 -38 -18 -63.5t-46.5 -42t-64 -23.5t-69.5 -7q-51 0 -97 9.5t-85 29.5
+l25 78q42 -25 79 -32.5t77 -7.5q22 0 42 2.5t35.5 9.5t24.5 20t9 33q0 32 -23 46t-61 23l-81 20q-74 18 -107.5 56t-33.5 90q0 82 80 127q-23 17 -34.5 37.5t-11.5 51.5q0 35 16 61.5t42 44t58.5 26t66.5 8.5q49 0 88.5 -10t79.5 -33zM184 419q-28 -15 -40.5 -31.5
+t-12.5 -43.5q0 -36 23.5 -52t71.5 -28l109 -27q24 11 37 32t13 45q0 19 -6.5 32t-18.5 22t-29.5 15.5t-40.5 11.5z" />
+    <glyph glyph-name="dieresis" unicode="&#xa8;" horiz-adv-x="406" 
+d="M91 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17zM314 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="copyright" unicode="&#xa9;" horiz-adv-x="761" 
+d="M477 393q-18 13 -39.5 18.5t-45.5 5.5q-53 0 -81.5 -27.5t-28.5 -77.5q0 -25 8.5 -44.5t23.5 -33t34.5 -20.5t40.5 -7q30 0 53.5 6.5t49.5 23.5l23 -62q-60 -36 -126 -36q-42 0 -76.5 12.5t-59.5 35t-38.5 54.5t-13.5 71q0 41 15.5 73t42 54t61.5 33.5t75 11.5
+q65 0 118 -34zM721 313q0 -73 -26 -133t-71.5 -103t-107.5 -66.5t-134 -23.5t-134 23t-108.5 65.5t-73 102.5t-26.5 135t26.5 135t73 102.5t108.5 65.5t134 23t134 -23.5t107.5 -66.5t71.5 -103t26 -133zM125 313q0 -64 20.5 -112t56 -79.5t82 -47.5t98.5 -16t98.5 16
+t82 47.5t56.5 79.5t21 112t-21 112t-56.5 79.5t-82 47.5t-98.5 16t-98.5 -16t-82 -47.5t-56 -79.5t-20.5 -112z" />
+    <glyph glyph-name="ordfeminine" unicode="&#xaa;" horiz-adv-x="395" 
+d="M49 634q64 29 136 29q70 0 108 -26t38 -85v-128q0 -23 7.5 -31t31.5 -8v-47q-20 -7 -47 -7q-20 0 -32.5 13.5t-12.5 39.5q-27 -26 -60 -39.5t-76 -13.5q-57 0 -84.5 26.5t-27.5 71.5q0 44 35 79q22 7 43 12t44 8t50.5 4.5t61.5 1.5v16q0 33 -17 43.5t-64 10.5
+q-30 0 -57.5 -6t-57.5 -20zM264 483q-28 0 -49 -1t-38.5 -3t-33 -5t-30.5 -8q-12 -13 -12 -34q0 -22 14 -31.5t38 -9.5q35 0 61 10t50 31v51z" />
+    <glyph glyph-name="guillemotleft" unicode="&#xab;" horiz-adv-x="459" 
+d="M252 358l-112 -99l112 -105l-38 -58l-174 141v38l174 141zM424 368l-112 -109l112 -115l-38 -58l-174 154v38l174 148z" />
+    <glyph glyph-name="logicalnot" unicode="&#xac;" horiz-adv-x="390" 
+d="M350 372l-7 -180h-68l-8 104h-227v76h310z" />
+    <glyph glyph-name="uni00AD" unicode="&#xad;" horiz-adv-x="381" 
+d="M341 303v-76h-301v76h301z" />
+    <glyph glyph-name="registered" unicode="&#xae;" horiz-adv-x="437" 
+d="M218 291q-39 0 -72 13.5t-58 37t-39 56.5t-14 72q0 38 14 71t38.5 57t58 38t72.5 14q38 0 71.5 -13.5t58 -37.5t39 -57t14.5 -72t-14.5 -71.5t-39 -56.5t-58 -37.5t-71.5 -13.5zM350 470q0 63 -36 98.5t-96 35.5q-63 0 -97.5 -35.5t-34.5 -98.5q0 -61 34.5 -97.5
+t97.5 -36.5q60 0 96 36.5t36 97.5zM297 376h-45l-33 72h-28v-72h-42v192h70q29 0 51.5 -13.5t22.5 -46.5q0 -20 -8.5 -31.5t-25.5 -18.5zM191 485h28q31 0 31 23q0 10 -6.5 16.5t-18.5 6.5h-34v-46z" />
+    <glyph glyph-name="macron" unicode="&#xaf;" horiz-adv-x="392" 
+d="M358 642v-70h-314v70h314z" />
+    <glyph glyph-name="degree" unicode="&#xb0;" horiz-adv-x="369" 
+d="M334 521q0 -31 -11.5 -57t-31.5 -45t-47.5 -29.5t-59.5 -10.5t-59 10.5t-47 29.5t-31.5 45t-11.5 57t11.5 57t31.5 45t47 29.5t59 10.5t59.5 -10.5t47.5 -29.5t31.5 -45t11.5 -57zM110 521q0 -38 20.5 -56.5t53.5 -18.5q34 0 54.5 18.5t20.5 56.5t-20.5 56.5t-54.5 18.5
+q-33 0 -53.5 -18.5t-20.5 -56.5z" />
+    <glyph glyph-name="plusminus" unicode="&#xb1;" horiz-adv-x="422" 
+d="M252 572v-130h130v-76h-130v-130h-82v130h-130v76h130v130h82zM362 187v-76h-301v76h301z" />
+    <glyph glyph-name="twosuperior" unicode="&#xb2;" horiz-adv-x="332" 
+d="M296 375h-271v61l157 119q26 19 37.5 37t11.5 41q0 26 -17.5 41.5t-54.5 15.5q-30 0 -54 -10.5t-48 -33.5l-34 51q29 29 62.5 43t78.5 14q63 0 100.5 -32.5t37.5 -87.5q0 -33 -14 -62t-42 -49l-117 -86h167v-62z" />
+    <glyph glyph-name="threesuperior" unicode="&#xb3;" horiz-adv-x="357" 
+d="M262 582q60 -27 60 -90q0 -33 -13 -56.5t-35 -39t-50.5 -23t-59.5 -7.5q-25 0 -44 2t-35 7t-30.5 13t-29.5 20l24 55q29 -22 54 -30.5t57 -8.5q39 0 66 17t27 51q0 29 -16.5 40.5t-54.5 11.5h-83v58h74q29 0 44.5 9.5t15.5 35.5q0 47 -67 47q-54 0 -95 -31l-33 47
+q30 25 61 34.5t73 9.5q22 0 44.5 -6t41 -18t30.5 -30.5t12 -43.5q0 -27 -9 -44.5t-29 -29.5z" />
+    <glyph glyph-name="acute" unicode="&#xb4;" horiz-adv-x="264" 
+d="M148 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="mu" unicode="&#xb5;" horiz-adv-x="604" 
+d="M65 -229v729h92v-302q0 -66 24 -99t88 -33q59 0 100.5 22t76.5 67v345h93v-500h-71l-16 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-39 0 -67 9t-51 31l7 -129v-131z" />
+    <glyph glyph-name="paragraph" unicode="&#xb6;" horiz-adv-x="384" 
+d="M337 566l-74 8h-25q-121 0 -121 -74q0 -38 17 -56.5t56 -18.5q54 0 75 52l52 -4v-440q0 -98 -29.5 -161t-94.5 -106l-57 53q27 22 44.5 44t28 45.5t15 49t4.5 55.5v342q-21 -8 -50 -8q-69 0 -108.5 40.5t-39.5 106.5q0 36 16 65t42 49.5t60.5 31t72.5 10.5h128z" />
+    <glyph glyph-name="periodcentered" unicode="&#xb7;" horiz-adv-x="233" 
+d="M116 310q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="cedilla" unicode="&#xb8;" horiz-adv-x="236" 
+d="M133 10l-5 -54q32 0 50.5 -15.5t18.5 -44.5q0 -16 -6.5 -34.5t-23 -36t-44 -33t-69.5 -24.5l-12 64q38 3 62.5 16t24.5 39q0 25 -30 25h-36v94z" />
+    <glyph glyph-name="onesuperior" unicode="&#xb9;" horiz-adv-x="226" 
+d="M137 748l39 -5v-368h-71v284l-71 -19l-17 43z" />
+    <glyph glyph-name="ordmasculine" unicode="&#xba;" horiz-adv-x="408" 
+d="M378 497q0 -36 -13.5 -66.5t-36.5 -52.5t-55 -34.5t-69 -12.5t-69 12.5t-55 34.5t-36.5 52.5t-13.5 66.5t13.5 66.5t36.5 52.5t55 34.5t69 12.5t69 -12.5t55 -34.5t36.5 -52.5t13.5 -66.5zM105 497q0 -26 8.5 -44.5t22 -31.5t31.5 -19t37 -6t37 6t31.5 19t22 31.5
+t8.5 44.5q0 25 -8.5 44t-22 32t-31.5 19t-37 6t-37 -6t-31.5 -19t-22 -32t-8.5 -44z" />
+    <glyph glyph-name="guillemotright" unicode="&#xbb;" horiz-adv-x="459" 
+d="M424 275v-38l-174 -141l-38 58l112 99l-112 105l38 58zM252 272v-38l-174 -148l-38 58l112 109l-112 115l38 58z" />
+    <glyph glyph-name="questiondown" unicode="&#xbf;" horiz-adv-x="468" 
+d="M265.013 480.011q13.999 -27.999 13.999 -66.9961q0 -28.998 -11 -50.9971q-10.999 -21.999 -28.498 -37.498t-38.9971 -26.498q-21.499 -10.999 -41.498 -18.999q-46.9971 -17.999 -72.9951 -44.4971t-25.998 -75.4951q0 -50.9971 38.9971 -82.4951
+q38.998 -31.498 111.993 -31.498q50.9971 0 99.9941 19.999q48.9971 19.998 83.9951 55.9961l47.9971 -62.9961q-44.998 -43.9971 -105.494 -66.9961q-60.4961 -22.998 -133.492 -22.998q-50.9961 0 -93.4941 12.499q-42.4971 12.499 -72.9951 37.498
+q-30.498 24.998 -47.9971 62.9961q-17.499 37.9971 -17.499 87.9941q0 45.9971 25.498 87.4951q25.499 41.4971 76.4961 67.4951q22.998 12 45.4971 20.999q22.498 8.99902 39.9971 20.499q17.499 11.499 28.499 28.498q10.999 16.999 10.999 44.9971v40.998h65.9961z
+M232.015 529.008q-31.998 0 -49.4971 18.998q-17.499 18.999 -17.499 46.9971q0 28.999 17.499 45.998t49.4971 16.999t48.9971 -16.999t16.999 -45.998q0 -27.998 -16.999 -46.9971q-16.999 -18.998 -48.9971 -18.998z" />
+    <glyph glyph-name="Agrave" unicode="&#xc0;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM379 698h-69l-98 136l78 47z" />
+    <glyph glyph-name="Aacute" unicode="&#xc1;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM416 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="Acircumflex" unicode="&#xc2;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM353 768l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="Atilde" unicode="&#xc3;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM172 765q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6
+q-49 0 -83 -39z" />
+    <glyph glyph-name="Adieresis" unicode="&#xc4;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM243 828q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM462 828q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43
+q0 27 17 44t46 17z" />
+    <glyph glyph-name="Aring" unicode="&#xc5;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM353 674q-52 0 -84.5 31.5t-32.5 80.5q0 23 9 43.5t24.5 35.5t37 23.5t46.5 8.5t46.5 -8.5t37 -23.5t24.5 -35.5t9 -43.5q0 -50 -33 -81t-84 -31zM409 786q0 24 -16 39.5
+t-40 15.5t-40.5 -15.5t-16.5 -39.5t16.5 -40t40.5 -16t40 16t16 40z" />
+    <glyph glyph-name="AE" unicode="&#xc6;" horiz-adv-x="951" 
+d="M894 650v-79h-323v-194h290v-78h-290v-220h337v-79h-433v140h-272l-86 -140h-102l390 650h489zM245 219h230v345h-27z" />
+    <glyph glyph-name="Ccedilla" unicode="&#xc7;" horiz-adv-x="660" 
+d="M584 518q-48 35 -93.5 51t-99.5 16q-58 0 -105.5 -19.5t-81 -54t-51.5 -82t-18 -103.5q0 -63 20 -110.5t54.5 -80t81 -48.5t99.5 -16q63 0 115 17.5t105 57.5l12 -94q-25 -17 -50 -29t-52 -19.5t-56 -11.5t-62 -5l-4 -35q35 0 54 -18.5t19 -51.5q0 -28 -14.5 -49.5
+t-37 -37t-50 -25.5t-52.5 -14l-13 61q41 7 68 21.5t27 41.5t-36 27h-33l3 83q-59 6 -112 29.5t-93 65t-64 101.5t-24 140q0 75 28 137.5t76 106.5t112 68.5t135 24.5q73 0 126.5 -17t104.5 -55z" />
+    <glyph glyph-name="Egrave" unicode="&#xc8;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM324 698h-69l-98 136l78 47z" />
+    <glyph glyph-name="Eacute" unicode="&#xc9;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM329 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="Ecircumflex" unicode="&#xca;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM285 777l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="Edieresis" unicode="&#xcb;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM171 828q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM390 828q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="Igrave" unicode="&#xcc;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM149 698h-69l-98 136l78 47z" />
+    <glyph glyph-name="Iacute" unicode="&#xcd;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM169 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="Icircumflex" unicode="&#xce;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM113 768l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="Idieresis" unicode="&#xcf;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM3 828q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM222 828q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="Eth" unicode="&#xd0;" horiz-adv-x="706" 
+d="M312 650q82 0 147.5 -22.5t111.5 -64.5t70.5 -103t24.5 -139q0 -80 -28 -140t-77.5 -100t-118.5 -60.5t-150 -20.5h-221v294h-66v75h66v281h241zM166 369h163v-75h-163v-215h134q58 0 108 13t86 42t56.5 75t20.5 112q0 67 -18.5 114.5t-52.5 78t-82 44.5t-106 14h-146
+v-203z" />
+    <glyph glyph-name="Ntilde" unicode="&#xd1;" horiz-adv-x="734" 
+d="M164 650l417 -531l-3 170v361h91v-650h-102l-418 533l6 -198v-335h-90v650h99zM186 765q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6q-49 0 -83 -39z" />
+    <glyph glyph-name="Ograve" unicode="&#xd2;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM420 703h-69l-98 136l78 47z" />
+    <glyph glyph-name="Oacute" unicode="&#xd3;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM441 886l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="Ocircumflex" unicode="&#xd4;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM390 777l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="Otilde" unicode="&#xd5;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM209 765q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5
+t-34.5 6q-49 0 -83 -39z" />
+    <glyph glyph-name="Odieresis" unicode="&#xd6;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM280 828q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM499 828q29 0 46 -17t17 -44q0 -26 -17 -43
+t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="multiply" unicode="&#xd7;" horiz-adv-x="382" 
+d="M191 373l103 102l53 -53l-102 -103l102 -103l-53 -53l-103 102l-103 -102l-53 53l102 103l-102 103l53 53z" />
+    <glyph glyph-name="Oslash" unicode="&#xd8;" horiz-adv-x="780" 
+d="M683 627l-46 -57q48 -45 75.5 -107.5t27.5 -137.5t-27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-103 0 -184 46l-46 -57l-70 44l50 62q-47 45 -73.5 106.5t-26.5 136.5t27 137t74.5 107t111 69.5t137.5 24.5q50 0 95 -11.5t84 -32.5l42 52zM258 98q59 -32 132 -32
+q52 0 98 17.5t80.5 50.5t55 81t20.5 110q0 56 -16.5 100t-45.5 77zM517 553q-59 31 -127 31q-52 0 -98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110q0 -55 15.5 -98.5t43.5 -75.5z" />
+    <glyph glyph-name="Ugrave" unicode="&#xd9;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM382 698h-69l-98 136l78 47z" />
+    <glyph glyph-name="Uacute" unicode="&#xda;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM380 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="Ucircumflex" unicode="&#xdb;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM340 777l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="Udieresis" unicode="&#xdc;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM230 828q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17
+t-16.5 43q0 27 16.5 44t45.5 17zM449 828q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="Yacute" unicode="&#xdd;" horiz-adv-x="646" 
+d="M131 650l192 -313h4l190 313h104l-250 -401v-249h-94v247l-252 403h106zM376 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="Thorn" unicode="&#xde;" 
+d="M160 663v-112h128q55 0 101.5 -14t80 -40t52 -64t18.5 -87q0 -50 -18.5 -88t-51.5 -64t-79.5 -39.5t-102.5 -13.5h-128v-141h-95v663h95zM160 218h115q85 0 127.5 29t42.5 99q0 128 -170 128h-115v-256z" />
+    <glyph glyph-name="agrave" unicode="&#xe0;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM331 563h-64l-103 136l73 47z" />
+    <glyph glyph-name="aacute" unicode="&#xe1;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM352 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="acircumflex" unicode="&#xe2;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM287 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="atilde" unicode="&#xe3;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM112 625q43 48 114 48q25 0 41.5 -6t30.5 -13.5t28 -13.5t34 -6q21 0 36 7t32 24l41 -46q-23 -29 -48.5 -42.5
+t-59.5 -13.5q-23 0 -40.5 6.5t-34 14t-32.5 14t-34 6.5q-26 0 -45 -9.5t-35 -29.5z" />
+    <glyph glyph-name="adieresis" unicode="&#xe4;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM184 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43
+t44.5 17zM407 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="aring" unicode="&#xe5;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM294 549q-21 0 -40.5 6.5t-35 19.5t-24.5 32t-9 45q0 23 8.5 42t23.5 32.5t35 21t42 7.5q20 0 39.5 -7t35 -20
+t25 -32t9.5 -44q0 -26 -9 -45.5t-24 -32t-35 -19t-41 -6.5zM294 603q22 0 37 13t15 36q0 21 -14.5 34.5t-37.5 13.5q-21 0 -36.5 -13.5t-15.5 -34.5t14.5 -35t37.5 -14z" />
+    <glyph glyph-name="ae" unicode="&#xe6;" horiz-adv-x="945" 
+d="M79 466q51 23 101 33.5t112 10.5q78 0 124 -19t66 -63q36 39 82 60.5t109 21.5q60 0 104 -15.5t73 -43t43 -65.5t14 -83q0 -21 -2 -38.5t-7 -37.5h-393q3 -40 18 -70t39 -49.5t56 -29.5t68 -10q28 0 50 1.5t42 5.5t39.5 11.5t42.5 19.5l28 -77q-24 -12 -47 -19.5t-48 -12
+t-52.5 -6t-60.5 -1.5q-75 0 -125 22t-85 64q-54 -49 -115.5 -67.5t-130.5 -18.5q-88 0 -131.5 41t-43.5 112q0 34 10.5 61.5t36.5 56.5q68 23 147.5 33t173.5 10v29q0 52 -28 76t-101 24q-95 0 -189 -41zM417 238q-81 0 -142.5 -8t-110.5 -22q-11 -12 -15.5 -27t-4.5 -33
+q0 -80 97 -80q50 0 92 12.5t84 44.5v113zM818 299q-2 72 -39 104t-116 32q-30 0 -58 -8.5t-49.5 -25.5t-35.5 -42.5t-17 -59.5h315z" />
+    <glyph glyph-name="ccedilla" unicode="&#xe7;" horiz-adv-x="548" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-43 -23 -86.5 -33t-94.5 -12l-3 -34q32 0 50.5 -15.5t18.5 -44.5q0 -16 -6.5 -34.5t-23 -36t-44 -33t-69.5 -24.5l-12 64q38 3 62.5 16
+t24.5 39q0 25 -30 25h-36v81q-53 7 -95 29t-71 55.5t-45 76.5t-16 93q0 59 22.5 107.5t61.5 83t91 53.5t112 19t102.5 -9t80.5 -29z" />
+    <glyph glyph-name="egrave" unicode="&#xe8;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM344 563h-64l-103 136l73 47z" />
+    <glyph glyph-name="eacute" unicode="&#xe9;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM381 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="ecircumflex" unicode="&#xea;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM319 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="edieresis" unicode="&#xeb;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM205 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17zM428 681q29 0 45.5 -17
+t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="igrave" unicode="&#xec;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM152 563h-64l-103 136l73 47z" />
+    <glyph glyph-name="iacute" unicode="&#xed;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM172 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="icircumflex" unicode="&#xee;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM112 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="idieresis" unicode="&#xef;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM0 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17zM223 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="eth" unicode="&#xf0;" horiz-adv-x="607" 
+d="M231 603q-33 8 -61.5 10.5t-73.5 3.5l5 76q41 0 85 -5.5t89 -16.5l53 86l65 -29l-49 -79q46 -19 86.5 -50t71 -75t48 -102.5t17.5 -131.5q0 -66 -17.5 -126.5t-59.5 -103.5q-36 -38 -85.5 -54t-105.5 -16q-54 0 -101.5 16.5t-82.5 48.5t-55 78.5t-20 106.5q0 50 20 92
+t53.5 73t78.5 48t95 17q60 0 111 -22.5t80 -68.5h6q-9 31 -23 61t-35.5 56t-52 48t-71.5 38l-47 -73l-65 29zM469 230q0 36 -11.5 66t-33.5 52.5t-53.5 35t-71.5 12.5q-80 0 -123.5 -42t-43.5 -121t43.5 -124t123.5 -45q78 0 124 43.5t46 122.5z" />
+    <glyph glyph-name="ntilde" unicode="&#xf1;" horiz-adv-x="611" 
+d="M146 500l6 -79q36 48 84.5 68.5t102.5 20.5q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-59 0 -102.5 -22t-78.5 -67v-345h-93v500h81zM136 625q43 48 114 48q25 0 41.5 -6t30.5 -13.5t28 -13.5t34 -6q21 0 36 7t32 24l41 -46q-23 -29 -48.5 -42.5
+t-59.5 -13.5q-23 0 -40.5 6.5t-34 14t-32.5 14t-34 6.5q-26 0 -45 -9.5t-35 -29.5z" />
+    <glyph glyph-name="ograve" unicode="&#xf2;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM343 563h-64l-103 136
+l73 47z" />
+    <glyph glyph-name="oacute" unicode="&#xf3;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM382 746l73 -47
+l-103 -136h-64z" />
+    <glyph glyph-name="ocircumflex" unicode="&#xf4;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM319 622l-112 -77
+l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="otilde" unicode="&#xf5;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM141 625q43 48 114 48
+q25 0 41.5 -6t30.5 -13.5t28 -13.5t34 -6q21 0 36 7t32 24l41 -46q-23 -29 -48.5 -42.5t-59.5 -13.5q-23 0 -40.5 6.5t-34 14t-32.5 14t-34 6.5q-26 0 -45 -9.5t-35 -29.5z" />
+    <glyph glyph-name="odieresis" unicode="&#xf6;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM207 681
+q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17zM430 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="divide" unicode="&#xf7;" horiz-adv-x="381" 
+d="M190 237q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19zM340 367v-76h-300v76h300zM190 550q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="oslash" unicode="&#xf8;" horiz-adv-x="638" 
+d="M533 518l-37 -62q47 -34 74.5 -86t27.5 -120q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18q-49 0 -91 12l-29 -49l-75 25l32 55q-53 33 -84.5 88t-31.5 129q0 60 22 108t59.5 82t88.5 52t109 18q57 0 107 -18l30 51zM388 427q-16 5 -33 7.5t-36 2.5q-88 0 -138 -50
+t-50 -137q0 -100 65 -150zM267 68q12 -2 25 -3.5t27 -1.5q88 0 138 50t50 137q0 86 -51 137z" />
+    <glyph glyph-name="ugrave" unicode="&#xf9;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM335 563h-64l-103 136l73 47z" />
+    <glyph glyph-name="uacute" unicode="&#xfa;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM351 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="ucircumflex" unicode="&#xfb;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM302 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="udieresis" unicode="&#xfc;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM190 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5
+q0 26 16.5 43t44.5 17zM413 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="yacute" unicode="&#xfd;" horiz-adv-x="599" 
+d="M128 500l193 -402h4l149 402h100l-246 -596q-20 -48 -63 -75.5t-94 -27.5q-19 0 -33 3t-33 10l14 72q22 -5 39 -5q35 0 60.5 14.5t42.5 55.5l18 45l-254 504h103zM365 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="thorn" unicode="&#xfe;" horiz-adv-x="609" 
+d="M158 730v-238q31 9 63.5 13.5t70.5 4.5q135 0 206 -69t71 -192q0 -57 -16 -104.5t-47.5 -82t-80 -53.5t-112.5 -19q-26 0 -47.5 3t-39.5 9t-34.5 16t-33.5 24v-272h-93v960h93zM158 119q37 -31 73 -41t81 -10q76 0 120 47.5t44 133.5q0 94 -48 140.5t-135 46.5
+q-23 0 -41 -1t-33.5 -3.5t-30 -6t-30.5 -8.5v-298z" />
+    <glyph glyph-name="ydieresis" unicode="&#xff;" horiz-adv-x="599" 
+d="M128 500l193 -402h4l149 402h100l-246 -596q-20 -48 -63 -75.5t-94 -27.5q-19 0 -33 3t-33 10l14 72q22 -5 39 -5q35 0 60.5 14.5t42.5 55.5l18 45l-254 504h103zM197 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5
+q0 26 16.5 43t44.5 17zM420 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="Amacron" unicode="&#x100;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM516 784v-73h-326v73h326z" />
+    <glyph glyph-name="amacron" unicode="&#x101;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM450 642v-70h-314v70h314z" />
+    <glyph glyph-name="Abreve" unicode="&#x102;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM257 829q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="abreve" unicode="&#x103;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM230 679q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27t-51.5 82z
+" />
+    <glyph glyph-name="Aogonek" unicode="&#x104;" horiz-adv-x="707" 
+d="M404 650l278 -650q-32 -17 -43 -34.5t-11 -37.5q0 -17 10.5 -30.5t38.5 -13.5q7 0 14.5 1t16.5 4l-16 -65q-10 -2 -19.5 -3t-17.5 -1q-48 0 -76 24.5t-28 62.5q0 25 13.5 51.5t43.5 41.5h-23l-85 197h-300l-80 -197h-95l279 650h100zM230 275h240l-114 283h-11z" />
+    <glyph glyph-name="aogonek" unicode="&#x105;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-30 -15 -39.5 -31.5t-9.5 -35.5q0 -16 9 -26.5t35 -10.5q14 0 29 4l-17 -65q-9 -2 -17.5 -3t-16.5 -1q-51 0 -72.5 24t-21.5 59q0 22 11 42.5t34 36.5q-26 5 -42 25.5
+t-18 53.5q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51
+v106z" />
+    <glyph glyph-name="Cacute" unicode="&#x106;" horiz-adv-x="660" 
+d="M584 518q-48 35 -93.5 51t-99.5 16q-58 0 -105.5 -19.5t-81 -54t-51.5 -82t-18 -103.5q0 -63 20 -110.5t54.5 -80t81 -48.5t99.5 -16q30 0 56 3t50 10.5t48 19.5t50 30l28 -82q-54 -37 -110.5 -51t-131.5 -14q-65 0 -126.5 20.5t-109 62t-76 105.5t-28.5 151
+q0 75 28 137.5t76 106.5t112 68.5t135 24.5q73 0 126.5 -17t104.5 -55zM440 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="cacute" unicode="&#x107;" horiz-adv-x="548" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19t102.5 -9t80.5 -29z
+M385 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="Ccircumflex" unicode="&#x108;" horiz-adv-x="660" 
+d="M584 518q-48 35 -93.5 51t-99.5 16q-58 0 -105.5 -19.5t-81 -54t-51.5 -82t-18 -103.5q0 -63 20 -110.5t54.5 -80t81 -48.5t99.5 -16q30 0 56 3t50 10.5t48 19.5t50 30l28 -82q-54 -37 -110.5 -51t-131.5 -14q-65 0 -126.5 20.5t-109 62t-76 105.5t-28.5 151
+q0 75 28 137.5t76 106.5t112 68.5t135 24.5q73 0 126.5 -17t104.5 -55zM391 777l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="ccircumflex" unicode="&#x109;" horiz-adv-x="548" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19t102.5 -9t80.5 -29z
+M329 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="Cdotaccent" unicode="&#x10a;" horiz-adv-x="660" 
+d="M584 518q-48 35 -93.5 51t-99.5 16q-58 0 -105.5 -19.5t-81 -54t-51.5 -82t-18 -103.5q0 -63 20 -110.5t54.5 -80t81 -48.5t99.5 -16q30 0 56 3t50 10.5t48 19.5t50 30l28 -82q-54 -37 -110.5 -51t-131.5 -14q-65 0 -126.5 20.5t-109 62t-76 105.5t-28.5 151
+q0 75 28 137.5t76 106.5t112 68.5t135 24.5q73 0 126.5 -17t104.5 -55zM397 840q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="cdotaccent" unicode="&#x10b;" horiz-adv-x="548" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19t102.5 -9t80.5 -29z
+M336 685q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="Ccaron" unicode="&#x10c;" horiz-adv-x="660" 
+d="M584 518q-48 35 -93.5 51t-99.5 16q-58 0 -105.5 -19.5t-81 -54t-51.5 -82t-18 -103.5q0 -63 20 -110.5t54.5 -80t81 -48.5t99.5 -16q30 0 56 3t50 10.5t48 19.5t50 30l28 -82q-54 -37 -110.5 -51t-131.5 -14q-65 0 -126.5 20.5t-109 62t-76 105.5t-28.5 151
+q0 75 28 137.5t76 106.5t112 68.5t135 24.5q73 0 126.5 -17t104.5 -55zM395 778l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="ccaron" unicode="&#x10d;" horiz-adv-x="548" 
+d="M328 646l112 77l23 -48l-123 -125h-23l-124 125l23 48zM471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102
+q0 59 22.5 107.5t61.5 83t91 53.5t112 19t102.5 -9t80.5 -29z" />
+    <glyph glyph-name="Dcaron" unicode="&#x10e;" horiz-adv-x="700" 
+d="M306 650q82 0 147.5 -22.5t111.5 -64.5t70.5 -103t24.5 -139q0 -80 -28 -140t-77.5 -100t-118.5 -60.5t-150 -20.5h-221v650h241zM160 79h134q58 0 108 13t86 42t56.5 75t20.5 112q0 67 -18.5 114.5t-52.5 78t-82 44.5t-106 14h-146v-493zM312 778l132 86l21 -54
+l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="dcaron" unicode="&#x10f;" horiz-adv-x="712" 
+d="M554 730v-712q-30 -8 -57.5 -13.5t-56.5 -8.5t-57 -4.5t-70 -1.5q-70 0 -118.5 16.5t-83 49.5t-53 82t-18.5 114q0 58 19.5 106t54.5 81.5t83.5 52t106.5 18.5q52 0 87.5 -8t69.5 -30v258h93zM461 387q-63 48 -149 48q-38 0 -70.5 -11.5t-56.5 -34t-37.5 -56.5t-13.5 -80
+q0 -48 14 -83.5t39.5 -58.5t60 -34.5t75.5 -11.5q42 0 72.5 3.5t65.5 12.5v306zM607 549q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="Dcroat" unicode="&#x110;" horiz-adv-x="706" 
+d="M312 650q82 0 147.5 -22.5t111.5 -64.5t70.5 -103t24.5 -139q0 -80 -28 -140t-77.5 -100t-118.5 -60.5t-150 -20.5h-221v294h-66v75h66v281h241zM166 369h163v-75h-163v-215h134q58 0 108 13t86 42t56.5 75t20.5 112q0 67 -18.5 114.5t-52.5 78t-82 44.5t-106 14h-146
+v-203z" />
+    <glyph glyph-name="dcroat" unicode="&#x111;" horiz-adv-x="619" 
+d="M554 730v-85h72v-73h-72v-554q-30 -8 -57.5 -13.5t-56.5 -8.5t-62 -4.5t-75 -1.5q-60 0 -108.5 16.5t-83 49.5t-53 82t-18.5 114q0 58 19.5 106t54.5 81.5t83.5 52t106.5 18.5q27 0 48.5 -2.5t39.5 -7.5t34.5 -13t34.5 -20v105h-132v73h132v85h93zM461 390
+q-37 23 -70.5 34t-78.5 11q-38 0 -70.5 -11.5t-56.5 -34t-37.5 -56.5t-13.5 -80q0 -48 14 -83.5t39.5 -58.5t60 -34.5t75.5 -11.5q22 0 39 1t33 3.5t31.5 5.5t34.5 8v307z" />
+    <glyph glyph-name="Emacron" unicode="&#x112;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM441 784v-73h-326v73h326z" />
+    <glyph glyph-name="emacron" unicode="&#x113;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM466 642v-70h-314v70h314z" />
+    <glyph glyph-name="Ebreve" unicode="&#x114;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM187 829q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="ebreve" unicode="&#x115;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM242 669q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27t-51.5 82z" />
+    <glyph glyph-name="Edotaccent" unicode="&#x116;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM283 826q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="edotaccent" unicode="&#x117;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM314 689q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="Eogonek" unicode="&#x118;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79q-32 -17 -43 -34.5t-11 -37.5q0 -17 10.5 -30.5t38.5 -13.5q7 0 14.5 1t16.5 4l-16 -65q-10 -2 -19.5 -3t-17.5 -1q-48 0 -76 24.5t-28 62.5q0 25 13.5 51.5t43.5 41.5h-359v650z" />
+    <glyph glyph-name="eogonek" unicode="&#x119;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-20 -8 -34.5 -25.5t-14.5 -41.5q0 -16 9 -26.5t35 -10.5q14 0 29 4l-17 -65q-9 -2 -17.5 -3t-16.5 -1q-51 0 -72.5 24t-21.5 59q0 27 15 53q-41 -7 -100 -7q-68 0 -120 18t-87.5 52
+t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323z" />
+    <glyph glyph-name="Ecaron" unicode="&#x11a;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM285 778l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="ecaron" unicode="&#x11b;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM316 646l112 77l23 -48l-123 -125h-23l-124 125l23 48z" />
+    <glyph glyph-name="Gcircumflex" unicode="&#x11c;" horiz-adv-x="700" 
+d="M645 347v-280q-57 -42 -123 -61t-145 -19q-69 0 -130.5 22t-107 64.5t-72.5 105.5t-27 146q0 75 27.5 137.5t75 107t110.5 69t135 24.5q73 0 132.5 -17t111.5 -55l-38 -73q-48 35 -100 50.5t-106 15.5q-58 0 -105 -19.5t-80 -54t-50.5 -82t-17.5 -103.5
+q0 -59 19.5 -106.5t53.5 -80.5t80 -51t99 -18q51 0 90 8.5t76 28.5v166h-170v75h262zM382 777l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="gcircumflex" unicode="&#x11d;" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q46 0 85 -4t81 -15q44 -50 44 -117q0 -55 -28 -90.5t-70.5 -56.5t-94 -29t-97.5 -8q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q25 0 59 4.5t64.5 16.5t51.5 33t21 54q0 14 -4 28t-12 26q-28 6 -51 7.5t-51 1.5
+h-120zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80zM294 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="Gbreve" unicode="&#x11e;" horiz-adv-x="700" 
+d="M645 347v-280q-57 -42 -123 -61t-145 -19q-69 0 -130.5 22t-107 64.5t-72.5 105.5t-27 146q0 75 27.5 137.5t75 107t110.5 69t135 24.5q73 0 132.5 -17t111.5 -55l-38 -73q-48 35 -100 50.5t-106 15.5q-58 0 -105 -19.5t-80 -54t-50.5 -82t-17.5 -103.5
+q0 -59 19.5 -106.5t53.5 -80.5t80 -51t99 -18q51 0 90 8.5t76 28.5v166h-170v75h262zM288 839q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="gbreve" unicode="&#x11f;" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q46 0 85 -4t81 -15q44 -50 44 -117q0 -55 -28 -90.5t-70.5 -56.5t-94 -29t-97.5 -8q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q25 0 59 4.5t64.5 16.5t51.5 33t21 54q0 14 -4 28t-12 26q-28 6 -51 7.5t-51 1.5
+h-120zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80zM232 679q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27t-51.5 82z" />
+    <glyph glyph-name="Gdotaccent" unicode="&#x120;" horiz-adv-x="700" 
+d="M645 347v-280q-57 -42 -123 -61t-145 -19q-69 0 -130.5 22t-107 64.5t-72.5 105.5t-27 146q0 75 27.5 137.5t75 107t110.5 69t135 24.5q73 0 132.5 -17t111.5 -55l-38 -73q-48 35 -100 50.5t-106 15.5q-58 0 -105 -19.5t-80 -54t-50.5 -82t-17.5 -103.5
+q0 -59 19.5 -106.5t53.5 -80.5t80 -51t99 -18q51 0 90 8.5t76 28.5v166h-170v75h262zM393 840q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="gdotaccent" unicode="&#x121;" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q46 0 85 -4t81 -15q44 -50 44 -117q0 -55 -28 -90.5t-70.5 -56.5t-94 -29t-97.5 -8q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q25 0 59 4.5t64.5 16.5t51.5 33t21 54q0 14 -4 28t-12 26q-28 6 -51 7.5t-51 1.5
+h-120zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80zM287 685q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="Gcommaaccent" unicode="&#x122;" horiz-adv-x="700" 
+d="M645 347v-280q-57 -42 -123 -61t-145 -19q-69 0 -130.5 22t-107 64.5t-72.5 105.5t-27 146q0 75 27.5 137.5t75 107t110.5 69t135 24.5q73 0 132.5 -17t111.5 -55l-38 -73q-48 35 -100 50.5t-106 15.5q-58 0 -105 -19.5t-80 -54t-50.5 -82t-17.5 -103.5
+q0 -59 19.5 -106.5t53.5 -80.5t80 -51t99 -18q51 0 90 8.5t76 28.5v166h-170v75h262zM329 -229q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="gcommaaccent" unicode="&#x123;" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q46 0 85 -4t81 -15q44 -50 44 -117q0 -55 -28 -90.5t-70.5 -56.5t-94 -29t-97.5 -8q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q25 0 59 4.5t64.5 16.5t51.5 33t21 54q0 14 -4 28t-12 26q-28 6 -51 7.5t-51 1.5
+h-120zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80zM357.003 732.996q-29.998 -14 -45.9971 -30.498q-15.999 -16.499 -15.999 -39.498q29.998 0 43.4971 -16.499t13.499 -37.498
+q0 -9.99902 -3 -19.998q-2.99902 -10 -9.99902 -18.499q-7 -8.5 -18.499 -13.999q-11.499 -5.5 -28.498 -5.5q-34.998 0 -52.9971 25.498q-17.999 25.499 -17.999 62.4971q0 18.998 7 39.4971q6.99902 20.499 20.499 38.998q13.499 18.498 34.4971 33.9971
+q20.999 15.5 48.9971 24.499z" />
+    <glyph glyph-name="Hcircumflex" unicode="&#x124;" horiz-adv-x="669" 
+d="M604 650v-650h-95v303h-349v-303h-95v650h95v-270h349v270h95zM334 777l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="hcircumflex" unicode="&#x125;" horiz-adv-x="610" 
+d="M158 730v-302q35 44 82.5 63t98.5 19q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-56 0 -100.5 -22t-80.5 -66v-346h-93v730h93zM108 823l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="Hbar" unicode="&#x126;" horiz-adv-x="669" 
+d="M160 650v-102h349v102h95v-102h45v-70h-45v-478h-95v303h-349v-303h-95v478h-45v70h45v102h95zM160 380h349v98h-349v-98z" />
+    <glyph glyph-name="hbar" unicode="&#x127;" horiz-adv-x="621" 
+d="M169 730v-85h175v-73h-175v-144q35 44 82.5 63t98.5 19q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-56 0 -100.5 -22t-80.5 -66v-346h-93v572h-76v73h76v85h93z" />
+    <glyph glyph-name="Itilde" unicode="&#x128;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM-68 765q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6q-49 0 -83 -39z" />
+    <glyph glyph-name="itilde" unicode="&#x129;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM-65 625q43 48 114 48q25 0 41.5 -6t30.5 -13.5t28 -13.5t34 -6q21 0 36 7t32 24l41 -46q-23 -29 -48.5 -42.5t-59.5 -13.5q-23 0 -40.5 6.5t-34 14t-32.5 14t-34 6.5q-26 0 -45 -9.5t-35 -29.5z" />
+    <glyph glyph-name="uni012A" unicode="&#x12a;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM277 791v-73h-326v73h326z" />
+    <glyph glyph-name="imacron" unicode="&#x12b;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM269 642v-70h-314v70h314z" />
+    <glyph glyph-name="Ibreve" unicode="&#x12c;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM17 829q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="Iogonek" unicode="&#x12e;" horiz-adv-x="225" 
+d="M160 650v-650q-32 -17 -43 -34.5t-11 -37.5q0 -17 10.5 -30.5t38.5 -13.5q7 0 14.5 1t16.5 4l-16 -65q-10 -2 -19.5 -3t-17.5 -1q-48 0 -76 24.5t-28 62.5q0 25 13.5 51.5t43.5 41.5h-21v650h95z" />
+    <glyph glyph-name="iogonek" unicode="&#x12f;" horiz-adv-x="223" 
+d="M158 500v-500q-30 -15 -39.5 -31.5t-9.5 -35.5q0 -16 9 -26.5t35 -10.5q14 0 29 4l-17 -65q-9 -2 -17.5 -3t-16.5 -1q-51 0 -72.5 24t-21.5 59q0 23 12.5 47.5t39.5 38.5h-24v500h93zM112 706q31 0 48 -18.5t17 -44.5q0 -25 -17 -44t-48 -19t-48 19t-17 44q0 26 17 44.5
+t48 18.5z" />
+    <glyph glyph-name="Idotaccent" unicode="&#x130;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM116 841q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="dotlessi" unicode="&#x131;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93z" />
+    <glyph glyph-name="Jcircumflex" unicode="&#x134;" horiz-adv-x="244" 
+d="M179 650v-632q0 -67 -17 -113.5t-47.5 -77t-72.5 -47t-91 -23.5l-30 78q40 6 71 16.5t51.5 30.5t31 53.5t10.5 85.5v629h94zM134 769l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="jcircumflex" unicode="&#x135;" horiz-adv-x="233" 
+d="M168 500v-529q0 -101 -52 -156.5t-149 -57.5l-7 79q59 4 86.5 32t27.5 86v546h94zM122 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="Kcommaaccent" unicode="&#x136;" horiz-adv-x="634" 
+d="M161 650v-341l313 341h119l-252 -265l279 -385h-113l-229 320l-117 -111v-209h-96v650h96zM237 -170q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="kcommaaccent" unicode="&#x137;" horiz-adv-x="579" 
+d="M157 730v-438l80 53l174 155h109l2 -10l-217 -189l227 -293l-3 -8h-105l-190 255l-77 -43v-212h-92v730h92zM223 -199q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39
+t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="kgreenlandic" unicode="&#x138;" horiz-adv-x="579" 
+d="M158 500v-224l254 224h110l4 -9l-231 -194l264 -289l-2 -8h-111l-216 246l-72 -60v-186h-93v500h93z" />
+    <glyph glyph-name="Lacute" unicode="&#x139;" horiz-adv-x="504" 
+d="M467 80v-80h-402v650h96v-570h306zM167 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="lacute" unicode="&#x13a;" horiz-adv-x="240" 
+d="M157 730v-582q0 -37 15.5 -52.5t57.5 -15.5v-84q-6 -1 -12 -1.5t-12 -0.5q-40 0 -67 11.5t-43.5 32.5t-23.5 51.5t-7 68.5v572h92zM168 956l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="Lcommaaccent" unicode="&#x13b;" horiz-adv-x="504" 
+d="M467 80v-80h-402v650h96v-570h306zM192 -223q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="lcommaaccent" unicode="&#x13c;" horiz-adv-x="240" 
+d="M157 730v-582q0 -37 15.5 -52.5t57.5 -15.5v-84q-6 -1 -12 -1.5t-12 -0.5q-40 0 -67 11.5t-43.5 32.5t-23.5 51.5t-7 68.5v572h92zM78 -225q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5
+q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="Lcaron" unicode="&#x13d;" horiz-adv-x="504" 
+d="M467 80v-80h-402v650h96v-570h306zM290 472q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="lcaron" unicode="&#x13e;" horiz-adv-x="336" 
+d="M157 730v-582q0 -37 15.5 -52.5t57.5 -15.5v-84q-6 -1 -12 -1.5t-12 -0.5q-40 0 -67 11.5t-43.5 32.5t-23.5 51.5t-7 68.5v572h92zM215 556q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5
+q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="Ldot" unicode="&#x13f;" horiz-adv-x="504" 
+d="M467 80v-80h-402v650h96v-570h306zM351 417q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="ldot" unicode="&#x140;" horiz-adv-x="344" 
+d="M157 730v-582q0 -37 15.5 -52.5t57.5 -15.5v-84q-6 -1 -12 -1.5t-12 -0.5q-40 0 -67 11.5t-43.5 32.5t-23.5 51.5t-7 68.5v572h92zM270 397q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="Lslash" unicode="&#x141;" horiz-adv-x="521" 
+d="M484 80v-80h-402v267l-56 -22l-26 65l82 32v308h96v-270l114 45l26 -65l-140 -55v-225h306z" />
+    <glyph glyph-name="lslash" unicode="&#x142;" horiz-adv-x="278" 
+d="M157 730v-310l99 36l17 -69l-116 -42v-197q0 -37 15.5 -52.5t57.5 -15.5v-84q-6 -1 -12 -1.5t-12 -0.5q-40 0 -67 11.5t-43.5 32.5t-23.5 51.5t-7 68.5v154l-46 -16l-17 68l63 23v343h92z" />
+    <glyph glyph-name="Nacute" unicode="&#x143;" horiz-adv-x="734" 
+d="M164 650l417 -531l-3 170v361h91v-650h-102l-418 533l6 -198v-335h-90v650h99zM418 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="nacute" unicode="&#x144;" horiz-adv-x="611" 
+d="M146 500l6 -79q36 48 84.5 68.5t102.5 20.5q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-59 0 -102.5 -22t-78.5 -67v-345h-93v500h81zM399 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="Ncommaaccent" unicode="&#x145;" horiz-adv-x="734" 
+d="M164 650l417 -531l-3 170v361h91v-650h-102l-418 533l6 -198v-335h-90v650h99zM285 -177q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="ncommaaccent" unicode="&#x146;" horiz-adv-x="611" 
+d="M146 500l6 -79q36 48 84.5 68.5t102.5 20.5q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-59 0 -102.5 -22t-78.5 -67v-345h-93v500h81zM241 -183q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5
+q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="Ncaron" unicode="&#x147;" horiz-adv-x="734" 
+d="M164 650l417 -531l-3 170v361h91v-650h-102l-418 533l6 -198v-335h-90v650h99zM367 778l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="ncaron" unicode="&#x148;" horiz-adv-x="611" 
+d="M146 500l6 -79q36 48 84.5 68.5t102.5 20.5q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-59 0 -102.5 -22t-78.5 -67v-345h-93v500h81zM341 646l112 77l23 -48l-123 -125h-23l-124 125l23 48z" />
+    <glyph glyph-name="napostrophe" unicode="&#x149;" horiz-adv-x="693" 
+d="M228 500l6 -79q36 48 84.5 68.5t102.5 20.5q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-59 0 -102.5 -22t-78.5 -67v-345h-93v500h81zM16 558q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5
+q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="Omacron" unicode="&#x14c;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM553 784v-73h-326v73h326z" />
+    <glyph glyph-name="omacron" unicode="&#x14d;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM476 642v-70h-314v70
+h314z" />
+    <glyph glyph-name="Obreve" unicode="&#x14e;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM294 839q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="obreve" unicode="&#x14f;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM251 679
+q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27t-51.5 82z" />
+    <glyph glyph-name="Ohungarumlaut" unicode="&#x150;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM345 893l82 -30l-69 -159h-64zM483 883l76 -47l-93 -132h-67z" />
+    <glyph glyph-name="ohungarumlaut" unicode="&#x151;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM279 746l82 -30
+l-69 -153h-64zM417 736l76 -47l-93 -126h-67z" />
+    <glyph glyph-name="OE" unicode="&#x152;" horiz-adv-x="1070" 
+d="M1013 650v-79h-319v-194h286v-78h-286v-220h333v-79h-424v89q-42 -48 -100 -75t-133 -27q-74 0 -134.5 24.5t-104 69.5t-67.5 107t-24 137t24 137t67.5 107t104 69.5t134.5 24.5q72 0 130.5 -26.5t102.5 -74.5v88h410zM136 325q0 -62 17 -110t48 -81t74 -50.5t95 -17.5
+t95 17.5t74 50.5t48 81t17 110t-17 110t-48 81t-74 50.5t-95 17.5t-95 -17.5t-74 -50.5t-48 -81t-17 -110z" />
+    <glyph glyph-name="oe" unicode="&#x153;" horiz-adv-x="1008" 
+d="M569 227q3 -40 17.5 -70t38.5 -49.5t56 -29.5t68 -10q28 0 50 1.5t41.5 6t39 11.5t42.5 19l27 -76q-24 -12 -47 -20t-47.5 -12.5t-51.5 -6t-60 -1.5q-155 0 -221 104q-36 -52 -91 -78t-122 -26q-58 0 -107.5 18t-85.5 52t-56 82t-20 108t20 108t56 82t85.5 52t107.5 18
+q68 0 124 -24.5t90 -81.5q32 57 88 81.5t125 24.5q60 0 104 -15.5t73 -43t43 -65.5t14 -83q0 -21 -2 -38.5t-7 -37.5h-392zM131 250q0 -87 45 -137t133 -50t133 50t45 137t-45 137t-133 50t-133 -50t-45 -137zM878 299q-2 72 -37.5 104t-114.5 32q-30 0 -57.5 -8.5
+t-49 -25.5t-35.5 -42.5t-17 -59.5h311z" />
+    <glyph glyph-name="Racute" unicode="&#x154;" horiz-adv-x="636" 
+d="M330 650q49 0 90.5 -12.5t71.5 -36.5t47 -58t17 -77q0 -58 -36 -104.5t-107 -65.5l188 -296h-111l-174 279h-156v-279h-95v650h265zM160 356h156q64 0 104.5 28t40.5 82t-39 80.5t-107 26.5h-155v-217zM334 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="racute" unicode="&#x155;" horiz-adv-x="491" 
+d="M146 500l6 -79q36 48 78 68.5t96 20.5q52 0 87 -15.5t67 -56.5l-63 -57q-21 26 -47 38.5t-54 12.5q-56 0 -91 -22t-67 -67v-343h-93v500h81zM358 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="Rcommaaccent" unicode="&#x156;" horiz-adv-x="636" 
+d="M330 650q49 0 90.5 -12.5t71.5 -36.5t47 -58t17 -77q0 -58 -36 -104.5t-107 -65.5l188 -296h-111l-174 279h-156v-279h-95v650h265zM160 356h156q64 0 104.5 28t40.5 82t-39 80.5t-107 26.5h-155v-217zM249 -177q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20
+t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="rcommaaccent" unicode="&#x157;" horiz-adv-x="491" 
+d="M146 500l6 -79q36 48 78 68.5t96 20.5q52 0 87 -15.5t67 -56.5l-63 -57q-21 26 -47 38.5t-54 12.5q-56 0 -91 -22t-67 -67v-343h-93v500h81zM52 -227q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5
+q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="Rcaron" unicode="&#x158;" horiz-adv-x="636" 
+d="M330 650q49 0 90.5 -12.5t71.5 -36.5t47 -58t17 -77q0 -58 -36 -104.5t-107 -65.5l188 -296h-111l-174 279h-156v-279h-95v650h265zM160 356h156q64 0 104.5 28t40.5 82t-39 80.5t-107 26.5h-155v-217zM289 778l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="rcaron" unicode="&#x159;" horiz-adv-x="491" 
+d="M146 500l6 -79q36 48 78 68.5t96 20.5q52 0 87 -15.5t67 -56.5l-63 -57q-21 26 -47 38.5t-54 12.5q-56 0 -91 -22t-67 -67v-343h-93v500h81zM295 646l112 77l23 -48l-123 -125h-23l-124 125l23 48z" />
+    <glyph glyph-name="Sacute" unicode="&#x15a;" horiz-adv-x="574" 
+d="M453 531q-41 29 -81 41.5t-91 12.5q-38 0 -62 -8.5t-38 -21.5t-19.5 -30t-5.5 -33q0 -36 23.5 -60t79.5 -35l93 -19q82 -17 130.5 -66t48.5 -132q0 -42 -17.5 -77t-50.5 -61t-80.5 -40.5t-107.5 -14.5q-36 0 -65.5 3t-56.5 10t-53.5 18t-55.5 27l32 87q56 -35 101 -48
+t96 -13q83 0 124.5 31t41.5 77q0 52 -27.5 78.5t-92.5 38.5l-102 19q-72 14 -111.5 62.5t-39.5 110.5q0 39 14 71t41.5 55t67.5 36t92 13q64 0 113 -12t99 -45zM345 886l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="sacute" unicode="&#x15b;" horiz-adv-x="502" 
+d="M406 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -37 33.5 -50.5t99.5 -20.5q94 -10 145.5 -45.5t51.5 -112.5q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 19 -7 32t-23 21.5t-42 14t-65 9.5
+q-98 10 -146 43t-48 101q0 30 13 56.5t39.5 46t67 31t94.5 11.5q27 0 49.5 -1.5t43.5 -5t42 -9.5t44 -15zM319 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="Scircumflex" unicode="&#x15c;" horiz-adv-x="574" 
+d="M453 531q-41 29 -81 41.5t-91 12.5q-38 0 -62 -8.5t-38 -21.5t-19.5 -30t-5.5 -33q0 -36 23.5 -60t79.5 -35l93 -19q82 -17 130.5 -66t48.5 -132q0 -42 -17.5 -77t-50.5 -61t-80.5 -40.5t-107.5 -14.5q-36 0 -65.5 3t-56.5 10t-53.5 18t-55.5 27l32 87q56 -35 101 -48
+t96 -13q83 0 124.5 31t41.5 77q0 52 -27.5 78.5t-92.5 38.5l-102 19q-72 14 -111.5 62.5t-39.5 110.5q0 39 14 71t41.5 55t67.5 36t92 13q64 0 113 -12t99 -45zM293 777l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="scircumflex" unicode="&#x15d;" horiz-adv-x="502" 
+d="M406 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -37 33.5 -50.5t99.5 -20.5q94 -10 145.5 -45.5t51.5 -112.5q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 19 -7 32t-23 21.5t-42 14t-65 9.5
+q-98 10 -146 43t-48 101q0 30 13 56.5t39.5 46t67 31t94.5 11.5q27 0 49.5 -1.5t43.5 -5t42 -9.5t44 -15zM254 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="Scedilla" unicode="&#x15e;" horiz-adv-x="574" 
+d="M453 531q-41 29 -81 41.5t-91 12.5q-38 0 -62 -8.5t-38 -21.5t-19.5 -30t-5.5 -33q0 -36 23.5 -60t79.5 -35l93 -19q82 -17 130.5 -66t48.5 -132q0 -39 -15 -72.5t-44 -59t-71 -41t-95 -19.5l-4 -36q35 0 54 -18.5t19 -51.5q0 -28 -14.5 -49.5t-37 -37t-50 -25.5
+t-52.5 -14l-13 61q41 7 68 21.5t27 41.5t-36 27h-33l3 81q-28 1 -52 5t-47 11t-46 17t-48 24l32 87q56 -35 101 -48t96 -13q83 0 124.5 31t41.5 77q0 52 -27.5 78.5t-92.5 38.5l-102 19q-72 14 -111.5 62.5t-39.5 110.5q0 39 14 71t41.5 55t67.5 36t92 13q64 0 113 -12
+t99 -45z" />
+    <glyph glyph-name="scedilla" unicode="&#x15f;" horiz-adv-x="502" 
+d="M406 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -20 10 -32t27.5 -19.5t42 -11.5t53.5 -7q47 -5 84 -15.5t62 -29t38 -46.5t13 -68q0 -73 -56.5 -110t-153.5 -42l-3 -35q32 0 50.5 -15.5t18.5 -44.5q0 -16 -6.5 -34.5t-23 -36t-44 -33
+t-69.5 -24.5l-12 64q38 3 62.5 16t24.5 39q0 25 -30 25h-36v79q-40 2 -73.5 10t-68.5 23l23 76q74 -35 155 -35q155 0 155 79q0 20 -7 33t-23 21t-42 13t-65 9q-98 10 -146 42.5t-48 102.5q0 30 13 56.5t39.5 46t67 31t94.5 11.5q27 0 49.5 -1.5t43.5 -5t42 -9.5t44 -15z
+" />
+    <glyph glyph-name="Scaron" unicode="&#x160;" horiz-adv-x="574" 
+d="M453 531q-41 29 -81 41.5t-91 12.5q-38 0 -62 -8.5t-38 -21.5t-19.5 -30t-5.5 -33q0 -36 23.5 -60t79.5 -35l93 -19q82 -17 130.5 -66t48.5 -132q0 -42 -17.5 -77t-50.5 -61t-80.5 -40.5t-107.5 -14.5q-36 0 -65.5 3t-56.5 10t-53.5 18t-55.5 27l32 87q56 -35 101 -48
+t96 -13q83 0 124.5 31t41.5 77q0 52 -27.5 78.5t-92.5 38.5l-102 19q-72 14 -111.5 62.5t-39.5 110.5q0 39 14 71t41.5 55t67.5 36t92 13q64 0 113 -12t99 -45zM285 783l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="scaron" unicode="&#x161;" horiz-adv-x="502" 
+d="M406 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -37 33.5 -50.5t99.5 -20.5q94 -10 145.5 -45.5t51.5 -112.5q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 19 -7 32t-23 21.5t-42 14t-65 9.5
+q-98 10 -146 43t-48 101q0 30 13 56.5t39.5 46t67 31t94.5 11.5q27 0 49.5 -1.5t43.5 -5t42 -9.5t44 -15zM251 646l112 77l23 -48l-123 -125h-23l-124 125l23 48z" />
+    <glyph glyph-name="Tcommaaccent" unicode="&#x162;" horiz-adv-x="544" 
+d="M30 650h484v-76h-194v-574h-96v574h-194v76zM209 -223q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="tcommaaccent" unicode="&#x163;" horiz-adv-x="434" 
+d="M15 469l68 43l73 113l31 -5v-120h199l-3 -77l-196 9v-263q0 -57 26 -81t67 -24q33 0 59.5 9t54.5 26l30 -67q-31 -22 -67.5 -32t-86.5 -10q-86 0 -131.5 45t-45.5 133v255l-78 11v35zM198 -226q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5
+t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="Tcaron" unicode="&#x164;" horiz-adv-x="544" 
+d="M30 650h484v-76h-194v-574h-96v574h-194v76zM272 778l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="tcaron" unicode="&#x165;" horiz-adv-x="434" 
+d="M15 469l68 43l73 113l31 -5v-120h199l-3 -77l-196 9v-263q0 -57 26 -81t67 -24q33 0 59.5 9t54.5 26l30 -67q-31 -22 -67.5 -32t-86.5 -10q-86 0 -131.5 45t-45.5 133v255l-78 11v35zM270 592q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5
+t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="Utilde" unicode="&#x168;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM159 765q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6
+q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6q-49 0 -83 -39z" />
+    <glyph glyph-name="utilde" unicode="&#x169;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM124 625q43 48 114 48q25 0 41.5 -6t30.5 -13.5t28 -13.5t34 -6q21 0 36 7t32 24l41 -46
+q-23 -29 -48.5 -42.5t-59.5 -13.5q-23 0 -40.5 6.5t-34 14t-32.5 14t-34 6.5q-26 0 -45 -9.5t-35 -29.5z" />
+    <glyph glyph-name="Umacron" unicode="&#x16a;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM503 784v-73h-326v73h326z" />
+    <glyph glyph-name="umacron" unicode="&#x16b;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM459 642v-70h-314v70h314z" />
+    <glyph glyph-name="Ubreve" unicode="&#x16c;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM244 826q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26
+q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="ubreve" unicode="&#x16d;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM234 665q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27
+t-51.5 82z" />
+    <glyph glyph-name="Uring" unicode="&#x16e;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM340 670q-52 0 -84.5 31.5t-32.5 80.5q0 23 9 43.5t24.5 35.5t37 23.5t46.5 8.5
+t46.5 -8.5t37 -23.5t24.5 -35.5t9 -43.5q0 -50 -33 -81t-84 -31zM396 782q0 24 -16 39.5t-40 15.5t-40.5 -15.5t-16.5 -39.5t16.5 -40t40.5 -16t40 16t16 40z" />
+    <glyph glyph-name="uring" unicode="&#x16f;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM302 543q-21 0 -40.5 6.5t-35 19.5t-24.5 32t-9 45q0 23 8.5 42t23.5 32.5t35 21t42 7.5q20 0 39.5 -7
+t35 -20t25 -32t9.5 -44q0 -26 -9 -45.5t-24 -32t-35 -19t-41 -6.5zM302 597q22 0 37 13t15 36q0 21 -14.5 34.5t-37.5 13.5q-21 0 -36.5 -13.5t-15.5 -34.5t14.5 -35t37.5 -14z" />
+    <glyph glyph-name="Uhungarumlaut" unicode="&#x170;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM294 881l82 -30l-69 -159h-64zM432 871l76 -47l-93 -132h-67z" />
+    <glyph glyph-name="uhungarumlaut" unicode="&#x171;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM264 741l82 -30l-69 -153h-64zM402 731l76 -47l-93 -126h-67z" />
+    <glyph glyph-name="Uogonek" unicode="&#x172;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -129 -55.5 -198.5t-152.5 -86.5q-31 -16 -41 -33.5t-10 -37.5q0 -17 10.5 -30.5t38.5 -13.5q7 0 14.5 1t16.5 4l-16 -65q-10 -2 -19.5 -3t-17.5 -1q-48 0 -76 24.5t-28 62.5q0 22 11 45.5
+t34 40.5q-60 2 -107.5 20.5t-81.5 54.5t-52 90t-18 126v372h95z" />
+    <glyph glyph-name="uogonek" unicode="&#x173;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500q-30 -15 -39.5 -31.5t-9.5 -35.5q0 -16 9 -26.5t35 -10.5q14 0 29 4l-17 -65q-9 -2 -17.5 -3t-16.5 -1q-51 0 -72.5 24t-21.5 59q0 23 12.5 47.5t39.5 38.5h-12l-6 79q-36 -48 -82.5 -68.5
+t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93z" />
+    <glyph glyph-name="Wcircumflex" unicode="&#x174;" horiz-adv-x="1009" 
+d="M231 0l-206 650h103l169 -555h4l162 555h85l171 -555h4l165 555h96l-205 -650h-128l-144 483h-4l-145 -483h-127zM504 775l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="wcircumflex" unicode="&#x175;" horiz-adv-x="862" 
+d="M25 500h96l134 -413h8l133 413h76l137 -413h8l127 413h93l-168 -500h-121l-112 344h-6l-113 -344h-124zM433 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="Ycircumflex" unicode="&#x176;" horiz-adv-x="646" 
+d="M131 650l192 -313h4l190 313h104l-250 -401v-249h-94v247l-252 403h106zM323 771l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="ycircumflex" unicode="&#x177;" horiz-adv-x="599" 
+d="M128 500l193 -402h4l149 402h100l-246 -596q-20 -48 -63 -75.5t-94 -27.5q-19 0 -33 3t-33 10l14 72q22 -5 39 -5q35 0 60.5 14.5t42.5 55.5l18 45l-254 504h103zM309 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="Ydieresis" unicode="&#x178;" horiz-adv-x="646" 
+d="M131 650l192 -313h4l190 313h104l-250 -401v-249h-94v247l-252 403h106zM213 828q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM432 828q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17
+z" />
+    <glyph glyph-name="Zacute" unicode="&#x179;" horiz-adv-x="617" 
+d="M573 650v-68l-417 -503h425v-79h-544v65l420 508h-393v77h509zM364 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="zacute" unicode="&#x17a;" horiz-adv-x="543" 
+d="M495 500v-55l-335 -373h337v-72h-455v56l332 372h-321v72h442zM305 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="Zdotaccent" unicode="&#x17b;" horiz-adv-x="617" 
+d="M573 650v-68l-417 -503h425v-79h-544v65l420 508h-393v77h509zM313 830q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="zdotaccent" unicode="&#x17c;" horiz-adv-x="543" 
+d="M495 500v-55l-335 -373h337v-72h-455v56l332 372h-321v72h442zM266 680q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="Zcaron" unicode="&#x17d;" horiz-adv-x="617" 
+d="M573 650v-68l-417 -503h425v-79h-544v65l420 508h-393v77h509zM316 778l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="zcaron" unicode="&#x17e;" horiz-adv-x="543" 
+d="M495 500v-55l-335 -373h337v-72h-455v56l332 372h-321v72h442zM263 646l112 77l23 -48l-123 -125h-23l-124 125l23 48z" />
+    <glyph glyph-name="longs" unicode="&#x17f;" horiz-adv-x="260" 
+d="M65 533q0 43 13.5 79t39 62.5t61.5 41t81 14.5q50 0 87 -10t74 -33l-44 -73q-51 36 -109 36q-57 0 -83.5 -30.5t-26.5 -83.5v-536h-93v533z" />
+    <glyph glyph-name="florin" unicode="&#x192;" horiz-adv-x="367" 
+d="M110 500v33q0 43 13.5 79t39 62.5t61.5 41t81 14.5q50 0 87 -10t74 -33l-39 -71q-49 34 -110 34q-62 0 -87.5 -31t-25.5 -83v-36h152l-4 -77l-148 5v-397q0 -101 -52 -156.5t-149 -57.5l-7 79q59 4 86.5 32t27.5 86v409l-87 12v65h87z" />
+    <glyph glyph-name="uni0200" unicode="&#x200;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM420 698h-64l-69 159l82 30zM315 698h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni0201" unicode="&#x201;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM380 563h-64l-69 153l82 30zM275 563h-67l-93 126l76 47z" />
+    <glyph glyph-name="uni0202" unicode="&#x202;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9zM353 768l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni0203" unicode="&#x203;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106zM294 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="uni0204" unicode="&#x204;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM375 698h-64l-69 159l82 30zM270 698h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni0205" unicode="&#x205;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM392 563h-64l-69 153l82 30zM287 563h-67l-93 126l76 47z" />
+    <glyph glyph-name="uni0206" unicode="&#x206;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM275 782l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni0207" unicode="&#x207;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM307 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="uni0208" unicode="&#x208;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM206 698h-64l-69 159l82 30zM101 698h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni0209" unicode="&#x209;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM198 563h-64l-69 153l82 30zM93 563h-67l-93 126l76 47z" />
+    <glyph glyph-name="uni020A" unicode="&#x20a;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM113 768l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni020B" unicode="&#x20b;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM112 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="uni020C" unicode="&#x20c;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM467 708h-64l-69 159l82 30zM362 708h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni020D" unicode="&#x20d;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM402 563h-64l-69 153
+l82 30zM297 563h-67l-93 126l76 47z" />
+    <glyph glyph-name="uni020E" unicode="&#x20e;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110zM390 781l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni020F" unicode="&#x20f;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137zM319 622l-112 -77
+l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="uni0210" unicode="&#x210;" horiz-adv-x="636" 
+d="M330 650q49 0 90.5 -12.5t71.5 -36.5t47 -58t17 -77q0 -58 -36 -104.5t-107 -65.5l188 -296h-111l-174 279h-156v-279h-95v650h265zM160 356h156q64 0 104.5 28t40.5 82t-39 80.5t-107 26.5h-155v-217zM376 698h-64l-69 159l82 30zM271 698h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni0211" unicode="&#x211;" horiz-adv-x="491" 
+d="M146 500l6 -79q36 48 78 68.5t96 20.5q52 0 87 -15.5t67 -56.5l-63 -57q-21 26 -47 38.5t-54 12.5q-56 0 -91 -22t-67 -67v-343h-93v500h81zM385 563h-64l-69 153l82 30zM280 563h-67l-93 126l76 47z" />
+    <glyph glyph-name="uni0212" unicode="&#x212;" horiz-adv-x="636" 
+d="M330 650q49 0 90.5 -12.5t71.5 -36.5t47 -58t17 -77q0 -58 -36 -104.5t-107 -65.5l188 -296h-111l-174 279h-156v-279h-95v650h265zM160 356h156q64 0 104.5 28t40.5 82t-39 80.5t-107 26.5h-155v-217zM297 780l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni0213" unicode="&#x213;" horiz-adv-x="491" 
+d="M146 500l6 -79q36 48 78 68.5t96 20.5q52 0 87 -15.5t67 -56.5l-63 -57q-21 26 -47 38.5t-54 12.5q-56 0 -91 -22t-67 -67v-343h-93v500h81zM303 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="uni0214" unicode="&#x214;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM437 687h-64l-69 159l82 30zM332 687h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni0215" unicode="&#x215;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM390 557h-64l-69 153l82 30zM285 557h-67l-93 126l76 47z" />
+    <glyph glyph-name="uni0216" unicode="&#x216;" horiz-adv-x="680" 
+d="M160 650v-379q0 -104 47.5 -156t132.5 -52t132.5 52t47.5 156v379h95v-372q0 -75 -19.5 -129.5t-55 -90.5t-86.5 -53.5t-114 -17.5t-114 17.5t-86.5 53.5t-55 90.5t-19.5 129.5v372h95zM340 772l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni0217" unicode="&#x217;" horiz-adv-x="604" 
+d="M158 500v-292q0 -65 23.5 -103.5t87.5 -38.5q59 0 100.5 22t76.5 67v345h93v-500h-81l-6 79q-36 -48 -82.5 -68.5t-100.5 -20.5q-104 0 -154 56.5t-50 162.5v291h93zM302 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="Scommaaccent" unicode="&#x218;" horiz-adv-x="574" 
+d="M453 531q-41 29 -81 41.5t-91 12.5q-38 0 -62 -8.5t-38 -21.5t-19.5 -30t-5.5 -33q0 -36 23.5 -60t79.5 -35l93 -19q82 -17 130.5 -66t48.5 -132q0 -42 -17.5 -77t-50.5 -61t-80.5 -40.5t-107.5 -14.5q-36 0 -65.5 3t-56.5 10t-53.5 18t-55.5 27l32 87q56 -35 101 -48
+t96 -13q83 0 124.5 31t41.5 77q0 52 -27.5 78.5t-92.5 38.5l-102 19q-72 14 -111.5 62.5t-39.5 110.5q0 39 14 71t41.5 55t67.5 36t92 13q64 0 113 -12t99 -45zM221 -229q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5
+t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="scommaaccent" unicode="&#x219;" horiz-adv-x="502" 
+d="M406 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -37 33.5 -50.5t99.5 -20.5q94 -10 145.5 -45.5t51.5 -112.5q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 19 -7 32t-23 21.5t-42 14t-65 9.5
+q-98 10 -146 43t-48 101q0 30 13 56.5t39.5 46t67 31t94.5 11.5q27 0 49.5 -1.5t43.5 -5t42 -9.5t44 -15zM166 -224q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34
+t-49 -24.5z" />
+    <glyph glyph-name="uni021A" unicode="&#x21a;" horiz-adv-x="544" 
+d="M30 650h484v-76h-194v-574h-96v574h-194v76zM208 -219q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="uni021B" unicode="&#x21b;" horiz-adv-x="434" 
+d="M15 469l68 43l73 113l31 -5v-120h199l-3 -77l-196 9v-263q0 -57 26 -81t67 -24q33 0 59.5 9t54.5 26l30 -67q-31 -22 -67.5 -32t-86.5 -10q-86 0 -131.5 45t-45.5 133v255l-78 11v35zM206 -234q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5
+t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="dotlessj" unicode="&#x237;" horiz-adv-x="233" 
+d="M168 500v-529q0 -101 -52 -156.5t-149 -57.5l-7 79q59 4 86.5 32t27.5 86v546h94z" />
+    <glyph glyph-name="circumflex" unicode="&#x2c6;" horiz-adv-x="360" 
+d="M180 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="caron" unicode="&#x2c7;" horiz-adv-x="360" 
+d="M180 646l112 77l23 -48l-123 -125h-23l-124 125l23 48z" />
+    <glyph glyph-name="uni02C9" unicode="&#x2c9;" horiz-adv-x="363" 
+d="M363 650v-71h-363v71h363z" />
+    <glyph glyph-name="breve" unicode="&#x2d8;" horiz-adv-x="366" 
+d="M115 669q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27t-51.5 82z" />
+    <glyph glyph-name="dotaccent" unicode="&#x2d9;" horiz-adv-x="233" 
+d="M116 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="ring" unicode="&#x2da;" horiz-adv-x="300" 
+d="M150 549q-21 0 -40.5 6.5t-35 19.5t-24.5 32t-9 45q0 23 8.5 42t23.5 32.5t35 21t42 7.5q20 0 39.5 -7t35 -20t25 -32t9.5 -44q0 -26 -9 -45.5t-24 -32t-35 -19t-41 -6.5zM150 603q22 0 37 13t15 36q0 21 -14.5 34.5t-37.5 13.5q-21 0 -36.5 -13.5t-15.5 -34.5t14.5 -35
+t37.5 -14z" />
+    <glyph glyph-name="ogonek" unicode="&#x2db;" horiz-adv-x="218" 
+d="M121 11q19 -7 34 -11q-30 -15 -39.5 -31.5t-9.5 -35.5q0 -16 9 -26.5t35 -10.5q14 0 29 4l-17 -65q-9 -2 -17.5 -3t-16.5 -1q-51 0 -72.5 24t-21.5 59q0 14 5 30t15.5 29.5t27 24t39.5 13.5z" />
+    <glyph glyph-name="tilde" unicode="&#x2dc;" horiz-adv-x="421" 
+d="M30 625q43 48 114 48q25 0 41.5 -6t30.5 -13.5t28 -13.5t34 -6q21 0 36 7t32 24l41 -46q-23 -29 -48.5 -42.5t-59.5 -13.5q-23 0 -40.5 6.5t-34 14t-32.5 14t-34 6.5q-26 0 -45 -9.5t-35 -29.5z" />
+    <glyph glyph-name="hungarumlaut" unicode="&#x2dd;" horiz-adv-x="375" 
+d="M106 746l82 -30l-69 -153h-64zM244 736l76 -47l-93 -126h-67z" />
+    <glyph glyph-name="pi" unicode="&#x3c0;" horiz-adv-x="583" 
+d="M503 500v-378q0 -31 12.5 -42.5t49.5 -11.5l-7 -68q-28 -7 -51 -7q-31 0 -50 10t-29 27.5t-13.5 41.5t-3.5 52v303h-239v-427h-92v423l-72 13v64h495z" />
+    <glyph glyph-name="afii10023" unicode="&#x401;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650zM165 828q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM384 828q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="afii10051" unicode="&#x402;" horiz-adv-x="765" 
+d="M509 650v-76h-192v-186q35 42 82.5 62t98.5 20q104 0 158 -56.5t54 -162.5v-230q0 -98 -52.5 -162.5t-149.5 -81.5l-17 79q66 19 95.5 59.5t29.5 98.5v238q0 66 -27 104t-91 38q-56 0 -100.5 -22t-80.5 -66v-306h-94v574h-198v76h484z" />
+    <glyph glyph-name="afii10052" unicode="&#x403;" horiz-adv-x="516" 
+d="M477 650v-77h-316v-573h-96v650h412zM325 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="afii10053" unicode="&#x404;" horiz-adv-x="660" 
+d="M472 302h-336q4 -57 25 -100.5t55 -72.5t78.5 -43.5t95.5 -14.5q30 0 56 3t50 10.5t48 19.5t50 30l28 -82q-54 -37 -110.5 -51t-131.5 -14q-65 0 -126.5 20.5t-109 62t-76 105.5t-28.5 151q0 75 28 137.5t76 106.5t112 68.5t135 24.5q73 0 126.5 -17t104.5 -55l-38 -73
+q-48 35 -93.5 51t-99.5 16q-51 0 -93.5 -15t-75 -42t-54 -65t-29.5 -83h333v-78z" />
+    <glyph glyph-name="afii10054" unicode="&#x405;" horiz-adv-x="574" 
+d="M453 531q-41 29 -81 41.5t-91 12.5q-38 0 -62 -8.5t-38 -21.5t-19.5 -30t-5.5 -33q0 -36 23.5 -60t79.5 -35l93 -19q82 -17 130.5 -66t48.5 -132q0 -42 -17.5 -77t-50.5 -61t-80.5 -40.5t-107.5 -14.5q-36 0 -65.5 3t-56.5 10t-53.5 18t-55.5 27l32 87q56 -35 101 -48
+t96 -13q83 0 124.5 31t41.5 77q0 52 -27.5 78.5t-92.5 38.5l-102 19q-72 14 -111.5 62.5t-39.5 110.5q0 39 14 71t41.5 55t67.5 36t92 13q64 0 113 -12t99 -45z" />
+    <glyph glyph-name="uni0406" unicode="&#x406;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95z" />
+    <glyph glyph-name="afii10056" unicode="&#x407;" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM3 829q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM222 829q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="afii10057" unicode="&#x408;" horiz-adv-x="244" 
+d="M179 650v-632q0 -67 -17 -113.5t-47.5 -77t-72.5 -47t-91 -23.5l-30 78q40 6 71 16.5t51.5 30.5t31 53.5t10.5 85.5v629h94z" />
+    <glyph glyph-name="afii10058" unicode="&#x409;" horiz-adv-x="1051" 
+d="M635 650v-243h137q54 0 98.5 -12.5t77 -38t50.5 -63.5t18 -89q0 -50 -18 -88t-50 -63.5t-77 -39t-99 -13.5h-232v574h-238v-151q0 -223 -52 -328q-52 -103 -157 -103q-25 0 -44 3t-36 11l14 77q15 -5 28.5 -7t27.5 -2q54 0 84 62q43 90 43 327v187h425zM635 78h133
+q33 0 61 6.5t48.5 21.5t32 39t11.5 59t-11.5 59t-32 38.5t-49 21t-62.5 6.5h-131v-251z" />
+    <glyph glyph-name="afii10059" unicode="&#x40a;" horiz-adv-x="1000" 
+d="M160 650v-253h324v253h95v-253h142q54 0 98.5 -12t77 -36t50.5 -61.5t18 -88.5q0 -50 -18 -87t-50 -62t-77 -37.5t-99 -12.5h-237v320h-324v-320h-95v650h95zM579 78h138q33 0 61 6t48.5 20t32 37t11.5 58t-11.5 58.5t-32 37t-49 19t-62.5 5.5h-136v-241z" />
+    <glyph glyph-name="afii10060" unicode="&#x40b;" horiz-adv-x="765" 
+d="M509 650v-76h-192v-186q35 42 82.5 62t98.5 20q104 0 158 -56.5t54 -162.5v-251h-94v252q0 66 -27 104t-91 38q-56 0 -100.5 -22t-80.5 -66v-306h-94v574h-198v76h484z" />
+    <glyph glyph-name="afii10061" unicode="&#x40c;" horiz-adv-x="659" 
+d="M161 650v-239h139l104 167q25 40 58.5 61t73.5 21q35 0 69 -10l-14 -80q-17 5 -35 5q-26 0 -46 -10.5t-39 -40.5l-95 -149l244 -375h-109l-211 332h-139v-332h-96v650h96zM350 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="afii10062" unicode="&#x40e;" horiz-adv-x="682" 
+d="M132 650l227 -519h6l194 519h98l-237 -601q-17 -44 -36 -77.5t-41.5 -56t-50 -34.5t-62.5 -12q-28 0 -49.5 5.5t-42.5 18.5l22 75q16 -8 29 -11t27 -3q31 0 57 15.5t42 62.5l-290 618h107zM248 829q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79
+t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="afii10145" unicode="&#x40f;" horiz-adv-x="656" 
+d="M161 650v-572h334v572h96v-650h-208l-12 -141h-70l-12 141h-224v650h96z" />
+    <glyph glyph-name="afii10017" unicode="&#x410;" horiz-adv-x="707" 
+d="M406 650l278 -650h-99l-85 197h-299l-81 -197h-98l279 650h105zM232 275h238l-114 283h-9z" />
+    <glyph glyph-name="afii10018" unicode="&#x411;" horiz-adv-x="594" 
+d="M500 650v-76h-340v-174h138q56 0 102 -12.5t79 -37.5t51 -62.5t18 -87.5q0 -49 -18 -86t-50.5 -62.5t-78 -38.5t-100.5 -13h-236v650h435zM160 77h128q87 0 126 29t39 94q0 70 -41.5 96.5t-126.5 26.5h-125v-246z" />
+    <glyph glyph-name="afii10019" unicode="&#x412;" horiz-adv-x="601" 
+d="M292 650q45 0 85.5 -9.5t70.5 -30.5t48 -54t18 -79q0 -38 -15.5 -69t-50.5 -51v-4q111 -56 111 -168q0 -45 -19 -79.5t-50.5 -58t-73.5 -35.5t-87 -12h-264v650h227zM159 77h161q28 0 54.5 5.5t46.5 18.5t32 34t12 52q0 60 -38 89.5t-99 29.5h-169v-229zM159 380h148
+q55 0 84.5 25.5t29.5 68.5q0 51 -32.5 75.5t-86.5 24.5h-143v-194z" />
+    <glyph glyph-name="afii10020" unicode="&#x413;" horiz-adv-x="516" 
+d="M477 650v-77h-316v-573h-96v650h412z" />
+    <glyph glyph-name="afii10021" unicode="&#x414;" horiz-adv-x="731" 
+d="M630 650v-572h70l-8 -219h-74l-12 141h-485l-15 -141h-76v217q15 4 31.5 11t27.5 18q24 22 42 57t29 78.5t16.5 95t5.5 106.5v208h448zM274 573v-120q0 -129 -25 -228t-75 -147h362v495h-262z" />
+    <glyph glyph-name="afii10022" unicode="&#x415;" horiz-adv-x="541" 
+d="M65 650h419v-79h-323v-194h290v-78h-290v-220h337v-79h-433v650z" />
+    <glyph glyph-name="afii10024" unicode="&#x416;" horiz-adv-x="1011" 
+d="M552 650v-244h109l84 172q20 42 56 62t76 20q35 0 69 -10l-12 -80q-17 5 -35 5q-26 0 -49 -10t-38 -41l-75 -154l234 -370h-109l-203 330h-107v-330h-94v330h-107l-203 -330h-109l234 370l-75 154q-15 31 -38 41t-49 10q-18 0 -35 -5l-12 80q34 10 69 10q40 0 76 -20
+t56 -62l84 -172h109v244h94z" />
+    <glyph glyph-name="afii10025" unicode="&#x417;" horiz-adv-x="566" 
+d="M293 395q51 0 80 24t29 68q0 18 -7 36t-22.5 32t-40 23t-60.5 9q-27 0 -49 -3t-42 -10t-39 -19t-40 -29l-38 66q47 40 98 55.5t113 15.5q48 0 88.5 -13t69.5 -36t45 -54.5t16 -68.5q0 -39 -19.5 -72t-55.5 -49v-4q48 -21 80 -66t32 -102q0 -49 -19 -88t-53 -66.5t-81 -42
+t-102 -14.5q-37 0 -67 2.5t-57 9.5t-53 19t-54 30l31 84q25 -21 47 -33.5t44.5 -20t47 -10t53.5 -2.5q33 0 64 8t54 24t37 40.5t14 57.5q0 63 -35 93.5t-96 30.5h-130v75h117z" />
+    <glyph glyph-name="afii10026" unicode="&#x418;" horiz-adv-x="702" 
+d="M158 650v-522h4l386 522h89v-650h-93v503h-4l-376 -503h-99v650h93z" />
+    <glyph glyph-name="afii10027" unicode="&#x419;" horiz-adv-x="702" 
+d="M158 650v-522h4l386 522h89v-650h-93v503h-4l-376 -503h-99v650h93zM255 829q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="afii10028" unicode="&#x41a;" horiz-adv-x="659" 
+d="M161 650v-239h139l104 167q25 40 58.5 61t73.5 21q35 0 69 -10l-14 -80q-17 5 -35 5q-26 0 -46 -10.5t-39 -40.5l-95 -149l244 -375h-109l-211 332h-139v-332h-96v650h96z" />
+    <glyph glyph-name="afii10029" unicode="&#x41b;" horiz-adv-x="720" 
+d="M655 650v-650h-95v574h-258v-151q0 -223 -52 -328q-52 -103 -157 -103q-25 0 -44 3t-36 11l14 77q15 -5 28.5 -7t27.5 -2q54 0 84 62q43 90 43 327v187h445z" />
+    <glyph glyph-name="afii10030" unicode="&#x41c;" horiz-adv-x="761" 
+d="M182 650l198 -296l202 296h114v-650h-92v368l2 164h-9l-199 -291h-39l-197 291h-8l1 -164v-368h-90v650h117z" />
+    <glyph glyph-name="afii10031" unicode="&#x41d;" horiz-adv-x="669" 
+d="M604 650v-650h-95v303h-349v-303h-95v650h95v-270h349v270h95z" />
+    <glyph glyph-name="afii10032" unicode="&#x41e;" horiz-adv-x="780" 
+d="M740 325q0 -75 -27 -137t-74.5 -107t-111.5 -69.5t-137 -24.5q-74 0 -137.5 24.5t-111 69.5t-74.5 107t-27 137t27 137t74.5 107t111 69.5t137.5 24.5q73 0 137 -24.5t111.5 -69.5t74.5 -107t27 -137zM136 325q0 -62 20.5 -110t55 -81t80.5 -50.5t98 -17.5t98 17.5
+t80.5 50.5t55 81t20.5 110t-20.5 110t-55 81t-80.5 50.5t-98 17.5t-98 -17.5t-80.5 -50.5t-55 -81t-20.5 -110z" />
+    <glyph glyph-name="afii10033" unicode="&#x41f;" horiz-adv-x="650" 
+d="M585 650v-650h-94v573h-332v-573h-94v650h520z" />
+    <glyph glyph-name="afii10034" unicode="&#x420;" horiz-adv-x="594" 
+d="M291 650q55 0 102 -14t81.5 -40t54 -64t19.5 -87q0 -50 -19 -88t-53 -64t-81.5 -39.5t-103.5 -13.5h-131v-240h-95v650h226zM160 317h118q85 0 130 29t45 99q0 65 -45 96.5t-130 31.5h-118v-256z" />
+    <glyph glyph-name="afii10035" unicode="&#x421;" horiz-adv-x="660" 
+d="M584 518q-48 35 -93.5 51t-99.5 16q-58 0 -105.5 -19.5t-81 -54t-51.5 -82t-18 -103.5q0 -63 20 -110.5t54.5 -80t81 -48.5t99.5 -16q30 0 56 3t50 10.5t48 19.5t50 30l28 -82q-54 -37 -110.5 -51t-131.5 -14q-65 0 -126.5 20.5t-109 62t-76 105.5t-28.5 151
+q0 75 28 137.5t76 106.5t112 68.5t135 24.5q73 0 126.5 -17t104.5 -55z" />
+    <glyph glyph-name="afii10036" unicode="&#x422;" horiz-adv-x="544" 
+d="M30 650h484v-76h-194v-574h-96v574h-194v76z" />
+    <glyph glyph-name="afii10037" unicode="&#x423;" horiz-adv-x="682" 
+d="M132 650l227 -519h6l194 519h98l-237 -601q-17 -44 -36 -77.5t-41.5 -56t-50 -34.5t-62.5 -12q-28 0 -49.5 5.5t-42.5 18.5l22 75q16 -8 29 -11t27 -3q31 0 57 15.5t42 62.5l-290 618h107z" />
+    <glyph glyph-name="afii10038" unicode="&#x424;" horiz-adv-x="838" 
+d="M466 663v-92q37 6 79 6q61 0 108.5 -19t79.5 -52t48.5 -78t16.5 -97t-16.5 -97t-48.5 -78.5t-79.5 -52.5t-108.5 -19q-42 0 -79 6v-90h-94v90q-38 -6 -79 -6q-62 0 -109 19t-79 52.5t-48.5 78.5t-16.5 97t16.5 97t48.5 78t79 52t109 19q41 0 79 -6v92h94zM372 494
+q-20 4 -40.5 5t-40.5 1q-75 0 -116 -43.5t-41 -125.5t41 -125.5t116 -43.5q20 0 40.5 1t40.5 5v326zM466 168q20 -4 40.5 -5t40.5 -1q75 0 116 43.5t41 125.5t-41 125.5t-116 43.5q-20 0 -40.5 -1t-40.5 -5v-326z" />
+    <glyph glyph-name="afii10039" unicode="&#x425;" horiz-adv-x="682" 
+d="M162 650l178 -257l182 257h107l-226 -307l254 -343h-115l-205 289l-204 -289h-108l249 343l-226 307h114z" />
+    <glyph glyph-name="afii10040" unicode="&#x426;" horiz-adv-x="693" 
+d="M160 650v-573h335v573h95v-573h73l-8 -218h-75l-12 141h-503v650h95z" />
+    <glyph glyph-name="afii10041" unicode="&#x427;" horiz-adv-x="626" 
+d="M465 0v206q-72 -9 -135 -9q-65 0 -116.5 15t-88 46t-56 79.5t-19.5 115.5v197h96v-189q0 -46 11.5 -80t37.5 -56.5t68.5 -33.5t105.5 -11q24 0 48.5 1t47.5 3v366h96v-650h-96z" />
+    <glyph glyph-name="afii10042" unicode="&#x428;" horiz-adv-x="921" 
+d="M65 650h95v-573h253v573h95v-573h253v573h95v-650h-791v650z" />
+    <glyph glyph-name="afii10043" unicode="&#x429;" horiz-adv-x="959" 
+d="M65 650h95v-574h253v574h95v-574h253v574h95v-574h73l-9 -217h-75l-12 141h-768v650z" />
+    <glyph glyph-name="afii10044" unicode="&#x42a;" horiz-adv-x="681" 
+d="M240 650v-243h157q54 0 98.5 -12.5t77 -38t50.5 -63.5t18 -89q0 -50 -18 -88t-50 -63.5t-77 -39t-99 -13.5h-252v570l-130 8v72h225zM240 78h153q33 0 61 6.5t48.5 21.5t32 39t11.5 59t-11.5 59t-32 38.5t-49 21t-62.5 6.5h-151v-251z" />
+    <glyph glyph-name="afii10045" unicode="&#x42b;" horiz-adv-x="782" 
+d="M622 650h95v-650h-95v650zM160 650v-243h157q54 0 98.5 -12.5t77 -38t50.5 -63.5t18 -89q0 -50 -18 -88t-50 -63.5t-77 -39t-99 -13.5h-252v650h95zM160 78h153q33 0 61 6.5t48.5 21.5t32.5 39t12 59t-11.5 59t-32.5 38.5t-49.5 21t-62.5 6.5h-151v-251z" />
+    <glyph glyph-name="afii10046" unicode="&#x42c;" horiz-adv-x="601" 
+d="M160 650v-243h157q54 0 98.5 -12.5t77 -38t50.5 -63.5t18 -89q0 -50 -18 -88t-50 -63.5t-77 -39t-99 -13.5h-252v650h95zM160 78h153q33 0 61 6.5t48.5 21.5t32 39t11.5 59t-11.5 59t-32 38.5t-49 21t-62.5 6.5h-151v-251z" />
+    <glyph glyph-name="afii10047" unicode="&#x42d;" horiz-adv-x="664" 
+d="M527 380q-8 45 -27.5 83t-50.5 65t-72 42t-92 15q-54 0 -104.5 -16t-98.5 -51l-38 73q51 38 109.5 55t131.5 17q71 0 133.5 -24t108.5 -68t72.5 -106t26.5 -139q0 -83 -28 -146.5t-74.5 -106.5t-107 -64.5t-125.5 -21.5q-77 0 -134 15t-110 50l24 82q26 -18 50.5 -30
+t49 -19.5t51.5 -10.5t59 -3q51 0 94.5 14.5t76.5 43.5t53.5 72.5t24.5 100.5h-324v78h321z" />
+    <glyph glyph-name="afii10048" unicode="&#x42e;" horiz-adv-x="964" 
+d="M160 650v-269h107q9 63 36 115t69.5 89t99 57.5t122.5 20.5q73 0 134 -24.5t104.5 -69.5t67.5 -107t24 -137t-24 -137t-67.5 -107t-104.5 -69.5t-134 -24.5q-71 0 -130 23t-102 64t-68 99t-29 129h-105v-302h-95v650h95zM360 325q0 -62 17 -110t48 -81t74 -50.5t95 -17.5
+t95 17.5t74 50.5t48 81t17 110t-17 110t-48 81t-74 50.5t-95 17.5t-95 -17.5t-74 -50.5t-48 -81t-17 -110z" />
+    <glyph glyph-name="afii10049" unicode="&#x42f;" horiz-adv-x="625" 
+d="M560 0h-95v257h-150l-172 -257h-110l189 268q-72 18 -115 65t-43 120q0 46 18 82.5t50 62t77 39t99 13.5h252v-650zM312 572q-33 0 -61 -6t-48.5 -20.5t-32 -37t-11.5 -55.5q0 -32 11.5 -54.5t32 -36.5t49 -20.5t62.5 -6.5h151v237h-153z" />
+    <glyph glyph-name="afii10065" unicode="&#x430;" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4v28
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 15t84.5 51v106z" />
+    <glyph glyph-name="afii10066" unicode="&#x431;" horiz-adv-x="616" 
+d="M566 687q-23 -42 -61.5 -67t-91.5 -35l-122 -22q-75 -14 -110 -62t-47 -125h8q29 50 79 72t113 22q52 0 96 -16t76.5 -46.5t51 -75.5t18.5 -102q0 -56 -20 -100t-55 -75.5t-82.5 -48t-101.5 -16.5q-71 0 -122 22t-84 62t-49 96.5t-16 124.5q0 82 17.5 143t50 104
+t77.5 68.5t100 35.5l117 22q28 5 55 22.5t47 44.5zM143 232q0 -38 13 -68.5t35.5 -53t54 -34.5t69.5 -12q37 0 68.5 11.5t53.5 33.5t34.5 52.5t12.5 68.5q0 79 -43.5 122.5t-123.5 43.5q-40 0 -72 -12.5t-54.5 -34t-35 -51.5t-12.5 -66z" />
+    <glyph glyph-name="afii10067" unicode="&#x432;" horiz-adv-x="554" 
+d="M307 500q35 0 66.5 -8t55.5 -24.5t38 -41.5t14 -60q0 -33 -15.5 -55t-41.5 -36v-5q39 -11 63.5 -42.5t24.5 -76.5q0 -36 -13.5 -64t-38 -47.5t-57.5 -29.5t-72 -10h-266v500h242zM158 70h163q48 0 73.5 19t25.5 60q0 40 -24 57t-74 17h-164v-153zM158 294h159
+q32 0 52.5 17t20.5 50q0 32 -20.5 50t-56.5 18h-155v-135z" />
+    <glyph glyph-name="afii10068" unicode="&#x433;" horiz-adv-x="476" 
+d="M435 500v-77h-277v-423h-93v500h370z" />
+    <glyph glyph-name="afii10069" unicode="&#x434;" horiz-adv-x="632" 
+d="M540 500v-426h63l-11 -196h-70l-11 122h-397l-17 -122h-73v192q38 20 63.5 49.5t40.5 67.5t21.5 83.5t6.5 97.5v132h384zM242 334q0 -84 -21.5 -148t-72.5 -112h299v351h-205v-91z" />
+    <glyph glyph-name="afii10070" unicode="&#x435;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323z" />
+    <glyph glyph-name="afii10072" unicode="&#x436;" horiz-adv-x="898" 
+d="M494 500v-188h96l67 126q19 35 46.5 52.5t69.5 17.5q18 0 33 -3t26 -8l-15 -69q-17 4 -34 4q-20 0 -34 -11t-26 -33l-60 -109l190 -279h-104l-159 240h-96v-240h-90v240h-96l-159 -240h-104l190 279l-60 109q-12 22 -26 33t-34 11q-17 0 -34 -4l-15 69q11 5 26 8t33 3
+q42 0 69.5 -17.5t46.5 -52.5l67 -126h96v188h90z" />
+    <glyph glyph-name="afii10073" unicode="&#x437;" horiz-adv-x="531" 
+d="M50 469q24 11 47 19t47 13t50 7t58 2q36 0 71.5 -7t63.5 -23t45.5 -43t17.5 -66q0 -32 -13 -55t-45 -38v-4q47 -17 68.5 -45t21.5 -76q0 -35 -14.5 -65t-44.5 -51.5t-76 -34t-109 -12.5q-68 0 -118 11.5t-96 38.5l34 69q41 -23 82.5 -34t96.5 -11q29 0 56 4t48 14.5
+t33.5 27.5t12.5 44q0 42 -26.5 60.5t-75.5 18.5h-148v72h141q15 0 29.5 3t26 9.5t18.5 18.5t7 30q0 41 -31 56t-88 15q-50 0 -87 -9t-75 -28z" />
+    <glyph glyph-name="afii10074" unicode="&#x438;" horiz-adv-x="604" 
+d="M156 500v-254l-3 -128l298 382h88v-500h-91v197l2 179l-295 -376h-90v500h91z" />
+    <glyph glyph-name="afii10075" unicode="&#x439;" horiz-adv-x="604" 
+d="M156 500v-254l-3 -128l298 382h88v-500h-91v197l2 179l-295 -376h-90v500h91zM234 669q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27t-51.5 82z" />
+    <glyph glyph-name="afii10076" unicode="&#x43a;" horiz-adv-x="566" 
+d="M158 500v-185h124l70 123q22 38 52.5 54t72.5 16q34 0 55 -11l-16 -74q-19 4 -35 4q-24 0 -37.5 -11t-26.5 -32l-63 -107l181 -277h-108l-150 240h-119v-240h-93v500h93z" />
+    <glyph glyph-name="afii10077" unicode="&#x43b;" horiz-adv-x="611" 
+d="M546 500v-500h-92v426h-204v-80q0 -47 -2.5 -91.5t-9.5 -84t-21 -72.5t-36 -58q-20 -23 -46.5 -35.5t-64.5 -12.5q-21 0 -36 3t-31 11l12 70q18 -6 34 -6q24 0 38.5 4t27.5 18q30 32 38.5 99t8.5 165v144h384z" />
+    <glyph glyph-name="afii10078" unicode="&#x43c;" horiz-adv-x="724" 
+d="M179 500l183 -304l180 304h117v-500h-92v226l3 162h-5l-176 -288h-60l-174 288h-5l2 -135v-253h-87v500h114z" />
+    <glyph glyph-name="afii10079" unicode="&#x43d;" horiz-adv-x="592" 
+d="M157 500v-203h278v203h92v-500h-92v223h-278v-223h-92v500h92z" />
+    <glyph glyph-name="afii10080" unicode="&#x43e;" horiz-adv-x="638" 
+d="M598 250q0 -60 -22 -108t-59.5 -82t-88.5 -52t-109 -18t-109 18t-88.5 52t-59.5 82t-22 108t22 108t59.5 82t88.5 52t109 18t109 -18t88.5 -52t59.5 -82t22 -108zM131 250q0 -87 50 -137t138 -50t138 50t50 137t-50 137t-138 50t-138 -50t-50 -137z" />
+    <glyph glyph-name="afii10081" unicode="&#x43f;" horiz-adv-x="583" 
+d="M518 500v-500h-92v426h-269v-426h-92v500h453z" />
+    <glyph glyph-name="afii10082" unicode="&#x440;" horiz-adv-x="609" 
+d="M65 481q29 7 55.5 12.5t53.5 9t56 5.5t62 2q135 0 206 -69t71 -192q0 -57 -16 -104.5t-47.5 -82t-80 -53.5t-112.5 -19q-51 0 -86 10.5t-69 36.5v-267h-93v711zM158 123q36 -31 73 -43t82 -12q76 0 119.5 47.5t43.5 133.5q0 94 -48.5 141t-135.5 47q-23 0 -40.5 -1
+t-33 -3.5t-30 -6t-30.5 -8.5v-295z" />
+    <glyph glyph-name="afii10083" unicode="&#x441;" horiz-adv-x="548" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19t102.5 -9t80.5 -29z" />
+    <glyph glyph-name="afii10084" unicode="&#x442;" horiz-adv-x="519" 
+d="M35 427v73h449v-73h-178v-427h-93v427h-178z" />
+    <glyph glyph-name="afii10085" unicode="&#x443;" horiz-adv-x="599" 
+d="M579 500l-224 -582q-23 -60 -65 -90t-96 -30q-15 0 -27 1t-25 5l10 75q26 0 46 2.5t35.5 10t26.5 21.5t20 37l18 50h-72l-206 500h105l158 -423h43l152 423h101z" />
+    <glyph glyph-name="afii10086" unicode="&#x444;" horiz-adv-x="814" 
+d="M452 720v-220q16 4 32.5 7t32.5 3q57 0 104.5 -17t81.5 -50t52.5 -81.5t18.5 -111.5q0 -59 -20 -107t-55 -82t-81.5 -52.5t-100.5 -18.5q-16 0 -32.5 3t-32.5 7v-230h-90v230q-16 -4 -32.5 -7t-32.5 -3q-57 0 -104.5 17t-81.5 50t-52.5 81.5t-18.5 111.5q0 59 20 107
+t55 82t81.5 52.5t100.5 18.5q16 0 32.5 -3t32.5 -7v220h90zM452 73q29 -10 60 -10q77 0 123 48t46 139t-46 139t-123 48q-31 0 -60 -10v-354zM362 427q-29 10 -60 10q-77 0 -123 -48t-46 -139t46 -139t123 -48q31 0 60 10v354z" />
+    <glyph glyph-name="afii10087" unicode="&#x445;" horiz-adv-x="607" 
+d="M155 500l135 -200h4l145 200h103l-176 -238l192 -262h-110l-154 220h-4l-156 -220h-104l193 261l-177 239h109z" />
+    <glyph glyph-name="afii10088" unicode="&#x446;" horiz-adv-x="607" 
+d="M158 500v-426h263v426h93v-426h64l-7 -196h-70l-11 122h-425v500h93z" />
+    <glyph glyph-name="afii10089" unicode="&#x447;" horiz-adv-x="569" 
+d="M137 500v-147q0 -67 32.5 -98.5t100.5 -31.5q43 0 75 8.5t66 29.5v239h93v-500h-93v188q-66 -42 -150 -42q-103 0 -160 51t-57 152v151h93z" />
+    <glyph glyph-name="afii10090" unicode="&#x448;" horiz-adv-x="836" 
+d="M65 500h92v-426h215v426h92v-426h215v426h92v-500h-706v500z" />
+    <glyph glyph-name="afii10091" unicode="&#x449;" horiz-adv-x="852" 
+d="M157 500v-426h214v426h92v-426h214v426h92v-426h53l-8 -196h-71l-11 122h-667v500h92z" />
+    <glyph glyph-name="afii10092" unicode="&#x44a;" horiz-adv-x="629" 
+d="M239 500v-175q30 13 65.5 21t74.5 8q47 0 86 -11.5t66.5 -33.5t43 -55.5t15.5 -76.5q0 -47 -16.5 -80t-45.5 -55t-68.5 -32t-85.5 -10h-228v425l-117 6v69h210zM364 73q66 0 99.5 25.5t33.5 79.5q0 49 -29 75.5t-82 26.5q-26 0 -46 -1t-36.5 -3.5t-32 -7t-32.5 -10.5
+v-185h125z" />
+    <glyph glyph-name="afii10093" unicode="&#x44b;" horiz-adv-x="734" 
+d="M669 500v-500h-93v500h93zM158 500v-175q30 13 65.5 21t74.5 8q47 0 86 -11.5t66.5 -33.5t43 -55.5t15.5 -76.5q0 -47 -16.5 -80t-45.5 -55t-68.5 -32t-85.5 -10h-228v500h93zM283 73q66 0 99.5 25.5t33.5 79.5q0 49 -29 75.5t-82 26.5q-26 0 -46 -1t-36.5 -3.5t-32 -7
+t-32.5 -10.5v-185h125z" />
+    <glyph glyph-name="afii10094" unicode="&#x44c;" horiz-adv-x="549" 
+d="M158 500v-175q30 13 65.5 21t74.5 8q47 0 86 -11.5t66.5 -33.5t43 -55.5t15.5 -76.5q0 -47 -16.5 -80t-45.5 -55t-68.5 -32t-85.5 -10h-228v500h93zM283 73q66 0 99.5 25.5t33.5 79.5q0 49 -29 75.5t-82 26.5q-26 0 -46 -1t-36.5 -3.5t-32 -7t-32.5 -10.5v-185h125z" />
+    <glyph glyph-name="afii10095" unicode="&#x44d;" horiz-adv-x="548" 
+d="M415 299q-11 69 -56 103t-120 34q-44 0 -83 -8.5t-79 -28.5l-33 66q48 23 96 34t105 11q68 0 118.5 -19.5t83.5 -54t49.5 -81.5t16.5 -102q0 -59 -21 -107.5t-59 -83t-91 -53.5t-116 -19q-54 0 -100 8.5t-90 30.5l23 75q41 -21 82 -29.5t79 -8.5q87 0 138.5 41t59.5 120
+h-266v72h263z" />
+    <glyph glyph-name="afii10096" unicode="&#x44e;" horiz-adv-x="808" 
+d="M156 500v-204h93q7 50 29 89.5t56 67.5t78 42.5t95 14.5q58 0 106 -18t82.5 -52t53.5 -82t19 -108t-19.5 -108t-54.5 -82t-82.5 -52t-104.5 -18q-55 0 -100.5 16t-79.5 46t-55 73t-25 97h-91v-222h-91v500h91zM336 250q0 -87 45.5 -137t125.5 -50t125.5 50t45.5 137
+t-45.5 137t-125.5 50t-125.5 -50t-45.5 -137z" />
+    <glyph glyph-name="afii10097" unicode="&#x44f;" horiz-adv-x="563" 
+d="M416 0v169q-30 -12 -71 -17t-89 -5l-96 -147h-104l113 166q-50 19 -82 57t-32 98q0 46 18 80t49 56t72 32.5t87 10.5h228v-500h-93zM288 427q-66 0 -103 -26.5t-37 -80.5q0 -49 32.5 -74.5t85.5 -25.5q67 2 84 4.5t33 5.5t33 8v189h-128z" />
+    <glyph glyph-name="afii10071" unicode="&#x451;" horiz-adv-x="587" 
+d="M136 227q3 -41 19 -71.5t42 -50t59.5 -29t69.5 -9.5q53 0 93.5 8.5t79.5 30.5l27 -76q-25 -12 -47.5 -19.5t-46.5 -12t-51.5 -6.5t-60.5 -2q-68 0 -120 18t-87.5 52t-54 82t-18.5 107q0 58 20.5 106.5t57.5 82.5t86.5 53t108.5 19q60 0 104 -15.5t73 -43t43 -65.5t14 -83
+q0 -21 -2 -38.5t-7 -37.5h-402zM457 299q-1 74 -37 105t-117 31q-30 0 -59.5 -8.5t-52.5 -25.5t-38.5 -42.5t-18.5 -59.5h323zM205 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17zM428 681q29 0 45.5 -17
+t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="afii10099" unicode="&#x452;" horiz-adv-x="625" 
+d="M173 729v-102h221l-4 -80l-217 8v-137q35 42 80 62t99 20q104 0 156.5 -57.5t52.5 -163.5v-243q0 -104 -52 -164.5t-149 -74.5l-17 76q66 13 95.5 51t29.5 103v253q0 66 -26 105t-90 39q-56 0 -98.5 -21.5t-80.5 -66.5v-336h-93v552l-80 8v49l70 38l62 86z" />
+    <glyph glyph-name="afii10100" unicode="&#x453;" horiz-adv-x="476" 
+d="M435 500v-77h-277v-423h-93v500h370zM322 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="afii10101" unicode="&#x454;" horiz-adv-x="548" 
+d="M405 227h-270q4 -37 20 -66.5t40.5 -50.5t56 -32.5t66.5 -11.5q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19t102.5 -9t80.5 -29l-39 -69q-33 17 -66.5 25t-71.5 8
+q-80 0 -130 -35t-64 -102h266v-72z" />
+    <glyph glyph-name="afii10102" unicode="&#x455;" horiz-adv-x="502" 
+d="M406 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -37 33.5 -50.5t99.5 -20.5q94 -10 145.5 -45.5t51.5 -112.5q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 19 -7 32t-23 21.5t-42 14t-65 9.5
+q-98 10 -146 43t-48 101q0 30 13 56.5t39.5 46t67 31t94.5 11.5q27 0 49.5 -1.5t43.5 -5t42 -9.5t44 -15z" />
+    <glyph glyph-name="afii10103" unicode="&#x456;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM112 716q31 0 48 -18.5t17 -44.5q0 -25 -17 -44t-48 -19t-48 19t-17 44q0 26 17 44.5t48 18.5z" />
+    <glyph glyph-name="afii10104" unicode="&#x457;" horiz-adv-x="223" 
+d="M158 500v-500h-93v500h93zM0 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17zM223 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="afii10105" unicode="&#x458;" horiz-adv-x="233" 
+d="M168 500v-529q0 -101 -52 -156.5t-149 -57.5l-7 79q59 4 86.5 32t27.5 86v546h94zM121 716q31 0 48 -18.5t17 -44.5q0 -25 -17 -44t-48 -19t-48 19t-17 44q0 26 17 44.5t48 18.5z" />
+    <glyph glyph-name="afii10106" unicode="&#x459;" horiz-adv-x="887" 
+d="M526 500v-175q26 14 54.5 21.5t65.5 7.5q47 0 86 -11.5t66.5 -33.5t43 -55.5t15.5 -76.5q0 -47 -16.5 -80t-45.5 -55t-68.5 -32t-85.5 -10h-207v426h-185v-80q0 -47 -2.5 -91.5t-9.5 -84t-21 -73t-36 -58.5q-20 -23 -46 -34.5t-64 -11.5q-21 0 -36 2t-31 9l11 70
+q9 -3 17.5 -4t16.5 -1q24 0 39 6t28 20q30 32 38.5 97.5t8.5 163.5v144h364zM631 73q66 0 99.5 25.5t33.5 79.5q0 49 -30.5 75.5t-90.5 26.5q-19 0 -33.5 -1t-28 -3.5t-27 -7t-28.5 -10.5v-185h105z" />
+    <glyph glyph-name="afii10107" unicode="&#x45a;" horiz-adv-x="877" 
+d="M157 500v-203h267v203h92v-175q26 14 55.5 21.5t66.5 7.5q47 0 85.5 -11.5t66 -33.5t42.5 -55.5t15 -76.5q0 -47 -16.5 -80t-45.5 -55t-68.5 -32t-85.5 -10h-207v223h-267v-223h-92v500h92zM621 73q66 0 99.5 25.5t33.5 79.5q0 49 -29.5 75.5t-89.5 26.5q-19 0 -34 -1
+t-29 -3.5t-27.5 -7t-28.5 -10.5v-185h105z" />
+    <glyph glyph-name="afii10108" unicode="&#x45b;" horiz-adv-x="625" 
+d="M173 729v-102h221l-4 -80l-217 8v-137q35 42 80 62t99 20q104 0 156.5 -57.5t52.5 -163.5v-279h-93v280q0 66 -26 105t-90 39q-56 0 -98.5 -21.5t-80.5 -66.5v-336h-93v552l-80 8v49l70 38l62 86z" />
+    <glyph glyph-name="afii10109" unicode="&#x45c;" horiz-adv-x="566" 
+d="M335 746l73 -47l-103 -136h-64zM158 500v-185h124l70 123q22 38 52.5 54t72.5 16q34 0 55 -11l-16 -74q-19 4 -35 4q-24 0 -37.5 -11t-26.5 -32l-63 -107l181 -277h-108l-150 240h-119v-240h-93v500h93z" />
+    <glyph glyph-name="afii10110" unicode="&#x45e;" horiz-adv-x="599" 
+d="M579 500l-224 -582q-23 -60 -65 -90t-96 -30q-15 0 -27 1t-25 5l10 75q26 0 46 2.5t35.5 10t26.5 21.5t20 37l18 50h-72l-206 500h105l158 -423h43l152 423h101zM232 669q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27t-51.5 82
+z" />
+    <glyph glyph-name="afii10193" unicode="&#x45f;" horiz-adv-x="615" 
+d="M159 500v-426h298v426h93v-500h-197l-9 -122h-73l-9 122h-197v500h94z" />
+    <glyph glyph-name="afii10050" unicode="&#x490;" horiz-adv-x="516" 
+d="M387 650l7 100h77l6 -177h-316v-573h-96v650h322z" />
+    <glyph glyph-name="afii10098" unicode="&#x491;" horiz-adv-x="483" 
+d="M365 500l11 88h71l6 -164h-295v-424h-93v500h300z" />
+    <glyph glyph-name="endash" unicode="&#x2013;" horiz-adv-x="579" 
+d="M499 303v-76h-419v76h419z" />
+    <glyph glyph-name="emdash" unicode="&#x2014;" horiz-adv-x="832" 
+d="M742 303v-76h-652v76h652z" />
+    <glyph glyph-name="quoteleft" unicode="&#x2018;" horiz-adv-x="206" 
+d="M161.002 657.996q-66.9961 -22.999 -66.9961 -66.9961q27.999 0 40.498 -15.499t12.499 -35.498q0 -17.999 -11.999 -35.998q-11.999 -17.998 -43.9971 -17.998q-32.998 0 -49.4971 23.498q-16.499 23.499 -16.499 58.4961q0 17.999 6.49902 36.998
+q6.5 18.999 18.999 36.498t31.998 31.998t45.4971 22.498z" />
+    <glyph glyph-name="quoteright" unicode="&#x2019;" horiz-adv-x="206" 
+d="M35 472q67 23 67 67q-28 0 -40.5 15.5t-12.5 35.5q0 18 12 36t44 18q33 0 49.5 -23.5t16.5 -58.5q0 -18 -6.5 -37t-19 -36.5t-32 -32t-45.5 -22.5z" />
+    <glyph glyph-name="quotesinglbase" unicode="&#x201a;" horiz-adv-x="206" 
+d="M35 -67q67 23 67 67q-28 0 -40.5 15.5t-12.5 35.5q0 18 12 36t44 18q33 0 49.5 -23.5t16.5 -58.5q0 -18 -6.5 -37t-19 -36.5t-32 -32t-45.5 -22.5z" />
+    <glyph glyph-name="quotedblleft" unicode="&#x201c;" horiz-adv-x="356" 
+d="M311.002 657.996q-66.9961 -22.999 -66.9961 -66.9961q27.999 0 40.498 -15.499t12.499 -35.498q0 -17.999 -11.999 -35.998q-11.999 -17.998 -43.9971 -17.998q-32.998 0 -49.4971 23.498q-16.499 23.499 -16.499 58.4961q0 17.999 6.49902 36.998
+q6.5 18.999 18.999 36.498t31.998 31.998t45.4971 22.498zM161.012 657.996q-66.9961 -22.999 -66.9961 -66.9961q27.998 0 40.4971 -15.499t12.499 -35.498q0 -17.999 -11.999 -35.998q-11.999 -17.998 -43.9971 -17.998q-32.998 0 -49.4971 23.498
+q-16.499 23.499 -16.499 58.4961q0 17.999 6.5 36.998q6.49902 18.999 18.998 36.498q12.5 17.499 31.998 31.998q19.499 14.499 45.498 22.498z" />
+    <glyph glyph-name="quotedblright" unicode="&#x201d;" horiz-adv-x="356" 
+d="M35 472q67 23 67 67q-28 0 -40.5 15.5t-12.5 35.5q0 18 12 36t44 18q33 0 49.5 -23.5t16.5 -58.5q0 -18 -6.5 -37t-19 -36.5t-32 -32t-45.5 -22.5zM185 472q67 23 67 67q-28 0 -40.5 15.5t-12.5 35.5q0 18 12 36t44 18q33 0 49.5 -23.5t16.5 -58.5q0 -18 -6.5 -37
+t-19 -36.5t-32 -32t-45.5 -22.5z" />
+    <glyph glyph-name="quotedblbase" unicode="&#x201e;" horiz-adv-x="356" 
+d="M35 -67q67 23 67 67q-28 0 -40.5 15.5t-12.5 35.5q0 18 12 36t44 18q33 0 49.5 -23.5t16.5 -58.5q0 -18 -6.5 -37t-19 -36.5t-32 -32t-45.5 -22.5zM185 -67q67 23 67 67q-28 0 -40.5 15.5t-12.5 35.5q0 18 12 36t44 18q33 0 49.5 -23.5t16.5 -58.5q0 -18 -6.5 -37
+t-19 -36.5t-32 -32t-45.5 -22.5z" />
+    <glyph glyph-name="dagger" unicode="&#x2020;" horiz-adv-x="373" 
+d="M199 547l30 -9v-66l-8 -49l47 8h80v-50l-11 -21h-69l-47 7l7 -54v-228l-11 -96l-47 -17l-24 16v325l6 54l-46 -7h-81v47l11 24h69l47 -8l-8 49v60z" />
+    <glyph glyph-name="daggerdbl" unicode="&#x2021;" horiz-adv-x="373" 
+d="M199 547l30 -9v-66l-8 -49l47 8h80v-50l-11 -21h-69l-47 7l7 -54l-7 -49l47 8h75v-50l-11 -21h-64l-47 7l7 -54v-69l-11 -96l-47 -17l-24 16v166l6 54l-46 -7h-81v47l11 24h69l47 -8l-6 49l6 54l-46 -7h-76v47l11 24h64l47 -8l-8 49v60z" />
+    <glyph glyph-name="bullet" unicode="&#x2022;" horiz-adv-x="254" 
+d="M204 309q0 -32 -21.5 -53t-55.5 -21q-35 0 -56 21t-21 53t21.5 53t55.5 21q35 0 56 -21t21 -53z" />
+    <glyph glyph-name="onedotenleader" unicode="&#x2024;" horiz-adv-x="233" 
+d="M116 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="twodotenleader" unicode="&#x2025;" horiz-adv-x="427" 
+d="M116 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19zM310 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="ellipsis" unicode="&#x2026;" horiz-adv-x="615" 
+d="M116 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19zM307 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19zM498 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46
+q0 28 17 47t49 19z" />
+    <glyph glyph-name="perthousand" unicode="&#x2030;" horiz-adv-x="1040" 
+d="M557 474l-348 -496l-57 37l349 497zM179 231q-30 0 -57.5 10.5t-47.5 29t-32 44t-12 55.5t11.5 55.5t31.5 44.5t47 29.5t59 10.5q30 0 57.5 -11t47.5 -30t32 -44.5t12 -54.5q0 -30 -12 -55.5t-32.5 -44t-47.5 -29t-57 -10.5zM179 294q32 0 55 20t23 56q0 35 -21.5 56
+t-56.5 21q-37 0 -58 -21.5t-21 -55.5t22 -55t57 -21zM538 -10q-30 0 -57.5 10.5t-47.5 29t-32 44t-12 55.5t11.5 55.5t31.5 44.5t47 29.5t59 10.5q30 0 57.5 -11t47.5 -30t32 -44.5t12 -54.5q0 -30 -12 -55.5t-32.5 -44t-47.5 -29t-57 -10.5zM538 53q32 0 55 20t23 56
+q0 35 -21.5 56t-56.5 21q-37 0 -58 -21.5t-21 -55.5t22 -55t57 -21zM861 -10q-30 0 -57.5 10.5t-47.5 29t-32 44t-12 55.5t11.5 55.5t31.5 44.5t47 29.5t59 10.5q30 0 57.5 -11t47.5 -30t32 -44.5t12 -54.5q0 -30 -12 -55.5t-32.5 -44t-47.5 -29t-57 -10.5zM861 53
+q32 0 55 20t23 56q0 35 -21.5 56t-56.5 21q-37 0 -58 -21.5t-21 -55.5t22 -55t57 -21z" />
+    <glyph glyph-name="guilsinglleft" unicode="&#x2039;" horiz-adv-x="332" 
+d="M297 385l-152 -129l152 -135l-38 -58l-219 171v38l219 171z" />
+    <glyph glyph-name="guilsinglright" unicode="&#x203a;" horiz-adv-x="332" 
+d="M292 272v-38l-219 -171l-38 58l152 129l-152 135l38 58z" />
+    <glyph glyph-name="exclamdbl" unicode="&#x203c;" horiz-adv-x="411" 
+d="M160 679l-13 -509h-69l-13 509h95zM112 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19zM346 679l-13 -509h-69l-13 509h95zM298 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="fraction" unicode="&#x2044;" horiz-adv-x="293" 
+d="M358 623l-358 -644l-62 31l355 643z" />
+    <glyph glyph-name="zerosuperior" unicode="&#x2070;" horiz-adv-x="448" 
+d="M103 559q0 -58 32 -94t89 -36t88.5 35.5t31.5 94.5q0 60 -32 95.5t-88 35.5t-88.5 -36.5t-32.5 -94.5zM418 559q0 -37 -12.5 -72t-37 -62t-61 -43t-83.5 -16t-83.5 16t-61 43t-37 62t-12.5 72t13 72.5t38 62.5t61 43.5t82 16.5t82 -16.5t61 -43.5t38 -62.5t13 -72.5z" />
+    <glyph glyph-name="foursuperior" unicode="&#x2074;" horiz-adv-x="381" 
+d="M311 745v-244h40v-62h-40v-64h-71v64h-211l-9 51l232 260zM112 501h128v144z" />
+    <glyph glyph-name="fivesuperior" unicode="&#x2075;" horiz-adv-x="343" 
+d="M293 745v-63h-177l-3 -75q30 9 70 9q28 0 52.5 -7.5t43 -22.5t29 -37.5t10.5 -53.5q0 -33 -12.5 -57t-33.5 -40t-49 -24t-59 -8q-25 0 -44.5 2t-36.5 6t-32 11t-30 18l32 58q28 -20 51.5 -28t55.5 -8q42 0 65.5 18t23.5 52q0 29 -20 46t-62 17q-27 0 -46 -3t-44 -11
+l-30 7l7 194h239z" />
+    <glyph glyph-name="sixsuperior" unicode="&#x2076;" horiz-adv-x="362" 
+d="M99 537q0 -49 21 -79.5t63 -30.5t62 18t20 48q0 68 -70 68q-29 0 -51.5 -6t-44.5 -18zM297 694q-54 -1 -89.5 -7.5t-57 -19.5t-32.5 -32.5t-17 -46.5q21 16 45.5 24.5t56.5 8.5q26 0 50 -7t43 -22.5t30 -40t11 -58.5q0 -33 -12 -57t-33 -39.5t-49 -23t-60 -7.5
+q-38 0 -67 13.5t-48 38t-28.5 58t-9.5 73.5q0 105 67.5 155t195.5 50z" />
+    <glyph glyph-name="sevensuperior" unicode="&#x2077;" horiz-adv-x="330" 
+d="M305 745v-46l-165 -324h-77l161 308h-199v62h280z" />
+    <glyph glyph-name="eightsuperior" unicode="&#x2078;" horiz-adv-x="380" 
+d="M156 554q-29 -5 -41.5 -21.5t-12.5 -44.5q0 -33 25 -48t63 -15q36 0 62 15.5t26 47.5q0 23 -11 36t-29 19.5t-39.5 8t-42.5 2.5zM217 607q20 6 28 16.5t8 29.5q0 24 -17 33.5t-45 9.5t-46 -8.5t-18 -33.5q0 -27 24.5 -36.5t65.5 -10.5zM91 583q-19 13 -27 31t-8 39
+q0 27 11.5 46t30 31t43 18t50.5 6q25 0 49 -6t43 -18t30 -30.5t11 -44.5q0 -20 -9 -38.5t-27 -29.5q34 -15 48 -39.5t14 -56.5q0 -33 -13.5 -57t-36 -39t-51 -22t-59.5 -7q-35 0 -64 7.5t-50.5 22.5t-33.5 38t-12 55q0 66 61 94z" />
+    <glyph glyph-name="ninesuperior" unicode="&#x2079;" horiz-adv-x="362" 
+d="M268 583q0 52 -21 81t-63 29q-43 0 -62.5 -18t-19.5 -48q0 -34 21 -51t56 -17q25 0 46 6t43 18zM71 426q37 0 70 6t59.5 18.5t43.5 32t22 45.5q-41 -29 -95 -29q-26 0 -51 7.5t-44.5 23t-31.5 39.5t-12 57t12 57t32.5 40t48 23.5t59.5 7.5q76 0 114.5 -50t38.5 -130
+q0 -105 -69.5 -156.5t-191.5 -51.5z" />
+    <glyph glyph-name="parenleftsuperior" unicode="&#x207d;" horiz-adv-x="156" 
+d="M145 767q-22 -42 -34 -96.5t-12 -107.5t12 -105t35 -95l-52 -26q-63 98 -63 226q0 63 16 122t47 107z" />
+    <glyph glyph-name="parenrightsuperior" unicode="&#x207e;" horiz-adv-x="156" 
+d="M62 792q31 -48 47.5 -106t16.5 -122q0 -63 -16.5 -121t-47.5 -106l-51 25q22 42 34 95t12 107q0 52 -12 105.5t-35 96.5z" />
+    <glyph glyph-name="zeroinferior" unicode="&#x2080;" horiz-adv-x="448" 
+d="M103 115q0 -58 32 -94t89 -36t88.5 35.5t31.5 94.5q0 60 -32 95.5t-88 35.5t-88.5 -36.5t-32.5 -94.5zM418 115q0 -37 -12.5 -72t-37 -62t-61 -43t-83.5 -16t-83.5 16t-61 43t-37 62t-12.5 72t13 72.5t38 62.5t61 43.5t82 16.5t82 -16.5t61 -43.5t38 -62.5t13 -72.5z" />
+    <glyph glyph-name="oneinferior" unicode="&#x2081;" horiz-adv-x="226" 
+d="M137 304l39 -5v-368h-71v284l-71 -19l-17 43z" />
+    <glyph glyph-name="twoinferior" unicode="&#x2082;" horiz-adv-x="332" 
+d="M296 -69h-271v61l157 119q26 19 37.5 37t11.5 41q0 26 -17.5 41.5t-54.5 15.5q-30 0 -54 -10.5t-48 -33.5l-34 51q29 29 62.5 43t78.5 14q63 0 100.5 -32.5t37.5 -87.5q0 -33 -14 -62t-42 -49l-117 -86h167v-62z" />
+    <glyph glyph-name="threeinferior" unicode="&#x2083;" horiz-adv-x="357" 
+d="M262 138q60 -27 60 -90q0 -33 -13 -56.5t-35 -39t-50.5 -23t-59.5 -7.5q-25 0 -44 2t-35 7t-30.5 13t-29.5 20l24 55q29 -22 54 -30.5t57 -8.5q39 0 66 17t27 51q0 29 -16.5 40.5t-54.5 11.5h-83v58h74q29 0 44.5 9.5t15.5 35.5q0 47 -67 47q-54 0 -95 -31l-33 47
+q30 25 61 34.5t73 9.5q22 0 44.5 -6t41 -18t30.5 -30.5t12 -43.5q0 -27 -9 -44.5t-29 -29.5z" />
+    <glyph glyph-name="fourinferior" unicode="&#x2084;" horiz-adv-x="381" 
+d="M311 301v-244h40v-62h-40v-64h-71v64h-211l-9 51l232 260zM112 57h128v144z" />
+    <glyph glyph-name="fiveinferior" unicode="&#x2085;" horiz-adv-x="343" 
+d="M293 301v-63h-177l-3 -75q30 9 70 9q28 0 52.5 -7.5t43 -22.5t29 -37.5t10.5 -53.5q0 -33 -12.5 -57t-33.5 -40t-49 -24t-59 -8q-25 0 -44.5 2t-36.5 6t-32 11t-30 18l32 58q28 -20 51.5 -28t55.5 -8q42 0 65.5 18t23.5 52q0 29 -20 46t-62 17q-27 0 -46 -3t-44 -11
+l-30 7l7 194h239z" />
+    <glyph glyph-name="sixinferior" unicode="&#x2086;" horiz-adv-x="362" 
+d="M99 93q0 -49 21 -79.5t63 -30.5t62 18t20 48q0 68 -70 68q-29 0 -51.5 -6t-44.5 -18zM297 250q-54 -1 -89.5 -7.5t-57 -19.5t-32.5 -32.5t-17 -46.5q21 16 45.5 24.5t56.5 8.5q26 0 50 -7t43 -22.5t30 -40t11 -58.5q0 -33 -12 -57t-33 -39.5t-49 -23t-60 -7.5
+q-38 0 -67 13.5t-48 38t-28.5 58t-9.5 73.5q0 105 67.5 155t195.5 50z" />
+    <glyph glyph-name="seveninferior" unicode="&#x2087;" horiz-adv-x="330" 
+d="M305 301v-46l-165 -324h-77l161 308h-199v62h280z" />
+    <glyph glyph-name="eightinferior" unicode="&#x2088;" horiz-adv-x="380" 
+d="M156 110q-29 -5 -41.5 -21.5t-12.5 -44.5q0 -33 25 -48t63 -15q36 0 62 15.5t26 47.5q0 23 -11 36t-29 19.5t-39.5 8t-42.5 2.5zM217 163q20 6 28 16.5t8 29.5q0 24 -17 33.5t-45 9.5t-46 -8.5t-18 -33.5q0 -27 24.5 -36.5t65.5 -10.5zM91 139q-19 13 -27 31t-8 39
+q0 27 11.5 46t30 31t43 18t50.5 6q25 0 49 -6t43 -18t30 -30.5t11 -44.5q0 -20 -9 -38.5t-27 -29.5q34 -15 48 -39.5t14 -56.5q0 -33 -13.5 -57t-36 -39t-51 -22t-59.5 -7q-35 0 -64 7.5t-50.5 22.5t-33.5 38t-12 55q0 66 61 94z" />
+    <glyph glyph-name="nineinferior" unicode="&#x2089;" horiz-adv-x="362" 
+d="M268 139q0 52 -21 81t-63 29q-43 0 -62.5 -18t-19.5 -48q0 -34 21 -51t56 -17q25 0 46 6t43 18zM71 -18q37 0 70 6t59.5 18.5t43.5 32t22 45.5q-41 -29 -95 -29q-26 0 -51 7.5t-44.5 23t-31.5 39.5t-12 57t12 57t32.5 40t48 23.5t59.5 7.5q76 0 114.5 -50t38.5 -130
+q0 -105 -69.5 -156.5t-191.5 -51.5z" />
+    <glyph glyph-name="parenleftinferior" unicode="&#x208d;" horiz-adv-x="156" 
+d="M145 323q-22 -42 -34 -96.5t-12 -107.5t12 -105t35 -95l-52 -26q-63 98 -63 226q0 63 16 122t47 107z" />
+    <glyph glyph-name="parenrightinferior" unicode="&#x208e;" horiz-adv-x="156" 
+d="M62 348q31 -48 47.5 -106t16.5 -122q0 -63 -16.5 -121t-47.5 -106l-51 25q22 42 34 95t12 107q0 52 -12 105.5t-35 96.5z" />
+    <glyph glyph-name="Euro" unicode="&#x20ac;" horiz-adv-x="649" 
+d="M452 286l-4 -69h-274q12 -38 33.5 -66.5t50.5 -48t64.5 -29t75.5 -9.5q57 0 102.5 14t93.5 46l27 -73q-49 -35 -105.5 -49.5t-126.5 -14.5q-52 0 -101.5 13.5t-90.5 42t-71 72t-43 102.5h-58l4 69h423zM91 395q15 49 45 88.5t70.5 67.5t90 42.5t102.5 14.5q67 0 116 -16
+t96 -50l-35 -67q-44 32 -86 46.5t-91 14.5q-81 0 -137.5 -37.5t-81.5 -103.5h277l-4 -69h-419l4 69h53z" />
+    <glyph glyph-name="afii61352" unicode="&#x2116;" horiz-adv-x="1024" 
+d="M964 198v-66h-242v66h242zM984 385q0 -29 -10.5 -53.5t-29.5 -42.5t-45 -28t-56 -10t-56 10t-45 28t-29.5 42.5t-10.5 53.5t10.5 53.5t29.5 42.5t45 28t56 10t56 -10t45 -28t29.5 -42.5t10.5 -53.5zM164 650l397 -530l-3 169v361h91v-650h-102l-398 531l6 -196v-335h-90
+v650h99zM773 385q0 -35 19 -53t51 -18t51 18t19 53t-19 53t-51 18t-51 -18t-19 -53z" />
+    <glyph glyph-name="trademark" unicode="&#x2122;" horiz-adv-x="620" 
+d="M252 650v-49h-81v-216h-60v216h-81v49h222zM343 650l83 -115l83 115h61v-265h-59v184l-77 -106h-22l-75 106v-184h-57v265h63z" />
+    <glyph glyph-name="Omega" unicode="&#x2126;" horiz-adv-x="770" 
+d="M448 60q94 50 137.5 117.5t43.5 155.5q0 58 -17.5 104.5t-49 79t-77 50t-100.5 17.5q-56 0 -101 -17.5t-76.5 -50t-49 -79t-17.5 -104.5q0 -88 43.5 -155.5t137.5 -117.5v-60h-264v73h116q-66 48 -97.5 116.5t-31.5 143.5q0 71 24 131.5t68.5 104.5t107.5 69t140 25
+t140 -25t107.5 -69t68.5 -104.5t24 -131.5q0 -75 -31.5 -143.5t-97.5 -116.5h116v-73h-264v60z" />
+    <glyph glyph-name="estimated" unicode="&#x212e;" horiz-adv-x="627" 
+d="M134 242q2 -57 16.5 -98.5t35.5 -70.5q35 -21 73.5 -28.5t76.5 -7.5q57 0 108 15.5t98 43.5l21 -49q-49 -31 -107.5 -44t-125.5 -13q-68 0 -121.5 18t-91 52t-57.5 82t-20 107q0 58 22.5 106.5t60.5 82.5t89.5 53t110.5 19q60 0 108 -15.5t82 -43t52.5 -65.5t18.5 -83
+q0 -16 -2 -32t-7 -29h-441zM495 284q-1 42 -7.5 77.5t-28.5 75.5q-32 18 -65.5 25t-80.5 7q-36 0 -67.5 -8t-57.5 -27q-24 -30 -37.5 -66.5t-16.5 -83.5h361z" />
+    <glyph glyph-name="arrowleft" unicode="&#x2190;" horiz-adv-x="778" 
+d="M728 239h-550l87 -88l-55 -36l-165 148v22l165 148l55 -36l-87 -88h550v-70z" />
+    <glyph glyph-name="arrowup" unicode="&#x2191;" horiz-adv-x="454" 
+d="M187 0v512l-98 -84l-39 52l166 170h22l166 -170l-39 -52l-98 84v-512h-80z" />
+    <glyph glyph-name="arrowright" unicode="&#x2192;" horiz-adv-x="778" 
+d="M45 309h550l-87 88l55 36l165 -148v-22l-165 -148l-55 36l87 88h-550v70z" />
+    <glyph glyph-name="arrowdown" unicode="&#x2193;" horiz-adv-x="454" 
+d="M267 650v-535l98 84l39 -52l-166 -170h-22l-166 170l39 52l98 -84v535h80z" />
+    <glyph glyph-name="partialdiff" unicode="&#x2202;" horiz-adv-x="623" 
+d="M163 692q47 -2 96.5 -8t96.5 -20.5t88 -38t72 -59t49 -85t18 -115.5q0 -91 -26 -170.5t-83 -132.5q-38 -34 -89 -53.5t-118 -19.5q-50 0 -91.5 15.5t-71.5 44t-47 68.5t-17 90q0 54 19.5 100t55.5 79.5t86 53t112 19.5q60 0 110 -20.5t73 -64.5h7q-3 50 -25 93.5
+t-65.5 75.5t-111 50.5t-160.5 18.5zM277 64q39 0 74.5 13t62.5 36.5t43 57.5t16 76q0 61 -43 100t-116 39q-85 0 -134 -43.5t-49 -133.5q0 -64 37.5 -104.5t108.5 -40.5z" />
+    <glyph glyph-name="Delta" unicode="&#x2206;" horiz-adv-x="660" 
+d="M388 650l237 -600v-50h-590v50l250 600h103zM142 77h376l-180 478h-11z" />
+    <glyph glyph-name="product" unicode="&#x220f;" horiz-adv-x="520" 
+d="M455 664v-724h-94v647h-202v-647h-94v724h390z" />
+    <glyph glyph-name="summation" unicode="&#x2211;" horiz-adv-x="528" 
+d="M484 650v-77h-295l212 -211v-38l-234 -245h331v-79h-443v77l249 265l-239 239v69h419z" />
+    <glyph glyph-name="minus" unicode="&#x2212;" horiz-adv-x="381" 
+d="M341 367v-76h-301v76h301z" />
+    <glyph glyph-name="radical" unicode="&#x221a;" horiz-adv-x="509" 
+d="M116 500l114 -416h4l140 656h177l-10 -76h-91l-147 -664h-137l-151 500h101z" />
+    <glyph glyph-name="infinity" unicode="&#x221e;" horiz-adv-x="623" 
+d="M274 323q-19 32 -43 54t-56 22q-78 0 -78 -77q0 -33 17.5 -54t51.5 -21q35 0 60 17.5t48 58.5zM307 265q-26 -41 -63 -64t-78 -23q-65 0 -103 40t-38 103q0 37 12.5 64.5t32.5 46t45.5 27.5t51.5 9q50 0 83.5 -22t65.5 -65q26 41 63 64t78 23q65 0 103 -40t38 -103
+q0 -37 -12.5 -64.5t-32.5 -46t-45.5 -27.5t-51.5 -9q-50 0 -83.5 22t-65.5 65zM349 323q19 -32 43 -54t56 -22q78 0 78 77q0 33 -17.5 54t-51.5 21q-35 0 -60 -17.5t-48 -58.5z" />
+    <glyph glyph-name="integral" unicode="&#x222b;" horiz-adv-x="223" 
+d="M262 672q-29 -2 -49 -6.5t-32 -15.5t-17.5 -29t-5.5 -47v-523q0 -101 -46.5 -146.5t-143.5 -47.5l-7 79q29 2 49 6.5t32 15.5t17.5 29t5.5 47v523q0 101 46.5 146.5t143.5 47.5z" />
+    <glyph glyph-name="approxequal" unicode="&#x2248;" horiz-adv-x="437" 
+d="M40 278q22 24 49 36t63 12q25 0 40.5 -6t28.5 -13.5t26 -13.5t33 -6q21 0 35.5 7t31.5 24l41 -50q-23 -29 -48 -42.5t-59 -13.5q-23 0 -40 6.5t-32 14t-30 14t-33 6.5q-26 0 -44 -9.5t-34 -29.5zM40 416q22 24 49 36t63 12q25 0 40.5 -6t28.5 -13.5t26 -13.5t33 -6
+q21 0 35.5 7t31.5 24l41 -50q-23 -29 -48 -42.5t-59 -13.5q-23 0 -40 6.5t-32 14t-30 14t-33 6.5q-26 0 -44 -9.5t-34 -29.5z" />
+    <glyph glyph-name="notequal" unicode="&#x2260;" horiz-adv-x="389" 
+d="M186 440l17 65l65 -17l-13 -48h94v-74h-113l-17 -68h130v-74h-149l-18 -70l-65 17l13 53h-90v74h109l18 68h-127v74h146z" />
+    <glyph glyph-name="lessequal" unicode="&#x2264;" horiz-adv-x="337" 
+d="M292 486l-160 -123l170 -120l-58 -50l-209 141v38l199 161zM252 124l-58 -49l-159 111l20 69z" />
+    <glyph glyph-name="greaterequal" unicode="&#x2265;" horiz-adv-x="337" 
+d="M302 372v-38l-209 -141l-58 50l170 120l-160 123l58 47zM302 186l-159 -111l-58 49l197 131z" />
+    <glyph glyph-name="triagup" unicode="&#x25b2;" horiz-adv-x="664" 
+d="M332 417l267 -243v-19h-534v18z" />
+    <glyph glyph-name="triagrt" unicode="&#x25ba;" horiz-adv-x="367" 
+d="M327 249l-243 -257h-19v514h18z" />
+    <glyph glyph-name="triagdn" unicode="&#x25bc;" horiz-adv-x="664" 
+d="M332 95l-267 243v19h534v-18z" />
+    <glyph glyph-name="triaglf" unicode="&#x25c4;" horiz-adv-x="367" 
+d="M40 249l243 257h19v-514h-18z" />
+    <glyph glyph-name="lozenge" unicode="&#x25ca;" horiz-adv-x="563" 
+d="M232 -114l-192 380v44l193 380h98l192 -380v-44l-193 -380h-98zM127 292l154 -313h4l150 304l-153 314h-4z" />
+    <glyph glyph-name="Acute" horiz-adv-x="264" 
+d="M143 881l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="Breve" horiz-adv-x="383" 
+d="M95 829q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="Caron" horiz-adv-x="360" 
+d="M180 778l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="Cedilla" horiz-adv-x="269" 
+d="M153 7l-7 -55q35 0 54 -18.5t19 -51.5q0 -28 -14.5 -49.5t-37 -37t-50 -25.5t-52.5 -14l-13 61q41 7 68 21.5t27 41.5t-36 27h-33l4 96z" />
+    <glyph glyph-name="Circumflex" horiz-adv-x="360" 
+d="M180 777l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="Dieresis" horiz-adv-x="454" 
+d="M117 828q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM336 828q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="Dotaccent" horiz-adv-x="230" 
+d="M116 121q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="Euro.sc" horiz-adv-x="582" 
+d="M12 259h361l-5 -68h-224q23 -61 76 -92.5t122 -31.5q27 0 49 2.5t42 8.5t40 15.5t43 23.5l26 -71q-46 -32 -95 -44t-114 -12q-47 0 -92 12.5t-83 37.5t-66.5 62.5t-41.5 88.5h-43zM18 368h37q15 44 43 79t65.5 59t83.5 36.5t96 12.5q63 0 108.5 -15t90.5 -47l-43 -65
+q-68 50 -160 50q-73 0 -121.5 -29t-70.5 -81h232l-5 -68h-361z" />
+    <glyph glyph-name="Euro.tab.sc" 
+d="M32 259h361l-5 -68h-224q23 -61 76 -92.5t122 -31.5q27 0 49 2.5t42 8.5t40 15.5t43 23.5l26 -71q-46 -32 -95 -44t-114 -12q-47 0 -92 12.5t-83 37.5t-66.5 62.5t-41.5 88.5h-43zM38 368h37q15 44 43 79t65.5 59t83.5 36.5t96 12.5q63 0 108.5 -15t90.5 -47l-43 -65
+q-68 50 -160 50q-73 0 -121.5 -29t-70.5 -81h232l-5 -68h-361z" />
+    <glyph glyph-name="Euro.taboldstyle" 
+d="M37 229h393v-66h-236q23 -46 67 -71.5t98 -25.5q25 0 46 2.5t40.5 7.5t38.5 14t40 21l29 -73q-48 -27 -95 -37.5t-105 -10.5q-100 0 -167.5 47.5t-92.5 125.5h-56v66zM37 333h57q14 40 40.5 73t62 56t79 35.5t91.5 12.5q60 0 102.5 -9t80.5 -29l-36 -73q-33 19 -68 27.5
+t-73 8.5q-69 0 -116 -25.5t-68 -76.5h241v-66h-393v66z" />
+    <glyph glyph-name="Eurooldstyle" horiz-adv-x="576" 
+d="M30 229h393v-66h-236q23 -46 67 -71.5t98 -25.5q25 0 46 2.5t40.5 7.5t38.5 14t40 21l29 -73q-48 -27 -95 -37.5t-105 -10.5q-100 0 -167.5 47.5t-92.5 125.5h-56v66zM30 333h57q14 40 40.5 73t62 56t79 35.5t91.5 12.5q60 0 102.5 -9t80.5 -29l-36 -73q-33 19 -68 27.5
+t-73 8.5q-69 0 -116 -25.5t-68 -76.5h241v-66h-393v66z" />
+    <glyph glyph-name="Grave" horiz-adv-x="264" 
+d="M211 698h-69l-98 136l78 47z" />
+    <glyph glyph-name="Hungarumlaut" horiz-adv-x="375" 
+d="M106 887l82 -30l-69 -159h-64zM244 877l76 -47l-93 -132h-67z" />
+    <glyph glyph-name="Imacron" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95zM276 787v-73h-326v73h326z" />
+    <glyph glyph-name="Macron" horiz-adv-x="418" 
+d="M372 784v-73h-326v73h326z" />
+    <glyph glyph-name="Ogonek" horiz-adv-x="218" 
+d="M165 0q-32 -17 -43 -34.5t-11 -37.5q0 -17 10.5 -30.5t38.5 -13.5q7 0 14.5 1t16.5 4l-16 -65q-10 -2 -19.5 -3t-17.5 -1q-48 0 -76 24.5t-28 62.5q0 15 5.5 32t17 32t29 26t42.5 15z" />
+    <glyph glyph-name="Ring" horiz-adv-x="331" 
+d="M162 674q-52 0 -84.5 31.5t-32.5 80.5q0 23 9 43.5t24.5 35.5t37 23.5t46.5 8.5t46.5 -8.5t37 -23.5t24.5 -35.5t9 -43.5q0 -50 -33 -81t-84 -31zM218 786q0 24 -16 39.5t-40 15.5t-40.5 -15.5t-16.5 -39.5t16.5 -40t40.5 -16t40 16t16 40z" />
+    <glyph glyph-name="Tilde" horiz-adv-x="422" 
+d="M30 765q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6q-49 0 -83 -39z" />
+    <glyph glyph-name="a.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106z" />
+    <glyph glyph-name="a.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6z" />
+    <glyph glyph-name="aacute.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM344 746l73 -47l-103 -136h-64z" />
+    <glyph glyph-name="aacute.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM379 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="abreve.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM221 679q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27
+t-51.5 82z" />
+    <glyph glyph-name="abreve.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM225 750q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="acircumflex.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM289 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="acircumflex.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM321 678l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="adieresis.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM186 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5
+q0 26 16.5 43t44.5 17zM409 681q29 0 45.5 -17t16.5 -43q0 -25 -16.5 -41.5t-45.5 -16.5q-28 0 -44.5 16.5t-16.5 41.5q0 26 16.5 43t44.5 17z" />
+    <glyph glyph-name="adieresis.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM211 749q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM430 749q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43
+q0 27 17 44t46 17z" />
+    <glyph glyph-name="ae.alt1" horiz-adv-x="945" 
+d="M79 466q51 23 101 33.5t112 10.5q78 0 124 -19t66 -63q36 39 82 60.5t109 21.5q60 0 104 -15.5t73 -43t43 -65.5t14 -83q0 -21 -2 -38.5t-7 -37.5h-393q3 -40 18 -70t39 -49.5t56 -29.5t68 -10q28 0 50 1.5t42 5.5t39.5 11.5t42.5 19.5l28 -77q-24 -12 -47 -19.5t-48 -12
+t-52.5 -6t-60.5 -1.5q-75 0 -125 22t-85 64q-54 -49 -115.5 -67.5t-130.5 -18.5q-88 0 -131.5 40t-43.5 111q0 37 14 62.5t37.5 43t53.5 28t63 16.5q46 8 95.5 11.5t104.5 3.5v27q0 52 -28 76t-101 24q-95 0 -189 -41zM417 238q-48 0 -88.5 -3t-75.5 -8q-21 -3 -41 -8.5
+t-35 -15.5t-24 -24.5t-9 -36.5q0 -39 23.5 -57t73.5 -18t92 13t84 45v113zM818 299q-2 72 -39 104t-116 32q-30 0 -58 -8.5t-49.5 -25.5t-35.5 -42.5t-17 -59.5h315z" />
+    <glyph glyph-name="ae.sc" horiz-adv-x="867" 
+d="M816 560v-74h-275v-155h251v-74h-251v-182h296v-75h-387v109h-264l-69 -109h-107l357 560h449zM225 185h225v301h-36z" />
+    <glyph glyph-name="afii10055" horiz-adv-x="225" 
+d="M160 650v-650h-95v650h95z" />
+    <glyph glyph-name="afii10065.77.liga" horiz-adv-x="1138" 
+d="M1073 500v-500h-92v426h-204v-80q0 -61 -3 -107.5t-11 -83t-21 -64t-32 -50.5q-20 -24 -56 -36.5t-74 -12.5q-60 0 -94.5 19t-48.5 59q-21 -23 -43 -38t-47.5 -24.5t-55.5 -13.5t-68 -4q-88 0 -133.5 41t-45.5 112q0 34 10.5 62t36.5 57q34 10 68.5 18t73.5 13t83 8t97 4
+v28q0 52 -28.5 76t-101.5 24q-94 0 -189 -41l-20 74q55 24 103 34t110 10q111 0 163.5 -41t52.5 -133v-190q0 -36 19.5 -57t55.5 -21q38 0 60.5 18.5t33.5 51.5q12 38 14.5 93.5t2.5 124.5v144h384zM413 238q-87 -2 -146.5 -9.5t-108.5 -20.5q-20 -24 -20 -60
+q0 -41 25.5 -61.5t72.5 -20.5q50 0 92.5 14t84.5 52v106z" />
+    <glyph glyph-name="afii10065.77.liga.alt1" horiz-adv-x="1138" 
+d="M1073 500v-500h-92v426h-204v-80q0 -61 -3 -107.5t-11 -83t-21 -64t-32 -50.5q-20 -24 -56 -36.5t-74 -12.5q-60 0 -94.5 19t-48.5 59q-21 -23 -43 -38t-47.5 -24.5t-55.5 -13.5t-68 -4q-88 0 -133.5 39t-45.5 110q0 41 16.5 68.5t44 45.5t63.5 27.5t76 14.5q37 5 78.5 7
+t90.5 3v28q0 52 -28.5 76t-101.5 24q-94 0 -189 -41l-20 74q55 24 103 34t110 10q111 0 163.5 -41t52.5 -133v-190q0 -36 19.5 -57t55.5 -21q38 0 60.5 18.5t33.5 51.5q12 38 14.5 93.5t2.5 124.5v144h384zM413 238q-48 -1 -86.5 -3.5t-73.5 -7.5q-21 -3 -41.5 -8.5t-37 -15
+t-26.5 -24t-10 -35.5q0 -41 25.5 -59.5t72.5 -18.5q50 0 92.5 14t84.5 52v106z" />
+    <glyph glyph-name="afii10065.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106z" />
+    <glyph glyph-name="afii10065.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6z" />
+    <glyph glyph-name="afii10066.sc" horiz-adv-x="535" 
+d="M439 560v-74h-283v-134h110q51 0 92 -11.5t70.5 -34t45.5 -55.5t16 -76q0 -42 -16.5 -74.5t-46 -55t-70 -34t-88.5 -11.5h-204v560h374zM156 75h102q76 0 108.5 22t32.5 78q0 60 -37.5 81.5t-106.5 21.5h-99v-203z" />
+    <glyph glyph-name="afii10067.sc" horiz-adv-x="526" 
+d="M281 560q34 0 66 -10t56 -29t38.5 -47t14.5 -64q0 -32 -16 -61t-46 -45v-4q45 -16 68.5 -53.5t23.5 -83.5q0 -39 -15.5 -69t-41.5 -51t-59.5 -32t-70.5 -11h-234v560h216zM155 72h121q27 0 49.5 4t38.5 14.5t24.5 28t8.5 43.5q0 42 -25.5 67.5t-73.5 25.5h-143v-183z
+M155 325h115q18 0 35.5 4t31 13t21.5 24.5t8 38.5q0 82 -106 82h-105v-162z" />
+    <glyph glyph-name="afii10068.sc" horiz-adv-x="451" 
+d="M426 560v-76h-270v-484h-91v560h361z" />
+    <glyph glyph-name="afii10069.sc" horiz-adv-x="679" 
+d="M574 560v-485h65l-7 -207h-69l-9 132h-431l-13 -132h-70v205q39 8 63 38t39 70q15 41 23.5 91.5t8.5 108.5v179h400zM264 390q0 -63 -8 -116.5t-22 -96.5q-11 -33 -26 -58.5t-34 -43.5h308v411h-218v-96z" />
+    <glyph glyph-name="afii10070.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560z" />
+    <glyph glyph-name="afii10071.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM152 749q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM371 749q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="afii10072.77.liga" horiz-adv-x="1431" 
+d="M489 312h96l66 126q20 38 51.5 54t73.5 16q34 0 55 -11l-16 -69q-19 4 -35 4q-24 0 -38.5 -11t-25.5 -32l-59 -112l98 -142q26 -37 52 -51t56 -14q26 0 43.5 8t31.5 23q16 18 24.5 44t13 59t5.5 71.5t1 80.5v144h384v-500h-92v426h-204v-80q0 -103 -14 -179.5t-59 -126.5
+q-20 -23 -58 -35.5t-76 -12.5q-40 0 -69 8t-50.5 22t-38 33t-31.5 42l-94 143h-91v-240h-90v240h-96l-159 -240h-104l190 279l-60 109q-12 22 -26 33t-34 11q-17 0 -34 -4l-15 69q11 5 26 8t33 3q42 0 69.5 -17.5t46.5 -52.5l67 -126h96v188h90v-188z" />
+    <glyph glyph-name="afii10072.sc" horiz-adv-x="848" 
+d="M469 560v-210h88l81 152q20 38 50.5 52t67.5 14q29 0 61 -9l-14 -73q-6 2 -13.5 3t-15.5 1q-23 0 -38 -6t-29 -32l-79 -137l205 -315h-103l-174 276h-87v-276h-90v276h-87l-174 -276h-103l205 315l-80 137q-15 26 -29.5 32t-37.5 6q-8 0 -15.5 -1t-13.5 -3l-14 73
+q31 9 61 9q37 0 67.5 -14t50.5 -52l82 -152h88v210h90z" />
+    <glyph glyph-name="afii10073.sc" horiz-adv-x="518" 
+d="M252 334q50 0 77 23.5t27 61.5q0 31 -27.5 55.5t-85.5 24.5q-43 0 -76.5 -12t-71.5 -38l-36 61q43 32 86 46t100 14q93 0 147 -40.5t54 -106.5q0 -37 -18.5 -66t-53.5 -45v-4q48 -15 73.5 -51t25.5 -86q0 -42 -17 -75.5t-47 -57t-71.5 -36t-91.5 -12.5q-67 0 -116 13
+t-94 46l32 70q45 -29 83.5 -41.5t87.5 -12.5q31 0 57 5.5t45 17.5t29.5 32t10.5 49q0 95 -118 95h-108v70h97z" />
+    <glyph glyph-name="afii10074.sc" horiz-adv-x="640" 
+d="M155 560v-425l337 425h83v-560h-90v424l-337 -424h-83v560h90z" />
+    <glyph glyph-name="afii10075.sc" horiz-adv-x="640" 
+d="M155 560v-425l337 425h83v-560h-90v424l-337 -424h-83v560h90zM224 750q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="afii10076.77.liga" horiz-adv-x="1128" 
+d="M158 500v-185h124l70 123q22 38 52.5 54t72.5 16q34 0 55 -11l-16 -74q-19 4 -35 4q-24 0 -37.5 -11t-26.5 -32l-63 -107l98 -142q26 -37 52 -51t56 -14q26 0 43.5 8t31.5 23q16 18 24.5 44t13 59t5.5 71.5t1 80.5v144h384v-500h-92v426h-204v-80q0 -103 -14 -179.5
+t-59 -126.5q-20 -23 -58 -35.5t-76 -12.5q-40 0 -69 8t-50.5 22t-38 33t-31.5 42l-94 143h-119v-240h-93v500h93z" />
+    <glyph glyph-name="afii10076.sc" horiz-adv-x="569" 
+d="M156 560v-206h101l94 144q22 34 54.5 52t69.5 18q16 0 30.5 -1.5t30.5 -6.5l-16 -74q-7 2 -14.5 3t-15.5 1q-23 0 -38.5 -9t-32.5 -33l-87 -129l227 -319h-107l-196 280h-100v-280h-91v560h91z" />
+    <glyph glyph-name="afii10077.sc" horiz-adv-x="662" 
+d="M597 560v-560h-91v486h-227v-118q0 -97 -14 -169t-37 -118t-61.5 -67.5t-84.5 -21.5q-21 0 -37.5 2.5t-31.5 9.5l12 76q25 -9 48 -9q24 0 42.5 10.5t31.5 37.5q19 40 30.5 108t11.5 176v157h408z" />
+    <glyph glyph-name="afii10078.sc" horiz-adv-x="712" 
+d="M171 560l185 -259l187 259h103v-560h-89v437h-5l-180 -249h-34l-180 249h-5v-437h-88v560h106z" />
+    <glyph glyph-name="afii10079.sc" horiz-adv-x="623" 
+d="M156 560v-230h311v230h91v-560h-91v254h-311v-254h-91v560h91z" />
+    <glyph glyph-name="afii10080.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93z" />
+    <glyph glyph-name="afii10081.sc" horiz-adv-x="625" 
+d="M560 560v-560h-91v484h-313v-484h-91v560h495z" />
+    <glyph glyph-name="afii10082.sc" horiz-adv-x="543" 
+d="M298 560q46 0 84 -13t65 -36.5t41.5 -55.5t14.5 -71q0 -44 -16 -78t-44.5 -57t-67 -34.5t-83.5 -11.5h-137v-203h-90v560h233zM155 278h130q60 0 93 23.5t33 82.5q0 55 -32.5 78t-104.5 23h-119v-207z" />
+    <glyph glyph-name="afii10083.sc" horiz-adv-x="582" 
+d="M497 437q-36 31 -76.5 43t-86.5 12q-50 0 -89 -15.5t-65 -43.5t-39.5 -67t-13.5 -85q0 -50 16 -89t44 -66t66.5 -41t83.5 -14q51 0 93 11.5t82 40.5l25 -77q-46 -32 -95 -44t-114 -12q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65 91.5t96.5 58.5
+t117.5 20.5q63 0 108.5 -15t90.5 -47z" />
+    <glyph glyph-name="afii10084.sc" horiz-adv-x="496" 
+d="M466 560v-75h-172v-485h-92v485h-172v75h436z" />
+    <glyph glyph-name="afii10085.sc" horiz-adv-x="629" 
+d="M128 560l203 -444h5l171 444h97l-218 -532q-32 -78 -70 -116.5t-100 -38.5q-23 0 -40 5t-32 16l10 74q14 -9 27 -12.5t25 -3.5q29 0 49.5 13t34.5 53l-265 542h103z" />
+    <glyph glyph-name="afii10086.sc" horiz-adv-x="775" 
+d="M433 570v-71q17 4 36 5.5t39 1.5q53 0 95 -18t71.5 -48t45 -70t15.5 -85t-15.5 -85.5t-45 -70.5t-71.5 -48t-95 -18q-21 0 -40 2.5t-35 7.5v-73h-90v73q-17 -5 -35 -7.5t-41 -2.5q-53 0 -95 18t-71.5 48t-45 70.5t-15.5 85.5t15.5 85t45 70t71.5 48t95 18q22 0 40.5 -2
+t35.5 -5v71h90zM343 426q-32 7 -77 7q-65 0 -100.5 -39t-35.5 -109q0 -71 35.5 -109t100.5 -38q25 0 42.5 1.5t34.5 6.5v280zM433 146q17 -5 34.5 -6.5t42.5 -1.5q64 0 99.5 38t35.5 109q0 70 -35.5 109t-99.5 39q-21 0 -41 -1.5t-36 -5.5v-280z" />
+    <glyph glyph-name="afii10087.sc" horiz-adv-x="623" 
+d="M149 560l164 -222l160 222h109l-209 -270l225 -290h-113l-175 241l-176 -241h-109l224 288l-210 272h110z" />
+    <glyph glyph-name="afii10088.sc" horiz-adv-x="626" 
+d="M65 560h90v-485h299v485h90v-485h57l-8 -207h-73l-8 132h-447v560z" />
+    <glyph glyph-name="afii10089.sc" horiz-adv-x="575" 
+d="M419 0v176q-34 -6 -61 -8.5t-63 -2.5q-48 0 -92.5 12t-78 40t-54 74.5t-20.5 115.5v153h90v-152q0 -42 9.5 -73.5t31.5 -52.5t58 -31.5t90 -10.5q26 0 46.5 2t43.5 7v311h91v-560h-91z" />
+    <glyph glyph-name="afii10090.sc" horiz-adv-x="850" 
+d="M155 560v-485h225v485h90v-485h225v485h90v-560h-720v560h90z" />
+    <glyph glyph-name="afii10091.sc" horiz-adv-x="861" 
+d="M155 560v-485h222v485h90v-485h222v485h90v-485h57l-8 -207h-73l-8 132h-682v560h90z" />
+    <glyph glyph-name="afii10092.sc" horiz-adv-x="618" 
+d="M227 560v-209h156q38 0 73.5 -11.5t62.5 -34t43 -55.5t16 -75q0 -41 -17 -73t-44.5 -55t-63 -35t-72.5 -12h-245v485l-111 4v71h202zM227 75h134q27 0 50.5 5.5t40.5 17.5t26.5 31t9.5 47q0 30 -10.5 49.5t-28.5 31t-41.5 15.5t-48.5 4h-132v-201z" />
+    <glyph glyph-name="afii10093.sc" horiz-adv-x="731" 
+d="M575 560h91v-560h-91v560zM156 560v-209h160q38 0 73.5 -11.5t62.5 -34t43 -55.5t16 -75q0 -41 -17 -73t-44.5 -55t-63 -35t-72.5 -12h-249v560h91zM156 75h138q26 0 49 5.5t40.5 17.5t27.5 31t10 47q0 30 -11 49.5t-29.5 31t-41.5 15.5t-47 4h-136v-201z" />
+    <glyph glyph-name="afii10094.sc" horiz-adv-x="551" 
+d="M156 560v-209h160q38 0 73.5 -11.5t62.5 -34t43 -55.5t16 -75q0 -41 -17 -73t-44.5 -55t-63 -35t-72.5 -12h-249v560h91zM156 75h138q26 0 49 5.5t40.5 17.5t27.5 31t10 47q0 30 -11 49.5t-29.5 31t-41.5 15.5t-47 4h-136v-201z" />
+    <glyph glyph-name="afii10095.sc" horiz-adv-x="582" 
+d="M448 325q-9 77 -61.5 124t-143.5 47q-46 0 -87.5 -12.5t-80.5 -41.5l-35 66q45 32 92.5 47t106.5 15q70 0 126 -20t95.5 -57.5t60.5 -91t21 -120.5q0 -71 -24 -125.5t-64 -91.5t-93 -55.5t-111 -18.5q-34 0 -62.5 2.5t-54 8.5t-49.5 16.5t-49 25.5l25 76q24 -16 45 -26.5
+t42 -16.5t44 -8.5t50 -2.5q91 0 146.5 49t61.5 137h-295v74h294z" />
+    <glyph glyph-name="afii10096.sc" horiz-adv-x="877" 
+d="M156 560v-232h97q7 56 32.5 101t63.5 76.5t88 48t107 16.5q63 0 116.5 -20t93 -57.5t61.5 -91.5t22 -121t-22 -120.5t-61.5 -91t-93 -58t-116.5 -20.5q-65 0 -118 19t-90.5 53.5t-59.5 83.5t-25 108h-95v-254h-91v560h91zM343 280q0 -52 15.5 -92t42.5 -67.5t63.5 -41.5
+t79.5 -14t79.5 14t63.5 41.5t42.5 67.5t15.5 92t-15.5 92t-42.5 67.5t-63.5 41.5t-79.5 14q-44 0 -80.5 -14t-63.5 -41.5t-42 -67.5t-15 -92z" />
+    <glyph glyph-name="afii10097.sc" horiz-adv-x="572" 
+d="M507 0h-90v223h-117l-165 -223h-105l176 231q-60 14 -97.5 56t-37.5 108q0 36 15 66.5t42 52t64 34t81 12.5h234v-560zM295 485q-63 0 -98 -19.5t-35 -70.5q0 -55 34 -76t88 -21h133v187h-122z" />
+    <glyph glyph-name="afii10098.sc" horiz-adv-x="451" 
+d="M338 560l9 90h73l6 -166h-270v-484h-91v560h273z" />
+    <glyph glyph-name="afii10099.sc" horiz-adv-x="718" 
+d="M492 560v-75h-195v-123q29 18 69.5 28t81.5 10q100 0 155 -54t55 -148v-145q0 -46 -10.5 -82t-29.5 -63t-44 -44.5t-53 -25.5l-42 64q43 20 66 57t23 93v139q0 71 -34.5 100.5t-94.5 29.5q-34 0 -73 -9t-69 -29v-283h-91v485h-176v75h462z" />
+    <glyph glyph-name="afii10100.sc" horiz-adv-x="451" 
+d="M426 560v-76h-270v-484h-91v560h361zM309 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="afii10101.sc" horiz-adv-x="582" 
+d="M431 251h-303q5 -43 22.5 -76.5t45 -56.5t63.5 -35t78 -12q51 0 93 12t82 41l25 -78q-46 -32 -95 -44t-114 -12q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65 91.5t96.5 58.5t117.5 20.5q63 0 108.5 -15t90.5 -47l-40 -71q-36 31 -76.5 43t-86.5 12
+q-89 0 -140.5 -46t-63.5 -121h301v-74z" />
+    <glyph glyph-name="afii10102.sc" horiz-adv-x="500" 
+d="M394 447q-41 28 -74.5 39t-75.5 11q-54 0 -78 -21.5t-24 -52.5t16.5 -46.5t51.5 -24.5l93 -24q37 -10 66 -23.5t49.5 -33.5t31 -47.5t10.5 -66.5q0 -35 -13.5 -65.5t-40.5 -53t-67.5 -35.5t-95.5 -13q-32 0 -58.5 2.5t-50.5 8.5t-47 15.5t-47 24.5l31 80
+q24 -15 44.5 -25.5t40.5 -17t42 -9.5t49 -3q63 0 95 23.5t32 66.5q0 42 -23 61.5t-76 32.5l-101 24q-59 14 -89.5 51t-30.5 96q0 66 50 107.5t137 41.5q58 0 105 -12.5t86 -39.5z" />
+    <glyph glyph-name="afii10103.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91z" />
+    <glyph glyph-name="afii10104.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91zM1 749q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM220 749q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="afii10105.sc" horiz-adv-x="231" 
+d="M166 560v-548q0 -45 -13 -86t-39.5 -73.5t-66.5 -54.5t-94 -28l-24 77q76 13 111 52.5t35 119.5v541h91z" />
+    <glyph glyph-name="afii10106.sc" horiz-adv-x="942" 
+d="M567 560v-209h140q38 0 73.5 -11.5t62.5 -34t43 -55.5t16 -75q0 -41 -17 -73t-44.5 -55t-63 -35t-72.5 -12h-229v486h-197v-118q0 -97 -14 -169t-37 -118t-61.5 -67.5t-84.5 -21.5q-21 0 -37.5 2.5t-31.5 9.5l12 76q25 -9 48 -9q24 0 42.5 10.5t31.5 37.5q19 40 30.5 108
+t11.5 176v157h378zM567 75h118q26 0 49 5.5t40.5 17.5t27.5 31t10 47q0 30 -11 49.5t-29.5 31t-41.5 15.5t-47 4h-116v-201z" />
+    <glyph glyph-name="afii10107.sc" horiz-adv-x="903" 
+d="M156 560v-209h281v209h91v-209h140q38 0 73.5 -11.5t62.5 -34t43 -55.5t16 -75q0 -41 -17 -73t-44.5 -55t-63 -35t-72.5 -12h-229v276h-281v-276h-91v560h91zM528 75h118q26 0 49 5.5t40.5 17.5t27.5 31t10 47q0 30 -11 49.5t-29.5 31t-41.5 15.5t-47 4h-116v-201z" />
+    <glyph glyph-name="afii10108.sc" horiz-adv-x="718" 
+d="M492 560v-75h-195v-123q29 18 69.5 28t81.5 10q100 0 155 -54t55 -148v-198h-90v191q0 71 -34.5 100.5t-94.5 29.5q-34 0 -73 -9t-69 -29v-283h-91v485h-176v75h462z" />
+    <glyph glyph-name="afii10109.sc" horiz-adv-x="569" 
+d="M156 560v-206h101l94 144q22 34 54.5 52t69.5 18q16 0 30.5 -1.5t30.5 -6.5l-16 -74q-7 2 -14.5 3t-15.5 1q-23 0 -38.5 -9t-32.5 -33l-87 -129l227 -319h-107l-196 280h-100v-280h-91v560h91zM326 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="afii10110.sc" horiz-adv-x="629" 
+d="M128 560l203 -444h5l171 444h97l-218 -532q-32 -78 -70 -116.5t-100 -38.5q-23 0 -40 5t-32 16l10 74q14 -9 27 -12.5t25 -3.5q29 0 49.5 13t34.5 53l-265 542h103zM218 740q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27
+t-57.5 78z" />
+    <glyph glyph-name="afii10193.sc" horiz-adv-x="615" 
+d="M263 0h-198v560h91v-484h303v484h91v-560h-198l-9 -132h-71z" />
+    <glyph glyph-name="agrave.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM322 563h-64l-103 136l73 47z" />
+    <glyph glyph-name="agrave.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM347 619h-69l-98 136l78 47z" />
+    <glyph glyph-name="amacron.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM447 642v-70h-314v70h314z" />
+    <glyph glyph-name="amacron.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM484 705v-73h-326v73h326z" />
+    <glyph glyph-name="ampersand.sc" horiz-adv-x="663" 
+d="M617 384v-67h-44q-2 -66 -13 -112.5t-34 -84.5q26 -24 53 -35t59 -12l-6 -79q-50 0 -87 15t-73 47q-42 -35 -91 -50.5t-102 -15.5q-113 0 -176 50t-63 147q0 54 29.5 93t79.5 64q-21 20 -30 42.5t-9 51.5q0 26 13 50t37 42t57.5 29t74.5 11q46 0 83.5 -9.5t81.5 -35.5
+l-39 -65q-30 22 -58.5 31.5t-63.5 9.5q-51 0 -77 -19t-26 -48q0 -23 10 -38.5t32 -34.5l229 -191q18 35 24 66t6 72l-81 19l8 57h196zM197 293q-32 -16 -49.5 -42.5t-17.5 -62.5q0 -31 11.5 -54.5t32 -39t47.5 -23.5t57 -8q47 0 78 11t59 34z" />
+    <glyph glyph-name="aogonek.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-30 -15 -39.5 -31.5t-9.5 -35.5q0 -16 9 -26.5t35 -10.5q14 0 29 4l-17 -65q-9 -2 -17.5 -3t-16.5 -1q-51 0 -72.5 24t-21.5 59q0 22 10 43.5t33 37.5q-26 5 -41 24.5
+t-17 52.5q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5
+q50 0 92.5 15t84.5 51v106z" />
+    <glyph glyph-name="aogonek.sc" horiz-adv-x="642" 
+d="M375 560l242 -560q-32 -17 -43 -34.5t-11 -37.5q0 -17 10.5 -30.5t38.5 -13.5q7 0 14.5 1t16.5 4l-16 -65q-10 -2 -19.5 -3t-17.5 -1q-48 0 -76 24.5t-28 62.5q0 25 13.5 51.5t43.5 41.5h-20l-71 162h-267l-68 -162h-92l244 560h106zM216 239h205l-98 244h-9z" />
+    <glyph glyph-name="approxequal.case" horiz-adv-x="437" 
+d="M40 219q22 24 49 36t63 12q25 0 40.5 -6t28.5 -13.5t26 -13.5t33 -6q21 0 35.5 7t31.5 24l41 -50q-23 -29 -48 -42.5t-59 -13.5q-23 0 -40 6.5t-32 14t-30 14t-33 6.5q-26 0 -44 -9.5t-34 -29.5zM40 357q22 24 49 36t63 12q25 0 40.5 -6t28.5 -13.5t26 -13.5t33 -6
+q21 0 35.5 7t31.5 24l41 -50q-23 -29 -48 -42.5t-59 -13.5q-23 0 -40 6.5t-32 14t-30 14t-33 6.5q-26 0 -44 -9.5t-34 -29.5z" />
+    <glyph glyph-name="aring.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM294 549q-21 0 -40.5 6.5t-35 19.5t-24.5 32t-9 45q0 23 8.5 42t23.5 32.5t35 21t42 7.5q20 0 39.5 -7
+t35 -20t25 -32t9.5 -44q0 -26 -9 -45.5t-24 -32t-35 -19t-41 -6.5zM294 603q22 0 37 13t15 36q0 21 -14.5 34.5t-37.5 13.5q-21 0 -36.5 -13.5t-15.5 -34.5t14.5 -35t37.5 -14z" />
+    <glyph glyph-name="aring.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM321 595q-52 0 -84.5 31.5t-32.5 80.5q0 23 9 43.5t24.5 35.5t37 23.5t46.5 8.5t46.5 -8.5t37 -23.5t24.5 -35.5t9 -43.5q0 -50 -33 -81t-84 -31zM377 707q0 24 -16 39.5t-40 15.5
+t-40.5 -15.5t-16.5 -39.5t16.5 -40t40.5 -16t40 16t16 40z" />
+    <glyph glyph-name="asciitilde.case" horiz-adv-x="445" 
+d="M30 276q23 25 51.5 37.5t67.5 12.5q26 0 44.5 -6t34.5 -13.5t32.5 -13.5t37.5 -6q23 0 39.5 7t33.5 24l41 -50q-26 -31 -51.5 -44.5t-63.5 -13.5q-22 0 -41.5 6.5t-37.5 15t-36.5 15t-38.5 6.5q-29 0 -49.5 -9.5t-35.5 -29.5z" />
+    <glyph glyph-name="at.case" horiz-adv-x="769" 
+d="M496 198q-47 -59 -128 -59q-78 0 -121 48t-43 124q0 33 13 63.5t36.5 53.5t56.5 36.5t73 13.5q33 0 57.5 -8.5t47.5 -25.5l24 24l35 -7v-187q0 -30 12.5 -45t35.5 -15q30 0 43.5 30t13.5 81q0 57 -19.5 101.5t-53.5 75t-80 46.5t-99 16q-58 0 -108.5 -17t-88 -50t-59 -80
+t-21.5 -107q0 -59 19 -106t53 -79t81.5 -49t103.5 -17q64 0 116.5 11t95.5 35l24 -70q-53 -23 -110 -35.5t-126 -12.5t-131 20.5t-108.5 61t-73.5 101t-27 140.5q0 74 28 134.5t77 103t114.5 66t140.5 23.5q67 0 127.5 -21.5t106 -61.5t72.5 -98t27 -131q0 -81 -37.5 -127.5
+t-106.5 -46.5q-67 0 -93 47zM474 388q-19 15 -39.5 20t-46.5 5q-50 0 -77 -27.5t-27 -75.5t22.5 -74.5t67.5 -26.5q64 0 100 45v134z" />
+    <glyph glyph-name="atilde.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM110 625q43 48 114 48q25 0 41.5 -6t30.5 -13.5t28 -13.5t34 -6q21 0 36 7t32 24l41 -46
+q-23 -29 -48.5 -42.5t-59.5 -13.5q-23 0 -40.5 6.5t-34 14t-32.5 14t-34 6.5q-26 0 -45 -9.5t-35 -29.5z" />
+    <glyph glyph-name="atilde.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM140 686q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6q-49 0 -83 -39
+z" />
+    <glyph glyph-name="b.sc" horiz-adv-x="526" 
+d="M281 560q34 0 66 -10t56 -29t38.5 -47t14.5 -64q0 -32 -16 -61t-46 -45v-4q45 -16 68.5 -53.5t23.5 -83.5q0 -39 -15.5 -69t-41.5 -51t-59.5 -32t-70.5 -11h-234v560h216zM155 72h121q27 0 49.5 4t38.5 14.5t24.5 28t8.5 43.5q0 42 -25.5 67.5t-73.5 25.5h-143v-183z
+M155 325h115q18 0 35.5 4t31 13t21.5 24.5t8 38.5q0 82 -106 82h-105v-162z" />
+    <glyph glyph-name="backslash.case" horiz-adv-x="302" 
+d="M302 -70l-77 -25l-241 697l75 25z" />
+    <glyph glyph-name="braceleft.case" horiz-adv-x="301" 
+d="M271 604h-33q-50 0 -50 -55v-185q0 -33 -13 -61t-38 -38v-4q23 -9 37 -36t14 -59v-189q0 -54 50 -54h33v-73h-42q-59 0 -94.5 33.5t-35.5 96.5v206q0 22 -11 31t-29 9h-29v74h29q18 0 29 9.5t11 30.5v206q0 63 35 96.5t95 33.5h42v-72z" />
+    <glyph glyph-name="braceright.case" horiz-adv-x="301" 
+d="M30 -75h33q50 0 50 54v185q0 33 13 61t38 38v4q-23 9 -37 36t-14 59v189q0 55 -50 55h-33v72h42q59 0 94.5 -33.5t35.5 -96.5v-206q0 -22 11 -31t29 -9h29v-74h-29q-18 0 -29 -9.5t-11 -30.5v-206q0 -63 -35 -96.5t-95 -33.5h-42v73z" />
+    <glyph glyph-name="bracketleft.case" horiz-adv-x="253" 
+d="M144 -77h74v-73h-163v826h163v-72h-74v-681z" />
+    <glyph glyph-name="bracketright.case" horiz-adv-x="253" 
+d="M129 604h-74v72h163v-826h-163v73h74v681z" />
+    <glyph glyph-name="bullet.case" horiz-adv-x="254" 
+d="M204 250q0 -32 -21.5 -53t-55.5 -21q-35 0 -56 21t-21 53t21.5 53t55.5 21q35 0 56 -21t21 -53z" />
+    <glyph glyph-name="c.sc" horiz-adv-x="582" 
+d="M497 437q-36 31 -76.5 43t-86.5 12q-50 0 -89 -15.5t-65 -43.5t-39.5 -67t-13.5 -85q0 -50 16 -89t44 -66t66.5 -41t83.5 -14q51 0 93 11.5t82 40.5l25 -77q-46 -32 -95 -44t-114 -12q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65 91.5t96.5 58.5
+t117.5 20.5q63 0 108.5 -15t90.5 -47z" />
+    <glyph glyph-name="cacute.sc" horiz-adv-x="582" 
+d="M497 437q-36 31 -76.5 43t-86.5 12q-50 0 -89 -15.5t-65 -43.5t-39.5 -67t-13.5 -85q0 -50 16 -89t44 -66t66.5 -41t83.5 -14q51 0 93 11.5t82 40.5l25 -77q-46 -32 -95 -44t-114 -12q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65 91.5t96.5 58.5
+t117.5 20.5q63 0 108.5 -15t90.5 -47zM381 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="cb.liga" horiz-adv-x="1168" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19q13 0 25 -0.5t23 -1.5
+q-15 30 -15 77q0 35 13.5 64t38 50t58 32.5t73.5 11.5q47 0 84.5 -6.5t79.5 -21.5v-249q38 23 79.5 33.5t92.5 10.5q122 0 185.5 -64t63.5 -191q0 -60 -22.5 -109t-62 -83.5t-93.5 -53.5t-118 -19q-35 0 -64 3t-54.5 9.5t-49.5 16.5t-50 24v617q-38 11 -79 11
+q-46 0 -71 -23.5t-25 -64.5q0 -33 14 -60.5t57 -50.5zM707 95q17 -8 31.5 -13t28.5 -8t30 -4.5t36 -1.5q92 0 146.5 47.5t54.5 140.5q0 90 -39 134.5t-124 44.5q-48 0 -88.5 -11t-75.5 -36v-293z" />
+    <glyph glyph-name="ccaron.sc" horiz-adv-x="582" 
+d="M497 437q-36 31 -76.5 43t-86.5 12q-50 0 -89 -15.5t-65 -43.5t-39.5 -67t-13.5 -85q0 -50 16 -89t44 -66t66.5 -41t83.5 -14q51 0 93 11.5t82 40.5l25 -77q-46 -32 -95 -44t-114 -12q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65 91.5t96.5 58.5
+t117.5 20.5q63 0 108.5 -15t90.5 -47zM333 699l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="ccedilla.sc" horiz-adv-x="582" 
+d="M497 437q-36 31 -76.5 43t-86.5 12q-50 0 -89 -15.5t-65 -43.5t-39.5 -67t-13.5 -85q0 -50 16 -89t44 -66t66.5 -41t83.5 -14q51 0 93 11.5t82 40.5l25 -77q-43 -30 -88.5 -42t-103.5 -14l-5 -38q35 0 54 -18.5t19 -51.5q0 -28 -14.5 -49.5t-37 -37t-50 -25.5t-52.5 -14
+l-13 61q41 7 68 21.5t27 41.5t-36 27h-33l3 87q-48 7 -91.5 28t-76.5 56.5t-52.5 86t-19.5 116.5q0 65 24 118.5t65 91.5t96.5 58.5t117.5 20.5q63 0 108.5 -15t90.5 -47z" />
+    <glyph glyph-name="ccircumflex.sc" horiz-adv-x="582" 
+d="M497 437q-36 31 -76.5 43t-86.5 12q-50 0 -89 -15.5t-65 -43.5t-39.5 -67t-13.5 -85q0 -50 16 -89t44 -66t66.5 -41t83.5 -14q51 0 93 11.5t82 40.5l25 -77q-46 -32 -95 -44t-114 -12q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65 91.5t96.5 58.5
+t117.5 20.5q63 0 108.5 -15t90.5 -47zM332 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="cdotaccent.sc" horiz-adv-x="582" 
+d="M497 437q-36 31 -76.5 43t-86.5 12q-50 0 -89 -15.5t-65 -43.5t-39.5 -67t-13.5 -85q0 -50 16 -89t44 -66t66.5 -41t83.5 -14q51 0 93 11.5t82 40.5l25 -77q-46 -32 -95 -44t-114 -12q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65 91.5t96.5 58.5
+t117.5 20.5q63 0 108.5 -15t90.5 -47zM338 747q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="cent.taboldstyle" 
+d="M489 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-38 -20 -76 -30t-81 -13v-75h-84v75q-54 6 -97.5 27.5t-74 55t-47 77.5t-16.5 95q0 52 18 96t49.5 77.5t74.5 55.5t93 30v71h84v-68
+q47 -2 82.5 -11t68.5 -26z" />
+    <glyph glyph-name="centoldstyle" horiz-adv-x="548" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-38 -20 -76 -30t-81 -13v-75h-84v75q-54 6 -97.5 27.5t-74 55t-47 77.5t-16.5 95q0 52 18 96t49.5 77.5t74.5 55.5t93 30v71h84v-68
+q47 -2 82.5 -11t68.5 -26z" />
+    <glyph glyph-name="ch.liga" horiz-adv-x="1155" 
+d="M471 403q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19q13 0 25 -0.5t23 -1.5
+q-15 30 -15 77q0 35 13.5 64t38 50t58 32.5t73.5 11.5q47 0 84.5 -6.5t79.5 -21.5v-287q35 44 82.5 63t98.5 19q104 0 155.5 -56.5t51.5 -162.5v-291h-93v292q0 66 -25 104t-89 38q-56 0 -100.5 -22t-80.5 -66v-346h-93v660q-18 7 -38 9t-41 2q-46 0 -71 -23.5t-25 -64.5
+q0 -33 14 -60.5t57 -50.5z" />
+    <glyph glyph-name="ck.liga" horiz-adv-x="1129" 
+d="M1070 500l2 -10l-217 -189l227 -293l-3 -8h-105l-190 255l-77 -43v-212h-92v660q-19 6 -39 8.5t-41 2.5q-46 0 -71 -23.5t-25 -64.5q0 -33 14 -60.5t57 -50.5l-39 -69q-33 17 -66.5 25t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13
+q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19q13 0 25 -0.5t23 -1.5q-15 30 -15 77q0 35 13.5 64t38 50t58 32.5t73.5 11.5q47 0 84.5 -6.5t79.5 -21.5v-423l80 53l174 155h109z
+" />
+    <glyph glyph-name="comma.denominator" horiz-adv-x="139" 
+d="M20 -63q16 14 25.5 28.5t9.5 30.5q-34 6 -34 43q0 21 12.5 34.5t36.5 13.5q26 0 37.5 -14.5t11.5 -35.5q0 -75 -60 -119z" />
+    <glyph glyph-name="comma.numerator" horiz-adv-x="139" 
+d="M20 217q16 14 25.5 28.5t9.5 30.5q-34 6 -34 43q0 21 12.5 34.5t36.5 13.5q26 0 37.5 -14.5t11.5 -35.5q0 -75 -60 -119z" />
+    <glyph glyph-name="commaaccent" horiz-adv-x="231" 
+d="M45 -70q30 14 46 30.5t16 39.5q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="commaaccent.cap" horiz-adv-x="231" 
+d="M45 -68q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="commainferior" horiz-adv-x="139" 
+d="M20 -132q16 14 25.5 28.5t9.5 30.5q-34 6 -34 43q0 21 12.5 34.5t36.5 13.5q26 0 37.5 -14.5t11.5 -35.5q0 -75 -60 -119z" />
+    <glyph glyph-name="commasuperior" horiz-adv-x="139" 
+d="M20 312q16 14 25.5 28.5t9.5 30.5q-34 6 -34 43q0 21 12.5 34.5t36.5 13.5q26 0 37.5 -14.5t11.5 -35.5q0 -75 -60 -119z" />
+    <glyph glyph-name="copyright.case" horiz-adv-x="664" 
+d="M414 326q-15 11 -34 15.5t-39 4.5q-92 0 -92 -88q0 -45 26.5 -66.5t63.5 -21.5q28 0 46.5 5t42.5 20l14 -57q-26 -15 -50 -21.5t-53 -6.5q-74 0 -117.5 38t-43.5 110q0 36 13 63.5t35.5 46t52.5 28t64 9.5q27 0 53.5 -7.5t48.5 -21.5zM623 259q0 -63 -22.5 -114
+t-61.5 -88t-92 -57t-114 -20q-62 0 -115 19.5t-93 56t-62.5 88t-22.5 115.5t22.5 115.5t62.5 88t93 56t115 19.5q61 0 114 -20t92 -57t61.5 -88t22.5 -114zM115 259q0 -52 17.5 -92t47.5 -67.5t69.5 -42t83.5 -14.5t83.5 14.5t69.5 42t47.5 67.5t17.5 92t-17.5 92
+t-47.5 67.5t-69.5 42t-83.5 14.5t-83.5 -14.5t-69.5 -42t-47.5 -67.5t-17.5 -92z" />
+    <glyph glyph-name="ct.liga" horiz-adv-x="977" 
+d="M926 423l-196 8v-262q0 -57 26 -81t67 -24q33 0 59.5 9t54.5 26l30 -67q-31 -22 -67.5 -32t-86.5 -10q-86 0 -131.5 45t-45.5 133v255l-68 11v35q51 21 67 51t16 63q0 41 -28.5 68.5t-79.5 27.5q-46 0 -75 -27.5t-29 -68.5q0 -39 15 -66t56 -45l-39 -69q-33 17 -66.5 25
+t-71.5 8q-94 0 -146.5 -48t-52.5 -139q0 -41 14.5 -75t40 -58t58.5 -37t71 -13q93 0 170 42l28 -73q-48 -26 -96 -35.5t-107 -9.5q-63 0 -113.5 19.5t-86 54t-54.5 81.5t-19 102q0 59 22.5 107.5t61.5 83t91 53.5t112 19q13 0 25 -0.5t23 -1.5q-15 30 -15 77q0 35 15 64
+t40.5 50t59 32.5t69.5 11.5q43 0 78 -13t59.5 -34.5t37.5 -49t13 -56.5q0 -27 -4.5 -48.5t-13.5 -41.5h215z" />
+    <glyph glyph-name="currency.taboldstyle" 
+d="M208 410q37 22 86 22t86 -22l48 51l56 -54l-50 -47q12 -20 18.5 -39t6.5 -44q0 -24 -6.5 -43t-18.5 -39l49 -47l-55 -55l-47 52q-37 -23 -87 -23q-49 0 -87 22l-47 -52l-54 56l49 46q-12 20 -18.5 39t-6.5 44t6.5 44.5t18.5 39.5l-49 48l55 53zM382 277q0 38 -26.5 61.5
+t-61.5 23.5q-17 0 -33 -6t-28 -17.5t-19 -27t-7 -34.5t7.5 -35t19.5 -27t27.5 -17.5t32.5 -6.5t33 6.5t28 17.5t19.5 27t7.5 35z" />
+    <glyph glyph-name="d.sc" horiz-adv-x="618" 
+d="M266 560q67 0 124 -18t99 -53t65.5 -86.5t23.5 -118.5q0 -69 -23.5 -122t-65.5 -89t-99.5 -54.5t-124.5 -18.5h-200v560h201zM157 75h117q46 0 85 13t67 39t43.5 65t15.5 92q0 52 -15.5 90t-44 63t-69 37t-89.5 12h-110v-411z" />
+    <glyph glyph-name="dblGrave" horiz-adv-x="375" 
+d="M320 698h-64l-69 159l82 30zM215 698h-67l-93 132l76 47z" />
+    <glyph glyph-name="dblgrave" horiz-adv-x="375" 
+d="M320 563h-64l-69 153l82 30zM215 563h-67l-93 126l76 47z" />
+    <glyph glyph-name="dcaron.sc" horiz-adv-x="618" 
+d="M266 560q67 0 124 -18t99 -53t65.5 -86.5t23.5 -118.5q0 -69 -23.5 -122t-65.5 -89t-99.5 -54.5t-124.5 -18.5h-200v560h201zM157 75h117q46 0 85 13t67 39t43.5 65t15.5 92q0 52 -15.5 90t-44 63t-69 37t-89.5 12h-110v-411zM267 699l132 86l21 -54l-141 -119h-23
+l-142 119l21 54z" />
+    <glyph glyph-name="dcroat.sc" horiz-adv-x="618" 
+d="M65 323v237h201q67 0 124 -18t99 -53t65.5 -86.5t23.5 -118.5q0 -69 -23.5 -122t-65.5 -89t-99.5 -54.5t-124.5 -18.5h-200v250h-60v73h60zM157 323h146v-73h-146v-175h117q46 0 85 13t67 39t43.5 65t15.5 92q0 52 -15.5 90t-44 63t-69 37t-89.5 12h-110v-163z" />
+    <glyph glyph-name="divide.case" horiz-adv-x="381" 
+d="M190 178q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19zM340 308v-76h-300v76h300zM190 491q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="dollar.sc" horiz-adv-x="503" 
+d="M213 -8q-46 2 -86 14.5t-80 38.5l35 76q49 -33 88.5 -44t85.5 -11q63 0 97 22t34 62q0 41 -25 60.5t-78 32.5l-101 26q-59 14 -89.5 49.5t-30.5 92.5q0 60 39 98t111 46v60h84v-61q41 -5 75.5 -17.5t66.5 -35.5l-40 -66q-22 14 -40.5 23.5t-36 15t-36 8t-39.5 2.5
+q-54 0 -76.5 -20t-22.5 -51t18 -44t53 -22l93 -24q36 -9 65.5 -22.5t50.5 -33.5t33 -47.5t12 -64.5q0 -65 -44.5 -109.5t-131.5 -53.5v-72h-84v72z" />
+    <glyph glyph-name="dollar.tab.sc" 
+d="M248 -8q-46 2 -86 14.5t-80 38.5l35 76q49 -33 88.5 -44t85.5 -11q63 0 97 22t34 62q0 41 -25 60.5t-78 32.5l-101 26q-59 14 -89.5 49.5t-30.5 92.5q0 60 39 98t111 46v60h84v-61q41 -5 75.5 -17.5t66.5 -35.5l-40 -66q-22 14 -40.5 23.5t-36 15t-36 8t-39.5 2.5
+q-54 0 -76.5 -20t-22.5 -51t18 -44t53 -22l93 -24q36 -9 65.5 -22.5t50.5 -33.5t33 -47.5t12 -64.5q0 -65 -44.5 -109.5t-131.5 -53.5v-72h-84v72z" />
+    <glyph glyph-name="dollar.taboldstyle" 
+d="M329 577v-68q37 -2 71 -9.5t73 -23.5l-32 -73q-39 19 -70.5 26.5t-71.5 7.5q-66 0 -95.5 -18t-29.5 -47q0 -20 10 -32t27.5 -19.5t42 -11.5t53.5 -7q47 -5 84 -15.5t62 -29t38 -46.5t13 -68q0 -66 -46.5 -103t-128.5 -46v-77h-84v73q-46 2 -81.5 8.5t-74.5 23.5l28 75
+q38 -18 72 -25t73 -7q79 0 116.5 19t37.5 60q0 20 -7 33t-22.5 21t-41.5 13t-65 9q-98 10 -146 42.5t-48 102.5q0 52 39 91t120 50v71h84z" />
+    <glyph glyph-name="dollaroldstyle" horiz-adv-x="502" 
+d="M285 577v-68q37 -2 71 -9.5t73 -23.5l-32 -73q-39 19 -70.5 26.5t-71.5 7.5q-66 0 -95.5 -18t-29.5 -47q0 -20 10 -32t27.5 -19.5t42 -11.5t53.5 -7q47 -5 84 -15.5t62 -29t38 -46.5t13 -68q0 -66 -46.5 -103t-128.5 -46v-77h-84v73q-46 2 -81.5 8.5t-74.5 23.5l28 75
+q38 -18 72 -25t73 -7q79 0 116.5 19t37.5 60q0 20 -7 33t-22.5 21t-41.5 13t-65 9q-98 10 -146 42.5t-48 102.5q0 52 39 91t120 50v71h84z" />
+    <glyph glyph-name="dotlessi.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91z" />
+    <glyph glyph-name="dotlessj.sc" horiz-adv-x="231" 
+d="M166 560v-548q0 -45 -13 -86t-39.5 -73.5t-66.5 -54.5t-94 -28l-24 77q76 13 111 52.5t35 119.5v541h91z" />
+    <glyph glyph-name="e.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560z" />
+    <glyph glyph-name="eacute.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM310 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="ebreve.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM168 750q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="ecaron.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM271 699l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="ecircumflex.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM265 698l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="edieresis.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM152 749q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM371 749q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="edotaccent.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM272 743q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="egrave.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM312 619h-69l-98 136l78 47z" />
+    <glyph glyph-name="eight.denominator" horiz-adv-x="380" 
+d="M156 179q-29 -5 -41.5 -21.5t-12.5 -44.5q0 -33 25 -48t63 -15q36 0 62 15.5t26 47.5q0 23 -11 36t-29 19.5t-39.5 8t-42.5 2.5zM217 232q20 6 28 16.5t8 29.5q0 24 -17 33.5t-45 9.5t-46 -8.5t-18 -33.5q0 -27 24.5 -36.5t65.5 -10.5zM91 208q-19 13 -27 31t-8 39
+q0 27 11.5 46t30 31t43 18t50.5 6q25 0 49 -6t43 -18t30 -30.5t11 -44.5q0 -20 -9 -38.5t-27 -29.5q34 -15 48 -39.5t14 -56.5q0 -33 -13.5 -57t-36 -39t-51 -22t-59.5 -7q-35 0 -64 7.5t-50.5 22.5t-33.5 38t-12 55q0 66 61 94z" />
+    <glyph glyph-name="eight.fitted" 
+d="M168 353q-32 17 -55 51.5t-23 79.5q0 40 15 73t42 56.5t64.5 36.5t83.5 13q41 0 78 -11.5t65 -33.5t44.5 -54t16.5 -73q0 -25 -7.5 -47t-18.5 -39.5t-25 -30.5t-27 -19v-4q52 -20 82 -61.5t30 -98.5q0 -49 -17 -87t-48 -64t-75 -39.5t-98 -13.5q-63 0 -108.5 15
+t-74.5 41.5t-42.5 63t-13.5 79.5q0 57 30.5 100.5t81.5 62.5v4zM295 383q56 0 83.5 28t27.5 75q0 43 -25.5 74t-85.5 31q-30 0 -51 -8.5t-34.5 -22.5t-20 -33t-6.5 -41q0 -21 7 -39.5t21 -32.5t35 -22.5t49 -8.5zM295 311q-68 0 -106.5 -32t-38.5 -93q0 -26 7.5 -48.5
+t25 -39.5t45 -26.5t67.5 -9.5q73 0 108 36t35 88q0 60 -36 92.5t-107 32.5z" />
+    <glyph glyph-name="eight.numerator" horiz-adv-x="380" 
+d="M156 459q-29 -5 -41.5 -21.5t-12.5 -44.5q0 -33 25 -48t63 -15q36 0 62 15.5t26 47.5q0 23 -11 36t-29 19.5t-39.5 8t-42.5 2.5zM217 512q20 6 28 16.5t8 29.5q0 24 -17 33.5t-45 9.5t-46 -8.5t-18 -33.5q0 -27 24.5 -36.5t65.5 -10.5zM91 488q-19 13 -27 31t-8 39
+q0 27 11.5 46t30 31t43 18t50.5 6q25 0 49 -6t43 -18t30 -30.5t11 -44.5q0 -20 -9 -38.5t-27 -29.5q34 -15 48 -39.5t14 -56.5q0 -33 -13.5 -57t-36 -39t-51 -22t-59.5 -7q-35 0 -64 7.5t-50.5 22.5t-33.5 38t-12 55q0 66 61 94z" />
+    <glyph glyph-name="eight.sc" horiz-adv-x="529" 
+d="M134 306q-29 17 -44 46.5t-15 68.5q0 32 13 59t37 47t59.5 31.5t80.5 11.5q41 0 76 -10.5t60.5 -29.5t39.5 -45.5t14 -57.5q0 -44 -19.5 -75t-45.5 -46v-5q44 -16 69 -51.5t25 -83.5q0 -42 -17 -75t-46 -55.5t-69.5 -34t-86.5 -11.5q-110 0 -165 48.5t-55 123.5
+q0 48 24.5 86t64.5 53v5zM265 332q48 0 74.5 23.5t26.5 63.5q0 38 -26.5 61t-74.5 23t-74.5 -23t-26.5 -61q0 -35 26 -61t75 -26zM265 264q-55 0 -92.5 -25t-37.5 -77q0 -45 34.5 -73.5t95.5 -28.5q28 0 51.5 7.5t41 21t27 32t9.5 41.5q0 52 -36 77t-93 25z" />
+    <glyph glyph-name="eight.tab.sc" 
+d="M164 306q-29 17 -44 46.5t-15 68.5q0 32 13 59t37 47t59.5 31.5t80.5 11.5q41 0 76 -10.5t60.5 -29.5t39.5 -45.5t14 -57.5q0 -44 -19.5 -75t-45.5 -46v-5q44 -16 69 -51.5t25 -83.5q0 -42 -17 -75t-46 -55.5t-69.5 -34t-86.5 -11.5q-110 0 -165 48.5t-55 123.5
+q0 48 24.5 86t64.5 53v5zM295 332q48 0 74.5 23.5t26.5 63.5q0 38 -26.5 61t-74.5 23t-74.5 -23t-26.5 -61q0 -35 26 -61t75 -26zM295 264q-55 0 -92.5 -25t-37.5 -77q0 -45 34.5 -73.5t95.5 -28.5q28 0 51.5 7.5t41 21t27 32t9.5 41.5q0 52 -36 77t-93 25z" />
+    <glyph glyph-name="eight.taboldstyle" 
+d="M165 353q-32 16 -55 53.5t-23 82.5q0 37 14 69t41 55.5t65.5 36.5t87.5 13q47 0 85 -12.5t65 -35.5t42 -55t15 -71q0 -25 -7.5 -47t-18.5 -39t-25 -29.5t-27 -18.5v-4q52 -20 82 -61.5t30 -98.5q0 -98 -65 -151t-176 -53q-62 0 -107.5 15t-75.5 41.5t-44.5 63t-14.5 79.5
+q0 57 30.5 100.5t81.5 62.5v4zM295 383q56 0 87 29.5t31 76.5q0 46 -29 75t-89 29t-89.5 -29t-29.5 -75q0 -21 8 -40t23.5 -33.5t37.5 -23.5t50 -9zM295 314q-68 0 -110 -31.5t-42 -94.5q0 -26 9 -49.5t27.5 -41t47 -28t68.5 -10.5q37 0 65 10t47 27.5t28.5 40.5t9.5 49
+q0 60 -39.5 94t-110.5 34z" />
+    <glyph glyph-name="eightoldstyle" horiz-adv-x="567" 
+d="M157 353q-32 16 -55 53.5t-23 82.5q0 37 14 69t41 55.5t65.5 36.5t87.5 13q47 0 85 -12.5t65 -35.5t42 -55t15 -71q0 -25 -7.5 -47t-18.5 -39t-25 -29.5t-27 -18.5v-4q52 -20 82 -61.5t30 -98.5q0 -98 -65 -151t-176 -53q-62 0 -107.5 15t-75.5 41.5t-44.5 63t-14.5 79.5
+q0 57 30.5 100.5t81.5 62.5v4zM287 383q56 0 87 29.5t31 76.5q0 46 -29 75t-89 29t-89.5 -29t-29.5 -75q0 -21 8 -40t23.5 -33.5t37.5 -23.5t50 -9zM287 314q-68 0 -110 -31.5t-42 -94.5q0 -26 9 -49.5t27.5 -41t47 -28t68.5 -10.5q37 0 65 10t47 27.5t28.5 40.5t9.5 49
+q0 60 -39.5 94t-110.5 34z" />
+    <glyph glyph-name="emacron.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM423 705v-73h-326v73h326z" />
+    <glyph glyph-name="emdash.case" horiz-adv-x="832" 
+d="M742 363v-76h-652v76h652z" />
+    <glyph glyph-name="endash.case" horiz-adv-x="579" 
+d="M499 363v-76h-419v76h419z" />
+    <glyph glyph-name="eogonek.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75q-32 -17 -43 -34.5t-11 -37.5q0 -17 10.5 -30.5t38.5 -13.5q7 0 14.5 1t16.5 4l-16 -65q-10 -2 -19.5 -3t-17.5 -1q-48 0 -76 24.5t-28 62.5q0 25 13.5 51.5t43.5 41.5h-319v560z" />
+    <glyph glyph-name="equal.case" horiz-adv-x="383" 
+d="M343 239v-74h-303v74h303zM343 381v-74h-303v74h303z" />
+    <glyph glyph-name="eth.sc" horiz-adv-x="618" 
+d="M65 323v237h201q67 0 124 -18t99 -53t65.5 -86.5t23.5 -118.5q0 -69 -23.5 -122t-65.5 -89t-99.5 -54.5t-124.5 -18.5h-200v250h-60v73h60zM157 323h146v-73h-146v-175h117q46 0 85 13t67 39t43.5 65t15.5 92q0 52 -15.5 90t-44 63t-69 37t-89.5 12h-110v-163z" />
+    <glyph glyph-name="exclamdown.case" horiz-adv-x="225" 
+d="M65.0098 -178.959l12.999 508.97h68.9961l12.999 -508.97h-94.9941zM113.007 379.008q-31.998 0 -49.4971 18.998q-17.499 18.999 -17.499 46.9971q0 28.999 17.499 45.998t49.4971 16.999t48.9971 -16.999t16.999 -45.998q0 -27.998 -16.999 -46.9971
+q-16.999 -18.998 -48.9971 -18.998z" />
+    <glyph glyph-name="f.sc" horiz-adv-x="466" 
+d="M426 560v-75h-270v-168h248v-75h-248v-242h-91v560h361z" />
+    <glyph glyph-name="five.denominator" horiz-adv-x="343" 
+d="M293 370v-63h-177l-3 -75q30 9 70 9q28 0 52.5 -7.5t43 -22.5t29 -37.5t10.5 -53.5q0 -33 -12.5 -57t-33.5 -40t-49 -24t-59 -8q-25 0 -44.5 2t-36.5 6t-32 11t-30 18l32 58q28 -20 51.5 -28t55.5 -8q42 0 65.5 18t23.5 52q0 29 -20 46t-62 17q-27 0 -46 -3t-44 -11
+l-30 7l7 194h239z" />
+    <glyph glyph-name="five.fitted" 
+d="M494 650v-81h-297v-167q29 6 50.5 8.5t53.5 2.5q50 0 92 -14.5t72 -41.5t47 -65.5t17 -87.5q0 -45 -16.5 -84.5t-49 -69t-80.5 -46.5t-110 -17q-34 0 -62.5 3.5t-53.5 12t-48.5 21.5t-48.5 33l42 76q22 -18 41 -30.5t38.5 -20t41 -10.5t47.5 -3q87 0 126.5 36.5
+t39.5 98.5q0 69 -41.5 99t-119.5 30q-44 0 -79 -6.5t-75 -20.5l-13 5v339h386z" />
+    <glyph glyph-name="five.numerator" horiz-adv-x="343" 
+d="M293 650v-63h-177l-3 -75q30 9 70 9q28 0 52.5 -7.5t43 -22.5t29 -37.5t10.5 -53.5q0 -33 -12.5 -57t-33.5 -40t-49 -24t-59 -8q-25 0 -44.5 2t-36.5 6t-32 11t-30 18l32 58q28 -20 51.5 -28t55.5 -8q42 0 65.5 18t23.5 52q0 29 -20 46t-62 17q-27 0 -46 -3t-44 -11
+l-30 7l7 194h239z" />
+    <glyph glyph-name="five.sc" horiz-adv-x="498" 
+d="M428 560v-75h-265v-131q23 5 44.5 6.5t44.5 1.5q42 0 79.5 -12.5t65.5 -36.5t44.5 -58.5t16.5 -77.5q0 -41 -15.5 -75t-44.5 -59t-70.5 -39t-94.5 -14q-63 0 -110.5 12.5t-87.5 52.5l41 68q38 -34 71 -45.5t78 -11.5q69 0 106 27.5t37 83.5q0 59 -40.5 84.5t-107.5 25.5
+q-38 0 -68.5 -5t-63.5 -17l-12 4v291h352z" />
+    <glyph glyph-name="five.tab.sc" 
+d="M476 560v-75h-265v-131q23 5 44.5 6.5t44.5 1.5q42 0 79.5 -12.5t65.5 -36.5t44.5 -58.5t16.5 -77.5q0 -41 -15.5 -75t-44.5 -59t-70.5 -39t-94.5 -14q-63 0 -110.5 12.5t-87.5 52.5l41 68q38 -34 71 -45.5t78 -11.5q69 0 106 27.5t37 83.5q0 59 -40.5 84.5t-107.5 25.5
+q-38 0 -68.5 -5t-63.5 -17l-12 4v291h352z" />
+    <glyph glyph-name="five.taboldstyle" 
+d="M481 510v-83h-287v-160q27 7 53 9.5t57 2.5q54 0 94.5 -18.5t66.5 -47.5t39 -64.5t13 -69.5q0 -56 -23 -98t-61 -70t-87 -42t-101 -14t-95 6.5t-83 23.5l25 79q20 -9 36.5 -14.5t33 -9t35.5 -4.5t44 -1q85 0 136 38.5t51 103.5q0 58 -39 90.5t-103 32.5q-45 0 -84 -7
+t-78 -22l-18 5v334h375z" />
+    <glyph glyph-name="fiveoldstyle" horiz-adv-x="545" 
+d="M459 510v-83h-287v-160q27 7 53 9.5t57 2.5q54 0 94.5 -18.5t66.5 -47.5t39 -64.5t13 -69.5q0 -56 -23 -98t-61 -70t-87 -42t-101 -14t-95 6.5t-83 23.5l25 79q20 -9 36.5 -14.5t33 -9t35.5 -4.5t44 -1q85 0 136 38.5t51 103.5q0 58 -39 90.5t-103 32.5q-45 0 -84 -7
+t-78 -22l-18 5v334h375z" />
+    <glyph glyph-name="florin.taboldstyle" 
+d="M174 500v33q0 43 13.5 79t39 62.5t61.5 41t81 14.5q50 0 87 -10t74 -33l-39 -71q-49 34 -110 34q-62 0 -87.5 -31t-25.5 -83v-36h152l-4 -77l-148 5v-397q0 -101 -52 -156.5t-149 -57.5l-7 79q59 4 86.5 32t27.5 86v409l-87 12v65h87z" />
+    <glyph glyph-name="four.denominator" horiz-adv-x="381" 
+d="M311 370v-244h40v-62h-40v-64h-71v64h-211l-9 51l232 260zM112 126h128v144z" />
+    <glyph glyph-name="four.fitted" 
+d="M477 650v-425h74l-10 -83h-64v-142h-93v142h-345v68l327 446zM384 536l-7 1l-226 -312h233v311z" />
+    <glyph glyph-name="four.numerator" horiz-adv-x="381" 
+d="M311 650v-244h40v-62h-40v-64h-71v64h-211l-9 51l232 260zM112 406h128v144z" />
+    <glyph glyph-name="four.sc" horiz-adv-x="542" 
+d="M444 560v-367h63l-8 -76h-55v-117h-91v117h-318v64l302 384zM353 454l-6 1l-204 -262h210v261z" />
+    <glyph glyph-name="four.tab.sc" 
+d="M468 560v-367h63l-8 -76h-55v-117h-91v117h-318v64l302 384zM377 454l-6 1l-204 -262h210v261z" />
+    <glyph glyph-name="four.taboldstyle" 
+d="M480 510v-421h74l-10 -77h-64v-147h-90v147h-355v68l340 436zM390 400l-7 1l-236 -312h243v311z" />
+    <glyph glyph-name="fouroldstyle" horiz-adv-x="595" 
+d="M480 510v-421h74l-10 -77h-64v-147h-90v147h-355v68l340 436zM390 400l-7 1l-236 -312h243v311z" />
+    <glyph glyph-name="g.alt1" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q115 0 166.5 -35t51.5 -113q0 -42 -22 -74t-61.5 -53.5t-94.5 -33t-120 -11.5q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q40 0 76.5 5t64 17t44 31.5t16.5 48.5q0 24 -10.5 38t-29.5 21t-45.5 8.5
+t-57.5 1.5h-100zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80z" />
+    <glyph glyph-name="g.sc" horiz-adv-x="623" 
+d="M578 317v-264q-30 -18 -58.5 -30.5t-57.5 -19.5t-61 -10t-68 -3q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65.5 91.5t97.5 58.5t119 20.5t116 -15t98 -47l-40 -71q-41 30 -86.5 42.5t-91.5 12.5q-50 0 -89 -15.5t-66 -43.5t-41 -67t-14 -85
+q0 -50 16 -89t44 -66t66.5 -41t83.5 -14t80.5 6t67.5 23v144h-142v73h230z" />
+    <glyph glyph-name="gbreve.alt1" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q115 0 166.5 -35t51.5 -113q0 -42 -22 -74t-61.5 -53.5t-94.5 -33t-120 -11.5q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q40 0 76.5 5t64 17t44 31.5t16.5 48.5q0 24 -10.5 38t-29.5 21t-45.5 8.5
+t-57.5 1.5h-100zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80zM221 679q5 -23 21.5 -37t46.5 -14t46.5 14t21.5 37l70 -13q-13 -55 -51.5 -82t-86.5 -27t-86.5 27t-51.5 82z" />
+    <glyph glyph-name="gbreve.sc" horiz-adv-x="623" 
+d="M578 317v-264q-30 -18 -58.5 -30.5t-57.5 -19.5t-61 -10t-68 -3q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65.5 91.5t97.5 58.5t119 20.5t116 -15t98 -47l-40 -71q-41 30 -86.5 42.5t-91.5 12.5q-50 0 -89 -15.5t-66 -43.5t-41 -67t-14 -85
+q0 -50 16 -89t44 -66t66.5 -41t83.5 -14t80.5 6t67.5 23v144h-142v73h230zM248 750q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="gcircumflex.alt1" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q115 0 166.5 -35t51.5 -113q0 -42 -22 -74t-61.5 -53.5t-94.5 -33t-120 -11.5q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q40 0 76.5 5t64 17t44 31.5t16.5 48.5q0 24 -10.5 38t-29.5 21t-45.5 8.5
+t-57.5 1.5h-100zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80zM298 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="gcircumflex.sc" horiz-adv-x="623" 
+d="M578 317v-264q-30 -18 -58.5 -30.5t-57.5 -19.5t-61 -10t-68 -3q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65.5 91.5t97.5 58.5t119 20.5t116 -15t98 -47l-40 -71q-41 30 -86.5 42.5t-91.5 12.5q-50 0 -89 -15.5t-66 -43.5t-41 -67t-14 -85
+q0 -50 16 -89t44 -66t66.5 -41t83.5 -14t80.5 6t67.5 23v144h-142v73h230zM349 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="gcommaaccent.alt1" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q115 0 166.5 -35t51.5 -113q0 -42 -22 -74t-61.5 -53.5t-94.5 -33t-120 -11.5q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q40 0 76.5 5t64 17t44 31.5t16.5 48.5q0 24 -10.5 38t-29.5 21t-45.5 8.5
+t-57.5 1.5h-100zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80zM348.003 731.996q-29.998 -14 -45.9971 -30.498q-15.999 -16.499 -15.999 -39.498q29.998 0 43.4971 -16.499
+t13.499 -37.498q0 -9.99902 -3 -19.998q-2.99902 -10 -9.99902 -18.499q-7 -8.5 -18.499 -13.999q-11.499 -5.5 -28.498 -5.5q-34.998 0 -52.9971 25.498q-17.999 25.499 -17.999 62.4971q0 18.998 7 39.4971q6.99902 20.499 20.499 38.998q13.499 18.498 34.4971 33.9971
+q20.999 15.5 48.9971 24.499z" />
+    <glyph glyph-name="gcommaaccent.sc" horiz-adv-x="623" 
+d="M578 317v-264q-30 -18 -58.5 -30.5t-57.5 -19.5t-61 -10t-68 -3q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65.5 91.5t97.5 58.5t119 20.5t116 -15t98 -47l-40 -71q-41 30 -86.5 42.5t-91.5 12.5q-50 0 -89 -15.5t-66 -43.5t-41 -67t-14 -85
+q0 -50 16 -89t44 -66t66.5 -41t83.5 -14t80.5 6t67.5 23v144h-142v73h230zM281 -227q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="gdotaccent.alt1" horiz-adv-x="582" 
+d="M567 444h-111q29 -23 42 -49t13 -62q0 -38 -15 -70t-43.5 -55.5t-70 -37t-94.5 -13.5q-79 0 -131 26q-15 -6 -24 -22t-9 -29q0 -23 19 -40.5t66 -17.5h128q115 0 166.5 -35t51.5 -113q0 -42 -22 -74t-61.5 -53.5t-94.5 -33t-120 -11.5q-111 0 -169 40.5t-58 117.5
+q0 38 15.5 65t51.5 50q-28 14 -42.5 40t-14.5 53q0 31 16 60t46 46q-36 42 -36 107q0 81 60 129t170 48h271v-66zM225 -2q-20 0 -35 1t-29 4q-47 -28 -47 -76q0 -53 40.5 -76.5t112.5 -23.5q40 0 76.5 5t64 17t44 31.5t16.5 48.5q0 24 -10.5 38t-29.5 21t-45.5 8.5
+t-57.5 1.5h-100zM151 333q0 -52 35.5 -78.5t101.5 -26.5q60 0 98.5 25.5t38.5 79.5q0 27 -10.5 47.5t-29 34t-43.5 20t-54 6.5q-63 0 -100 -28t-37 -80zM287 685q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="gdotaccent.sc" horiz-adv-x="623" 
+d="M578 317v-264q-30 -18 -58.5 -30.5t-57.5 -19.5t-61 -10t-68 -3q-56 0 -109 17.5t-94 53t-65.5 90.5t-24.5 130q0 65 24 118.5t65.5 91.5t97.5 58.5t119 20.5t116 -15t98 -47l-40 -71q-41 30 -86.5 42.5t-91.5 12.5q-50 0 -89 -15.5t-66 -43.5t-41 -67t-14 -85
+q0 -50 16 -89t44 -66t66.5 -41t83.5 -14t80.5 6t67.5 23v144h-142v73h230zM351 744q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="germandbls.sc" horiz-adv-x="992" 
+d="M394 447q-41 28 -74.5 39t-75.5 11q-54 0 -78 -21.5t-24 -52.5t16.5 -46.5t51.5 -24.5l93 -24q37 -10 66 -23.5t49.5 -33.5t31 -47.5t10.5 -66.5q0 -35 -13.5 -65.5t-40.5 -53t-67.5 -35.5t-95.5 -13q-32 0 -58.5 2.5t-50.5 8.5t-47 15.5t-47 24.5l31 80
+q24 -15 44.5 -25.5t40.5 -17t42 -9.5t49 -3q63 0 95 23.5t32 66.5q0 42 -23 61.5t-76 32.5l-101 24q-59 14 -89.5 51t-30.5 96q0 66 50 107.5t137 41.5q58 0 105 -12.5t86 -39.5zM886 447q-41 28 -74.5 39t-75.5 11q-54 0 -78 -21.5t-24 -52.5t16.5 -46.5t51.5 -24.5l93 -24
+q37 -10 66 -23.5t49.5 -33.5t31 -47.5t10.5 -66.5q0 -35 -13.5 -65.5t-40.5 -53t-67.5 -35.5t-95.5 -13q-32 0 -58.5 2.5t-50.5 8.5t-47 15.5t-47 24.5l31 80q24 -15 44.5 -25.5t40.5 -17t42 -9.5t49 -3q63 0 95 23.5t32 66.5q0 42 -23 61.5t-76 32.5l-101 24
+q-59 14 -89.5 51t-30.5 96q0 66 50 107.5t137 41.5q58 0 105 -12.5t86 -39.5z" />
+    <glyph glyph-name="greater.case" horiz-adv-x="332" 
+d="M292 283v-38l-199 -161l-58 47l160 133l-160 132l58 48z" />
+    <glyph glyph-name="greaterequal.case" horiz-adv-x="337" 
+d="M302 313v-38l-209 -141l-58 50l170 120l-160 123l58 47zM302 127l-159 -111l-58 49l197 131z" />
+    <glyph glyph-name="guillemotleft.case" horiz-adv-x="459" 
+d="M252 418l-112 -99l112 -105l-38 -58l-174 141v38l174 141zM424 428l-112 -109l112 -115l-38 -58l-174 154v38l174 148z" />
+    <glyph glyph-name="guillemotright.case" horiz-adv-x="459" 
+d="M424 335v-38l-174 -141l-38 58l112 99l-112 105l38 58zM252 332v-38l-174 -148l-38 58l112 109l-112 115l38 58z" />
+    <glyph glyph-name="guilsinglleft.case" horiz-adv-x="332" 
+d="M297 445l-152 -129l152 -135l-38 -58l-219 171v38l219 171z" />
+    <glyph glyph-name="guilsinglright.case" horiz-adv-x="332" 
+d="M292 332v-38l-219 -171l-38 58l152 129l-152 135l38 58z" />
+    <glyph glyph-name="h.sc" horiz-adv-x="623" 
+d="M156 560v-230h311v230h91v-560h-91v254h-311v-254h-91v560h91z" />
+    <glyph glyph-name="hbar.sc" horiz-adv-x="623" 
+d="M156 560v-79h311v79h91v-79h44v-68h-44v-413h-91v254h-311v-254h-91v413h-44v68h44v79h91zM467 330v83h-311v-83h311z" />
+    <glyph glyph-name="hcircumflex.sc" horiz-adv-x="623" 
+d="M156 560v-230h311v230h91v-560h-91v254h-311v-254h-91v560h91zM311 698l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="hyphen.case" horiz-adv-x="381" 
+d="M341 363v-76h-301v76h301z" />
+    <glyph glyph-name="hyphen.denominator" horiz-adv-x="253" 
+d="M221 221v-64h-189v64h189z" />
+    <glyph glyph-name="hyphen.numerator" horiz-adv-x="253" 
+d="M221 501v-64h-189v64h189z" />
+    <glyph glyph-name="hypheninferior" horiz-adv-x="253" 
+d="M221 152v-64h-189v64h189z" />
+    <glyph glyph-name="hyphensuperior" horiz-adv-x="253" 
+d="M221 596v-64h-189v64h189z" />
+    <glyph glyph-name="i.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91z" />
+    <glyph glyph-name="iacute.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91zM162 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="icircumflex.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91zM111 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="idieresis.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91zM1 749q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM220 749q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="igrave.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91zM151 619h-69l-98 136l78 47z" />
+    <glyph glyph-name="ii.liga.sc" horiz-adv-x="442" 
+d="M220 748q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM422 748q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17zM377 560v-560h-91v560h91zM156 560v-560h-91v560h91zM20 748
+q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="imacron.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91zM274 705v-73h-326v73h326z" />
+    <glyph glyph-name="iogonek.sc" horiz-adv-x="221" 
+d="M156 560v-560q-32 -17 -43 -34.5t-11 -37.5q0 -17 10.5 -30.5t38.5 -13.5q7 0 14.5 1t16.5 4l-16 -65q-10 -2 -19.5 -3t-17.5 -1q-48 0 -76 24.5t-28 62.5q0 25 13.5 51.5t43.5 41.5h-17v560h91z" />
+    <glyph glyph-name="itilde.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91zM-71 686q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6q-49 0 -83 -39z" />
+    <glyph glyph-name="j.sc" horiz-adv-x="231" 
+d="M166 560v-548q0 -45 -13 -86t-39.5 -73.5t-66.5 -54.5t-94 -28l-24 77q76 13 111 52.5t35 119.5v541h91z" />
+    <glyph glyph-name="jcircumflex.sc" horiz-adv-x="231" 
+d="M166 560v-548q0 -45 -13 -86t-39.5 -73.5t-66.5 -54.5t-94 -28l-24 77q76 13 111 52.5t35 119.5v541h91zM116 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="k.sc" horiz-adv-x="583" 
+d="M148 560v-276l270 276h113l-212 -218l244 -342h-107l-201 287l-107 -99v-188h-90v560h90z" />
+    <glyph glyph-name="kcommaaccent.sc" horiz-adv-x="583" 
+d="M148 560v-276l270 276h113l-212 -218l244 -342h-107l-201 287l-107 -99v-188h-90v560h90zM220 -180q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="kgreenlandic.sc" horiz-adv-x="583" 
+d="M148 560v-276l270 276h113l-212 -218l244 -342h-107l-201 287l-107 -99v-188h-90v560h90z" />
+    <glyph glyph-name="l.sc" horiz-adv-x="471" 
+d="M156 560v-485h285v-75h-376v560h91z" />
+    <glyph glyph-name="lacute.sc" horiz-adv-x="471" 
+d="M156 560v-485h285v-75h-376v560h91zM162 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="lcaron.sc" horiz-adv-x="471" 
+d="M156 560v-485h285v-75h-376v560h91zM306 388q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="lcommaaccent.sc" horiz-adv-x="471" 
+d="M156 560v-485h285v-75h-376v560h91zM200 -217q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="ldot.sc" horiz-adv-x="471" 
+d="M156 560v-485h285v-75h-376v560h91zM345 392q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="less.case" horiz-adv-x="332" 
+d="M292 397l-160 -133l160 -132l-58 -48l-199 161v38l199 161z" />
+    <glyph glyph-name="lessequal.case" horiz-adv-x="337" 
+d="M292 427l-160 -123l170 -120l-58 -50l-209 141v38l199 161zM252 65l-58 -49l-159 111l20 69z" />
+    <glyph glyph-name="logicalnot.case" horiz-adv-x="390" 
+d="M350 313l-7 -180h-68l-8 104h-227v76h310z" />
+    <glyph glyph-name="lslash.sc" horiz-adv-x="484" 
+d="M169 560v-226l106 40l19 -68l-125 -47v-184h285v-75h-376v225l-53 -19l-19 68l72 27v259h91z" />
+    <glyph glyph-name="m.sc" horiz-adv-x="711" 
+d="M171 560l185 -259l187 259h103v-560h-89v437h-5l-180 -249h-34l-180 249h-5v-437h-88v560h106z" />
+    <glyph glyph-name="minus.case" horiz-adv-x="381" 
+d="M341 308v-76h-301v76h301z" />
+    <glyph glyph-name="multiply.case" horiz-adv-x="382" 
+d="M191 314l103 102l53 -53l-102 -103l102 -103l-53 -53l-103 102l-103 -102l-53 53l102 103l-102 103l53 53z" />
+    <glyph glyph-name="n.sc" horiz-adv-x="670" 
+d="M160 560l361 -445l-4 134v311h88v-560h-98l-358 448l4 -159v-289h-88v560h95z" />
+    <glyph glyph-name="nacute.sc" horiz-adv-x="670" 
+d="M160 560l361 -445l-4 134v311h88v-560h-98l-358 448l4 -159v-289h-88v560h95zM381 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="ncaron.sc" horiz-adv-x="670" 
+d="M160 560l361 -445l-4 134v311h88v-560h-98l-358 448l4 -159v-289h-88v560h95zM335 689l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="ncommaaccent.sc" horiz-adv-x="670" 
+d="M160 560l361 -445l-4 134v311h88v-560h-98l-358 448l4 -159v-289h-88v560h95zM253 -179q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="nine.denominator" horiz-adv-x="362" 
+d="M268 208q0 52 -21 81t-63 29q-43 0 -62.5 -18t-19.5 -48q0 -34 21 -51t56 -17q25 0 46 6t43 18zM71 51q37 0 70 6t59.5 18.5t43.5 32t22 45.5q-41 -29 -95 -29q-26 0 -51 7.5t-44.5 23t-31.5 39.5t-12 57t12 57t32.5 40t48 23.5t59.5 7.5q76 0 114.5 -50t38.5 -130
+q0 -105 -69.5 -156.5t-191.5 -51.5z" />
+    <glyph glyph-name="nine.fitted" 
+d="M115 68q65 0 123 14.5t101.5 44t69.5 72.5t28 100q-34 -30 -75.5 -44.5t-96.5 -14.5q-44 0 -82 14.5t-66 41.5t-44 66t-16 87q0 57 19.5 97.5t52 66t74 37.5t85.5 12q54 0 98.5 -18t76.5 -56.5t49.5 -99.5t17.5 -148q0 -90 -31 -156.5t-86 -110t-129 -65t-161 -21.5z
+M435 381q0 100 -39 152t-114 52q-63 0 -97 -33t-34 -101q0 -63 32.5 -96.5t95.5 -33.5q51 0 87.5 15t68.5 45z" />
+    <glyph glyph-name="nine.numerator" horiz-adv-x="362" 
+d="M268 488q0 52 -21 81t-63 29q-43 0 -62.5 -18t-19.5 -48q0 -34 21 -51t56 -17q25 0 46 6t43 18zM71 331q37 0 70 6t59.5 18.5t43.5 32t22 45.5q-41 -29 -95 -29q-26 0 -51 7.5t-44.5 23t-31.5 39.5t-12 57t12 57t32.5 40t48 23.5t59.5 7.5q76 0 114.5 -50t38.5 -130
+q0 -105 -69.5 -156.5t-191.5 -51.5z" />
+    <glyph glyph-name="nine.sc" horiz-adv-x="512" 
+d="M112 65q126 0 198 46t86 138q-61 -50 -155 -50q-43 0 -79 13t-62 37t-40.5 58.5t-14.5 76.5q0 47 18.5 82t48.5 58t69 34.5t80 11.5q112 0 166.5 -74t54.5 -209q0 -65 -22.5 -119t-67.5 -94t-113 -62t-160 -22zM395 322q0 85 -38.5 128.5t-96.5 43.5t-92 -26.5t-34 -83.5
+q0 -54 31 -81t88 -27q39 0 77 11.5t65 34.5z" />
+    <glyph glyph-name="nine.tab.sc" 
+d="M154 65q126 0 198 46t86 138q-61 -50 -155 -50q-43 0 -79 13t-62 37t-40.5 58.5t-14.5 76.5q0 47 18.5 82t48.5 58t69 34.5t80 11.5q112 0 166.5 -74t54.5 -209q0 -65 -22.5 -119t-67.5 -94t-113 -62t-160 -22zM437 322q0 85 -38.5 128.5t-96.5 43.5t-92 -26.5t-34 -83.5
+q0 -54 31 -81t88 -27q39 0 77 11.5t65 34.5z" />
+    <glyph glyph-name="nine.taboldstyle" 
+d="M114 -61q59 0 114.5 10.5t100 35t73.5 66t35 104.5q-34 -28 -75 -42.5t-96 -14.5q-44 0 -82 14.5t-66 41.5t-44 66t-16 87q0 57 20 97t52.5 66t74 38t84.5 12q57 0 101.5 -18.5t75 -58t47 -100.5t16.5 -146q0 -92 -33 -156.5t-89 -105t-130 -59.5t-157 -19zM436 237
+q0 55 -11.5 94t-32 63.5t-48.5 35.5t-60 11q-67 0 -99.5 -34t-32.5 -98q0 -63 31.5 -97t94.5 -34q51 0 88.5 15t69.5 44z" />
+    <glyph glyph-name="nineoldstyle" horiz-adv-x="561" 
+d="M101 -61q59 0 114.5 10.5t100 35t73.5 66t35 104.5q-34 -28 -75 -42.5t-96 -14.5q-44 0 -82 14.5t-66 41.5t-44 66t-16 87q0 57 20 97t52.5 66t74 38t84.5 12q57 0 101.5 -18.5t75 -58t47 -100.5t16.5 -146q0 -92 -33 -156.5t-89 -105t-130 -59.5t-157 -19zM423 237
+q0 55 -11.5 94t-32 63.5t-48.5 35.5t-60 11q-67 0 -99.5 -34t-32.5 -98q0 -63 31.5 -97t94.5 -34q51 0 88.5 15t69.5 44z" />
+    <glyph glyph-name="notequal.case" horiz-adv-x="389" 
+d="M186 381l17 65l65 -17l-13 -48h94v-74h-113l-17 -68h130v-74h-149l-18 -70l-65 17l13 53h-90v74h109l18 68h-127v74h146z" />
+    <glyph glyph-name="ntilde.sc" horiz-adv-x="670" 
+d="M160 560l361 -445l-4 134v311h88v-560h-98l-358 448l4 -159v-289h-88v560h95zM154 686q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6q-49 0 -83 -39z" />
+    <glyph glyph-name="numbersign.case" horiz-adv-x="524" 
+d="M238 465l-10 -99h77l11 105l72 -6l-10 -99h106l-3 -65h-109l-7 -73h110l-3 -64h-112l-11 -97l-71 5l9 92h-76l-11 -97l-72 5l9 92h-97l3 64h100l8 73h-102l3 65h103l12 105zM216 228h77l7 73h-77z" />
+    <glyph glyph-name="numbersign.taboldstyle" 
+d="M271 464l-10 -99h77l11 105l72 -6l-10 -99h106l-3 -65h-109l-7 -73h110l-3 -64h-112l-11 -97l-71 5l9 92h-76l-11 -97l-72 5l9 92h-97l3 64h100l8 73h-102l3 65h103l12 105zM249 227h77l7 73h-77z" />
+    <glyph glyph-name="numbersignoldstyle" horiz-adv-x="524" 
+d="M238 464l-10 -99h77l11 105l72 -6l-10 -99h106l-3 -65h-109l-7 -73h110l-3 -64h-112l-11 -97l-71 5l9 92h-76l-11 -97l-72 5l9 92h-97l3 64h100l8 73h-102l3 65h103l12 105zM216 227h77l7 73h-77z" />
+    <glyph glyph-name="o.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93z" />
+    <glyph glyph-name="oacute.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM393 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="obreve.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM244 750q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="ocircumflex.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM340 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="odieresis.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM230 749q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM449 749q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17
+t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="oe.sc" horiz-adv-x="965" 
+d="M903 560v-74h-290v-155h265v-74h-265v-182h302v-75h-389v73q-37 -46 -88 -64.5t-108 -18.5q-64 0 -117.5 21t-91.5 59.5t-59.5 91.5t-21.5 118q0 64 21.5 117.5t59.5 92t91.5 59.5t117.5 21q57 0 108 -17t88 -59v66h377zM132 280q0 -52 15 -91.5t42 -66.5t63 -40.5
+t78 -13.5q43 0 79 13t62.5 39.5t41.5 66.5t15 93t-15 93t-41.5 66.5t-62.5 39.5t-79 13q-42 0 -78.5 -13t-63 -39.5t-41.5 -66.5t-15 -93z" />
+    <glyph glyph-name="ograve.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM377 619h-69l-98 136l78 47z" />
+    <glyph glyph-name="ohungarumlaut.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM303 808l82 -30l-69 -159h-64zM441 798l76 -47l-93 -132h-67z" />
+    <glyph glyph-name="omacron.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM503 705v-73h-326v73h326z" />
+    <glyph glyph-name="one.denominator" horiz-adv-x="226" 
+d="M137 373l39 -5v-368h-71v284l-71 -19l-17 43z" />
+    <glyph glyph-name="one.fitted" 
+d="M385 648v-571h119v-77h-360v77h148v468l-143 -52l-25 69l194 89z" />
+    <glyph glyph-name="one.numerator" horiz-adv-x="226" 
+d="M137 653l39 -5v-368h-71v284l-71 -19l-17 43z" />
+    <glyph glyph-name="one.sc" horiz-adv-x="322" 
+d="M257 559v-559h-91v457l-93 -44l-35 62l154 88z" />
+    <glyph glyph-name="one.tab.sc" 
+d="M384 558v-482h116v-76h-361v76h154v381l-143 -52l-25 67l194 89z" />
+    <glyph glyph-name="one.taboldstyle" 
+d="M371 508v-432h119v-76h-365v76h156v331l-152 -49l-19 66l196 87z" />
+    <glyph glyph-name="oneoldstyle" horiz-adv-x="321" 
+d="M256 510v-510h-90v407l-112 -49l-19 66l156 87z" />
+    <glyph glyph-name="oslash.sc" horiz-adv-x="681" 
+d="M564 540l-25 -37q48 -38 75 -95t27 -128q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21q-74 0 -136 28l-32 -46l-65 44l30 44q-45 38 -71 94.5t-26 125.5q0 64 23 117.5t63 92t95 59.5t119 21q69 0 130 -25l27 39zM250 85q43 -17 90 -17q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93q0 51 -15.5 90t-41.5 65zM424 477q-39 15 -84 15q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93q0 -48 14.5 -85.5t39.5 -64.5z" />
+    <glyph glyph-name="otilde.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM159 686q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6
+q-49 0 -83 -39z" />
+    <glyph glyph-name="p.sc" horiz-adv-x="543" 
+d="M298 560q46 0 84 -13t65 -36.5t41.5 -55.5t14.5 -71q0 -44 -16 -78t-44.5 -57t-67 -34.5t-83.5 -11.5h-137v-203h-90v560h233zM155 278h130q60 0 93 23.5t33 82.5q0 55 -32.5 78t-104.5 23h-119v-207z" />
+    <glyph glyph-name="parenleft.case" horiz-adv-x="247" 
+d="M217 616q-42 -86 -62.5 -174t-20.5 -188t20.5 -188t62.5 -174l-68 -33q-54 75 -81.5 176t-27.5 219t27.5 219t81.5 176z" />
+    <glyph glyph-name="parenleft.denominator" horiz-adv-x="156" 
+d="M145 392q-22 -42 -34 -96.5t-12 -107.5t12 -105t35 -95l-52 -26q-63 98 -63 226q0 63 16 122t47 107z" />
+    <glyph glyph-name="parenleft.numerator" horiz-adv-x="156" 
+d="M145 672q-22 -42 -34 -96.5t-12 -107.5t12 -105t35 -95l-52 -26q-63 98 -63 226q0 63 16 122t47 107z" />
+    <glyph glyph-name="parenright.case" horiz-adv-x="247" 
+d="M98 649q54 -75 81.5 -176t27.5 -219t-27.5 -219t-81.5 -176l-68 33q42 86 62.5 174t20.5 188t-20.5 188t-62.5 174z" />
+    <glyph glyph-name="parenright.denominator" horiz-adv-x="156" 
+d="M62 417q31 -48 47.5 -106t16.5 -122q0 -63 -16.5 -121t-47.5 -106l-51 25q22 42 34 95t12 107q0 52 -12 105.5t-35 96.5z" />
+    <glyph glyph-name="parenright.numerator" horiz-adv-x="156" 
+d="M62 697q31 -48 47.5 -106t16.5 -122q0 -63 -16.5 -121t-47.5 -106l-51 25q22 42 34 95t12 107q0 52 -12 105.5t-35 96.5z" />
+    <glyph glyph-name="percent" horiz-adv-x="717" 
+d="M557 474l-348 -496l-57 37l349 497zM179 231q-30 0 -57.5 10.5t-47.5 29t-32 44t-12 55.5t11.5 55.5t31.5 44.5t47 29.5t59 10.5q30 0 57.5 -11t47.5 -30t32 -44.5t12 -54.5q0 -30 -12 -55.5t-32.5 -44t-47.5 -29t-57 -10.5zM179 294q32 0 55 20t23 56q0 35 -21.5 56
+t-56.5 21q-37 0 -58 -21.5t-21 -55.5t22 -55t57 -21zM538 -10q-30 0 -57.5 10.5t-47.5 29t-32 44t-12 55.5t11.5 55.5t31.5 44.5t47 29.5t59 10.5q30 0 57.5 -11t47.5 -30t32 -44.5t12 -54.5q0 -30 -12 -55.5t-32.5 -44t-47.5 -29t-57 -10.5zM538 53q32 0 55 20t23 56
+q0 35 -21.5 56t-56.5 21q-37 0 -58 -21.5t-21 -55.5t22 -55t57 -21z" />
+    <glyph glyph-name="period.denominator" horiz-adv-x="139" 
+d="M69 87q23 0 36.5 -13.5t13.5 -33.5q0 -21 -13.5 -34t-36.5 -13t-36 13t-13 34q0 20 13 33.5t36 13.5z" />
+    <glyph glyph-name="period.numerator" horiz-adv-x="139" 
+d="M69 367q23 0 36.5 -13.5t13.5 -33.5q0 -21 -13.5 -34t-36.5 -13t-36 13t-13 34q0 20 13 33.5t36 13.5z" />
+    <glyph glyph-name="periodcentered.case" horiz-adv-x="233" 
+d="M116 401q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="periodinferior" horiz-adv-x="139" 
+d="M69 18q23 0 36.5 -13.5t13.5 -33.5q0 -21 -13.5 -34t-36.5 -13t-36 13t-13 34q0 20 13 33.5t36 13.5z" />
+    <glyph glyph-name="periodsuperior" horiz-adv-x="139" 
+d="M69 462q23 0 36.5 -13.5t13.5 -33.5q0 -21 -13.5 -34t-36.5 -13t-36 13t-13 34q0 20 13 33.5t36 13.5z" />
+    <glyph glyph-name="plus.case" horiz-adv-x="422" 
+d="M252 438v-130h130v-76h-130v-130h-82v130h-130v76h130v130h82z" />
+    <glyph glyph-name="plusminus.case" horiz-adv-x="422" 
+d="M252 513v-130h130v-76h-130v-130h-82v130h-130v76h130v130h82zM362 128v-76h-301v76h301z" />
+    <glyph glyph-name="q.sc" horiz-adv-x="681" 
+d="M568 -113l-328 117q-102 37 -151 110t-49 166q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5q0 -100 -54.5 -171.5t-155.5 -97.5l162 -46zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5t45 66.5
+t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93z" />
+    <glyph glyph-name="questiondown.case" horiz-adv-x="468" 
+d="M265.013 330.011q13.999 -27.999 13.999 -66.9961q0 -28.998 -11 -50.9971q-10.999 -21.999 -28.498 -37.498t-38.9971 -26.498q-21.499 -10.999 -41.498 -18.999q-46.9971 -17.999 -72.9951 -44.4971t-25.998 -75.4951q0 -50.9971 38.9971 -82.4951
+q38.998 -31.498 111.993 -31.498q50.9971 0 99.9941 19.999q48.9971 19.998 83.9951 55.9961l47.9971 -62.9961q-44.998 -43.9971 -105.494 -66.9961q-60.4961 -22.998 -133.492 -22.998q-50.9961 0 -93.4941 12.499q-42.4971 12.499 -72.9951 37.498
+q-30.498 24.998 -47.9971 62.9961q-17.499 37.9971 -17.499 87.9941q0 45.9971 25.498 87.4951q25.499 41.4971 76.4961 67.4951q22.998 12 45.4971 20.999q22.498 8.99902 39.9971 20.499q17.499 11.499 28.499 28.498q10.999 16.999 10.999 44.9971v40.998h65.9961z
+M232.015 379.008q-31.998 0 -49.4971 18.998q-17.499 18.999 -17.499 46.9971q0 28.999 17.499 45.998t49.4971 16.999t48.9971 -16.999t16.999 -45.998q0 -27.998 -16.999 -46.9971q-16.999 -18.998 -48.9971 -18.998z" />
+    <glyph glyph-name="r.sc" horiz-adv-x="565" 
+d="M291 560q45 0 82.5 -11t64 -32t41 -50.5t14.5 -66.5q0 -32 -9.5 -58t-26.5 -45.5t-40 -33t-49 -21.5l159 -242h-105l-148 234h-119v-234h-90v560h226zM155 308h128q25 0 47 4.5t38.5 15t26 28.5t9.5 44q0 48 -33 67t-93 19h-123v-178z" />
+    <glyph glyph-name="racute.sc" horiz-adv-x="565" 
+d="M291 560q45 0 82.5 -11t64 -32t41 -50.5t14.5 -66.5q0 -32 -9.5 -58t-26.5 -45.5t-40 -33t-49 -21.5l159 -242h-105l-148 234h-119v-234h-90v560h226zM155 308h128q25 0 47 4.5t38.5 15t26 28.5t9.5 44q0 48 -33 67t-93 19h-123v-178zM303 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="rcaron.sc" horiz-adv-x="565" 
+d="M291 560q45 0 82.5 -11t64 -32t41 -50.5t14.5 -66.5q0 -32 -9.5 -58t-26.5 -45.5t-40 -33t-49 -21.5l159 -242h-105l-148 234h-119v-234h-90v560h226zM155 308h128q25 0 47 4.5t38.5 15t26 28.5t9.5 44q0 48 -33 67t-93 19h-123v-178zM264 699l132 86l21 -54l-141 -119
+h-23l-142 119l21 54z" />
+    <glyph glyph-name="rcommaaccent.sc" horiz-adv-x="565" 
+d="M291 560q45 0 82.5 -11t64 -32t41 -50.5t14.5 -66.5q0 -32 -9.5 -58t-26.5 -45.5t-40 -33t-49 -21.5l159 -242h-105l-148 234h-119v-234h-90v560h226zM155 308h128q25 0 47 4.5t38.5 15t26 28.5t9.5 44q0 48 -33 67t-93 19h-123v-178zM213 -178q62 24 62 68
+q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="rupiah" horiz-adv-x="577" 
+d="M305 595q51 0 94.5 -10.5t75 -32t49.5 -55.5t18 -82q0 -49 -19 -84t-51 -57.5t-75 -33t-92 -10.5h-130v-59h171v-72h-171v-99h-91v99h-61v72h61v59h-61v76h61v289h221zM175 306h121q34 0 63 4t50 16t32.5 33.5t11.5 55.5q0 31 -11 51.5t-31.5 32.5t-49.5 16.5t-65 4.5
+h-121v-214z" />
+    <glyph glyph-name="rupiah.fitted" 
+d="M317 595q51 0 94.5 -10.5t75 -32t49.5 -55.5t18 -82q0 -49 -19 -84t-51 -57.5t-75 -33t-92 -10.5h-130v-59h171v-72h-171v-99h-91v99h-61v72h61v59h-61v76h61v289h221zM187 306h121q34 0 63 4t50 16t32.5 33.5t11.5 55.5q0 31 -11 51.5t-31.5 32.5t-49.5 16.5t-65 4.5
+h-121v-214z" />
+    <glyph glyph-name="rupiah.sc" horiz-adv-x="529" 
+d="M285 551q44 0 82 -9.5t66.5 -29.5t44.5 -51.5t16 -75.5q0 -42 -16 -74t-45 -53.5t-69 -33t-89 -11.5h-105v-55h153v-70h-153v-88h-90v88h-57v70h57v55h-57v71h57v267h205zM170 284h111q60 0 93.5 23.5t33.5 77.5q0 50 -32 73t-95 23h-111v-197z" />
+    <glyph glyph-name="rupiah.tab.sc" 
+d="M321 551q44 0 82 -9.5t66.5 -29.5t44.5 -51.5t16 -75.5q0 -42 -16 -74t-45 -53.5t-69 -33t-89 -11.5h-105v-55h153v-70h-153v-88h-90v88h-57v70h57v55h-57v71h57v267h205zM206 284h111q60 0 93.5 23.5t33.5 77.5q0 50 -32 73t-95 23h-111v-197z" />
+    <glyph glyph-name="rupiah.taboldstyle" 
+d="M17 -59h58v119l-71 47l39 67l32 -19v326q29 7 55.5 12.5t53.5 9t56 5.5t62 2q135 0 206 -69t71 -192q0 -57 -16 -104.5t-47.5 -82t-80 -53.5t-112.5 -19q-47 0 -82 5t-73 19v-73h147v-72h-147v-99h-93v99h-58v72zM168 101q37 -18 72.5 -25.5t81.5 -7.5q76 0 120 47.5
+t44 133.5q0 94 -48.5 141t-135.5 47q-23 0 -40.5 -1t-33 -3.5t-30 -6t-30.5 -8.5v-317z" />
+    <glyph glyph-name="rupiaholdstyle" horiz-adv-x="609" 
+d="M7 -59h58v119l-71 47l39 67l32 -19v326q29 7 55.5 12.5t53.5 9t56 5.5t62 2q135 0 206 -69t71 -192q0 -57 -16 -104.5t-47.5 -82t-80 -53.5t-112.5 -19q-47 0 -82 5t-73 19v-73h147v-72h-147v-99h-93v99h-58v72zM158 101q37 -18 72.5 -25.5t81.5 -7.5q76 0 120 47.5
+t44 133.5q0 94 -48.5 141t-135.5 47q-23 0 -40.5 -1t-33 -3.5t-30 -6t-30.5 -8.5v-317z" />
+    <glyph glyph-name="s.sc" horiz-adv-x="500" 
+d="M394 447q-41 28 -74.5 39t-75.5 11q-54 0 -78 -21.5t-24 -52.5t16.5 -46.5t51.5 -24.5l93 -24q37 -10 66 -23.5t49.5 -33.5t31 -47.5t10.5 -66.5q0 -35 -13.5 -65.5t-40.5 -53t-67.5 -35.5t-95.5 -13q-32 0 -58.5 2.5t-50.5 8.5t-47 15.5t-47 24.5l31 80
+q24 -15 44.5 -25.5t40.5 -17t42 -9.5t49 -3q63 0 95 23.5t32 66.5q0 42 -23 61.5t-76 32.5l-101 24q-59 14 -89.5 51t-30.5 96q0 66 50 107.5t137 41.5q58 0 105 -12.5t86 -39.5z" />
+    <glyph glyph-name="sacute.sc" horiz-adv-x="500" 
+d="M394 447q-41 28 -74.5 39t-75.5 11q-54 0 -78 -21.5t-24 -52.5t16.5 -46.5t51.5 -24.5l93 -24q37 -10 66 -23.5t49.5 -33.5t31 -47.5t10.5 -66.5q0 -35 -13.5 -65.5t-40.5 -53t-67.5 -35.5t-95.5 -13q-32 0 -58.5 2.5t-50.5 8.5t-47 15.5t-47 24.5l31 80
+q24 -15 44.5 -25.5t40.5 -17t42 -9.5t49 -3q63 0 95 23.5t32 66.5q0 42 -23 61.5t-76 32.5l-101 24q-59 14 -89.5 51t-30.5 96q0 66 50 107.5t137 41.5q58 0 105 -12.5t86 -39.5zM299 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="sb.liga" horiz-adv-x="1088" 
+d="M406 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -20 10 -32t27.5 -19.5t42 -11.5t53.5 -7q47 -5 84 -15.5t62 -29t38 -46.5t13 -68q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 20 -7 33
+t-23 21t-42 13t-65 9q-98 10 -146 42.5t-48 102.5q0 30 13 56.5t39.5 46t67 31t94.5 11.5h20.5t18.5 -1q-8 15 -11.5 35t-3.5 41q0 35 13.5 64t38 50t58 32.5t73.5 11.5q47 0 84.5 -6.5t79.5 -21.5v-249q38 23 79.5 33.5t92.5 10.5q122 0 185.5 -64t63.5 -191
+q0 -60 -22.5 -109t-62 -83.5t-93.5 -53.5t-118 -19q-35 0 -64 3t-54.5 9.5t-49.5 16.5t-50 24v617q-38 11 -79 11q-46 0 -71 -23.5t-25 -64.5q0 -35 17 -59t59 -45zM627 95q17 -8 31.5 -13t28.5 -8t30 -4.5t36 -1.5q92 0 146.5 47.5t54.5 140.5q0 90 -39 134.5t-124 44.5
+q-48 0 -88.5 -11t-75.5 -36v-293z" />
+    <glyph glyph-name="scaron.sc" horiz-adv-x="500" 
+d="M394 447q-41 28 -74.5 39t-75.5 11q-54 0 -78 -21.5t-24 -52.5t16.5 -46.5t51.5 -24.5l93 -24q37 -10 66 -23.5t49.5 -33.5t31 -47.5t10.5 -66.5q0 -35 -13.5 -65.5t-40.5 -53t-67.5 -35.5t-95.5 -13q-32 0 -58.5 2.5t-50.5 8.5t-47 15.5t-47 24.5l31 80
+q24 -15 44.5 -25.5t40.5 -17t42 -9.5t49 -3q63 0 95 23.5t32 66.5q0 42 -23 61.5t-76 32.5l-101 24q-59 14 -89.5 51t-30.5 96q0 66 50 107.5t137 41.5q58 0 105 -12.5t86 -39.5zM250 699l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="scedilla.sc" horiz-adv-x="500" 
+d="M394 447q-41 28 -74.5 39t-75.5 11q-54 0 -78 -21.5t-24 -52.5t16.5 -46.5t51.5 -24.5l93 -24q37 -10 66 -23.5t49.5 -34t31 -48t10.5 -65.5q0 -34 -12.5 -63.5t-38 -52t-64 -36.5t-89.5 -15l-5 -38q35 0 54 -18.5t19 -51.5q0 -28 -14.5 -49.5t-37 -37t-50 -25.5
+t-52.5 -14l-13 61q41 7 68 21.5t27 41.5t-36 27h-33l3 86q-41 4 -75 14.5t-71 33.5l31 80q24 -15 44.5 -25.5t40.5 -17t42 -9.5t49 -3q63 0 95 23.5t32 66.5q0 42 -23 61.5t-76 32.5l-101 24q-59 14 -89.5 51t-30.5 96q0 66 50 107.5t137 41.5q58 0 105 -12.5t86 -39.5z" />
+    <glyph glyph-name="scircumflex.sc" horiz-adv-x="500" 
+d="M394 447q-41 28 -74.5 39t-75.5 11q-54 0 -78 -21.5t-24 -52.5t16.5 -46.5t51.5 -24.5l93 -24q37 -10 66 -23.5t49.5 -33.5t31 -47.5t10.5 -66.5q0 -35 -13.5 -65.5t-40.5 -53t-67.5 -35.5t-95.5 -13q-32 0 -58.5 2.5t-50.5 8.5t-47 15.5t-47 24.5l31 80
+q24 -15 44.5 -25.5t40.5 -17t42 -9.5t49 -3q63 0 95 23.5t32 66.5q0 42 -23 61.5t-76 32.5l-101 24q-59 14 -89.5 51t-30.5 96q0 66 50 107.5t137 41.5q58 0 105 -12.5t86 -39.5zM250 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="scommaaccent.sc" horiz-adv-x="500" 
+d="M394 447q-41 28 -74.5 39t-75.5 11q-54 0 -78 -21.5t-24 -52.5t16.5 -46.5t51.5 -24.5l93 -24q37 -10 66 -23.5t49.5 -33.5t31 -47.5t10.5 -66.5q0 -35 -13.5 -65.5t-40.5 -53t-67.5 -35.5t-95.5 -13q-32 0 -58.5 2.5t-50.5 8.5t-47 15.5t-47 24.5l31 80
+q24 -15 44.5 -25.5t40.5 -17t42 -9.5t49 -3q63 0 95 23.5t32 66.5q0 42 -23 61.5t-76 32.5l-101 24q-59 14 -89.5 51t-30.5 96q0 66 50 107.5t137 41.5q58 0 105 -12.5t86 -39.5zM175 -225q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5
+q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="section.case" horiz-adv-x="516" 
+d="M388 520q-30 20 -59.5 28t-64.5 8q-46 0 -71.5 -14t-25.5 -50q0 -17 7.5 -27.5t20 -17.5t28 -11t31.5 -8l63 -15q67 -16 110.5 -51t43.5 -97q0 -40 -20 -74.5t-57 -54.5q23 -19 34 -45.5t11 -56.5q0 -38 -18 -63.5t-46.5 -42t-64 -23.5t-69.5 -7q-51 0 -97 9.5t-85 29.5
+l25 78q42 -25 79 -32.5t77 -7.5q22 0 42 2.5t35.5 9.5t24.5 20t9 33q0 32 -23 46t-61 23l-81 20q-74 18 -107.5 56t-33.5 90q0 82 80 127q-23 17 -34.5 37.5t-11.5 51.5q0 35 16 61.5t42 44t58.5 26t66.5 8.5q49 0 88.5 -10t79.5 -33zM184 360q-28 -15 -40.5 -31.5
+t-12.5 -43.5q0 -36 23.5 -52t71.5 -28l109 -27q24 11 37 32t13 45q0 19 -6.5 32t-18.5 22t-29.5 15.5t-40.5 11.5z" />
+    <glyph glyph-name="seven.denominator" horiz-adv-x="330" 
+d="M305 370v-46l-165 -324h-77l161 308h-199v62h280z" />
+    <glyph glyph-name="seven.fitted" 
+d="M508 650v-58l-256 -592h-103l254 566h-321v84h426z" />
+    <glyph glyph-name="seven.numerator" horiz-adv-x="330" 
+d="M305 650v-46l-165 -324h-77l161 308h-199v62h280z" />
+    <glyph glyph-name="seven.sc" horiz-adv-x="462" 
+d="M432 560v-50l-241 -510h-98l229 484h-277v76h387z" />
+    <glyph glyph-name="seven.tab.sc" 
+d="M488 560v-50l-241 -510h-98l229 484h-277v76h387z" />
+    <glyph glyph-name="seven.taboldstyle" 
+d="M513 510v-58l-270 -587h-99l262 562h-329v83h436z" />
+    <glyph glyph-name="sevenoldstyle" horiz-adv-x="536" 
+d="M481 510v-58l-270 -587h-99l262 562h-329v83h436z" />
+    <glyph glyph-name="sh.liga" horiz-adv-x="1075" 
+d="M406 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -20 10 -32t27.5 -19.5t42 -11.5t53.5 -7q47 -5 84 -15.5t62 -29t38 -46.5t13 -68q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 20 -7 33
+t-23 21t-42 13t-65 9q-98 10 -146 42.5t-48 102.5q0 30 13 56.5t39.5 46t67 31t94.5 11.5h19.5t18.5 -1q-7 15 -10.5 33.5t-3.5 42.5q0 35 13.5 64t38 50t58 32.5t73.5 11.5q47 0 84.5 -6.5t79.5 -21.5v-287q35 44 82.5 63t98.5 19q104 0 155.5 -56.5t51.5 -162.5v-291h-93
+v292q0 66 -25 104t-89 38q-56 0 -100.5 -22t-80.5 -66v-346h-93v660q-38 11 -79 11q-46 0 -71 -23.5t-25 -64.5q0 -35 16 -60t60 -44z" />
+    <glyph glyph-name="six.denominator" horiz-adv-x="362" 
+d="M99 162q0 -49 21 -79.5t63 -30.5t62 18t20 48q0 68 -70 68q-29 0 -51.5 -6t-44.5 -18zM297 319q-54 -1 -89.5 -7.5t-57 -19.5t-32.5 -32.5t-17 -46.5q21 16 45.5 24.5t56.5 8.5q26 0 50 -7t43 -22.5t30 -40t11 -58.5q0 -33 -12 -57t-33 -39.5t-49 -23t-60 -7.5
+q-38 0 -67 13.5t-48 38t-28.5 58t-9.5 73.5q0 105 67.5 155t195.5 50z" />
+    <glyph glyph-name="six.fitted" 
+d="M461 582q-62 0 -118 -13.5t-99 -42t-68 -73t-25 -106.5q34 30 74.5 46t95.5 16q44 0 82 -14.5t66 -41.5t44 -66t16 -87q0 -58 -21 -98.5t-54.5 -66t-75.5 -37t-85 -11.5q-54 0 -97.5 19.5t-74 59.5t-47.5 100.5t-17 142.5q0 90 31.5 156.5t85.5 110t126 65t153 21.5z
+M153 272q0 -54 11 -93t31.5 -63.5t49 -36t63.5 -11.5q61 0 94 33t33 99q0 63 -32.5 97t-95.5 34q-51 0 -86.5 -15t-67.5 -44z" />
+    <glyph glyph-name="six.numerator" horiz-adv-x="362" 
+d="M99 442q0 -49 21 -79.5t63 -30.5t62 18t20 48q0 68 -70 68q-29 0 -51.5 -6t-44.5 -18zM297 599q-54 -1 -89.5 -7.5t-57 -19.5t-32.5 -32.5t-17 -46.5q21 16 45.5 24.5t56.5 8.5q26 0 50 -7t43 -22.5t30 -40t11 -58.5q0 -33 -12 -57t-33 -39.5t-49 -23t-60 -7.5
+q-38 0 -67 13.5t-48 38t-28.5 58t-9.5 73.5q0 105 67.5 155t195.5 50z" />
+    <glyph glyph-name="six.sc" horiz-adv-x="512" 
+d="M415 497q-54 -2 -103.5 -11t-88 -30.5t-62 -57t-25.5 -89.5q61 50 150 50q42 0 78 -12.5t62 -36.5t41 -58t15 -76q0 -47 -18 -82t-47.5 -58t-67.5 -34.5t-79 -11.5q-112 0 -168.5 74.5t-56.5 209.5q0 143 94 219.5t269 76.5zM135 238q0 -85 37 -128.5t95 -43.5
+q55 0 89 26.5t34 83.5q0 108 -116 108q-39 0 -75.5 -11.5t-63.5 -34.5z" />
+    <glyph glyph-name="six.tab.sc" 
+d="M446 497q-54 -2 -103.5 -11t-88 -30.5t-62 -57t-25.5 -89.5q61 50 150 50q42 0 78 -12.5t62 -36.5t41 -58t15 -76q0 -47 -18 -82t-47.5 -58t-67.5 -34.5t-79 -11.5q-112 0 -168.5 74.5t-56.5 209.5q0 143 94 219.5t269 76.5zM166 238q0 -85 37 -128.5t95 -43.5
+q55 0 89 26.5t34 83.5q0 108 -116 108q-39 0 -75.5 -11.5t-63.5 -34.5z" />
+    <glyph glyph-name="six.taboldstyle" 
+d="M461 573q-62 0 -118.5 -11.5t-99 -37.5t-68 -69.5t-25.5 -107.5q34 30 75 46t96 16q44 0 82 -14.5t66 -41.5t44 -66t16 -87q0 -58 -21 -98.5t-54.5 -66t-75.5 -37t-85 -11.5q-109 0 -172 80.5t-63 247.5q0 90 32 154t87 104t127 58.5t153 18.5zM151 272q0 -55 12 -94
+t32 -63.5t46.5 -35.5t56.5 -11q67 0 102 33t35 99q0 63 -32.5 97t-95.5 34q-51 0 -87.5 -15t-68.5 -44z" />
+    <glyph glyph-name="sixoldstyle" horiz-adv-x="561" 
+d="M448 573q-62 0 -118.5 -11.5t-99 -37.5t-68 -69.5t-25.5 -107.5q34 30 75 46t96 16q44 0 82 -14.5t66 -41.5t44 -66t16 -87q0 -58 -21 -98.5t-54.5 -66t-75.5 -37t-85 -11.5q-109 0 -172 80.5t-63 247.5q0 90 32 154t87 104t127 58.5t153 18.5zM138 272q0 -55 12 -94
+t32 -63.5t46.5 -35.5t56.5 -11q67 0 102 33t35 99q0 63 -32.5 97t-95.5 34q-51 0 -87.5 -15t-68.5 -44z" />
+    <glyph glyph-name="sk.liga" horiz-adv-x="1032" 
+d="M627 292l80 53l174 155h118l-226 -201l229 -299h-108l-190 255l-77 -43v-212h-93v660q-38 11 -79 11q-46 0 -71 -23.5t-25 -64.5q0 -35 17 -59.5t59 -44.5l-29 -72q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -20 10 -32t27.5 -19.5t42 -11.5t53.5 -7
+q47 -5 84 -15.5t62 -29t38 -46.5t13 -68q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 20 -7 33t-23 21t-42 13t-65 9q-98 10 -146 42.5t-48 102.5q0 30 13 56.5t39.5 46t67 31t94.5 11.5h20.5t18.5 -1q-8 15 -11.5 33.5
+t-3.5 42.5q0 35 13.5 64t38 50t58 32.5t73.5 11.5q47 0 84.5 -6.5t79.5 -21.5v-423z" />
+    <glyph glyph-name="slash.case" horiz-adv-x="302" 
+d="M243 627l77 -25l-243 -697l-75 25z" />
+    <glyph glyph-name="space.tab" 
+ />
+    <glyph glyph-name="st.liga" horiz-adv-x="897" 
+d="M402 407q-39 18 -74.5 24.5t-76.5 6.5q-66 0 -95.5 -18.5t-29.5 -47.5q0 -20 10 -32t27.5 -19.5t42 -11.5t53.5 -7q47 -5 84 -15.5t62 -29t38 -46.5t13 -68q0 -78 -64 -115.5t-174 -37.5q-55 0 -96.5 7.5t-85.5 26.5l23 76q74 -35 155 -35q155 0 155 79q0 20 -7 33
+t-23 21t-42 13t-65 9q-98 10 -146 42.5t-48 102.5q0 30 13 56.5t39.5 46t67 31t94.5 11.5h21.5t20.5 -1q-7 15 -10.5 33.5t-3.5 42.5q0 35 15 64t40.5 50t59 32.5t69.5 11.5q43 0 78 -13t59.5 -34.5t37.5 -49t13 -56.5q0 -27 -4.5 -48.5t-13.5 -41.5h215l-3 -77l-196 8v-262
+q0 -57 26 -81t67 -24q33 0 59.5 9t54.5 26l30 -67q-31 -22 -67.5 -32t-86.5 -10q-86 0 -131.5 45t-45.5 133v255l-68 11v35q51 21 67 51t16 63q0 41 -28.5 68.5t-79.5 27.5q-46 0 -75 -27.5t-29 -68.5q0 -35 15 -59t57 -45z" />
+    <glyph glyph-name="t.sc" horiz-adv-x="496" 
+d="M466 560v-75h-172v-485h-92v485h-172v75h436z" />
+    <glyph glyph-name="tcaron.sc" horiz-adv-x="496" 
+d="M466 560v-75h-172v-485h-92v485h-172v75h436zM248 699l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="tcommaaccent.sc" horiz-adv-x="496" 
+d="M466 560v-75h-172v-485h-92v485h-172v75h436zM183 -218q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="thorn.sc" horiz-adv-x="546" 
+d="M155 570v-77h140q46 0 85 -15t67 -41t43.5 -60.5t15.5 -73.5q0 -44 -17 -80.5t-46.5 -62.5t-69 -40t-84.5 -14h-134v-106h-90v570h90zM155 181h127q60 0 96 31.5t36 90.5q0 55 -35.5 85t-107.5 30h-116v-237z" />
+    <glyph glyph-name="three.denominator" horiz-adv-x="357" 
+d="M262 207q60 -27 60 -90q0 -33 -13 -56.5t-35 -39t-50.5 -23t-59.5 -7.5q-25 0 -44 2t-35 7t-30.5 13t-29.5 20l24 55q29 -22 54 -30.5t57 -8.5q39 0 66 17t27 51q0 29 -16.5 40.5t-54.5 11.5h-83v58h74q29 0 44.5 9.5t15.5 35.5q0 47 -67 47q-54 0 -95 -31l-33 47
+q30 25 61 34.5t73 9.5q22 0 44.5 -6t41 -18t30.5 -30.5t12 -43.5q0 -27 -9 -44.5t-29 -29.5z" />
+    <glyph glyph-name="three.fitted" 
+d="M290 395q48 0 76 27.5t28 69.5q0 46 -32.5 69.5t-77.5 23.5q-50 0 -85.5 -11.5t-72.5 -41.5l-41 65q47 37 94.5 51.5t107.5 14.5q44 0 80.5 -12t63 -33.5t41 -51t14.5 -65.5q0 -40 -18.5 -76.5t-59.5 -54.5v-4q51 -18 82.5 -59.5t31.5 -104.5q0 -44 -16.5 -83t-47 -68.5
+t-75.5 -46.5t-102 -17q-75 0 -128 18t-96 59l44 77q44 -41 83.5 -57t90.5 -16q33 0 61.5 9t49 26.5t32.5 42t12 54.5q0 63 -38.5 90.5t-96.5 27.5h-105v77h100z" />
+    <glyph glyph-name="three.numerator" horiz-adv-x="357" 
+d="M262 487q60 -27 60 -90q0 -33 -13 -56.5t-35 -39t-50.5 -23t-59.5 -7.5q-25 0 -44 2t-35 7t-30.5 13t-29.5 20l24 55q29 -22 54 -30.5t57 -8.5q39 0 66 17t27 51q0 29 -16.5 40.5t-54.5 11.5h-83v58h74q29 0 44.5 9.5t15.5 35.5q0 47 -67 47q-54 0 -95 -31l-33 47
+q30 25 61 34.5t73 9.5q22 0 44.5 -6t41 -18t30.5 -30.5t12 -43.5q0 -27 -9 -44.5t-29 -29.5z" />
+    <glyph glyph-name="three.sc" horiz-adv-x="502" 
+d="M246 336q42 0 64 24.5t22 59.5q0 39 -30.5 58t-69.5 19q-46 0 -75.5 -12t-55.5 -36l-43 61q41 32 81.5 46t95.5 14q38 0 71.5 -10.5t59 -29.5t40.5 -45.5t15 -57.5q0 -36 -17.5 -68t-52.5 -47v-4q44 -14 72.5 -49t28.5 -90q0 -39 -15 -72t-43.5 -56.5t-68.5 -37
+t-89 -13.5q-69 0 -116.5 14t-84.5 51l41 68q19 -17 36.5 -28t35.5 -17.5t37 -9t42 -2.5q63 0 99.5 24.5t36.5 77.5q0 52 -29.5 74.5t-80.5 22.5h-107v71h100z" />
+    <glyph glyph-name="three.tab.sc" 
+d="M295 336q42 0 64 24.5t22 59.5q0 39 -30.5 58t-69.5 19q-46 0 -75.5 -12t-55.5 -36l-43 61q41 32 81.5 46t95.5 14q38 0 71.5 -10.5t59 -29.5t40.5 -45.5t15 -57.5q0 -36 -17.5 -68t-52.5 -47v-4q44 -14 72.5 -49t28.5 -90q0 -39 -15 -72t-43.5 -56.5t-68.5 -37
+t-89 -13.5q-69 0 -116.5 14t-84.5 51l41 68q19 -17 36.5 -28t35.5 -17.5t37 -9t42 -2.5q63 0 99.5 24.5t36.5 77.5q0 52 -29.5 74.5t-80.5 22.5h-107v71h100z" />
+    <glyph glyph-name="three.taboldstyle" 
+d="M290 261q48 0 74 22.5t26 64.5q0 46 -30 70t-83 24q-42 0 -77.5 -11.5t-77.5 -40.5l-36 63q45 36 91 51t108 15q44 0 80 -12t61.5 -33t39.5 -50.5t14 -65.5q0 -42 -18 -73t-59 -49v-4q52 -17 82 -54t30 -97q0 -57 -22.5 -99.5t-61 -70.5t-89 -42t-105.5 -14
+q-48 0 -87 6.5t-79 23.5l26 77q18 -8 34 -13t32.5 -8t35 -4.5t41.5 -1.5q40 0 74.5 10.5t59 29.5t38.5 45.5t14 58.5q0 60 -36 83t-94 23h-112v76h106z" />
+    <glyph glyph-name="threeoldstyle" horiz-adv-x="545" 
+d="M265 261q48 0 74 22.5t26 64.5q0 46 -30 70t-83 24q-42 0 -77.5 -11.5t-77.5 -40.5l-36 63q45 36 91 51t108 15q44 0 80 -12t61.5 -33t39.5 -50.5t14 -65.5q0 -42 -18 -73t-59 -49v-4q52 -17 82 -54t30 -97q0 -57 -22.5 -99.5t-61 -70.5t-89 -42t-105.5 -14
+q-48 0 -87 6.5t-79 23.5l26 77q18 -8 34 -13t32.5 -8t35 -4.5t41.5 -1.5q40 0 74.5 10.5t59 29.5t38.5 45.5t14 58.5q0 60 -36 83t-94 23h-112v76h106z" />
+    <glyph glyph-name="two.denominator" horiz-adv-x="332" 
+d="M296 0h-271v61l157 119q26 19 37.5 37t11.5 41q0 26 -17.5 41.5t-54.5 15.5q-30 0 -54 -10.5t-48 -33.5l-34 51q29 29 62.5 43t78.5 14q63 0 100.5 -32.5t37.5 -87.5q0 -33 -14 -62t-42 -49l-117 -86h167v-62z" />
+    <glyph glyph-name="two.fitted" 
+d="M89 76l224 219q51 50 79 88.5t28 85.5q0 56 -35 84t-91 28q-53 0 -93.5 -13.5t-80.5 -45.5l-45 71q53 39 104.5 54.5t119.5 15.5q48 0 86.5 -13t66.5 -38t43 -60.5t15 -80.5q0 -57 -33 -112t-90 -110l-169 -160v-4h299v-85h-428v76z" />
+    <glyph glyph-name="two.numerator" horiz-adv-x="332" 
+d="M296 280h-271v61l157 119q26 19 37.5 37t11.5 41q0 26 -17.5 41.5t-54.5 15.5q-30 0 -54 -10.5t-48 -33.5l-34 51q29 29 62.5 43t78.5 14q63 0 100.5 -32.5t37.5 -87.5q0 -33 -14 -62t-42 -49l-117 -86h167v-62z" />
+    <glyph glyph-name="two.sc" horiz-adv-x="504" 
+d="M68 70l210 188q22 20 38.5 37t27 33.5t15.5 34.5t5 40q0 24 -9.5 41t-25 28.5t-36.5 17t-43 5.5q-47 0 -81.5 -14t-72.5 -46l-44 66q22 18 44 31t46 21.5t51.5 12.5t60.5 4q42 0 78.5 -11.5t63.5 -32.5t42 -51.5t15 -69.5q0 -26 -4.5 -49t-16 -45.5t-31 -46t-49.5 -48.5
+l-157 -135v-4h258v-77h-385v70z" />
+    <glyph glyph-name="two.tab.sc" 
+d="M106 70l210 188q22 20 38.5 37t27 33.5t15.5 34.5t5 40q0 24 -9.5 41t-25 28.5t-36.5 17t-43 5.5q-47 0 -81.5 -14t-72.5 -46l-44 66q22 18 44 31t46 21.5t51.5 12.5t60.5 4q42 0 78.5 -11.5t63.5 -32.5t42 -51.5t15 -69.5q0 -26 -4.5 -49t-16 -45.5t-31 -46t-49.5 -48.5
+l-157 -135v-4h258v-77h-385v70z" />
+    <glyph glyph-name="two.taboldstyle" 
+d="M315 224q28 18 47 32.5t30.5 29.5t16.5 31t5 36q0 88 -121 88q-47 0 -85.5 -13t-85.5 -47l-37 68q48 37 99.5 53.5t111.5 16.5q99 0 153 -44.5t54 -119.5q0 -54 -29.5 -97t-91.5 -80l-154 -94v-4h271v-80h-413v79z" />
+    <glyph glyph-name="twooldstyle" horiz-adv-x="548" 
+d="M299 224q28 18 47 32.5t30.5 29.5t16.5 31t5 36q0 88 -121 88q-47 0 -85.5 -13t-85.5 -47l-37 68q48 37 99.5 53.5t111.5 16.5q99 0 153 -44.5t54 -119.5q0 -54 -29.5 -97t-91.5 -80l-154 -94v-4h271v-80h-413v79z" />
+    <glyph glyph-name="u.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91z" />
+    <glyph glyph-name="uacute.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM354 792l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="ubreve.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM213 740q14 -29 39.5 -44t56.5 -15q68 0 97 59l60 -27
+q-17 -53 -58.5 -79t-98.5 -26q-56 0 -98.5 27t-57.5 78z" />
+    <glyph glyph-name="ucircumflex.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM309 698l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="udieresis.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM199 749q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17
+q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM418 749q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z" />
+    <glyph glyph-name="ugrave.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM351 609h-69l-98 136l78 47z" />
+    <glyph glyph-name="uhungarumlaut.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM264 798l82 -30l-69 -159h-64zM402 788l76 -47l-93 -132h-67z
+" />
+    <glyph glyph-name="umacron.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM472 705v-73h-326v73h326z" />
+    <glyph glyph-name="uni00AD.case" horiz-adv-x="381" 
+d="M341 363v-76h-301v76h301z" />
+    <glyph glyph-name="uni0201.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM379 563h-64l-69 153l82 30zM274 563h-67l-93 126l76 47z" />
+    <glyph glyph-name="uni0201.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM400 619h-64l-69 159l82 30zM295 619h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni0203.alt1" horiz-adv-x="602" 
+d="M79 466q56 23 103.5 33.5t109.5 10.5q111 0 163.5 -41t52.5 -133v-209q0 -36 12 -48.5t50 -12.5v-66q-17 -5 -31.5 -7.5t-34.5 -2.5q-31 0 -49 22t-21 60q-69 -82 -206 -82q-88 0 -133.5 39.5t-45.5 107.5q0 40 16 68t43 46.5t63 28.5t75 15q38 5 79.5 7.5t92.5 3.5v27
+q0 52 -28.5 76t-101.5 24q-94 0 -189 -41zM418 238q-50 -1 -90 -3t-74 -8q-54 -9 -82.5 -27.5t-28.5 -57.5q0 -41 25.5 -58.5t72.5 -17.5q50 0 92.5 15t84.5 51v106zM291 622l-112 -77l-23 48l123 125h23l124 -125l-23 -48z" />
+    <glyph glyph-name="uni0203.sc" horiz-adv-x="642" 
+d="M375 560l242 -560h-94l-71 162h-266l-69 -162h-92l244 560h106zM217 239h204l-98 244h-6zM321 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni0205.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM359 619h-64l-69 159l82 30zM254 619h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni0207.sc" horiz-adv-x="508" 
+d="M65 560h381v-74h-290v-155h265v-74h-265v-182h302v-75h-393v560zM263 698l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni0209.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91zM203 619h-64l-69 159l82 30zM98 619h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni020B.sc" horiz-adv-x="221" 
+d="M156 560v-560h-91v560h91zM111 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni020D.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM430 619h-64l-69 159l82 30zM325 619h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni020F.sc" horiz-adv-x="681" 
+d="M641 280q0 -65 -23 -118t-63.5 -91.5t-95.5 -59.5t-119 -21t-119 21t-95 59.5t-63 91.5t-23 118q0 64 23 117.5t63 92t95 59.5t119 21q65 0 120 -21t95.5 -59.5t63 -92t22.5 -117.5zM132 280q0 -52 16.5 -91.5t45 -66.5t66.5 -40.5t80 -13.5q43 0 80.5 13t66 39.5
+t45 66.5t16.5 93t-16.5 93t-45 66.5t-66 39.5t-80.5 13q-42 0 -80 -13t-66.5 -39.5t-45 -66.5t-16.5 -93zM340 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni0211.sc" horiz-adv-x="565" 
+d="M291 560q45 0 82.5 -11t64 -32t41 -50.5t14.5 -66.5q0 -32 -9.5 -58t-26.5 -45.5t-40 -33t-49 -21.5l159 -242h-105l-148 234h-119v-234h-90v560h226zM155 308h128q25 0 47 4.5t38.5 15t26 28.5t9.5 44q0 48 -33 67t-93 19h-123v-178zM350 619h-64l-69 159l82 30z
+M245 619h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni0213.sc" horiz-adv-x="565" 
+d="M291 560q45 0 82.5 -11t64 -32t41 -50.5t14.5 -66.5q0 -32 -9.5 -58t-26.5 -45.5t-40 -33t-49 -21.5l159 -242h-105l-148 234h-119v-234h-90v560h226zM155 308h128q25 0 47 4.5t38.5 15t26 28.5t9.5 44q0 48 -33 67t-93 19h-123v-178zM261 698l-132 -86l-21 54l141 119
+h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni0215.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM401 609h-64l-69 159l82 30zM296 609h-67l-93 132l76 47z" />
+    <glyph glyph-name="uni0217.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM309 698l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="uni021B.sc" horiz-adv-x="496" 
+d="M466 560v-75h-172v-485h-92v485h-172v75h436zM175 -226q62 24 62 68q-30 0 -43.5 16.5t-13.5 37.5q0 10 3 20t10 18.5t18.5 14t28.5 5.5q35 0 53 -25.5t18 -62.5q0 -19 -7 -39.5t-20.5 -39t-34.5 -34t-49 -24.5z" />
+    <glyph glyph-name="uogonek.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -105 -51.5 -167.5t-135.5 -78.5q-26 -15 -34.5 -31t-8.5 -35q0 -17 10.5 -30.5t38.5 -13.5q7 0 14.5 1t16.5 4l-16 -65q-10 -2 -19.5 -3t-17.5 -1q-48 0 -76 24.5t-28 62.5
+q0 21 9.5 43.5t30.5 39.5q-48 3 -89 20t-70 47.5t-45.5 75t-16.5 102.5v325h91z" />
+    <glyph glyph-name="uring.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM309 585q-52 0 -84.5 31.5t-32.5 80.5q0 23 9 43.5t24.5 35.5
+t37 23.5t46.5 8.5t46.5 -8.5t37 -23.5t24.5 -35.5t9 -43.5q0 -50 -33 -81t-84 -31zM365 697q0 24 -16 39.5t-40 15.5t-40.5 -15.5t-16.5 -39.5t16.5 -40t40.5 -16t40 16t16 40z" />
+    <glyph glyph-name="utilde.sc" horiz-adv-x="618" 
+d="M156 560v-332q0 -81 42 -123t111 -42q33 0 61 10.5t48.5 31.5t32 51.5t11.5 71.5v332h91v-320q0 -61 -18 -107.5t-50.5 -78.5t-77 -48.5t-98.5 -16.5q-53 0 -97.5 15.5t-77 46t-51 76.5t-18.5 108v325h91zM128 686q22 22 52.5 33.5t64.5 11.5q24 0 41 -6t32 -12.5
+t29.5 -12.5t34.5 -6q21 0 36 6t32 23l41 -48q-43 -55 -108 -55q-23 0 -41 6t-35 13.5t-33.5 13.5t-34.5 6q-49 0 -83 -39z" />
+    <glyph glyph-name="v.sc" horiz-adv-x="635" 
+d="M129 560l187 -480h12l181 480h101l-229 -560h-124l-232 560h104z" />
+    <glyph glyph-name="w.sc" horiz-adv-x="925" 
+d="M124 560l140 -480h7l131 480h120l133 -480h7l140 480h98l-177 -560h-135l-126 456h-5l-125 -456h-134l-173 560h99z" />
+    <glyph glyph-name="wcircumflex.sc" horiz-adv-x="925" 
+d="M124 560l140 -480h7l131 480h120l133 -480h7l140 480h98l-177 -560h-135l-126 456h-5l-125 -456h-134l-173 560h99zM462 688l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="x.sc" horiz-adv-x="623" 
+d="M149 560l164 -222l160 222h109l-209 -270l225 -290h-113l-175 241l-176 -241h-109l224 288l-210 272h110z" />
+    <glyph glyph-name="y.sc" horiz-adv-x="579" 
+d="M246 0v232l-221 328h103l162 -245h5l159 245h100l-219 -328v-232h-89z" />
+    <glyph glyph-name="yacute.sc" horiz-adv-x="579" 
+d="M246 0v232l-221 328h103l162 -245h5l159 245h100l-219 -328v-232h-89zM332 792l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="ycircumflex.sc" horiz-adv-x="579" 
+d="M246 0v232l-221 328h103l162 -245h5l159 245h100l-219 -328v-232h-89zM289 698l-132 -86l-21 54l141 119h23l142 -119l-21 -54z" />
+    <glyph glyph-name="ydieresis.sc" horiz-adv-x="579" 
+d="M246 0v232l-221 328h103l162 -245h5l159 245h100l-219 -328v-232h-89zM179 749q31 0 47.5 -17t16.5 -44q0 -26 -16.5 -43t-47.5 -17q-29 0 -45.5 17t-16.5 43q0 27 16.5 44t45.5 17zM398 749q29 0 46 -17t17 -44q0 -26 -17 -43t-46 -17t-46 17t-17 43q0 27 17 44t46 17z
+" />
+    <glyph glyph-name="yen.sc" horiz-adv-x="588" 
+d="M131 551l162 -280h6l160 280h104l-179 -286h66v-62h-308v62h64l-181 286h106zM450 165v-62h-108v-103h-92v103h-108v62h308z" />
+    <glyph glyph-name="yen.tab.sc" 
+d="M132 551l162 -280h6l160 280h104l-179 -286h66v-62h-308v62h64l-181 286h106zM451 165v-62h-108v-103h-92v103h-108v62h308z" />
+    <glyph glyph-name="yen.taboldstyle" 
+d="M111 500l183 -331h7l182 331h103l-193 -327h71v-67h-332v67h69l-195 327h105zM464 71v-67h-120v-141h-94v141h-118v67h332z" />
+    <glyph glyph-name="yenoldstyle" horiz-adv-x="630" 
+d="M130 500l183 -331h7l182 331h103l-193 -327h71v-67h-332v67h69l-195 327h105zM483 71v-67h-120v-141h-94v141h-118v67h332z" />
+    <glyph glyph-name="z.sc" horiz-adv-x="542" 
+d="M500 560v-57l-340 -428h342v-75h-462v60l341 426h-331v74h450z" />
+    <glyph glyph-name="zacute.sc" horiz-adv-x="542" 
+d="M500 560v-57l-340 -428h342v-75h-462v60l341 426h-331v74h450zM305 802l78 -47l-98 -136h-69z" />
+    <glyph glyph-name="zcaron.sc" horiz-adv-x="542" 
+d="M500 560v-57l-340 -428h342v-75h-462v60l341 426h-331v74h450zM271 699l132 86l21 -54l-141 -119h-23l-142 119l21 54z" />
+    <glyph glyph-name="zdotaccent.sc" horiz-adv-x="542" 
+d="M500 560v-57l-340 -428h342v-75h-462v60l341 426h-331v74h450zM268 742q32 0 49.5 -19t17.5 -47q0 -29 -17.5 -46t-49.5 -17t-49 17t-17 46q0 28 17 47t49 19z" />
+    <glyph glyph-name="zero.denominator" horiz-adv-x="448" 
+d="M103 184q0 -58 32 -94t89 -36t88.5 35.5t31.5 94.5q0 60 -32 95.5t-88 35.5t-88.5 -36.5t-32.5 -94.5zM418 184q0 -37 -12.5 -72t-37 -62t-61 -43t-83.5 -16t-83.5 16t-61 43t-37 62t-12.5 72t13 72.5t38 62.5t61 43.5t82 16.5t82 -16.5t61 -43.5t38 -62.5t13 -72.5z" />
+    <glyph glyph-name="zero.fitted" 
+d="M565 325q0 -75 -17.5 -137t-51.5 -107t-84.5 -69.5t-116.5 -24.5t-116.5 24.5t-84.5 69.5t-51.5 107t-17.5 137t17.5 137t51.5 107t84.5 69.5t116.5 24.5t116.5 -24.5t84.5 -69.5t51.5 -107t17.5 -137zM121 325q0 -125 42.5 -191.5t131.5 -66.5t131.5 66.5t42.5 191.5
+t-42.5 192t-131.5 67t-131.5 -67t-42.5 -192z" />
+    <glyph glyph-name="zero.numerator" horiz-adv-x="448" 
+d="M103 464q0 -58 32 -94t89 -36t88.5 35.5t31.5 94.5q0 60 -32 95.5t-88 35.5t-88.5 -36.5t-32.5 -94.5zM418 464q0 -37 -12.5 -72t-37 -62t-61 -43t-83.5 -16t-83.5 16t-61 43t-37 62t-12.5 72t13 72.5t38 62.5t61 43.5t82 16.5t82 -16.5t61 -43.5t38 -62.5t13 -72.5z" />
+    <glyph glyph-name="zero.sc" horiz-adv-x="652" 
+d="M612 280q0 -65 -22 -118t-60.5 -91.5t-91 -59.5t-112.5 -21t-112.5 21t-91 59.5t-60.5 91.5t-22 118t22 118.5t60.5 91.5t91 59t112.5 21t112.5 -21t91 -59t60.5 -91.5t22 -118.5zM133 280q0 -51 14.5 -91.5t40.5 -68t61 -42t77 -14.5q41 0 76.5 14.5t61.5 42t41 68
+t15 91.5q0 52 -15 92t-41 68t-61.5 42.5t-76.5 14.5q-42 0 -77 -14.5t-61 -42.5t-40.5 -68t-14.5 -92z" />
+    <glyph glyph-name="zero.slash" horiz-adv-x="720" 
+d="M680 325q0 -75 -24 -137t-66.5 -107t-101 -69.5t-128.5 -24.5t-128.5 24.5t-101 69.5t-66.5 107t-24 137t24 137t66.5 107t101 69.5t128.5 24.5t128.5 -24.5t101 -69.5t66.5 -107t24 -137zM462 560q-45 24 -102 24q-48 0 -89 -17.5t-71 -50.5t-47 -81t-17 -110
+q0 -65 18 -114t50 -82zM270 84q42 -17 90 -17t89 17t71 50t47 81t17 110q0 59 -15.5 105t-42.5 79z" />
+    <glyph glyph-name="zero.slash.fitted" 
+d="M565 325q0 -75 -17.5 -137t-51.5 -107t-84.5 -69.5t-116.5 -24.5t-116.5 24.5t-84.5 69.5t-51.5 107t-17.5 137t17.5 137t51.5 107t84.5 69.5t116.5 24.5t116.5 -24.5t84.5 -69.5t51.5 -107t17.5 -137zM342 578q-21 6 -47 6q-89 0 -131.5 -67t-42.5 -192q0 -75 15 -128.5
+t46 -85.5zM252 71q10 -2 21 -3t22 -1q89 0 131.5 66.5t42.5 191.5q0 71 -13.5 124t-42.5 85z" />
+    <glyph glyph-name="zero.slash.oldstyle" horiz-adv-x="618" 
+d="M40 255q0 60 19.5 109t55 83.5t85 53.5t109.5 19t109.5 -19t85 -53.5t55 -83.5t19.5 -109t-19.5 -109t-55 -83.5t-85 -53.5t-109.5 -19t-109.5 19t-85 53.5t-55 83.5t-19.5 109zM234 79q17 -6 35.5 -9.5t39.5 -3.5q84 0 130.5 51t46.5 138q0 42 -11.5 75.5t-32.5 57.5z
+M378 432q-30 12 -69 12q-84 0 -130.5 -51t-46.5 -138q0 -80 40 -131z" />
+    <glyph glyph-name="zero.slash.sc" horiz-adv-x="652" 
+d="M612 278q0 -65 -22 -118.5t-60.5 -91.5t-91 -59.5t-112.5 -21.5t-112.5 21.5t-91 59.5t-60.5 91.5t-22 118.5t22 118.5t60.5 92t91 60t112.5 21.5t112.5 -21.5t91 -60t60.5 -92t22 -118.5zM261 71q29 -10 65 -10q41 0 76.5 14.5t61.5 42.5t41 68.5t15 91.5
+q0 56 -17.5 98.5t-46.5 70.5zM387 488q-29 9 -61 9q-42 0 -77 -15t-61 -43t-40.5 -68.5t-14.5 -92.5q0 -55 16 -97t45 -70z" />
+    <glyph glyph-name="zero.slash.tab.sc" 
+d="M561 280q0 -65 -20.5 -118t-56 -91.5t-84 -59.5t-105.5 -21t-105.5 21t-84 59.5t-56 91.5t-20.5 118t20.5 118.5t56 91.5t84 59t105.5 21t105.5 -21t84 -59t56 -91.5t20.5 -118.5zM352 487q-14 5 -28 7.5t-29 2.5q-38 0 -69.5 -14.5t-54.5 -42.5t-36 -68t-13 -92
+q0 -57 15.5 -100t42.5 -71zM249 70q21 -6 46 -6q37 0 69 14.5t55.5 42t36.5 68t13 91.5q0 52 -13.5 92t-36.5 68z" />
+    <glyph glyph-name="zero.slash.taboldstyle" 
+d="M36 255q0 60 18 109t51.5 83.5t81.5 53.5t108 19t108 -19t81.5 -53.5t51.5 -83.5t18 -109t-18 -109t-51.5 -83.5t-81.5 -53.5t-108 -19t-108 19t-81.5 53.5t-51.5 83.5t-18 109zM340 439q-23 5 -45 5q-84 0 -125.5 -51t-41.5 -138q0 -100 53 -150zM252 70q17 -4 43 -4
+q84 0 125.5 51t41.5 138q0 97 -51 147z" />
+    <glyph glyph-name="zero.tab.sc" 
+d="M561 280q0 -65 -20.5 -118t-56 -91.5t-84 -59.5t-105.5 -21t-105.5 21t-84 59.5t-56 91.5t-20.5 118t20.5 118.5t56 91.5t84 59t105.5 21t105.5 -21t84 -59t56 -91.5t20.5 -118.5zM122 280q0 -51 13 -91.5t36 -68t54.5 -42t69.5 -14.5q37 0 69 14.5t55.5 42t36.5 68
+t13 91.5q0 52 -13 92t-36.5 68t-55.5 42.5t-69 14.5q-38 0 -69.5 -14.5t-54.5 -42.5t-36 -68t-13 -92z" />
+    <glyph glyph-name="zero.taboldstyle" 
+d="M462 255q0 87 -41.5 138t-125.5 51t-125.5 -51t-41.5 -138t41.5 -138t125.5 -51t125.5 51t41.5 138zM36 255q0 60 18 109t51.5 83.5t81.5 53.5t108 19t108 -19t81.5 -53.5t51.5 -83.5t18 -109t-18 -109t-51.5 -83.5t-81.5 -53.5t-108 -19t-108 19t-81.5 53.5t-51.5 83.5
+t-18 109z" />
+    <glyph glyph-name="zerooldstyle" horiz-adv-x="618" 
+d="M486 255q0 87 -46.5 138t-130.5 51t-130.5 -51t-46.5 -138t46.5 -138t130.5 -51t130.5 51t46.5 138zM40 255q0 60 19.5 109t55 83.5t85 53.5t109.5 19t109.5 -19t85 -53.5t55 -83.5t19.5 -109t-19.5 -109t-55 -83.5t-85 -53.5t-109.5 -19t-109.5 19t-85 53.5t-55 83.5
+t-19.5 109z" />
+    <glyph glyph-name="idlstlogo" horiz-adv-x="6435" 
+d="M1285 387v-349h46v-114h-46v76h-447v-76h-46v114q19 0 36 7.5t30 20t20 29t7 34.5v258h400zM929 137q0 -29 -10.5 -56t-32.5 -43h353v311h-310v-212zM2735 0h-49l-57 99h-262l-57 -99h-49l238 410zM2048 387v-38h-401v-136h401v-38h-401v-137h401v-38h-447v387h447z
+M6315 1009l-448 -387l149 259l-149 128h448zM166 387v-334l402 349v-402h-46v307l-356 -307h-46v387h46zM4834 387q35 0 58.5 -21t29.5 -54h-46q-6 37 -42 37h-270q-19 0 -31.5 -13t-12.5 -33v-219q0 -17 12.5 -31.5t31.5 -14.5h270q17 0 28 12t14 26h46q-6 -32 -29.5 -54
+t-58.5 -22h-270q-18 0 -34.5 7.5t-28.5 20t-19.5 29t-7.5 34.5v208q0 19 7.5 35t19.5 28t28.5 18.5t34.5 6.5h270zM3396 387v-387h-46v349h-310v-258q0 -37 -23.5 -64t-68.5 -27v38q25 0 36.5 14.5t11.5 31.5v303h400zM5598 387v-38h-203v-349h-46v349h-203v38h452z
+M2388 137h220l-109 194zM3757 387v-334l402 349v-402h-46v307l-356 -307h-46v387h46z" />
+    <hkern u1="&#x20;" g2="j.sc" k="-70" />
+    <hkern u1="&#x22;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x22;" g2="three.sc" k="10" />
+    <hkern u1="&#x22;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#x22;" g2="nine.sc" k="30" />
+    <hkern u1="&#x22;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#x22;" g2="four.sc" k="100" />
+    <hkern u1="&#x22;" g2="eight.sc" k="30" />
+    <hkern u1="&#x22;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#x22;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x22;" u2="&#x34;" k="70" />
+    <hkern u1="&#x23;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x23;" g2="two.sc" k="40" />
+    <hkern u1="&#x23;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x23;" g2="three.sc" k="20" />
+    <hkern u1="&#x23;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x23;" g2="seven.sc" k="40" />
+    <hkern u1="&#x23;" g2="s.sc" k="20" />
+    <hkern u1="&#x23;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x23;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x23;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x23;" u2="&#x442;" k="50" />
+    <hkern u1="&#x23;" u2="&#x37;" k="60" />
+    <hkern u1="&#x24;" g2="s.sc" k="10" />
+    <hkern u1="&#x27;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x27;" g2="three.sc" k="10" />
+    <hkern u1="&#x27;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#x27;" g2="nine.sc" k="30" />
+    <hkern u1="&#x27;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#x27;" g2="four.sc" k="100" />
+    <hkern u1="&#x27;" g2="eight.sc" k="30" />
+    <hkern u1="&#x27;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#x27;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x27;" u2="&#x34;" k="70" />
+    <hkern u1="&#x2a;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2a;" g2="three.sc" k="10" />
+    <hkern u1="&#x2a;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#x2a;" g2="nine.sc" k="30" />
+    <hkern u1="&#x2a;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#x2a;" g2="four.sc" k="100" />
+    <hkern u1="&#x2a;" g2="eight.sc" k="30" />
+    <hkern u1="&#x2a;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#x2a;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x2a;" u2="&#x34;" k="70" />
+    <hkern u1="&#x2b;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2b;" g2="two.sc" k="40" />
+    <hkern u1="&#x2b;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2b;" g2="three.sc" k="20" />
+    <hkern u1="&#x2b;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2b;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2b;" g2="s.sc" k="20" />
+    <hkern u1="&#x2b;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2b;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2b;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2b;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2b;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2c;" g2="sevenoldstyle" k="40" />
+    <hkern u1="&#x2c;" g2="eightoldstyle" k="20" />
+    <hkern u1="&#x2c;" g2="afii10092.sc" k="90" />
+    <hkern u1="&#x2c;" g2="afii10089.sc" k="90" />
+    <hkern u1="&#x2c;" u2="&#x44a;" k="80" />
+    <hkern u1="&#x2c;" u2="&#x447;" k="80" />
+    <hkern u1="&#x2c;" u2="&#x442;" k="90" />
+    <hkern u1="&#x2c;" u2="&#x39;" k="40" />
+    <hkern u1="&#x2c;" u2="&#x38;" k="30" />
+    <hkern u1="&#x2c;" u2="&#x37;" k="70" />
+    <hkern u1="&#x2c;" u2="&#x34;" k="40" />
+    <hkern u1="&#x2d;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2d;" g2="two.sc" k="40" />
+    <hkern u1="&#x2d;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2d;" g2="three.sc" k="20" />
+    <hkern u1="&#x2d;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2d;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2d;" g2="s.sc" k="20" />
+    <hkern u1="&#x2d;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2d;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2d;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2d;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2d;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2e;" g2="sevenoldstyle" k="40" />
+    <hkern u1="&#x2e;" g2="eightoldstyle" k="20" />
+    <hkern u1="&#x2e;" g2="afii10092.sc" k="90" />
+    <hkern u1="&#x2e;" g2="afii10089.sc" k="90" />
+    <hkern u1="&#x2e;" u2="&#x44a;" k="80" />
+    <hkern u1="&#x2e;" u2="&#x447;" k="80" />
+    <hkern u1="&#x2e;" u2="&#x442;" k="90" />
+    <hkern u1="&#x2e;" u2="&#x39;" k="40" />
+    <hkern u1="&#x2e;" u2="&#x38;" k="30" />
+    <hkern u1="&#x2e;" u2="&#x37;" k="70" />
+    <hkern u1="&#x2e;" u2="&#x34;" k="40" />
+    <hkern u1="&#x2f;" g2="zerooldstyle" k="50" />
+    <hkern u1="&#x2f;" g2="zero.slash.sc" k="40" />
+    <hkern u1="&#x2f;" g2="zero.slash.oldstyle" k="50" />
+    <hkern u1="&#x2f;" g2="zero.slash" k="30" />
+    <hkern u1="&#x2f;" g2="zero.sc" k="40" />
+    <hkern u1="&#x2f;" g2="uni020F.sc" k="40" />
+    <hkern u1="&#x2f;" g2="uni020D.sc" k="40" />
+    <hkern u1="&#x2f;" g2="uni0203.sc" k="90" />
+    <hkern u1="&#x2f;" g2="uni0201.sc" k="90" />
+    <hkern u1="&#x2f;" g2="st.liga" k="50" />
+    <hkern u1="&#x2f;" g2="sk.liga" k="50" />
+    <hkern u1="&#x2f;" g2="sixoldstyle" k="30" />
+    <hkern u1="&#x2f;" g2="six.sc" k="40" />
+    <hkern u1="&#x2f;" g2="sh.liga" k="50" />
+    <hkern u1="&#x2f;" g2="sb.liga" k="50" />
+    <hkern u1="&#x2f;" g2="q.sc" k="40" />
+    <hkern u1="&#x2f;" g2="otilde.sc" k="40" />
+    <hkern u1="&#x2f;" g2="oslash.sc" k="40" />
+    <hkern u1="&#x2f;" g2="oneoldstyle" k="30" />
+    <hkern u1="&#x2f;" g2="omacron.sc" k="40" />
+    <hkern u1="&#x2f;" g2="ohungarumlaut.sc" k="40" />
+    <hkern u1="&#x2f;" g2="ograve.sc" k="40" />
+    <hkern u1="&#x2f;" g2="oe.sc" k="40" />
+    <hkern u1="&#x2f;" g2="odieresis.sc" k="40" />
+    <hkern u1="&#x2f;" g2="ocircumflex.sc" k="40" />
+    <hkern u1="&#x2f;" g2="obreve.sc" k="40" />
+    <hkern u1="&#x2f;" g2="oacute.sc" k="40" />
+    <hkern u1="&#x2f;" g2="o.sc" k="40" />
+    <hkern u1="&#x2f;" g2="gdotaccent.sc" k="40" />
+    <hkern u1="&#x2f;" g2="gdotaccent.alt1" k="50" />
+    <hkern u1="&#x2f;" g2="gcommaaccent.sc" k="40" />
+    <hkern u1="&#x2f;" g2="gcommaaccent.alt1" k="50" />
+    <hkern u1="&#x2f;" g2="gcircumflex.sc" k="40" />
+    <hkern u1="&#x2f;" g2="gcircumflex.alt1" k="50" />
+    <hkern u1="&#x2f;" g2="gbreve.sc" k="40" />
+    <hkern u1="&#x2f;" g2="gbreve.alt1" k="50" />
+    <hkern u1="&#x2f;" g2="g.sc" k="40" />
+    <hkern u1="&#x2f;" g2="g.alt1" k="50" />
+    <hkern u1="&#x2f;" g2="dollaroldstyle" k="50" />
+    <hkern u1="&#x2f;" g2="ct.liga" k="50" />
+    <hkern u1="&#x2f;" g2="copyright.case" k="50" />
+    <hkern u1="&#x2f;" g2="ck.liga" k="50" />
+    <hkern u1="&#x2f;" g2="ch.liga" k="50" />
+    <hkern u1="&#x2f;" g2="centoldstyle" k="50" />
+    <hkern u1="&#x2f;" g2="cdotaccent.sc" k="40" />
+    <hkern u1="&#x2f;" g2="ccircumflex.sc" k="40" />
+    <hkern u1="&#x2f;" g2="ccedilla.sc" k="40" />
+    <hkern u1="&#x2f;" g2="ccaron.sc" k="40" />
+    <hkern u1="&#x2f;" g2="cb.liga" k="50" />
+    <hkern u1="&#x2f;" g2="cacute.sc" k="40" />
+    <hkern u1="&#x2f;" g2="c.sc" k="40" />
+    <hkern u1="&#x2f;" g2="atilde.sc" k="90" />
+    <hkern u1="&#x2f;" g2="aring.sc" k="90" />
+    <hkern u1="&#x2f;" g2="aogonek.sc" k="90" />
+    <hkern u1="&#x2f;" g2="amacron.sc" k="90" />
+    <hkern u1="&#x2f;" g2="agrave.sc" k="90" />
+    <hkern u1="&#x2f;" g2="afii10106.sc" k="80" />
+    <hkern u1="&#x2f;" g2="afii10101.sc" k="40" />
+    <hkern u1="&#x2f;" g2="afii10086.sc" k="40" />
+    <hkern u1="&#x2f;" g2="afii10083.sc" k="40" />
+    <hkern u1="&#x2f;" g2="afii10080.sc" k="40" />
+    <hkern u1="&#x2f;" g2="afii10077.sc" k="80" />
+    <hkern u1="&#x2f;" g2="afii10072.77.liga" k="20" />
+    <hkern u1="&#x2f;" g2="afii10069.sc" k="80" />
+    <hkern u1="&#x2f;" g2="afii10065.sc" k="90" />
+    <hkern u1="&#x2f;" g2="ae.sc" k="90" />
+    <hkern u1="&#x2f;" g2="adieresis.sc" k="90" />
+    <hkern u1="&#x2f;" g2="acircumflex.sc" k="90" />
+    <hkern u1="&#x2f;" g2="abreve.sc" k="90" />
+    <hkern u1="&#x2f;" g2="aacute.sc" k="90" />
+    <hkern u1="&#x2f;" g2="a.sc" k="90" />
+    <hkern u1="&#x2f;" g2="Eurooldstyle" k="50" />
+    <hkern u1="&#x2f;" g2="Euro.sc" k="40" />
+    <hkern u1="&#x2f;" u2="&#x2206;" k="100" />
+    <hkern u1="&#x2f;" u2="&#x2202;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x212e;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x2126;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x20ac;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x491;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x45f;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x45c;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x45a;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x455;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x454;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x453;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x451;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x44e;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x44c;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x44b;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x449;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x448;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x446;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x445;" k="20" />
+    <hkern u1="&#x2f;" u2="&#x444;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x441;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x440;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x43f;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x43e;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x43d;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x43c;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x43a;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x439;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x438;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x436;" k="20" />
+    <hkern u1="&#x2f;" u2="&#x435;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x433;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x432;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x431;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x424;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x422;" k="-40" />
+    <hkern u1="&#x2f;" u2="&#x421;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x41e;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x410;" k="100" />
+    <hkern u1="&#x2f;" u2="&#x40b;" k="-40" />
+    <hkern u1="&#x2f;" u2="&#x405;" k="10" />
+    <hkern u1="&#x2f;" u2="&#x404;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x402;" k="-40" />
+    <hkern u1="&#x2f;" u2="&#x237;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x21a;" k="-40" />
+    <hkern u1="&#x2f;" u2="&#x219;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x218;" k="10" />
+    <hkern u1="&#x2f;" u2="&#x20f;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x20e;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x20d;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x20c;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x202;" k="100" />
+    <hkern u1="&#x2f;" u2="&#x200;" k="100" />
+    <hkern u1="&#x2f;" u2="&#x17e;" k="20" />
+    <hkern u1="&#x2f;" u2="&#x17c;" k="20" />
+    <hkern u1="&#x2f;" u2="&#x17a;" k="20" />
+    <hkern u1="&#x2f;" u2="&#x164;" k="-40" />
+    <hkern u1="&#x2f;" u2="&#x162;" k="-40" />
+    <hkern u1="&#x2f;" u2="&#x161;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x160;" k="10" />
+    <hkern u1="&#x2f;" u2="&#x15f;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x15e;" k="10" />
+    <hkern u1="&#x2f;" u2="&#x15d;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x15c;" k="10" />
+    <hkern u1="&#x2f;" u2="&#x15b;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x15a;" k="10" />
+    <hkern u1="&#x2f;" u2="&#x159;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x157;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x155;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x153;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x152;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x151;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x150;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x14f;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x14e;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x14d;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x14c;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x148;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x146;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x144;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x138;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x131;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x123;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x122;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x121;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x120;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x11f;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x11e;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x11d;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x11c;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x11b;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x119;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x117;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x115;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x113;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x111;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x10f;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x10d;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x10c;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x10b;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x10a;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x109;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x108;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x107;" k="50" />
+    <hkern u1="&#x2f;" u2="&#x106;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x104;" k="100" />
+    <hkern u1="&#x2f;" u2="&#x102;" k="100" />
+    <hkern u1="&#x2f;" u2="&#x100;" k="100" />
+    <hkern u1="&#x2f;" u2="&#xf8;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xf6;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xf5;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xf4;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xf3;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xf2;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xf1;" k="30" />
+    <hkern u1="&#x2f;" u2="&#xf0;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xeb;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xea;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xe9;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xe8;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xe7;" k="50" />
+    <hkern u1="&#x2f;" u2="&#xd8;" k="30" />
+    <hkern u1="&#x2f;" u2="&#xd6;" k="30" />
+    <hkern u1="&#x2f;" u2="&#xd5;" k="30" />
+    <hkern u1="&#x2f;" u2="&#xd4;" k="30" />
+    <hkern u1="&#x2f;" u2="&#xd3;" k="30" />
+    <hkern u1="&#x2f;" u2="&#xd2;" k="30" />
+    <hkern u1="&#x2f;" u2="&#xc7;" k="30" />
+    <hkern u1="&#x2f;" u2="&#xc6;" k="100" />
+    <hkern u1="&#x2f;" u2="&#xc5;" k="100" />
+    <hkern u1="&#x2f;" u2="&#xc4;" k="100" />
+    <hkern u1="&#x2f;" u2="&#xc3;" k="100" />
+    <hkern u1="&#x2f;" u2="&#xc2;" k="100" />
+    <hkern u1="&#x2f;" u2="&#xc1;" k="100" />
+    <hkern u1="&#x2f;" u2="&#xc0;" k="100" />
+    <hkern u1="&#x2f;" u2="&#xa9;" k="30" />
+    <hkern u1="&#x2f;" u2="&#xa2;" k="50" />
+    <hkern u1="&#x2f;" u2="z" k="20" />
+    <hkern u1="&#x2f;" u2="x" k="20" />
+    <hkern u1="&#x2f;" u2="s" k="50" />
+    <hkern u1="&#x2f;" u2="r" k="30" />
+    <hkern u1="&#x2f;" u2="q" k="50" />
+    <hkern u1="&#x2f;" u2="p" k="30" />
+    <hkern u1="&#x2f;" u2="o" k="50" />
+    <hkern u1="&#x2f;" u2="n" k="30" />
+    <hkern u1="&#x2f;" u2="m" k="30" />
+    <hkern u1="&#x2f;" u2="g" k="50" />
+    <hkern u1="&#x2f;" u2="e" k="50" />
+    <hkern u1="&#x2f;" u2="d" k="50" />
+    <hkern u1="&#x2f;" u2="c" k="50" />
+    <hkern u1="&#x2f;" u2="T" k="-40" />
+    <hkern u1="&#x2f;" u2="S" k="10" />
+    <hkern u1="&#x2f;" u2="Q" k="30" />
+    <hkern u1="&#x2f;" u2="O" k="30" />
+    <hkern u1="&#x2f;" u2="G" k="30" />
+    <hkern u1="&#x2f;" u2="C" k="30" />
+    <hkern u1="&#x2f;" u2="A" k="100" />
+    <hkern u1="&#x2f;" u2="&#x36;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x30;" k="30" />
+    <hkern u1="&#x2f;" u2="&#x24;" k="10" />
+    <hkern u1="&#x2f;" g2="twooldstyle" k="30" />
+    <hkern u1="&#x2f;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x2f;" g2="s.sc" k="30" />
+    <hkern u1="&#x2f;" g2="nineoldstyle" k="20" />
+    <hkern u1="&#x2f;" g2="nine.sc" k="20" />
+    <hkern u1="&#x2f;" g2="fouroldstyle" k="80" />
+    <hkern u1="&#x2f;" g2="four.sc" k="70" />
+    <hkern u1="&#x2f;" g2="eight.sc" k="10" />
+    <hkern u1="&#x2f;" g2="afii10097.sc" k="40" />
+    <hkern u1="&#x2f;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x2f;" u2="&#x38;" k="20" />
+    <hkern u1="&#x2f;" u2="&#x34;" k="70" />
+    <hkern u1="&#x30;" g2="s.sc" k="10" />
+    <hkern u1="&#x30;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x30;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x30;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x30;" u2="&#x447;" k="10" />
+    <hkern u1="&#x30;" u2="&#x442;" k="10" />
+    <hkern u1="&#x30;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x30;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x30;" u2="&#x427;" k="20" />
+    <hkern u1="&#x30;" u2="&#x37;" k="40" />
+    <hkern u1="&#x30;" u2="&#x33;" k="40" />
+    <hkern u1="&#x30;" u2="&#x32;" k="30" />
+    <hkern u1="&#x30;" u2="&#x31;" k="20" />
+    <hkern u1="&#x30;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x32;" u2="&#x34;" k="20" />
+    <hkern u1="&#x32;" u2="&#x32;" k="20" />
+    <hkern u1="&#x33;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x33;" g2="afii10092.sc" k="40" />
+    <hkern u1="&#x33;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x33;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x33;" u2="&#x44a;" k="20" />
+    <hkern u1="&#x33;" u2="&#x447;" k="20" />
+    <hkern u1="&#x33;" u2="&#x442;" k="30" />
+    <hkern u1="&#x33;" u2="&#x42f;" k="30" />
+    <hkern u1="&#x33;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x33;" u2="&#x427;" k="30" />
+    <hkern u1="&#x33;" u2="&#x39;" k="20" />
+    <hkern u1="&#x33;" u2="&#x37;" k="20" />
+    <hkern u1="&#x33;" u2="&#x35;" k="10" />
+    <hkern u1="&#x33;" u2="&#x33;" k="20" />
+    <hkern u1="&#x33;" u2="&#x32;" k="30" />
+    <hkern u1="&#x33;" u2="&#x31;" k="20" />
+    <hkern u1="&#x34;" u2="&#x2026;" k="30" />
+    <hkern u1="&#x34;" u2="&#x2025;" k="30" />
+    <hkern u1="&#x34;" u2="&#x2024;" k="30" />
+    <hkern u1="&#x34;" u2="&#x201e;" k="30" />
+    <hkern u1="&#x34;" u2="&#x201a;" k="30" />
+    <hkern u1="&#x34;" u2="_" k="30" />
+    <hkern u1="&#x34;" u2="&#x2e;" k="30" />
+    <hkern u1="&#x34;" u2="&#x2c;" k="30" />
+    <hkern u1="&#x34;" u2="&#x32;" k="20" />
+    <hkern u1="&#x35;" u2="&#x2026;" k="30" />
+    <hkern u1="&#x35;" u2="&#x2025;" k="30" />
+    <hkern u1="&#x35;" u2="&#x2024;" k="30" />
+    <hkern u1="&#x35;" u2="&#x201e;" k="30" />
+    <hkern u1="&#x35;" u2="&#x201a;" k="30" />
+    <hkern u1="&#x35;" u2="_" k="30" />
+    <hkern u1="&#x35;" u2="&#x2e;" k="30" />
+    <hkern u1="&#x35;" u2="&#x2c;" k="30" />
+    <hkern u1="&#x35;" u2="&#x39;" k="20" />
+    <hkern u1="&#x35;" u2="&#x38;" k="10" />
+    <hkern u1="&#x35;" u2="&#x37;" k="10" />
+    <hkern u1="&#x35;" u2="&#x35;" k="10" />
+    <hkern u1="&#x35;" u2="&#x33;" k="10" />
+    <hkern u1="&#x35;" u2="&#x32;" k="30" />
+    <hkern u1="&#x36;" u2="&#x2026;" k="30" />
+    <hkern u1="&#x36;" u2="&#x2025;" k="30" />
+    <hkern u1="&#x36;" u2="&#x2024;" k="30" />
+    <hkern u1="&#x36;" u2="&#x201e;" k="30" />
+    <hkern u1="&#x36;" u2="&#x201a;" k="30" />
+    <hkern u1="&#x36;" u2="_" k="30" />
+    <hkern u1="&#x36;" u2="&#x2e;" k="30" />
+    <hkern u1="&#x36;" u2="&#x2c;" k="30" />
+    <hkern u1="&#x36;" u2="&#x39;" k="20" />
+    <hkern u1="&#x36;" u2="&#x37;" k="30" />
+    <hkern u1="&#x36;" u2="&#x33;" k="10" />
+    <hkern u1="&#x36;" u2="&#x32;" k="30" />
+    <hkern u1="&#x36;" u2="&#x31;" k="30" />
+    <hkern u1="&#x37;" g2="uni00AD.case" k="40" />
+    <hkern u1="&#x37;" g2="plusminus.case" k="40" />
+    <hkern u1="&#x37;" g2="plus.case" k="40" />
+    <hkern u1="&#x37;" g2="periodcentered.case" k="40" />
+    <hkern u1="&#x37;" g2="numbersign.case" k="40" />
+    <hkern u1="&#x37;" g2="notequal.case" k="40" />
+    <hkern u1="&#x37;" g2="multiply.case" k="40" />
+    <hkern u1="&#x37;" g2="minus.case" k="40" />
+    <hkern u1="&#x37;" g2="logicalnot.case" k="40" />
+    <hkern u1="&#x37;" g2="lessequal.case" k="40" />
+    <hkern u1="&#x37;" g2="less.case" k="40" />
+    <hkern u1="&#x37;" g2="hyphen.case" k="40" />
+    <hkern u1="&#x37;" g2="guilsinglright.case" k="40" />
+    <hkern u1="&#x37;" g2="guilsinglleft.case" k="40" />
+    <hkern u1="&#x37;" g2="guillemotright.case" k="40" />
+    <hkern u1="&#x37;" g2="guillemotleft.case" k="40" />
+    <hkern u1="&#x37;" g2="greaterequal.case" k="40" />
+    <hkern u1="&#x37;" g2="greater.case" k="40" />
+    <hkern u1="&#x37;" g2="equal.case" k="40" />
+    <hkern u1="&#x37;" g2="endash.case" k="40" />
+    <hkern u1="&#x37;" g2="emdash.case" k="40" />
+    <hkern u1="&#x37;" g2="divide.case" k="40" />
+    <hkern u1="&#x37;" g2="currency.taboldstyle" k="40" />
+    <hkern u1="&#x37;" g2="bullet.case" k="40" />
+    <hkern u1="&#x37;" g2="asciitilde.case" k="40" />
+    <hkern u1="&#x37;" g2="approxequal.case" k="40" />
+    <hkern u1="&#x37;" u2="&#x2265;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2264;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2260;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2248;" k="40" />
+    <hkern u1="&#x37;" u2="&#x221e;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2212;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2192;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2190;" k="40" />
+    <hkern u1="&#x37;" u2="&#x203a;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2039;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2014;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2013;" k="40" />
+    <hkern u1="&#x37;" u2="&#xf7;" k="40" />
+    <hkern u1="&#x37;" u2="&#xd7;" k="40" />
+    <hkern u1="&#x37;" u2="&#xbb;" k="40" />
+    <hkern u1="&#x37;" u2="&#xb7;" k="40" />
+    <hkern u1="&#x37;" u2="&#xb1;" k="40" />
+    <hkern u1="&#x37;" u2="&#xad;" k="40" />
+    <hkern u1="&#x37;" u2="&#xac;" k="40" />
+    <hkern u1="&#x37;" u2="&#xab;" k="40" />
+    <hkern u1="&#x37;" u2="&#xa4;" k="40" />
+    <hkern u1="&#x37;" u2="&#x7e;" k="40" />
+    <hkern u1="&#x37;" u2="&#x3e;" k="40" />
+    <hkern u1="&#x37;" u2="&#x3d;" k="40" />
+    <hkern u1="&#x37;" u2="&#x3c;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2d;" k="40" />
+    <hkern u1="&#x37;" u2="&#x2b;" k="40" />
+    <hkern u1="&#x37;" u2="&#x23;" k="40" />
+    <hkern u1="&#x37;" g2="zero.slash" k="30" />
+    <hkern u1="&#x37;" g2="sixoldstyle" k="30" />
+    <hkern u1="&#x37;" u2="&#x2126;" k="30" />
+    <hkern u1="&#x37;" u2="&#x20ac;" k="30" />
+    <hkern u1="&#x37;" u2="&#x2026;" k="100" />
+    <hkern u1="&#x37;" u2="&#x2025;" k="100" />
+    <hkern u1="&#x37;" u2="&#x2024;" k="100" />
+    <hkern u1="&#x37;" u2="&#x201e;" k="100" />
+    <hkern u1="&#x37;" u2="&#x201a;" k="100" />
+    <hkern u1="&#x37;" u2="&#x424;" k="30" />
+    <hkern u1="&#x37;" u2="&#x421;" k="30" />
+    <hkern u1="&#x37;" u2="&#x41e;" k="30" />
+    <hkern u1="&#x37;" u2="&#x404;" k="30" />
+    <hkern u1="&#x37;" u2="&#x20e;" k="30" />
+    <hkern u1="&#x37;" u2="&#x20c;" k="30" />
+    <hkern u1="&#x37;" u2="&#x152;" k="30" />
+    <hkern u1="&#x37;" u2="&#x150;" k="30" />
+    <hkern u1="&#x37;" u2="&#x14e;" k="30" />
+    <hkern u1="&#x37;" u2="&#x14c;" k="30" />
+    <hkern u1="&#x37;" u2="&#x122;" k="30" />
+    <hkern u1="&#x37;" u2="&#x120;" k="30" />
+    <hkern u1="&#x37;" u2="&#x11e;" k="30" />
+    <hkern u1="&#x37;" u2="&#x11c;" k="30" />
+    <hkern u1="&#x37;" u2="&#x10c;" k="30" />
+    <hkern u1="&#x37;" u2="&#x10a;" k="30" />
+    <hkern u1="&#x37;" u2="&#x108;" k="30" />
+    <hkern u1="&#x37;" u2="&#x106;" k="30" />
+    <hkern u1="&#x37;" u2="&#xd8;" k="30" />
+    <hkern u1="&#x37;" u2="&#xd6;" k="30" />
+    <hkern u1="&#x37;" u2="&#xd5;" k="30" />
+    <hkern u1="&#x37;" u2="&#xd4;" k="30" />
+    <hkern u1="&#x37;" u2="&#xd3;" k="30" />
+    <hkern u1="&#x37;" u2="&#xd2;" k="30" />
+    <hkern u1="&#x37;" u2="&#xc7;" k="30" />
+    <hkern u1="&#x37;" u2="&#xa9;" k="30" />
+    <hkern u1="&#x37;" u2="_" k="100" />
+    <hkern u1="&#x37;" u2="Q" k="30" />
+    <hkern u1="&#x37;" u2="O" k="30" />
+    <hkern u1="&#x37;" u2="G" k="30" />
+    <hkern u1="&#x37;" u2="C" k="30" />
+    <hkern u1="&#x37;" u2="&#x36;" k="30" />
+    <hkern u1="&#x37;" u2="&#x30;" k="30" />
+    <hkern u1="&#x37;" u2="&#x2e;" k="100" />
+    <hkern u1="&#x37;" u2="&#x2c;" k="100" />
+    <hkern u1="&#x37;" u2="&#x39;" k="10" />
+    <hkern u1="&#x37;" u2="&#x38;" k="30" />
+    <hkern u1="&#x37;" u2="&#x34;" k="60" />
+    <hkern u1="&#x37;" u2="&#x33;" k="10" />
+    <hkern u1="&#x37;" u2="&#x32;" k="10" />
+    <hkern u1="&#x37;" u2="&#x2f;" k="90" />
+    <hkern u1="&#x38;" u2="&#x2026;" k="30" />
+    <hkern u1="&#x38;" u2="&#x2025;" k="30" />
+    <hkern u1="&#x38;" u2="&#x2024;" k="30" />
+    <hkern u1="&#x38;" u2="&#x201e;" k="30" />
+    <hkern u1="&#x38;" u2="&#x201a;" k="30" />
+    <hkern u1="&#x38;" u2="_" k="30" />
+    <hkern u1="&#x38;" u2="&#x2e;" k="30" />
+    <hkern u1="&#x38;" u2="&#x2c;" k="30" />
+    <hkern u1="&#x38;" u2="&#x37;" k="20" />
+    <hkern u1="&#x38;" u2="&#x32;" k="40" />
+    <hkern u1="&#x39;" g2="s.sc" k="10" />
+    <hkern u1="&#x39;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x39;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x39;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x39;" u2="&#x447;" k="10" />
+    <hkern u1="&#x39;" u2="&#x442;" k="10" />
+    <hkern u1="&#x39;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x39;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x39;" u2="&#x427;" k="20" />
+    <hkern u1="&#x39;" u2="&#x37;" k="40" />
+    <hkern u1="&#x39;" u2="&#x33;" k="40" />
+    <hkern u1="&#x39;" u2="&#x32;" k="30" />
+    <hkern u1="&#x39;" u2="&#x31;" k="20" />
+    <hkern u1="&#x39;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x3c;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x3c;" g2="two.sc" k="40" />
+    <hkern u1="&#x3c;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x3c;" g2="three.sc" k="20" />
+    <hkern u1="&#x3c;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x3c;" g2="seven.sc" k="40" />
+    <hkern u1="&#x3c;" g2="s.sc" k="20" />
+    <hkern u1="&#x3c;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x3c;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x3c;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x3c;" u2="&#x442;" k="50" />
+    <hkern u1="&#x3c;" u2="&#x37;" k="60" />
+    <hkern u1="&#x3d;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x3d;" g2="two.sc" k="40" />
+    <hkern u1="&#x3d;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x3d;" g2="three.sc" k="20" />
+    <hkern u1="&#x3d;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x3d;" g2="seven.sc" k="40" />
+    <hkern u1="&#x3d;" g2="s.sc" k="20" />
+    <hkern u1="&#x3d;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x3d;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x3d;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x3d;" u2="&#x442;" k="50" />
+    <hkern u1="&#x3d;" u2="&#x37;" k="60" />
+    <hkern u1="&#x3e;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x3e;" g2="two.sc" k="40" />
+    <hkern u1="&#x3e;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x3e;" g2="three.sc" k="20" />
+    <hkern u1="&#x3e;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x3e;" g2="seven.sc" k="40" />
+    <hkern u1="&#x3e;" g2="s.sc" k="20" />
+    <hkern u1="&#x3e;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x3e;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x3e;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x3e;" u2="&#x442;" k="50" />
+    <hkern u1="&#x3e;" u2="&#x37;" k="60" />
+    <hkern u1="A" g2="afii10089.sc" k="60" />
+    <hkern u1="A" u2="&#x44a;" k="70" />
+    <hkern u1="A" u2="&#x447;" k="50" />
+    <hkern u1="A" u2="&#x442;" k="60" />
+    <hkern u1="A" u2="&#x42a;" k="60" />
+    <hkern u1="A" u2="&#x427;" k="70" />
+    <hkern u1="A" u2="&#x2f;" k="-30" />
+    <hkern u1="B" g2="afii10097.sc" k="20" />
+    <hkern u1="B" g2="afii10092.sc" k="40" />
+    <hkern u1="B" g2="afii10089.sc" k="20" />
+    <hkern u1="B" u2="&#x44f;" k="20" />
+    <hkern u1="B" u2="&#x44a;" k="20" />
+    <hkern u1="B" u2="&#x447;" k="20" />
+    <hkern u1="B" u2="&#x442;" k="30" />
+    <hkern u1="B" u2="&#x42f;" k="30" />
+    <hkern u1="B" u2="&#x42a;" k="20" />
+    <hkern u1="B" u2="&#x427;" k="30" />
+    <hkern u1="B" u2="&#x39;" k="20" />
+    <hkern u1="B" u2="&#x37;" k="20" />
+    <hkern u1="B" u2="&#x35;" k="10" />
+    <hkern u1="B" u2="&#x33;" k="20" />
+    <hkern u1="B" u2="&#x32;" k="30" />
+    <hkern u1="B" u2="&#x31;" k="20" />
+    <hkern u1="C" g2="s.sc" k="20" />
+    <hkern u1="C" g2="afii10089.sc" k="10" />
+    <hkern u1="C" u2="&#x44f;" k="10" />
+    <hkern u1="C" u2="&#x44a;" k="40" />
+    <hkern u1="C" u2="&#x447;" k="20" />
+    <hkern u1="D" g2="s.sc" k="10" />
+    <hkern u1="D" g2="afii10097.sc" k="20" />
+    <hkern u1="D" g2="afii10092.sc" k="20" />
+    <hkern u1="D" u2="&#x44a;" k="10" />
+    <hkern u1="D" u2="&#x447;" k="10" />
+    <hkern u1="D" u2="&#x442;" k="10" />
+    <hkern u1="D" u2="&#x42f;" k="20" />
+    <hkern u1="D" u2="&#x42a;" k="40" />
+    <hkern u1="D" u2="&#x427;" k="20" />
+    <hkern u1="D" u2="&#x37;" k="40" />
+    <hkern u1="D" u2="&#x33;" k="40" />
+    <hkern u1="D" u2="&#x32;" k="30" />
+    <hkern u1="D" u2="&#x31;" k="20" />
+    <hkern u1="D" u2="&#x2f;" k="30" />
+    <hkern u1="E" g2="s.sc" k="10" />
+    <hkern u1="E" g2="afii10092.sc" k="20" />
+    <hkern u1="E" g2="afii10089.sc" k="20" />
+    <hkern u1="E" u2="&#x44f;" k="10" />
+    <hkern u1="E" u2="&#x44a;" k="30" />
+    <hkern u1="E" u2="&#x447;" k="20" />
+    <hkern u1="E" u2="&#x442;" k="20" />
+    <hkern u1="E" u2="&#x42f;" k="10" />
+    <hkern u1="E" u2="&#x427;" k="20" />
+    <hkern u1="F" g2="zerooldstyle" k="30" />
+    <hkern u1="F" g2="zero.slash.sc" k="40" />
+    <hkern u1="F" g2="zero.slash.oldstyle" k="30" />
+    <hkern u1="F" g2="zero.slash" k="30" />
+    <hkern u1="F" g2="zero.sc" k="40" />
+    <hkern u1="F" g2="zdotaccent.sc" k="30" />
+    <hkern u1="F" g2="zcaron.sc" k="30" />
+    <hkern u1="F" g2="zacute.sc" k="30" />
+    <hkern u1="F" g2="z.sc" k="30" />
+    <hkern u1="F" g2="yenoldstyle" k="20" />
+    <hkern u1="F" g2="yen.sc" k="20" />
+    <hkern u1="F" g2="ydieresis.sc" k="30" />
+    <hkern u1="F" g2="ycircumflex.sc" k="30" />
+    <hkern u1="F" g2="yacute.sc" k="30" />
+    <hkern u1="F" g2="y.sc" k="30" />
+    <hkern u1="F" g2="x.sc" k="20" />
+    <hkern u1="F" g2="wcircumflex.sc" k="20" />
+    <hkern u1="F" g2="w.sc" k="20" />
+    <hkern u1="F" g2="v.sc" k="20" />
+    <hkern u1="F" g2="utilde.sc" k="20" />
+    <hkern u1="F" g2="uring.sc" k="20" />
+    <hkern u1="F" g2="uogonek.sc" k="20" />
+    <hkern u1="F" g2="uni021B.sc" k="20" />
+    <hkern u1="F" g2="uni0217.sc" k="20" />
+    <hkern u1="F" g2="uni0215.sc" k="20" />
+    <hkern u1="F" g2="uni020F.sc" k="40" />
+    <hkern u1="F" g2="uni020D.sc" k="40" />
+    <hkern u1="F" g2="uni0203.sc" k="90" />
+    <hkern u1="F" g2="uni0203.alt1" k="40" />
+    <hkern u1="F" g2="uni0201.sc" k="90" />
+    <hkern u1="F" g2="uni0201.alt1" k="40" />
+    <hkern u1="F" g2="uni00AD.case" k="20" />
+    <hkern u1="F" g2="umacron.sc" k="20" />
+    <hkern u1="F" g2="uhungarumlaut.sc" k="20" />
+    <hkern u1="F" g2="ugrave.sc" k="20" />
+    <hkern u1="F" g2="udieresis.sc" k="20" />
+    <hkern u1="F" g2="ucircumflex.sc" k="20" />
+    <hkern u1="F" g2="ubreve.sc" k="20" />
+    <hkern u1="F" g2="uacute.sc" k="20" />
+    <hkern u1="F" g2="u.sc" k="20" />
+    <hkern u1="F" g2="tcommaaccent.sc" k="20" />
+    <hkern u1="F" g2="tcaron.sc" k="20" />
+    <hkern u1="F" g2="t.sc" k="20" />
+    <hkern u1="F" g2="st.liga" k="20" />
+    <hkern u1="F" g2="sk.liga" k="20" />
+    <hkern u1="F" g2="sixoldstyle" k="30" />
+    <hkern u1="F" g2="six.sc" k="40" />
+    <hkern u1="F" g2="sh.liga" k="20" />
+    <hkern u1="F" g2="sb.liga" k="20" />
+    <hkern u1="F" g2="q.sc" k="40" />
+    <hkern u1="F" g2="plusminus.case" k="20" />
+    <hkern u1="F" g2="plus.case" k="20" />
+    <hkern u1="F" g2="periodcentered.case" k="20" />
+    <hkern u1="F" g2="otilde.sc" k="40" />
+    <hkern u1="F" g2="oslash.sc" k="40" />
+    <hkern u1="F" g2="oneoldstyle" k="20" />
+    <hkern u1="F" g2="omacron.sc" k="40" />
+    <hkern u1="F" g2="ohungarumlaut.sc" k="40" />
+    <hkern u1="F" g2="ograve.sc" k="40" />
+    <hkern u1="F" g2="oe.sc" k="40" />
+    <hkern u1="F" g2="odieresis.sc" k="40" />
+    <hkern u1="F" g2="ocircumflex.sc" k="40" />
+    <hkern u1="F" g2="obreve.sc" k="40" />
+    <hkern u1="F" g2="oacute.sc" k="40" />
+    <hkern u1="F" g2="o.sc" k="40" />
+    <hkern u1="F" g2="numbersign.case" k="20" />
+    <hkern u1="F" g2="notequal.case" k="20" />
+    <hkern u1="F" g2="multiply.case" k="20" />
+    <hkern u1="F" g2="minus.case" k="20" />
+    <hkern u1="F" g2="lst.liga" k="10" />
+    <hkern u1="F" g2="lslsj.liga" k="10" />
+    <hkern u1="F" g2="lslsi.liga" k="10" />
+    <hkern u1="F" g2="lsls.liga" k="10" />
+    <hkern u1="F" g2="lsl.liga" k="10" />
+    <hkern u1="F" g2="lsk.liga" k="10" />
+    <hkern u1="F" g2="lsj.liga" k="10" />
+    <hkern u1="F" g2="lsi.liga" k="10" />
+    <hkern u1="F" g2="lsh.liga" k="10" />
+    <hkern u1="F" g2="lsb.liga" k="10" />
+    <hkern u1="F" g2="logicalnot.case" k="20" />
+    <hkern u1="F" g2="lessequal.case" k="20" />
+    <hkern u1="F" g2="less.case" k="20" />
+    <hkern u1="F" g2="ii.liga" k="10" />
+    <hkern u1="F" g2="hyphen.case" k="20" />
+    <hkern u1="F" g2="guilsinglright.case" k="20" />
+    <hkern u1="F" g2="guilsinglleft.case" k="20" />
+    <hkern u1="F" g2="guillemotright.case" k="20" />
+    <hkern u1="F" g2="guillemotleft.case" k="20" />
+    <hkern u1="F" g2="greaterequal.case" k="20" />
+    <hkern u1="F" g2="greater.case" k="20" />
+    <hkern u1="F" g2="gdotaccent.sc" k="40" />
+    <hkern u1="F" g2="gdotaccent.alt1" k="40" />
+    <hkern u1="F" g2="gcommaaccent.sc" k="40" />
+    <hkern u1="F" g2="gcommaaccent.alt1" k="40" />
+    <hkern u1="F" g2="gcircumflex.sc" k="40" />
+    <hkern u1="F" g2="gcircumflex.alt1" k="40" />
+    <hkern u1="F" g2="gbreve.sc" k="40" />
+    <hkern u1="F" g2="gbreve.alt1" k="40" />
+    <hkern u1="F" g2="g.sc" k="40" />
+    <hkern u1="F" g2="g.alt1" k="40" />
+    <hkern u1="F" g2="ft.liga" k="20" />
+    <hkern u1="F" g2="fl.liga" k="20" />
+    <hkern u1="F" g2="fk.liga" k="20" />
+    <hkern u1="F" g2="fj.liga" k="20" />
+    <hkern u1="F" g2="fi.liga" k="20" />
+    <hkern u1="F" g2="fh.liga" k="20" />
+    <hkern u1="F" g2="ffj.liga" k="20" />
+    <hkern u1="F" g2="ffi.liga" k="20" />
+    <hkern u1="F" g2="ff.liga" k="20" />
+    <hkern u1="F" g2="fb.liga" k="20" />
+    <hkern u1="F" g2="equal.case" k="20" />
+    <hkern u1="F" g2="endash.case" k="20" />
+    <hkern u1="F" g2="emdash.case" k="20" />
+    <hkern u1="F" g2="dollaroldstyle" k="20" />
+    <hkern u1="F" g2="divide.case" k="20" />
+    <hkern u1="F" g2="currency.taboldstyle" k="20" />
+    <hkern u1="F" g2="ct.liga" k="30" />
+    <hkern u1="F" g2="copyright.case" k="30" />
+    <hkern u1="F" g2="ck.liga" k="30" />
+    <hkern u1="F" g2="ch.liga" k="30" />
+    <hkern u1="F" g2="centoldstyle" k="30" />
+    <hkern u1="F" g2="cdotaccent.sc" k="40" />
+    <hkern u1="F" g2="ccircumflex.sc" k="40" />
+    <hkern u1="F" g2="ccedilla.sc" k="40" />
+    <hkern u1="F" g2="ccaron.sc" k="40" />
+    <hkern u1="F" g2="cb.liga" k="30" />
+    <hkern u1="F" g2="cacute.sc" k="40" />
+    <hkern u1="F" g2="c.sc" k="40" />
+    <hkern u1="F" g2="bullet.case" k="20" />
+    <hkern u1="F" g2="atilde.sc" k="90" />
+    <hkern u1="F" g2="atilde.alt1" k="40" />
+    <hkern u1="F" g2="asciitilde.case" k="20" />
+    <hkern u1="F" g2="aring.sc" k="90" />
+    <hkern u1="F" g2="aring.alt1" k="40" />
+    <hkern u1="F" g2="approxequal.case" k="20" />
+    <hkern u1="F" g2="aogonek.sc" k="90" />
+    <hkern u1="F" g2="aogonek.alt1" k="40" />
+    <hkern u1="F" g2="amacron.sc" k="90" />
+    <hkern u1="F" g2="amacron.alt1" k="40" />
+    <hkern u1="F" g2="agrave.sc" k="90" />
+    <hkern u1="F" g2="agrave.alt1" k="40" />
+    <hkern u1="F" g2="afii10110.sc" k="20" />
+    <hkern u1="F" g2="afii10108.sc" k="20" />
+    <hkern u1="F" g2="afii10101.sc" k="40" />
+    <hkern u1="F" g2="afii10099.sc" k="20" />
+    <hkern u1="F" g2="afii10087.sc" k="20" />
+    <hkern u1="F" g2="afii10086.sc" k="40" />
+    <hkern u1="F" g2="afii10085.sc" k="20" />
+    <hkern u1="F" g2="afii10084.sc" k="20" />
+    <hkern u1="F" g2="afii10083.sc" k="40" />
+    <hkern u1="F" g2="afii10080.sc" k="40" />
+    <hkern u1="F" g2="afii10072.sc" k="20" />
+    <hkern u1="F" g2="afii10072.77.liga" k="10" />
+    <hkern u1="F" g2="afii10065.sc" k="90" />
+    <hkern u1="F" g2="afii10065.alt1" k="40" />
+    <hkern u1="F" g2="afii10065.77.liga" k="40" />
+    <hkern u1="F" g2="ae.sc" k="90" />
+    <hkern u1="F" g2="ae.alt1" k="40" />
+    <hkern u1="F" g2="adieresis.sc" k="90" />
+    <hkern u1="F" g2="adieresis.alt1" k="40" />
+    <hkern u1="F" g2="acircumflex.sc" k="90" />
+    <hkern u1="F" g2="acircumflex.alt1" k="40" />
+    <hkern u1="F" g2="abreve.sc" k="90" />
+    <hkern u1="F" g2="abreve.alt1" k="40" />
+    <hkern u1="F" g2="aacute.sc" k="90" />
+    <hkern u1="F" g2="aacute.alt1" k="40" />
+    <hkern u1="F" g2="a.sc" k="90" />
+    <hkern u1="F" g2="a.alt1" k="40" />
+    <hkern u1="F" g2="Eurooldstyle" k="30" />
+    <hkern u1="F" g2="Euro.sc" k="40" />
+    <hkern u1="F" u2="&#x2265;" k="20" />
+    <hkern u1="F" u2="&#x2264;" k="20" />
+    <hkern u1="F" u2="&#x2260;" k="20" />
+    <hkern u1="F" u2="&#x2248;" k="20" />
+    <hkern u1="F" u2="&#x221e;" k="20" />
+    <hkern u1="F" u2="&#x221a;" k="20" />
+    <hkern u1="F" u2="&#x2212;" k="20" />
+    <hkern u1="F" u2="&#x2206;" k="50" />
+    <hkern u1="F" u2="&#x2202;" k="30" />
+    <hkern u1="F" u2="&#x2192;" k="20" />
+    <hkern u1="F" u2="&#x2190;" k="20" />
+    <hkern u1="F" u2="&#x212e;" k="30" />
+    <hkern u1="F" u2="&#x2126;" k="30" />
+    <hkern u1="F" u2="&#x20ac;" k="30" />
+    <hkern u1="F" u2="&#x203a;" k="20" />
+    <hkern u1="F" u2="&#x2039;" k="20" />
+    <hkern u1="F" u2="&#x2014;" k="20" />
+    <hkern u1="F" u2="&#x2013;" k="20" />
+    <hkern u1="F" u2="&#x491;" k="20" />
+    <hkern u1="F" u2="&#x45f;" k="20" />
+    <hkern u1="F" u2="&#x45e;" k="20" />
+    <hkern u1="F" u2="&#x45c;" k="20" />
+    <hkern u1="F" u2="&#x45b;" k="10" />
+    <hkern u1="F" u2="&#x45a;" k="20" />
+    <hkern u1="F" u2="&#x458;" k="10" />
+    <hkern u1="F" u2="&#x457;" k="10" />
+    <hkern u1="F" u2="&#x456;" k="10" />
+    <hkern u1="F" u2="&#x455;" k="20" />
+    <hkern u1="F" u2="&#x454;" k="30" />
+    <hkern u1="F" u2="&#x453;" k="20" />
+    <hkern u1="F" u2="&#x452;" k="10" />
+    <hkern u1="F" u2="&#x451;" k="30" />
+    <hkern u1="F" u2="&#x44e;" k="20" />
+    <hkern u1="F" u2="&#x44c;" k="20" />
+    <hkern u1="F" u2="&#x44b;" k="20" />
+    <hkern u1="F" u2="&#x449;" k="20" />
+    <hkern u1="F" u2="&#x448;" k="20" />
+    <hkern u1="F" u2="&#x446;" k="20" />
+    <hkern u1="F" u2="&#x445;" k="10" />
+    <hkern u1="F" u2="&#x444;" k="30" />
+    <hkern u1="F" u2="&#x443;" k="20" />
+    <hkern u1="F" u2="&#x441;" k="30" />
+    <hkern u1="F" u2="&#x440;" k="20" />
+    <hkern u1="F" u2="&#x43f;" k="20" />
+    <hkern u1="F" u2="&#x43e;" k="30" />
+    <hkern u1="F" u2="&#x43d;" k="20" />
+    <hkern u1="F" u2="&#x43c;" k="20" />
+    <hkern u1="F" u2="&#x43a;" k="20" />
+    <hkern u1="F" u2="&#x439;" k="20" />
+    <hkern u1="F" u2="&#x438;" k="20" />
+    <hkern u1="F" u2="&#x436;" k="10" />
+    <hkern u1="F" u2="&#x435;" k="30" />
+    <hkern u1="F" u2="&#x433;" k="20" />
+    <hkern u1="F" u2="&#x432;" k="20" />
+    <hkern u1="F" u2="&#x431;" k="30" />
+    <hkern u1="F" u2="&#x430;" k="40" />
+    <hkern u1="F" u2="&#x425;" k="20" />
+    <hkern u1="F" u2="&#x424;" k="30" />
+    <hkern u1="F" u2="&#x423;" k="10" />
+    <hkern u1="F" u2="&#x421;" k="30" />
+    <hkern u1="F" u2="&#x41e;" k="30" />
+    <hkern u1="F" u2="&#x416;" k="20" />
+    <hkern u1="F" u2="&#x410;" k="50" />
+    <hkern u1="F" u2="&#x40e;" k="10" />
+    <hkern u1="F" u2="&#x405;" k="30" />
+    <hkern u1="F" u2="&#x404;" k="30" />
+    <hkern u1="F" u2="&#x237;" k="20" />
+    <hkern u1="F" u2="&#x21b;" k="10" />
+    <hkern u1="F" u2="&#x219;" k="20" />
+    <hkern u1="F" u2="&#x218;" k="30" />
+    <hkern u1="F" u2="&#x217;" k="20" />
+    <hkern u1="F" u2="&#x216;" k="10" />
+    <hkern u1="F" u2="&#x215;" k="20" />
+    <hkern u1="F" u2="&#x214;" k="10" />
+    <hkern u1="F" u2="&#x20f;" k="30" />
+    <hkern u1="F" u2="&#x20e;" k="30" />
+    <hkern u1="F" u2="&#x20d;" k="30" />
+    <hkern u1="F" u2="&#x20c;" k="30" />
+    <hkern u1="F" u2="&#x20b;" k="10" />
+    <hkern u1="F" u2="&#x209;" k="10" />
+    <hkern u1="F" u2="&#x203;" k="40" />
+    <hkern u1="F" u2="&#x202;" k="50" />
+    <hkern u1="F" u2="&#x201;" k="40" />
+    <hkern u1="F" u2="&#x200;" k="50" />
+    <hkern u1="F" u2="&#x192;" k="20" />
+    <hkern u1="F" u2="&#x17e;" k="20" />
+    <hkern u1="F" u2="&#x17d;" k="40" />
+    <hkern u1="F" u2="&#x17c;" k="20" />
+    <hkern u1="F" u2="&#x17b;" k="40" />
+    <hkern u1="F" u2="&#x17a;" k="20" />
+    <hkern u1="F" u2="&#x179;" k="40" />
+    <hkern u1="F" u2="&#x178;" k="10" />
+    <hkern u1="F" u2="&#x177;" k="20" />
+    <hkern u1="F" u2="&#x176;" k="10" />
+    <hkern u1="F" u2="&#x175;" k="20" />
+    <hkern u1="F" u2="&#x174;" k="10" />
+    <hkern u1="F" u2="&#x173;" k="20" />
+    <hkern u1="F" u2="&#x172;" k="10" />
+    <hkern u1="F" u2="&#x171;" k="20" />
+    <hkern u1="F" u2="&#x170;" k="10" />
+    <hkern u1="F" u2="&#x16f;" k="20" />
+    <hkern u1="F" u2="&#x16e;" k="10" />
+    <hkern u1="F" u2="&#x16d;" k="20" />
+    <hkern u1="F" u2="&#x16c;" k="10" />
+    <hkern u1="F" u2="&#x16b;" k="20" />
+    <hkern u1="F" u2="&#x16a;" k="10" />
+    <hkern u1="F" u2="&#x169;" k="20" />
+    <hkern u1="F" u2="&#x168;" k="10" />
+    <hkern u1="F" u2="&#x165;" k="10" />
+    <hkern u1="F" u2="&#x163;" k="10" />
+    <hkern u1="F" u2="&#x161;" k="20" />
+    <hkern u1="F" u2="&#x160;" k="30" />
+    <hkern u1="F" u2="&#x15f;" k="20" />
+    <hkern u1="F" u2="&#x15e;" k="30" />
+    <hkern u1="F" u2="&#x15d;" k="20" />
+    <hkern u1="F" u2="&#x15c;" k="30" />
+    <hkern u1="F" u2="&#x15b;" k="20" />
+    <hkern u1="F" u2="&#x15a;" k="30" />
+    <hkern u1="F" u2="&#x159;" k="20" />
+    <hkern u1="F" u2="&#x157;" k="20" />
+    <hkern u1="F" u2="&#x155;" k="20" />
+    <hkern u1="F" u2="&#x153;" k="30" />
+    <hkern u1="F" u2="&#x152;" k="30" />
+    <hkern u1="F" u2="&#x151;" k="30" />
+    <hkern u1="F" u2="&#x150;" k="30" />
+    <hkern u1="F" u2="&#x14f;" k="30" />
+    <hkern u1="F" u2="&#x14e;" k="30" />
+    <hkern u1="F" u2="&#x14d;" k="30" />
+    <hkern u1="F" u2="&#x14c;" k="30" />
+    <hkern u1="F" u2="&#x148;" k="20" />
+    <hkern u1="F" u2="&#x146;" k="20" />
+    <hkern u1="F" u2="&#x144;" k="20" />
+    <hkern u1="F" u2="&#x142;" k="10" />
+    <hkern u1="F" u2="&#x140;" k="10" />
+    <hkern u1="F" u2="&#x13c;" k="10" />
+    <hkern u1="F" u2="&#x13a;" k="10" />
+    <hkern u1="F" u2="&#x138;" k="20" />
+    <hkern u1="F" u2="&#x137;" k="10" />
+    <hkern u1="F" u2="&#x135;" k="10" />
+    <hkern u1="F" u2="&#x131;" k="20" />
+    <hkern u1="F" u2="&#x12f;" k="10" />
+    <hkern u1="F" u2="&#x12b;" k="10" />
+    <hkern u1="F" u2="&#x129;" k="10" />
+    <hkern u1="F" u2="&#x127;" k="10" />
+    <hkern u1="F" u2="&#x125;" k="10" />
+    <hkern u1="F" u2="&#x123;" k="40" />
+    <hkern u1="F" u2="&#x122;" k="30" />
+    <hkern u1="F" u2="&#x121;" k="40" />
+    <hkern u1="F" u2="&#x120;" k="30" />
+    <hkern u1="F" u2="&#x11f;" k="40" />
+    <hkern u1="F" u2="&#x11e;" k="30" />
+    <hkern u1="F" u2="&#x11d;" k="40" />
+    <hkern u1="F" u2="&#x11c;" k="30" />
+    <hkern u1="F" u2="&#x11b;" k="30" />
+    <hkern u1="F" u2="&#x119;" k="30" />
+    <hkern u1="F" u2="&#x117;" k="30" />
+    <hkern u1="F" u2="&#x115;" k="30" />
+    <hkern u1="F" u2="&#x113;" k="30" />
+    <hkern u1="F" u2="&#x111;" k="30" />
+    <hkern u1="F" u2="&#x10f;" k="30" />
+    <hkern u1="F" u2="&#x10d;" k="30" />
+    <hkern u1="F" u2="&#x10c;" k="30" />
+    <hkern u1="F" u2="&#x10b;" k="30" />
+    <hkern u1="F" u2="&#x10a;" k="30" />
+    <hkern u1="F" u2="&#x109;" k="30" />
+    <hkern u1="F" u2="&#x108;" k="30" />
+    <hkern u1="F" u2="&#x107;" k="30" />
+    <hkern u1="F" u2="&#x106;" k="30" />
+    <hkern u1="F" u2="&#x105;" k="40" />
+    <hkern u1="F" u2="&#x104;" k="50" />
+    <hkern u1="F" u2="&#x103;" k="40" />
+    <hkern u1="F" u2="&#x102;" k="50" />
+    <hkern u1="F" u2="&#x101;" k="40" />
+    <hkern u1="F" u2="&#x100;" k="50" />
+    <hkern u1="F" u2="&#xff;" k="20" />
+    <hkern u1="F" u2="&#xfe;" k="10" />
+    <hkern u1="F" u2="&#xfd;" k="20" />
+    <hkern u1="F" u2="&#xfc;" k="20" />
+    <hkern u1="F" u2="&#xfb;" k="20" />
+    <hkern u1="F" u2="&#xfa;" k="20" />
+    <hkern u1="F" u2="&#xf9;" k="20" />
+    <hkern u1="F" u2="&#xf8;" k="30" />
+    <hkern u1="F" u2="&#xf7;" k="20" />
+    <hkern u1="F" u2="&#xf6;" k="30" />
+    <hkern u1="F" u2="&#xf5;" k="30" />
+    <hkern u1="F" u2="&#xf4;" k="30" />
+    <hkern u1="F" u2="&#xf3;" k="30" />
+    <hkern u1="F" u2="&#xf2;" k="30" />
+    <hkern u1="F" u2="&#xf1;" k="20" />
+    <hkern u1="F" u2="&#xf0;" k="30" />
+    <hkern u1="F" u2="&#xef;" k="10" />
+    <hkern u1="F" u2="&#xee;" k="10" />
+    <hkern u1="F" u2="&#xed;" k="10" />
+    <hkern u1="F" u2="&#xec;" k="10" />
+    <hkern u1="F" u2="&#xeb;" k="30" />
+    <hkern u1="F" u2="&#xea;" k="30" />
+    <hkern u1="F" u2="&#xe9;" k="30" />
+    <hkern u1="F" u2="&#xe8;" k="30" />
+    <hkern u1="F" u2="&#xe7;" k="30" />
+    <hkern u1="F" u2="&#xe6;" k="40" />
+    <hkern u1="F" u2="&#xe5;" k="40" />
+    <hkern u1="F" u2="&#xe4;" k="40" />
+    <hkern u1="F" u2="&#xe3;" k="40" />
+    <hkern u1="F" u2="&#xe2;" k="40" />
+    <hkern u1="F" u2="&#xe1;" k="40" />
+    <hkern u1="F" u2="&#xe0;" k="40" />
+    <hkern u1="F" g2="germandbls" k="10" />
+    <hkern u1="F" u2="&#xdd;" k="10" />
+    <hkern u1="F" u2="&#xdc;" k="10" />
+    <hkern u1="F" u2="&#xdb;" k="10" />
+    <hkern u1="F" u2="&#xda;" k="10" />
+    <hkern u1="F" u2="&#xd9;" k="10" />
+    <hkern u1="F" u2="&#xd8;" k="30" />
+    <hkern u1="F" u2="&#xd7;" k="20" />
+    <hkern u1="F" u2="&#xd6;" k="30" />
+    <hkern u1="F" u2="&#xd5;" k="30" />
+    <hkern u1="F" u2="&#xd4;" k="30" />
+    <hkern u1="F" u2="&#xd3;" k="30" />
+    <hkern u1="F" u2="&#xd2;" k="30" />
+    <hkern u1="F" u2="&#xc7;" k="30" />
+    <hkern u1="F" u2="&#xc6;" k="50" />
+    <hkern u1="F" u2="&#xc5;" k="50" />
+    <hkern u1="F" u2="&#xc4;" k="50" />
+    <hkern u1="F" u2="&#xc3;" k="50" />
+    <hkern u1="F" u2="&#xc2;" k="50" />
+    <hkern u1="F" u2="&#xc1;" k="50" />
+    <hkern u1="F" u2="&#xc0;" k="50" />
+    <hkern u1="F" u2="&#xbb;" k="20" />
+    <hkern u1="F" u2="&#xb7;" k="20" />
+    <hkern u1="F" u2="&#xb1;" k="20" />
+    <hkern u1="F" u2="&#xad;" k="20" />
+    <hkern u1="F" u2="&#xac;" k="20" />
+    <hkern u1="F" u2="&#xab;" k="20" />
+    <hkern u1="F" u2="&#xa9;" k="30" />
+    <hkern u1="F" u2="&#xa5;" k="10" />
+    <hkern u1="F" u2="&#xa4;" k="20" />
+    <hkern u1="F" u2="&#xa2;" k="30" />
+    <hkern u1="F" u2="&#x7e;" k="20" />
+    <hkern u1="F" u2="z" k="20" />
+    <hkern u1="F" u2="y" k="20" />
+    <hkern u1="F" u2="x" k="10" />
+    <hkern u1="F" u2="w" k="20" />
+    <hkern u1="F" u2="v" k="20" />
+    <hkern u1="F" u2="u" k="20" />
+    <hkern u1="F" u2="t" k="10" />
+    <hkern u1="F" u2="s" k="20" />
+    <hkern u1="F" u2="r" k="20" />
+    <hkern u1="F" u2="q" k="30" />
+    <hkern u1="F" u2="p" k="20" />
+    <hkern u1="F" u2="o" k="30" />
+    <hkern u1="F" u2="n" k="20" />
+    <hkern u1="F" u2="m" k="20" />
+    <hkern u1="F" u2="l" k="10" />
+    <hkern u1="F" u2="k" k="10" />
+    <hkern u1="F" u2="j" k="10" />
+    <hkern u1="F" u2="i" k="10" />
+    <hkern u1="F" u2="h" k="10" />
+    <hkern u1="F" u2="g" k="40" />
+    <hkern u1="F" u2="f" k="20" />
+    <hkern u1="F" u2="e" k="30" />
+    <hkern u1="F" u2="d" k="30" />
+    <hkern u1="F" u2="c" k="30" />
+    <hkern u1="F" u2="b" k="10" />
+    <hkern u1="F" u2="a" k="40" />
+    <hkern u1="F" u2="Z" k="40" />
+    <hkern u1="F" u2="Y" k="10" />
+    <hkern u1="F" u2="X" k="20" />
+    <hkern u1="F" u2="W" k="10" />
+    <hkern u1="F" u2="V" k="10" />
+    <hkern u1="F" u2="U" k="10" />
+    <hkern u1="F" u2="S" k="30" />
+    <hkern u1="F" u2="Q" k="30" />
+    <hkern u1="F" u2="O" k="30" />
+    <hkern u1="F" u2="G" k="30" />
+    <hkern u1="F" u2="C" k="30" />
+    <hkern u1="F" u2="A" k="50" />
+    <hkern u1="F" u2="&#x3e;" k="20" />
+    <hkern u1="F" u2="&#x3d;" k="20" />
+    <hkern u1="F" u2="&#x3c;" k="20" />
+    <hkern u1="F" u2="&#x36;" k="30" />
+    <hkern u1="F" u2="&#x30;" k="30" />
+    <hkern u1="F" u2="&#x2d;" k="20" />
+    <hkern u1="F" u2="&#x2b;" k="20" />
+    <hkern u1="F" u2="&#x24;" k="30" />
+    <hkern u1="F" u2="&#x23;" k="20" />
+    <hkern u1="F" g2="s.sc" k="30" />
+    <hkern u1="F" g2="j.sc" k="30" />
+    <hkern u1="F" g2="d.sc" k="10" />
+    <hkern u1="F" u2="J" k="20" />
+    <hkern u1="F" u2="&#x2f;" k="80" />
+    <hkern u1="G" g2="zero.slash" k="10" />
+    <hkern u1="G" g2="yenoldstyle" k="20" />
+    <hkern u1="G" g2="yen.sc" k="20" />
+    <hkern u1="G" g2="ydieresis.sc" k="30" />
+    <hkern u1="G" g2="ycircumflex.sc" k="30" />
+    <hkern u1="G" g2="yacute.sc" k="30" />
+    <hkern u1="G" g2="y.sc" k="30" />
+    <hkern u1="G" g2="wcircumflex.sc" k="20" />
+    <hkern u1="G" g2="w.sc" k="20" />
+    <hkern u1="G" g2="v.sc" k="20" />
+    <hkern u1="G" g2="utilde.sc" k="10" />
+    <hkern u1="G" g2="uring.sc" k="10" />
+    <hkern u1="G" g2="uogonek.sc" k="10" />
+    <hkern u1="G" g2="uni021B.sc" k="10" />
+    <hkern u1="G" g2="uni0217.sc" k="10" />
+    <hkern u1="G" g2="uni0215.sc" k="10" />
+    <hkern u1="G" g2="umacron.sc" k="10" />
+    <hkern u1="G" g2="uhungarumlaut.sc" k="10" />
+    <hkern u1="G" g2="ugrave.sc" k="10" />
+    <hkern u1="G" g2="udieresis.sc" k="10" />
+    <hkern u1="G" g2="ucircumflex.sc" k="10" />
+    <hkern u1="G" g2="ubreve.sc" k="10" />
+    <hkern u1="G" g2="uacute.sc" k="10" />
+    <hkern u1="G" g2="u.sc" k="10" />
+    <hkern u1="G" g2="tcommaaccent.sc" k="10" />
+    <hkern u1="G" g2="tcaron.sc" k="10" />
+    <hkern u1="G" g2="t.sc" k="10" />
+    <hkern u1="G" g2="sixoldstyle" k="10" />
+    <hkern u1="G" g2="ft.liga" k="20" />
+    <hkern u1="G" g2="fl.liga" k="20" />
+    <hkern u1="G" g2="fk.liga" k="20" />
+    <hkern u1="G" g2="fj.liga" k="20" />
+    <hkern u1="G" g2="fi.liga" k="20" />
+    <hkern u1="G" g2="fh.liga" k="20" />
+    <hkern u1="G" g2="ffj.liga" k="20" />
+    <hkern u1="G" g2="ffi.liga" k="20" />
+    <hkern u1="G" g2="ff.liga" k="20" />
+    <hkern u1="G" g2="fb.liga" k="20" />
+    <hkern u1="G" g2="afii10110.sc" k="20" />
+    <hkern u1="G" g2="afii10108.sc" k="10" />
+    <hkern u1="G" g2="afii10099.sc" k="10" />
+    <hkern u1="G" g2="afii10085.sc" k="20" />
+    <hkern u1="G" g2="afii10084.sc" k="10" />
+    <hkern u1="G" g2="afii10072.77.liga" k="20" />
+    <hkern u1="G" u2="&#x221a;" k="20" />
+    <hkern u1="G" u2="&#x2126;" k="10" />
+    <hkern u1="G" u2="&#x20ac;" k="10" />
+    <hkern u1="G" u2="&#x45e;" k="20" />
+    <hkern u1="G" u2="&#x445;" k="20" />
+    <hkern u1="G" u2="&#x443;" k="20" />
+    <hkern u1="G" u2="&#x436;" k="20" />
+    <hkern u1="G" u2="&#x424;" k="10" />
+    <hkern u1="G" u2="&#x421;" k="10" />
+    <hkern u1="G" u2="&#x41e;" k="10" />
+    <hkern u1="G" u2="&#x404;" k="10" />
+    <hkern u1="G" u2="&#x21b;" k="20" />
+    <hkern u1="G" u2="&#x20e;" k="10" />
+    <hkern u1="G" u2="&#x20c;" k="10" />
+    <hkern u1="G" u2="&#x192;" k="20" />
+    <hkern u1="G" u2="&#x177;" k="20" />
+    <hkern u1="G" u2="&#x175;" k="20" />
+    <hkern u1="G" u2="&#x165;" k="20" />
+    <hkern u1="G" u2="&#x163;" k="20" />
+    <hkern u1="G" u2="&#x152;" k="10" />
+    <hkern u1="G" u2="&#x150;" k="10" />
+    <hkern u1="G" u2="&#x14e;" k="10" />
+    <hkern u1="G" u2="&#x14c;" k="10" />
+    <hkern u1="G" u2="&#x122;" k="10" />
+    <hkern u1="G" u2="&#x120;" k="10" />
+    <hkern u1="G" u2="&#x11e;" k="10" />
+    <hkern u1="G" u2="&#x11c;" k="10" />
+    <hkern u1="G" u2="&#x10c;" k="10" />
+    <hkern u1="G" u2="&#x10a;" k="10" />
+    <hkern u1="G" u2="&#x108;" k="10" />
+    <hkern u1="G" u2="&#x106;" k="10" />
+    <hkern u1="G" u2="&#xff;" k="20" />
+    <hkern u1="G" u2="&#xfd;" k="20" />
+    <hkern u1="G" u2="&#xd8;" k="10" />
+    <hkern u1="G" u2="&#xd6;" k="10" />
+    <hkern u1="G" u2="&#xd5;" k="10" />
+    <hkern u1="G" u2="&#xd4;" k="10" />
+    <hkern u1="G" u2="&#xd3;" k="10" />
+    <hkern u1="G" u2="&#xd2;" k="10" />
+    <hkern u1="G" u2="&#xc7;" k="10" />
+    <hkern u1="G" u2="&#xa9;" k="10" />
+    <hkern u1="G" u2="y" k="20" />
+    <hkern u1="G" u2="x" k="20" />
+    <hkern u1="G" u2="w" k="20" />
+    <hkern u1="G" u2="v" k="20" />
+    <hkern u1="G" u2="t" k="20" />
+    <hkern u1="G" u2="f" k="20" />
+    <hkern u1="G" u2="Q" k="10" />
+    <hkern u1="G" u2="O" k="10" />
+    <hkern u1="G" u2="G" k="10" />
+    <hkern u1="G" u2="C" k="10" />
+    <hkern u1="G" u2="&#x36;" k="10" />
+    <hkern u1="G" u2="&#x30;" k="10" />
+    <hkern u1="K" g2="s.sc" k="20" />
+    <hkern u1="K" g2="afii10097.sc" k="20" />
+    <hkern u1="K" g2="afii10092.sc" k="50" />
+    <hkern u1="K" g2="afii10089.sc" k="50" />
+    <hkern u1="K" u2="&#x44f;" k="20" />
+    <hkern u1="K" u2="&#x44a;" k="40" />
+    <hkern u1="K" u2="&#x447;" k="50" />
+    <hkern u1="K" u2="&#x442;" k="40" />
+    <hkern u1="K" u2="&#x42f;" k="20" />
+    <hkern u1="K" u2="&#x42a;" k="20" />
+    <hkern u1="K" u2="&#x427;" k="30" />
+    <hkern u1="O" g2="s.sc" k="10" />
+    <hkern u1="O" g2="afii10097.sc" k="20" />
+    <hkern u1="O" g2="afii10092.sc" k="20" />
+    <hkern u1="O" u2="&#x44a;" k="10" />
+    <hkern u1="O" u2="&#x447;" k="10" />
+    <hkern u1="O" u2="&#x442;" k="10" />
+    <hkern u1="O" u2="&#x42f;" k="20" />
+    <hkern u1="O" u2="&#x42a;" k="40" />
+    <hkern u1="O" u2="&#x427;" k="20" />
+    <hkern u1="O" u2="&#x37;" k="40" />
+    <hkern u1="O" u2="&#x33;" k="40" />
+    <hkern u1="O" u2="&#x32;" k="30" />
+    <hkern u1="O" u2="&#x31;" k="20" />
+    <hkern u1="O" u2="&#x2f;" k="30" />
+    <hkern u1="P" g2="afii10097.sc" k="30" />
+    <hkern u1="P" g2="afii10089.sc" k="10" />
+    <hkern u1="P" u2="&#x44f;" k="20" />
+    <hkern u1="P" u2="&#x44a;" k="10" />
+    <hkern u1="P" u2="&#x447;" k="20" />
+    <hkern u1="P" u2="&#x42f;" k="30" />
+    <hkern u1="P" u2="&#x42a;" k="20" />
+    <hkern u1="P" u2="&#x427;" k="10" />
+    <hkern u1="P" u2="J" k="30" />
+    <hkern u1="P" u2="&#x2f;" k="70" />
+    <hkern u1="Q" g2="s.sc" k="10" />
+    <hkern u1="Q" g2="afii10097.sc" k="20" />
+    <hkern u1="Q" g2="afii10092.sc" k="20" />
+    <hkern u1="Q" u2="&#x44a;" k="10" />
+    <hkern u1="Q" u2="&#x447;" k="10" />
+    <hkern u1="Q" u2="&#x442;" k="10" />
+    <hkern u1="Q" u2="&#x42f;" k="20" />
+    <hkern u1="Q" u2="&#x42a;" k="40" />
+    <hkern u1="Q" u2="&#x427;" k="20" />
+    <hkern u1="Q" u2="&#x37;" k="40" />
+    <hkern u1="Q" u2="&#x33;" k="40" />
+    <hkern u1="Q" u2="&#x32;" k="30" />
+    <hkern u1="Q" u2="&#x31;" k="20" />
+    <hkern u1="Q" u2="&#x2f;" k="30" />
+    <hkern u1="R" g2="s.sc" k="20" />
+    <hkern u1="S" g2="s.sc" k="10" />
+    <hkern u1="T" g2="s.sc" k="30" />
+    <hkern u1="T" g2="r.sc" k="20" />
+    <hkern u1="T" g2="p.sc" k="20" />
+    <hkern u1="T" g2="n.sc" k="20" />
+    <hkern u1="T" g2="m.sc" k="20" />
+    <hkern u1="T" g2="l.sc" k="20" />
+    <hkern u1="T" g2="k.sc" k="20" />
+    <hkern u1="T" g2="i.sc" k="20" />
+    <hkern u1="T" g2="h.sc" k="20" />
+    <hkern u1="T" g2="f.sc" k="20" />
+    <hkern u1="T" g2="e.sc" k="20" />
+    <hkern u1="T" g2="d.sc" k="20" />
+    <hkern u1="T" g2="b.sc" k="40" />
+    <hkern u1="T" g2="afii10097.sc" k="70" />
+    <hkern u1="T" g2="afii10096.sc" k="20" />
+    <hkern u1="T" g2="afii10094.sc" k="20" />
+    <hkern u1="T" g2="afii10093.sc" k="20" />
+    <hkern u1="T" g2="afii10092.sc" k="10" />
+    <hkern u1="T" g2="afii10091.sc" k="20" />
+    <hkern u1="T" g2="afii10090.sc" k="20" />
+    <hkern u1="T" g2="afii10089.sc" k="20" />
+    <hkern u1="T" g2="afii10088.sc" k="20" />
+    <hkern u1="T" g2="afii10082.sc" k="20" />
+    <hkern u1="T" g2="afii10081.sc" k="20" />
+    <hkern u1="T" g2="afii10079.sc" k="20" />
+    <hkern u1="T" g2="afii10078.sc" k="20" />
+    <hkern u1="T" g2="afii10076.sc" k="20" />
+    <hkern u1="T" g2="afii10074.sc" k="20" />
+    <hkern u1="T" g2="afii10070.sc" k="20" />
+    <hkern u1="T" g2="afii10068.sc" k="20" />
+    <hkern u1="T" g2="afii10067.sc" k="20" />
+    <hkern u1="T" g2="afii10066.sc" k="20" />
+    <hkern u1="T" u2="&#x44f;" k="40" />
+    <hkern u1="T" u2="&#x44a;" k="50" />
+    <hkern u1="T" u2="&#x447;" k="40" />
+    <hkern u1="T" u2="&#x442;" k="60" />
+    <hkern u1="T" u2="&#x42f;" k="40" />
+    <hkern u1="T" u2="J" k="20" />
+    <hkern u1="T" u2="&#x2f;" k="100" />
+    <hkern u1="U" u2="J" k="20" />
+    <hkern u1="U" u2="&#x2f;" k="50" />
+    <hkern u1="V" g2="s.sc" k="30" />
+    <hkern u1="V" g2="r.sc" k="10" />
+    <hkern u1="V" g2="p.sc" k="10" />
+    <hkern u1="V" g2="n.sc" k="10" />
+    <hkern u1="V" g2="m.sc" k="10" />
+    <hkern u1="V" g2="l.sc" k="10" />
+    <hkern u1="V" g2="k.sc" k="10" />
+    <hkern u1="V" g2="j.sc" k="10" />
+    <hkern u1="V" g2="i.sc" k="10" />
+    <hkern u1="V" g2="h.sc" k="10" />
+    <hkern u1="V" g2="f.sc" k="10" />
+    <hkern u1="V" g2="e.sc" k="10" />
+    <hkern u1="V" g2="d.sc" k="10" />
+    <hkern u1="V" g2="b.sc" k="10" />
+    <hkern u1="V" g2="afii10097.sc" k="30" />
+    <hkern u1="V" g2="afii10096.sc" k="10" />
+    <hkern u1="V" g2="afii10094.sc" k="10" />
+    <hkern u1="V" g2="afii10093.sc" k="10" />
+    <hkern u1="V" g2="afii10092.sc" k="10" />
+    <hkern u1="V" g2="afii10091.sc" k="10" />
+    <hkern u1="V" g2="afii10090.sc" k="10" />
+    <hkern u1="V" g2="afii10089.sc" k="10" />
+    <hkern u1="V" g2="afii10088.sc" k="10" />
+    <hkern u1="V" g2="afii10082.sc" k="10" />
+    <hkern u1="V" g2="afii10081.sc" k="10" />
+    <hkern u1="V" g2="afii10079.sc" k="10" />
+    <hkern u1="V" g2="afii10078.sc" k="10" />
+    <hkern u1="V" g2="afii10076.sc" k="10" />
+    <hkern u1="V" g2="afii10074.sc" k="10" />
+    <hkern u1="V" u2="&#x44f;" k="50" />
+    <hkern u1="V" u2="&#x44a;" k="20" />
+    <hkern u1="V" u2="&#x447;" k="30" />
+    <hkern u1="V" u2="&#x442;" k="30" />
+    <hkern u1="V" u2="&#x42f;" k="40" />
+    <hkern u1="V" u2="J" k="20" />
+    <hkern u1="V" u2="&#x2f;" k="100" />
+    <hkern u1="W" g2="s.sc" k="20" />
+    <hkern u1="W" u2="J" k="20" />
+    <hkern u1="W" u2="&#x2f;" k="80" />
+    <hkern u1="X" g2="s.sc" k="20" />
+    <hkern u1="X" g2="afii10097.sc" k="20" />
+    <hkern u1="X" g2="afii10092.sc" k="50" />
+    <hkern u1="X" g2="afii10089.sc" k="50" />
+    <hkern u1="X" u2="&#x44f;" k="20" />
+    <hkern u1="X" u2="&#x44a;" k="40" />
+    <hkern u1="X" u2="&#x447;" k="50" />
+    <hkern u1="X" u2="&#x442;" k="40" />
+    <hkern u1="X" u2="&#x42f;" k="20" />
+    <hkern u1="X" u2="&#x42a;" k="20" />
+    <hkern u1="X" u2="&#x427;" k="30" />
+    <hkern u1="Y" g2="s.sc" k="40" />
+    <hkern u1="Y" g2="r.sc" k="10" />
+    <hkern u1="Y" g2="p.sc" k="10" />
+    <hkern u1="Y" g2="n.sc" k="10" />
+    <hkern u1="Y" g2="m.sc" k="10" />
+    <hkern u1="Y" g2="l.sc" k="10" />
+    <hkern u1="Y" g2="k.sc" k="10" />
+    <hkern u1="Y" g2="j.sc" k="10" />
+    <hkern u1="Y" g2="i.sc" k="10" />
+    <hkern u1="Y" g2="h.sc" k="10" />
+    <hkern u1="Y" g2="f.sc" k="10" />
+    <hkern u1="Y" g2="e.sc" k="10" />
+    <hkern u1="Y" g2="d.sc" k="10" />
+    <hkern u1="Y" g2="b.sc" k="10" />
+    <hkern u1="Y" u2="J" k="20" />
+    <hkern u1="Y" u2="&#x2f;" k="90" />
+    <hkern u1="Z" g2="s.sc" k="10" />
+    <hkern u1="^" g2="threeoldstyle" k="20" />
+    <hkern u1="^" g2="three.sc" k="10" />
+    <hkern u1="^" g2="nineoldstyle" k="30" />
+    <hkern u1="^" g2="nine.sc" k="30" />
+    <hkern u1="^" g2="fouroldstyle" k="160" />
+    <hkern u1="^" g2="four.sc" k="100" />
+    <hkern u1="^" g2="eight.sc" k="30" />
+    <hkern u1="^" g2="afii10097.sc" k="60" />
+    <hkern u1="^" u2="&#x44f;" k="30" />
+    <hkern u1="^" u2="&#x34;" k="70" />
+    <hkern u1="_" g2="sevenoldstyle" k="40" />
+    <hkern u1="_" g2="eightoldstyle" k="20" />
+    <hkern u1="_" g2="afii10092.sc" k="90" />
+    <hkern u1="_" g2="afii10089.sc" k="90" />
+    <hkern u1="_" u2="&#x44a;" k="80" />
+    <hkern u1="_" u2="&#x447;" k="80" />
+    <hkern u1="_" u2="&#x442;" k="90" />
+    <hkern u1="_" u2="&#x39;" k="40" />
+    <hkern u1="_" u2="&#x38;" k="30" />
+    <hkern u1="_" u2="&#x37;" k="70" />
+    <hkern u1="_" u2="&#x34;" k="40" />
+    <hkern u1="a" u2="&#x44a;" k="40" />
+    <hkern u1="a" u2="&#x447;" k="20" />
+    <hkern u1="a" u2="&#x442;" k="40" />
+    <hkern u1="b" g2="threeoldstyle" k="40" />
+    <hkern u1="b" g2="sevenoldstyle" k="20" />
+    <hkern u1="b" u2="&#x44f;" k="10" />
+    <hkern u1="b" u2="&#x44a;" k="40" />
+    <hkern u1="b" u2="&#x447;" k="10" />
+    <hkern u1="b" u2="&#x442;" k="20" />
+    <hkern u1="b" u2="&#x2f;" k="20" />
+    <hkern u1="c" u2="&#x447;" k="20" />
+    <hkern u1="e" u2="&#x44a;" k="30" />
+    <hkern u1="e" u2="&#x447;" k="10" />
+    <hkern u1="e" u2="&#x442;" k="30" />
+    <hkern u1="f" u2="&#x2f;" k="40" />
+    <hkern u1="j" g2="uni0203.alt1" k="10" />
+    <hkern u1="j" g2="uni0201.alt1" k="10" />
+    <hkern u1="j" g2="atilde.alt1" k="10" />
+    <hkern u1="j" g2="aring.alt1" k="10" />
+    <hkern u1="j" g2="aogonek.alt1" k="10" />
+    <hkern u1="j" g2="amacron.alt1" k="10" />
+    <hkern u1="j" g2="agrave.alt1" k="10" />
+    <hkern u1="j" g2="afii10065.alt1" k="10" />
+    <hkern u1="j" g2="afii10065.77.liga" k="10" />
+    <hkern u1="j" g2="ae.alt1" k="10" />
+    <hkern u1="j" g2="adieresis.alt1" k="10" />
+    <hkern u1="j" g2="acircumflex.alt1" k="10" />
+    <hkern u1="j" g2="abreve.alt1" k="10" />
+    <hkern u1="j" g2="aacute.alt1" k="10" />
+    <hkern u1="j" g2="a.alt1" k="10" />
+    <hkern u1="j" u2="&#x430;" k="10" />
+    <hkern u1="j" u2="&#x203;" k="10" />
+    <hkern u1="j" u2="&#x201;" k="10" />
+    <hkern u1="j" u2="&#x105;" k="10" />
+    <hkern u1="j" u2="&#x103;" k="10" />
+    <hkern u1="j" u2="&#x101;" k="10" />
+    <hkern u1="j" u2="&#xe6;" k="10" />
+    <hkern u1="j" u2="&#xe5;" k="10" />
+    <hkern u1="j" u2="&#xe4;" k="10" />
+    <hkern u1="j" u2="&#xe3;" k="10" />
+    <hkern u1="j" u2="&#xe2;" k="10" />
+    <hkern u1="j" u2="&#xe1;" k="10" />
+    <hkern u1="j" u2="&#xe0;" k="10" />
+    <hkern u1="j" u2="a" k="10" />
+    <hkern u1="k" u2="&#x44f;" k="20" />
+    <hkern u1="k" u2="&#x44a;" k="30" />
+    <hkern u1="k" u2="&#x447;" k="30" />
+    <hkern u1="k" u2="&#x442;" k="30" />
+    <hkern u1="l" g2="yenoldstyle" k="40" />
+    <hkern u1="l" g2="uni0203.alt1" k="-20" />
+    <hkern u1="l" g2="uni0201.alt1" k="-20" />
+    <hkern u1="l" g2="atilde.alt1" k="-20" />
+    <hkern u1="l" g2="aring.alt1" k="-20" />
+    <hkern u1="l" g2="aogonek.alt1" k="-20" />
+    <hkern u1="l" g2="amacron.alt1" k="-20" />
+    <hkern u1="l" g2="agrave.alt1" k="-20" />
+    <hkern u1="l" g2="afii10065.alt1" k="-20" />
+    <hkern u1="l" g2="afii10065.77.liga" k="-20" />
+    <hkern u1="l" g2="ae.alt1" k="-20" />
+    <hkern u1="l" g2="adieresis.alt1" k="-20" />
+    <hkern u1="l" g2="acircumflex.alt1" k="-20" />
+    <hkern u1="l" g2="abreve.alt1" k="-20" />
+    <hkern u1="l" g2="aacute.alt1" k="-20" />
+    <hkern u1="l" g2="a.alt1" k="-20" />
+    <hkern u1="l" u2="&#x221a;" k="40" />
+    <hkern u1="l" u2="&#x2122;" k="30" />
+    <hkern u1="l" u2="&#x201d;" k="30" />
+    <hkern u1="l" u2="&#x201c;" k="30" />
+    <hkern u1="l" u2="&#x2019;" k="30" />
+    <hkern u1="l" u2="&#x2018;" k="30" />
+    <hkern u1="l" u2="&#x45e;" k="40" />
+    <hkern u1="l" u2="&#x443;" k="40" />
+    <hkern u1="l" u2="&#x430;" k="-20" />
+    <hkern u1="l" u2="&#x2c9;" k="30" />
+    <hkern u1="l" u2="&#x21b;" k="20" />
+    <hkern u1="l" u2="&#x217;" k="20" />
+    <hkern u1="l" u2="&#x215;" k="20" />
+    <hkern u1="l" u2="&#x203;" k="-20" />
+    <hkern u1="l" u2="&#x201;" k="-20" />
+    <hkern u1="l" u2="&#x177;" k="40" />
+    <hkern u1="l" u2="&#x175;" k="30" />
+    <hkern u1="l" u2="&#x173;" k="20" />
+    <hkern u1="l" u2="&#x171;" k="20" />
+    <hkern u1="l" u2="&#x16f;" k="20" />
+    <hkern u1="l" u2="&#x16d;" k="20" />
+    <hkern u1="l" u2="&#x16b;" k="20" />
+    <hkern u1="l" u2="&#x169;" k="20" />
+    <hkern u1="l" u2="&#x165;" k="20" />
+    <hkern u1="l" u2="&#x163;" k="20" />
+    <hkern u1="l" u2="&#x105;" k="-20" />
+    <hkern u1="l" u2="&#x103;" k="-20" />
+    <hkern u1="l" u2="&#x101;" k="-20" />
+    <hkern u1="l" u2="&#xff;" k="40" />
+    <hkern u1="l" u2="&#xfd;" k="40" />
+    <hkern u1="l" u2="&#xfc;" k="20" />
+    <hkern u1="l" u2="&#xfb;" k="20" />
+    <hkern u1="l" u2="&#xfa;" k="20" />
+    <hkern u1="l" u2="&#xf9;" k="20" />
+    <hkern u1="l" u2="&#xe6;" k="-20" />
+    <hkern u1="l" u2="&#xe5;" k="-20" />
+    <hkern u1="l" u2="&#xe4;" k="-20" />
+    <hkern u1="l" u2="&#xe3;" k="-20" />
+    <hkern u1="l" u2="&#xe2;" k="-20" />
+    <hkern u1="l" u2="&#xe1;" k="-20" />
+    <hkern u1="l" u2="&#xe0;" k="-20" />
+    <hkern u1="l" u2="&#xba;" k="30" />
+    <hkern u1="l" u2="&#xb0;" k="30" />
+    <hkern u1="l" u2="&#xae;" k="30" />
+    <hkern u1="l" u2="&#xaa;" k="30" />
+    <hkern u1="l" u2="y" k="40" />
+    <hkern u1="l" u2="w" k="30" />
+    <hkern u1="l" u2="v" k="40" />
+    <hkern u1="l" u2="u" k="20" />
+    <hkern u1="l" u2="t" k="20" />
+    <hkern u1="l" u2="a" k="-20" />
+    <hkern u1="l" u2="^" k="30" />
+    <hkern u1="l" u2="&#x2a;" k="30" />
+    <hkern u1="l" u2="&#x27;" k="30" />
+    <hkern u1="l" u2="&#x22;" k="30" />
+    <hkern u1="o" g2="threeoldstyle" k="40" />
+    <hkern u1="o" g2="sevenoldstyle" k="20" />
+    <hkern u1="o" u2="&#x44f;" k="10" />
+    <hkern u1="o" u2="&#x44a;" k="40" />
+    <hkern u1="o" u2="&#x447;" k="10" />
+    <hkern u1="o" u2="&#x442;" k="20" />
+    <hkern u1="o" u2="&#x2f;" k="20" />
+    <hkern u1="p" g2="threeoldstyle" k="40" />
+    <hkern u1="p" g2="sevenoldstyle" k="20" />
+    <hkern u1="p" u2="&#x44f;" k="10" />
+    <hkern u1="p" u2="&#x44a;" k="40" />
+    <hkern u1="p" u2="&#x447;" k="10" />
+    <hkern u1="p" u2="&#x442;" k="20" />
+    <hkern u1="p" u2="&#x2f;" k="20" />
+    <hkern u1="q" g2="zerooldstyle" k="10" />
+    <hkern u1="q" g2="zero.slash.oldstyle" k="10" />
+    <hkern u1="q" g2="yenoldstyle" k="20" />
+    <hkern u1="q" g2="ct.liga" k="10" />
+    <hkern u1="q" g2="copyright.case" k="10" />
+    <hkern u1="q" g2="ck.liga" k="10" />
+    <hkern u1="q" g2="ch.liga" k="10" />
+    <hkern u1="q" g2="centoldstyle" k="10" />
+    <hkern u1="q" g2="cb.liga" k="10" />
+    <hkern u1="q" g2="Eurooldstyle" k="10" />
+    <hkern u1="q" u2="&#x221a;" k="20" />
+    <hkern u1="q" u2="&#x2202;" k="10" />
+    <hkern u1="q" u2="&#x212e;" k="10" />
+    <hkern u1="q" u2="&#x2122;" k="30" />
+    <hkern u1="q" u2="&#x201d;" k="30" />
+    <hkern u1="q" u2="&#x201c;" k="30" />
+    <hkern u1="q" u2="&#x2019;" k="30" />
+    <hkern u1="q" u2="&#x2018;" k="30" />
+    <hkern u1="q" u2="&#x45e;" k="20" />
+    <hkern u1="q" u2="&#x454;" k="10" />
+    <hkern u1="q" u2="&#x451;" k="10" />
+    <hkern u1="q" u2="&#x444;" k="10" />
+    <hkern u1="q" u2="&#x443;" k="20" />
+    <hkern u1="q" u2="&#x441;" k="10" />
+    <hkern u1="q" u2="&#x43e;" k="10" />
+    <hkern u1="q" u2="&#x435;" k="10" />
+    <hkern u1="q" u2="&#x431;" k="10" />
+    <hkern u1="q" u2="&#x2c9;" k="30" />
+    <hkern u1="q" u2="&#x20f;" k="10" />
+    <hkern u1="q" u2="&#x20d;" k="10" />
+    <hkern u1="q" u2="&#x177;" k="20" />
+    <hkern u1="q" u2="&#x153;" k="10" />
+    <hkern u1="q" u2="&#x151;" k="10" />
+    <hkern u1="q" u2="&#x14f;" k="10" />
+    <hkern u1="q" u2="&#x14d;" k="10" />
+    <hkern u1="q" u2="&#x11b;" k="10" />
+    <hkern u1="q" u2="&#x119;" k="10" />
+    <hkern u1="q" u2="&#x117;" k="10" />
+    <hkern u1="q" u2="&#x115;" k="10" />
+    <hkern u1="q" u2="&#x113;" k="10" />
+    <hkern u1="q" u2="&#x111;" k="10" />
+    <hkern u1="q" u2="&#x10f;" k="10" />
+    <hkern u1="q" u2="&#x10d;" k="10" />
+    <hkern u1="q" u2="&#x10b;" k="10" />
+    <hkern u1="q" u2="&#x109;" k="10" />
+    <hkern u1="q" u2="&#x107;" k="10" />
+    <hkern u1="q" u2="&#xff;" k="20" />
+    <hkern u1="q" u2="&#xfd;" k="20" />
+    <hkern u1="q" u2="&#xf8;" k="10" />
+    <hkern u1="q" u2="&#xf6;" k="10" />
+    <hkern u1="q" u2="&#xf5;" k="10" />
+    <hkern u1="q" u2="&#xf4;" k="10" />
+    <hkern u1="q" u2="&#xf3;" k="10" />
+    <hkern u1="q" u2="&#xf2;" k="10" />
+    <hkern u1="q" u2="&#xf0;" k="10" />
+    <hkern u1="q" u2="&#xeb;" k="10" />
+    <hkern u1="q" u2="&#xea;" k="10" />
+    <hkern u1="q" u2="&#xe9;" k="10" />
+    <hkern u1="q" u2="&#xe8;" k="10" />
+    <hkern u1="q" u2="&#xe7;" k="10" />
+    <hkern u1="q" u2="&#xba;" k="30" />
+    <hkern u1="q" u2="&#xb0;" k="30" />
+    <hkern u1="q" u2="&#xae;" k="30" />
+    <hkern u1="q" u2="&#xaa;" k="30" />
+    <hkern u1="q" u2="&#xa2;" k="10" />
+    <hkern u1="q" u2="y" k="20" />
+    <hkern u1="q" u2="v" k="20" />
+    <hkern u1="q" u2="q" k="10" />
+    <hkern u1="q" u2="o" k="10" />
+    <hkern u1="q" u2="e" k="10" />
+    <hkern u1="q" u2="d" k="10" />
+    <hkern u1="q" u2="c" k="10" />
+    <hkern u1="q" u2="^" k="30" />
+    <hkern u1="q" u2="&#x2a;" k="30" />
+    <hkern u1="q" u2="&#x27;" k="30" />
+    <hkern u1="q" u2="&#x22;" k="30" />
+    <hkern u1="r" u2="&#x44f;" k="10" />
+    <hkern u1="t" u2="&#x447;" k="20" />
+    <hkern u1="v" u2="&#x2f;" k="80" />
+    <hkern u1="w" u2="&#x2f;" k="70" />
+    <hkern u1="x" u2="&#x44f;" k="20" />
+    <hkern u1="x" u2="&#x44a;" k="30" />
+    <hkern u1="x" u2="&#x447;" k="30" />
+    <hkern u1="x" u2="&#x442;" k="30" />
+    <hkern u1="y" u2="&#x2f;" k="80" />
+    <hkern u1="&#x7e;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x7e;" g2="two.sc" k="40" />
+    <hkern u1="&#x7e;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x7e;" g2="three.sc" k="20" />
+    <hkern u1="&#x7e;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x7e;" g2="seven.sc" k="40" />
+    <hkern u1="&#x7e;" g2="s.sc" k="20" />
+    <hkern u1="&#x7e;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x7e;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x7e;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x7e;" u2="&#x442;" k="50" />
+    <hkern u1="&#x7e;" u2="&#x37;" k="60" />
+    <hkern u1="&#xa2;" u2="&#x447;" k="20" />
+    <hkern u1="&#xa4;" g2="twooldstyle" k="10" />
+    <hkern u1="&#xa4;" g2="two.sc" k="40" />
+    <hkern u1="&#xa4;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xa4;" g2="three.sc" k="20" />
+    <hkern u1="&#xa4;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xa4;" g2="seven.sc" k="40" />
+    <hkern u1="&#xa4;" g2="s.sc" k="20" />
+    <hkern u1="&#xa4;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#xa4;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#xa4;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xa4;" u2="&#x442;" k="50" />
+    <hkern u1="&#xa4;" u2="&#x37;" k="60" />
+    <hkern u1="&#xa5;" g2="s.sc" k="30" />
+    <hkern u1="&#xa5;" g2="r.sc" k="10" />
+    <hkern u1="&#xa5;" g2="p.sc" k="10" />
+    <hkern u1="&#xa5;" g2="n.sc" k="10" />
+    <hkern u1="&#xa5;" g2="m.sc" k="10" />
+    <hkern u1="&#xa5;" g2="l.sc" k="10" />
+    <hkern u1="&#xa5;" g2="k.sc" k="10" />
+    <hkern u1="&#xa5;" g2="j.sc" k="10" />
+    <hkern u1="&#xa5;" g2="i.sc" k="10" />
+    <hkern u1="&#xa5;" g2="h.sc" k="10" />
+    <hkern u1="&#xa5;" g2="f.sc" k="10" />
+    <hkern u1="&#xa5;" g2="e.sc" k="10" />
+    <hkern u1="&#xa5;" g2="d.sc" k="10" />
+    <hkern u1="&#xa5;" g2="b.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xa5;" g2="afii10096.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10094.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10093.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10091.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10090.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10088.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10082.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10081.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10079.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10078.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10076.sc" k="10" />
+    <hkern u1="&#xa5;" g2="afii10074.sc" k="10" />
+    <hkern u1="&#xa5;" u2="&#x44f;" k="50" />
+    <hkern u1="&#xa5;" u2="&#x44a;" k="20" />
+    <hkern u1="&#xa5;" u2="&#x447;" k="30" />
+    <hkern u1="&#xa5;" u2="&#x442;" k="30" />
+    <hkern u1="&#xa5;" u2="&#x42f;" k="40" />
+    <hkern u1="&#xa5;" u2="J" k="20" />
+    <hkern u1="&#xa5;" u2="&#x2f;" k="100" />
+    <hkern u1="&#xa9;" g2="s.sc" k="10" />
+    <hkern u1="&#xa9;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xa9;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xa9;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xa9;" u2="&#x447;" k="10" />
+    <hkern u1="&#xa9;" u2="&#x442;" k="10" />
+    <hkern u1="&#xa9;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xa9;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xa9;" u2="&#x427;" k="20" />
+    <hkern u1="&#xa9;" u2="&#x37;" k="40" />
+    <hkern u1="&#xa9;" u2="&#x33;" k="40" />
+    <hkern u1="&#xa9;" u2="&#x32;" k="30" />
+    <hkern u1="&#xa9;" u2="&#x31;" k="20" />
+    <hkern u1="&#xa9;" u2="&#x2f;" k="30" />
+    <hkern u1="&#xaa;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xaa;" g2="three.sc" k="10" />
+    <hkern u1="&#xaa;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#xaa;" g2="nine.sc" k="30" />
+    <hkern u1="&#xaa;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#xaa;" g2="four.sc" k="100" />
+    <hkern u1="&#xaa;" g2="eight.sc" k="30" />
+    <hkern u1="&#xaa;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#xaa;" u2="&#x44f;" k="30" />
+    <hkern u1="&#xaa;" u2="&#x34;" k="70" />
+    <hkern u1="&#xab;" g2="twooldstyle" k="10" />
+    <hkern u1="&#xab;" g2="two.sc" k="40" />
+    <hkern u1="&#xab;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xab;" g2="three.sc" k="20" />
+    <hkern u1="&#xab;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xab;" g2="seven.sc" k="40" />
+    <hkern u1="&#xab;" g2="s.sc" k="20" />
+    <hkern u1="&#xab;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#xab;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#xab;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xab;" u2="&#x442;" k="50" />
+    <hkern u1="&#xab;" u2="&#x37;" k="60" />
+    <hkern u1="&#xac;" g2="twooldstyle" k="10" />
+    <hkern u1="&#xac;" g2="two.sc" k="40" />
+    <hkern u1="&#xac;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xac;" g2="three.sc" k="20" />
+    <hkern u1="&#xac;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xac;" g2="seven.sc" k="40" />
+    <hkern u1="&#xac;" g2="s.sc" k="20" />
+    <hkern u1="&#xac;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#xac;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#xac;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xac;" u2="&#x442;" k="50" />
+    <hkern u1="&#xac;" u2="&#x37;" k="60" />
+    <hkern u1="&#xad;" g2="twooldstyle" k="10" />
+    <hkern u1="&#xad;" g2="two.sc" k="40" />
+    <hkern u1="&#xad;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xad;" g2="three.sc" k="20" />
+    <hkern u1="&#xad;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xad;" g2="seven.sc" k="40" />
+    <hkern u1="&#xad;" g2="s.sc" k="20" />
+    <hkern u1="&#xad;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#xad;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#xad;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xad;" u2="&#x442;" k="50" />
+    <hkern u1="&#xad;" u2="&#x37;" k="60" />
+    <hkern u1="&#xae;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xae;" g2="three.sc" k="10" />
+    <hkern u1="&#xae;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#xae;" g2="nine.sc" k="30" />
+    <hkern u1="&#xae;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#xae;" g2="four.sc" k="100" />
+    <hkern u1="&#xae;" g2="eight.sc" k="30" />
+    <hkern u1="&#xae;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#xae;" u2="&#x44f;" k="30" />
+    <hkern u1="&#xae;" u2="&#x34;" k="70" />
+    <hkern u1="&#xb0;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xb0;" g2="three.sc" k="10" />
+    <hkern u1="&#xb0;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#xb0;" g2="nine.sc" k="30" />
+    <hkern u1="&#xb0;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#xb0;" g2="four.sc" k="100" />
+    <hkern u1="&#xb0;" g2="eight.sc" k="30" />
+    <hkern u1="&#xb0;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#xb0;" u2="&#x44f;" k="30" />
+    <hkern u1="&#xb0;" u2="&#x34;" k="70" />
+    <hkern u1="&#xb1;" g2="twooldstyle" k="10" />
+    <hkern u1="&#xb1;" g2="two.sc" k="40" />
+    <hkern u1="&#xb1;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xb1;" g2="three.sc" k="20" />
+    <hkern u1="&#xb1;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xb1;" g2="seven.sc" k="40" />
+    <hkern u1="&#xb1;" g2="s.sc" k="20" />
+    <hkern u1="&#xb1;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#xb1;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#xb1;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xb1;" u2="&#x442;" k="50" />
+    <hkern u1="&#xb1;" u2="&#x37;" k="60" />
+    <hkern u1="&#xb7;" g2="twooldstyle" k="10" />
+    <hkern u1="&#xb7;" g2="two.sc" k="40" />
+    <hkern u1="&#xb7;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xb7;" g2="three.sc" k="20" />
+    <hkern u1="&#xb7;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xb7;" g2="seven.sc" k="40" />
+    <hkern u1="&#xb7;" g2="s.sc" k="20" />
+    <hkern u1="&#xb7;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#xb7;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#xb7;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xb7;" u2="&#x442;" k="50" />
+    <hkern u1="&#xb7;" u2="&#x37;" k="60" />
+    <hkern u1="&#xba;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xba;" g2="three.sc" k="10" />
+    <hkern u1="&#xba;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#xba;" g2="nine.sc" k="30" />
+    <hkern u1="&#xba;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#xba;" g2="four.sc" k="100" />
+    <hkern u1="&#xba;" g2="eight.sc" k="30" />
+    <hkern u1="&#xba;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#xba;" u2="&#x44f;" k="30" />
+    <hkern u1="&#xba;" u2="&#x34;" k="70" />
+    <hkern u1="&#xbb;" g2="twooldstyle" k="10" />
+    <hkern u1="&#xbb;" g2="two.sc" k="40" />
+    <hkern u1="&#xbb;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xbb;" g2="three.sc" k="20" />
+    <hkern u1="&#xbb;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xbb;" g2="seven.sc" k="40" />
+    <hkern u1="&#xbb;" g2="s.sc" k="20" />
+    <hkern u1="&#xbb;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#xbb;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#xbb;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xbb;" u2="&#x442;" k="50" />
+    <hkern u1="&#xbb;" u2="&#x37;" k="60" />
+    <hkern u1="&#xc0;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#xc0;" u2="&#x44a;" k="70" />
+    <hkern u1="&#xc0;" u2="&#x447;" k="50" />
+    <hkern u1="&#xc0;" u2="&#x442;" k="60" />
+    <hkern u1="&#xc0;" u2="&#x42a;" k="60" />
+    <hkern u1="&#xc0;" u2="&#x427;" k="70" />
+    <hkern u1="&#xc0;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#xc1;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#xc1;" u2="&#x44a;" k="70" />
+    <hkern u1="&#xc1;" u2="&#x447;" k="50" />
+    <hkern u1="&#xc1;" u2="&#x442;" k="60" />
+    <hkern u1="&#xc1;" u2="&#x42a;" k="60" />
+    <hkern u1="&#xc1;" u2="&#x427;" k="70" />
+    <hkern u1="&#xc1;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#xc2;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#xc2;" u2="&#x44a;" k="70" />
+    <hkern u1="&#xc2;" u2="&#x447;" k="50" />
+    <hkern u1="&#xc2;" u2="&#x442;" k="60" />
+    <hkern u1="&#xc2;" u2="&#x42a;" k="60" />
+    <hkern u1="&#xc2;" u2="&#x427;" k="70" />
+    <hkern u1="&#xc2;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#xc3;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#xc3;" u2="&#x44a;" k="70" />
+    <hkern u1="&#xc3;" u2="&#x447;" k="50" />
+    <hkern u1="&#xc3;" u2="&#x442;" k="60" />
+    <hkern u1="&#xc3;" u2="&#x42a;" k="60" />
+    <hkern u1="&#xc3;" u2="&#x427;" k="70" />
+    <hkern u1="&#xc3;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#xc4;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#xc4;" u2="&#x44a;" k="70" />
+    <hkern u1="&#xc4;" u2="&#x447;" k="50" />
+    <hkern u1="&#xc4;" u2="&#x442;" k="60" />
+    <hkern u1="&#xc4;" u2="&#x42a;" k="60" />
+    <hkern u1="&#xc4;" u2="&#x427;" k="70" />
+    <hkern u1="&#xc4;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#xc5;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#xc5;" u2="&#x44a;" k="70" />
+    <hkern u1="&#xc5;" u2="&#x447;" k="50" />
+    <hkern u1="&#xc5;" u2="&#x442;" k="60" />
+    <hkern u1="&#xc5;" u2="&#x42a;" k="60" />
+    <hkern u1="&#xc5;" u2="&#x427;" k="70" />
+    <hkern u1="&#xc5;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#xc6;" g2="s.sc" k="10" />
+    <hkern u1="&#xc6;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xc6;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#xc6;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xc6;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xc6;" u2="&#x447;" k="20" />
+    <hkern u1="&#xc6;" u2="&#x442;" k="20" />
+    <hkern u1="&#xc6;" u2="&#x42f;" k="10" />
+    <hkern u1="&#xc6;" u2="&#x427;" k="20" />
+    <hkern u1="&#xc7;" g2="s.sc" k="20" />
+    <hkern u1="&#xc7;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#xc7;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xc7;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xc7;" u2="&#x447;" k="20" />
+    <hkern u1="&#xc8;" g2="s.sc" k="10" />
+    <hkern u1="&#xc8;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xc8;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#xc8;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xc8;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xc8;" u2="&#x447;" k="20" />
+    <hkern u1="&#xc8;" u2="&#x442;" k="20" />
+    <hkern u1="&#xc8;" u2="&#x42f;" k="10" />
+    <hkern u1="&#xc8;" u2="&#x427;" k="20" />
+    <hkern u1="&#xc9;" g2="s.sc" k="10" />
+    <hkern u1="&#xc9;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xc9;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#xc9;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xc9;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xc9;" u2="&#x447;" k="20" />
+    <hkern u1="&#xc9;" u2="&#x442;" k="20" />
+    <hkern u1="&#xc9;" u2="&#x42f;" k="10" />
+    <hkern u1="&#xc9;" u2="&#x427;" k="20" />
+    <hkern u1="&#xca;" g2="s.sc" k="10" />
+    <hkern u1="&#xca;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xca;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#xca;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xca;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xca;" u2="&#x447;" k="20" />
+    <hkern u1="&#xca;" u2="&#x442;" k="20" />
+    <hkern u1="&#xca;" u2="&#x42f;" k="10" />
+    <hkern u1="&#xca;" u2="&#x427;" k="20" />
+    <hkern u1="&#xcb;" g2="s.sc" k="10" />
+    <hkern u1="&#xcb;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xcb;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#xcb;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xcb;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xcb;" u2="&#x447;" k="20" />
+    <hkern u1="&#xcb;" u2="&#x442;" k="20" />
+    <hkern u1="&#xcb;" u2="&#x42f;" k="10" />
+    <hkern u1="&#xcb;" u2="&#x427;" k="20" />
+    <hkern u1="&#xd0;" g2="s.sc" k="10" />
+    <hkern u1="&#xd0;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xd0;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xd0;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xd0;" u2="&#x447;" k="10" />
+    <hkern u1="&#xd0;" u2="&#x442;" k="10" />
+    <hkern u1="&#xd0;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xd0;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xd0;" u2="&#x427;" k="20" />
+    <hkern u1="&#xd0;" u2="&#x37;" k="40" />
+    <hkern u1="&#xd0;" u2="&#x33;" k="40" />
+    <hkern u1="&#xd0;" u2="&#x32;" k="30" />
+    <hkern u1="&#xd0;" u2="&#x31;" k="20" />
+    <hkern u1="&#xd0;" u2="&#x2f;" k="30" />
+    <hkern u1="&#xd2;" g2="s.sc" k="10" />
+    <hkern u1="&#xd2;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xd2;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xd2;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xd2;" u2="&#x447;" k="10" />
+    <hkern u1="&#xd2;" u2="&#x442;" k="10" />
+    <hkern u1="&#xd2;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xd2;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xd2;" u2="&#x427;" k="20" />
+    <hkern u1="&#xd2;" u2="&#x37;" k="40" />
+    <hkern u1="&#xd2;" u2="&#x33;" k="40" />
+    <hkern u1="&#xd2;" u2="&#x32;" k="30" />
+    <hkern u1="&#xd2;" u2="&#x31;" k="20" />
+    <hkern u1="&#xd2;" u2="&#x2f;" k="30" />
+    <hkern u1="&#xd3;" g2="s.sc" k="10" />
+    <hkern u1="&#xd3;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xd3;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xd3;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xd3;" u2="&#x447;" k="10" />
+    <hkern u1="&#xd3;" u2="&#x442;" k="10" />
+    <hkern u1="&#xd3;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xd3;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xd3;" u2="&#x427;" k="20" />
+    <hkern u1="&#xd3;" u2="&#x37;" k="40" />
+    <hkern u1="&#xd3;" u2="&#x33;" k="40" />
+    <hkern u1="&#xd3;" u2="&#x32;" k="30" />
+    <hkern u1="&#xd3;" u2="&#x31;" k="20" />
+    <hkern u1="&#xd3;" u2="&#x2f;" k="30" />
+    <hkern u1="&#xd4;" g2="s.sc" k="10" />
+    <hkern u1="&#xd4;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xd4;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xd4;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xd4;" u2="&#x447;" k="10" />
+    <hkern u1="&#xd4;" u2="&#x442;" k="10" />
+    <hkern u1="&#xd4;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xd4;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xd4;" u2="&#x427;" k="20" />
+    <hkern u1="&#xd4;" u2="&#x37;" k="40" />
+    <hkern u1="&#xd4;" u2="&#x33;" k="40" />
+    <hkern u1="&#xd4;" u2="&#x32;" k="30" />
+    <hkern u1="&#xd4;" u2="&#x31;" k="20" />
+    <hkern u1="&#xd4;" u2="&#x2f;" k="30" />
+    <hkern u1="&#xd5;" g2="s.sc" k="10" />
+    <hkern u1="&#xd5;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xd5;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xd5;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xd5;" u2="&#x447;" k="10" />
+    <hkern u1="&#xd5;" u2="&#x442;" k="10" />
+    <hkern u1="&#xd5;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xd5;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xd5;" u2="&#x427;" k="20" />
+    <hkern u1="&#xd5;" u2="&#x37;" k="40" />
+    <hkern u1="&#xd5;" u2="&#x33;" k="40" />
+    <hkern u1="&#xd5;" u2="&#x32;" k="30" />
+    <hkern u1="&#xd5;" u2="&#x31;" k="20" />
+    <hkern u1="&#xd5;" u2="&#x2f;" k="30" />
+    <hkern u1="&#xd6;" g2="s.sc" k="10" />
+    <hkern u1="&#xd6;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xd6;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xd6;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xd6;" u2="&#x447;" k="10" />
+    <hkern u1="&#xd6;" u2="&#x442;" k="10" />
+    <hkern u1="&#xd6;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xd6;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xd6;" u2="&#x427;" k="20" />
+    <hkern u1="&#xd6;" u2="&#x37;" k="40" />
+    <hkern u1="&#xd6;" u2="&#x33;" k="40" />
+    <hkern u1="&#xd6;" u2="&#x32;" k="30" />
+    <hkern u1="&#xd6;" u2="&#x31;" k="20" />
+    <hkern u1="&#xd6;" u2="&#x2f;" k="30" />
+    <hkern u1="&#xd7;" g2="twooldstyle" k="10" />
+    <hkern u1="&#xd7;" g2="two.sc" k="40" />
+    <hkern u1="&#xd7;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xd7;" g2="three.sc" k="20" />
+    <hkern u1="&#xd7;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xd7;" g2="seven.sc" k="40" />
+    <hkern u1="&#xd7;" g2="s.sc" k="20" />
+    <hkern u1="&#xd7;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#xd7;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#xd7;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xd7;" u2="&#x442;" k="50" />
+    <hkern u1="&#xd7;" u2="&#x37;" k="60" />
+    <hkern u1="&#xd8;" g2="s.sc" k="10" />
+    <hkern u1="&#xd8;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xd8;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xd8;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xd8;" u2="&#x447;" k="10" />
+    <hkern u1="&#xd8;" u2="&#x442;" k="10" />
+    <hkern u1="&#xd8;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xd8;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xd8;" u2="&#x427;" k="20" />
+    <hkern u1="&#xd8;" u2="&#x37;" k="40" />
+    <hkern u1="&#xd8;" u2="&#x33;" k="40" />
+    <hkern u1="&#xd8;" u2="&#x32;" k="30" />
+    <hkern u1="&#xd8;" u2="&#x31;" k="20" />
+    <hkern u1="&#xd8;" u2="&#x2f;" k="30" />
+    <hkern u1="&#xd9;" u2="J" k="20" />
+    <hkern u1="&#xd9;" u2="&#x2f;" k="50" />
+    <hkern u1="&#xda;" u2="J" k="20" />
+    <hkern u1="&#xda;" u2="&#x2f;" k="50" />
+    <hkern u1="&#xdb;" u2="J" k="20" />
+    <hkern u1="&#xdb;" u2="&#x2f;" k="50" />
+    <hkern u1="&#xdc;" u2="J" k="20" />
+    <hkern u1="&#xdc;" u2="&#x2f;" k="50" />
+    <hkern u1="&#xdd;" g2="s.sc" k="40" />
+    <hkern u1="&#xdd;" g2="r.sc" k="10" />
+    <hkern u1="&#xdd;" g2="p.sc" k="10" />
+    <hkern u1="&#xdd;" g2="n.sc" k="10" />
+    <hkern u1="&#xdd;" g2="m.sc" k="10" />
+    <hkern u1="&#xdd;" g2="l.sc" k="10" />
+    <hkern u1="&#xdd;" g2="k.sc" k="10" />
+    <hkern u1="&#xdd;" g2="j.sc" k="10" />
+    <hkern u1="&#xdd;" g2="i.sc" k="10" />
+    <hkern u1="&#xdd;" g2="h.sc" k="10" />
+    <hkern u1="&#xdd;" g2="f.sc" k="10" />
+    <hkern u1="&#xdd;" g2="e.sc" k="10" />
+    <hkern u1="&#xdd;" g2="d.sc" k="10" />
+    <hkern u1="&#xdd;" g2="b.sc" k="10" />
+    <hkern u1="&#xdd;" u2="J" k="20" />
+    <hkern u1="&#xdd;" u2="&#x2f;" k="90" />
+    <hkern u1="&#xde;" g2="s.sc" k="10" />
+    <hkern u1="&#xde;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xde;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xde;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xde;" u2="&#x447;" k="10" />
+    <hkern u1="&#xde;" u2="&#x442;" k="10" />
+    <hkern u1="&#xde;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xde;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xde;" u2="&#x427;" k="20" />
+    <hkern u1="&#xde;" u2="&#x37;" k="40" />
+    <hkern u1="&#xde;" u2="&#x33;" k="40" />
+    <hkern u1="&#xde;" u2="&#x32;" k="30" />
+    <hkern u1="&#xde;" u2="&#x31;" k="20" />
+    <hkern u1="&#xde;" u2="&#x2f;" k="30" />
+    <hkern g1="germandbls" g2="afii10097.sc" k="20" />
+    <hkern g1="germandbls" g2="afii10092.sc" k="40" />
+    <hkern g1="germandbls" g2="afii10089.sc" k="20" />
+    <hkern g1="germandbls" u2="&#x44f;" k="20" />
+    <hkern g1="germandbls" u2="&#x44a;" k="20" />
+    <hkern g1="germandbls" u2="&#x447;" k="20" />
+    <hkern g1="germandbls" u2="&#x442;" k="30" />
+    <hkern g1="germandbls" u2="&#x42f;" k="30" />
+    <hkern g1="germandbls" u2="&#x42a;" k="20" />
+    <hkern g1="germandbls" u2="&#x427;" k="30" />
+    <hkern g1="germandbls" u2="&#x39;" k="20" />
+    <hkern g1="germandbls" u2="&#x37;" k="20" />
+    <hkern g1="germandbls" u2="&#x35;" k="10" />
+    <hkern g1="germandbls" u2="&#x33;" k="20" />
+    <hkern g1="germandbls" u2="&#x32;" k="30" />
+    <hkern g1="germandbls" u2="&#x31;" k="20" />
+    <hkern u1="&#xe0;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xe0;" u2="&#x447;" k="20" />
+    <hkern u1="&#xe0;" u2="&#x442;" k="40" />
+    <hkern u1="&#xe1;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xe1;" u2="&#x447;" k="20" />
+    <hkern u1="&#xe1;" u2="&#x442;" k="40" />
+    <hkern u1="&#xe2;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xe2;" u2="&#x447;" k="20" />
+    <hkern u1="&#xe2;" u2="&#x442;" k="40" />
+    <hkern u1="&#xe3;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xe3;" u2="&#x447;" k="20" />
+    <hkern u1="&#xe3;" u2="&#x442;" k="40" />
+    <hkern u1="&#xe4;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xe4;" u2="&#x447;" k="20" />
+    <hkern u1="&#xe4;" u2="&#x442;" k="40" />
+    <hkern u1="&#xe5;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xe5;" u2="&#x447;" k="20" />
+    <hkern u1="&#xe5;" u2="&#x442;" k="40" />
+    <hkern u1="&#xe6;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xe6;" u2="&#x447;" k="10" />
+    <hkern u1="&#xe6;" u2="&#x442;" k="30" />
+    <hkern u1="&#xe7;" u2="&#x447;" k="20" />
+    <hkern u1="&#xe8;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xe8;" u2="&#x447;" k="10" />
+    <hkern u1="&#xe8;" u2="&#x442;" k="30" />
+    <hkern u1="&#xe9;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xe9;" u2="&#x447;" k="10" />
+    <hkern u1="&#xe9;" u2="&#x442;" k="30" />
+    <hkern u1="&#xea;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xea;" u2="&#x447;" k="10" />
+    <hkern u1="&#xea;" u2="&#x442;" k="30" />
+    <hkern u1="&#xeb;" u2="&#x44a;" k="30" />
+    <hkern u1="&#xeb;" u2="&#x447;" k="10" />
+    <hkern u1="&#xeb;" u2="&#x442;" k="30" />
+    <hkern u1="&#xf0;" g2="s.sc" k="10" />
+    <hkern u1="&#xf0;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#xf0;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#xf0;" u2="&#x44a;" k="10" />
+    <hkern u1="&#xf0;" u2="&#x447;" k="10" />
+    <hkern u1="&#xf0;" u2="&#x442;" k="10" />
+    <hkern u1="&#xf0;" u2="&#x42f;" k="20" />
+    <hkern u1="&#xf0;" u2="&#x42a;" k="40" />
+    <hkern u1="&#xf0;" u2="&#x427;" k="20" />
+    <hkern u1="&#xf0;" u2="&#x37;" k="40" />
+    <hkern u1="&#xf0;" u2="&#x33;" k="40" />
+    <hkern u1="&#xf0;" u2="&#x32;" k="30" />
+    <hkern u1="&#xf0;" u2="&#x31;" k="20" />
+    <hkern u1="&#xf0;" u2="&#x2f;" k="30" />
+    <hkern u1="&#xf2;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#xf2;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xf2;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xf2;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xf2;" u2="&#x447;" k="10" />
+    <hkern u1="&#xf2;" u2="&#x442;" k="20" />
+    <hkern u1="&#xf2;" u2="&#x2f;" k="20" />
+    <hkern u1="&#xf3;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#xf3;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xf3;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xf3;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xf3;" u2="&#x447;" k="10" />
+    <hkern u1="&#xf3;" u2="&#x442;" k="20" />
+    <hkern u1="&#xf3;" u2="&#x2f;" k="20" />
+    <hkern u1="&#xf4;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#xf4;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xf4;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xf4;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xf4;" u2="&#x447;" k="10" />
+    <hkern u1="&#xf4;" u2="&#x442;" k="20" />
+    <hkern u1="&#xf4;" u2="&#x2f;" k="20" />
+    <hkern u1="&#xf5;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#xf5;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xf5;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xf5;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xf5;" u2="&#x447;" k="10" />
+    <hkern u1="&#xf5;" u2="&#x442;" k="20" />
+    <hkern u1="&#xf5;" u2="&#x2f;" k="20" />
+    <hkern u1="&#xf6;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#xf6;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xf6;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xf6;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xf6;" u2="&#x447;" k="10" />
+    <hkern u1="&#xf6;" u2="&#x442;" k="20" />
+    <hkern u1="&#xf6;" u2="&#x2f;" k="20" />
+    <hkern u1="&#xf7;" g2="twooldstyle" k="10" />
+    <hkern u1="&#xf7;" g2="two.sc" k="40" />
+    <hkern u1="&#xf7;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#xf7;" g2="three.sc" k="20" />
+    <hkern u1="&#xf7;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xf7;" g2="seven.sc" k="40" />
+    <hkern u1="&#xf7;" g2="s.sc" k="20" />
+    <hkern u1="&#xf7;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#xf7;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#xf7;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#xf7;" u2="&#x442;" k="50" />
+    <hkern u1="&#xf7;" u2="&#x37;" k="60" />
+    <hkern u1="&#xf8;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#xf8;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xf8;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xf8;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xf8;" u2="&#x447;" k="10" />
+    <hkern u1="&#xf8;" u2="&#x442;" k="20" />
+    <hkern u1="&#xf8;" u2="&#x2f;" k="20" />
+    <hkern u1="&#xfd;" u2="&#x2f;" k="80" />
+    <hkern u1="&#xfe;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#xfe;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#xfe;" u2="&#x44f;" k="10" />
+    <hkern u1="&#xfe;" u2="&#x44a;" k="40" />
+    <hkern u1="&#xfe;" u2="&#x447;" k="10" />
+    <hkern u1="&#xfe;" u2="&#x442;" k="20" />
+    <hkern u1="&#xfe;" u2="&#x2f;" k="20" />
+    <hkern u1="&#xff;" u2="&#x2f;" k="80" />
+    <hkern u1="&#x100;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#x100;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x100;" u2="&#x447;" k="50" />
+    <hkern u1="&#x100;" u2="&#x442;" k="60" />
+    <hkern u1="&#x100;" u2="&#x42a;" k="60" />
+    <hkern u1="&#x100;" u2="&#x427;" k="70" />
+    <hkern u1="&#x100;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#x101;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x101;" u2="&#x447;" k="20" />
+    <hkern u1="&#x101;" u2="&#x442;" k="40" />
+    <hkern u1="&#x102;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#x102;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x102;" u2="&#x447;" k="50" />
+    <hkern u1="&#x102;" u2="&#x442;" k="60" />
+    <hkern u1="&#x102;" u2="&#x42a;" k="60" />
+    <hkern u1="&#x102;" u2="&#x427;" k="70" />
+    <hkern u1="&#x102;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#x103;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x103;" u2="&#x447;" k="20" />
+    <hkern u1="&#x103;" u2="&#x442;" k="40" />
+    <hkern u1="&#x104;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#x104;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x104;" u2="&#x447;" k="50" />
+    <hkern u1="&#x104;" u2="&#x442;" k="60" />
+    <hkern u1="&#x104;" u2="&#x42a;" k="60" />
+    <hkern u1="&#x104;" u2="&#x427;" k="70" />
+    <hkern u1="&#x104;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#x105;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x105;" u2="&#x447;" k="20" />
+    <hkern u1="&#x105;" u2="&#x442;" k="40" />
+    <hkern u1="&#x106;" g2="s.sc" k="20" />
+    <hkern u1="&#x106;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x106;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x106;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x106;" u2="&#x447;" k="20" />
+    <hkern u1="&#x107;" u2="&#x447;" k="20" />
+    <hkern u1="&#x108;" g2="s.sc" k="20" />
+    <hkern u1="&#x108;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x108;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x108;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x108;" u2="&#x447;" k="20" />
+    <hkern u1="&#x109;" u2="&#x447;" k="20" />
+    <hkern u1="&#x10a;" g2="s.sc" k="20" />
+    <hkern u1="&#x10a;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x10a;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x10a;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x10a;" u2="&#x447;" k="20" />
+    <hkern u1="&#x10b;" u2="&#x447;" k="20" />
+    <hkern u1="&#x10c;" g2="s.sc" k="20" />
+    <hkern u1="&#x10c;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x10c;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x10c;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x10c;" u2="&#x447;" k="20" />
+    <hkern u1="&#x10d;" u2="&#x447;" k="20" />
+    <hkern u1="&#x10e;" g2="s.sc" k="10" />
+    <hkern u1="&#x10e;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x10e;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x10e;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x10e;" u2="&#x447;" k="10" />
+    <hkern u1="&#x10e;" u2="&#x442;" k="10" />
+    <hkern u1="&#x10e;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x10e;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x10e;" u2="&#x427;" k="20" />
+    <hkern u1="&#x10e;" u2="&#x37;" k="40" />
+    <hkern u1="&#x10e;" u2="&#x33;" k="40" />
+    <hkern u1="&#x10e;" u2="&#x32;" k="30" />
+    <hkern u1="&#x10e;" u2="&#x31;" k="20" />
+    <hkern u1="&#x10e;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x10f;" u2="&#x2f;" k="40" />
+    <hkern u1="&#x110;" g2="s.sc" k="10" />
+    <hkern u1="&#x110;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x110;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x110;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x110;" u2="&#x447;" k="10" />
+    <hkern u1="&#x110;" u2="&#x442;" k="10" />
+    <hkern u1="&#x110;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x110;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x110;" u2="&#x427;" k="20" />
+    <hkern u1="&#x110;" u2="&#x37;" k="40" />
+    <hkern u1="&#x110;" u2="&#x33;" k="40" />
+    <hkern u1="&#x110;" u2="&#x32;" k="30" />
+    <hkern u1="&#x110;" u2="&#x31;" k="20" />
+    <hkern u1="&#x110;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x112;" g2="s.sc" k="10" />
+    <hkern u1="&#x112;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x112;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x112;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x112;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x112;" u2="&#x447;" k="20" />
+    <hkern u1="&#x112;" u2="&#x442;" k="20" />
+    <hkern u1="&#x112;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x112;" u2="&#x427;" k="20" />
+    <hkern u1="&#x113;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x113;" u2="&#x447;" k="10" />
+    <hkern u1="&#x113;" u2="&#x442;" k="30" />
+    <hkern u1="&#x114;" g2="s.sc" k="10" />
+    <hkern u1="&#x114;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x114;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x114;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x114;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x114;" u2="&#x447;" k="20" />
+    <hkern u1="&#x114;" u2="&#x442;" k="20" />
+    <hkern u1="&#x114;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x114;" u2="&#x427;" k="20" />
+    <hkern u1="&#x115;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x115;" u2="&#x447;" k="10" />
+    <hkern u1="&#x115;" u2="&#x442;" k="30" />
+    <hkern u1="&#x116;" g2="s.sc" k="10" />
+    <hkern u1="&#x116;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x116;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x116;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x116;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x116;" u2="&#x447;" k="20" />
+    <hkern u1="&#x116;" u2="&#x442;" k="20" />
+    <hkern u1="&#x116;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x116;" u2="&#x427;" k="20" />
+    <hkern u1="&#x117;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x117;" u2="&#x447;" k="10" />
+    <hkern u1="&#x117;" u2="&#x442;" k="30" />
+    <hkern u1="&#x118;" g2="s.sc" k="10" />
+    <hkern u1="&#x118;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x118;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x118;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x118;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x118;" u2="&#x447;" k="20" />
+    <hkern u1="&#x118;" u2="&#x442;" k="20" />
+    <hkern u1="&#x118;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x118;" u2="&#x427;" k="20" />
+    <hkern u1="&#x119;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x119;" u2="&#x447;" k="10" />
+    <hkern u1="&#x119;" u2="&#x442;" k="30" />
+    <hkern u1="&#x11a;" g2="s.sc" k="10" />
+    <hkern u1="&#x11a;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x11a;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x11a;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x11a;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x11a;" u2="&#x447;" k="20" />
+    <hkern u1="&#x11a;" u2="&#x442;" k="20" />
+    <hkern u1="&#x11a;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x11a;" u2="&#x427;" k="20" />
+    <hkern u1="&#x11b;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x11b;" u2="&#x447;" k="10" />
+    <hkern u1="&#x11b;" u2="&#x442;" k="30" />
+    <hkern u1="&#x136;" g2="s.sc" k="20" />
+    <hkern u1="&#x136;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x136;" g2="afii10092.sc" k="50" />
+    <hkern u1="&#x136;" g2="afii10089.sc" k="50" />
+    <hkern u1="&#x136;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x136;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x136;" u2="&#x447;" k="50" />
+    <hkern u1="&#x136;" u2="&#x442;" k="40" />
+    <hkern u1="&#x136;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x136;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x136;" u2="&#x427;" k="30" />
+    <hkern u1="&#x137;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x137;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x137;" u2="&#x447;" k="30" />
+    <hkern u1="&#x137;" u2="&#x442;" k="30" />
+    <hkern u1="&#x138;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x138;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x138;" u2="&#x447;" k="30" />
+    <hkern u1="&#x138;" u2="&#x442;" k="30" />
+    <hkern u1="&#x13e;" u2="&#x2f;" k="40" />
+    <hkern u1="&#x14c;" g2="s.sc" k="10" />
+    <hkern u1="&#x14c;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x14c;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x14c;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x14c;" u2="&#x447;" k="10" />
+    <hkern u1="&#x14c;" u2="&#x442;" k="10" />
+    <hkern u1="&#x14c;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x14c;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x14c;" u2="&#x427;" k="20" />
+    <hkern u1="&#x14c;" u2="&#x37;" k="40" />
+    <hkern u1="&#x14c;" u2="&#x33;" k="40" />
+    <hkern u1="&#x14c;" u2="&#x32;" k="30" />
+    <hkern u1="&#x14c;" u2="&#x31;" k="20" />
+    <hkern u1="&#x14c;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x14d;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x14d;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x14d;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x14d;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x14d;" u2="&#x447;" k="10" />
+    <hkern u1="&#x14d;" u2="&#x442;" k="20" />
+    <hkern u1="&#x14d;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x14e;" g2="s.sc" k="10" />
+    <hkern u1="&#x14e;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x14e;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x14e;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x14e;" u2="&#x447;" k="10" />
+    <hkern u1="&#x14e;" u2="&#x442;" k="10" />
+    <hkern u1="&#x14e;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x14e;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x14e;" u2="&#x427;" k="20" />
+    <hkern u1="&#x14e;" u2="&#x37;" k="40" />
+    <hkern u1="&#x14e;" u2="&#x33;" k="40" />
+    <hkern u1="&#x14e;" u2="&#x32;" k="30" />
+    <hkern u1="&#x14e;" u2="&#x31;" k="20" />
+    <hkern u1="&#x14e;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x14f;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x14f;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x14f;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x14f;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x14f;" u2="&#x447;" k="10" />
+    <hkern u1="&#x14f;" u2="&#x442;" k="20" />
+    <hkern u1="&#x14f;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x150;" g2="s.sc" k="10" />
+    <hkern u1="&#x150;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x150;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x150;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x150;" u2="&#x447;" k="10" />
+    <hkern u1="&#x150;" u2="&#x442;" k="10" />
+    <hkern u1="&#x150;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x150;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x150;" u2="&#x427;" k="20" />
+    <hkern u1="&#x150;" u2="&#x37;" k="40" />
+    <hkern u1="&#x150;" u2="&#x33;" k="40" />
+    <hkern u1="&#x150;" u2="&#x32;" k="30" />
+    <hkern u1="&#x150;" u2="&#x31;" k="20" />
+    <hkern u1="&#x150;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x151;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x151;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x151;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x151;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x151;" u2="&#x447;" k="10" />
+    <hkern u1="&#x151;" u2="&#x442;" k="20" />
+    <hkern u1="&#x151;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x152;" g2="s.sc" k="10" />
+    <hkern u1="&#x152;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x152;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x152;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x152;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x152;" u2="&#x447;" k="20" />
+    <hkern u1="&#x152;" u2="&#x442;" k="20" />
+    <hkern u1="&#x152;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x152;" u2="&#x427;" k="20" />
+    <hkern u1="&#x153;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x153;" u2="&#x447;" k="10" />
+    <hkern u1="&#x153;" u2="&#x442;" k="30" />
+    <hkern u1="&#x154;" g2="s.sc" k="20" />
+    <hkern u1="&#x155;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x156;" g2="s.sc" k="20" />
+    <hkern u1="&#x157;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x158;" g2="s.sc" k="20" />
+    <hkern u1="&#x159;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x15a;" g2="s.sc" k="10" />
+    <hkern u1="&#x15c;" g2="s.sc" k="10" />
+    <hkern u1="&#x15e;" g2="s.sc" k="10" />
+    <hkern u1="&#x160;" g2="s.sc" k="10" />
+    <hkern u1="&#x162;" g2="s.sc" k="30" />
+    <hkern u1="&#x162;" g2="r.sc" k="20" />
+    <hkern u1="&#x162;" g2="p.sc" k="20" />
+    <hkern u1="&#x162;" g2="n.sc" k="20" />
+    <hkern u1="&#x162;" g2="m.sc" k="20" />
+    <hkern u1="&#x162;" g2="l.sc" k="20" />
+    <hkern u1="&#x162;" g2="k.sc" k="20" />
+    <hkern u1="&#x162;" g2="i.sc" k="20" />
+    <hkern u1="&#x162;" g2="h.sc" k="20" />
+    <hkern u1="&#x162;" g2="f.sc" k="20" />
+    <hkern u1="&#x162;" g2="e.sc" k="20" />
+    <hkern u1="&#x162;" g2="d.sc" k="20" />
+    <hkern u1="&#x162;" g2="b.sc" k="40" />
+    <hkern u1="&#x162;" g2="afii10097.sc" k="70" />
+    <hkern u1="&#x162;" g2="afii10096.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10094.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10093.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#x162;" g2="afii10091.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10090.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10088.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10082.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10081.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10079.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10078.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10076.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10074.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10070.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10068.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10067.sc" k="20" />
+    <hkern u1="&#x162;" g2="afii10066.sc" k="20" />
+    <hkern u1="&#x162;" u2="&#x44f;" k="40" />
+    <hkern u1="&#x162;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x162;" u2="&#x447;" k="40" />
+    <hkern u1="&#x162;" u2="&#x442;" k="60" />
+    <hkern u1="&#x162;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x162;" u2="J" k="20" />
+    <hkern u1="&#x162;" u2="&#x2f;" k="100" />
+    <hkern u1="&#x163;" u2="&#x447;" k="20" />
+    <hkern u1="&#x164;" g2="s.sc" k="30" />
+    <hkern u1="&#x164;" g2="r.sc" k="20" />
+    <hkern u1="&#x164;" g2="p.sc" k="20" />
+    <hkern u1="&#x164;" g2="n.sc" k="20" />
+    <hkern u1="&#x164;" g2="m.sc" k="20" />
+    <hkern u1="&#x164;" g2="l.sc" k="20" />
+    <hkern u1="&#x164;" g2="k.sc" k="20" />
+    <hkern u1="&#x164;" g2="i.sc" k="20" />
+    <hkern u1="&#x164;" g2="h.sc" k="20" />
+    <hkern u1="&#x164;" g2="f.sc" k="20" />
+    <hkern u1="&#x164;" g2="e.sc" k="20" />
+    <hkern u1="&#x164;" g2="d.sc" k="20" />
+    <hkern u1="&#x164;" g2="b.sc" k="40" />
+    <hkern u1="&#x164;" g2="afii10097.sc" k="70" />
+    <hkern u1="&#x164;" g2="afii10096.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10094.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10093.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#x164;" g2="afii10091.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10090.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10088.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10082.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10081.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10079.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10078.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10076.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10074.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10070.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10068.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10067.sc" k="20" />
+    <hkern u1="&#x164;" g2="afii10066.sc" k="20" />
+    <hkern u1="&#x164;" u2="&#x44f;" k="40" />
+    <hkern u1="&#x164;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x164;" u2="&#x447;" k="40" />
+    <hkern u1="&#x164;" u2="&#x442;" k="60" />
+    <hkern u1="&#x164;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x164;" u2="J" k="20" />
+    <hkern u1="&#x164;" u2="&#x2f;" k="100" />
+    <hkern u1="&#x165;" u2="&#x447;" k="20" />
+    <hkern u1="&#x168;" u2="J" k="20" />
+    <hkern u1="&#x168;" u2="&#x2f;" k="50" />
+    <hkern u1="&#x16a;" u2="J" k="20" />
+    <hkern u1="&#x16a;" u2="&#x2f;" k="50" />
+    <hkern u1="&#x16c;" u2="J" k="20" />
+    <hkern u1="&#x16c;" u2="&#x2f;" k="50" />
+    <hkern u1="&#x16e;" u2="J" k="20" />
+    <hkern u1="&#x16e;" u2="&#x2f;" k="50" />
+    <hkern u1="&#x170;" u2="J" k="20" />
+    <hkern u1="&#x170;" u2="&#x2f;" k="50" />
+    <hkern u1="&#x172;" u2="J" k="20" />
+    <hkern u1="&#x172;" u2="&#x2f;" k="50" />
+    <hkern u1="&#x174;" g2="s.sc" k="20" />
+    <hkern u1="&#x174;" u2="J" k="20" />
+    <hkern u1="&#x174;" u2="&#x2f;" k="80" />
+    <hkern u1="&#x175;" u2="&#x2f;" k="70" />
+    <hkern u1="&#x176;" g2="s.sc" k="40" />
+    <hkern u1="&#x176;" g2="r.sc" k="10" />
+    <hkern u1="&#x176;" g2="p.sc" k="10" />
+    <hkern u1="&#x176;" g2="n.sc" k="10" />
+    <hkern u1="&#x176;" g2="m.sc" k="10" />
+    <hkern u1="&#x176;" g2="l.sc" k="10" />
+    <hkern u1="&#x176;" g2="k.sc" k="10" />
+    <hkern u1="&#x176;" g2="j.sc" k="10" />
+    <hkern u1="&#x176;" g2="i.sc" k="10" />
+    <hkern u1="&#x176;" g2="h.sc" k="10" />
+    <hkern u1="&#x176;" g2="f.sc" k="10" />
+    <hkern u1="&#x176;" g2="e.sc" k="10" />
+    <hkern u1="&#x176;" g2="d.sc" k="10" />
+    <hkern u1="&#x176;" g2="b.sc" k="10" />
+    <hkern u1="&#x176;" u2="J" k="20" />
+    <hkern u1="&#x176;" u2="&#x2f;" k="90" />
+    <hkern u1="&#x177;" u2="&#x2f;" k="80" />
+    <hkern u1="&#x178;" g2="s.sc" k="40" />
+    <hkern u1="&#x178;" g2="r.sc" k="10" />
+    <hkern u1="&#x178;" g2="p.sc" k="10" />
+    <hkern u1="&#x178;" g2="n.sc" k="10" />
+    <hkern u1="&#x178;" g2="m.sc" k="10" />
+    <hkern u1="&#x178;" g2="l.sc" k="10" />
+    <hkern u1="&#x178;" g2="k.sc" k="10" />
+    <hkern u1="&#x178;" g2="j.sc" k="10" />
+    <hkern u1="&#x178;" g2="i.sc" k="10" />
+    <hkern u1="&#x178;" g2="h.sc" k="10" />
+    <hkern u1="&#x178;" g2="f.sc" k="10" />
+    <hkern u1="&#x178;" g2="e.sc" k="10" />
+    <hkern u1="&#x178;" g2="d.sc" k="10" />
+    <hkern u1="&#x178;" g2="b.sc" k="10" />
+    <hkern u1="&#x178;" u2="J" k="20" />
+    <hkern u1="&#x178;" u2="&#x2f;" k="90" />
+    <hkern u1="&#x179;" g2="s.sc" k="10" />
+    <hkern u1="&#x17b;" g2="s.sc" k="10" />
+    <hkern u1="&#x17d;" g2="s.sc" k="10" />
+    <hkern u1="&#x17f;" u2="&#x2f;" k="40" />
+    <hkern u1="&#x200;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#x200;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x200;" u2="&#x447;" k="50" />
+    <hkern u1="&#x200;" u2="&#x442;" k="60" />
+    <hkern u1="&#x200;" u2="&#x42a;" k="60" />
+    <hkern u1="&#x200;" u2="&#x427;" k="70" />
+    <hkern u1="&#x200;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#x201;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x201;" u2="&#x447;" k="20" />
+    <hkern u1="&#x201;" u2="&#x442;" k="40" />
+    <hkern u1="&#x202;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#x202;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x202;" u2="&#x447;" k="50" />
+    <hkern u1="&#x202;" u2="&#x442;" k="60" />
+    <hkern u1="&#x202;" u2="&#x42a;" k="60" />
+    <hkern u1="&#x202;" u2="&#x427;" k="70" />
+    <hkern u1="&#x202;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#x203;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x203;" u2="&#x447;" k="20" />
+    <hkern u1="&#x203;" u2="&#x442;" k="40" />
+    <hkern u1="&#x204;" g2="s.sc" k="10" />
+    <hkern u1="&#x204;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x204;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x204;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x204;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x204;" u2="&#x447;" k="20" />
+    <hkern u1="&#x204;" u2="&#x442;" k="20" />
+    <hkern u1="&#x204;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x204;" u2="&#x427;" k="20" />
+    <hkern u1="&#x205;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x205;" u2="&#x447;" k="10" />
+    <hkern u1="&#x205;" u2="&#x442;" k="30" />
+    <hkern u1="&#x206;" g2="s.sc" k="10" />
+    <hkern u1="&#x206;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x206;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x206;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x206;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x206;" u2="&#x447;" k="20" />
+    <hkern u1="&#x206;" u2="&#x442;" k="20" />
+    <hkern u1="&#x206;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x206;" u2="&#x427;" k="20" />
+    <hkern u1="&#x207;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x207;" u2="&#x447;" k="10" />
+    <hkern u1="&#x207;" u2="&#x442;" k="30" />
+    <hkern u1="&#x20c;" g2="s.sc" k="10" />
+    <hkern u1="&#x20c;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x20c;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x20c;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x20c;" u2="&#x447;" k="10" />
+    <hkern u1="&#x20c;" u2="&#x442;" k="10" />
+    <hkern u1="&#x20c;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x20c;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x20c;" u2="&#x427;" k="20" />
+    <hkern u1="&#x20c;" u2="&#x37;" k="40" />
+    <hkern u1="&#x20c;" u2="&#x33;" k="40" />
+    <hkern u1="&#x20c;" u2="&#x32;" k="30" />
+    <hkern u1="&#x20c;" u2="&#x31;" k="20" />
+    <hkern u1="&#x20c;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x20d;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x20d;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x20d;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x20d;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x20d;" u2="&#x447;" k="10" />
+    <hkern u1="&#x20d;" u2="&#x442;" k="20" />
+    <hkern u1="&#x20d;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x20e;" g2="s.sc" k="10" />
+    <hkern u1="&#x20e;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x20e;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x20e;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x20e;" u2="&#x447;" k="10" />
+    <hkern u1="&#x20e;" u2="&#x442;" k="10" />
+    <hkern u1="&#x20e;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x20e;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x20e;" u2="&#x427;" k="20" />
+    <hkern u1="&#x20e;" u2="&#x37;" k="40" />
+    <hkern u1="&#x20e;" u2="&#x33;" k="40" />
+    <hkern u1="&#x20e;" u2="&#x32;" k="30" />
+    <hkern u1="&#x20e;" u2="&#x31;" k="20" />
+    <hkern u1="&#x20e;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x20f;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x20f;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x20f;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x20f;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x20f;" u2="&#x447;" k="10" />
+    <hkern u1="&#x20f;" u2="&#x442;" k="20" />
+    <hkern u1="&#x20f;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x210;" g2="s.sc" k="20" />
+    <hkern u1="&#x211;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x212;" g2="s.sc" k="20" />
+    <hkern u1="&#x213;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x214;" u2="J" k="20" />
+    <hkern u1="&#x214;" u2="&#x2f;" k="50" />
+    <hkern u1="&#x216;" u2="J" k="20" />
+    <hkern u1="&#x216;" u2="&#x2f;" k="50" />
+    <hkern u1="&#x218;" g2="s.sc" k="10" />
+    <hkern u1="&#x21a;" g2="s.sc" k="30" />
+    <hkern u1="&#x21a;" g2="r.sc" k="20" />
+    <hkern u1="&#x21a;" g2="p.sc" k="20" />
+    <hkern u1="&#x21a;" g2="n.sc" k="20" />
+    <hkern u1="&#x21a;" g2="m.sc" k="20" />
+    <hkern u1="&#x21a;" g2="l.sc" k="20" />
+    <hkern u1="&#x21a;" g2="k.sc" k="20" />
+    <hkern u1="&#x21a;" g2="i.sc" k="20" />
+    <hkern u1="&#x21a;" g2="h.sc" k="20" />
+    <hkern u1="&#x21a;" g2="f.sc" k="20" />
+    <hkern u1="&#x21a;" g2="e.sc" k="20" />
+    <hkern u1="&#x21a;" g2="d.sc" k="20" />
+    <hkern u1="&#x21a;" g2="b.sc" k="40" />
+    <hkern u1="&#x21a;" g2="afii10097.sc" k="70" />
+    <hkern u1="&#x21a;" g2="afii10096.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10094.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10093.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#x21a;" g2="afii10091.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10090.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10088.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10082.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10081.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10079.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10078.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10076.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10074.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10070.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10068.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10067.sc" k="20" />
+    <hkern u1="&#x21a;" g2="afii10066.sc" k="20" />
+    <hkern u1="&#x21a;" u2="&#x44f;" k="40" />
+    <hkern u1="&#x21a;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x21a;" u2="&#x447;" k="40" />
+    <hkern u1="&#x21a;" u2="&#x442;" k="60" />
+    <hkern u1="&#x21a;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x21a;" u2="J" k="20" />
+    <hkern u1="&#x21a;" u2="&#x2f;" k="100" />
+    <hkern u1="&#x21b;" u2="&#x447;" k="20" />
+    <hkern u1="&#x2c9;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2c9;" g2="three.sc" k="10" />
+    <hkern u1="&#x2c9;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#x2c9;" g2="nine.sc" k="30" />
+    <hkern u1="&#x2c9;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#x2c9;" g2="four.sc" k="100" />
+    <hkern u1="&#x2c9;" g2="eight.sc" k="30" />
+    <hkern u1="&#x2c9;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#x2c9;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x2c9;" u2="&#x34;" k="70" />
+    <hkern u1="&#x401;" g2="s.sc" k="10" />
+    <hkern u1="&#x401;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x401;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x401;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x401;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x401;" u2="&#x447;" k="20" />
+    <hkern u1="&#x401;" u2="&#x442;" k="20" />
+    <hkern u1="&#x401;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x401;" u2="&#x427;" k="20" />
+    <hkern u1="&#x403;" g2="s.sc" k="30" />
+    <hkern u1="&#x403;" g2="r.sc" k="20" />
+    <hkern u1="&#x403;" g2="p.sc" k="20" />
+    <hkern u1="&#x403;" g2="n.sc" k="20" />
+    <hkern u1="&#x403;" g2="m.sc" k="20" />
+    <hkern u1="&#x403;" g2="l.sc" k="20" />
+    <hkern u1="&#x403;" g2="k.sc" k="20" />
+    <hkern u1="&#x403;" g2="i.sc" k="20" />
+    <hkern u1="&#x403;" g2="h.sc" k="20" />
+    <hkern u1="&#x403;" g2="f.sc" k="20" />
+    <hkern u1="&#x403;" g2="e.sc" k="20" />
+    <hkern u1="&#x403;" g2="d.sc" k="20" />
+    <hkern u1="&#x403;" g2="b.sc" k="40" />
+    <hkern u1="&#x403;" g2="afii10097.sc" k="70" />
+    <hkern u1="&#x403;" g2="afii10096.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10094.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10093.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#x403;" g2="afii10091.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10090.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10088.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10082.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10081.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10079.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10078.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10076.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10074.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10070.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10068.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10067.sc" k="20" />
+    <hkern u1="&#x403;" g2="afii10066.sc" k="20" />
+    <hkern u1="&#x403;" u2="&#x44f;" k="40" />
+    <hkern u1="&#x403;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x403;" u2="&#x447;" k="40" />
+    <hkern u1="&#x403;" u2="&#x442;" k="60" />
+    <hkern u1="&#x403;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x403;" u2="J" k="20" />
+    <hkern u1="&#x403;" u2="&#x2f;" k="100" />
+    <hkern u1="&#x404;" g2="s.sc" k="20" />
+    <hkern u1="&#x404;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x404;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x404;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x404;" u2="&#x447;" k="20" />
+    <hkern u1="&#x405;" g2="s.sc" k="10" />
+    <hkern u1="&#x409;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x409;" g2="afii10092.sc" k="60" />
+    <hkern u1="&#x409;" g2="afii10089.sc" k="30" />
+    <hkern u1="&#x409;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x409;" u2="&#x442;" k="40" />
+    <hkern u1="&#x409;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x409;" u2="&#x42a;" k="70" />
+    <hkern u1="&#x409;" u2="&#x427;" k="50" />
+    <hkern u1="&#x40a;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x40a;" g2="afii10092.sc" k="60" />
+    <hkern u1="&#x40a;" g2="afii10089.sc" k="30" />
+    <hkern u1="&#x40a;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x40a;" u2="&#x442;" k="40" />
+    <hkern u1="&#x40a;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x40a;" u2="&#x42a;" k="70" />
+    <hkern u1="&#x40a;" u2="&#x427;" k="50" />
+    <hkern u1="&#x40c;" g2="s.sc" k="20" />
+    <hkern u1="&#x40c;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x40c;" g2="afii10092.sc" k="50" />
+    <hkern u1="&#x40c;" g2="afii10089.sc" k="50" />
+    <hkern u1="&#x40c;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x40c;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x40c;" u2="&#x447;" k="50" />
+    <hkern u1="&#x40c;" u2="&#x442;" k="40" />
+    <hkern u1="&#x40c;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x40c;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x40c;" u2="&#x427;" k="30" />
+    <hkern u1="&#x40e;" g2="s.sc" k="30" />
+    <hkern u1="&#x40e;" g2="r.sc" k="10" />
+    <hkern u1="&#x40e;" g2="p.sc" k="10" />
+    <hkern u1="&#x40e;" g2="n.sc" k="10" />
+    <hkern u1="&#x40e;" g2="m.sc" k="10" />
+    <hkern u1="&#x40e;" g2="l.sc" k="10" />
+    <hkern u1="&#x40e;" g2="k.sc" k="10" />
+    <hkern u1="&#x40e;" g2="j.sc" k="10" />
+    <hkern u1="&#x40e;" g2="i.sc" k="10" />
+    <hkern u1="&#x40e;" g2="h.sc" k="10" />
+    <hkern u1="&#x40e;" g2="f.sc" k="10" />
+    <hkern u1="&#x40e;" g2="e.sc" k="10" />
+    <hkern u1="&#x40e;" g2="d.sc" k="10" />
+    <hkern u1="&#x40e;" g2="b.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x40e;" g2="afii10096.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10094.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10093.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10091.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10090.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10088.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10082.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10081.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10079.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10078.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10076.sc" k="10" />
+    <hkern u1="&#x40e;" g2="afii10074.sc" k="10" />
+    <hkern u1="&#x40e;" u2="&#x44f;" k="50" />
+    <hkern u1="&#x40e;" u2="&#x44a;" k="20" />
+    <hkern u1="&#x40e;" u2="&#x447;" k="30" />
+    <hkern u1="&#x40e;" u2="&#x442;" k="30" />
+    <hkern u1="&#x40e;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x40e;" u2="J" k="20" />
+    <hkern u1="&#x40e;" u2="&#x2f;" k="100" />
+    <hkern u1="&#x410;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#x410;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x410;" u2="&#x447;" k="50" />
+    <hkern u1="&#x410;" u2="&#x442;" k="60" />
+    <hkern u1="&#x410;" u2="&#x42a;" k="60" />
+    <hkern u1="&#x410;" u2="&#x427;" k="70" />
+    <hkern u1="&#x410;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#x411;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x411;" g2="afii10092.sc" k="40" />
+    <hkern u1="&#x411;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x411;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x411;" u2="&#x44a;" k="20" />
+    <hkern u1="&#x411;" u2="&#x447;" k="20" />
+    <hkern u1="&#x411;" u2="&#x442;" k="30" />
+    <hkern u1="&#x411;" u2="&#x42f;" k="30" />
+    <hkern u1="&#x411;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x411;" u2="&#x427;" k="30" />
+    <hkern u1="&#x411;" u2="&#x39;" k="20" />
+    <hkern u1="&#x411;" u2="&#x37;" k="20" />
+    <hkern u1="&#x411;" u2="&#x35;" k="10" />
+    <hkern u1="&#x411;" u2="&#x33;" k="20" />
+    <hkern u1="&#x411;" u2="&#x32;" k="30" />
+    <hkern u1="&#x411;" u2="&#x31;" k="20" />
+    <hkern u1="&#x412;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x412;" g2="afii10092.sc" k="40" />
+    <hkern u1="&#x412;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x412;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x412;" u2="&#x44a;" k="20" />
+    <hkern u1="&#x412;" u2="&#x447;" k="20" />
+    <hkern u1="&#x412;" u2="&#x442;" k="30" />
+    <hkern u1="&#x412;" u2="&#x42f;" k="30" />
+    <hkern u1="&#x412;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x412;" u2="&#x427;" k="30" />
+    <hkern u1="&#x412;" u2="&#x39;" k="20" />
+    <hkern u1="&#x412;" u2="&#x37;" k="20" />
+    <hkern u1="&#x412;" u2="&#x35;" k="10" />
+    <hkern u1="&#x412;" u2="&#x33;" k="20" />
+    <hkern u1="&#x412;" u2="&#x32;" k="30" />
+    <hkern u1="&#x412;" u2="&#x31;" k="20" />
+    <hkern u1="&#x413;" g2="s.sc" k="30" />
+    <hkern u1="&#x413;" g2="r.sc" k="20" />
+    <hkern u1="&#x413;" g2="p.sc" k="20" />
+    <hkern u1="&#x413;" g2="n.sc" k="20" />
+    <hkern u1="&#x413;" g2="m.sc" k="20" />
+    <hkern u1="&#x413;" g2="l.sc" k="20" />
+    <hkern u1="&#x413;" g2="k.sc" k="20" />
+    <hkern u1="&#x413;" g2="i.sc" k="20" />
+    <hkern u1="&#x413;" g2="h.sc" k="20" />
+    <hkern u1="&#x413;" g2="f.sc" k="20" />
+    <hkern u1="&#x413;" g2="e.sc" k="20" />
+    <hkern u1="&#x413;" g2="d.sc" k="20" />
+    <hkern u1="&#x413;" g2="b.sc" k="40" />
+    <hkern u1="&#x413;" g2="afii10097.sc" k="70" />
+    <hkern u1="&#x413;" g2="afii10096.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10094.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10093.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#x413;" g2="afii10091.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10090.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10088.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10082.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10081.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10079.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10078.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10076.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10074.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10070.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10068.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10067.sc" k="20" />
+    <hkern u1="&#x413;" g2="afii10066.sc" k="20" />
+    <hkern u1="&#x413;" u2="&#x44f;" k="40" />
+    <hkern u1="&#x413;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x413;" u2="&#x447;" k="40" />
+    <hkern u1="&#x413;" u2="&#x442;" k="60" />
+    <hkern u1="&#x413;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x413;" u2="J" k="20" />
+    <hkern u1="&#x413;" u2="&#x2f;" k="100" />
+    <hkern u1="&#x414;" g2="afii10092.sc" k="30" />
+    <hkern u1="&#x414;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x414;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x414;" u2="&#x447;" k="30" />
+    <hkern u1="&#x414;" u2="&#x442;" k="20" />
+    <hkern u1="&#x414;" u2="&#x42a;" k="30" />
+    <hkern u1="&#x414;" u2="&#x427;" k="40" />
+    <hkern u1="&#x415;" g2="s.sc" k="10" />
+    <hkern u1="&#x415;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x415;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x415;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x415;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x415;" u2="&#x447;" k="20" />
+    <hkern u1="&#x415;" u2="&#x442;" k="20" />
+    <hkern u1="&#x415;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x415;" u2="&#x427;" k="20" />
+    <hkern u1="&#x416;" g2="s.sc" k="20" />
+    <hkern u1="&#x416;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x416;" g2="afii10092.sc" k="50" />
+    <hkern u1="&#x416;" g2="afii10089.sc" k="50" />
+    <hkern u1="&#x416;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x416;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x416;" u2="&#x447;" k="50" />
+    <hkern u1="&#x416;" u2="&#x442;" k="40" />
+    <hkern u1="&#x416;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x416;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x416;" u2="&#x427;" k="30" />
+    <hkern u1="&#x417;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x417;" g2="afii10092.sc" k="40" />
+    <hkern u1="&#x417;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x417;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x417;" u2="&#x44a;" k="20" />
+    <hkern u1="&#x417;" u2="&#x447;" k="20" />
+    <hkern u1="&#x417;" u2="&#x442;" k="30" />
+    <hkern u1="&#x417;" u2="&#x42f;" k="30" />
+    <hkern u1="&#x417;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x417;" u2="&#x427;" k="30" />
+    <hkern u1="&#x417;" u2="&#x39;" k="20" />
+    <hkern u1="&#x417;" u2="&#x37;" k="20" />
+    <hkern u1="&#x417;" u2="&#x35;" k="10" />
+    <hkern u1="&#x417;" u2="&#x33;" k="20" />
+    <hkern u1="&#x417;" u2="&#x32;" k="30" />
+    <hkern u1="&#x417;" u2="&#x31;" k="20" />
+    <hkern u1="&#x41a;" g2="s.sc" k="20" />
+    <hkern u1="&#x41a;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x41a;" g2="afii10092.sc" k="50" />
+    <hkern u1="&#x41a;" g2="afii10089.sc" k="50" />
+    <hkern u1="&#x41a;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x41a;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x41a;" u2="&#x447;" k="50" />
+    <hkern u1="&#x41a;" u2="&#x442;" k="40" />
+    <hkern u1="&#x41a;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x41a;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x41a;" u2="&#x427;" k="30" />
+    <hkern u1="&#x41e;" g2="s.sc" k="10" />
+    <hkern u1="&#x41e;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x41e;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x41e;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x41e;" u2="&#x447;" k="10" />
+    <hkern u1="&#x41e;" u2="&#x442;" k="10" />
+    <hkern u1="&#x41e;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x41e;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x41e;" u2="&#x427;" k="20" />
+    <hkern u1="&#x41e;" u2="&#x37;" k="40" />
+    <hkern u1="&#x41e;" u2="&#x33;" k="40" />
+    <hkern u1="&#x41e;" u2="&#x32;" k="30" />
+    <hkern u1="&#x41e;" u2="&#x31;" k="20" />
+    <hkern u1="&#x41e;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x420;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x420;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x420;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x420;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x420;" u2="&#x447;" k="20" />
+    <hkern u1="&#x420;" u2="&#x42f;" k="30" />
+    <hkern u1="&#x420;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x420;" u2="&#x427;" k="10" />
+    <hkern u1="&#x420;" u2="J" k="30" />
+    <hkern u1="&#x420;" u2="&#x2f;" k="70" />
+    <hkern u1="&#x421;" g2="s.sc" k="20" />
+    <hkern u1="&#x421;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x421;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x421;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x421;" u2="&#x447;" k="20" />
+    <hkern u1="&#x422;" g2="s.sc" k="30" />
+    <hkern u1="&#x422;" g2="r.sc" k="20" />
+    <hkern u1="&#x422;" g2="p.sc" k="20" />
+    <hkern u1="&#x422;" g2="n.sc" k="20" />
+    <hkern u1="&#x422;" g2="m.sc" k="20" />
+    <hkern u1="&#x422;" g2="l.sc" k="20" />
+    <hkern u1="&#x422;" g2="k.sc" k="20" />
+    <hkern u1="&#x422;" g2="i.sc" k="20" />
+    <hkern u1="&#x422;" g2="h.sc" k="20" />
+    <hkern u1="&#x422;" g2="f.sc" k="20" />
+    <hkern u1="&#x422;" g2="e.sc" k="20" />
+    <hkern u1="&#x422;" g2="d.sc" k="20" />
+    <hkern u1="&#x422;" g2="b.sc" k="40" />
+    <hkern u1="&#x422;" g2="afii10097.sc" k="70" />
+    <hkern u1="&#x422;" g2="afii10096.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10094.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10093.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#x422;" g2="afii10091.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10090.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10088.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10082.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10081.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10079.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10078.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10076.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10074.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10070.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10068.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10067.sc" k="20" />
+    <hkern u1="&#x422;" g2="afii10066.sc" k="20" />
+    <hkern u1="&#x422;" u2="&#x44f;" k="40" />
+    <hkern u1="&#x422;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x422;" u2="&#x447;" k="40" />
+    <hkern u1="&#x422;" u2="&#x442;" k="60" />
+    <hkern u1="&#x422;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x422;" u2="J" k="20" />
+    <hkern u1="&#x422;" u2="&#x2f;" k="100" />
+    <hkern u1="&#x423;" g2="s.sc" k="30" />
+    <hkern u1="&#x423;" g2="r.sc" k="10" />
+    <hkern u1="&#x423;" g2="p.sc" k="10" />
+    <hkern u1="&#x423;" g2="n.sc" k="10" />
+    <hkern u1="&#x423;" g2="m.sc" k="10" />
+    <hkern u1="&#x423;" g2="l.sc" k="10" />
+    <hkern u1="&#x423;" g2="k.sc" k="10" />
+    <hkern u1="&#x423;" g2="j.sc" k="10" />
+    <hkern u1="&#x423;" g2="i.sc" k="10" />
+    <hkern u1="&#x423;" g2="h.sc" k="10" />
+    <hkern u1="&#x423;" g2="f.sc" k="10" />
+    <hkern u1="&#x423;" g2="e.sc" k="10" />
+    <hkern u1="&#x423;" g2="d.sc" k="10" />
+    <hkern u1="&#x423;" g2="b.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x423;" g2="afii10096.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10094.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10093.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10091.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10090.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10088.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10082.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10081.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10079.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10078.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10076.sc" k="10" />
+    <hkern u1="&#x423;" g2="afii10074.sc" k="10" />
+    <hkern u1="&#x423;" u2="&#x44f;" k="50" />
+    <hkern u1="&#x423;" u2="&#x44a;" k="20" />
+    <hkern u1="&#x423;" u2="&#x447;" k="30" />
+    <hkern u1="&#x423;" u2="&#x442;" k="30" />
+    <hkern u1="&#x423;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x423;" u2="J" k="20" />
+    <hkern u1="&#x423;" u2="&#x2f;" k="100" />
+    <hkern u1="&#x424;" g2="s.sc" k="10" />
+    <hkern u1="&#x424;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x424;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x424;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x424;" u2="&#x447;" k="10" />
+    <hkern u1="&#x424;" u2="&#x442;" k="10" />
+    <hkern u1="&#x424;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x424;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x424;" u2="&#x427;" k="20" />
+    <hkern u1="&#x424;" u2="&#x37;" k="40" />
+    <hkern u1="&#x424;" u2="&#x33;" k="40" />
+    <hkern u1="&#x424;" u2="&#x32;" k="30" />
+    <hkern u1="&#x424;" u2="&#x31;" k="20" />
+    <hkern u1="&#x424;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x425;" g2="s.sc" k="20" />
+    <hkern u1="&#x425;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x425;" g2="afii10092.sc" k="50" />
+    <hkern u1="&#x425;" g2="afii10089.sc" k="50" />
+    <hkern u1="&#x425;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x425;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x425;" u2="&#x447;" k="50" />
+    <hkern u1="&#x425;" u2="&#x442;" k="40" />
+    <hkern u1="&#x425;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x425;" u2="&#x42a;" k="20" />
+    <hkern u1="&#x425;" u2="&#x427;" k="30" />
+    <hkern u1="&#x426;" g2="afii10092.sc" k="30" />
+    <hkern u1="&#x426;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x426;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x426;" u2="&#x447;" k="30" />
+    <hkern u1="&#x426;" u2="&#x442;" k="20" />
+    <hkern u1="&#x426;" u2="&#x42a;" k="30" />
+    <hkern u1="&#x426;" u2="&#x427;" k="40" />
+    <hkern u1="&#x429;" g2="afii10092.sc" k="30" />
+    <hkern u1="&#x429;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x429;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x429;" u2="&#x447;" k="30" />
+    <hkern u1="&#x429;" u2="&#x442;" k="20" />
+    <hkern u1="&#x429;" u2="&#x42a;" k="30" />
+    <hkern u1="&#x429;" u2="&#x427;" k="40" />
+    <hkern u1="&#x42a;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x42a;" g2="afii10092.sc" k="60" />
+    <hkern u1="&#x42a;" g2="afii10089.sc" k="30" />
+    <hkern u1="&#x42a;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x42a;" u2="&#x442;" k="40" />
+    <hkern u1="&#x42a;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x42a;" u2="&#x42a;" k="70" />
+    <hkern u1="&#x42a;" u2="&#x427;" k="50" />
+    <hkern u1="&#x42c;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x42c;" g2="afii10092.sc" k="60" />
+    <hkern u1="&#x42c;" g2="afii10089.sc" k="30" />
+    <hkern u1="&#x42c;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x42c;" u2="&#x442;" k="40" />
+    <hkern u1="&#x42c;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x42c;" u2="&#x42a;" k="70" />
+    <hkern u1="&#x42c;" u2="&#x427;" k="50" />
+    <hkern u1="&#x42d;" g2="s.sc" k="10" />
+    <hkern u1="&#x42d;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x42d;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x42d;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x42d;" u2="&#x447;" k="10" />
+    <hkern u1="&#x42d;" u2="&#x442;" k="10" />
+    <hkern u1="&#x42d;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x42d;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x42d;" u2="&#x427;" k="20" />
+    <hkern u1="&#x42d;" u2="&#x37;" k="40" />
+    <hkern u1="&#x42d;" u2="&#x33;" k="40" />
+    <hkern u1="&#x42d;" u2="&#x32;" k="30" />
+    <hkern u1="&#x42d;" u2="&#x31;" k="20" />
+    <hkern u1="&#x42d;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x42e;" g2="s.sc" k="10" />
+    <hkern u1="&#x42e;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x42e;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x42e;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x42e;" u2="&#x447;" k="10" />
+    <hkern u1="&#x42e;" u2="&#x442;" k="10" />
+    <hkern u1="&#x42e;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x42e;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x42e;" u2="&#x427;" k="20" />
+    <hkern u1="&#x42e;" u2="&#x37;" k="40" />
+    <hkern u1="&#x42e;" u2="&#x33;" k="40" />
+    <hkern u1="&#x42e;" u2="&#x32;" k="30" />
+    <hkern u1="&#x42e;" u2="&#x31;" k="20" />
+    <hkern u1="&#x42e;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x430;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x430;" u2="&#x447;" k="20" />
+    <hkern u1="&#x430;" u2="&#x442;" k="40" />
+    <hkern u1="&#x431;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x431;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x431;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x431;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x431;" u2="&#x447;" k="10" />
+    <hkern u1="&#x431;" u2="&#x442;" k="20" />
+    <hkern u1="&#x431;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x432;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x432;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x432;" u2="&#x447;" k="20" />
+    <hkern u1="&#x432;" u2="&#x442;" k="30" />
+    <hkern u1="&#x432;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x433;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x434;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x434;" u2="&#x447;" k="20" />
+    <hkern u1="&#x434;" u2="&#x442;" k="20" />
+    <hkern u1="&#x435;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x435;" u2="&#x447;" k="10" />
+    <hkern u1="&#x435;" u2="&#x442;" k="30" />
+    <hkern u1="&#x436;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x436;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x436;" u2="&#x447;" k="30" />
+    <hkern u1="&#x436;" u2="&#x442;" k="30" />
+    <hkern u1="&#x437;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x437;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x437;" u2="&#x447;" k="20" />
+    <hkern u1="&#x437;" u2="&#x442;" k="30" />
+    <hkern u1="&#x437;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x43a;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x43a;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x43a;" u2="&#x447;" k="30" />
+    <hkern u1="&#x43a;" u2="&#x442;" k="30" />
+    <hkern u1="&#x43b;" g2="afii10072.77.liga" k="10" />
+    <hkern u1="&#x43b;" u2="&#x445;" k="10" />
+    <hkern u1="&#x43b;" u2="&#x436;" k="10" />
+    <hkern u1="&#x43b;" u2="x" k="10" />
+    <hkern u1="&#x43e;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x43e;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x43e;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x43e;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x43e;" u2="&#x447;" k="10" />
+    <hkern u1="&#x43e;" u2="&#x442;" k="20" />
+    <hkern u1="&#x43e;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x440;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x440;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x440;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x440;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x440;" u2="&#x447;" k="10" />
+    <hkern u1="&#x440;" u2="&#x442;" k="20" />
+    <hkern u1="&#x440;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x441;" u2="&#x447;" k="20" />
+    <hkern u1="&#x442;" g2="zerooldstyle" k="20" />
+    <hkern u1="&#x442;" g2="zero.slash.oldstyle" k="20" />
+    <hkern u1="&#x442;" g2="uni0203.alt1" k="20" />
+    <hkern u1="&#x442;" g2="uni0201.alt1" k="20" />
+    <hkern u1="&#x442;" g2="uni00AD.case" k="50" />
+    <hkern u1="&#x442;" g2="plusminus.case" k="50" />
+    <hkern u1="&#x442;" g2="plus.case" k="50" />
+    <hkern u1="&#x442;" g2="periodcentered.case" k="50" />
+    <hkern u1="&#x442;" g2="numbersign.case" k="50" />
+    <hkern u1="&#x442;" g2="notequal.case" k="50" />
+    <hkern u1="&#x442;" g2="multiply.case" k="50" />
+    <hkern u1="&#x442;" g2="minus.case" k="50" />
+    <hkern u1="&#x442;" g2="logicalnot.case" k="50" />
+    <hkern u1="&#x442;" g2="lessequal.case" k="50" />
+    <hkern u1="&#x442;" g2="less.case" k="50" />
+    <hkern u1="&#x442;" g2="hyphen.case" k="50" />
+    <hkern u1="&#x442;" g2="guilsinglright.case" k="50" />
+    <hkern u1="&#x442;" g2="guilsinglleft.case" k="50" />
+    <hkern u1="&#x442;" g2="guillemotright.case" k="50" />
+    <hkern u1="&#x442;" g2="guillemotleft.case" k="50" />
+    <hkern u1="&#x442;" g2="greaterequal.case" k="50" />
+    <hkern u1="&#x442;" g2="greater.case" k="50" />
+    <hkern u1="&#x442;" g2="equal.case" k="50" />
+    <hkern u1="&#x442;" g2="endash.case" k="50" />
+    <hkern u1="&#x442;" g2="emdash.case" k="50" />
+    <hkern u1="&#x442;" g2="divide.case" k="50" />
+    <hkern u1="&#x442;" g2="currency.taboldstyle" k="50" />
+    <hkern u1="&#x442;" g2="ct.liga" k="20" />
+    <hkern u1="&#x442;" g2="copyright.case" k="20" />
+    <hkern u1="&#x442;" g2="ck.liga" k="20" />
+    <hkern u1="&#x442;" g2="ch.liga" k="20" />
+    <hkern u1="&#x442;" g2="centoldstyle" k="20" />
+    <hkern u1="&#x442;" g2="cb.liga" k="20" />
+    <hkern u1="&#x442;" g2="bullet.case" k="50" />
+    <hkern u1="&#x442;" g2="atilde.alt1" k="20" />
+    <hkern u1="&#x442;" g2="asciitilde.case" k="50" />
+    <hkern u1="&#x442;" g2="aring.alt1" k="20" />
+    <hkern u1="&#x442;" g2="approxequal.case" k="50" />
+    <hkern u1="&#x442;" g2="aogonek.alt1" k="20" />
+    <hkern u1="&#x442;" g2="amacron.alt1" k="20" />
+    <hkern u1="&#x442;" g2="agrave.alt1" k="20" />
+    <hkern u1="&#x442;" g2="afii10072.77.liga" k="30" />
+    <hkern u1="&#x442;" g2="afii10065.alt1" k="20" />
+    <hkern u1="&#x442;" g2="afii10065.77.liga" k="20" />
+    <hkern u1="&#x442;" g2="ae.alt1" k="20" />
+    <hkern u1="&#x442;" g2="adieresis.alt1" k="20" />
+    <hkern u1="&#x442;" g2="acircumflex.alt1" k="20" />
+    <hkern u1="&#x442;" g2="abreve.alt1" k="20" />
+    <hkern u1="&#x442;" g2="aacute.alt1" k="20" />
+    <hkern u1="&#x442;" g2="a.alt1" k="20" />
+    <hkern u1="&#x442;" g2="Eurooldstyle" k="20" />
+    <hkern u1="&#x442;" u2="&#x2265;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2264;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2260;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2248;" k="50" />
+    <hkern u1="&#x442;" u2="&#x221e;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2212;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2202;" k="20" />
+    <hkern u1="&#x442;" u2="&#x2192;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2190;" k="50" />
+    <hkern u1="&#x442;" u2="&#x212e;" k="20" />
+    <hkern u1="&#x442;" u2="&#x203a;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2039;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2026;" k="90" />
+    <hkern u1="&#x442;" u2="&#x2025;" k="90" />
+    <hkern u1="&#x442;" u2="&#x2024;" k="90" />
+    <hkern u1="&#x442;" u2="&#x201e;" k="90" />
+    <hkern u1="&#x442;" u2="&#x201a;" k="90" />
+    <hkern u1="&#x442;" u2="&#x2014;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2013;" k="50" />
+    <hkern u1="&#x442;" u2="&#x459;" k="50" />
+    <hkern u1="&#x442;" u2="&#x454;" k="20" />
+    <hkern u1="&#x442;" u2="&#x451;" k="20" />
+    <hkern u1="&#x442;" u2="&#x44d;" k="10" />
+    <hkern u1="&#x442;" u2="&#x445;" k="30" />
+    <hkern u1="&#x442;" u2="&#x444;" k="20" />
+    <hkern u1="&#x442;" u2="&#x441;" k="20" />
+    <hkern u1="&#x442;" u2="&#x43e;" k="20" />
+    <hkern u1="&#x442;" u2="&#x43b;" k="50" />
+    <hkern u1="&#x442;" u2="&#x437;" k="10" />
+    <hkern u1="&#x442;" u2="&#x436;" k="30" />
+    <hkern u1="&#x442;" u2="&#x435;" k="20" />
+    <hkern u1="&#x442;" u2="&#x434;" k="50" />
+    <hkern u1="&#x442;" u2="&#x431;" k="20" />
+    <hkern u1="&#x442;" u2="&#x430;" k="20" />
+    <hkern u1="&#x442;" u2="&#x20f;" k="20" />
+    <hkern u1="&#x442;" u2="&#x20d;" k="20" />
+    <hkern u1="&#x442;" u2="&#x203;" k="20" />
+    <hkern u1="&#x442;" u2="&#x201;" k="20" />
+    <hkern u1="&#x442;" u2="&#x153;" k="20" />
+    <hkern u1="&#x442;" u2="&#x151;" k="20" />
+    <hkern u1="&#x442;" u2="&#x14f;" k="20" />
+    <hkern u1="&#x442;" u2="&#x14d;" k="20" />
+    <hkern u1="&#x442;" u2="&#x11b;" k="20" />
+    <hkern u1="&#x442;" u2="&#x119;" k="20" />
+    <hkern u1="&#x442;" u2="&#x117;" k="20" />
+    <hkern u1="&#x442;" u2="&#x115;" k="20" />
+    <hkern u1="&#x442;" u2="&#x113;" k="20" />
+    <hkern u1="&#x442;" u2="&#x111;" k="20" />
+    <hkern u1="&#x442;" u2="&#x10f;" k="20" />
+    <hkern u1="&#x442;" u2="&#x10d;" k="20" />
+    <hkern u1="&#x442;" u2="&#x10b;" k="20" />
+    <hkern u1="&#x442;" u2="&#x109;" k="20" />
+    <hkern u1="&#x442;" u2="&#x107;" k="20" />
+    <hkern u1="&#x442;" u2="&#x105;" k="20" />
+    <hkern u1="&#x442;" u2="&#x103;" k="20" />
+    <hkern u1="&#x442;" u2="&#x101;" k="20" />
+    <hkern u1="&#x442;" u2="&#xf8;" k="20" />
+    <hkern u1="&#x442;" u2="&#xf7;" k="50" />
+    <hkern u1="&#x442;" u2="&#xf6;" k="20" />
+    <hkern u1="&#x442;" u2="&#xf5;" k="20" />
+    <hkern u1="&#x442;" u2="&#xf4;" k="20" />
+    <hkern u1="&#x442;" u2="&#xf3;" k="20" />
+    <hkern u1="&#x442;" u2="&#xf2;" k="20" />
+    <hkern u1="&#x442;" u2="&#xf0;" k="20" />
+    <hkern u1="&#x442;" u2="&#xeb;" k="20" />
+    <hkern u1="&#x442;" u2="&#xea;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe9;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe8;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe7;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe6;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe5;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe4;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe3;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe2;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe1;" k="20" />
+    <hkern u1="&#x442;" u2="&#xe0;" k="20" />
+    <hkern u1="&#x442;" u2="&#xd7;" k="50" />
+    <hkern u1="&#x442;" u2="&#xbb;" k="50" />
+    <hkern u1="&#x442;" u2="&#xb7;" k="50" />
+    <hkern u1="&#x442;" u2="&#xb1;" k="50" />
+    <hkern u1="&#x442;" u2="&#xad;" k="50" />
+    <hkern u1="&#x442;" u2="&#xac;" k="50" />
+    <hkern u1="&#x442;" u2="&#xab;" k="50" />
+    <hkern u1="&#x442;" u2="&#xa4;" k="50" />
+    <hkern u1="&#x442;" u2="&#xa2;" k="20" />
+    <hkern u1="&#x442;" u2="&#x7e;" k="50" />
+    <hkern u1="&#x442;" u2="x" k="30" />
+    <hkern u1="&#x442;" u2="q" k="20" />
+    <hkern u1="&#x442;" u2="o" k="20" />
+    <hkern u1="&#x442;" u2="e" k="20" />
+    <hkern u1="&#x442;" u2="d" k="20" />
+    <hkern u1="&#x442;" u2="c" k="20" />
+    <hkern u1="&#x442;" u2="a" k="20" />
+    <hkern u1="&#x442;" u2="_" k="90" />
+    <hkern u1="&#x442;" u2="&#x3e;" k="50" />
+    <hkern u1="&#x442;" u2="&#x3d;" k="50" />
+    <hkern u1="&#x442;" u2="&#x3c;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2e;" k="90" />
+    <hkern u1="&#x442;" u2="&#x2d;" k="50" />
+    <hkern u1="&#x442;" u2="&#x2c;" k="90" />
+    <hkern u1="&#x442;" u2="&#x2b;" k="50" />
+    <hkern u1="&#x442;" u2="&#x23;" k="50" />
+    <hkern u1="&#x442;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x442;" u2="&#x2f;" k="70" />
+    <hkern u1="&#x443;" u2="&#x2f;" k="80" />
+    <hkern u1="&#x444;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x444;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x444;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x444;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x444;" u2="&#x447;" k="10" />
+    <hkern u1="&#x444;" u2="&#x442;" k="20" />
+    <hkern u1="&#x444;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x445;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x445;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x445;" u2="&#x447;" k="30" />
+    <hkern u1="&#x445;" u2="&#x442;" k="30" />
+    <hkern u1="&#x446;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x446;" u2="&#x447;" k="20" />
+    <hkern u1="&#x446;" u2="&#x442;" k="20" />
+    <hkern u1="&#x447;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x449;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x449;" u2="&#x447;" k="20" />
+    <hkern u1="&#x449;" u2="&#x442;" k="20" />
+    <hkern u1="&#x44a;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x44a;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x44a;" u2="&#x447;" k="20" />
+    <hkern u1="&#x44a;" u2="&#x442;" k="70" />
+    <hkern u1="&#x44c;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x44c;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x44c;" u2="&#x447;" k="20" />
+    <hkern u1="&#x44c;" u2="&#x442;" k="70" />
+    <hkern u1="&#x44d;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x44d;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x44d;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x44d;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x44d;" u2="&#x447;" k="10" />
+    <hkern u1="&#x44d;" u2="&#x442;" k="20" />
+    <hkern u1="&#x44d;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x44e;" g2="threeoldstyle" k="40" />
+    <hkern u1="&#x44e;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x44e;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x44e;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x44e;" u2="&#x447;" k="10" />
+    <hkern u1="&#x44e;" u2="&#x442;" k="20" />
+    <hkern u1="&#x44e;" u2="&#x2f;" k="20" />
+    <hkern u1="&#x44f;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x44f;" u2="&#x442;" k="10" />
+    <hkern u1="&#x451;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x451;" u2="&#x447;" k="10" />
+    <hkern u1="&#x451;" u2="&#x442;" k="30" />
+    <hkern u1="&#x453;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x454;" u2="&#x447;" k="20" />
+    <hkern u1="&#x459;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x459;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x459;" u2="&#x447;" k="20" />
+    <hkern u1="&#x459;" u2="&#x442;" k="70" />
+    <hkern u1="&#x45a;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x45a;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x45a;" u2="&#x447;" k="20" />
+    <hkern u1="&#x45a;" u2="&#x442;" k="70" />
+    <hkern u1="&#x45c;" u2="&#x44f;" k="20" />
+    <hkern u1="&#x45c;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x45c;" u2="&#x447;" k="30" />
+    <hkern u1="&#x45c;" u2="&#x442;" k="30" />
+    <hkern u1="&#x45e;" u2="&#x2f;" k="80" />
+    <hkern u1="&#x490;" g2="s.sc" k="30" />
+    <hkern u1="&#x490;" g2="r.sc" k="20" />
+    <hkern u1="&#x490;" g2="p.sc" k="20" />
+    <hkern u1="&#x490;" g2="n.sc" k="20" />
+    <hkern u1="&#x490;" g2="m.sc" k="20" />
+    <hkern u1="&#x490;" g2="l.sc" k="20" />
+    <hkern u1="&#x490;" g2="k.sc" k="20" />
+    <hkern u1="&#x490;" g2="i.sc" k="20" />
+    <hkern u1="&#x490;" g2="h.sc" k="20" />
+    <hkern u1="&#x490;" g2="f.sc" k="20" />
+    <hkern u1="&#x490;" g2="e.sc" k="20" />
+    <hkern u1="&#x490;" g2="d.sc" k="20" />
+    <hkern u1="&#x490;" g2="b.sc" k="40" />
+    <hkern u1="&#x490;" g2="afii10097.sc" k="70" />
+    <hkern u1="&#x490;" g2="afii10096.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10094.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10093.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10092.sc" k="10" />
+    <hkern u1="&#x490;" g2="afii10091.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10090.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10088.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10082.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10081.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10079.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10078.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10076.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10074.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10070.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10068.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10067.sc" k="20" />
+    <hkern u1="&#x490;" g2="afii10066.sc" k="20" />
+    <hkern u1="&#x490;" u2="&#x44f;" k="40" />
+    <hkern u1="&#x490;" u2="&#x44a;" k="50" />
+    <hkern u1="&#x490;" u2="&#x447;" k="40" />
+    <hkern u1="&#x490;" u2="&#x442;" k="60" />
+    <hkern u1="&#x490;" u2="&#x42f;" k="40" />
+    <hkern u1="&#x490;" u2="J" k="20" />
+    <hkern u1="&#x490;" u2="&#x2f;" k="100" />
+    <hkern u1="&#x491;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x2013;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2013;" g2="two.sc" k="40" />
+    <hkern u1="&#x2013;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2013;" g2="three.sc" k="20" />
+    <hkern u1="&#x2013;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2013;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2013;" g2="s.sc" k="20" />
+    <hkern u1="&#x2013;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2013;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2013;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2013;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2013;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2014;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2014;" g2="two.sc" k="40" />
+    <hkern u1="&#x2014;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2014;" g2="three.sc" k="20" />
+    <hkern u1="&#x2014;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2014;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2014;" g2="s.sc" k="20" />
+    <hkern u1="&#x2014;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2014;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2014;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2014;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2014;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2018;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2018;" g2="three.sc" k="10" />
+    <hkern u1="&#x2018;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#x2018;" g2="nine.sc" k="30" />
+    <hkern u1="&#x2018;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#x2018;" g2="four.sc" k="100" />
+    <hkern u1="&#x2018;" g2="eight.sc" k="30" />
+    <hkern u1="&#x2018;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#x2018;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x2018;" u2="&#x34;" k="70" />
+    <hkern u1="&#x2019;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2019;" g2="three.sc" k="10" />
+    <hkern u1="&#x2019;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#x2019;" g2="nine.sc" k="30" />
+    <hkern u1="&#x2019;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#x2019;" g2="four.sc" k="100" />
+    <hkern u1="&#x2019;" g2="eight.sc" k="30" />
+    <hkern u1="&#x2019;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#x2019;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x2019;" u2="&#x34;" k="70" />
+    <hkern u1="&#x201a;" g2="sevenoldstyle" k="40" />
+    <hkern u1="&#x201a;" g2="eightoldstyle" k="20" />
+    <hkern u1="&#x201a;" g2="afii10092.sc" k="90" />
+    <hkern u1="&#x201a;" g2="afii10089.sc" k="90" />
+    <hkern u1="&#x201a;" u2="&#x44a;" k="80" />
+    <hkern u1="&#x201a;" u2="&#x447;" k="80" />
+    <hkern u1="&#x201a;" u2="&#x442;" k="90" />
+    <hkern u1="&#x201a;" u2="&#x39;" k="40" />
+    <hkern u1="&#x201a;" u2="&#x38;" k="30" />
+    <hkern u1="&#x201a;" u2="&#x37;" k="70" />
+    <hkern u1="&#x201a;" u2="&#x34;" k="40" />
+    <hkern u1="&#x201c;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x201c;" g2="three.sc" k="10" />
+    <hkern u1="&#x201c;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#x201c;" g2="nine.sc" k="30" />
+    <hkern u1="&#x201c;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#x201c;" g2="four.sc" k="100" />
+    <hkern u1="&#x201c;" g2="eight.sc" k="30" />
+    <hkern u1="&#x201c;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#x201c;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x201c;" u2="&#x34;" k="70" />
+    <hkern u1="&#x201d;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x201d;" g2="three.sc" k="10" />
+    <hkern u1="&#x201d;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#x201d;" g2="nine.sc" k="30" />
+    <hkern u1="&#x201d;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#x201d;" g2="four.sc" k="100" />
+    <hkern u1="&#x201d;" g2="eight.sc" k="30" />
+    <hkern u1="&#x201d;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#x201d;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x201d;" u2="&#x34;" k="70" />
+    <hkern u1="&#x201e;" g2="sevenoldstyle" k="40" />
+    <hkern u1="&#x201e;" g2="eightoldstyle" k="20" />
+    <hkern u1="&#x201e;" g2="afii10092.sc" k="90" />
+    <hkern u1="&#x201e;" g2="afii10089.sc" k="90" />
+    <hkern u1="&#x201e;" u2="&#x44a;" k="80" />
+    <hkern u1="&#x201e;" u2="&#x447;" k="80" />
+    <hkern u1="&#x201e;" u2="&#x442;" k="90" />
+    <hkern u1="&#x201e;" u2="&#x39;" k="40" />
+    <hkern u1="&#x201e;" u2="&#x38;" k="30" />
+    <hkern u1="&#x201e;" u2="&#x37;" k="70" />
+    <hkern u1="&#x201e;" u2="&#x34;" k="40" />
+    <hkern u1="&#x2024;" g2="sevenoldstyle" k="40" />
+    <hkern u1="&#x2024;" g2="eightoldstyle" k="20" />
+    <hkern u1="&#x2024;" g2="afii10092.sc" k="90" />
+    <hkern u1="&#x2024;" g2="afii10089.sc" k="90" />
+    <hkern u1="&#x2024;" u2="&#x44a;" k="80" />
+    <hkern u1="&#x2024;" u2="&#x447;" k="80" />
+    <hkern u1="&#x2024;" u2="&#x442;" k="90" />
+    <hkern u1="&#x2024;" u2="&#x39;" k="40" />
+    <hkern u1="&#x2024;" u2="&#x38;" k="30" />
+    <hkern u1="&#x2024;" u2="&#x37;" k="70" />
+    <hkern u1="&#x2024;" u2="&#x34;" k="40" />
+    <hkern u1="&#x2025;" g2="sevenoldstyle" k="40" />
+    <hkern u1="&#x2025;" g2="eightoldstyle" k="20" />
+    <hkern u1="&#x2025;" g2="afii10092.sc" k="90" />
+    <hkern u1="&#x2025;" g2="afii10089.sc" k="90" />
+    <hkern u1="&#x2025;" u2="&#x44a;" k="80" />
+    <hkern u1="&#x2025;" u2="&#x447;" k="80" />
+    <hkern u1="&#x2025;" u2="&#x442;" k="90" />
+    <hkern u1="&#x2025;" u2="&#x39;" k="40" />
+    <hkern u1="&#x2025;" u2="&#x38;" k="30" />
+    <hkern u1="&#x2025;" u2="&#x37;" k="70" />
+    <hkern u1="&#x2025;" u2="&#x34;" k="40" />
+    <hkern u1="&#x2026;" g2="sevenoldstyle" k="40" />
+    <hkern u1="&#x2026;" g2="eightoldstyle" k="20" />
+    <hkern u1="&#x2026;" g2="afii10092.sc" k="90" />
+    <hkern u1="&#x2026;" g2="afii10089.sc" k="90" />
+    <hkern u1="&#x2026;" u2="&#x44a;" k="80" />
+    <hkern u1="&#x2026;" u2="&#x447;" k="80" />
+    <hkern u1="&#x2026;" u2="&#x442;" k="90" />
+    <hkern u1="&#x2026;" u2="&#x39;" k="40" />
+    <hkern u1="&#x2026;" u2="&#x38;" k="30" />
+    <hkern u1="&#x2026;" u2="&#x37;" k="70" />
+    <hkern u1="&#x2026;" u2="&#x34;" k="40" />
+    <hkern u1="&#x2039;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2039;" g2="two.sc" k="40" />
+    <hkern u1="&#x2039;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2039;" g2="three.sc" k="20" />
+    <hkern u1="&#x2039;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2039;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2039;" g2="s.sc" k="20" />
+    <hkern u1="&#x2039;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2039;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2039;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2039;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2039;" u2="&#x37;" k="60" />
+    <hkern u1="&#x203a;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x203a;" g2="two.sc" k="40" />
+    <hkern u1="&#x203a;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x203a;" g2="three.sc" k="20" />
+    <hkern u1="&#x203a;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x203a;" g2="seven.sc" k="40" />
+    <hkern u1="&#x203a;" g2="s.sc" k="20" />
+    <hkern u1="&#x203a;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x203a;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x203a;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x203a;" u2="&#x442;" k="50" />
+    <hkern u1="&#x203a;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2044;" g2="zero.numerator" k="-50" />
+    <hkern u1="&#x2044;" g2="zero.denominator" k="110" />
+    <hkern u1="&#x2044;" g2="two.numerator" k="-80" />
+    <hkern u1="&#x2044;" g2="two.denominator" k="40" />
+    <hkern u1="&#x2044;" g2="three.numerator" k="-60" />
+    <hkern u1="&#x2044;" g2="three.denominator" k="60" />
+    <hkern u1="&#x2044;" g2="six.numerator" k="-30" />
+    <hkern u1="&#x2044;" g2="six.denominator" k="90" />
+    <hkern u1="&#x2044;" g2="seven.numerator" k="-100" />
+    <hkern u1="&#x2044;" g2="seven.denominator" k="40" />
+    <hkern u1="&#x2044;" g2="one.numerator" k="-80" />
+    <hkern u1="&#x2044;" g2="one.denominator" k="40" />
+    <hkern u1="&#x2044;" g2="nine.numerator" k="-70" />
+    <hkern u1="&#x2044;" g2="nine.denominator" k="80" />
+    <hkern u1="&#x2044;" g2="four.numerator" k="-10" />
+    <hkern u1="&#x2044;" g2="four.denominator" k="140" />
+    <hkern u1="&#x2044;" g2="five.numerator" k="-60" />
+    <hkern u1="&#x2044;" g2="five.denominator" k="60" />
+    <hkern u1="&#x2044;" g2="eight.numerator" k="-40" />
+    <hkern u1="&#x2044;" g2="eight.denominator" k="60" />
+    <hkern u1="&#x20ac;" g2="s.sc" k="20" />
+    <hkern u1="&#x20ac;" g2="afii10089.sc" k="10" />
+    <hkern u1="&#x20ac;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x20ac;" u2="&#x44a;" k="40" />
+    <hkern u1="&#x20ac;" u2="&#x447;" k="20" />
+    <hkern u1="&#x2122;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2122;" g2="three.sc" k="10" />
+    <hkern u1="&#x2122;" g2="nineoldstyle" k="30" />
+    <hkern u1="&#x2122;" g2="nine.sc" k="30" />
+    <hkern u1="&#x2122;" g2="fouroldstyle" k="160" />
+    <hkern u1="&#x2122;" g2="four.sc" k="100" />
+    <hkern u1="&#x2122;" g2="eight.sc" k="30" />
+    <hkern u1="&#x2122;" g2="afii10097.sc" k="60" />
+    <hkern u1="&#x2122;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x2122;" u2="&#x34;" k="70" />
+    <hkern u1="&#x2126;" g2="s.sc" k="10" />
+    <hkern u1="&#x2126;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x2126;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x2126;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x2126;" u2="&#x447;" k="10" />
+    <hkern u1="&#x2126;" u2="&#x442;" k="10" />
+    <hkern u1="&#x2126;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x2126;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x2126;" u2="&#x427;" k="20" />
+    <hkern u1="&#x2126;" u2="&#x37;" k="40" />
+    <hkern u1="&#x2126;" u2="&#x33;" k="40" />
+    <hkern u1="&#x2126;" u2="&#x32;" k="30" />
+    <hkern u1="&#x2126;" u2="&#x31;" k="20" />
+    <hkern u1="&#x2126;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x2190;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2190;" g2="two.sc" k="40" />
+    <hkern u1="&#x2190;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2190;" g2="three.sc" k="20" />
+    <hkern u1="&#x2190;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2190;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2190;" g2="s.sc" k="20" />
+    <hkern u1="&#x2190;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2190;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2190;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2190;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2190;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2192;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2192;" g2="two.sc" k="40" />
+    <hkern u1="&#x2192;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2192;" g2="three.sc" k="20" />
+    <hkern u1="&#x2192;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2192;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2192;" g2="s.sc" k="20" />
+    <hkern u1="&#x2192;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2192;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2192;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2192;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2192;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2202;" g2="s.sc" k="10" />
+    <hkern u1="&#x2202;" g2="afii10097.sc" k="20" />
+    <hkern u1="&#x2202;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x2202;" u2="&#x44a;" k="10" />
+    <hkern u1="&#x2202;" u2="&#x447;" k="10" />
+    <hkern u1="&#x2202;" u2="&#x442;" k="10" />
+    <hkern u1="&#x2202;" u2="&#x42f;" k="20" />
+    <hkern u1="&#x2202;" u2="&#x42a;" k="40" />
+    <hkern u1="&#x2202;" u2="&#x427;" k="20" />
+    <hkern u1="&#x2202;" u2="&#x37;" k="40" />
+    <hkern u1="&#x2202;" u2="&#x33;" k="40" />
+    <hkern u1="&#x2202;" u2="&#x32;" k="30" />
+    <hkern u1="&#x2202;" u2="&#x31;" k="20" />
+    <hkern u1="&#x2202;" u2="&#x2f;" k="30" />
+    <hkern u1="&#x2206;" g2="afii10089.sc" k="60" />
+    <hkern u1="&#x2206;" u2="&#x44a;" k="70" />
+    <hkern u1="&#x2206;" u2="&#x447;" k="50" />
+    <hkern u1="&#x2206;" u2="&#x442;" k="60" />
+    <hkern u1="&#x2206;" u2="&#x42a;" k="60" />
+    <hkern u1="&#x2206;" u2="&#x427;" k="70" />
+    <hkern u1="&#x2206;" u2="&#x2f;" k="-30" />
+    <hkern u1="&#x2211;" g2="s.sc" k="10" />
+    <hkern u1="&#x2211;" g2="afii10092.sc" k="20" />
+    <hkern u1="&#x2211;" g2="afii10089.sc" k="20" />
+    <hkern u1="&#x2211;" u2="&#x44f;" k="10" />
+    <hkern u1="&#x2211;" u2="&#x44a;" k="30" />
+    <hkern u1="&#x2211;" u2="&#x447;" k="20" />
+    <hkern u1="&#x2211;" u2="&#x442;" k="20" />
+    <hkern u1="&#x2211;" u2="&#x42f;" k="10" />
+    <hkern u1="&#x2211;" u2="&#x427;" k="20" />
+    <hkern u1="&#x2212;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2212;" g2="two.sc" k="40" />
+    <hkern u1="&#x2212;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2212;" g2="three.sc" k="20" />
+    <hkern u1="&#x2212;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2212;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2212;" g2="s.sc" k="20" />
+    <hkern u1="&#x2212;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2212;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2212;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2212;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2212;" u2="&#x37;" k="60" />
+    <hkern u1="&#x221a;" u2="&#x2f;" k="80" />
+    <hkern u1="&#x221e;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x221e;" g2="two.sc" k="40" />
+    <hkern u1="&#x221e;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x221e;" g2="three.sc" k="20" />
+    <hkern u1="&#x221e;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x221e;" g2="seven.sc" k="40" />
+    <hkern u1="&#x221e;" g2="s.sc" k="20" />
+    <hkern u1="&#x221e;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x221e;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x221e;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x221e;" u2="&#x442;" k="50" />
+    <hkern u1="&#x221e;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2248;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2248;" g2="two.sc" k="40" />
+    <hkern u1="&#x2248;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2248;" g2="three.sc" k="20" />
+    <hkern u1="&#x2248;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2248;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2248;" g2="s.sc" k="20" />
+    <hkern u1="&#x2248;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2248;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2248;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2248;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2248;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2260;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2260;" g2="two.sc" k="40" />
+    <hkern u1="&#x2260;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2260;" g2="three.sc" k="20" />
+    <hkern u1="&#x2260;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2260;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2260;" g2="s.sc" k="20" />
+    <hkern u1="&#x2260;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2260;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2260;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2260;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2260;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2264;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2264;" g2="two.sc" k="40" />
+    <hkern u1="&#x2264;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2264;" g2="three.sc" k="20" />
+    <hkern u1="&#x2264;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2264;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2264;" g2="s.sc" k="20" />
+    <hkern u1="&#x2264;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2264;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2264;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2264;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2264;" u2="&#x37;" k="60" />
+    <hkern u1="&#x2265;" g2="twooldstyle" k="10" />
+    <hkern u1="&#x2265;" g2="two.sc" k="40" />
+    <hkern u1="&#x2265;" g2="threeoldstyle" k="20" />
+    <hkern u1="&#x2265;" g2="three.sc" k="20" />
+    <hkern u1="&#x2265;" g2="sevenoldstyle" k="20" />
+    <hkern u1="&#x2265;" g2="seven.sc" k="40" />
+    <hkern u1="&#x2265;" g2="s.sc" k="20" />
+    <hkern u1="&#x2265;" g2="fouroldstyle" k="30" />
+    <hkern u1="&#x2265;" g2="fiveoldstyle" k="10" />
+    <hkern u1="&#x2265;" g2="afii10097.sc" k="30" />
+    <hkern u1="&#x2265;" u2="&#x442;" k="50" />
+    <hkern u1="&#x2265;" u2="&#x37;" k="60" />
+    <hkern g1="Euro.sc" g2="s.sc" k="10" />
+    <hkern g1="Euro.sc" g2="j.sc" k="10" />
+    <hkern g1="Euro.sc" g2="afii10097.sc" k="10" />
+    <hkern g1="Eurooldstyle" u2="&#x447;" k="20" />
+    <hkern g1="a.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="a.alt1" u2="&#x447;" k="20" />
+    <hkern g1="a.alt1" u2="&#x442;" k="40" />
+    <hkern g1="a.sc" g2="s.sc" k="20" />
+    <hkern g1="a.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="a.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="aacute.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="aacute.alt1" u2="&#x447;" k="20" />
+    <hkern g1="aacute.alt1" u2="&#x442;" k="40" />
+    <hkern g1="aacute.sc" g2="s.sc" k="20" />
+    <hkern g1="aacute.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="aacute.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="abreve.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="abreve.alt1" u2="&#x447;" k="20" />
+    <hkern g1="abreve.alt1" u2="&#x442;" k="40" />
+    <hkern g1="abreve.sc" g2="s.sc" k="20" />
+    <hkern g1="abreve.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="abreve.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="acircumflex.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="acircumflex.alt1" u2="&#x447;" k="20" />
+    <hkern g1="acircumflex.alt1" u2="&#x442;" k="40" />
+    <hkern g1="acircumflex.sc" g2="s.sc" k="20" />
+    <hkern g1="acircumflex.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="acircumflex.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="adieresis.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="adieresis.alt1" u2="&#x447;" k="20" />
+    <hkern g1="adieresis.alt1" u2="&#x442;" k="40" />
+    <hkern g1="adieresis.sc" g2="s.sc" k="20" />
+    <hkern g1="adieresis.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="adieresis.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="ae.alt1" u2="&#x44a;" k="30" />
+    <hkern g1="ae.alt1" u2="&#x447;" k="10" />
+    <hkern g1="ae.alt1" u2="&#x442;" k="30" />
+    <hkern g1="ae.sc" g2="s.sc" k="10" />
+    <hkern g1="ae.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="ae.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="ae.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="afii10065.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="afii10065.alt1" u2="&#x447;" k="20" />
+    <hkern g1="afii10065.alt1" u2="&#x442;" k="40" />
+    <hkern g1="afii10065.sc" g2="s.sc" k="20" />
+    <hkern g1="afii10065.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="afii10065.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="afii10066.sc" g2="two.sc" k="40" />
+    <hkern g1="afii10066.sc" g2="three.sc" k="20" />
+    <hkern g1="afii10066.sc" g2="seven.sc" k="10" />
+    <hkern g1="afii10066.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10066.sc" g2="one.sc" k="30" />
+    <hkern g1="afii10066.sc" g2="nine.sc" k="20" />
+    <hkern g1="afii10066.sc" g2="five.sc" k="20" />
+    <hkern g1="afii10066.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10066.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="afii10066.sc" g2="afii10089.sc" k="30" />
+    <hkern g1="afii10066.sc" u2="&#x2f;" k="10" />
+    <hkern g1="afii10067.sc" g2="two.sc" k="40" />
+    <hkern g1="afii10067.sc" g2="three.sc" k="20" />
+    <hkern g1="afii10067.sc" g2="seven.sc" k="10" />
+    <hkern g1="afii10067.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10067.sc" g2="one.sc" k="30" />
+    <hkern g1="afii10067.sc" g2="nine.sc" k="20" />
+    <hkern g1="afii10067.sc" g2="five.sc" k="20" />
+    <hkern g1="afii10067.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10067.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="afii10067.sc" g2="afii10089.sc" k="30" />
+    <hkern g1="afii10067.sc" u2="&#x2f;" k="10" />
+    <hkern g1="afii10068.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10068.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10068.sc" u2="&#x2f;" k="60" />
+    <hkern g1="afii10069.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="afii10069.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="afii10070.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10070.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10070.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="afii10070.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="afii10071.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10071.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10071.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="afii10071.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="afii10072.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10072.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="afii10072.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="afii10073.sc" g2="two.sc" k="40" />
+    <hkern g1="afii10073.sc" g2="three.sc" k="20" />
+    <hkern g1="afii10073.sc" g2="seven.sc" k="10" />
+    <hkern g1="afii10073.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10073.sc" g2="one.sc" k="30" />
+    <hkern g1="afii10073.sc" g2="nine.sc" k="20" />
+    <hkern g1="afii10073.sc" g2="five.sc" k="20" />
+    <hkern g1="afii10073.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10073.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="afii10073.sc" g2="afii10089.sc" k="30" />
+    <hkern g1="afii10073.sc" u2="&#x2f;" k="10" />
+    <hkern g1="afii10074.sc" g2="afii10106.sc" k="20" />
+    <hkern g1="afii10074.sc" g2="afii10077.sc" k="20" />
+    <hkern g1="afii10074.sc" g2="afii10069.sc" k="20" />
+    <hkern g1="afii10076.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10076.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="afii10076.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="afii10077.sc" g2="afii10106.sc" k="10" />
+    <hkern g1="afii10077.sc" g2="afii10077.sc" k="10" />
+    <hkern g1="afii10077.sc" g2="afii10069.sc" k="10" />
+    <hkern g1="afii10080.sc" g2="two.sc" k="30" />
+    <hkern g1="afii10080.sc" g2="three.sc" k="20" />
+    <hkern g1="afii10080.sc" g2="seven.sc" k="20" />
+    <hkern g1="afii10080.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10080.sc" g2="one.sc" k="30" />
+    <hkern g1="afii10080.sc" g2="j.sc" k="10" />
+    <hkern g1="afii10080.sc" g2="five.sc" k="20" />
+    <hkern g1="afii10080.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10080.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="afii10080.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="afii10080.sc" u2="&#x2f;" k="40" />
+    <hkern g1="afii10081.sc" g2="afii10106.sc" k="20" />
+    <hkern g1="afii10081.sc" g2="afii10077.sc" k="20" />
+    <hkern g1="afii10081.sc" g2="afii10069.sc" k="20" />
+    <hkern g1="afii10082.sc" g2="j.sc" k="10" />
+    <hkern g1="afii10082.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10082.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="afii10082.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="afii10082.sc" u2="&#x2f;" k="50" />
+    <hkern g1="afii10083.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10083.sc" g2="j.sc" k="10" />
+    <hkern g1="afii10083.sc" g2="afii10097.sc" k="10" />
+    <hkern g1="afii10084.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10084.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10084.sc" u2="&#x2f;" k="60" />
+    <hkern g1="afii10085.sc" g2="s.sc" k="20" />
+    <hkern g1="afii10085.sc" g2="j.sc" k="10" />
+    <hkern g1="afii10085.sc" g2="afii10097.sc" k="40" />
+    <hkern g1="afii10085.sc" u2="&#x2f;" k="70" />
+    <hkern g1="afii10086.sc" g2="two.sc" k="30" />
+    <hkern g1="afii10086.sc" g2="three.sc" k="20" />
+    <hkern g1="afii10086.sc" g2="seven.sc" k="20" />
+    <hkern g1="afii10086.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10086.sc" g2="one.sc" k="30" />
+    <hkern g1="afii10086.sc" g2="j.sc" k="10" />
+    <hkern g1="afii10086.sc" g2="five.sc" k="20" />
+    <hkern g1="afii10086.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10086.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="afii10086.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="afii10086.sc" u2="&#x2f;" k="40" />
+    <hkern g1="afii10087.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10087.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="afii10087.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="afii10088.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="afii10088.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="afii10089.sc" g2="afii10106.sc" k="20" />
+    <hkern g1="afii10089.sc" g2="afii10077.sc" k="20" />
+    <hkern g1="afii10089.sc" g2="afii10069.sc" k="20" />
+    <hkern g1="afii10090.sc" g2="afii10106.sc" k="20" />
+    <hkern g1="afii10090.sc" g2="afii10077.sc" k="20" />
+    <hkern g1="afii10090.sc" g2="afii10069.sc" k="20" />
+    <hkern g1="afii10091.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="afii10091.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="afii10092.sc" g2="afii10097.sc" k="30" />
+    <hkern g1="afii10092.sc" g2="afii10092.sc" k="60" />
+    <hkern g1="afii10092.sc" g2="afii10089.sc" k="50" />
+    <hkern g1="afii10094.sc" g2="afii10097.sc" k="30" />
+    <hkern g1="afii10094.sc" g2="afii10092.sc" k="60" />
+    <hkern g1="afii10094.sc" g2="afii10089.sc" k="50" />
+    <hkern g1="afii10095.sc" g2="two.sc" k="30" />
+    <hkern g1="afii10095.sc" g2="three.sc" k="20" />
+    <hkern g1="afii10095.sc" g2="seven.sc" k="20" />
+    <hkern g1="afii10095.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10095.sc" g2="one.sc" k="30" />
+    <hkern g1="afii10095.sc" g2="j.sc" k="10" />
+    <hkern g1="afii10095.sc" g2="five.sc" k="20" />
+    <hkern g1="afii10095.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10095.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="afii10095.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="afii10095.sc" u2="&#x2f;" k="40" />
+    <hkern g1="afii10096.sc" g2="two.sc" k="30" />
+    <hkern g1="afii10096.sc" g2="three.sc" k="20" />
+    <hkern g1="afii10096.sc" g2="seven.sc" k="20" />
+    <hkern g1="afii10096.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10096.sc" g2="one.sc" k="30" />
+    <hkern g1="afii10096.sc" g2="j.sc" k="10" />
+    <hkern g1="afii10096.sc" g2="five.sc" k="20" />
+    <hkern g1="afii10096.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10096.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="afii10096.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="afii10096.sc" u2="&#x2f;" k="40" />
+    <hkern g1="afii10098.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10098.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10098.sc" u2="&#x2f;" k="60" />
+    <hkern g1="afii10100.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10100.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="afii10100.sc" u2="&#x2f;" k="60" />
+    <hkern g1="afii10101.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10101.sc" g2="j.sc" k="10" />
+    <hkern g1="afii10101.sc" g2="afii10097.sc" k="10" />
+    <hkern g1="afii10102.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10106.sc" g2="afii10097.sc" k="30" />
+    <hkern g1="afii10106.sc" g2="afii10092.sc" k="60" />
+    <hkern g1="afii10106.sc" g2="afii10089.sc" k="50" />
+    <hkern g1="afii10107.sc" g2="afii10097.sc" k="30" />
+    <hkern g1="afii10107.sc" g2="afii10092.sc" k="60" />
+    <hkern g1="afii10107.sc" g2="afii10089.sc" k="50" />
+    <hkern g1="afii10109.sc" g2="s.sc" k="10" />
+    <hkern g1="afii10109.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="afii10109.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="afii10110.sc" g2="s.sc" k="20" />
+    <hkern g1="afii10110.sc" g2="j.sc" k="10" />
+    <hkern g1="afii10110.sc" g2="afii10097.sc" k="40" />
+    <hkern g1="afii10110.sc" u2="&#x2f;" k="70" />
+    <hkern g1="agrave.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="agrave.alt1" u2="&#x447;" k="20" />
+    <hkern g1="agrave.alt1" u2="&#x442;" k="40" />
+    <hkern g1="agrave.sc" g2="s.sc" k="20" />
+    <hkern g1="agrave.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="agrave.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="amacron.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="amacron.alt1" u2="&#x447;" k="20" />
+    <hkern g1="amacron.alt1" u2="&#x442;" k="40" />
+    <hkern g1="amacron.sc" g2="s.sc" k="20" />
+    <hkern g1="amacron.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="amacron.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="aogonek.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="aogonek.alt1" u2="&#x447;" k="20" />
+    <hkern g1="aogonek.alt1" u2="&#x442;" k="40" />
+    <hkern g1="aogonek.sc" g2="s.sc" k="20" />
+    <hkern g1="aogonek.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="aogonek.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="approxequal.case" g2="twooldstyle" k="10" />
+    <hkern g1="approxequal.case" g2="two.sc" k="40" />
+    <hkern g1="approxequal.case" g2="threeoldstyle" k="20" />
+    <hkern g1="approxequal.case" g2="three.sc" k="20" />
+    <hkern g1="approxequal.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="approxequal.case" g2="seven.sc" k="40" />
+    <hkern g1="approxequal.case" g2="s.sc" k="20" />
+    <hkern g1="approxequal.case" g2="fouroldstyle" k="30" />
+    <hkern g1="approxequal.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="approxequal.case" g2="afii10097.sc" k="30" />
+    <hkern g1="approxequal.case" u2="&#x442;" k="50" />
+    <hkern g1="approxequal.case" u2="&#x37;" k="60" />
+    <hkern g1="aring.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="aring.alt1" u2="&#x447;" k="20" />
+    <hkern g1="aring.alt1" u2="&#x442;" k="40" />
+    <hkern g1="aring.sc" g2="s.sc" k="20" />
+    <hkern g1="aring.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="aring.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="asciitilde.case" g2="twooldstyle" k="10" />
+    <hkern g1="asciitilde.case" g2="two.sc" k="40" />
+    <hkern g1="asciitilde.case" g2="threeoldstyle" k="20" />
+    <hkern g1="asciitilde.case" g2="three.sc" k="20" />
+    <hkern g1="asciitilde.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="asciitilde.case" g2="seven.sc" k="40" />
+    <hkern g1="asciitilde.case" g2="s.sc" k="20" />
+    <hkern g1="asciitilde.case" g2="fouroldstyle" k="30" />
+    <hkern g1="asciitilde.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="asciitilde.case" g2="afii10097.sc" k="30" />
+    <hkern g1="asciitilde.case" u2="&#x442;" k="50" />
+    <hkern g1="asciitilde.case" u2="&#x37;" k="60" />
+    <hkern g1="atilde.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="atilde.alt1" u2="&#x447;" k="20" />
+    <hkern g1="atilde.alt1" u2="&#x442;" k="40" />
+    <hkern g1="atilde.sc" g2="s.sc" k="20" />
+    <hkern g1="atilde.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="atilde.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="b.sc" g2="two.sc" k="40" />
+    <hkern g1="b.sc" g2="three.sc" k="20" />
+    <hkern g1="b.sc" g2="seven.sc" k="10" />
+    <hkern g1="b.sc" g2="s.sc" k="10" />
+    <hkern g1="b.sc" g2="one.sc" k="30" />
+    <hkern g1="b.sc" g2="nine.sc" k="20" />
+    <hkern g1="b.sc" g2="five.sc" k="20" />
+    <hkern g1="b.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="b.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="b.sc" g2="afii10089.sc" k="30" />
+    <hkern g1="b.sc" u2="&#x2f;" k="10" />
+    <hkern g1="bullet.case" g2="twooldstyle" k="10" />
+    <hkern g1="bullet.case" g2="two.sc" k="40" />
+    <hkern g1="bullet.case" g2="threeoldstyle" k="20" />
+    <hkern g1="bullet.case" g2="three.sc" k="20" />
+    <hkern g1="bullet.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="bullet.case" g2="seven.sc" k="40" />
+    <hkern g1="bullet.case" g2="s.sc" k="20" />
+    <hkern g1="bullet.case" g2="fouroldstyle" k="30" />
+    <hkern g1="bullet.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="bullet.case" g2="afii10097.sc" k="30" />
+    <hkern g1="bullet.case" u2="&#x442;" k="50" />
+    <hkern g1="bullet.case" u2="&#x37;" k="60" />
+    <hkern g1="c.sc" g2="s.sc" k="10" />
+    <hkern g1="c.sc" g2="j.sc" k="10" />
+    <hkern g1="c.sc" g2="afii10097.sc" k="10" />
+    <hkern g1="cacute.sc" g2="s.sc" k="10" />
+    <hkern g1="cacute.sc" g2="j.sc" k="10" />
+    <hkern g1="cacute.sc" g2="afii10097.sc" k="10" />
+    <hkern g1="cb.liga" g2="threeoldstyle" k="40" />
+    <hkern g1="cb.liga" g2="sevenoldstyle" k="20" />
+    <hkern g1="cb.liga" u2="&#x44f;" k="10" />
+    <hkern g1="cb.liga" u2="&#x44a;" k="40" />
+    <hkern g1="cb.liga" u2="&#x447;" k="10" />
+    <hkern g1="cb.liga" u2="&#x442;" k="20" />
+    <hkern g1="cb.liga" u2="&#x2f;" k="20" />
+    <hkern g1="ccaron.sc" g2="s.sc" k="10" />
+    <hkern g1="ccaron.sc" g2="j.sc" k="10" />
+    <hkern g1="ccaron.sc" g2="afii10097.sc" k="10" />
+    <hkern g1="ccedilla.sc" g2="s.sc" k="10" />
+    <hkern g1="ccedilla.sc" g2="j.sc" k="10" />
+    <hkern g1="ccedilla.sc" g2="afii10097.sc" k="10" />
+    <hkern g1="ccircumflex.sc" g2="s.sc" k="10" />
+    <hkern g1="ccircumflex.sc" g2="j.sc" k="10" />
+    <hkern g1="ccircumflex.sc" g2="afii10097.sc" k="10" />
+    <hkern g1="cdotaccent.sc" g2="s.sc" k="10" />
+    <hkern g1="cdotaccent.sc" g2="j.sc" k="10" />
+    <hkern g1="cdotaccent.sc" g2="afii10097.sc" k="10" />
+    <hkern g1="centoldstyle" u2="&#x447;" k="20" />
+    <hkern g1="ck.liga" u2="&#x44f;" k="20" />
+    <hkern g1="ck.liga" u2="&#x44a;" k="30" />
+    <hkern g1="ck.liga" u2="&#x447;" k="30" />
+    <hkern g1="ck.liga" u2="&#x442;" k="30" />
+    <hkern g1="copyright.case" g2="threeoldstyle" k="40" />
+    <hkern g1="copyright.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="copyright.case" u2="&#x44f;" k="10" />
+    <hkern g1="copyright.case" u2="&#x44a;" k="40" />
+    <hkern g1="copyright.case" u2="&#x447;" k="10" />
+    <hkern g1="copyright.case" u2="&#x442;" k="20" />
+    <hkern g1="copyright.case" u2="&#x2f;" k="20" />
+    <hkern g1="ct.liga" u2="&#x447;" k="20" />
+    <hkern g1="currency.taboldstyle" g2="twooldstyle" k="10" />
+    <hkern g1="currency.taboldstyle" g2="two.sc" k="40" />
+    <hkern g1="currency.taboldstyle" g2="threeoldstyle" k="20" />
+    <hkern g1="currency.taboldstyle" g2="three.sc" k="20" />
+    <hkern g1="currency.taboldstyle" g2="sevenoldstyle" k="20" />
+    <hkern g1="currency.taboldstyle" g2="seven.sc" k="40" />
+    <hkern g1="currency.taboldstyle" g2="s.sc" k="20" />
+    <hkern g1="currency.taboldstyle" g2="fouroldstyle" k="30" />
+    <hkern g1="currency.taboldstyle" g2="fiveoldstyle" k="10" />
+    <hkern g1="currency.taboldstyle" g2="afii10097.sc" k="30" />
+    <hkern g1="currency.taboldstyle" u2="&#x442;" k="50" />
+    <hkern g1="currency.taboldstyle" u2="&#x37;" k="60" />
+    <hkern g1="d.sc" g2="two.sc" k="30" />
+    <hkern g1="d.sc" g2="three.sc" k="20" />
+    <hkern g1="d.sc" g2="seven.sc" k="20" />
+    <hkern g1="d.sc" g2="s.sc" k="10" />
+    <hkern g1="d.sc" g2="one.sc" k="30" />
+    <hkern g1="d.sc" g2="j.sc" k="10" />
+    <hkern g1="d.sc" g2="five.sc" k="20" />
+    <hkern g1="d.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="d.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="d.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="d.sc" u2="&#x2f;" k="40" />
+    <hkern g1="dcaron.sc" g2="two.sc" k="30" />
+    <hkern g1="dcaron.sc" g2="three.sc" k="20" />
+    <hkern g1="dcaron.sc" g2="seven.sc" k="20" />
+    <hkern g1="dcaron.sc" g2="s.sc" k="10" />
+    <hkern g1="dcaron.sc" g2="one.sc" k="30" />
+    <hkern g1="dcaron.sc" g2="j.sc" k="10" />
+    <hkern g1="dcaron.sc" g2="five.sc" k="20" />
+    <hkern g1="dcaron.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="dcaron.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="dcaron.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="dcaron.sc" u2="&#x2f;" k="40" />
+    <hkern g1="dcroat.sc" g2="two.sc" k="30" />
+    <hkern g1="dcroat.sc" g2="three.sc" k="20" />
+    <hkern g1="dcroat.sc" g2="seven.sc" k="20" />
+    <hkern g1="dcroat.sc" g2="s.sc" k="10" />
+    <hkern g1="dcroat.sc" g2="one.sc" k="30" />
+    <hkern g1="dcroat.sc" g2="j.sc" k="10" />
+    <hkern g1="dcroat.sc" g2="five.sc" k="20" />
+    <hkern g1="dcroat.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="dcroat.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="dcroat.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="dcroat.sc" u2="&#x2f;" k="40" />
+    <hkern g1="divide.case" g2="twooldstyle" k="10" />
+    <hkern g1="divide.case" g2="two.sc" k="40" />
+    <hkern g1="divide.case" g2="threeoldstyle" k="20" />
+    <hkern g1="divide.case" g2="three.sc" k="20" />
+    <hkern g1="divide.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="divide.case" g2="seven.sc" k="40" />
+    <hkern g1="divide.case" g2="s.sc" k="20" />
+    <hkern g1="divide.case" g2="fouroldstyle" k="30" />
+    <hkern g1="divide.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="divide.case" g2="afii10097.sc" k="30" />
+    <hkern g1="divide.case" u2="&#x442;" k="50" />
+    <hkern g1="divide.case" u2="&#x37;" k="60" />
+    <hkern g1="dollar.sc" g2="s.sc" k="10" />
+    <hkern g1="e.sc" g2="s.sc" k="10" />
+    <hkern g1="e.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="e.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="e.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="eacute.sc" g2="s.sc" k="10" />
+    <hkern g1="eacute.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="eacute.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="eacute.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="ebreve.sc" g2="s.sc" k="10" />
+    <hkern g1="ebreve.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="ebreve.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="ebreve.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="ecaron.sc" g2="s.sc" k="10" />
+    <hkern g1="ecaron.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="ecaron.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="ecaron.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="ecircumflex.sc" g2="s.sc" k="10" />
+    <hkern g1="ecircumflex.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="ecircumflex.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="ecircumflex.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="edieresis.sc" g2="s.sc" k="10" />
+    <hkern g1="edieresis.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="edieresis.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="edieresis.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="edotaccent.sc" g2="s.sc" k="10" />
+    <hkern g1="edotaccent.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="edotaccent.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="edotaccent.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="egrave.sc" g2="s.sc" k="10" />
+    <hkern g1="egrave.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="egrave.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="egrave.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="eight.denominator" u2="&#x2044;" k="-60" />
+    <hkern g1="eight.numerator" u2="&#x2044;" k="90" />
+    <hkern g1="eight.sc" u2="&#x2122;" k="30" />
+    <hkern g1="eight.sc" u2="&#x201d;" k="30" />
+    <hkern g1="eight.sc" u2="&#x201c;" k="30" />
+    <hkern g1="eight.sc" u2="&#x2019;" k="30" />
+    <hkern g1="eight.sc" u2="&#x2018;" k="30" />
+    <hkern g1="eight.sc" u2="&#x2c9;" k="30" />
+    <hkern g1="eight.sc" u2="&#xba;" k="30" />
+    <hkern g1="eight.sc" u2="&#xb0;" k="30" />
+    <hkern g1="eight.sc" u2="&#xae;" k="30" />
+    <hkern g1="eight.sc" u2="&#xaa;" k="30" />
+    <hkern g1="eight.sc" u2="^" k="30" />
+    <hkern g1="eight.sc" u2="&#x2a;" k="30" />
+    <hkern g1="eight.sc" u2="&#x27;" k="30" />
+    <hkern g1="eight.sc" u2="&#x22;" k="30" />
+    <hkern g1="eight.sc" g2="two.sc" k="30" />
+    <hkern g1="eight.sc" g2="three.sc" k="20" />
+    <hkern g1="eight.sc" g2="seven.sc" k="10" />
+    <hkern g1="eight.sc" g2="one.sc" k="20" />
+    <hkern g1="eight.sc" g2="nine.sc" k="30" />
+    <hkern g1="eight.sc" g2="five.sc" k="10" />
+    <hkern g1="eightoldstyle" u2="&#x2026;" k="20" />
+    <hkern g1="eightoldstyle" u2="&#x2025;" k="20" />
+    <hkern g1="eightoldstyle" u2="&#x2024;" k="20" />
+    <hkern g1="eightoldstyle" u2="&#x201e;" k="20" />
+    <hkern g1="eightoldstyle" u2="&#x201a;" k="20" />
+    <hkern g1="eightoldstyle" u2="_" k="20" />
+    <hkern g1="eightoldstyle" u2="&#x2e;" k="20" />
+    <hkern g1="eightoldstyle" u2="&#x2c;" k="20" />
+    <hkern g1="eightoldstyle" g2="threeoldstyle" k="10" />
+    <hkern g1="eightoldstyle" g2="sevenoldstyle" k="20" />
+    <hkern g1="eightoldstyle" g2="nineoldstyle" k="20" />
+    <hkern g1="emacron.sc" g2="s.sc" k="10" />
+    <hkern g1="emacron.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="emacron.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="emacron.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="emdash.case" g2="twooldstyle" k="10" />
+    <hkern g1="emdash.case" g2="two.sc" k="40" />
+    <hkern g1="emdash.case" g2="threeoldstyle" k="20" />
+    <hkern g1="emdash.case" g2="three.sc" k="20" />
+    <hkern g1="emdash.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="emdash.case" g2="seven.sc" k="40" />
+    <hkern g1="emdash.case" g2="s.sc" k="20" />
+    <hkern g1="emdash.case" g2="fouroldstyle" k="30" />
+    <hkern g1="emdash.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="emdash.case" g2="afii10097.sc" k="30" />
+    <hkern g1="emdash.case" u2="&#x442;" k="50" />
+    <hkern g1="emdash.case" u2="&#x37;" k="60" />
+    <hkern g1="endash.case" g2="twooldstyle" k="10" />
+    <hkern g1="endash.case" g2="two.sc" k="40" />
+    <hkern g1="endash.case" g2="threeoldstyle" k="20" />
+    <hkern g1="endash.case" g2="three.sc" k="20" />
+    <hkern g1="endash.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="endash.case" g2="seven.sc" k="40" />
+    <hkern g1="endash.case" g2="s.sc" k="20" />
+    <hkern g1="endash.case" g2="fouroldstyle" k="30" />
+    <hkern g1="endash.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="endash.case" g2="afii10097.sc" k="30" />
+    <hkern g1="endash.case" u2="&#x442;" k="50" />
+    <hkern g1="endash.case" u2="&#x37;" k="60" />
+    <hkern g1="eogonek.sc" g2="s.sc" k="10" />
+    <hkern g1="eogonek.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="eogonek.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="eogonek.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="equal.case" g2="twooldstyle" k="10" />
+    <hkern g1="equal.case" g2="two.sc" k="40" />
+    <hkern g1="equal.case" g2="threeoldstyle" k="20" />
+    <hkern g1="equal.case" g2="three.sc" k="20" />
+    <hkern g1="equal.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="equal.case" g2="seven.sc" k="40" />
+    <hkern g1="equal.case" g2="s.sc" k="20" />
+    <hkern g1="equal.case" g2="fouroldstyle" k="30" />
+    <hkern g1="equal.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="equal.case" g2="afii10097.sc" k="30" />
+    <hkern g1="equal.case" u2="&#x442;" k="50" />
+    <hkern g1="equal.case" u2="&#x37;" k="60" />
+    <hkern g1="eth.sc" g2="two.sc" k="30" />
+    <hkern g1="eth.sc" g2="three.sc" k="20" />
+    <hkern g1="eth.sc" g2="seven.sc" k="20" />
+    <hkern g1="eth.sc" g2="s.sc" k="10" />
+    <hkern g1="eth.sc" g2="one.sc" k="30" />
+    <hkern g1="eth.sc" g2="j.sc" k="10" />
+    <hkern g1="eth.sc" g2="five.sc" k="20" />
+    <hkern g1="eth.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="eth.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="eth.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="eth.sc" u2="&#x2f;" k="40" />
+    <hkern g1="f.sc" g2="zero.slash.sc" k="10" />
+    <hkern g1="f.sc" g2="zero.sc" k="10" />
+    <hkern g1="f.sc" g2="zdotaccent.sc" k="10" />
+    <hkern g1="f.sc" g2="zcaron.sc" k="10" />
+    <hkern g1="f.sc" g2="zacute.sc" k="10" />
+    <hkern g1="f.sc" g2="z.sc" k="10" />
+    <hkern g1="f.sc" g2="x.sc" k="10" />
+    <hkern g1="f.sc" g2="utilde.sc" k="10" />
+    <hkern g1="f.sc" g2="uring.sc" k="10" />
+    <hkern g1="f.sc" g2="uogonek.sc" k="10" />
+    <hkern g1="f.sc" g2="uni0217.sc" k="10" />
+    <hkern g1="f.sc" g2="uni0215.sc" k="10" />
+    <hkern g1="f.sc" g2="uni020F.sc" k="10" />
+    <hkern g1="f.sc" g2="uni020D.sc" k="10" />
+    <hkern g1="f.sc" g2="uni0203.sc" k="30" />
+    <hkern g1="f.sc" g2="uni0201.sc" k="30" />
+    <hkern g1="f.sc" g2="umacron.sc" k="10" />
+    <hkern g1="f.sc" g2="uhungarumlaut.sc" k="10" />
+    <hkern g1="f.sc" g2="ugrave.sc" k="10" />
+    <hkern g1="f.sc" g2="udieresis.sc" k="10" />
+    <hkern g1="f.sc" g2="ucircumflex.sc" k="10" />
+    <hkern g1="f.sc" g2="ubreve.sc" k="10" />
+    <hkern g1="f.sc" g2="uacute.sc" k="10" />
+    <hkern g1="f.sc" g2="u.sc" k="10" />
+    <hkern g1="f.sc" g2="six.sc" k="10" />
+    <hkern g1="f.sc" g2="q.sc" k="10" />
+    <hkern g1="f.sc" g2="otilde.sc" k="10" />
+    <hkern g1="f.sc" g2="oslash.sc" k="10" />
+    <hkern g1="f.sc" g2="omacron.sc" k="10" />
+    <hkern g1="f.sc" g2="ohungarumlaut.sc" k="10" />
+    <hkern g1="f.sc" g2="ograve.sc" k="10" />
+    <hkern g1="f.sc" g2="oe.sc" k="10" />
+    <hkern g1="f.sc" g2="odieresis.sc" k="10" />
+    <hkern g1="f.sc" g2="ocircumflex.sc" k="10" />
+    <hkern g1="f.sc" g2="obreve.sc" k="10" />
+    <hkern g1="f.sc" g2="oacute.sc" k="10" />
+    <hkern g1="f.sc" g2="o.sc" k="10" />
+    <hkern g1="f.sc" g2="gdotaccent.sc" k="10" />
+    <hkern g1="f.sc" g2="gcommaaccent.sc" k="10" />
+    <hkern g1="f.sc" g2="gcircumflex.sc" k="10" />
+    <hkern g1="f.sc" g2="gbreve.sc" k="10" />
+    <hkern g1="f.sc" g2="g.sc" k="10" />
+    <hkern g1="f.sc" g2="cdotaccent.sc" k="10" />
+    <hkern g1="f.sc" g2="ccircumflex.sc" k="10" />
+    <hkern g1="f.sc" g2="ccedilla.sc" k="10" />
+    <hkern g1="f.sc" g2="ccaron.sc" k="10" />
+    <hkern g1="f.sc" g2="cacute.sc" k="10" />
+    <hkern g1="f.sc" g2="c.sc" k="10" />
+    <hkern g1="f.sc" g2="atilde.sc" k="30" />
+    <hkern g1="f.sc" g2="aring.sc" k="30" />
+    <hkern g1="f.sc" g2="aogonek.sc" k="30" />
+    <hkern g1="f.sc" g2="amacron.sc" k="30" />
+    <hkern g1="f.sc" g2="agrave.sc" k="30" />
+    <hkern g1="f.sc" g2="afii10101.sc" k="10" />
+    <hkern g1="f.sc" g2="afii10087.sc" k="10" />
+    <hkern g1="f.sc" g2="afii10086.sc" k="10" />
+    <hkern g1="f.sc" g2="afii10083.sc" k="10" />
+    <hkern g1="f.sc" g2="afii10080.sc" k="10" />
+    <hkern g1="f.sc" g2="afii10072.sc" k="10" />
+    <hkern g1="f.sc" g2="afii10065.sc" k="30" />
+    <hkern g1="f.sc" g2="ae.sc" k="30" />
+    <hkern g1="f.sc" g2="adieresis.sc" k="30" />
+    <hkern g1="f.sc" g2="acircumflex.sc" k="30" />
+    <hkern g1="f.sc" g2="abreve.sc" k="30" />
+    <hkern g1="f.sc" g2="aacute.sc" k="30" />
+    <hkern g1="f.sc" g2="a.sc" k="30" />
+    <hkern g1="f.sc" g2="Euro.sc" k="10" />
+    <hkern g1="f.sc" u2="&#x2026;" k="40" />
+    <hkern g1="f.sc" u2="&#x2025;" k="40" />
+    <hkern g1="f.sc" u2="&#x2024;" k="40" />
+    <hkern g1="f.sc" u2="&#x201e;" k="40" />
+    <hkern g1="f.sc" u2="&#x201a;" k="40" />
+    <hkern g1="f.sc" u2="_" k="40" />
+    <hkern g1="f.sc" u2="&#x2e;" k="40" />
+    <hkern g1="f.sc" u2="&#x2c;" k="40" />
+    <hkern g1="f.sc" g2="s.sc" k="10" />
+    <hkern g1="f.sc" g2="j.sc" k="10" />
+    <hkern g1="fb.liga" g2="threeoldstyle" k="40" />
+    <hkern g1="fb.liga" g2="sevenoldstyle" k="20" />
+    <hkern g1="fb.liga" u2="&#x44f;" k="10" />
+    <hkern g1="fb.liga" u2="&#x44a;" k="40" />
+    <hkern g1="fb.liga" u2="&#x447;" k="10" />
+    <hkern g1="fb.liga" u2="&#x442;" k="20" />
+    <hkern g1="fb.liga" u2="&#x2f;" k="20" />
+    <hkern g1="ff.liga" u2="&#x2f;" k="40" />
+    <hkern g1="five.denominator" u2="&#x2044;" k="-50" />
+    <hkern g1="five.numerator" u2="&#x2044;" k="70" />
+    <hkern g1="five.sc" g2="zero.slash.sc" k="20" />
+    <hkern g1="five.sc" g2="zero.sc" k="20" />
+    <hkern g1="five.sc" g2="uni020F.sc" k="20" />
+    <hkern g1="five.sc" g2="uni020D.sc" k="20" />
+    <hkern g1="five.sc" g2="six.sc" k="20" />
+    <hkern g1="five.sc" g2="q.sc" k="20" />
+    <hkern g1="five.sc" g2="otilde.sc" k="20" />
+    <hkern g1="five.sc" g2="oslash.sc" k="20" />
+    <hkern g1="five.sc" g2="omacron.sc" k="20" />
+    <hkern g1="five.sc" g2="ohungarumlaut.sc" k="20" />
+    <hkern g1="five.sc" g2="ograve.sc" k="20" />
+    <hkern g1="five.sc" g2="oe.sc" k="20" />
+    <hkern g1="five.sc" g2="odieresis.sc" k="20" />
+    <hkern g1="five.sc" g2="ocircumflex.sc" k="20" />
+    <hkern g1="five.sc" g2="obreve.sc" k="20" />
+    <hkern g1="five.sc" g2="oacute.sc" k="20" />
+    <hkern g1="five.sc" g2="o.sc" k="20" />
+    <hkern g1="five.sc" g2="gdotaccent.sc" k="20" />
+    <hkern g1="five.sc" g2="gcommaaccent.sc" k="20" />
+    <hkern g1="five.sc" g2="gcircumflex.sc" k="20" />
+    <hkern g1="five.sc" g2="gbreve.sc" k="20" />
+    <hkern g1="five.sc" g2="g.sc" k="20" />
+    <hkern g1="five.sc" g2="cdotaccent.sc" k="20" />
+    <hkern g1="five.sc" g2="ccircumflex.sc" k="20" />
+    <hkern g1="five.sc" g2="ccedilla.sc" k="20" />
+    <hkern g1="five.sc" g2="ccaron.sc" k="20" />
+    <hkern g1="five.sc" g2="cacute.sc" k="20" />
+    <hkern g1="five.sc" g2="c.sc" k="20" />
+    <hkern g1="five.sc" g2="afii10101.sc" k="20" />
+    <hkern g1="five.sc" g2="afii10086.sc" k="20" />
+    <hkern g1="five.sc" g2="afii10083.sc" k="20" />
+    <hkern g1="five.sc" g2="afii10080.sc" k="20" />
+    <hkern g1="five.sc" g2="Euro.sc" k="20" />
+    <hkern g1="five.sc" g2="two.sc" k="10" />
+    <hkern g1="five.sc" g2="three.sc" k="10" />
+    <hkern g1="five.sc" g2="seven.sc" k="20" />
+    <hkern g1="five.sc" g2="one.sc" k="20" />
+    <hkern g1="five.sc" g2="nine.sc" k="30" />
+    <hkern g1="five.sc" g2="five.sc" k="20" />
+    <hkern g1="five.sc" g2="eight.sc" k="10" />
+    <hkern g1="fiveoldstyle" g2="uni00AD.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="plusminus.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="plus.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="periodcentered.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="oneoldstyle" k="20" />
+    <hkern g1="fiveoldstyle" g2="numbersign.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="notequal.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="multiply.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="minus.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="logicalnot.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="lessequal.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="less.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="hyphen.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="guilsinglright.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="guilsinglleft.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="guillemotright.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="guillemotleft.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="greaterequal.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="greater.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="equal.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="endash.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="emdash.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="divide.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="currency.taboldstyle" k="10" />
+    <hkern g1="fiveoldstyle" g2="bullet.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="asciitilde.case" k="10" />
+    <hkern g1="fiveoldstyle" g2="approxequal.case" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2265;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2264;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2260;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2248;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x221e;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2212;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2192;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2190;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x203a;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2039;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2014;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2013;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x491;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x45f;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x45c;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x45a;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x453;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x44e;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x44c;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x44b;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x449;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x448;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x446;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x440;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x43f;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x43d;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x43c;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x43a;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x439;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x438;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x433;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x432;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x237;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x159;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x157;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x155;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x148;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x146;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x144;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x138;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x131;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#xf7;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#xf1;" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#xd7;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#xbb;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#xb7;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#xb1;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#xad;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#xac;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#xab;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#xa4;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x7e;" k="10" />
+    <hkern g1="fiveoldstyle" u2="r" k="20" />
+    <hkern g1="fiveoldstyle" u2="p" k="20" />
+    <hkern g1="fiveoldstyle" u2="n" k="20" />
+    <hkern g1="fiveoldstyle" u2="m" k="20" />
+    <hkern g1="fiveoldstyle" u2="&#x3e;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x3d;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x3c;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2d;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x2b;" k="10" />
+    <hkern g1="fiveoldstyle" u2="&#x23;" k="10" />
+    <hkern g1="fiveoldstyle" g2="threeoldstyle" k="10" />
+    <hkern g1="fiveoldstyle" g2="sevenoldstyle" k="10" />
+    <hkern g1="fiveoldstyle" g2="fiveoldstyle" k="20" />
+    <hkern g1="fk.liga" u2="&#x44f;" k="20" />
+    <hkern g1="fk.liga" u2="&#x44a;" k="30" />
+    <hkern g1="fk.liga" u2="&#x447;" k="30" />
+    <hkern g1="fk.liga" u2="&#x442;" k="30" />
+    <hkern g1="four.denominator" u2="&#x2044;" k="-60" />
+    <hkern g1="four.numerator" u2="&#x2044;" k="80" />
+    <hkern g1="four.sc" g2="two.sc" k="20" />
+    <hkern g1="four.sc" g2="one.sc" k="20" />
+    <hkern g1="four.sc" g2="nine.sc" k="30" />
+    <hkern g1="fouroldstyle" g2="uni00AD.case" k="10" />
+    <hkern g1="fouroldstyle" g2="plusminus.case" k="10" />
+    <hkern g1="fouroldstyle" g2="plus.case" k="10" />
+    <hkern g1="fouroldstyle" g2="periodcentered.case" k="10" />
+    <hkern g1="fouroldstyle" g2="oneoldstyle" k="30" />
+    <hkern g1="fouroldstyle" g2="numbersign.case" k="10" />
+    <hkern g1="fouroldstyle" g2="notequal.case" k="10" />
+    <hkern g1="fouroldstyle" g2="multiply.case" k="10" />
+    <hkern g1="fouroldstyle" g2="minus.case" k="10" />
+    <hkern g1="fouroldstyle" g2="logicalnot.case" k="10" />
+    <hkern g1="fouroldstyle" g2="lessequal.case" k="10" />
+    <hkern g1="fouroldstyle" g2="less.case" k="10" />
+    <hkern g1="fouroldstyle" g2="hyphen.case" k="10" />
+    <hkern g1="fouroldstyle" g2="guilsinglright.case" k="10" />
+    <hkern g1="fouroldstyle" g2="guilsinglleft.case" k="10" />
+    <hkern g1="fouroldstyle" g2="guillemotright.case" k="10" />
+    <hkern g1="fouroldstyle" g2="guillemotleft.case" k="10" />
+    <hkern g1="fouroldstyle" g2="greaterequal.case" k="10" />
+    <hkern g1="fouroldstyle" g2="greater.case" k="10" />
+    <hkern g1="fouroldstyle" g2="equal.case" k="10" />
+    <hkern g1="fouroldstyle" g2="endash.case" k="10" />
+    <hkern g1="fouroldstyle" g2="emdash.case" k="10" />
+    <hkern g1="fouroldstyle" g2="divide.case" k="10" />
+    <hkern g1="fouroldstyle" g2="currency.taboldstyle" k="10" />
+    <hkern g1="fouroldstyle" g2="bullet.case" k="10" />
+    <hkern g1="fouroldstyle" g2="asciitilde.case" k="10" />
+    <hkern g1="fouroldstyle" g2="approxequal.case" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2265;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2264;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2260;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2248;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x221e;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2212;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2192;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2190;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x203a;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2039;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2014;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2013;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x491;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x45f;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x45c;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x45a;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x453;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x44e;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x44c;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x44b;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x449;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x448;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x446;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x440;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x43f;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x43d;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x43c;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x43a;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x439;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x438;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x433;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x432;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x237;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x159;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x157;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x155;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x148;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x146;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x144;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x138;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x131;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#xf7;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#xf1;" k="30" />
+    <hkern g1="fouroldstyle" u2="&#xd7;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#xbb;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#xb7;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#xb1;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#xad;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#xac;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#xab;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#xa4;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x7e;" k="10" />
+    <hkern g1="fouroldstyle" u2="r" k="30" />
+    <hkern g1="fouroldstyle" u2="p" k="30" />
+    <hkern g1="fouroldstyle" u2="n" k="30" />
+    <hkern g1="fouroldstyle" u2="m" k="30" />
+    <hkern g1="fouroldstyle" u2="&#x3e;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x3d;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x3c;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2d;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x2b;" k="10" />
+    <hkern g1="fouroldstyle" u2="&#x23;" k="10" />
+    <hkern g1="fouroldstyle" g2="sevenoldstyle" k="20" />
+    <hkern g1="fouroldstyle" g2="eightoldstyle" k="20" />
+    <hkern g1="ft.liga" u2="&#x447;" k="20" />
+    <hkern g1="g.sc" g2="yen.sc" k="10" />
+    <hkern g1="g.sc" g2="ydieresis.sc" k="20" />
+    <hkern g1="g.sc" g2="ycircumflex.sc" k="20" />
+    <hkern g1="g.sc" g2="yacute.sc" k="20" />
+    <hkern g1="g.sc" g2="y.sc" k="20" />
+    <hkern g1="g.sc" g2="wcircumflex.sc" k="10" />
+    <hkern g1="g.sc" g2="w.sc" k="10" />
+    <hkern g1="g.sc" g2="v.sc" k="10" />
+    <hkern g1="g.sc" g2="uni021B.sc" k="10" />
+    <hkern g1="g.sc" g2="tcommaaccent.sc" k="10" />
+    <hkern g1="g.sc" g2="tcaron.sc" k="10" />
+    <hkern g1="g.sc" g2="t.sc" k="10" />
+    <hkern g1="g.sc" g2="afii10110.sc" k="10" />
+    <hkern g1="g.sc" g2="afii10108.sc" k="10" />
+    <hkern g1="g.sc" g2="afii10099.sc" k="10" />
+    <hkern g1="g.sc" g2="afii10085.sc" k="10" />
+    <hkern g1="g.sc" g2="afii10084.sc" k="10" />
+    <hkern g1="greater.case" g2="twooldstyle" k="10" />
+    <hkern g1="greater.case" g2="two.sc" k="40" />
+    <hkern g1="greater.case" g2="threeoldstyle" k="20" />
+    <hkern g1="greater.case" g2="three.sc" k="20" />
+    <hkern g1="greater.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="greater.case" g2="seven.sc" k="40" />
+    <hkern g1="greater.case" g2="s.sc" k="20" />
+    <hkern g1="greater.case" g2="fouroldstyle" k="30" />
+    <hkern g1="greater.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="greater.case" g2="afii10097.sc" k="30" />
+    <hkern g1="greater.case" u2="&#x442;" k="50" />
+    <hkern g1="greater.case" u2="&#x37;" k="60" />
+    <hkern g1="greaterequal.case" g2="twooldstyle" k="10" />
+    <hkern g1="greaterequal.case" g2="two.sc" k="40" />
+    <hkern g1="greaterequal.case" g2="threeoldstyle" k="20" />
+    <hkern g1="greaterequal.case" g2="three.sc" k="20" />
+    <hkern g1="greaterequal.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="greaterequal.case" g2="seven.sc" k="40" />
+    <hkern g1="greaterequal.case" g2="s.sc" k="20" />
+    <hkern g1="greaterequal.case" g2="fouroldstyle" k="30" />
+    <hkern g1="greaterequal.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="greaterequal.case" g2="afii10097.sc" k="30" />
+    <hkern g1="greaterequal.case" u2="&#x442;" k="50" />
+    <hkern g1="greaterequal.case" u2="&#x37;" k="60" />
+    <hkern g1="guillemotleft.case" g2="twooldstyle" k="10" />
+    <hkern g1="guillemotleft.case" g2="two.sc" k="40" />
+    <hkern g1="guillemotleft.case" g2="threeoldstyle" k="20" />
+    <hkern g1="guillemotleft.case" g2="three.sc" k="20" />
+    <hkern g1="guillemotleft.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="guillemotleft.case" g2="seven.sc" k="40" />
+    <hkern g1="guillemotleft.case" g2="s.sc" k="20" />
+    <hkern g1="guillemotleft.case" g2="fouroldstyle" k="30" />
+    <hkern g1="guillemotleft.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="guillemotleft.case" g2="afii10097.sc" k="30" />
+    <hkern g1="guillemotleft.case" u2="&#x442;" k="50" />
+    <hkern g1="guillemotleft.case" u2="&#x37;" k="60" />
+    <hkern g1="guillemotright.case" g2="twooldstyle" k="10" />
+    <hkern g1="guillemotright.case" g2="two.sc" k="40" />
+    <hkern g1="guillemotright.case" g2="threeoldstyle" k="20" />
+    <hkern g1="guillemotright.case" g2="three.sc" k="20" />
+    <hkern g1="guillemotright.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="guillemotright.case" g2="seven.sc" k="40" />
+    <hkern g1="guillemotright.case" g2="s.sc" k="20" />
+    <hkern g1="guillemotright.case" g2="fouroldstyle" k="30" />
+    <hkern g1="guillemotright.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="guillemotright.case" g2="afii10097.sc" k="30" />
+    <hkern g1="guillemotright.case" u2="&#x442;" k="50" />
+    <hkern g1="guillemotright.case" u2="&#x37;" k="60" />
+    <hkern g1="guilsinglleft.case" g2="twooldstyle" k="10" />
+    <hkern g1="guilsinglleft.case" g2="two.sc" k="40" />
+    <hkern g1="guilsinglleft.case" g2="threeoldstyle" k="20" />
+    <hkern g1="guilsinglleft.case" g2="three.sc" k="20" />
+    <hkern g1="guilsinglleft.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="guilsinglleft.case" g2="seven.sc" k="40" />
+    <hkern g1="guilsinglleft.case" g2="s.sc" k="20" />
+    <hkern g1="guilsinglleft.case" g2="fouroldstyle" k="30" />
+    <hkern g1="guilsinglleft.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="guilsinglleft.case" g2="afii10097.sc" k="30" />
+    <hkern g1="guilsinglleft.case" u2="&#x442;" k="50" />
+    <hkern g1="guilsinglleft.case" u2="&#x37;" k="60" />
+    <hkern g1="guilsinglright.case" g2="twooldstyle" k="10" />
+    <hkern g1="guilsinglright.case" g2="two.sc" k="40" />
+    <hkern g1="guilsinglright.case" g2="threeoldstyle" k="20" />
+    <hkern g1="guilsinglright.case" g2="three.sc" k="20" />
+    <hkern g1="guilsinglright.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="guilsinglright.case" g2="seven.sc" k="40" />
+    <hkern g1="guilsinglright.case" g2="s.sc" k="20" />
+    <hkern g1="guilsinglright.case" g2="fouroldstyle" k="30" />
+    <hkern g1="guilsinglright.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="guilsinglright.case" g2="afii10097.sc" k="30" />
+    <hkern g1="guilsinglright.case" u2="&#x442;" k="50" />
+    <hkern g1="guilsinglright.case" u2="&#x37;" k="60" />
+    <hkern g1="hyphen.case" g2="twooldstyle" k="10" />
+    <hkern g1="hyphen.case" g2="two.sc" k="40" />
+    <hkern g1="hyphen.case" g2="threeoldstyle" k="20" />
+    <hkern g1="hyphen.case" g2="three.sc" k="20" />
+    <hkern g1="hyphen.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="hyphen.case" g2="seven.sc" k="40" />
+    <hkern g1="hyphen.case" g2="s.sc" k="20" />
+    <hkern g1="hyphen.case" g2="fouroldstyle" k="30" />
+    <hkern g1="hyphen.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="hyphen.case" g2="afii10097.sc" k="30" />
+    <hkern g1="hyphen.case" u2="&#x442;" k="50" />
+    <hkern g1="hyphen.case" u2="&#x37;" k="60" />
+    <hkern g1="j.sc" g2="zero.slash.sc" k="10" />
+    <hkern g1="j.sc" g2="zero.sc" k="10" />
+    <hkern g1="j.sc" g2="yen.sc" k="10" />
+    <hkern g1="j.sc" g2="ydieresis.sc" k="10" />
+    <hkern g1="j.sc" g2="ycircumflex.sc" k="10" />
+    <hkern g1="j.sc" g2="yacute.sc" k="10" />
+    <hkern g1="j.sc" g2="y.sc" k="10" />
+    <hkern g1="j.sc" g2="wcircumflex.sc" k="10" />
+    <hkern g1="j.sc" g2="w.sc" k="10" />
+    <hkern g1="j.sc" g2="v.sc" k="10" />
+    <hkern g1="j.sc" g2="uni020F.sc" k="10" />
+    <hkern g1="j.sc" g2="uni020D.sc" k="10" />
+    <hkern g1="j.sc" g2="uni0203.sc" k="10" />
+    <hkern g1="j.sc" g2="uni0201.sc" k="10" />
+    <hkern g1="j.sc" g2="six.sc" k="10" />
+    <hkern g1="j.sc" g2="q.sc" k="10" />
+    <hkern g1="j.sc" g2="otilde.sc" k="10" />
+    <hkern g1="j.sc" g2="oslash.sc" k="10" />
+    <hkern g1="j.sc" g2="omacron.sc" k="10" />
+    <hkern g1="j.sc" g2="ohungarumlaut.sc" k="10" />
+    <hkern g1="j.sc" g2="ograve.sc" k="10" />
+    <hkern g1="j.sc" g2="oe.sc" k="10" />
+    <hkern g1="j.sc" g2="odieresis.sc" k="10" />
+    <hkern g1="j.sc" g2="ocircumflex.sc" k="10" />
+    <hkern g1="j.sc" g2="obreve.sc" k="10" />
+    <hkern g1="j.sc" g2="oacute.sc" k="10" />
+    <hkern g1="j.sc" g2="o.sc" k="10" />
+    <hkern g1="j.sc" g2="gdotaccent.sc" k="10" />
+    <hkern g1="j.sc" g2="gcommaaccent.sc" k="10" />
+    <hkern g1="j.sc" g2="gcircumflex.sc" k="10" />
+    <hkern g1="j.sc" g2="gbreve.sc" k="10" />
+    <hkern g1="j.sc" g2="g.sc" k="10" />
+    <hkern g1="j.sc" g2="cdotaccent.sc" k="10" />
+    <hkern g1="j.sc" g2="ccircumflex.sc" k="10" />
+    <hkern g1="j.sc" g2="ccedilla.sc" k="10" />
+    <hkern g1="j.sc" g2="ccaron.sc" k="10" />
+    <hkern g1="j.sc" g2="cacute.sc" k="10" />
+    <hkern g1="j.sc" g2="c.sc" k="10" />
+    <hkern g1="j.sc" g2="atilde.sc" k="10" />
+    <hkern g1="j.sc" g2="aring.sc" k="10" />
+    <hkern g1="j.sc" g2="aogonek.sc" k="10" />
+    <hkern g1="j.sc" g2="amacron.sc" k="10" />
+    <hkern g1="j.sc" g2="agrave.sc" k="10" />
+    <hkern g1="j.sc" g2="afii10110.sc" k="10" />
+    <hkern g1="j.sc" g2="afii10101.sc" k="10" />
+    <hkern g1="j.sc" g2="afii10086.sc" k="10" />
+    <hkern g1="j.sc" g2="afii10085.sc" k="10" />
+    <hkern g1="j.sc" g2="afii10083.sc" k="10" />
+    <hkern g1="j.sc" g2="afii10080.sc" k="10" />
+    <hkern g1="j.sc" g2="afii10065.sc" k="10" />
+    <hkern g1="j.sc" g2="ae.sc" k="10" />
+    <hkern g1="j.sc" g2="adieresis.sc" k="10" />
+    <hkern g1="j.sc" g2="acircumflex.sc" k="10" />
+    <hkern g1="j.sc" g2="abreve.sc" k="10" />
+    <hkern g1="j.sc" g2="aacute.sc" k="10" />
+    <hkern g1="j.sc" g2="a.sc" k="10" />
+    <hkern g1="j.sc" g2="Euro.sc" k="10" />
+    <hkern g1="k.sc" g2="s.sc" k="10" />
+    <hkern g1="k.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="k.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="kcommaaccent.sc" g2="s.sc" k="10" />
+    <hkern g1="kcommaaccent.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="kcommaaccent.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="kgreenlandic.sc" g2="s.sc" k="10" />
+    <hkern g1="kgreenlandic.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="kgreenlandic.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="l.sc" g2="s.sc" k="10" />
+    <hkern g1="lacute.sc" g2="s.sc" k="10" />
+    <hkern g1="lcaron.sc" g2="s.sc" k="10" />
+    <hkern g1="lcommaaccent.sc" g2="s.sc" k="10" />
+    <hkern g1="ldot.sc" g2="s.sc" k="10" />
+    <hkern g1="less.case" g2="twooldstyle" k="10" />
+    <hkern g1="less.case" g2="two.sc" k="40" />
+    <hkern g1="less.case" g2="threeoldstyle" k="20" />
+    <hkern g1="less.case" g2="three.sc" k="20" />
+    <hkern g1="less.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="less.case" g2="seven.sc" k="40" />
+    <hkern g1="less.case" g2="s.sc" k="20" />
+    <hkern g1="less.case" g2="fouroldstyle" k="30" />
+    <hkern g1="less.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="less.case" g2="afii10097.sc" k="30" />
+    <hkern g1="less.case" u2="&#x442;" k="50" />
+    <hkern g1="less.case" u2="&#x37;" k="60" />
+    <hkern g1="lessequal.case" g2="twooldstyle" k="10" />
+    <hkern g1="lessequal.case" g2="two.sc" k="40" />
+    <hkern g1="lessequal.case" g2="threeoldstyle" k="20" />
+    <hkern g1="lessequal.case" g2="three.sc" k="20" />
+    <hkern g1="lessequal.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="lessequal.case" g2="seven.sc" k="40" />
+    <hkern g1="lessequal.case" g2="s.sc" k="20" />
+    <hkern g1="lessequal.case" g2="fouroldstyle" k="30" />
+    <hkern g1="lessequal.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="lessequal.case" g2="afii10097.sc" k="30" />
+    <hkern g1="lessequal.case" u2="&#x442;" k="50" />
+    <hkern g1="lessequal.case" u2="&#x37;" k="60" />
+    <hkern g1="logicalnot.case" g2="twooldstyle" k="10" />
+    <hkern g1="logicalnot.case" g2="two.sc" k="40" />
+    <hkern g1="logicalnot.case" g2="threeoldstyle" k="20" />
+    <hkern g1="logicalnot.case" g2="three.sc" k="20" />
+    <hkern g1="logicalnot.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="logicalnot.case" g2="seven.sc" k="40" />
+    <hkern g1="logicalnot.case" g2="s.sc" k="20" />
+    <hkern g1="logicalnot.case" g2="fouroldstyle" k="30" />
+    <hkern g1="logicalnot.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="logicalnot.case" g2="afii10097.sc" k="30" />
+    <hkern g1="logicalnot.case" u2="&#x442;" k="50" />
+    <hkern g1="logicalnot.case" u2="&#x37;" k="60" />
+    <hkern g1="lsb.liga" g2="threeoldstyle" k="40" />
+    <hkern g1="lsb.liga" g2="sevenoldstyle" k="20" />
+    <hkern g1="lsb.liga" u2="&#x44f;" k="10" />
+    <hkern g1="lsb.liga" u2="&#x44a;" k="40" />
+    <hkern g1="lsb.liga" u2="&#x447;" k="10" />
+    <hkern g1="lsb.liga" u2="&#x442;" k="20" />
+    <hkern g1="lsb.liga" u2="&#x2f;" k="20" />
+    <hkern g1="lsk.liga" u2="&#x44f;" k="20" />
+    <hkern g1="lsk.liga" u2="&#x44a;" k="30" />
+    <hkern g1="lsk.liga" u2="&#x447;" k="30" />
+    <hkern g1="lsk.liga" u2="&#x442;" k="30" />
+    <hkern g1="lslash.sc" g2="s.sc" k="10" />
+    <hkern g1="lsls.liga" u2="&#x2f;" k="40" />
+    <hkern g1="lst.liga" u2="&#x447;" k="20" />
+    <hkern g1="minus.case" g2="twooldstyle" k="10" />
+    <hkern g1="minus.case" g2="two.sc" k="40" />
+    <hkern g1="minus.case" g2="threeoldstyle" k="20" />
+    <hkern g1="minus.case" g2="three.sc" k="20" />
+    <hkern g1="minus.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="minus.case" g2="seven.sc" k="40" />
+    <hkern g1="minus.case" g2="s.sc" k="20" />
+    <hkern g1="minus.case" g2="fouroldstyle" k="30" />
+    <hkern g1="minus.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="minus.case" g2="afii10097.sc" k="30" />
+    <hkern g1="minus.case" u2="&#x442;" k="50" />
+    <hkern g1="minus.case" u2="&#x37;" k="60" />
+    <hkern g1="multiply.case" g2="twooldstyle" k="10" />
+    <hkern g1="multiply.case" g2="two.sc" k="40" />
+    <hkern g1="multiply.case" g2="threeoldstyle" k="20" />
+    <hkern g1="multiply.case" g2="three.sc" k="20" />
+    <hkern g1="multiply.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="multiply.case" g2="seven.sc" k="40" />
+    <hkern g1="multiply.case" g2="s.sc" k="20" />
+    <hkern g1="multiply.case" g2="fouroldstyle" k="30" />
+    <hkern g1="multiply.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="multiply.case" g2="afii10097.sc" k="30" />
+    <hkern g1="multiply.case" u2="&#x442;" k="50" />
+    <hkern g1="multiply.case" u2="&#x37;" k="60" />
+    <hkern g1="nine.denominator" u2="&#x2044;" k="-30" />
+    <hkern g1="nine.numerator" u2="&#x2044;" k="110" />
+    <hkern g1="nine.sc" g2="two.sc" k="30" />
+    <hkern g1="nine.sc" g2="three.sc" k="20" />
+    <hkern g1="nine.sc" g2="seven.sc" k="20" />
+    <hkern g1="nine.sc" g2="s.sc" k="10" />
+    <hkern g1="nine.sc" g2="one.sc" k="30" />
+    <hkern g1="nine.sc" g2="j.sc" k="10" />
+    <hkern g1="nine.sc" g2="five.sc" k="20" />
+    <hkern g1="nine.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="nine.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="nine.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="nine.sc" u2="&#x2f;" k="40" />
+    <hkern g1="nineoldstyle" u2="&#x2122;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#x201d;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#x201c;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#x2019;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#x2018;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#x2c9;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#xba;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#xb0;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#xae;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#xaa;" k="30" />
+    <hkern g1="nineoldstyle" u2="^" k="30" />
+    <hkern g1="nineoldstyle" u2="&#x2a;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#x27;" k="30" />
+    <hkern g1="nineoldstyle" u2="&#x22;" k="30" />
+    <hkern g1="nineoldstyle" g2="threeoldstyle" k="20" />
+    <hkern g1="nineoldstyle" g2="sevenoldstyle" k="20" />
+    <hkern g1="notequal.case" g2="twooldstyle" k="10" />
+    <hkern g1="notequal.case" g2="two.sc" k="40" />
+    <hkern g1="notequal.case" g2="threeoldstyle" k="20" />
+    <hkern g1="notequal.case" g2="three.sc" k="20" />
+    <hkern g1="notequal.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="notequal.case" g2="seven.sc" k="40" />
+    <hkern g1="notequal.case" g2="s.sc" k="20" />
+    <hkern g1="notequal.case" g2="fouroldstyle" k="30" />
+    <hkern g1="notequal.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="notequal.case" g2="afii10097.sc" k="30" />
+    <hkern g1="notequal.case" u2="&#x442;" k="50" />
+    <hkern g1="notequal.case" u2="&#x37;" k="60" />
+    <hkern g1="numbersign.case" g2="twooldstyle" k="10" />
+    <hkern g1="numbersign.case" g2="two.sc" k="40" />
+    <hkern g1="numbersign.case" g2="threeoldstyle" k="20" />
+    <hkern g1="numbersign.case" g2="three.sc" k="20" />
+    <hkern g1="numbersign.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="numbersign.case" g2="seven.sc" k="40" />
+    <hkern g1="numbersign.case" g2="s.sc" k="20" />
+    <hkern g1="numbersign.case" g2="fouroldstyle" k="30" />
+    <hkern g1="numbersign.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="numbersign.case" g2="afii10097.sc" k="30" />
+    <hkern g1="numbersign.case" u2="&#x442;" k="50" />
+    <hkern g1="numbersign.case" u2="&#x37;" k="60" />
+    <hkern g1="o.sc" g2="two.sc" k="30" />
+    <hkern g1="o.sc" g2="three.sc" k="20" />
+    <hkern g1="o.sc" g2="seven.sc" k="20" />
+    <hkern g1="o.sc" g2="s.sc" k="10" />
+    <hkern g1="o.sc" g2="one.sc" k="30" />
+    <hkern g1="o.sc" g2="j.sc" k="10" />
+    <hkern g1="o.sc" g2="five.sc" k="20" />
+    <hkern g1="o.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="o.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="o.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="o.sc" u2="&#x2f;" k="40" />
+    <hkern g1="oacute.sc" g2="two.sc" k="30" />
+    <hkern g1="oacute.sc" g2="three.sc" k="20" />
+    <hkern g1="oacute.sc" g2="seven.sc" k="20" />
+    <hkern g1="oacute.sc" g2="s.sc" k="10" />
+    <hkern g1="oacute.sc" g2="one.sc" k="30" />
+    <hkern g1="oacute.sc" g2="j.sc" k="10" />
+    <hkern g1="oacute.sc" g2="five.sc" k="20" />
+    <hkern g1="oacute.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="oacute.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="oacute.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="oacute.sc" u2="&#x2f;" k="40" />
+    <hkern g1="obreve.sc" g2="two.sc" k="30" />
+    <hkern g1="obreve.sc" g2="three.sc" k="20" />
+    <hkern g1="obreve.sc" g2="seven.sc" k="20" />
+    <hkern g1="obreve.sc" g2="s.sc" k="10" />
+    <hkern g1="obreve.sc" g2="one.sc" k="30" />
+    <hkern g1="obreve.sc" g2="j.sc" k="10" />
+    <hkern g1="obreve.sc" g2="five.sc" k="20" />
+    <hkern g1="obreve.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="obreve.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="obreve.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="obreve.sc" u2="&#x2f;" k="40" />
+    <hkern g1="ocircumflex.sc" g2="two.sc" k="30" />
+    <hkern g1="ocircumflex.sc" g2="three.sc" k="20" />
+    <hkern g1="ocircumflex.sc" g2="seven.sc" k="20" />
+    <hkern g1="ocircumflex.sc" g2="s.sc" k="10" />
+    <hkern g1="ocircumflex.sc" g2="one.sc" k="30" />
+    <hkern g1="ocircumflex.sc" g2="j.sc" k="10" />
+    <hkern g1="ocircumflex.sc" g2="five.sc" k="20" />
+    <hkern g1="ocircumflex.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="ocircumflex.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="ocircumflex.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="ocircumflex.sc" u2="&#x2f;" k="40" />
+    <hkern g1="odieresis.sc" g2="two.sc" k="30" />
+    <hkern g1="odieresis.sc" g2="three.sc" k="20" />
+    <hkern g1="odieresis.sc" g2="seven.sc" k="20" />
+    <hkern g1="odieresis.sc" g2="s.sc" k="10" />
+    <hkern g1="odieresis.sc" g2="one.sc" k="30" />
+    <hkern g1="odieresis.sc" g2="j.sc" k="10" />
+    <hkern g1="odieresis.sc" g2="five.sc" k="20" />
+    <hkern g1="odieresis.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="odieresis.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="odieresis.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="odieresis.sc" u2="&#x2f;" k="40" />
+    <hkern g1="oe.sc" g2="s.sc" k="10" />
+    <hkern g1="oe.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="oe.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="oe.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="ograve.sc" g2="two.sc" k="30" />
+    <hkern g1="ograve.sc" g2="three.sc" k="20" />
+    <hkern g1="ograve.sc" g2="seven.sc" k="20" />
+    <hkern g1="ograve.sc" g2="s.sc" k="10" />
+    <hkern g1="ograve.sc" g2="one.sc" k="30" />
+    <hkern g1="ograve.sc" g2="j.sc" k="10" />
+    <hkern g1="ograve.sc" g2="five.sc" k="20" />
+    <hkern g1="ograve.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="ograve.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="ograve.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="ograve.sc" u2="&#x2f;" k="40" />
+    <hkern g1="ohungarumlaut.sc" g2="two.sc" k="30" />
+    <hkern g1="ohungarumlaut.sc" g2="three.sc" k="20" />
+    <hkern g1="ohungarumlaut.sc" g2="seven.sc" k="20" />
+    <hkern g1="ohungarumlaut.sc" g2="s.sc" k="10" />
+    <hkern g1="ohungarumlaut.sc" g2="one.sc" k="30" />
+    <hkern g1="ohungarumlaut.sc" g2="j.sc" k="10" />
+    <hkern g1="ohungarumlaut.sc" g2="five.sc" k="20" />
+    <hkern g1="ohungarumlaut.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="ohungarumlaut.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="ohungarumlaut.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="ohungarumlaut.sc" u2="&#x2f;" k="40" />
+    <hkern g1="omacron.sc" g2="two.sc" k="30" />
+    <hkern g1="omacron.sc" g2="three.sc" k="20" />
+    <hkern g1="omacron.sc" g2="seven.sc" k="20" />
+    <hkern g1="omacron.sc" g2="s.sc" k="10" />
+    <hkern g1="omacron.sc" g2="one.sc" k="30" />
+    <hkern g1="omacron.sc" g2="j.sc" k="10" />
+    <hkern g1="omacron.sc" g2="five.sc" k="20" />
+    <hkern g1="omacron.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="omacron.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="omacron.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="omacron.sc" u2="&#x2f;" k="40" />
+    <hkern g1="one.denominator" u2="&#x2044;" k="-70" />
+    <hkern g1="one.numerator" u2="&#x2044;" k="70" />
+    <hkern g1="oneoldstyle" g2="threeoldstyle" k="10" />
+    <hkern g1="oslash.sc" g2="two.sc" k="30" />
+    <hkern g1="oslash.sc" g2="three.sc" k="20" />
+    <hkern g1="oslash.sc" g2="seven.sc" k="20" />
+    <hkern g1="oslash.sc" g2="s.sc" k="10" />
+    <hkern g1="oslash.sc" g2="one.sc" k="30" />
+    <hkern g1="oslash.sc" g2="j.sc" k="10" />
+    <hkern g1="oslash.sc" g2="five.sc" k="20" />
+    <hkern g1="oslash.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="oslash.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="oslash.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="oslash.sc" u2="&#x2f;" k="40" />
+    <hkern g1="otilde.sc" g2="two.sc" k="30" />
+    <hkern g1="otilde.sc" g2="three.sc" k="20" />
+    <hkern g1="otilde.sc" g2="seven.sc" k="20" />
+    <hkern g1="otilde.sc" g2="s.sc" k="10" />
+    <hkern g1="otilde.sc" g2="one.sc" k="30" />
+    <hkern g1="otilde.sc" g2="j.sc" k="10" />
+    <hkern g1="otilde.sc" g2="five.sc" k="20" />
+    <hkern g1="otilde.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="otilde.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="otilde.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="otilde.sc" u2="&#x2f;" k="40" />
+    <hkern g1="p.sc" g2="j.sc" k="10" />
+    <hkern g1="p.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="p.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="p.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="p.sc" u2="&#x2f;" k="50" />
+    <hkern g1="periodcentered.case" g2="twooldstyle" k="10" />
+    <hkern g1="periodcentered.case" g2="two.sc" k="40" />
+    <hkern g1="periodcentered.case" g2="threeoldstyle" k="20" />
+    <hkern g1="periodcentered.case" g2="three.sc" k="20" />
+    <hkern g1="periodcentered.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="periodcentered.case" g2="seven.sc" k="40" />
+    <hkern g1="periodcentered.case" g2="s.sc" k="20" />
+    <hkern g1="periodcentered.case" g2="fouroldstyle" k="30" />
+    <hkern g1="periodcentered.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="periodcentered.case" g2="afii10097.sc" k="30" />
+    <hkern g1="periodcentered.case" u2="&#x442;" k="50" />
+    <hkern g1="periodcentered.case" u2="&#x37;" k="60" />
+    <hkern g1="plus.case" g2="twooldstyle" k="10" />
+    <hkern g1="plus.case" g2="two.sc" k="40" />
+    <hkern g1="plus.case" g2="threeoldstyle" k="20" />
+    <hkern g1="plus.case" g2="three.sc" k="20" />
+    <hkern g1="plus.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="plus.case" g2="seven.sc" k="40" />
+    <hkern g1="plus.case" g2="s.sc" k="20" />
+    <hkern g1="plus.case" g2="fouroldstyle" k="30" />
+    <hkern g1="plus.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="plus.case" g2="afii10097.sc" k="30" />
+    <hkern g1="plus.case" u2="&#x442;" k="50" />
+    <hkern g1="plus.case" u2="&#x37;" k="60" />
+    <hkern g1="plusminus.case" g2="twooldstyle" k="10" />
+    <hkern g1="plusminus.case" g2="two.sc" k="40" />
+    <hkern g1="plusminus.case" g2="threeoldstyle" k="20" />
+    <hkern g1="plusminus.case" g2="three.sc" k="20" />
+    <hkern g1="plusminus.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="plusminus.case" g2="seven.sc" k="40" />
+    <hkern g1="plusminus.case" g2="s.sc" k="20" />
+    <hkern g1="plusminus.case" g2="fouroldstyle" k="30" />
+    <hkern g1="plusminus.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="plusminus.case" g2="afii10097.sc" k="30" />
+    <hkern g1="plusminus.case" u2="&#x442;" k="50" />
+    <hkern g1="plusminus.case" u2="&#x37;" k="60" />
+    <hkern g1="q.sc" g2="two.sc" k="30" />
+    <hkern g1="q.sc" g2="three.sc" k="20" />
+    <hkern g1="q.sc" g2="seven.sc" k="20" />
+    <hkern g1="q.sc" g2="s.sc" k="10" />
+    <hkern g1="q.sc" g2="one.sc" k="30" />
+    <hkern g1="q.sc" g2="j.sc" k="10" />
+    <hkern g1="q.sc" g2="five.sc" k="20" />
+    <hkern g1="q.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="q.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="q.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="q.sc" u2="&#x2f;" k="40" />
+    <hkern g1="rupiah" g2="afii10097.sc" k="30" />
+    <hkern g1="rupiah" g2="afii10089.sc" k="10" />
+    <hkern g1="rupiah" u2="&#x44f;" k="20" />
+    <hkern g1="rupiah" u2="&#x44a;" k="10" />
+    <hkern g1="rupiah" u2="&#x447;" k="20" />
+    <hkern g1="rupiah" u2="&#x42f;" k="30" />
+    <hkern g1="rupiah" u2="&#x42a;" k="20" />
+    <hkern g1="rupiah" u2="&#x427;" k="10" />
+    <hkern g1="rupiah" u2="J" k="30" />
+    <hkern g1="rupiah" u2="&#x2f;" k="70" />
+    <hkern g1="rupiah.sc" g2="j.sc" k="10" />
+    <hkern g1="rupiah.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="rupiah.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="rupiah.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="rupiah.sc" u2="&#x2f;" k="50" />
+    <hkern g1="rupiaholdstyle" g2="threeoldstyle" k="40" />
+    <hkern g1="rupiaholdstyle" g2="sevenoldstyle" k="20" />
+    <hkern g1="rupiaholdstyle" u2="&#x44f;" k="10" />
+    <hkern g1="rupiaholdstyle" u2="&#x44a;" k="40" />
+    <hkern g1="rupiaholdstyle" u2="&#x447;" k="10" />
+    <hkern g1="rupiaholdstyle" u2="&#x442;" k="20" />
+    <hkern g1="rupiaholdstyle" u2="&#x2f;" k="20" />
+    <hkern g1="s.sc" g2="s.sc" k="10" />
+    <hkern g1="sacute.sc" g2="s.sc" k="10" />
+    <hkern g1="sb.liga" g2="threeoldstyle" k="40" />
+    <hkern g1="sb.liga" g2="sevenoldstyle" k="20" />
+    <hkern g1="sb.liga" u2="&#x44f;" k="10" />
+    <hkern g1="sb.liga" u2="&#x44a;" k="40" />
+    <hkern g1="sb.liga" u2="&#x447;" k="10" />
+    <hkern g1="sb.liga" u2="&#x442;" k="20" />
+    <hkern g1="sb.liga" u2="&#x2f;" k="20" />
+    <hkern g1="scaron.sc" g2="s.sc" k="10" />
+    <hkern g1="scedilla.sc" g2="s.sc" k="10" />
+    <hkern g1="scircumflex.sc" g2="s.sc" k="10" />
+    <hkern g1="scommaaccent.sc" g2="s.sc" k="10" />
+    <hkern g1="seven.numerator" u2="&#x2044;" k="150" />
+    <hkern g1="seven.sc" g2="zero.slash.sc" k="10" />
+    <hkern g1="seven.sc" g2="zero.sc" k="10" />
+    <hkern g1="seven.sc" g2="uni020F.sc" k="10" />
+    <hkern g1="seven.sc" g2="uni020D.sc" k="10" />
+    <hkern g1="seven.sc" g2="uni00AD.case" k="30" />
+    <hkern g1="seven.sc" g2="six.sc" k="10" />
+    <hkern g1="seven.sc" g2="q.sc" k="10" />
+    <hkern g1="seven.sc" g2="plusminus.case" k="30" />
+    <hkern g1="seven.sc" g2="plus.case" k="30" />
+    <hkern g1="seven.sc" g2="periodcentered.case" k="30" />
+    <hkern g1="seven.sc" g2="otilde.sc" k="10" />
+    <hkern g1="seven.sc" g2="oslash.sc" k="10" />
+    <hkern g1="seven.sc" g2="omacron.sc" k="10" />
+    <hkern g1="seven.sc" g2="ohungarumlaut.sc" k="10" />
+    <hkern g1="seven.sc" g2="ograve.sc" k="10" />
+    <hkern g1="seven.sc" g2="oe.sc" k="10" />
+    <hkern g1="seven.sc" g2="odieresis.sc" k="10" />
+    <hkern g1="seven.sc" g2="ocircumflex.sc" k="10" />
+    <hkern g1="seven.sc" g2="obreve.sc" k="10" />
+    <hkern g1="seven.sc" g2="oacute.sc" k="10" />
+    <hkern g1="seven.sc" g2="o.sc" k="10" />
+    <hkern g1="seven.sc" g2="numbersign.case" k="30" />
+    <hkern g1="seven.sc" g2="notequal.case" k="30" />
+    <hkern g1="seven.sc" g2="multiply.case" k="30" />
+    <hkern g1="seven.sc" g2="minus.case" k="30" />
+    <hkern g1="seven.sc" g2="logicalnot.case" k="30" />
+    <hkern g1="seven.sc" g2="lessequal.case" k="30" />
+    <hkern g1="seven.sc" g2="less.case" k="30" />
+    <hkern g1="seven.sc" g2="hyphen.case" k="30" />
+    <hkern g1="seven.sc" g2="guilsinglright.case" k="30" />
+    <hkern g1="seven.sc" g2="guilsinglleft.case" k="30" />
+    <hkern g1="seven.sc" g2="guillemotright.case" k="30" />
+    <hkern g1="seven.sc" g2="guillemotleft.case" k="30" />
+    <hkern g1="seven.sc" g2="greaterequal.case" k="30" />
+    <hkern g1="seven.sc" g2="greater.case" k="30" />
+    <hkern g1="seven.sc" g2="gdotaccent.sc" k="10" />
+    <hkern g1="seven.sc" g2="gcommaaccent.sc" k="10" />
+    <hkern g1="seven.sc" g2="gcircumflex.sc" k="10" />
+    <hkern g1="seven.sc" g2="gbreve.sc" k="10" />
+    <hkern g1="seven.sc" g2="g.sc" k="10" />
+    <hkern g1="seven.sc" g2="equal.case" k="30" />
+    <hkern g1="seven.sc" g2="endash.case" k="30" />
+    <hkern g1="seven.sc" g2="emdash.case" k="30" />
+    <hkern g1="seven.sc" g2="divide.case" k="30" />
+    <hkern g1="seven.sc" g2="currency.taboldstyle" k="30" />
+    <hkern g1="seven.sc" g2="cdotaccent.sc" k="10" />
+    <hkern g1="seven.sc" g2="ccircumflex.sc" k="10" />
+    <hkern g1="seven.sc" g2="ccedilla.sc" k="10" />
+    <hkern g1="seven.sc" g2="ccaron.sc" k="10" />
+    <hkern g1="seven.sc" g2="cacute.sc" k="10" />
+    <hkern g1="seven.sc" g2="c.sc" k="10" />
+    <hkern g1="seven.sc" g2="bullet.case" k="30" />
+    <hkern g1="seven.sc" g2="asciitilde.case" k="30" />
+    <hkern g1="seven.sc" g2="approxequal.case" k="30" />
+    <hkern g1="seven.sc" g2="afii10101.sc" k="10" />
+    <hkern g1="seven.sc" g2="afii10086.sc" k="10" />
+    <hkern g1="seven.sc" g2="afii10083.sc" k="10" />
+    <hkern g1="seven.sc" g2="afii10080.sc" k="10" />
+    <hkern g1="seven.sc" g2="Euro.sc" k="10" />
+    <hkern g1="seven.sc" u2="&#x2265;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2264;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2260;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2248;" k="30" />
+    <hkern g1="seven.sc" u2="&#x221e;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2212;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2192;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2190;" k="30" />
+    <hkern g1="seven.sc" u2="&#x203a;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2039;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2014;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2013;" k="30" />
+    <hkern g1="seven.sc" u2="&#xf7;" k="30" />
+    <hkern g1="seven.sc" u2="&#xd7;" k="30" />
+    <hkern g1="seven.sc" u2="&#xbb;" k="30" />
+    <hkern g1="seven.sc" u2="&#xb7;" k="30" />
+    <hkern g1="seven.sc" u2="&#xb1;" k="30" />
+    <hkern g1="seven.sc" u2="&#xad;" k="30" />
+    <hkern g1="seven.sc" u2="&#xac;" k="30" />
+    <hkern g1="seven.sc" u2="&#xab;" k="30" />
+    <hkern g1="seven.sc" u2="&#xa4;" k="30" />
+    <hkern g1="seven.sc" u2="&#x7e;" k="30" />
+    <hkern g1="seven.sc" u2="&#x3e;" k="30" />
+    <hkern g1="seven.sc" u2="&#x3d;" k="30" />
+    <hkern g1="seven.sc" u2="&#x3c;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2d;" k="30" />
+    <hkern g1="seven.sc" u2="&#x2b;" k="30" />
+    <hkern g1="seven.sc" u2="&#x23;" k="30" />
+    <hkern g1="seven.sc" g2="two.sc" k="10" />
+    <hkern g1="seven.sc" g2="three.sc" k="20" />
+    <hkern g1="seven.sc" g2="four.sc" k="60" />
+    <hkern g1="seven.sc" g2="five.sc" k="10" />
+    <hkern g1="seven.sc" u2="&#x2f;" k="60" />
+    <hkern g1="sevenoldstyle" g2="zerooldstyle" k="30" />
+    <hkern g1="sevenoldstyle" g2="zero.slash.oldstyle" k="30" />
+    <hkern g1="sevenoldstyle" g2="oneoldstyle" k="20" />
+    <hkern g1="sevenoldstyle" g2="ct.liga" k="30" />
+    <hkern g1="sevenoldstyle" g2="copyright.case" k="30" />
+    <hkern g1="sevenoldstyle" g2="ck.liga" k="30" />
+    <hkern g1="sevenoldstyle" g2="ch.liga" k="30" />
+    <hkern g1="sevenoldstyle" g2="centoldstyle" k="30" />
+    <hkern g1="sevenoldstyle" g2="cb.liga" k="30" />
+    <hkern g1="sevenoldstyle" g2="Eurooldstyle" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x2202;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x212e;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x2026;" k="50" />
+    <hkern g1="sevenoldstyle" u2="&#x2025;" k="50" />
+    <hkern g1="sevenoldstyle" u2="&#x2024;" k="50" />
+    <hkern g1="sevenoldstyle" u2="&#x201e;" k="50" />
+    <hkern g1="sevenoldstyle" u2="&#x201a;" k="50" />
+    <hkern g1="sevenoldstyle" u2="&#x491;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x45f;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x45c;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x45a;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x454;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x453;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x451;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x44e;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x44c;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x44b;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x449;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x448;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x446;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x444;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x441;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x440;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x43f;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x43e;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x43d;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x43c;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x43a;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x439;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x438;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x435;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x433;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x432;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x431;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x237;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x20f;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x20d;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x159;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x157;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x155;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x153;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x151;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x14f;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x14d;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x148;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x146;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x144;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x138;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x131;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x11b;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x119;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x117;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x115;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x113;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x111;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x10f;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x10d;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x10b;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x109;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#x107;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xf8;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xf6;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xf5;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xf4;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xf3;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xf2;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xf1;" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#xf0;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xeb;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xea;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xe9;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xe8;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xe7;" k="30" />
+    <hkern g1="sevenoldstyle" u2="&#xa2;" k="30" />
+    <hkern g1="sevenoldstyle" u2="r" k="20" />
+    <hkern g1="sevenoldstyle" u2="q" k="30" />
+    <hkern g1="sevenoldstyle" u2="p" k="20" />
+    <hkern g1="sevenoldstyle" u2="o" k="30" />
+    <hkern g1="sevenoldstyle" u2="n" k="20" />
+    <hkern g1="sevenoldstyle" u2="m" k="20" />
+    <hkern g1="sevenoldstyle" u2="e" k="30" />
+    <hkern g1="sevenoldstyle" u2="d" k="30" />
+    <hkern g1="sevenoldstyle" u2="c" k="30" />
+    <hkern g1="sevenoldstyle" u2="_" k="50" />
+    <hkern g1="sevenoldstyle" u2="&#x2e;" k="50" />
+    <hkern g1="sevenoldstyle" u2="&#x2c;" k="50" />
+    <hkern g1="sevenoldstyle" g2="twooldstyle" k="20" />
+    <hkern g1="sevenoldstyle" g2="threeoldstyle" k="10" />
+    <hkern g1="sevenoldstyle" g2="sevenoldstyle" k="10" />
+    <hkern g1="sevenoldstyle" g2="fouroldstyle" k="40" />
+    <hkern g1="sevenoldstyle" g2="fiveoldstyle" k="20" />
+    <hkern g1="sevenoldstyle" u2="&#x2f;" k="60" />
+    <hkern g1="six.denominator" u2="&#x2044;" k="-70" />
+    <hkern g1="six.numerator" u2="&#x2044;" k="80" />
+    <hkern g1="six.sc" g2="two.sc" k="10" />
+    <hkern g1="six.sc" g2="seven.sc" k="20" />
+    <hkern g1="six.sc" g2="one.sc" k="30" />
+    <hkern g1="six.sc" g2="nine.sc" k="30" />
+    <hkern g1="sixoldstyle" g2="oneoldstyle" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x2026;" k="10" />
+    <hkern g1="sixoldstyle" u2="&#x2025;" k="10" />
+    <hkern g1="sixoldstyle" u2="&#x2024;" k="10" />
+    <hkern g1="sixoldstyle" u2="&#x201e;" k="10" />
+    <hkern g1="sixoldstyle" u2="&#x201a;" k="10" />
+    <hkern g1="sixoldstyle" u2="&#x491;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x45f;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x45c;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x45a;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x453;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x44e;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x44c;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x44b;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x449;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x448;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x446;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x440;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x43f;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x43d;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x43c;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x43a;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x439;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x438;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x433;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x432;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x237;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x159;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x157;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x155;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x148;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x146;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x144;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x138;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#x131;" k="30" />
+    <hkern g1="sixoldstyle" u2="&#xf1;" k="30" />
+    <hkern g1="sixoldstyle" u2="r" k="30" />
+    <hkern g1="sixoldstyle" u2="p" k="30" />
+    <hkern g1="sixoldstyle" u2="n" k="30" />
+    <hkern g1="sixoldstyle" u2="m" k="30" />
+    <hkern g1="sixoldstyle" u2="_" k="10" />
+    <hkern g1="sixoldstyle" u2="&#x2e;" k="10" />
+    <hkern g1="sixoldstyle" u2="&#x2c;" k="10" />
+    <hkern g1="sixoldstyle" g2="twooldstyle" k="30" />
+    <hkern g1="sixoldstyle" g2="threeoldstyle" k="20" />
+    <hkern g1="sixoldstyle" g2="sevenoldstyle" k="20" />
+    <hkern g1="sixoldstyle" g2="fouroldstyle" k="10" />
+    <hkern g1="sixoldstyle" g2="fiveoldstyle" k="10" />
+    <hkern g1="sk.liga" u2="&#x44f;" k="20" />
+    <hkern g1="sk.liga" u2="&#x44a;" k="30" />
+    <hkern g1="sk.liga" u2="&#x447;" k="30" />
+    <hkern g1="sk.liga" u2="&#x442;" k="30" />
+    <hkern g1="st.liga" u2="&#x447;" k="20" />
+    <hkern g1="t.sc" g2="s.sc" k="10" />
+    <hkern g1="t.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="t.sc" u2="&#x2f;" k="60" />
+    <hkern g1="tcaron.sc" g2="s.sc" k="10" />
+    <hkern g1="tcaron.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="tcaron.sc" u2="&#x2f;" k="60" />
+    <hkern g1="tcommaaccent.sc" g2="s.sc" k="10" />
+    <hkern g1="tcommaaccent.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="tcommaaccent.sc" u2="&#x2f;" k="60" />
+    <hkern g1="thorn.sc" g2="two.sc" k="30" />
+    <hkern g1="thorn.sc" g2="three.sc" k="20" />
+    <hkern g1="thorn.sc" g2="seven.sc" k="20" />
+    <hkern g1="thorn.sc" g2="s.sc" k="10" />
+    <hkern g1="thorn.sc" g2="one.sc" k="30" />
+    <hkern g1="thorn.sc" g2="j.sc" k="10" />
+    <hkern g1="thorn.sc" g2="five.sc" k="20" />
+    <hkern g1="thorn.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="thorn.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="thorn.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="thorn.sc" u2="&#x2f;" k="40" />
+    <hkern g1="three.denominator" u2="&#x2044;" k="-40" />
+    <hkern g1="three.numerator" u2="&#x2044;" k="90" />
+    <hkern g1="three.sc" g2="two.sc" k="40" />
+    <hkern g1="three.sc" g2="three.sc" k="20" />
+    <hkern g1="three.sc" g2="seven.sc" k="10" />
+    <hkern g1="three.sc" g2="s.sc" k="10" />
+    <hkern g1="three.sc" g2="one.sc" k="30" />
+    <hkern g1="three.sc" g2="nine.sc" k="20" />
+    <hkern g1="three.sc" g2="five.sc" k="20" />
+    <hkern g1="three.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="three.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="three.sc" g2="afii10089.sc" k="30" />
+    <hkern g1="three.sc" u2="&#x2f;" k="10" />
+    <hkern g1="threeoldstyle" g2="oneoldstyle" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x2122;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#x201d;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#x201c;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#x2019;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#x2018;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#x491;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x45f;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x45c;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x45a;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x453;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x44e;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x44c;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x44b;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x449;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x448;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x446;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x440;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x43f;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x43d;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x43c;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x43a;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x439;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x438;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x433;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x432;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x2c9;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#x237;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x159;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x157;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x155;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x148;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x146;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x144;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x138;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#x131;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#xf1;" k="20" />
+    <hkern g1="threeoldstyle" u2="&#xba;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#xb0;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#xae;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#xaa;" k="40" />
+    <hkern g1="threeoldstyle" u2="r" k="20" />
+    <hkern g1="threeoldstyle" u2="p" k="20" />
+    <hkern g1="threeoldstyle" u2="n" k="20" />
+    <hkern g1="threeoldstyle" u2="m" k="20" />
+    <hkern g1="threeoldstyle" u2="^" k="40" />
+    <hkern g1="threeoldstyle" u2="&#x2a;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#x27;" k="40" />
+    <hkern g1="threeoldstyle" u2="&#x22;" k="40" />
+    <hkern g1="threeoldstyle" g2="sevenoldstyle" k="30" />
+    <hkern g1="threeoldstyle" g2="nineoldstyle" k="10" />
+    <hkern g1="threeoldstyle" g2="fiveoldstyle" k="10" />
+    <hkern g1="two.denominator" u2="&#x2044;" k="-50" />
+    <hkern g1="two.numerator" u2="&#x2044;" k="70" />
+    <hkern g1="two.sc" g2="zero.slash.sc" k="10" />
+    <hkern g1="two.sc" g2="zero.sc" k="10" />
+    <hkern g1="two.sc" g2="uni020F.sc" k="10" />
+    <hkern g1="two.sc" g2="uni020D.sc" k="10" />
+    <hkern g1="two.sc" g2="six.sc" k="10" />
+    <hkern g1="two.sc" g2="q.sc" k="10" />
+    <hkern g1="two.sc" g2="otilde.sc" k="10" />
+    <hkern g1="two.sc" g2="oslash.sc" k="10" />
+    <hkern g1="two.sc" g2="omacron.sc" k="10" />
+    <hkern g1="two.sc" g2="ohungarumlaut.sc" k="10" />
+    <hkern g1="two.sc" g2="ograve.sc" k="10" />
+    <hkern g1="two.sc" g2="oe.sc" k="10" />
+    <hkern g1="two.sc" g2="odieresis.sc" k="10" />
+    <hkern g1="two.sc" g2="ocircumflex.sc" k="10" />
+    <hkern g1="two.sc" g2="obreve.sc" k="10" />
+    <hkern g1="two.sc" g2="oacute.sc" k="10" />
+    <hkern g1="two.sc" g2="o.sc" k="10" />
+    <hkern g1="two.sc" g2="gdotaccent.sc" k="10" />
+    <hkern g1="two.sc" g2="gcommaaccent.sc" k="10" />
+    <hkern g1="two.sc" g2="gcircumflex.sc" k="10" />
+    <hkern g1="two.sc" g2="gbreve.sc" k="10" />
+    <hkern g1="two.sc" g2="g.sc" k="10" />
+    <hkern g1="two.sc" g2="cdotaccent.sc" k="10" />
+    <hkern g1="two.sc" g2="ccircumflex.sc" k="10" />
+    <hkern g1="two.sc" g2="ccedilla.sc" k="10" />
+    <hkern g1="two.sc" g2="ccaron.sc" k="10" />
+    <hkern g1="two.sc" g2="cacute.sc" k="10" />
+    <hkern g1="two.sc" g2="c.sc" k="10" />
+    <hkern g1="two.sc" g2="afii10101.sc" k="10" />
+    <hkern g1="two.sc" g2="afii10086.sc" k="10" />
+    <hkern g1="two.sc" g2="afii10083.sc" k="10" />
+    <hkern g1="two.sc" g2="afii10080.sc" k="10" />
+    <hkern g1="two.sc" g2="Euro.sc" k="10" />
+    <hkern g1="two.sc" g2="two.sc" k="20" />
+    <hkern g1="two.sc" g2="seven.sc" k="10" />
+    <hkern g1="two.sc" g2="one.sc" k="10" />
+    <hkern g1="two.sc" g2="nine.sc" k="10" />
+    <hkern g1="two.sc" g2="four.sc" k="20" />
+    <hkern g1="twooldstyle" g2="threeoldstyle" k="10" />
+    <hkern g1="twooldstyle" g2="sevenoldstyle" k="40" />
+    <hkern g1="twooldstyle" g2="fiveoldstyle" k="10" />
+    <hkern g1="u.sc" g2="s.sc" k="10" />
+    <hkern g1="u.sc" u2="&#x2f;" k="40" />
+    <hkern g1="uacute.sc" g2="s.sc" k="10" />
+    <hkern g1="uacute.sc" u2="&#x2f;" k="40" />
+    <hkern g1="ubreve.sc" g2="s.sc" k="10" />
+    <hkern g1="ubreve.sc" u2="&#x2f;" k="40" />
+    <hkern g1="ucircumflex.sc" g2="s.sc" k="10" />
+    <hkern g1="ucircumflex.sc" u2="&#x2f;" k="40" />
+    <hkern g1="udieresis.sc" g2="s.sc" k="10" />
+    <hkern g1="udieresis.sc" u2="&#x2f;" k="40" />
+    <hkern g1="ugrave.sc" g2="s.sc" k="10" />
+    <hkern g1="ugrave.sc" u2="&#x2f;" k="40" />
+    <hkern g1="uhungarumlaut.sc" g2="s.sc" k="10" />
+    <hkern g1="uhungarumlaut.sc" u2="&#x2f;" k="40" />
+    <hkern g1="umacron.sc" g2="s.sc" k="10" />
+    <hkern g1="umacron.sc" u2="&#x2f;" k="40" />
+    <hkern g1="uni00AD.case" g2="twooldstyle" k="10" />
+    <hkern g1="uni00AD.case" g2="two.sc" k="40" />
+    <hkern g1="uni00AD.case" g2="threeoldstyle" k="20" />
+    <hkern g1="uni00AD.case" g2="three.sc" k="20" />
+    <hkern g1="uni00AD.case" g2="sevenoldstyle" k="20" />
+    <hkern g1="uni00AD.case" g2="seven.sc" k="40" />
+    <hkern g1="uni00AD.case" g2="s.sc" k="20" />
+    <hkern g1="uni00AD.case" g2="fouroldstyle" k="30" />
+    <hkern g1="uni00AD.case" g2="fiveoldstyle" k="10" />
+    <hkern g1="uni00AD.case" g2="afii10097.sc" k="30" />
+    <hkern g1="uni00AD.case" u2="&#x442;" k="50" />
+    <hkern g1="uni00AD.case" u2="&#x37;" k="60" />
+    <hkern g1="uni0201.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="uni0201.alt1" u2="&#x447;" k="20" />
+    <hkern g1="uni0201.alt1" u2="&#x442;" k="40" />
+    <hkern g1="uni0201.sc" g2="s.sc" k="20" />
+    <hkern g1="uni0201.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="uni0201.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="uni0203.alt1" u2="&#x44a;" k="40" />
+    <hkern g1="uni0203.alt1" u2="&#x447;" k="20" />
+    <hkern g1="uni0203.alt1" u2="&#x442;" k="40" />
+    <hkern g1="uni0203.sc" g2="s.sc" k="20" />
+    <hkern g1="uni0203.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="uni0203.sc" g2="afii10089.sc" k="40" />
+    <hkern g1="uni0205.sc" g2="s.sc" k="10" />
+    <hkern g1="uni0205.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="uni0205.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="uni0205.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="uni0207.sc" g2="s.sc" k="10" />
+    <hkern g1="uni0207.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="uni0207.sc" g2="afii10092.sc" k="20" />
+    <hkern g1="uni0207.sc" g2="afii10089.sc" k="20" />
+    <hkern g1="uni020D.sc" g2="two.sc" k="30" />
+    <hkern g1="uni020D.sc" g2="three.sc" k="20" />
+    <hkern g1="uni020D.sc" g2="seven.sc" k="20" />
+    <hkern g1="uni020D.sc" g2="s.sc" k="10" />
+    <hkern g1="uni020D.sc" g2="one.sc" k="30" />
+    <hkern g1="uni020D.sc" g2="j.sc" k="10" />
+    <hkern g1="uni020D.sc" g2="five.sc" k="20" />
+    <hkern g1="uni020D.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="uni020D.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="uni020D.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="uni020D.sc" u2="&#x2f;" k="40" />
+    <hkern g1="uni020F.sc" g2="two.sc" k="30" />
+    <hkern g1="uni020F.sc" g2="three.sc" k="20" />
+    <hkern g1="uni020F.sc" g2="seven.sc" k="20" />
+    <hkern g1="uni020F.sc" g2="s.sc" k="10" />
+    <hkern g1="uni020F.sc" g2="one.sc" k="30" />
+    <hkern g1="uni020F.sc" g2="j.sc" k="10" />
+    <hkern g1="uni020F.sc" g2="five.sc" k="20" />
+    <hkern g1="uni020F.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="uni020F.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="uni020F.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="uni020F.sc" u2="&#x2f;" k="40" />
+    <hkern g1="uni0215.sc" g2="s.sc" k="10" />
+    <hkern g1="uni0215.sc" u2="&#x2f;" k="40" />
+    <hkern g1="uni0217.sc" g2="s.sc" k="10" />
+    <hkern g1="uni0217.sc" u2="&#x2f;" k="40" />
+    <hkern g1="uni021B.sc" g2="s.sc" k="10" />
+    <hkern g1="uni021B.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="uni021B.sc" u2="&#x2f;" k="60" />
+    <hkern g1="uogonek.sc" g2="s.sc" k="10" />
+    <hkern g1="uogonek.sc" u2="&#x2f;" k="40" />
+    <hkern g1="uring.sc" g2="s.sc" k="10" />
+    <hkern g1="uring.sc" u2="&#x2f;" k="40" />
+    <hkern g1="utilde.sc" g2="s.sc" k="10" />
+    <hkern g1="utilde.sc" u2="&#x2f;" k="40" />
+    <hkern g1="v.sc" g2="s.sc" k="20" />
+    <hkern g1="v.sc" g2="j.sc" k="10" />
+    <hkern g1="v.sc" g2="afii10097.sc" k="40" />
+    <hkern g1="v.sc" u2="&#x2f;" k="70" />
+    <hkern g1="w.sc" g2="s.sc" k="20" />
+    <hkern g1="w.sc" g2="j.sc" k="10" />
+    <hkern g1="w.sc" u2="&#x2f;" k="60" />
+    <hkern g1="wcircumflex.sc" g2="s.sc" k="20" />
+    <hkern g1="wcircumflex.sc" g2="j.sc" k="10" />
+    <hkern g1="wcircumflex.sc" u2="&#x2f;" k="60" />
+    <hkern g1="x.sc" g2="s.sc" k="10" />
+    <hkern g1="x.sc" g2="afii10092.sc" k="10" />
+    <hkern g1="x.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="y.sc" g2="s.sc" k="40" />
+    <hkern g1="y.sc" g2="r.sc" k="20" />
+    <hkern g1="y.sc" g2="j.sc" k="10" />
+    <hkern g1="y.sc" u2="&#x2f;" k="100" />
+    <hkern g1="yacute.sc" g2="s.sc" k="40" />
+    <hkern g1="yacute.sc" g2="r.sc" k="20" />
+    <hkern g1="yacute.sc" g2="j.sc" k="10" />
+    <hkern g1="yacute.sc" u2="&#x2f;" k="100" />
+    <hkern g1="ycircumflex.sc" g2="s.sc" k="40" />
+    <hkern g1="ycircumflex.sc" g2="r.sc" k="20" />
+    <hkern g1="ycircumflex.sc" g2="j.sc" k="10" />
+    <hkern g1="ycircumflex.sc" u2="&#x2f;" k="100" />
+    <hkern g1="ydieresis.sc" g2="s.sc" k="40" />
+    <hkern g1="ydieresis.sc" g2="r.sc" k="20" />
+    <hkern g1="ydieresis.sc" g2="j.sc" k="10" />
+    <hkern g1="ydieresis.sc" u2="&#x2f;" k="100" />
+    <hkern g1="yen.sc" g2="s.sc" k="20" />
+    <hkern g1="yen.sc" g2="j.sc" k="10" />
+    <hkern g1="yen.sc" g2="afii10097.sc" k="40" />
+    <hkern g1="yen.sc" u2="&#x2f;" k="70" />
+    <hkern g1="yenoldstyle" u2="&#x2f;" k="80" />
+    <hkern g1="z.sc" g2="s.sc" k="10" />
+    <hkern g1="zacute.sc" g2="s.sc" k="10" />
+    <hkern g1="zcaron.sc" g2="s.sc" k="10" />
+    <hkern g1="zdotaccent.sc" g2="s.sc" k="10" />
+    <hkern g1="zero.denominator" u2="&#x2044;" k="-40" />
+    <hkern g1="zero.numerator" u2="&#x2044;" k="130" />
+    <hkern g1="zero.sc" g2="two.sc" k="30" />
+    <hkern g1="zero.sc" g2="three.sc" k="20" />
+    <hkern g1="zero.sc" g2="seven.sc" k="20" />
+    <hkern g1="zero.sc" g2="s.sc" k="10" />
+    <hkern g1="zero.sc" g2="one.sc" k="30" />
+    <hkern g1="zero.sc" g2="j.sc" k="10" />
+    <hkern g1="zero.sc" g2="five.sc" k="20" />
+    <hkern g1="zero.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="zero.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="zero.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="zero.sc" u2="&#x2f;" k="40" />
+    <hkern g1="zero.slash" g2="s.sc" k="10" />
+    <hkern g1="zero.slash" g2="afii10097.sc" k="20" />
+    <hkern g1="zero.slash" g2="afii10092.sc" k="20" />
+    <hkern g1="zero.slash" u2="&#x44a;" k="10" />
+    <hkern g1="zero.slash" u2="&#x447;" k="10" />
+    <hkern g1="zero.slash" u2="&#x442;" k="10" />
+    <hkern g1="zero.slash" u2="&#x42f;" k="20" />
+    <hkern g1="zero.slash" u2="&#x42a;" k="40" />
+    <hkern g1="zero.slash" u2="&#x427;" k="20" />
+    <hkern g1="zero.slash" u2="&#x37;" k="40" />
+    <hkern g1="zero.slash" u2="&#x33;" k="40" />
+    <hkern g1="zero.slash" u2="&#x32;" k="30" />
+    <hkern g1="zero.slash" u2="&#x31;" k="20" />
+    <hkern g1="zero.slash" u2="&#x2f;" k="30" />
+    <hkern g1="zero.slash.oldstyle" g2="threeoldstyle" k="40" />
+    <hkern g1="zero.slash.oldstyle" g2="sevenoldstyle" k="20" />
+    <hkern g1="zero.slash.oldstyle" u2="&#x44f;" k="10" />
+    <hkern g1="zero.slash.oldstyle" u2="&#x44a;" k="40" />
+    <hkern g1="zero.slash.oldstyle" u2="&#x447;" k="10" />
+    <hkern g1="zero.slash.oldstyle" u2="&#x442;" k="20" />
+    <hkern g1="zero.slash.oldstyle" u2="&#x2f;" k="20" />
+    <hkern g1="zero.slash.sc" g2="two.sc" k="30" />
+    <hkern g1="zero.slash.sc" g2="three.sc" k="20" />
+    <hkern g1="zero.slash.sc" g2="seven.sc" k="20" />
+    <hkern g1="zero.slash.sc" g2="s.sc" k="10" />
+    <hkern g1="zero.slash.sc" g2="one.sc" k="30" />
+    <hkern g1="zero.slash.sc" g2="j.sc" k="10" />
+    <hkern g1="zero.slash.sc" g2="five.sc" k="20" />
+    <hkern g1="zero.slash.sc" g2="afii10097.sc" k="20" />
+    <hkern g1="zero.slash.sc" g2="afii10092.sc" k="30" />
+    <hkern g1="zero.slash.sc" g2="afii10089.sc" k="10" />
+    <hkern g1="zero.slash.sc" u2="&#x2f;" k="40" />
+    <hkern g1="zerooldstyle" g2="threeoldstyle" k="40" />
+    <hkern g1="zerooldstyle" g2="sevenoldstyle" k="20" />
+    <hkern g1="zerooldstyle" u2="&#x44f;" k="10" />
+    <hkern g1="zerooldstyle" u2="&#x44a;" k="40" />
+    <hkern g1="zerooldstyle" u2="&#x447;" k="10" />
+    <hkern g1="zerooldstyle" u2="&#x442;" k="20" />
+    <hkern g1="zerooldstyle" u2="&#x2f;" k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="30" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="10" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="60" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="V,yen,afii10062,afii10037"
+	k="50" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="W,Wcircumflex"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="X,afii10024,afii10039"
+	k="40" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="40" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="30" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="50" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="40" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="afii10058,afii10021,afii10029"
+	k="50" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="afii10025,afii10047,summation"
+	k="40" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="10" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="w,wcircumflex"
+	k="10" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="10" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="10" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="10" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="afii10073,afii10095"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="30" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="w.sc,wcircumflex.sc"
+	k="10" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="20" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="60" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="50" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="afii10073.sc,afii10095.sc"
+	k="30" />
+    <hkern g1="zero,nine,D,O,Q,copyright,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Thorn,eth,Dcaron,Dcroat,Omacron,Obreve,Ohungarumlaut,uni020C,uni020E,afii10032,afii10038,afii10047,afii10048,Omega,partialdiff,zero.slash"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="10" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="50" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="V,yen,afii10062,afii10037"
+	k="70" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="W,Wcircumflex"
+	k="50" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="70" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="120" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="afii10025,afii10047,summation"
+	k="10" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="w,wcircumflex"
+	k="60" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="60" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="20" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="afii10073,afii10095"
+	k="10" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="60" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="40" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="80" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="w.sc,wcircumflex.sc"
+	k="60" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="afii10073.sc,afii10095.sc"
+	k="10" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="30" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="30" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	k="20" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="30" />
+    <hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="10" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="50" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="20" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="X,afii10024,afii10039"
+	k="20" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="10" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="80" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="afii10058,afii10021,afii10029"
+	k="70" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="afii10025,afii10047,summation"
+	k="20" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="60" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="70" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="50" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="60" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="w,wcircumflex"
+	k="70" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="80" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="50" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="afii10073,afii10095"
+	k="40" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="10" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="10" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="20" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="20" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="90" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="80" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="afii10073.sc,afii10095.sc"
+	k="20" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="60" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="50" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="50" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="60" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="40" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="60" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="m,n,p,r,ntilde,dotlessi,kgreenlandic,nacute,ncommaaccent,ncaron,racute,rcommaaccent,rcaron,dotlessj,afii10067,afii10068,afii10074,afii10075,afii10076,afii10078,afii10079,afii10081,afii10082,afii10088,afii10090,afii10091,afii10093,afii10094,afii10096,afii10100,afii10107,afii10109,afii10193,afii10098,oneoldstyle"
+	k="50" />
+    <hkern g1="T,Tcommaaccent,Tcaron,uni021A,afii10052,afii10020,afii10036,afii10050"
+	g2="afii10069,afii10077,afii10106"
+	k="90" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="70" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="20" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="X,afii10024,afii10039"
+	k="20" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="20" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="110" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="afii10058,afii10021,afii10029"
+	k="80" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="afii10025,afii10047,summation"
+	k="30" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="60" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="80" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="40" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="40" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="40" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="40" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="30" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="10" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="90" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="50" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="afii10073.sc,afii10095.sc"
+	k="30" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="40" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="30" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="30" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="70" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="20" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="50" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="m,n,p,r,ntilde,dotlessi,kgreenlandic,nacute,ncommaaccent,ncaron,racute,rcommaaccent,rcaron,dotlessj,afii10067,afii10068,afii10074,afii10075,afii10076,afii10078,afii10079,afii10081,afii10082,afii10088,afii10090,afii10091,afii10093,afii10094,afii10096,afii10100,afii10107,afii10109,afii10193,afii10098,oneoldstyle"
+	k="40" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="afii10069,afii10077,afii10106"
+	k="60" />
+    <hkern g1="V,yen,afii10062,afii10037"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="20" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="30" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="V,yen,afii10062,afii10037"
+	k="40" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="W,Wcircumflex"
+	k="30" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="X,afii10024,afii10039"
+	k="20" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="50" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="30" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="40" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="afii10058,afii10021,afii10029"
+	k="20" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="afii10025,afii10047,summation"
+	k="30" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="30" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="afii10073,afii10095"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="30" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="30" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="20" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="40" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="w.sc,wcircumflex.sc"
+	k="20" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="30" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="40" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="afii10073.sc,afii10095.sc"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="10" />
+    <hkern g1="three,B,germandbls,afii10018,afii10019,afii10025"
+	g2="afii10069,afii10077,afii10106"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="10" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="10" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="afii10073,afii10095"
+	k="10" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="10" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="w.sc,wcircumflex.sc"
+	k="10" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="10" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="10" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="C,Ccedilla,Cacute,Ccircumflex,Cdotaccent,Ccaron,afii10053,afii10035,Euro"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="V,yen,afii10062,afii10037"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="X,afii10024,afii10039"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="afii10058,afii10021,afii10029"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="afii10025,afii10047,summation"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="w,wcircumflex"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="20" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="w.sc,wcircumflex.sc"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="20" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="20" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	k="10" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="E,AE,Egrave,Eacute,Ecircumflex,Edieresis,Emacron,Ebreve,Edotaccent,Eogonek,Ecaron,OE,uni0204,uni0206,afii10023,afii10022,summation"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="10" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="10" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="10" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="V,yen,afii10062,afii10037"
+	k="20" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="W,Wcircumflex"
+	k="20" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="20" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="30" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="afii10058,afii10021,afii10029"
+	k="20" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="afii10025,afii10047,summation"
+	k="30" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="30" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="w,wcircumflex"
+	k="40" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="40" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="20" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="afii10073,afii10095"
+	k="10" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="40" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="40" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="30" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="30" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="w.sc,wcircumflex.sc"
+	k="30" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="20" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="afii10073.sc,afii10095.sc"
+	k="20" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="50" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="40" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	k="20" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="30" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="30" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="20" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="10" />
+    <hkern g1="K,X,Kcommaaccent,afii10061,afii10024,afii10028,afii10039"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="90" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="V,yen,afii10062,afii10037"
+	k="100" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="W,Wcircumflex"
+	k="80" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="90" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="110" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="20" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="w,wcircumflex"
+	k="50" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="60" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="30" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="70" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="70" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="70" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="w.sc,wcircumflex.sc"
+	k="60" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="40" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="40" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	k="40" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="30" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="10" />
+    <hkern g1="L,Lacute,Lcommaaccent,Lcaron,Ldot,Lslash"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="70" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="30" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="30" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="V,yen,afii10062,afii10037"
+	k="40" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="W,Wcircumflex"
+	k="20" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="X,afii10024,afii10039"
+	k="50" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="40" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="40" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="120" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="afii10058,afii10021,afii10029"
+	k="90" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="afii10025,afii10047,summation"
+	k="40" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="30" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="40" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="10" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="10" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="afii10073,afii10095"
+	k="10" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="10" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="10" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="w.sc,wcircumflex.sc"
+	k="10" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="80" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="80" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="afii10073.sc,afii10095.sc"
+	k="30" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="20" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="20" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="afii10069,afii10077,afii10106"
+	k="70" />
+    <hkern g1="P,afii10034,rupiah"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="40" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="V,yen,afii10062,afii10037"
+	k="40" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="W,Wcircumflex"
+	k="30" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="50" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="40" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="30" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="10" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="30" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="10" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="40" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="w.sc,wcircumflex.sc"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="30" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="30" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="30" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="10" />
+    <hkern g1="R,Racute,Rcommaaccent,Rcaron,uni0210,uni0212"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="10" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="10" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="30" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="V,yen,afii10062,afii10037"
+	k="30" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="W,Wcircumflex"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="X,afii10024,afii10039"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="50" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="10" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="30" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="30" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="30" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="30" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="w.sc,wcircumflex.sc"
+	k="30" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="10" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	k="10" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="10" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="m,n,p,r,ntilde,dotlessi,kgreenlandic,nacute,ncommaaccent,ncaron,racute,rcommaaccent,rcaron,dotlessj,afii10067,afii10068,afii10074,afii10075,afii10076,afii10078,afii10079,afii10081,afii10082,afii10088,afii10090,afii10091,afii10093,afii10094,afii10096,afii10100,afii10107,afii10109,afii10193,afii10098,oneoldstyle"
+	k="20" />
+    <hkern g1="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="20" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="10" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="X,afii10024,afii10039"
+	k="20" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="10" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="50" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="30" />
+    <hkern g1="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="20" />
+    <hkern g1="W,Wcircumflex"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="50" />
+    <hkern g1="W,Wcircumflex"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="20" />
+    <hkern g1="W,Wcircumflex"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="20" />
+    <hkern g1="W,Wcircumflex"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="70" />
+    <hkern g1="W,Wcircumflex"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="60" />
+    <hkern g1="W,Wcircumflex"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="60" />
+    <hkern g1="W,Wcircumflex"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="30" />
+    <hkern g1="W,Wcircumflex"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="40" />
+    <hkern g1="W,Wcircumflex"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="W,Wcircumflex"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="30" />
+    <hkern g1="W,Wcircumflex"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="40" />
+    <hkern g1="W,Wcircumflex"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="20" />
+    <hkern g1="W,Wcircumflex"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="90" />
+    <hkern g1="W,Wcircumflex"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="30" />
+    <hkern g1="W,Wcircumflex"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="20" />
+    <hkern g1="W,Wcircumflex"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="W,Wcircumflex"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="50" />
+    <hkern g1="W,Wcircumflex"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="20" />
+    <hkern g1="W,Wcircumflex"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="40" />
+    <hkern g1="W,Wcircumflex"
+	g2="m,n,p,r,ntilde,dotlessi,kgreenlandic,nacute,ncommaaccent,ncaron,racute,rcommaaccent,rcaron,dotlessj,afii10067,afii10068,afii10074,afii10075,afii10076,afii10078,afii10079,afii10081,afii10082,afii10088,afii10090,afii10091,afii10093,afii10094,afii10096,afii10100,afii10107,afii10109,afii10193,afii10098,oneoldstyle"
+	k="40" />
+    <hkern g1="W,Wcircumflex"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="70" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="30" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="20" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="100" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="110" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="80" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="40" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="30" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="w,wcircumflex"
+	k="30" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="30" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="50" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="40" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="10" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="10" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="10" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="10" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="10" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="w.sc,wcircumflex.sc"
+	k="10" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="110" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="50" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="40" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="30" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="70" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="40" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="80" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="m,n,p,r,ntilde,dotlessi,kgreenlandic,nacute,ncommaaccent,ncaron,racute,rcommaaccent,rcaron,dotlessj,afii10067,afii10068,afii10074,afii10075,afii10076,afii10078,afii10079,afii10081,afii10082,afii10088,afii10090,afii10091,afii10093,afii10094,afii10096,afii10100,afii10107,afii10109,afii10193,afii10098,oneoldstyle"
+	k="50" />
+    <hkern g1="Y,Yacute,Ycircumflex,Ydieresis"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="10" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="20" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="10" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="10" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="10" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="30" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="10" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	k="10" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="10" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="10" />
+    <hkern g1="Z,Zacute,Zdotaccent,Zcaron"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="20" />
+    <hkern g1="afii10021,afii10040,afii10043"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="20" />
+    <hkern g1="afii10021,afii10040,afii10043"
+	g2="V,yen,afii10062,afii10037"
+	k="20" />
+    <hkern g1="afii10021,afii10040,afii10043"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="afii10021,afii10040,afii10043"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="afii10021,afii10040,afii10043"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="20" />
+    <hkern g1="afii10021,afii10040,afii10043"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="30" />
+    <hkern g1="afii10021,afii10040,afii10043"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="afii10021,afii10040,afii10043"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="10" />
+    <hkern g1="afii10021,afii10040,afii10043"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="60" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="V,yen,afii10062,afii10037"
+	k="70" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="X,afii10024,afii10039"
+	k="40" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="afii10058,afii10021,afii10029"
+	k="20" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="afii10025,afii10047,summation"
+	k="20" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="30" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="40" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="afii10073,afii10095"
+	k="20" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="60" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="60" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="30" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="20" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="afii10073.sc,afii10095.sc"
+	k="20" />
+    <hkern g1="afii10058,afii10059,afii10044,afii10046"
+	g2="afii10069,afii10077,afii10106"
+	k="20" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="50" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="50" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="10" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="30" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="w,wcircumflex"
+	k="30" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="20" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="afii10073,afii10095"
+	k="20" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="20" />
+    <hkern g1="b,o,p,ograve,oacute,ocircumflex,otilde,odieresis,oslash,thorn,omacron,obreve,ohungarumlaut,uni020D,uni020F,afii10066,afii10080,afii10082,afii10086,afii10095,afii10096,cb.liga,copyright.case,fb.liga,lsb.liga,rupiaholdstyle,sb.liga,zero.slash.oldstyle,zerooldstyle"
+	g2="afii10069,afii10077,afii10106"
+	k="20" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="70" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="10" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="30" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="10" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="30" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="10" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="20" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="m,n,p,r,ntilde,dotlessi,kgreenlandic,nacute,ncommaaccent,ncaron,racute,rcommaaccent,rcaron,dotlessj,afii10067,afii10068,afii10074,afii10075,afii10076,afii10078,afii10079,afii10081,afii10082,afii10088,afii10090,afii10091,afii10093,afii10094,afii10096,afii10100,afii10107,afii10109,afii10193,afii10098,oneoldstyle"
+	k="10" />
+    <hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="20" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="20" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="10" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="10" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="w,wcircumflex"
+	k="10" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="10" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="10" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="c,t,cent,ccedilla,cacute,ccircumflex,cdotaccent,ccaron,tcommaaccent,tcaron,uni021B,afii10083,afii10101,Eurooldstyle,centoldstyle,ct.liga,ft.liga,lst.liga,st.liga"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="10" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="30" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="30" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="10" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="20" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="afii10069,afii10077,afii10106"
+	k="10" />
+    <hkern g1="e,ae,egrave,eacute,ecircumflex,edieresis,emacron,ebreve,edotaccent,eogonek,ecaron,oe,uni0205,uni0207,afii10070,afii10071,ae.alt1"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="20" />
+    <hkern g1="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="40" />
+    <hkern g1="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="10" />
+    <hkern g1="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="10" />
+    <hkern g1="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="10" />
+    <hkern g1="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="h,m,n,ntilde,hcircumflex,hbar,nacute,ncommaaccent,ncaron,napostrophe,afii10099,afii10108,ch.liga,fh.liga,lsh.liga,sh.liga"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="80" />
+    <hkern g1="h,m,n,ntilde,hcircumflex,hbar,nacute,ncommaaccent,ncaron,napostrophe,afii10099,afii10108,ch.liga,fh.liga,lsh.liga,sh.liga"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="10" />
+    <hkern g1="h,m,n,ntilde,hcircumflex,hbar,nacute,ncommaaccent,ncaron,napostrophe,afii10099,afii10108,ch.liga,fh.liga,lsh.liga,sh.liga"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="10" />
+    <hkern g1="h,m,n,ntilde,hcircumflex,hbar,nacute,ncommaaccent,ncaron,napostrophe,afii10099,afii10108,ch.liga,fh.liga,lsh.liga,sh.liga"
+	g2="w,wcircumflex"
+	k="30" />
+    <hkern g1="h,m,n,ntilde,hcircumflex,hbar,nacute,ncommaaccent,ncaron,napostrophe,afii10099,afii10108,ch.liga,fh.liga,lsh.liga,sh.liga"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="40" />
+    <hkern g1="h,m,n,ntilde,hcircumflex,hbar,nacute,ncommaaccent,ncaron,napostrophe,afii10099,afii10108,ch.liga,fh.liga,lsh.liga,sh.liga"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="20" />
+    <hkern g1="h,m,n,ntilde,hcircumflex,hbar,nacute,ncommaaccent,ncaron,napostrophe,afii10099,afii10108,ch.liga,fh.liga,lsh.liga,sh.liga"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="10" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="10" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="f,florin,fb.liga,ff.liga,ffi.liga,ffj.liga,fh.liga,fi.liga,fj.liga,fk.liga,fl.liga,ft.liga"
+	k="10" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="afii10073,afii10095"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="40" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="10" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="10" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="m,n,p,r,ntilde,dotlessi,kgreenlandic,nacute,ncommaaccent,ncaron,racute,rcommaaccent,rcaron,dotlessj,afii10067,afii10068,afii10074,afii10075,afii10076,afii10078,afii10079,afii10081,afii10082,afii10088,afii10090,afii10091,afii10093,afii10094,afii10096,afii10100,afii10107,afii10109,afii10193,afii10098,oneoldstyle"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="afii10069,afii10077,afii10106"
+	k="20" />
+    <hkern g1="k,x,kcommaaccent,kgreenlandic,afii10072,afii10076,afii10087,afii10109,ck.liga,fk.liga,lsk.liga,sk.liga"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="130" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="30" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="10" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="w,wcircumflex"
+	k="10" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="10" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="afii10073,afii10095"
+	k="10" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="40" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="30" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="10" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="afii10069,afii10077,afii10106"
+	k="60" />
+    <hkern g1="r,racute,rcommaaccent,rcaron,uni0211,uni0213,afii10068,afii10100,afii10098"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="20" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="30" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="10" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="20" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="10" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="10" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="10" />
+    <hkern g1="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="10" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="50" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="-80" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="50" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="30" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="30" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="30" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="10" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="20" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="30" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="30" />
+    <hkern g1="f,dcaron,lcaron,longs,ff.liga,lsls.liga"
+	g2="m,n,p,r,ntilde,dotlessi,kgreenlandic,nacute,ncommaaccent,ncaron,racute,rcommaaccent,rcaron,dotlessj,afii10067,afii10068,afii10074,afii10075,afii10076,afii10078,afii10079,afii10081,afii10082,afii10088,afii10090,afii10091,afii10093,afii10094,afii10096,afii10100,afii10107,afii10109,afii10193,afii10098,oneoldstyle"
+	k="20" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="80" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="20" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="50" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="20" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="afii10069,afii10077,afii10106"
+	k="50" />
+    <hkern g1="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="30" />
+    <hkern g1="w,wcircumflex"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="100" />
+    <hkern g1="w,wcircumflex"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="20" />
+    <hkern g1="w,wcircumflex"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="w,wcircumflex"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="50" />
+    <hkern g1="w,wcircumflex"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="10" />
+    <hkern g1="w,wcircumflex"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="w,wcircumflex"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="10" />
+    <hkern g1="w,wcircumflex"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="30" />
+    <hkern g1="w,wcircumflex"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="20" />
+    <hkern g1="w,wcircumflex"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="30" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="20" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="10" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="w,wcircumflex"
+	k="10" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="10" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="20" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="z,zacute,zdotaccent,zcaron"
+	g2="b,h,i,j,k,l,germandbls,igrave,iacute,icircumflex,idieresis,thorn,hcircumflex,hbar,itilde,imacron,iogonek,jcircumflex,kcommaaccent,lacute,lcommaaccent,ldot,lslash,uni0209,uni020B,afii10099,afii10103,afii10104,afii10105,afii10108,ii.liga,lsb.liga,lsh.liga,lsi.liga,lsj.liga,lsk.liga,lsl.liga,lsls.liga,lslsi.liga,lslsj.liga,lst.liga"
+	k="30" />
+    <hkern g1="afii10067,afii10073"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="40" />
+    <hkern g1="afii10067,afii10073"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="20" />
+    <hkern g1="afii10067,afii10073"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="afii10067,afii10073"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="afii10067,afii10073"
+	g2="afii10073,afii10095"
+	k="10" />
+    <hkern g1="afii10067,afii10073"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="20" />
+    <hkern g1="afii10067,afii10073"
+	g2="afii10069,afii10077,afii10106"
+	k="10" />
+    <hkern g1="afii10069,afii10088,afii10091"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="40" />
+    <hkern g1="afii10069,afii10088,afii10091"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="10" />
+    <hkern g1="afii10069,afii10088,afii10091"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="afii10069,afii10088,afii10091"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="10" />
+    <hkern g1="afii10092,afii10094,afii10106,afii10107"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="30" />
+    <hkern g1="afii10092,afii10094,afii10106,afii10107"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="90" />
+    <hkern g1="afii10092,afii10094,afii10106,afii10107"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="10" />
+    <hkern g1="afii10092,afii10094,afii10106,afii10107"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="40" />
+    <hkern g1="afii10092,afii10094,afii10106,afii10107"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="40" />
+    <hkern g1="afii10092,afii10094,afii10106,afii10107"
+	g2="afii10073,afii10095"
+	k="20" />
+    <hkern g1="afii10092,afii10094,afii10106,afii10107"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="10" />
+    <hkern g1="afii10092,afii10094,afii10106,afii10107"
+	g2="afii10069,afii10077,afii10106"
+	k="20" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="40" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="30" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="50" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="10" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="30" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="30" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="60" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="30" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="30" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="20" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="50" />
+    <hkern g1="afii10080.sc,afii10086.sc,afii10095.sc,afii10096.sc,d.sc,dcaron.sc,dcroat.sc,eth.sc,nine.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,thorn.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	g2="afii10073.sc,afii10095.sc"
+	k="10" />
+    <hkern g1="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="90" />
+    <hkern g1="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="50" />
+    <hkern g1="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="50" />
+    <hkern g1="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="50" />
+    <hkern g1="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="40" />
+    <hkern g1="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="20" />
+    <hkern g1="afii10068.sc,afii10084.sc,afii10098.sc,afii10100.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="70" />
+    <hkern g1="afii10068.sc,afii10084.sc,afii10098.sc,afii10100.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="10" />
+    <hkern g1="afii10068.sc,afii10084.sc,afii10098.sc,afii10100.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="50" />
+    <hkern g1="afii10068.sc,afii10084.sc,afii10098.sc,afii10100.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="60" />
+    <hkern g1="afii10068.sc,afii10084.sc,afii10098.sc,afii10100.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	g2="afii10073.sc,afii10095.sc"
+	k="10" />
+    <hkern g1="afii10068.sc,afii10084.sc,afii10098.sc,afii10100.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="30" />
+    <hkern g1="afii10068.sc,afii10084.sc,afii10098.sc,afii10100.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="50" />
+    <hkern g1="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="40" />
+    <hkern g1="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="10" />
+    <hkern g1="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="50" />
+    <hkern g1="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="70" />
+    <hkern g1="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	g2="afii10073.sc,afii10095.sc"
+	k="20" />
+    <hkern g1="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="30" />
+    <hkern g1="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="30" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="10" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="30" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="30" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="30" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="30" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="20" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="20" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="30" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="afii10073.sc,afii10095.sc"
+	k="20" />
+    <hkern g1="afii10066.sc,afii10067.sc,afii10073.sc,b.sc,three.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="10" />
+    <hkern g1="Euro.sc,afii10083.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="10" />
+    <hkern g1="Euro.sc,afii10083.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="10" />
+    <hkern g1="Euro.sc,afii10083.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="10" />
+    <hkern g1="Euro.sc,afii10083.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="20" />
+    <hkern g1="Euro.sc,afii10083.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="10" />
+    <hkern g1="Euro.sc,afii10083.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="10" />
+    <hkern g1="Euro.sc,afii10083.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="10" />
+    <hkern g1="Euro.sc,afii10083.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="30" />
+    <hkern g1="Euro.sc,afii10083.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="30" />
+    <hkern g1="ae.sc,afii10070.sc,afii10071.sc,e.sc,eacute.sc,ebreve.sc,ecaron.sc,ecircumflex.sc,edieresis.sc,edotaccent.sc,egrave.sc,emacron.sc,eogonek.sc,oe.sc,uni0205.sc,uni0207.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="10" />
+    <hkern g1="ae.sc,afii10070.sc,afii10071.sc,e.sc,eacute.sc,ebreve.sc,ecaron.sc,ecircumflex.sc,edieresis.sc,edotaccent.sc,egrave.sc,emacron.sc,eogonek.sc,oe.sc,uni0205.sc,uni0207.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="ae.sc,afii10070.sc,afii10071.sc,e.sc,eacute.sc,ebreve.sc,ecaron.sc,ecircumflex.sc,edieresis.sc,edotaccent.sc,egrave.sc,emacron.sc,eogonek.sc,oe.sc,uni0205.sc,uni0207.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="10" />
+    <hkern g1="ae.sc,afii10070.sc,afii10071.sc,e.sc,eacute.sc,ebreve.sc,ecaron.sc,ecircumflex.sc,edieresis.sc,edotaccent.sc,egrave.sc,emacron.sc,eogonek.sc,oe.sc,uni0205.sc,uni0207.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="10" />
+    <hkern g1="ae.sc,afii10070.sc,afii10071.sc,e.sc,eacute.sc,ebreve.sc,ecaron.sc,ecircumflex.sc,edieresis.sc,edotaccent.sc,egrave.sc,emacron.sc,eogonek.sc,oe.sc,uni0205.sc,uni0207.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="10" />
+    <hkern g1="ae.sc,afii10070.sc,afii10071.sc,e.sc,eacute.sc,ebreve.sc,ecaron.sc,ecircumflex.sc,edieresis.sc,edotaccent.sc,egrave.sc,emacron.sc,eogonek.sc,oe.sc,uni0205.sc,uni0207.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="10" />
+    <hkern g1="ae.sc,afii10070.sc,afii10071.sc,e.sc,eacute.sc,ebreve.sc,ecaron.sc,ecircumflex.sc,edieresis.sc,edotaccent.sc,egrave.sc,emacron.sc,eogonek.sc,oe.sc,uni0205.sc,uni0207.sc"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="10" />
+    <hkern g1="ae.sc,afii10070.sc,afii10071.sc,e.sc,eacute.sc,ebreve.sc,ecaron.sc,ecircumflex.sc,edieresis.sc,edotaccent.sc,egrave.sc,emacron.sc,eogonek.sc,oe.sc,uni0205.sc,uni0207.sc"
+	g2="afii10073.sc,afii10095.sc"
+	k="10" />
+    <hkern g1="ae.sc,afii10070.sc,afii10071.sc,e.sc,eacute.sc,ebreve.sc,ecaron.sc,ecircumflex.sc,edieresis.sc,edotaccent.sc,egrave.sc,emacron.sc,eogonek.sc,oe.sc,uni0205.sc,uni0207.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="10" />
+    <hkern g1="afii10072.sc,afii10076.sc,afii10087.sc,afii10109.sc,k.sc,kcommaaccent.sc,kgreenlandic.sc,x.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="10" />
+    <hkern g1="afii10072.sc,afii10076.sc,afii10087.sc,afii10109.sc,k.sc,kcommaaccent.sc,kgreenlandic.sc,x.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="20" />
+    <hkern g1="afii10072.sc,afii10076.sc,afii10087.sc,afii10109.sc,k.sc,kcommaaccent.sc,kgreenlandic.sc,x.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="10" />
+    <hkern g1="afii10072.sc,afii10076.sc,afii10087.sc,afii10109.sc,k.sc,kcommaaccent.sc,kgreenlandic.sc,x.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="20" />
+    <hkern g1="afii10072.sc,afii10076.sc,afii10087.sc,afii10109.sc,k.sc,kcommaaccent.sc,kgreenlandic.sc,x.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="20" />
+    <hkern g1="afii10072.sc,afii10076.sc,afii10087.sc,afii10109.sc,k.sc,kcommaaccent.sc,kgreenlandic.sc,x.sc"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="20" />
+    <hkern g1="afii10072.sc,afii10076.sc,afii10087.sc,afii10109.sc,k.sc,kcommaaccent.sc,kgreenlandic.sc,x.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="30" />
+    <hkern g1="afii10072.sc,afii10076.sc,afii10087.sc,afii10109.sc,k.sc,kcommaaccent.sc,kgreenlandic.sc,x.sc"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="40" />
+    <hkern g1="l.sc,lacute.sc,lcaron.sc,lcommaaccent.sc,ldot.sc,lslash.sc"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="140" />
+    <hkern g1="l.sc,lacute.sc,lcaron.sc,lcommaaccent.sc,ldot.sc,lslash.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="70" />
+    <hkern g1="l.sc,lacute.sc,lcaron.sc,lcommaaccent.sc,ldot.sc,lslash.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="40" />
+    <hkern g1="l.sc,lacute.sc,lcaron.sc,lcommaaccent.sc,ldot.sc,lslash.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="80" />
+    <hkern g1="l.sc,lacute.sc,lcaron.sc,lcommaaccent.sc,ldot.sc,lslash.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="100" />
+    <hkern g1="l.sc,lacute.sc,lcaron.sc,lcommaaccent.sc,ldot.sc,lslash.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="70" />
+    <hkern g1="l.sc,lacute.sc,lcaron.sc,lcommaaccent.sc,ldot.sc,lslash.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="30" />
+    <hkern g1="l.sc,lacute.sc,lcaron.sc,lcommaaccent.sc,ldot.sc,lslash.sc"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="50" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="70" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="20" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="20" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="30" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="30" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="20" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="20" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="50" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="80" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="afii10073.sc,afii10095.sc"
+	k="20" />
+    <hkern g1="afii10082.sc,p.sc,rupiah.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="r.sc,racute.sc,rcaron.sc,rcommaaccent.sc,uni0211.sc,uni0213.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="20" />
+    <hkern g1="r.sc,racute.sc,rcaron.sc,rcommaaccent.sc,uni0211.sc,uni0213.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="30" />
+    <hkern g1="r.sc,racute.sc,rcaron.sc,rcommaaccent.sc,uni0211.sc,uni0213.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="40" />
+    <hkern g1="r.sc,racute.sc,rcaron.sc,rcommaaccent.sc,uni0211.sc,uni0213.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="40" />
+    <hkern g1="r.sc,racute.sc,rcaron.sc,rcommaaccent.sc,uni0211.sc,uni0213.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="60" />
+    <hkern g1="r.sc,racute.sc,rcaron.sc,rcommaaccent.sc,uni0211.sc,uni0213.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="30" />
+    <hkern g1="r.sc,racute.sc,rcaron.sc,rcommaaccent.sc,uni0211.sc,uni0213.sc"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="r.sc,racute.sc,rcaron.sc,rcommaaccent.sc,uni0211.sc,uni0213.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="20" />
+    <hkern g1="r.sc,racute.sc,rcaron.sc,rcommaaccent.sc,uni0211.sc,uni0213.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="30" />
+    <hkern g1="afii10102.sc,dollar.sc,s.sc,sacute.sc,scaron.sc,scedilla.sc,scircumflex.sc,scommaaccent.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="20" />
+    <hkern g1="afii10102.sc,dollar.sc,s.sc,sacute.sc,scaron.sc,scedilla.sc,scircumflex.sc,scommaaccent.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="10" />
+    <hkern g1="afii10102.sc,dollar.sc,s.sc,sacute.sc,scaron.sc,scedilla.sc,scircumflex.sc,scommaaccent.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="20" />
+    <hkern g1="afii10102.sc,dollar.sc,s.sc,sacute.sc,scaron.sc,scedilla.sc,scircumflex.sc,scommaaccent.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="10" />
+    <hkern g1="afii10102.sc,dollar.sc,s.sc,sacute.sc,scaron.sc,scedilla.sc,scircumflex.sc,scommaaccent.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="40" />
+    <hkern g1="afii10102.sc,dollar.sc,s.sc,sacute.sc,scaron.sc,scedilla.sc,scircumflex.sc,scommaaccent.sc"
+	g2="w.sc,wcircumflex.sc"
+	k="20" />
+    <hkern g1="afii10102.sc,dollar.sc,s.sc,sacute.sc,scaron.sc,scedilla.sc,scircumflex.sc,scommaaccent.sc"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="afii10102.sc,dollar.sc,s.sc,sacute.sc,scaron.sc,scedilla.sc,scircumflex.sc,scommaaccent.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="30" />
+    <hkern g1="afii10102.sc,dollar.sc,s.sc,sacute.sc,scaron.sc,scedilla.sc,scircumflex.sc,scommaaccent.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="10" />
+    <hkern g1="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="30" />
+    <hkern g1="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="10" />
+    <hkern g1="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="20" />
+    <hkern g1="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="10" />
+    <hkern g1="w.sc,wcircumflex.sc"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="40" />
+    <hkern g1="w.sc,wcircumflex.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="40" />
+    <hkern g1="w.sc,wcircumflex.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="w.sc,wcircumflex.sc"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="30" />
+    <hkern g1="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	g2="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	k="70" />
+    <hkern g1="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="10" />
+    <hkern g1="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="10" />
+    <hkern g1="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="50" />
+    <hkern g1="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="40" />
+    <hkern g1="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	g2="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	k="40" />
+    <hkern g1="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="10" />
+    <hkern g1="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="10" />
+    <hkern g1="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="10" />
+    <hkern g1="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="afii10069.sc,afii10088.sc,afii10091.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="30" />
+    <hkern g1="afii10069.sc,afii10088.sc,afii10091.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="20" />
+    <hkern g1="afii10069.sc,afii10088.sc,afii10091.sc"
+	g2="afii10073.sc,afii10095.sc"
+	k="10" />
+    <hkern g1="afii10069.sc,afii10088.sc,afii10091.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="afii10092.sc,afii10094.sc,afii10106.sc,afii10107.sc"
+	g2="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	k="160" />
+    <hkern g1="afii10092.sc,afii10094.sc,afii10106.sc,afii10107.sc"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="80" />
+    <hkern g1="afii10092.sc,afii10094.sc,afii10106.sc,afii10107.sc"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="50" />
+    <hkern g1="afii10092.sc,afii10094.sc,afii10106.sc,afii10107.sc"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="20" />
+    <hkern g1="afii10092.sc,afii10094.sc,afii10106.sc,afii10107.sc"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="20" />
+    <hkern g1="afii10092.sc,afii10094.sc,afii10106.sc,afii10107.sc"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="30" />
+    <hkern g1="afii10092.sc,afii10094.sc,afii10106.sc,afii10107.sc"
+	g2="afii10073.sc,afii10095.sc"
+	k="20" />
+    <hkern g1="afii10092.sc,afii10094.sc,afii10106.sc,afii10107.sc"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="10" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="130" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="30" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring,ae,amacron,abreve,aogonek,uni0201,uni0203,afii10065,a.alt1,aacute.alt1,abreve.alt1,acircumflex.alt1,adieresis.alt1,ae.alt1,afii10065.77.liga,afii10065.alt1,agrave.alt1,amacron.alt1,aogonek.alt1,aring.alt1,atilde.alt1,uni0201.alt1,uni0203.alt1"
+	k="40" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="g,gcircumflex,gbreve,gdotaccent,gcommaaccent,g.alt1,gbreve.alt1,gcircumflex.alt1,gcommaaccent.alt1,gdotaccent.alt1"
+	k="80" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="w,wcircumflex"
+	k="20" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="afii10073,afii10095"
+	k="20" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="90" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="110" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="50" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="60" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="60" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="s,sacute,scircumflex,scedilla,scaron,scommaaccent,afii10102,dollaroldstyle,sb.liga,sh.liga,sk.liga,st.liga"
+	k="40" />
+    <hkern g1="quotedbl,quotesingle,asterisk,asciicircum,ordfeminine,registered,degree,ordmasculine,uni02C9,quoteleft,quoteright,quotedblleft,quotedblright,trademark"
+	g2="afii10069,afii10077,afii10106"
+	k="60" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring,AE,Amacron,Abreve,Aogonek,uni0200,uni0202,afii10017,Delta"
+	k="20" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="dollar,S,Sacute,Scircumflex,Scedilla,Scaron,Scommaaccent,afii10054"
+	k="20" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="50" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="V,yen,afii10062,afii10037"
+	k="30" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="W,Wcircumflex"
+	k="20" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="X,afii10024,afii10039"
+	k="30" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="30" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="Z,Zacute,Zdotaccent,Zcaron"
+	k="20" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="z,zacute,zdotaccent,zcaron"
+	k="20" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="x,afii10072,afii10087,afii10072.77.liga"
+	k="20" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="w,wcircumflex"
+	k="10" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="20" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="afii10073,afii10095"
+	k="20" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="30" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="50" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="afii10072.sc,afii10087.sc,x.sc"
+	k="40" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="40" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="w.sc,wcircumflex.sc"
+	k="30" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="z.sc,zacute.sc,zcaron.sc,zdotaccent.sc"
+	k="30" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="a.sc,aacute.sc,abreve.sc,acircumflex.sc,adieresis.sc,ae.sc,afii10065.sc,agrave.sc,amacron.sc,aogonek.sc,aring.sc,atilde.sc,uni0201.sc,uni0203.sc"
+	k="20" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="afii10069.sc,afii10077.sc,afii10106.sc"
+	k="50" />
+    <hkern g1="numbersign,plus,hyphen,less,equal,greater,asciitilde,currency,guillemotleft,logicalnot,uni00AD,plusminus,periodcentered,guillemotright,multiply,divide,endash,emdash,guilsinglleft,guilsinglright,arrowleft,arrowright,minus,infinity,approxequal,notequal,lessequal,greaterequal,approxequal.case,asciitilde.case,bullet.case,currency.taboldstyle,divide.case,emdash.case,endash.case,equal.case,greater.case,greaterequal.case,guillemotleft.case,guillemotright.case,guilsinglleft.case,guilsinglright.case,hyphen.case,less.case,lessequal.case,logicalnot.case,minus.case,multiply.case,notequal.case,numbersign.case,periodcentered.case,plus.case,plusminus.case,uni00AD.case"
+	g2="afii10073.sc,afii10095.sc"
+	k="30" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="T,Tcommaaccent,Tcaron,uni021A,afii10051,afii10060,afii10036"
+	k="80" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="V,yen,afii10062,afii10037"
+	k="110" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="W,Wcircumflex"
+	k="70" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="Y,Yacute,Ycircumflex,Ydieresis"
+	k="100" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="w,wcircumflex"
+	k="100" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="v,y,yacute,ydieresis,ycircumflex,afii10085,afii10110,radical,yenoldstyle"
+	k="120" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="u,ugrave,uacute,ucircumflex,udieresis,utilde,umacron,ubreve,uring,uhungarumlaut,uogonek,uni0215,uni0217"
+	k="50" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="afii10085.sc,afii10110.sc,v.sc,yen.sc"
+	k="40" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="u.sc,uacute.sc,ubreve.sc,ucircumflex.sc,udieresis.sc,ugrave.sc,uhungarumlaut.sc,umacron.sc,uni0215.sc,uni0217.sc,uogonek.sc,uring.sc,utilde.sc"
+	k="30" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="afii10084.sc,afii10099.sc,afii10108.sc,t.sc,tcaron.sc,tcommaaccent.sc,uni021B.sc"
+	k="70" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="y.sc,yacute.sc,ycircumflex.sc,ydieresis.sc"
+	k="70" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="w.sc,wcircumflex.sc"
+	k="40" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="afii10073.sc,afii10095.sc"
+	k="20" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="Euro.sc,afii10080.sc,afii10083.sc,afii10086.sc,afii10101.sc,c.sc,cacute.sc,ccaron.sc,ccedilla.sc,ccircumflex.sc,cdotaccent.sc,g.sc,gbreve.sc,gcircumflex.sc,gcommaaccent.sc,gdotaccent.sc,o.sc,oacute.sc,obreve.sc,ocircumflex.sc,odieresis.sc,oe.sc,ograve.sc,ohungarumlaut.sc,omacron.sc,oslash.sc,otilde.sc,q.sc,six.sc,uni020D.sc,uni020F.sc,zero.sc,zero.slash.sc"
+	k="20" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="zero,six,C,G,O,Q,copyright,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,Cacute,Ccircumflex,Cdotaccent,Ccaron,Gcircumflex,Gbreve,Gdotaccent,Gcommaaccent,Omacron,Obreve,Ohungarumlaut,OE,uni020C,uni020E,afii10053,afii10032,afii10035,afii10038,Euro,Omega,sixoldstyle,zero.slash"
+	k="50" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="U,Ugrave,Uacute,Ucircumflex,Udieresis,Utilde,Umacron,Ubreve,Uring,Uhungarumlaut,Uogonek,uni0214,uni0216"
+	k="50" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="c,d,e,o,q,cent,ccedilla,egrave,eacute,ecircumflex,edieresis,eth,ograve,oacute,ocircumflex,otilde,odieresis,oslash,cacute,ccircumflex,cdotaccent,ccaron,dcaron,dcroat,emacron,ebreve,edotaccent,eogonek,ecaron,omacron,obreve,ohungarumlaut,oe,uni020D,uni020F,afii10066,afii10070,afii10080,afii10083,afii10086,afii10071,afii10101,estimated,partialdiff,Eurooldstyle,cb.liga,centoldstyle,ch.liga,ck.liga,copyright.case,ct.liga,zero.slash.oldstyle,zerooldstyle"
+	k="40" />
+    <hkern g1="comma,period,underscore,quotesinglbase,quotedblbase,onedotenleader,twodotenleader,ellipsis"
+	g2="t,tcommaaccent,tcaron,uni021B"
+	k="50" />
+    <hkern g1="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	g2="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	k="10" />
+    <hkern g1="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	g2="onesuperior,oneinferior,one.denominator,one.numerator"
+	k="20" />
+    <hkern g1="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	g2="twosuperior,twoinferior,two.denominator,two.numerator"
+	k="10" />
+    <hkern g1="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	g2="threesuperior,threeinferior,three.denominator,three.numerator"
+	k="20" />
+    <hkern g1="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	g2="fivesuperior,fiveinferior,five.denominator,five.numerator"
+	k="20" />
+    <hkern g1="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	g2="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	k="10" />
+    <hkern g1="twosuperior,twoinferior,two.denominator,two.numerator"
+	g2="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	k="10" />
+    <hkern g1="threesuperior,threeinferior,three.denominator,three.numerator"
+	g2="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	k="20" />
+    <hkern g1="threesuperior,threeinferior,three.denominator,three.numerator"
+	g2="onesuperior,oneinferior,one.denominator,one.numerator"
+	k="20" />
+    <hkern g1="threesuperior,threeinferior,three.denominator,three.numerator"
+	g2="twosuperior,twoinferior,two.denominator,two.numerator"
+	k="10" />
+    <hkern g1="threesuperior,threeinferior,three.denominator,three.numerator"
+	g2="threesuperior,threeinferior,three.denominator,three.numerator"
+	k="20" />
+    <hkern g1="threesuperior,threeinferior,three.denominator,three.numerator"
+	g2="fivesuperior,fiveinferior,five.denominator,five.numerator"
+	k="10" />
+    <hkern g1="threesuperior,threeinferior,three.denominator,three.numerator"
+	g2="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	k="20" />
+    <hkern g1="threesuperior,threeinferior,three.denominator,three.numerator"
+	g2="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	k="10" />
+    <hkern g1="threesuperior,threeinferior,three.denominator,three.numerator"
+	g2="eightsuperior,eightinferior,eight.denominator,eight.numerator"
+	k="10" />
+    <hkern g1="threesuperior,threeinferior,three.denominator,three.numerator"
+	g2="foursuperior,fourinferior,four.denominator,four.numerator"
+	k="20" />
+    <hkern g1="foursuperior,fourinferior,four.denominator,four.numerator"
+	g2="onesuperior,oneinferior,one.denominator,one.numerator"
+	k="20" />
+    <hkern g1="foursuperior,fourinferior,four.denominator,four.numerator"
+	g2="threesuperior,threeinferior,three.denominator,three.numerator"
+	k="10" />
+    <hkern g1="foursuperior,fourinferior,four.denominator,four.numerator"
+	g2="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	k="10" />
+    <hkern g1="foursuperior,fourinferior,four.denominator,four.numerator"
+	g2="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	k="10" />
+    <hkern g1="fivesuperior,fiveinferior,five.denominator,five.numerator"
+	g2="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	k="10" />
+    <hkern g1="sixsuperior,sixinferior,six.denominator,six.numerator"
+	g2="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	k="10" />
+    <hkern g1="sixsuperior,sixinferior,six.denominator,six.numerator"
+	g2="threesuperior,threeinferior,three.denominator,three.numerator"
+	k="10" />
+    <hkern g1="sixsuperior,sixinferior,six.denominator,six.numerator"
+	g2="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	k="20" />
+    <hkern g1="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	g2="threesuperior,threeinferior,three.denominator,three.numerator"
+	k="10" />
+    <hkern g1="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	g2="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	k="10" />
+    <hkern g1="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	g2="zerosuperior,zeroinferior,zero.denominator,zero.numerator"
+	k="20" />
+    <hkern g1="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	g2="eightsuperior,eightinferior,eight.denominator,eight.numerator"
+	k="20" />
+    <hkern g1="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	g2="foursuperior,fourinferior,four.denominator,four.numerator"
+	k="40" />
+    <hkern g1="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	g2="sixsuperior,sixinferior,six.denominator,six.numerator"
+	k="20" />
+    <hkern g1="eightsuperior,eightinferior,eight.denominator,eight.numerator"
+	g2="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	k="20" />
+    <hkern g1="eightsuperior,eightinferior,eight.denominator,eight.numerator"
+	g2="onesuperior,oneinferior,one.denominator,one.numerator"
+	k="20" />
+    <hkern g1="eightsuperior,eightinferior,eight.denominator,eight.numerator"
+	g2="twosuperior,twoinferior,two.denominator,two.numerator"
+	k="10" />
+    <hkern g1="eightsuperior,eightinferior,eight.denominator,eight.numerator"
+	g2="threesuperior,threeinferior,three.denominator,three.numerator"
+	k="20" />
+    <hkern g1="eightsuperior,eightinferior,eight.denominator,eight.numerator"
+	g2="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	k="20" />
+    <hkern g1="eightsuperior,eightinferior,eight.denominator,eight.numerator"
+	g2="foursuperior,fourinferior,four.denominator,four.numerator"
+	k="10" />
+    <hkern g1="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	g2="sevensuperior,seveninferior,seven.denominator,seven.numerator"
+	k="10" />
+    <hkern g1="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	g2="threesuperior,threeinferior,three.denominator,three.numerator"
+	k="10" />
+    <hkern g1="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	g2="ninesuperior,nineinferior,nine.denominator,nine.numerator"
+	k="10" />
+  </font>
+</defs></svg>
diff --git a/corp/rih/frontend/fonts/IdealistSans.ttf b/corp/rih/frontend/fonts/IdealistSans.ttf
new file mode 100644
index 0000000000..91765b1b11
--- /dev/null
+++ b/corp/rih/frontend/fonts/IdealistSans.ttf
Binary files differdiff --git a/corp/rih/frontend/fonts/IdealistSans.woff b/corp/rih/frontend/fonts/IdealistSans.woff
new file mode 100644
index 0000000000..563cc4b618
--- /dev/null
+++ b/corp/rih/frontend/fonts/IdealistSans.woff
Binary files differdiff --git a/corp/rih/frontend/fonts/IdealistSans.woff2 b/corp/rih/frontend/fonts/IdealistSans.woff2
new file mode 100644
index 0000000000..8123d6e9a1
--- /dev/null
+++ b/corp/rih/frontend/fonts/IdealistSans.woff2
Binary files differdiff --git a/corp/rih/frontend/fonts/futurabookc.eot b/corp/rih/frontend/fonts/futurabookc.eot
new file mode 100644
index 0000000000..a50f47723b
--- /dev/null
+++ b/corp/rih/frontend/fonts/futurabookc.eot
Binary files differdiff --git a/corp/rih/frontend/fonts/futurabookc.svg b/corp/rih/frontend/fonts/futurabookc.svg
new file mode 100644
index 0000000000..bb271a4749
--- /dev/null
+++ b/corp/rih/frontend/fonts/futurabookc.svg
@@ -0,0 +1,992 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
+<metadata>
+Created by FontForge 20190801 at Wed May 12 13:27:22 2004
+ By www-data
+Copyright (c) 1990 Neufville SL, Copyright (c) 1990-2000 ParaType, Inc. All Rights Reserved. Futura is a registered trade mark of Neufville SL.
+</metadata>
+<defs>
+<font id="futurabookc" horiz-adv-x="568" >
+  <font-face 
+    font-family="futurabookc"
+    font-weight="400"
+    font-stretch="normal"
+    units-per-em="1000"
+    panose-1="4 0 5 0 0 0 0 0 0 0"
+    ascent="750"
+    descent="-250"
+    x-height="415"
+    cap-height="667"
+    bbox="-46 -250 1027 882"
+    underline-thickness="50"
+    underline-position="-200"
+    unicode-range="U+0020-2122"
+  />
+<missing-glyph horiz-adv-x="233" 
+ />
+    <glyph glyph-name=".notdef" horiz-adv-x="233" 
+ />
+    <glyph glyph-name=".null" horiz-adv-x="0" 
+ />
+    <glyph glyph-name="nonmarkingreturn" horiz-adv-x="333" 
+ />
+    <glyph glyph-name="space" unicode=" " horiz-adv-x="233" 
+ />
+    <glyph glyph-name="space" unicode="&#xa0;" horiz-adv-x="233" 
+ />
+    <glyph glyph-name="exclam" unicode="!" horiz-adv-x="280" 
+d="M176 135h-72v532h72v-532zM90 40q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35z" />
+    <glyph glyph-name="quotedbl" unicode="&#x22;" horiz-adv-x="330" 
+d="M134 667l-13 -268h-54l-13 268h80zM276 667l-13 -268h-54l-13 268h80z" />
+    <glyph glyph-name="numbersign" unicode="#" horiz-adv-x="600" 
+d="M394 0h-73l32 207h-136l-32 -207h-73l32 207h-110l10 66h110l20 133h-110l10 66h110l30 195h73l-30 -195h136l30 195h73l-30 -195h110l-10 -66h-110l-20 -133h110l-10 -66h-110zM363 273l20 133h-136l-20 -133h136z" />
+    <glyph glyph-name="dollar" unicode="$" 
+d="M467 574l-59 -39q-15 30 -38 50q-19 17 -43 21v-222q79 -31 117 -70q47 -48 47 -121q0 -72 -44 -130.5t-120 -70.5v-93h-60v89q-75 3 -128 54q-46 46 -62 118l72 20q7 -53 40 -85q34 -35 78 -37v274q-72 29 -110 65q-46 43 -46 109q0 70 45 117t111 53v87h60v-87
+q42 -4 76 -28q40 -28 64 -74zM327 304v-242q40 11 64 48t24 80q0 47 -32 79q-21 21 -56 35zM267 411v195q-39 -6 -60.5 -34t-21.5 -65q0 -35 25 -60q22 -22 57 -36z" />
+    <glyph glyph-name="percent" unicode="%" horiz-adv-x="710" 
+d="M178 673q61 0 103 -42t42 -103t-42 -103t-103 -42t-103 42t-42 103t42 103t103 42zM178 438q36 0 60.5 26.5t24.5 63.5t-24.5 63.5t-60.5 26.5q-35 0 -60 -26.5t-25 -63.5t25 -63.5t60 -26.5zM141 10l389 670l39 -22l-389 -671zM532 283q61 0 103 -42t42 -103t-42 -103
+t-103 -42t-103 42t-42 103t42 103t103 42zM532 48q36 0 60.5 26.5t24.5 63.5t-24.5 63.5t-60.5 26.5q-35 0 -60 -26.5t-25 -63.5t25 -63.5t60 -26.5z" />
+    <glyph glyph-name="ampersand" unicode="&#x26;" horiz-adv-x="636" 
+d="M545 289l54 -45q-33 -45 -85 -99l112 -145h-92l-72 93q-46 -46 -103.5 -75.5t-111.5 -29.5q-82 0 -137.5 53t-55.5 132q0 78 60 138q31 31 112 83q-66 85 -66 143q0 57 38.5 99t106.5 42q66 0 103.5 -40t37.5 -94q0 -67 -62 -121q-7 -6 -18.5 -14.5t-35.5 -28.5l139 -179
+q51 54 76 88zM417 148l-148 191q-53 -33 -94 -69q-47 -42 -47 -96q0 -50 33.5 -84t84.5 -34q75 0 171 92zM257 475l30 -41l44 34q45 36 45 77q0 33 -21.5 51t-50.5 18q-31 0 -52.5 -21t-21.5 -52q0 -29 27 -66z" />
+    <glyph glyph-name="quotesingle" unicode="'" horiz-adv-x="188" 
+d="M134 667l-13 -268h-54l-13 268h80z" />
+    <glyph glyph-name="parenleft" unicode="(" horiz-adv-x="294" 
+d="M167 734l63 -29q-32 -86 -46 -149q-30 -123 -30 -279t30 -279q14 -63 46 -149l-63 -29q-28 74 -49 150q-38 135 -38 307t38 307q21 76 49 150z" />
+    <glyph glyph-name="parenright" unicode=")" horiz-adv-x="294" 
+d="M127 -180l-63 29q32 86 46 149q30 123 30 279t-30 279q-14 63 -46 149l63 29q28 -74 49 -150q38 -135 38 -307t-38 -307q-21 -76 -49 -150z" />
+    <glyph glyph-name="asterisk" unicode="*" 
+d="M139 648l116 -43l-5 124h68l-5 -124l116 43l21 -63l-120 -34l77 -97l-54 -39l-69 103l-69 -103l-54 39l77 97l-120 34z" />
+    <glyph glyph-name="plus" unicode="+" horiz-adv-x="500" 
+d="M33 300v66h181v172h72v-172h181v-66h-181v-172h-72v172h-181z" />
+    <glyph glyph-name="comma" unicode="," horiz-adv-x="274" 
+d="M24 -133l82 231l66 -24l-101 -226z" />
+    <glyph glyph-name="hyphen" unicode="-" horiz-adv-x="300" 
+d="M40 176v74h220v-74h-220z" />
+    <glyph glyph-name="hyphen" unicode="&#xad;" horiz-adv-x="300" 
+d="M40 176v74h220v-74h-220z" />
+    <glyph glyph-name="period" unicode="." horiz-adv-x="228" 
+d="M64 40q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35z" />
+    <glyph glyph-name="slash" unicode="/" horiz-adv-x="580" 
+d="M0 -152l502 902l58 -30l-502 -900z" />
+    <glyph glyph-name="zero" unicode="0" 
+d="M284 -12q-102 0 -171 100q-67 97 -67 245t67 245q69 100 171 100t171 -100q67 -97 67 -245t-67 -245q-69 -100 -171 -100zM284 610q-69 0 -115 -74q-49 -78 -49 -203t49 -203q46 -74 115 -74t115 74q49 78 49 203t-49 203q-46 74 -115 74z" />
+    <glyph glyph-name="one" unicode="1" 
+d="M266 0v597h-112l39 70h147v-667h-74z" />
+    <glyph glyph-name="two" unicode="2" 
+d="M481 0h-431l243 281q62 73 78 97q37 53 37 102q0 57 -37 93.5t-96 36.5q-72 0 -111 -59q-21 -32 -22 -79h-74q4 85 50 138q60 68 158 68q89 0 147.5 -56.5t58.5 -142.5q0 -67 -53 -143q-27 -37 -91 -111l-139 -157h282v-68z" />
+    <glyph glyph-name="three" unicode="3" 
+d="M160 502h-74q3 53 31 98q50 78 155 78q89 0 138.5 -53t49.5 -127q0 -105 -86 -148q13 -5 25.5 -13.5t30.5 -26.5t29 -49.5t11 -71.5q0 -91 -58.5 -146t-146.5 -55q-59 0 -110 27.5t-76 87.5q-13 32 -15 70h74q5 -41 24 -67q34 -50 103 -50q55 0 93 39.5t38 95.5
+q0 82 -66 112q-33 16 -76 14v66q43 -3 76 14q56 29 56 99q0 49 -28 81.5t-86 32.5q-56 0 -86 -38q-23 -30 -26 -70z" />
+    <glyph glyph-name="four" unicode="4" 
+d="M436 704v-527h82v-68h-82v-109h-74v109h-342zM151 177h211v301z" />
+    <glyph glyph-name="five" unicode="5" 
+d="M479 667v-68h-214l-43 -144q17 4 42 4q99 0 159.5 -66t60.5 -165q0 -108 -70.5 -174t-172.5 -66q-86 0 -149 52q-36 31 -58 68l59 42q15 -30 45 -54q49 -40 108 -40q70 0 117 50t47 121q0 72 -46.5 118t-117.5 46q-63 0 -129 -46l97 322h265z" />
+    <glyph glyph-name="six" unicode="6" 
+d="M336 683l56 -42l-153 -213q28 9 59 9q95 0 155.5 -63t60.5 -157q0 -100 -64.5 -164.5t-164.5 -64.5q-74 0 -128.5 35.5t-78.5 86t-24 105.5q0 76 55 153zM284 56q68 0 112 45.5t44 113.5q0 67 -44.5 110.5t-112.5 43.5t-111.5 -44t-43.5 -110q0 -68 43.5 -113.5
+t112.5 -45.5z" />
+    <glyph glyph-name="seven" unicode="7" 
+d="M52 23l379 576h-365v68h490l-446 -683z" />
+    <glyph glyph-name="eight" unicode="8" 
+d="M387 347q101 -45 101 -163q0 -83 -53.5 -139.5t-150.5 -56.5t-150.5 56.5t-53.5 139.5q0 118 101 163q-81 47 -81 146q0 76 49 130.5t135 54.5t135 -54.5t49 -130.5q0 -99 -81 -146zM284 377q49 0 81.5 33t32.5 82q0 53 -33.5 85.5t-80.5 32.5t-80.5 -32.5t-33.5 -85.5
+q0 -49 32.5 -82t81.5 -33zM284 56q58 0 94 36.5t36 93.5q0 56 -36.5 91.5t-93.5 35.5t-93.5 -35.5t-36.5 -91.5q0 -57 36 -93.5t94 -36.5z" />
+    <glyph glyph-name="nine" unicode="9" 
+d="M232 -16l-56 41l153 213q-29 -9 -59 -9q-95 0 -155.5 63t-60.5 157q0 100 64.5 164.5t164.5 64.5q74 0 128.5 -35.5t78.5 -86t24 -105.5q0 -77 -55 -153zM284 610q-68 0 -112 -45.5t-44 -113.5q0 -67 44.5 -110.5t112.5 -43.5t111.5 44t43.5 110q0 68 -43.5 113.5
+t-112.5 45.5z" />
+    <glyph glyph-name="colon" unicode=":" horiz-adv-x="228" 
+d="M64 375q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35zM64 40q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35z" />
+    <glyph glyph-name="semicolon" unicode=";" horiz-adv-x="274" 
+d="M78 375q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35zM24 -133l82 231l66 -24l-101 -226z" />
+    <glyph glyph-name="less" unicode="&#x3c;" horiz-adv-x="500" 
+d="M110 330l357 -162v-72l-434 203v62l434 203v-72z" />
+    <glyph glyph-name="equal" unicode="=" horiz-adv-x="500" 
+d="M33 384v66h434v-66h-434zM33 230v66h434v-66h-434z" />
+    <glyph glyph-name="greater" unicode="&#x3e;" horiz-adv-x="500" 
+d="M390 330l-357 162v72l434 -203v-62l-434 -203v72z" />
+    <glyph glyph-name="question" unicode="?" horiz-adv-x="436" 
+d="M289 286h72q2 -55 -23 -92q-18 -27 -50.5 -44t-69.5 -17q-63 0 -104 41t-41 103q0 85 83 121q19 8 43.5 16t41.5 14q29 12 46.5 30.5t17.5 56.5q0 44 -23 68t-65 24q-8 0 -18 -2t-28 -9.5t-30 -28.5t-12 -52q0 -17 6 -38h-74q-4 21 -4 46q0 71 45 111.5t115 40.5
+q80 0 121 -47t41 -112q0 -112 -111 -150q-9 -3 -26 -8t-29 -9.5t-25 -11.5q-41 -21 -41 -65q0 -32 20 -51.5t50 -19.5q39 0 58 30q14 24 14 55zM168 40q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35z" />
+    <glyph glyph-name="at" unicode="@" horiz-adv-x="733" 
+d="M480 427l13 64h52q-38 -161 -47 -249v-8q3 -46 37 -46q39 0 76 54t37 137q0 111 -77.5 180t-189.5 69q-124 0 -210.5 -86.5t-86.5 -211.5q0 -51 16 -100t50 -94t96 -73t144 -28q110 0 209 46l15 -46q-100 -48 -225 -48q-156 0 -256 96t-100 248q0 145 100.5 245
+t246.5 100q131 0 223.5 -82.5t92.5 -213.5q0 -100 -50.5 -168t-112.5 -68q-31 0 -51 18.5t-22 48.5q-46 -71 -125 -71q-63 0 -101.5 46t-38.5 115q0 83 49.5 143t125.5 60q43 0 70.5 -21.5t39.5 -55.5zM346 194q51 0 83 47.5t32 111.5q0 49 -25 73t-61 24q-51 0 -86.5 -44.5
+t-35.5 -102.5q0 -51 27.5 -80t65.5 -29z" />
+    <glyph glyph-name="A" unicode="A" horiz-adv-x="622" 
+d="M532 0l-78 188h-284l-80 -188h-82l307 699l299 -699h-82zM199 258h226l-112 267z" />
+    <glyph glyph-name="B" unicode="B" horiz-adv-x="530" 
+d="M86 667h120q100 0 153 -42q60 -46 60 -138q0 -86 -66 -131q64 -16 95.5 -64.5t31.5 -103.5q0 -45 -19.5 -83t-48.5 -61q-56 -44 -160 -44h-166v667zM160 597v-223h56q15 0 29 1.5t33.5 8t34 17.5t24.5 33t10 51q0 31 -12 53.5t-27 33.5t-36 17t-32 7t-22 1h-58zM160 308
+v-238h85q81 0 117 28q44 32 44 92q0 67 -57 98q-36 20 -113 20h-76z" />
+    <glyph glyph-name="C" unicode="C" horiz-adv-x="646" 
+d="M596 612v-92q-40 40 -87 62q-57 26 -115 26q-115 0 -191.5 -80.5t-76.5 -197.5q0 -126 83.5 -199t185.5 -73q64 0 124 31q42 21 77 56v-92q-91 -65 -201 -65q-148 0 -246.5 99t-98.5 244q0 149 99.5 248t249.5 99q110 0 197 -66z" />
+    <glyph glyph-name="D" unicode="D" horiz-adv-x="632" 
+d="M86 0v667h136q154 0 246 -78q114 -95 114 -256q0 -156 -117 -256q-48 -41 -103 -59t-141 -18h-135zM160 597v-527h65q119 0 191 60q90 76 90 203q0 120 -77 192t-204 72h-65z" />
+    <glyph glyph-name="E" unicode="E" horiz-adv-x="482" 
+d="M432 667v-70h-272v-198h264v-70h-264v-259h272v-70h-346v667h346z" />
+    <glyph glyph-name="F" unicode="F" horiz-adv-x="434" 
+d="M390 667v-70h-230v-197h222v-70h-222v-330h-74v667h304z" />
+    <glyph glyph-name="G" unicode="G" horiz-adv-x="758" 
+d="M430 265v70h278v-16q0 -124 -64 -211q-89 -120 -248 -120q-148 0 -247 99.5t-99 245.5t99.5 245.5t248.5 99.5q118 0 204 -66q44 -34 77 -79l-55 -50q-29 41 -63 69q-69 56 -162 56q-119 0 -196 -79t-77 -197q0 -86 42 -150.5t102.5 -94t126.5 -29.5t120.5 30.5
+t83.5 81.5q25 43 27 95h-198z" />
+    <glyph glyph-name="H" unicode="H" horiz-adv-x="662" 
+d="M160 667v-271h342v271h74v-667h-74v326h-342v-326h-74v667h74z" />
+    <glyph glyph-name="I" unicode="I" horiz-adv-x="246" 
+d="M86 667h74v-667h-74v667z" />
+    <glyph glyph-name="J" unicode="J" horiz-adv-x="306" 
+d="M146 667h74v-539q0 -63 -12.5 -104.5t-52.5 -68.5q-37 -24 -82 -24q-63 0 -119 49l41 58q41 -37 77 -37t54 25q20 25 20 102v539z" />
+    <glyph glyph-name="K" unicode="K" 
+d="M160 667v-286l284 286h100l-312 -311l322 -356h-102l-273 306l-19 -19v-287h-74v667h74z" />
+    <glyph glyph-name="L" unicode="L" horiz-adv-x="358" 
+d="M86 667h74v-597h184v-70h-258v667z" />
+    <glyph glyph-name="M" unicode="M" horiz-adv-x="863" 
+d="M139 0h-76l111 704l256 -563l261 564l104 -705h-76l-63 464l-227 -492l-224 492z" />
+    <glyph glyph-name="N" unicode="N" horiz-adv-x="766" 
+d="M160 0h-74v695l520 -545v517h74v-697l-520 545v-515z" />
+    <glyph glyph-name="O" unicode="O" horiz-adv-x="794" 
+d="M397 -12q-146 0 -246.5 99t-100.5 246q0 148 101 246.5t246 98.5t246 -98.5t101 -246.5q0 -147 -100.5 -246t-246.5 -99zM397 608q-113 0 -192 -80t-79 -195t79 -195t192 -80t192 80t79 195t-79 195t-192 80z" />
+    <glyph glyph-name="P" unicode="P" horiz-adv-x="456" 
+d="M86 667h121q114 0 168 -47q61 -52 61 -143q0 -78 -52.5 -132.5t-158.5 -54.5h-65v-290h-74v667zM160 597v-237h61q66 0 103.5 32t37.5 89q0 116 -147 116h-55z" />
+    <glyph glyph-name="Q" unicode="Q" horiz-adv-x="794" 
+d="M652 -7l-54 56q-90 -61 -201 -61q-145 0 -246 99t-101 246q0 148 101 246.5t246 98.5t246 -98.5t101 -246.5q0 -140 -94 -239l98 -101h-96zM480 268l117 -120q71 77 71 185q0 114 -79.5 194.5t-191.5 80.5q-113 0 -192 -80t-79 -195t79 -195t192 -80q81 0 148 44
+l-161 166h96z" />
+    <glyph glyph-name="R" unicode="R" horiz-adv-x="502" 
+d="M86 667h107q108 0 162 -37q73 -49 73 -148q0 -77 -46.5 -128t-121.5 -56l212 -298h-90l-203 292h-19v-292h-74v667zM160 597v-239h47q69 0 108 32t39 90q0 68 -53 98q-34 19 -96 19h-45z" />
+    <glyph glyph-name="S" unicode="S" horiz-adv-x="518" 
+d="M443 574l-59 -39q-16 31 -39 50q-30 23 -75 23q-50 0 -81 -28.5t-31 -74.5q0 -34 25 -58q20 -20 70 -42l64 -28q71 -31 103 -63q48 -48 48 -121q0 -91 -60 -148t-151 -57q-90 0 -145 54q-45 44 -62 118l72 20q7 -50 40 -85q37 -37 96 -37q64 0 99 38.5t35 93.5
+q0 48 -33 78q-19 18 -80 46l-60 28q-61 28 -89 55q-46 44 -46 109q0 77 52.5 124.5t134.5 47.5q62 0 107 -30q41 -27 65 -74z" />
+    <glyph glyph-name="T" unicode="T" horiz-adv-x="426" 
+d="M412 597h-162v-597h-74v597h-162v70h398v-70z" />
+    <glyph glyph-name="U" unicode="U" horiz-adv-x="646" 
+d="M160 667v-409q0 -38 1.5 -59t14 -51.5t37.5 -51.5q45 -38 110 -38t110 38q25 21 37.5 51.5t14 51.5t1.5 59v409h74v-429q0 -102 -42 -160q-31 -42 -81.5 -66t-113.5 -24t-113.5 24t-81.5 66q-42 58 -42 160v429h74z" />
+    <glyph glyph-name="V" unicode="V" horiz-adv-x="556" 
+d="M8 667h82l188 -509l188 509h82l-270 -707z" />
+    <glyph glyph-name="W" unicode="W" horiz-adv-x="958" 
+d="M8 667h82l176 -500l209 534l198 -534l195 500h82l-280 -702l-196 534l-210 -534z" />
+    <glyph glyph-name="X" unicode="X" horiz-adv-x="504" 
+d="M14 0l197 345l-179 322h84l138 -251l142 251h84l-183 -322l193 -345h-84l-153 274l-155 -274h-84z" />
+    <glyph glyph-name="Y" unicode="Y" horiz-adv-x="530" 
+d="M227 0v287l-219 380h86l170 -298l172 298h86l-221 -380v-287h-74z" />
+    <glyph glyph-name="Z" unicode="Z" horiz-adv-x="536" 
+d="M512 667l-364 -597h354v-70h-478l364 597h-317v70h441z" />
+    <glyph glyph-name="bracketleft" unicode="[" horiz-adv-x="290" 
+d="M248 663h-96v-773h96v-66h-168v905h168v-66z" />
+    <glyph glyph-name="backslash" unicode="\" horiz-adv-x="580" 
+d="M580 -152l-58 -28l-502 900l58 30z" />
+    <glyph glyph-name="bracketright" unicode="]" horiz-adv-x="290" 
+d="M42 -110h96v773h-96v66h168v-905h-168v66z" />
+    <glyph glyph-name="asciicircum" unicode="^" horiz-adv-x="500" 
+d="M250 612l-146 -247h-78l177 302h94l177 -302h-78z" />
+    <glyph glyph-name="underscore" unicode="_" horiz-adv-x="500" 
+d="M0 -132v64h500v-64h-500z" />
+    <glyph glyph-name="grave" unicode="`" horiz-adv-x="500" 
+d="M249 497l-135 135l74 34l105 -145z" />
+    <glyph glyph-name="a" unicode="a" horiz-adv-x="526" 
+d="M386 356v59h72v-415h-72v58q-54 -70 -142 -70q-84 0 -143 58t-59 162q0 102 58.5 159.5t143.5 57.5q90 0 142 -69zM252 361q-57 0 -96.5 -40.5t-39.5 -112.5q0 -69 36.5 -112.5t99.5 -43.5t100.5 45t37.5 110q0 74 -42 114t-96 40z" />
+    <glyph glyph-name="b" unicode="b" horiz-adv-x="526" 
+d="M140 58v-58h-72v729h72v-372q55 68 142 68q93 0 147.5 -65.5t54.5 -154.5q0 -90 -54 -153.5t-145 -63.5t-145 70zM275 52q61 0 98 44.5t37 108.5q0 72 -41 114t-95 42t-96 -42t-42 -113q0 -65 38.5 -109.5t100.5 -44.5z" />
+    <glyph glyph-name="c" unicode="c" horiz-adv-x="438" 
+d="M393 385v-86q-49 62 -130 62q-61 0 -104 -41.5t-43 -113.5t43 -113t103 -41q80 0 134 65v-86q-59 -43 -132 -43q-97 0 -159.5 61.5t-62.5 155.5q0 95 63 157.5t159 62.5q72 0 129 -40z" />
+    <glyph glyph-name="d" unicode="d" horiz-adv-x="526" 
+d="M386 356v373h72v-729h-72v58q-54 -70 -142 -70q-84 0 -143 58t-59 162q0 102 58.5 159.5t143.5 57.5q90 0 142 -69zM252 361q-57 0 -96.5 -40.5t-39.5 -112.5q0 -69 36.5 -112.5t99.5 -43.5t100.5 45t37.5 110q0 74 -42 114t-96 40z" />
+    <glyph glyph-name="e" unicode="e" horiz-adv-x="481" 
+d="M376 139l60 -33q-21 -40 -51 -67q-57 -51 -138 -51q-84 0 -144.5 54.5t-60.5 162.5q0 95 54.5 157.5t146.5 62.5q104 0 157 -76q42 -60 39 -156h-323q1 -63 39 -102t93 -39q49 0 85 30q28 24 43 57zM121 253h244q-9 49 -42.5 78.5t-79.5 29.5q-45 0 -79.5 -30.5
+t-42.5 -77.5z" />
+    <glyph glyph-name="f" unicode="f" horiz-adv-x="240" 
+d="M244 349h-102v-349h-72v349h-40v66h40v179q0 83 40 117q32 27 83 27q27 0 51 -9v-70q-22 9 -47 9q-33 0 -46 -22q-9 -15 -9 -60v-171h102v-66z" />
+    <glyph glyph-name="g" unicode="g" horiz-adv-x="526" 
+d="M386 356v59h72v-406q0 -109 -40 -163q-57 -77 -166 -77q-92 0 -148 56q-54 54 -54 134h71q0 -50 32 -86q36 -40 100 -40q75 0 110 58q23 37 23 106v61q-54 -70 -142 -70q-84 0 -143 58t-59 162q0 102 58.5 159.5t143.5 57.5q90 0 142 -69zM252 361q-57 0 -96.5 -40.5
+t-39.5 -112.5q0 -69 36.5 -112.5t99.5 -43.5t100.5 45t37.5 110q0 74 -42 114t-96 40z" />
+    <glyph glyph-name="h" unicode="h" horiz-adv-x="470" 
+d="M68 0v729h72v-362q44 58 119 58q80 0 119 -57q24 -35 24 -111v-257h-72v255q0 51 -19 76q-23 30 -68 30q-46 0 -73 -32q-30 -35 -30 -112v-217h-72z" />
+    <glyph glyph-name="i" unicode="i" horiz-adv-x="208" 
+d="M68 415h72v-415h-72v415zM53 572q0 21 15 36t36 15t36 -15t15 -36t-15 -36t-36 -15t-36 15t-15 36z" />
+    <glyph glyph-name="j" unicode="j" horiz-adv-x="208" 
+d="M68 415h72v-649h-72v649zM53 572q0 21 15 36t36 15t36 -15t15 -36t-15 -36t-36 -15t-36 15t-15 36z" />
+    <glyph glyph-name="k" unicode="k" horiz-adv-x="422" 
+d="M68 729h72v-468l152 154h94l-178 -178l210 -237h-94l-166 189l-18 -18v-171h-72v729z" />
+    <glyph glyph-name="l" unicode="l" horiz-adv-x="208" 
+d="M68 729h72v-729h-72v729z" />
+    <glyph glyph-name="m" unicode="m" horiz-adv-x="685" 
+d="M68 0v415h72v-44q37 54 103 54q76 0 114 -67q43 67 125 67q135 0 135 -162v-263h-72v252q0 30 -3.5 50.5t-21 39.5t-49.5 19q-92 0 -92 -130v-231h-72v246q0 115 -76 115q-91 0 -91 -135v-226h-72z" />
+    <glyph glyph-name="n" unicode="n" horiz-adv-x="470" 
+d="M68 0v415h72v-48q44 58 119 58q80 0 119 -57q24 -35 24 -111v-257h-72v255q0 51 -19 76q-23 30 -68 30q-46 0 -73 -32q-30 -35 -30 -112v-217h-72z" />
+    <glyph glyph-name="o" unicode="o" horiz-adv-x="524" 
+d="M262 -12q-95 0 -157.5 62t-62.5 156q0 95 62.5 157t157.5 62t157.5 -62t62.5 -157q0 -94 -62.5 -156t-157.5 -62zM262 361q-61 0 -103.5 -41.5t-42.5 -113.5t43 -113t103 -41t103 41t43 113q0 73 -42.5 114t-103.5 41z" />
+    <glyph glyph-name="p" unicode="p" horiz-adv-x="526" 
+d="M140 58v-292h-72v649h72v-58q55 68 142 68q93 0 147.5 -65.5t54.5 -154.5q0 -90 -54 -153.5t-145 -63.5t-145 70zM275 52q61 0 98 44.5t37 108.5q0 72 -41 114t-95 42t-96 -42t-42 -113q0 -65 38.5 -109.5t100.5 -44.5z" />
+    <glyph glyph-name="q" unicode="q" horiz-adv-x="526" 
+d="M386 356v59h72v-649h-72v292q-54 -70 -142 -70q-84 0 -143 58t-59 162q0 102 58.5 159.5t143.5 57.5q90 0 142 -69zM252 361q-57 0 -96.5 -40.5t-39.5 -112.5q0 -69 36.5 -112.5t99.5 -43.5t100.5 45t37.5 110q0 74 -42 114t-96 40z" />
+    <glyph glyph-name="r" unicode="r" horiz-adv-x="296" 
+d="M68 0v415h72v-45q35 55 94 55q30 0 64 -17l-34 -65q-20 14 -42 14q-38 0 -58 -26q-24 -32 -24 -112v-219h-72z" />
+    <glyph glyph-name="s" unicode="s" horiz-adv-x="352" 
+d="M298 350l-59 -31q-17 42 -58 42q-21 0 -35.5 -12t-14.5 -35q0 -21 13.5 -31.5t53.5 -27.5q79 -33 104 -64q22 -27 22 -67q0 -63 -40.5 -99.5t-102.5 -36.5q-12 0 -26 2.5t-38.5 11t-46.5 32.5t-34 60l62 26q25 -68 84 -68q33 0 50.5 20t17.5 46q0 14 -4.5 24.5t-15 18.5
+t-20 13t-27.5 13q-80 35 -101 61q-19 24 -19 61q0 54 33.5 85t87.5 31q79 0 114 -75z" />
+    <glyph glyph-name="t" unicode="t" horiz-adv-x="214" 
+d="M214 349h-72v-349h-72v349h-44v66h44v150h72v-150h72v-66z" />
+    <glyph glyph-name="u" unicode="u" horiz-adv-x="466" 
+d="M140 415v-242q0 -63 20 -89q25 -32 73 -32t73 32q20 26 20 89v242h72v-250q0 -84 -38 -125q-49 -52 -127 -52t-127 52q-38 41 -38 125v250h72z" />
+    <glyph glyph-name="v" unicode="v" horiz-adv-x="424" 
+d="M8 415h80l124 -280l124 280h80l-204 -442z" />
+    <glyph glyph-name="w" unicode="w" horiz-adv-x="682" 
+d="M8 415h80l123 -282l130 314l129 -314l124 282h80l-206 -444l-128 310l-128 -310z" />
+    <glyph glyph-name="x" unicode="x" horiz-adv-x="454" 
+d="M8 0l177 222l-151 193h88l107 -141l109 141h88l-152 -194l172 -221h-88l-131 169l-131 -169h-88z" />
+    <glyph glyph-name="y" unicode="y" horiz-adv-x="460" 
+d="M65 -234l134 273l-191 376h82l147 -297l135 297h80l-307 -649h-80z" />
+    <glyph glyph-name="z" unicode="z" horiz-adv-x="460" 
+d="M452 415l-300 -349h288v-66h-432l300 349h-242v66h386z" />
+    <glyph glyph-name="braceleft" unicode="{" horiz-adv-x="501" 
+d="M275 530v-170q0 -48 -13 -71q-16 -27 -48 -42q32 -15 48 -42q13 -23 13 -71v-170q0 -51 19 -69t62 -18h15v-61h-27q-63 0 -93 20q-48 32 -48 116v177q0 39 -13 61q-16 26 -44 28v58q28 2 44 28q13 22 13 61v177q0 84 48 116q30 20 93 20h27v-61h-15q-43 0 -62 -18
+t-19 -69z" />
+    <glyph glyph-name="bar" unicode="|" horiz-adv-x="500" 
+d="M214 -250v1000h72v-1000h-72z" />
+    <glyph glyph-name="braceright" unicode="}" horiz-adv-x="501" 
+d="M226 -36v170q0 48 13 71q16 27 48 42q-32 15 -48 42q-13 23 -13 71v170q0 51 -19 69t-62 18h-15v61h27q63 0 93 -20q48 -32 48 -116v-177q0 -39 13 -61q16 -26 44 -28v-58q-28 -2 -44 -28q-13 -22 -13 -61v-177q0 -84 -48 -116q-30 -20 -93 -20h-27v61h15q43 0 62 18
+t19 69z" />
+    <glyph glyph-name="asciitilde" unicode="~" horiz-adv-x="668" 
+d="M530 727l44 -26q-19 -44 -50 -73q-39 -39 -96 -39q-34 0 -108 34q-69 32 -95 32q-29 0 -52 -20q-19 -16 -35 -52l-44 26q23 49 48 78q34 38 87 38q42 0 113 -34q69 -32 95 -32q37 0 61 24q19 17 32 44z" />
+    <glyph glyph-name="currency" unicode="&#xa4;" horiz-adv-x="668" 
+d="M94 636l95 -94q59 51 145 51t145 -51l95 94l40 -40l-95 -95q51 -60 51 -142q0 -83 -51 -142l94 -94l-40 -40l-94 93q-59 -51 -145 -51t-145 51l-94 -93l-40 40l94 94q-51 59 -51 142t51 142l-95 95zM334 189q72 0 120 48t48 122t-48 122t-120 48t-120 -48t-48 -122
+t48 -122t120 -48z" />
+    <glyph glyph-name="brokenbar" unicode="&#xa6;" horiz-adv-x="500" 
+d="M214 390v360h72v-360h-72zM214 -250v360h72v-360h-72z" />
+    <glyph glyph-name="section" unicode="&#xa7;" 
+d="M464 548h-72q1 42 -17 77q-25 46 -81 46q-47 0 -75.5 -29.5t-28.5 -72.5q0 -21 9 -38.5t18 -27t32 -21t30 -14.5t33 -12q73 -26 102 -51q53 -46 53 -124q0 -53 -27 -93.5t-74 -62.5q85 -38 85 -136q0 -70 -44 -121t-131 -51q-61 0 -101 27.5t-57 70.5q-16 39 -14 87h72
+q0 -46 19 -76q27 -43 82 -43q47 0 73.5 27.5t26.5 75.5q0 44 -23.5 66.5t-64.5 38.5q-4 1 -25 8.5t-30.5 12t-27 12t-31 16.5t-24.5 19q-50 45 -50 119q0 115 101 157q-84 44 -84 136q0 69 45.5 117.5t129.5 48.5q113 0 154 -96q18 -44 17 -93zM284 389q-46 0 -77.5 -31.5
+t-31.5 -77.5t31.5 -77.5t77.5 -31.5t77.5 31.5t31.5 77.5t-31.5 77.5t-77.5 31.5z" />
+    <glyph glyph-name="copyright" unicode="&#xa9;" horiz-adv-x="740" 
+d="M25 333q0 143 101.5 244t243.5 101t243.5 -101t101.5 -244q0 -142 -101.5 -243t-243.5 -101t-243.5 101t-101.5 243zM659 333q0 120 -84.5 204.5t-204.5 84.5t-204.5 -84.5t-84.5 -204.5q0 -119 84.5 -203.5t204.5 -84.5t204.5 84.5t84.5 203.5zM514 497v-80
+q-24 30 -58 44t-64 14q-57 0 -97.5 -39.5t-40.5 -106.5q0 -68 41 -106t96 -38q75 0 126 61v-81q-55 -40 -124 -40q-90 0 -149 57t-59 146q0 88 59.5 147.5t148.5 59.5q69 0 121 -38z" />
+    <glyph glyph-name="guillemotleft" unicode="&#xab;" horiz-adv-x="430" 
+d="M112 212l120 -146l-46 -36l-150 182l150 188l46 -37zM260 212l120 -146l-46 -36l-150 182l150 188l46 -37z" />
+    <glyph glyph-name="logicalnot" unicode="&#xac;" horiz-adv-x="500" 
+d="M467 0h-70v203h-364v64h434v-267z" />
+    <glyph glyph-name="registered" unicode="&#xae;" horiz-adv-x="752" 
+d="M33 332q0 147 98 245t245 98t245 -98t98 -245t-98 -245t-245 -98t-245 98t-98 245zM663 332q0 123 -82 205t-205 82t-205 -82t-82 -205t82 -205t205 -82t205 82t82 205zM515 145h-68l-122 158v-158h-54v374h68q144 0 144 -107q0 -42 -27 -70.5t-70 -33.5zM325 471v-120
+h22q82 0 82 59q0 17 -6 29t-13.5 18t-20.5 9.5t-22 4t-22 0.5h-20z" />
+    <glyph glyph-name="degree" unicode="&#xb0;" horiz-adv-x="493" 
+d="M108 531q0 59 39.5 98.5t98.5 39.5t98.5 -39.5t39.5 -98.5t-39.5 -98.5t-98.5 -39.5t-98.5 39.5t-39.5 98.5zM158 531q0 -43 27.5 -67.5t60.5 -24.5t60.5 24.5t27.5 67.5t-27.5 67.5t-60.5 24.5t-60.5 -24.5t-27.5 -67.5z" />
+    <glyph glyph-name="plusminus" unicode="&#xb1;" horiz-adv-x="500" 
+d="M33 345v66h181v171h72v-171h181v-66h-181v-171h-72v171h-181zM33 67v66h434v-66h-434z" />
+    <glyph glyph-name="mu" unicode="&#xb5;" horiz-adv-x="523" 
+d="M140 -170h-72v585h72v-207q0 -80 30 -118t88 -38q66 0 101 59q24 39 24 106v198h72v-415h-72v86q-16 -44 -49 -71t-78 -27q-34 0 -62 17t-37.5 30t-16.5 26v-231z" />
+    <glyph glyph-name="mu" unicode="&#x3bc;" horiz-adv-x="523" 
+d="M140 -170h-72v585h72v-207q0 -80 30 -118t88 -38q66 0 101 59q24 39 24 106v198h72v-415h-72v86q-16 -44 -49 -71t-78 -27q-34 0 -62 17t-37.5 30t-16.5 26v-231z" />
+    <glyph glyph-name="paragraph" unicode="&#xb6;" horiz-adv-x="580" 
+d="M282 -99h-62v413h-15q-80 0 -131.5 49t-51.5 125q0 50 25.5 95.5t82.5 66.5q44 17 112 17h268v-58h-60v-708h-62v708h-106v-708z" />
+    <glyph glyph-name="periodcentered" unicode="&#xb7;" horiz-adv-x="200" 
+d="M36 340q0 26 19 45t45 19t45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45z" />
+    <glyph glyph-name="periodcentered" unicode="&#x2219;" horiz-adv-x="200" 
+d="M36 340q0 26 19 45t45 19t45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45z" />
+    <glyph glyph-name="guillemotright" unicode="&#xbb;" horiz-adv-x="430" 
+d="M170 218l-120 146l46 36l150 -182l-150 -188l-46 37zM318 218l-120 146l46 36l150 -182l-150 -188l-46 37z" />
+    <glyph glyph-name="afii10023" unicode="&#x401;" horiz-adv-x="482" 
+d="M432 667v-70h-272v-198h264v-70h-264v-259h272v-70h-346v667h346zM112 784q0 21 15 36t36 15t36 -15t15 -36t-15 -36t-36 -15t-36 15t-15 36zM302 784q0 21 15 36t36 15t36 -15t15 -36t-15 -36t-36 -15t-36 15t-15 36z" />
+    <glyph glyph-name="afii10051" unicode="&#x402;" horiz-adv-x="678" 
+d="M450 597h-202v-196q84 7 138 7q92 0 150 -32q100 -55 100 -186q0 -92 -55.5 -147t-142.5 -55q-69 0 -128 31l24 69q51 -30 105 -30q55 0 88 35t33 94q0 93 -71 130q-42 21 -111 21q-58 0 -130 -7v-331h-74v597h-160v70h436v-70z" />
+    <glyph glyph-name="afii10052" unicode="&#x403;" horiz-adv-x="404" 
+d="M86 0v667h304v-70h-230v-597h-74zM322 882l70 -45l-153 -117l-43 32z" />
+    <glyph glyph-name="afii10053" unicode="&#x404;" horiz-adv-x="645" 
+d="M595 612v-92q-36 36 -87 60q-64 28 -135 28q-101 0 -168.5 -65.5t-75.5 -162.5h386v-70h-389q-1 -110 73 -181t175 -71q75 0 144 33q47 23 77 54v-92q-101 -65 -221 -65q-148 0 -236 97t-88 246q0 152 89.5 249.5t238.5 97.5q121 0 217 -66z" />
+    <glyph glyph-name="afii10054" unicode="&#x405;" horiz-adv-x="518" 
+d="M443 574l-59 -39q-16 31 -39 50q-30 23 -75 23q-50 0 -81 -28.5t-31 -74.5q0 -34 25 -58q20 -20 70 -42l64 -28q71 -31 103 -63q48 -48 48 -121q0 -91 -60 -148t-151 -57q-90 0 -145 54q-45 44 -62 118l72 20q7 -50 40 -85q37 -37 96 -37q64 0 99 38.5t35 93.5
+q0 48 -33 78q-19 18 -80 46l-60 28q-61 28 -89 55q-46 44 -46 109q0 77 52.5 124.5t134.5 47.5q62 0 107 -30q41 -27 65 -74z" />
+    <glyph glyph-name="afii10055" unicode="&#x406;" horiz-adv-x="246" 
+d="M86 667h74v-667h-74v667z" />
+    <glyph glyph-name="afii10056" unicode="&#x407;" horiz-adv-x="246" 
+d="M86 667h74v-667h-74v667zM0 784q0 21 15 36t36 15t36 -15t15 -36t-15 -36t-36 -15t-36 15t-15 36zM144 784q0 21 15 36t36 15t36 -15t15 -36t-15 -36t-36 -15t-36 15t-15 36z" />
+    <glyph glyph-name="afii10057" unicode="&#x408;" horiz-adv-x="306" 
+d="M146 667h74v-539q0 -63 -12.5 -104.5t-52.5 -68.5q-37 -24 -82 -24q-63 0 -119 49l41 58q41 -37 77 -37t54 25q20 25 20 102v539z" />
+    <glyph glyph-name="afii10058" unicode="&#x409;" horiz-adv-x="922" 
+d="M154 667h406v-277h90q230 0 230 -188q0 -94 -58.5 -148t-157.5 -54h-178v597h-258v-358q0 -47 -1 -70.5t-15.5 -66t-43.5 -68.5q-45 -40 -114 -40q-16 0 -40 6v70q20 -6 37 -6q39 0 65 26q18 18 27 51.5t10 56.5t1 71v398zM560 320v-250h86q85 0 123 37q35 33 35 95
+q0 72 -54 101q-31 17 -112 17h-78z" />
+    <glyph glyph-name="afii10059" unicode="&#x40a;" horiz-adv-x="938" 
+d="M160 667v-277h342v277h74v-277h90q230 0 230 -188q0 -94 -58.5 -148t-157.5 -54h-178v320h-342v-320h-74v667h74zM576 320v-250h86q85 0 123 37q35 33 35 95q0 72 -54 101q-31 17 -112 17h-78z" />
+    <glyph glyph-name="afii10060" unicode="&#x40b;" horiz-adv-x="678" 
+d="M450 597h-202v-196q93 7 138 7q119 0 165 -36q61 -47 61 -155v-217h-74v207q0 81 -40 110q-28 21 -116 21q-59 0 -134 -7v-331h-74v597h-160v70h436v-70z" />
+    <glyph glyph-name="afii10061" unicode="&#x40c;" 
+d="M160 667v-286l284 286h100l-312 -311l322 -356h-102l-273 306l-19 -19v-287h-74v667h74zM366 882l70 -45l-153 -117l-43 32z" />
+    <glyph glyph-name="afii10062" unicode="&#x40e;" 
+d="M8 667h90l208 -393l174 393h80l-301 -667h-80l85 186zM135 818l70 23q6 -19 18.5 -31.5t28 -17t23.5 -6t15 -1.5t15 1.5t23.5 6t28 17t18.5 31.5l70 -23q-4 -11 -11.5 -23t-24.5 -31t-48 -31t-71 -12t-71 12t-48 31t-24.5 31t-11.5 23z" />
+    <glyph glyph-name="afii10145" unicode="&#x40f;" horiz-adv-x="658" 
+d="M86 0v667h74v-597h338v597h74v-667h-208v-160h-70v160h-208z" />
+    <glyph glyph-name="afii10017" unicode="&#x410;" horiz-adv-x="622" 
+d="M532 0l-78 188h-284l-80 -188h-82l307 699l299 -699h-82zM199 258h226l-112 267z" />
+    <glyph glyph-name="afii10018" unicode="&#x411;" horiz-adv-x="530" 
+d="M86 0v667h331v-70h-257v-207h90q230 0 230 -188q0 -94 -58.5 -148t-157.5 -54h-178zM160 320v-250h86q85 0 123 37q35 33 35 95q0 72 -54 101q-31 17 -112 17h-78z" />
+    <glyph glyph-name="afii10019" unicode="&#x412;" horiz-adv-x="530" 
+d="M86 667h120q100 0 153 -42q60 -46 60 -138q0 -86 -66 -131q64 -16 95.5 -64.5t31.5 -103.5q0 -45 -19.5 -83t-48.5 -61q-56 -44 -160 -44h-166v667zM160 597v-223h56q15 0 29 1.5t33.5 8t34 17.5t24.5 33t10 51q0 31 -12 53.5t-27 33.5t-36 17t-32 7t-22 1h-58zM160 308
+v-238h85q81 0 117 28q44 32 44 92q0 67 -57 98q-36 20 -113 20h-76z" />
+    <glyph glyph-name="afii10020" unicode="&#x413;" horiz-adv-x="404" 
+d="M86 0v667h304v-70h-230v-597h-74z" />
+    <glyph glyph-name="afii10021" unicode="&#x414;" horiz-adv-x="640" 
+d="M144 667h406v-597h76v-210h-70v140h-472v-140h-70v210q34 -1 62 24.5t44 65.5q24 61 24 175v332zM218 597v-262q0 -123 -24 -186q-17 -45 -51 -79h333v527h-258z" />
+    <glyph glyph-name="afii10022" unicode="&#x415;" horiz-adv-x="482" 
+d="M432 667v-70h-272v-198h264v-70h-264v-259h272v-70h-346v667h346z" />
+    <glyph glyph-name="afii10024" unicode="&#x416;" horiz-adv-x="810" 
+d="M368 667h74v-286l254 286h92l-280 -311l288 -356h-94l-244 305l-16 -18v-287h-74v287l-16 18l-244 -305h-94l288 356l-280 311h92l254 -286v286z" />
+    <glyph glyph-name="afii10025" unicode="&#x417;" horiz-adv-x="576" 
+d="M157 536l-57 45q65 97 191 97q90 0 141 -47q52 -47 52 -123q0 -49 -25.5 -85.5t-66.5 -55.5q65 -15 99.5 -62t34.5 -110q0 -104 -83 -163q-64 -44 -152 -44q-91 0 -151 46t-90 114l72 32q20 -56 66 -89t106 -33q61 0 102 28q54 38 54 107q0 131 -178 131h-60v70h60
+q66 0 101 32t35 81q0 43 -27 70q-30 31 -90 31q-84 0 -134 -72z" />
+    <glyph glyph-name="afii10026" unicode="&#x418;" horiz-adv-x="710" 
+d="M550 0v509l-464 -539v697h74v-511l464 539v-695h-74z" />
+    <glyph glyph-name="afii10027" unicode="&#x419;" horiz-adv-x="710" 
+d="M550 0v509l-464 -539v697h74v-511l464 539v-695h-74zM200 818l70 23q6 -19 18.5 -31.5t28 -17t23.5 -6t15 -1.5t15 1.5t23.5 6t28 17t18.5 31.5l70 -23q-4 -11 -11.5 -23t-24.5 -31t-48 -31t-71 -12t-71 12t-48 31t-24.5 31t-11.5 23z" />
+    <glyph glyph-name="afii10028" unicode="&#x41a;" 
+d="M160 667v-286l284 286h100l-312 -311l322 -356h-102l-273 306l-19 -19v-287h-74v667h74z" />
+    <glyph glyph-name="afii10029" unicode="&#x41b;" horiz-adv-x="646" 
+d="M154 667h406v-667h-74v597h-258v-358q0 -47 -1 -70.5t-15.5 -66t-43.5 -68.5q-45 -40 -114 -40q-16 0 -40 6v70q20 -6 37 -6q39 0 65 26q18 18 27 51.5t10 56.5t1 71v398z" />
+    <glyph glyph-name="afii10030" unicode="&#x41c;" horiz-adv-x="863" 
+d="M139 0h-76l111 704l256 -563l261 564l104 -705h-76l-63 464l-227 -492l-224 492z" />
+    <glyph glyph-name="afii10031" unicode="&#x41d;" horiz-adv-x="662" 
+d="M160 667v-271h342v271h74v-667h-74v326h-342v-326h-74v667h74z" />
+    <glyph glyph-name="afii10032" unicode="&#x41e;" horiz-adv-x="794" 
+d="M397 -12q-146 0 -246.5 99t-100.5 246q0 148 101 246.5t246 98.5t246 -98.5t101 -246.5q0 -147 -100.5 -246t-246.5 -99zM397 608q-113 0 -192 -80t-79 -195t79 -195t192 -80t192 80t79 195t-79 195t-192 80z" />
+    <glyph glyph-name="afii10033" unicode="&#x41f;" horiz-adv-x="662" 
+d="M86 667h490v-667h-74v597h-342v-597h-74v667z" />
+    <glyph glyph-name="afii10034" unicode="&#x420;" horiz-adv-x="456" 
+d="M86 667h121q114 0 168 -47q61 -52 61 -143q0 -78 -52.5 -132.5t-158.5 -54.5h-65v-290h-74v667zM160 597v-237h61q66 0 103.5 32t37.5 89q0 116 -147 116h-55z" />
+    <glyph glyph-name="afii10035" unicode="&#x421;" horiz-adv-x="646" 
+d="M596 612v-92q-40 40 -87 62q-57 26 -115 26q-115 0 -191.5 -80.5t-76.5 -197.5q0 -126 83.5 -199t185.5 -73q64 0 124 31q42 21 77 56v-92q-91 -65 -201 -65q-148 0 -246.5 99t-98.5 244q0 149 99.5 248t249.5 99q110 0 197 -66z" />
+    <glyph glyph-name="afii10036" unicode="&#x422;" horiz-adv-x="426" 
+d="M412 597h-162v-597h-74v597h-162v70h398v-70z" />
+    <glyph glyph-name="afii10037" unicode="&#x423;" 
+d="M8 667h90l208 -393l174 393h80l-301 -667h-80l85 186z" />
+    <glyph glyph-name="afii10038" unicode="&#x424;" horiz-adv-x="822" 
+d="M374 0v94h-18q-71 0 -126.5 17t-88.5 43t-54.5 62t-29 69.5t-7.5 68.5t7.5 68.5t29 69.5t54.5 62t88.5 43t126.5 17h18v80h74v-80h18q71 0 126.5 -17t88.5 -43t54.5 -62t29 -69.5t7.5 -68.5t-7.5 -68.5t-29 -69.5t-54.5 -62t-88.5 -43t-126.5 -17h-18v-94h-74zM374 164
+v380h-20q-65 0 -112 -17t-71 -46.5t-34.5 -60.5t-10.5 -66t10.5 -66t34.5 -60.5t71 -46.5t112 -17h20zM448 544v-380h20q65 0 112 17t71 46.5t34.5 60.5t10.5 66t-10.5 66t-34.5 60.5t-71 46.5t-112 17h-20z" />
+    <glyph glyph-name="afii10039" unicode="&#x425;" horiz-adv-x="504" 
+d="M14 0l197 345l-179 322h84l138 -251l142 251h84l-183 -322l193 -345h-84l-153 274l-155 -274h-84z" />
+    <glyph glyph-name="afii10040" unicode="&#x426;" horiz-adv-x="662" 
+d="M86 0v667h74v-597h338v597h74v-597h76v-210h-70v140h-492z" />
+    <glyph glyph-name="afii10041" unicode="&#x427;" horiz-adv-x="590" 
+d="M430 0v286q-93 -7 -138 -7q-119 0 -165 36q-61 47 -61 155v197h74v-187q0 -81 40 -110q28 -21 116 -21q59 0 134 7v311h74v-667h-74z" />
+    <glyph glyph-name="afii10042" unicode="&#x428;" horiz-adv-x="922" 
+d="M86 0v667h74v-597h264v597h74v-597h264v597h74v-667h-750z" />
+    <glyph glyph-name="afii10043" unicode="&#x429;" horiz-adv-x="926" 
+d="M86 0v667h74v-597h264v597h74v-597h264v597h74v-597h76v-210h-70v140h-756z" />
+    <glyph glyph-name="afii10044" unicode="&#x42a;" horiz-adv-x="610" 
+d="M174 0v597h-160v70h234v-277h90q230 0 230 -188q0 -94 -58.5 -148t-157.5 -54h-178zM248 320v-250h86q85 0 123 37q35 33 35 95q0 72 -54 101q-31 17 -112 17h-78z" />
+    <glyph glyph-name="afii10045" unicode="&#x42b;" horiz-adv-x="710" 
+d="M86 0v667h74v-277h90q230 0 230 -188q0 -94 -58.5 -148t-157.5 -54h-178zM160 320v-250h86q85 0 123 37q35 33 35 95q0 72 -54 101q-31 17 -112 17h-78zM550 667h74v-667h-74v667z" />
+    <glyph glyph-name="afii10046" unicode="&#x42c;" horiz-adv-x="522" 
+d="M86 0v667h74v-277h90q230 0 230 -188q0 -94 -58.5 -148t-157.5 -54h-178zM160 320v-250h86q85 0 123 37q35 33 35 95q0 72 -54 101q-31 17 -112 17h-78z" />
+    <glyph glyph-name="afii10047" unicode="&#x42d;" horiz-adv-x="645" 
+d="M50 520v92q96 66 217 66q149 0 238.5 -97.5t89.5 -249.5q0 -149 -88 -246t-236 -97q-120 0 -221 65v92q30 -31 77 -54q69 -33 144 -33q101 0 175 71t73 181h-389v70h386q-8 97 -75.5 162.5t-168.5 65.5q-71 0 -135 -28q-51 -24 -87 -60z" />
+    <glyph glyph-name="afii10048" unicode="&#x42e;" horiz-adv-x="990" 
+d="M160 389h102q18 125 111.5 207t225.5 82q148 0 244.5 -97t96.5 -248q0 -149 -97 -247t-244 -98q-145 0 -240 97t-100 234h-99v-319h-74v667h74v-278zM599 608q-111 0 -187.5 -79t-76.5 -196t76.5 -196t187.5 -79t188 79t77 196t-77 196t-188 79z" />
+    <glyph glyph-name="afii10049" unicode="&#x42f;" horiz-adv-x="510" 
+d="M30 0l196 296q-68 7 -112 42q-58 47 -58 136q0 82 46 133q54 60 166 60h156v-667h-74v292h-40l-190 -292h-90zM350 597h-72q-80 0 -114 -35q-32 -34 -32 -86q0 -59 38 -90q36 -28 102 -28h78v239z" />
+    <glyph glyph-name="afii10065" unicode="&#x430;" horiz-adv-x="526" 
+d="M386 356v59h72v-415h-72v58q-54 -70 -142 -70q-84 0 -143 58t-59 162q0 102 58.5 159.5t143.5 57.5q90 0 142 -69zM252 361q-57 0 -96.5 -40.5t-39.5 -112.5q0 -69 36.5 -112.5t99.5 -43.5t100.5 45t37.5 110q0 74 -42 114t-96 40z" />
+    <glyph glyph-name="afii10066" unicode="&#x431;" horiz-adv-x="524" 
+d="M388 700h80q-11 -37 -37.5 -62.5t-48 -34.5t-50.5 -16q-5 -1 -24 -5.5t-29 -7.5t-26 -8t-29 -12t-24 -15q-38 -29 -61.5 -80.5t-25.5 -109.5h2q45 76 151 76q93 0 154.5 -63t61.5 -156q0 -94 -63 -156t-164 -62q-87 0 -143 52q-70 64 -70 214q0 261 118 342q9 6 19 11.5
+t21.5 10t19.5 8t22.5 7t20 5t22 5t20.5 4.5q40 9 58 21.5t25 31.5zM262 361q-61 0 -103.5 -41.5t-42.5 -113.5t43 -113t103 -41t103 41t43 113q0 73 -42.5 114t-103.5 41z" />
+    <glyph glyph-name="afii10067" unicode="&#x432;" horiz-adv-x="430" 
+d="M68 415h157q57 0 87 -24q36 -29 36 -78q0 -54 -45 -83q31 -8 52 -28q33 -32 33 -80q0 -60 -43 -94q-37 -28 -98 -28h-179v415zM140 246h76q29 0 46 12q20 15 20 43q0 25 -20 40q-15 10 -58 10h-64v-105zM140 64h89q46 0 65 14q24 18 24 46q0 30 -21 47q-19 15 -65 15h-92
+v-122z" />
+    <glyph glyph-name="afii10068" unicode="&#x433;" horiz-adv-x="318" 
+d="M68 0v415h246v-66h-174v-349h-72z" />
+    <glyph glyph-name="afii10069" unicode="&#x434;" horiz-adv-x="460" 
+d="M98 415h292v-349h62v-176h-66v110h-316v-110h-66v176q52 1 77 54q17 35 17 110v185zM318 66v283h-148v-156q0 -83 -50 -127h198z" />
+    <glyph glyph-name="afii10070" unicode="&#x435;" horiz-adv-x="481" 
+d="M376 139l60 -33q-21 -40 -51 -67q-57 -51 -138 -51q-84 0 -144.5 54.5t-60.5 162.5q0 95 54.5 157.5t146.5 62.5q104 0 157 -76q42 -60 39 -156h-323q1 -63 39 -102t93 -39q49 0 85 30q28 24 43 57zM121 253h244q-9 49 -42.5 78.5t-79.5 29.5q-45 0 -79.5 -30.5
+t-42.5 -77.5z" />
+    <glyph glyph-name="afii10072" unicode="&#x436;" horiz-adv-x="596" 
+d="M262 415h72v-170l156 170h84l-180 -192l198 -223h-90l-154 175l-14 -14v-161h-72v161l-14 14l-154 -175h-90l198 223l-180 192h84l156 -170v170z" />
+    <glyph glyph-name="afii10073" unicode="&#x437;" horiz-adv-x="422" 
+d="M122 309l-50 41q24 35 53 52q39 23 91 23q63 0 98 -29q36 -31 36 -79q0 -58 -47 -87q30 -7 48 -27q29 -31 29 -76q0 -67 -54 -107q-44 -32 -118 -32q-66 0 -111 35q-34 25 -55 71l62 26q11 -28 37 -47q28 -21 68 -21q47 0 70 16q29 19 29 58q0 32 -25 48q-21 14 -68 14
+h-42v66h35q38 0 54 11q22 14 22 38q0 28 -18 43q-17 15 -53 15q-31 0 -57 -16q-22 -14 -34 -36z" />
+    <glyph glyph-name="afii10074" unicode="&#x438;" horiz-adv-x="492" 
+d="M68 415h72v-274l284 306v-447h-72v277l-284 -306v444z" />
+    <glyph glyph-name="afii10075" unicode="&#x439;" horiz-adv-x="492" 
+d="M68 415h72v-274l284 306v-447h-72v277l-284 -306v444zM104 594l60 23q18 -60 85 -60t85 60l60 -23q-16 -45 -54.5 -71t-90.5 -26t-90.5 26t-54.5 71z" />
+    <glyph glyph-name="afii10076" unicode="&#x43a;" horiz-adv-x="422" 
+d="M68 415h72v-170l168 170h88l-180 -185l202 -230h-94l-162 182l-22 -21v-161h-72v415z" />
+    <glyph glyph-name="afii10077" unicode="&#x43b;" horiz-adv-x="450" 
+d="M382 415v-415h-72v349h-148v-187q0 -81 -32 -120q-36 -44 -100 -44q-8 0 -22 2v66q10 -2 16 -2q35 0 52 27q14 22 14 71v253h292z" />
+    <glyph glyph-name="afii10078" unicode="&#x43c;" horiz-adv-x="580" 
+d="M20 0l100 447l172 -338l168 338l100 -447h-74l-55 257l-141 -286l-143 286l-53 -257h-74z" />
+    <glyph glyph-name="afii10079" unicode="&#x43d;" horiz-adv-x="476" 
+d="M68 0v415h72v-170h196v170h72v-415h-72v179h-196v-179h-72z" />
+    <glyph glyph-name="afii10080" unicode="&#x43e;" horiz-adv-x="524" 
+d="M262 -12q-95 0 -157.5 62t-62.5 156q0 95 62.5 157t157.5 62t157.5 -62t62.5 -157q0 -94 -62.5 -156t-157.5 -62zM262 361q-61 0 -103.5 -41.5t-42.5 -113.5t43 -113t103 -41t103 41t43 113q0 73 -42.5 114t-103.5 41z" />
+    <glyph glyph-name="afii10081" unicode="&#x43f;" horiz-adv-x="470" 
+d="M68 415h334v-415h-72v349h-190v-349h-72v415z" />
+    <glyph glyph-name="afii10082" unicode="&#x440;" horiz-adv-x="526" 
+d="M140 58v-292h-72v649h72v-58q55 68 142 68q93 0 147.5 -65.5t54.5 -154.5q0 -90 -54 -153.5t-145 -63.5t-145 70zM275 52q61 0 98 44.5t37 108.5q0 72 -41 114t-95 42t-96 -42t-42 -113q0 -65 38.5 -109.5t100.5 -44.5z" />
+    <glyph glyph-name="afii10083" unicode="&#x441;" horiz-adv-x="438" 
+d="M393 385v-86q-49 62 -130 62q-61 0 -104 -41.5t-43 -113.5t43 -113t103 -41q80 0 134 65v-86q-59 -43 -132 -43q-97 0 -159.5 61.5t-62.5 155.5q0 95 63 157.5t159 62.5q72 0 129 -40z" />
+    <glyph glyph-name="afii10084" unicode="&#x442;" horiz-adv-x="348" 
+d="M340 349h-130v-349h-72v349h-130v66h332v-66z" />
+    <glyph glyph-name="afii10085" unicode="&#x443;" horiz-adv-x="460" 
+d="M65 -234l134 273l-191 376h82l147 -297l135 297h80l-307 -649h-80z" />
+    <glyph glyph-name="afii10086" unicode="&#x444;" horiz-adv-x="650" 
+d="M289 -234v227q-109 0 -175 54q-72 58 -72 161q0 102 71 160q64 54 176 54v245h72v-245q112 0 176 -54q71 -58 71 -160q0 -103 -72 -161q-66 -54 -175 -54v-227h-72zM289 57v301q-65 3 -111 -26q-62 -39 -62 -124q0 -80 58 -121q47 -33 115 -30zM361 358v-301
+q68 -3 115 30q58 41 58 121q0 85 -62 124q-46 29 -111 26z" />
+    <glyph glyph-name="afii10087" unicode="&#x445;" horiz-adv-x="454" 
+d="M8 0l177 222l-151 193h88l107 -141l109 141h88l-152 -194l172 -221h-88l-131 169l-131 -169h-88z" />
+    <glyph glyph-name="afii10088" unicode="&#x446;" horiz-adv-x="474" 
+d="M68 0v415h72v-349h186v349h72v-349h68v-176h-66v110h-332z" />
+    <glyph glyph-name="afii10089" unicode="&#x447;" horiz-adv-x="430" 
+d="M290 0v163q-59 -9 -98 -9q-68 0 -101 30q-35 31 -35 102v129h72v-119q0 -34 9 -49q14 -27 63 -27q44 0 90 9v186h72v-415h-72z" />
+    <glyph glyph-name="afii10090" unicode="&#x448;" horiz-adv-x="672" 
+d="M68 0v415h72v-349h160v349h72v-349h160v349h72v-415h-536z" />
+    <glyph glyph-name="afii10091" unicode="&#x449;" horiz-adv-x="680" 
+d="M68 0v415h72v-349h160v349h72v-349h160v349h72v-349h68v-176h-66v110h-538z" />
+    <glyph glyph-name="afii10092" unicode="&#x44a;" horiz-adv-x="430" 
+d="M98 0v349h-90v66h162v-159h74q86 0 126 -41q32 -32 32 -87q0 -58 -37 -93t-122 -35h-145zM170 64h67q45 0 64 11q27 18 27 53q0 36 -26 52q-18 12 -64 12h-68v-128z" />
+    <glyph glyph-name="afii10093" unicode="&#x44b;" horiz-adv-x="562" 
+d="M68 0v415h72v-159h74q86 0 126 -41q32 -32 32 -87q0 -58 -37 -93t-122 -35h-145zM140 64h67q45 0 64 11q27 18 27 53q0 36 -26 52q-18 12 -64 12h-68v-128zM422 0v415h72v-415h-72z" />
+    <glyph glyph-name="afii10094" unicode="&#x44c;" horiz-adv-x="400" 
+d="M68 0v415h72v-159h74q86 0 126 -41q32 -32 32 -87q0 -58 -37 -93t-122 -35h-145zM140 64h67q45 0 64 11q27 18 27 53q0 36 -26 52q-18 12 -64 12h-68v-128z" />
+    <glyph glyph-name="afii10095" unicode="&#x44d;" horiz-adv-x="423" 
+d="M42 334v70q52 21 113 21q105 0 165.5 -61t60.5 -159q0 -95 -61.5 -156t-159.5 -61q-62 0 -118 23v70q55 -29 117 -29q58 0 100 35.5t48 92.5h-219v64h219q-12 55 -53 86t-96 31q-58 0 -116 -27z" />
+    <glyph glyph-name="afii10096" unicode="&#x44e;" horiz-adv-x="690" 
+d="M140 245h75q10 70 56 118q59 62 157 62q96 0 158 -62t62 -157q0 -94 -62.5 -156t-157.5 -62q-109 0 -171 78q-40 50 -46 113h-71v-179h-72v415h72v-170zM428 361q-60 0 -101.5 -41.5t-41.5 -113.5q0 -73 42 -113.5t101 -40.5q60 0 103 41t43 113t-42.5 113.5t-103.5 41.5
+z" />
+    <glyph glyph-name="afii10097" unicode="&#x44f;" horiz-adv-x="408" 
+d="M20 0l129 171q-6 0 -16.5 2t-30.5 10t-33 21q-33 31 -33 83q0 59 37 93q37 35 122 35h145v-415h-72v167h-44l-118 -167h-86zM268 227v124h-67q-47 0 -65 -11q-26 -17 -26 -51t26 -50q19 -12 64 -12h68z" />
+    <glyph glyph-name="afii10071" unicode="&#x451;" horiz-adv-x="482" 
+d="M377 139l60 -33q-21 -40 -51 -67q-56 -51 -138 -51q-84 0 -145 55t-61 162q0 95 55 157.5t147 62.5q104 0 157 -76q42 -60 39 -156h-324q1 -63 39 -102t94 -39q51 0 85 30q28 24 43 57zM122 253h244q-9 49 -42.5 78.5t-79.5 29.5q-45 0 -79.5 -30.5t-42.5 -77.5zM110 550
+q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35zM284 550q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35z" />
+    <glyph glyph-name="afii10099" unicode="&#x452;" horiz-adv-x="470" 
+d="M68 0v554h-68v60h68v115h72v-115h200v-60h-200v-187q44 58 119 58q80 0 119 -57q24 -35 24 -111v-347q0 -83 -40 -117q-32 -27 -83 -27q-27 0 -51 9v70q22 -9 47 -9q33 0 46 22q9 15 9 60v337q0 51 -19 76q-23 30 -68 30q-46 0 -73 -32q-30 -35 -30 -112v-217h-72z" />
+    <glyph glyph-name="afii10100" unicode="&#x453;" horiz-adv-x="318" 
+d="M68 0v415h246v-66h-174v-349h-72zM252 666l74 -34l-134 -135l-45 24z" />
+    <glyph glyph-name="afii10101" unicode="&#x454;" horiz-adv-x="438" 
+d="M393 385v-86q-52 62 -130 62q-51 0 -92.5 -31.5t-51.5 -85.5h217v-64h-218q7 -57 48 -92.5t96 -35.5q80 0 134 65v-86q-59 -43 -132 -43q-97 0 -159.5 61.5t-62.5 155.5q0 95 63 157.5t159 62.5q72 0 129 -40z" />
+    <glyph glyph-name="afii10102" unicode="&#x455;" horiz-adv-x="352" 
+d="M298 350l-59 -31q-17 42 -58 42q-21 0 -35.5 -12t-14.5 -35q0 -21 13.5 -31.5t53.5 -27.5q79 -33 104 -64q22 -27 22 -67q0 -63 -40.5 -99.5t-102.5 -36.5q-12 0 -26 2.5t-38.5 11t-46.5 32.5t-34 60l62 26q25 -68 84 -68q33 0 50.5 20t17.5 46q0 14 -4.5 24.5t-15 18.5
+t-20 13t-27.5 13q-80 35 -101 61q-19 24 -19 61q0 54 33.5 85t87.5 31q79 0 114 -75z" />
+    <glyph glyph-name="afii10103" unicode="&#x456;" horiz-adv-x="208" 
+d="M68 415h72v-415h-72v415zM53 572q0 21 15 36t36 15t36 -15t15 -36t-15 -36t-36 -15t-36 15t-15 36z" />
+    <glyph glyph-name="afii10104" unicode="&#x457;" horiz-adv-x="208" 
+d="M68 415h72v-415h-72v415zM-12 550q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35zM120 550q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35z" />
+    <glyph glyph-name="afii10105" unicode="&#x458;" horiz-adv-x="208" 
+d="M68 415h72v-649h-72v649zM53 572q0 21 15 36t36 15t36 -15t15 -36t-15 -36t-36 -15t-36 15t-15 36z" />
+    <glyph glyph-name="afii10106" unicode="&#x459;" horiz-adv-x="642" 
+d="M382 415v-159h74q86 0 126 -41q32 -32 32 -87q0 -58 -37 -93t-122 -35h-145v349h-148v-187q0 -81 -32 -120q-36 -44 -100 -44q-8 0 -22 2v66q10 -2 16 -2q35 0 52 27q14 22 14 71v253h292zM382 64h67q45 0 64 11q27 18 27 53q0 36 -26 52q-18 12 -64 12h-68v-128z" />
+    <glyph glyph-name="afii10107" unicode="&#x45a;" horiz-adv-x="668" 
+d="M68 0v415h72v-157h196v157h72v-159h74q86 0 126 -41q32 -32 32 -87q0 -58 -37 -93t-122 -35h-145v192h-196v-192h-72zM408 64h67q45 0 64 11q27 18 27 53q0 36 -26 52q-18 12 -64 12h-68v-128z" />
+    <glyph glyph-name="afii10108" unicode="&#x45b;" horiz-adv-x="470" 
+d="M68 0v554h-68v60h68v115h72v-115h200v-60h-200v-187q44 58 119 58q80 0 119 -57q24 -35 24 -111v-257h-72v255q0 51 -19 76q-23 30 -68 30q-46 0 -73 -32q-30 -35 -30 -112v-217h-72z" />
+    <glyph glyph-name="afii10109" unicode="&#x45c;" horiz-adv-x="422" 
+d="M68 415h72v-170l168 170h88l-180 -185l202 -230h-94l-162 182l-22 -21v-161h-72v415zM273 666l74 -34l-134 -135l-45 24z" />
+    <glyph glyph-name="afii10110" unicode="&#x45e;" horiz-adv-x="460" 
+d="M65 -234l134 273l-191 376h82l147 -297l135 297h80l-307 -649h-80zM95 594l60 23q18 -60 85 -60t85 60l60 -23q-16 -45 -54.5 -71t-90.5 -26t-90.5 26t-54.5 71z" />
+    <glyph glyph-name="afii10193" unicode="&#x45f;" horiz-adv-x="466" 
+d="M68 0v415h72v-349h186v349h72v-415h-132v-140h-66v140h-132z" />
+    <glyph glyph-name="afii10050" unicode="&#x490;" horiz-adv-x="404" 
+d="M86 0v667h234v140h70v-210h-230v-597h-74z" />
+    <glyph glyph-name="afii10098" unicode="&#x491;" horiz-adv-x="328" 
+d="M68 0v415h180v110h66v-176h-174v-349h-72z" />
+    <glyph glyph-name="endash" unicode="&#x2013;" horiz-adv-x="540" 
+d="M50 181v64h440v-64h-440z" />
+    <glyph glyph-name="emdash" unicode="&#x2014;" horiz-adv-x="760" 
+d="M50 181v64h660v-64h-660z" />
+    <glyph glyph-name="quoteleft" unicode="&#x2018;" horiz-adv-x="264" 
+d="M206 681l-83 -231l-65 24l101 226z" />
+    <glyph glyph-name="quoteright" unicode="&#x2019;" horiz-adv-x="308" 
+d="M58 469l81 231l67 -24l-101 -226z" />
+    <glyph glyph-name="quotesinglbase" unicode="&#x201a;" horiz-adv-x="308" 
+d="M58 -133l81 231l67 -24l-101 -226z" />
+    <glyph glyph-name="quotedblleft" unicode="&#x201c;" horiz-adv-x="410" 
+d="M206 681l-83 -231l-65 24l101 226zM352 681l-81 -231l-67 24l101 226z" />
+    <glyph glyph-name="quotedblright" unicode="&#x201d;" horiz-adv-x="454" 
+d="M58 469l81 231l67 -24l-101 -226zM204 469l83 231l65 -24l-101 -226z" />
+    <glyph glyph-name="quotedblbase" unicode="&#x201e;" horiz-adv-x="454" 
+d="M58 -133l81 231l67 -24l-101 -226zM204 -133l83 231l65 -24l-101 -226z" />
+    <glyph glyph-name="dagger" unicode="&#x2020;" 
+d="M248 565v164h72v-164h148v-68h-148v-673h-72v673h-148v68h148z" />
+    <glyph glyph-name="daggerdbl" unicode="&#x2021;" 
+d="M248 565v164h72v-164h148v-68h-148v-303h148v-68h-148v-302h-72v302h-148v68h148v303h-148v68h148z" />
+    <glyph glyph-name="bullet" unicode="&#x2022;" horiz-adv-x="668" 
+d="M194 340q0 58 41 99t99 41t99 -41t41 -99t-41 -99t-99 -41t-99 41t-41 99z" />
+    <glyph glyph-name="ellipsis" unicode="&#x2026;" horiz-adv-x="746" 
+d="M75 40q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35zM323 40q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35zM571 40q0 20 15 35t35 15t35 -15t15 -35t-15 -35t-35 -15t-35 15t-15 35z" />
+    <glyph glyph-name="perthousand" unicode="&#x2030;" horiz-adv-x="1060" 
+d="M178 673q61 0 103 -42t42 -103t-42 -103t-103 -42t-103 42t-42 103t42 103t103 42zM178 438q36 0 60.5 26.5t24.5 63.5t-24.5 63.5t-60.5 26.5q-35 0 -60 -26.5t-25 -63.5t25 -63.5t60 -26.5zM141 10l389 670l39 -22l-389 -671zM532 283q61 0 103 -42t42 -103t-42 -103
+t-103 -42t-103 42t-42 103t42 103t103 42zM532 48q36 0 60.5 26.5t24.5 63.5t-24.5 63.5t-60.5 26.5q-35 0 -60 -26.5t-25 -63.5t25 -63.5t60 -26.5zM882 283q61 0 103 -42t42 -103t-42 -103t-103 -42t-103 42t-42 103t42 103t103 42zM882 48q36 0 60.5 26.5t24.5 63.5
+t-24.5 63.5t-60.5 26.5q-35 0 -60 -26.5t-25 -63.5t25 -63.5t60 -26.5z" />
+    <glyph glyph-name="guilsinglleft" unicode="&#x2039;" horiz-adv-x="282" 
+d="M112 212l120 -146l-46 -36l-150 182l150 188l46 -37z" />
+    <glyph glyph-name="guilsinglright" unicode="&#x203a;" horiz-adv-x="282" 
+d="M170 218l-120 146l46 36l150 -182l-150 -188l-46 37z" />
+    <glyph glyph-name="Euro" unicode="&#x20ac;" 
+d="M523 631v-73q-57 55 -157 55q-85 0 -133.5 -51.5t-59.5 -134.5h306l-21 -56h-293q0 -4 -0.5 -16.5t-0.5 -18.5q0 -8 0.5 -21t0.5 -17h265l-20 -56h-236q12 -90 61.5 -139.5t130.5 -49.5q95 0 157 55v-73q-21 -19 -65 -32.5t-100 -13.5q-221 0 -257 253h-74l21 56h47
+q-2 18 -2 38q0 6 0.5 18.5t0.5 16.5h-67l21 56h52q17 115 80 183t178 68q108 0 165 -47z" />
+    <glyph glyph-name="afii61352" unicode="&#x2116;" horiz-adv-x="1020" 
+d="M160 0h-74v695l466 -529v501h74v-697l-466 529v-499zM845 667q61 0 103 -42t42 -103t-42 -103t-103 -42t-103 42t-42 103t42 103t103 42zM845 432q36 0 60.5 26.5t24.5 63.5t-24.5 63.5t-60.5 26.5q-35 0 -60 -26.5t-25 -63.5t25 -63.5t60 -26.5zM720 227v70h250v-70
+h-250z" />
+    <glyph glyph-name="trademark" unicode="&#x2122;" horiz-adv-x="722" 
+d="M234 621h-90v-334h-50v334h-90v46h230v-46zM316 287h-46l65 404l143 -315l147 315l61 -404h-46l-35 257l-127 -276l-125 276z" />
+    <glyph glyph-name="hyphenminus" horiz-adv-x="300" 
+d="M40 176v74h220v-74h-220z" />
+    <hkern u1="A" u2="&#x2122;" k="-56" />
+    <hkern u1="A" u2="&#x201d;" k="38" />
+    <hkern u1="A" u2="&#x201c;" k="97" />
+    <hkern u1="A" u2="&#x2019;" k="14" />
+    <hkern u1="A" u2="&#x2018;" k="97" />
+    <hkern u1="A" u2="y" k="16" />
+    <hkern u1="A" u2="w" k="22" />
+    <hkern u1="A" u2="v" k="22" />
+    <hkern u1="A" u2="Y" k="63" />
+    <hkern u1="A" u2="W" k="87" />
+    <hkern u1="A" u2="V" k="99" />
+    <hkern u1="A" u2="T" k="12" />
+    <hkern u1="A" u2="&#x2a;" k="116" />
+    <hkern u1="B" u2="Y" k="20" />
+    <hkern u1="B" u2="V" k="1" />
+    <hkern u1="D" u2="Y" k="23" />
+    <hkern u1="F" u2="&#x2026;" k="177" />
+    <hkern u1="F" u2="q" k="42" />
+    <hkern u1="F" u2="o" k="42" />
+    <hkern u1="F" u2="g" k="42" />
+    <hkern u1="F" u2="e" k="42" />
+    <hkern u1="F" u2="d" k="42" />
+    <hkern u1="F" u2="c" k="42" />
+    <hkern u1="F" u2="a" k="42" />
+    <hkern u1="F" u2="J" k="18" />
+    <hkern u1="F" u2="A" k="33" />
+    <hkern u1="F" u2="&#x2e;" k="158" />
+    <hkern u1="F" u2="&#x2c;" k="53" />
+    <hkern u1="K" u2="y" k="22" />
+    <hkern u1="K" u2="w" k="41" />
+    <hkern u1="K" u2="v" k="42" />
+    <hkern u1="K" u2="Q" k="86" />
+    <hkern u1="K" u2="O" k="86" />
+    <hkern u1="K" u2="G" k="84" />
+    <hkern u1="K" u2="C" k="84" />
+    <hkern u1="L" u2="&#x2122;" k="-33" />
+    <hkern u1="L" u2="&#x201d;" k="84" />
+    <hkern u1="L" u2="&#x2019;" k="84" />
+    <hkern u1="L" u2="y" k="22" />
+    <hkern u1="L" u2="w" k="9" />
+    <hkern u1="L" u2="v" k="9" />
+    <hkern u1="L" u2="Y" k="34" />
+    <hkern u1="L" u2="W" k="34" />
+    <hkern u1="L" u2="V" k="34" />
+    <hkern u1="L" u2="T" k="18" />
+    <hkern u1="L" u2="Q" k="34" />
+    <hkern u1="L" u2="O" k="34" />
+    <hkern u1="L" u2="G" k="31" />
+    <hkern u1="L" u2="C" k="32" />
+    <hkern u1="L" u2="&#x2a;" k="144" />
+    <hkern u1="O" u2="Y" k="40" />
+    <hkern u1="O" u2="X" k="28" />
+    <hkern u1="O" u2="W" k="40" />
+    <hkern u1="O" u2="V" k="40" />
+    <hkern u1="O" u2="A" k="40" />
+    <hkern u1="P" u2="&#x2026;" k="199" />
+    <hkern u1="P" u2="o" k="7" />
+    <hkern u1="P" u2="c" k="7" />
+    <hkern u1="P" u2="J" k="49" />
+    <hkern u1="P" u2="A" k="84" />
+    <hkern u1="P" u2="&#x2e;" k="134" />
+    <hkern u1="P" u2="&#x2c;" k="118" />
+    <hkern u1="Q" u2="Y" k="23" />
+    <hkern u1="S" u2="T" k="42" />
+    <hkern u1="T" u2="&#x2026;" k="79" />
+    <hkern u1="T" u2="z" k="57" />
+    <hkern u1="T" u2="y" k="56" />
+    <hkern u1="T" u2="x" k="56" />
+    <hkern u1="T" u2="w" k="56" />
+    <hkern u1="T" u2="v" k="56" />
+    <hkern u1="T" u2="u" k="72" />
+    <hkern u1="T" u2="t" k="30" />
+    <hkern u1="T" u2="s" k="93" />
+    <hkern u1="T" u2="r" k="72" />
+    <hkern u1="T" u2="q" k="84" />
+    <hkern u1="T" u2="p" k="72" />
+    <hkern u1="T" u2="o" k="84" />
+    <hkern u1="T" u2="n" k="72" />
+    <hkern u1="T" u2="m" k="72" />
+    <hkern u1="T" u2="g" k="84" />
+    <hkern u1="T" u2="e" k="84" />
+    <hkern u1="T" u2="d" k="84" />
+    <hkern u1="T" u2="c" k="84" />
+    <hkern u1="T" u2="a" k="84" />
+    <hkern u1="T" u2="S" k="27" />
+    <hkern u1="T" u2="Q" k="35" />
+    <hkern u1="T" u2="O" k="35" />
+    <hkern u1="T" u2="G" k="33" />
+    <hkern u1="T" u2="C" k="35" />
+    <hkern u1="T" u2="A" k="52" />
+    <hkern u1="T" u2="&#x3b;" k="8" />
+    <hkern u1="T" u2="&#x3a;" k="68" />
+    <hkern u1="T" u2="&#x2e;" k="68" />
+    <hkern u1="T" u2="&#x2c;" k="22" />
+    <hkern u1="V" u2="&#x2026;" k="148" />
+    <hkern u1="V" u2="x" k="33" />
+    <hkern u1="V" u2="u" k="47" />
+    <hkern u1="V" u2="s" k="46" />
+    <hkern u1="V" u2="r" k="47" />
+    <hkern u1="V" u2="q" k="68" />
+    <hkern u1="V" u2="p" k="47" />
+    <hkern u1="V" u2="o" k="68" />
+    <hkern u1="V" u2="n" k="47" />
+    <hkern u1="V" u2="m" k="47" />
+    <hkern u1="V" u2="g" k="68" />
+    <hkern u1="V" u2="e" k="68" />
+    <hkern u1="V" u2="d" k="68" />
+    <hkern u1="V" u2="c" k="97" />
+    <hkern u1="V" u2="a" k="97" />
+    <hkern u1="V" u2="A" k="99" />
+    <hkern u1="V" u2="&#x3b;" k="23" />
+    <hkern u1="V" u2="&#x3a;" k="9" />
+    <hkern u1="V" u2="&#x2e;" k="122" />
+    <hkern u1="V" u2="&#x2c;" k="33" />
+    <hkern u1="W" u2="&#x2026;" k="158" />
+    <hkern u1="W" u2="z" k="57" />
+    <hkern u1="W" u2="y" k="46" />
+    <hkern u1="W" u2="x" k="48" />
+    <hkern u1="W" u2="w" k="42" />
+    <hkern u1="W" u2="v" k="42" />
+    <hkern u1="W" u2="u" k="59" />
+    <hkern u1="W" u2="s" k="31" />
+    <hkern u1="W" u2="r" k="59" />
+    <hkern u1="W" u2="q" k="41" />
+    <hkern u1="W" u2="p" k="59" />
+    <hkern u1="W" u2="o" k="70" />
+    <hkern u1="W" u2="n" k="59" />
+    <hkern u1="W" u2="m" k="59" />
+    <hkern u1="W" u2="g" k="73" />
+    <hkern u1="W" u2="e" k="73" />
+    <hkern u1="W" u2="d" k="73" />
+    <hkern u1="W" u2="c" k="73" />
+    <hkern u1="W" u2="a" k="73" />
+    <hkern u1="W" u2="A" k="110" />
+    <hkern u1="W" u2="&#x3b;" k="27" />
+    <hkern u1="W" u2="&#x3a;" k="13" />
+    <hkern u1="W" u2="&#x2e;" k="122" />
+    <hkern u1="W" u2="&#x2c;" k="106" />
+    <hkern u1="X" u2="y" k="3" />
+    <hkern u1="X" u2="w" k="3" />
+    <hkern u1="X" u2="v" k="3" />
+    <hkern u1="X" u2="Q" k="26" />
+    <hkern u1="X" u2="O" k="26" />
+    <hkern u1="X" u2="G" k="25" />
+    <hkern u1="X" u2="C" k="24" />
+    <hkern u1="Y" u2="&#x2026;" k="132" />
+    <hkern u1="Y" u2="z" k="49" />
+    <hkern u1="Y" u2="y" k="54" />
+    <hkern u1="Y" u2="x" k="56" />
+    <hkern u1="Y" u2="w" k="43" />
+    <hkern u1="Y" u2="v" k="43" />
+    <hkern u1="Y" u2="u" k="93" />
+    <hkern u1="Y" u2="t" k="42" />
+    <hkern u1="Y" u2="s" k="87" />
+    <hkern u1="Y" u2="r" k="82" />
+    <hkern u1="Y" u2="q" k="99" />
+    <hkern u1="Y" u2="p" k="82" />
+    <hkern u1="Y" u2="o" k="99" />
+    <hkern u1="Y" u2="n" k="82" />
+    <hkern u1="Y" u2="m" k="82" />
+    <hkern u1="Y" u2="g" k="99" />
+    <hkern u1="Y" u2="e" k="99" />
+    <hkern u1="Y" u2="d" k="99" />
+    <hkern u1="Y" u2="c" k="99" />
+    <hkern u1="Y" u2="a" k="99" />
+    <hkern u1="Y" u2="Q" k="24" />
+    <hkern u1="Y" u2="O" k="24" />
+    <hkern u1="Y" u2="G" k="25" />
+    <hkern u1="Y" u2="C" k="24" />
+    <hkern u1="Y" u2="A" k="65" />
+    <hkern u1="Y" u2="&#x3b;" k="76" />
+    <hkern u1="Y" u2="&#x3a;" k="62" />
+    <hkern u1="Y" u2="&#x2e;" k="121" />
+    <hkern u1="Y" u2="&#x2c;" k="106" />
+    <hkern u1="a" u2="T" k="72" />
+    <hkern u1="r" u2="&#x2026;" k="59" />
+    <hkern u1="r" u2="q" k="24" />
+    <hkern u1="r" u2="o" k="24" />
+    <hkern u1="r" u2="g" k="24" />
+    <hkern u1="r" u2="e" k="24" />
+    <hkern u1="r" u2="d" k="24" />
+    <hkern u1="r" u2="c" k="24" />
+    <hkern u1="r" u2="&#x2e;" k="48" />
+    <hkern u1="r" u2="&#x2c;" k="1" />
+    <hkern u1="v" u2="&#x2026;" k="80" />
+    <hkern u1="v" u2="&#x2e;" k="69" />
+    <hkern u1="v" u2="&#x2c;" k="13" />
+    <hkern u1="w" u2="&#x2026;" k="80" />
+    <hkern u1="w" u2="&#x2e;" k="69" />
+    <hkern u1="w" u2="&#x2c;" k="17" />
+    <hkern u1="y" u2="&#x2026;" k="84" />
+    <hkern u1="y" u2="&#x2e;" k="73" />
+    <hkern u1="y" u2="&#x2c;" k="2" />
+    <hkern u1="&#x403;" u2="&#x2026;" k="147" />
+    <hkern u1="&#x403;" u2="&#x451;" k="48" />
+    <hkern u1="&#x403;" u2="&#x44f;" k="92" />
+    <hkern u1="&#x403;" u2="&#x44e;" k="140" />
+    <hkern u1="&#x403;" u2="&#x44d;" k="114" />
+    <hkern u1="&#x403;" u2="&#x44c;" k="140" />
+    <hkern u1="&#x403;" u2="&#x44b;" k="140" />
+    <hkern u1="&#x403;" u2="&#x449;" k="140" />
+    <hkern u1="&#x403;" u2="&#x448;" k="140" />
+    <hkern u1="&#x403;" u2="&#x447;" k="128" />
+    <hkern u1="&#x403;" u2="&#x446;" k="140" />
+    <hkern u1="&#x403;" u2="&#x445;" k="80" />
+    <hkern u1="&#x403;" u2="&#x444;" k="114" />
+    <hkern u1="&#x403;" u2="&#x443;" k="80" />
+    <hkern u1="&#x403;" u2="&#x442;" k="80" />
+    <hkern u1="&#x403;" u2="&#x441;" k="114" />
+    <hkern u1="&#x403;" u2="&#x440;" k="140" />
+    <hkern u1="&#x403;" u2="&#x43f;" k="140" />
+    <hkern u1="&#x403;" u2="&#x43e;" k="114" />
+    <hkern u1="&#x403;" u2="&#x43d;" k="140" />
+    <hkern u1="&#x403;" u2="&#x43c;" k="92" />
+    <hkern u1="&#x403;" u2="&#x43b;" k="80" />
+    <hkern u1="&#x403;" u2="&#x43a;" k="140" />
+    <hkern u1="&#x403;" u2="&#x439;" k="59" />
+    <hkern u1="&#x403;" u2="&#x438;" k="140" />
+    <hkern u1="&#x403;" u2="&#x437;" k="114" />
+    <hkern u1="&#x403;" u2="&#x436;" k="76" />
+    <hkern u1="&#x403;" u2="&#x435;" k="114" />
+    <hkern u1="&#x403;" u2="&#x434;" k="76" />
+    <hkern u1="&#x403;" u2="&#x433;" k="140" />
+    <hkern u1="&#x403;" u2="&#x432;" k="140" />
+    <hkern u1="&#x403;" u2="&#x431;" k="50" />
+    <hkern u1="&#x403;" u2="&#x430;" k="114" />
+    <hkern u1="&#x403;" u2="&#x424;" k="84" />
+    <hkern u1="&#x403;" u2="&#x421;" k="35" />
+    <hkern u1="&#x403;" u2="&#x41e;" k="35" />
+    <hkern u1="&#x403;" u2="&#x41b;" k="42" />
+    <hkern u1="&#x403;" u2="&#x417;" k="37" />
+    <hkern u1="&#x403;" u2="&#x414;" k="37" />
+    <hkern u1="&#x403;" u2="&#x410;" k="80" />
+    <hkern u1="&#x403;" u2="&#x3b;" k="115" />
+    <hkern u1="&#x403;" u2="&#x3a;" k="128" />
+    <hkern u1="&#x403;" u2="&#x2e;" k="128" />
+    <hkern u1="&#x403;" u2="&#x2c;" k="112" />
+    <hkern u1="&#x40c;" u2="&#x447;" k="62" />
+    <hkern u1="&#x40c;" u2="&#x443;" k="22" />
+    <hkern u1="&#x40e;" u2="&#x2026;" k="190" />
+    <hkern u1="&#x40e;" u2="&#x451;" k="54" />
+    <hkern u1="&#x40e;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x40e;" u2="&#x44e;" k="54" />
+    <hkern u1="&#x40e;" u2="&#x44d;" k="46" />
+    <hkern u1="&#x40e;" u2="&#x44c;" k="18" />
+    <hkern u1="&#x40e;" u2="&#x44b;" k="54" />
+    <hkern u1="&#x40e;" u2="&#x449;" k="54" />
+    <hkern u1="&#x40e;" u2="&#x448;" k="54" />
+    <hkern u1="&#x40e;" u2="&#x447;" k="6" />
+    <hkern u1="&#x40e;" u2="&#x446;" k="54" />
+    <hkern u1="&#x40e;" u2="&#x445;" k="43" />
+    <hkern u1="&#x40e;" u2="&#x444;" k="63" />
+    <hkern u1="&#x40e;" u2="&#x443;" k="39" />
+    <hkern u1="&#x40e;" u2="&#x442;" k="28" />
+    <hkern u1="&#x40e;" u2="&#x441;" k="84" />
+    <hkern u1="&#x40e;" u2="&#x440;" k="54" />
+    <hkern u1="&#x40e;" u2="&#x43f;" k="47" />
+    <hkern u1="&#x40e;" u2="&#x43e;" k="89" />
+    <hkern u1="&#x40e;" u2="&#x43d;" k="57" />
+    <hkern u1="&#x40e;" u2="&#x43c;" k="83" />
+    <hkern u1="&#x40e;" u2="&#x43b;" k="68" />
+    <hkern u1="&#x40e;" u2="&#x43a;" k="56" />
+    <hkern u1="&#x40e;" u2="&#x439;" k="36" />
+    <hkern u1="&#x40e;" u2="&#x438;" k="55" />
+    <hkern u1="&#x40e;" u2="&#x437;" k="97" />
+    <hkern u1="&#x40e;" u2="&#x436;" k="46" />
+    <hkern u1="&#x40e;" u2="&#x435;" k="100" />
+    <hkern u1="&#x40e;" u2="&#x434;" k="48" />
+    <hkern u1="&#x40e;" u2="&#x433;" k="54" />
+    <hkern u1="&#x40e;" u2="&#x432;" k="54" />
+    <hkern u1="&#x40e;" u2="&#x431;" k="32" />
+    <hkern u1="&#x40e;" u2="&#x430;" k="89" />
+    <hkern u1="&#x40e;" u2="&#x421;" k="1" />
+    <hkern u1="&#x40e;" u2="&#x41e;" k="2" />
+    <hkern u1="&#x40e;" u2="&#x41b;" k="61" />
+    <hkern u1="&#x40e;" u2="&#x417;" k="49" />
+    <hkern u1="&#x40e;" u2="&#x414;" k="26" />
+    <hkern u1="&#x40e;" u2="&#x410;" k="137" />
+    <hkern u1="&#x40e;" u2="&#x3b;" k="41" />
+    <hkern u1="&#x40e;" u2="&#x3a;" k="27" />
+    <hkern u1="&#x40e;" u2="&#x2e;" k="122" />
+    <hkern u1="&#x40e;" u2="&#x2c;" k="106" />
+    <hkern u1="&#x410;" u2="&#x2122;" k="-30" />
+    <hkern u1="&#x410;" u2="&#x201d;" k="95" />
+    <hkern u1="&#x410;" u2="&#x2019;" k="95" />
+    <hkern u1="&#x410;" u2="&#x443;" k="16" />
+    <hkern u1="&#x410;" u2="&#x427;" k="78" />
+    <hkern u1="&#x410;" u2="&#x424;" k="12" />
+    <hkern u1="&#x410;" u2="&#x423;" k="15" />
+    <hkern u1="&#x410;" u2="&#x422;" k="12" />
+    <hkern u1="&#x410;" u2="&#x2a;" k="5" />
+    <hkern u1="&#x411;" u2="&#x423;" k="38" />
+    <hkern u1="&#x413;" u2="&#x2026;" k="147" />
+    <hkern u1="&#x413;" u2="&#x451;" k="48" />
+    <hkern u1="&#x413;" u2="&#x44f;" k="92" />
+    <hkern u1="&#x413;" u2="&#x44e;" k="140" />
+    <hkern u1="&#x413;" u2="&#x44d;" k="114" />
+    <hkern u1="&#x413;" u2="&#x44c;" k="140" />
+    <hkern u1="&#x413;" u2="&#x44b;" k="140" />
+    <hkern u1="&#x413;" u2="&#x449;" k="140" />
+    <hkern u1="&#x413;" u2="&#x448;" k="140" />
+    <hkern u1="&#x413;" u2="&#x447;" k="128" />
+    <hkern u1="&#x413;" u2="&#x446;" k="140" />
+    <hkern u1="&#x413;" u2="&#x445;" k="80" />
+    <hkern u1="&#x413;" u2="&#x444;" k="114" />
+    <hkern u1="&#x413;" u2="&#x443;" k="80" />
+    <hkern u1="&#x413;" u2="&#x442;" k="80" />
+    <hkern u1="&#x413;" u2="&#x441;" k="114" />
+    <hkern u1="&#x413;" u2="&#x440;" k="140" />
+    <hkern u1="&#x413;" u2="&#x43f;" k="140" />
+    <hkern u1="&#x413;" u2="&#x43e;" k="114" />
+    <hkern u1="&#x413;" u2="&#x43d;" k="140" />
+    <hkern u1="&#x413;" u2="&#x43c;" k="92" />
+    <hkern u1="&#x413;" u2="&#x43b;" k="80" />
+    <hkern u1="&#x413;" u2="&#x43a;" k="140" />
+    <hkern u1="&#x413;" u2="&#x439;" k="59" />
+    <hkern u1="&#x413;" u2="&#x438;" k="140" />
+    <hkern u1="&#x413;" u2="&#x437;" k="114" />
+    <hkern u1="&#x413;" u2="&#x436;" k="76" />
+    <hkern u1="&#x413;" u2="&#x435;" k="114" />
+    <hkern u1="&#x413;" u2="&#x434;" k="76" />
+    <hkern u1="&#x413;" u2="&#x433;" k="140" />
+    <hkern u1="&#x413;" u2="&#x432;" k="140" />
+    <hkern u1="&#x413;" u2="&#x431;" k="50" />
+    <hkern u1="&#x413;" u2="&#x430;" k="114" />
+    <hkern u1="&#x413;" u2="&#x424;" k="84" />
+    <hkern u1="&#x413;" u2="&#x421;" k="35" />
+    <hkern u1="&#x413;" u2="&#x41e;" k="35" />
+    <hkern u1="&#x413;" u2="&#x41b;" k="42" />
+    <hkern u1="&#x413;" u2="&#x417;" k="37" />
+    <hkern u1="&#x413;" u2="&#x414;" k="37" />
+    <hkern u1="&#x413;" u2="&#x410;" k="80" />
+    <hkern u1="&#x413;" u2="&#x3b;" k="115" />
+    <hkern u1="&#x413;" u2="&#x3a;" k="128" />
+    <hkern u1="&#x413;" u2="&#x2e;" k="128" />
+    <hkern u1="&#x413;" u2="&#x2c;" k="112" />
+    <hkern u1="&#x416;" u2="&#x447;" k="40" />
+    <hkern u1="&#x416;" u2="&#x443;" k="22" />
+    <hkern u1="&#x41a;" u2="&#x447;" k="62" />
+    <hkern u1="&#x41a;" u2="&#x443;" k="22" />
+    <hkern u1="&#x41e;" u2="&#x423;" k="16" />
+    <hkern u1="&#x420;" u2="&#x2026;" k="199" />
+    <hkern u1="&#x420;" u2="&#x41b;" k="28" />
+    <hkern u1="&#x420;" u2="&#x414;" k="32" />
+    <hkern u1="&#x420;" u2="&#x410;" k="48" />
+    <hkern u1="&#x420;" u2="&#x2e;" k="134" />
+    <hkern u1="&#x420;" u2="&#x2c;" k="118" />
+    <hkern u1="&#x422;" u2="&#x2026;" k="79" />
+    <hkern u1="&#x422;" u2="&#x451;" k="61" />
+    <hkern u1="&#x422;" u2="&#x44f;" k="24" />
+    <hkern u1="&#x422;" u2="&#x44e;" k="72" />
+    <hkern u1="&#x422;" u2="&#x44d;" k="46" />
+    <hkern u1="&#x422;" u2="&#x44c;" k="72" />
+    <hkern u1="&#x422;" u2="&#x44b;" k="72" />
+    <hkern u1="&#x422;" u2="&#x449;" k="72" />
+    <hkern u1="&#x422;" u2="&#x448;" k="72" />
+    <hkern u1="&#x422;" u2="&#x447;" k="60" />
+    <hkern u1="&#x422;" u2="&#x446;" k="72" />
+    <hkern u1="&#x422;" u2="&#x445;" k="57" />
+    <hkern u1="&#x422;" u2="&#x444;" k="74" />
+    <hkern u1="&#x422;" u2="&#x443;" k="41" />
+    <hkern u1="&#x422;" u2="&#x442;" k="46" />
+    <hkern u1="&#x422;" u2="&#x441;" k="66" />
+    <hkern u1="&#x422;" u2="&#x440;" k="72" />
+    <hkern u1="&#x422;" u2="&#x43f;" k="72" />
+    <hkern u1="&#x422;" u2="&#x43e;" k="62" />
+    <hkern u1="&#x422;" u2="&#x43d;" k="72" />
+    <hkern u1="&#x422;" u2="&#x43c;" k="42" />
+    <hkern u1="&#x422;" u2="&#x43b;" k="39" />
+    <hkern u1="&#x422;" u2="&#x43a;" k="72" />
+    <hkern u1="&#x422;" u2="&#x438;" k="72" />
+    <hkern u1="&#x422;" u2="&#x437;" k="74" />
+    <hkern u1="&#x422;" u2="&#x436;" k="35" />
+    <hkern u1="&#x422;" u2="&#x435;" k="88" />
+    <hkern u1="&#x422;" u2="&#x434;" k="8" />
+    <hkern u1="&#x422;" u2="&#x433;" k="72" />
+    <hkern u1="&#x422;" u2="&#x432;" k="72" />
+    <hkern u1="&#x422;" u2="&#x431;" k="40" />
+    <hkern u1="&#x422;" u2="&#x430;" k="72" />
+    <hkern u1="&#x422;" u2="&#x424;" k="54" />
+    <hkern u1="&#x422;" u2="&#x421;" k="35" />
+    <hkern u1="&#x422;" u2="&#x41e;" k="35" />
+    <hkern u1="&#x422;" u2="&#x41b;" k="33" />
+    <hkern u1="&#x422;" u2="&#x417;" k="39" />
+    <hkern u1="&#x422;" u2="&#x414;" k="33" />
+    <hkern u1="&#x422;" u2="&#x410;" k="47" />
+    <hkern u1="&#x422;" u2="&#x3b;" k="75" />
+    <hkern u1="&#x422;" u2="&#x3a;" k="68" />
+    <hkern u1="&#x422;" u2="&#x2e;" k="68" />
+    <hkern u1="&#x422;" u2="&#x2c;" k="75" />
+    <hkern u1="&#x423;" u2="&#x2026;" k="190" />
+    <hkern u1="&#x423;" u2="&#x451;" k="54" />
+    <hkern u1="&#x423;" u2="&#x44f;" k="30" />
+    <hkern u1="&#x423;" u2="&#x44e;" k="54" />
+    <hkern u1="&#x423;" u2="&#x44d;" k="46" />
+    <hkern u1="&#x423;" u2="&#x44c;" k="18" />
+    <hkern u1="&#x423;" u2="&#x44b;" k="54" />
+    <hkern u1="&#x423;" u2="&#x449;" k="54" />
+    <hkern u1="&#x423;" u2="&#x448;" k="54" />
+    <hkern u1="&#x423;" u2="&#x447;" k="6" />
+    <hkern u1="&#x423;" u2="&#x446;" k="54" />
+    <hkern u1="&#x423;" u2="&#x445;" k="43" />
+    <hkern u1="&#x423;" u2="&#x444;" k="63" />
+    <hkern u1="&#x423;" u2="&#x443;" k="39" />
+    <hkern u1="&#x423;" u2="&#x442;" k="28" />
+    <hkern u1="&#x423;" u2="&#x441;" k="84" />
+    <hkern u1="&#x423;" u2="&#x440;" k="54" />
+    <hkern u1="&#x423;" u2="&#x43f;" k="47" />
+    <hkern u1="&#x423;" u2="&#x43e;" k="89" />
+    <hkern u1="&#x423;" u2="&#x43d;" k="57" />
+    <hkern u1="&#x423;" u2="&#x43c;" k="83" />
+    <hkern u1="&#x423;" u2="&#x43b;" k="68" />
+    <hkern u1="&#x423;" u2="&#x43a;" k="56" />
+    <hkern u1="&#x423;" u2="&#x439;" k="36" />
+    <hkern u1="&#x423;" u2="&#x438;" k="55" />
+    <hkern u1="&#x423;" u2="&#x437;" k="97" />
+    <hkern u1="&#x423;" u2="&#x436;" k="46" />
+    <hkern u1="&#x423;" u2="&#x435;" k="100" />
+    <hkern u1="&#x423;" u2="&#x434;" k="48" />
+    <hkern u1="&#x423;" u2="&#x433;" k="54" />
+    <hkern u1="&#x423;" u2="&#x432;" k="54" />
+    <hkern u1="&#x423;" u2="&#x431;" k="32" />
+    <hkern u1="&#x423;" u2="&#x430;" k="89" />
+    <hkern u1="&#x423;" u2="&#x421;" k="1" />
+    <hkern u1="&#x423;" u2="&#x41e;" k="2" />
+    <hkern u1="&#x423;" u2="&#x41b;" k="61" />
+    <hkern u1="&#x423;" u2="&#x417;" k="49" />
+    <hkern u1="&#x423;" u2="&#x414;" k="26" />
+    <hkern u1="&#x423;" u2="&#x410;" k="137" />
+    <hkern u1="&#x423;" u2="&#x3b;" k="41" />
+    <hkern u1="&#x423;" u2="&#x3a;" k="27" />
+    <hkern u1="&#x423;" u2="&#x2e;" k="122" />
+    <hkern u1="&#x423;" u2="&#x2c;" k="106" />
+    <hkern u1="&#x424;" u2="&#x423;" k="15" />
+    <hkern u1="&#x424;" u2="&#x410;" k="15" />
+    <hkern u1="&#x425;" u2="&#x447;" k="31" />
+    <hkern u1="&#x425;" u2="&#x443;" k="3" />
+    <hkern u1="&#x42a;" u2="&#x423;" k="97" />
+    <hkern u1="&#x42a;" u2="&#x422;" k="46" />
+    <hkern u1="&#x42c;" u2="&#x423;" k="97" />
+    <hkern u1="&#x42c;" u2="&#x422;" k="46" />
+    <hkern u1="&#x433;" u2="&#x2026;" k="81" />
+    <hkern u1="&#x433;" u2="&#x2e;" k="70" />
+    <hkern u1="&#x433;" u2="&#x2c;" k="-2" />
+    <hkern u1="&#x442;" u2="&#x2026;" k="41" />
+    <hkern u1="&#x442;" u2="&#x2e;" k="30" />
+    <hkern u1="&#x442;" u2="&#x2c;" k="-22" />
+    <hkern u1="&#x443;" u2="&#x2026;" k="84" />
+    <hkern u1="&#x443;" u2="&#x437;" k="9" />
+    <hkern u1="&#x443;" u2="&#x2e;" k="73" />
+    <hkern u1="&#x443;" u2="&#x2c;" k="6" />
+    <hkern u1="&#x453;" u2="&#x2026;" k="81" />
+    <hkern u1="&#x453;" u2="&#x2e;" k="70" />
+    <hkern u1="&#x453;" u2="&#x2c;" k="-2" />
+    <hkern u1="&#x45e;" u2="&#x2026;" k="84" />
+    <hkern u1="&#x45e;" u2="&#x437;" k="9" />
+    <hkern u1="&#x45e;" u2="&#x2e;" k="73" />
+    <hkern u1="&#x45e;" u2="&#x2c;" k="6" />
+    <hkern u1="&#x2018;" u2="J" k="37" />
+    <hkern u1="&#x2018;" u2="A" k="48" />
+    <hkern u1="&#x2019;" u2="J" k="80" />
+    <hkern u1="&#x2019;" u2="A" k="184" />
+    <hkern u1="&#x201c;" u2="J" k="37" />
+    <hkern u1="&#x201c;" u2="A" k="52" />
+    <hkern u1="&#x201d;" u2="J" k="80" />
+    <hkern u1="&#x201d;" u2="A" k="235" />
+  </font>
+</defs></svg>
diff --git a/corp/rih/frontend/fonts/futurabookc.ttf b/corp/rih/frontend/fonts/futurabookc.ttf
new file mode 100644
index 0000000000..3c03479718
--- /dev/null
+++ b/corp/rih/frontend/fonts/futurabookc.ttf
Binary files differdiff --git a/corp/rih/frontend/fonts/futurabookc.woff b/corp/rih/frontend/fonts/futurabookc.woff
new file mode 100644
index 0000000000..7069214ae4
--- /dev/null
+++ b/corp/rih/frontend/fonts/futurabookc.woff
Binary files differdiff --git a/corp/rih/frontend/fonts/futurabookc.woff2 b/corp/rih/frontend/fonts/futurabookc.woff2
new file mode 100644
index 0000000000..52d8e62223
--- /dev/null
+++ b/corp/rih/frontend/fonts/futurabookc.woff2
Binary files differdiff --git a/corp/rih/frontend/img/fon.png b/corp/rih/frontend/img/fon.png
new file mode 100644
index 0000000000..fcf24c96d7
--- /dev/null
+++ b/corp/rih/frontend/img/fon.png
Binary files differdiff --git a/corp/rih/frontend/img/it.png b/corp/rih/frontend/img/it.png
new file mode 100644
index 0000000000..4780bae8fa
--- /dev/null
+++ b/corp/rih/frontend/img/it.png
Binary files differdiff --git a/corp/rih/frontend/img/mat.png b/corp/rih/frontend/img/mat.png
new file mode 100644
index 0000000000..bf80285432
--- /dev/null
+++ b/corp/rih/frontend/img/mat.png
Binary files differdiff --git a/corp/rih/frontend/img/mat2.png b/corp/rih/frontend/img/mat2.png
new file mode 100644
index 0000000000..d57e29b64f
--- /dev/null
+++ b/corp/rih/frontend/img/mat2.png
Binary files differdiff --git a/corp/rih/frontend/img/rus.png b/corp/rih/frontend/img/rus.png
new file mode 100644
index 0000000000..e5ca5f9f35
--- /dev/null
+++ b/corp/rih/frontend/img/rus.png
Binary files differdiff --git a/corp/rih/frontend/img/work.png b/corp/rih/frontend/img/work.png
new file mode 100644
index 0000000000..4aea813f0e
--- /dev/null
+++ b/corp/rih/frontend/img/work.png
Binary files differdiff --git a/corp/rih/frontend/index.css b/corp/rih/frontend/index.css
new file mode 100644
index 0000000000..dc067e00e8
--- /dev/null
+++ b/corp/rih/frontend/index.css
@@ -0,0 +1,248 @@
+@font-face {
+    font-family: 'IdealistSans';
+    src: url('fonts/IdealistSans.eot');
+    src: url('fonts/IdealistSans.eot') format('embedded-opentype'),
+		url('fonts/IdealistSans.woff2') format('woff2'),
+		url('fonts/IdealistSans.woff') format('woff'),
+		url('fonts/IdealistSans.ttf') format('truetype'),
+		url('fonts/IdealistSans.svg') format('svg');
+}
+@font-face {
+    font-family: 'futurabookc';
+    src: url('fonts/futurabookc.eot');
+    src: url('fonts/futurabookc.eot') format('embedded-opentype'),
+		url('fonts/futurabookc.woff2') format('woff2'),
+		url('fonts/futurabookc.woff') format('woff'),
+		url('fonts/futurabookc.ttf') format('truetype'),
+		url('fonts/futurabookc.svg') format('svg');
+}
+body{
+	font-size: 16px;
+	color: #000000;
+	font-family: futurabookc;
+}
+
+
+.font-india{
+	font-family: IdealistSans !important;
+}
+
+.font-size-075{
+	font-size: 0.75rem!important;
+}
+.font-size-100{
+	font-size: 1rem!important;
+}
+.font-size-125{
+	font-size: 1.25rem!important;
+}
+.font-size-150{
+	font-size: 1.5rem!important;
+}
+.font-size-200{
+	font-size: 2rem!important;
+}
+.font-size-250{
+	font-size: 2.5rem!important;
+}
+.font-size-300{
+	font-size: 3rem!important;
+}
+.font-weight-bold{
+	font-weight: bold!important;
+}
+.font-weight-500{
+	font-weight: 500!important;
+}
+.font-weight-600{
+	font-weight: 600!important;
+}
+.font-weight-normal{
+	font-weight: normal!important;
+}
+
+@media (min-width:576px){
+	.font-size-sm-075{
+		font-size: 0.75rem!important;
+	}
+	.font-size-sm-100{
+		font-size: 1rem!important;
+	}
+	.font-size-sm-125{
+		font-size: 1.25rem!important;
+	}
+	.font-size-sm-150{
+		font-size: 1.5rem!important;
+	}
+	.font-size-sm-200{
+		font-size: 2rem!important;
+	}
+	.font-size-sm-250{
+		font-size: 2.5rem!important;
+	}
+	.font-size-sm-300{
+		font-size: 3rem!important;
+	}
+
+}
+@media (min-width:768px){
+	.font-size-md-100{
+		font-size: 1rem!important;
+	}
+	.font-size-md-125{
+		font-size: 1.25rem!important
+	}
+	.font-size-md-150{
+		font-size: 1.5rem!important;
+	}
+	.font-size-md-200{
+		font-size: 2rem!important;
+	}
+	.font-size-md-250{
+		font-size: 2.5rem!important;
+	}
+	.font-size-md-300{
+		font-size: 3rem!important;
+	}
+
+}
+
+@media (min-width:992px){
+	.font-size-lg-100{
+		font-size: 1rem!important;
+	}
+	.font-size-lg-125{
+		font-size: 1.25rem!important
+	}
+	.font-size-lg-150{
+		font-size: 1.5rem!important;
+	}
+	.font-size-lg-200{
+		font-size: 2rem!important;
+	}
+	.font-size-lg-250{
+		font-size: 2.5rem!important;
+	}
+	.font-size-lg-300{
+		font-size: 3rem!important;
+	}
+
+}
+
+img{
+	width:auto;
+	height: auto;
+	max-width: 100%;
+	max-height: 100%;
+	vertical-align: middle;
+}
+
+.text-white {
+    color: #ffffff !important;
+}
+
+.text-red{
+	color:#E32D26 !important;
+}
+.text-grey1{
+	color:#81878d  !important;
+}
+.text-grey2{
+	color:rgba(88, 85, 85, 0.63)  !important;
+}
+.first_block{
+	background: #141F29 url(img/fon.png) left top no-repeat;
+	background-size: auto 100% ;
+	color:#ffffff;
+	padding-bottom: 200px;
+	overflow-x: hidden;
+}
+
+
+.second_block{
+	margin-top:-150px;
+	z-index: 10;
+    position: relative;
+}
+.second_block .container > div{
+	background: #ffffff;
+	border:10px solid  #141F29;
+	border-radius: 10px;
+}
+
+.footer{
+	background: #141F29;
+	color:#ffffff;
+}
+.footer a{
+	color:#ffffff;
+}
+
+@media  (max-width: 575.98px){
+	.hidden-xs{
+		display:none!important
+	}
+
+}
+@media (min-width:576px) and (max-width:767.98px){
+	.hidden-sm{
+		display:none!important
+	}
+}
+@media (min-width:768px) and (max-width:991.98px){
+	.hidden-md{
+		display:none!important
+	}
+}
+@media (min-width:992px) and (max-width:1199.98px){
+	.hidden-lg{
+		display:none!important
+	}
+}
+@media (min-width:1200px) and (max-width:1399.98px){
+	.hidden-xl{
+		display:none!important
+	}
+}
+@media (min-width:1400px){
+	.hidden-xxl{
+		display:none!important
+	}
+}
+
+
+@media (min-width:768px){
+	.first_block > div{
+		background: url(img/mat2.png) right top no-repeat;
+		margin-bottom: -200px;
+		padding-bottom: 40px;
+	}
+	.first_block .text-block{
+		height: 1100px;
+	}
+}
+@media (min-width:992px){
+	.first_block > div {
+		min-height: 820px;
+	}
+	.first_block .text-block{
+		height: 820px;
+	}
+}
+@media (min-width:1200px){
+	.first_block > div{
+		min-height: 950px;
+	}
+	.first_block .text-block{
+		height: 950px;
+	}
+}
+
+@media (min-width:1400px){
+	.first_block > div{
+		min-height: 1100px;
+	}
+	.first_block .text-block{
+		height: 1100px;
+	}
+}
diff --git a/corp/rih/frontend/index.html b/corp/rih/frontend/index.html
new file mode 100644
index 0000000000..9fab6b1273
--- /dev/null
+++ b/corp/rih/frontend/index.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en" data-bs-theme="auto">
+  <head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="theme-color" content="#712cf9">
+    <title>Russia is Hiring</title>
+    <link data-trunk rel="inline" href="index.css">
+    <link data-trunk rel="copy-file" href="rih-logo.png">
+    <link data-trunk rel="copy-file" href="privacy-policy.html">
+    <link data-trunk rel="copy-dir" href="img">
+    <link data-trunk rel="copy-dir" href="fonts">
+
+    <!-- Bootstrap -->
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
+
+    <!-- Captcha setup -->
+    <script src="https://smartcaptcha.yandexcloud.net/captcha.js" defer></script>
+  </head>
+</html>
diff --git a/corp/rih/frontend/privacy-policy.html b/corp/rih/frontend/privacy-policy.html
new file mode 100644
index 0000000000..c1c0f8985b
--- /dev/null
+++ b/corp/rih/frontend/privacy-policy.html
@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<html lang="en" data-bs-theme="auto">
+  <head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="theme-color" content="#712cf9">
+    <title>Russia is Hiring</title>
+    <style type="text/css">
+      @font-face {
+          font-family: 'IdealistSans';
+          src: url('fonts/IdealistSans.eot');
+          src: url('fonts/IdealistSans.eot') format('embedded-opentype'),
+	       url('fonts/IdealistSans.woff2') format('woff2'),
+	       url('fonts/IdealistSans.woff') format('woff'),
+	       url('fonts/IdealistSans.ttf') format('truetype'),
+	       url('fonts/IdealistSans.svg') format('svg');
+      }
+
+      body{
+          font-size: 16px;
+          color: #000000;
+      }
+
+      .font-india {
+          font-family: IdealistSans !important;
+      }
+
+      .font-size-150 {
+          font-size: 1.5rem!important;
+      }
+
+      .font-size-250 {
+          font-size: 2.5rem!important;
+      }
+
+      .text-red{
+          color:#E32D26 !important;
+      }
+
+      .text-grey1 {
+          color:#81878d  !important;
+      }
+
+      .text-grey2 {
+          color:#92989e  !important;
+      }
+
+      .first_block {
+          background: #141F29 left top no-repeat;
+          background-size: auto 100% ;
+          color:#ffffff;
+          padding-bottom: 200px;
+          overflow-x: hidden;
+      }
+
+      @media (min-width:768px) {
+          .first_block > div {
+	      margin-bottom: -200px;
+	      padding-bottom: 40px;
+          }
+      }
+      @media (min-width:992px){
+          .first_block > div {
+	      min-height: 820px;
+          }
+      }
+      @media (min-width:1200px){
+          .first_block > div{
+	      min-height: 950px;
+          }
+      }
+
+      @media (min-width:1400px){
+          .first_block > div{
+	      min-height: 1100px;
+          }
+      }
+    </style>
+
+    <!-- Bootstrap -->
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
+  </head>
+  <body>
+    <div class="first_block" id="top">
+      <div class="container">
+	<div class="row h-100 align-items-stretch">
+	  <div class="col-12 col-md-10 col-lg-8">
+	    <div class="d-flex flex-column justify-content-around text-block">
+	      <div class="col-auto pt-5 pt-mb-0">
+		<div class="mb-3 font-size-250 font-india text-center text-md-start">
+		  Privacy Policy
+		</div>
+
+		<div class="mb-3 text-grey1">
+                  <p><i>Effective as of: 2023-05-26</i></p>
+                  <p>This policy outlines who, how and what happens with personal
+                    information entered on russiaishiring.com.</p>
+                  <ol start="0">
+                    <li>
+                      <p>Parties</p>
+                      <p>Entities involved in the collection, storage and processing of
+                        data are the following, all registered in Russia:</p>
+                      <ul>
+                        <li>
+                          <p><a class="text-grey2" href="https://vista-immigration.ru/">VFBS LLC</a> (Vista Foreign Business
+                            Support), INN 7709963942, Π³. Москва, ΠΌΡƒΠ½ΠΈΡ†ΠΈΠΏΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠΊΡ€ΡƒΠ³
+                            Ваганский Π²Π½.Ρ‚Π΅Ρ€.Π³., ΡƒΠ». Ваганская, Π΄. 17-23</p>
+                          <p>Contact: <a class="text-grey2" href="mailto:tbis@vfbs.ru">tbis@vfbs.ru</a></p>
+                        </li>
+                        <li>
+                          <p><a class="text-grey2" href="https://tvl.su/">TVL LLC</a>, INN 9703038861, Π³. Москва, Π²Π½.Ρ‚Π΅Ρ€.Π³.
+                            ΠΌΡƒΠ½ΠΈΡ†ΠΈΠΏΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠΊΡ€ΡƒΠ³ Π‘Π΅Π³ΠΎΠ²ΠΎΠΉ, ΡƒΠ» ΠŸΡ€Π°Π²Π΄Ρ‹, Π΄. 24, стр. 2, ΠΏΠΎΠΌΠ΅Ρ‰. 3П</p>
+                          <p>Contact: <a class="text-grey2" href="mailto:contact@tvl.su">contact@tvl.su</a></p>
+                        </li>
+                        <li>
+                          <p><a class="text-grey2" href="https://yandex.com/company/">Yandex LLC</a>, INN 1027700229193,
+                            119021, Π³ΠΎΡ€ΠΎΠ΄ Москва, ΡƒΠ». Π›ΡŒΠ²Π° Волстого, Π΄.16</p>
+                          <p>Contacts: <a class="text-grey2" href="https://yandex.com/company/contacts/moscow/">https://yandex.com/company/contacts/moscow/</a></p>
+                        </li>
+                      </ul>
+                      <p>In the following text, VFBS LLC will be referred to as &quot;we&quot;.</p>
+                    </li>
+                    <li>
+                      <p>Information we collect</p>
+                      <p>We collect information used for the purpose of matching a potential
+                        job candidate with a vacancy. This includes:</p>
+                      <ul>
+                        <li>Name</li>
+                        <li>Contact e-mail address</li>
+                        <li>Work background</li>
+                        <li>Citizenship</li>
+                        <li>Language skills</li>
+                      </ul>
+                      <p>We collect other <strong>optional</strong> information, such as data about the
+                        work background or expectations from a new job, as deemed relevant
+                        and supplied by the user.</p>
+                      <p>As part of our technical processes we also store the IP address
+                        from which data was submitted.</p>
+                    </li>
+                    <li>
+                      <p>Data storage</p>
+                      <p>We store all collected data in services in Yandex Cloud, with the
+                        help of our technical services provider TVL LLC.</p>
+                      <p>Stored data is subject to the <a class="text-grey2" href="https://yandex.ru/legal/confidential/">Yandex privacy
+                          policy</a>.</p>
+                      <p>Collected data is technically accessible by employees of TVL LLC
+                        and VFBC LLC.</p>
+                    </li>
+                    <li>
+                      <p>Data processing</p>
+                      <p>Data is processed by automatic systems in Yandex Cloud. During
+                        processing, no access is granted to any parties not mentioned
+                        above.</p>
+                    </li>
+                    <li>
+                      <p>Data sharing with third-parties</p>
+                      <p>As part of the operation of the service, data may be passed on to
+                        companies looking to hire candidates based on the data set.</p>
+                      <p>This is never done without the user's consent. If you submit data
+                        to us and a suitable job opportunity is found, we will reach out to
+                        you and confirm the further process as well as any data sharing.</p>
+                    </li>
+                    <li>
+                      <p>User rights</p>
+                      <p>Users that have submitted data to the service always have the right
+                        to request its removal. Please contact us at
+                        <a class="text-grey2" href="mailto:privacy@russiaishiring.com">privacy@russiaishiring.com</a> for help with data removal.</p>
+                      <p>Note that we may require you to verify your identity through the
+                        e-mail address you supplied to us when submitting data.</p>
+                    </li>
+                    <li>
+                      <p>Tracking technologies, cookies, advertising</p>
+                      <p>We do not have any tracking technologies or advertising on
+                        russiaishiring.com. We do not use cookies. Only data explicitly
+                        entered by the user in the form is submitted to us.</p>
+                      <p>The site uses local storage on the user's machine to store form
+                        data between visits to the site.</p>
+                    </li>
+                    <li>
+                      <p>Retention period</p>
+                      <p>We will retain individual data no longer than 2 years from the date
+                        of entering the data. If updated data is received for a user, this
+                        period is reset.
+                        &quot;#)</p>
+                    </li>
+                  </ol>
+		</div>
+
+                <hr>
+		<a href="/" class="d-block text-decoration-none text-grey2 font-size-150 mb-3 font-india text-center text-md-start">
+		  Go back β†’
+		</a>
+	      </div>
+	    </div>
+	  </div>
+	</div>
+
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/corp/rih/frontend/rih-logo.png b/corp/rih/frontend/rih-logo.png
new file mode 100644
index 0000000000..5d12843ede
--- /dev/null
+++ b/corp/rih/frontend/rih-logo.png
Binary files differdiff --git a/corp/rih/frontend/src/home.html b/corp/rih/frontend/src/home.html
new file mode 100644
index 0000000000..96d81da279
--- /dev/null
+++ b/corp/rih/frontend/src/home.html
@@ -0,0 +1,289 @@
+html! {
+<>
+<main>
+  <script>
+    {r#"function captchaOnload(sitekey, callback) {
+      if (window.smartCaptcha) {
+        const container = document.getElementById('captcha-container');
+        const widgetId = window.smartCaptcha.render(container, {
+            sitekey: sitekey,
+            hl: 'en',
+            callback: callback,
+        });
+      }
+    }"#}
+  </script>
+
+  <div class="first_block" id="top">
+    <div class="h-100">
+      <div class="container h-100">
+	<div class="row h-100 align-items-stretch">
+	  <div class="col-12 col-md-8 col-lg-6">
+	    <div class="d-flex flex-column justify-content-around text-block">
+	      <div class="col-auto pt-5 pt-mb-0">
+		<div class="mb-3 font-size-250 font-india text-center text-md-start">
+		  {"Are you an IT-specialist on the hunt for a job?"}
+		</div>
+
+		<div class="mb-3 text-grey1">
+		  {"Well, times are tough in Western countries at the moment. Meanwhile tech is booming in Russia, and national support programs make life as an IT-specialist very comfortable.  Why not look East?"}
+		</div>
+
+		<div class="mb-3 text-grey1">
+		  {"We can help you find an employer in Russia, sort out the formalities and get you started. Sign up and tell us a bit about your profile, or read on below about the benefits of life in Russia."}
+		</div>
+
+		<a href="#signup" class="d-block text-decoration-none text-red font-size-150 mb-3 font-india text-center text-md-start">
+		  {"Sign up β†’"}
+		</a>
+
+	      </div>
+	      <img src="img/mat.png" class="hidden-md hidden-lg hidden-xl hidden-xxl my-3" width="" height="" />
+	      <div class="col-auto text-center text-md-start">
+		<div class="text-red mb-3 font-size-250 font-india">
+		  {"Russia is well placed to draw highly-qualified specialists"}
+		</div>
+		<div class="mb-3 font-size-125">
+		  {"looking to relocate from any part of the World."}
+		</div>
+	      </div>
+	    </div>
+	  </div>
+	</div>
+      </div>
+    </div>
+  </div>
+
+  <div class="second_block mb-3 mb-sm-4 mb-md-5">
+    <div class="container">
+      <div class="px-3">
+	<div class="row py-3 py-sm-4">
+	  <div class="col-12 col-md-4 text-red font-size-150 mb-3 font-india">
+	    {"Russia is"}
+            <br/>
+            {"an industrialized country"}
+	  </div>
+	  <div class="col-12 col-md-8 font-size-125">
+	    <div class="mb-3">
+	      {"and the sole European power whose economic cycle is synchronized with Asian economic growth rather than with Western economic contraction."}
+	    </div>
+	    <div class="mb-3">
+	      {"Despite the recent sanctions and political tensions, Russia is developing rapidly and needs skilled workers to help fuel its growth. The country has a diverse economy, with strong industries in areas such as energy, technology, finance, and manufacturing. Russian economy has much less risks compared to the Western economies, it is isolated from the Western financial sector and is not a Bubble economy."}
+	    </div>
+	  </div>
+	</div>
+      </div>
+    </div>
+  </div>
+
+  <div class="mb-3 mb-sm-4 mb-md-5">
+    <div class="container">
+      <div class="row">
+	<div class="col-12 col-md-6">
+	  <div class=" font-size-150 mb-3 font-india">
+	    {"There are opportunities for professionals in a variety of fields, from engineering and IT to marketing and finance."}
+	  </div>
+	  <div class=" font-size-125 mb-3">
+	    {"So, if you're a skilled professional looking for new opportunities and want to have for you and your family a great quality of life, consider working in Russia."}
+	  </div>
+	</div>
+	<div class="col-12 col-md-6">
+	  <div class=" font-size-150 mb-3 font-india">
+	    {"The country welcomes foreign workers and their families and is ready to offer you the support you need to succeed."}
+	  </div>
+	  <div class=" font-size-125 mb-3">
+	    {"Don't let politics or misconceptions hold you back - come see what Russia has to offer and help build a brighter future for all."}
+	  </div>
+	</div>
+      </div>
+    </div>
+  </div>
+
+  <div class="mb-3 mb-sm-4 mb-md-5">
+    <div class="container">
+      <div class="row align-items-center">
+	<div class="col-12 col-md-4">
+	  <img src="img/it.png" alt="" title=""/>
+	</div>
+	<div class="col-12 col-md-8">
+	  <div class=" font-size-150 text-red mb-3 font-india">
+	    {"As an IT specialist you can qualify for"}
+	  </div>
+	  <div class=" font-size-125 mb-3 font-india">
+	    {"a Highly Qualified Specialist work permit"}
+	  </div>
+	  <div class=" font-size-125 mb-3 font-india">
+	    {"a 3 year work visa that gives you a flat 13% tax rate from day 1 on your salary"}
+	  </div>
+	  <div class=" font-size-125 mb-3 font-india">
+	    {"Moreover, in case your Russian employer is an accredited IT company you are eligible to obtain a permanent residency in Russia within 3-4 months after employment"}
+	  </div>
+	</div>
+      </div>
+    </div>
+  </div>
+
+  <div class="mb-3 mb-sm-4 mb-md-5">
+    <div class="container">
+      <div class="row align-items-center flex-md-row-reverse">
+	<div class="col-12 col-md-6 hidden-xs hidden-sm">
+	  <img src="img/work.png" alt="" title=""/>
+	</div>
+	<div class="col-12 col-md-6">
+	  <div class=" font-size-200 text-red mb-3 font-india">
+	    {"Finding Work in"}
+            <span class="hidden-xs hidden-sm">{"Russia"}</span>
+	  </div>
+	  <img src="img/rus.png" class="mb-3 hidden-md hidden-lg hidden-xl hidden-xxl" alt="" title=""/>
+	  <div class="font-size-125 mb-3 font-india">
+	    {"Usually landing the most interesting jobs requires you to have a well-developed network of contacts, but this is tough when you set your eyes on a new country."}
+          </div>
+	  <div class="font-size-125 mb-3 font-india">
+	    {"Luckily we at Vista Immigration have contacts with many tech companies in Russia, large and small, and can help you with this!"}
+	  </div>
+	  <div class="font-size-125 mb-3 font-india">
+	    {"Tell us a bit about yourself, the technologies you'd like to work with, and your situation in regards to relocating to Russia. We will then match up your profile with companies that match your interests, and establish contact between you and a potential employer if there is a good fit. No generic recruiter spam, guaranteed - we'd rather not send you anything, than send you something irrelevant!"}
+	  </div>
+	  <div class="font-size-125 mb-3 font-india">
+	    {"If you get hired, our experts can assist you with legal and other support for your move."}
+	  </div>
+	</div>
+      </div>
+    </div>
+  </div>
+
+  <hr/>
+
+  if !self.submitted {
+  <div class="container font-size-200 text-center mb-3 mb-sm-4 mb-md-5 font-india" id="signup">
+    {"Welcome to Russia"}
+    <br/>
+    {"Π”ΠΎΠ±Ρ€ΠΎ ΠΏΠΎΠΆΠ°Π»ΠΎΠ²Π°Ρ‚ΡŒ Π² Россию!"}
+  </div>
+
+  <div class="container font-size-150  mb-3 mb-sm-4 mb-md-5 font-india">
+    {"Let's get started with you telling us a bit"}
+    <br/>
+    {"about what kind of job you would like!"}
+  </div>
+
+  <form class="font-size-125  mb-3 mb-sm-4 mb-md-5" >
+    <div class="container">
+
+      <div class="mb-3">
+        <label for="job" class="form-label">{"What job(s) are you looking for?"}</label>
+        <input
+          type="text" class="form-control" id="job"
+          placeholder="Backend/frontend engineer, Test automation, DevOps/SRE, UI/UX ..."
+          oninput={link.callback(|event| input_message(event, Msg::SetPosition))} />
+      </div>
+
+      <div class="mb-3">
+        <label for="technologies" class="form-label">{"Which technologies do you want to work with?"}</label>
+        <div>{render_technologies(link, &self.record.technologies)}</div>
+
+        <input type="text" class="form-control" id="technologies"
+               aria-describedby="technologiesHelp"
+               onkeypress={link.callback(add_tech)}/>
+        <div id="technologiesHelp" class="form-text">{"Press enter after each technology."}</div>
+      </div>
+
+      <div class="mb-3">
+        <label for="jobDetails" class="form-label">{"What's your work background?"}</label>
+        <textarea class="form-control" id="workBackground" rows=3
+                  aria-describedby="workBackgroundHelp"
+                  oninput={link.callback(|event| textarea_message(event, Msg::SetWorkBackground))} >
+        </textarea>
+        <div id="workBackgroundHelp" class="form-text">{"Tell us about your work experience, and/or leave links to your CV on your site, LinkedIn or wherever."}</div>
+      </div>
+
+      <div class="mb-3">
+        <label for="jobDetails" class="form-label">{"Other job details:"}</label>
+        <textarea class="form-control" id="jobDetails" rows=3
+                  aria-describedby="jobDetailsHelp"
+                  oninput={link.callback(|event| textarea_message(event, Msg::SetJobDetails))}>
+        </textarea>
+        <div id="jobDetailsHelp" class="form-text">{"Tell us a bit about what you're looking for in a job and in an employer."}</div>
+      </div>
+
+      <div class="font-size-150 mb-3 ">
+	{"Now we also need some personal details about you:"}
+      </div>
+
+      <div class="mb-3">
+        <label for="name" class="form-label">{"What's your name?"}</label>
+        <input type="text" class="form-control" id="name"
+               oninput={link.callback(|event| input_message(event, Msg::SetName))} />
+      </div>
+
+      <div class="mb-3">
+        <label for="email" class="form-label">{"What's your email address?"}</label>
+        <input type="email" class="form-control" id="email" aria-describedby="emailHelp"
+               oninput={link.callback(|event| input_message(event, Msg::SetEmail))}/>
+        <div id="emailHelp" class="form-text">{"No newsletters, no spam - we will only reach out if there's a match!"}</div>
+      </div>
+
+      <div class="mb-3">
+        <label id="citizenship" class="form-label">{"What citizenship do you hold?"}</label>
+        {citizenship_input(self, link)}
+        <div id="citizenshipHelp" class="form-text">{"We need to know this to estimate immigration-related bureaucracy. If you hold more than one citizenship, pick the one with which you'd want to receive a work visa."}</div>
+      </div>
+
+      {"" /* TODO(tazjin): language knowledge selector */}
+
+      <div class="mb-3">
+        <label for="personalDetails" class="form-label">{"Other relevant information:"}</label>
+        <textarea class="form-control" id="personalDetails" rows=3
+                  aria-describedby="personalDetailsHelp"
+                  oninput={link.callback(|event| textarea_message(event, Msg::SetPersonalDetails))} >
+        </textarea>
+        <div id="personalDetailsHelp" class="form-text">{"Any specific places where you'd like to live? Would you be moving with family? Any other assistance required?"}</div>
+      </div>
+
+      <div class="form-check mb-3">
+	<input class="form-check-input" type="checkbox" id="privacy-policy" required=true />
+	<label class="form-check-label" for="privacy-policy">
+	  {"I have read and agree to the "}
+          <a href="/privacy-policy.html">{"privacy policy"}</a>
+	</label>
+      </div>
+
+      <div id="captcha-container" class="smart-captcha mb-3" style="height: 100px; width=350px;"/>
+
+      <div class="mb-3 text-center">
+        <button type="submit" class="mb-3 btn text-red font-india"
+                disabled={!(self.record.is_complete() && self.captcha_token.is_some())}
+                onclick={link.callback(|_| Msg::Submit)}>
+          {"Submit β†’"}
+        </button>
+      </div>
+
+    </div>
+  </form>
+  } else {
+  <div class="mx-auto col-6 border rounded-3 shadow">
+    <p>{"Thank you for submitting your data! We will reach out to confirm your email address, and further if any matches are found. You can contact us at contact@russiaishiring.com with any questions you might have."}</p>
+  </div>
+  }
+</main>
+<footer class="footer m-0 py-3">
+  <div class="container">
+    <div class="row text-center">
+      <div class="col-12 col-sm-4 text-sm-start">
+        <a href="/privacy-policy.html">{"privacy policy"}</a>
+      </div>
+      <div class="col-12 col-sm-4">
+        {"By "}
+        <a href={VISTA_URL} class="text-white">{"Vista Immigration"}</a>
+        {", with help from "}
+        <a href="https://tvl.su/" class="text-white">{"TVL"}</a>
+        {"."}
+        <br/>
+        {"Β© 2023"}
+      </div>
+      <div class="col-12 col-sm-4 text-sm-end"><a href="#top" class="text-decoration-none">{"Go to top↑"}</a></div>
+    </div>
+  </div>
+</footer>
+</>
+}
diff --git a/corp/rih/frontend/src/main.rs b/corp/rih/frontend/src/main.rs
new file mode 100644
index 0000000000..65f9c79a54
--- /dev/null
+++ b/corp/rih/frontend/src/main.rs
@@ -0,0 +1,512 @@
+use fuzzy_matcher::skim::SkimMatcherV2;
+use fuzzy_matcher::FuzzyMatcher;
+use gloo::console;
+use gloo::history::{BrowserHistory, History};
+use gloo::net::http;
+use gloo::storage::{LocalStorage, Storage};
+use rand::seq::IteratorRandom;
+use rand::thread_rng;
+use serde::{Deserialize, Serialize};
+use static_markdown::markdown;
+use std::collections::BTreeSet;
+use uuid::Uuid;
+use wasm_bindgen::closure::Closure;
+use wasm_bindgen::{JsCast, JsValue};
+use web_sys::{HtmlInputElement, HtmlTextAreaElement, KeyboardEvent};
+use yew::html::Scope;
+use yew::prelude::*;
+use yew_router::prelude::*;
+
+/// Form submission is protected with a captcha. The development
+/// version of the captcha does not do domain checking and works on
+/// `localhost` as well.
+#[cfg(debug_assertions)]
+const CAPTCHA_KEY: &'static str = "ysc1_K7iOi3FSmsyO8pZGu8Im2iQClCtPsVx7jSRyhyCV435a732c";
+
+#[cfg(not(debug_assertions))]
+const CAPTCHA_KEY: &'static str = "ysc1_a3LVlaDRDMwU8CLSZ0WKENTI2exyOxz5J2c6x28P5339d410";
+
+// Form data is submitted to different endpoints in dev/prod.
+#[cfg(debug_assertions)]
+const SUBMIT_URL: &'static str = "http://localhost:9090/submit";
+
+#[cfg(not(debug_assertions))]
+const SUBMIT_URL: &'static str = "https://api.russiaishiring.com/submit";
+
+/// This code ends up being compiled for the native and for the
+/// webassembly architectures during the build & test process.
+/// However, the `rust_iso3166` crate exposes a different API (!)
+/// based on the platform.
+///
+/// This trait acts as a platform-independent wrapper for the crate.
+///
+/// Upstream issue: https://github.com/rust-iso/rust_iso3166/issues/7
+trait CountryCodeAccess {
+    fn country_alpha2(&self) -> String;
+    fn country_alpha3(&self) -> String;
+    fn country_name(&self) -> String;
+}
+
+#[cfg(target_arch = "wasm32")]
+impl CountryCodeAccess for rust_iso3166::CountryCode {
+    fn country_alpha2(&self) -> String {
+        self.alpha2()
+    }
+
+    fn country_alpha3(&self) -> String {
+        self.alpha3()
+    }
+
+    fn country_name(&self) -> String {
+        self.name()
+    }
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+impl CountryCodeAccess for rust_iso3166::CountryCode {
+    fn country_alpha2(&self) -> String {
+        self.alpha2.to_string()
+    }
+
+    fn country_alpha3(&self) -> String {
+        self.alpha3.to_string()
+    }
+
+    fn country_name(&self) -> String {
+        self.name.to_string()
+    }
+}
+
+const VISTA_URL: &'static str = "https://vista-immigration.ru/";
+
+#[derive(Debug, Clone, Copy, PartialEq, Routable)]
+enum Route {
+    #[at("/")]
+    Home,
+    #[at("/privacy-policy")]
+    PrivacyPolicy,
+    #[not_found]
+    #[at("/404")]
+    NotFound,
+}
+
+/// Represents a single record as filled in by a user. This is the
+/// primary data structure we want to populate and persist somewhere.
+#[derive(Clone, Default, Debug, Deserialize, Serialize)]
+struct Record {
+    // Record-specific metadata
+    uuid: Uuid,
+
+    // Personal information
+    name: String,
+    email: String,
+    citizenship: String, // TODO
+    personal_details: String,
+
+    // Job information
+    position: String,
+    technologies: BTreeSet<String>,
+    job_details: String,
+    work_background: String,
+}
+
+impl Record {
+    fn is_complete(&self) -> bool {
+        !self.name.is_empty()
+            && !self.email.is_empty()
+            && !self.citizenship.is_empty()
+            && !self.position.is_empty()
+            && !self.technologies.is_empty()
+    }
+}
+
+struct App {
+    // The record being populated.
+    record: Record,
+
+    // Is the citizenship input focused?
+    citizenship_focus: bool,
+
+    // Current query in the citizenship field.
+    citizenship_query: String,
+
+    // History handler.
+    history: BrowserHistory,
+
+    // Captcha token, if the captcha has been solved.
+    captcha_token: Option<String>,
+
+    // Captcha callback closure which needs to be kept alive for the
+    // lifecycle of the app.
+    captcha_callback: Closure<dyn FnMut(String)>,
+
+    // Has data been submitted already by this user?
+    submitted: bool,
+}
+
+#[derive(Clone, Debug)]
+enum Msg {
+    NoOp,
+    AddTechnology(String),
+    RemoveTechnology(String),
+
+    FocusCitizenship,
+    BlurCitizenship,
+    QueryCitizenship(String),
+    SetCitizenship(String),
+
+    SetName(String),
+    SetEmail(String),
+    SetPersonalDetails(String),
+    SetPosition(String),
+    SetJobDetails(String),
+    SetWorkBackground(String),
+
+    CaptchaSolved(String),
+    Submit,
+}
+
+/// Callback handler for adding a technology.
+fn add_tech(e: KeyboardEvent) -> Msg {
+    if e.key_code() != 13 {
+        return Msg::NoOp;
+    }
+
+    let input = e.target_unchecked_into::<HtmlInputElement>();
+    let tech = input.value();
+    input.set_value("");
+    Msg::AddTechnology(tech)
+}
+
+fn select_country_enter(event: KeyboardEvent) -> Msg {
+    if event.key_code() != 13 {
+        return Msg::NoOp;
+    }
+
+    let input = event.target_unchecked_into::<HtmlInputElement>();
+    if let Some(country) = fuzzy_country_matches(&input.value()).next() {
+        input.set_value(&country.country_name());
+        return Msg::SetCitizenship(country.country_name());
+    }
+
+    Msg::NoOp
+}
+
+fn fuzzy_country_matches(query: &str) -> Box<dyn Iterator<Item = rust_iso3166::CountryCode> + '_> {
+    if query.is_empty() {
+        let rng = &mut thread_rng();
+        return Box::new(
+            rust_iso3166::ALL
+                .iter()
+                .choose_multiple(rng, 5)
+                .into_iter()
+                .map(Clone::clone),
+        );
+    }
+
+    let matcher = SkimMatcherV2::default();
+    let query = query.to_lowercase();
+    let query_len = query.len();
+
+    let mut results: Vec<_> = rust_iso3166::ALL
+        .iter()
+        .filter_map(|code| {
+            let mut score = None;
+            // Prioritize exact matches for country codes if query length <= 3
+            if query_len <= 3 {
+                if code.country_alpha2().eq_ignore_ascii_case(&query)
+                    || code.country_alpha3().eq_ignore_ascii_case(&query)
+                {
+                    score = Some(100);
+                }
+            }
+            // If no exact match, do a fuzzy match
+            if score.is_none() {
+                score = matcher.fuzzy_match(&code.country_name().to_lowercase(), &query);
+            }
+
+            score.map(|score| (score, code))
+        })
+        .collect();
+
+    // Sort by score in descending order
+    results.sort_by(|a, b| b.0.cmp(&a.0));
+
+    // Get iterator over the best matches
+    Box::new(results.into_iter().map(|(_score, code)| *code))
+}
+
+// Callback for an input element's value being mapped straight into a
+// message.
+fn input_message(e: InputEvent, msg: fn(String) -> Msg) -> Msg {
+    let input = e.target_unchecked_into::<HtmlInputElement>();
+    msg(input.value())
+}
+
+// Callback for a text area's value being mapped straight into a
+// message.
+fn textarea_message(e: InputEvent, msg: fn(String) -> Msg) -> Msg {
+    let textarea = e.target_unchecked_into::<HtmlTextAreaElement>();
+    msg(textarea.value())
+}
+
+fn schedule_blur(event: FocusEvent, link: Scope<App>) -> Msg {
+    let input = event.target_unchecked_into::<HtmlInputElement>();
+    let closure = Closure::once_into_js(Box::new(move || {
+        if let Some(app) = link.get_component() {
+            input.set_value(&app.record.citizenship);
+        }
+
+        link.send_message(Msg::BlurCitizenship);
+    }) as Box<dyn FnOnce()>);
+
+    let window = web_sys::window().expect("no global `window` exists");
+    let _ =
+        window.set_timeout_with_callback_and_timeout_and_arguments_0(closure.unchecked_ref(), 100);
+
+    Msg::NoOp
+}
+
+/// Creates an input field for citizenship selection with suggestions.
+fn citizenship_input(app: &App, link: &Scope<App>) -> Html {
+    let dropdown_classes = if app.citizenship_focus {
+        "dropdown-menu show"
+    } else {
+        "dropdown-menu"
+    };
+
+    let choices = fuzzy_country_matches(&app.citizenship_query).map(|country| {
+        let msg = Msg::SetCitizenship(country.country_name());
+        html! {
+            <li><a class="dropdown-item" onclick={link.callback(move |_| msg.clone())}>{country.country_name()}</a></li>
+        }
+    });
+
+    let blur_link = link.clone();
+    html! {
+      <div class="dropdown">
+        <input type="text" class="form-control" id="citizenship" aria-describedby="citizenshipHelp"
+            autocomplete="off"
+            oninput={link.callback(|event| input_message(event, Msg::QueryCitizenship))}
+            onkeypress={link.callback(select_country_enter)}
+            onfocus={link.callback(|_| Msg::FocusCitizenship)}
+            onblur={link.callback(move |event| schedule_blur(event, blur_link.clone()))} />
+            <ul class={dropdown_classes} style="position: absolute; inset: 0px auto auto 0px; margin: 0px; transform: translate(0px, 40px);" data-popper-placement="bottom-start">
+          { choices.collect::<Html>() }
+        </ul>
+      </div>
+    }
+}
+
+/// Creates a list of technologies which can be deleted again by clicking them.
+fn render_technologies(link: &Scope<App>, technologies: &BTreeSet<String>) -> Html {
+    if technologies.is_empty() {
+        return html! {};
+    }
+
+    let items = technologies.iter().map(|tech| {
+        let msg: Msg = Msg::RemoveTechnology(tech.to_string());
+        html! {
+            <>
+              <span class="btn btn-secondary btn-sm"
+                onclick={link.callback(move |_| msg.clone())}>
+                {tech}
+                <span class="mx-auto text-center text-black">{" x"}</span>
+              </span>{" "}
+            </>
+        }
+    });
+    html! {
+        <div class="p-1">
+            { items.collect::<Html>() }
+        </div>
+    }
+}
+
+/// Submit the collected data to the backend service.
+async fn submit_data(captcha_token: &str, record: &Record) -> bool {
+    let response = http::Request::get(SUBMIT_URL)
+        .method(http::Method::POST)
+        .json(&serde_json::json!({
+            "captcha_token": captcha_token,
+            "record": record,
+        }))
+        .expect("serialising a serde_json::Value can not fail")
+        .send()
+        .await
+        .unwrap();
+
+    // currently there is nothing we can actually do with the response
+    // here, but we should add some way to communicate back some
+    // server errors etc., even if the whole thing should be as
+    // forgiving as possible.
+    response.ok()
+}
+
+/// Handle the submit event, if all data was successfully collected.
+fn handle_submit(app: &App, _link: Scope<App>) -> Msg {
+    let token = app.captcha_token.as_ref().unwrap().clone();
+    let record = app.record.clone();
+
+    wasm_bindgen_futures::spawn_local(async move {
+        if !submit_data(&token, &record).await {
+            console::warn!("failed to submit data for some reason");
+        } else {
+            console::log!("submitted data successfully");
+        }
+    });
+
+    Msg::NoOp
+}
+
+impl Component for App {
+    type Message = Msg;
+    type Properties = ();
+
+    fn create(ctx: &Context<Self>) -> Self {
+        App {
+            record: LocalStorage::get("record").unwrap_or_else(|_| {
+                let mut new_record = Record::default();
+                new_record.uuid = Uuid::new_v4();
+                new_record
+            }),
+            citizenship_focus: false,
+            citizenship_query: String::default(),
+            history: BrowserHistory::default(),
+            captcha_token: None,
+            captcha_callback: {
+                let link = ctx.link().clone();
+                Closure::wrap(Box::new(move |val| {
+                    link.send_message(Msg::CaptchaSolved(val));
+                }))
+            },
+            submitted: false,
+        }
+    }
+
+    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
+        console::log!("handling ", format!("{:?}", msg));
+        let (state_change, view_change) = match msg {
+            Msg::NoOp => (false, false),
+
+            Msg::AddTechnology(tech) => {
+                console::log!("adding technology", &tech);
+                self.record.technologies.insert(tech);
+                (true, true)
+            }
+
+            Msg::RemoveTechnology(tech) => {
+                console::log!("removing technology ", &tech);
+                self.record.technologies.remove(&tech);
+                (true, true)
+            }
+
+            Msg::QueryCitizenship(query) => {
+                self.citizenship_query = query;
+                (false, true)
+            }
+
+            Msg::FocusCitizenship => {
+                self.citizenship_focus = true;
+                (false, true)
+            }
+
+            Msg::BlurCitizenship => {
+                self.citizenship_focus = false;
+                (false, true)
+            }
+
+            Msg::SetCitizenship(country) => {
+                self.record.citizenship = country;
+                (true, false)
+            }
+
+            Msg::SetName(name) => {
+                self.record.name = name;
+                (true, false)
+            }
+
+            Msg::SetEmail(email) => {
+                self.record.email = email;
+                (true, false)
+            }
+
+            Msg::SetPersonalDetails(details) => {
+                self.record.personal_details = details;
+                (true, false)
+            }
+
+            Msg::SetPosition(position) => {
+                self.record.position = position;
+                (true, false)
+            }
+
+            Msg::SetJobDetails(details) => {
+                self.record.job_details = details;
+                (true, false)
+            }
+
+            Msg::SetWorkBackground(background) => {
+                self.record.work_background = background;
+                (true, false)
+            }
+
+            Msg::CaptchaSolved(token) => {
+                self.captcha_token = Some(token);
+                (false, true)
+            }
+
+            Msg::Submit => {
+                if self.record.is_complete() && self.captcha_token.is_some() {
+                    self.submitted = true;
+                    handle_submit(self, ctx.link().clone());
+                    (false, true)
+                } else {
+                    console::warn!("submitted data, but form or captcha was not ready");
+                    (false, false)
+                }
+            }
+        };
+
+        if state_change {
+            if let Err(err) = LocalStorage::set("record", &self.record) {
+                console::warn!(
+                    "failed to persist record in local storage: ",
+                    err.to_string()
+                );
+            }
+        }
+
+        view_change
+    }
+
+    fn view(&self, ctx: &Context<Self>) -> Html {
+        let link = ctx.link();
+        let location = self.history.location();
+        let route = Route::recognize(location.path()).unwrap_or(Route::NotFound);
+
+        match route {
+            Route::Home => include!("home.html"),
+            Route::PrivacyPolicy => html! {
+                <main>{include!("privacy-policy.md")}</main>
+            },
+            Route::NotFound => todo!(),
+        }
+    }
+
+    fn rendered(&mut self, _: &Context<Self>, first_render: bool) {
+        if first_render {
+            let func =
+                js_sys::Function::new_with_args("key, callback", "captchaOnload(key, callback)");
+            let _ = func.call2(
+                &JsValue::NULL,
+                &JsValue::from_str(CAPTCHA_KEY),
+                &self.captcha_callback.as_ref().unchecked_ref(),
+            );
+        }
+    }
+}
+
+fn main() {
+    yew::Renderer::<App>::new().render();
+}
diff --git a/corp/rih/frontend/src/privacy-policy.md b/corp/rih/frontend/src/privacy-policy.md
new file mode 100644
index 0000000000..843aac4c0a
--- /dev/null
+++ b/corp/rih/frontend/src/privacy-policy.md
@@ -0,0 +1,100 @@
+markdown!(r#"
+Privacy Policy
+==============
+
+Effective as of: 2023-05-26
+
+This policy outlines who, how and what happens with personal
+information entered on russiaishiring.com.
+
+0. Parties
+
+    Entities involved in the collection, storage and processing of
+    data are the following, all registered in Russia:
+
+   - [VFBS LLC](https://vista-immigration.ru/) (Vista Foreign Business
+     Support), INN 7709963942, Π³. Москва, ΠΌΡƒΠ½ΠΈΡ†ΠΈΠΏΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠΊΡ€ΡƒΠ³
+     Ваганский Π²Π½.Ρ‚Π΅Ρ€.Π³., ΡƒΠ». Ваганская, Π΄. 17-23
+
+     Contact: tbis@vfbs.ru
+
+   - [TVL LLC](https://tvl.su/), INN 9703038861, Π³. Москва, Π²Π½.Ρ‚Π΅Ρ€.Π³.
+     ΠΌΡƒΠ½ΠΈΡ†ΠΈΠΏΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠΊΡ€ΡƒΠ³ Π‘Π΅Π³ΠΎΠ²ΠΎΠΉ, ΡƒΠ» ΠŸΡ€Π°Π²Π΄Ρ‹, Π΄. 24, стр. 2, ΠΏΠΎΠΌΠ΅Ρ‰. 3П
+
+     Contact: contact@tvl.su
+
+   - [Yandex LLC](https://yandex.com/company/), INN 1027700229193,
+     119021, Π³ΠΎΡ€ΠΎΠ΄ Москва, ΡƒΠ». Π›ΡŒΠ²Π° Волстого, Π΄.16
+
+     Contacts: https://yandex.com/company/contacts/moscow/
+
+   In the following text, VFBS LLC will be referred to as "we".
+
+1. Information we collect
+
+   We collect information used for the purpose of matching a potential
+   job candidate with a vacancy. This includes:
+
+   - Name
+   - Contact e-mail address
+   - Work background
+   - Citizenship
+   - Language skills
+
+   We collect other **optional** information, such as data about the
+   work background or expectations from a new job, as deemed relevant
+   and supplied by the user.
+
+   As part of our technical processes we also store the IP address
+   from which data was submitted.
+
+2. Data storage
+
+   We store all collected data in services in Yandex Cloud, with the
+   help of our technical services provider TVL LLC.
+
+   Stored data is subject to the [Yandex privacy
+   policy](https://yandex.ru/legal/confidential/).
+
+   Collected data is technically accessible by employees of TVL LLC
+   and VFBC LLC.
+
+3. Data processing
+
+   Data is processed by automatic systems in Yandex Cloud. During
+   processing, no access is granted to any parties not mentioned
+   above.
+
+4. Data sharing with third-parties
+
+   As part of the operation of the service, data may be passed on to
+   companies looking to hire candidates based on the data set.
+
+   This is never done without the user's consent. If you submit data
+   to us and a suitable job opportunity is found, we will reach out to
+   you and confirm the further process as well as any data sharing.
+
+5. User rights
+
+   Users that have submitted data to the service always have the right
+   to request its removal. Please contact us at
+   privacy@russiaishiring.com for help with data removal.
+
+   Note that we may require you to verify your identity through the
+   e-mail address you supplied to us when submitting data.
+
+6. Tracking technologies, cookies, advertising
+
+   We do not have any tracking technologies or advertising on
+   russiaishiring.com. We do not use cookies. Only data explicitly
+   entered by the user in the form is submitted to us.
+
+   The site uses local storage on the user's machine to store form
+   data between visits to the site.
+
+7. Retention period
+
+   We will retain individual data no longer than 2 years from the date
+   of entering the data. If updated data is received for a user, this
+   period is reset.
+"#)
diff --git a/corp/rih/frontend/static-markdown/Cargo.toml b/corp/rih/frontend/static-markdown/Cargo.toml
new file mode 100644
index 0000000000..c0e298d44c
--- /dev/null
+++ b/corp/rih/frontend/static-markdown/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+version = "1.0.0"
+name = "static_markdown"
+edition = "2018"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = "1.0"
+quote = "1.0"
+comrak = "0.18"
diff --git a/corp/rih/frontend/static-markdown/src/lib.rs b/corp/rih/frontend/static-markdown/src/lib.rs
new file mode 100644
index 0000000000..6a25c92926
--- /dev/null
+++ b/corp/rih/frontend/static-markdown/src/lib.rs
@@ -0,0 +1,27 @@
+extern crate proc_macro;
+
+use comrak::{markdown_to_html, ComrakOptions};
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{parse_macro_input, LitStr};
+
+#[proc_macro]
+pub fn markdown(input: TokenStream) -> TokenStream {
+    let input = parse_macro_input!(input as LitStr);
+
+    let mut options = ComrakOptions::default();
+    options.extension.strikethrough = true;
+    options.extension.tagfilter = true;
+    options.extension.table = true;
+    options.extension.autolink = true;
+
+    let rendered_html = markdown_to_html(&input.value(), &options);
+
+    let tokens = quote! {
+        yew::virtual_dom::VNode::VRaw(yew::virtual_dom::VRaw {
+            html: #rendered_html.into(),
+        })
+    };
+
+    tokens.into()
+}
diff --git a/corp/russian/README.md b/corp/russian/README.md
new file mode 100644
index 0000000000..23c3d594c8
--- /dev/null
+++ b/corp/russian/README.md
@@ -0,0 +1,9 @@
+//corp/russian
+==============
+
+This folder contains TVL corp projects related to the Russian
+language, such as the code powering
+[ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½ΠΈΠΊ](https://predlozhnik.ru).
+
+Unless otherwise specified, all rights to these projects are reserved
+by ООО "Π’Π’Π›".
diff --git a/corp/russian/data-import/.gitignore b/corp/russian/data-import/.gitignore
new file mode 100644
index 0000000000..e918c641ce
--- /dev/null
+++ b/corp/russian/data-import/.gitignore
@@ -0,0 +1,2 @@
+target/
+all_events.txt
diff --git a/corp/russian/data-import/Cargo.lock b/corp/russian/data-import/Cargo.lock
new file mode 100644
index 0000000000..cd85e05810
--- /dev/null
+++ b/corp/russian/data-import/Cargo.lock
@@ -0,0 +1,499 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bstr"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+dependencies = [
+ "lazy_static",
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "csv"
+version = "1.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
+dependencies = [
+ "bstr",
+ "csv-core",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "data-import"
+version = "0.1.0"
+dependencies = [
+ "csv",
+ "env_logger",
+ "log",
+ "rusqlite",
+ "serde",
+ "xml-rs",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fallible-iterator"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "getrandom"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa"
+dependencies = [
+ "hashbrown",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
+dependencies = [
+ "hermit-abi",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.139"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.25.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "once_cell"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+
+[[package]]
+name = "rusqlite"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a"
+dependencies = [
+ "bitflags",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
+name = "rustix"
+version = "0.36.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
+
+[[package]]
+name = "serde"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
+name = "syn"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
diff --git a/corp/russian/data-import/Cargo.toml b/corp/russian/data-import/Cargo.toml
new file mode 100644
index 0000000000..1aae2e8305
--- /dev/null
+++ b/corp/russian/data-import/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "data-import"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+csv = "1.1"
+env_logger = "0.10.0"
+log = "0.4.17"
+rusqlite = "0.28"
+serde = { version = "1.0", features = ["derive"] }
+xml-rs = "0.8"
+
+[profile.release-with-debug]
+inherits = "release"
+debug = true
diff --git a/corp/russian/data-import/default.nix b/corp/russian/data-import/default.nix
new file mode 100644
index 0000000000..6aa8ad6aa3
--- /dev/null
+++ b/corp/russian/data-import/default.nix
@@ -0,0 +1,55 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  buildInputs = with pkgs; [
+    sqlite
+    pkg-config
+  ];
+
+  # mirrored input data from OpenCorpora, as of 2023-01-17.
+  #
+  # This data is licensed under CC-BY-SA.
+  openCorporaArchive = pkgs.fetchurl {
+    name = "dict.opcorpora.xml.bz";
+    url = "https://tazj.in/blobs/opencorpora-20230117.xml.bz2";
+    sha256 = "04n5g43fkfc93z6xlwf2qfdrfdfl562pc2ivdb3cbbbsy56gkqg6";
+  };
+
+  openCorpora = pkgs.runCommand "dict.opcorpora.xml" { } ''
+    ${pkgs.bzip2}/bin/bunzip2 -k -c ${openCorporaArchive} > $out
+  '';
+
+  # mirrored input data from OpenRussian, as of 2023-01-17.
+  #
+  # This data is licensed under CC-BY-SA.
+  openRussianArchive = pkgs.fetchzip {
+    name = "openrussian-20230117";
+    url = "https://tazj.in/blobs/openrussian-20230117.tar.xz";
+    sha256 = "06jl7i23cx58a0n2626hb82xlzimixvnxp7lxdw0g664kv9bmw25";
+  };
+
+  # development shell with native deps
+  shell = pkgs.mkShell {
+    inherit buildInputs;
+
+    # make datasets available in the environment
+    OPENCORPORA_DATA = openCorpora;
+    OPENRUSSIAN_DATA = openRussianArchive;
+  };
+
+in
+lib.fix (self: depot.third_party.naersk.buildPackage {
+  src = depot.third_party.gitignoreSource ./.;
+  inherit buildInputs;
+
+  passthru = depot.nix.readTree.drvTargets {
+    inherit shell openCorpora;
+
+    # target that actually builds an entire database
+    database = pkgs.runCommand "tvl-russian-db.sqlite"
+      {
+        OPENCORPORA_DATA = openCorpora;
+        OPENRUSSIAN_DATA = openRussianArchive;
+      } "${self}/bin/data-import --output $out";
+  };
+})
diff --git a/corp/russian/data-import/src/db_setup.rs b/corp/russian/data-import/src/db_setup.rs
new file mode 100644
index 0000000000..c9fb517386
--- /dev/null
+++ b/corp/russian/data-import/src/db_setup.rs
@@ -0,0 +1,298 @@
+//! This module prepares the database layout.
+//!
+//! The XML import may be in an arbitrary order, so importing data is
+//! a multi-step process where we first set up schemas matching the
+//! data layout, import the data, and then modify the schema to
+//! introduce things like foreign key constraints between tables that
+//! represent relations.
+
+use super::Ensure;
+use crate::oc_parser::*;
+use crate::or_parser;
+use log::{debug, info};
+use rusqlite::Connection;
+
+/// Sets up an initial schema which matches the OpenCorpora data.
+pub fn initial_oc_schema(conn: &Connection) {
+    conn.execute_batch(
+        r#"
+-- table for plain import of grammemes from XML
+CREATE TABLE oc_grammemes (
+    name TEXT PRIMARY KEY,
+    parent TEXT,
+    alias TEXT,
+    description TEXT
+) STRICT;
+
+-- table for plain import of lemmas (*not* their variations!)
+CREATE TABLE oc_lemmas (
+    id INTEGER PRIMARY KEY,
+    lemma TEXT NOT NULL
+) STRICT;
+
+-- table for relationship between grammemes and lemmas
+CREATE TABLE oc_lemma_grammemes (
+    lemma INTEGER,
+    grammeme TEXT NOT NULL,
+    FOREIGN KEY(lemma) REFERENCES oc_lemmas(id)
+) STRICT;
+
+-- table for all words, i.e. including variations of lemmata
+CREATE TABLE oc_words (
+    lemma INTEGER NOT NULL,
+    word TEXT NOT NULL,
+    FOREIGN KEY(lemma) REFERENCES oc_lemmas(id)
+) STRICT;
+
+-- table for relationship between words and grammemes
+CREATE TABLE oc_word_grammemes (
+    word INTEGER NOT NULL,
+    grammeme TEXT NOT NULL,
+    FOREIGN KEY(word) REFERENCES oc_words(ROWID)
+) STRICT;
+
+-- table for link types
+CREATE TABLE oc_link_types (
+  id INTEGER PRIMARY KEY,
+  name TEXT
+) STRICT;
+
+-- table for links between lemmata
+CREATE TABLE oc_links (
+  id INTEGER PRIMARY KEY,
+  link_type INTEGER NOT NULL,
+  from_lemma INTEGER NOT NULL,
+  to_lemma INTEGER NOT NULL,
+  FOREIGN KEY(link_type) REFERENCES oc_link_types(id),
+  FOREIGN KEY(from_lemma) REFERENCES oc_lemmas(id),
+  FOREIGN KEY(to_lemma) REFERENCES oc_lemmas(id)
+) STRICT;
+
+"#,
+    )
+    .ensure("setting up OpenCorpora table schema failed");
+
+    info!("set up initial table schema for OpenCorpora import");
+}
+
+/// Inserts a single OpenCorpora element into the initial table structure.
+pub fn insert_oc_element(conn: &Connection, elem: OcElement) {
+    match elem {
+        OcElement::Grammeme(grammeme) => {
+            conn.execute(
+                "INSERT INTO oc_grammemes (name, parent, alias, description) VALUES (?1, ?2, ?3, ?4)",
+                (
+                    &grammeme.name,
+                    &grammeme.parent,
+                    &grammeme.alias,
+                    &grammeme.description,
+                ),
+            )
+            .ensure("failed to insert grammeme");
+
+            debug!("inserted grammeme {}", grammeme.name);
+        }
+
+        OcElement::Lemma(lemma) => insert_lemma(conn, lemma),
+
+        OcElement::LinkType(lt) => {
+            conn.execute(
+                "INSERT INTO oc_link_types (id, name) VALUES (?1, ?2)",
+                (&lt.id, &lt.name),
+            )
+            .ensure("failed to insert link type");
+
+            info!("inserted link type {}", lt.name);
+        }
+
+        OcElement::Link(link) => {
+            let mut stmt = conn
+                .prepare_cached(
+                    "INSERT INTO oc_links (id, link_type, from_lemma, to_lemma) VALUES (?1, ?2, ?3, ?4)",
+                )
+                .ensure("failed to prepare link statement");
+
+            stmt.execute((&link.id, &link.link_type, &link.from, &link.to))
+                .ensure("failed to insert link");
+
+            debug!("inserted link {}", link.id);
+        }
+    }
+}
+
+/// Insert a single lemma into the initial structure. This is somewhat
+/// involved because it also establishes a bunch of relations.
+fn insert_lemma(conn: &Connection, lemma: Lemma) {
+    // insert the lemma itself
+    let mut stmt = conn
+        .prepare_cached("INSERT INTO oc_lemmas (id, lemma) VALUES (?1, ?2)")
+        .ensure("failed to prepare statement");
+
+    stmt.execute((&lemma.id, &lemma.lemma.word))
+        .ensure("failed to insert grammeme");
+
+    // followed by its relations to the grammemes set
+    let mut stmt = conn
+        .prepare_cached("INSERT INTO oc_lemma_grammemes (lemma, grammeme) VALUES (?1, ?2)")
+        .ensure("failed to prepare statement");
+
+    for grammeme in lemma.grammemes {
+        stmt.execute((&lemma.id, grammeme))
+            .ensure("failed to insert grammeme<>lemma relationship");
+    }
+
+    // followed by all of its variations ...
+    let mut word_insert = conn
+        .prepare_cached("INSERT INTO oc_words (lemma, word) VALUES (?1, ?2)")
+        .unwrap();
+
+    let mut word_grammeme = conn
+        .prepare_cached("INSERT INTO oc_word_grammemes (word, grammeme) VALUES (?1, ?2)")
+        .unwrap();
+
+    for variation in lemma.variations {
+        // insert the word itself and get its rowid
+        word_insert
+            .execute((&lemma.id, &variation.word))
+            .ensure("failed to insert word");
+        let row_id = conn.last_insert_rowid();
+
+        // then insert its grammeme links
+        for grammeme in variation.grammemes {
+            word_grammeme
+                .execute((row_id, grammeme))
+                .ensure("failed to insert word<>grammeme link");
+        }
+    }
+
+    debug!("inserted lemma {}", lemma.id);
+}
+
+/// Sets up an initial schema for the OpenRussian data.
+pub fn initial_or_schema(conn: &Connection) {
+    conn.execute_batch(
+        r#"
+CREATE TABLE or_words (
+    id INTEGER PRIMARY KEY,
+    bare TEXT NOT NULL,
+    accented TEXT,
+    derived_from_word_id INTEGER,
+    rank INTEGER,
+    word_type TEXT,
+    level TEXT
+) STRICT;
+
+CREATE TABLE or_words_forms (
+    id INTEGER PRIMARY KEY,
+    word_id INTEGER NOT NULL,
+    form_type TEXT,
+    position TEXT,
+    form TEXT,
+    form_bare TEXT,
+    FOREIGN KEY(word_id) REFERENCES words(id)
+) STRICT;
+
+CREATE TABLE or_translations (
+    id INTEGER PRIMARY KEY,
+    word_id INTEGER NOT NULL,
+    translation TEXT,
+    example_ru TEXT,
+    example_tl TEXT,
+    info TEXT,
+    FOREIGN KEY(word_id) REFERENCES words(id)
+) STRICT;
+"#,
+    )
+    .ensure("setting up OpenRussian table schema failed");
+
+    info!("set up initial table schema for OpenRussian import");
+}
+
+pub fn insert_or_words<I: Iterator<Item = or_parser::Word>>(conn: &Connection, words: I) {
+    let mut stmt = conn
+        .prepare_cached(
+            "
+INSERT INTO or_words (id, bare, accented, derived_from_word_id, rank, word_type, level)
+VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
+",
+        )
+        .ensure("failed to prepare OR words statement");
+    let mut count = 0;
+
+    for word in words {
+        stmt.execute((
+            word.id,
+            word.bare,
+            word.accented,
+            word.derived_from_word_id,
+            word.rank,
+            word.word_type,
+            word.level,
+        ))
+        .ensure("failed to insert OR word");
+        count += 1;
+    }
+
+    info!("inserted {} OpenRussian words", count);
+}
+
+pub fn insert_or_word_forms<I: Iterator<Item = or_parser::WordForm>>(conn: &Connection, forms: I) {
+    let mut stmt = conn
+        .prepare_cached(
+            "
+INSERT INTO or_words_forms (id, word_id, form_type, position, form, form_bare)
+VALUES (?1, ?2, ?3, ?4, ?5, ?6)
+",
+        )
+        .ensure("failed to prepare OR word forms statement");
+    let mut count = 0;
+
+    for form in forms {
+        stmt.execute((
+            form.id,
+            form.word_id,
+            form.form_type,
+            form.position,
+            form.form,
+            form.form_bare,
+        ))
+        .ensure("failed to insert OR word form");
+        count += 1;
+    }
+
+    info!("inserted {} OpenRussian word forms", count);
+}
+
+pub fn insert_or_translations<I: Iterator<Item = or_parser::Translation>>(
+    conn: &Connection,
+    translations: I,
+) {
+    let mut stmt = conn
+        .prepare_cached(
+            "INSERT INTO or_translations (id, word_id, translation, example_ru, example_tl, info)
+             VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
+        )
+        .ensure("failed to prepare OR translation statement");
+
+    let mut count = 0;
+
+    for tl in translations {
+        if tl.lang != "en" {
+            continue;
+        }
+
+        stmt.execute((
+            tl.id,
+            tl.word_id,
+            tl.tl,
+            tl.example_ru,
+            tl.example_tl,
+            tl.info,
+        ))
+        .ensure("failed to insert OR translation");
+
+        count += 1;
+    }
+
+    info!("inserted {} OpenRussian translations", count);
+}
diff --git a/corp/russian/data-import/src/main.rs b/corp/russian/data-import/src/main.rs
new file mode 100644
index 0000000000..21da48e8d8
--- /dev/null
+++ b/corp/russian/data-import/src/main.rs
@@ -0,0 +1,298 @@
+//! This program imports Russian language data from OpenCorpora
+//! ("ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΉ корпус") and OpenRussian into a SQLite database that
+//! can be used for [//corp/russian][corp-russian] projects.
+//!
+//! [corp-russian]: https://at.tvl.fyi/?q=%2F%2Fcorp%2Frussian
+//!
+//! Ideally, running this on intact dumps should yield a fully
+//! functional SQLite database compatible with all other tools
+//! consuming it.
+//!
+//! ## OpenCorpora format
+//!
+//! The format used is partially documented on the [OpenCorpora
+//! website][format-docs]. This seems to be a slightly outdated
+//! format, however, hence some information about what the format
+//! seems to be today.
+//!
+//! [format-docs]: http://opencorpora.org/?page=export
+//!
+//! The format is an XML file, which has several categories of data,
+//! each with their own schema:
+//!
+//! * `grammemes`: These define units of grammar. They're *likely* pretty
+//!   static, and we'll *likely* want to map them into a custom set of
+//!   (simpler) categories.
+//!
+//!   They form some kind of internal hierarchy, where some of them have a
+//!   `parent` attribute set to some other grammemes `name`.
+//!
+//!   There's a ridiculous number of these.
+//!
+//! * `restrictions`: Unclear, not documented on the page. They describe
+//!   something about the relationship between grammemes.
+//!
+//! * `lemmata`: this lists the actual lemmas, as well as all their
+//!   included morphological variants
+//!
+//!   Each lemma has an `id` attribute uniquely identifying its dictionary
+//!   form, as well as a number of sub-elements:
+//!
+//!   * the `l` attribute contains the lemma itself
+//!   * the `f` attributes contain morphological variations
+//!
+//!   Each of these sub elements again contains a number of `g` elements,
+//!   which refer to the IDs of grammems in their `v` attributes.
+//!
+//! * `<link_types>` These list possible "relationships between lemmas",
+//!   basically just assigning them IDs and names. There's only 27 of
+//!   these.
+//!
+//! * `<links>`: Using the types defined above, this establishes links
+//!   between lemmas that have some kind of relationship.
+//!
+//!   For example, a relationship `cardinal/ordinal` might be established
+//!   between the lemmas "Π΄Π²Π°" and "Π²Ρ‚ΠΎΡ€ΠΎΠΉ".
+//!
+//! ## OpenRussian format
+//!
+//! The [OpenRussian](https://en.openrussian.org/dictionary) project
+//! lets users export its database as a set of CSV-files. For our
+//! purposes, we download the files using `<tab>` separators.
+//!
+//! Whereas OpenCorpora opts for a flat structure with a "tag" system
+//! (through its flexible grammemes), OpenRussian has a fixed pre-hoc
+//! structure into which it sorts some words with their morphologies.
+//! The OpenRussian database is much smaller as of January 2023 (~1.7
+//! million words vs. >5 million for OpenCorpora), but some of the
+//! information is much more practically useful.
+//!
+//! Two very important bits of information OpenRussian has are accent
+//! marks (most tables containing actual words have a normal form
+//! containing and accent mark, and a "bare" form without) and
+//! translations into English and German.
+//!
+//! The full dump includes the following tables (and some more):
+//!
+//! * `words`: List of lemmas in the corpus, with various bits of
+//!    metadata as well as hand-written notes.
+//!
+//! * `adjectives`: Contains IDs for words that are adjectives.
+//!
+//! * `nouns`: IDs for words that are nouns; and noun metadata (e.g.
+//!   gender, declinability)
+//!
+//! * `verbs`: IDs of words that are verbs, including their aspect and
+//!   "partnered" verb in the other aspect
+//!
+//! * `words_forms`: Contains all morphed variants of the lemmas from
+//!   `words`, including information about their grammeme, and accent
+//!   marks.
+//!
+//! * `words_rels`: Contains relations between words, containing
+//!   information like "synonyms" or general relation between words.
+//!
+//! * `translations`: Contains translations tagged by target language,
+//!   as well as examples and (occasionally) additional information.
+//!
+//! These tables also contain something, but have not been analysed
+//! yet:
+//!
+//! * `expressions_words`
+//! * `sentences`
+//! * `sentences_translations`
+//! * `sentences_words`
+
+use log::{error, info};
+use rusqlite::{Connection, Result};
+use std::env;
+use std::fmt::Display;
+use std::fs::File;
+use std::io::BufReader;
+
+mod db_setup;
+mod mappings;
+mod oc_parser;
+mod or_parser;
+
+struct Args {
+    output: String,
+    or_input: String,
+    oc_input: String,
+}
+
+impl Args {
+    fn populated(&self) -> bool {
+        !(self.output.is_empty() || self.or_input.is_empty() || self.oc_input.is_empty())
+    }
+}
+
+fn usage(binary_name: &str) {
+    bail(format!(
+        "usage: {} --output <output-file> --or-input <or-input> --oc-input <oc-input>",
+        binary_name
+    ));
+}
+
+fn parse_args() -> Args {
+    let mut args_iter = env::args();
+    let binary_name = args_iter.next().unwrap();
+
+    let mut args = Args {
+        output: "".into(),
+        or_input: env::var("OPENRUSSIAN_DATA").unwrap_or_default(),
+        oc_input: env::var("OPENCORPORA_DATA").unwrap_or_default(),
+    };
+
+    loop {
+        if args.populated() {
+            break;
+        }
+
+        while let Some(arg) = args_iter.next() {
+            match arg.as_str() {
+                "--output" => {
+                    args.output = args_iter.next().unwrap();
+                }
+
+                "--or-input" => {
+                    args.or_input = args_iter.next().unwrap();
+                }
+
+                "--oc-input" => {
+                    args.oc_input = args_iter.next().unwrap();
+                }
+
+                _ => usage(&binary_name),
+            }
+        }
+    }
+
+    if args.output.is_empty() || args.or_input.is_empty() || args.oc_input.is_empty() {
+        usage(&binary_name);
+    }
+
+    args
+}
+
+fn open_corpora(conn: &Connection, args: &Args) {
+    let input_file = File::open(&args.oc_input).ensure("failed to open input file");
+    let mut parser = oc_parser::OpenCorporaParser::new(BufReader::new(input_file));
+    db_setup::initial_oc_schema(&conn);
+
+    let mut tx = conn
+        .unchecked_transaction()
+        .ensure("failed to start transaction");
+
+    let mut count = 0;
+
+    while let Some(elem) = parser.next_element() {
+        // commit every 1000 things
+        if count % 1000 == 0 {
+            tx.commit().ensure("transaction failed");
+            tx = conn
+                .unchecked_transaction()
+                .ensure("failed to start new transaction");
+            info!("transaction committed at watermark {}", count);
+        }
+
+        db_setup::insert_oc_element(&tx, elem);
+
+        count += 1;
+    }
+
+    tx.commit().ensure("final OpenCorpora commit failed");
+
+    info!("finished OpenCorpora import");
+}
+
+fn open_russian(conn: &Connection, args: &Args) {
+    let parser = or_parser::OpenRussianParser::new(&args.or_input);
+
+    db_setup::initial_or_schema(conn);
+
+    {
+        let tx = conn
+            .unchecked_transaction()
+            .ensure("failed to start transaction");
+
+        db_setup::insert_or_words(&tx, parser.words());
+        tx.commit().ensure("OpenRussian words commit failed");
+    }
+
+    {
+        let tx = conn
+            .unchecked_transaction()
+            .ensure("failed to start transaction");
+
+        db_setup::insert_or_word_forms(&tx, parser.words_forms());
+        tx.commit().ensure("OpenRussian word forms commit failed");
+    }
+
+    {
+        let tx = conn
+            .unchecked_transaction()
+            .ensure("failed to start transaction");
+
+        db_setup::insert_or_translations(&tx, parser.translations());
+        tx.commit().ensure("OpenRussian translations commit failed");
+    }
+
+    info!("finished OpenRussian import");
+}
+
+fn main() {
+    env_logger::builder()
+        .filter_level(log::LevelFilter::Info)
+        .init();
+
+    let args = parse_args();
+
+    info!("output path: {}", args.output);
+    info!("OpenCorpora input path: {}", args.oc_input);
+    info!("OpenRussian input path: {}", args.or_input);
+
+    let conn = Connection::open(&args.output).ensure("failed to open DB connection");
+
+    open_corpora(&conn, &args);
+    open_russian(&conn, &args);
+
+    // afterwards:
+    // add actual IDs to grammemes
+    // properly reference keys internally
+    // add foreign key constraint on lemma_grammemes.grammeme
+}
+
+/// It's like `expect`, but through `log::error`.
+trait Ensure<T> {
+    fn ensure<S: Into<String>>(self, msg: S) -> T;
+}
+
+impl<T, E: Display> Ensure<T> for Result<T, E> {
+    fn ensure<S: Into<String>>(self, msg: S) -> T {
+        match self {
+            Ok(x) => x,
+            Err(err) => {
+                error!("{}: {}", msg.into(), err);
+                std::process::exit(1);
+            }
+        }
+    }
+}
+
+impl<T> Ensure<T> for Option<T> {
+    fn ensure<S: Into<String>>(self, msg: S) -> T {
+        match self {
+            Some(x) => x,
+            None => {
+                error!("{}", msg.into());
+                std::process::exit(1);
+            }
+        }
+    }
+}
+
+fn bail<S: Into<String>>(msg: S) -> ! {
+    error!("{}", msg.into());
+    std::process::exit(1);
+}
diff --git a/corp/russian/data-import/src/mappings.rs b/corp/russian/data-import/src/mappings.rs
new file mode 100644
index 0000000000..985088a566
--- /dev/null
+++ b/corp/russian/data-import/src/mappings.rs
@@ -0,0 +1,185 @@
+//! Manual mapping of some data structures in OC/OR corpora.
+
+/// Maps the *names* of OpenRussian word types (the `word_type` field
+/// in the `or_words` table) to the *set* of OpenCorpora grammemes
+/// commonly attached to lemmata of this type in OC.
+///
+/// Some word types just don't map over, and are omitted. Many words
+/// also have an empty word type.
+pub const WORD_TYPES_GRAMMEME_MAP: &'static [(&'static str, &'static [&'static str])] = &[
+    ("adjective", &["ADJF"]),
+    ("adverb", &["ADVB"]),
+    ("noun", &["NOUN"]),
+    ("verb", &["INFN"]), // or "VERB" ...
+];
+
+/// Maps the *names* of OpenRussian grammemes (the `form_type` fields
+/// in the `or_word_forms` table) to the *set* of OpenCorpora
+/// grammemes attached to them corresponding lemma in the `oc_lemmas`
+/// table.
+///
+/// This *only* includes grammatical information about the lemma of
+/// the word (such as whether it is a verb or other type), but *not*
+/// information about the specific instance of the word (such as its
+/// gender).
+///
+/// Correctly corresponding these requires use of all mapping tables.
+pub const FORMS_LEMMATA_GRAMMEME_MAP: &'static [(&'static str, &'static [&'static str])] = &[
+    ("ru_adj_comparative", &["COMP"]),
+    ("ru_adj_superlative", &["ADJF", "Supr"]),
+    ("ru_adj_f_acc", &["ADJF"]),
+    ("ru_adj_f_dat", &["ADJF"]),
+    ("ru_adj_f_gen", &["ADJF"]),
+    ("ru_adj_f_inst", &["ADJF"]),
+    ("ru_adj_f_nom", &["ADJF"]),
+    ("ru_adj_f_prep", &["ADJF"]),
+    ("ru_adj_m_acc", &["ADJF"]),
+    ("ru_adj_m_dat", &["ADJF"]),
+    ("ru_adj_m_gen", &["ADJF"]),
+    ("ru_adj_m_inst", &["ADJF"]),
+    ("ru_adj_m_nom", &["ADJF"]),
+    ("ru_adj_m_prep", &["ADJF"]),
+    ("ru_adj_n_acc", &["ADJF"]),
+    ("ru_adj_n_dat", &["ADJF"]),
+    ("ru_adj_n_gen", &["ADJF"]),
+    ("ru_adj_n_inst", &["ADJF"]),
+    ("ru_adj_n_nom", &["ADJF"]),
+    ("ru_adj_n_prep", &["ADJF"]),
+    ("ru_adj_pl_acc", &["ADJF"]),
+    ("ru_adj_pl_dat", &["ADJF"]),
+    ("ru_adj_pl_gen", &["ADJF"]),
+    ("ru_adj_pl_inst", &["ADJF"]),
+    ("ru_adj_pl_nom", &["ADJF"]),
+    ("ru_adj_pl_prep", &["ADJF"]),
+    ("ru_adj_short_f", &["ADJS"]),
+    ("ru_adj_short_m", &["ADJS"]),
+    ("ru_adj_short_n", &["ADJS"]),
+    ("ru_adj_short_pl", &["ADJS"]),
+    ("ru_noun_pl_acc", &["NOUN"]),
+    ("ru_noun_pl_dat", &["NOUN"]),
+    ("ru_noun_pl_gen", &["NOUN"]),
+    ("ru_noun_pl_inst", &["NOUN"]),
+    ("ru_noun_pl_nom", &["NOUN"]),
+    ("ru_noun_pl_prep", &["NOUN"]),
+    ("ru_noun_sg_acc", &["NOUN"]),
+    ("ru_noun_sg_dat", &["NOUN"]),
+    ("ru_noun_sg_gen", &["NOUN"]),
+    ("ru_noun_sg_inst", &["NOUN"]),
+    ("ru_noun_sg_nom", &["NOUN"]),
+    ("ru_noun_sg_prep", &["NOUN"]),
+    ("ru_verb_gerund_past", &["GRND"]),
+    ("ru_verb_gerund_present", &["GRND"]),
+    ("ru_verb_imperative_pl", &["VERB"]),
+    ("ru_verb_imperative_sg", &["VERB"]),
+    ("ru_verb_past_f", &["VERB"]),
+    ("ru_verb_past_m", &["VERB"]),
+    ("ru_verb_past_n", &["VERB"]),
+    ("ru_verb_past_pl", &["VERB"]),
+    ("ru_verb_presfut_pl1", &["VERB"]),
+    ("ru_verb_presfut_pl2", &["VERB"]),
+    ("ru_verb_presfut_pl3", &["VERB"]),
+    ("ru_verb_presfut_sg1", &["VERB"]),
+    ("ru_verb_presfut_sg2", &["VERB"]),
+    ("ru_verb_presfut_sg3", &["VERB"]),
+    (
+        "ru_base",
+        &[ /* nothing consistent, except often 'Fixd' */ ],
+    ),
+    ("ru_verb_participle_active_past", &["PRTF", "past", "actv"]),
+    (
+        "ru_verb_participle_active_present",
+        &["PRTF", "pres", "actv"],
+    ),
+    (
+        "ru_verb_participle_passive_past",
+        &["PRTF", "past", "passv"],
+    ),
+    (
+        "ru_verb_participle_passive_present",
+        &["PRTF", "pres", "passv"],
+    ),
+];
+
+/// Maps the *names* of OpenRussian grammemes (the `form_type` fields
+/// in the `or_word_forms` table) to the *set* of OpenCorpora
+/// grammemes attached to them corresponding words in the `oc_words`
+/// table.
+///
+/// This includes grammatical information about the "instance" of the
+/// word (such as its gender), but *not* the higher-level type
+/// information about its lemma.
+///
+/// Correctly corresponding these requires use of all mapping tables.
+pub const FORMS_WORDS_GRAMMEME_MAP: &'static [(&'static str, &'static [&'static str])] = &[
+    ("ru_adj_comparative", &["Cmp2"]),
+    ("ru_adj_f_acc", &["femn", "sing", "accs"]),
+    ("ru_adj_f_dat", &["femn", "sing", "datv"]),
+    ("ru_adj_f_gen", &["femn", "sing", "gent"]),
+    ("ru_adj_f_inst", &["femn", "sing", "ablt"]),
+    ("ru_adj_f_nom", &["femn", "sing", "nomn"]),
+    ("ru_adj_f_prep", &["femn", "sing", "loct"]),
+    ("ru_adj_m_acc", &["masc", "sing", "accs"]),
+    ("ru_adj_m_dat", &["masc", "sing", "datv"]),
+    ("ru_adj_m_gen", &["masc", "sing", "gent"]),
+    ("ru_adj_m_inst", &["masc", "sing", "ablt"]),
+    ("ru_adj_m_nom", &["masc", "sing", "nomn"]),
+    ("ru_adj_m_prep", &["masc", "sing", "loct"]),
+    ("ru_adj_n_acc", &["neut", "sing", "accs"]),
+    ("ru_adj_n_dat", &["neut", "sing", "datv"]),
+    ("ru_adj_n_gen", &["neut", "sing", "gent"]),
+    ("ru_adj_n_inst", &["neut", "sing", "ablt"]),
+    ("ru_adj_n_nom", &["neut", "sing", "nomn"]),
+    ("ru_adj_n_prep", &["neut", "sing", "loct"]),
+    ("ru_adj_pl_acc", &["plur", "accs"]),
+    ("ru_adj_pl_dat", &["plur", "datv"]),
+    ("ru_adj_pl_gen", &["plur", "gent"]),
+    ("ru_adj_pl_inst", &["plur", "ablt"]),
+    ("ru_adj_pl_nom", &["plur", "nomn"]),
+    ("ru_adj_pl_prep", &["plur", "loct"]),
+    ("ru_adj_short_f", &["femn", "sing"]),
+    ("ru_adj_short_m", &["masc", "sing"]),
+    ("ru_adj_short_n", &["neut", "sing"]),
+    ("ru_adj_short_pl", &["plur"]),
+    ("ru_noun_pl_acc", &["plur", "accs"]),
+    ("ru_noun_pl_dat", &["plur", "datv"]),
+    ("ru_noun_pl_gen", &["plur", "gent"]),
+    ("ru_noun_pl_inst", &["plur", "ablt"]),
+    ("ru_noun_pl_nom", &["plur", "nomn"]),
+    ("ru_noun_pl_prep", &["plur", "loct"]),
+    ("ru_noun_sg_acc", &["sing", "accs"]),
+    ("ru_noun_sg_dat", &["sing", "datv"]),
+    ("ru_noun_sg_gen", &["sing", "gent"]),
+    ("ru_noun_sg_inst", &["sing", "ablt"]),
+    ("ru_noun_sg_nom", &["sing", "nomn"]),
+    ("ru_noun_sg_prep", &["sing", "loct"]),
+    ("ru_verb_gerund_past", &["past", "V-sh"]),
+    ("ru_verb_imperative_pl", &["plur", "impr"]),
+    ("ru_verb_imperative_sg", &["sing", "impr"]),
+    ("ru_verb_past_f", &["femn", "sing", "past"]),
+    ("ru_verb_past_m", &["masc", "sing", "past"]),
+    ("ru_verb_past_n", &["neut", "sing", "past"]),
+    ("ru_verb_past_pl", &["plur", "past"]),
+    // these also contain "pres" or "futr", depending on the verb.
+    ("ru_verb_presfut_pl1", &["plur", "1per"]),
+    ("ru_verb_presfut_pl2", &["plur", "2per"]),
+    ("ru_verb_presfut_pl3", &["plur", "3per"]),
+    ("ru_verb_presfut_sg1", &["sing", "1per"]),
+    ("ru_verb_presfut_sg2", &["sing", "2per"]),
+    ("ru_verb_presfut_sg3", &["sing", "3per"]),
+    // Unclear items, probably only useful tags on lemmata
+    (
+        "ru_verb_gerund_present",
+        &["pres" /* prob. something missing? */],
+    ),
+    (
+        "ru_adj_superlative",
+        &[/* TODO: unclear, random list of grammemes?! */],
+    ),
+    ("ru_base", &[/* TODO: unclear */]),
+    // These have no useful tags in the forms table, only gender &
+    // case tagging.
+    ("ru_verb_participle_active_past", &[]),
+    ("ru_verb_participle_active_present", &[]),
+    ("ru_verb_participle_passive_past", &[]),
+    ("ru_verb_participle_passive_present", &[]),
+];
diff --git a/corp/russian/data-import/src/oc_parser.rs b/corp/russian/data-import/src/oc_parser.rs
new file mode 100644
index 0000000000..8103ebd923
--- /dev/null
+++ b/corp/russian/data-import/src/oc_parser.rs
@@ -0,0 +1,470 @@
+use super::{bail, Ensure};
+use log::{info, warn};
+use std::str::FromStr;
+use xml::attribute::OwnedAttribute;
+use xml::name::OwnedName;
+use xml::reader::XmlEvent;
+use xml::EventReader;
+
+#[derive(Default, Debug)]
+pub struct Grammeme {
+    pub parent: Option<String>,
+    pub name: String,
+    pub alias: String,
+    pub description: String,
+}
+
+/// Single form of a word (either its lemma, or the variations).
+#[derive(Debug, Default)]
+pub struct Variation {
+    pub word: String,
+    pub grammemes: Vec<String>,
+}
+
+#[derive(Debug, Default)]
+pub struct Lemma {
+    pub id: u64,
+    pub lemma: Variation,
+    pub grammemes: Vec<String>,
+    pub variations: Vec<Variation>,
+}
+
+#[derive(Debug, Default)]
+pub struct LinkType {
+    pub id: u64,
+    pub name: String,
+}
+
+#[derive(Debug, Default)]
+pub struct Link {
+    pub id: u64,   // link itself
+    pub from: u64, // lemma
+    pub to: u64,   // lemma
+    pub link_type: u64,
+}
+
+#[derive(Debug)]
+pub enum OcElement {
+    Grammeme(Grammeme),
+    Lemma(Lemma),
+    LinkType(LinkType),
+    Link(Link),
+}
+
+#[derive(Debug, PartialEq)]
+enum ParserState {
+    /// Parser is not parsing any particular section and waiting for a
+    /// start tag instead.
+    Init,
+
+    /// Parser is parsing grammemes.
+    Grammemes,
+
+    /// Parser is parsing lemmata.
+    Lemmata,
+
+    /// Parser is inside a lemma's actual lemma.
+    Lemma,
+
+    /// Parser is parsing a morphological variation of a lemma.
+    Variation,
+
+    /// Parser is parsing link types.
+    LinkTypes,
+
+    /// Parser is parsing links.
+    Links,
+
+    /// Parser has seen the end of the line and nothing more is
+    /// available.
+    Ended,
+}
+
+pub struct OpenCorporaParser<R: std::io::Read> {
+    reader: EventReader<R>,
+    state: ParserState,
+}
+
+#[derive(PartialEq)]
+enum SectionState {
+    /// Actively interested in parsing this section.
+    Active,
+
+    /// Section is known, but currently ignored.
+    Inactive,
+
+    /// Section is unknown (probably a bug).
+    Unknown,
+}
+
+fn section_state(section: &str) -> SectionState {
+    match section {
+        "grammemes" | "lemmata" | "link_types" | "links" => SectionState::Active,
+        "restrictions" => SectionState::Inactive,
+        _ => SectionState::Unknown,
+    }
+}
+
+impl<R: std::io::Read> OpenCorporaParser<R> {
+    pub fn new(reader: R) -> Self {
+        let config = xml::ParserConfig::new().trim_whitespace(true);
+        let reader = EventReader::new_with_config(reader, config);
+
+        Self {
+            reader,
+            state: ParserState::Init,
+        }
+    }
+
+    /// Pull an `OcElement` out of the parser. Returns `None` if the
+    /// parser stream has ended.
+    pub fn next_element(&mut self) -> Option<OcElement> {
+        if self.state == ParserState::Ended {
+            return None;
+        }
+
+        // Pull the next element to determine what context to enter
+        // next.
+        loop {
+            match &self.next() {
+                // no-op events that do not affect parser state
+                XmlEvent::Comment(_)
+                | XmlEvent::Whitespace(_)
+                | XmlEvent::ProcessingInstruction { .. }
+                | XmlEvent::StartDocument { .. } => continue,
+                XmlEvent::StartElement { name, .. } | XmlEvent::EndElement { name }
+                    if name.local_name == "dictionary" =>
+                {
+                    continue
+                }
+
+                // end of the file, nothing more to return
+                XmlEvent::EndDocument => {
+                    self.state = ParserState::Ended;
+                    return None;
+                }
+
+                // some sections are skipped
+                XmlEvent::StartElement { name, .. } | XmlEvent::EndElement { name }
+                    if section_state(&name.local_name) == SectionState::Inactive =>
+                {
+                    info!("skipping {} section", name.local_name);
+                    self.skip_section(&name.local_name);
+                }
+
+                // active section events start specific parser states ...
+                XmlEvent::StartElement { name, .. }
+                    if section_state(&name.local_name) == SectionState::Active =>
+                {
+                    self.state = match name.local_name.as_str() {
+                        "grammemes" => ParserState::Grammemes,
+                        "lemmata" => ParserState::Lemmata,
+                        "link_types" => ParserState::LinkTypes,
+                        "links" => ParserState::Links,
+                        _ => unreachable!(),
+                    };
+                }
+
+                // ... or end them
+                XmlEvent::EndElement { name, .. }
+                    if section_state(&name.local_name) == SectionState::Active =>
+                {
+                    // TODO: assert that the right section ended
+                    self.state = ParserState::Init;
+                }
+
+                // actual beginning of an actual element, dispatch accordingly
+                event @ XmlEvent::StartElement {
+                    name, attributes, ..
+                } => match &self.state {
+                    ParserState::Grammemes => {
+                        return Some(OcElement::Grammeme(self.parse_grammeme(name, attributes)))
+                    }
+
+                    ParserState::Lemmata => {
+                        return Some(OcElement::Lemma(self.parse_lemma(name, attributes)))
+                    }
+
+                    ParserState::LinkTypes => {
+                        return Some(OcElement::LinkType(self.parse_link_type(name, attributes)))
+                    }
+
+                    ParserState::Links if name.local_name == "link" => {
+                        return Some(OcElement::Link(self.parse_link(attributes)))
+                    }
+
+                    ParserState::Init | ParserState::Ended => bail(format!(
+                        "parser received an unexpected start element while in state {:?}: {:?}",
+                        self.state, event
+                    )),
+
+                    other => bail(format!(
+                        "next_element() called while parser was in state {:?}",
+                        other
+                    )),
+                },
+
+                // finally, events that indicate a bug if they're
+                // encountered here
+                event @ XmlEvent::EndElement { .. }
+                | event @ XmlEvent::CData(_)
+                | event @ XmlEvent::Characters(_) => {
+                    bail(format!("unexpected XML event: {:?}", event))
+                }
+            }
+        }
+    }
+
+    /// Skip a section by advancing the parser state until we see an
+    /// end element for the skipped section.
+    fn skip_section(&mut self, section: &str) {
+        loop {
+            match self.next() {
+                XmlEvent::EndElement { name } if name.local_name == section => return,
+                _ => continue,
+            }
+        }
+    }
+
+    fn next(&mut self) -> XmlEvent {
+        self.reader.next().ensure("XML parsing failed")
+    }
+
+    /// Parse a tag that should have plain string content.
+    fn parse_string(&mut self, tag_name: &str) -> String {
+        let mut out = String::new();
+
+        loop {
+            match self.next() {
+                // ignore irrelevant things
+                XmlEvent::Comment(_) | XmlEvent::Whitespace(_) => continue,
+
+                // set the content
+                XmlEvent::Characters(content) => {
+                    out = content;
+                }
+
+                // expect the end of the element
+                XmlEvent::EndElement { name } if name.local_name == tag_name => return out,
+
+                // fail on everything unexpected
+                event => bail(format!(
+                    "unexpected element while parsing <{}>: {:?}",
+                    tag_name, event
+                )),
+            }
+        }
+    }
+
+    /// Parse a single `<grammeme>` tag.
+    fn parse_grammeme(&mut self, name: &OwnedName, attributes: &[OwnedAttribute]) -> Grammeme {
+        if name.local_name != "grammeme" {
+            bail(format!(
+                "expected to parse a grammeme, but found <{}>",
+                name.local_name
+            ));
+        }
+
+        let mut grammeme = Grammeme::default();
+
+        for attr in attributes {
+            if attr.name.local_name == "parent" && !attr.value.is_empty() {
+                grammeme.parent = Some(attr.value.clone());
+            }
+        }
+
+        loop {
+            match self.next() {
+                // ignore irrelevant things
+                XmlEvent::Comment(_) | XmlEvent::Whitespace(_) => continue,
+
+                // expect known tags
+                XmlEvent::StartElement { name, .. } if name.local_name == "name" => {
+                    grammeme.name = self.parse_string("name");
+                }
+
+                XmlEvent::StartElement { name, .. } if name.local_name == "alias" => {
+                    grammeme.alias = self.parse_string("alias");
+                }
+
+                XmlEvent::StartElement { name, .. } if name.local_name == "description" => {
+                    grammeme.description = self.parse_string("description");
+                }
+
+                // handle end of the grammeme
+                XmlEvent::EndElement { name } if name.local_name == "grammeme" => break,
+
+                // fail on everything unexpected
+                event => bail(format!(
+                    "unexpected element while parsing <grammeme>: {:?}",
+                    event
+                )),
+            }
+        }
+
+        grammeme
+    }
+
+    fn parse_lemma(&mut self, name: &OwnedName, attributes: &[OwnedAttribute]) -> Lemma {
+        if name.local_name != "lemma" {
+            bail(format!(
+                "expected to parse a lemma, but found <{}>",
+                name.local_name
+            ));
+        }
+
+        self.state = ParserState::Lemma;
+        let mut lemma = Lemma::default();
+
+        for attr in attributes {
+            if attr.name.local_name == "id" {
+                lemma.id = u64::from_str(&attr.value).ensure("failed to parse lemma ID");
+            }
+        }
+
+        loop {
+            match self.next() {
+                // <lemma> has ended
+                XmlEvent::EndElement { name } if name.local_name == "lemma" => {
+                    self.state = ParserState::Lemmata;
+                    return lemma;
+                }
+
+                // actual lemma content
+                XmlEvent::StartElement {
+                    name, attributes, ..
+                } => {
+                    match name.local_name.as_str() {
+                        // beginning to parse the lemma itself
+                        "l" => {
+                            lemma.lemma.word = attributes
+                                .into_iter()
+                                .find(|attr| attr.name.local_name == "t")
+                                .map(|attr| attr.value)
+                                .ensure(format!("lemma {} had no actual word", lemma.id));
+                        }
+
+                        // parsing a lemma variation
+                        "f" => {
+                            self.state = ParserState::Variation;
+
+                            let word = attributes
+                                .into_iter()
+                                .find(|attr| attr.name.local_name == "t")
+                                .map(|attr| attr.value)
+                                .ensure(format!(
+                                    "variation of lemma {} had no actual word",
+                                    lemma.id
+                                ));
+
+                            lemma.variations.push(Variation {
+                                word,
+                                grammemes: vec![],
+                            });
+                        }
+
+                        // parse a grammeme association
+                        "g" => {
+                            let grammeme = attributes
+                                .into_iter()
+                                .find(|attr| attr.name.local_name == "v")
+                                .map(|attr| attr.value)
+                                .ensure(format!(
+                                    "grammeme association in lemma {} missing ID",
+                                    lemma.id
+                                ));
+
+                            match self.state {
+                                ParserState::Lemma => {
+                                    lemma.grammemes.push(grammeme);
+                                }
+
+                                ParserState::Variation => {
+                                    lemma
+                                        .variations
+                                        .last_mut()
+                                        .ensure("variations should be non-empty")
+                                        .grammemes
+                                        .push(grammeme);
+                                }
+
+                                _ => bail(format!("invalid parser state: encountered grammeme association while in {:?}", self.state)),
+                            }
+                        }
+
+                        other => bail(format!("unexpected element while parsing lemma: {other}")),
+                    };
+                }
+
+                XmlEvent::EndElement { name } => match name.local_name.as_str() {
+                    "l" if self.state == ParserState::Lemma => continue,
+                    "f" if self.state == ParserState::Variation => {
+                        self.state = ParserState::Lemma;
+                        continue;
+                    }
+                    "g" => continue,
+                    other => bail(format!(
+                        "unexpected </{other}> while parsing lemma {}",
+                        lemma.id
+                    )),
+                },
+
+                _ => continue,
+            }
+        }
+    }
+
+    fn parse_link_type(&mut self, name: &OwnedName, attributes: &[OwnedAttribute]) -> LinkType {
+        if name.local_name != "type" {
+            bail(format!(
+                "expected to parse a link type, but found <{}>",
+                name.local_name
+            ));
+        }
+
+        let mut link_type = LinkType::default();
+
+        for attr in attributes {
+            if attr.name.local_name == "id" {
+                link_type.id = u64::from_str(&attr.value).ensure("failed to parse link type ID");
+            }
+        }
+
+        link_type.name = self.parse_string("type");
+        link_type
+    }
+
+    fn parse_link(&mut self, attributes: &[OwnedAttribute]) -> Link {
+        let mut link = Link::default();
+
+        for attr in attributes {
+            let i_val = || u64::from_str(&attr.value).ensure("failed to parse link field");
+
+            match attr.name.local_name.as_str() {
+                "id" => {
+                    link.id = i_val();
+                }
+                "from" => {
+                    link.from = i_val();
+                }
+                "to" => {
+                    link.to = i_val();
+                }
+                "type" => {
+                    link.link_type = i_val();
+                }
+
+                other => {
+                    warn!("unexpected attribute {} on <link>", other);
+                    continue;
+                }
+            }
+        }
+
+        // expect the end of the <link> element, though since these
+        // are empty it should be immediate.
+        self.skip_section("link");
+
+        link
+    }
+}
diff --git a/corp/russian/data-import/src/or_parser.rs b/corp/russian/data-import/src/or_parser.rs
new file mode 100644
index 0000000000..8bfc61dbef
--- /dev/null
+++ b/corp/russian/data-import/src/or_parser.rs
@@ -0,0 +1,105 @@
+//! Parser for the OpenRussian data format.
+//!
+//! Note that when exporting OpenRussian data from the project you
+//! have to choose an encoding. We choose tab-separated CSV files, as
+//! tabs have a very low probability of actually appearing in the
+//! input data and this skips some potential encoding issues.
+
+use super::Ensure;
+use serde::Deserialize;
+use std::fs::File;
+use std::io::BufReader;
+use std::path::PathBuf;
+
+/// A word from the `words` table.
+#[derive(Debug, Deserialize)]
+pub struct Word {
+    pub id: usize,
+    pub position: String, // TODO: unknown
+    pub bare: String,     // TODO: unknown
+    pub accented: String, // TODO: unknown
+    pub derived_from_word_id: Option<usize>,
+    pub rank: Option<usize>,
+    pub disabled: String,     // TODO: unknown
+    pub audio: String,        // TODO: unknown
+    pub usage_en: String,     // TODO: unknown
+    pub usage_de: String,     // TODO: unknown
+    pub number_value: String, // TODO: unknown
+
+    #[serde(rename = "type")]
+    pub word_type: String, // TODO: unknown
+
+    pub level: String,      // TODO: unknown
+    pub created_at: String, // TODO: unknown
+}
+
+/// A word form from the `words_forms` table.
+#[derive(Debug, Deserialize)]
+pub struct WordForm {
+    pub id: usize,
+    pub word_id: usize,
+    pub form_type: String,
+    pub position: String,
+    pub form: String,
+    pub form_bare: String,
+}
+
+/// A translation from the `translations` table.
+#[derive(Debug, Deserialize)]
+pub struct Translation {
+    pub id: usize,
+    pub lang: String,
+    pub word_id: usize,
+    pub position: String,
+    pub tl: String, // unknown
+    pub example_ru: String,
+    pub example_tl: String,
+    pub info: String,
+}
+
+pub struct OpenRussianParser {
+    or_directory: PathBuf,
+}
+
+pub type DynIter<T> = Box<dyn Iterator<Item = T>>;
+
+impl OpenRussianParser {
+    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
+        OpenRussianParser {
+            or_directory: path.into(),
+        }
+    }
+
+    pub fn words(&self) -> DynIter<Word> {
+        self.parser_for("words.csv")
+    }
+
+    pub fn words_forms(&self) -> DynIter<WordForm> {
+        self.parser_for("words_forms.csv")
+    }
+
+    pub fn translations(&self) -> DynIter<Translation> {
+        self.parser_for("translations.csv")
+    }
+
+    fn parser_for<T: serde::de::DeserializeOwned + 'static>(
+        &self,
+        file_name: &str,
+    ) -> Box<dyn Iterator<Item = T>> {
+        let mut path = self.or_directory.clone();
+        path.push(file_name);
+
+        let reader = csv::ReaderBuilder::new()
+            .delimiter(b'\t')
+            .from_reader(BufReader::new(
+                File::open(&path).ensure("failed to open words.csv"),
+            ));
+
+        Box::new(reader.into_deserialize().map(|result| {
+            result.ensure(format!(
+                "failed to deserialize {}",
+                std::any::type_name::<T>()
+            ))
+        }))
+    }
+}
diff --git a/corp/russian/predlozhnik/.gitignore b/corp/russian/predlozhnik/.gitignore
new file mode 100644
index 0000000000..58eaf3e326
--- /dev/null
+++ b/corp/russian/predlozhnik/.gitignore
@@ -0,0 +1,3 @@
+/target/
+**/*.rs.bk
+dist/
diff --git a/corp/russian/predlozhnik/Cargo.lock b/corp/russian/predlozhnik/Cargo.lock
new file mode 100644
index 0000000000..1cacb43b39
--- /dev/null
+++ b/corp/russian/predlozhnik/Cargo.lock
@@ -0,0 +1,471 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "boolinator"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
+
+[[package]]
+name = "bumpalo"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23947965eee55e3e97a5cd142dd4c10631cc349b48cecca0ed230fd296f568cd"
+dependencies = [
+ "gloo-console",
+ "gloo-dialogs",
+ "gloo-events",
+ "gloo-file",
+ "gloo-render",
+ "gloo-storage",
+ "gloo-timers",
+ "gloo-utils",
+]
+
+[[package]]
+name = "gloo-console"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-dialogs"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-events"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-file"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7"
+dependencies = [
+ "gloo-events",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-render"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-storage"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40913a05c8297adca04392f707b1e73b12ba7b8eab7244a4961580b1fd34063c"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
+[[package]]
+name = "once_cell"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
+
+[[package]]
+name = "predlozhnik"
+version = "0.1.0"
+dependencies = [
+ "lazy_static",
+ "maplit",
+ "wasm-bindgen",
+ "yew",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.101",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "scoped-tls-hkt"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e9d7eaddb227e8fbaaa71136ae0e1e913ca159b86c7da82f3e8f0044ad3a63"
+
+[[package]]
+name = "serde"
+version = "1.0.145"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.145"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.101",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.101",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
+
+[[package]]
+name = "web-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "yew"
+version = "0.19.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a1ccb53e57d3f7d847338cf5758befa811cabe207df07f543c06f502f9998cd"
+dependencies = [
+ "console_error_panic_hook",
+ "gloo",
+ "gloo-utils",
+ "indexmap",
+ "js-sys",
+ "scoped-tls-hkt",
+ "slab",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "yew-macro",
+]
+
+[[package]]
+name = "yew-macro"
+version = "0.19.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fab79082b556d768d6e21811869c761893f0450e1d550a67892b9bce303b7bb"
+dependencies = [
+ "boolinator",
+ "lazy_static",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.101",
+]
diff --git a/corp/russian/predlozhnik/Cargo.toml b/corp/russian/predlozhnik/Cargo.toml
new file mode 100644
index 0000000000..87537b560b
--- /dev/null
+++ b/corp/russian/predlozhnik/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "predlozhnik"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+maplit = "1.0"
+lazy_static = "1.4"
+yew = "0.19"
+
+# needs to be in sync with nixpkgs
+wasm-bindgen = "= 0.2.91"
diff --git a/corp/russian/predlozhnik/default.nix b/corp/russian/predlozhnik/default.nix
new file mode 100644
index 0000000000..2137be1112
--- /dev/null
+++ b/corp/russian/predlozhnik/default.nix
@@ -0,0 +1,52 @@
+{ lib, pkgs, ... }:
+
+let
+  wasmRust = pkgs.rust-bin.stable.latest.default.override {
+    targets = [ "wasm32-unknown-unknown" ];
+  };
+
+  cargoToml = with builtins; fromTOML (readFile ./Cargo.toml);
+
+  wasmBindgenMatch =
+    cargoToml.dependencies.wasm-bindgen == "= ${pkgs.wasm-bindgen-cli.version}";
+
+  assertWasmBindgen = assert (lib.assertMsg wasmBindgenMatch ''
+    Due to instability in the Rust WASM ecosystem, the trunk build
+    tool enforces that the Cargo-dependency version of `wasm-bindgen`
+    MUST match the version of the CLI supplied in the environment.
+
+    This can get out of sync when nixpkgs is updated. To resolve it,
+    wasm-bindgen must be bumped in the Cargo.toml file and cargo needs
+    to be run to resolve the dependencies.
+
+    Versions of `wasm-bindgen` in Cargo.toml:
+
+      Expected: '= ${pkgs.wasm-bindgen-cli.version}'
+      Actual:   '${cargoToml.dependencies.wasm-bindgen}'
+  ''); pkgs.wasm-bindgen-cli;
+
+  deps = with pkgs; [
+    binaryen
+    sass
+    wasmRust
+    trunk
+    assertWasmBindgen
+  ];
+
+in
+pkgs.rustPlatform.buildRustPackage rec {
+  pname = "predlozhnik";
+  version = "canon";
+  src = lib.cleanSource ./.;
+  cargoLock.lockFile = ./Cargo.lock;
+
+  buildPhase = ''
+    export PATH=${lib.makeBinPath deps}:$PATH
+    mkdir home
+    export HOME=$PWD/.home
+    env
+    trunk build --release -d $out
+  '';
+
+  dontInstall = true;
+}
diff --git a/corp/russian/predlozhnik/index.css b/corp/russian/predlozhnik/index.css
new file mode 100644
index 0000000000..3529574c4f
--- /dev/null
+++ b/corp/russian/predlozhnik/index.css
@@ -0,0 +1,29 @@
+body {
+    max-width: 800px;
+    margin: 40px auto;
+}
+
+#header {
+    display: flex;
+    flex-direction: column;
+}
+
+.btn.btn-ghost:disabled {
+    border-color: #9f9f9f;
+    color: #9f9f9f;
+}
+
+#predlogi,#padezhi {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: wrap;
+}
+
+.btn {
+    margin: 3px;
+    flex-grow: 1;
+}
+
+.footer {
+    text-align: right;
+}
diff --git a/corp/russian/predlozhnik/index.html b/corp/russian/predlozhnik/index.html
new file mode 100644
index 0000000000..6af1adc0bf
--- /dev/null
+++ b/corp/russian/predlozhnik/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet"
+          href="https://unpkg.com/terminal.css@0.7.2/dist/terminal.min.css" />
+    <link data-trunk rel="inline" href="index.css">
+    <title>ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½ΠΈΠΊ</title>
+
+    <!-- Yandex.RTB -->
+    <script>window.yaContextCb=window.yaContextCb||[]</script>
+    <script src="https://yandex.ru/ads/system/context.js" async></script>
+  </head>
+  <body>
+    <noscript>
+      <h1>ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½ΠΈΠΊ</h1>
+      <p>
+        ... ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ с ΠΊΠ°ΠΊΠΈΠΌΠΈ ΠΏΠ°Π΄Π΅ΠΆΠ°ΠΌΠΈ ΡƒΠΏΠΎΡ‚Ρ€Π΅Π±Π»ΡΡŽΡ‚ΡΡ ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ Π²
+        русском языкС. Но, ΠΊ соТалСнию, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Javascript.
+      </p>
+    </noscript>
+  </body>
+</html>
diff --git a/corp/russian/predlozhnik/src/main.rs b/corp/russian/predlozhnik/src/main.rs
new file mode 100644
index 0000000000..56ff04808f
--- /dev/null
+++ b/corp/russian/predlozhnik/src/main.rs
@@ -0,0 +1,345 @@
+use yew::html::Scope;
+use yew::prelude::*;
+
+use lazy_static::lazy_static;
+use maplit::hashmap;
+use std::collections::BTreeSet;
+use std::collections::HashMap;
+
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
+enum ПадСТ {
+    Π˜ΠΌΠ΅Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+    Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+    Π”Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+    Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+    Π’Π²ΠΎΡ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+    ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½Ρ‹ΠΉ,
+}
+
+impl ПадСТ {
+    const Π’Π‘Π•: [Self; 6] = [
+        Self::Π˜ΠΌΠ΅Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+        Self::Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+        Self::Π”Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+        Self::Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+        Self::Π’Π²ΠΎΡ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ,
+        Self::ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½Ρ‹ΠΉ,
+    ];
+
+    fn вопрос(&self) -> &str {
+        use ПадСТ::*;
+        match self {
+            Π˜ΠΌΠ΅Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ => "ΠΊΡ‚ΠΎ? Π§Ρ‚ΠΎ?",
+            Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ => "ΠΊΠΎΠ³ΠΎ? Π§Π΅Π³ΠΎ?",
+            Π”Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ => "ΠΊΠΎΠΌΡƒ? Π§Π΅ΠΌΡƒ?",
+            Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ => "ΠΊΠΎΠ³ΠΎ? Π§Ρ‚ΠΎ?",
+            Π’Π²ΠΎΡ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ => "ΠΊΠ΅ΠΌ? Π§Π΅ΠΌ?",
+            ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½Ρ‹ΠΉ => "ΠΊΠΎΠΌ? Π§Ρ‘ΠΌ?",
+        }
+    }
+}
+
+lazy_static! {
+    static ref ПО_ΠŸΠ Π•Π”Π›ΠžΠ“Π£: HashMap<&'static str, BTreeSet<ПадСТ>> = {
+        use ПадСТ::*;
+
+        hashmap! {
+            "Π±Π΅Π·" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π±Π»ΠΈΠ·" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π²" => BTreeSet::from([Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½Ρ‹ΠΉ]),
+            "вмСсто" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π²Π½Π΅" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π²Π½ΡƒΡ‚Ρ€ΠΈ" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π²ΠΎΠ·Π»Π΅" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π²ΠΎΠΊΡ€ΡƒΠ³" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π²Ρ€ΠΎΠ΄Π΅" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "для" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π΄ΠΎ" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π·Π°" => BTreeSet::from([Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, Π’Π²ΠΎΡ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΈΠ·" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΈΠ·-Π·Π°" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΈΠ·-ΠΏΠΎΠ΄" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΊ" => BTreeSet::from([Π”Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΊΡ€ΠΎΠΌΠ΅" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΌΠ΅ΠΆΠ΄Ρƒ" => BTreeSet::from([Π’Π²ΠΎΡ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π½Π°" => BTreeSet::from([Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½Ρ‹ΠΉ]),
+            "Π½Π°Π΄" => BTreeSet::from([Π’Π²ΠΎΡ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Π½Π΅Ρ‚" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΎ" => BTreeSet::from([Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½Ρ‹ΠΉ]),
+            "ΠΎΠΊΠΎΠ»ΠΎ" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΎΡ‚" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΏΠ΅Ρ€Π΅Π΄" => BTreeSet::from([Π’Π²ΠΎΡ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΏΠΎ" => BTreeSet::from([Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, Π”Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½Ρ‹ΠΉ]),
+            "ΠΏΠΎΠ΄" => BTreeSet::from([Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, Π’Π²ΠΎΡ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "послС" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "ΠΏΡ€ΠΈ" => BTreeSet::from([ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½Ρ‹ΠΉ]),
+            "ΠΏΡ€ΠΎ" => BTreeSet::from([Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Ρ€Π°Π΄ΠΈ" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "с" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ, Π’Π²ΠΎΡ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "сквозь" => BTreeSet::from([Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "срСди" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Ρƒ" => BTreeSet::from([Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+            "Ρ‡Π΅Ρ€Π΅Π·" => BTreeSet::from([Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ]),
+        }
+    };
+    static ref ПО_ΠŸΠΠ”Π•Π–Π£: HashMap<ПадСТ, BTreeSet<&'static str>> = {
+        let mut m = hashmap!();
+
+        for c in ПадСТ::Π’Π‘Π• {
+            let mut ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ: BTreeSet<&'static str> = BTreeSet::new();
+            for (k, v) in &*ПО_ΠŸΠ Π•Π”Π›ΠžΠ“Π£ {
+                if v.contains(&c) {
+                    ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ.insert(k);
+                }
+            }
+
+            m.insert(c, ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ);
+        }
+
+        m
+    };
+    static ref ΠŸΠΠ”Π•Π–Π˜: BTreeSet<ПадСТ> = BTreeSet::from(ПадСТ::Π’Π‘Π•);
+    static ref ΠŸΠ Π•Π”Π›ΠžΠ“Π˜: BTreeSet<&'static str> = {
+        let mut s: BTreeSet<&'static str> = BTreeSet::new();
+
+        for ΠΏ in ПО_ΠŸΠ Π•Π”Π›ΠžΠ“Π£.keys() {
+            s.insert(ΠΏ);
+        }
+
+        s
+    };
+}
+
+fn ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅(ΠΏΡ€Π΅Π΄Π»ΠΎΠ³: &str, ΠΏΠ°Π΄Π΅ΠΆ: ПадСТ) -> Option<Html> {
+    use ПадСТ::*;
+
+    match (ΠΏΡ€Π΅Π΄Π»ΠΎΠ³, ΠΏΠ°Π΄Π΅ΠΆ) {
+        ("Π²", Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ) => Some(html! {"Π’ΠΎ Ρ‡Ρ‚ΠΎ? Π’ ΠΊΠΎΠ³ΠΎ?"}),
+
+        ("ΠΎ", Π’ΠΈΠ½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ) => Some(html! {
+            <>
+              <p>{"О ΠΊΠΎΠ³ΠΎ? Обо Ρ‡Ρ‚ΠΎ?"}</p>
+              <p>{"Π Π΅Π΄ΠΊΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ. НапримСр:"}</p>
+              <ul>
+                <li>{"Π£Π΄Π°Ρ€ΠΈΡ‚ΡŒΡΡ ΠΎ ΠΏΡ€ΠΈΡ‚ΠΎΠ»ΠΎΠΊΡƒ."}</li>
+                <li>{"Ρ‚ΠΎΡ‡ΠΈΡ‚ΡŒ ΠΎ камСнь."}</li>
+              </ul>
+            </>
+        }),
+
+        ("ΠΌΠ΅ΠΆΠ΄Ρƒ", Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ) => Some(html! {
+            <>
+              <p>{"ΠœΠ΅ΠΆΠ΄Ρƒ Ρ‡Π΅Π³ΠΎ?"}</p>
+              <p>{"Π Π΅Π΄ΠΊΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ. Волько Π² ΠΈΠ΄ΠΈΠΎΠΌΠ°Ρ… ΠΈ старой Π»ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΡƒΡ€Π΅:"}</p>
+              <ul>
+                <li>{"Π§ΠΈΡ‚Π°ΡŽ ΠΌΠ΅ΠΆΠ΄Ρƒ строк."}</li>
+              </ul>
+            </>
+        }),
+
+        _ => None,
+    }
+}
+
+enum Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ {
+    Π’Ρ‹Π±Ρ€Π°Π»ΠŸΠ°Π΄Π΅ΠΆ(Option<ПадСТ>),
+    Π’Ρ‹Π±Ρ€Π°Π»ΠŸΡ€Π΅Π΄Π»ΠΎΠ³(Option<&'static str>),
+}
+
+#[derive(Default)]
+struct МодСль {
+    падСТ: Option<ПадСТ>,
+    ΠΏΡ€Π΅Π΄Π»ΠΎΠ³: Option<&'static str>,
+}
+
+struct Π’Ρ‹Π²ΠΎΠ΄ {
+    доступныС_ΠΏΠ°Π΄Π΅ΠΆΠΈ: BTreeSet<ПадСТ>,
+    доступныС_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ: BTreeSet<&'static str>,
+    объяснСниС: Option<Html>,
+}
+
+fn объясни(ΠΏΠ°Π΄Π΅ΠΆ: ПадСТ, ΠΏΡ€Π΅Π΄Π»ΠΎΠ³: &str) -> Html {
+    let иск = match ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅(ΠΏΡ€Π΅Π΄Π»ΠΎΠ³, ΠΏΠ°Π΄Π΅ΠΆ) {
+        Some(exp) => html! { exp },
+        None => html! { format!("{} {}", ΠΏΡ€Π΅Π΄Π»ΠΎΠ³, ΠΏΠ°Π΄Π΅ΠΆ.вопрос()) },
+    };
+
+    html! {
+        <div id="obyasnenie">
+          <hr/>
+          <h2>{"ΠŸΡ€ΠΈΠΌΠ΅Ρ€:"}</h2>
+          {иск}
+        </div>
+    }
+}
+
+fn ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚ΡŒ(ΠΌ: &МодСль) -> Π’Ρ‹Π²ΠΎΠ΄ {
+    match (ΠΌ.ΠΏΠ°Π΄Π΅ΠΆ, &ΠΌ.ΠΏΡ€Π΅Π΄Π»ΠΎΠ³) {
+        (Some(ΠΏΠΆ), Some(ΠΏΠ»)) => Π’Ρ‹Π²ΠΎΠ΄ {
+            доступныС_ΠΏΠ°Π΄Π΅ΠΆΠΈ: (*ПО_ΠŸΠ Π•Π”Π›ΠžΠ“Π£)[ΠΏΠ»].clone(),
+            доступныС_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ: (*ПО_ΠŸΠΠ”Π•Π–Π£)[&ΠΏΠΆ].clone(),
+            объяснСниС: Some(объясни(пТ, пл)),
+        },
+
+        (Some(ΠΏΠΆ), None) => Π’Ρ‹Π²ΠΎΠ΄ {
+            доступныС_ΠΏΠ°Π΄Π΅ΠΆΠΈ: BTreeSet::from([ΠΏΠΆ]),
+            доступныС_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ: (*ПО_ΠŸΠΠ”Π•Π–Π£)[&ΠΏΠΆ].clone(),
+            объяснСниС: None,
+        },
+
+        (None, Some(ΠΏΠ»)) => Π’Ρ‹Π²ΠΎΠ΄ {
+            доступныС_ΠΏΠ°Π΄Π΅ΠΆΠΈ: (*ПО_ΠŸΠ Π•Π”Π›ΠžΠ“Π£)[ΠΏΠ»].clone(),
+            доступныС_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ: BTreeSet::from([*ΠΏΠ»]),
+            объяснСниС: None,
+        },
+
+        (None, None) => Π’Ρ‹Π²ΠΎΠ΄ {
+            доступныС_ΠΏΠ°Π΄Π΅ΠΆΠΈ: ΠŸΠΠ”Π•Π–Π˜.clone(),
+            доступныС_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ: ΠŸΠ Π•Π”Π›ΠžΠ“Π˜.clone(),
+            объяснСниС: None,
+        },
+    }
+}
+
+fn класс_ΠΊΠ½ΠΎΠΏΠΊΠΈ(Π²Ρ‹Π±Ρ€Π°Π½: bool, доступСн: bool) -> String {
+    let класс = "btn ".to_string();
+    класс
+        + match (Π²Ρ‹Π±Ρ€Π°Π½, доступСн) {
+            (true, _) => "btn-primary",
+            (false, true) => "btn-ghost btn-primary",
+            (false, false) => "btn-ghost btn-default",
+        }
+}
+
+fn ΠΏΠΎΠΊΠ°ΠΆΠΈ_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³(
+    link: &Scope<МодСль>,
+    м: &МодСль,
+    Π²Π²: &Π’Ρ‹Π²ΠΎΠ΄,
+    ΠΏ: &'static str,
+) -> Html {
+    let Π²Ρ‹Π±Ρ€Π°Π½ = ΠΌ.ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ == Some(ΠΏ);
+    let доступСн = Π²Π².доступныС_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ.contains(ΠΏ);
+    let класс = класс_ΠΊΠ½ΠΎΠΏΠΊΠΈ(Π²Ρ‹Π±Ρ€Π°Π½, доступСн);
+
+    html! {
+        <button class={класс}
+         onclick={link.callback(move |_| if Π²Ρ‹Π±Ρ€Π°Π½ {
+             Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅::Π’Ρ‹Π±Ρ€Π°Π»ΠŸΡ€Π΅Π΄Π»ΠΎΠ³(None)
+         } else {
+             Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅::Π’Ρ‹Π±Ρ€Π°Π»ΠŸΡ€Π΅Π΄Π»ΠΎΠ³(Some(ΠΏ))
+         })}
+         disabled={!доступСн}>
+        {ΠΏ}
+        </button>
+    }
+}
+
+fn ΠΏΠΎΠΊΠ°ΠΆΠΈ_ΠΏΠ°Π΄Π΅ΠΆ(
+    link: &Scope<МодСль>, ΠΌ: &МодСль, Π²Π²: &Π’Ρ‹Π²ΠΎΠ΄, ΠΏ: ПадСТ
+) -> Html {
+    let Π²Ρ‹Π±Ρ€Π°Π½ = ΠΌ.ΠΏΠ°Π΄Π΅ΠΆ == Some(ΠΏ);
+    let доступСн = Π²Π².доступныС_ΠΏΠ°Π΄Π΅ΠΆΠΈ.contains(&ΠΏ);
+    let класс = класс_ΠΊΠ½ΠΎΠΏΠΊΠΈ(Π²Ρ‹Π±Ρ€Π°Π½, доступСн);
+
+    html! {
+        <button class={класс}
+         onclick={link.callback(move |_| if Π²Ρ‹Π±Ρ€Π°Π½ {
+             Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅::Π’Ρ‹Π±Ρ€Π°Π»ΠŸΠ°Π΄Π΅ΠΆ(None)
+         } else {
+             Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅::Π’Ρ‹Π±Ρ€Π°Π»ΠŸΠ°Π΄Π΅ΠΆ(Some(ΠΏ))
+         })}
+         disabled={!доступСн}>
+        {format!("{:?}", ΠΏ)}
+        </button>
+    }
+}
+
+impl Component for МодСль {
+    type Message = Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅;
+    type Properties = ();
+
+    fn create(_ctx: &Context<Self>) -> Self {
+        Default::default()
+    }
+
+    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
+        match msg {
+            Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅::Π’Ρ‹Π±Ρ€Π°Π»ΠŸΠ°Π΄Π΅ΠΆ(ΠΏΠΆ) => self.ΠΏΠ°Π΄Π΅ΠΆ = ΠΏΠΆ,
+            Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅::Π’Ρ‹Π±Ρ€Π°Π»ΠŸΡ€Π΅Π΄Π»ΠΎΠ³(ΠΏΠ») => self.ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ = ΠΏΠ»,
+        }
+
+        true
+    }
+
+    fn view(&self, ctx: &Context<Self>) -> Html {
+        let Π²Π² = ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚ΡŒ(self);
+        let link = ctx.link();
+
+        let ΠΊΠ½ΠΎΠΏΠΊΠΈ_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΎΠ² = ΠŸΠ Π•Π”Π›ΠžΠ“Π˜
+            .iter()
+            .map(|ΠΏ| ΠΏΠΎΠΊΠ°ΠΆΠΈ_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³(link, self, &Π²Π², ΠΏ))
+            .collect::<Html>();
+
+        let ΠΊΠ½ΠΎΠΏΠΊΠΈ_ΠΏΠ°Π΄Π΅ΠΆΠΎΠ² = ΠŸΠΠ”Π•Π–Π˜
+            .iter()
+            .map(|ΠΏ| ΠΏΠΎΠΊΠ°ΠΆΠΈ_ΠΏΠ°Π΄Π΅ΠΆ(link, self, &Π²Π², *ΠΏ))
+            .collect::<Html>();
+
+        let объяснСниС = вв.объяснСниС.map(|exp| exp).unwrap_or_else(|| html! {});
+
+        let footer = html! {
+            <footer>
+              <hr/>
+              <p class="footer">
+                <a href="https://code.tvl.fyi/tree/corp/russian/predlozhnik">{"ΠΊΠΎΠ΄"}</a>
+                {" | "}
+                {"сдСлано "}<a href="https://tvl.su">{"ООО \"Π’Π’Π›\""}</a>
+              </p>
+            </footer>
+        };
+
+        let ΠΊΠΎΠ΄_Ρ€Π΅ΠΊΠ»Π°ΠΌΡ‹ = r#"
+window.yaContextCb.push(()=>{
+  Ya.Context.AdvManager.render({
+    renderTo: 'yandex_rtb_R-A-1773485-1',
+    blockId: 'R-A-1773485-1'
+  })
+})
+"#;
+
+        let Ρ€Π΅ΠΊΠ»Π°ΠΌΠ° = html! {
+            <div id="ad">
+              <div id="yandex_rtb_R-A-1773485-1"></div>
+              <script>{ΠΊΠΎΠ΄_Ρ€Π΅ΠΊΠ»Π°ΠΌΡ‹}</script>
+            </div>
+        };
+
+        html! {
+            <>
+                <div id="header">
+                  <h1>{"ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½ΠΈΠΊ"}</h1>
+                  <p>{"... ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ с ΠΊΠ°ΠΊΠΈΠΌΠΈ ΠΏΠ°Π΄Π΅ΠΆΠ°ΠΌΠΈ ΡƒΠΏΠΎΡ‚Ρ€Π΅Π±Π»ΡΡŽΡ‚ΡΡ ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΈ Π² русском языкС."}</p>
+                </div>
+
+                <h2>{"Π’Ρ‹Π±ΠΈΡ€Π°ΠΉ ΠΏΡ€Π΅Π΄Π»ΠΎΠ³:"}</h2>
+                <div id="predlogi">
+                  {ΠΊΠ½ΠΎΠΏΠΊΠΈ_ΠΏΡ€Π΅Π΄Π»ΠΎΠ³ΠΎΠ²}
+                </div>
+                <hr/>
+
+                <h2>{"Π’Ρ‹Π±ΠΈΡ€Π°ΠΉ ΠΏΠ°Π΄Π΅ΠΆ:"}</h2>
+                <div id="padezhi">
+                  {ΠΊΠ½ΠΎΠΏΠΊΠΈ_ΠΏΠ°Π΄Π΅ΠΆΠΎΠ²}
+                </div>
+
+                {объяснСниС}
+                {footer}
+                {Ρ€Π΅ΠΊΠ»Π°ΠΌΠ°}
+            </>
+        }
+    }
+}
+
+fn main() {
+    yew::start_app::<МодСль>();
+}
diff --git a/corp/website/content-en.md b/corp/website/content-en.md
new file mode 100644
index 0000000000..3f60ddb236
--- /dev/null
+++ b/corp/website/content-en.md
@@ -0,0 +1,94 @@
+<p class="lang-links" style="text-align: right;">
+    <a href="/ru">ru</a> | <span class="active-lang">en</span>
+</p>
+<img class="tvl-logo" src="https://static.tvl.su/latest/logo-animated.svg"
+     alt="Virus with lambda-shaped spike proteins sitting on an armchair">
+
+----------------
+
+Welcome to the website of TVL LLC, the corporate face of the [**TVL**][tvl] Community.
+We are a technology company headquartered in Moscow, working with a variety of topics:
+
+* <details><summary><b>Monorepos</b>. Effective ways for an organisation to
+  structure their internal codebase in a single repository, unify tooling across
+  languages, and reduce <a
+  href="https://en.wikipedia.org/wiki/No_Silver_Bullet#Summary">accidental
+  complexity</a> in software development.</summary>
+
+  With experience from companies like Google, Spotify and DeepMind, we help
+  organisations of different sizes to find streamlined software development
+  workflows that reduce mental load and increase code quality.
+
+  We use our own monorepo solutions in our internal software development flows,
+  and all of this is visible in our [public monorepo][depot].
+  </details>
+* <details><summary><b>Nix</b>. We believe that functional and declarative
+  computer systems are a massive and as-of-yet underrated step forward for
+  computing, and that Nix is the most promising solution for this
+  purpose.</summary>
+
+  Nix allows companies to significantly improve in areas such as:
+
+  1. Unification of development and production environments, leading to fewer
+     surprises when deploying an application.
+
+  2. Tailoring their stack to their use-case. Avoid the complexity of running
+     something like Kubernetes while you are scaling up, but *also* avoid the
+     complexity of rewriting your infrastructure stack once you need it.
+
+  3. Unified developer tooling across different programming languages, without
+     the overhead of using something like Bazel.
+
+  Its radically new model can bring many other advantages which depend
+  on the exact use-cases.
+  </details>
+
+* **Software development**. We offer a wide range of software development
+  services. Whether you need existence with existing projects, or want to create
+  a new solution from scratch, we can help. We specialize in helping
+  organizations avoid the trap of building overly complex systems that don't
+  meet their needs.
+
+* **Site Reliability Engineering (SRE)**. We can help with many infrastructure
+  concerns, such as deployment, scaling, monitoring, troubleshooting analysing
+  failure points in existing solutions. We offer this for any Linux-based
+  technology stack.
+
+--------------
+
+We support open-source software development, and prefer to work on our projects
+in the open. Some of our projects are:
+
+* The public TVL [monorepo][depot], the **depot**, is a demonstration of the
+  monorepo tooling we have been working on for the last couple of years.
+
+  It contains many open-source projects, work by lots of international
+  open-source contributors, and all public code of the company.
+
+* [**Tvix**][tvix], a new implementation of Nix that is fully compatible with
+  existing Nix code. Architectural differences between Nix and Tvix allow us to
+  develop tooling that is better tailored to collaborative software development,
+  and to develop domestic, high-quality solutions for CI/CD.
+
+  We run a demonstration of some parts of Tvix online as [tvixbolt].
+
+* [**Nixery**][nixery] is a service that lets users easily build and deploy
+  ad-hoc container images from their software build definitions.
+
+* Out of personal interest, we also develop free tools that help with learning
+  the Russian language, such as [**ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½ΠΈΠΊ**][predlozhnik].
+
+--------------
+
+Reach out to us at **contact@tvl.su** if you are interested in working with us.
+
+TVL originated as an international community of software developers that wanted
+to socialise and collaborate on projects. Many people from all over the world
+contribute to our open-source software projects. Check out the [TVL community
+website][tvl] for more information.
+
+[tvl]: https://tvl.fyi
+[tvix]: https://tvl.fyi/blog/rewriting-nix
+[nixery]: https://nixery.dev
+[predlozhnik]: https://predlozhnik.ru/
+[depot]: https://cs.tvl.fyi/depot
diff --git a/corp/website/content-ru.md b/corp/website/content-ru.md
new file mode 100644
index 0000000000..637f366a1f
--- /dev/null
+++ b/corp/website/content-ru.md
@@ -0,0 +1,98 @@
+<p class="lang-links" style="text-align: right;">
+    <span class="active-lang">ru</span> | <a href="/en">en</a>
+</p>
+<img class="tvl-logo" src="https://static.tvl.su/latest/logo-animated.svg"
+     alt="Вирус со спайк-Π±Π΅Π»ΠΊΠ°ΠΌΠΈ Π² Ρ„ΠΎΡ€ΠΌΠ΅ лямбды, сидящий Π½Π° Π΄ΠΈΠ²Π°Π½Π΅">
+
+----------------
+
+Π”ΠΎΠ±Ρ€ΠΎ ΠΏΠΎΠΆΠ°Π»ΠΎΠ²Π°Ρ‚ΡŒ Π½Π° ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ сайт Π’Π’Π›. ΠœΡ‹ - тСхнологичСская компания Π²
+МосквС, Π·Π°Π½ΠΈΠΌΠ°ΡŽΡ‰Π°ΡΡΡ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌΠΈ направлСниями:
+
+* <details><summary><b>ΠœΠΎΠ½ΠΎΡ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ</b>. Набор эффСктивных способов ΠΏΠΎ
+  ΡΡ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½Π΅ΠΉ ΠΊΠΎΠ΄ΠΎΠ²ΠΎΠΉ Π±Π°Π·Ρ‹ Π² Π΅Π΄ΠΈΠ½ΠΎΠΌ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ, ΡƒΠ½ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ
+  инструмСнтов Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹ΠΌΠΈ языками, Π° Ρ‚Π°ΠΊΠΆΠ΅ сниТСния <a
+  href="https://ru.wikipedia.org/wiki/%D0%A1%D0%B5%D1%80%D0%B5%D0%B1%D1%80%D1%8F%D0%BD%D0%BE%D0%B9_%D0%BF%D1%83%D0%BB%D0%B8_%D0%BD%D0%B5%D1%82#%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5">ΠΏΠΎΠ±ΠΎΡ‡Π½Ρ‹x
+  слоТностСй</a> Π² Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ ПО.</summary>
+
+  Наш ΠΎΠΏΡ‹Ρ‚ Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Ρ‚Π°ΠΊΠΈΠΌΠΈ компаниями, ΠΊΠ°ΠΊ Google, Spotify ΠΈ DeepMind, позволяСт
+  Π½Π°ΠΌ ΠΏΠΎΠΌΠΎΡ‡ΡŒ организациям любого Ρ€Π°Π·ΠΌΠ΅Ρ€Π° ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ процСссы Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ПО,
+  ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΡ‚ΡŒ Π½Π°Π³Ρ€ΡƒΠ·ΠΊΡƒ Π½Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² ΠΈ ΡƒΠ»ΡƒΡ‡ΡˆΠΈΡ‚ΡŒ качСство ΠΊΠΎΠ΄Π°.
+
+  ΠœΡ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ собствСнныС ΠΌΠΎΠ½ΠΎΡ€Π΅ΠΏΠΎ-Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π²ΠΎ Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½ΠΈΡ… процСссах Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ
+  ПО. Всё это ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ Π² нашСм [ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠΌ ΠΌΠΎΠ½ΠΎΡ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ][depot].
+  </details>
+* <details><summary><b>Nix</b>. ΠœΡ‹ считаСм, Ρ‡Ρ‚ΠΎ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½Ρ‹Π΅ ΠΈ Π΄Π΅ΠΊΠ»Π°Ρ€Π°Ρ‚ΠΈΠ²Π½Ρ‹Π΅
+  ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π½Ρ‹Π΅ систСмы ΡΠ²Π»ΡΡŽΡ‚ΡΡ Π²Π°ΠΆΠ½Ρ‹ΠΌ ΠΈ ΠΏΠΎΠΊΠ° Π΅Ρ‰Ρ‘ Π½Π΅Π΄ΠΎΠΎΡ†Π΅Π½Π΅Π½Π½Ρ‹ΠΌ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΎΠΌ ΠΊ
+  вычислСниям, ΠΈ <a href="https://nixos.org">Nix</a> являСтся Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅
+  пСрспСктивным Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ΠΌ Π² этом Π½Π°ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠΈ.</summary>
+
+  Nix позволяСт компаниям Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΡƒΠ»ΡƒΡ‡ΡˆΠΈΡ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ области:
+
+  1. Унификация срСд Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΈ производства, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΈΠ²ΠΎΠ΄ΠΈΡ‚ ΠΊ ΠΌΠ΅Π½ΡŒΡˆΠ΅ΠΌΡƒ
+     количСству ΡΡŽΡ€ΠΏΡ€ΠΈΠ·ΠΎΠ² ΠΏΡ€ΠΈ Ρ€Π°Π·Π²Π΅Ρ€Ρ‚Ρ‹Π²Π°Π½ΠΈΠΈ прилоТСния.
+
+  2. Настройка стСка ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ ΠΏΠΎΠ΄ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹Π΅ Π·Π°Π΄Π°Ρ‡ΠΈ. Π˜Π·Π±Π΅Π³Π°ΠΉΡ‚Π΅ слоТности
+     инструмСнтов Π²Ρ€ΠΎΠ΄Π΅ Kubernetes ΠΏΡ€ΠΈ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠΈ, Π½ΠΎ Ρ‚Π°ΠΊΠΆΠ΅ ΠΈΠ·Π±Π΅Π³Π°ΠΉΡ‚Π΅
+     измСнСния всСй инфраструктуры ΠΏΠΎ малСйшСй нСобходимости.
+
+  3. Π•Π΄ΠΈΠ½Ρ‹ΠΉ Π½Π°Π±ΠΎΡ€ инструмСнтов Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ для Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Ρ… языков программирования,
+     Π±Π΅Π· слоТностСй, связанных с использованиСм Bazel ΠΈ ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Ρ….
+
+  Nix ΠΏΡ€Π΅Π»Π°Π³Π°Π΅Ρ‚ Ρ€Π°Π΄ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎ Π½ΠΎΠ²ΡƒΡŽ модСль, которая ΠΌΠΎΠΆΠ΅Ρ‚ принСсти мноТСство
+  прСимущСств для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ способа использования.
+  </details>
+
+* **Π Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΠΎΠ³ΠΎ обСспСчСния**. ΠœΡ‹ ΠΏΡ€Π΅Π΄Π»Π°Π³Π°Π΅ΠΌ ΡˆΠΈΡ€ΠΎΠΊΠΈΠΉ спСктр услуг ΠΏΠΎ
+  Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ ПО. Π’Π½Π΅ зависимости ΠΎΡ‚ Ρ‚ΠΎΠ³ΠΎ, Π½ΡƒΠΆΠ΄Π°Π΅Ρ‚Π΅ΡΡŒ Π²Ρ‹ Π² ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ…
+  ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ² ΠΈΠ»ΠΈ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²ΠΎΠ΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ с нуля, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²Π°ΠΌ ΠΏΠΎΠΌΠΎΡ‡ΡŒ. Наша
+  Π·Π°Π΄Π°Ρ‡Π° - ΠΏΠΎΠΌΠΎΡ‡ΡŒ компаниям ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ Π»ΠΎΠ²ΡƒΡˆΠΊΠΈ излишнС слоТных систСм, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½Π΅
+  ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‚ ΠΈΡ… потрСбностям.
+
+* **Site Reliability Engineering (SRE)**. ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠΌΠΎΡ‡ΡŒ с мноТСством
+  инфраструктурных ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ, Ρ‚Π°ΠΊΠΈΡ… ΠΊΠ°ΠΊ Ρ€Π°Π·Π²Π΅Ρ€Ρ‚Ρ‹Π²Π°Π½ΠΈΠ΅, ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅,
+  ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³, Π°Π½Π°Π»ΠΈΠ· ΠΈ устранСниС Π½Π΅ΠΏΠΎΠ»Π°Π΄ΠΎΠΊ Π² ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… Ρ€Π΅ΡˆΠ΅Π½ΠΈΡΡ…. ΠœΡ‹
+  Ρ€Π°Π±ΠΎΡ‚Π°Π΅ΠΌ с Π»ΡŽΠ±Ρ‹ΠΌΠΈ тСхнологичСскими стСками Π½Π° Π±Π°Π·Π΅ Linux.
+
+--------------
+
+ΠœΡ‹ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΠΎΠ³ΠΎ обСспСчСния с ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΌ исходным ΠΊΠΎΠ΄ΠΎΠΌ ΠΈ
+ΠΏΡ€Π΅Π΄ΠΏΠΎΡ‡ΠΈΡ‚Π°Π΅ΠΌ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π½Π°Π΄ нашими ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°ΠΌΠΈ Π² ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΎΠΌ доступС. НСкоторыС ΠΈΠ·
+Π½Π°ΡˆΠΈΡ… ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ²:
+
+* ΠŸΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ Π’Π’Π›, [**depot**][depot], являСтся дСмонстрациСй
+  инструмСнтов ΠΌΠΎΠ½ΠΎΡ€Π΅ΠΏΠΎ, Π½Π°Π΄ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌΠΈ ΠΌΡ‹ Ρ€Π°Π±ΠΎΡ‚Π°Π΅ΠΌ Π² Ρ‚Π΅Ρ‡Π΅Π½ΠΈΠ΅ послСдних Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ…
+  Π»Π΅Ρ‚.
+
+  Π’ Π½Π΅ΠΌ содСрТится мноТСство ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ² c ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΌ исходным ΠΊΠΎΠ΄ΠΎΠΌ, Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚
+  Ρ€Π°Π±ΠΎΡ‚Ρ‹ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² ΠΈΠ· Ρ€Π°Π·Π½Ρ‹Ρ… стран, Π° Ρ‚Π°ΠΊΠΆΠ΅ вСсь ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ.
+
+* [**Tvix**][tvix] - это новая рСализация Nix, которая ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ совмСстима с
+  ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΌ ΠΊΠΎΠ΄ΠΎΠΌ Nix. АрхитСктурныС различия ΠΌΠ΅ΠΆΠ΄Ρƒ Nix ΠΈ Tvix ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ Π½Π°ΠΌ
+  Ρ€Π°Π·Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ инструмСнты, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π»ΡƒΡ‡ΡˆΠ΅ подходят для совмСстной Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ
+  ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΠΎΠ³ΠΎ обСспСчСния, ΠΈ Ρ€Π°Π·Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ отСчСствСнныС качСствСнныС Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ
+  для CI/CD.
+
+  ΠœΡ‹ запускаСм Π΄Π΅ΠΌΠΎΠ½ΡΡ‚Ρ€Π°Ρ†ΠΈΡŽ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² Tvix ΠΎΠ½Π»Π°ΠΉΠ½: [tvixbolt][].
+
+* [Nixery][nixery] - это сСрвис, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ позволяСт ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌ с Π»Π΅Π³ΠΊΠΎΡΡ‚ΡŒΡŽ
+  ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ ΠΈ Ρ€Π°Π·Π²Ρ‘Ρ€Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ ΠΎΠ±Ρ€Π°Π·Ρ‹ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΎΠ² Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ ΠΈΠ· ΠΈΡ… инструкций сборки
+  софта.
+
+* Из Π»ΠΈΡ‡Π½Ρ‹Ρ… интСрСсов, ΠΌΡ‹ Ρ‚Π°ΠΊΠΆΠ΅ Ρ€Π°Π·Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌ бСсплатныС инструмСнты, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅
+  ΠΏΠΎΠΌΠΎΠ³Π°ΡŽΡ‚ Π² ΠΈΠ·ΡƒΡ‡Π΅Π½ΠΈΠΈ русского языка, Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ [**ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ½ΠΈΠΊ**][predlozhnik].
+
+Π‘Π²ΡΠΆΠΈΡ‚Π΅ΡΡŒ с Π½Π°ΠΌΠΈ ΠΏΠΎ адрСсу **contact@tvl.su**, Ссли Π²Ρ‹ заинтСрСсованы Π²
+сотрудничСствС с Π½Π°ΠΌΠΈ.
+
+Π’Π’Π› Π²ΠΎΠ·Π½ΠΈΠΊΠ»Π° ΠΊΠ°ΠΊ ΠΌΠ΅ΠΆΠ΄ΡƒΠ½Π°Ρ€ΠΎΠ΄Π½ΠΎΠ΅ сообщСство программистов для общСния ΠΈ совмСстной
+Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π½Π°Π΄ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°ΠΌΠΈ. Π›ΡŽΠ΄ΠΈ ΠΈΠ· Ρ€Π°Π·Π½Ρ‹Ρ… стран вносят свой Π²ΠΊΠ»Π°Π΄ Π² наши ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹Π΅
+ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρ‹ ΠΏΠΎ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΠΎΠ³ΠΎ обСспСчСния. ΠŸΠΎΡΠ΅Ρ‚ΠΈΡ‚Π΅ [Π²Π΅Π±-сайт сообщСства
+TVL][tvl], Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ·Π½Π°Ρ‚ΡŒ большС.
+
+[tvl]: https://tvl.fyi
+[tvix]: https://tvl.fyi/blog/rewriting-nix
+[nixery]: https://nixery.dev
+[predlozhnik]: https://predlozhnik.ru/
+[depot]: https://cs.tvl.fyi/depot
+[tvixbolt]: https://bolt.tvix.dev/
diff --git a/corp/website/content.md b/corp/website/content.md
deleted file mode 100644
index f7ca9786fb..0000000000
--- a/corp/website/content.md
+++ /dev/null
@@ -1,26 +0,0 @@
-The Virus Lounge
-================
-
-----------------
-
-<img class="tvl-logo" src="https://static.tvl.su/latest/logo-animated.svg"
-     alt="Virus with lambda-shaped spike proteins sitting on an armchair">
-
-Welcome to the corporate face of [The Virus Lounge][tvl-fyi].
-
-We provide technology consulting around a variety of topics, for
-example:
-
-* Advice and setup of organisation-wide monorepos for effective
-  developer workflows, including associated tooling like CI/CD
-* Assistance with anything related to Nix/NixOS
-* Software development in various languages (Rust, Common Lisp,
-  Erlang, Java and more)
-
-We might be able to help you with other things on request.
-
-Note: We are still in the process of getting started and have limited
-capacity at the moment. If you would like our help, please reach out
-at **contact {at} tvl.su** for a discussion.
-
-[tvl-fyi]: https://tvl.fyi
diff --git a/corp/website/default.nix b/corp/website/default.nix
index 8740041d9b..a8ac132cb2 100644
--- a/corp/website/default.nix
+++ b/corp/website/default.nix
@@ -7,16 +7,15 @@ let
     "@context" = "https://schema.org";
     "@type" = "Organisation";
     url = "https://tvl.su";
-    logo = "https://static.tvl.fyi/${depot.web.static.drvHash}/logo-animated.svg";
+    logo = "https://static.tvl.fyi/latest/logo-animated.svg";
   };
-  index = depot.web.tvl.template {
-    title = "TVL (The Virus Lounge) - Software consulting";
-    content = builtins.readFile ./content.md;
+
+  common = description: {
     extraFooter = "\n|\n Β© ООО Π’Π’Π›";
+    staticUrl = "https://static.tvl.su/latest";
 
-    # TODO(tazjin): The `.tvl-logo` thing can probably go in the shared CSS.
     extraHead = ''
-      <meta name="description" content="TVL provides technology consulting for monorepos, Nix, and other SRE/DevOps/Software Engineering topics.">
+      <meta name="description" content="${description}">
       <script type="application/ld+json">
         ${builtins.toJSON structuredData}
       </script>
@@ -27,10 +26,34 @@ let
           margin-left: auto;
           margin-right: auto;
         }
+
+        .active-lang {
+          color: black;
+          font-weight: bold;
+        }
+
+        .inactive-lang {
+          color: inherit;
+        }
       </style>
     '';
   };
-in pkgs.runCommandNoCC "corp-website" {} ''
-  mkdir $out
-  cp ${index} $out/index.html
+
+  descEn = "TVL provides technology consulting for monorepos, Nix, and other SRE/DevOps/Software Engineering topics.";
+  indexEn = depot.web.tvl.template ({
+    title = "TVL (The Virus Lounge) - Software consulting";
+    content = builtins.readFile ./content-en.md;
+  } // common descEn);
+
+  descRu = "TVL прСдоставляСт тСхнологичСскоС ΠΊΠΎΠ½ΡΡƒΠ»ΡŒΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎ монорСпозиториям, Nix ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠΌ Ρ‚Π΅ΠΌΠ°ΠΌ SRE/DevOps/Software Engineering.";
+  indexRu = depot.web.tvl.template ({
+    title = "Π’Π’Π› - ΠœΠΎΠ½ΠΎΡ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ, SRE, Nix, ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΠΎΠ΅ обСспСчСниС";
+    content = builtins.readFile ./content-ru.md;
+  } // common descRu);
+in
+pkgs.runCommand "corp-website" { } ''
+  mkdir -p $out/{en,ru}
+  cp ${indexEn} $out/index.html
+  cp ${indexEn} $out/en/index.html
+  cp ${indexRu} $out/ru/index.html
 ''
diff --git a/default.nix b/default.nix
index 0ffda5b4d3..015fdd1ead 100644
--- a/default.nix
+++ b/default.nix
@@ -4,14 +4,18 @@
 
 { nixpkgsBisectPath ? null
 , parentTargetMap ? null
-, nixpkgsConfig ? {}, ... }@args:
+, nixpkgsConfig ? { }
+, localSystem ? builtins.currentSystem
+, crossSystem ? null
+, ...
+}@args:
 
 let
   inherit (builtins)
     filter
     ;
 
-  readTree = import ./nix/readTree {};
+  readTree = import ./nix/readTree { };
 
   # Disallow access to //users from other depot parts.
   usersFilter = readTree.restrictFolder {
@@ -59,18 +63,23 @@ let
     filter = parts: args: corpFilter parts (usersFilter parts args);
     scopedArgs = {
       __findFile = _: _: throw "Do not import from NIX_PATH in the depot!";
+      builtins = builtins // {
+        currentSystem = throw "Use localSystem from the readTree args instead of builtins.currentSystem!";
+      };
     };
   };
 
   # To determine build targets, we walk through the depot tree and
   # fetch attributes that were imported by readTree and are buildable.
   #
-  # Any build target that contains `meta.ci.skip = true` will be skipped.
-
+  # Any build target that contains `meta.ci.skip = true` or is marked
+  # broken will be skipped.
   # Is this tree node eligible for build inclusion?
-  eligible = node: (node ? outPath) && !(node.meta.ci.skip or false);
+  eligible = node: (node ? outPath) && !(node.meta.ci.skip or (node.meta.broken or false));
 
-in readTree.fix(self: (readDepot {
+in
+readTree.fix (self: (readDepot {
+  inherit localSystem crossSystem;
   depot = self;
 
   # Pass third_party as 'pkgs' (for compatibility with external
@@ -95,23 +104,38 @@ in readTree.fix(self: (readDepot {
     filter = self.third_party.nixpkgs.lib.cleanSourceFilter;
   };
 
+  # Additionally targets can be excluded from CI by adding them to the
+  # list below.
+  ci.excluded = [
+    # xanthous and related targets are disabled until cl/9186 is submitted
+    self.users.aspen.xanthous
+    self.users.aspen.system.system.mugwumpSystem
+
+    # Temporarily disabled after cl/11289. Hopefully these failures are transient
+    # and will disappear with the next channel bump.
+    self.users.wpcarro.nixos.avaSystem
+    self.users.wpcarro.nixos.kyokoSystem
+    self.users.wpcarro.nixos.marcusSystem
+    self.users.wpcarro.nixos.tarascoSystem
+  ];
+
   # List of all buildable targets, for CI purposes.
   #
   # Note: To prevent infinite recursion, this *must* be a nested
   # attribute set (which does not have a __readTree attribute).
-  ci.targets = readTree.gather eligible (self // {
-    # remove the pipelines themselves from the set over which to
-    # generate pipelines because that also leads to infinite
-    # recursion.
-    ops = self.ops // { pipelines = null; };
-
-    # remove nixpkgs from the set, for obvious reasons.
-    third_party = self.third_party // { nixpkgs = null; };
-  });
+  ci.targets = readTree.gather
+    (t: (eligible t) && (!builtins.elem t self.ci.excluded))
+    (self // {
+      # remove the pipelines themselves from the set over which to
+      # generate pipelines because that also leads to infinite
+      # recursion.
+      ops = self.ops // { pipelines = null; };
+    });
 
   # Derivation that gcroots all depot targets.
-  ci.gcroot = with self.third_party.nixpkgs; makeSetupHook {
-    name = "depot-gcroot";
-    deps = self.ci.targets;
-  } emptyFile;
+  ci.gcroot = with self.third_party.nixpkgs; writeText "depot-gcroot"
+    (builtins.concatStringsSep "\n"
+      (lib.flatten
+        (map (p: map (o: p.${o}) p.outputs or [ ]) # list all outputs of each drv
+          self.ci.targets)));
 })
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index dd138d3bfc..70410d290e 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -29,15 +29,15 @@ When adding a feature you should consider whether it is only useful for your
 particular use-case or whether it is generally applicable for other users of the
 project.
 
-When in doubt - just ask! You can reach out to us at
-[depot@tazj.in](mailto:depot@tazj.in) or on Twitter, IRC, etc.
+When in doubt - just ask! You can reach out to us via mail at
+[depot@tvl.su](mailto:depot@tvl.su) or on IRC.
 
 ## Commit messages
 
 All commit messages should be structured like this:
 
 ```
-type(scope): Subject line with at most a 68 character length
+type(scope): Subject line with at most a 72 character length
 
 Body of the commit message with an empty line between subject and
 body. This text should explain what the change does and why it has
diff --git a/docs/REVIEWS.md b/docs/REVIEWS.md
index e1c657b333..d76f11f410 100644
--- a/docs/REVIEWS.md
+++ b/docs/REVIEWS.md
@@ -10,6 +10,7 @@ TVL Code Reviews
     - [Review process & approvals](#review-process--approvals)
     - [Registration](#registration)
     - [Submitting changes via email](#submitting-changes-via-email)
+    - [Gerrit for Github users](#gerrit-for-github-users)
 
 <!-- markdown-toc end -->
 
@@ -34,7 +35,8 @@ a commit hook should be installed as follows:
 
 ```
 git clone "ssh://$USER@code.tvl.fyi:29418/depot"
-scp -p -P 29418 $USER@code.tvl.fyi:hooks/commit-msg "depot/.git/hooks/"
+curl -Lo depot/.git/hooks/commit-msg https://cl.tvl.fyi/tools/hooks/commit-msg
+chmod +x depot/.git/hooks/commit-msg
 ```
 
 If you have a previous clone of the depot via HTTP you can use `git remote
@@ -45,8 +47,11 @@ set-url` to update the origin URL and install the hook in the same way as above.
 The developer workflow on Gerrit is quite different from what GitHub-users are
 used to.
 
-The depot does not have branches (other than Gerrit's internal metadata refs)
-and all development happens at `HEAD`.
+Instead of pushing changes to remote branches, all changes have to be pushed to
+`refs/for/canon`. For each commit that is pushed there, a change request is
+created automatically.
+
+Changes should usually be based on the remote `HEAD` (the `canon` branch).
 
 Every time you create a new commit the change hook will insert a unique
 `Change-Id` tag into the commit message. Once you are satisfied with the state
@@ -73,7 +78,7 @@ git push origin
 git push origin HEAD:refs/for/canon%wip
 ```
 
-TIP: Every individual commit will become a separate change. We do not merge
+TIP: Every individual commit will become a separate change. We do not squash
 related commits, but instead submit them one by one. Be aware that if you are
 expecting a different behaviour and attempt something like an unsquashed subtree
 merge, you will produce a *lot* of CLs. This is strongly discouraged.
@@ -108,33 +113,37 @@ themselves**.
 
 ## Registration
 
-If you would like to have an account on the Gerrit instance, follow these
-instructions:
+You may log into Gerrit using a GitHub, StackOverflow or GitLab.com account.
+
+If you would like to have a TVL-specific account on the Gerrit
+instance, follow these instructions:
 
 1. Be a member of `#tvl` on [hackint][].
 2. Clone the depot locally (via `git clone "https://cl.tvl.fyi/depot"`).
 3. Create a user entry in our LDAP server in [ops/users][ops-users].
 
-   We recommend using ARGON2 password hashes, which can be created
-   with the `slappasswd` tool if OpenLDAP was compiled with ARGON2
-   support.
-
-   For convenience, we provide a wrapper script for this that you can
-   build with `nix-build -A tools.hash-password` in a depot checkout.
-   Alternatively, if you have `direnv` installed, you can add the
-   depot to your allowlist and just run `hash-password` which should
-   be added to your `$PATH` by `direnv`.
-
-   You can probably create ARGON2 hashes with other tools, but that is
-   your job to figure out.
+   The entry can be generated using [//web/pwcrypt](https://signup.tvl.fyi/).
 4. Create a commit adding yourself (see e.g.
    [CL/2671](https://cl.tvl.fyi/c/depot/+/2671))
 5. Submit the commit via email (see below).
 
 ## Submitting changes via email
+Please keep in mind this process is more complicated and requires more work from
+both sides:
 
-You can submit a patch via email to `depot@tazj.in` and it will be added to
-Gerrit by a contributor.
+ - Someone needs to relay potential comments from Gerrit to you, you won't get
+   emails from Gerrit.
+ - Uploading new revisions needs to be done by the person sending it to Gerrit
+   on your behalf.
+ - If you decide to get a Gerrit account later on, existing CLs need to be
+   abandoned and recreated (as CLs can't change Owner).
+   This causing earlier reviews do be more disconnected, causing more churn.
+
+We provide local accounts and do SSO with various third-parties, so getting the
+account should usually be low-friction.
+
+If you still decide differently, you can submit a patch via email to
+`depot@tvl.su` and it will be added to Gerrit by a contributor.
 
 Create an appropriate commit locally and send it us using either of these options:
 
@@ -143,12 +152,48 @@ Create an appropriate commit locally and send it us using either of these option
 * `git send-email`: If configured on your system, this will take care of the
   whole emailing process for you.
 
-The email address is a [public group][].
+The email address is a [public inbox][].
+
+## Gerrit for Github Users
+
+There is a walkthrough that describes [only the parts that differ
+from Github][github-diff], although it does not cover [attention
+sets][], which are important to understand.
+
+### Attention Sets
+
+The attention set of a CL is somewhat similar to the set of Github
+users who have unread notifications for a PR.  The "your turn" list
+on the dashboard is similar to your unread notifications list in
+Github.  These similarities are only rough approximations, however.
+
+Unfortunately the rules for updating attention sets are very
+different and complex.  If you don't read and understand them, you
+may end up leaving comments that nobody ever finds out about.  Here
+are a few unexpected features:
+
+- Voting on or replying to a CL will remove you from the attention
+  set.  You can also do this by clicking on the gray chevron shape
+  next to your name.
+
+- If you comment on a merged or abandoned change without marking
+  your comment "unresolved", *nobody will be notified of your
+  comment*.  If you want to the owner of a merged or abandoned
+  change to read your comment, you must mark it as "unresolved" or
+  manually add them to the attention set by hovering your mouse over
+  their name and clicking "add to attention set"
+
+There are many more [rules][attention-set-rules], which you should
+read.
+
 
 [Gerrit SSH]: https://cl.tvl.fyi/settings/#SSHKeys
 [Gerrit walkthrough]: https://gerrit-review.googlesource.com/Documentation/intro-gerrit-walkthrough.html
 [OWNERS]: https://cl.tvl.fyi/plugins/owners/Documentation/config.md
 [guidelines]: ./CONTRIBUTING.md#commit-messages
 [ops-users]: ../ops/users/default.nix
-[public group]: https://groups.google.com/a/tazj.in/forum/?hl=en#!forum/depot
+[public inbox]: https://inbox.tvl.su/depot/
 [hackint]: https://hackint.org
+[github-diff]: https://gerrit.wikimedia.org/r/Documentation/intro-gerrit-walkthrough-github.html
+[attention sets]: https://gerrit-review.googlesource.com/Documentation/user-attention-set.html
+[attention-set-rules]: https://gerrit-review.googlesource.com/Documentation/user-attention-set.html#_rules
diff --git a/docs/designs/SPARSE_CHECKOUTS.md b/docs/designs/SPARSE_CHECKOUTS.md
index 7bd4963f61..820cb2c586 100644
--- a/docs/designs/SPARSE_CHECKOUTS.md
+++ b/docs/designs/SPARSE_CHECKOUTS.md
@@ -1,3 +1,9 @@
+NOTE: This proposal is archived. We run `josh` instead, and long-term
+might want to integrate per-target dependency analysis with josh's
+workspace functionality.
+
+-------------
+
 Below is a prototype for a script to create Git sparse checkouts of the depot.
 The script below works today with relatively recent versions of git.
 
diff --git a/fun/defer_rs/examples/defer-with-error.rs b/fun/defer_rs/examples/defer-with-error.rs
index 26d56d77cf..f8b8a01413 100644
--- a/fun/defer_rs/examples/defer-with-error.rs
+++ b/fun/defer_rs/examples/defer-with-error.rs
@@ -4,17 +4,17 @@ use std::rc::Rc;
 use std::sync::RwLock;
 
 struct Defer<F: Fn()> {
-    f: F
+    f: F,
 }
 
-impl <F: Fn()> Drop for Defer<F> {
+impl<F: Fn()> Drop for Defer<F> {
     fn drop(&mut self) {
         (self.f)()
     }
 }
 
 // Only added this for Go-syntax familiarity ;-)
-fn  defer<F: Fn()>(f: F) -> Defer<F> {
+fn defer<F: Fn()>(f: F) -> Defer<F> {
     Defer { f }
 }
 
@@ -29,7 +29,9 @@ type ErrorHandle<T> = Rc<RwLock<Option<T>>>;
 ///////////////////
 
 #[derive(Debug)] // Debug trait for some default way to print the type.
-enum Error { DropError }
+enum Error {
+    DropError,
+}
 
 fn main() {
     // Create a place to store the error.
@@ -60,7 +62,7 @@ fn main() {
 
     match *drop_err.read().unwrap() {
         Some(ref err) => println!("Oh no, an error occured: {:?}!", err),
-        None => println!("Phew, everything went well.")
+        None => println!("Phew, everything went well."),
     };
 }
 
diff --git a/fun/defer_rs/examples/defer.rs b/fun/defer_rs/examples/defer.rs
index eadac795f8..0c99d00c82 100644
--- a/fun/defer_rs/examples/defer.rs
+++ b/fun/defer_rs/examples/defer.rs
@@ -1,17 +1,17 @@
 // Go's defer in Rust!
 
 struct Defer<F: Fn()> {
-    f: F
+    f: F,
 }
 
-impl <F: Fn()> Drop for Defer<F> {
+impl<F: Fn()> Drop for Defer<F> {
     fn drop(&mut self) {
         (self.f)()
     }
 }
 
 // Only added this for Go-syntax familiarity ;-)
-fn  defer<F: Fn()>(f: F) -> Defer<F> {
+fn defer<F: Fn()>(f: F) -> Defer<F> {
     Defer { f }
 }
 
diff --git a/fun/defer_rs/examples/undefer.rs b/fun/defer_rs/examples/undefer.rs
index 17ad8a6b54..fa659de891 100644
--- a/fun/defer_rs/examples/undefer.rs
+++ b/fun/defer_rs/examples/undefer.rs
@@ -1,10 +1,10 @@
 // Go's defer in Rust, with a little twist!
 
 struct Defer<F: Fn()> {
-    f: F
+    f: F,
 }
 
-impl <F: Fn()> Drop for Defer<F> {
+impl<F: Fn()> Drop for Defer<F> {
     fn drop(&mut self) {
         (self.f)()
     }
diff --git a/fun/gemma/default.nix b/fun/gemma/default.nix
index 0b5e67b159..339b86d269 100644
--- a/fun/gemma/default.nix
+++ b/fun/gemma/default.nix
@@ -1,8 +1,7 @@
-{ depot, pkgs, ... }:
+{ depot, ... }:
 
 let
-  inherit (pkgs) cacert iana-etc libredirect stdenv runCommandNoCC writeText;
-  elmPackages = depot.third_party.elmPackages_0_18;
+  inherit (depot.third_party.elmPackages_0_18) cacert iana-etc libredirect stdenv runCommand writeText elmPackages;
 
   frontend = stdenv.mkDerivation {
     name = "gemma-frontend.html";
@@ -28,12 +27,13 @@ let
 
   injectFrontend = writeText "gemma-frontend.lisp" ''
     (in-package :gemma)
-    (setq *static-file-location* "${runCommandNoCC "frontend" {} ''
+    (setq *static-file-location* "${runCommand "frontend" {} ''
       mkdir -p $out
       cp ${frontend} $out/index.html
     ''}/")
   '';
-in depot.nix.buildLisp.program {
+in
+depot.nix.buildLisp.program {
   name = "gemma";
 
   deps = with depot.third_party.lisp; [
diff --git a/fun/idual/default.nix b/fun/idual/default.nix
index 0f87f4ae1a..1acf287bfb 100644
--- a/fun/idual/default.nix
+++ b/fun/idual/default.nix
@@ -4,19 +4,20 @@ let
   inherit (pkgs) python3 python3Packages;
 
   opts = {
-    pname   = "idualctl";
+    pname = "idualctl";
     version = "0.1";
-    src     = ./.;
+    src = ./.;
 
     propagatedBuildInputs = [
       depot.third_party.python.broadlink
     ];
   };
   package = python3Packages.buildPythonPackage opts;
-  script  = python3Packages.buildPythonApplication opts;
-in depot.nix.readTree.drvTargets {
+  script = python3Packages.buildPythonApplication opts;
+in
+depot.nix.readTree.drvTargets {
   inherit script;
-  python  = python3.withPackages (_: [ package ]);
+  python = python3.withPackages (_: [ package ]);
   setAlarm = pkgs.writeShellScriptBin "set-alarm" ''
     echo "setting an alarm for ''${1}"
     ${pkgs.systemd}/bin/systemd-run --user --on-calendar="''${1} Europe/London" --unit=light-alarm.service
diff --git a/fun/owothia/default.nix b/fun/owothia/default.nix
index b70d0525c1..04f98e97fb 100644
--- a/fun/owothia/default.nix
+++ b/fun/owothia/default.nix
@@ -1,6 +1,7 @@
-{ depot ? (import ../../../. {})
+{ depot ? (import ../../../. { })
 , pkgs ? depot.third_party.nixpkgs
-, ... }:
+, ...
+}:
 
 let
   basePkg = pkgs.haskellPackages.callPackage ./pkg.nix { };
diff --git a/fun/owothia/pkg.nix b/fun/owothia/pkg.nix
index d0941a8489..c812e5e116 100644
--- a/fun/owothia/pkg.nix
+++ b/fun/owothia/pkg.nix
@@ -1,5 +1,15 @@
-{ mkDerivation, base, bytestring, chatter, containers, envy
-, irc-client, lens, lib, random, relude, text
+{ mkDerivation
+, base
+, bytestring
+, chatter
+, containers
+, envy
+, irc-client
+, lens
+, lib
+, random
+, relude
+, text
 }:
 mkDerivation {
   pname = "owothia";
@@ -8,8 +18,16 @@ mkDerivation {
   isLibrary = false;
   isExecutable = true;
   executableHaskellDepends = [
-    base bytestring chatter containers envy irc-client lens random
-    relude text
+    base
+    bytestring
+    chatter
+    containers
+    envy
+    irc-client
+    lens
+    random
+    relude
+    text
   ];
   license = "unknown";
   hydraPlatforms = lib.platforms.none;
diff --git a/fun/owothia/shell.nix b/fun/owothia/shell.nix
index 1ad70c907b..0304581d9d 100644
--- a/fun/owothia/shell.nix
+++ b/fun/owothia/shell.nix
@@ -1,4 +1,4 @@
-{ pkgs ? (import ../../../. {}).third_party, ... }:
+{ pkgs ? (import ../../../. { }).third_party, ... }:
 
 let
   inherit (pkgs)
diff --git a/fun/paroxysm/Cargo.lock b/fun/paroxysm/Cargo.lock
index c0962dfff6..23e4a0d3a4 100644
--- a/fun/paroxysm/Cargo.lock
+++ b/fun/paroxysm/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
 
 [[package]]
 name = "addr2line"
-version = "0.16.0"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
 dependencies = [
  "gimli",
 ]
@@ -19,35 +19,50 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.18"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "atty"
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 dependencies = [
- "hermit-abi",
+ "hermit-abi 0.1.19",
  "libc",
  "winapi 0.3.9",
 ]
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "backtrace"
-version = "0.3.61"
+version = "0.3.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
 dependencies = [
  "addr2line",
  "cc",
@@ -65,16 +80,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
+name = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[package]]
 name = "bufstream"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
 
 [[package]]
+name = "bumpalo"
+version = "3.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
+
+[[package]]
 name = "byteorder"
-version = "1.4.3"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
@@ -88,9 +115,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.71"
+version = "1.0.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
 
 [[package]]
 name = "cfg-if"
@@ -106,15 +133,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.19"
+version = "0.4.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a"
 dependencies = [
- "libc",
- "num-integer",
- "num-traits 0.2.14",
- "time",
- "winapi 0.3.9",
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits 0.2.18",
+ "wasm-bindgen",
+ "windows-targets 0.52.4",
 ]
 
 [[package]]
@@ -123,7 +151,7 @@ version = "0.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
@@ -135,7 +163,7 @@ dependencies = [
  "lazy_static 1.4.0",
  "nom",
  "rust-ini",
- "serde 1.0.130",
+ "serde 1.0.197",
  "serde-hjson",
  "serde_json",
  "toml",
@@ -144,9 +172,9 @@ dependencies = [
 
 [[package]]
 name = "core-foundation"
-version = "0.9.1"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -154,18 +182,16 @@ dependencies = [
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.2"
+version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
 
 [[package]]
 name = "crimp"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe8f9a320ad9c1a2e3bacedaa281587bd297fb10a10179fd39f777049d04794"
+version = "4087.0.0"
 dependencies = [
  "curl",
- "serde 1.0.130",
+ "serde 1.0.197",
  "serde_json",
 ]
 
@@ -219,9 +245,9 @@ dependencies = [
 
 [[package]]
 name = "curl"
-version = "0.4.39"
+version = "0.4.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aaa3b8db7f3341ddef15786d250106334d4a6c4b0ae4a46cd77082777d9849b9"
+checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6"
 dependencies = [
  "curl-sys",
  "libc",
@@ -229,14 +255,14 @@ dependencies = [
  "openssl-sys",
  "schannel",
  "socket2",
- "winapi 0.3.9",
+ "windows-sys",
 ]
 
 [[package]]
 name = "curl-sys"
-version = "0.4.49+curl-7.79.1"
+version = "0.4.72+curl-8.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0f44960aea24a786a46907b8824ebc0e66ca06bf4e4978408c7499620343483"
+checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea"
 dependencies = [
  "cc",
  "libc",
@@ -244,7 +270,7 @@ dependencies = [
  "openssl-sys",
  "pkg-config",
  "vcpkg",
- "winapi 0.3.9",
+ "windows-sys",
 ]
 
 [[package]]
@@ -253,7 +279,7 @@ version = "1.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "byteorder",
  "chrono",
  "diesel_derives",
@@ -269,7 +295,7 @@ checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -350,6 +376,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "errno"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
 name = "failure"
 version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -367,11 +403,17 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
  "synstructure",
 ]
 
 [[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+
+[[package]]
 name = "fnv"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -398,7 +440,7 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "fuchsia-zircon-sys",
 ]
 
@@ -422,25 +464,14 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "wasi 0.10.2+wasi-snapshot-preview1",
+ "wasi",
 ]
 
 [[package]]
 name = "gimli"
-version = "0.25.0"
+version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
 
 [[package]]
 name = "hermit-abi"
@@ -452,6 +483,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
 name = "humantime"
 version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -461,12 +498,26 @@ dependencies = [
 ]
 
 [[package]]
-name = "instant"
-version = "0.1.11"
+name = "iana-time-zone"
+version = "0.1.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
 dependencies = [
- "cfg-if 1.0.0",
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
 ]
 
 [[package]]
@@ -492,7 +543,7 @@ dependencies = [
  "futures",
  "log",
  "native-tls",
- "serde 1.0.130",
+ "serde 1.0.197",
  "serde_derive",
  "tokio-codec",
  "tokio-core",
@@ -505,9 +556,18 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "0.4.8"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
 
 [[package]]
 name = "kernel32-sys"
@@ -533,15 +593,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.103"
+version = "0.2.153"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
 
 [[package]]
 name = "libz-sys"
-version = "1.1.3"
+version = "1.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
+checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6"
 dependencies = [
  "cc",
  "libc",
@@ -561,9 +621,15 @@ dependencies = [
 
 [[package]]
 name = "linked-hash-map"
-version = "0.5.4"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 
 [[package]]
 name = "lock_api"
@@ -576,21 +642,19 @@ dependencies = [
 
 [[package]]
 name = "lock_api"
-version = "0.4.5"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
 dependencies = [
+ "autocfg",
  "scopeguard",
 ]
 
 [[package]]
 name = "log"
-version = "0.4.14"
+version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
-dependencies = [
- "cfg-if 1.0.0",
-]
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
 
 [[package]]
 name = "maybe-uninit"
@@ -600,9 +664,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
 
 [[package]]
 name = "memchr"
-version = "2.4.1"
+version = "2.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
 
 [[package]]
 name = "memoffset"
@@ -615,12 +679,11 @@ dependencies = [
 
 [[package]]
 name = "miniz_oxide"
-version = "0.4.4"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
 dependencies = [
  "adler",
- "autocfg",
 ]
 
 [[package]]
@@ -638,7 +701,7 @@ dependencies = [
  "log",
  "miow",
  "net2",
- "slab 0.4.4",
+ "slab 0.4.9",
  "winapi 0.2.8",
 ]
 
@@ -667,9 +730,9 @@ dependencies = [
 
 [[package]]
 name = "native-tls"
-version = "0.2.8"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
+checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
 dependencies = [
  "lazy_static 1.4.0",
  "libc",
@@ -685,9 +748,9 @@ dependencies = [
 
 [[package]]
 name = "net2"
-version = "0.2.37"
+version = "0.2.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
+checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
@@ -705,85 +768,86 @@ dependencies = [
 ]
 
 [[package]]
-name = "num-integer"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
-dependencies = [
- "autocfg",
- "num-traits 0.2.14",
-]
-
-[[package]]
 name = "num-traits"
 version = "0.1.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
 dependencies = [
- "num-traits 0.2.14",
+ "num-traits 0.2.18",
 ]
 
 [[package]]
 name = "num-traits"
-version = "0.2.14"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "num_cpus"
-version = "1.13.0"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.9",
  "libc",
 ]
 
 [[package]]
 name = "object"
-version = "0.26.2"
+version = "0.32.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.8.0"
+version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "openssl"
-version = "0.10.36"
+version = "0.10.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a"
+checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
 dependencies = [
- "bitflags",
+ "bitflags 2.4.2",
  "cfg-if 1.0.0",
  "foreign-types",
  "libc",
  "once_cell",
+ "openssl-macros",
  "openssl-sys",
 ]
 
 [[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.52",
+]
+
+[[package]]
 name = "openssl-probe"
-version = "0.1.4"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.67"
+version = "0.9.101"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69df2d8dfc6ce3aaf44b40dec6f487d5a886516cf6879c49e98e0710f310a058"
+checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
 dependencies = [
- "autocfg",
  "cc",
  "libc",
  "pkg-config",
@@ -797,26 +861,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
 dependencies = [
  "lock_api 0.3.4",
- "parking_lot_core 0.6.2",
+ "parking_lot_core 0.6.3",
  "rustc_version",
 ]
 
 [[package]]
 name = "parking_lot"
-version = "0.11.2"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
 dependencies = [
- "instant",
- "lock_api 0.4.5",
- "parking_lot_core 0.8.5",
+ "lock_api 0.4.11",
+ "parking_lot_core 0.9.9",
 ]
 
 [[package]]
 name = "parking_lot_core"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
+checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a"
 dependencies = [
  "cfg-if 0.1.10",
  "cloudabi",
@@ -829,16 +892,15 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.8.5"
+version = "0.9.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
 dependencies = [
  "cfg-if 1.0.0",
- "instant",
  "libc",
- "redox_syscall 0.2.10",
- "smallvec 1.7.0",
- "winapi 0.3.9",
+ "redox_syscall 0.4.1",
+ "smallvec 1.13.1",
+ "windows-targets 0.48.5",
 ]
 
 [[package]]
@@ -854,39 +916,39 @@ dependencies = [
  "irc",
  "lazy_static 1.4.0",
  "log",
- "rand 0.7.3",
+ "rand",
  "regex",
- "serde 1.0.130",
+ "serde 1.0.197",
 ]
 
 [[package]]
 name = "pkg-config"
-version = "0.3.20"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.10"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "pq-sys"
-version = "0.4.6"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda"
+checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd"
 dependencies = [
  "vcpkg",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.29"
+version = "1.0.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
 dependencies = [
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -897,21 +959,21 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 
 [[package]]
 name = "quote"
-version = "1.0.10"
+version = "1.0.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "r2d2"
-version = "0.8.9"
+version = "0.8.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
+checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
 dependencies = [
  "log",
- "parking_lot 0.11.2",
+ "parking_lot 0.12.1",
  "scheduled-thread-pool",
 ]
 
@@ -921,23 +983,11 @@ version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
 dependencies = [
- "getrandom 0.1.16",
- "libc",
- "rand_chacha 0.2.2",
- "rand_core 0.5.1",
- "rand_hc 0.2.0",
-]
-
-[[package]]
-name = "rand"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
-dependencies = [
+ "getrandom",
  "libc",
- "rand_chacha 0.3.1",
- "rand_core 0.6.3",
- "rand_hc 0.3.1",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
 ]
 
 [[package]]
@@ -947,17 +997,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
 dependencies = [
  "ppv-lite86",
- "rand_core 0.5.1",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core 0.6.3",
+ "rand_core",
 ]
 
 [[package]]
@@ -966,16 +1006,7 @@ version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
 dependencies = [
- "getrandom 0.1.16",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
-dependencies = [
- "getrandom 0.2.3",
+ "getrandom",
 ]
 
 [[package]]
@@ -984,16 +1015,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
 dependencies = [
- "rand_core 0.5.1",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
-dependencies = [
- "rand_core 0.6.3",
+ "rand_core",
 ]
 
 [[package]]
@@ -1004,38 +1026,41 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.10"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.4"
+version = "1.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
 dependencies = [
  "aho-corasick",
  "memchr",
+ "regex-automata",
  "regex-syntax",
 ]
 
 [[package]]
-name = "regex-syntax"
-version = "0.6.25"
+name = "regex-automata"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
 
 [[package]]
-name = "remove_dir_all"
-version = "0.5.3"
+name = "regex-syntax"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi 0.3.9",
-]
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
 
 [[package]]
 name = "rust-ini"
@@ -1045,9 +1070,9 @@ checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.21"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
 
 [[package]]
 name = "rustc_version"
@@ -1059,28 +1084,40 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustix"
+version = "0.38.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
+dependencies = [
+ "bitflags 2.4.2",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
 name = "ryu"
-version = "1.0.5"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
 
 [[package]]
 name = "schannel"
-version = "0.1.19"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
 dependencies = [
- "lazy_static 1.4.0",
- "winapi 0.3.9",
+ "windows-sys",
 ]
 
 [[package]]
 name = "scheduled-thread-pool"
-version = "0.2.5"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
+checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
 dependencies = [
- "parking_lot 0.11.2",
+ "parking_lot 0.12.1",
 ]
 
 [[package]]
@@ -1091,17 +1128,17 @@ checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
 
 [[package]]
 name = "scopeguard"
-version = "1.1.0"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "security-framework"
-version = "2.4.2"
+version = "2.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87"
+checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "core-foundation",
  "core-foundation-sys",
  "libc",
@@ -1110,9 +1147,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.4.2"
+version = "2.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e"
+checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -1141,9 +1178,9 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
 
 [[package]]
 name = "serde"
-version = "1.0.130"
+version = "1.0.197"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
 dependencies = [
  "serde_derive",
 ]
@@ -1163,24 +1200,24 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.130"
+version = "1.0.197"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.52",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.68"
+version = "1.0.114"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
+checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
 dependencies = [
  "itoa",
  "ryu",
- "serde 1.0.130",
+ "serde 1.0.197",
 ]
 
 [[package]]
@@ -1200,9 +1237,12 @@ checksum = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
 
 [[package]]
 name = "slab"
-version = "0.4.4"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
 
 [[package]]
 name = "smallvec"
@@ -1215,29 +1255,40 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.7.0"
+version = "1.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
+checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
 
 [[package]]
 name = "socket2"
-version = "0.4.2"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
+checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
 dependencies = [
  "libc",
- "winapi 0.3.9",
+ "windows-sys",
 ]
 
 [[package]]
 name = "syn"
-version = "1.0.80"
+version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
 dependencies = [
  "proc-macro2",
  "quote",
- "unicode-xid",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -1248,44 +1299,32 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
  "unicode-xid",
 ]
 
 [[package]]
 name = "tempfile"
-version = "3.2.0"
+version = "3.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
 dependencies = [
  "cfg-if 1.0.0",
- "libc",
- "rand 0.8.4",
- "redox_syscall 0.2.10",
- "remove_dir_all",
- "winapi 0.3.9",
+ "fastrand",
+ "rustix",
+ "windows-sys",
 ]
 
 [[package]]
 name = "termcolor"
-version = "1.1.2"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
 dependencies = [
  "winapi-util",
 ]
 
 [[package]]
-name = "time"
-version = "0.1.43"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
-dependencies = [
- "libc",
- "winapi 0.3.9",
-]
-
-[[package]]
 name = "tokio"
 version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1404,7 +1443,7 @@ dependencies = [
  "mio",
  "num_cpus",
  "parking_lot 0.9.0",
- "slab 0.4.4",
+ "slab 0.4.9",
  "tokio-executor",
  "tokio-io",
  "tokio-sync",
@@ -1447,7 +1486,7 @@ dependencies = [
  "lazy_static 1.4.0",
  "log",
  "num_cpus",
- "slab 0.4.4",
+ "slab 0.4.9",
  "tokio-executor",
 ]
 
@@ -1469,7 +1508,7 @@ checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
 dependencies = [
  "crossbeam-utils",
  "futures",
- "slab 0.4.4",
+ "slab 0.4.9",
  "tokio-executor",
 ]
 
@@ -1523,14 +1562,20 @@ version = "0.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
 dependencies = [
- "serde 1.0.130",
+ "serde 1.0.197",
 ]
 
 [[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
 name = "unicode-xid"
-version = "0.2.2"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
 
 [[package]]
 name = "vcpkg"
@@ -1551,10 +1596,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
 [[package]]
-name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
+name = "wasm-bindgen"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if 1.0.0",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.52",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.52",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
 
 [[package]]
 name = "winapi"
@@ -1586,9 +1679,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
 dependencies = [
  "winapi 0.3.9",
 ]
@@ -1600,6 +1693,138 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.4",
+ "windows_aarch64_msvc 0.52.4",
+ "windows_i686_gnu 0.52.4",
+ "windows_i686_msvc 0.52.4",
+ "windows_x86_64_gnu 0.52.4",
+ "windows_x86_64_gnullvm 0.52.4",
+ "windows_x86_64_msvc 0.52.4",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
+
+[[package]]
 name = "ws2_32-sys"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1615,5 +1840,5 @@ version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
 dependencies = [
- "linked-hash-map 0.5.4",
+ "linked-hash-map 0.5.6",
 ]
diff --git a/fun/paroxysm/Cargo.nix b/fun/paroxysm/Cargo.nix
new file mode 100644
index 0000000000..4318ee2cb8
--- /dev/null
+++ b/fun/paroxysm/Cargo.nix
@@ -0,0 +1,6064 @@
+# This file was @generated by crate2nix 0.12.0 with the command:
+#   "generate"
+# See https://github.com/kolloch/crate2nix for more info.
+
+{ nixpkgs ? <nixpkgs>
+, pkgs ? import nixpkgs { config = { }; }
+, lib ? pkgs.lib
+, stdenv ? pkgs.stdenv
+, buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate
+  # This is used as the `crateOverrides` argument for `buildRustCrate`.
+, defaultCrateOverrides ? pkgs.defaultCrateOverrides
+  # The features to enable for the root_crate or the workspace_members.
+, rootFeatures ? [ "default" ]
+  # If true, throw errors instead of issueing deprecation warnings.
+, strictDeprecation ? false
+  # Used for conditional compilation based on CPU feature detection.
+, targetFeatures ? [ ]
+  # Whether to perform release builds: longer compile times, faster binaries.
+, release ? true
+  # Additional crate2nix configuration if it exists.
+, crateConfig ? if builtins.pathExists ./crate-config.nix
+  then pkgs.callPackage ./crate-config.nix { }
+  else { }
+}:
+
+rec {
+  #
+  # "public" attributes that we attempt to keep stable with new versions of crate2nix.
+  #
+
+  rootCrate = rec {
+    packageId = "paroxysm";
+
+    # Use this attribute to refer to the derivation building your root crate package.
+    # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }.
+    build = internal.buildRustCrateWithFeatures {
+      inherit packageId;
+    };
+
+    # Debug support which might change between releases.
+    # File a bug if you depend on any for non-debug work!
+    debug = internal.debugCrate { inherit packageId; };
+  };
+  # Refer your crate build derivation by name here.
+  # You can override the features with
+  # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }.
+  workspaceMembers = {
+    "paroxysm" = rec {
+      packageId = "paroxysm";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "paroxysm";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+  };
+
+  # A derivation that joins the outputs of all workspace members together.
+  allWorkspaceMembers = pkgs.symlinkJoin {
+    name = "all-workspace-members";
+    paths =
+      let members = builtins.attrValues workspaceMembers;
+      in builtins.map (m: m.build) members;
+  };
+
+  #
+  # "internal" ("private") attributes that may change in every new version of crate2nix.
+  #
+
+  internal = rec {
+    # Build and dependency information for crates.
+    # Many of the fields are passed one-to-one to buildRustCrate.
+    #
+    # Noteworthy:
+    # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate.
+    #   but with additional information which is used during dependency/feature resolution.
+    # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging.
+    # * `devDependencies` as of now not used by `buildRustCrate` but used to
+    #   inject test dependencies into the build
+
+    crates = {
+      "addr2line" = rec {
+        crateName = "addr2line";
+        version = "0.21.0";
+        edition = "2018";
+        sha256 = "1jx0k3iwyqr8klqbzk6kjvr496yd94aspis10vwsj5wy7gib4c4a";
+        dependencies = [
+          {
+            name = "gimli";
+            packageId = "gimli";
+            usesDefaultFeatures = false;
+            features = [ "read" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "rustc-demangle" "cpp_demangle" "std-object" "fallible-iterator" "smallvec" "memmap2" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "memmap2" = [ "dep:memmap2" ];
+          "object" = [ "dep:object" ];
+          "rustc-demangle" = [ "dep:rustc-demangle" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "gimli/rustc-dep-of-std" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "gimli/std" ];
+          "std-object" = [ "std" "object" "object/std" "object/compression" "gimli/endian-reader" ];
+        };
+      };
+      "adler" = rec {
+        crateName = "adler";
+        version = "1.0.2";
+        edition = "2015";
+        sha256 = "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj";
+        authors = [
+          "Jonas Schievink <jonasschievink@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "aho-corasick" = rec {
+        crateName = "aho-corasick";
+        version = "1.1.2";
+        edition = "2021";
+        sha256 = "1w510wnixvlgimkx1zjbvlxh6xps2vjgfqgwf5a6adlbjp5rv5mj";
+        libName = "aho_corasick";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf-literal" ];
+          "logging" = [ "dep:log" ];
+          "perf-literal" = [ "dep:memchr" ];
+          "std" = [ "memchr?/std" ];
+        };
+        resolvedDefaultFeatures = [ "perf-literal" "std" ];
+      };
+      "android-tzdata" = rec {
+        crateName = "android-tzdata";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        authors = [
+          "RumovZ"
+        ];
+
+      };
+      "android_system_properties" = rec {
+        crateName = "android_system_properties";
+        version = "0.1.5";
+        edition = "2018";
+        sha256 = "04b3wrz12837j7mdczqd95b732gw5q7q66cv4yn4646lvccp57l1";
+        authors = [
+          "Nicolas Silva <nical@fastmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "atty" = rec {
+        crateName = "atty";
+        version = "0.2.14";
+        edition = "2015";
+        sha256 = "1s7yslcs6a28c5vz7jwj63lkfgyx8mx99fdirlhi9lbhhzhrpcyr";
+        authors = [
+          "softprops <d.tangren@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi 0.1.19";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi 0.3.9";
+            target = { target, features }: (target."windows" or false);
+            features = [ "consoleapi" "processenv" "minwinbase" "minwindef" "winbase" ];
+          }
+        ];
+
+      };
+      "autocfg" = rec {
+        crateName = "autocfg";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "1ylp3cb47ylzabimazvbz9ms6ap784zhb6syaz6c1jqpmcmq0s6l";
+        authors = [
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+
+      };
+      "backtrace" = rec {
+        crateName = "backtrace";
+        version = "0.3.69";
+        edition = "2018";
+        sha256 = "0dsq23dhw4pfndkx2nsa1ml2g31idm7ss7ljxp8d57avygivg290";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "addr2line";
+            packageId = "addr2line";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 1.0.0";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "object";
+            packageId = "object";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+            features = [ "read_core" "elf" "macho" "pe" "unaligned" "archive" ];
+          }
+          {
+            name = "rustc-demangle";
+            packageId = "rustc-demangle";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "std" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "serialize-rustc" = [ "rustc-serialize" ];
+          "serialize-serde" = [ "serde" ];
+          "verify-winapi" = [ "winapi/dbghelp" "winapi/handleapi" "winapi/libloaderapi" "winapi/memoryapi" "winapi/minwindef" "winapi/processthreadsapi" "winapi/synchapi" "winapi/tlhelp32" "winapi/winbase" "winapi/winnt" ];
+          "winapi" = [ "dep:winapi" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "bitflags 1.3.2" = rec {
+        crateName = "bitflags";
+        version = "1.3.2";
+        edition = "2018";
+        sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bitflags 2.4.2" = rec {
+        crateName = "bitflags";
+        version = "2.4.2";
+        edition = "2021";
+        sha256 = "1pqd142hyqlzr7p9djxq2ff0jx07a2sb2xp9lhw69cbf80s0jmzd";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "bufstream" = rec {
+        crateName = "bufstream";
+        version = "0.1.4";
+        edition = "2015";
+        sha256 = "1j7f52rv73hd1crzrrfb9dr50ccmi3hb1ybd6s5dyg6jmllqkqs0";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "futures" = [ "dep:futures" ];
+          "tokio" = [ "futures" "tokio-io" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bumpalo" = rec {
+        crateName = "bumpalo";
+        version = "3.15.4";
+        edition = "2021";
+        sha256 = "1ahfhgw2lzlgv5j0h07z8mkdnk4kvl2grf8dkb32dm4zsjfrpxkz";
+        authors = [
+          "Nick Fitzgerald <fitzgen@gmail.com>"
+        ];
+        features = {
+          "allocator-api2" = [ "dep:allocator-api2" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "byteorder" = rec {
+        crateName = "byteorder";
+        version = "1.5.0";
+        edition = "2021";
+        sha256 = "0jzncxyf404mwqdbspihyzpkndfgda450l0893pz5xj685cg5l0z";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "bytes" = rec {
+        crateName = "bytes";
+        version = "0.4.12";
+        edition = "2015";
+        sha256 = "0768a55q2fsqdjsvcv98ndg9dq7w2g44dvq1avhwpxrdzbydyvr0";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "byteorder";
+            packageId = "byteorder";
+          }
+          {
+            name = "iovec";
+            packageId = "iovec";
+          }
+        ];
+        features = {
+          "either" = [ "dep:either" ];
+          "i128" = [ "byteorder/i128" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "cc" = rec {
+        crateName = "cc";
+        version = "1.0.90";
+        edition = "2018";
+        sha256 = "1xg1bqnq50dpf6g1hl90caxgz4afnf74pxa426gh7wxch9561mlc";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "jobserver" = [ "dep:jobserver" ];
+          "libc" = [ "dep:libc" ];
+          "parallel" = [ "libc" "jobserver" ];
+        };
+      };
+      "cfg-if 0.1.10" = rec {
+        crateName = "cfg-if";
+        version = "0.1.10";
+        edition = "2018";
+        sha256 = "08h80ihs74jcyp24cd75wwabygbbdgl05k6p5dmq8akbr78vv1a7";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "cfg-if 1.0.0" = rec {
+        crateName = "cfg-if";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "chrono" = rec {
+        crateName = "chrono";
+        version = "0.4.35";
+        edition = "2021";
+        sha256 = "16k3caxzip1ql827pz5rj7aqfqy7yhpxyxzb5wqkj2mwvh1mkbwf";
+        dependencies = [
+          {
+            name = "android-tzdata";
+            packageId = "android-tzdata";
+            optional = true;
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "iana-time-zone";
+            packageId = "iana-time-zone";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+            features = [ "fallback" ];
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            optional = true;
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!(("emscripten" == target."os" or null) || ("wasi" == target."os" or null))));
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits 0.2.18";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            optional = true;
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!(("emscripten" == target."os" or null) || ("wasi" == target."os" or null))));
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.4";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "android-tzdata" = [ "dep:android-tzdata" ];
+          "arbitrary" = [ "dep:arbitrary" ];
+          "clock" = [ "winapi" "iana-time-zone" "android-tzdata" "now" ];
+          "default" = [ "clock" "std" "oldtime" "wasmbind" ];
+          "iana-time-zone" = [ "dep:iana-time-zone" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "now" = [ "std" ];
+          "pure-rust-locales" = [ "dep:pure-rust-locales" ];
+          "rkyv" = [ "dep:rkyv" "rkyv/size_32" ];
+          "rkyv-16" = [ "dep:rkyv" "rkyv?/size_16" ];
+          "rkyv-32" = [ "dep:rkyv" "rkyv?/size_32" ];
+          "rkyv-64" = [ "dep:rkyv" "rkyv?/size_64" ];
+          "rkyv-validation" = [ "rkyv?/validation" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "unstable-locales" = [ "pure-rust-locales" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+          "wasmbind" = [ "wasm-bindgen" "js-sys" ];
+          "winapi" = [ "windows-targets" ];
+          "windows-targets" = [ "dep:windows-targets" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "android-tzdata" "clock" "default" "iana-time-zone" "js-sys" "now" "oldtime" "std" "wasm-bindgen" "wasmbind" "winapi" "windows-targets" ];
+      };
+      "cloudabi" = rec {
+        crateName = "cloudabi";
+        version = "0.0.3";
+        edition = "2015";
+        sha256 = "0kxcg83jlihy0phnd2g8c2c303px3l2p3pkjz357ll6llnd5pz6x";
+        libPath = "cloudabi.rs";
+        authors = [
+          "Nuxi (https://nuxi.nl/) and contributors"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+            optional = true;
+          }
+        ];
+        features = {
+          "bitflags" = [ "dep:bitflags" ];
+          "default" = [ "bitflags" ];
+        };
+        resolvedDefaultFeatures = [ "bitflags" "default" ];
+      };
+      "config" = rec {
+        crateName = "config";
+        version = "0.9.3";
+        edition = "2015";
+        sha256 = "1rppjv8q5ffdyir6rawgizyqrm5yg9j8xlg7hrdgmcv2xmw7s47r";
+        authors = [
+          "Ryan Leckey <leckey.ryan@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lazy_static";
+            packageId = "lazy_static 1.4.0";
+          }
+          {
+            name = "nom";
+            packageId = "nom";
+          }
+          {
+            name = "rust-ini";
+            packageId = "rust-ini";
+            optional = true;
+          }
+          {
+            name = "serde";
+            packageId = "serde 1.0.197";
+          }
+          {
+            name = "serde-hjson";
+            packageId = "serde-hjson";
+            optional = true;
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+          }
+          {
+            name = "toml";
+            packageId = "toml";
+            optional = true;
+          }
+          {
+            name = "yaml-rust";
+            packageId = "yaml-rust";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "toml" "json" "yaml" "hjson" "ini" ];
+          "hjson" = [ "serde-hjson" ];
+          "ini" = [ "rust-ini" ];
+          "json" = [ "serde_json" ];
+          "rust-ini" = [ "dep:rust-ini" ];
+          "serde-hjson" = [ "dep:serde-hjson" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "toml" = [ "dep:toml" ];
+          "yaml" = [ "yaml-rust" ];
+          "yaml-rust" = [ "dep:yaml-rust" ];
+        };
+        resolvedDefaultFeatures = [ "default" "hjson" "ini" "json" "rust-ini" "serde-hjson" "serde_json" "toml" "yaml" "yaml-rust" ];
+      };
+      "core-foundation" = rec {
+        crateName = "core-foundation";
+        version = "0.9.4";
+        edition = "2018";
+        sha256 = "13zvbbj07yk3b61b8fhwfzhy35535a583irf23vlcg59j7h9bqci";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "chrono" = [ "dep:chrono" ];
+          "default" = [ "link" ];
+          "link" = [ "core-foundation-sys/link" ];
+          "mac_os_10_7_support" = [ "core-foundation-sys/mac_os_10_7_support" ];
+          "mac_os_10_8_features" = [ "core-foundation-sys/mac_os_10_8_features" ];
+          "uuid" = [ "dep:uuid" ];
+          "with-chrono" = [ "chrono" ];
+          "with-uuid" = [ "uuid" ];
+        };
+        resolvedDefaultFeatures = [ "default" "link" ];
+      };
+      "core-foundation-sys" = rec {
+        crateName = "core-foundation-sys";
+        version = "0.8.6";
+        edition = "2018";
+        sha256 = "13w6sdf06r0hn7bx2b45zxsg1mm2phz34jikm6xc5qrbr6djpsh6";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "default" = [ "link" ];
+        };
+        resolvedDefaultFeatures = [ "default" "link" ];
+      };
+      "crimp" = rec {
+        crateName = "crimp";
+        version = "4087.0.0";
+        edition = "2015";
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ../../net/crimp; }
+          else ../../net/crimp;
+        authors = [
+          "Vincent Ambo <tazjin@tvl.su>"
+        ];
+        dependencies = [
+          {
+            name = "curl";
+            packageId = "curl";
+          }
+          {
+            name = "serde";
+            packageId = "serde 1.0.197";
+            optional = true;
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "json" ];
+          "json" = [ "serde" "serde_json" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+        };
+        resolvedDefaultFeatures = [ "default" "json" "serde" "serde_json" ];
+      };
+      "crossbeam-deque" = rec {
+        crateName = "crossbeam-deque";
+        version = "0.7.4";
+        edition = "2015";
+        sha256 = "1v99xcdjk4zixvxnq7pssip670mlyhw1ma3qc88ca11jxnfz43y2";
+        authors = [
+          "The Crossbeam Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-epoch";
+            packageId = "crossbeam-epoch";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+          {
+            name = "maybe-uninit";
+            packageId = "maybe-uninit";
+          }
+        ];
+
+      };
+      "crossbeam-epoch" = rec {
+        crateName = "crossbeam-epoch";
+        version = "0.8.2";
+        edition = "2015";
+        sha256 = "1knsf0zz7rgzxn0nwz5gajjcrivxpw3zrdcp946gdhdgr9sd53h5";
+        authors = [
+          "The Crossbeam Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 0.1.10";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static 1.4.0";
+            optional = true;
+          }
+          {
+            name = "maybe-uninit";
+            packageId = "maybe-uninit";
+          }
+          {
+            name = "memoffset";
+            packageId = "memoffset";
+          }
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "alloc" = [ "crossbeam-utils/alloc" ];
+          "default" = [ "std" ];
+          "lazy_static" = [ "dep:lazy_static" ];
+          "nightly" = [ "crossbeam-utils/nightly" ];
+          "std" = [ "crossbeam-utils/std" "lazy_static" ];
+        };
+        resolvedDefaultFeatures = [ "default" "lazy_static" "std" ];
+      };
+      "crossbeam-queue" = rec {
+        crateName = "crossbeam-queue";
+        version = "0.2.3";
+        edition = "2015";
+        sha256 = "0w15z68nz3ac4f2s4djhwha8vmlwsh9dlfrmsl4x84y2ah5acjvp";
+        authors = [
+          "The Crossbeam Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 0.1.10";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "maybe-uninit";
+            packageId = "maybe-uninit";
+          }
+        ];
+        features = {
+          "alloc" = [ "crossbeam-utils/alloc" ];
+          "default" = [ "std" ];
+          "std" = [ "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crossbeam-utils" = rec {
+        crateName = "crossbeam-utils";
+        version = "0.7.2";
+        edition = "2015";
+        sha256 = "1a31wbrda1320gj2a6az1lin2d34xfc3xf88da4c17qy5lxcgiy3";
+        authors = [
+          "The Crossbeam Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 0.1.10";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static 1.4.0";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "lazy_static" = [ "dep:lazy_static" ];
+          "std" = [ "lazy_static" ];
+        };
+        resolvedDefaultFeatures = [ "default" "lazy_static" "std" ];
+      };
+      "curl" = rec {
+        crateName = "curl";
+        version = "0.4.46";
+        edition = "2018";
+        sha256 = "1dk7xi1fv57ak5wsgzig702czv3ssrgyk120b7qhy2dsdvfn288y";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "curl-sys";
+            packageId = "curl-sys";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "openssl-probe";
+            packageId = "openssl-probe";
+            optional = true;
+            target = { target, features }: ((target."unix" or false) && (!("macos" == target."os" or null)));
+          }
+          {
+            name = "openssl-sys";
+            packageId = "openssl-sys";
+            optional = true;
+            target = { target, features }: ((target."unix" or false) && (!("macos" == target."os" or null)));
+          }
+          {
+            name = "schannel";
+            packageId = "schannel";
+            target = { target, features }: ("msvc" == target."env" or null);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: ("msvc" == target."env" or null);
+            features = [ "Win32_Foundation" "Win32_System_LibraryLoader" "Win32_Security_Cryptography" ];
+          }
+        ];
+        features = {
+          "default" = [ "ssl" ];
+          "force-system-lib-on-osx" = [ "curl-sys/force-system-lib-on-osx" ];
+          "http2" = [ "curl-sys/http2" ];
+          "mesalink" = [ "curl-sys/mesalink" ];
+          "ntlm" = [ "curl-sys/ntlm" ];
+          "openssl-probe" = [ "dep:openssl-probe" ];
+          "openssl-sys" = [ "dep:openssl-sys" ];
+          "poll_7_68_0" = [ "curl-sys/poll_7_68_0" ];
+          "protocol-ftp" = [ "curl-sys/protocol-ftp" ];
+          "rustls" = [ "curl-sys/rustls" ];
+          "spnego" = [ "curl-sys/spnego" ];
+          "ssl" = [ "openssl-sys" "openssl-probe" "curl-sys/ssl" ];
+          "static-curl" = [ "curl-sys/static-curl" ];
+          "static-ssl" = [ "curl-sys/static-ssl" ];
+          "upkeep_7_62_0" = [ "curl-sys/upkeep_7_62_0" ];
+          "windows-static-ssl" = [ "static-curl" "curl-sys/windows-static-ssl" ];
+          "zlib-ng-compat" = [ "curl-sys/zlib-ng-compat" "static-curl" ];
+        };
+        resolvedDefaultFeatures = [ "default" "openssl-probe" "openssl-sys" "ssl" ];
+      };
+      "curl-sys" = rec {
+        crateName = "curl-sys";
+        version = "0.4.72+curl-8.6.0";
+        edition = "2018";
+        links = "curl";
+        sha256 = "1sn97cah732ldcwkw5knm6kh57hx0gfxqmniiwgd2iy42j1xrjr9";
+        libName = "curl_sys";
+        libPath = "lib.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "libz-sys";
+            packageId = "libz-sys";
+            usesDefaultFeatures = false;
+            features = [ "libc" ];
+          }
+          {
+            name = "openssl-sys";
+            packageId = "openssl-sys";
+            optional = true;
+            target = { target, features }: ((target."unix" or false) && (!("macos" == target."os" or null)));
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Networking_WinSock" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+          {
+            name = "vcpkg";
+            packageId = "vcpkg";
+            target = { target, features }: ("msvc" == target."env" or null);
+          }
+        ];
+        features = {
+          "default" = [ "ssl" ];
+          "http2" = [ "libnghttp2-sys" ];
+          "libnghttp2-sys" = [ "dep:libnghttp2-sys" ];
+          "openssl-sys" = [ "dep:openssl-sys" ];
+          "rustls" = [ "rustls-ffi" ];
+          "rustls-ffi" = [ "dep:rustls-ffi" ];
+          "ssl" = [ "openssl-sys" ];
+          "static-ssl" = [ "openssl-sys/vendored" ];
+          "zlib-ng-compat" = [ "libz-sys/zlib-ng" "static-curl" ];
+        };
+        resolvedDefaultFeatures = [ "openssl-sys" "ssl" ];
+      };
+      "diesel" = rec {
+        crateName = "diesel";
+        version = "1.4.8";
+        edition = "2015";
+        sha256 = "0kcfkfhsv5yv3ksj440ajgic930359i2bqi77ss4dm5pyvn3b0dj";
+        authors = [
+          "Sean Griffin <sean@seantheprogrammer.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+            optional = true;
+          }
+          {
+            name = "byteorder";
+            packageId = "byteorder";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            optional = true;
+          }
+          {
+            name = "diesel_derives";
+            packageId = "diesel_derives";
+          }
+          {
+            name = "pq-sys";
+            packageId = "pq-sys";
+            optional = true;
+          }
+          {
+            name = "r2d2";
+            packageId = "r2d2";
+            optional = true;
+          }
+        ];
+        features = {
+          "128-column-tables" = [ "64-column-tables" ];
+          "64-column-tables" = [ "32-column-tables" ];
+          "bigdecimal" = [ "dep:bigdecimal" ];
+          "bitflags" = [ "dep:bitflags" ];
+          "chrono" = [ "dep:chrono" ];
+          "default" = [ "with-deprecated" "32-column-tables" ];
+          "deprecated-time" = [ "time" ];
+          "extras" = [ "chrono" "serde_json" "uuid" "deprecated-time" "network-address" "numeric" "r2d2" ];
+          "huge-tables" = [ "64-column-tables" ];
+          "ipnetwork" = [ "dep:ipnetwork" ];
+          "large-tables" = [ "32-column-tables" ];
+          "libc" = [ "dep:libc" ];
+          "libsqlite3-sys" = [ "dep:libsqlite3-sys" ];
+          "mysql" = [ "mysqlclient-sys" "url" "diesel_derives/mysql" ];
+          "mysqlclient-sys" = [ "dep:mysqlclient-sys" ];
+          "network-address" = [ "ipnetwork" "libc" ];
+          "num-bigint" = [ "dep:num-bigint" ];
+          "num-integer" = [ "dep:num-integer" ];
+          "num-traits" = [ "dep:num-traits" ];
+          "numeric" = [ "num-bigint" "bigdecimal" "num-traits" "num-integer" ];
+          "postgres" = [ "pq-sys" "bitflags" "diesel_derives/postgres" ];
+          "pq-sys" = [ "dep:pq-sys" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "r2d2" = [ "dep:r2d2" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "sqlite" = [ "libsqlite3-sys" "diesel_derives/sqlite" ];
+          "time" = [ "dep:time" ];
+          "unstable" = [ "diesel_derives/nightly" ];
+          "url" = [ "dep:url" ];
+          "uuid" = [ "dep:uuid" ];
+          "uuidv07" = [ "dep:uuidv07" ];
+          "x128-column-tables" = [ "128-column-tables" ];
+          "x32-column-tables" = [ "32-column-tables" ];
+          "x64-column-tables" = [ "64-column-tables" ];
+        };
+        resolvedDefaultFeatures = [ "32-column-tables" "bitflags" "chrono" "default" "postgres" "pq-sys" "r2d2" "with-deprecated" ];
+      };
+      "diesel_derives" = rec {
+        crateName = "diesel_derives";
+        version = "1.4.1";
+        edition = "2015";
+        sha256 = "1lsq133fwk0zj8xvxhdxqgg0xs31zf3abnwdyshaf0ldca7hkxa5";
+        procMacro = true;
+        authors = [
+          "Sean Griffin <sean@seantheprogrammer.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "full" "fold" ];
+          }
+        ];
+        features = {
+          "nightly" = [ "proc-macro2/nightly" ];
+        };
+        resolvedDefaultFeatures = [ "default" "postgres" ];
+      };
+      "encoding" = rec {
+        crateName = "encoding";
+        version = "0.2.33";
+        edition = "2015";
+        sha256 = "1v1ndmkarh9z3n5hk53da4z56hgk9wa5kcsm7cnx345raqw983bb";
+        authors = [
+          "Kang Seonghoon <public+rust@mearie.org>"
+        ];
+        dependencies = [
+          {
+            name = "encoding-index-japanese";
+            packageId = "encoding-index-japanese";
+          }
+          {
+            name = "encoding-index-korean";
+            packageId = "encoding-index-korean";
+          }
+          {
+            name = "encoding-index-simpchinese";
+            packageId = "encoding-index-simpchinese";
+          }
+          {
+            name = "encoding-index-singlebyte";
+            packageId = "encoding-index-singlebyte";
+          }
+          {
+            name = "encoding-index-tradchinese";
+            packageId = "encoding-index-tradchinese";
+          }
+        ];
+
+      };
+      "encoding-index-japanese" = rec {
+        crateName = "encoding-index-japanese";
+        version = "1.20141219.5";
+        edition = "2015";
+        sha256 = "148c1lmd640p1d7fzk0nv7892mbyavvwddgqvcsm78798bzv5s04";
+        libName = "encoding_index_japanese";
+        libPath = "lib.rs";
+        authors = [
+          "Kang Seonghoon <public+rust@mearie.org>"
+        ];
+        dependencies = [
+          {
+            name = "encoding_index_tests";
+            packageId = "encoding_index_tests";
+          }
+        ];
+
+      };
+      "encoding-index-korean" = rec {
+        crateName = "encoding-index-korean";
+        version = "1.20141219.5";
+        edition = "2015";
+        sha256 = "10cxabp5ppygbq4y6y680856zl9zjvq7ahpiw8zj3fmwwsw3zhsd";
+        libName = "encoding_index_korean";
+        libPath = "lib.rs";
+        authors = [
+          "Kang Seonghoon <public+rust@mearie.org>"
+        ];
+        dependencies = [
+          {
+            name = "encoding_index_tests";
+            packageId = "encoding_index_tests";
+          }
+        ];
+
+      };
+      "encoding-index-simpchinese" = rec {
+        crateName = "encoding-index-simpchinese";
+        version = "1.20141219.5";
+        edition = "2015";
+        sha256 = "1xria2i7mc5dqdrpqxasdbxv1qx46jjbm53if3y1i4cvj2a72ynq";
+        libName = "encoding_index_simpchinese";
+        libPath = "lib.rs";
+        authors = [
+          "Kang Seonghoon <public+rust@mearie.org>"
+        ];
+        dependencies = [
+          {
+            name = "encoding_index_tests";
+            packageId = "encoding_index_tests";
+          }
+        ];
+
+      };
+      "encoding-index-singlebyte" = rec {
+        crateName = "encoding-index-singlebyte";
+        version = "1.20141219.5";
+        edition = "2015";
+        sha256 = "0jp85bz2pprzvg9m95w4q0vibh67b6w3bx35lafay95jzyndal9k";
+        libName = "encoding_index_singlebyte";
+        libPath = "lib.rs";
+        authors = [
+          "Kang Seonghoon <public+rust@mearie.org>"
+        ];
+        dependencies = [
+          {
+            name = "encoding_index_tests";
+            packageId = "encoding_index_tests";
+          }
+        ];
+
+      };
+      "encoding-index-tradchinese" = rec {
+        crateName = "encoding-index-tradchinese";
+        version = "1.20141219.5";
+        edition = "2015";
+        sha256 = "060ci4iz6xfvzk38syfbjvs7pix5hch3mvxkksswmqwcd3aj03px";
+        libName = "encoding_index_tradchinese";
+        libPath = "lib.rs";
+        authors = [
+          "Kang Seonghoon <public+rust@mearie.org>"
+        ];
+        dependencies = [
+          {
+            name = "encoding_index_tests";
+            packageId = "encoding_index_tests";
+          }
+        ];
+
+      };
+      "encoding_index_tests" = rec {
+        crateName = "encoding_index_tests";
+        version = "0.1.4";
+        edition = "2015";
+        sha256 = "0s85y091gl17ixass49bzaivng7w8p82p6nyvz2r3my9w4mxhim2";
+        libPath = "index_tests.rs";
+        authors = [
+          "Kang Seonghoon <public+rust@mearie.org>"
+        ];
+
+      };
+      "env_logger" = rec {
+        crateName = "env_logger";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "0djx8h8xfib43g5w94r1m1mkky5spcw4wblzgnhiyg5vnfxknls4";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "atty";
+            packageId = "atty";
+            optional = true;
+          }
+          {
+            name = "humantime";
+            packageId = "humantime";
+            optional = true;
+          }
+          {
+            name = "log";
+            packageId = "log";
+            features = [ "std" ];
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "termcolor";
+            packageId = "termcolor";
+            optional = true;
+          }
+        ];
+        features = {
+          "atty" = [ "dep:atty" ];
+          "default" = [ "termcolor" "atty" "humantime" "regex" ];
+          "humantime" = [ "dep:humantime" ];
+          "regex" = [ "dep:regex" ];
+          "termcolor" = [ "dep:termcolor" ];
+        };
+        resolvedDefaultFeatures = [ "atty" "default" "humantime" "regex" "termcolor" ];
+      };
+      "errno" = rec {
+        crateName = "errno";
+        version = "0.3.8";
+        edition = "2018";
+        sha256 = "0ia28ylfsp36i27g1qih875cyyy4by2grf80ki8vhgh6vinf8n52";
+        authors = [
+          "Chris Wong <lambda.fairy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_System_Diagnostics_Debug" ];
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "libc/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "failure" = rec {
+        crateName = "failure";
+        version = "0.1.8";
+        edition = "2015";
+        sha256 = "11jg1wmbkijrs6bk9fqnbrm9zf0850whnqpgnxyswbn0dk8rnbnk";
+        authors = [
+          "Without Boats <boats@mozilla.com>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            optional = true;
+          }
+          {
+            name = "failure_derive";
+            packageId = "failure_derive";
+            optional = true;
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "default" = [ "std" "derive" ];
+          "derive" = [ "failure_derive" ];
+          "failure_derive" = [ "dep:failure_derive" ];
+          "std" = [ "backtrace" ];
+        };
+        resolvedDefaultFeatures = [ "backtrace" "default" "derive" "failure_derive" "std" ];
+      };
+      "failure_derive" = rec {
+        crateName = "failure_derive";
+        version = "0.1.8";
+        edition = "2015";
+        sha256 = "1936adqqk080439kx2bjf1bds7h89sg6wcif4jw0syndcv3s6kda";
+        procMacro = true;
+        authors = [
+          "Without Boats <woboats@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+          }
+          {
+            name = "synstructure";
+            packageId = "synstructure";
+          }
+        ];
+        features = { };
+      };
+      "fastrand" = rec {
+        crateName = "fastrand";
+        version = "2.0.1";
+        edition = "2018";
+        sha256 = "19flpv5zbzpf0rk4x77z4zf25in0brg8l7m304d3yrf47qvwxjr5";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "getrandom" = [ "dep:getrandom" ];
+          "js" = [ "std" "getrandom" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "fnv" = rec {
+        crateName = "fnv";
+        version = "1.0.7";
+        edition = "2015";
+        sha256 = "1hc2mcqha06aibcaza94vbi81j6pr9a1bbxrxjfhc91zin8yr7iz";
+        libPath = "lib.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "foreign-types" = rec {
+        crateName = "foreign-types";
+        version = "0.3.2";
+        edition = "2015";
+        sha256 = "1cgk0vyd7r45cj769jym4a6s7vwshvd0z4bqrb92q1fwibmkkwzn";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "foreign-types-shared";
+            packageId = "foreign-types-shared";
+          }
+        ];
+
+      };
+      "foreign-types-shared" = rec {
+        crateName = "foreign-types-shared";
+        version = "0.1.1";
+        edition = "2015";
+        sha256 = "0jxgzd04ra4imjv8jgkmdq59kj8fsz6w4zxsbmlai34h26225c00";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+
+      };
+      "fuchsia-zircon" = rec {
+        crateName = "fuchsia-zircon";
+        version = "0.3.3";
+        edition = "2015";
+        sha256 = "10jxc5ks1x06gpd0xg51kcjrxr35nj6qhx2zlc5n7bmskv3675rf";
+        authors = [
+          "Raph Levien <raph@google.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "fuchsia-zircon-sys";
+            packageId = "fuchsia-zircon-sys";
+          }
+        ];
+
+      };
+      "fuchsia-zircon-sys" = rec {
+        crateName = "fuchsia-zircon-sys";
+        version = "0.3.3";
+        edition = "2015";
+        sha256 = "19zp2085qsyq2bh1gvcxq1lb8w6v6jj9kbdkhpdjrl95fypakjix";
+        authors = [
+          "Raph Levien <raph@google.com>"
+        ];
+
+      };
+      "futures" = rec {
+        crateName = "futures";
+        version = "0.1.31";
+        edition = "2015";
+        sha256 = "0y46qbmhi37dqkch8dlfq5aninqpzqgrr98awkb3rn4fxww1lirs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "default" = [ "use_std" "with-deprecated" ];
+        };
+        resolvedDefaultFeatures = [ "default" "use_std" "with-deprecated" ];
+      };
+      "getrandom" = rec {
+        crateName = "getrandom";
+        version = "0.1.16";
+        edition = "2018";
+        sha256 = "1kjzmz60qx9mn615ks1akjbf36n3lkv27zfwbcam0fzmj56wphwg";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 1.0.0";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+        ];
+        features = {
+          "bindgen" = [ "dep:bindgen" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "log" = [ "dep:log" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" ];
+          "stdweb" = [ "dep:stdweb" ];
+          "test-in-browser" = [ "wasm-bindgen" ];
+          "wasm-bindgen" = [ "bindgen" "js-sys" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "gimli" = rec {
+        crateName = "gimli";
+        version = "0.28.1";
+        edition = "2018";
+        sha256 = "0lv23wc8rxvmjia3mcxc6hj9vkqnv1bqq0h8nzjcgf71mrxx6wa2";
+        features = {
+          "default" = [ "read-all" "write" ];
+          "endian-reader" = [ "read" "dep:stable_deref_trait" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "read" = [ "read-core" ];
+          "read-all" = [ "read" "std" "fallible-iterator" "endian-reader" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" ];
+          "std" = [ "fallible-iterator?/std" "stable_deref_trait?/std" ];
+          "write" = [ "dep:indexmap" ];
+        };
+        resolvedDefaultFeatures = [ "read" "read-core" ];
+      };
+      "hermit-abi 0.1.19" = rec {
+        crateName = "hermit-abi";
+        version = "0.1.19";
+        edition = "2018";
+        sha256 = "0cxcm8093nf5fyn114w8vxbrbcyvv91d4015rdnlgfll7cs6gd32";
+        authors = [
+          "Stefan Lankes"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins/rustc-dep-of-std" "libc/rustc-dep-of-std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "hermit-abi 0.3.9" = rec {
+        crateName = "hermit-abi";
+        version = "0.3.9";
+        edition = "2021";
+        sha256 = "092hxjbjnq5fmz66grd9plxd0sh6ssg5fhgwwwqbrzgzkjwdycfj";
+        authors = [
+          "Stefan Lankes"
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins/rustc-dep-of-std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "humantime" = rec {
+        crateName = "humantime";
+        version = "1.3.0";
+        edition = "2015";
+        sha256 = "0krwgbf35pd46xvkqg14j070vircsndabahahlv3rwhflpy4q06z";
+        authors = [
+          "Paul Colomiets <paul@colomiets.name>"
+        ];
+        dependencies = [
+          {
+            name = "quick-error";
+            packageId = "quick-error";
+          }
+        ];
+
+      };
+      "iana-time-zone" = rec {
+        crateName = "iana-time-zone";
+        version = "0.1.60";
+        edition = "2018";
+        sha256 = "0hdid5xz3jznm04lysjm3vi93h3c523w0hcc3xba47jl3ddbpzz7";
+        authors = [
+          "Andrew Straw <strawman@astraw.com>"
+          "RenΓ© Kijewski <rene.kijewski@fu-berlin.de>"
+          "Ryan Lopopolo <rjl@hyperbo.la>"
+        ];
+        dependencies = [
+          {
+            name = "android_system_properties";
+            packageId = "android_system_properties";
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "iana-time-zone-haiku";
+            packageId = "iana-time-zone-haiku";
+            target = { target, features }: ("haiku" == target."os" or null);
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "windows-core";
+            packageId = "windows-core";
+            target = { target, features }: ("windows" == target."os" or null);
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "fallback" ];
+      };
+      "iana-time-zone-haiku" = rec {
+        crateName = "iana-time-zone-haiku";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        authors = [
+          "RenΓ© Kijewski <crates.io@k6i.de>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "iovec" = rec {
+        crateName = "iovec";
+        version = "0.1.4";
+        edition = "2015";
+        sha256 = "0ph73qygwx8i0mblrf110cj59l00gkmsgrpzz1rm85syz5pymcxj";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+
+      };
+      "irc" = rec {
+        crateName = "irc";
+        version = "0.13.6";
+        edition = "2015";
+        sha256 = "0rmlaaay5gw46gxqg27lnrrrfy73pm3y6rs4hxxwfpg9k9n6ddwf";
+        authors = [
+          "Aaron Weiss <awe@pdgn.co>"
+        ];
+        dependencies = [
+          {
+            name = "bufstream";
+            packageId = "bufstream";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+          }
+          {
+            name = "encoding";
+            packageId = "encoding";
+          }
+          {
+            name = "failure";
+            packageId = "failure";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "native-tls";
+            packageId = "native-tls";
+          }
+          {
+            name = "serde";
+            packageId = "serde 1.0.197";
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+          {
+            name = "tokio-codec";
+            packageId = "tokio-codec";
+          }
+          {
+            name = "tokio-core";
+            packageId = "tokio-core";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+          {
+            name = "tokio-mockstream";
+            packageId = "tokio-mockstream";
+          }
+          {
+            name = "tokio-timer";
+            packageId = "tokio-timer 0.1.2";
+          }
+          {
+            name = "tokio-tls";
+            packageId = "tokio-tls";
+          }
+          {
+            name = "toml";
+            packageId = "toml";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "ctcp" "toml" ];
+          "json" = [ "serde_json" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "serde_yaml" = [ "dep:serde_yaml" ];
+          "toml" = [ "dep:toml" ];
+          "yaml" = [ "serde_yaml" ];
+        };
+        resolvedDefaultFeatures = [ "ctcp" "default" "toml" ];
+      };
+      "itoa" = rec {
+        crateName = "itoa";
+        version = "1.0.10";
+        edition = "2018";
+        sha256 = "0k7xjfki7mnv6yzjrbnbnjllg86acmbnk4izz2jmm1hx2wd6v95i";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "js-sys" = rec {
+        crateName = "js-sys";
+        version = "0.3.69";
+        edition = "2018";
+        sha256 = "0v99rz97asnzapb0jsc3jjhvxpfxr7h7qd97yqyrf9i7viimbh99";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+        ];
+
+      };
+      "kernel32-sys" = rec {
+        crateName = "kernel32-sys";
+        version = "0.2.2";
+        edition = "2015";
+        sha256 = "1389av0601a9yz8dvx5zha9vmkd6ik7ax0idpb032d28555n41vm";
+        libName = "kernel32";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi";
+            packageId = "winapi 0.2.8";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "winapi-build";
+            packageId = "winapi-build";
+          }
+        ];
+
+      };
+      "lazy_static 0.2.11" = rec {
+        crateName = "lazy_static";
+        version = "0.2.11";
+        edition = "2015";
+        sha256 = "0wxy8vak7jsx6r8gx475pjqpx11p2bfq4wvw6idmqi31mp3k7w3n";
+        authors = [
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        features = {
+          "compiletest" = [ "compiletest_rs" ];
+          "compiletest_rs" = [ "dep:compiletest_rs" ];
+          "spin" = [ "dep:spin" ];
+          "spin_no_std" = [ "nightly" "spin" ];
+        };
+      };
+      "lazy_static 1.4.0" = rec {
+        crateName = "lazy_static";
+        version = "1.4.0";
+        edition = "2015";
+        sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2";
+        authors = [
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        features = {
+          "spin" = [ "dep:spin" ];
+          "spin_no_std" = [ "spin" ];
+        };
+      };
+      "libc" = rec {
+        crateName = "libc";
+        version = "0.2.153";
+        edition = "2015";
+        sha256 = "1gg7m1ils5dms5miq9fyllrcp0jxnbpgkx71chd2i0lafa8qy6cw";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ];
+          "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "extra_traits" "std" ];
+      };
+      "libz-sys" = rec {
+        crateName = "libz-sys";
+        version = "1.1.15";
+        edition = "2018";
+        links = "z";
+        sha256 = "1xj89rjhk642x8271xr9phj7da7ivwyvd5g8fmb7ma5asgsk2xq3";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Josh Triplett <josh@joshtriplett.org>"
+          "Sebastian Thiel <sebastian.thiel@icloud.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+          {
+            name = "vcpkg";
+            packageId = "vcpkg";
+          }
+        ];
+        features = {
+          "cmake" = [ "dep:cmake" ];
+          "default" = [ "libc" "stock-zlib" ];
+          "libc" = [ "dep:libc" ];
+          "zlib-ng" = [ "libc" "cmake" ];
+        };
+        resolvedDefaultFeatures = [ "libc" ];
+      };
+      "linked-hash-map 0.3.0" = rec {
+        crateName = "linked-hash-map";
+        version = "0.3.0";
+        edition = "2015";
+        sha256 = "1kaf95grvfqchxn8pl0854g8ab0fzl56217hndhhhz5qqm2j09kd";
+        authors = [
+          "Stepan Koltsov <stepan.koltsov@gmail.com>"
+          "Andrew Paseltiner <apaseltiner@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde 0.8.23";
+            optional = true;
+          }
+          {
+            name = "serde_test";
+            packageId = "serde_test";
+            optional = true;
+          }
+        ];
+        features = {
+          "clippy" = [ "dep:clippy" ];
+          "serde" = [ "dep:serde" ];
+          "serde_impl" = [ "serde" "serde_test" ];
+          "serde_test" = [ "dep:serde_test" ];
+        };
+        resolvedDefaultFeatures = [ "serde" "serde_impl" "serde_test" ];
+      };
+      "linked-hash-map 0.5.6" = rec {
+        crateName = "linked-hash-map";
+        version = "0.5.6";
+        edition = "2015";
+        sha256 = "03vpgw7x507g524nx5i1jf5dl8k3kv0fzg8v3ip6qqwbpkqww5q7";
+        authors = [
+          "Stepan Koltsov <stepan.koltsov@gmail.com>"
+          "Andrew Paseltiner <apaseltiner@gmail.com>"
+        ];
+        features = {
+          "heapsize" = [ "dep:heapsize" ];
+          "heapsize_impl" = [ "heapsize" ];
+          "serde" = [ "dep:serde" ];
+          "serde_impl" = [ "serde" ];
+        };
+      };
+      "linux-raw-sys" = rec {
+        crateName = "linux-raw-sys";
+        version = "0.4.13";
+        edition = "2021";
+        sha256 = "172k2c6422gsc914ig8rh99mb9yc7siw6ikc3d9xw1k7vx0s3k81";
+        authors = [
+          "Dan Gohman <dev@sunfishcode.online>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" "general" "errno" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "no_std" ];
+        };
+        resolvedDefaultFeatures = [ "elf" "errno" "general" "ioctl" "no_std" ];
+      };
+      "lock_api 0.3.4" = rec {
+        crateName = "lock_api";
+        version = "0.3.4";
+        edition = "2018";
+        sha256 = "0xgc5dzmajh0akbh5d6d7rj9mh5rzpk74pyrc946v2ixgakj9nn4";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "owning_ref" = [ "dep:owning_ref" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "lock_api 0.4.11" = rec {
+        crateName = "lock_api";
+        version = "0.4.11";
+        edition = "2018";
+        sha256 = "0iggx0h4jx63xm35861106af3jkxq06fpqhpkhgw0axi2n38y5iw";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "atomic_usize" ];
+          "owning_ref" = [ "dep:owning_ref" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "atomic_usize" "default" ];
+      };
+      "log" = rec {
+        crateName = "log";
+        version = "0.4.21";
+        edition = "2021";
+        sha256 = "074hldq1q8rlzq2s2qa8f25hj4s3gpw71w64vdwzjd01a4g8rvch";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "kv_serde" = [ "kv_std" "value-bag/serde" "serde" ];
+          "kv_std" = [ "std" "kv" "value-bag/error" ];
+          "kv_sval" = [ "kv" "value-bag/sval" "sval" "sval_ref" ];
+          "kv_unstable" = [ "kv" "value-bag" ];
+          "kv_unstable_serde" = [ "kv_serde" "kv_unstable_std" ];
+          "kv_unstable_std" = [ "kv_std" "kv_unstable" ];
+          "kv_unstable_sval" = [ "kv_sval" "kv_unstable" ];
+          "serde" = [ "dep:serde" ];
+          "sval" = [ "dep:sval" ];
+          "sval_ref" = [ "dep:sval_ref" ];
+          "value-bag" = [ "dep:value-bag" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "maybe-uninit" = rec {
+        crateName = "maybe-uninit";
+        version = "2.0.0";
+        edition = "2015";
+        sha256 = "004y0nzmpfdrhz251278341z6ql34iv1k6dp1h6af7d6nd6jwc30";
+        authors = [
+          "est31 <MTest31@outlook.com>"
+          "The Rust Project Developers"
+        ];
+
+      };
+      "memchr" = rec {
+        crateName = "memchr";
+        version = "2.7.1";
+        edition = "2021";
+        sha256 = "0jf1kicqa4vs9lyzj4v4y1p90q0dh87hvhsdd5xvhnp527sw8gaj";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+          "bluss"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "logging" = [ "dep:log" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "std" = [ "alloc" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" "use_std" ];
+      };
+      "memoffset" = rec {
+        crateName = "memoffset";
+        version = "0.5.6";
+        edition = "2015";
+        sha256 = "1ahi51aa650s2p9ib1a4ifgqv0pzmsxlm9z4xdgvi9zdd7q7ac84";
+        authors = [
+          "Gilad Naaman <gilad.naaman@gmail.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "miniz_oxide" = rec {
+        crateName = "miniz_oxide";
+        version = "0.7.2";
+        edition = "2018";
+        sha256 = "19qlxb21s6kabgqq61mk7kd1qk2invyygj076jz6i1gj2lz1z0cx";
+        authors = [
+          "Frommi <daniil.liferenko@gmail.com>"
+          "oyvindln <oyvindln@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "adler";
+            packageId = "adler";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "with-alloc" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "adler/rustc-dep-of-std" ];
+          "simd" = [ "simd-adler32" ];
+          "simd-adler32" = [ "dep:simd-adler32" ];
+        };
+      };
+      "mio" = rec {
+        crateName = "mio";
+        version = "0.6.23";
+        edition = "2015";
+        sha256 = "1i2c1vl8lr45apkh8xbh9k56ihfsmqff5l7s2fya7whvp7sndzaa";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 0.1.10";
+          }
+          {
+            name = "fuchsia-zircon";
+            packageId = "fuchsia-zircon";
+            target = { target, features }: ("fuchsia" == target."os" or null);
+          }
+          {
+            name = "fuchsia-zircon-sys";
+            packageId = "fuchsia-zircon-sys";
+            target = { target, features }: ("fuchsia" == target."os" or null);
+          }
+          {
+            name = "iovec";
+            packageId = "iovec";
+          }
+          {
+            name = "kernel32-sys";
+            packageId = "kernel32-sys";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "miow";
+            packageId = "miow";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "net2";
+            packageId = "net2";
+          }
+          {
+            name = "slab";
+            packageId = "slab 0.4.9";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi 0.2.8";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "default" = [ "with-deprecated" ];
+        };
+        resolvedDefaultFeatures = [ "default" "with-deprecated" ];
+      };
+      "mio-uds" = rec {
+        crateName = "mio-uds";
+        version = "0.6.8";
+        edition = "2015";
+        sha256 = "1w36w09gd8as1mah80wdy0kgpshmphmljj68gij34hvdnag6kjxg";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "iovec";
+            packageId = "iovec";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+
+      };
+      "miow" = rec {
+        crateName = "miow";
+        version = "0.2.2";
+        edition = "2015";
+        sha256 = "0kcl8rnv0bhiarcdakik670w8fnxzlxhi1ys7152sck68510in7b";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "kernel32-sys";
+            packageId = "kernel32-sys";
+          }
+          {
+            name = "net2";
+            packageId = "net2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "winapi";
+            packageId = "winapi 0.2.8";
+          }
+          {
+            name = "ws2_32-sys";
+            packageId = "ws2_32-sys";
+          }
+        ];
+
+      };
+      "native-tls" = rec {
+        crateName = "native-tls";
+        version = "0.2.11";
+        edition = "2015";
+        sha256 = "0bmrlg0fmzxaycjpkgkchi93av07v2yf9k33gc12ca9gqdrn28h7";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lazy_static";
+            packageId = "lazy_static 1.4.0";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "log";
+            packageId = "log";
+            target = { target, features }: (!(("windows" == target."os" or null) || ("macos" == target."os" or null) || ("ios" == target."os" or null)));
+          }
+          {
+            name = "openssl";
+            packageId = "openssl";
+            target = { target, features }: (!(("windows" == target."os" or null) || ("macos" == target."os" or null) || ("ios" == target."os" or null)));
+          }
+          {
+            name = "openssl-probe";
+            packageId = "openssl-probe";
+            target = { target, features }: (!(("windows" == target."os" or null) || ("macos" == target."os" or null) || ("ios" == target."os" or null)));
+          }
+          {
+            name = "openssl-sys";
+            packageId = "openssl-sys";
+            target = { target, features }: (!(("windows" == target."os" or null) || ("macos" == target."os" or null) || ("ios" == target."os" or null)));
+          }
+          {
+            name = "schannel";
+            packageId = "schannel";
+            target = { target, features }: ("windows" == target."os" or null);
+          }
+          {
+            name = "security-framework";
+            packageId = "security-framework";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "security-framework-sys";
+            packageId = "security-framework-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+          }
+        ];
+        features = {
+          "alpn" = [ "security-framework/alpn" ];
+          "vendored" = [ "openssl/vendored" ];
+        };
+      };
+      "net2" = rec {
+        crateName = "net2";
+        version = "0.2.39";
+        edition = "2015";
+        sha256 = "1b1lxvs192xsvqnszcz7dn4dw3fsvzxnc23qvq39scx26s068fxi";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 0.1.10";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ((target."unix" or false) || ("wasi" == target."os" or null));
+          }
+          {
+            name = "winapi";
+            packageId = "winapi 0.3.9";
+            target = { target, features }: (target."windows" or false);
+            features = [ "handleapi" "winsock2" "ws2def" "ws2ipdef" "ws2tcpip" ];
+          }
+        ];
+        features = {
+          "default" = [ "duration" ];
+        };
+        resolvedDefaultFeatures = [ "default" "duration" ];
+      };
+      "nom" = rec {
+        crateName = "nom";
+        version = "4.2.3";
+        edition = "2015";
+        sha256 = "1mkvby8b4m61p4g1px0pwr58yfkphyp1jcfbp4qfp7l6iqdaklia";
+        authors = [
+          "contact@geoffroycouprie.com"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "lazy_static" = [ "dep:lazy_static" ];
+          "regex" = [ "dep:regex" ];
+          "regexp" = [ "regex" ];
+          "regexp_macros" = [ "regexp" "lazy_static" ];
+          "std" = [ "alloc" "memchr/use_std" ];
+          "verbose-errors" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "num-traits 0.1.43" = rec {
+        crateName = "num-traits";
+        version = "0.1.43";
+        edition = "2015";
+        sha256 = "0c9whknf2dm74a3cqirafy6gj83a76gl56g4v3g19k6lkwz13rcj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits 0.2.18";
+          }
+        ];
+
+      };
+      "num-traits 0.2.18" = rec {
+        crateName = "num-traits";
+        version = "0.2.18";
+        edition = "2018";
+        sha256 = "0yjib8p2p9kzmaz48xwhs69w5dh1wipph9jgnillzd2x33jz03fs";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "libm" = [ "dep:libm" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "num_cpus" = rec {
+        crateName = "num_cpus";
+        version = "1.16.0";
+        edition = "2015";
+        sha256 = "0hra6ihpnh06dvfvz9ipscys0xfqa9ca9hzp384d5m02ssvgqqa1";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi 0.3.9";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(target."windows" or false));
+          }
+        ];
+
+      };
+      "object" = rec {
+        crateName = "object";
+        version = "0.32.2";
+        edition = "2018";
+        sha256 = "0hc4cjwyngiy6k51hlzrlsxgv5z25vv7c2cp0ky1lckfic0259m6";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "all" = [ "read" "write" "std" "compression" "wasm" ];
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "compression" = [ "dep:flate2" "dep:ruzstd" "std" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "read" "compression" ];
+          "doc" = [ "read_core" "write_std" "std" "compression" "archive" "coff" "elf" "macho" "pe" "wasm" "xcoff" ];
+          "pe" = [ "coff" ];
+          "read" = [ "read_core" "archive" "coff" "elf" "macho" "pe" "xcoff" "unaligned" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "alloc" "memchr/rustc-dep-of-std" ];
+          "std" = [ "memchr/std" ];
+          "unstable-all" = [ "all" "unstable" ];
+          "wasm" = [ "dep:wasmparser" ];
+          "write" = [ "write_std" "coff" "elf" "macho" "pe" "xcoff" ];
+          "write_core" = [ "dep:crc32fast" "dep:indexmap" "dep:hashbrown" ];
+          "write_std" = [ "write_core" "std" "indexmap?/std" "crc32fast?/std" ];
+        };
+        resolvedDefaultFeatures = [ "archive" "coff" "elf" "macho" "pe" "read_core" "unaligned" ];
+      };
+      "once_cell" = rec {
+        crateName = "once_cell";
+        version = "1.19.0";
+        edition = "2021";
+        sha256 = "14kvw7px5z96dk4dwdm1r9cqhhy2cyj1l5n5b29mynbb8yr15nrz";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        features = {
+          "alloc" = [ "race" ];
+          "atomic-polyfill" = [ "critical-section" ];
+          "critical-section" = [ "dep:critical-section" "portable-atomic" ];
+          "default" = [ "std" ];
+          "parking_lot" = [ "dep:parking_lot_core" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "race" "std" ];
+      };
+      "openssl" = rec {
+        crateName = "openssl";
+        version = "0.10.64";
+        edition = "2018";
+        sha256 = "07vb455yh08qh3n493ssw1qsa3zg3zfj438kk2180453hq94i84m";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 1.0.0";
+          }
+          {
+            name = "foreign-types";
+            packageId = "foreign-types";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "openssl-macros";
+            packageId = "openssl-macros";
+          }
+          {
+            name = "openssl-sys";
+            packageId = "openssl-sys";
+            rename = "ffi";
+          }
+        ];
+        features = {
+          "bindgen" = [ "ffi/bindgen" ];
+          "unstable_boringssl" = [ "ffi/unstable_boringssl" ];
+          "vendored" = [ "ffi/vendored" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "openssl-macros" = rec {
+        crateName = "openssl-macros";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "173xxvfc63rr5ybwqwylsir0vq6xsj4kxiv4hmg4c3vscdmncj59";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.52";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "openssl-probe" = rec {
+        crateName = "openssl-probe";
+        version = "0.1.5";
+        edition = "2015";
+        sha256 = "1kq18qm48rvkwgcggfkqq6pm948190czqc94d6bm2sir5hq1l0gz";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+
+      };
+      "openssl-sys" = rec {
+        crateName = "openssl-sys";
+        version = "0.9.101";
+        edition = "2018";
+        links = "openssl";
+        sha256 = "1zwd35nc5bq7m26vjsmja4hxf3fzk389blgpmhpzr3p78krv18nx";
+        build = "build/main.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+          {
+            name = "vcpkg";
+            packageId = "vcpkg";
+          }
+        ];
+        features = {
+          "bindgen" = [ "dep:bindgen" ];
+          "bssl-sys" = [ "dep:bssl-sys" ];
+          "openssl-src" = [ "dep:openssl-src" ];
+          "unstable_boringssl" = [ "bssl-sys" ];
+          "vendored" = [ "openssl-src" ];
+        };
+      };
+      "parking_lot 0.12.1" = rec {
+        crateName = "parking_lot";
+        version = "0.12.1";
+        edition = "2018";
+        sha256 = "13r2xk7mnxfc5g0g6dkdxqdqad99j7s7z8zhzz4npw5r0g0v4hip";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lock_api";
+            packageId = "lock_api 0.4.11";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core 0.9.9";
+          }
+        ];
+        features = {
+          "arc_lock" = [ "lock_api/arc_lock" ];
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot 0.9.0" = rec {
+        crateName = "parking_lot";
+        version = "0.9.0";
+        edition = "2018";
+        sha256 = "0lk2vq3hp88ygpgsrypdr3ss71fidnqbykva0csgxhmn5scb2hpq";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lock_api";
+            packageId = "lock_api 0.3.4";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core 0.6.3";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot_core 0.6.3" = rec {
+        crateName = "parking_lot_core";
+        version = "0.6.3";
+        edition = "2018";
+        sha256 = "02kbwqrr0w5mw0hkklqcg35aaiq1cck3g1w0d8bpbgk21a0np9mx";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 0.1.10";
+          }
+          {
+            name = "cloudabi";
+            packageId = "cloudabi";
+            target = { target, features }: ("cloudabi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.1.57";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec 0.6.14";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi 0.3.9";
+            target = { target, features }: (target."windows" or false);
+            features = [ "winnt" "ntstatus" "minwindef" "winerror" "winbase" "errhandlingapi" "handleapi" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "parking_lot_core 0.9.9" = rec {
+        crateName = "parking_lot_core";
+        version = "0.9.9";
+        edition = "2018";
+        sha256 = "13h0imw1aq86wj28gxkblhkzx6z1gk8q18n0v76qmmj6cliajhjc";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 1.0.0";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.4.1";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec 1.13.1";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "paroxysm" = rec {
+        crateName = "paroxysm";
+        version = "0.1.0";
+        edition = "2018";
+        crateBin = [
+          {
+            name = "paroxysm";
+            path = "src/main.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./.; }
+          else ./.;
+        authors = [
+          "eeeeeta <eta@theta.eu.org>"
+        ];
+        dependencies = [
+          {
+            name = "chrono";
+            packageId = "chrono";
+          }
+          {
+            name = "config";
+            packageId = "config";
+          }
+          {
+            name = "crimp";
+            packageId = "crimp";
+          }
+          {
+            name = "diesel";
+            packageId = "diesel";
+            features = [ "postgres" "chrono" "r2d2" ];
+          }
+          {
+            name = "env_logger";
+            packageId = "env_logger";
+          }
+          {
+            name = "failure";
+            packageId = "failure";
+          }
+          {
+            name = "irc";
+            packageId = "irc";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static 1.4.0";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "serde";
+            packageId = "serde 1.0.197";
+            features = [ "derive" ];
+          }
+        ];
+
+      };
+      "pkg-config" = rec {
+        crateName = "pkg-config";
+        version = "0.3.30";
+        edition = "2015";
+        sha256 = "1v07557dj1sa0aly9c90wsygc0i8xv5vnmyv0g94lpkvj8qb4cfj";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+
+      };
+      "ppv-lite86" = rec {
+        crateName = "ppv-lite86";
+        version = "0.2.17";
+        edition = "2018";
+        sha256 = "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v";
+        authors = [
+          "The CryptoCorrosion Contributors"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "simd" "std" ];
+      };
+      "pq-sys" = rec {
+        crateName = "pq-sys";
+        version = "0.4.8";
+        edition = "2015";
+        links = "pq";
+        sha256 = "1gfygvp69i5i6vxbi9qp2xaf75x09js9wy1hpl67r6fz4qj0bh1i";
+        libName = "pq_sys";
+        buildDependencies = [
+          {
+            name = "vcpkg";
+            packageId = "vcpkg";
+            target = { target, features }: ("msvc" == target."env" or null);
+          }
+        ];
+        features = {
+          "pkg-config" = [ "dep:pkg-config" ];
+        };
+      };
+      "proc-macro2" = rec {
+        crateName = "proc-macro2";
+        version = "1.0.78";
+        edition = "2021";
+        sha256 = "1bjak27pqdn4f4ih1c9nr3manzyavsgqmf76ygw9k76q8pb2lhp2";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "quick-error" = rec {
+        crateName = "quick-error";
+        version = "1.2.3";
+        edition = "2015";
+        sha256 = "1q6za3v78hsspisc197bg3g7rpc989qycy8ypr8ap8igv10ikl51";
+        authors = [
+          "Paul Colomiets <paul@colomiets.name>"
+          "Colin Kiegel <kiegel@gmx.de>"
+        ];
+
+      };
+      "quote" = rec {
+        crateName = "quote";
+        version = "1.0.35";
+        edition = "2018";
+        sha256 = "1vv8r2ncaz4pqdr78x7f138ka595sp2ncr1sa2plm4zxbsmwj7i9";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "r2d2" = rec {
+        crateName = "r2d2";
+        version = "0.8.10";
+        edition = "2018";
+        sha256 = "14qw32y4m564xb1f5ya8ii7dwqyknvk8bsx2r0lljlmn7zxqbpji";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot 0.12.1";
+          }
+          {
+            name = "scheduled-thread-pool";
+            packageId = "scheduled-thread-pool";
+          }
+        ];
+
+      };
+      "rand" = rec {
+        crateName = "rand";
+        version = "0.7.3";
+        edition = "2018";
+        sha256 = "00sdaimkbz491qgi6qxkv582yivl32m2jd401kzbn94vsiwicsva";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            rename = "getrandom_package";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "rand_chacha";
+            packageId = "rand_chacha";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!("emscripten" == target."os" or null));
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+          {
+            name = "rand_hc";
+            packageId = "rand_hc";
+            target = { target, features }: ("emscripten" == target."os" or null);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rand_hc";
+            packageId = "rand_hc";
+          }
+        ];
+        features = {
+          "alloc" = [ "rand_core/alloc" ];
+          "default" = [ "std" ];
+          "getrandom" = [ "getrandom_package" "rand_core/getrandom" ];
+          "getrandom_package" = [ "dep:getrandom_package" ];
+          "libc" = [ "dep:libc" ];
+          "log" = [ "dep:log" ];
+          "nightly" = [ "simd_support" ];
+          "packed_simd" = [ "dep:packed_simd" ];
+          "rand_pcg" = [ "dep:rand_pcg" ];
+          "simd_support" = [ "packed_simd" ];
+          "small_rng" = [ "rand_pcg" ];
+          "std" = [ "rand_core/std" "rand_chacha/std" "alloc" "getrandom" "libc" ];
+          "stdweb" = [ "getrandom_package/stdweb" ];
+          "wasm-bindgen" = [ "getrandom_package/wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "getrandom" "getrandom_package" "libc" "std" ];
+      };
+      "rand_chacha" = rec {
+        crateName = "rand_chacha";
+        version = "0.2.2";
+        edition = "2018";
+        sha256 = "00il36fkdbsmpr99p9ksmmp6dn1md7rmnwmz0rr77jbrca2yvj7l";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+          "The CryptoCorrosion Contributors"
+        ];
+        dependencies = [
+          {
+            name = "ppv-lite86";
+            packageId = "ppv-lite86";
+            usesDefaultFeatures = false;
+            features = [ "simd" ];
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "default" = [ "std" "simd" ];
+          "std" = [ "ppv-lite86/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "rand_core" = rec {
+        crateName = "rand_core";
+        version = "0.5.1";
+        edition = "2018";
+        sha256 = "06bdvx08v3rkz451cm7z59xwwqn1rkfh6v9ay77b14f8dwlybgch";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+        ];
+        features = {
+          "getrandom" = [ "dep:getrandom" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "alloc" "getrandom" "getrandom/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "getrandom" "std" ];
+      };
+      "rand_hc" = rec {
+        crateName = "rand_hc";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "0g31sqwpmsirdlwr0svnacr4dbqyz339im4ssl9738cjgfpjjcfa";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+
+      };
+      "redox_syscall 0.1.57" = rec {
+        crateName = "redox_syscall";
+        version = "0.1.57";
+        edition = "2015";
+        sha256 = "1kh59fpwy33w9nwd5iyc283yglq8pf2s41hnhvl48iax9mz0zk21";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+
+      };
+      "redox_syscall 0.4.1" = rec {
+        crateName = "redox_syscall";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1aiifyz5dnybfvkk4cdab9p2kmphag1yad6iknc7aszlxxldf8j7";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+        features = {
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "bitflags/rustc-dep-of-std" ];
+        };
+      };
+      "regex" = rec {
+        crateName = "regex";
+        version = "1.10.3";
+        edition = "2021";
+        sha256 = "05cvihqy0wgnh9i8a9y2n803n5azg2h0b7nlqy6rsvxhy00vwbdn";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata";
+            usesDefaultFeatures = false;
+            features = [ "alloc" "syntax" "meta" "nfa-pikevm" ];
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf" "unicode" "regex-syntax/default" ];
+          "logging" = [ "aho-corasick?/logging" "memchr?/logging" "regex-automata/logging" ];
+          "perf" = [ "perf-cache" "perf-dfa" "perf-onepass" "perf-backtrack" "perf-inline" "perf-literal" ];
+          "perf-backtrack" = [ "regex-automata/nfa-backtrack" ];
+          "perf-dfa" = [ "regex-automata/hybrid" ];
+          "perf-dfa-full" = [ "regex-automata/dfa-build" "regex-automata/dfa-search" ];
+          "perf-inline" = [ "regex-automata/perf-inline" ];
+          "perf-literal" = [ "dep:aho-corasick" "dep:memchr" "regex-automata/perf-literal" ];
+          "perf-onepass" = [ "regex-automata/dfa-onepass" ];
+          "std" = [ "aho-corasick?/std" "memchr?/std" "regex-automata/std" "regex-syntax/std" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "regex-automata/unicode" "regex-syntax/unicode" ];
+          "unicode-age" = [ "regex-automata/unicode-age" "regex-syntax/unicode-age" ];
+          "unicode-bool" = [ "regex-automata/unicode-bool" "regex-syntax/unicode-bool" ];
+          "unicode-case" = [ "regex-automata/unicode-case" "regex-syntax/unicode-case" ];
+          "unicode-gencat" = [ "regex-automata/unicode-gencat" "regex-syntax/unicode-gencat" ];
+          "unicode-perl" = [ "regex-automata/unicode-perl" "regex-automata/unicode-word-boundary" "regex-syntax/unicode-perl" ];
+          "unicode-script" = [ "regex-automata/unicode-script" "regex-syntax/unicode-script" ];
+          "unicode-segment" = [ "regex-automata/unicode-segment" "regex-syntax/unicode-segment" ];
+          "unstable" = [ "pattern" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "perf" "perf-backtrack" "perf-cache" "perf-dfa" "perf-inline" "perf-literal" "perf-onepass" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "regex-automata" = rec {
+        crateName = "regex-automata";
+        version = "0.4.6";
+        edition = "2021";
+        sha256 = "1spaq7y4im7s56d1gxa2hi4hzf6dwswb1bv8xyavzya7k25kpf46";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "syntax" "perf" "unicode" "meta" "nfa" "dfa" "hybrid" ];
+          "dfa" = [ "dfa-build" "dfa-search" "dfa-onepass" ];
+          "dfa-build" = [ "nfa-thompson" "dfa-search" ];
+          "dfa-onepass" = [ "nfa-thompson" ];
+          "hybrid" = [ "alloc" "nfa-thompson" ];
+          "internal-instrument" = [ "internal-instrument-pikevm" ];
+          "internal-instrument-pikevm" = [ "logging" "std" ];
+          "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ];
+          "meta" = [ "syntax" "nfa-pikevm" ];
+          "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ];
+          "nfa-backtrack" = [ "nfa-thompson" ];
+          "nfa-pikevm" = [ "nfa-thompson" ];
+          "nfa-thompson" = [ "alloc" ];
+          "perf" = [ "perf-inline" "perf-literal" ];
+          "perf-literal" = [ "perf-literal-substring" "perf-literal-multisubstring" ];
+          "perf-literal-multisubstring" = [ "std" "dep:aho-corasick" ];
+          "perf-literal-substring" = [ "aho-corasick?/perf-literal" "dep:memchr" ];
+          "std" = [ "regex-syntax?/std" "memchr?/std" "aho-corasick?/std" "alloc" ];
+          "syntax" = [ "dep:regex-syntax" "alloc" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" "regex-syntax?/unicode" ];
+          "unicode-age" = [ "regex-syntax?/unicode-age" ];
+          "unicode-bool" = [ "regex-syntax?/unicode-bool" ];
+          "unicode-case" = [ "regex-syntax?/unicode-case" ];
+          "unicode-gencat" = [ "regex-syntax?/unicode-gencat" ];
+          "unicode-perl" = [ "regex-syntax?/unicode-perl" ];
+          "unicode-script" = [ "regex-syntax?/unicode-script" ];
+          "unicode-segment" = [ "regex-syntax?/unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "dfa-onepass" "hybrid" "meta" "nfa-backtrack" "nfa-pikevm" "nfa-thompson" "perf-inline" "perf-literal" "perf-literal-multisubstring" "perf-literal-substring" "std" "syntax" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" ];
+      };
+      "regex-syntax" = rec {
+        crateName = "regex-syntax";
+        version = "0.8.2";
+        edition = "2021";
+        sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" "unicode" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "rust-ini" = rec {
+        crateName = "rust-ini";
+        version = "0.13.0";
+        edition = "2015";
+        sha256 = "1hifnbgaz01zja5995chy6vjacbif2m76nlxsisw7y1pxx4c2liy";
+        libName = "ini";
+        authors = [
+          "Y. T. Chung <zonyitoo@gmail.com>"
+        ];
+
+      };
+      "rustc-demangle" = rec {
+        crateName = "rustc-demangle";
+        version = "0.1.23";
+        edition = "2015";
+        sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "rustc_version" = rec {
+        crateName = "rustc_version";
+        version = "0.2.3";
+        edition = "2015";
+        sha256 = "02h3x57lcr8l2pm0a645s9whdh33pn5cnrwvn5cb57vcrc53x3hk";
+        authors = [
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "semver";
+            packageId = "semver";
+          }
+        ];
+
+      };
+      "rustix" = rec {
+        crateName = "rustix";
+        version = "0.38.31";
+        edition = "2021";
+        sha256 = "0jg9yj3i6qnzk1y82hng7rb1bwhslfbh57507dxcs9mgcakf38vf";
+        authors = [
+          "Dan Gohman <dev@sunfishcode.online>"
+          "Jakub Konka <kubkon@jakubkonka.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."windows" or false)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+            features = [ "extra_traits" ];
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."windows" or false)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+            features = [ "extra_traits" ];
+          }
+          {
+            name = "linux-raw-sys";
+            packageId = "linux-raw-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((("android" == target."os" or null) || ("linux" == target."os" or null)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+            features = [ "general" "ioctl" "no_std" ];
+          }
+          {
+            name = "linux-raw-sys";
+            packageId = "linux-raw-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+            features = [ "general" "errno" "ioctl" "no_std" "elf" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_NetworkManagement_IpHelper" "Win32_System_Threading" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "all-apis" = [ "event" "fs" "io_uring" "mm" "mount" "net" "param" "pipe" "process" "procfs" "pty" "rand" "runtime" "shm" "stdio" "system" "termios" "thread" "time" ];
+          "default" = [ "std" "use-libc-auxv" ];
+          "io_uring" = [ "event" "fs" "net" "linux-raw-sys/io_uring" ];
+          "itoa" = [ "dep:itoa" ];
+          "libc" = [ "dep:libc" ];
+          "libc_errno" = [ "dep:libc_errno" ];
+          "linux_latest" = [ "linux_4_11" ];
+          "net" = [ "linux-raw-sys/net" "linux-raw-sys/netlink" "linux-raw-sys/if_ether" "linux-raw-sys/xdp" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "param" = [ "fs" ];
+          "process" = [ "linux-raw-sys/prctl" ];
+          "procfs" = [ "once_cell" "itoa" "fs" ];
+          "pty" = [ "itoa" "fs" ];
+          "runtime" = [ "linux-raw-sys/prctl" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" "linux-raw-sys/rustc-dep-of-std" "bitflags/rustc-dep-of-std" "compiler_builtins?/rustc-dep-of-std" ];
+          "shm" = [ "fs" ];
+          "std" = [ "bitflags/std" "alloc" "libc?/std" "libc_errno?/std" ];
+          "system" = [ "linux-raw-sys/system" ];
+          "thread" = [ "linux-raw-sys/prctl" ];
+          "use-libc" = [ "libc_errno" "libc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fs" "std" "use-libc-auxv" ];
+      };
+      "ryu" = rec {
+        crateName = "ryu";
+        version = "1.0.17";
+        edition = "2018";
+        sha256 = "188vrsh3zlnl5xl7lw0rp2sc0knpx8yaqpwvr648b6h12v4rfrp8";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "schannel" = rec {
+        crateName = "schannel";
+        version = "0.1.23";
+        edition = "2018";
+        sha256 = "0d1m156bsjrws6xzzr1wyfyih9i22mb2csb5pc5kmkrvci2ibjgv";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Steffen Butzer <steffen.butzer@outlook.com>"
+        ];
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            features = [ "Win32_Foundation" "Win32_Security_Cryptography" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_System_Memory" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            features = [ "Win32_System_SystemInformation" "Win32_System_Time" ];
+          }
+        ];
+
+      };
+      "scheduled-thread-pool" = rec {
+        crateName = "scheduled-thread-pool";
+        version = "0.2.7";
+        edition = "2018";
+        sha256 = "068s77f9xcpvzl70nsxk8750dzzc6f9pixajhd979815cj0ndg1w";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "parking_lot";
+            packageId = "parking_lot 0.12.1";
+          }
+        ];
+
+      };
+      "scoped-tls" = rec {
+        crateName = "scoped-tls";
+        version = "0.1.2";
+        edition = "2015";
+        sha256 = "0a2bn9d2mb07c6l16sadijy4p540g498zddfxyiq4rsqpwrglbrk";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = { };
+      };
+      "scopeguard" = rec {
+        crateName = "scopeguard";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "0jcz9sd47zlsgcnm1hdw0664krxwb5gczlif4qngj2aif8vky54l";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+        };
+      };
+      "security-framework" = rec {
+        crateName = "security-framework";
+        version = "2.9.2";
+        edition = "2021";
+        sha256 = "1pplxk15s5yxvi2m1sz5xfmjibp96cscdcl432w9jzbk0frlzdh5";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Kornel <kornel@geekhood.net>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "core-foundation";
+            packageId = "core-foundation";
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "security-framework-sys";
+            packageId = "security-framework-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "OSX_10_10" = [ "OSX_10_9" "security-framework-sys/OSX_10_10" ];
+          "OSX_10_11" = [ "OSX_10_10" "security-framework-sys/OSX_10_11" ];
+          "OSX_10_12" = [ "OSX_10_11" "security-framework-sys/OSX_10_12" ];
+          "OSX_10_13" = [ "OSX_10_12" "security-framework-sys/OSX_10_13" "alpn" "session-tickets" "serial-number-bigint" ];
+          "OSX_10_14" = [ "OSX_10_13" "security-framework-sys/OSX_10_14" ];
+          "OSX_10_15" = [ "OSX_10_14" "security-framework-sys/OSX_10_15" ];
+          "OSX_10_9" = [ "security-framework-sys/OSX_10_9" ];
+          "default" = [ "OSX_10_9" ];
+          "log" = [ "dep:log" ];
+          "serial-number-bigint" = [ "dep:num-bigint" ];
+        };
+        resolvedDefaultFeatures = [ "OSX_10_9" "default" ];
+      };
+      "security-framework-sys" = rec {
+        crateName = "security-framework-sys";
+        version = "2.9.1";
+        edition = "2021";
+        sha256 = "0yhciwlsy9dh0ps1gw3197kvyqx1bvc4knrhiznhid6kax196cp9";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Kornel <kornel@geekhood.net>"
+        ];
+        dependencies = [
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "OSX_10_10" = [ "OSX_10_9" ];
+          "OSX_10_11" = [ "OSX_10_10" ];
+          "OSX_10_12" = [ "OSX_10_11" ];
+          "OSX_10_13" = [ "OSX_10_12" ];
+          "OSX_10_14" = [ "OSX_10_13" ];
+          "OSX_10_15" = [ "OSX_10_14" ];
+          "default" = [ "OSX_10_9" ];
+        };
+        resolvedDefaultFeatures = [ "OSX_10_9" "default" ];
+      };
+      "semver" = rec {
+        crateName = "semver";
+        version = "0.9.0";
+        edition = "2015";
+        sha256 = "00q4lkcj0rrgbhviv9sd4p6qmdsipkwkbra7rh11jrhq5kpvjzhx";
+        authors = [
+          "Steve Klabnik <steve@steveklabnik.com>"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "semver-parser";
+            packageId = "semver-parser";
+          }
+        ];
+        features = {
+          "ci" = [ "serde" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "semver-parser" = rec {
+        crateName = "semver-parser";
+        version = "0.7.0";
+        edition = "2015";
+        sha256 = "18vhypw6zgccnrlm5ps1pwa0khz7ry927iznpr88b87cagr1v2iq";
+        authors = [
+          "Steve Klabnik <steve@steveklabnik.com>"
+        ];
+
+      };
+      "serde 0.8.23" = rec {
+        crateName = "serde";
+        version = "0.8.23";
+        edition = "2015";
+        sha256 = "1j4ajipn0sf4ya0crgcb94s848qp7mfc35n6d0q2rf8rk5skzbcx";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+        ];
+        features = {
+          "alloc" = [ "unstable" ];
+          "clippy" = [ "dep:clippy" ];
+          "collections" = [ "alloc" ];
+          "default" = [ "std" ];
+          "unstable-testing" = [ "clippy" "unstable" "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "serde 1.0.197" = rec {
+        crateName = "serde";
+        version = "1.0.197";
+        edition = "2018";
+        sha256 = "1qjcxqd3p4yh5cmmax9q4ics1zy34j5ij32cvjj5dc5rw5rwic9z";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            optional = true;
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "derive" "serde_derive" "std" ];
+      };
+      "serde-hjson" = rec {
+        crateName = "serde-hjson";
+        version = "0.8.2";
+        edition = "2015";
+        sha256 = "0lv1qwis9rr767xv9w27y1g1r71ayf02k2wkypawwlkxsrd3r0qb";
+        authors = [
+          "Christian Zangl <laktak@cdak.net>"
+        ];
+        dependencies = [
+          {
+            name = "lazy_static";
+            packageId = "lazy_static 0.2.11";
+          }
+          {
+            name = "linked-hash-map";
+            packageId = "linked-hash-map 0.3.0";
+            optional = true;
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits 0.1.43";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "serde";
+            packageId = "serde 0.8.23";
+          }
+        ];
+        features = {
+          "clippy" = [ "dep:clippy" ];
+          "default" = [ "preserve_order" ];
+          "linked-hash-map" = [ "dep:linked-hash-map" ];
+          "preserve_order" = [ "linked-hash-map" "linked-hash-map/serde_impl" ];
+          "unstable-testing" = [ "clippy" ];
+        };
+        resolvedDefaultFeatures = [ "default" "linked-hash-map" "preserve_order" ];
+      };
+      "serde_derive" = rec {
+        crateName = "serde_derive";
+        version = "1.0.197";
+        edition = "2015";
+        sha256 = "02v1x0sdv8qy06lpr6by4ar1n3jz3hmab15cgimpzhgd895v7c3y";
+        procMacro = true;
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.52";
+            usesDefaultFeatures = false;
+            features = [ "clone-impls" "derive" "parsing" "printing" "proc-macro" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "serde_json" = rec {
+        crateName = "serde_json";
+        version = "1.0.114";
+        edition = "2021";
+        sha256 = "1q4saigxwkf8bw4y5kp6k33dnavlvvwa2q4zmag59vrjsqdrpw65";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde 1.0.197";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde 1.0.197";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" ];
+          "default" = [ "std" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "preserve_order" = [ "indexmap" "std" ];
+          "std" = [ "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "serde_test" = rec {
+        crateName = "serde_test";
+        version = "0.8.23";
+        edition = "2015";
+        sha256 = "1m939j7cgs7i58r6vxf0ffp3nbr8advr8p9dqa9w8zk0z2yks2qi";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde 0.8.23";
+          }
+        ];
+
+      };
+      "slab 0.3.0" = rec {
+        crateName = "slab";
+        version = "0.3.0";
+        edition = "2015";
+        sha256 = "08xw8w61zdfn1094qkq1d554vh5wmm9bqdys8gqqxc4sv2pgrd0p";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+
+      };
+      "slab 0.4.9" = rec {
+        crateName = "slab";
+        version = "0.4.9";
+        edition = "2018";
+        sha256 = "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "smallvec 0.6.14" = rec {
+        crateName = "smallvec";
+        version = "0.6.14";
+        edition = "2015";
+        sha256 = "1q4hz0ssnv24s6fq5kfp2wzrrprrrjiwc42a0h7s7nwym3mwlzxr";
+        libPath = "lib.rs";
+        authors = [
+          "Simon Sapin <simon.sapin@exyr.org>"
+        ];
+        dependencies = [
+          {
+            name = "maybe-uninit";
+            packageId = "maybe-uninit";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "smallvec 1.13.1" = rec {
+        crateName = "smallvec";
+        version = "1.13.1";
+        edition = "2018";
+        sha256 = "1mzk9j117pn3k1gabys0b7nz8cdjsx5xc6q7fwnm8r0an62d7v76";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "const_new" = [ "const_generics" ];
+          "drain_keep_rest" = [ "drain_filter" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "socket2" = rec {
+        crateName = "socket2";
+        version = "0.5.6";
+        edition = "2021";
+        sha256 = "0w98g7dh9m74vpxln401hl4knpjzrx7jhng7cbh46x9vm70dkzq5";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = { };
+      };
+      "syn 1.0.109" = rec {
+        crateName = "syn";
+        version = "1.0.109";
+        edition = "2018";
+        sha256 = "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "fold" "full" "parsing" "printing" "proc-macro" "quote" "visit" ];
+      };
+      "syn 2.0.52" = rec {
+        crateName = "syn";
+        version = "2.0.52";
+        edition = "2021";
+        sha256 = "01saay6pi9x19f6lin3mw3xawdyyagpzzy39ghz2rw6i6rdx36dn";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "full" "parsing" "printing" "proc-macro" "quote" "visit" ];
+      };
+      "synstructure" = rec {
+        crateName = "synstructure";
+        version = "0.12.6";
+        edition = "2018";
+        sha256 = "03r1lydbf3japnlpc4wka7y90pmz1i0danaj3f9a7b431akdlszk";
+        authors = [
+          "Nika Layzell <nika@thelayzells.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            usesDefaultFeatures = false;
+            features = [ "derive" "parsing" "printing" "clone-impls" "visit" "extra-traits" ];
+          }
+          {
+            name = "unicode-xid";
+            packageId = "unicode-xid";
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "syn/proc-macro" "quote/proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "tempfile" = rec {
+        crateName = "tempfile";
+        version = "3.10.1";
+        edition = "2021";
+        sha256 = "1wdzz35ri168jn9al4s1g2rnsrr5ci91khgarc2rvpb3nappzdw5";
+        authors = [
+          "Steven Allen <steven@stebalien.com>"
+          "The Rust Project Developers"
+          "Ashley Mannix <ashleymannix@live.com.au>"
+          "Jason White <me@jasonwhite.io>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 1.0.0";
+          }
+          {
+            name = "fastrand";
+            packageId = "fastrand";
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            target = { target, features }: ((target."unix" or false) || ("wasi" == target."os" or null));
+            features = [ "fs" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Storage_FileSystem" "Win32_Foundation" ];
+          }
+        ];
+        features = { };
+      };
+      "termcolor" = rec {
+        crateName = "termcolor";
+        version = "1.4.1";
+        edition = "2018";
+        sha256 = "0mappjh3fj3p2nmrg4y7qv94rchwi9mzmgmfflr8p2awdj7lyy86";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-util";
+            packageId = "winapi-util";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+
+      };
+      "tokio" = rec {
+        crateName = "tokio";
+        version = "0.1.22";
+        edition = "2015";
+        sha256 = "1xhaadfmm6m37f79xv5020gc3np9wqza3bq95ymp522qpfsw02as";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            optional = true;
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+            optional = true;
+          }
+          {
+            name = "tokio-codec";
+            packageId = "tokio-codec";
+            optional = true;
+          }
+          {
+            name = "tokio-current-thread";
+            packageId = "tokio-current-thread";
+            optional = true;
+          }
+          {
+            name = "tokio-executor";
+            packageId = "tokio-executor";
+            optional = true;
+          }
+          {
+            name = "tokio-fs";
+            packageId = "tokio-fs";
+            optional = true;
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+            optional = true;
+          }
+          {
+            name = "tokio-reactor";
+            packageId = "tokio-reactor";
+            optional = true;
+          }
+          {
+            name = "tokio-sync";
+            packageId = "tokio-sync";
+            optional = true;
+          }
+          {
+            name = "tokio-tcp";
+            packageId = "tokio-tcp";
+            optional = true;
+          }
+          {
+            name = "tokio-threadpool";
+            packageId = "tokio-threadpool";
+            optional = true;
+          }
+          {
+            name = "tokio-timer";
+            packageId = "tokio-timer 0.2.13";
+            optional = true;
+          }
+          {
+            name = "tokio-udp";
+            packageId = "tokio-udp";
+            optional = true;
+          }
+          {
+            name = "tokio-uds";
+            packageId = "tokio-uds";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+          }
+        ];
+        features = {
+          "bytes" = [ "dep:bytes" ];
+          "codec" = [ "io" "tokio-codec" ];
+          "default" = [ "codec" "fs" "io" "reactor" "rt-full" "sync" "tcp" "timer" "udp" "uds" ];
+          "experimental-tracing" = [ "tracing-core" ];
+          "fs" = [ "tokio-fs" ];
+          "io" = [ "bytes" "tokio-io" ];
+          "mio" = [ "dep:mio" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "reactor" = [ "io" "mio" "tokio-reactor" ];
+          "rt-full" = [ "num_cpus" "reactor" "timer" "tokio-current-thread" "tokio-executor" "tokio-threadpool" ];
+          "sync" = [ "tokio-sync" ];
+          "tcp" = [ "tokio-tcp" ];
+          "timer" = [ "tokio-timer" ];
+          "tokio-codec" = [ "dep:tokio-codec" ];
+          "tokio-current-thread" = [ "dep:tokio-current-thread" ];
+          "tokio-executor" = [ "dep:tokio-executor" ];
+          "tokio-fs" = [ "dep:tokio-fs" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+          "tokio-reactor" = [ "dep:tokio-reactor" ];
+          "tokio-sync" = [ "dep:tokio-sync" ];
+          "tokio-tcp" = [ "dep:tokio-tcp" ];
+          "tokio-threadpool" = [ "dep:tokio-threadpool" ];
+          "tokio-timer" = [ "dep:tokio-timer" ];
+          "tokio-udp" = [ "dep:tokio-udp" ];
+          "tokio-uds" = [ "dep:tokio-uds" ];
+          "tracing-core" = [ "dep:tracing-core" ];
+          "udp" = [ "tokio-udp" ];
+          "uds" = [ "tokio-uds" ];
+        };
+        resolvedDefaultFeatures = [ "bytes" "codec" "default" "fs" "io" "mio" "num_cpus" "reactor" "rt-full" "sync" "tcp" "timer" "tokio-codec" "tokio-current-thread" "tokio-executor" "tokio-fs" "tokio-io" "tokio-reactor" "tokio-sync" "tokio-tcp" "tokio-threadpool" "tokio-timer" "tokio-udp" "tokio-uds" "udp" "uds" ];
+      };
+      "tokio-codec" = rec {
+        crateName = "tokio-codec";
+        version = "0.1.2";
+        edition = "2015";
+        sha256 = "0swpfngcb331lzggk6j68yks6w0bnw35vpl4hv8p03msc239kci5";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Bryan Burgers <bryan@burgers.io>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+        ];
+
+      };
+      "tokio-core" = rec {
+        crateName = "tokio-core";
+        version = "0.1.18";
+        edition = "2015";
+        sha256 = "1m7zij19xy13wmlb7a1bghvi4vs8s1hlyggnaajvqfj46i9kkcc7";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "iovec";
+            packageId = "iovec";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+          }
+          {
+            name = "scoped-tls";
+            packageId = "scoped-tls";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "tokio-executor";
+            packageId = "tokio-executor";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+          {
+            name = "tokio-reactor";
+            packageId = "tokio-reactor";
+          }
+          {
+            name = "tokio-timer";
+            packageId = "tokio-timer 0.2.13";
+          }
+        ];
+
+      };
+      "tokio-current-thread" = rec {
+        crateName = "tokio-current-thread";
+        version = "0.1.7";
+        edition = "2015";
+        sha256 = "03p2w316ha0irgzvy37njx9hl71133gcrmrq4801w4rzm0r0xpmi";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "tokio-executor";
+            packageId = "tokio-executor";
+          }
+        ];
+
+      };
+      "tokio-executor" = rec {
+        crateName = "tokio-executor";
+        version = "0.1.10";
+        edition = "2015";
+        sha256 = "0w8n78d2vixs1vghqc4wy9w0d1h6qkli51c1yzhzbns88n7inbgv";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+        ];
+
+      };
+      "tokio-fs" = rec {
+        crateName = "tokio-fs";
+        version = "0.1.7";
+        edition = "2015";
+        sha256 = "1x3gkdi5x7bjlzzg7qlnymb549rb546p0nykxsh04qyaw0314yi9";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+          {
+            name = "tokio-threadpool";
+            packageId = "tokio-threadpool";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+        ];
+
+      };
+      "tokio-io" = rec {
+        crateName = "tokio-io";
+        version = "0.1.13";
+        edition = "2015";
+        sha256 = "0x06zyzinans1pn90g6i150lgixijdf1cg8y2gipjd09ms58dz2p";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+
+      };
+      "tokio-mockstream" = rec {
+        crateName = "tokio-mockstream";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "0mg1i39cl8x32wxwbn74hlirks8a6f3g0gfzkb0n0zwbxwvc9gs1";
+        authors = [
+          "Aaron Weiss <awe@pdgn.co>"
+        ];
+        dependencies = [
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+        ];
+
+      };
+      "tokio-reactor" = rec {
+        crateName = "tokio-reactor";
+        version = "0.1.12";
+        edition = "2015";
+        sha256 = "0l8klnd41q55f3ialzz0lb7s5bfwa38nh86sa9vai2xsqh75kg09";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static 1.4.0";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot 0.9.0";
+          }
+          {
+            name = "slab";
+            packageId = "slab 0.4.9";
+          }
+          {
+            name = "tokio-executor";
+            packageId = "tokio-executor";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+          {
+            name = "tokio-sync";
+            packageId = "tokio-sync";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+          }
+        ];
+
+      };
+      "tokio-sync" = rec {
+        crateName = "tokio-sync";
+        version = "0.1.8";
+        edition = "2015";
+        sha256 = "1vkxz0y7qf9sshfpxvn506pvxy4vza8piavd8p64y5n85cam1zpd";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+        ];
+
+      };
+      "tokio-tcp" = rec {
+        crateName = "tokio-tcp";
+        version = "0.1.4";
+        edition = "2015";
+        sha256 = "0whzqnkyfym1ipzznibyjl3j9281walq4n0q5xs2xdz3cvniipwq";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "iovec";
+            packageId = "iovec";
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+          {
+            name = "tokio-reactor";
+            packageId = "tokio-reactor";
+          }
+        ];
+
+      };
+      "tokio-threadpool" = rec {
+        crateName = "tokio-threadpool";
+        version = "0.1.18";
+        edition = "2015";
+        sha256 = "12azq8jm71b7hdm72pxrgqm2879bn6b0fcdl1s7i2k3qh5jhnwnz";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-deque";
+            packageId = "crossbeam-deque";
+          }
+          {
+            name = "crossbeam-queue";
+            packageId = "crossbeam-queue";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static 1.4.0";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+          }
+          {
+            name = "slab";
+            packageId = "slab 0.4.9";
+          }
+          {
+            name = "tokio-executor";
+            packageId = "tokio-executor";
+          }
+        ];
+
+      };
+      "tokio-timer 0.1.2" = rec {
+        crateName = "tokio-timer";
+        version = "0.1.2";
+        edition = "2015";
+        sha256 = "1z0fwbh5bm6hdbfm0y17fa5l60na7fl9vbca7wdzz1vp0f0ffcb1";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "slab";
+            packageId = "slab 0.3.0";
+          }
+        ];
+
+      };
+      "tokio-timer 0.2.13" = rec {
+        crateName = "tokio-timer";
+        version = "0.2.13";
+        edition = "2015";
+        sha256 = "15pjjj6daks3sii8p24a509b0dapl2kyk740nwfgz59w64nly14k";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "slab";
+            packageId = "slab 0.4.9";
+          }
+          {
+            name = "tokio-executor";
+            packageId = "tokio-executor";
+          }
+        ];
+
+      };
+      "tokio-tls" = rec {
+        crateName = "tokio-tls";
+        version = "0.2.1";
+        edition = "2015";
+        sha256 = "0z0gmvv7jrpan6y42p5f5wd48rqcd96igp592w1c5cr573c8qjrm";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "native-tls";
+            packageId = "native-tls";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+        ];
+
+      };
+      "tokio-udp" = rec {
+        crateName = "tokio-udp";
+        version = "0.1.6";
+        edition = "2015";
+        sha256 = "10hdcnxdp0dxvj44jl1nrrpg30jbisqclbqs0f5w6f8bc47b3872";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+          }
+          {
+            name = "tokio-codec";
+            packageId = "tokio-codec";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+          {
+            name = "tokio-reactor";
+            packageId = "tokio-reactor";
+          }
+        ];
+
+      };
+      "tokio-uds" = rec {
+        crateName = "tokio-uds";
+        version = "0.2.7";
+        edition = "2015";
+        sha256 = "1q74sydx22l4mkmrz02l4i5swddwr1pryxvhrzdwkj0i86na8mxb";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "iovec";
+            packageId = "iovec";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+          }
+          {
+            name = "mio-uds";
+            packageId = "mio-uds";
+          }
+          {
+            name = "tokio-codec";
+            packageId = "tokio-codec";
+          }
+          {
+            name = "tokio-io";
+            packageId = "tokio-io";
+          }
+          {
+            name = "tokio-reactor";
+            packageId = "tokio-reactor";
+          }
+        ];
+
+      };
+      "toml" = rec {
+        crateName = "toml";
+        version = "0.4.10";
+        edition = "2015";
+        sha256 = "07qilkzinn8z13vq2sss65n2lza7wrmqpvkbclw919m3f7y691km";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde 1.0.197";
+          }
+        ];
+
+      };
+      "unicode-ident" = rec {
+        crateName = "unicode-ident";
+        version = "1.0.12";
+        edition = "2018";
+        sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "unicode-xid" = rec {
+        crateName = "unicode-xid";
+        version = "0.2.4";
+        edition = "2015";
+        sha256 = "131dfzf7d8fsr1ivch34x42c2d1ik5ig3g78brxncnn0r1sdyqpr";
+        authors = [
+          "erick.tryzelaar <erick.tryzelaar@gmail.com>"
+          "kwantam <kwantam@gmail.com>"
+          "Manish Goregaokar <manishsmail@gmail.com>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "vcpkg" = rec {
+        crateName = "vcpkg";
+        version = "0.2.15";
+        edition = "2015";
+        sha256 = "09i4nf5y8lig6xgj3f7fyrvzd3nlaw4znrihw8psidvv5yk4xkdc";
+        authors = [
+          "Jim McGrath <jimmc2@gmail.com>"
+        ];
+
+      };
+      "version_check" = rec {
+        crateName = "version_check";
+        version = "0.1.5";
+        edition = "2015";
+        sha256 = "1pf91pvj8n6akh7w6j5ypka6aqz08b3qpzgs0ak2kjf4frkiljwi";
+        authors = [
+          "Sergio Benitez <sb@sergio.bz>"
+        ];
+
+      };
+      "wasi" = rec {
+        crateName = "wasi";
+        version = "0.9.0+wasi-snapshot-preview1";
+        edition = "2018";
+        sha256 = "06g5v3vrdapfzvfq662cij7v8a1flwr2my45nnncdv2galrdzkfc";
+        authors = [
+          "The Cranelift Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "rustc-std-workspace-alloc" ];
+          "rustc-std-workspace-alloc" = [ "dep:rustc-std-workspace-alloc" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "wasm-bindgen" = rec {
+        crateName = "wasm-bindgen";
+        version = "0.2.92";
+        edition = "2018";
+        sha256 = "1a4mcw13nsk3fr8fxjzf9kk1wj88xkfsmnm0pjraw01ryqfm7qjb";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if 1.0.0";
+          }
+          {
+            name = "wasm-bindgen-macro";
+            packageId = "wasm-bindgen-macro";
+          }
+        ];
+        features = {
+          "default" = [ "spans" "std" ];
+          "enable-interning" = [ "std" ];
+          "gg-alloc" = [ "wasm-bindgen-test/gg-alloc" ];
+          "serde" = [ "dep:serde" ];
+          "serde-serialize" = [ "serde" "serde_json" "std" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "spans" = [ "wasm-bindgen-macro/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro/strict-macro" ];
+          "xxx_debug_only_print_generated_code" = [ "wasm-bindgen-macro/xxx_debug_only_print_generated_code" ];
+        };
+        resolvedDefaultFeatures = [ "default" "spans" "std" ];
+      };
+      "wasm-bindgen-backend" = rec {
+        crateName = "wasm-bindgen-backend";
+        version = "0.2.92";
+        edition = "2018";
+        sha256 = "1nj7wxbi49f0rw9d44rjzms26xlw6r76b2mrggx8jfbdjrxphkb1";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "bumpalo";
+            packageId = "bumpalo";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.52";
+            features = [ "full" ];
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro" = rec {
+        crateName = "wasm-bindgen-macro";
+        version = "0.2.92";
+        edition = "2018";
+        sha256 = "09npa1srjjabd6nfph5yc03jb26sycjlxhy0c2a1pdrpx4yq5y51";
+        procMacro = true;
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "wasm-bindgen-macro-support";
+            packageId = "wasm-bindgen-macro-support";
+          }
+        ];
+        features = {
+          "spans" = [ "wasm-bindgen-macro-support/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro-support/strict-macro" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro-support" = rec {
+        crateName = "wasm-bindgen-macro-support";
+        version = "0.2.92";
+        edition = "2018";
+        sha256 = "1dqv2xs8zcyw4kjgzj84bknp2h76phmsb3n7j6hn396h4ssifkz9";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.52";
+            features = [ "visit" "full" ];
+          }
+          {
+            name = "wasm-bindgen-backend";
+            packageId = "wasm-bindgen-backend";
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+          "spans" = [ "wasm-bindgen-backend/spans" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-shared" = rec {
+        crateName = "wasm-bindgen-shared";
+        version = "0.2.92";
+        edition = "2018";
+        links = "wasm_bindgen";
+        sha256 = "15kyavsrna2cvy30kg03va257fraf9x00ny554vxngvpyaa0q6dg";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+
+      };
+      "winapi 0.2.8" = rec {
+        crateName = "winapi";
+        version = "0.2.8";
+        edition = "2015";
+        sha256 = "0yh816lh6lf56dpsgxy189c2ai1z3j8mw9si6izqb6wsjkbcjz8n";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi 0.3.9" = rec {
+        crateName = "winapi";
+        version = "0.3.9";
+        edition = "2015";
+        sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-i686-pc-windows-gnu";
+            packageId = "winapi-i686-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+          }
+          {
+            name = "winapi-x86_64-pc-windows-gnu";
+            packageId = "winapi-x86_64-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+          }
+        ];
+        features = {
+          "debug" = [ "impl-debug" ];
+        };
+        resolvedDefaultFeatures = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "minwinbase" "minwindef" "ntstatus" "processenv" "std" "sysinfoapi" "winbase" "wincon" "winerror" "winnt" "winsock2" "ws2def" "ws2ipdef" "ws2tcpip" ];
+      };
+      "winapi-build" = rec {
+        crateName = "winapi-build";
+        version = "0.1.1";
+        edition = "2015";
+        sha256 = "1g4rqsgjky0a7530qajn2bbfcrl2v0zb39idgdws9b1l7gp5wc9d";
+        libName = "build";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi-i686-pc-windows-gnu" = rec {
+        crateName = "winapi-i686-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi-util" = rec {
+        crateName = "winapi-util";
+        version = "0.1.6";
+        edition = "2021";
+        sha256 = "15i5lm39wd44004i9d5qspry2cynkrpvwzghr6s2c3dsk28nz7pj";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi";
+            packageId = "winapi 0.3.9";
+            target = { target, features }: (target."windows" or false);
+            features = [ "std" "consoleapi" "errhandlingapi" "fileapi" "minwindef" "processenv" "sysinfoapi" "winbase" "wincon" "winerror" "winnt" ];
+          }
+        ];
+
+      };
+      "winapi-x86_64-pc-windows-gnu" = rec {
+        crateName = "winapi-x86_64-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "windows-core" = rec {
+        crateName = "windows-core";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1nc3qv7sy24x0nlnb32f7alzpd6f72l4p24vl65vydbyil669ark";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.4";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "windows-sys" = rec {
+        crateName = "windows-sys";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.4";
+          }
+        ];
+        features = {
+          "Wdk_Foundation" = [ "Wdk" ];
+          "Wdk_Graphics" = [ "Wdk" ];
+          "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ];
+          "Wdk_Storage" = [ "Wdk" ];
+          "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ];
+          "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ];
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_IO" = [ "Wdk_System" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Wdk_System_Registry" = [ "Wdk_System" ];
+          "Wdk_System_SystemInformation" = [ "Wdk_System" ];
+          "Wdk_System_SystemServices" = [ "Wdk_System" ];
+          "Wdk_System_Threading" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_Nvme" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_Variant" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_NetworkManagement" "Win32_NetworkManagement_IpHelper" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_Security_Cryptography" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_LibraryLoader" "Win32_System_Memory" "Win32_System_Threading" "Win32_System_WindowsProgramming" "default" ];
+      };
+      "windows-targets 0.48.5" = rec {
+        crateName = "windows-targets";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.48.5";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows-targets 0.52.4" = rec {
+        crateName = "windows-targets";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "06sdd7fin3dj9cmlg6n1dw0n1l10jhn9b8ckz1cqf0drb9z7plvx";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.52.4";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.52.4";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.52.4";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.52.4";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.52.4";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.52.4";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.52.4";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.48.5" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.52.4" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "1jfam5qfngg8v1syxklnvy8la94b5igm7klkrk8z5ik5qgs6rx5w";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.48.5" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.52.4" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "0xdn6db0rk8idn7dxsyflixq2dbj9x60kzdzal5rkxmwsffjb7ys";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.48.5" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.52.4" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "1lq1g35sbj55ms86by4c080jcqrlfjy9bw5r4mgrkq4riwkdhx5l";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.48.5" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.52.4" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "00lfzw88dkf3fdcf2hpfhp74i9pwbp7rwnj1nhy79vavksifj58m";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.48.5" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.52.4" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "00qs6x33bf9lai2q68faxl56cszbv7mf7zqlslmc1778j0ahkvjy";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.48.5" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.52.4" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "0xr13xxakp14hs4v4hg2ynjcv7wrzr3hg7zk5agglj8v8pr7kjkp";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.48.5" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.52.4" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "1n0yc7xiv9iki1j3xl8nxlwwkr7dzsnwwvycvgxxv81d5bjm5drj";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "ws2_32-sys" = rec {
+        crateName = "ws2_32-sys";
+        version = "0.2.1";
+        edition = "2015";
+        sha256 = "0ppscg5qfqaw0gzwv2a4nhn5bn01ff9iwn6ysqnzm4n8s3myz76m";
+        libName = "ws2_32";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi";
+            packageId = "winapi 0.2.8";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "winapi-build";
+            packageId = "winapi-build";
+          }
+        ];
+
+      };
+      "yaml-rust" = rec {
+        crateName = "yaml-rust";
+        version = "0.4.5";
+        edition = "2018";
+        sha256 = "118wbqrr4n6wgk5rjjnlrdlahawlxc1bdsx146mwk8f79in97han";
+        authors = [
+          "Yuheng Chen <yuhengchen@sensetime.com>"
+        ];
+        dependencies = [
+          {
+            name = "linked-hash-map";
+            packageId = "linked-hash-map 0.5.6";
+          }
+        ];
+
+      };
+    };
+
+    #
+    # crate2nix/default.nix (excerpt start)
+    #
+
+    /* Target (platform) data for conditional dependencies.
+      This corresponds roughly to what buildRustCrate is setting.
+    */
+    makeDefaultTarget = platform: {
+      unix = platform.isUnix;
+      windows = platform.isWindows;
+      fuchsia = true;
+      test = false;
+
+      /* We are choosing an arbitrary rust version to grab `lib` from,
+      which is unfortunate, but `lib` has been version-agnostic the
+      whole time so this is good enough for now.
+      */
+      os = pkgs.rust.lib.toTargetOs platform;
+      arch = pkgs.rust.lib.toTargetArch platform;
+      family = pkgs.rust.lib.toTargetFamily platform;
+      vendor = pkgs.rust.lib.toTargetVendor platform;
+      env = "gnu";
+      endian =
+        if platform.parsed.cpu.significantByte.name == "littleEndian"
+        then "little" else "big";
+      pointer_width = toString platform.parsed.cpu.bits;
+      debug_assertions = false;
+    };
+
+    /* Filters common temp files and build files. */
+    # TODO(pkolloch): Substitute with gitignore filter
+    sourceFilter = name: type:
+      let
+        baseName = builtins.baseNameOf (builtins.toString name);
+      in
+        ! (
+          # Filter out git
+          baseName == ".gitignore"
+          || (type == "directory" && baseName == ".git")
+
+          # Filter out build results
+          || (
+            type == "directory" && (
+              baseName == "target"
+              || baseName == "_site"
+              || baseName == ".sass-cache"
+              || baseName == ".jekyll-metadata"
+              || baseName == "build-artifacts"
+            )
+          )
+
+          # Filter out nix-build result symlinks
+          || (
+            type == "symlink" && lib.hasPrefix "result" baseName
+          )
+
+          # Filter out IDE config
+          || (
+            type == "directory" && (
+              baseName == ".idea" || baseName == ".vscode"
+            )
+          ) || lib.hasSuffix ".iml" baseName
+
+          # Filter out nix build files
+          || baseName == "Cargo.nix"
+
+          # Filter out editor backup / swap files.
+          || lib.hasSuffix "~" baseName
+          || builtins.match "^\\.sw[a-z]$$" baseName != null
+          || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null
+          || lib.hasSuffix ".tmp" baseName
+          || lib.hasSuffix ".bak" baseName
+          || baseName == "tests.nix"
+        );
+
+    /* Returns a crate which depends on successful test execution
+      of crate given as the second argument.
+
+      testCrateFlags: list of flags to pass to the test exectuable
+      testInputs: list of packages that should be available during test execution
+    */
+    crateWithTest = { crate, testCrate, testCrateFlags, testInputs, testPreRun, testPostRun }:
+      assert builtins.typeOf testCrateFlags == "list";
+      assert builtins.typeOf testInputs == "list";
+      assert builtins.typeOf testPreRun == "string";
+      assert builtins.typeOf testPostRun == "string";
+      let
+        # override the `crate` so that it will build and execute tests instead of
+        # building the actual lib and bin targets We just have to pass `--test`
+        # to rustc and it will do the right thing.  We execute the tests and copy
+        # their log and the test executables to $out for later inspection.
+        test =
+          let
+            drv = testCrate.override
+              (
+                _: {
+                  buildTests = true;
+                }
+              );
+            # If the user hasn't set any pre/post commands, we don't want to
+            # insert empty lines. This means that any existing users of crate2nix
+            # don't get a spurious rebuild unless they set these explicitly.
+            testCommand = pkgs.lib.concatStringsSep "\n"
+              (pkgs.lib.filter (s: s != "") [
+                testPreRun
+                "$f $testCrateFlags 2>&1 | tee -a $out"
+                testPostRun
+              ]);
+          in
+          pkgs.runCommand "run-tests-${testCrate.name}"
+            {
+              inherit testCrateFlags;
+              buildInputs = testInputs;
+            } ''
+            set -e
+
+            export RUST_BACKTRACE=1
+
+            # recreate a file hierarchy as when running tests with cargo
+
+            # the source for test data
+            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+
+            # build outputs
+            testRoot=target/debug
+            mkdir -p $testRoot
+
+            # executables of the crate
+            # we copy to prevent std::env::current_exe() to resolve to a store location
+            for i in ${crate}/bin/*; do
+              cp "$i" "$testRoot"
+            done
+            chmod +w -R .
+
+            # test harness executables are suffixed with a hash, like cargo does
+            # this allows to prevent name collision with the main
+            # executables of the crate
+            hash=$(basename $out)
+            for file in ${drv}/tests/*; do
+              f=$testRoot/$(basename $file)-$hash
+              cp $file $f
+              ${testCommand}
+            done
+          '';
+      in
+      pkgs.runCommand "${crate.name}-linked"
+        {
+          inherit (crate) outputs crateName;
+          passthru = (crate.passthru or { }) // {
+            inherit test;
+          };
+        }
+        (lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+          echo tested by ${test}
+        '' + ''
+          ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs}
+        '');
+
+    /* A restricted overridable version of builtRustCratesWithFeatures. */
+    buildRustCrateWithFeatures =
+      { packageId
+      , features ? rootFeatures
+      , crateOverrides ? defaultCrateOverrides
+      , buildRustCrateForPkgsFunc ? null
+      , runTests ? false
+      , testCrateFlags ? [ ]
+      , testInputs ? [ ]
+        # Any command to run immediatelly before a test is executed.
+      , testPreRun ? ""
+        # Any command run immediatelly after a test is executed.
+      , testPostRun ? ""
+      }:
+      lib.makeOverridable
+        (
+          { features
+          , crateOverrides
+          , runTests
+          , testCrateFlags
+          , testInputs
+          , testPreRun
+          , testPostRun
+          }:
+          let
+            buildRustCrateForPkgsFuncOverriden =
+              if buildRustCrateForPkgsFunc != null
+              then buildRustCrateForPkgsFunc
+              else
+                (
+                  if crateOverrides == pkgs.defaultCrateOverrides
+                  then buildRustCrateForPkgs
+                  else
+                    pkgs: (buildRustCrateForPkgs pkgs).override {
+                      defaultCrateOverrides = crateOverrides;
+                    }
+                );
+            builtRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = false;
+            };
+            builtTestRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = true;
+            };
+            drv = builtRustCrates.crates.${packageId};
+            testDrv = builtTestRustCrates.crates.${packageId};
+            derivation =
+              if runTests then
+                crateWithTest
+                  {
+                    crate = drv;
+                    testCrate = testDrv;
+                    inherit testCrateFlags testInputs testPreRun testPostRun;
+                  }
+              else drv;
+          in
+          derivation
+        )
+        { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun; };
+
+    /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc
+      for the corresponding crate.
+    */
+    builtRustCratesWithFeatures =
+      { packageId
+      , features
+      , crateConfigs ? crates
+      , buildRustCrateForPkgsFunc
+      , runTests
+      , makeTarget ? makeDefaultTarget
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isList features);
+        assert (builtins.isAttrs (makeTarget stdenv.hostPlatform));
+        assert (builtins.isBool runTests);
+        let
+          rootPackageId = packageId;
+          mergedFeatures = mergePackageFeatures
+            (
+              args // {
+                inherit rootPackageId;
+                target = makeTarget stdenv.hostPlatform // { test = runTests; };
+              }
+            );
+          # Memoize built packages so that reappearing packages are only built once.
+          builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs;
+          mkBuiltByPackageIdByPkgs = pkgs:
+            let
+              self = {
+                crates = lib.mapAttrs (packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId) crateConfigs;
+                target = makeTarget pkgs.stdenv.hostPlatform;
+                build = mkBuiltByPackageIdByPkgs pkgs.buildPackages;
+              };
+            in
+            self;
+          buildByPackageIdForPkgsImpl = self: pkgs: packageId:
+            let
+              features = mergedFeatures."${packageId}" or [ ];
+              crateConfig' = crateConfigs."${packageId}";
+              crateConfig =
+                builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ];
+              devDependencies =
+                lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig'.devDependencies or [ ]);
+              dependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self) target;
+                  buildByPackageId = depPackageId:
+                    # proc_macro crates must be compiled for the build architecture
+                    if crateConfigs.${depPackageId}.procMacro or false
+                    then self.build.crates.${depPackageId}
+                    else self.crates.${depPackageId};
+                  dependencies =
+                    (crateConfig.dependencies or [ ])
+                    ++ devDependencies;
+                };
+              buildDependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self.build) target;
+                  buildByPackageId = depPackageId:
+                    self.build.crates.${depPackageId};
+                  dependencies = crateConfig.buildDependencies or [ ];
+                };
+              dependenciesWithRenames =
+                let
+                  buildDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self) target;
+                    dependencies = crateConfig.dependencies or [ ] ++ devDependencies;
+                  };
+                  hostDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self.build) target;
+                    dependencies = crateConfig.buildDependencies or [ ];
+                  };
+                in
+                lib.filter (d: d ? "rename") (hostDeps ++ buildDeps);
+              # Crate renames have the form:
+              #
+              # {
+              #    crate_name = [
+              #       { version = "1.2.3"; rename = "crate_name01"; }
+              #    ];
+              #    # ...
+              # }
+              crateRenames =
+                let
+                  grouped =
+                    lib.groupBy
+                      (dependency: dependency.name)
+                      dependenciesWithRenames;
+                  versionAndRename = dep:
+                    let
+                      package = crateConfigs."${dep.packageId}";
+                    in
+                    { inherit (dep) rename; inherit (package) version; };
+                in
+                lib.mapAttrs (name: builtins.map versionAndRename) grouped;
+            in
+            buildRustCrateForPkgsFunc pkgs
+              (
+                crateConfig // {
+                  # https://github.com/NixOS/nixpkgs/issues/218712
+                  dontStrip = stdenv.hostPlatform.isDarwin;
+                  src = crateConfig.src or (
+                    pkgs.fetchurl rec {
+                      name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz";
+                      # https://www.pietroalbini.org/blog/downloading-crates-io/
+                      # Not rate-limited, CDN URL.
+                      url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate";
+                      sha256 =
+                        assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}");
+                        crateConfig.sha256;
+                    }
+                  );
+                  extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}";
+                  inherit features dependencies buildDependencies crateRenames release;
+                }
+              );
+        in
+        builtByPackageIdByPkgs;
+
+    /* Returns the actual derivations for the given dependencies. */
+    dependencyDerivations =
+      { buildByPackageId
+      , features
+      , dependencies
+      , target
+      }:
+        assert (builtins.isList features);
+        assert (builtins.isList dependencies);
+        assert (builtins.isAttrs target);
+        let
+          enabledDependencies = filterEnabledDependencies {
+            inherit dependencies features target;
+          };
+          depDerivation = dependency: buildByPackageId dependency.packageId;
+        in
+        map depDerivation enabledDependencies;
+
+    /* Returns a sanitized version of val with all values substituted that cannot
+      be serialized as JSON.
+    */
+    sanitizeForJson = val:
+      if builtins.isAttrs val
+      then lib.mapAttrs (n: sanitizeForJson) val
+      else if builtins.isList val
+      then builtins.map sanitizeForJson val
+      else if builtins.isFunction val
+      then "function"
+      else val;
+
+    /* Returns various tools to debug a crate. */
+    debugCrate = { packageId, target ? makeDefaultTarget stdenv.hostPlatform }:
+      assert (builtins.isString packageId);
+      let
+        debug = rec {
+          # The built tree as passed to buildRustCrate.
+          buildTree = buildRustCrateWithFeatures {
+            buildRustCrateForPkgsFunc = _: lib.id;
+            inherit packageId;
+          };
+          sanitizedBuildTree = sanitizeForJson buildTree;
+          dependencyTree = sanitizeForJson
+            (
+              buildRustCrateWithFeatures {
+                buildRustCrateForPkgsFunc = _: crate: {
+                  "01_crateName" = crate.crateName or false;
+                  "02_features" = crate.features or [ ];
+                  "03_dependencies" = crate.dependencies or [ ];
+                };
+                inherit packageId;
+              }
+            );
+          mergedPackageFeatures = mergePackageFeatures {
+            features = rootFeatures;
+            inherit packageId target;
+          };
+          diffedDefaultPackageFeatures = diffDefaultPackageFeatures {
+            inherit packageId target;
+          };
+        };
+      in
+      { internal = debug; };
+
+    /* Returns differences between cargo default features and crate2nix default
+      features.
+
+      This is useful for verifying the feature resolution in crate2nix.
+    */
+    diffDefaultPackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , target
+      }:
+        assert (builtins.isAttrs crateConfigs);
+        let
+          prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; });
+          mergedFeatures =
+            prefixValues
+              "crate2nix"
+              (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; });
+          configs = prefixValues "cargo" crateConfigs;
+          combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ];
+          onlyInCargo =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined);
+          onlyInCrate2Nix =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined);
+          differentFeatures = lib.filterAttrs
+            (
+              n: v:
+                (v ? "crate2nix")
+                && (v ? "cargo")
+                && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ])
+            )
+            combined;
+        in
+        builtins.toJSON {
+          inherit onlyInCargo onlyInCrate2Nix differentFeatures;
+        };
+
+    /* Returns an attrset mapping packageId to the list of enabled features.
+
+      If multiple paths to a dependency enable different features, the
+      corresponding feature sets are merged. Features in rust are additive.
+    */
+    mergePackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , rootPackageId ? packageId
+      , features ? rootFeatures
+      , dependencyPath ? [ crates.${packageId}.crateName ]
+      , featuresByPackageId ? { }
+      , target
+        # Adds devDependencies to the crate with rootPackageId.
+      , runTests ? false
+      , ...
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isString rootPackageId);
+        assert (builtins.isList features);
+        assert (builtins.isList dependencyPath);
+        assert (builtins.isAttrs featuresByPackageId);
+        assert (builtins.isAttrs target);
+        assert (builtins.isBool runTests);
+        let
+          crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
+          expandedFeatures = expandFeatures (crateConfig.features or { }) features;
+          enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
+          depWithResolvedFeatures = dependency:
+            let
+              inherit (dependency) packageId;
+              features = dependencyFeatures enabledFeatures dependency;
+            in
+            { inherit packageId features; };
+          resolveDependencies = cache: path: dependencies:
+            assert (builtins.isAttrs cache);
+            assert (builtins.isList dependencies);
+            let
+              enabledDependencies = filterEnabledDependencies {
+                inherit dependencies target;
+                features = enabledFeatures;
+              };
+              directDependencies = map depWithResolvedFeatures enabledDependencies;
+              foldOverCache = op: lib.foldl op cache directDependencies;
+            in
+            foldOverCache
+              (
+                cache: { packageId, features }:
+                  let
+                    cacheFeatures = cache.${packageId} or [ ];
+                    combinedFeatures = sortedUnique (cacheFeatures ++ features);
+                  in
+                  if cache ? ${packageId} && cache.${packageId} == combinedFeatures
+                  then cache
+                  else
+                    mergePackageFeatures {
+                      features = combinedFeatures;
+                      featuresByPackageId = cache;
+                      inherit crateConfigs packageId target runTests rootPackageId;
+                    }
+              );
+          cacheWithSelf =
+            let
+              cacheFeatures = featuresByPackageId.${packageId} or [ ];
+              combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
+            in
+            featuresByPackageId // {
+              "${packageId}" = combinedFeatures;
+            };
+          cacheWithDependencies =
+            resolveDependencies cacheWithSelf "dep"
+              (
+                crateConfig.dependencies or [ ]
+                ++ lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig.devDependencies or [ ])
+              );
+          cacheWithAll =
+            resolveDependencies
+              cacheWithDependencies "build"
+              (crateConfig.buildDependencies or [ ]);
+        in
+        cacheWithAll;
+
+    /* Returns the enabled dependencies given the enabled features. */
+    filterEnabledDependencies = { dependencies, features, target }:
+      assert (builtins.isList dependencies);
+      assert (builtins.isList features);
+      assert (builtins.isAttrs target);
+
+      lib.filter
+        (
+          dep:
+          let
+            targetFunc = dep.target or (features: true);
+          in
+          targetFunc { inherit features target; }
+          && (
+            !(dep.optional or false)
+            || builtins.any (doesFeatureEnableDependency dep) features
+          )
+        )
+        dependencies;
+
+    /* Returns whether the given feature should enable the given dependency. */
+    doesFeatureEnableDependency = dependency: feature:
+      let
+        name = dependency.rename or dependency.name;
+        prefix = "${name}/";
+        len = builtins.stringLength prefix;
+        startsWithPrefix = builtins.substring 0 len feature == prefix;
+      in
+      feature == name || feature == "dep:" + name || startsWithPrefix;
+
+    /* Returns the expanded features for the given inputFeatures by applying the
+      rules in featureMap.
+
+      featureMap is an attribute set which maps feature names to lists of further
+      feature names to enable in case this feature is selected.
+    */
+    expandFeatures = featureMap: inputFeatures:
+      assert (builtins.isAttrs featureMap);
+      assert (builtins.isList inputFeatures);
+      let
+        expandFeaturesNoCycle = oldSeen: inputFeatures:
+          if inputFeatures != [ ]
+          then
+            let
+              # The feature we're currently expanding.
+              feature = builtins.head inputFeatures;
+              # All the features we've seen/expanded so far, including the one
+              # we're currently processing.
+              seen = oldSeen // { ${feature} = 1; };
+              # Expand the feature but be careful to not re-introduce a feature
+              # that we've already seen: this can easily cause a cycle, see issue
+              # #209.
+              enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]);
+            in
+            [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
+          # No more features left, nothing to expand to.
+          else [ ];
+        outFeatures = expandFeaturesNoCycle { } inputFeatures;
+      in
+      sortedUnique outFeatures;
+
+    /* This function adds optional dependencies as features if they are enabled
+      indirectly by dependency features. This function mimics Cargo's behavior
+      described in a note at:
+      https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
+    */
+    enableFeatures = dependencies: features:
+      assert (builtins.isList features);
+      assert (builtins.isList dependencies);
+      let
+        additionalFeatures = lib.concatMap
+          (
+            dependency:
+              assert (builtins.isAttrs dependency);
+              let
+                enabled = builtins.any (doesFeatureEnableDependency dependency) features;
+              in
+              if (dependency.optional or false) && enabled
+              then [ (dependency.rename or dependency.name) ]
+              else [ ]
+          )
+          dependencies;
+      in
+      sortedUnique (features ++ additionalFeatures);
+
+    /*
+      Returns the actual features for the given dependency.
+
+      features: The features of the crate that refers this dependency.
+    */
+    dependencyFeatures = features: dependency:
+      assert (builtins.isList features);
+      assert (builtins.isAttrs dependency);
+      let
+        defaultOrNil =
+          if dependency.usesDefaultFeatures or true
+          then [ "default" ]
+          else [ ];
+        explicitFeatures = dependency.features or [ ];
+        additionalDependencyFeatures =
+          let
+            name = dependency.rename or dependency.name;
+            stripPrefixMatch = prefix: s:
+              if lib.hasPrefix prefix s
+              then lib.removePrefix prefix s
+              else null;
+            extractFeature = feature: lib.findFirst
+              (f: f != null)
+              null
+              (map (prefix: stripPrefixMatch prefix feature) [
+                (name + "/")
+                (name + "?/")
+              ]);
+            dependencyFeatures = lib.filter (f: f != null) (map extractFeature features);
+          in
+          dependencyFeatures;
+      in
+      defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures;
+
+    /* Sorts and removes duplicates from a list of strings. */
+    sortedUnique = features:
+      assert (builtins.isList features);
+      assert (builtins.all builtins.isString features);
+      let
+        outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features;
+        outFeaturesUnique = builtins.attrNames outFeaturesSet;
+      in
+      builtins.sort (a: b: a < b) outFeaturesUnique;
+
+    deprecationWarning = message: value:
+      if strictDeprecation
+      then builtins.throw "strictDeprecation enabled, aborting: ${message}"
+      else builtins.trace message value;
+
+    #
+    # crate2nix/default.nix (excerpt end)
+    #
+  };
+}
+
diff --git a/fun/paroxysm/Cargo.toml b/fun/paroxysm/Cargo.toml
index 4d282285fd..7207f46c30 100644
--- a/fun/paroxysm/Cargo.toml
+++ b/fun/paroxysm/Cargo.toml
@@ -7,7 +7,7 @@ version = "0.1.0"
 [dependencies]
 chrono = "0.4"
 config = "0.9"
-crimp = "0.2"
+crimp = { path = "../../net/crimp" }
 env_logger = "0.7"
 failure = "0.1"
 irc = "0.13"
diff --git a/fun/paroxysm/OWNERS b/fun/paroxysm/OWNERS
index 7f8beb1aa7..1120d0dcc8 100644
--- a/fun/paroxysm/OWNERS
+++ b/fun/paroxysm/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - eta
+eta
diff --git a/fun/paroxysm/default.nix b/fun/paroxysm/default.nix
index e4ce4df1ae..54f59dc476 100644
--- a/fun/paroxysm/default.nix
+++ b/fun/paroxysm/default.nix
@@ -1,14 +1,10 @@
-{ depot, pkgs, ... }:
+{ pkgs, ... }:
 
-depot.third_party.naersk.buildPackage {
-  name = "paroxysm";
-  version = "0.0.2";
-  src = ./.;
+let
+  cargoNix = import ./Cargo.nix {
+    inherit pkgs;
+    nixpkgs = pkgs.path;
+  };
+in
 
-  buildInputs = with pkgs; [
-    openssl
-    pkgconfig
-    postgresql.lib
-    curl
-  ];
-}
+cargoNix.rootCrate.build
diff --git a/fun/paroxysm/src/keyword.rs b/fun/paroxysm/src/keyword.rs
index 1b2b6ce592..fa40f5347a 100644
--- a/fun/paroxysm/src/keyword.rs
+++ b/fun/paroxysm/src/keyword.rs
@@ -1,8 +1,7 @@
 use crate::models::{Entry, Keyword, NewEntry, NewKeyword};
 use diesel::pg::PgConnection;
 use diesel::prelude::*;
-use failure::format_err;
-use failure::Error;
+use failure::{format_err, Error};
 use std::borrow::Cow;
 
 /// Maximum number of times we'll follow a `see: ` pointer.
diff --git a/fun/paroxysm/src/main.rs b/fun/paroxysm/src/main.rs
index 11a0e7bf60..46a215c225 100644
--- a/fun/paroxysm/src/main.rs
+++ b/fun/paroxysm/src/main.rs
@@ -7,8 +7,7 @@ use crate::cfg::Config;
 use crate::keyword::KeywordDetails;
 use diesel::pg::PgConnection;
 use diesel::r2d2::{ConnectionManager, Pool};
-use failure::format_err;
-use failure::Error;
+use failure::{format_err, Error};
 use irc::client::prelude::*;
 use lazy_static::lazy_static;
 use log::{debug, info, warn};
@@ -153,8 +152,9 @@ impl App {
         // Use `nick` here, so things like "grfn: see glittershark" work.
         let val = if let Some(last) = chan_lastmsgs.get(nick_to_grab) {
             if last.starts_with("\x01ACTION ") {
-                // Yes, this is inefficient, but it's better than writing some hacky CTCP parsing code
-                // I guess (also, characters are hard, so just blindly slicing seems like a bad idea)
+                // Yes, this is inefficient, but it's better than writing some hacky CTCP parsing
+                // code I guess (also, characters are hard, so just blindly slicing
+                // seems like a bad idea)
                 format!(
                     "* {} {}",
                     nick_to_grab,
@@ -208,7 +208,7 @@ impl App {
     pub fn handle_query(
         &mut self,
         target: &str,
-        nick: &str,
+        _nick: &str,
         chan: &str,
         query: Captures,
     ) -> Result<(), Error> {
diff --git a/fun/streamTVL/default.nix b/fun/streamTVL/default.nix
deleted file mode 100644
index 8d8263c9dd..0000000000
--- a/fun/streamTVL/default.nix
+++ /dev/null
@@ -1,19 +0,0 @@
-{ pkgs, ... }:
-
-pkgs.writeShellScriptBin "start-tvl-stream" ''
-  env LD_LIBRARY_PATH=/run/opengl-driver/lib/ ${pkgs.ffmpeg-full}/bin/ffmpeg \
-       -vsync 0 \
-       -hwaccel cuvid \
-       -init_hw_device cuda=0 -filter_hw_device 0 \
-       -f x11grab \
-       -video_size 1920x1080 \
-       -framerate 60 \
-       -thread_queue_size 256 \
-       -i :0.0+0,0 \
-       -filter:v "format=nv12,hwupload,scale_npp=w=1280:h=720:interp_algo=lanczos" \
-       -c:v h264_nvenc \
-       -preset:v llhq \
-       -rc:v cbr_ld_hq \
-       -an \
-       -f flv rtmp://tazj.in:1935/tvl
-''
diff --git a/fun/tvl-ebooks/OWNERS b/fun/tvl-ebooks/OWNERS
index 7dd8c27a57..a58e6540a3 100644
--- a/fun/tvl-ebooks/OWNERS
+++ b/fun/tvl-ebooks/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - ben
\ No newline at end of file
+ben
diff --git a/fun/tvl-ebooks/default.nix b/fun/tvl-ebooks/default.nix
index fde6e05822..68e61c2525 100644
--- a/fun/tvl-ebooks/default.nix
+++ b/fun/tvl-ebooks/default.nix
@@ -2,6 +2,6 @@
 
 pkgs.buildGoModule {
   name = "tvl-ebooks";
-  vendorSha256 = "1p7bazh2vbhvvm559bcvfff9s4yy4q9jmklxr3sfp97inwpv6hzy";
+  vendorHash = "sha256:1p7bazh2vbhvvm559bcvfff9s4yy4q9jmklxr3sfp97inwpv6hzy";
   src = ./.;
 }
diff --git a/fun/uggc/default.nix b/fun/uggc/default.nix
index ca622666dc..980ad16bcc 100644
--- a/fun/uggc/default.nix
+++ b/fun/uggc/default.nix
@@ -12,7 +12,8 @@ let
       gopkgs."github.com".pkg.browser.gopkg
     ];
   };
-in uggc.overrideAttrs(old: {
+in
+uggc.overrideAttrs (old: {
   buildCommand = old.buildCommand + ''
     install -D ${./uggc.desktop} $out/share/applications/uggc.desktop
     sed "s|@out@|$out|g" -i $out/share/applications/uggc.desktop
diff --git a/fun/πŸ•°οΈ/OWNERS b/fun/πŸ•°οΈ/OWNERS
index f16dd105d7..2e95807063 100644
--- a/fun/πŸ•°οΈ/OWNERS
+++ b/fun/πŸ•°οΈ/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - sterni
+sterni
diff --git a/fun/πŸ•°οΈ/default.nix b/fun/πŸ•°οΈ/default.nix
index 230d9f02f1..2b1a946400 100644
--- a/fun/πŸ•°οΈ/default.nix
+++ b/fun/πŸ•°οΈ/default.nix
@@ -38,6 +38,7 @@ let
       "ecl" # refuses to create non-ASCII paths even on POSIX…
     ];
   };
-in bin // {
+in
+bin // {
   inherit lib;
 }
diff --git a/lisp/klatre/OWNERS b/lisp/klatre/OWNERS
index ce7e0e37ee..b381c4e660 100644
--- a/lisp/klatre/OWNERS
+++ b/lisp/klatre/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - grfn
+aspen
diff --git a/net/alcoholic_jwt/Cargo.lock b/net/alcoholic_jwt/Cargo.lock
index 4f541cd8ac..4ca139e254 100644
--- a/net/alcoholic_jwt/Cargo.lock
+++ b/net/alcoholic_jwt/Cargo.lock
@@ -1,8 +1,10 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
 [[package]]
 name = "alcoholic_jwt"
-version = "1.0.1"
+version = "4091.0.0"
 dependencies = [
  "base64",
  "openssl",
@@ -12,37 +14,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "autocfg"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
-
-[[package]]
 name = "base64"
-version = "0.10.1"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
-dependencies = [
- "byteorder",
-]
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "bitflags"
-version = "1.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2da1976d75adbe5fbc88130ecd119529cf1cc6a93ae1546d8696ee66f0d21af1"
-
-[[package]]
-name = "byteorder"
-version = "1.4.3"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
 
 [[package]]
 name = "cc"
-version = "1.0.69"
+version = "1.0.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
+checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "cfg-if"
@@ -67,43 +57,54 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 
 [[package]]
 name = "itoa"
-version = "0.4.7"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
 [[package]]
 name = "libc"
-version = "0.2.99"
+version = "0.2.150"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
 
 [[package]]
 name = "once_cell"
-version = "1.8.0"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
 [[package]]
 name = "openssl"
-version = "0.10.35"
+version = "0.10.59"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "549430950c79ae24e6d02e0b7404534ecf311d94cc9f861e9e4020187d13d885"
+checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33"
 dependencies = [
  "bitflags",
  "cfg-if",
  "foreign-types",
  "libc",
  "once_cell",
+ "openssl-macros",
  "openssl-sys",
 ]
 
 [[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "openssl-sys"
-version = "0.9.65"
+version = "0.9.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a7907e3bfa08bb85105209cdfcb6c63d109f8f6c1ed6ca318fff5c1853fbc1d"
+checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9"
 dependencies = [
- "autocfg",
  "cc",
  "libc",
  "pkg-config",
@@ -112,45 +113,48 @@ dependencies = [
 
 [[package]]
 name = "pkg-config"
-version = "0.3.19"
+version = "0.3.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.28"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
 dependencies = [
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.9"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "ryu"
-version = "1.0.5"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
 
 [[package]]
 name = "serde"
-version = "1.0.127"
+version = "1.0.192"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
+checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+dependencies = [
+ "serde_derive",
+]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.127"
+version = "1.0.192"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc"
+checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -159,9 +163,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.66"
+version = "1.0.108"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
 dependencies = [
  "itoa",
  "ryu",
@@ -170,20 +174,20 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.74"
+version = "2.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
 dependencies = [
  "proc-macro2",
  "quote",
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
-name = "unicode-xid"
-version = "0.2.2"
+name = "unicode-ident"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "vcpkg"
diff --git a/net/alcoholic_jwt/Cargo.toml b/net/alcoholic_jwt/Cargo.toml
index 4430e06879..b5135d14d1 100644
--- a/net/alcoholic_jwt/Cargo.toml
+++ b/net/alcoholic_jwt/Cargo.toml
@@ -1,15 +1,16 @@
 [package]
 name = "alcoholic_jwt"
 description = "Library for validation of RS256 JWTs"
-version = "1.0.1"
-authors = ["Vincent Ambo <mail@tazj.in>"]
+version = "4091.0.0"
+authors = ["Vincent Ambo <tazjin@tvl.su>"]
 keywords = ["jwt", "token", "jwks"]
 categories = ["authentication"]
 license = "GPL-3.0-or-later"
-repository = "https://code.tvl.fyi/tree/net/alcoholic_jwt"
+homepage = "https://code.tvl.fyi/about/net/alcoholic_jwt"
+repository = "https://code.tvl.fyi/depot.git:/net/alcoholic_jwt.git"
 
 [dependencies]
-base64 = "0.10"
+base64 = "0.13"
 openssl = "0.10"
 serde = "1.0"
 serde_derive = "1.0"
diff --git a/net/alcoholic_jwt/README.md b/net/alcoholic_jwt/README.md
index b9ff57df4b..c6da0ab8da 100644
--- a/net/alcoholic_jwt/README.md
+++ b/net/alcoholic_jwt/README.md
@@ -1,8 +1,6 @@
 alcoholic_jwt
 =============
 
-[![Build Status](https://travis-ci.org/aprilabank/alcoholic_jwt.svg?branch=master)](https://travis-ci.org/aprilabank/alcoholic_jwt)
-
 This is a library for **validation** of **RS256** JWTs using keys from
 a JWKS. Nothing more, nothing less.
 
@@ -56,7 +54,19 @@ This library aims to only use trustworthy off-the-shelf components to
 do the work. Cryptographic operations are provided by the `openssl`
 crate, JSON-serialisation is provided by `serde_json`.
 
+## Contributing
+
+This project is developed in the [TVL monorepo][depot]. To work on it,
+you can either use a local clone of the entire repository or clone
+just the `alcoholic_jwt` subtree:
+
+    https://code.tvl.fyi/depot.git:/net/alcoholic_jwt.git
+
+Please follow the TVL [contribution guidelines][contributing].
+
 [Google]: https://www.google.com/
 [Aprila]: https://www.aprila.no/
 [JWKS]: https://tools.ietf.org/html/rfc7517
 [`kid` claim]: https://tools.ietf.org/html/rfc7515#section-4.1.4
+[depot]: https://code.tvl.fyi/
+[contributing]: https://code.tvl.fyi/about/docs/CONTRIBUTING.md
diff --git a/net/alcoholic_jwt/default.nix b/net/alcoholic_jwt/default.nix
index c6b84fb7ab..aa1df10c8b 100644
--- a/net/alcoholic_jwt/default.nix
+++ b/net/alcoholic_jwt/default.nix
@@ -4,6 +4,6 @@ depot.third_party.naersk.buildPackage {
   src = ./.;
   buildInputs = with pkgs; [
     openssl
-    pkgconfig
+    pkg-config
   ];
 }
diff --git a/net/alcoholic_jwt/src/lib.rs b/net/alcoholic_jwt/src/lib.rs
index 4acd8d1e90..4a3ece8af1 100644
--- a/net/alcoholic_jwt/src/lib.rs
+++ b/net/alcoholic_jwt/src/lib.rs
@@ -1,4 +1,4 @@
-// Copyright (C) 2018  Aprila Bank ASA
+// Copyright (C) 2019-2022 The TVL Community
 //
 // alcoholic_jwt is free software: you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
@@ -67,23 +67,26 @@
 //!
 //! [JWKS]: https://tools.ietf.org/html/rfc7517
 
-#[macro_use] extern crate serde_derive;
+#[macro_use]
+extern crate serde_derive;
 
 extern crate base64;
 extern crate openssl;
 extern crate serde;
 extern crate serde_json;
 
-use base64::{URL_SAFE_NO_PAD, Config, DecodeError};
+use base64::{Config, DecodeError, URL_SAFE_NO_PAD};
 use openssl::bn::BigNum;
 use openssl::error::ErrorStack;
 use openssl::hash::MessageDigest;
-use openssl::pkey::{Public, PKey};
+use openssl::pkey::{PKey, Public};
 use openssl::rsa::Rsa;
 use openssl::sign::Verifier;
 use serde::de::DeserializeOwned;
 use serde_json::Value;
-use std::time::{UNIX_EPOCH, Duration, SystemTime};
+use std::error::Error;
+use std::fmt::{self, Display};
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
 
 #[cfg(test)]
 mod tests;
@@ -101,12 +104,16 @@ fn jwt_forgiving() -> Config {
 /// JWT algorithm used. The only supported algorithm is currently
 /// RS256.
 #[derive(Clone, Deserialize, Debug)]
-enum KeyAlgorithm { RS256 }
+enum KeyAlgorithm {
+    RS256,
+}
 
 /// Type of key contained in a JWT. The only supported key type is
 /// currently RSA.
 #[derive(Clone, Deserialize, Debug)]
-enum KeyType { RSA }
+enum KeyType {
+    RSA,
+}
 
 /// Representation of a single JSON Web Key. See [RFC
 /// 7517](https://tools.ietf.org/html/rfc7517#section-4).
@@ -146,7 +153,7 @@ impl JWKS {
 
 /// Representation of an undecoded JSON Web Token. See [RFC
 /// 7519](https://tools.ietf.org/html/rfc7519).
-struct JWT<'a> (&'a str);
+struct JWT<'a>(&'a str);
 
 /// Representation of a decoded and validated JSON Web Token.
 ///
@@ -214,18 +221,56 @@ pub enum ValidationError {
     InvalidClaims(Vec<&'static str>),
 }
 
+impl Error for ValidationError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        match self {
+            ValidationError::InvalidBase64(e) => Some(e),
+            ValidationError::OpenSSL(e) => Some(e),
+            ValidationError::JSON(e) => Some(e),
+            ValidationError::InvalidComponents
+            | ValidationError::InvalidJWK
+            | ValidationError::InvalidSignature
+            | ValidationError::InvalidClaims(_) => None,
+        }
+    }
+}
+
+impl Display for ValidationError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            ValidationError::InvalidComponents => {
+                f.write_str("Invalid number of token components in JWT")
+            }
+            ValidationError::InvalidBase64(_) => f.write_str("Invalid Base64 encoding in JWT"),
+            ValidationError::InvalidJWK => f.write_str("JWK decoding failed"),
+            ValidationError::InvalidSignature => f.write_str("JWT signature validation failed"),
+            ValidationError::OpenSSL(e) => write!(f, "SSL error: {}", e),
+            ValidationError::JSON(e) => write!(f, "JSON error: {}", e),
+            ValidationError::InvalidClaims(errs) => {
+                write!(f, "Invalid claims: {}", errs.join(", "))
+            }
+        }
+    }
+}
+
 type JWTResult<T> = Result<T, ValidationError>;
 
 impl From<ErrorStack> for ValidationError {
-    fn from(err: ErrorStack) -> Self { ValidationError::OpenSSL(err) }
+    fn from(err: ErrorStack) -> Self {
+        ValidationError::OpenSSL(err)
+    }
 }
 
 impl From<serde_json::Error> for ValidationError {
-    fn from(err: serde_json::Error) -> Self { ValidationError::JSON(err) }
+    fn from(err: serde_json::Error) -> Self {
+        ValidationError::JSON(err)
+    }
 }
 
 impl From<DecodeError> for ValidationError {
-    fn from(err: DecodeError) -> Self { ValidationError::InvalidBase64(err) }
+    fn from(err: DecodeError) -> Self {
+        ValidationError::InvalidBase64(err)
+    }
 }
 
 /// Attempt to extract the `kid`-claim out of a JWT's header claims.
@@ -266,9 +311,7 @@ pub fn token_kid(token: &str) -> JWTResult<Option<String>> {
 ///
 /// It is the user's task to ensure that the correct JWK is passed in
 /// for validation.
-pub fn validate(token: &str,
-                jwk: &JWK,
-                validations: Vec<Validation>) -> JWTResult<ValidJWT> {
+pub fn validate(token: &str, jwk: &JWK, validations: Vec<Validation>) -> JWTResult<ValidJWT> {
     let jwt = JWT(token);
     let public_key = public_key_from_jwk(&jwk)?;
     validate_jwt_signature(&jwt, public_key)?;
@@ -279,7 +322,7 @@ pub fn validate(token: &str,
     if parts.len() != 3 {
         // This is unlikely considering that validation has already
         // been performed at this point, but better safe than sorry.
-        return Err(ValidationError::InvalidComponents)
+        return Err(ValidationError::InvalidComponents);
     }
 
     // Perform claim validations before constructing the valid token:
@@ -351,7 +394,7 @@ fn validate_jwt_signature(jwt: &JWT, key: Rsa<Public>) -> JWTResult<()> {
     verifier.update(data.as_bytes())?;
 
     match verifier.verify(&sig)? {
-        true  => Ok(()),
+        true => Ok(()),
         false => Err(ValidationError::InvalidSignature),
     }
 }
@@ -362,7 +405,7 @@ fn validate_jwt_signature(jwt: &JWT, key: Rsa<Public>) -> JWTResult<()> {
 #[serde(untagged)]
 enum Audience {
     Single(String),
-    Multi(Vec<String>)
+    Multi(Vec<String>),
 }
 
 /// Internal helper struct for claims that are relevant for claim
@@ -376,15 +419,14 @@ struct PartialClaims {
 }
 
 /// Apply a single validation to the claim set of a token.
-fn apply_validation(claims: &PartialClaims,
-                    validation: Validation) -> Result<(), &'static str> {
+fn apply_validation(claims: &PartialClaims, validation: Validation) -> Result<(), &'static str> {
     match validation {
         // Validate that an 'iss' claim is present and matches the
         // supplied value.
-        Validation::Issuer(iss) => {
-            match claims.iss {
-                None => Err("'iss' claim is missing"),
-                Some(ref claim) => if *claim == iss {
+        Validation::Issuer(iss) => match claims.iss {
+            None => Err("'iss' claim is missing"),
+            Some(ref claim) => {
+                if *claim == iss {
                     Ok(())
                 } else {
                     Err("'iss' claim does not match")
@@ -394,15 +436,17 @@ fn apply_validation(claims: &PartialClaims,
 
         // Validate that an 'aud' claim is present and matches the
         // supplied value.
-        Validation::Audience(aud) => {
-            match claims.aud {
-                None => Err("'aud' claim is missing"),
-                Some(Audience::Single(ref claim)) => if *claim == aud {
+        Validation::Audience(aud) => match claims.aud {
+            None => Err("'aud' claim is missing"),
+            Some(Audience::Single(ref claim)) => {
+                if *claim == aud {
                     Ok(())
                 } else {
                     Err("'aud' claim does not match")
-                },
-                Some(Audience::Multi(ref claims)) => if claims.contains(&aud) {
+                }
+            }
+            Some(Audience::Multi(ref claims)) => {
+                if claims.contains(&aud) {
                     Ok(())
                 } else {
                     Err("'aud' claim does not match")
@@ -447,12 +491,12 @@ fn apply_validation(claims: &PartialClaims,
 }
 
 /// Apply all requested validations to a partial claim set.
-fn validate_claims(claims: PartialClaims,
-                   validations: Vec<Validation>) -> JWTResult<()> {
-    let validation_errors: Vec<_> = validations.into_iter()
+fn validate_claims(claims: PartialClaims, validations: Vec<Validation>) -> JWTResult<()> {
+    let validation_errors: Vec<_> = validations
+        .into_iter()
         .map(|v| apply_validation(&claims, v))
         .filter_map(|result| match result {
-            Ok(_)    => None,
+            Ok(_) => None,
             Err(err) => Some(err),
         })
         .collect();
diff --git a/net/alcoholic_jwt/src/tests.rs b/net/alcoholic_jwt/src/tests.rs
index 81890986f8..c264bb5a13 100644
--- a/net/alcoholic_jwt/src/tests.rs
+++ b/net/alcoholic_jwt/src/tests.rs
@@ -1,4 +1,4 @@
-// Copyright (C) 2018  Aprila Bank ASA
+// Copyright (C) 2019-2022 The TVL Community
 //
 // alcoholic_jwt is free software: you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
@@ -21,14 +21,19 @@ fn test_fragment_decoding() {
     let bignum = decode_fragment(fragment).expect("Failed to decode fragment");
 
     let expected = "19947781743618558124649689124245117083485690334420160711273532766920651190711502679542723943527557680293732686428091794139998732541701457212387600480039297092835433997837314251024513773285252960725418984381935183495143908023024822433135775773958512751261112853383693442999603704969543668619221464654540065497665889289271044207667765128672709218996183649696030570183970367596949687544839066873508106034650634722970893169823917299050098551447676778961773465887890052852528696684907153295689693676910831376066659456592813140662563597179711588277621736656871685099184755908108451080261403193680966083938080206832839445289";
-    assert_eq!(expected, format!("{}", bignum), "Decoded fragment should match ");
+    assert_eq!(
+        expected,
+        format!("{}", bignum),
+        "Decoded fragment should match "
+    );
 }
 
 #[test]
 fn test_decode_find_jwks() {
     let json = "{\"keys\":[{\"kty\":\"RSA\",\"alg\":\"RS256\",\"use\":\"sig\",\"kid\":\"mUjI\\/rIMLLtung35BKZfdbrqtlEAAYJ4JX\\/SKvnLxJc=\",\"n\":\"ngRRjNbXgPW29oNtF0JgsyyfTwPyEL0u_X16s453X2AOc33XGFxVKLEQ7R_TiMenaKcr-tPifYqgps_deyi0XOr4I3SOdOMtAVKDZJCANe--CANOHZb-meIfjKhCHisvT90fm5Apd6qPRVsXsZ7A8pmClZHKM5fwZUkBv8NsPLm2Xy2sGOZIiwP_7z8m3j0abUzniPQsx2b3xcWimB9vRtshFHN1KgPUf1ALQ5xzLfJnlFkCxC7kmOxKC7_NpQ4kJR_DKzKFV_r3HxTqf-jddHcXIrrMcLQXCSyeLQtLaz7whQ4F-EfL42z4XgwPr4ji3sct2gWL13EqlbE5DDxLKQ\",\"e\":\"GK7oLCDbNPAF59LhvyseqcG04hDnPs58qGYolr_HHmaR4lulWJ90ozx6e4Ut363yKG2p9vwvivR5UIC-aLPtqT2qr-OtjhBFzUFVaMGZ6mPCvMKk0AgMYdOHvWTgBSqQtNJTvl1yYLnhcWyoE2fLQhoEbY9qUyCBCEOScXOZRDpnmBtz5I8q5yYMV6a920J24T_IYbxHgkGcEU2SGg-b1cOMD7Rja7vCfV---CQ2pR4leQ0jufzudDoe7z3mziJm-Ihcdrz2Ujy5kPEMdz6R55prJ-ENKrkD_X4u5aSlSRaetwmHS3oAVkjr1JwUNbqnpM-kOqieqHEp8LUmez-Znw\"}]}";
     let jwks: JWKS = serde_json::from_str(json).expect("Failed to decode JWKS");
-    let jwk = jwks.find("mUjI/rIMLLtung35BKZfdbrqtlEAAYJ4JX/SKvnLxJc=")
+    let jwk = jwks
+        .find("mUjI/rIMLLtung35BKZfdbrqtlEAAYJ4JX/SKvnLxJc=")
         .expect("Failed to find required JWK");
 
     public_key_from_jwk(&jwk).expect("Failed to construct public key from JWK");
@@ -39,18 +44,21 @@ fn test_token_kid() {
     let jwt = "eyJraWQiOiI4ckRxOFB3MEZaY2FvWFdURVZRbzcrVGYyWXpTTDFmQnhOS1BDZWJhYWk0PSIsImFsZyI6IlJTMjU2IiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdXRoLnRlc3QuYXByaWxhLm5vIiwiaWF0IjoxNTM2MDUwNjkzLCJleHAiOjE1MzYwNTQyOTMsInN1YiI6IjQyIiwiZXh0Ijoic21va2V0ZXN0IiwicHJ2IjoiYXJpc3RpIiwic2NwIjoicHJvY2VzcyJ9.gOLsv98109qLkmRK6Dn7WWRHLW7o8W78WZcWvFZoxPLzVO0qvRXXRLYc9h5chpfvcWreLZ4f1cOdvxv31_qnCRSQQPOeQ7r7hj_sPEDzhKjk-q2aoNHaGGJg1vabI--9EFkFsGQfoS7UbMMssS44dgR68XEnKtjn0Vys-Vzbvz_CBSCH6yQhRLik2SU2jR2L7BoFvh4LGZ6EKoQWzm8Z-CHXLGLUs4Hp5aPhF46dGzgAzwlPFW4t9G4DciX1uB4vv1XnfTc5wqJch6ltjKMde1GZwLR757a8dJSBcmGWze3UNE2YH_VLD7NCwH2kkqr3gh8rn7lWKG4AUIYPxsw9CB";
 
     let kid = token_kid(&jwt).expect("Failed to extract token KID");
-    assert_eq!(Some("8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=".into()),
-               kid, "Extracted KID did not match expected KID");
+    assert_eq!(
+        Some("8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=".into()),
+        kid,
+        "Extracted KID did not match expected KID"
+    );
 }
 
 #[test]
 fn test_validate_jwt() {
     let jwks_json = "{\"keys\":[{\"kty\":\"RSA\",\"alg\":\"RS256\",\"use\":\"sig\",\"kid\":\"8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=\",\"n\":\"l4UTgk1zr-8C8utt0E57DtBV6qqAPWzVRrIuQS2j0_hp2CviaNl5XzGRDnB8gwk0Hx95YOhJupAe6RNq5ok3fDdxL7DLvppJNRLz3Ag9CsmDLcbXgNEQys33fBJaPw1v3GcaFC4tisU5p-o1f5RfWwvwdBtdBfGiwT1GRvbc5sFx6M4iYjg9uv1lNKW60PqSJW4iDYrfqzZmB0zF1SJ0BL_rnQZ1Wi_UkFmNe9arM8W9tI9T3Ie59HITFuyVSTCt6qQEtSfa1e5PiBaVuV3qoFI2jPBiVZQ6LPGBWEDyz4QtrHLdECPPoTF30NN6TSVwwlRbCuUUrdNdXdjYe2dMFQ\",\"e\":\"DhaD5zC7mzaDvHO192wKT_9sfsVmdy8w8T8C9VG17_b1jG2srd3cmc6Ycw-0blDf53Wrpi9-KGZXKHX6_uIuJK249WhkP7N1SHrTJxO0sUJ8AhK482PLF09Qtu6cUfJqY1X1y1S2vACJZItU4Vjr3YAfiVGQXeA8frAf7Sm4O1CBStCyg6yCcIbGojII0jfh2vSB-GD9ok1F69Nmk-R-bClyqMCV_Oq-5a0gqClVS8pDyGYMgKTww2RHgZaFSUcG13KeLMQsG2UOB2OjSC8FkOXK00NBlAjU3d0Vv-IamaLIszO7FQBY3Oh0uxNOvIE9ofQyCOpB-xIK6V9CTTphxw\"}]}";
 
-    let jwks: JWKS = serde_json::from_str(jwks_json)
-        .expect("Failed to decode JWKS");
+    let jwks: JWKS = serde_json::from_str(jwks_json).expect("Failed to decode JWKS");
 
-    let jwk = jwks.find("8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=")
+    let jwk = jwks
+        .find("8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=")
         .expect("Failed to find required JWK");
 
     let pkey = public_key_from_jwk(&jwk).expect("Failed to construct public key");
diff --git a/net/crimp/Cargo.lock b/net/crimp/Cargo.lock
index 9875e6004b..74a5a2c474 100644
--- a/net/crimp/Cargo.lock
+++ b/net/crimp/Cargo.lock
@@ -1,20 +1,16 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-[[package]]
-name = "autocfg"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+version = 3
 
 [[package]]
 name = "cc"
-version = "1.0.69"
+version = "1.0.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
 
 [[package]]
 name = "crimp"
-version = "0.2.2"
+version = "4087.0.0"
 dependencies = [
  "curl",
  "serde",
@@ -23,9 +19,9 @@ dependencies = [
 
 [[package]]
 name = "curl"
-version = "0.4.38"
+version = "0.4.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "003cb79c1c6d1c93344c7e1201bb51c2148f24ec2bd9c253709d6b2efb796515"
+checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6"
 dependencies = [
  "curl-sys",
  "libc",
@@ -33,14 +29,14 @@ dependencies = [
  "openssl-sys",
  "schannel",
  "socket2",
- "winapi",
+ "windows-sys",
 ]
 
 [[package]]
 name = "curl-sys"
-version = "0.4.45+curl-7.78.0"
+version = "0.4.72+curl-8.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de9e5a72b1c744eb5dd20b2be4d7eb84625070bb5c4ab9b347b70464ab1e62eb"
+checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea"
 dependencies = [
  "cc",
  "libc",
@@ -48,32 +44,26 @@ dependencies = [
  "openssl-sys",
  "pkg-config",
  "vcpkg",
- "winapi",
+ "windows-sys",
 ]
 
 [[package]]
 name = "itoa"
-version = "0.4.7"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
 
 [[package]]
 name = "libc"
-version = "0.2.99"
+version = "0.2.153"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
 
 [[package]]
 name = "libz-sys"
-version = "1.1.3"
+version = "1.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
+checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6"
 dependencies = [
  "cc",
  "libc",
@@ -83,17 +73,16 @@ dependencies = [
 
 [[package]]
 name = "openssl-probe"
-version = "0.1.4"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.65"
+version = "0.9.101"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a7907e3bfa08bb85105209cdfcb6c63d109f8f6c1ed6ca318fff5c1853fbc1d"
+checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
 dependencies = [
- "autocfg",
  "cc",
  "libc",
  "pkg-config",
@@ -102,37 +91,68 @@ dependencies = [
 
 [[package]]
 name = "pkg-config"
-version = "0.3.19"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
 
 [[package]]
 name = "ryu"
-version = "1.0.5"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
 
 [[package]]
 name = "schannel"
-version = "0.1.19"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
 dependencies = [
- "lazy_static",
- "winapi",
+ "windows-sys",
 ]
 
 [[package]]
 name = "serde"
-version = "1.0.127"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
 
 [[package]]
 name = "serde_json"
-version = "1.0.66"
+version = "1.0.114"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127"
+checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
 dependencies = [
  "itoa",
  "ryu",
@@ -141,38 +161,99 @@ dependencies = [
 
 [[package]]
 name = "socket2"
-version = "0.4.1"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad"
+checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
 dependencies = [
  "libc",
- "winapi",
+ "windows-sys",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
 ]
 
 [[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
 name = "vcpkg"
 version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
 [[package]]
-name = "winapi"
-version = "0.3.9"
+name = "windows-sys"
+version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "windows-targets",
 ]
 
 [[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
+name = "windows-targets"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
 
 [[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "windows_x86_64_msvc"
+version = "0.52.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
diff --git a/net/crimp/Cargo.toml b/net/crimp/Cargo.toml
index 13c80f1f69..1e529cfadc 100644
--- a/net/crimp/Cargo.toml
+++ b/net/crimp/Cargo.toml
@@ -1,12 +1,14 @@
 [package]
 name = "crimp"
 description = "Higher-level Rust API for cURL bindings"
-version = "0.2.2"
-authors = ["Vincent Ambo <mail@tazj.in>"]
+version = "4087.0.0"
+authors = ["Vincent Ambo <tazjin@tvl.su>"]
 keywords = [ "http", "curl" ]
 categories = [ "api-bindings" ]
 license = "GPL-3.0-or-later"
-repository = "https://github.com/tazjin/crimp"
+homepage = "https://code.tvl.fyi/about/net/crimp"
+repository = "https://code.tvl.fyi/depot.git:/net/crimp.git"
+
 
 [features]
 default = [ "json" ]
diff --git a/net/crimp/README.md b/net/crimp/README.md
index 791c354fc1..65a9d4293b 100644
--- a/net/crimp/README.md
+++ b/net/crimp/README.md
@@ -1,7 +1,6 @@
 crimp
 =====
 
-[![Build Status](https://travis-ci.org/tazjin/crimp.svg?branch=master)](https://travis-ci.org/tazjin/crimp)
 [![](https://img.shields.io/crates/v/crimp.svg)](https://crates.io/crates/crimp)
 [![](https://docs.rs/crimp/badge.svg)](https://docs.rs/crimp)
 
@@ -11,5 +10,17 @@ cURL.
 The documentation for this crate is primarily in the [module
 documentation][]
 
+-------
+
+This project is developed in the [TVL monorepo][depot]. To work on it,
+you can either use a local clone of the entire repository or clone
+just the `crimp` subtree:
+
+    https://code.tvl.fyi/depot.git:/net/crimp.git
+
+Please follow the TVL [contribution guidelines][contributing].
+
 [Rust bindings]: https://docs.rs/curl
 [module documentation]: https://docs.rs/crimp
+[depot]: https://code.tvl.fyi/
+[contributing]: https://code.tvl.fyi/about/docs/CONTRIBUTING.md
diff --git a/net/crimp/default.nix b/net/crimp/default.nix
index c6b84fb7ab..aa1df10c8b 100644
--- a/net/crimp/default.nix
+++ b/net/crimp/default.nix
@@ -4,6 +4,6 @@ depot.third_party.naersk.buildPackage {
   src = ./.;
   buildInputs = with pkgs; [
     openssl
-    pkgconfig
+    pkg-config
   ];
 }
diff --git a/net/crimp/src/lib.rs b/net/crimp/src/lib.rs
index b52ebc3ef0..7dfd261ee0 100644
--- a/net/crimp/src/lib.rs
+++ b/net/crimp/src/lib.rs
@@ -33,9 +33,12 @@
 //! use crimp::Request;
 //!
 //! let response = Request::get("http://httpbin.org/get")
-//!     .user_agent("crimp test suite").unwrap()
-//!     .send().unwrap()
-//!     .as_string().unwrap();
+//!     .user_agent("crimp test suite")
+//!     .unwrap()
+//!     .send()
+//!     .unwrap()
+//!     .as_string()
+//!     .unwrap();
 //!
 //! println!("Status: {}\nBody: {}", response.status, response.body);
 //! # assert_eq!(response.status, 200);
@@ -54,10 +57,9 @@
 //!
 //! All optional features are enabled by default.
 //!
-//! * `json`: Adds `Request::json` and `Response::as_json` methods
-//!   which can be used for convenient serialisation of
-//!   request/response bodies using `serde_json`. This feature adds a
-//!   dependency on the `serde` and `serde_json` crates.
+//! * `json`: Adds `Request::json` and `Response::as_json` methods which can be used for convenient
+//!   serialisation of request/response bodies using `serde_json`. This feature adds a dependency on
+//!   the `serde` and `serde_json` crates.
 //!
 //! ## Initialisation
 //!
@@ -72,32 +74,42 @@
 
 extern crate curl;
 
-#[cfg(feature = "json")] extern crate serde;
-#[cfg(feature = "json")] extern crate serde_json;
+#[cfg(feature = "json")]
+extern crate serde;
+#[cfg(feature = "json")]
+extern crate serde_json;
 
 pub use curl::init;
 
-use curl::easy::{Auth, Easy, Form, List, Transfer, ReadError, WriteError};
+use curl::easy::{Auth, Easy, Form, List, ReadError, Transfer, WriteError};
 use std::collections::HashMap;
 use std::io::Write;
 use std::path::Path;
 use std::string::{FromUtf8Error, ToString};
 use std::time::Duration;
 
-#[cfg(feature = "json")] use serde::Serialize;
-#[cfg(feature = "json")] use serde::de::DeserializeOwned;
+#[cfg(feature = "json")]
+use serde::de::DeserializeOwned;
+#[cfg(feature = "json")]
+use serde::Serialize;
 
 #[cfg(test)]
 mod tests;
 
 /// HTTP method to use for the request.
 enum Method {
-    Get, Post, Put, Patch, Delete
+    Get,
+    Post,
+    Put,
+    Patch,
+    Delete,
 }
 
 /// Certificate types for client-certificate key pairs.
 pub enum CertType {
-    P12, PEM, DER
+    P12,
+    PEM,
+    DER,
 }
 
 /// Builder structure for an HTTP request.
@@ -145,7 +157,7 @@ pub struct Response<T> {
     pub body: T,
 }
 
-impl <'a> Request<'a> {
+impl<'a> Request<'a> {
     /// Initiate an HTTP request with the given method and URL.
     fn new(method: Method, url: &'a str) -> Self {
         Request {
@@ -158,19 +170,29 @@ impl <'a> Request<'a> {
     }
 
     /// Initiate a GET request with the given URL.
-    pub fn get(url: &'a str) -> Self { Request::new(Method::Get, url) }
+    pub fn get(url: &'a str) -> Self {
+        Request::new(Method::Get, url)
+    }
 
     /// Initiate a POST request with the given URL.
-    pub fn post(url: &'a str) -> Self { Request::new(Method::Post, url) }
+    pub fn post(url: &'a str) -> Self {
+        Request::new(Method::Post, url)
+    }
 
     /// Initiate a PUT request with the given URL.
-    pub fn put(url: &'a str) -> Self { Request::new(Method::Put, url) }
+    pub fn put(url: &'a str) -> Self {
+        Request::new(Method::Put, url)
+    }
 
     /// Initiate a PATCH request with the given URL.
-    pub fn patch(url: &'a str) -> Self { Request::new(Method::Patch, url) }
+    pub fn patch(url: &'a str) -> Self {
+        Request::new(Method::Patch, url)
+    }
 
     /// Initiate a DELETE request with the given URL.
-    pub fn delete(url: &'a str) -> Self { Request::new(Method::Delete, url) }
+    pub fn delete(url: &'a str) -> Self {
+        Request::new(Method::Delete, url)
+    }
 
     /// Add an HTTP header to a request.
     pub fn header(mut self, k: &str, v: &str) -> Result<Self, curl::Error> {
@@ -188,7 +210,8 @@ impl <'a> Request<'a> {
     /// Set the `Authorization` header to a `Bearer` value with the
     /// supplied token.
     pub fn bearer_auth(mut self, token: &str) -> Result<Self, curl::Error> {
-        self.headers.append(&format!("Authorization: Bearer {}", token))?;
+        self.headers
+            .append(&format!("Authorization: Bearer {}", token))?;
         Ok(self)
     }
 
@@ -212,8 +235,11 @@ impl <'a> Request<'a> {
     /// Consult the documentation for the `ssl_cert` and `ssl_key`
     /// functions in `curl::easy::Easy2` for details on supported
     /// formats and defaults.
-    pub fn tls_client_cert<P: AsRef<Path>>(mut self, cert_type: CertType, cert: P)
-                                           -> Result<Self, curl::Error> {
+    pub fn tls_client_cert<P: AsRef<Path>>(
+        mut self,
+        cert_type: CertType,
+        cert: P,
+    ) -> Result<Self, curl::Error> {
         self.handle.ssl_cert(cert)?;
         self.handle.ssl_cert_type(match cert_type {
             CertType::P12 => "P12",
@@ -262,13 +288,17 @@ impl <'a> Request<'a> {
     /// ```
     /// # use crimp::Request;
     /// let response = Request::get("https://httpbin.org/get")
-    ///     .with_handle(|mut handle| handle.referer("Example-Referer")).unwrap()
-    ///     .send().unwrap();
+    ///     .with_handle(|mut handle| handle.referer("Example-Referer"))
+    ///     .unwrap()
+    ///     .send()
+    ///     .unwrap();
     /// #
     /// # assert!(response.is_success());
     /// ```
     pub fn with_handle<F>(mut self, function: F) -> Result<Self, curl::Error>
-    where F: FnOnce(&mut Easy) -> Result<(), curl::Error> {
+    where
+        F: FnOnce(&mut Easy) -> Result<(), curl::Error>,
+    {
         function(&mut self.handle)?;
         Ok(self)
     }
@@ -293,12 +323,15 @@ impl <'a> Request<'a> {
     /// let mut form = Form::new();
     /// form.part("some-name")
     ///     .contents("some-data".as_bytes())
-    ///     .add().unwrap();
+    ///     .add()
+    ///     .unwrap();
     ///
     /// let response = Request::post("https://httpbin.org/post")
-    ///     .user_agent("crimp test suite").unwrap()
+    ///     .user_agent("crimp test suite")
+    ///     .unwrap()
     ///     .form(form)
-    ///     .send().unwrap();
+    ///     .send()
+    ///     .unwrap();
     /// #
     /// # assert_eq!(200, response.status, "form POST should succeed");
     /// # assert_eq!(
@@ -330,10 +363,10 @@ impl <'a> Request<'a> {
         self.handle.url(self.url)?;
 
         match self.method {
-            Method::Get    => self.handle.get(true)?,
-            Method::Post   => self.handle.post(true)?,
-            Method::Put    => self.handle.put(true)?,
-            Method::Patch  => self.handle.custom_request("PATCH")?,
+            Method::Get => self.handle.get(true)?,
+            Method::Post => self.handle.post(true)?,
+            Method::Put => self.handle.upload(true)?,
+            Method::Patch => self.handle.custom_request("PATCH")?,
             Method::Delete => self.handle.custom_request("DELETE")?,
         }
 
@@ -351,21 +384,31 @@ impl <'a> Request<'a> {
 
         // Optionally set content type if a body payload is configured
         // and configure the expected body size (or form payload).
-         match self.body {
+        match self.body {
             Body::Bytes { content_type, data } => {
-                self.handle.post_field_size(data.len() as u64)?;
-                self.headers.append(&format!("Content-Type: {}", content_type))?;
-            },
+                match self.method {
+                    Method::Put => self.handle.in_filesize(data.len() as u64)?,
+                    // TODO(sterni): this may still be wrong for some request types?
+                    _ => self.handle.post_field_size(data.len() as u64)?,
+                };
+
+                self.headers
+                    .append(&format!("Content-Type: {}", content_type))?;
+            }
 
             #[cfg(feature = "json")]
             Body::Json(ref data) => {
-                self.handle.post_field_size(data.len() as u64)?;
+                match self.method {
+                    Method::Put => self.handle.in_filesize(data.len() as u64)?,
+                    // TODO(sterni): this may still be wrong for some request types?
+                    _ => self.handle.post_field_size(data.len() as u64)?,
+                };
                 self.headers.append("Content-Type: application/json")?;
-            },
+            }
 
-             // Do not set content-type header at all if there is no
-             // body, or if the form handler was invoked above.
-             _ => (),
+            // Do not set content-type header at all if there is no
+            // body, or if the form handler was invoked above.
+            _ => (),
         };
 
         // Configure headers on the request:
@@ -407,9 +450,7 @@ impl <'a> Request<'a> {
                     return true;
                 }
 
-                headers.insert(
-                    split[0].trim().to_string(), split[1].trim().to_string()
-                );
+                headers.insert(split[0].trim().to_string(), split[1].trim().to_string());
                 true
             })?;
 
@@ -427,7 +468,7 @@ impl <'a> Request<'a> {
         Ok(Response {
             status: self.handle.response_code()?,
             headers,
-            body
+            body,
         })
     }
 }
@@ -438,13 +479,14 @@ impl <'a> Request<'a> {
 ///
 /// As we manually set the expected upload size, cURL will call the
 /// read callback repeatedly until it has all the data it needs.
-fn chunked_read_function<'easy, 'data>(transfer: &mut Transfer<'easy, 'data>,
-                                       data: &'data [u8]) -> Result<(), curl::Error> {
+fn chunked_read_function<'easy, 'data>(
+    transfer: &mut Transfer<'easy, 'data>,
+    data: &'data [u8],
+) -> Result<(), curl::Error> {
     let mut data = data;
 
     transfer.read_function(move |mut into| {
-        let written = into.write(data)
-            .map_err(|_| ReadError::Abort)?;
+        let written = into.write(data).map_err(|_| ReadError::Abort)?;
 
         data = &data[written..];
 
@@ -452,7 +494,7 @@ fn chunked_read_function<'easy, 'data>(transfer: &mut Transfer<'easy, 'data>,
     })
 }
 
-impl <T> Response<T> {
+impl<T> Response<T> {
     /// Check whether the status code of this HTTP response is a
     /// success (i.e. in the 200-299 range).
     pub fn is_success(&self) -> bool {
@@ -466,9 +508,11 @@ impl <T> Response<T> {
     /// This function exists for convenience to avoid having to write
     /// repetitive `if !response.is_success() { ... }` blocks.
     pub fn error_for_status<F, E>(self, closure: F) -> Result<Self, E>
-    where F: FnOnce(Self) -> E {
+    where
+        F: FnOnce(Self) -> E,
+    {
         if !self.is_success() {
-            return Err(closure(self))
+            return Err(closure(self));
         }
 
         Ok(self)
diff --git a/net/crimp/src/tests.rs b/net/crimp/src/tests.rs
index 6c2bc4f5b3..e8e9223ce8 100644
--- a/net/crimp/src/tests.rs
+++ b/net/crimp/src/tests.rs
@@ -6,7 +6,7 @@
 //    docker run --rm -p 4662:80 kennethreitz/httpbin
 
 use super::*;
-use serde_json::{Value, json};
+use serde_json::{json, Value};
 
 // These tests check whether the correct HTTP method is used in the
 // requests.
@@ -14,7 +14,8 @@ use serde_json::{Value, json};
 #[test]
 fn test_http_get() {
     let resp = Request::get("http://127.0.0.1:4662/get")
-        .send().expect("failed to send request");
+        .send()
+        .expect("failed to send request");
 
     assert!(resp.is_success(), "request should have succeeded");
 }
@@ -22,7 +23,8 @@ fn test_http_get() {
 #[test]
 fn test_http_delete() {
     let resp = Request::delete("http://127.0.0.1:4662/delete")
-        .send().expect("failed to send request");
+        .send()
+        .expect("failed to send request");
 
     assert_eq!(200, resp.status, "response status should be 200 OK");
 }
@@ -30,7 +32,8 @@ fn test_http_delete() {
 #[test]
 fn test_http_put() {
     let resp = Request::put("http://127.0.0.1:4662/put")
-        .send().expect("failed to send request");
+        .send()
+        .expect("failed to send request");
 
     assert_eq!(200, resp.status, "response status should be 200 OK");
 }
@@ -38,7 +41,8 @@ fn test_http_put() {
 #[test]
 fn test_http_patch() {
     let resp = Request::patch("http://127.0.0.1:4662/patch")
-        .send().expect("failed to send request");
+        .send()
+        .expect("failed to send request");
 
     assert_eq!(200, resp.status, "response status should be 200 OK");
 }
@@ -50,18 +54,25 @@ fn test_http_patch() {
 fn test_http_post() {
     let body = "test body";
     let response = Request::post("http://127.0.0.1:4662/post")
-        .user_agent("crimp test suite").expect("failed to set user-agent")
-        .timeout(Duration::from_secs(5)).expect("failed to set request timeout")
+        .user_agent("crimp test suite")
+        .expect("failed to set user-agent")
+        .timeout(Duration::from_secs(5))
+        .expect("failed to set request timeout")
         .body("text/plain", &body.as_bytes())
-        .send().expect("failed to send request")
-        .as_json::<Value>().expect("failed to deserialize response");
+        .send()
+        .expect("failed to send request")
+        .as_json::<Value>()
+        .expect("failed to deserialize response");
 
     let data = response.body;
 
     assert_eq!(200, response.status, "response status should be 200 OK");
 
-    assert_eq!(data.get("data").unwrap(), &json!("test body"),
-               "test body should have been POSTed");
+    assert_eq!(
+        data.get("data").unwrap(),
+        &json!("test body"),
+        "test body should have been POSTed"
+    );
 
     assert_eq!(
         data.get("headers").unwrap().get("Content-Type").unwrap(),
@@ -70,26 +81,34 @@ fn test_http_post() {
     );
 }
 
-#[cfg(feature = "json")] #[test]
+#[cfg(feature = "json")]
+#[test]
 fn test_http_post_json() {
     let body = json!({
         "purpose": "testing!"
     });
 
     let response = Request::post("http://127.0.0.1:4662/post")
-        .user_agent("crimp test suite").expect("failed to set user-agent")
-        .timeout(Duration::from_secs(5)).expect("failed to set request timeout")
-        .json(&body).expect("request serialization failed")
-        .send().expect("failed to send request")
-        .as_json::<Value>().expect("failed to deserialize response");
-
+        .user_agent("crimp test suite")
+        .expect("failed to set user-agent")
+        .timeout(Duration::from_secs(5))
+        .expect("failed to set request timeout")
+        .json(&body)
+        .expect("request serialization failed")
+        .send()
+        .expect("failed to send request")
+        .as_json::<Value>()
+        .expect("failed to deserialize response");
 
     let data = response.body;
 
     assert_eq!(200, response.status, "response status should be 200 OK");
 
-    assert_eq!(data.get("json").unwrap(), &body,
-               "test body should have been POSTed");
+    assert_eq!(
+        data.get("json").unwrap(),
+        &body,
+        "test body should have been POSTed"
+    );
 
     assert_eq!(
         data.get("headers").unwrap().get("Content-Type").unwrap(),
@@ -104,8 +123,10 @@ fn test_http_post_json() {
 #[test]
 fn test_bearer_auth() {
     let response = Request::get("http://127.0.0.1:4662/bearer")
-        .bearer_auth("some-token").expect("failed to set auth header")
-        .send().expect("failed to send request");
+        .bearer_auth("some-token")
+        .expect("failed to set auth header")
+        .send()
+        .expect("failed to send request");
 
     assert!(response.is_success(), "authorized request should succeed");
 }
@@ -115,8 +136,10 @@ fn test_basic_auth() {
     let request = Request::get("http://127.0.0.1:4662/basic-auth/alan_watts/oneness");
 
     let response = request
-        .basic_auth("alan_watts", "oneness").expect("failed to set auth header")
-        .send().expect("failed to send request");
+        .basic_auth("alan_watts", "oneness")
+        .expect("failed to set auth header")
+        .send()
+        .expect("failed to send request");
 
     assert!(response.is_success(), "authorized request should succeed");
 }
@@ -129,14 +152,20 @@ fn test_large_body() {
 
     let resp = Request::post("http://127.0.0.1:4662/post")
         .body("application/octet-stream", &[0; BODY_SIZE])
-        .send().expect("sending request")
-        .as_json::<Value>().expect("JSON deserialisation");
+        .send()
+        .expect("sending request")
+        .as_json::<Value>()
+        .expect("JSON deserialisation");
 
     // httpbin returns the uploaded data as a string in the `data`
     // field.
     let data = resp.body.get("data").unwrap().as_str().unwrap();
 
-    assert_eq!(BODY_SIZE, data.len(), "uploaded data length should be correct");
+    assert_eq!(
+        BODY_SIZE,
+        data.len(),
+        "uploaded data length should be correct"
+    );
 }
 
 // Tests for various other features.
@@ -144,9 +173,13 @@ fn test_large_body() {
 #[test]
 fn test_error_for_status() {
     let response = Request::get("http://127.0.0.1:4662/patch")
-        .send().expect("failed to send request")
+        .send()
+        .expect("failed to send request")
         .error_for_status(|resp| format!("Response error code: {}", resp.status));
 
-    assert_eq!(Err("Response error code: 405".into()), response,
-               "returned error should be converted into Result::Err");
+    assert_eq!(
+        Err("Response error code: 405".into()),
+        response,
+        "returned error should be converted into Result::Err"
+    );
 }
diff --git a/nix/OWNERS b/nix/OWNERS
index a742d0d22b..a640227914 100644
--- a/nix/OWNERS
+++ b/nix/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - Profpatsch
+Profpatsch
diff --git a/nix/binify/default.nix b/nix/binify/default.nix
index d40930fd33..a9900caf43 100644
--- a/nix/binify/default.nix
+++ b/nix/binify/default.nix
@@ -10,7 +10,7 @@
 # with `binify { exe = …; name = "hello" }`.
 { exe, name }:
 
-pkgs.runCommandLocal "${name}-bin" {} ''
+pkgs.runCommandLocal "${name}-bin" { } ''
   mkdir -p $out/bin
   ln -sT ${lib.escapeShellArg exe} $out/bin/${lib.escapeShellArg name}
 ''
diff --git a/nix/bufCheck/default.nix b/nix/bufCheck/default.nix
index 039303ba68..ec98cfc376 100644
--- a/nix/bufCheck/default.nix
+++ b/nix/bufCheck/default.nix
@@ -1,9 +1,26 @@
-# Check protobuf syntax and breaking.
+# Check protobuf breaking. Lints already happen in individual targets.
 #
-{ depot, pkgs, ... }:
+{ depot, pkgs, lib, ... }:
 
-pkgs.writeShellScriptBin "ci-buf-check" ''
-  ${depot.third_party.bufbuild}/bin/buf check lint --input .
-  # Report-only
-  ${depot.third_party.bufbuild}/bin/buf check breaking --input "." --against-input "./.git#branch=canon" || true
-''
+let
+  inherit (depot.nix) bufCheck;# self reference
+
+  script = pkgs.writeShellScriptBin "ci-buf-check" ''
+    export PATH="$PATH:${pkgs.lib.makeBinPath [ pkgs.buf ]}"
+    # Report-only
+    (cd $(git rev-parse --show-toplevel) && (buf breaking . --against "./.git#ref=HEAD~1" || true))
+  '';
+in
+
+script.overrideAttrs (old: {
+  meta = lib.recursiveUpdate old.meta {
+    # Protobuf check step executed in the buildkite pipeline which
+    # validates that changes to .proto files between revisions
+    # don't cause backwards-incompatible or otherwise flawed changes.
+    ci.extraSteps.protoCheck = {
+      alwaysRun = true;
+      label = ":water_buffalo: protoCheck";
+      command = pkgs.writeShellScript "ci-buf-check-step" "exec ${depot.nix.bufCheck}/bin/ci-buf-check";
+    };
+  };
+})
diff --git a/nix/buildGo/README.md b/nix/buildGo/README.md
index 37e0c06933..e9667c039a 100644
--- a/nix/buildGo/README.md
+++ b/nix/buildGo/README.md
@@ -2,8 +2,7 @@ buildGo.nix
 ===========
 
 This is an alternative [Nix][] build system for [Go][]. It supports building Go
-libraries and programs, and even automatically generating Protobuf & gRPC
-libraries.
+libraries and programs.
 
 *Note:* This will probably end up being folded into [Nixery][].
 
@@ -33,7 +32,6 @@ Given a program layout like this:
 β”œβ”€β”€ lib          <-- some library component
 β”‚Β Β  β”œβ”€β”€ bar.go
 β”‚Β Β  └── foo.go
-β”œβ”€β”€ api.proto    <-- gRPC API definition
 β”œβ”€β”€ main.go      <-- program implementation
 └── default.nix  <-- build instructions
 ```
@@ -44,11 +42,6 @@ The contents of `default.nix` could look like this:
 { buildGo }:
 
 let
-  api = buildGo.grpc {
-    name  = "someapi";
-    proto = ./api.proto;
-  };
-
   lib = buildGo.package {
     name = "somelib";
     srcs = [
@@ -58,7 +51,7 @@ let
   };
 in buildGo.program {
   name = "my-program";
-  deps = [ api lib ];
+  deps = [ lib ];
 
   srcs = [
     ./main.go
@@ -105,22 +98,6 @@ in buildGo.program {
   | `src`     | `path`         | Path to the source **directory**              | yes       |
   | `deps`    | `list<drv>`    | List of dependencies (i.e. other Go packages) | no        |
 
-  For some examples of how `buildGo.external` is used, check out
-  [`proto.nix`](./proto.nix).
-
-* `buildGo.proto`: Build a Go library out of the specified Protobuf definition.
-
-  | parameter   | type        | use                                              | required? |
-  |-------------|-------------|--------------------------------------------------|-----------|
-  | `name`      | `string`    | Name for the resulting library                   | yes       |
-  | `proto`     | `path`      | Path to the Protobuf definition file             | yes       |
-  | `path`      | `string`    | Import path for the resulting Go library         | no        |
-  | `extraDeps` | `list<drv>` | Additional Go dependencies to add to the library | no        |
-
-* `buildGo.grpc`: Build a Go library out of the specified gRPC definition.
-
-  The parameters are identical to `buildGo.proto`.
-
 ## Current status
 
 This project is work-in-progress. Crucially it is lacking the following features:
diff --git a/nix/buildGo/default.nix b/nix/buildGo/default.nix
index a2396dc3f7..c93642a127 100644
--- a/nix/buildGo/default.nix
+++ b/nix/buildGo/default.nix
@@ -4,8 +4,9 @@
 # buildGo provides Nix functions to build Go packages in the style of Bazel's
 # rules_go.
 
-{ pkgs ? import <nixpkgs> {}
-, ... }:
+{ pkgs ? import <nixpkgs> { }
+, ...
+}:
 
 let
   inherit (builtins)
@@ -21,7 +22,8 @@ let
     replaceStrings
     toString;
 
-  inherit (pkgs) lib go runCommand fetchFromGitHub protobuf symlinkJoin;
+  inherit (pkgs) lib runCommand fetchFromGitHub protobuf symlinkJoin go;
+  goStdlib = buildStdlib go;
 
   # Helpers for low-level Go compiler invocations
   spaceOut = lib.concatStringsSep " ";
@@ -40,8 +42,6 @@ let
 
   xFlags = x_defs: spaceOut (map (k: "-X ${k}=${x_defs."${k}"}") (attrNames x_defs));
 
-  pathToName = p: replaceStrings ["/"] ["_"] (toString p);
-
   # Add an `overrideGo` attribute to a function result that works
   # similar to `overrideAttrs`, but is used specifically for the
   # arguments passed to Go builders.
@@ -49,52 +49,92 @@ let
     overrideGo = new: makeOverridable f (orig // (new orig));
   };
 
+  buildStdlib = go: runCommand "go-stdlib-${go.version}"
+    {
+      nativeBuildInputs = [ go ];
+    } ''
+    HOME=$NIX_BUILD_TOP/home
+    mkdir $HOME
+
+    goroot="$(go env GOROOT)"
+    cp -R "$goroot/src" "$goroot/pkg" .
+
+    chmod -R +w .
+    GODEBUG=installgoroot=all GOROOT=$NIX_BUILD_TOP go install -v --trimpath std
+
+    mkdir $out
+    cp -r pkg/*_*/* $out
+
+    find $out -name '*.a' | while read -r ARCHIVE_FULL; do
+      ARCHIVE="''${ARCHIVE_FULL#"$out/"}"
+      PACKAGE="''${ARCHIVE%.a}"
+      echo "packagefile $PACKAGE=$ARCHIVE_FULL"
+    done > $out/importcfg
+  '';
+
+  importcfgCmd = { name, deps, out ? "importcfg" }: ''
+    echo "# nix buildGo ${name}" > "${out}"
+    cat "${goStdlib}/importcfg" >> "${out}"
+    ${lib.concatStringsSep "\n" (map (dep: ''
+      find "${dep}" -name '*.a' | while read -r pkgp; do
+        relpath="''${pkgp#"${dep}/"}"
+        pkgname="''${relpath%.a}"
+        echo "packagefile $pkgname=$pkgp"
+      done >> "${out}"
+    '') deps)}
+  '';
+
   # High-level build functions
 
   # Build a Go program out of the specified files and dependencies.
-  program = { name, srcs, deps ? [], x_defs ? {} }:
-  let uniqueDeps = allDeps (map (d: d.gopkg) deps);
-  in runCommand name {} ''
-    ${go}/bin/go tool compile -o ${name}.a -trimpath=$PWD -trimpath=${go} ${includeSources uniqueDeps} ${spaceOut srcs}
-    mkdir -p $out/bin
-    export GOROOT_FINAL=go
-    ${go}/bin/go tool link -o $out/bin/${name} -buildid nix ${xFlags x_defs} ${includeLibs uniqueDeps} ${name}.a
-  '';
+  program = { name, srcs, deps ? [ ], x_defs ? { } }:
+    let uniqueDeps = allDeps (map (d: d.gopkg) deps);
+    in runCommand name { } ''
+      ${importcfgCmd { inherit name; deps = uniqueDeps; }}
+      ${go}/bin/go tool compile -o ${name}.a -importcfg=importcfg -trimpath=$PWD -trimpath=${go} -p main ${includeSources uniqueDeps} ${spaceOut srcs}
+      mkdir -p $out/bin
+      export GOROOT_FINAL=go
+      ${go}/bin/go tool link -o $out/bin/${name} -importcfg=importcfg -buildid nix ${xFlags x_defs} ${includeLibs uniqueDeps} ${name}.a
+    '';
 
   # Build a Go library assembled out of the specified files.
   #
   # This outputs both the sources and compiled binary, as both are
   # needed when downstream packages depend on it.
-  package = { name, srcs, deps ? [], path ? name, sfiles ? [] }:
-  let
-    uniqueDeps = allDeps (map (d: d.gopkg) deps);
-
-    # The build steps below need to be executed conditionally for Go
-    # assembly if the analyser detected any *.s files.
-    #
-    # This is required for several popular packages (e.g. x/sys).
-    ifAsm = do: lib.optionalString (sfiles != []) do;
-    asmBuild = ifAsm ''
-      ${go}/bin/go tool asm -trimpath $PWD -I $PWD -I ${go}/share/go/pkg/include -D GOOS_linux -D GOARCH_amd64 -gensymabis -o ./symabis ${spaceOut sfiles}
-      ${go}/bin/go tool asm -trimpath $PWD -I $PWD -I ${go}/share/go/pkg/include -D GOOS_linux -D GOARCH_amd64 -o ./asm.o ${spaceOut sfiles}
-    '';
-    asmLink = ifAsm "-symabis ./symabis -asmhdr $out/go_asm.h";
-    asmPack = ifAsm ''
-      ${go}/bin/go tool pack r $out/${path}.a ./asm.o
-    '';
-
-    gopkg = (runCommand "golib-${name}" {} ''
-      mkdir -p $out/${path}
-      ${srcList path (map (s: "${s}") srcs)}
-      ${asmBuild}
-      ${go}/bin/go tool compile -pack ${asmLink} -o $out/${path}.a -trimpath=$PWD -trimpath=${go} -p ${path} ${includeSources uniqueDeps} ${spaceOut srcs}
-      ${asmPack}
-    '') // {
-      inherit gopkg;
-      goDeps = uniqueDeps;
-      goImportPath = path;
-    };
-  in gopkg;
+  package = { name, srcs, deps ? [ ], path ? name, sfiles ? [ ] }:
+    let
+      uniqueDeps = allDeps (map (d: d.gopkg) deps);
+
+      # The build steps below need to be executed conditionally for Go
+      # assembly if the analyser detected any *.s files.
+      #
+      # This is required for several popular packages (e.g. x/sys).
+      ifAsm = do: lib.optionalString (sfiles != [ ]) do;
+      asmBuild = ifAsm ''
+        ${go}/bin/go tool asm -p ${path} -trimpath $PWD -I $PWD -I ${go}/share/go/pkg/include -D GOOS_linux -D GOARCH_amd64 -gensymabis -o ./symabis ${spaceOut sfiles}
+        ${go}/bin/go tool asm -p ${path} -trimpath $PWD -I $PWD -I ${go}/share/go/pkg/include -D GOOS_linux -D GOARCH_amd64 -o ./asm.o ${spaceOut sfiles}
+      '';
+      asmLink = ifAsm "-symabis ./symabis -asmhdr $out/go_asm.h";
+      asmPack = ifAsm ''
+        ${go}/bin/go tool pack r $out/${path}.a ./asm.o
+      '';
+
+      gopkg = (runCommand "golib-${name}" { } ''
+        mkdir -p $out/${path}
+        ${srcList path (map (s: "${s}") srcs)}
+        ${asmBuild}
+        ${importcfgCmd { inherit name; deps = uniqueDeps; }}
+        ${go}/bin/go tool compile -pack ${asmLink} -o $out/${path}.a -importcfg=importcfg -trimpath=$PWD -trimpath=${go} -p ${path} ${includeSources uniqueDeps} ${spaceOut srcs}
+        ${asmPack}
+      '').overrideAttrs (_: {
+        passthru = {
+          inherit gopkg;
+          goDeps = uniqueDeps;
+          goImportPath = path;
+        };
+      });
+    in
+    gopkg;
 
   # Build a tree of Go libraries out of an external Go source
   # directory that follows the standard Go layout and was not built
@@ -104,32 +144,14 @@ let
   # named "gopkg", and an attribute named "gobin" for binaries.
   external = import ./external { inherit pkgs program package; };
 
-  # Import support libraries needed for protobuf & gRPC support
-  protoLibs = import ./proto.nix {
-    inherit external;
-  };
-
-  # Build a Go library out of the specified protobuf definition.
-  proto = { name, proto, path ? name, goPackage ? name, extraDeps ? [] }: (makeOverridable package) {
-    inherit name path;
-    deps = [ protoLibs.goProto.proto.gopkg ] ++ extraDeps;
-    srcs = lib.singleton (runCommand "goproto-${name}.pb.go" {} ''
-      cp ${proto} ${baseNameOf proto}
-      ${protobuf}/bin/protoc --plugin=${protoLibs.goProto.protoc-gen-go.gopkg}/bin/protoc-gen-go \
-        --go_out=plugins=grpc,import_path=${baseNameOf path}:. ${baseNameOf proto}
-      mv ./${goPackage}/*.pb.go $out
-    '');
-  };
-
-  # Build a Go library out of the specified gRPC definition.
-  grpc = args: proto (args // { extraDeps = [ protoLibs.goGrpc.gopkg ]; });
-
-in {
+in
+{
   # Only the high-level builder functions are exposed, but made
   # overrideable.
   program = makeOverridable program;
   package = makeOverridable package;
-  proto = makeOverridable proto;
-  grpc = makeOverridable grpc;
   external = makeOverridable external;
+
+  # re-expose the Go version used
+  inherit go;
 }
diff --git a/nix/buildGo/example/default.nix b/nix/buildGo/example/default.nix
index 99c0a7d79b..6756bf39e2 100644
--- a/nix/buildGo/example/default.nix
+++ b/nix/buildGo/example/default.nix
@@ -8,7 +8,7 @@
 # users a quick introduction to how to use buildGo.
 
 let
-  buildGo = import ../default.nix {};
+  buildGo = import ../default.nix { };
 
   # Example use of buildGo.package, which creates an importable Go
   # package from the specified source files.
@@ -19,17 +19,11 @@ let
     ];
   };
 
-  # Example use of buildGo.proto, which generates a Go library from a
-  # Protobuf definition file.
-  exampleProto = buildGo.proto {
-    name = "exampleproto";
-    proto = ./thing.proto;
-  };
-
   # Example use of buildGo.program, which builds an executable using
   # the specified name and dependencies (which in turn must have been
   # created via buildGo.package etc.)
-in buildGo.program {
+in
+buildGo.program {
   name = "example";
 
   srcs = [
@@ -38,7 +32,6 @@ in buildGo.program {
 
   deps = [
     examplePackage
-    exampleProto
   ];
 
   x_defs = {
diff --git a/nix/buildGo/example/thing.proto b/nix/buildGo/example/thing.proto
deleted file mode 100644
index 0f6d6575e0..0000000000
--- a/nix/buildGo/example/thing.proto
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2019 Google LLC.
-// SPDX-License-Identifier: Apache-2.0
-
-syntax = "proto3";
-package example;
-
-message Thing {
-  string id = 1;
-  string kind_of_thing = 2;
-}
diff --git a/nix/buildGo/external/default.nix b/nix/buildGo/external/default.nix
index 6540faf04c..42592c67e4 100644
--- a/nix/buildGo/external/default.nix
+++ b/nix/buildGo/external/default.nix
@@ -13,16 +13,17 @@ let
     readFile
     replaceStrings
     tail
+    unsafeDiscardStringContext
     throw;
 
   inherit (pkgs) lib runCommand go jq ripgrep;
 
-  pathToName = p: replaceStrings ["/"] ["_"] (toString p);
+  pathToName = p: replaceStrings [ "/" ] [ "_" ] (toString p);
 
   # Collect all non-vendored dependencies from the Go standard library
   # into a file that can be used to filter them out when processing
   # dependencies.
-  stdlibPackages = runCommand "stdlib-pkgs.json" {} ''
+  stdlibPackages = runCommand "stdlib-pkgs.json" { } ''
     export HOME=$PWD
     export GOPATH=/dev/null
     ${go}/bin/go list std | \
@@ -45,20 +46,28 @@ let
   };
 
   mkset = path: value:
-    if path == [] then { gopkg = value; }
+    if path == [ ] then { gopkg = value; }
     else { "${head path}" = mkset (tail path) value; };
 
   last = l: elemAt l ((length l) - 1);
 
   toPackage = self: src: path: depMap: entry:
     let
-      localDeps = map (d: lib.attrByPath (d ++ [ "gopkg" ]) (
-        throw "missing local dependency '${lib.concatStringsSep "." d}' in '${path}'"
-      ) self) entry.localDeps;
-
-      foreignDeps = map (d: lib.attrByPath [ d.path ] (
-        throw "missing foreign dependency '${d.path}' in '${path}, imported at ${d.position}'"
-      ) depMap) entry.foreignDeps;
+      localDeps = map
+        (d: lib.attrByPath (d ++ [ "gopkg" ])
+          (
+            throw "missing local dependency '${lib.concatStringsSep "." d}' in '${path}'"
+          )
+          self)
+        entry.localDeps;
+
+      foreignDeps = map
+        (d: lib.attrByPath [ d.path ]
+          (
+            throw "missing foreign dependency '${d.path}' in '${path}, imported at ${d.position}'"
+          )
+          depMap)
+        entry.foreignDeps;
 
       args = {
         srcs = map (f: src + ("/" + f)) entry.files;
@@ -74,22 +83,30 @@ let
       binArgs = args // {
         name = (last ((lib.splitString "/" path) ++ entry.locator));
       };
-    in if entry.isCommand then (program binArgs) else (package libArgs);
+    in
+    if entry.isCommand then (program binArgs) else (package libArgs);
 
-in { src, path, deps ? [] }: let
+in
+{ src, path, deps ? [ ] }:
+let
   # Build a map of dependencies (from their import paths to their
   # derivation) so that they can be conditionally imported only in
   # sub-packages that require them.
-  depMap = listToAttrs (map (d: {
-    name = d.goImportPath;
-    value = d;
-  }) (map (d: d.gopkg) deps));
+  depMap = listToAttrs (map
+    (d: {
+      name = d.goImportPath;
+      value = d;
+    })
+    (map (d: d.gopkg) deps));
 
   name = pathToName path;
-  analysisOutput = runCommand "${name}-structure.json" {} ''
+  analysisOutput = runCommand "${name}-structure.json" { } ''
     ${analyser}/bin/analyser -path ${path} -source ${src} > $out
   '';
-  analysis = fromJSON (readFile analysisOutput);
-in lib.fix(self: foldl' lib.recursiveUpdate {} (
+  # readFile adds the references of the read in file to the string context for
+  # Nix >= 2.6 which would break the attribute set construction in fromJSON
+  analysis = fromJSON (unsafeDiscardStringContext (readFile analysisOutput));
+in
+lib.fix (self: foldl' lib.recursiveUpdate { } (
   map (entry: mkset entry.locator (toPackage self src path depMap entry)) analysis
 ))
diff --git a/nix/buildGo/external/main.go b/nix/buildGo/external/main.go
index a77c43b371..4402a8eb86 100644
--- a/nix/buildGo/external/main.go
+++ b/nix/buildGo/external/main.go
@@ -10,7 +10,6 @@ import (
 	"flag"
 	"fmt"
 	"go/build"
-	"io/ioutil"
 	"log"
 	"os"
 	"path"
@@ -74,8 +73,8 @@ func findGoDirs(at string) ([]string, error) {
 	}
 
 	goDirs := []string{}
-	for k, _ := range dirSet {
-		goDirs = append(goDirs, k)
+	for goDir := range dirSet {
+		goDirs = append(goDirs, goDir)
 	}
 
 	return goDirs, nil
@@ -148,7 +147,7 @@ func analysePackage(root, source, importpath string, stdlib map[string]bool) (pk
 }
 
 func loadStdlibPkgs(from string) (pkgs map[string]bool, err error) {
-	f, err := ioutil.ReadFile(from)
+	f, err := os.ReadFile(from)
 	if err != nil {
 		return
 	}
diff --git a/nix/buildGo/proto.nix b/nix/buildGo/proto.nix
deleted file mode 100644
index 4bd3a57276..0000000000
--- a/nix/buildGo/proto.nix
+++ /dev/null
@@ -1,86 +0,0 @@
-# Copyright 2019 Google LLC.
-# SPDX-License-Identifier: Apache-2.0
-#
-# This file provides derivations for the dependencies of a gRPC
-# service in Go.
-
-{ external }:
-
-let
-  inherit (builtins) fetchGit map;
-in rec {
-  goProto = external {
-    path = "github.com/golang/protobuf";
-    src = fetchGit {
-      url = "https://github.com/golang/protobuf";
-      rev = "ed6926b37a637426117ccab59282c3839528a700";
-    };
-  };
-
-  xnet = external {
-    path = "golang.org/x/net";
-
-    src = fetchGit {
-      url = "https://go.googlesource.com/net";
-      rev = "ffdde105785063a81acd95bdf89ea53f6e0aac2d";
-    };
-
-    deps = [
-      xtext.secure.bidirule
-      xtext.unicode.bidi
-      xtext.unicode.norm
-    ];
-  };
-
-  xsys = external {
-    path = "golang.org/x/sys";
-    src = fetchGit {
-      url = "https://go.googlesource.com/sys";
-      rev = "bd437916bb0eb726b873ee8e9b2dcf212d32e2fd";
-    };
-  };
-
-  xtext = external {
-    path = "golang.org/x/text";
-    src = fetchGit {
-      url = "https://go.googlesource.com/text";
-      rev = "cbf43d21aaebfdfeb81d91a5f444d13a3046e686";
-    };
-  };
-
-  genproto = external {
-    path = "google.golang.org/genproto";
-    src = fetchGit {
-      url = "https://github.com/google/go-genproto";
-      # necessary because https://github.com/NixOS/nix/issues/1923
-      ref = "main";
-      rev = "83cc0476cb11ea0da33dacd4c6354ab192de6fe6";
-    };
-
-    deps = with goProto; [
-      proto
-      ptypes.any
-    ];
-  };
-
-  goGrpc = external {
-    path = "google.golang.org/grpc";
-    deps = ([
-      xnet.trace
-      xnet.http2
-      xsys.unix
-      xnet.http2.hpack
-      genproto.googleapis.rpc.status
-    ] ++ (with goProto; [
-      proto
-      ptypes
-      ptypes.duration
-      ptypes.timestamp
-    ]));
-
-    src = fetchGit {
-      url = "https://github.com/grpc/grpc-go";
-      rev = "d8e3da36ac481ef00e510ca119f6b68177713689";
-    };
-  };
-}
diff --git a/nix/buildLisp/README.md b/nix/buildLisp/README.md
index 214700750d..0d1e469834 100644
--- a/nix/buildLisp/README.md
+++ b/nix/buildLisp/README.md
@@ -220,7 +220,7 @@ buildLisp.library {
 
 Additionally a `brokenOn` parameter is accepted which takes a list of
 implementation names on which the derivation is not expected to work.
-This only influences `meta.targets` which is read by depot's CI to
+This only influences `meta.ci.targets` which is read by depot's CI to
 check which variants (see "Implementations") of the derivation to
 build, so it may not be useful outside of depot.
 
diff --git a/nix/buildLisp/default.nix b/nix/buildLisp/default.nix
index ab23b30210..0d68a2818b 100644
--- a/nix/buildLisp/default.nix
+++ b/nix/buildLisp/default.nix
@@ -4,11 +4,11 @@
 # buildLisp is designed to enforce conventions and do away with the
 # free-for-all of existing Lisp build systems.
 
-{ pkgs ? import <nixpkgs> {}, ... }:
+{ pkgs ? import <nixpkgs> { }, ... }:
 
 let
   inherit (builtins) map elemAt match filter;
-  inherit (pkgs) lib runCommandNoCC makeWrapper writeText writeShellScriptBin sbcl ecl-static ccl;
+  inherit (pkgs) lib runCommand makeWrapper writeText writeShellScriptBin sbcl ecl-static ccl;
   inherit (pkgs.stdenv) targetPlatform;
 
   #
@@ -70,11 +70,16 @@ let
   implFilter = impl: xs:
     let
       isFilterSet = x: builtins.isAttrs x && !(lib.isDerivation x);
-    in builtins.map (
-      x: if isFilterSet x then x.${impl.name} or x.default else x
-    ) (builtins.filter (
-      x: !(isFilterSet x) || x ? ${impl.name} || x ? default
-    ) xs);
+    in
+    builtins.map
+      (
+        x: if isFilterSet x then x.${impl.name} or x.default else x
+      )
+      (builtins.filter
+        (
+          x: !(isFilterSet x) || x ? ${impl.name} || x ? default
+        )
+        xs);
 
   # Generates lisp code which instructs the given lisp implementation to load
   # all the given dependencies.
@@ -103,17 +108,21 @@ let
   # 'allDeps' flattens the list of dependencies (and their
   # dependencies) into one ordered list of unique deps which
   # all use the given implementation.
-  allDeps = impl: deps: let
-    # The override _should_ propagate itself recursively, as every derivation
-    # would only expose its actually used dependencies. Use implementation
-    # attribute created by withExtras if present, override in all other cases
-    # (mainly bundled).
-    deps' = builtins.map (dep: dep."${impl.name}" or (dep.overrideLisp (_: {
-      implementation = impl;
-    }))) deps;
-  in (lib.toposort dependsOn (lib.unique (
-    lib.flatten (deps' ++ (map (d: d.lispDeps) deps'))
-  ))).result;
+  allDeps = impl: deps:
+    let
+      # The override _should_ propagate itself recursively, as every derivation
+      # would only expose its actually used dependencies. Use implementation
+      # attribute created by withExtras if present, override in all other cases
+      # (mainly bundled).
+      deps' = builtins.map
+        (dep: dep."${impl.name}" or (dep.overrideLisp (_: {
+          implementation = impl;
+        })))
+        deps;
+    in
+    (lib.toposort dependsOn (lib.unique (
+      lib.flatten (deps' ++ (map (d: d.lispDeps) deps'))
+    ))).result;
 
   # 'allNative' extracts all native dependencies of a dependency list
   # to ensure that library load paths are set correctly during all
@@ -138,42 +147,51 @@ let
   withExtras = f: args:
     let
       drv = (makeOverridable f) args;
-    in lib.fix (self:
-      drv.overrideLisp (old:
-        let
-          implementation = old.implementation or defaultImplementation;
-          brokenOn = old.brokenOn or [];
-          targets = lib.subtractLists (brokenOn ++ [ implementation.name ])
-            (builtins.attrNames impls);
-        in {
-          passthru = (old.passthru or {}) // {
-            repl = implementation.lispWith [ self ];
-
-            # meta is done via passthru to minimize rebuilds caused by overriding
-            meta = (old.passthru.meta or {}) // {
-              inherit targets;
-            };
-          } // builtins.listToAttrs (builtins.map (impl: {
-            inherit (impl) name;
-            value = self.overrideLisp (_: {
-              implementation = impl;
-            });
-          }) (builtins.attrValues impls));
-        }) // {
-          overrideLisp = new: withExtras f (args // new args);
-        });
+    in
+    lib.fix (self:
+      drv.overrideLisp
+        (old:
+          let
+            implementation = old.implementation or defaultImplementation;
+            brokenOn = old.brokenOn or [ ];
+            targets = lib.subtractLists (brokenOn ++ [ implementation.name ])
+              (builtins.attrNames impls);
+          in
+          {
+            passthru = (old.passthru or { }) // {
+              repl = implementation.lispWith [ self ];
+
+              # meta is done via passthru to minimize rebuilds caused by overriding
+              meta = (old.passthru.meta or { }) // {
+                ci = (old.passthru.meta.ci or { }) // {
+                  inherit targets;
+                };
+              };
+            } // builtins.listToAttrs (builtins.map
+              (impl: {
+                inherit (impl) name;
+                value = self.overrideLisp (_: {
+                  implementation = impl;
+                });
+              })
+              (builtins.attrValues impls));
+          }) // {
+        overrideLisp = new: withExtras f (args // new args);
+      });
 
   # 'testSuite' builds a Common Lisp test suite that loads all of srcs and deps,
   # and then executes expression to check its result
-  testSuite = { name, expression, srcs, deps ? [], native ? [], implementation }:
+  testSuite = { name, expression, srcs, deps ? [ ], native ? [ ], implementation }:
     let
       lispDeps = allDeps implementation (implFilter implementation deps);
       lispNativeDeps = allNative native lispDeps;
       filteredSrcs = implFilter implementation srcs;
-    in runCommandNoCC name {
-      LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
-      LANG = "C.UTF-8";
-    } ''
+    in
+    runCommand name
+      {
+        LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
+        LANG = "C.UTF-8";
+      } ''
       echo "Running test suite ${name}"
 
       ${implementation.runScript} ${
@@ -303,6 +321,11 @@ let
         (let* ((bindir (concatenate 'string (sb-posix:getenv "out") "/bin"))
                (outpath (make-pathname :name "${name}"
                                        :directory bindir)))
+
+          ;; Tell UIOP that argv[0] will refer to running image, not the lisp impl
+          (when (find-package :uiop)
+            (eval `(setq ,(find-symbol "*IMAGE-DUMPED-P*" :uiop) :executable)))
+
           (save-lisp-and-die outpath
                              :executable t
                              :toplevel
@@ -422,7 +445,7 @@ let
                           ;; to handle argument parsing and such properly. Since
                           ;; this needs to work even when we're not using UIOP,
                           ;; we need to do some compile-time acrobatics.
-                          ,(when (find-package 'uiop)
+                          ,(when (find-package :uiop)
                             `(setf ,(find-symbol "*IMAGE-DUMPED-P*" :uiop) :executable))
                           ;; Run the actual application…
                           (${main})
@@ -452,15 +475,16 @@ let
           } $@
         '';
 
-      bundled = name: runCommandNoCC "${name}-cllib" {
-        passthru = {
-          lispName = name;
-          lispNativeDeps = [];
-          lispDeps = [];
-          lispBinary = false;
-          repl = impls.ecl.lispWith [ (impls.ecl.bundled name) ];
-        };
-      } ''
+      bundled = name: runCommand "${name}-cllib"
+        {
+          passthru = {
+            lispName = name;
+            lispNativeDeps = [ ];
+            lispDeps = [ ];
+            lispBinary = false;
+            repl = impls.ecl.lispWith [ (impls.ecl.bundled name) ];
+          };
+        } ''
         mkdir -p "$out"
         ln -s "${ecl-static}/lib/ecl-${ecl-static.version}/${name}.${impls.ecl.faslExt}" -t "$out"
         ln -s "${ecl-static}/lib/ecl-${ecl-static.version}/lib${name}.a" "$out/${name}.a"
@@ -489,8 +513,8 @@ let
 
       # See https://ccl.clozure.com/docs/ccl.html#building-definitions
       faslExt =
-        /**/ if targetPlatform.isPowerPC && targetPlatform.is32bit then "pfsl"
-        else if targetPlatform.isPowerPC && targetPlatform.is64bit then "p64fsl"
+        if targetPlatform.isPower && targetPlatform.is32bit then "pfsl"
+        else if targetPlatform.isPower && targetPlatform.is64bit then "p64fsl"
         else if targetPlatform.isx86_64 && targetPlatform.isLinux then "lx64fsl"
         else if targetPlatform.isx86_32 && targetPlatform.isLinux then "lx32fsl"
         else if targetPlatform.isAarch32 && targetPlatform.isLinux then "lafsl"
@@ -543,6 +567,10 @@ let
                (bindir (concatenate 'string out "/bin/"))
                (executable (make-pathname :directory bindir :name "${name}")))
 
+          ;; Tell UIOP that argv[0] will refer to running image, not the lisp impl
+          (when (find-package :uiop)
+            (eval `(setf ,(find-symbol "*IMAGE-DUMPED-P*" :uiop) :executable)))
+
           (save-application executable
                             :purify t
                             :error-handler :quit
@@ -572,7 +600,7 @@ let
             lib.optionalString (deps != [])
               "--load ${writeText "load.lisp" (impls.ccl.genLoadLisp lispDeps)}"
           } "$@"
-      '';
+        '';
     };
   };
 
@@ -586,37 +614,42 @@ let
   library =
     { name
     , implementation ? defaultImplementation
-    , brokenOn ? [] # TODO(sterni): make this a warning
+    , brokenOn ? [ ] # TODO(sterni): make this a warning
     , srcs
-    , deps ? []
-    , native ? []
+    , deps ? [ ]
+    , native ? [ ]
     , tests ? null
-    , passthru ? {}
+    , passthru ? { }
     }:
     let
       filteredDeps = implFilter implementation deps;
       filteredSrcs = implFilter implementation srcs;
       lispNativeDeps = (allNative native filteredDeps);
       lispDeps = allDeps implementation filteredDeps;
-      testDrv = if ! isNull tests
-        then testSuite {
-          name = tests.name or "${name}-test";
-          srcs = filteredSrcs ++ (tests.srcs or []);
-          deps = filteredDeps ++ (tests.deps or []);
-          expression = tests.expression;
-          inherit implementation;
-        }
+      testDrv =
+        if ! isNull tests
+        then
+          testSuite
+            {
+              name = tests.name or "${name}-test";
+              srcs = filteredSrcs ++ (tests.srcs or [ ]);
+              deps = filteredDeps ++ (tests.deps or [ ]);
+              expression = tests.expression;
+              inherit implementation;
+            }
         else null;
-    in lib.fix (self: runCommandNoCC "${name}-cllib" {
-      LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
-      LANG = "C.UTF-8";
-      passthru = passthru // {
-        inherit lispNativeDeps lispDeps;
-        lispName = name;
-        lispBinary = false;
-        tests = testDrv;
-      };
-    } ''
+    in
+    lib.fix (self: runCommand "${name}-cllib"
+      {
+        LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
+        LANG = "C.UTF-8";
+        passthru = passthru // {
+          inherit lispNativeDeps lispDeps;
+          lispName = name;
+          lispBinary = false;
+          tests = testDrv;
+        };
+      } ''
       ${if ! isNull testDrv
         then "echo 'Test ${testDrv} succeeded'"
         else "echo 'No tests run'"}
@@ -637,13 +670,13 @@ let
   program =
     { name
     , implementation ? defaultImplementation
-    , brokenOn ? [] # TODO(sterni): make this a warning
+    , brokenOn ? [ ] # TODO(sterni): make this a warning
     , main ? "${name}:main"
     , srcs
-    , deps ? []
-    , native ? []
+    , deps ? [ ]
+    , native ? [ ]
     , tests ? null
-    , passthru ? {}
+    , passthru ? { }
     }:
     let
       filteredSrcs = implFilter implementation srcs;
@@ -656,45 +689,53 @@ let
         deps = lispDeps;
         srcs = filteredSrcs;
       };
-      testDrv = if ! isNull tests
-        then testSuite {
-          name = tests.name or "${name}-test";
-          srcs =
-            ( # testSuite does run implFilter as well
-              filteredSrcs ++ (tests.srcs or []));
-          deps = filteredDeps ++ (tests.deps or []);
-          expression = tests.expression;
-          inherit implementation;
-        }
+      testDrv =
+        if ! isNull tests
+        then
+          testSuite
+            {
+              name = tests.name or "${name}-test";
+              srcs =
+                (
+                  # testSuite does run implFilter as well
+                  filteredSrcs ++ (tests.srcs or [ ])
+                );
+              deps = filteredDeps ++ (tests.deps or [ ]);
+              expression = tests.expression;
+              inherit implementation;
+            }
         else null;
-    in lib.fix (self: runCommandNoCC "${name}" {
-      nativeBuildInputs = [ makeWrapper ];
-      LD_LIBRARY_PATH = libPath;
-      LANG = "C.UTF-8";
-      passthru = passthru // {
-        lispName = name;
-        lispDeps = [ selfLib ];
-        lispNativeDeps = native;
-        lispBinary = true;
-        tests = testDrv;
-      };
-    } (''
-      ${if ! isNull testDrv
-        then "echo 'Test ${testDrv} succeeded'"
-        else ""}
-      mkdir -p $out/bin
-
-      ${implementation.runScript} ${
-        implementation.genDumpLisp {
-          inherit name main;
-          deps = ([ selfLib ] ++ lispDeps);
-        }
+    in
+    lib.fix (self: runCommand "${name}"
+      {
+        nativeBuildInputs = [ makeWrapper ];
+        LD_LIBRARY_PATH = libPath;
+        LANG = "C.UTF-8";
+        passthru = passthru // {
+          lispName = name;
+          lispDeps = [ selfLib ];
+          lispNativeDeps = native;
+          lispBinary = true;
+          tests = testDrv;
+        };
       }
-    '' + lib.optionalString implementation.wrapProgram ''
-      wrapProgram $out/bin/${name} \
-        --prefix LD_LIBRARY_PATH : "${libPath}" \
-        --add-flags "\$NIX_BUILDLISP_LISP_ARGS --"
-    ''));
+      (''
+        ${if ! isNull testDrv
+          then "echo 'Test ${testDrv} succeeded'"
+          else ""}
+        mkdir -p $out/bin
+
+        ${implementation.runScript} ${
+          implementation.genDumpLisp {
+            inherit name main;
+            deps = ([ selfLib ] ++ lispDeps);
+          }
+        }
+      '' + lib.optionalString implementation.wrapProgram ''
+        wrapProgram $out/bin/${name} \
+          --prefix LD_LIBRARY_PATH : "${libPath}" \
+          --add-flags "\$NIX_BUILDLISP_LISP_ARGS --"
+      ''));
 
   # 'bundled' creates a "library" which makes a built-in package available,
   # such as any of SBCL's sb-* packages or ASDF. By default this is done
@@ -714,11 +755,13 @@ let
         }:
         implementation.bundled or (defaultBundled implementation) name;
 
-    in (makeOverridable bundled') {
+    in
+    (makeOverridable bundled') {
       inherit name;
     };
 
-in {
+in
+{
   library = withExtras library;
   program = withExtras program;
   inherit bundled;
diff --git a/nix/buildLisp/example/default.nix b/nix/buildLisp/example/default.nix
index 6a518e4964..6add2676f1 100644
--- a/nix/buildLisp/example/default.nix
+++ b/nix/buildLisp/example/default.nix
@@ -14,15 +14,16 @@ let
     ];
   };
 
-# Example Lisp program.
-#
-# This builds & writes an executable for a program using the library
-# above to disk.
-#
-# By default, buildLisp.program expects the entry point to be
-# `$name:main`. This can be overridden by configuring the `main`
-# attribute.
-in buildLisp.program {
+  # Example Lisp program.
+  #
+  # This builds & writes an executable for a program using the library
+  # above to disk.
+  #
+  # By default, buildLisp.program expects the entry point to be
+  # `$name:main`. This can be overridden by configuring the `main`
+  # attribute.
+in
+buildLisp.program {
   name = "example";
   deps = [ libExample ];
 
diff --git a/nix/buildLisp/tests/argv0.nix b/nix/buildLisp/tests/argv0.nix
new file mode 100644
index 0000000000..ca5f2b9741
--- /dev/null
+++ b/nix/buildLisp/tests/argv0.nix
@@ -0,0 +1,58 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  # Trivial test program that outputs argv[0] and exits
+  prog =
+    depot.nix.buildLisp.program {
+      name = "argv0-test";
+
+      srcs = [
+        (pkgs.writeText "argv0-test.lisp" ''
+          (defpackage :argv0-test (:use :common-lisp :uiop) (:export :main))
+          (in-package :argv0-test)
+
+          (defun main ()
+            (format t "~A~%" (uiop:argv0)))
+        '')
+      ];
+
+      deps = [
+        {
+          sbcl = depot.nix.buildLisp.bundled "uiop";
+          default = depot.nix.buildLisp.bundled "asdf";
+        }
+      ];
+    };
+
+  # Extract verify argv[0] output for given buildLisp program
+  checkImplementation = prog:
+    pkgs.runCommand "check-argv0" { } ''
+      set -eux
+
+      checkInvocation() {
+        invocation="$1"
+        test "$invocation" = "$("$invocation")"
+      }
+
+      checkInvocation "${prog}/bin/argv0-test"
+
+      cd ${prog}
+      checkInvocation "./bin/argv0-test"
+
+      cd bin
+      checkInvocation ./argv0-test
+
+      set +x
+
+      touch "$out"
+    '';
+
+  inherit (prog.meta.ci) targets;
+in
+
+(checkImplementation prog).overrideAttrs (_: {
+  # Wire up a subtarget all (active) non-default implementations
+  passthru = lib.genAttrs targets (name: checkImplementation prog.${name});
+
+  meta.ci = { inherit targets; };
+})
diff --git a/nix/buildManPages/OWNERS b/nix/buildManPages/OWNERS
index f16dd105d7..2e95807063 100644
--- a/nix/buildManPages/OWNERS
+++ b/nix/buildManPages/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - sterni
+sterni
diff --git a/nix/buildManPages/default.nix b/nix/buildManPages/default.nix
index fe6d145f6c..746ed25182 100644
--- a/nix/buildManPages/default.nix
+++ b/nix/buildManPages/default.nix
@@ -13,9 +13,9 @@ let
     ;
 
   bins = getBins mandoc [ "mandoc" ]
-      // getBins gzip   [ "gzip" ]
-      // getBins coreutils [ "mkdir" "ln" "cp" ]
-      ;
+    // getBins gzip [ "gzip" ]
+    // getBins coreutils [ "mkdir" "ln" "cp" ]
+  ;
 
   defaultGzip = true;
 
@@ -35,41 +35,68 @@ let
     }:
     { content
     , ...
-    }@page: let
+    }@page:
+    let
       source = builtins.toFile (basename false page) content;
-    in runExecline (basename gzip page) {} ([
-      (if requireLint then "if" else "foreground") [
-        bins.mandoc "-mdoc" "-T" "lint" source
+    in
+    runExecline (basename gzip page) { } ([
+      (if requireLint then "if" else "foreground")
+      [
+        bins.mandoc
+        "-mdoc"
+        "-T"
+        "lint"
+        source
       ]
-      "importas" "out" "out"
+      "importas"
+      "out"
+      "out"
     ] ++ (if gzip then [
-      "redirfd" "-w" "1" "$out"
-      bins.gzip "-c" source
+      "redirfd"
+      "-w"
+      "1"
+      "$out"
+      bins.gzip
+      "-c"
+      source
     ] else [
-      bins.cp "--reflink=auto" source "$out"
+      bins.cp
+      "--reflink=auto"
+      source
+      "$out"
     ]));
 
   buildManPages =
     name:
-    { derivationArgs ? {}
+    { derivationArgs ? { }
     , gzip ? defaultGzip
     , ...
     }@args:
     pages:
-    runExecline "${name}-man-pages" {
-      inherit derivationArgs;
-    } ([
-      "importas" "out" "out"
-    ] ++ lib.concatMap ({ name, section, content }@page: [
-      "if" [ bins.mkdir "-p" (manDir page) ]
-      "if" [
-        bins.ln "-s"
-        (buildManPage args page)
-        (target gzip page)
-      ]
-    ]) pages);
+    runExecline "${name}-man-pages"
+      {
+        inherit derivationArgs;
+      }
+      ([
+        "importas"
+        "out"
+        "out"
+      ] ++ lib.concatMap
+        ({ name, section, content }@page: [
+          "if"
+          [ bins.mkdir "-p" (manDir page) ]
+          "if"
+          [
+            bins.ln
+            "-s"
+            (buildManPage args page)
+            (target gzip page)
+          ]
+        ])
+        pages);
 
-in {
+in
+{
   __functor = _: buildManPages;
 
   single = buildManPage;
diff --git a/nix/buildkite/default.nix b/nix/buildkite/default.nix
index f8d69b2399..9abba9408a 100644
--- a/nix/buildkite/default.nix
+++ b/nix/buildkite/default.nix
@@ -6,16 +6,15 @@
 #
 # The structure of the file that is being created is documented here:
 #   https://buildkite.com/docs/pipelines/defining-steps
-{ pkgs, ... }:
+{ depot, pkgs, ... }:
 
 let
   inherit (builtins)
     attrValues
-    concatMap
+    concatLists
     concatStringsSep
-    filter
+    elem
     foldl'
-    getEnv
     hasAttr
     hashString
     isNull
@@ -23,84 +22,130 @@ let
     length
     listToAttrs
     mapAttrs
-    partition
-    pathExists
     toJSON
     unsafeDiscardStringContext;
 
-  inherit (pkgs) lib runCommandNoCC writeText;
-in rec {
-  # Creates a Nix expression that yields the target at the specified
-  # location in the repository.
-  #
-  # This makes a distinction between normal targets (which physically
-  # exist in the repository) and subtargets (which are "virtual"
-  # targets exposed by a physical one) to make it clear in the build
-  # output which is which.
-  mkBuildExpr = target:
+  inherit (pkgs) lib runCommand writeText;
+  inherit (depot.nix.readTree) mkLabel;
+
+  inherit (depot.nix) dependency-analyzer;
+in
+rec {
+  # Create a unique key for the buildkite pipeline based on the given derivation
+  # or drvPath. A consequence of using such keys is that every derivation may
+  # only be exposed as a single, unique step in the pipeline.
+  keyForDrv = drvOrPath:
+    let
+      drvPath =
+        if lib.isDerivation drvOrPath then drvOrPath.drvPath
+        else if lib.isString drvOrPath then drvOrPath
+        else builtins.throw "keyForDrv: expected string or derivation";
+
+      # Only use the drv hash to prevent escaping problems. Buildkite also has a
+      # limit of 100 characters on keys.
+    in
+    "drv-" + (builtins.substring 0 32
+      (builtins.baseNameOf (unsafeDiscardStringContext drvPath))
+    );
+
+  # Given an arbitrary attribute path generate a Nix expression which obtains
+  # this from the root of depot (assumed to be ./.). Attributes may be any
+  # Nix strings suitable as attribute names, not just Nix literal-safe strings.
+  mkBuildExpr = attrPath:
     let
       descend = expr: attr: "builtins.getAttr \"${attr}\" (${expr})";
-      targetExpr = foldl' descend "import ./. {}" target.__readTree;
-      subtargetExpr = descend targetExpr target.__subtarget;
-    in if target ? __subtarget then subtargetExpr else targetExpr;
-
-  # Create a pipeline label from the target's tree location.
-  mkLabel = target:
-    let label = concatStringsSep "/" target.__readTree;
-    in if target ? __subtarget
-      then "${label}:${target.__subtarget}"
-      else label;
+    in
+    foldl' descend "import ./. {}" attrPath;
 
   # Determine whether to skip a target if it has not diverged from the
   # HEAD branch.
-  shouldSkip = parentTargetMap: label: drvPath:
+  shouldSkip = { parentTargetMap ? { }, label, drvPath }:
     if (hasAttr label parentTargetMap) && parentTargetMap."${label}".drvPath == drvPath
     then "Target has not changed."
     else false;
 
-  # Create build command for a derivation target.
-  mkBuildCommand = target: drvPath: concatStringsSep " " [
+  # Create build command for an attribute path pointing to a derivation.
+  mkBuildCommand = { attrPath, drvPath, outLink ? "result" }: concatStringsSep " " [
+    # If the nix build fails, the Nix command's exit status should be used.
+    "set -o pipefail;"
+
     # First try to realise the drvPath of the target so we don't evaluate twice.
     # Nix has no concept of depending on a derivation file without depending on
     # at least one of its `outPath`s, so we need to discard the string context
     # if we don't want to build everything during pipeline construction.
-    "nix-store --realise '${drvPath}' --add-root result --indirect"
+    #
+    # To make this more uniform with how nix-build(1) works, we call realpath(1)
+    # on nix-store(1)'s output since it has the habit of printing the path of the
+    # out link, not the store path.
+    "(nix-store --realise '${drvPath}' --add-root '${outLink}' --indirect | xargs -r realpath)"
 
     # Since we don't gcroot the derivation files, they may be deleted by the
     # garbage collector. In that case we can reevaluate and build the attribute
     # using nix-build.
-    "|| (test ! -f '${drvPath}' && nix-build -E '${mkBuildExpr target}' --show-trace)"
+    "|| (test ! -f '${drvPath}' && nix-build -E '${mkBuildExpr attrPath}' --show-trace --out-link '${outLink}')"
   ];
 
+  # Attribute path of a target relative to the depot root. Needs to take into
+  # account whether the target is a physical target (which corresponds to a path
+  # in the filesystem) or the subtarget of a physical target.
+  targetAttrPath = target:
+    target.__readTree
+    ++ lib.optionals (target ? __subtarget) [ target.__subtarget ];
+
+  # Given a derivation (identified by drvPath) that is part of the list of
+  # targets passed to mkPipeline, determine all derivations that it depends on
+  # and are also part of the pipeline. Finally, return the keys of the steps
+  # that build them. This is used to populate `depends_on` in `mkStep`.
+  #
+  # See //nix/dependency-analyzer for documentation on the structure of `targetDepMap`.
+  getTargetPipelineDeps = targetDepMap: drvPath:
+    # Sanity check: We should only call this function on targets explicitly
+    # passed to mkPipeline. Thus it should have been passed as a β€œknown” drv to
+    # dependency-analyzer.
+    assert targetDepMap.${drvPath}.known;
+    builtins.map keyForDrv targetDepMap.${drvPath}.knownDeps;
+
   # Create a pipeline step from a single target.
-  mkStep = headBranch: parentTargetMap: target:
-  let
-    label = mkLabel target;
-    drvPath = unsafeDiscardStringContext target.drvPath;
-    shouldSkip' = shouldSkip parentTargetMap;
-  in {
-    label = ":nix: " + label;
-    key = hashString "sha1" label;
-    skip = shouldSkip' label drvPath;
-    command = mkBuildCommand target drvPath;
-    env.READTREE_TARGET = label;
-
-    # Add a dependency on the initial static pipeline step which
-    # always runs. This allows build steps uploaded in batches to
-    # start running before all batches have been uploaded.
-    depends_on = ":init:";
-  };
+  mkStep = { headBranch, parentTargetMap, targetDepMap, target, cancelOnBuildFailing }:
+    let
+      label = mkLabel target;
+      drvPath = unsafeDiscardStringContext target.drvPath;
+    in
+    {
+      label = ":nix: " + label;
+      key = keyForDrv target;
+      skip = shouldSkip { inherit label drvPath parentTargetMap; };
+      command = mkBuildCommand {
+        attrPath = targetAttrPath target;
+        inherit drvPath;
+      };
+      env.READTREE_TARGET = label;
+      cancel_on_build_failing = cancelOnBuildFailing;
+
+      # Add a dependency on the initial static pipeline step which
+      # always runs. This allows build steps uploaded in batches to
+      # start running before all batches have been uploaded.
+      depends_on = [ ":init:" ]
+      ++ getTargetPipelineDeps targetDepMap drvPath
+      ++ lib.optionals (target ? meta.ci.buildkiteExtraDeps) target.meta.ci.buildkiteExtraDeps;
+    } // lib.optionalAttrs (target ? meta.timeout) {
+      timeout_in_minutes = target.meta.timeout / 60;
+      # Additional arguments to set on the step.
+      # Keep in mind these *overwrite* existing step args, not extend. Use with caution.
+    } // lib.optionalAttrs (target ? meta.ci.buildkiteExtraStepArgs) target.meta.ci.buildkiteExtraStepArgs;
 
   # Helper function to inelegantly divide a list into chunks of at
   # most n elements.
   #
   # This works by assigning each element a chunk ID based on its
   # index, and then grouping all elements by their chunk ID.
-  chunksOf = n: list: let
-    chunkId = idx: toString (idx / n + 1);
-    assigned = lib.imap1 (idx: value: { inherit value ; chunk = chunkId idx; }) list;
-    unchunk = mapAttrs (_: elements: map (e: e.value) elements);
-  in unchunk (lib.groupBy (e: e.chunk) assigned);
+  chunksOf = n: list:
+    let
+      chunkId = idx: toString (idx / n + 1);
+      assigned = lib.imap1 (idx: value: { inherit value; chunk = chunkId idx; }) list;
+      unchunk = mapAttrs (_: elements: map (e: e.value) elements);
+    in
+    unchunk (lib.groupBy (e: e.chunk) assigned);
 
   # Define a build pipeline chunk as a JSON file, using the pipeline
   # format documented on
@@ -112,112 +157,159 @@ in rec {
     });
   };
 
-  # Split the pipeline into chunks of at most 256 steps at once, which
+  # Split the pipeline into chunks of at most 192 steps at once, which
   # are uploaded sequentially. This is because of a limitation in the
   # Buildkite backend which struggles to process more than a specific
   # number of chunks at once.
   pipelineChunks = name: steps:
-    attrValues (mapAttrs (makePipelineChunk name) (chunksOf 256 steps));
+    attrValues (mapAttrs (makePipelineChunk name) (chunksOf 192 steps));
 
   # Create a pipeline structure for the given targets.
-  mkPipeline = {
-    # HEAD branch of the repository on which release steps, GC
-    # anchoring and other "mainline only" steps should run.
-    headBranch,
-
-    # List of derivations as read by readTree (in most cases just the
-    # output of readTree.gather) that should be built in Buildkite.
-    #
-    # These are scheduled as the first build steps and run as fast as
-    # possible, in order, without any concurrency restrictions.
-    drvTargets,
-
-    # Derivation map of a parent commit. Only targets which no longer
-    # correspond to the content of this map will be built. Passing an
-    # empty map will always build all targets.
-    parentTargetMap ? {},
-
-    # A list of plain Buildkite step structures to run alongside the
-    # build for all drvTargets, but before proceeding with any
-    # post-build actions such as status reporting.
-    #
-    # Can be used for things like code formatting checks.
-    additionalSteps ? [],
-
-    # A list of plain Buildkite step structures to run after all
-    # previous steps succeeded.
-    #
-    # Can be used for status reporting steps and the like.
-    postBuildSteps ? []
-  }: let
-    # Convert a target into all of its build and post-build steps,
-    # treated separately as they need to be in different chunks.
-    targetToSteps = target: let
-      step = mkStep headBranch parentTargetMap target;
-
-      # Split build/post-build steps
-      splitExtraSteps = partition ({ postStep, ... }: postStep)
-       (attrValues (mapAttrs (name: value: {
-         inherit name value;
-         postStep = (value ? prompt) || (value.postBuild or false);
-        }) (target.meta.ci.extraSteps or {})));
-
-      mkExtraStep' = { name, value, ... }: mkExtraStep step name value;
-      extraBuildSteps = map mkExtraStep' splitExtraSteps.wrong; # 'wrong' -> no prompt
-      extraPostSteps = map mkExtraStep' splitExtraSteps.right; # 'right' -> has prompt
-    in {
-      buildSteps = [ step ] ++ extraBuildSteps;
-      postSteps = extraPostSteps;
-    };
-
-    # Combine all target steps into separate build and post-build step lists.
-    steps = foldl' (acc: t: {
-      buildSteps = acc.buildSteps ++ t.buildSteps;
-      postSteps = acc.postSteps ++ t.postSteps;
-    }) { buildSteps = []; postSteps = []; } (map targetToSteps drvTargets);
-
-    buildSteps =
-      # Add build steps for each derivation target and their extra
-      # steps.
-      steps.buildSteps
-
-      # Add additional steps (if set).
-      ++ additionalSteps;
-
-    postSteps =
-      # Add post-build steps for each derivation target.
-      steps.postSteps
-
-      # Add any globally defined post-build steps.
-      ++ postBuildSteps;
-
-    buildChunks = pipelineChunks "build" buildSteps;
-    postBuildChunks = pipelineChunks "post" postSteps;
-    chunks = buildChunks ++ postBuildChunks;
-  in runCommandNoCC "buildkite-pipeline" {} ''
-    mkdir $out
-    echo "Generated ${toString (length chunks)} pipeline chunks"
-    ${
-      lib.concatMapStringsSep "\n"
-        (chunk: "cp ${chunk.path} $out/${chunk.filename}") chunks
-    }
-  '';
+  mkPipeline =
+    {
+      # HEAD branch of the repository on which release steps, GC
+      # anchoring and other "mainline only" steps should run.
+      headBranch
+    , # List of derivations as read by readTree (in most cases just the
+      # output of readTree.gather) that should be built in Buildkite.
+      #
+      # These are scheduled as the first build steps and run as fast as
+      # possible, in order, without any concurrency restrictions.
+      drvTargets
+    , # Derivation map of a parent commit. Only targets which no longer
+      # correspond to the content of this map will be built. Passing an
+      # empty map will always build all targets.
+      parentTargetMap ? { }
+    , # A list of plain Buildkite step structures to run alongside the
+      # build for all drvTargets, but before proceeding with any
+      # post-build actions such as status reporting.
+      #
+      # Can be used for things like code formatting checks.
+      additionalSteps ? [ ]
+    , # A list of plain Buildkite step structures to run after all
+      # previous steps succeeded.
+      #
+      # Can be used for status reporting steps and the like.
+      postBuildSteps ? [ ]
+      # The list of phases known by the current Buildkite
+      # pipeline. Dynamic pipeline chunks for each phase are uploaded
+      # to Buildkite on execution of static part of the
+      # pipeline. Phases selection is hard-coded in the static
+      # pipeline.
+      #
+      # Pipeline generation will fail when an extra step with
+      # unregistered phase is added.
+      #
+      # Common scenarios for different phase:
+      #   - "build" - main phase for building all Nix targets
+      #   - "release" - pushing artifacts to external repositories
+      #   - "deploy" - updating external deployment configurations
+    , phases ? [ "build" "release" ]
+      # Build phases that are active for this invocation (i.e. their
+      # steps should be generated).
+      #
+      # This can be used to disable outputting parts of a pipeline if,
+      # for example, build and release phases are created in separate
+      # eval contexts.
+      #
+      # TODO(tazjin): Fail/warn if unknown phase is requested.
+    , activePhases ? phases
+      # Setting this attribute to true cancels dynamic pipeline steps
+      # as soon as the build is marked as failing.
+      #
+      # To enable this feature one should enable "Fail Fast" setting
+      # at Buildkite pipeline or on organization level.
+    , cancelOnBuildFailing ? false
+    }:
+    let
+      # List of phases to include.
+      enabledPhases = lib.intersectLists activePhases phases;
+
+      # Is the 'build' phase included? This phase is treated specially
+      # because it always contains the plain Nix builds, and some
+      # logic/optimisation depends on knowing whether is executing.
+      buildEnabled = elem "build" enabledPhases;
+
+      # Dependency relations between the `drvTargets`. See also //nix/dependency-analyzer.
+      targetDepMap = dependency-analyzer (dependency-analyzer.drvsToPaths drvTargets);
+
+      # Convert a target into all of its steps, separated by build
+      # phase (as phases end up in different chunks).
+      targetToSteps = target:
+        let
+          mkStepArgs = {
+            inherit headBranch parentTargetMap targetDepMap target cancelOnBuildFailing;
+          };
+          step = mkStep mkStepArgs;
+
+          # Same step, but with an override function applied. This is
+          # used in mkExtraStep if the extra step needs to modify the
+          # parent derivation somehow.
+          #
+          # Note that this will never affect the label.
+          overridable = f: mkStep (mkStepArgs // { target = (f target); });
+
+          # Split extra steps by phase.
+          splitExtraSteps = lib.groupBy ({ phase, ... }: phase)
+            (attrValues (mapAttrs (normaliseExtraStep phases overridable)
+              (target.meta.ci.extraSteps or { })));
+
+          extraSteps = mapAttrs
+            (_: steps:
+              map (mkExtraStep (targetAttrPath target) buildEnabled) steps)
+            splitExtraSteps;
+        in
+        if !buildEnabled then extraSteps
+        else extraSteps // {
+          build = [ step ] ++ (extraSteps.build or [ ]);
+        };
+
+      # Combine all target steps into step lists per phase.
+      #
+      # TODO(tazjin): Refactor when configurable phases show up.
+      globalSteps = {
+        build = additionalSteps;
+        release = postBuildSteps;
+      };
+
+      phasesWithSteps = lib.zipAttrsWithNames enabledPhases (_: concatLists)
+        ((map targetToSteps drvTargets) ++ [ globalSteps ]);
+
+      # Generate pipeline chunks for each phase.
+      chunks = foldl'
+        (acc: phase:
+          let phaseSteps = phasesWithSteps.${phase} or [ ]; in
+          if phaseSteps == [ ]
+          then acc
+          else acc ++ (pipelineChunks phase phaseSteps))
+        [ ]
+        enabledPhases;
+
+    in
+    runCommand "buildkite-pipeline" { } ''
+      mkdir $out
+      echo "Generated ${toString (length chunks)} pipeline chunks"
+      ${
+        lib.concatMapStringsSep "\n"
+          (chunk: "cp ${chunk.path} $out/${chunk.filename}") chunks
+      }
+    '';
 
   # Create a drvmap structure for the given targets, containing the
   # mapping of all target paths to their derivations. The mapping can
   # be persisted for future use.
-  mkDrvmap = drvTargets: writeText "drvmap.json" (toJSON (listToAttrs (map (target: {
-    name = mkLabel target;
-    value = {
-      drvPath = unsafeDiscardStringContext target.drvPath;
-
-      # Include the attrPath in the output to reconstruct the drv
-      # without parsing the human-readable label.
-      attrPath = target.__readTree ++ lib.optionals (target ? __subtarget) [
-        target.__subtarget
-      ];
-    };
-  }) drvTargets)));
+  mkDrvmap = drvTargets: writeText "drvmap.json" (toJSON (listToAttrs (map
+    (target: {
+      name = mkLabel target;
+      value = {
+        drvPath = unsafeDiscardStringContext target.drvPath;
+
+        # Include the attrPath in the output to reconstruct the drv
+        # without parsing the human-readable label.
+        attrPath = targetAttrPath target;
+      };
+    })
+    drvTargets)));
 
   # Implementation of extra step logic.
   #
@@ -238,15 +330,15 @@ in rec {
   #     confirmation. These steps always run after the main build is
   #     done and have no influence on CI status.
   #
-  #   postBuild (optional): If set to true, this step will run after
-  #     all primary build steps (that is, after status has been reported
-  #     back to CI).
-  #
   #   needsOutput (optional): If set to true, the parent derivation
   #     will be built in the working directory before running the
   #     command. Output will be available as 'result'.
   #     TODO: Figure out multiple-output derivations.
   #
+  #   parentOverride (optional): A function (drv -> drv) to override
+  #     the parent's target definition when preparing its output. Only
+  #     used in extra steps that use needsOutput.
+  #
   #   branches (optional): Git references (branches, tags ... ) on
   #     which this step should be allowed to run. List of strings.
   #
@@ -258,14 +350,14 @@ in rec {
   # Create a gated step in a step group, independent from any other
   # steps.
   mkGatedStep = { step, label, parent, prompt }: {
-    inherit (step) branches depends_on;
+    inherit (step) depends_on;
     group = label;
     skip = parent.skip or false;
 
     steps = [
       {
-        inherit (step) branches;
         inherit prompt;
+        branches = step.branches or [ ];
         block = ":radio_button: Run ${label}? (from ${parent.env.READTREE_TARGET})";
       }
 
@@ -276,36 +368,124 @@ in rec {
     ];
   };
 
+  # Validate and normalise extra step configuration before actually
+  # generating build steps, in order to use user-provided metadata
+  # during the pipeline generation.
+  normaliseExtraStep = phases: overridableParent: key:
+    { command
+    , label ? key
+    , needsOutput ? false
+    , parentOverride ? (x: x)
+    , branches ? null
+    , alwaysRun ? false
+    , prompt ? false
+    , softFail ? false
+    , phase ? "build"
+    , skip ? false
+    , agents ? null
+    }:
+    let
+      parent = overridableParent parentOverride;
+      parentLabel = parent.env.READTREE_TARGET;
+
+      validPhase = lib.throwIfNot (elem phase phases) ''
+        In step '${label}' (from ${parentLabel}):
+
+        Phase '${phase}' is not valid.
+
+        Known phases: ${concatStringsSep ", " phases}
+      ''
+        phase;
+    in
+    {
+      inherit
+        alwaysRun
+        branches
+        command
+        key
+        label
+        needsOutput
+        parent
+        parentLabel
+        softFail
+        skip
+        agents;
+
+      phase = validPhase;
+
+      prompt = lib.throwIf (prompt != false && phase == "build") ''
+        In step '${label}' (from ${parentLabel}):
+
+        The 'prompt' feature can not be used by steps in the "build"
+        phase, because CI builds should not be gated on manual human
+        approvals.
+      ''
+        prompt;
+    };
+
   # Create the Buildkite configuration for an extra step, optionally
   # wrapping it in a gate group.
-  mkExtraStep = parent: key: {
-    command,
-    label ? key,
-    prompt ? false,
-    needsOutput ? false,
-    branches ? null,
-    alwaysRun ? false,
-    postBuild ? false
-  }@cfg: let
-    parentLabel = parent.env.READTREE_TARGET;
-
-    step = {
-      label = ":gear: ${label} (from ${parentLabel})";
-      skip = if alwaysRun then false else parent.skip or false;
-      depends_on = lib.optional (!alwaysRun && !needsOutput) parent.key;
-      branches = if branches != null then lib.concatStringsSep " " branches else null;
-
-      command = pkgs.writeShellScript "${key}-script" ''
-        set -ueo pipefail
-        ${lib.optionalString needsOutput "echo '~~~ Preparing build output of ${parentLabel}'"}
-        ${lib.optionalString needsOutput parent.command}
-        echo '+++ Running extra step command'
-        exec ${command}
-      '';
-    };
-  in if (isString prompt)
-    then mkGatedStep {
-      inherit step label parent prompt;
-    }
+  mkExtraStep = parentAttrPath: buildEnabled: cfg:
+    let
+      # ATTN: needs to match an entry in .gitignore so that the tree won't get dirty
+      commandScriptLink = "nix-buildkite-extra-step-command-script";
+
+      step = {
+        key = "extra-step-" + hashString "sha1" "${cfg.label}-${cfg.parentLabel}";
+        label = ":gear: ${cfg.label} (from ${cfg.parentLabel})";
+        skip =
+          let
+            # When parent doesn't have skip attribute set, default to false
+            parentSkip = cfg.parent.skip or false;
+            # Extra step skip parameter can be string explaining the
+            # skip reason.
+            extraStepSkip = if builtins.isString cfg.skip then true else cfg.skip;
+            # Don't run if extra step is explicitly set to skip. If
+            # parameter is not set or equal to false, follow parent behavior.
+            skip' = if extraStepSkip then cfg.skip else parentSkip;
+          in
+          if cfg.alwaysRun then false else skip';
+
+        depends_on = lib.optional
+          (buildEnabled && !cfg.alwaysRun && !cfg.needsOutput)
+          cfg.parent.key;
+
+        command = pkgs.writeShellScript "${cfg.key}-script" ''
+          set -ueo pipefail
+          ${lib.optionalString cfg.needsOutput
+            "echo '~~~ Preparing build output of ${cfg.parentLabel}'"
+          }
+          ${lib.optionalString cfg.needsOutput cfg.parent.command}
+          echo '--- Building extra step script'
+          command_script="$(${
+            # Using command substitution in this way assumes the script drv only has one output
+            assert builtins.length cfg.command.outputs == 1;
+            mkBuildCommand {
+              # script is exposed at <parent>.meta.ci.extraSteps.<key>.command
+              attrPath =
+                parentAttrPath
+                ++ [ "meta" "ci" "extraSteps" cfg.key "command" ];
+              drvPath = unsafeDiscardStringContext cfg.command.drvPath;
+              # make sure it doesn't conflict with result (from needsOutput)
+              outLink = commandScriptLink;
+            }
+          })"
+          echo '+++ Running extra step script'
+          exec "$command_script"
+        '';
+
+        soft_fail = cfg.softFail;
+      } // (lib.optionalAttrs (cfg.agents != null) { inherit (cfg) agents; })
+      // (lib.optionalAttrs (cfg.branches != null) {
+        branches = lib.concatStringsSep " " cfg.branches;
+      });
+    in
+    if (isString cfg.prompt)
+    then
+      mkGatedStep
+        {
+          inherit step;
+          inherit (cfg) label parent prompt;
+        }
     else step;
 }
diff --git a/nix/buildkite/fetch-parent-targets.sh b/nix/buildkite/fetch-parent-targets.sh
index ca65646abd..08c2d1f3ab 100755
--- a/nix/buildkite/fetch-parent-targets.sh
+++ b/nix/buildkite/fetch-parent-targets.sh
@@ -2,42 +2,54 @@
 set -ueo pipefail
 
 # Each Buildkite build stores the derivation target map as a pipeline
-# artifact. This script determines the most appropriate commit (the
-# fork point of the current chain from HEAD) and fetches the artifact.
+# artifact. To reduce the amount of work done by CI, each CI build is
+# diffed against the latest such derivation map found for the
+# repository.
 #
-# New builds can be based on HEAD before the pipeline for the last
-# commit has finished, in which case it is possible that the fork
-# point has no derivation map. To account for this, up to 3 commits
-# prior to HEAD are also queried to find a map.
+# Note that this does not take into account when the currently
+# processing CL was forked off from the canonical branch, meaning that
+# things like nixpkgs updates in between will cause mass rebuilds in
+# any case.
 #
 # If no map is found, the failure mode is not critical: We simply
 # build all targets.
 
-: ${DRVMAP_PATH:=pipeline/drvmap.json}
-
-git fetch -v origin "${BUILDKITE_PIPELINE_DEFAULT_BRANCH}"
+readonly REPO_ROOT=$(git rev-parse --show-toplevel)
 
-FIRST=$(git merge-base FETCH_HEAD "${BUILDKITE_COMMIT}")
-SECOND=$(git rev-parse "$FIRST~1")
-THIRD=$(git rev-parse "$FIRST~2")
+: ${DRVMAP_PATH:=pipeline/drvmap.json}
+: ${BUILDKITE_TOKEN_PATH:=~/buildkite-token}
 
-function most_relevant_builds {
+# Runs a fairly complex Buildkite GraphQL query that attempts to fetch all
+# pipeline-gen steps from the default branch, as long as one appears within the
+# last 50 builds or so. The query restricts build states to running or passed
+# builds, which means that it *should* be unlikely that nothing is found.
+#
+# There is no way to filter this more loosely (e.g. by saying "any recent build
+# matching these conditions").
+#
+# The returned data structure is complex, and disassembled by a JQ script that
+# first filters out all builds with no matching jobs (e.g. builds that are still
+# in progress), and then filters those down to builds with artifacts, and then
+# to drvmap artifacts specifically.
+#
+# If a recent drvmap was found, this returns its download URL. Otherwise, it
+# returns the string "null".
+function latest_drvmap_url {
     set -u
     curl 'https://graphql.buildkite.com/v1' \
          --silent \
-         -H "Authorization: Bearer $(cat /run/agenix/buildkite-graphql-token)" \
-         -d "{\"query\": \"query { pipeline(slug: \\\"$BUILDKITE_ORGANIZATION_SLUG/$BUILDKITE_PIPELINE_SLUG\\\") { builds(commit: [\\\"$FIRST\\\",\\\"$SECOND\\\",\\\"$THIRD\\\"]) { edges { node { uuid }}}}}\"}" | \
-         jq -r '.data.pipeline.builds.edges[] | .node.uuid'
+         -H "Authorization: Bearer $(cat ${BUILDKITE_TOKEN_PATH})" \
+         -H "Content-Type: application/json" \
+         -d "{\"query\": \"{ pipeline(slug: \\\"$BUILDKITE_ORGANIZATION_SLUG/$BUILDKITE_PIPELINE_SLUG\\\") { builds(first: 50, branch: [\\\"%default\\\"], state: [RUNNING, PASSED]) { edges { node { jobs(passed: true, first: 1, type: [COMMAND], step: {key: [\\\"pipeline-gen\\\"]}) { edges { node { ... on JobTypeCommand { url artifacts { edges { node { downloadURL path }}}}}}}}}}}}\"}" | tee out.json | \
+        jq -r '[.data.pipeline.builds.edges[] | select((.node.jobs.edges | length) > 0) | .node.jobs.edges[] | .node.artifacts[][] | select(.node.path == "pipeline/drvmap.json")][0].node.downloadURL'
 }
 
-mkdir -p tmp
-for build in $(most_relevant_builds); do
-    echo "Checking artifacts for build $build"
-    buildkite-agent artifact download --build "${build}" "${DRVMAP_PATH}" 'tmp/' || true
+readonly DOWNLOAD_URL=$(latest_drvmap_url)
 
-    if [[ -f "tmp/${DRVMAP_PATH}" ]]; then
-        echo "Fetched target map from build ${build}"
-        mv "tmp/${DRVMAP_PATH}" tmp/parent-target-map.json
-        break
-    fi
-done
+if [[ ${DOWNLOAD_URL} != "null" ]]; then
+    mkdir -p tmp
+    curl -o tmp/parent-target-map.json ${DOWNLOAD_URL} && echo "downloaded parent derivation map" \
+            || echo "failed to download derivation map!"
+else
+    echo "no derivation map found!"
+fi
diff --git a/nix/dependency-analyzer/default.nix b/nix/dependency-analyzer/default.nix
new file mode 100644
index 0000000000..2ec8d7b5b9
--- /dev/null
+++ b/nix/dependency-analyzer/default.nix
@@ -0,0 +1,263 @@
+{ lib, depot, pkgs, ... }:
+
+let
+  inherit (builtins) unsafeDiscardStringContext appendContext;
+
+  #
+  # Utilities
+  #
+
+  # Determine all paths a derivation depends on, i.e. input derivations and
+  # files imported into the Nix store.
+  #
+  # Implementation for Nix < 2.6 is quite hacky at the moment.
+  #
+  # Type: str -> [str]
+  #
+  # TODO(sterni): clean this up and expose it
+  directDrvDeps =
+    let
+      getDeps =
+        if lib.versionAtLeast builtins.nixVersion "2.6"
+        then
+        # Since https://github.com/NixOS/nix/pull/1643, Nix apparently Β»preserves
+        # string contextΒ« through a readFile invocation. This has the side effect
+        # that it becomes possible to query the actual references a store path has.
+        # Not a 100% sure this is intended, but _very_ convenient for us here.
+          drvPath:
+          builtins.attrNames (builtins.getContext (builtins.readFile drvPath))
+        else
+        # For Nix < 2.6 we have to rely on HACK, namely grepping for quoted
+        # store path references in the file. In the future this should be
+        # replaced by a proper derivation parser.
+          drvPath: builtins.concatLists (
+            builtins.filter builtins.isList (
+              builtins.split
+                "\"(${lib.escapeRegex builtins.storeDir}/[[:alnum:]+._?=-]+.drv)\""
+                (builtins.readFile drvPath)
+            )
+          );
+    in
+    drvPath:
+    # if the passed path is not a derivation we can't necessarily get its
+    # dependencies, since it may not be representable as a Nix string due to
+    # NUL bytes, e.g. compressed patch files imported into the Nix store.
+    if builtins.match "^.+\\.drv$" drvPath == null
+    then [ ]
+    else getDeps drvPath;
+
+  # Maps a list of derivation to the list of corresponding `drvPath`s.
+  #
+  # Type: [drv] -> [str]
+  drvsToPaths = drvs:
+    builtins.map (drv: builtins.unsafeDiscardOutputDependency drv.drvPath) drvs;
+
+  #
+  # Calculate map of direct derivation dependencies
+  #
+
+  # Create the dependency map entry for a given `drvPath` which mainly includes
+  # a list of other `drvPath`s it depends on. Additionally we store whether the
+  # derivation is `known`, i.e. part of the initial list of derivations we start
+  # generating the map from
+  #
+  # Type: bool -> string -> set
+  drvEntry = known: drvPath:
+    let
+      # key may not refer to a store path, …
+      key = unsafeDiscardStringContext drvPath;
+      # but we must read from the .drv file.
+      path = builtins.unsafeDiscardOutputDependency drvPath;
+    in
+    {
+      inherit key;
+      # trick so we can call listToAttrs directly on the result of genericClosure
+      name = key;
+      value = {
+        deps = directDrvDeps path;
+        inherit known;
+      };
+    };
+
+  # Create an attribute set that maps every derivation in the combined
+  # dependency closure of the list of input derivation paths to every of their
+  # direct dependencies. Additionally every entry will have set their `known`
+  # attribute to `true` if it is in the list of input derivation paths.
+  #
+  # Type: [str] -> set
+  plainDrvDepMap = drvPaths:
+    builtins.listToAttrs (
+      builtins.genericClosure {
+        startSet = builtins.map (drvEntry true) drvPaths;
+        operator = { value, ... }: builtins.map (drvEntry false) value.deps;
+      }
+    );
+
+  #
+  # Calculate closest known dependencies in the dependency map
+  #
+
+  inherit (depot.nix.stateMonad)
+    after
+    bind
+    for_
+    get
+    getAttr
+    run
+    setAttr
+    pure
+    ;
+
+  # This is an action in stateMonad which expects the (initial) state to have
+  # been produced by `plainDrvDepMap`. Given a `drvPath`, it calculates a
+  # `knownDeps` list which holds the `drvPath`s of the closest derivation marked
+  # as `known` along every edge. This list is inserted into the dependency map
+  # for `drvPath` and every other derivation in its dependecy closure (unless
+  # the information was already present). This means that the known dependency
+  # information for a derivation never has to be recalculated, as long as they
+  # are part of the same stateful computation.
+  #
+  # The upshot is that after calling `insertKnownDeps drvPath`,
+  # `fmap (builtins.getAttr "knownDeps") (getAttr drvPath)` will always succeed.
+  #
+  # Type: str -> stateMonad drvDepMap null
+  insertKnownDeps = drvPathWithContext:
+    let
+      # We no longer need to read from the store, so context is irrelevant, but
+      # we need to check for attr names which requires the absence of context.
+      drvPath = unsafeDiscardStringContext drvPathWithContext;
+    in
+    bind get (initDepMap:
+      # Get the dependency map's state before we've done anything to obtain the
+      # entry we'll be manipulating later as well as its dependencies.
+      let
+        entryPoint = initDepMap.${drvPath};
+
+        # We don't need to recurse if our direct dependencies either have their
+        # knownDeps list already populated or are known dependencies themselves.
+        depsPrecalculated =
+          builtins.partition
+            (dep:
+              initDepMap.${dep}.known
+              || initDepMap.${dep} ? knownDeps
+            )
+            entryPoint.deps;
+
+        # If a direct dependency is known, it goes right to our known dependency
+        # list. If it is unknown, we can copy its knownDeps list into our own.
+        initiallyKnownDeps =
+          builtins.concatLists (
+            builtins.map
+              (dep:
+                if initDepMap.${dep}.known
+                then [ dep ]
+                else initDepMap.${dep}.knownDeps
+              )
+              depsPrecalculated.right
+          );
+      in
+
+      # If the information was already calculated before, we can exit right away
+      if entryPoint ? knownDeps
+      then pure null
+      else
+        after
+          # For all unknown direct dependencies which don't have a `knownDeps`
+          # list, we call ourselves recursively to populate it. Since this is
+          # done sequentially in the state monad, we avoid recalculating the
+          # list for the same derivation multiple times.
+          (for_
+            depsPrecalculated.wrong
+            insertKnownDeps)
+          # After this we can obtain the updated dependency map which will have
+          # a `knownDeps` list for all our direct dependencies and update the
+          # entry for the input `drvPath`.
+          (bind
+            get
+            (populatedDepMap:
+              (setAttr drvPath (entryPoint // {
+                knownDeps =
+                  lib.unique (
+                    initiallyKnownDeps
+                      ++ builtins.concatLists (
+                      builtins.map
+                        (dep: populatedDepMap.${dep}.knownDeps)
+                        depsPrecalculated.wrong
+                    )
+                  );
+              }))))
+    );
+
+  # This function puts it all together and is exposed via `__functor`.
+  #
+  # For a list of `drvPath`s, calculate an attribute set which maps every
+  # `drvPath` to a set of the following form:
+  #
+  #     {
+  #       known = true /* if it is in the list of input derivation paths */;
+  #       deps = [
+  #         /* list of derivation paths it depends on directly */
+  #       ];
+  #       knownDeps = [
+  #         /* list of the closest derivation paths marked as known this
+  #            derivation depends on.
+  #         */
+  #       ];
+  #     }
+  knownDrvDepMap = knownDrvPaths:
+    run
+      (plainDrvDepMap knownDrvPaths)
+      (after
+        (for_
+          knownDrvPaths
+          insertKnownDeps)
+        get);
+
+  #
+  # Other things based on knownDrvDepMap
+  #
+
+  # Create a SVG visualizing `knownDrvDepMap`. Nodes are identified by derivation
+  # name, so multiple entries can be collapsed if they have the same name.
+  #
+  # Type: [drv] -> drv
+  knownDependencyGraph = name: drvs:
+    let
+      justName = drvPath:
+        builtins.substring
+          (builtins.stringLength builtins.storeDir + 1 + 32 + 1)
+          (builtins.stringLength drvPath)
+          (unsafeDiscardStringContext drvPath);
+
+      gv = pkgs.writeText "${name}-dependency-analysis.gv" ''
+        digraph depot {
+        ${
+          (lib.concatStringsSep "\n"
+          (lib.mapAttrsToList (name: value:
+            if !value.known then ""
+            else lib.concatMapStringsSep "\n"
+              (knownDep: "  \"${justName name}\" -> \"${justName knownDep}\"")
+              value.knownDeps
+          )
+          (depot.nix.dependency-analyzer (
+            drvsToPaths drvs
+          ))))
+        }
+        }
+      '';
+    in
+
+    pkgs.runCommand "${name}-dependency-analysis.svg"
+      {
+        nativeBuildInputs = [
+          pkgs.buildPackages.graphviz
+        ];
+      }
+      "dot -Tsvg < ${gv} > $out";
+in
+
+{
+  __functor = _: knownDrvDepMap;
+
+  inherit knownDependencyGraph plainDrvDepMap drvsToPaths;
+}
diff --git a/nix/dependency-analyzer/examples/ci-targets.nix b/nix/dependency-analyzer/examples/ci-targets.nix
new file mode 100644
index 0000000000..597abd4109
--- /dev/null
+++ b/nix/dependency-analyzer/examples/ci-targets.nix
@@ -0,0 +1,12 @@
+{ depot, lib, ... }:
+
+(
+  depot.nix.dependency-analyzer.knownDependencyGraph
+    "depot"
+    depot.ci.targets
+).overrideAttrs (old: {
+  # Causes an infinite recursion via ci.targets otherwise
+  meta = lib.recursiveUpdate (old.meta or { }) {
+    ci.skip = true;
+  };
+})
diff --git a/nix/dependency-analyzer/examples/lisp.nix b/nix/dependency-analyzer/examples/lisp.nix
new file mode 100644
index 0000000000..775eb9ab57
--- /dev/null
+++ b/nix/dependency-analyzer/examples/lisp.nix
@@ -0,0 +1,5 @@
+{ depot, lib, ... }:
+
+depot.nix.dependency-analyzer.knownDependencyGraph "3p-lisp" (
+  builtins.filter lib.isDerivation (builtins.attrValues depot.third_party.lisp)
+)
diff --git a/nix/dependency-analyzer/tests/default.nix b/nix/dependency-analyzer/tests/default.nix
new file mode 100644
index 0000000000..79ac127e92
--- /dev/null
+++ b/nix/dependency-analyzer/tests/default.nix
@@ -0,0 +1,36 @@
+{ depot, lib, ... }:
+
+let
+  inherit (depot.nix.runTestsuite)
+    runTestsuite
+    assertEq
+    it
+    ;
+
+  inherit (depot.nix.dependency-analyzer)
+    plainDrvDepMap
+    drvsToPaths
+    ;
+
+  knownDrvs = drvsToPaths (
+    builtins.filter lib.isDerivation (builtins.attrValues depot.third_party.lisp)
+  );
+  exampleMap = plainDrvDepMap knownDrvs;
+
+  # These will be needed to index into the attribute set which can't have context
+  # in the attribute names.
+  knownDrvsNoContext = builtins.map builtins.unsafeDiscardStringContext knownDrvs;
+in
+
+runTestsuite "dependency-analyzer" [
+  (it "checks plainDrvDepMap properties" [
+    (assertEq "all known drvs are marked known"
+      (builtins.all (drv: exampleMap.${drv}.known) knownDrvsNoContext)
+      true)
+    (assertEq "no unknown drv is marked known"
+      (builtins.all (entry: !entry.known) (
+        builtins.attrValues (builtins.removeAttrs exampleMap knownDrvsNoContext)
+      ))
+      true)
+  ])
+]
diff --git a/nix/drvSeqL/default.nix b/nix/drvSeqL/default.nix
index 3339289b3b..6437e1a043 100644
--- a/nix/drvSeqL/default.nix
+++ b/nix/drvSeqL/default.nix
@@ -17,9 +17,10 @@ let
   drvSeqL = defun [ (list drv) drv drv ]
     (drvDeps: drvOut:
       let
-        drvOutOutputs = drvOut.outputs or ["out"];
+        drvOutOutputs = drvOut.outputs or [ "out" ];
       in
-        pkgs.runCommandLocal drvOut.name {
+      pkgs.runCommandLocal drvOut.name
+        {
           # we inherit all attributes in order to replicate
           # the original derivation as much as possible
           outputs = drvOutOutputs;
@@ -29,15 +30,18 @@ let
         }
         # the outputs of the original derivation are replicated
         # by creating a symlink to the old output path
-        (lib.concatMapStrings (output: ''
-          target=${lib.escapeShellArg drvOut.${output}}
-          # if the target is already a symlink, follow it until it’s not;
-          # this is done to prevent too many dereferences
-          target=$(readlink -e "$target")
-          # link to the output
-          ln -s "$target" "${"$"}${output}"
-        '') drvOutOutputs));
+        (lib.concatMapStrings
+          (output: ''
+            target=${lib.escapeShellArg drvOut.${output}}
+            # if the target is already a symlink, follow it until it’s not;
+            # this is done to prevent too many dereferences
+            target=$(readlink -e "$target")
+            # link to the output
+            ln -s "$target" "${"$"}${output}"
+          '')
+          drvOutOutputs));
 
-in {
+in
+{
   __functor = _: drvSeqL;
 }
diff --git a/nix/emptyDerivation/OWNERS b/nix/emptyDerivation/OWNERS
index a742d0d22b..a640227914 100644
--- a/nix/emptyDerivation/OWNERS
+++ b/nix/emptyDerivation/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - Profpatsch
+Profpatsch
diff --git a/nix/emptyDerivation/default.nix b/nix/emptyDerivation/default.nix
index 4165d4fd9a..f808aa228d 100644
--- a/nix/emptyDerivation/default.nix
+++ b/nix/emptyDerivation/default.nix
@@ -1,10 +1,11 @@
-{ depot, pkgs, ... }:
+{ depot, pkgs, localSystem, ... }:
 
 let
   emptyDerivation = import ./emptyDerivation.nix {
     inherit pkgs;
     inherit (pkgs) stdenv;
     inherit (depot.nix) getBins;
+    system = localSystem;
   };
 
   tests = import ./tests.nix {
@@ -14,7 +15,8 @@ let
     inherit (depot.nix.runTestsuite) runTestsuite it assertEq;
   };
 
-in {
+in
+{
   __functor = _: emptyDerivation;
   inherit tests;
 }
diff --git a/nix/emptyDerivation/emptyDerivation.nix b/nix/emptyDerivation/emptyDerivation.nix
index 5e84abe2d5..d7de7ccfbc 100644
--- a/nix/emptyDerivation/emptyDerivation.nix
+++ b/nix/emptyDerivation/emptyDerivation.nix
@@ -1,4 +1,4 @@
-{ stdenv, pkgs, getBins }:
+{ stdenv, system, pkgs, getBins }:
 
 # The empty derivation. All it does is touch $out.
 # Basically the unit value for derivations.
@@ -11,22 +11,24 @@
 
 let
   bins = getBins pkgs.s6-portable-utils [ "s6-touch" ]
-      // getBins pkgs.execline [ "importas" "exec" ];
+    // getBins pkgs.execline [ "importas" "exec" ];
 
   emptiness = {
     name = "empty-derivation";
-
-    # TODO(Profpatsch): can we get system from tvl?
-    inherit (stdenv) system;
+    inherit system;
 
     builder = bins.exec;
     args = [
-      bins.importas "out" "out"
-      bins.s6-touch "$out"
+      bins.importas
+      "out"
+      "out"
+      bins.s6-touch
+      "$out"
     ];
   };
 
-in (derivation emptiness) // {
+in
+(derivation emptiness) // {
   # This allows us to call the empty derivation
   # like a function and override fields/add new fields.
   __functor = _: overrides:
diff --git a/nix/emptyDerivation/tests.nix b/nix/emptyDerivation/tests.nix
index 053603b027..a738428824 100644
--- a/nix/emptyDerivation/tests.nix
+++ b/nix/emptyDerivation/tests.nix
@@ -10,10 +10,17 @@ let
   ];
 
   fooOut = emptyDerivation {
-    builder = writeExecline "foo-builder" {} [
-      "importas" "out" "out"
-      "redirfd" "-w" "1" "$out"
-      bins.s6-echo "-n" "foo"
+    builder = writeExecline "foo-builder" { } [
+      "importas"
+      "out"
+      "out"
+      "redirfd"
+      "-w"
+      "1"
+      "$out"
+      bins.s6-echo
+      "-n"
+      "foo"
     ];
   };
 
@@ -26,7 +33,8 @@ let
       "bar")
   ];
 
-in runTestsuite "emptyDerivation" [
+in
+runTestsuite "emptyDerivation" [
   empty
   overrideBuilder
 ]
diff --git a/nix/escapeExecline/default.nix b/nix/escapeExecline/default.nix
index deef5c2c4e..d2c39dd398 100644
--- a/nix/escapeExecline/default.nix
+++ b/nix/escapeExecline/default.nix
@@ -16,14 +16,17 @@ let
   #   escapeExecline [ "if" [ "somecommand" ] "true" ]
   #   == ''"if" { "somecommand" } "true"''
   escapeExecline = execlineList: lib.concatStringsSep " "
-    (let
-      go = arg:
-        if      builtins.isString arg then [(escapeExeclineArg arg)]
-        else if builtins.isPath arg then [(escapeExeclineArg "${arg}")]
-        else if lib.isDerivation arg then [(escapeExeclineArg arg)]
-        else if builtins.isList arg then [ "{" ] ++ builtins.concatMap go arg ++ [ "}" ]
-        else abort "escapeExecline can only hande nested lists of strings, was ${lib.generators.toPretty {} arg}";
-     in builtins.concatMap go execlineList);
+    (
+      let
+        go = arg:
+          if builtins.isString arg then [ (escapeExeclineArg arg) ]
+          else if builtins.isPath arg then [ (escapeExeclineArg "${arg}") ]
+          else if lib.isDerivation arg then [ (escapeExeclineArg arg) ]
+          else if builtins.isList arg then [ "{" ] ++ builtins.concatMap go arg ++ [ "}" ]
+          else abort "escapeExecline can only hande nested lists of strings, was ${lib.generators.toPretty {} arg}";
+      in
+      builtins.concatMap go execlineList
+    );
 
 in
 escapeExecline
diff --git a/nix/getBins/default.nix b/nix/getBins/default.nix
index 5ba7584ed8..e354b176c8 100644
--- a/nix/getBins/default.nix
+++ b/nix/getBins/default.nix
@@ -26,14 +26,16 @@
 
 let
   getBins = drv: xs:
-    let f = x:
-      # TODO(Profpatsch): typecheck
-      let x' = if builtins.isString x then { use = x; as = x; } else x;
-      in {
-        name = x'.as;
-        value = "${lib.getBin drv}/bin/${x'.use}";
-      };
-    in builtins.listToAttrs (builtins.map f xs);
+    let
+      f = x:
+        # TODO(Profpatsch): typecheck
+        let x' = if builtins.isString x then { use = x; as = x; } else x;
+        in {
+          name = x'.as;
+          value = "${lib.getBin drv}/bin/${x'.use}";
+        };
+    in
+    builtins.listToAttrs (builtins.map f xs);
 
 
   tests = import ./tests.nix {
@@ -42,7 +44,8 @@ let
     inherit (depot.nix.runTestsuite) assertEq it runTestsuite;
   };
 
-in {
+in
+{
   __functor = _: getBins;
   inherit tests;
 }
diff --git a/nix/getBins/tests.nix b/nix/getBins/tests.nix
index ff81deb5f1..e0f5ab4263 100644
--- a/nix/getBins/tests.nix
+++ b/nix/getBins/tests.nix
@@ -5,11 +5,11 @@ let
   drv2 = writeScriptBin "goodbye" "tschau";
 
   bins = getBins drv [
-            "hello"
-            { use = "hello"; as = "also-hello"; }
-          ]
-      // getBins drv2 [ "goodbye" ]
-      ;
+    "hello"
+    { use = "hello"; as = "also-hello"; }
+  ]
+  // getBins drv2 [ "goodbye" ]
+  ;
 
   simple = it "path is equal to the executable name" [
     (assertEq "path"
@@ -33,8 +33,8 @@ let
   ];
 
 in
-  runTestsuite "getBins" [
-    simple
-    useAs
-    secondDrv
-  ]
+runTestsuite "getBins" [
+  simple
+  useAs
+  secondDrv
+]
diff --git a/nix/lazy-deps/default.nix b/nix/lazy-deps/default.nix
new file mode 100644
index 0000000000..fbdb30b38e
--- /dev/null
+++ b/nix/lazy-deps/default.nix
@@ -0,0 +1,91 @@
+# Helper function to synthesize a directory of "lazy-built" binaries
+# that can be added to $PATH inside of a repository.
+#
+# Using this, a Nix shell environment in some repository can contain
+# several slow-to-build commands without blowing up evaluation and
+# build time whenever the shell is loaded.
+#
+# Note that the generated script is deliberately impure to speed up
+# evaluation, and expects both `git` and `nix-build` to exist in the
+# user's $PATH. If required, this can be done in the shell
+# configuration invoking this function.
+{ pkgs, lib, ... }:
+
+let
+  inherit (builtins) attrNames attrValues mapAttrs;
+  inherit (lib) fix concatStringsSep;
+
+  # Create the case statement for a command invocations, optionally
+  # overriding the `TARGET_TOOL` variable.
+  invoke = name: { attr, cmd ? null }: ''
+    ${name})
+      attr="${attr}"
+      ${if cmd != null then "TARGET_TOOL=\"${cmd}\"\n;;" else ";;"}
+  '';
+
+  # Create command to symlink to the dispatch script for each tool.
+  link = name: "ln -s $target $out/bin/${name}";
+
+  invocations = tools: concatStringsSep "\n" (attrValues (mapAttrs invoke tools));
+in
+fix (self:
+
+# Attribute set of tools that should be lazily-added to the $PATH.
+#
+# The name of each attribute is used as the command name (on $PATH).
+# It must contain the keys 'attr' (containing the Nix attribute path
+# to the tool's derivation from the top-level), and may optionally
+# contain the key 'cmd' to override the name of the binary inside the
+# derivation.
+tools:
+
+pkgs.runCommandNoCC "lazy-dispatch"
+{
+  passthru.overrideDeps = newTools: self (tools // newTools);
+  passthru.tools = tools;
+
+  text = ''
+    #!${pkgs.runtimeShell}
+    set -ue
+
+    if ! type git>/dev/null || ! type nix-build>/dev/null; then
+      echo "The 'git' and 'nix-build' commands must be available." >&2
+      exit 127
+    fi
+
+    readonly REPO_ROOT=$(git rev-parse --show-toplevel)
+    TARGET_TOOL=$(basename "$0")
+
+    case "''${TARGET_TOOL}" in
+    ${invocations tools}
+    *)
+      echo "''${TARGET_TOOL} is currently not installed in this repository." >&2
+      exit 127
+      ;;
+    esac
+
+    result=$(nix-build --no-out-link --attr "''${attr}" "''${REPO_ROOT}")
+    PATH="''${result}/bin:$PATH"
+    exec "''${TARGET_TOOL}" "''${@}"
+  '';
+
+  # Access this to get a compatible nix-shell
+  passthru.devShell = pkgs.mkShellNoCC {
+    name = "${self.name}-shell";
+    packages = [ self ];
+  };
+}
+  ''
+    # Write the dispatch code
+    target=$out/bin/__dispatch
+    mkdir -p "$(dirname "$target")"
+    echo "$text" > $target
+    chmod +x $target
+
+    # Add symlinks from all the tools to the dispatch
+    ${concatStringsSep "\n" (map link (attrNames tools))}
+
+    # Check that it's working-ish
+    ${pkgs.stdenv.shellDryRun} $target
+  ''
+)
diff --git a/nix/mergePatch/default.nix b/nix/mergePatch/default.nix
index 0f80b93d4c..d56106925a 100644
--- a/nix/mergePatch/default.nix
+++ b/nix/mergePatch/default.nix
@@ -8,31 +8,31 @@
   For example, given the following original document:
 
   {
-    a = "b";
-    c = {
+  a = "b";
+  c = {
       d = "e";
       f = "g";
-    }
+  }
   }
 
   Changing the value of `a` and removing `f` can be achieved by merging the patch
 
   {
-    a = "z";
-    c.f = null;
+  a = "z";
+  c.f = null;
   }
 
   which results in
 
   {
-    a = "z";
-    c = {
+  a = "z";
+  c = {
       d = "e";
-    };
+  };
   }
 
   Pseudo-code:
-    define MergePatch(Target, Patch):
+  define MergePatch(Target, Patch):
       if Patch is an Object:
         if Target is not an Object:
           Target = {} # Ignore the contents and set it to an empty Object
@@ -55,19 +55,19 @@ let
   mergePatch = target: patch:
     if lib.isAttrs patch
     then
-      let target' = if lib.isAttrs target then target else {};
+      let target' = if lib.isAttrs target then target else { };
       in foldlAttrs
-          (acc: patchEl:
-            if patchEl.value == null
-            then removeAttrs acc [ patchEl.name ]
-            else acc // {
-              ${patchEl.name} =
-                mergePatch
-                  (acc.${patchEl.name} or "unnused")
-                  patchEl.value;
-            })
-          target'
-          patch
+        (acc: patchEl:
+          if patchEl.value == null
+          then removeAttrs acc [ patchEl.name ]
+          else acc // {
+            ${patchEl.name} =
+              mergePatch
+                (acc.${patchEl.name} or "unnused")
+                patchEl.value;
+          })
+        target'
+        patch
     else patch;
 
   inherit (depot.nix.runTestsuite)
@@ -93,46 +93,49 @@ let
       };
       emptyPatch = it "the empty patch returns the original target" [
         (assertEq "id"
-          (mergePatch testTarget {})
+          (mergePatch testTarget { })
           testTarget)
       ];
       nonAttrs = it "one side is a non-attrset value" [
         (assertEq "target is a value means the value is replaced by the patch"
           (mergePatch 42 testPatch)
-          (mergePatch {} testPatch))
+          (mergePatch { } testPatch))
         (assertEq "patch is a value means it replaces target alltogether"
           (mergePatch testTarget 42)
           42)
       ];
       rfcExamples = it "the examples from the RFC" [
         (assertEq "a subset is deleted and overwritten"
-          (mergePatch testTarget testPatch) {
+          (mergePatch testTarget testPatch)
+          {
             a = "z";
             c = {
               d = "e";
             };
           })
         (assertEq "a more complicated example from the example section"
-          (mergePatch {
-            title = "Goodbye!";
+          (mergePatch
+            {
+              title = "Goodbye!";
               author = {
                 givenName = "John";
                 familyName = "Doe";
               };
-            tags = [ "example" "sample" ];
-            content = "This will be unchanged";
-          } {
-            title = "Hello!";
-            phoneNumber = "+01-123-456-7890";
-            author.familyName = null;
-            tags = [ "example" ];
-          })
+              tags = [ "example" "sample" ];
+              content = "This will be unchanged";
+            }
+            {
+              title = "Hello!";
+              phoneNumber = "+01-123-456-7890";
+              author.familyName = null;
+              tags = [ "example" ];
+            })
           {
             title = "Hello!";
             phoneNumber = "+01-123-456-7890";
-              author = {
-                givenName = "John";
-              };
+            author = {
+              givenName = "John";
+            };
             tags = [ "example" ];
             content = "This will be unchanged";
           })
@@ -144,42 +147,45 @@ let
             (assertEq "test number ${toString index}"
               (mergePatch target patch)
               res);
-          in it "the test suite from the RFC" [
-              (r 1  {"a" = "b";}       {"a" = "c";}       {"a" = "c";})
-              (r 2  {"a" = "b";}       {"b" = "c";}       {"a" = "b"; "b" = "c";})
-              (r 3  {"a" = "b";}       {"a" = null;}      {})
-              (r 4  {"a" = "b"; "b" = "c";}
-                    {"a" = null;}
-                    {"b" = "c";})
-              (r 5  {"a" = ["b"];}     {"a" = "c";}       {"a" = "c";})
-              (r 6  {"a" = "c";}       {"a" = ["b"];}     {"a" = ["b"];})
-              (r 7  {"a" = {"b" = "c";}; }
-                    {"a" = {"b" = "d"; "c" = null;};}
-                    {"a" = {"b" = "d";};})
-              (r 8  {"a" = [{"b" = "c";}];}
-                    {"a" = [1];}
-                    {"a" = [1];})
-              (r 9  ["a" "b"]          ["c" "d"]       ["c" "d"])
-              (r 10 {"a" = "b";}       ["c"]           ["c"])
-              (r 11 {"a" = "foo";}     null            null)
-              (r 12 {"a" = "foo";}     "bar"           "bar")
-              (r 13 {"e" = null;}      {"a" = 1;}      {"e" = null; "a" = 1;})
-              (r 14 [1 2]
-                    {"a" = "b"; "c" = null;}
-                    {"a" = "b";})
-              (r 15 {}
-                {"a" = {"bb" = {"ccc" = null;};};}
-                {"a" = {"bb" = {};};})
-            ];
-
-    in runTestsuite "mergePatch" [
+        in
+        it "the test suite from the RFC" [
+          (r 1 { "a" = "b"; } { "a" = "c"; } { "a" = "c"; })
+          (r 2 { "a" = "b"; } { "b" = "c"; } { "a" = "b"; "b" = "c"; })
+          (r 3 { "a" = "b"; } { "a" = null; } { })
+          (r 4 { "a" = "b"; "b" = "c"; }
+            { "a" = null; }
+            { "b" = "c"; })
+          (r 5 { "a" = [ "b" ]; } { "a" = "c"; } { "a" = "c"; })
+          (r 6 { "a" = "c"; } { "a" = [ "b" ]; } { "a" = [ "b" ]; })
+          (r 7 { "a" = { "b" = "c"; }; }
+            { "a" = { "b" = "d"; "c" = null; }; }
+            { "a" = { "b" = "d"; }; })
+          (r 8 { "a" = [{ "b" = "c"; }]; }
+            { "a" = [ 1 ]; }
+            { "a" = [ 1 ]; })
+          (r 9 [ "a" "b" ] [ "c" "d" ] [ "c" "d" ])
+          (r 10 { "a" = "b"; } [ "c" ] [ "c" ])
+          (r 11 { "a" = "foo"; } null null)
+          (r 12 { "a" = "foo"; } "bar" "bar")
+          (r 13 { "e" = null; } { "a" = 1; } { "e" = null; "a" = 1; })
+          (r 14 [ 1 2 ]
+            { "a" = "b"; "c" = null; }
+            { "a" = "b"; })
+          (r 15 { }
+            { "a" = { "bb" = { "ccc" = null; }; }; }
+            { "a" = { "bb" = { }; }; })
+        ];
+
+    in
+    runTestsuite "mergePatch" [
       emptyPatch
       nonAttrs
       rfcExamples
       rfcTests
     ];
 
-in {
+in
+{
   __functor = _: mergePatch;
 
   inherit tests;
diff --git a/nix/netstring/attrsToKeyValList.nix b/nix/netstring/attrsToKeyValList.nix
index 2805d0fbce..c854b56955 100644
--- a/nix/netstring/attrsToKeyValList.nix
+++ b/nix/netstring/attrsToKeyValList.nix
@@ -28,6 +28,6 @@ attrs:
 lib.concatStrings
   (lib.mapAttrsToList
     (k: v: depot.nix.netstring.fromString
-       ( depot.nix.netstring.fromString k
-       + depot.nix.netstring.fromString v))
+      (depot.nix.netstring.fromString k
+        + depot.nix.netstring.fromString v))
     attrs)
diff --git a/nix/nint/OWNERS b/nix/nint/OWNERS
index f16dd105d7..2e95807063 100644
--- a/nix/nint/OWNERS
+++ b/nix/nint/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - sterni
+sterni
diff --git a/nix/nint/default.nix b/nix/nint/default.nix
index 5cf83d15d6..0087fc0416 100644
--- a/nix/nint/default.nix
+++ b/nix/nint/default.nix
@@ -6,9 +6,11 @@ let
     ;
 in
 
-  rustSimpleBin {
-    name = "nint";
-    dependencies = [
-      depot.third_party.rust-crates.serde_json
-    ];
-  } (builtins.readFile ./nint.rs)
+rustSimpleBin
+{
+  name = "nint";
+  dependencies = [
+    depot.third_party.rust-crates.serde_json
+  ];
+}
+  (builtins.readFile ./nint.rs)
diff --git a/nix/nint/nint.rs b/nix/nint/nint.rs
index 1fa4dccb4f..abb0153c3a 100644
--- a/nix/nint/nint.rs
+++ b/nix/nint/nint.rs
@@ -1,11 +1,11 @@
 extern crate serde_json;
 
 use serde_json::Value;
+use std::convert::TryFrom;
 use std::ffi::OsString;
-use std::os::unix::ffi::{OsStringExt, OsStrExt};
-use std::io::{Error, ErrorKind, Write, stdout, stderr};
+use std::io::{stderr, stdout, Error, ErrorKind, Write};
+use std::os::unix::ffi::{OsStrExt, OsStringExt};
 use std::process::Command;
-use std::convert::{TryFrom};
 
 fn render_nix_string(s: &OsString) -> OsString {
     let mut rendered = Vec::new();
@@ -16,8 +16,8 @@ fn render_nix_string(s: &OsString) -> OsString {
         match char::from(*b) {
             '\"' => rendered.extend(b"\\\""),
             '\\' => rendered.extend(b"\\\\"),
-            '$'  => rendered.extend(b"\\$"),
-            _    => rendered.push(*b),
+            '$' => rendered.extend(b"\\$"),
+            _ => rendered.push(*b),
         }
     }
 
@@ -48,17 +48,14 @@ fn render_nix_list(arr: &[OsString]) -> OsString {
 macro_rules! handle_set_output {
     ($map_name:ident, $output_name:ident) => {
         match $map_name.get(stringify!($output_name)) {
-            Some(Value::String(s)) =>
-                $output_name().write_all(s.as_bytes()),
-            Some(_) => Err(
-                Error::new(
-                    ErrorKind::Other,
-                    format!("Attribute {} must be a string!", stringify!($output_name)),
-                )
-            ),
+            Some(Value::String(s)) => $output_name().write_all(s.as_bytes()),
+            Some(_) => Err(Error::new(
+                ErrorKind::Other,
+                format!("Attribute {} must be a string!", stringify!($output_name)),
+            )),
             None => Ok(()),
         }
-    }
+    };
 }
 
 fn main() -> std::io::Result<()> {
@@ -83,7 +80,7 @@ fn main() -> std::io::Result<()> {
         }
 
         if in_args {
-            match(arg.to_str()) {
+            match (arg.to_str()) {
                 Some("--arg") | Some("--argstr") => {
                     nix_args.push(arg);
                     nix_args.push(args.next().unwrap());
@@ -116,9 +113,7 @@ fn main() -> std::io::Result<()> {
 
         nix_args.push(argv[0].clone());
 
-        let run = Command::new("nix-instantiate")
-                          .args(nix_args)
-                          .output()?;
+        let run = Command::new("nix-instantiate").args(nix_args).output()?;
 
         match serde_json::from_slice(&run.stdout[..]) {
             Ok(Value::String(s)) => stdout().write_all(s.as_bytes()),
@@ -132,25 +127,23 @@ fn main() -> std::io::Result<()> {
 
                         match code {
                             Some(i) => std::process::exit(i),
-                            None => Err(
-                                Error::new(
-                                    ErrorKind::Other,
-                                    "Attribute exit is not an i32"
-                                )
-                            ),
+                            None => {
+                                Err(Error::new(ErrorKind::Other, "Attribute exit is not an i32"))
+                            }
                         }
-                    },
-                    Some(_) => Err(
-                        Error::new(ErrorKind::Other, "exit must be a number")
-                    ),
+                    }
+                    Some(_) => Err(Error::new(ErrorKind::Other, "exit must be a number")),
                     None => Ok(()),
                 }
-            },
-            Ok(_) => Err(Error::new(ErrorKind::Other, "output must be a string or an object")),
+            }
+            Ok(_) => Err(Error::new(
+                ErrorKind::Other,
+                "output must be a string or an object",
+            )),
             _ => {
                 stderr().write_all(&run.stderr[..]);
                 Err(Error::new(ErrorKind::Other, "internal nix error"))
-            },
+            }
         }
     }
 }
diff --git a/nix/nix-1p/README.md b/nix/nix-1p/README.md
new file mode 100644
index 0000000000..309eddb51e
--- /dev/null
+++ b/nix/nix-1p/README.md
@@ -0,0 +1,648 @@
+> [!TIP]
+> Are you interested in hacking on Nix projects for a week, together
+> with other Nix users? Do you have time at the end of August? Great,
+> come join us at [Volga Sprint](https://volgasprint.org/)!
+
+Nix - A One Pager
+=================
+
+[Nix](https://nixos.org/nix/), the package manager, is built on and with Nix,
+the language. This page serves as a fast intro to most of the (small) language.
+
+Unless otherwise specified, the word "Nix" refers only to the language below.
+
+Please file an issue if something in here confuses you or you think something
+important is missing.
+
+If you have Nix installed, you can try the examples below by running `nix repl`
+and entering code snippets there.
+
+<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
+**Table of Contents**
+
+- [Overview](#overview)
+- [Language constructs](#language-constructs)
+    - [Primitives / literals](#primitives--literals)
+    - [Operators](#operators)
+        - [`//` (merge) operator](#-merge-operator)
+    - [Variable bindings](#variable-bindings)
+    - [Functions](#functions)
+        - [Multiple arguments (currying)](#multiple-arguments-currying)
+        - [Multiple arguments (attribute sets)](#multiple-arguments-attribute-sets)
+    - [`if ... then ... else ...`](#if--then--else-)
+    - [`inherit` keyword](#inherit-keyword)
+    - [`with` statements](#with-statements)
+    - [`import` / `NIX_PATH` / `<entry>`](#import--nix_path--entry)
+    - [`or` expressions](#or-expressions)
+- [Standard libraries](#standard-libraries)
+    - [`builtins`](#builtins)
+    - [`pkgs.lib`](#pkgslib)
+    - [`pkgs` itself](#pkgs-itself)
+- [Derivations](#derivations)
+- [Nix Idioms](#nix-idioms)
+    - [File lambdas](#file-lambdas)
+    - [`callPackage`](#callpackage)
+    - [Overrides / Overlays](#overrides--overlays)
+
+<!-- markdown-toc end -->
+
+
+# Overview
+
+Nix is:
+
+*   **purely functional**. It has no concept of sequential steps being executed,
+    any dependency between operations is established by depending on *data* from
+    previous operations.
+
+    Any valid piece of Nix code is an *expression* that returns a value.
+
+    Evaluating a Nix expression *yields a single data structure*, it does not
+    execute a sequence of operations.
+
+    Every Nix file evaluates to a *single expression*.
+*   **lazy**. It will only evaluate expressions when their result is actually
+    requested.
+
+    For example, the builtin function `throw` causes evaluation to stop.
+    Entering the following expression works fine however, because we never
+    actually ask for the part of the structure that causes the `throw`.
+
+    ```nix
+    let attrs = { a = 15; b = builtins.throw "Oh no!"; };
+    in "The value of 'a' is ${toString attrs.a}"
+    ```
+*   **purpose-built**. Nix only exists to be the language for Nix, the package
+    manager. While people have occasionally used it for other use-cases, it is
+    explicitly not a general-purpose language.
+
+# Language constructs
+
+This section describes the language constructs in Nix. It is a small language
+and most of these should be self-explanatory.
+
+## Primitives / literals
+
+Nix has a handful of data types which can be represented literally in source
+code, similar to many other languages.
+
+```nix
+# numbers
+42
+1.72394
+
+# strings & paths
+"hello"
+./some-file.json
+
+# strings support interpolation
+"Hello ${name}"
+
+# multi-line strings (common prefix whitespace is dropped)
+''
+first line
+second line
+''
+
+# lists (note: no commas!)
+[ 1 2 3 ]
+
+# attribute sets (field access with dot syntax)
+{ a = 15; b = "something else"; }
+
+# recursive attribute sets (fields can reference each other)
+rec { a = 15; b = a * 2; }
+```
+
+## Operators
+
+Nix has several operators, most of which are unsurprising:
+
+| Syntax                    | Description                                                                 |
+|---------------------------|-----------------------------------------------------------------------------|
+| `+`, `-`, `*`, `/`        | Numerical operations                                                        |
+| `+`                       | String concatenation                                                        |
+| `++`                      | List concatenation                                                          |
+| `==`                      | Equality                                                                    |
+| `>`, `>=`, `<`, `<=`      | Ordering comparators                                                        |
+| `&&`                      | Logical `AND`                                                               |
+| <code>&vert;&vert;</code> | Logical `OR`                                                                |
+| `e1 -> e2`                | Logical implication (i.e. <code>!e1 &vert;&vert; e2</code>)                 |
+| `!`                       | Boolean negation                                                            |
+| `set.attr`                | Access attribute `attr` in attribute set `set`                              |
+| `set ? attribute`         | Test whether attribute set contains an attribute                            |
+| `left // right`           | Merge `left` & `right` attribute sets, with the right set taking precedence |
+
+
+### `//` (merge) operator
+
+The `//`-operator is used pervasively in Nix code. You should familiarise
+yourself with it, as it is likely also the least familiar one.
+
+It merges the left and right attribute sets given to it:
+
+```nix
+{ a = 1; } // { b = 2; }
+
+# yields { a = 1; b = 2; }
+```
+
+Values from the right side take precedence:
+
+```nix
+{ a = "left"; } // { a = "right"; }
+
+# yields { a = "right"; }
+```
+
+The merge operator does *not* recursively merge attribute sets;
+
+```nix
+{ a = { b = 1; }; } // { a = { c = 2; }; }
+
+# yields { a = { c = 2; }; }
+```
+
+Helper functions for recursive merging exist in the [`lib` library](#pkgslib).
+
+## Variable bindings
+
+Bindings in Nix are introduced locally via `let` expressions, which make some
+variables available within a given scope.
+
+For example:
+
+```nix
+let
+  a = 15;
+  b = 2;
+in a * b
+
+# yields 30
+```
+
+Variables are immutable. This means that after defining what `a` or `b` are, you
+can not *modify* their value in the scope in which they are available.
+
+You can nest `let`-expressions to shadow variables.
+
+Variables are *not* available outside of the scope of the `let` expression.
+There are no global variables.
+
+## Functions
+
+All functions in Nix are anonymous lambdas. This means that they are treated
+just like data. Giving them names is accomplished by assigning them to
+variables, or setting them as values in an attribute set (more on that below).
+
+```
+# simple function
+# declaration is simply the argument followed by a colon
+name: "Hello ${name}"
+```
+
+### Multiple arguments (currying)
+
+Technically any Nix function can only accept **one argument**. Sometimes
+however, a function needs multiple arguments. This is achieved in Nix via
+[currying][], which means to create a function with one argument, that returns a
+function with another argument, that returns ... and so on.
+
+For example:
+
+```nix
+name: age: "${name} is ${toString age} years old"
+```
+
+An additional benefit of this approach is that you can pass one parameter to a
+curried function, and receive back a function that you can re-use (similar to
+partial application):
+
+```nix
+let
+  multiply = a: b: a * b;
+  doubleIt = multiply 2; # at this point we have passed in the value for 'a' and
+                         # receive back another function that still expects 'b'
+in
+  doubleIt 15
+
+# yields 30
+```
+
+### Multiple arguments (attribute sets)
+
+Another way of specifying multiple arguments to a function in Nix is to make it
+accept an attribute set, which enables multiple other features:
+
+```nix
+{ name, age }: "${name} is ${toString age} years old"
+```
+
+Using this method, we gain the ability to specify default arguments (so that
+callers can omit them):
+
+```nix
+{ name, age ? 42 }: "${name} is ${toString age} years old"
+
+```
+
+Or in practice:
+
+```nix
+let greeter =  { name, age ? 42 }: "${name} is ${toString age} years old";
+in greeter { name = "Slartibartfast"; }
+
+# yields "Slartibartfast is 42 years old"
+# (note: Slartibartfast is actually /significantly/ older)
+```
+
+Additionally we can introduce an ellipsis using `...`, meaning that we can
+accept an attribute set as our input that contains more variables than are
+needed for the function.
+
+```nix
+let greeter = { name, age, ... }: "${name} is ${toString age} years old";
+    person = {
+      name = "Slartibartfast";
+      age = 42;
+      # the 'email' attribute is not expected by the 'greeter' function ...
+      email = "slartibartfast@magrath.ea";
+    };
+in greeter person # ... but the call works due to the ellipsis.
+```
+
+Nix also supports binding the whole set of passed in attributes to a
+parameter using the `@` syntax:
+
+```nix
+let func = { name, age, ... }@args: builtins.attrNames args;
+in func {
+    name = "Slartibartfast";
+    age = 42;
+    email = "slartibartfast@magrath.ea";
+}
+
+# yields: [ "age" "email" "name" ]
+```
+
+**Warning:** Combining the `@` syntax with default arguments can lead
+to surprising behaviour, as the passed attributes are bound verbatim.
+This means that defaulted arguments are not included in the bound
+attribute set:
+
+```nix
+({ a ? 1, b }@args: args.a) { b = 1; }
+# throws: error: attribute 'a' missing
+
+({ a ? 1, b }@args: args.a) { b = 1; a = 2; }
+# => 2
+```
+
+## `if ... then ... else ...`
+
+Nix has simple conditional support. Note that `if` is an **expression** in Nix,
+which means that both branches must be specified.
+
+```nix
+if someCondition
+then "it was true"
+else "it was false"
+```
+
+## `inherit` keyword
+
+The `inherit` keyword is used in attribute sets or `let` bindings to "inherit"
+variables from the parent scope.
+
+In short, a statement like `inherit foo;` expands to `foo = foo;`.
+
+Consider this example:
+
+```nix
+let
+  name = "Slartibartfast";
+  # ... other variables
+in {
+  name = name; # set the attribute set key 'name' to the value of the 'name' var
+  # ... other attributes
+}
+```
+
+The `name = name;` line can be replaced with `inherit name;`:
+
+```nix
+let
+  name = "Slartibartfast";
+  # ... other variables
+in {
+  inherit name;
+  # ... other attributes
+}
+```
+
+This is often convenient, especially because inherit supports multiple variables
+at the same time as well as "inheritance" from other attribute sets:
+
+```nix
+{
+  inherit name age; # equivalent to `name = name; age = age;`
+  inherit (otherAttrs) email; # equivalent to `email = otherAttrs.email`;
+}
+```
+
+## `with` statements
+
+The `with` statement "imports" all attributes from an attribute set into
+variables of the same name:
+
+```nix
+let attrs = { a = 15; b = 2; };
+in with attrs; a + b # 'a' and 'b' become variables in the scope following 'with'
+```
+
+The scope of a `with`-"block" is the expression immediately following the
+semicolon, i.e.:
+
+```nix
+let attrs = { /* some attributes */ };
+in with attrs; (/* this is the scope of the `with` */)
+```
+
+## `import` / `NIX_PATH` / `<entry>`
+
+Nix files can import each other by using the builtin `import` function and a
+literal path:
+
+```nix
+# assuming there is a file lib.nix with some useful functions
+let myLib = import ./lib.nix;
+in myLib.usefulFunction 42
+```
+
+The `import` function will read and evaluate the file, and return its Nix value.
+
+Nix files often begin with a function header to pass parameters into the rest of
+the file, so you will often see imports of the form `import ./some-file { ... }`.
+
+Nix has a concept of a `NIX_PATH` (similar to the standard `PATH` environment
+variable) which contains named aliases for file paths containing Nix
+expressions.
+
+In a standard Nix installation, several [channels][] will be present (for
+example `nixpkgs` or `nixos-unstable`) on the `NIX_PATH`.
+
+`NIX_PATH` entries can be accessed using the `<entry>` syntax, which simply
+evaluates to their file path:
+
+```nix
+<nixpkgs>
+# might yield something like `/home/tazjin/.nix-defexpr/channels/nixpkgs`
+```
+
+This is commonly used to import from channels:
+
+```nix
+let pkgs = import <nixpkgs> {};
+in pkgs.something
+```
+
+## `or` expressions
+
+Nix has a keyword called `or` which can be used to access a value from an
+attribute set while providing a fallback to a default value.
+
+The syntax is simple:
+
+```nix
+# Access an existing attribute
+let set = { a = 42; };
+in set.a or 23
+```
+
+Since the attribute `a` exists, this will return `42`.
+
+
+```nix
+# ... or fall back to a default if there is no such key
+let set = { };
+in set.a or 23
+```
+
+Since the attribute `a` does not exist, this will fall back to returning the
+default value `23`.
+
+Note that `or` expressions also work for nested attribute set access.
+
+# Standard libraries
+
+Yes, libraries, plural.
+
+Nix has three major things that could be considered its standard library and
+while there's a lot of debate to be had about this point, you still need to know
+all three.
+
+## `builtins`
+
+Nix comes with several functions that are baked into the language. These work
+regardless of which other Nix code you may or may not have imported.
+
+Most of these functions are implemented in the Nix interpreter itself, which
+means that they are rather fast when compared to some of the equivalents which
+are implemented in Nix itself.
+
+The Nix manual has [a section listing all `builtins`][builtins] and their usage.
+
+Examples of builtins that you will commonly encounter include, but are not
+limited to:
+
+* `derivation` (see [Derivations](#derivations))
+* `toJSON` / `fromJSON`
+* `toString`
+* `toPath` / `fromPath`
+
+The builtins also include several functions that have the (spooky) ability to
+break Nix' evaluation purity. No functions written in Nix itself can do this.
+
+Examples of those include:
+
+* `fetchGit` which can fetch a git-repository using the environment's default
+  git/ssh configuration
+* `fetchTarball` which can fetch & extract archives without having to specify
+  hashes
+
+Read through the manual linked above to get the full overview.
+
+## `pkgs.lib`
+
+The Nix package set, commonly referred to by Nixers simply as [nixpkgs][],
+contains a child attribute set called `lib` which provides a large number of
+useful functions.
+
+The canonical definition of these functions is [their source code][lib-src]. I
+wrote a tool ([nixdoc][]) in 2018 which generates manual entries for these
+functions, however not all of the files are included as of July 2019.
+
+See the [Nixpkgs manual entry on `lib`][lib-manual] for the documentation.
+
+These functions include various utilities for dealing with the data types in Nix
+(lists, attribute sets, strings etc.) and it is useful to at least skim through
+them to familiarise yourself with what is available.
+
+```nix
+{ pkgs ? import <nixpkgs> {} }:
+
+with pkgs.lib; # bring contents pkgs.lib into scope
+
+strings.toUpper "hello"
+
+# yields "HELLO"
+```
+
+## `pkgs` itself
+
+The Nix package set itself does not just contain packages, but also many useful
+functions which you might run into while creating new Nix packages.
+
+One particular subset of these that stands out are the [trivial builders][],
+which provide utilities for writing text files or shell scripts, running shell
+commands and capturing their output and so on.
+
+```nix
+{ pkgs ? import <nixpkgs> {} }:
+
+pkgs.writeText "hello.txt" "Hello dear reader!"
+
+# yields a derivation which creates a text file with the above content
+```
+
+# Derivations
+
+When a Nix expression is evaluated it may yield one or more *derivations*.
+Derivations describe a single build action that, when run, places one or more
+outputs (whether they be files or folders) in the Nix store.
+
+The builtin function `derivation` is responsible for creating derivations at a
+lower level. Usually when Nix users create derivations they will use the
+higher-level functions such as [stdenv.mkDerivation][smkd].
+
+Please see the manual [on derivations][drv-manual] for more information, as the
+general build logic is out of scope for this document.
+
+# Nix Idioms
+
+There are several idioms in Nix which are not technically part of the language
+specification, but will commonly be encountered in the wild.
+
+This section is an (incomplete) list of them.
+
+## File lambdas
+
+It is customary to start every file with a function header that receives the
+files dependencies, instead of importing them directly in the file.
+
+Sticking to this pattern lets users of your code easily change out, for example,
+the specific version of `nixpkgs` that is used.
+
+A common file header pattern would look like this:
+
+```nix
+{ pkgs ? import <nixpkgs> {} }:
+
+# ... 'pkgs' is then used in the code
+```
+
+In some sense, you might consider the function header of a file to be its "API".
+
+## `callPackage`
+
+Building on the previous pattern, there is a custom in nixpkgs of specifying the
+dependencies of your file explicitly instead of accepting the entire package
+set.
+
+For example, a file containing build instructions for a tool that needs the
+standard build environment and `libsvg` might start like this:
+
+```nix
+# my-funky-program.nix
+{ stdenv, libsvg }:
+
+stdenv.mkDerivation { ... }
+```
+
+Any time a file follows this header pattern it is probably meant to be imported
+using a special function called `callPackage` which is part of the top-level
+package set (as well as certain subsets, such as `haskellPackages`).
+
+```nix
+{ pkgs ? import <nixpkgs> {} }:
+
+let my-funky-program = pkgs.callPackage ./my-funky-program.nix {};
+in # ... something happens with my-funky-program
+```
+
+The `callPackage` function looks at the expected arguments (via
+`builtins.functionArgs`) and passes the appropriate keys from the set in which
+it is defined as the values for each corresponding argument.
+
+## Overrides / Overlays
+
+One of the most powerful features of Nix is that the representation of all build
+instructions as data means that they can easily be *overridden* to get a
+different result.
+
+For example, assuming there is a package `someProgram` which is built without
+our favourite configuration flag (`--mimic-threaten-tag`) we might override it
+like this:
+
+```nix
+someProgram.overrideAttrs(old: {
+    configureFlags = old.configureFlags or [] ++ ["--mimic-threaten-tag"];
+})
+```
+
+This pattern has a variety of applications of varying complexity. The top-level
+package set itself can have an `overlays` argument passed to it which may add
+new packages to the imported set.
+
+Note the use of the `or` operator to default to an empty list if the
+original flags do not include `configureFlags`. This is required in
+case a package does not set any flags by itself.
+
+Since this can change in a package over time, it is useful to guard
+against it using `or`.
+
+For a slightly more advanced example, assume that we want to import `<nixpkgs>`
+but have the modification above be reflected in the imported package set:
+
+```nix
+let
+  overlay = (final: prev: {
+    someProgram = prev.someProgram.overrideAttrs(old: {
+      configureFlags = old.configureFlags or [] ++ ["--mimic-threaten-tag"];
+    });
+  });
+in import <nixpkgs> { overlays = [ overlay ]; }
+```
+
+The overlay function receives two arguments, `final` and `prev`. `final` is
+the [fixed point][fp] of the overlay's evaluation, i.e. the package set
+*including* the new packages and `prev` is the "original" package set.
+
+See the Nix manual sections [on overrides][] and [on overlays][] for more
+details (note: the convention has moved away from using `self` in favor of
+`final`, and `prev` instead of `super`, but the documentation has not been
+updated to reflect this).
+
+[currying]: https://en.wikipedia.org/wiki/Currying
+[builtins]: https://nixos.org/manual/nix/stable/language/builtins
+[nixpkgs]: https://github.com/NixOS/nixpkgs
+[lib-src]: https://github.com/NixOS/nixpkgs/tree/master/lib
+[nixdoc]: https://github.com/tazjin/nixdoc
+[lib-manual]: https://nixos.org/manual/nixpkgs/stable/#sec-functions-library
+[channels]: https://nixos.org/manual/nix/stable/command-ref/files/channels
+[trivial builders]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/trivial-builders/default.nix
+[smkd]: https://nixos.org/manual/nixpkgs/stable/#chap-stdenv
+[drv-manual]: https://nixos.org/manual/nix/stable/language/derivations
+[fp]: https://github.com/NixOS/nixpkgs/blob/master/lib/fixed-points.nix
+[on overrides]: https://nixos.org/manual/nixpkgs/stable/#chap-overrides
+[on overlays]: https://nixos.org/manual/nixpkgs/stable/#chap-overlays
diff --git a/nix/nix-1p/default.nix b/nix/nix-1p/default.nix
new file mode 100644
index 0000000000..6cc71b9548
--- /dev/null
+++ b/nix/nix-1p/default.nix
@@ -0,0 +1,16 @@
+# The canonical source location of nix-1p is //nix/nix-1p in the TVL
+# depot: https://code.tvl.fyi/about/nix/nix-1p
+#
+# This file configures TVL CI to mirror the subtree to GitHub.
+{ depot ? { }, pkgs ? import <nixpkgs> { }, ... }:
+
+(pkgs.runCommandLocal "nix-1p" { } ''
+  mkdir $out
+  cp ${./README.md} $out/README.md
+'').overrideAttrs (_: {
+  meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+    filter = ":/nix/nix-1p";
+    remote = "git@github.com:tazjin/nix-1p.git";
+    ref = "refs/heads/master";
+  };
+})
diff --git a/nix/readTree/README.md b/nix/readTree/README.md
index f8bbe2255e..5d430d1cfc 100644
--- a/nix/readTree/README.md
+++ b/nix/readTree/README.md
@@ -52,14 +52,17 @@ true;` attribute merged into it.
 `readTree` will follow any subdirectories of a tree and import all Nix files,
 with some exceptions:
 
+* If a folder contains a `default.nix` file, no *sibling* Nix files will be
+  imported - however children are traversed as normal.
+* If a folder contains a `default.nix` it is loaded and, if it
+  evaluates to a set, *merged* with the children. If it evaluates to
+  anything other than a set, else the children are *not traversed*.
+* A folder can opt out from readTree completely by containing a
+  `.skip-tree` file. The content of the file is not read. These
+  folders will be missing completely from the readTree structure.
 * A folder can declare that its children are off-limit by containing a
   `.skip-subtree` file. Since the content of the file is not checked, it can be
   useful to leave a note for a human in the file.
-* If a folder contains a `default.nix` file, no *sibling* Nix files will be
-  imported - however children are traversed as normal.
-* If a folder contains a `default.nix` it is loaded and, if it evaluates to a
-  set, *merged* with the children. If it evaluates to anything else the children
-  are *not traversed*.
 * The `default.nix` of the top-level folder on which readTree is
   called is **not** read to avoid infinite recursion (as, presumably,
   this file is where readTree itself is called).
diff --git a/nix/readTree/default.nix b/nix/readTree/default.nix
index 259f2f2fbf..4a745ce33c 100644
--- a/nix/readTree/default.nix
+++ b/nix/readTree/default.nix
@@ -2,7 +2,7 @@
 # Copyright (c) 2020-2021 The TVL Authors
 # SPDX-License-Identifier: MIT
 #
-# Provides a function to automatically read a a filesystem structure
+# Provides a function to automatically read a filesystem structure
 # into a Nix attribute set.
 #
 # Called with an attribute set taking the following arguments:
@@ -41,12 +41,16 @@ let
   readDirVisible = path:
     let
       children = readDir path;
-      isVisible = f: f == ".skip-subtree" || (substring 0 1 f) != ".";
+      # skip hidden files, except for those that contain special instructions to readTree
+      isVisible = f: f == ".skip-subtree" || f == ".skip-tree" || (substring 0 1 f) != ".";
       names = filter isVisible (attrNames children);
-    in listToAttrs (map (name: {
-      inherit name;
-      value = children.${name};
-    }) names);
+    in
+    listToAttrs (map
+      (name: {
+        inherit name;
+        value = children.${name};
+      })
+      names);
 
   # Create a mark containing the location of this attribute and
   # a list of all child attribute names added by readTree.
@@ -55,66 +59,121 @@ let
     __readTreeChildren = builtins.attrNames children;
   };
 
+  # Create a label from a target's tree location.
+  mkLabel = target:
+    let label = concatStringsSep "/" target.__readTree;
+    in if target ? __subtarget
+    then "${label}:${target.__subtarget}"
+    else label;
+
+  # Merge two attribute sets, but place attributes in `passthru` via
+  # `overrideAttrs` for derivation targets that support it.
+  merge = a: b:
+    if a ? overrideAttrs
+    then
+      a.overrideAttrs
+        (prev: {
+          passthru = (prev.passthru or { }) // b;
+        })
+    else a // b;
+
   # Import a file and enforce our calling convention
   importFile = args: scopedArgs: path: parts: filter:
-  let
-      importedFile = if scopedArgs != {}
-                     then builtins.scopedImport scopedArgs path
-                     else import path;
+    let
+      importedFile =
+        if scopedArgs != { } && builtins ? scopedImport # For tvix
+        then builtins.scopedImport scopedArgs path
+        else import path;
       pathType = builtins.typeOf importedFile;
-  in
+    in
     if pathType != "lambda"
-    then builtins.throw "readTree: trying to import ${toString path}, but it’s a ${pathType}, you need to make it a function like { depot, pkgs, ... }"
+    then throw "readTree: trying to import ${toString path}, but it’s a ${pathType}, you need to make it a function like { depot, pkgs, ... }"
     else importedFile (filter parts (argsWithPath args parts));
 
   nixFileName = file:
     let res = match "(.*)\\.nix" file;
     in if res == null then null else head res;
 
-  readTree = { args, initPath, rootDir, parts, argsFilter, scopedArgs }:
+  # Internal implementation of readTree, which handles things like the
+  # skipping of trees and subtrees.
+  #
+  # This method returns an attribute sets with either of two shapes:
+  #
+  # { ok = ...; }    # a tree was read successfully
+  # { skip = true; } # a tree was skipped
+  #
+  # The higher-level `readTree` method assembles the final attribute
+  # set out of these results at the top-level, and the internal
+  # `children` implementation unwraps and processes nested trees.
+  readTreeImpl = { args, initPath, rootDir, parts, argsFilter, scopedArgs }:
     let
       dir = readDirVisible initPath;
+
+      # Determine whether any part of this tree should be skipped.
+      #
+      # Adding a `.skip-subtree` file will still allow the import of
+      # the current node's "default.nix" file, but stop recursion
+      # there.
+      #
+      # Adding a `.skip-tree` file will completely ignore the folder
+      # in which this file is located.
+      skipTree = hasAttr ".skip-tree" dir;
+      skipSubtree = skipTree || hasAttr ".skip-subtree" dir;
+
       joinChild = c: initPath + ("/" + c);
 
-      self = if rootDir
-        then { __readTree = []; }
+      self =
+        if rootDir
+        then { __readTree = [ ]; }
         else importFile args scopedArgs initPath parts argsFilter;
 
-      # Import subdirectories of the current one, unless the special
-      # `.skip-subtree` file exists which makes readTree ignore the
-      # children.
+      # Import subdirectories of the current one, unless any skip
+      # instructions exist.
       #
       # This file can optionally contain information on why the tree
       # should be ignored, but its content is not inspected by
       # readTree
       filterDir = f: dir."${f}" == "directory";
-      children = if hasAttr ".skip-subtree" dir then [] else map (c: {
-        name = c;
-        value = readTree {
-          inherit argsFilter scopedArgs;
-          args = args;
-          initPath = (joinChild c);
-          rootDir = false;
-          parts = (parts ++ [ c ]);
-        };
-      }) (filter filterDir (attrNames dir));
+      filteredChildren = map
+        (c: {
+          name = c;
+          value = readTreeImpl {
+            inherit argsFilter scopedArgs;
+            args = args;
+            initPath = (joinChild c);
+            rootDir = false;
+            parts = (parts ++ [ c ]);
+          };
+        })
+        (filter filterDir (attrNames dir));
+
+      # Remove skipped children from the final set, and unwrap the
+      # result set.
+      children =
+        if skipSubtree then [ ]
+        else map ({ name, value }: { inherit name; value = value.ok; }) (filter (child: child.value ? ok) filteredChildren);
 
       # Import Nix files
-      nixFiles = if hasAttr ".skip-subtree" dir then []
+      nixFiles =
+        if skipSubtree then [ ]
         else filter (f: f != null) (map nixFileName (attrNames dir));
-      nixChildren = map (c: let
-        p = joinChild (c + ".nix");
-        childParts = parts ++ [ c ];
-        imported = importFile args scopedArgs p childParts argsFilter;
-      in {
-        name = c;
-        value =
-          if isAttrs imported
-          then imported // marker childParts {}
-          else imported;
-      }) nixFiles;
-
-      nodeValue = if dir ? "default.nix" then self else {};
+      nixChildren = map
+        (c:
+          let
+            p = joinChild (c + ".nix");
+            childParts = parts ++ [ c ];
+            imported = importFile args scopedArgs p childParts argsFilter;
+          in
+          {
+            name = c;
+            value =
+              if isAttrs imported
+              then merge imported (marker childParts { })
+              else imported;
+          })
+        nixFiles;
+
+      nodeValue = if dir ? "default.nix" then self else { };
 
       allChildren = listToAttrs (
         if dir ? "default.nix"
@@ -123,16 +182,45 @@ let
       );
 
     in
-      if isAttrs nodeValue
-      then nodeValue // allChildren // (marker parts allChildren)
-      else nodeValue;
+    if skipTree
+    then { skip = true; }
+    else {
+      ok =
+        if isAttrs nodeValue
+        then merge nodeValue (allChildren // (marker parts allChildren))
+        else nodeValue;
+    };
+
+  # Top-level implementation of readTree itself.
+  readTree = args:
+    let
+      tree = readTreeImpl args;
+    in
+    if tree ? skip
+    then throw "Top-level folder has a .skip-tree marker and could not be read by readTree!"
+    else tree.ok;
+
+  # Helper function to fetch subtargets from a target. This is a
+  # temporary helper to warn on the use of the `meta.targets`
+  # attribute, which is deprecated in favour of `meta.ci.targets`.
+  subtargets = node:
+    let targets = (node.meta.targets or [ ]) ++ (node.meta.ci.targets or [ ]);
+    in if node ? meta.targets then
+      builtins.trace ''
+        Warning: The meta.targets attribute is deprecated.
+
+        Please move the subtargets of //${mkLabel node} to the
+        meta.ci.targets attribute.
+        
+      ''
+        targets else targets;
 
   # Function which can be used to find all readTree targets within an
   # attribute set.
   #
   # This function will gather physical targets, that is targets which
   # correspond directly to a location in the repository, as well as
-  # subtargets (specified in the meta.targets attribute of a node).
+  # subtargets (specified in the meta.ci.targets attribute of a node).
   #
   # This can be used to discover targets for inclusion in CI
   # pipelines.
@@ -143,40 +231,42 @@ let
   #             should be included in the build.
   gather = eligible: node:
     if node ? __readTree then
-      # Include the node itself if it is eligible.
-      (if eligible node then [ node ] else [])
+    # Include the node itself if it is eligible.
+      (if eligible node then [ node ] else [ ])
       # Include eligible children of the node
       ++ concatMap (gather eligible) (map (attr: node."${attr}") node.__readTreeChildren)
       # Include specified sub-targets of the node
       ++ filter eligible (map
-           (k: (node."${k}" or {}) // {
-             # Keep the same tree location, but explicitly mark this
-             # node as a subtarget.
-             __readTree = node.__readTree;
-             __readTreeChildren = [];
-             __subtarget = k;
-           })
-           (node.meta.targets or []))
-    else [];
+        (k: (node."${k}" or { }) // {
+          # Keep the same tree location, but explicitly mark this
+          # node as a subtarget.
+          __readTree = node.__readTree;
+          __readTreeChildren = [ ];
+          __subtarget = k;
+        })
+        (subtargets node))
+    else [ ];
 
   # Determine whether a given value is a derivation.
   # Copied from nixpkgs/lib for cases where lib is not available yet.
   isDerivation = x: isAttrs x && x ? type && x.type == "derivation";
-in {
-  inherit gather;
+in
+{
+  inherit gather mkLabel;
 
   __functor = _:
     { path
     , args
     , filter ? (_parts: x: x)
-    , scopedArgs ? {} }:
-      readTree {
-        inherit args scopedArgs;
-        argsFilter = filter;
-        initPath = path;
-        rootDir = true;
-        parts = [];
-      };
+    , scopedArgs ? { }
+    }:
+    readTree {
+      inherit args scopedArgs;
+      argsFilter = filter;
+      initPath = path;
+      rootDir = true;
+      parts = [ ];
+    };
 
   # In addition to readTree itself, some functionality is exposed that
   # is useful for users of readTree.
@@ -193,7 +283,7 @@ in {
   #               which should be able to access the restricted folder.
   #
   #   reason: Textual explanation for the restriction (included in errors)
-  restrictFolder = { folder, exceptions ? [], reason }: parts: args:
+  restrictFolder = { folder, exceptions ? [ ], reason }: parts: args:
     if (elemAt parts 0) == folder || elem parts exceptions
     then args
     else args // {
@@ -216,16 +306,21 @@ in {
   # It is often required to create the args attribute set.
   fix = f: let x = f x; in x;
 
-  # Takes an attribute set and adds a meta.targets attribute to it
+  # Takes an attribute set and adds a meta.ci.targets attribute to it
   # which contains all direct children of the attribute set which are
   # derivations.
   #
   # Type: attrs -> attrs
-  drvTargets = attrs: attrs // {
-    meta = {
-      targets = builtins.filter
-      (x: isDerivation attrs."${x}")
-      (builtins.attrNames attrs);
-    } // (attrs.meta or {});
-  };
+  drvTargets = attrs:
+    attrs // {
+      # preserve .meta from original attrs
+      meta = (attrs.meta or { }) // {
+        # preserve .meta.ci (except .targets) from original attrs
+        ci = (attrs.meta.ci or { }) // {
+          targets = builtins.filter
+            (x: isDerivation attrs."${x}")
+            (builtins.attrNames attrs);
+        };
+      };
+    };
 }
diff --git a/nix/readTree/tests/default.nix b/nix/readTree/tests/default.nix
index 3354a4fe5e..6f9eb02eff 100644
--- a/nix/readTree/tests/default.nix
+++ b/nix/readTree/tests/default.nix
@@ -10,13 +10,13 @@ let
 
   tree-ex = depot.nix.readTree {
     path = ./test-example;
-    args = {};
+    args = { };
   };
 
   example = it "corresponds to the README example" [
     (assertEq "third_party attrset"
       (lib.isAttrs tree-ex.third_party
-      && (! lib.isDerivation tree-ex.third_party))
+        && (! lib.isDerivation tree-ex.third_party))
       true)
     (assertEq "third_party attrset other attribute"
       tree-ex.third_party.favouriteColour
@@ -37,10 +37,20 @@ let
 
   tree-tl = depot.nix.readTree {
     path = ./test-tree-traversal;
-    args = {};
+    args = { };
   };
 
   traversal-logic = it "corresponds to the traversal logic in the README" [
+    (assertEq "skip-tree/a is read"
+      tree-tl.skip-tree.a
+      "a is read normally")
+    (assertEq "skip-tree does not contain b"
+      (builtins.attrNames tree-tl.skip-tree)
+      [ "__readTree" "__readTreeChildren" "a" ])
+    (assertEq "skip-tree children list does not contain b"
+      tree-tl.skip-tree.__readTreeChildren
+      [ "a" ])
+
     (assertEq "skip subtree default.nix is read"
       tree-tl.skip-subtree.but
       "the default.nix is still read")
@@ -82,7 +92,7 @@ let
       "Picked up through the drv")
     (assertEq "default.nix drv is not changed by readTree"
       tree-tl.default-nix.can-be-drv
-      (import ./test-tree-traversal/default-nix/can-be-drv/default.nix {}))
+      (import ./test-tree-traversal/default-nix/can-be-drv/default.nix { }))
   ];
 
   # these each call readTree themselves because the throws have to happen inside assertThrows
@@ -90,7 +100,7 @@ let
     (assertThrows "this file is not a function"
       (depot.nix.readTree {
         path = ./test-wrong-not-a-function;
-        args = {};
+        args = { };
       }).not-a-function)
     # can’t test for that, assertThrows can’t catch this error
     # (assertThrows "this file is a function but doesn’t have dots"
@@ -99,12 +109,13 @@ let
 
   read-markers = depot.nix.readTree {
     path = ./test-marker;
-    args = {};
+    args = { };
   };
 
   assertMarkerByPath = path:
     assertEq "${lib.concatStringsSep "." path} is marked correctly"
-      (lib.getAttrFromPath path read-markers).__readTree path;
+      (lib.getAttrFromPath path read-markers).__readTree
+      path;
 
   markers = it "marks nodes correctly" [
     (assertMarkerByPath [ "directory-marked" ])
@@ -119,7 +130,8 @@ let
       read-markers.directory-marked.nested.__readTreeChildren [ ])
   ];
 
-in runTestsuite "readTree" [
+in
+runTestsuite "readTree" [
   example
   traversal-logic
   wrong
diff --git a/nix/readTree/tests/test-marker/directory-marked/default.nix b/nix/readTree/tests/test-marker/directory-marked/default.nix
index a3f961128e..5bd3e36b53 100644
--- a/nix/readTree/tests/test-marker/directory-marked/default.nix
+++ b/nix/readTree/tests/test-marker/directory-marked/default.nix
@@ -1,3 +1,3 @@
 { ... }:
 
-{}
+{ }
diff --git a/nix/readTree/tests/test-marker/directory-marked/nested/default.nix b/nix/readTree/tests/test-marker/directory-marked/nested/default.nix
index a3f961128e..5bd3e36b53 100644
--- a/nix/readTree/tests/test-marker/directory-marked/nested/default.nix
+++ b/nix/readTree/tests/test-marker/directory-marked/nested/default.nix
@@ -1,3 +1,3 @@
 { ... }:
 
-{}
+{ }
diff --git a/nix/readTree/tests/test-marker/file-children/one.nix b/nix/readTree/tests/test-marker/file-children/one.nix
index a3f961128e..5bd3e36b53 100644
--- a/nix/readTree/tests/test-marker/file-children/one.nix
+++ b/nix/readTree/tests/test-marker/file-children/one.nix
@@ -1,3 +1,3 @@
 { ... }:
 
-{}
+{ }
diff --git a/nix/readTree/tests/test-marker/file-children/two.nix b/nix/readTree/tests/test-marker/file-children/two.nix
index a3f961128e..5bd3e36b53 100644
--- a/nix/readTree/tests/test-marker/file-children/two.nix
+++ b/nix/readTree/tests/test-marker/file-children/two.nix
@@ -1,3 +1,3 @@
 { ... }:
 
-{}
+{ }
diff --git a/nix/readTree/tests/test-tree-traversal/skip-tree/a/default.nix b/nix/readTree/tests/test-tree-traversal/skip-tree/a/default.nix
new file mode 100644
index 0000000000..186488be3c
--- /dev/null
+++ b/nix/readTree/tests/test-tree-traversal/skip-tree/a/default.nix
@@ -0,0 +1 @@
+_: "a is read normally"
diff --git a/nix/readTree/tests/test-tree-traversal/skip-tree/b/.skip-tree b/nix/readTree/tests/test-tree-traversal/skip-tree/b/.skip-tree
new file mode 100644
index 0000000000..34936b45d1
--- /dev/null
+++ b/nix/readTree/tests/test-tree-traversal/skip-tree/b/.skip-tree
@@ -0,0 +1 @@
+b subfolder should be skipped completely
diff --git a/nix/readTree/tests/test-tree-traversal/skip-tree/b/default.nix b/nix/readTree/tests/test-tree-traversal/skip-tree/b/default.nix
new file mode 100644
index 0000000000..7903f8e95a
--- /dev/null
+++ b/nix/readTree/tests/test-tree-traversal/skip-tree/b/default.nix
@@ -0,0 +1 @@
+throw "b is skipped completely"
diff --git a/nix/renderMarkdown/default.nix b/nix/renderMarkdown/default.nix
index 3e5a59954b..8759ada0fe 100644
--- a/nix/renderMarkdown/default.nix
+++ b/nix/renderMarkdown/default.nix
@@ -3,6 +3,19 @@
 
 with depot.nix.yants;
 
-defun [ path drv ] (file: pkgs.runCommandNoCC "${file}.rendered.html" {} ''
-  cat ${file} | ${depot.tools.cheddar}/bin/cheddar --about-filter ${file} > $out
-'')
+let
+  args = struct "args" {
+    path = path;
+    tagfilter = option bool;
+  };
+in
+defun [ (either path args) drv ]
+  (arg: pkgs.runCommand "${arg.path or arg}.rendered.html" { }
+    (
+      let
+        tagfilter = if (arg.tagfilter or true) then "" else "--no-tagfilter";
+      in
+      ''
+        cat ${arg.path or arg} | ${depot.tools.cheddar}/bin/cheddar --about-filter ${tagfilter} ${arg.path or arg} > $out
+      ''
+    ))
diff --git a/nix/runExecline/default.nix b/nix/runExecline/default.nix
index fd92203d01..76fffdce7b 100644
--- a/nix/runExecline/default.nix
+++ b/nix/runExecline/default.nix
@@ -9,7 +9,7 @@ let
   runExeclineLocal = name: args: execline:
     runExecline name
       (args // {
-        derivationArgs = args.derivationArgs or {} // {
+        derivationArgs = args.derivationArgs or { } // {
           preferLocalBuild = true;
           allowSubstitutes = false;
         };
@@ -23,7 +23,8 @@ let
     inherit pkgs;
   };
 
-in {
+in
+{
   __functor = _: runExecline;
   local = runExeclineLocal;
   inherit tests;
diff --git a/nix/runExecline/runExecline.nix b/nix/runExecline/runExecline.nix
index 0e45080735..23b9a63303 100644
--- a/nix/runExecline/runExecline.nix
+++ b/nix/runExecline/runExecline.nix
@@ -35,32 +35,32 @@
 
 let
   bins = getBins pkgs.execline [
-           "execlineb"
-           { use = "if"; as = "execlineIf"; }
-           "redirfd"
-           "importas"
-           "exec"
-         ]
-      // getBins pkgs.s6-portable-utils [
-           "s6-cat"
-           "s6-grep"
-           "s6-touch"
-           "s6-test"
-           "s6-chmod"
-         ];
+    "execlineb"
+    { use = "if"; as = "execlineIf"; }
+    "redirfd"
+    "importas"
+    "exec"
+  ]
+  // getBins pkgs.s6-portable-utils [
+    "s6-cat"
+    "s6-grep"
+    "s6-touch"
+    "s6-test"
+    "s6-chmod"
+  ];
 
 in
 
 # TODO: move name into the attrset
 name:
 {
-# a string to pass as stdin to the execline script
-stdin ? ""
-# a program wrapping the acutal execline invocation;
-# should be in Bernstein-chaining style
+  # a string to pass as stdin to the execline script
+  stdin ? ""
+  # a program wrapping the acutal execline invocation;
+  # should be in Bernstein-chaining style
 , builderWrapper ? bins.exec
-# additional arguments to pass to the derivation
-, derivationArgs ? {}
+  # additional arguments to pass to the derivation
+, derivationArgs ? { }
 }:
 # the execline script as a nested list of string,
 # representing the blocks;
@@ -90,33 +90,33 @@ derivation (derivationArgs // {
   passAsFile = [
     "_runExeclineScript"
     "_runExeclineStdin"
-  ] ++ derivationArgs.passAsFile or [];
+  ] ++ derivationArgs.passAsFile or [ ];
 
   # the default, exec acts as identity executable
   builder = builderWrapper;
 
   args = [
-    bins.importas            # import script file as $script
-    "-ui"                    # drop the envvar afterwards
-    "script"                 # substitution name
+    bins.importas # import script file as $script
+    "-ui" # drop the envvar afterwards
+    "script" # substitution name
     "_runExeclineScriptPath" # passed script file
 
-    bins.importas            # do the same for $stdin
+    bins.importas # do the same for $stdin
     "-ui"
     "stdin"
     "_runExeclineStdinPath"
 
-    bins.redirfd             # now we
-    "-r"                     # read the file
-    "0"                      # into the stdin of execlineb
-    "$stdin"                 # that was given via stdin
+    bins.redirfd # now we
+    "-r" # read the file
+    "0" # into the stdin of execlineb
+    "$stdin" # that was given via stdin
 
-    bins.execlineb           # the actual invocation
+    bins.execlineb # the actual invocation
     # TODO(Profpatsch): depending on the use-case, -S0 might not be enough
     # in all use-cases, then a wrapper for execlineb arguments
     # should be added (-P, -S, -s).
-    "-S0"                    # set $@ inside the execline script
-    "-W"                     # die on syntax error
-    "$script"                # substituted by importas
+    "-S0" # set $@ inside the execline script
+    "-W" # die on syntax error
+    "$script" # substituted by importas
   ];
 })
diff --git a/nix/runExecline/tests.nix b/nix/runExecline/tests.nix
index d2f5a1780c..f82b544224 100644
--- a/nix/runExecline/tests.nix
+++ b/nix/runExecline/tests.nix
@@ -1,23 +1,29 @@
-{ stdenv, pkgs, runExecline, runExeclineLocal, getBins, writeScript
-# https://www.mail-archive.com/skaware@list.skarnet.org/msg01256.html
-, coreutils }:
+{ stdenv
+, pkgs
+, runExecline
+, runExeclineLocal
+, getBins
+, writeScript
+  # https://www.mail-archive.com/skaware@list.skarnet.org/msg01256.html
+, coreutils
+}:
 
 let
 
   bins = getBins coreutils [ "mv" ]
-      // getBins pkgs.execline [
-           "execlineb"
-           { use = "if"; as = "execlineIf"; }
-           "redirfd"
-           "importas"
-         ]
-      // getBins pkgs.s6-portable-utils [
-           "s6-chmod"
-           "s6-grep"
-           "s6-touch"
-           "s6-cat"
-           "s6-test"
-         ];
+    // getBins pkgs.execline [
+    "execlineb"
+    { use = "if"; as = "execlineIf"; }
+    "redirfd"
+    "importas"
+  ]
+    // getBins pkgs.s6-portable-utils [
+    "s6-chmod"
+    "s6-grep"
+    "s6-touch"
+    "s6-cat"
+    "s6-test"
+  ];
 
   # execline block of depth 1
   block = args: builtins.map (arg: " ${arg}") args ++ [ "" ];
@@ -31,49 +37,80 @@ let
     builder = bins.execlineIf;
     args =
       (block [
-        bins.redirfd "-r" "0" file   # read file to stdin
-        bins.s6-grep "-F" "-q" line   # and grep for the line
+        bins.redirfd
+        "-r"
+        "0"
+        file # read file to stdin
+        bins.s6-grep
+        "-F"
+        "-q"
+        line # and grep for the line
       ])
       ++ [
         # if the block succeeded, touch $out
-        bins.importas "-ui" "out" "out"
-        bins.s6-touch "$out"
+        bins.importas
+        "-ui"
+        "out"
+        "out"
+        bins.s6-touch
+        "$out"
       ];
     preferLocalBuild = true;
     allowSubstitutes = false;
   };
 
   # basic test that touches out
-  basic = runExeclineLocal "run-execline-test-basic" {
-  } [
-      "importas" "-ui" "out" "out"
-      "${bins.s6-touch}" "$out"
+  basic = runExeclineLocal "run-execline-test-basic"
+    { } [
+    "importas"
+    "-ui"
+    "out"
+    "out"
+    "${bins.s6-touch}"
+    "$out"
   ];
 
   # whether the stdin argument works as intended
-  stdin = fileHasLine "foo" (runExeclineLocal "run-execline-test-stdin" {
-    stdin = "foo\nbar\nfoo";
-  } [
-      "importas" "-ui" "out" "out"
-      # this pipes stdout of s6-cat to $out
-      # and s6-cat redirects from stdin to stdout
-      "redirfd" "-w" "1" "$out" bins.s6-cat
+  stdin = fileHasLine "foo" (runExeclineLocal "run-execline-test-stdin"
+    {
+      stdin = "foo\nbar\nfoo";
+    } [
+    "importas"
+    "-ui"
+    "out"
+    "out"
+    # this pipes stdout of s6-cat to $out
+    # and s6-cat redirects from stdin to stdout
+    "redirfd"
+    "-w"
+    "1"
+    "$out"
+    bins.s6-cat
   ]);
 
 
-  wrapWithVar = runExeclineLocal "run-execline-test-wrap-with-var" {
-    builderWrapper = writeScript "var-wrapper" ''
-      #!${bins.execlineb} -S0
-      export myvar myvalue $@
-    '';
-  } [
-    "importas" "-ui" "v" "myvar"
-    "if" [ bins.s6-test "myvalue" "=" "$v" ]
-      "importas" "out" "out"
-      bins.s6-touch "$out"
+  wrapWithVar = runExeclineLocal "run-execline-test-wrap-with-var"
+    {
+      builderWrapper = writeScript "var-wrapper" ''
+        #!${bins.execlineb} -S0
+        export myvar myvalue $@
+      '';
+    } [
+    "importas"
+    "-ui"
+    "v"
+    "myvar"
+    "if"
+    [ bins.s6-test "myvalue" "=" "$v" ]
+    "importas"
+    "out"
+    "out"
+    bins.s6-touch
+    "$out"
   ];
 
-in [
+in
+[
   basic
   stdin
   wrapWithVar
diff --git a/nix/runTestsuite/default.nix b/nix/runTestsuite/default.nix
index 9eb5070996..8b02ed86d8 100644
--- a/nix/runTestsuite/default.nix
+++ b/nix/runTestsuite/default.nix
@@ -38,11 +38,11 @@ let
     ;
 
   bins = depot.nix.getBins pkgs.coreutils [ "printf" ]
-      // depot.nix.getBins pkgs.s6-portable-utils [ "s6-touch" "s6-false" "s6-cat" ];
+    // depot.nix.getBins pkgs.s6-portable-utils [ "s6-touch" "s6-false" "s6-cat" ];
 
   # Returns true if the given expression throws when `deepSeq`-ed
   throws = expr:
-    !(builtins.tryEval (builtins.deepSeq expr {})).success;
+    !(builtins.tryEval (builtins.deepSeq expr { })).success;
 
   # rewrite the builtins.partition result
   # to use `ok` and `err` instead of `right` and `wrong`.
@@ -99,11 +99,12 @@ let
     (context: desc: res:
       if res
       then { yep = { test = desc; }; }
-      else { nope = {
-        test = desc;
-        inherit context;
-      };
-    });
+      else {
+        nope = {
+          test = desc;
+          inherit context;
+        };
+      });
 
   # assert that left and right values are equal
   assertEq = defun [ string any any AssertResult ]
@@ -111,7 +112,7 @@ let
       let
         context = { not-equal = { inherit left right; }; };
       in
-        assertBoolContext context desc (left == right));
+      assertBoolContext context desc (left == right));
 
   # assert that the expression throws when `deepSeq`-ed
   assertThrows = defun [ string any AssertResult ]
@@ -119,7 +120,7 @@ let
       let
         context = { should-throw = { inherit expr; }; };
       in
-        assertBoolContext context desc (throws expr));
+      assertBoolContext context desc (throws expr));
 
   # assert that the expression does not throw when `deepSeq`-ed
   assertDoesNotThrow = defun [ string any AssertResult ]
@@ -144,31 +145,50 @@ let
           yep = _: true;
           nope = _: false;
         };
-        res = partitionTests (it:
-          (partitionTests goodAss it.asserts).err == []
-        ) itResults;
-        prettyRes = lib.generators.toPretty {} res;
+        res = partitionTests
+          (it:
+            (partitionTests goodAss it.asserts).err == [ ]
+          )
+          itResults;
+        prettyRes = lib.generators.toPretty { } res;
       in
-        if res.err == []
-        then depot.nix.runExecline.local "testsuite-${name}-successful" {} [
-          "importas" "out" "out"
+      if res.err == [ ]
+      then
+        depot.nix.runExecline.local "testsuite-${name}-successful" { } [
+          "importas"
+          "out"
+          "out"
           # force derivation to rebuild if test case list changes
-          "ifelse" [ bins.s6-false ] [
-            bins.printf "" (builtins.hashString "sha512" prettyRes)
+          "ifelse"
+          [ bins.s6-false ]
+          [
+            bins.printf
+            ""
+            (builtins.hashString "sha512" prettyRes)
           ]
-          "if" [ bins.printf "%s\n" "testsuite ${name} successful!" ]
-          bins.s6-touch "$out"
+          "if"
+          [ bins.printf "%s\n" "testsuite ${name} successful!" ]
+          bins.s6-touch
+          "$out"
         ]
-        else depot.nix.runExecline.local "testsuite-${name}-failed" {
-          stdin = prettyRes + "\n";
-        } [
-          "importas" "out" "out"
-          "if" [ bins.printf "%s\n" "testsuite ${name} failed!" ]
-          "if" [ bins.s6-cat ]
-          "exit" "1"
+      else
+        depot.nix.runExecline.local "testsuite-${name}-failed"
+          {
+            stdin = prettyRes + "\n";
+          } [
+          "importas"
+          "out"
+          "out"
+          "if"
+          [ bins.printf "%s\n" "testsuite ${name} failed!" ]
+          "if"
+          [ bins.s6-cat ]
+          "exit"
+          "1"
         ]);
 
-in {
+in
+{
   inherit
     assertEq
     assertThrows
diff --git a/nix/sparseTree/OWNERS b/nix/sparseTree/OWNERS
index fdf6d72040..2e95807063 100644
--- a/nix/sparseTree/OWNERS
+++ b/nix/sparseTree/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - sterni
\ No newline at end of file
+sterni
diff --git a/nix/sparseTree/default.nix b/nix/sparseTree/default.nix
index 5184f33d5c..35fa459e1c 100644
--- a/nix/sparseTree/default.nix
+++ b/nix/sparseTree/default.nix
@@ -2,22 +2,33 @@
 # and directories if they are listed in a supplied list:
 #
 # # A very minimal depot
-# sparseTree ./depot [
-#   ./default.nix
-#   ./depot/nix/readTree/default.nix
-#   ./third_party/nixpkgs
-#   ./third_party/overlays
-# ]
+# sparseTree {
+#   root = ./depot;
+#   paths = [
+#     ./default.nix
+#     ./depot/nix/readTree/default.nix
+#     ./third_party/nixpkgs
+#     ./third_party/overlays
+#   ];
+# }
 { pkgs, lib, ... }:
 
-# root path to use as a reference point
-root:
-# list of paths below `root` that should be
-# included in the resulting directory
-#
-# If path, need to refer to the actual file / directory to be included.
-# If a string, it is treated as a string relative to the root.
-paths:
+{
+  # root path to use as a reference point
+  root
+, # list of paths below `root` that should be
+  # included in the resulting directory
+  #
+  # If path, need to refer to the actual file / directory to be included.
+  # If a string, it is treated as a string relative to the root.
+  paths
+, # (optional) name to use for the derivation
+  #
+  # This should always be set when using roots that do not have
+  # controlled names, such as when passing the top-level of a git
+  # repository (e.g. `depot.path.origSrc`).
+  name ? builtins.baseNameOf root
+}:
 
 let
   rootLength = builtins.stringLength (toString root);
@@ -45,14 +56,15 @@ let
     let
       withLeading = p: if builtins.substring 0 1 p == "/" then p else "/" + p;
       fullPath =
-        /**/ if builtins.isPath path then path
+        if builtins.isPath path then path
         else if builtins.isString path then (root + withLeading path)
         else builtins.throw "Unsupported path type ${builtins.typeOf path}";
       strPath = toString fullPath;
       contextPath = "${fullPath}";
       belowRoot = builtins.substring rootLength (-1) strPath;
       prefix = builtins.substring 0 rootLength strPath;
-    in assert toString root == prefix; {
+    in
+    assert toString root == prefix; {
       src = contextPath;
       dst = belowRoot;
     };
@@ -61,10 +73,12 @@ let
 in
 
 # TODO(sterni): teach readTree to also read symlinked directories,
-# so we ln -sT instead of cp -aT.
-pkgs.runCommandNoCC "sparse-${builtins.baseNameOf root}" {} (
-  lib.concatMapStrings ({ src, dst }: ''
-    mkdir -p "$(dirname "$out${dst}")"
-    cp -aT --reflink=auto "${src}" "$out${dst}"
-  '') symlinks
+  # so we ln -sT instead of cp -aT.
+pkgs.runCommand "sparse-${name}" { } (
+  lib.concatMapStrings
+    ({ src, dst }: ''
+      mkdir -p "$(dirname "$out${dst}")"
+      cp -aT --reflink=auto "${src}" "$out${dst}"
+    '')
+    symlinks
 )
diff --git a/nix/stateMonad/default.nix b/nix/stateMonad/default.nix
new file mode 100644
index 0000000000..209412e099
--- /dev/null
+++ b/nix/stateMonad/default.nix
@@ -0,0 +1,76 @@
+# Simple state monad represented as
+#
+#     stateMonad s a = s -> { state : s; value : a }
+#
+{ ... }:
+
+rec {
+  #
+  # Monad
+  #
+
+  # Type: stateMonad s a -> (a -> stateMonad s b) -> stateMonad s b
+  bind = action: f: state:
+    let
+      afterAction = action state;
+    in
+    (f afterAction.value) afterAction.state;
+
+  # Type: stateMonad s a -> stateMonad s b -> stateMonad s b
+  after = action1: action2: state: action2 (action1 state).state;
+
+  # Type: stateMonad s (stateMonad s a) -> stateMonad s a
+  join = action: bind action (action': action');
+
+  # Type: [a] -> (a -> stateMonad s b) -> stateMonad s null
+  for_ = xs: f:
+    builtins.foldl'
+      (laterAction: x:
+        after (f x) laterAction
+      )
+      (pure null)
+      xs;
+
+  #
+  # Applicative
+  #
+
+  # Type: a -> stateMonad s a
+  pure = value: state: { inherit state value; };
+
+  # TODO(sterni): <*>, lift2, …
+
+  #
+  # Functor
+  #
+
+  # Type: (a -> b) -> stateMonad s a -> stateMonad s b
+  fmap = f: action: bind action (result: pure (f result));
+
+  #
+  # State Monad
+  #
+
+  # Type: (s -> s) -> stateMonad s null
+  modify = f: state: { value = null; state = f state; };
+
+  # Type: stateMonad s s
+  get = state: { value = state; inherit state; };
+
+  # Type: s -> stateMonad s null
+  set = new: modify (_: new);
+
+  # Type: str -> stateMonad set set.${str}
+  getAttr = attr: fmap (state: state.${attr}) get;
+
+  # Type: str -> (any -> any) -> stateMonad s null
+  modifyAttr = attr: f: modify (state: state // {
+    ${attr} = f state.${attr};
+  });
+
+  # Type: str -> any -> stateMonad s null
+  setAttr = attr: value: modifyAttr attr (_: value);
+
+  # Type: s -> stateMonad s a -> a
+  run = state: action: (action state).value;
+}
diff --git a/nix/stateMonad/tests/default.nix b/nix/stateMonad/tests/default.nix
new file mode 100644
index 0000000000..c3cb5c99b5
--- /dev/null
+++ b/nix/stateMonad/tests/default.nix
@@ -0,0 +1,110 @@
+{ depot, ... }:
+
+let
+  inherit (depot.nix.runTestsuite)
+    runTestsuite
+    it
+    assertEq
+    ;
+
+  inherit (depot.nix.stateMonad)
+    pure
+    run
+    join
+    fmap
+    bind
+    get
+    set
+    modify
+    after
+    for_
+    getAttr
+    setAttr
+    modifyAttr
+    ;
+
+  runStateIndependent = run (throw "This should never be evaluated!");
+in
+
+runTestsuite "stateMonad" [
+  (it "behaves correctly independent of state" [
+    (assertEq "pure" (runStateIndependent (pure 21)) 21)
+    (assertEq "join pure" (runStateIndependent (join (pure (pure 42)))) 42)
+    (assertEq "fmap pure" (runStateIndependent (fmap (builtins.mul 2) (pure 21))) 42)
+    (assertEq "bind pure" (runStateIndependent (bind (pure 12) (x: pure x))) 12)
+  ])
+  (it "behaves correctly with an integer state" [
+    (assertEq "get" (run 42 get) 42)
+    (assertEq "after set get" (run 21 (after (set 42) get)) 42)
+    (assertEq "after modify get" (run 21 (after (modify (builtins.mul 2)) get)) 42)
+    (assertEq "fmap get" (run 40 (fmap (builtins.add 2) get)) 42)
+    (assertEq "stateful sum list"
+      (run 0 (after
+        (for_
+          [
+            15
+            12
+            10
+            5
+          ]
+          (x: modify (builtins.add x)))
+        get))
+      42)
+  ])
+  (it "behaves correctly with an attr set state" [
+    (assertEq "getAttr" (run { foo = 42; } (getAttr "foo")) 42)
+    (assertEq "after setAttr getAttr"
+      (run { foo = 21; } (after (setAttr "foo" 42) (getAttr "foo")))
+      42)
+    (assertEq "after modifyAttr getAttr"
+      (run { foo = 10.5; }
+        (after
+          (modifyAttr "foo" (builtins.mul 4))
+          (getAttr "foo")))
+      42)
+    (assertEq "fmap getAttr"
+      (run { foo = 21; } (fmap (builtins.mul 2) (getAttr "foo")))
+      42)
+    (assertEq "after setAttr to insert getAttr"
+      (run { } (after (setAttr "foo" 42) (getAttr "foo")))
+      42)
+    (assertEq "insert permutations"
+      (run
+        {
+          a = 2;
+          b = 3;
+          c = 5;
+        }
+        (after
+          (bind get
+            (state:
+              let
+                names = builtins.attrNames state;
+              in
+              for_ names (name1:
+                for_ names (name2:
+                  # this is of course a bit silly, but making it more cumbersome
+                  # makes sure the test exercises more of the code.
+                  (bind (getAttr name1)
+                    (value1:
+                      (bind (getAttr name2)
+                        (value2:
+                          setAttr "${name1}_${name2}" (value1 * value2)))))))))
+          get))
+      {
+        a = 2;
+        b = 3;
+        c = 5;
+        a_a = 4;
+        a_b = 6;
+        a_c = 10;
+        b_a = 6;
+        b_b = 9;
+        b_c = 15;
+        c_c = 25;
+        c_a = 10;
+        c_b = 15;
+      }
+    )
+  ])
+]
diff --git a/nix/tag/default.nix b/nix/tag/default.nix
index 9c55e6263b..2955656323 100644
--- a/nix/tag/default.nix
+++ b/nix/tag/default.nix
@@ -4,22 +4,24 @@ let
   # if so sets `isTag` to `true` and sets the name and value.
   # If not, sets `isTag` to `false` and sets `errmsg`.
   verifyTag = tag:
-    let cases = builtins.attrNames tag;
-        len = builtins.length cases;
+    let
+      cases = builtins.attrNames tag;
+      len = builtins.length cases;
     in
     if builtins.length cases == 1
-    then let name = builtins.head cases; in {
-      isTag = true;
-      name = name;
-      val = tag.${name};
-      errmsg = null;
-    }
+    then
+      let name = builtins.head cases; in {
+        isTag = true;
+        name = name;
+        val = tag.${name};
+        errmsg = null;
+      }
     else {
       isTag = false;
       errmsg =
-        ( "match: an instance of a sum is an attrset "
-        + "with exactly one element, yours had ${toString len}"
-        + ", namely: ${lib.generators.toPretty {} cases}" );
+        ("match: an instance of a sum is an attrset "
+          + "with exactly one element, yours had ${toString len}"
+          + ", namely: ${lib.generators.toPretty {} cases}");
       name = null;
       val = null;
     };
@@ -63,21 +65,22 @@ let
   #   ] 1
   #   => { smol = 1; }
   discrDef = defTag: fs: v:
-    let res = lib.findFirst
-                (t: t.val v)
-                null
-                (map assertIsTag fs);
+    let
+      res = lib.findFirst
+        (t: t.val v)
+        null
+        (map assertIsTag fs);
     in
-      if res == null
-      then { ${defTag} = v; }
-      else { ${res.name} = v; };
+    if res == null
+    then { ${defTag} = v; }
+    else { ${res.name} = v; };
 
   # Like `discrDef`, but fail if there is no match.
   discr = fs: v:
     let res = discrDef null fs v; in
-      assert lib.assertMsg (res != null)
-        "tag.discr: No predicate found that matches ${lib.generators.toPretty {} v}";
-      res;
+    assert lib.assertMsg (res != { })
+      "tag.discr: No predicate found that matches ${lib.generators.toPretty {} v}";
+    res;
 
   # The canonical pattern matching primitive.
   # A sum value is an attribute set with one element,
@@ -104,17 +107,17 @@ let
   match = sum: matcher:
     let cases = builtins.attrNames sum;
     in assert
-      let len = builtins.length cases; in
-        lib.assertMsg (len == 1)
-          ( "match: an instance of a sum is an attrset "
-          + "with exactly one element, yours had ${toString len}"
-          + ", namely: ${lib.generators.toPretty {} cases}" );
+    let len = builtins.length cases; in
+    lib.assertMsg (len == 1)
+      ("match: an instance of a sum is an attrset "
+        + "with exactly one element, yours had ${toString len}"
+        + ", namely: ${lib.generators.toPretty {} cases}");
     let case = builtins.head cases;
     in assert
-        lib.assertMsg (matcher ? ${case})
-        ( "match: \"${case}\" is not a valid case of this sum, "
+    lib.assertMsg (matcher ? ${case})
+      ("match: \"${case}\" is not a valid case of this sum, "
         + "the matcher accepts: ${lib.generators.toPretty {}
-            (builtins.attrNames matcher)}" );
+            (builtins.attrNames matcher)}");
     matcher.${case} sum.${case};
 
   # A `match` with the arguments flipped.
@@ -148,15 +151,16 @@ let
       ;
   };
 
-in {
-   inherit
-     verifyTag
-     tagName
-     tagValue
-     discr
-     discrDef
-     match
-     matchLam
-     tests
-     ;
+in
+{
+  inherit
+    verifyTag
+    tagName
+    tagValue
+    discr
+    discrDef
+    match
+    matchLam
+    tests
+    ;
 }
diff --git a/nix/tag/tests.nix b/nix/tag/tests.nix
index 8c9c738074..e0085b4837 100644
--- a/nix/tag/tests.nix
+++ b/nix/tag/tests.nix
@@ -4,6 +4,7 @@ let
   inherit (depot.nix.runTestsuite)
     runTestsuite
     assertEq
+    assertThrows
     it
     ;
 
@@ -17,7 +18,7 @@ let
         errmsg = null;
       })
     (assertEq "is not Tag"
-      (removeAttrs (verifyTag { foo = "bar"; baz = 42; }) ["errmsg"])
+      (removeAttrs (verifyTag { foo = "bar"; baz = 42; }) [ "errmsg" ])
       {
         isTag = false;
         name = null;
@@ -41,7 +42,8 @@ let
       (discr [
         { bool = lib.isBool; }
         { int = lib.isInt; }
-      ] true)
+      ]
+        true)
       { bool = true; })
     (assertEq "fallback to default"
       (discrDef "def" [
@@ -49,23 +51,32 @@ let
         { int = lib.isInt; }
       ] "foo")
       { def = "foo"; })
+    (assertThrows "throws failing to match"
+      (discr [
+        { fish = x: x == 42; }
+      ] 21))
   ];
 
   match-test = it "can match things" [
     (assertEq "match example"
-      (let
-        success = { res = 42; };
-        failure = { err = "no answer"; };
-        matcher = {
-          res = i: i + 1;
-          err = _: 0;
-        };
-      in {
-        one = match success matcher;
-        two = match failure matcher;
+      (
+        let
+          success = { res = 42; };
+          failure = { err = "no answer"; };
+          matcher = {
+            res = i: i + 1;
+            err = _: 0;
+          };
+        in
+        {
+          one = match success matcher;
+          two = match failure matcher;
+        }
+      )
+      {
+        one = 43;
+        two = 0;
       })
-      { one = 43;
-        two = 0; })
     (assertEq "matchLam & pipe"
       (lib.pipe { foo = 42; } [
         (matchLam {
@@ -81,8 +92,8 @@ let
   ];
 
 in
-  runTestsuite "tag" [
-    isTag-test
-    discr-test
-    match-test
-  ]
+runTestsuite "tag" [
+  isTag-test
+  discr-test
+  match-test
+]
diff --git a/nix/tailscale/default.nix b/nix/tailscale/default.nix
index 8d6a0f661b..363f717db6 100644
--- a/nix/tailscale/default.nix
+++ b/nix/tailscale/default.nix
@@ -27,4 +27,5 @@ let
     # Actual ACL entries
     ACLs = list acl;
   };
-in config: pkgs.writeText "tailscale-acl.json" (toJSON (aclConfig config))
+in
+config: pkgs.writeText "tailscale-acl.json" (toJSON (aclConfig config))
diff --git a/nix/utils/OWNERS b/nix/utils/OWNERS
index f16dd105d7..2e95807063 100644
--- a/nix/utils/OWNERS
+++ b/nix/utils/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - sterni
+sterni
diff --git a/nix/utils/default.nix b/nix/utils/default.nix
index 258e372a2a..0c6c88fafd 100644
--- a/nix/utils/default.nix
+++ b/nix/utils/default.nix
@@ -34,14 +34,14 @@ let
         basename = builtins.unsafeDiscardStringContext
           (builtins.baseNameOf strPath);
       in
-        # If p is a direct child of storeDir, we need to remove
+      # If p is a direct child of storeDir, we need to remove
         # the leading hash as well to make sure that:
         # `storePathName drv == storePathName (toString drv)`.
-        if noStoreDir == basename
-        then builtins.substring 33 (-1) basename
-        else basename
+      if noStoreDir == basename
+      then builtins.substring 33 (-1) basename
+      else basename
     else builtins.throw "Don't know how to get (base)name of "
-      + lib.generators.toPretty {} p;
+      + lib.generators.toPretty { } p;
 
   /* Query the type of a path exposing the same information as would be by
      `builtins.readDir`, but for a single, specific target path.
@@ -53,13 +53,7 @@ let
      * `regular`: is a regular file, always `true` if returned
      * `directory`: is a directory, always `true` if returned
      * `missing`: path does not exist, always `true` if returned
-     * `symlink`: path is a symlink, value is a string describing the type
-       of its realpath which may be either:
-
-       * `"directory"`: realpath of the symlink is a directory
-       * `"regular-or-missing`": realpath of the symlink is either a regular
-         file or does not exist. Due to limitations of the Nix expression
-         language, we can't tell which.
+     * `symlink`: path is a symlink, always `true` if returned
 
      Type: path(-like) -> tag
 
@@ -73,10 +67,10 @@ let
        => { directory = true; }
 
        pathType ./result
-       => { symlink = "directory"; }
+       => { symlink = true; }
 
        pathType ./link-to-file
-       => { symlink = "regular-or-missing"; }
+       => { symlink = true; }
 
        pathType /does/not/exist
        => { missing = true; }
@@ -90,12 +84,12 @@ let
 
        # Match on the result using //nix/tag
        nix.tag.match (nix.utils.pathType ./result) {
-         symlink = v: "symlink to ${v}";
+         symlink = _: "symlink";
          directory  = _: "directory";
          regular = _: "regular";
          missing = _: "path does not exist";
        }
-       => "symlink to directory"
+       => "symlink"
 
        # Query path type
        nix.tag.tagName (pathType /path)
@@ -106,7 +100,7 @@ let
       # We need to call toString to prevent unsafeDiscardStringContext
       # from importing a path into store which messes with base- and
       # dirname of course.
-      path'= builtins.unsafeDiscardStringContext (toString path);
+      path' = builtins.unsafeDiscardStringContext (toString path);
       # To read the containing directory we absolutely need
       # to keep the string context, otherwise a derivation
       # would not be realized before our check (at eval time)
@@ -120,20 +114,18 @@ let
       # directory. If not, either the target doesn't exist or is a regular file.
       # TODO(sterni): is there a way to check reliably if the symlink target exists?
       isSymlinkDir = builtins.pathExists (path' + "/.");
-    in {
-      ${thisPathType} =
-        /**/ if thisPathType != "symlink" then true
-        else if isSymlinkDir              then "directory"
-        else                                   "regular-or-missing";
+    in
+    {
+      ${thisPathType} = true;
     };
 
   pathType' = path:
     let
       p = pathType path;
     in
-      if p ? missing
-      then builtins.throw "${lib.generators.toPretty {} path} does not exist"
-      else p;
+    if p ? missing
+    then builtins.throw "${lib.generators.toPretty {} path} does not exist"
+    else p;
 
   /* Check whether the given path is a directory.
      Throws if the path in question doesn't exist.
@@ -142,19 +134,6 @@ let
   */
   isDirectory = path: pathType' path ? directory;
 
-  /* Checks whether the given path is a directory or
-     a symlink to a directory. Throws if the path in
-     question doesn't exist.
-
-     Warning: Does not throw if the target file or
-     directory doesn't exist, but the symlink does.
-
-     Type: path(-like) -> bool
-  */
-  realPathIsDirectory = path: let
-    pt = pathType' path;
-  in pt ? directory || pt.symlink or null == "directory";
-
   /* Check whether the given path is a regular file.
      Throws if the path in question doesn't exist.
 
@@ -169,12 +148,12 @@ let
   */
   isSymlink = path: pathType' path ? symlink;
 
-in {
+in
+{
   inherit
     storePathName
     pathType
     isDirectory
-    realPathIsDirectory
     isRegularFile
     isSymlink
     ;
diff --git a/nix/utils/tests/default.nix b/nix/utils/tests/default.nix
index 8a078684f3..344a1771d7 100644
--- a/nix/utils/tests/default.nix
+++ b/nix/utils/tests/default.nix
@@ -11,7 +11,6 @@ let
 
   inherit (depot.nix.utils)
     isDirectory
-    realPathIsDirectory
     isRegularFile
     isSymlink
     pathType
@@ -26,62 +25,52 @@ let
   pathPredicates = it "judges paths correctly" (lib.flatten [
     # isDirectory
     (assertUtilsPred "directory isDirectory"
-      (isDirectory ./directory) true)
+      (isDirectory ./directory)
+      true)
     (assertUtilsPred "symlink not isDirectory"
-      (isDirectory ./symlink-directory) false)
+      (isDirectory ./symlink-directory)
+      false)
     (assertUtilsPred "file not isDirectory"
-      (isDirectory ./directory/file) false)
-    # realPathIsDirectory
-    (assertUtilsPred "directory realPathIsDirectory"
-      (realPathIsDirectory ./directory) true)
-    (assertUtilsPred "symlink to directory realPathIsDirectory"
-      (realPathIsDirectory ./symlink-directory) true)
-    (assertUtilsPred "realPathIsDirectory resolves chained symlinks"
-      (realPathIsDirectory ./symlink-symlink-directory) true)
+      (isDirectory ./directory/file)
+      false)
     # isRegularFile
     (assertUtilsPred "file isRegularFile"
-      (isRegularFile ./directory/file) true)
+      (isRegularFile ./directory/file)
+      true)
     (assertUtilsPred "symlink not isRegularFile"
-      (isRegularFile ./symlink-file) false)
+      (isRegularFile ./symlink-file)
+      false)
     (assertUtilsPred "directory not isRegularFile"
-      (isRegularFile ./directory) false)
+      (isRegularFile ./directory)
+      false)
     # isSymlink
     (assertUtilsPred "symlink to file isSymlink"
-      (isSymlink ./symlink-file) true)
+      (isSymlink ./symlink-file)
+      true)
     (assertUtilsPred "symlink to directory isSymlink"
-      (isSymlink ./symlink-directory) true)
+      (isSymlink ./symlink-directory)
+      true)
     (assertUtilsPred "symlink to symlink isSymlink"
-      (isSymlink ./symlink-symlink-file) true)
+      (isSymlink ./symlink-symlink-file)
+      true)
     (assertUtilsPred "symlink to missing file isSymlink"
-      (isSymlink ./missing) true)
+      (isSymlink ./missing)
+      true)
     (assertUtilsPred "directory not isSymlink"
-      (isSymlink ./directory) false)
+      (isSymlink ./directory)
+      false)
     (assertUtilsPred "file not isSymlink"
-      (isSymlink ./directory/file) false)
+      (isSymlink ./directory/file)
+      false)
     # missing files throw
     (assertThrows "isDirectory throws on missing file"
       (isDirectory ./does-not-exist))
-    (assertThrows "realPathIsDirectory throws on missing file"
-      (realPathIsDirectory ./does-not-exist))
     (assertThrows "isRegularFile throws on missing file"
       (isRegularFile ./does-not-exist))
     (assertThrows "isSymlink throws on missing file"
       (isSymlink ./does-not-exist))
   ]);
 
-  symlinkPathTypeTests = it "correctly judges symlinks" [
-    (assertEq "symlinks to directories are detected correcty"
-      ((pathType ./symlink-directory).symlink or null) "directory")
-    (assertEq "symlinks to symlinks to directories are detected correctly"
-      ((pathType ./symlink-symlink-directory).symlink or null) "directory")
-    (assertEq "symlinks to files are detected-ish"
-      ((pathType ./symlink-file).symlink or null) "regular-or-missing")
-    (assertEq "symlinks to symlinks to files are detected-ish"
-      ((pathType ./symlink-symlink-file).symlink or null) "regular-or-missing")
-    (assertEq "symlinks to nowhere are not distinguished from files"
-      ((pathType ./missing).symlink or null) "regular-or-missing")
-  ];
-
   cheddarStorePath =
     builtins.unsafeDiscardStringContext depot.tools.cheddar.outPath;
 
@@ -89,20 +78,22 @@ let
 
   storePathNameTests = it "correctly gets the basename of a store path" [
     (assertEq "base name of a derivation"
-      (storePathName depot.tools.cheddar) depot.tools.cheddar.name)
+      (storePathName depot.tools.cheddar)
+      depot.tools.cheddar.name)
     (assertEq "base name of a store path string"
-      (storePathName cheddarStorePath) depot.tools.cheddar.name)
+      (storePathName cheddarStorePath)
+      depot.tools.cheddar.name)
     (assertEq "base name of a path within a store path"
       (storePathName "${cheddarStorePath}/bin/cheddar") "cheddar")
     (assertEq "base name of a path"
       (storePathName ../default.nix) "default.nix")
     (assertEq "base name of a cleanSourced path"
-      (storePathName cleanedSource) cleanedSource.name)
+      (storePathName cleanedSource)
+      cleanedSource.name)
   ];
 in
 
 runTestsuite "nix.utils" [
   pathPredicates
-  symlinkPathTypeTests
   storePathNameTests
 ]
diff --git a/nix/writeElispBin/default.nix b/nix/writeElispBin/default.nix
index c116607e0a..ee3dc7a3ed 100644
--- a/nix/writeElispBin/default.nix
+++ b/nix/writeElispBin/default.nix
@@ -1,18 +1,20 @@
 { depot, pkgs, ... }:
 
-{ name, src, deps ? (_: []), emacs ? pkgs.emacs27-nox }:
+{ name, src, deps ? (_: [ ]), emacs ? pkgs.emacs28-nox }:
 
 let
-  inherit (pkgs) emacsPackages emacsPackagesGen;
+  inherit (pkgs) emacsPackages emacsPackagesFor;
   inherit (builtins) isString toFile;
 
-  finalEmacs = (emacsPackagesGen emacs).emacsWithPackages deps;
+  finalEmacs = (emacsPackagesFor emacs).emacsWithPackages deps;
 
-  srcFile = if isString src
+  srcFile =
+    if isString src
     then toFile "${name}.el" src
     else src;
 
-in depot.nix.writeScriptBin name ''
+in
+depot.nix.writeScriptBin name ''
   #!/bin/sh
   ${finalEmacs}/bin/emacs --batch --no-site-file --script ${srcFile} $@
 ''
diff --git a/nix/writeExecline/default.nix b/nix/writeExecline/default.nix
index 8626aa4608..5169b01386 100644
--- a/nix/writeExecline/default.nix
+++ b/nix/writeExecline/default.nix
@@ -14,9 +14,10 @@ name:
   # "env": don’t substitute, set # and 0…n environment vaariables, where n=$#
   # "none": don’t substitute or set any positional arguments
   # "env-no-push": like "env", but bypass the push-phase. Not recommended.
-  argMode ? "var",
-  # Number of arguments to be substituted as variables (passed to "var"/"-s" or "var-full"/"-S"
-  readNArgs ? 0,
+  argMode ? "var"
+, # Number of arguments to be substituted as variables (passed to "var"/"-s" or "var-full"/"-S"
+  readNArgs ? 0
+,
 }:
 # Nested list of lists of commands.
 # Inner lists are translated to execline blocks.
@@ -24,7 +25,7 @@ argList:
 
 let
   env =
-    if      argMode == "var" then "s${toString readNArgs}"
+    if argMode == "var" then "s${toString readNArgs}"
     else if argMode == "var-full" then "S${toString readNArgs}"
     else if argMode == "env" then ""
     else if argMode == "none" then "P"
@@ -32,7 +33,7 @@ let
     else abort ''"${toString argMode}" is not a valid argMode, use one of "var", "var-full", "env", "none", "env-no-push".'';
 
 in
-  depot.nix.writeScript name ''
-    #!${pkgs.execline}/bin/execlineb -W${env}
-    ${depot.nix.escapeExecline argList}
-  ''
+depot.nix.writeScript name ''
+  #!${pkgs.execline}/bin/execlineb -W${env}
+  ${depot.nix.escapeExecline argList}
+''
diff --git a/nix/writeScript/default.nix b/nix/writeScript/default.nix
index e8e6e0fa10..1f53b4e4ff 100644
--- a/nix/writeScript/default.nix
+++ b/nix/writeScript/default.nix
@@ -5,25 +5,31 @@
 
 let
   bins = depot.nix.getBins pkgs.s6-portable-utils [
-           "s6-cat"
-           "s6-chmod"
-         ];
+    "s6-cat"
+    "s6-chmod"
+  ];
 
 in
 name:
 # string of the executable script that is put in $out
 script:
 
-depot.nix.runExecline name {
+depot.nix.runExecline name
+{
   stdin = script;
   derivationArgs = {
     preferLocalBuild = true;
     allowSubstitutes = false;
   };
 } [
-  "importas" "out" "out"
+  "importas"
+  "out"
+  "out"
   # this pipes stdout of s6-cat to $out
   # and s6-cat redirects from stdin to stdout
-  "if" [ "redirfd" "-w" "1" "$out" bins.s6-cat ]
-  bins.s6-chmod "0755" "$out"
+  "if"
+  [ "redirfd" "-w" "1" "$out" bins.s6-cat ]
+  bins.s6-chmod
+  "0755"
+  "$out"
 ]
diff --git a/nix/writeTree/OWNERS b/nix/writeTree/OWNERS
new file mode 100644
index 0000000000..b381c4e660
--- /dev/null
+++ b/nix/writeTree/OWNERS
@@ -0,0 +1 @@
+aspen
diff --git a/nix/writeTree/default.nix b/nix/writeTree/default.nix
new file mode 100644
index 0000000000..0c7c2a130f
--- /dev/null
+++ b/nix/writeTree/default.nix
@@ -0,0 +1,43 @@
+{ depot, lib, pkgs, ... }:
+let
+  inherit (lib) fix pipe mapAttrsToList isAttrs concatLines isString isDerivation isPath;
+
+  # TODO(sterni): move to //nix/utils with clearer naming and alternative similar to lib.types.path
+  isPathLike = value:
+    isPath value
+    || isDerivation value
+    || (isString value && builtins.hasContext value);
+
+  esc = s: lib.escapeShellArg /* ensure paths import into store */ "${s}";
+
+  writeTreeAtPath = path: tree:
+    ''
+      mkdir -p "$out/"${esc path}
+    ''
+    + pipe tree [
+      (mapAttrsToList (k: v:
+        if isPathLike v then
+          "cp -R --reflink=auto ${v} \"$out/\"${esc path}/${esc k}"
+        else if lib.isAttrs v then
+          writeTreeAtPath (path + "/" + k) v
+        else
+          throw "invalid type (expected path, derivation, string with context, or attrs)"))
+      concatLines
+    ];
+
+  /* Create a directory tree specified by a Nix attribute set structure.
+
+     Each value in `tree` should either be a file, a directory, or another tree
+     attribute set. Those paths will be written to a directory tree
+     corresponding to the structure of the attribute set.
+
+     Type: string -> attrSet -> derivation
+  */
+  writeTree = name: tree:
+    pkgs.runCommandLocal name { } (writeTreeAtPath "" tree);
+in
+
+# __functor trick so readTree can add the tests attribute
+{
+  __functor = _: writeTree;
+}
diff --git a/nix/writeTree/tests/default.nix b/nix/writeTree/tests/default.nix
new file mode 100644
index 0000000000..c5858ee96e
--- /dev/null
+++ b/nix/writeTree/tests/default.nix
@@ -0,0 +1,93 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  inherit (pkgs) runCommand writeText writeTextFile;
+  inherit (depot.nix) writeTree;
+
+  checkTree = name: tree: expected:
+    runCommand "writeTree-test-${name}"
+      {
+        nativeBuildInputs = [ pkgs.buildPackages.lr ];
+        passAsFile = [ "expected" ];
+        inherit expected;
+      } ''
+      actualPath="$NIX_BUILD_TOP/actual"
+      cd ${lib.escapeShellArg (writeTree name tree)}
+      lr . > "$actualPath"
+      diff -u "$expectedPath" "$actualPath" | tee "$out"
+    '';
+in
+
+depot.nix.readTree.drvTargets {
+  empty = checkTree "empty" { }
+    ''
+      .
+    '';
+
+  simple-paths = checkTree "simple"
+    {
+      writeTree = {
+        meta = {
+          "owners.txt" = ../OWNERS;
+        };
+        "code.nix" = ../default.nix;
+        all-tests = ./.;
+        nested.dirs.eval-time = builtins.toFile "owothia" ''
+          hold me owo
+        '';
+      };
+    }
+    ''
+      .
+      ./writeTree
+      ./writeTree/all-tests
+      ./writeTree/all-tests/default.nix
+      ./writeTree/code.nix
+      ./writeTree/meta
+      ./writeTree/meta/owners.txt
+      ./writeTree/nested
+      ./writeTree/nested/dirs
+      ./writeTree/nested/dirs/eval-time
+    '';
+
+  empty-dirs = checkTree "empty-dirs"
+    {
+      this.dir.is.empty = { };
+      so.is.this.one = { };
+    }
+    ''
+      .
+      ./so
+      ./so/is
+      ./so/is/this
+      ./so/is/this/one
+      ./this
+      ./this/dir
+      ./this/dir/is
+      ./this/dir/is/empty
+    '';
+
+  drvs = checkTree "drvs"
+    {
+      file-drv = writeText "road.txt" ''
+        Any road followed precisely to its end leads precisely nowhere.
+      '';
+      dir-drv = writeTextFile {
+        name = "dir-of-text";
+        destination = "/text/in/more/dirs.txt";
+        text = ''
+          Climb the mountain just a little bit to test that it’s a mountain.
+          From the top of the mountain, you cannot see the mountain.
+        '';
+      };
+    }
+    ''
+      .
+      ./dir-drv
+      ./dir-drv/text
+      ./dir-drv/text/in
+      ./dir-drv/text/in/more
+      ./dir-drv/text/in/more/dirs.txt
+      ./file-drv
+    '';
+}
diff --git a/nix/writers/default.nix b/nix/writers/default.nix
index b0795ab2c8..55355913a9 100644
--- a/nix/writers/default.nix
+++ b/nix/writers/default.nix
@@ -2,62 +2,71 @@
 
 let
   bins = depot.nix.getBins pkgs.s6-portable-utils [ "s6-ln" "s6-ls" "s6-touch" ]
-    ;
+  ;
 
-  linkTo = name: path: depot.nix.runExecline.local name {} [
-    "importas" "out" "out"
-    bins.s6-ln "-s" path "$out"
+  linkTo = name: path: depot.nix.runExecline.local name { } [
+    "importas"
+    "out"
+    "out"
+    bins.s6-ln
+    "-s"
+    path
+    "$out"
   ];
 
   # Build a rust executable, $out is the executable.
-  rustSimple = args@{name, ...}: src:
+  rustSimple = args@{ name, ... }: src:
     linkTo name "${rustSimpleBin args src}/bin/${name}";
 
   # Like `rustSimple`, but put the binary in `$out/bin/`.
-  rustSimpleBin = {
-    name,
-    dependencies ? [],
-    doCheck ? true,
-  }: src:
+  rustSimpleBin =
+    { name
+    , dependencies ? [ ]
+    , doCheck ? true
+    ,
+    }: src:
     (if doCheck then testRustSimple else pkgs.lib.id)
-    (pkgs.buildRustCrate ({
-      pname = name;
-      version = "1.0.0";
-      crateName = name;
-      crateBin = [ name ];
-      dependencies = dependencies;
-      src = pkgs.runCommandLocal "write-main.rs" {
-        src = src;
-        passAsFile = [ "src" ];
-      } ''
-        mkdir -p $out/src/bin
-        cp "$srcPath" $out/src/bin/${name}.rs
-        find $out
-      '';
-    }));
+      (pkgs.buildRustCrate ({
+        pname = name;
+        version = "1.0.0";
+        crateName = name;
+        crateBin = [ name ];
+        dependencies = dependencies;
+        src = pkgs.runCommandLocal "write-main.rs"
+          {
+            src = src;
+            passAsFile = [ "src" ];
+          } ''
+          mkdir -p $out/src/bin
+          cp "$srcPath" $out/src/bin/${name}.rs
+          find $out
+        '';
+      }));
 
   # Build a rust library, that can be used as dependency to `rustSimple`.
   # Wrapper around `pkgs.buildRustCrate`, takes all its arguments.
-  rustSimpleLib = {
-    name,
-    dependencies ? [],
-    doCheck ? true,
-  }: src:
+  rustSimpleLib =
+    { name
+    , dependencies ? [ ]
+    , doCheck ? true
+    ,
+    }: src:
     (if doCheck then testRustSimple else pkgs.lib.id)
-    (pkgs.buildRustCrate ({
-      pname = name;
-      version = "1.0.0";
-      crateName = name;
-      dependencies = dependencies;
-      src = pkgs.runCommandLocal "write-lib.rs" {
-        src = src;
-        passAsFile = [ "src" ];
-      } ''
-        mkdir -p $out/src
-        cp "$srcPath" $out/src/lib.rs
-        find $out
-      '';
-    }));
+      (pkgs.buildRustCrate ({
+        pname = name;
+        version = "1.0.0";
+        crateName = name;
+        dependencies = dependencies;
+        src = pkgs.runCommandLocal "write-lib.rs"
+          {
+            src = src;
+            passAsFile = [ "src" ];
+          } ''
+          mkdir -p $out/src
+          cp "$srcPath" $out/src/lib.rs
+          find $out
+        '';
+      }));
 
   /* Takes a `buildRustCrate` derivation as an input,
     * builds it with `{ buildTests = true; }` and runs
@@ -72,19 +81,30 @@ let
   testRustSimple = rustDrv:
     let
       crate = buildTests: rustDrv.override { inherit buildTests; };
-      tests = depot.nix.runExecline.local "${rustDrv.name}-tests-run" {} [
-        "importas" "out" "out"
-        "if" [
-          "pipeline" [ bins.s6-ls "${crate true}/tests" ]
-          "forstdin" "-o0" "test"
-          "importas" "test" "test"
+      tests = depot.nix.runExecline.local "${rustDrv.name}-tests-run" { } [
+        "importas"
+        "out"
+        "out"
+        "if"
+        [
+          "pipeline"
+          [ bins.s6-ls "${crate true}/tests" ]
+          "forstdin"
+          "-o0"
+          "test"
+          "importas"
+          "test"
+          "test"
           "${crate true}/tests/$test"
         ]
-        bins.s6-touch "$out"
+        bins.s6-touch
+        "$out"
       ];
-    in depot.nix.drvSeqL [ tests ] (crate false);
+    in
+    depot.nix.drvSeqL [ tests ] (crate false);
 
-in {
+in
+{
   inherit
     rustSimple
     rustSimpleBin
diff --git a/nix/writers/tests/rust.nix b/nix/writers/tests/rust.nix
index 8a12c95ec7..232a2dc608 100644
--- a/nix/writers/tests/rust.nix
+++ b/nix/writers/tests/rust.nix
@@ -11,15 +11,20 @@ let
     coreutils
     ;
 
-  run = drv: depot.nix.runExecline.local "run-${drv.name}" {} [
-    "if" [ drv ]
-    "importas" "out" "out"
-    "${coreutils}/bin/touch" "$out"
+  run = drv: depot.nix.runExecline.local "run-${drv.name}" { } [
+    "if"
+    [ drv ]
+    "importas"
+    "out"
+    "out"
+    "${coreutils}/bin/touch"
+    "$out"
   ];
 
-  rustTransitiveLib = rustSimpleLib {
-    name = "transitive";
-  } ''
+  rustTransitiveLib = rustSimpleLib
+    {
+      name = "transitive";
+    } ''
     pub fn transitive(s: &str) -> String {
       let mut new = s.to_string();
       new.push_str(" 1 2 3");
@@ -37,10 +42,11 @@ let
     }
   '';
 
-  rustTestLib = rustSimpleLib {
-    name = "test_lib";
-    dependencies = [ rustTransitiveLib ];
-  } ''
+  rustTestLib = rustSimpleLib
+    {
+      name = "test_lib";
+      dependencies = [ rustTransitiveLib ];
+    } ''
     extern crate transitive;
     use transitive::{transitive};
     pub fn test() -> String {
@@ -48,10 +54,11 @@ let
     }
   '';
 
-  rustWithLib = run (rustSimple {
-    name = "rust-with-lib";
-    dependencies = [ rustTestLib ];
-  } ''
+  rustWithLib = run (rustSimple
+    {
+      name = "rust-with-lib";
+      dependencies = [ rustTestLib ];
+    } ''
     extern crate test_lib;
 
     fn main() {
@@ -60,7 +67,8 @@ let
   '');
 
 
-in depot.nix.readTree.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   inherit
     rustTransitiveLib
     rustWithLib
diff --git a/nix/yants/README.md b/nix/yants/README.md
index d17ea61b52..98e6642e2d 100644
--- a/nix/yants/README.md
+++ b/nix/yants/README.md
@@ -44,9 +44,9 @@ Currently lacking:
 Yants can be imported from its `default.nix`. A single attribute (`lib`) can be
 passed, which will otherwise be imported from `<nixpkgs>`.
 
-TIP: You do not need to clone my whole repository to use Yants! It is split out
-into the `nix/yants` branch which you can clone with, for example, `git clone -b
-nix/yants https://git.tazj.in yants`.
+TIP: You do not need to clone the entire TVL repository to use Yants!
+You can clone just this project through josh: `git clone
+https://code.tvl.fyi/depot.git:/nix/yants.git`
 
 Examples for the most common import methods would be:
 
diff --git a/nix/yants/default.nix b/nix/yants/default.nix
index 2bbf4dd15a..cb9fc08287 100644
--- a/nix/yants/default.nix
+++ b/nix/yants/default.nix
@@ -6,10 +6,10 @@
 #
 # All types (should) compose as expected.
 
-{ lib ?  (import <nixpkgs> {}).lib, ... }:
+{ lib ? (import <nixpkgs> { }).lib, ... }:
 
 with builtins; let
-  prettyPrint = lib.generators.toPretty {};
+  prettyPrint = lib.generators.toPretty { };
 
   # typedef' :: struct {
   #   name = string;
@@ -34,41 +34,44 @@ with builtins; let
   #
   # This function is the low-level primitive used to create types. For
   # many cases the higher-level 'typedef' function is more appropriate.
-  typedef' = { name, checkType
-             , checkToBool ? (result: result.ok)
-             , toError ? (_: result: result.err)
-             , def ? null
-             , match ? null }: {
-    inherit name checkToBool toError;
-
-    # check :: a -> bool
-    #
-    # This function is used to determine whether a given type is
-    # conformant.
-    check = value: checkToBool (checkType value);
-
-    # checkType :: a -> struct { ok = bool; err = option string; }
-    #
-    # This function checks whether the passed value is type conformant
-    # and returns an optional type error string otherwise.
-    inherit checkType;
-
-    # __functor :: a -> a
-    #
-    # This function checks whether the passed value is type conformant
-    # and throws an error if it is not.
-    #
-    # The name of this function is a special attribute in Nix that
-    # makes it possible to execute a type attribute set like a normal
-    # function.
-    __functor = self: value:
-    let result = self.checkType value;
-    in if checkToBool result then value
-       else throw (toError value result);
-  };
+  typedef' =
+    { name
+    , checkType
+    , checkToBool ? (result: result.ok)
+    , toError ? (_: result: result.err)
+    , def ? null
+    , match ? null
+    }: {
+      inherit name checkToBool toError;
+
+      # check :: a -> bool
+      #
+      # This function is used to determine whether a given type is
+      # conformant.
+      check = value: checkToBool (checkType value);
+
+      # checkType :: a -> struct { ok = bool; err = option string; }
+      #
+      # This function checks whether the passed value is type conformant
+      # and returns an optional type error string otherwise.
+      inherit checkType;
+
+      # __functor :: a -> a
+      #
+      # This function checks whether the passed value is type conformant
+      # and throws an error if it is not.
+      #
+      # The name of this function is a special attribute in Nix that
+      # makes it possible to execute a type attribute set like a normal
+      # function.
+      __functor = self: value:
+        let result = self.checkType value;
+        in if checkToBool result then value
+        else throw (toError value result);
+    };
 
   typeError = type: val:
-  "expected type '${type}', but value '${prettyPrint val}' is of type '${typeOf val}'";
+    "expected type '${type}', but value '${prettyPrint val}' is of type '${typeOf val}'";
 
   # typedef :: string -> (a -> bool) -> type
   #
@@ -85,27 +88,34 @@ with builtins; let
       });
   };
 
-  checkEach = name: t: l: foldl' (acc: e:
-    let res = t.checkType e;
+  checkEach = name: t: l: foldl'
+    (acc: e:
+      let
+        res = t.checkType e;
         isT = t.checkToBool res;
-    in {
-      ok = acc.ok && isT;
-      err = if isT
-        then acc.err
-        else acc.err + "${prettyPrint e}: ${t.toError e res}\n";
-    }) { ok = true; err = "expected type ${name}, but found:\n"; } l;
-in lib.fix (self: {
+      in
+      {
+        ok = acc.ok && isT;
+        err =
+          if isT
+          then acc.err
+          else acc.err + "${prettyPrint e}: ${t.toError e res}\n";
+      })
+    { ok = true; err = "expected type ${name}, but found:\n"; }
+    l;
+in
+lib.fix (self: {
   # Primitive types
-  any      = typedef "any" (_: true);
-  unit     = typedef "unit" (v: v == {});
-  int      = typedef "int" isInt;
-  bool     = typedef "bool" isBool;
-  float    = typedef "float" isFloat;
-  string   = typedef "string" isString;
-  path     = typedef "path" (x: typeOf x == "path");
-  drv      = typedef "derivation" (x: isAttrs x && x ? "type" && x.type == "derivation");
+  any = typedef "any" (_: true);
+  unit = typedef "unit" (v: v == { });
+  int = typedef "int" isInt;
+  bool = typedef "bool" isBool;
+  float = typedef "float" isFloat;
+  string = typedef "string" isString;
+  path = typedef "path" (x: typeOf x == "path");
+  drv = typedef "derivation" (x: isAttrs x && x ? "type" && x.type == "derivation");
   function = typedef "function" (x: isFunction x || (isAttrs x && x ? "__functor"
-                                                 && isFunction x.__functor));
+    && isFunction x.__functor));
 
   # Type for types themselves. Useful when defining polymorphic types.
   type = typedef "type" (x:
@@ -124,7 +134,7 @@ in lib.fix (self: {
       in {
         ok = isNull v || (self.type t).checkToBool res;
         err = "expected type ${name}, but value does not conform to '${t.name}': "
-         + t.toError v res;
+          + t.toError v res;
       };
   };
 
@@ -136,7 +146,8 @@ in lib.fix (self: {
   list = t: typedef' rec {
     name = "list<${t.name}>";
 
-    checkType = v: if isList v
+    checkType = v:
+      if isList v
       then checkEach name (self.type t) v
       else {
         ok = false;
@@ -147,7 +158,8 @@ in lib.fix (self: {
   attrs = t: typedef' rec {
     name = "attrs<${t.name}>";
 
-    checkType = v: if isAttrs v
+    checkType = v:
+      if isAttrs v
       then checkEach name (self.type t) (attrValues v)
       else {
         ok = false;
@@ -172,20 +184,23 @@ in lib.fix (self: {
       # checkField checks an individual field of the struct against
       # its definition and creates a typecheck result. These results
       # are aggregated during the actual checking.
-      checkField = def: name: value: let result = def.checkType value; in rec {
-        ok = def.checkToBool result;
-        err = if !ok && isNull value
-          then "missing required ${def.name} field '${name}'\n"
-          else "field '${name}': ${def.toError value result}\n";
-      };
+      checkField = def: name: value:
+        let result = def.checkType value; in rec {
+          ok = def.checkToBool result;
+          err =
+            if !ok && isNull value
+            then "missing required ${def.name} field '${name}'\n"
+            else "field '${name}': ${def.toError value result}\n";
+        };
 
       # checkExtraneous determines whether a (closed) struct contains
       # any fields that are not part of the definition.
       checkExtraneous = def: has: acc:
         if (length has) == 0 then acc
         else if (hasAttr (head has) def)
-          then checkExtraneous def (tail has) acc
-          else checkExtraneous def (tail has) {
+        then checkExtraneous def (tail has) acc
+        else
+          checkExtraneous def (tail has) {
             ok = false;
             err = acc.err + "unexpected struct field '${head has}'\n";
           };
@@ -197,85 +212,102 @@ in lib.fix (self: {
           init = { ok = true; err = ""; };
           extraneous = checkExtraneous def (attrNames value) init;
 
-          checkedFields = map (n:
-            let v = if hasAttr n value then value."${n}" else null;
-            in checkField def."${n}" n v) (attrNames def);
-
-          combined = foldl' (acc: res: {
-            ok = acc.ok && res.ok;
-            err = if !res.ok then acc.err + res.err else acc.err;
-          }) init checkedFields;
-        in {
+          checkedFields = map
+            (n:
+              let v = if hasAttr n value then value."${n}" else null;
+              in checkField def."${n}" n v)
+            (attrNames def);
+
+          combined = foldl'
+            (acc: res: {
+              ok = acc.ok && res.ok;
+              err = if !res.ok then acc.err + res.err else acc.err;
+            })
+            init
+            checkedFields;
+        in
+        {
           ok = combined.ok && extraneous.ok;
           err = combined.err + extraneous.err;
         };
 
       struct' = name: def: typedef' {
         inherit name def;
-        checkType = value: if isAttrs value
+        checkType = value:
+          if isAttrs value
           then (checkStruct (self.attrs self.type def) value)
           else { ok = false; err = typeError name value; };
 
-          toError = _: result: "expected '${name}'-struct, but found:\n" + result.err;
+        toError = _: result: "expected '${name}'-struct, but found:\n" + result.err;
       };
-    in arg: if isString arg then (struct' arg) else (struct' "anon" arg);
+    in
+    arg: if isString arg then (struct' arg) else (struct' "anon" arg);
 
   # Enums & pattern matching
   enum =
-  let
-    plain = name: def: typedef' {
-      inherit name def;
+    let
+      plain = name: def: typedef' {
+        inherit name def;
 
-      checkType = (x: isString x && elem x def);
-      checkToBool = x: x;
-      toError = value: _: "'${prettyPrint value} is not a member of enum ${name}";
-    };
-    enum' = name: def: lib.fix (e: (plain name def) // {
-      match = x: actions: deepSeq (map e (attrNames actions)) (
-      let
-        actionKeys = attrNames actions;
-        missing = foldl' (m: k: if (elem k actionKeys) then m else m ++ [ k ]) [] def;
-      in if (length missing) > 0
-        then throw "Missing match action for members: ${prettyPrint missing}"
-        else actions."${e x}");
-    });
-  in arg: if isString arg then (enum' arg) else (enum' "anon" arg);
+        checkType = (x: isString x && elem x def);
+        checkToBool = x: x;
+        toError = value: _: "'${prettyPrint value} is not a member of enum ${name}";
+      };
+      enum' = name: def: lib.fix (e: (plain name def) // {
+        match = x: actions: deepSeq (map e (attrNames actions)) (
+          let
+            actionKeys = attrNames actions;
+            missing = foldl' (m: k: if (elem k actionKeys) then m else m ++ [ k ]) [ ] def;
+          in
+          if (length missing) > 0
+          then throw "Missing match action for members: ${prettyPrint missing}"
+          else actions."${e x}"
+        );
+      });
+    in
+    arg: if isString arg then (enum' arg) else (enum' "anon" arg);
 
   # Sum types
   #
   # The representation of a sum type is an attribute set with only one
   # value, where the key of the value denotes the variant of the type.
   sum =
-  let
-    plain = name: def: typedef' {
-      inherit name def;
-      checkType = (x:
-        let variant = elemAt (attrNames x) 0;
-        in if isAttrs x && length (attrNames x) == 1 && hasAttr variant def
-          then let t = def."${variant}";
-                   v = x."${variant}";
-                   res = t.checkType v;
-               in if t.checkToBool res
-                  then { ok = true; }
-                  else {
-                    ok = false;
-                    err = "while checking '${name}' variant '${variant}': "
-                          + t.toError v res;
-                  }
+    let
+      plain = name: def: typedef' {
+        inherit name def;
+        checkType = (x:
+          let variant = elemAt (attrNames x) 0;
+          in if isAttrs x && length (attrNames x) == 1 && hasAttr variant def
+          then
+            let
+              t = def."${variant}";
+              v = x."${variant}";
+              res = t.checkType v;
+            in
+            if t.checkToBool res
+            then { ok = true; }
+            else {
+              ok = false;
+              err = "while checking '${name}' variant '${variant}': "
+                + t.toError v res;
+            }
           else { ok = false; err = typeError name x; }
-      );
-    };
-    sum' = name: def: lib.fix (s: (plain name def) // {
-    match = x: actions:
-    let variant = deepSeq (s x) (elemAt (attrNames x) 0);
-        actionKeys = attrNames actions;
-        defKeys = attrNames def;
-        missing = foldl' (m: k: if (elem k actionKeys) then m else m ++ [ k ]) [] defKeys;
-    in if (length missing) > 0
-      then throw "Missing match action for variants: ${prettyPrint missing}"
-      else actions."${variant}" x."${variant}";
-    });
-    in arg: if isString arg then (sum' arg) else (sum' "anon" arg);
+        );
+      };
+      sum' = name: def: lib.fix (s: (plain name def) // {
+        match = x: actions:
+          let
+            variant = deepSeq (s x) (elemAt (attrNames x) 0);
+            actionKeys = attrNames actions;
+            defKeys = attrNames def;
+            missing = foldl' (m: k: if (elem k actionKeys) then m else m ++ [ k ]) [ ] defKeys;
+          in
+          if (length missing) > 0
+          then throw "Missing match action for variants: ${prettyPrint missing}"
+          else actions."${variant}" x."${variant}";
+      });
+    in
+    arg: if isString arg then (sum' arg) else (sum' "anon" arg);
 
   # Typed function definitions
   #
@@ -289,15 +321,19 @@ in lib.fix (self: {
       mkFunc = sig: f: {
         inherit sig;
         __toString = self: foldl' (s: t: "${s} -> ${t.name}")
-                                  "Ξ» :: ${(head self.sig).name}" (tail self.sig);
+          "Ξ» :: ${(head self.sig).name}"
+          (tail self.sig);
         __functor = _: f;
       };
 
-      defun' = sig: func: if length sig > 2
+      defun' = sig: func:
+        if length sig > 2
         then mkFunc sig (x: defun' (tail sig) (func ((head sig) x)))
         else mkFunc sig (x: ((head (tail sig)) (func ((head sig) x))));
 
-    in sig: func: if length sig < 2
+    in
+    sig: func:
+      if length sig < 2
       then (throw "Signature must at least have two types (a -> b)")
       else defun' sig func;
 
@@ -311,21 +347,22 @@ in lib.fix (self: {
   # depend on the value being of the wrapped type.
   restrict = name: pred: t:
     let restriction = "${t.name}[${name}]"; in typedef' {
-    name = restriction;
-    checkType = v:
-      let res = t.checkType v;
-      in
+      name = restriction;
+      checkType = v:
+        let res = t.checkType v;
+        in
         if !(t.checkToBool res)
         then res
         else
           let
             iok = pred v;
-          in if isBool iok then {
+          in
+          if isBool iok then {
             ok = iok;
             err = "${prettyPrint v} does not conform to restriction '${restriction}'";
           } else
-            # use throw here to avoid spamming the build log
+          # use throw here to avoid spamming the build log
             throw "restriction '${restriction}' predicate returned unexpected value '${prettyPrint iok}' instead of boolean";
-  };
+    };
 
 })
diff --git a/nix/yants/tests/default.nix b/nix/yants/tests/default.nix
index 9a0b2403e1..0c7ec24188 100644
--- a/nix/yants/tests/default.nix
+++ b/nix/yants/tests/default.nix
@@ -25,7 +25,7 @@ let
   };
 
   testPrimitives = it "checks that all primitive types match" [
-    (assertDoesNotThrow "unit type" (unit {}))
+    (assertDoesNotThrow "unit type" (unit { }))
     (assertDoesNotThrow "int type" (int 15))
     (assertDoesNotThrow "bool type" (bool false))
     (assertDoesNotThrow "float type" (float 13.37))
@@ -44,7 +44,7 @@ let
   # Test that structures work as planned.
   person = struct "person" {
     name = string;
-    age  = int;
+    age = int;
 
     contact = option (struct {
       email = string;
@@ -55,7 +55,7 @@ let
   testStruct = it "checks that structures work as intended" [
     (assertDoesNotThrow "person struct" (person {
       name = "Brynhjulf";
-      age  = 42;
+      age = 42;
       contact.email = "brynhjulf@yants.nix";
     }))
   ];
@@ -70,7 +70,8 @@ let
 
   testEnum = it "checks enum definitions and matching" [
     (assertEq "enum is matched correctly"
-      "It is in fact red!" (colour.match "red" colourMatcher))
+      "It is in fact red!"
+      (colour.match "red" colourMatcher))
     (assertThrows "out of bounds enum fails"
       (colour.match "alpha" (colourMatcher // {
         alpha = "This should never happen";
@@ -97,7 +98,8 @@ let
   testSum = it "checks sum types definitions and matching" [
     (assertDoesNotThrow "creature sum type" some-human)
     (assertEq "sum type is matched correctly"
-      "It's a human named Brynhjulf" (creature.match some-human {
+      "It's a human named Brynhjulf"
+      (creature.match some-human {
         human = v: "It's a human named ${v.name}";
         pet = v: "It's not supposed to be a pet!";
       })
@@ -106,7 +108,7 @@ let
 
   # Test curried function definitions
   func = defun [ string int string ]
-  (name: age: "${name} is ${toString age} years old");
+    (name: age: "${name} is ${toString age} years old");
 
   testFunctions = it "checks function definitions" [
     (assertDoesNotThrow "function application" (func "Brynhjulf" 42))
@@ -144,13 +146,13 @@ let
   ];
 
 in
-  runTestsuite "yants" [
-    testPrimitives
-    testPoly
-    testStruct
-    testEnum
-    testSum
-    testFunctions
-    testTypes
-    testRestrict
-  ]
+runTestsuite "yants" [
+  testPrimitives
+  testPoly
+  testStruct
+  testEnum
+  testSum
+  testFunctions
+  testTypes
+  testRestrict
+]
diff --git a/ops/besadii/main.go b/ops/besadii/main.go
index e22dbb1e48..809acc29e8 100644
--- a/ops/besadii/main.go
+++ b/ops/besadii/main.go
@@ -19,7 +19,7 @@ import (
 	"encoding/json"
 	"flag"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"log/syslog"
 	"net/http"
 	"net/mail"
@@ -130,7 +130,7 @@ func loadConfig() (*config, error) {
 		}
 	}
 
-	configJson, err := ioutil.ReadFile(configPath)
+	configJson, err := os.ReadFile(configPath)
 	if err != nil {
 		return nil, fmt.Errorf("failed to load besadii config: %w", err)
 	}
@@ -182,12 +182,12 @@ func linkToChange(cfg *config, changeId, patchset string) string {
 // updateGerrit posts a comment on a Gerrit CL to indicate the current build status.
 func updateGerrit(cfg *config, review reviewInput, changeId, patchset string) {
 	body, _ := json.Marshal(review)
-	reader := ioutil.NopCloser(bytes.NewReader(body))
+	reader := io.NopCloser(bytes.NewReader(body))
 
 	url := fmt.Sprintf("%s/a/changes/%s/revisions/%s/review", cfg.GerritUrl, changeId, patchset)
 	req, err := http.NewRequest("POST", url, reader)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "failed to create an HTTP request: %w", err)
+		fmt.Fprintf(os.Stderr, "failed to create an HTTP request: %s", err)
 		os.Exit(1)
 	}
 
@@ -196,12 +196,12 @@ func updateGerrit(cfg *config, review reviewInput, changeId, patchset string) {
 
 	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
-		fmt.Errorf("failed to update %s on %s: %w", cfg.GerritChangeName, cfg.GerritUrl, err)
+		fmt.Fprintf(os.Stderr, "failed to update %s on %s: %s", cfg.GerritChangeName, cfg.GerritUrl, err)
 	}
 	defer resp.Body.Close()
 
 	if resp.StatusCode != http.StatusOK {
-		respBody, _ := ioutil.ReadAll(resp.Body)
+		respBody, _ := io.ReadAll(resp.Body)
 		fmt.Fprintf(os.Stderr, "received non-success response from Gerrit: %s (%v)", respBody, resp.Status)
 	} else {
 		fmt.Printf("Added CI status comment on %s", linkToChange(cfg, changeId, patchset))
@@ -241,7 +241,7 @@ func triggerBuild(cfg *config, log *syslog.Writer, trigger *buildTrigger) error
 	}
 
 	body, _ := json.Marshal(build)
-	reader := ioutil.NopCloser(bytes.NewReader(body))
+	reader := io.NopCloser(bytes.NewReader(body))
 
 	bkUrl := fmt.Sprintf("https://api.buildkite.com/v2/organizations/%s/pipelines/%s/builds", cfg.BuildkiteOrg, cfg.BuildkiteProject)
 	req, err := http.NewRequest("POST", bkUrl, reader)
@@ -259,7 +259,7 @@ func triggerBuild(cfg *config, log *syslog.Writer, trigger *buildTrigger) error
 	}
 	defer resp.Body.Close()
 
-	respBody, err := ioutil.ReadAll(resp.Body)
+	respBody, err := io.ReadAll(resp.Body)
 	if err != nil {
 		return fmt.Errorf("failed to read Buildkite response body: %w", err)
 	}
@@ -459,7 +459,7 @@ func gerritHookMain(cfg *config, log *syslog.Writer, trigger *buildTrigger) {
 		log.Err(fmt.Sprintf("failed to trigger Buildkite build: %s", err))
 	}
 
-	if cfg.SourcegraphUrl != "" && trigger.ref == "refs/heads/canon" {
+	if cfg.SourcegraphUrl != "" && trigger.ref == cfg.Branch {
 		err = triggerIndexUpdate(cfg, log)
 		if err != nil {
 			log.Err(fmt.Sprintf("failed to trigger sourcegraph index update: %s", err))
@@ -475,7 +475,7 @@ func postCommandMain(cfg *config) {
 		// If these variables are unset, but the hook was invoked, the
 		// build was most likely for a branch and not for a CL - no status
 		// needs to be reported back to Gerrit!
-		fmt.Println("This isn't a %s build, nothing to do. Have a nice day!", cfg.GerritChangeName)
+		fmt.Printf("This isn't a %s build, nothing to do. Have a nice day!\n", cfg.GerritChangeName)
 		return
 	}
 
diff --git a/ops/buildkite/.gitignore b/ops/buildkite/.gitignore
new file mode 100644
index 0000000000..41c1b33462
--- /dev/null
+++ b/ops/buildkite/.gitignore
@@ -0,0 +1,2 @@
+.envrc
+.terraform*
diff --git a/ops/buildkite/README.md b/ops/buildkite/README.md
new file mode 100644
index 0000000000..9d31a53fd3
--- /dev/null
+++ b/ops/buildkite/README.md
@@ -0,0 +1,24 @@
+Buildkite configuration
+=======================
+
+This contains Terraform configuration for setting up our Buildkite
+pipelines.
+
+Each pipeline (such as the one for depot itself, or exported subsets
+of the depot) needs some static configuration stored in Buildkite.
+
+Through `//tools/depot-deps` a `tf-buildkite` binary is made available
+which contains a Terraform binary pre-configured with the correct
+providers. This is automatically on your `$PATH` through `direnv`.
+
+However, secrets still need to be loaded to access the Terraform state
+and speak to the Buildkite API. These are available to certain users
+through `//ops/secrets`.
+
+This can be done with separate direnv configuration, for example:
+
+```
+# //ops/buildkite/.envrc
+source_up
+eval $(age --decrypt -i ~/.ssh/id_ed25519 $(git rev-parse --show-toplevel)/ops/secrets/tf-buildkite.age)
+```
diff --git a/ops/buildkite/default.nix b/ops/buildkite/default.nix
new file mode 100644
index 0000000000..0d39bc0601
--- /dev/null
+++ b/ops/buildkite/default.nix
@@ -0,0 +1,14 @@
+{ depot, lib, pkgs, ... }:
+
+depot.nix.readTree.drvTargets rec {
+  terraform = pkgs.terraform.withPlugins (p: [
+    p.buildkite
+  ]);
+
+  validate = depot.tools.checks.validateTerraform {
+    inherit terraform;
+    name = "buildkite";
+    src = lib.cleanSource ./.;
+    env.BUILDKITE_API_TOKEN = "ci-dummy";
+  };
+}
diff --git a/ops/buildkite/steps-depot.yml b/ops/buildkite/steps-depot.yml
new file mode 100644
index 0000000000..011b299771
--- /dev/null
+++ b/ops/buildkite/steps-depot.yml
@@ -0,0 +1,6 @@
+---
+steps:
+  - label: ":buildkite:"
+    key: ":init:"
+    command: |
+      buildkite-agent pipeline upload ops/pipelines/static-pipeline.yaml
diff --git a/ops/buildkite/steps-tvix.yml b/ops/buildkite/steps-tvix.yml
new file mode 100644
index 0000000000..a6e9f13b16
--- /dev/null
+++ b/ops/buildkite/steps-tvix.yml
@@ -0,0 +1,4 @@
+---
+steps:
+  - label: ":buildkite: Upload pipeline"
+    command: "buildkite-agent pipeline upload"
diff --git a/ops/buildkite/steps-tvl-kit.yml b/ops/buildkite/steps-tvl-kit.yml
new file mode 100644
index 0000000000..a6e9f13b16
--- /dev/null
+++ b/ops/buildkite/steps-tvl-kit.yml
@@ -0,0 +1,4 @@
+---
+steps:
+  - label: ":buildkite: Upload pipeline"
+    command: "buildkite-agent pipeline upload"
diff --git a/ops/buildkite/tvl.tf b/ops/buildkite/tvl.tf
new file mode 100644
index 0000000000..4c45909a0c
--- /dev/null
+++ b/ops/buildkite/tvl.tf
@@ -0,0 +1,48 @@
+# Buildkite configuration for TVL.
+
+terraform {
+  required_providers {
+    buildkite = {
+      source = "buildkite/buildkite"
+    }
+  }
+
+  backend "s3" {
+    endpoint = "https://objects.dc-sto1.glesys.net"
+    bucket   = "tvl-state"
+    key      = "terraform/tvl-buildkite"
+    region   = "glesys"
+
+    skip_credentials_validation = true
+    skip_region_validation      = true
+    skip_metadata_api_check     = true
+  }
+}
+
+provider "buildkite" {
+  organization = "tvl"
+}
+
+resource "buildkite_pipeline" "depot" {
+  name           = "depot"
+  description    = "Run full CI pipeline of the depot, TVL's monorepo."
+  repository     = "https://cl.tvl.fyi/depot"
+  steps          = file("./steps-depot.yml")
+  default_branch = "refs/heads/canon"
+}
+
+resource "buildkite_pipeline" "tvix" {
+  name           = "tvix"
+  description    = "Tvix, an exported subset of TVL depot"
+  repository     = "https://code.tvl.fyi/depot.git:workspace=views/tvix.git"
+  steps          = file("./steps-tvix.yml")
+  default_branch = "canon"
+}
+
+resource "buildkite_pipeline" "tvl_kit" {
+  name           = "tvl-kit"
+  description    = "TVL Kit, an exported subset of TVL depot"
+  repository     = "https://code.tvl.fyi/depot.git:workspace=views/kit.git"
+  steps          = file("./steps-tvl-kit.yml")
+  default_branch = "canon"
+}
diff --git a/ops/dns/default.nix b/ops/dns/default.nix
index 136a4c58dc..33fe6d6fe7 100644
--- a/ops/dns/default.nix
+++ b/ops/dns/default.nix
@@ -2,11 +2,12 @@
 { depot, pkgs, ... }:
 
 let
-  checkZone = zone: file: pkgs.runCommandNoCC "${zone}-check" {} ''
+  checkZone = zone: file: pkgs.runCommand "${zone}-check" { } ''
     ${pkgs.bind}/bin/named-checkzone -i local ${zone} ${file} | tee $out
   '';
 
-in depot.nix.readTree.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   nixery-dev = checkZone "nixery.dev" ./nixery.dev.zone;
   tvl-fyi = checkZone "tvl.fyi" ./tvl.fyi.zone;
   tvl-su = checkZone "tvl.su" ./tvl.su.zone;
diff --git a/users/grfn/xanthous/server/.gitignore b/ops/gerrit-autosubmit/.gitignore
index 2f7896d1d1..2f7896d1d1 100644
--- a/users/grfn/xanthous/server/.gitignore
+++ b/ops/gerrit-autosubmit/.gitignore
diff --git a/ops/gerrit-autosubmit/Cargo.lock b/ops/gerrit-autosubmit/Cargo.lock
new file mode 100644
index 0000000000..7516c74034
--- /dev/null
+++ b/ops/gerrit-autosubmit/Cargo.lock
@@ -0,0 +1,302 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crimp"
+version = "4087.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ead2c83f7d1f9b8e5a6f7a25985d0d1759ccd2cd72abb1eee2db65d05e12b39"
+dependencies = [
+ "curl",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "curl"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "winapi",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.68+curl-8.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "windows-sys",
+]
+
+[[package]]
+name = "gerrit-autosubmit"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "crimp",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "libc"
+version = "0.2.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+
+[[package]]
+name = "libz-sys"
+version = "1.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "schannel"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.193"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.193"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/ops/gerrit-autosubmit/Cargo.toml b/ops/gerrit-autosubmit/Cargo.toml
new file mode 100644
index 0000000000..fa51614a08
--- /dev/null
+++ b/ops/gerrit-autosubmit/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "gerrit-autosubmit"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0.75"
+crimp = "4087.0.0"
+serde = { version = "1.0.193", features = ["derive"] }
+serde_json = "1.0.108"
diff --git a/ops/gerrit-autosubmit/default.nix b/ops/gerrit-autosubmit/default.nix
new file mode 100644
index 0000000000..f69a9248e3
--- /dev/null
+++ b/ops/gerrit-autosubmit/default.nix
@@ -0,0 +1,7 @@
+{ depot, pkgs, ... }:
+
+depot.third_party.naersk.buildPackage {
+  src = ./.;
+  nativeBuildInputs = [ pkgs.pkg-config ];
+  buildInputs = [ pkgs.openssl ];
+}
diff --git a/ops/gerrit-autosubmit/src/main.rs b/ops/gerrit-autosubmit/src/main.rs
new file mode 100644
index 0000000000..85d8a6af61
--- /dev/null
+++ b/ops/gerrit-autosubmit/src/main.rs
@@ -0,0 +1,194 @@
+//! gerrit-autosubmit connects to a Gerrit instance and submits the
+//! longest chain of changes in which all ancestors are ready and
+//! marked for autosubmit.
+//!
+//! It works like this:
+//!
+//! * it fetches all changes the Gerrit query API considers
+//!   submittable (i.e. all requirements fulfilled), and that have the
+//!   `Autosubmit` label set
+//!
+//! * it filters these changes down to those that are _actually_
+//!   submittable (in Gerrit API terms: that have an active Submit button)
+//!
+//! * it filters out those that would submit ancestors that are *not*
+//!   marked with the `Autosubmit` label
+//!
+//! * it submits the longest chain
+//!
+//! After that it just loops.
+
+use anyhow::{Context, Result};
+use std::collections::{BTreeMap, HashMap, HashSet};
+use std::{thread, time};
+
+mod gerrit {
+    use anyhow::{anyhow, Context, Result};
+    use serde::Deserialize;
+    use serde_json::Value;
+    use std::collections::HashMap;
+    use std::env;
+
+    pub struct Config {
+        gerrit_url: String,
+        username: String,
+        password: String,
+    }
+
+    impl Config {
+        pub fn from_env() -> Result<Self> {
+            Ok(Config {
+                gerrit_url: env::var("GERRIT_URL")
+                    .context("Gerrit base URL (no trailing slash) must be set in GERRIT_URL")?,
+                username: env::var("GERRIT_USERNAME")
+                    .context("Gerrit username must be set in GERRIT_USERNAME")?,
+                password: env::var("GERRIT_PASSWORD")
+                    .context("Gerrit password must be set in GERRIT_PASSWORD")?,
+            })
+        }
+    }
+
+    #[derive(Deserialize)]
+    pub struct ChangeInfo {
+        pub id: String,
+        pub revisions: HashMap<String, Value>,
+    }
+
+    #[derive(Deserialize)]
+    pub struct Action {
+        #[serde(default)]
+        pub enabled: bool,
+    }
+
+    const GERRIT_RESPONSE_PREFIX: &str = ")]}'";
+
+    pub fn get<T: serde::de::DeserializeOwned>(cfg: &Config, endpoint: &str) -> Result<T> {
+        let response = crimp::Request::get(&format!("{}/a{}", cfg.gerrit_url, endpoint))
+            .user_agent("gerrit-autosubmit")?
+            .basic_auth(&cfg.username, &cfg.password)?
+            .send()?
+            .error_for_status(|r| anyhow!("request failed with status {}", r.status))?;
+
+        let result: T = serde_json::from_slice(&response.body[GERRIT_RESPONSE_PREFIX.len()..])?;
+        Ok(result)
+    }
+
+    pub fn submit(cfg: &Config, change_id: &str) -> Result<()> {
+        crimp::Request::post(&format!(
+            "{}/a/changes/{}/submit",
+            cfg.gerrit_url, change_id
+        ))
+        .user_agent("gerrit-autosubmit")?
+        .basic_auth(&cfg.username, &cfg.password)?
+        .send()?
+        .error_for_status(|r| anyhow!("submit failed with status {}", r.status))?;
+
+        Ok(())
+    }
+}
+
+#[derive(Debug)]
+struct SubmittableChange {
+    id: String,
+    revision: String,
+}
+
+fn list_submittable(cfg: &gerrit::Config) -> Result<Vec<SubmittableChange>> {
+    let mut out = Vec::new();
+
+    let changes: Vec<gerrit::ChangeInfo> = gerrit::get(
+        &cfg,
+        "/changes/?q=is:submittable+label:Autosubmit+-is:wip+is:open&o=SKIP_DIFFSTAT&o=CURRENT_REVISION",
+    )
+    .context("failed to list submittable changes")?;
+
+    for change in changes.into_iter() {
+        out.push(SubmittableChange {
+            id: change.id,
+            revision: change
+                .revisions
+                .into_keys()
+                .next()
+                .context("change had no current revision")?,
+        });
+    }
+
+    Ok(out)
+}
+
+fn is_submittable(cfg: &gerrit::Config, change: &SubmittableChange) -> Result<bool> {
+    let response: HashMap<String, gerrit::Action> = gerrit::get(
+        cfg,
+        &format!(
+            "/changes/{}/revisions/{}/actions",
+            change.id, change.revision
+        ),
+    )
+    .context("failed to fetch actions for change")?;
+
+    match response.get("submit") {
+        None => Ok(false),
+        Some(action) => Ok(action.enabled),
+    }
+}
+
+fn submitted_with(cfg: &gerrit::Config, change_id: &str) -> Result<HashSet<String>> {
+    let response: Vec<gerrit::ChangeInfo> =
+        gerrit::get(cfg, &format!("/changes/{}/submitted_together", change_id))
+            .context("failed to fetch related change list")?;
+
+    Ok(response.into_iter().map(|c| c.id).collect())
+}
+
+fn autosubmit(cfg: &gerrit::Config) -> Result<bool> {
+    let mut submittable_changes: HashSet<String> = Default::default();
+
+    for change in list_submittable(&cfg)? {
+        if !is_submittable(&cfg, &change)? {
+            continue;
+        }
+
+        submittable_changes.insert(change.id.clone());
+    }
+
+    let mut chains: BTreeMap<usize, String> = Default::default();
+    for change_id in &submittable_changes {
+        let ancestors = submitted_with(&cfg, &change_id)?;
+        if ancestors.is_subset(&submittable_changes) {
+            chains.insert(
+                if ancestors.is_empty() {
+                    1
+                } else {
+                    ancestors.len()
+                },
+                change_id.clone(),
+            );
+        }
+    }
+
+    // BTreeMap::last_key_value gives us the value associated with the
+    // largest key, i.e. with the longest submittable chain of changes.
+    if let Some((count, change_id)) = chains.last_key_value() {
+        println!(
+            "submitting change {} with chain length {}",
+            change_id, count
+        );
+
+        gerrit::submit(cfg, change_id).context("while submitting")?;
+
+        Ok(true)
+    } else {
+        println!("nothing ready for autosubmit, waiting ...");
+        Ok(false)
+    }
+}
+
+fn main() -> Result<()> {
+    let cfg = gerrit::Config::from_env()?;
+
+    loop {
+        if !autosubmit(&cfg)? {
+            thread::sleep(time::Duration::from_secs(30));
+        }
+    }
+}
diff --git a/ops/gerrit-tvl/static/tvl.js b/ops/gerrit-tvl/static/tvl.js
index 2c4d7ee473..684636de30 100644
--- a/ops/gerrit-tvl/static/tvl.js
+++ b/ops/gerrit-tvl/static/tvl.js
@@ -79,12 +79,11 @@ function jobStateToCheckRunStatus(state) {
 
 const tvlChecksProvider = {
   async fetch(change) {
-    let {changeNumber, patchsetNumber, repo} = change;
+    let {patchsetSha, repo} = change;
 
     const experiments = window.ENABLED_EXPERIMENTS || [];
     if (experiments.includes("UiFeature__tvl_check_debug")) {
-      changeNumber = 2872;
-      patchsetNumber = 4;
+      patchsetSha = '76692104f58b849b1503a8d8a700298003fa7b5f';
       repo = 'depot';
     }
 
@@ -94,8 +93,7 @@ const tvlChecksProvider = {
     }
 
     const params = {
-      // besadii groups different patchsets of the same CL under this fake ref
-      branch: `cl/${changeNumber.toString()}`,
+      commit: patchsetSha,
     };
     const url = `https://api.buildkite.com/v2/organizations/tvl/pipelines/depot/builds?${encodeParams(params)}`;
     const resp = await fetch(url, {
diff --git a/ops/glesys/default.nix b/ops/glesys/default.nix
index f4c0478c5d..e511e1f6b6 100644
--- a/ops/glesys/default.nix
+++ b/ops/glesys/default.nix
@@ -1,8 +1,15 @@
-{ depot, pkgs, ... }:
+{ depot, lib, pkgs, ... }:
 
-depot.nix.readTree.drvTargets {
+depot.nix.readTree.drvTargets rec {
   # Provide a Terraform wrapper with the right provider installed.
-  terraform = pkgs.terraform.withPlugins(_: [
+  terraform = pkgs.terraform.withPlugins (_: [
     depot.third_party.terraform-provider-glesys
   ]);
+
+  validate = depot.tools.checks.validateTerraform {
+    inherit terraform;
+    name = "glesys";
+    src = lib.cleanSource ./.;
+    env.GLESYS_TOKEN = "ci-dummy";
+  };
 }
diff --git a/ops/glesys/dns-nixery-dev.tf b/ops/glesys/dns-nixery-dev.tf
index 53a421d20e..42bcec7e21 100644
--- a/ops/glesys/dns-nixery-dev.tf
+++ b/ops/glesys/dns-nixery-dev.tf
@@ -12,14 +12,7 @@ resource "glesys_dnsdomain_record" "nixery_dev_apex_A" {
   domain = glesys_dnsdomain.nixery_dev.id
   host   = "@"
   type   = "A"
-  data   = var.whitby_ipv4
-}
-
-resource "glesys_dnsdomain_record" "nixery_dev_apex_AAAA" {
-  domain = glesys_dnsdomain.nixery_dev.id
-  host   = "@"
-  type   = "AAAA"
-  data   = var.whitby_ipv6
+  data   = "51.250.51.78" # nixery-01.tvl.fyi
 }
 
 resource "glesys_dnsdomain_record" "nixery_dev_NS1" {
diff --git a/ops/glesys/dns-tvix-dev.tf b/ops/glesys/dns-tvix-dev.tf
new file mode 100644
index 0000000000..296532a02b
--- /dev/null
+++ b/ops/glesys/dns-tvix-dev.tf
@@ -0,0 +1,54 @@
+# DNS configuration for tvix.dev
+
+resource "glesys_dnsdomain" "tvix_dev" {
+  name = "tvix.dev"
+}
+
+resource "glesys_dnsdomain_record" "tvix_dev_apex_A" {
+  domain = glesys_dnsdomain.tvix_dev.id
+  host   = "@"
+  type   = "A"
+  data   = var.whitby_ipv4
+}
+
+resource "glesys_dnsdomain_record" "tvix_dev_apex_AAAA" {
+  domain = glesys_dnsdomain.tvix_dev.id
+  host   = "@"
+  type   = "AAAA"
+  data   = var.whitby_ipv6
+}
+
+resource "glesys_dnsdomain_record" "tvix_dev_bolt_CNAME" {
+  domain = glesys_dnsdomain.tvix_dev.id
+  host   = "bolt"
+  type   = "CNAME"
+  data   = "whitby.tvl.su."
+}
+
+resource "glesys_dnsdomain_record" "tvix_dev_docs_CNAME" {
+  domain = glesys_dnsdomain.tvix_dev.id
+  host   = "docs"
+  type   = "CNAME"
+  data   = "whitby.tvl.fyi."
+}
+
+resource "glesys_dnsdomain_record" "tvix_dev_NS1" {
+  domain = glesys_dnsdomain.tvix_dev.id
+  host   = "@"
+  type   = "NS"
+  data   = "ns1.namesystem.se."
+}
+
+resource "glesys_dnsdomain_record" "tvix_dev_NS2" {
+  domain = glesys_dnsdomain.tvix_dev.id
+  host   = "@"
+  type   = "NS"
+  data   = "ns2.namesystem.se."
+}
+
+resource "glesys_dnsdomain_record" "tvix_dev_NS3" {
+  domain = glesys_dnsdomain.tvix_dev.id
+  host   = "@"
+  type   = "NS"
+  data   = "ns3.namesystem.se."
+}
diff --git a/ops/glesys/dns-tvl-fyi.tf b/ops/glesys/dns-tvl-fyi.tf
index 803bfeae08..9d7972c412 100644
--- a/ops/glesys/dns-tvl-fyi.tf
+++ b/ops/glesys/dns-tvl-fyi.tf
@@ -53,13 +53,27 @@ resource "glesys_dnsdomain_record" "tvl_fyi_whitby_AAAA" {
   data   = var.whitby_ipv6
 }
 
-# This record is responsible for hosting ~all TVL services. Be
-# mindful!
-resource "glesys_dnsdomain_record" "tvl_fyi_wildcard" {
+resource "glesys_dnsdomain_record" "tvl_fyi_nixery-01_A" {
+  domain = glesys_dnsdomain.tvl_fyi.id
+  host   = "nixery-01"
+  type   = "A"
+  data   = "51.250.51.78"
+}
+
+# Explicit records for all services running on whitby
+resource "glesys_dnsdomain_record" "tvl_fyi_whitby_services" {
+  domain   = glesys_dnsdomain.tvl_fyi.id
+  type     = "CNAME"
+  data     = "whitby.tvl.fyi."
+  host     = each.key
+  for_each = toset(local.whitby_services)
+}
+
+resource "glesys_dnsdomain_record" "tvl_fyi_net_CNAME" {
   domain = glesys_dnsdomain.tvl_fyi.id
-  host   = "*"
   type   = "CNAME"
-  data   = "whitby.tvl.fyi."
+  data   = "sanduny.tvl.su."
+  host   = "net"
 }
 
 # Google Domains mail forwarding configuration (no sending)
diff --git a/ops/glesys/dns-tvl-su.tf b/ops/glesys/dns-tvl-su.tf
index 9b8bcd9a87..f2286cf1cf 100644
--- a/ops/glesys/dns-tvl-su.tf
+++ b/ops/glesys/dns-tvl-su.tf
@@ -53,68 +53,85 @@ resource "glesys_dnsdomain_record" "tvl_su_whitby_AAAA" {
   data   = var.whitby_ipv6
 }
 
-# This record is responsible for hosting ~all TVL services. Be
-# mindful!
-resource "glesys_dnsdomain_record" "tvl_su_wildcard" {
+resource "glesys_dnsdomain_record" "tvl_su_sanduny_A" {
   domain = glesys_dnsdomain.tvl_su.id
-  host   = "*"
-  type   = "CNAME"
-  data   = "whitby.tvl.su."
+  host   = "sanduny"
+  type   = "A"
+  data   = var.sanduny_ipv4
 }
 
-# # Google Domains mail forwarding configuration (no sending)
-resource "glesys_dnsdomain_record" "tvl_su_MX_aspmx" {
+resource "glesys_dnsdomain_record" "tvl_su_sanduny_AAAA" {
   domain = glesys_dnsdomain.tvl_su.id
-  host   = "@"
-  type   = "MX"
-  data   = "1 aspmx.l.google.com."
+  host   = "sanduny"
+  type   = "AAAA"
+  data   = var.sanduny_ipv6
+}
+
+# Explicit records for all services running on whitby
+resource "glesys_dnsdomain_record" "tvl_su_whitby_services" {
+  domain   = glesys_dnsdomain.tvl_su.id
+  type     = "CNAME"
+  data     = "whitby.tvl.su."
+  host     = each.key
+  for_each = toset(local.whitby_services)
 }
 
-resource "glesys_dnsdomain_record" "tvl_su_MX_alt1" {
+# historical tvixbolt.tvl.su record, redirects to bolt.tvix.dev
+resource "glesys_dnsdomain_record" "tvix_su_tvixbolt_CNAME" {
   domain = glesys_dnsdomain.tvl_su.id
-  host   = "@"
-  type   = "MX"
-  data   = "5 alt1.aspmx.l.google.com."
+  host   = "tvixbolt"
+  type   = "CNAME"
+  data   = "whitby.tvl.su."
 }
 
-resource "glesys_dnsdomain_record" "tvl_su_MX_alt2" {
+resource "glesys_dnsdomain_record" "tvl_su_inbox_CNAME" {
+  domain = glesys_dnsdomain.tvl_su.id
+  type   = "CNAME"
+  data   = "sanduny.tvl.su."
+  host   = "inbox.tvl.su."
+}
+
+resource "glesys_dnsdomain_record" "tvl_su_TXT_google_site" {
   domain = glesys_dnsdomain.tvl_su.id
   host   = "@"
-  type   = "MX"
-  data   = "5 alt2.aspmx.l.google.com."
+  type   = "TXT"
+  data   = "google-site-verification=3ksTBzFK3lZlzD3ddBfpaHs9qasfAiYBmvbW2T_ejH4"
 }
 
-resource "glesys_dnsdomain_record" "tvl_su_MX_alt3" {
+# Yandex 360 setup
+
+resource "glesys_dnsdomain_record" "tvl_su_TXT_yandex" {
   domain = glesys_dnsdomain.tvl_su.id
   host   = "@"
-  type   = "MX"
-  data   = "10 alt3.aspmx.l.google.com."
+  type   = "TXT"
+  data   = "yandex-verification: b99c43b7838949dc"
 }
 
-resource "glesys_dnsdomain_record" "tvl_su_MX_alt4" {
+resource "glesys_dnsdomain_record" "tvl_su_MX_yandex" {
   domain = glesys_dnsdomain.tvl_su.id
   host   = "@"
   type   = "MX"
-  data   = "10 alt4.aspmx.l.google.com."
+  data   = "10 mx.yandex.net."
 }
 
-resource "glesys_dnsdomain_record" "tvl_su_TXT_google_site" {
+resource "glesys_dnsdomain_record" "tvl_su_TXT_yandex_spf" {
   domain = glesys_dnsdomain.tvl_su.id
   host   = "@"
   type   = "TXT"
-  data   = "google-site-verification=3ksTBzFK3lZlzD3ddBfpaHs9qasfAiYBmvbW2T_ejH4"
+  data   = "v=spf1 redirect=_spf.yandex.net"
+
 }
 
-resource "glesys_dnsdomain_record" "tvl_su_TXT_google_spf" {
+resource "glesys_dnsdomain_record" "tvl_su_TXT_yandex_dkim" {
   domain = glesys_dnsdomain.tvl_su.id
-  host   = "@"
+  host   = "mail._domainkey"
   type   = "TXT"
-  data   = "v=spf1 include:_spf.google.com ~all"
+  data   = "v=DKIM1; k=rsa; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaRdWF8BtCHlTTQN8O+E5Qn27FVIpUEAdk1uq2vdIKh1Un/3NfdWtxStcS1Mf0iEprt1Fb4zgWOkDlPi+hH/UZqiC9QNeNqEBGMB9kgJyfsUt6cDCIVGvn8PT9JcZW1jxSziOj8nUWB4noqbaVcQNqNbwtaHPm3aifwKwScxVO7wIDAQAB"
 }
 
-resource "glesys_dnsdomain_record" "tvl_su_TXT_google_dkim" {
+resource "glesys_dnsdomain_record" "tvl_su_CNAME_yandex_mail" {
   domain = glesys_dnsdomain.tvl_su.id
-  host   = "google._domainkey"
-  type   = "TXT"
-  data   = "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlqCbnGa8oPwrudJK60l6MJj3NBnwj8wAPXNGtYy2SXrOBi7FT+ySwW7ATpfv6Xq9zGDUWJsENPUlFmvDiUs7Qi4scnNvSO1L+sDseB9/q1m3gMFVnTuieDO/T+KKkg0+uYgMM7YX5PahsAAJJ+EMb/r4afl3tcBMPR64VveKQ0hiSHA4zIYPsB9FB+b8S5C46uyY0r6WR7IzGjq2Gzb1do0kxvaKItTITWLSImcUu5ZZuXOUKJb441frVBWur5lXaYuedkxb1IRTTK0V/mBODE1D7k73MxGrqlzaMPdCqz+c3hRE18WVUkBTYjANVXDrs3yzBBVxaIAeu++vkO6BvQIDAQAB"
+  host   = "mail"
+  type   = "CNAME"
+  data   = "domain.mail.yandex.net."
 }
diff --git a/ops/glesys/main.tf b/ops/glesys/main.tf
index 857c1677fb..ec6bb7c397 100644
--- a/ops/glesys/main.tf
+++ b/ops/glesys/main.tf
@@ -12,14 +12,18 @@ terraform {
   }
 
   backend "s3" {
-    endpoint = "https://objects.dc-sto1.glesys.net"
-    bucket   = "tvl-state"
-    key      = "terraform/tvl-glesys"
-    region   = "glesys"
+    endpoints = {
+      s3 = "https://objects.dc-sto1.glesys.net"
+    }
+    bucket = "tvl-state"
+    key    = "terraform/tvl-glesys"
+    region = "glesys"
 
     skip_credentials_validation = true
     skip_region_validation      = true
     skip_metadata_api_check     = true
+    skip_requesting_account_id  = true
+    skip_s3_checksum            = true
   }
 }
 
@@ -35,10 +39,6 @@ resource "glesys_objectstorage_instance" "tvl-backups" {
 resource "glesys_objectstorage_instance" "tvl-state" {
   description = "tvl-state"
   datacenter  = "dc-sto1"
-
-  lifecycle {
-    ignore_changes = [accesskey]
-  }
 }
 
 resource "glesys_objectstorage_credential" "terraform-state" {
@@ -60,3 +60,33 @@ variable "whitby_ipv6" {
   type    = string
   default = "2a01:4f8:242:5b21:0:feed:edef:beef"
 }
+
+variable "sanduny_ipv4" {
+  type    = string
+  default = "85.119.82.231"
+}
+
+variable "sanduny_ipv6" {
+  type    = string
+  default = "2001:ba8:1f1:f109::feed:edef:beef"
+}
+
+locals {
+  # Hostnames of all public services on whitby
+  whitby_services = [
+    "at",
+    "atward",
+    "auth",
+    "b",
+    "cache",
+    "cl",
+    "code",
+    "cs",
+    "deploys",
+    "images",
+    "signup",
+    "static",
+    "status",
+    "todo",
+  ]
+}
diff --git a/ops/journaldriver/Cargo.lock b/ops/journaldriver/Cargo.lock
index f7c697068f..97bbe16ceb 100644
--- a/ops/journaldriver/Cargo.lock
+++ b/ops/journaldriver/Cargo.lock
@@ -1,818 +1,646 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-[[package]]
-name = "aho-corasick"
-version = "0.6.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "ascii"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "atty"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+version = 3
 
 [[package]]
-name = "backtrace"
-version = "0.3.9"
+name = "aho-corasick"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
 dependencies = [
- "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr",
 ]
 
 [[package]]
-name = "backtrace-sys"
-version = "0.1.24"
+name = "anyhow"
+version = "1.0.75"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
 
 [[package]]
 name = "base64"
-version = "0.9.3"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "bitflags"
-version = "1.0.4"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
 
 [[package]]
-name = "byteorder"
-version = "1.2.6"
+name = "build-env"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e068f31938f954b695423ecaf756179597627d0828c0d3e48c0a722a8b23cf9e"
 
 [[package]]
 name = "cc"
-version = "1.0.25"
+version = "1.0.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "cfg-if"
-version = "0.1.5"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
-name = "chrono"
-version = "0.4.6"
+name = "crimp"
+version = "4087.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ead2c83f7d1f9b8e5a6f7a25985d0d1759ccd2cd72abb1eee2db65d05e12b39"
 dependencies = [
- "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl",
+ "serde",
+ "serde_json",
 ]
 
 [[package]]
-name = "chunked_transfer"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "cloudabi"
-version = "0.0.3"
+name = "cstr-argument"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bd9c8e659a473bce955ae5c35b116af38af11a7acb0b480e01f3ed348aeb40"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if",
+ "memchr",
 ]
 
 [[package]]
-name = "cookie"
-version = "0.11.0"
+name = "curl"
+version = "0.4.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
 dependencies = [
- "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "winapi",
 ]
 
 [[package]]
-name = "core-foundation"
-version = "0.5.1"
+name = "curl-sys"
+version = "0.4.68+curl-8.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f"
 dependencies = [
- "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "windows-sys",
 ]
 
 [[package]]
-name = "core-foundation-sys"
-version = "0.5.1"
+name = "deranged"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
 dependencies = [
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "powerfmt",
+ "serde",
 ]
 
 [[package]]
-name = "cstr-argument"
-version = "0.0.2"
+name = "env_logger"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
 dependencies = [
- "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
 ]
 
 [[package]]
-name = "env_logger"
-version = "0.5.13"
+name = "errno"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
 dependencies = [
- "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc",
+ "windows-sys",
 ]
 
 [[package]]
-name = "failure"
-version = "0.1.2"
+name = "foreign-types"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
 dependencies = [
- "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "foreign-types-shared 0.1.1",
 ]
 
 [[package]]
-name = "failure_derive"
-version = "0.1.2"
+name = "foreign-types"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
 dependencies = [
- "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "foreign-types-macros",
+ "foreign-types-shared 0.3.1",
 ]
 
 [[package]]
-name = "foreign-types"
-version = "0.3.2"
+name = "foreign-types-macros"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
 dependencies = [
- "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
 name = "foreign-types-shared"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 
 [[package]]
-name = "fuchsia-zircon"
-version = "0.3.3"
+name = "foreign-types-shared"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
 
 [[package]]
-name = "fuchsia-zircon-sys"
+name = "hermit-abi"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
 
 [[package]]
 name = "humantime"
-version = "1.1.1"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
-name = "idna"
-version = "0.1.5"
+name = "is-terminal"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
 dependencies = [
- "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hermit-abi",
+ "rustix",
+ "windows-sys",
 ]
 
 [[package]]
 name = "itoa"
-version = "0.4.3"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
 [[package]]
 name = "journaldriver"
-version = "1.1.0"
+version = "5656.0.0"
 dependencies = [
- "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
- "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "medallion 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
- "systemd 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "ureq 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "anyhow",
+ "crimp",
+ "env_logger",
+ "lazy_static",
+ "log",
+ "medallion",
+ "pkg-config",
+ "serde",
+ "serde_json",
+ "systemd",
+ "time",
 ]
 
 [[package]]
 name = "lazy_static"
-version = "1.1.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.43"
+version = "0.2.150"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
 
 [[package]]
 name = "libsystemd-sys"
-version = "0.2.1"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d28ad38d7bee81aabd41201ee7d36df8d7f76aa0a455c77d5c365c4669b4b4b6"
 dependencies = [
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "build-env",
+ "libc",
+ "pkg-config",
 ]
 
 [[package]]
-name = "log"
-version = "0.4.5"
+name = "libz-sys"
+version = "1.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
 dependencies = [
- "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
 ]
 
 [[package]]
-name = "matches"
-version = "0.1.8"
+name = "linux-raw-sys"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
 
 [[package]]
-name = "medallion"
-version = "2.2.3"
+name = "log"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "openssl 0.10.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
 [[package]]
-name = "memchr"
-version = "1.0.2"
+name = "medallion"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35b83c0c3277d722b53a6eb24e3c1321172f85b715cc7405add8ffd1f2f06288"
 dependencies = [
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "anyhow",
+ "base64",
+ "openssl",
+ "serde",
+ "serde_json",
+ "time",
 ]
 
 [[package]]
 name = "memchr"
-version = "2.1.0"
+version = "2.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
 
 [[package]]
-name = "native-tls"
-version = "0.2.1"
+name = "once_cell"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "openssl 0.10.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "openssl-sys 0.9.36 (registry+https://github.com/rust-lang/crates.io-index)",
- "schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
 [[package]]
-name = "num-integer"
-version = "0.1.39"
+name = "openssl"
+version = "0.10.59"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33"
 dependencies = [
- "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags",
+ "cfg-if",
+ "foreign-types 0.3.2",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
 ]
 
 [[package]]
-name = "num-traits"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "openssl"
-version = "0.10.12"
+name = "openssl-macros"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "openssl-sys 0.9.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
 name = "openssl-probe"
-version = "0.1.2"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.36"
+version = "0.9.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9"
 dependencies = [
- "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
 ]
 
 [[package]]
-name = "percent-encoding"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
 name = "pkg-config"
-version = "0.3.14"
+version = "0.3.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
 
 [[package]]
-name = "proc-macro2"
-version = "0.4.20"
+name = "powerfmt"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
-name = "qstring"
-version = "0.6.0"
+name = "proc-macro2"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
 dependencies = [
- "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-ident",
 ]
 
 [[package]]
-name = "quick-error"
-version = "1.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
 name = "quote"
-version = "0.6.8"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
- "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2",
 ]
 
 [[package]]
-name = "rand"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "redox_syscall"
-version = "0.1.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "redox_termios"
-version = "0.1.1"
+name = "regex"
+version = "1.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
 dependencies = [
- "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
 ]
 
 [[package]]
-name = "regex"
-version = "1.0.5"
+name = "regex-automata"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
 dependencies = [
- "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.2"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
 
 [[package]]
-name = "remove_dir_all"
-version = "0.5.1"
+name = "rustix"
+version = "0.38.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
 dependencies = [
- "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
 ]
 
 [[package]]
-name = "rustc-demangle"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
 name = "ryu"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "safemem"
-version = "0.3.0"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
 
 [[package]]
 name = "schannel"
-version = "0.1.14"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
 dependencies = [
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "windows-sys",
 ]
 
 [[package]]
-name = "security-framework"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "security-framework-sys"
-version = "0.2.1"
+name = "serde"
+version = "1.0.192"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
 dependencies = [
- "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive",
 ]
 
 [[package]]
-name = "serde"
-version = "1.0.79"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
 name = "serde_derive"
-version = "1.0.79"
+version = "1.0.192"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
 dependencies = [
- "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.15.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.32"
+version = "1.0.108"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
 dependencies = [
- "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa",
+ "ryu",
+ "serde",
 ]
 
 [[package]]
-name = "syn"
-version = "0.14.9"
+name = "socket2"
+version = "0.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
 dependencies = [
- "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc",
+ "winapi",
 ]
 
 [[package]]
 name = "syn"
-version = "0.15.8"
+version = "2.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
 dependencies = [
- "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
 ]
 
 [[package]]
-name = "synstructure"
-version = "0.9.0"
+name = "systemd"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da95085b9c6eedbcf0b828302a3483a84bdbf772158e586b787092112008fd1f"
 dependencies = [
- "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cstr-argument",
+ "foreign-types 0.5.0",
+ "libc",
+ "libsystemd-sys",
+ "log",
+ "utf8-cstr",
 ]
 
 [[package]]
-name = "systemd"
-version = "0.3.0"
+name = "termcolor"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
 dependencies = [
- "cstr-argument 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "libsystemd-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "utf8-cstr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util",
 ]
 
 [[package]]
-name = "tempfile"
-version = "3.0.4"
+name = "time"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
 dependencies = [
- "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "deranged",
+ "itoa",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
 ]
 
 [[package]]
-name = "termcolor"
-version = "1.0.4"
+name = "time-core"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
-name = "termion"
-version = "1.5.1"
+name = "time-macros"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
 dependencies = [
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time-core",
 ]
 
 [[package]]
-name = "thread_local"
-version = "0.3.6"
+name = "unicode-ident"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
-name = "time"
-version = "0.1.40"
+name = "utf8-cstr"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "55bcbb425141152b10d5693095950b51c3745d019363fc2929ffd8f61449b628"
 
 [[package]]
-name = "ucd-util"
-version = "0.1.1"
+name = "vcpkg"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
 [[package]]
-name = "unicode-bidi"
-version = "0.3.4"
+name = "winapi"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 dependencies = [
- "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
 ]
 
 [[package]]
-name = "unicode-normalization"
-version = "0.1.7"
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
-name = "unicode-xid"
-version = "0.1.0"
+name = "winapi-util"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
 
 [[package]]
-name = "ureq"
-version = "0.6.2"
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "native-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "qstring 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
-name = "url"
-version = "1.7.1"
+name = "windows-sys"
+version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "windows-targets",
 ]
 
 [[package]]
-name = "utf8-cstr"
-version = "0.1.6"
+name = "windows-targets"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
 
 [[package]]
-name = "utf8-ranges"
-version = "1.0.1"
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
 [[package]]
-name = "vcpkg"
-version = "0.2.6"
+name = "windows_aarch64_msvc"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 [[package]]
-name = "version_check"
-version = "0.1.5"
+name = "windows_i686_gnu"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 [[package]]
-name = "winapi"
-version = "0.3.6"
+name = "windows_i686_msvc"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 [[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
+name = "windows_x86_64_gnu"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
-name = "winapi-util"
-version = "0.1.1"
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 [[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "windows_x86_64_msvc"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "wincolor"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[metadata]
-"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a"
-"checksum ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5fc969a8ce2c9c0c4b0429bb8431544f6658283c8326ba5ff8c762b75369335"
-"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
-"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
-"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
-"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
-"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
-"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781"
-"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
-"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
-"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
-"checksum chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87"
-"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
-"checksum cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1465f8134efa296b4c19db34d909637cb2bf0f7aaf21299e23e18fa29ac557cf"
-"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980"
-"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa"
-"checksum cstr-argument 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "514570a4b719329df37f93448a70df2baac553020d0eb43a8dfa9c1f5ba7b658"
-"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38"
-"checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9"
-"checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426"
-"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
-"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
-"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
-"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
-"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
-"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
-"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
-"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7"
-"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
-"checksum libsystemd-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e751b723417158e0949ba470bee4affd6f1dd6b67622b5240d79186631b6a0d9"
-"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
-"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
-"checksum medallion 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b2e6f0713b388174fc3de9b63a0a63dfcee191a8abc8e06c0a9c6d80821c1891"
-"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
-"checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b"
-"checksum native-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8b0a7bd714e83db15676d31caf968ad7318e9cc35f93c85a90231c8f22867549"
-"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
-"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
-"checksum openssl 0.10.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2e79eede055813a3ac52fb3915caf8e1c9da2dec1587871aec9f6f7b48508d"
-"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
-"checksum openssl-sys 0.9.36 (registry+https://github.com/rust-lang/crates.io-index)" = "409d77eeb492a1aebd6eb322b2ee72ff7c7496b4434d98b3bf8be038755de65e"
-"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
-"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
-"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee"
-"checksum qstring 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "545ec057a36a93e25fb5883baed912e4984af4e2543bbf0e3463d962e0408469"
-"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
-"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5"
-"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
-"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
-"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
-"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
-"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
-"checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341"
-"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d"
-"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
-"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
-"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7"
-"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
-"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56"
-"checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0"
-"checksum security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab01dfbe5756785b5b4d46e0289e5a18071dfa9a7c2b24213ea00b9ef9b665bf"
-"checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9"
-"checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe"
-"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce"
-"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
-"checksum syn 0.15.8 (registry+https://github.com/rust-lang/crates.io-index)" = "356d1c5043597c40489e9af2d2498c7fefc33e99b7d75b43be336c8a59b3e45e"
-"checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7"
-"checksum systemd 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b62a732355787f960c25536210ae0a981aca2e5dae9dab8491bdae39613ce48"
-"checksum tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "55c1195ef8513f3273d55ff59fe5da6940287a0d7a98331254397f464833675b"
-"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
-"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
-"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
-"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
-"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
-"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
-"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
-"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
-"checksum ureq 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f3f941c0434783c82e46d30508834be5f3c1f2c85dd1b98f0681984c7be8e03"
-"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
-"checksum utf8-cstr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "55bcbb425141152b10d5693095950b51c3745d019363fc2929ffd8f61449b628"
-"checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4"
-"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d"
-"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
-"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
-"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab"
-"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/ops/journaldriver/Cargo.toml b/ops/journaldriver/Cargo.toml
index 248b22807f..65510d8705 100644
--- a/ops/journaldriver/Cargo.toml
+++ b/ops/journaldriver/Cargo.toml
@@ -1,21 +1,21 @@
 [package]
 name = "journaldriver"
-version = "1.1.0"
-authors = ["Vincent Ambo <mail@tazj.in>"]
+version = "5656.0.0"
+authors = ["Vincent Ambo <tazjin@tvl.su>"]
 license = "GPL-3.0-or-later"
+edition = "2021"
 
 [dependencies]
-chrono = { version = "0.4", features = [ "serde" ]}
-env_logger = "0.5"
-failure = "0.1"
-lazy_static = "1.0"
+anyhow = "1.0"
+crimp = "4087.0"
+env_logger = "0.10"
+lazy_static = "1.4"
 log = "0.4"
-medallion = "2.2"
-serde = "1.0"
-serde_derive = "1.0"
+medallion = "2.5"
+serde = { version = "1.0", features = [ "derive" ] }
 serde_json = "1.0"
-systemd = "0.3"
-ureq = { version = "0.6.2", features = [ "json" ]}
+systemd = "0.5"
+time = { version = "0.3", features = [ "serde-well-known", "macros" ]}
 
 [build-dependencies]
 pkg-config = "0.3"
diff --git a/ops/journaldriver/build.rs b/ops/journaldriver/build.rs
index d64c82a88a..79eb1001bf 100644
--- a/ops/journaldriver/build.rs
+++ b/ops/journaldriver/build.rs
@@ -1,6 +1,5 @@
 extern crate pkg_config;
 
 fn main() {
-    pkg_config::probe_library("libsystemd")
-        .expect("Could not probe libsystemd");
+    pkg_config::probe_library("libsystemd").expect("Could not probe libsystemd");
 }
diff --git a/ops/journaldriver/default.nix b/ops/journaldriver/default.nix
index d2413e74cc..2a3836c358 100644
--- a/ops/journaldriver/default.nix
+++ b/ops/journaldriver/default.nix
@@ -4,6 +4,8 @@ depot.third_party.naersk.buildPackage {
   src = ./.;
 
   buildInputs = with pkgs; [
-    pkgconfig openssl systemd.dev
+    pkg-config
+    openssl
+    systemd.dev
   ];
 }
diff --git a/ops/journaldriver/src/main.rs b/ops/journaldriver/src/main.rs
index 9886af1c36..4c404e607e 100644
--- a/ops/journaldriver/src/main.rs
+++ b/ops/journaldriver/src/main.rs
@@ -31,30 +31,17 @@
 //! `GOOGLE_APPLICATION_CREDENTIALS`, `GOOGLE_CLOUD_PROJECT` and
 //! `LOG_NAME` environment variables.
 
-#[macro_use] extern crate failure;
-#[macro_use] extern crate log;
-#[macro_use] extern crate serde_derive;
-#[macro_use] extern crate serde_json;
-#[macro_use] extern crate lazy_static;
-
-extern crate chrono;
-extern crate env_logger;
-extern crate medallion;
-extern crate serde;
-extern crate systemd;
-extern crate ureq;
-
-use chrono::offset::LocalResult;
-use chrono::prelude::{DateTime, TimeZone, Utc};
-use failure::ResultExt;
-use serde_json::{from_str, Value};
-use std::env;
-use std::fs::{self, File, rename};
-use std::io::{self, Read, ErrorKind, Write};
-use std::mem;
+use anyhow::{bail, Context, Result};
+use lazy_static::lazy_static;
+use log::{debug, error, info, trace};
+use serde::{Deserialize, Serialize};
+use serde_json::{from_str, json, Value};
+use std::convert::TryInto;
+use std::fs::{self, rename, File};
+use std::io::{self, ErrorKind, Read, Write};
 use std::path::PathBuf;
-use std::process;
 use std::time::{Duration, Instant};
+use std::{env, mem, process};
 use systemd::journal::{Journal, JournalFiles, JournalRecord, JournalSeek};
 
 #[cfg(test)]
@@ -62,13 +49,12 @@ mod tests;
 
 const LOGGING_SERVICE: &str = "https://logging.googleapis.com/google.logging.v2.LoggingServiceV2";
 const ENTRIES_WRITE_URL: &str = "https://logging.googleapis.com/v2/entries:write";
-const METADATA_TOKEN_URL: &str = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
+const METADATA_TOKEN_URL: &str =
+    "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
 const METADATA_ID_URL: &str = "http://metadata.google.internal/computeMetadata/v1/instance/id";
 const METADATA_ZONE_URL: &str = "http://metadata.google.internal/computeMetadata/v1/instance/zone";
-const METADATA_PROJECT_URL: &str = "http://metadata.google.internal/computeMetadata/v1/project/project-id";
-
-/// Convenience type alias for results using failure's `Error` type.
-type Result<T> = std::result::Result<T, failure::Error>;
+const METADATA_PROJECT_URL: &str =
+    "http://metadata.google.internal/computeMetadata/v1/project/project-id";
 
 /// Representation of static service account credentials for GCP.
 #[derive(Debug, Deserialize)]
@@ -126,32 +112,27 @@ lazy_static! {
 
 /// Convenience helper for retrieving values from the metadata server.
 fn get_metadata(url: &str) -> Result<String> {
-    let response = ureq::get(url)
-        .set("Metadata-Flavor", "Google")
-        .timeout_connect(5000)
-        .timeout_read(5000)
-        .call();
-
-    if response.ok() {
-        // Whitespace is trimmed to remove newlines from responses.
-        let body = response.into_string()
-            .context("Failed to decode metadata response")?
-            .trim().to_string();
-
-        Ok(body)
-    } else {
-        let status = response.status_line().to_string();
-        let body = response.into_string()
-            .unwrap_or_else(|e| format!("Metadata body error: {}", e));
-        bail!("Metadata failure: {} ({})", body, status)
+    let response = crimp::Request::get(url)
+        .header("Metadata-Flavor", "Google")?
+        .timeout(std::time::Duration::from_secs(5))?
+        .send()?
+        .as_string()?;
+
+    if !response.is_success() {
+        bail!(
+            "Error response ({}) from metadata server: {}",
+            response.status,
+            response.body
+        );
     }
+
+    Ok(response.body.trim().to_owned())
 }
 
 /// Convenience helper for determining the project ID.
 fn get_project_id() -> String {
     env::var("GOOGLE_CLOUD_PROJECT")
-        .map_err(Into::into)
-        .or_else(|_: failure::Error| get_metadata(METADATA_PROJECT_URL))
+        .or_else(|_| get_metadata(METADATA_PROJECT_URL))
         .expect("Could not determine project ID")
 }
 
@@ -186,11 +167,9 @@ fn determine_monitored_resource() -> Value {
             }
         })
     } else {
-        let instance_id = get_metadata(METADATA_ID_URL)
-            .expect("Could not determine instance ID");
+        let instance_id = get_metadata(METADATA_ID_URL).expect("Could not determine instance ID");
 
-        let zone = get_metadata(METADATA_ZONE_URL)
-            .expect("Could not determine instance zone");
+        let zone = get_metadata(METADATA_ZONE_URL).expect("Could not determine instance zone");
 
         json!({
             "type": "gce_instance",
@@ -252,9 +231,8 @@ fn get_metadata_token() -> Result<Token> {
 fn sign_service_account_token(credentials: &Credentials) -> Result<Token> {
     use medallion::{Algorithm, Header, Payload};
 
-    let iat = Utc::now();
-    let exp = iat.checked_add_signed(chrono::Duration::seconds(3600))
-        .ok_or_else(|| format_err!("Failed to calculate token expiry"))?;
+    let iat = time::OffsetDateTime::now_utc();
+    let exp = iat + time::Duration::seconds(3600);
 
     let header = Header {
         alg: Algorithm::RS256,
@@ -267,8 +245,8 @@ fn sign_service_account_token(credentials: &Credentials) -> Result<Token> {
         iss: Some(credentials.client_email.clone()),
         sub: Some(credentials.client_email.clone()),
         aud: Some(LOGGING_SERVICE.to_string()),
-        iat: Some(iat.timestamp() as u64),
-        exp: Some(exp.timestamp() as u64),
+        iat: Some(iat.unix_timestamp().try_into().unwrap()),
+        exp: Some(exp.unix_timestamp().try_into().unwrap()),
         ..Default::default()
     };
 
@@ -323,7 +301,9 @@ enum Payload {
 /// text format.
 fn message_to_payload(message: Option<String>) -> Payload {
     match message {
-        None => Payload::TextPayload { text_payload: "empty log entry".into() },
+        None => Payload::TextPayload {
+            text_payload: "empty log entry".into(),
+        },
         Some(text_payload) => {
             // Attempt to deserialize the text payload as a generic
             // JSON value.
@@ -333,7 +313,7 @@ fn message_to_payload(message: Option<String>) -> Payload {
                 // expect other types of JSON payload) and return it
                 // in that case.
                 if json_payload.is_object() {
-                    return Payload::JsonPayload { json_payload }
+                    return Payload::JsonPayload { json_payload };
                 }
             }
 
@@ -348,18 +328,15 @@ fn message_to_payload(message: Option<String>) -> Payload {
 /// Parse errors are dismissed and returned as empty options: There
 /// simply aren't any useful fallback mechanisms other than defaulting
 /// to ingestion time for journaldriver's use-case.
-fn parse_microseconds(input: String) -> Option<DateTime<Utc>> {
+fn parse_microseconds(input: String) -> Option<time::OffsetDateTime> {
     if input.len() != 16 {
         return None;
     }
 
-    let seconds: i64 = (&input[..10]).parse().ok()?;
-    let micros: u32 = (&input[10..]).parse().ok()?;
+    let micros: i128 = input.parse().ok()?;
+    let nanos: i128 = micros * 1000;
 
-    match Utc.timestamp_opt(seconds, micros * 1000) {
-        LocalResult::Single(time) => Some(time),
-        _ => None,
-    }
+    time::OffsetDateTime::from_unix_timestamp_nanos(nanos).ok()
 }
 
 /// Converts a journald log message priority to a
@@ -408,7 +385,8 @@ struct LogEntry {
     labels: Value,
 
     #[serde(skip_serializing_if = "Option::is_none")]
-    timestamp: Option<DateTime<Utc>>,
+    #[serde(with = "time::serde::rfc3339::option")]
+    timestamp: Option<time::OffsetDateTime>,
 
     #[serde(flatten)]
     payload: Payload,
@@ -450,9 +428,7 @@ impl From<JournalRecord> for LogEntry {
         // Journald uses syslogd's concept of priority. No idea if this is
         // always present, but it's optional in the Stackdriver API, so we just
         // omit it if we can't find or parse it.
-        let severity = record
-            .remove("PRIORITY")
-            .and_then(priority_to_severity);
+        let severity = record.remove("PRIORITY").and_then(priority_to_severity);
 
         LogEntry {
             payload,
@@ -468,8 +444,7 @@ impl From<JournalRecord> for LogEntry {
 
 /// Attempt to read from the journal. If no new entry is present,
 /// await the next one up to the specified timeout.
-fn receive_next_record(timeout: Duration, journal: &mut Journal)
-                       -> Result<Option<JournalRecord>> {
+fn receive_next_record(timeout: Duration, journal: &mut Journal) -> Result<Option<JournalRecord>> {
     let next_record = journal.next_record()?;
     if next_record.is_some() {
         return Ok(next_record);
@@ -525,11 +500,10 @@ fn persist_cursor(cursor: String) -> Result<()> {
     if cursor.is_empty() {
         error!("Received empty journald cursor position, refusing to persist!");
         error!("Please report this message at https://github.com/tazjin/journaldriver/issues/2");
-        return Ok(())
+        return Ok(());
     }
 
-    let mut file = File::create(&*CURSOR_TMP_FILE)
-        .context("Failed to create cursor file")?;
+    let mut file = File::create(&*CURSOR_TMP_FILE).context("Failed to create cursor file")?;
 
     write!(file, "{}", cursor).context("Failed to write cursor file")?;
 
@@ -547,9 +521,7 @@ fn persist_cursor(cursor: String) -> Result<()> {
 ///
 /// If flushing is successful the last cursor position will be
 /// persisted to disk.
-fn flush(token: &mut Token,
-         entries: Vec<LogEntry>,
-         cursor: String) -> Result<()> {
+fn flush(token: &mut Token, entries: Vec<LogEntry>, cursor: String) -> Result<()> {
     if token.is_expired() {
         debug!("Refreshing Google metadata access token");
         let new_token = get_token()?;
@@ -583,25 +555,28 @@ fn prepare_request(entries: &[LogEntry]) -> Value {
 
 /// Perform the log entry insertion in Stackdriver Logging.
 fn write_entries(token: &Token, request: Value) -> Result<()> {
-    let response = ureq::post(ENTRIES_WRITE_URL)
-        .set("Authorization", format!("Bearer {}", token.token).as_str())
+    let response = crimp::Request::post(ENTRIES_WRITE_URL)
+        .json(&request)?
+        .header("Authorization", format!("Bearer {}", token.token).as_str())?
         // The timeout values are set relatively high, not because of
         // an expectation of Stackdriver being slow but just to
-        // eventually hit an error case in case of network troubles.
+        // eventually force an error in case of network troubles.
         // Presumably no request in a functioning environment will
         // ever hit these limits.
-        .timeout_connect(2000)
-        .timeout_read(5000)
-        .send_json(request);
+        .timeout(std::time::Duration::from_secs(5))?
+        .send()?;
 
-    if response.ok() {
-        Ok(())
-    } else {
-        let status = response.status_line().to_string();
-        let body = response.into_string()
-            .unwrap_or_else(|_| "no response body".into());
-        bail!("Write failure: {} ({})", body, status)
+    if !response.is_success() {
+        let status = response.status;
+        let body = response
+            .as_string()
+            .map(|r| r.body)
+            .unwrap_or_else(|_| "no valid response body".to_owned());
+
+        bail!("Writing to Stackdriver failed({}): {}", status, body);
     }
+
+    Ok(())
 }
 
 /// Attempt to read the initial cursor position from the configured
@@ -624,14 +599,12 @@ fn initial_cursor() -> Result<JournalSeek> {
         Err(ref err) if err.kind() == ErrorKind::NotFound => {
             info!("No previous cursor position, reading from journal tail");
             Ok(JournalSeek::Tail)
-        },
-        Err(err) => {
-            (Err(err).context("Could not read cursor position"))?
         }
+        Err(err) => (Err(err).context("Could not read cursor position"))?,
     }
 }
 
-fn main () {
+fn main() {
     env_logger::init();
 
     // The directory in which cursor positions are persisted should
@@ -641,17 +614,17 @@ fn main () {
         process::exit(1);
     }
 
-    let cursor_position_dir = CURSOR_FILE.parent()
+    let cursor_position_dir = CURSOR_FILE
+        .parent()
         .expect("Invalid cursor position file path");
 
     fs::create_dir_all(cursor_position_dir)
         .expect("Could not create directory to store cursor position in");
 
-    let mut journal = Journal::open(JournalFiles::All, false, true)
-        .expect("Failed to open systemd journal");
+    let mut journal =
+        Journal::open(JournalFiles::All, false, true).expect("Failed to open systemd journal");
 
-    let seek_position = initial_cursor()
-        .expect("Failed to determine initial cursor position");
+    let seek_position = initial_cursor().expect("Failed to determine initial cursor position");
 
     match journal.seek(seek_position) {
         Ok(cursor) => info!("Opened journal at cursor '{}'", cursor),
diff --git a/ops/journaldriver/src/tests.rs b/ops/journaldriver/src/tests.rs
index 779add7a70..6f5045d6a5 100644
--- a/ops/journaldriver/src/tests.rs
+++ b/ops/journaldriver/src/tests.rs
@@ -1,5 +1,6 @@
 use super::*;
 use serde_json::to_string;
+use time::macros::datetime;
 
 #[test]
 fn test_text_entry_serialization() {
@@ -15,7 +16,31 @@ fn test_text_entry_serialization() {
     let expected = "{\"labels\":null,\"textPayload\":\"test entry\"}";
     let result = to_string(&entry).expect("serialization failed");
 
-    assert_eq!(expected, result, "Plain text payload should serialize correctly")
+    assert_eq!(
+        expected, result,
+        "Plain text payload should serialize correctly"
+    )
+}
+
+#[test]
+fn test_timestamped_entry_serialization() {
+    let entry = LogEntry {
+        labels: Value::Null,
+        timestamp: Some(datetime!(1952-10-07 12:00:00 UTC)),
+        payload: Payload::TextPayload {
+            text_payload: "test entry".into(),
+        },
+        severity: None,
+    };
+
+    let expected =
+        "{\"labels\":null,\"timestamp\":\"1952-10-07T12:00:00Z\",\"textPayload\":\"test entry\"}";
+    let result = to_string(&entry).expect("serialization failed");
+
+    assert_eq!(
+        expected, result,
+        "Plain text payload should serialize correctly"
+    )
 }
 
 #[test]
@@ -26,7 +51,7 @@ fn test_json_entry_serialization() {
         payload: Payload::JsonPayload {
             json_payload: json!({
                 "message": "JSON test"
-            })
+            }),
         },
         severity: None,
     };
@@ -34,7 +59,7 @@ fn test_json_entry_serialization() {
     let expected = "{\"labels\":null,\"jsonPayload\":{\"message\":\"JSON test\"}}";
     let result = to_string(&entry).expect("serialization failed");
 
-    assert_eq!(expected, result, "JSOn payload should serialize correctly")
+    assert_eq!(expected, result, "JSON payload should serialize correctly")
 }
 
 #[test]
@@ -45,7 +70,10 @@ fn test_plain_text_payload() {
         text_payload: "plain text payload".into(),
     };
 
-    assert_eq!(expected, payload, "Plain text payload should be detected correctly");
+    assert_eq!(
+        expected, payload,
+        "Plain text payload should be detected correctly"
+    );
 }
 
 #[test]
@@ -55,7 +83,10 @@ fn test_empty_payload() {
         text_payload: "empty log entry".into(),
     };
 
-    assert_eq!(expected, payload, "Empty payload should be handled correctly");
+    assert_eq!(
+        expected, payload,
+        "Empty payload should be handled correctly"
+    );
 }
 
 #[test]
@@ -66,10 +97,13 @@ fn test_json_payload() {
         json_payload: json!({
             "someKey": "someValue",
             "otherKey": 42
-        })
+        }),
     };
 
-    assert_eq!(expected, payload, "JSON payload should be detected correctly");
+    assert_eq!(
+        expected, payload,
+        "JSON payload should be detected correctly"
+    );
 }
 
 #[test]
@@ -82,14 +116,16 @@ fn test_json_no_object() {
         text_payload: "42".into(),
     };
 
-    assert_eq!(expected, payload, "Non-object JSON payload should be plain text");
+    assert_eq!(
+        expected, payload,
+        "Non-object JSON payload should be plain text"
+    );
 }
 
 #[test]
 fn test_parse_microseconds() {
     let input: String = "1529175149291187".into();
-    let expected: DateTime<Utc> = "2018-06-16T18:52:29.291187Z"
-        .to_string().parse().unwrap();
+    let expected: time::OffsetDateTime = datetime!(2018-06-16 18:52:29.291187 UTC);
 
     assert_eq!(Some(expected), parse_microseconds(input));
 }
diff --git a/ops/keycloak/README.md b/ops/keycloak/README.md
index e8ffd700b5..fd72daa87d 100644
--- a/ops/keycloak/README.md
+++ b/ops/keycloak/README.md
@@ -12,7 +12,7 @@ credentials.
 An example `direnv` configuration used by tazjin is this:
 
 ```
-# //ops/secrets/.envrc
+# //ops/keycloak/.envrc
 source_up
 eval $(age --decrypt -i ~/.ssh/id_ed25519 $(git rev-parse --show-toplevel)/ops/secrets/tf-keycloak.age)
 ```
diff --git a/ops/keycloak/clients.tf b/ops/keycloak/clients.tf
index 5f2fd21a35..178971ae36 100644
--- a/ops/keycloak/clients.tf
+++ b/ops/keycloak/clients.tf
@@ -70,23 +70,16 @@ resource "keycloak_saml_user_attribute_protocol_mapper" "buildkite_name" {
   saml_attribute_name_format = "Unspecified"
 }
 
-resource "keycloak_openid_client" "oauth2_proxy" {
+resource "keycloak_openid_client" "panettone" {
   realm_id              = keycloak_realm.tvl.id
-  client_id             = "oauth2-proxy"
-  name                  = "TVL OAuth2 Proxy"
+  client_id             = "panettone"
+  name                  = "Panettone"
   enabled               = true
   access_type           = "CONFIDENTIAL"
   standard_flow_enabled = true
 
   valid_redirect_uris = [
-    "https://login.tvl.fyi/oauth2/callback",
-    "http://localhost:4774/oauth2/callback",
+    "https://b.tvl.fyi/auth",
+    "http://localhost:6161/auth",
   ]
 }
-
-resource "keycloak_openid_audience_protocol_mapper" "oauth2_proxy_audience" {
-  realm_id                 = keycloak_realm.tvl.id
-  client_id                = keycloak_openid_client.oauth2_proxy.id
-  name                     = "oauth2-proxy-audience"
-  included_custom_audience = keycloak_openid_client.oauth2_proxy.client_id
-}
diff --git a/ops/keycloak/default.nix b/ops/keycloak/default.nix
index 96f0c40e5e..94ed912dc9 100644
--- a/ops/keycloak/default.nix
+++ b/ops/keycloak/default.nix
@@ -1,8 +1,14 @@
-{ depot, pkgs, ... }:
+{ depot, lib, pkgs, ... }:
 
-depot.nix.readTree.drvTargets {
+depot.nix.readTree.drvTargets rec {
   # Provide a Terraform wrapper with the right provider installed.
-  terraform = pkgs.terraform.withPlugins(p: [
+  terraform = pkgs.terraform.withPlugins (p: [
     p.keycloak
   ]);
+
+  validate = depot.tools.checks.validateTerraform {
+    inherit terraform;
+    name = "keycloak";
+    src = lib.cleanSource ./.;
+  };
 }
diff --git a/ops/keycloak/main.tf b/ops/keycloak/main.tf
index 819267ff96..923ac19397 100644
--- a/ops/keycloak/main.tf
+++ b/ops/keycloak/main.tf
@@ -1,6 +1,6 @@
 # Configure TVL Keycloak instance.
 #
-# TODO(tazjin): Configure GitHub/GitLab IDP
+# TODO(tazjin): Configure GitLab IDP
 
 terraform {
   required_providers {
@@ -31,4 +31,14 @@ resource "keycloak_realm" "tvl" {
   enabled                     = true
   display_name                = "The Virus Lounge"
   default_signature_algorithm = "RS256"
+
+  smtp_server {
+    from              = "tvlbot@tazj.in"
+    from_display_name = "The Virus Lounge"
+    host              = "127.0.0.1"
+    port              = "25"
+    reply_to          = "depot@tvl.su"
+    ssl               = false
+    starttls          = false
+  }
 }
diff --git a/ops/keycloak/user_sources.tf b/ops/keycloak/user_sources.tf
index 3fde6e07cc..01307fff8d 100644
--- a/ops/keycloak/user_sources.tf
+++ b/ops/keycloak/user_sources.tf
@@ -2,6 +2,10 @@
 # information (either by accessing a system like LDAP or integration
 # through protocols like OIDC).
 
+variable "github_client_secret" {
+  type = string
+}
+
 resource "keycloak_ldap_user_federation" "tvl_ldap" {
   name                    = "tvl-ldap"
   realm_id                = keycloak_realm.tvl.id
@@ -19,3 +23,22 @@ resource "keycloak_ldap_user_federation" "tvl_ldap" {
     "organizationalPerson",
   ]
 }
+
+# keycloak_oidc_identity_provider.github will be destroyed
+# (because keycloak_oidc_identity_provider.github is not in configuration)
+resource "keycloak_oidc_identity_provider" "github" {
+  alias                 = "github"
+  provider_id           = "github"
+  client_id             = "6d7f8bb2e82bb6739556"
+  client_secret         = var.github_client_secret
+  realm                 = keycloak_realm.tvl.id
+  backchannel_supported = false
+  gui_order             = "1"
+  store_token           = false
+  sync_mode             = "IMPORT"
+  trust_email           = true
+
+  # These default to built-in values for the `github` provider_id.
+  authorization_url = ""
+  token_url         = ""
+}
diff --git a/ops/kontemplate/release.nix b/ops/kontemplate/release.nix
index 8a04109526..6a3dbd5efe 100644
--- a/ops/kontemplate/release.nix
+++ b/ops/kontemplate/release.nix
@@ -10,13 +10,17 @@
 # This file is the Nix derivation used to build release binaries for
 # several different architectures and operating systems.
 
-let pkgs = import ((import <nixpkgs> {}).fetchFromGitHub {
-  owner = "NixOS";
-  repo = "nixpkgs-channels";
-  rev = "541d9cce8af7a490fb9085305939569567cb58e6";
-  sha256 = "0jgz72hhzkd5vyq5v69vpljjlnf0lqaz7fh327bvb3cvmwbfxrja";
-}) {};
-in with pkgs; buildGoPackage rec {
+let
+  pkgs = import
+    ((import <nixpkgs> { }).fetchFromGitHub {
+      owner = "NixOS";
+      repo = "nixpkgs-channels";
+      rev = "541d9cce8af7a490fb9085305939569567cb58e6";
+      sha256 = "0jgz72hhzkd5vyq5v69vpljjlnf0lqaz7fh327bvb3cvmwbfxrja";
+    })
+    { };
+in
+with pkgs; buildGoPackage rec {
   name = "kontemplate-${version}";
   version = "canon";
   src = ./.;
@@ -29,8 +33,8 @@ in with pkgs; buildGoPackage rec {
   # reason for setting the 'allowGoReference' flag.
   dontStrip = true; # Linker configuration handles stripping
   allowGoReference = true;
-  CGO_ENABLED="0";
-  GOCACHE="off";
+  CGO_ENABLED = "0";
+  GOCACHE = "off";
 
   # Configure release builds via the "build-matrix" script:
   buildInputs = [ git ];
diff --git a/ops/machines/all-systems.nix b/ops/machines/all-systems.nix
index df1cfa6a48..c4382fbddb 100644
--- a/ops/machines/all-systems.nix
+++ b/ops/machines/all-systems.nix
@@ -1,6 +1,7 @@
 { depot, ... }:
 
 (with depot.ops.machines; [
+  sanduny
   whitby
 ]) ++
 
@@ -8,13 +9,19 @@
   camden
   frog
   tverskoy
+  zamalek
 ]) ++
 
-(with depot.users.grfn.system.system; [
+(with depot.users.aspen.system.system; [
   yeren
   mugwump
+  ogopogo
+  lusca
 ]) ++
 
 (with depot.users.wpcarro.nixos; [
+  ava
+  kyoko
   marcus
+  tarasco
 ])
diff --git a/ops/machines/nixery-01/default.nix b/ops/machines/nixery-01/default.nix
new file mode 100644
index 0000000000..c99db214d8
--- /dev/null
+++ b/ops/machines/nixery-01/default.nix
@@ -0,0 +1,40 @@
+# nixery.dev backing host in ru-central1-b
+{ depot, lib, pkgs, ... }: # readTree options
+{ config, ... }: # passed by module system
+
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+in
+{
+  imports = [
+    (mod "known-hosts.nix")
+    (mod "nixery.nix")
+    (mod "tvl-users.nix")
+    (mod "www/nixery.dev.nix")
+    (mod "yandex-cloud.nix")
+
+    (depot.third_party.agenix.src + "/modules/age.nix")
+  ];
+
+  networking = {
+    hostName = "nixery-01";
+    domain = "tvl.fyi";
+    firewall.allowedTCPPorts = [ 22 80 443 ];
+  };
+
+  security.sudo.extraRules = lib.singleton {
+    groups = [ "wheel" ];
+    commands = [{ command = "ALL"; options = [ "NOPASSWD" ]; }];
+  };
+
+  services.depot.nixery.enable = true;
+
+  # Automatically collect garbage from the Nix store.
+  services.depot.automatic-gc = {
+    enable = true;
+    interval = "1 hour";
+    diskThreshold = 25; # GiB
+    maxFreed = 150; # GiB
+    preserveGenerations = "30d";
+  };
+}
diff --git a/ops/machines/sanduny/default.nix b/ops/machines/sanduny/default.nix
new file mode 100644
index 0000000000..af2dfb02a5
--- /dev/null
+++ b/ops/machines/sanduny/default.nix
@@ -0,0 +1,138 @@
+# sanduny.tvl.su
+#
+# This is a VPS hosted with Bitfolk, intended to additionally serve
+# some of our public services like cgit, josh and the websites.
+#
+# In case of whitby going down, sanduny will keep depot available.
+
+_: # ignore readTree options
+
+{ config, depot, lib, pkgs, ... }:
+
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+in
+{
+  imports = [
+    (mod "cgit.nix")
+    (mod "depot-inbox.nix")
+    (mod "depot-replica.nix")
+    (mod "journaldriver.nix")
+    (mod "known-hosts.nix")
+    (mod "tvl-cache.nix")
+    (mod "tvl-headscale.nix")
+    (mod "tvl-users.nix")
+    (mod "www/inbox.tvl.su.nix")
+    (mod "www/self-redirect.nix")
+    (mod "www/volgasprint.org.nix")
+  ];
+
+  networking = {
+    hostName = "sanduny";
+    domain = "tvl.su";
+    useDHCP = false;
+
+    interfaces.eth0 = {
+      ipv4.addresses = lib.singleton {
+        address = "85.119.82.231";
+        prefixLength = 21;
+      };
+
+      ipv6.addresses = lib.singleton {
+        address = "2001:ba8:1f1:f109::feed:edef:beef";
+        prefixLength = 64;
+      };
+    };
+
+    defaultGateway = "85.119.80.1";
+    defaultGateway6.address = "2001:ba8:1f1:f109::1";
+
+    firewall.allowedTCPPorts = [ 22 80 443 ];
+
+    # https://bitfolk.com/customer_information.html#toc_2_DNS
+    nameservers = [
+      "85.119.80.232"
+      "85.119.80.233"
+      "2001:ba8:1f1:f205::53"
+      "2001:ba8:1f1:f206::53"
+    ];
+  };
+
+  security.sudo.wheelNeedsPassword = false;
+
+  environment.systemPackages = with pkgs; [
+    emacs-nox
+    vim
+    curl
+    unzip
+    htop
+  ];
+
+  programs.mtr.enable = true;
+
+  services.openssh.enable = true;
+  services.fail2ban.enable = true;
+
+  # Run tailscale for the TVL net.tvl.fyi network.
+  # tailscale up --login-server https://net.tvl.fyi --accept-dns=false --advertise-exit-node
+  services.tailscale = {
+    enable = true;
+    useRoutingFeatures = "server"; # for exit-node usage
+  };
+
+  # Automatically collect garbage from the Nix store.
+  services.depot.automatic-gc = {
+    enable = true;
+    interval = "1 hour";
+    diskThreshold = 2; # GiB
+    maxFreed = 5; # GiB
+    preserveGenerations = "90d";
+  };
+
+  # Allow Gerrit to replicate depot to /var/lib/depot
+  services.depot.replica.enable = true;
+
+  # Run git serving tools locally ...
+  services.depot.cgit = {
+    enable = true;
+    repo = "/var/lib/depot";
+  };
+
+  # Serve public-inbox ...
+  services.depot.inbox.enable = true;
+
+  time.timeZone = "UTC";
+
+  # GRUB does not actually need to be installed on disk; Bitfolk have
+  # their own way of booting systems as long as config is in place.
+  boot.loader.grub.device = "nodev";
+  boot.loader.grub.enable = true;
+  boot.initrd.availableKernelModules = [ "xen_blkfront" ];
+
+  hardware.cpu.intel.updateMicrocode = true;
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/disk/by-uuid/aabc3638-43ca-45f3-af89-c451e8448e92";
+      fsType = "ext4";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/75aa99d5-fed7-4c5c-8570-7745f6cff9f5";
+      fsType = "ext3";
+    };
+
+    "/nix" = {
+      device = "/dev/disk/by-uuid/d1721678-c294-482b-b72e-3b15f2c56c63";
+      fsType = "ext4";
+    };
+  };
+
+  tvl.cache.enable = true;
+
+  swapDevices = lib.singleton {
+    device = "/dev/disk/by-uuid/df4ad9da-0a06-4c27-93e5-5d44e4750e55";
+  };
+
+  system.stateVersion = "22.05"; # Did you read the comment?
+}
diff --git a/ops/machines/whitby/OWNERS b/ops/machines/whitby/OWNERS
index b1b749e871..4581a80d61 100644
--- a/ops/machines/whitby/OWNERS
+++ b/ops/machines/whitby/OWNERS
@@ -1,6 +1,5 @@
-inherited: false
+set noparent
 
 # Want in on this list? Try paying!
-owners:
-  - lukegb
-  - tazjin
+lukegb
+tazjin
diff --git a/ops/machines/whitby/default.nix b/ops/machines/whitby/default.nix
index dbee33aa3f..6a8ee56abc 100644
--- a/ops/machines/whitby/default.nix
+++ b/ops/machines/whitby/default.nix
@@ -4,43 +4,52 @@
 let
   inherit (builtins) listToAttrs;
   inherit (lib) range;
-in {
+
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+in
+{
   imports = [
-    "${depot.path}/ops/modules/atward.nix"
-    "${depot.path}/ops/modules/clbot.nix"
-    "${depot.path}/ops/modules/gerrit-queue.nix"
-    "${depot.path}/ops/modules/git-serving.nix"
-    "${depot.path}/ops/modules/irccat.nix"
-    "${depot.path}/ops/modules/monorepo-gerrit.nix"
-    "${depot.path}/ops/modules/nixery.nix"
-    "${depot.path}/ops/modules/oauth2_proxy.nix"
-    "${depot.path}/ops/modules/owothia.nix"
-    "${depot.path}/ops/modules/panettone.nix"
-    "${depot.path}/ops/modules/paroxysm.nix"
-    "${depot.path}/ops/modules/restic.nix"
-    "${depot.path}/ops/modules/smtprelay.nix"
-    "${depot.path}/ops/modules/sourcegraph.nix"
-    "${depot.path}/ops/modules/tvl-buildkite.nix"
-    "${depot.path}/ops/modules/tvl-slapd/default.nix"
-    "${depot.path}/ops/modules/www/atward.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/auth.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/b.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/cache.tvl.su.nix"
-    "${depot.path}/ops/modules/www/cl.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/code.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/cs.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/deploys.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/images.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/nixery.dev.nix"
-    "${depot.path}/ops/modules/www/static.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/status.tvl.su.nix"
-    "${depot.path}/ops/modules/www/tazj.in.nix"
-    "${depot.path}/ops/modules/www/todo.tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/tvl.fyi.nix"
-    "${depot.path}/ops/modules/www/tvl.su.nix"
-    "${depot.path}/ops/modules/www/wigglydonke.rs.nix"
-    "${depot.third_party.agenix.src}/modules/age.nix"
-    "${pkgs.path}/nixos/modules/services/web-apps/gerrit.nix"
+    (mod "atward.nix")
+    (mod "cgit.nix")
+    (mod "clbot.nix")
+    (mod "gerrit-autosubmit.nix")
+    (mod "irccat.nix")
+    (mod "josh.nix")
+    (mod "journaldriver.nix")
+    (mod "known-hosts.nix")
+    (mod "livegrep.nix")
+    (mod "monorepo-gerrit.nix")
+    (mod "owothia.nix")
+    (mod "panettone.nix")
+    (mod "paroxysm.nix")
+    (mod "restic.nix")
+    (mod "smtprelay.nix")
+    (mod "sourcegraph.nix")
+    (mod "tvl-buildkite.nix")
+    (mod "tvl-slapd/default.nix")
+    (mod "tvl-users.nix")
+    (mod "www/atward.tvl.fyi.nix")
+    (mod "www/auth.tvl.fyi.nix")
+    (mod "www/b.tvl.fyi.nix")
+    (mod "www/cache.tvl.su.nix")
+    (mod "www/cl.tvl.fyi.nix")
+    (mod "www/code.tvl.fyi.nix")
+    (mod "www/cs.tvl.fyi.nix")
+    (mod "www/deploys.tvl.fyi.nix")
+    (mod "www/self-redirect.nix")
+    (mod "www/signup.tvl.fyi.nix")
+    (mod "www/static.tvl.fyi.nix")
+    (mod "www/status.tvl.su.nix")
+    (mod "www/todo.tvl.fyi.nix")
+    (mod "www/tvix.dev.nix")
+    (mod "www/tvl.fyi.nix")
+    (mod "www/tvl.su.nix")
+    (mod "www/wigglydonke.rs.nix")
+
+    # experimental!
+    (mod "www/grep.tvl.fyi.nix")
+
+    (depot.third_party.agenix.src + "/modules/age.nix")
   ];
 
   hardware = {
@@ -49,13 +58,19 @@ in {
   };
 
   boot = {
-    tmpOnTmpfs = true;
+    tmp.useTmpfs = true;
     kernelModules = [ "kvm-amd" ];
     supportedFilesystems = [ "zfs" ];
 
     initrd = {
       availableKernelModules = [
-        "igb" "xhci_pci" "nvme" "ahci" "usbhid" "usb_storage" "sr_mod"
+        "igb"
+        "xhci_pci"
+        "nvme"
+        "ahci"
+        "usbhid"
+        "usb_storage"
+        "sr_mod"
       ];
 
       # Enable SSH in the initrd so that we can enter disk encryption
@@ -68,7 +83,7 @@ in {
           authorizedKeys =
             depot.users.tazjin.keys.all
             ++ depot.users.lukegb.keys.all
-            ++ [ depot.users.grfn.keys.whitby ];
+            ++ [ depot.users.aspen.keys.whitby ];
 
           hostKeys = [
             /etc/secrets/initrd_host_ed25519_key
@@ -89,7 +104,6 @@ in {
 
     loader.grub = {
       enable = true;
-      version = 2;
       efiSupport = true;
       efiInstallAsRemovable = true;
       device = "/dev/disk/by-id/nvme-SAMSUNG_MZQLB1T9HAJR-00007_S439NA0N201620";
@@ -170,26 +184,26 @@ in {
 
   nix = {
     nrBuildUsers = 256;
-    maxJobs = lib.mkDefault 64;
-    extraOptions = ''
-      secret-key-files = /run/agenix/nix-cache-priv
-    '';
-
-    trustedUsers = [
-      "grfn"
-      "lukegb"
-      "tazjin"
-      "sterni"
-    ];
+    settings = {
+      max-jobs = lib.mkDefault 64;
+      secret-key-files = "/run/agenix/nix-cache-priv";
+
+      trusted-users = [
+        "aspen"
+        "lukegb"
+        "tazjin"
+        "sterni"
+      ];
+    };
 
     sshServe = {
       enable = true;
       keys = with depot.users;
         tazjin.keys.all
         ++ lukegb.keys.all
-        ++ [ grfn.keys.whitby ]
+        ++ [ aspen.keys.whitby ]
         ++ sterni.keys.all
-        ;
+      ;
     };
   };
 
@@ -197,22 +211,24 @@ in {
   programs.mosh.enable = true;
   services.openssh = {
     enable = true;
-    passwordAuthentication = false;
-    challengeResponseAuthentication = false;
+    settings = {
+      PasswordAuthentication = false;
+      KbdInteractiveAuthentication = false;
+    };
   };
 
   # Configure secrets for services that need them.
   age.secrets =
     let
       secretFile = name: depot.ops.secrets."${name}.age";
-    in {
+    in
+    {
       clbot.file = secretFile "clbot";
-      gerrit-queue.file = secretFile "gerrit-queue";
+      gerrit-autosubmit.file = secretFile "gerrit-autosubmit";
       grafana.file = secretFile "grafana";
       irccat.file = secretFile "irccat";
       keycloak-db.file = secretFile "keycloak-db";
       nix-cache-priv.file = secretFile "nix-cache-priv";
-      oauth2_proxy.file = secretFile "oauth2_proxy";
       owothia.file = secretFile "owothia";
       panettone.file = secretFile "panettone";
       smtprelay.file = secretFile "smtprelay";
@@ -235,6 +251,12 @@ in {
         group = "buildkite-agents";
       };
 
+      buildkite-private-key = {
+        file = secretFile "buildkite-ssh-private-key";
+        mode = "0440";
+        group = "buildkite-agents";
+      };
+
       gerrit-besadii-config = {
         file = secretFile "besadii";
         owner = "git";
@@ -257,6 +279,14 @@ in {
         file = secretFile "nix-cache-pub";
         mode = "0444";
       };
+
+      depot-replica-key = {
+        file = secretFile "depot-replica-key";
+        mode = "0500";
+        owner = "git";
+        group = "git";
+        path = "/var/lib/git/.ssh/id_ed25519";
+      };
     };
 
   # Automatically collect garbage from the Nix store.
@@ -315,13 +345,13 @@ in {
   # Start the Gerrit->IRC bot
   services.depot.clbot = {
     enable = true;
-    channels = [ "#tvl" ];
+    channels = [ "#tvix-dev" "#tvl" ];
 
     # See //fun/clbot for details.
     flags = {
       gerrit_host = "cl.tvl.fyi:29418";
       gerrit_ssh_auth_username = "clbot";
-      gerrit_ssh_auth_key = "/run/agenix/clbot-ssh";
+      gerrit_ssh_auth_key = config.age.secretsDir + "/clbot-ssh";
 
       irc_server = "localhost:${toString config.services.znc.config.Listener.l.Port}";
       irc_user = "tvlbot";
@@ -340,6 +370,9 @@ in {
     # Run a SourceGraph code search instance
     sourcegraph.enable = true;
 
+    # Run a livegrep code search instance
+    livegrep.enable = true;
+
     # Run the Panettone issue tracker
     panettone = {
       enable = true;
@@ -380,11 +413,13 @@ in {
     # Run atward, the search engine redirection thing.
     atward.enable = true;
 
-    # Run a Nixery instance
-    nixery.enable = true;
-
     # Run cgit & josh to serve git
-    git-serving.enable = true;
+    cgit = {
+      enable = true;
+      user = "git"; # run as the same user as gerrit
+    };
+
+    josh.enable = true;
 
     # Configure backups to GleSYS
     restic = {
@@ -397,15 +432,13 @@ in {
     };
 
     # Run autosubmit bot for Gerrit
-    gerrit-queue.enable = true;
-
-    # Run oauth2_proxy for internal service auth
-    oauth2_proxy.enable = true;
+    gerrit-autosubmit.enable = true;
   };
 
   services.postgresql = {
     enable = true;
     enableTCPIP = true;
+    package = pkgs.postgresql_16;
 
     authentication = lib.mkForce ''
       local all all trust
@@ -421,9 +454,7 @@ in {
 
     ensureUsers = [{
       name = "panettone";
-      ensurePermissions = {
-        "DATABASE panettone" = "ALL PRIVILEGES";
-      };
+      ensureDBOwnership = true;
     }];
   };
 
@@ -439,30 +470,26 @@ in {
   services.nix-serve = {
     enable = true;
     port = 6443;
-    secretKeyFile = "/run/agenix/nix-cache-priv";
+    secretKeyFile = config.age.secretsDir + "/nix-cache-priv";
     bindAddress = "localhost";
   };
 
   services.fail2ban.enable = true;
 
   environment.systemPackages = (with pkgs; [
-    alacritty.terminfo
     bat
     bb
     curl
     direnv
     emacs-nox
     fd
-    foot.terminfo
     git
     htop
     hyperfine
     jq
-    kitty.terminfo
     nano
     nvd
     ripgrep
-    rxvt_unicode.terminfo
     tree
     unzip
     vim
@@ -472,98 +499,97 @@ in {
     ops.deploy-whitby
   ]);
 
-  services.journaldriver = {
-    enable = true;
-    googleCloudProject = "tvl-fyi";
-    logStream = "whitby";
-    applicationCredentials = "/var/lib/journaldriver/key.json";
-  };
+  # Required for prometheus to be able to scrape stats
+  services.nginx.statusPage = true;
 
   # Configure Prometheus & Grafana. Exporter configuration for
   # Prometheus is inside the respective service modules.
   services.prometheus = {
     enable = true;
-    exporters.node = {
-      enable = true;
+    retentionTime = "90d";
 
-      enabledCollectors = [
-        "logind"
-        "processes"
-        "systemd"
-      ];
+    exporters = {
+      node = {
+        enable = true;
+
+        enabledCollectors = [
+          "logind"
+          "processes"
+          "systemd"
+        ];
+      };
+
+      nginx = {
+        enable = true;
+        sslVerify = false;
+        constLabels = [ "host=whitby" ];
+      };
     };
 
     scrapeConfigs = [{
       job_name = "node";
       scrape_interval = "5s";
       static_configs = [{
-        targets = ["localhost:${toString config.services.prometheus.exporters.node.port}"];
+        targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ];
+      }];
+    }
+      {
+        job_name = "nginx";
+        scrape_interval = "5s";
+        static_configs = [{
+          targets = [ "localhost:${toString config.services.prometheus.exporters.nginx.port}" ];
+        }];
       }];
-    }];
   };
 
   services.grafana = {
     enable = true;
-    port = 4723; # "graf" on phone keyboard
-    domain = "status.tvl.su";
-    rootUrl = "https://status.tvl.su";
-    analytics.reporting.enable = false;
-    extraOptions = let
-      options = {
-        auth = {
-          generic_oauth = {
-            enabled = true;
-            client_id = "grafana";
-            scopes = "openid profile email";
-            name = "TVL";
-            email_attribute_path = "mail";
-            login_attribute_path = "sub";
-            name_attribute_path = "displayName";
-            auth_url = "https://auth.tvl.fyi/auth/realms/TVL/protocol/openid-connect/auth";
-            token_url = "https://auth.tvl.fyi/auth/realms/TVL/protocol/openid-connect/token";
-            api_url = "https://auth.tvl.fyi/auth/realms/TVL/protocol/openid-connect/userinfo";
-
-            # Give lukegb, grfn, tazjin "Admin" rights.
-            role_attribute_path = "((sub == 'lukegb' || sub == 'grfn' || sub == 'tazjin') && 'Admin') || 'Editor'";
-
-            # Allow creating new Grafana accounts from OAuth accounts.
-            allow_sign_up = true;
-          };
-
-          anonymous = {
-            enabled = true;
-            org_name = "The Virus Lounge";
-            org_role = "Viewer";
-          };
-
-          basic.enabled = false;
-          oauth_auto_login = true;
-          disable_login_form = true;
-        };
+
+    settings = {
+      server = {
+        http_port = 4723; # "graf" on phone keyboard
+        domain = "status.tvl.su";
+        root_url = "https://status.tvl.su";
+      };
+
+      analytics.reporting_enabled = false;
+
+      "auth.generic_oauth" = {
+        enabled = true;
+        client_id = "grafana";
+        scopes = "openid profile email";
+        name = "TVL";
+        email_attribute_path = "mail";
+        login_attribute_path = "sub";
+        name_attribute_path = "displayName";
+        auth_url = "https://auth.tvl.fyi/auth/realms/TVL/protocol/openid-connect/auth";
+        token_url = "https://auth.tvl.fyi/auth/realms/TVL/protocol/openid-connect/token";
+        api_url = "https://auth.tvl.fyi/auth/realms/TVL/protocol/openid-connect/userinfo";
+
+        # Give lukegb, aspen, tazjin "Admin" rights.
+        role_attribute_path = "((sub == 'lukegb' || sub == 'aspen' || sub == 'tazjin') && 'Admin') || 'Editor'";
+
+        # Allow creating new Grafana accounts from OAuth accounts.
+        allow_sign_up = true;
       };
-      inherit (builtins) typeOf replaceStrings listToAttrs concatLists;
-      inherit (lib) toUpper mapAttrsToList nameValuePair concatStringsSep;
-
-      # Take ["auth" "generic_oauth" "enabled"] and turn it into OPTIONS_GENERIC_OAUTH_ENABLED.
-      encodeName = raw: replaceStrings ["."] ["_"] (toUpper (concatStringsSep "_" raw));
-
-      # Turn an option value into a string, but we want bools to be sensible strings and not "1" or "".
-      optionToString = value:
-        if (typeOf value) == "bool" then
-          if value then "true" else "false"
-        else builtins.toString value;
-
-      # Turn an nested options attrset into a flat listToAttrs-compatible list.
-      encodeOptions = prefix: inp: concatLists (mapAttrsToList (name: value:
-        if (typeOf value) == "set"
-          then encodeOptions (prefix ++ [name]) value
-          else [ (nameValuePair (encodeName (prefix ++ [name])) (optionToString value)) ]
-        ) inp);
-    in listToAttrs (encodeOptions [] options);
+
+      "auth.anonymous" = {
+        enabled = true;
+        org_name = "The Virus Lounge";
+        org_role = "Viewer";
+      };
+
+      "auth.basic".enabled = false;
+
+      auth = {
+        oauth_auto_login = true;
+        disable_login_form = true;
+      };
+    };
 
     provision = {
       enable = true;
-      datasources = [{
+      datasources.settings.datasources = [{
         name = "Prometheus";
         type = "prometheus";
         url = "http://localhost:9090";
@@ -572,30 +598,29 @@ in {
   };
 
   # Contains GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET.
-  systemd.services.grafana.serviceConfig.EnvironmentFile = "/run/agenix/grafana";
+  systemd.services.grafana.serviceConfig.EnvironmentFile = config.age.secretsDir + "/grafana";
 
   services.keycloak = {
     enable = true;
-    httpPort = "5925"; # "kycl"
-    frontendUrl = "https://auth.tvl.fyi/auth/";
+
+    settings = {
+      http-port = 5925; # kycl
+      hostname = "auth.tvl.fyi";
+      http-relative-path = "/auth";
+      proxy = "edge";
+    };
 
     database = {
       type = "postgresql";
-      passwordFile = "/run/agenix/keycloak-db";
+      passwordFile = config.age.secretsDir + "/keycloak-db";
       createLocally = false;
     };
+  };
 
-    # Configure Keycloak to look at forwarded headers from the reverse
-    # proxy.
-    extraConfig = {
-      "subsystem=undertow" = {
-        "server=default-server" = {
-          "http-listener=default" = {
-            proxy-address-forwarding = "true";
-          };
-        };
-      };
-    };
+  # Join TVL Tailscale network at net.tvl.fyi
+  services.tailscale = {
+    enable = true;
+    useRoutingFeatures = "server"; # for exit-node usage
   };
 
   # Allow Keycloak access to the LDAP module by forcing in the JVM
@@ -605,89 +630,14 @@ in {
 
   security.sudo.extraRules = [
     {
-      groups = ["wheel"];
-      commands = [{ command = "ALL"; options = ["NOPASSWD"]; }];
+      groups = [ "wheel" ];
+      commands = [{ command = "ALL"; options = [ "NOPASSWD" ]; }];
     }
   ];
 
   users = {
-    users.tazjin = {
-      isNormalUser = true;
-      extraGroups = [ "git" "wheel" ];
-      shell = pkgs.fish;
-      openssh.authorizedKeys.keys = depot.users.tazjin.keys.all;
-    };
-
-    users.lukegb = {
-      isNormalUser = true;
-      extraGroups = [ "git" "wheel" ];
-      openssh.authorizedKeys.keys = depot.users.lukegb.keys.all;
-    };
-
-    users.grfn = {
-      isNormalUser = true;
-      extraGroups = [ "git" "wheel" ];
-      openssh.authorizedKeys.keys = [
-        depot.users.grfn.keys.whitby
-      ];
-    };
-
-    users.isomer = {
-      isNormalUser = true;
-      extraGroups = [ "git" ];
-      openssh.authorizedKeys.keys = depot.users.isomer.keys.all;
-    };
-
-    users.riking = {
-      isNormalUser = true;
-      extraGroups = [ "git" ];
-      openssh.authorizedKeys.keys = depot.users.riking.keys.u2f ++ depot.users.riking.keys.passworded;
-    };
-
-    users.edef = {
-      isNormalUser = true;
-      extraGroups = [ "git" ];
-      openssh.authorizedKeys.keys = depot.users.edef.keys.all;
-    };
-
-    users.qyliss = {
-      isNormalUser = true;
-      extraGroups = [ "git" ];
-      openssh.authorizedKeys.keys = depot.users.qyliss.keys.all;
-    };
-
-    users.eta = {
-      isNormalUser = true;
-      extraGroups = [ "git" ];
-      openssh.authorizedKeys.keys = depot.users.eta.keys.whitby;
-    };
-
-    users.cynthia = {
-      isNormalUser = true; # I'm normal OwO :3
-      extraGroups = [ "git" ];
-      openssh.authorizedKeys.keys = depot.users.cynthia.keys.all;
-    };
-
-    users.firefly = {
-      isNormalUser = true;
-      extraGroups = [ "git" ];
-      openssh.authorizedKeys.keys = depot.users.firefly.keys.whitby;
-    };
-
-    users.sterni = {
-      isNormalUser = true;
-      extraGroups = [ "git" "wheel" ];
-      openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
-    };
-
-    users.flokli = {
-      isNormalUser = true;
-      extraGroups = [ "git" ];
-      openssh.authorizedKeys.keys = depot.users.flokli.keys.all;
-    };
-
     # Set up a user & group for git shenanigans
-    groups.git = {};
+    groups.git = { };
     users.git = {
       group = "git";
       isSystemUser = true;
@@ -696,10 +646,7 @@ in {
     };
   };
 
-  security.acme = {
-    acceptTerms = true;
-    defaults.email = "certs@tvl.fyi";
-  };
+  zramSwap.enable = true;
 
   system.stateVersion = "20.03";
 }
diff --git a/ops/modules/atward.nix b/ops/modules/atward.nix
index 354f9ebdd3..f345a08e31 100644
--- a/ops/modules/atward.nix
+++ b/ops/modules/atward.nix
@@ -3,7 +3,8 @@
 let
   cfg = config.services.depot.atward;
   description = "atward - (attempt to) cleverly route queries";
-in {
+in
+{
   options.services.depot.atward = {
     enable = lib.mkEnableOption description;
 
diff --git a/ops/modules/auto-deploy.nix b/ops/modules/auto-deploy.nix
index 83a8273562..c504906b2b 100644
--- a/ops/modules/auto-deploy.nix
+++ b/ops/modules/auto-deploy.nix
@@ -45,7 +45,8 @@ let
     # NixOS in $STATE_DIRECTORY
     (cd / && ${rebuild-system}/bin/rebuild-system)
   '';
-in {
+in
+{
   options.services.depot.auto-deploy = {
     enable = lib.mkEnableOption description;
 
diff --git a/ops/modules/automatic-gc.nix b/ops/modules/automatic-gc.nix
index 6347857210..003f160919 100644
--- a/ops/modules/automatic-gc.nix
+++ b/ops/modules/automatic-gc.nix
@@ -13,6 +13,11 @@ let
   gcScript = pkgs.writeShellScript "automatic-nix-gc" ''
     set -ueo pipefail
 
+    if [ -e /run/stop-automatic-gc ]; then
+      echo "GC is disabled through /run/stop-automatic-gc"
+      exit 0
+    fi
+
     readonly MIN_THRESHOLD_KIB="${toString (GiBtoKiB cfg.diskThreshold)}"
     readonly MAX_FREED_BYTES="${toString (GiBtoBytes cfg.maxFreed)}"
     readonly GEN_THRESHOLD="${cfg.preserveGenerations}"
@@ -29,7 +34,8 @@ let
       echo "Skipping GC, enough space available"
     fi
   '';
-in {
+in
+{
   options.services.depot.automatic-gc = {
     enable = lib.mkEnableOption description;
 
diff --git a/ops/modules/btrfs-auto-scrub.nix b/ops/modules/btrfs-auto-scrub.nix
new file mode 100644
index 0000000000..748bb75c5f
--- /dev/null
+++ b/ops/modules/btrfs-auto-scrub.nix
@@ -0,0 +1,25 @@
+# Automatically performs a scrub on all btrfs filesystems configured in
+# `config.fileSystems` on a daily schedule (by default). Activated by importing.
+{ config, lib, ... }:
+
+{
+  config = {
+    services = {
+      btrfs.autoScrub = {
+        enable = true;
+        interval = lib.mkDefault "*-*-* 03:30:00";
+        # gather all btrfs fileSystems, extra ones can be added via the NixOS
+        # module merging mechanism, of course.
+        fileSystems = lib.concatLists (
+          lib.mapAttrsToList
+            (
+              _:
+              { fsType, mountPoint, ... }:
+              if fsType == "btrfs" then [ mountPoint ] else [ ]
+            )
+            config.fileSystems
+        );
+      };
+    };
+  };
+}
diff --git a/ops/modules/cgit.nix b/ops/modules/cgit.nix
new file mode 100644
index 0000000000..fc3f171585
--- /dev/null
+++ b/ops/modules/cgit.nix
@@ -0,0 +1,55 @@
+# Configuration for running the TVL cgit instance using thttpd.
+{ config, depot, lib, pkgs, ... }:
+
+let
+  cfg = config.services.depot.cgit;
+
+  userConfig =
+    if builtins.isNull cfg.user then {
+      DynamicUser = true;
+    } else {
+      User = cfg.user;
+      Group = cfg.user;
+    };
+in
+{
+  options.services.depot.cgit = with lib; {
+    enable = mkEnableOption "Run cgit web interface for depot";
+
+    port = mkOption {
+      description = "Port on which cgit should listen";
+      type = types.int;
+      default = 2448;
+    };
+
+    repo = mkOption {
+      description = "Path to depot's .git folder on the machine";
+      type = types.str;
+      default = "/var/lib/gerrit/git/depot.git/";
+    };
+
+    user = mkOption {
+      description = ''
+        User to use for the cgit service. It is expected that this is
+        also the name of the user's primary group.
+      '';
+
+      type = with types; nullOr str;
+      default = null;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.cgit = {
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Restart = "on-failure";
+
+        ExecStart = depot.web.cgit-tvl.override {
+          inherit (cfg) port repo;
+        };
+      } // userConfig;
+    };
+  };
+}
diff --git a/ops/modules/clbot.nix b/ops/modules/clbot.nix
index ef4c2ab237..bdddff6c81 100644
--- a/ops/modules/clbot.nix
+++ b/ops/modules/clbot.nix
@@ -3,7 +3,7 @@
 
 let
   inherit (builtins) attrValues concatStringsSep mapAttrs readFile;
-  inherit (pkgs) runCommandNoCC;
+  inherit (pkgs) runCommand;
 
   inherit (lib)
     listToAttrs
@@ -21,7 +21,7 @@ let
       (attrValues (mapAttrs (key: value: "-${key} \"${toString value}\"") flags));
 
   # Escapes a unit name for use in systemd
-  systemdEscape = name: removeSuffix "\n" (readFile (runCommandNoCC "unit-name" {} ''
+  systemdEscape = name: removeSuffix "\n" (readFile (runCommand "unit-name" { } ''
     ${pkgs.systemd}/bin/systemd-escape '${name}' >> $out
   ''));
 
@@ -42,7 +42,8 @@ let
       };
     };
   };
-in {
+in
+{
   options.services.depot.clbot = {
     enable = mkEnableOption description;
 
@@ -59,7 +60,7 @@ in {
     secretsFile = mkOption {
       type = types.str;
       description = "EnvironmentFile from which to load secrets";
-      default = "/run/agenix/clbot";
+      default = config.age.secretsDir + "/clbot";
     };
   };
 
@@ -68,7 +69,7 @@ in {
     # (notably the SSH private key) readable by this user outside of
     # the module.
     users = {
-      groups.clbot = {};
+      groups.clbot = { };
 
       users.clbot = {
         group = "clbot";
diff --git a/ops/modules/default.nix b/ops/modules/default.nix
index 8bdfecdf41..d747e8e131 100644
--- a/ops/modules/default.nix
+++ b/ops/modules/default.nix
@@ -1,2 +1,2 @@
 # Make readTree happy at this level.
-_: {}
+_: { }
diff --git a/ops/modules/depot-inbox.nix b/ops/modules/depot-inbox.nix
new file mode 100644
index 0000000000..14fc646a9a
--- /dev/null
+++ b/ops/modules/depot-inbox.nix
@@ -0,0 +1,148 @@
+# public-inbox configuration for depot@tvl.su
+#
+# The account itself is a Yandex 360 account in the tvl.su organisation, which
+# is accessed via IMAP. Yandex takes care of spam filtering for us, so there is
+# no particular SpamAssassin or other configuration.
+{ config, depot, lib, pkgs, ... }:
+
+let
+  cfg = config.services.depot.inbox;
+
+  imapConfig = pkgs.writeText "offlineimaprc" ''
+    [general]
+    accounts = depot
+
+    [Account depot]
+    localrepository = Local
+    remoterepository = Remote
+
+    [Repository Local]
+    type = Maildir
+    localfolders = /var/lib/public-inbox/depot-imap
+
+    [Repository Remote]
+    type = IMAP
+    ssl = yes
+    sslcacertfile = /etc/ssl/certs/ca-bundle.crt
+    remotehost = imap.yandex.ru
+    remoteuser = depot@tvl.su
+    remotepassfile = /var/run/agenix/depot-inbox-imap
+  '';
+in
+{
+  options.services.depot.inbox = with lib; {
+    enable = mkEnableOption "Enable public-inbox for depot@tvl.su";
+
+    depotPath = mkOption {
+      description = "path to local depot replica";
+      type = types.str;
+      default = "/var/lib/depot";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    # Having nginx *and* other services use ACME certificates for the
+    # same hostname is unsupported in NixOS without resorting to doing
+    # all ACME configuration manually.
+    #
+    # To work around this, we duplicate the TLS certificate used by
+    # nginx to a location that is readable by public-inbox daemons.
+    systemd.services.inbox-cert-sync = {
+      startAt = "daily";
+
+      script = ''
+        ${pkgs.coreutils}/bin/install -D -g ${config.users.groups."public-inbox".name} -m 0440 \
+          /var/lib/acme/inbox.tvl.su/fullchain.pem /var/lib/public-inbox/tls/fullchain.pem
+
+        ${pkgs.coreutils}/bin/install -D -g ${config.users.groups."public-inbox".name} -m 0440 \
+          /var/lib/acme/inbox.tvl.su/key.pem /var/lib/public-inbox/tls/key.pem
+      '';
+    };
+
+    services.public-inbox = {
+      enable = true;
+
+      http.enable = true;
+      http.port = 8053;
+
+      imap = {
+        enable = true;
+        port = 993;
+        cert = "/var/lib/public-inbox/tls/fullchain.pem";
+        key = "/var/lib/public-inbox/tls/key.pem";
+      };
+
+      nntp = {
+        enable = true;
+        port = 563;
+        cert = "/var/lib/public-inbox/tls/fullchain.pem";
+        key = "/var/lib/public-inbox/tls/key.pem";
+      };
+
+      inboxes.depot = rec {
+        address = [
+          "depot@tvl.su" # primary address
+          "depot@tazj.in" # legacy address
+        ];
+
+        description = "TVL depot development (mail to depot@tvl.su)";
+        coderepo = [ "depot" ];
+        url = "https://inbox.tvl.su/depot";
+
+        watch = [
+          "maildir:/var/lib/public-inbox/depot-imap/INBOX/"
+        ];
+
+        newsgroup = "su.tvl.depot";
+      };
+
+      settings.coderepo.depot = {
+        dir = cfg.depotPath;
+        cgitUrl = "https://code.tvl.fyi";
+      };
+
+      settings.publicinbox = {
+        wwwlisting = "all";
+        nntpserver = [ "inbox.tvl.su" ];
+        imapserver = [ "inbox.tvl.su" ];
+
+        depot.obfuscate = true;
+        noObfuscate = [
+          "tvl.su"
+          "tvl.fyi"
+        ];
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = [
+      993 # imap
+      563 # nntp
+    ];
+
+    age.secrets.depot-inbox-imap = {
+      file = depot.ops.secrets."depot-inbox-imap.age";
+      mode = "0440";
+      group = config.users.groups."public-inbox".name;
+    };
+
+    systemd.services.offlineimap-depot = {
+      description = "download mail for depot@tvl.su";
+      wantedBy = [ "multi-user.target" ];
+      startAt = "minutely";
+
+      script = ''
+        mkdir -p /var/lib/public-inbox/depot-imap
+        ${pkgs.offlineimap}/bin/offlineimap -c ${imapConfig}
+      '';
+
+      serviceConfig = {
+        Type = "oneshot";
+
+        # Run in the same user context as public-inbox itself to avoid
+        # permissions trouble.
+        User = config.users.users."public-inbox".name;
+        Group = config.users.groups."public-inbox".name;
+      };
+    };
+  };
+}
diff --git a/ops/modules/depot-replica.nix b/ops/modules/depot-replica.nix
new file mode 100644
index 0000000000..b71f10409a
--- /dev/null
+++ b/ops/modules/depot-replica.nix
@@ -0,0 +1,45 @@
+# Configuration for receiving a depot replica from Gerrit's
+# replication plugin.
+#
+# This only prepares the user and folder for receiving the replica,
+# but Gerrit configuration still needs to be modified in addition.
+{ config, depot, lib, pkgs, ... }:
+
+let
+  cfg = config.services.depot.replica;
+in
+{
+  options.services.depot.replica = with lib; {
+    enable = mkEnableOption "Receive depot git replica from Gerrit";
+
+    key = mkOption {
+      description = "Public key to use for replication";
+      type = types.str;
+      default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFFab9O1xaQ1TCyn+CxmXHexdlLzURREG+UR3Qdi3BvH";
+    };
+
+    path = mkOption {
+      description = "Replication destination path (will be created)";
+      type = types.str;
+      default = "/var/lib/depot";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    users.groups.depot = { };
+
+    users.users.depot = {
+      group = "depot";
+      isSystemUser = true;
+      createHome = true;
+      home = cfg.path;
+      homeMode = "755"; # everyone can read depot
+      openssh.authorizedKeys.keys = lib.singleton cfg.key;
+      shell = pkgs.bashInteractive; # gerrit needs to run shell commands
+    };
+
+    environment.systemPackages = [
+      pkgs.git
+    ];
+  };
+}
diff --git a/ops/modules/gerrit-autosubmit.nix b/ops/modules/gerrit-autosubmit.nix
new file mode 100644
index 0000000000..34342c8d55
--- /dev/null
+++ b/ops/modules/gerrit-autosubmit.nix
@@ -0,0 +1,43 @@
+# Configuration for the Gerrit autosubmit bot (//ops/gerrit-autosubmit)
+{ depot, pkgs, config, lib, ... }:
+
+let
+  cfg = config.services.depot.gerrit-autosubmit;
+  description = "gerrit-autosubmit - autosubmit bot for Gerrit";
+  mkStringOption = default: lib.mkOption {
+    inherit default;
+    type = lib.types.str;
+  };
+in
+{
+  options.services.depot.gerrit-autosubmit = {
+    enable = lib.mkEnableOption description;
+    gerritUrl = mkStringOption "https://cl.tvl.fyi";
+
+    secretsFile = with lib; mkOption {
+      description = "Path to a systemd EnvironmentFile containing secrets";
+      default = config.age.secretsDir + "/gerrit-autosubmit";
+      type = types.str;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.gerrit-autosubmit = {
+      inherit description;
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+
+      serviceConfig = {
+        ExecStart = "${depot.ops.gerrit-autosubmit}/bin/gerrit-autosubmit";
+        DynamicUser = true;
+        Restart = "always";
+        EnvironmentFile = cfg.secretsFile;
+      };
+
+      environment = {
+        GERRIT_URL = cfg.gerritUrl;
+      };
+    };
+  };
+}
diff --git a/ops/modules/gerrit-queue.nix b/ops/modules/gerrit-queue.nix
deleted file mode 100644
index a4b073f856..0000000000
--- a/ops/modules/gerrit-queue.nix
+++ /dev/null
@@ -1,51 +0,0 @@
-# Configuration for the Gerrit autosubmit bot (//third_party/gerrit-queue)
-{ depot, pkgs, config, lib, ... }:
-
-let
-  cfg = config.services.depot.gerrit-queue;
-  description = "gerrit-queue - autosubmit bot for Gerrit";
-  mkStringOption = default: lib.mkOption {
-    inherit default;
-    type = lib.types.str;
-  };
-in {
-  options.services.depot.gerrit-queue = {
-    enable = lib.mkEnableOption description;
-    gerritUrl = mkStringOption "https://cl.tvl.fyi";
-    gerritProject = mkStringOption "depot";
-    gerritBranch = mkStringOption "canon";
-
-    interval = with lib; mkOption {
-      type = types.int;
-      default = 60;
-      description = "Interval (in seconds) for submit queue checks";
-    };
-
-    secretsFile = with lib; mkOption {
-      description = "Path to a systemd EnvironmentFile containing secrets";
-      default = "/run/agenix/gerrit-queue";
-      type = types.str;
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    systemd.services.gerrit-queue = {
-      inherit description;
-      wantedBy = [ "multi-user.target" ];
-
-      serviceConfig = {
-        ExecStart = "${depot.third_party.gerrit-queue}/bin/gerrit-queue";
-        DynamicUser = true;
-        Restart = "always";
-        EnvironmentFile = cfg.secretsFile;
-      };
-
-      environment = {
-        GERRIT_URL = cfg.gerritUrl;
-        GERRIT_PROJECT = cfg.gerritProject;
-        GERRIT_BRANCH = cfg.gerritBranch;
-        SUBMIT_QUEUE_TRIGGER_INTERVAL = toString cfg.interval;
-      };
-    };
-  };
-}
diff --git a/ops/modules/git-serving.nix b/ops/modules/git-serving.nix
deleted file mode 100644
index 6b8bef29b1..0000000000
--- a/ops/modules/git-serving.nix
+++ /dev/null
@@ -1,53 +0,0 @@
-# Configures public git-serving infrastructure for TVL, this involves:
-#
-# 1. cgit (running at code.tvl.fyi) for web views of the repository
-# 2. josh (for cloning the repository and its distinct subtrees)
-#
-# We also run Sourcegraph for browsing the repository, but this is
-# currently configured in a separate module
-# (//ops/modules/sourcegraph.nix)
-#
-# TODO(tazjin): Move //web/cgit-taz configuration in here instead.
-{ config, depot, lib, pkgs, ... }:
-
-let
-  cfg = config.services.depot.git-serving;
-in {
-  options.services.depot.git-serving = with lib; {
-    enable = mkEnableOption "Enable cgit & josh configuration";
-
-    joshPort = mkOption {
-      description = "Port on which josh should listen";
-      type = types.int;
-      default = 5674;
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    # Run cgit for the depot. The onion here is nginx(thttpd(cgit)).
-    systemd.services.cgit = {
-      wantedBy = [ "multi-user.target" ];
-      script = "${depot.web.cgit-taz}/bin/cgit-launch";
-
-      serviceConfig = {
-        Restart = "on-failure";
-        User = "git";
-        Group = "git";
-      };
-    };
-
-    # Run josh for the depot.
-    systemd.services.josh = {
-      description = "josh - partial cloning of monorepos";
-      wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.git pkgs.bash ];
-
-      serviceConfig = {
-        DynamicUser = true;
-        StateDirectory = "josh";
-        Restart = "always";
-        ExecStart = "${depot.third_party.josh}/bin/josh-proxy --no-background --local /var/lib/josh --port ${toString cfg.joshPort} --remote https://cl.tvl.fyi/";
-      };
-    };
-  };
-}
diff --git a/ops/modules/irccat.nix b/ops/modules/irccat.nix
index deb0b4ecaf..2263118d99 100644
--- a/ops/modules/irccat.nix
+++ b/ops/modules/irccat.nix
@@ -27,19 +27,20 @@ let
 
     exec ${depot.third_party.irccat}/bin/irccat
   '';
-in {
+in
+{
   options.services.depot.irccat = {
     enable = lib.mkEnableOption description;
 
     config = lib.mkOption {
-      type = lib.types.attrs; # varying value types
+      type = lib.types.attrsOf lib.types.anything; # varying value types
       description = "Configuration structure (unchecked!)";
     };
 
     secretsFile = lib.mkOption {
       type = lib.types.str;
       description = "Path to the secrets file to be merged";
-      default = "/run/agenix/irccat";
+      default = config.age.secretsDir + "/irccat";
     };
   };
 
diff --git a/ops/modules/josh.nix b/ops/modules/josh.nix
new file mode 100644
index 0000000000..3c37d0fec3
--- /dev/null
+++ b/ops/modules/josh.nix
@@ -0,0 +1,33 @@
+# Configures the public josh instance for serving the depot.
+{ config, depot, lib, pkgs, ... }:
+
+let
+  cfg = config.services.depot.josh;
+in
+{
+  options.services.depot.josh = with lib; {
+    enable = mkEnableOption "Enable josh for serving the depot";
+
+    port = mkOption {
+      description = "Port on which josh should listen";
+      type = types.int;
+      default = 5674;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    # Run josh for the depot.
+    systemd.services.josh = {
+      description = "josh - partial cloning of monorepos";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.git pkgs.bash ];
+
+      serviceConfig = {
+        DynamicUser = true;
+        StateDirectory = "josh";
+        Restart = "always";
+        ExecStart = "${pkgs.josh}/bin/josh-proxy --no-background --local /var/lib/josh --port ${toString cfg.port} --remote https://cl.tvl.fyi/ --require-auth";
+      };
+    };
+  };
+}
diff --git a/ops/modules/journaldriver.nix b/ops/modules/journaldriver.nix
new file mode 100644
index 0000000000..0d6b0bcc7f
--- /dev/null
+++ b/ops/modules/journaldriver.nix
@@ -0,0 +1,26 @@
+# Configures journaldriver to forward to the tvl-fyi GCP project from
+# TVL machines.
+{ config, depot, lib, pkgs, ... }:
+
+{
+  imports = [
+    (depot.third_party.agenix.src + "/modules/age.nix")
+  ];
+
+  age.secrets.journaldriver.file = depot.ops.secrets."journaldriver.age";
+
+  services.journaldriver = {
+    enable = true;
+    googleCloudProject = "tvl-fyi";
+    logStream = config.networking.hostName;
+  };
+
+  # Override the systemd service defined in the nixpkgs module to use
+  # the credentials provided by agenix.
+  systemd.services.journaldriver = {
+    serviceConfig = {
+      LoadCredential = "journaldriver.json:/run/agenix/journaldriver";
+      ExecStart = lib.mkForce "${pkgs.coreutils}/bin/env GOOGLE_APPLICATION_CREDENTIALS=\"\${CREDENTIALS_DIRECTORY}/journaldriver.json\" ${depot.ops.journaldriver}/bin/journaldriver";
+    };
+  };
+}
diff --git a/ops/modules/known-hosts.nix b/ops/modules/known-hosts.nix
new file mode 100644
index 0000000000..9ea689178e
--- /dev/null
+++ b/ops/modules/known-hosts.nix
@@ -0,0 +1,21 @@
+# Configure public keys for SSH hosts known to TVL.
+{ ... }:
+
+{
+  programs.ssh.knownHosts = {
+    whitby = {
+      hostNames = [ "whitby.tvl.fyi" "whitby.tvl.su" ];
+      publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILNh/w4BSKov0jdz3gKBc98tpoLta5bb87fQXWBhAl2I";
+    };
+
+    sanduny = {
+      hostNames = [ "sanduny.tvl.su" ];
+      publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOag0XhylaTVhmT6HB8EN2Fv5Ymrc4ZfypOXONUkykTX";
+    };
+
+    github = {
+      hostNames = [ "github.com" ];
+      publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
+    };
+  };
+}
diff --git a/ops/modules/livegrep.nix b/ops/modules/livegrep.nix
new file mode 100644
index 0000000000..e25a301829
--- /dev/null
+++ b/ops/modules/livegrep.nix
@@ -0,0 +1,106 @@
+# Configures a code search instance using Livegrep.
+#
+# We do not currently build Livegrep in Nix, because it's a complex,
+# multi-language Bazel build and doesn't play nicely with Nix.
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.depot.livegrep;
+
+  livegrepConfig = {
+    name = "livegrep";
+
+    fs_paths = [{
+      name = "depot";
+      path = "/depot";
+      metadata.url_pattern = "https://code.tvl.fyi/tree/{path}?id={version}#n{lno}";
+    }];
+
+    repositories = [{
+      name = "depot";
+      path = "/depot";
+      revisions = [ "HEAD" ];
+
+      metadata = {
+        url_pattern = "https://code.tvl.fyi/tree/{path}?id={version}#n{lno}";
+        remote = "https://cl.tvl.fyi/depot.git";
+      };
+    }];
+  };
+
+  configFile = pkgs.writeText "livegrep-config.json" (builtins.toJSON livegrepConfig);
+
+  # latest as of 2024-02-17
+  image = "ghcr.io/livegrep/livegrep/base:033fa0e93c";
+in
+{
+  options.services.depot.livegrep = with lib; {
+    enable = mkEnableOption "Run livegrep code search for depot";
+
+    port = mkOption {
+      description = "Port on which livegrep web UI should listen";
+      type = types.int;
+      default = 5477; # lgrp
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    virtualisation.oci-containers.containers.livegrep-codesearch = {
+      inherit image;
+      extraOptions = [ "--net=host" ];
+
+      volumes = [
+        "${configFile}:/etc/livegrep-config.json:ro"
+        "/var/lib/gerrit/git/depot.git:/depot:ro"
+      ];
+
+      entrypoint = "/livegrep/bin/codesearch";
+      cmd = [
+        "-grpc"
+        "0.0.0.0:5427" # lgcs
+        "-reload_rpc"
+        "-revparse"
+        "/etc/livegrep-config.json"
+      ];
+    };
+
+    virtualisation.oci-containers.containers.livegrep-frontend = {
+      inherit image;
+      dependsOn = [ "livegrep-codesearch" ];
+      extraOptions = [ "--net=host" ];
+
+      entrypoint = "/livegrep/bin/livegrep";
+      cmd = [
+        "-listen"
+        "0.0.0.0:${toString cfg.port}"
+        "-reload"
+        "-connect"
+        "localhost:5427"
+        "-docroot"
+        "/livegrep/web"
+        # TODO(tazjin): docroot with styles etc.
+      ];
+    };
+
+    systemd.services.livegrep-reindex = {
+      script = "${pkgs.docker}/bin/docker exec livegrep-codesearch /livegrep/bin/livegrep-reload localhost:5427";
+      serviceConfig.Type = "oneshot";
+    };
+
+    systemd.paths.livegrep-reindex = {
+      description = "Executes a livegrep reindex if depot refs change";
+      wantedBy = [ "multi-user.target" ];
+
+      pathConfig = {
+        PathChanged = [
+          "/var/lib/gerrit/git/depot.git/packed-refs"
+          "/var/lib/gerrit/git/depot.git/refs"
+        ];
+      };
+    };
+  };
+}
+
+
+# sudo docker exec -ti livegrep /livegrep/bin/codesearch -reload_rpc -revparse /var/lib/livegrep/config.jsno
+# sudo docker run -d --ip 172.17.0.3 --name livegrep -v /var/lib/livegrep:/varlib/livegrep -v /var/lib/gerrit/git/depot.git:/depot:ro -v /home/tazjin/livegrep-web:/livegrep/web:ro ghcr.io/livegrep/livegrep/base /livegrep/bin/livegrep -listen 0.0.0.0:8910 -reload -docroot /livegrep/webbsudo docker run -d --ip 172.17.0.3 --name livegrep -v /var/lib/livegrep:/varlib/livegrep -v /var/lib/gerrit/git/depot.git:/depot:ro -v /home/tazjin/livegrep-web:/livegrep/web:ro ghcr.io/livegrep/livegrep/base /livegrep/bin/livegrep -listen 0.0.0.0:8910 -reload -docroot /livegrep/webb
diff --git a/ops/modules/monorepo-gerrit.nix b/ops/modules/monorepo-gerrit.nix
index 6638f30b3f..b335fe61d5 100644
--- a/ops/modules/monorepo-gerrit.nix
+++ b/ops/modules/monorepo-gerrit.nix
@@ -9,23 +9,26 @@ let
     exec -a ${name} ${depot.ops.besadii}/bin/besadii "$@"
   '';
 
-  gerritHooks = pkgs.runCommandNoCC "gerrit-hooks" {} ''
+  gerritHooks = pkgs.runCommand "gerrit-hooks" { } ''
     mkdir -p $out
     ln -s ${besadiiWithConfig "change-merged"} $out/change-merged
     ln -s ${besadiiWithConfig "patchset-created"} $out/patchset-created
   '';
-in {
+in
+{
   services.gerrit = {
     enable = true;
     listenAddress = "[::]:4778"; # 4778 - grrt
     serverId = "4fdfa107-4df9-4596-8e0a-1d2bbdd96e36";
+
     builtinPlugins = [
       "download-commands"
       "hooks"
+      "replication"
     ];
 
     plugins = with depot.third_party.gerrit_plugins; [
-      owners
+      code-owners
       oauth
       depot.ops.gerrit-tvl
     ];
@@ -39,7 +42,7 @@ in {
     # Gerrit.
     #
     # TODO(tazjin): Update Gerrit and remove this when possible.
-    jvmPackage = pkgs.openjdk11_headless;
+    jvmPackage = pkgs.openjdk17_headless;
 
     settings = {
       core.packedGitLimit = "100m";
@@ -84,26 +87,35 @@ in {
 
       # Auto-link panettone bug links
       commentlink.panettone = {
-        match = "b/(\\\\d+)";
-        html = "<a href=\"https://b.tvl.fyi/issues/$1\">b/$1</a>";
+        match = "b/(\\d+)";
+        link = "https://b.tvl.fyi/issues/$1";
       };
 
       # Auto-link other CLs
       commentlink.gerrit = {
-        match = "cl/(\\\\d+)";
-        html = "<a href=\"https://cl.tvl.fyi/$1\">cl/$1</a>";
+        match = "cl/(\\d+)";
+        link = "https://cl.tvl.fyi/$1";
       };
 
       # Configures integration with Keycloak, which then integrates with a
       # variety of backends.
       auth.type = "OAUTH";
       plugin.gerrit-oauth-provider-keycloak-oauth = {
-        root-url = "https://auth.tvl.fyi";
+        root-url = "https://auth.tvl.fyi/auth";
         realm = "TVL";
         client-id = "gerrit";
         # client-secret is set in /var/lib/gerrit/etc/secure.config.
       };
 
+      plugin.code-owners = {
+        # A Code-Review +2 vote is required from a code owner.
+        requiredApproval = "Code-Review+2";
+        # The OWNERS check can be overriden using an Owners-Override vote.
+        overrideApproval = "Owners-Override+1";
+        # People implicitly approve their own changes automatically.
+        enableImplicitApprovals = "TRUE";
+      };
+
       # Allow users to add additional email addresses to their accounts.
       oauth.allowRegisterNewEmail = true;
 
@@ -129,6 +141,17 @@ in {
         smtpServerPort = 2525;
       };
     };
+
+    # Replication of the depot repository to secondary machines, for
+    # serving cgit/josh.
+    replicationSettings = {
+      gerrit.replicateOnStartup = true;
+
+      remote.sanduny = {
+        url = "depot@sanduny.tvl.su:/var/lib/depot";
+        projects = "depot";
+      };
+    };
   };
 
   systemd.services.gerrit = {
diff --git a/ops/modules/nixery.nix b/ops/modules/nixery.nix
index 60d1510457..29da46cc1d 100644
--- a/ops/modules/nixery.nix
+++ b/ops/modules/nixery.nix
@@ -5,8 +5,10 @@
 let
   cfg = config.services.depot.nixery;
   description = "Nixery - container images on-demand";
-  storagePath = "/var/lib/nixery/${pkgs.nixpkgsCommits.unstable}";
-in {
+  nixpkgsSrc = depot.third_party.sources.nixpkgs-stable;
+  storagePath = "/var/lib/nixery/${nixpkgsSrc.rev}";
+in
+{
   options.services.depot.nixery = {
     enable = lib.mkEnableOption description;
 
@@ -27,12 +29,12 @@ in {
         StateDirectory = "nixery";
         Restart = "always";
         ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${storagePath}";
-        ExecStart = "${depot.third_party.nixery.nixery-bin}/bin/nixery";
+        ExecStart = "${depot.tools.nixery.nixery}/bin/server";
       };
 
       environment = {
         PORT = toString cfg.port;
-        NIXERY_PKGS_PATH = pkgs.path;
+        NIXERY_PKGS_PATH = nixpkgsSrc.outPath;
         NIXERY_STORAGE_BACKEND = "filesystem";
         NIX_TIMEOUT = "60"; # seconds
         STORAGE_PATH = storagePath;
diff --git a/ops/modules/oauth2_proxy.nix b/ops/modules/oauth2_proxy.nix
deleted file mode 100644
index 07ba8861e7..0000000000
--- a/ops/modules/oauth2_proxy.nix
+++ /dev/null
@@ -1,52 +0,0 @@
-# Configuration for oauth2_proxy, which is used as a handler for nginx
-# auth-request setups.
-#
-# This module exports a helper function at
-# `config.services.depot.oauth2_proxy.withAuth` that can be wrapped
-# around nginx server configuration blocks to configure their
-# authentication setup.
-{ config, depot, pkgs, lib, ... }:
-
-let
-  description = "OAuth2 proxy to authenticate TVL services";
-  cfg = config.services.depot.oauth2_proxy;
-  configFile = pkgs.writeText "oauth2_proxy.cfg" ''
-    email_domains = [ "*" ]
-    http_address = "127.0.0.1:${toString cfg.port}"
-    provider = "keycloak-oidc"
-    client_id = "oauth2-proxy"
-    oidc_issuer_url = "https://auth.tvl.fyi/auth/realms/TVL"
-    reverse_proxy = true
-    set_xauthrequest = true
-  '';
-in {
-  options.services.depot.oauth2_proxy = {
-    enable = lib.mkEnableOption description;
-
-    port = lib.mkOption {
-      description = "Port to listen on";
-      type = lib.types.int;
-      default = 2884; # "auth"
-    };
-
-    secretsFile = lib.mkOption {
-      type = lib.types.str;
-      description = "EnvironmentFile from which to load secrets";
-      default = "/run/agenix/oauth2_proxy";
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    systemd.services.oauth2_proxy = {
-      inherit description;
-      wantedBy = [ "multi-user.target" ];
-
-      serviceConfig = {
-        Restart = "always";
-        DynamicUser = true;
-        EnvironmentFile = cfg.secretsFile;
-        ExecStart = "${pkgs.oauth2_proxy}/bin/oauth2-proxy --config ${configFile}";
-      };
-    };
-  };
-}
diff --git a/ops/modules/open_eid.nix b/ops/modules/open_eid.nix
new file mode 100644
index 0000000000..fa577f0f57
--- /dev/null
+++ b/ops/modules/open_eid.nix
@@ -0,0 +1,54 @@
+# NixOS module to configure the Estonian e-ID software.
+{ pkgs, ... }:
+
+{
+  services.pcscd.enable = true;
+
+  # Tell p11-kit to load/proxy opensc-pkcs11.so, providing all available slots
+  # (PIN1 for authentication/decryption, PIN2 for signing).
+  environment.etc."pkcs11/modules/opensc-pkcs11".text = ''
+    module: ${pkgs.opensc}/lib/opensc-pkcs11.so
+  '';
+
+  # Configure Firefox (in case users set `programs.firefox.enable = true;`)
+  programs.firefox = {
+    # Allow a possibly installed "Web eID" extension to do native messaging with
+    # the "web-eid-app" native component.
+    # Users not using `programs.firefox.enable` can override their firefox
+    # derivation, by setting `extraNativeMessagingHosts = [ pkgs.web-eid-app ]`.
+    nativeMessagingHosts.packages = [ pkgs.web-eid-app ];
+    # Configure Firefox to load smartcards via p11kit-proxy.
+    # Users not using `programs.firefox.enable` can override their firefox
+    # derivation, by setting
+    # `extraPolicies.SecurityDevices.p11-kit-proxy "${pkgs.p11-kit}/lib/p11-kit-proxy.so"`.
+    policies.SecurityDevices.p11-kit-proxy = "${pkgs.p11-kit}/lib/p11-kit-proxy.so";
+  };
+
+  # Chromium users need a symlink to their (slightly different) .json file
+  # in the native messaging hosts' manifest file location.
+  environment.etc."chromium/native-messaging-hosts/eu.webeid.json".source = "${pkgs.web-eid-app}/share/web-eid/eu.webeid.json";
+  environment.etc."opt/chrome/native-messaging-hosts/eu.webeid.json".source = "${pkgs.web-eid-app}/share/web-eid/eu.webeid.json";
+
+  environment.systemPackages = with pkgs; [
+    libdigidocpp.bin # provides digidoc-tool(1)
+    qdigidoc
+
+    # Wrapper script to tell to Chrome/Chromium to use p11-kit-proxy to load
+    # security devices, so they can be used for TLS client auth.
+    # Each user needs to run this themselves, it does not work on a system level
+    # due to a bug in Chromium:
+    #
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=16387
+    #
+    # Firefox users can just set
+    # extraPolicies.SecurityDevices.p11-kit-proxy "${pkgs.p11-kit}/lib/p11-kit-proxy.so";
+    # when overriding the firefox derivation.
+    (pkgs.writeShellScriptBin "setup-browser-eid" ''
+      NSSDB="''${HOME}/.pki/nssdb"
+      mkdir -p ''${NSSDB}
+
+      ${pkgs.nssTools}/bin/modutil -force -dbdir sql:$NSSDB -add p11-kit-proxy \
+        -libfile ${pkgs.p11-kit}/lib/p11-kit-proxy.so
+    '')
+  ];
+}
diff --git a/ops/modules/owothia.nix b/ops/modules/owothia.nix
index b2a77cddc2..b9746c1720 100644
--- a/ops/modules/owothia.nix
+++ b/ops/modules/owothia.nix
@@ -4,14 +4,15 @@
 let
   cfg = config.services.depot.owothia;
   description = "owothia - i'm a service owo";
-in {
+in
+{
   options.services.depot.owothia = {
     enable = lib.mkEnableOption description;
 
     secretsFile = lib.mkOption {
       type = lib.types.str;
       description = "File path from which systemd should read secrets";
-      default = "/run/agenix/owothia";
+      default = config.age.secretsDir + "/owothia";
     };
 
     owoChance = lib.mkOption {
diff --git a/ops/modules/panettone.nix b/ops/modules/panettone.nix
index 11e934ec2e..e23dd028ab 100644
--- a/ops/modules/panettone.nix
+++ b/ops/modules/panettone.nix
@@ -2,7 +2,8 @@
 
 let
   cfg = config.services.depot.panettone;
-in {
+in
+{
   options.services.depot.panettone = with lib; {
     enable = mkEnableOption "Panettone issue tracker";
 
@@ -36,7 +37,7 @@ in {
         by systemd's EnvironmentFile
       '';
       type = types.str;
-      default = "/run/agenix/panettone";
+      default = config.age.secretsDir + "/panettone";
     };
 
     irccatHost = mkOption {
@@ -62,23 +63,26 @@ in {
       assertion =
         cfg.dbHost != "localhost" || config.services.postgresql.enable;
       message = "Panettone requires a postgresql database";
-    } {
-      assertion =
-        cfg.dbHost != "localhost" || config.services.postgresql.enableTCPIP;
-      message = "Panettone can only connect to the postgresql database over TCP";
-    } {
-      assertion =
-        cfg.dbHost != "localhost" || (lib.any
-          (user: user.name == cfg.dbUser)
-          config.services.postgresql.ensureUsers);
-      message = "Panettone requires a database user";
-    } {
-      assertion =
-        cfg.dbHost != "localhost" || (lib.any
-          (db: db == cfg.dbName)
-          config.services.postgresql.ensureDatabases);
-      message = "Panettone requires a database";
-    }];
+    }
+      {
+        assertion =
+          cfg.dbHost != "localhost" || config.services.postgresql.enableTCPIP;
+        message = "Panettone can only connect to the postgresql database over TCP";
+      }
+      {
+        assertion =
+          cfg.dbHost != "localhost" || (lib.any
+            (user: user.name == cfg.dbUser)
+            config.services.postgresql.ensureUsers);
+        message = "Panettone requires a database user";
+      }
+      {
+        assertion =
+          cfg.dbHost != "localhost" || (lib.any
+            (db: db == cfg.dbName)
+            config.services.postgresql.ensureDatabases);
+        message = "Panettone requires a database";
+      }];
 
     systemd.services.panettone = {
       wantedBy = [ "multi-user.target" ];
@@ -100,5 +104,16 @@ in {
         ISSUECHANNEL = cfg.irccatChannel;
       };
     };
+
+    systemd.services.panettone-fixer = {
+      description = "Restart panettone regularly to work around b/225";
+      wantedBy = [ "multi-user.target" ];
+      script = "${pkgs.systemd}/bin/systemctl restart panettone";
+      serviceConfig.Type = "oneshot";
+
+      # We don't exactly know how frequently this occurs, but
+      # _probably_ not more than hourly.
+      startAt = "hourly";
+    };
   };
 }
diff --git a/ops/modules/paroxysm.nix b/ops/modules/paroxysm.nix
index cd9cd3866e..070e7623db 100644
--- a/ops/modules/paroxysm.nix
+++ b/ops/modules/paroxysm.nix
@@ -3,7 +3,8 @@
 let
   cfg = config.services.depot.paroxysm;
   description = "TVL's majestic IRC bot";
-in {
+in
+{
   options.services.depot.paroxysm.enable = lib.mkEnableOption description;
 
   config = lib.mkIf cfg.enable {
diff --git a/ops/modules/quassel.nix b/ops/modules/quassel.nix
index 9c8692629a..6acb0615f4 100644
--- a/ops/modules/quassel.nix
+++ b/ops/modules/quassel.nix
@@ -8,7 +8,8 @@ let
     enableDaemon = true;
     withKDE = false;
   };
-in {
+in
+{
   options.services.depot.quassel = with lib; {
     enable = mkEnableOption "Quassel IRC daemon";
 
@@ -42,6 +43,8 @@ in {
   };
 
   config = with lib; mkIf cfg.enable {
+    networking.firewall.allowedTCPPorts = [ cfg.port ];
+
     systemd.services.quassel = {
       description = "Quassel IRC daemon";
       wantedBy = [ "multi-user.target" ];
@@ -52,7 +55,7 @@ in {
         "--port=${toString cfg.port}"
         "--configdir=/var/lib/quassel"
         "--require-ssl"
-        "--ssl-cert=/var/lib/acme/${cfg.acmeHost}/full.pem"
+        "--ssl-cert=$CREDENTIALS_DIRECTORY/quassel.pem"
         "--loglevel=${cfg.logLevel}"
       ];
 
@@ -61,6 +64,10 @@ in {
         User = "quassel";
         Group = "quassel";
         StateDirectory = "quassel";
+
+        # Avoid trouble with the ACME file permissions by using the
+        # systemd credentials feature.
+        LoadCredential = "quassel.pem:/var/lib/acme/${cfg.acmeHost}/full.pem";
       };
     };
 
@@ -70,7 +77,7 @@ in {
         group = "quassel";
       };
 
-      groups.quassel = {};
+      groups.quassel = { };
     };
   };
 }
diff --git a/ops/modules/restic.nix b/ops/modules/restic.nix
index 1aacf68973..8695396035 100644
--- a/ops/modules/restic.nix
+++ b/ops/modules/restic.nix
@@ -14,7 +14,8 @@ let
     inherit default;
     type = lib.types.str;
   };
-in {
+in
+{
   options.services.depot.restic = {
     enable = lib.mkEnableOption description;
     bucketEndpoint = mkStringOption "objects.dc-sto1.glesys.net";
diff --git a/ops/modules/smtprelay.nix b/ops/modules/smtprelay.nix
index 106593fe39..f6ce262175 100644
--- a/ops/modules/smtprelay.nix
+++ b/ops/modules/smtprelay.nix
@@ -27,8 +27,9 @@ let
   prepareArgs = args:
     concatStringsSep " "
       (attrValues (mapAttrs (key: value: "-${key} \"${toString value}\"")
-                            (args // overrideArgs)));
-in {
+        (args // overrideArgs)));
+in
+{
   options.services.depot.smtprelay = {
     enable = mkEnableOption description;
 
@@ -39,7 +40,7 @@ in {
 
     secretsFile = mkOption {
       type = types.str;
-      default = "/run/agenix/smtprelay";
+      default = config.age.secretsDir + "/smtprelay";
     };
   };
 
diff --git a/ops/modules/sourcegraph.nix b/ops/modules/sourcegraph.nix
index a72cd75d47..cbf836ab64 100644
--- a/ops/modules/sourcegraph.nix
+++ b/ops/modules/sourcegraph.nix
@@ -4,7 +4,8 @@
 
 let
   cfg = config.services.depot.sourcegraph;
-in {
+in
+{
   options.services.depot.sourcegraph = with lib; {
     enable = mkEnableOption "SourceGraph code search engine";
 
@@ -34,7 +35,7 @@ in {
     };
 
     virtualisation.oci-containers.containers.sourcegraph = {
-      image = "sourcegraph/server:3.31.2";
+      image = "sourcegraph/server:3.40.0";
 
       ports = [
         "127.0.0.1:${toString cfg.port}:7080"
@@ -51,7 +52,8 @@ in {
       # Sourcegraph needs a higher nofile limit, it logs warnings
       # otherwise (unclear whether it actually affects the service).
       extraOptions = [
-        "--ulimit" "nofile=10000:10000"
+        "--ulimit"
+        "nofile=10000:10000"
       ];
     };
   };
diff --git a/ops/modules/tvl-buildkite.nix b/ops/modules/tvl-buildkite.nix
index aaeb5a0f75..3c6d88404f 100644
--- a/ops/modules/tvl-buildkite.nix
+++ b/ops/modules/tvl-buildkite.nix
@@ -13,7 +13,7 @@ let
 
   # All Buildkite hooks are actually besadii, but it's being invoked
   # with different names.
-  buildkiteHooks = pkgs.runCommandNoCC "buildkite-hooks" {} ''
+  buildkiteHooks = pkgs.runCommand "buildkite-hooks" { } ''
     mkdir -p $out/bin
     ln -s ${besadiiWithConfig "post-command"} $out/bin/post-command
   '';
@@ -22,7 +22,8 @@ let
     echo 'username=buildkite'
     echo "password=$(jq -r '.gerritPassword' /run/agenix/buildkite-besadii-config)"
   '';
-in {
+in
+{
   options.services.depot.buildkite = {
     enable = lib.mkEnableOption description;
     agentCount = lib.mkOption {
@@ -33,39 +34,47 @@ in {
 
   config = lib.mkIf cfg.enable {
     # Run the Buildkite agents using the default upstream module.
-    services.buildkite-agents = builtins.listToAttrs (map (n: rec {
-      name = "whitby-${toString n}";
-      value = {
-        inherit name;
-        enable = true;
-        tokenPath = "/run/agenix/buildkite-agent-token";
-        hooks.post-command = "${buildkiteHooks}/bin/post-command";
+    services.buildkite-agents = builtins.listToAttrs (map
+      (n: rec {
+        name = "whitby-${toString n}";
+        value = {
+          inherit name;
+          enable = true;
+          tokenPath = config.age.secretsDir + "/buildkite-agent-token";
+          privateSshKeyPath = config.age.secretsDir + "/buildkite-private-key";
+          hooks.post-command = "${buildkiteHooks}/bin/post-command";
+          hooks.environment = ''
+            export PATH=$PATH:/run/wrappers/bin
+          '';
 
-        runtimePackages = with pkgs; [
-          bash
-          coreutils
-          credentialHelper
-          curl
-          git
-          gnutar
-          gzip
-          jq
-          nix
-        ];
-      };
-    }) agents);
+          runtimePackages = with pkgs; [
+            bash
+            coreutils
+            credentialHelper
+            curl
+            git
+            gnutar
+            gzip
+            jq
+            nix
+          ];
+        };
+      })
+      agents);
 
     # Set up a group for all Buildkite agent users
     users = {
-      groups.buildkite-agents = {};
-      users = builtins.listToAttrs (map (n: rec {
-        name = "buildkite-agent-whitby-${toString n}";
-        value = {
-          isSystemUser = true;
-          group = lib.mkForce "buildkite-agents";
-          extraGroups = [ name "docker" ];
-        };
-      }) agents);
+      groups.buildkite-agents = { };
+      users = builtins.listToAttrs (map
+        (n: rec {
+          name = "buildkite-agent-whitby-${toString n}";
+          value = {
+            isSystemUser = true;
+            group = lib.mkForce "buildkite-agents";
+            extraGroups = [ name "docker" ];
+          };
+        })
+        agents);
     };
   };
 }
diff --git a/ops/modules/tvl-cache.nix b/ops/modules/tvl-cache.nix
index 4d574821df..683818d103 100644
--- a/ops/modules/tvl-cache.nix
+++ b/ops/modules/tvl-cache.nix
@@ -6,12 +6,12 @@
   };
 
   config = lib.mkIf config.tvl.cache.enable {
-    nix = {
-      binaryCachePublicKeys = [
+    nix.settings = {
+      trusted-public-keys = [
         "cache.tvl.su:kjc6KOMupXc1vHVufJUoDUYeLzbwSr9abcAKdn/U1Jk="
       ];
 
-      binaryCaches = [
+      substituters = [
         "https://cache.tvl.su"
       ];
     };
diff --git a/ops/modules/tvl-headscale.nix b/ops/modules/tvl-headscale.nix
new file mode 100644
index 0000000000..a07021c788
--- /dev/null
+++ b/ops/modules/tvl-headscale.nix
@@ -0,0 +1,62 @@
+# Configuration for the coordination server for net.tvl.fyi, a
+# tailscale network run using headscale.
+#
+# All TVL members can join this network, which provides several exit
+# nodes through which traffic can be routed.
+#
+# The coordination server is currently run on sanduny.tvl.su. It is
+# managed manually, ping somebody with access ... for access.
+#
+# Servers should join using approximately this command:
+#   tailscale up --login-server https://net.tvl.fyi --accept-dns=false --advertise-exit-node
+#
+# Clients should join using approximately this command:
+#   tailscale up --login-server https://net.tvl.fyi --accept-dns=false
+{ config, pkgs, ... }:
+
+{
+  # TODO(tazjin): run embedded DERP server
+  services.headscale = {
+    enable = true;
+    port = 4725; # hscl
+
+    settings = {
+      server_url = "https://net.tvl.fyi";
+      dns_config.nameservers = [
+        "8.8.8.8"
+        "1.1.1.1"
+        "77.88.8.8"
+      ];
+
+      # TLS is handled by nginx
+      tls_cert_path = null;
+      tls_key_path = null;
+    };
+  };
+
+  environment.systemPackages = [ pkgs.headscale ]; # admin CLI
+
+  services.nginx.virtualHosts."net.tvl.fyi" = {
+    serverName = "net.tvl.fyi";
+    enableACME = true;
+    forceSSL = true;
+
+    # See https://github.com/juanfont/headscale/blob/v0.22.3/docs/reverse-proxy.md#nginx
+    extraConfig = ''
+      location / {
+        proxy_pass http://localhost:${toString config.services.headscale.port};
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection $connection_upgrade;
+        proxy_set_header Host $server_name;
+        proxy_redirect http:// https://;
+        proxy_buffering off;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
+        add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
+      }
+    '';
+  };
+
+}
diff --git a/ops/modules/tvl-slapd/default.nix b/ops/modules/tvl-slapd/default.nix
index dbcf139338..c08cd30f16 100644
--- a/ops/modules/tvl-slapd/default.nix
+++ b/ops/modules/tvl-slapd/default.nix
@@ -26,7 +26,8 @@ let
 
   inherit (depot.ops) users;
 
-in {
+in
+{
   services.openldap = {
     enable = true;
 
@@ -34,7 +35,7 @@ in {
       "olcDatabase={1}mdb".attrs = {
         objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
         olcDatabase = "{1}mdb";
-        olcDbDirectory = "/var/lib/openldap";
+        olcDbDirectory = "/var/lib/openldap/db";
         olcSuffix = "dc=tvl,dc=fyi";
         olcAccess = "to *  by * read";
         olcRootDN = "cn=admin,dc=tvl,dc=fyi";
@@ -43,12 +44,12 @@ in {
 
       "cn=module{0}".attrs = {
         objectClass = "olcModuleList";
-        olcModuleLoad = "pw-argon2";
+        olcModuleLoad = "argon2";
       };
 
       "cn=schema".includes =
         map (schema: "${pkgs.openldap}/etc/schema/${schema}.ldif")
-            [ "core" "cosine" "inetorgperson" "nis" ];
+          [ "core" "cosine" "inetorgperson" "nis" ];
     };
 
     # Contents are immutable at runtime, and adding user accounts etc.
diff --git a/ops/modules/tvl-users.nix b/ops/modules/tvl-users.nix
new file mode 100644
index 0000000000..ea83b435f4
--- /dev/null
+++ b/ops/modules/tvl-users.nix
@@ -0,0 +1,83 @@
+# Standard NixOS users for TVL machines, as well as configuration that
+# should following along when they are added to a machine.
+{ depot, pkgs, ... }:
+
+{
+  users = {
+    users.tazjin = {
+      isNormalUser = true;
+      extraGroups = [ "git" "wheel" ];
+      shell = pkgs.fish;
+      openssh.authorizedKeys.keys = depot.users.tazjin.keys.all;
+    };
+
+    users.lukegb = {
+      isNormalUser = true;
+      extraGroups = [ "git" "wheel" ];
+      openssh.authorizedKeys.keys = depot.users.lukegb.keys.all;
+    };
+
+    users.aspen = {
+      isNormalUser = true;
+      extraGroups = [ "git" "wheel" ];
+      openssh.authorizedKeys.keys = [ depot.users.aspen.keys.whitby ];
+    };
+
+    users.edef = {
+      isNormalUser = true;
+      extraGroups = [ "git" ];
+      openssh.authorizedKeys.keys = depot.users.edef.keys.all;
+    };
+
+    users.qyliss = {
+      isNormalUser = true;
+      description = "Alyssa Ross";
+      extraGroups = [ "git" ];
+      openssh.authorizedKeys.keys = depot.users.qyliss.keys.all;
+    };
+
+    users.eta = {
+      isNormalUser = true;
+      extraGroups = [ "git" ];
+      openssh.authorizedKeys.keys = depot.users.eta.keys.whitby;
+    };
+
+    users.cynthia = {
+      isNormalUser = true; # I'm normal OwO :3
+      extraGroups = [ "git" ];
+      openssh.authorizedKeys.keys = depot.users.cynthia.keys.all;
+    };
+
+    users.firefly = {
+      isNormalUser = true;
+      extraGroups = [ "git" ];
+      openssh.authorizedKeys.keys = depot.users.firefly.keys.whitby;
+    };
+
+    users.sterni = {
+      isNormalUser = true;
+      extraGroups = [ "git" "wheel" ];
+      openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
+    };
+
+    users.flokli = {
+      isNormalUser = true;
+      extraGroups = [ "git" "wheel" ];
+      openssh.authorizedKeys.keys = depot.users.flokli.keys.all;
+    };
+  };
+
+  programs.fish.enable = true;
+
+  environment.systemPackages = with pkgs; [
+    alacritty.terminfo
+    foot.terminfo
+    rxvt-unicode-unwrapped.terminfo
+    kitty.terminfo
+  ];
+
+  security.sudo.extraRules = [{
+    groups = [ "wheel" ];
+    commands = [{ command = "ALL"; options = [ "NOPASSWD" ]; }];
+  }];
+}
diff --git a/ops/modules/v4l2loopback.nix b/ops/modules/v4l2loopback.nix
deleted file mode 100644
index 636b2ff6cf..0000000000
--- a/ops/modules/v4l2loopback.nix
+++ /dev/null
@@ -1,12 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  boot = {
-    extraModulePackages = [ config.boot.kernelPackages.v4l2loopback ];
-    kernelModules = [ "v4l2loopback" ];
-    extraModprobeConfig = ''
-      options v4l2loopback exclusive_caps=1
-    '';
-  };
-}
-
diff --git a/ops/modules/www/auth.tvl.fyi.nix b/ops/modules/www/auth.tvl.fyi.nix
index e0c031bf70..a068f02365 100644
--- a/ops/modules/www/auth.tvl.fyi.nix
+++ b/ops/modules/www/auth.tvl.fyi.nix
@@ -12,8 +12,12 @@
       forceSSL = true;
 
       extraConfig = ''
+        # increase buffer size for large headers
+        proxy_buffers 8 16k;
+        proxy_buffer_size 16k;
+
         location / {
-          proxy_pass http://localhost:${config.services.keycloak.httpPort};
+          proxy_pass http://localhost:${toString config.services.keycloak.settings.http-port};
           proxy_set_header X-Forwarded-For $remote_addr;
           proxy_set_header X-Forwarded-Proto https;
           proxy_set_header Host $host;
diff --git a/ops/modules/www/base.nix b/ops/modules/www/base.nix
index cfa9bf0bc6..50fceff0fa 100644
--- a/ops/modules/www/base.nix
+++ b/ops/modules/www/base.nix
@@ -2,6 +2,11 @@
 
 {
   config = {
+    security.acme = {
+      acceptTerms = true;
+      defaults.email = "letsencrypt@tvl.su";
+    };
+
     services.nginx = {
       enable = true;
       enableReload = true;
@@ -10,31 +15,27 @@
       recommendedGzipSettings = true;
       recommendedProxySettings = true;
 
+      commonHttpConfig = ''
+        log_format json_combined escape=json
+        '{'
+            '"remote_addr":"$remote_addr",'
+            '"method":"$request_method",'
+            '"host":"$host",'
+            '"uri":"$request_uri",'
+            '"status":$status,'
+            '"request_size":$request_length,'
+            '"response_size":$body_bytes_sent,'
+            '"response_time":$request_time,'
+            '"referrer":"$http_referer",'
+            '"user_agent":"$http_user_agent"'
+        '}';
+
+        access_log syslog:server=unix:/dev/log,nohostname json_combined;
+      '';
+
       appendHttpConfig = ''
         add_header Permissions-Policy "interest-cohort=()";
       '';
     };
-
-    # NixOS 20.03 broke nginx and I can't be bothered to debug it
-    # anymore, all solution attempts have failed, so here's a
-    # brute-force fix.
-    #
-    # TODO(tazjin): Find a link to the upstream issue and see if
-    # they've sorted it after ~20.09
-    systemd.services.fix-nginx = {
-      script = "${pkgs.coreutils}/bin/chown -f -R nginx: /var/spool/nginx /var/cache/nginx";
-
-      serviceConfig = {
-        User = "root";
-        Type = "oneshot";
-      };
-    };
-
-    systemd.timers.fix-nginx = {
-      wantedBy = [ "multi-user.target" ];
-      timerConfig = {
-        OnCalendar = "minutely";
-      };
-    };
   };
 }
diff --git a/ops/modules/www/cl.tvl.fyi.nix b/ops/modules/www/cl.tvl.fyi.nix
index 470122c395..36422a6c4e 100644
--- a/ops/modules/www/cl.tvl.fyi.nix
+++ b/ops/modules/www/cl.tvl.fyi.nix
@@ -24,6 +24,10 @@
           # The :443 suffix is a workaround for https://b.tvl.fyi/issues/88.
           proxy_set_header  Host $host:443;
         }
+
+        location = /robots.txt {
+          return 200 'User-agent: *\nAllow: /';
+        }
       '';
     };
   };
diff --git a/ops/modules/www/code.tvl.fyi.nix b/ops/modules/www/code.tvl.fyi.nix
index 4c182d34f2..ee0211990d 100644
--- a/ops/modules/www/code.tvl.fyi.nix
+++ b/ops/modules/www/code.tvl.fyi.nix
@@ -1,4 +1,4 @@
-{ depot, config, ... }:
+{ depot, pkgs, config, ... }:
 
 {
   imports = [
@@ -13,16 +13,49 @@
       forceSSL = true;
 
       extraConfig = ''
-        # Serve the rendered Tvix component SVG.
-        #
-        # TODO(tazjin): Implement a way of serving this dynamically
-        location = /about/tvix/docs/component-flow.svg {
-            alias ${depot.tvix.docs.svg}/component-flow.svg;
+        location = /go-get/tvix/build-go {
+            alias ${pkgs.writeText "go-import-metadata.html" ''<html><meta name="go-import" content="code.tvl.fyi/tvix/build-go git https://code.tvl.fyi/depot.git:/tvix/build-go.git"></html>''};
+        }
+
+        location = /go-get/tvix/castore-go {
+            alias ${pkgs.writeText "go-import-metadata.html" ''<html><meta name="go-import" content="code.tvl.fyi/tvix/castore-go git https://code.tvl.fyi/depot.git:/tvix/castore-go.git"></html>''};
+        }
+
+        location = /go-get/tvix/store-go {
+            alias ${pkgs.writeText "go-import-metadata.html" ''<html><meta name="go-import" content="code.tvl.fyi/tvix/store-go git https://code.tvl.fyi/depot.git:/tvix/store-go.git"></html>''};
+        }
+
+        location = /go-get/tvix/nar-bridge {
+            alias ${pkgs.writeText "go-import-metadata.html" ''<html><meta name="go-import" content="code.tvl.fyi/tvix/nar-bridge git https://code.tvl.fyi/depot.git:/tvix/nar-bridge.git"></html>''};
+        }
+
+        location = /tvix/build-go {
+            if ($args ~* "/?go-get=1") {
+                return 302 /go-get/tvix/build-go;
+            }
+        }
+
+        location = /tvix/castore-go {
+            if ($args ~* "/?go-get=1") {
+                return 302 /go-get/tvix/castore-go;
+            }
+        }
+
+        location = /tvix/store-go {
+            if ($args ~* "/?go-get=1") {
+                return 302 /go-get/tvix/store-go;
+            }
+        }
+
+        location = /tvix/nar-bridge {
+            if ($args ~* "/?go-get=1") {
+                return 302 /go-get/tvix/nar-bridge;
+            }
         }
 
         # Git operations on depot.git hit josh
         location /depot.git {
-            proxy_pass http://localhost:${toString config.services.depot.git-serving.joshPort};
+            proxy_pass http://127.0.0.1:${toString config.services.depot.josh.port};
         }
 
         # Git clone operations on '/' should be redirected to josh now.
diff --git a/ops/modules/www/grep.tvl.fyi.nix b/ops/modules/www/grep.tvl.fyi.nix
new file mode 100644
index 0000000000..93ef5eabd2
--- /dev/null
+++ b/ops/modules/www/grep.tvl.fyi.nix
@@ -0,0 +1,19 @@
+# Experimental configuration for manually Livegrep.
+{ config, ... }:
+
+{
+  imports = [
+    ./base.nix
+  ];
+
+  config = {
+    services.nginx.virtualHosts."grep.tvl.fyi" = {
+      enableACME = true;
+      forceSSL = true;
+
+      locations."/" = {
+        proxyPass = "http://127.0.0.1:${toString config.services.depot.livegrep.port}";
+      };
+    };
+  };
+}
diff --git a/ops/modules/www/images.tvl.fyi.nix b/ops/modules/www/images.tvl.fyi.nix
deleted file mode 100644
index 7d027b2991..0000000000
--- a/ops/modules/www/images.tvl.fyi.nix
+++ /dev/null
@@ -1,22 +0,0 @@
-{ config, ... }:
-
-{
-  imports = [
-    ./base.nix
-  ];
-
-  config = {
-    services.nginx.virtualHosts."images.tvl.fyi" = {
-      serverName = "images.tvl.fyi";
-      serverAliases = [ "images.tvl.su" ];
-      enableACME = true;
-      forceSSL = true;
-
-      extraConfig = ''
-        location / {
-          proxy_pass http://localhost:${toString config.services.depot.nixery.port};
-        }
-      '';
-    };
-  };
-}
diff --git a/ops/modules/www/inbox.tvl.su.nix b/ops/modules/www/inbox.tvl.su.nix
new file mode 100644
index 0000000000..38db5d2a8e
--- /dev/null
+++ b/ops/modules/www/inbox.tvl.su.nix
@@ -0,0 +1,31 @@
+{ config, depot, ... }:
+
+{
+  imports = [
+    ./base.nix
+  ];
+
+  config = {
+    services.nginx.virtualHosts."inbox.tvl.su" = {
+      enableACME = true;
+      forceSSL = true;
+
+      extraConfig = ''
+        # nginx is incapable of serving a single file at /, hence this hack:
+        location = / {
+          index /landing-page;
+        }
+
+        location = /landing-page {
+          types { } default_type "text/html; charset=utf-8";
+          alias ${depot.web.inbox};
+        }
+
+        # rest of requests is proxied to public-inbox-httpd
+        location / {
+          proxy_pass http://localhost:${toString config.services.public-inbox.http.port};
+        }
+      '';
+    };
+  };
+}
diff --git a/ops/modules/www/self-redirect.nix b/ops/modules/www/self-redirect.nix
new file mode 100644
index 0000000000..5bf1627be9
--- /dev/null
+++ b/ops/modules/www/self-redirect.nix
@@ -0,0 +1,27 @@
+# Redirect the hostname of a machine to its configuration in a web
+# browser.
+#
+# Works by convention, assuming that the machine has its configuration
+# at //ops/machines/${hostname}.
+{ config, ... }:
+
+let
+  host = "${config.networking.hostName}.${config.networking.domain}";
+in
+{
+  imports = [
+    ./base.nix
+  ];
+
+  config.services.nginx.virtualHosts."${host}" = {
+    serverName = host;
+    addSSL = true; # SSL is not forced on these redirects
+    enableACME = true;
+
+    extraConfig = ''
+      location = / {
+        return 302 https://at.tvl.fyi/?q=%2F%2Fops%2Fmachines%2F${config.networking.hostName};
+      }
+    '';
+  };
+}
diff --git a/ops/modules/www/signup.tvl.fyi.nix b/ops/modules/www/signup.tvl.fyi.nix
new file mode 100644
index 0000000000..1b193f99a9
--- /dev/null
+++ b/ops/modules/www/signup.tvl.fyi.nix
@@ -0,0 +1,19 @@
+{ depot, ... }:
+
+{
+  imports = [
+    ./base.nix
+  ];
+
+  config = {
+    services.nginx.virtualHosts."signup.tvl.fyi" = {
+      root = depot.web.pwcrypt;
+      enableACME = true;
+      forceSSL = true;
+
+      extraConfig = ''
+        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
+      '';
+    };
+  };
+}
diff --git a/ops/modules/www/status.tvl.su.nix b/ops/modules/www/status.tvl.su.nix
index 2bb6093c14..7079c60260 100644
--- a/ops/modules/www/status.tvl.su.nix
+++ b/ops/modules/www/status.tvl.su.nix
@@ -18,7 +18,7 @@
       forceSSL = true;
 
       locations."/" = {
-        proxyPass = "http://localhost:${toString config.services.grafana.port}";
+        proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
       };
     };
   };
diff --git a/ops/modules/www/tazj.in.nix b/ops/modules/www/tazj.in.nix
index 7d658a5ec4..47eefca2a6 100644
--- a/ops/modules/www/tazj.in.nix
+++ b/ops/modules/www/tazj.in.nix
@@ -11,8 +11,13 @@
       enableACME = true;
       forceSSL = true;
       root = depot.users.tazjin.homepage;
+      serverAliases = [ "www.tazj.in" ];
 
       extraConfig = ''
+        location = /en/rss.xml {
+          return 301 https://tazj.in/feed.atom;
+        }
+
         ${depot.users.tazjin.blog.oldRedirects}
         location /blog/ {
           alias ${depot.users.tazjin.blog.rendered}/;
@@ -24,6 +29,15 @@
           try_files $uri $uri.html $uri/ =404;
         }
 
+        location = /predlozhnik {
+          return 302 https://predlozhnik.ru;
+        }
+
+        # redirect for easier entry on a TV
+        location = /tv {
+          return 302 https://tazj.in/blobs/play.html;
+        }
+
         # Temporary place for serving static files.
         location /blobs/ {
           alias /var/lib/tazjins-blobs/;
diff --git a/ops/modules/www/tvix.dev.nix b/ops/modules/www/tvix.dev.nix
new file mode 100644
index 0000000000..f884bc30ed
--- /dev/null
+++ b/ops/modules/www/tvix.dev.nix
@@ -0,0 +1,46 @@
+{ depot, ... }:
+
+{
+  imports = [
+    ./base.nix
+  ];
+
+  config = {
+    services.nginx.virtualHosts."tvix.dev" = {
+      serverName = "tvix.dev";
+      enableACME = true;
+      forceSSL = true;
+      root = depot.tvix.website;
+    };
+
+    services.nginx.virtualHosts."bolt.tvix.dev" = {
+      root = depot.web.tvixbolt;
+      enableACME = true;
+      forceSSL = true;
+    };
+
+    # old domain, serve redirect
+    services.nginx.virtualHosts."tvixbolt.tvl.su" = {
+      enableACME = true;
+      forceSSL = true;
+      extraConfig = "return 301 https://bolt.tvix.dev$request_uri;";
+    };
+
+    services.nginx.virtualHosts."docs.tvix.dev" = {
+      serverName = "docs.tvix.dev";
+      enableACME = true;
+      forceSSL = true;
+
+      extraConfig = ''
+        location = / {
+          # until we have a better default page here
+          return 301 https://docs.tvix.dev/rust/tvix_eval/index.html;
+        }
+
+        location /rust/ {
+          alias ${depot.tvix.rust-docs}/;
+        }
+      '';
+    };
+  };
+}
diff --git a/ops/modules/www/tvl.fyi.nix b/ops/modules/www/tvl.fyi.nix
index f422bb8487..59ee1bc27f 100644
--- a/ops/modules/www/tvl.fyi.nix
+++ b/ops/modules/www/tvl.fyi.nix
@@ -35,7 +35,11 @@
         }
 
         location = /blog {
-          return 302 /;
+          return 302 /#blog;
+        }
+
+        location = /blog/ {
+          return 302 /#blog;
         }
       '';
     };
diff --git a/ops/modules/www/volgasprint.org.nix b/ops/modules/www/volgasprint.org.nix
new file mode 100644
index 0000000000..7e5abe5561
--- /dev/null
+++ b/ops/modules/www/volgasprint.org.nix
@@ -0,0 +1,15 @@
+{ depot, ... }:
+
+{
+  imports = [
+    ./base.nix
+  ];
+
+  config = {
+    services.nginx.virtualHosts."volgasprint.org" = {
+      enableACME = true;
+      forceSSL = true;
+      root = "${depot.web.volgasprint}";
+    };
+  };
+}
diff --git a/ops/modules/www/wigglydonke.rs.nix b/ops/modules/www/wigglydonke.rs.nix
index 3d85e4eb98..6440164325 100644
--- a/ops/modules/www/wigglydonke.rs.nix
+++ b/ops/modules/www/wigglydonke.rs.nix
@@ -9,7 +9,7 @@
     services.nginx.virtualHosts."wigglydonke.rs" = {
       enableACME = true;
       forceSSL = true;
-      root = "${depot.path + "/users/grfn/wigglydonke.rs"}";
+      root = "${depot.path + "/users/aspen/wigglydonke.rs"}";
     };
   };
 }
diff --git a/ops/modules/yandex-cloud.nix b/ops/modules/yandex-cloud.nix
new file mode 100644
index 0000000000..cf6d1eb810
--- /dev/null
+++ b/ops/modules/yandex-cloud.nix
@@ -0,0 +1,78 @@
+# Profile for virtual machines on Yandex Cloud, intended for disk
+# images.
+#
+# https://cloud.yandex.com/en/docs/compute/operations/image-create/custom-image
+#
+# TODO(tazjin): Upstream to nixpkgs once it works well.
+{ config, lib, pkgs, modulesPath, ... }:
+
+let
+  cfg = config.virtualisation.yandexCloud;
+
+  # Kernel modules required for interacting with the hypervisor. These
+  # must be available during stage 1 boot and during normal operation,
+  # as disks and network do not work without them.
+  modules = [
+    "virtio-net"
+    "virtio-blk"
+    "virtio-pci"
+    "virtiofs"
+  ];
+in
+{
+  imports = [
+    "${modulesPath}/profiles/headless.nix"
+  ];
+
+  options = {
+    virtualisation.yandexCloud.rootPartitionUuid = with lib; mkOption {
+      type = types.str;
+      default = "C55A5EE2-E5FA-485C-B3AE-CC928429AB6B";
+
+      description = ''
+        UUID to use for the root partition of the disk image. Yandex
+        Cloud requires that root partitions are mounted by UUID.
+
+        Most users do not need to set this to a non-default value.
+      '';
+    };
+  };
+
+  config = {
+    fileSystems."/" = {
+      device = "/dev/disk/by-uuid/${lib.toLower cfg.rootPartitionUuid}";
+      fsType = "ext4";
+      autoResize = true;
+    };
+
+    boot = {
+      loader.grub.device = "/dev/vda";
+
+      initrd.kernelModules = modules;
+      kernelModules = modules;
+      kernelParams = [
+        # Enable support for the serial console
+        "console=ttyS0"
+      ];
+
+      growPartition = true;
+    };
+
+    environment.etc.securetty = {
+      text = "ttyS0";
+      mode = "0644";
+    };
+
+    systemd.services."serial-getty@ttyS0".enable = true;
+
+    services.openssh.enable = true;
+
+    system.build.yandexCloudImage = import (pkgs.path + "/nixos/lib/make-disk-image.nix") {
+      inherit lib config pkgs;
+      additionalSpace = "128M";
+      format = "qcow2";
+      partitionTableType = "legacy+gpt";
+      rootGPUID = cfg.rootPartitionUuid;
+    };
+  };
+}
diff --git a/ops/mq_cli/Cargo.lock b/ops/mq_cli/Cargo.lock
index f418d77c34..18fed3621d 100644
--- a/ops/mq_cli/Cargo.lock
+++ b/ops/mq_cli/Cargo.lock
@@ -1,159 +1,168 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
 [[package]]
 name = "ansi_term"
-version = "0.11.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
 dependencies = [
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi",
 ]
 
 [[package]]
 name = "atty"
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 dependencies = [
- "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hermit-abi",
+ "libc",
+ "winapi",
 ]
 
 [[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
 name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "cc"
-version = "1.0.50"
+version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
 
 [[package]]
 name = "cfg-if"
-version = "0.1.10"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "clap"
-version = "2.33.0"
+version = "2.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
 dependencies = [
- "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
 ]
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.6"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
 dependencies = [
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc",
 ]
 
 [[package]]
 name = "libc"
-version = "0.2.66"
+version = "0.2.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
 
 [[package]]
-name = "mq"
-version = "1.0.0"
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
 dependencies = [
- "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "posix_mq 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "autocfg",
+]
+
+[[package]]
+name = "mq_cli"
+version = "3773.0.0"
+dependencies = [
+ "clap",
+ "libc",
+ "nix",
+ "posix_mq",
 ]
 
 [[package]]
 name = "nix"
-version = "0.16.1"
+version = "0.23.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
 dependencies = [
- "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
- "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "memoffset",
 ]
 
 [[package]]
 name = "posix_mq"
-version = "0.9.0"
+version = "3771.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f462ad79a99ea13f3ef76d9c271956e924183f5aeb67a8649c8c2b6bdd079da8"
 dependencies = [
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc",
+ "nix",
 ]
 
 [[package]]
 name = "strsim"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 
 [[package]]
 name = "textwrap"
 version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
 dependencies = [
- "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width",
 ]
 
 [[package]]
 name = "unicode-width"
-version = "0.1.7"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
 
 [[package]]
 name = "vec_map"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "void"
-version = "1.0.2"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 
 [[package]]
 name = "winapi"
-version = "0.3.8"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 dependencies = [
- "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
 ]
 
 [[package]]
 name = "winapi-i686-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[metadata]
-"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
-"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
-"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
-"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
-"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772"
-"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
-"checksum nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dd0eaf8df8bab402257e0a5c17a254e4cc1f72a93588a1ddfb5d356c801aa7cb"
-"checksum posix_mq 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13ae339e13cc96902a4597a5aab6b76473093969c55d36ba33f6a7bf3268573f"
-"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
-"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
-"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
-"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
-"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
-"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/ops/mq_cli/Cargo.toml b/ops/mq_cli/Cargo.toml
index b412d88787..816a370759 100644
--- a/ops/mq_cli/Cargo.toml
+++ b/ops/mq_cli/Cargo.toml
@@ -1,10 +1,14 @@
 [package]
-name = "mq"
-version = "1.0.0"
-authors = ["Vincent Ambo <mail@tazj.in>"]
+name = "mq_cli"
+description = "CLI tool for accessing POSIX message queues (mq_overview(7))"
+license = "MIT"
+version = "3773.0.0"
+authors = ["Vincent Ambo <tazjin@tvl.su>"]
+homepage = "https://cs.tvl.fyi/depot/-/tree/ops/mq_cli"
+repository = "https://code.tvl.fyi/depot.git:/ops/mq_cli.git"
 
 [dependencies]
-clap = "2.33"
+clap = "2.34"
 libc = "0.2"
-nix = "0.16"
-posix_mq = "0.9"
+nix = "0.23"
+posix_mq = "3771.0.0"
diff --git a/ops/mq_cli/README.md b/ops/mq_cli/README.md
index e612553e74..1045de896b 100644
--- a/ops/mq_cli/README.md
+++ b/ops/mq_cli/README.md
@@ -27,5 +27,16 @@ SUBCOMMANDS:
     send       Send a message to a queue
 ```
 
+## Development
+
+Development happens in the [TVL
+monorepo](https://cs.tvl.fyi/depot/-/tree/ops/mq_cli).
+
+Starting from version `3773.0.0`, the version numbers correspond to
+_revisions_ of the TVL repository, available as git refs (e.g.
+`refs/r/3773`).
+
+See the TVL documentation for more information about how to contribute
+to the codebase.
 
 [POSIX message queues]: https://linux.die.net/man/7/mq_overview
diff --git a/ops/mq_cli/src/main.rs b/ops/mq_cli/src/main.rs
index 55ff006429..927993b486 100644
--- a/ops/mq_cli/src/main.rs
+++ b/ops/mq_cli/src/main.rs
@@ -1,36 +1,38 @@
 extern crate clap;
-extern crate posix_mq;
 extern crate libc;
 extern crate nix;
+extern crate posix_mq;
 
-use clap::{App, SubCommand, Arg, ArgMatches, AppSettings};
-use posix_mq::{Name, Queue, Message};
+use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
+use posix_mq::{Message, Name, Queue};
 use std::fs::{read_dir, File};
 use std::io::{self, Read, Write};
 use std::process::exit;
 
 fn run_ls() {
-    let mqueues = read_dir("/dev/mqueue")
-        .expect("Could not read message queues");
+    let mqueues = read_dir("/dev/mqueue").expect("Could not read message queues");
 
     for queue in mqueues {
         let path = queue.unwrap().path();
         let status = {
-            let mut file = File::open(&path)
-                .expect("Could not open queue file");
+            let mut file = File::open(&path).expect("Could not open queue file");
 
             let mut content = String::new();
-            file.read_to_string(&mut content).expect("Could not read queue file");
+            file.read_to_string(&mut content)
+                .expect("Could not read queue file");
 
             content
         };
 
-        let queue_name = path.components().last().unwrap()
+        let queue_name = path
+            .components()
+            .last()
+            .unwrap()
             .as_os_str()
             .to_string_lossy();
 
         println!("/{}: {}", queue_name, status)
-    };
+    }
 }
 
 fn run_inspect(queue_name: &str) {
@@ -47,8 +49,7 @@ fn run_create(cmd: &ArgMatches) {
         set_rlimit(rlimit.parse().expect("Invalid rlimit value"));
     }
 
-    let name = Name::new(cmd.value_of("queue").unwrap())
-        .expect("Invalid queue name");
+    let name = Name::new(cmd.value_of("queue").unwrap()).expect("Invalid queue name");
 
     let max_pending: i64 = cmd.value_of("max-pending").unwrap().parse().unwrap();
     let max_size: i64 = cmd.value_of("max-size").unwrap().parse().unwrap();
@@ -56,11 +57,11 @@ fn run_create(cmd: &ArgMatches) {
     let queue = Queue::create(name, max_pending, max_size * 1024);
 
     match queue {
-        Ok(_)  => println!("Queue created successfully"),
+        Ok(_) => println!("Queue created successfully"),
         Err(e) => {
             writeln!(io::stderr(), "Could not create queue: {}", e).ok();
             exit(1);
-        },
+        }
     };
 }
 
@@ -120,7 +121,12 @@ fn run_rlimit() {
     };
 
     if errno != 0 {
-        writeln!(io::stderr(), "Could not get message queue rlimit: {}", errno).ok();
+        writeln!(
+            io::stderr(),
+            "Could not get message queue rlimit: {}",
+            errno
+        )
+        .ok();
     } else {
         println!("Message queue rlimit:");
         println!("Current limit: {}", rlimit.rlim_cur);
@@ -170,16 +176,20 @@ fn main() {
         .about("Create a new queue")
         .arg(&queue_arg)
         .arg(&rlimit_arg)
-        .arg(Arg::with_name("max-size")
-            .help("maximum message size (in kB)")
-            .long("max-size")
-            .required(true)
-            .takes_value(true))
-        .arg(Arg::with_name("max-pending")
-            .help("maximum # of pending messages")
-            .long("max-pending")
-            .required(true)
-            .takes_value(true));
+        .arg(
+            Arg::with_name("max-size")
+                .help("maximum message size (in kB)")
+                .long("max-size")
+                .required(true)
+                .takes_value(true),
+        )
+        .arg(
+            Arg::with_name("max-pending")
+                .help("maximum # of pending messages")
+                .long("max-pending")
+                .required(true)
+                .takes_value(true),
+        );
 
     let receive = SubCommand::with_name("receive")
         .about("Receive a message from a queue")
@@ -188,9 +198,11 @@ fn main() {
     let send = SubCommand::with_name("send")
         .about("Send a message to a queue")
         .arg(&queue_arg)
-        .arg(Arg::with_name("message")
-            .help("the message to send")
-            .required(true));
+        .arg(
+            Arg::with_name("message")
+                .help("the message to send")
+                .required(true),
+        );
 
     let rlimit = SubCommand::with_name("rlimit")
         .about("Get the message queue rlimit")
@@ -211,13 +223,13 @@ fn main() {
     match matches.subcommand() {
         ("ls", _) => run_ls(),
         ("inspect", Some(cmd)) => run_inspect(cmd.value_of("queue").unwrap()),
-        ("create",  Some(cmd)) => run_create(cmd),
+        ("create", Some(cmd)) => run_create(cmd),
         ("receive", Some(cmd)) => run_receive(cmd.value_of("queue").unwrap()),
-        ("send",    Some(cmd)) => run_send(
+        ("send", Some(cmd)) => run_send(
             cmd.value_of("queue").unwrap(),
-            cmd.value_of("message").unwrap()
+            cmd.value_of("message").unwrap(),
         ),
-        ("rlimit",  _) => run_rlimit(),
+        ("rlimit", _) => run_rlimit(),
         _ => unimplemented!(),
     }
 }
diff --git a/ops/nixos.nix b/ops/nixos.nix
index 66ca188c5b..1442d89b30 100644
--- a/ops/nixos.nix
+++ b/ops/nixos.nix
@@ -7,10 +7,18 @@ in rec {
   baseModule = { ... }: {
     # Ensure that pkgs == third_party.nix
     nixpkgs.pkgs = depot.third_party.nixpkgs;
-    nix.nixPath = [
-      "nixos=${pkgs.path}"
-      "nixpkgs=${pkgs.path}"
-    ];
+    nix.nixPath =
+      let
+        # Due to nixpkgsBisectPath, pkgs.path is not always in the nix store
+        nixpkgsStorePath =
+          if lib.hasPrefix builtins.storeDir (toString pkgs.path)
+          then builtins.storePath pkgs.path # nixpkgs is already in the store
+          else pkgs.path; # we need to dump nixpkgs to the store either way
+      in
+      [
+        ("nixos=" + nixpkgsStorePath)
+        ("nixpkgs=" + nixpkgsStorePath)
+      ];
   };
 
   nixosFor = configuration: (depot.third_party.nixos {
@@ -32,7 +40,10 @@ in rec {
       (throw "${hostname} is not a known NixOS host")
       (map nixosFor depot.ops.machines.all-systems));
 
-  rebuild-system = rebuildSystemWith depot.path;
+  rebuild-system = rebuildSystemWith (
+    # HACK: use the string of the original source to avoid copying the whole
+    # depot into the store just for this
+    builtins.toString depot.path.origSrc);
 
   rebuildSystemWith = depotPath: pkgs.writeShellScriptBin "rebuild-system" ''
     set -ue
@@ -50,5 +61,7 @@ in rec {
 
   # Systems that should be built in CI
   whitbySystem = (nixosFor depot.ops.machines.whitby).system;
-  meta.targets = [ "whitbySystem" ];
+  sandunySystem = (nixosFor depot.ops.machines.sanduny).system;
+  nixeryDev01System = (nixosFor depot.ops.machines.nixery-01).system;
+  meta.ci.targets = [ "sandunySystem" "whitbySystem" "nixeryDev01System" ];
 }
diff --git a/ops/pipelines/depot.nix b/ops/pipelines/depot.nix
index b6941ba38a..5eff622671 100644
--- a/ops/pipelines/depot.nix
+++ b/ops/pipelines/depot.nix
@@ -3,22 +3,14 @@
 { depot, pkgs, externalArgs, ... }:
 
 let
-  # Protobuf check step which validates that changes to .proto files
-  # between revisions don't cause backwards-incompatible or otherwise
-  # flawed changes.
-  protoCheck = {
-    command = "${depot.nix.bufCheck}/bin/ci-buf-check";
-    label = ":water_buffalo:";
-  };
-
   pipeline = depot.nix.buildkite.mkPipeline {
     headBranch = "refs/heads/canon";
     drvTargets = depot.ci.targets;
-    additionalSteps = [ protoCheck ];
 
-    parentTargetMap = if (externalArgs ? parentTargetMap)
+    parentTargetMap =
+      if (externalArgs ? parentTargetMap)
       then builtins.fromJSON (builtins.readFile externalArgs.parentTargetMap)
-      else {};
+      else { };
 
     postBuildSteps = [
       # After successful builds, create a gcroot for builds on canon.
@@ -40,7 +32,8 @@ let
   };
 
   drvmap = depot.nix.buildkite.mkDrvmap depot.ci.targets;
-in pkgs.runCommandNoCC "depot-pipeline" {} ''
+in
+pkgs.runCommand "depot-pipeline" { } ''
   mkdir $out
   cp -r ${pipeline}/* $out
   cp ${drvmap} $out/drvmap.json
diff --git a/ops/pipelines/static-pipeline.yaml b/ops/pipelines/static-pipeline.yaml
index 23a1fba4f2..af4f9d784e 100644
--- a/ops/pipelines/static-pipeline.yaml
+++ b/ops/pipelines/static-pipeline.yaml
@@ -4,6 +4,8 @@
 # If something fails during the creation of the pipeline, the fallback
 # is executed instead which will simply report an error to Gerrit.
 ---
+env:
+  BUILDKITE_TOKEN_PATH: /run/agenix/buildkite-graphql-token
 steps:
   # Run pipeline for tvl-kit when new commits arrive on canon. Since
   # it is not part of the depot build tree, this is a useful
@@ -15,6 +17,16 @@ steps:
     build:
       message: "Verification triggered by ${BUILDKITE_COMMIT}"
 
+  # Run pipeline for tvix when new commits arrive on canon. Since
+  # it is not part of the depot build tree, this is a useful
+  # verification to ensure we don't break external things (too much).
+  - trigger: "tvix"
+    async: true
+    label: ":fork:"
+    branches: "refs/heads/canon"
+    build:
+      message: "Verification triggered by ${BUILDKITE_COMMIT}"
+
   # Create a revision number for the current commit for builds on
   # canon.
   #
@@ -23,6 +35,11 @@ steps:
   #
   # Revision numbers are defined as the number of commits in the
   # lineage of HEAD, following only the first parent of merges.
+  #
+  # Note that git does not fetch these refs by default, instead
+  # you'll have to modify your git config using
+  # `git config --add remote.origin.fetch '+refs/r/*:refs/r/*'`.
+  # The refs are available after the next `git fetch`.
   - label: ":git:"
     branches: "refs/heads/canon"
     command: |
@@ -32,12 +49,14 @@ steps:
   # Generate & upload dynamic build steps
   - label: ":llama:"
     key: "pipeline-gen"
+    concurrency_group: 'depot-nix-eval'
+    concurrency: 5 # much more than this and whitby will OOM
     command: |
       set -ue
 
       if test -n "$${GERRIT_CHANGE_URL-}"; then
         echo "This is a build of [cl/$$GERRIT_CHANGE_ID]($$GERRIT_CHANGE_URL) (at patchset #$$GERRIT_PATCHSET)" | \
-          buildkite-agent annotate
+          buildkite-agent annotate --context cl-annotation
       fi
 
       # Attempt to fetch a target map from a parent commit on canon,
@@ -50,7 +69,11 @@ steps:
         PIPELINE_ARGS="--arg parentTargetMap tmp/parent-target-map.json"
       fi
 
-      nix-build -A ops.pipelines.depot -o pipeline --show-trace $$PIPELINE_ARGS
+      nix-build --option restrict-eval true --include "depot=$${PWD}" \
+        --include "store=/nix/store" \
+        --allowed-uris 'https://' \
+        -A ops.pipelines.depot \
+        -o pipeline --show-trace $$PIPELINE_ARGS
 
       # Steps need to be uploaded in reverse order because pipeline
       # upload prepends instead of appending.
@@ -85,7 +108,7 @@ steps:
 
       readonly FAILED_JOBS=$(curl 'https://graphql.buildkite.com/v1' \
         --silent \
-        -H "Authorization: Bearer $(cat /run/agenix/buildkite-graphql-token)" \
+        -H "Authorization: Bearer $(cat ${BUILDKITE_TOKEN_PATH})" \
         -d "{\"query\": \"query BuildStatusQuery { build(uuid: \\\"$BUILDKITE_BUILD_ID\\\") { jobs(passed: false) { count } } }\"}" | \
         jq -r '.data.build.jobs.count')
 
@@ -95,8 +118,8 @@ steps:
         exit 1
       fi
 
-  # After duck, on success, upload and run any post-build steps that
-  # were output by the dynamic pipeline.
+  # After duck, on success, upload and run any release steps that were
+  # output by the dynamic pipeline.
   - label: ":arrow_heading_down:"
     depends_on:
       - step: ":duck:"
@@ -106,6 +129,6 @@ steps:
 
       buildkite-agent artifact download "pipeline/*" .
 
-      find ./pipeline -name 'post-chunk-*.json' | tac | while read chunk; do
+      find ./pipeline -name 'release-chunk-*.json' | tac | while read chunk; do
         buildkite-agent pipeline upload $$chunk
       done
diff --git a/ops/posix_mq.rs/Cargo.lock b/ops/posix_mq.rs/Cargo.lock
index fdd0086c4d..dc344613d0 100644
--- a/ops/posix_mq.rs/Cargo.lock
+++ b/ops/posix_mq.rs/Cargo.lock
@@ -1,54 +1,63 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 
 [[package]]
 name = "cc"
 version = "1.0.50"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
 
 [[package]]
 name = "cfg-if"
-version = "0.1.10"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "libc"
-version = "0.2.66"
+version = "0.2.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
 
 [[package]]
-name = "nix"
-version = "0.16.1"
+name = "memoffset"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
 dependencies = [
- "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
- "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "autocfg",
 ]
 
 [[package]]
-name = "posix_mq"
-version = "0.9.0"
+name = "nix"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
 dependencies = [
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "memoffset",
 ]
 
 [[package]]
-name = "void"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[metadata]
-"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
-"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
-"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
-"checksum nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dd0eaf8df8bab402257e0a5c17a254e4cc1f72a93588a1ddfb5d356c801aa7cb"
-"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+name = "posix_mq"
+version = "3771.0.0"
+dependencies = [
+ "libc",
+ "nix",
+]
diff --git a/ops/posix_mq.rs/Cargo.toml b/ops/posix_mq.rs/Cargo.toml
index d72e87a3dc..8390b80b86 100644
--- a/ops/posix_mq.rs/Cargo.toml
+++ b/ops/posix_mq.rs/Cargo.toml
@@ -1,11 +1,12 @@
 [package]
 name = "posix_mq"
-version = "0.9.0"
-authors = ["Vincent Ambo <mail@tazj.in>"]
+version = "3771.0.0"
+authors = ["Vincent Ambo <tazjin@tvl.su>"]
 description = "(Higher-level) Rust bindings to POSIX message queues"
 license = "MIT"
-repository = "https://git.tazj.in/tree/ops/posix_mq.rs"
+homepage = "https://cs.tvl.fyi/depot/-/tree/ops/posix_mq.rs"
+repository = "https://code.tvl.fyi/depot.git:/ops/posix_mq.rs.git"
 
 [dependencies]
-nix = "0.16"
+nix = "0.23"
 libc = "0.2"
diff --git a/ops/posix_mq.rs/README.md b/ops/posix_mq.rs/README.md
index 9370c6c087..800d2221e4 100644
--- a/ops/posix_mq.rs/README.md
+++ b/ops/posix_mq.rs/README.md
@@ -1,7 +1,6 @@
 posix_mq
 ========
 
-[![Build Status](https://travis-ci.org/aprilabank/posix_mq.rs.svg?branch=master)](https://travis-ci.org/aprilabank/posix_mq.rs)
 [![crates.io](https://img.shields.io/crates/v/posix_mq.svg)](https://crates.io/crates/posix_mq)
 
 This is a simple, relatively high-level library for the POSIX [message queue API][]. It wraps the lower-level API in a
@@ -29,5 +28,17 @@ queue.send(&message).expect("message sending failed");
 let result = queue.receive().expect("message receiving failed");
 ```
 
+## Development
+
+Development happens in the [TVL
+monorepo](https://cs.tvl.fyi/depot/-/tree/ops/posix_mq.rs).
+
+Starting from version `3771.0.0`, the version numbers correspond to
+_revisions_ of the TVL repository, available as git refs (e.g.
+`refs/r/3771`).
+
+See the TVL documentation for more information about how to contribute
+to the codebase.
+
 [message queue API]: https://linux.die.net/man/7/mq_overview
 [sister library]: https://github.com/aprilabank/posix_mq.kt
diff --git a/ops/posix_mq.rs/src/error.rs b/ops/posix_mq.rs/src/error.rs
index 1ef585c01e..bacd2aeb39 100644
--- a/ops/posix_mq.rs/src/error.rs
+++ b/ops/posix_mq.rs/src/error.rs
@@ -1,8 +1,5 @@
 use nix;
-use std::error;
-use std::fmt;
-use std::io;
-use std::num;
+use std::{error, fmt, io, num};
 
 /// This module implements a simple error type to match the errors that can be thrown from the C
 /// functions as well as some extra errors resulting from internal validations.
@@ -17,7 +14,7 @@ use std::num;
 /// * ENAMETOOLONG: This crate performs name validation
 ///
 /// If an unexpected error is encountered it will be wrapped appropriately and should be reported
-/// as a bug on https://github.com/aprilabank/posix_mq.rs
+/// as a bug on https://b.tvl.fyi
 
 #[derive(Debug)]
 pub enum Error {
@@ -47,13 +44,13 @@ pub enum Error {
 
     // Some other unexpected / unknown error occured. This is probably an error from
     // the nix crate. Bug reports also welcome for this!
-    UnknownInternalError(Option<nix::Error>),
+    UnknownInternalError(),
 }
 
-impl error::Error for Error {
-    fn description(&self) -> &str {
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use Error::*;
-        match *self {
+        f.write_str(match *self {
             // This error contains more sensible description strings already
             InvalidQueueName(e) => e,
             ValueReadingError(_) => "error reading system configuration for message queues",
@@ -67,31 +64,44 @@ impl error::Error for Error {
             QueueNotFound() => "the specified queue could not be found",
             InsufficientMemory() => "insufficient memory to call queue method",
             InsufficientSpace() => "insufficient space to call queue method",
-            ProcessFileDescriptorLimitReached() =>
-                "maximum number of process file descriptors reached",
-            SystemFileDescriptorLimitReached() =>
-                "maximum number of system file descriptors reached",
+            ProcessFileDescriptorLimitReached() => {
+                "maximum number of process file descriptors reached"
+            }
+            SystemFileDescriptorLimitReached() => {
+                "maximum number of system file descriptors reached"
+            }
             UnknownForeignError(_) => "unknown foreign error occured: please report a bug!",
-            UnknownInternalError(_) => "unknown internal error occured: please report a bug!",
-        }
+            UnknownInternalError() => "unknown internal error occured: please report a bug!",
+        })
     }
 }
 
-impl fmt::Display for Error {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        // Explicitly import this to gain access to Error::description()
-        use std::error::Error;
-        f.write_str(self.description())
+impl error::Error for Error {
+    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+        match self {
+            Error::ValueReadingError(e) => Some(e),
+            Error::UnknownForeignError(e) => Some(e),
+            _ => None,
+        }
     }
 }
 
 /// This from implementation is used to translate errors from the lower-level
 /// C-calls into sensible Rust errors.
-impl From<nix::Error> for Error {
-    fn from(e: nix::Error) -> Self {
-        match e {
-            nix::Error::Sys(e) => match_errno(e),
-            _ => Error::UnknownInternalError(Some(e)),
+impl From<nix::errno::Errno> for Error {
+    fn from(err: nix::Error) -> Self {
+        use nix::errno::Errno::*;
+        match err {
+            EACCES => Error::PermissionDenied(),
+            EBADF => Error::InvalidQueueDescriptor(),
+            EINTR => Error::QueueCallInterrupted(),
+            EEXIST => Error::QueueAlreadyExists(),
+            EMFILE => Error::ProcessFileDescriptorLimitReached(),
+            ENFILE => Error::SystemFileDescriptorLimitReached(),
+            ENOENT => Error::QueueNotFound(),
+            ENOMEM => Error::InsufficientMemory(),
+            ENOSPC => Error::InsufficientSpace(),
+            _ => Error::UnknownForeignError(err),
         }
     }
 }
@@ -107,24 +117,6 @@ impl From<io::Error> for Error {
 // here because the system is probably seriously broken if those files don't contain numbers.
 impl From<num::ParseIntError> for Error {
     fn from(_: num::ParseIntError) -> Self {
-        Error::UnknownInternalError(None)
-    }
-}
-
-
-fn match_errno(err: nix::errno::Errno) -> Error {
-    use nix::errno::Errno::*;
-
-    match err {
-        EACCES => Error::PermissionDenied(),
-        EBADF  => Error::InvalidQueueDescriptor(),
-        EINTR  => Error::QueueCallInterrupted(),
-        EEXIST => Error::QueueAlreadyExists(),
-        EMFILE => Error::ProcessFileDescriptorLimitReached(),
-        ENFILE => Error::SystemFileDescriptorLimitReached(),
-        ENOENT => Error::QueueNotFound(),
-        ENOMEM => Error::InsufficientMemory(),
-        ENOSPC => Error::InsufficientSpace(),
-        _      => Error::UnknownForeignError(err),
+        Error::UnknownInternalError()
     }
 }
diff --git a/ops/posix_mq.rs/src/lib.rs b/ops/posix_mq.rs/src/lib.rs
index 057601eccf..ed35fb03be 100644
--- a/ops/posix_mq.rs/src/lib.rs
+++ b/ops/posix_mq.rs/src/lib.rs
@@ -1,5 +1,5 @@
-extern crate nix;
 extern crate libc;
+extern crate nix;
 
 use error::Error;
 use libc::mqd_t;
@@ -8,8 +8,8 @@ use nix::sys::stat;
 use std::ffi::CString;
 use std::fs::File;
 use std::io::Read;
-use std::string::ToString;
 use std::ops::Drop;
+use std::string::ToString;
 
 pub mod error;
 
@@ -33,16 +33,20 @@ impl Name {
         // have tried just using '/' as a queue name.
         if string.len() == 1 {
             return Err(Error::InvalidQueueName(
-                "Queue name must be a slash followed by one or more characters"
+                "Queue name must be a slash followed by one or more characters",
             ));
         }
 
         if string.len() > 255 {
-            return Err(Error::InvalidQueueName("Queue name must not exceed 255 characters"));
+            return Err(Error::InvalidQueueName(
+                "Queue name must not exceed 255 characters",
+            ));
         }
 
         if string.matches('/').count() > 1 {
-            return Err(Error::InvalidQueueName("Queue name can not contain more than one slash"));
+            return Err(Error::InvalidQueueName(
+                "Queue name can not contain more than one slash",
+            ));
         }
 
         // TODO: What error is being thrown away here? Is it possible?
@@ -97,16 +101,9 @@ impl Queue {
             flags
         };
 
-        let attr = mqueue::MqAttr::new(
-            0, max_pending, max_size, 0
-        );
+        let attr = mqueue::MqAttr::new(0, max_pending, max_size, 0);
 
-        let queue_descriptor = mqueue::mq_open(
-            &name.0,
-            oflags,
-            default_mode(),
-            Some(&attr),
-        )?;
+        let queue_descriptor = mqueue::mq_open(&name.0, oflags, default_mode(), Some(&attr))?;
 
         Ok(Queue {
             name,
@@ -121,12 +118,7 @@ impl Queue {
         // No extra flags need to be constructed as the default is to open and fail if the
         // queue does not exist yet - which is what we want here.
         let oflags = mqueue::MQ_OFlag::O_RDWR;
-        let queue_descriptor = mqueue::mq_open(
-            &name.0,
-            oflags,
-            default_mode(),
-            None,
-        )?;
+        let queue_descriptor = mqueue::mq_open(&name.0, oflags, default_mode(), None)?;
 
         let attr = mq_getattr(queue_descriptor)?;
 
@@ -151,16 +143,9 @@ impl Queue {
 
         let default_pending = read_i64_from_file(MSG_DEFAULT)?;
         let default_size = read_i64_from_file(MSGSIZE_DEFAULT)?;
-        let attr = mqueue::MqAttr::new(
-            0, default_pending, default_size, 0
-        );
+        let attr = mqueue::MqAttr::new(0, default_pending, default_size, 0);
 
-        let queue_descriptor = mqueue::mq_open(
-            &name.0,
-            oflags,
-            default_mode(),
-            Some(&attr),
-        )?;
+        let queue_descriptor = mqueue::mq_open(&name.0, oflags, default_mode(), Some(&attr))?;
 
         let actual_attr = mq_getattr(queue_descriptor)?;
 
@@ -187,11 +172,8 @@ impl Queue {
             return Err(Error::MessageSizeExceeded());
         }
 
-        mqueue::mq_send(
-            self.queue_descriptor,
-            msg.data.as_ref(),
-            msg.priority,
-        ).map_err(|e| e.into())
+        mqueue::mq_send(self.queue_descriptor, msg.data.as_ref(), msg.priority)
+            .map_err(|e| e.into())
     }
 
     /// Receive a message from the message queue.
@@ -200,11 +182,7 @@ impl Queue {
         let mut data: Vec<u8> = vec![0; self.max_size as usize];
         let mut priority: u32 = 0;
 
-        let msg_size = mqueue::mq_receive(
-            self.queue_descriptor,
-            data.as_mut(),
-            &mut priority,
-        )?;
+        let msg_size = mqueue::mq_receive(self.queue_descriptor, data.as_mut(), &mut priority)?;
 
         data.truncate(msg_size);
         Ok(Message { data, priority })
@@ -261,9 +239,9 @@ fn read_i64_from_file(name: &str) -> Result<i64, Error> {
 /// To work around it, this method calls the C-function directly.
 fn mq_getattr(mqd: mqd_t) -> Result<libc::mq_attr, Error> {
     use std::mem;
-    let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() };
-    let res = unsafe { libc::mq_getattr(mqd, &mut attr) };
+    let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
+    let res = unsafe { libc::mq_getattr(mqd, attr.as_mut_ptr()) };
     nix::errno::Errno::result(res)
-        .map(|_| attr)
+        .map(|_| unsafe { attr.assume_init() })
         .map_err(|e| e.into())
 }
diff --git a/ops/posix_mq.rs/src/tests.rs b/ops/posix_mq.rs/src/tests.rs
index 7a08876aea..1f4ea9a58d 100644
--- a/ops/posix_mq.rs/src/tests.rs
+++ b/ops/posix_mq.rs/src/tests.rs
@@ -4,8 +4,7 @@ use super::*;
 fn test_open_delete() {
     // Simple test with default queue settings
     let name = Name::new("/test-queue").unwrap();
-    let queue = Queue::open_or_create(name)
-        .expect("Opening queue failed");
+    let queue = Queue::open_or_create(name).expect("Opening queue failed");
 
     let message = Message {
         data: "test-message".as_bytes().to_vec(),
diff --git a/ops/secrets/besadii.age b/ops/secrets/besadii.age
index b78f02da8f..50c2d1442d 100644
--- a/ops/secrets/besadii.age
+++ b/ops/secrets/besadii.age
Binary files differdiff --git a/ops/secrets/buildkite-agent-token.age b/ops/secrets/buildkite-agent-token.age
index 35e592ee51..66802310bb 100644
--- a/ops/secrets/buildkite-agent-token.age
+++ b/ops/secrets/buildkite-agent-token.age
Binary files differdiff --git a/ops/secrets/buildkite-graphql-token.age b/ops/secrets/buildkite-graphql-token.age
index e1c30b785b..6ebf3efca7 100644
--- a/ops/secrets/buildkite-graphql-token.age
+++ b/ops/secrets/buildkite-graphql-token.age
@@ -1,15 +1,16 @@
 age-encryption.org/v1
--> ssh-ed25519 dcsaLw eGKM1q69QdToZ9wbtsdAgAfGHOsVrc/IJ4IFbHfoeAA
-eogaENxdhqW/2H+FM7SPWgN1UcXPzUTx3tYiVU9K8Rk
--> ssh-ed25519 CpJBgQ v00XK32Div5ddrWPdzjv5ZFPECtW14rPv3G6iFvXUFI
-OQAJaolWVUiVXTK14b9Q5ZTYR4YQL2e6Ye5TY4Xxq0Y
--> ssh-ed25519 aXKGcg ieOvBBSHPSP7k05I5unpRn6+S4K9NfRqwUb5s0XM0js
-z8Q+psAM7Zj02M7m3KNNjSTLmiLH9S+nOzQE5xz1nr0
--> ssh-ed25519 OkGqLg OKzXlZJyHE73V36WVZ2KhvFhif3HZtZDjjBBv5g3hyA
-ilL9pohUoCXfNi1jLekPx35Iu3dGOBAe1H2JFXrKHTU
--> VQDa2-grease 'HsH ^-&
-YuO3YgYZ3Q1CjlIayGFg1Y9zplKgzqR0mZeZlyaOJDMHDrWSOaWRPXjFVU/s2EvP
-MECrypRbNRaHEdPSY7udi1q5cVBPNj3Dci5uiq9t
---- HKTtOZJq9MSAhr3x1eUhk6yFJU3y7TCPilXPhMNfbwA
-1“0‘ζ?ρΧa³€¬“₯ΰσ9ξ½‘ό4ωbWyӌ†ι
-­jb¨ηnγπΊύ6	PpΥš»–lΗ'YΈFyχχρ
\ No newline at end of file
+-> ssh-ed25519 dcsaLw X7cI9stdU1F8M8Mhk/5a4UwU2Ze6rBXuwRDxUTKCTHw
+CnksXNl+VEs2CYiucBeIgfpzpA05VshlECkbmTUZSpI
+-> ssh-ed25519 zcCuhA 7KOsie4KRM0pPKZk8MeDISuX4tT9MAw/5mehSQcNOE8
+UfbpAlKJVhZOH5j4YIw5CVDen7UebTO/S55sLT9tVyc
+-> ssh-ed25519 CpJBgQ EiDs9pCdSnPb4T4HvgF+gdyJ9f5orhtn1OVUp45e3jM
+SlMWEzpi/mMlhfBPzVBn6jZknvjWCbRQMLoJEklJV2w
+-> ssh-ed25519 aXKGcg kiuat73hEcxKvRZ9Gk115LjB3WVgd0h5KrjMOyTRLzw
+CwEmQX6vmi6DnJp/TeYFOSdsfrprHylXAzhnAaQ3aKw
+-> ssh-ed25519 OkGqLg R+moPPGckVPXrAnwQXFPqsizUwK+8UlL2VAA1965d1Y
+J0sxPR2PDqK3k39dSLOzFQkUUZ5cfYqww6NHQ7E4ql4
+-> lb6ND/-grease !D$d P~ Tj.
+HjRsXF0B07o957mq0zRgyHlckismT8UI8KcyFN55ff9FlWpci3+LEcPCb08wtraP
+DSRvOi4
+--- AomJrDQJ4VQghgD6b7ItcPNyiu+cDmNQM31FOqYBbEk
+
0:“ΰΉΉX΄0b₯™^Ί(Ας:Œ°ΣV¦r%GT―hμΑ>~·ΆΏ…Ίqο‘Ϊ*Όεώ	›Χͺ½;}$ψ
\ No newline at end of file
diff --git a/ops/secrets/buildkite-ssh-private-key.age b/ops/secrets/buildkite-ssh-private-key.age
new file mode 100644
index 0000000000..c9aa988277
--- /dev/null
+++ b/ops/secrets/buildkite-ssh-private-key.age
Binary files differdiff --git a/ops/secrets/clbot-ssh.age b/ops/secrets/clbot-ssh.age
index ab51ccc68e..c24f8f45d3 100644
--- a/ops/secrets/clbot-ssh.age
+++ b/ops/secrets/clbot-ssh.age
Binary files differdiff --git a/ops/secrets/clbot.age b/ops/secrets/clbot.age
index c44c77f583..2cec1f7f36 100644
--- a/ops/secrets/clbot.age
+++ b/ops/secrets/clbot.age
@@ -1,14 +1,15 @@
 age-encryption.org/v1
--> ssh-ed25519 dcsaLw sjFTLxJ9JArZ/GU/R/hqRVgX73x3sDO4uNdVrRrZpXE
-cbMS1tn4+diLX4Hf1Pe0XBYvJH5G3ueZIIA+3KImq3Y
--> ssh-ed25519 CpJBgQ 3yeOIq2DxFqr8NW4VpdaUVoEmwvQayWThPzoMo9UCmY
-xLyNilVdqXZ6WjAbT9NDFIssFc4564C/13z4w8WGnpU
--> ssh-ed25519 aXKGcg peKlfil+osni6uHra2unBeQM5MBeK9TVmBg3BpozVy0
-KsKJ5yQQFWGbuiANV8uOck3sSIW82v/JKqLEuLJRsAo
--> ssh-ed25519 OkGqLg Jo5YHWYNkou8JIBKrSrRJBG1VMdStmDqe/S62hdo+Ac
-U5zaBxJ6TKsuaB3vKS7+03vBJLe+nAWMZ6fSlwF+VQs
--> 8SA_}x-grease
-J/zFiD0MDxVK5FDCv4fmA6sawl8gQZcPg0h1NunSjVnBUPNXx9FZylONpu9M56y8
-Z2JJ
---- bR5Pl8ZiMNPIgx/n6ozwOkikLE9E6GWEK2SVIMUlbvI
-gΨyΔ†Λx_ˆεψγαι‘nψ2κ	uTτΟZΔΫRΣτGς7,i£΅S%ZSαKΝQdζ.`―,y(πYΫn‰9c¬„ΐ…ώ	
\ No newline at end of file
+-> ssh-ed25519 dcsaLw ZkAwxhi/ckHaVTnF7bmzOXhQG3HHqw1CpMe6nQL0rHc
+9qnf0AY/inCEvk1VBd4RC3M0kATM/JuIyWxqisjersY
+-> ssh-ed25519 zcCuhA o3PRUMcah5zjj39LtDWpgmBPFtHyx1N9WQz++lFrFEI
+7K1kZHKfmlV5G/xVbgeOuLAO2iXKqcEyRYm+YfTvURs
+-> ssh-ed25519 CpJBgQ pFnL2XmxzppshipadVltN/zSgiRiMh6emu6O8EZTpxI
+K/RPjooKVSwqxc2aAUBtdTnkKoZvXDi+2NPB2NPXT9E
+-> ssh-ed25519 aXKGcg sTN4w5iMnwxmp/E7OKu5I3pUc695OXBYmfOY8/hs1AM
+DguaArDGVn7scD0NrDntgePjN1LFlfrPKfjEd1T9iOI
+-> ssh-ed25519 OkGqLg xuRTDdql+UBNW2go+XxkC/FJZa+N/e6Kj/Fjm7MzG3E
+KC39o7+WV+d/psN4mYSxeUSHsSCxPWTJgYjY1f1Dd3w
+-> J:e-grease
+CISPWfdtr4GKDU+lhCFk6B/EVyOmYwDxhChu
+--- nwu3QYk6rfvIJWJrTB8RSBsWjS1uok8rSxc9FCzoA9k
+WSάMrό
g#MSBχ}A"Ϋ֞ύ˜–ήϊŽψ¨w›„}†€ŠΩ―“σΗΝ-θΕZ”α1ΘΓρoo„Go8χ¨wΓΣ…
\ No newline at end of file
diff --git a/ops/secrets/depot-inbox-imap.age b/ops/secrets/depot-inbox-imap.age
new file mode 100644
index 0000000000..9bce1845cb
--- /dev/null
+++ b/ops/secrets/depot-inbox-imap.age
@@ -0,0 +1,15 @@
+age-encryption.org/v1
+-> ssh-ed25519 dcsaLw cpeIOVtFcfaHZpIAp495fkQLJoT++h1v6p0crBeuzFM
++zomKCg7UVNl/FlfcZflVPbo48C45uGoGoR1tbetEdk
+-> ssh-ed25519 zcCuhA loSmQUCnO0EBaGg+wFYYkXOdLBQ6Z+pPl4Y3oGx6xzw
++RdXNYYtIDDXGr1Z0Mh28psvF9gzg12M3EJTUqmdFtU
+-> ssh-ed25519 CpJBgQ 0W0LWu8WW6pQzUhK21CeNDUtW0srwR5gNCRjwTy94B4
+A02F+AyP+DajnVTJakx+0jynYRDix9I/9uZUDPjXpis
+-> ssh-ed25519 aXKGcg SVBo2urAYGSYrlj3ieoi9nkrffcZ9ZroCn86pZkn4nI
+xQRrLNeNcI9cpQY+X2xfLDoBqLNQixGjaYtMDWtHio4
+-> ssh-ed25519 BXptmQ UKNJPPjIiqPQndZ6/yASSg+5PQIn2N9nUy2hQMREq1Y
+X9zM/ji9R3jLOEDGLpIVESjU13VU0e3cTAR1xEMhY5I
+-> B-grease Y
+vUOYknqY0okoUOKZD/8MpnpwkOU31sszuUZfeSVsuVyUMPEbFjWQT74
+--- ymKMaoUQXFPRc9U0ZvULBEC0Az0ew2oEyHwH/kR9ETI
+ŠEu”…	«―­x§αΝΣe_)zPΊεh‡ΣΣΚωˆ–s£žGΰθ΄Κ•BLQ
\ No newline at end of file
diff --git a/ops/secrets/depot-replica-key.age b/ops/secrets/depot-replica-key.age
new file mode 100644
index 0000000000..5e8ce94d5d
--- /dev/null
+++ b/ops/secrets/depot-replica-key.age
Binary files differdiff --git a/ops/secrets/gerrit-autosubmit.age b/ops/secrets/gerrit-autosubmit.age
new file mode 100644
index 0000000000..2e04be952d
--- /dev/null
+++ b/ops/secrets/gerrit-autosubmit.age
Binary files differdiff --git a/ops/secrets/gerrit-queue.age b/ops/secrets/gerrit-queue.age
deleted file mode 100644
index 68dd1e7e2e..0000000000
--- a/ops/secrets/gerrit-queue.age
+++ /dev/null
Binary files differdiff --git a/ops/secrets/gerrit-secrets.age b/ops/secrets/gerrit-secrets.age
index 02a3c66b53..9ad123d578 100644
--- a/ops/secrets/gerrit-secrets.age
+++ b/ops/secrets/gerrit-secrets.age
Binary files differdiff --git a/ops/secrets/grafana.age b/ops/secrets/grafana.age
index ad503dc32a..eef349d64c 100644
--- a/ops/secrets/grafana.age
+++ b/ops/secrets/grafana.age
@@ -1,13 +1,16 @@
 age-encryption.org/v1
--> ssh-ed25519 dcsaLw CrJGrkztUpn+XkED1hn4Clr/oBNrer9J+/fdqDhgx18
-VWENh02k4HTkhDS2F219vrCUVuxXFOCPsCW+8eeZHs4
--> ssh-ed25519 CpJBgQ 8Lm14o93CEh/aerPtMiStKYtqF/HdgJD05uRRegLgUs
-b0H5XBOe4nepmGzl646Ar0XAazzHAJeTLCCGUVaZyW0
--> ssh-ed25519 aXKGcg SKWLHNM0WeFJoGlOPbI6v7CebdSK3qAmQ6kMW5YbIz4
-kQD7Oh9mQeCXyXzOc1kVI8ShE0J89TzuZBOboaQn7sE
--> ssh-ed25519 OkGqLg ablfqKN1GYY3GWGCHGtciRFJwO4e0kbcS75Kaj+elUA
-PQPeRVzV/Yi0lxI7U+lNbCpeatymazj7GjQLhmL4YI8
--> gse~-grease
-I9X7cHnmfbsnu/4AeVVtTRlbguJDylrAlCOqTOt11Gtg/Ft2fnZZTOmsKo8
---- 3xk3ls7SR7s394FtfqLwxgUDjTPMjnhLz79ClvIm4pE
-ΌyFτzΌΈΔHρΨ©ΩΑ·γ*MΎΘ\«26I`koΞƒΈ&baWŸvM™£έ.΄Υέjγώ£‘ΑFΘ;ZνΕNΦ*λP²
•_JŠσc…_ό‰(έSͺjζdΉο8Fα
\ No newline at end of file
+-> ssh-ed25519 dcsaLw 0h55HIHm0kf6LqtI99LFUWBCoERBmpoF+anfnxjhDBU
+0bHlgfRABn51BoMwAIjUlaVnCr3ZDXkQPmFOiIV3TvI
+-> ssh-ed25519 zcCuhA 0vFMP1qFEiN4MUt+1qQCqtEovmO2d6QHj+KjHBrvqB4
+CUM2MDNPEKpksyCQmfDg/k/CKz7/ckgafw4aj0FLcmE
+-> ssh-ed25519 CpJBgQ Y971kTqyElTHpOw4D7mUfkIQFWELOBeuGPUE6bqSrXQ
+zt3ju2cqDfQJg9BsSsWcOGfPu5Q4XuIz0k2gasaRCPE
+-> ssh-ed25519 aXKGcg eNxh3cCMbxG/u4luhlE2WQVzFMlZIcDKDx4dcpK43hY
+HGJZYkWbYA0I7HtArCz9ErXwAAfOBHe20JH1J5Bx904
+-> ssh-ed25519 OkGqLg a1+l3dkThz8LLp7C1D9l7CzdB8Q4hxjNzaY7B6HMSnQ
+du3nw0b61TGdF91Mq7C/PpjDlnIIph1dVEIivcDpM7M
+-> \gwpw]-grease p#:x#sA ^S5*A/ ZpY
+1rTU2Rc5MnpJj8zwOK4yR9HvDPOiKjCKHOURq6ak4SUmEgqqyqoujzRaL4I0cKf0
+zMFTkoKnLXjjLiHyvJWqCGwCRq9veUsTiJ6jqs+y6L+YaT71qDzDXi3YfX2p
+--- hraNRaUxkHCnhk6AC/3jyxaAj1gyyIi0Q7cqoupcRrA
+‘ϋ:Ά'ƒ!«37« ›s+0»@ΑγΧ―¨Ώdκ ?ο!%οl¬Ψ΄ΐ͎ΐ;ΕψϋΑ2’ΏΛξ‚‘BΎ—!†/όύg½Ψγ±/Ž°:wuΥ‰―ςδ[©ύ~˜Ž₯³ΐΡχp‹©F΅
\ No newline at end of file
diff --git a/ops/secrets/irccat.age b/ops/secrets/irccat.age
index 5a45efa7cc..2002b15c49 100644
--- a/ops/secrets/irccat.age
+++ b/ops/secrets/irccat.age
Binary files differdiff --git a/ops/secrets/journaldriver.age b/ops/secrets/journaldriver.age
new file mode 100644
index 0000000000..c58773f36b
--- /dev/null
+++ b/ops/secrets/journaldriver.age
Binary files differdiff --git a/ops/secrets/keycloak-db.age b/ops/secrets/keycloak-db.age
index 5942bf24c2..54194df183 100644
--- a/ops/secrets/keycloak-db.age
+++ b/ops/secrets/keycloak-db.age
Binary files differdiff --git a/ops/secrets/mkSecrets.nix b/ops/secrets/mkSecrets.nix
index 4e40112b96..c99130835f 100644
--- a/ops/secrets/mkSecrets.nix
+++ b/ops/secrets/mkSecrets.nix
@@ -22,6 +22,6 @@ in
 
 defun [ path (attrs agenixSecret) (attrs any) ]
   (path: secrets:
-    depot.nix.readTree.drvTargets
-      # Import each secret into the Nix store
-      (builtins.mapAttrs (name: _: "${path}/${name}") secrets))
+  depot.nix.readTree.drvTargets
+    # Import each secret into the Nix store
+    (builtins.mapAttrs (name: _: "${path}/${name}") secrets))
diff --git a/ops/secrets/nix-cache-priv.age b/ops/secrets/nix-cache-priv.age
index 4a16897eb2..0381fb1290 100644
--- a/ops/secrets/nix-cache-priv.age
+++ b/ops/secrets/nix-cache-priv.age
Binary files differdiff --git a/ops/secrets/nix-cache-pub.age b/ops/secrets/nix-cache-pub.age
index 692d869015..ae06f49d69 100644
--- a/ops/secrets/nix-cache-pub.age
+++ b/ops/secrets/nix-cache-pub.age
@@ -1,13 +1,16 @@
 age-encryption.org/v1
--> ssh-ed25519 dcsaLw 2wWiYCk+TcJdGdiT+YWVvv1FZ28EJYykwseyiZ9pkzs
-AMvMQQsWe3nar2TQM+wcyD2PEKlE9PeSx8G2ufJzEzI
--> ssh-ed25519 CpJBgQ SpGruCznXleG0wmFMUTGJf7VNGKLEYqeQb/mv+axKxM
-SL4MTYEiOFgp6+90Fp3QFnSzFUfMWxNF2OHdH3Q+uy0
--> ssh-ed25519 aXKGcg wWO1kn2tUlBZoMFsO1JrVhyqJCfv1BNhoVfKBwfidmA
-A3PAoWzbJWSlIKxGYsUEvuwRbDvRTjZYUdeSi+LQa1M
--> ssh-ed25519 OkGqLg 2usxSwcnF2tZbJt6R7M+psTSW2M5HcZgr51t47D01GI
-HVGRSasPX9/I9E9oZhhMd6hVK/ga3n/UYzRAe2CjRqI
--> /oh-grease v* Qu8SiS 2
-5dc
---- 59MLx4Yl2G9G8QjEp+gOrKBPjCqm/ntgg8guQICu/x0
-Ω`ΰ	8DJΖ]ξsΣPλέ±Rwa!β7k47<i:'?)ΏΦΠΥ©τξ‡εΩΏόSΈ}ςRΑop)θ_wIKpδ:έήSŒ5ς0k½ω	žΞj˜ν
\ No newline at end of file
+-> ssh-ed25519 dcsaLw +jfxfM1YDu5CoYtFeRWtpkUQhmFWn/kNBYsBnie7BVg
+XxL9l87hXD0zCUEwbSR9OHSYgpOw89Km5iyxPPnVDGQ
+-> ssh-ed25519 zcCuhA VAoDkN2gwErUFE/59V4IF9PbSBSleOjt2gosvYnHxWg
+Pf6eh8EfAdATjZIkQfhhqOXuJXIdwIpybITcn+rcutI
+-> ssh-ed25519 CpJBgQ C6zIv78gu+wBeAjhmXANegSNqGHnugemXBPQcTimgxg
+80109g83Hk+smWuZkTIZJ6VFQqJ+LU1boWKQIH1AHjc
+-> ssh-ed25519 aXKGcg lPb+kGr0vuJkQO6VutAm4Yh1CVi/XfqNdGbAh/B7ZRk
+h4xb++7I9iv8208oqY0xLruA1r62mepISFcusczdbgs
+-> ssh-ed25519 OkGqLg aOHt9OR8JChtYpclkgn9wCFnlayFje7WsMGQb8AqChU
+3VRTDMUwFtDcoxGU/wiBzTvS0SB/xOpBG6s+ENvAXVE
+-> Kow$7|\-grease
+8OGnQnY7gm4vMJRXjnBogA0HRU7hqIxs2sErFc7sV1CUNkZlFjdK8tZomlNwshjc
+p18HgtjJnaGhSqg1LyP7cJAo/XnSwDYCeNna/6vdlKBR3JeuOGTmx1NIG/cGSg
+--- w+jJplb/J3av+UcltcFf4qSqHoQ8Ol8lH/fFB3051Gw
+qIόνe:1*`j8υ±sΊnHcyΞΰ7£²™ΘΓ΅ε(ͺγΎ.•˜xžDΈά_}‚%σ)P,DζΣ6«SΝιHΕκΓU9°λ”¬Τ0έν8ΝΤν\³φ—'
\ No newline at end of file
diff --git a/ops/secrets/oauth2_proxy.age b/ops/secrets/oauth2_proxy.age
deleted file mode 100644
index baddeef1e3..0000000000
--- a/ops/secrets/oauth2_proxy.age
+++ /dev/null
@@ -1,14 +0,0 @@
-age-encryption.org/v1
--> ssh-ed25519 dcsaLw 3vCzURGgzn7i3pZp39oSfYy1F331qBDewFgjocK4/nE
-doccb4CZhyrA7jvbuG3i8nowApVGKWfIejJjLeXnb9Q
--> ssh-ed25519 CpJBgQ 4KovCGvA0cBvEkhfyantUCny49hTu4L038xj/ZG2lCg
-o9iaan7jKGYukS4IiTVLV5YqjiycaWyPXyo3x6k8Jhk
--> ssh-ed25519 aXKGcg stGJqj37f0E6S0qJW/r/cYXIoT+l4ERG0c/CMckpS28
-aNP2LcrFe9wLB1dnbJjoUTa8ckpMbR3cJtltDn/8st8
--> ssh-ed25519 OkGqLg UK89eEeI/SOWUaR4jg4rDuKFOkzsf6PgNkcphUCoyj8
-o3WFOhB0B2T1F8mxb5qw25S4r9bYyc4tqwLb/iK0TAY
--> ZcBt-grease P*F$|]1G *a9 ^dTv-Whe K`GVU
-mwq98CjcnoinoAsGUM2PolGrXBZhs9jbUQB8qEAZ7Qtzd6z6BjGoPGr4bjokZQ08
-RwOx9jBmAAFaW9Ak5JX9RBvxu/IIz6xVmQ8a8ev95tA
---- ShfGC5iYYwDC5fXRkZV9Oh1aHJONbdR1EaAp+lrKWUE
-Γω’t~EͺV¨Ά/Φ–υE}‚_ϋHšηRΙ7†PsSw>Β½Ϋμ­ZoœD`n)™έ·K,T}
ˆ'Fύ[ S³KyΞ7m"ΠEΏG3ζ"Ξυ΅Ηlj倕ςΩ^b[ΦΑMδlf›pžήXχCΗπ”²Χ]ε!N]mϋφωγ
g¨·Ύ0Φφ€ϊ!°g˜π―1­‡ΠβY ΪXύ
\ No newline at end of file
diff --git a/ops/secrets/owothia.age b/ops/secrets/owothia.age
index 845252dd1d..177ee61383 100644
--- a/ops/secrets/owothia.age
+++ b/ops/secrets/owothia.age
@@ -1,15 +1,16 @@
 age-encryption.org/v1
--> ssh-ed25519 dcsaLw ZV01yZa6uSpirIxPgW8fLJ3lI/RRb0tRObGey3zlgGE
-cu64HZYAxEL0qbUKcQEGzzQpwkAvXwp6NYYGaoHNwPw
--> ssh-ed25519 CpJBgQ +NoCEPUKCscQxZLdjFI5YwWNiQuj8klra4AceYAOR1A
-xhNGia9flgRDn2QNsklyotwU7nJ9elXV8jMkT8XfUEA
--> ssh-ed25519 aXKGcg MsimFAWS4vN6exoeKA2PVin+82QXzt32oS9iei6f4l4
-i+ph/HZ6a5f9QWorgwt0RFvmV4E4HpGSmkZAqdXhZ68
--> ssh-ed25519 OkGqLg zLXi3YNberKHC7b/La1FdrLgLowjB4wovnXo/ayqeQs
-dYIN5zvmbMsN5yjhVrccjwYqXJHV9zcEJCjTnMIs55g
--> FlqGql'-grease
-g+GgOSpwwnqLywaY4h9wMA2h7buTMM8vYEufiyTOOOSD7ljq1cgBePAoCFluW8UW
-8SDabs5WTRYgqqDnzVkx9V3JeIWJrfiKQj9coLZ1Crx5+YRD9r766eGEvHOC5eat
-432j
---- V/bZkitOabEh8PO3J8dmv/IgycQOF5CmMvGTsHTdmlo
-Ουυ7(‘±t²NΩΦΣΒFϊ·gžΐΚΏZ‡&oo,Ζ¦²Q1μ(^乍μ…K}W‚Ω14ο«)υ‚D@׏ΈŸŽ”πYΐΪόYi
\ No newline at end of file
+-> ssh-ed25519 dcsaLw 8XtdgZ++/ZqmK4j8CO8oiuskTxjvKhWDK7fet5hbqiM
+Fs4O1vFtQL1JamnuCMPLzfzRPb90nxfXB6OXkyCMoHo
+-> ssh-ed25519 zcCuhA 6PNsPMdRXM77ci+mBQNRxr1oMGDNdlQilpUB0Q5es28
+APw2L/0htM9U0fJ1IUthdkoem/UTM/6NNQrgn4Vmpcs
+-> ssh-ed25519 CpJBgQ ed00il0q23M+3KH6hf5fFPaXGUKcz03Bn01jSoKiB1U
+jEN0Dk2edJBQreAlNE11sx0cI5u1mfFDT11Ev0KJ+gs
+-> ssh-ed25519 aXKGcg NocBhG6QGlWDZhjsA6Sxvjv9Gs+3Pq5gcOqnVdiefBg
+HYnqBv0pdPz8bqgZ98VDfYFeKcFNeuJrlOsyWt551Sg
+-> ssh-ed25519 OkGqLg e0081m/IkQafXh1gAWUZ2glYG7bklCG/LaUy63rK6gc
+G2RNMxCxRnqocYhiq142T8EPZQD8cRHHs7AHKFrMLaU
+-> +J}@hPk-grease
+406BMfqUt/KjayTopj4dNa4owPZphR6AsBXPurJwU/zV9ipirfW3oEeaprdh4uLg
+RHO0bSZQV1uu1YmbXkuwMaVj1cVn2vsDPEv3xG2SRzMoEpAAKaFCBba8
+--- 5ncLI9pS25vz5CebIZjPPDQ5cHISlyRFF55rGgFQnnM
+α$"‹PKͺ&C·\ε(ΥGΐ[›Ο(
š¬·DπΦlΗΊΉ8‘4ηξP”Ζ)©΅‰¬Ή™ΊB‚’ΤΌ£ρc\₯U:ψΚ¬(
\ No newline at end of file
diff --git a/ops/secrets/panettone.age b/ops/secrets/panettone.age
index a8a176fb13..0be42dc0a7 100644
--- a/ops/secrets/panettone.age
+++ b/ops/secrets/panettone.age
@@ -1,16 +1,15 @@
 age-encryption.org/v1
--> ssh-ed25519 dcsaLw lFE6Oxzl0jaGpmfxEzmvywEyxsmPNfhv+NNR95XGiDI
-NJhZ6KFNLcScSR5iNB5IAL4UqWzort+jWypbKQPsxu0
--> ssh-ed25519 CpJBgQ 7sMqCFUdss274yNWtYbXe+l7oevKaR99d6E7c4LWtjg
-rqwEyv2dT07qd87suVZxk+8+bmA2W6MFkoG8NktRRbY
--> ssh-ed25519 aXKGcg 9/0QlqFKxPVwjwagBTWHdhJXWWYXn0v649ZhmzpUxWc
-pMs+PoMRi3FghN2odcBQ9tpE+0Mb/jaErnOnuuoq4sw
--> ssh-ed25519 OkGqLg Is/FQ/8s+oq+qThcwOdnAgCrZX/kNBLc0Cwpvi2NMwk
-Zf31SwMF/fyBd1d899GPv8Z8A8GSBy5xuG4d8zL9Zz0
--> wyU-grease Dzk;3o # ,q\WtGwI
-PoJGe6Xlhl47AhFLxM4HLaEYAqcx9lzodHasyZ1AH0BtdSFYT92cYw/1rSNWheTk
-YedxiXNrosw
---- tJE6XbPtWlMYKHItyPlThcnLnmp/9AS1muhfgDosTCk
-$;˜ !†Ψσ?;
-žb²ηHŸ}ωοΤΠΓfx°QΕΝ€dήaα·κˆ‡K7Ώ€1O†UΣ³<Ή‘Μθ‹	«ηy8Υmf–ΊV~ΙJͺό°Z½π0³¬|‡tNψΰ[ΓOχ;π2;/™³3)vδGP%ͺ€ν‰J0ΦY%Ψ¨’d’[1¦k/ύ+³
-ώ
\ No newline at end of file
+-> ssh-ed25519 dcsaLw zzUe0JqhICtd/kgZnXFpwaQ1Ma6nqy/hMWaOJpRHmDs
+4cR+OnWShG6MpB/u0yfsSxplEch7x7DbygfBiJGxOOs
+-> ssh-ed25519 zcCuhA 0RZEYC9IuazO9fROalwoOCIgc0j+rNBP3gw7SKG0yEw
+mPRhN0hvccEr1A9ihWAFMH4/24vpBKpxBVq4BKBMmYM
+-> ssh-ed25519 CpJBgQ VrmfTtTVxuQmpUxMxtXtCnr8pFyqwtdyLHdbzYrlKlM
+kHgEdPmoIOLnGuMF5F5Ol1yZWcactSE4OZI0BSmDN+g
+-> ssh-ed25519 aXKGcg On4jwgsH504ZjYRwfw5oAfIDk3wU0+xgd43ryAn9H0I
+fayzht1ZPPiFCjuYTdwVtJu2nOUg4wtp5IipOR4oJm8
+-> ssh-ed25519 OkGqLg mubp0xI0fvsKOAUaNaftFkHJ+bxgFHbgjn+A7sR8XVs
+X68Zr8HvC4/XPC0AFIA5f1SKu7NSR/23oeX8cW1qfis
+-> ?`-grease
+hOy2Rwvk6+vXpHWWA49Wp10wKbw9TfsLXw
+--- 9MLGx6BVm40C0CSV3bq6dnXrpy3QunBlh2/uO5OisUU
+Η³Gž<ΥεΑΠYΧAχVs³π/-%όgͺϊ.έe@†,Zρ‹ζ•όF˜Wζ”Ά&ζξ§σ<Oφq@Ύ>wε‡Μ›Q‡>™-gΗ“'©Μ†`‘Ά¨XφŸΟP8—³x<RNv·9Χ#'/)ΐ¦g‚¦ϊθέm2υ©Τvπύ<,ί7…χγΙι‚’ΠΗq―¦ͺv焁Q»·AOΞ-σژ†+gεcΚ#ή΅—ε½ξ’*–’°Ÿeν -§·) ω;
\ No newline at end of file
diff --git a/ops/secrets/secrets.nix b/ops/secrets/secrets.nix
index 53f0d39318..5cbf2bf612 100644
--- a/ops/secrets/secrets.nix
+++ b/ops/secrets/secrets.nix
@@ -1,10 +1,17 @@
 let
+  flokli = [
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTVTXOutUZZjXLB0lUSgeKcSY/8mxKkC0ingGK1whD2 flokli"
+  ];
+
   tazjin = [
     # tverskoy
     "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM1fGWz/gsq+ZeZXjvUrV+pBlanw1c3zJ9kLTax9FWQy"
+
+    # zamalek
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDBRXeb8EuecLHP0bW4zuebXp4KRnXgJTZfeVWXQ1n1R"
   ];
 
-  grfn = [
+  aspen = [
     "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMcBGBoWd5pPIIQQP52rcFOQN3wAY0J/+K2fuU6SffjA "
   ];
 
@@ -12,26 +19,36 @@ let
     "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJk+KvgvI2oJTppMASNUfMcMkA2G5ZNt+HnWDzaXKLlo"
   ];
 
+  sanduny = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOag0XhylaTVhmT6HB8EN2Fv5Ymrc4ZfypOXONUkykTX";
   whitby = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILNh/w4BSKov0jdz3gKBc98tpoLta5bb87fQXWBhAl2I";
 
-  default.publicKeys = tazjin ++ grfn ++ sterni ++ [ whitby ];
-in {
-  "besadii.age" = default;
-  "buildkite-agent-token.age" = default;
-  "buildkite-graphql-token.age" = default;
-  "clbot-ssh.age" = default;
-  "clbot.age" = default;
-  "gerrit-queue.age" = default;
-  "gerrit-secrets.age" = default;
-  "grafana.age" = default;
-  "irccat.age" = default;
-  "keycloak-db.age" = default;
-  "nix-cache-priv.age" = default;
-  "nix-cache-pub.age" = default;
-  "oauth2_proxy.age" = default;
-  "owothia.age" = default;
-  "panettone.age" = default;
-  "smtprelay.age" = default;
-  "tf-glesys.age" = default;
-  "tf-keycloak.age" = default;
+  terraform.publicKeys = tazjin ++ aspen ++ sterni ++ flokli;
+  whitbyDefault.publicKeys = tazjin ++ aspen ++ sterni ++ [ whitby ];
+  allDefault.publicKeys = tazjin ++ aspen ++ sterni ++ [ sanduny whitby ];
+  sandunyDefault.publicKeys = tazjin ++ aspen ++ sterni ++ [ sanduny ];
+in
+{
+  "besadii.age" = whitbyDefault;
+  "buildkite-agent-token.age" = whitbyDefault;
+  "buildkite-graphql-token.age" = whitbyDefault;
+  "buildkite-ssh-private-key.age" = whitbyDefault;
+  "clbot-ssh.age" = whitbyDefault;
+  "clbot.age" = whitbyDefault;
+  "depot-inbox-imap.age" = sandunyDefault;
+  "depot-replica-key.age" = whitbyDefault;
+  "gerrit-autosubmit.age" = whitbyDefault;
+  "gerrit-secrets.age" = whitbyDefault;
+  "grafana.age" = whitbyDefault;
+  "irccat.age" = whitbyDefault;
+  "journaldriver.age" = allDefault;
+  "keycloak-db.age" = whitbyDefault;
+  "nix-cache-priv.age" = whitbyDefault;
+  "nix-cache-pub.age" = whitbyDefault;
+  "owothia.age" = whitbyDefault;
+  "panettone.age" = whitbyDefault;
+  "smtprelay.age" = whitbyDefault;
+  "tf-buildkite.age" = terraform;
+  "tf-glesys.age" = terraform;
+  "tf-keycloak.age" = terraform;
+  "tvl-alerts-bot-telegram-token.age" = whitbyDefault;
 }
diff --git a/ops/secrets/smtprelay.age b/ops/secrets/smtprelay.age
index 166d2638e1..62fbaffadf 100644
--- a/ops/secrets/smtprelay.age
+++ b/ops/secrets/smtprelay.age
@@ -1,14 +1,16 @@
 age-encryption.org/v1
--> ssh-ed25519 dcsaLw xcNp0GhoE++itBIAUi+0OIKlLENHGqklq02/YGQbH0A
-34OgtbXFlhvjYJQI8zysSKdZiK7FBKn+lunvR1TWYrE
--> ssh-ed25519 CpJBgQ RSWDjIWDt3nbVmvOusrkmy8K+A15Fph/ApbbBw5L7VA
-mP+nnsLaVkeMAAMJ8nsBq4CAw66lVF87bmvGMmsT55A
--> ssh-ed25519 aXKGcg YBiyBkcEWP+5m8fTHPWlGKTfyN92gfhJQkmAxJ3Zei0
-dnnJmSII9wmPJ1jL8s8COPjxoIip4HwWPpmK5jNNlcE
--> ssh-ed25519 OkGqLg 1A5xPUHzoN+lXYlwKlbV42JCI1l361IyyllZ2HmxGCc
-Mi8igtdp0yFEM6lfiT/PqtA6+KWwqS5EWkmtKS+JBWk
--> ]3/,-grease Fj#1m Vq3REqK
-+sNTJq8Vdns
---- y1d0IBqYwo/ABm9XOEQG26UA7NtTg+8mg/QLtPyMLwc
-OΩt΅γXƒOW +]+Μ7|ˆD€|n#Ώ‘pP₯S·a“έ”#•ζ}v2†‘bM»ΞΘφœv¨?VΨϋΌ ω9²BΛ±₯}£v¨MόϋŒο
-ΗUv—Mq‘€@ΐI
5mήNx
ΜΗ
\ No newline at end of file
+-> ssh-ed25519 dcsaLw CW2Lgm0tSWUDwKSNSX/aLkVzQ/QeEeQgU3NITpz2D0M
+F7dA+zWdCz21s443bj9zCz6lBsRlFIxiG+l8CdbuPFk
+-> ssh-ed25519 zcCuhA l8rsBoYDwhUB5stbeGXYTQ4Fz745ywXFCOQZn2cMBW0
+TycVcUZjR2TDv5DPC54+RwoU6Fj4QpRUJj1j0HM/JCE
+-> ssh-ed25519 CpJBgQ CbwZO5LmSxd0HRYkf+lV+ymFcXSn/49GAPHG4l1I7gw
+xSmab5+BnAZF/B0n32xX1qZPdHgfoEMGIuZqlpnISjc
+-> ssh-ed25519 aXKGcg Tr+odf9p1RBrQK1guR6ToeN4wG1KLA3jwiPIkgyEjws
+TaeCnjiRp8VZoMS5qs+OfVbBc6zudayD693h/eGvVOo
+-> ssh-ed25519 OkGqLg Dmnsqz6PKzMd6w4t+l6+EWuia+stPwSEtu00KVuAojo
+rZ/i1WJhrCM/ZQTAroRRSjzUVJw2UJlPUe1uHYqSscw
+-> w!^Z-grease i86O2 i0.Rch
+/zsRadAGYzAY6F/J5m6lMjmojkN7NbY3TbfQbA
+--- /rQgwuY9SVGLKeUzY5P6c+sGQ1I1aw5cQxmO46QKDSQ
+ ι(`――€U ¬ω‹š,γΓcΌι|‘Pζη• Ώ9α@&	«ΗgMί’
+CHβž3ikχΑΔ3#|έεΦgžΈMΦ³A•΄—g’AΦϊnZσΗY—βt¨Ψϋ―Μ2Ή‰±K2ޘ…YΪ
\ No newline at end of file
diff --git a/ops/secrets/tf-buildkite.age b/ops/secrets/tf-buildkite.age
new file mode 100644
index 0000000000..0cf6066fa6
--- /dev/null
+++ b/ops/secrets/tf-buildkite.age
Binary files differdiff --git a/ops/secrets/tf-glesys.age b/ops/secrets/tf-glesys.age
index 53aa5e1acb..4e50454b62 100644
--- a/ops/secrets/tf-glesys.age
+++ b/ops/secrets/tf-glesys.age
Binary files differdiff --git a/ops/secrets/tf-keycloak.age b/ops/secrets/tf-keycloak.age
index ddc477b21a..237b9377bd 100644
--- a/ops/secrets/tf-keycloak.age
+++ b/ops/secrets/tf-keycloak.age
Binary files differdiff --git a/ops/secrets/tvl-alerts-bot-telegram-token.age b/ops/secrets/tvl-alerts-bot-telegram-token.age
new file mode 100644
index 0000000000..e897fedc03
--- /dev/null
+++ b/ops/secrets/tvl-alerts-bot-telegram-token.age
@@ -0,0 +1,15 @@
+age-encryption.org/v1
+-> ssh-ed25519 dcsaLw JGXCnhez0LnlUV8eOitxizmxw/gV+1taBRhNvwvVcms
+qsRTOpifnoc0eorFjd4UlP7O3hkRR3KjDUcImASK0jY
+-> ssh-ed25519 zcCuhA KUcyaHcmuqCGtJBzvc2UK17gRrjzuzIxll+TS9Q4nWs
+CAJ19ClA9Tqj1fcYySq+K9gdZe6Uv0toZLnhlovr3tM
+-> ssh-ed25519 CpJBgQ OAE+u9JuC6KoefjCOTj4NkQElZRe6/EEIAGBN/XelnU
+M9MHlKxbEBJ+gACo2FiYqmm1cAoYW31+nP16qnVZ7Zw
+-> ssh-ed25519 aXKGcg Ll6v6v5HpUIEuOzjpVsPMmPQMnNkmyB4fz/YwNXfCHU
+MmFQy2WkKn5SM0bhe4NNe/lMnneKoOF+Ufq0t0QjNbw
+-> ssh-ed25519 OkGqLg PS6KLwat1z2BSQ9sIKDaryVU39EJR+iiAaKSP/KSPk0
+qUQP2f4MFk83zQ9edlSNC8jwpJvmp2xhOysd8rnYzW4
+-> >NI-grease @mOcHT z|%,s- mw^c *
+zu0M2pS6v3zehnLg
+--- jltBYy9brAtpkEIqPoGmIVe3s5XnWtpa9EmuXlAf91c
+št”dX2-Ή"ΔΣ#Ζ1›νn'ώƒ\‰ψ'{Dlw;PΦ΄Π@Ϊ̏™ή{ωίB	!y£+™xυΛΠνW΅ΆΔB:wtΩqph
\ No newline at end of file
diff --git a/ops/terraform/README.md b/ops/terraform/README.md
new file mode 100644
index 0000000000..9ff6c23d47
--- /dev/null
+++ b/ops/terraform/README.md
@@ -0,0 +1,5 @@
+//ops/terraform
+===============
+
+This folder contains Terraform modules and other related
+Terraform-tooling by TVL.
diff --git a/ops/terraform/deploy-nixos/README.md b/ops/terraform/deploy-nixos/README.md
new file mode 100644
index 0000000000..fd0bd1b442
--- /dev/null
+++ b/ops/terraform/deploy-nixos/README.md
@@ -0,0 +1,50 @@
+<!--
+SPDX-FileCopyrightText: 2023 The TVL Authors
+
+SPDX-License-Identifier: MIT
+-->
+
+deploy-nixos
+============
+
+This is a Terraform module to deploy a NixOS system closure to a
+remote machine.
+
+The system closure must be accessible by Nix-importing the repository
+root and building a specific attribute
+(e.g. `nix-build -A ops.machines.machine-name`).
+
+The target machine must be accessible normally over SSH, and an SSH
+key must be used for access.
+
+Notably this module separates the evaluation of the system closure from building
+and deploying it, and uses the closure's derivation hash to determine whether a
+deploy is necessary.
+
+## Usage example:
+
+```terraform
+module "deploy_somehost" {
+  source              = "git::https://code.tvl.fyi/depot.git:/ops/terraform/deploy-nixos.git"
+  attrpath            = "ops.nixos.somehost"
+  target_host         = "somehost.tvl.su"
+  target_user         = "someone"
+  target_user_ssh_key = tls_private_key.somehost.private_key_pem
+}
+```
+
+## Future work
+
+Several things can be improved about this module, for example:
+
+* The repository root (relative to which the attribute path is evaluated) could
+  be made configurable.
+
+* The remote system closure could be discovered to restore remote system state
+  after manual deploys on the target (i.e. "stomping" of changes).
+
+More ideas and contributions are, of course, welcome.
+
+## Acknowledgements
+
+Development of this module was sponsored by [Resoptima](https://resoptima.com/).
diff --git a/ops/terraform/deploy-nixos/main.tf b/ops/terraform/deploy-nixos/main.tf
new file mode 100644
index 0000000000..50278b248e
--- /dev/null
+++ b/ops/terraform/deploy-nixos/main.tf
@@ -0,0 +1,113 @@
+# SPDX-FileCopyrightText: 2023 The TVL Authors
+#
+# SPDX-License-Identifier: MIT
+
+# This module deploys a NixOS host by building a system closure
+# located at the specified attribute in the current repository.
+#
+# The closure's derivation path is persisted in the Terraform state to
+# determine after Nix evaluation whether the system closure has
+# changed and needs to be built/deployed.
+#
+# The system configuration is then built (or substituted) on the
+# machine that runs `terraform apply`, then copied and activated on
+# the target machine using `nix-copy-closure`.
+
+variable "attrpath" {
+  description = "attribute set path pointing to the NixOS system closure"
+  type        = string
+}
+
+variable "target_host" {
+  description = "address (IP or hostname) at which the target is reachable"
+  type        = string
+}
+
+variable "entrypoint" {
+  description = <<EOT
+    Path to a .nix file (or directory containing `default.nix` file)
+    that provides the attrset specified in `closure`.
+    If unset, asks git for the root of the repository.
+  EOT
+  type        = string
+  default     = ""
+}
+
+variable "target_user" {
+  description = "username on the target machine"
+  type        = string
+}
+
+variable "target_user_ssh_key" {
+  description = "SSH key to use for connecting to the target"
+  type        = string
+  default     = ""
+  sensitive   = true
+}
+
+variable "triggers" {
+  type        = map(string)
+  description = "Triggers for deploy"
+  default     = {}
+}
+
+# Fetch the derivation hash for the NixOS system.
+data "external" "nixos_system" {
+  program = ["${path.module}/nix-eval.sh"]
+
+  query = {
+    attrpath   = var.attrpath
+    entrypoint = var.entrypoint
+  }
+}
+
+# Deploy the NixOS configuration if anything changed.
+resource "null_resource" "nixos_deploy" {
+  connection {
+    type        = "ssh"
+    host        = var.target_host
+    user        = var.target_user
+    private_key = var.target_user_ssh_key
+  }
+
+  # 1. Wait for SSH to become available.
+  provisioner "remote-exec" {
+    inline = ["true"]
+  }
+
+  # 2. Build NixOS system.
+  provisioner "local-exec" {
+    command = "nix-build ${data.external.nixos_system.result.drv} --no-out-link"
+  }
+
+  # 3. Copy closure to the target.
+  provisioner "local-exec" {
+    command = "${path.module}/nixos-copy.sh"
+
+    environment = {
+      SYSTEM_DRV  = data.external.nixos_system.result.drv
+      TARGET_HOST = var.target_host
+      DEPLOY_KEY  = var.target_user_ssh_key
+      TARGET_USER = var.target_user
+    }
+  }
+
+  # 4. Activate closure on the target.
+  provisioner "remote-exec" {
+    inline = [
+      "set -eu",
+      "SYSTEM=$(nix-build ${data.external.nixos_system.result.drv} --no-out-link)",
+      "sudo nix-env --profile /nix/var/nix/profiles/system --set $SYSTEM",
+      "sudo $SYSTEM/bin/switch-to-configuration switch",
+    ]
+  }
+
+  triggers = merge({
+    nixos_drv   = data.external.nixos_system.result.drv
+    target_host = var.target_host
+  }, var.triggers)
+}
+
+output "nixos_drv" {
+  value = data.external.nixos_system.result
+}
diff --git a/ops/terraform/deploy-nixos/nix-eval.sh b/ops/terraform/deploy-nixos/nix-eval.sh
new file mode 100755
index 0000000000..65f534180b
--- /dev/null
+++ b/ops/terraform/deploy-nixos/nix-eval.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+# SPDX-FileCopyrightText: 2023 The TVL Authors
+#
+# SPDX-License-Identifier: MIT
+set -ueo pipefail
+
+# Evaluates a Nix expression.
+#
+# Receives input parameters as JSON from stdin.
+# It expects a dict with the following keys:
+#
+#  - `attrpath`: the attribute.path pointing to the expression to instantiate.
+#    Required.
+#  - `entrypoint`: the path to the Nix file to invoke.
+#    Optional. If omitted, will shell out to git to determine the repo root,
+#    and Nix will use `default.nix` in there.
+#  - `argstr_json`: A string JSON-encoding a map containing string keys and
+#    values which should be passed to Nix as `--argstr $key $value`.
+#    command line args. Optional.
+#  - `build`: A boolean (or string being "true" or "false") stating whether the
+#    expression should also be built/substituted on the machine executing this script.
+#
+# jq's @sh format takes care of escaping.
+eval "$(jq -r '@sh "attrpath=\(.attrpath) && entrypoint=\(.entrypoint) && argstr=\((.argstr_json // "{}"|fromjson) | to_entries | map ("--argstr", .key, .value) | join(" ")) build=\(.build)"')"
+
+# Evaluate the expression.
+[[ -z "$entrypoint" ]] && entrypoint=$(git rev-parse --show-toplevel)
+# shellcheck disable=SC2086,SC2154
+drv=$(nix-instantiate -A "${attrpath}" "${entrypoint}" ${argstr})
+
+# If `build` is set to true, invoke nix-build on the .drv.
+# We need to swallow all stdout, to not garble the JSON printed later.
+# shellcheck disable=SC2154
+if [ "${build}" == "true" ]; then
+  nix-build --no-out-link "${drv}" > /dev/null
+fi
+
+# Determine the output path.
+outPath=$(nix show-derivation "${drv}" | jq -r ".\"${drv}\".outputs.out.path")
+
+# Return a JSON back to stdout.
+# It contains the following keys:
+#
+# - `drv`: the store path of the Derivation that has been instantiated.
+# - `outPath`: the output store path.
+jq -n --arg drv "$drv" --arg outPath "$outPath" '{"drv":$drv, "outPath":$outPath}'
diff --git a/ops/terraform/deploy-nixos/nixos-copy.sh b/ops/terraform/deploy-nixos/nixos-copy.sh
new file mode 100755
index 0000000000..6b843c3a49
--- /dev/null
+++ b/ops/terraform/deploy-nixos/nixos-copy.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+# SPDX-FileCopyrightText: 2023 The TVL Authors
+#
+# SPDX-License-Identifier: MIT
+
+#
+# Copies a NixOS system to a target host, using the provided key,
+# or whatever ambient key is configured if the key is not set.
+set -ueo pipefail
+
+export NIX_SSHOPTS="\
+    -o StrictHostKeyChecking=no\
+    -o UserKnownHostsFile=/dev/null\
+    -o GlobalKnownHostsFile=/dev/null"
+
+# If DEPLOY_KEY was passed, write it to $scratch/id_deploy
+if [ -n "${DEPLOY_KEY-}" ]; then
+  scratch="$(mktemp -d)"
+  trap 'rm -rf -- "${scratch}"' EXIT
+
+  echo -n "$DEPLOY_KEY" > $scratch/id_deploy
+  chmod 0600 $scratch/id_deploy
+  export NIX_SSHOPTS="$NIX_SSHOPTS -o IdentityFile=$scratch/id_deploy"
+fi
+
+nix-copy-closure \
+  --to ${TARGET_USER}@${TARGET_HOST} \
+  ${SYSTEM_DRV} \
+  --gzip \
+  --include-outputs \
+  --use-substitutes
diff --git a/ops/users/default.nix b/ops/users/default.nix
index 4f88e75b65..c54a681dce 100644
--- a/ops/users/default.nix
+++ b/ops/users/default.nix
@@ -2,6 +2,11 @@
 
 [
   {
+    username = "aaqaishtyaq";
+    email = "aaqaishtyaq@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$IpWJeEYTYEsrgGBNQcnbWA$w4+gQmeJlhddeaHvmbpNa3hDVg1BkJESZSVAd2eSOs4";
+  }
+  {
     username = "adisbladis";
     email = "adisbladis@gmail.com";
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$wdgoLRrUgZuz0Kin9YiNgQ$E40VIgzgpMpylZqkfByTKiWQnerupfuf7LDgOsU8tJA";
@@ -12,6 +17,11 @@
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$8lefg7+8UPAEh9Ott8zH0A$7YuLRraTC1IgxTNTxFJF03AWmqBS3GX2+vfD4XVTrb0";
   }
   {
+    username = "aspen";
+    email = "root@gws.fyi";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$5NEYPJ19nDITK5sGr4bzhQ$Xzpzth6y4w+HGvioHiYgzqFiwMDx0B7HAh+PVbkRuuk";
+  }
+  {
     username = "cschilling";
     email = "christian.schilling.de@gmail.com";
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$9VN3IS6ViW5FFbVKWOZI6Q$gZxuYAYk0Opq4E5i8cbcNjfznCQNc+RiP7Xv1CUnrQU";
@@ -52,9 +62,9 @@
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$TrezbwIY5TKLnJiii0wafQ$K0S2p9I8tiqP907nkgoK6IbG9ia4IuDiylTcIs5pesw";
   }
   {
-    username = "grfn";
-    email = "grfn@gws.fyi";
-    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$5NEYPJ19nDITK5sGr4bzhQ$Xzpzth6y4w+HGvioHiYgzqFiwMDx0B7HAh+PVbkRuuk";
+    username = "ghuntley";
+    email = "ghuntley@ghuntley.com";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$ciCuQHeA7csqrFUv7+asgw$7GUC5fLJWWVoHP8DvpA+C1u4+iFdV2E311kwTFwGzaQ";
   }
   {
     username = "htbf";
@@ -62,11 +72,31 @@
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$2iVXQQfd26icaIguHJg/CQ$hA9ziqn7kQ06AV6uQxJCGXoG8f+LWmH+nVlk00a1n/c";
   }
   {
+    username = "IslandUsurper";
+    email = "lyle@menteeth.us";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$rNSsa8aYU4qvxeFnADgW1g$Zu6B6Al2usRRNfAKhWXzCAfiTfV3XQb0W6Op5TYN1oI";
+  }
+  {
     username = "isomer";
     email = "isomer@tvl.fyi";
     password = "{SSHA}OhWQkPJgH1rRJqYIaMUbbKC4iLEzvCev";
   }
   {
+    username = "j4m3s";
+    email = "james.landrein@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$dMYmo+Uym9irtzAGXB2eNw$69OFcuqCqoLPBXKmmtYaQCquXximpyxsb2Kf8U7GdxM";
+  }
+  {
+    username = "jfroche";
+    email = "jfroche@pyxel.be";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$kA19gDabD1Fjy82olcmnsA$TTbkpAc0WYaA4DT2vc7+NAGXhC4Os1tPqZVpHFkzecE";
+  }
+  {
+    username = "jrhahn";
+    email = "mail.jhahn@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$giiu99hS7CzfsDZgxMNvKg$JiZZnFxOGHZRlUziYd3TkEiUplMz7Emy8fXfyLawPS0";
+  }
+  {
     username = "kn";
     email = "klemens@posteo.de";
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$CoRZInysud4sduDoMjVOCw$/bdvAvyPO2DPxOcHlBiG2+rbTGF9XAcHUhPurxiIpZM";
@@ -77,6 +107,11 @@
     password = "{SSHA}7a85VNhpFElFw+N5xcjgGmt4HnBsaGp4";
   }
   {
+    username = "noteed";
+    email = "noteed@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$rcLfF9xXysSx5sahVQLiMA$EgRgAVXn8+r2Csa3XgIHIEBf3hX4Y58pOHf2eDaBUnA";
+  }
+  {
     username = "nyanotech";
     email = "nyanotechnology@gmail.com";
     password = "{SSHA}NIJ2RCRb1+Q4Bs63cyE91VZyiN47DG6y";
@@ -104,6 +139,11 @@
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$o2OcfhfKOry+UrcmODyQCw$qloaQgoIRDESwaA3yqPxxy8sgLk3mrjYFBbF41elVrM";
   }
   {
+    username = "talyz";
+    email = "kim.lindberger@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$KYgHYsxX/DZDhnxdkzn1/w$L2Yyc2lYAREZP0FD3iX57MB6gzoOCcVmCGDxIsUGAgk";
+  }
+  {
     username = "tazjin";
     email = "tazjin@tvl.su";
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$wOPEl9D3kSke//oLtbvqrg$j0npwwXgaXQ/emefKUwL59tH8hdmtzbgH2rQzWSmE2Y";
@@ -155,8 +195,38 @@
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$NQdBVPNwh2ioDq9zWfMusA$2cABJGI8cU2JZirnVU5E5C28sTiePkiOPEAaqNUp/Fk";
   }
   {
-    username = "zseri";
-    email = "zseri.devel@ytrizja.de";
+    username = "fogti";
+    email = "fogti+devel@ytrizja.de";
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$wVNkImXloXIkCycnecdFeA$ECAdGdNzUUEq9sFGsIl0jb7AALGsHE+ndWRn6ilSmdE";
   }
+  {
+    username = "brainrake";
+    email = "martonboros@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$f4/ewdyRBQbClL4KzqypHg$6Ql/xkmfIr60Qp1XMaFherqhh4cekLIbsi7KMM6izfE";
+  }
+  {
+    username = "raitobezarius";
+    email = "tvl@lahfa.xyz";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$3NZTBbF5dZssAHC/ktcA/Q$AZxHGG0ycNMOkIxC/ONYbyhNxC9hb6cpWvnsNH8LWZk";
+  }
+  {
+    username = "hsjobeki";
+    email = "hsjobeki@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$jez9eVa2v0BznIJMOhw+hw$wUbwCS+Bfcjjzr08saQE6NNTPWNXWWaxv+UtBCdYC2s";
+  }
+  {
+    username = "totikom";
+    email = "eugene.lomov@protonmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$r/EsEGkqCcv8ccjQ84pX7Q$ebpWno7LI1RXkWKBjnkDHZM1gPuPj1LSMoFUsX0j6AU";
+  }
+  {
+    username = "espes";
+    email = "espes@pequalsnp.com";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$eXeFrbNxuKn/JCpQr5VmxA$NtMNBceNg/JtqMfHk/qHxEHsEVsTWmHJbpq4ve/+XYg";
+  }
+  {
+    username = "caralice";
+    email = "tvl@alice-carroll.pet";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$mt/0RzKw4RHxm7ybpMHP5Q$P/SDBMv5si9D98NFO/eZgh2+InlByqYxqAvQWhl+p0c";
+  }
 ]
diff --git a/ops/yandex-base-image/default.nix b/ops/yandex-base-image/default.nix
new file mode 100644
index 0000000000..3dc4b8f589
--- /dev/null
+++ b/ops/yandex-base-image/default.nix
@@ -0,0 +1,9 @@
+# Base image for Yandex Cloud VMs.
+{ depot, ... }:
+
+(depot.ops.nixos.nixosFor {
+  imports = [
+    (depot.path.origSrc + ("/ops/modules/yandex-cloud.nix"))
+    (depot.path.origSrc + ("/ops/modules/tvl-users.nix"))
+  ];
+}).config.system.build.yandexCloudImage
diff --git a/ops/yandex-cloud-rs/.gitignore b/ops/yandex-cloud-rs/.gitignore
new file mode 100644
index 0000000000..ab3f21a96e
--- /dev/null
+++ b/ops/yandex-cloud-rs/.gitignore
@@ -0,0 +1,5 @@
+target/
+result/
+# Ignore everything under src (except for lib.rs)
+src/*
+!src/lib.rs
diff --git a/ops/yandex-cloud-rs/Cargo.lock b/ops/yandex-cloud-rs/Cargo.lock
new file mode 100644
index 0000000000..0015d43106
--- /dev/null
+++ b/ops/yandex-cloud-rs/Cargo.lock
@@ -0,0 +1,1368 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "anyhow"
+version = "1.0.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "axum"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "sync_wrapper",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "mime",
+ "rustversion",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "flate2"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "h2"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
+[[package]]
+name = "hyper"
+version = "0.14.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
+dependencies = [
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+ "tokio-io-timeout",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
+name = "js-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.146"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "log"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+
+[[package]]
+name = "matchit"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "multimap"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "petgraph"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "prettyplease"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+dependencies = [
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prost"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
+dependencies = [
+ "bytes",
+ "heck",
+ "itertools",
+ "lazy_static",
+ "log",
+ "multimap",
+ "petgraph",
+ "prettyplease",
+ "prost",
+ "prost-types",
+ "regex",
+ "syn 1.0.109",
+ "tempfile",
+ "which",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
+dependencies = [
+ "prost",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
+dependencies = [
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "rustix"
+version = "0.37.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-webpki",
+ "sct",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
+dependencies = [
+ "base64",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.100.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
+dependencies = [
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "sct"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.164"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
+
+[[package]]
+name = "slab"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "tempfile"
+version = "3.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio"
+version = "1.28.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2"
+dependencies = [
+ "autocfg",
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-io-timeout"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
+dependencies = [
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tonic"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
+dependencies = [
+ "async-stream",
+ "async-trait",
+ "axum",
+ "base64",
+ "bytes",
+ "flate2",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-timeout",
+ "percent-encoding",
+ "pin-project",
+ "prost",
+ "rustls-native-certs",
+ "rustls-pemfile",
+ "tokio",
+ "tokio-rustls",
+ "tokio-stream",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tonic-build"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07"
+dependencies = [
+ "prettyplease",
+ "proc-macro2",
+ "prost-build",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "indexmap",
+ "pin-project",
+ "pin-project-lite",
+ "rand",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "walkdir"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
+dependencies = [
+ "log",
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+
+[[package]]
+name = "web-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "which"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
+dependencies = [
+ "either",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "yandex-cloud"
+version = "2023.9.4"
+dependencies = [
+ "prost",
+ "prost-types",
+ "tokio",
+ "tonic",
+ "tonic-build",
+ "walkdir",
+]
diff --git a/ops/yandex-cloud-rs/Cargo.toml b/ops/yandex-cloud-rs/Cargo.toml
new file mode 100644
index 0000000000..a72d11d59a
--- /dev/null
+++ b/ops/yandex-cloud-rs/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "yandex-cloud"
+description = "Generated gRPC clients for the Yandex Cloud API"
+license = "MIT"
+version = "2023.9.4"
+edition = "2021"
+homepage = "https://cs.tvl.fyi/depot/-/tree/ops/yandex-cloud-rs"
+repository = "https://code.tvl.fyi/depot.git:/ops/yandex-cloud-rs.git"
+include = [ "/src", "README.md" ]
+
+[dependencies]
+prost = "0.11"
+prost-types = "0.11"
+
+[dependencies.tonic]
+version = "0.9"
+features = [ "tls", "tls-roots", "gzip" ]
+
+[build-dependencies]
+tonic-build = "0.9"
+walkdir = "2.3.3"
+
+[dev-dependencies]
+tokio = "1.28" # check when updating tonic
diff --git a/ops/yandex-cloud-rs/README.md b/ops/yandex-cloud-rs/README.md
new file mode 100644
index 0000000000..a80fa83163
--- /dev/null
+++ b/ops/yandex-cloud-rs/README.md
@@ -0,0 +1,49 @@
+yandex-cloud-rs
+===============
+
+Client library for Yandex Cloud gRPC APIs, as published in their
+[GitHub repository][repo].
+
+Please see the [online documentation][docs] for user-facing
+information, this README is intended for library developers.
+
+The source code of the library lives [in the TVL repository][code].
+
+-------------
+
+In order to build this library, the gRPC API definitions need to be
+fetched from GitHub. By default this is done by Nix (see
+`default.nix`), which then injects the location of the API definitions
+through the `YANDEX_CLOUD_PROTOS` environment variable.
+
+The actual code generation happens through the calls in `build.rs`.
+
+Releases of this library are done from *dirty* trees, meaning that the
+version on crates.io should already contain all the generated code. In
+order to do this, after bumping the version in `Cargo.toml` and the
+API commit in `default.nix`, the following release procedure should be
+used:
+
+```
+# Get rid of all generated source files
+find src | grep '.rs$' | grep -v '^src/lib.rs$' | xargs rm
+
+# Get rid of all old artefacts
+cargo clean
+
+# Verify that a clean build works as intended
+cargo build
+
+# Verify that all documentation builds, and verify that it looks fine:
+#
+# - Is the version correct (current date)?
+# - Are all the services included (i.e. not an accidental empty build)?
+cargo doc --open
+
+# If everything looks fine, release:
+cargo publish --allow-dirty
+```
+
+[repo]: https://github.com/yandex-cloud/cloudapi
+[docs]: https://docs.rs/yandex-cloud/latest/yandex_cloud/
+[code]: https://cs.tvl.fyi/depot/-/tree/ops/yandex-cloud-rs
diff --git a/ops/yandex-cloud-rs/build.rs b/ops/yandex-cloud-rs/build.rs
new file mode 100644
index 0000000000..e9a96ef9df
--- /dev/null
+++ b/ops/yandex-cloud-rs/build.rs
@@ -0,0 +1,43 @@
+use std::path::PathBuf;
+use walkdir::{DirEntry, WalkDir};
+
+fn proto_files(proto_dir: &str) -> Vec<PathBuf> {
+    let mut out = vec![];
+
+    fn is_proto(entry: &DirEntry) -> bool {
+        entry.file_type().is_file()
+            && entry
+                .path()
+                .extension()
+                .map(|e| e.to_string_lossy() == "proto")
+                .unwrap_or(false)
+    }
+
+    for entry in WalkDir::new(format!("{}/yandex", proto_dir)).into_iter() {
+        let entry = entry.expect("failed to list proto files");
+
+        if is_proto(&entry) {
+            out.push(entry.into_path())
+        }
+    }
+
+    out
+}
+
+fn main() {
+    if let Some(proto_dir) = option_env!("YANDEX_CLOUD_PROTOS") {
+        tonic_build::configure()
+            .build_client(true)
+            .build_server(false)
+            .out_dir("src/")
+            .include_file("includes.rs")
+            .compile(
+                &proto_files(proto_dir),
+                &[
+                    format!("{}", proto_dir),
+                    format!("{}/third_party/googleapis", proto_dir),
+                ],
+            )
+            .expect("failed to generate gRPC clients for Yandex Cloud")
+    }
+}
diff --git a/ops/yandex-cloud-rs/default.nix b/ops/yandex-cloud-rs/default.nix
new file mode 100644
index 0000000000..6a8b263dee
--- /dev/null
+++ b/ops/yandex-cloud-rs/default.nix
@@ -0,0 +1,22 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  protoSrc = pkgs.fetchFromGitHub {
+    owner = "yandex-cloud";
+    repo = "cloudapi";
+    rev = "b4383be5ebe360bd946e49c8eaf647a73e9c44c0";
+    sha256 = "0z4jyw2cylvyrq5ja8pcaqnlf6lf6ximj85hgjag6ckawayk1rzx";
+  };
+in
+pkgs.rustPlatform.buildRustPackage rec {
+  name = "yandex-cloud-rs";
+  src = depot.third_party.gitignoreSource ./.;
+  cargoLock.lockFile = ./Cargo.lock;
+  YANDEX_CLOUD_PROTOS = "${protoSrc}";
+  nativeBuildInputs = [ pkgs.protobuf ];
+
+  # The generated doc comments contain lots of things that rustc
+  # *thinks* are doctests, but are actually just garbage leading to
+  # compiler errors.
+  doCheck = false;
+}
diff --git a/ops/yandex-cloud-rs/examples/log-write.rs b/ops/yandex-cloud-rs/examples/log-write.rs
new file mode 100644
index 0000000000..84d183421a
--- /dev/null
+++ b/ops/yandex-cloud-rs/examples/log-write.rs
@@ -0,0 +1,37 @@
+//! This example uses the Yandex Cloud Logging API to write a log entry.
+
+use prost_types::Timestamp;
+use tonic::transport::channel::Endpoint;
+use yandex_cloud::yandex::cloud::logging::v1::destination::Destination;
+use yandex_cloud::yandex::cloud::logging::v1::log_ingestion_service_client::LogIngestionServiceClient;
+use yandex_cloud::yandex::cloud::logging::v1::Destination as OuterDestination;
+use yandex_cloud::yandex::cloud::logging::v1::IncomingLogEntry;
+use yandex_cloud::yandex::cloud::logging::v1::WriteRequest;
+use yandex_cloud::AuthInterceptor;
+
+#[tokio::main(flavor = "current_thread")]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let channel = Endpoint::from_static("https://ingester.logging.yandexcloud.net")
+        .connect()
+        .await?;
+
+    let mut client = LogIngestionServiceClient::with_interceptor(
+        channel,
+        AuthInterceptor::new("YOUR_TOKEN_HERE"),
+    );
+
+    let request = WriteRequest {
+        destination: Some(OuterDestination {
+            destination: Some(Destination::LogGroupId("YOUR_LOG_GROUP_ID".into())),
+        }),
+        entries: vec![IncomingLogEntry {
+            timestamp: Some(Timestamp::date_time(2023, 04, 24, 23, 44, 30).unwrap()),
+            message: "test log message".into(),
+            ..Default::default()
+        }],
+        ..Default::default()
+    };
+
+    client.write(request).await.unwrap();
+    Ok(())
+}
diff --git a/ops/yandex-cloud-rs/src/lib.rs b/ops/yandex-cloud-rs/src/lib.rs
new file mode 100644
index 0000000000..e7f79c75be
--- /dev/null
+++ b/ops/yandex-cloud-rs/src/lib.rs
@@ -0,0 +1,108 @@
+//! This module provides low-level generated gRPC clients for the
+//! Yandex Cloud APIs.
+//!
+//! The clients are generated using the [tonic][] and [prost][]
+//! crates and have default configuration.
+//!
+//! Documentation present in the protos is retained into the generated
+//! Rust types, but for detailed API information you should visit the
+//! official Yandex Cloud Documentation pages:
+//!
+//! * [in English](https://cloud.yandex.com/en-ru/docs/overview/api)
+//! * [in Russian](https://cloud.yandex.ru/docs/overview/api)
+//!
+//! The proto sources are available on the [Yandex Cloud GitHub][protos].
+//!
+//! [tonic]: https://docs.rs/tonic/latest/tonic/
+//! [prost]: https://docs.rs/prost/latest/prost/
+//! [protos]: https://github.com/yandex-cloud/cloudapi
+//!
+//! The majority of user-facing structures can be found in the
+//! [`yandex::cloud`] module.
+//!
+//! ## Usage
+//!
+//! Typically to use these APIs, you need to provide an authentication
+//! credential and an endpoint to connect to. The full list of
+//! Yandex's endpoints is [available online][endpoints] and you should
+//! look up the service you plan to use and pick the correct endpoint
+//! from the list.
+//!
+//! Authentication is done via an HTTP header using an IAM token,
+//! which can be done in Tonic using [interceptors][]. The
+//! [`AuthInterceptor`] provided by this crate can be used for that
+//! purpose.
+//!
+//! Full usage examples are [available here][examples].
+//!
+//! [endpoints]: https://cloud.yandex.com/en/docs/api-design-guide/concepts/endpoints
+//! [interceptors]: https://docs.rs/tonic/latest/tonic/service/trait.Interceptor.html
+//! [examples]: https://code.tvl.fyi/tree/ops/yandex-cloud-rs/examples
+
+use tonic::metadata::{Ascii, MetadataValue};
+use tonic::service::Interceptor;
+
+/// Publicly re-export some types from tonic which users might need
+/// for implementing traits, or for naming concrete client types.
+pub mod tonic_exports {
+    pub use tonic::service::interceptor::InterceptedService;
+    pub use tonic::transport::Channel;
+    pub use tonic::transport::Endpoint;
+    pub use tonic::Status;
+}
+
+/// Helper trait for types or closures that can provide authentication
+/// tokens for Yandex Cloud.
+pub trait TokenProvider {
+    /// Fetch a currently valid authentication token for Yandex Cloud.
+    fn get_token<'a>(&'a mut self) -> Result<&'a str, tonic::Status>;
+}
+
+impl TokenProvider for String {
+    fn get_token<'a>(&'a mut self) -> Result<&'a str, tonic::Status> {
+        Ok(self.as_str())
+    }
+}
+
+impl TokenProvider for &'static str {
+    fn get_token(&mut self) -> Result<&'static str, tonic::Status> {
+        Ok(*self)
+    }
+}
+
+/// Interceptor for adding authentication headers to gRPC requests.
+/// This is constructed with a callable that returns authentication
+/// tokens.
+///
+/// This callable is responsible for ensuring that the returned tokens
+/// are valid at the given time, i.e. it should take care of
+/// refreshing and so on.
+pub struct AuthInterceptor<T: TokenProvider> {
+    token_provider: T,
+}
+
+impl<T: TokenProvider> AuthInterceptor<T> {
+    pub fn new(token_provider: T) -> Self {
+        Self { token_provider }
+    }
+}
+
+impl<T: TokenProvider> Interceptor for AuthInterceptor<T> {
+    fn call(
+        &mut self,
+        mut request: tonic::Request<()>,
+    ) -> Result<tonic::Request<()>, tonic::Status> {
+        let token: MetadataValue<Ascii> = format!("Bearer {}", self.token_provider.get_token()?)
+            .try_into()
+            .map_err(|_| {
+                tonic::Status::invalid_argument("authorization token contained invalid characters")
+            })?;
+
+        request.metadata_mut().insert("authorization", token);
+
+        Ok(request)
+    }
+}
+
+// The rest of this file is generated by the build script at ../build.rs.
+include!("includes.rs");
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 0000000000..3a26366d4d
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+edition = "2021"
diff --git a/third_party/abseil_cpp/.clang-format b/third_party/abseil_cpp/.clang-format
deleted file mode 100644
index 06ea346a10..0000000000
--- a/third_party/abseil_cpp/.clang-format
+++ /dev/null
@@ -1,4 +0,0 @@
----
-Language:        Cpp
-BasedOnStyle:  Google
-...
diff --git a/third_party/abseil_cpp/.github/ISSUE_TEMPLATE/00-bug_report.md b/third_party/abseil_cpp/.github/ISSUE_TEMPLATE/00-bug_report.md
deleted file mode 100644
index 1edf3de0ba..0000000000
--- a/third_party/abseil_cpp/.github/ISSUE_TEMPLATE/00-bug_report.md
+++ /dev/null
@@ -1,41 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: 'bug'
-assignees: ''
----
-
-**Describe the bug**
-
-Include a clear and concise description of what the problem is, including what
-you expected to happen, and what actually happened.
-
-**Steps to reproduce the bug**
-
-It's important that we are able to reproduce the problem that you are
-experiencing. Please provide all code and relevant steps to reproduce the
-problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links
-to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the
-problem are also helpful.
-
-**What version of Abseil are you using?**
-
-**What operating system and version are you using**
-
-If you are using a Linux distribution please include the name and version of the
-distribution as well.
-
-**What compiler and version are you using?**
-
-Please include the output of `gcc -v` or `clang -v`, or the equivalent for your
-compiler.
-
-**What build system are you using?**
-
-Please include the output of `bazel --version` or `cmake --version`, or the
-equivalent for your build system.
-
-**Additional context**
-
-Add any other context about the problem here.
diff --git a/third_party/abseil_cpp/.github/ISSUE_TEMPLATE/90-question.md b/third_party/abseil_cpp/.github/ISSUE_TEMPLATE/90-question.md
deleted file mode 100644
index 84cf349189..0000000000
--- a/third_party/abseil_cpp/.github/ISSUE_TEMPLATE/90-question.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-name: Question
-about: Have a question? Ask us anything! :-)
-title: ''
-labels: 'question'
-assignees: ''
----
diff --git a/third_party/abseil_cpp/.github/ISSUE_TEMPLATE/config.yml b/third_party/abseil_cpp/.github/ISSUE_TEMPLATE/config.yml
deleted file mode 100644
index 9794ae1db8..0000000000
--- a/third_party/abseil_cpp/.github/ISSUE_TEMPLATE/config.yml
+++ /dev/null
@@ -1 +0,0 @@
-blank_issues_enables: true
diff --git a/third_party/abseil_cpp/.gitignore b/third_party/abseil_cpp/.gitignore
deleted file mode 100644
index d54fa5a91f..0000000000
--- a/third_party/abseil_cpp/.gitignore
+++ /dev/null
@@ -1,15 +0,0 @@
-# Ignore all bazel-* symlinks.
-/bazel-*
-# Ignore Bazel verbose explanations
---verbose_explanations
-# Ignore CMake usual build directory
-build
-# Ignore Vim files
-*.swp
-# Ignore QtCreator Project file
-CMakeLists.txt.user
-# Ignore VS Code files
-.vscode/*
-# Ignore generated python artifacts
-*.pyc
-copts/__pycache__/
diff --git a/third_party/abseil_cpp/.skip-subtree b/third_party/abseil_cpp/.skip-subtree
deleted file mode 100644
index d49b47f75a..0000000000
--- a/third_party/abseil_cpp/.skip-subtree
+++ /dev/null
@@ -1 +0,0 @@
-Third-party code with non-depot layout.
diff --git a/third_party/abseil_cpp/ABSEIL_ISSUE_TEMPLATE.md b/third_party/abseil_cpp/ABSEIL_ISSUE_TEMPLATE.md
deleted file mode 100644
index ed5461f166..0000000000
--- a/third_party/abseil_cpp/ABSEIL_ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,22 +0,0 @@
-Please submit a new Abseil Issue using the template below:
-
-## [Short title of proposed API change(s)]
-
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
-
-## Background
-
-[Provide the background information that is required in order to evaluate the
-proposed API changes. No controversial claims should be made here. If there are
-design constraints that need to be considered, they should be presented here
-**along with justification for those constraints**. Linking to other docs is
-good, but please keep the **pertinent information as self contained** as
-possible in this section.]
-
-## Proposed API Change (s)
-
-[Please clearly describe the API change(s) being proposed. If multiple changes,
-please keep them clearly distinguished. When possible, **use example code
-snippets to illustrate before-after API usages**. List pros-n-cons. Highlight
-the main questions that you want to be answered. Given the Abseil project compatibility requirements, describe why the API change is safe.]
diff --git a/third_party/abseil_cpp/AUTHORS b/third_party/abseil_cpp/AUTHORS
deleted file mode 100644
index 976d31defc..0000000000
--- a/third_party/abseil_cpp/AUTHORS
+++ /dev/null
@@ -1,6 +0,0 @@
-# This is the list of Abseil authors for copyright purposes.
-#
-# This does not necessarily list everyone who has contributed code, since in
-# some cases, their employer may be the copyright holder.  To see the full list
-# of contributors, see the revision history in source control.
-Google Inc.
diff --git a/third_party/abseil_cpp/BUILD.bazel b/third_party/abseil_cpp/BUILD.bazel
deleted file mode 100644
index 79fb0ecd73..0000000000
--- a/third_party/abseil_cpp/BUILD.bazel
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# Copyright 2020 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])  # Apache 2.0
-
-# Expose license for external usage through bazel.
-exports_files([
-    "AUTHORS",
-    "LICENSE",
-])
diff --git a/third_party/abseil_cpp/CMake/AbseilDll.cmake b/third_party/abseil_cpp/CMake/AbseilDll.cmake
deleted file mode 100644
index e0ff2492e7..0000000000
--- a/third_party/abseil_cpp/CMake/AbseilDll.cmake
+++ /dev/null
@@ -1,514 +0,0 @@
-include(CMakeParseArguments)
-
-set(ABSL_INTERNAL_DLL_FILES
-  "algorithm/algorithm.h"
-  "algorithm/container.h"
-  "base/attributes.h"
-  "base/call_once.h"
-  "base/casts.h"
-  "base/config.h"
-  "base/const_init.h"
-  "base/dynamic_annotations.h"
-  "base/internal/atomic_hook.h"
-  "base/internal/bits.h"
-  "base/internal/cycleclock.cc"
-  "base/internal/cycleclock.h"
-  "base/internal/direct_mmap.h"
-  "base/internal/dynamic_annotations.h"
-  "base/internal/endian.h"
-  "base/internal/errno_saver.h"
-  "base/internal/exponential_biased.cc"
-  "base/internal/exponential_biased.h"
-  "base/internal/fast_type_id.h"
-  "base/internal/hide_ptr.h"
-  "base/internal/identity.h"
-  "base/internal/invoke.h"
-  "base/internal/inline_variable.h"
-  "base/internal/low_level_alloc.cc"
-  "base/internal/low_level_alloc.h"
-  "base/internal/low_level_scheduling.h"
-  "base/internal/per_thread_tls.h"
-  "base/internal/periodic_sampler.cc"
-  "base/internal/periodic_sampler.h"
-  "base/internal/pretty_function.h"
-  "base/internal/raw_logging.cc"
-  "base/internal/raw_logging.h"
-  "base/internal/scheduling_mode.h"
-  "base/internal/scoped_set_env.cc"
-  "base/internal/scoped_set_env.h"
-  "base/internal/strerror.h"
-  "base/internal/strerror.cc"
-  "base/internal/spinlock.cc"
-  "base/internal/spinlock.h"
-  "base/internal/spinlock_wait.cc"
-  "base/internal/spinlock_wait.h"
-  "base/internal/sysinfo.cc"
-  "base/internal/sysinfo.h"
-  "base/internal/thread_annotations.h"
-  "base/internal/thread_identity.cc"
-  "base/internal/thread_identity.h"
-  "base/internal/throw_delegate.cc"
-  "base/internal/throw_delegate.h"
-  "base/internal/tsan_mutex_interface.h"
-  "base/internal/unaligned_access.h"
-  "base/internal/unscaledcycleclock.cc"
-  "base/internal/unscaledcycleclock.h"
-  "base/log_severity.cc"
-  "base/log_severity.h"
-  "base/macros.h"
-  "base/optimization.h"
-  "base/options.h"
-  "base/policy_checks.h"
-  "base/port.h"
-  "base/thread_annotations.h"
-  "container/btree_map.h"
-  "container/btree_set.h"
-  "container/fixed_array.h"
-  "container/flat_hash_map.h"
-  "container/flat_hash_set.h"
-  "container/inlined_vector.h"
-  "container/internal/btree.h"
-  "container/internal/btree_container.h"
-  "container/internal/common.h"
-  "container/internal/compressed_tuple.h"
-  "container/internal/container_memory.h"
-  "container/internal/counting_allocator.h"
-  "container/internal/hash_function_defaults.h"
-  "container/internal/hash_policy_traits.h"
-  "container/internal/hashtable_debug.h"
-  "container/internal/hashtable_debug_hooks.h"
-  "container/internal/hashtablez_sampler.cc"
-  "container/internal/hashtablez_sampler.h"
-  "container/internal/hashtablez_sampler_force_weak_definition.cc"
-  "container/internal/have_sse.h"
-  "container/internal/inlined_vector.h"
-  "container/internal/layout.h"
-  "container/internal/node_hash_policy.h"
-  "container/internal/raw_hash_map.h"
-  "container/internal/raw_hash_set.cc"
-  "container/internal/raw_hash_set.h"
-  "container/internal/tracked.h"
-  "container/node_hash_map.h"
-  "container/node_hash_set.h"
-  "debugging/failure_signal_handler.cc"
-  "debugging/failure_signal_handler.h"
-  "debugging/leak_check.h"
-  "debugging/leak_check_disable.cc"
-  "debugging/stacktrace.cc"
-  "debugging/stacktrace.h"
-  "debugging/symbolize.cc"
-  "debugging/symbolize.h"
-  "debugging/internal/address_is_readable.cc"
-  "debugging/internal/address_is_readable.h"
-  "debugging/internal/demangle.cc"
-  "debugging/internal/demangle.h"
-  "debugging/internal/elf_mem_image.cc"
-  "debugging/internal/elf_mem_image.h"
-  "debugging/internal/examine_stack.cc"
-  "debugging/internal/examine_stack.h"
-  "debugging/internal/stack_consumption.cc"
-  "debugging/internal/stack_consumption.h"
-  "debugging/internal/stacktrace_config.h"
-  "debugging/internal/symbolize.h"
-  "debugging/internal/vdso_support.cc"
-  "debugging/internal/vdso_support.h"
-  "functional/internal/front_binder.h"
-  "functional/bind_front.h"
-  "functional/function_ref.h"
-  "functional/internal/function_ref.h"
-  "hash/hash.h"
-  "hash/internal/city.h"
-  "hash/internal/city.cc"
-  "hash/internal/hash.h"
-  "hash/internal/hash.cc"
-  "hash/internal/spy_hash_state.h"
-  "memory/memory.h"
-  "meta/type_traits.h"
-  "numeric/int128.cc"
-  "numeric/int128.h"
-  "random/bernoulli_distribution.h"
-  "random/beta_distribution.h"
-  "random/bit_gen_ref.h"
-  "random/discrete_distribution.cc"
-  "random/discrete_distribution.h"
-  "random/distributions.h"
-  "random/exponential_distribution.h"
-  "random/gaussian_distribution.cc"
-  "random/gaussian_distribution.h"
-  "random/internal/distribution_caller.h"
-  "random/internal/fastmath.h"
-  "random/internal/fast_uniform_bits.h"
-  "random/internal/generate_real.h"
-  "random/internal/iostream_state_saver.h"
-  "random/internal/mock_helpers.h"
-  "random/internal/nonsecure_base.h"
-  "random/internal/pcg_engine.h"
-  "random/internal/platform.h"
-  "random/internal/pool_urbg.cc"
-  "random/internal/pool_urbg.h"
-  "random/internal/randen.cc"
-  "random/internal/randen.h"
-  "random/internal/randen_detect.cc"
-  "random/internal/randen_detect.h"
-  "random/internal/randen_engine.h"
-  "random/internal/randen_hwaes.cc"
-  "random/internal/randen_hwaes.h"
-  "random/internal/randen_round_keys.cc"
-  "random/internal/randen_slow.cc"
-  "random/internal/randen_slow.h"
-  "random/internal/randen_traits.h"
-  "random/internal/salted_seed_seq.h"
-  "random/internal/seed_material.cc"
-  "random/internal/seed_material.h"
-  "random/internal/sequence_urbg.h"
-  "random/internal/traits.h"
-  "random/internal/uniform_helper.h"
-  "random/internal/wide_multiply.h"
-  "random/log_uniform_int_distribution.h"
-  "random/poisson_distribution.h"
-  "random/random.h"
-  "random/seed_gen_exception.cc"
-  "random/seed_gen_exception.h"
-  "random/seed_sequences.cc"
-  "random/seed_sequences.h"
-  "random/uniform_int_distribution.h"
-  "random/uniform_real_distribution.h"
-  "random/zipf_distribution.h"
-  "status/internal/status_internal.h"
-  "status/internal/statusor_internal.h"
-  "status/status.h"
-  "status/status.cc"
-  "status/statusor.h"
-  "status/statusor.cc"
-  "status/status_payload_printer.h"
-  "status/status_payload_printer.cc"
-  "strings/ascii.cc"
-  "strings/ascii.h"
-  "strings/charconv.cc"
-  "strings/charconv.h"
-  "strings/cord.cc"
-  "strings/cord.h"
-  "strings/escaping.cc"
-  "strings/escaping.h"
-  "strings/internal/cord_internal.h"
-  "strings/internal/charconv_bigint.cc"
-  "strings/internal/charconv_bigint.h"
-  "strings/internal/charconv_parse.cc"
-  "strings/internal/charconv_parse.h"
-  "strings/internal/stl_type_traits.h"
-  "strings/internal/string_constant.h"
-  "strings/match.cc"
-  "strings/match.h"
-  "strings/numbers.cc"
-  "strings/numbers.h"
-  "strings/str_format.h"
-  "strings/str_cat.cc"
-  "strings/str_cat.h"
-  "strings/str_join.h"
-  "strings/str_replace.cc"
-  "strings/str_replace.h"
-  "strings/str_split.cc"
-  "strings/str_split.h"
-  "strings/string_view.cc"
-  "strings/string_view.h"
-  "strings/strip.h"
-  "strings/substitute.cc"
-  "strings/substitute.h"
-  "strings/internal/char_map.h"
-  "strings/internal/escaping.h"
-  "strings/internal/escaping.cc"
-  "strings/internal/memutil.cc"
-  "strings/internal/memutil.h"
-  "strings/internal/ostringstream.cc"
-  "strings/internal/ostringstream.h"
-  "strings/internal/pow10_helper.cc"
-  "strings/internal/pow10_helper.h"
-  "strings/internal/resize_uninitialized.h"
-  "strings/internal/str_format/arg.cc"
-  "strings/internal/str_format/arg.h"
-  "strings/internal/str_format/bind.cc"
-  "strings/internal/str_format/bind.h"
-  "strings/internal/str_format/checker.h"
-  "strings/internal/str_format/extension.cc"
-  "strings/internal/str_format/extension.h"
-  "strings/internal/str_format/float_conversion.cc"
-  "strings/internal/str_format/float_conversion.h"
-  "strings/internal/str_format/output.cc"
-  "strings/internal/str_format/output.h"
-  "strings/internal/str_format/parser.cc"
-  "strings/internal/str_format/parser.h"
-  "strings/internal/str_join_internal.h"
-  "strings/internal/str_split_internal.h"
-  "strings/internal/utf8.cc"
-  "strings/internal/utf8.h"
-  "synchronization/barrier.cc"
-  "synchronization/barrier.h"
-  "synchronization/blocking_counter.cc"
-  "synchronization/blocking_counter.h"
-  "synchronization/mutex.cc"
-  "synchronization/mutex.h"
-  "synchronization/notification.cc"
-  "synchronization/notification.h"
-  "synchronization/internal/create_thread_identity.cc"
-  "synchronization/internal/create_thread_identity.h"
-  "synchronization/internal/futex.h"
-  "synchronization/internal/graphcycles.cc"
-  "synchronization/internal/graphcycles.h"
-  "synchronization/internal/kernel_timeout.h"
-  "synchronization/internal/per_thread_sem.cc"
-  "synchronization/internal/per_thread_sem.h"
-  "synchronization/internal/thread_pool.h"
-  "synchronization/internal/waiter.cc"
-  "synchronization/internal/waiter.h"
-  "time/civil_time.cc"
-  "time/civil_time.h"
-  "time/clock.cc"
-  "time/clock.h"
-  "time/duration.cc"
-  "time/format.cc"
-  "time/time.cc"
-  "time/time.h"
-  "time/internal/cctz/include/cctz/civil_time.h"
-  "time/internal/cctz/include/cctz/civil_time_detail.h"
-  "time/internal/cctz/include/cctz/time_zone.h"
-  "time/internal/cctz/include/cctz/zone_info_source.h"
-  "time/internal/cctz/src/civil_time_detail.cc"
-  "time/internal/cctz/src/time_zone_fixed.cc"
-  "time/internal/cctz/src/time_zone_fixed.h"
-  "time/internal/cctz/src/time_zone_format.cc"
-  "time/internal/cctz/src/time_zone_if.cc"
-  "time/internal/cctz/src/time_zone_if.h"
-  "time/internal/cctz/src/time_zone_impl.cc"
-  "time/internal/cctz/src/time_zone_impl.h"
-  "time/internal/cctz/src/time_zone_info.cc"
-  "time/internal/cctz/src/time_zone_info.h"
-  "time/internal/cctz/src/time_zone_libc.cc"
-  "time/internal/cctz/src/time_zone_libc.h"
-  "time/internal/cctz/src/time_zone_lookup.cc"
-  "time/internal/cctz/src/time_zone_posix.cc"
-  "time/internal/cctz/src/time_zone_posix.h"
-  "time/internal/cctz/src/tzfile.h"
-  "time/internal/cctz/src/zone_info_source.cc"
-  "types/any.h"
-  "types/bad_any_cast.cc"
-  "types/bad_any_cast.h"
-  "types/bad_optional_access.cc"
-  "types/bad_optional_access.h"
-  "types/bad_variant_access.cc"
-  "types/bad_variant_access.h"
-  "types/compare.h"
-  "types/internal/conformance_aliases.h"
-  "types/internal/conformance_archetype.h"
-  "types/internal/conformance_profile.h"
-  "types/internal/parentheses.h"
-  "types/internal/transform_args.h"
-  "types/internal/variant.h"
-  "types/optional.h"
-  "types/internal/optional.h"
-  "types/span.h"
-  "types/internal/span.h"
-  "types/variant.h"
-  "utility/utility.h"
-)
-
-set(ABSL_INTERNAL_DLL_TARGETS
-  "stacktrace"
-  "symbolize"
-  "examine_stack"
-  "failure_signal_handler"
-  "debugging_internal"
-  "demangle_internal"
-  "leak_check"
-  "leak_check_disable"
-  "stack_consumption"
-  "debugging"
-  "hash"
-  "spy_hash_state"
-  "city"
-  "memory"
-  "strings"
-  "strings_internal"
-  "cord"
-  "str_format"
-  "str_format_internal"
-  "pow10_helper"
-  "int128"
-  "numeric"
-  "utility"
-  "any"
-  "bad_any_cast"
-  "bad_any_cast_impl"
-  "span"
-  "optional"
-  "bad_optional_access"
-  "bad_variant_access"
-  "variant"
-  "compare"
-  "algorithm"
-  "algorithm_container"
-  "graphcycles_internal"
-  "kernel_timeout_internal"
-  "synchronization"
-  "thread_pool"
-  "bind_front"
-  "function_ref"
-  "atomic_hook"
-  "log_severity"
-  "raw_logging_internal"
-  "spinlock_wait"
-  "config"
-  "dynamic_annotations"
-  "core_headers"
-  "malloc_internal"
-  "base_internal"
-  "base"
-  "throw_delegate"
-  "pretty_function"
-  "endian"
-  "bits"
-  "exponential_biased"
-  "periodic_sampler"
-  "scoped_set_env"
-  "type_traits"
-  "meta"
-  "random_random"
-  "random_bit_gen_ref"
-  "random_distributions"
-  "random_seed_gen_exception"
-  "random_seed_sequences"
-  "random_internal_traits"
-  "random_internal_distribution_caller"
-  "random_internal_distributions"
-  "random_internal_fast_uniform_bits"
-  "random_internal_seed_material"
-  "random_internal_pool_urbg"
-  "random_internal_explicit_seed_seq"
-  "random_internal_sequence_urbg"
-  "random_internal_salted_seed_seq"
-  "random_internal_iostream_state_saver"
-  "random_internal_generate_real"
-  "random_internal_wide_multiply"
-  "random_internal_fastmath"
-  "random_internal_nonsecure_base"
-  "random_internal_pcg_engine"
-  "random_internal_randen_engine"
-  "random_internal_platform"
-  "random_internal_randen"
-  "random_internal_randen_slow"
-  "random_internal_randen_hwaes"
-  "random_internal_randen_hwaes_impl"
-  "random_internal_uniform_helper"
-  "status"
-  "time"
-  "civil_time"
-  "time_zone"
-  "container"
-  "btree"
-  "compressed_tuple"
-  "fixed_array"
-  "inlined_vector_internal"
-  "inlined_vector"
-  "counting_allocator"
-  "flat_hash_map"
-  "flat_hash_set"
-  "node_hash_map"
-  "node_hash_set"
-  "container_memory"
-  "hash_function_defaults"
-  "hash_policy_traits"
-  "hashtablez_sampler"
-  "hashtable_debug"
-  "hashtable_debug_hooks"
-  "have_sse"
-  "node_hash_policy"
-  "raw_hash_map"
-  "container_common"
-  "raw_hash_set"
-  "layout"
-  "tracked"
-)
-
-function(absl_internal_dll_contains)
-  cmake_parse_arguments(ABSL_INTERNAL_DLL
-    ""
-    "OUTPUT;TARGET"
-    ""
-    ${ARGN}
-  )
-
-  STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_DLL_TARGET})
-
-  list(FIND
-    ABSL_INTERNAL_DLL_TARGETS
-    "${_target}"
-    _index)
-
-  if (${_index} GREATER -1)
-    set(${ABSL_INTERNAL_DLL_OUTPUT} 1 PARENT_SCOPE)
-  else()
-    set(${ABSL_INTERNAL_DLL_OUTPUT} 0 PARENT_SCOPE)
-  endif()
-endfunction()
-
-function(absl_internal_dll_targets)
-  cmake_parse_arguments(ABSL_INTERNAL_DLL
-  ""
-  "OUTPUT"
-  "DEPS"
-  ${ARGN}
-  )
-
-  set(_deps "")
-  foreach(dep IN LISTS ABSL_INTERNAL_DLL_DEPS)
-    absl_internal_dll_contains(TARGET ${dep} OUTPUT _contains)
-    if (_contains)
-      list(APPEND _deps abseil_dll)
-    else()
-      list(APPEND _deps ${dep})
-    endif()
-  endforeach()
-
-  # Because we may have added the DLL multiple times
-  list(REMOVE_DUPLICATES _deps)
-  set(${ABSL_INTERNAL_DLL_OUTPUT} "${_deps}" PARENT_SCOPE)
-endfunction()
-
-function(absl_make_dll)
-  add_library(
-    abseil_dll
-    SHARED
-      "${ABSL_INTERNAL_DLL_FILES}"
-  )
-  target_link_libraries(
-    abseil_dll
-    PRIVATE
-      ${ABSL_DEFAULT_LINKOPTS}
-  )
-  set_property(TARGET abseil_dll PROPERTY LINKER_LANGUAGE "CXX")
-  target_include_directories(
-    abseil_dll
-    PUBLIC
-      "$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>"
-      $<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}>
-  )
-
-  target_compile_options(
-    abseil_dll
-    PRIVATE
-      ${ABSL_DEFAULT_COPTS}
-  )
-
-  target_compile_definitions(
-    abseil_dll
-    PRIVATE
-      ABSL_BUILD_DLL
-      NOMINMAX
-    INTERFACE
-      ${ABSL_CC_LIB_DEFINES}
-  )
-  install(TARGETS abseil_dll EXPORT ${PROJECT_NAME}Targets
-        RUNTIME DESTINATION ${ABSL_INSTALL_BINDIR}
-        LIBRARY DESTINATION ${ABSL_INSTALL_LIBDIR}
-        ARCHIVE DESTINATION ${ABSL_INSTALL_LIBDIR}
-  )
-endfunction()
diff --git a/third_party/abseil_cpp/CMake/AbseilHelpers.cmake b/third_party/abseil_cpp/CMake/AbseilHelpers.cmake
deleted file mode 100644
index e88507de56..0000000000
--- a/third_party/abseil_cpp/CMake/AbseilHelpers.cmake
+++ /dev/null
@@ -1,402 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-include(CMakeParseArguments)
-include(AbseilConfigureCopts)
-include(AbseilDll)
-include(AbseilInstallDirs)
-
-# The IDE folder for Abseil that will be used if Abseil is included in a CMake
-# project that sets
-#    set_property(GLOBAL PROPERTY USE_FOLDERS ON)
-# For example, Visual Studio supports folders.
-if(NOT DEFINED ABSL_IDE_FOLDER)
-  set(ABSL_IDE_FOLDER Abseil)
-endif()
-
-# absl_cc_library()
-#
-# CMake function to imitate Bazel's cc_library rule.
-#
-# Parameters:
-# NAME: name of target (see Note)
-# HDRS: List of public header files for the library
-# SRCS: List of source files for the library
-# DEPS: List of other libraries to be linked in to the binary targets
-# COPTS: List of private compile options
-# DEFINES: List of public defines
-# LINKOPTS: List of link options
-# PUBLIC: Add this so that this library will be exported under absl::
-# Also in IDE, target will appear in Abseil folder while non PUBLIC will be in Abseil/internal.
-# TESTONLY: When added, this target will only be built if user passes -DABSL_RUN_TESTS=ON to CMake.
-#
-# Note:
-# By default, absl_cc_library will always create a library named absl_${NAME},
-# and alias target absl::${NAME}.  The absl:: form should always be used.
-# This is to reduce namespace pollution.
-#
-# absl_cc_library(
-#   NAME
-#     awesome
-#   HDRS
-#     "a.h"
-#   SRCS
-#     "a.cc"
-# )
-# absl_cc_library(
-#   NAME
-#     fantastic_lib
-#   SRCS
-#     "b.cc"
-#   DEPS
-#     absl::awesome # not "awesome" !
-#   PUBLIC
-# )
-#
-# absl_cc_library(
-#   NAME
-#     main_lib
-#   ...
-#   DEPS
-#     absl::fantastic_lib
-# )
-#
-# TODO: Implement "ALWAYSLINK"
-function(absl_cc_library)
-  cmake_parse_arguments(ABSL_CC_LIB
-    "DISABLE_INSTALL;PUBLIC;TESTONLY"
-    "NAME"
-    "HDRS;SRCS;COPTS;DEFINES;LINKOPTS;DEPS"
-    ${ARGN}
-  )
-
-  if(ABSL_CC_LIB_TESTONLY AND NOT ABSL_RUN_TESTS)
-    return()
-  endif()
-
-  if(ABSL_ENABLE_INSTALL)
-    set(_NAME "${ABSL_CC_LIB_NAME}")
-  else()
-    set(_NAME "absl_${ABSL_CC_LIB_NAME}")
-  endif()
-
-  # Check if this is a header-only library
-  # Note that as of February 2019, many popular OS's (for example, Ubuntu
-  # 16.04 LTS) only come with cmake 3.5 by default.  For this reason, we can't
-  # use list(FILTER...)
-  set(ABSL_CC_SRCS "${ABSL_CC_LIB_SRCS}")
-  foreach(src_file IN LISTS ABSL_CC_SRCS)
-    if(${src_file} MATCHES ".*\\.(h|inc)")
-      list(REMOVE_ITEM ABSL_CC_SRCS "${src_file}")
-    endif()
-  endforeach()
-
-  if("${ABSL_CC_SRCS}" STREQUAL "")
-    set(ABSL_CC_LIB_IS_INTERFACE 1)
-  else()
-    set(ABSL_CC_LIB_IS_INTERFACE 0)
-  endif()
-
-  # Determine this build target's relationship to the DLL. It's one of four things:
-  # 1. "dll"     -- This target is part of the DLL
-  # 2. "dll_dep" -- This target is not part of the DLL, but depends on the DLL.
-  #                 Note that we assume any target not in the DLL depends on the
-  #                 DLL. This is not a technical necessity but a convenience
-  #                 which happens to be true, because nearly every target is
-  #                 part of the DLL.
-  # 3. "shared"  -- This is a shared library, perhaps on a non-windows platform
-  #                 where DLL doesn't make sense.
-  # 4. "static"  -- This target does not depend on the DLL and should be built
-  #                 statically.
-  if (${ABSL_BUILD_DLL})
-    if(ABSL_ENABLE_INSTALL)
-      absl_internal_dll_contains(TARGET ${_NAME} OUTPUT _in_dll)
-    else()
-      absl_internal_dll_contains(TARGET ${ABSL_CC_LIB_NAME} OUTPUT _in_dll)
-    endif()
-    if (${_in_dll})
-      # This target should be replaced by the DLL
-      set(_build_type "dll")
-      set(ABSL_CC_LIB_IS_INTERFACE 1)
-    else()
-      # Building a DLL, but this target is not part of the DLL
-      set(_build_type "dll_dep")
-    endif()
-  elseif(BUILD_SHARED_LIBS)
-    set(_build_type "shared")
-  else()
-    set(_build_type "static")
-  endif()
-
-  # Generate a pkg-config file for every library:
-  if(${_build_type} STREQUAL "static" OR ${_build_type} STREQUAL "shared")
-    if(NOT ABSL_CC_LIB_TESTONLY)
-      if(absl_VERSION)
-        set(PC_VERSION "${absl_VERSION}")
-      else()
-        set(PC_VERSION "head")
-      endif()
-      foreach(dep ${ABSL_CC_LIB_DEPS})
-        if(${dep} MATCHES "^absl::(.*)")
-          set(PC_DEPS "${PC_DEPS} absl_${CMAKE_MATCH_1} = ${PC_VERSION}")
-        endif()
-      endforeach()
-      foreach(cflag ${ABSL_CC_LIB_COPTS})
-        if(${cflag} MATCHES "^(-Wno|/wd)")
-          # These flags are needed to suppress warnings that might fire in our headers.
-          set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
-        elseif(${cflag} MATCHES "^(-W|/w[1234eo])")
-          # Don't impose our warnings on others.
-        else()
-          set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
-        endif()
-      endforeach()
-      FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\
-prefix=${CMAKE_INSTALL_PREFIX}\n\
-exec_prefix=\${prefix}\n\
-libdir=\${prefix}/lib\n\
-includedir=\${prefix}/include\n\
-\n\
-Name: absl_${_NAME}\n\
-Description: Abseil ${_NAME} library\n\
-URL: https://abseil.io/\n\
-Version: ${PC_VERSION}\n\
-Requires.private:${PC_DEPS}\n\
-Libs: -L\${libdir} $<JOIN:${ABSL_CC_LIB_LINKOPTS}, > $<$<NOT:$<BOOL:${ABSL_CC_LIB_IS_INTERFACE}>>:-labsl_${_NAME}>\n\
-Cflags: -I\${includedir}${PC_CFLAGS}\n")
-      INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc"
-              DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
-    endif()
-  endif()
-
-  if(NOT ABSL_CC_LIB_IS_INTERFACE)
-    if(${_build_type} STREQUAL "dll_dep")
-      # This target depends on the DLL. When adding dependencies to this target,
-      # any depended-on-target which is contained inside the DLL is replaced
-      # with a dependency on the DLL.
-      add_library(${_NAME} STATIC "")
-      target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS})
-      absl_internal_dll_targets(
-        DEPS ${ABSL_CC_LIB_DEPS}
-        OUTPUT _dll_deps
-      )
-      target_link_libraries(${_NAME}
-        PUBLIC ${_dll_deps}
-        PRIVATE
-          ${ABSL_CC_LIB_LINKOPTS}
-          ${ABSL_DEFAULT_LINKOPTS}
-      )
-
-      if (ABSL_CC_LIB_TESTONLY)
-        set(_gtest_link_define "GTEST_LINKED_AS_SHARED_LIBRARY=1")
-      else()
-        set(_gtest_link_define)
-      endif()
-
-      target_compile_definitions(${_NAME}
-        PUBLIC
-          ABSL_CONSUME_DLL
-          "${_gtest_link_define}"
-      )
-
-    elseif(${_build_type} STREQUAL "static" OR ${_build_type} STREQUAL "shared")
-      add_library(${_NAME} "")
-      target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS})
-      target_link_libraries(${_NAME}
-      PUBLIC ${ABSL_CC_LIB_DEPS}
-      PRIVATE
-        ${ABSL_CC_LIB_LINKOPTS}
-        ${ABSL_DEFAULT_LINKOPTS}
-      )
-    else()
-      message(FATAL_ERROR "Invalid build type: ${_build_type}")
-    endif()
-
-    # Linker language can be inferred from sources, but in the case of DLLs we
-    # don't have any .cc files so it would be ambiguous. We could set it
-    # explicitly only in the case of DLLs but, because "CXX" is always the
-    # correct linker language for static or for shared libraries, we set it
-    # unconditionally.
-    set_property(TARGET ${_NAME} PROPERTY LINKER_LANGUAGE "CXX")
-
-    target_include_directories(${_NAME}
-      PUBLIC
-        "$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>"
-        $<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}>
-    )
-    target_compile_options(${_NAME}
-      PRIVATE ${ABSL_CC_LIB_COPTS})
-    target_compile_definitions(${_NAME} PUBLIC ${ABSL_CC_LIB_DEFINES})
-
-    # Add all Abseil targets to a a folder in the IDE for organization.
-    if(ABSL_CC_LIB_PUBLIC)
-      set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER})
-    elseif(ABSL_CC_LIB_TESTONLY)
-      set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test)
-    else()
-      set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/internal)
-    endif()
-
-    # INTERFACE libraries can't have the CXX_STANDARD property set
-    set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
-    set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
-
-    # When being installed, we lose the absl_ prefix.  We want to put it back
-    # to have properly named lib files.  This is a no-op when we are not being
-    # installed.
-    if(ABSL_ENABLE_INSTALL)
-      set_target_properties(${_NAME} PROPERTIES
-        OUTPUT_NAME "absl_${_NAME}"
-        # TODO(b/173696973): Figure out how to set SOVERSION for LTS releases.
-        SOVERSION 0
-      )
-    endif()
-  else()
-    # Generating header-only library
-    add_library(${_NAME} INTERFACE)
-    target_include_directories(${_NAME}
-      INTERFACE
-        "$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>"
-        $<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}>
-      )
-
-    if (${_build_type} STREQUAL "dll")
-        set(ABSL_CC_LIB_DEPS abseil_dll)
-    endif()
-
-    target_link_libraries(${_NAME}
-      INTERFACE
-        ${ABSL_CC_LIB_DEPS}
-        ${ABSL_CC_LIB_LINKOPTS}
-        ${ABSL_DEFAULT_LINKOPTS}
-    )
-    target_compile_definitions(${_NAME} INTERFACE ${ABSL_CC_LIB_DEFINES})
-  endif()
-
-  # TODO currently we don't install googletest alongside abseil sources, so
-  # installed abseil can't be tested.
-  if(NOT ABSL_CC_LIB_TESTONLY AND ABSL_ENABLE_INSTALL)
-    install(TARGETS ${_NAME} EXPORT ${PROJECT_NAME}Targets
-          RUNTIME DESTINATION ${ABSL_INSTALL_BINDIR}
-          LIBRARY DESTINATION ${ABSL_INSTALL_LIBDIR}
-          ARCHIVE DESTINATION ${ABSL_INSTALL_LIBDIR}
-    )
-  endif()
-
-    add_library(absl::${ABSL_CC_LIB_NAME} ALIAS ${_NAME})
-endfunction()
-
-# absl_cc_test()
-#
-# CMake function to imitate Bazel's cc_test rule.
-#
-# Parameters:
-# NAME: name of target (see Usage below)
-# SRCS: List of source files for the binary
-# DEPS: List of other libraries to be linked in to the binary targets
-# COPTS: List of private compile options
-# DEFINES: List of public defines
-# LINKOPTS: List of link options
-#
-# Note:
-# By default, absl_cc_test will always create a binary named absl_${NAME}.
-# This will also add it to ctest list as absl_${NAME}.
-#
-# Usage:
-# absl_cc_library(
-#   NAME
-#     awesome
-#   HDRS
-#     "a.h"
-#   SRCS
-#     "a.cc"
-#   PUBLIC
-# )
-#
-# absl_cc_test(
-#   NAME
-#     awesome_test
-#   SRCS
-#     "awesome_test.cc"
-#   DEPS
-#     absl::awesome
-#     gmock
-#     gtest_main
-# )
-function(absl_cc_test)
-  if(NOT ABSL_RUN_TESTS)
-    return()
-  endif()
-
-  cmake_parse_arguments(ABSL_CC_TEST
-    ""
-    "NAME"
-    "SRCS;COPTS;DEFINES;LINKOPTS;DEPS"
-    ${ARGN}
-  )
-
-  set(_NAME "absl_${ABSL_CC_TEST_NAME}")
-
-  add_executable(${_NAME} "")
-  target_sources(${_NAME} PRIVATE ${ABSL_CC_TEST_SRCS})
-  target_include_directories(${_NAME}
-    PUBLIC ${ABSL_COMMON_INCLUDE_DIRS}
-    PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}
-  )
-
-  if (${ABSL_BUILD_DLL})
-    target_compile_definitions(${_NAME}
-      PUBLIC
-        ${ABSL_CC_TEST_DEFINES}
-        ABSL_CONSUME_DLL
-        GTEST_LINKED_AS_SHARED_LIBRARY=1
-    )
-
-    # Replace dependencies on targets inside the DLL with abseil_dll itself.
-    absl_internal_dll_targets(
-      DEPS ${ABSL_CC_TEST_DEPS}
-      OUTPUT ABSL_CC_TEST_DEPS
-    )
-  else()
-    target_compile_definitions(${_NAME}
-      PUBLIC
-        ${ABSL_CC_TEST_DEFINES}
-    )
-  endif()
-  target_compile_options(${_NAME}
-    PRIVATE ${ABSL_CC_TEST_COPTS}
-  )
-
-  target_link_libraries(${_NAME}
-    PUBLIC ${ABSL_CC_TEST_DEPS}
-    PRIVATE ${ABSL_CC_TEST_LINKOPTS}
-  )
-  # Add all Abseil targets to a folder in the IDE for organization.
-  set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test)
-
-  set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD})
-  set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
-
-  add_test(NAME ${_NAME} COMMAND ${_NAME})
-endfunction()
-
-
-function(check_target my_target)
-  if(NOT TARGET ${my_target})
-    message(FATAL_ERROR " ABSL: compiling absl requires a ${my_target} CMake target in your project,
-                   see CMake/README.md for more details")
-  endif(NOT TARGET ${my_target})
-endfunction()
diff --git a/third_party/abseil_cpp/CMake/AbseilInstallDirs.cmake b/third_party/abseil_cpp/CMake/AbseilInstallDirs.cmake
deleted file mode 100644
index 6fc914b60f..0000000000
--- a/third_party/abseil_cpp/CMake/AbseilInstallDirs.cmake
+++ /dev/null
@@ -1,20 +0,0 @@
-include(GNUInstallDirs)
-
-# absl_VERSION is only set if we are an LTS release being installed, in which
-# case it may be into a system directory and so we need to make subdirectories
-# for each installed version of Abseil.  This mechanism is implemented in
-# Abseil's internal Copybara (https://github.com/google/copybara) workflows and
-# isn't visible in the CMake buildsystem itself.
-
-if(absl_VERSION)
-  set(ABSL_SUBDIR "${PROJECT_NAME}_${PROJECT_VERSION}")
-  set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}/${ABSL_SUBDIR}")
-  set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${ABSL_SUBDIR}")
-  set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/${ABSL_SUBDIR}")
-  set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/${ABSL_SUBDIR}")
-else()
-  set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}")
-  set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
-  set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
-  set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
-endif()
diff --git a/third_party/abseil_cpp/CMake/Googletest/CMakeLists.txt.in b/third_party/abseil_cpp/CMake/Googletest/CMakeLists.txt.in
deleted file mode 100644
index 5769e3a97b..0000000000
--- a/third_party/abseil_cpp/CMake/Googletest/CMakeLists.txt.in
+++ /dev/null
@@ -1,14 +0,0 @@
-cmake_minimum_required(VERSION 2.8.2)
-
-project(googletest-external NONE)
-
-include(ExternalProject)
-ExternalProject_Add(googletest
-  URL               "${absl_gtest_download_url}"  # May be empty
-  SOURCE_DIR        "${absl_gtest_src_dir}"
-  BINARY_DIR        "${absl_gtest_build_dir}"
-  CONFIGURE_COMMAND ""
-  BUILD_COMMAND     ""
-  INSTALL_COMMAND   ""
-  TEST_COMMAND      ""
-)
diff --git a/third_party/abseil_cpp/CMake/Googletest/DownloadGTest.cmake b/third_party/abseil_cpp/CMake/Googletest/DownloadGTest.cmake
deleted file mode 100644
index 9d071c9170..0000000000
--- a/third_party/abseil_cpp/CMake/Googletest/DownloadGTest.cmake
+++ /dev/null
@@ -1,41 +0,0 @@
-# Integrates googletest at configure time.  Based on the instructions at
-# https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project
-
-# Set up the external googletest project, downloading the latest from Github
-# master if requested.
-configure_file(
-  ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in
-  ${CMAKE_BINARY_DIR}/googletest-external/CMakeLists.txt
-)
-
-set(ABSL_SAVE_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
-set(ABSL_SAVE_CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
-if (BUILD_SHARED_LIBS)
-  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_CREATE_SHARED_LIBRARY=1")
-endif()
-
-# Configure and build the googletest source.
-execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
-  RESULT_VARIABLE result
-  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-external )
-if(result)
-  message(FATAL_ERROR "CMake step for googletest failed: ${result}")
-endif()
-
-execute_process(COMMAND ${CMAKE_COMMAND} --build .
-  RESULT_VARIABLE result
-  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-external)
-if(result)
-  message(FATAL_ERROR "Build step for googletest failed: ${result}")
-endif()
-
-set(CMAKE_CXX_FLAGS ${ABSL_SAVE_CMAKE_CXX_FLAGS})
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ABSL_SAVE_CMAKE_RUNTIME_OUTPUT_DIRECTORY})
-
-# Prevent overriding the parent project's compiler/linker settings on Windows
-set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
-
-# Add googletest directly to our build. This defines the gtest and gtest_main
-# targets.
-add_subdirectory(${absl_gtest_src_dir} ${absl_gtest_build_dir} EXCLUDE_FROM_ALL)
diff --git a/third_party/abseil_cpp/CMake/README.md b/third_party/abseil_cpp/CMake/README.md
deleted file mode 100644
index 8f73475a16..0000000000
--- a/third_party/abseil_cpp/CMake/README.md
+++ /dev/null
@@ -1,101 +0,0 @@
-# Abseil CMake Build Instructions
-
-Abseil comes with a CMake build script ([CMakeLists.txt](../CMakeLists.txt))
-that can be used on a wide range of platforms ("C" stands for cross-platform.).
-If you don't have CMake installed already, you can download it for free from
-<https://www.cmake.org/>.
-
-CMake works by generating native makefiles or build projects that can
-be used in the compiler environment of your choice.
-
-For API/ABI compatibility reasons, we strongly recommend building Abseil in a
-subdirectory of your project or as an embedded dependency.
-
-## Incorporating Abseil Into a CMake Project
-
-The recommendations below are similar to those for using CMake within the
-googletest framework
-(<https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project>)
-
-### Step-by-Step Instructions
-
-1. If you want to build the Abseil tests, integrate the Abseil dependency
-[Google Test](https://github.com/google/googletest) into your CMake project. To disable Abseil tests, you have to pass
-`-DBUILD_TESTING=OFF` when configuring your project with CMake.
-
-2. Download Abseil and copy it into a subdirectory in your CMake project or add
-Abseil as a [git submodule](https://git-scm.com/docs/git-submodule) in your
-CMake project.
-
-3. You can then use the CMake command
-[`add_subdirectory()`](https://cmake.org/cmake/help/latest/command/add_subdirectory.html)
-to include Abseil directly in your CMake project.
-
-4. Add the **absl::** target you wish to use to the
-[`target_link_libraries()`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html)
-section of your executable or of your library.<br>
-Here is a short CMakeLists.txt example of a project file using Abseil.
-
-```cmake
-cmake_minimum_required(VERSION 3.5)
-project(my_project)
-
-# Pick the C++ standard to compile with.
-# Abseil currently supports C++11, C++14, and C++17.
-set(CMAKE_CXX_STANDARD 11)
-
-add_subdirectory(abseil-cpp)
-
-add_executable(my_exe source.cpp)
-target_link_libraries(my_exe absl::base absl::synchronization absl::strings)
-```
-
-### Running Abseil Tests with CMake
-
-Use the `-DABSL_RUN_TESTS=ON` flag to run Abseil tests.  Note that if the `-DBUILD_TESTING=OFF` flag is passed then Abseil tests will not be run.
-
-You will need to provide Abseil with a Googletest dependency.  There are two
-options for how to do this:
-
-* Use `-DABSL_USE_GOOGLETEST_HEAD`.  This will automatically download the latest
-Googletest source into the build directory at configure time.  Googletest will
-then be compiled directly alongside Abseil's tests.
-* Manually integrate Googletest with your build.  See
-https://github.com/google/googletest/blob/master/googletest/README.md#using-cmake
-for more information on using Googletest in a CMake project.
-
-For example, to run just the Abseil tests, you could use this script:
-
-```
-cd path/to/abseil-cpp
-mkdir build
-cd build
-cmake -DABSL_USE_GOOGLETEST_HEAD=ON -DABSL_RUN_TESTS=ON ..
-make -j
-ctest
-```
-
-Currently, we only run our tests with CMake in a Linux environment, but we are
-working on the rest of our supported platforms. See
-https://github.com/abseil/abseil-cpp/projects/1 and
-https://github.com/abseil/abseil-cpp/issues/109 for more information.
-
-### Available Abseil CMake Public Targets
-
-Here's a non-exhaustive list of Abseil CMake public targets:
-
-```cmake
-absl::algorithm
-absl::base
-absl::debugging
-absl::flat_hash_map
-absl::flags
-absl::memory
-absl::meta
-absl::numeric
-absl::random_random
-absl::strings
-absl::synchronization
-absl::time
-absl::utility
-```
diff --git a/third_party/abseil_cpp/CMake/abslConfig.cmake.in b/third_party/abseil_cpp/CMake/abslConfig.cmake.in
deleted file mode 100644
index 62d246d01e..0000000000
--- a/third_party/abseil_cpp/CMake/abslConfig.cmake.in
+++ /dev/null
@@ -1,8 +0,0 @@
-# absl CMake configuration file.
-
-include(CMakeFindDependencyMacro)
-find_dependency(Threads)
-
-@PACKAGE_INIT@
-
-include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
diff --git a/third_party/abseil_cpp/CMake/install_test_project/CMakeLists.txt b/third_party/abseil_cpp/CMake/install_test_project/CMakeLists.txt
deleted file mode 100644
index 06b797e9ed..0000000000
--- a/third_party/abseil_cpp/CMake/install_test_project/CMakeLists.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# A simple CMakeLists.txt for testing cmake installation
-
-cmake_minimum_required(VERSION 3.5)
-project(absl_cmake_testing CXX)
-
-set(CMAKE_CXX_STANDARD 11)
-
-add_executable(simple simple.cc)
-
-find_package(absl REQUIRED)
-
-target_link_libraries(simple absl::strings)
diff --git a/third_party/abseil_cpp/CMake/install_test_project/simple.cc b/third_party/abseil_cpp/CMake/install_test_project/simple.cc
deleted file mode 100644
index e9e352912b..0000000000
--- a/third_party/abseil_cpp/CMake/install_test_project/simple.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <iostream>
-#include "absl/strings/substitute.h"
-
-int main(int argc, char** argv) {
-  for (int i = 0; i < argc; ++i) {
-    std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]);
-  }
-}
diff --git a/third_party/abseil_cpp/CMake/install_test_project/test.sh b/third_party/abseil_cpp/CMake/install_test_project/test.sh
deleted file mode 100755
index ddc7726b6c..0000000000
--- a/third_party/abseil_cpp/CMake/install_test_project/test.sh
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# "Unit" and integration tests for Absl CMake installation
-
-# TODO(absl-team): This script isn't fully hermetic because
-# -DABSL_USE_GOOGLETEST_HEAD=ON means that this script isn't pinned to a fixed
-# version of GoogleTest. This means that an upstream change to GoogleTest could
-# break this test. Fix this by allowing this script to pin to a known-good
-# version of GoogleTest.
-
-# Fail on any error. Treat unset variables an error. Print commands as executed.
-set -euox pipefail
-
-install_absl() {
-  pushd "${absl_build_dir}"
-  if [[ "${#}" -eq 1 ]]; then
-    cmake -DCMAKE_INSTALL_PREFIX="${1}" "${absl_dir}"
-  else
-    cmake "${absl_dir}"
-  fi
-  cmake --build . --target install -- -j
-  popd
-}
-
-uninstall_absl() {
-  xargs rm < "${absl_build_dir}"/install_manifest.txt
-  rm -rf "${absl_build_dir}"
-  mkdir -p "${absl_build_dir}"
-}
-
-lts_install=""
-
-while getopts ":l" lts; do
-  case "${lts}" in
-    l )
-      lts_install="true"
-      ;;
-  esac
-done
-
-absl_dir=/abseil-cpp
-absl_build_dir=/buildfs/absl-build
-project_dir="${absl_dir}"/CMake/install_test_project
-project_build_dir=/buildfs/project-build
-
-mkdir -p "${absl_build_dir}"
-mkdir -p "${project_build_dir}"
-
-if [[ "${lts_install}" ]]; then
-  install_dir="/usr/local"
-else
-  install_dir="${project_build_dir}"/install
-fi
-mkdir -p "${install_dir}"
-
-# Test build, install, and link against installed abseil
-pushd "${project_build_dir}"
-if [[ "${lts_install}" ]]; then
-  install_absl
-  cmake "${project_dir}"
-else
-  install_absl "${install_dir}"
-  cmake "${project_dir}" -DCMAKE_PREFIX_PATH="${install_dir}"
-fi
-
-cmake --build . --target simple
-
-output="$(${project_build_dir}/simple "printme" 2>&1)"
-if [[ "${output}" != *"Arg 1: printme"* ]]; then
-  echo "Faulty output on simple project:"
-  echo "${output}"
-  exit 1
-fi
-
-popd
-
-# Test that we haven't accidentally made absl::abslblah
-pushd "${install_dir}"
-
-# Starting in CMake 3.12 the default install dir is lib$bit_width
-if [[ -d lib64 ]]; then
-  libdir="lib64"
-elif [[ -d lib ]]; then
-  libdir="lib"
-else
-  echo "ls *, */*, */*/*:"
-  ls *
-  ls */*
-  ls */*/*
-  echo "unknown lib dir"
-fi
-
-if [[ "${lts_install}" ]]; then
-  # LTS versions append the date of the release to the subdir.
-  # 9999/99/99 is the dummy date used in the local_lts workflow.
-  absl_subdir="absl_99999999"
-else
-  absl_subdir="absl"
-fi
-
-if ! grep absl::strings "${libdir}/cmake/${absl_subdir}/abslTargets.cmake"; then
-  cat "${libdir}"/cmake/absl/abslTargets.cmake
-  echo "CMake targets named incorrectly"
-  exit 1
-fi
-
-pushd "${HOME}"
-cat > hello-abseil.cc << EOF
-#include <cstdlib>
-
-#include "absl/strings/str_format.h"
-
-int main(int argc, char **argv) {
-  absl::PrintF("Hello Abseil!\n");
-  return EXIT_SUCCESS;
-}
-EOF
-export PKG_CONFIG_PATH="${install_dir}/${libdir}/pkgconfig"
-pc_args=($(pkg-config --cflags --libs --static absl_str_format))
-g++ -static -o hello-abseil hello-abseil.cc "${pc_args[@]}"
-hello="$(./hello-abseil)"
-[[ "${hello}" == "Hello Abseil!" ]]
-popd
-
-uninstall_absl
-popd
-
-if [[ ! "${lts_install}" ]]; then
-  # Test that we warn if installed without a prefix or a system prefix
-  output="$(install_absl 2>&1)"
-  if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then
-    echo "Install without prefix didn't warn as expected. Output:"
-    echo "${output}"
-    exit 1
-  fi
-  uninstall_absl
-
-  output="$(install_absl /usr 2>&1)"
-  if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then
-    echo "Install with /usr didn't warn as expected. Output:"
-    echo "${output}"
-    exit 1
-  fi
-  uninstall_absl
-fi
-
-echo "Install test complete!"
-exit 0
diff --git a/third_party/abseil_cpp/CMakeLists.txt b/third_party/abseil_cpp/CMakeLists.txt
deleted file mode 100644
index 2120cb0097..0000000000
--- a/third_party/abseil_cpp/CMakeLists.txt
+++ /dev/null
@@ -1,200 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Most widely used distributions have cmake 3.5 or greater available as of March
-# 2019.  A notable exception is RHEL-7 (CentOS7).  You can install a current
-# version of CMake by first installing Extra Packages for Enterprise Linux
-# (https://fedoraproject.org/wiki/EPEL#Extra_Packages_for_Enterprise_Linux_.28EPEL.29)
-# and then issuing `yum install cmake3` on the command line.
-cmake_minimum_required(VERSION 3.5)
-
-# Compiler id for Apple Clang is now AppleClang.
-if (POLICY CMP0025)
-  cmake_policy(SET CMP0025 NEW)
-endif (POLICY CMP0025)
-
-# if command can use IN_LIST
-if (POLICY CMP0057)
-  cmake_policy(SET CMP0057 NEW)
-endif (POLICY CMP0057)
-
-# Project version variables are the empty string if version is unspecified
-if (POLICY CMP0048)
-  cmake_policy(SET CMP0048 NEW)
-endif (POLICY CMP0048)
-
-# option() honor variables
-if (POLICY CMP0077)
-  cmake_policy(SET CMP0077 NEW)
-endif (POLICY CMP0077)
-
-project(absl CXX)
-
-# Output directory is correct by default for most build setups. However, when
-# building Abseil as a DLL, it is important to have the DLL in the same
-# directory as the executable using it. Thus, we put all executables in a single
-# /bin directory.
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
-
-# when absl is included as subproject (i.e. using add_subdirectory(abseil-cpp))
-# in the source tree of a project that uses it, install rules are disabled.
-if(NOT "^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$")
-  option(ABSL_ENABLE_INSTALL "Enable install rule" OFF)
-else()
-  option(ABSL_ENABLE_INSTALL "Enable install rule" ON)
-endif()
-
-list(APPEND CMAKE_MODULE_PATH
-  ${CMAKE_CURRENT_LIST_DIR}/CMake
-  ${CMAKE_CURRENT_LIST_DIR}/absl/copts
-)
-
-# Linking Abseil to shared libraries (such as Nix) requires PIC.
-set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
-
-include(AbseilInstallDirs)
-include(CMakePackageConfigHelpers)
-include(AbseilDll)
-include(AbseilHelpers)
-
-
-##
-## Using absl targets
-##
-## all public absl targets are
-## exported with the absl:: prefix
-##
-## e.g absl::base absl::synchronization absl::strings ....
-##
-## DO NOT rely on the internal targets outside of the prefix
-
-
-# include current path
-list(APPEND ABSL_COMMON_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
-
-if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
-  set(ABSL_USING_CLANG ON)
-else()
-  set(ABSL_USING_CLANG OFF)
-endif()
-
-# find dependencies
-## pthread
-find_package(Threads REQUIRED)
-
-option(ABSL_USE_EXTERNAL_GOOGLETEST
-  "If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subproject." OFF)
-
-option(ABSL_USE_GOOGLETEST_HEAD
-  "If ON, abseil will download HEAD from GoogleTest at config time." OFF)
-
-set(ABSL_GOOGLETEST_DOWNLOAD_URL "" CACHE STRING "If set, download GoogleTest from this URL")
-
-set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH
-  "If ABSL_USE_GOOGLETEST_HEAD is OFF and ABSL_GOOGLETEST_URL is not set, specifies the directory of a local GoogleTest checkout."
-  )
-
-option(ABSL_RUN_TESTS "If ON, Abseil tests will be run." OFF)
-
-if(${ABSL_RUN_TESTS})
-  # enable CTest.  This will set BUILD_TESTING to ON unless otherwise specified
-  # on the command line
-  include(CTest)
-
-  ## check targets
-  if (NOT ABSL_USE_EXTERNAL_GOOGLETEST)
-    set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build)
-    if(ABSL_USE_GOOGLETEST_HEAD AND ABSL_GOOGLETEST_DOWNLOAD_URL)
-      message(FATAL_ERROR "Do not set both ABSL_USE_GOOGLETEST_HEAD and ABSL_GOOGLETEST_DOWNLOAD_URL")
-    endif()
-    if(ABSL_USE_GOOGLETEST_HEAD)
-      set(absl_gtest_download_url "https://github.com/google/googletest/archive/master.zip")
-    elseif(ABSL_GOOGLETEST_DOWNLOAD_URL)
-      set(absl_gtest_download_url ${ABSL_GOOGLETEST_DOWNLOAD_URL})
-    endif()
-    if(absl_gtest_download_url)
-      set(absl_gtest_src_dir ${CMAKE_BINARY_DIR}/googletest-src)
-    else()
-      set(absl_gtest_src_dir ${ABSL_LOCAL_GOOGLETEST_DIR})
-    endif()
-    include(CMake/Googletest/DownloadGTest.cmake)
-  endif()
-
-  check_target(gtest)
-  check_target(gtest_main)
-  check_target(gmock)
-
-  list(APPEND ABSL_TEST_COMMON_LIBRARIES
-    gtest_main
-    gtest
-    gmock
-    ${CMAKE_THREAD_LIBS_INIT}
-  )
-endif()
-
-add_subdirectory(absl)
-
-if(ABSL_ENABLE_INSTALL)
-  # absl:lts-remove-begin(system installation is supported for LTS releases)
-  # We don't support system-wide installation
-  list(APPEND SYSTEM_INSTALL_DIRS "/usr/local" "/usr" "/opt/" "/opt/local" "c:/Program Files/${PROJECT_NAME}")
-  if(NOT DEFINED CMAKE_INSTALL_PREFIX OR CMAKE_INSTALL_PREFIX IN_LIST SYSTEM_INSTALL_DIRS)
-    message(WARNING "\
-  The default and system-level install directories are unsupported except in LTS \
-  releases of Abseil.  Please set CMAKE_INSTALL_PREFIX to install Abseil in your \
-  source or build tree directly.\
-    ")
-  endif()
-  # absl:lts-remove-end
-
-  # install as a subdirectory only
-  install(EXPORT ${PROJECT_NAME}Targets
-    NAMESPACE absl::
-    DESTINATION "${ABSL_INSTALL_CONFIGDIR}"
-  )
-
-  configure_package_config_file(
-    CMake/abslConfig.cmake.in
-    "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
-    INSTALL_DESTINATION "${ABSL_INSTALL_CONFIGDIR}"
-  )
-  install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
-    DESTINATION "${ABSL_INSTALL_CONFIGDIR}"
-  )
-
-  # Abseil only has a version in LTS releases.  This mechanism is accomplished
-  # Abseil's internal Copybara (https://github.com/google/copybara) workflows and
-  # isn't visible in the CMake buildsystem itself.
-  if(absl_VERSION)
-    write_basic_package_version_file(
-      "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
-      COMPATIBILITY ExactVersion
-    )
-
-    install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
-      DESTINATION ${ABSL_INSTALL_CONFIGDIR}
-    )
-  endif()  # absl_VERSION
-
-  install(DIRECTORY absl
-    DESTINATION ${ABSL_INSTALL_INCLUDEDIR}
-    FILES_MATCHING
-      PATTERN "*.inc"
-      PATTERN "*.h"
-      PATTERN "copts" EXCLUDE
-      PATTERN "testdata" EXCLUDE
-    )
-endif()  # ABSL_ENABLE_INSTALL
diff --git a/third_party/abseil_cpp/CONTRIBUTING.md b/third_party/abseil_cpp/CONTRIBUTING.md
deleted file mode 100644
index 9dadae9376..0000000000
--- a/third_party/abseil_cpp/CONTRIBUTING.md
+++ /dev/null
@@ -1,141 +0,0 @@
-# How to Contribute to Abseil
-
-We'd love to accept your patches and contributions to this project. There are
-just a few small guidelines you need to follow.
-
-NOTE: If you are new to GitHub, please start by reading [Pull Request
-howto](https://help.github.com/articles/about-pull-requests/)
-
-## Contributor License Agreement
-
-Contributions to this project must be accompanied by a Contributor License
-Agreement. You (or your employer) retain the copyright to your contribution,
-this simply gives us permission to use and redistribute your contributions as
-part of the project. Head over to <https://cla.developers.google.com/> to see
-your current agreements on file or to sign a new one.
-
-You generally only need to submit a CLA once, so if you've already submitted one
-(even if it was for a different project), you probably don't need to do it
-again.
-
-## Contribution Guidelines
-
-Potential contributors sometimes ask us if the Abseil project is the appropriate
-home for their utility library code or for specific functions implementing
-missing portions of the standard. Often, the answer to this question is "no".
-We’d like to articulate our thinking on this issue so that our choices can be
-understood by everyone and so that contributors can have a better intuition
-about whether Abseil might be interested in adopting a new library.
-
-### Priorities
-
-Although our mission is to augment the C++ standard library, our goal is not to
-provide a full forward-compatible implementation of the latest standard. For us
-to consider a library for inclusion in Abseil, it is not enough that a library
-is useful. We generally choose to release a library when it meets at least one
-of the following criteria:
-
-*   **Widespread usage** - Using our internal codebase to help gauge usage, most
-    of the libraries we've released have tens of thousands of users.
-*   **Anticipated widespread usage** - Pre-adoption of some standard-compliant
-    APIs may not have broad adoption initially but can be expected to pick up
-    usage when it replaces legacy APIs. `absl::from_chars`, for example,
-    replaces existing code that converts strings to numbers and will therefore
-    likely see usage growth.
-*   **High impact** - APIs that provide a key solution to a specific problem,
-    such as `absl::FixedArray`, have higher impact than usage numbers may signal
-    and are released because of their importance.
-*   **Direct support for a library that falls under one of the above** - When we
-    want access to a smaller library as an implementation detail for a
-    higher-priority library we plan to release, we may release it, as we did
-    with portions of `absl/meta/type_traits.h`. One consequence of this is that
-    the presence of a library in Abseil does not necessarily mean that other
-    similar libraries would be a high priority.
-
-### API Freeze Consequences
-
-Via the
-[Abseil Compatibility Guidelines](https://abseil.io/about/compatibility), we
-have promised a large degree of API stability. In particular, we will not make
-backward-incompatible changes to released APIs without also shipping a tool or
-process that can upgrade our users' code. We are not yet at the point of easily
-releasing such tools. Therefore, at this time, shipping a library establishes an
-API contract which is borderline unchangeable. (We can add new functionality,
-but we cannot easily change existing behavior.) This constraint forces us to
-very carefully review all APIs that we ship.
-
-
-## Coding Style
-
-To keep the source consistent, readable, diffable and easy to merge, we use a
-fairly rigid coding style, as defined by the
-[google-styleguide](https://github.com/google/styleguide) project. All patches
-will be expected to conform to the style outlined
-[here](https://google.github.io/styleguide/cppguide.html).
-
-## Guidelines for Pull Requests
-
-*   If you are a Googler, it is preferable to first create an internal CL and
-    have it reviewed and submitted. The code propagation process will deliver
-    the change to GitHub.
-
-*   Create **small PRs** that are narrowly focused on **addressing a single
-    concern**. We often receive PRs that are trying to fix several things at a
-    time, but if only one fix is considered acceptable, nothing gets merged and
-    both author's & review's time is wasted. Create more PRs to address
-    different concerns and everyone will be happy.
-
-*   For speculative changes, consider opening an [Abseil
-    issue](https://github.com/abseil/abseil-cpp/issues) and discussing it first.
-    If you are suggesting a behavioral or API change, consider starting with an
-    [Abseil proposal template](ABSEIL_ISSUE_TEMPLATE.md).
-
-*   Provide a good **PR description** as a record of **what** change is being
-    made and **why** it was made. Link to a GitHub issue if it exists.
-
-*   Don't fix code style and formatting unless you are already changing that
-    line to address an issue. Formatting of modified lines may be done using
-   `git clang-format`. PRs with irrelevant changes won't be merged. If
-    you do want to fix formatting or style, do that in a separate PR.
-
-*   Unless your PR is trivial, you should expect there will be reviewer comments
-    that you'll need to address before merging. We expect you to be reasonably
-    responsive to those comments, otherwise the PR will be closed after 2-3
-    weeks of inactivity.
-
-*   Maintain **clean commit history** and use **meaningful commit messages**.
-    PRs with messy commit history are difficult to review and won't be merged.
-    Use `rebase -i upstream/master` to curate your commit history and/or to
-    bring in latest changes from master (but avoid rebasing in the middle of a
-    code review).
-
-*   Keep your PR up to date with upstream/master (if there are merge conflicts,
-    we can't really merge your change).
-
-*   **All tests need to be passing** before your change can be merged. We
-    recommend you **run tests locally** (see below)
-
-*   Exceptions to the rules can be made if there's a compelling reason for doing
-    so. That is - the rules are here to serve us, not the other way around, and
-    the rules need to be serving their intended purpose to be valuable.
-
-*   All submissions, including submissions by project members, require review.
-
-## Running Tests
-
-If you have [Bazel](https://bazel.build/) installed, use `bazel test
---test_tag_filters="-benchmark" ...` to run the unit tests.
-
-If you are running the Linux operating system and have
-[Docker](https://www.docker.com/) installed, you can also run the `linux_*.sh`
-scripts under the `ci/`(https://github.com/abseil/abseil-cpp/tree/master/ci)
-directory to test Abseil under a variety of conditions.
-
-## Abseil Committers
-
-The current members of the Abseil engineering team are the only committers at
-present.
-
-## Release Process
-
-Abseil lives at head, where latest-and-greatest code can be found.
diff --git a/third_party/abseil_cpp/FAQ.md b/third_party/abseil_cpp/FAQ.md
deleted file mode 100644
index 78028fc09f..0000000000
--- a/third_party/abseil_cpp/FAQ.md
+++ /dev/null
@@ -1,164 +0,0 @@
-# Abseil FAQ
-
-## Is Abseil the right home for my utility library?
-
-Most often the answer to the question is "no." As both the [About
-Abseil](https://abseil.io/about/) page and our [contributing
-guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md#contribution-guidelines)
-explain, Abseil contains a variety of core C++ library code that is widely used
-at [Google](https://www.google.com/). As such, Abseil's primary purpose is to be
-used as a dependency by Google's open source C++ projects. While we do hope that
-Abseil is also useful to the C++ community at large, this added constraint also
-means that we are unlikely to accept a contribution of utility code that isn't
-already widely used by Google.
-
-## How to I set the C++ dialect used to build Abseil?
-
-The short answer is that whatever mechanism you choose, you need to make sure
-that you set this option consistently at the global level for your entire
-project. If, for example, you want to set the C++ dialect to C++17, with
-[Bazel](https://bazel/build/) as the build system and `gcc` or `clang` as the
-compiler, there several ways to do this:
-* Pass `--cxxopt=-std=c++17` on the command line (for example, `bazel build
-  --cxxopt=-std=c++17 ...`)
-* Set the environment variable `BAZEL_CXXOPTS` (for example,
-  `BAZEL_CXXOPTS=-std=c++17`)
-* Add `build --cxxopt=-std=c++17` to your [`.bazelrc`
-  file](https://docs.bazel.build/versions/master/guide.html#bazelrc)
-
-If you are using CMake as the build system, you'll need to add a line like
-`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. See the
-[CMake build
-instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md)
-for more information.
-
-For a longer answer to this question and to understand why some other approaches
-don't work, see the answer to ["What is ABI and why don't you recommend using a
-pre-compiled version of
-Abseil?"](#what-is-abi-and-why-dont-you-recommend-using-a-pre-compiled-version-of-abseil)
-
-## What is ABI and why don't you recommend using a pre-compiled version of Abseil?
-
-For the purposes of this discussion, you can think of
-[ABI](https://en.wikipedia.org/wiki/Application_binary_interface) as the
-compiled representation of the interfaces in code. This is in contrast to
-[API](https://en.wikipedia.org/wiki/Application_programming_interface), which
-you can think of as the interfaces as defined by the code itself. [Abseil has a
-strong promise of API compatibility, but does not make any promise of ABI
-compatibility](https://abseil.io/about/compatibility). Let's take a look at what
-this means in practice.
-
-You might be tempted to do something like this in a
-[Bazel](https://bazel.build/) `BUILD` file:
-
-```
-# DON'T DO THIS!!!
-cc_library(
-    name = "my_library",
-    srcs = ["my_library.cc"],
-    copts = ["-std=c++17"],  # May create a mixed-mode compile!
-    deps = ["@com_google_absl//absl/strings"],
-)
-```
-
-Applying `-std=c++17` to an individual target in your `BUILD` file is going to
-compile that specific target in C++17 mode, but it isn't going to ensure the
-Abseil library is built in C++17 mode, since the Abseil library itself is a
-different build target. If your code includes an Abseil header, then your
-program may contain conflicting definitions of the same
-class/function/variable/enum, etc. As a rule, all compile options that affect
-the ABI of a program need to be applied to the entire build on a global basis.
-
-C++ has something called the [One Definition
-Rule](https://en.wikipedia.org/wiki/One_Definition_Rule) (ODR). C++ doesn't
-allow multiple definitions of the same class/function/variable/enum, etc. ODR
-violations sometimes result in linker errors, but linkers do not always catch
-violations. Uncaught ODR violations can result in strange runtime behaviors or
-crashes that can be hard to debug.
-
-If you build the Abseil library and your code using different compile options
-that affect ABI, there is a good chance you will run afoul of the One Definition
-Rule. Examples of GCC compile options that affect ABI include (but aren't
-limited to) language dialect (e.g. `-std=`), optimization level (e.g. `-O2`),
-code generation flags (e.g. `-fexceptions`), and preprocessor defines
-(e.g. `-DNDEBUG`).
-
-If you use a pre-compiled version of Abseil, (for example, from your Linux
-distribution package manager or from something like
-[vcpkg](https://github.com/microsoft/vcpkg)) you have to be very careful to
-ensure ABI compatibility across the components of your program. The only way you
-can be sure your program is going to be correct regarding ABI is to ensure
-you've used the exact same compile options as were used to build the
-pre-compiled library. This does not mean that Abseil cannot work as part of a
-Linux distribution since a knowledgeable binary packager will have ensured that
-all packages have been built with consistent compile options. This is one of the
-reasons we warn against - though do not outright reject - using Abseil as a
-pre-compiled library.
-
-Another possible way that you might afoul of ABI issues is if you accidentally
-include two versions of Abseil in your program. Multiple versions of Abseil can
-end up within the same binary if your program uses the Abseil library and
-another library also transitively depends on Abseil (resulting in what is
-sometimes called the diamond dependency problem). In cases such as this you must
-structure your build so that all libraries use the same version of Abseil.
-[Abseil's strong promise of API compatibility between
-releases](https://abseil.io/about/compatibility) means the latest "HEAD" release
-of Abseil is almost certainly the right choice if you are doing as we recommend
-and building all of your code from source.
-
-For these reasons we recommend you avoid pre-compiled code and build the Abseil
-library yourself in a consistent manner with the rest of your code.
-
-## What is "live at head" and how do I do it?
-
-From Abseil's point-of-view, "live at head" means that every Abseil source
-release (which happens on an almost daily basis) is either API compatible with
-the previous release, or comes with an automated tool that you can run over code
-to make it compatible. In practice, the need to use an automated tool is
-extremely rare. This means that upgrading from one source release to another
-should be a routine practice that can and should be performed often.
-
-We recommend you update to the [latest commit in the `master` branch of
-Abseil](https://github.com/abseil/abseil-cpp/commits/master) as often as
-possible. Not only will you pick up bug fixes more quickly, but if you have good
-automated testing, you will catch and be able to fix any [Hyrum's
-Law](https://www.hyrumslaw.com/) dependency problems on an incremental basis
-instead of being overwhelmed by them and having difficulty isolating them if you
-wait longer between updates.
-
-If you are using the [Bazel](https://bazel.build/) build system and its
-[external dependencies](https://docs.bazel.build/versions/master/external.html)
-feature, updating the
-[`http_archive`](https://docs.bazel.build/versions/master/repo/http.html#http_archive)
-rule in your
-[`WORKSPACE`](https://docs.bazel.build/versions/master/be/workspace.html) for
-`com_google_abseil` to point to the [latest commit in the `master` branch of
-Abseil](https://github.com/abseil/abseil-cpp/commits/master) is all you need to
-do. For example, on February 11, 2020, the latest commit to the master branch
-was `98eb410c93ad059f9bba1bf43f5bb916fc92a5ea`. To update to this commit, you
-would add the following snippet to your `WORKSPACE` file:
-
-```
-http_archive(
-  name = "com_google_absl",
-  urls = ["https://github.com/abseil/abseil-cpp/archive/98eb410c93ad059f9bba1bf43f5bb916fc92a5ea.zip"],  # 2020-02-11T18:50:53Z
-  strip_prefix = "abseil-cpp-98eb410c93ad059f9bba1bf43f5bb916fc92a5ea",
-  sha256 = "aabf6c57e3834f8dc3873a927f37eaf69975d4b28117fc7427dfb1c661542a87",
-)
-```
-
-To get the `sha256` of this URL, run `curl -sL --output -
-https://github.com/abseil/abseil-cpp/archive/98eb410c93ad059f9bba1bf43f5bb916fc92a5ea.zip
-| sha256sum -`.
-
-You can commit the updated `WORKSPACE` file to your source control every time
-you update, and if you have good automated testing, you might even consider
-automating this.
-
-One thing we don't recommend is using GitHub's `master.zip` files (for example
-[https://github.com/abseil/abseil-cpp/archive/master.zip](https://github.com/abseil/abseil-cpp/archive/master.zip)),
-which are always the latest commit in the `master` branch, to implement live at
-head. Since these `master.zip` URLs are not versioned, you will lose build
-reproducibility. In addition, some build systems, including Bazel, will simply
-cache this file, which means you won't actually be updating to the latest
-release until your cache is cleared or invalidated.
diff --git a/third_party/abseil_cpp/LICENSE b/third_party/abseil_cpp/LICENSE
deleted file mode 100644
index ccd61dcfe3..0000000000
--- a/third_party/abseil_cpp/LICENSE
+++ /dev/null
@@ -1,203 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        https://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       https://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
diff --git a/third_party/abseil_cpp/LTS.md b/third_party/abseil_cpp/LTS.md
deleted file mode 100644
index ade8b17c73..0000000000
--- a/third_party/abseil_cpp/LTS.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Long Term Support (LTS) Branches
-
-This repository contains periodic snapshots of the Abseil codebase that are
-Long Term Support (LTS) branches. An LTS branch allows you to use a known
-version of Abseil without interfering with other projects which may also, in
-turn, use Abseil. (For more information about our releases, see the
-[Abseil Release Management](https://abseil.io/about/releases) guide.)
-
-## LTS Branches
-
-The following lists LTS branches and the dates on which they have been released:
-
-* [LTS Branch December 18, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_12_18/)
-* [LTS Branch June 20, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_06_20/)
-* [LTS Branch August 8, 2019](https://github.com/abseil/abseil-cpp/tree/lts_2019_08_08/)
-* [LTS Branch February 25, 2020](https://github.com/abseil/abseil-cpp/tree/lts_2020_02_25/)
diff --git a/third_party/abseil_cpp/README.md b/third_party/abseil_cpp/README.md
deleted file mode 100644
index 85de569658..0000000000
--- a/third_party/abseil_cpp/README.md
+++ /dev/null
@@ -1,114 +0,0 @@
-# Abseil - C++ Common Libraries
-
-The repository contains the Abseil C++ library code. Abseil is an open-source
-collection of C++ code (compliant to C++11) designed to augment the C++
-standard library.
-
-## Table of Contents
-
-- [About Abseil](#about)
-- [Quickstart](#quickstart)
-- [Building Abseil](#build)
-- [Codemap](#codemap)
-- [License](#license)
-- [Links](#links)
-
-<a name="about"></a>
-## About Abseil
-
-Abseil is an open-source collection of C++ library code designed to augment
-the C++ standard library. The Abseil library code is collected from Google's
-own C++ code base, has been extensively tested and used in production, and
-is the same code we depend on in our daily coding lives.
-
-In some cases, Abseil provides pieces missing from the C++ standard; in
-others, Abseil provides alternatives to the standard for special needs
-we've found through usage in the Google code base. We denote those cases
-clearly within the library code we provide you.
-
-Abseil is not meant to be a competitor to the standard library; we've
-just found that many of these utilities serve a purpose within our code
-base, and we now want to provide those resources to the C++ community as
-a whole.
-
-<a name="quickstart"></a>
-## Quickstart
-
-If you want to just get started, make sure you at least run through the
-[Abseil Quickstart](https://abseil.io/docs/cpp/quickstart). The Quickstart
-contains information about setting up your development environment, downloading
-the Abseil code, running tests, and getting a simple binary working.
-
-<a name="build"></a>
-## Building Abseil
-
-[Bazel](https://bazel.build) is the official build system for Abseil,
-which is supported on most major platforms (Linux, Windows, macOS, for example)
-and compilers. See the [quickstart](https://abseil.io/docs/cpp/quickstart) for
-more information on building Abseil using the Bazel build system.
-
-<a name="cmake"></a>
-If you require CMake support, please check the
-[CMake build instructions](CMake/README.md).
-
-## Codemap
-
-Abseil contains the following C++ library components:
-
-* [`base`](absl/base/) Abseil Fundamentals
-  <br /> The `base` library contains initialization code and other code which
-  all other Abseil code depends on. Code within `base` may not depend on any
-  other code (other than the C++ standard library).
-* [`algorithm`](absl/algorithm/)
-  <br /> The `algorithm` library contains additions to the C++ `<algorithm>`
-  library and container-based versions of such algorithms.
-* [`container`](absl/container/)
-  <br /> The `container` library contains additional STL-style containers,
-  including Abseil's unordered "Swiss table" containers.
-* [`debugging`](absl/debugging/)
-  <br /> The `debugging` library contains code useful for enabling leak
-  checks, and stacktrace and symbolization utilities.
-* [`hash`](absl/hash/)
-  <br /> The `hash` library contains the hashing framework and default hash
-  functor implementations for hashable types in Abseil.
-* [`memory`](absl/memory/)
-  <br /> The `memory` library contains C++11-compatible versions of
-  `std::make_unique()` and related memory management facilities.
-* [`meta`](absl/meta/)
-  <br /> The `meta` library contains C++11-compatible versions of type checks
-  available within C++14 and C++17 versions of the C++ `<type_traits>` library.
-* [`numeric`](absl/numeric/)
-  <br /> The `numeric` library contains C++11-compatible 128-bit integers.
-* [`strings`](absl/strings/)
-  <br /> The `strings` library contains a variety of strings routines and
-  utilities, including a C++11-compatible version of the C++17
-  `std::string_view` type.
-* [`synchronization`](absl/synchronization/)
-  <br /> The `synchronization` library contains concurrency primitives (Abseil's
-  `absl::Mutex` class, an alternative to `std::mutex`) and a variety of
-  synchronization abstractions.
-* [`time`](absl/time/)
-  <br /> The `time` library contains abstractions for computing with absolute
-  points in time, durations of time, and formatting and parsing time within
-  time zones.
-* [`types`](absl/types/)
-  <br /> The `types` library contains non-container utility types, like a
-  C++11-compatible version of the C++17 `std::optional` type.
-* [`utility`](absl/utility/)
-  <br /> The `utility` library contains utility and helper code.
-
-## License
-
-The Abseil C++ library is licensed under the terms of the Apache
-license. See [LICENSE](LICENSE) for more information.
-
-## Links
-
-For more information about Abseil:
-
-* Consult our [Abseil Introduction](https://abseil.io/about/intro)
-* Read [Why Adopt Abseil](https://abseil.io/about/philosophy) to understand our
-  design philosophy.
-* Peruse our
-  [Abseil Compatibility Guarantees](https://abseil.io/about/compatibility) to
-  understand both what we promise to you, and what we expect of you in return.
diff --git a/third_party/abseil_cpp/UPGRADES.md b/third_party/abseil_cpp/UPGRADES.md
deleted file mode 100644
index 35599d0878..0000000000
--- a/third_party/abseil_cpp/UPGRADES.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# C++ Upgrade Tools
-
-Abseil may occassionally release API-breaking changes. As noted in our
-[Compatibility Guidelines][compatibility-guide], we will aim to provide a tool
-to do the work of effecting such API-breaking changes, when absolutely
-necessary.
-
-These tools will be listed on the [C++ Upgrade Tools][upgrade-tools] guide on
-https://abseil.io.
-
-For more information, the [C++ Automated Upgrade Guide][api-upgrades-guide]
-outlines this process.
-
-[compatibility-guide]: https://abseil.io/about/compatibility
-[api-upgrades-guide]: https://abseil.io/docs/cpp/tools/api-upgrades
-[upgrade-tools]: https://abseil.io/docs/cpp/tools/upgrades/
-
diff --git a/third_party/abseil_cpp/WORKSPACE b/third_party/abseil_cpp/WORKSPACE
deleted file mode 100644
index ed90d2ba95..0000000000
--- a/third_party/abseil_cpp/WORKSPACE
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-workspace(name = "com_google_absl")
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-
-# GoogleTest/GoogleMock framework. Used by most unit-tests.
-http_archive(
-    name = "com_google_googletest",
-    # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh.
-    urls = ["https://github.com/google/googletest/archive/8567b09290fe402cf01923e2131c5635b8ed851b.zip"],  # 2020-06-12T22:24:28Z
-    strip_prefix = "googletest-8567b09290fe402cf01923e2131c5635b8ed851b",
-    sha256 = "9a8a166eb6a56c7b3d7b19dc2c946fe4778fd6f21c7a12368ad3b836d8f1be48",
-)
-
-# Google benchmark.
-http_archive(
-    name = "com_github_google_benchmark",
-    urls = ["https://github.com/google/benchmark/archive/16703ff83c1ae6d53e5155df3bb3ab0bc96083be.zip"],
-    strip_prefix = "benchmark-16703ff83c1ae6d53e5155df3bb3ab0bc96083be",
-    sha256 = "59f918c8ccd4d74b6ac43484467b500f1d64b40cc1010daa055375b322a43ba3",
-)
-
-# C++ rules for Bazel.
-http_archive(
-    name = "rules_cc",
-    sha256 = "9a446e9dd9c1bb180c86977a8dc1e9e659550ae732ae58bd2e8fd51e15b2c91d",
-    strip_prefix = "rules_cc-262ebec3c2296296526740db4aefce68c80de7fa",
-    urls = [
-        "https://github.com/bazelbuild/rules_cc/archive/262ebec3c2296296526740db4aefce68c80de7fa.zip",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/BUILD.bazel b/third_party/abseil_cpp/absl/BUILD.bazel
deleted file mode 100644
index 6da20c49d2..0000000000
--- a/third_party/abseil_cpp/absl/BUILD.bazel
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-config_setting(
-    name = "clang_compiler",
-    flag_values = {
-        "@bazel_tools//tools/cpp:compiler": "clang",
-    },
-    visibility = [":__subpackages__"],
-)
-
-config_setting(
-    name = "osx",
-    constraint_values = [
-        "@bazel_tools//platforms:osx",
-    ],
-)
-
-config_setting(
-    name = "ios",
-    constraint_values = [
-        "@bazel_tools//platforms:ios",
-    ],
-)
-
-config_setting(
-    name = "windows",
-    constraint_values = [
-        "@bazel_tools//platforms:x86_64",
-        "@bazel_tools//platforms:windows",
-    ],
-    visibility = [":__subpackages__"],
-)
-
-config_setting(
-    name = "ppc",
-    values = {
-        "cpu": "ppc",
-    },
-    visibility = [":__subpackages__"],
-)
-
-config_setting(
-    name = "wasm",
-    values = {
-        "cpu": "wasm32",
-    },
-    visibility = [":__subpackages__"],
-)
diff --git a/third_party/abseil_cpp/absl/CMakeLists.txt b/third_party/abseil_cpp/absl/CMakeLists.txt
deleted file mode 100644
index fbfa7822b5..0000000000
--- a/third_party/abseil_cpp/absl/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-add_subdirectory(base)
-add_subdirectory(algorithm)
-add_subdirectory(container)
-add_subdirectory(debugging)
-add_subdirectory(flags)
-add_subdirectory(functional)
-add_subdirectory(hash)
-add_subdirectory(memory)
-add_subdirectory(meta)
-add_subdirectory(numeric)
-add_subdirectory(random)
-add_subdirectory(status)
-add_subdirectory(strings)
-add_subdirectory(synchronization)
-add_subdirectory(time)
-add_subdirectory(types)
-add_subdirectory(utility)
-
-if (${ABSL_BUILD_DLL})
-  absl_make_dll()
-endif()
diff --git a/third_party/abseil_cpp/absl/abseil.podspec.gen.py b/third_party/abseil_cpp/absl/abseil.podspec.gen.py
deleted file mode 100755
index 63752980d0..0000000000
--- a/third_party/abseil_cpp/absl/abseil.podspec.gen.py
+++ /dev/null
@@ -1,229 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""This script generates abseil.podspec from all BUILD.bazel files.
-
-This is expected to run on abseil git repository with Bazel 1.0 on Linux.
-It recursively analyzes BUILD.bazel files using query command of Bazel to
-dump its build rules in XML format. From these rules, it constructs podspec
-structure.
-"""
-
-import argparse
-import collections
-import os
-import re
-import subprocess
-import xml.etree.ElementTree
-
-# Template of root podspec.
-SPEC_TEMPLATE = """
-# This file has been automatically generated from a script.
-# Please make modifications to `abseil.podspec.gen.py` instead.
-Pod::Spec.new do |s|
-  s.name     = 'abseil'
-  s.version  = '${version}'
-  s.summary  = 'Abseil Common Libraries (C++) from Google'
-  s.homepage = 'https://abseil.io'
-  s.license  = 'Apache License, Version 2.0'
-  s.authors  = { 'Abseil Team' => 'abseil-io@googlegroups.com' }
-  s.source = {
-    :git => 'https://github.com/abseil/abseil-cpp.git',
-    :tag => '${tag}',
-  }
-  s.module_name = 'absl'
-  s.header_mappings_dir = 'absl'
-  s.header_dir = 'absl'
-  s.libraries = 'c++'
-  s.compiler_flags = '-Wno-everything'
-  s.pod_target_xcconfig = {
-    'USER_HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_TARGET_SRCROOT)"',
-    'USE_HEADERMAP' => 'NO',
-    'ALWAYS_SEARCH_USER_PATHS' => 'NO',
-  }
-  s.ios.deployment_target = '9.0'
-  s.osx.deployment_target = '10.10'
-  s.tvos.deployment_target = '9.0'
-  s.watchos.deployment_target = '2.0'
-"""
-
-# Rule object representing the rule of Bazel BUILD.
-Rule = collections.namedtuple(
-    "Rule", "type name package srcs hdrs textual_hdrs deps visibility testonly")
-
-
-def get_elem_value(elem, name):
-  """Returns the value of XML element with the given name."""
-  for child in elem:
-    if child.attrib.get("name") != name:
-      continue
-    if child.tag == "string":
-      return child.attrib.get("value")
-    if child.tag == "boolean":
-      return child.attrib.get("value") == "true"
-    if child.tag == "list":
-      return [nested_child.attrib.get("value") for nested_child in child]
-    raise "Cannot recognize tag: " + child.tag
-  return None
-
-
-def normalize_paths(paths):
-  """Returns the list of normalized path."""
-  # e.g. ["//absl/strings:dir/header.h"] -> ["absl/strings/dir/header.h"]
-  return [path.lstrip("/").replace(":", "/") for path in paths]
-
-
-def parse_rule(elem, package):
-  """Returns a rule from bazel XML rule."""
-  return Rule(
-      type=elem.attrib["class"],
-      name=get_elem_value(elem, "name"),
-      package=package,
-      srcs=normalize_paths(get_elem_value(elem, "srcs") or []),
-      hdrs=normalize_paths(get_elem_value(elem, "hdrs") or []),
-      textual_hdrs=normalize_paths(get_elem_value(elem, "textual_hdrs") or []),
-      deps=get_elem_value(elem, "deps") or [],
-      visibility=get_elem_value(elem, "visibility") or [],
-      testonly=get_elem_value(elem, "testonly") or False)
-
-
-def read_build(package):
-  """Runs bazel query on given package file and returns all cc rules."""
-  result = subprocess.check_output(
-      ["bazel", "query", package + ":all", "--output", "xml"])
-  root = xml.etree.ElementTree.fromstring(result)
-  return [
-      parse_rule(elem, package)
-      for elem in root
-      if elem.tag == "rule" and elem.attrib["class"].startswith("cc_")
-  ]
-
-
-def collect_rules(root_path):
-  """Collects and returns all rules from root path recursively."""
-  rules = []
-  for cur, _, _ in os.walk(root_path):
-    build_path = os.path.join(cur, "BUILD.bazel")
-    if os.path.exists(build_path):
-      rules.extend(read_build("//" + cur))
-  return rules
-
-
-def relevant_rule(rule):
-  """Returns true if a given rule is relevant when generating a podspec."""
-  return (
-      # cc_library only (ignore cc_test, cc_binary)
-      rule.type == "cc_library" and
-      # ignore empty rule
-      (rule.hdrs + rule.textual_hdrs + rule.srcs) and
-      # ignore test-only rule
-      not rule.testonly)
-
-
-def get_spec_var(depth):
-  """Returns the name of variable for spec with given depth."""
-  return "s" if depth == 0 else "s{}".format(depth)
-
-
-def get_spec_name(label):
-  """Converts the label of bazel rule to the name of podspec."""
-  assert label.startswith("//absl/"), "{} doesn't start with //absl/".format(
-      label)
-  # e.g. //absl/apple/banana -> abseil/apple/banana
-  return "abseil/" + label[7:]
-
-
-def write_podspec(f, rules, args):
-  """Writes a podspec from given rules and args."""
-  rule_dir = build_rule_directory(rules)["abseil"]
-  # Write root part with given arguments
-  spec = re.sub(r"\$\{(\w+)\}", lambda x: args[x.group(1)],
-                SPEC_TEMPLATE).lstrip()
-  f.write(spec)
-  # Write all target rules
-  write_podspec_map(f, rule_dir, 0)
-  f.write("end\n")
-
-
-def build_rule_directory(rules):
-  """Builds a tree-style rule directory from given rules."""
-  rule_dir = {}
-  for rule in rules:
-    cur = rule_dir
-    for frag in get_spec_name(rule.package).split("/"):
-      cur = cur.setdefault(frag, {})
-    cur[rule.name] = rule
-  return rule_dir
-
-
-def write_podspec_map(f, cur_map, depth):
-  """Writes podspec from rule map recursively."""
-  for key, value in sorted(cur_map.items()):
-    indent = "  " * (depth + 1)
-    f.write("{indent}{var0}.subspec '{key}' do |{var1}|\n".format(
-        indent=indent,
-        key=key,
-        var0=get_spec_var(depth),
-        var1=get_spec_var(depth + 1)))
-    if isinstance(value, dict):
-      write_podspec_map(f, value, depth + 1)
-    else:
-      write_podspec_rule(f, value, depth + 1)
-    f.write("{indent}end\n".format(indent=indent))
-
-
-def write_podspec_rule(f, rule, depth):
-  """Writes podspec from given rule."""
-  indent = "  " * (depth + 1)
-  spec_var = get_spec_var(depth)
-  # Puts all files in hdrs, textual_hdrs, and srcs into source_files.
-  # Since CocoaPods treats header_files a bit differently from bazel,
-  # this won't generate a header_files field so that all source_files
-  # are considered as header files.
-  srcs = sorted(set(rule.hdrs + rule.textual_hdrs + rule.srcs))
-  write_indented_list(
-      f, "{indent}{var}.source_files = ".format(indent=indent, var=spec_var),
-      srcs)
-  # Writes dependencies of this rule.
-  for dep in sorted(rule.deps):
-    name = get_spec_name(dep.replace(":", "/"))
-    f.write("{indent}{var}.dependency '{dep}'\n".format(
-        indent=indent, var=spec_var, dep=name))
-
-
-def write_indented_list(f, leading, values):
-  """Writes leading values in an indented style."""
-  f.write(leading)
-  f.write((",\n" + " " * len(leading)).join("'{}'".format(v) for v in values))
-  f.write("\n")
-
-
-def generate(args):
-  """Generates a podspec file from all BUILD files under absl directory."""
-  rules = filter(relevant_rule, collect_rules("absl"))
-  with open(args.output, "wt") as f:
-    write_podspec(f, rules, vars(args))
-
-
-def main():
-  parser = argparse.ArgumentParser(
-      description="Generates abseil.podspec from BUILD.bazel")
-  parser.add_argument(
-      "-v", "--version", help="The version of podspec", required=True)
-  parser.add_argument(
-      "-t",
-      "--tag",
-      default=None,
-      help="The name of git tag (default: version)")
-  parser.add_argument(
-      "-o",
-      "--output",
-      default="abseil.podspec",
-      help="The name of output file (default: abseil.podspec)")
-  args = parser.parse_args()
-  if args.tag is None:
-    args.tag = args.version
-  generate(args)
-
-
-if __name__ == "__main__":
-  main()
diff --git a/third_party/abseil_cpp/absl/algorithm/BUILD.bazel b/third_party/abseil_cpp/absl/algorithm/BUILD.bazel
deleted file mode 100644
index a3002b7dcd..0000000000
--- a/third_party/abseil_cpp/absl/algorithm/BUILD.bazel
+++ /dev/null
@@ -1,91 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "algorithm",
-    hdrs = ["algorithm.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-    ],
-)
-
-cc_test(
-    name = "algorithm_test",
-    size = "small",
-    srcs = ["algorithm_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":algorithm",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "algorithm_benchmark",
-    srcs = ["equal_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    deps = [
-        ":algorithm",
-        "//absl/base:core_headers",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_library(
-    name = "container",
-    hdrs = [
-        "container.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":algorithm",
-        "//absl/base:core_headers",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_test(
-    name = "container_test",
-    srcs = ["container_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":container",
-        "//absl/base",
-        "//absl/base:core_headers",
-        "//absl/memory",
-        "//absl/types:span",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/algorithm/CMakeLists.txt b/third_party/abseil_cpp/absl/algorithm/CMakeLists.txt
deleted file mode 100644
index 56cd0fb85b..0000000000
--- a/third_party/abseil_cpp/absl/algorithm/CMakeLists.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    algorithm
-  HDRS
-    "algorithm.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    algorithm_test
-  SRCS
-    "algorithm_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::algorithm
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    algorithm_container
-  HDRS
-    "container.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::algorithm
-    absl::core_headers
-    absl::meta
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    container_test
-  SRCS
-    "container_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::algorithm_container
-    absl::base
-    absl::core_headers
-    absl::memory
-    absl::span
-    gmock_main
-)
diff --git a/third_party/abseil_cpp/absl/algorithm/algorithm.h b/third_party/abseil_cpp/absl/algorithm/algorithm.h
deleted file mode 100644
index e9b4733872..0000000000
--- a/third_party/abseil_cpp/absl/algorithm/algorithm.h
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: algorithm.h
-// -----------------------------------------------------------------------------
-//
-// This header file contains Google extensions to the standard <algorithm> C++
-// header.
-
-#ifndef ABSL_ALGORITHM_ALGORITHM_H_
-#define ABSL_ALGORITHM_ALGORITHM_H_
-
-#include <algorithm>
-#include <iterator>
-#include <type_traits>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace algorithm_internal {
-
-// Performs comparisons with operator==, similar to C++14's `std::equal_to<>`.
-struct EqualTo {
-  template <typename T, typename U>
-  bool operator()(const T& a, const U& b) const {
-    return a == b;
-  }
-};
-
-template <typename InputIter1, typename InputIter2, typename Pred>
-bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-               InputIter2 last2, Pred pred, std::input_iterator_tag,
-               std::input_iterator_tag) {
-  while (true) {
-    if (first1 == last1) return first2 == last2;
-    if (first2 == last2) return false;
-    if (!pred(*first1, *first2)) return false;
-    ++first1;
-    ++first2;
-  }
-}
-
-template <typename InputIter1, typename InputIter2, typename Pred>
-bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-               InputIter2 last2, Pred&& pred, std::random_access_iterator_tag,
-               std::random_access_iterator_tag) {
-  return (last1 - first1 == last2 - first2) &&
-         std::equal(first1, last1, first2, std::forward<Pred>(pred));
-}
-
-// When we are using our own internal predicate that just applies operator==, we
-// forward to the non-predicate form of std::equal. This enables an optimization
-// in libstdc++ that can result in std::memcmp being used for integer types.
-template <typename InputIter1, typename InputIter2>
-bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-               InputIter2 last2, algorithm_internal::EqualTo /* unused */,
-               std::random_access_iterator_tag,
-               std::random_access_iterator_tag) {
-  return (last1 - first1 == last2 - first2) &&
-         std::equal(first1, last1, first2);
-}
-
-template <typename It>
-It RotateImpl(It first, It middle, It last, std::true_type) {
-  return std::rotate(first, middle, last);
-}
-
-template <typename It>
-It RotateImpl(It first, It middle, It last, std::false_type) {
-  std::rotate(first, middle, last);
-  return std::next(first, std::distance(middle, last));
-}
-
-}  // namespace algorithm_internal
-
-// equal()
-//
-// Compares the equality of two ranges specified by pairs of iterators, using
-// the given predicate, returning true iff for each corresponding iterator i1
-// and i2 in the first and second range respectively, pred(*i1, *i2) == true
-//
-// This comparison takes at most min(`last1` - `first1`, `last2` - `first2`)
-// invocations of the predicate. Additionally, if InputIter1 and InputIter2 are
-// both random-access iterators, and `last1` - `first1` != `last2` - `first2`,
-// then the predicate is never invoked and the function returns false.
-//
-// This is a C++11-compatible implementation of C++14 `std::equal`.  See
-// https://en.cppreference.com/w/cpp/algorithm/equal for more information.
-template <typename InputIter1, typename InputIter2, typename Pred>
-bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-           InputIter2 last2, Pred&& pred) {
-  return algorithm_internal::EqualImpl(
-      first1, last1, first2, last2, std::forward<Pred>(pred),
-      typename std::iterator_traits<InputIter1>::iterator_category{},
-      typename std::iterator_traits<InputIter2>::iterator_category{});
-}
-
-// Overload of equal() that performs comparison of two ranges specified by pairs
-// of iterators using operator==.
-template <typename InputIter1, typename InputIter2>
-bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
-           InputIter2 last2) {
-  return absl::equal(first1, last1, first2, last2,
-                     algorithm_internal::EqualTo{});
-}
-
-// linear_search()
-//
-// Performs a linear search for `value` using the iterator `first` up to
-// but not including `last`, returning true if [`first`, `last`) contains an
-// element equal to `value`.
-//
-// A linear search is of O(n) complexity which is guaranteed to make at most
-// n = (`last` - `first`) comparisons. A linear search over short containers
-// may be faster than a binary search, even when the container is sorted.
-template <typename InputIterator, typename EqualityComparable>
-bool linear_search(InputIterator first, InputIterator last,
-                   const EqualityComparable& value) {
-  return std::find(first, last, value) != last;
-}
-
-// rotate()
-//
-// Performs a left rotation on a range of elements (`first`, `last`) such that
-// `middle` is now the first element. `rotate()` returns an iterator pointing to
-// the first element before rotation. This function is exactly the same as
-// `std::rotate`, but fixes a bug in gcc
-// <= 4.9 where `std::rotate` returns `void` instead of an iterator.
-//
-// The complexity of this algorithm is the same as that of `std::rotate`, but if
-// `ForwardIterator` is not a random-access iterator, then `absl::rotate`
-// performs an additional pass over the range to construct the return value.
-template <typename ForwardIterator>
-ForwardIterator rotate(ForwardIterator first, ForwardIterator middle,
-                       ForwardIterator last) {
-  return algorithm_internal::RotateImpl(
-      first, middle, last,
-      std::is_same<decltype(std::rotate(first, middle, last)),
-                   ForwardIterator>());
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_ALGORITHM_ALGORITHM_H_
diff --git a/third_party/abseil_cpp/absl/algorithm/algorithm_test.cc b/third_party/abseil_cpp/absl/algorithm/algorithm_test.cc
deleted file mode 100644
index 81fccb6135..0000000000
--- a/third_party/abseil_cpp/absl/algorithm/algorithm_test.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/algorithm/algorithm.h"
-
-#include <algorithm>
-#include <list>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-TEST(EqualTest, DefaultComparisonRandomAccess) {
-  std::vector<int> v1{1, 2, 3};
-  std::vector<int> v2 = v1;
-  std::vector<int> v3 = {1, 2};
-  std::vector<int> v4 = {1, 2, 4};
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end()));
-}
-
-TEST(EqualTest, DefaultComparison) {
-  std::list<int> lst1{1, 2, 3};
-  std::list<int> lst2 = lst1;
-  std::list<int> lst3{1, 2};
-  std::list<int> lst4{1, 2, 4};
-
-  EXPECT_TRUE(absl::equal(lst1.begin(), lst1.end(), lst2.begin(), lst2.end()));
-  EXPECT_FALSE(absl::equal(lst1.begin(), lst1.end(), lst3.begin(), lst3.end()));
-  EXPECT_FALSE(absl::equal(lst1.begin(), lst1.end(), lst4.begin(), lst4.end()));
-}
-
-TEST(EqualTest, EmptyRange) {
-  std::vector<int> v1{1, 2, 3};
-  std::vector<int> empty1;
-  std::vector<int> empty2;
-
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), empty1.begin(), empty1.end()));
-  EXPECT_FALSE(absl::equal(empty1.begin(), empty1.end(), v1.begin(), v1.end()));
-  EXPECT_TRUE(
-      absl::equal(empty1.begin(), empty1.end(), empty2.begin(), empty2.end()));
-}
-
-TEST(EqualTest, MixedIterTypes) {
-  std::vector<int> v1{1, 2, 3};
-  std::list<int> lst1{v1.begin(), v1.end()};
-  std::list<int> lst2{1, 2, 4};
-  std::list<int> lst3{1, 2};
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), lst1.begin(), lst1.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), lst2.begin(), lst2.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), lst3.begin(), lst3.end()));
-}
-
-TEST(EqualTest, MixedValueTypes) {
-  std::vector<int> v1{1, 2, 3};
-  std::vector<char> v2{1, 2, 3};
-  std::vector<char> v3{1, 2};
-  std::vector<char> v4{1, 2, 4};
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end()));
-}
-
-TEST(EqualTest, WeirdIterators) {
-  std::vector<bool> v1{true, false};
-  std::vector<bool> v2 = v1;
-  std::vector<bool> v3{true};
-  std::vector<bool> v4{true, true, true};
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end()));
-}
-
-TEST(EqualTest, CustomComparison) {
-  int n[] = {1, 2, 3, 4};
-  std::vector<int*> v1{&n[0], &n[1], &n[2]};
-  std::vector<int*> v2 = v1;
-  std::vector<int*> v3{&n[0], &n[1], &n[3]};
-  std::vector<int*> v4{&n[0], &n[1]};
-
-  auto eq = [](int* a, int* b) { return *a == *b; };
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), eq));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end(), eq));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end(), eq));
-}
-
-TEST(EqualTest, MoveOnlyPredicate) {
-  std::vector<int> v1{1, 2, 3};
-  std::vector<int> v2{4, 5, 6};
-
-  // move-only equality predicate
-  struct Eq {
-    Eq() = default;
-    Eq(Eq &&) = default;
-    Eq(const Eq &) = delete;
-    Eq &operator=(const Eq &) = delete;
-    bool operator()(const int a, const int b) const { return a == b; }
-  };
-
-  EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v1.begin(), v1.end(), Eq()));
-  EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), Eq()));
-}
-
-struct CountingTrivialPred {
-  int* count;
-  bool operator()(int, int) const {
-    ++*count;
-    return true;
-  }
-};
-
-TEST(EqualTest, RandomAccessComplexity) {
-  std::vector<int> v1{1, 1, 3};
-  std::vector<int> v2 = v1;
-  std::vector<int> v3{1, 2};
-
-  do {
-    int count = 0;
-    absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(),
-                CountingTrivialPred{&count});
-    EXPECT_LE(count, 3);
-  } while (std::next_permutation(v2.begin(), v2.end()));
-
-  int count = 0;
-  absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end(),
-              CountingTrivialPred{&count});
-  EXPECT_EQ(count, 0);
-}
-
-class LinearSearchTest : public testing::Test {
- protected:
-  LinearSearchTest() : container_{1, 2, 3} {}
-
-  static bool Is3(int n) { return n == 3; }
-  static bool Is4(int n) { return n == 4; }
-
-  std::vector<int> container_;
-};
-
-TEST_F(LinearSearchTest, linear_search) {
-  EXPECT_TRUE(absl::linear_search(container_.begin(), container_.end(), 3));
-  EXPECT_FALSE(absl::linear_search(container_.begin(), container_.end(), 4));
-}
-
-TEST_F(LinearSearchTest, linear_searchConst) {
-  const std::vector<int> *const const_container = &container_;
-  EXPECT_TRUE(
-      absl::linear_search(const_container->begin(), const_container->end(), 3));
-  EXPECT_FALSE(
-      absl::linear_search(const_container->begin(), const_container->end(), 4));
-}
-
-TEST(RotateTest, Rotate) {
-  std::vector<int> v{0, 1, 2, 3, 4};
-  EXPECT_EQ(*absl::rotate(v.begin(), v.begin() + 2, v.end()), 0);
-  EXPECT_THAT(v, testing::ElementsAreArray({2, 3, 4, 0, 1}));
-
-  std::list<int> l{0, 1, 2, 3, 4};
-  EXPECT_EQ(*absl::rotate(l.begin(), std::next(l.begin(), 3), l.end()), 0);
-  EXPECT_THAT(l, testing::ElementsAreArray({3, 4, 0, 1, 2}));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/algorithm/container.h b/third_party/abseil_cpp/absl/algorithm/container.h
deleted file mode 100644
index 6398438f08..0000000000
--- a/third_party/abseil_cpp/absl/algorithm/container.h
+++ /dev/null
@@ -1,1764 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: container.h
-// -----------------------------------------------------------------------------
-//
-// This header file provides Container-based versions of algorithmic functions
-// within the C++ standard library. The following standard library sets of
-// functions are covered within this file:
-//
-//   * Algorithmic <iterator> functions
-//   * Algorithmic <numeric> functions
-//   * <algorithm> functions
-//
-// The standard library functions operate on iterator ranges; the functions
-// within this API operate on containers, though many return iterator ranges.
-//
-// All functions within this API are named with a `c_` prefix. Calls such as
-// `absl::c_xx(container, ...) are equivalent to std:: functions such as
-// `std::xx(std::begin(cont), std::end(cont), ...)`. Functions that act on
-// iterators but not conceptually on iterator ranges (e.g. `std::iter_swap`)
-// have no equivalent here.
-//
-// For template parameter and variable naming, `C` indicates the container type
-// to which the function is applied, `Pred` indicates the predicate object type
-// to be used by the function and `T` indicates the applicable element type.
-
-#ifndef ABSL_ALGORITHM_CONTAINER_H_
-#define ABSL_ALGORITHM_CONTAINER_H_
-
-#include <algorithm>
-#include <cassert>
-#include <iterator>
-#include <numeric>
-#include <type_traits>
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "absl/algorithm/algorithm.h"
-#include "absl/base/macros.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_algorithm_internal {
-
-// NOTE: it is important to defer to ADL lookup for building with C++ modules,
-// especially for headers like <valarray> which are not visible from this file
-// but specialize std::begin and std::end.
-using std::begin;
-using std::end;
-
-// The type of the iterator given by begin(c) (possibly std::begin(c)).
-// ContainerIter<const vector<T>> gives vector<T>::const_iterator,
-// while ContainerIter<vector<T>> gives vector<T>::iterator.
-template <typename C>
-using ContainerIter = decltype(begin(std::declval<C&>()));
-
-// An MSVC bug involving template parameter substitution requires us to use
-// decltype() here instead of just std::pair.
-template <typename C1, typename C2>
-using ContainerIterPairType =
-    decltype(std::make_pair(ContainerIter<C1>(), ContainerIter<C2>()));
-
-template <typename C>
-using ContainerDifferenceType =
-    decltype(std::distance(std::declval<ContainerIter<C>>(),
-                           std::declval<ContainerIter<C>>()));
-
-template <typename C>
-using ContainerPointerType =
-    typename std::iterator_traits<ContainerIter<C>>::pointer;
-
-// container_algorithm_internal::c_begin and
-// container_algorithm_internal::c_end are abbreviations for proper ADL
-// lookup of std::begin and std::end, i.e.
-//   using std::begin;
-//   using std::end;
-//   std::foo(begin(c), end(c));
-// becomes
-//   std::foo(container_algorithm_internal::begin(c),
-//            container_algorithm_internal::end(c));
-// These are meant for internal use only.
-
-template <typename C>
-ContainerIter<C> c_begin(C& c) { return begin(c); }
-
-template <typename C>
-ContainerIter<C> c_end(C& c) { return end(c); }
-
-template <typename T>
-struct IsUnorderedContainer : std::false_type {};
-
-template <class Key, class T, class Hash, class KeyEqual, class Allocator>
-struct IsUnorderedContainer<
-    std::unordered_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
-
-template <class Key, class Hash, class KeyEqual, class Allocator>
-struct IsUnorderedContainer<std::unordered_set<Key, Hash, KeyEqual, Allocator>>
-    : std::true_type {};
-
-// container_algorithm_internal::c_size. It is meant for internal use only.
-
-template <class C>
-auto c_size(C& c) -> decltype(c.size()) {
-  return c.size();
-}
-
-template <class T, std::size_t N>
-constexpr std::size_t c_size(T (&)[N]) {
-  return N;
-}
-
-}  // namespace container_algorithm_internal
-
-// PUBLIC API
-
-//------------------------------------------------------------------------------
-// Abseil algorithm.h functions
-//------------------------------------------------------------------------------
-
-// c_linear_search()
-//
-// Container-based version of absl::linear_search() for performing a linear
-// search within a container.
-template <typename C, typename EqualityComparable>
-bool c_linear_search(const C& c, EqualityComparable&& value) {
-  return linear_search(container_algorithm_internal::c_begin(c),
-                       container_algorithm_internal::c_end(c),
-                       std::forward<EqualityComparable>(value));
-}
-
-//------------------------------------------------------------------------------
-// <iterator> algorithms
-//------------------------------------------------------------------------------
-
-// c_distance()
-//
-// Container-based version of the <iterator> `std::distance()` function to
-// return the number of elements within a container.
-template <typename C>
-container_algorithm_internal::ContainerDifferenceType<const C> c_distance(
-    const C& c) {
-  return std::distance(container_algorithm_internal::c_begin(c),
-                       container_algorithm_internal::c_end(c));
-}
-
-//------------------------------------------------------------------------------
-// <algorithm> Non-modifying sequence operations
-//------------------------------------------------------------------------------
-
-// c_all_of()
-//
-// Container-based version of the <algorithm> `std::all_of()` function to
-// test a condition on all elements within a container.
-template <typename C, typename Pred>
-bool c_all_of(const C& c, Pred&& pred) {
-  return std::all_of(container_algorithm_internal::c_begin(c),
-                     container_algorithm_internal::c_end(c),
-                     std::forward<Pred>(pred));
-}
-
-// c_any_of()
-//
-// Container-based version of the <algorithm> `std::any_of()` function to
-// test if any element in a container fulfills a condition.
-template <typename C, typename Pred>
-bool c_any_of(const C& c, Pred&& pred) {
-  return std::any_of(container_algorithm_internal::c_begin(c),
-                     container_algorithm_internal::c_end(c),
-                     std::forward<Pred>(pred));
-}
-
-// c_none_of()
-//
-// Container-based version of the <algorithm> `std::none_of()` function to
-// test if no elements in a container fulfill a condition.
-template <typename C, typename Pred>
-bool c_none_of(const C& c, Pred&& pred) {
-  return std::none_of(container_algorithm_internal::c_begin(c),
-                      container_algorithm_internal::c_end(c),
-                      std::forward<Pred>(pred));
-}
-
-// c_for_each()
-//
-// Container-based version of the <algorithm> `std::for_each()` function to
-// apply a function to a container's elements.
-template <typename C, typename Function>
-decay_t<Function> c_for_each(C&& c, Function&& f) {
-  return std::for_each(container_algorithm_internal::c_begin(c),
-                       container_algorithm_internal::c_end(c),
-                       std::forward<Function>(f));
-}
-
-// c_find()
-//
-// Container-based version of the <algorithm> `std::find()` function to find
-// the first element containing the passed value within a container value.
-template <typename C, typename T>
-container_algorithm_internal::ContainerIter<C> c_find(C& c, T&& value) {
-  return std::find(container_algorithm_internal::c_begin(c),
-                   container_algorithm_internal::c_end(c),
-                   std::forward<T>(value));
-}
-
-// c_find_if()
-//
-// Container-based version of the <algorithm> `std::find_if()` function to find
-// the first element in a container matching the given condition.
-template <typename C, typename Pred>
-container_algorithm_internal::ContainerIter<C> c_find_if(C& c, Pred&& pred) {
-  return std::find_if(container_algorithm_internal::c_begin(c),
-                      container_algorithm_internal::c_end(c),
-                      std::forward<Pred>(pred));
-}
-
-// c_find_if_not()
-//
-// Container-based version of the <algorithm> `std::find_if_not()` function to
-// find the first element in a container not matching the given condition.
-template <typename C, typename Pred>
-container_algorithm_internal::ContainerIter<C> c_find_if_not(C& c,
-                                                             Pred&& pred) {
-  return std::find_if_not(container_algorithm_internal::c_begin(c),
-                          container_algorithm_internal::c_end(c),
-                          std::forward<Pred>(pred));
-}
-
-// c_find_end()
-//
-// Container-based version of the <algorithm> `std::find_end()` function to
-// find the last subsequence within a container.
-template <typename Sequence1, typename Sequence2>
-container_algorithm_internal::ContainerIter<Sequence1> c_find_end(
-    Sequence1& sequence, Sequence2& subsequence) {
-  return std::find_end(container_algorithm_internal::c_begin(sequence),
-                       container_algorithm_internal::c_end(sequence),
-                       container_algorithm_internal::c_begin(subsequence),
-                       container_algorithm_internal::c_end(subsequence));
-}
-
-// Overload of c_find_end() for using a predicate evaluation other than `==` as
-// the function's test condition.
-template <typename Sequence1, typename Sequence2, typename BinaryPredicate>
-container_algorithm_internal::ContainerIter<Sequence1> c_find_end(
-    Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) {
-  return std::find_end(container_algorithm_internal::c_begin(sequence),
-                       container_algorithm_internal::c_end(sequence),
-                       container_algorithm_internal::c_begin(subsequence),
-                       container_algorithm_internal::c_end(subsequence),
-                       std::forward<BinaryPredicate>(pred));
-}
-
-// c_find_first_of()
-//
-// Container-based version of the <algorithm> `std::find_first_of()` function to
-// find the first element within the container that is also within the options
-// container.
-template <typename C1, typename C2>
-container_algorithm_internal::ContainerIter<C1> c_find_first_of(C1& container,
-                                                                C2& options) {
-  return std::find_first_of(container_algorithm_internal::c_begin(container),
-                            container_algorithm_internal::c_end(container),
-                            container_algorithm_internal::c_begin(options),
-                            container_algorithm_internal::c_end(options));
-}
-
-// Overload of c_find_first_of() for using a predicate evaluation other than
-// `==` as the function's test condition.
-template <typename C1, typename C2, typename BinaryPredicate>
-container_algorithm_internal::ContainerIter<C1> c_find_first_of(
-    C1& container, C2& options, BinaryPredicate&& pred) {
-  return std::find_first_of(container_algorithm_internal::c_begin(container),
-                            container_algorithm_internal::c_end(container),
-                            container_algorithm_internal::c_begin(options),
-                            container_algorithm_internal::c_end(options),
-                            std::forward<BinaryPredicate>(pred));
-}
-
-// c_adjacent_find()
-//
-// Container-based version of the <algorithm> `std::adjacent_find()` function to
-// find equal adjacent elements within a container.
-template <typename Sequence>
-container_algorithm_internal::ContainerIter<Sequence> c_adjacent_find(
-    Sequence& sequence) {
-  return std::adjacent_find(container_algorithm_internal::c_begin(sequence),
-                            container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_adjacent_find() for using a predicate evaluation other than
-// `==` as the function's test condition.
-template <typename Sequence, typename BinaryPredicate>
-container_algorithm_internal::ContainerIter<Sequence> c_adjacent_find(
-    Sequence& sequence, BinaryPredicate&& pred) {
-  return std::adjacent_find(container_algorithm_internal::c_begin(sequence),
-                            container_algorithm_internal::c_end(sequence),
-                            std::forward<BinaryPredicate>(pred));
-}
-
-// c_count()
-//
-// Container-based version of the <algorithm> `std::count()` function to count
-// values that match within a container.
-template <typename C, typename T>
-container_algorithm_internal::ContainerDifferenceType<const C> c_count(
-    const C& c, T&& value) {
-  return std::count(container_algorithm_internal::c_begin(c),
-                    container_algorithm_internal::c_end(c),
-                    std::forward<T>(value));
-}
-
-// c_count_if()
-//
-// Container-based version of the <algorithm> `std::count_if()` function to
-// count values matching a condition within a container.
-template <typename C, typename Pred>
-container_algorithm_internal::ContainerDifferenceType<const C> c_count_if(
-    const C& c, Pred&& pred) {
-  return std::count_if(container_algorithm_internal::c_begin(c),
-                       container_algorithm_internal::c_end(c),
-                       std::forward<Pred>(pred));
-}
-
-// c_mismatch()
-//
-// Container-based version of the <algorithm> `std::mismatch()` function to
-// return the first element where two ordered containers differ. Applies `==` to
-// the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)).
-template <typename C1, typename C2>
-container_algorithm_internal::ContainerIterPairType<C1, C2>
-c_mismatch(C1& c1, C2& c2) {
-  auto first1 = container_algorithm_internal::c_begin(c1);
-  auto last1 = container_algorithm_internal::c_end(c1);
-  auto first2 = container_algorithm_internal::c_begin(c2);
-  auto last2 = container_algorithm_internal::c_end(c2);
-
-  for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
-    // Negates equality because Cpp17EqualityComparable doesn't require clients
-    // to overload both `operator==` and `operator!=`.
-    if (!(*first1 == *first2)) {
-      break;
-    }
-  }
-
-  return std::make_pair(first1, first2);
-}
-
-// Overload of c_mismatch() for using a predicate evaluation other than `==` as
-// the function's test condition. Applies `pred`to the first N elements of `c1`
-// and `c2`, where N = min(size(c1), size(c2)).
-template <typename C1, typename C2, typename BinaryPredicate>
-container_algorithm_internal::ContainerIterPairType<C1, C2>
-c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) {
-  auto first1 = container_algorithm_internal::c_begin(c1);
-  auto last1 = container_algorithm_internal::c_end(c1);
-  auto first2 = container_algorithm_internal::c_begin(c2);
-  auto last2 = container_algorithm_internal::c_end(c2);
-
-  for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
-    if (!pred(*first1, *first2)) {
-      break;
-    }
-  }
-
-  return std::make_pair(first1, first2);
-}
-
-// c_equal()
-//
-// Container-based version of the <algorithm> `std::equal()` function to
-// test whether two containers are equal.
-//
-// NOTE: the semantics of c_equal() are slightly different than those of
-// equal(): while the latter iterates over the second container only up to the
-// size of the first container, c_equal() also checks whether the container
-// sizes are equal.  This better matches expectations about c_equal() based on
-// its signature.
-//
-// Example:
-//   vector v1 = <1, 2, 3>;
-//   vector v2 = <1, 2, 3, 4>;
-//   equal(std::begin(v1), std::end(v1), std::begin(v2)) returns true
-//   c_equal(v1, v2) returns false
-
-template <typename C1, typename C2>
-bool c_equal(const C1& c1, const C2& c2) {
-  return ((container_algorithm_internal::c_size(c1) ==
-           container_algorithm_internal::c_size(c2)) &&
-          std::equal(container_algorithm_internal::c_begin(c1),
-                     container_algorithm_internal::c_end(c1),
-                     container_algorithm_internal::c_begin(c2)));
-}
-
-// Overload of c_equal() for using a predicate evaluation other than `==` as
-// the function's test condition.
-template <typename C1, typename C2, typename BinaryPredicate>
-bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
-  return ((container_algorithm_internal::c_size(c1) ==
-           container_algorithm_internal::c_size(c2)) &&
-          std::equal(container_algorithm_internal::c_begin(c1),
-                     container_algorithm_internal::c_end(c1),
-                     container_algorithm_internal::c_begin(c2),
-                     std::forward<BinaryPredicate>(pred)));
-}
-
-// c_is_permutation()
-//
-// Container-based version of the <algorithm> `std::is_permutation()` function
-// to test whether a container is a permutation of another.
-template <typename C1, typename C2>
-bool c_is_permutation(const C1& c1, const C2& c2) {
-  using std::begin;
-  using std::end;
-  return c1.size() == c2.size() &&
-         std::is_permutation(begin(c1), end(c1), begin(c2));
-}
-
-// Overload of c_is_permutation() for using a predicate evaluation other than
-// `==` as the function's test condition.
-template <typename C1, typename C2, typename BinaryPredicate>
-bool c_is_permutation(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
-  using std::begin;
-  using std::end;
-  return c1.size() == c2.size() &&
-         std::is_permutation(begin(c1), end(c1), begin(c2),
-                             std::forward<BinaryPredicate>(pred));
-}
-
-// c_search()
-//
-// Container-based version of the <algorithm> `std::search()` function to search
-// a container for a subsequence.
-template <typename Sequence1, typename Sequence2>
-container_algorithm_internal::ContainerIter<Sequence1> c_search(
-    Sequence1& sequence, Sequence2& subsequence) {
-  return std::search(container_algorithm_internal::c_begin(sequence),
-                     container_algorithm_internal::c_end(sequence),
-                     container_algorithm_internal::c_begin(subsequence),
-                     container_algorithm_internal::c_end(subsequence));
-}
-
-// Overload of c_search() for using a predicate evaluation other than
-// `==` as the function's test condition.
-template <typename Sequence1, typename Sequence2, typename BinaryPredicate>
-container_algorithm_internal::ContainerIter<Sequence1> c_search(
-    Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) {
-  return std::search(container_algorithm_internal::c_begin(sequence),
-                     container_algorithm_internal::c_end(sequence),
-                     container_algorithm_internal::c_begin(subsequence),
-                     container_algorithm_internal::c_end(subsequence),
-                     std::forward<BinaryPredicate>(pred));
-}
-
-// c_search_n()
-//
-// Container-based version of the <algorithm> `std::search_n()` function to
-// search a container for the first sequence of N elements.
-template <typename Sequence, typename Size, typename T>
-container_algorithm_internal::ContainerIter<Sequence> c_search_n(
-    Sequence& sequence, Size count, T&& value) {
-  return std::search_n(container_algorithm_internal::c_begin(sequence),
-                       container_algorithm_internal::c_end(sequence), count,
-                       std::forward<T>(value));
-}
-
-// Overload of c_search_n() for using a predicate evaluation other than
-// `==` as the function's test condition.
-template <typename Sequence, typename Size, typename T,
-          typename BinaryPredicate>
-container_algorithm_internal::ContainerIter<Sequence> c_search_n(
-    Sequence& sequence, Size count, T&& value, BinaryPredicate&& pred) {
-  return std::search_n(container_algorithm_internal::c_begin(sequence),
-                       container_algorithm_internal::c_end(sequence), count,
-                       std::forward<T>(value),
-                       std::forward<BinaryPredicate>(pred));
-}
-
-//------------------------------------------------------------------------------
-// <algorithm> Modifying sequence operations
-//------------------------------------------------------------------------------
-
-// c_copy()
-//
-// Container-based version of the <algorithm> `std::copy()` function to copy a
-// container's elements into an iterator.
-template <typename InputSequence, typename OutputIterator>
-OutputIterator c_copy(const InputSequence& input, OutputIterator output) {
-  return std::copy(container_algorithm_internal::c_begin(input),
-                   container_algorithm_internal::c_end(input), output);
-}
-
-// c_copy_n()
-//
-// Container-based version of the <algorithm> `std::copy_n()` function to copy a
-// container's first N elements into an iterator.
-template <typename C, typename Size, typename OutputIterator>
-OutputIterator c_copy_n(const C& input, Size n, OutputIterator output) {
-  return std::copy_n(container_algorithm_internal::c_begin(input), n, output);
-}
-
-// c_copy_if()
-//
-// Container-based version of the <algorithm> `std::copy_if()` function to copy
-// a container's elements satisfying some condition into an iterator.
-template <typename InputSequence, typename OutputIterator, typename Pred>
-OutputIterator c_copy_if(const InputSequence& input, OutputIterator output,
-                         Pred&& pred) {
-  return std::copy_if(container_algorithm_internal::c_begin(input),
-                      container_algorithm_internal::c_end(input), output,
-                      std::forward<Pred>(pred));
-}
-
-// c_copy_backward()
-//
-// Container-based version of the <algorithm> `std::copy_backward()` function to
-// copy a container's elements in reverse order into an iterator.
-template <typename C, typename BidirectionalIterator>
-BidirectionalIterator c_copy_backward(const C& src,
-                                      BidirectionalIterator dest) {
-  return std::copy_backward(container_algorithm_internal::c_begin(src),
-                            container_algorithm_internal::c_end(src), dest);
-}
-
-// c_move()
-//
-// Container-based version of the <algorithm> `std::move()` function to move
-// a container's elements into an iterator.
-template <typename C, typename OutputIterator>
-OutputIterator c_move(C&& src, OutputIterator dest) {
-  return std::move(container_algorithm_internal::c_begin(src),
-                   container_algorithm_internal::c_end(src), dest);
-}
-
-// c_move_backward()
-//
-// Container-based version of the <algorithm> `std::move_backward()` function to
-// move a container's elements into an iterator in reverse order.
-template <typename C, typename BidirectionalIterator>
-BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) {
-  return std::move_backward(container_algorithm_internal::c_begin(src),
-                            container_algorithm_internal::c_end(src), dest);
-}
-
-// c_swap_ranges()
-//
-// Container-based version of the <algorithm> `std::swap_ranges()` function to
-// swap a container's elements with another container's elements. Swaps the
-// first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)).
-template <typename C1, typename C2>
-container_algorithm_internal::ContainerIter<C2> c_swap_ranges(C1& c1, C2& c2) {
-  auto first1 = container_algorithm_internal::c_begin(c1);
-  auto last1 = container_algorithm_internal::c_end(c1);
-  auto first2 = container_algorithm_internal::c_begin(c2);
-  auto last2 = container_algorithm_internal::c_end(c2);
-
-  using std::swap;
-  for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) {
-    swap(*first1, *first2);
-  }
-  return first2;
-}
-
-// c_transform()
-//
-// Container-based version of the <algorithm> `std::transform()` function to
-// transform a container's elements using the unary operation, storing the
-// result in an iterator pointing to the last transformed element in the output
-// range.
-template <typename InputSequence, typename OutputIterator, typename UnaryOp>
-OutputIterator c_transform(const InputSequence& input, OutputIterator output,
-                           UnaryOp&& unary_op) {
-  return std::transform(container_algorithm_internal::c_begin(input),
-                        container_algorithm_internal::c_end(input), output,
-                        std::forward<UnaryOp>(unary_op));
-}
-
-// Overload of c_transform() for performing a transformation using a binary
-// predicate. Applies `binary_op` to the first N elements of `c1` and `c2`,
-// where N = min(size(c1), size(c2)).
-template <typename InputSequence1, typename InputSequence2,
-          typename OutputIterator, typename BinaryOp>
-OutputIterator c_transform(const InputSequence1& input1,
-                           const InputSequence2& input2, OutputIterator output,
-                           BinaryOp&& binary_op) {
-  auto first1 = container_algorithm_internal::c_begin(input1);
-  auto last1 = container_algorithm_internal::c_end(input1);
-  auto first2 = container_algorithm_internal::c_begin(input2);
-  auto last2 = container_algorithm_internal::c_end(input2);
-  for (; first1 != last1 && first2 != last2;
-       ++first1, (void)++first2, ++output) {
-    *output = binary_op(*first1, *first2);
-  }
-
-  return output;
-}
-
-// c_replace()
-//
-// Container-based version of the <algorithm> `std::replace()` function to
-// replace a container's elements of some value with a new value. The container
-// is modified in place.
-template <typename Sequence, typename T>
-void c_replace(Sequence& sequence, const T& old_value, const T& new_value) {
-  std::replace(container_algorithm_internal::c_begin(sequence),
-               container_algorithm_internal::c_end(sequence), old_value,
-               new_value);
-}
-
-// c_replace_if()
-//
-// Container-based version of the <algorithm> `std::replace_if()` function to
-// replace a container's elements of some value with a new value based on some
-// condition. The container is modified in place.
-template <typename C, typename Pred, typename T>
-void c_replace_if(C& c, Pred&& pred, T&& new_value) {
-  std::replace_if(container_algorithm_internal::c_begin(c),
-                  container_algorithm_internal::c_end(c),
-                  std::forward<Pred>(pred), std::forward<T>(new_value));
-}
-
-// c_replace_copy()
-//
-// Container-based version of the <algorithm> `std::replace_copy()` function to
-// replace a container's elements of some value with a new value  and return the
-// results within an iterator.
-template <typename C, typename OutputIterator, typename T>
-OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value,
-                              T&& new_value) {
-  return std::replace_copy(container_algorithm_internal::c_begin(c),
-                           container_algorithm_internal::c_end(c), result,
-                           std::forward<T>(old_value),
-                           std::forward<T>(new_value));
-}
-
-// c_replace_copy_if()
-//
-// Container-based version of the <algorithm> `std::replace_copy_if()` function
-// to replace a container's elements of some value with a new value based on
-// some condition, and return the results within an iterator.
-template <typename C, typename OutputIterator, typename Pred, typename T>
-OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred,
-                                 T&& new_value) {
-  return std::replace_copy_if(container_algorithm_internal::c_begin(c),
-                              container_algorithm_internal::c_end(c), result,
-                              std::forward<Pred>(pred),
-                              std::forward<T>(new_value));
-}
-
-// c_fill()
-//
-// Container-based version of the <algorithm> `std::fill()` function to fill a
-// container with some value.
-template <typename C, typename T>
-void c_fill(C& c, T&& value) {
-  std::fill(container_algorithm_internal::c_begin(c),
-            container_algorithm_internal::c_end(c), std::forward<T>(value));
-}
-
-// c_fill_n()
-//
-// Container-based version of the <algorithm> `std::fill_n()` function to fill
-// the first N elements in a container with some value.
-template <typename C, typename Size, typename T>
-void c_fill_n(C& c, Size n, T&& value) {
-  std::fill_n(container_algorithm_internal::c_begin(c), n,
-              std::forward<T>(value));
-}
-
-// c_generate()
-//
-// Container-based version of the <algorithm> `std::generate()` function to
-// assign a container's elements to the values provided by the given generator.
-template <typename C, typename Generator>
-void c_generate(C& c, Generator&& gen) {
-  std::generate(container_algorithm_internal::c_begin(c),
-                container_algorithm_internal::c_end(c),
-                std::forward<Generator>(gen));
-}
-
-// c_generate_n()
-//
-// Container-based version of the <algorithm> `std::generate_n()` function to
-// assign a container's first N elements to the values provided by the given
-// generator.
-template <typename C, typename Size, typename Generator>
-container_algorithm_internal::ContainerIter<C> c_generate_n(C& c, Size n,
-                                                            Generator&& gen) {
-  return std::generate_n(container_algorithm_internal::c_begin(c), n,
-                         std::forward<Generator>(gen));
-}
-
-// Note: `c_xx()` <algorithm> container versions for `remove()`, `remove_if()`,
-// and `unique()` are omitted, because it's not clear whether or not such
-// functions should call erase on their supplied sequences afterwards. Either
-// behavior would be surprising for a different set of users.
-
-// c_remove_copy()
-//
-// Container-based version of the <algorithm> `std::remove_copy()` function to
-// copy a container's elements while removing any elements matching the given
-// `value`.
-template <typename C, typename OutputIterator, typename T>
-OutputIterator c_remove_copy(const C& c, OutputIterator result, T&& value) {
-  return std::remove_copy(container_algorithm_internal::c_begin(c),
-                          container_algorithm_internal::c_end(c), result,
-                          std::forward<T>(value));
-}
-
-// c_remove_copy_if()
-//
-// Container-based version of the <algorithm> `std::remove_copy_if()` function
-// to copy a container's elements while removing any elements matching the given
-// condition.
-template <typename C, typename OutputIterator, typename Pred>
-OutputIterator c_remove_copy_if(const C& c, OutputIterator result,
-                                Pred&& pred) {
-  return std::remove_copy_if(container_algorithm_internal::c_begin(c),
-                             container_algorithm_internal::c_end(c), result,
-                             std::forward<Pred>(pred));
-}
-
-// c_unique_copy()
-//
-// Container-based version of the <algorithm> `std::unique_copy()` function to
-// copy a container's elements while removing any elements containing duplicate
-// values.
-template <typename C, typename OutputIterator>
-OutputIterator c_unique_copy(const C& c, OutputIterator result) {
-  return std::unique_copy(container_algorithm_internal::c_begin(c),
-                          container_algorithm_internal::c_end(c), result);
-}
-
-// Overload of c_unique_copy() for using a predicate evaluation other than
-// `==` for comparing uniqueness of the element values.
-template <typename C, typename OutputIterator, typename BinaryPredicate>
-OutputIterator c_unique_copy(const C& c, OutputIterator result,
-                             BinaryPredicate&& pred) {
-  return std::unique_copy(container_algorithm_internal::c_begin(c),
-                          container_algorithm_internal::c_end(c), result,
-                          std::forward<BinaryPredicate>(pred));
-}
-
-// c_reverse()
-//
-// Container-based version of the <algorithm> `std::reverse()` function to
-// reverse a container's elements.
-template <typename Sequence>
-void c_reverse(Sequence& sequence) {
-  std::reverse(container_algorithm_internal::c_begin(sequence),
-               container_algorithm_internal::c_end(sequence));
-}
-
-// c_reverse_copy()
-//
-// Container-based version of the <algorithm> `std::reverse()` function to
-// reverse a container's elements and write them to an iterator range.
-template <typename C, typename OutputIterator>
-OutputIterator c_reverse_copy(const C& sequence, OutputIterator result) {
-  return std::reverse_copy(container_algorithm_internal::c_begin(sequence),
-                           container_algorithm_internal::c_end(sequence),
-                           result);
-}
-
-// c_rotate()
-//
-// Container-based version of the <algorithm> `std::rotate()` function to
-// shift a container's elements leftward such that the `middle` element becomes
-// the first element in the container.
-template <typename C,
-          typename Iterator = container_algorithm_internal::ContainerIter<C>>
-Iterator c_rotate(C& sequence, Iterator middle) {
-  return absl::rotate(container_algorithm_internal::c_begin(sequence), middle,
-                      container_algorithm_internal::c_end(sequence));
-}
-
-// c_rotate_copy()
-//
-// Container-based version of the <algorithm> `std::rotate_copy()` function to
-// shift a container's elements leftward such that the `middle` element becomes
-// the first element in a new iterator range.
-template <typename C, typename OutputIterator>
-OutputIterator c_rotate_copy(
-    const C& sequence,
-    container_algorithm_internal::ContainerIter<const C> middle,
-    OutputIterator result) {
-  return std::rotate_copy(container_algorithm_internal::c_begin(sequence),
-                          middle, container_algorithm_internal::c_end(sequence),
-                          result);
-}
-
-// c_shuffle()
-//
-// Container-based version of the <algorithm> `std::shuffle()` function to
-// randomly shuffle elements within the container using a `gen()` uniform random
-// number generator.
-template <typename RandomAccessContainer, typename UniformRandomBitGenerator>
-void c_shuffle(RandomAccessContainer& c, UniformRandomBitGenerator&& gen) {
-  std::shuffle(container_algorithm_internal::c_begin(c),
-               container_algorithm_internal::c_end(c),
-               std::forward<UniformRandomBitGenerator>(gen));
-}
-
-//------------------------------------------------------------------------------
-// <algorithm> Partition functions
-//------------------------------------------------------------------------------
-
-// c_is_partitioned()
-//
-// Container-based version of the <algorithm> `std::is_partitioned()` function
-// to test whether all elements in the container for which `pred` returns `true`
-// precede those for which `pred` is `false`.
-template <typename C, typename Pred>
-bool c_is_partitioned(const C& c, Pred&& pred) {
-  return std::is_partitioned(container_algorithm_internal::c_begin(c),
-                             container_algorithm_internal::c_end(c),
-                             std::forward<Pred>(pred));
-}
-
-// c_partition()
-//
-// Container-based version of the <algorithm> `std::partition()` function
-// to rearrange all elements in a container in such a way that all elements for
-// which `pred` returns `true` precede all those for which it returns `false`,
-// returning an iterator to the first element of the second group.
-template <typename C, typename Pred>
-container_algorithm_internal::ContainerIter<C> c_partition(C& c, Pred&& pred) {
-  return std::partition(container_algorithm_internal::c_begin(c),
-                        container_algorithm_internal::c_end(c),
-                        std::forward<Pred>(pred));
-}
-
-// c_stable_partition()
-//
-// Container-based version of the <algorithm> `std::stable_partition()` function
-// to rearrange all elements in a container in such a way that all elements for
-// which `pred` returns `true` precede all those for which it returns `false`,
-// preserving the relative ordering between the two groups. The function returns
-// an iterator to the first element of the second group.
-template <typename C, typename Pred>
-container_algorithm_internal::ContainerIter<C> c_stable_partition(C& c,
-                                                                  Pred&& pred) {
-  return std::stable_partition(container_algorithm_internal::c_begin(c),
-                               container_algorithm_internal::c_end(c),
-                               std::forward<Pred>(pred));
-}
-
-// c_partition_copy()
-//
-// Container-based version of the <algorithm> `std::partition_copy()` function
-// to partition a container's elements and return them into two iterators: one
-// for which `pred` returns `true`, and one for which `pred` returns `false.`
-
-template <typename C, typename OutputIterator1, typename OutputIterator2,
-          typename Pred>
-std::pair<OutputIterator1, OutputIterator2> c_partition_copy(
-    const C& c, OutputIterator1 out_true, OutputIterator2 out_false,
-    Pred&& pred) {
-  return std::partition_copy(container_algorithm_internal::c_begin(c),
-                             container_algorithm_internal::c_end(c), out_true,
-                             out_false, std::forward<Pred>(pred));
-}
-
-// c_partition_point()
-//
-// Container-based version of the <algorithm> `std::partition_point()` function
-// to return the first element of an already partitioned container for which
-// the given `pred` is not `true`.
-template <typename C, typename Pred>
-container_algorithm_internal::ContainerIter<C> c_partition_point(C& c,
-                                                                 Pred&& pred) {
-  return std::partition_point(container_algorithm_internal::c_begin(c),
-                              container_algorithm_internal::c_end(c),
-                              std::forward<Pred>(pred));
-}
-
-//------------------------------------------------------------------------------
-// <algorithm> Sorting functions
-//------------------------------------------------------------------------------
-
-// c_sort()
-//
-// Container-based version of the <algorithm> `std::sort()` function
-// to sort elements in ascending order of their values.
-template <typename C>
-void c_sort(C& c) {
-  std::sort(container_algorithm_internal::c_begin(c),
-            container_algorithm_internal::c_end(c));
-}
-
-// Overload of c_sort() for performing a `comp` comparison other than the
-// default `operator<`.
-template <typename C, typename Compare>
-void c_sort(C& c, Compare&& comp) {
-  std::sort(container_algorithm_internal::c_begin(c),
-            container_algorithm_internal::c_end(c),
-            std::forward<Compare>(comp));
-}
-
-// c_stable_sort()
-//
-// Container-based version of the <algorithm> `std::stable_sort()` function
-// to sort elements in ascending order of their values, preserving the order
-// of equivalents.
-template <typename C>
-void c_stable_sort(C& c) {
-  std::stable_sort(container_algorithm_internal::c_begin(c),
-                   container_algorithm_internal::c_end(c));
-}
-
-// Overload of c_stable_sort() for performing a `comp` comparison other than the
-// default `operator<`.
-template <typename C, typename Compare>
-void c_stable_sort(C& c, Compare&& comp) {
-  std::stable_sort(container_algorithm_internal::c_begin(c),
-                   container_algorithm_internal::c_end(c),
-                   std::forward<Compare>(comp));
-}
-
-// c_is_sorted()
-//
-// Container-based version of the <algorithm> `std::is_sorted()` function
-// to evaluate whether the given container is sorted in ascending order.
-template <typename C>
-bool c_is_sorted(const C& c) {
-  return std::is_sorted(container_algorithm_internal::c_begin(c),
-                        container_algorithm_internal::c_end(c));
-}
-
-// c_is_sorted() overload for performing a `comp` comparison other than the
-// default `operator<`.
-template <typename C, typename Compare>
-bool c_is_sorted(const C& c, Compare&& comp) {
-  return std::is_sorted(container_algorithm_internal::c_begin(c),
-                        container_algorithm_internal::c_end(c),
-                        std::forward<Compare>(comp));
-}
-
-// c_partial_sort()
-//
-// Container-based version of the <algorithm> `std::partial_sort()` function
-// to rearrange elements within a container such that elements before `middle`
-// are sorted in ascending order.
-template <typename RandomAccessContainer>
-void c_partial_sort(
-    RandomAccessContainer& sequence,
-    container_algorithm_internal::ContainerIter<RandomAccessContainer> middle) {
-  std::partial_sort(container_algorithm_internal::c_begin(sequence), middle,
-                    container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_partial_sort() for performing a `comp` comparison other than
-// the default `operator<`.
-template <typename RandomAccessContainer, typename Compare>
-void c_partial_sort(
-    RandomAccessContainer& sequence,
-    container_algorithm_internal::ContainerIter<RandomAccessContainer> middle,
-    Compare&& comp) {
-  std::partial_sort(container_algorithm_internal::c_begin(sequence), middle,
-                    container_algorithm_internal::c_end(sequence),
-                    std::forward<Compare>(comp));
-}
-
-// c_partial_sort_copy()
-//
-// Container-based version of the <algorithm> `std::partial_sort_copy()`
-// function to sort the elements in the given range `result` within the larger
-// `sequence` in ascending order (and using `result` as the output parameter).
-// At most min(result.last - result.first, sequence.last - sequence.first)
-// elements from the sequence will be stored in the result.
-template <typename C, typename RandomAccessContainer>
-container_algorithm_internal::ContainerIter<RandomAccessContainer>
-c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) {
-  return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence),
-                                container_algorithm_internal::c_end(sequence),
-                                container_algorithm_internal::c_begin(result),
-                                container_algorithm_internal::c_end(result));
-}
-
-// Overload of c_partial_sort_copy() for performing a `comp` comparison other
-// than the default `operator<`.
-template <typename C, typename RandomAccessContainer, typename Compare>
-container_algorithm_internal::ContainerIter<RandomAccessContainer>
-c_partial_sort_copy(const C& sequence, RandomAccessContainer& result,
-                    Compare&& comp) {
-  return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence),
-                                container_algorithm_internal::c_end(sequence),
-                                container_algorithm_internal::c_begin(result),
-                                container_algorithm_internal::c_end(result),
-                                std::forward<Compare>(comp));
-}
-
-// c_is_sorted_until()
-//
-// Container-based version of the <algorithm> `std::is_sorted_until()` function
-// to return the first element within a container that is not sorted in
-// ascending order as an iterator.
-template <typename C>
-container_algorithm_internal::ContainerIter<C> c_is_sorted_until(C& c) {
-  return std::is_sorted_until(container_algorithm_internal::c_begin(c),
-                              container_algorithm_internal::c_end(c));
-}
-
-// Overload of c_is_sorted_until() for performing a `comp` comparison other than
-// the default `operator<`.
-template <typename C, typename Compare>
-container_algorithm_internal::ContainerIter<C> c_is_sorted_until(
-    C& c, Compare&& comp) {
-  return std::is_sorted_until(container_algorithm_internal::c_begin(c),
-                              container_algorithm_internal::c_end(c),
-                              std::forward<Compare>(comp));
-}
-
-// c_nth_element()
-//
-// Container-based version of the <algorithm> `std::nth_element()` function
-// to rearrange the elements within a container such that the `nth` element
-// would be in that position in an ordered sequence; other elements may be in
-// any order, except that all preceding `nth` will be less than that element,
-// and all following `nth` will be greater than that element.
-template <typename RandomAccessContainer>
-void c_nth_element(
-    RandomAccessContainer& sequence,
-    container_algorithm_internal::ContainerIter<RandomAccessContainer> nth) {
-  std::nth_element(container_algorithm_internal::c_begin(sequence), nth,
-                   container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_nth_element() for performing a `comp` comparison other than
-// the default `operator<`.
-template <typename RandomAccessContainer, typename Compare>
-void c_nth_element(
-    RandomAccessContainer& sequence,
-    container_algorithm_internal::ContainerIter<RandomAccessContainer> nth,
-    Compare&& comp) {
-  std::nth_element(container_algorithm_internal::c_begin(sequence), nth,
-                   container_algorithm_internal::c_end(sequence),
-                   std::forward<Compare>(comp));
-}
-
-//------------------------------------------------------------------------------
-// <algorithm> Binary Search
-//------------------------------------------------------------------------------
-
-// c_lower_bound()
-//
-// Container-based version of the <algorithm> `std::lower_bound()` function
-// to return an iterator pointing to the first element in a sorted container
-// which does not compare less than `value`.
-template <typename Sequence, typename T>
-container_algorithm_internal::ContainerIter<Sequence> c_lower_bound(
-    Sequence& sequence, T&& value) {
-  return std::lower_bound(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value));
-}
-
-// Overload of c_lower_bound() for performing a `comp` comparison other than
-// the default `operator<`.
-template <typename Sequence, typename T, typename Compare>
-container_algorithm_internal::ContainerIter<Sequence> c_lower_bound(
-    Sequence& sequence, T&& value, Compare&& comp) {
-  return std::lower_bound(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value), std::forward<Compare>(comp));
-}
-
-// c_upper_bound()
-//
-// Container-based version of the <algorithm> `std::upper_bound()` function
-// to return an iterator pointing to the first element in a sorted container
-// which is greater than `value`.
-template <typename Sequence, typename T>
-container_algorithm_internal::ContainerIter<Sequence> c_upper_bound(
-    Sequence& sequence, T&& value) {
-  return std::upper_bound(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value));
-}
-
-// Overload of c_upper_bound() for performing a `comp` comparison other than
-// the default `operator<`.
-template <typename Sequence, typename T, typename Compare>
-container_algorithm_internal::ContainerIter<Sequence> c_upper_bound(
-    Sequence& sequence, T&& value, Compare&& comp) {
-  return std::upper_bound(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value), std::forward<Compare>(comp));
-}
-
-// c_equal_range()
-//
-// Container-based version of the <algorithm> `std::equal_range()` function
-// to return an iterator pair pointing to the first and last elements in a
-// sorted container which compare equal to `value`.
-template <typename Sequence, typename T>
-container_algorithm_internal::ContainerIterPairType<Sequence, Sequence>
-c_equal_range(Sequence& sequence, T&& value) {
-  return std::equal_range(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value));
-}
-
-// Overload of c_equal_range() for performing a `comp` comparison other than
-// the default `operator<`.
-template <typename Sequence, typename T, typename Compare>
-container_algorithm_internal::ContainerIterPairType<Sequence, Sequence>
-c_equal_range(Sequence& sequence, T&& value, Compare&& comp) {
-  return std::equal_range(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<T>(value), std::forward<Compare>(comp));
-}
-
-// c_binary_search()
-//
-// Container-based version of the <algorithm> `std::binary_search()` function
-// to test if any element in the sorted container contains a value equivalent to
-// 'value'.
-template <typename Sequence, typename T>
-bool c_binary_search(Sequence&& sequence, T&& value) {
-  return std::binary_search(container_algorithm_internal::c_begin(sequence),
-                            container_algorithm_internal::c_end(sequence),
-                            std::forward<T>(value));
-}
-
-// Overload of c_binary_search() for performing a `comp` comparison other than
-// the default `operator<`.
-template <typename Sequence, typename T, typename Compare>
-bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) {
-  return std::binary_search(container_algorithm_internal::c_begin(sequence),
-                            container_algorithm_internal::c_end(sequence),
-                            std::forward<T>(value),
-                            std::forward<Compare>(comp));
-}
-
-//------------------------------------------------------------------------------
-// <algorithm> Merge functions
-//------------------------------------------------------------------------------
-
-// c_merge()
-//
-// Container-based version of the <algorithm> `std::merge()` function
-// to merge two sorted containers into a single sorted iterator.
-template <typename C1, typename C2, typename OutputIterator>
-OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) {
-  return std::merge(container_algorithm_internal::c_begin(c1),
-                    container_algorithm_internal::c_end(c1),
-                    container_algorithm_internal::c_begin(c2),
-                    container_algorithm_internal::c_end(c2), result);
-}
-
-// Overload of c_merge() for performing a `comp` comparison other than
-// the default `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare>
-OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result,
-                       Compare&& comp) {
-  return std::merge(container_algorithm_internal::c_begin(c1),
-                    container_algorithm_internal::c_end(c1),
-                    container_algorithm_internal::c_begin(c2),
-                    container_algorithm_internal::c_end(c2), result,
-                    std::forward<Compare>(comp));
-}
-
-// c_inplace_merge()
-//
-// Container-based version of the <algorithm> `std::inplace_merge()` function
-// to merge a supplied iterator `middle` into a container.
-template <typename C>
-void c_inplace_merge(C& c,
-                     container_algorithm_internal::ContainerIter<C> middle) {
-  std::inplace_merge(container_algorithm_internal::c_begin(c), middle,
-                     container_algorithm_internal::c_end(c));
-}
-
-// Overload of c_inplace_merge() for performing a merge using a `comp` other
-// than `operator<`.
-template <typename C, typename Compare>
-void c_inplace_merge(C& c,
-                     container_algorithm_internal::ContainerIter<C> middle,
-                     Compare&& comp) {
-  std::inplace_merge(container_algorithm_internal::c_begin(c), middle,
-                     container_algorithm_internal::c_end(c),
-                     std::forward<Compare>(comp));
-}
-
-// c_includes()
-//
-// Container-based version of the <algorithm> `std::includes()` function
-// to test whether a sorted container `c1` entirely contains another sorted
-// container `c2`.
-template <typename C1, typename C2>
-bool c_includes(const C1& c1, const C2& c2) {
-  return std::includes(container_algorithm_internal::c_begin(c1),
-                       container_algorithm_internal::c_end(c1),
-                       container_algorithm_internal::c_begin(c2),
-                       container_algorithm_internal::c_end(c2));
-}
-
-// Overload of c_includes() for performing a merge using a `comp` other than
-// `operator<`.
-template <typename C1, typename C2, typename Compare>
-bool c_includes(const C1& c1, const C2& c2, Compare&& comp) {
-  return std::includes(container_algorithm_internal::c_begin(c1),
-                       container_algorithm_internal::c_end(c1),
-                       container_algorithm_internal::c_begin(c2),
-                       container_algorithm_internal::c_end(c2),
-                       std::forward<Compare>(comp));
-}
-
-// c_set_union()
-//
-// Container-based version of the <algorithm> `std::set_union()` function
-// to return an iterator containing the union of two containers; duplicate
-// values are not copied into the output.
-template <typename C1, typename C2, typename OutputIterator,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C1>::value,
-              void>::type,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C2>::value,
-              void>::type>
-OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) {
-  return std::set_union(container_algorithm_internal::c_begin(c1),
-                        container_algorithm_internal::c_end(c1),
-                        container_algorithm_internal::c_begin(c2),
-                        container_algorithm_internal::c_end(c2), output);
-}
-
-// Overload of c_set_union() for performing a merge using a `comp` other than
-// `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C1>::value,
-              void>::type,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C2>::value,
-              void>::type>
-OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
-                           Compare&& comp) {
-  return std::set_union(container_algorithm_internal::c_begin(c1),
-                        container_algorithm_internal::c_end(c1),
-                        container_algorithm_internal::c_begin(c2),
-                        container_algorithm_internal::c_end(c2), output,
-                        std::forward<Compare>(comp));
-}
-
-// c_set_intersection()
-//
-// Container-based version of the <algorithm> `std::set_intersection()` function
-// to return an iterator containing the intersection of two containers.
-template <typename C1, typename C2, typename OutputIterator,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C1>::value,
-              void>::type,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C2>::value,
-              void>::type>
-OutputIterator c_set_intersection(const C1& c1, const C2& c2,
-                                  OutputIterator output) {
-  return std::set_intersection(container_algorithm_internal::c_begin(c1),
-                               container_algorithm_internal::c_end(c1),
-                               container_algorithm_internal::c_begin(c2),
-                               container_algorithm_internal::c_end(c2), output);
-}
-
-// Overload of c_set_intersection() for performing a merge using a `comp` other
-// than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C1>::value,
-              void>::type,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C2>::value,
-              void>::type>
-OutputIterator c_set_intersection(const C1& c1, const C2& c2,
-                                  OutputIterator output, Compare&& comp) {
-  return std::set_intersection(container_algorithm_internal::c_begin(c1),
-                               container_algorithm_internal::c_end(c1),
-                               container_algorithm_internal::c_begin(c2),
-                               container_algorithm_internal::c_end(c2), output,
-                               std::forward<Compare>(comp));
-}
-
-// c_set_difference()
-//
-// Container-based version of the <algorithm> `std::set_difference()` function
-// to return an iterator containing elements present in the first container but
-// not in the second.
-template <typename C1, typename C2, typename OutputIterator,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C1>::value,
-              void>::type,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C2>::value,
-              void>::type>
-OutputIterator c_set_difference(const C1& c1, const C2& c2,
-                                OutputIterator output) {
-  return std::set_difference(container_algorithm_internal::c_begin(c1),
-                             container_algorithm_internal::c_end(c1),
-                             container_algorithm_internal::c_begin(c2),
-                             container_algorithm_internal::c_end(c2), output);
-}
-
-// Overload of c_set_difference() for performing a merge using a `comp` other
-// than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C1>::value,
-              void>::type,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C2>::value,
-              void>::type>
-OutputIterator c_set_difference(const C1& c1, const C2& c2,
-                                OutputIterator output, Compare&& comp) {
-  return std::set_difference(container_algorithm_internal::c_begin(c1),
-                             container_algorithm_internal::c_end(c1),
-                             container_algorithm_internal::c_begin(c2),
-                             container_algorithm_internal::c_end(c2), output,
-                             std::forward<Compare>(comp));
-}
-
-// c_set_symmetric_difference()
-//
-// Container-based version of the <algorithm> `std::set_symmetric_difference()`
-// function to return an iterator containing elements present in either one
-// container or the other, but not both.
-template <typename C1, typename C2, typename OutputIterator,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C1>::value,
-              void>::type,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C2>::value,
-              void>::type>
-OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
-                                          OutputIterator output) {
-  return std::set_symmetric_difference(
-      container_algorithm_internal::c_begin(c1),
-      container_algorithm_internal::c_end(c1),
-      container_algorithm_internal::c_begin(c2),
-      container_algorithm_internal::c_end(c2), output);
-}
-
-// Overload of c_set_symmetric_difference() for performing a merge using a
-// `comp` other than `operator<`.
-template <typename C1, typename C2, typename OutputIterator, typename Compare,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C1>::value,
-              void>::type,
-          typename = typename std::enable_if<
-              !container_algorithm_internal::IsUnorderedContainer<C2>::value,
-              void>::type>
-OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2,
-                                          OutputIterator output,
-                                          Compare&& comp) {
-  return std::set_symmetric_difference(
-      container_algorithm_internal::c_begin(c1),
-      container_algorithm_internal::c_end(c1),
-      container_algorithm_internal::c_begin(c2),
-      container_algorithm_internal::c_end(c2), output,
-      std::forward<Compare>(comp));
-}
-
-//------------------------------------------------------------------------------
-// <algorithm> Heap functions
-//------------------------------------------------------------------------------
-
-// c_push_heap()
-//
-// Container-based version of the <algorithm> `std::push_heap()` function
-// to push a value onto a container heap.
-template <typename RandomAccessContainer>
-void c_push_heap(RandomAccessContainer& sequence) {
-  std::push_heap(container_algorithm_internal::c_begin(sequence),
-                 container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_push_heap() for performing a push operation on a heap using a
-// `comp` other than `operator<`.
-template <typename RandomAccessContainer, typename Compare>
-void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) {
-  std::push_heap(container_algorithm_internal::c_begin(sequence),
-                 container_algorithm_internal::c_end(sequence),
-                 std::forward<Compare>(comp));
-}
-
-// c_pop_heap()
-//
-// Container-based version of the <algorithm> `std::pop_heap()` function
-// to pop a value from a heap container.
-template <typename RandomAccessContainer>
-void c_pop_heap(RandomAccessContainer& sequence) {
-  std::pop_heap(container_algorithm_internal::c_begin(sequence),
-                container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_pop_heap() for performing a pop operation on a heap using a
-// `comp` other than `operator<`.
-template <typename RandomAccessContainer, typename Compare>
-void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) {
-  std::pop_heap(container_algorithm_internal::c_begin(sequence),
-                container_algorithm_internal::c_end(sequence),
-                std::forward<Compare>(comp));
-}
-
-// c_make_heap()
-//
-// Container-based version of the <algorithm> `std::make_heap()` function
-// to make a container a heap.
-template <typename RandomAccessContainer>
-void c_make_heap(RandomAccessContainer& sequence) {
-  std::make_heap(container_algorithm_internal::c_begin(sequence),
-                 container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_make_heap() for performing heap comparisons using a
-// `comp` other than `operator<`
-template <typename RandomAccessContainer, typename Compare>
-void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) {
-  std::make_heap(container_algorithm_internal::c_begin(sequence),
-                 container_algorithm_internal::c_end(sequence),
-                 std::forward<Compare>(comp));
-}
-
-// c_sort_heap()
-//
-// Container-based version of the <algorithm> `std::sort_heap()` function
-// to sort a heap into ascending order (after which it is no longer a heap).
-template <typename RandomAccessContainer>
-void c_sort_heap(RandomAccessContainer& sequence) {
-  std::sort_heap(container_algorithm_internal::c_begin(sequence),
-                 container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_sort_heap() for performing heap comparisons using a
-// `comp` other than `operator<`
-template <typename RandomAccessContainer, typename Compare>
-void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) {
-  std::sort_heap(container_algorithm_internal::c_begin(sequence),
-                 container_algorithm_internal::c_end(sequence),
-                 std::forward<Compare>(comp));
-}
-
-// c_is_heap()
-//
-// Container-based version of the <algorithm> `std::is_heap()` function
-// to check whether the given container is a heap.
-template <typename RandomAccessContainer>
-bool c_is_heap(const RandomAccessContainer& sequence) {
-  return std::is_heap(container_algorithm_internal::c_begin(sequence),
-                      container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_is_heap() for performing heap comparisons using a
-// `comp` other than `operator<`
-template <typename RandomAccessContainer, typename Compare>
-bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) {
-  return std::is_heap(container_algorithm_internal::c_begin(sequence),
-                      container_algorithm_internal::c_end(sequence),
-                      std::forward<Compare>(comp));
-}
-
-// c_is_heap_until()
-//
-// Container-based version of the <algorithm> `std::is_heap_until()` function
-// to find the first element in a given container which is not in heap order.
-template <typename RandomAccessContainer>
-container_algorithm_internal::ContainerIter<RandomAccessContainer>
-c_is_heap_until(RandomAccessContainer& sequence) {
-  return std::is_heap_until(container_algorithm_internal::c_begin(sequence),
-                            container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_is_heap_until() for performing heap comparisons using a
-// `comp` other than `operator<`
-template <typename RandomAccessContainer, typename Compare>
-container_algorithm_internal::ContainerIter<RandomAccessContainer>
-c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) {
-  return std::is_heap_until(container_algorithm_internal::c_begin(sequence),
-                            container_algorithm_internal::c_end(sequence),
-                            std::forward<Compare>(comp));
-}
-
-//------------------------------------------------------------------------------
-//  <algorithm> Min/max
-//------------------------------------------------------------------------------
-
-// c_min_element()
-//
-// Container-based version of the <algorithm> `std::min_element()` function
-// to return an iterator pointing to the element with the smallest value, using
-// `operator<` to make the comparisons.
-template <typename Sequence>
-container_algorithm_internal::ContainerIter<Sequence> c_min_element(
-    Sequence& sequence) {
-  return std::min_element(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_min_element() for performing a `comp` comparison other than
-// `operator<`.
-template <typename Sequence, typename Compare>
-container_algorithm_internal::ContainerIter<Sequence> c_min_element(
-    Sequence& sequence, Compare&& comp) {
-  return std::min_element(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<Compare>(comp));
-}
-
-// c_max_element()
-//
-// Container-based version of the <algorithm> `std::max_element()` function
-// to return an iterator pointing to the element with the largest value, using
-// `operator<` to make the comparisons.
-template <typename Sequence>
-container_algorithm_internal::ContainerIter<Sequence> c_max_element(
-    Sequence& sequence) {
-  return std::max_element(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence));
-}
-
-// Overload of c_max_element() for performing a `comp` comparison other than
-// `operator<`.
-template <typename Sequence, typename Compare>
-container_algorithm_internal::ContainerIter<Sequence> c_max_element(
-    Sequence& sequence, Compare&& comp) {
-  return std::max_element(container_algorithm_internal::c_begin(sequence),
-                          container_algorithm_internal::c_end(sequence),
-                          std::forward<Compare>(comp));
-}
-
-// c_minmax_element()
-//
-// Container-based version of the <algorithm> `std::minmax_element()` function
-// to return a pair of iterators pointing to the elements containing the
-// smallest and largest values, respectively, using `operator<` to make the
-// comparisons.
-template <typename C>
-container_algorithm_internal::ContainerIterPairType<C, C>
-c_minmax_element(C& c) {
-  return std::minmax_element(container_algorithm_internal::c_begin(c),
-                             container_algorithm_internal::c_end(c));
-}
-
-// Overload of c_minmax_element() for performing `comp` comparisons other than
-// `operator<`.
-template <typename C, typename Compare>
-container_algorithm_internal::ContainerIterPairType<C, C>
-c_minmax_element(C& c, Compare&& comp) {
-  return std::minmax_element(container_algorithm_internal::c_begin(c),
-                             container_algorithm_internal::c_end(c),
-                             std::forward<Compare>(comp));
-}
-
-//------------------------------------------------------------------------------
-//  <algorithm> Lexicographical Comparisons
-//------------------------------------------------------------------------------
-
-// c_lexicographical_compare()
-//
-// Container-based version of the <algorithm> `std::lexicographical_compare()`
-// function to lexicographically compare (e.g. sort words alphabetically) two
-// container sequences. The comparison is performed using `operator<`. Note
-// that capital letters ("A-Z") have ASCII values less than lowercase letters
-// ("a-z").
-template <typename Sequence1, typename Sequence2>
-bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) {
-  return std::lexicographical_compare(
-      container_algorithm_internal::c_begin(sequence1),
-      container_algorithm_internal::c_end(sequence1),
-      container_algorithm_internal::c_begin(sequence2),
-      container_algorithm_internal::c_end(sequence2));
-}
-
-// Overload of c_lexicographical_compare() for performing a lexicographical
-// comparison using a `comp` operator instead of `operator<`.
-template <typename Sequence1, typename Sequence2, typename Compare>
-bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2,
-                               Compare&& comp) {
-  return std::lexicographical_compare(
-      container_algorithm_internal::c_begin(sequence1),
-      container_algorithm_internal::c_end(sequence1),
-      container_algorithm_internal::c_begin(sequence2),
-      container_algorithm_internal::c_end(sequence2),
-      std::forward<Compare>(comp));
-}
-
-// c_next_permutation()
-//
-// Container-based version of the <algorithm> `std::next_permutation()` function
-// to rearrange a container's elements into the next lexicographically greater
-// permutation.
-template <typename C>
-bool c_next_permutation(C& c) {
-  return std::next_permutation(container_algorithm_internal::c_begin(c),
-                               container_algorithm_internal::c_end(c));
-}
-
-// Overload of c_next_permutation() for performing a lexicographical
-// comparison using a `comp` operator instead of `operator<`.
-template <typename C, typename Compare>
-bool c_next_permutation(C& c, Compare&& comp) {
-  return std::next_permutation(container_algorithm_internal::c_begin(c),
-                               container_algorithm_internal::c_end(c),
-                               std::forward<Compare>(comp));
-}
-
-// c_prev_permutation()
-//
-// Container-based version of the <algorithm> `std::prev_permutation()` function
-// to rearrange a container's elements into the next lexicographically lesser
-// permutation.
-template <typename C>
-bool c_prev_permutation(C& c) {
-  return std::prev_permutation(container_algorithm_internal::c_begin(c),
-                               container_algorithm_internal::c_end(c));
-}
-
-// Overload of c_prev_permutation() for performing a lexicographical
-// comparison using a `comp` operator instead of `operator<`.
-template <typename C, typename Compare>
-bool c_prev_permutation(C& c, Compare&& comp) {
-  return std::prev_permutation(container_algorithm_internal::c_begin(c),
-                               container_algorithm_internal::c_end(c),
-                               std::forward<Compare>(comp));
-}
-
-//------------------------------------------------------------------------------
-// <numeric> algorithms
-//------------------------------------------------------------------------------
-
-// c_iota()
-//
-// Container-based version of the <algorithm> `std::iota()` function
-// to compute successive values of `value`, as if incremented with `++value`
-// after each element is written. and write them to the container.
-template <typename Sequence, typename T>
-void c_iota(Sequence& sequence, T&& value) {
-  std::iota(container_algorithm_internal::c_begin(sequence),
-            container_algorithm_internal::c_end(sequence),
-            std::forward<T>(value));
-}
-// c_accumulate()
-//
-// Container-based version of the <algorithm> `std::accumulate()` function
-// to accumulate the element values of a container to `init` and return that
-// accumulation by value.
-//
-// Note: Due to a language technicality this function has return type
-// absl::decay_t<T>. As a user of this function you can casually read
-// this as "returns T by value" and assume it does the right thing.
-template <typename Sequence, typename T>
-decay_t<T> c_accumulate(const Sequence& sequence, T&& init) {
-  return std::accumulate(container_algorithm_internal::c_begin(sequence),
-                         container_algorithm_internal::c_end(sequence),
-                         std::forward<T>(init));
-}
-
-// Overload of c_accumulate() for using a binary operations other than
-// addition for computing the accumulation.
-template <typename Sequence, typename T, typename BinaryOp>
-decay_t<T> c_accumulate(const Sequence& sequence, T&& init,
-                        BinaryOp&& binary_op) {
-  return std::accumulate(container_algorithm_internal::c_begin(sequence),
-                         container_algorithm_internal::c_end(sequence),
-                         std::forward<T>(init),
-                         std::forward<BinaryOp>(binary_op));
-}
-
-// c_inner_product()
-//
-// Container-based version of the <algorithm> `std::inner_product()` function
-// to compute the cumulative inner product of container element pairs.
-//
-// Note: Due to a language technicality this function has return type
-// absl::decay_t<T>. As a user of this function you can casually read
-// this as "returns T by value" and assume it does the right thing.
-template <typename Sequence1, typename Sequence2, typename T>
-decay_t<T> c_inner_product(const Sequence1& factors1, const Sequence2& factors2,
-                           T&& sum) {
-  return std::inner_product(container_algorithm_internal::c_begin(factors1),
-                            container_algorithm_internal::c_end(factors1),
-                            container_algorithm_internal::c_begin(factors2),
-                            std::forward<T>(sum));
-}
-
-// Overload of c_inner_product() for using binary operations other than
-// `operator+` (for computing the accumulation) and `operator*` (for computing
-// the product between the two container's element pair).
-template <typename Sequence1, typename Sequence2, typename T,
-          typename BinaryOp1, typename BinaryOp2>
-decay_t<T> c_inner_product(const Sequence1& factors1, const Sequence2& factors2,
-                           T&& sum, BinaryOp1&& op1, BinaryOp2&& op2) {
-  return std::inner_product(container_algorithm_internal::c_begin(factors1),
-                            container_algorithm_internal::c_end(factors1),
-                            container_algorithm_internal::c_begin(factors2),
-                            std::forward<T>(sum), std::forward<BinaryOp1>(op1),
-                            std::forward<BinaryOp2>(op2));
-}
-
-// c_adjacent_difference()
-//
-// Container-based version of the <algorithm> `std::adjacent_difference()`
-// function to compute the difference between each element and the one preceding
-// it and write it to an iterator.
-template <typename InputSequence, typename OutputIt>
-OutputIt c_adjacent_difference(const InputSequence& input,
-                               OutputIt output_first) {
-  return std::adjacent_difference(container_algorithm_internal::c_begin(input),
-                                  container_algorithm_internal::c_end(input),
-                                  output_first);
-}
-
-// Overload of c_adjacent_difference() for using a binary operation other than
-// subtraction to compute the adjacent difference.
-template <typename InputSequence, typename OutputIt, typename BinaryOp>
-OutputIt c_adjacent_difference(const InputSequence& input,
-                               OutputIt output_first, BinaryOp&& op) {
-  return std::adjacent_difference(container_algorithm_internal::c_begin(input),
-                                  container_algorithm_internal::c_end(input),
-                                  output_first, std::forward<BinaryOp>(op));
-}
-
-// c_partial_sum()
-//
-// Container-based version of the <algorithm> `std::partial_sum()` function
-// to compute the partial sum of the elements in a sequence and write them
-// to an iterator. The partial sum is the sum of all element values so far in
-// the sequence.
-template <typename InputSequence, typename OutputIt>
-OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first) {
-  return std::partial_sum(container_algorithm_internal::c_begin(input),
-                          container_algorithm_internal::c_end(input),
-                          output_first);
-}
-
-// Overload of c_partial_sum() for using a binary operation other than addition
-// to compute the "partial sum".
-template <typename InputSequence, typename OutputIt, typename BinaryOp>
-OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first,
-                       BinaryOp&& op) {
-  return std::partial_sum(container_algorithm_internal::c_begin(input),
-                          container_algorithm_internal::c_end(input),
-                          output_first, std::forward<BinaryOp>(op));
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_ALGORITHM_CONTAINER_H_
diff --git a/third_party/abseil_cpp/absl/algorithm/container_test.cc b/third_party/abseil_cpp/absl/algorithm/container_test.cc
deleted file mode 100644
index 605afc8040..0000000000
--- a/third_party/abseil_cpp/absl/algorithm/container_test.cc
+++ /dev/null
@@ -1,1124 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/algorithm/container.h"
-
-#include <functional>
-#include <initializer_list>
-#include <iterator>
-#include <list>
-#include <memory>
-#include <ostream>
-#include <random>
-#include <set>
-#include <unordered_set>
-#include <utility>
-#include <valarray>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/casts.h"
-#include "absl/base/macros.h"
-#include "absl/memory/memory.h"
-#include "absl/types/span.h"
-
-namespace {
-
-using ::testing::Each;
-using ::testing::ElementsAre;
-using ::testing::Gt;
-using ::testing::IsNull;
-using ::testing::Lt;
-using ::testing::Pointee;
-using ::testing::Truly;
-using ::testing::UnorderedElementsAre;
-
-// Most of these tests just check that the code compiles, not that it
-// does the right thing. That's fine since the functions just forward
-// to the STL implementation.
-class NonMutatingTest : public testing::Test {
- protected:
-  std::unordered_set<int> container_ = {1, 2, 3};
-  std::list<int> sequence_ = {1, 2, 3};
-  std::vector<int> vector_ = {1, 2, 3};
-  int array_[3] = {1, 2, 3};
-};
-
-struct AccumulateCalls {
-  void operator()(int value) { calls.push_back(value); }
-  std::vector<int> calls;
-};
-
-bool Predicate(int value) { return value < 3; }
-bool BinPredicate(int v1, int v2) { return v1 < v2; }
-bool Equals(int v1, int v2) { return v1 == v2; }
-bool IsOdd(int x) { return x % 2 != 0; }
-
-TEST_F(NonMutatingTest, Distance) {
-  EXPECT_EQ(container_.size(), absl::c_distance(container_));
-  EXPECT_EQ(sequence_.size(), absl::c_distance(sequence_));
-  EXPECT_EQ(vector_.size(), absl::c_distance(vector_));
-  EXPECT_EQ(ABSL_ARRAYSIZE(array_), absl::c_distance(array_));
-
-  // Works with a temporary argument.
-  EXPECT_EQ(vector_.size(), absl::c_distance(std::vector<int>(vector_)));
-}
-
-TEST_F(NonMutatingTest, Distance_OverloadedBeginEnd) {
-  // Works with classes which have custom ADL-selected overloads of std::begin
-  // and std::end.
-  std::initializer_list<int> a = {1, 2, 3};
-  std::valarray<int> b = {1, 2, 3};
-  EXPECT_EQ(3, absl::c_distance(a));
-  EXPECT_EQ(3, absl::c_distance(b));
-
-  // It is assumed that other c_* functions use the same mechanism for
-  // ADL-selecting begin/end overloads.
-}
-
-TEST_F(NonMutatingTest, ForEach) {
-  AccumulateCalls c = absl::c_for_each(container_, AccumulateCalls());
-  // Don't rely on the unordered_set's order.
-  std::sort(c.calls.begin(), c.calls.end());
-  EXPECT_EQ(vector_, c.calls);
-
-  // Works with temporary container, too.
-  AccumulateCalls c2 =
-      absl::c_for_each(std::unordered_set<int>(container_), AccumulateCalls());
-  std::sort(c2.calls.begin(), c2.calls.end());
-  EXPECT_EQ(vector_, c2.calls);
-}
-
-TEST_F(NonMutatingTest, FindReturnsCorrectType) {
-  auto it = absl::c_find(container_, 3);
-  EXPECT_EQ(3, *it);
-  absl::c_find(absl::implicit_cast<const std::list<int>&>(sequence_), 3);
-}
-
-TEST_F(NonMutatingTest, FindIf) { absl::c_find_if(container_, Predicate); }
-
-TEST_F(NonMutatingTest, FindIfNot) {
-  absl::c_find_if_not(container_, Predicate);
-}
-
-TEST_F(NonMutatingTest, FindEnd) {
-  absl::c_find_end(sequence_, vector_);
-  absl::c_find_end(vector_, sequence_);
-}
-
-TEST_F(NonMutatingTest, FindEndWithPredicate) {
-  absl::c_find_end(sequence_, vector_, BinPredicate);
-  absl::c_find_end(vector_, sequence_, BinPredicate);
-}
-
-TEST_F(NonMutatingTest, FindFirstOf) {
-  absl::c_find_first_of(container_, sequence_);
-  absl::c_find_first_of(sequence_, container_);
-}
-
-TEST_F(NonMutatingTest, FindFirstOfWithPredicate) {
-  absl::c_find_first_of(container_, sequence_, BinPredicate);
-  absl::c_find_first_of(sequence_, container_, BinPredicate);
-}
-
-TEST_F(NonMutatingTest, AdjacentFind) { absl::c_adjacent_find(sequence_); }
-
-TEST_F(NonMutatingTest, AdjacentFindWithPredicate) {
-  absl::c_adjacent_find(sequence_, BinPredicate);
-}
-
-TEST_F(NonMutatingTest, Count) { EXPECT_EQ(1, absl::c_count(container_, 3)); }
-
-TEST_F(NonMutatingTest, CountIf) {
-  EXPECT_EQ(2, absl::c_count_if(container_, Predicate));
-  const std::unordered_set<int>& const_container = container_;
-  EXPECT_EQ(2, absl::c_count_if(const_container, Predicate));
-}
-
-TEST_F(NonMutatingTest, Mismatch) {
-  // Testing necessary as absl::c_mismatch executes logic.
-  {
-    auto result = absl::c_mismatch(vector_, sequence_);
-    EXPECT_EQ(result.first, vector_.end());
-    EXPECT_EQ(result.second, sequence_.end());
-  }
-  {
-    auto result = absl::c_mismatch(sequence_, vector_);
-    EXPECT_EQ(result.first, sequence_.end());
-    EXPECT_EQ(result.second, vector_.end());
-  }
-
-  sequence_.back() = 5;
-  {
-    auto result = absl::c_mismatch(vector_, sequence_);
-    EXPECT_EQ(result.first, std::prev(vector_.end()));
-    EXPECT_EQ(result.second, std::prev(sequence_.end()));
-  }
-  {
-    auto result = absl::c_mismatch(sequence_, vector_);
-    EXPECT_EQ(result.first, std::prev(sequence_.end()));
-    EXPECT_EQ(result.second, std::prev(vector_.end()));
-  }
-
-  sequence_.pop_back();
-  {
-    auto result = absl::c_mismatch(vector_, sequence_);
-    EXPECT_EQ(result.first, std::prev(vector_.end()));
-    EXPECT_EQ(result.second, sequence_.end());
-  }
-  {
-    auto result = absl::c_mismatch(sequence_, vector_);
-    EXPECT_EQ(result.first, sequence_.end());
-    EXPECT_EQ(result.second, std::prev(vector_.end()));
-  }
-  {
-    struct NoNotEquals {
-      constexpr bool operator==(NoNotEquals) const { return true; }
-      constexpr bool operator!=(NoNotEquals) const = delete;
-    };
-    std::vector<NoNotEquals> first;
-    std::list<NoNotEquals> second;
-
-    // Check this still compiles.
-    absl::c_mismatch(first, second);
-  }
-}
-
-TEST_F(NonMutatingTest, MismatchWithPredicate) {
-  // Testing necessary as absl::c_mismatch executes logic.
-  {
-    auto result = absl::c_mismatch(vector_, sequence_, BinPredicate);
-    EXPECT_EQ(result.first, vector_.begin());
-    EXPECT_EQ(result.second, sequence_.begin());
-  }
-  {
-    auto result = absl::c_mismatch(sequence_, vector_, BinPredicate);
-    EXPECT_EQ(result.first, sequence_.begin());
-    EXPECT_EQ(result.second, vector_.begin());
-  }
-
-  sequence_.front() = 0;
-  {
-    auto result = absl::c_mismatch(vector_, sequence_, BinPredicate);
-    EXPECT_EQ(result.first, vector_.begin());
-    EXPECT_EQ(result.second, sequence_.begin());
-  }
-  {
-    auto result = absl::c_mismatch(sequence_, vector_, BinPredicate);
-    EXPECT_EQ(result.first, std::next(sequence_.begin()));
-    EXPECT_EQ(result.second, std::next(vector_.begin()));
-  }
-
-  sequence_.clear();
-  {
-    auto result = absl::c_mismatch(vector_, sequence_, BinPredicate);
-    EXPECT_EQ(result.first, vector_.begin());
-    EXPECT_EQ(result.second, sequence_.end());
-  }
-  {
-    auto result = absl::c_mismatch(sequence_, vector_, BinPredicate);
-    EXPECT_EQ(result.first, sequence_.end());
-    EXPECT_EQ(result.second, vector_.begin());
-  }
-}
-
-TEST_F(NonMutatingTest, Equal) {
-  EXPECT_TRUE(absl::c_equal(vector_, sequence_));
-  EXPECT_TRUE(absl::c_equal(sequence_, vector_));
-  EXPECT_TRUE(absl::c_equal(sequence_, array_));
-  EXPECT_TRUE(absl::c_equal(array_, vector_));
-
-  // Test that behavior appropriately differs from that of equal().
-  std::vector<int> vector_plus = {1, 2, 3};
-  vector_plus.push_back(4);
-  EXPECT_FALSE(absl::c_equal(vector_plus, sequence_));
-  EXPECT_FALSE(absl::c_equal(sequence_, vector_plus));
-  EXPECT_FALSE(absl::c_equal(array_, vector_plus));
-}
-
-TEST_F(NonMutatingTest, EqualWithPredicate) {
-  EXPECT_TRUE(absl::c_equal(vector_, sequence_, Equals));
-  EXPECT_TRUE(absl::c_equal(sequence_, vector_, Equals));
-  EXPECT_TRUE(absl::c_equal(array_, sequence_, Equals));
-  EXPECT_TRUE(absl::c_equal(vector_, array_, Equals));
-
-  // Test that behavior appropriately differs from that of equal().
-  std::vector<int> vector_plus = {1, 2, 3};
-  vector_plus.push_back(4);
-  EXPECT_FALSE(absl::c_equal(vector_plus, sequence_, Equals));
-  EXPECT_FALSE(absl::c_equal(sequence_, vector_plus, Equals));
-  EXPECT_FALSE(absl::c_equal(vector_plus, array_, Equals));
-}
-
-TEST_F(NonMutatingTest, IsPermutation) {
-  auto vector_permut_ = vector_;
-  std::next_permutation(vector_permut_.begin(), vector_permut_.end());
-  EXPECT_TRUE(absl::c_is_permutation(vector_permut_, sequence_));
-  EXPECT_TRUE(absl::c_is_permutation(sequence_, vector_permut_));
-
-  // Test that behavior appropriately differs from that of is_permutation().
-  std::vector<int> vector_plus = {1, 2, 3};
-  vector_plus.push_back(4);
-  EXPECT_FALSE(absl::c_is_permutation(vector_plus, sequence_));
-  EXPECT_FALSE(absl::c_is_permutation(sequence_, vector_plus));
-}
-
-TEST_F(NonMutatingTest, IsPermutationWithPredicate) {
-  auto vector_permut_ = vector_;
-  std::next_permutation(vector_permut_.begin(), vector_permut_.end());
-  EXPECT_TRUE(absl::c_is_permutation(vector_permut_, sequence_, Equals));
-  EXPECT_TRUE(absl::c_is_permutation(sequence_, vector_permut_, Equals));
-
-  // Test that behavior appropriately differs from that of is_permutation().
-  std::vector<int> vector_plus = {1, 2, 3};
-  vector_plus.push_back(4);
-  EXPECT_FALSE(absl::c_is_permutation(vector_plus, sequence_, Equals));
-  EXPECT_FALSE(absl::c_is_permutation(sequence_, vector_plus, Equals));
-}
-
-TEST_F(NonMutatingTest, Search) {
-  absl::c_search(sequence_, vector_);
-  absl::c_search(vector_, sequence_);
-  absl::c_search(array_, sequence_);
-}
-
-TEST_F(NonMutatingTest, SearchWithPredicate) {
-  absl::c_search(sequence_, vector_, BinPredicate);
-  absl::c_search(vector_, sequence_, BinPredicate);
-}
-
-TEST_F(NonMutatingTest, SearchN) { absl::c_search_n(sequence_, 3, 1); }
-
-TEST_F(NonMutatingTest, SearchNWithPredicate) {
-  absl::c_search_n(sequence_, 3, 1, BinPredicate);
-}
-
-TEST_F(NonMutatingTest, LowerBound) {
-  std::list<int>::iterator i = absl::c_lower_bound(sequence_, 3);
-  ASSERT_TRUE(i != sequence_.end());
-  EXPECT_EQ(2, std::distance(sequence_.begin(), i));
-  EXPECT_EQ(3, *i);
-}
-
-TEST_F(NonMutatingTest, LowerBoundWithPredicate) {
-  std::vector<int> v(vector_);
-  std::sort(v.begin(), v.end(), std::greater<int>());
-  std::vector<int>::iterator i = absl::c_lower_bound(v, 3, std::greater<int>());
-  EXPECT_TRUE(i == v.begin());
-  EXPECT_EQ(3, *i);
-}
-
-TEST_F(NonMutatingTest, UpperBound) {
-  std::list<int>::iterator i = absl::c_upper_bound(sequence_, 1);
-  ASSERT_TRUE(i != sequence_.end());
-  EXPECT_EQ(1, std::distance(sequence_.begin(), i));
-  EXPECT_EQ(2, *i);
-}
-
-TEST_F(NonMutatingTest, UpperBoundWithPredicate) {
-  std::vector<int> v(vector_);
-  std::sort(v.begin(), v.end(), std::greater<int>());
-  std::vector<int>::iterator i = absl::c_upper_bound(v, 1, std::greater<int>());
-  EXPECT_EQ(3, i - v.begin());
-  EXPECT_TRUE(i == v.end());
-}
-
-TEST_F(NonMutatingTest, EqualRange) {
-  std::pair<std::list<int>::iterator, std::list<int>::iterator> p =
-      absl::c_equal_range(sequence_, 2);
-  EXPECT_EQ(1, std::distance(sequence_.begin(), p.first));
-  EXPECT_EQ(2, std::distance(sequence_.begin(), p.second));
-}
-
-TEST_F(NonMutatingTest, EqualRangeArray) {
-  auto p = absl::c_equal_range(array_, 2);
-  EXPECT_EQ(1, std::distance(std::begin(array_), p.first));
-  EXPECT_EQ(2, std::distance(std::begin(array_), p.second));
-}
-
-TEST_F(NonMutatingTest, EqualRangeWithPredicate) {
-  std::vector<int> v(vector_);
-  std::sort(v.begin(), v.end(), std::greater<int>());
-  std::pair<std::vector<int>::iterator, std::vector<int>::iterator> p =
-      absl::c_equal_range(v, 2, std::greater<int>());
-  EXPECT_EQ(1, std::distance(v.begin(), p.first));
-  EXPECT_EQ(2, std::distance(v.begin(), p.second));
-}
-
-TEST_F(NonMutatingTest, BinarySearch) {
-  EXPECT_TRUE(absl::c_binary_search(vector_, 2));
-  EXPECT_TRUE(absl::c_binary_search(std::vector<int>(vector_), 2));
-}
-
-TEST_F(NonMutatingTest, BinarySearchWithPredicate) {
-  std::vector<int> v(vector_);
-  std::sort(v.begin(), v.end(), std::greater<int>());
-  EXPECT_TRUE(absl::c_binary_search(v, 2, std::greater<int>()));
-  EXPECT_TRUE(
-      absl::c_binary_search(std::vector<int>(v), 2, std::greater<int>()));
-}
-
-TEST_F(NonMutatingTest, MinElement) {
-  std::list<int>::iterator i = absl::c_min_element(sequence_);
-  ASSERT_TRUE(i != sequence_.end());
-  EXPECT_EQ(*i, 1);
-}
-
-TEST_F(NonMutatingTest, MinElementWithPredicate) {
-  std::list<int>::iterator i =
-      absl::c_min_element(sequence_, std::greater<int>());
-  ASSERT_TRUE(i != sequence_.end());
-  EXPECT_EQ(*i, 3);
-}
-
-TEST_F(NonMutatingTest, MaxElement) {
-  std::list<int>::iterator i = absl::c_max_element(sequence_);
-  ASSERT_TRUE(i != sequence_.end());
-  EXPECT_EQ(*i, 3);
-}
-
-TEST_F(NonMutatingTest, MaxElementWithPredicate) {
-  std::list<int>::iterator i =
-      absl::c_max_element(sequence_, std::greater<int>());
-  ASSERT_TRUE(i != sequence_.end());
-  EXPECT_EQ(*i, 1);
-}
-
-TEST_F(NonMutatingTest, LexicographicalCompare) {
-  EXPECT_FALSE(absl::c_lexicographical_compare(sequence_, sequence_));
-
-  std::vector<int> v;
-  v.push_back(1);
-  v.push_back(2);
-  v.push_back(4);
-
-  EXPECT_TRUE(absl::c_lexicographical_compare(sequence_, v));
-  EXPECT_TRUE(absl::c_lexicographical_compare(std::list<int>(sequence_), v));
-}
-
-TEST_F(NonMutatingTest, LexicographicalCopmareWithPredicate) {
-  EXPECT_FALSE(absl::c_lexicographical_compare(sequence_, sequence_,
-                                               std::greater<int>()));
-
-  std::vector<int> v;
-  v.push_back(1);
-  v.push_back(2);
-  v.push_back(4);
-
-  EXPECT_TRUE(
-      absl::c_lexicographical_compare(v, sequence_, std::greater<int>()));
-  EXPECT_TRUE(absl::c_lexicographical_compare(
-      std::vector<int>(v), std::list<int>(sequence_), std::greater<int>()));
-}
-
-TEST_F(NonMutatingTest, Includes) {
-  std::set<int> s(vector_.begin(), vector_.end());
-  s.insert(4);
-  EXPECT_TRUE(absl::c_includes(s, vector_));
-}
-
-TEST_F(NonMutatingTest, IncludesWithPredicate) {
-  std::vector<int> v = {3, 2, 1};
-  std::set<int, std::greater<int>> s(v.begin(), v.end());
-  s.insert(4);
-  EXPECT_TRUE(absl::c_includes(s, v, std::greater<int>()));
-}
-
-class NumericMutatingTest : public testing::Test {
- protected:
-  std::list<int> list_ = {1, 2, 3};
-  std::vector<int> output_;
-};
-
-TEST_F(NumericMutatingTest, Iota) {
-  absl::c_iota(list_, 5);
-  std::list<int> expected{5, 6, 7};
-  EXPECT_EQ(list_, expected);
-}
-
-TEST_F(NonMutatingTest, Accumulate) {
-  EXPECT_EQ(absl::c_accumulate(sequence_, 4), 1 + 2 + 3 + 4);
-}
-
-TEST_F(NonMutatingTest, AccumulateWithBinaryOp) {
-  EXPECT_EQ(absl::c_accumulate(sequence_, 4, std::multiplies<int>()),
-            1 * 2 * 3 * 4);
-}
-
-TEST_F(NonMutatingTest, AccumulateLvalueInit) {
-  int lvalue = 4;
-  EXPECT_EQ(absl::c_accumulate(sequence_, lvalue), 1 + 2 + 3 + 4);
-}
-
-TEST_F(NonMutatingTest, AccumulateWithBinaryOpLvalueInit) {
-  int lvalue = 4;
-  EXPECT_EQ(absl::c_accumulate(sequence_, lvalue, std::multiplies<int>()),
-            1 * 2 * 3 * 4);
-}
-
-TEST_F(NonMutatingTest, InnerProduct) {
-  EXPECT_EQ(absl::c_inner_product(sequence_, vector_, 1000),
-            1000 + 1 * 1 + 2 * 2 + 3 * 3);
-}
-
-TEST_F(NonMutatingTest, InnerProductWithBinaryOps) {
-  EXPECT_EQ(absl::c_inner_product(sequence_, vector_, 10,
-                                  std::multiplies<int>(), std::plus<int>()),
-            10 * (1 + 1) * (2 + 2) * (3 + 3));
-}
-
-TEST_F(NonMutatingTest, InnerProductLvalueInit) {
-  int lvalue = 1000;
-  EXPECT_EQ(absl::c_inner_product(sequence_, vector_, lvalue),
-            1000 + 1 * 1 + 2 * 2 + 3 * 3);
-}
-
-TEST_F(NonMutatingTest, InnerProductWithBinaryOpsLvalueInit) {
-  int lvalue = 10;
-  EXPECT_EQ(absl::c_inner_product(sequence_, vector_, lvalue,
-                                  std::multiplies<int>(), std::plus<int>()),
-            10 * (1 + 1) * (2 + 2) * (3 + 3));
-}
-
-TEST_F(NumericMutatingTest, AdjacentDifference) {
-  auto last = absl::c_adjacent_difference(list_, std::back_inserter(output_));
-  *last = 1000;
-  std::vector<int> expected{1, 2 - 1, 3 - 2, 1000};
-  EXPECT_EQ(output_, expected);
-}
-
-TEST_F(NumericMutatingTest, AdjacentDifferenceWithBinaryOp) {
-  auto last = absl::c_adjacent_difference(list_, std::back_inserter(output_),
-                                          std::multiplies<int>());
-  *last = 1000;
-  std::vector<int> expected{1, 2 * 1, 3 * 2, 1000};
-  EXPECT_EQ(output_, expected);
-}
-
-TEST_F(NumericMutatingTest, PartialSum) {
-  auto last = absl::c_partial_sum(list_, std::back_inserter(output_));
-  *last = 1000;
-  std::vector<int> expected{1, 1 + 2, 1 + 2 + 3, 1000};
-  EXPECT_EQ(output_, expected);
-}
-
-TEST_F(NumericMutatingTest, PartialSumWithBinaryOp) {
-  auto last = absl::c_partial_sum(list_, std::back_inserter(output_),
-                                  std::multiplies<int>());
-  *last = 1000;
-  std::vector<int> expected{1, 1 * 2, 1 * 2 * 3, 1000};
-  EXPECT_EQ(output_, expected);
-}
-
-TEST_F(NonMutatingTest, LinearSearch) {
-  EXPECT_TRUE(absl::c_linear_search(container_, 3));
-  EXPECT_FALSE(absl::c_linear_search(container_, 4));
-}
-
-TEST_F(NonMutatingTest, AllOf) {
-  const std::vector<int>& v = vector_;
-  EXPECT_FALSE(absl::c_all_of(v, [](int x) { return x > 1; }));
-  EXPECT_TRUE(absl::c_all_of(v, [](int x) { return x > 0; }));
-}
-
-TEST_F(NonMutatingTest, AnyOf) {
-  const std::vector<int>& v = vector_;
-  EXPECT_TRUE(absl::c_any_of(v, [](int x) { return x > 2; }));
-  EXPECT_FALSE(absl::c_any_of(v, [](int x) { return x > 5; }));
-}
-
-TEST_F(NonMutatingTest, NoneOf) {
-  const std::vector<int>& v = vector_;
-  EXPECT_FALSE(absl::c_none_of(v, [](int x) { return x > 2; }));
-  EXPECT_TRUE(absl::c_none_of(v, [](int x) { return x > 5; }));
-}
-
-TEST_F(NonMutatingTest, MinMaxElementLess) {
-  std::pair<std::vector<int>::const_iterator, std::vector<int>::const_iterator>
-      p = absl::c_minmax_element(vector_, std::less<int>());
-  EXPECT_TRUE(p.first == vector_.begin());
-  EXPECT_TRUE(p.second == vector_.begin() + 2);
-}
-
-TEST_F(NonMutatingTest, MinMaxElementGreater) {
-  std::pair<std::vector<int>::const_iterator, std::vector<int>::const_iterator>
-      p = absl::c_minmax_element(vector_, std::greater<int>());
-  EXPECT_TRUE(p.first == vector_.begin() + 2);
-  EXPECT_TRUE(p.second == vector_.begin());
-}
-
-TEST_F(NonMutatingTest, MinMaxElementNoPredicate) {
-  std::pair<std::vector<int>::const_iterator, std::vector<int>::const_iterator>
-      p = absl::c_minmax_element(vector_);
-  EXPECT_TRUE(p.first == vector_.begin());
-  EXPECT_TRUE(p.second == vector_.begin() + 2);
-}
-
-class SortingTest : public testing::Test {
- protected:
-  std::list<int> sorted_ = {1, 2, 3, 4};
-  std::list<int> unsorted_ = {2, 4, 1, 3};
-  std::list<int> reversed_ = {4, 3, 2, 1};
-};
-
-TEST_F(SortingTest, IsSorted) {
-  EXPECT_TRUE(absl::c_is_sorted(sorted_));
-  EXPECT_FALSE(absl::c_is_sorted(unsorted_));
-  EXPECT_FALSE(absl::c_is_sorted(reversed_));
-}
-
-TEST_F(SortingTest, IsSortedWithPredicate) {
-  EXPECT_FALSE(absl::c_is_sorted(sorted_, std::greater<int>()));
-  EXPECT_FALSE(absl::c_is_sorted(unsorted_, std::greater<int>()));
-  EXPECT_TRUE(absl::c_is_sorted(reversed_, std::greater<int>()));
-}
-
-TEST_F(SortingTest, IsSortedUntil) {
-  EXPECT_EQ(1, *absl::c_is_sorted_until(unsorted_));
-  EXPECT_EQ(4, *absl::c_is_sorted_until(unsorted_, std::greater<int>()));
-}
-
-TEST_F(SortingTest, NthElement) {
-  std::vector<int> unsorted = {2, 4, 1, 3};
-  absl::c_nth_element(unsorted, unsorted.begin() + 2);
-  EXPECT_THAT(unsorted, ElementsAre(Lt(3), Lt(3), 3, Gt(3)));
-  absl::c_nth_element(unsorted, unsorted.begin() + 2, std::greater<int>());
-  EXPECT_THAT(unsorted, ElementsAre(Gt(2), Gt(2), 2, Lt(2)));
-}
-
-TEST(MutatingTest, IsPartitioned) {
-  EXPECT_TRUE(
-      absl::c_is_partitioned(std::vector<int>{1, 3, 5, 2, 4, 6}, IsOdd));
-  EXPECT_FALSE(
-      absl::c_is_partitioned(std::vector<int>{1, 2, 3, 4, 5, 6}, IsOdd));
-  EXPECT_FALSE(
-      absl::c_is_partitioned(std::vector<int>{2, 4, 6, 1, 3, 5}, IsOdd));
-}
-
-TEST(MutatingTest, Partition) {
-  std::vector<int> actual = {1, 2, 3, 4, 5};
-  absl::c_partition(actual, IsOdd);
-  EXPECT_THAT(actual, Truly([](const std::vector<int>& c) {
-                return absl::c_is_partitioned(c, IsOdd);
-              }));
-}
-
-TEST(MutatingTest, StablePartition) {
-  std::vector<int> actual = {1, 2, 3, 4, 5};
-  absl::c_stable_partition(actual, IsOdd);
-  EXPECT_THAT(actual, ElementsAre(1, 3, 5, 2, 4));
-}
-
-TEST(MutatingTest, PartitionCopy) {
-  const std::vector<int> initial = {1, 2, 3, 4, 5};
-  std::vector<int> odds, evens;
-  auto ends = absl::c_partition_copy(initial, back_inserter(odds),
-                                     back_inserter(evens), IsOdd);
-  *ends.first = 7;
-  *ends.second = 6;
-  EXPECT_THAT(odds, ElementsAre(1, 3, 5, 7));
-  EXPECT_THAT(evens, ElementsAre(2, 4, 6));
-}
-
-TEST(MutatingTest, PartitionPoint) {
-  const std::vector<int> initial = {1, 3, 5, 2, 4};
-  auto middle = absl::c_partition_point(initial, IsOdd);
-  EXPECT_EQ(2, *middle);
-}
-
-TEST(MutatingTest, CopyMiddle) {
-  const std::vector<int> initial = {4, -1, -2, -3, 5};
-  const std::list<int> input = {1, 2, 3};
-  const std::vector<int> expected = {4, 1, 2, 3, 5};
-
-  std::list<int> test_list(initial.begin(), initial.end());
-  absl::c_copy(input, ++test_list.begin());
-  EXPECT_EQ(std::list<int>(expected.begin(), expected.end()), test_list);
-
-  std::vector<int> test_vector = initial;
-  absl::c_copy(input, test_vector.begin() + 1);
-  EXPECT_EQ(expected, test_vector);
-}
-
-TEST(MutatingTest, CopyFrontInserter) {
-  const std::list<int> initial = {4, 5};
-  const std::list<int> input = {1, 2, 3};
-  const std::list<int> expected = {3, 2, 1, 4, 5};
-
-  std::list<int> test_list = initial;
-  absl::c_copy(input, std::front_inserter(test_list));
-  EXPECT_EQ(expected, test_list);
-}
-
-TEST(MutatingTest, CopyBackInserter) {
-  const std::vector<int> initial = {4, 5};
-  const std::list<int> input = {1, 2, 3};
-  const std::vector<int> expected = {4, 5, 1, 2, 3};
-
-  std::list<int> test_list(initial.begin(), initial.end());
-  absl::c_copy(input, std::back_inserter(test_list));
-  EXPECT_EQ(std::list<int>(expected.begin(), expected.end()), test_list);
-
-  std::vector<int> test_vector = initial;
-  absl::c_copy(input, std::back_inserter(test_vector));
-  EXPECT_EQ(expected, test_vector);
-}
-
-TEST(MutatingTest, CopyN) {
-  const std::vector<int> initial = {1, 2, 3, 4, 5};
-  const std::vector<int> expected = {1, 2};
-  std::vector<int> actual;
-  absl::c_copy_n(initial, 2, back_inserter(actual));
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(MutatingTest, CopyIf) {
-  const std::list<int> input = {1, 2, 3};
-  std::vector<int> output;
-  absl::c_copy_if(input, std::back_inserter(output),
-                  [](int i) { return i != 2; });
-  EXPECT_THAT(output, ElementsAre(1, 3));
-}
-
-TEST(MutatingTest, CopyBackward) {
-  std::vector<int> actual = {1, 2, 3, 4, 5};
-  std::vector<int> expected = {1, 2, 1, 2, 3};
-  absl::c_copy_backward(absl::MakeSpan(actual.data(), 3), actual.end());
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(MutatingTest, Move) {
-  std::vector<std::unique_ptr<int>> src;
-  src.emplace_back(absl::make_unique<int>(1));
-  src.emplace_back(absl::make_unique<int>(2));
-  src.emplace_back(absl::make_unique<int>(3));
-  src.emplace_back(absl::make_unique<int>(4));
-  src.emplace_back(absl::make_unique<int>(5));
-
-  std::vector<std::unique_ptr<int>> dest = {};
-  absl::c_move(src, std::back_inserter(dest));
-  EXPECT_THAT(src, Each(IsNull()));
-  EXPECT_THAT(dest, ElementsAre(Pointee(1), Pointee(2), Pointee(3), Pointee(4),
-                                Pointee(5)));
-}
-
-TEST(MutatingTest, MoveBackward) {
-  std::vector<std::unique_ptr<int>> actual;
-  actual.emplace_back(absl::make_unique<int>(1));
-  actual.emplace_back(absl::make_unique<int>(2));
-  actual.emplace_back(absl::make_unique<int>(3));
-  actual.emplace_back(absl::make_unique<int>(4));
-  actual.emplace_back(absl::make_unique<int>(5));
-  auto subrange = absl::MakeSpan(actual.data(), 3);
-  absl::c_move_backward(subrange, actual.end());
-  EXPECT_THAT(actual, ElementsAre(IsNull(), IsNull(), Pointee(1), Pointee(2),
-                                  Pointee(3)));
-}
-
-TEST(MutatingTest, MoveWithRvalue) {
-  auto MakeRValueSrc = [] {
-    std::vector<std::unique_ptr<int>> src;
-    src.emplace_back(absl::make_unique<int>(1));
-    src.emplace_back(absl::make_unique<int>(2));
-    src.emplace_back(absl::make_unique<int>(3));
-    return src;
-  };
-
-  std::vector<std::unique_ptr<int>> dest = MakeRValueSrc();
-  absl::c_move(MakeRValueSrc(), std::back_inserter(dest));
-  EXPECT_THAT(dest, ElementsAre(Pointee(1), Pointee(2), Pointee(3), Pointee(1),
-                                Pointee(2), Pointee(3)));
-}
-
-TEST(MutatingTest, SwapRanges) {
-  std::vector<int> odds = {2, 4, 6};
-  std::vector<int> evens = {1, 3, 5};
-  absl::c_swap_ranges(odds, evens);
-  EXPECT_THAT(odds, ElementsAre(1, 3, 5));
-  EXPECT_THAT(evens, ElementsAre(2, 4, 6));
-
-  odds.pop_back();
-  absl::c_swap_ranges(odds, evens);
-  EXPECT_THAT(odds, ElementsAre(2, 4));
-  EXPECT_THAT(evens, ElementsAre(1, 3, 6));
-
-  absl::c_swap_ranges(evens, odds);
-  EXPECT_THAT(odds, ElementsAre(1, 3));
-  EXPECT_THAT(evens, ElementsAre(2, 4, 6));
-}
-
-TEST_F(NonMutatingTest, Transform) {
-  std::vector<int> x{0, 2, 4}, y, z;
-  auto end = absl::c_transform(x, back_inserter(y), std::negate<int>());
-  EXPECT_EQ(std::vector<int>({0, -2, -4}), y);
-  *end = 7;
-  EXPECT_EQ(std::vector<int>({0, -2, -4, 7}), y);
-
-  y = {1, 3, 0};
-  end = absl::c_transform(x, y, back_inserter(z), std::plus<int>());
-  EXPECT_EQ(std::vector<int>({1, 5, 4}), z);
-  *end = 7;
-  EXPECT_EQ(std::vector<int>({1, 5, 4, 7}), z);
-
-  z.clear();
-  y.pop_back();
-  end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>());
-  EXPECT_EQ(std::vector<int>({1, 5}), z);
-  *end = 7;
-  EXPECT_EQ(std::vector<int>({1, 5, 7}), z);
-
-  z.clear();
-  std::swap(x, y);
-  end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>());
-  EXPECT_EQ(std::vector<int>({1, 5}), z);
-  *end = 7;
-  EXPECT_EQ(std::vector<int>({1, 5, 7}), z);
-}
-
-TEST(MutatingTest, Replace) {
-  const std::vector<int> initial = {1, 2, 3, 1, 4, 5};
-  const std::vector<int> expected = {4, 2, 3, 4, 4, 5};
-
-  std::vector<int> test_vector = initial;
-  absl::c_replace(test_vector, 1, 4);
-  EXPECT_EQ(expected, test_vector);
-
-  std::list<int> test_list(initial.begin(), initial.end());
-  absl::c_replace(test_list, 1, 4);
-  EXPECT_EQ(std::list<int>(expected.begin(), expected.end()), test_list);
-}
-
-TEST(MutatingTest, ReplaceIf) {
-  std::vector<int> actual = {1, 2, 3, 4, 5};
-  const std::vector<int> expected = {0, 2, 0, 4, 0};
-
-  absl::c_replace_if(actual, IsOdd, 0);
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(MutatingTest, ReplaceCopy) {
-  const std::vector<int> initial = {1, 2, 3, 1, 4, 5};
-  const std::vector<int> expected = {4, 2, 3, 4, 4, 5};
-
-  std::vector<int> actual;
-  absl::c_replace_copy(initial, back_inserter(actual), 1, 4);
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(MutatingTest, Sort) {
-  std::vector<int> test_vector = {2, 3, 1, 4};
-  absl::c_sort(test_vector);
-  EXPECT_THAT(test_vector, ElementsAre(1, 2, 3, 4));
-}
-
-TEST(MutatingTest, SortWithPredicate) {
-  std::vector<int> test_vector = {2, 3, 1, 4};
-  absl::c_sort(test_vector, std::greater<int>());
-  EXPECT_THAT(test_vector, ElementsAre(4, 3, 2, 1));
-}
-
-// For absl::c_stable_sort tests. Needs an operator< that does not cover all
-// fields so that the test can check the sort preserves order of equal elements.
-struct Element {
-  int key;
-  int value;
-  friend bool operator<(const Element& e1, const Element& e2) {
-    return e1.key < e2.key;
-  }
-  // Make gmock print useful diagnostics.
-  friend std::ostream& operator<<(std::ostream& o, const Element& e) {
-    return o << "{" << e.key << ", " << e.value << "}";
-  }
-};
-
-MATCHER_P2(IsElement, key, value, "") {
-  return arg.key == key && arg.value == value;
-}
-
-TEST(MutatingTest, StableSort) {
-  std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
-  absl::c_stable_sort(test_vector);
-  EXPECT_THAT(test_vector,
-              ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1),
-                          IsElement(2, 0), IsElement(2, 2)));
-}
-
-TEST(MutatingTest, StableSortWithPredicate) {
-  std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
-  absl::c_stable_sort(test_vector, [](const Element& e1, const Element& e2) {
-    return e2 < e1;
-  });
-  EXPECT_THAT(test_vector,
-              ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2),
-                          IsElement(1, 1), IsElement(1, 0)));
-}
-
-TEST(MutatingTest, ReplaceCopyIf) {
-  const std::vector<int> initial = {1, 2, 3, 4, 5};
-  const std::vector<int> expected = {0, 2, 0, 4, 0};
-
-  std::vector<int> actual;
-  absl::c_replace_copy_if(initial, back_inserter(actual), IsOdd, 0);
-  EXPECT_EQ(expected, actual);
-}
-
-TEST(MutatingTest, Fill) {
-  std::vector<int> actual(5);
-  absl::c_fill(actual, 1);
-  EXPECT_THAT(actual, ElementsAre(1, 1, 1, 1, 1));
-}
-
-TEST(MutatingTest, FillN) {
-  std::vector<int> actual(5, 0);
-  absl::c_fill_n(actual, 2, 1);
-  EXPECT_THAT(actual, ElementsAre(1, 1, 0, 0, 0));
-}
-
-TEST(MutatingTest, Generate) {
-  std::vector<int> actual(5);
-  int x = 0;
-  absl::c_generate(actual, [&x]() { return ++x; });
-  EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5));
-}
-
-TEST(MutatingTest, GenerateN) {
-  std::vector<int> actual(5, 0);
-  int x = 0;
-  absl::c_generate_n(actual, 3, [&x]() { return ++x; });
-  EXPECT_THAT(actual, ElementsAre(1, 2, 3, 0, 0));
-}
-
-TEST(MutatingTest, RemoveCopy) {
-  std::vector<int> actual;
-  absl::c_remove_copy(std::vector<int>{1, 2, 3}, back_inserter(actual), 2);
-  EXPECT_THAT(actual, ElementsAre(1, 3));
-}
-
-TEST(MutatingTest, RemoveCopyIf) {
-  std::vector<int> actual;
-  absl::c_remove_copy_if(std::vector<int>{1, 2, 3}, back_inserter(actual),
-                         IsOdd);
-  EXPECT_THAT(actual, ElementsAre(2));
-}
-
-TEST(MutatingTest, UniqueCopy) {
-  std::vector<int> actual;
-  absl::c_unique_copy(std::vector<int>{1, 2, 2, 2, 3, 3, 2},
-                      back_inserter(actual));
-  EXPECT_THAT(actual, ElementsAre(1, 2, 3, 2));
-}
-
-TEST(MutatingTest, UniqueCopyWithPredicate) {
-  std::vector<int> actual;
-  absl::c_unique_copy(std::vector<int>{1, 2, 3, -1, -2, -3, 1},
-                      back_inserter(actual),
-                      [](int x, int y) { return (x < 0) == (y < 0); });
-  EXPECT_THAT(actual, ElementsAre(1, -1, 1));
-}
-
-TEST(MutatingTest, Reverse) {
-  std::vector<int> test_vector = {1, 2, 3, 4};
-  absl::c_reverse(test_vector);
-  EXPECT_THAT(test_vector, ElementsAre(4, 3, 2, 1));
-
-  std::list<int> test_list = {1, 2, 3, 4};
-  absl::c_reverse(test_list);
-  EXPECT_THAT(test_list, ElementsAre(4, 3, 2, 1));
-}
-
-TEST(MutatingTest, ReverseCopy) {
-  std::vector<int> actual;
-  absl::c_reverse_copy(std::vector<int>{1, 2, 3, 4}, back_inserter(actual));
-  EXPECT_THAT(actual, ElementsAre(4, 3, 2, 1));
-}
-
-TEST(MutatingTest, Rotate) {
-  std::vector<int> actual = {1, 2, 3, 4};
-  auto it = absl::c_rotate(actual, actual.begin() + 2);
-  EXPECT_THAT(actual, testing::ElementsAreArray({3, 4, 1, 2}));
-  EXPECT_EQ(*it, 1);
-}
-
-TEST(MutatingTest, RotateCopy) {
-  std::vector<int> initial = {1, 2, 3, 4};
-  std::vector<int> actual;
-  auto end =
-      absl::c_rotate_copy(initial, initial.begin() + 2, back_inserter(actual));
-  *end = 5;
-  EXPECT_THAT(actual, ElementsAre(3, 4, 1, 2, 5));
-}
-
-TEST(MutatingTest, Shuffle) {
-  std::vector<int> actual = {1, 2, 3, 4, 5};
-  absl::c_shuffle(actual, std::random_device());
-  EXPECT_THAT(actual, UnorderedElementsAre(1, 2, 3, 4, 5));
-}
-
-TEST(MutatingTest, PartialSort) {
-  std::vector<int> sequence{5, 3, 42, 0};
-  absl::c_partial_sort(sequence, sequence.begin() + 2);
-  EXPECT_THAT(absl::MakeSpan(sequence.data(), 2), ElementsAre(0, 3));
-  absl::c_partial_sort(sequence, sequence.begin() + 2, std::greater<int>());
-  EXPECT_THAT(absl::MakeSpan(sequence.data(), 2), ElementsAre(42, 5));
-}
-
-TEST(MutatingTest, PartialSortCopy) {
-  const std::vector<int> initial = {5, 3, 42, 0};
-  std::vector<int> actual(2);
-  absl::c_partial_sort_copy(initial, actual);
-  EXPECT_THAT(actual, ElementsAre(0, 3));
-  absl::c_partial_sort_copy(initial, actual, std::greater<int>());
-  EXPECT_THAT(actual, ElementsAre(42, 5));
-}
-
-TEST(MutatingTest, Merge) {
-  std::vector<int> actual;
-  absl::c_merge(std::vector<int>{1, 3, 5}, std::vector<int>{2, 4},
-                back_inserter(actual));
-  EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5));
-}
-
-TEST(MutatingTest, MergeWithComparator) {
-  std::vector<int> actual;
-  absl::c_merge(std::vector<int>{5, 3, 1}, std::vector<int>{4, 2},
-                back_inserter(actual), std::greater<int>());
-  EXPECT_THAT(actual, ElementsAre(5, 4, 3, 2, 1));
-}
-
-TEST(MutatingTest, InplaceMerge) {
-  std::vector<int> actual = {1, 3, 5, 2, 4};
-  absl::c_inplace_merge(actual, actual.begin() + 3);
-  EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5));
-}
-
-TEST(MutatingTest, InplaceMergeWithComparator) {
-  std::vector<int> actual = {5, 3, 1, 4, 2};
-  absl::c_inplace_merge(actual, actual.begin() + 3, std::greater<int>());
-  EXPECT_THAT(actual, ElementsAre(5, 4, 3, 2, 1));
-}
-
-class SetOperationsTest : public testing::Test {
- protected:
-  std::vector<int> a_ = {1, 2, 3};
-  std::vector<int> b_ = {1, 3, 5};
-
-  std::vector<int> a_reversed_ = {3, 2, 1};
-  std::vector<int> b_reversed_ = {5, 3, 1};
-};
-
-TEST_F(SetOperationsTest, SetUnion) {
-  std::vector<int> actual;
-  absl::c_set_union(a_, b_, back_inserter(actual));
-  EXPECT_THAT(actual, ElementsAre(1, 2, 3, 5));
-}
-
-TEST_F(SetOperationsTest, SetUnionWithComparator) {
-  std::vector<int> actual;
-  absl::c_set_union(a_reversed_, b_reversed_, back_inserter(actual),
-                    std::greater<int>());
-  EXPECT_THAT(actual, ElementsAre(5, 3, 2, 1));
-}
-
-TEST_F(SetOperationsTest, SetIntersection) {
-  std::vector<int> actual;
-  absl::c_set_intersection(a_, b_, back_inserter(actual));
-  EXPECT_THAT(actual, ElementsAre(1, 3));
-}
-
-TEST_F(SetOperationsTest, SetIntersectionWithComparator) {
-  std::vector<int> actual;
-  absl::c_set_intersection(a_reversed_, b_reversed_, back_inserter(actual),
-                           std::greater<int>());
-  EXPECT_THAT(actual, ElementsAre(3, 1));
-}
-
-TEST_F(SetOperationsTest, SetDifference) {
-  std::vector<int> actual;
-  absl::c_set_difference(a_, b_, back_inserter(actual));
-  EXPECT_THAT(actual, ElementsAre(2));
-}
-
-TEST_F(SetOperationsTest, SetDifferenceWithComparator) {
-  std::vector<int> actual;
-  absl::c_set_difference(a_reversed_, b_reversed_, back_inserter(actual),
-                         std::greater<int>());
-  EXPECT_THAT(actual, ElementsAre(2));
-}
-
-TEST_F(SetOperationsTest, SetSymmetricDifference) {
-  std::vector<int> actual;
-  absl::c_set_symmetric_difference(a_, b_, back_inserter(actual));
-  EXPECT_THAT(actual, ElementsAre(2, 5));
-}
-
-TEST_F(SetOperationsTest, SetSymmetricDifferenceWithComparator) {
-  std::vector<int> actual;
-  absl::c_set_symmetric_difference(a_reversed_, b_reversed_,
-                                   back_inserter(actual), std::greater<int>());
-  EXPECT_THAT(actual, ElementsAre(5, 2));
-}
-
-TEST(HeapOperationsTest, WithoutComparator) {
-  std::vector<int> heap = {1, 2, 3};
-  EXPECT_FALSE(absl::c_is_heap(heap));
-  absl::c_make_heap(heap);
-  EXPECT_TRUE(absl::c_is_heap(heap));
-  heap.push_back(4);
-  EXPECT_EQ(3, absl::c_is_heap_until(heap) - heap.begin());
-  absl::c_push_heap(heap);
-  EXPECT_EQ(4, heap[0]);
-  absl::c_pop_heap(heap);
-  EXPECT_EQ(4, heap[3]);
-  absl::c_make_heap(heap);
-  absl::c_sort_heap(heap);
-  EXPECT_THAT(heap, ElementsAre(1, 2, 3, 4));
-  EXPECT_FALSE(absl::c_is_heap(heap));
-}
-
-TEST(HeapOperationsTest, WithComparator) {
-  using greater = std::greater<int>;
-  std::vector<int> heap = {3, 2, 1};
-  EXPECT_FALSE(absl::c_is_heap(heap, greater()));
-  absl::c_make_heap(heap, greater());
-  EXPECT_TRUE(absl::c_is_heap(heap, greater()));
-  heap.push_back(0);
-  EXPECT_EQ(3, absl::c_is_heap_until(heap, greater()) - heap.begin());
-  absl::c_push_heap(heap, greater());
-  EXPECT_EQ(0, heap[0]);
-  absl::c_pop_heap(heap, greater());
-  EXPECT_EQ(0, heap[3]);
-  absl::c_make_heap(heap, greater());
-  absl::c_sort_heap(heap, greater());
-  EXPECT_THAT(heap, ElementsAre(3, 2, 1, 0));
-  EXPECT_FALSE(absl::c_is_heap(heap, greater()));
-}
-
-TEST(MutatingTest, PermutationOperations) {
-  std::vector<int> initial = {1, 2, 3, 4};
-  std::vector<int> permuted = initial;
-
-  absl::c_next_permutation(permuted);
-  EXPECT_TRUE(absl::c_is_permutation(initial, permuted));
-  EXPECT_TRUE(absl::c_is_permutation(initial, permuted, std::equal_to<int>()));
-
-  std::vector<int> permuted2 = initial;
-  absl::c_prev_permutation(permuted2, std::greater<int>());
-  EXPECT_EQ(permuted, permuted2);
-
-  absl::c_prev_permutation(permuted);
-  EXPECT_EQ(initial, permuted);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/algorithm/equal_benchmark.cc b/third_party/abseil_cpp/absl/algorithm/equal_benchmark.cc
deleted file mode 100644
index 7bf62c9a7f..0000000000
--- a/third_party/abseil_cpp/absl/algorithm/equal_benchmark.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cstdint>
-#include <cstring>
-
-#include "benchmark/benchmark.h"
-#include "absl/algorithm/algorithm.h"
-
-namespace {
-
-// The range of sequence sizes to benchmark.
-constexpr int kMinBenchmarkSize = 1024;
-constexpr int kMaxBenchmarkSize = 8 * 1024 * 1024;
-
-// A user-defined type for use in equality benchmarks. Note that we expect
-// std::memcmp to win for this type: libstdc++'s std::equal only defers to
-// memcmp for integral types. This is because it is not straightforward to
-// guarantee that std::memcmp would produce a result "as-if" compared by
-// operator== for other types (example gotchas: NaN floats, structs with
-// padding).
-struct EightBits {
-  explicit EightBits(int /* unused */) : data(0) {}
-  bool operator==(const EightBits& rhs) const { return data == rhs.data; }
-  uint8_t data;
-};
-
-template <typename T>
-void BM_absl_equal_benchmark(benchmark::State& state) {
-  std::vector<T> xs(state.range(0), T(0));
-  std::vector<T> ys = xs;
-  while (state.KeepRunning()) {
-    const bool same = absl::equal(xs.begin(), xs.end(), ys.begin(), ys.end());
-    benchmark::DoNotOptimize(same);
-  }
-}
-
-template <typename T>
-void BM_std_equal_benchmark(benchmark::State& state) {
-  std::vector<T> xs(state.range(0), T(0));
-  std::vector<T> ys = xs;
-  while (state.KeepRunning()) {
-    const bool same = std::equal(xs.begin(), xs.end(), ys.begin());
-    benchmark::DoNotOptimize(same);
-  }
-}
-
-template <typename T>
-void BM_memcmp_benchmark(benchmark::State& state) {
-  std::vector<T> xs(state.range(0), T(0));
-  std::vector<T> ys = xs;
-  while (state.KeepRunning()) {
-    const bool same =
-        std::memcmp(xs.data(), ys.data(), xs.size() * sizeof(T)) == 0;
-    benchmark::DoNotOptimize(same);
-  }
-}
-
-// The expectation is that the compiler should be able to elide the equality
-// comparison altogether for sufficiently simple types.
-template <typename T>
-void BM_absl_equal_self_benchmark(benchmark::State& state) {
-  std::vector<T> xs(state.range(0), T(0));
-  while (state.KeepRunning()) {
-    const bool same = absl::equal(xs.begin(), xs.end(), xs.begin(), xs.end());
-    benchmark::DoNotOptimize(same);
-  }
-}
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint8_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint8_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint8_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint8_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint16_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint16_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint16_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint16_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint32_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint32_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint32_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint32_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint64_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint64_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint64_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint64_t)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, EightBits)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, EightBits)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, EightBits)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, EightBits)
-    ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/BUILD.bazel b/third_party/abseil_cpp/absl/base/BUILD.bazel
deleted file mode 100644
index 9d96abeb33..0000000000
--- a/third_party/abseil_cpp/absl/base/BUILD.bazel
+++ /dev/null
@@ -1,818 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "atomic_hook",
-    hdrs = ["internal/atomic_hook.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-        ":core_headers",
-    ],
-)
-
-cc_library(
-    name = "errno_saver",
-    hdrs = ["internal/errno_saver.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [":config"],
-)
-
-cc_library(
-    name = "log_severity",
-    srcs = ["log_severity.cc"],
-    hdrs = ["log_severity.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":core_headers",
-    ],
-)
-
-cc_library(
-    name = "raw_logging_internal",
-    srcs = ["internal/raw_logging.cc"],
-    hdrs = ["internal/raw_logging.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":atomic_hook",
-        ":config",
-        ":core_headers",
-        ":log_severity",
-    ],
-)
-
-cc_library(
-    name = "spinlock_wait",
-    srcs = [
-        "internal/spinlock_akaros.inc",
-        "internal/spinlock_linux.inc",
-        "internal/spinlock_posix.inc",
-        "internal/spinlock_wait.cc",
-        "internal/spinlock_win32.inc",
-    ],
-    hdrs = ["internal/spinlock_wait.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl/base:__pkg__",
-    ],
-    deps = [
-        ":base_internal",
-        ":core_headers",
-        ":errno_saver",
-    ],
-)
-
-cc_library(
-    name = "config",
-    hdrs = [
-        "config.h",
-        "options.h",
-        "policy_checks.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-)
-
-cc_library(
-    name = "dynamic_annotations",
-    srcs = [
-        "internal/dynamic_annotations.h",
-    ],
-    hdrs = [
-        "dynamic_annotations.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":core_headers",
-    ],
-)
-
-cc_library(
-    name = "core_headers",
-    srcs = [
-        "internal/thread_annotations.h",
-    ],
-    hdrs = [
-        "attributes.h",
-        "const_init.h",
-        "macros.h",
-        "optimization.h",
-        "port.h",
-        "thread_annotations.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-    ],
-)
-
-cc_library(
-    name = "malloc_internal",
-    srcs = [
-        "internal/low_level_alloc.cc",
-    ],
-    hdrs = [
-        "internal/direct_mmap.h",
-        "internal/low_level_alloc.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = select({
-        "//absl:windows": [],
-        "//absl:wasm": [],
-        "//conditions:default": ["-pthread"],
-    }) + ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//visibility:public",
-    ],
-    deps = [
-        ":base",
-        ":base_internal",
-        ":config",
-        ":core_headers",
-        ":dynamic_annotations",
-        ":raw_logging_internal",
-    ],
-)
-
-cc_library(
-    name = "base_internal",
-    hdrs = [
-        "internal/hide_ptr.h",
-        "internal/identity.h",
-        "internal/inline_variable.h",
-        "internal/invoke.h",
-        "internal/scheduling_mode.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_library(
-    name = "base",
-    srcs = [
-        "internal/cycleclock.cc",
-        "internal/spinlock.cc",
-        "internal/sysinfo.cc",
-        "internal/thread_identity.cc",
-        "internal/unscaledcycleclock.cc",
-    ],
-    hdrs = [
-        "call_once.h",
-        "casts.h",
-        "internal/cycleclock.h",
-        "internal/low_level_scheduling.h",
-        "internal/per_thread_tls.h",
-        "internal/spinlock.h",
-        "internal/sysinfo.h",
-        "internal/thread_identity.h",
-        "internal/tsan_mutex_interface.h",
-        "internal/unscaledcycleclock.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = select({
-        "//absl:windows": [
-            "-DEFAULTLIB:advapi32.lib",
-        ],
-        "//absl:wasm": [],
-        "//conditions:default": ["-pthread"],
-    }) + ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":atomic_hook",
-        ":base_internal",
-        ":config",
-        ":core_headers",
-        ":dynamic_annotations",
-        ":log_severity",
-        ":raw_logging_internal",
-        ":spinlock_wait",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_library(
-    name = "atomic_hook_test_helper",
-    testonly = 1,
-    srcs = ["internal/atomic_hook_test_helper.cc"],
-    hdrs = ["internal/atomic_hook_test_helper.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":atomic_hook",
-        ":core_headers",
-    ],
-)
-
-cc_test(
-    name = "atomic_hook_test",
-    size = "small",
-    srcs = ["internal/atomic_hook_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":atomic_hook",
-        ":atomic_hook_test_helper",
-        ":core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "bit_cast_test",
-    size = "small",
-    srcs = [
-        "bit_cast_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":base",
-        ":core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "throw_delegate",
-    srcs = ["internal/throw_delegate.cc"],
-    hdrs = ["internal/throw_delegate.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-        ":raw_logging_internal",
-    ],
-)
-
-cc_test(
-    name = "throw_delegate_test",
-    srcs = ["throw_delegate_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":throw_delegate",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "errno_saver_test",
-    size = "small",
-    srcs = ["internal/errno_saver_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":errno_saver",
-        ":strerror",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "exception_testing",
-    testonly = 1,
-    hdrs = ["internal/exception_testing.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "pretty_function",
-    hdrs = ["internal/pretty_function.h"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//absl:__subpackages__"],
-)
-
-cc_library(
-    name = "exception_safety_testing",
-    testonly = 1,
-    srcs = ["internal/exception_safety_testing.cc"],
-    hdrs = ["internal/exception_safety_testing.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":pretty_function",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "//absl/utility",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "exception_safety_testing_test",
-    srcs = ["exception_safety_testing_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":exception_safety_testing",
-        "//absl/memory",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "inline_variable_test",
-    size = "small",
-    srcs = [
-        "inline_variable_test.cc",
-        "inline_variable_test_a.cc",
-        "inline_variable_test_b.cc",
-        "internal/inline_variable_testing.h",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":base_internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "invoke_test",
-    size = "small",
-    srcs = ["invoke_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":base_internal",
-        "//absl/memory",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-# Common test library made available for use in non-absl code that overrides
-# AbslInternalSpinLockDelay and AbslInternalSpinLockWake.
-cc_library(
-    name = "spinlock_test_common",
-    testonly = 1,
-    srcs = ["spinlock_test_common.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":base",
-        ":base_internal",
-        ":config",
-        ":core_headers",
-        "//absl/synchronization",
-        "@com_google_googletest//:gtest",
-    ],
-    alwayslink = 1,
-)
-
-cc_test(
-    name = "spinlock_test",
-    size = "medium",
-    srcs = ["spinlock_test_common.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":base",
-        ":base_internal",
-        ":config",
-        ":core_headers",
-        "//absl/synchronization",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "spinlock_benchmark_common",
-    testonly = 1,
-    srcs = ["internal/spinlock_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl/base:__pkg__",
-    ],
-    deps = [
-        ":base",
-        ":base_internal",
-        ":raw_logging_internal",
-        "//absl/synchronization",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-    alwayslink = 1,
-)
-
-cc_binary(
-    name = "spinlock_benchmark",
-    testonly = 1,
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":spinlock_benchmark_common",
-    ],
-)
-
-cc_library(
-    name = "endian",
-    hdrs = [
-        "internal/endian.h",
-        "internal/unaligned_access.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":core_headers",
-    ],
-)
-
-cc_test(
-    name = "endian_test",
-    srcs = ["internal/endian_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":config",
-        ":endian",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "config_test",
-    srcs = ["config_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        "//absl/synchronization:thread_pool",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "call_once_test",
-    srcs = ["call_once_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":base",
-        ":core_headers",
-        "//absl/synchronization",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "raw_logging_test",
-    srcs = ["raw_logging_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":raw_logging_internal",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "sysinfo_test",
-    size = "small",
-    srcs = ["internal/sysinfo_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":base",
-        "//absl/synchronization",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "low_level_alloc_test",
-    size = "medium",
-    srcs = ["internal/low_level_alloc_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["no_test_ios_x86_64"],
-    deps = [
-        ":malloc_internal",
-        "//absl/container:node_hash_map",
-    ],
-)
-
-cc_test(
-    name = "thread_identity_test",
-    size = "small",
-    srcs = ["internal/thread_identity_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":base",
-        ":core_headers",
-        "//absl/synchronization",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "thread_identity_benchmark",
-    srcs = ["internal/thread_identity_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":base",
-        "//absl/synchronization",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_library(
-    name = "bits",
-    hdrs = ["internal/bits.h"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-        ":core_headers",
-    ],
-)
-
-cc_test(
-    name = "bits_test",
-    size = "small",
-    srcs = ["internal/bits_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":bits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "exponential_biased",
-    srcs = ["internal/exponential_biased.cc"],
-    hdrs = ["internal/exponential_biased.h"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-        ":core_headers",
-    ],
-)
-
-cc_test(
-    name = "exponential_biased_test",
-    size = "small",
-    srcs = ["internal/exponential_biased_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":exponential_biased",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "periodic_sampler",
-    srcs = ["internal/periodic_sampler.cc"],
-    hdrs = ["internal/periodic_sampler.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":core_headers",
-        ":exponential_biased",
-    ],
-)
-
-cc_test(
-    name = "periodic_sampler_test",
-    size = "small",
-    srcs = ["internal/periodic_sampler_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":core_headers",
-        ":periodic_sampler",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_binary(
-    name = "periodic_sampler_benchmark",
-    testonly = 1,
-    srcs = ["internal/periodic_sampler_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":core_headers",
-        ":periodic_sampler",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_library(
-    name = "scoped_set_env",
-    testonly = 1,
-    srcs = ["internal/scoped_set_env.cc"],
-    hdrs = ["internal/scoped_set_env.h"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-        ":raw_logging_internal",
-    ],
-)
-
-cc_test(
-    name = "scoped_set_env_test",
-    size = "small",
-    srcs = ["internal/scoped_set_env_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":scoped_set_env",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "log_severity_test",
-    size = "small",
-    srcs = ["log_severity_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":log_severity",
-        "//absl/flags:flag_internal",
-        "//absl/flags:marshalling",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "strerror",
-    srcs = ["internal/strerror.cc"],
-    hdrs = ["internal/strerror.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-        ":core_headers",
-        ":errno_saver",
-    ],
-)
-
-cc_test(
-    name = "strerror_test",
-    size = "small",
-    srcs = ["internal/strerror_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":strerror",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_binary(
-    name = "strerror_benchmark",
-    testonly = 1,
-    srcs = ["internal/strerror_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strerror",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_library(
-    name = "fast_type_id",
-    hdrs = ["internal/fast_type_id.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":config",
-    ],
-)
-
-cc_test(
-    name = "fast_type_id_test",
-    size = "small",
-    srcs = ["internal/fast_type_id_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":fast_type_id",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "unique_small_name_test",
-    size = "small",
-    srcs = ["internal/unique_small_name_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    linkstatic = 1,
-    deps = [
-        ":core_headers",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "optimization_test",
-    size = "small",
-    srcs = ["optimization_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":core_headers",
-        "//absl/types:optional",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/base/CMakeLists.txt b/third_party/abseil_cpp/absl/base/CMakeLists.txt
deleted file mode 100644
index 9ff5aa243c..0000000000
--- a/third_party/abseil_cpp/absl/base/CMakeLists.txt
+++ /dev/null
@@ -1,717 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-find_library(LIBRT rt)
-
-absl_cc_library(
-  NAME
-    atomic_hook
-  HDRS
-    "internal/atomic_hook.h"
-  DEPS
-    absl::config
-    absl::core_headers
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-)
-
-absl_cc_library(
-  NAME
-    errno_saver
-  HDRS
-    "internal/errno_saver.h"
-  DEPS
-    absl::config
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-)
-
-absl_cc_library(
-  NAME
-    log_severity
-  HDRS
-    "log_severity.h"
-  SRCS
-    "log_severity.cc"
-  DEPS
-    absl::core_headers
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-)
-
-absl_cc_library(
-  NAME
-    raw_logging_internal
-  HDRS
-    "internal/raw_logging.h"
-  SRCS
-    "internal/raw_logging.cc"
-  DEPS
-    absl::atomic_hook
-    absl::config
-    absl::core_headers
-    absl::log_severity
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-)
-
-absl_cc_library(
-  NAME
-    spinlock_wait
-  HDRS
-    "internal/spinlock_wait.h"
-  SRCS
-    "internal/spinlock_akaros.inc"
-    "internal/spinlock_linux.inc"
-    "internal/spinlock_posix.inc"
-    "internal/spinlock_wait.cc"
-    "internal/spinlock_win32.inc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base_internal
-    absl::core_headers
-    absl::errno_saver
-)
-
-absl_cc_library(
-  NAME
-    config
-  HDRS
-    "config.h"
-    "options.h"
-    "policy_checks.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    dynamic_annotations
-  HDRS
-    "dynamic_annotations.h"
-  SRCS
-    "internal/dynamic_annotations.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    core_headers
-  HDRS
-    "attributes.h"
-    "const_init.h"
-    "macros.h"
-    "optimization.h"
-    "port.h"
-    "thread_annotations.h"
-    "internal/thread_annotations.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    malloc_internal
-  HDRS
-    "internal/direct_mmap.h"
-    "internal/low_level_alloc.h"
-  SRCS
-    "internal/low_level_alloc.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base
-    absl::base_internal
-    absl::config
-    absl::core_headers
-    absl::dynamic_annotations
-    absl::raw_logging_internal
-    Threads::Threads
-)
-
-absl_cc_library(
-  NAME
-    base_internal
-  HDRS
-    "internal/hide_ptr.h"
-    "internal/identity.h"
-    "internal/inline_variable.h"
-    "internal/invoke.h"
-    "internal/scheduling_mode.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::type_traits
-)
-
-absl_cc_library(
-  NAME
-    base
-  HDRS
-    "call_once.h"
-    "casts.h"
-    "internal/cycleclock.h"
-    "internal/low_level_scheduling.h"
-    "internal/per_thread_tls.h"
-    "internal/spinlock.h"
-    "internal/sysinfo.h"
-    "internal/thread_identity.h"
-    "internal/tsan_mutex_interface.h"
-    "internal/unscaledcycleclock.h"
-  SRCS
-    "internal/cycleclock.cc"
-    "internal/spinlock.cc"
-    "internal/sysinfo.cc"
-    "internal/thread_identity.cc"
-    "internal/unscaledcycleclock.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-    $<$<BOOL:${LIBRT}>:-lrt>
-    $<$<BOOL:${MINGW}>:"advapi32">
-  DEPS
-    absl::atomic_hook
-    absl::base_internal
-    absl::config
-    absl::core_headers
-    absl::dynamic_annotations
-    absl::log_severity
-    absl::raw_logging_internal
-    absl::spinlock_wait
-    absl::type_traits
-    Threads::Threads
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    throw_delegate
-  HDRS
-    "internal/throw_delegate.h"
-  SRCS
-    "internal/throw_delegate.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::raw_logging_internal
-)
-
-absl_cc_library(
-  NAME
-    exception_testing
-  HDRS
-    "internal/exception_testing.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    gtest
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    pretty_function
-  HDRS
-    "internal/pretty_function.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-)
-
-absl_cc_library(
-  NAME
-    exception_safety_testing
-  HDRS
-    "internal/exception_safety_testing.h"
-  SRCS
-    "internal/exception_safety_testing.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::config
-    absl::pretty_function
-    absl::memory
-    absl::meta
-    absl::strings
-    absl::utility
-    gtest
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    absl_exception_safety_testing_test
-  SRCS
-    "exception_safety_testing_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::exception_safety_testing
-    absl::memory
-    gtest_main
-)
-
-absl_cc_library(
-  NAME
-    atomic_hook_test_helper
-  SRCS
-    "internal/atomic_hook_test_helper.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::atomic_hook
-    absl::core_headers
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    atomic_hook_test
-  SRCS
-    "internal/atomic_hook_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::atomic_hook_test_helper
-    absl::atomic_hook
-    absl::core_headers
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    bit_cast_test
-  SRCS
-    "bit_cast_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::core_headers
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    errno_saver_test
-  SRCS
-    "internal/errno_saver_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::errno_saver
-    absl::strerror
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    throw_delegate_test
-  SRCS
-    "throw_delegate_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::config
-    absl::throw_delegate
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    inline_variable_test
-  SRCS
-    "internal/inline_variable_testing.h"
-    "inline_variable_test.cc"
-    "inline_variable_test_a.cc"
-    "inline_variable_test_b.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base_internal
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    invoke_test
-  SRCS
-    "invoke_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base_internal
-    absl::memory
-    absl::strings
-    gmock
-    gtest_main
-)
-
-absl_cc_library(
-  NAME
-    spinlock_test_common
-  SRCS
-    "spinlock_test_common.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::config
-    absl::base_internal
-    absl::core_headers
-    absl::synchronization
-    gtest
-  TESTONLY
-)
-
-# On bazel BUILD this target use "alwayslink = 1" which is not implemented here
-absl_cc_test(
-  NAME
-    spinlock_test
-  SRCS
-    "spinlock_test_common.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::base_internal
-    absl::config
-    absl::core_headers
-    absl::synchronization
-    gtest_main
-)
-
-absl_cc_library(
-  NAME
-    endian
-  HDRS
-    "internal/endian.h"
-    "internal/unaligned_access.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    endian_test
-  SRCS
-    "internal/endian_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::config
-    absl::endian
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    config_test
-  SRCS
-    "config_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::config
-    absl::synchronization
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    call_once_test
-  SRCS
-    "call_once_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::core_headers
-    absl::synchronization
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    raw_logging_test
-  SRCS
-    "raw_logging_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::raw_logging_internal
-    absl::strings
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    sysinfo_test
-  SRCS
-    "internal/sysinfo_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::synchronization
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    low_level_alloc_test
-  SRCS
-    "internal/low_level_alloc_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::malloc_internal
-    absl::node_hash_map
-    Threads::Threads
-)
-
-absl_cc_test(
-  NAME
-    thread_identity_test
-  SRCS
-    "internal/thread_identity_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::core_headers
-    absl::synchronization
-    Threads::Threads
-    gtest_main
-)
-
-absl_cc_library(
-  NAME
-    bits
-  HDRS
-    "internal/bits.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-)
-
-absl_cc_test(
-  NAME
-    bits_test
-  SRCS
-    "internal/bits_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::bits
-    gtest_main
-)
-
-absl_cc_library(
-  NAME
-    exponential_biased
-  SRCS
-    "internal/exponential_biased.cc"
-  HDRS
-    "internal/exponential_biased.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-)
-
-absl_cc_test(
-  NAME
-    exponential_biased_test
-  SRCS
-    "internal/exponential_biased_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::exponential_biased
-    absl::strings
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    periodic_sampler
-  SRCS
-    "internal/periodic_sampler.cc"
-  HDRS
-    "internal/periodic_sampler.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::core_headers
-    absl::exponential_biased
-)
-
-absl_cc_test(
-  NAME
-    periodic_sampler_test
-  SRCS
-    "internal/periodic_sampler_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::core_headers
-    absl::periodic_sampler
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    scoped_set_env
-  SRCS
-    "internal/scoped_set_env.cc"
-  HDRS
-    "internal/scoped_set_env.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::raw_logging_internal
-)
-
-absl_cc_test(
-  NAME
-    scoped_set_env_test
-  SRCS
-    "internal/scoped_set_env_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::scoped_set_env
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    cmake_thread_test
-  SRCS
-    "internal/cmake_thread_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-)
-
-absl_cc_test(
-  NAME
-    log_severity_test
-  SRCS
-    "log_severity_test.cc"
-  DEPS
-    absl::flags_internal
-    absl::flags_marshalling
-    absl::log_severity
-    absl::strings
-    gmock
-    gtest_main
-)
-
-absl_cc_library(
-  NAME
-    strerror
-  SRCS
-    "internal/strerror.cc"
-  HDRS
-    "internal/strerror.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::errno_saver
-)
-
-absl_cc_test(
-  NAME
-    strerror_test
-  SRCS
-    "internal/strerror_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strerror
-    absl::strings
-    gmock
-    gtest_main
-)
-
-absl_cc_library(
-  NAME
-    fast_type_id
-  HDRS
-    "internal/fast_type_id.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-)
-
-absl_cc_test(
-  NAME
-    fast_type_id_test
-  SRCS
-    "internal/fast_type_id_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::fast_type_id
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    optimization_test
-  SRCS
-    "optimization_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::core_headers
-    absl::optional
-    gtest_main
-)
diff --git a/third_party/abseil_cpp/absl/base/attributes.h b/third_party/abseil_cpp/absl/base/attributes.h
deleted file mode 100644
index f1d3cfe4d1..0000000000
--- a/third_party/abseil_cpp/absl/base/attributes.h
+++ /dev/null
@@ -1,683 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This header file defines macros for declaring attributes for functions,
-// types, and variables.
-//
-// These macros are used within Abseil and allow the compiler to optimize, where
-// applicable, certain function calls.
-//
-// This file is used for both C and C++!
-//
-// Most macros here are exposing GCC or Clang features, and are stubbed out for
-// other compilers.
-//
-// GCC attributes documentation:
-//   https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
-//   https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Variable-Attributes.html
-//   https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Type-Attributes.html
-//
-// Most attributes in this file are already supported by GCC 4.7. However, some
-// of them are not supported in older version of Clang. Thus, we check
-// `__has_attribute()` first. If the check fails, we check if we are on GCC and
-// assume the attribute exists on GCC (which is verified on GCC 4.7).
-
-#ifndef ABSL_BASE_ATTRIBUTES_H_
-#define ABSL_BASE_ATTRIBUTES_H_
-
-#include "absl/base/config.h"
-
-// ABSL_HAVE_ATTRIBUTE
-//
-// A function-like feature checking macro that is a wrapper around
-// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a
-// nonzero constant integer if the attribute is supported or 0 if not.
-//
-// It evaluates to zero if `__has_attribute` is not defined by the compiler.
-//
-// GCC: https://gcc.gnu.org/gcc-5/changes.html
-// Clang: https://clang.llvm.org/docs/LanguageExtensions.html
-#ifdef __has_attribute
-#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x)
-#else
-#define ABSL_HAVE_ATTRIBUTE(x) 0
-#endif
-
-// ABSL_HAVE_CPP_ATTRIBUTE
-//
-// A function-like feature checking macro that accepts C++11 style attributes.
-// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6
-// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't
-// find `__has_cpp_attribute`, will evaluate to 0.
-#if defined(__cplusplus) && defined(__has_cpp_attribute)
-// NOTE: requiring __cplusplus above should not be necessary, but
-// works around https://bugs.llvm.org/show_bug.cgi?id=23435.
-#define ABSL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
-#else
-#define ABSL_HAVE_CPP_ATTRIBUTE(x) 0
-#endif
-
-// -----------------------------------------------------------------------------
-// Function Attributes
-// -----------------------------------------------------------------------------
-//
-// GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
-// Clang: https://clang.llvm.org/docs/AttributeReference.html
-
-// ABSL_PRINTF_ATTRIBUTE
-// ABSL_SCANF_ATTRIBUTE
-//
-// Tells the compiler to perform `printf` format string checking if the
-// compiler supports it; see the 'format' attribute in
-// <https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>.
-//
-// Note: As the GCC manual states, "[s]ince non-static C++ methods
-// have an implicit 'this' argument, the arguments of such methods
-// should be counted from two, not one."
-#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \
-  __attribute__((__format__(__printf__, string_index, first_to_check)))
-#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \
-  __attribute__((__format__(__scanf__, string_index, first_to_check)))
-#else
-#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check)
-#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check)
-#endif
-
-// ABSL_ATTRIBUTE_ALWAYS_INLINE
-// ABSL_ATTRIBUTE_NOINLINE
-//
-// Forces functions to either inline or not inline. Introduced in gcc 3.1.
-#if ABSL_HAVE_ATTRIBUTE(always_inline) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
-#define ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE 1
-#else
-#define ABSL_ATTRIBUTE_ALWAYS_INLINE
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_NOINLINE __attribute__((noinline))
-#define ABSL_HAVE_ATTRIBUTE_NOINLINE 1
-#else
-#define ABSL_ATTRIBUTE_NOINLINE
-#endif
-
-// ABSL_ATTRIBUTE_NO_TAIL_CALL
-//
-// Prevents the compiler from optimizing away stack frames for functions which
-// end in a call to another function.
-#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls)
-#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
-#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls))
-#elif defined(__GNUC__) && !defined(__clang__)
-#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
-#define ABSL_ATTRIBUTE_NO_TAIL_CALL \
-  __attribute__((optimize("no-optimize-sibling-calls")))
-#else
-#define ABSL_ATTRIBUTE_NO_TAIL_CALL
-#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0
-#endif
-
-// ABSL_ATTRIBUTE_WEAK
-//
-// Tags a function as weak for the purposes of compilation and linking.
-// Weak attributes currently do not work properly in LLVM's Windows backend,
-// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
-// for further information.
-// The MinGW compiler doesn't complain about the weak attribute until the link
-// step, presumably because Windows doesn't use ELF binaries.
-#if (ABSL_HAVE_ATTRIBUTE(weak) ||                   \
-     (defined(__GNUC__) && !defined(__clang__))) && \
-    !(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__)
-#undef ABSL_ATTRIBUTE_WEAK
-#define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
-#define ABSL_HAVE_ATTRIBUTE_WEAK 1
-#else
-#define ABSL_ATTRIBUTE_WEAK
-#define ABSL_HAVE_ATTRIBUTE_WEAK 0
-#endif
-
-// ABSL_ATTRIBUTE_NONNULL
-//
-// Tells the compiler either (a) that a particular function parameter
-// should be a non-null pointer, or (b) that all pointer arguments should
-// be non-null.
-//
-// Note: As the GCC manual states, "[s]ince non-static C++ methods
-// have an implicit 'this' argument, the arguments of such methods
-// should be counted from two, not one."
-//
-// Args are indexed starting at 1.
-//
-// For non-static class member functions, the implicit `this` argument
-// is arg 1, and the first explicit argument is arg 2. For static class member
-// functions, there is no implicit `this`, and the first explicit argument is
-// arg 1.
-//
-// Example:
-//
-//   /* arg_a cannot be null, but arg_b can */
-//   void Function(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(1);
-//
-//   class C {
-//     /* arg_a cannot be null, but arg_b can */
-//     void Method(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(2);
-//
-//     /* arg_a cannot be null, but arg_b can */
-//     static void StaticMethod(void* arg_a, void* arg_b)
-//     ABSL_ATTRIBUTE_NONNULL(1);
-//   };
-//
-// If no arguments are provided, then all pointer arguments should be non-null.
-//
-//  /* No pointer arguments may be null. */
-//  void Function(void* arg_a, void* arg_b, int arg_c) ABSL_ATTRIBUTE_NONNULL();
-//
-// NOTE: The GCC nonnull attribute actually accepts a list of arguments, but
-// ABSL_ATTRIBUTE_NONNULL does not.
-#if ABSL_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index)))
-#else
-#define ABSL_ATTRIBUTE_NONNULL(...)
-#endif
-
-// ABSL_ATTRIBUTE_NORETURN
-//
-// Tells the compiler that a given function never returns.
-#if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_NORETURN __attribute__((noreturn))
-#elif defined(_MSC_VER)
-#define ABSL_ATTRIBUTE_NORETURN __declspec(noreturn)
-#else
-#define ABSL_ATTRIBUTE_NORETURN
-#endif
-
-// ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
-//
-// Tells the AddressSanitizer (or other memory testing tools) to ignore a given
-// function. Useful for cases when a function reads random locations on stack,
-// calls _exit from a cloned subprocess, deliberately accesses buffer
-// out of bounds or does other scary things with memory.
-// NOTE: GCC supports AddressSanitizer(asan) since 4.8.
-// https://gcc.gnu.org/gcc-4.8/changes.html
-#if ABSL_HAVE_ATTRIBUTE(no_sanitize_address)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
-#endif
-
-// ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
-//
-// Tells the MemorySanitizer to relax the handling of a given function. All "Use
-// of uninitialized value" warnings from such functions will be suppressed, and
-// all values loaded from memory will be considered fully initialized.  This
-// attribute is similar to the ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS attribute
-// above, but deals with initialized-ness rather than addressability issues.
-// NOTE: MemorySanitizer(msan) is supported by Clang but not GCC.
-#if ABSL_HAVE_ATTRIBUTE(no_sanitize_memory)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
-#endif
-
-// ABSL_ATTRIBUTE_NO_SANITIZE_THREAD
-//
-// Tells the ThreadSanitizer to not instrument a given function.
-// NOTE: GCC supports ThreadSanitizer(tsan) since 4.8.
-// https://gcc.gnu.org/gcc-4.8/changes.html
-#if ABSL_HAVE_ATTRIBUTE(no_sanitize_thread)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD
-#endif
-
-// ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
-//
-// Tells the UndefinedSanitizer to ignore a given function. Useful for cases
-// where certain behavior (eg. division by zero) is being used intentionally.
-// NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9.
-// https://gcc.gnu.org/gcc-4.9/changes.html
-#if ABSL_HAVE_ATTRIBUTE(no_sanitize_undefined)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \
-  __attribute__((no_sanitize_undefined))
-#elif ABSL_HAVE_ATTRIBUTE(no_sanitize)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \
-  __attribute__((no_sanitize("undefined")))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
-#endif
-
-// ABSL_ATTRIBUTE_NO_SANITIZE_CFI
-//
-// Tells the ControlFlowIntegrity sanitizer to not instrument a given function.
-// See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details.
-#if ABSL_HAVE_ATTRIBUTE(no_sanitize)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi")))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI
-#endif
-
-// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
-//
-// Tells the SafeStack to not instrument a given function.
-// See https://clang.llvm.org/docs/SafeStack.html for details.
-#if ABSL_HAVE_ATTRIBUTE(no_sanitize)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \
-  __attribute__((no_sanitize("safe-stack")))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
-#endif
-
-// ABSL_ATTRIBUTE_RETURNS_NONNULL
-//
-// Tells the compiler that a particular function never returns a null pointer.
-#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \
-    (defined(__GNUC__) && \
-     (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \
-     !defined(__clang__))
-#define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
-#else
-#define ABSL_ATTRIBUTE_RETURNS_NONNULL
-#endif
-
-// ABSL_HAVE_ATTRIBUTE_SECTION
-//
-// Indicates whether labeled sections are supported. Weak symbol support is
-// a prerequisite. Labeled sections are not supported on Darwin/iOS.
-#ifdef ABSL_HAVE_ATTRIBUTE_SECTION
-#error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set
-#elif (ABSL_HAVE_ATTRIBUTE(section) ||                \
-       (defined(__GNUC__) && !defined(__clang__))) && \
-    !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK
-#define ABSL_HAVE_ATTRIBUTE_SECTION 1
-
-// ABSL_ATTRIBUTE_SECTION
-//
-// Tells the compiler/linker to put a given function into a section and define
-// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section.
-// This functionality is supported by GNU linker.  Any function annotated with
-// `ABSL_ATTRIBUTE_SECTION` must not be inlined, or it will be placed into
-// whatever section its caller is placed into.
-//
-#ifndef ABSL_ATTRIBUTE_SECTION
-#define ABSL_ATTRIBUTE_SECTION(name) \
-  __attribute__((section(#name))) __attribute__((noinline))
-#endif
-
-
-// ABSL_ATTRIBUTE_SECTION_VARIABLE
-//
-// Tells the compiler/linker to put a given variable into a section and define
-// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section.
-// This functionality is supported by GNU linker.
-#ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE
-#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name)))
-#endif
-
-// ABSL_DECLARE_ATTRIBUTE_SECTION_VARS
-//
-// A weak section declaration to be used as a global declaration
-// for ABSL_ATTRIBUTE_SECTION_START|STOP(name) to compile and link
-// even without functions with ABSL_ATTRIBUTE_SECTION(name).
-// ABSL_DEFINE_ATTRIBUTE_SECTION should be in the exactly one file; it's
-// a no-op on ELF but not on Mach-O.
-//
-#ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS
-#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \
-  extern char __start_##name[] ABSL_ATTRIBUTE_WEAK;    \
-  extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK
-#endif
-#ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS
-#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name)
-#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name)
-#endif
-
-// ABSL_ATTRIBUTE_SECTION_START
-//
-// Returns `void*` pointers to start/end of a section of code with
-// functions having ABSL_ATTRIBUTE_SECTION(name).
-// Returns 0 if no such functions exist.
-// One must ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) for this to compile and
-// link.
-//
-#define ABSL_ATTRIBUTE_SECTION_START(name) \
-  (reinterpret_cast<void *>(__start_##name))
-#define ABSL_ATTRIBUTE_SECTION_STOP(name) \
-  (reinterpret_cast<void *>(__stop_##name))
-
-#else  // !ABSL_HAVE_ATTRIBUTE_SECTION
-
-#define ABSL_HAVE_ATTRIBUTE_SECTION 0
-
-// provide dummy definitions
-#define ABSL_ATTRIBUTE_SECTION(name)
-#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name)
-#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name)
-#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name)
-#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name)
-#define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(0))
-#define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(0))
-
-#endif  // ABSL_ATTRIBUTE_SECTION
-
-// ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-//
-// Support for aligning the stack on 32-bit x86.
-#if ABSL_HAVE_ATTRIBUTE(force_align_arg_pointer) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#if defined(__i386__)
-#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC \
-  __attribute__((force_align_arg_pointer))
-#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
-#elif defined(__x86_64__)
-#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (1)
-#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-#else  // !__i386__ && !__x86_64
-#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
-#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-#endif  // __i386__
-#else
-#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
-#endif
-
-// ABSL_MUST_USE_RESULT
-//
-// Tells the compiler to warn about unused results.
-//
-// When annotating a function, it must appear as the first part of the
-// declaration or definition. The compiler will warn if the return value from
-// such a function is unused:
-//
-//   ABSL_MUST_USE_RESULT Sprocket* AllocateSprocket();
-//   AllocateSprocket();  // Triggers a warning.
-//
-// When annotating a class, it is equivalent to annotating every function which
-// returns an instance.
-//
-//   class ABSL_MUST_USE_RESULT Sprocket {};
-//   Sprocket();  // Triggers a warning.
-//
-//   Sprocket MakeSprocket();
-//   MakeSprocket();  // Triggers a warning.
-//
-// Note that references and pointers are not instances:
-//
-//   Sprocket* SprocketPointer();
-//   SprocketPointer();  // Does *not* trigger a warning.
-//
-// ABSL_MUST_USE_RESULT allows using cast-to-void to suppress the unused result
-// warning. For that, warn_unused_result is used only for clang but not for gcc.
-// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425
-//
-// Note: past advice was to place the macro after the argument list.
-#if ABSL_HAVE_ATTRIBUTE(nodiscard)
-#define ABSL_MUST_USE_RESULT [[nodiscard]]
-#elif defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result)
-#define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result))
-#else
-#define ABSL_MUST_USE_RESULT
-#endif
-
-// ABSL_ATTRIBUTE_HOT, ABSL_ATTRIBUTE_COLD
-//
-// Tells GCC that a function is hot or cold. GCC can use this information to
-// improve static analysis, i.e. a conditional branch to a cold function
-// is likely to be not-taken.
-// This annotation is used for function declarations.
-//
-// Example:
-//
-//   int foo() ABSL_ATTRIBUTE_HOT;
-#if ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_HOT __attribute__((hot))
-#else
-#define ABSL_ATTRIBUTE_HOT
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_COLD __attribute__((cold))
-#else
-#define ABSL_ATTRIBUTE_COLD
-#endif
-
-// ABSL_XRAY_ALWAYS_INSTRUMENT, ABSL_XRAY_NEVER_INSTRUMENT, ABSL_XRAY_LOG_ARGS
-//
-// We define the ABSL_XRAY_ALWAYS_INSTRUMENT and ABSL_XRAY_NEVER_INSTRUMENT
-// macro used as an attribute to mark functions that must always or never be
-// instrumented by XRay. Currently, this is only supported in Clang/LLVM.
-//
-// For reference on the LLVM XRay instrumentation, see
-// http://llvm.org/docs/XRay.html.
-//
-// A function with the XRAY_ALWAYS_INSTRUMENT macro attribute in its declaration
-// will always get the XRay instrumentation sleds. These sleds may introduce
-// some binary size and runtime overhead and must be used sparingly.
-//
-// These attributes only take effect when the following conditions are met:
-//
-//   * The file/target is built in at least C++11 mode, with a Clang compiler
-//     that supports XRay attributes.
-//   * The file/target is built with the -fxray-instrument flag set for the
-//     Clang/LLVM compiler.
-//   * The function is defined in the translation unit (the compiler honors the
-//     attribute in either the definition or the declaration, and must match).
-//
-// There are cases when, even when building with XRay instrumentation, users
-// might want to control specifically which functions are instrumented for a
-// particular build using special-case lists provided to the compiler. These
-// special case lists are provided to Clang via the
-// -fxray-always-instrument=... and -fxray-never-instrument=... flags. The
-// attributes in source take precedence over these special-case lists.
-//
-// To disable the XRay attributes at build-time, users may define
-// ABSL_NO_XRAY_ATTRIBUTES. Do NOT define ABSL_NO_XRAY_ATTRIBUTES on specific
-// packages/targets, as this may lead to conflicting definitions of functions at
-// link-time.
-//
-// XRay isn't currently supported on Android:
-// https://github.com/android/ndk/issues/368
-#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \
-    !defined(ABSL_NO_XRAY_ATTRIBUTES) && !defined(__ANDROID__)
-#define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]]
-#define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]]
-#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args)
-#define ABSL_XRAY_LOG_ARGS(N) \
-    [[clang::xray_always_instrument, clang::xray_log_args(N)]]
-#else
-#define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]]
-#endif
-#else
-#define ABSL_XRAY_ALWAYS_INSTRUMENT
-#define ABSL_XRAY_NEVER_INSTRUMENT
-#define ABSL_XRAY_LOG_ARGS(N)
-#endif
-
-// ABSL_ATTRIBUTE_REINITIALIZES
-//
-// Indicates that a member function reinitializes the entire object to a known
-// state, independent of the previous state of the object.
-//
-// The clang-tidy check bugprone-use-after-move allows member functions marked
-// with this attribute to be called on objects that have been moved from;
-// without the attribute, this would result in a use-after-move warning.
-#if ABSL_HAVE_CPP_ATTRIBUTE(clang::reinitializes)
-#define ABSL_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]]
-#else
-#define ABSL_ATTRIBUTE_REINITIALIZES
-#endif
-
-// -----------------------------------------------------------------------------
-// Variable Attributes
-// -----------------------------------------------------------------------------
-
-// ABSL_ATTRIBUTE_UNUSED
-//
-// Prevents the compiler from complaining about variables that appear unused.
-#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
-#undef ABSL_ATTRIBUTE_UNUSED
-#define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__))
-#else
-#define ABSL_ATTRIBUTE_UNUSED
-#endif
-
-// ABSL_ATTRIBUTE_INITIAL_EXEC
-//
-// Tells the compiler to use "initial-exec" mode for a thread-local variable.
-// See http://people.redhat.com/drepper/tls.pdf for the gory details.
-#if ABSL_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec")))
-#else
-#define ABSL_ATTRIBUTE_INITIAL_EXEC
-#endif
-
-// ABSL_ATTRIBUTE_PACKED
-//
-// Instructs the compiler not to use natural alignment for a tagged data
-// structure, but instead to reduce its alignment to 1. This attribute can
-// either be applied to members of a structure or to a structure in its
-// entirety. Applying this attribute (judiciously) to a structure in its
-// entirety to optimize the memory footprint of very commonly-used structs is
-// fine. Do not apply this attribute to a structure in its entirety if the
-// purpose is to control the offsets of the members in the structure. Instead,
-// apply this attribute only to structure members that need it.
-//
-// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the
-// natural alignment of structure members not annotated is preserved. Aligned
-// member accesses are faster than non-aligned member accesses even if the
-// targeted microprocessor supports non-aligned accesses.
-#if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__))
-#else
-#define ABSL_ATTRIBUTE_PACKED
-#endif
-
-// ABSL_ATTRIBUTE_FUNC_ALIGN
-//
-// Tells the compiler to align the function start at least to certain
-// alignment boundary
-#if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) __attribute__((aligned(bytes)))
-#else
-#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes)
-#endif
-
-// ABSL_FALLTHROUGH_INTENDED
-//
-// Annotates implicit fall-through between switch labels, allowing a case to
-// indicate intentional fallthrough and turn off warnings about any lack of a
-// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by
-// a semicolon and can be used in most places where `break` can, provided that
-// no statements exist between it and the next switch label.
-//
-// Example:
-//
-//  switch (x) {
-//    case 40:
-//    case 41:
-//      if (truth_is_out_there) {
-//        ++x;
-//        ABSL_FALLTHROUGH_INTENDED;  // Use instead of/along with annotations
-//                                    // in comments
-//      } else {
-//        return x;
-//      }
-//    case 42:
-//      ...
-//
-// Notes: when compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED
-// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed
-// when  performing switch labels fall-through diagnostic
-// (`-Wimplicit-fallthrough`). See clang documentation on language extensions
-// for details:
-// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough
-//
-// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro
-// has no effect on diagnostics. In any case this macro has no effect on runtime
-// behavior and performance of code.
-
-#ifdef ABSL_FALLTHROUGH_INTENDED
-#error "ABSL_FALLTHROUGH_INTENDED should not be defined."
-#endif
-
-// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported.
-#if defined(__clang__) && defined(__has_warning)
-#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
-#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]]
-#endif
-#elif defined(__GNUC__) && __GNUC__ >= 7
-#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]]
-#endif
-
-#ifndef ABSL_FALLTHROUGH_INTENDED
-#define ABSL_FALLTHROUGH_INTENDED \
-  do {                            \
-  } while (0)
-#endif
-
-// ABSL_DEPRECATED()
-//
-// Marks a deprecated class, struct, enum, function, method and variable
-// declarations. The macro argument is used as a custom diagnostic message (e.g.
-// suggestion of a better alternative).
-//
-// Examples:
-//
-//   class ABSL_DEPRECATED("Use Bar instead") Foo {...};
-//
-//   ABSL_DEPRECATED("Use Baz() instead") void Bar() {...}
-//
-//   template <typename T>
-//   ABSL_DEPRECATED("Use DoThat() instead")
-//   void DoThis();
-//
-// Every usage of a deprecated entity will trigger a warning when compiled with
-// clang's `-Wdeprecated-declarations` option. This option is turned off by
-// default, but the warnings will be reported by clang-tidy.
-#if defined(__clang__) && __cplusplus >= 201103L
-#define ABSL_DEPRECATED(message) __attribute__((deprecated(message)))
-#endif
-
-#ifndef ABSL_DEPRECATED
-#define ABSL_DEPRECATED(message)
-#endif
-
-// ABSL_CONST_INIT
-//
-// A variable declaration annotated with the `ABSL_CONST_INIT` attribute will
-// not compile (on supported platforms) unless the variable has a constant
-// initializer. This is useful for variables with static and thread storage
-// duration, because it guarantees that they will not suffer from the so-called
-// "static init order fiasco".  Prefer to put this attribute on the most visible
-// declaration of the variable, if there's more than one, because code that
-// accesses the variable can then use the attribute for optimization.
-//
-// Example:
-//
-//   class MyClass {
-//    public:
-//     ABSL_CONST_INIT static MyType my_var;
-//   };
-//
-//   MyType MyClass::my_var = MakeMyType(...);
-//
-// Note that this attribute is redundant if the variable is declared constexpr.
-#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
-#define ABSL_CONST_INIT [[clang::require_constant_initialization]]
-#else
-#define ABSL_CONST_INIT
-#endif  // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
-
-#endif  // ABSL_BASE_ATTRIBUTES_H_
diff --git a/third_party/abseil_cpp/absl/base/bit_cast_test.cc b/third_party/abseil_cpp/absl/base/bit_cast_test.cc
deleted file mode 100644
index 8a3a41ea02..0000000000
--- a/third_party/abseil_cpp/absl/base/bit_cast_test.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Unit test for bit_cast template.
-
-#include <cstdint>
-#include <cstring>
-
-#include "gtest/gtest.h"
-#include "absl/base/casts.h"
-#include "absl/base/macros.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-template <int N>
-struct marshall { char buf[N]; };
-
-template <typename T>
-void TestMarshall(const T values[], int num_values) {
-  for (int i = 0; i < num_values; ++i) {
-    T t0 = values[i];
-    marshall<sizeof(T)> m0 = absl::bit_cast<marshall<sizeof(T)> >(t0);
-    T t1 = absl::bit_cast<T>(m0);
-    marshall<sizeof(T)> m1 = absl::bit_cast<marshall<sizeof(T)> >(t1);
-    ASSERT_EQ(0, memcmp(&t0, &t1, sizeof(T)));
-    ASSERT_EQ(0, memcmp(&m0, &m1, sizeof(T)));
-  }
-}
-
-// Convert back and forth to an integral type.  The C++ standard does
-// not guarantee this will work, but we test that this works on all the
-// platforms we support.
-//
-// Likewise, we below make assumptions about sizeof(float) and
-// sizeof(double) which the standard does not guarantee, but which hold on the
-// platforms we support.
-
-template <typename T, typename I>
-void TestIntegral(const T values[], int num_values) {
-  for (int i = 0; i < num_values; ++i) {
-    T t0 = values[i];
-    I i0 = absl::bit_cast<I>(t0);
-    T t1 = absl::bit_cast<T>(i0);
-    I i1 = absl::bit_cast<I>(t1);
-    ASSERT_EQ(0, memcmp(&t0, &t1, sizeof(T)));
-    ASSERT_EQ(i0, i1);
-  }
-}
-
-TEST(BitCast, Bool) {
-  static const bool bool_list[] = { false, true };
-  TestMarshall<bool>(bool_list, ABSL_ARRAYSIZE(bool_list));
-}
-
-TEST(BitCast, Int32) {
-  static const int32_t int_list[] =
-    { 0, 1, 100, 2147483647, -1, -100, -2147483647, -2147483647-1 };
-  TestMarshall<int32_t>(int_list, ABSL_ARRAYSIZE(int_list));
-}
-
-TEST(BitCast, Int64) {
-  static const int64_t int64_list[] =
-    { 0, 1, 1LL << 40, -1, -(1LL<<40) };
-  TestMarshall<int64_t>(int64_list, ABSL_ARRAYSIZE(int64_list));
-}
-
-TEST(BitCast, Uint64) {
-  static const uint64_t uint64_list[] =
-    { 0, 1, 1LLU << 40, 1LLU << 63 };
-  TestMarshall<uint64_t>(uint64_list, ABSL_ARRAYSIZE(uint64_list));
-}
-
-TEST(BitCast, Float) {
-  static const float float_list[] =
-    { 0.0f, 1.0f, -1.0f, 10.0f, -10.0f,
-      1e10f, 1e20f, 1e-10f, 1e-20f,
-      2.71828f, 3.14159f };
-  TestMarshall<float>(float_list, ABSL_ARRAYSIZE(float_list));
-  TestIntegral<float, int>(float_list, ABSL_ARRAYSIZE(float_list));
-  TestIntegral<float, unsigned>(float_list, ABSL_ARRAYSIZE(float_list));
-}
-
-TEST(BitCast, Double) {
-  static const double double_list[] =
-    { 0.0, 1.0, -1.0, 10.0, -10.0,
-      1e10, 1e100, 1e-10, 1e-100,
-      2.718281828459045,
-      3.141592653589793238462643383279502884197169399375105820974944 };
-  TestMarshall<double>(double_list, ABSL_ARRAYSIZE(double_list));
-  TestIntegral<double, int64_t>(double_list, ABSL_ARRAYSIZE(double_list));
-  TestIntegral<double, uint64_t>(double_list, ABSL_ARRAYSIZE(double_list));
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/call_once.h b/third_party/abseil_cpp/absl/base/call_once.h
deleted file mode 100644
index 5b468af855..0000000000
--- a/third_party/abseil_cpp/absl/base/call_once.h
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: call_once.h
-// -----------------------------------------------------------------------------
-//
-// This header file provides an Abseil version of `std::call_once` for invoking
-// a given function at most once, across all threads. This Abseil version is
-// faster than the C++11 version and incorporates the C++17 argument-passing
-// fix, so that (for example) non-const references may be passed to the invoked
-// function.
-
-#ifndef ABSL_BASE_CALL_ONCE_H_
-#define ABSL_BASE_CALL_ONCE_H_
-
-#include <algorithm>
-#include <atomic>
-#include <cstdint>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/internal/invoke.h"
-#include "absl/base/internal/low_level_scheduling.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/scheduling_mode.h"
-#include "absl/base/internal/spinlock_wait.h"
-#include "absl/base/macros.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class once_flag;
-
-namespace base_internal {
-std::atomic<uint32_t>* ControlWord(absl::once_flag* flag);
-}  // namespace base_internal
-
-// call_once()
-//
-// For all invocations using a given `once_flag`, invokes a given `fn` exactly
-// once across all threads. The first call to `call_once()` with a particular
-// `once_flag` argument (that does not throw an exception) will run the
-// specified function with the provided `args`; other calls with the same
-// `once_flag` argument will not run the function, but will wait
-// for the provided function to finish running (if it is still running).
-//
-// This mechanism provides a safe, simple, and fast mechanism for one-time
-// initialization in a multi-threaded process.
-//
-// Example:
-//
-// class MyInitClass {
-//  public:
-//  ...
-//  mutable absl::once_flag once_;
-//
-//  MyInitClass* init() const {
-//    absl::call_once(once_, &MyInitClass::Init, this);
-//    return ptr_;
-//  }
-//
-template <typename Callable, typename... Args>
-void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args);
-
-// once_flag
-//
-// Objects of this type are used to distinguish calls to `call_once()` and
-// ensure the provided function is only invoked once across all threads. This
-// type is not copyable or movable. However, it has a `constexpr`
-// constructor, and is safe to use as a namespace-scoped global variable.
-class once_flag {
- public:
-  constexpr once_flag() : control_(0) {}
-  once_flag(const once_flag&) = delete;
-  once_flag& operator=(const once_flag&) = delete;
-
- private:
-  friend std::atomic<uint32_t>* base_internal::ControlWord(once_flag* flag);
-  std::atomic<uint32_t> control_;
-};
-
-//------------------------------------------------------------------------------
-// End of public interfaces.
-// Implementation details follow.
-//------------------------------------------------------------------------------
-
-namespace base_internal {
-
-// Like call_once, but uses KERNEL_ONLY scheduling. Intended to be used to
-// initialize entities used by the scheduler implementation.
-template <typename Callable, typename... Args>
-void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args);
-
-// Disables scheduling while on stack when scheduling mode is non-cooperative.
-// No effect for cooperative scheduling modes.
-class SchedulingHelper {
- public:
-  explicit SchedulingHelper(base_internal::SchedulingMode mode) : mode_(mode) {
-    if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) {
-      guard_result_ = base_internal::SchedulingGuard::DisableRescheduling();
-    }
-  }
-
-  ~SchedulingHelper() {
-    if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) {
-      base_internal::SchedulingGuard::EnableRescheduling(guard_result_);
-    }
-  }
-
- private:
-  base_internal::SchedulingMode mode_;
-  bool guard_result_;
-};
-
-// Bit patterns for call_once state machine values.  Internal implementation
-// detail, not for use by clients.
-//
-// The bit patterns are arbitrarily chosen from unlikely values, to aid in
-// debugging.  However, kOnceInit must be 0, so that a zero-initialized
-// once_flag will be valid for immediate use.
-enum {
-  kOnceInit = 0,
-  kOnceRunning = 0x65C2937B,
-  kOnceWaiter = 0x05A308D2,
-  // A very small constant is chosen for kOnceDone so that it fit in a single
-  // compare with immediate instruction for most common ISAs.  This is verified
-  // for x86, POWER and ARM.
-  kOnceDone = 221,    // Random Number
-};
-
-template <typename Callable, typename... Args>
-ABSL_ATTRIBUTE_NOINLINE
-void CallOnceImpl(std::atomic<uint32_t>* control,
-                  base_internal::SchedulingMode scheduling_mode, Callable&& fn,
-                  Args&&... args) {
-#ifndef NDEBUG
-  {
-    uint32_t old_control = control->load(std::memory_order_relaxed);
-    if (old_control != kOnceInit &&
-        old_control != kOnceRunning &&
-        old_control != kOnceWaiter &&
-        old_control != kOnceDone) {
-      ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",
-                   static_cast<unsigned long>(old_control));  // NOLINT
-    }
-  }
-#endif  // NDEBUG
-  static const base_internal::SpinLockWaitTransition trans[] = {
-      {kOnceInit, kOnceRunning, true},
-      {kOnceRunning, kOnceWaiter, false},
-      {kOnceDone, kOnceDone, true}};
-
-  // Must do this before potentially modifying control word's state.
-  base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);
-  // Short circuit the simplest case to avoid procedure call overhead.
-  // The base_internal::SpinLockWait() call returns either kOnceInit or
-  // kOnceDone. If it returns kOnceDone, it must have loaded the control word
-  // with std::memory_order_acquire and seen a value of kOnceDone.
-  uint32_t old_control = kOnceInit;
-  if (control->compare_exchange_strong(old_control, kOnceRunning,
-                                       std::memory_order_relaxed) ||
-      base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans,
-                                  scheduling_mode) == kOnceInit) {
-    base_internal::invoke(std::forward<Callable>(fn),
-                          std::forward<Args>(args)...);
-    // The call to SpinLockWake below is an optimization, because the waiter
-    // in SpinLockWait is waiting with a short timeout. The atomic load/store
-    // sequence is slightly faster than an atomic exchange:
-    //   old_control = control->exchange(base_internal::kOnceDone,
-    //                                   std::memory_order_release);
-    // We opt for a slightly faster case when there are no waiters, in spite
-    // of longer tail latency when there are waiters.
-    old_control = control->load(std::memory_order_relaxed);
-    control->store(base_internal::kOnceDone, std::memory_order_release);
-    if (old_control == base_internal::kOnceWaiter) {
-      base_internal::SpinLockWake(control, true);
-    }
-  }  // else *control is already kOnceDone
-}
-
-inline std::atomic<uint32_t>* ControlWord(once_flag* flag) {
-  return &flag->control_;
-}
-
-template <typename Callable, typename... Args>
-void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args) {
-  std::atomic<uint32_t>* once = base_internal::ControlWord(flag);
-  uint32_t s = once->load(std::memory_order_acquire);
-  if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) {
-    base_internal::CallOnceImpl(once, base_internal::SCHEDULE_KERNEL_ONLY,
-                                std::forward<Callable>(fn),
-                                std::forward<Args>(args)...);
-  }
-}
-
-}  // namespace base_internal
-
-template <typename Callable, typename... Args>
-void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) {
-  std::atomic<uint32_t>* once = base_internal::ControlWord(&flag);
-  uint32_t s = once->load(std::memory_order_acquire);
-  if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) {
-    base_internal::CallOnceImpl(
-        once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL,
-        std::forward<Callable>(fn), std::forward<Args>(args)...);
-  }
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_CALL_ONCE_H_
diff --git a/third_party/abseil_cpp/absl/base/call_once_test.cc b/third_party/abseil_cpp/absl/base/call_once_test.cc
deleted file mode 100644
index 11d26c44d1..0000000000
--- a/third_party/abseil_cpp/absl/base/call_once_test.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/call_once.h"
-
-#include <thread>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/const_init.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-absl::once_flag once;
-
-ABSL_CONST_INIT Mutex counters_mu(absl::kConstInit);
-
-int running_thread_count ABSL_GUARDED_BY(counters_mu) = 0;
-int call_once_invoke_count ABSL_GUARDED_BY(counters_mu) = 0;
-int call_once_finished_count ABSL_GUARDED_BY(counters_mu) = 0;
-int call_once_return_count ABSL_GUARDED_BY(counters_mu) = 0;
-bool done_blocking ABSL_GUARDED_BY(counters_mu) = false;
-
-// Function to be called from absl::call_once.  Waits for a notification.
-void WaitAndIncrement() {
-  counters_mu.Lock();
-  ++call_once_invoke_count;
-  counters_mu.Unlock();
-
-  counters_mu.LockWhen(Condition(&done_blocking));
-  ++call_once_finished_count;
-  counters_mu.Unlock();
-}
-
-void ThreadBody() {
-  counters_mu.Lock();
-  ++running_thread_count;
-  counters_mu.Unlock();
-
-  absl::call_once(once, WaitAndIncrement);
-
-  counters_mu.Lock();
-  ++call_once_return_count;
-  counters_mu.Unlock();
-}
-
-// Returns true if all threads are set up for the test.
-bool ThreadsAreSetup(void*) ABSL_EXCLUSIVE_LOCKS_REQUIRED(counters_mu) {
-  // All ten threads must be running, and WaitAndIncrement should be blocked.
-  return running_thread_count == 10 && call_once_invoke_count == 1;
-}
-
-TEST(CallOnceTest, ExecutionCount) {
-  std::vector<std::thread> threads;
-
-  // Start 10 threads all calling call_once on the same once_flag.
-  for (int i = 0; i < 10; ++i) {
-    threads.emplace_back(ThreadBody);
-  }
-
-
-  // Wait until all ten threads have started, and WaitAndIncrement has been
-  // invoked.
-  counters_mu.LockWhen(Condition(ThreadsAreSetup, nullptr));
-
-  // WaitAndIncrement should have been invoked by exactly one call_once()
-  // instance.  That thread should be blocking on a notification, and all other
-  // call_once instances should be blocking as well.
-  EXPECT_EQ(call_once_invoke_count, 1);
-  EXPECT_EQ(call_once_finished_count, 0);
-  EXPECT_EQ(call_once_return_count, 0);
-
-  // Allow WaitAndIncrement to finish executing.  Once it does, the other
-  // call_once waiters will be unblocked.
-  done_blocking = true;
-  counters_mu.Unlock();
-
-  for (std::thread& thread : threads) {
-    thread.join();
-  }
-
-  counters_mu.Lock();
-  EXPECT_EQ(call_once_invoke_count, 1);
-  EXPECT_EQ(call_once_finished_count, 1);
-  EXPECT_EQ(call_once_return_count, 10);
-  counters_mu.Unlock();
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/casts.h b/third_party/abseil_cpp/absl/base/casts.h
deleted file mode 100644
index 83c691265f..0000000000
--- a/third_party/abseil_cpp/absl/base/casts.h
+++ /dev/null
@@ -1,187 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: casts.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines casting templates to fit use cases not covered by
-// the standard casts provided in the C++ standard. As with all cast operations,
-// use these with caution and only if alternatives do not exist.
-
-#ifndef ABSL_BASE_CASTS_H_
-#define ABSL_BASE_CASTS_H_
-
-#include <cstring>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/internal/identity.h"
-#include "absl/base/macros.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace internal_casts {
-
-template <class Dest, class Source>
-struct is_bitcastable
-    : std::integral_constant<
-          bool,
-          sizeof(Dest) == sizeof(Source) &&
-              type_traits_internal::is_trivially_copyable<Source>::value &&
-              type_traits_internal::is_trivially_copyable<Dest>::value &&
-              std::is_default_constructible<Dest>::value> {};
-
-}  // namespace internal_casts
-
-// implicit_cast()
-//
-// Performs an implicit conversion between types following the language
-// rules for implicit conversion; if an implicit conversion is otherwise
-// allowed by the language in the given context, this function performs such an
-// implicit conversion.
-//
-// Example:
-//
-//   // If the context allows implicit conversion:
-//   From from;
-//   To to = from;
-//
-//   // Such code can be replaced by:
-//   implicit_cast<To>(from);
-//
-// An `implicit_cast()` may also be used to annotate numeric type conversions
-// that, although safe, may produce compiler warnings (such as `long` to `int`).
-// Additionally, an `implicit_cast()` is also useful within return statements to
-// indicate a specific implicit conversion is being undertaken.
-//
-// Example:
-//
-//   return implicit_cast<double>(size_in_bytes) / capacity_;
-//
-// Annotating code with `implicit_cast()` allows you to explicitly select
-// particular overloads and template instantiations, while providing a safer
-// cast than `reinterpret_cast()` or `static_cast()`.
-//
-// Additionally, an `implicit_cast()` can be used to allow upcasting within a
-// type hierarchy where incorrect use of `static_cast()` could accidentally
-// allow downcasting.
-//
-// Finally, an `implicit_cast()` can be used to perform implicit conversions
-// from unrelated types that otherwise couldn't be implicitly cast directly;
-// C++ will normally only implicitly cast "one step" in such conversions.
-//
-// That is, if C is a type which can be implicitly converted to B, with B being
-// a type that can be implicitly converted to A, an `implicit_cast()` can be
-// used to convert C to B (which the compiler can then implicitly convert to A
-// using language rules).
-//
-// Example:
-//
-//   // Assume an object C is convertible to B, which is implicitly convertible
-//   // to A
-//   A a = implicit_cast<B>(C);
-//
-// Such implicit cast chaining may be useful within template logic.
-template <typename To>
-constexpr To implicit_cast(typename absl::internal::identity_t<To> to) {
-  return to;
-}
-
-// bit_cast()
-//
-// Performs a bitwise cast on a type without changing the underlying bit
-// representation of that type's value. The two types must be of the same size
-// and both types must be trivially copyable. As with most casts, use with
-// caution. A `bit_cast()` might be needed when you need to temporarily treat a
-// type as some other type, such as in the following cases:
-//
-//    * Serialization (casting temporarily to `char *` for those purposes is
-//      always allowed by the C++ standard)
-//    * Managing the individual bits of a type within mathematical operations
-//      that are not normally accessible through that type
-//    * Casting non-pointer types to pointer types (casting the other way is
-//      allowed by `reinterpret_cast()` but round-trips cannot occur the other
-//      way).
-//
-// Example:
-//
-//   float f = 3.14159265358979;
-//   int i = bit_cast<int32_t>(f);
-//   // i = 0x40490fdb
-//
-// Casting non-pointer types to pointer types and then dereferencing them
-// traditionally produces undefined behavior.
-//
-// Example:
-//
-//   // WRONG
-//   float f = 3.14159265358979;            // WRONG
-//   int i = * reinterpret_cast<int*>(&f);  // WRONG
-//
-// The address-casting method produces undefined behavior according to the ISO
-// C++ specification section [basic.lval]. Roughly, this section says: if an
-// object in memory has one type, and a program accesses it with a different
-// type, the result is undefined behavior for most values of "different type".
-//
-// Such casting results in type punning: holding an object in memory of one type
-// and reading its bits back using a different type. A `bit_cast()` avoids this
-// issue by implementing its casts using `memcpy()`, which avoids introducing
-// this undefined behavior.
-//
-// NOTE: The requirements here are more strict than the bit_cast of standard
-// proposal p0476 due to the need for workarounds and lack of intrinsics.
-// Specifically, this implementation also requires `Dest` to be
-// default-constructible.
-template <
-    typename Dest, typename Source,
-    typename std::enable_if<internal_casts::is_bitcastable<Dest, Source>::value,
-                            int>::type = 0>
-inline Dest bit_cast(const Source& source) {
-  Dest dest;
-  memcpy(static_cast<void*>(std::addressof(dest)),
-         static_cast<const void*>(std::addressof(source)), sizeof(dest));
-  return dest;
-}
-
-// NOTE: This overload is only picked if the requirements of bit_cast are
-// not met. It is therefore UB, but is provided temporarily as previous
-// versions of this function template were unchecked. Do not use this in
-// new code.
-template <
-    typename Dest, typename Source,
-    typename std::enable_if<
-        !internal_casts::is_bitcastable<Dest, Source>::value,
-        int>::type = 0>
-ABSL_DEPRECATED(
-    "absl::bit_cast type requirements were violated. Update the types "
-    "being used such that they are the same size and are both "
-    "TriviallyCopyable.")
-inline Dest bit_cast(const Source& source) {
-  static_assert(sizeof(Dest) == sizeof(Source),
-                "Source and destination types should have equal sizes.");
-
-  Dest dest;
-  memcpy(&dest, &source, sizeof(dest));
-  return dest;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_CASTS_H_
diff --git a/third_party/abseil_cpp/absl/base/config.h b/third_party/abseil_cpp/absl/base/config.h
deleted file mode 100644
index 3f7f32b9e6..0000000000
--- a/third_party/abseil_cpp/absl/base/config.h
+++ /dev/null
@@ -1,714 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: config.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines a set of macros for checking the presence of
-// important compiler and platform features. Such macros can be used to
-// produce portable code by parameterizing compilation based on the presence or
-// lack of a given feature.
-//
-// We define a "feature" as some interface we wish to program to: for example,
-// a library function or system call. A value of `1` indicates support for
-// that feature; any other value indicates the feature support is undefined.
-//
-// Example:
-//
-// Suppose a programmer wants to write a program that uses the 'mmap()' system
-// call. The Abseil macro for that feature (`ABSL_HAVE_MMAP`) allows you to
-// selectively include the `mmap.h` header and bracket code using that feature
-// in the macro:
-//
-//   #include "absl/base/config.h"
-//
-//   #ifdef ABSL_HAVE_MMAP
-//   #include "sys/mman.h"
-//   #endif  //ABSL_HAVE_MMAP
-//
-//   ...
-//   #ifdef ABSL_HAVE_MMAP
-//   void *ptr = mmap(...);
-//   ...
-//   #endif  // ABSL_HAVE_MMAP
-
-#ifndef ABSL_BASE_CONFIG_H_
-#define ABSL_BASE_CONFIG_H_
-
-// Included for the __GLIBC__ macro (or similar macros on other systems).
-#include <limits.h>
-
-#ifdef __cplusplus
-// Included for __GLIBCXX__, _LIBCPP_VERSION
-#include <cstddef>
-#endif  // __cplusplus
-
-#if defined(__APPLE__)
-// Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED,
-// __IPHONE_8_0.
-#include <Availability.h>
-#include <TargetConditionals.h>
-#endif
-
-#include "absl/base/options.h"
-#include "absl/base/policy_checks.h"
-
-// Helper macro to convert a CPP variable to a string literal.
-#define ABSL_INTERNAL_DO_TOKEN_STR(x) #x
-#define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x)
-
-// -----------------------------------------------------------------------------
-// Abseil namespace annotations
-// -----------------------------------------------------------------------------
-
-// ABSL_NAMESPACE_BEGIN/ABSL_NAMESPACE_END
-//
-// An annotation placed at the beginning/end of each `namespace absl` scope.
-// This is used to inject an inline namespace.
-//
-// The proper way to write Abseil code in the `absl` namespace is:
-//
-// namespace absl {
-// ABSL_NAMESPACE_BEGIN
-//
-// void Foo();  // absl::Foo().
-//
-// ABSL_NAMESPACE_END
-// }  // namespace absl
-//
-// Users of Abseil should not use these macros, because users of Abseil should
-// not write `namespace absl {` in their own code for any reason.  (Abseil does
-// not support forward declarations of its own types, nor does it support
-// user-provided specialization of Abseil templates.  Code that violates these
-// rules may be broken without warning.)
-#if !defined(ABSL_OPTION_USE_INLINE_NAMESPACE) || \
-    !defined(ABSL_OPTION_INLINE_NAMESPACE_NAME)
-#error options.h is misconfigured.
-#endif
-
-// Check that ABSL_OPTION_INLINE_NAMESPACE_NAME is neither "head" nor ""
-#if defined(__cplusplus) && ABSL_OPTION_USE_INLINE_NAMESPACE == 1
-
-#define ABSL_INTERNAL_INLINE_NAMESPACE_STR \
-  ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME)
-
-static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != '\0',
-              "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must "
-              "not be empty.");
-static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
-                  ABSL_INTERNAL_INLINE_NAMESPACE_STR[1] != 'e' ||
-                  ABSL_INTERNAL_INLINE_NAMESPACE_STR[2] != 'a' ||
-                  ABSL_INTERNAL_INLINE_NAMESPACE_STR[3] != 'd' ||
-                  ABSL_INTERNAL_INLINE_NAMESPACE_STR[4] != '\0',
-              "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must "
-              "be changed to a new, unique identifier name.");
-
-#endif
-
-#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0
-#define ABSL_NAMESPACE_BEGIN
-#define ABSL_NAMESPACE_END
-#elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1
-#define ABSL_NAMESPACE_BEGIN \
-  inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME {
-#define ABSL_NAMESPACE_END }
-#else
-#error options.h is misconfigured.
-#endif
-
-// -----------------------------------------------------------------------------
-// Compiler Feature Checks
-// -----------------------------------------------------------------------------
-
-// ABSL_HAVE_BUILTIN()
-//
-// Checks whether the compiler supports a Clang Feature Checking Macro, and if
-// so, checks whether it supports the provided builtin function "x" where x
-// is one of the functions noted in
-// https://clang.llvm.org/docs/LanguageExtensions.html
-//
-// Note: Use this macro to avoid an extra level of #ifdef __has_builtin check.
-// http://releases.llvm.org/3.3/tools/clang/docs/LanguageExtensions.html
-#ifdef __has_builtin
-#define ABSL_HAVE_BUILTIN(x) __has_builtin(x)
-#else
-#define ABSL_HAVE_BUILTIN(x) 0
-#endif
-
-#if defined(__is_identifier)
-#define ABSL_INTERNAL_HAS_KEYWORD(x) !(__is_identifier(x))
-#else
-#define ABSL_INTERNAL_HAS_KEYWORD(x) 0
-#endif
-
-#ifdef __has_feature
-#define ABSL_HAVE_FEATURE(f) __has_feature(f)
-#else
-#define ABSL_HAVE_FEATURE(f) 0
-#endif
-
-// ABSL_HAVE_TLS is defined to 1 when __thread should be supported.
-// We assume __thread is supported on Linux when compiled with Clang or compiled
-// against libstdc++ with _GLIBCXX_HAVE_TLS defined.
-#ifdef ABSL_HAVE_TLS
-#error ABSL_HAVE_TLS cannot be directly set
-#elif defined(__linux__) && (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS))
-#define ABSL_HAVE_TLS 1
-#endif
-
-// ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
-//
-// Checks whether `std::is_trivially_destructible<T>` is supported.
-//
-// Notes: All supported compilers using libc++ support this feature, as does
-// gcc >= 4.8.1 using libstdc++, and Visual Studio.
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
-#error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set
-#elif defined(_LIBCPP_VERSION) ||                                        \
-    (!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \
-     (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) ||        \
-    defined(_MSC_VER)
-#define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
-#endif
-
-// ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
-//
-// Checks whether `std::is_trivially_default_constructible<T>` and
-// `std::is_trivially_copy_constructible<T>` are supported.
-
-// ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
-//
-// Checks whether `std::is_trivially_copy_assignable<T>` is supported.
-
-// Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with
-// either libc++ or libstdc++, and Visual Studio (but not NVCC).
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
-#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
-#elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE)
-#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
-#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) ||        \
-    (!defined(__clang__) && defined(__GNUC__) &&                 \
-     (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \
-     (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) ||      \
-    (defined(_MSC_VER) && !defined(__NVCC__))
-#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
-#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
-#endif
-
-// ABSL_HAVE_SOURCE_LOCATION_CURRENT
-//
-// Indicates whether `absl::SourceLocation::current()` will return useful
-// information in some contexts.
-#ifndef ABSL_HAVE_SOURCE_LOCATION_CURRENT
-#if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \
-    ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE)
-#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1
-#endif
-#endif
-
-// ABSL_HAVE_THREAD_LOCAL
-//
-// Checks whether C++11's `thread_local` storage duration specifier is
-// supported.
-#ifdef ABSL_HAVE_THREAD_LOCAL
-#error ABSL_HAVE_THREAD_LOCAL cannot be directly set
-#elif defined(__APPLE__)
-// Notes:
-// * Xcode's clang did not support `thread_local` until version 8, and
-//   even then not for all iOS < 9.0.
-// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
-//   targeting iOS 9.x.
-// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
-//   making ABSL_HAVE_FEATURE unreliable there.
-//
-#if ABSL_HAVE_FEATURE(cxx_thread_local) && \
-    !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
-#define ABSL_HAVE_THREAD_LOCAL 1
-#endif
-#else  // !defined(__APPLE__)
-#define ABSL_HAVE_THREAD_LOCAL 1
-#endif
-
-// There are platforms for which TLS should not be used even though the compiler
-// makes it seem like it's supported (Android NDK < r12b for example).
-// This is primarily because of linker problems and toolchain misconfiguration:
-// Abseil does not intend to support this indefinitely. Currently, the newest
-// toolchain that we intend to support that requires this behavior is the
-// r11 NDK - allowing for a 5 year support window on that means this option
-// is likely to be removed around June of 2021.
-// TLS isn't supported until NDK r12b per
-// https://developer.android.com/ndk/downloads/revision_history.html
-// Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in
-// <android/ndk-version.h>. For NDK < r16, users should define these macros,
-// e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11.
-#if defined(__ANDROID__) && defined(__clang__)
-#if __has_include(<android/ndk-version.h>)
-#include <android/ndk-version.h>
-#endif  // __has_include(<android/ndk-version.h>)
-#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \
-    defined(__NDK_MINOR__) &&                                               \
-    ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))
-#undef ABSL_HAVE_TLS
-#undef ABSL_HAVE_THREAD_LOCAL
-#endif
-#endif  // defined(__ANDROID__) && defined(__clang__)
-
-// ABSL_HAVE_INTRINSIC_INT128
-//
-// Checks whether the __int128 compiler extension for a 128-bit integral type is
-// supported.
-//
-// Note: __SIZEOF_INT128__ is defined by Clang and GCC when __int128 is
-// supported, but we avoid using it in certain cases:
-// * On Clang:
-//   * Building using Clang for Windows, where the Clang runtime library has
-//     128-bit support only on LP64 architectures, but Windows is LLP64.
-// * On Nvidia's nvcc:
-//   * nvcc also defines __GNUC__ and __SIZEOF_INT128__, but not all versions
-//     actually support __int128.
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-#error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set
-#elif defined(__SIZEOF_INT128__)
-#if (defined(__clang__) && !defined(_WIN32)) || \
-    (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) ||                \
-    (defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__))
-#define ABSL_HAVE_INTRINSIC_INT128 1
-#elif defined(__CUDACC__)
-// __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a
-// string explaining that it has been removed starting with CUDA 9. We use
-// nested #ifs because there is no short-circuiting in the preprocessor.
-// NOTE: `__CUDACC__` could be undefined while `__CUDACC_VER__` is defined.
-#if __CUDACC_VER__ >= 70000
-#define ABSL_HAVE_INTRINSIC_INT128 1
-#endif  // __CUDACC_VER__ >= 70000
-#endif  // defined(__CUDACC__)
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-// ABSL_HAVE_EXCEPTIONS
-//
-// Checks whether the compiler both supports and enables exceptions. Many
-// compilers support a "no exceptions" mode that disables exceptions.
-//
-// Generally, when ABSL_HAVE_EXCEPTIONS is not defined:
-//
-// * Code using `throw` and `try` may not compile.
-// * The `noexcept` specifier will still compile and behave as normal.
-// * The `noexcept` operator may still return `false`.
-//
-// For further details, consult the compiler's documentation.
-#ifdef ABSL_HAVE_EXCEPTIONS
-#error ABSL_HAVE_EXCEPTIONS cannot be directly set.
-
-#elif defined(__clang__)
-
-#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6)
-// Clang >= 3.6
-#if ABSL_HAVE_FEATURE(cxx_exceptions)
-#define ABSL_HAVE_EXCEPTIONS 1
-#endif  // ABSL_HAVE_FEATURE(cxx_exceptions)
-#else
-// Clang < 3.6
-// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro
-#if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions)
-#define ABSL_HAVE_EXCEPTIONS 1
-#endif  // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions)
-#endif  // __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6)
-
-// Handle remaining special cases and default to exceptions being supported.
-#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) &&    \
-    !(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \
-    !(defined(_MSC_VER) && !defined(_CPPUNWIND))
-#define ABSL_HAVE_EXCEPTIONS 1
-#endif
-
-// -----------------------------------------------------------------------------
-// Platform Feature Checks
-// -----------------------------------------------------------------------------
-
-// Currently supported operating systems and associated preprocessor
-// symbols:
-//
-//   Linux and Linux-derived           __linux__
-//   Android                           __ANDROID__ (implies __linux__)
-//   Linux (non-Android)               __linux__ && !__ANDROID__
-//   Darwin (macOS and iOS)            __APPLE__
-//   Akaros (http://akaros.org)        __ros__
-//   Windows                           _WIN32
-//   NaCL                              __native_client__
-//   AsmJS                             __asmjs__
-//   WebAssembly                       __wasm__
-//   Fuchsia                           __Fuchsia__
-//
-// Note that since Android defines both __ANDROID__ and __linux__, one
-// may probe for either Linux or Android by simply testing for __linux__.
-
-// ABSL_HAVE_MMAP
-//
-// Checks whether the platform has an mmap(2) implementation as defined in
-// POSIX.1-2001.
-#ifdef ABSL_HAVE_MMAP
-#error ABSL_HAVE_MMAP cannot be directly set
-#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) ||   \
-    defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \
-    defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \
-    defined(__ASYLO__) || defined(__myriad2__)
-#define ABSL_HAVE_MMAP 1
-#endif
-
-// ABSL_HAVE_PTHREAD_GETSCHEDPARAM
-//
-// Checks whether the platform implements the pthread_(get|set)schedparam(3)
-// functions as defined in POSIX.1-2001.
-#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
-#error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set
-#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
-    defined(__ros__)
-#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1
-#endif
-
-// ABSL_HAVE_SCHED_YIELD
-//
-// Checks whether the platform implements sched_yield(2) as defined in
-// POSIX.1-2001.
-#ifdef ABSL_HAVE_SCHED_YIELD
-#error ABSL_HAVE_SCHED_YIELD cannot be directly set
-#elif defined(__linux__) || defined(__ros__) || defined(__native_client__)
-#define ABSL_HAVE_SCHED_YIELD 1
-#endif
-
-// ABSL_HAVE_SEMAPHORE_H
-//
-// Checks whether the platform supports the <semaphore.h> header and sem_init(3)
-// family of functions as standardized in POSIX.1-2001.
-//
-// Note: While Apple provides <semaphore.h> for both iOS and macOS, it is
-// explicitly deprecated and will cause build failures if enabled for those
-// platforms.  We side-step the issue by not defining it here for Apple
-// platforms.
-#ifdef ABSL_HAVE_SEMAPHORE_H
-#error ABSL_HAVE_SEMAPHORE_H cannot be directly set
-#elif defined(__linux__) || defined(__ros__)
-#define ABSL_HAVE_SEMAPHORE_H 1
-#endif
-
-// ABSL_HAVE_ALARM
-//
-// Checks whether the platform supports the <signal.h> header and alarm(2)
-// function as standardized in POSIX.1-2001.
-#ifdef ABSL_HAVE_ALARM
-#error ABSL_HAVE_ALARM cannot be directly set
-#elif defined(__GOOGLE_GRTE_VERSION__)
-// feature tests for Google's GRTE
-#define ABSL_HAVE_ALARM 1
-#elif defined(__GLIBC__)
-// feature test for glibc
-#define ABSL_HAVE_ALARM 1
-#elif defined(_MSC_VER)
-// feature tests for Microsoft's library
-#elif defined(__MINGW32__)
-// mingw32 doesn't provide alarm(2):
-// https://osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.2-trunk/mingwrt/include/unistd.h
-// mingw-w64 provides a no-op implementation:
-// https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c
-#elif defined(__EMSCRIPTEN__)
-// emscripten doesn't support signals
-#elif defined(__Fuchsia__)
-// Signals don't exist on fuchsia.
-#elif defined(__native_client__)
-#else
-// other standard libraries
-#define ABSL_HAVE_ALARM 1
-#endif
-
-// ABSL_IS_LITTLE_ENDIAN
-// ABSL_IS_BIG_ENDIAN
-//
-// Checks the endianness of the platform.
-//
-// Notes: uses the built in endian macros provided by GCC (since 4.6) and
-// Clang (since 3.2); see
-// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html.
-// Otherwise, if _WIN32, assume little endian. Otherwise, bail with an error.
-#if defined(ABSL_IS_BIG_ENDIAN)
-#error "ABSL_IS_BIG_ENDIAN cannot be directly set."
-#endif
-#if defined(ABSL_IS_LITTLE_ENDIAN)
-#error "ABSL_IS_LITTLE_ENDIAN cannot be directly set."
-#endif
-
-#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
-     __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
-#define ABSL_IS_LITTLE_ENDIAN 1
-#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
-    __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-#define ABSL_IS_BIG_ENDIAN 1
-#elif defined(_WIN32)
-#define ABSL_IS_LITTLE_ENDIAN 1
-#else
-#error "absl endian detection needs to be set up for your compiler"
-#endif
-
-// macOS 10.13 and iOS 10.11 don't let you use <any>, <optional>, or <variant>
-// even though the headers exist and are publicly noted to work.  See
-// https://github.com/abseil/abseil-cpp/issues/207 and
-// https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes
-// libc++ spells out the availability requirements in the file
-// llvm-project/libcxx/include/__config via the #define
-// _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS.
-#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \
-  ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
-   __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \
-  (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \
-   __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \
-  (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \
-   __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) || \
-  (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \
-   __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000))
-#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1
-#else
-#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0
-#endif
-
-// ABSL_HAVE_STD_ANY
-//
-// Checks whether C++17 std::any is available by checking whether <any> exists.
-#ifdef ABSL_HAVE_STD_ANY
-#error "ABSL_HAVE_STD_ANY cannot be directly set."
-#endif
-
-#ifdef __has_include
-#if __has_include(<any>) && __cplusplus >= 201703L && \
-    !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
-#define ABSL_HAVE_STD_ANY 1
-#endif
-#endif
-
-// ABSL_HAVE_STD_OPTIONAL
-//
-// Checks whether C++17 std::optional is available.
-#ifdef ABSL_HAVE_STD_OPTIONAL
-#error "ABSL_HAVE_STD_OPTIONAL cannot be directly set."
-#endif
-
-#ifdef __has_include
-#if __has_include(<optional>) && __cplusplus >= 201703L && \
-    !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
-#define ABSL_HAVE_STD_OPTIONAL 1
-#endif
-#endif
-
-// ABSL_HAVE_STD_VARIANT
-//
-// Checks whether C++17 std::variant is available.
-#ifdef ABSL_HAVE_STD_VARIANT
-#error "ABSL_HAVE_STD_VARIANT cannot be directly set."
-#endif
-
-#ifdef __has_include
-#if __has_include(<variant>) && __cplusplus >= 201703L && \
-    !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
-#define ABSL_HAVE_STD_VARIANT 1
-#endif
-#endif
-
-// ABSL_HAVE_STD_STRING_VIEW
-//
-// Checks whether C++17 std::string_view is available.
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-#error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set."
-#endif
-
-#ifdef __has_include
-#if __has_include(<string_view>) && __cplusplus >= 201703L
-#define ABSL_HAVE_STD_STRING_VIEW 1
-#endif
-#endif
-
-// For MSVC, `__has_include` is supported in VS 2017 15.3, which is later than
-// the support for <optional>, <any>, <string_view>, <variant>. So we use
-// _MSC_VER to check whether we have VS 2017 RTM (when <optional>, <any>,
-// <string_view>, <variant> is implemented) or higher. Also, `__cplusplus` is
-// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language
-// version.
-// TODO(zhangxy): fix tests before enabling aliasing for `std::any`.
-#if defined(_MSC_VER) && _MSC_VER >= 1910 && \
-    ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402)
-// #define ABSL_HAVE_STD_ANY 1
-#define ABSL_HAVE_STD_OPTIONAL 1
-#define ABSL_HAVE_STD_VARIANT 1
-#define ABSL_HAVE_STD_STRING_VIEW 1
-#endif
-
-// ABSL_USES_STD_ANY
-//
-// Indicates whether absl::any is an alias for std::any.
-#if !defined(ABSL_OPTION_USE_STD_ANY)
-#error options.h is misconfigured.
-#elif ABSL_OPTION_USE_STD_ANY == 0 || \
-    (ABSL_OPTION_USE_STD_ANY == 2 && !defined(ABSL_HAVE_STD_ANY))
-#undef ABSL_USES_STD_ANY
-#elif ABSL_OPTION_USE_STD_ANY == 1 || \
-    (ABSL_OPTION_USE_STD_ANY == 2 && defined(ABSL_HAVE_STD_ANY))
-#define ABSL_USES_STD_ANY 1
-#else
-#error options.h is misconfigured.
-#endif
-
-// ABSL_USES_STD_OPTIONAL
-//
-// Indicates whether absl::optional is an alias for std::optional.
-#if !defined(ABSL_OPTION_USE_STD_OPTIONAL)
-#error options.h is misconfigured.
-#elif ABSL_OPTION_USE_STD_OPTIONAL == 0 || \
-    (ABSL_OPTION_USE_STD_OPTIONAL == 2 && !defined(ABSL_HAVE_STD_OPTIONAL))
-#undef ABSL_USES_STD_OPTIONAL
-#elif ABSL_OPTION_USE_STD_OPTIONAL == 1 || \
-    (ABSL_OPTION_USE_STD_OPTIONAL == 2 && defined(ABSL_HAVE_STD_OPTIONAL))
-#define ABSL_USES_STD_OPTIONAL 1
-#else
-#error options.h is misconfigured.
-#endif
-
-// ABSL_USES_STD_VARIANT
-//
-// Indicates whether absl::variant is an alias for std::variant.
-#if !defined(ABSL_OPTION_USE_STD_VARIANT)
-#error options.h is misconfigured.
-#elif ABSL_OPTION_USE_STD_VARIANT == 0 || \
-    (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT))
-#undef ABSL_USES_STD_VARIANT
-#elif ABSL_OPTION_USE_STD_VARIANT == 1 || \
-    (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT))
-#define ABSL_USES_STD_VARIANT 1
-#else
-#error options.h is misconfigured.
-#endif
-
-// ABSL_USES_STD_STRING_VIEW
-//
-// Indicates whether absl::string_view is an alias for std::string_view.
-#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW)
-#error options.h is misconfigured.
-#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \
-    (ABSL_OPTION_USE_STD_STRING_VIEW == 2 &&  \
-     !defined(ABSL_HAVE_STD_STRING_VIEW))
-#undef ABSL_USES_STD_STRING_VIEW
-#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \
-    (ABSL_OPTION_USE_STD_STRING_VIEW == 2 &&  \
-     defined(ABSL_HAVE_STD_STRING_VIEW))
-#define ABSL_USES_STD_STRING_VIEW 1
-#else
-#error options.h is misconfigured.
-#endif
-
-// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION
-// SEH exception from emplace for variant<SomeStruct> when constructing the
-// struct can throw. This defeats some of variant_test and
-// variant_exception_safety_test.
-#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG)
-#define ABSL_INTERNAL_MSVC_2017_DBG_MODE
-#endif
-
-// ABSL_INTERNAL_MANGLED_NS
-// ABSL_INTERNAL_MANGLED_BACKREFERENCE
-//
-// Internal macros for building up mangled names in our internal fork of CCTZ.
-// This implementation detail is only needed and provided for the MSVC build.
-//
-// These macros both expand to string literals.  ABSL_INTERNAL_MANGLED_NS is
-// the mangled spelling of the `absl` namespace, and
-// ABSL_INTERNAL_MANGLED_BACKREFERENCE is a back-reference integer representing
-// the proper count to skip past the CCTZ fork namespace names.  (This number
-// is one larger when there is an inline namespace name to skip.)
-#if defined(_MSC_VER)
-#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0
-#define ABSL_INTERNAL_MANGLED_NS "absl"
-#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "5"
-#else
-#define ABSL_INTERNAL_MANGLED_NS \
-  ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) "@absl"
-#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "6"
-#endif
-#endif
-
-#undef ABSL_INTERNAL_HAS_KEYWORD
-
-// ABSL_DLL
-//
-// When building Abseil as a DLL, this macro expands to `__declspec(dllexport)`
-// so we can annotate symbols appropriately as being exported. When used in
-// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so
-// that consumers know the symbol is defined inside the DLL. In all other cases,
-// the macro expands to nothing.
-#if defined(_MSC_VER)
-#if defined(ABSL_BUILD_DLL)
-#define ABSL_DLL __declspec(dllexport)
-#elif defined(ABSL_CONSUME_DLL)
-#define ABSL_DLL __declspec(dllimport)
-#else
-#define ABSL_DLL
-#endif
-#else
-#define ABSL_DLL
-#endif  // defined(_MSC_VER)
-
-// ABSL_HAVE_MEMORY_SANITIZER
-//
-// MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of
-// a compiler instrumentation module and a run-time library.
-#ifdef ABSL_HAVE_MEMORY_SANITIZER
-#error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set."
-#elif defined(MEMORY_SANITIZER)
-// The MEMORY_SANITIZER macro is deprecated but we will continue to honor it
-// for now.
-#define ABSL_HAVE_MEMORY_SANITIZER 1
-#elif defined(__SANITIZE_MEMORY__)
-#define ABSL_HAVE_MEMORY_SANITIZER 1
-#elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer)
-#define ABSL_HAVE_MEMORY_SANITIZER 1
-#endif
-
-// ABSL_HAVE_THREAD_SANITIZER
-//
-// ThreadSanitizer (TSan) is a fast data race detector.
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-#error "ABSL_HAVE_THREAD_SANITIZER cannot be directly set."
-#elif defined(THREAD_SANITIZER)
-// The THREAD_SANITIZER macro is deprecated but we will continue to honor it
-// for now.
-#define ABSL_HAVE_THREAD_SANITIZER 1
-#elif defined(__SANITIZE_THREAD__)
-#define ABSL_HAVE_THREAD_SANITIZER 1
-#elif ABSL_HAVE_FEATURE(thread_sanitizer)
-#define ABSL_HAVE_THREAD_SANITIZER 1
-#endif
-
-// ABSL_HAVE_ADDRESS_SANITIZER
-//
-// AddressSanitizer (ASan) is a fast memory error detector.
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-#error "ABSL_HAVE_ADDRESS_SANITIZER cannot be directly set."
-#elif defined(ADDRESS_SANITIZER)
-// The ADDRESS_SANITIZER macro is deprecated but we will continue to honor it
-// for now.
-#define ABSL_HAVE_ADDRESS_SANITIZER 1
-#elif defined(__SANITIZE_ADDRESS__)
-#define ABSL_HAVE_ADDRESS_SANITIZER 1
-#elif ABSL_HAVE_FEATURE(address_sanitizer)
-#define ABSL_HAVE_ADDRESS_SANITIZER 1
-#endif
-
-#endif  // ABSL_BASE_CONFIG_H_
diff --git a/third_party/abseil_cpp/absl/base/config_test.cc b/third_party/abseil_cpp/absl/base/config_test.cc
deleted file mode 100644
index 7e0c033de5..0000000000
--- a/third_party/abseil_cpp/absl/base/config_test.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/config.h"
-
-#include <cstdint>
-
-#include "gtest/gtest.h"
-#include "absl/synchronization/internal/thread_pool.h"
-
-namespace {
-
-TEST(ConfigTest, Endianness) {
-  union {
-    uint32_t value;
-    uint8_t data[sizeof(uint32_t)];
-  } number;
-  number.data[0] = 0x00;
-  number.data[1] = 0x01;
-  number.data[2] = 0x02;
-  number.data[3] = 0x03;
-#if defined(ABSL_IS_LITTLE_ENDIAN) && defined(ABSL_IS_BIG_ENDIAN)
-#error Both ABSL_IS_LITTLE_ENDIAN and ABSL_IS_BIG_ENDIAN are defined
-#elif defined(ABSL_IS_LITTLE_ENDIAN)
-  EXPECT_EQ(UINT32_C(0x03020100), number.value);
-#elif defined(ABSL_IS_BIG_ENDIAN)
-  EXPECT_EQ(UINT32_C(0x00010203), number.value);
-#else
-#error Unknown endianness
-#endif
-}
-
-#if defined(ABSL_HAVE_THREAD_LOCAL)
-TEST(ConfigTest, ThreadLocal) {
-  static thread_local int mine_mine_mine = 16;
-  EXPECT_EQ(16, mine_mine_mine);
-  {
-    absl::synchronization_internal::ThreadPool pool(1);
-    pool.Schedule([&] {
-      EXPECT_EQ(16, mine_mine_mine);
-      mine_mine_mine = 32;
-      EXPECT_EQ(32, mine_mine_mine);
-    });
-  }
-  EXPECT_EQ(16, mine_mine_mine);
-}
-#endif
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/const_init.h b/third_party/abseil_cpp/absl/base/const_init.h
deleted file mode 100644
index 16520b61d9..0000000000
--- a/third_party/abseil_cpp/absl/base/const_init.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// kConstInit
-// -----------------------------------------------------------------------------
-//
-// A constructor tag used to mark an object as safe for use as a global
-// variable, avoiding the usual lifetime issues that can affect globals.
-
-#ifndef ABSL_BASE_CONST_INIT_H_
-#define ABSL_BASE_CONST_INIT_H_
-
-#include "absl/base/config.h"
-
-// In general, objects with static storage duration (such as global variables)
-// can trigger tricky object lifetime situations.  Attempting to access them
-// from the constructors or destructors of other global objects can result in
-// undefined behavior, unless their constructors and destructors are designed
-// with this issue in mind.
-//
-// The normal way to deal with this issue in C++11 is to use constant
-// initialization and trivial destructors.
-//
-// Constant initialization is guaranteed to occur before any other code
-// executes.  Constructors that are declared 'constexpr' are eligible for
-// constant initialization.  You can annotate a variable declaration with the
-// ABSL_CONST_INIT macro to express this intent.  For compilers that support
-// it, this annotation will cause a compilation error for declarations that
-// aren't subject to constant initialization (perhaps because a runtime value
-// was passed as a constructor argument).
-//
-// On program shutdown, lifetime issues can be avoided on global objects by
-// ensuring that they contain  trivial destructors.  A class has a trivial
-// destructor unless it has a user-defined destructor, a virtual method or base
-// class, or a data member or base class with a non-trivial destructor of its
-// own.  Objects with static storage duration and a trivial destructor are not
-// cleaned up on program shutdown, and are thus safe to access from other code
-// running during shutdown.
-//
-// For a few core Abseil classes, we make a best effort to allow for safe global
-// instances, even though these classes have non-trivial destructors.  These
-// objects can be created with the absl::kConstInit tag.  For example:
-//   ABSL_CONST_INIT absl::Mutex global_mutex(absl::kConstInit);
-//
-// The line above declares a global variable of type absl::Mutex which can be
-// accessed at any point during startup or shutdown.  global_mutex's destructor
-// will still run, but will not invalidate the object.  Note that C++ specifies
-// that accessing an object after its destructor has run results in undefined
-// behavior, but this pattern works on the toolchains we support.
-//
-// The absl::kConstInit tag should only be used to define objects with static
-// or thread_local storage duration.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-enum ConstInitType {
-  kConstInit,
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_CONST_INIT_H_
diff --git a/third_party/abseil_cpp/absl/base/dynamic_annotations.h b/third_party/abseil_cpp/absl/base/dynamic_annotations.h
deleted file mode 100644
index 545f8cbc91..0000000000
--- a/third_party/abseil_cpp/absl/base/dynamic_annotations.h
+++ /dev/null
@@ -1,482 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file defines dynamic annotations for use with dynamic analysis tool
-// such as valgrind, PIN, etc.
-//
-// Dynamic annotation is a source code annotation that affects the generated
-// code (that is, the annotation is not a comment). Each such annotation is
-// attached to a particular instruction and/or to a particular object (address)
-// in the program.
-//
-// The annotations that should be used by users are macros in all upper-case
-// (e.g., ABSL_ANNOTATE_THREAD_NAME).
-//
-// Actual implementation of these macros may differ depending on the dynamic
-// analysis tool being used.
-//
-// This file supports the following configurations:
-// - Dynamic Annotations enabled (with static thread-safety warnings disabled).
-//   In this case, macros expand to functions implemented by Thread Sanitizer,
-//   when building with TSan. When not provided an external implementation,
-//   dynamic_annotations.cc provides no-op implementations.
-//
-// - Static Clang thread-safety warnings enabled.
-//   When building with a Clang compiler that supports thread-safety warnings,
-//   a subset of annotations can be statically-checked at compile-time. We
-//   expand these macros to static-inline functions that can be analyzed for
-//   thread-safety, but afterwards elided when building the final binary.
-//
-// - All annotations are disabled.
-//   If neither Dynamic Annotations nor Clang thread-safety warnings are
-//   enabled, then all annotation-macros expand to empty.
-
-#ifndef ABSL_BASE_DYNAMIC_ANNOTATIONS_H_
-#define ABSL_BASE_DYNAMIC_ANNOTATIONS_H_
-
-#include <stddef.h>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#ifdef __cplusplus
-#include "absl/base/macros.h"
-#endif
-
-// TODO(rogeeff): Remove after the backward compatibility period.
-#include "absl/base/internal/dynamic_annotations.h"  // IWYU pragma: export
-
-// -------------------------------------------------------------------------
-// Decide which features are enabled.
-
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-
-#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1
-#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1
-#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1
-#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0
-#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1
-
-#else
-
-#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0
-#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0
-#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0
-
-// Clang provides limited support for static thread-safety analysis through a
-// feature called Annotalysis. We configure macro-definitions according to
-// whether Annotalysis support is available. When running in opt-mode, GCC
-// will issue a warning, if these attributes are compiled. Only include them
-// when compiling using Clang.
-
-#if defined(__clang__)
-#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 1
-#if !defined(SWIG)
-#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1
-#endif
-#else
-#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0
-#endif
-
-// Read/write annotations are enabled in Annotalysis mode; disabled otherwise.
-#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \
-  ABSL_INTERNAL_ANNOTALYSIS_ENABLED
-
-#endif  // ABSL_HAVE_THREAD_SANITIZER
-
-#ifdef __cplusplus
-#define ABSL_INTERNAL_BEGIN_EXTERN_C extern "C" {
-#define ABSL_INTERNAL_END_EXTERN_C }  // extern "C"
-#define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F
-#define ABSL_INTERNAL_STATIC_INLINE inline
-#else
-#define ABSL_INTERNAL_BEGIN_EXTERN_C  // empty
-#define ABSL_INTERNAL_END_EXTERN_C    // empty
-#define ABSL_INTERNAL_GLOBAL_SCOPED(F) F
-#define ABSL_INTERNAL_STATIC_INLINE static inline
-#endif
-
-// -------------------------------------------------------------------------
-// Define race annotations.
-
-#if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1
-
-// -------------------------------------------------------------
-// Annotations that suppress errors. It is usually better to express the
-// program's synchronization using the other annotations, but these can be used
-// when all else fails.
-
-// Report that we may have a benign race at `pointer`, with size
-// "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the
-// point where `pointer` has been allocated, preferably close to the point
-// where the race happens. See also ABSL_ANNOTATE_BENIGN_RACE_STATIC.
-#define ABSL_ANNOTATE_BENIGN_RACE(pointer, description) \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized)  \
-  (__FILE__, __LINE__, pointer, sizeof(*(pointer)), description)
-
-// Same as ABSL_ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to
-// the memory range [`address`, `address`+`size`).
-#define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized)              \
-  (__FILE__, __LINE__, address, size, description)
-
-// Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads.
-// This annotation could be useful if you want to skip expensive race analysis
-// during some period of program execution, e.g. during initialization.
-#define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable)        \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \
-  (__FILE__, __LINE__, enable)
-
-// -------------------------------------------------------------
-// Annotations useful for debugging.
-
-// Report the current thread `name` to a race detector.
-#define ABSL_ANNOTATE_THREAD_NAME(name) \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName)(__FILE__, __LINE__, name)
-
-// -------------------------------------------------------------
-// Annotations useful when implementing locks. They are not normally needed by
-// modules that merely use locks. The `lock` argument is a pointer to the lock
-// object.
-
-// Report that a lock has been created at address `lock`.
-#define ABSL_ANNOTATE_RWLOCK_CREATE(lock) \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate)(__FILE__, __LINE__, lock)
-
-// Report that a linker initialized lock has been created at address `lock`.
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock)          \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \
-  (__FILE__, __LINE__, lock)
-#else
-#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) \
-  ABSL_ANNOTATE_RWLOCK_CREATE(lock)
-#endif
-
-// Report that the lock at address `lock` is about to be destroyed.
-#define ABSL_ANNOTATE_RWLOCK_DESTROY(lock) \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock)
-
-// Report that the lock at address `lock` has been acquired.
-// `is_w`=1 for writer lock, `is_w`=0 for reader lock.
-#define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w)     \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \
-  (__FILE__, __LINE__, lock, is_w)
-
-// Report that the lock at address `lock` is about to be released.
-// `is_w`=1 for writer lock, `is_w`=0 for reader lock.
-#define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w)     \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \
-  (__FILE__, __LINE__, lock, is_w)
-
-// Apply ABSL_ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`.
-#define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description)      \
-  namespace {                                                          \
-  class static_var##_annotator {                                       \
-   public:                                                             \
-    static_var##_annotator() {                                         \
-      ABSL_ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \
-                                      #static_var ": " description);   \
-    }                                                                  \
-  };                                                                   \
-  static static_var##_annotator the##static_var##_annotator;           \
-  }  // namespace
-
-// Function prototypes of annotations provided by the compiler-based sanitizer
-// implementation.
-ABSL_INTERNAL_BEGIN_EXTERN_C
-void AnnotateRWLockCreate(const char* file, int line,
-                          const volatile void* lock);
-void AnnotateRWLockCreateStatic(const char* file, int line,
-                                const volatile void* lock);
-void AnnotateRWLockDestroy(const char* file, int line,
-                           const volatile void* lock);
-void AnnotateRWLockAcquired(const char* file, int line,
-                            const volatile void* lock, long is_w);  // NOLINT
-void AnnotateRWLockReleased(const char* file, int line,
-                            const volatile void* lock, long is_w);  // NOLINT
-void AnnotateBenignRace(const char* file, int line,
-                        const volatile void* address, const char* description);
-void AnnotateBenignRaceSized(const char* file, int line,
-                             const volatile void* address, size_t size,
-                             const char* description);
-void AnnotateThreadName(const char* file, int line, const char* name);
-void AnnotateEnableRaceDetection(const char* file, int line, int enable);
-ABSL_INTERNAL_END_EXTERN_C
-
-#else  // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0
-
-#define ABSL_ANNOTATE_RWLOCK_CREATE(lock)                            // empty
-#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock)                     // empty
-#define ABSL_ANNOTATE_RWLOCK_DESTROY(lock)                           // empty
-#define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w)                    // empty
-#define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w)                    // empty
-#define ABSL_ANNOTATE_BENIGN_RACE(address, description)              // empty
-#define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description)  // empty
-#define ABSL_ANNOTATE_THREAD_NAME(name)                              // empty
-#define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable)                  // empty
-#define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description)    // empty
-
-#endif  // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED
-
-// -------------------------------------------------------------------------
-// Define memory annotations.
-
-#ifdef ABSL_HAVE_MEMORY_SANITIZER
-
-#include <sanitizer/msan_interface.h>
-
-#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
-  __msan_unpoison(address, size)
-
-#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
-  __msan_allocated_memory(address, size)
-
-#else  // !defined(ABSL_HAVE_MEMORY_SANITIZER)
-
-// TODO(rogeeff): remove this branch
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
-  do {                                                     \
-    (void)(address);                                       \
-    (void)(size);                                          \
-  } while (0)
-#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
-  do {                                                       \
-    (void)(address);                                         \
-    (void)(size);                                            \
-  } while (0)
-#else
-
-#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size)    // empty
-#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size)  // empty
-
-#endif
-
-#endif  // ABSL_HAVE_MEMORY_SANITIZER
-
-// -------------------------------------------------------------------------
-// Define IGNORE_READS_BEGIN/_END attributes.
-
-#if defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED)
-
-#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \
-  __attribute((exclusive_lock_function("*")))
-#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \
-  __attribute((unlock_function("*")))
-
-#else  // !defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED)
-
-#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE  // empty
-#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE    // empty
-
-#endif  // defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED)
-
-// -------------------------------------------------------------------------
-// Define IGNORE_READS_BEGIN/_END annotations.
-
-#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1
-
-// Request the analysis tool to ignore all reads in the current thread until
-// ABSL_ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey
-// reads, while still checking other reads and all writes.
-// See also ABSL_ANNOTATE_UNPROTECTED_READ.
-#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__)
-
-// Stop ignoring reads.
-#define ABSL_ANNOTATE_IGNORE_READS_END() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__)
-
-// Function prototypes of annotations provided by the compiler-based sanitizer
-// implementation.
-ABSL_INTERNAL_BEGIN_EXTERN_C
-void AnnotateIgnoreReadsBegin(const char* file, int line)
-    ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE;
-void AnnotateIgnoreReadsEnd(const char* file,
-                            int line) ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE;
-ABSL_INTERNAL_END_EXTERN_C
-
-#elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED)
-
-// When Annotalysis is enabled without Dynamic Annotations, the use of
-// static-inline functions allows the annotations to be read at compile-time,
-// while still letting the compiler elide the functions from the final build.
-//
-// TODO(delesley) -- The exclusive lock here ignores writes as well, but
-// allows IGNORE_READS_AND_WRITES to work properly.
-
-#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin)()
-
-#define ABSL_ANNOTATE_IGNORE_READS_END() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd)()
-
-ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsBegin()
-    ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE {}
-
-ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsEnd()
-    ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE {}
-
-#else
-
-#define ABSL_ANNOTATE_IGNORE_READS_BEGIN()  // empty
-#define ABSL_ANNOTATE_IGNORE_READS_END()    // empty
-
-#endif
-
-// -------------------------------------------------------------------------
-// Define IGNORE_WRITES_BEGIN/_END annotations.
-
-#if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1
-
-// Similar to ABSL_ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead.
-#define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__)
-
-// Stop ignoring writes.
-#define ABSL_ANNOTATE_IGNORE_WRITES_END() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__)
-
-// Function prototypes of annotations provided by the compiler-based sanitizer
-// implementation.
-ABSL_INTERNAL_BEGIN_EXTERN_C
-void AnnotateIgnoreWritesBegin(const char* file, int line);
-void AnnotateIgnoreWritesEnd(const char* file, int line);
-ABSL_INTERNAL_END_EXTERN_C
-
-#else
-
-#define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN()  // empty
-#define ABSL_ANNOTATE_IGNORE_WRITES_END()    // empty
-
-#endif
-
-// -------------------------------------------------------------------------
-// Define the ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more
-// primitive annotations defined above.
-//
-//     Instead of doing
-//        ABSL_ANNOTATE_IGNORE_READS_BEGIN();
-//        ... = x;
-//        ABSL_ANNOTATE_IGNORE_READS_END();
-//     one can use
-//        ... = ABSL_ANNOTATE_UNPROTECTED_READ(x);
-
-#if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED)
-
-// Start ignoring all memory accesses (both reads and writes).
-#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
-  do {                                                \
-    ABSL_ANNOTATE_IGNORE_READS_BEGIN();               \
-    ABSL_ANNOTATE_IGNORE_WRITES_BEGIN();              \
-  } while (0)
-
-// Stop ignoring both reads and writes.
-#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END() \
-  do {                                              \
-    ABSL_ANNOTATE_IGNORE_WRITES_END();              \
-    ABSL_ANNOTATE_IGNORE_READS_END();               \
-  } while (0)
-
-#ifdef __cplusplus
-// ABSL_ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads.
-#define ABSL_ANNOTATE_UNPROTECTED_READ(x) \
-  absl::base_internal::AnnotateUnprotectedRead(x)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-template <typename T>
-inline T AnnotateUnprotectedRead(const volatile T& x) {  // NOLINT
-  ABSL_ANNOTATE_IGNORE_READS_BEGIN();
-  T res = x;
-  ABSL_ANNOTATE_IGNORE_READS_END();
-  return res;
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-#endif
-
-#else
-
-#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN()  // empty
-#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END()    // empty
-#define ABSL_ANNOTATE_UNPROTECTED_READ(x) (x)
-
-#endif
-
-#ifdef __cplusplus
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-ABSL_INTERNAL_BEGIN_EXTERN_C
-int RunningOnValgrind();
-double ValgrindSlowdown();
-ABSL_INTERNAL_END_EXTERN_C
-#else
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-ABSL_DEPRECATED(
-    "Don't use this interface. It is misleading and is being deleted.")
-ABSL_ATTRIBUTE_ALWAYS_INLINE inline int RunningOnValgrind() { return 0; }
-ABSL_DEPRECATED(
-    "Don't use this interface. It is misleading and is being deleted.")
-ABSL_ATTRIBUTE_ALWAYS_INLINE inline double ValgrindSlowdown() { return 1.0; }
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-using absl::base_internal::RunningOnValgrind;
-using absl::base_internal::ValgrindSlowdown;
-#endif
-#endif
-
-// -------------------------------------------------------------------------
-// Address sanitizer annotations
-
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-// Describe the current state of a contiguous container such as e.g.
-// std::vector or std::string. For more details see
-// sanitizer/common_interface_defs.h, which is provided by the compiler.
-#include <sanitizer/common_interface_defs.h>
-
-#define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \
-  __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid)
-#define ABSL_ADDRESS_SANITIZER_REDZONE(name) \
-  struct {                                   \
-    char x[8] __attribute__((aligned(8)));   \
-  } name
-
-#else
-
-#define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid)  // empty
-#define ABSL_ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "")
-
-#endif  // ABSL_HAVE_ADDRESS_SANITIZER
-
-// -------------------------------------------------------------------------
-// Undefine the macros intended only for this file.
-
-#undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED
-#undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED
-#undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED
-#undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED
-#undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED
-#undef ABSL_INTERNAL_BEGIN_EXTERN_C
-#undef ABSL_INTERNAL_END_EXTERN_C
-#undef ABSL_INTERNAL_STATIC_INLINE
-
-#endif  // ABSL_BASE_DYNAMIC_ANNOTATIONS_H_
diff --git a/third_party/abseil_cpp/absl/base/exception_safety_testing_test.cc b/third_party/abseil_cpp/absl/base/exception_safety_testing_test.cc
deleted file mode 100644
index a59be29e91..0000000000
--- a/third_party/abseil_cpp/absl/base/exception_safety_testing_test.cc
+++ /dev/null
@@ -1,956 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/exception_safety_testing.h"
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-#include <cstddef>
-#include <exception>
-#include <iostream>
-#include <list>
-#include <type_traits>
-#include <vector>
-
-#include "gtest/gtest-spi.h"
-#include "gtest/gtest.h"
-#include "absl/memory/memory.h"
-
-namespace testing {
-
-namespace {
-
-using ::testing::exceptions_internal::SetCountdown;
-using ::testing::exceptions_internal::TestException;
-using ::testing::exceptions_internal::UnsetCountdown;
-
-// EXPECT_NO_THROW can't inspect the thrown inspection in general.
-template <typename F>
-void ExpectNoThrow(const F& f) {
-  try {
-    f();
-  } catch (const TestException& e) {
-    ADD_FAILURE() << "Unexpected exception thrown from " << e.what();
-  }
-}
-
-TEST(ThrowingValueTest, Throws) {
-  SetCountdown();
-  EXPECT_THROW(ThrowingValue<> bomb, TestException);
-
-  // It's not guaranteed that every operator only throws *once*.  The default
-  // ctor only throws once, though, so use it to make sure we only throw when
-  // the countdown hits 0
-  SetCountdown(2);
-  ExpectNoThrow([]() { ThrowingValue<> bomb; });
-  ExpectNoThrow([]() { ThrowingValue<> bomb; });
-  EXPECT_THROW(ThrowingValue<> bomb, TestException);
-
-  UnsetCountdown();
-}
-
-// Tests that an operation throws when the countdown is at 0, doesn't throw when
-// the countdown doesn't hit 0, and doesn't modify the state of the
-// ThrowingValue if it throws
-template <typename F>
-void TestOp(const F& f) {
-  ExpectNoThrow(f);
-
-  SetCountdown();
-  EXPECT_THROW(f(), TestException);
-  UnsetCountdown();
-}
-
-TEST(ThrowingValueTest, ThrowingCtors) {
-  ThrowingValue<> bomb;
-
-  TestOp([]() { ThrowingValue<> bomb(1); });
-  TestOp([&]() { ThrowingValue<> bomb1 = bomb; });
-  TestOp([&]() { ThrowingValue<> bomb1 = std::move(bomb); });
-}
-
-TEST(ThrowingValueTest, ThrowingAssignment) {
-  ThrowingValue<> bomb, bomb1;
-
-  TestOp([&]() { bomb = bomb1; });
-  TestOp([&]() { bomb = std::move(bomb1); });
-
-  // Test that when assignment throws, the assignment should fail (lhs != rhs)
-  // and strong guarantee fails (lhs != lhs_copy).
-  {
-    ThrowingValue<> lhs(39), rhs(42);
-    ThrowingValue<> lhs_copy(lhs);
-    SetCountdown();
-    EXPECT_THROW(lhs = rhs, TestException);
-    UnsetCountdown();
-    EXPECT_NE(lhs, rhs);
-    EXPECT_NE(lhs_copy, lhs);
-  }
-  {
-    ThrowingValue<> lhs(39), rhs(42);
-    ThrowingValue<> lhs_copy(lhs), rhs_copy(rhs);
-    SetCountdown();
-    EXPECT_THROW(lhs = std::move(rhs), TestException);
-    UnsetCountdown();
-    EXPECT_NE(lhs, rhs_copy);
-    EXPECT_NE(lhs_copy, lhs);
-  }
-}
-
-TEST(ThrowingValueTest, ThrowingComparisons) {
-  ThrowingValue<> bomb1, bomb2;
-  TestOp([&]() { return bomb1 == bomb2; });
-  TestOp([&]() { return bomb1 != bomb2; });
-  TestOp([&]() { return bomb1 < bomb2; });
-  TestOp([&]() { return bomb1 <= bomb2; });
-  TestOp([&]() { return bomb1 > bomb2; });
-  TestOp([&]() { return bomb1 >= bomb2; });
-}
-
-TEST(ThrowingValueTest, ThrowingArithmeticOps) {
-  ThrowingValue<> bomb1(1), bomb2(2);
-
-  TestOp([&bomb1]() { +bomb1; });
-  TestOp([&bomb1]() { -bomb1; });
-  TestOp([&bomb1]() { ++bomb1; });
-  TestOp([&bomb1]() { bomb1++; });
-  TestOp([&bomb1]() { --bomb1; });
-  TestOp([&bomb1]() { bomb1--; });
-
-  TestOp([&]() { bomb1 + bomb2; });
-  TestOp([&]() { bomb1 - bomb2; });
-  TestOp([&]() { bomb1* bomb2; });
-  TestOp([&]() { bomb1 / bomb2; });
-  TestOp([&]() { bomb1 << 1; });
-  TestOp([&]() { bomb1 >> 1; });
-}
-
-TEST(ThrowingValueTest, ThrowingLogicalOps) {
-  ThrowingValue<> bomb1, bomb2;
-
-  TestOp([&bomb1]() { !bomb1; });
-  TestOp([&]() { bomb1&& bomb2; });
-  TestOp([&]() { bomb1 || bomb2; });
-}
-
-TEST(ThrowingValueTest, ThrowingBitwiseOps) {
-  ThrowingValue<> bomb1, bomb2;
-
-  TestOp([&bomb1]() { ~bomb1; });
-  TestOp([&]() { bomb1& bomb2; });
-  TestOp([&]() { bomb1 | bomb2; });
-  TestOp([&]() { bomb1 ^ bomb2; });
-}
-
-TEST(ThrowingValueTest, ThrowingCompoundAssignmentOps) {
-  ThrowingValue<> bomb1(1), bomb2(2);
-
-  TestOp([&]() { bomb1 += bomb2; });
-  TestOp([&]() { bomb1 -= bomb2; });
-  TestOp([&]() { bomb1 *= bomb2; });
-  TestOp([&]() { bomb1 /= bomb2; });
-  TestOp([&]() { bomb1 %= bomb2; });
-  TestOp([&]() { bomb1 &= bomb2; });
-  TestOp([&]() { bomb1 |= bomb2; });
-  TestOp([&]() { bomb1 ^= bomb2; });
-  TestOp([&]() { bomb1 *= bomb2; });
-}
-
-TEST(ThrowingValueTest, ThrowingStreamOps) {
-  ThrowingValue<> bomb;
-
-  TestOp([&]() {
-    std::istringstream stream;
-    stream >> bomb;
-  });
-  TestOp([&]() {
-    std::stringstream stream;
-    stream << bomb;
-  });
-}
-
-// Tests the operator<< of ThrowingValue by forcing ConstructorTracker to emit
-// a nonfatal failure that contains the string representation of the Thrower
-TEST(ThrowingValueTest, StreamOpsOutput) {
-  using ::testing::TypeSpec;
-  exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
-
-  // Test default spec list (kEverythingThrows)
-  EXPECT_NONFATAL_FAILURE(
-      {
-        using Thrower = ThrowingValue<TypeSpec{}>;
-        auto thrower = Thrower(123);
-        thrower.~Thrower();
-      },
-      "ThrowingValue<>(123)");
-
-  // Test with one item in spec list (kNoThrowCopy)
-  EXPECT_NONFATAL_FAILURE(
-      {
-        using Thrower = ThrowingValue<TypeSpec::kNoThrowCopy>;
-        auto thrower = Thrower(234);
-        thrower.~Thrower();
-      },
-      "ThrowingValue<kNoThrowCopy>(234)");
-
-  // Test with multiple items in spec list (kNoThrowMove, kNoThrowNew)
-  EXPECT_NONFATAL_FAILURE(
-      {
-        using Thrower =
-            ThrowingValue<TypeSpec::kNoThrowMove | TypeSpec::kNoThrowNew>;
-        auto thrower = Thrower(345);
-        thrower.~Thrower();
-      },
-      "ThrowingValue<kNoThrowMove | kNoThrowNew>(345)");
-
-  // Test with all items in spec list (kNoThrowCopy, kNoThrowMove, kNoThrowNew)
-  EXPECT_NONFATAL_FAILURE(
-      {
-        using Thrower = ThrowingValue<static_cast<TypeSpec>(-1)>;
-        auto thrower = Thrower(456);
-        thrower.~Thrower();
-      },
-      "ThrowingValue<kNoThrowCopy | kNoThrowMove | kNoThrowNew>(456)");
-}
-
-template <typename F>
-void TestAllocatingOp(const F& f) {
-  ExpectNoThrow(f);
-
-  SetCountdown();
-  EXPECT_THROW(f(), exceptions_internal::TestBadAllocException);
-  UnsetCountdown();
-}
-
-TEST(ThrowingValueTest, ThrowingAllocatingOps) {
-  // make_unique calls unqualified operator new, so these exercise the
-  // ThrowingValue overloads.
-  TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>>(1); });
-  TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>[]>(2); });
-}
-
-TEST(ThrowingValueTest, NonThrowingMoveCtor) {
-  ThrowingValue<TypeSpec::kNoThrowMove> nothrow_ctor;
-
-  SetCountdown();
-  ExpectNoThrow([&nothrow_ctor]() {
-    ThrowingValue<TypeSpec::kNoThrowMove> nothrow1 = std::move(nothrow_ctor);
-  });
-  UnsetCountdown();
-}
-
-TEST(ThrowingValueTest, NonThrowingMoveAssign) {
-  ThrowingValue<TypeSpec::kNoThrowMove> nothrow_assign1, nothrow_assign2;
-
-  SetCountdown();
-  ExpectNoThrow([&nothrow_assign1, &nothrow_assign2]() {
-    nothrow_assign1 = std::move(nothrow_assign2);
-  });
-  UnsetCountdown();
-}
-
-TEST(ThrowingValueTest, ThrowingCopyCtor) {
-  ThrowingValue<> tv;
-
-  TestOp([&]() { ThrowingValue<> tv_copy(tv); });
-}
-
-TEST(ThrowingValueTest, ThrowingCopyAssign) {
-  ThrowingValue<> tv1, tv2;
-
-  TestOp([&]() { tv1 = tv2; });
-}
-
-TEST(ThrowingValueTest, NonThrowingCopyCtor) {
-  ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_ctor;
-
-  SetCountdown();
-  ExpectNoThrow([&nothrow_ctor]() {
-    ThrowingValue<TypeSpec::kNoThrowCopy> nothrow1(nothrow_ctor);
-  });
-  UnsetCountdown();
-}
-
-TEST(ThrowingValueTest, NonThrowingCopyAssign) {
-  ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_assign1, nothrow_assign2;
-
-  SetCountdown();
-  ExpectNoThrow([&nothrow_assign1, &nothrow_assign2]() {
-    nothrow_assign1 = nothrow_assign2;
-  });
-  UnsetCountdown();
-}
-
-TEST(ThrowingValueTest, ThrowingSwap) {
-  ThrowingValue<> bomb1, bomb2;
-  TestOp([&]() { std::swap(bomb1, bomb2); });
-}
-
-TEST(ThrowingValueTest, NonThrowingSwap) {
-  ThrowingValue<TypeSpec::kNoThrowMove> bomb1, bomb2;
-  ExpectNoThrow([&]() { std::swap(bomb1, bomb2); });
-}
-
-TEST(ThrowingValueTest, NonThrowingAllocation) {
-  ThrowingValue<TypeSpec::kNoThrowNew>* allocated;
-  ThrowingValue<TypeSpec::kNoThrowNew>* array;
-
-  ExpectNoThrow([&allocated]() {
-    allocated = new ThrowingValue<TypeSpec::kNoThrowNew>(1);
-    delete allocated;
-  });
-  ExpectNoThrow([&array]() {
-    array = new ThrowingValue<TypeSpec::kNoThrowNew>[2];
-    delete[] array;
-  });
-}
-
-TEST(ThrowingValueTest, NonThrowingDelete) {
-  auto* allocated = new ThrowingValue<>(1);
-  auto* array = new ThrowingValue<>[2];
-
-  SetCountdown();
-  ExpectNoThrow([allocated]() { delete allocated; });
-  SetCountdown();
-  ExpectNoThrow([array]() { delete[] array; });
-
-  UnsetCountdown();
-}
-
-TEST(ThrowingValueTest, NonThrowingPlacementDelete) {
-  constexpr int kArrayLen = 2;
-  // We intentionally create extra space to store the tag allocated by placement
-  // new[].
-  constexpr int kStorageLen = 4;
-
-  alignas(ThrowingValue<>) unsigned char buf[sizeof(ThrowingValue<>)];
-  alignas(ThrowingValue<>) unsigned char
-      array_buf[sizeof(ThrowingValue<>[kStorageLen])];
-  auto* placed = new (&buf) ThrowingValue<>(1);
-  auto placed_array = new (&array_buf) ThrowingValue<>[kArrayLen];
-
-  SetCountdown();
-  ExpectNoThrow([placed, &buf]() {
-    placed->~ThrowingValue<>();
-    ThrowingValue<>::operator delete(placed, &buf);
-  });
-
-  SetCountdown();
-  ExpectNoThrow([&, placed_array]() {
-    for (int i = 0; i < kArrayLen; ++i) placed_array[i].~ThrowingValue<>();
-    ThrowingValue<>::operator delete[](placed_array, &array_buf);
-  });
-
-  UnsetCountdown();
-}
-
-TEST(ThrowingValueTest, NonThrowingDestructor) {
-  auto* allocated = new ThrowingValue<>();
-
-  SetCountdown();
-  ExpectNoThrow([allocated]() { delete allocated; });
-  UnsetCountdown();
-}
-
-TEST(ThrowingBoolTest, ThrowingBool) {
-  ThrowingBool t = true;
-
-  // Test that it's contextually convertible to bool
-  if (t) {  // NOLINT(whitespace/empty_if_body)
-  }
-  EXPECT_TRUE(t);
-
-  TestOp([&]() { (void)!t; });
-}
-
-TEST(ThrowingAllocatorTest, MemoryManagement) {
-  // Just exercise the memory management capabilities under LSan to make sure we
-  // don't leak.
-  ThrowingAllocator<int> int_alloc;
-  int* ip = int_alloc.allocate(1);
-  int_alloc.deallocate(ip, 1);
-  int* i_array = int_alloc.allocate(2);
-  int_alloc.deallocate(i_array, 2);
-
-  ThrowingAllocator<ThrowingValue<>> tv_alloc;
-  ThrowingValue<>* ptr = tv_alloc.allocate(1);
-  tv_alloc.deallocate(ptr, 1);
-  ThrowingValue<>* tv_array = tv_alloc.allocate(2);
-  tv_alloc.deallocate(tv_array, 2);
-}
-
-TEST(ThrowingAllocatorTest, CallsGlobalNew) {
-  ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate> nothrow_alloc;
-  ThrowingValue<>* ptr;
-
-  SetCountdown();
-  // This will only throw if ThrowingValue::new is called.
-  ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); });
-  nothrow_alloc.deallocate(ptr, 1);
-
-  UnsetCountdown();
-}
-
-TEST(ThrowingAllocatorTest, ThrowingConstructors) {
-  ThrowingAllocator<int> int_alloc;
-  int* ip = nullptr;
-
-  SetCountdown();
-  EXPECT_THROW(ip = int_alloc.allocate(1), TestException);
-  ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
-
-  *ip = 1;
-  SetCountdown();
-  EXPECT_THROW(int_alloc.construct(ip, 2), TestException);
-  EXPECT_EQ(*ip, 1);
-  int_alloc.deallocate(ip, 1);
-
-  UnsetCountdown();
-}
-
-TEST(ThrowingAllocatorTest, NonThrowingConstruction) {
-  {
-    ThrowingAllocator<int, AllocSpec::kNoThrowAllocate> int_alloc;
-    int* ip = nullptr;
-
-    SetCountdown();
-    ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
-
-    SetCountdown();
-    ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
-
-    EXPECT_EQ(*ip, 2);
-    int_alloc.deallocate(ip, 1);
-
-    UnsetCountdown();
-  }
-
-  {
-    ThrowingAllocator<int> int_alloc;
-    int* ip = nullptr;
-    ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
-    ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
-    EXPECT_EQ(*ip, 2);
-    int_alloc.deallocate(ip, 1);
-  }
-
-  {
-    ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate>
-        nothrow_alloc;
-    ThrowingValue<>* ptr;
-
-    SetCountdown();
-    ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); });
-
-    SetCountdown();
-    ExpectNoThrow(
-        [&]() { nothrow_alloc.construct(ptr, 2, testing::nothrow_ctor); });
-
-    EXPECT_EQ(ptr->Get(), 2);
-    nothrow_alloc.destroy(ptr);
-    nothrow_alloc.deallocate(ptr, 1);
-
-    UnsetCountdown();
-  }
-
-  {
-    ThrowingAllocator<int> a;
-
-    SetCountdown();
-    ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = a; });
-
-    SetCountdown();
-    ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = std::move(a); });
-
-    UnsetCountdown();
-  }
-}
-
-TEST(ThrowingAllocatorTest, ThrowingAllocatorConstruction) {
-  ThrowingAllocator<int> a;
-  TestOp([]() { ThrowingAllocator<int> a; });
-  TestOp([&]() { a.select_on_container_copy_construction(); });
-}
-
-TEST(ThrowingAllocatorTest, State) {
-  ThrowingAllocator<int> a1, a2;
-  EXPECT_NE(a1, a2);
-
-  auto a3 = a1;
-  EXPECT_EQ(a3, a1);
-  int* ip = a1.allocate(1);
-  EXPECT_EQ(a3, a1);
-  a3.deallocate(ip, 1);
-  EXPECT_EQ(a3, a1);
-}
-
-TEST(ThrowingAllocatorTest, InVector) {
-  std::vector<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> v;
-  for (int i = 0; i < 20; ++i) v.push_back({});
-  for (int i = 0; i < 20; ++i) v.pop_back();
-}
-
-TEST(ThrowingAllocatorTest, InList) {
-  std::list<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> l;
-  for (int i = 0; i < 20; ++i) l.push_back({});
-  for (int i = 0; i < 20; ++i) l.pop_back();
-  for (int i = 0; i < 20; ++i) l.push_front({});
-  for (int i = 0; i < 20; ++i) l.pop_front();
-}
-
-template <typename TesterInstance, typename = void>
-struct NullaryTestValidator : public std::false_type {};
-
-template <typename TesterInstance>
-struct NullaryTestValidator<
-    TesterInstance,
-    absl::void_t<decltype(std::declval<TesterInstance>().Test())>>
-    : public std::true_type {};
-
-template <typename TesterInstance>
-bool HasNullaryTest(const TesterInstance&) {
-  return NullaryTestValidator<TesterInstance>::value;
-}
-
-void DummyOp(void*) {}
-
-template <typename TesterInstance, typename = void>
-struct UnaryTestValidator : public std::false_type {};
-
-template <typename TesterInstance>
-struct UnaryTestValidator<
-    TesterInstance,
-    absl::void_t<decltype(std::declval<TesterInstance>().Test(DummyOp))>>
-    : public std::true_type {};
-
-template <typename TesterInstance>
-bool HasUnaryTest(const TesterInstance&) {
-  return UnaryTestValidator<TesterInstance>::value;
-}
-
-TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) {
-  using T = exceptions_internal::UninitializedT;
-  auto op = [](T* t) {};
-  auto inv = [](T*) { return testing::AssertionSuccess(); };
-  auto fac = []() { return absl::make_unique<T>(); };
-
-  // Test that providing operation and inveriants still does not allow for the
-  // the invocation of .Test() and .Test(op) because it lacks a factory
-  auto without_fac =
-      testing::MakeExceptionSafetyTester().WithOperation(op).WithContracts(
-          inv, testing::strong_guarantee);
-  EXPECT_FALSE(HasNullaryTest(without_fac));
-  EXPECT_FALSE(HasUnaryTest(without_fac));
-
-  // Test that providing contracts and factory allows the invocation of
-  // .Test(op) but does not allow for .Test() because it lacks an operation
-  auto without_op = testing::MakeExceptionSafetyTester()
-                        .WithContracts(inv, testing::strong_guarantee)
-                        .WithFactory(fac);
-  EXPECT_FALSE(HasNullaryTest(without_op));
-  EXPECT_TRUE(HasUnaryTest(without_op));
-
-  // Test that providing operation and factory still does not allow for the
-  // the invocation of .Test() and .Test(op) because it lacks contracts
-  auto without_inv =
-      testing::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac);
-  EXPECT_FALSE(HasNullaryTest(without_inv));
-  EXPECT_FALSE(HasUnaryTest(without_inv));
-}
-
-struct ExampleStruct {};
-
-std::unique_ptr<ExampleStruct> ExampleFunctionFactory() {
-  return absl::make_unique<ExampleStruct>();
-}
-
-void ExampleFunctionOperation(ExampleStruct*) {}
-
-testing::AssertionResult ExampleFunctionContract(ExampleStruct*) {
-  return testing::AssertionSuccess();
-}
-
-struct {
-  std::unique_ptr<ExampleStruct> operator()() const {
-    return ExampleFunctionFactory();
-  }
-} example_struct_factory;
-
-struct {
-  void operator()(ExampleStruct*) const {}
-} example_struct_operation;
-
-struct {
-  testing::AssertionResult operator()(ExampleStruct* example_struct) const {
-    return ExampleFunctionContract(example_struct);
-  }
-} example_struct_contract;
-
-auto example_lambda_factory = []() { return ExampleFunctionFactory(); };
-
-auto example_lambda_operation = [](ExampleStruct*) {};
-
-auto example_lambda_contract = [](ExampleStruct* example_struct) {
-  return ExampleFunctionContract(example_struct);
-};
-
-// Testing that function references, pointers, structs with operator() and
-// lambdas can all be used with ExceptionSafetyTester
-TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) {
-  // function reference
-  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
-                  .WithFactory(ExampleFunctionFactory)
-                  .WithOperation(ExampleFunctionOperation)
-                  .WithContracts(ExampleFunctionContract)
-                  .Test());
-
-  // function pointer
-  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
-                  .WithFactory(&ExampleFunctionFactory)
-                  .WithOperation(&ExampleFunctionOperation)
-                  .WithContracts(&ExampleFunctionContract)
-                  .Test());
-
-  // struct
-  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
-                  .WithFactory(example_struct_factory)
-                  .WithOperation(example_struct_operation)
-                  .WithContracts(example_struct_contract)
-                  .Test());
-
-  // lambda
-  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
-                  .WithFactory(example_lambda_factory)
-                  .WithOperation(example_lambda_operation)
-                  .WithContracts(example_lambda_contract)
-                  .Test());
-}
-
-struct NonNegative {
-  bool operator==(const NonNegative& other) const { return i == other.i; }
-  int i;
-};
-
-testing::AssertionResult CheckNonNegativeInvariants(NonNegative* g) {
-  if (g->i >= 0) {
-    return testing::AssertionSuccess();
-  }
-  return testing::AssertionFailure()
-         << "i should be non-negative but is " << g->i;
-}
-
-struct {
-  template <typename T>
-  void operator()(T* t) const {
-    (*t)();
-  }
-} invoker;
-
-auto tester =
-    testing::MakeExceptionSafetyTester().WithOperation(invoker).WithContracts(
-        CheckNonNegativeInvariants);
-auto strong_tester = tester.WithContracts(testing::strong_guarantee);
-
-struct FailsBasicGuarantee : public NonNegative {
-  void operator()() {
-    --i;
-    ThrowingValue<> bomb;
-    ++i;
-  }
-};
-
-TEST(ExceptionCheckTest, BasicGuaranteeFailure) {
-  EXPECT_FALSE(tester.WithInitialValue(FailsBasicGuarantee{}).Test());
-}
-
-struct FollowsBasicGuarantee : public NonNegative {
-  void operator()() {
-    ++i;
-    ThrowingValue<> bomb;
-  }
-};
-
-TEST(ExceptionCheckTest, BasicGuarantee) {
-  EXPECT_TRUE(tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
-}
-
-TEST(ExceptionCheckTest, StrongGuaranteeFailure) {
-  EXPECT_FALSE(strong_tester.WithInitialValue(FailsBasicGuarantee{}).Test());
-  EXPECT_FALSE(strong_tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
-}
-
-struct BasicGuaranteeWithExtraContracts : public NonNegative {
-  // After operator(), i is incremented.  If operator() throws, i is set to 9999
-  void operator()() {
-    int old_i = i;
-    i = kExceptionSentinel;
-    ThrowingValue<> bomb;
-    i = ++old_i;
-  }
-
-  static constexpr int kExceptionSentinel = 9999;
-};
-constexpr int BasicGuaranteeWithExtraContracts::kExceptionSentinel;
-
-TEST(ExceptionCheckTest, BasicGuaranteeWithExtraContracts) {
-  auto tester_with_val =
-      tester.WithInitialValue(BasicGuaranteeWithExtraContracts{});
-  EXPECT_TRUE(tester_with_val.Test());
-  EXPECT_TRUE(
-      tester_with_val
-          .WithContracts([](BasicGuaranteeWithExtraContracts* o) {
-            if (o->i == BasicGuaranteeWithExtraContracts::kExceptionSentinel) {
-              return testing::AssertionSuccess();
-            }
-            return testing::AssertionFailure()
-                   << "i should be "
-                   << BasicGuaranteeWithExtraContracts::kExceptionSentinel
-                   << ", but is " << o->i;
-          })
-          .Test());
-}
-
-struct FollowsStrongGuarantee : public NonNegative {
-  void operator()() { ThrowingValue<> bomb; }
-};
-
-TEST(ExceptionCheckTest, StrongGuarantee) {
-  EXPECT_TRUE(tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
-  EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
-}
-
-struct HasReset : public NonNegative {
-  void operator()() {
-    i = -1;
-    ThrowingValue<> bomb;
-    i = 1;
-  }
-
-  void reset() { i = 0; }
-};
-
-testing::AssertionResult CheckHasResetContracts(HasReset* h) {
-  h->reset();
-  return testing::AssertionResult(h->i == 0);
-}
-
-TEST(ExceptionCheckTest, ModifyingChecker) {
-  auto set_to_1000 = [](FollowsBasicGuarantee* g) {
-    g->i = 1000;
-    return testing::AssertionSuccess();
-  };
-  auto is_1000 = [](FollowsBasicGuarantee* g) {
-    return testing::AssertionResult(g->i == 1000);
-  };
-  auto increment = [](FollowsStrongGuarantee* g) {
-    ++g->i;
-    return testing::AssertionSuccess();
-  };
-
-  EXPECT_FALSE(tester.WithInitialValue(FollowsBasicGuarantee{})
-                   .WithContracts(set_to_1000, is_1000)
-                   .Test());
-  EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{})
-                  .WithContracts(increment)
-                  .Test());
-  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
-                  .WithInitialValue(HasReset{})
-                  .WithContracts(CheckHasResetContracts)
-                  .Test(invoker));
-}
-
-TEST(ExceptionSafetyTesterTest, ResetsCountdown) {
-  auto test =
-      testing::MakeExceptionSafetyTester()
-          .WithInitialValue(ThrowingValue<>())
-          .WithContracts([](ThrowingValue<>*) { return AssertionSuccess(); })
-          .WithOperation([](ThrowingValue<>*) {});
-  ASSERT_TRUE(test.Test());
-  // If the countdown isn't reset because there were no exceptions thrown, then
-  // this will fail with a termination from an unhandled exception
-  EXPECT_TRUE(test.Test());
-}
-
-struct NonCopyable : public NonNegative {
-  NonCopyable(const NonCopyable&) = delete;
-  NonCopyable() : NonNegative{0} {}
-
-  void operator()() { ThrowingValue<> bomb; }
-};
-
-TEST(ExceptionCheckTest, NonCopyable) {
-  auto factory = []() { return absl::make_unique<NonCopyable>(); };
-  EXPECT_TRUE(tester.WithFactory(factory).Test());
-  EXPECT_TRUE(strong_tester.WithFactory(factory).Test());
-}
-
-struct NonEqualityComparable : public NonNegative {
-  void operator()() { ThrowingValue<> bomb; }
-
-  void ModifyOnThrow() {
-    ++i;
-    ThrowingValue<> bomb;
-    static_cast<void>(bomb);
-    --i;
-  }
-};
-
-TEST(ExceptionCheckTest, NonEqualityComparable) {
-  auto nec_is_strong = [](NonEqualityComparable* nec) {
-    return testing::AssertionResult(nec->i == NonEqualityComparable().i);
-  };
-  auto strong_nec_tester = tester.WithInitialValue(NonEqualityComparable{})
-                               .WithContracts(nec_is_strong);
-
-  EXPECT_TRUE(strong_nec_tester.Test());
-  EXPECT_FALSE(strong_nec_tester.Test(
-      [](NonEqualityComparable* n) { n->ModifyOnThrow(); }));
-}
-
-template <typename T>
-struct ExhaustivenessTester {
-  void operator()() {
-    successes |= 1;
-    T b1;
-    static_cast<void>(b1);
-    successes |= (1 << 1);
-    T b2;
-    static_cast<void>(b2);
-    successes |= (1 << 2);
-    T b3;
-    static_cast<void>(b3);
-    successes |= (1 << 3);
-  }
-
-  bool operator==(const ExhaustivenessTester<ThrowingValue<>>&) const {
-    return true;
-  }
-
-  static unsigned char successes;
-};
-
-struct {
-  template <typename T>
-  testing::AssertionResult operator()(ExhaustivenessTester<T>*) const {
-    return testing::AssertionSuccess();
-  }
-} CheckExhaustivenessTesterContracts;
-
-template <typename T>
-unsigned char ExhaustivenessTester<T>::successes = 0;
-
-TEST(ExceptionCheckTest, Exhaustiveness) {
-  auto exhaust_tester = testing::MakeExceptionSafetyTester()
-                            .WithContracts(CheckExhaustivenessTesterContracts)
-                            .WithOperation(invoker);
-
-  EXPECT_TRUE(
-      exhaust_tester.WithInitialValue(ExhaustivenessTester<int>{}).Test());
-  EXPECT_EQ(ExhaustivenessTester<int>::successes, 0xF);
-
-  EXPECT_TRUE(
-      exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{})
-          .WithContracts(testing::strong_guarantee)
-          .Test());
-  EXPECT_EQ(ExhaustivenessTester<ThrowingValue<>>::successes, 0xF);
-}
-
-struct LeaksIfCtorThrows : private exceptions_internal::TrackedObject {
-  LeaksIfCtorThrows() : TrackedObject(ABSL_PRETTY_FUNCTION) {
-    ++counter;
-    ThrowingValue<> v;
-    static_cast<void>(v);
-    --counter;
-  }
-  LeaksIfCtorThrows(const LeaksIfCtorThrows&) noexcept
-      : TrackedObject(ABSL_PRETTY_FUNCTION) {}
-  static int counter;
-};
-int LeaksIfCtorThrows::counter = 0;
-
-TEST(ExceptionCheckTest, TestLeakyCtor) {
-  testing::TestThrowingCtor<LeaksIfCtorThrows>();
-  EXPECT_EQ(LeaksIfCtorThrows::counter, 1);
-  LeaksIfCtorThrows::counter = 0;
-}
-
-struct Tracked : private exceptions_internal::TrackedObject {
-  Tracked() : TrackedObject(ABSL_PRETTY_FUNCTION) {}
-};
-
-TEST(ConstructorTrackerTest, CreatedBefore) {
-  Tracked a, b, c;
-  exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
-}
-
-TEST(ConstructorTrackerTest, CreatedAfter) {
-  exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
-  Tracked a, b, c;
-}
-
-TEST(ConstructorTrackerTest, NotDestroyedAfter) {
-  alignas(Tracked) unsigned char storage[sizeof(Tracked)];
-  EXPECT_NONFATAL_FAILURE(
-      {
-        exceptions_internal::ConstructorTracker ct(
-            exceptions_internal::countdown);
-        new (&storage) Tracked();
-      },
-      "not destroyed");
-}
-
-TEST(ConstructorTrackerTest, DestroyedTwice) {
-  exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
-  EXPECT_NONFATAL_FAILURE(
-      {
-        Tracked t;
-        t.~Tracked();
-      },
-      "re-destroyed");
-}
-
-TEST(ConstructorTrackerTest, ConstructedTwice) {
-  exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
-  alignas(Tracked) unsigned char storage[sizeof(Tracked)];
-  EXPECT_NONFATAL_FAILURE(
-      {
-        new (&storage) Tracked();
-        new (&storage) Tracked();
-        reinterpret_cast<Tracked*>(&storage)->~Tracked();
-      },
-      "re-constructed");
-}
-
-TEST(ThrowingValueTraitsTest, RelationalOperators) {
-  ThrowingValue<> a, b;
-  EXPECT_TRUE((std::is_convertible<decltype(a == b), bool>::value));
-  EXPECT_TRUE((std::is_convertible<decltype(a != b), bool>::value));
-  EXPECT_TRUE((std::is_convertible<decltype(a < b), bool>::value));
-  EXPECT_TRUE((std::is_convertible<decltype(a <= b), bool>::value));
-  EXPECT_TRUE((std::is_convertible<decltype(a > b), bool>::value));
-  EXPECT_TRUE((std::is_convertible<decltype(a >= b), bool>::value));
-}
-
-TEST(ThrowingAllocatorTraitsTest, Assignablility) {
-  EXPECT_TRUE(absl::is_move_assignable<ThrowingAllocator<int>>::value);
-  EXPECT_TRUE(absl::is_copy_assignable<ThrowingAllocator<int>>::value);
-  EXPECT_TRUE(std::is_nothrow_move_assignable<ThrowingAllocator<int>>::value);
-  EXPECT_TRUE(std::is_nothrow_copy_assignable<ThrowingAllocator<int>>::value);
-}
-
-}  // namespace
-
-}  // namespace testing
-
-#endif  // ABSL_HAVE_EXCEPTIONS
diff --git a/third_party/abseil_cpp/absl/base/inline_variable_test.cc b/third_party/abseil_cpp/absl/base/inline_variable_test.cc
deleted file mode 100644
index 37a40e1e40..0000000000
--- a/third_party/abseil_cpp/absl/base/inline_variable_test.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <type_traits>
-
-#include "absl/base/internal/inline_variable.h"
-#include "absl/base/internal/inline_variable_testing.h"
-
-#include "gtest/gtest.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace inline_variable_testing_internal {
-namespace {
-
-TEST(InlineVariableTest, Constexpr) {
-  static_assert(inline_variable_foo.value == 5, "");
-  static_assert(other_inline_variable_foo.value == 5, "");
-  static_assert(inline_variable_int == 5, "");
-  static_assert(other_inline_variable_int == 5, "");
-}
-
-TEST(InlineVariableTest, DefaultConstructedIdentityEquality) {
-  EXPECT_EQ(get_foo_a().value, 5);
-  EXPECT_EQ(get_foo_b().value, 5);
-  EXPECT_EQ(&get_foo_a(), &get_foo_b());
-}
-
-TEST(InlineVariableTest, DefaultConstructedIdentityInequality) {
-  EXPECT_NE(&inline_variable_foo, &other_inline_variable_foo);
-}
-
-TEST(InlineVariableTest, InitializedIdentityEquality) {
-  EXPECT_EQ(get_int_a(), 5);
-  EXPECT_EQ(get_int_b(), 5);
-  EXPECT_EQ(&get_int_a(), &get_int_b());
-}
-
-TEST(InlineVariableTest, InitializedIdentityInequality) {
-  EXPECT_NE(&inline_variable_int, &other_inline_variable_int);
-}
-
-TEST(InlineVariableTest, FunPtrType) {
-  static_assert(
-      std::is_same<void(*)(),
-                   std::decay<decltype(inline_variable_fun_ptr)>::type>::value,
-      "");
-}
-
-}  // namespace
-}  // namespace inline_variable_testing_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/inline_variable_test_a.cc b/third_party/abseil_cpp/absl/base/inline_variable_test_a.cc
deleted file mode 100644
index f96a58d9b4..0000000000
--- a/third_party/abseil_cpp/absl/base/inline_variable_test_a.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/inline_variable_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace inline_variable_testing_internal {
-
-const Foo& get_foo_a() { return inline_variable_foo; }
-
-const int& get_int_a() { return inline_variable_int; }
-
-}  // namespace inline_variable_testing_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/inline_variable_test_b.cc b/third_party/abseil_cpp/absl/base/inline_variable_test_b.cc
deleted file mode 100644
index 038adc30a9..0000000000
--- a/third_party/abseil_cpp/absl/base/inline_variable_test_b.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/inline_variable_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace inline_variable_testing_internal {
-
-const Foo& get_foo_b() { return inline_variable_foo; }
-
-const int& get_int_b() { return inline_variable_int; }
-
-}  // namespace inline_variable_testing_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/atomic_hook.h b/third_party/abseil_cpp/absl/base/internal/atomic_hook.h
deleted file mode 100644
index ae21cd7fe5..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/atomic_hook.h
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
-#define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
-
-#include <atomic>
-#include <cassert>
-#include <cstdint>
-#include <utility>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-
-#if defined(_MSC_VER) && !defined(__clang__)
-#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0
-#else
-#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1
-#endif
-
-#if defined(_MSC_VER)
-#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
-#else
-#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-template <typename T>
-class AtomicHook;
-
-// To workaround AtomicHook not being constant-initializable on some platforms,
-// prefer to annotate instances with `ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES`
-// instead of `ABSL_CONST_INIT`.
-#if ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
-#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_CONST_INIT
-#else
-#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
-#endif
-
-// `AtomicHook` is a helper class, templatized on a raw function pointer type,
-// for implementing Abseil customization hooks.  It is a callable object that
-// dispatches to the registered hook.  Objects of type `AtomicHook` must have
-// static or thread storage duration.
-//
-// A default constructed object performs a no-op (and returns a default
-// constructed object) if no hook has been registered.
-//
-// Hooks can be pre-registered via constant initialization, for example:
-//
-// ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static AtomicHook<void(*)()>
-//     my_hook(DefaultAction);
-//
-// and then changed at runtime via a call to `Store()`.
-//
-// Reads and writes guarantee memory_order_acquire/memory_order_release
-// semantics.
-template <typename ReturnType, typename... Args>
-class AtomicHook<ReturnType (*)(Args...)> {
- public:
-  using FnPtr = ReturnType (*)(Args...);
-
-  // Constructs an object that by default performs a no-op (and
-  // returns a default constructed object) when no hook as been registered.
-  constexpr AtomicHook() : AtomicHook(DummyFunction) {}
-
-  // Constructs an object that by default dispatches to/returns the
-  // pre-registered default_fn when no hook has been registered at runtime.
-#if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
-  explicit constexpr AtomicHook(FnPtr default_fn)
-      : hook_(default_fn), default_fn_(default_fn) {}
-#elif ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
-  explicit constexpr AtomicHook(FnPtr default_fn)
-      : hook_(kUninitialized), default_fn_(default_fn) {}
-#else
-  // As of January 2020, on all known versions of MSVC this constructor runs in
-  // the global constructor sequence.  If `Store()` is called by a dynamic
-  // initializer, we want to preserve the value, even if this constructor runs
-  // after the call to `Store()`.  If not, `hook_` will be
-  // zero-initialized by the linker and we have no need to set it.
-  // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html
-  explicit constexpr AtomicHook(FnPtr default_fn)
-      : /* hook_(deliberately omitted), */ default_fn_(default_fn) {
-    static_assert(kUninitialized == 0, "here we rely on zero-initialization");
-  }
-#endif
-
-  // Stores the provided function pointer as the value for this hook.
-  //
-  // This is intended to be called once.  Multiple calls are legal only if the
-  // same function pointer is provided for each call.  The store is implemented
-  // as a memory_order_release operation, and read accesses are implemented as
-  // memory_order_acquire.
-  void Store(FnPtr fn) {
-    bool success = DoStore(fn);
-    static_cast<void>(success);
-    assert(success);
-  }
-
-  // Invokes the registered callback.  If no callback has yet been registered, a
-  // default-constructed object of the appropriate type is returned instead.
-  template <typename... CallArgs>
-  ReturnType operator()(CallArgs&&... args) const {
-    return DoLoad()(std::forward<CallArgs>(args)...);
-  }
-
-  // Returns the registered callback, or nullptr if none has been registered.
-  // Useful if client code needs to conditionalize behavior based on whether a
-  // callback was registered.
-  //
-  // Note that atomic_hook.Load()() and atomic_hook() have different semantics:
-  // operator()() will perform a no-op if no callback was registered, while
-  // Load()() will dereference a null function pointer.  Prefer operator()() to
-  // Load()() unless you must conditionalize behavior on whether a hook was
-  // registered.
-  FnPtr Load() const {
-    FnPtr ptr = DoLoad();
-    return (ptr == DummyFunction) ? nullptr : ptr;
-  }
-
- private:
-  static ReturnType DummyFunction(Args...) {
-    return ReturnType();
-  }
-
-  // Current versions of MSVC (as of September 2017) have a broken
-  // implementation of std::atomic<T*>:  Its constructor attempts to do the
-  // equivalent of a reinterpret_cast in a constexpr context, which is not
-  // allowed.
-  //
-  // This causes an issue when building with LLVM under Windows.  To avoid this,
-  // we use a less-efficient, intptr_t-based implementation on Windows.
-#if ABSL_HAVE_WORKING_ATOMIC_POINTER
-  // Return the stored value, or DummyFunction if no value has been stored.
-  FnPtr DoLoad() const { return hook_.load(std::memory_order_acquire); }
-
-  // Store the given value.  Returns false if a different value was already
-  // stored to this object.
-  bool DoStore(FnPtr fn) {
-    assert(fn);
-    FnPtr expected = default_fn_;
-    const bool store_succeeded = hook_.compare_exchange_strong(
-        expected, fn, std::memory_order_acq_rel, std::memory_order_acquire);
-    const bool same_value_already_stored = (expected == fn);
-    return store_succeeded || same_value_already_stored;
-  }
-
-  std::atomic<FnPtr> hook_;
-#else  // !ABSL_HAVE_WORKING_ATOMIC_POINTER
-  // Use a sentinel value unlikely to be the address of an actual function.
-  static constexpr intptr_t kUninitialized = 0;
-
-  static_assert(sizeof(intptr_t) >= sizeof(FnPtr),
-                "intptr_t can't contain a function pointer");
-
-  FnPtr DoLoad() const {
-    const intptr_t value = hook_.load(std::memory_order_acquire);
-    if (value == kUninitialized) {
-      return default_fn_;
-    }
-    return reinterpret_cast<FnPtr>(value);
-  }
-
-  bool DoStore(FnPtr fn) {
-    assert(fn);
-    const auto value = reinterpret_cast<intptr_t>(fn);
-    intptr_t expected = kUninitialized;
-    const bool store_succeeded = hook_.compare_exchange_strong(
-        expected, value, std::memory_order_acq_rel, std::memory_order_acquire);
-    const bool same_value_already_stored = (expected == value);
-    return store_succeeded || same_value_already_stored;
-  }
-
-  std::atomic<intptr_t> hook_;
-#endif
-
-  const FnPtr default_fn_;
-};
-
-#undef ABSL_HAVE_WORKING_ATOMIC_POINTER
-#undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/atomic_hook_test.cc b/third_party/abseil_cpp/absl/base/internal/atomic_hook_test.cc
deleted file mode 100644
index e577a8fd93..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/atomic_hook_test.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/atomic_hook.h"
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/internal/atomic_hook_test_helper.h"
-
-namespace {
-
-using ::testing::Eq;
-
-int value = 0;
-void TestHook(int x) { value = x; }
-
-TEST(AtomicHookTest, NoDefaultFunction) {
-  ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
-      void (*)(int)>
-      hook;
-  value = 0;
-
-  // Test the default DummyFunction.
-  EXPECT_TRUE(hook.Load() == nullptr);
-  EXPECT_EQ(value, 0);
-  hook(1);
-  EXPECT_EQ(value, 0);
-
-  // Test a stored hook.
-  hook.Store(TestHook);
-  EXPECT_TRUE(hook.Load() == TestHook);
-  EXPECT_EQ(value, 0);
-  hook(1);
-  EXPECT_EQ(value, 1);
-
-  // Calling Store() with the same hook should not crash.
-  hook.Store(TestHook);
-  EXPECT_TRUE(hook.Load() == TestHook);
-  EXPECT_EQ(value, 1);
-  hook(2);
-  EXPECT_EQ(value, 2);
-}
-
-TEST(AtomicHookTest, WithDefaultFunction) {
-  // Set the default value to TestHook at compile-time.
-  ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
-      void (*)(int)>
-      hook(TestHook);
-  value = 0;
-
-  // Test the default value is TestHook.
-  EXPECT_TRUE(hook.Load() == TestHook);
-  EXPECT_EQ(value, 0);
-  hook(1);
-  EXPECT_EQ(value, 1);
-
-  // Calling Store() with the same hook should not crash.
-  hook.Store(TestHook);
-  EXPECT_TRUE(hook.Load() == TestHook);
-  EXPECT_EQ(value, 1);
-  hook(2);
-  EXPECT_EQ(value, 2);
-}
-
-ABSL_CONST_INIT int override_func_calls = 0;
-void OverrideFunc() { override_func_calls++; }
-static struct OverrideInstaller {
-  OverrideInstaller() { absl::atomic_hook_internal::func.Store(OverrideFunc); }
-} override_installer;
-
-TEST(AtomicHookTest, DynamicInitFromAnotherTU) {
-  // MSVC 14.2 doesn't do constexpr static init correctly; in particular it
-  // tends to sequence static init (i.e. defaults) of `AtomicHook` objects
-  // after their dynamic init (i.e. overrides), overwriting whatever value was
-  // written during dynamic init.  This regression test validates the fix.
-  // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html
-  EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0));
-  EXPECT_THAT(override_func_calls, Eq(0));
-  absl::atomic_hook_internal::func();
-  EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0));
-  EXPECT_THAT(override_func_calls, Eq(1));
-  EXPECT_THAT(absl::atomic_hook_internal::func.Load(), Eq(OverrideFunc));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/atomic_hook_test_helper.cc b/third_party/abseil_cpp/absl/base/internal/atomic_hook_test_helper.cc
deleted file mode 100644
index 537d47cd2d..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/atomic_hook_test_helper.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/atomic_hook_test_helper.h"
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/atomic_hook.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace atomic_hook_internal {
-
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<VoidF>
-    func(DefaultFunc);
-ABSL_CONST_INIT int default_func_calls = 0;
-void DefaultFunc() { default_func_calls++; }
-void RegisterFunc(VoidF f) { func.Store(f); }
-
-}  // namespace atomic_hook_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/atomic_hook_test_helper.h b/third_party/abseil_cpp/absl/base/internal/atomic_hook_test_helper.h
deleted file mode 100644
index 3e72b4977d..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/atomic_hook_test_helper.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_
-#define ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_
-
-#include "absl/base/internal/atomic_hook.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace atomic_hook_internal {
-
-using VoidF = void (*)();
-extern absl::base_internal::AtomicHook<VoidF> func;
-extern int default_func_calls;
-void DefaultFunc();
-void RegisterFunc(VoidF func);
-
-}  // namespace atomic_hook_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/bits.h b/third_party/abseil_cpp/absl/base/internal/bits.h
deleted file mode 100644
index 81648e2c39..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/bits.h
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_BITS_H_
-#define ABSL_BASE_INTERNAL_BITS_H_
-
-// This file contains bitwise ops which are implementation details of various
-// absl libraries.
-
-#include <cstdint>
-
-#include "absl/base/config.h"
-
-// Clang on Windows has __builtin_clzll; otherwise we need to use the
-// windows intrinsic functions.
-#if defined(_MSC_VER) && !defined(__clang__)
-#include <intrin.h>
-#if defined(_M_X64)
-#pragma intrinsic(_BitScanReverse64)
-#pragma intrinsic(_BitScanForward64)
-#endif
-#pragma intrinsic(_BitScanReverse)
-#pragma intrinsic(_BitScanForward)
-#endif
-
-#include "absl/base/attributes.h"
-
-#if defined(_MSC_VER) && !defined(__clang__)
-// We can achieve something similar to attribute((always_inline)) with MSVC by
-// using the __forceinline keyword, however this is not perfect. MSVC is
-// much less aggressive about inlining, and even with the __forceinline keyword.
-#define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline
-#else
-// Use default attribute inline.
-#define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE
-#endif
-
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) {
-  int zeroes = 60;
-  if (n >> 32) {
-    zeroes -= 32;
-    n >>= 32;
-  }
-  if (n >> 16) {
-    zeroes -= 16;
-    n >>= 16;
-  }
-  if (n >> 8) {
-    zeroes -= 8;
-    n >>= 8;
-  }
-  if (n >> 4) {
-    zeroes -= 4;
-    n >>= 4;
-  }
-  return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) {
-#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64)
-  // MSVC does not have __buitin_clzll. Use _BitScanReverse64.
-  unsigned long result = 0;  // NOLINT(runtime/int)
-  if (_BitScanReverse64(&result, n)) {
-    return 63 - result;
-  }
-  return 64;
-#elif defined(_MSC_VER) && !defined(__clang__)
-  // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse
-  unsigned long result = 0;  // NOLINT(runtime/int)
-  if ((n >> 32) &&
-      _BitScanReverse(&result, static_cast<unsigned long>(n >> 32))) {
-    return 31 - result;
-  }
-  if (_BitScanReverse(&result, static_cast<unsigned long>(n))) {
-    return 63 - result;
-  }
-  return 64;
-#elif defined(__GNUC__) || defined(__clang__)
-  // Use __builtin_clzll, which uses the following instructions:
-  //  x86: bsr
-  //  ARM64: clz
-  //  PPC: cntlzd
-  static_assert(sizeof(unsigned long long) == sizeof(n),  // NOLINT(runtime/int)
-                "__builtin_clzll does not take 64-bit arg");
-
-  // Handle 0 as a special case because __builtin_clzll(0) is undefined.
-  if (n == 0) {
-    return 64;
-  }
-  return __builtin_clzll(n);
-#else
-  return CountLeadingZeros64Slow(n);
-#endif
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) {
-  int zeroes = 28;
-  if (n >> 16) {
-    zeroes -= 16;
-    n >>= 16;
-  }
-  if (n >> 8) {
-    zeroes -= 8;
-    n >>= 8;
-  }
-  if (n >> 4) {
-    zeroes -= 4;
-    n >>= 4;
-  }
-  return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
-#if defined(_MSC_VER) && !defined(__clang__)
-  unsigned long result = 0;  // NOLINT(runtime/int)
-  if (_BitScanReverse(&result, n)) {
-    return 31 - result;
-  }
-  return 32;
-#elif defined(__GNUC__) || defined(__clang__)
-  // Use __builtin_clz, which uses the following instructions:
-  //  x86: bsr
-  //  ARM64: clz
-  //  PPC: cntlzd
-  static_assert(sizeof(int) == sizeof(n),
-                "__builtin_clz does not take 32-bit arg");
-
-  // Handle 0 as a special case because __builtin_clz(0) is undefined.
-  if (n == 0) {
-    return 32;
-  }
-  return __builtin_clz(n);
-#else
-  return CountLeadingZeros32Slow(n);
-#endif
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) {
-  int c = 63;
-  n &= ~n + 1;
-  if (n & 0x00000000FFFFFFFF) c -= 32;
-  if (n & 0x0000FFFF0000FFFF) c -= 16;
-  if (n & 0x00FF00FF00FF00FF) c -= 8;
-  if (n & 0x0F0F0F0F0F0F0F0F) c -= 4;
-  if (n & 0x3333333333333333) c -= 2;
-  if (n & 0x5555555555555555) c -= 1;
-  return c;
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
-#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64)
-  unsigned long result = 0;  // NOLINT(runtime/int)
-  _BitScanForward64(&result, n);
-  return result;
-#elif defined(_MSC_VER) && !defined(__clang__)
-  unsigned long result = 0;  // NOLINT(runtime/int)
-  if (static_cast<uint32_t>(n) == 0) {
-    _BitScanForward(&result, static_cast<unsigned long>(n >> 32));
-    return result + 32;
-  }
-  _BitScanForward(&result, static_cast<unsigned long>(n));
-  return result;
-#elif defined(__GNUC__) || defined(__clang__)
-  static_assert(sizeof(unsigned long long) == sizeof(n),  // NOLINT(runtime/int)
-                "__builtin_ctzll does not take 64-bit arg");
-  return __builtin_ctzll(n);
-#else
-  return CountTrailingZerosNonZero64Slow(n);
-#endif
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) {
-  int c = 31;
-  n &= ~n + 1;
-  if (n & 0x0000FFFF) c -= 16;
-  if (n & 0x00FF00FF) c -= 8;
-  if (n & 0x0F0F0F0F) c -= 4;
-  if (n & 0x33333333) c -= 2;
-  if (n & 0x55555555) c -= 1;
-  return c;
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) {
-#if defined(_MSC_VER) && !defined(__clang__)
-  unsigned long result = 0;  // NOLINT(runtime/int)
-  _BitScanForward(&result, n);
-  return result;
-#elif defined(__GNUC__) || defined(__clang__)
-  static_assert(sizeof(int) == sizeof(n),
-                "__builtin_ctz does not take 32-bit arg");
-  return __builtin_ctz(n);
-#else
-  return CountTrailingZerosNonZero32Slow(n);
-#endif
-}
-
-#undef ABSL_BASE_INTERNAL_FORCEINLINE
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_BITS_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/bits_test.cc b/third_party/abseil_cpp/absl/base/internal/bits_test.cc
deleted file mode 100644
index 7855fa6297..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/bits_test.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/bits.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-int CLZ64(uint64_t n) {
-  int fast = absl::base_internal::CountLeadingZeros64(n);
-  int slow = absl::base_internal::CountLeadingZeros64Slow(n);
-  EXPECT_EQ(fast, slow) << n;
-  return fast;
-}
-
-TEST(BitsTest, CountLeadingZeros64) {
-  EXPECT_EQ(64, CLZ64(uint64_t{}));
-  EXPECT_EQ(0, CLZ64(~uint64_t{}));
-
-  for (int index = 0; index < 64; index++) {
-    uint64_t x = static_cast<uint64_t>(1) << index;
-    const auto cnt = 63 - index;
-    ASSERT_EQ(cnt, CLZ64(x)) << index;
-    ASSERT_EQ(cnt, CLZ64(x + x - 1)) << index;
-  }
-}
-
-int CLZ32(uint32_t n) {
-  int fast = absl::base_internal::CountLeadingZeros32(n);
-  int slow = absl::base_internal::CountLeadingZeros32Slow(n);
-  EXPECT_EQ(fast, slow) << n;
-  return fast;
-}
-
-TEST(BitsTest, CountLeadingZeros32) {
-  EXPECT_EQ(32, CLZ32(uint32_t{}));
-  EXPECT_EQ(0, CLZ32(~uint32_t{}));
-
-  for (int index = 0; index < 32; index++) {
-    uint32_t x = static_cast<uint32_t>(1) << index;
-    const auto cnt = 31 - index;
-    ASSERT_EQ(cnt, CLZ32(x)) << index;
-    ASSERT_EQ(cnt, CLZ32(x + x - 1)) << index;
-    ASSERT_EQ(CLZ64(x), CLZ32(x) + 32);
-  }
-}
-
-int CTZ64(uint64_t n) {
-  int fast = absl::base_internal::CountTrailingZerosNonZero64(n);
-  int slow = absl::base_internal::CountTrailingZerosNonZero64Slow(n);
-  EXPECT_EQ(fast, slow) << n;
-  return fast;
-}
-
-TEST(BitsTest, CountTrailingZerosNonZero64) {
-  EXPECT_EQ(0, CTZ64(~uint64_t{}));
-
-  for (int index = 0; index < 64; index++) {
-    uint64_t x = static_cast<uint64_t>(1) << index;
-    const auto cnt = index;
-    ASSERT_EQ(cnt, CTZ64(x)) << index;
-    ASSERT_EQ(cnt, CTZ64(~(x - 1))) << index;
-  }
-}
-
-int CTZ32(uint32_t n) {
-  int fast = absl::base_internal::CountTrailingZerosNonZero32(n);
-  int slow = absl::base_internal::CountTrailingZerosNonZero32Slow(n);
-  EXPECT_EQ(fast, slow) << n;
-  return fast;
-}
-
-TEST(BitsTest, CountTrailingZerosNonZero32) {
-  EXPECT_EQ(0, CTZ32(~uint32_t{}));
-
-  for (int index = 0; index < 32; index++) {
-    uint32_t x = static_cast<uint32_t>(1) << index;
-    const auto cnt = index;
-    ASSERT_EQ(cnt, CTZ32(x)) << index;
-    ASSERT_EQ(cnt, CTZ32(~(x - 1))) << index;
-  }
-}
-
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/cmake_thread_test.cc b/third_party/abseil_cpp/absl/base/internal/cmake_thread_test.cc
deleted file mode 100644
index f70bb24eb7..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/cmake_thread_test.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <iostream>
-#include "absl/base/internal/thread_identity.h"
-
-int main() {
-  auto* tid = absl::base_internal::CurrentThreadIdentityIfPresent();
-  // Make sure the above call can't be optimized out
-  std::cout << (void*)tid << std::endl;
-}
diff --git a/third_party/abseil_cpp/absl/base/internal/cycleclock.cc b/third_party/abseil_cpp/absl/base/internal/cycleclock.cc
deleted file mode 100644
index 0e65005b89..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/cycleclock.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// The implementation of CycleClock::Frequency.
-//
-// NOTE: only i386 and x86_64 have been well tested.
-// PPC, sparc, alpha, and ia64 are based on
-//    http://peter.kuscsik.com/wordpress/?p=14
-// with modifications by m3b.  See also
-//    https://setisvn.ssl.berkeley.edu/svn/lib/fftw-3.0.1/kernel/cycle.h
-
-#include "absl/base/internal/cycleclock.h"
-
-#include <atomic>
-#include <chrono>  // NOLINT(build/c++11)
-
-#include "absl/base/internal/unscaledcycleclock.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-#if ABSL_USE_UNSCALED_CYCLECLOCK
-
-namespace {
-
-#ifdef NDEBUG
-#ifdef ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
-// Not debug mode and the UnscaledCycleClock frequency is the CPU
-// frequency.  Scale the CycleClock to prevent overflow if someone
-// tries to represent the time as cycles since the Unix epoch.
-static constexpr int32_t kShift = 1;
-#else
-// Not debug mode and the UnscaledCycleClock isn't operating at the
-// raw CPU frequency. There is no need to do any scaling, so don't
-// needlessly sacrifice precision.
-static constexpr int32_t kShift = 0;
-#endif
-#else
-// In debug mode use a different shift to discourage depending on a
-// particular shift value.
-static constexpr int32_t kShift = 2;
-#endif
-
-static constexpr double kFrequencyScale = 1.0 / (1 << kShift);
-static std::atomic<CycleClockSourceFunc> cycle_clock_source;
-
-CycleClockSourceFunc LoadCycleClockSource() {
-  // Optimize for the common case (no callback) by first doing a relaxed load;
-  // this is significantly faster on non-x86 platforms.
-  if (cycle_clock_source.load(std::memory_order_relaxed) == nullptr) {
-    return nullptr;
-  }
-  // This corresponds to the store(std::memory_order_release) in
-  // CycleClockSource::Register, and makes sure that any updates made prior to
-  // registering the callback are visible to this thread before the callback is
-  // invoked.
-  return cycle_clock_source.load(std::memory_order_acquire);
-}
-
-}  // namespace
-
-int64_t CycleClock::Now() {
-  auto fn = LoadCycleClockSource();
-  if (fn == nullptr) {
-    return base_internal::UnscaledCycleClock::Now() >> kShift;
-  }
-  return fn() >> kShift;
-}
-
-double CycleClock::Frequency() {
-  return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency();
-}
-
-void CycleClockSource::Register(CycleClockSourceFunc source) {
-  // Corresponds to the load(std::memory_order_acquire) in LoadCycleClockSource.
-  cycle_clock_source.store(source, std::memory_order_release);
-}
-
-#else
-
-int64_t CycleClock::Now() {
-  return std::chrono::duration_cast<std::chrono::nanoseconds>(
-             std::chrono::steady_clock::now().time_since_epoch())
-      .count();
-}
-
-double CycleClock::Frequency() {
-  return 1e9;
-}
-
-#endif
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/cycleclock.h b/third_party/abseil_cpp/absl/base/internal/cycleclock.h
deleted file mode 100644
index a18b584445..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/cycleclock.h
+++ /dev/null
@@ -1,94 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// -----------------------------------------------------------------------------
-// File: cycleclock.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines a `CycleClock`, which yields the value and frequency
-// of a cycle counter that increments at a rate that is approximately constant.
-//
-// NOTE:
-//
-// The cycle counter frequency is not necessarily related to the core clock
-// frequency and should not be treated as such. That is, `CycleClock` cycles are
-// not necessarily "CPU cycles" and code should not rely on that behavior, even
-// if experimentally observed.
-//
-// An arbitrary offset may have been added to the counter at power on.
-//
-// On some platforms, the rate and offset of the counter may differ
-// slightly when read from different CPUs of a multiprocessor. Usually,
-// we try to ensure that the operating system adjusts values periodically
-// so that values agree approximately.   If you need stronger guarantees,
-// consider using alternate interfaces.
-//
-// The CPU is not required to maintain the ordering of a cycle counter read
-// with respect to surrounding instructions.
-
-#ifndef ABSL_BASE_INTERNAL_CYCLECLOCK_H_
-#define ABSL_BASE_INTERNAL_CYCLECLOCK_H_
-
-#include <cstdint>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// -----------------------------------------------------------------------------
-// CycleClock
-// -----------------------------------------------------------------------------
-class CycleClock {
- public:
-  // CycleClock::Now()
-  //
-  // Returns the value of a cycle counter that counts at a rate that is
-  // approximately constant.
-  static int64_t Now();
-
-  // CycleClock::Frequency()
-  //
-  // Returns the amount by which `CycleClock::Now()` increases per second. Note
-  // that this value may not necessarily match the core CPU clock frequency.
-  static double Frequency();
-
- private:
-  CycleClock() = delete;  // no instances
-  CycleClock(const CycleClock&) = delete;
-  CycleClock& operator=(const CycleClock&) = delete;
-};
-
-using CycleClockSourceFunc = int64_t (*)();
-
-class CycleClockSource {
- private:
-  // CycleClockSource::Register()
-  //
-  // Register a function that provides an alternate source for the unscaled CPU
-  // cycle count value. The source function must be async signal safe, must not
-  // call CycleClock::Now(), and must have a frequency that matches that of the
-  // unscaled clock used by CycleClock. A nullptr value resets CycleClock to use
-  // the default source.
-  static void Register(CycleClockSourceFunc source);
-};
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_CYCLECLOCK_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/direct_mmap.h b/third_party/abseil_cpp/absl/base/internal/direct_mmap.h
deleted file mode 100644
index 16accf0966..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/direct_mmap.h
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Functions for directly invoking mmap() via syscall, avoiding the case where
-// mmap() has been locally overridden.
-
-#ifndef ABSL_BASE_INTERNAL_DIRECT_MMAP_H_
-#define ABSL_BASE_INTERNAL_DIRECT_MMAP_H_
-
-#include "absl/base/config.h"
-
-#if ABSL_HAVE_MMAP
-
-#include <sys/mman.h>
-
-#ifdef __linux__
-
-#include <sys/types.h>
-#ifdef __BIONIC__
-#include <sys/syscall.h>
-#else
-#include <syscall.h>
-#endif
-
-#include <linux/unistd.h>
-#include <unistd.h>
-#include <cerrno>
-#include <cstdarg>
-#include <cstdint>
-
-#ifdef __mips__
-// Include definitions of the ABI currently in use.
-#ifdef __BIONIC__
-// Android doesn't have sgidefs.h, but does have asm/sgidefs.h, which has the
-// definitions we need.
-#include <asm/sgidefs.h>
-#else
-#include <sgidefs.h>
-#endif  // __BIONIC__
-#endif  // __mips__
-
-// SYS_mmap and SYS_munmap are not defined in Android.
-#ifdef __BIONIC__
-extern "C" void* __mmap2(void*, size_t, int, int, int, size_t);
-#if defined(__NR_mmap) && !defined(SYS_mmap)
-#define SYS_mmap __NR_mmap
-#endif
-#ifndef SYS_munmap
-#define SYS_munmap __NR_munmap
-#endif
-#endif  // __BIONIC__
-
-#if defined(__NR_mmap2) && !defined(SYS_mmap2)
-#define SYS_mmap2 __NR_mmap2
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// Platform specific logic extracted from
-// https://chromium.googlesource.com/linux-syscall-support/+/master/linux_syscall_support.h
-inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
-                        off64_t offset) noexcept {
-#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \
-    (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) ||                   \
-    (defined(__PPC__) && !defined(__PPC64__)) ||                             \
-    (defined(__riscv) && __riscv_xlen == 32) ||                              \
-    (defined(__s390__) && !defined(__s390x__))
-  // On these architectures, implement mmap with mmap2.
-  static int pagesize = 0;
-  if (pagesize == 0) {
-#if defined(__wasm__) || defined(__asmjs__)
-    pagesize = getpagesize();
-#else
-    pagesize = sysconf(_SC_PAGESIZE);
-#endif
-  }
-  if (offset < 0 || offset % pagesize != 0) {
-    errno = EINVAL;
-    return MAP_FAILED;
-  }
-#ifdef __BIONIC__
-  // SYS_mmap2 has problems on Android API level <= 16.
-  // Workaround by invoking __mmap2() instead.
-  return __mmap2(start, length, prot, flags, fd, offset / pagesize);
-#else
-  return reinterpret_cast<void*>(
-      syscall(SYS_mmap2, start, length, prot, flags, fd,
-              static_cast<off_t>(offset / pagesize)));
-#endif
-#elif defined(__s390x__)
-  // On s390x, mmap() arguments are passed in memory.
-  unsigned long buf[6] = {reinterpret_cast<unsigned long>(start),  // NOLINT
-                          static_cast<unsigned long>(length),      // NOLINT
-                          static_cast<unsigned long>(prot),        // NOLINT
-                          static_cast<unsigned long>(flags),       // NOLINT
-                          static_cast<unsigned long>(fd),          // NOLINT
-                          static_cast<unsigned long>(offset)};     // NOLINT
-  return reinterpret_cast<void*>(syscall(SYS_mmap, buf));
-#elif defined(__x86_64__)
-// The x32 ABI has 32 bit longs, but the syscall interface is 64 bit.
-// We need to explicitly cast to an unsigned 64 bit type to avoid implicit
-// sign extension.  We can't cast pointers directly because those are
-// 32 bits, and gcc will dump ugly warnings about casting from a pointer
-// to an integer of a different size. We also need to make sure __off64_t
-// isn't truncated to 32-bits under x32.
-#define MMAP_SYSCALL_ARG(x) ((uint64_t)(uintptr_t)(x))
-  return reinterpret_cast<void*>(
-      syscall(SYS_mmap, MMAP_SYSCALL_ARG(start), MMAP_SYSCALL_ARG(length),
-              MMAP_SYSCALL_ARG(prot), MMAP_SYSCALL_ARG(flags),
-              MMAP_SYSCALL_ARG(fd), static_cast<uint64_t>(offset)));
-#undef MMAP_SYSCALL_ARG
-#else  // Remaining 64-bit aritectures.
-  static_assert(sizeof(unsigned long) == 8, "Platform is not 64-bit");
-  return reinterpret_cast<void*>(
-      syscall(SYS_mmap, start, length, prot, flags, fd, offset));
-#endif
-}
-
-inline int DirectMunmap(void* start, size_t length) {
-  return static_cast<int>(syscall(SYS_munmap, start, length));
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // !__linux__
-
-// For non-linux platforms where we have mmap, just dispatch directly to the
-// actual mmap()/munmap() methods.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
-                        off_t offset) {
-  return mmap(start, length, prot, flags, fd, offset);
-}
-
-inline int DirectMunmap(void* start, size_t length) {
-  return munmap(start, length);
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // __linux__
-
-#endif  // ABSL_HAVE_MMAP
-
-#endif  // ABSL_BASE_INTERNAL_DIRECT_MMAP_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/dynamic_annotations.h b/third_party/abseil_cpp/absl/base/internal/dynamic_annotations.h
deleted file mode 100644
index b23c5ec1c4..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/dynamic_annotations.h
+++ /dev/null
@@ -1,398 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file defines dynamic annotations for use with dynamic analysis tool
-// such as valgrind, PIN, etc.
-//
-// Dynamic annotation is a source code annotation that affects the generated
-// code (that is, the annotation is not a comment). Each such annotation is
-// attached to a particular instruction and/or to a particular object (address)
-// in the program.
-//
-// The annotations that should be used by users are macros in all upper-case
-// (e.g., ANNOTATE_THREAD_NAME).
-//
-// Actual implementation of these macros may differ depending on the dynamic
-// analysis tool being used.
-//
-// This file supports the following configurations:
-// - Dynamic Annotations enabled (with static thread-safety warnings disabled).
-//   In this case, macros expand to functions implemented by Thread Sanitizer,
-//   when building with TSan. When not provided an external implementation,
-//   dynamic_annotations.cc provides no-op implementations.
-//
-// - Static Clang thread-safety warnings enabled.
-//   When building with a Clang compiler that supports thread-safety warnings,
-//   a subset of annotations can be statically-checked at compile-time. We
-//   expand these macros to static-inline functions that can be analyzed for
-//   thread-safety, but afterwards elided when building the final binary.
-//
-// - All annotations are disabled.
-//   If neither Dynamic Annotations nor Clang thread-safety warnings are
-//   enabled, then all annotation-macros expand to empty.
-
-#ifndef ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_
-#define ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_
-
-#include <stddef.h>
-
-#include "absl/base/config.h"
-
-// -------------------------------------------------------------------------
-// Decide which features are enabled
-
-#ifndef DYNAMIC_ANNOTATIONS_ENABLED
-#define DYNAMIC_ANNOTATIONS_ENABLED 0
-#endif
-
-#if defined(__clang__) && !defined(SWIG)
-#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1
-#endif
-
-#if DYNAMIC_ANNOTATIONS_ENABLED != 0
-
-#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1
-#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1
-#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1
-#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0
-#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1
-
-#else
-
-#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0
-#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0
-#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0
-
-// Clang provides limited support for static thread-safety analysis through a
-// feature called Annotalysis. We configure macro-definitions according to
-// whether Annotalysis support is available. When running in opt-mode, GCC
-// will issue a warning, if these attributes are compiled. Only include them
-// when compiling using Clang.
-
-// ANNOTALYSIS_ENABLED == 1 when IGNORE_READ_ATTRIBUTE_ENABLED == 1
-#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED \
-  defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED)
-// Read/write annotations are enabled in Annotalysis mode; disabled otherwise.
-#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \
-  ABSL_INTERNAL_ANNOTALYSIS_ENABLED
-#endif
-
-// Memory annotations are also made available to LLVM's Memory Sanitizer
-#if defined(ABSL_HAVE_MEMORY_SANITIZER) && !defined(__native_client__)
-#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 1
-#endif
-
-#ifndef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
-#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 0
-#endif
-
-#ifdef __cplusplus
-#define ABSL_INTERNAL_BEGIN_EXTERN_C extern "C" {
-#define ABSL_INTERNAL_END_EXTERN_C }  // extern "C"
-#define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F
-#define ABSL_INTERNAL_STATIC_INLINE inline
-#else
-#define ABSL_INTERNAL_BEGIN_EXTERN_C  // empty
-#define ABSL_INTERNAL_END_EXTERN_C    // empty
-#define ABSL_INTERNAL_GLOBAL_SCOPED(F) F
-#define ABSL_INTERNAL_STATIC_INLINE static inline
-#endif
-
-// -------------------------------------------------------------------------
-// Define race annotations.
-
-#if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1
-
-// -------------------------------------------------------------
-// Annotations that suppress errors. It is usually better to express the
-// program's synchronization using the other annotations, but these can be used
-// when all else fails.
-
-// Report that we may have a benign race at `pointer`, with size
-// "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the
-// point where `pointer` has been allocated, preferably close to the point
-// where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC.
-#define ANNOTATE_BENIGN_RACE(pointer, description)     \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \
-  (__FILE__, __LINE__, pointer, sizeof(*(pointer)), description)
-
-// Same as ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to
-// the memory range [`address`, `address`+`size`).
-#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized)         \
-  (__FILE__, __LINE__, address, size, description)
-
-// Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads.
-// This annotation could be useful if you want to skip expensive race analysis
-// during some period of program execution, e.g. during initialization.
-#define ANNOTATE_ENABLE_RACE_DETECTION(enable)             \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \
-  (__FILE__, __LINE__, enable)
-
-// -------------------------------------------------------------
-// Annotations useful for debugging.
-
-// Report the current thread `name` to a race detector.
-#define ANNOTATE_THREAD_NAME(name) \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName)(__FILE__, __LINE__, name)
-
-// -------------------------------------------------------------
-// Annotations useful when implementing locks. They are not normally needed by
-// modules that merely use locks. The `lock` argument is a pointer to the lock
-// object.
-
-// Report that a lock has been created at address `lock`.
-#define ANNOTATE_RWLOCK_CREATE(lock) \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate)(__FILE__, __LINE__, lock)
-
-// Report that a linker initialized lock has been created at address `lock`.
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-#define ANNOTATE_RWLOCK_CREATE_STATIC(lock)               \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \
-  (__FILE__, __LINE__, lock)
-#else
-#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock)
-#endif
-
-// Report that the lock at address `lock` is about to be destroyed.
-#define ANNOTATE_RWLOCK_DESTROY(lock) \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock)
-
-// Report that the lock at address `lock` has been acquired.
-// `is_w`=1 for writer lock, `is_w`=0 for reader lock.
-#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w)          \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \
-  (__FILE__, __LINE__, lock, is_w)
-
-// Report that the lock at address `lock` is about to be released.
-// `is_w`=1 for writer lock, `is_w`=0 for reader lock.
-#define ANNOTATE_RWLOCK_RELEASED(lock, is_w)          \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \
-  (__FILE__, __LINE__, lock, is_w)
-
-// Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`.
-#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description)      \
-  namespace {                                                     \
-  class static_var##_annotator {                                  \
-   public:                                                        \
-    static_var##_annotator() {                                    \
-      ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \
-                                 #static_var ": " description);   \
-    }                                                             \
-  };                                                              \
-  static static_var##_annotator the##static_var##_annotator;      \
-  }  // namespace
-
-#else  // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0
-
-#define ANNOTATE_RWLOCK_CREATE(lock)                            // empty
-#define ANNOTATE_RWLOCK_CREATE_STATIC(lock)                     // empty
-#define ANNOTATE_RWLOCK_DESTROY(lock)                           // empty
-#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w)                    // empty
-#define ANNOTATE_RWLOCK_RELEASED(lock, is_w)                    // empty
-#define ANNOTATE_BENIGN_RACE(address, description)              // empty
-#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description)  // empty
-#define ANNOTATE_THREAD_NAME(name)                              // empty
-#define ANNOTATE_ENABLE_RACE_DETECTION(enable)                  // empty
-#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description)    // empty
-
-#endif  // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED
-
-// -------------------------------------------------------------------------
-// Define memory annotations.
-
-#if ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 1
-
-#include <sanitizer/msan_interface.h>
-
-#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
-  __msan_unpoison(address, size)
-
-#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
-  __msan_allocated_memory(address, size)
-
-#else  // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 0
-
-#if DYNAMIC_ANNOTATIONS_ENABLED == 1
-#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
-  do {                                                \
-    (void)(address);                                  \
-    (void)(size);                                     \
-  } while (0)
-#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
-  do {                                                  \
-    (void)(address);                                    \
-    (void)(size);                                       \
-  } while (0)
-#else
-#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size)    // empty
-#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size)  // empty
-#endif
-
-#endif  // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
-
-// -------------------------------------------------------------------------
-// Define IGNORE_READS_BEGIN/_END attributes.
-
-#if defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED)
-
-#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \
-  __attribute((exclusive_lock_function("*")))
-#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \
-  __attribute((unlock_function("*")))
-
-#else  // !defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED)
-
-#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE  // empty
-#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE    // empty
-
-#endif  // defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED)
-
-// -------------------------------------------------------------------------
-// Define IGNORE_READS_BEGIN/_END annotations.
-
-#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1
-
-// Request the analysis tool to ignore all reads in the current thread until
-// ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey
-// reads, while still checking other reads and all writes.
-// See also ANNOTATE_UNPROTECTED_READ.
-#define ANNOTATE_IGNORE_READS_BEGIN() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__)
-
-// Stop ignoring reads.
-#define ANNOTATE_IGNORE_READS_END() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__)
-
-#elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED)
-
-// When Annotalysis is enabled without Dynamic Annotations, the use of
-// static-inline functions allows the annotations to be read at compile-time,
-// while still letting the compiler elide the functions from the final build.
-//
-// TODO(delesley) -- The exclusive lock here ignores writes as well, but
-// allows IGNORE_READS_AND_WRITES to work properly.
-
-#define ANNOTATE_IGNORE_READS_BEGIN() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin)()
-
-#define ANNOTATE_IGNORE_READS_END() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd)()
-
-#else
-
-#define ANNOTATE_IGNORE_READS_BEGIN()  // empty
-#define ANNOTATE_IGNORE_READS_END()    // empty
-
-#endif
-
-// -------------------------------------------------------------------------
-// Define IGNORE_WRITES_BEGIN/_END annotations.
-
-#if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1
-
-// Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead.
-#define ANNOTATE_IGNORE_WRITES_BEGIN() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__)
-
-// Stop ignoring writes.
-#define ANNOTATE_IGNORE_WRITES_END() \
-  ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__)
-
-#else
-
-#define ANNOTATE_IGNORE_WRITES_BEGIN()  // empty
-#define ANNOTATE_IGNORE_WRITES_END()    // empty
-
-#endif
-
-// -------------------------------------------------------------------------
-// Define the ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more
-// primitive annotations defined above.
-//
-//     Instead of doing
-//        ANNOTATE_IGNORE_READS_BEGIN();
-//        ... = x;
-//        ANNOTATE_IGNORE_READS_END();
-//     one can use
-//        ... = ANNOTATE_UNPROTECTED_READ(x);
-
-#if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED)
-
-// Start ignoring all memory accesses (both reads and writes).
-#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
-  do {                                           \
-    ANNOTATE_IGNORE_READS_BEGIN();               \
-    ANNOTATE_IGNORE_WRITES_BEGIN();              \
-  } while (0)
-
-// Stop ignoring both reads and writes.
-#define ANNOTATE_IGNORE_READS_AND_WRITES_END() \
-  do {                                         \
-    ANNOTATE_IGNORE_WRITES_END();              \
-    ANNOTATE_IGNORE_READS_END();               \
-  } while (0)
-
-#ifdef __cplusplus
-// ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads.
-#define ANNOTATE_UNPROTECTED_READ(x) \
-  absl::base_internal::AnnotateUnprotectedRead(x)
-
-#endif
-
-#else
-
-#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN()  // empty
-#define ANNOTATE_IGNORE_READS_AND_WRITES_END()    // empty
-#define ANNOTATE_UNPROTECTED_READ(x) (x)
-
-#endif
-
-// -------------------------------------------------------------------------
-// Address sanitizer annotations
-
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-// Describe the current state of a contiguous container such as e.g.
-// std::vector or std::string. For more details see
-// sanitizer/common_interface_defs.h, which is provided by the compiler.
-#include <sanitizer/common_interface_defs.h>
-
-#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \
-  __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid)
-#define ADDRESS_SANITIZER_REDZONE(name)    \
-  struct {                                 \
-    char x[8] __attribute__((aligned(8))); \
-  } name
-
-#else
-
-#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid)
-#define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "")
-
-#endif  // ABSL_HAVE_ADDRESS_SANITIZER
-
-// -------------------------------------------------------------------------
-// Undefine the macros intended only for this file.
-
-#undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED
-#undef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED
-#undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED
-#undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED
-#undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED
-#undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED
-#undef ABSL_INTERNAL_BEGIN_EXTERN_C
-#undef ABSL_INTERNAL_END_EXTERN_C
-#undef ABSL_INTERNAL_STATIC_INLINE
-
-#endif  // ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/endian.h b/third_party/abseil_cpp/absl/base/internal/endian.h
deleted file mode 100644
index 9677530e8d..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/endian.h
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_BASE_INTERNAL_ENDIAN_H_
-#define ABSL_BASE_INTERNAL_ENDIAN_H_
-
-// The following guarantees declaration of the byte swap functions
-#ifdef _MSC_VER
-#include <stdlib.h>  // NOLINT(build/include)
-#elif defined(__FreeBSD__)
-#include <sys/endian.h>
-#elif defined(__GLIBC__)
-#include <byteswap.h>  // IWYU pragma: export
-#endif
-
-#include <cstdint>
-#include "absl/base/config.h"
-#include "absl/base/internal/unaligned_access.h"
-#include "absl/base/port.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Use compiler byte-swapping intrinsics if they are available.  32-bit
-// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0.
-// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0.
-// For simplicity, we enable them all only for GCC 4.8.0 or later.
-#if defined(__clang__) || \
-    (defined(__GNUC__) && \
-     ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5))
-inline uint64_t gbswap_64(uint64_t host_int) {
-  return __builtin_bswap64(host_int);
-}
-inline uint32_t gbswap_32(uint32_t host_int) {
-  return __builtin_bswap32(host_int);
-}
-inline uint16_t gbswap_16(uint16_t host_int) {
-  return __builtin_bswap16(host_int);
-}
-
-#elif defined(_MSC_VER)
-inline uint64_t gbswap_64(uint64_t host_int) {
-  return _byteswap_uint64(host_int);
-}
-inline uint32_t gbswap_32(uint32_t host_int) {
-  return _byteswap_ulong(host_int);
-}
-inline uint16_t gbswap_16(uint16_t host_int) {
-  return _byteswap_ushort(host_int);
-}
-
-#else
-inline uint64_t gbswap_64(uint64_t host_int) {
-#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__)
-  // Adapted from /usr/include/byteswap.h.  Not available on Mac.
-  if (__builtin_constant_p(host_int)) {
-    return __bswap_constant_64(host_int);
-  } else {
-    uint64_t result;
-    __asm__("bswap %0" : "=r"(result) : "0"(host_int));
-    return result;
-  }
-#elif defined(__GLIBC__)
-  return bswap_64(host_int);
-#else
-  return (((host_int & uint64_t{0xFF}) << 56) |
-          ((host_int & uint64_t{0xFF00}) << 40) |
-          ((host_int & uint64_t{0xFF0000}) << 24) |
-          ((host_int & uint64_t{0xFF000000}) << 8) |
-          ((host_int & uint64_t{0xFF00000000}) >> 8) |
-          ((host_int & uint64_t{0xFF0000000000}) >> 24) |
-          ((host_int & uint64_t{0xFF000000000000}) >> 40) |
-          ((host_int & uint64_t{0xFF00000000000000}) >> 56));
-#endif  // bswap_64
-}
-
-inline uint32_t gbswap_32(uint32_t host_int) {
-#if defined(__GLIBC__)
-  return bswap_32(host_int);
-#else
-  return (((host_int & uint32_t{0xFF}) << 24) |
-          ((host_int & uint32_t{0xFF00}) << 8) |
-          ((host_int & uint32_t{0xFF0000}) >> 8) |
-          ((host_int & uint32_t{0xFF000000}) >> 24));
-#endif
-}
-
-inline uint16_t gbswap_16(uint16_t host_int) {
-#if defined(__GLIBC__)
-  return bswap_16(host_int);
-#else
-  return (((host_int & uint16_t{0xFF}) << 8) |
-          ((host_int & uint16_t{0xFF00}) >> 8));
-#endif
-}
-
-#endif  // intrinsics available
-
-#ifdef ABSL_IS_LITTLE_ENDIAN
-
-// Definitions for ntohl etc. that don't require us to include
-// netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather
-// than just #defining them because in debug mode, gcc doesn't
-// correctly handle the (rather involved) definitions of bswap_32.
-// gcc guarantees that inline functions are as fast as macros, so
-// this isn't a performance hit.
-inline uint16_t ghtons(uint16_t x) { return gbswap_16(x); }
-inline uint32_t ghtonl(uint32_t x) { return gbswap_32(x); }
-inline uint64_t ghtonll(uint64_t x) { return gbswap_64(x); }
-
-#elif defined ABSL_IS_BIG_ENDIAN
-
-// These definitions are simpler on big-endian machines
-// These are functions instead of macros to avoid self-assignment warnings
-// on calls such as "i = ghtnol(i);".  This also provides type checking.
-inline uint16_t ghtons(uint16_t x) { return x; }
-inline uint32_t ghtonl(uint32_t x) { return x; }
-inline uint64_t ghtonll(uint64_t x) { return x; }
-
-#else
-#error \
-    "Unsupported byte order: Either ABSL_IS_BIG_ENDIAN or " \
-       "ABSL_IS_LITTLE_ENDIAN must be defined"
-#endif  // byte order
-
-inline uint16_t gntohs(uint16_t x) { return ghtons(x); }
-inline uint32_t gntohl(uint32_t x) { return ghtonl(x); }
-inline uint64_t gntohll(uint64_t x) { return ghtonll(x); }
-
-// Utilities to convert numbers between the current hosts's native byte
-// order and little-endian byte order
-//
-// Load/Store methods are alignment safe
-namespace little_endian {
-// Conversion functions.
-#ifdef ABSL_IS_LITTLE_ENDIAN
-
-inline uint16_t FromHost16(uint16_t x) { return x; }
-inline uint16_t ToHost16(uint16_t x) { return x; }
-
-inline uint32_t FromHost32(uint32_t x) { return x; }
-inline uint32_t ToHost32(uint32_t x) { return x; }
-
-inline uint64_t FromHost64(uint64_t x) { return x; }
-inline uint64_t ToHost64(uint64_t x) { return x; }
-
-inline constexpr bool IsLittleEndian() { return true; }
-
-#elif defined ABSL_IS_BIG_ENDIAN
-
-inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); }
-inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); }
-
-inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); }
-inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); }
-
-inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); }
-inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); }
-
-inline constexpr bool IsLittleEndian() { return false; }
-
-#endif /* ENDIAN */
-
-// Functions to do unaligned loads and stores in little-endian order.
-inline uint16_t Load16(const void *p) {
-  return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
-}
-
-inline void Store16(void *p, uint16_t v) {
-  ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
-}
-
-inline uint32_t Load32(const void *p) {
-  return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p));
-}
-
-inline void Store32(void *p, uint32_t v) {
-  ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
-}
-
-inline uint64_t Load64(const void *p) {
-  return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p));
-}
-
-inline void Store64(void *p, uint64_t v) {
-  ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
-}
-
-}  // namespace little_endian
-
-// Utilities to convert numbers between the current hosts's native byte
-// order and big-endian byte order (same as network byte order)
-//
-// Load/Store methods are alignment safe
-namespace big_endian {
-#ifdef ABSL_IS_LITTLE_ENDIAN
-
-inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); }
-inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); }
-
-inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); }
-inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); }
-
-inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); }
-inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); }
-
-inline constexpr bool IsLittleEndian() { return true; }
-
-#elif defined ABSL_IS_BIG_ENDIAN
-
-inline uint16_t FromHost16(uint16_t x) { return x; }
-inline uint16_t ToHost16(uint16_t x) { return x; }
-
-inline uint32_t FromHost32(uint32_t x) { return x; }
-inline uint32_t ToHost32(uint32_t x) { return x; }
-
-inline uint64_t FromHost64(uint64_t x) { return x; }
-inline uint64_t ToHost64(uint64_t x) { return x; }
-
-inline constexpr bool IsLittleEndian() { return false; }
-
-#endif /* ENDIAN */
-
-// Functions to do unaligned loads and stores in big-endian order.
-inline uint16_t Load16(const void *p) {
-  return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
-}
-
-inline void Store16(void *p, uint16_t v) {
-  ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
-}
-
-inline uint32_t Load32(const void *p) {
-  return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p));
-}
-
-inline void Store32(void *p, uint32_t v) {
-  ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
-}
-
-inline uint64_t Load64(const void *p) {
-  return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p));
-}
-
-inline void Store64(void *p, uint64_t v) {
-  ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
-}
-
-}  // namespace big_endian
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_ENDIAN_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/endian_test.cc b/third_party/abseil_cpp/absl/base/internal/endian_test.cc
deleted file mode 100644
index a1691b1f82..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/endian_test.cc
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/endian.h"
-
-#include <algorithm>
-#include <cstdint>
-#include <limits>
-#include <random>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-const uint64_t kInitialNumber{0x0123456789abcdef};
-const uint64_t k64Value{kInitialNumber};
-const uint32_t k32Value{0x01234567};
-const uint16_t k16Value{0x0123};
-const int kNumValuesToTest = 1000000;
-const int kRandomSeed = 12345;
-
-#if defined(ABSL_IS_BIG_ENDIAN)
-const uint64_t kInitialInNetworkOrder{kInitialNumber};
-const uint64_t k64ValueLE{0xefcdab8967452301};
-const uint32_t k32ValueLE{0x67452301};
-const uint16_t k16ValueLE{0x2301};
-
-const uint64_t k64ValueBE{kInitialNumber};
-const uint32_t k32ValueBE{k32Value};
-const uint16_t k16ValueBE{k16Value};
-#elif defined(ABSL_IS_LITTLE_ENDIAN)
-const uint64_t kInitialInNetworkOrder{0xefcdab8967452301};
-const uint64_t k64ValueLE{kInitialNumber};
-const uint32_t k32ValueLE{k32Value};
-const uint16_t k16ValueLE{k16Value};
-
-const uint64_t k64ValueBE{0xefcdab8967452301};
-const uint32_t k32ValueBE{0x67452301};
-const uint16_t k16ValueBE{0x2301};
-#endif
-
-std::vector<uint16_t> GenerateAllUint16Values() {
-  std::vector<uint16_t> result;
-  result.reserve(size_t{1} << (sizeof(uint16_t) * 8));
-  for (uint32_t i = std::numeric_limits<uint16_t>::min();
-       i <= std::numeric_limits<uint16_t>::max(); ++i) {
-    result.push_back(static_cast<uint16_t>(i));
-  }
-  return result;
-}
-
-template<typename T>
-std::vector<T> GenerateRandomIntegers(size_t num_values_to_test) {
-  std::vector<T> result;
-  result.reserve(num_values_to_test);
-  std::mt19937_64 rng(kRandomSeed);
-  for (size_t i = 0; i < num_values_to_test; ++i) {
-    result.push_back(rng());
-  }
-  return result;
-}
-
-void ManualByteSwap(char* bytes, int length) {
-  if (length == 1)
-    return;
-
-  EXPECT_EQ(0, length % 2);
-  for (int i = 0; i < length / 2; ++i) {
-    int j = (length - 1) - i;
-    using std::swap;
-    swap(bytes[i], bytes[j]);
-  }
-}
-
-template<typename T>
-inline T UnalignedLoad(const char* p) {
-  static_assert(
-      sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8,
-      "Unexpected type size");
-
-  switch (sizeof(T)) {
-    case 1: return *reinterpret_cast<const T*>(p);
-    case 2:
-      return ABSL_INTERNAL_UNALIGNED_LOAD16(p);
-    case 4:
-      return ABSL_INTERNAL_UNALIGNED_LOAD32(p);
-    case 8:
-      return ABSL_INTERNAL_UNALIGNED_LOAD64(p);
-    default:
-      // Suppresses invalid "not all control paths return a value" on MSVC
-      return {};
-  }
-}
-
-template <typename T, typename ByteSwapper>
-static void GBSwapHelper(const std::vector<T>& host_values_to_test,
-                         const ByteSwapper& byte_swapper) {
-  // Test byte_swapper against a manual byte swap.
-  for (typename std::vector<T>::const_iterator it = host_values_to_test.begin();
-       it != host_values_to_test.end(); ++it) {
-    T host_value = *it;
-
-    char actual_value[sizeof(host_value)];
-    memcpy(actual_value, &host_value, sizeof(host_value));
-    byte_swapper(actual_value);
-
-    char expected_value[sizeof(host_value)];
-    memcpy(expected_value, &host_value, sizeof(host_value));
-    ManualByteSwap(expected_value, sizeof(host_value));
-
-    ASSERT_EQ(0, memcmp(actual_value, expected_value, sizeof(host_value)))
-        << "Swap output for 0x" << std::hex << host_value << " does not match. "
-        << "Expected: 0x" << UnalignedLoad<T>(expected_value) << "; "
-        << "actual: 0x" <<  UnalignedLoad<T>(actual_value);
-  }
-}
-
-void Swap16(char* bytes) {
-  ABSL_INTERNAL_UNALIGNED_STORE16(
-      bytes, gbswap_16(ABSL_INTERNAL_UNALIGNED_LOAD16(bytes)));
-}
-
-void Swap32(char* bytes) {
-  ABSL_INTERNAL_UNALIGNED_STORE32(
-      bytes, gbswap_32(ABSL_INTERNAL_UNALIGNED_LOAD32(bytes)));
-}
-
-void Swap64(char* bytes) {
-  ABSL_INTERNAL_UNALIGNED_STORE64(
-      bytes, gbswap_64(ABSL_INTERNAL_UNALIGNED_LOAD64(bytes)));
-}
-
-TEST(EndianessTest, Uint16) {
-  GBSwapHelper(GenerateAllUint16Values(), &Swap16);
-}
-
-TEST(EndianessTest, Uint32) {
-  GBSwapHelper(GenerateRandomIntegers<uint32_t>(kNumValuesToTest), &Swap32);
-}
-
-TEST(EndianessTest, Uint64) {
-  GBSwapHelper(GenerateRandomIntegers<uint64_t>(kNumValuesToTest), &Swap64);
-}
-
-TEST(EndianessTest, ghtonll_gntohll) {
-  // Test that absl::ghtonl compiles correctly
-  uint32_t test = 0x01234567;
-  EXPECT_EQ(absl::gntohl(absl::ghtonl(test)), test);
-
-  uint64_t comp = absl::ghtonll(kInitialNumber);
-  EXPECT_EQ(comp, kInitialInNetworkOrder);
-  comp = absl::gntohll(kInitialInNetworkOrder);
-  EXPECT_EQ(comp, kInitialNumber);
-
-  // Test that htonll and ntohll are each others' inverse functions on a
-  // somewhat assorted batch of numbers. 37 is chosen to not be anything
-  // particularly nice base 2.
-  uint64_t value = 1;
-  for (int i = 0; i < 100; ++i) {
-    comp = absl::ghtonll(absl::gntohll(value));
-    EXPECT_EQ(value, comp);
-    comp = absl::gntohll(absl::ghtonll(value));
-    EXPECT_EQ(value, comp);
-    value *= 37;
-  }
-}
-
-TEST(EndianessTest, little_endian) {
-  // Check little_endian uint16_t.
-  uint64_t comp = little_endian::FromHost16(k16Value);
-  EXPECT_EQ(comp, k16ValueLE);
-  comp = little_endian::ToHost16(k16ValueLE);
-  EXPECT_EQ(comp, k16Value);
-
-  // Check little_endian uint32_t.
-  comp = little_endian::FromHost32(k32Value);
-  EXPECT_EQ(comp, k32ValueLE);
-  comp = little_endian::ToHost32(k32ValueLE);
-  EXPECT_EQ(comp, k32Value);
-
-  // Check little_endian uint64_t.
-  comp = little_endian::FromHost64(k64Value);
-  EXPECT_EQ(comp, k64ValueLE);
-  comp = little_endian::ToHost64(k64ValueLE);
-  EXPECT_EQ(comp, k64Value);
-
-  // Check little-endian Load and store functions.
-  uint16_t u16Buf;
-  uint32_t u32Buf;
-  uint64_t u64Buf;
-
-  little_endian::Store16(&u16Buf, k16Value);
-  EXPECT_EQ(u16Buf, k16ValueLE);
-  comp = little_endian::Load16(&u16Buf);
-  EXPECT_EQ(comp, k16Value);
-
-  little_endian::Store32(&u32Buf, k32Value);
-  EXPECT_EQ(u32Buf, k32ValueLE);
-  comp = little_endian::Load32(&u32Buf);
-  EXPECT_EQ(comp, k32Value);
-
-  little_endian::Store64(&u64Buf, k64Value);
-  EXPECT_EQ(u64Buf, k64ValueLE);
-  comp = little_endian::Load64(&u64Buf);
-  EXPECT_EQ(comp, k64Value);
-}
-
-TEST(EndianessTest, big_endian) {
-  // Check big-endian Load and store functions.
-  uint16_t u16Buf;
-  uint32_t u32Buf;
-  uint64_t u64Buf;
-
-  unsigned char buffer[10];
-  big_endian::Store16(&u16Buf, k16Value);
-  EXPECT_EQ(u16Buf, k16ValueBE);
-  uint64_t comp = big_endian::Load16(&u16Buf);
-  EXPECT_EQ(comp, k16Value);
-
-  big_endian::Store32(&u32Buf, k32Value);
-  EXPECT_EQ(u32Buf, k32ValueBE);
-  comp = big_endian::Load32(&u32Buf);
-  EXPECT_EQ(comp, k32Value);
-
-  big_endian::Store64(&u64Buf, k64Value);
-  EXPECT_EQ(u64Buf, k64ValueBE);
-  comp = big_endian::Load64(&u64Buf);
-  EXPECT_EQ(comp, k64Value);
-
-  big_endian::Store16(buffer + 1, k16Value);
-  EXPECT_EQ(u16Buf, k16ValueBE);
-  comp = big_endian::Load16(buffer + 1);
-  EXPECT_EQ(comp, k16Value);
-
-  big_endian::Store32(buffer + 1, k32Value);
-  EXPECT_EQ(u32Buf, k32ValueBE);
-  comp = big_endian::Load32(buffer + 1);
-  EXPECT_EQ(comp, k32Value);
-
-  big_endian::Store64(buffer + 1, k64Value);
-  EXPECT_EQ(u64Buf, k64ValueBE);
-  comp = big_endian::Load64(buffer + 1);
-  EXPECT_EQ(comp, k64Value);
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/errno_saver.h b/third_party/abseil_cpp/absl/base/internal/errno_saver.h
deleted file mode 100644
index 251de510fc..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/errno_saver.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_ERRNO_SAVER_H_
-#define ABSL_BASE_INTERNAL_ERRNO_SAVER_H_
-
-#include <cerrno>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// `ErrnoSaver` captures the value of `errno` upon construction and restores it
-// upon deletion.  It is used in low-level code and must be super fast.  Do not
-// add instrumentation, even in debug modes.
-class ErrnoSaver {
- public:
-  ErrnoSaver() : saved_errno_(errno) {}
-  ~ErrnoSaver() { errno = saved_errno_; }
-  int operator()() const { return saved_errno_; }
-
- private:
-  const int saved_errno_;
-};
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_ERRNO_SAVER_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/errno_saver_test.cc b/third_party/abseil_cpp/absl/base/internal/errno_saver_test.cc
deleted file mode 100644
index e9b742c588..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/errno_saver_test.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/errno_saver.h"
-
-#include <cerrno>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/strerror.h"
-
-namespace {
-using ::testing::Eq;
-
-struct ErrnoPrinter {
-  int no;
-};
-std::ostream &operator<<(std::ostream &os, ErrnoPrinter ep) {
-  return os << absl::base_internal::StrError(ep.no) << " [" << ep.no << "]";
-}
-bool operator==(ErrnoPrinter one, ErrnoPrinter two) { return one.no == two.no; }
-
-TEST(ErrnoSaverTest, Works) {
-  errno = EDOM;
-  {
-    absl::base_internal::ErrnoSaver errno_saver;
-    EXPECT_THAT(ErrnoPrinter{errno}, Eq(ErrnoPrinter{EDOM}));
-    errno = ERANGE;
-    EXPECT_THAT(ErrnoPrinter{errno}, Eq(ErrnoPrinter{ERANGE}));
-    EXPECT_THAT(ErrnoPrinter{errno_saver()}, Eq(ErrnoPrinter{EDOM}));
-  }
-  EXPECT_THAT(ErrnoPrinter{errno}, Eq(ErrnoPrinter{EDOM}));
-}
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/exception_safety_testing.cc b/third_party/abseil_cpp/absl/base/internal/exception_safety_testing.cc
deleted file mode 100644
index 6ccac41864..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/exception_safety_testing.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/exception_safety_testing.h"
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-
-namespace testing {
-
-exceptions_internal::NoThrowTag nothrow_ctor;
-
-exceptions_internal::StrongGuaranteeTagType strong_guarantee;
-
-exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester() {
-  return {};
-}
-
-namespace exceptions_internal {
-
-int countdown = -1;
-
-ConstructorTracker* ConstructorTracker::current_tracker_instance_ = nullptr;
-
-void MaybeThrow(absl::string_view msg, bool throw_bad_alloc) {
-  if (countdown-- == 0) {
-    if (throw_bad_alloc) throw TestBadAllocException(msg);
-    throw TestException(msg);
-  }
-}
-
-testing::AssertionResult FailureMessage(const TestException& e,
-                                        int countdown) noexcept {
-  return testing::AssertionFailure() << "Exception thrown from " << e.what();
-}
-
-std::string GetSpecString(TypeSpec spec) {
-  std::string out;
-  absl::string_view sep;
-  const auto append = [&](absl::string_view s) {
-    absl::StrAppend(&out, sep, s);
-    sep = " | ";
-  };
-  if (static_cast<bool>(TypeSpec::kNoThrowCopy & spec)) {
-    append("kNoThrowCopy");
-  }
-  if (static_cast<bool>(TypeSpec::kNoThrowMove & spec)) {
-    append("kNoThrowMove");
-  }
-  if (static_cast<bool>(TypeSpec::kNoThrowNew & spec)) {
-    append("kNoThrowNew");
-  }
-  return out;
-}
-
-std::string GetSpecString(AllocSpec spec) {
-  return static_cast<bool>(AllocSpec::kNoThrowAllocate & spec)
-             ? "kNoThrowAllocate"
-             : "";
-}
-
-}  // namespace exceptions_internal
-
-}  // namespace testing
-
-#endif  // ABSL_HAVE_EXCEPTIONS
diff --git a/third_party/abseil_cpp/absl/base/internal/exception_safety_testing.h b/third_party/abseil_cpp/absl/base/internal/exception_safety_testing.h
deleted file mode 100644
index 6ba89d05df..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/exception_safety_testing.h
+++ /dev/null
@@ -1,1101 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Utilities for testing exception-safety
-
-#ifndef ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
-#define ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
-
-#include "absl/base/config.h"
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-#include <cstddef>
-#include <cstdint>
-#include <functional>
-#include <initializer_list>
-#include <iosfwd>
-#include <string>
-#include <tuple>
-#include <unordered_map>
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/pretty_function.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-#include "absl/strings/substitute.h"
-#include "absl/utility/utility.h"
-
-namespace testing {
-
-enum class TypeSpec;
-enum class AllocSpec;
-
-constexpr TypeSpec operator|(TypeSpec a, TypeSpec b) {
-  using T = absl::underlying_type_t<TypeSpec>;
-  return static_cast<TypeSpec>(static_cast<T>(a) | static_cast<T>(b));
-}
-
-constexpr TypeSpec operator&(TypeSpec a, TypeSpec b) {
-  using T = absl::underlying_type_t<TypeSpec>;
-  return static_cast<TypeSpec>(static_cast<T>(a) & static_cast<T>(b));
-}
-
-constexpr AllocSpec operator|(AllocSpec a, AllocSpec b) {
-  using T = absl::underlying_type_t<AllocSpec>;
-  return static_cast<AllocSpec>(static_cast<T>(a) | static_cast<T>(b));
-}
-
-constexpr AllocSpec operator&(AllocSpec a, AllocSpec b) {
-  using T = absl::underlying_type_t<AllocSpec>;
-  return static_cast<AllocSpec>(static_cast<T>(a) & static_cast<T>(b));
-}
-
-namespace exceptions_internal {
-
-std::string GetSpecString(TypeSpec);
-std::string GetSpecString(AllocSpec);
-
-struct NoThrowTag {};
-struct StrongGuaranteeTagType {};
-
-// A simple exception class.  We throw this so that test code can catch
-// exceptions specifically thrown by ThrowingValue.
-class TestException {
- public:
-  explicit TestException(absl::string_view msg) : msg_(msg) {}
-  virtual ~TestException() {}
-  virtual const char* what() const noexcept { return msg_.c_str(); }
-
- private:
-  std::string msg_;
-};
-
-// TestBadAllocException exists because allocation functions must throw an
-// exception which can be caught by a handler of std::bad_alloc.  We use a child
-// class of std::bad_alloc so we can customise the error message, and also
-// derive from TestException so we don't accidentally end up catching an actual
-// bad_alloc exception in TestExceptionSafety.
-class TestBadAllocException : public std::bad_alloc, public TestException {
- public:
-  explicit TestBadAllocException(absl::string_view msg) : TestException(msg) {}
-  using TestException::what;
-};
-
-extern int countdown;
-
-// Allows the countdown variable to be set manually (defaulting to the initial
-// value of 0)
-inline void SetCountdown(int i = 0) { countdown = i; }
-// Sets the countdown to the terminal value -1
-inline void UnsetCountdown() { SetCountdown(-1); }
-
-void MaybeThrow(absl::string_view msg, bool throw_bad_alloc = false);
-
-testing::AssertionResult FailureMessage(const TestException& e,
-                                        int countdown) noexcept;
-
-struct TrackedAddress {
-  bool is_alive;
-  std::string description;
-};
-
-// Inspects the constructions and destructions of anything inheriting from
-// TrackedObject. This allows us to safely "leak" TrackedObjects, as
-// ConstructorTracker will destroy everything left over in its destructor.
-class ConstructorTracker {
- public:
-  explicit ConstructorTracker(int count) : countdown_(count) {
-    assert(current_tracker_instance_ == nullptr);
-    current_tracker_instance_ = this;
-  }
-
-  ~ConstructorTracker() {
-    assert(current_tracker_instance_ == this);
-    current_tracker_instance_ = nullptr;
-
-    for (auto& it : address_map_) {
-      void* address = it.first;
-      TrackedAddress& tracked_address = it.second;
-      if (tracked_address.is_alive) {
-        ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
-                                      countdown_, "Object was not destroyed.");
-      }
-    }
-  }
-
-  static void ObjectConstructed(void* address, std::string description) {
-    if (!CurrentlyTracking()) return;
-
-    TrackedAddress& tracked_address =
-        current_tracker_instance_->address_map_[address];
-    if (tracked_address.is_alive) {
-      ADD_FAILURE() << ErrorMessage(
-          address, tracked_address.description,
-          current_tracker_instance_->countdown_,
-          "Object was re-constructed. Current object was constructed by " +
-              description);
-    }
-    tracked_address = {true, std::move(description)};
-  }
-
-  static void ObjectDestructed(void* address) {
-    if (!CurrentlyTracking()) return;
-
-    auto it = current_tracker_instance_->address_map_.find(address);
-    // Not tracked. Ignore.
-    if (it == current_tracker_instance_->address_map_.end()) return;
-
-    TrackedAddress& tracked_address = it->second;
-    if (!tracked_address.is_alive) {
-      ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
-                                    current_tracker_instance_->countdown_,
-                                    "Object was re-destroyed.");
-    }
-    tracked_address.is_alive = false;
-  }
-
- private:
-  static bool CurrentlyTracking() {
-    return current_tracker_instance_ != nullptr;
-  }
-
-  static std::string ErrorMessage(void* address,
-                                  const std::string& address_description,
-                                  int countdown,
-                                  const std::string& error_description) {
-    return absl::Substitute(
-        "With coundtown at $0:\n"
-        "  $1\n"
-        "  Object originally constructed by $2\n"
-        "  Object address: $3\n",
-        countdown, error_description, address_description, address);
-  }
-
-  std::unordered_map<void*, TrackedAddress> address_map_;
-  int countdown_;
-
-  static ConstructorTracker* current_tracker_instance_;
-};
-
-class TrackedObject {
- public:
-  TrackedObject(const TrackedObject&) = delete;
-  TrackedObject(TrackedObject&&) = delete;
-
- protected:
-  explicit TrackedObject(std::string description) {
-    ConstructorTracker::ObjectConstructed(this, std::move(description));
-  }
-
-  ~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); }
-};
-}  // namespace exceptions_internal
-
-extern exceptions_internal::NoThrowTag nothrow_ctor;
-
-extern exceptions_internal::StrongGuaranteeTagType strong_guarantee;
-
-// A test class which is convertible to bool.  The conversion can be
-// instrumented to throw at a controlled time.
-class ThrowingBool {
- public:
-  ThrowingBool(bool b) noexcept : b_(b) {}  // NOLINT(runtime/explicit)
-  operator bool() const {                   // NOLINT
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return b_;
-  }
-
- private:
-  bool b_;
-};
-
-/*
- * Configuration enum for the ThrowingValue type that defines behavior for the
- * lifetime of the instance. Use testing::nothrow_ctor to prevent the integer
- * constructor from throwing.
- *
- * kEverythingThrows: Every operation can throw an exception
- * kNoThrowCopy: Copy construction and copy assignment will not throw
- * kNoThrowMove: Move construction and move assignment will not throw
- * kNoThrowNew: Overloaded operators new and new[] will not throw
- */
-enum class TypeSpec {
-  kEverythingThrows = 0,
-  kNoThrowCopy = 1,
-  kNoThrowMove = 1 << 1,
-  kNoThrowNew = 1 << 2,
-};
-
-/*
- * A testing class instrumented to throw an exception at a controlled time.
- *
- * ThrowingValue implements a slightly relaxed version of the Regular concept --
- * that is it's a value type with the expected semantics.  It also implements
- * arithmetic operations.  It doesn't implement member and pointer operators
- * like operator-> or operator[].
- *
- * ThrowingValue can be instrumented to have certain operations be noexcept by
- * using compile-time bitfield template arguments.  That is, to make an
- * ThrowingValue which has noexcept move construction/assignment and noexcept
- * copy construction/assignment, use the following:
- *   ThrowingValue<testing::kNoThrowMove | testing::kNoThrowCopy> my_thrwr{val};
- */
-template <TypeSpec Spec = TypeSpec::kEverythingThrows>
-class ThrowingValue : private exceptions_internal::TrackedObject {
-  static constexpr bool IsSpecified(TypeSpec spec) {
-    return static_cast<bool>(Spec & spec);
-  }
-
-  static constexpr int kDefaultValue = 0;
-  static constexpr int kBadValue = 938550620;
-
- public:
-  ThrowingValue() : TrackedObject(GetInstanceString(kDefaultValue)) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ = kDefaultValue;
-  }
-
-  ThrowingValue(const ThrowingValue& other) noexcept(
-      IsSpecified(TypeSpec::kNoThrowCopy))
-      : TrackedObject(GetInstanceString(other.dummy_)) {
-    if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
-      exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    }
-    dummy_ = other.dummy_;
-  }
-
-  ThrowingValue(ThrowingValue&& other) noexcept(
-      IsSpecified(TypeSpec::kNoThrowMove))
-      : TrackedObject(GetInstanceString(other.dummy_)) {
-    if (!IsSpecified(TypeSpec::kNoThrowMove)) {
-      exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    }
-    dummy_ = other.dummy_;
-  }
-
-  explicit ThrowingValue(int i) : TrackedObject(GetInstanceString(i)) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ = i;
-  }
-
-  ThrowingValue(int i, exceptions_internal::NoThrowTag) noexcept
-      : TrackedObject(GetInstanceString(i)), dummy_(i) {}
-
-  // absl expects nothrow destructors
-  ~ThrowingValue() noexcept = default;
-
-  ThrowingValue& operator=(const ThrowingValue& other) noexcept(
-      IsSpecified(TypeSpec::kNoThrowCopy)) {
-    dummy_ = kBadValue;
-    if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
-      exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    }
-    dummy_ = other.dummy_;
-    return *this;
-  }
-
-  ThrowingValue& operator=(ThrowingValue&& other) noexcept(
-      IsSpecified(TypeSpec::kNoThrowMove)) {
-    dummy_ = kBadValue;
-    if (!IsSpecified(TypeSpec::kNoThrowMove)) {
-      exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    }
-    dummy_ = other.dummy_;
-    return *this;
-  }
-
-  // Arithmetic Operators
-  ThrowingValue operator+(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ + other.dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue operator+() const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue operator-(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ - other.dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue operator-() const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(-dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue& operator++() {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    ++dummy_;
-    return *this;
-  }
-
-  ThrowingValue operator++(int) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    auto out = ThrowingValue(dummy_, nothrow_ctor);
-    ++dummy_;
-    return out;
-  }
-
-  ThrowingValue& operator--() {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    --dummy_;
-    return *this;
-  }
-
-  ThrowingValue operator--(int) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    auto out = ThrowingValue(dummy_, nothrow_ctor);
-    --dummy_;
-    return out;
-  }
-
-  ThrowingValue operator*(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ * other.dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue operator/(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ / other.dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue operator%(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ % other.dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue operator<<(int shift) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ << shift, nothrow_ctor);
-  }
-
-  ThrowingValue operator>>(int shift) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ >> shift, nothrow_ctor);
-  }
-
-  // Comparison Operators
-  // NOTE: We use `ThrowingBool` instead of `bool` because most STL
-  // types/containers requires T to be convertible to bool.
-  friend ThrowingBool operator==(const ThrowingValue& a,
-                                 const ThrowingValue& b) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return a.dummy_ == b.dummy_;
-  }
-  friend ThrowingBool operator!=(const ThrowingValue& a,
-                                 const ThrowingValue& b) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return a.dummy_ != b.dummy_;
-  }
-  friend ThrowingBool operator<(const ThrowingValue& a,
-                                const ThrowingValue& b) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return a.dummy_ < b.dummy_;
-  }
-  friend ThrowingBool operator<=(const ThrowingValue& a,
-                                 const ThrowingValue& b) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return a.dummy_ <= b.dummy_;
-  }
-  friend ThrowingBool operator>(const ThrowingValue& a,
-                                const ThrowingValue& b) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return a.dummy_ > b.dummy_;
-  }
-  friend ThrowingBool operator>=(const ThrowingValue& a,
-                                 const ThrowingValue& b) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return a.dummy_ >= b.dummy_;
-  }
-
-  // Logical Operators
-  ThrowingBool operator!() const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return !dummy_;
-  }
-
-  ThrowingBool operator&&(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return dummy_ && other.dummy_;
-  }
-
-  ThrowingBool operator||(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return dummy_ || other.dummy_;
-  }
-
-  // Bitwise Logical Operators
-  ThrowingValue operator~() const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(~dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue operator&(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ & other.dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue operator|(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ | other.dummy_, nothrow_ctor);
-  }
-
-  ThrowingValue operator^(const ThrowingValue& other) const {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return ThrowingValue(dummy_ ^ other.dummy_, nothrow_ctor);
-  }
-
-  // Compound Assignment operators
-  ThrowingValue& operator+=(const ThrowingValue& other) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ += other.dummy_;
-    return *this;
-  }
-
-  ThrowingValue& operator-=(const ThrowingValue& other) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ -= other.dummy_;
-    return *this;
-  }
-
-  ThrowingValue& operator*=(const ThrowingValue& other) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ *= other.dummy_;
-    return *this;
-  }
-
-  ThrowingValue& operator/=(const ThrowingValue& other) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ /= other.dummy_;
-    return *this;
-  }
-
-  ThrowingValue& operator%=(const ThrowingValue& other) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ %= other.dummy_;
-    return *this;
-  }
-
-  ThrowingValue& operator&=(const ThrowingValue& other) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ &= other.dummy_;
-    return *this;
-  }
-
-  ThrowingValue& operator|=(const ThrowingValue& other) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ |= other.dummy_;
-    return *this;
-  }
-
-  ThrowingValue& operator^=(const ThrowingValue& other) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ ^= other.dummy_;
-    return *this;
-  }
-
-  ThrowingValue& operator<<=(int shift) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ <<= shift;
-    return *this;
-  }
-
-  ThrowingValue& operator>>=(int shift) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ >>= shift;
-    return *this;
-  }
-
-  // Pointer operators
-  void operator&() const = delete;  // NOLINT(runtime/operator)
-
-  // Stream operators
-  friend std::ostream& operator<<(std::ostream& os, const ThrowingValue& tv) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return os << GetInstanceString(tv.dummy_);
-  }
-
-  friend std::istream& operator>>(std::istream& is, const ThrowingValue&) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    return is;
-  }
-
-  // Memory management operators
-  // Args.. allows us to overload regular and placement new in one shot
-  template <typename... Args>
-  static void* operator new(size_t s, Args&&... args) noexcept(
-      IsSpecified(TypeSpec::kNoThrowNew)) {
-    if (!IsSpecified(TypeSpec::kNoThrowNew)) {
-      exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
-    }
-    return ::operator new(s, std::forward<Args>(args)...);
-  }
-
-  template <typename... Args>
-  static void* operator new[](size_t s, Args&&... args) noexcept(
-      IsSpecified(TypeSpec::kNoThrowNew)) {
-    if (!IsSpecified(TypeSpec::kNoThrowNew)) {
-      exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
-    }
-    return ::operator new[](s, std::forward<Args>(args)...);
-  }
-
-  // Abseil doesn't support throwing overloaded operator delete.  These are
-  // provided so a throwing operator-new can clean up after itself.
-  //
-  // We provide both regular and templated operator delete because if only the
-  // templated version is provided as we did with operator new, the compiler has
-  // no way of knowing which overload of operator delete to call. See
-  // https://en.cppreference.com/w/cpp/memory/new/operator_delete and
-  // https://en.cppreference.com/w/cpp/language/delete for the gory details.
-  void operator delete(void* p) noexcept { ::operator delete(p); }
-
-  template <typename... Args>
-  void operator delete(void* p, Args&&... args) noexcept {
-    ::operator delete(p, std::forward<Args>(args)...);
-  }
-
-  void operator delete[](void* p) noexcept { return ::operator delete[](p); }
-
-  template <typename... Args>
-  void operator delete[](void* p, Args&&... args) noexcept {
-    return ::operator delete[](p, std::forward<Args>(args)...);
-  }
-
-  // Non-standard access to the actual contained value.  No need for this to
-  // throw.
-  int& Get() noexcept { return dummy_; }
-  const int& Get() const noexcept { return dummy_; }
-
- private:
-  static std::string GetInstanceString(int dummy) {
-    return absl::StrCat("ThrowingValue<",
-                        exceptions_internal::GetSpecString(Spec), ">(", dummy,
-                        ")");
-  }
-
-  int dummy_;
-};
-// While not having to do with exceptions, explicitly delete comma operator, to
-// make sure we don't use it on user-supplied types.
-template <TypeSpec Spec, typename T>
-void operator,(const ThrowingValue<Spec>&, T&&) = delete;
-template <TypeSpec Spec, typename T>
-void operator,(T&&, const ThrowingValue<Spec>&) = delete;
-
-/*
- * Configuration enum for the ThrowingAllocator type that defines behavior for
- * the lifetime of the instance.
- *
- * kEverythingThrows: Calls to the member functions may throw
- * kNoThrowAllocate: Calls to the member functions will not throw
- */
-enum class AllocSpec {
-  kEverythingThrows = 0,
-  kNoThrowAllocate = 1,
-};
-
-/*
- * An allocator type which is instrumented to throw at a controlled time, or not
- * to throw, using AllocSpec. The supported settings are the default of every
- * function which is allowed to throw in a conforming allocator possibly
- * throwing, or nothing throws, in line with the ABSL_ALLOCATOR_THROWS
- * configuration macro.
- */
-template <typename T, AllocSpec Spec = AllocSpec::kEverythingThrows>
-class ThrowingAllocator : private exceptions_internal::TrackedObject {
-  static constexpr bool IsSpecified(AllocSpec spec) {
-    return static_cast<bool>(Spec & spec);
-  }
-
- public:
-  using pointer = T*;
-  using const_pointer = const T*;
-  using reference = T&;
-  using const_reference = const T&;
-  using void_pointer = void*;
-  using const_void_pointer = const void*;
-  using value_type = T;
-  using size_type = size_t;
-  using difference_type = ptrdiff_t;
-
-  using is_nothrow =
-      std::integral_constant<bool, Spec == AllocSpec::kNoThrowAllocate>;
-  using propagate_on_container_copy_assignment = std::true_type;
-  using propagate_on_container_move_assignment = std::true_type;
-  using propagate_on_container_swap = std::true_type;
-  using is_always_equal = std::false_type;
-
-  ThrowingAllocator() : TrackedObject(GetInstanceString(next_id_)) {
-    exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
-    dummy_ = std::make_shared<const int>(next_id_++);
-  }
-
-  template <typename U>
-  ThrowingAllocator(const ThrowingAllocator<U, Spec>& other) noexcept  // NOLINT
-      : TrackedObject(GetInstanceString(*other.State())),
-        dummy_(other.State()) {}
-
-  // According to C++11 standard [17.6.3.5], Table 28, the move/copy ctors of
-  // allocator shall not exit via an exception, thus they are marked noexcept.
-  ThrowingAllocator(const ThrowingAllocator& other) noexcept
-      : TrackedObject(GetInstanceString(*other.State())),
-        dummy_(other.State()) {}
-
-  template <typename U>
-  ThrowingAllocator(ThrowingAllocator<U, Spec>&& other) noexcept  // NOLINT
-      : TrackedObject(GetInstanceString(*other.State())),
-        dummy_(std::move(other.State())) {}
-
-  ThrowingAllocator(ThrowingAllocator&& other) noexcept
-      : TrackedObject(GetInstanceString(*other.State())),
-        dummy_(std::move(other.State())) {}
-
-  ~ThrowingAllocator() noexcept = default;
-
-  ThrowingAllocator& operator=(const ThrowingAllocator& other) noexcept {
-    dummy_ = other.State();
-    return *this;
-  }
-
-  template <typename U>
-  ThrowingAllocator& operator=(
-      const ThrowingAllocator<U, Spec>& other) noexcept {
-    dummy_ = other.State();
-    return *this;
-  }
-
-  template <typename U>
-  ThrowingAllocator& operator=(ThrowingAllocator<U, Spec>&& other) noexcept {
-    dummy_ = std::move(other.State());
-    return *this;
-  }
-
-  template <typename U>
-  struct rebind {
-    using other = ThrowingAllocator<U, Spec>;
-  };
-
-  pointer allocate(size_type n) noexcept(
-      IsSpecified(AllocSpec::kNoThrowAllocate)) {
-    ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
-    return static_cast<pointer>(::operator new(n * sizeof(T)));
-  }
-
-  pointer allocate(size_type n, const_void_pointer) noexcept(
-      IsSpecified(AllocSpec::kNoThrowAllocate)) {
-    return allocate(n);
-  }
-
-  void deallocate(pointer ptr, size_type) noexcept {
-    ReadState();
-    ::operator delete(static_cast<void*>(ptr));
-  }
-
-  template <typename U, typename... Args>
-  void construct(U* ptr, Args&&... args) noexcept(
-      IsSpecified(AllocSpec::kNoThrowAllocate)) {
-    ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
-    ::new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...);
-  }
-
-  template <typename U>
-  void destroy(U* p) noexcept {
-    ReadState();
-    p->~U();
-  }
-
-  size_type max_size() const noexcept {
-    return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
-  }
-
-  ThrowingAllocator select_on_container_copy_construction() noexcept(
-      IsSpecified(AllocSpec::kNoThrowAllocate)) {
-    auto& out = *this;
-    ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
-    return out;
-  }
-
-  template <typename U>
-  bool operator==(const ThrowingAllocator<U, Spec>& other) const noexcept {
-    return dummy_ == other.dummy_;
-  }
-
-  template <typename U>
-  bool operator!=(const ThrowingAllocator<U, Spec>& other) const noexcept {
-    return dummy_ != other.dummy_;
-  }
-
-  template <typename, AllocSpec>
-  friend class ThrowingAllocator;
-
- private:
-  static std::string GetInstanceString(int dummy) {
-    return absl::StrCat("ThrowingAllocator<",
-                        exceptions_internal::GetSpecString(Spec), ">(", dummy,
-                        ")");
-  }
-
-  const std::shared_ptr<const int>& State() const { return dummy_; }
-  std::shared_ptr<const int>& State() { return dummy_; }
-
-  void ReadState() {
-    // we know that this will never be true, but the compiler doesn't, so this
-    // should safely force a read of the value.
-    if (*dummy_ < 0) std::abort();
-  }
-
-  void ReadStateAndMaybeThrow(absl::string_view msg) const {
-    if (!IsSpecified(AllocSpec::kNoThrowAllocate)) {
-      exceptions_internal::MaybeThrow(
-          absl::Substitute("Allocator id $0 threw from $1", *dummy_, msg));
-    }
-  }
-
-  static int next_id_;
-  std::shared_ptr<const int> dummy_;
-};
-
-template <typename T, AllocSpec Spec>
-int ThrowingAllocator<T, Spec>::next_id_ = 0;
-
-// Tests for resource leaks by attempting to construct a T using args repeatedly
-// until successful, using the countdown method.  Side effects can then be
-// tested for resource leaks.
-template <typename T, typename... Args>
-void TestThrowingCtor(Args&&... args) {
-  struct Cleanup {
-    ~Cleanup() { exceptions_internal::UnsetCountdown(); }
-  } c;
-  for (int count = 0;; ++count) {
-    exceptions_internal::ConstructorTracker ct(count);
-    exceptions_internal::SetCountdown(count);
-    try {
-      T temp(std::forward<Args>(args)...);
-      static_cast<void>(temp);
-      break;
-    } catch (const exceptions_internal::TestException&) {
-    }
-  }
-}
-
-// Tests the nothrow guarantee of the provided nullary operation. If the an
-// exception is thrown, the result will be AssertionFailure(). Otherwise, it
-// will be AssertionSuccess().
-template <typename Operation>
-testing::AssertionResult TestNothrowOp(const Operation& operation) {
-  struct Cleanup {
-    Cleanup() { exceptions_internal::SetCountdown(); }
-    ~Cleanup() { exceptions_internal::UnsetCountdown(); }
-  } c;
-  try {
-    operation();
-    return testing::AssertionSuccess();
-  } catch (const exceptions_internal::TestException&) {
-    return testing::AssertionFailure()
-           << "TestException thrown during call to operation() when nothrow "
-              "guarantee was expected.";
-  } catch (...) {
-    return testing::AssertionFailure()
-           << "Unknown exception thrown during call to operation() when "
-              "nothrow guarantee was expected.";
-  }
-}
-
-namespace exceptions_internal {
-
-// Dummy struct for ExceptionSafetyTestBuilder<> partial state.
-struct UninitializedT {};
-
-template <typename T>
-class DefaultFactory {
- public:
-  explicit DefaultFactory(const T& t) : t_(t) {}
-  std::unique_ptr<T> operator()() const { return absl::make_unique<T>(t_); }
-
- private:
-  T t_;
-};
-
-template <size_t LazyContractsCount, typename LazyFactory,
-          typename LazyOperation>
-using EnableIfTestable = typename absl::enable_if_t<
-    LazyContractsCount != 0 &&
-    !std::is_same<LazyFactory, UninitializedT>::value &&
-    !std::is_same<LazyOperation, UninitializedT>::value>;
-
-template <typename Factory = UninitializedT,
-          typename Operation = UninitializedT, typename... Contracts>
-class ExceptionSafetyTestBuilder;
-
-}  // namespace exceptions_internal
-
-/*
- * Constructs an empty ExceptionSafetyTestBuilder. All
- * ExceptionSafetyTestBuilder objects are immutable and all With[thing] mutation
- * methods return new instances of ExceptionSafetyTestBuilder.
- *
- * In order to test a T for exception safety, a factory for that T, a testable
- * operation, and at least one contract callback returning an assertion
- * result must be applied using the respective methods.
- */
-exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester();
-
-namespace exceptions_internal {
-template <typename T>
-struct IsUniquePtr : std::false_type {};
-
-template <typename T, typename D>
-struct IsUniquePtr<std::unique_ptr<T, D>> : std::true_type {};
-
-template <typename Factory>
-struct FactoryPtrTypeHelper {
-  using type = decltype(std::declval<const Factory&>()());
-
-  static_assert(IsUniquePtr<type>::value, "Factories must return a unique_ptr");
-};
-
-template <typename Factory>
-using FactoryPtrType = typename FactoryPtrTypeHelper<Factory>::type;
-
-template <typename Factory>
-using FactoryElementType = typename FactoryPtrType<Factory>::element_type;
-
-template <typename T>
-class ExceptionSafetyTest {
-  using Factory = std::function<std::unique_ptr<T>()>;
-  using Operation = std::function<void(T*)>;
-  using Contract = std::function<AssertionResult(T*)>;
-
- public:
-  template <typename... Contracts>
-  explicit ExceptionSafetyTest(const Factory& f, const Operation& op,
-                               const Contracts&... contracts)
-      : factory_(f), operation_(op), contracts_{WrapContract(contracts)...} {}
-
-  AssertionResult Test() const {
-    for (int count = 0;; ++count) {
-      exceptions_internal::ConstructorTracker ct(count);
-
-      for (const auto& contract : contracts_) {
-        auto t_ptr = factory_();
-        try {
-          SetCountdown(count);
-          operation_(t_ptr.get());
-          // Unset for the case that the operation throws no exceptions, which
-          // would leave the countdown set and break the *next* exception safety
-          // test after this one.
-          UnsetCountdown();
-          return AssertionSuccess();
-        } catch (const exceptions_internal::TestException& e) {
-          if (!contract(t_ptr.get())) {
-            return AssertionFailure() << e.what() << " failed contract check";
-          }
-        }
-      }
-    }
-  }
-
- private:
-  template <typename ContractFn>
-  Contract WrapContract(const ContractFn& contract) {
-    return [contract](T* t_ptr) { return AssertionResult(contract(t_ptr)); };
-  }
-
-  Contract WrapContract(StrongGuaranteeTagType) {
-    return [this](T* t_ptr) { return AssertionResult(*factory_() == *t_ptr); };
-  }
-
-  Factory factory_;
-  Operation operation_;
-  std::vector<Contract> contracts_;
-};
-
-/*
- * Builds a tester object that tests if performing a operation on a T follows
- * exception safety guarantees. Verification is done via contract assertion
- * callbacks applied to T instances post-throw.
- *
- * Template parameters for ExceptionSafetyTestBuilder:
- *
- * - Factory: The factory object (passed in via tester.WithFactory(...) or
- *   tester.WithInitialValue(...)) must be invocable with the signature
- *   `std::unique_ptr<T> operator()() const` where T is the type being tested.
- *   It is used for reliably creating identical T instances to test on.
- *
- * - Operation: The operation object (passsed in via tester.WithOperation(...)
- *   or tester.Test(...)) must be invocable with the signature
- *   `void operator()(T*) const` where T is the type being tested. It is used
- *   for performing steps on a T instance that may throw and that need to be
- *   checked for exception safety. Each call to the operation will receive a
- *   fresh T instance so it's free to modify and destroy the T instances as it
- *   pleases.
- *
- * - Contracts...: The contract assertion callback objects (passed in via
- *   tester.WithContracts(...)) must be invocable with the signature
- *   `testing::AssertionResult operator()(T*) const` where T is the type being
- *   tested. Contract assertion callbacks are provided T instances post-throw.
- *   They must return testing::AssertionSuccess when the type contracts of the
- *   provided T instance hold. If the type contracts of the T instance do not
- *   hold, they must return testing::AssertionFailure. Execution order of
- *   Contracts... is unspecified. They will each individually get a fresh T
- *   instance so they are free to modify and destroy the T instances as they
- *   please.
- */
-template <typename Factory, typename Operation, typename... Contracts>
-class ExceptionSafetyTestBuilder {
- public:
-  /*
-   * Returns a new ExceptionSafetyTestBuilder with an included T factory based
-   * on the provided T instance. The existing factory will not be included in
-   * the newly created tester instance. The created factory returns a new T
-   * instance by copy-constructing the provided const T& t.
-   *
-   * Preconditions for tester.WithInitialValue(const T& t):
-   *
-   * - The const T& t object must be copy-constructible where T is the type
-   *   being tested. For non-copy-constructible objects, use the method
-   *   tester.WithFactory(...).
-   */
-  template <typename T>
-  ExceptionSafetyTestBuilder<DefaultFactory<T>, Operation, Contracts...>
-  WithInitialValue(const T& t) const {
-    return WithFactory(DefaultFactory<T>(t));
-  }
-
-  /*
-   * Returns a new ExceptionSafetyTestBuilder with the provided T factory
-   * included. The existing factory will not be included in the newly-created
-   * tester instance. This method is intended for use with types lacking a copy
-   * constructor. Types that can be copy-constructed should instead use the
-   * method tester.WithInitialValue(...).
-   */
-  template <typename NewFactory>
-  ExceptionSafetyTestBuilder<absl::decay_t<NewFactory>, Operation, Contracts...>
-  WithFactory(const NewFactory& new_factory) const {
-    return {new_factory, operation_, contracts_};
-  }
-
-  /*
-   * Returns a new ExceptionSafetyTestBuilder with the provided testable
-   * operation included. The existing operation will not be included in the
-   * newly created tester.
-   */
-  template <typename NewOperation>
-  ExceptionSafetyTestBuilder<Factory, absl::decay_t<NewOperation>, Contracts...>
-  WithOperation(const NewOperation& new_operation) const {
-    return {factory_, new_operation, contracts_};
-  }
-
-  /*
-   * Returns a new ExceptionSafetyTestBuilder with the provided MoreContracts...
-   * combined with the Contracts... that were already included in the instance
-   * on which the method was called. Contracts... cannot be removed or replaced
-   * once added to an ExceptionSafetyTestBuilder instance. A fresh object must
-   * be created in order to get an empty Contracts... list.
-   *
-   * In addition to passing in custom contract assertion callbacks, this method
-   * accepts `testing::strong_guarantee` as an argument which checks T instances
-   * post-throw against freshly created T instances via operator== to verify
-   * that any state changes made during the execution of the operation were
-   * properly rolled back.
-   */
-  template <typename... MoreContracts>
-  ExceptionSafetyTestBuilder<Factory, Operation, Contracts...,
-                             absl::decay_t<MoreContracts>...>
-  WithContracts(const MoreContracts&... more_contracts) const {
-    return {
-        factory_, operation_,
-        std::tuple_cat(contracts_, std::tuple<absl::decay_t<MoreContracts>...>(
-                                       more_contracts...))};
-  }
-
-  /*
-   * Returns a testing::AssertionResult that is the reduced result of the
-   * exception safety algorithm. The algorithm short circuits and returns
-   * AssertionFailure after the first contract callback returns an
-   * AssertionFailure. Otherwise, if all contract callbacks return an
-   * AssertionSuccess, the reduced result is AssertionSuccess.
-   *
-   * The passed-in testable operation will not be saved in a new tester instance
-   * nor will it modify/replace the existing tester instance. This is useful
-   * when each operation being tested is unique and does not need to be reused.
-   *
-   * Preconditions for tester.Test(const NewOperation& new_operation):
-   *
-   * - May only be called after at least one contract assertion callback and a
-   *   factory or initial value have been provided.
-   */
-  template <
-      typename NewOperation,
-      typename = EnableIfTestable<sizeof...(Contracts), Factory, NewOperation>>
-  testing::AssertionResult Test(const NewOperation& new_operation) const {
-    return TestImpl(new_operation, absl::index_sequence_for<Contracts...>());
-  }
-
-  /*
-   * Returns a testing::AssertionResult that is the reduced result of the
-   * exception safety algorithm. The algorithm short circuits and returns
-   * AssertionFailure after the first contract callback returns an
-   * AssertionFailure. Otherwise, if all contract callbacks return an
-   * AssertionSuccess, the reduced result is AssertionSuccess.
-   *
-   * Preconditions for tester.Test():
-   *
-   * - May only be called after at least one contract assertion callback, a
-   *   factory or initial value and a testable operation have been provided.
-   */
-  template <
-      typename LazyOperation = Operation,
-      typename = EnableIfTestable<sizeof...(Contracts), Factory, LazyOperation>>
-  testing::AssertionResult Test() const {
-    return Test(operation_);
-  }
-
- private:
-  template <typename, typename, typename...>
-  friend class ExceptionSafetyTestBuilder;
-
-  friend ExceptionSafetyTestBuilder<> testing::MakeExceptionSafetyTester();
-
-  ExceptionSafetyTestBuilder() {}
-
-  ExceptionSafetyTestBuilder(const Factory& f, const Operation& o,
-                             const std::tuple<Contracts...>& i)
-      : factory_(f), operation_(o), contracts_(i) {}
-
-  template <typename SelectedOperation, size_t... Indices>
-  testing::AssertionResult TestImpl(SelectedOperation selected_operation,
-                                    absl::index_sequence<Indices...>) const {
-    return ExceptionSafetyTest<FactoryElementType<Factory>>(
-               factory_, selected_operation, std::get<Indices>(contracts_)...)
-        .Test();
-  }
-
-  Factory factory_;
-  Operation operation_;
-  std::tuple<Contracts...> contracts_;
-};
-
-}  // namespace exceptions_internal
-
-}  // namespace testing
-
-#endif  // ABSL_HAVE_EXCEPTIONS
-
-#endif  // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/exception_testing.h b/third_party/abseil_cpp/absl/base/internal/exception_testing.h
deleted file mode 100644
index 01b5465571..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/exception_testing.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Testing utilities for ABSL types which throw exceptions.
-
-#ifndef ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_
-#define ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_
-
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-
-// ABSL_BASE_INTERNAL_EXPECT_FAIL tests either for a specified thrown exception
-// if exceptions are enabled, or for death with a specified text in the error
-// message
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-#define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \
-  EXPECT_THROW(expr, exception_t)
-
-#elif defined(__ANDROID__)
-// Android asserts do not log anywhere that gtest can currently inspect.
-// So we expect exit, but cannot match the message.
-#define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \
-  EXPECT_DEATH(expr, ".*")
-#else
-#define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \
-  EXPECT_DEATH_IF_SUPPORTED(expr, text)
-
-#endif
-
-#endif  // ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/exponential_biased.cc b/third_party/abseil_cpp/absl/base/internal/exponential_biased.cc
deleted file mode 100644
index 1b30c061e3..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/exponential_biased.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/exponential_biased.h"
-
-#include <stdint.h>
-
-#include <algorithm>
-#include <atomic>
-#include <cmath>
-#include <limits>
-
-#include "absl/base/attributes.h"
-#include "absl/base/optimization.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// The algorithm generates a random number between 0 and 1 and applies the
-// inverse cumulative distribution function for an exponential. Specifically:
-// Let m be the inverse of the sample period, then the probability
-// distribution function is m*exp(-mx) so the CDF is
-// p = 1 - exp(-mx), so
-// q = 1 - p = exp(-mx)
-// log_e(q) = -mx
-// -log_e(q)/m = x
-// log_2(q) * (-log_e(2) * 1/m) = x
-// In the code, q is actually in the range 1 to 2**26, hence the -26 below
-int64_t ExponentialBiased::GetSkipCount(int64_t mean) {
-  if (ABSL_PREDICT_FALSE(!initialized_)) {
-    Initialize();
-  }
-
-  uint64_t rng = NextRandom(rng_);
-  rng_ = rng;
-
-  // Take the top 26 bits as the random number
-  // (This plus the 1<<58 sampling bound give a max possible step of
-  // 5194297183973780480 bytes.)
-  // The uint32_t cast is to prevent a (hard-to-reproduce) NAN
-  // under piii debug for some binaries.
-  double q = static_cast<uint32_t>(rng >> (kPrngNumBits - 26)) + 1.0;
-  // Put the computed p-value through the CDF of a geometric.
-  double interval = bias_ + (std::log2(q) - 26) * (-std::log(2.0) * mean);
-  // Very large values of interval overflow int64_t. To avoid that, we will
-  // cheat and clamp any huge values to (int64_t max)/2. This is a potential
-  // source of bias, but the mean would need to be such a large value that it's
-  // not likely to come up. For example, with a mean of 1e18, the probability of
-  // hitting this condition is about 1/1000. For a mean of 1e17, standard
-  // calculators claim that this event won't happen.
-  if (interval > static_cast<double>(std::numeric_limits<int64_t>::max() / 2)) {
-    // Assume huge values are bias neutral, retain bias for next call.
-    return std::numeric_limits<int64_t>::max() / 2;
-  }
-  double value = std::round(interval);
-  bias_ = interval - value;
-  return value;
-}
-
-int64_t ExponentialBiased::GetStride(int64_t mean) {
-  return GetSkipCount(mean - 1) + 1;
-}
-
-void ExponentialBiased::Initialize() {
-  // We don't get well distributed numbers from `this` so we call NextRandom() a
-  // bunch to mush the bits around. We use a global_rand to handle the case
-  // where the same thread (by memory address) gets created and destroyed
-  // repeatedly.
-  ABSL_CONST_INIT static std::atomic<uint32_t> global_rand(0);
-  uint64_t r = reinterpret_cast<uint64_t>(this) +
-               global_rand.fetch_add(1, std::memory_order_relaxed);
-  for (int i = 0; i < 20; ++i) {
-    r = NextRandom(r);
-  }
-  rng_ = r;
-  initialized_ = true;
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/exponential_biased.h b/third_party/abseil_cpp/absl/base/internal/exponential_biased.h
deleted file mode 100644
index 94f79a3378..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/exponential_biased.h
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
-#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
-
-#include <stdint.h>
-
-#include "absl/base/config.h"
-#include "absl/base/macros.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// ExponentialBiased provides a small and fast random number generator for a
-// rounded exponential distribution. This generator manages very little state,
-// and imposes no synchronization overhead. This makes it useful in specialized
-// scenarios requiring minimum overhead, such as stride based periodic sampling.
-//
-// ExponentialBiased provides two closely related functions, GetSkipCount() and
-// GetStride(), both returning a rounded integer defining a number of events
-// required before some event with a given mean probability occurs.
-//
-// The distribution is useful to generate a random wait time or some periodic
-// event with a given mean probability. For example, if an action is supposed to
-// happen on average once every 'N' events, then we can get a random 'stride'
-// counting down how long before the event to happen. For example, if we'd want
-// to sample one in every 1000 'Frobber' calls, our code could look like this:
-//
-//   Frobber::Frobber() {
-//     stride_ = exponential_biased_.GetStride(1000);
-//   }
-//
-//   void Frobber::Frob(int arg) {
-//     if (--stride == 0) {
-//       SampleFrob(arg);
-//       stride_ = exponential_biased_.GetStride(1000);
-//     }
-//     ...
-//   }
-//
-// The rounding of the return value creates a bias, especially for smaller means
-// where the distribution of the fraction is not evenly distributed. We correct
-// this bias by tracking the fraction we rounded up or down on each iteration,
-// effectively tracking the distance between the cumulative value, and the
-// rounded cumulative value. For example, given a mean of 2:
-//
-//   raw = 1.63076, cumulative = 1.63076, rounded = 2, bias = -0.36923
-//   raw = 0.14624, cumulative = 1.77701, rounded = 2, bias =  0.14624
-//   raw = 4.93194, cumulative = 6.70895, rounded = 7, bias = -0.06805
-//   raw = 0.24206, cumulative = 6.95101, rounded = 7, bias =  0.24206
-//   etc...
-//
-// Adjusting with rounding bias is relatively trivial:
-//
-//    double value = bias_ + exponential_distribution(mean)();
-//    double rounded_value = std::round(value);
-//    bias_ = value - rounded_value;
-//    return rounded_value;
-//
-// This class is thread-compatible.
-class ExponentialBiased {
- public:
-  // The number of bits set by NextRandom.
-  static constexpr int kPrngNumBits = 48;
-
-  // `GetSkipCount()` returns the number of events to skip before some chosen
-  // event happens. For example, randomly tossing a coin, we will on average
-  // throw heads once before we get tails. We can simulate random coin tosses
-  // using GetSkipCount() as:
-  //
-  //   ExponentialBiased eb;
-  //   for (...) {
-  //     int number_of_heads_before_tail = eb.GetSkipCount(1);
-  //     for (int flips = 0; flips < number_of_heads_before_tail; ++flips) {
-  //       printf("head...");
-  //     }
-  //     printf("tail\n");
-  //   }
-  //
-  int64_t GetSkipCount(int64_t mean);
-
-  // GetStride() returns the number of events required for a specific event to
-  // happen. See the class comments for a usage example. `GetStride()` is
-  // equivalent to `GetSkipCount(mean - 1) + 1`. When to use `GetStride()` or
-  // `GetSkipCount()` depends mostly on what best fits the use case.
-  int64_t GetStride(int64_t mean);
-
-  // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1]
-  //
-  // This is public to enable testing.
-  static uint64_t NextRandom(uint64_t rnd);
-
- private:
-  void Initialize();
-
-  uint64_t rng_{0};
-  double bias_{0};
-  bool initialized_{false};
-};
-
-// Returns the next prng value.
-// pRNG is: aX+b mod c with a = 0x5DEECE66D, b =  0xB, c = 1<<48
-// This is the lrand64 generator.
-inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) {
-  const uint64_t prng_mult = uint64_t{0x5DEECE66D};
-  const uint64_t prng_add = 0xB;
-  const uint64_t prng_mod_power = 48;
-  const uint64_t prng_mod_mask =
-      ~((~static_cast<uint64_t>(0)) << prng_mod_power);
-  return (prng_mult * rnd + prng_add) & prng_mod_mask;
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/exponential_biased_test.cc b/third_party/abseil_cpp/absl/base/internal/exponential_biased_test.cc
deleted file mode 100644
index 075583ca6f..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/exponential_biased_test.cc
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/exponential_biased.h"
-
-#include <stddef.h>
-
-#include <cmath>
-#include <cstdint>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/str_cat.h"
-
-using ::testing::Ge;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-MATCHER_P2(IsBetween, a, b,
-           absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a,
-                        " and ", b)) {
-  return a <= arg && arg <= b;
-}
-
-// Tests of the quality of the random numbers generated
-// This uses the Anderson Darling test for uniformity.
-// See "Evaluating the Anderson-Darling Distribution" by Marsaglia
-// for details.
-
-// Short cut version of ADinf(z), z>0 (from Marsaglia)
-// This returns the p-value for Anderson Darling statistic in
-// the limit as n-> infinity. For finite n, apply the error fix below.
-double AndersonDarlingInf(double z) {
-  if (z < 2) {
-    return exp(-1.2337141 / z) / sqrt(z) *
-           (2.00012 +
-            (0.247105 -
-             (0.0649821 - (0.0347962 - (0.011672 - 0.00168691 * z) * z) * z) *
-                 z) *
-                z);
-  }
-  return exp(
-      -exp(1.0776 -
-           (2.30695 -
-            (0.43424 - (0.082433 - (0.008056 - 0.0003146 * z) * z) * z) * z) *
-               z));
-}
-
-// Corrects the approximation error in AndersonDarlingInf for small values of n
-// Add this to AndersonDarlingInf to get a better approximation
-// (from Marsaglia)
-double AndersonDarlingErrFix(int n, double x) {
-  if (x > 0.8) {
-    return (-130.2137 +
-            (745.2337 -
-             (1705.091 - (1950.646 - (1116.360 - 255.7844 * x) * x) * x) * x) *
-                x) /
-           n;
-  }
-  double cutoff = 0.01265 + 0.1757 / n;
-  if (x < cutoff) {
-    double t = x / cutoff;
-    t = sqrt(t) * (1 - t) * (49 * t - 102);
-    return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n;
-  } else {
-    double t = (x - cutoff) / (0.8 - cutoff);
-    t = -0.00022633 +
-        (6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864 * t) * t) * t) * t) *
-            t;
-    return t * (0.04213 + 0.01365 / n) / n;
-  }
-}
-
-// Returns the AndersonDarling p-value given n and the value of the statistic
-double AndersonDarlingPValue(int n, double z) {
-  double ad = AndersonDarlingInf(z);
-  double errfix = AndersonDarlingErrFix(n, ad);
-  return ad + errfix;
-}
-
-double AndersonDarlingStatistic(const std::vector<double>& random_sample) {
-  int n = random_sample.size();
-  double ad_sum = 0;
-  for (int i = 0; i < n; i++) {
-    ad_sum += (2 * i + 1) *
-              std::log(random_sample[i] * (1 - random_sample[n - 1 - i]));
-  }
-  double ad_statistic = -n - 1 / static_cast<double>(n) * ad_sum;
-  return ad_statistic;
-}
-
-// Tests if the array of doubles is uniformly distributed.
-// Returns the p-value of the Anderson Darling Statistic
-// for the given set of sorted random doubles
-// See "Evaluating the Anderson-Darling Distribution" by
-// Marsaglia and Marsaglia for details.
-double AndersonDarlingTest(const std::vector<double>& random_sample) {
-  double ad_statistic = AndersonDarlingStatistic(random_sample);
-  double p = AndersonDarlingPValue(random_sample.size(), ad_statistic);
-  return p;
-}
-
-TEST(ExponentialBiasedTest, CoinTossDemoWithGetSkipCount) {
-  ExponentialBiased eb;
-  for (int runs = 0; runs < 10; ++runs) {
-    for (int flips = eb.GetSkipCount(1); flips > 0; --flips) {
-      printf("head...");
-    }
-    printf("tail\n");
-  }
-  int heads = 0;
-  for (int i = 0; i < 10000000; i += 1 + eb.GetSkipCount(1)) {
-    ++heads;
-  }
-  printf("Heads = %d (%f%%)\n", heads, 100.0 * heads / 10000000);
-}
-
-TEST(ExponentialBiasedTest, SampleDemoWithStride) {
-  ExponentialBiased eb;
-  int stride = eb.GetStride(10);
-  int samples = 0;
-  for (int i = 0; i < 10000000; ++i) {
-    if (--stride == 0) {
-      ++samples;
-      stride = eb.GetStride(10);
-    }
-  }
-  printf("Samples = %d (%f%%)\n", samples, 100.0 * samples / 10000000);
-}
-
-
-// Testing that NextRandom generates uniform random numbers. Applies the
-// Anderson-Darling test for uniformity
-TEST(ExponentialBiasedTest, TestNextRandom) {
-  for (auto n : std::vector<int>({
-           10,  // Check short-range correlation
-           100, 1000,
-           10000  // Make sure there's no systemic error
-       })) {
-    uint64_t x = 1;
-    // This assumes that the prng returns 48 bit numbers
-    uint64_t max_prng_value = static_cast<uint64_t>(1) << 48;
-    // Initialize.
-    for (int i = 1; i <= 20; i++) {
-      x = ExponentialBiased::NextRandom(x);
-    }
-    std::vector<uint64_t> int_random_sample(n);
-    // Collect samples
-    for (int i = 0; i < n; i++) {
-      int_random_sample[i] = x;
-      x = ExponentialBiased::NextRandom(x);
-    }
-    // First sort them...
-    std::sort(int_random_sample.begin(), int_random_sample.end());
-    std::vector<double> random_sample(n);
-    // Convert them to uniform randoms (in the range [0,1])
-    for (int i = 0; i < n; i++) {
-      random_sample[i] =
-          static_cast<double>(int_random_sample[i]) / max_prng_value;
-    }
-    // Now compute the Anderson-Darling statistic
-    double ad_pvalue = AndersonDarlingTest(random_sample);
-    EXPECT_GT(std::min(ad_pvalue, 1 - ad_pvalue), 0.0001)
-        << "prng is not uniform: n = " << n << " p = " << ad_pvalue;
-  }
-}
-
-// The generator needs to be available as a thread_local and as a static
-// variable.
-TEST(ExponentialBiasedTest, InitializationModes) {
-  ABSL_CONST_INIT static ExponentialBiased eb_static;
-  EXPECT_THAT(eb_static.GetSkipCount(2), Ge(0));
-
-#ifdef ABSL_HAVE_THREAD_LOCAL
-  thread_local ExponentialBiased eb_thread;
-  EXPECT_THAT(eb_thread.GetSkipCount(2), Ge(0));
-#endif
-
-  ExponentialBiased eb_stack;
-  EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0));
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/fast_type_id.h b/third_party/abseil_cpp/absl/base/internal/fast_type_id.h
deleted file mode 100644
index 3db59e8374..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/fast_type_id.h
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_
-#define ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-template <typename Type>
-struct FastTypeTag {
-  constexpr static char dummy_var = 0;
-};
-
-template <typename Type>
-constexpr char FastTypeTag<Type>::dummy_var;
-
-// FastTypeId<Type>() evaluates at compile/link-time to a unique pointer for the
-// passed-in type. These are meant to be good match for keys into maps or
-// straight up comparisons.
-using FastTypeIdType = const void*;
-
-template <typename Type>
-constexpr inline FastTypeIdType FastTypeId() {
-  return &FastTypeTag<Type>::dummy_var;
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/fast_type_id_test.cc b/third_party/abseil_cpp/absl/base/internal/fast_type_id_test.cc
deleted file mode 100644
index 16f3c1458b..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/fast_type_id_test.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/fast_type_id.h"
-
-#include <cstdint>
-#include <map>
-#include <vector>
-
-#include "gtest/gtest.h"
-
-namespace {
-namespace bi = absl::base_internal;
-
-// NOLINTNEXTLINE
-#define PRIM_TYPES(A)   \
-  A(bool)               \
-  A(short)              \
-  A(unsigned short)     \
-  A(int)                \
-  A(unsigned int)       \
-  A(long)               \
-  A(unsigned long)      \
-  A(long long)          \
-  A(unsigned long long) \
-  A(float)              \
-  A(double)             \
-  A(long double)
-
-TEST(FastTypeIdTest, PrimitiveTypes) {
-  bi::FastTypeIdType type_ids[] = {
-#define A(T) bi::FastTypeId<T>(),
-    PRIM_TYPES(A)
-#undef A
-#define A(T) bi::FastTypeId<const T>(),
-    PRIM_TYPES(A)
-#undef A
-#define A(T) bi::FastTypeId<volatile T>(),
-    PRIM_TYPES(A)
-#undef A
-#define A(T) bi::FastTypeId<const volatile T>(),
-    PRIM_TYPES(A)
-#undef A
-  };
-  size_t total_type_ids = sizeof(type_ids) / sizeof(bi::FastTypeIdType);
-
-  for (int i = 0; i < total_type_ids; ++i) {
-    EXPECT_EQ(type_ids[i], type_ids[i]);
-    for (int j = 0; j < i; ++j) {
-      EXPECT_NE(type_ids[i], type_ids[j]);
-    }
-  }
-}
-
-#define FIXED_WIDTH_TYPES(A) \
-  A(int8_t)                  \
-  A(uint8_t)                 \
-  A(int16_t)                 \
-  A(uint16_t)                \
-  A(int32_t)                 \
-  A(uint32_t)                \
-  A(int64_t)                 \
-  A(uint64_t)
-
-TEST(FastTypeIdTest, FixedWidthTypes) {
-  bi::FastTypeIdType type_ids[] = {
-#define A(T) bi::FastTypeId<T>(),
-    FIXED_WIDTH_TYPES(A)
-#undef A
-#define A(T) bi::FastTypeId<const T>(),
-    FIXED_WIDTH_TYPES(A)
-#undef A
-#define A(T) bi::FastTypeId<volatile T>(),
-    FIXED_WIDTH_TYPES(A)
-#undef A
-#define A(T) bi::FastTypeId<const volatile T>(),
-    FIXED_WIDTH_TYPES(A)
-#undef A
-  };
-  size_t total_type_ids = sizeof(type_ids) / sizeof(bi::FastTypeIdType);
-
-  for (int i = 0; i < total_type_ids; ++i) {
-    EXPECT_EQ(type_ids[i], type_ids[i]);
-    for (int j = 0; j < i; ++j) {
-      EXPECT_NE(type_ids[i], type_ids[j]);
-    }
-  }
-}
-
-TEST(FastTypeIdTest, AliasTypes) {
-  using int_alias = int;
-  EXPECT_EQ(bi::FastTypeId<int_alias>(), bi::FastTypeId<int>());
-}
-
-TEST(FastTypeIdTest, TemplateSpecializations) {
-  EXPECT_NE(bi::FastTypeId<std::vector<int>>(),
-            bi::FastTypeId<std::vector<long>>());
-
-  EXPECT_NE((bi::FastTypeId<std::map<int, float>>()),
-            (bi::FastTypeId<std::map<int, double>>()));
-}
-
-struct Base {};
-struct Derived : Base {};
-struct PDerived : private Base {};
-
-TEST(FastTypeIdTest, Inheritance) {
-  EXPECT_NE(bi::FastTypeId<Base>(), bi::FastTypeId<Derived>());
-  EXPECT_NE(bi::FastTypeId<Base>(), bi::FastTypeId<PDerived>());
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/hide_ptr.h b/third_party/abseil_cpp/absl/base/internal/hide_ptr.h
deleted file mode 100644
index 1dba80909a..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/hide_ptr.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_HIDE_PTR_H_
-#define ABSL_BASE_INTERNAL_HIDE_PTR_H_
-
-#include <cstdint>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// Arbitrary value with high bits set. Xor'ing with it is unlikely
-// to map one valid pointer to another valid pointer.
-constexpr uintptr_t HideMask() {
-  return (uintptr_t{0xF03A5F7BU} << (sizeof(uintptr_t) - 4) * 8) | 0xF03A5F7BU;
-}
-
-// Hide a pointer from the leak checker. For internal use only.
-// Differs from absl::IgnoreLeak(ptr) in that absl::IgnoreLeak(ptr) causes ptr
-// and all objects reachable from ptr to be ignored by the leak checker.
-template <class T>
-inline uintptr_t HidePtr(T* ptr) {
-  return reinterpret_cast<uintptr_t>(ptr) ^ HideMask();
-}
-
-// Return a pointer that has been hidden from the leak checker.
-// For internal use only.
-template <class T>
-inline T* UnhidePtr(uintptr_t hidden) {
-  return reinterpret_cast<T*>(hidden ^ HideMask());
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_HIDE_PTR_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/identity.h b/third_party/abseil_cpp/absl/base/internal/identity.h
deleted file mode 100644
index a3154ed7bc..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/identity.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_BASE_INTERNAL_IDENTITY_H_
-#define ABSL_BASE_INTERNAL_IDENTITY_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace internal {
-
-template <typename T>
-struct identity {
-  typedef T type;
-};
-
-template <typename T>
-using identity_t = typename identity<T>::type;
-
-}  // namespace internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_IDENTITY_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/inline_variable.h b/third_party/abseil_cpp/absl/base/internal/inline_variable.h
deleted file mode 100644
index 130d8c2476..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/inline_variable.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_
-#define ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_
-
-#include <type_traits>
-
-#include "absl/base/internal/identity.h"
-
-// File:
-//   This file define a macro that allows the creation of or emulation of C++17
-//   inline variables based on whether or not the feature is supported.
-
-////////////////////////////////////////////////////////////////////////////////
-// Macro: ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init)
-//
-// Description:
-//   Expands to the equivalent of an inline constexpr instance of the specified
-//   `type` and `name`, initialized to the value `init`. If the compiler being
-//   used is detected as supporting actual inline variables as a language
-//   feature, then the macro expands to an actual inline variable definition.
-//
-// Requires:
-//   `type` is a type that is usable in an extern variable declaration.
-//
-// Requires: `name` is a valid identifier
-//
-// Requires:
-//   `init` is an expression that can be used in the following definition:
-//     constexpr type name = init;
-//
-// Usage:
-//
-//   // Equivalent to: `inline constexpr size_t variant_npos = -1;`
-//   ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1);
-//
-// Differences in implementation:
-//   For a direct, language-level inline variable, decltype(name) will be the
-//   type that was specified along with const qualification, whereas for
-//   emulated inline variables, decltype(name) may be different (in practice
-//   it will likely be a reference type).
-////////////////////////////////////////////////////////////////////////////////
-
-#ifdef __cpp_inline_variables
-
-// Clang's -Wmissing-variable-declarations option erroneously warned that
-// inline constexpr objects need to be pre-declared. This has now been fixed,
-// but we will need to support this workaround for people building with older
-// versions of clang.
-//
-// Bug: https://bugs.llvm.org/show_bug.cgi?id=35862
-//
-// Note:
-//   identity_t is used here so that the const and name are in the
-//   appropriate place for pointer types, reference types, function pointer
-//   types, etc..
-#if defined(__clang__)
-#define ABSL_INTERNAL_EXTERN_DECL(type, name) \
-  extern const ::absl::internal::identity_t<type> name;
-#else  // Otherwise, just define the macro to do nothing.
-#define ABSL_INTERNAL_EXTERN_DECL(type, name)
-#endif  // defined(__clang__)
-
-// See above comment at top of file for details.
-#define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \
-  ABSL_INTERNAL_EXTERN_DECL(type, name)                  \
-  inline constexpr ::absl::internal::identity_t<type> name = init
-
-#else
-
-// See above comment at top of file for details.
-//
-// Note:
-//   identity_t is used here so that the const and name are in the
-//   appropriate place for pointer types, reference types, function pointer
-//   types, etc..
-#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init)                  \
-  template <class /*AbslInternalDummy*/ = void>                               \
-  struct AbslInternalInlineVariableHolder##name {                             \
-    static constexpr ::absl::internal::identity_t<var_type> kInstance = init; \
-  };                                                                          \
-                                                                              \
-  template <class AbslInternalDummy>                                          \
-  constexpr ::absl::internal::identity_t<var_type>                            \
-      AbslInternalInlineVariableHolder##name<AbslInternalDummy>::kInstance;   \
-                                                                              \
-  static constexpr const ::absl::internal::identity_t<var_type>&              \
-      name = /* NOLINT */                                                     \
-      AbslInternalInlineVariableHolder##name<>::kInstance;                    \
-  static_assert(sizeof(void (*)(decltype(name))) != 0,                        \
-                "Silence unused variable warnings.")
-
-#endif  // __cpp_inline_variables
-
-#endif  // ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/inline_variable_testing.h b/third_party/abseil_cpp/absl/base/internal/inline_variable_testing.h
deleted file mode 100644
index 3856b9f80f..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/inline_variable_testing.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INLINE_VARIABLE_TESTING_H_
-#define ABSL_BASE_INLINE_VARIABLE_TESTING_H_
-
-#include "absl/base/internal/inline_variable.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace inline_variable_testing_internal {
-
-struct Foo {
-  int value = 5;
-};
-
-ABSL_INTERNAL_INLINE_CONSTEXPR(Foo, inline_variable_foo, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(Foo, other_inline_variable_foo, {});
-
-ABSL_INTERNAL_INLINE_CONSTEXPR(int, inline_variable_int, 5);
-ABSL_INTERNAL_INLINE_CONSTEXPR(int, other_inline_variable_int, 5);
-
-ABSL_INTERNAL_INLINE_CONSTEXPR(void(*)(), inline_variable_fun_ptr, nullptr);
-
-const Foo& get_foo_a();
-const Foo& get_foo_b();
-
-const int& get_int_a();
-const int& get_int_b();
-
-}  // namespace inline_variable_testing_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INLINE_VARIABLE_TESTING_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/invoke.h b/third_party/abseil_cpp/absl/base/internal/invoke.h
deleted file mode 100644
index 5c71f32823..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/invoke.h
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// absl::base_internal::invoke(f, args...) is an implementation of
-// INVOKE(f, args...) from section [func.require] of the C++ standard.
-//
-// [func.require]
-// Define INVOKE (f, t1, t2, ..., tN) as follows:
-// 1. (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T
-//    and t1 is an object of type T or a reference to an object of type T or a
-//    reference to an object of a type derived from T;
-// 2. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
-//    class T and t1 is not one of the types described in the previous item;
-// 3. t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is
-//    an object of type T or a reference to an object of type T or a reference
-//    to an object of a type derived from T;
-// 4. (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1
-//    is not one of the types described in the previous item;
-// 5. f(t1, t2, ..., tN) in all other cases.
-//
-// The implementation is SFINAE-friendly: substitution failure within invoke()
-// isn't an error.
-
-#ifndef ABSL_BASE_INTERNAL_INVOKE_H_
-#define ABSL_BASE_INTERNAL_INVOKE_H_
-
-#include <algorithm>
-#include <type_traits>
-#include <utility>
-
-#include "absl/meta/type_traits.h"
-
-// The following code is internal implementation detail.  See the comment at the
-// top of this file for the API documentation.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// The five classes below each implement one of the clauses from the definition
-// of INVOKE. The inner class template Accept<F, Args...> checks whether the
-// clause is applicable; static function template Invoke(f, args...) does the
-// invocation.
-//
-// By separating the clause selection logic from invocation we make sure that
-// Invoke() does exactly what the standard says.
-
-template <typename Derived>
-struct StrippedAccept {
-  template <typename... Args>
-  struct Accept : Derived::template AcceptImpl<typename std::remove_cv<
-                      typename std::remove_reference<Args>::type>::type...> {};
-};
-
-// (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T
-// and t1 is an object of type T or a reference to an object of type T or a
-// reference to an object of a type derived from T.
-struct MemFunAndRef : StrippedAccept<MemFunAndRef> {
-  template <typename... Args>
-  struct AcceptImpl : std::false_type {};
-
-  template <typename MemFunType, typename C, typename Obj, typename... Args>
-  struct AcceptImpl<MemFunType C::*, Obj, Args...>
-      : std::integral_constant<bool, std::is_base_of<C, Obj>::value &&
-                                         absl::is_function<MemFunType>::value> {
-  };
-
-  template <typename MemFun, typename Obj, typename... Args>
-  static decltype((std::declval<Obj>().*
-                   std::declval<MemFun>())(std::declval<Args>()...))
-  Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) {
-    return (std::forward<Obj>(obj).*
-            std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...);
-  }
-};
-
-// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
-// class T and t1 is not one of the types described in the previous item.
-struct MemFunAndPtr : StrippedAccept<MemFunAndPtr> {
-  template <typename... Args>
-  struct AcceptImpl : std::false_type {};
-
-  template <typename MemFunType, typename C, typename Ptr, typename... Args>
-  struct AcceptImpl<MemFunType C::*, Ptr, Args...>
-      : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value &&
-                                         absl::is_function<MemFunType>::value> {
-  };
-
-  template <typename MemFun, typename Ptr, typename... Args>
-  static decltype(((*std::declval<Ptr>()).*
-                   std::declval<MemFun>())(std::declval<Args>()...))
-  Invoke(MemFun&& mem_fun, Ptr&& ptr, Args&&... args) {
-    return ((*std::forward<Ptr>(ptr)).*
-            std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...);
-  }
-};
-
-// t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is
-// an object of type T or a reference to an object of type T or a reference
-// to an object of a type derived from T.
-struct DataMemAndRef : StrippedAccept<DataMemAndRef> {
-  template <typename... Args>
-  struct AcceptImpl : std::false_type {};
-
-  template <typename R, typename C, typename Obj>
-  struct AcceptImpl<R C::*, Obj>
-      : std::integral_constant<bool, std::is_base_of<C, Obj>::value &&
-                                         !absl::is_function<R>::value> {};
-
-  template <typename DataMem, typename Ref>
-  static decltype(std::declval<Ref>().*std::declval<DataMem>()) Invoke(
-      DataMem&& data_mem, Ref&& ref) {
-    return std::forward<Ref>(ref).*std::forward<DataMem>(data_mem);
-  }
-};
-
-// (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1
-// is not one of the types described in the previous item.
-struct DataMemAndPtr : StrippedAccept<DataMemAndPtr> {
-  template <typename... Args>
-  struct AcceptImpl : std::false_type {};
-
-  template <typename R, typename C, typename Ptr>
-  struct AcceptImpl<R C::*, Ptr>
-      : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value &&
-                                         !absl::is_function<R>::value> {};
-
-  template <typename DataMem, typename Ptr>
-  static decltype((*std::declval<Ptr>()).*std::declval<DataMem>()) Invoke(
-      DataMem&& data_mem, Ptr&& ptr) {
-    return (*std::forward<Ptr>(ptr)).*std::forward<DataMem>(data_mem);
-  }
-};
-
-// f(t1, t2, ..., tN) in all other cases.
-struct Callable {
-  // Callable doesn't have Accept because it's the last clause that gets picked
-  // when none of the previous clauses are applicable.
-  template <typename F, typename... Args>
-  static decltype(std::declval<F>()(std::declval<Args>()...)) Invoke(
-      F&& f, Args&&... args) {
-    return std::forward<F>(f)(std::forward<Args>(args)...);
-  }
-};
-
-// Resolves to the first matching clause.
-template <typename... Args>
-struct Invoker {
-  typedef typename std::conditional<
-      MemFunAndRef::Accept<Args...>::value, MemFunAndRef,
-      typename std::conditional<
-          MemFunAndPtr::Accept<Args...>::value, MemFunAndPtr,
-          typename std::conditional<
-              DataMemAndRef::Accept<Args...>::value, DataMemAndRef,
-              typename std::conditional<DataMemAndPtr::Accept<Args...>::value,
-                                        DataMemAndPtr, Callable>::type>::type>::
-          type>::type type;
-};
-
-// The result type of Invoke<F, Args...>.
-template <typename F, typename... Args>
-using invoke_result_t = decltype(Invoker<F, Args...>::type::Invoke(
-    std::declval<F>(), std::declval<Args>()...));
-
-// Invoke(f, args...) is an implementation of INVOKE(f, args...) from section
-// [func.require] of the C++ standard.
-template <typename F, typename... Args>
-invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) {
-  return Invoker<F, Args...>::type::Invoke(std::forward<F>(f),
-                                           std::forward<Args>(args)...);
-}
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_INVOKE_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/low_level_alloc.cc b/third_party/abseil_cpp/absl/base/internal/low_level_alloc.cc
deleted file mode 100644
index 229ab9162d..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/low_level_alloc.cc
+++ /dev/null
@@ -1,620 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// A low-level allocator that can be used by other low-level
-// modules without introducing dependency cycles.
-// This allocator is slow and wasteful of memory;
-// it should not be used when performance is key.
-
-#include "absl/base/internal/low_level_alloc.h"
-
-#include <type_traits>
-
-#include "absl/base/call_once.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/direct_mmap.h"
-#include "absl/base/internal/scheduling_mode.h"
-#include "absl/base/macros.h"
-#include "absl/base/thread_annotations.h"
-
-// LowLevelAlloc requires that the platform support low-level
-// allocation of virtual memory. Platforms lacking this cannot use
-// LowLevelAlloc.
-#ifndef ABSL_LOW_LEVEL_ALLOC_MISSING
-
-#ifndef _WIN32
-#include <pthread.h>
-#include <signal.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#else
-#include <windows.h>
-#endif
-
-#include <string.h>
-#include <algorithm>
-#include <atomic>
-#include <cerrno>
-#include <cstddef>
-#include <new>                   // for placement-new
-
-#include "absl/base/dynamic_annotations.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/spinlock.h"
-
-// MAP_ANONYMOUS
-#if defined(__APPLE__)
-// For mmap, Linux defines both MAP_ANONYMOUS and MAP_ANON and says MAP_ANON is
-// deprecated. In Darwin, MAP_ANON is all there is.
-#if !defined MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif  // !MAP_ANONYMOUS
-#endif  // __APPLE__
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// A first-fit allocator with amortized logarithmic free() time.
-
-// ---------------------------------------------------------------------------
-static const int kMaxLevel = 30;
-
-namespace {
-// This struct describes one allocated block, or one free block.
-struct AllocList {
-  struct Header {
-    // Size of entire region, including this field. Must be
-    // first. Valid in both allocated and unallocated blocks.
-    uintptr_t size;
-
-    // kMagicAllocated or kMagicUnallocated xor this.
-    uintptr_t magic;
-
-    // Pointer to parent arena.
-    LowLevelAlloc::Arena *arena;
-
-    // Aligns regions to 0 mod 2*sizeof(void*).
-    void *dummy_for_alignment;
-  } header;
-
-  // Next two fields: in unallocated blocks: freelist skiplist data
-  //                  in allocated blocks: overlaps with client data
-
-  // Levels in skiplist used.
-  int levels;
-
-  // Actually has levels elements. The AllocList node may not have room
-  // for all kMaxLevel entries. See max_fit in LLA_SkiplistLevels().
-  AllocList *next[kMaxLevel];
-};
-}  // namespace
-
-// ---------------------------------------------------------------------------
-// A trivial skiplist implementation.  This is used to keep the freelist
-// in address order while taking only logarithmic time per insert and delete.
-
-// An integer approximation of log2(size/base)
-// Requires size >= base.
-static int IntLog2(size_t size, size_t base) {
-  int result = 0;
-  for (size_t i = size; i > base; i >>= 1) {  // i == floor(size/2**result)
-    result++;
-  }
-  //    floor(size / 2**result) <= base < floor(size / 2**(result-1))
-  // =>     log2(size/(base+1)) <= result < 1+log2(size/base)
-  // => result ~= log2(size/base)
-  return result;
-}
-
-// Return a random integer n:  p(n)=1/(2**n) if 1 <= n; p(n)=0 if n < 1.
-static int Random(uint32_t *state) {
-  uint32_t r = *state;
-  int result = 1;
-  while ((((r = r*1103515245 + 12345) >> 30) & 1) == 0) {
-    result++;
-  }
-  *state = r;
-  return result;
-}
-
-// Return a number of skiplist levels for a node of size bytes, where
-// base is the minimum node size.  Compute level=log2(size / base)+n
-// where n is 1 if random is false and otherwise a random number generated with
-// the standard distribution for a skiplist:  See Random() above.
-// Bigger nodes tend to have more skiplist levels due to the log2(size / base)
-// term, so first-fit searches touch fewer nodes.  "level" is clipped so
-// level<kMaxLevel and next[level-1] will fit in the node.
-// 0 < LLA_SkiplistLevels(x,y,false) <= LLA_SkiplistLevels(x,y,true) < kMaxLevel
-static int LLA_SkiplistLevels(size_t size, size_t base, uint32_t *random) {
-  // max_fit is the maximum number of levels that will fit in a node for the
-  // given size.   We can't return more than max_fit, no matter what the
-  // random number generator says.
-  size_t max_fit = (size - offsetof(AllocList, next)) / sizeof(AllocList *);
-  int level = IntLog2(size, base) + (random != nullptr ? Random(random) : 1);
-  if (static_cast<size_t>(level) > max_fit) level = static_cast<int>(max_fit);
-  if (level > kMaxLevel-1) level = kMaxLevel - 1;
-  ABSL_RAW_CHECK(level >= 1, "block not big enough for even one level");
-  return level;
-}
-
-// Return "atleast", the first element of AllocList *head s.t. *atleast >= *e.
-// For 0 <= i < head->levels, set prev[i] to "no_greater", where no_greater
-// points to the last element at level i in the AllocList less than *e, or is
-// head if no such element exists.
-static AllocList *LLA_SkiplistSearch(AllocList *head,
-                                     AllocList *e, AllocList **prev) {
-  AllocList *p = head;
-  for (int level = head->levels - 1; level >= 0; level--) {
-    for (AllocList *n; (n = p->next[level]) != nullptr && n < e; p = n) {
-    }
-    prev[level] = p;
-  }
-  return (head->levels == 0) ? nullptr : prev[0]->next[0];
-}
-
-// Insert element *e into AllocList *head.  Set prev[] as LLA_SkiplistSearch.
-// Requires that e->levels be previously set by the caller (using
-// LLA_SkiplistLevels())
-static void LLA_SkiplistInsert(AllocList *head, AllocList *e,
-                               AllocList **prev) {
-  LLA_SkiplistSearch(head, e, prev);
-  for (; head->levels < e->levels; head->levels++) {  // extend prev pointers
-    prev[head->levels] = head;                        // to all *e's levels
-  }
-  for (int i = 0; i != e->levels; i++) {  // add element to list
-    e->next[i] = prev[i]->next[i];
-    prev[i]->next[i] = e;
-  }
-}
-
-// Remove element *e from AllocList *head.  Set prev[] as LLA_SkiplistSearch().
-// Requires that e->levels be previous set by the caller (using
-// LLA_SkiplistLevels())
-static void LLA_SkiplistDelete(AllocList *head, AllocList *e,
-                               AllocList **prev) {
-  AllocList *found = LLA_SkiplistSearch(head, e, prev);
-  ABSL_RAW_CHECK(e == found, "element not in freelist");
-  for (int i = 0; i != e->levels && prev[i]->next[i] == e; i++) {
-    prev[i]->next[i] = e->next[i];
-  }
-  while (head->levels > 0 && head->next[head->levels - 1] == nullptr) {
-    head->levels--;   // reduce head->levels if level unused
-  }
-}
-
-// ---------------------------------------------------------------------------
-// Arena implementation
-
-// Metadata for an LowLevelAlloc arena instance.
-struct LowLevelAlloc::Arena {
-  // Constructs an arena with the given LowLevelAlloc flags.
-  explicit Arena(uint32_t flags_value);
-
-  base_internal::SpinLock mu;
-  // Head of free list, sorted by address
-  AllocList freelist ABSL_GUARDED_BY(mu);
-  // Count of allocated blocks
-  int32_t allocation_count ABSL_GUARDED_BY(mu);
-  // flags passed to NewArena
-  const uint32_t flags;
-  // Result of sysconf(_SC_PAGESIZE)
-  const size_t pagesize;
-  // Lowest power of two >= max(16, sizeof(AllocList))
-  const size_t round_up;
-  // Smallest allocation block size
-  const size_t min_size;
-  // PRNG state
-  uint32_t random ABSL_GUARDED_BY(mu);
-};
-
-namespace {
-// Static storage space for the lazily-constructed, default global arena
-// instances.  We require this space because the whole point of LowLevelAlloc
-// is to avoid relying on malloc/new.
-alignas(LowLevelAlloc::Arena) unsigned char default_arena_storage[sizeof(
-    LowLevelAlloc::Arena)];
-alignas(LowLevelAlloc::Arena) unsigned char unhooked_arena_storage[sizeof(
-    LowLevelAlloc::Arena)];
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-alignas(
-    LowLevelAlloc::Arena) unsigned char unhooked_async_sig_safe_arena_storage
-    [sizeof(LowLevelAlloc::Arena)];
-#endif
-
-// We must use LowLevelCallOnce here to construct the global arenas, rather than
-// using function-level statics, to avoid recursively invoking the scheduler.
-absl::once_flag create_globals_once;
-
-void CreateGlobalArenas() {
-  new (&default_arena_storage)
-      LowLevelAlloc::Arena(LowLevelAlloc::kCallMallocHook);
-  new (&unhooked_arena_storage) LowLevelAlloc::Arena(0);
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-  new (&unhooked_async_sig_safe_arena_storage)
-      LowLevelAlloc::Arena(LowLevelAlloc::kAsyncSignalSafe);
-#endif
-}
-
-// Returns a global arena that does not call into hooks.  Used by NewArena()
-// when kCallMallocHook is not set.
-LowLevelAlloc::Arena* UnhookedArena() {
-  base_internal::LowLevelCallOnce(&create_globals_once, CreateGlobalArenas);
-  return reinterpret_cast<LowLevelAlloc::Arena*>(&unhooked_arena_storage);
-}
-
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-// Returns a global arena that is async-signal safe.  Used by NewArena() when
-// kAsyncSignalSafe is set.
-LowLevelAlloc::Arena *UnhookedAsyncSigSafeArena() {
-  base_internal::LowLevelCallOnce(&create_globals_once, CreateGlobalArenas);
-  return reinterpret_cast<LowLevelAlloc::Arena *>(
-      &unhooked_async_sig_safe_arena_storage);
-}
-#endif
-
-}  // namespace
-
-// Returns the default arena, as used by LowLevelAlloc::Alloc() and friends.
-LowLevelAlloc::Arena *LowLevelAlloc::DefaultArena() {
-  base_internal::LowLevelCallOnce(&create_globals_once, CreateGlobalArenas);
-  return reinterpret_cast<LowLevelAlloc::Arena*>(&default_arena_storage);
-}
-
-// magic numbers to identify allocated and unallocated blocks
-static const uintptr_t kMagicAllocated = 0x4c833e95U;
-static const uintptr_t kMagicUnallocated = ~kMagicAllocated;
-
-namespace {
-class ABSL_SCOPED_LOCKABLE ArenaLock {
- public:
-  explicit ArenaLock(LowLevelAlloc::Arena *arena)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION(arena->mu)
-      : arena_(arena) {
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-    if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
-      sigset_t all;
-      sigfillset(&all);
-      mask_valid_ = pthread_sigmask(SIG_BLOCK, &all, &mask_) == 0;
-    }
-#endif
-    arena_->mu.Lock();
-  }
-  ~ArenaLock() { ABSL_RAW_CHECK(left_, "haven't left Arena region"); }
-  void Leave() ABSL_UNLOCK_FUNCTION() {
-    arena_->mu.Unlock();
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-    if (mask_valid_) {
-      const int err = pthread_sigmask(SIG_SETMASK, &mask_, nullptr);
-      if (err != 0) {
-        ABSL_RAW_LOG(FATAL, "pthread_sigmask failed: %d", err);
-      }
-    }
-#endif
-    left_ = true;
-  }
-
- private:
-  bool left_ = false;  // whether left region
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-  bool mask_valid_ = false;
-  sigset_t mask_;  // old mask of blocked signals
-#endif
-  LowLevelAlloc::Arena *arena_;
-  ArenaLock(const ArenaLock &) = delete;
-  ArenaLock &operator=(const ArenaLock &) = delete;
-};
-}  // namespace
-
-// create an appropriate magic number for an object at "ptr"
-// "magic" should be kMagicAllocated or kMagicUnallocated
-inline static uintptr_t Magic(uintptr_t magic, AllocList::Header *ptr) {
-  return magic ^ reinterpret_cast<uintptr_t>(ptr);
-}
-
-namespace {
-size_t GetPageSize() {
-#ifdef _WIN32
-  SYSTEM_INFO system_info;
-  GetSystemInfo(&system_info);
-  return std::max(system_info.dwPageSize, system_info.dwAllocationGranularity);
-#elif defined(__wasm__) || defined(__asmjs__)
-  return getpagesize();
-#else
-  return sysconf(_SC_PAGESIZE);
-#endif
-}
-
-size_t RoundedUpBlockSize() {
-  // Round up block sizes to a power of two close to the header size.
-  size_t round_up = 16;
-  while (round_up < sizeof(AllocList::Header)) {
-    round_up += round_up;
-  }
-  return round_up;
-}
-
-}  // namespace
-
-LowLevelAlloc::Arena::Arena(uint32_t flags_value)
-    : mu(base_internal::SCHEDULE_KERNEL_ONLY),
-      allocation_count(0),
-      flags(flags_value),
-      pagesize(GetPageSize()),
-      round_up(RoundedUpBlockSize()),
-      min_size(2 * round_up),
-      random(0) {
-  freelist.header.size = 0;
-  freelist.header.magic =
-      Magic(kMagicUnallocated, &freelist.header);
-  freelist.header.arena = this;
-  freelist.levels = 0;
-  memset(freelist.next, 0, sizeof(freelist.next));
-}
-
-// L < meta_data_arena->mu
-LowLevelAlloc::Arena *LowLevelAlloc::NewArena(int32_t flags) {
-  Arena *meta_data_arena = DefaultArena();
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-  if ((flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
-    meta_data_arena = UnhookedAsyncSigSafeArena();
-  } else  // NOLINT(readability/braces)
-#endif
-      if ((flags & LowLevelAlloc::kCallMallocHook) == 0) {
-    meta_data_arena = UnhookedArena();
-  }
-  Arena *result =
-    new (AllocWithArena(sizeof (*result), meta_data_arena)) Arena(flags);
-  return result;
-}
-
-// L < arena->mu, L < arena->arena->mu
-bool LowLevelAlloc::DeleteArena(Arena *arena) {
-  ABSL_RAW_CHECK(
-      arena != nullptr && arena != DefaultArena() && arena != UnhookedArena(),
-      "may not delete default arena");
-  ArenaLock section(arena);
-  if (arena->allocation_count != 0) {
-    section.Leave();
-    return false;
-  }
-  while (arena->freelist.next[0] != nullptr) {
-    AllocList *region = arena->freelist.next[0];
-    size_t size = region->header.size;
-    arena->freelist.next[0] = region->next[0];
-    ABSL_RAW_CHECK(
-        region->header.magic == Magic(kMagicUnallocated, &region->header),
-        "bad magic number in DeleteArena()");
-    ABSL_RAW_CHECK(region->header.arena == arena,
-                   "bad arena pointer in DeleteArena()");
-    ABSL_RAW_CHECK(size % arena->pagesize == 0,
-                   "empty arena has non-page-aligned block size");
-    ABSL_RAW_CHECK(reinterpret_cast<uintptr_t>(region) % arena->pagesize == 0,
-                   "empty arena has non-page-aligned block");
-    int munmap_result;
-#ifdef _WIN32
-    munmap_result = VirtualFree(region, 0, MEM_RELEASE);
-    ABSL_RAW_CHECK(munmap_result != 0,
-                   "LowLevelAlloc::DeleteArena: VitualFree failed");
-#else
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-    if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) {
-      munmap_result = munmap(region, size);
-    } else {
-      munmap_result = base_internal::DirectMunmap(region, size);
-    }
-#else
-    munmap_result = munmap(region, size);
-#endif  // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-    if (munmap_result != 0) {
-      ABSL_RAW_LOG(FATAL, "LowLevelAlloc::DeleteArena: munmap failed: %d",
-                   errno);
-    }
-#endif  // _WIN32
-  }
-  section.Leave();
-  arena->~Arena();
-  Free(arena);
-  return true;
-}
-
-// ---------------------------------------------------------------------------
-
-// Addition, checking for overflow.  The intent is to die if an external client
-// manages to push through a request that would cause arithmetic to fail.
-static inline uintptr_t CheckedAdd(uintptr_t a, uintptr_t b) {
-  uintptr_t sum = a + b;
-  ABSL_RAW_CHECK(sum >= a, "LowLevelAlloc arithmetic overflow");
-  return sum;
-}
-
-// Return value rounded up to next multiple of align.
-// align must be a power of two.
-static inline uintptr_t RoundUp(uintptr_t addr, uintptr_t align) {
-  return CheckedAdd(addr, align - 1) & ~(align - 1);
-}
-
-// Equivalent to "return prev->next[i]" but with sanity checking
-// that the freelist is in the correct order, that it
-// consists of regions marked "unallocated", and that no two regions
-// are adjacent in memory (they should have been coalesced).
-// L >= arena->mu
-static AllocList *Next(int i, AllocList *prev, LowLevelAlloc::Arena *arena) {
-  ABSL_RAW_CHECK(i < prev->levels, "too few levels in Next()");
-  AllocList *next = prev->next[i];
-  if (next != nullptr) {
-    ABSL_RAW_CHECK(
-        next->header.magic == Magic(kMagicUnallocated, &next->header),
-        "bad magic number in Next()");
-    ABSL_RAW_CHECK(next->header.arena == arena, "bad arena pointer in Next()");
-    if (prev != &arena->freelist) {
-      ABSL_RAW_CHECK(prev < next, "unordered freelist");
-      ABSL_RAW_CHECK(reinterpret_cast<char *>(prev) + prev->header.size <
-                         reinterpret_cast<char *>(next),
-                     "malformed freelist");
-    }
-  }
-  return next;
-}
-
-// Coalesce list item "a" with its successor if they are adjacent.
-static void Coalesce(AllocList *a) {
-  AllocList *n = a->next[0];
-  if (n != nullptr && reinterpret_cast<char *>(a) + a->header.size ==
-                          reinterpret_cast<char *>(n)) {
-    LowLevelAlloc::Arena *arena = a->header.arena;
-    a->header.size += n->header.size;
-    n->header.magic = 0;
-    n->header.arena = nullptr;
-    AllocList *prev[kMaxLevel];
-    LLA_SkiplistDelete(&arena->freelist, n, prev);
-    LLA_SkiplistDelete(&arena->freelist, a, prev);
-    a->levels = LLA_SkiplistLevels(a->header.size, arena->min_size,
-                                   &arena->random);
-    LLA_SkiplistInsert(&arena->freelist, a, prev);
-  }
-}
-
-// Adds block at location "v" to the free list
-// L >= arena->mu
-static void AddToFreelist(void *v, LowLevelAlloc::Arena *arena) {
-  AllocList *f = reinterpret_cast<AllocList *>(
-                        reinterpret_cast<char *>(v) - sizeof (f->header));
-  ABSL_RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header),
-                 "bad magic number in AddToFreelist()");
-  ABSL_RAW_CHECK(f->header.arena == arena,
-                 "bad arena pointer in AddToFreelist()");
-  f->levels = LLA_SkiplistLevels(f->header.size, arena->min_size,
-                                 &arena->random);
-  AllocList *prev[kMaxLevel];
-  LLA_SkiplistInsert(&arena->freelist, f, prev);
-  f->header.magic = Magic(kMagicUnallocated, &f->header);
-  Coalesce(f);                  // maybe coalesce with successor
-  Coalesce(prev[0]);            // maybe coalesce with predecessor
-}
-
-// Frees storage allocated by LowLevelAlloc::Alloc().
-// L < arena->mu
-void LowLevelAlloc::Free(void *v) {
-  if (v != nullptr) {
-    AllocList *f = reinterpret_cast<AllocList *>(
-                        reinterpret_cast<char *>(v) - sizeof (f->header));
-    LowLevelAlloc::Arena *arena = f->header.arena;
-    ArenaLock section(arena);
-    AddToFreelist(v, arena);
-    ABSL_RAW_CHECK(arena->allocation_count > 0, "nothing in arena to free");
-    arena->allocation_count--;
-    section.Leave();
-  }
-}
-
-// allocates and returns a block of size bytes, to be freed with Free()
-// L < arena->mu
-static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
-  void *result = nullptr;
-  if (request != 0) {
-    AllocList *s;       // will point to region that satisfies request
-    ArenaLock section(arena);
-    // round up with header
-    size_t req_rnd = RoundUp(CheckedAdd(request, sizeof (s->header)),
-                             arena->round_up);
-    for (;;) {      // loop until we find a suitable region
-      // find the minimum levels that a block of this size must have
-      int i = LLA_SkiplistLevels(req_rnd, arena->min_size, nullptr) - 1;
-      if (i < arena->freelist.levels) {   // potential blocks exist
-        AllocList *before = &arena->freelist;  // predecessor of s
-        while ((s = Next(i, before, arena)) != nullptr &&
-               s->header.size < req_rnd) {
-          before = s;
-        }
-        if (s != nullptr) {       // we found a region
-          break;
-        }
-      }
-      // we unlock before mmap() both because mmap() may call a callback hook,
-      // and because it may be slow.
-      arena->mu.Unlock();
-      // mmap generous 64K chunks to decrease
-      // the chances/impact of fragmentation:
-      size_t new_pages_size = RoundUp(req_rnd, arena->pagesize * 16);
-      void *new_pages;
-#ifdef _WIN32
-      new_pages = VirtualAlloc(0, new_pages_size,
-                               MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
-      ABSL_RAW_CHECK(new_pages != nullptr, "VirtualAlloc failed");
-#else
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-      if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
-        new_pages = base_internal::DirectMmap(nullptr, new_pages_size,
-            PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
-      } else {
-        new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
-                         MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-      }
-#else
-      new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
-                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-#endif  // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-      if (new_pages == MAP_FAILED) {
-        ABSL_RAW_LOG(FATAL, "mmap error: %d", errno);
-      }
-
-#endif  // _WIN32
-      arena->mu.Lock();
-      s = reinterpret_cast<AllocList *>(new_pages);
-      s->header.size = new_pages_size;
-      // Pretend the block is allocated; call AddToFreelist() to free it.
-      s->header.magic = Magic(kMagicAllocated, &s->header);
-      s->header.arena = arena;
-      AddToFreelist(&s->levels, arena);  // insert new region into free list
-    }
-    AllocList *prev[kMaxLevel];
-    LLA_SkiplistDelete(&arena->freelist, s, prev);    // remove from free list
-    // s points to the first free region that's big enough
-    if (CheckedAdd(req_rnd, arena->min_size) <= s->header.size) {
-      // big enough to split
-      AllocList *n = reinterpret_cast<AllocList *>
-                        (req_rnd + reinterpret_cast<char *>(s));
-      n->header.size = s->header.size - req_rnd;
-      n->header.magic = Magic(kMagicAllocated, &n->header);
-      n->header.arena = arena;
-      s->header.size = req_rnd;
-      AddToFreelist(&n->levels, arena);
-    }
-    s->header.magic = Magic(kMagicAllocated, &s->header);
-    ABSL_RAW_CHECK(s->header.arena == arena, "");
-    arena->allocation_count++;
-    section.Leave();
-    result = &s->levels;
-  }
-  ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(result, request);
-  return result;
-}
-
-void *LowLevelAlloc::Alloc(size_t request) {
-  void *result = DoAllocWithArena(request, DefaultArena());
-  return result;
-}
-
-void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) {
-  ABSL_RAW_CHECK(arena != nullptr, "must pass a valid arena");
-  void *result = DoAllocWithArena(request, arena);
-  return result;
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/third_party/abseil_cpp/absl/base/internal/low_level_alloc.h b/third_party/abseil_cpp/absl/base/internal/low_level_alloc.h
deleted file mode 100644
index db91951c82..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/low_level_alloc.h
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_
-#define ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_
-
-// A simple thread-safe memory allocator that does not depend on
-// mutexes or thread-specific data.  It is intended to be used
-// sparingly, and only when malloc() would introduce an unwanted
-// dependency, such as inside the heap-checker, or the Mutex
-// implementation.
-
-// IWYU pragma: private, include "base/low_level_alloc.h"
-
-#include <sys/types.h>
-
-#include <cstdint>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-
-// LowLevelAlloc requires that the platform support low-level
-// allocation of virtual memory. Platforms lacking this cannot use
-// LowLevelAlloc.
-#ifdef ABSL_LOW_LEVEL_ALLOC_MISSING
-#error ABSL_LOW_LEVEL_ALLOC_MISSING cannot be directly set
-#elif !defined(ABSL_HAVE_MMAP) && !defined(_WIN32)
-#define ABSL_LOW_LEVEL_ALLOC_MISSING 1
-#endif
-
-// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows or
-// asm.js / WebAssembly.
-// See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
-// for more information.
-#ifdef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-#error ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING cannot be directly set
-#elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__)
-#define ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING 1
-#endif
-
-#include <cstddef>
-
-#include "absl/base/port.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-class LowLevelAlloc {
- public:
-  struct Arena;       // an arena from which memory may be allocated
-
-  // Returns a pointer to a block of at least "request" bytes
-  // that have been newly allocated from the specific arena.
-  // for Alloc() call the DefaultArena() is used.
-  // Returns 0 if passed request==0.
-  // Does not return 0 under other circumstances; it crashes if memory
-  // is not available.
-  static void *Alloc(size_t request) ABSL_ATTRIBUTE_SECTION(malloc_hook);
-  static void *AllocWithArena(size_t request, Arena *arena)
-      ABSL_ATTRIBUTE_SECTION(malloc_hook);
-
-  // Deallocates a region of memory that was previously allocated with
-  // Alloc().   Does nothing if passed 0.   "s" must be either 0,
-  // or must have been returned from a call to Alloc() and not yet passed to
-  // Free() since that call to Alloc().  The space is returned to the arena
-  // from which it was allocated.
-  static void Free(void *s) ABSL_ATTRIBUTE_SECTION(malloc_hook);
-
-  // ABSL_ATTRIBUTE_SECTION(malloc_hook) for Alloc* and Free
-  // are to put all callers of MallocHook::Invoke* in this module
-  // into special section,
-  // so that MallocHook::GetCallerStackTrace can function accurately.
-
-  // Create a new arena.
-  // The root metadata for the new arena is allocated in the
-  // meta_data_arena; the DefaultArena() can be passed for meta_data_arena.
-  // These values may be ored into flags:
-  enum {
-    // Report calls to Alloc() and Free() via the MallocHook interface.
-    // Set in the DefaultArena.
-    kCallMallocHook = 0x0001,
-
-#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-    // Make calls to Alloc(), Free() be async-signal-safe. Not set in
-    // DefaultArena(). Not supported on all platforms.
-    kAsyncSignalSafe = 0x0002,
-#endif
-  };
-  // Construct a new arena.  The allocation of the underlying metadata honors
-  // the provided flags.  For example, the call NewArena(kAsyncSignalSafe)
-  // is itself async-signal-safe, as well as generatating an arena that provides
-  // async-signal-safe Alloc/Free.
-  static Arena *NewArena(int32_t flags);
-
-  // Destroys an arena allocated by NewArena and returns true,
-  // provided no allocated blocks remain in the arena.
-  // If allocated blocks remain in the arena, does nothing and
-  // returns false.
-  // It is illegal to attempt to destroy the DefaultArena().
-  static bool DeleteArena(Arena *arena);
-
-  // The default arena that always exists.
-  static Arena *DefaultArena();
-
- private:
-  LowLevelAlloc();      // no instances
-};
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/low_level_alloc_test.cc b/third_party/abseil_cpp/absl/base/internal/low_level_alloc_test.cc
deleted file mode 100644
index 2f2eaffa03..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/low_level_alloc_test.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/low_level_alloc.h"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <thread>  // NOLINT(build/c++11)
-#include <unordered_map>
-#include <utility>
-
-#include "absl/container/node_hash_map.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-namespace {
-
-// This test doesn't use gtest since it needs to test that everything
-// works before main().
-#define TEST_ASSERT(x)                                           \
-  if (!(x)) {                                                    \
-    printf("TEST_ASSERT(%s) FAILED ON LINE %d\n", #x, __LINE__); \
-    abort();                                                     \
-  }
-
-// a block of memory obtained from the allocator
-struct BlockDesc {
-  char *ptr;      // pointer to memory
-  int len;        // number of bytes
-  int fill;       // filled with data starting with this
-};
-
-// Check that the pattern placed in the block d
-// by RandomizeBlockDesc is still there.
-static void CheckBlockDesc(const BlockDesc &d) {
-  for (int i = 0; i != d.len; i++) {
-    TEST_ASSERT((d.ptr[i] & 0xff) == ((d.fill + i) & 0xff));
-  }
-}
-
-// Fill the block "*d" with a pattern
-// starting with a random byte.
-static void RandomizeBlockDesc(BlockDesc *d) {
-  d->fill = rand() & 0xff;
-  for (int i = 0; i != d->len; i++) {
-    d->ptr[i] = (d->fill + i) & 0xff;
-  }
-}
-
-// Use to indicate to the malloc hooks that
-// this calls is from LowLevelAlloc.
-static bool using_low_level_alloc = false;
-
-// n times, toss a coin, and based on the outcome
-// either allocate a new block or deallocate an old block.
-// New blocks are placed in a std::unordered_map with a random key
-// and initialized with RandomizeBlockDesc().
-// If keys conflict, the older block is freed.
-// Old blocks are always checked with CheckBlockDesc()
-// before being freed.  At the end of the run,
-// all remaining allocated blocks are freed.
-// If use_new_arena is true, use a fresh arena, and then delete it.
-// If call_malloc_hook is true and user_arena is true,
-// allocations and deallocations are reported via the MallocHook
-// interface.
-static void Test(bool use_new_arena, bool call_malloc_hook, int n) {
-  typedef absl::node_hash_map<int, BlockDesc> AllocMap;
-  AllocMap allocated;
-  AllocMap::iterator it;
-  BlockDesc block_desc;
-  int rnd;
-  LowLevelAlloc::Arena *arena = 0;
-  if (use_new_arena) {
-    int32_t flags = call_malloc_hook ? LowLevelAlloc::kCallMallocHook : 0;
-    arena = LowLevelAlloc::NewArena(flags);
-  }
-  for (int i = 0; i != n; i++) {
-    if (i != 0 && i % 10000 == 0) {
-      printf(".");
-      fflush(stdout);
-    }
-
-    switch (rand() & 1) {      // toss a coin
-    case 0:     // coin came up heads: add a block
-      using_low_level_alloc = true;
-      block_desc.len = rand() & 0x3fff;
-      block_desc.ptr =
-        reinterpret_cast<char *>(
-                        arena == 0
-                        ? LowLevelAlloc::Alloc(block_desc.len)
-                        : LowLevelAlloc::AllocWithArena(block_desc.len, arena));
-      using_low_level_alloc = false;
-      RandomizeBlockDesc(&block_desc);
-      rnd = rand();
-      it = allocated.find(rnd);
-      if (it != allocated.end()) {
-        CheckBlockDesc(it->second);
-        using_low_level_alloc = true;
-        LowLevelAlloc::Free(it->second.ptr);
-        using_low_level_alloc = false;
-        it->second = block_desc;
-      } else {
-        allocated[rnd] = block_desc;
-      }
-      break;
-    case 1:     // coin came up tails: remove a block
-      it = allocated.begin();
-      if (it != allocated.end()) {
-        CheckBlockDesc(it->second);
-        using_low_level_alloc = true;
-        LowLevelAlloc::Free(it->second.ptr);
-        using_low_level_alloc = false;
-        allocated.erase(it);
-      }
-      break;
-    }
-  }
-  // remove all remaining blocks
-  while ((it = allocated.begin()) != allocated.end()) {
-    CheckBlockDesc(it->second);
-    using_low_level_alloc = true;
-    LowLevelAlloc::Free(it->second.ptr);
-    using_low_level_alloc = false;
-    allocated.erase(it);
-  }
-  if (use_new_arena) {
-    TEST_ASSERT(LowLevelAlloc::DeleteArena(arena));
-  }
-}
-
-// LowLevelAlloc is designed to be safe to call before main().
-static struct BeforeMain {
-  BeforeMain() {
-    Test(false, false, 50000);
-    Test(true, false, 50000);
-    Test(true, true, 50000);
-  }
-} before_main;
-
-}  // namespace
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-int main(int argc, char *argv[]) {
-  // The actual test runs in the global constructor of `before_main`.
-  printf("PASS\n");
-  return 0;
-}
diff --git a/third_party/abseil_cpp/absl/base/internal/low_level_scheduling.h b/third_party/abseil_cpp/absl/base/internal/low_level_scheduling.h
deleted file mode 100644
index 9baccc0659..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/low_level_scheduling.h
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Core interfaces and definitions used by by low-level interfaces such as
-// SpinLock.
-
-#ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_
-#define ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/scheduling_mode.h"
-#include "absl/base/macros.h"
-
-// The following two declarations exist so SchedulingGuard may friend them with
-// the appropriate language linkage.  These callbacks allow libc internals, such
-// as function level statics, to schedule cooperatively when locking.
-extern "C" bool __google_disable_rescheduling(void);
-extern "C" void __google_enable_rescheduling(bool disable_result);
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-class CondVar;
-class Mutex;
-
-namespace synchronization_internal {
-int MutexDelay(int32_t c, int mode);
-}  // namespace synchronization_internal
-
-namespace base_internal {
-
-class SchedulingHelper;  // To allow use of SchedulingGuard.
-class SpinLock;          // To allow use of SchedulingGuard.
-
-// SchedulingGuard
-// Provides guard semantics that may be used to disable cooperative rescheduling
-// of the calling thread within specific program blocks.  This is used to
-// protect resources (e.g. low-level SpinLocks or Domain code) that cooperative
-// scheduling depends on.
-//
-// Domain implementations capable of rescheduling in reaction to involuntary
-// kernel thread actions (e.g blocking due to a pagefault or syscall) must
-// guarantee that an annotated thread is not allowed to (cooperatively)
-// reschedule until the annotated region is complete.
-//
-// It is an error to attempt to use a cooperatively scheduled resource (e.g.
-// Mutex) within a rescheduling-disabled region.
-//
-// All methods are async-signal safe.
-class SchedulingGuard {
- public:
-  // Returns true iff the calling thread may be cooperatively rescheduled.
-  static bool ReschedulingIsAllowed();
-  SchedulingGuard(const SchedulingGuard&) = delete;
-  SchedulingGuard& operator=(const SchedulingGuard&) = delete;
-
- private:
-  // Disable cooperative rescheduling of the calling thread.  It may still
-  // initiate scheduling operations (e.g. wake-ups), however, it may not itself
-  // reschedule.  Nestable.  The returned result is opaque, clients should not
-  // attempt to interpret it.
-  // REQUIRES: Result must be passed to a pairing EnableScheduling().
-  static bool DisableRescheduling();
-
-  // Marks the end of a rescheduling disabled region, previously started by
-  // DisableRescheduling().
-  // REQUIRES: Pairs with innermost call (and result) of DisableRescheduling().
-  static void EnableRescheduling(bool disable_result);
-
-  // A scoped helper for {Disable, Enable}Rescheduling().
-  // REQUIRES: destructor must run in same thread as constructor.
-  struct ScopedDisable {
-    ScopedDisable() { disabled = SchedulingGuard::DisableRescheduling(); }
-    ~ScopedDisable() { SchedulingGuard::EnableRescheduling(disabled); }
-
-    bool disabled;
-  };
-
-  // A scoped helper to enable rescheduling temporarily.
-  // REQUIRES: destructor must run in same thread as constructor.
-  class ScopedEnable {
-   public:
-    ScopedEnable();
-    ~ScopedEnable();
-
-   private:
-    int scheduling_disabled_depth_;
-  };
-
-  // Access to SchedulingGuard is explicitly permitted.
-  friend class absl::CondVar;
-  friend class absl::Mutex;
-  friend class SchedulingHelper;
-  friend class SpinLock;
-  friend int absl::synchronization_internal::MutexDelay(int32_t c, int mode);
-};
-
-//------------------------------------------------------------------------------
-// End of public interfaces.
-//------------------------------------------------------------------------------
-
-inline bool SchedulingGuard::ReschedulingIsAllowed() {
-  return false;
-}
-
-inline bool SchedulingGuard::DisableRescheduling() {
-  return false;
-}
-
-inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) {
-  return;
-}
-
-inline SchedulingGuard::ScopedEnable::ScopedEnable()
-    : scheduling_disabled_depth_(0) {}
-inline SchedulingGuard::ScopedEnable::~ScopedEnable() {
-  ABSL_RAW_CHECK(scheduling_disabled_depth_ == 0, "disable unused warning");
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/per_thread_tls.h b/third_party/abseil_cpp/absl/base/internal/per_thread_tls.h
deleted file mode 100644
index cf5e97a047..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/per_thread_tls.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_
-#define ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_
-
-// This header defines two macros:
-//
-// If the platform supports thread-local storage:
-//
-// * ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a
-//   thread-local variable
-// * ABSL_PER_THREAD_TLS is 1
-//
-// Otherwise:
-//
-// * ABSL_PER_THREAD_TLS_KEYWORD is empty
-// * ABSL_PER_THREAD_TLS is 0
-//
-// Microsoft C supports thread-local storage.
-// GCC supports it if the appropriate version of glibc is available,
-// which the programmer can indicate by defining ABSL_HAVE_TLS
-
-#include "absl/base/port.h"  // For ABSL_HAVE_TLS
-
-#if defined(ABSL_PER_THREAD_TLS)
-#error ABSL_PER_THREAD_TLS cannot be directly set
-#elif defined(ABSL_PER_THREAD_TLS_KEYWORD)
-#error ABSL_PER_THREAD_TLS_KEYWORD cannot be directly set
-#elif defined(ABSL_HAVE_TLS)
-#define ABSL_PER_THREAD_TLS_KEYWORD __thread
-#define ABSL_PER_THREAD_TLS 1
-#elif defined(_MSC_VER)
-#define ABSL_PER_THREAD_TLS_KEYWORD __declspec(thread)
-#define ABSL_PER_THREAD_TLS 1
-#else
-#define ABSL_PER_THREAD_TLS_KEYWORD
-#define ABSL_PER_THREAD_TLS 0
-#endif
-
-#endif  // ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/periodic_sampler.cc b/third_party/abseil_cpp/absl/base/internal/periodic_sampler.cc
deleted file mode 100644
index 520dabbaa0..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/periodic_sampler.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/periodic_sampler.h"
-
-#include <atomic>
-
-#include "absl/base/internal/exponential_biased.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept {
-  return rng_.GetStride(period);
-}
-
-bool PeriodicSamplerBase::SubtleConfirmSample() noexcept {
-  int current_period = period();
-
-  // Deal with period case 0 (always off) and 1 (always on)
-  if (ABSL_PREDICT_FALSE(current_period < 2)) {
-    stride_ = 0;
-    return current_period == 1;
-  }
-
-  // Check if this is the first call to Sample()
-  if (ABSL_PREDICT_FALSE(stride_ == 1)) {
-    stride_ = static_cast<uint64_t>(-GetExponentialBiased(current_period));
-    if (static_cast<int64_t>(stride_) < -1) {
-      ++stride_;
-      return false;
-    }
-  }
-
-  stride_ = static_cast<uint64_t>(-GetExponentialBiased(current_period));
-  return true;
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/periodic_sampler.h b/third_party/abseil_cpp/absl/base/internal/periodic_sampler.h
deleted file mode 100644
index f8a86796b1..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/periodic_sampler.h
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
-#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
-
-#include <stdint.h>
-
-#include <atomic>
-
-#include "absl/base/internal/exponential_biased.h"
-#include "absl/base/optimization.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// PeriodicSamplerBase provides the basic period sampler implementation.
-//
-// This is the base class for the templated PeriodicSampler class, which holds
-// a global std::atomic value identified by a user defined tag, such that
-// each specific PeriodSampler implementation holds its own global period.
-//
-// PeriodicSamplerBase is thread-compatible except where stated otherwise.
-class PeriodicSamplerBase {
- public:
-  // PeriodicSamplerBase is trivial / copyable / movable / destructible.
-  PeriodicSamplerBase() = default;
-  PeriodicSamplerBase(PeriodicSamplerBase&&) = default;
-  PeriodicSamplerBase(const PeriodicSamplerBase&) = default;
-
-  // Returns true roughly once every `period` calls. This is established by a
-  // randomly picked `stride` that is counted down on each call to `Sample`.
-  // This stride is picked such that the probability of `Sample()` returning
-  // true is 1 in `period`.
-  inline bool Sample() noexcept;
-
-  // The below methods are intended for optimized use cases where the
-  // size of the inlined fast path code is highly important. Applications
-  // should use the `Sample()` method unless they have proof that their
-  // specific use case requires the optimizations offered by these methods.
-  //
-  // An example of such a use case is SwissTable sampling. All sampling checks
-  // are in inlined SwissTable methods, and the number of call sites is huge.
-  // In this case, the inlined code size added to each translation unit calling
-  // SwissTable methods is non-trivial.
-  //
-  // The `SubtleMaybeSample()` function spuriously returns true even if the
-  // function should not be sampled, applications MUST match each call to
-  // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call,
-  // and use the result of the latter as the sampling decision.
-  // In other words: the code should logically be equivalent to:
-  //
-  //    if (SubtleMaybeSample() && SubtleConfirmSample()) {
-  //      // Sample this call
-  //    }
-  //
-  // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can
-  // be placed out of line, for example, the typical use case looks as follows:
-  //
-  //   // --- frobber.h -----------
-  //   void FrobberSampled();
-  //
-  //   inline void FrobberImpl() {
-  //     // ...
-  //   }
-  //
-  //   inline void Frobber() {
-  //     if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) {
-  //       FrobberSampled();
-  //     } else {
-  //       FrobberImpl();
-  //     }
-  //   }
-  //
-  //   // --- frobber.cc -----------
-  //   void FrobberSampled() {
-  //     if (!sampler.SubtleConfirmSample())) {
-  //       // Spurious false positive
-  //       FrobberImpl();
-  //       return;
-  //     }
-  //
-  //     // Sampled execution
-  //     // ...
-  //   }
-  inline bool SubtleMaybeSample() noexcept;
-  bool SubtleConfirmSample() noexcept;
-
- protected:
-  // We explicitly don't use a virtual destructor as this class is never
-  // virtually destroyed, and it keeps the class trivial, which avoids TLS
-  // prologue and epilogue code for our TLS instances.
-  ~PeriodicSamplerBase() = default;
-
-  // Returns the next stride for our sampler.
-  // This function is virtual for testing purposes only.
-  virtual int64_t GetExponentialBiased(int period) noexcept;
-
- private:
-  // Returns the current period of this sampler. Thread-safe.
-  virtual int period() const noexcept = 0;
-
-  // Keep and decrement stride_ as an unsigned integer, but compare the value
-  // to zero casted as a signed int. clang and msvc do not create optimum code
-  // if we use signed for the combined decrement and sign comparison.
-  //
-  // Below 3 alternative options, all compiles generate the best code
-  // using the unsigned increment <---> signed int comparison option.
-  //
-  // Option 1:
-  //   int64_t stride_;
-  //   if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... }
-  //
-  //   GCC   x64 (OK) : https://gcc.godbolt.org/z/R5MzzA
-  //   GCC   ppc (OK) : https://gcc.godbolt.org/z/z7NZAt
-  //   Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd
-  //   ICC   x64 (OK) : https://gcc.godbolt.org/z/rE6s8W
-  //   MSVC  x64 (OK) : https://gcc.godbolt.org/z/ARMXqS
-  //
-  // Option 2:
-  //   int64_t stride_ = 0;
-  //   if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... }
-  //
-  //   GCC   x64 (OK) : https://gcc.godbolt.org/z/jSQxYK
-  //   GCC   ppc (OK) : https://gcc.godbolt.org/z/VJdYaA
-  //   Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX
-  //   ICC   x64 (OK) : https://gcc.godbolt.org/z/4snaFd
-  //   MSVC  x64 (BAD): https://gcc.godbolt.org/z/BgnEKE
-  //
-  // Option 3:
-  //   uint64_t stride_;
-  //   if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) { ... }
-  //
-  //   GCC   x64 (OK) : https://gcc.godbolt.org/z/bFbfPy
-  //   GCC   ppc (OK) : https://gcc.godbolt.org/z/S9KkUE
-  //   Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4
-  //   ICC   x64 (OK) : https://gcc.godbolt.org/z/ptTNfD
-  //   MSVC  x64 (OK) : https://gcc.godbolt.org/z/76j4-5
-  uint64_t stride_ = 0;
-  ExponentialBiased rng_;
-};
-
-inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept {
-  // See comments on `stride_` for the unsigned increment / signed compare.
-  if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) {
-    return false;
-  }
-  return true;
-}
-
-inline bool PeriodicSamplerBase::Sample() noexcept {
-  return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample()
-                                                 : false;
-}
-
-// PeriodicSampler is a concreted periodic sampler implementation.
-// The user provided Tag identifies the implementation, and is required to
-// isolate the global state of this instance from other instances.
-//
-// Typical use case:
-//
-//   struct HashTablezTag {};
-//   thread_local PeriodicSampler sampler;
-//
-//   void HashTableSamplingLogic(...) {
-//     if (sampler.Sample()) {
-//       HashTableSlowSamplePath(...);
-//     }
-//   }
-//
-template <typename Tag, int default_period = 0>
-class PeriodicSampler final : public PeriodicSamplerBase {
- public:
-  ~PeriodicSampler() = default;
-
-  int period() const noexcept final {
-    return period_.load(std::memory_order_relaxed);
-  }
-
-  // Sets the global period for this sampler. Thread-safe.
-  // Setting a period of 0 disables the sampler, i.e., every call to Sample()
-  // will return false. Setting a period of 1 puts the sampler in 'always on'
-  // mode, i.e., every call to Sample() returns true.
-  static void SetGlobalPeriod(int period) {
-    period_.store(period, std::memory_order_relaxed);
-  }
-
- private:
-  static std::atomic<int> period_;
-};
-
-template <typename Tag, int default_period>
-std::atomic<int> PeriodicSampler<Tag, default_period>::period_(default_period);
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/periodic_sampler_benchmark.cc b/third_party/abseil_cpp/absl/base/internal/periodic_sampler_benchmark.cc
deleted file mode 100644
index 5ad469ce79..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/periodic_sampler_benchmark.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "benchmark/benchmark.h"
-#include "absl/base/internal/periodic_sampler.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-namespace {
-
-template <typename Sampler>
-void BM_Sample(Sampler* sampler, benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(sampler);
-    benchmark::DoNotOptimize(sampler->Sample());
-  }
-}
-
-template <typename Sampler>
-void BM_SampleMinunumInlined(Sampler* sampler, benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(sampler);
-    if (ABSL_PREDICT_FALSE(sampler->SubtleMaybeSample())) {
-      benchmark::DoNotOptimize(sampler->SubtleConfirmSample());
-    }
-  }
-}
-
-void BM_PeriodicSampler_TinySample(benchmark::State& state) {
-  struct Tag {};
-  PeriodicSampler<Tag, 10> sampler;
-  BM_Sample(&sampler, state);
-}
-BENCHMARK(BM_PeriodicSampler_TinySample);
-
-void BM_PeriodicSampler_ShortSample(benchmark::State& state) {
-  struct Tag {};
-  PeriodicSampler<Tag, 1024> sampler;
-  BM_Sample(&sampler, state);
-}
-BENCHMARK(BM_PeriodicSampler_ShortSample);
-
-void BM_PeriodicSampler_LongSample(benchmark::State& state) {
-  struct Tag {};
-  PeriodicSampler<Tag, 1024 * 1024> sampler;
-  BM_Sample(&sampler, state);
-}
-BENCHMARK(BM_PeriodicSampler_LongSample);
-
-void BM_PeriodicSampler_LongSampleMinunumInlined(benchmark::State& state) {
-  struct Tag {};
-  PeriodicSampler<Tag, 1024 * 1024> sampler;
-  BM_SampleMinunumInlined(&sampler, state);
-}
-BENCHMARK(BM_PeriodicSampler_LongSampleMinunumInlined);
-
-void BM_PeriodicSampler_Disabled(benchmark::State& state) {
-  struct Tag {};
-  PeriodicSampler<Tag, 0> sampler;
-  BM_Sample(&sampler, state);
-}
-BENCHMARK(BM_PeriodicSampler_Disabled);
-
-}  // namespace
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/periodic_sampler_test.cc b/third_party/abseil_cpp/absl/base/internal/periodic_sampler_test.cc
deleted file mode 100644
index 3b301e37ab..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/periodic_sampler_test.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/periodic_sampler.h"
-
-#include <thread>  // NOLINT(build/c++11)
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/macros.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-namespace {
-
-using testing::Eq;
-using testing::Return;
-using testing::StrictMock;
-
-class MockPeriodicSampler : public PeriodicSamplerBase {
- public:
-  virtual ~MockPeriodicSampler() = default;
-
-  MOCK_METHOD(int, period, (), (const, noexcept));
-  MOCK_METHOD(int64_t, GetExponentialBiased, (int), (noexcept));
-};
-
-TEST(PeriodicSamplerBaseTest, Sample) {
-  StrictMock<MockPeriodicSampler> sampler;
-
-  EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(16));
-  EXPECT_CALL(sampler, GetExponentialBiased(16))
-      .WillOnce(Return(2))
-      .WillOnce(Return(3))
-      .WillOnce(Return(4));
-
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_TRUE(sampler.Sample());
-
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_TRUE(sampler.Sample());
-
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-}
-
-TEST(PeriodicSamplerBaseTest, ImmediatelySample) {
-  StrictMock<MockPeriodicSampler> sampler;
-
-  EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16));
-  EXPECT_CALL(sampler, GetExponentialBiased(16))
-      .WillOnce(Return(1))
-      .WillOnce(Return(2))
-      .WillOnce(Return(3));
-
-  EXPECT_TRUE(sampler.Sample());
-
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_TRUE(sampler.Sample());
-
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-}
-
-TEST(PeriodicSamplerBaseTest, Disabled) {
-  StrictMock<MockPeriodicSampler> sampler;
-
-  EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(0));
-
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-}
-
-TEST(PeriodicSamplerBaseTest, AlwaysOn) {
-  StrictMock<MockPeriodicSampler> sampler;
-
-  EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(1));
-
-  EXPECT_TRUE(sampler.Sample());
-  EXPECT_TRUE(sampler.Sample());
-  EXPECT_TRUE(sampler.Sample());
-}
-
-TEST(PeriodicSamplerBaseTest, Disable) {
-  StrictMock<MockPeriodicSampler> sampler;
-
-  EXPECT_CALL(sampler, period()).WillOnce(Return(16));
-  EXPECT_CALL(sampler, GetExponentialBiased(16)).WillOnce(Return(3));
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-
-  EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(0));
-
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-}
-
-TEST(PeriodicSamplerBaseTest, Enable) {
-  StrictMock<MockPeriodicSampler> sampler;
-
-  EXPECT_CALL(sampler, period()).WillOnce(Return(0));
-  EXPECT_FALSE(sampler.Sample());
-
-  EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16));
-  EXPECT_CALL(sampler, GetExponentialBiased(16))
-      .Times(2)
-      .WillRepeatedly(Return(3));
-
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_TRUE(sampler.Sample());
-
-  EXPECT_FALSE(sampler.Sample());
-  EXPECT_FALSE(sampler.Sample());
-}
-
-TEST(PeriodicSamplerTest, ConstructConstInit) {
-  struct Tag {};
-  ABSL_CONST_INIT static PeriodicSampler<Tag> sampler;
-  (void)sampler;
-}
-
-TEST(PeriodicSamplerTest, DefaultPeriod0) {
-  struct Tag {};
-  PeriodicSampler<Tag> sampler;
-  EXPECT_THAT(sampler.period(), Eq(0));
-}
-
-TEST(PeriodicSamplerTest, DefaultPeriod) {
-  struct Tag {};
-  PeriodicSampler<Tag, 100> sampler;
-  EXPECT_THAT(sampler.period(), Eq(100));
-}
-
-TEST(PeriodicSamplerTest, SetGlobalPeriod) {
-  struct Tag1 {};
-  struct Tag2 {};
-  PeriodicSampler<Tag1, 25> sampler1;
-  PeriodicSampler<Tag2, 50> sampler2;
-
-  EXPECT_THAT(sampler1.period(), Eq(25));
-  EXPECT_THAT(sampler2.period(), Eq(50));
-
-  std::thread thread([] {
-    PeriodicSampler<Tag1, 25> sampler1;
-    PeriodicSampler<Tag2, 50> sampler2;
-    EXPECT_THAT(sampler1.period(), Eq(25));
-    EXPECT_THAT(sampler2.period(), Eq(50));
-    sampler1.SetGlobalPeriod(10);
-    sampler2.SetGlobalPeriod(20);
-  });
-  thread.join();
-
-  EXPECT_THAT(sampler1.period(), Eq(10));
-  EXPECT_THAT(sampler2.period(), Eq(20));
-}
-
-}  // namespace
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/pretty_function.h b/third_party/abseil_cpp/absl/base/internal/pretty_function.h
deleted file mode 100644
index 35d51676dc..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/pretty_function.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_
-#define ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_
-
-// ABSL_PRETTY_FUNCTION
-//
-// In C++11, __func__ gives the undecorated name of the current function.  That
-// is, "main", not "int main()".  Various compilers give extra macros to get the
-// decorated function name, including return type and arguments, to
-// differentiate between overload sets.  ABSL_PRETTY_FUNCTION is a portable
-// version of these macros which forwards to the correct macro on each compiler.
-#if defined(_MSC_VER)
-#define ABSL_PRETTY_FUNCTION __FUNCSIG__
-#elif defined(__GNUC__)
-#define ABSL_PRETTY_FUNCTION __PRETTY_FUNCTION__
-#else
-#error "Unsupported compiler"
-#endif
-
-#endif  // ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/raw_logging.cc b/third_party/abseil_cpp/absl/base/internal/raw_logging.cc
deleted file mode 100644
index ae8754c6a1..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/raw_logging.cc
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/raw_logging.h"
-
-#include <stddef.h>
-#include <cstdarg>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/atomic_hook.h"
-#include "absl/base/log_severity.h"
-
-// We know how to perform low-level writes to stderr in POSIX and Windows.  For
-// these platforms, we define the token ABSL_LOW_LEVEL_WRITE_SUPPORTED.
-// Much of raw_logging.cc becomes a no-op when we can't output messages,
-// although a FATAL ABSL_RAW_LOG message will still abort the process.
-
-// ABSL_HAVE_POSIX_WRITE is defined when the platform provides posix write()
-// (as from unistd.h)
-//
-// This preprocessor token is also defined in raw_io.cc.  If you need to copy
-// this, consider moving both to config.h instead.
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
-    defined(__Fuchsia__) || defined(__native_client__) || \
-    defined(__EMSCRIPTEN__) || defined(__ASYLO__)
-
-#include <unistd.h>
-
-#define ABSL_HAVE_POSIX_WRITE 1
-#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
-#else
-#undef ABSL_HAVE_POSIX_WRITE
-#endif
-
-// ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall
-//   syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len);
-// for low level operations that want to avoid libc.
-#if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__)
-#include <sys/syscall.h>
-#define ABSL_HAVE_SYSCALL_WRITE 1
-#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
-#else
-#undef ABSL_HAVE_SYSCALL_WRITE
-#endif
-
-#ifdef _WIN32
-#include <io.h>
-
-#define ABSL_HAVE_RAW_IO 1
-#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
-#else
-#undef ABSL_HAVE_RAW_IO
-#endif
-
-// TODO(gfalcon): We want raw-logging to work on as many platforms as possible.
-// Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a
-// selected set of platforms for which we expect not to be able to raw log.
-
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
-    absl::raw_logging_internal::LogPrefixHook>
-    log_prefix_hook;
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
-    absl::raw_logging_internal::AbortHook>
-    abort_hook;
-
-#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
-static const char kTruncated[] = " ... (message truncated)\n";
-
-// sprintf the format to the buffer, adjusting *buf and *size to reflect the
-// consumed bytes, and return whether the message fit without truncation.  If
-// truncation occurred, if possible leave room in the buffer for the message
-// kTruncated[].
-inline static bool VADoRawLog(char** buf, int* size, const char* format,
-                              va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0);
-inline static bool VADoRawLog(char** buf, int* size,
-                              const char* format, va_list ap) {
-  int n = vsnprintf(*buf, *size, format, ap);
-  bool result = true;
-  if (n < 0 || n > *size) {
-    result = false;
-    if (static_cast<size_t>(*size) > sizeof(kTruncated)) {
-      n = *size - sizeof(kTruncated);  // room for truncation message
-    } else {
-      n = 0;                           // no room for truncation message
-    }
-  }
-  *size -= n;
-  *buf += n;
-  return result;
-}
-#endif  // ABSL_LOW_LEVEL_WRITE_SUPPORTED
-
-static constexpr int kLogBufSize = 3000;
-
-namespace {
-
-// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths
-// that invoke malloc() and getenv() that might acquire some locks.
-
-// Helper for RawLog below.
-// *DoRawLog writes to *buf of *size and move them past the written portion.
-// It returns true iff there was no overflow or error.
-bool DoRawLog(char** buf, int* size, const char* format, ...)
-    ABSL_PRINTF_ATTRIBUTE(3, 4);
-bool DoRawLog(char** buf, int* size, const char* format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  int n = vsnprintf(*buf, *size, format, ap);
-  va_end(ap);
-  if (n < 0 || n > *size) return false;
-  *size -= n;
-  *buf += n;
-  return true;
-}
-
-void RawLogVA(absl::LogSeverity severity, const char* file, int line,
-              const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0);
-void RawLogVA(absl::LogSeverity severity, const char* file, int line,
-              const char* format, va_list ap) {
-  char buffer[kLogBufSize];
-  char* buf = buffer;
-  int size = sizeof(buffer);
-#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
-  bool enabled = true;
-#else
-  bool enabled = false;
-#endif
-
-#ifdef ABSL_MIN_LOG_LEVEL
-  if (severity < static_cast<absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) &&
-      severity < absl::LogSeverity::kFatal) {
-    enabled = false;
-  }
-#endif
-
-  auto log_prefix_hook_ptr = log_prefix_hook.Load();
-  if (log_prefix_hook_ptr) {
-    enabled = log_prefix_hook_ptr(severity, file, line, &buf, &size);
-  } else {
-    if (enabled) {
-      DoRawLog(&buf, &size, "[%s : %d] RAW: ", file, line);
-    }
-  }
-  const char* const prefix_end = buf;
-
-#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
-  if (enabled) {
-    bool no_chop = VADoRawLog(&buf, &size, format, ap);
-    if (no_chop) {
-      DoRawLog(&buf, &size, "\n");
-    } else {
-      DoRawLog(&buf, &size, "%s", kTruncated);
-    }
-    absl::raw_logging_internal::SafeWriteToStderr(buffer, strlen(buffer));
-  }
-#else
-  static_cast<void>(format);
-  static_cast<void>(ap);
-#endif
-
-  // Abort the process after logging a FATAL message, even if the output itself
-  // was suppressed.
-  if (severity == absl::LogSeverity::kFatal) {
-    abort_hook(file, line, buffer, prefix_end, buffer + kLogBufSize);
-    abort();
-  }
-}
-
-}  // namespace
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace raw_logging_internal {
-void SafeWriteToStderr(const char *s, size_t len) {
-#if defined(ABSL_HAVE_SYSCALL_WRITE)
-  syscall(SYS_write, STDERR_FILENO, s, len);
-#elif defined(ABSL_HAVE_POSIX_WRITE)
-  write(STDERR_FILENO, s, len);
-#elif defined(ABSL_HAVE_RAW_IO)
-  _write(/* stderr */ 2, s, len);
-#else
-  // stderr logging unsupported on this platform
-  (void) s;
-  (void) len;
-#endif
-}
-
-void RawLog(absl::LogSeverity severity, const char* file, int line,
-            const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
-void RawLog(absl::LogSeverity severity, const char* file, int line,
-            const char* format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  RawLogVA(severity, file, line, format, ap);
-  va_end(ap);
-}
-
-// Non-formatting version of RawLog().
-//
-// TODO(gfalcon): When string_view no longer depends on base, change this
-// interface to take its message as a string_view instead.
-static void DefaultInternalLog(absl::LogSeverity severity, const char* file,
-                               int line, const std::string& message) {
-  RawLog(severity, file, line, "%s", message.c_str());
-}
-
-bool RawLoggingFullySupported() {
-#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
-  return true;
-#else  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
-  return false;
-#endif  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
-}
-
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL
-    absl::base_internal::AtomicHook<InternalLogFunction>
-        internal_log_function(DefaultInternalLog);
-
-void RegisterInternalLogFunction(InternalLogFunction func) {
-  internal_log_function.Store(func);
-}
-
-}  // namespace raw_logging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/raw_logging.h b/third_party/abseil_cpp/absl/base/internal/raw_logging.h
deleted file mode 100644
index 20f4291b16..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/raw_logging.h
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Thread-safe logging routines that do not allocate any memory or
-// acquire any locks, and can therefore be used by low-level memory
-// allocation, synchronization, and signal-handling code.
-
-#ifndef ABSL_BASE_INTERNAL_RAW_LOGGING_H_
-#define ABSL_BASE_INTERNAL_RAW_LOGGING_H_
-
-#include <string>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/atomic_hook.h"
-#include "absl/base/log_severity.h"
-#include "absl/base/macros.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"
-
-// This is similar to LOG(severity) << format..., but
-// * it is to be used ONLY by low-level modules that can't use normal LOG()
-// * it is designed to be a low-level logger that does not allocate any
-//   memory and does not need any locks, hence:
-// * it logs straight and ONLY to STDERR w/o buffering
-// * it uses an explicit printf-format and arguments list
-// * it will silently chop off really long message strings
-// Usage example:
-//   ABSL_RAW_LOG(ERROR, "Failed foo with %i: %s", status, error);
-// This will print an almost standard log line like this to stderr only:
-//   E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file
-
-#define ABSL_RAW_LOG(severity, ...)                                            \
-  do {                                                                         \
-    constexpr const char* absl_raw_logging_internal_basename =                 \
-        ::absl::raw_logging_internal::Basename(__FILE__,                       \
-                                               sizeof(__FILE__) - 1);          \
-    ::absl::raw_logging_internal::RawLog(ABSL_RAW_LOGGING_INTERNAL_##severity, \
-                                         absl_raw_logging_internal_basename,   \
-                                         __LINE__, __VA_ARGS__);               \
-  } while (0)
-
-// Similar to CHECK(condition) << message, but for low-level modules:
-// we use only ABSL_RAW_LOG that does not allocate memory.
-// We do not want to provide args list here to encourage this usage:
-//   if (!cond)  ABSL_RAW_LOG(FATAL, "foo ...", hard_to_compute_args);
-// so that the args are not computed when not needed.
-#define ABSL_RAW_CHECK(condition, message)                             \
-  do {                                                                 \
-    if (ABSL_PREDICT_FALSE(!(condition))) {                            \
-      ABSL_RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \
-    }                                                                  \
-  } while (0)
-
-// ABSL_INTERNAL_LOG and ABSL_INTERNAL_CHECK work like the RAW variants above,
-// except that if the richer log library is linked into the binary, we dispatch
-// to that instead.  This is potentially useful for internal logging and
-// assertions, where we are using RAW_LOG neither for its async-signal-safety
-// nor for its non-allocating nature, but rather because raw logging has very
-// few other dependencies.
-//
-// The API is a subset of the above: each macro only takes two arguments.  Use
-// StrCat if you need to build a richer message.
-#define ABSL_INTERNAL_LOG(severity, message)                                 \
-  do {                                                                       \
-    constexpr const char* absl_raw_logging_internal_filename = __FILE__;     \
-    ::absl::raw_logging_internal::internal_log_function(                     \
-        ABSL_RAW_LOGGING_INTERNAL_##severity,                                \
-        absl_raw_logging_internal_filename, __LINE__, message);              \
-    if (ABSL_RAW_LOGGING_INTERNAL_##severity == ::absl::LogSeverity::kFatal) \
-      ABSL_INTERNAL_UNREACHABLE;                                             \
-  } while (0)
-
-#define ABSL_INTERNAL_CHECK(condition, message)                    \
-  do {                                                             \
-    if (ABSL_PREDICT_FALSE(!(condition))) {                        \
-      std::string death_message = "Check " #condition " failed: "; \
-      death_message += std::string(message);                       \
-      ABSL_INTERNAL_LOG(FATAL, death_message);                     \
-    }                                                              \
-  } while (0)
-
-#define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo
-#define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning
-#define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError
-#define ABSL_RAW_LOGGING_INTERNAL_FATAL ::absl::LogSeverity::kFatal
-#define ABSL_RAW_LOGGING_INTERNAL_LEVEL(severity) \
-  ::absl::NormalizeLogSeverity(severity)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace raw_logging_internal {
-
-// Helper function to implement ABSL_RAW_LOG
-// Logs format... at "severity" level, reporting it
-// as called from file:line.
-// This does not allocate memory or acquire locks.
-void RawLog(absl::LogSeverity severity, const char* file, int line,
-            const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
-
-// Writes the provided buffer directly to stderr, in a safe, low-level manner.
-//
-// In POSIX this means calling write(), which is async-signal safe and does
-// not malloc.  If the platform supports the SYS_write syscall, we invoke that
-// directly to side-step any libc interception.
-void SafeWriteToStderr(const char *s, size_t len);
-
-// compile-time function to get the "base" filename, that is, the part of
-// a filename after the last "/" or "\" path separator.  The search starts at
-// the end of the string; the second parameter is the length of the string.
-constexpr const char* Basename(const char* fname, int offset) {
-  return offset == 0 || fname[offset - 1] == '/' || fname[offset - 1] == '\\'
-             ? fname + offset
-             : Basename(fname, offset - 1);
-}
-
-// For testing only.
-// Returns true if raw logging is fully supported. When it is not
-// fully supported, no messages will be emitted, but a log at FATAL
-// severity will cause an abort.
-//
-// TODO(gfalcon): Come up with a better name for this method.
-bool RawLoggingFullySupported();
-
-// Function type for a raw_logging customization hook for suppressing messages
-// by severity, and for writing custom prefixes on non-suppressed messages.
-//
-// The installed hook is called for every raw log invocation.  The message will
-// be logged to stderr only if the hook returns true.  FATAL errors will cause
-// the process to abort, even if writing to stderr is suppressed.  The hook is
-// also provided with an output buffer, where it can write a custom log message
-// prefix.
-//
-// The raw_logging system does not allocate memory or grab locks.  User-provided
-// hooks must avoid these operations, and must not throw exceptions.
-//
-// 'severity' is the severity level of the message being written.
-// 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro
-// was located.
-// 'buffer' and 'buf_size' are pointers to the buffer and buffer size.  If the
-// hook writes a prefix, it must increment *buffer and decrement *buf_size
-// accordingly.
-using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file,
-                               int line, char** buffer, int* buf_size);
-
-// Function type for a raw_logging customization hook called to abort a process
-// when a FATAL message is logged.  If the provided AbortHook() returns, the
-// logging system will call abort().
-//
-// 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro
-// was located.
-// The NUL-terminated logged message lives in the buffer between 'buf_start'
-// and 'buf_end'.  'prefix_end' points to the first non-prefix character of the
-// buffer (as written by the LogPrefixHook.)
-using AbortHook = void (*)(const char* file, int line, const char* buf_start,
-                           const char* prefix_end, const char* buf_end);
-
-// Internal logging function for ABSL_INTERNAL_LOG to dispatch to.
-//
-// TODO(gfalcon): When string_view no longer depends on base, change this
-// interface to take its message as a string_view instead.
-using InternalLogFunction = void (*)(absl::LogSeverity severity,
-                                     const char* file, int line,
-                                     const std::string& message);
-
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL extern base_internal::AtomicHook<
-    InternalLogFunction>
-    internal_log_function;
-
-void RegisterInternalLogFunction(InternalLogFunction func);
-
-}  // namespace raw_logging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_RAW_LOGGING_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/scheduling_mode.h b/third_party/abseil_cpp/absl/base/internal/scheduling_mode.h
deleted file mode 100644
index 8be5ab6dd3..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/scheduling_mode.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Core interfaces and definitions used by by low-level interfaces such as
-// SpinLock.
-
-#ifndef ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_
-#define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// Used to describe how a thread may be scheduled.  Typically associated with
-// the declaration of a resource supporting synchronized access.
-//
-// SCHEDULE_COOPERATIVE_AND_KERNEL:
-// Specifies that when waiting, a cooperative thread (e.g. a Fiber) may
-// reschedule (using base::scheduling semantics); allowing other cooperative
-// threads to proceed.
-//
-// SCHEDULE_KERNEL_ONLY: (Also described as "non-cooperative")
-// Specifies that no cooperative scheduling semantics may be used, even if the
-// current thread is itself cooperatively scheduled.  This means that
-// cooperative threads will NOT allow other cooperative threads to execute in
-// their place while waiting for a resource of this type.  Host operating system
-// semantics (e.g. a futex) may still be used.
-//
-// When optional, clients should strongly prefer SCHEDULE_COOPERATIVE_AND_KERNEL
-// by default.  SCHEDULE_KERNEL_ONLY should only be used for resources on which
-// base::scheduling (e.g. the implementation of a Scheduler) may depend.
-//
-// NOTE: Cooperative resources may not be nested below non-cooperative ones.
-// This means that it is invalid to to acquire a SCHEDULE_COOPERATIVE_AND_KERNEL
-// resource if a SCHEDULE_KERNEL_ONLY resource is already held.
-enum SchedulingMode {
-  SCHEDULE_KERNEL_ONLY = 0,         // Allow scheduling only the host OS.
-  SCHEDULE_COOPERATIVE_AND_KERNEL,  // Also allow cooperative scheduling.
-};
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/scoped_set_env.cc b/third_party/abseil_cpp/absl/base/internal/scoped_set_env.cc
deleted file mode 100644
index 8a934cb511..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/scoped_set_env.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/scoped_set_env.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-#include <cstdlib>
-
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-namespace {
-
-#ifdef _WIN32
-const int kMaxEnvVarValueSize = 1024;
-#endif
-
-void SetEnvVar(const char* name, const char* value) {
-#ifdef _WIN32
-  SetEnvironmentVariableA(name, value);
-#else
-  if (value == nullptr) {
-    ::unsetenv(name);
-  } else {
-    ::setenv(name, value, 1);
-  }
-#endif
-}
-
-}  // namespace
-
-ScopedSetEnv::ScopedSetEnv(const char* var_name, const char* new_value)
-    : var_name_(var_name), was_unset_(false) {
-#ifdef _WIN32
-  char buf[kMaxEnvVarValueSize];
-  auto get_res = GetEnvironmentVariableA(var_name_.c_str(), buf, sizeof(buf));
-  ABSL_INTERNAL_CHECK(get_res < sizeof(buf), "value exceeds buffer size");
-
-  if (get_res == 0) {
-    was_unset_ = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);
-  } else {
-    old_value_.assign(buf, get_res);
-  }
-
-  SetEnvironmentVariableA(var_name_.c_str(), new_value);
-#else
-  const char* val = ::getenv(var_name_.c_str());
-  if (val == nullptr) {
-    was_unset_ = true;
-  } else {
-    old_value_ = val;
-  }
-#endif
-
-  SetEnvVar(var_name_.c_str(), new_value);
-}
-
-ScopedSetEnv::~ScopedSetEnv() {
-  SetEnvVar(var_name_.c_str(), was_unset_ ? nullptr : old_value_.c_str());
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/scoped_set_env.h b/third_party/abseil_cpp/absl/base/internal/scoped_set_env.h
deleted file mode 100644
index 19ec7b5d8a..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/scoped_set_env.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_
-#define ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_
-
-#include <string>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-class ScopedSetEnv {
- public:
-  ScopedSetEnv(const char* var_name, const char* new_value);
-  ~ScopedSetEnv();
-
- private:
-  std::string var_name_;
-  std::string old_value_;
-
-  // True if the environment variable was initially not set.
-  bool was_unset_;
-};
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/scoped_set_env_test.cc b/third_party/abseil_cpp/absl/base/internal/scoped_set_env_test.cc
deleted file mode 100644
index 5cbad246c6..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/scoped_set_env_test.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/scoped_set_env.h"
-
-namespace {
-
-using absl::base_internal::ScopedSetEnv;
-
-std::string GetEnvVar(const char* name) {
-#ifdef _WIN32
-  char buf[1024];
-  auto get_res = GetEnvironmentVariableA(name, buf, sizeof(buf));
-  if (get_res >= sizeof(buf)) {
-    return "TOO_BIG";
-  }
-
-  if (get_res == 0) {
-    return "UNSET";
-  }
-
-  return std::string(buf, get_res);
-#else
-  const char* val = ::getenv(name);
-  if (val == nullptr) {
-    return "UNSET";
-  }
-
-  return val;
-#endif
-}
-
-TEST(ScopedSetEnvTest, SetNonExistingVarToString) {
-  EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
-
-  {
-    ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value");
-
-    EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
-  }
-
-  EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
-}
-
-TEST(ScopedSetEnvTest, SetNonExistingVarToNull) {
-  EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
-
-  {
-    ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", nullptr);
-
-    EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
-  }
-
-  EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
-}
-
-TEST(ScopedSetEnvTest, SetExistingVarToString) {
-  ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value");
-  EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
-
-  {
-    ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "new_value");
-
-    EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "new_value");
-  }
-
-  EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
-}
-
-TEST(ScopedSetEnvTest, SetExistingVarToNull) {
-  ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value");
-  EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
-
-  {
-    ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", nullptr);
-
-    EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET");
-  }
-
-  EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value");
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/spinlock.cc b/third_party/abseil_cpp/absl/base/internal/spinlock.cc
deleted file mode 100644
index a7d44f3eb0..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/spinlock.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/spinlock.h"
-
-#include <algorithm>
-#include <atomic>
-#include <limits>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/atomic_hook.h"
-#include "absl/base/internal/cycleclock.h"
-#include "absl/base/internal/spinlock_wait.h"
-#include "absl/base/internal/sysinfo.h" /* For NumCPUs() */
-#include "absl/base/call_once.h"
-
-// Description of lock-word:
-//  31..00: [............................3][2][1][0]
-//
-//     [0]: kSpinLockHeld
-//     [1]: kSpinLockCooperative
-//     [2]: kSpinLockDisabledScheduling
-// [31..3]: ONLY kSpinLockSleeper OR
-//          Wait time in cycles >> PROFILE_TIMESTAMP_SHIFT
-//
-// Detailed descriptions:
-//
-// Bit [0]: The lock is considered held iff kSpinLockHeld is set.
-//
-// Bit [1]: Eligible waiters (e.g. Fibers) may co-operatively reschedule when
-//          contended iff kSpinLockCooperative is set.
-//
-// Bit [2]: This bit is exclusive from bit [1].  It is used only by a
-//          non-cooperative lock.  When set, indicates that scheduling was
-//          successfully disabled when the lock was acquired.  May be unset,
-//          even if non-cooperative, if a ThreadIdentity did not yet exist at
-//          time of acquisition.
-//
-// Bit [3]: If this is the only upper bit ([31..3]) set then this lock was
-//          acquired without contention, however, at least one waiter exists.
-//
-//          Otherwise, bits [31..3] represent the time spent by the current lock
-//          holder to acquire the lock.  There may be outstanding waiter(s).
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static base_internal::AtomicHook<void (*)(
-    const void *lock, int64_t wait_cycles)>
-    submit_profile_data;
-
-void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock,
-                                         int64_t wait_cycles)) {
-  submit_profile_data.Store(fn);
-}
-
-// Static member variable definitions.
-constexpr uint32_t SpinLock::kSpinLockHeld;
-constexpr uint32_t SpinLock::kSpinLockCooperative;
-constexpr uint32_t SpinLock::kSpinLockDisabledScheduling;
-constexpr uint32_t SpinLock::kSpinLockSleeper;
-constexpr uint32_t SpinLock::kWaitTimeMask;
-
-// Uncommon constructors.
-SpinLock::SpinLock(base_internal::SchedulingMode mode)
-    : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) {
-  ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static);
-}
-
-// Monitor the lock to see if its value changes within some time period
-// (adaptive_spin_count loop iterations). The last value read from the lock
-// is returned from the method.
-uint32_t SpinLock::SpinLoop() {
-  // We are already in the slow path of SpinLock, initialize the
-  // adaptive_spin_count here.
-  ABSL_CONST_INIT static absl::once_flag init_adaptive_spin_count;
-  ABSL_CONST_INIT static int adaptive_spin_count = 0;
-  base_internal::LowLevelCallOnce(&init_adaptive_spin_count, []() {
-    adaptive_spin_count = base_internal::NumCPUs() > 1 ? 1000 : 1;
-  });
-
-  int c = adaptive_spin_count;
-  uint32_t lock_value;
-  do {
-    lock_value = lockword_.load(std::memory_order_relaxed);
-  } while ((lock_value & kSpinLockHeld) != 0 && --c > 0);
-  return lock_value;
-}
-
-void SpinLock::SlowLock() {
-  uint32_t lock_value = SpinLoop();
-  lock_value = TryLockInternal(lock_value, 0);
-  if ((lock_value & kSpinLockHeld) == 0) {
-    return;
-  }
-
-  base_internal::SchedulingMode scheduling_mode;
-  if ((lock_value & kSpinLockCooperative) != 0) {
-    scheduling_mode = base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL;
-  } else {
-    scheduling_mode = base_internal::SCHEDULE_KERNEL_ONLY;
-  }
-
-  // The lock was not obtained initially, so this thread needs to wait for
-  // it.  Record the current timestamp in the local variable wait_start_time
-  // so the total wait time can be stored in the lockword once this thread
-  // obtains the lock.
-  int64_t wait_start_time = CycleClock::Now();
-  uint32_t wait_cycles = 0;
-  int lock_wait_call_count = 0;
-  while ((lock_value & kSpinLockHeld) != 0) {
-    // If the lock is currently held, but not marked as having a sleeper, mark
-    // it as having a sleeper.
-    if ((lock_value & kWaitTimeMask) == 0) {
-      // Here, just "mark" that the thread is going to sleep.  Don't store the
-      // lock wait time in the lock as that will cause the current lock
-      // owner to think it experienced contention.
-      if (lockword_.compare_exchange_strong(
-              lock_value, lock_value | kSpinLockSleeper,
-              std::memory_order_relaxed, std::memory_order_relaxed)) {
-        // Successfully transitioned to kSpinLockSleeper.  Pass
-        // kSpinLockSleeper to the SpinLockWait routine to properly indicate
-        // the last lock_value observed.
-        lock_value |= kSpinLockSleeper;
-      } else if ((lock_value & kSpinLockHeld) == 0) {
-        // Lock is free again, so try and acquire it before sleeping.  The
-        // new lock state will be the number of cycles this thread waited if
-        // this thread obtains the lock.
-        lock_value = TryLockInternal(lock_value, wait_cycles);
-        continue;   // Skip the delay at the end of the loop.
-      }
-    }
-
-    // SpinLockDelay() calls into fiber scheduler, we need to see
-    // synchronization there to avoid false positives.
-    ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0);
-    // Wait for an OS specific delay.
-    base_internal::SpinLockDelay(&lockword_, lock_value, ++lock_wait_call_count,
-                                 scheduling_mode);
-    ABSL_TSAN_MUTEX_POST_DIVERT(this, 0);
-    // Spin again after returning from the wait routine to give this thread
-    // some chance of obtaining the lock.
-    lock_value = SpinLoop();
-    wait_cycles = EncodeWaitCycles(wait_start_time, CycleClock::Now());
-    lock_value = TryLockInternal(lock_value, wait_cycles);
-  }
-}
-
-void SpinLock::SlowUnlock(uint32_t lock_value) {
-  base_internal::SpinLockWake(&lockword_,
-                              false);  // wake waiter if necessary
-
-  // If our acquisition was contended, collect contentionz profile info.  We
-  // reserve a unitary wait time to represent that a waiter exists without our
-  // own acquisition having been contended.
-  if ((lock_value & kWaitTimeMask) != kSpinLockSleeper) {
-    const uint64_t wait_cycles = DecodeWaitCycles(lock_value);
-    ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0);
-    submit_profile_data(this, wait_cycles);
-    ABSL_TSAN_MUTEX_POST_DIVERT(this, 0);
-  }
-}
-
-// We use the upper 29 bits of the lock word to store the time spent waiting to
-// acquire this lock.  This is reported by contentionz profiling.  Since the
-// lower bits of the cycle counter wrap very quickly on high-frequency
-// processors we divide to reduce the granularity to 2^kProfileTimestampShift
-// sized units.  On a 4Ghz machine this will lose track of wait times greater
-// than (2^29/4 Ghz)*128 =~ 17.2 seconds.  Such waits should be extremely rare.
-static constexpr int kProfileTimestampShift = 7;
-
-// We currently reserve the lower 3 bits.
-static constexpr int kLockwordReservedShift = 3;
-
-uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time,
-                                    int64_t wait_end_time) {
-  static const int64_t kMaxWaitTime =
-      std::numeric_limits<uint32_t>::max() >> kLockwordReservedShift;
-  int64_t scaled_wait_time =
-      (wait_end_time - wait_start_time) >> kProfileTimestampShift;
-
-  // Return a representation of the time spent waiting that can be stored in
-  // the lock word's upper bits.
-  uint32_t clamped = static_cast<uint32_t>(
-      std::min(scaled_wait_time, kMaxWaitTime) << kLockwordReservedShift);
-
-  if (clamped == 0) {
-    return kSpinLockSleeper;  // Just wake waiters, but don't record contention.
-  }
-  // Bump up value if necessary to avoid returning kSpinLockSleeper.
-  const uint32_t kMinWaitTime =
-      kSpinLockSleeper + (1 << kLockwordReservedShift);
-  if (clamped == kSpinLockSleeper) {
-    return kMinWaitTime;
-  }
-  return clamped;
-}
-
-uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) {
-  // Cast to uint32_t first to ensure bits [63:32] are cleared.
-  const uint64_t scaled_wait_time =
-      static_cast<uint32_t>(lock_value & kWaitTimeMask);
-  return scaled_wait_time << (kProfileTimestampShift - kLockwordReservedShift);
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/spinlock.h b/third_party/abseil_cpp/absl/base/internal/spinlock.h
deleted file mode 100644
index e6ac9e6400..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/spinlock.h
+++ /dev/null
@@ -1,237 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-//  Most users requiring mutual exclusion should use Mutex.
-//  SpinLock is provided for use in three situations:
-//   - for use in code that Mutex itself depends on
-//   - to get a faster fast-path release under low contention (without an
-//     atomic read-modify-write) In return, SpinLock has worse behaviour under
-//     contention, which is why Mutex is preferred in most situations.
-//   - for async signal safety (see below)
-
-// SpinLock is async signal safe.  If a spinlock is used within a signal
-// handler, all code that acquires the lock must ensure that the signal cannot
-// arrive while they are holding the lock.  Typically, this is done by blocking
-// the signal.
-
-#ifndef ABSL_BASE_INTERNAL_SPINLOCK_H_
-#define ABSL_BASE_INTERNAL_SPINLOCK_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <atomic>
-
-#include "absl/base/attributes.h"
-#include "absl/base/const_init.h"
-#include "absl/base/dynamic_annotations.h"
-#include "absl/base/internal/low_level_scheduling.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/scheduling_mode.h"
-#include "absl/base/internal/tsan_mutex_interface.h"
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/base/thread_annotations.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-class ABSL_LOCKABLE SpinLock {
- public:
-  SpinLock() : lockword_(kSpinLockCooperative) {
-    ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static);
-  }
-
-  // Constructors that allow non-cooperative spinlocks to be created for use
-  // inside thread schedulers.  Normal clients should not use these.
-  explicit SpinLock(base_internal::SchedulingMode mode);
-
-  // Constructor for global SpinLock instances.  See absl/base/const_init.h.
-  constexpr SpinLock(absl::ConstInitType, base_internal::SchedulingMode mode)
-      : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) {}
-
-  // For global SpinLock instances prefer trivial destructor when possible.
-  // Default but non-trivial destructor in some build configurations causes an
-  // extra static initializer.
-#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE
-  ~SpinLock() { ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); }
-#else
-  ~SpinLock() = default;
-#endif
-
-  // Acquire this SpinLock.
-  inline void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() {
-    ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
-    if (!TryLockImpl()) {
-      SlowLock();
-    }
-    ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
-  }
-
-  // Try to acquire this SpinLock without blocking and return true if the
-  // acquisition was successful.  If the lock was not acquired, false is
-  // returned.  If this SpinLock is free at the time of the call, TryLock
-  // will return true with high probability.
-  inline bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
-    ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock);
-    bool res = TryLockImpl();
-    ABSL_TSAN_MUTEX_POST_LOCK(
-        this, __tsan_mutex_try_lock | (res ? 0 : __tsan_mutex_try_lock_failed),
-        0);
-    return res;
-  }
-
-  // Release this SpinLock, which must be held by the calling thread.
-  inline void Unlock() ABSL_UNLOCK_FUNCTION() {
-    ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0);
-    uint32_t lock_value = lockword_.load(std::memory_order_relaxed);
-    lock_value = lockword_.exchange(lock_value & kSpinLockCooperative,
-                                    std::memory_order_release);
-
-    if ((lock_value & kSpinLockDisabledScheduling) != 0) {
-      base_internal::SchedulingGuard::EnableRescheduling(true);
-    }
-    if ((lock_value & kWaitTimeMask) != 0) {
-      // Collect contentionz profile info, and speed the wakeup of any waiter.
-      // The wait_cycles value indicates how long this thread spent waiting
-      // for the lock.
-      SlowUnlock(lock_value);
-    }
-    ABSL_TSAN_MUTEX_POST_UNLOCK(this, 0);
-  }
-
-  // Determine if the lock is held.  When the lock is held by the invoking
-  // thread, true will always be returned. Intended to be used as
-  // CHECK(lock.IsHeld()).
-  inline bool IsHeld() const {
-    return (lockword_.load(std::memory_order_relaxed) & kSpinLockHeld) != 0;
-  }
-
- protected:
-  // These should not be exported except for testing.
-
-  // Store number of cycles between wait_start_time and wait_end_time in a
-  // lock value.
-  static uint32_t EncodeWaitCycles(int64_t wait_start_time,
-                                   int64_t wait_end_time);
-
-  // Extract number of wait cycles in a lock value.
-  static uint64_t DecodeWaitCycles(uint32_t lock_value);
-
-  // Provide access to protected method above.  Use for testing only.
-  friend struct SpinLockTest;
-
- private:
-  // lockword_ is used to store the following:
-  //
-  // bit[0] encodes whether a lock is being held.
-  // bit[1] encodes whether a lock uses cooperative scheduling.
-  // bit[2] encodes whether a lock disables scheduling.
-  // bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int.
-  static constexpr uint32_t kSpinLockHeld = 1;
-  static constexpr uint32_t kSpinLockCooperative = 2;
-  static constexpr uint32_t kSpinLockDisabledScheduling = 4;
-  static constexpr uint32_t kSpinLockSleeper = 8;
-  // Includes kSpinLockSleeper.
-  static constexpr uint32_t kWaitTimeMask =
-      ~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling);
-
-  // Returns true if the provided scheduling mode is cooperative.
-  static constexpr bool IsCooperative(
-      base_internal::SchedulingMode scheduling_mode) {
-    return scheduling_mode == base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL;
-  }
-
-  uint32_t TryLockInternal(uint32_t lock_value, uint32_t wait_cycles);
-  void SlowLock() ABSL_ATTRIBUTE_COLD;
-  void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD;
-  uint32_t SpinLoop();
-
-  inline bool TryLockImpl() {
-    uint32_t lock_value = lockword_.load(std::memory_order_relaxed);
-    return (TryLockInternal(lock_value, 0) & kSpinLockHeld) == 0;
-  }
-
-  std::atomic<uint32_t> lockword_;
-
-  SpinLock(const SpinLock&) = delete;
-  SpinLock& operator=(const SpinLock&) = delete;
-};
-
-// Corresponding locker object that arranges to acquire a spinlock for
-// the duration of a C++ scope.
-class ABSL_SCOPED_LOCKABLE SpinLockHolder {
- public:
-  inline explicit SpinLockHolder(SpinLock* l) ABSL_EXCLUSIVE_LOCK_FUNCTION(l)
-      : lock_(l) {
-    l->Lock();
-  }
-  inline ~SpinLockHolder() ABSL_UNLOCK_FUNCTION() { lock_->Unlock(); }
-
-  SpinLockHolder(const SpinLockHolder&) = delete;
-  SpinLockHolder& operator=(const SpinLockHolder&) = delete;
-
- private:
-  SpinLock* lock_;
-};
-
-// Register a hook for profiling support.
-//
-// The function pointer registered here will be called whenever a spinlock is
-// contended.  The callback is given an opaque handle to the contended spinlock
-// and the number of wait cycles.  This is thread-safe, but only a single
-// profiler can be registered.  It is an error to call this function multiple
-// times with different arguments.
-void RegisterSpinLockProfiler(void (*fn)(const void* lock,
-                                         int64_t wait_cycles));
-
-//------------------------------------------------------------------------------
-// Public interface ends here.
-//------------------------------------------------------------------------------
-
-// If (result & kSpinLockHeld) == 0, then *this was successfully locked.
-// Otherwise, returns last observed value for lockword_.
-inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value,
-                                          uint32_t wait_cycles) {
-  if ((lock_value & kSpinLockHeld) != 0) {
-    return lock_value;
-  }
-
-  uint32_t sched_disabled_bit = 0;
-  if ((lock_value & kSpinLockCooperative) == 0) {
-    // For non-cooperative locks we must make sure we mark ourselves as
-    // non-reschedulable before we attempt to CompareAndSwap.
-    if (base_internal::SchedulingGuard::DisableRescheduling()) {
-      sched_disabled_bit = kSpinLockDisabledScheduling;
-    }
-  }
-
-  if (!lockword_.compare_exchange_strong(
-          lock_value,
-          kSpinLockHeld | lock_value | wait_cycles | sched_disabled_bit,
-          std::memory_order_acquire, std::memory_order_relaxed)) {
-    base_internal::SchedulingGuard::EnableRescheduling(sched_disabled_bit != 0);
-  }
-
-  return lock_value;
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_SPINLOCK_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/spinlock_akaros.inc b/third_party/abseil_cpp/absl/base/internal/spinlock_akaros.inc
deleted file mode 100644
index bc468940fc..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/spinlock_akaros.inc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This file is an Akaros-specific part of spinlock_wait.cc
-
-#include <atomic>
-
-#include "absl/base/internal/scheduling_mode.h"
-
-extern "C" {
-
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
-    std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */,
-    int /* loop */, absl::base_internal::SchedulingMode /* mode */) {
-  // In Akaros, one must take care not to call anything that could cause a
-  // malloc(), a blocking system call, or a uthread_yield() while holding a
-  // spinlock. Our callers assume will not call into libraries or other
-  // arbitrary code.
-}
-
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(
-    std::atomic<uint32_t>* /* lock_word */, bool /* all */) {}
-
-}  // extern "C"
diff --git a/third_party/abseil_cpp/absl/base/internal/spinlock_benchmark.cc b/third_party/abseil_cpp/absl/base/internal/spinlock_benchmark.cc
deleted file mode 100644
index 0451c65f95..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/spinlock_benchmark.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// See also //absl/synchronization:mutex_benchmark for a comparison of SpinLock
-// and Mutex performance under varying levels of contention.
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/scheduling_mode.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/synchronization/internal/create_thread_identity.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-template <absl::base_internal::SchedulingMode scheduling_mode>
-static void BM_SpinLock(benchmark::State& state) {
-  // Ensure a ThreadIdentity is installed.
-  ABSL_INTERNAL_CHECK(
-      absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() !=
-          nullptr,
-      "GetOrCreateCurrentThreadIdentity() failed");
-
-  static auto* spinlock = new absl::base_internal::SpinLock(scheduling_mode);
-  for (auto _ : state) {
-    absl::base_internal::SpinLockHolder holder(spinlock);
-  }
-}
-
-BENCHMARK_TEMPLATE(BM_SpinLock,
-                   absl::base_internal::SCHEDULE_KERNEL_ONLY)
-    ->UseRealTime()
-    ->Threads(1)
-    ->ThreadPerCpu();
-
-BENCHMARK_TEMPLATE(BM_SpinLock,
-                   absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL)
-    ->UseRealTime()
-    ->Threads(1)
-    ->ThreadPerCpu();
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/spinlock_linux.inc b/third_party/abseil_cpp/absl/base/internal/spinlock_linux.inc
deleted file mode 100644
index e31c6ed477..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/spinlock_linux.inc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This file is a Linux-specific part of spinlock_wait.cc
-
-#include <linux/futex.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#include <atomic>
-#include <climits>
-#include <cstdint>
-#include <ctime>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/errno_saver.h"
-
-// The SpinLock lockword is `std::atomic<uint32_t>`. Here we assert that
-// `std::atomic<uint32_t>` is bitwise equivalent of the `int` expected
-// by SYS_futex. We also assume that reads/writes done to the lockword
-// by SYS_futex have rational semantics with regard to the
-// std::atomic<> API. C++ provides no guarantees of these assumptions,
-// but they are believed to hold in practice.
-static_assert(sizeof(std::atomic<uint32_t>) == sizeof(int),
-              "SpinLock lockword has the wrong size for a futex");
-
-// Some Android headers are missing these definitions even though they
-// support these futex operations.
-#ifdef __BIONIC__
-#ifndef SYS_futex
-#define SYS_futex __NR_futex
-#endif
-#ifndef FUTEX_PRIVATE_FLAG
-#define FUTEX_PRIVATE_FLAG 128
-#endif
-#endif
-
-#if defined(__NR_futex_time64) && !defined(SYS_futex_time64)
-#define SYS_futex_time64 __NR_futex_time64
-#endif
-
-#if defined(SYS_futex_time64) && !defined(SYS_futex)
-#define SYS_futex SYS_futex_time64
-#endif
-
-extern "C" {
-
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
-    std::atomic<uint32_t> *w, uint32_t value, int loop,
-    absl::base_internal::SchedulingMode) {
-  absl::base_internal::ErrnoSaver errno_saver;
-  struct timespec tm;
-  tm.tv_sec = 0;
-  tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop);
-  syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm);
-}
-
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w,
-                                                  bool all) {
-  syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0);
-}
-
-}  // extern "C"
diff --git a/third_party/abseil_cpp/absl/base/internal/spinlock_posix.inc b/third_party/abseil_cpp/absl/base/internal/spinlock_posix.inc
deleted file mode 100644
index fcd21b151b..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/spinlock_posix.inc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This file is a Posix-specific part of spinlock_wait.cc
-
-#include <sched.h>
-
-#include <atomic>
-#include <ctime>
-
-#include "absl/base/internal/errno_saver.h"
-#include "absl/base/internal/scheduling_mode.h"
-#include "absl/base/port.h"
-
-extern "C" {
-
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
-    std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop,
-    absl::base_internal::SchedulingMode /* mode */) {
-  absl::base_internal::ErrnoSaver errno_saver;
-  if (loop == 0) {
-  } else if (loop == 1) {
-    sched_yield();
-  } else {
-    struct timespec tm;
-    tm.tv_sec = 0;
-    tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop);
-    nanosleep(&tm, nullptr);
-  }
-}
-
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(
-    std::atomic<uint32_t>* /* lock_word */, bool /* all */) {}
-
-}  // extern "C"
diff --git a/third_party/abseil_cpp/absl/base/internal/spinlock_wait.cc b/third_party/abseil_cpp/absl/base/internal/spinlock_wait.cc
deleted file mode 100644
index fa824be1c0..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/spinlock_wait.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// The OS-specific header included below must provide two calls:
-// AbslInternalSpinLockDelay() and AbslInternalSpinLockWake().
-// See spinlock_wait.h for the specs.
-
-#include <atomic>
-#include <cstdint>
-
-#include "absl/base/internal/spinlock_wait.h"
-
-#if defined(_WIN32)
-#include "absl/base/internal/spinlock_win32.inc"
-#elif defined(__linux__)
-#include "absl/base/internal/spinlock_linux.inc"
-#elif defined(__akaros__)
-#include "absl/base/internal/spinlock_akaros.inc"
-#else
-#include "absl/base/internal/spinlock_posix.inc"
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// See spinlock_wait.h for spec.
-uint32_t SpinLockWait(std::atomic<uint32_t> *w, int n,
-                      const SpinLockWaitTransition trans[],
-                      base_internal::SchedulingMode scheduling_mode) {
-  int loop = 0;
-  for (;;) {
-    uint32_t v = w->load(std::memory_order_acquire);
-    int i;
-    for (i = 0; i != n && v != trans[i].from; i++) {
-    }
-    if (i == n) {
-      SpinLockDelay(w, v, ++loop, scheduling_mode);  // no matching transition
-    } else if (trans[i].to == v ||                   // null transition
-               w->compare_exchange_strong(v, trans[i].to,
-                                          std::memory_order_acquire,
-                                          std::memory_order_relaxed)) {
-      if (trans[i].done) return v;
-    }
-  }
-}
-
-static std::atomic<uint64_t> delay_rand;
-
-// Return a suggested delay in nanoseconds for iteration number "loop"
-int SpinLockSuggestedDelayNS(int loop) {
-  // Weak pseudo-random number generator to get some spread between threads
-  // when many are spinning.
-  uint64_t r = delay_rand.load(std::memory_order_relaxed);
-  r = 0x5deece66dLL * r + 0xb;   // numbers from nrand48()
-  delay_rand.store(r, std::memory_order_relaxed);
-
-  if (loop < 0 || loop > 32) {   // limit loop to 0..32
-    loop = 32;
-  }
-  const int kMinDelay = 128 << 10;  // 128us
-  // Double delay every 8 iterations, up to 16x (2ms).
-  int delay = kMinDelay << (loop / 8);
-  // Randomize in delay..2*delay range, for resulting 128us..4ms range.
-  return delay | ((delay - 1) & static_cast<int>(r));
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/spinlock_wait.h b/third_party/abseil_cpp/absl/base/internal/spinlock_wait.h
deleted file mode 100644
index 169bc749fb..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/spinlock_wait.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_
-#define ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_
-
-// Operations to make atomic transitions on a word, and to allow
-// waiting for those transitions to become possible.
-
-#include <stdint.h>
-#include <atomic>
-
-#include "absl/base/internal/scheduling_mode.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// SpinLockWait() waits until it can perform one of several transitions from
-// "from" to "to".  It returns when it performs a transition where done==true.
-struct SpinLockWaitTransition {
-  uint32_t from;
-  uint32_t to;
-  bool done;
-};
-
-// Wait until *w can transition from trans[i].from to trans[i].to for some i
-// satisfying 0<=i<n && trans[i].done, atomically make the transition,
-// then return the old value of *w.   Make any other atomic transitions
-// where !trans[i].done, but continue waiting.
-uint32_t SpinLockWait(std::atomic<uint32_t> *w, int n,
-                      const SpinLockWaitTransition trans[],
-                      SchedulingMode scheduling_mode);
-
-// If possible, wake some thread that has called SpinLockDelay(w, ...). If
-// "all" is true, wake all such threads.  This call is a hint, and on some
-// systems it may be a no-op; threads calling SpinLockDelay() will always wake
-// eventually even if SpinLockWake() is never called.
-void SpinLockWake(std::atomic<uint32_t> *w, bool all);
-
-// Wait for an appropriate spin delay on iteration "loop" of a
-// spin loop on location *w, whose previously observed value was "value".
-// SpinLockDelay() may do nothing, may yield the CPU, may sleep a clock tick,
-// or may wait for a delay that can be truncated by a call to SpinLockWake(w).
-// In all cases, it must return in bounded time even if SpinLockWake() is not
-// called.
-void SpinLockDelay(std::atomic<uint32_t> *w, uint32_t value, int loop,
-                   base_internal::SchedulingMode scheduling_mode);
-
-// Helper used by AbslInternalSpinLockDelay.
-// Returns a suggested delay in nanoseconds for iteration number "loop".
-int SpinLockSuggestedDelayNS(int loop);
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// In some build configurations we pass --detect-odr-violations to the
-// gold linker.  This causes it to flag weak symbol overrides as ODR
-// violations.  Because ODR only applies to C++ and not C,
-// --detect-odr-violations ignores symbols not mangled with C++ names.
-// By changing our extension points to be extern "C", we dodge this
-// check.
-extern "C" {
-void AbslInternalSpinLockWake(std::atomic<uint32_t> *w, bool all);
-void AbslInternalSpinLockDelay(
-    std::atomic<uint32_t> *w, uint32_t value, int loop,
-    absl::base_internal::SchedulingMode scheduling_mode);
-}
-
-inline void absl::base_internal::SpinLockWake(std::atomic<uint32_t> *w,
-                                              bool all) {
-  AbslInternalSpinLockWake(w, all);
-}
-
-inline void absl::base_internal::SpinLockDelay(
-    std::atomic<uint32_t> *w, uint32_t value, int loop,
-    absl::base_internal::SchedulingMode scheduling_mode) {
-  AbslInternalSpinLockDelay(w, value, loop, scheduling_mode);
-}
-
-#endif  // ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/spinlock_win32.inc b/third_party/abseil_cpp/absl/base/internal/spinlock_win32.inc
deleted file mode 100644
index 78654b5b59..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/spinlock_win32.inc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This file is a Win32-specific part of spinlock_wait.cc
-
-#include <windows.h>
-#include <atomic>
-#include "absl/base/internal/scheduling_mode.h"
-
-extern "C" {
-
-void AbslInternalSpinLockDelay(std::atomic<uint32_t>* /* lock_word */,
-                               uint32_t /* value */, int loop,
-                               absl::base_internal::SchedulingMode /* mode */) {
-  if (loop == 0) {
-  } else if (loop == 1) {
-    Sleep(0);
-  } else {
-    Sleep(absl::base_internal::SpinLockSuggestedDelayNS(loop) / 1000000);
-  }
-}
-
-void AbslInternalSpinLockWake(std::atomic<uint32_t>* /* lock_word */,
-                              bool /* all */) {}
-
-}  // extern "C"
diff --git a/third_party/abseil_cpp/absl/base/internal/strerror.cc b/third_party/abseil_cpp/absl/base/internal/strerror.cc
deleted file mode 100644
index d66ba12011..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/strerror.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/strerror.h"
-
-#include <array>
-#include <cerrno>
-#include <cstddef>
-#include <cstdio>
-#include <cstring>
-#include <string>
-#include <type_traits>
-
-#include "absl/base/internal/errno_saver.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-namespace {
-
-const char* StrErrorAdaptor(int errnum, char* buf, size_t buflen) {
-#if defined(_WIN32)
-  int rc = strerror_s(buf, buflen, errnum);
-  buf[buflen - 1] = '\0';  // guarantee NUL termination
-  if (rc == 0 && strncmp(buf, "Unknown error", buflen) == 0) *buf = '\0';
-  return buf;
-#else
-  // The type of `ret` is platform-specific; both of these branches must compile
-  // either way but only one will execute on any given platform:
-  auto ret = strerror_r(errnum, buf, buflen);
-  if (std::is_same<decltype(ret), int>::value) {
-    // XSI `strerror_r`; `ret` is `int`:
-    if (ret) *buf = '\0';
-    return buf;
-  } else {
-    // GNU `strerror_r`; `ret` is `char *`:
-    return reinterpret_cast<const char*>(ret);
-  }
-#endif
-}
-
-std::string StrErrorInternal(int errnum) {
-  absl::base_internal::ErrnoSaver errno_saver;
-  char buf[100];
-  const char* str = StrErrorAdaptor(errnum, buf, sizeof buf);
-  if (*str == '\0') {
-    snprintf(buf, sizeof buf, "Unknown error %d", errnum);
-    str = buf;
-  }
-  return str;
-}
-
-// kSysNerr is the number of errors from a recent glibc. `StrError()` falls back
-// to `StrErrorAdaptor()` if the value is larger than this.
-constexpr int kSysNerr = 135;
-
-std::array<std::string, kSysNerr>* NewStrErrorTable() {
-  auto* table = new std::array<std::string, kSysNerr>;
-  for (int i = 0; i < static_cast<int>(table->size()); ++i) {
-    (*table)[i] = StrErrorInternal(i);
-  }
-  return table;
-}
-
-}  // namespace
-
-std::string StrError(int errnum) {
-  static const auto* table = NewStrErrorTable();
-  if (errnum >= 0 && errnum < static_cast<int>(table->size())) {
-    return (*table)[errnum];
-  }
-  return StrErrorInternal(errnum);
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/strerror.h b/third_party/abseil_cpp/absl/base/internal/strerror.h
deleted file mode 100644
index 350097366e..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/strerror.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_STRERROR_H_
-#define ABSL_BASE_INTERNAL_STRERROR_H_
-
-#include <string>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// A portable and thread-safe alternative to C89's `strerror`.
-//
-// The C89 specification of `strerror` is not suitable for use in a
-// multi-threaded application as the returned string may be changed by calls to
-// `strerror` from another thread.  The many non-stdlib alternatives differ
-// enough in their names, availability, and semantics to justify this wrapper
-// around them.  `errno` will not be modified by a call to `absl::StrError`.
-std::string StrError(int errnum);
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_STRERROR_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/strerror_benchmark.cc b/third_party/abseil_cpp/absl/base/internal/strerror_benchmark.cc
deleted file mode 100644
index c9ab14a89d..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/strerror_benchmark.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cerrno>
-#include <cstdio>
-#include <string>
-
-#include "absl/base/internal/strerror.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-void BM_AbslStrError(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::base_internal::StrError(ERANGE));
-  }
-}
-BENCHMARK(BM_AbslStrError);
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/strerror_test.cc b/third_party/abseil_cpp/absl/base/internal/strerror_test.cc
deleted file mode 100644
index a53da97f92..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/strerror_test.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/strerror.h"
-
-#include <atomic>
-#include <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <string>
-#include <thread>  // NOLINT(build/c++11)
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/match.h"
-
-namespace {
-using ::testing::AnyOf;
-using ::testing::Eq;
-
-TEST(StrErrorTest, ValidErrorCode) {
-  errno = ERANGE;
-  EXPECT_THAT(absl::base_internal::StrError(EDOM), Eq(strerror(EDOM)));
-  EXPECT_THAT(errno, Eq(ERANGE));
-}
-
-TEST(StrErrorTest, InvalidErrorCode) {
-  errno = ERANGE;
-  EXPECT_THAT(absl::base_internal::StrError(-1),
-              AnyOf(Eq("No error information"), Eq("Unknown error -1")));
-  EXPECT_THAT(errno, Eq(ERANGE));
-}
-
-TEST(StrErrorTest, MultipleThreads) {
-  // In this test, we will start up 2 threads and have each one call
-  // StrError 1000 times, each time with a different errnum.  We
-  // expect that StrError(errnum) will return a string equal to the
-  // one returned by strerror(errnum), if the code is known.  Since
-  // strerror is known to be thread-hostile, collect all the expected
-  // strings up front.
-  const int kNumCodes = 1000;
-  std::vector<std::string> expected_strings(kNumCodes);
-  for (int i = 0; i < kNumCodes; ++i) {
-    expected_strings[i] = strerror(i);
-  }
-
-  std::atomic_int counter(0);
-  auto thread_fun = [&]() {
-    for (int i = 0; i < kNumCodes; ++i) {
-      ++counter;
-      errno = ERANGE;
-      const std::string value = absl::base_internal::StrError(i);
-      // Only the GNU implementation is guaranteed to provide the
-      // string "Unknown error nnn". POSIX doesn't say anything.
-      if (!absl::StartsWith(value, "Unknown error ")) {
-        EXPECT_THAT(absl::base_internal::StrError(i), Eq(expected_strings[i]));
-      }
-      EXPECT_THAT(errno, Eq(ERANGE));
-    }
-  };
-
-  const int kNumThreads = 100;
-  std::vector<std::thread> threads;
-  for (int i = 0; i < kNumThreads; ++i) {
-    threads.push_back(std::thread(thread_fun));
-  }
-  for (auto& thread : threads) {
-    thread.join();
-  }
-
-  EXPECT_THAT(counter, Eq(kNumThreads * kNumCodes));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/sysinfo.cc b/third_party/abseil_cpp/absl/base/internal/sysinfo.cc
deleted file mode 100644
index 4a3b205034..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/sysinfo.cc
+++ /dev/null
@@ -1,439 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/sysinfo.h"
-
-#include "absl/base/attributes.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#else
-#include <fcntl.h>
-#include <pthread.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#endif
-
-#ifdef __linux__
-#include <sys/syscall.h>
-#endif
-
-#if defined(__APPLE__) || defined(__FreeBSD__)
-#include <sys/sysctl.h>
-#endif
-
-#if defined(__myriad2__)
-#include <rtems.h>
-#endif
-
-#include <string.h>
-
-#include <cassert>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <ctime>
-#include <limits>
-#include <thread>  // NOLINT(build/c++11)
-#include <utility>
-#include <vector>
-
-#include "absl/base/call_once.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/base/internal/unscaledcycleclock.h"
-#include "absl/base/thread_annotations.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-static int GetNumCPUs() {
-#if defined(__myriad2__)
-  return 1;
-#else
-  // Other possibilities:
-  //  - Read /sys/devices/system/cpu/online and use cpumask_parse()
-  //  - sysconf(_SC_NPROCESSORS_ONLN)
-  return std::thread::hardware_concurrency();
-#endif
-}
-
-#if defined(_WIN32)
-
-static double GetNominalCPUFrequency() {
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
-    !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
-  // UWP apps don't have access to the registry and currently don't provide an
-  // API informing about CPU nominal frequency.
-  return 1.0;
-#else
-#pragma comment(lib, "advapi32.lib")  // For Reg* functions.
-  HKEY key;
-  // Use the Reg* functions rather than the SH functions because shlwapi.dll
-  // pulls in gdi32.dll which makes process destruction much more costly.
-  if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
-                    "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0,
-                    KEY_READ, &key) == ERROR_SUCCESS) {
-    DWORD type = 0;
-    DWORD data = 0;
-    DWORD data_size = sizeof(data);
-    auto result = RegQueryValueExA(key, "~MHz", 0, &type,
-                                   reinterpret_cast<LPBYTE>(&data), &data_size);
-    RegCloseKey(key);
-    if (result == ERROR_SUCCESS && type == REG_DWORD &&
-        data_size == sizeof(data)) {
-      return data * 1e6;  // Value is MHz.
-    }
-  }
-  return 1.0;
-#endif  // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
-}
-
-#elif defined(CTL_HW) && defined(HW_CPU_FREQ)
-
-static double GetNominalCPUFrequency() {
-  unsigned freq;
-  size_t size = sizeof(freq);
-  int mib[2] = {CTL_HW, HW_CPU_FREQ};
-  if (sysctl(mib, 2, &freq, &size, nullptr, 0) == 0) {
-    return static_cast<double>(freq);
-  }
-  return 1.0;
-}
-
-#else
-
-// Helper function for reading a long from a file. Returns true if successful
-// and the memory location pointed to by value is set to the value read.
-static bool ReadLongFromFile(const char *file, long *value) {
-  bool ret = false;
-  int fd = open(file, O_RDONLY);
-  if (fd != -1) {
-    char line[1024];
-    char *err;
-    memset(line, '\0', sizeof(line));
-    int len = read(fd, line, sizeof(line) - 1);
-    if (len <= 0) {
-      ret = false;
-    } else {
-      const long temp_value = strtol(line, &err, 10);
-      if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
-        *value = temp_value;
-        ret = true;
-      }
-    }
-    close(fd);
-  }
-  return ret;
-}
-
-#if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
-
-// Reads a monotonic time source and returns a value in
-// nanoseconds. The returned value uses an arbitrary epoch, not the
-// Unix epoch.
-static int64_t ReadMonotonicClockNanos() {
-  struct timespec t;
-#ifdef CLOCK_MONOTONIC_RAW
-  int rc = clock_gettime(CLOCK_MONOTONIC_RAW, &t);
-#else
-  int rc = clock_gettime(CLOCK_MONOTONIC, &t);
-#endif
-  if (rc != 0) {
-    perror("clock_gettime() failed");
-    abort();
-  }
-  return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec;
-}
-
-class UnscaledCycleClockWrapperForInitializeFrequency {
- public:
-  static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
-};
-
-struct TimeTscPair {
-  int64_t time;  // From ReadMonotonicClockNanos().
-  int64_t tsc;   // From UnscaledCycleClock::Now().
-};
-
-// Returns a pair of values (monotonic kernel time, TSC ticks) that
-// approximately correspond to each other.  This is accomplished by
-// doing several reads and picking the reading with the lowest
-// latency.  This approach is used to minimize the probability that
-// our thread was preempted between clock reads.
-static TimeTscPair GetTimeTscPair() {
-  int64_t best_latency = std::numeric_limits<int64_t>::max();
-  TimeTscPair best;
-  for (int i = 0; i < 10; ++i) {
-    int64_t t0 = ReadMonotonicClockNanos();
-    int64_t tsc = UnscaledCycleClockWrapperForInitializeFrequency::Now();
-    int64_t t1 = ReadMonotonicClockNanos();
-    int64_t latency = t1 - t0;
-    if (latency < best_latency) {
-      best_latency = latency;
-      best.time = t0;
-      best.tsc = tsc;
-    }
-  }
-  return best;
-}
-
-// Measures and returns the TSC frequency by taking a pair of
-// measurements approximately `sleep_nanoseconds` apart.
-static double MeasureTscFrequencyWithSleep(int sleep_nanoseconds) {
-  auto t0 = GetTimeTscPair();
-  struct timespec ts;
-  ts.tv_sec = 0;
-  ts.tv_nsec = sleep_nanoseconds;
-  while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {}
-  auto t1 = GetTimeTscPair();
-  double elapsed_ticks = t1.tsc - t0.tsc;
-  double elapsed_time = (t1.time - t0.time) * 1e-9;
-  return elapsed_ticks / elapsed_time;
-}
-
-// Measures and returns the TSC frequency by calling
-// MeasureTscFrequencyWithSleep(), doubling the sleep interval until the
-// frequency measurement stabilizes.
-static double MeasureTscFrequency() {
-  double last_measurement = -1.0;
-  int sleep_nanoseconds = 1000000;  // 1 millisecond.
-  for (int i = 0; i < 8; ++i) {
-    double measurement = MeasureTscFrequencyWithSleep(sleep_nanoseconds);
-    if (measurement * 0.99 < last_measurement &&
-        last_measurement < measurement * 1.01) {
-      // Use the current measurement if it is within 1% of the
-      // previous measurement.
-      return measurement;
-    }
-    last_measurement = measurement;
-    sleep_nanoseconds *= 2;
-  }
-  return last_measurement;
-}
-
-#endif  // ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
-
-static double GetNominalCPUFrequency() {
-  long freq = 0;
-
-  // Google's production kernel has a patch to export the TSC
-  // frequency through sysfs. If the kernel is exporting the TSC
-  // frequency use that. There are issues where cpuinfo_max_freq
-  // cannot be relied on because the BIOS may be exporting an invalid
-  // p-state (on x86) or p-states may be used to put the processor in
-  // a new mode (turbo mode). Essentially, those frequencies cannot
-  // always be relied upon. The same reasons apply to /proc/cpuinfo as
-  // well.
-  if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
-    return freq * 1e3;  // Value is kHz.
-  }
-
-#if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
-  // On these platforms, the TSC frequency is the nominal CPU
-  // frequency.  But without having the kernel export it directly
-  // though /sys/devices/system/cpu/cpu0/tsc_freq_khz, there is no
-  // other way to reliably get the TSC frequency, so we have to
-  // measure it ourselves.  Some CPUs abuse cpuinfo_max_freq by
-  // exporting "fake" frequencies for implementing new features. For
-  // example, Intel's turbo mode is enabled by exposing a p-state
-  // value with a higher frequency than that of the real TSC
-  // rate. Because of this, we prefer to measure the TSC rate
-  // ourselves on i386 and x86-64.
-  return MeasureTscFrequency();
-#else
-
-  // If CPU scaling is in effect, we want to use the *maximum*
-  // frequency, not whatever CPU speed some random processor happens
-  // to be using now.
-  if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
-                       &freq)) {
-    return freq * 1e3;  // Value is kHz.
-  }
-
-  return 1.0;
-#endif  // !ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
-}
-
-#endif
-
-ABSL_CONST_INIT static once_flag init_num_cpus_once;
-ABSL_CONST_INIT static int num_cpus = 0;
-
-// NumCPUs() may be called before main() and before malloc is properly
-// initialized, therefore this must not allocate memory.
-int NumCPUs() {
-  base_internal::LowLevelCallOnce(
-      &init_num_cpus_once, []() { num_cpus = GetNumCPUs(); });
-  return num_cpus;
-}
-
-// A default frequency of 0.0 might be dangerous if it is used in division.
-ABSL_CONST_INIT static once_flag init_nominal_cpu_frequency_once;
-ABSL_CONST_INIT static double nominal_cpu_frequency = 1.0;
-
-// NominalCPUFrequency() may be called before main() and before malloc is
-// properly initialized, therefore this must not allocate memory.
-double NominalCPUFrequency() {
-  base_internal::LowLevelCallOnce(
-      &init_nominal_cpu_frequency_once,
-      []() { nominal_cpu_frequency = GetNominalCPUFrequency(); });
-  return nominal_cpu_frequency;
-}
-
-#if defined(_WIN32)
-
-pid_t GetTID() {
-  return pid_t{GetCurrentThreadId()};
-}
-
-#elif defined(__linux__)
-
-#ifndef SYS_gettid
-#define SYS_gettid __NR_gettid
-#endif
-
-pid_t GetTID() {
-  return syscall(SYS_gettid);
-}
-
-#elif defined(__akaros__)
-
-pid_t GetTID() {
-  // Akaros has a concept of "vcore context", which is the state the program
-  // is forced into when we need to make a user-level scheduling decision, or
-  // run a signal handler.  This is analogous to the interrupt context that a
-  // CPU might enter if it encounters some kind of exception.
-  //
-  // There is no current thread context in vcore context, but we need to give
-  // a reasonable answer if asked for a thread ID (e.g., in a signal handler).
-  // Thread 0 always exists, so if we are in vcore context, we return that.
-  //
-  // Otherwise, we know (since we are using pthreads) that the uthread struct
-  // current_uthread is pointing to is the first element of a
-  // struct pthread_tcb, so we extract and return the thread ID from that.
-  //
-  // TODO(dcross): Akaros anticipates moving the thread ID to the uthread
-  // structure at some point. We should modify this code to remove the cast
-  // when that happens.
-  if (in_vcore_context())
-    return 0;
-  return reinterpret_cast<struct pthread_tcb *>(current_uthread)->id;
-}
-
-#elif defined(__myriad2__)
-
-pid_t GetTID() {
-  uint32_t tid;
-  rtems_task_ident(RTEMS_SELF, 0, &tid);
-  return tid;
-}
-
-#else
-
-// Fallback implementation of GetTID using pthread_getspecific.
-ABSL_CONST_INIT static once_flag tid_once;
-ABSL_CONST_INIT static pthread_key_t tid_key;
-ABSL_CONST_INIT static absl::base_internal::SpinLock tid_lock(
-    absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
-
-// We set a bit per thread in this array to indicate that an ID is in
-// use. ID 0 is unused because it is the default value returned by
-// pthread_getspecific().
-ABSL_CONST_INIT static std::vector<uint32_t> *tid_array
-    ABSL_GUARDED_BY(tid_lock) = nullptr;
-static constexpr int kBitsPerWord = 32;  // tid_array is uint32_t.
-
-// Returns the TID to tid_array.
-static void FreeTID(void *v) {
-  intptr_t tid = reinterpret_cast<intptr_t>(v);
-  int word = tid / kBitsPerWord;
-  uint32_t mask = ~(1u << (tid % kBitsPerWord));
-  absl::base_internal::SpinLockHolder lock(&tid_lock);
-  assert(0 <= word && static_cast<size_t>(word) < tid_array->size());
-  (*tid_array)[word] &= mask;
-}
-
-static void InitGetTID() {
-  if (pthread_key_create(&tid_key, FreeTID) != 0) {
-    // The logging system calls GetTID() so it can't be used here.
-    perror("pthread_key_create failed");
-    abort();
-  }
-
-  // Initialize tid_array.
-  absl::base_internal::SpinLockHolder lock(&tid_lock);
-  tid_array = new std::vector<uint32_t>(1);
-  (*tid_array)[0] = 1;  // ID 0 is never-allocated.
-}
-
-// Return a per-thread small integer ID from pthread's thread-specific data.
-pid_t GetTID() {
-  absl::call_once(tid_once, InitGetTID);
-
-  intptr_t tid = reinterpret_cast<intptr_t>(pthread_getspecific(tid_key));
-  if (tid != 0) {
-    return tid;
-  }
-
-  int bit;  // tid_array[word] = 1u << bit;
-  size_t word;
-  {
-    // Search for the first unused ID.
-    absl::base_internal::SpinLockHolder lock(&tid_lock);
-    // First search for a word in the array that is not all ones.
-    word = 0;
-    while (word < tid_array->size() && ~(*tid_array)[word] == 0) {
-      ++word;
-    }
-    if (word == tid_array->size()) {
-      tid_array->push_back(0);  // No space left, add kBitsPerWord more IDs.
-    }
-    // Search for a zero bit in the word.
-    bit = 0;
-    while (bit < kBitsPerWord && (((*tid_array)[word] >> bit) & 1) != 0) {
-      ++bit;
-    }
-    tid = (word * kBitsPerWord) + bit;
-    (*tid_array)[word] |= 1u << bit;  // Mark the TID as allocated.
-  }
-
-  if (pthread_setspecific(tid_key, reinterpret_cast<void *>(tid)) != 0) {
-    perror("pthread_setspecific failed");
-    abort();
-  }
-
-  return static_cast<pid_t>(tid);
-}
-
-#endif
-
-// GetCachedTID() caches the thread ID in thread-local storage (which is a
-// userspace construct) to avoid unnecessary system calls. Without this caching,
-// it can take roughly 98ns, while it takes roughly 1ns with this caching.
-pid_t GetCachedTID() {
-#ifdef ABSL_HAVE_THREAD_LOCAL
-  static thread_local pid_t thread_id = GetTID();
-  return thread_id;
-#else
-  return GetTID();
-#endif  // ABSL_HAVE_THREAD_LOCAL
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/sysinfo.h b/third_party/abseil_cpp/absl/base/internal/sysinfo.h
deleted file mode 100644
index 119cf1f0e8..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/sysinfo.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This file includes routines to find out characteristics
-// of the machine a program is running on.  It is undoubtedly
-// system-dependent.
-
-// Functions listed here that accept a pid_t as an argument act on the
-// current process if the pid_t argument is 0
-// All functions here are thread-hostile due to file caching unless
-// commented otherwise.
-
-#ifndef ABSL_BASE_INTERNAL_SYSINFO_H_
-#define ABSL_BASE_INTERNAL_SYSINFO_H_
-
-#ifndef _WIN32
-#include <sys/types.h>
-#endif
-
-#include <cstdint>
-
-#include "absl/base/config.h"
-#include "absl/base/port.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// Nominal core processor cycles per second of each processor.   This is _not_
-// necessarily the frequency of the CycleClock counter (see cycleclock.h)
-// Thread-safe.
-double NominalCPUFrequency();
-
-// Number of logical processors (hyperthreads) in system. Thread-safe.
-int NumCPUs();
-
-// Return the thread id of the current thread, as told by the system.
-// No two currently-live threads implemented by the OS shall have the same ID.
-// Thread ids of exited threads may be reused.   Multiple user-level threads
-// may have the same thread ID if multiplexed on the same OS thread.
-//
-// On Linux, you may send a signal to the resulting ID with kill().  However,
-// it is recommended for portability that you use pthread_kill() instead.
-#ifdef _WIN32
-// On Windows, process id and thread id are of the same type according to the
-// return types of GetProcessId() and GetThreadId() are both DWORD, an unsigned
-// 32-bit type.
-using pid_t = uint32_t;
-#endif
-pid_t GetTID();
-
-// Like GetTID(), but caches the result in thread-local storage in order
-// to avoid unnecessary system calls. Note that there are some cases where
-// one must call through to GetTID directly, which is why this exists as a
-// separate function. For example, GetCachedTID() is not safe to call in
-// an asynchronous signal-handling context nor right after a call to fork().
-pid_t GetCachedTID();
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_SYSINFO_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/sysinfo_test.cc b/third_party/abseil_cpp/absl/base/internal/sysinfo_test.cc
deleted file mode 100644
index fa8b88b1dc..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/sysinfo_test.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/sysinfo.h"
-
-#ifndef _WIN32
-#include <sys/types.h>
-#include <unistd.h>
-#endif
-
-#include <thread>  // NOLINT(build/c++11)
-#include <unordered_set>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/synchronization/barrier.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-namespace {
-
-TEST(SysinfoTest, NumCPUs) {
-  EXPECT_NE(NumCPUs(), 0)
-      << "NumCPUs() should not have the default value of 0";
-}
-
-TEST(SysinfoTest, NominalCPUFrequency) {
-#if !(defined(__aarch64__) && defined(__linux__)) && !defined(__EMSCRIPTEN__)
-  EXPECT_GE(NominalCPUFrequency(), 1000.0)
-      << "NominalCPUFrequency() did not return a reasonable value";
-#else
-  // Aarch64 cannot read the CPU frequency from sysfs, so we get back 1.0.
-  // Emscripten does not have a sysfs to read from at all.
-  EXPECT_EQ(NominalCPUFrequency(), 1.0)
-      << "CPU frequency detection was fixed! Please update unittest.";
-#endif
-}
-
-TEST(SysinfoTest, GetTID) {
-  EXPECT_EQ(GetTID(), GetTID());  // Basic compile and equality test.
-#ifdef __native_client__
-  // Native Client has a race condition bug that leads to memory
-  // exaustion when repeatedly creating and joining threads.
-  // https://bugs.chromium.org/p/nativeclient/issues/detail?id=1027
-  return;
-#endif
-  // Test that TIDs are unique to each thread.
-  // Uses a few loops to exercise implementations that reallocate IDs.
-  for (int i = 0; i < 10; ++i) {
-    constexpr int kNumThreads = 10;
-    Barrier all_threads_done(kNumThreads);
-    std::vector<std::thread> threads;
-
-    Mutex mutex;
-    std::unordered_set<pid_t> tids;
-
-    for (int j = 0; j < kNumThreads; ++j) {
-      threads.push_back(std::thread([&]() {
-        pid_t id = GetTID();
-        {
-          MutexLock lock(&mutex);
-          ASSERT_TRUE(tids.find(id) == tids.end());
-          tids.insert(id);
-        }
-        // We can't simply join the threads here. The threads need to
-        // be alive otherwise the TID might have been reallocated to
-        // another live thread.
-        all_threads_done.Block();
-      }));
-    }
-    for (auto& thread : threads) {
-      thread.join();
-    }
-  }
-}
-
-#ifdef __linux__
-TEST(SysinfoTest, LinuxGetTID) {
-  // On Linux, for the main thread, GetTID()==getpid() is guaranteed by the API.
-  EXPECT_EQ(GetTID(), getpid());
-}
-#endif
-
-}  // namespace
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/thread_annotations.h b/third_party/abseil_cpp/absl/base/internal/thread_annotations.h
deleted file mode 100644
index 4dab6a9c15..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/thread_annotations.h
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: thread_annotations.h
-// -----------------------------------------------------------------------------
-//
-// WARNING: This is a backwards compatible header and it will be removed after
-// the migration to prefixed thread annotations is finished; please include
-// "absl/base/thread_annotations.h".
-//
-// This header file contains macro definitions for thread safety annotations
-// that allow developers to document the locking policies of multi-threaded
-// code. The annotations can also help program analysis tools to identify
-// potential thread safety issues.
-//
-// These annotations are implemented using compiler attributes. Using the macros
-// defined here instead of raw attributes allow for portability and future
-// compatibility.
-//
-// When referring to mutexes in the arguments of the attributes, you should
-// use variable names or more complex expressions (e.g. my_object->mutex_)
-// that evaluate to a concrete mutex object whenever possible. If the mutex
-// you want to refer to is not in scope, you may use a member pointer
-// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object.
-
-#ifndef ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_
-#define ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_
-
-#if defined(__clang__)
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
-#else
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   // no-op
-#endif
-
-// GUARDED_BY()
-//
-// Documents if a shared field or global variable needs to be protected by a
-// mutex. GUARDED_BY() allows the user to specify a particular mutex that
-// should be held when accessing the annotated variable.
-//
-// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to
-// local variables, a local variable and its associated mutex can often be
-// combined into a small class or struct, thereby allowing the annotation.
-//
-// Example:
-//
-//   class Foo {
-//     Mutex mu_;
-//     int p1_ GUARDED_BY(mu_);
-//     ...
-//   };
-#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
-
-// PT_GUARDED_BY()
-//
-// Documents if the memory location pointed to by a pointer should be guarded
-// by a mutex when dereferencing the pointer.
-//
-// Example:
-//   class Foo {
-//     Mutex mu_;
-//     int *p1_ PT_GUARDED_BY(mu_);
-//     ...
-//   };
-//
-// Note that a pointer variable to a shared memory location could itself be a
-// shared variable.
-//
-// Example:
-//
-//   // `q_`, guarded by `mu1_`, points to a shared memory location that is
-//   // guarded by `mu2_`:
-//   int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_);
-#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
-
-// ACQUIRED_AFTER() / ACQUIRED_BEFORE()
-//
-// Documents the acquisition order between locks that can be held
-// simultaneously by a thread. For any two locks that need to be annotated
-// to establish an acquisition order, only one of them needs the annotation.
-// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
-// and ACQUIRED_BEFORE.)
-//
-// As with GUARDED_BY, this is only applicable to mutexes that are shared
-// fields or global variables.
-//
-// Example:
-//
-//   Mutex m1_;
-//   Mutex m2_ ACQUIRED_AFTER(m1_);
-#define ACQUIRED_AFTER(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
-
-#define ACQUIRED_BEFORE(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
-
-// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED()
-//
-// Documents a function that expects a mutex to be held prior to entry.
-// The mutex is expected to be held both on entry to, and exit from, the
-// function.
-//
-// An exclusive lock allows read-write access to the guarded data member(s), and
-// only one thread can acquire a lock exclusively at any one time. A shared lock
-// allows read-only access, and any number of threads can acquire a shared lock
-// concurrently.
-//
-// Generally, non-const methods should be annotated with
-// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with
-// SHARED_LOCKS_REQUIRED.
-//
-// Example:
-//
-//   Mutex mu1, mu2;
-//   int a GUARDED_BY(mu1);
-//   int b GUARDED_BY(mu2);
-//
-//   void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
-//   void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
-#define EXCLUSIVE_LOCKS_REQUIRED(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
-
-#define SHARED_LOCKS_REQUIRED(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
-
-// LOCKS_EXCLUDED()
-//
-// Documents the locks acquired in the body of the function. These locks
-// cannot be held when calling this function (as Abseil's `Mutex` locks are
-// non-reentrant).
-#define LOCKS_EXCLUDED(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
-
-// LOCK_RETURNED()
-//
-// Documents a function that returns a mutex without acquiring it.  For example,
-// a public getter method that returns a pointer to a private mutex should
-// be annotated with LOCK_RETURNED.
-#define LOCK_RETURNED(x) \
-  THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
-
-// LOCKABLE
-//
-// Documents if a class/type is a lockable type (such as the `Mutex` class).
-#define LOCKABLE \
-  THREAD_ANNOTATION_ATTRIBUTE__(lockable)
-
-// SCOPED_LOCKABLE
-//
-// Documents if a class does RAII locking (such as the `MutexLock` class).
-// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is
-// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no
-// arguments; the analysis will assume that the destructor unlocks whatever the
-// constructor locked.
-#define SCOPED_LOCKABLE \
-  THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
-
-// EXCLUSIVE_LOCK_FUNCTION()
-//
-// Documents functions that acquire a lock in the body of a function, and do
-// not release it.
-#define EXCLUSIVE_LOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
-
-// SHARED_LOCK_FUNCTION()
-//
-// Documents functions that acquire a shared (reader) lock in the body of a
-// function, and do not release it.
-#define SHARED_LOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
-
-// UNLOCK_FUNCTION()
-//
-// Documents functions that expect a lock to be held on entry to the function,
-// and release it in the body of the function.
-#define UNLOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
-
-// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION()
-//
-// Documents functions that try to acquire a lock, and return success or failure
-// (or a non-boolean value that can be interpreted as a boolean).
-// The first argument should be `true` for functions that return `true` on
-// success, or `false` for functions that return `false` on success. The second
-// argument specifies the mutex that is locked on success. If unspecified, this
-// mutex is assumed to be `this`.
-#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
-
-#define SHARED_TRYLOCK_FUNCTION(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
-
-// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK()
-//
-// Documents functions that dynamically check to see if a lock is held, and fail
-// if it is not held.
-#define ASSERT_EXCLUSIVE_LOCK(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
-
-#define ASSERT_SHARED_LOCK(...) \
-  THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
-
-// NO_THREAD_SAFETY_ANALYSIS
-//
-// Turns off thread safety checking within the body of a particular function.
-// This annotation is used to mark functions that are known to be correct, but
-// the locking behavior is more complicated than the analyzer can handle.
-#define NO_THREAD_SAFETY_ANALYSIS \
-  THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-
-//------------------------------------------------------------------------------
-// Tool-Supplied Annotations
-//------------------------------------------------------------------------------
-
-// TS_UNCHECKED should be placed around lock expressions that are not valid
-// C++ syntax, but which are present for documentation purposes.  These
-// annotations will be ignored by the analysis.
-#define TS_UNCHECKED(x) ""
-
-// TS_FIXME is used to mark lock expressions that are not valid C++ syntax.
-// It is used by automated tools to mark and disable invalid expressions.
-// The annotation should either be fixed, or changed to TS_UNCHECKED.
-#define TS_FIXME(x) ""
-
-// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of
-// a particular function.  However, this attribute is used to mark functions
-// that are incorrect and need to be fixed.  It is used by automated tools to
-// avoid breaking the build when the analysis is updated.
-// Code owners are expected to eventually fix the routine.
-#define NO_THREAD_SAFETY_ANALYSIS_FIXME  NO_THREAD_SAFETY_ANALYSIS
-
-// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY
-// annotation that needs to be fixed, because it is producing thread safety
-// warning.  It disables the GUARDED_BY.
-#define GUARDED_BY_FIXME(x)
-
-// Disables warnings for a single read operation.  This can be used to avoid
-// warnings when it is known that the read is not actually involved in a race,
-// but the compiler cannot confirm that.
-#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x)
-
-
-namespace thread_safety_analysis {
-
-// Takes a reference to a guarded data member, and returns an unguarded
-// reference.
-template <typename T>
-inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS {
-  return v;
-}
-
-template <typename T>
-inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS {
-  return v;
-}
-
-}  // namespace thread_safety_analysis
-
-#endif  // ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/thread_identity.cc b/third_party/abseil_cpp/absl/base/internal/thread_identity.cc
deleted file mode 100644
index 6ea010ed0d..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/thread_identity.cc
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/thread_identity.h"
-
-#ifndef _WIN32
-#include <pthread.h>
-#include <signal.h>
-#endif
-
-#include <atomic>
-#include <cassert>
-#include <memory>
-
-#include "absl/base/attributes.h"
-#include "absl/base/call_once.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/spinlock.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-#if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-namespace {
-// Used to co-ordinate one-time creation of our pthread_key
-absl::once_flag init_thread_identity_key_once;
-pthread_key_t thread_identity_pthread_key;
-std::atomic<bool> pthread_key_initialized(false);
-
-void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) {
-  pthread_key_create(&thread_identity_pthread_key, reclaimer);
-  pthread_key_initialized.store(true, std::memory_order_release);
-}
-}  // namespace
-#endif
-
-#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \
-    ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-// The actual TLS storage for a thread's currently associated ThreadIdentity.
-// This is referenced by inline accessors in the header.
-// "protected" visibility ensures that if multiple instances of Abseil code
-// exist within a process (via dlopen() or similar), references to
-// thread_identity_ptr from each instance of the code will refer to
-// *different* instances of this ptr.
-// Apple platforms have the visibility attribute, but issue a compile warning
-// that protected visibility is unsupported.
-#if ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__)
-__attribute__((visibility("protected")))
-#endif  // ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__)
-#if ABSL_PER_THREAD_TLS
-// Prefer __thread to thread_local as benchmarks indicate it is a bit faster.
-ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr = nullptr;
-#elif defined(ABSL_HAVE_THREAD_LOCAL)
-thread_local ThreadIdentity* thread_identity_ptr = nullptr;
-#endif  // ABSL_PER_THREAD_TLS
-#endif  // TLS or CPP11
-
-void SetCurrentThreadIdentity(
-    ThreadIdentity* identity, ThreadIdentityReclaimerFunction reclaimer) {
-  assert(CurrentThreadIdentityIfPresent() == nullptr);
-  // Associate our destructor.
-  // NOTE: This call to pthread_setspecific is currently the only immovable
-  // barrier to CurrentThreadIdentity() always being async signal safe.
-#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
-  // NOTE: Not async-safe.  But can be open-coded.
-  absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
-                  reclaimer);
-
-#if defined(__EMSCRIPTEN__) || defined(__MINGW32__)
-  // Emscripten and MinGW pthread implementations does not support signals.
-  // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
-  // for more information.
-  pthread_setspecific(thread_identity_pthread_key,
-                      reinterpret_cast<void*>(identity));
-#else
-  // We must mask signals around the call to setspecific as with current glibc,
-  // a concurrent getspecific (needed for GetCurrentThreadIdentityIfPresent())
-  // may zero our value.
-  //
-  // While not officially async-signal safe, getspecific within a signal handler
-  // is otherwise OK.
-  sigset_t all_signals;
-  sigset_t curr_signals;
-  sigfillset(&all_signals);
-  pthread_sigmask(SIG_SETMASK, &all_signals, &curr_signals);
-  pthread_setspecific(thread_identity_pthread_key,
-                      reinterpret_cast<void*>(identity));
-  pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr);
-#endif  // !__EMSCRIPTEN__ && !__MINGW32__
-
-#elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS
-  // NOTE: Not async-safe.  But can be open-coded.
-  absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
-                  reclaimer);
-  pthread_setspecific(thread_identity_pthread_key,
-                      reinterpret_cast<void*>(identity));
-  thread_identity_ptr = identity;
-#elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-  thread_local std::unique_ptr<ThreadIdentity, ThreadIdentityReclaimerFunction>
-      holder(identity, reclaimer);
-  thread_identity_ptr = identity;
-#else
-#error Unimplemented ABSL_THREAD_IDENTITY_MODE
-#endif
-}
-
-#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \
-    ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-
-// Please see the comment on `CurrentThreadIdentityIfPresent` in
-// thread_identity.h. Because DLLs cannot expose thread_local variables in
-// headers, we opt for the correct-but-slower option of placing the definition
-// of this function only in a translation unit inside DLL.
-#if defined(ABSL_BUILD_DLL) || defined(ABSL_CONSUME_DLL)
-ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; }
-#endif
-#endif
-
-void ClearCurrentThreadIdentity() {
-#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \
-    ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-  thread_identity_ptr = nullptr;
-#elif ABSL_THREAD_IDENTITY_MODE == \
-      ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
-  // pthread_setspecific expected to clear value on destruction
-  assert(CurrentThreadIdentityIfPresent() == nullptr);
-#endif
-}
-
-#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
-ThreadIdentity* CurrentThreadIdentityIfPresent() {
-  bool initialized = pthread_key_initialized.load(std::memory_order_acquire);
-  if (!initialized) {
-    return nullptr;
-  }
-  return reinterpret_cast<ThreadIdentity*>(
-      pthread_getspecific(thread_identity_pthread_key));
-}
-#endif
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/thread_identity.h b/third_party/abseil_cpp/absl/base/internal/thread_identity.h
deleted file mode 100644
index d2a65fd811..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/thread_identity.h
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Each active thread has an ThreadIdentity that may represent the thread in
-// various level interfaces.  ThreadIdentity objects are never deallocated.
-// When a thread terminates, its ThreadIdentity object may be reused for a
-// thread created later.
-
-#ifndef ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_
-#define ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_
-
-#ifndef _WIN32
-#include <pthread.h>
-// Defines __GOOGLE_GRTE_VERSION__ (via glibc-specific features.h) when
-// supported.
-#include <unistd.h>
-#endif
-
-#include <atomic>
-#include <cstdint>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/per_thread_tls.h"
-#include "absl/base/optimization.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-struct SynchLocksHeld;
-struct SynchWaitParams;
-
-namespace base_internal {
-
-class SpinLock;
-struct ThreadIdentity;
-
-// Used by the implementation of absl::Mutex and absl::CondVar.
-struct PerThreadSynch {
-  // The internal representation of absl::Mutex and absl::CondVar rely
-  // on the alignment of PerThreadSynch. Both store the address of the
-  // PerThreadSynch in the high-order bits of their internal state,
-  // which means the low kLowZeroBits of the address of PerThreadSynch
-  // must be zero.
-  static constexpr int kLowZeroBits = 8;
-  static constexpr int kAlignment = 1 << kLowZeroBits;
-
-  // Returns the associated ThreadIdentity.
-  // This can be implemented as a cast because we guarantee
-  // PerThreadSynch is the first element of ThreadIdentity.
-  ThreadIdentity* thread_identity() {
-    return reinterpret_cast<ThreadIdentity*>(this);
-  }
-
-  PerThreadSynch *next;  // Circular waiter queue; initialized to 0.
-  PerThreadSynch *skip;  // If non-zero, all entries in Mutex queue
-                         // up to and including "skip" have same
-                         // condition as this, and will be woken later
-  bool may_skip;         // if false while on mutex queue, a mutex unlocker
-                         // is using this PerThreadSynch as a terminator.  Its
-                         // skip field must not be filled in because the loop
-                         // might then skip over the terminator.
-  bool wake;             // This thread is to be woken from a Mutex.
-  // If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the
-  // waiter is waiting on the mutex as part of a CV Wait or Mutex Await.
-  //
-  // The value of "x->cond_waiter" is meaningless if "x" is not on a
-  // Mutex waiter list.
-  bool cond_waiter;
-  bool maybe_unlocking;  // Valid at head of Mutex waiter queue;
-                         // true if UnlockSlow could be searching
-                         // for a waiter to wake.  Used for an optimization
-                         // in Enqueue().  true is always a valid value.
-                         // Can be reset to false when the unlocker or any
-                         // writer releases the lock, or a reader fully
-                         // releases the lock.  It may not be set to false
-                         // by a reader that decrements the count to
-                         // non-zero. protected by mutex spinlock
-  bool suppress_fatal_errors;  // If true, try to proceed even in the face
-                               // of broken invariants.  This is used within
-                               // fatal signal handlers to improve the
-                               // chances of debug logging information being
-                               // output successfully.
-  int priority;                // Priority of thread (updated every so often).
-
-  // State values:
-  //   kAvailable: This PerThreadSynch is available.
-  //   kQueued: This PerThreadSynch is unavailable, it's currently queued on a
-  //            Mutex or CondVar waistlist.
-  //
-  // Transitions from kQueued to kAvailable require a release
-  // barrier. This is needed as a waiter may use "state" to
-  // independently observe that it's no longer queued.
-  //
-  // Transitions from kAvailable to kQueued require no barrier, they
-  // are externally ordered by the Mutex.
-  enum State {
-    kAvailable,
-    kQueued
-  };
-  std::atomic<State> state;
-
-  // The wait parameters of the current wait.  waitp is null if the
-  // thread is not waiting. Transitions from null to non-null must
-  // occur before the enqueue commit point (state = kQueued in
-  // Enqueue() and CondVarEnqueue()). Transitions from non-null to
-  // null must occur after the wait is finished (state = kAvailable in
-  // Mutex::Block() and CondVar::WaitCommon()). This field may be
-  // changed only by the thread that describes this PerThreadSynch.  A
-  // special case is Fer(), which calls Enqueue() on another thread,
-  // but with an identical SynchWaitParams pointer, thus leaving the
-  // pointer unchanged.
-  SynchWaitParams* waitp;
-
-  intptr_t readers;     // Number of readers in mutex.
-
-  // When priority will next be read (cycles).
-  int64_t next_priority_read_cycles;
-
-  // Locks held; used during deadlock detection.
-  // Allocated in Synch_GetAllLocks() and freed in ReclaimThreadIdentity().
-  SynchLocksHeld *all_locks;
-};
-
-// The instances of this class are allocated in NewThreadIdentity() with an
-// alignment of PerThreadSynch::kAlignment.
-struct ThreadIdentity {
-  // Must be the first member.  The Mutex implementation requires that
-  // the PerThreadSynch object associated with each thread is
-  // PerThreadSynch::kAlignment aligned.  We provide this alignment on
-  // ThreadIdentity itself.
-  PerThreadSynch per_thread_synch;
-
-  // Private: Reserved for absl::synchronization_internal::Waiter.
-  struct WaiterState {
-    char data[128];
-  } waiter_state;
-
-  // Used by PerThreadSem::{Get,Set}ThreadBlockedCounter().
-  std::atomic<int>* blocked_count_ptr;
-
-  // The following variables are mostly read/written just by the
-  // thread itself.  The only exception is that these are read by
-  // a ticker thread as a hint.
-  std::atomic<int> ticker;      // Tick counter, incremented once per second.
-  std::atomic<int> wait_start;  // Ticker value when thread started waiting.
-  std::atomic<bool> is_idle;    // Has thread become idle yet?
-
-  ThreadIdentity* next;
-};
-
-// Returns the ThreadIdentity object representing the calling thread; guaranteed
-// to be unique for its lifetime.  The returned object will remain valid for the
-// program's lifetime; although it may be re-assigned to a subsequent thread.
-// If one does not exist, return nullptr instead.
-//
-// Does not malloc(*), and is async-signal safe.
-// [*] Technically pthread_setspecific() does malloc on first use; however this
-// is handled internally within tcmalloc's initialization already.
-//
-// New ThreadIdentity objects can be constructed and associated with a thread
-// by calling GetOrCreateCurrentThreadIdentity() in per-thread-sem.h.
-ThreadIdentity* CurrentThreadIdentityIfPresent();
-
-using ThreadIdentityReclaimerFunction = void (*)(void*);
-
-// Sets the current thread identity to the given value.  'reclaimer' is a
-// pointer to the global function for cleaning up instances on thread
-// destruction.
-void SetCurrentThreadIdentity(ThreadIdentity* identity,
-                              ThreadIdentityReclaimerFunction reclaimer);
-
-// Removes the currently associated ThreadIdentity from the running thread.
-// This must be called from inside the ThreadIdentityReclaimerFunction, and only
-// from that function.
-void ClearCurrentThreadIdentity();
-
-// May be chosen at compile time via: -DABSL_FORCE_THREAD_IDENTITY_MODE=<mode
-// index>
-#ifdef ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
-#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be direcly set
-#else
-#define ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 0
-#endif
-
-#ifdef ABSL_THREAD_IDENTITY_MODE_USE_TLS
-#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be direcly set
-#else
-#define ABSL_THREAD_IDENTITY_MODE_USE_TLS 1
-#endif
-
-#ifdef ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be direcly set
-#else
-#define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2
-#endif
-
-#ifdef ABSL_THREAD_IDENTITY_MODE
-#error ABSL_THREAD_IDENTITY_MODE cannot be direcly set
-#elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE)
-#define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE
-#elif defined(_WIN32) && !defined(__MINGW32__)
-#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-#elif defined(__APPLE__) && defined(ABSL_HAVE_THREAD_LOCAL)
-#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-#elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) &&        \
-    (__GOOGLE_GRTE_VERSION__ >= 20140228L)
-// Support for async-safe TLS was specifically added in GRTEv4.  It's not
-// present in the upstream eglibc.
-// Note:  Current default for production systems.
-#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_TLS
-#else
-#define ABSL_THREAD_IDENTITY_MODE \
-  ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
-#endif
-
-#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \
-    ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-
-#if ABSL_PER_THREAD_TLS
-ABSL_CONST_INIT extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity*
-    thread_identity_ptr;
-#elif defined(ABSL_HAVE_THREAD_LOCAL)
-ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr;
-#else
-#error Thread-local storage not detected on this platform
-#endif
-
-// thread_local variables cannot be in headers exposed by DLLs. However, it is
-// important for performance reasons in general that
-// `CurrentThreadIdentityIfPresent` be inlined. This is not possible across a
-// DLL boundary so, with DLLs, we opt to have the function not be inlined. Note
-// that `CurrentThreadIdentityIfPresent` is declared above so we can exclude
-// this entire inline definition when compiling as a DLL.
-#if !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL)
-inline ThreadIdentity* CurrentThreadIdentityIfPresent() {
-  return thread_identity_ptr;
-}
-#endif
-
-#elif ABSL_THREAD_IDENTITY_MODE != \
-    ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
-#error Unknown ABSL_THREAD_IDENTITY_MODE
-#endif
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/thread_identity_benchmark.cc b/third_party/abseil_cpp/absl/base/internal/thread_identity_benchmark.cc
deleted file mode 100644
index 0ae10f2b1e..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/thread_identity_benchmark.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "benchmark/benchmark.h"
-#include "absl/base/internal/thread_identity.h"
-#include "absl/synchronization/internal/create_thread_identity.h"
-#include "absl/synchronization/internal/per_thread_sem.h"
-
-namespace {
-
-void BM_SafeCurrentThreadIdentity(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::synchronization_internal::GetOrCreateCurrentThreadIdentity());
-  }
-}
-BENCHMARK(BM_SafeCurrentThreadIdentity);
-
-void BM_UnsafeCurrentThreadIdentity(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::base_internal::CurrentThreadIdentityIfPresent());
-  }
-}
-BENCHMARK(BM_UnsafeCurrentThreadIdentity);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/thread_identity_test.cc b/third_party/abseil_cpp/absl/base/internal/thread_identity_test.cc
deleted file mode 100644
index 46a6f74346..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/thread_identity_test.cc
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/thread_identity.h"
-
-#include <thread>  // NOLINT(build/c++11)
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/base/macros.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/synchronization/internal/per_thread_sem.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-namespace {
-
-ABSL_CONST_INIT static absl::base_internal::SpinLock map_lock(
-    absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
-ABSL_CONST_INIT static int num_identities_reused ABSL_GUARDED_BY(map_lock);
-
-static const void* const kCheckNoIdentity = reinterpret_cast<void*>(1);
-
-static void TestThreadIdentityCurrent(const void* assert_no_identity) {
-  ThreadIdentity* identity;
-
-  // We have to test this conditionally, because if the test framework relies
-  // on Abseil, then some previous action may have already allocated an
-  // identity.
-  if (assert_no_identity == kCheckNoIdentity) {
-    identity = CurrentThreadIdentityIfPresent();
-    EXPECT_TRUE(identity == nullptr);
-  }
-
-  identity = synchronization_internal::GetOrCreateCurrentThreadIdentity();
-  EXPECT_TRUE(identity != nullptr);
-  ThreadIdentity* identity_no_init;
-  identity_no_init = CurrentThreadIdentityIfPresent();
-  EXPECT_TRUE(identity == identity_no_init);
-
-  // Check that per_thread_synch is correctly aligned.
-  EXPECT_EQ(0, reinterpret_cast<intptr_t>(&identity->per_thread_synch) %
-                   PerThreadSynch::kAlignment);
-  EXPECT_EQ(identity, identity->per_thread_synch.thread_identity());
-
-  absl::base_internal::SpinLockHolder l(&map_lock);
-  num_identities_reused++;
-}
-
-TEST(ThreadIdentityTest, BasicIdentityWorks) {
-  // This tests for the main() thread.
-  TestThreadIdentityCurrent(nullptr);
-}
-
-TEST(ThreadIdentityTest, BasicIdentityWorksThreaded) {
-  // Now try the same basic test with multiple threads being created and
-  // destroyed.  This makes sure that:
-  // - New threads are created without a ThreadIdentity.
-  // - We re-allocate ThreadIdentity objects from the free-list.
-  // - If a thread implementation chooses to recycle threads, that
-  //   correct re-initialization occurs.
-  static const int kNumLoops = 3;
-  static const int kNumThreads = 32;
-  for (int iter = 0; iter < kNumLoops; iter++) {
-    std::vector<std::thread> threads;
-    for (int i = 0; i < kNumThreads; ++i) {
-      threads.push_back(
-          std::thread(TestThreadIdentityCurrent, kCheckNoIdentity));
-    }
-    for (auto& thread : threads) {
-      thread.join();
-    }
-  }
-
-  // We should have recycled ThreadIdentity objects above; while (external)
-  // library threads allocating their own identities may preclude some
-  // reuse, we should have sufficient repetitions to exclude this.
-  absl::base_internal::SpinLockHolder l(&map_lock);
-  EXPECT_LT(kNumThreads, num_identities_reused);
-}
-
-TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) {
-  // This test repeatly creates and joins a series of threads, each of
-  // which acquires and releases shared Mutex locks. This verifies
-  // Mutex operations work correctly under a reused
-  // ThreadIdentity. Note that the most likely failure mode of this
-  // test is a crash or deadlock.
-  static const int kNumLoops = 10;
-  static const int kNumThreads = 12;
-  static const int kNumMutexes = 3;
-  static const int kNumLockLoops = 5;
-
-  Mutex mutexes[kNumMutexes];
-  for (int iter = 0; iter < kNumLoops; ++iter) {
-    std::vector<std::thread> threads;
-    for (int thread = 0; thread < kNumThreads; ++thread) {
-      threads.push_back(std::thread([&]() {
-        for (int l = 0; l < kNumLockLoops; ++l) {
-          for (int m = 0; m < kNumMutexes; ++m) {
-            MutexLock lock(&mutexes[m]);
-          }
-        }
-      }));
-    }
-    for (auto& thread : threads) {
-      thread.join();
-    }
-  }
-}
-
-}  // namespace
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/throw_delegate.cc b/third_party/abseil_cpp/absl/base/internal/throw_delegate.cc
deleted file mode 100644
index c260ff1eed..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/throw_delegate.cc
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/throw_delegate.h"
-
-#include <cstdlib>
-#include <functional>
-#include <new>
-#include <stdexcept>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// NOTE: The various STL exception throwing functions are placed within the
-// #ifdef blocks so the symbols aren't exposed on platforms that don't support
-// them, such as the Android NDK. For example, ANGLE fails to link when building
-// within AOSP without them, since the STL functions don't exist.
-namespace {
-#ifdef ABSL_HAVE_EXCEPTIONS
-template <typename T>
-[[noreturn]] void Throw(const T& error) {
-  throw error;
-}
-#endif
-}  // namespace
-
-void ThrowStdLogicError(const std::string& what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::logic_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
-  std::abort();
-#endif
-}
-void ThrowStdLogicError(const char* what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::logic_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg);
-  std::abort();
-#endif
-}
-void ThrowStdInvalidArgument(const std::string& what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::invalid_argument(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
-  std::abort();
-#endif
-}
-void ThrowStdInvalidArgument(const char* what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::invalid_argument(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg);
-  std::abort();
-#endif
-}
-
-void ThrowStdDomainError(const std::string& what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::domain_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
-  std::abort();
-#endif
-}
-void ThrowStdDomainError(const char* what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::domain_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg);
-  std::abort();
-#endif
-}
-
-void ThrowStdLengthError(const std::string& what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::length_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
-  std::abort();
-#endif
-}
-void ThrowStdLengthError(const char* what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::length_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg);
-  std::abort();
-#endif
-}
-
-void ThrowStdOutOfRange(const std::string& what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::out_of_range(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
-  std::abort();
-#endif
-}
-void ThrowStdOutOfRange(const char* what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::out_of_range(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg);
-  std::abort();
-#endif
-}
-
-void ThrowStdRuntimeError(const std::string& what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::runtime_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
-  std::abort();
-#endif
-}
-void ThrowStdRuntimeError(const char* what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::runtime_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg);
-  std::abort();
-#endif
-}
-
-void ThrowStdRangeError(const std::string& what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::range_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
-  std::abort();
-#endif
-}
-void ThrowStdRangeError(const char* what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::range_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg);
-  std::abort();
-#endif
-}
-
-void ThrowStdOverflowError(const std::string& what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::overflow_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
-  std::abort();
-#endif
-}
-void ThrowStdOverflowError(const char* what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::overflow_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg);
-  std::abort();
-#endif
-}
-
-void ThrowStdUnderflowError(const std::string& what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::underflow_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str());
-  std::abort();
-#endif
-}
-void ThrowStdUnderflowError(const char* what_arg) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::underflow_error(what_arg));
-#else
-  ABSL_RAW_LOG(FATAL, "%s", what_arg);
-  std::abort();
-#endif
-}
-
-void ThrowStdBadFunctionCall() {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::bad_function_call());
-#else
-  std::abort();
-#endif
-}
-
-void ThrowStdBadAlloc() {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  Throw(std::bad_alloc());
-#else
-  std::abort();
-#endif
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/internal/throw_delegate.h b/third_party/abseil_cpp/absl/base/internal/throw_delegate.h
deleted file mode 100644
index 075f527254..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/throw_delegate.h
+++ /dev/null
@@ -1,75 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_BASE_INTERNAL_THROW_DELEGATE_H_
-#define ABSL_BASE_INTERNAL_THROW_DELEGATE_H_
-
-#include <string>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// Helper functions that allow throwing exceptions consistently from anywhere.
-// The main use case is for header-based libraries (eg templates), as they will
-// be built by many different targets with their own compiler options.
-// In particular, this will allow a safe way to throw exceptions even if the
-// caller is compiled with -fno-exceptions.  This is intended for implementing
-// things like map<>::at(), which the standard documents as throwing an
-// exception on error.
-//
-// Using other techniques like #if tricks could lead to ODR violations.
-//
-// You shouldn't use it unless you're writing code that you know will be built
-// both with and without exceptions and you need to conform to an interface
-// that uses exceptions.
-
-[[noreturn]] void ThrowStdLogicError(const std::string& what_arg);
-[[noreturn]] void ThrowStdLogicError(const char* what_arg);
-[[noreturn]] void ThrowStdInvalidArgument(const std::string& what_arg);
-[[noreturn]] void ThrowStdInvalidArgument(const char* what_arg);
-[[noreturn]] void ThrowStdDomainError(const std::string& what_arg);
-[[noreturn]] void ThrowStdDomainError(const char* what_arg);
-[[noreturn]] void ThrowStdLengthError(const std::string& what_arg);
-[[noreturn]] void ThrowStdLengthError(const char* what_arg);
-[[noreturn]] void ThrowStdOutOfRange(const std::string& what_arg);
-[[noreturn]] void ThrowStdOutOfRange(const char* what_arg);
-[[noreturn]] void ThrowStdRuntimeError(const std::string& what_arg);
-[[noreturn]] void ThrowStdRuntimeError(const char* what_arg);
-[[noreturn]] void ThrowStdRangeError(const std::string& what_arg);
-[[noreturn]] void ThrowStdRangeError(const char* what_arg);
-[[noreturn]] void ThrowStdOverflowError(const std::string& what_arg);
-[[noreturn]] void ThrowStdOverflowError(const char* what_arg);
-[[noreturn]] void ThrowStdUnderflowError(const std::string& what_arg);
-[[noreturn]] void ThrowStdUnderflowError(const char* what_arg);
-
-[[noreturn]] void ThrowStdBadFunctionCall();
-[[noreturn]] void ThrowStdBadAlloc();
-
-// ThrowStdBadArrayNewLength() cannot be consistently supported because
-// std::bad_array_new_length is missing in libstdc++ until 4.9.0.
-// https://gcc.gnu.org/onlinedocs/gcc-4.8.3/libstdc++/api/a01379_source.html
-// https://gcc.gnu.org/onlinedocs/gcc-4.9.0/libstdc++/api/a01327_source.html
-// libcxx (as of 3.2) and msvc (as of 2015) both have it.
-// [[noreturn]] void ThrowStdBadArrayNewLength();
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/tsan_mutex_interface.h b/third_party/abseil_cpp/absl/base/internal/tsan_mutex_interface.h
deleted file mode 100644
index 39207d8a5c..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/tsan_mutex_interface.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This file is intended solely for spinlock.h.
-// It provides ThreadSanitizer annotations for custom mutexes.
-// See <sanitizer/tsan_interface.h> for meaning of these annotations.
-
-#ifndef ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_
-#define ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_
-
-#include "absl/base/config.h"
-
-// ABSL_INTERNAL_HAVE_TSAN_INTERFACE
-// Macro intended only for internal use.
-//
-// Checks whether LLVM Thread Sanitizer interfaces are available.
-// First made available in LLVM 5.0 (Sep 2017).
-#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE
-#error "ABSL_INTERNAL_HAVE_TSAN_INTERFACE cannot be directly set."
-#endif
-
-#if defined(ABSL_HAVE_THREAD_SANITIZER) && defined(__has_include)
-#if __has_include(<sanitizer/tsan_interface.h>)
-#define ABSL_INTERNAL_HAVE_TSAN_INTERFACE 1
-#endif
-#endif
-
-#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE
-#include <sanitizer/tsan_interface.h>
-
-#define ABSL_TSAN_MUTEX_CREATE __tsan_mutex_create
-#define ABSL_TSAN_MUTEX_DESTROY __tsan_mutex_destroy
-#define ABSL_TSAN_MUTEX_PRE_LOCK __tsan_mutex_pre_lock
-#define ABSL_TSAN_MUTEX_POST_LOCK __tsan_mutex_post_lock
-#define ABSL_TSAN_MUTEX_PRE_UNLOCK __tsan_mutex_pre_unlock
-#define ABSL_TSAN_MUTEX_POST_UNLOCK __tsan_mutex_post_unlock
-#define ABSL_TSAN_MUTEX_PRE_SIGNAL __tsan_mutex_pre_signal
-#define ABSL_TSAN_MUTEX_POST_SIGNAL __tsan_mutex_post_signal
-#define ABSL_TSAN_MUTEX_PRE_DIVERT __tsan_mutex_pre_divert
-#define ABSL_TSAN_MUTEX_POST_DIVERT __tsan_mutex_post_divert
-
-#else
-
-#define ABSL_TSAN_MUTEX_CREATE(...)
-#define ABSL_TSAN_MUTEX_DESTROY(...)
-#define ABSL_TSAN_MUTEX_PRE_LOCK(...)
-#define ABSL_TSAN_MUTEX_POST_LOCK(...)
-#define ABSL_TSAN_MUTEX_PRE_UNLOCK(...)
-#define ABSL_TSAN_MUTEX_POST_UNLOCK(...)
-#define ABSL_TSAN_MUTEX_PRE_SIGNAL(...)
-#define ABSL_TSAN_MUTEX_POST_SIGNAL(...)
-#define ABSL_TSAN_MUTEX_PRE_DIVERT(...)
-#define ABSL_TSAN_MUTEX_POST_DIVERT(...)
-
-#endif
-
-#endif  // ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/unaligned_access.h b/third_party/abseil_cpp/absl/base/internal/unaligned_access.h
deleted file mode 100644
index 093dd9b499..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/unaligned_access.h
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_
-#define ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_
-
-#include <string.h>
-
-#include <cstdint>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-
-// unaligned APIs
-
-// Portable handling of unaligned loads, stores, and copies.
-
-// The unaligned API is C++ only.  The declarations use C++ features
-// (namespaces, inline) which are absent or incompatible in C.
-#if defined(__cplusplus)
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-inline uint16_t UnalignedLoad16(const void *p) {
-  uint16_t t;
-  memcpy(&t, p, sizeof t);
-  return t;
-}
-
-inline uint32_t UnalignedLoad32(const void *p) {
-  uint32_t t;
-  memcpy(&t, p, sizeof t);
-  return t;
-}
-
-inline uint64_t UnalignedLoad64(const void *p) {
-  uint64_t t;
-  memcpy(&t, p, sizeof t);
-  return t;
-}
-
-inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); }
-
-inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); }
-
-inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
-  (absl::base_internal::UnalignedLoad16(_p))
-#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
-  (absl::base_internal::UnalignedLoad32(_p))
-#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \
-  (absl::base_internal::UnalignedLoad64(_p))
-
-#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
-  (absl::base_internal::UnalignedStore16(_p, _val))
-#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
-  (absl::base_internal::UnalignedStore32(_p, _val))
-#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
-  (absl::base_internal::UnalignedStore64(_p, _val))
-
-#endif  // defined(__cplusplus), end of unaligned API
-
-#endif  // ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_
diff --git a/third_party/abseil_cpp/absl/base/internal/unique_small_name_test.cc b/third_party/abseil_cpp/absl/base/internal/unique_small_name_test.cc
deleted file mode 100644
index ff8c2b3fb4..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/unique_small_name_test.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "gtest/gtest.h"
-#include "absl/base/optimization.h"
-#include "absl/strings/string_view.h"
-
-// This test by itself does not do anything fancy, but it serves as binary I can
-// query in shell test.
-
-namespace {
-
-template <class T>
-void DoNotOptimize(const T& var) {
-#ifdef __GNUC__
-  asm volatile("" : "+m"(const_cast<T&>(var)));
-#else
-  std::cout << (void*)&var;
-#endif
-}
-
-int very_long_int_variable_name ABSL_INTERNAL_UNIQUE_SMALL_NAME() = 0;
-char very_long_str_variable_name[] ABSL_INTERNAL_UNIQUE_SMALL_NAME() = "abc";
-
-TEST(UniqueSmallName, NonAutomaticVar) {
-  EXPECT_EQ(very_long_int_variable_name, 0);
-  EXPECT_EQ(absl::string_view(very_long_str_variable_name), "abc");
-}
-
-int VeryLongFreeFunctionName() ABSL_INTERNAL_UNIQUE_SMALL_NAME();
-
-TEST(UniqueSmallName, FreeFunction) {
-  DoNotOptimize(&VeryLongFreeFunctionName);
-
-  EXPECT_EQ(VeryLongFreeFunctionName(), 456);
-}
-
-int VeryLongFreeFunctionName() { return 456; }
-
-struct VeryLongStructName {
-  explicit VeryLongStructName(int i);
-
-  int VeryLongMethodName() ABSL_INTERNAL_UNIQUE_SMALL_NAME();
-
-  static int VeryLongStaticMethodName() ABSL_INTERNAL_UNIQUE_SMALL_NAME();
-
- private:
-  int fld;
-};
-
-TEST(UniqueSmallName, Struct) {
-  VeryLongStructName var(10);
-
-  DoNotOptimize(var);
-  DoNotOptimize(&VeryLongStructName::VeryLongMethodName);
-  DoNotOptimize(&VeryLongStructName::VeryLongStaticMethodName);
-
-  EXPECT_EQ(var.VeryLongMethodName(), 10);
-  EXPECT_EQ(VeryLongStructName::VeryLongStaticMethodName(), 123);
-}
-
-VeryLongStructName::VeryLongStructName(int i) : fld(i) {}
-int VeryLongStructName::VeryLongMethodName() { return fld; }
-int VeryLongStructName::VeryLongStaticMethodName() { return 123; }
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/internal/unscaledcycleclock.cc b/third_party/abseil_cpp/absl/base/internal/unscaledcycleclock.cc
deleted file mode 100644
index 1545288cf2..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/unscaledcycleclock.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/unscaledcycleclock.h"
-
-#if ABSL_USE_UNSCALED_CYCLECLOCK
-
-#if defined(_WIN32)
-#include <intrin.h>
-#endif
-
-#if defined(__powerpc__) || defined(__ppc__)
-#ifdef __GLIBC__
-#include <sys/platform/ppc.h>
-#elif defined(__FreeBSD__)
-#include <sys/sysctl.h>
-#include <sys/types.h>
-#endif
-#endif
-
-#include "absl/base/internal/sysinfo.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-#if defined(__i386__)
-
-int64_t UnscaledCycleClock::Now() {
-  int64_t ret;
-  __asm__ volatile("rdtsc" : "=A"(ret));
-  return ret;
-}
-
-double UnscaledCycleClock::Frequency() {
-  return base_internal::NominalCPUFrequency();
-}
-
-#elif defined(__x86_64__)
-
-int64_t UnscaledCycleClock::Now() {
-  uint64_t low, high;
-  __asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
-  return (high << 32) | low;
-}
-
-double UnscaledCycleClock::Frequency() {
-  return base_internal::NominalCPUFrequency();
-}
-
-#elif defined(__powerpc__) || defined(__ppc__)
-
-int64_t UnscaledCycleClock::Now() {
-#ifdef __GLIBC__
-  return __ppc_get_timebase();
-#else
-#ifdef __powerpc64__
-  int64_t tbr;
-  asm volatile("mfspr %0, 268" : "=r"(tbr));
-  return tbr;
-#else
-  int32_t tbu, tbl, tmp;
-  asm volatile(
-      "0:\n"
-      "mftbu %[hi32]\n"
-      "mftb %[lo32]\n"
-      "mftbu %[tmp]\n"
-      "cmpw %[tmp],%[hi32]\n"
-      "bne 0b\n"
-      : [ hi32 ] "=r"(tbu), [ lo32 ] "=r"(tbl), [ tmp ] "=r"(tmp));
-  return (static_cast<int64_t>(tbu) << 32) | tbl;
-#endif
-#endif
-}
-
-double UnscaledCycleClock::Frequency() {
-#ifdef __GLIBC__
-  return __ppc_get_timebase_freq();
-#elif defined(__FreeBSD__)
-  static once_flag init_timebase_frequency_once;
-  static double timebase_frequency = 0.0;
-  base_internal::LowLevelCallOnce(&init_timebase_frequency_once, [&]() {
-    size_t length = sizeof(timebase_frequency);
-    sysctlbyname("kern.timecounter.tc.timebase.frequency", &timebase_frequency,
-                 &length, nullptr, 0);
-  });
-  return timebase_frequency;
-#else
-#error Must implement UnscaledCycleClock::Frequency()
-#endif
-}
-
-#elif defined(__aarch64__)
-
-// System timer of ARMv8 runs at a different frequency than the CPU's.
-// The frequency is fixed, typically in the range 1-50MHz.  It can be
-// read at CNTFRQ special register.  We assume the OS has set up
-// the virtual timer properly.
-int64_t UnscaledCycleClock::Now() {
-  int64_t virtual_timer_value;
-  asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
-  return virtual_timer_value;
-}
-
-double UnscaledCycleClock::Frequency() {
-  uint64_t aarch64_timer_frequency;
-  asm volatile("mrs %0, cntfrq_el0" : "=r"(aarch64_timer_frequency));
-  return aarch64_timer_frequency;
-}
-
-#elif defined(_M_IX86) || defined(_M_X64)
-
-#pragma intrinsic(__rdtsc)
-
-int64_t UnscaledCycleClock::Now() { return __rdtsc(); }
-
-double UnscaledCycleClock::Frequency() {
-  return base_internal::NominalCPUFrequency();
-}
-
-#endif
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_USE_UNSCALED_CYCLECLOCK
diff --git a/third_party/abseil_cpp/absl/base/internal/unscaledcycleclock.h b/third_party/abseil_cpp/absl/base/internal/unscaledcycleclock.h
deleted file mode 100644
index 82f2c87a95..0000000000
--- a/third_party/abseil_cpp/absl/base/internal/unscaledcycleclock.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// UnscaledCycleClock
-//    An UnscaledCycleClock yields the value and frequency of a cycle counter
-//    that increments at a rate that is approximately constant.
-//    This class is for internal use only, you should consider using CycleClock
-//    instead.
-//
-// Notes:
-// The cycle counter frequency is not necessarily the core clock frequency.
-// That is, CycleCounter cycles are not necessarily "CPU cycles".
-//
-// An arbitrary offset may have been added to the counter at power on.
-//
-// On some platforms, the rate and offset of the counter may differ
-// slightly when read from different CPUs of a multiprocessor.  Usually,
-// we try to ensure that the operating system adjusts values periodically
-// so that values agree approximately.   If you need stronger guarantees,
-// consider using alternate interfaces.
-//
-// The CPU is not required to maintain the ordering of a cycle counter read
-// with respect to surrounding instructions.
-
-#ifndef ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_
-#define ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_
-
-#include <cstdint>
-
-#if defined(__APPLE__)
-#include <TargetConditionals.h>
-#endif
-
-#include "absl/base/port.h"
-
-// The following platforms have an implementation of a hardware counter.
-#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
-  defined(__powerpc__) || defined(__ppc__) || \
-  defined(_M_IX86) || defined(_M_X64)
-#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1
-#else
-#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0
-#endif
-
-// The following platforms often disable access to the hardware
-// counter (through a sandbox) even if the underlying hardware has a
-// usable counter. The CycleTimer interface also requires a *scaled*
-// CycleClock that runs at atleast 1 MHz. We've found some Android
-// ARM64 devices where this is not the case, so we disable it by
-// default on Android ARM64.
-#if defined(__native_client__) ||                      \
-    (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \
-    (defined(__ANDROID__) && defined(__aarch64__))
-#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0
-#else
-#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 1
-#endif
-
-// UnscaledCycleClock is an optional internal feature.
-// Use "#if ABSL_USE_UNSCALED_CYCLECLOCK" to test for its presence.
-// Can be overridden at compile-time via -DABSL_USE_UNSCALED_CYCLECLOCK=0|1
-#if !defined(ABSL_USE_UNSCALED_CYCLECLOCK)
-#define ABSL_USE_UNSCALED_CYCLECLOCK               \
-  (ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION && \
-   ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT)
-#endif
-
-#if ABSL_USE_UNSCALED_CYCLECLOCK
-
-// This macro can be used to test if UnscaledCycleClock::Frequency()
-// is NominalCPUFrequency() on a particular platform.
-#if  (defined(__i386__) || defined(__x86_64__) || \
-      defined(_M_IX86) || defined(_M_X64))
-#define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-class UnscaledCycleClockWrapperForGetCurrentTime;
-}  // namespace time_internal
-
-namespace base_internal {
-class CycleClock;
-class UnscaledCycleClockWrapperForInitializeFrequency;
-
-class UnscaledCycleClock {
- private:
-  UnscaledCycleClock() = delete;
-
-  // Return the value of a cycle counter that counts at a rate that is
-  // approximately constant.
-  static int64_t Now();
-
-  // Return the how much UnscaledCycleClock::Now() increases per second.
-  // This is not necessarily the core CPU clock frequency.
-  // It may be the nominal value report by the kernel, rather than a measured
-  // value.
-  static double Frequency();
-
-  // Allowed users
-  friend class base_internal::CycleClock;
-  friend class time_internal::UnscaledCycleClockWrapperForGetCurrentTime;
-  friend class base_internal::UnscaledCycleClockWrapperForInitializeFrequency;
-};
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_USE_UNSCALED_CYCLECLOCK
-
-#endif  // ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_
diff --git a/third_party/abseil_cpp/absl/base/invoke_test.cc b/third_party/abseil_cpp/absl/base/invoke_test.cc
deleted file mode 100644
index bcdef36c3b..0000000000
--- a/third_party/abseil_cpp/absl/base/invoke_test.cc
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/invoke.h"
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/str_cat.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-namespace {
-
-int Function(int a, int b) { return a - b; }
-
-int Sink(std::unique_ptr<int> p) {
-  return *p;
-}
-
-std::unique_ptr<int> Factory(int n) {
-  return make_unique<int>(n);
-}
-
-void NoOp() {}
-
-struct ConstFunctor {
-  int operator()(int a, int b) const { return a - b; }
-};
-
-struct MutableFunctor {
-  int operator()(int a, int b) { return a - b; }
-};
-
-struct EphemeralFunctor {
-  int operator()(int a, int b) && { return a - b; }
-};
-
-struct OverloadedFunctor {
-  template <typename... Args>
-  std::string operator()(const Args&... args) & {
-    return StrCat("&", args...);
-  }
-  template <typename... Args>
-  std::string operator()(const Args&... args) const& {
-    return StrCat("const&", args...);
-  }
-  template <typename... Args>
-  std::string operator()(const Args&... args) && {
-    return StrCat("&&", args...);
-  }
-};
-
-struct Class {
-  int Method(int a, int b) { return a - b; }
-  int ConstMethod(int a, int b) const { return a - b; }
-  int RefMethod(int a, int b) & { return a - b; }
-  int RefRefMethod(int a, int b) && { return a - b; }
-  int NoExceptMethod(int a, int b) noexcept { return a - b; }
-  int VolatileMethod(int a, int b) volatile { return a - b; }
-
-  int member;
-};
-
-struct FlipFlop {
-  int ConstMethod() const { return member; }
-  FlipFlop operator*() const { return {-member}; }
-
-  int member;
-};
-
-// CallMaybeWithArg(f) resolves either to invoke(f) or invoke(f, 42), depending
-// on which one is valid.
-template <typename F>
-decltype(base_internal::invoke(std::declval<const F&>())) CallMaybeWithArg(
-    const F& f) {
-  return base_internal::invoke(f);
-}
-
-template <typename F>
-decltype(base_internal::invoke(std::declval<const F&>(), 42)) CallMaybeWithArg(
-    const F& f) {
-  return base_internal::invoke(f, 42);
-}
-
-TEST(InvokeTest, Function) {
-  EXPECT_EQ(1, base_internal::invoke(Function, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Function, 3, 2));
-}
-
-TEST(InvokeTest, NonCopyableArgument) {
-  EXPECT_EQ(42, base_internal::invoke(Sink, make_unique<int>(42)));
-}
-
-TEST(InvokeTest, NonCopyableResult) {
-  EXPECT_THAT(base_internal::invoke(Factory, 42), ::testing::Pointee(42));
-}
-
-TEST(InvokeTest, VoidResult) { base_internal::invoke(NoOp); }
-
-TEST(InvokeTest, ConstFunctor) {
-  EXPECT_EQ(1, base_internal::invoke(ConstFunctor(), 3, 2));
-}
-
-TEST(InvokeTest, MutableFunctor) {
-  MutableFunctor f;
-  EXPECT_EQ(1, base_internal::invoke(f, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(MutableFunctor(), 3, 2));
-}
-
-TEST(InvokeTest, EphemeralFunctor) {
-  EphemeralFunctor f;
-  EXPECT_EQ(1, base_internal::invoke(std::move(f), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(EphemeralFunctor(), 3, 2));
-}
-
-TEST(InvokeTest, OverloadedFunctor) {
-  OverloadedFunctor f;
-  const OverloadedFunctor& cf = f;
-
-  EXPECT_EQ("&", base_internal::invoke(f));
-  EXPECT_EQ("& 42", base_internal::invoke(f, " 42"));
-
-  EXPECT_EQ("const&", base_internal::invoke(cf));
-  EXPECT_EQ("const& 42", base_internal::invoke(cf, " 42"));
-
-  EXPECT_EQ("&&", base_internal::invoke(std::move(f)));
-
-  OverloadedFunctor f2;
-  EXPECT_EQ("&& 42", base_internal::invoke(std::move(f2), " 42"));
-}
-
-TEST(InvokeTest, ReferenceWrapper) {
-  ConstFunctor cf;
-  MutableFunctor mf;
-  EXPECT_EQ(1, base_internal::invoke(std::cref(cf), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(std::ref(cf), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(std::ref(mf), 3, 2));
-}
-
-TEST(InvokeTest, MemberFunction) {
-  std::unique_ptr<Class> p(new Class);
-  std::unique_ptr<const Class> cp(new Class);
-  std::unique_ptr<volatile Class> vp(new Class);
-
-  EXPECT_EQ(1, base_internal::invoke(&Class::Method, p, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::Method, p.get(), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::Method, *p, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::RefMethod, p, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::RefMethod, p.get(), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::RefMethod, *p, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::RefRefMethod, std::move(*p), 3,
-                                     2));  // NOLINT
-  EXPECT_EQ(1, base_internal::invoke(&Class::NoExceptMethod, p, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::NoExceptMethod, p.get(), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::NoExceptMethod, *p, 3, 2));
-
-  EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, p, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, p.get(), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, *p, 3, 2));
-
-  EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, cp, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, cp.get(), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, *cp, 3, 2));
-
-  EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, p, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, p.get(), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, *p, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, vp, 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, vp.get(), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, *vp, 3, 2));
-
-  EXPECT_EQ(1,
-            base_internal::invoke(&Class::Method, make_unique<Class>(), 3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, make_unique<Class>(),
-                                     3, 2));
-  EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod,
-                                     make_unique<const Class>(), 3, 2));
-}
-
-TEST(InvokeTest, DataMember) {
-  std::unique_ptr<Class> p(new Class{42});
-  std::unique_ptr<const Class> cp(new Class{42});
-  EXPECT_EQ(42, base_internal::invoke(&Class::member, p));
-  EXPECT_EQ(42, base_internal::invoke(&Class::member, *p));
-  EXPECT_EQ(42, base_internal::invoke(&Class::member, p.get()));
-
-  base_internal::invoke(&Class::member, p) = 42;
-  base_internal::invoke(&Class::member, p.get()) = 42;
-
-  EXPECT_EQ(42, base_internal::invoke(&Class::member, cp));
-  EXPECT_EQ(42, base_internal::invoke(&Class::member, *cp));
-  EXPECT_EQ(42, base_internal::invoke(&Class::member, cp.get()));
-}
-
-TEST(InvokeTest, FlipFlop) {
-  FlipFlop obj = {42};
-  // This call could resolve to (obj.*&FlipFlop::ConstMethod)() or
-  // ((*obj).*&FlipFlop::ConstMethod)(). We verify that it's the former.
-  EXPECT_EQ(42, base_internal::invoke(&FlipFlop::ConstMethod, obj));
-  EXPECT_EQ(42, base_internal::invoke(&FlipFlop::member, obj));
-}
-
-TEST(InvokeTest, SfinaeFriendly) {
-  CallMaybeWithArg(NoOp);
-  EXPECT_THAT(CallMaybeWithArg(Factory), ::testing::Pointee(42));
-}
-
-}  // namespace
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/log_severity.cc b/third_party/abseil_cpp/absl/base/log_severity.cc
deleted file mode 100644
index 72312afd36..0000000000
--- a/third_party/abseil_cpp/absl/base/log_severity.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/log_severity.h"
-
-#include <ostream>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-std::ostream& operator<<(std::ostream& os, absl::LogSeverity s) {
-  if (s == absl::NormalizeLogSeverity(s)) return os << absl::LogSeverityName(s);
-  return os << "absl::LogSeverity(" << static_cast<int>(s) << ")";
-}
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/log_severity.h b/third_party/abseil_cpp/absl/base/log_severity.h
deleted file mode 100644
index 045f17f81d..0000000000
--- a/third_party/abseil_cpp/absl/base/log_severity.h
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_LOG_SEVERITY_H_
-#define ABSL_BASE_LOG_SEVERITY_H_
-
-#include <array>
-#include <ostream>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::LogSeverity
-//
-// Four severity levels are defined. Logging APIs should terminate the program
-// when a message is logged at severity `kFatal`; the other levels have no
-// special semantics.
-//
-// Values other than the four defined levels (e.g. produced by `static_cast`)
-// are valid, but their semantics when passed to a function, macro, or flag
-// depend on the function, macro, or flag. The usual behavior is to normalize
-// such values to a defined severity level, however in some cases values other
-// than the defined levels are useful for comparison.
-//
-// Exmaple:
-//
-//   // Effectively disables all logging:
-//   SetMinLogLevel(static_cast<absl::LogSeverity>(100));
-//
-// Abseil flags may be defined with type `LogSeverity`. Dependency layering
-// constraints require that the `AbslParseFlag()` overload be declared and
-// defined in the flags library itself rather than here. The `AbslUnparseFlag()`
-// overload is defined there as well for consistency.
-//
-// absl::LogSeverity Flag String Representation
-//
-// An `absl::LogSeverity` has a string representation used for parsing
-// command-line flags based on the enumerator name (e.g. `kFatal`) or
-// its unprefixed name (without the `k`) in any case-insensitive form. (E.g.
-// "FATAL", "fatal" or "Fatal" are all valid.) Unparsing such flags produces an
-// unprefixed string representation in all caps (e.g. "FATAL") or an integer.
-//
-// Additionally, the parser accepts arbitrary integers (as if the type were
-// `int`).
-//
-// Examples:
-//
-//   --my_log_level=kInfo
-//   --my_log_level=INFO
-//   --my_log_level=info
-//   --my_log_level=0
-//
-// Unparsing a flag produces the same result as `absl::LogSeverityName()` for
-// the standard levels and a base-ten integer otherwise.
-enum class LogSeverity : int {
-  kInfo = 0,
-  kWarning = 1,
-  kError = 2,
-  kFatal = 3,
-};
-
-// LogSeverities()
-//
-// Returns an iterable of all standard `absl::LogSeverity` values, ordered from
-// least to most severe.
-constexpr std::array<absl::LogSeverity, 4> LogSeverities() {
-  return {{absl::LogSeverity::kInfo, absl::LogSeverity::kWarning,
-           absl::LogSeverity::kError, absl::LogSeverity::kFatal}};
-}
-
-// LogSeverityName()
-//
-// Returns the all-caps string representation (e.g. "INFO") of the specified
-// severity level if it is one of the standard levels and "UNKNOWN" otherwise.
-constexpr const char* LogSeverityName(absl::LogSeverity s) {
-  return s == absl::LogSeverity::kInfo
-             ? "INFO"
-             : s == absl::LogSeverity::kWarning
-                   ? "WARNING"
-                   : s == absl::LogSeverity::kError
-                         ? "ERROR"
-                         : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN";
-}
-
-// NormalizeLogSeverity()
-//
-// Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal`
-// normalize to `kError` (**NOT** `kFatal`).
-constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) {
-  return s < absl::LogSeverity::kInfo
-             ? absl::LogSeverity::kInfo
-             : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s;
-}
-constexpr absl::LogSeverity NormalizeLogSeverity(int s) {
-  return absl::NormalizeLogSeverity(static_cast<absl::LogSeverity>(s));
-}
-
-// operator<<
-//
-// The exact representation of a streamed `absl::LogSeverity` is deliberately
-// unspecified; do not rely on it.
-std::ostream& operator<<(std::ostream& os, absl::LogSeverity s);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_LOG_SEVERITY_H_
diff --git a/third_party/abseil_cpp/absl/base/log_severity_test.cc b/third_party/abseil_cpp/absl/base/log_severity_test.cc
deleted file mode 100644
index 2c6872b00a..0000000000
--- a/third_party/abseil_cpp/absl/base/log_severity_test.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/log_severity.h"
-
-#include <cstdint>
-#include <ios>
-#include <limits>
-#include <ostream>
-#include <sstream>
-#include <string>
-#include <tuple>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/flags/internal/flag.h"
-#include "absl/flags/marshalling.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-using ::testing::Eq;
-using ::testing::IsFalse;
-using ::testing::IsTrue;
-using ::testing::TestWithParam;
-using ::testing::Values;
-
-std::string StreamHelper(absl::LogSeverity value) {
-  std::ostringstream stream;
-  stream << value;
-  return stream.str();
-}
-
-TEST(StreamTest, Works) {
-  EXPECT_THAT(StreamHelper(static_cast<absl::LogSeverity>(-100)),
-              Eq("absl::LogSeverity(-100)"));
-  EXPECT_THAT(StreamHelper(absl::LogSeverity::kInfo), Eq("INFO"));
-  EXPECT_THAT(StreamHelper(absl::LogSeverity::kWarning), Eq("WARNING"));
-  EXPECT_THAT(StreamHelper(absl::LogSeverity::kError), Eq("ERROR"));
-  EXPECT_THAT(StreamHelper(absl::LogSeverity::kFatal), Eq("FATAL"));
-  EXPECT_THAT(StreamHelper(static_cast<absl::LogSeverity>(4)),
-              Eq("absl::LogSeverity(4)"));
-}
-
-static_assert(
-    absl::flags_internal::FlagUseOneWordStorage<absl::LogSeverity>::value,
-    "Flags of type absl::LogSeverity ought to be lock-free.");
-
-using ParseFlagFromOutOfRangeIntegerTest = TestWithParam<int64_t>;
-INSTANTIATE_TEST_SUITE_P(
-    Instantiation, ParseFlagFromOutOfRangeIntegerTest,
-    Values(static_cast<int64_t>(std::numeric_limits<int>::min()) - 1,
-           static_cast<int64_t>(std::numeric_limits<int>::max()) + 1));
-TEST_P(ParseFlagFromOutOfRangeIntegerTest, ReturnsError) {
-  const std::string to_parse = absl::StrCat(GetParam());
-  absl::LogSeverity value;
-  std::string error;
-  EXPECT_THAT(absl::ParseFlag(to_parse, &value, &error), IsFalse()) << value;
-}
-
-using ParseFlagFromAlmostOutOfRangeIntegerTest = TestWithParam<int>;
-INSTANTIATE_TEST_SUITE_P(Instantiation,
-                         ParseFlagFromAlmostOutOfRangeIntegerTest,
-                         Values(std::numeric_limits<int>::min(),
-                                std::numeric_limits<int>::max()));
-TEST_P(ParseFlagFromAlmostOutOfRangeIntegerTest, YieldsExpectedValue) {
-  const auto expected = static_cast<absl::LogSeverity>(GetParam());
-  const std::string to_parse = absl::StrCat(GetParam());
-  absl::LogSeverity value;
-  std::string error;
-  ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error;
-  EXPECT_THAT(value, Eq(expected));
-}
-
-using ParseFlagFromIntegerMatchingEnumeratorTest =
-    TestWithParam<std::tuple<absl::string_view, absl::LogSeverity>>;
-INSTANTIATE_TEST_SUITE_P(
-    Instantiation, ParseFlagFromIntegerMatchingEnumeratorTest,
-    Values(std::make_tuple("0", absl::LogSeverity::kInfo),
-           std::make_tuple(" 0", absl::LogSeverity::kInfo),
-           std::make_tuple("-0", absl::LogSeverity::kInfo),
-           std::make_tuple("+0", absl::LogSeverity::kInfo),
-           std::make_tuple("00", absl::LogSeverity::kInfo),
-           std::make_tuple("0 ", absl::LogSeverity::kInfo),
-           std::make_tuple("0x0", absl::LogSeverity::kInfo),
-           std::make_tuple("1", absl::LogSeverity::kWarning),
-           std::make_tuple("+1", absl::LogSeverity::kWarning),
-           std::make_tuple("2", absl::LogSeverity::kError),
-           std::make_tuple("3", absl::LogSeverity::kFatal)));
-TEST_P(ParseFlagFromIntegerMatchingEnumeratorTest, YieldsExpectedValue) {
-  const absl::string_view to_parse = std::get<0>(GetParam());
-  const absl::LogSeverity expected = std::get<1>(GetParam());
-  absl::LogSeverity value;
-  std::string error;
-  ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error;
-  EXPECT_THAT(value, Eq(expected));
-}
-
-using ParseFlagFromOtherIntegerTest =
-    TestWithParam<std::tuple<absl::string_view, int>>;
-INSTANTIATE_TEST_SUITE_P(Instantiation, ParseFlagFromOtherIntegerTest,
-                         Values(std::make_tuple("-1", -1),
-                                std::make_tuple("4", 4),
-                                std::make_tuple("010", 10),
-                                std::make_tuple("0x10", 16)));
-TEST_P(ParseFlagFromOtherIntegerTest, YieldsExpectedValue) {
-  const absl::string_view to_parse = std::get<0>(GetParam());
-  const auto expected = static_cast<absl::LogSeverity>(std::get<1>(GetParam()));
-  absl::LogSeverity value;
-  std::string error;
-  ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error;
-  EXPECT_THAT(value, Eq(expected));
-}
-
-using ParseFlagFromEnumeratorTest =
-    TestWithParam<std::tuple<absl::string_view, absl::LogSeverity>>;
-INSTANTIATE_TEST_SUITE_P(
-    Instantiation, ParseFlagFromEnumeratorTest,
-    Values(std::make_tuple("INFO", absl::LogSeverity::kInfo),
-           std::make_tuple("info", absl::LogSeverity::kInfo),
-           std::make_tuple("kInfo", absl::LogSeverity::kInfo),
-           std::make_tuple("iNfO", absl::LogSeverity::kInfo),
-           std::make_tuple("kInFo", absl::LogSeverity::kInfo),
-           std::make_tuple("WARNING", absl::LogSeverity::kWarning),
-           std::make_tuple("warning", absl::LogSeverity::kWarning),
-           std::make_tuple("kWarning", absl::LogSeverity::kWarning),
-           std::make_tuple("WaRnInG", absl::LogSeverity::kWarning),
-           std::make_tuple("KwArNiNg", absl::LogSeverity::kWarning),
-           std::make_tuple("ERROR", absl::LogSeverity::kError),
-           std::make_tuple("error", absl::LogSeverity::kError),
-           std::make_tuple("kError", absl::LogSeverity::kError),
-           std::make_tuple("eRrOr", absl::LogSeverity::kError),
-           std::make_tuple("kErRoR", absl::LogSeverity::kError),
-           std::make_tuple("FATAL", absl::LogSeverity::kFatal),
-           std::make_tuple("fatal", absl::LogSeverity::kFatal),
-           std::make_tuple("kFatal", absl::LogSeverity::kFatal),
-           std::make_tuple("FaTaL", absl::LogSeverity::kFatal),
-           std::make_tuple("KfAtAl", absl::LogSeverity::kFatal)));
-TEST_P(ParseFlagFromEnumeratorTest, YieldsExpectedValue) {
-  const absl::string_view to_parse = std::get<0>(GetParam());
-  const absl::LogSeverity expected = std::get<1>(GetParam());
-  absl::LogSeverity value;
-  std::string error;
-  ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error;
-  EXPECT_THAT(value, Eq(expected));
-}
-
-using ParseFlagFromGarbageTest = TestWithParam<absl::string_view>;
-INSTANTIATE_TEST_SUITE_P(Instantiation, ParseFlagFromGarbageTest,
-                         Values("", "\0", " ", "garbage", "kkinfo", "I"));
-TEST_P(ParseFlagFromGarbageTest, ReturnsError) {
-  const absl::string_view to_parse = GetParam();
-  absl::LogSeverity value;
-  std::string error;
-  EXPECT_THAT(absl::ParseFlag(to_parse, &value, &error), IsFalse()) << value;
-}
-
-using UnparseFlagToEnumeratorTest =
-    TestWithParam<std::tuple<absl::LogSeverity, absl::string_view>>;
-INSTANTIATE_TEST_SUITE_P(
-    Instantiation, UnparseFlagToEnumeratorTest,
-    Values(std::make_tuple(absl::LogSeverity::kInfo, "INFO"),
-           std::make_tuple(absl::LogSeverity::kWarning, "WARNING"),
-           std::make_tuple(absl::LogSeverity::kError, "ERROR"),
-           std::make_tuple(absl::LogSeverity::kFatal, "FATAL")));
-TEST_P(UnparseFlagToEnumeratorTest, ReturnsExpectedValueAndRoundTrips) {
-  const absl::LogSeverity to_unparse = std::get<0>(GetParam());
-  const absl::string_view expected = std::get<1>(GetParam());
-  const std::string stringified_value = absl::UnparseFlag(to_unparse);
-  EXPECT_THAT(stringified_value, Eq(expected));
-  absl::LogSeverity reparsed_value;
-  std::string error;
-  EXPECT_THAT(absl::ParseFlag(stringified_value, &reparsed_value, &error),
-              IsTrue());
-  EXPECT_THAT(reparsed_value, Eq(to_unparse));
-}
-
-using UnparseFlagToOtherIntegerTest = TestWithParam<int>;
-INSTANTIATE_TEST_SUITE_P(Instantiation, UnparseFlagToOtherIntegerTest,
-                         Values(std::numeric_limits<int>::min(), -1, 4,
-                                std::numeric_limits<int>::max()));
-TEST_P(UnparseFlagToOtherIntegerTest, ReturnsExpectedValueAndRoundTrips) {
-  const absl::LogSeverity to_unparse =
-      static_cast<absl::LogSeverity>(GetParam());
-  const std::string expected = absl::StrCat(GetParam());
-  const std::string stringified_value = absl::UnparseFlag(to_unparse);
-  EXPECT_THAT(stringified_value, Eq(expected));
-  absl::LogSeverity reparsed_value;
-  std::string error;
-  EXPECT_THAT(absl::ParseFlag(stringified_value, &reparsed_value, &error),
-              IsTrue());
-  EXPECT_THAT(reparsed_value, Eq(to_unparse));
-}
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/macros.h b/third_party/abseil_cpp/absl/base/macros.h
deleted file mode 100644
index 3e085a916b..0000000000
--- a/third_party/abseil_cpp/absl/base/macros.h
+++ /dev/null
@@ -1,158 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: macros.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the set of language macros used within Abseil code.
-// For the set of macros used to determine supported compilers and platforms,
-// see absl/base/config.h instead.
-//
-// This code is compiled directly on many platforms, including client
-// platforms like Windows, Mac, and embedded systems.  Before making
-// any changes here, make sure that you're not breaking any platforms.
-
-#ifndef ABSL_BASE_MACROS_H_
-#define ABSL_BASE_MACROS_H_
-
-#include <cassert>
-#include <cstddef>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"
-
-// ABSL_ARRAYSIZE()
-//
-// Returns the number of elements in an array as a compile-time constant, which
-// can be used in defining new arrays. If you use this macro on a pointer by
-// mistake, you will get a compile-time error.
-#define ABSL_ARRAYSIZE(array) \
-  (sizeof(::absl::macros_internal::ArraySizeHelper(array)))
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace macros_internal {
-// Note: this internal template function declaration is used by ABSL_ARRAYSIZE.
-// The function doesn't need a definition, as we only use its type.
-template <typename T, size_t N>
-auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
-}  // namespace macros_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// ABSL_BAD_CALL_IF()
-//
-// Used on a function overload to trap bad calls: any call that matches the
-// overload will cause a compile-time error. This macro uses a clang-specific
-// "enable_if" attribute, as described at
-// https://clang.llvm.org/docs/AttributeReference.html#enable-if
-//
-// Overloads which use this macro should be bracketed by
-// `#ifdef ABSL_BAD_CALL_IF`.
-//
-// Example:
-//
-//   int isdigit(int c);
-//   #ifdef ABSL_BAD_CALL_IF
-//   int isdigit(int c)
-//     ABSL_BAD_CALL_IF(c <= -1 || c > 255,
-//                       "'c' must have the value of an unsigned char or EOF");
-//   #endif // ABSL_BAD_CALL_IF
-#if ABSL_HAVE_ATTRIBUTE(enable_if)
-#define ABSL_BAD_CALL_IF(expr, msg) \
-  __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg)))
-#endif
-
-// ABSL_ASSERT()
-//
-// In C++11, `assert` can't be used portably within constexpr functions.
-// ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr
-// functions.  Example:
-//
-// constexpr double Divide(double a, double b) {
-//   return ABSL_ASSERT(b != 0), a / b;
-// }
-//
-// This macro is inspired by
-// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
-#if defined(NDEBUG)
-#define ABSL_ASSERT(expr) \
-  (false ? static_cast<void>(expr) : static_cast<void>(0))
-#else
-#define ABSL_ASSERT(expr)                           \
-  (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \
-                             : [] { assert(false && #expr); }())  // NOLINT
-#endif
-
-// `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()`
-// aborts the program in release mode (when NDEBUG is defined). The
-// implementation should abort the program as quickly as possible and ideally it
-// should not be possible to ignore the abort request.
-#if (ABSL_HAVE_BUILTIN(__builtin_trap) &&         \
-     ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_INTERNAL_HARDENING_ABORT() \
-  do {                                  \
-    __builtin_trap();                   \
-    __builtin_unreachable();            \
-  } while (false)
-#else
-#define ABSL_INTERNAL_HARDENING_ABORT() abort()
-#endif
-
-// ABSL_HARDENING_ASSERT()
-//
-// `ABSL_HARDENING_ASSERT()` is like `ABSL_ASSERT()`, but used to implement
-// runtime assertions that should be enabled in hardened builds even when
-// `NDEBUG` is defined.
-//
-// When `NDEBUG` is not defined, `ABSL_HARDENING_ASSERT()` is identical to
-// `ABSL_ASSERT()`.
-//
-// See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on
-// hardened mode.
-#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG)
-#define ABSL_HARDENING_ASSERT(expr)                 \
-  (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \
-                             : [] { ABSL_INTERNAL_HARDENING_ABORT(); }())
-#else
-#define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr)
-#endif
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-#define ABSL_INTERNAL_TRY try
-#define ABSL_INTERNAL_CATCH_ANY catch (...)
-#define ABSL_INTERNAL_RETHROW do { throw; } while (false)
-#else  // ABSL_HAVE_EXCEPTIONS
-#define ABSL_INTERNAL_TRY if (true)
-#define ABSL_INTERNAL_CATCH_ANY else if (false)
-#define ABSL_INTERNAL_RETHROW do {} while (false)
-#endif  // ABSL_HAVE_EXCEPTIONS
-
-// `ABSL_INTERNAL_UNREACHABLE` is an unreachable statement.  A program which
-// reaches one has undefined behavior, and the compiler may optimize
-// accordingly.
-#if defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable)
-#define ABSL_INTERNAL_UNREACHABLE __builtin_unreachable()
-#elif defined(_MSC_VER)
-#define ABSL_INTERNAL_UNREACHABLE __assume(0)
-#else
-#define ABSL_INTERNAL_UNREACHABLE
-#endif
-
-#endif  // ABSL_BASE_MACROS_H_
diff --git a/third_party/abseil_cpp/absl/base/optimization.h b/third_party/abseil_cpp/absl/base/optimization.h
deleted file mode 100644
index 393fc3a423..0000000000
--- a/third_party/abseil_cpp/absl/base/optimization.h
+++ /dev/null
@@ -1,241 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: optimization.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines portable macros for performance optimization.
-
-#ifndef ABSL_BASE_OPTIMIZATION_H_
-#define ABSL_BASE_OPTIMIZATION_H_
-
-#include "absl/base/config.h"
-
-// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION
-//
-// Instructs the compiler to avoid optimizing tail-call recursion. Use of this
-// macro is useful when you wish to preserve the existing function order within
-// a stack trace for logging, debugging, or profiling purposes.
-//
-// Example:
-//
-//   int f() {
-//     int result = g();
-//     ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
-//     return result;
-//   }
-#if defined(__pnacl__)
-#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
-#elif defined(__clang__)
-// Clang will not tail call given inline volatile assembly.
-#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
-#elif defined(__GNUC__)
-// GCC will not tail call given inline volatile assembly.
-#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
-#elif defined(_MSC_VER)
-#include <intrin.h>
-// The __nop() intrinsic blocks the optimisation.
-#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __nop()
-#else
-#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
-#endif
-
-// ABSL_CACHELINE_SIZE
-//
-// Explicitly defines the size of the L1 cache for purposes of alignment.
-// Setting the cacheline size allows you to specify that certain objects be
-// aligned on a cacheline boundary with `ABSL_CACHELINE_ALIGNED` declarations.
-// (See below.)
-//
-// NOTE: this macro should be replaced with the following C++17 features, when
-// those are generally available:
-//
-//   * `std::hardware_constructive_interference_size`
-//   * `std::hardware_destructive_interference_size`
-//
-// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html
-// for more information.
-#if defined(__GNUC__)
-// Cache line alignment
-#if defined(__i386__) || defined(__x86_64__)
-#define ABSL_CACHELINE_SIZE 64
-#elif defined(__powerpc64__)
-#define ABSL_CACHELINE_SIZE 128
-#elif defined(__aarch64__)
-// We would need to read special register ctr_el0 to find out L1 dcache size.
-// This value is a good estimate based on a real aarch64 machine.
-#define ABSL_CACHELINE_SIZE 64
-#elif defined(__arm__)
-// Cache line sizes for ARM: These values are not strictly correct since
-// cache line sizes depend on implementations, not architectures.  There
-// are even implementations with cache line sizes configurable at boot
-// time.
-#if defined(__ARM_ARCH_5T__)
-#define ABSL_CACHELINE_SIZE 32
-#elif defined(__ARM_ARCH_7A__)
-#define ABSL_CACHELINE_SIZE 64
-#endif
-#endif
-
-#ifndef ABSL_CACHELINE_SIZE
-// A reasonable default guess.  Note that overestimates tend to waste more
-// space, while underestimates tend to waste more time.
-#define ABSL_CACHELINE_SIZE 64
-#endif
-
-// ABSL_CACHELINE_ALIGNED
-//
-// Indicates that the declared object be cache aligned using
-// `ABSL_CACHELINE_SIZE` (see above). Cacheline aligning objects allows you to
-// load a set of related objects in the L1 cache for performance improvements.
-// Cacheline aligning objects properly allows constructive memory sharing and
-// prevents destructive (or "false") memory sharing.
-//
-// NOTE: this macro should be replaced with usage of `alignas()` using
-// `std::hardware_constructive_interference_size` and/or
-// `std::hardware_destructive_interference_size` when available within C++17.
-//
-// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html
-// for more information.
-//
-// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to an `__attribute__`
-// or `__declspec` attribute. For compilers where this is not known to work,
-// the macro expands to nothing.
-//
-// No further guarantees are made here. The result of applying the macro
-// to variables and types is always implementation-defined.
-//
-// WARNING: It is easy to use this attribute incorrectly, even to the point
-// of causing bugs that are difficult to diagnose, crash, etc. It does not
-// of itself guarantee that objects are aligned to a cache line.
-//
-// NOTE: Some compilers are picky about the locations of annotations such as
-// this attribute, so prefer to put it at the beginning of your declaration.
-// For example,
-//
-//   ABSL_CACHELINE_ALIGNED static Foo* foo = ...
-//
-//   class ABSL_CACHELINE_ALIGNED Bar { ...
-//
-// Recommendations:
-//
-// 1) Consult compiler documentation; this comment is not kept in sync as
-//    toolchains evolve.
-// 2) Verify your use has the intended effect. This often requires inspecting
-//    the generated machine code.
-// 3) Prefer applying this attribute to individual variables. Avoid
-//    applying it to types. This tends to localize the effect.
-#define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE)))
-#elif defined(_MSC_VER)
-#define ABSL_CACHELINE_SIZE 64
-#define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE))
-#else
-#define ABSL_CACHELINE_SIZE 64
-#define ABSL_CACHELINE_ALIGNED
-#endif
-
-// ABSL_PREDICT_TRUE, ABSL_PREDICT_FALSE
-//
-// Enables the compiler to prioritize compilation using static analysis for
-// likely paths within a boolean branch.
-//
-// Example:
-//
-//   if (ABSL_PREDICT_TRUE(expression)) {
-//     return result;                        // Faster if more likely
-//   } else {
-//     return 0;
-//   }
-//
-// Compilers can use the information that a certain branch is not likely to be
-// taken (for instance, a CHECK failure) to optimize for the common case in
-// the absence of better information (ie. compiling gcc with `-fprofile-arcs`).
-//
-// Recommendation: Modern CPUs dynamically predict branch execution paths,
-// typically with accuracy greater than 97%. As a result, annotating every
-// branch in a codebase is likely counterproductive; however, annotating
-// specific branches that are both hot and consistently mispredicted is likely
-// to yield performance improvements.
-#if ABSL_HAVE_BUILTIN(__builtin_expect) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_PREDICT_FALSE(x) (__builtin_expect(false || (x), false))
-#define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true))
-#else
-#define ABSL_PREDICT_FALSE(x) (x)
-#define ABSL_PREDICT_TRUE(x) (x)
-#endif
-
-// ABSL_INTERNAL_ASSUME(cond)
-// Informs the compiler that a condition is always true and that it can assume
-// it to be true for optimization purposes. The call has undefined behavior if
-// the condition is false.
-// In !NDEBUG mode, the condition is checked with an assert().
-// NOTE: The expression must not have side effects, as it will only be evaluated
-// in some compilation modes and not others.
-//
-// Example:
-//
-//   int x = ...;
-//   ABSL_INTERNAL_ASSUME(x >= 0);
-//   // The compiler can optimize the division to a simple right shift using the
-//   // assumption specified above.
-//   int y = x / 16;
-//
-#if !defined(NDEBUG)
-#define ABSL_INTERNAL_ASSUME(cond) assert(cond)
-#elif ABSL_HAVE_BUILTIN(__builtin_assume)
-#define ABSL_INTERNAL_ASSUME(cond) __builtin_assume(cond)
-#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable)
-#define ABSL_INTERNAL_ASSUME(cond)        \
-  do {                                    \
-    if (!(cond)) __builtin_unreachable(); \
-  } while (0)
-#elif defined(_MSC_VER)
-#define ABSL_INTERNAL_ASSUME(cond) __assume(cond)
-#else
-#define ABSL_INTERNAL_ASSUME(cond)      \
-  do {                                  \
-    static_cast<void>(false && (cond)); \
-  } while (0)
-#endif
-
-// ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond)
-// This macro forces small unique name on a static file level symbols like
-// static local variables or static functions. This is intended to be used in
-// macro definitions to optimize the cost of generated code. Do NOT use it on
-// symbols exported from translation unit since it may casue a link time
-// conflict.
-//
-// Example:
-//
-// #define MY_MACRO(txt)
-// namespace {
-//  char VeryVeryLongVarName[] ABSL_INTERNAL_UNIQUE_SMALL_NAME() = txt;
-//  const char* VeryVeryLongFuncName() ABSL_INTERNAL_UNIQUE_SMALL_NAME();
-//  const char* VeryVeryLongFuncName() { return txt; }
-// }
-//
-
-#if defined(__GNUC__)
-#define ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) #x
-#define ABSL_INTERNAL_UNIQUE_SMALL_NAME1(x) ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x)
-#define ABSL_INTERNAL_UNIQUE_SMALL_NAME() \
-  asm(ABSL_INTERNAL_UNIQUE_SMALL_NAME1(.absl.__COUNTER__))
-#else
-#define ABSL_INTERNAL_UNIQUE_SMALL_NAME()
-#endif
-
-#endif  // ABSL_BASE_OPTIMIZATION_H_
diff --git a/third_party/abseil_cpp/absl/base/optimization_test.cc b/third_party/abseil_cpp/absl/base/optimization_test.cc
deleted file mode 100644
index e83369f322..0000000000
--- a/third_party/abseil_cpp/absl/base/optimization_test.cc
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/optimization.h"
-
-#include "gtest/gtest.h"
-#include "absl/types/optional.h"
-
-namespace {
-
-// Tests for the ABSL_PREDICT_TRUE and ABSL_PREDICT_FALSE macros.
-// The tests only verify that the macros are functionally correct - i.e. code
-// behaves as if they weren't used. They don't try to check their impact on
-// optimization.
-
-TEST(PredictTest, PredictTrue) {
-  EXPECT_TRUE(ABSL_PREDICT_TRUE(true));
-  EXPECT_FALSE(ABSL_PREDICT_TRUE(false));
-  EXPECT_TRUE(ABSL_PREDICT_TRUE(1 == 1));
-  EXPECT_FALSE(ABSL_PREDICT_TRUE(1 == 2));
-
-  if (ABSL_PREDICT_TRUE(false)) ADD_FAILURE();
-  if (!ABSL_PREDICT_TRUE(true)) ADD_FAILURE();
-
-  EXPECT_TRUE(ABSL_PREDICT_TRUE(true) && true);
-  EXPECT_TRUE(ABSL_PREDICT_TRUE(true) || false);
-}
-
-TEST(PredictTest, PredictFalse) {
-  EXPECT_TRUE(ABSL_PREDICT_FALSE(true));
-  EXPECT_FALSE(ABSL_PREDICT_FALSE(false));
-  EXPECT_TRUE(ABSL_PREDICT_FALSE(1 == 1));
-  EXPECT_FALSE(ABSL_PREDICT_FALSE(1 == 2));
-
-  if (ABSL_PREDICT_FALSE(false)) ADD_FAILURE();
-  if (!ABSL_PREDICT_FALSE(true)) ADD_FAILURE();
-
-  EXPECT_TRUE(ABSL_PREDICT_FALSE(true) && true);
-  EXPECT_TRUE(ABSL_PREDICT_FALSE(true) || false);
-}
-
-TEST(PredictTest, OneEvaluation) {
-  // Verify that the expression is only evaluated once.
-  int x = 0;
-  if (ABSL_PREDICT_TRUE((++x) == 0)) ADD_FAILURE();
-  EXPECT_EQ(x, 1);
-  if (ABSL_PREDICT_FALSE((++x) == 0)) ADD_FAILURE();
-  EXPECT_EQ(x, 2);
-}
-
-TEST(PredictTest, OperatorOrder) {
-  // Verify that operator order inside and outside the macro behaves well.
-  // These would fail for a naive '#define ABSL_PREDICT_TRUE(x) x'
-  EXPECT_TRUE(ABSL_PREDICT_TRUE(1 && 2) == true);
-  EXPECT_TRUE(ABSL_PREDICT_FALSE(1 && 2) == true);
-  EXPECT_TRUE(!ABSL_PREDICT_TRUE(1 == 2));
-  EXPECT_TRUE(!ABSL_PREDICT_FALSE(1 == 2));
-}
-
-TEST(PredictTest, Pointer) {
-  const int x = 3;
-  const int *good_intptr = &x;
-  const int *null_intptr = nullptr;
-  EXPECT_TRUE(ABSL_PREDICT_TRUE(good_intptr));
-  EXPECT_FALSE(ABSL_PREDICT_TRUE(null_intptr));
-  EXPECT_TRUE(ABSL_PREDICT_FALSE(good_intptr));
-  EXPECT_FALSE(ABSL_PREDICT_FALSE(null_intptr));
-}
-
-TEST(PredictTest, Optional) {
-  // Note: An optional's truth value is the value's existence, not its truth.
-  absl::optional<bool> has_value(false);
-  absl::optional<bool> no_value;
-  EXPECT_TRUE(ABSL_PREDICT_TRUE(has_value));
-  EXPECT_FALSE(ABSL_PREDICT_TRUE(no_value));
-  EXPECT_TRUE(ABSL_PREDICT_FALSE(has_value));
-  EXPECT_FALSE(ABSL_PREDICT_FALSE(no_value));
-}
-
-class ImplictlyConvertibleToBool {
- public:
-  explicit ImplictlyConvertibleToBool(bool value) : value_(value) {}
-  operator bool() const {  // NOLINT(google-explicit-constructor)
-    return value_;
-  }
-
- private:
-  bool value_;
-};
-
-TEST(PredictTest, ImplicitBoolConversion) {
-  const ImplictlyConvertibleToBool is_true(true);
-  const ImplictlyConvertibleToBool is_false(false);
-  if (!ABSL_PREDICT_TRUE(is_true)) ADD_FAILURE();
-  if (ABSL_PREDICT_TRUE(is_false)) ADD_FAILURE();
-  if (!ABSL_PREDICT_FALSE(is_true)) ADD_FAILURE();
-  if (ABSL_PREDICT_FALSE(is_false)) ADD_FAILURE();
-}
-
-class ExplictlyConvertibleToBool {
- public:
-  explicit ExplictlyConvertibleToBool(bool value) : value_(value) {}
-  explicit operator bool() const { return value_; }
-
- private:
-  bool value_;
-};
-
-TEST(PredictTest, ExplicitBoolConversion) {
-  const ExplictlyConvertibleToBool is_true(true);
-  const ExplictlyConvertibleToBool is_false(false);
-  if (!ABSL_PREDICT_TRUE(is_true)) ADD_FAILURE();
-  if (ABSL_PREDICT_TRUE(is_false)) ADD_FAILURE();
-  if (!ABSL_PREDICT_FALSE(is_true)) ADD_FAILURE();
-  if (ABSL_PREDICT_FALSE(is_false)) ADD_FAILURE();
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/options.h b/third_party/abseil_cpp/absl/base/options.h
deleted file mode 100644
index 230bf1eecc..0000000000
--- a/third_party/abseil_cpp/absl/base/options.h
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: options.h
-// -----------------------------------------------------------------------------
-//
-// This file contains Abseil configuration options for setting specific
-// implementations instead of letting Abseil determine which implementation to
-// use at compile-time. Setting these options may be useful for package or build
-// managers who wish to guarantee ABI stability within binary builds (which are
-// otherwise difficult to enforce).
-//
-// *** IMPORTANT NOTICE FOR PACKAGE MANAGERS:  It is important that
-// maintainers of package managers who wish to package Abseil read and
-// understand this file! ***
-//
-// Abseil contains a number of possible configuration endpoints, based on
-// parameters such as the detected platform, language version, or command-line
-// flags used to invoke the underlying binary. As is the case with all
-// libraries, binaries which contain Abseil code must ensure that separate
-// packages use the same compiled copy of Abseil to avoid a diamond dependency
-// problem, which can occur if two packages built with different Abseil
-// configuration settings are linked together. Diamond dependency problems in
-// C++ may manifest as violations to the One Definition Rule (ODR) (resulting in
-// linker errors), or undefined behavior (resulting in crashes).
-//
-// Diamond dependency problems can be avoided if all packages utilize the same
-// exact version of Abseil. Building from source code with the same compilation
-// parameters is the easiest way to avoid such dependency problems. However, for
-// package managers who cannot control such compilation parameters, we are
-// providing the file to allow you to inject ABI (Application Binary Interface)
-// stability across builds. Settings options in this file will neither change
-// API nor ABI, providing a stable copy of Abseil between packages.
-//
-// Care must be taken to keep options within these configurations isolated
-// from any other dynamic settings, such as command-line flags which could alter
-// these options. This file is provided specifically to help build and package
-// managers provide a stable copy of Abseil within their libraries and binaries;
-// other developers should not have need to alter the contents of this file.
-//
-// -----------------------------------------------------------------------------
-// Usage
-// -----------------------------------------------------------------------------
-//
-// For any particular package release, set the appropriate definitions within
-// this file to whatever value makes the most sense for your package(s). Note
-// that, by default, most of these options, at the moment, affect the
-// implementation of types; future options may affect other implementation
-// details.
-//
-// NOTE: the defaults within this file all assume that Abseil can select the
-// proper Abseil implementation at compile-time, which will not be sufficient
-// to guarantee ABI stability to package managers.
-
-#ifndef ABSL_BASE_OPTIONS_H_
-#define ABSL_BASE_OPTIONS_H_
-
-// Include a standard library header to allow configuration based on the
-// standard library in use.
-#ifdef __cplusplus
-#include <ciso646>
-#endif
-
-// -----------------------------------------------------------------------------
-// Type Compatibility Options
-// -----------------------------------------------------------------------------
-//
-// ABSL_OPTION_USE_STD_ANY
-//
-// This option controls whether absl::any is implemented as an alias to
-// std::any, or as an independent implementation.
-//
-// A value of 0 means to use Abseil's implementation.  This requires only C++11
-// support, and is expected to work on every toolchain we support.
-//
-// A value of 1 means to use an alias to std::any.  This requires that all code
-// using Abseil is built in C++17 mode or later.
-//
-// A value of 2 means to detect the C++ version being used to compile Abseil,
-// and use an alias only if a working std::any is available.  This option is
-// useful when you are building your entire program, including all of its
-// dependencies, from source.  It should not be used otherwise -- for example,
-// if you are distributing Abseil in a binary package manager -- since in
-// mode 2, absl::any will name a different type, with a different mangled name
-// and binary layout, depending on the compiler flags passed by the end user.
-// For more info, see https://abseil.io/about/design/dropin-types.
-//
-// User code should not inspect this macro.  To check in the preprocessor if
-// absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY.
-
-#define ABSL_OPTION_USE_STD_ANY 2
-
-
-// ABSL_OPTION_USE_STD_OPTIONAL
-//
-// This option controls whether absl::optional is implemented as an alias to
-// std::optional, or as an independent implementation.
-//
-// A value of 0 means to use Abseil's implementation.  This requires only C++11
-// support, and is expected to work on every toolchain we support.
-//
-// A value of 1 means to use an alias to std::optional.  This requires that all
-// code using Abseil is built in C++17 mode or later.
-//
-// A value of 2 means to detect the C++ version being used to compile Abseil,
-// and use an alias only if a working std::optional is available.  This option
-// is useful when you are building your program from source.  It should not be
-// used otherwise -- for example, if you are distributing Abseil in a binary
-// package manager -- since in mode 2, absl::optional will name a different
-// type, with a different mangled name and binary layout, depending on the
-// compiler flags passed by the end user.  For more info, see
-// https://abseil.io/about/design/dropin-types.
-
-// User code should not inspect this macro.  To check in the preprocessor if
-// absl::optional is a typedef of std::optional, use the feature macro
-// ABSL_USES_STD_OPTIONAL.
-
-#define ABSL_OPTION_USE_STD_OPTIONAL 2
-
-
-// ABSL_OPTION_USE_STD_STRING_VIEW
-//
-// This option controls whether absl::string_view is implemented as an alias to
-// std::string_view, or as an independent implementation.
-//
-// A value of 0 means to use Abseil's implementation.  This requires only C++11
-// support, and is expected to work on every toolchain we support.
-//
-// A value of 1 means to use an alias to std::string_view.  This requires that
-// all code using Abseil is built in C++17 mode or later.
-//
-// A value of 2 means to detect the C++ version being used to compile Abseil,
-// and use an alias only if a working std::string_view is available.  This
-// option is useful when you are building your program from source.  It should
-// not be used otherwise -- for example, if you are distributing Abseil in a
-// binary package manager -- since in mode 2, absl::string_view will name a
-// different type, with a different mangled name and binary layout, depending on
-// the compiler flags passed by the end user.  For more info, see
-// https://abseil.io/about/design/dropin-types.
-//
-// User code should not inspect this macro.  To check in the preprocessor if
-// absl::string_view is a typedef of std::string_view, use the feature macro
-// ABSL_USES_STD_STRING_VIEW.
-
-#define ABSL_OPTION_USE_STD_STRING_VIEW 2
-
-// ABSL_OPTION_USE_STD_VARIANT
-//
-// This option controls whether absl::variant is implemented as an alias to
-// std::variant, or as an independent implementation.
-//
-// A value of 0 means to use Abseil's implementation.  This requires only C++11
-// support, and is expected to work on every toolchain we support.
-//
-// A value of 1 means to use an alias to std::variant.  This requires that all
-// code using Abseil is built in C++17 mode or later.
-//
-// A value of 2 means to detect the C++ version being used to compile Abseil,
-// and use an alias only if a working std::variant is available.  This option
-// is useful when you are building your program from source.  It should not be
-// used otherwise -- for example, if you are distributing Abseil in a binary
-// package manager -- since in mode 2, absl::variant will name a different
-// type, with a different mangled name and binary layout, depending on the
-// compiler flags passed by the end user.  For more info, see
-// https://abseil.io/about/design/dropin-types.
-//
-// User code should not inspect this macro.  To check in the preprocessor if
-// absl::variant is a typedef of std::variant, use the feature macro
-// ABSL_USES_STD_VARIANT.
-
-#define ABSL_OPTION_USE_STD_VARIANT 2
-
-
-// ABSL_OPTION_USE_INLINE_NAMESPACE
-// ABSL_OPTION_INLINE_NAMESPACE_NAME
-//
-// These options controls whether all entities in the absl namespace are
-// contained within an inner inline namespace.  This does not affect the
-// user-visible API of Abseil, but it changes the mangled names of all symbols.
-//
-// This can be useful as a version tag if you are distributing Abseil in
-// precompiled form.  This will prevent a binary library build of Abseil with
-// one inline namespace being used with headers configured with a different
-// inline namespace name.  Binary packagers are reminded that Abseil does not
-// guarantee any ABI stability in Abseil, so any update of Abseil or
-// configuration change in such a binary package should be combined with a
-// new, unique value for the inline namespace name.
-//
-// A value of 0 means not to use inline namespaces.
-//
-// A value of 1 means to use an inline namespace with the given name inside
-// namespace absl.  If this is set, ABSL_OPTION_INLINE_NAMESPACE_NAME must also
-// be changed to a new, unique identifier name.  In particular "head" is not
-// allowed.
-
-#define ABSL_OPTION_USE_INLINE_NAMESPACE 0
-#define ABSL_OPTION_INLINE_NAMESPACE_NAME head
-
-// ABSL_OPTION_HARDENED
-//
-// This option enables a "hardened" build in release mode (in this context,
-// release mode is defined as a build where the `NDEBUG` macro is defined).
-//
-// A value of 0 means that "hardened" mode is not enabled.
-//
-// A value of 1 means that "hardened" mode is enabled.
-//
-// Hardened builds have additional security checks enabled when `NDEBUG` is
-// defined. Defining `NDEBUG` is normally used to turn `assert()` macro into a
-// no-op, as well as disabling other bespoke program consistency checks. By
-// defining ABSL_OPTION_HARDENED to 1, a select set of checks remain enabled in
-// release mode. These checks guard against programming errors that may lead to
-// security vulnerabilities. In release mode, when one of these programming
-// errors is encountered, the program will immediately abort, possibly without
-// any attempt at logging.
-//
-// The checks enabled by this option are not free; they do incur runtime cost.
-//
-// The checks enabled by this option are always active when `NDEBUG` is not
-// defined, even in the case when ABSL_OPTION_HARDENED is defined to 0. The
-// checks enabled by this option may abort the program in a different way and
-// log additional information when `NDEBUG` is not defined.
-
-#define ABSL_OPTION_HARDENED 0
-
-#endif  // ABSL_BASE_OPTIONS_H_
diff --git a/third_party/abseil_cpp/absl/base/policy_checks.h b/third_party/abseil_cpp/absl/base/policy_checks.h
deleted file mode 100644
index 06b3243916..0000000000
--- a/third_party/abseil_cpp/absl/base/policy_checks.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: policy_checks.h
-// -----------------------------------------------------------------------------
-//
-// This header enforces a minimum set of policies at build time, such as the
-// supported compiler and library versions. Unsupported configurations are
-// reported with `#error`. This enforcement is best effort, so successfully
-// compiling this header does not guarantee a supported configuration.
-
-#ifndef ABSL_BASE_POLICY_CHECKS_H_
-#define ABSL_BASE_POLICY_CHECKS_H_
-
-// Included for the __GLIBC_PREREQ macro used below.
-#include <limits.h>
-
-// Included for the _STLPORT_VERSION macro used below.
-#if defined(__cplusplus)
-#include <cstddef>
-#endif
-
-// -----------------------------------------------------------------------------
-// Operating System Check
-// -----------------------------------------------------------------------------
-
-#if defined(__CYGWIN__)
-#error "Cygwin is not supported."
-#endif
-
-// -----------------------------------------------------------------------------
-// Toolchain Check
-// -----------------------------------------------------------------------------
-
-// We support MSVC++ 14.0 update 2 and later.
-// This minimum will go up.
-#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__)
-#error "This package requires Visual Studio 2015 Update 2 or higher."
-#endif
-
-// We support gcc 4.7 and later.
-// This minimum will go up.
-#if defined(__GNUC__) && !defined(__clang__)
-#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
-#error "This package requires gcc 4.7 or higher."
-#endif
-#endif
-
-// We support Apple Xcode clang 4.2.1 (version 421.11.65) and later.
-// This corresponds to Apple Xcode version 4.5.
-// This minimum will go up.
-#if defined(__apple_build_version__) && __apple_build_version__ < 4211165
-#error "This package requires __apple_build_version__ of 4211165 or higher."
-#endif
-
-// -----------------------------------------------------------------------------
-// C++ Version Check
-// -----------------------------------------------------------------------------
-
-// Enforce C++11 as the minimum.  Note that Visual Studio has not
-// advanced __cplusplus despite being good enough for our purposes, so
-// so we exempt it from the check.
-#if defined(__cplusplus) && !defined(_MSC_VER)
-#if __cplusplus < 201103L
-#error "C++ versions less than C++11 are not supported."
-#endif
-#endif
-
-// -----------------------------------------------------------------------------
-// Standard Library Check
-// -----------------------------------------------------------------------------
-
-#if defined(_STLPORT_VERSION)
-#error "STLPort is not supported."
-#endif
-
-// -----------------------------------------------------------------------------
-// `char` Size Check
-// -----------------------------------------------------------------------------
-
-// Abseil currently assumes CHAR_BIT == 8. If you would like to use Abseil on a
-// platform where this is not the case, please provide us with the details about
-// your platform so we can consider relaxing this requirement.
-#if CHAR_BIT != 8
-#error "Abseil assumes CHAR_BIT == 8."
-#endif
-
-// -----------------------------------------------------------------------------
-// `int` Size Check
-// -----------------------------------------------------------------------------
-
-// Abseil currently assumes that an int is 4 bytes. If you would like to use
-// Abseil on a platform where this is not the case, please provide us with the
-// details about your platform so we can consider relaxing this requirement.
-#if INT_MAX < 2147483647
-#error "Abseil assumes that int is at least 4 bytes. "
-#endif
-
-#endif  // ABSL_BASE_POLICY_CHECKS_H_
diff --git a/third_party/abseil_cpp/absl/base/port.h b/third_party/abseil_cpp/absl/base/port.h
deleted file mode 100644
index 6c28068d4f..0000000000
--- a/third_party/abseil_cpp/absl/base/port.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This files is a forwarding header for other headers containing various
-// portability macros and functions.
-// This file is used for both C and C++!
-
-#ifndef ABSL_BASE_PORT_H_
-#define ABSL_BASE_PORT_H_
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/optimization.h"
-
-#endif  // ABSL_BASE_PORT_H_
diff --git a/third_party/abseil_cpp/absl/base/raw_logging_test.cc b/third_party/abseil_cpp/absl/base/raw_logging_test.cc
deleted file mode 100644
index 3d30bd3861..0000000000
--- a/third_party/abseil_cpp/absl/base/raw_logging_test.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This test serves primarily as a compilation test for base/raw_logging.h.
-// Raw logging testing is covered by logging_unittest.cc, which is not as
-// portable as this test.
-
-#include "absl/base/internal/raw_logging.h"
-
-#include <tuple>
-
-#include "gtest/gtest.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-TEST(RawLoggingCompilationTest, Log) {
-  ABSL_RAW_LOG(INFO, "RAW INFO: %d", 1);
-  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d", 1, 2);
-  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d", 1, 2, 3);
-  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d", 1, 2, 3, 4);
-  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d %d", 1, 2, 3, 4, 5);
-  ABSL_RAW_LOG(WARNING, "RAW WARNING: %d", 1);
-  ABSL_RAW_LOG(ERROR, "RAW ERROR: %d", 1);
-}
-
-TEST(RawLoggingCompilationTest, PassingCheck) {
-  ABSL_RAW_CHECK(true, "RAW CHECK");
-}
-
-// Not all platforms support output from raw log, so we don't verify any
-// particular output for RAW check failures (expecting the empty string
-// accomplishes this).  This test is primarily a compilation test, but we
-// are verifying process death when EXPECT_DEATH works for a platform.
-const char kExpectedDeathOutput[] = "";
-
-TEST(RawLoggingDeathTest, FailingCheck) {
-  EXPECT_DEATH_IF_SUPPORTED(ABSL_RAW_CHECK(1 == 0, "explanation"),
-                            kExpectedDeathOutput);
-}
-
-TEST(RawLoggingDeathTest, LogFatal) {
-  EXPECT_DEATH_IF_SUPPORTED(ABSL_RAW_LOG(FATAL, "my dog has fleas"),
-                            kExpectedDeathOutput);
-}
-
-TEST(InternalLog, CompilationTest) {
-  ABSL_INTERNAL_LOG(INFO, "Internal Log");
-  std::string log_msg = "Internal Log";
-  ABSL_INTERNAL_LOG(INFO, log_msg);
-
-  ABSL_INTERNAL_LOG(INFO, log_msg + " 2");
-
-  float d = 1.1f;
-  ABSL_INTERNAL_LOG(INFO, absl::StrCat("Internal log ", 3, " + ", d));
-}
-
-TEST(InternalLogDeathTest, FailingCheck) {
-  EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_CHECK(1 == 0, "explanation"),
-                            kExpectedDeathOutput);
-}
-
-TEST(InternalLogDeathTest, LogFatal) {
-  EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_LOG(FATAL, "my dog has fleas"),
-                            kExpectedDeathOutput);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/base/spinlock_test_common.cc b/third_party/abseil_cpp/absl/base/spinlock_test_common.cc
deleted file mode 100644
index dee266e4f4..0000000000
--- a/third_party/abseil_cpp/absl/base/spinlock_test_common.cc
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// A bunch of threads repeatedly hash an array of ints protected by a
-// spinlock.  If the spinlock is working properly, all elements of the
-// array should be equal at the end of the test.
-
-#include <cstdint>
-#include <limits>
-#include <random>
-#include <thread>  // NOLINT(build/c++11)
-#include <type_traits>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/low_level_scheduling.h"
-#include "absl/base/internal/scheduling_mode.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/base/internal/sysinfo.h"
-#include "absl/base/macros.h"
-#include "absl/synchronization/blocking_counter.h"
-#include "absl/synchronization/notification.h"
-
-constexpr int32_t kNumThreads = 10;
-constexpr int32_t kIters = 1000;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// This is defined outside of anonymous namespace so that it can be
-// a friend of SpinLock to access protected methods for testing.
-struct SpinLockTest {
-  static uint32_t EncodeWaitCycles(int64_t wait_start_time,
-                                   int64_t wait_end_time) {
-    return SpinLock::EncodeWaitCycles(wait_start_time, wait_end_time);
-  }
-  static uint64_t DecodeWaitCycles(uint32_t lock_value) {
-    return SpinLock::DecodeWaitCycles(lock_value);
-  }
-};
-
-namespace {
-
-static constexpr int kArrayLength = 10;
-static uint32_t values[kArrayLength];
-
-ABSL_CONST_INIT static SpinLock static_cooperative_spinlock(
-    absl::kConstInit, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
-ABSL_CONST_INIT static SpinLock static_noncooperative_spinlock(
-    absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
-
-// Simple integer hash function based on the public domain lookup2 hash.
-// http://burtleburtle.net/bob/c/lookup2.c
-static uint32_t Hash32(uint32_t a, uint32_t c) {
-  uint32_t b = 0x9e3779b9UL;  // The golden ratio; an arbitrary value.
-  a -= b; a -= c; a ^= (c >> 13);
-  b -= c; b -= a; b ^= (a << 8);
-  c -= a; c -= b; c ^= (b >> 13);
-  a -= b; a -= c; a ^= (c >> 12);
-  b -= c; b -= a; b ^= (a << 16);
-  c -= a; c -= b; c ^= (b >> 5);
-  a -= b; a -= c; a ^= (c >> 3);
-  b -= c; b -= a; b ^= (a << 10);
-  c -= a; c -= b; c ^= (b >> 15);
-  return c;
-}
-
-static void TestFunction(int thread_salt, SpinLock* spinlock) {
-  for (int i = 0; i < kIters; i++) {
-    SpinLockHolder h(spinlock);
-    for (int j = 0; j < kArrayLength; j++) {
-      const int index = (j + thread_salt) % kArrayLength;
-      values[index] = Hash32(values[index], thread_salt);
-      std::this_thread::yield();
-    }
-  }
-}
-
-static void ThreadedTest(SpinLock* spinlock) {
-  std::vector<std::thread> threads;
-  for (int i = 0; i < kNumThreads; ++i) {
-    threads.push_back(std::thread(TestFunction, i, spinlock));
-  }
-  for (auto& thread : threads) {
-    thread.join();
-  }
-
-  SpinLockHolder h(spinlock);
-  for (int i = 1; i < kArrayLength; i++) {
-    EXPECT_EQ(values[0], values[i]);
-  }
-}
-
-#ifndef ABSL_HAVE_THREAD_SANITIZER
-static_assert(std::is_trivially_destructible<SpinLock>(), "");
-#endif
-
-TEST(SpinLock, StackNonCooperativeDisablesScheduling) {
-  SpinLock spinlock(base_internal::SCHEDULE_KERNEL_ONLY);
-  spinlock.Lock();
-  EXPECT_FALSE(base_internal::SchedulingGuard::ReschedulingIsAllowed());
-  spinlock.Unlock();
-}
-
-TEST(SpinLock, StaticNonCooperativeDisablesScheduling) {
-  static_noncooperative_spinlock.Lock();
-  EXPECT_FALSE(base_internal::SchedulingGuard::ReschedulingIsAllowed());
-  static_noncooperative_spinlock.Unlock();
-}
-
-TEST(SpinLock, WaitCyclesEncoding) {
-  // These are implementation details not exported by SpinLock.
-  const int kProfileTimestampShift = 7;
-  const int kLockwordReservedShift = 3;
-  const uint32_t kSpinLockSleeper = 8;
-
-  // We should be able to encode up to (1^kMaxCycleBits - 1) without clamping
-  // but the lower kProfileTimestampShift will be dropped.
-  const int kMaxCyclesShift =
-    32 - kLockwordReservedShift + kProfileTimestampShift;
-  const uint64_t kMaxCycles = (int64_t{1} << kMaxCyclesShift) - 1;
-
-  // These bits should be zero after encoding.
-  const uint32_t kLockwordReservedMask = (1 << kLockwordReservedShift) - 1;
-
-  // These bits are dropped when wait cycles are encoded.
-  const uint64_t kProfileTimestampMask = (1 << kProfileTimestampShift) - 1;
-
-  // Test a bunch of random values
-  std::default_random_engine generator;
-  // Shift to avoid overflow below.
-  std::uniform_int_distribution<uint64_t> time_distribution(
-      0, std::numeric_limits<uint64_t>::max() >> 4);
-  std::uniform_int_distribution<uint64_t> cycle_distribution(0, kMaxCycles);
-
-  for (int i = 0; i < 100; i++) {
-    int64_t start_time = time_distribution(generator);
-    int64_t cycles = cycle_distribution(generator);
-    int64_t end_time = start_time + cycles;
-    uint32_t lock_value = SpinLockTest::EncodeWaitCycles(start_time, end_time);
-    EXPECT_EQ(0, lock_value & kLockwordReservedMask);
-    uint64_t decoded = SpinLockTest::DecodeWaitCycles(lock_value);
-    EXPECT_EQ(0, decoded & kProfileTimestampMask);
-    EXPECT_EQ(cycles & ~kProfileTimestampMask, decoded);
-  }
-
-  // Test corner cases
-  int64_t start_time = time_distribution(generator);
-  EXPECT_EQ(kSpinLockSleeper,
-            SpinLockTest::EncodeWaitCycles(start_time, start_time));
-  EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(0));
-  EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(kLockwordReservedMask));
-  EXPECT_EQ(kMaxCycles & ~kProfileTimestampMask,
-            SpinLockTest::DecodeWaitCycles(~kLockwordReservedMask));
-
-  // Check that we cannot produce kSpinLockSleeper during encoding.
-  int64_t sleeper_cycles =
-      kSpinLockSleeper << (kProfileTimestampShift - kLockwordReservedShift);
-  uint32_t sleeper_value =
-      SpinLockTest::EncodeWaitCycles(start_time, start_time + sleeper_cycles);
-  EXPECT_NE(sleeper_value, kSpinLockSleeper);
-
-  // Test clamping
-  uint32_t max_value =
-    SpinLockTest::EncodeWaitCycles(start_time, start_time + kMaxCycles);
-  uint64_t max_value_decoded = SpinLockTest::DecodeWaitCycles(max_value);
-  uint64_t expected_max_value_decoded = kMaxCycles & ~kProfileTimestampMask;
-  EXPECT_EQ(expected_max_value_decoded, max_value_decoded);
-
-  const int64_t step = (1 << kProfileTimestampShift);
-  uint32_t after_max_value =
-    SpinLockTest::EncodeWaitCycles(start_time, start_time + kMaxCycles + step);
-  uint64_t after_max_value_decoded =
-      SpinLockTest::DecodeWaitCycles(after_max_value);
-  EXPECT_EQ(expected_max_value_decoded, after_max_value_decoded);
-
-  uint32_t before_max_value = SpinLockTest::EncodeWaitCycles(
-      start_time, start_time + kMaxCycles - step);
-  uint64_t before_max_value_decoded =
-    SpinLockTest::DecodeWaitCycles(before_max_value);
-  EXPECT_GT(expected_max_value_decoded, before_max_value_decoded);
-}
-
-TEST(SpinLockWithThreads, StackSpinLock) {
-  SpinLock spinlock;
-  ThreadedTest(&spinlock);
-}
-
-TEST(SpinLockWithThreads, StackCooperativeSpinLock) {
-  SpinLock spinlock(base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
-  ThreadedTest(&spinlock);
-}
-
-TEST(SpinLockWithThreads, StackNonCooperativeSpinLock) {
-  SpinLock spinlock(base_internal::SCHEDULE_KERNEL_ONLY);
-  ThreadedTest(&spinlock);
-}
-
-TEST(SpinLockWithThreads, StaticCooperativeSpinLock) {
-  ThreadedTest(&static_cooperative_spinlock);
-}
-
-TEST(SpinLockWithThreads, StaticNonCooperativeSpinLock) {
-  ThreadedTest(&static_noncooperative_spinlock);
-}
-
-TEST(SpinLockWithThreads, DoesNotDeadlock) {
-  struct Helper {
-    static void NotifyThenLock(Notification* locked, SpinLock* spinlock,
-                               BlockingCounter* b) {
-      locked->WaitForNotification();  // Wait for LockThenWait() to hold "s".
-      b->DecrementCount();
-      SpinLockHolder l(spinlock);
-    }
-
-    static void LockThenWait(Notification* locked, SpinLock* spinlock,
-                             BlockingCounter* b) {
-      SpinLockHolder l(spinlock);
-      locked->Notify();
-      b->Wait();
-    }
-
-    static void DeadlockTest(SpinLock* spinlock, int num_spinners) {
-      Notification locked;
-      BlockingCounter counter(num_spinners);
-      std::vector<std::thread> threads;
-
-      threads.push_back(
-          std::thread(Helper::LockThenWait, &locked, spinlock, &counter));
-      for (int i = 0; i < num_spinners; ++i) {
-        threads.push_back(
-            std::thread(Helper::NotifyThenLock, &locked, spinlock, &counter));
-      }
-
-      for (auto& thread : threads) {
-        thread.join();
-      }
-    }
-  };
-
-  SpinLock stack_cooperative_spinlock(
-      base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
-  SpinLock stack_noncooperative_spinlock(base_internal::SCHEDULE_KERNEL_ONLY);
-  Helper::DeadlockTest(&stack_cooperative_spinlock,
-                       base_internal::NumCPUs() * 2);
-  Helper::DeadlockTest(&stack_noncooperative_spinlock,
-                       base_internal::NumCPUs() * 2);
-  Helper::DeadlockTest(&static_cooperative_spinlock,
-                       base_internal::NumCPUs() * 2);
-  Helper::DeadlockTest(&static_noncooperative_spinlock,
-                       base_internal::NumCPUs() * 2);
-}
-
-}  // namespace
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/base/thread_annotations.h b/third_party/abseil_cpp/absl/base/thread_annotations.h
deleted file mode 100644
index e23fff1d20..0000000000
--- a/third_party/abseil_cpp/absl/base/thread_annotations.h
+++ /dev/null
@@ -1,335 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: thread_annotations.h
-// -----------------------------------------------------------------------------
-//
-// This header file contains macro definitions for thread safety annotations
-// that allow developers to document the locking policies of multi-threaded
-// code. The annotations can also help program analysis tools to identify
-// potential thread safety issues.
-//
-// These annotations are implemented using compiler attributes. Using the macros
-// defined here instead of raw attributes allow for portability and future
-// compatibility.
-//
-// When referring to mutexes in the arguments of the attributes, you should
-// use variable names or more complex expressions (e.g. my_object->mutex_)
-// that evaluate to a concrete mutex object whenever possible. If the mutex
-// you want to refer to is not in scope, you may use a member pointer
-// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object.
-
-#ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_
-#define ABSL_BASE_THREAD_ANNOTATIONS_H_
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-// TODO(mbonadei): Remove after the backward compatibility period.
-#include "absl/base/internal/thread_annotations.h"  // IWYU pragma: export
-
-// ABSL_GUARDED_BY()
-//
-// Documents if a shared field or global variable needs to be protected by a
-// mutex. ABSL_GUARDED_BY() allows the user to specify a particular mutex that
-// should be held when accessing the annotated variable.
-//
-// Although this annotation (and ABSL_PT_GUARDED_BY, below) cannot be applied to
-// local variables, a local variable and its associated mutex can often be
-// combined into a small class or struct, thereby allowing the annotation.
-//
-// Example:
-//
-//   class Foo {
-//     Mutex mu_;
-//     int p1_ ABSL_GUARDED_BY(mu_);
-//     ...
-//   };
-#if ABSL_HAVE_ATTRIBUTE(guarded_by)
-#define ABSL_GUARDED_BY(x) __attribute__((guarded_by(x)))
-#else
-#define ABSL_GUARDED_BY(x)
-#endif
-
-// ABSL_PT_GUARDED_BY()
-//
-// Documents if the memory location pointed to by a pointer should be guarded
-// by a mutex when dereferencing the pointer.
-//
-// Example:
-//   class Foo {
-//     Mutex mu_;
-//     int *p1_ ABSL_PT_GUARDED_BY(mu_);
-//     ...
-//   };
-//
-// Note that a pointer variable to a shared memory location could itself be a
-// shared variable.
-//
-// Example:
-//
-//   // `q_`, guarded by `mu1_`, points to a shared memory location that is
-//   // guarded by `mu2_`:
-//   int *q_ ABSL_GUARDED_BY(mu1_) ABSL_PT_GUARDED_BY(mu2_);
-#if ABSL_HAVE_ATTRIBUTE(pt_guarded_by)
-#define ABSL_PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x)))
-#else
-#define ABSL_PT_GUARDED_BY(x)
-#endif
-
-// ABSL_ACQUIRED_AFTER() / ABSL_ACQUIRED_BEFORE()
-//
-// Documents the acquisition order between locks that can be held
-// simultaneously by a thread. For any two locks that need to be annotated
-// to establish an acquisition order, only one of them needs the annotation.
-// (i.e. You don't have to annotate both locks with both ABSL_ACQUIRED_AFTER
-// and ABSL_ACQUIRED_BEFORE.)
-//
-// As with ABSL_GUARDED_BY, this is only applicable to mutexes that are shared
-// fields or global variables.
-//
-// Example:
-//
-//   Mutex m1_;
-//   Mutex m2_ ABSL_ACQUIRED_AFTER(m1_);
-#if ABSL_HAVE_ATTRIBUTE(acquired_after)
-#define ABSL_ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
-#else
-#define ABSL_ACQUIRED_AFTER(...)
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(acquired_before)
-#define ABSL_ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
-#else
-#define ABSL_ACQUIRED_BEFORE(...)
-#endif
-
-// ABSL_EXCLUSIVE_LOCKS_REQUIRED() / ABSL_SHARED_LOCKS_REQUIRED()
-//
-// Documents a function that expects a mutex to be held prior to entry.
-// The mutex is expected to be held both on entry to, and exit from, the
-// function.
-//
-// An exclusive lock allows read-write access to the guarded data member(s), and
-// only one thread can acquire a lock exclusively at any one time. A shared lock
-// allows read-only access, and any number of threads can acquire a shared lock
-// concurrently.
-//
-// Generally, non-const methods should be annotated with
-// ABSL_EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with
-// ABSL_SHARED_LOCKS_REQUIRED.
-//
-// Example:
-//
-//   Mutex mu1, mu2;
-//   int a ABSL_GUARDED_BY(mu1);
-//   int b ABSL_GUARDED_BY(mu2);
-//
-//   void foo() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
-//   void bar() const ABSL_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
-#if ABSL_HAVE_ATTRIBUTE(exclusive_locks_required)
-#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) \
-  __attribute__((exclusive_locks_required(__VA_ARGS__)))
-#else
-#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...)
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(shared_locks_required)
-#define ABSL_SHARED_LOCKS_REQUIRED(...) \
-  __attribute__((shared_locks_required(__VA_ARGS__)))
-#else
-#define ABSL_SHARED_LOCKS_REQUIRED(...)
-#endif
-
-// ABSL_LOCKS_EXCLUDED()
-//
-// Documents the locks acquired in the body of the function. These locks
-// cannot be held when calling this function (as Abseil's `Mutex` locks are
-// non-reentrant).
-#if ABSL_HAVE_ATTRIBUTE(locks_excluded)
-#define ABSL_LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__)))
-#else
-#define ABSL_LOCKS_EXCLUDED(...)
-#endif
-
-// ABSL_LOCK_RETURNED()
-//
-// Documents a function that returns a mutex without acquiring it.  For example,
-// a public getter method that returns a pointer to a private mutex should
-// be annotated with ABSL_LOCK_RETURNED.
-#if ABSL_HAVE_ATTRIBUTE(lock_returned)
-#define ABSL_LOCK_RETURNED(x) __attribute__((lock_returned(x)))
-#else
-#define ABSL_LOCK_RETURNED(x)
-#endif
-
-// ABSL_LOCKABLE
-//
-// Documents if a class/type is a lockable type (such as the `Mutex` class).
-#if ABSL_HAVE_ATTRIBUTE(lockable)
-#define ABSL_LOCKABLE __attribute__((lockable))
-#else
-#define ABSL_LOCKABLE
-#endif
-
-// ABSL_SCOPED_LOCKABLE
-//
-// Documents if a class does RAII locking (such as the `MutexLock` class).
-// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is
-// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no
-// arguments; the analysis will assume that the destructor unlocks whatever the
-// constructor locked.
-#if ABSL_HAVE_ATTRIBUTE(scoped_lockable)
-#define ABSL_SCOPED_LOCKABLE __attribute__((scoped_lockable))
-#else
-#define ABSL_SCOPED_LOCKABLE
-#endif
-
-// ABSL_EXCLUSIVE_LOCK_FUNCTION()
-//
-// Documents functions that acquire a lock in the body of a function, and do
-// not release it.
-#if ABSL_HAVE_ATTRIBUTE(exclusive_lock_function)
-#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) \
-  __attribute__((exclusive_lock_function(__VA_ARGS__)))
-#else
-#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...)
-#endif
-
-// ABSL_SHARED_LOCK_FUNCTION()
-//
-// Documents functions that acquire a shared (reader) lock in the body of a
-// function, and do not release it.
-#if ABSL_HAVE_ATTRIBUTE(shared_lock_function)
-#define ABSL_SHARED_LOCK_FUNCTION(...) \
-  __attribute__((shared_lock_function(__VA_ARGS__)))
-#else
-#define ABSL_SHARED_LOCK_FUNCTION(...)
-#endif
-
-// ABSL_UNLOCK_FUNCTION()
-//
-// Documents functions that expect a lock to be held on entry to the function,
-// and release it in the body of the function.
-#if ABSL_HAVE_ATTRIBUTE(unlock_function)
-#define ABSL_UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__)))
-#else
-#define ABSL_UNLOCK_FUNCTION(...)
-#endif
-
-// ABSL_EXCLUSIVE_TRYLOCK_FUNCTION() / ABSL_SHARED_TRYLOCK_FUNCTION()
-//
-// Documents functions that try to acquire a lock, and return success or failure
-// (or a non-boolean value that can be interpreted as a boolean).
-// The first argument should be `true` for functions that return `true` on
-// success, or `false` for functions that return `false` on success. The second
-// argument specifies the mutex that is locked on success. If unspecified, this
-// mutex is assumed to be `this`.
-#if ABSL_HAVE_ATTRIBUTE(exclusive_trylock_function)
-#define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) \
-  __attribute__((exclusive_trylock_function(__VA_ARGS__)))
-#else
-#define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...)
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(shared_trylock_function)
-#define ABSL_SHARED_TRYLOCK_FUNCTION(...) \
-  __attribute__((shared_trylock_function(__VA_ARGS__)))
-#else
-#define ABSL_SHARED_TRYLOCK_FUNCTION(...)
-#endif
-
-// ABSL_ASSERT_EXCLUSIVE_LOCK() / ABSL_ASSERT_SHARED_LOCK()
-//
-// Documents functions that dynamically check to see if a lock is held, and fail
-// if it is not held.
-#if ABSL_HAVE_ATTRIBUTE(assert_exclusive_lock)
-#define ABSL_ASSERT_EXCLUSIVE_LOCK(...) \
-  __attribute__((assert_exclusive_lock(__VA_ARGS__)))
-#else
-#define ABSL_ASSERT_EXCLUSIVE_LOCK(...)
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(assert_shared_lock)
-#define ABSL_ASSERT_SHARED_LOCK(...) \
-  __attribute__((assert_shared_lock(__VA_ARGS__)))
-#else
-#define ABSL_ASSERT_SHARED_LOCK(...)
-#endif
-
-// ABSL_NO_THREAD_SAFETY_ANALYSIS
-//
-// Turns off thread safety checking within the body of a particular function.
-// This annotation is used to mark functions that are known to be correct, but
-// the locking behavior is more complicated than the analyzer can handle.
-#if ABSL_HAVE_ATTRIBUTE(no_thread_safety_analysis)
-#define ABSL_NO_THREAD_SAFETY_ANALYSIS \
-  __attribute__((no_thread_safety_analysis))
-#else
-#define ABSL_NO_THREAD_SAFETY_ANALYSIS
-#endif
-
-//------------------------------------------------------------------------------
-// Tool-Supplied Annotations
-//------------------------------------------------------------------------------
-
-// ABSL_TS_UNCHECKED should be placed around lock expressions that are not valid
-// C++ syntax, but which are present for documentation purposes.  These
-// annotations will be ignored by the analysis.
-#define ABSL_TS_UNCHECKED(x) ""
-
-// ABSL_TS_FIXME is used to mark lock expressions that are not valid C++ syntax.
-// It is used by automated tools to mark and disable invalid expressions.
-// The annotation should either be fixed, or changed to ABSL_TS_UNCHECKED.
-#define ABSL_TS_FIXME(x) ""
-
-// Like ABSL_NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body
-// of a particular function.  However, this attribute is used to mark functions
-// that are incorrect and need to be fixed.  It is used by automated tools to
-// avoid breaking the build when the analysis is updated.
-// Code owners are expected to eventually fix the routine.
-#define ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME ABSL_NO_THREAD_SAFETY_ANALYSIS
-
-// Similar to ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a
-// ABSL_GUARDED_BY annotation that needs to be fixed, because it is producing
-// thread safety warning. It disables the ABSL_GUARDED_BY.
-#define ABSL_GUARDED_BY_FIXME(x)
-
-// Disables warnings for a single read operation.  This can be used to avoid
-// warnings when it is known that the read is not actually involved in a race,
-// but the compiler cannot confirm that.
-#define ABSL_TS_UNCHECKED_READ(x) absl::base_internal::ts_unchecked_read(x)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-// Takes a reference to a guarded data member, and returns an unguarded
-// reference.
-// Do not used this function directly, use ABSL_TS_UNCHECKED_READ instead.
-template <typename T>
-inline const T& ts_unchecked_read(const T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-  return v;
-}
-
-template <typename T>
-inline T& ts_unchecked_read(T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-  return v;
-}
-
-}  // namespace base_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_THREAD_ANNOTATIONS_H_
diff --git a/third_party/abseil_cpp/absl/base/throw_delegate_test.cc b/third_party/abseil_cpp/absl/base/throw_delegate_test.cc
deleted file mode 100644
index 5ba4ce55e6..0000000000
--- a/third_party/abseil_cpp/absl/base/throw_delegate_test.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/throw_delegate.h"
-
-#include <functional>
-#include <new>
-#include <stdexcept>
-
-#include "absl/base/config.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-using absl::base_internal::ThrowStdLogicError;
-using absl::base_internal::ThrowStdInvalidArgument;
-using absl::base_internal::ThrowStdDomainError;
-using absl::base_internal::ThrowStdLengthError;
-using absl::base_internal::ThrowStdOutOfRange;
-using absl::base_internal::ThrowStdRuntimeError;
-using absl::base_internal::ThrowStdRangeError;
-using absl::base_internal::ThrowStdOverflowError;
-using absl::base_internal::ThrowStdUnderflowError;
-using absl::base_internal::ThrowStdBadFunctionCall;
-using absl::base_internal::ThrowStdBadAlloc;
-
-constexpr const char* what_arg = "The quick brown fox jumps over the lazy dog";
-
-template <typename E>
-void ExpectThrowChar(void (*f)(const char*)) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  try {
-    f(what_arg);
-    FAIL() << "Didn't throw";
-  } catch (const E& e) {
-    EXPECT_STREQ(e.what(), what_arg);
-  }
-#else
-  EXPECT_DEATH_IF_SUPPORTED(f(what_arg), what_arg);
-#endif
-}
-
-template <typename E>
-void ExpectThrowString(void (*f)(const std::string&)) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  try {
-    f(what_arg);
-    FAIL() << "Didn't throw";
-  } catch (const E& e) {
-    EXPECT_STREQ(e.what(), what_arg);
-  }
-#else
-  EXPECT_DEATH_IF_SUPPORTED(f(what_arg), what_arg);
-#endif
-}
-
-template <typename E>
-void ExpectThrowNoWhat(void (*f)()) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  try {
-    f();
-    FAIL() << "Didn't throw";
-  } catch (const E& e) {
-  }
-#else
-  EXPECT_DEATH_IF_SUPPORTED(f(), "");
-#endif
-}
-
-TEST(ThrowHelper, Test) {
-  // Not using EXPECT_THROW because we want to check the .what() message too.
-  ExpectThrowChar<std::logic_error>(ThrowStdLogicError);
-  ExpectThrowChar<std::invalid_argument>(ThrowStdInvalidArgument);
-  ExpectThrowChar<std::domain_error>(ThrowStdDomainError);
-  ExpectThrowChar<std::length_error>(ThrowStdLengthError);
-  ExpectThrowChar<std::out_of_range>(ThrowStdOutOfRange);
-  ExpectThrowChar<std::runtime_error>(ThrowStdRuntimeError);
-  ExpectThrowChar<std::range_error>(ThrowStdRangeError);
-  ExpectThrowChar<std::overflow_error>(ThrowStdOverflowError);
-  ExpectThrowChar<std::underflow_error>(ThrowStdUnderflowError);
-
-  ExpectThrowString<std::logic_error>(ThrowStdLogicError);
-  ExpectThrowString<std::invalid_argument>(ThrowStdInvalidArgument);
-  ExpectThrowString<std::domain_error>(ThrowStdDomainError);
-  ExpectThrowString<std::length_error>(ThrowStdLengthError);
-  ExpectThrowString<std::out_of_range>(ThrowStdOutOfRange);
-  ExpectThrowString<std::runtime_error>(ThrowStdRuntimeError);
-  ExpectThrowString<std::range_error>(ThrowStdRangeError);
-  ExpectThrowString<std::overflow_error>(ThrowStdOverflowError);
-  ExpectThrowString<std::underflow_error>(ThrowStdUnderflowError);
-
-  ExpectThrowNoWhat<std::bad_function_call>(ThrowStdBadFunctionCall);
-  ExpectThrowNoWhat<std::bad_alloc>(ThrowStdBadAlloc);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/container/BUILD.bazel b/third_party/abseil_cpp/absl/container/BUILD.bazel
deleted file mode 100644
index 8e72ad0354..0000000000
--- a/third_party/abseil_cpp/absl/container/BUILD.bazel
+++ /dev/null
@@ -1,921 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "compressed_tuple",
-    hdrs = ["internal/compressed_tuple.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/utility",
-    ],
-)
-
-cc_test(
-    name = "compressed_tuple_test",
-    srcs = ["internal/compressed_tuple_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":compressed_tuple",
-        ":test_instance_tracker",
-        "//absl/memory",
-        "//absl/types:any",
-        "//absl/types:optional",
-        "//absl/utility",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "fixed_array",
-    hdrs = ["fixed_array.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":compressed_tuple",
-        "//absl/algorithm",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:dynamic_annotations",
-        "//absl/base:throw_delegate",
-        "//absl/memory",
-    ],
-)
-
-cc_test(
-    name = "fixed_array_test",
-    srcs = ["fixed_array_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":counting_allocator",
-        ":fixed_array",
-        "//absl/base:config",
-        "//absl/base:exception_testing",
-        "//absl/hash:hash_testing",
-        "//absl/memory",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "fixed_array_exception_safety_test",
-    srcs = ["fixed_array_exception_safety_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":fixed_array",
-        "//absl/base:config",
-        "//absl/base:exception_safety_testing",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "fixed_array_benchmark",
-    srcs = ["fixed_array_benchmark.cc"],
-    copts = ABSL_TEST_COPTS + ["$(STACK_FRAME_UNLIMITED)"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    deps = [
-        ":fixed_array",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_library(
-    name = "inlined_vector_internal",
-    hdrs = ["internal/inlined_vector.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":compressed_tuple",
-        "//absl/base:core_headers",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/types:span",
-    ],
-)
-
-cc_library(
-    name = "inlined_vector",
-    hdrs = ["inlined_vector.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":inlined_vector_internal",
-        "//absl/algorithm",
-        "//absl/base:core_headers",
-        "//absl/base:throw_delegate",
-        "//absl/memory",
-    ],
-)
-
-cc_library(
-    name = "counting_allocator",
-    testonly = 1,
-    hdrs = ["internal/counting_allocator.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = ["//absl/base:config"],
-)
-
-cc_test(
-    name = "inlined_vector_test",
-    srcs = ["inlined_vector_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":counting_allocator",
-        ":inlined_vector",
-        ":test_instance_tracker",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:exception_testing",
-        "//absl/base:raw_logging_internal",
-        "//absl/hash:hash_testing",
-        "//absl/memory",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "inlined_vector_benchmark",
-    srcs = ["inlined_vector_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    deps = [
-        ":inlined_vector",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/strings",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "inlined_vector_exception_safety_test",
-    srcs = ["inlined_vector_exception_safety_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":inlined_vector",
-        "//absl/base:config",
-        "//absl/base:exception_safety_testing",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "test_instance_tracker",
-    testonly = 1,
-    srcs = ["internal/test_instance_tracker.cc"],
-    hdrs = ["internal/test_instance_tracker.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = ["//absl/types:compare"],
-)
-
-cc_test(
-    name = "test_instance_tracker_test",
-    srcs = ["internal/test_instance_tracker_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":test_instance_tracker",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-NOTEST_TAGS_NONMOBILE = [
-    "no_test_darwin_x86_64",
-    "no_test_loonix",
-]
-
-NOTEST_TAGS_MOBILE = [
-    "no_test_android_arm",
-    "no_test_android_arm64",
-    "no_test_android_x86",
-    "no_test_ios_x86_64",
-]
-
-NOTEST_TAGS = NOTEST_TAGS_MOBILE + NOTEST_TAGS_NONMOBILE
-
-cc_library(
-    name = "flat_hash_map",
-    hdrs = ["flat_hash_map.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":container_memory",
-        ":hash_function_defaults",
-        ":raw_hash_map",
-        "//absl/algorithm:container",
-        "//absl/memory",
-    ],
-)
-
-cc_test(
-    name = "flat_hash_map_test",
-    srcs = ["flat_hash_map_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
-    deps = [
-        ":flat_hash_map",
-        ":hash_generator_testing",
-        ":unordered_map_constructor_test",
-        ":unordered_map_lookup_test",
-        ":unordered_map_members_test",
-        ":unordered_map_modifiers_test",
-        "//absl/base:raw_logging_internal",
-        "//absl/types:any",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "flat_hash_set",
-    hdrs = ["flat_hash_set.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":container_memory",
-        ":hash_function_defaults",
-        ":raw_hash_set",
-        "//absl/algorithm:container",
-        "//absl/base:core_headers",
-        "//absl/memory",
-    ],
-)
-
-cc_test(
-    name = "flat_hash_set_test",
-    srcs = ["flat_hash_set_test.cc"],
-    copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
-    deps = [
-        ":flat_hash_set",
-        ":hash_generator_testing",
-        ":unordered_set_constructor_test",
-        ":unordered_set_lookup_test",
-        ":unordered_set_members_test",
-        ":unordered_set_modifiers_test",
-        "//absl/base:raw_logging_internal",
-        "//absl/memory",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "node_hash_map",
-    hdrs = ["node_hash_map.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":container_memory",
-        ":hash_function_defaults",
-        ":node_hash_policy",
-        ":raw_hash_map",
-        "//absl/algorithm:container",
-        "//absl/memory",
-    ],
-)
-
-cc_test(
-    name = "node_hash_map_test",
-    srcs = ["node_hash_map_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
-    deps = [
-        ":hash_generator_testing",
-        ":node_hash_map",
-        ":tracked",
-        ":unordered_map_constructor_test",
-        ":unordered_map_lookup_test",
-        ":unordered_map_members_test",
-        ":unordered_map_modifiers_test",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "node_hash_set",
-    hdrs = ["node_hash_set.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_function_defaults",
-        ":node_hash_policy",
-        ":raw_hash_set",
-        "//absl/algorithm:container",
-        "//absl/memory",
-    ],
-)
-
-cc_test(
-    name = "node_hash_set_test",
-    srcs = ["node_hash_set_test.cc"],
-    copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
-    deps = [
-        ":node_hash_set",
-        ":unordered_set_constructor_test",
-        ":unordered_set_lookup_test",
-        ":unordered_set_members_test",
-        ":unordered_set_modifiers_test",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "container_memory",
-    hdrs = ["internal/container_memory.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/utility",
-    ],
-)
-
-cc_test(
-    name = "container_memory_test",
-    srcs = ["internal/container_memory_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
-    deps = [
-        ":container_memory",
-        ":test_instance_tracker",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "hash_function_defaults",
-    hdrs = ["internal/hash_function_defaults.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/hash",
-        "//absl/strings",
-        "//absl/strings:cord",
-    ],
-)
-
-cc_test(
-    name = "hash_function_defaults_test",
-    srcs = ["internal/hash_function_defaults_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS,
-    deps = [
-        ":hash_function_defaults",
-        "//absl/hash",
-        "//absl/random",
-        "//absl/strings",
-        "//absl/strings:cord",
-        "//absl/strings:cord_test_helpers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "hash_generator_testing",
-    testonly = 1,
-    srcs = ["internal/hash_generator_testing.cc"],
-    hdrs = ["internal/hash_generator_testing.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_policy_testing",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-    ],
-)
-
-cc_library(
-    name = "hash_policy_testing",
-    testonly = 1,
-    hdrs = ["internal/hash_policy_testing.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/hash",
-        "//absl/strings",
-    ],
-)
-
-cc_test(
-    name = "hash_policy_testing_test",
-    srcs = ["internal/hash_policy_testing_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_policy_testing",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "hash_policy_traits",
-    hdrs = ["internal/hash_policy_traits.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = ["//absl/meta:type_traits"],
-)
-
-cc_test(
-    name = "hash_policy_traits_test",
-    srcs = ["internal/hash_policy_traits_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_policy_traits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "hashtable_debug",
-    hdrs = ["internal/hashtable_debug.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hashtable_debug_hooks",
-    ],
-)
-
-cc_library(
-    name = "hashtable_debug_hooks",
-    hdrs = ["internal/hashtable_debug_hooks.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-    ],
-)
-
-cc_library(
-    name = "hashtablez_sampler",
-    srcs = [
-        "internal/hashtablez_sampler.cc",
-        "internal/hashtablez_sampler_force_weak_definition.cc",
-    ],
-    hdrs = ["internal/hashtablez_sampler.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":have_sse",
-        "//absl/base",
-        "//absl/base:core_headers",
-        "//absl/base:exponential_biased",
-        "//absl/debugging:stacktrace",
-        "//absl/memory",
-        "//absl/synchronization",
-        "//absl/utility",
-    ],
-)
-
-cc_test(
-    name = "hashtablez_sampler_test",
-    srcs = ["internal/hashtablez_sampler_test.cc"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hashtablez_sampler",
-        ":have_sse",
-        "//absl/base:core_headers",
-        "//absl/synchronization",
-        "//absl/synchronization:thread_pool",
-        "//absl/time",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "node_hash_policy",
-    hdrs = ["internal/node_hash_policy.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = ["//absl/base:config"],
-)
-
-cc_test(
-    name = "node_hash_policy_test",
-    srcs = ["internal/node_hash_policy_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_policy_traits",
-        ":node_hash_policy",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "raw_hash_map",
-    hdrs = ["internal/raw_hash_map.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":container_memory",
-        ":raw_hash_set",
-        "//absl/base:throw_delegate",
-    ],
-)
-
-cc_library(
-    name = "have_sse",
-    hdrs = ["internal/have_sse.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-)
-
-cc_library(
-    name = "common",
-    hdrs = ["internal/common.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/meta:type_traits",
-        "//absl/types:optional",
-    ],
-)
-
-cc_library(
-    name = "raw_hash_set",
-    srcs = ["internal/raw_hash_set.cc"],
-    hdrs = ["internal/raw_hash_set.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":common",
-        ":compressed_tuple",
-        ":container_memory",
-        ":hash_policy_traits",
-        ":hashtable_debug_hooks",
-        ":hashtablez_sampler",
-        ":have_sse",
-        ":layout",
-        "//absl/base:bits",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:endian",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/utility",
-    ],
-)
-
-cc_test(
-    name = "raw_hash_set_test",
-    srcs = ["internal/raw_hash_set_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkstatic = 1,
-    tags = NOTEST_TAGS,
-    deps = [
-        ":container_memory",
-        ":hash_function_defaults",
-        ":hash_policy_testing",
-        ":hashtable_debug",
-        ":raw_hash_set",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "raw_hash_set_allocator_test",
-    size = "small",
-    srcs = ["internal/raw_hash_set_allocator_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":raw_hash_set",
-        ":tracked",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "layout",
-    hdrs = ["internal/layout.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "//absl/types:span",
-        "//absl/utility",
-    ],
-)
-
-cc_test(
-    name = "layout_test",
-    size = "small",
-    srcs = ["internal/layout_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":layout",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/types:span",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "tracked",
-    testonly = 1,
-    hdrs = ["internal/tracked.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-    ],
-)
-
-cc_library(
-    name = "unordered_map_constructor_test",
-    testonly = 1,
-    hdrs = ["internal/unordered_map_constructor_test.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_generator_testing",
-        ":hash_policy_testing",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "unordered_map_lookup_test",
-    testonly = 1,
-    hdrs = ["internal/unordered_map_lookup_test.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_generator_testing",
-        ":hash_policy_testing",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "unordered_map_modifiers_test",
-    testonly = 1,
-    hdrs = ["internal/unordered_map_modifiers_test.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_generator_testing",
-        ":hash_policy_testing",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "unordered_set_constructor_test",
-    testonly = 1,
-    hdrs = ["internal/unordered_set_constructor_test.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_generator_testing",
-        ":hash_policy_testing",
-        "//absl/meta:type_traits",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "unordered_set_members_test",
-    testonly = 1,
-    hdrs = ["internal/unordered_set_members_test.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/meta:type_traits",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "unordered_map_members_test",
-    testonly = 1,
-    hdrs = ["internal/unordered_map_members_test.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/meta:type_traits",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "unordered_set_lookup_test",
-    testonly = 1,
-    hdrs = ["internal/unordered_set_lookup_test.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_generator_testing",
-        ":hash_policy_testing",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "unordered_set_modifiers_test",
-    testonly = 1,
-    hdrs = ["internal/unordered_set_modifiers_test.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash_generator_testing",
-        ":hash_policy_testing",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "unordered_set_test",
-    srcs = ["internal/unordered_set_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
-    deps = [
-        ":unordered_set_constructor_test",
-        ":unordered_set_lookup_test",
-        ":unordered_set_members_test",
-        ":unordered_set_modifiers_test",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "unordered_map_test",
-    srcs = ["internal/unordered_map_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = NOTEST_TAGS_NONMOBILE,
-    deps = [
-        ":unordered_map_constructor_test",
-        ":unordered_map_lookup_test",
-        ":unordered_map_members_test",
-        ":unordered_map_modifiers_test",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "btree",
-    srcs = [
-        "internal/btree.h",
-        "internal/btree_container.h",
-    ],
-    hdrs = [
-        "btree_map.h",
-        "btree_set.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:public"],
-    deps = [
-        ":common",
-        ":compressed_tuple",
-        ":container_memory",
-        ":layout",
-        "//absl/base:core_headers",
-        "//absl/base:throw_delegate",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "//absl/strings:cord",
-        "//absl/types:compare",
-        "//absl/utility",
-    ],
-)
-
-cc_library(
-    name = "btree_test_common",
-    testonly = 1,
-    hdrs = ["btree_test.h"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":btree",
-        ":flat_hash_set",
-        "//absl/strings",
-        "//absl/strings:cord",
-        "//absl/time",
-    ],
-)
-
-cc_test(
-    name = "btree_test",
-    size = "large",
-    srcs = [
-        "btree_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    shard_count = 10,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":btree",
-        ":btree_test_common",
-        ":counting_allocator",
-        ":test_instance_tracker",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/flags:flag",
-        "//absl/hash:hash_testing",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "//absl/types:compare",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_binary(
-    name = "btree_benchmark",
-    testonly = 1,
-    srcs = [
-        "btree_benchmark.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":btree",
-        ":btree_test_common",
-        ":flat_hash_map",
-        ":flat_hash_set",
-        ":hashtable_debug",
-        "//absl/base:raw_logging_internal",
-        "//absl/flags:flag",
-        "//absl/hash",
-        "//absl/memory",
-        "//absl/strings:cord",
-        "//absl/strings:str_format",
-        "//absl/time",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/container/CMakeLists.txt b/third_party/abseil_cpp/absl/container/CMakeLists.txt
deleted file mode 100644
index eb202c459b..0000000000
--- a/third_party/abseil_cpp/absl/container/CMakeLists.txt
+++ /dev/null
@@ -1,905 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# This is deprecated and will be removed in the future.  It also doesn't do
-# anything anyways.  Prefer to use the library associated with the API you are
-# using.
-absl_cc_library(
-  NAME
-    container
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    btree
-  HDRS
-    "btree_map.h"
-    "btree_set.h"
-    "internal/btree.h"
-    "internal/btree_container.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::container_common
-    absl::compare
-    absl::compressed_tuple
-    absl::container_memory
-    absl::cord
-    absl::core_headers
-    absl::layout
-    absl::memory
-    absl::strings
-    absl::throw_delegate
-    absl::type_traits
-    absl::utility
-)
-
-absl_cc_library(
-  NAME
-    btree_test_common
-  hdrs
-    "btree_test.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::btree
-    absl::cord
-    absl::flat_hash_set
-    absl::strings
-    absl::time
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    btree_test
-  SRCS
-    "btree_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::btree
-    absl::btree_test_common
-    absl::compare
-    absl::core_headers
-    absl::counting_allocator
-    absl::flags
-    absl::hash_testing
-    absl::raw_logging_internal
-    absl::strings
-    absl::test_instance_tracker
-    absl::type_traits
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    compressed_tuple
-  HDRS
-    "internal/compressed_tuple.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::utility
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    compressed_tuple_test
-  SRCS
-    "internal/compressed_tuple_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::any
-    absl::compressed_tuple
-    absl::memory
-    absl::optional
-    absl::test_instance_tracker
-    absl::utility
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    fixed_array
-  HDRS
-   "fixed_array.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::compressed_tuple
-    absl::algorithm
-    absl::config
-    absl::core_headers
-    absl::dynamic_annotations
-    absl::throw_delegate
-    absl::memory
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    fixed_array_test
-  SRCS
-    "fixed_array_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::fixed_array
-    absl::counting_allocator
-    absl::config
-    absl::exception_testing
-    absl::hash_testing
-    absl::memory
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    fixed_array_exception_safety_test
-  SRCS
-    "fixed_array_exception_safety_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::fixed_array
-    absl::config
-    absl::exception_safety_testing
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    inlined_vector_internal
-  HDRS
-   "internal/inlined_vector.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::compressed_tuple
-    absl::core_headers
-    absl::memory
-    absl::span
-    absl::type_traits
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    inlined_vector
-  HDRS
-   "inlined_vector.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::algorithm
-    absl::core_headers
-    absl::inlined_vector_internal
-    absl::throw_delegate
-    absl::memory
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    counting_allocator
-  HDRS
-    "internal/counting_allocator.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-)
-
-absl_cc_test(
-  NAME
-    inlined_vector_test
-  SRCS
-    "inlined_vector_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::counting_allocator
-    absl::inlined_vector
-    absl::test_instance_tracker
-    absl::config
-    absl::core_headers
-    absl::exception_testing
-    absl::hash_testing
-    absl::memory
-    absl::raw_logging_internal
-    absl::strings
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    inlined_vector_exception_safety_test
-  SRCS
-    "inlined_vector_exception_safety_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::inlined_vector
-    absl::config
-    absl::exception_safety_testing
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    test_instance_tracker
-  HDRS
-    "internal/test_instance_tracker.h"
-  SRCS
-    "internal/test_instance_tracker.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::compare
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    test_instance_tracker_test
-  SRCS
-    "internal/test_instance_tracker_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::test_instance_tracker
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    flat_hash_map
-  HDRS
-    "flat_hash_map.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::container_memory
-    absl::hash_function_defaults
-    absl::raw_hash_map
-    absl::algorithm_container
-    absl::memory
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    flat_hash_map_test
-  SRCS
-    "flat_hash_map_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flat_hash_map
-    absl::hash_generator_testing
-    absl::unordered_map_constructor_test
-    absl::unordered_map_lookup_test
-    absl::unordered_map_members_test
-    absl::unordered_map_modifiers_test
-    absl::any
-    absl::raw_logging_internal
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    flat_hash_set
-  HDRS
-    "flat_hash_set.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::container_memory
-    absl::hash_function_defaults
-    absl::raw_hash_set
-    absl::algorithm_container
-    absl::core_headers
-    absl::memory
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    flat_hash_set_test
-  SRCS
-    "flat_hash_set_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-    "-DUNORDERED_SET_CXX17"
-  DEPS
-    absl::flat_hash_set
-    absl::hash_generator_testing
-    absl::unordered_set_constructor_test
-    absl::unordered_set_lookup_test
-    absl::unordered_set_members_test
-    absl::unordered_set_modifiers_test
-    absl::memory
-    absl::raw_logging_internal
-    absl::strings
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    node_hash_map
-  HDRS
-    "node_hash_map.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::container_memory
-    absl::hash_function_defaults
-    absl::node_hash_policy
-    absl::raw_hash_map
-    absl::algorithm_container
-    absl::memory
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    node_hash_map_test
-  SRCS
-    "node_hash_map_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_generator_testing
-    absl::node_hash_map
-    absl::tracked
-    absl::unordered_map_constructor_test
-    absl::unordered_map_lookup_test
-    absl::unordered_map_members_test
-    absl::unordered_map_modifiers_test
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    node_hash_set
-  HDRS
-    "node_hash_set.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::hash_function_defaults
-    absl::node_hash_policy
-    absl::raw_hash_set
-    absl::algorithm_container
-    absl::memory
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    node_hash_set_test
-  SRCS
-    "node_hash_set_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-    "-DUNORDERED_SET_CXX17"
-  DEPS
-    absl::hash_generator_testing
-    absl::node_hash_set
-    absl::unordered_set_constructor_test
-    absl::unordered_set_lookup_test
-    absl::unordered_set_members_test
-    absl::unordered_set_modifiers_test
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    container_memory
-  HDRS
-    "internal/container_memory.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::memory
-    absl::type_traits
-    absl::utility
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    container_memory_test
-  SRCS
-    "internal/container_memory_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::container_memory
-    absl::strings
-    absl::test_instance_tracker
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    hash_function_defaults
-  HDRS
-    "internal/hash_function_defaults.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::cord
-    absl::hash
-    absl::strings
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    hash_function_defaults_test
-  SRCS
-    "internal/hash_function_defaults_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::cord
-    absl::cord_test_helpers
-    absl::hash_function_defaults
-    absl::hash
-    absl::random_random
-    absl::strings
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    hash_generator_testing
-  HDRS
-    "internal/hash_generator_testing.h"
-  SRCS
-    "internal/hash_generator_testing.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_policy_testing
-    absl::memory
-    absl::meta
-    absl::strings
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    hash_policy_testing
-  HDRS
-    "internal/hash_policy_testing.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash
-    absl::strings
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    hash_policy_testing_test
-  SRCS
-    "internal/hash_policy_testing_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_policy_testing
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    hash_policy_traits
-  HDRS
-    "internal/hash_policy_traits.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::meta
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    hash_policy_traits_test
-  SRCS
-    "internal/hash_policy_traits_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_policy_traits
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    hashtablez_sampler
-  HDRS
-    "internal/hashtablez_sampler.h"
-  SRCS
-    "internal/hashtablez_sampler.cc"
-    "internal/hashtablez_sampler_force_weak_definition.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base
-    absl::exponential_biased
-    absl::have_sse
-    absl::synchronization
-)
-
-absl_cc_test(
-  NAME
-    hashtablez_sampler_test
-  SRCS
-    "internal/hashtablez_sampler_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hashtablez_sampler
-    absl::have_sse
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    hashtable_debug
-  HDRS
-    "internal/hashtable_debug.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::hashtable_debug_hooks
-)
-
-absl_cc_library(
-  NAME
-    hashtable_debug_hooks
-  HDRS
-    "internal/hashtable_debug_hooks.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    have_sse
-  HDRS
-    "internal/have_sse.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-)
-
-absl_cc_library(
-  NAME
-    node_hash_policy
-  HDRS
-    "internal/node_hash_policy.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    node_hash_policy_test
-  SRCS
-    "internal/node_hash_policy_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_policy_traits
-    absl::node_hash_policy
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    raw_hash_map
-  HDRS
-    "internal/raw_hash_map.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::container_memory
-    absl::raw_hash_set
-    absl::throw_delegate
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    container_common
-  HDRS
-    "internal/common.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::type_traits
-)
-
-absl_cc_library(
-  NAME
-    raw_hash_set
-  HDRS
-    "internal/raw_hash_set.h"
-  SRCS
-    "internal/raw_hash_set.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::bits
-    absl::compressed_tuple
-    absl::config
-    absl::container_common
-    absl::container_memory
-    absl::core_headers
-    absl::endian
-    absl::hash_policy_traits
-    absl::hashtable_debug_hooks
-    absl::have_sse
-    absl::layout
-    absl::memory
-    absl::meta
-    absl::optional
-    absl::utility
-    absl::hashtablez_sampler
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    raw_hash_set_test
-  SRCS
-    "internal/raw_hash_set_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::container_memory
-    absl::hash_function_defaults
-    absl::hash_policy_testing
-    absl::hashtable_debug
-    absl::raw_hash_set
-    absl::base
-    absl::config
-    absl::core_headers
-    absl::raw_logging_internal
-    absl::strings
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    raw_hash_set_allocator_test
-  SRCS
-    "internal/raw_hash_set_allocator_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::raw_hash_set
-    absl::tracked
-    absl::core_headers
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    layout
-  HDRS
-    "internal/layout.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::meta
-    absl::strings
-    absl::span
-    absl::utility
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    layout_test
-  SRCS
-    "internal/layout_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::layout
-    absl::config
-    absl::core_headers
-    absl::raw_logging_internal
-    absl::span
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    tracked
-  HDRS
-    "internal/tracked.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::config
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    unordered_map_constructor_test
-  HDRS
-    "internal/unordered_map_constructor_test.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_generator_testing
-    absl::hash_policy_testing
-    gmock
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    unordered_map_lookup_test
-  HDRS
-    "internal/unordered_map_lookup_test.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_generator_testing
-    absl::hash_policy_testing
-    gmock
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    unordered_map_members_test
-  HDRS
-    "internal/unordered_map_members_test.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::type_traits
-    gmock
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    unordered_map_modifiers_test
-  HDRS
-    "internal/unordered_map_modifiers_test.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_generator_testing
-    absl::hash_policy_testing
-    gmock
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    unordered_set_constructor_test
-  HDRS
-    "internal/unordered_set_constructor_test.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_generator_testing
-    absl::hash_policy_testing
-    gmock
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    unordered_set_lookup_test
-  HDRS
-    "internal/unordered_set_lookup_test.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_generator_testing
-    absl::hash_policy_testing
-    gmock
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    unordered_set_members_test
-  HDRS
-    "internal/unordered_set_members_test.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::type_traits
-    gmock
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    unordered_set_modifiers_test
-  HDRS
-    "internal/unordered_set_modifiers_test.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::hash_generator_testing
-    absl::hash_policy_testing
-    gmock
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    unordered_set_test
-  SRCS
-    "internal/unordered_set_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::unordered_set_constructor_test
-    absl::unordered_set_lookup_test
-    absl::unordered_set_members_test
-    absl::unordered_set_modifiers_test
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    unordered_map_test
-  SRCS
-    "internal/unordered_map_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::unordered_map_constructor_test
-    absl::unordered_map_lookup_test
-    absl::unordered_map_members_test
-    absl::unordered_map_modifiers_test
-    gmock_main
-)
diff --git a/third_party/abseil_cpp/absl/container/btree_benchmark.cc b/third_party/abseil_cpp/absl/container/btree_benchmark.cc
deleted file mode 100644
index 467986768a..0000000000
--- a/third_party/abseil_cpp/absl/container/btree_benchmark.cc
+++ /dev/null
@@ -1,735 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <stdint.h>
-
-#include <algorithm>
-#include <functional>
-#include <map>
-#include <numeric>
-#include <random>
-#include <set>
-#include <string>
-#include <type_traits>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/container/btree_map.h"
-#include "absl/container/btree_set.h"
-#include "absl/container/btree_test.h"
-#include "absl/container/flat_hash_map.h"
-#include "absl/container/flat_hash_set.h"
-#include "absl/container/internal/hashtable_debug.h"
-#include "absl/flags/flag.h"
-#include "absl/hash/hash.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/cord.h"
-#include "absl/strings/str_format.h"
-#include "absl/time/time.h"
-#include "benchmark/benchmark.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-constexpr size_t kBenchmarkValues = 1 << 20;
-
-// How many times we add and remove sub-batches in one batch of *AddRem
-// benchmarks.
-constexpr size_t kAddRemBatchSize = 1 << 2;
-
-// Generates n values in the range [0, 4 * n].
-template <typename V>
-std::vector<V> GenerateValues(int n) {
-  constexpr int kSeed = 23;
-  return GenerateValuesWithSeed<V>(n, 4 * n, kSeed);
-}
-
-// Benchmark insertion of values into a container.
-template <typename T>
-void BM_InsertImpl(benchmark::State& state, bool sorted) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  typename KeyOfValue<typename T::key_type, V>::type key_of_value;
-
-  std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
-  if (sorted) {
-    std::sort(values.begin(), values.end());
-  }
-  T container(values.begin(), values.end());
-
-  // Remove and re-insert 10% of the keys per batch.
-  const int batch_size = (kBenchmarkValues + 9) / 10;
-  while (state.KeepRunningBatch(batch_size)) {
-    state.PauseTiming();
-    const auto i = static_cast<int>(state.iterations());
-
-    for (int j = i; j < i + batch_size; j++) {
-      int x = j % kBenchmarkValues;
-      container.erase(key_of_value(values[x]));
-    }
-
-    state.ResumeTiming();
-
-    for (int j = i; j < i + batch_size; j++) {
-      int x = j % kBenchmarkValues;
-      container.insert(values[x]);
-    }
-  }
-}
-
-template <typename T>
-void BM_Insert(benchmark::State& state) {
-  BM_InsertImpl<T>(state, false);
-}
-
-template <typename T>
-void BM_InsertSorted(benchmark::State& state) {
-  BM_InsertImpl<T>(state, true);
-}
-
-// container::insert sometimes returns a pair<iterator, bool> and sometimes
-// returns an iterator (for multi- containers).
-template <typename Iter>
-Iter GetIterFromInsert(const std::pair<Iter, bool>& pair) {
-  return pair.first;
-}
-template <typename Iter>
-Iter GetIterFromInsert(const Iter iter) {
-  return iter;
-}
-
-// Benchmark insertion of values into a container at the end.
-template <typename T>
-void BM_InsertEnd(benchmark::State& state) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  typename KeyOfValue<typename T::key_type, V>::type key_of_value;
-
-  T container;
-  const int kSize = 10000;
-  for (int i = 0; i < kSize; ++i) {
-    container.insert(Generator<V>(kSize)(i));
-  }
-  V v = Generator<V>(kSize)(kSize - 1);
-  typename T::key_type k = key_of_value(v);
-
-  auto it = container.find(k);
-  while (state.KeepRunning()) {
-    // Repeatedly removing then adding v.
-    container.erase(it);
-    it = GetIterFromInsert(container.insert(v));
-  }
-}
-
-// Benchmark inserting the first few elements in a container. In b-tree, this is
-// when the root node grows.
-template <typename T>
-void BM_InsertSmall(benchmark::State& state) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-
-  const int kSize = 8;
-  std::vector<V> values = GenerateValues<V>(kSize);
-  T container;
-
-  while (state.KeepRunningBatch(kSize)) {
-    for (int i = 0; i < kSize; ++i) {
-      benchmark::DoNotOptimize(container.insert(values[i]));
-    }
-    state.PauseTiming();
-    // Do not measure the time it takes to clear the container.
-    container.clear();
-    state.ResumeTiming();
-  }
-}
-
-template <typename T>
-void BM_LookupImpl(benchmark::State& state, bool sorted) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  typename KeyOfValue<typename T::key_type, V>::type key_of_value;
-
-  std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
-  if (sorted) {
-    std::sort(values.begin(), values.end());
-  }
-  T container(values.begin(), values.end());
-
-  while (state.KeepRunning()) {
-    int idx = state.iterations() % kBenchmarkValues;
-    benchmark::DoNotOptimize(container.find(key_of_value(values[idx])));
-  }
-}
-
-// Benchmark lookup of values in a container.
-template <typename T>
-void BM_Lookup(benchmark::State& state) {
-  BM_LookupImpl<T>(state, false);
-}
-
-// Benchmark lookup of values in a full container, meaning that values
-// are inserted in-order to take advantage of biased insertion, which
-// yields a full tree.
-template <typename T>
-void BM_FullLookup(benchmark::State& state) {
-  BM_LookupImpl<T>(state, true);
-}
-
-// Benchmark deletion of values from a container.
-template <typename T>
-void BM_Delete(benchmark::State& state) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  typename KeyOfValue<typename T::key_type, V>::type key_of_value;
-  std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
-  T container(values.begin(), values.end());
-
-  // Remove and re-insert 10% of the keys per batch.
-  const int batch_size = (kBenchmarkValues + 9) / 10;
-  while (state.KeepRunningBatch(batch_size)) {
-    const int i = state.iterations();
-
-    for (int j = i; j < i + batch_size; j++) {
-      int x = j % kBenchmarkValues;
-      container.erase(key_of_value(values[x]));
-    }
-
-    state.PauseTiming();
-    for (int j = i; j < i + batch_size; j++) {
-      int x = j % kBenchmarkValues;
-      container.insert(values[x]);
-    }
-    state.ResumeTiming();
-  }
-}
-
-// Benchmark deletion of multiple values from a container.
-template <typename T>
-void BM_DeleteRange(benchmark::State& state) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  typename KeyOfValue<typename T::key_type, V>::type key_of_value;
-  std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
-  T container(values.begin(), values.end());
-
-  // Remove and re-insert 10% of the keys per batch.
-  const int batch_size = (kBenchmarkValues + 9) / 10;
-  while (state.KeepRunningBatch(batch_size)) {
-    const int i = state.iterations();
-
-    const int start_index = i % kBenchmarkValues;
-
-    state.PauseTiming();
-    {
-      std::vector<V> removed;
-      removed.reserve(batch_size);
-      auto itr = container.find(key_of_value(values[start_index]));
-      auto start = itr;
-      for (int j = 0; j < batch_size; j++) {
-        if (itr == container.end()) {
-          state.ResumeTiming();
-          container.erase(start, itr);
-          state.PauseTiming();
-          itr = container.begin();
-          start = itr;
-        }
-        removed.push_back(*itr++);
-      }
-
-      state.ResumeTiming();
-      container.erase(start, itr);
-      state.PauseTiming();
-
-      container.insert(removed.begin(), removed.end());
-    }
-    state.ResumeTiming();
-  }
-}
-
-// Benchmark steady-state insert (into first half of range) and remove (from
-// second half of range), treating the container approximately like a queue with
-// log-time access for all elements. This benchmark does not test the case where
-// insertion and removal happen in the same region of the tree.  This benchmark
-// counts two value constructors.
-template <typename T>
-void BM_QueueAddRem(benchmark::State& state) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  typename KeyOfValue<typename T::key_type, V>::type key_of_value;
-
-  ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance");
-
-  T container;
-
-  const size_t half = kBenchmarkValues / 2;
-  std::vector<int> remove_keys(half);
-  std::vector<int> add_keys(half);
-
-  // We want to do the exact same work repeatedly, and the benchmark can end
-  // after a different number of iterations depending on the speed of the
-  // individual run so we use a large batch size here and ensure that we do
-  // deterministic work every batch.
-  while (state.KeepRunningBatch(half * kAddRemBatchSize)) {
-    state.PauseTiming();
-
-    container.clear();
-
-    for (size_t i = 0; i < half; ++i) {
-      remove_keys[i] = i;
-      add_keys[i] = i;
-    }
-    constexpr int kSeed = 5;
-    std::mt19937_64 rand(kSeed);
-    std::shuffle(remove_keys.begin(), remove_keys.end(), rand);
-    std::shuffle(add_keys.begin(), add_keys.end(), rand);
-
-    // Note needs lazy generation of values.
-    Generator<V> g(kBenchmarkValues * kAddRemBatchSize);
-
-    for (size_t i = 0; i < half; ++i) {
-      container.insert(g(add_keys[i]));
-      container.insert(g(half + remove_keys[i]));
-    }
-
-    // There are three parts each of size "half":
-    // 1 is being deleted from  [offset - half, offset)
-    // 2 is standing            [offset, offset + half)
-    // 3 is being inserted into [offset + half, offset + 2 * half)
-    size_t offset = 0;
-
-    for (size_t i = 0; i < kAddRemBatchSize; ++i) {
-      std::shuffle(remove_keys.begin(), remove_keys.end(), rand);
-      std::shuffle(add_keys.begin(), add_keys.end(), rand);
-      offset += half;
-
-      state.ResumeTiming();
-      for (size_t idx = 0; idx < half; ++idx) {
-        container.erase(key_of_value(g(offset - half + remove_keys[idx])));
-        container.insert(g(offset + half + add_keys[idx]));
-      }
-      state.PauseTiming();
-    }
-    state.ResumeTiming();
-  }
-}
-
-// Mixed insertion and deletion in the same range using pre-constructed values.
-template <typename T>
-void BM_MixedAddRem(benchmark::State& state) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  typename KeyOfValue<typename T::key_type, V>::type key_of_value;
-
-  ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance");
-
-  T container;
-
-  // Create two random shuffles
-  std::vector<int> remove_keys(kBenchmarkValues);
-  std::vector<int> add_keys(kBenchmarkValues);
-
-  // We want to do the exact same work repeatedly, and the benchmark can end
-  // after a different number of iterations depending on the speed of the
-  // individual run so we use a large batch size here and ensure that we do
-  // deterministic work every batch.
-  while (state.KeepRunningBatch(kBenchmarkValues * kAddRemBatchSize)) {
-    state.PauseTiming();
-
-    container.clear();
-
-    constexpr int kSeed = 7;
-    std::mt19937_64 rand(kSeed);
-
-    std::vector<V> values = GenerateValues<V>(kBenchmarkValues * 2);
-
-    // Insert the first half of the values (already in random order)
-    container.insert(values.begin(), values.begin() + kBenchmarkValues);
-
-    // Insert the first half of the values (already in random order)
-    for (size_t i = 0; i < kBenchmarkValues; ++i) {
-      // remove_keys and add_keys will be swapped before each round,
-      // therefore fill add_keys here w/ the keys being inserted, so
-      // they'll be the first to be removed.
-      remove_keys[i] = i + kBenchmarkValues;
-      add_keys[i] = i;
-    }
-
-    for (size_t i = 0; i < kAddRemBatchSize; ++i) {
-      remove_keys.swap(add_keys);
-      std::shuffle(remove_keys.begin(), remove_keys.end(), rand);
-      std::shuffle(add_keys.begin(), add_keys.end(), rand);
-
-      state.ResumeTiming();
-      for (size_t idx = 0; idx < kBenchmarkValues; ++idx) {
-        container.erase(key_of_value(values[remove_keys[idx]]));
-        container.insert(values[add_keys[idx]]);
-      }
-      state.PauseTiming();
-    }
-    state.ResumeTiming();
-  }
-}
-
-// Insertion at end, removal from the beginning.  This benchmark
-// counts two value constructors.
-// TODO(ezb): we could add a GenerateNext version of generator that could reduce
-// noise for string-like types.
-template <typename T>
-void BM_Fifo(benchmark::State& state) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-
-  T container;
-  // Need lazy generation of values as state.max_iterations is large.
-  Generator<V> g(kBenchmarkValues + state.max_iterations);
-
-  for (int i = 0; i < kBenchmarkValues; i++) {
-    container.insert(g(i));
-  }
-
-  while (state.KeepRunning()) {
-    container.erase(container.begin());
-    container.insert(container.end(), g(state.iterations() + kBenchmarkValues));
-  }
-}
-
-// Iteration (forward) through the tree
-template <typename T>
-void BM_FwdIter(benchmark::State& state) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  using R = typename T::value_type const*;
-
-  std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
-  T container(values.begin(), values.end());
-
-  auto iter = container.end();
-
-  R r = nullptr;
-
-  while (state.KeepRunning()) {
-    if (iter == container.end()) iter = container.begin();
-    r = &(*iter);
-    ++iter;
-  }
-
-  benchmark::DoNotOptimize(r);
-}
-
-// Benchmark random range-construction of a container.
-template <typename T>
-void BM_RangeConstructionImpl(benchmark::State& state, bool sorted) {
-  using V = typename remove_pair_const<typename T::value_type>::type;
-
-  std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
-  if (sorted) {
-    std::sort(values.begin(), values.end());
-  }
-  {
-    T container(values.begin(), values.end());
-  }
-
-  while (state.KeepRunning()) {
-    T container(values.begin(), values.end());
-    benchmark::DoNotOptimize(container);
-  }
-}
-
-template <typename T>
-void BM_InsertRangeRandom(benchmark::State& state) {
-  BM_RangeConstructionImpl<T>(state, false);
-}
-
-template <typename T>
-void BM_InsertRangeSorted(benchmark::State& state) {
-  BM_RangeConstructionImpl<T>(state, true);
-}
-
-#define STL_ORDERED_TYPES(value)                     \
-  using stl_set_##value = std::set<value>;           \
-  using stl_map_##value = std::map<value, intptr_t>; \
-  using stl_multiset_##value = std::multiset<value>; \
-  using stl_multimap_##value = std::multimap<value, intptr_t>
-
-using StdString = std::string;
-STL_ORDERED_TYPES(int32_t);
-STL_ORDERED_TYPES(int64_t);
-STL_ORDERED_TYPES(StdString);
-STL_ORDERED_TYPES(Cord);
-STL_ORDERED_TYPES(Time);
-
-#define STL_UNORDERED_TYPES(value)                                       \
-  using stl_unordered_set_##value = std::unordered_set<value>;           \
-  using stl_unordered_map_##value = std::unordered_map<value, intptr_t>; \
-  using flat_hash_set_##value = flat_hash_set<value>;                    \
-  using flat_hash_map_##value = flat_hash_map<value, intptr_t>;          \
-  using stl_unordered_multiset_##value = std::unordered_multiset<value>; \
-  using stl_unordered_multimap_##value =                                 \
-      std::unordered_multimap<value, intptr_t>
-
-#define STL_UNORDERED_TYPES_CUSTOM_HASH(value, hash)                           \
-  using stl_unordered_set_##value = std::unordered_set<value, hash>;           \
-  using stl_unordered_map_##value = std::unordered_map<value, intptr_t, hash>; \
-  using flat_hash_set_##value = flat_hash_set<value, hash>;                    \
-  using flat_hash_map_##value = flat_hash_map<value, intptr_t, hash>;          \
-  using stl_unordered_multiset_##value = std::unordered_multiset<value, hash>; \
-  using stl_unordered_multimap_##value =                                       \
-      std::unordered_multimap<value, intptr_t, hash>
-
-STL_UNORDERED_TYPES_CUSTOM_HASH(Cord, absl::Hash<absl::Cord>);
-
-STL_UNORDERED_TYPES(int32_t);
-STL_UNORDERED_TYPES(int64_t);
-STL_UNORDERED_TYPES(StdString);
-STL_UNORDERED_TYPES_CUSTOM_HASH(Time, absl::Hash<absl::Time>);
-
-#define BTREE_TYPES(value)                                            \
-  using btree_256_set_##value =                                       \
-      btree_set<value, std::less<value>, std::allocator<value>>;      \
-  using btree_256_map_##value =                                       \
-      btree_map<value, intptr_t, std::less<value>,                    \
-                std::allocator<std::pair<const value, intptr_t>>>;    \
-  using btree_256_multiset_##value =                                  \
-      btree_multiset<value, std::less<value>, std::allocator<value>>; \
-  using btree_256_multimap_##value =                                  \
-      btree_multimap<value, intptr_t, std::less<value>,               \
-                     std::allocator<std::pair<const value, intptr_t>>>
-
-BTREE_TYPES(int32_t);
-BTREE_TYPES(int64_t);
-BTREE_TYPES(StdString);
-BTREE_TYPES(Cord);
-BTREE_TYPES(Time);
-
-#define MY_BENCHMARK4(type, func)                                              \
-  void BM_##type##_##func(benchmark::State& state) { BM_##func<type>(state); } \
-  BENCHMARK(BM_##type##_##func)
-
-#define MY_BENCHMARK3(type)               \
-  MY_BENCHMARK4(type, Insert);            \
-  MY_BENCHMARK4(type, InsertSorted);      \
-  MY_BENCHMARK4(type, InsertEnd);         \
-  MY_BENCHMARK4(type, InsertSmall);       \
-  MY_BENCHMARK4(type, Lookup);            \
-  MY_BENCHMARK4(type, FullLookup);        \
-  MY_BENCHMARK4(type, Delete);            \
-  MY_BENCHMARK4(type, DeleteRange);       \
-  MY_BENCHMARK4(type, QueueAddRem);       \
-  MY_BENCHMARK4(type, MixedAddRem);       \
-  MY_BENCHMARK4(type, Fifo);              \
-  MY_BENCHMARK4(type, FwdIter);           \
-  MY_BENCHMARK4(type, InsertRangeRandom); \
-  MY_BENCHMARK4(type, InsertRangeSorted)
-
-#define MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type) \
-  MY_BENCHMARK3(stl_##type);                    \
-  MY_BENCHMARK3(stl_unordered_##type);          \
-  MY_BENCHMARK3(btree_256_##type)
-
-#define MY_BENCHMARK2(type)                \
-  MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type); \
-  MY_BENCHMARK3(flat_hash_##type)
-
-// Define MULTI_TESTING to see benchmarks for multi-containers also.
-//
-// You can use --copt=-DMULTI_TESTING.
-#ifdef MULTI_TESTING
-#define MY_BENCHMARK(type)                            \
-  MY_BENCHMARK2(set_##type);                          \
-  MY_BENCHMARK2(map_##type);                          \
-  MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multiset_##type); \
-  MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multimap_##type)
-#else
-#define MY_BENCHMARK(type)   \
-  MY_BENCHMARK2(set_##type); \
-  MY_BENCHMARK2(map_##type)
-#endif
-
-MY_BENCHMARK(int32_t);
-MY_BENCHMARK(int64_t);
-MY_BENCHMARK(StdString);
-MY_BENCHMARK(Cord);
-MY_BENCHMARK(Time);
-
-// Define a type whose size and cost of moving are independently customizable.
-// When sizeof(value_type) increases, we expect btree to no longer have as much
-// cache-locality advantage over STL. When cost of moving increases, we expect
-// btree to actually do more work than STL because it has to move values around
-// and STL doesn't have to.
-template <int Size, int Copies>
-struct BigType {
-  BigType() : BigType(0) {}
-  explicit BigType(int x) { std::iota(values.begin(), values.end(), x); }
-
-  void Copy(const BigType& other) {
-    for (int i = 0; i < Size && i < Copies; ++i) values[i] = other.values[i];
-    // If Copies > Size, do extra copies.
-    for (int i = Size, idx = 0; i < Copies; ++i) {
-      int64_t tmp = other.values[idx];
-      benchmark::DoNotOptimize(tmp);
-      idx = idx + 1 == Size ? 0 : idx + 1;
-    }
-  }
-
-  BigType(const BigType& other) { Copy(other); }
-  BigType& operator=(const BigType& other) {
-    Copy(other);
-    return *this;
-  }
-
-  // Compare only the first Copies elements if Copies is less than Size.
-  bool operator<(const BigType& other) const {
-    return std::lexicographical_compare(
-        values.begin(), values.begin() + std::min(Size, Copies),
-        other.values.begin(), other.values.begin() + std::min(Size, Copies));
-  }
-  bool operator==(const BigType& other) const {
-    return std::equal(values.begin(), values.begin() + std::min(Size, Copies),
-                      other.values.begin());
-  }
-
-  // Support absl::Hash.
-  template <typename State>
-  friend State AbslHashValue(State h, const BigType& b) {
-    for (int i = 0; i < Size && i < Copies; ++i)
-      h = State::combine(std::move(h), b.values[i]);
-    return h;
-  }
-
-  std::array<int64_t, Size> values;
-};
-
-#define BIG_TYPE_BENCHMARKS(SIZE, COPIES)                                     \
-  using stl_set_size##SIZE##copies##COPIES = std::set<BigType<SIZE, COPIES>>; \
-  using stl_map_size##SIZE##copies##COPIES =                                  \
-      std::map<BigType<SIZE, COPIES>, intptr_t>;                              \
-  using stl_multiset_size##SIZE##copies##COPIES =                             \
-      std::multiset<BigType<SIZE, COPIES>>;                                   \
-  using stl_multimap_size##SIZE##copies##COPIES =                             \
-      std::multimap<BigType<SIZE, COPIES>, intptr_t>;                         \
-  using stl_unordered_set_size##SIZE##copies##COPIES =                        \
-      std::unordered_set<BigType<SIZE, COPIES>,                               \
-                         absl::Hash<BigType<SIZE, COPIES>>>;                  \
-  using stl_unordered_map_size##SIZE##copies##COPIES =                        \
-      std::unordered_map<BigType<SIZE, COPIES>, intptr_t,                     \
-                         absl::Hash<BigType<SIZE, COPIES>>>;                  \
-  using flat_hash_set_size##SIZE##copies##COPIES =                            \
-      flat_hash_set<BigType<SIZE, COPIES>>;                                   \
-  using flat_hash_map_size##SIZE##copies##COPIES =                            \
-      flat_hash_map<BigType<SIZE, COPIES>, intptr_t>;                         \
-  using stl_unordered_multiset_size##SIZE##copies##COPIES =                   \
-      std::unordered_multiset<BigType<SIZE, COPIES>,                          \
-                              absl::Hash<BigType<SIZE, COPIES>>>;             \
-  using stl_unordered_multimap_size##SIZE##copies##COPIES =                   \
-      std::unordered_multimap<BigType<SIZE, COPIES>, intptr_t,                \
-                              absl::Hash<BigType<SIZE, COPIES>>>;             \
-  using btree_256_set_size##SIZE##copies##COPIES =                            \
-      btree_set<BigType<SIZE, COPIES>>;                                       \
-  using btree_256_map_size##SIZE##copies##COPIES =                            \
-      btree_map<BigType<SIZE, COPIES>, intptr_t>;                             \
-  using btree_256_multiset_size##SIZE##copies##COPIES =                       \
-      btree_multiset<BigType<SIZE, COPIES>>;                                  \
-  using btree_256_multimap_size##SIZE##copies##COPIES =                       \
-      btree_multimap<BigType<SIZE, COPIES>, intptr_t>;                        \
-  MY_BENCHMARK(size##SIZE##copies##COPIES)
-
-// Define BIG_TYPE_TESTING to see benchmarks for more big types.
-//
-// You can use --copt=-DBIG_TYPE_TESTING.
-#ifndef NODESIZE_TESTING
-#ifdef BIG_TYPE_TESTING
-BIG_TYPE_BENCHMARKS(1, 4);
-BIG_TYPE_BENCHMARKS(4, 1);
-BIG_TYPE_BENCHMARKS(4, 4);
-BIG_TYPE_BENCHMARKS(1, 8);
-BIG_TYPE_BENCHMARKS(8, 1);
-BIG_TYPE_BENCHMARKS(8, 8);
-BIG_TYPE_BENCHMARKS(1, 16);
-BIG_TYPE_BENCHMARKS(16, 1);
-BIG_TYPE_BENCHMARKS(16, 16);
-BIG_TYPE_BENCHMARKS(1, 32);
-BIG_TYPE_BENCHMARKS(32, 1);
-BIG_TYPE_BENCHMARKS(32, 32);
-#else
-BIG_TYPE_BENCHMARKS(32, 32);
-#endif
-#endif
-
-// Benchmark using unique_ptrs to large value types. In order to be able to use
-// the same benchmark code as the other types, use a type that holds a
-// unique_ptr and has a copy constructor.
-template <int Size>
-struct BigTypePtr {
-  BigTypePtr() : BigTypePtr(0) {}
-  explicit BigTypePtr(int x) {
-    ptr = absl::make_unique<BigType<Size, Size>>(x);
-  }
-  BigTypePtr(const BigTypePtr& other) {
-    ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr);
-  }
-  BigTypePtr(BigTypePtr&& other) noexcept = default;
-  BigTypePtr& operator=(const BigTypePtr& other) {
-    ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr);
-  }
-  BigTypePtr& operator=(BigTypePtr&& other) noexcept = default;
-
-  bool operator<(const BigTypePtr& other) const { return *ptr < *other.ptr; }
-  bool operator==(const BigTypePtr& other) const { return *ptr == *other.ptr; }
-
-  std::unique_ptr<BigType<Size, Size>> ptr;
-};
-
-template <int Size>
-double ContainerInfo(const btree_set<BigTypePtr<Size>>& b) {
-  const double bytes_used =
-      b.bytes_used() + b.size() * sizeof(BigType<Size, Size>);
-  const double bytes_per_value = bytes_used / b.size();
-  BtreeContainerInfoLog(b, bytes_used, bytes_per_value);
-  return bytes_per_value;
-}
-template <int Size>
-double ContainerInfo(const btree_map<int, BigTypePtr<Size>>& b) {
-  const double bytes_used =
-      b.bytes_used() + b.size() * sizeof(BigType<Size, Size>);
-  const double bytes_per_value = bytes_used / b.size();
-  BtreeContainerInfoLog(b, bytes_used, bytes_per_value);
-  return bytes_per_value;
-}
-
-#define BIG_TYPE_PTR_BENCHMARKS(SIZE)                                          \
-  using stl_set_size##SIZE##copies##SIZE##ptr = std::set<BigType<SIZE, SIZE>>; \
-  using stl_map_size##SIZE##copies##SIZE##ptr =                                \
-      std::map<int, BigType<SIZE, SIZE>>;                                      \
-  using stl_unordered_set_size##SIZE##copies##SIZE##ptr =                      \
-      std::unordered_set<BigType<SIZE, SIZE>,                                  \
-                         absl::Hash<BigType<SIZE, SIZE>>>;                     \
-  using stl_unordered_map_size##SIZE##copies##SIZE##ptr =                      \
-      std::unordered_map<int, BigType<SIZE, SIZE>>;                            \
-  using flat_hash_set_size##SIZE##copies##SIZE##ptr =                          \
-      flat_hash_set<BigType<SIZE, SIZE>>;                                      \
-  using flat_hash_map_size##SIZE##copies##SIZE##ptr =                          \
-      flat_hash_map<int, BigTypePtr<SIZE>>;                                    \
-  using btree_256_set_size##SIZE##copies##SIZE##ptr =                          \
-      btree_set<BigTypePtr<SIZE>>;                                             \
-  using btree_256_map_size##SIZE##copies##SIZE##ptr =                          \
-      btree_map<int, BigTypePtr<SIZE>>;                                        \
-  MY_BENCHMARK3(stl_set_size##SIZE##copies##SIZE##ptr);                        \
-  MY_BENCHMARK3(stl_unordered_set_size##SIZE##copies##SIZE##ptr);              \
-  MY_BENCHMARK3(flat_hash_set_size##SIZE##copies##SIZE##ptr);                  \
-  MY_BENCHMARK3(btree_256_set_size##SIZE##copies##SIZE##ptr);                  \
-  MY_BENCHMARK3(stl_map_size##SIZE##copies##SIZE##ptr);                        \
-  MY_BENCHMARK3(stl_unordered_map_size##SIZE##copies##SIZE##ptr);              \
-  MY_BENCHMARK3(flat_hash_map_size##SIZE##copies##SIZE##ptr);                  \
-  MY_BENCHMARK3(btree_256_map_size##SIZE##copies##SIZE##ptr)
-
-BIG_TYPE_PTR_BENCHMARKS(32);
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/btree_map.h b/third_party/abseil_cpp/absl/container/btree_map.h
deleted file mode 100644
index abc09b0ac0..0000000000
--- a/third_party/abseil_cpp/absl/container/btree_map.h
+++ /dev/null
@@ -1,769 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: btree_map.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines B-tree maps: sorted associative containers mapping
-// keys to values.
-//
-//     * `absl::btree_map<>`
-//     * `absl::btree_multimap<>`
-//
-// These B-tree types are similar to the corresponding types in the STL
-// (`std::map` and `std::multimap`) and generally conform to the STL interfaces
-// of those types. However, because they are implemented using B-trees, they
-// are more efficient in most situations.
-//
-// Unlike `std::map` and `std::multimap`, which are commonly implemented using
-// red-black tree nodes, B-tree maps use more generic B-tree nodes able to hold
-// multiple values per node. Holding multiple values per node often makes
-// B-tree maps perform better than their `std::map` counterparts, because
-// multiple entries can be checked within the same cache hit.
-//
-// However, these types should not be considered drop-in replacements for
-// `std::map` and `std::multimap` as there are some API differences, which are
-// noted in this header file.
-//
-// Importantly, insertions and deletions may invalidate outstanding iterators,
-// pointers, and references to elements. Such invalidations are typically only
-// an issue if insertion and deletion operations are interleaved with the use of
-// more than one iterator, pointer, or reference simultaneously. For this
-// reason, `insert()` and `erase()` return a valid iterator at the current
-// position.
-
-#ifndef ABSL_CONTAINER_BTREE_MAP_H_
-#define ABSL_CONTAINER_BTREE_MAP_H_
-
-#include "absl/container/internal/btree.h"  // IWYU pragma: export
-#include "absl/container/internal/btree_container.h"  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::btree_map<>
-//
-// An `absl::btree_map<K, V>` is an ordered associative container of
-// unique keys and associated values designed to be a more efficient replacement
-// for `std::map` (in most cases).
-//
-// Keys are sorted using an (optional) comparison function, which defaults to
-// `std::less<K>`.
-//
-// An `absl::btree_map<K, V>` uses a default allocator of
-// `std::allocator<std::pair<const K, V>>` to allocate (and deallocate)
-// nodes, and construct and destruct values within those nodes. You may
-// instead specify a custom allocator `A` (which in turn requires specifying a
-// custom comparator `C`) as in `absl::btree_map<K, V, C, A>`.
-//
-template <typename Key, typename Value, typename Compare = std::less<Key>,
-          typename Alloc = std::allocator<std::pair<const Key, Value>>>
-class btree_map
-    : public container_internal::btree_map_container<
-          container_internal::btree<container_internal::map_params<
-              Key, Value, Compare, Alloc, /*TargetNodeSize=*/256,
-              /*Multi=*/false>>> {
-  using Base = typename btree_map::btree_map_container;
-
- public:
-  // Constructors and Assignment Operators
-  //
-  // A `btree_map` supports the same overload set as `std::map`
-  // for construction and assignment:
-  //
-  // * Default constructor
-  //
-  //   absl::btree_map<int, std::string> map1;
-  //
-  // * Initializer List constructor
-  //
-  //   absl::btree_map<int, std::string> map2 =
-  //       {{1, "huey"}, {2, "dewey"}, {3, "louie"},};
-  //
-  // * Copy constructor
-  //
-  //   absl::btree_map<int, std::string> map3(map2);
-  //
-  // * Copy assignment operator
-  //
-  //  absl::btree_map<int, std::string> map4;
-  //  map4 = map3;
-  //
-  // * Move constructor
-  //
-  //   // Move is guaranteed efficient
-  //   absl::btree_map<int, std::string> map5(std::move(map4));
-  //
-  // * Move assignment operator
-  //
-  //   // May be efficient if allocators are compatible
-  //   absl::btree_map<int, std::string> map6;
-  //   map6 = std::move(map5);
-  //
-  // * Range constructor
-  //
-  //   std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}};
-  //   absl::btree_map<int, std::string> map7(v.begin(), v.end());
-  btree_map() {}
-  using Base::Base;
-
-  // btree_map::begin()
-  //
-  // Returns an iterator to the beginning of the `btree_map`.
-  using Base::begin;
-
-  // btree_map::cbegin()
-  //
-  // Returns a const iterator to the beginning of the `btree_map`.
-  using Base::cbegin;
-
-  // btree_map::end()
-  //
-  // Returns an iterator to the end of the `btree_map`.
-  using Base::end;
-
-  // btree_map::cend()
-  //
-  // Returns a const iterator to the end of the `btree_map`.
-  using Base::cend;
-
-  // btree_map::empty()
-  //
-  // Returns whether or not the `btree_map` is empty.
-  using Base::empty;
-
-  // btree_map::max_size()
-  //
-  // Returns the largest theoretical possible number of elements within a
-  // `btree_map` under current memory constraints. This value can be thought
-  // of as the largest value of `std::distance(begin(), end())` for a
-  // `btree_map<Key, T>`.
-  using Base::max_size;
-
-  // btree_map::size()
-  //
-  // Returns the number of elements currently within the `btree_map`.
-  using Base::size;
-
-  // btree_map::clear()
-  //
-  // Removes all elements from the `btree_map`. Invalidates any references,
-  // pointers, or iterators referring to contained elements.
-  using Base::clear;
-
-  // btree_map::erase()
-  //
-  // Erases elements within the `btree_map`. If an erase occurs, any references,
-  // pointers, or iterators are invalidated.
-  // Overloads are listed below.
-  //
-  // iterator erase(iterator position):
-  // iterator erase(const_iterator position):
-  //
-  //   Erases the element at `position` of the `btree_map`, returning
-  //   the iterator pointing to the element after the one that was erased
-  //   (or end() if none exists).
-  //
-  // iterator erase(const_iterator first, const_iterator last):
-  //
-  //   Erases the elements in the open interval [`first`, `last`), returning
-  //   the iterator pointing to the element after the interval that was erased
-  //   (or end() if none exists).
-  //
-  // template <typename K> size_type erase(const K& key):
-  //
-  //   Erases the element with the matching key, if it exists, returning the
-  //   number of elements erased (0 or 1).
-  using Base::erase;
-
-  // btree_map::insert()
-  //
-  // Inserts an element of the specified value into the `btree_map`,
-  // returning an iterator pointing to the newly inserted element, provided that
-  // an element with the given key does not already exist. If an insertion
-  // occurs, any references, pointers, or iterators are invalidated.
-  // Overloads are listed below.
-  //
-  // std::pair<iterator,bool> insert(const value_type& value):
-  //
-  //   Inserts a value into the `btree_map`. Returns a pair consisting of an
-  //   iterator to the inserted element (or to the element that prevented the
-  //   insertion) and a bool denoting whether the insertion took place.
-  //
-  // std::pair<iterator,bool> insert(value_type&& value):
-  //
-  //   Inserts a moveable value into the `btree_map`. Returns a pair
-  //   consisting of an iterator to the inserted element (or to the element that
-  //   prevented the insertion) and a bool denoting whether the insertion took
-  //   place.
-  //
-  // iterator insert(const_iterator hint, const value_type& value):
-  // iterator insert(const_iterator hint, value_type&& value):
-  //
-  //   Inserts a value, using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search. Returns an iterator to the
-  //   inserted element, or to the existing element that prevented the
-  //   insertion.
-  //
-  // void insert(InputIterator first, InputIterator last):
-  //
-  //   Inserts a range of values [`first`, `last`).
-  //
-  // void insert(std::initializer_list<init_type> ilist):
-  //
-  //   Inserts the elements within the initializer list `ilist`.
-  using Base::insert;
-
-  // btree_map::insert_or_assign()
-  //
-  // Inserts an element of the specified value into the `btree_map` provided
-  // that a value with the given key does not already exist, or replaces the
-  // corresponding mapped type with the forwarded `obj` argument if a key for
-  // that value already exists, returning an iterator pointing to the newly
-  // inserted element. Overloads are listed below.
-  //
-  // pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj):
-  // pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj):
-  //
-  //   Inserts/Assigns (or moves) the element of the specified key into the
-  //   `btree_map`. If the returned bool is true, insertion took place, and if
-  //   it's false, assignment took place.
-  //
-  // iterator insert_or_assign(const_iterator hint,
-  //                           const key_type& k, M&& obj):
-  // iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj):
-  //
-  //   Inserts/Assigns (or moves) the element of the specified key into the
-  //   `btree_map` using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search.
-  using Base::insert_or_assign;
-
-  // btree_map::emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `btree_map`, provided that no element with the given key
-  // already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately. Prefer `try_emplace()` unless your key is not
-  // copyable or moveable.
-  //
-  // If an insertion occurs, any references, pointers, or iterators are
-  // invalidated.
-  using Base::emplace;
-
-  // btree_map::emplace_hint()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `btree_map`, using the position of `hint` as a non-binding
-  // suggestion for where to begin the insertion search, and only inserts
-  // provided that no element with the given key already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately. Prefer `try_emplace()` unless your key is not
-  // copyable or moveable.
-  //
-  // If an insertion occurs, any references, pointers, or iterators are
-  // invalidated.
-  using Base::emplace_hint;
-
-  // btree_map::try_emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `btree_map`, provided that no element with the given key
-  // already exists. Unlike `emplace()`, if an element with the given key
-  // already exists, we guarantee that no element is constructed.
-  //
-  // If an insertion occurs, any references, pointers, or iterators are
-  // invalidated.
-  //
-  // Overloads are listed below.
-  //
-  //   std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args):
-  //   std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args):
-  //
-  // Inserts (via copy or move) the element of the specified key into the
-  // `btree_map`.
-  //
-  //   iterator try_emplace(const_iterator hint,
-  //                        const key_type& k, Args&&... args):
-  //   iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args):
-  //
-  // Inserts (via copy or move) the element of the specified key into the
-  // `btree_map` using the position of `hint` as a non-binding suggestion
-  // for where to begin the insertion search.
-  using Base::try_emplace;
-
-  // btree_map::extract()
-  //
-  // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
-  //
-  // node_type extract(const_iterator position):
-  //
-  //   Extracts the element at the indicated position and returns a node handle
-  //   owning that extracted data.
-  //
-  // template <typename K> node_type extract(const K& k):
-  //
-  //   Extracts the element with the key matching the passed key value and
-  //   returns a node handle owning that extracted data. If the `btree_map`
-  //   does not contain an element with a matching key, this function returns an
-  //   empty node handle.
-  //
-  // NOTE: when compiled in an earlier version of C++ than C++17,
-  // `node_type::key()` returns a const reference to the key instead of a
-  // mutable reference. We cannot safely return a mutable reference without
-  // std::launder (which is not available before C++17).
-  //
-  // NOTE: In this context, `node_type` refers to the C++17 concept of a
-  // move-only type that owns and provides access to the elements in associative
-  // containers (https://en.cppreference.com/w/cpp/container/node_handle).
-  // It does NOT refer to the data layout of the underlying btree.
-  using Base::extract;
-
-  // btree_map::merge()
-  //
-  // Extracts elements from a given `source` btree_map into this
-  // `btree_map`. If the destination `btree_map` already contains an
-  // element with an equivalent key, that element is not extracted.
-  using Base::merge;
-
-  // btree_map::swap(btree_map& other)
-  //
-  // Exchanges the contents of this `btree_map` with those of the `other`
-  // btree_map, avoiding invocation of any move, copy, or swap operations on
-  // individual elements.
-  //
-  // All iterators and references on the `btree_map` remain valid, excepting
-  // for the past-the-end iterator, which is invalidated.
-  using Base::swap;
-
-  // btree_map::at()
-  //
-  // Returns a reference to the mapped value of the element with key equivalent
-  // to the passed key.
-  using Base::at;
-
-  // btree_map::contains()
-  //
-  // template <typename K> bool contains(const K& key) const:
-  //
-  // Determines whether an element comparing equal to the given `key` exists
-  // within the `btree_map`, returning `true` if so or `false` otherwise.
-  //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
-  using Base::contains;
-
-  // btree_map::count()
-  //
-  // template <typename K> size_type count(const K& key) const:
-  //
-  // Returns the number of elements comparing equal to the given `key` within
-  // the `btree_map`. Note that this function will return either `1` or `0`
-  // since duplicate elements are not allowed within a `btree_map`.
-  //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
-  using Base::count;
-
-  // btree_map::equal_range()
-  //
-  // Returns a closed range [first, last], defined by a `std::pair` of two
-  // iterators, containing all elements with the passed key in the
-  // `btree_map`.
-  using Base::equal_range;
-
-  // btree_map::find()
-  //
-  // template <typename K> iterator find(const K& key):
-  // template <typename K> const_iterator find(const K& key) const:
-  //
-  // Finds an element with the passed `key` within the `btree_map`.
-  //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
-  using Base::find;
-
-  // btree_map::operator[]()
-  //
-  // Returns a reference to the value mapped to the passed key within the
-  // `btree_map`, performing an `insert()` if the key does not already
-  // exist.
-  //
-  // If an insertion occurs, any references, pointers, or iterators are
-  // invalidated. Otherwise iterators are not affected and references are not
-  // invalidated. Overloads are listed below.
-  //
-  // T& operator[](key_type&& key):
-  // T& operator[](const key_type& key):
-  //
-  //   Inserts a value_type object constructed in-place if the element with the
-  //   given key does not exist.
-  using Base::operator[];
-
-  // btree_map::get_allocator()
-  //
-  // Returns the allocator function associated with this `btree_map`.
-  using Base::get_allocator;
-
-  // btree_map::key_comp();
-  //
-  // Returns the key comparator associated with this `btree_map`.
-  using Base::key_comp;
-
-  // btree_map::value_comp();
-  //
-  // Returns the value comparator associated with this `btree_map`.
-  using Base::value_comp;
-};
-
-// absl::swap(absl::btree_map<>, absl::btree_map<>)
-//
-// Swaps the contents of two `absl::btree_map` containers.
-template <typename K, typename V, typename C, typename A>
-void swap(btree_map<K, V, C, A> &x, btree_map<K, V, C, A> &y) {
-  return x.swap(y);
-}
-
-// absl::erase_if(absl::btree_map<>, Pred)
-//
-// Erases all elements that satisfy the predicate pred from the container.
-template <typename K, typename V, typename C, typename A, typename Pred>
-void erase_if(btree_map<K, V, C, A> &map, Pred pred) {
-  for (auto it = map.begin(); it != map.end();) {
-    if (pred(*it)) {
-      it = map.erase(it);
-    } else {
-      ++it;
-    }
-  }
-}
-
-// absl::btree_multimap
-//
-// An `absl::btree_multimap<K, V>` is an ordered associative container of
-// keys and associated values designed to be a more efficient replacement for
-// `std::multimap` (in most cases). Unlike `absl::btree_map`, a B-tree multimap
-// allows multiple elements with equivalent keys.
-//
-// Keys are sorted using an (optional) comparison function, which defaults to
-// `std::less<K>`.
-//
-// An `absl::btree_multimap<K, V>` uses a default allocator of
-// `std::allocator<std::pair<const K, V>>` to allocate (and deallocate)
-// nodes, and construct and destruct values within those nodes. You may
-// instead specify a custom allocator `A` (which in turn requires specifying a
-// custom comparator `C`) as in `absl::btree_multimap<K, V, C, A>`.
-//
-template <typename Key, typename Value, typename Compare = std::less<Key>,
-          typename Alloc = std::allocator<std::pair<const Key, Value>>>
-class btree_multimap
-    : public container_internal::btree_multimap_container<
-          container_internal::btree<container_internal::map_params<
-              Key, Value, Compare, Alloc, /*TargetNodeSize=*/256,
-              /*Multi=*/true>>> {
-  using Base = typename btree_multimap::btree_multimap_container;
-
- public:
-  // Constructors and Assignment Operators
-  //
-  // A `btree_multimap` supports the same overload set as `std::multimap`
-  // for construction and assignment:
-  //
-  // * Default constructor
-  //
-  //   absl::btree_multimap<int, std::string> map1;
-  //
-  // * Initializer List constructor
-  //
-  //   absl::btree_multimap<int, std::string> map2 =
-  //       {{1, "huey"}, {2, "dewey"}, {3, "louie"},};
-  //
-  // * Copy constructor
-  //
-  //   absl::btree_multimap<int, std::string> map3(map2);
-  //
-  // * Copy assignment operator
-  //
-  //  absl::btree_multimap<int, std::string> map4;
-  //  map4 = map3;
-  //
-  // * Move constructor
-  //
-  //   // Move is guaranteed efficient
-  //   absl::btree_multimap<int, std::string> map5(std::move(map4));
-  //
-  // * Move assignment operator
-  //
-  //   // May be efficient if allocators are compatible
-  //   absl::btree_multimap<int, std::string> map6;
-  //   map6 = std::move(map5);
-  //
-  // * Range constructor
-  //
-  //   std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}};
-  //   absl::btree_multimap<int, std::string> map7(v.begin(), v.end());
-  btree_multimap() {}
-  using Base::Base;
-
-  // btree_multimap::begin()
-  //
-  // Returns an iterator to the beginning of the `btree_multimap`.
-  using Base::begin;
-
-  // btree_multimap::cbegin()
-  //
-  // Returns a const iterator to the beginning of the `btree_multimap`.
-  using Base::cbegin;
-
-  // btree_multimap::end()
-  //
-  // Returns an iterator to the end of the `btree_multimap`.
-  using Base::end;
-
-  // btree_multimap::cend()
-  //
-  // Returns a const iterator to the end of the `btree_multimap`.
-  using Base::cend;
-
-  // btree_multimap::empty()
-  //
-  // Returns whether or not the `btree_multimap` is empty.
-  using Base::empty;
-
-  // btree_multimap::max_size()
-  //
-  // Returns the largest theoretical possible number of elements within a
-  // `btree_multimap` under current memory constraints. This value can be
-  // thought of as the largest value of `std::distance(begin(), end())` for a
-  // `btree_multimap<Key, T>`.
-  using Base::max_size;
-
-  // btree_multimap::size()
-  //
-  // Returns the number of elements currently within the `btree_multimap`.
-  using Base::size;
-
-  // btree_multimap::clear()
-  //
-  // Removes all elements from the `btree_multimap`. Invalidates any references,
-  // pointers, or iterators referring to contained elements.
-  using Base::clear;
-
-  // btree_multimap::erase()
-  //
-  // Erases elements within the `btree_multimap`. If an erase occurs, any
-  // references, pointers, or iterators are invalidated.
-  // Overloads are listed below.
-  //
-  // iterator erase(iterator position):
-  // iterator erase(const_iterator position):
-  //
-  //   Erases the element at `position` of the `btree_multimap`, returning
-  //   the iterator pointing to the element after the one that was erased
-  //   (or end() if none exists).
-  //
-  // iterator erase(const_iterator first, const_iterator last):
-  //
-  //   Erases the elements in the open interval [`first`, `last`), returning
-  //   the iterator pointing to the element after the interval that was erased
-  //   (or end() if none exists).
-  //
-  // template <typename K> size_type erase(const K& key):
-  //
-  //   Erases the elements matching the key, if any exist, returning the
-  //   number of elements erased.
-  using Base::erase;
-
-  // btree_multimap::insert()
-  //
-  // Inserts an element of the specified value into the `btree_multimap`,
-  // returning an iterator pointing to the newly inserted element.
-  // Any references, pointers, or iterators are invalidated.  Overloads are
-  // listed below.
-  //
-  // iterator insert(const value_type& value):
-  //
-  //   Inserts a value into the `btree_multimap`, returning an iterator to the
-  //   inserted element.
-  //
-  // iterator insert(value_type&& value):
-  //
-  //   Inserts a moveable value into the `btree_multimap`, returning an iterator
-  //   to the inserted element.
-  //
-  // iterator insert(const_iterator hint, const value_type& value):
-  // iterator insert(const_iterator hint, value_type&& value):
-  //
-  //   Inserts a value, using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search. Returns an iterator to the
-  //   inserted element.
-  //
-  // void insert(InputIterator first, InputIterator last):
-  //
-  //   Inserts a range of values [`first`, `last`).
-  //
-  // void insert(std::initializer_list<init_type> ilist):
-  //
-  //   Inserts the elements within the initializer list `ilist`.
-  using Base::insert;
-
-  // btree_multimap::emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `btree_multimap`. Any references, pointers, or iterators are
-  // invalidated.
-  using Base::emplace;
-
-  // btree_multimap::emplace_hint()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `btree_multimap`, using the position of `hint` as a non-binding
-  // suggestion for where to begin the insertion search.
-  //
-  // Any references, pointers, or iterators are invalidated.
-  using Base::emplace_hint;
-
-  // btree_multimap::extract()
-  //
-  // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
-  //
-  // node_type extract(const_iterator position):
-  //
-  //   Extracts the element at the indicated position and returns a node handle
-  //   owning that extracted data.
-  //
-  // template <typename K> node_type extract(const K& k):
-  //
-  //   Extracts the element with the key matching the passed key value and
-  //   returns a node handle owning that extracted data. If the `btree_multimap`
-  //   does not contain an element with a matching key, this function returns an
-  //   empty node handle.
-  //
-  // NOTE: when compiled in an earlier version of C++ than C++17,
-  // `node_type::key()` returns a const reference to the key instead of a
-  // mutable reference. We cannot safely return a mutable reference without
-  // std::launder (which is not available before C++17).
-  //
-  // NOTE: In this context, `node_type` refers to the C++17 concept of a
-  // move-only type that owns and provides access to the elements in associative
-  // containers (https://en.cppreference.com/w/cpp/container/node_handle).
-  // It does NOT refer to the data layout of the underlying btree.
-  using Base::extract;
-
-  // btree_multimap::merge()
-  //
-  // Extracts elements from a given `source` btree_multimap into this
-  // `btree_multimap`. If the destination `btree_multimap` already contains an
-  // element with an equivalent key, that element is not extracted.
-  using Base::merge;
-
-  // btree_multimap::swap(btree_multimap& other)
-  //
-  // Exchanges the contents of this `btree_multimap` with those of the `other`
-  // btree_multimap, avoiding invocation of any move, copy, or swap operations
-  // on individual elements.
-  //
-  // All iterators and references on the `btree_multimap` remain valid,
-  // excepting for the past-the-end iterator, which is invalidated.
-  using Base::swap;
-
-  // btree_multimap::contains()
-  //
-  // template <typename K> bool contains(const K& key) const:
-  //
-  // Determines whether an element comparing equal to the given `key` exists
-  // within the `btree_multimap`, returning `true` if so or `false` otherwise.
-  //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
-  using Base::contains;
-
-  // btree_multimap::count()
-  //
-  // template <typename K> size_type count(const K& key) const:
-  //
-  // Returns the number of elements comparing equal to the given `key` within
-  // the `btree_multimap`.
-  //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
-  using Base::count;
-
-  // btree_multimap::equal_range()
-  //
-  // Returns a closed range [first, last], defined by a `std::pair` of two
-  // iterators, containing all elements with the passed key in the
-  // `btree_multimap`.
-  using Base::equal_range;
-
-  // btree_multimap::find()
-  //
-  // template <typename K> iterator find(const K& key):
-  // template <typename K> const_iterator find(const K& key) const:
-  //
-  // Finds an element with the passed `key` within the `btree_multimap`.
-  //
-  // Supports heterogeneous lookup, provided that the map is provided a
-  // compatible heterogeneous comparator.
-  using Base::find;
-
-  // btree_multimap::get_allocator()
-  //
-  // Returns the allocator function associated with this `btree_multimap`.
-  using Base::get_allocator;
-
-  // btree_multimap::key_comp();
-  //
-  // Returns the key comparator associated with this `btree_multimap`.
-  using Base::key_comp;
-
-  // btree_multimap::value_comp();
-  //
-  // Returns the value comparator associated with this `btree_multimap`.
-  using Base::value_comp;
-};
-
-// absl::swap(absl::btree_multimap<>, absl::btree_multimap<>)
-//
-// Swaps the contents of two `absl::btree_multimap` containers.
-template <typename K, typename V, typename C, typename A>
-void swap(btree_multimap<K, V, C, A> &x, btree_multimap<K, V, C, A> &y) {
-  return x.swap(y);
-}
-
-// absl::erase_if(absl::btree_multimap<>, Pred)
-//
-// Erases all elements that satisfy the predicate pred from the container.
-template <typename K, typename V, typename C, typename A, typename Pred>
-void erase_if(btree_multimap<K, V, C, A> &map, Pred pred) {
-  for (auto it = map.begin(); it != map.end();) {
-    if (pred(*it)) {
-      it = map.erase(it);
-    } else {
-      ++it;
-    }
-  }
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_BTREE_MAP_H_
diff --git a/third_party/abseil_cpp/absl/container/btree_set.h b/third_party/abseil_cpp/absl/container/btree_set.h
deleted file mode 100644
index 21ef0a032a..0000000000
--- a/third_party/abseil_cpp/absl/container/btree_set.h
+++ /dev/null
@@ -1,683 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: btree_set.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines B-tree sets: sorted associative containers of
-// values.
-//
-//     * `absl::btree_set<>`
-//     * `absl::btree_multiset<>`
-//
-// These B-tree types are similar to the corresponding types in the STL
-// (`std::set` and `std::multiset`) and generally conform to the STL interfaces
-// of those types. However, because they are implemented using B-trees, they
-// are more efficient in most situations.
-//
-// Unlike `std::set` and `std::multiset`, which are commonly implemented using
-// red-black tree nodes, B-tree sets use more generic B-tree nodes able to hold
-// multiple values per node. Holding multiple values per node often makes
-// B-tree sets perform better than their `std::set` counterparts, because
-// multiple entries can be checked within the same cache hit.
-//
-// However, these types should not be considered drop-in replacements for
-// `std::set` and `std::multiset` as there are some API differences, which are
-// noted in this header file.
-//
-// Importantly, insertions and deletions may invalidate outstanding iterators,
-// pointers, and references to elements. Such invalidations are typically only
-// an issue if insertion and deletion operations are interleaved with the use of
-// more than one iterator, pointer, or reference simultaneously. For this
-// reason, `insert()` and `erase()` return a valid iterator at the current
-// position.
-
-#ifndef ABSL_CONTAINER_BTREE_SET_H_
-#define ABSL_CONTAINER_BTREE_SET_H_
-
-#include "absl/container/internal/btree.h"  // IWYU pragma: export
-#include "absl/container/internal/btree_container.h"  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::btree_set<>
-//
-// An `absl::btree_set<K>` is an ordered associative container of unique key
-// values designed to be a more efficient replacement for `std::set` (in most
-// cases).
-//
-// Keys are sorted using an (optional) comparison function, which defaults to
-// `std::less<K>`.
-//
-// An `absl::btree_set<K>` uses a default allocator of `std::allocator<K>` to
-// allocate (and deallocate) nodes, and construct and destruct values within
-// those nodes. You may instead specify a custom allocator `A` (which in turn
-// requires specifying a custom comparator `C`) as in
-// `absl::btree_set<K, C, A>`.
-//
-template <typename Key, typename Compare = std::less<Key>,
-          typename Alloc = std::allocator<Key>>
-class btree_set
-    : public container_internal::btree_set_container<
-          container_internal::btree<container_internal::set_params<
-              Key, Compare, Alloc, /*TargetNodeSize=*/256,
-              /*Multi=*/false>>> {
-  using Base = typename btree_set::btree_set_container;
-
- public:
-  // Constructors and Assignment Operators
-  //
-  // A `btree_set` supports the same overload set as `std::set`
-  // for construction and assignment:
-  //
-  // * Default constructor
-  //
-  //   absl::btree_set<std::string> set1;
-  //
-  // * Initializer List constructor
-  //
-  //   absl::btree_set<std::string> set2 =
-  //       {{"huey"}, {"dewey"}, {"louie"},};
-  //
-  // * Copy constructor
-  //
-  //   absl::btree_set<std::string> set3(set2);
-  //
-  // * Copy assignment operator
-  //
-  //  absl::btree_set<std::string> set4;
-  //  set4 = set3;
-  //
-  // * Move constructor
-  //
-  //   // Move is guaranteed efficient
-  //   absl::btree_set<std::string> set5(std::move(set4));
-  //
-  // * Move assignment operator
-  //
-  //   // May be efficient if allocators are compatible
-  //   absl::btree_set<std::string> set6;
-  //   set6 = std::move(set5);
-  //
-  // * Range constructor
-  //
-  //   std::vector<std::string> v = {"a", "b"};
-  //   absl::btree_set<std::string> set7(v.begin(), v.end());
-  btree_set() {}
-  using Base::Base;
-
-  // btree_set::begin()
-  //
-  // Returns an iterator to the beginning of the `btree_set`.
-  using Base::begin;
-
-  // btree_set::cbegin()
-  //
-  // Returns a const iterator to the beginning of the `btree_set`.
-  using Base::cbegin;
-
-  // btree_set::end()
-  //
-  // Returns an iterator to the end of the `btree_set`.
-  using Base::end;
-
-  // btree_set::cend()
-  //
-  // Returns a const iterator to the end of the `btree_set`.
-  using Base::cend;
-
-  // btree_set::empty()
-  //
-  // Returns whether or not the `btree_set` is empty.
-  using Base::empty;
-
-  // btree_set::max_size()
-  //
-  // Returns the largest theoretical possible number of elements within a
-  // `btree_set` under current memory constraints. This value can be thought
-  // of as the largest value of `std::distance(begin(), end())` for a
-  // `btree_set<Key>`.
-  using Base::max_size;
-
-  // btree_set::size()
-  //
-  // Returns the number of elements currently within the `btree_set`.
-  using Base::size;
-
-  // btree_set::clear()
-  //
-  // Removes all elements from the `btree_set`. Invalidates any references,
-  // pointers, or iterators referring to contained elements.
-  using Base::clear;
-
-  // btree_set::erase()
-  //
-  // Erases elements within the `btree_set`. Overloads are listed below.
-  //
-  // iterator erase(iterator position):
-  // iterator erase(const_iterator position):
-  //
-  //   Erases the element at `position` of the `btree_set`, returning
-  //   the iterator pointing to the element after the one that was erased
-  //   (or end() if none exists).
-  //
-  // iterator erase(const_iterator first, const_iterator last):
-  //
-  //   Erases the elements in the open interval [`first`, `last`), returning
-  //   the iterator pointing to the element after the interval that was erased
-  //   (or end() if none exists).
-  //
-  // template <typename K> size_type erase(const K& key):
-  //
-  //   Erases the element with the matching key, if it exists, returning the
-  //   number of elements erased (0 or 1).
-  using Base::erase;
-
-  // btree_set::insert()
-  //
-  // Inserts an element of the specified value into the `btree_set`,
-  // returning an iterator pointing to the newly inserted element, provided that
-  // an element with the given key does not already exist. If an insertion
-  // occurs, any references, pointers, or iterators are invalidated.
-  // Overloads are listed below.
-  //
-  // std::pair<iterator,bool> insert(const value_type& value):
-  //
-  //   Inserts a value into the `btree_set`. Returns a pair consisting of an
-  //   iterator to the inserted element (or to the element that prevented the
-  //   insertion) and a bool denoting whether the insertion took place.
-  //
-  // std::pair<iterator,bool> insert(value_type&& value):
-  //
-  //   Inserts a moveable value into the `btree_set`. Returns a pair
-  //   consisting of an iterator to the inserted element (or to the element that
-  //   prevented the insertion) and a bool denoting whether the insertion took
-  //   place.
-  //
-  // iterator insert(const_iterator hint, const value_type& value):
-  // iterator insert(const_iterator hint, value_type&& value):
-  //
-  //   Inserts a value, using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search. Returns an iterator to the
-  //   inserted element, or to the existing element that prevented the
-  //   insertion.
-  //
-  // void insert(InputIterator first, InputIterator last):
-  //
-  //   Inserts a range of values [`first`, `last`).
-  //
-  // void insert(std::initializer_list<init_type> ilist):
-  //
-  //   Inserts the elements within the initializer list `ilist`.
-  using Base::insert;
-
-  // btree_set::emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `btree_set`, provided that no element with the given key
-  // already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately.
-  //
-  // If an insertion occurs, any references, pointers, or iterators are
-  // invalidated.
-  using Base::emplace;
-
-  // btree_set::emplace_hint()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `btree_set`, using the position of `hint` as a non-binding
-  // suggestion for where to begin the insertion search, and only inserts
-  // provided that no element with the given key already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately.
-  //
-  // If an insertion occurs, any references, pointers, or iterators are
-  // invalidated.
-  using Base::emplace_hint;
-
-  // btree_set::extract()
-  //
-  // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
-  //
-  // node_type extract(const_iterator position):
-  //
-  //   Extracts the element at the indicated position and returns a node handle
-  //   owning that extracted data.
-  //
-  // template <typename K> node_type extract(const K& k):
-  //
-  //   Extracts the element with the key matching the passed key value and
-  //   returns a node handle owning that extracted data. If the `btree_set`
-  //   does not contain an element with a matching key, this function returns an
-  //   empty node handle.
-  //
-  // NOTE: In this context, `node_type` refers to the C++17 concept of a
-  // move-only type that owns and provides access to the elements in associative
-  // containers (https://en.cppreference.com/w/cpp/container/node_handle).
-  // It does NOT refer to the data layout of the underlying btree.
-  using Base::extract;
-
-  // btree_set::merge()
-  //
-  // Extracts elements from a given `source` btree_set into this
-  // `btree_set`. If the destination `btree_set` already contains an
-  // element with an equivalent key, that element is not extracted.
-  using Base::merge;
-
-  // btree_set::swap(btree_set& other)
-  //
-  // Exchanges the contents of this `btree_set` with those of the `other`
-  // btree_set, avoiding invocation of any move, copy, or swap operations on
-  // individual elements.
-  //
-  // All iterators and references on the `btree_set` remain valid, excepting
-  // for the past-the-end iterator, which is invalidated.
-  using Base::swap;
-
-  // btree_set::contains()
-  //
-  // template <typename K> bool contains(const K& key) const:
-  //
-  // Determines whether an element comparing equal to the given `key` exists
-  // within the `btree_set`, returning `true` if so or `false` otherwise.
-  //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
-  using Base::contains;
-
-  // btree_set::count()
-  //
-  // template <typename K> size_type count(const K& key) const:
-  //
-  // Returns the number of elements comparing equal to the given `key` within
-  // the `btree_set`. Note that this function will return either `1` or `0`
-  // since duplicate elements are not allowed within a `btree_set`.
-  //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
-  using Base::count;
-
-  // btree_set::equal_range()
-  //
-  // Returns a closed range [first, last], defined by a `std::pair` of two
-  // iterators, containing all elements with the passed key in the
-  // `btree_set`.
-  using Base::equal_range;
-
-  // btree_set::find()
-  //
-  // template <typename K> iterator find(const K& key):
-  // template <typename K> const_iterator find(const K& key) const:
-  //
-  // Finds an element with the passed `key` within the `btree_set`.
-  //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
-  using Base::find;
-
-  // btree_set::get_allocator()
-  //
-  // Returns the allocator function associated with this `btree_set`.
-  using Base::get_allocator;
-
-  // btree_set::key_comp();
-  //
-  // Returns the key comparator associated with this `btree_set`.
-  using Base::key_comp;
-
-  // btree_set::value_comp();
-  //
-  // Returns the value comparator associated with this `btree_set`. The keys to
-  // sort the elements are the values themselves, therefore `value_comp` and its
-  // sibling member function `key_comp` are equivalent.
-  using Base::value_comp;
-};
-
-// absl::swap(absl::btree_set<>, absl::btree_set<>)
-//
-// Swaps the contents of two `absl::btree_set` containers.
-template <typename K, typename C, typename A>
-void swap(btree_set<K, C, A> &x, btree_set<K, C, A> &y) {
-  return x.swap(y);
-}
-
-// absl::erase_if(absl::btree_set<>, Pred)
-//
-// Erases all elements that satisfy the predicate pred from the container.
-template <typename K, typename C, typename A, typename Pred>
-void erase_if(btree_set<K, C, A> &set, Pred pred) {
-  for (auto it = set.begin(); it != set.end();) {
-    if (pred(*it)) {
-      it = set.erase(it);
-    } else {
-      ++it;
-    }
-  }
-}
-
-// absl::btree_multiset<>
-//
-// An `absl::btree_multiset<K>` is an ordered associative container of
-// keys and associated values designed to be a more efficient replacement
-// for `std::multiset` (in most cases). Unlike `absl::btree_set`, a B-tree
-// multiset allows equivalent elements.
-//
-// Keys are sorted using an (optional) comparison function, which defaults to
-// `std::less<K>`.
-//
-// An `absl::btree_multiset<K>` uses a default allocator of `std::allocator<K>`
-// to allocate (and deallocate) nodes, and construct and destruct values within
-// those nodes. You may instead specify a custom allocator `A` (which in turn
-// requires specifying a custom comparator `C`) as in
-// `absl::btree_multiset<K, C, A>`.
-//
-template <typename Key, typename Compare = std::less<Key>,
-          typename Alloc = std::allocator<Key>>
-class btree_multiset
-    : public container_internal::btree_multiset_container<
-          container_internal::btree<container_internal::set_params<
-              Key, Compare, Alloc, /*TargetNodeSize=*/256,
-              /*Multi=*/true>>> {
-  using Base = typename btree_multiset::btree_multiset_container;
-
- public:
-  // Constructors and Assignment Operators
-  //
-  // A `btree_multiset` supports the same overload set as `std::set`
-  // for construction and assignment:
-  //
-  // * Default constructor
-  //
-  //   absl::btree_multiset<std::string> set1;
-  //
-  // * Initializer List constructor
-  //
-  //   absl::btree_multiset<std::string> set2 =
-  //       {{"huey"}, {"dewey"}, {"louie"},};
-  //
-  // * Copy constructor
-  //
-  //   absl::btree_multiset<std::string> set3(set2);
-  //
-  // * Copy assignment operator
-  //
-  //  absl::btree_multiset<std::string> set4;
-  //  set4 = set3;
-  //
-  // * Move constructor
-  //
-  //   // Move is guaranteed efficient
-  //   absl::btree_multiset<std::string> set5(std::move(set4));
-  //
-  // * Move assignment operator
-  //
-  //   // May be efficient if allocators are compatible
-  //   absl::btree_multiset<std::string> set6;
-  //   set6 = std::move(set5);
-  //
-  // * Range constructor
-  //
-  //   std::vector<std::string> v = {"a", "b"};
-  //   absl::btree_multiset<std::string> set7(v.begin(), v.end());
-  btree_multiset() {}
-  using Base::Base;
-
-  // btree_multiset::begin()
-  //
-  // Returns an iterator to the beginning of the `btree_multiset`.
-  using Base::begin;
-
-  // btree_multiset::cbegin()
-  //
-  // Returns a const iterator to the beginning of the `btree_multiset`.
-  using Base::cbegin;
-
-  // btree_multiset::end()
-  //
-  // Returns an iterator to the end of the `btree_multiset`.
-  using Base::end;
-
-  // btree_multiset::cend()
-  //
-  // Returns a const iterator to the end of the `btree_multiset`.
-  using Base::cend;
-
-  // btree_multiset::empty()
-  //
-  // Returns whether or not the `btree_multiset` is empty.
-  using Base::empty;
-
-  // btree_multiset::max_size()
-  //
-  // Returns the largest theoretical possible number of elements within a
-  // `btree_multiset` under current memory constraints. This value can be
-  // thought of as the largest value of `std::distance(begin(), end())` for a
-  // `btree_multiset<Key>`.
-  using Base::max_size;
-
-  // btree_multiset::size()
-  //
-  // Returns the number of elements currently within the `btree_multiset`.
-  using Base::size;
-
-  // btree_multiset::clear()
-  //
-  // Removes all elements from the `btree_multiset`. Invalidates any references,
-  // pointers, or iterators referring to contained elements.
-  using Base::clear;
-
-  // btree_multiset::erase()
-  //
-  // Erases elements within the `btree_multiset`. Overloads are listed below.
-  //
-  // iterator erase(iterator position):
-  // iterator erase(const_iterator position):
-  //
-  //   Erases the element at `position` of the `btree_multiset`, returning
-  //   the iterator pointing to the element after the one that was erased
-  //   (or end() if none exists).
-  //
-  // iterator erase(const_iterator first, const_iterator last):
-  //
-  //   Erases the elements in the open interval [`first`, `last`), returning
-  //   the iterator pointing to the element after the interval that was erased
-  //   (or end() if none exists).
-  //
-  // template <typename K> size_type erase(const K& key):
-  //
-  //   Erases the elements matching the key, if any exist, returning the
-  //   number of elements erased.
-  using Base::erase;
-
-  // btree_multiset::insert()
-  //
-  // Inserts an element of the specified value into the `btree_multiset`,
-  // returning an iterator pointing to the newly inserted element.
-  // Any references, pointers, or iterators are invalidated.  Overloads are
-  // listed below.
-  //
-  // iterator insert(const value_type& value):
-  //
-  //   Inserts a value into the `btree_multiset`, returning an iterator to the
-  //   inserted element.
-  //
-  // iterator insert(value_type&& value):
-  //
-  //   Inserts a moveable value into the `btree_multiset`, returning an iterator
-  //   to the inserted element.
-  //
-  // iterator insert(const_iterator hint, const value_type& value):
-  // iterator insert(const_iterator hint, value_type&& value):
-  //
-  //   Inserts a value, using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search. Returns an iterator to the
-  //   inserted element.
-  //
-  // void insert(InputIterator first, InputIterator last):
-  //
-  //   Inserts a range of values [`first`, `last`).
-  //
-  // void insert(std::initializer_list<init_type> ilist):
-  //
-  //   Inserts the elements within the initializer list `ilist`.
-  using Base::insert;
-
-  // btree_multiset::emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `btree_multiset`. Any references, pointers, or iterators are
-  // invalidated.
-  using Base::emplace;
-
-  // btree_multiset::emplace_hint()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `btree_multiset`, using the position of `hint` as a non-binding
-  // suggestion for where to begin the insertion search.
-  //
-  // Any references, pointers, or iterators are invalidated.
-  using Base::emplace_hint;
-
-  // btree_multiset::extract()
-  //
-  // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
-  //
-  // node_type extract(const_iterator position):
-  //
-  //   Extracts the element at the indicated position and returns a node handle
-  //   owning that extracted data.
-  //
-  // template <typename K> node_type extract(const K& k):
-  //
-  //   Extracts the element with the key matching the passed key value and
-  //   returns a node handle owning that extracted data. If the `btree_multiset`
-  //   does not contain an element with a matching key, this function returns an
-  //   empty node handle.
-  //
-  // NOTE: In this context, `node_type` refers to the C++17 concept of a
-  // move-only type that owns and provides access to the elements in associative
-  // containers (https://en.cppreference.com/w/cpp/container/node_handle).
-  // It does NOT refer to the data layout of the underlying btree.
-  using Base::extract;
-
-  // btree_multiset::merge()
-  //
-  // Extracts elements from a given `source` btree_multiset into this
-  // `btree_multiset`. If the destination `btree_multiset` already contains an
-  // element with an equivalent key, that element is not extracted.
-  using Base::merge;
-
-  // btree_multiset::swap(btree_multiset& other)
-  //
-  // Exchanges the contents of this `btree_multiset` with those of the `other`
-  // btree_multiset, avoiding invocation of any move, copy, or swap operations
-  // on individual elements.
-  //
-  // All iterators and references on the `btree_multiset` remain valid,
-  // excepting for the past-the-end iterator, which is invalidated.
-  using Base::swap;
-
-  // btree_multiset::contains()
-  //
-  // template <typename K> bool contains(const K& key) const:
-  //
-  // Determines whether an element comparing equal to the given `key` exists
-  // within the `btree_multiset`, returning `true` if so or `false` otherwise.
-  //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
-  using Base::contains;
-
-  // btree_multiset::count()
-  //
-  // template <typename K> size_type count(const K& key) const:
-  //
-  // Returns the number of elements comparing equal to the given `key` within
-  // the `btree_multiset`.
-  //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
-  using Base::count;
-
-  // btree_multiset::equal_range()
-  //
-  // Returns a closed range [first, last], defined by a `std::pair` of two
-  // iterators, containing all elements with the passed key in the
-  // `btree_multiset`.
-  using Base::equal_range;
-
-  // btree_multiset::find()
-  //
-  // template <typename K> iterator find(const K& key):
-  // template <typename K> const_iterator find(const K& key) const:
-  //
-  // Finds an element with the passed `key` within the `btree_multiset`.
-  //
-  // Supports heterogeneous lookup, provided that the set is provided a
-  // compatible heterogeneous comparator.
-  using Base::find;
-
-  // btree_multiset::get_allocator()
-  //
-  // Returns the allocator function associated with this `btree_multiset`.
-  using Base::get_allocator;
-
-  // btree_multiset::key_comp();
-  //
-  // Returns the key comparator associated with this `btree_multiset`.
-  using Base::key_comp;
-
-  // btree_multiset::value_comp();
-  //
-  // Returns the value comparator associated with this `btree_multiset`. The
-  // keys to sort the elements are the values themselves, therefore `value_comp`
-  // and its sibling member function `key_comp` are equivalent.
-  using Base::value_comp;
-};
-
-// absl::swap(absl::btree_multiset<>, absl::btree_multiset<>)
-//
-// Swaps the contents of two `absl::btree_multiset` containers.
-template <typename K, typename C, typename A>
-void swap(btree_multiset<K, C, A> &x, btree_multiset<K, C, A> &y) {
-  return x.swap(y);
-}
-
-// absl::erase_if(absl::btree_multiset<>, Pred)
-//
-// Erases all elements that satisfy the predicate pred from the container.
-template <typename K, typename C, typename A, typename Pred>
-void erase_if(btree_multiset<K, C, A> &set, Pred pred) {
-  for (auto it = set.begin(); it != set.end();) {
-    if (pred(*it)) {
-      it = set.erase(it);
-    } else {
-      ++it;
-    }
-  }
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_BTREE_SET_H_
diff --git a/third_party/abseil_cpp/absl/container/btree_test.cc b/third_party/abseil_cpp/absl/container/btree_test.cc
deleted file mode 100644
index 9b1b6436c7..0000000000
--- a/third_party/abseil_cpp/absl/container/btree_test.cc
+++ /dev/null
@@ -1,2827 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/btree_test.h"
-
-#include <cstdint>
-#include <limits>
-#include <map>
-#include <memory>
-#include <stdexcept>
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/container/btree_map.h"
-#include "absl/container/btree_set.h"
-#include "absl/container/internal/counting_allocator.h"
-#include "absl/container/internal/test_instance_tracker.h"
-#include "absl/flags/flag.h"
-#include "absl/hash/hash_testing.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_split.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/compare.h"
-
-ABSL_FLAG(int, test_values, 10000, "The number of values to use for tests");
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using ::absl::test_internal::CopyableMovableInstance;
-using ::absl::test_internal::InstanceTracker;
-using ::absl::test_internal::MovableOnlyInstance;
-using ::testing::ElementsAre;
-using ::testing::ElementsAreArray;
-using ::testing::IsEmpty;
-using ::testing::IsNull;
-using ::testing::Pair;
-using ::testing::SizeIs;
-
-template <typename T, typename U>
-void CheckPairEquals(const T &x, const U &y) {
-  ABSL_INTERNAL_CHECK(x == y, "Values are unequal.");
-}
-
-template <typename T, typename U, typename V, typename W>
-void CheckPairEquals(const std::pair<T, U> &x, const std::pair<V, W> &y) {
-  CheckPairEquals(x.first, y.first);
-  CheckPairEquals(x.second, y.second);
-}
-}  // namespace
-
-// The base class for a sorted associative container checker. TreeType is the
-// container type to check and CheckerType is the container type to check
-// against. TreeType is expected to be btree_{set,map,multiset,multimap} and
-// CheckerType is expected to be {set,map,multiset,multimap}.
-template <typename TreeType, typename CheckerType>
-class base_checker {
- public:
-  using key_type = typename TreeType::key_type;
-  using value_type = typename TreeType::value_type;
-  using key_compare = typename TreeType::key_compare;
-  using pointer = typename TreeType::pointer;
-  using const_pointer = typename TreeType::const_pointer;
-  using reference = typename TreeType::reference;
-  using const_reference = typename TreeType::const_reference;
-  using size_type = typename TreeType::size_type;
-  using difference_type = typename TreeType::difference_type;
-  using iterator = typename TreeType::iterator;
-  using const_iterator = typename TreeType::const_iterator;
-  using reverse_iterator = typename TreeType::reverse_iterator;
-  using const_reverse_iterator = typename TreeType::const_reverse_iterator;
-
- public:
-  base_checker() : const_tree_(tree_) {}
-  base_checker(const base_checker &other)
-      : tree_(other.tree_), const_tree_(tree_), checker_(other.checker_) {}
-  template <typename InputIterator>
-  base_checker(InputIterator b, InputIterator e)
-      : tree_(b, e), const_tree_(tree_), checker_(b, e) {}
-
-  iterator begin() { return tree_.begin(); }
-  const_iterator begin() const { return tree_.begin(); }
-  iterator end() { return tree_.end(); }
-  const_iterator end() const { return tree_.end(); }
-  reverse_iterator rbegin() { return tree_.rbegin(); }
-  const_reverse_iterator rbegin() const { return tree_.rbegin(); }
-  reverse_iterator rend() { return tree_.rend(); }
-  const_reverse_iterator rend() const { return tree_.rend(); }
-
-  template <typename IterType, typename CheckerIterType>
-  IterType iter_check(IterType tree_iter, CheckerIterType checker_iter) const {
-    if (tree_iter == tree_.end()) {
-      ABSL_INTERNAL_CHECK(checker_iter == checker_.end(),
-                          "Checker iterator not at end.");
-    } else {
-      CheckPairEquals(*tree_iter, *checker_iter);
-    }
-    return tree_iter;
-  }
-  template <typename IterType, typename CheckerIterType>
-  IterType riter_check(IterType tree_iter, CheckerIterType checker_iter) const {
-    if (tree_iter == tree_.rend()) {
-      ABSL_INTERNAL_CHECK(checker_iter == checker_.rend(),
-                          "Checker iterator not at rend.");
-    } else {
-      CheckPairEquals(*tree_iter, *checker_iter);
-    }
-    return tree_iter;
-  }
-  void value_check(const value_type &v) {
-    typename KeyOfValue<typename TreeType::key_type,
-                        typename TreeType::value_type>::type key_of_value;
-    const key_type &key = key_of_value(v);
-    CheckPairEquals(*find(key), v);
-    lower_bound(key);
-    upper_bound(key);
-    equal_range(key);
-    contains(key);
-    count(key);
-  }
-  void erase_check(const key_type &key) {
-    EXPECT_FALSE(tree_.contains(key));
-    EXPECT_EQ(tree_.find(key), const_tree_.end());
-    EXPECT_FALSE(const_tree_.contains(key));
-    EXPECT_EQ(const_tree_.find(key), tree_.end());
-    EXPECT_EQ(tree_.equal_range(key).first,
-              const_tree_.equal_range(key).second);
-  }
-
-  iterator lower_bound(const key_type &key) {
-    return iter_check(tree_.lower_bound(key), checker_.lower_bound(key));
-  }
-  const_iterator lower_bound(const key_type &key) const {
-    return iter_check(tree_.lower_bound(key), checker_.lower_bound(key));
-  }
-  iterator upper_bound(const key_type &key) {
-    return iter_check(tree_.upper_bound(key), checker_.upper_bound(key));
-  }
-  const_iterator upper_bound(const key_type &key) const {
-    return iter_check(tree_.upper_bound(key), checker_.upper_bound(key));
-  }
-  std::pair<iterator, iterator> equal_range(const key_type &key) {
-    std::pair<typename CheckerType::iterator, typename CheckerType::iterator>
-        checker_res = checker_.equal_range(key);
-    std::pair<iterator, iterator> tree_res = tree_.equal_range(key);
-    iter_check(tree_res.first, checker_res.first);
-    iter_check(tree_res.second, checker_res.second);
-    return tree_res;
-  }
-  std::pair<const_iterator, const_iterator> equal_range(
-      const key_type &key) const {
-    std::pair<typename CheckerType::const_iterator,
-              typename CheckerType::const_iterator>
-        checker_res = checker_.equal_range(key);
-    std::pair<const_iterator, const_iterator> tree_res = tree_.equal_range(key);
-    iter_check(tree_res.first, checker_res.first);
-    iter_check(tree_res.second, checker_res.second);
-    return tree_res;
-  }
-  iterator find(const key_type &key) {
-    return iter_check(tree_.find(key), checker_.find(key));
-  }
-  const_iterator find(const key_type &key) const {
-    return iter_check(tree_.find(key), checker_.find(key));
-  }
-  bool contains(const key_type &key) const { return find(key) != end(); }
-  size_type count(const key_type &key) const {
-    size_type res = checker_.count(key);
-    EXPECT_EQ(res, tree_.count(key));
-    return res;
-  }
-
-  base_checker &operator=(const base_checker &other) {
-    tree_ = other.tree_;
-    checker_ = other.checker_;
-    return *this;
-  }
-
-  int erase(const key_type &key) {
-    int size = tree_.size();
-    int res = checker_.erase(key);
-    EXPECT_EQ(res, tree_.count(key));
-    EXPECT_EQ(res, tree_.erase(key));
-    EXPECT_EQ(tree_.count(key), 0);
-    EXPECT_EQ(tree_.size(), size - res);
-    erase_check(key);
-    return res;
-  }
-  iterator erase(iterator iter) {
-    key_type key = iter.key();
-    int size = tree_.size();
-    int count = tree_.count(key);
-    auto checker_iter = checker_.lower_bound(key);
-    for (iterator tmp(tree_.lower_bound(key)); tmp != iter; ++tmp) {
-      ++checker_iter;
-    }
-    auto checker_next = checker_iter;
-    ++checker_next;
-    checker_.erase(checker_iter);
-    iter = tree_.erase(iter);
-    EXPECT_EQ(tree_.size(), checker_.size());
-    EXPECT_EQ(tree_.size(), size - 1);
-    EXPECT_EQ(tree_.count(key), count - 1);
-    if (count == 1) {
-      erase_check(key);
-    }
-    return iter_check(iter, checker_next);
-  }
-
-  void erase(iterator begin, iterator end) {
-    int size = tree_.size();
-    int count = std::distance(begin, end);
-    auto checker_begin = checker_.lower_bound(begin.key());
-    for (iterator tmp(tree_.lower_bound(begin.key())); tmp != begin; ++tmp) {
-      ++checker_begin;
-    }
-    auto checker_end =
-        end == tree_.end() ? checker_.end() : checker_.lower_bound(end.key());
-    if (end != tree_.end()) {
-      for (iterator tmp(tree_.lower_bound(end.key())); tmp != end; ++tmp) {
-        ++checker_end;
-      }
-    }
-    const auto checker_ret = checker_.erase(checker_begin, checker_end);
-    const auto tree_ret = tree_.erase(begin, end);
-    EXPECT_EQ(std::distance(checker_.begin(), checker_ret),
-              std::distance(tree_.begin(), tree_ret));
-    EXPECT_EQ(tree_.size(), checker_.size());
-    EXPECT_EQ(tree_.size(), size - count);
-  }
-
-  void clear() {
-    tree_.clear();
-    checker_.clear();
-  }
-  void swap(base_checker &other) {
-    tree_.swap(other.tree_);
-    checker_.swap(other.checker_);
-  }
-
-  void verify() const {
-    tree_.verify();
-    EXPECT_EQ(tree_.size(), checker_.size());
-
-    // Move through the forward iterators using increment.
-    auto checker_iter = checker_.begin();
-    const_iterator tree_iter(tree_.begin());
-    for (; tree_iter != tree_.end(); ++tree_iter, ++checker_iter) {
-      CheckPairEquals(*tree_iter, *checker_iter);
-    }
-
-    // Move through the forward iterators using decrement.
-    for (int n = tree_.size() - 1; n >= 0; --n) {
-      iter_check(tree_iter, checker_iter);
-      --tree_iter;
-      --checker_iter;
-    }
-    EXPECT_EQ(tree_iter, tree_.begin());
-    EXPECT_EQ(checker_iter, checker_.begin());
-
-    // Move through the reverse iterators using increment.
-    auto checker_riter = checker_.rbegin();
-    const_reverse_iterator tree_riter(tree_.rbegin());
-    for (; tree_riter != tree_.rend(); ++tree_riter, ++checker_riter) {
-      CheckPairEquals(*tree_riter, *checker_riter);
-    }
-
-    // Move through the reverse iterators using decrement.
-    for (int n = tree_.size() - 1; n >= 0; --n) {
-      riter_check(tree_riter, checker_riter);
-      --tree_riter;
-      --checker_riter;
-    }
-    EXPECT_EQ(tree_riter, tree_.rbegin());
-    EXPECT_EQ(checker_riter, checker_.rbegin());
-  }
-
-  const TreeType &tree() const { return tree_; }
-
-  size_type size() const {
-    EXPECT_EQ(tree_.size(), checker_.size());
-    return tree_.size();
-  }
-  size_type max_size() const { return tree_.max_size(); }
-  bool empty() const {
-    EXPECT_EQ(tree_.empty(), checker_.empty());
-    return tree_.empty();
-  }
-
- protected:
-  TreeType tree_;
-  const TreeType &const_tree_;
-  CheckerType checker_;
-};
-
-namespace {
-// A checker for unique sorted associative containers. TreeType is expected to
-// be btree_{set,map} and CheckerType is expected to be {set,map}.
-template <typename TreeType, typename CheckerType>
-class unique_checker : public base_checker<TreeType, CheckerType> {
-  using super_type = base_checker<TreeType, CheckerType>;
-
- public:
-  using iterator = typename super_type::iterator;
-  using value_type = typename super_type::value_type;
-
- public:
-  unique_checker() : super_type() {}
-  unique_checker(const unique_checker &other) : super_type(other) {}
-  template <class InputIterator>
-  unique_checker(InputIterator b, InputIterator e) : super_type(b, e) {}
-  unique_checker &operator=(const unique_checker &) = default;
-
-  // Insertion routines.
-  std::pair<iterator, bool> insert(const value_type &v) {
-    int size = this->tree_.size();
-    std::pair<typename CheckerType::iterator, bool> checker_res =
-        this->checker_.insert(v);
-    std::pair<iterator, bool> tree_res = this->tree_.insert(v);
-    CheckPairEquals(*tree_res.first, *checker_res.first);
-    EXPECT_EQ(tree_res.second, checker_res.second);
-    EXPECT_EQ(this->tree_.size(), this->checker_.size());
-    EXPECT_EQ(this->tree_.size(), size + tree_res.second);
-    return tree_res;
-  }
-  iterator insert(iterator position, const value_type &v) {
-    int size = this->tree_.size();
-    std::pair<typename CheckerType::iterator, bool> checker_res =
-        this->checker_.insert(v);
-    iterator tree_res = this->tree_.insert(position, v);
-    CheckPairEquals(*tree_res, *checker_res.first);
-    EXPECT_EQ(this->tree_.size(), this->checker_.size());
-    EXPECT_EQ(this->tree_.size(), size + checker_res.second);
-    return tree_res;
-  }
-  template <typename InputIterator>
-  void insert(InputIterator b, InputIterator e) {
-    for (; b != e; ++b) {
-      insert(*b);
-    }
-  }
-};
-
-// A checker for multiple sorted associative containers. TreeType is expected
-// to be btree_{multiset,multimap} and CheckerType is expected to be
-// {multiset,multimap}.
-template <typename TreeType, typename CheckerType>
-class multi_checker : public base_checker<TreeType, CheckerType> {
-  using super_type = base_checker<TreeType, CheckerType>;
-
- public:
-  using iterator = typename super_type::iterator;
-  using value_type = typename super_type::value_type;
-
- public:
-  multi_checker() : super_type() {}
-  multi_checker(const multi_checker &other) : super_type(other) {}
-  template <class InputIterator>
-  multi_checker(InputIterator b, InputIterator e) : super_type(b, e) {}
-  multi_checker &operator=(const multi_checker &) = default;
-
-  // Insertion routines.
-  iterator insert(const value_type &v) {
-    int size = this->tree_.size();
-    auto checker_res = this->checker_.insert(v);
-    iterator tree_res = this->tree_.insert(v);
-    CheckPairEquals(*tree_res, *checker_res);
-    EXPECT_EQ(this->tree_.size(), this->checker_.size());
-    EXPECT_EQ(this->tree_.size(), size + 1);
-    return tree_res;
-  }
-  iterator insert(iterator position, const value_type &v) {
-    int size = this->tree_.size();
-    auto checker_res = this->checker_.insert(v);
-    iterator tree_res = this->tree_.insert(position, v);
-    CheckPairEquals(*tree_res, *checker_res);
-    EXPECT_EQ(this->tree_.size(), this->checker_.size());
-    EXPECT_EQ(this->tree_.size(), size + 1);
-    return tree_res;
-  }
-  template <typename InputIterator>
-  void insert(InputIterator b, InputIterator e) {
-    for (; b != e; ++b) {
-      insert(*b);
-    }
-  }
-};
-
-template <typename T, typename V>
-void DoTest(const char *name, T *b, const std::vector<V> &values) {
-  typename KeyOfValue<typename T::key_type, V>::type key_of_value;
-
-  T &mutable_b = *b;
-  const T &const_b = *b;
-
-  // Test insert.
-  for (int i = 0; i < values.size(); ++i) {
-    mutable_b.insert(values[i]);
-    mutable_b.value_check(values[i]);
-  }
-  ASSERT_EQ(mutable_b.size(), values.size());
-
-  const_b.verify();
-
-  // Test copy constructor.
-  T b_copy(const_b);
-  EXPECT_EQ(b_copy.size(), const_b.size());
-  for (int i = 0; i < values.size(); ++i) {
-    CheckPairEquals(*b_copy.find(key_of_value(values[i])), values[i]);
-  }
-
-  // Test range constructor.
-  T b_range(const_b.begin(), const_b.end());
-  EXPECT_EQ(b_range.size(), const_b.size());
-  for (int i = 0; i < values.size(); ++i) {
-    CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]);
-  }
-
-  // Test range insertion for values that already exist.
-  b_range.insert(b_copy.begin(), b_copy.end());
-  b_range.verify();
-
-  // Test range insertion for new values.
-  b_range.clear();
-  b_range.insert(b_copy.begin(), b_copy.end());
-  EXPECT_EQ(b_range.size(), b_copy.size());
-  for (int i = 0; i < values.size(); ++i) {
-    CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]);
-  }
-
-  // Test assignment to self. Nothing should change.
-  b_range.operator=(b_range);
-  EXPECT_EQ(b_range.size(), b_copy.size());
-
-  // Test assignment of new values.
-  b_range.clear();
-  b_range = b_copy;
-  EXPECT_EQ(b_range.size(), b_copy.size());
-
-  // Test swap.
-  b_range.clear();
-  b_range.swap(b_copy);
-  EXPECT_EQ(b_copy.size(), 0);
-  EXPECT_EQ(b_range.size(), const_b.size());
-  for (int i = 0; i < values.size(); ++i) {
-    CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]);
-  }
-  b_range.swap(b_copy);
-
-  // Test non-member function swap.
-  swap(b_range, b_copy);
-  EXPECT_EQ(b_copy.size(), 0);
-  EXPECT_EQ(b_range.size(), const_b.size());
-  for (int i = 0; i < values.size(); ++i) {
-    CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]);
-  }
-  swap(b_range, b_copy);
-
-  // Test erase via values.
-  for (int i = 0; i < values.size(); ++i) {
-    mutable_b.erase(key_of_value(values[i]));
-    // Erasing a non-existent key should have no effect.
-    ASSERT_EQ(mutable_b.erase(key_of_value(values[i])), 0);
-  }
-
-  const_b.verify();
-  EXPECT_EQ(const_b.size(), 0);
-
-  // Test erase via iterators.
-  mutable_b = b_copy;
-  for (int i = 0; i < values.size(); ++i) {
-    mutable_b.erase(mutable_b.find(key_of_value(values[i])));
-  }
-
-  const_b.verify();
-  EXPECT_EQ(const_b.size(), 0);
-
-  // Test insert with hint.
-  for (int i = 0; i < values.size(); i++) {
-    mutable_b.insert(mutable_b.upper_bound(key_of_value(values[i])), values[i]);
-  }
-
-  const_b.verify();
-
-  // Test range erase.
-  mutable_b.erase(mutable_b.begin(), mutable_b.end());
-  EXPECT_EQ(mutable_b.size(), 0);
-  const_b.verify();
-
-  // First half.
-  mutable_b = b_copy;
-  typename T::iterator mutable_iter_end = mutable_b.begin();
-  for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_end;
-  mutable_b.erase(mutable_b.begin(), mutable_iter_end);
-  EXPECT_EQ(mutable_b.size(), values.size() - values.size() / 2);
-  const_b.verify();
-
-  // Second half.
-  mutable_b = b_copy;
-  typename T::iterator mutable_iter_begin = mutable_b.begin();
-  for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_begin;
-  mutable_b.erase(mutable_iter_begin, mutable_b.end());
-  EXPECT_EQ(mutable_b.size(), values.size() / 2);
-  const_b.verify();
-
-  // Second quarter.
-  mutable_b = b_copy;
-  mutable_iter_begin = mutable_b.begin();
-  for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_begin;
-  mutable_iter_end = mutable_iter_begin;
-  for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_end;
-  mutable_b.erase(mutable_iter_begin, mutable_iter_end);
-  EXPECT_EQ(mutable_b.size(), values.size() - values.size() / 4);
-  const_b.verify();
-
-  mutable_b.clear();
-}
-
-template <typename T>
-void ConstTest() {
-  using value_type = typename T::value_type;
-  typename KeyOfValue<typename T::key_type, value_type>::type key_of_value;
-
-  T mutable_b;
-  const T &const_b = mutable_b;
-
-  // Insert a single value into the container and test looking it up.
-  value_type value = Generator<value_type>(2)(2);
-  mutable_b.insert(value);
-  EXPECT_TRUE(mutable_b.contains(key_of_value(value)));
-  EXPECT_NE(mutable_b.find(key_of_value(value)), const_b.end());
-  EXPECT_TRUE(const_b.contains(key_of_value(value)));
-  EXPECT_NE(const_b.find(key_of_value(value)), mutable_b.end());
-  EXPECT_EQ(*const_b.lower_bound(key_of_value(value)), value);
-  EXPECT_EQ(const_b.upper_bound(key_of_value(value)), const_b.end());
-  EXPECT_EQ(*const_b.equal_range(key_of_value(value)).first, value);
-
-  // We can only create a non-const iterator from a non-const container.
-  typename T::iterator mutable_iter(mutable_b.begin());
-  EXPECT_EQ(mutable_iter, const_b.begin());
-  EXPECT_NE(mutable_iter, const_b.end());
-  EXPECT_EQ(const_b.begin(), mutable_iter);
-  EXPECT_NE(const_b.end(), mutable_iter);
-  typename T::reverse_iterator mutable_riter(mutable_b.rbegin());
-  EXPECT_EQ(mutable_riter, const_b.rbegin());
-  EXPECT_NE(mutable_riter, const_b.rend());
-  EXPECT_EQ(const_b.rbegin(), mutable_riter);
-  EXPECT_NE(const_b.rend(), mutable_riter);
-
-  // We can create a const iterator from a non-const iterator.
-  typename T::const_iterator const_iter(mutable_iter);
-  EXPECT_EQ(const_iter, mutable_b.begin());
-  EXPECT_NE(const_iter, mutable_b.end());
-  EXPECT_EQ(mutable_b.begin(), const_iter);
-  EXPECT_NE(mutable_b.end(), const_iter);
-  typename T::const_reverse_iterator const_riter(mutable_riter);
-  EXPECT_EQ(const_riter, mutable_b.rbegin());
-  EXPECT_NE(const_riter, mutable_b.rend());
-  EXPECT_EQ(mutable_b.rbegin(), const_riter);
-  EXPECT_NE(mutable_b.rend(), const_riter);
-
-  // Make sure various methods can be invoked on a const container.
-  const_b.verify();
-  ASSERT_TRUE(!const_b.empty());
-  EXPECT_EQ(const_b.size(), 1);
-  EXPECT_GT(const_b.max_size(), 0);
-  EXPECT_TRUE(const_b.contains(key_of_value(value)));
-  EXPECT_EQ(const_b.count(key_of_value(value)), 1);
-}
-
-template <typename T, typename C>
-void BtreeTest() {
-  ConstTest<T>();
-
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  const std::vector<V> random_values = GenerateValuesWithSeed<V>(
-      absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values),
-      testing::GTEST_FLAG(random_seed));
-
-  unique_checker<T, C> container;
-
-  // Test key insertion/deletion in sorted order.
-  std::vector<V> sorted_values(random_values);
-  std::sort(sorted_values.begin(), sorted_values.end());
-  DoTest("sorted:    ", &container, sorted_values);
-
-  // Test key insertion/deletion in reverse sorted order.
-  std::reverse(sorted_values.begin(), sorted_values.end());
-  DoTest("rsorted:   ", &container, sorted_values);
-
-  // Test key insertion/deletion in random order.
-  DoTest("random:    ", &container, random_values);
-}
-
-template <typename T, typename C>
-void BtreeMultiTest() {
-  ConstTest<T>();
-
-  using V = typename remove_pair_const<typename T::value_type>::type;
-  const std::vector<V> random_values = GenerateValuesWithSeed<V>(
-      absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values),
-      testing::GTEST_FLAG(random_seed));
-
-  multi_checker<T, C> container;
-
-  // Test keys in sorted order.
-  std::vector<V> sorted_values(random_values);
-  std::sort(sorted_values.begin(), sorted_values.end());
-  DoTest("sorted:    ", &container, sorted_values);
-
-  // Test keys in reverse sorted order.
-  std::reverse(sorted_values.begin(), sorted_values.end());
-  DoTest("rsorted:   ", &container, sorted_values);
-
-  // Test keys in random order.
-  DoTest("random:    ", &container, random_values);
-
-  // Test keys in random order w/ duplicates.
-  std::vector<V> duplicate_values(random_values);
-  duplicate_values.insert(duplicate_values.end(), random_values.begin(),
-                          random_values.end());
-  DoTest("duplicates:", &container, duplicate_values);
-
-  // Test all identical keys.
-  std::vector<V> identical_values(100);
-  std::fill(identical_values.begin(), identical_values.end(),
-            Generator<V>(2)(2));
-  DoTest("identical: ", &container, identical_values);
-}
-
-template <typename T>
-struct PropagatingCountingAlloc : public CountingAllocator<T> {
-  using propagate_on_container_copy_assignment = std::true_type;
-  using propagate_on_container_move_assignment = std::true_type;
-  using propagate_on_container_swap = std::true_type;
-
-  using Base = CountingAllocator<T>;
-  using Base::Base;
-
-  template <typename U>
-  explicit PropagatingCountingAlloc(const PropagatingCountingAlloc<U> &other)
-      : Base(other.bytes_used_) {}
-
-  template <typename U>
-  struct rebind {
-    using other = PropagatingCountingAlloc<U>;
-  };
-};
-
-template <typename T>
-void BtreeAllocatorTest() {
-  using value_type = typename T::value_type;
-
-  int64_t bytes1 = 0, bytes2 = 0;
-  PropagatingCountingAlloc<T> allocator1(&bytes1);
-  PropagatingCountingAlloc<T> allocator2(&bytes2);
-  Generator<value_type> generator(1000);
-
-  // Test that we allocate properly aligned memory. If we don't, then Layout
-  // will assert fail.
-  auto unused1 = allocator1.allocate(1);
-  auto unused2 = allocator2.allocate(1);
-
-  // Test copy assignment
-  {
-    T b1(typename T::key_compare(), allocator1);
-    T b2(typename T::key_compare(), allocator2);
-
-    int64_t original_bytes1 = bytes1;
-    b1.insert(generator(0));
-    EXPECT_GT(bytes1, original_bytes1);
-
-    // This should propagate the allocator.
-    b1 = b2;
-    EXPECT_EQ(b1.size(), 0);
-    EXPECT_EQ(b2.size(), 0);
-    EXPECT_EQ(bytes1, original_bytes1);
-
-    for (int i = 1; i < 1000; i++) {
-      b1.insert(generator(i));
-    }
-
-    // We should have allocated out of allocator2.
-    EXPECT_GT(bytes2, bytes1);
-  }
-
-  // Test move assignment
-  {
-    T b1(typename T::key_compare(), allocator1);
-    T b2(typename T::key_compare(), allocator2);
-
-    int64_t original_bytes1 = bytes1;
-    b1.insert(generator(0));
-    EXPECT_GT(bytes1, original_bytes1);
-
-    // This should propagate the allocator.
-    b1 = std::move(b2);
-    EXPECT_EQ(b1.size(), 0);
-    EXPECT_EQ(bytes1, original_bytes1);
-
-    for (int i = 1; i < 1000; i++) {
-      b1.insert(generator(i));
-    }
-
-    // We should have allocated out of allocator2.
-    EXPECT_GT(bytes2, bytes1);
-  }
-
-  // Test swap
-  {
-    T b1(typename T::key_compare(), allocator1);
-    T b2(typename T::key_compare(), allocator2);
-
-    int64_t original_bytes1 = bytes1;
-    b1.insert(generator(0));
-    EXPECT_GT(bytes1, original_bytes1);
-
-    // This should swap the allocators.
-    swap(b1, b2);
-    EXPECT_EQ(b1.size(), 0);
-    EXPECT_EQ(b2.size(), 1);
-    EXPECT_GT(bytes1, original_bytes1);
-
-    for (int i = 1; i < 1000; i++) {
-      b1.insert(generator(i));
-    }
-
-    // We should have allocated out of allocator2.
-    EXPECT_GT(bytes2, bytes1);
-  }
-
-  allocator1.deallocate(unused1, 1);
-  allocator2.deallocate(unused2, 1);
-}
-
-template <typename T>
-void BtreeMapTest() {
-  using value_type = typename T::value_type;
-  using mapped_type = typename T::mapped_type;
-
-  mapped_type m = Generator<mapped_type>(0)(0);
-  (void)m;
-
-  T b;
-
-  // Verify we can insert using operator[].
-  for (int i = 0; i < 1000; i++) {
-    value_type v = Generator<value_type>(1000)(i);
-    b[v.first] = v.second;
-  }
-  EXPECT_EQ(b.size(), 1000);
-
-  // Test whether we can use the "->" operator on iterators and
-  // reverse_iterators. This stresses the btree_map_params::pair_pointer
-  // mechanism.
-  EXPECT_EQ(b.begin()->first, Generator<value_type>(1000)(0).first);
-  EXPECT_EQ(b.begin()->second, Generator<value_type>(1000)(0).second);
-  EXPECT_EQ(b.rbegin()->first, Generator<value_type>(1000)(999).first);
-  EXPECT_EQ(b.rbegin()->second, Generator<value_type>(1000)(999).second);
-}
-
-template <typename T>
-void BtreeMultiMapTest() {
-  using mapped_type = typename T::mapped_type;
-  mapped_type m = Generator<mapped_type>(0)(0);
-  (void)m;
-}
-
-template <typename K, int N = 256>
-void SetTest() {
-  EXPECT_EQ(
-      sizeof(absl::btree_set<K>),
-      2 * sizeof(void *) + sizeof(typename absl::btree_set<K>::size_type));
-  using BtreeSet = absl::btree_set<K>;
-  using CountingBtreeSet =
-      absl::btree_set<K, std::less<K>, PropagatingCountingAlloc<K>>;
-  BtreeTest<BtreeSet, std::set<K>>();
-  BtreeAllocatorTest<CountingBtreeSet>();
-}
-
-template <typename K, int N = 256>
-void MapTest() {
-  EXPECT_EQ(
-      sizeof(absl::btree_map<K, K>),
-      2 * sizeof(void *) + sizeof(typename absl::btree_map<K, K>::size_type));
-  using BtreeMap = absl::btree_map<K, K>;
-  using CountingBtreeMap =
-      absl::btree_map<K, K, std::less<K>,
-                      PropagatingCountingAlloc<std::pair<const K, K>>>;
-  BtreeTest<BtreeMap, std::map<K, K>>();
-  BtreeAllocatorTest<CountingBtreeMap>();
-  BtreeMapTest<BtreeMap>();
-}
-
-TEST(Btree, set_int32) { SetTest<int32_t>(); }
-TEST(Btree, set_int64) { SetTest<int64_t>(); }
-TEST(Btree, set_string) { SetTest<std::string>(); }
-TEST(Btree, set_cord) { SetTest<absl::Cord>(); }
-TEST(Btree, set_pair) { SetTest<std::pair<int, int>>(); }
-TEST(Btree, map_int32) { MapTest<int32_t>(); }
-TEST(Btree, map_int64) { MapTest<int64_t>(); }
-TEST(Btree, map_string) { MapTest<std::string>(); }
-TEST(Btree, map_cord) { MapTest<absl::Cord>(); }
-TEST(Btree, map_pair) { MapTest<std::pair<int, int>>(); }
-
-template <typename K, int N = 256>
-void MultiSetTest() {
-  EXPECT_EQ(
-      sizeof(absl::btree_multiset<K>),
-      2 * sizeof(void *) + sizeof(typename absl::btree_multiset<K>::size_type));
-  using BtreeMSet = absl::btree_multiset<K>;
-  using CountingBtreeMSet =
-      absl::btree_multiset<K, std::less<K>, PropagatingCountingAlloc<K>>;
-  BtreeMultiTest<BtreeMSet, std::multiset<K>>();
-  BtreeAllocatorTest<CountingBtreeMSet>();
-}
-
-template <typename K, int N = 256>
-void MultiMapTest() {
-  EXPECT_EQ(sizeof(absl::btree_multimap<K, K>),
-            2 * sizeof(void *) +
-                sizeof(typename absl::btree_multimap<K, K>::size_type));
-  using BtreeMMap = absl::btree_multimap<K, K>;
-  using CountingBtreeMMap =
-      absl::btree_multimap<K, K, std::less<K>,
-                           PropagatingCountingAlloc<std::pair<const K, K>>>;
-  BtreeMultiTest<BtreeMMap, std::multimap<K, K>>();
-  BtreeMultiMapTest<BtreeMMap>();
-  BtreeAllocatorTest<CountingBtreeMMap>();
-}
-
-TEST(Btree, multiset_int32) { MultiSetTest<int32_t>(); }
-TEST(Btree, multiset_int64) { MultiSetTest<int64_t>(); }
-TEST(Btree, multiset_string) { MultiSetTest<std::string>(); }
-TEST(Btree, multiset_cord) { MultiSetTest<absl::Cord>(); }
-TEST(Btree, multiset_pair) { MultiSetTest<std::pair<int, int>>(); }
-TEST(Btree, multimap_int32) { MultiMapTest<int32_t>(); }
-TEST(Btree, multimap_int64) { MultiMapTest<int64_t>(); }
-TEST(Btree, multimap_string) { MultiMapTest<std::string>(); }
-TEST(Btree, multimap_cord) { MultiMapTest<absl::Cord>(); }
-TEST(Btree, multimap_pair) { MultiMapTest<std::pair<int, int>>(); }
-
-struct CompareIntToString {
-  bool operator()(const std::string &a, const std::string &b) const {
-    return a < b;
-  }
-  bool operator()(const std::string &a, int b) const {
-    return a < absl::StrCat(b);
-  }
-  bool operator()(int a, const std::string &b) const {
-    return absl::StrCat(a) < b;
-  }
-  using is_transparent = void;
-};
-
-struct NonTransparentCompare {
-  template <typename T, typename U>
-  bool operator()(const T &t, const U &u) const {
-    // Treating all comparators as transparent can cause inefficiencies (see
-    // N3657 C++ proposal). Test that for comparators without 'is_transparent'
-    // alias (like this one), we do not attempt heterogeneous lookup.
-    EXPECT_TRUE((std::is_same<T, U>()));
-    return t < u;
-  }
-};
-
-template <typename T>
-bool CanEraseWithEmptyBrace(T t, decltype(t.erase({})) *) {
-  return true;
-}
-
-template <typename T>
-bool CanEraseWithEmptyBrace(T, ...) {
-  return false;
-}
-
-template <typename T>
-void TestHeterogeneous(T table) {
-  auto lb = table.lower_bound("3");
-  EXPECT_EQ(lb, table.lower_bound(3));
-  EXPECT_NE(lb, table.lower_bound(4));
-  EXPECT_EQ(lb, table.lower_bound({"3"}));
-  EXPECT_NE(lb, table.lower_bound({}));
-
-  auto ub = table.upper_bound("3");
-  EXPECT_EQ(ub, table.upper_bound(3));
-  EXPECT_NE(ub, table.upper_bound(5));
-  EXPECT_EQ(ub, table.upper_bound({"3"}));
-  EXPECT_NE(ub, table.upper_bound({}));
-
-  auto er = table.equal_range("3");
-  EXPECT_EQ(er, table.equal_range(3));
-  EXPECT_NE(er, table.equal_range(4));
-  EXPECT_EQ(er, table.equal_range({"3"}));
-  EXPECT_NE(er, table.equal_range({}));
-
-  auto it = table.find("3");
-  EXPECT_EQ(it, table.find(3));
-  EXPECT_NE(it, table.find(4));
-  EXPECT_EQ(it, table.find({"3"}));
-  EXPECT_NE(it, table.find({}));
-
-  EXPECT_TRUE(table.contains(3));
-  EXPECT_FALSE(table.contains(4));
-  EXPECT_TRUE(table.count({"3"}));
-  EXPECT_FALSE(table.contains({}));
-
-  EXPECT_EQ(1, table.count(3));
-  EXPECT_EQ(0, table.count(4));
-  EXPECT_EQ(1, table.count({"3"}));
-  EXPECT_EQ(0, table.count({}));
-
-  auto copy = table;
-  copy.erase(3);
-  EXPECT_EQ(table.size() - 1, copy.size());
-  copy.erase(4);
-  EXPECT_EQ(table.size() - 1, copy.size());
-  copy.erase({"5"});
-  EXPECT_EQ(table.size() - 2, copy.size());
-  EXPECT_FALSE(CanEraseWithEmptyBrace(table, nullptr));
-
-  // Also run it with const T&.
-  if (std::is_class<T>()) TestHeterogeneous<const T &>(table);
-}
-
-TEST(Btree, HeterogeneousLookup) {
-  TestHeterogeneous(btree_set<std::string, CompareIntToString>{"1", "3", "5"});
-  TestHeterogeneous(btree_map<std::string, int, CompareIntToString>{
-      {"1", 1}, {"3", 3}, {"5", 5}});
-  TestHeterogeneous(
-      btree_multiset<std::string, CompareIntToString>{"1", "3", "5"});
-  TestHeterogeneous(btree_multimap<std::string, int, CompareIntToString>{
-      {"1", 1}, {"3", 3}, {"5", 5}});
-
-  // Only maps have .at()
-  btree_map<std::string, int, CompareIntToString> map{
-      {"", -1}, {"1", 1}, {"3", 3}, {"5", 5}};
-  EXPECT_EQ(1, map.at(1));
-  EXPECT_EQ(3, map.at({"3"}));
-  EXPECT_EQ(-1, map.at({}));
-  const auto &cmap = map;
-  EXPECT_EQ(1, cmap.at(1));
-  EXPECT_EQ(3, cmap.at({"3"}));
-  EXPECT_EQ(-1, cmap.at({}));
-}
-
-TEST(Btree, NoHeterogeneousLookupWithoutAlias) {
-  using StringSet = absl::btree_set<std::string, NonTransparentCompare>;
-  StringSet s;
-  ASSERT_TRUE(s.insert("hello").second);
-  ASSERT_TRUE(s.insert("world").second);
-  EXPECT_TRUE(s.end() == s.find("blah"));
-  EXPECT_TRUE(s.begin() == s.lower_bound("hello"));
-  EXPECT_EQ(1, s.count("world"));
-  EXPECT_TRUE(s.contains("hello"));
-  EXPECT_TRUE(s.contains("world"));
-  EXPECT_FALSE(s.contains("blah"));
-
-  using StringMultiSet =
-      absl::btree_multiset<std::string, NonTransparentCompare>;
-  StringMultiSet ms;
-  ms.insert("hello");
-  ms.insert("world");
-  ms.insert("world");
-  EXPECT_TRUE(ms.end() == ms.find("blah"));
-  EXPECT_TRUE(ms.begin() == ms.lower_bound("hello"));
-  EXPECT_EQ(2, ms.count("world"));
-  EXPECT_TRUE(ms.contains("hello"));
-  EXPECT_TRUE(ms.contains("world"));
-  EXPECT_FALSE(ms.contains("blah"));
-}
-
-TEST(Btree, DefaultTransparent) {
-  {
-    // `int` does not have a default transparent comparator.
-    // The input value is converted to key_type.
-    btree_set<int> s = {1};
-    double d = 1.1;
-    EXPECT_EQ(s.begin(), s.find(d));
-    EXPECT_TRUE(s.contains(d));
-  }
-
-  {
-    // `std::string` has heterogeneous support.
-    btree_set<std::string> s = {"A"};
-    EXPECT_EQ(s.begin(), s.find(absl::string_view("A")));
-    EXPECT_TRUE(s.contains(absl::string_view("A")));
-  }
-}
-
-class StringLike {
- public:
-  StringLike() = default;
-
-  StringLike(const char *s) : s_(s) {  // NOLINT
-    ++constructor_calls_;
-  }
-
-  bool operator<(const StringLike &a) const { return s_ < a.s_; }
-
-  static void clear_constructor_call_count() { constructor_calls_ = 0; }
-
-  static int constructor_calls() { return constructor_calls_; }
-
- private:
-  static int constructor_calls_;
-  std::string s_;
-};
-
-int StringLike::constructor_calls_ = 0;
-
-TEST(Btree, HeterogeneousLookupDoesntDegradePerformance) {
-  using StringSet = absl::btree_set<StringLike>;
-  StringSet s;
-  for (int i = 0; i < 100; ++i) {
-    ASSERT_TRUE(s.insert(absl::StrCat(i).c_str()).second);
-  }
-  StringLike::clear_constructor_call_count();
-  s.find("50");
-  ASSERT_EQ(1, StringLike::constructor_calls());
-
-  StringLike::clear_constructor_call_count();
-  s.contains("50");
-  ASSERT_EQ(1, StringLike::constructor_calls());
-
-  StringLike::clear_constructor_call_count();
-  s.count("50");
-  ASSERT_EQ(1, StringLike::constructor_calls());
-
-  StringLike::clear_constructor_call_count();
-  s.lower_bound("50");
-  ASSERT_EQ(1, StringLike::constructor_calls());
-
-  StringLike::clear_constructor_call_count();
-  s.upper_bound("50");
-  ASSERT_EQ(1, StringLike::constructor_calls());
-
-  StringLike::clear_constructor_call_count();
-  s.equal_range("50");
-  ASSERT_EQ(1, StringLike::constructor_calls());
-
-  StringLike::clear_constructor_call_count();
-  s.erase("50");
-  ASSERT_EQ(1, StringLike::constructor_calls());
-}
-
-// Verify that swapping btrees swaps the key comparison functors and that we can
-// use non-default constructible comparators.
-struct SubstringLess {
-  SubstringLess() = delete;
-  explicit SubstringLess(int length) : n(length) {}
-  bool operator()(const std::string &a, const std::string &b) const {
-    return absl::string_view(a).substr(0, n) <
-           absl::string_view(b).substr(0, n);
-  }
-  int n;
-};
-
-TEST(Btree, SwapKeyCompare) {
-  using SubstringSet = absl::btree_set<std::string, SubstringLess>;
-  SubstringSet s1(SubstringLess(1), SubstringSet::allocator_type());
-  SubstringSet s2(SubstringLess(2), SubstringSet::allocator_type());
-
-  ASSERT_TRUE(s1.insert("a").second);
-  ASSERT_FALSE(s1.insert("aa").second);
-
-  ASSERT_TRUE(s2.insert("a").second);
-  ASSERT_TRUE(s2.insert("aa").second);
-  ASSERT_FALSE(s2.insert("aaa").second);
-
-  swap(s1, s2);
-
-  ASSERT_TRUE(s1.insert("b").second);
-  ASSERT_TRUE(s1.insert("bb").second);
-  ASSERT_FALSE(s1.insert("bbb").second);
-
-  ASSERT_TRUE(s2.insert("b").second);
-  ASSERT_FALSE(s2.insert("bb").second);
-}
-
-TEST(Btree, UpperBoundRegression) {
-  // Regress a bug where upper_bound would default-construct a new key_compare
-  // instead of copying the existing one.
-  using SubstringSet = absl::btree_set<std::string, SubstringLess>;
-  SubstringSet my_set(SubstringLess(3));
-  my_set.insert("aab");
-  my_set.insert("abb");
-  // We call upper_bound("aaa").  If this correctly uses the length 3
-  // comparator, aaa < aab < abb, so we should get aab as the result.
-  // If it instead uses the default-constructed length 2 comparator,
-  // aa == aa < ab, so we'll get abb as our result.
-  SubstringSet::iterator it = my_set.upper_bound("aaa");
-  ASSERT_TRUE(it != my_set.end());
-  EXPECT_EQ("aab", *it);
-}
-
-TEST(Btree, Comparison) {
-  const int kSetSize = 1201;
-  absl::btree_set<int64_t> my_set;
-  for (int i = 0; i < kSetSize; ++i) {
-    my_set.insert(i);
-  }
-  absl::btree_set<int64_t> my_set_copy(my_set);
-  EXPECT_TRUE(my_set_copy == my_set);
-  EXPECT_TRUE(my_set == my_set_copy);
-  EXPECT_FALSE(my_set_copy != my_set);
-  EXPECT_FALSE(my_set != my_set_copy);
-
-  my_set.insert(kSetSize);
-  EXPECT_FALSE(my_set_copy == my_set);
-  EXPECT_FALSE(my_set == my_set_copy);
-  EXPECT_TRUE(my_set_copy != my_set);
-  EXPECT_TRUE(my_set != my_set_copy);
-
-  my_set.erase(kSetSize - 1);
-  EXPECT_FALSE(my_set_copy == my_set);
-  EXPECT_FALSE(my_set == my_set_copy);
-  EXPECT_TRUE(my_set_copy != my_set);
-  EXPECT_TRUE(my_set != my_set_copy);
-
-  absl::btree_map<std::string, int64_t> my_map;
-  for (int i = 0; i < kSetSize; ++i) {
-    my_map[std::string(i, 'a')] = i;
-  }
-  absl::btree_map<std::string, int64_t> my_map_copy(my_map);
-  EXPECT_TRUE(my_map_copy == my_map);
-  EXPECT_TRUE(my_map == my_map_copy);
-  EXPECT_FALSE(my_map_copy != my_map);
-  EXPECT_FALSE(my_map != my_map_copy);
-
-  ++my_map_copy[std::string(7, 'a')];
-  EXPECT_FALSE(my_map_copy == my_map);
-  EXPECT_FALSE(my_map == my_map_copy);
-  EXPECT_TRUE(my_map_copy != my_map);
-  EXPECT_TRUE(my_map != my_map_copy);
-
-  my_map_copy = my_map;
-  my_map["hello"] = kSetSize;
-  EXPECT_FALSE(my_map_copy == my_map);
-  EXPECT_FALSE(my_map == my_map_copy);
-  EXPECT_TRUE(my_map_copy != my_map);
-  EXPECT_TRUE(my_map != my_map_copy);
-
-  my_map.erase(std::string(kSetSize - 1, 'a'));
-  EXPECT_FALSE(my_map_copy == my_map);
-  EXPECT_FALSE(my_map == my_map_copy);
-  EXPECT_TRUE(my_map_copy != my_map);
-  EXPECT_TRUE(my_map != my_map_copy);
-}
-
-TEST(Btree, RangeCtorSanity) {
-  std::vector<int> ivec;
-  ivec.push_back(1);
-  std::map<int, int> imap;
-  imap.insert(std::make_pair(1, 2));
-  absl::btree_multiset<int> tmset(ivec.begin(), ivec.end());
-  absl::btree_multimap<int, int> tmmap(imap.begin(), imap.end());
-  absl::btree_set<int> tset(ivec.begin(), ivec.end());
-  absl::btree_map<int, int> tmap(imap.begin(), imap.end());
-  EXPECT_EQ(1, tmset.size());
-  EXPECT_EQ(1, tmmap.size());
-  EXPECT_EQ(1, tset.size());
-  EXPECT_EQ(1, tmap.size());
-}
-
-}  // namespace
-
-class BtreeNodePeer {
- public:
-  // Yields the size of a leaf node with a specific number of values.
-  template <typename ValueType>
-  constexpr static size_t GetTargetNodeSize(size_t target_values_per_node) {
-    return btree_node<
-        set_params<ValueType, std::less<ValueType>, std::allocator<ValueType>,
-                   /*TargetNodeSize=*/256,  // This parameter isn't used here.
-                   /*Multi=*/false>>::SizeWithNValues(target_values_per_node);
-  }
-
-  // Yields the number of values in a (non-root) leaf node for this btree.
-  template <typename Btree>
-  constexpr static size_t GetNumValuesPerNode() {
-    return btree_node<typename Btree::params_type>::kNodeValues;
-  }
-
-  template <typename Btree>
-  constexpr static size_t GetMaxFieldType() {
-    return std::numeric_limits<
-        typename btree_node<typename Btree::params_type>::field_type>::max();
-  }
-
-  template <typename Btree>
-  constexpr static bool UsesLinearNodeSearch() {
-    return btree_node<typename Btree::params_type>::use_linear_search::value;
-  }
-};
-
-namespace {
-
-class BtreeMapTest : public ::testing::Test {
- public:
-  struct Key {};
-  struct Cmp {
-    template <typename T>
-    bool operator()(T, T) const {
-      return false;
-    }
-  };
-
-  struct KeyLin {
-    using absl_btree_prefer_linear_node_search = std::true_type;
-  };
-  struct CmpLin : Cmp {
-    using absl_btree_prefer_linear_node_search = std::true_type;
-  };
-
-  struct KeyBin {
-    using absl_btree_prefer_linear_node_search = std::false_type;
-  };
-  struct CmpBin : Cmp {
-    using absl_btree_prefer_linear_node_search = std::false_type;
-  };
-
-  template <typename K, typename C>
-  static bool IsLinear() {
-    return BtreeNodePeer::UsesLinearNodeSearch<absl::btree_map<K, int, C>>();
-  }
-};
-
-TEST_F(BtreeMapTest, TestLinearSearchPreferredForKeyLinearViaAlias) {
-  // Test requesting linear search by directly exporting an alias.
-  EXPECT_FALSE((IsLinear<Key, Cmp>()));
-  EXPECT_TRUE((IsLinear<KeyLin, Cmp>()));
-  EXPECT_TRUE((IsLinear<Key, CmpLin>()));
-  EXPECT_TRUE((IsLinear<KeyLin, CmpLin>()));
-}
-
-TEST_F(BtreeMapTest, LinearChoiceTree) {
-  // Cmp has precedence, and is forcing binary
-  EXPECT_FALSE((IsLinear<Key, CmpBin>()));
-  EXPECT_FALSE((IsLinear<KeyLin, CmpBin>()));
-  EXPECT_FALSE((IsLinear<KeyBin, CmpBin>()));
-  EXPECT_FALSE((IsLinear<int, CmpBin>()));
-  EXPECT_FALSE((IsLinear<std::string, CmpBin>()));
-  // Cmp has precedence, and is forcing linear
-  EXPECT_TRUE((IsLinear<Key, CmpLin>()));
-  EXPECT_TRUE((IsLinear<KeyLin, CmpLin>()));
-  EXPECT_TRUE((IsLinear<KeyBin, CmpLin>()));
-  EXPECT_TRUE((IsLinear<int, CmpLin>()));
-  EXPECT_TRUE((IsLinear<std::string, CmpLin>()));
-  // Cmp has no preference, Key determines linear vs binary.
-  EXPECT_FALSE((IsLinear<Key, Cmp>()));
-  EXPECT_TRUE((IsLinear<KeyLin, Cmp>()));
-  EXPECT_FALSE((IsLinear<KeyBin, Cmp>()));
-  // arithmetic key w/ std::less or std::greater: linear
-  EXPECT_TRUE((IsLinear<int, std::less<int>>()));
-  EXPECT_TRUE((IsLinear<double, std::greater<double>>()));
-  // arithmetic key w/ custom compare: binary
-  EXPECT_FALSE((IsLinear<int, Cmp>()));
-  // non-arithmetic key: binary
-  EXPECT_FALSE((IsLinear<std::string, std::less<std::string>>()));
-}
-
-TEST(Btree, BtreeMapCanHoldMoveOnlyTypes) {
-  absl::btree_map<std::string, std::unique_ptr<std::string>> m;
-
-  std::unique_ptr<std::string> &v = m["A"];
-  EXPECT_TRUE(v == nullptr);
-  v.reset(new std::string("X"));
-
-  auto iter = m.find("A");
-  EXPECT_EQ("X", *iter->second);
-}
-
-TEST(Btree, InitializerListConstructor) {
-  absl::btree_set<std::string> set({"a", "b"});
-  EXPECT_EQ(set.count("a"), 1);
-  EXPECT_EQ(set.count("b"), 1);
-
-  absl::btree_multiset<int> mset({1, 1, 4});
-  EXPECT_EQ(mset.count(1), 2);
-  EXPECT_EQ(mset.count(4), 1);
-
-  absl::btree_map<int, int> map({{1, 5}, {2, 10}});
-  EXPECT_EQ(map[1], 5);
-  EXPECT_EQ(map[2], 10);
-
-  absl::btree_multimap<int, int> mmap({{1, 5}, {1, 10}});
-  auto range = mmap.equal_range(1);
-  auto it = range.first;
-  ASSERT_NE(it, range.second);
-  EXPECT_EQ(it->second, 5);
-  ASSERT_NE(++it, range.second);
-  EXPECT_EQ(it->second, 10);
-  EXPECT_EQ(++it, range.second);
-}
-
-TEST(Btree, InitializerListInsert) {
-  absl::btree_set<std::string> set;
-  set.insert({"a", "b"});
-  EXPECT_EQ(set.count("a"), 1);
-  EXPECT_EQ(set.count("b"), 1);
-
-  absl::btree_multiset<int> mset;
-  mset.insert({1, 1, 4});
-  EXPECT_EQ(mset.count(1), 2);
-  EXPECT_EQ(mset.count(4), 1);
-
-  absl::btree_map<int, int> map;
-  map.insert({{1, 5}, {2, 10}});
-  // Test that inserting one element using an initializer list also works.
-  map.insert({3, 15});
-  EXPECT_EQ(map[1], 5);
-  EXPECT_EQ(map[2], 10);
-  EXPECT_EQ(map[3], 15);
-
-  absl::btree_multimap<int, int> mmap;
-  mmap.insert({{1, 5}, {1, 10}});
-  auto range = mmap.equal_range(1);
-  auto it = range.first;
-  ASSERT_NE(it, range.second);
-  EXPECT_EQ(it->second, 5);
-  ASSERT_NE(++it, range.second);
-  EXPECT_EQ(it->second, 10);
-  EXPECT_EQ(++it, range.second);
-}
-
-template <typename Compare, typename K>
-void AssertKeyCompareToAdapted() {
-  using Adapted = typename key_compare_to_adapter<Compare>::type;
-  static_assert(!std::is_same<Adapted, Compare>::value,
-                "key_compare_to_adapter should have adapted this comparator.");
-  static_assert(
-      std::is_same<absl::weak_ordering,
-                   absl::result_of_t<Adapted(const K &, const K &)>>::value,
-      "Adapted comparator should be a key-compare-to comparator.");
-}
-template <typename Compare, typename K>
-void AssertKeyCompareToNotAdapted() {
-  using Unadapted = typename key_compare_to_adapter<Compare>::type;
-  static_assert(
-      std::is_same<Unadapted, Compare>::value,
-      "key_compare_to_adapter shouldn't have adapted this comparator.");
-  static_assert(
-      std::is_same<bool,
-                   absl::result_of_t<Unadapted(const K &, const K &)>>::value,
-      "Un-adapted comparator should return bool.");
-}
-
-TEST(Btree, KeyCompareToAdapter) {
-  AssertKeyCompareToAdapted<std::less<std::string>, std::string>();
-  AssertKeyCompareToAdapted<std::greater<std::string>, std::string>();
-  AssertKeyCompareToAdapted<std::less<absl::string_view>, absl::string_view>();
-  AssertKeyCompareToAdapted<std::greater<absl::string_view>,
-                            absl::string_view>();
-  AssertKeyCompareToAdapted<std::less<absl::Cord>, absl::Cord>();
-  AssertKeyCompareToAdapted<std::greater<absl::Cord>, absl::Cord>();
-  AssertKeyCompareToNotAdapted<std::less<int>, int>();
-  AssertKeyCompareToNotAdapted<std::greater<int>, int>();
-}
-
-TEST(Btree, RValueInsert) {
-  InstanceTracker tracker;
-
-  absl::btree_set<MovableOnlyInstance> set;
-  set.insert(MovableOnlyInstance(1));
-  set.insert(MovableOnlyInstance(3));
-  MovableOnlyInstance two(2);
-  set.insert(set.find(MovableOnlyInstance(3)), std::move(two));
-  auto it = set.find(MovableOnlyInstance(2));
-  ASSERT_NE(it, set.end());
-  ASSERT_NE(++it, set.end());
-  EXPECT_EQ(it->value(), 3);
-
-  absl::btree_multiset<MovableOnlyInstance> mset;
-  MovableOnlyInstance zero(0);
-  MovableOnlyInstance zero2(0);
-  mset.insert(std::move(zero));
-  mset.insert(mset.find(MovableOnlyInstance(0)), std::move(zero2));
-  EXPECT_EQ(mset.count(MovableOnlyInstance(0)), 2);
-
-  absl::btree_map<int, MovableOnlyInstance> map;
-  std::pair<const int, MovableOnlyInstance> p1 = {1, MovableOnlyInstance(5)};
-  std::pair<const int, MovableOnlyInstance> p2 = {2, MovableOnlyInstance(10)};
-  std::pair<const int, MovableOnlyInstance> p3 = {3, MovableOnlyInstance(15)};
-  map.insert(std::move(p1));
-  map.insert(std::move(p3));
-  map.insert(map.find(3), std::move(p2));
-  ASSERT_NE(map.find(2), map.end());
-  EXPECT_EQ(map.find(2)->second.value(), 10);
-
-  absl::btree_multimap<int, MovableOnlyInstance> mmap;
-  std::pair<const int, MovableOnlyInstance> p4 = {1, MovableOnlyInstance(5)};
-  std::pair<const int, MovableOnlyInstance> p5 = {1, MovableOnlyInstance(10)};
-  mmap.insert(std::move(p4));
-  mmap.insert(mmap.find(1), std::move(p5));
-  auto range = mmap.equal_range(1);
-  auto it1 = range.first;
-  ASSERT_NE(it1, range.second);
-  EXPECT_EQ(it1->second.value(), 10);
-  ASSERT_NE(++it1, range.second);
-  EXPECT_EQ(it1->second.value(), 5);
-  EXPECT_EQ(++it1, range.second);
-
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.swaps(), 0);
-}
-
-// A btree set with a specific number of values per node.
-template <typename Key, int TargetValuesPerNode, typename Cmp = std::less<Key>>
-class SizedBtreeSet
-    : public btree_set_container<btree<
-          set_params<Key, Cmp, std::allocator<Key>,
-                     BtreeNodePeer::GetTargetNodeSize<Key>(TargetValuesPerNode),
-                     /*Multi=*/false>>> {
-  using Base = typename SizedBtreeSet::btree_set_container;
-
- public:
-  SizedBtreeSet() {}
-  using Base::Base;
-};
-
-template <typename Set>
-void ExpectOperationCounts(const int expected_moves,
-                           const int expected_comparisons,
-                           const std::vector<int> &values,
-                           InstanceTracker *tracker, Set *set) {
-  for (const int v : values) set->insert(MovableOnlyInstance(v));
-  set->clear();
-  EXPECT_EQ(tracker->moves(), expected_moves);
-  EXPECT_EQ(tracker->comparisons(), expected_comparisons);
-  EXPECT_EQ(tracker->copies(), 0);
-  EXPECT_EQ(tracker->swaps(), 0);
-  tracker->ResetCopiesMovesSwaps();
-}
-
-// Note: when the values in this test change, it is expected to have an impact
-// on performance.
-TEST(Btree, MovesComparisonsCopiesSwapsTracking) {
-  InstanceTracker tracker;
-  // Note: this is minimum number of values per node.
-  SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3> set3;
-  // Note: this is the default number of values per node for a set of int32s
-  // (with 64-bit pointers).
-  SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61> set61;
-  SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/100> set100;
-
-  // Don't depend on flags for random values because then the expectations will
-  // fail if the flags change.
-  std::vector<int> values =
-      GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23);
-
-  EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3);
-  EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61);
-  EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100);
-  if (sizeof(void *) == 8) {
-    EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(),
-              BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>());
-  }
-
-  // Test key insertion/deletion in random order.
-  ExpectOperationCounts(45281, 132551, values, &tracker, &set3);
-  ExpectOperationCounts(386718, 129807, values, &tracker, &set61);
-  ExpectOperationCounts(586761, 130310, values, &tracker, &set100);
-
-  // Test key insertion/deletion in sorted order.
-  std::sort(values.begin(), values.end());
-  ExpectOperationCounts(26638, 92134, values, &tracker, &set3);
-  ExpectOperationCounts(20208, 87757, values, &tracker, &set61);
-  ExpectOperationCounts(20124, 96583, values, &tracker, &set100);
-
-  // Test key insertion/deletion in reverse sorted order.
-  std::reverse(values.begin(), values.end());
-  ExpectOperationCounts(49951, 119325, values, &tracker, &set3);
-  ExpectOperationCounts(338813, 118266, values, &tracker, &set61);
-  ExpectOperationCounts(534529, 125279, values, &tracker, &set100);
-}
-
-struct MovableOnlyInstanceThreeWayCompare {
-  absl::weak_ordering operator()(const MovableOnlyInstance &a,
-                                 const MovableOnlyInstance &b) const {
-    return a.compare(b);
-  }
-};
-
-// Note: when the values in this test change, it is expected to have an impact
-// on performance.
-TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) {
-  InstanceTracker tracker;
-  // Note: this is minimum number of values per node.
-  SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3,
-                MovableOnlyInstanceThreeWayCompare>
-      set3;
-  // Note: this is the default number of values per node for a set of int32s
-  // (with 64-bit pointers).
-  SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61,
-                MovableOnlyInstanceThreeWayCompare>
-      set61;
-  SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/100,
-                MovableOnlyInstanceThreeWayCompare>
-      set100;
-
-  // Don't depend on flags for random values because then the expectations will
-  // fail if the flags change.
-  std::vector<int> values =
-      GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23);
-
-  EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3);
-  EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61);
-  EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100);
-  if (sizeof(void *) == 8) {
-    EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(),
-              BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>());
-  }
-
-  // Test key insertion/deletion in random order.
-  ExpectOperationCounts(45281, 122560, values, &tracker, &set3);
-  ExpectOperationCounts(386718, 119816, values, &tracker, &set61);
-  ExpectOperationCounts(586761, 120319, values, &tracker, &set100);
-
-  // Test key insertion/deletion in sorted order.
-  std::sort(values.begin(), values.end());
-  ExpectOperationCounts(26638, 92134, values, &tracker, &set3);
-  ExpectOperationCounts(20208, 87757, values, &tracker, &set61);
-  ExpectOperationCounts(20124, 96583, values, &tracker, &set100);
-
-  // Test key insertion/deletion in reverse sorted order.
-  std::reverse(values.begin(), values.end());
-  ExpectOperationCounts(49951, 109326, values, &tracker, &set3);
-  ExpectOperationCounts(338813, 108267, values, &tracker, &set61);
-  ExpectOperationCounts(534529, 115280, values, &tracker, &set100);
-}
-
-struct NoDefaultCtor {
-  int num;
-  explicit NoDefaultCtor(int i) : num(i) {}
-
-  friend bool operator<(const NoDefaultCtor &a, const NoDefaultCtor &b) {
-    return a.num < b.num;
-  }
-};
-
-TEST(Btree, BtreeMapCanHoldNoDefaultCtorTypes) {
-  absl::btree_map<NoDefaultCtor, NoDefaultCtor> m;
-
-  for (int i = 1; i <= 99; ++i) {
-    SCOPED_TRACE(i);
-    EXPECT_TRUE(m.emplace(NoDefaultCtor(i), NoDefaultCtor(100 - i)).second);
-  }
-  EXPECT_FALSE(m.emplace(NoDefaultCtor(78), NoDefaultCtor(0)).second);
-
-  auto iter99 = m.find(NoDefaultCtor(99));
-  ASSERT_NE(iter99, m.end());
-  EXPECT_EQ(iter99->second.num, 1);
-
-  auto iter1 = m.find(NoDefaultCtor(1));
-  ASSERT_NE(iter1, m.end());
-  EXPECT_EQ(iter1->second.num, 99);
-
-  auto iter50 = m.find(NoDefaultCtor(50));
-  ASSERT_NE(iter50, m.end());
-  EXPECT_EQ(iter50->second.num, 50);
-
-  auto iter25 = m.find(NoDefaultCtor(25));
-  ASSERT_NE(iter25, m.end());
-  EXPECT_EQ(iter25->second.num, 75);
-}
-
-TEST(Btree, BtreeMultimapCanHoldNoDefaultCtorTypes) {
-  absl::btree_multimap<NoDefaultCtor, NoDefaultCtor> m;
-
-  for (int i = 1; i <= 99; ++i) {
-    SCOPED_TRACE(i);
-    m.emplace(NoDefaultCtor(i), NoDefaultCtor(100 - i));
-  }
-
-  auto iter99 = m.find(NoDefaultCtor(99));
-  ASSERT_NE(iter99, m.end());
-  EXPECT_EQ(iter99->second.num, 1);
-
-  auto iter1 = m.find(NoDefaultCtor(1));
-  ASSERT_NE(iter1, m.end());
-  EXPECT_EQ(iter1->second.num, 99);
-
-  auto iter50 = m.find(NoDefaultCtor(50));
-  ASSERT_NE(iter50, m.end());
-  EXPECT_EQ(iter50->second.num, 50);
-
-  auto iter25 = m.find(NoDefaultCtor(25));
-  ASSERT_NE(iter25, m.end());
-  EXPECT_EQ(iter25->second.num, 75);
-}
-
-TEST(Btree, MapAt) {
-  absl::btree_map<int, int> map = {{1, 2}, {2, 4}};
-  EXPECT_EQ(map.at(1), 2);
-  EXPECT_EQ(map.at(2), 4);
-  map.at(2) = 8;
-  const absl::btree_map<int, int> &const_map = map;
-  EXPECT_EQ(const_map.at(1), 2);
-  EXPECT_EQ(const_map.at(2), 8);
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW(map.at(3), std::out_of_range);
-#else
-  EXPECT_DEATH_IF_SUPPORTED(map.at(3), "absl::btree_map::at");
-#endif
-}
-
-TEST(Btree, BtreeMultisetEmplace) {
-  const int value_to_insert = 123456;
-  absl::btree_multiset<int> s;
-  auto iter = s.emplace(value_to_insert);
-  ASSERT_NE(iter, s.end());
-  EXPECT_EQ(*iter, value_to_insert);
-  auto iter2 = s.emplace(value_to_insert);
-  EXPECT_NE(iter2, iter);
-  ASSERT_NE(iter2, s.end());
-  EXPECT_EQ(*iter2, value_to_insert);
-  auto result = s.equal_range(value_to_insert);
-  EXPECT_EQ(std::distance(result.first, result.second), 2);
-}
-
-TEST(Btree, BtreeMultisetEmplaceHint) {
-  const int value_to_insert = 123456;
-  absl::btree_multiset<int> s;
-  auto iter = s.emplace(value_to_insert);
-  ASSERT_NE(iter, s.end());
-  EXPECT_EQ(*iter, value_to_insert);
-  auto emplace_iter = s.emplace_hint(iter, value_to_insert);
-  EXPECT_NE(emplace_iter, iter);
-  ASSERT_NE(emplace_iter, s.end());
-  EXPECT_EQ(*emplace_iter, value_to_insert);
-}
-
-TEST(Btree, BtreeMultimapEmplace) {
-  const int key_to_insert = 123456;
-  const char value0[] = "a";
-  absl::btree_multimap<int, std::string> s;
-  auto iter = s.emplace(key_to_insert, value0);
-  ASSERT_NE(iter, s.end());
-  EXPECT_EQ(iter->first, key_to_insert);
-  EXPECT_EQ(iter->second, value0);
-  const char value1[] = "b";
-  auto iter2 = s.emplace(key_to_insert, value1);
-  EXPECT_NE(iter2, iter);
-  ASSERT_NE(iter2, s.end());
-  EXPECT_EQ(iter2->first, key_to_insert);
-  EXPECT_EQ(iter2->second, value1);
-  auto result = s.equal_range(key_to_insert);
-  EXPECT_EQ(std::distance(result.first, result.second), 2);
-}
-
-TEST(Btree, BtreeMultimapEmplaceHint) {
-  const int key_to_insert = 123456;
-  const char value0[] = "a";
-  absl::btree_multimap<int, std::string> s;
-  auto iter = s.emplace(key_to_insert, value0);
-  ASSERT_NE(iter, s.end());
-  EXPECT_EQ(iter->first, key_to_insert);
-  EXPECT_EQ(iter->second, value0);
-  const char value1[] = "b";
-  auto emplace_iter = s.emplace_hint(iter, key_to_insert, value1);
-  EXPECT_NE(emplace_iter, iter);
-  ASSERT_NE(emplace_iter, s.end());
-  EXPECT_EQ(emplace_iter->first, key_to_insert);
-  EXPECT_EQ(emplace_iter->second, value1);
-}
-
-TEST(Btree, ConstIteratorAccessors) {
-  absl::btree_set<int> set;
-  for (int i = 0; i < 100; ++i) {
-    set.insert(i);
-  }
-
-  auto it = set.cbegin();
-  auto r_it = set.crbegin();
-  for (int i = 0; i < 100; ++i, ++it, ++r_it) {
-    ASSERT_EQ(*it, i);
-    ASSERT_EQ(*r_it, 99 - i);
-  }
-  EXPECT_EQ(it, set.cend());
-  EXPECT_EQ(r_it, set.crend());
-}
-
-TEST(Btree, StrSplitCompatible) {
-  const absl::btree_set<std::string> split_set = absl::StrSplit("a,b,c", ',');
-  const absl::btree_set<std::string> expected_set = {"a", "b", "c"};
-
-  EXPECT_EQ(split_set, expected_set);
-}
-
-// We can't use EXPECT_EQ/etc. to compare absl::weak_ordering because they
-// convert literal 0 to int and absl::weak_ordering can only be compared with
-// literal 0. Defining this function allows for avoiding ClangTidy warnings.
-bool Identity(const bool b) { return b; }
-
-TEST(Btree, ValueComp) {
-  absl::btree_set<int> s;
-  EXPECT_TRUE(s.value_comp()(1, 2));
-  EXPECT_FALSE(s.value_comp()(2, 2));
-  EXPECT_FALSE(s.value_comp()(2, 1));
-
-  absl::btree_map<int, int> m1;
-  EXPECT_TRUE(m1.value_comp()(std::make_pair(1, 0), std::make_pair(2, 0)));
-  EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(2, 0)));
-  EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(1, 0)));
-
-  absl::btree_map<std::string, int> m2;
-  EXPECT_TRUE(Identity(
-      m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0)) < 0));
-  EXPECT_TRUE(Identity(
-      m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0)) == 0));
-  EXPECT_TRUE(Identity(
-      m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0)) > 0));
-}
-
-TEST(Btree, DefaultConstruction) {
-  absl::btree_set<int> s;
-  absl::btree_map<int, int> m;
-  absl::btree_multiset<int> ms;
-  absl::btree_multimap<int, int> mm;
-
-  EXPECT_TRUE(s.empty());
-  EXPECT_TRUE(m.empty());
-  EXPECT_TRUE(ms.empty());
-  EXPECT_TRUE(mm.empty());
-}
-
-TEST(Btree, SwissTableHashable) {
-  static constexpr int kValues = 10000;
-  std::vector<int> values(kValues);
-  std::iota(values.begin(), values.end(), 0);
-  std::vector<std::pair<int, int>> map_values;
-  for (int v : values) map_values.emplace_back(v, -v);
-
-  using set = absl::btree_set<int>;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
-      set{},
-      set{1},
-      set{2},
-      set{1, 2},
-      set{2, 1},
-      set(values.begin(), values.end()),
-      set(values.rbegin(), values.rend()),
-  }));
-
-  using mset = absl::btree_multiset<int>;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
-      mset{},
-      mset{1},
-      mset{1, 1},
-      mset{2},
-      mset{2, 2},
-      mset{1, 2},
-      mset{1, 1, 2},
-      mset{1, 2, 2},
-      mset{1, 1, 2, 2},
-      mset(values.begin(), values.end()),
-      mset(values.rbegin(), values.rend()),
-  }));
-
-  using map = absl::btree_map<int, int>;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
-      map{},
-      map{{1, 0}},
-      map{{1, 1}},
-      map{{2, 0}},
-      map{{2, 2}},
-      map{{1, 0}, {2, 1}},
-      map(map_values.begin(), map_values.end()),
-      map(map_values.rbegin(), map_values.rend()),
-  }));
-
-  using mmap = absl::btree_multimap<int, int>;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
-      mmap{},
-      mmap{{1, 0}},
-      mmap{{1, 1}},
-      mmap{{1, 0}, {1, 1}},
-      mmap{{1, 1}, {1, 0}},
-      mmap{{2, 0}},
-      mmap{{2, 2}},
-      mmap{{1, 0}, {2, 1}},
-      mmap(map_values.begin(), map_values.end()),
-      mmap(map_values.rbegin(), map_values.rend()),
-  }));
-}
-
-TEST(Btree, ComparableSet) {
-  absl::btree_set<int> s1 = {1, 2};
-  absl::btree_set<int> s2 = {2, 3};
-  EXPECT_LT(s1, s2);
-  EXPECT_LE(s1, s2);
-  EXPECT_LE(s1, s1);
-  EXPECT_GT(s2, s1);
-  EXPECT_GE(s2, s1);
-  EXPECT_GE(s1, s1);
-}
-
-TEST(Btree, ComparableSetsDifferentLength) {
-  absl::btree_set<int> s1 = {1, 2};
-  absl::btree_set<int> s2 = {1, 2, 3};
-  EXPECT_LT(s1, s2);
-  EXPECT_LE(s1, s2);
-  EXPECT_GT(s2, s1);
-  EXPECT_GE(s2, s1);
-}
-
-TEST(Btree, ComparableMultiset) {
-  absl::btree_multiset<int> s1 = {1, 2};
-  absl::btree_multiset<int> s2 = {2, 3};
-  EXPECT_LT(s1, s2);
-  EXPECT_LE(s1, s2);
-  EXPECT_LE(s1, s1);
-  EXPECT_GT(s2, s1);
-  EXPECT_GE(s2, s1);
-  EXPECT_GE(s1, s1);
-}
-
-TEST(Btree, ComparableMap) {
-  absl::btree_map<int, int> s1 = {{1, 2}};
-  absl::btree_map<int, int> s2 = {{2, 3}};
-  EXPECT_LT(s1, s2);
-  EXPECT_LE(s1, s2);
-  EXPECT_LE(s1, s1);
-  EXPECT_GT(s2, s1);
-  EXPECT_GE(s2, s1);
-  EXPECT_GE(s1, s1);
-}
-
-TEST(Btree, ComparableMultimap) {
-  absl::btree_multimap<int, int> s1 = {{1, 2}};
-  absl::btree_multimap<int, int> s2 = {{2, 3}};
-  EXPECT_LT(s1, s2);
-  EXPECT_LE(s1, s2);
-  EXPECT_LE(s1, s1);
-  EXPECT_GT(s2, s1);
-  EXPECT_GE(s2, s1);
-  EXPECT_GE(s1, s1);
-}
-
-TEST(Btree, ComparableSetWithCustomComparator) {
-  // As specified by
-  // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf section
-  // [container.requirements.general].12, ordering associative containers always
-  // uses default '<' operator
-  // - even if otherwise the container uses custom functor.
-  absl::btree_set<int, std::greater<int>> s1 = {1, 2};
-  absl::btree_set<int, std::greater<int>> s2 = {2, 3};
-  EXPECT_LT(s1, s2);
-  EXPECT_LE(s1, s2);
-  EXPECT_LE(s1, s1);
-  EXPECT_GT(s2, s1);
-  EXPECT_GE(s2, s1);
-  EXPECT_GE(s1, s1);
-}
-
-TEST(Btree, EraseReturnsIterator) {
-  absl::btree_set<int> set = {1, 2, 3, 4, 5};
-  auto result_it = set.erase(set.begin(), set.find(3));
-  EXPECT_EQ(result_it, set.find(3));
-  result_it = set.erase(set.find(5));
-  EXPECT_EQ(result_it, set.end());
-}
-
-TEST(Btree, ExtractAndInsertNodeHandleSet) {
-  absl::btree_set<int> src1 = {1, 2, 3, 4, 5};
-  auto nh = src1.extract(src1.find(3));
-  EXPECT_THAT(src1, ElementsAre(1, 2, 4, 5));
-  absl::btree_set<int> other;
-  absl::btree_set<int>::insert_return_type res = other.insert(std::move(nh));
-  EXPECT_THAT(other, ElementsAre(3));
-  EXPECT_EQ(res.position, other.find(3));
-  EXPECT_TRUE(res.inserted);
-  EXPECT_TRUE(res.node.empty());
-
-  absl::btree_set<int> src2 = {3, 4};
-  nh = src2.extract(src2.find(3));
-  EXPECT_THAT(src2, ElementsAre(4));
-  res = other.insert(std::move(nh));
-  EXPECT_THAT(other, ElementsAre(3));
-  EXPECT_EQ(res.position, other.find(3));
-  EXPECT_FALSE(res.inserted);
-  ASSERT_FALSE(res.node.empty());
-  EXPECT_EQ(res.node.value(), 3);
-}
-
-template <typename Set>
-void TestExtractWithTrackingForSet() {
-  InstanceTracker tracker;
-  {
-    Set s;
-    // Add enough elements to make sure we test internal nodes too.
-    const size_t kSize = 1000;
-    while (s.size() < kSize) {
-      s.insert(MovableOnlyInstance(s.size()));
-    }
-    for (int i = 0; i < kSize; ++i) {
-      // Extract with key
-      auto nh = s.extract(MovableOnlyInstance(i));
-      EXPECT_EQ(s.size(), kSize - 1);
-      EXPECT_EQ(nh.value().value(), i);
-      // Insert with node
-      s.insert(std::move(nh));
-      EXPECT_EQ(s.size(), kSize);
-
-      // Extract with iterator
-      auto it = s.find(MovableOnlyInstance(i));
-      nh = s.extract(it);
-      EXPECT_EQ(s.size(), kSize - 1);
-      EXPECT_EQ(nh.value().value(), i);
-      // Insert with node and hint
-      s.insert(s.begin(), std::move(nh));
-      EXPECT_EQ(s.size(), kSize);
-    }
-  }
-  EXPECT_EQ(0, tracker.instances());
-}
-
-template <typename Map>
-void TestExtractWithTrackingForMap() {
-  InstanceTracker tracker;
-  {
-    Map m;
-    // Add enough elements to make sure we test internal nodes too.
-    const size_t kSize = 1000;
-    while (m.size() < kSize) {
-      m.insert(
-          {CopyableMovableInstance(m.size()), MovableOnlyInstance(m.size())});
-    }
-    for (int i = 0; i < kSize; ++i) {
-      // Extract with key
-      auto nh = m.extract(CopyableMovableInstance(i));
-      EXPECT_EQ(m.size(), kSize - 1);
-      EXPECT_EQ(nh.key().value(), i);
-      EXPECT_EQ(nh.mapped().value(), i);
-      // Insert with node
-      m.insert(std::move(nh));
-      EXPECT_EQ(m.size(), kSize);
-
-      // Extract with iterator
-      auto it = m.find(CopyableMovableInstance(i));
-      nh = m.extract(it);
-      EXPECT_EQ(m.size(), kSize - 1);
-      EXPECT_EQ(nh.key().value(), i);
-      EXPECT_EQ(nh.mapped().value(), i);
-      // Insert with node and hint
-      m.insert(m.begin(), std::move(nh));
-      EXPECT_EQ(m.size(), kSize);
-    }
-  }
-  EXPECT_EQ(0, tracker.instances());
-}
-
-TEST(Btree, ExtractTracking) {
-  TestExtractWithTrackingForSet<absl::btree_set<MovableOnlyInstance>>();
-  TestExtractWithTrackingForSet<absl::btree_multiset<MovableOnlyInstance>>();
-  TestExtractWithTrackingForMap<
-      absl::btree_map<CopyableMovableInstance, MovableOnlyInstance>>();
-  TestExtractWithTrackingForMap<
-      absl::btree_multimap<CopyableMovableInstance, MovableOnlyInstance>>();
-}
-
-TEST(Btree, ExtractAndInsertNodeHandleMultiSet) {
-  absl::btree_multiset<int> src1 = {1, 2, 3, 3, 4, 5};
-  auto nh = src1.extract(src1.find(3));
-  EXPECT_THAT(src1, ElementsAre(1, 2, 3, 4, 5));
-  absl::btree_multiset<int> other;
-  auto res = other.insert(std::move(nh));
-  EXPECT_THAT(other, ElementsAre(3));
-  EXPECT_EQ(res, other.find(3));
-
-  absl::btree_multiset<int> src2 = {3, 4};
-  nh = src2.extract(src2.find(3));
-  EXPECT_THAT(src2, ElementsAre(4));
-  res = other.insert(std::move(nh));
-  EXPECT_THAT(other, ElementsAre(3, 3));
-  EXPECT_EQ(res, ++other.find(3));
-}
-
-TEST(Btree, ExtractAndInsertNodeHandleMap) {
-  absl::btree_map<int, int> src1 = {{1, 2}, {3, 4}, {5, 6}};
-  auto nh = src1.extract(src1.find(3));
-  EXPECT_THAT(src1, ElementsAre(Pair(1, 2), Pair(5, 6)));
-  absl::btree_map<int, int> other;
-  absl::btree_map<int, int>::insert_return_type res =
-      other.insert(std::move(nh));
-  EXPECT_THAT(other, ElementsAre(Pair(3, 4)));
-  EXPECT_EQ(res.position, other.find(3));
-  EXPECT_TRUE(res.inserted);
-  EXPECT_TRUE(res.node.empty());
-
-  absl::btree_map<int, int> src2 = {{3, 6}};
-  nh = src2.extract(src2.find(3));
-  EXPECT_TRUE(src2.empty());
-  res = other.insert(std::move(nh));
-  EXPECT_THAT(other, ElementsAre(Pair(3, 4)));
-  EXPECT_EQ(res.position, other.find(3));
-  EXPECT_FALSE(res.inserted);
-  ASSERT_FALSE(res.node.empty());
-  EXPECT_EQ(res.node.key(), 3);
-  EXPECT_EQ(res.node.mapped(), 6);
-}
-
-TEST(Btree, ExtractAndInsertNodeHandleMultiMap) {
-  absl::btree_multimap<int, int> src1 = {{1, 2}, {3, 4}, {5, 6}};
-  auto nh = src1.extract(src1.find(3));
-  EXPECT_THAT(src1, ElementsAre(Pair(1, 2), Pair(5, 6)));
-  absl::btree_multimap<int, int> other;
-  auto res = other.insert(std::move(nh));
-  EXPECT_THAT(other, ElementsAre(Pair(3, 4)));
-  EXPECT_EQ(res, other.find(3));
-
-  absl::btree_multimap<int, int> src2 = {{3, 6}};
-  nh = src2.extract(src2.find(3));
-  EXPECT_TRUE(src2.empty());
-  res = other.insert(std::move(nh));
-  EXPECT_THAT(other, ElementsAre(Pair(3, 4), Pair(3, 6)));
-  EXPECT_EQ(res, ++other.begin());
-}
-
-// For multisets, insert with hint also affects correctness because we need to
-// insert immediately before the hint if possible.
-struct InsertMultiHintData {
-  int key;
-  int not_key;
-  bool operator==(const InsertMultiHintData other) const {
-    return key == other.key && not_key == other.not_key;
-  }
-};
-
-struct InsertMultiHintDataKeyCompare {
-  using is_transparent = void;
-  bool operator()(const InsertMultiHintData a,
-                  const InsertMultiHintData b) const {
-    return a.key < b.key;
-  }
-  bool operator()(const int a, const InsertMultiHintData b) const {
-    return a < b.key;
-  }
-  bool operator()(const InsertMultiHintData a, const int b) const {
-    return a.key < b;
-  }
-};
-
-TEST(Btree, InsertHintNodeHandle) {
-  // For unique sets, insert with hint is just a performance optimization.
-  // Test that insert works correctly when the hint is right or wrong.
-  {
-    absl::btree_set<int> src = {1, 2, 3, 4, 5};
-    auto nh = src.extract(src.find(3));
-    EXPECT_THAT(src, ElementsAre(1, 2, 4, 5));
-    absl::btree_set<int> other = {0, 100};
-    // Test a correct hint.
-    auto it = other.insert(other.lower_bound(3), std::move(nh));
-    EXPECT_THAT(other, ElementsAre(0, 3, 100));
-    EXPECT_EQ(it, other.find(3));
-
-    nh = src.extract(src.find(5));
-    // Test an incorrect hint.
-    it = other.insert(other.end(), std::move(nh));
-    EXPECT_THAT(other, ElementsAre(0, 3, 5, 100));
-    EXPECT_EQ(it, other.find(5));
-  }
-
-  absl::btree_multiset<InsertMultiHintData, InsertMultiHintDataKeyCompare> src =
-      {{1, 2}, {3, 4}, {3, 5}};
-  auto nh = src.extract(src.lower_bound(3));
-  EXPECT_EQ(nh.value(), (InsertMultiHintData{3, 4}));
-  absl::btree_multiset<InsertMultiHintData, InsertMultiHintDataKeyCompare>
-      other = {{3, 1}, {3, 2}, {3, 3}};
-  auto it = other.insert(--other.end(), std::move(nh));
-  EXPECT_THAT(
-      other, ElementsAre(InsertMultiHintData{3, 1}, InsertMultiHintData{3, 2},
-                         InsertMultiHintData{3, 4}, InsertMultiHintData{3, 3}));
-  EXPECT_EQ(it, --(--other.end()));
-
-  nh = src.extract(src.find(3));
-  EXPECT_EQ(nh.value(), (InsertMultiHintData{3, 5}));
-  it = other.insert(other.begin(), std::move(nh));
-  EXPECT_THAT(other,
-              ElementsAre(InsertMultiHintData{3, 5}, InsertMultiHintData{3, 1},
-                          InsertMultiHintData{3, 2}, InsertMultiHintData{3, 4},
-                          InsertMultiHintData{3, 3}));
-  EXPECT_EQ(it, other.begin());
-}
-
-struct IntCompareToCmp {
-  absl::weak_ordering operator()(int a, int b) const {
-    if (a < b) return absl::weak_ordering::less;
-    if (a > b) return absl::weak_ordering::greater;
-    return absl::weak_ordering::equivalent;
-  }
-};
-
-TEST(Btree, MergeIntoUniqueContainers) {
-  absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3};
-  absl::btree_multiset<int> src2 = {3, 4, 4, 5};
-  absl::btree_set<int> dst;
-
-  dst.merge(src1);
-  EXPECT_TRUE(src1.empty());
-  EXPECT_THAT(dst, ElementsAre(1, 2, 3));
-  dst.merge(src2);
-  EXPECT_THAT(src2, ElementsAre(3, 4));
-  EXPECT_THAT(dst, ElementsAre(1, 2, 3, 4, 5));
-}
-
-TEST(Btree, MergeIntoUniqueContainersWithCompareTo) {
-  absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3};
-  absl::btree_multiset<int> src2 = {3, 4, 4, 5};
-  absl::btree_set<int, IntCompareToCmp> dst;
-
-  dst.merge(src1);
-  EXPECT_TRUE(src1.empty());
-  EXPECT_THAT(dst, ElementsAre(1, 2, 3));
-  dst.merge(src2);
-  EXPECT_THAT(src2, ElementsAre(3, 4));
-  EXPECT_THAT(dst, ElementsAre(1, 2, 3, 4, 5));
-}
-
-TEST(Btree, MergeIntoMultiContainers) {
-  absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3};
-  absl::btree_multiset<int> src2 = {3, 4, 4, 5};
-  absl::btree_multiset<int> dst;
-
-  dst.merge(src1);
-  EXPECT_TRUE(src1.empty());
-  EXPECT_THAT(dst, ElementsAre(1, 2, 3));
-  dst.merge(src2);
-  EXPECT_TRUE(src2.empty());
-  EXPECT_THAT(dst, ElementsAre(1, 2, 3, 3, 4, 4, 5));
-}
-
-TEST(Btree, MergeIntoMultiContainersWithCompareTo) {
-  absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3};
-  absl::btree_multiset<int> src2 = {3, 4, 4, 5};
-  absl::btree_multiset<int, IntCompareToCmp> dst;
-
-  dst.merge(src1);
-  EXPECT_TRUE(src1.empty());
-  EXPECT_THAT(dst, ElementsAre(1, 2, 3));
-  dst.merge(src2);
-  EXPECT_TRUE(src2.empty());
-  EXPECT_THAT(dst, ElementsAre(1, 2, 3, 3, 4, 4, 5));
-}
-
-TEST(Btree, MergeIntoMultiMapsWithDifferentComparators) {
-  absl::btree_map<int, int, IntCompareToCmp> src1 = {{1, 1}, {2, 2}, {3, 3}};
-  absl::btree_multimap<int, int, std::greater<int>> src2 = {
-      {5, 5}, {4, 1}, {4, 4}, {3, 2}};
-  absl::btree_multimap<int, int> dst;
-
-  dst.merge(src1);
-  EXPECT_TRUE(src1.empty());
-  EXPECT_THAT(dst, ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3)));
-  dst.merge(src2);
-  EXPECT_TRUE(src2.empty());
-  EXPECT_THAT(dst, ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), Pair(3, 2),
-                               Pair(4, 1), Pair(4, 4), Pair(5, 5)));
-}
-
-TEST(Btree, MergeIntoSetMovableOnly) {
-  absl::btree_set<MovableOnlyInstance> src;
-  src.insert(MovableOnlyInstance(1));
-  absl::btree_multiset<MovableOnlyInstance> dst1;
-  dst1.insert(MovableOnlyInstance(2));
-  absl::btree_set<MovableOnlyInstance> dst2;
-
-  // Test merge into multiset.
-  dst1.merge(src);
-
-  EXPECT_TRUE(src.empty());
-  // ElementsAre/ElementsAreArray don't work with move-only types.
-  ASSERT_THAT(dst1, SizeIs(2));
-  EXPECT_EQ(*dst1.begin(), MovableOnlyInstance(1));
-  EXPECT_EQ(*std::next(dst1.begin()), MovableOnlyInstance(2));
-
-  // Test merge into set.
-  dst2.merge(dst1);
-
-  EXPECT_TRUE(dst1.empty());
-  ASSERT_THAT(dst2, SizeIs(2));
-  EXPECT_EQ(*dst2.begin(), MovableOnlyInstance(1));
-  EXPECT_EQ(*std::next(dst2.begin()), MovableOnlyInstance(2));
-}
-
-struct KeyCompareToWeakOrdering {
-  template <typename T>
-  absl::weak_ordering operator()(const T &a, const T &b) const {
-    return a < b ? absl::weak_ordering::less
-                 : a == b ? absl::weak_ordering::equivalent
-                          : absl::weak_ordering::greater;
-  }
-};
-
-struct KeyCompareToStrongOrdering {
-  template <typename T>
-  absl::strong_ordering operator()(const T &a, const T &b) const {
-    return a < b ? absl::strong_ordering::less
-                 : a == b ? absl::strong_ordering::equal
-                          : absl::strong_ordering::greater;
-  }
-};
-
-TEST(Btree, UserProvidedKeyCompareToComparators) {
-  absl::btree_set<int, KeyCompareToWeakOrdering> weak_set = {1, 2, 3};
-  EXPECT_TRUE(weak_set.contains(2));
-  EXPECT_FALSE(weak_set.contains(4));
-
-  absl::btree_set<int, KeyCompareToStrongOrdering> strong_set = {1, 2, 3};
-  EXPECT_TRUE(strong_set.contains(2));
-  EXPECT_FALSE(strong_set.contains(4));
-}
-
-TEST(Btree, TryEmplaceBasicTest) {
-  absl::btree_map<int, std::string> m;
-
-  // Should construct a string from the literal.
-  m.try_emplace(1, "one");
-  EXPECT_EQ(1, m.size());
-
-  // Try other string constructors and const lvalue key.
-  const int key(42);
-  m.try_emplace(key, 3, 'a');
-  m.try_emplace(2, std::string("two"));
-
-  EXPECT_TRUE(std::is_sorted(m.begin(), m.end()));
-  EXPECT_THAT(m, ElementsAreArray(std::vector<std::pair<int, std::string>>{
-                     {1, "one"}, {2, "two"}, {42, "aaa"}}));
-}
-
-TEST(Btree, TryEmplaceWithHintWorks) {
-  // Use a counting comparator here to verify that hint is used.
-  int calls = 0;
-  auto cmp = [&calls](int x, int y) {
-    ++calls;
-    return x < y;
-  };
-  using Cmp = decltype(cmp);
-
-  absl::btree_map<int, int, Cmp> m(cmp);
-  for (int i = 0; i < 128; ++i) {
-    m.emplace(i, i);
-  }
-
-  // Sanity check for the comparator
-  calls = 0;
-  m.emplace(127, 127);
-  EXPECT_GE(calls, 4);
-
-  // Try with begin hint:
-  calls = 0;
-  auto it = m.try_emplace(m.begin(), -1, -1);
-  EXPECT_EQ(129, m.size());
-  EXPECT_EQ(it, m.begin());
-  EXPECT_LE(calls, 2);
-
-  // Try with end hint:
-  calls = 0;
-  std::pair<int, int> pair1024 = {1024, 1024};
-  it = m.try_emplace(m.end(), pair1024.first, pair1024.second);
-  EXPECT_EQ(130, m.size());
-  EXPECT_EQ(it, --m.end());
-  EXPECT_LE(calls, 2);
-
-  // Try value already present, bad hint; ensure no duplicate added:
-  calls = 0;
-  it = m.try_emplace(m.end(), 16, 17);
-  EXPECT_EQ(130, m.size());
-  EXPECT_GE(calls, 4);
-  EXPECT_EQ(it, m.find(16));
-
-  // Try value already present, hint points directly to it:
-  calls = 0;
-  it = m.try_emplace(it, 16, 17);
-  EXPECT_EQ(130, m.size());
-  EXPECT_LE(calls, 2);
-  EXPECT_EQ(it, m.find(16));
-
-  m.erase(2);
-  EXPECT_EQ(129, m.size());
-  auto hint = m.find(3);
-  // Try emplace in the middle of two other elements.
-  calls = 0;
-  m.try_emplace(hint, 2, 2);
-  EXPECT_EQ(130, m.size());
-  EXPECT_LE(calls, 2);
-
-  EXPECT_TRUE(std::is_sorted(m.begin(), m.end()));
-}
-
-TEST(Btree, TryEmplaceWithBadHint) {
-  absl::btree_map<int, int> m = {{1, 1}, {9, 9}};
-
-  // Bad hint (too small), should still emplace:
-  auto it = m.try_emplace(m.begin(), 2, 2);
-  EXPECT_EQ(it, ++m.begin());
-  EXPECT_THAT(m, ElementsAreArray(
-                     std::vector<std::pair<int, int>>{{1, 1}, {2, 2}, {9, 9}}));
-
-  // Bad hint, too large this time:
-  it = m.try_emplace(++(++m.begin()), 0, 0);
-  EXPECT_EQ(it, m.begin());
-  EXPECT_THAT(m, ElementsAreArray(std::vector<std::pair<int, int>>{
-                     {0, 0}, {1, 1}, {2, 2}, {9, 9}}));
-}
-
-TEST(Btree, TryEmplaceMaintainsSortedOrder) {
-  absl::btree_map<int, std::string> m;
-  std::pair<int, std::string> pair5 = {5, "five"};
-
-  // Test both lvalue & rvalue emplace.
-  m.try_emplace(10, "ten");
-  m.try_emplace(pair5.first, pair5.second);
-  EXPECT_EQ(2, m.size());
-  EXPECT_TRUE(std::is_sorted(m.begin(), m.end()));
-
-  int int100{100};
-  m.try_emplace(int100, "hundred");
-  m.try_emplace(1, "one");
-  EXPECT_EQ(4, m.size());
-  EXPECT_TRUE(std::is_sorted(m.begin(), m.end()));
-}
-
-TEST(Btree, TryEmplaceWithHintAndNoValueArgsWorks) {
-  absl::btree_map<int, int> m;
-  m.try_emplace(m.end(), 1);
-  EXPECT_EQ(0, m[1]);
-}
-
-TEST(Btree, TryEmplaceWithHintAndMultipleValueArgsWorks) {
-  absl::btree_map<int, std::string> m;
-  m.try_emplace(m.end(), 1, 10, 'a');
-  EXPECT_EQ(std::string(10, 'a'), m[1]);
-}
-
-TEST(Btree, MoveAssignmentAllocatorPropagation) {
-  InstanceTracker tracker;
-
-  int64_t bytes1 = 0, bytes2 = 0;
-  PropagatingCountingAlloc<MovableOnlyInstance> allocator1(&bytes1);
-  PropagatingCountingAlloc<MovableOnlyInstance> allocator2(&bytes2);
-  std::less<MovableOnlyInstance> cmp;
-
-  // Test propagating allocator_type.
-  {
-    absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
-                    PropagatingCountingAlloc<MovableOnlyInstance>>
-        set1(cmp, allocator1), set2(cmp, allocator2);
-
-    for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
-
-    tracker.ResetCopiesMovesSwaps();
-    set2 = std::move(set1);
-    EXPECT_EQ(tracker.moves(), 0);
-  }
-  // Test non-propagating allocator_type with equal allocators.
-  {
-    absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
-                    CountingAllocator<MovableOnlyInstance>>
-        set1(cmp, allocator1), set2(cmp, allocator1);
-
-    for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
-
-    tracker.ResetCopiesMovesSwaps();
-    set2 = std::move(set1);
-    EXPECT_EQ(tracker.moves(), 0);
-  }
-  // Test non-propagating allocator_type with different allocators.
-  {
-    absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
-                    CountingAllocator<MovableOnlyInstance>>
-        set1(cmp, allocator1), set2(cmp, allocator2);
-
-    for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
-
-    tracker.ResetCopiesMovesSwaps();
-    set2 = std::move(set1);
-    EXPECT_GE(tracker.moves(), 100);
-  }
-}
-
-TEST(Btree, EmptyTree) {
-  absl::btree_set<int> s;
-  EXPECT_TRUE(s.empty());
-  EXPECT_EQ(s.size(), 0);
-  EXPECT_GT(s.max_size(), 0);
-}
-
-bool IsEven(int k) { return k % 2 == 0; }
-
-TEST(Btree, EraseIf) {
-  // Test that erase_if works with all the container types and supports lambdas.
-  {
-    absl::btree_set<int> s = {1, 3, 5, 6, 100};
-    erase_if(s, [](int k) { return k > 3; });
-    EXPECT_THAT(s, ElementsAre(1, 3));
-  }
-  {
-    absl::btree_multiset<int> s = {1, 3, 3, 5, 6, 6, 100};
-    erase_if(s, [](int k) { return k <= 3; });
-    EXPECT_THAT(s, ElementsAre(5, 6, 6, 100));
-  }
-  {
-    absl::btree_map<int, int> m = {{1, 1}, {3, 3}, {6, 6}, {100, 100}};
-    erase_if(m, [](std::pair<const int, int> kv) { return kv.first > 3; });
-    EXPECT_THAT(m, ElementsAre(Pair(1, 1), Pair(3, 3)));
-  }
-  {
-    absl::btree_multimap<int, int> m = {{1, 1}, {3, 3}, {3, 6},
-                                        {6, 6}, {6, 7}, {100, 6}};
-    erase_if(m, [](std::pair<const int, int> kv) { return kv.second == 6; });
-    EXPECT_THAT(m, ElementsAre(Pair(1, 1), Pair(3, 3), Pair(6, 7)));
-  }
-  // Test that erasing all elements from a large set works and test support for
-  // function pointers.
-  {
-    absl::btree_set<int> s;
-    for (int i = 0; i < 1000; ++i) s.insert(2 * i);
-    erase_if(s, IsEven);
-    EXPECT_THAT(s, IsEmpty());
-  }
-  // Test that erase_if supports other format of function pointers.
-  {
-    absl::btree_set<int> s = {1, 3, 5, 6, 100};
-    erase_if(s, &IsEven);
-    EXPECT_THAT(s, ElementsAre(1, 3, 5));
-  }
-}
-
-TEST(Btree, InsertOrAssign) {
-  absl::btree_map<int, int> m = {{1, 1}, {3, 3}};
-  using value_type = typename decltype(m)::value_type;
-
-  auto ret = m.insert_or_assign(4, 4);
-  EXPECT_EQ(*ret.first, value_type(4, 4));
-  EXPECT_TRUE(ret.second);
-  ret = m.insert_or_assign(3, 100);
-  EXPECT_EQ(*ret.first, value_type(3, 100));
-  EXPECT_FALSE(ret.second);
-
-  auto hint_ret = m.insert_or_assign(ret.first, 3, 200);
-  EXPECT_EQ(*hint_ret, value_type(3, 200));
-  hint_ret = m.insert_or_assign(m.find(1), 0, 1);
-  EXPECT_EQ(*hint_ret, value_type(0, 1));
-  // Test with bad hint.
-  hint_ret = m.insert_or_assign(m.end(), -1, 1);
-  EXPECT_EQ(*hint_ret, value_type(-1, 1));
-
-  EXPECT_THAT(m, ElementsAre(Pair(-1, 1), Pair(0, 1), Pair(1, 1), Pair(3, 200),
-                             Pair(4, 4)));
-}
-
-TEST(Btree, InsertOrAssignMovableOnly) {
-  absl::btree_map<int, MovableOnlyInstance> m;
-  using value_type = typename decltype(m)::value_type;
-
-  auto ret = m.insert_or_assign(4, MovableOnlyInstance(4));
-  EXPECT_EQ(*ret.first, value_type(4, MovableOnlyInstance(4)));
-  EXPECT_TRUE(ret.second);
-  ret = m.insert_or_assign(4, MovableOnlyInstance(100));
-  EXPECT_EQ(*ret.first, value_type(4, MovableOnlyInstance(100)));
-  EXPECT_FALSE(ret.second);
-
-  auto hint_ret = m.insert_or_assign(ret.first, 3, MovableOnlyInstance(200));
-  EXPECT_EQ(*hint_ret, value_type(3, MovableOnlyInstance(200)));
-
-  EXPECT_EQ(m.size(), 2);
-}
-
-TEST(Btree, BitfieldArgument) {
-  union {
-    int n : 1;
-  };
-  n = 0;
-  absl::btree_map<int, int> m;
-  m.erase(n);
-  m.count(n);
-  m.find(n);
-  m.contains(n);
-  m.equal_range(n);
-  m.insert_or_assign(n, n);
-  m.insert_or_assign(m.end(), n, n);
-  m.try_emplace(n);
-  m.try_emplace(m.end(), n);
-  m.at(n);
-  m[n];
-}
-
-TEST(Btree, SetRangeConstructorAndInsertSupportExplicitConversionComparable) {
-  const absl::string_view names[] = {"n1", "n2"};
-
-  absl::btree_set<std::string> name_set1{std::begin(names), std::end(names)};
-  EXPECT_THAT(name_set1, ElementsAreArray(names));
-
-  absl::btree_set<std::string> name_set2;
-  name_set2.insert(std::begin(names), std::end(names));
-  EXPECT_THAT(name_set2, ElementsAreArray(names));
-}
-
-// A type that is explicitly convertible from int and counts constructor calls.
-struct ConstructorCounted {
-  explicit ConstructorCounted(int i) : i(i) { ++constructor_calls; }
-  bool operator==(int other) const { return i == other; }
-
-  int i;
-  static int constructor_calls;
-};
-int ConstructorCounted::constructor_calls = 0;
-
-struct ConstructorCountedCompare {
-  bool operator()(int a, const ConstructorCounted &b) const { return a < b.i; }
-  bool operator()(const ConstructorCounted &a, int b) const { return a.i < b; }
-  bool operator()(const ConstructorCounted &a,
-                  const ConstructorCounted &b) const {
-    return a.i < b.i;
-  }
-  using is_transparent = void;
-};
-
-TEST(Btree,
-     SetRangeConstructorAndInsertExplicitConvComparableLimitConstruction) {
-  const int i[] = {0, 1, 1};
-  ConstructorCounted::constructor_calls = 0;
-
-  absl::btree_set<ConstructorCounted, ConstructorCountedCompare> set{
-      std::begin(i), std::end(i)};
-  EXPECT_THAT(set, ElementsAre(0, 1));
-  EXPECT_EQ(ConstructorCounted::constructor_calls, 2);
-
-  set.insert(std::begin(i), std::end(i));
-  EXPECT_THAT(set, ElementsAre(0, 1));
-  EXPECT_EQ(ConstructorCounted::constructor_calls, 2);
-}
-
-TEST(Btree,
-     SetRangeConstructorAndInsertSupportExplicitConversionNonComparable) {
-  const int i[] = {0, 1};
-
-  absl::btree_set<std::vector<void *>> s1{std::begin(i), std::end(i)};
-  EXPECT_THAT(s1, ElementsAre(IsEmpty(), ElementsAre(IsNull())));
-
-  absl::btree_set<std::vector<void *>> s2;
-  s2.insert(std::begin(i), std::end(i));
-  EXPECT_THAT(s2, ElementsAre(IsEmpty(), ElementsAre(IsNull())));
-}
-
-// libstdc++ included with GCC 4.9 has a bug in the std::pair constructors that
-// prevents explicit conversions between pair types.
-// We only run this test for the libstdc++ from GCC 7 or newer because we can't
-// reliably check the libstdc++ version prior to that release.
-#if !defined(__GLIBCXX__) || \
-    (defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 7)
-TEST(Btree, MapRangeConstructorAndInsertSupportExplicitConversionComparable) {
-  const std::pair<absl::string_view, int> names[] = {{"n1", 1}, {"n2", 2}};
-
-  absl::btree_map<std::string, int> name_map1{std::begin(names),
-                                              std::end(names)};
-  EXPECT_THAT(name_map1, ElementsAre(Pair("n1", 1), Pair("n2", 2)));
-
-  absl::btree_map<std::string, int> name_map2;
-  name_map2.insert(std::begin(names), std::end(names));
-  EXPECT_THAT(name_map2, ElementsAre(Pair("n1", 1), Pair("n2", 2)));
-}
-
-TEST(Btree,
-     MapRangeConstructorAndInsertExplicitConvComparableLimitConstruction) {
-  const std::pair<int, int> i[] = {{0, 1}, {1, 2}, {1, 3}};
-  ConstructorCounted::constructor_calls = 0;
-
-  absl::btree_map<ConstructorCounted, int, ConstructorCountedCompare> map{
-      std::begin(i), std::end(i)};
-  EXPECT_THAT(map, ElementsAre(Pair(0, 1), Pair(1, 2)));
-  EXPECT_EQ(ConstructorCounted::constructor_calls, 2);
-
-  map.insert(std::begin(i), std::end(i));
-  EXPECT_THAT(map, ElementsAre(Pair(0, 1), Pair(1, 2)));
-  EXPECT_EQ(ConstructorCounted::constructor_calls, 2);
-}
-
-TEST(Btree,
-     MapRangeConstructorAndInsertSupportExplicitConversionNonComparable) {
-  const std::pair<int, int> i[] = {{0, 1}, {1, 2}};
-
-  absl::btree_map<std::vector<void *>, int> m1{std::begin(i), std::end(i)};
-  EXPECT_THAT(m1,
-              ElementsAre(Pair(IsEmpty(), 1), Pair(ElementsAre(IsNull()), 2)));
-
-  absl::btree_map<std::vector<void *>, int> m2;
-  m2.insert(std::begin(i), std::end(i));
-  EXPECT_THAT(m2,
-              ElementsAre(Pair(IsEmpty(), 1), Pair(ElementsAre(IsNull()), 2)));
-}
-
-TEST(Btree, HeterogeneousTryEmplace) {
-  absl::btree_map<std::string, int> m;
-  std::string s = "key";
-  absl::string_view sv = s;
-  m.try_emplace(sv, 1);
-  EXPECT_EQ(m[s], 1);
-
-  m.try_emplace(m.end(), sv, 2);
-  EXPECT_EQ(m[s], 1);
-}
-
-TEST(Btree, HeterogeneousOperatorMapped) {
-  absl::btree_map<std::string, int> m;
-  std::string s = "key";
-  absl::string_view sv = s;
-  m[sv] = 1;
-  EXPECT_EQ(m[s], 1);
-
-  m[sv] = 2;
-  EXPECT_EQ(m[s], 2);
-}
-
-TEST(Btree, HeterogeneousInsertOrAssign) {
-  absl::btree_map<std::string, int> m;
-  std::string s = "key";
-  absl::string_view sv = s;
-  m.insert_or_assign(sv, 1);
-  EXPECT_EQ(m[s], 1);
-
-  m.insert_or_assign(m.end(), sv, 2);
-  EXPECT_EQ(m[s], 2);
-}
-#endif
-
-// This test requires std::launder for mutable key access in node handles.
-#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
-TEST(Btree, NodeHandleMutableKeyAccess) {
-  {
-    absl::btree_map<std::string, std::string> map;
-
-    map["key1"] = "mapped";
-
-    auto nh = map.extract(map.begin());
-    nh.key().resize(3);
-    map.insert(std::move(nh));
-
-    EXPECT_THAT(map, ElementsAre(Pair("key", "mapped")));
-  }
-  // Also for multimap.
-  {
-    absl::btree_multimap<std::string, std::string> map;
-
-    map.emplace("key1", "mapped");
-
-    auto nh = map.extract(map.begin());
-    nh.key().resize(3);
-    map.insert(std::move(nh));
-
-    EXPECT_THAT(map, ElementsAre(Pair("key", "mapped")));
-  }
-}
-#endif
-
-struct MultiKey {
-  int i1;
-  int i2;
-};
-
-bool operator==(const MultiKey a, const MultiKey b) {
-  return a.i1 == b.i1 && a.i2 == b.i2;
-}
-
-// A heterogeneous comparator that has different equivalence classes for
-// different lookup types.
-struct MultiKeyComp {
-  using is_transparent = void;
-  bool operator()(const MultiKey a, const MultiKey b) const {
-    if (a.i1 != b.i1) return a.i1 < b.i1;
-    return a.i2 < b.i2;
-  }
-  bool operator()(const int a, const MultiKey b) const { return a < b.i1; }
-  bool operator()(const MultiKey a, const int b) const { return a.i1 < b; }
-};
-
-TEST(Btree, MultiKeyEqualRange) {
-  absl::btree_set<MultiKey, MultiKeyComp> set;
-
-  for (int i = 0; i < 100; ++i) {
-    for (int j = 0; j < 100; ++j) {
-      set.insert({i, j});
-    }
-  }
-
-  for (int i = 0; i < 100; ++i) {
-    auto equal_range = set.equal_range(i);
-    EXPECT_EQ(equal_range.first->i1, i);
-    EXPECT_EQ(equal_range.first->i2, 0);
-    EXPECT_EQ(std::distance(equal_range.first, equal_range.second), 100) << i;
-  }
-}
-
-TEST(Btree, MultiKeyErase) {
-  absl::btree_set<MultiKey, MultiKeyComp> set = {
-      {1, 1}, {2, 1}, {2, 2}, {3, 1}};
-  EXPECT_EQ(set.erase(2), 2);
-  EXPECT_THAT(set, ElementsAre(MultiKey{1, 1}, MultiKey{3, 1}));
-}
-
-TEST(Btree, MultiKeyCount) {
-  const absl::btree_set<MultiKey, MultiKeyComp> set = {
-      {1, 1}, {2, 1}, {2, 2}, {3, 1}};
-  EXPECT_EQ(set.count(2), 2);
-}
-
-TEST(Btree, AllocConstructor) {
-  using Alloc = CountingAllocator<int>;
-  using Set = absl::btree_set<int, std::less<int>, Alloc>;
-  int64_t bytes_used = 0;
-  Alloc alloc(&bytes_used);
-  Set set(alloc);
-
-  set.insert({1, 2, 3});
-
-  EXPECT_THAT(set, ElementsAre(1, 2, 3));
-  EXPECT_GT(bytes_used, set.size() * sizeof(int));
-}
-
-TEST(Btree, AllocInitializerListConstructor) {
-  using Alloc = CountingAllocator<int>;
-  using Set = absl::btree_set<int, std::less<int>, Alloc>;
-  int64_t bytes_used = 0;
-  Alloc alloc(&bytes_used);
-  Set set({1, 2, 3}, alloc);
-
-  EXPECT_THAT(set, ElementsAre(1, 2, 3));
-  EXPECT_GT(bytes_used, set.size() * sizeof(int));
-}
-
-TEST(Btree, AllocRangeConstructor) {
-  using Alloc = CountingAllocator<int>;
-  using Set = absl::btree_set<int, std::less<int>, Alloc>;
-  int64_t bytes_used = 0;
-  Alloc alloc(&bytes_used);
-  std::vector<int> v = {1, 2, 3};
-  Set set(v.begin(), v.end(), alloc);
-
-  EXPECT_THAT(set, ElementsAre(1, 2, 3));
-  EXPECT_GT(bytes_used, set.size() * sizeof(int));
-}
-
-TEST(Btree, AllocCopyConstructor) {
-  using Alloc = CountingAllocator<int>;
-  using Set = absl::btree_set<int, std::less<int>, Alloc>;
-  int64_t bytes_used1 = 0;
-  Alloc alloc1(&bytes_used1);
-  Set set1(alloc1);
-
-  set1.insert({1, 2, 3});
-
-  int64_t bytes_used2 = 0;
-  Alloc alloc2(&bytes_used2);
-  Set set2(set1, alloc2);
-
-  EXPECT_THAT(set1, ElementsAre(1, 2, 3));
-  EXPECT_THAT(set2, ElementsAre(1, 2, 3));
-  EXPECT_GT(bytes_used1, set1.size() * sizeof(int));
-  EXPECT_EQ(bytes_used1, bytes_used2);
-}
-
-TEST(Btree, AllocMoveConstructor_SameAlloc) {
-  using Alloc = CountingAllocator<int>;
-  using Set = absl::btree_set<int, std::less<int>, Alloc>;
-  int64_t bytes_used = 0;
-  Alloc alloc(&bytes_used);
-  Set set1(alloc);
-
-  set1.insert({1, 2, 3});
-
-  const int64_t original_bytes_used = bytes_used;
-  EXPECT_GT(original_bytes_used, set1.size() * sizeof(int));
-
-  Set set2(std::move(set1), alloc);
-
-  EXPECT_THAT(set2, ElementsAre(1, 2, 3));
-  EXPECT_EQ(bytes_used, original_bytes_used);
-}
-
-TEST(Btree, AllocMoveConstructor_DifferentAlloc) {
-  using Alloc = CountingAllocator<int>;
-  using Set = absl::btree_set<int, std::less<int>, Alloc>;
-  int64_t bytes_used1 = 0;
-  Alloc alloc1(&bytes_used1);
-  Set set1(alloc1);
-
-  set1.insert({1, 2, 3});
-
-  const int64_t original_bytes_used = bytes_used1;
-  EXPECT_GT(original_bytes_used, set1.size() * sizeof(int));
-
-  int64_t bytes_used2 = 0;
-  Alloc alloc2(&bytes_used2);
-  Set set2(std::move(set1), alloc2);
-
-  EXPECT_THAT(set2, ElementsAre(1, 2, 3));
-  // We didn't free these bytes allocated by `set1` yet.
-  EXPECT_EQ(bytes_used1, original_bytes_used);
-  EXPECT_EQ(bytes_used2, original_bytes_used);
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/btree_test.h b/third_party/abseil_cpp/absl/container/btree_test.h
deleted file mode 100644
index 624908072d..0000000000
--- a/third_party/abseil_cpp/absl/container/btree_test.h
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_BTREE_TEST_H_
-#define ABSL_CONTAINER_BTREE_TEST_H_
-
-#include <algorithm>
-#include <cassert>
-#include <random>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "absl/container/btree_map.h"
-#include "absl/container/btree_set.h"
-#include "absl/container/flat_hash_set.h"
-#include "absl/strings/cord.h"
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// Like remove_const but propagates the removal through std::pair.
-template <typename T>
-struct remove_pair_const {
-  using type = typename std::remove_const<T>::type;
-};
-template <typename T, typename U>
-struct remove_pair_const<std::pair<T, U> > {
-  using type = std::pair<typename remove_pair_const<T>::type,
-                         typename remove_pair_const<U>::type>;
-};
-
-// Utility class to provide an accessor for a key given a value. The default
-// behavior is to treat the value as a pair and return the first element.
-template <typename K, typename V>
-struct KeyOfValue {
-  struct type {
-    const K& operator()(const V& p) const { return p.first; }
-  };
-};
-
-// Partial specialization of KeyOfValue class for when the key and value are
-// the same type such as in set<> and btree_set<>.
-template <typename K>
-struct KeyOfValue<K, K> {
-  struct type {
-    const K& operator()(const K& k) const { return k; }
-  };
-};
-
-inline char* GenerateDigits(char buf[16], unsigned val, unsigned maxval) {
-  assert(val <= maxval);
-  constexpr unsigned kBase = 64;  // avoid integer division.
-  unsigned p = 15;
-  buf[p--] = 0;
-  while (maxval > 0) {
-    buf[p--] = ' ' + (val % kBase);
-    val /= kBase;
-    maxval /= kBase;
-  }
-  return buf + p + 1;
-}
-
-template <typename K>
-struct Generator {
-  int maxval;
-  explicit Generator(int m) : maxval(m) {}
-  K operator()(int i) const {
-    assert(i <= maxval);
-    return K(i);
-  }
-};
-
-template <>
-struct Generator<absl::Time> {
-  int maxval;
-  explicit Generator(int m) : maxval(m) {}
-  absl::Time operator()(int i) const { return absl::FromUnixMillis(i); }
-};
-
-template <>
-struct Generator<std::string> {
-  int maxval;
-  explicit Generator(int m) : maxval(m) {}
-  std::string operator()(int i) const {
-    char buf[16];
-    return GenerateDigits(buf, i, maxval);
-  }
-};
-
-template <>
-struct Generator<Cord> {
-  int maxval;
-  explicit Generator(int m) : maxval(m) {}
-  Cord operator()(int i) const {
-    char buf[16];
-    return Cord(GenerateDigits(buf, i, maxval));
-  }
-};
-
-template <typename T, typename U>
-struct Generator<std::pair<T, U> > {
-  Generator<typename remove_pair_const<T>::type> tgen;
-  Generator<typename remove_pair_const<U>::type> ugen;
-
-  explicit Generator(int m) : tgen(m), ugen(m) {}
-  std::pair<T, U> operator()(int i) const {
-    return std::make_pair(tgen(i), ugen(i));
-  }
-};
-
-// Generate n values for our tests and benchmarks. Value range is [0, maxval].
-inline std::vector<int> GenerateNumbersWithSeed(int n, int maxval, int seed) {
-  // NOTE: Some tests rely on generated numbers not changing between test runs.
-  // We use std::minstd_rand0 because it is well-defined, but don't use
-  // std::uniform_int_distribution because platforms use different algorithms.
-  std::minstd_rand0 rng(seed);
-
-  std::vector<int> values;
-  absl::flat_hash_set<int> unique_values;
-  if (values.size() < n) {
-    for (int i = values.size(); i < n; i++) {
-      int value;
-      do {
-        value = static_cast<int>(rng()) % (maxval + 1);
-      } while (!unique_values.insert(value).second);
-
-      values.push_back(value);
-    }
-  }
-  return values;
-}
-
-// Generates n values in the range [0, maxval].
-template <typename V>
-std::vector<V> GenerateValuesWithSeed(int n, int maxval, int seed) {
-  const std::vector<int> nums = GenerateNumbersWithSeed(n, maxval, seed);
-  Generator<V> gen(maxval);
-  std::vector<V> vec;
-
-  vec.reserve(n);
-  for (int i = 0; i < n; i++) {
-    vec.push_back(gen(nums[i]));
-  }
-
-  return vec;
-}
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_BTREE_TEST_H_
diff --git a/third_party/abseil_cpp/absl/container/fixed_array.h b/third_party/abseil_cpp/absl/container/fixed_array.h
deleted file mode 100644
index fcb3e545b2..0000000000
--- a/third_party/abseil_cpp/absl/container/fixed_array.h
+++ /dev/null
@@ -1,532 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: fixed_array.h
-// -----------------------------------------------------------------------------
-//
-// A `FixedArray<T>` represents a non-resizable array of `T` where the length of
-// the array can be determined at run-time. It is a good replacement for
-// non-standard and deprecated uses of `alloca()` and variable length arrays
-// within the GCC extension. (See
-// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html).
-//
-// `FixedArray` allocates small arrays inline, keeping performance fast by
-// avoiding heap operations. It also helps reduce the chances of
-// accidentally overflowing your stack if large input is passed to
-// your function.
-
-#ifndef ABSL_CONTAINER_FIXED_ARRAY_H_
-#define ABSL_CONTAINER_FIXED_ARRAY_H_
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <initializer_list>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <new>
-#include <type_traits>
-
-#include "absl/algorithm/algorithm.h"
-#include "absl/base/config.h"
-#include "absl/base/dynamic_annotations.h"
-#include "absl/base/internal/throw_delegate.h"
-#include "absl/base/macros.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"
-#include "absl/container/internal/compressed_tuple.h"
-#include "absl/memory/memory.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
-
-// -----------------------------------------------------------------------------
-// FixedArray
-// -----------------------------------------------------------------------------
-//
-// A `FixedArray` provides a run-time fixed-size array, allocating a small array
-// inline for efficiency.
-//
-// Most users should not specify an `inline_elements` argument and let
-// `FixedArray` automatically determine the number of elements
-// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
-// `FixedArray` implementation will use inline storage for arrays with a
-// length <= `inline_elements`.
-//
-// Note that a `FixedArray` constructed with a `size_type` argument will
-// default-initialize its values by leaving trivially constructible types
-// uninitialized (e.g. int, int[4], double), and others default-constructed.
-// This matches the behavior of c-style arrays and `std::array`, but not
-// `std::vector`.
-//
-// Note that `FixedArray` does not provide a public allocator; if it requires a
-// heap allocation, it will do so with global `::operator new[]()` and
-// `::operator delete[]()`, even if T provides class-scope overrides for these
-// operators.
-template <typename T, size_t N = kFixedArrayUseDefault,
-          typename A = std::allocator<T>>
-class FixedArray {
-  static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
-                "Arrays with unknown bounds cannot be used with FixedArray.");
-
-  static constexpr size_t kInlineBytesDefault = 256;
-
-  using AllocatorTraits = std::allocator_traits<A>;
-  // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
-  // but this seems to be mostly pedantic.
-  template <typename Iterator>
-  using EnableIfForwardIterator = absl::enable_if_t<std::is_convertible<
-      typename std::iterator_traits<Iterator>::iterator_category,
-      std::forward_iterator_tag>::value>;
-  static constexpr bool NoexceptCopyable() {
-    return std::is_nothrow_copy_constructible<StorageElement>::value &&
-           absl::allocator_is_nothrow<allocator_type>::value;
-  }
-  static constexpr bool NoexceptMovable() {
-    return std::is_nothrow_move_constructible<StorageElement>::value &&
-           absl::allocator_is_nothrow<allocator_type>::value;
-  }
-  static constexpr bool DefaultConstructorIsNonTrivial() {
-    return !absl::is_trivially_default_constructible<StorageElement>::value;
-  }
-
- public:
-  using allocator_type = typename AllocatorTraits::allocator_type;
-  using value_type = typename AllocatorTraits::value_type;
-  using pointer = typename AllocatorTraits::pointer;
-  using const_pointer = typename AllocatorTraits::const_pointer;
-  using reference = value_type&;
-  using const_reference = const value_type&;
-  using size_type = typename AllocatorTraits::size_type;
-  using difference_type = typename AllocatorTraits::difference_type;
-  using iterator = pointer;
-  using const_iterator = const_pointer;
-  using reverse_iterator = std::reverse_iterator<iterator>;
-  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-
-  static constexpr size_type inline_elements =
-      (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type)
-                                  : static_cast<size_type>(N));
-
-  FixedArray(
-      const FixedArray& other,
-      const allocator_type& a = allocator_type()) noexcept(NoexceptCopyable())
-      : FixedArray(other.begin(), other.end(), a) {}
-
-  FixedArray(
-      FixedArray&& other,
-      const allocator_type& a = allocator_type()) noexcept(NoexceptMovable())
-      : FixedArray(std::make_move_iterator(other.begin()),
-                   std::make_move_iterator(other.end()), a) {}
-
-  // Creates an array object that can store `n` elements.
-  // Note that trivially constructible elements will be uninitialized.
-  explicit FixedArray(size_type n, const allocator_type& a = allocator_type())
-      : storage_(n, a) {
-    if (DefaultConstructorIsNonTrivial()) {
-      memory_internal::ConstructRange(storage_.alloc(), storage_.begin(),
-                                      storage_.end());
-    }
-  }
-
-  // Creates an array initialized with `n` copies of `val`.
-  FixedArray(size_type n, const value_type& val,
-             const allocator_type& a = allocator_type())
-      : storage_(n, a) {
-    memory_internal::ConstructRange(storage_.alloc(), storage_.begin(),
-                                    storage_.end(), val);
-  }
-
-  // Creates an array initialized with the size and contents of `init_list`.
-  FixedArray(std::initializer_list<value_type> init_list,
-             const allocator_type& a = allocator_type())
-      : FixedArray(init_list.begin(), init_list.end(), a) {}
-
-  // Creates an array initialized with the elements from the input
-  // range. The array's size will always be `std::distance(first, last)`.
-  // REQUIRES: Iterator must be a forward_iterator or better.
-  template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr>
-  FixedArray(Iterator first, Iterator last,
-             const allocator_type& a = allocator_type())
-      : storage_(std::distance(first, last), a) {
-    memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last);
-  }
-
-  ~FixedArray() noexcept {
-    for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) {
-      AllocatorTraits::destroy(storage_.alloc(), cur);
-    }
-  }
-
-  // Assignments are deleted because they break the invariant that the size of a
-  // `FixedArray` never changes.
-  void operator=(FixedArray&&) = delete;
-  void operator=(const FixedArray&) = delete;
-
-  // FixedArray::size()
-  //
-  // Returns the length of the fixed array.
-  size_type size() const { return storage_.size(); }
-
-  // FixedArray::max_size()
-  //
-  // Returns the largest possible value of `std::distance(begin(), end())` for a
-  // `FixedArray<T>`. This is equivalent to the most possible addressable bytes
-  // over the number of bytes taken by T.
-  constexpr size_type max_size() const {
-    return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
-  }
-
-  // FixedArray::empty()
-  //
-  // Returns whether or not the fixed array is empty.
-  bool empty() const { return size() == 0; }
-
-  // FixedArray::memsize()
-  //
-  // Returns the memory size of the fixed array in bytes.
-  size_t memsize() const { return size() * sizeof(value_type); }
-
-  // FixedArray::data()
-  //
-  // Returns a const T* pointer to elements of the `FixedArray`. This pointer
-  // can be used to access (but not modify) the contained elements.
-  const_pointer data() const { return AsValueType(storage_.begin()); }
-
-  // Overload of FixedArray::data() to return a T* pointer to elements of the
-  // fixed array. This pointer can be used to access and modify the contained
-  // elements.
-  pointer data() { return AsValueType(storage_.begin()); }
-
-  // FixedArray::operator[]
-  //
-  // Returns a reference the ith element of the fixed array.
-  // REQUIRES: 0 <= i < size()
-  reference operator[](size_type i) {
-    ABSL_HARDENING_ASSERT(i < size());
-    return data()[i];
-  }
-
-  // Overload of FixedArray::operator()[] to return a const reference to the
-  // ith element of the fixed array.
-  // REQUIRES: 0 <= i < size()
-  const_reference operator[](size_type i) const {
-    ABSL_HARDENING_ASSERT(i < size());
-    return data()[i];
-  }
-
-  // FixedArray::at
-  //
-  // Bounds-checked access.  Returns a reference to the ith element of the fixed
-  // array, or throws std::out_of_range
-  reference at(size_type i) {
-    if (ABSL_PREDICT_FALSE(i >= size())) {
-      base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
-    }
-    return data()[i];
-  }
-
-  // Overload of FixedArray::at() to return a const reference to the ith element
-  // of the fixed array.
-  const_reference at(size_type i) const {
-    if (ABSL_PREDICT_FALSE(i >= size())) {
-      base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
-    }
-    return data()[i];
-  }
-
-  // FixedArray::front()
-  //
-  // Returns a reference to the first element of the fixed array.
-  reference front() {
-    ABSL_HARDENING_ASSERT(!empty());
-    return data()[0];
-  }
-
-  // Overload of FixedArray::front() to return a reference to the first element
-  // of a fixed array of const values.
-  const_reference front() const {
-    ABSL_HARDENING_ASSERT(!empty());
-    return data()[0];
-  }
-
-  // FixedArray::back()
-  //
-  // Returns a reference to the last element of the fixed array.
-  reference back() {
-    ABSL_HARDENING_ASSERT(!empty());
-    return data()[size() - 1];
-  }
-
-  // Overload of FixedArray::back() to return a reference to the last element
-  // of a fixed array of const values.
-  const_reference back() const {
-    ABSL_HARDENING_ASSERT(!empty());
-    return data()[size() - 1];
-  }
-
-  // FixedArray::begin()
-  //
-  // Returns an iterator to the beginning of the fixed array.
-  iterator begin() { return data(); }
-
-  // Overload of FixedArray::begin() to return a const iterator to the
-  // beginning of the fixed array.
-  const_iterator begin() const { return data(); }
-
-  // FixedArray::cbegin()
-  //
-  // Returns a const iterator to the beginning of the fixed array.
-  const_iterator cbegin() const { return begin(); }
-
-  // FixedArray::end()
-  //
-  // Returns an iterator to the end of the fixed array.
-  iterator end() { return data() + size(); }
-
-  // Overload of FixedArray::end() to return a const iterator to the end of the
-  // fixed array.
-  const_iterator end() const { return data() + size(); }
-
-  // FixedArray::cend()
-  //
-  // Returns a const iterator to the end of the fixed array.
-  const_iterator cend() const { return end(); }
-
-  // FixedArray::rbegin()
-  //
-  // Returns a reverse iterator from the end of the fixed array.
-  reverse_iterator rbegin() { return reverse_iterator(end()); }
-
-  // Overload of FixedArray::rbegin() to return a const reverse iterator from
-  // the end of the fixed array.
-  const_reverse_iterator rbegin() const {
-    return const_reverse_iterator(end());
-  }
-
-  // FixedArray::crbegin()
-  //
-  // Returns a const reverse iterator from the end of the fixed array.
-  const_reverse_iterator crbegin() const { return rbegin(); }
-
-  // FixedArray::rend()
-  //
-  // Returns a reverse iterator from the beginning of the fixed array.
-  reverse_iterator rend() { return reverse_iterator(begin()); }
-
-  // Overload of FixedArray::rend() for returning a const reverse iterator
-  // from the beginning of the fixed array.
-  const_reverse_iterator rend() const {
-    return const_reverse_iterator(begin());
-  }
-
-  // FixedArray::crend()
-  //
-  // Returns a reverse iterator from the beginning of the fixed array.
-  const_reverse_iterator crend() const { return rend(); }
-
-  // FixedArray::fill()
-  //
-  // Assigns the given `value` to all elements in the fixed array.
-  void fill(const value_type& val) { std::fill(begin(), end(), val); }
-
-  // Relational operators. Equality operators are elementwise using
-  // `operator==`, while order operators order FixedArrays lexicographically.
-  friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
-    return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
-  }
-
-  friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
-    return !(lhs == rhs);
-  }
-
-  friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) {
-    return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(),
-                                        rhs.end());
-  }
-
-  friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) {
-    return rhs < lhs;
-  }
-
-  friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) {
-    return !(rhs < lhs);
-  }
-
-  friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) {
-    return !(lhs < rhs);
-  }
-
-  template <typename H>
-  friend H AbslHashValue(H h, const FixedArray& v) {
-    return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()),
-                      v.size());
-  }
-
- private:
-  // StorageElement
-  //
-  // For FixedArrays with a C-style-array value_type, StorageElement is a POD
-  // wrapper struct called StorageElementWrapper that holds the value_type
-  // instance inside. This is needed for construction and destruction of the
-  // entire array regardless of how many dimensions it has. For all other cases,
-  // StorageElement is just an alias of value_type.
-  //
-  // Maintainer's Note: The simpler solution would be to simply wrap value_type
-  // in a struct whether it's an array or not. That causes some paranoid
-  // diagnostics to misfire, believing that 'data()' returns a pointer to a
-  // single element, rather than the packed array that it really is.
-  // e.g.:
-  //
-  //     FixedArray<char> buf(1);
-  //     sprintf(buf.data(), "foo");
-  //
-  //     error: call to int __builtin___sprintf_chk(etc...)
-  //     will always overflow destination buffer [-Werror]
-  //
-  template <typename OuterT, typename InnerT = absl::remove_extent_t<OuterT>,
-            size_t InnerN = std::extent<OuterT>::value>
-  struct StorageElementWrapper {
-    InnerT array[InnerN];
-  };
-
-  using StorageElement =
-      absl::conditional_t<std::is_array<value_type>::value,
-                          StorageElementWrapper<value_type>, value_type>;
-
-  static pointer AsValueType(pointer ptr) { return ptr; }
-  static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
-    return std::addressof(ptr->array);
-  }
-
-  static_assert(sizeof(StorageElement) == sizeof(value_type), "");
-  static_assert(alignof(StorageElement) == alignof(value_type), "");
-
-  class NonEmptyInlinedStorage {
-   public:
-    StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); }
-    void AnnotateConstruct(size_type n);
-    void AnnotateDestruct(size_type n);
-
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-    void* RedzoneBegin() { return &redzone_begin_; }
-    void* RedzoneEnd() { return &redzone_end_ + 1; }
-#endif  // ABSL_HAVE_ADDRESS_SANITIZER
-
-   private:
-    ABSL_ADDRESS_SANITIZER_REDZONE(redzone_begin_);
-    alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])];
-    ABSL_ADDRESS_SANITIZER_REDZONE(redzone_end_);
-  };
-
-  class EmptyInlinedStorage {
-   public:
-    StorageElement* data() { return nullptr; }
-    void AnnotateConstruct(size_type) {}
-    void AnnotateDestruct(size_type) {}
-  };
-
-  using InlinedStorage =
-      absl::conditional_t<inline_elements == 0, EmptyInlinedStorage,
-                          NonEmptyInlinedStorage>;
-
-  // Storage
-  //
-  // An instance of Storage manages the inline and out-of-line memory for
-  // instances of FixedArray. This guarantees that even when construction of
-  // individual elements fails in the FixedArray constructor body, the
-  // destructor for Storage will still be called and out-of-line memory will be
-  // properly deallocated.
-  //
-  class Storage : public InlinedStorage {
-   public:
-    Storage(size_type n, const allocator_type& a)
-        : size_alloc_(n, a), data_(InitializeData()) {}
-
-    ~Storage() noexcept {
-      if (UsingInlinedStorage(size())) {
-        InlinedStorage::AnnotateDestruct(size());
-      } else {
-        AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
-      }
-    }
-
-    size_type size() const { return size_alloc_.template get<0>(); }
-    StorageElement* begin() const { return data_; }
-    StorageElement* end() const { return begin() + size(); }
-    allocator_type& alloc() { return size_alloc_.template get<1>(); }
-
-   private:
-    static bool UsingInlinedStorage(size_type n) {
-      return n <= inline_elements;
-    }
-
-    StorageElement* InitializeData() {
-      if (UsingInlinedStorage(size())) {
-        InlinedStorage::AnnotateConstruct(size());
-        return InlinedStorage::data();
-      } else {
-        return reinterpret_cast<StorageElement*>(
-            AllocatorTraits::allocate(alloc(), size()));
-      }
-    }
-
-    // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s
-    container_internal::CompressedTuple<size_type, allocator_type> size_alloc_;
-    StorageElement* data_;
-  };
-
-  Storage storage_;
-};
-
-template <typename T, size_t N, typename A>
-constexpr size_t FixedArray<T, N, A>::kInlineBytesDefault;
-
-template <typename T, size_t N, typename A>
-constexpr typename FixedArray<T, N, A>::size_type
-    FixedArray<T, N, A>::inline_elements;
-
-template <typename T, size_t N, typename A>
-void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateConstruct(
-    typename FixedArray<T, N, A>::size_type n) {
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-  if (!n) return;
-  ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(),
-                                     data() + n);
-  ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(),
-                                     RedzoneBegin());
-#endif  // ABSL_HAVE_ADDRESS_SANITIZER
-  static_cast<void>(n);  // Mark used when not in asan mode
-}
-
-template <typename T, size_t N, typename A>
-void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct(
-    typename FixedArray<T, N, A>::size_type n) {
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-  if (!n) return;
-  ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n,
-                                     RedzoneEnd());
-  ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(),
-                                     data());
-#endif  // ABSL_HAVE_ADDRESS_SANITIZER
-  static_cast<void>(n);  // Mark used when not in asan mode
-}
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_FIXED_ARRAY_H_
diff --git a/third_party/abseil_cpp/absl/container/fixed_array_benchmark.cc b/third_party/abseil_cpp/absl/container/fixed_array_benchmark.cc
deleted file mode 100644
index 3c7a5a7234..0000000000
--- a/third_party/abseil_cpp/absl/container/fixed_array_benchmark.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <stddef.h>
-
-#include <string>
-
-#include "benchmark/benchmark.h"
-#include "absl/container/fixed_array.h"
-
-namespace {
-
-// For benchmarking -- simple class with constructor and destructor that
-// set an int to a constant..
-class SimpleClass {
- public:
-  SimpleClass() : i(3) {}
-  ~SimpleClass() { i = 0; }
-
- private:
-  int i;
-};
-
-template <typename C, size_t stack_size>
-void BM_FixedArray(benchmark::State& state) {
-  const int size = state.range(0);
-  for (auto _ : state) {
-    absl::FixedArray<C, stack_size> fa(size);
-    benchmark::DoNotOptimize(fa.data());
-  }
-}
-BENCHMARK_TEMPLATE(BM_FixedArray, char, absl::kFixedArrayUseDefault)
-    ->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, char, 0)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, char, 1)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, char, 16)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, char, 256)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, char, 65536)->Range(0, 1 << 16);
-
-BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, absl::kFixedArrayUseDefault)
-    ->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 0)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 1)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 16)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 256)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 65536)->Range(0, 1 << 16);
-
-BENCHMARK_TEMPLATE(BM_FixedArray, std::string, absl::kFixedArrayUseDefault)
-    ->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 0)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 1)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 16)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 256)->Range(0, 1 << 16);
-BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 65536)->Range(0, 1 << 16);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/container/fixed_array_exception_safety_test.cc b/third_party/abseil_cpp/absl/container/fixed_array_exception_safety_test.cc
deleted file mode 100644
index e5f59299b3..0000000000
--- a/third_party/abseil_cpp/absl/container/fixed_array_exception_safety_test.cc
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/config.h"
-#include "absl/container/fixed_array.h"
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-#include <initializer_list>
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/exception_safety_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace {
-
-constexpr size_t kInlined = 25;
-constexpr size_t kSmallSize = kInlined / 2;
-constexpr size_t kLargeSize = kInlined * 2;
-
-constexpr int kInitialValue = 5;
-constexpr int kUpdatedValue = 10;
-
-using ::testing::TestThrowingCtor;
-
-using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
-using ThrowAlloc =
-    testing::ThrowingAllocator<Thrower, testing::AllocSpec::kEverythingThrows>;
-using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
-using MoveThrowAlloc =
-    testing::ThrowingAllocator<MoveThrower,
-                               testing::AllocSpec::kEverythingThrows>;
-
-using FixedArr = absl::FixedArray<Thrower, kInlined>;
-using FixedArrWithAlloc = absl::FixedArray<Thrower, kInlined, ThrowAlloc>;
-
-using MoveFixedArr = absl::FixedArray<MoveThrower, kInlined>;
-using MoveFixedArrWithAlloc =
-    absl::FixedArray<MoveThrower, kInlined, MoveThrowAlloc>;
-
-TEST(FixedArrayExceptionSafety, CopyConstructor) {
-  auto small = FixedArr(kSmallSize);
-  TestThrowingCtor<FixedArr>(small);
-
-  auto large = FixedArr(kLargeSize);
-  TestThrowingCtor<FixedArr>(large);
-}
-
-TEST(FixedArrayExceptionSafety, CopyConstructorWithAlloc) {
-  auto small = FixedArrWithAlloc(kSmallSize);
-  TestThrowingCtor<FixedArrWithAlloc>(small);
-
-  auto large = FixedArrWithAlloc(kLargeSize);
-  TestThrowingCtor<FixedArrWithAlloc>(large);
-}
-
-TEST(FixedArrayExceptionSafety, MoveConstructor) {
-  TestThrowingCtor<FixedArr>(FixedArr(kSmallSize));
-  TestThrowingCtor<FixedArr>(FixedArr(kLargeSize));
-
-  // TypeSpec::kNoThrowMove
-  TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kSmallSize));
-  TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kLargeSize));
-}
-
-TEST(FixedArrayExceptionSafety, MoveConstructorWithAlloc) {
-  TestThrowingCtor<FixedArrWithAlloc>(FixedArrWithAlloc(kSmallSize));
-  TestThrowingCtor<FixedArrWithAlloc>(FixedArrWithAlloc(kLargeSize));
-
-  // TypeSpec::kNoThrowMove
-  TestThrowingCtor<MoveFixedArrWithAlloc>(MoveFixedArrWithAlloc(kSmallSize));
-  TestThrowingCtor<MoveFixedArrWithAlloc>(MoveFixedArrWithAlloc(kLargeSize));
-}
-
-TEST(FixedArrayExceptionSafety, SizeConstructor) {
-  TestThrowingCtor<FixedArr>(kSmallSize);
-  TestThrowingCtor<FixedArr>(kLargeSize);
-}
-
-TEST(FixedArrayExceptionSafety, SizeConstructorWithAlloc) {
-  TestThrowingCtor<FixedArrWithAlloc>(kSmallSize);
-  TestThrowingCtor<FixedArrWithAlloc>(kLargeSize);
-}
-
-TEST(FixedArrayExceptionSafety, SizeValueConstructor) {
-  TestThrowingCtor<FixedArr>(kSmallSize, Thrower());
-  TestThrowingCtor<FixedArr>(kLargeSize, Thrower());
-}
-
-TEST(FixedArrayExceptionSafety, SizeValueConstructorWithAlloc) {
-  TestThrowingCtor<FixedArrWithAlloc>(kSmallSize, Thrower());
-  TestThrowingCtor<FixedArrWithAlloc>(kLargeSize, Thrower());
-}
-
-TEST(FixedArrayExceptionSafety, IteratorConstructor) {
-  auto small = FixedArr(kSmallSize);
-  TestThrowingCtor<FixedArr>(small.begin(), small.end());
-
-  auto large = FixedArr(kLargeSize);
-  TestThrowingCtor<FixedArr>(large.begin(), large.end());
-}
-
-TEST(FixedArrayExceptionSafety, IteratorConstructorWithAlloc) {
-  auto small = FixedArrWithAlloc(kSmallSize);
-  TestThrowingCtor<FixedArrWithAlloc>(small.begin(), small.end());
-
-  auto large = FixedArrWithAlloc(kLargeSize);
-  TestThrowingCtor<FixedArrWithAlloc>(large.begin(), large.end());
-}
-
-TEST(FixedArrayExceptionSafety, InitListConstructor) {
-  constexpr int small_inlined = 3;
-  using SmallFixedArr = absl::FixedArray<Thrower, small_inlined>;
-
-  TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{});
-  // Test inlined allocation
-  TestThrowingCtor<SmallFixedArr>(
-      std::initializer_list<Thrower>{Thrower{}, Thrower{}});
-  // Test out of line allocation
-  TestThrowingCtor<SmallFixedArr>(std::initializer_list<Thrower>{
-      Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}});
-}
-
-TEST(FixedArrayExceptionSafety, InitListConstructorWithAlloc) {
-  constexpr int small_inlined = 3;
-  using SmallFixedArrWithAlloc =
-      absl::FixedArray<Thrower, small_inlined, ThrowAlloc>;
-
-  TestThrowingCtor<SmallFixedArrWithAlloc>(std::initializer_list<Thrower>{});
-  // Test inlined allocation
-  TestThrowingCtor<SmallFixedArrWithAlloc>(
-      std::initializer_list<Thrower>{Thrower{}, Thrower{}});
-  // Test out of line allocation
-  TestThrowingCtor<SmallFixedArrWithAlloc>(std::initializer_list<Thrower>{
-      Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}});
-}
-
-template <typename FixedArrT>
-testing::AssertionResult ReadMemory(FixedArrT* fixed_arr) {
-  int sum = 0;
-  for (const auto& thrower : *fixed_arr) {
-    sum += thrower.Get();
-  }
-  return testing::AssertionSuccess() << "Values sum to [" << sum << "]";
-}
-
-TEST(FixedArrayExceptionSafety, Fill) {
-  auto test_fill = testing::MakeExceptionSafetyTester()
-                       .WithContracts(ReadMemory<FixedArr>)
-                       .WithOperation([&](FixedArr* fixed_arr_ptr) {
-                         auto thrower =
-                             Thrower(kUpdatedValue, testing::nothrow_ctor);
-                         fixed_arr_ptr->fill(thrower);
-                       });
-
-  EXPECT_TRUE(
-      test_fill.WithInitialValue(FixedArr(kSmallSize, Thrower(kInitialValue)))
-          .Test());
-  EXPECT_TRUE(
-      test_fill.WithInitialValue(FixedArr(kLargeSize, Thrower(kInitialValue)))
-          .Test());
-}
-
-TEST(FixedArrayExceptionSafety, FillWithAlloc) {
-  auto test_fill = testing::MakeExceptionSafetyTester()
-                       .WithContracts(ReadMemory<FixedArrWithAlloc>)
-                       .WithOperation([&](FixedArrWithAlloc* fixed_arr_ptr) {
-                         auto thrower =
-                             Thrower(kUpdatedValue, testing::nothrow_ctor);
-                         fixed_arr_ptr->fill(thrower);
-                       });
-
-  EXPECT_TRUE(test_fill
-                  .WithInitialValue(
-                      FixedArrWithAlloc(kSmallSize, Thrower(kInitialValue)))
-                  .Test());
-  EXPECT_TRUE(test_fill
-                  .WithInitialValue(
-                      FixedArrWithAlloc(kLargeSize, Thrower(kInitialValue)))
-                  .Test());
-}
-
-}  // namespace
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HAVE_EXCEPTIONS
diff --git a/third_party/abseil_cpp/absl/container/fixed_array_test.cc b/third_party/abseil_cpp/absl/container/fixed_array_test.cc
deleted file mode 100644
index 49598e7a05..0000000000
--- a/third_party/abseil_cpp/absl/container/fixed_array_test.cc
+++ /dev/null
@@ -1,837 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/fixed_array.h"
-
-#include <stdio.h>
-
-#include <cstring>
-#include <list>
-#include <memory>
-#include <numeric>
-#include <scoped_allocator>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/exception_testing.h"
-#include "absl/base/options.h"
-#include "absl/container/internal/counting_allocator.h"
-#include "absl/hash/hash_testing.h"
-#include "absl/memory/memory.h"
-
-using ::testing::ElementsAreArray;
-
-namespace {
-
-// Helper routine to determine if a absl::FixedArray used stack allocation.
-template <typename ArrayType>
-static bool IsOnStack(const ArrayType& a) {
-  return a.size() <= ArrayType::inline_elements;
-}
-
-class ConstructionTester {
- public:
-  ConstructionTester() : self_ptr_(this), value_(0) { constructions++; }
-  ~ConstructionTester() {
-    assert(self_ptr_ == this);
-    self_ptr_ = nullptr;
-    destructions++;
-  }
-
-  // These are incremented as elements are constructed and destructed so we can
-  // be sure all elements are properly cleaned up.
-  static int constructions;
-  static int destructions;
-
-  void CheckConstructed() { assert(self_ptr_ == this); }
-
-  void set(int value) { value_ = value; }
-  int get() { return value_; }
-
- private:
-  // self_ptr_ should always point to 'this' -- that's how we can be sure the
-  // constructor has been called.
-  ConstructionTester* self_ptr_;
-  int value_;
-};
-
-int ConstructionTester::constructions = 0;
-int ConstructionTester::destructions = 0;
-
-// ThreeInts will initialize its three ints to the value stored in
-// ThreeInts::counter. The constructor increments counter so that each object
-// in an array of ThreeInts will have different values.
-class ThreeInts {
- public:
-  ThreeInts() {
-    x_ = counter;
-    y_ = counter;
-    z_ = counter;
-    ++counter;
-  }
-
-  static int counter;
-
-  int x_, y_, z_;
-};
-
-int ThreeInts::counter = 0;
-
-TEST(FixedArrayTest, CopyCtor) {
-  absl::FixedArray<int, 10> on_stack(5);
-  std::iota(on_stack.begin(), on_stack.end(), 0);
-  absl::FixedArray<int, 10> stack_copy = on_stack;
-  EXPECT_THAT(stack_copy, ElementsAreArray(on_stack));
-  EXPECT_TRUE(IsOnStack(stack_copy));
-
-  absl::FixedArray<int, 10> allocated(15);
-  std::iota(allocated.begin(), allocated.end(), 0);
-  absl::FixedArray<int, 10> alloced_copy = allocated;
-  EXPECT_THAT(alloced_copy, ElementsAreArray(allocated));
-  EXPECT_FALSE(IsOnStack(alloced_copy));
-}
-
-TEST(FixedArrayTest, MoveCtor) {
-  absl::FixedArray<std::unique_ptr<int>, 10> on_stack(5);
-  for (int i = 0; i < 5; ++i) {
-    on_stack[i] = absl::make_unique<int>(i);
-  }
-
-  absl::FixedArray<std::unique_ptr<int>, 10> stack_copy = std::move(on_stack);
-  for (int i = 0; i < 5; ++i) EXPECT_EQ(*(stack_copy[i]), i);
-  EXPECT_EQ(stack_copy.size(), on_stack.size());
-
-  absl::FixedArray<std::unique_ptr<int>, 10> allocated(15);
-  for (int i = 0; i < 15; ++i) {
-    allocated[i] = absl::make_unique<int>(i);
-  }
-
-  absl::FixedArray<std::unique_ptr<int>, 10> alloced_copy =
-      std::move(allocated);
-  for (int i = 0; i < 15; ++i) EXPECT_EQ(*(alloced_copy[i]), i);
-  EXPECT_EQ(allocated.size(), alloced_copy.size());
-}
-
-TEST(FixedArrayTest, SmallObjects) {
-  // Small object arrays
-  {
-    // Short arrays should be on the stack
-    absl::FixedArray<int> array(4);
-    EXPECT_TRUE(IsOnStack(array));
-  }
-
-  {
-    // Large arrays should be on the heap
-    absl::FixedArray<int> array(1048576);
-    EXPECT_FALSE(IsOnStack(array));
-  }
-
-  {
-    // Arrays of <= default size should be on the stack
-    absl::FixedArray<int, 100> array(100);
-    EXPECT_TRUE(IsOnStack(array));
-  }
-
-  {
-    // Arrays of > default size should be on the heap
-    absl::FixedArray<int, 100> array(101);
-    EXPECT_FALSE(IsOnStack(array));
-  }
-
-  {
-    // Arrays with different size elements should use approximately
-    // same amount of stack space
-    absl::FixedArray<int> array1(0);
-    absl::FixedArray<char> array2(0);
-    EXPECT_LE(sizeof(array1), sizeof(array2) + 100);
-    EXPECT_LE(sizeof(array2), sizeof(array1) + 100);
-  }
-
-  {
-    // Ensure that vectors are properly constructed inside a fixed array.
-    absl::FixedArray<std::vector<int>> array(2);
-    EXPECT_EQ(0, array[0].size());
-    EXPECT_EQ(0, array[1].size());
-  }
-
-  {
-    // Regardless of absl::FixedArray implementation, check that a type with a
-    // low alignment requirement and a non power-of-two size is initialized
-    // correctly.
-    ThreeInts::counter = 1;
-    absl::FixedArray<ThreeInts> array(2);
-    EXPECT_EQ(1, array[0].x_);
-    EXPECT_EQ(1, array[0].y_);
-    EXPECT_EQ(1, array[0].z_);
-    EXPECT_EQ(2, array[1].x_);
-    EXPECT_EQ(2, array[1].y_);
-    EXPECT_EQ(2, array[1].z_);
-  }
-}
-
-TEST(FixedArrayTest, AtThrows) {
-  absl::FixedArray<int> a = {1, 2, 3};
-  EXPECT_EQ(a.at(2), 3);
-  ABSL_BASE_INTERNAL_EXPECT_FAIL(a.at(3), std::out_of_range,
-                                 "failed bounds check");
-}
-
-TEST(FixedArrayTest, Hardened) {
-#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
-  absl::FixedArray<int> a = {1, 2, 3};
-  EXPECT_EQ(a[2], 3);
-  EXPECT_DEATH_IF_SUPPORTED(a[3], "");
-  EXPECT_DEATH_IF_SUPPORTED(a[-1], "");
-
-  absl::FixedArray<int> empty(0);
-  EXPECT_DEATH_IF_SUPPORTED(empty[0], "");
-  EXPECT_DEATH_IF_SUPPORTED(empty[-1], "");
-  EXPECT_DEATH_IF_SUPPORTED(empty.front(), "");
-  EXPECT_DEATH_IF_SUPPORTED(empty.back(), "");
-#endif
-}
-
-TEST(FixedArrayRelationalsTest, EqualArrays) {
-  for (int i = 0; i < 10; ++i) {
-    absl::FixedArray<int, 5> a1(i);
-    std::iota(a1.begin(), a1.end(), 0);
-    absl::FixedArray<int, 5> a2(a1.begin(), a1.end());
-
-    EXPECT_TRUE(a1 == a2);
-    EXPECT_FALSE(a1 != a2);
-    EXPECT_TRUE(a2 == a1);
-    EXPECT_FALSE(a2 != a1);
-    EXPECT_FALSE(a1 < a2);
-    EXPECT_FALSE(a1 > a2);
-    EXPECT_FALSE(a2 < a1);
-    EXPECT_FALSE(a2 > a1);
-    EXPECT_TRUE(a1 <= a2);
-    EXPECT_TRUE(a1 >= a2);
-    EXPECT_TRUE(a2 <= a1);
-    EXPECT_TRUE(a2 >= a1);
-  }
-}
-
-TEST(FixedArrayRelationalsTest, UnequalArrays) {
-  for (int i = 1; i < 10; ++i) {
-    absl::FixedArray<int, 5> a1(i);
-    std::iota(a1.begin(), a1.end(), 0);
-    absl::FixedArray<int, 5> a2(a1.begin(), a1.end());
-    --a2[i / 2];
-
-    EXPECT_FALSE(a1 == a2);
-    EXPECT_TRUE(a1 != a2);
-    EXPECT_FALSE(a2 == a1);
-    EXPECT_TRUE(a2 != a1);
-    EXPECT_FALSE(a1 < a2);
-    EXPECT_TRUE(a1 > a2);
-    EXPECT_TRUE(a2 < a1);
-    EXPECT_FALSE(a2 > a1);
-    EXPECT_FALSE(a1 <= a2);
-    EXPECT_TRUE(a1 >= a2);
-    EXPECT_TRUE(a2 <= a1);
-    EXPECT_FALSE(a2 >= a1);
-  }
-}
-
-template <int stack_elements>
-static void TestArray(int n) {
-  SCOPED_TRACE(n);
-  SCOPED_TRACE(stack_elements);
-  ConstructionTester::constructions = 0;
-  ConstructionTester::destructions = 0;
-  {
-    absl::FixedArray<ConstructionTester, stack_elements> array(n);
-
-    EXPECT_THAT(array.size(), n);
-    EXPECT_THAT(array.memsize(), sizeof(ConstructionTester) * n);
-    EXPECT_THAT(array.begin() + n, array.end());
-
-    // Check that all elements were constructed
-    for (int i = 0; i < n; i++) {
-      array[i].CheckConstructed();
-    }
-    // Check that no other elements were constructed
-    EXPECT_THAT(ConstructionTester::constructions, n);
-
-    // Test operator[]
-    for (int i = 0; i < n; i++) {
-      array[i].set(i);
-    }
-    for (int i = 0; i < n; i++) {
-      EXPECT_THAT(array[i].get(), i);
-      EXPECT_THAT(array.data()[i].get(), i);
-    }
-
-    // Test data()
-    for (int i = 0; i < n; i++) {
-      array.data()[i].set(i + 1);
-    }
-    for (int i = 0; i < n; i++) {
-      EXPECT_THAT(array[i].get(), i + 1);
-      EXPECT_THAT(array.data()[i].get(), i + 1);
-    }
-  }  // Close scope containing 'array'.
-
-  // Check that all constructed elements were destructed.
-  EXPECT_EQ(ConstructionTester::constructions,
-            ConstructionTester::destructions);
-}
-
-template <int elements_per_inner_array, int inline_elements>
-static void TestArrayOfArrays(int n) {
-  SCOPED_TRACE(n);
-  SCOPED_TRACE(inline_elements);
-  SCOPED_TRACE(elements_per_inner_array);
-  ConstructionTester::constructions = 0;
-  ConstructionTester::destructions = 0;
-  {
-    using InnerArray = ConstructionTester[elements_per_inner_array];
-    // Heap-allocate the FixedArray to avoid blowing the stack frame.
-    auto array_ptr =
-        absl::make_unique<absl::FixedArray<InnerArray, inline_elements>>(n);
-    auto& array = *array_ptr;
-
-    ASSERT_EQ(array.size(), n);
-    ASSERT_EQ(array.memsize(),
-              sizeof(ConstructionTester) * elements_per_inner_array * n);
-    ASSERT_EQ(array.begin() + n, array.end());
-
-    // Check that all elements were constructed
-    for (int i = 0; i < n; i++) {
-      for (int j = 0; j < elements_per_inner_array; j++) {
-        (array[i])[j].CheckConstructed();
-      }
-    }
-    // Check that no other elements were constructed
-    ASSERT_EQ(ConstructionTester::constructions, n * elements_per_inner_array);
-
-    // Test operator[]
-    for (int i = 0; i < n; i++) {
-      for (int j = 0; j < elements_per_inner_array; j++) {
-        (array[i])[j].set(i * elements_per_inner_array + j);
-      }
-    }
-    for (int i = 0; i < n; i++) {
-      for (int j = 0; j < elements_per_inner_array; j++) {
-        ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j);
-        ASSERT_EQ((array.data()[i])[j].get(), i * elements_per_inner_array + j);
-      }
-    }
-
-    // Test data()
-    for (int i = 0; i < n; i++) {
-      for (int j = 0; j < elements_per_inner_array; j++) {
-        (array.data()[i])[j].set((i + 1) * elements_per_inner_array + j);
-      }
-    }
-    for (int i = 0; i < n; i++) {
-      for (int j = 0; j < elements_per_inner_array; j++) {
-        ASSERT_EQ((array[i])[j].get(), (i + 1) * elements_per_inner_array + j);
-        ASSERT_EQ((array.data()[i])[j].get(),
-                  (i + 1) * elements_per_inner_array + j);
-      }
-    }
-  }  // Close scope containing 'array'.
-
-  // Check that all constructed elements were destructed.
-  EXPECT_EQ(ConstructionTester::constructions,
-            ConstructionTester::destructions);
-}
-
-TEST(IteratorConstructorTest, NonInline) {
-  int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
-  absl::FixedArray<int, ABSL_ARRAYSIZE(kInput) - 1> const fixed(
-      kInput, kInput + ABSL_ARRAYSIZE(kInput));
-  ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size());
-  for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) {
-    ASSERT_EQ(kInput[i], fixed[i]);
-  }
-}
-
-TEST(IteratorConstructorTest, Inline) {
-  int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
-  absl::FixedArray<int, ABSL_ARRAYSIZE(kInput)> const fixed(
-      kInput, kInput + ABSL_ARRAYSIZE(kInput));
-  ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size());
-  for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) {
-    ASSERT_EQ(kInput[i], fixed[i]);
-  }
-}
-
-TEST(IteratorConstructorTest, NonPod) {
-  char const* kInput[] = {"red",  "orange", "yellow", "green",
-                          "blue", "indigo", "violet"};
-  absl::FixedArray<std::string> const fixed(kInput,
-                                            kInput + ABSL_ARRAYSIZE(kInput));
-  ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size());
-  for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) {
-    ASSERT_EQ(kInput[i], fixed[i]);
-  }
-}
-
-TEST(IteratorConstructorTest, FromEmptyVector) {
-  std::vector<int> const empty;
-  absl::FixedArray<int> const fixed(empty.begin(), empty.end());
-  EXPECT_EQ(0, fixed.size());
-  EXPECT_EQ(empty.size(), fixed.size());
-}
-
-TEST(IteratorConstructorTest, FromNonEmptyVector) {
-  int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
-  std::vector<int> const items(kInput, kInput + ABSL_ARRAYSIZE(kInput));
-  absl::FixedArray<int> const fixed(items.begin(), items.end());
-  ASSERT_EQ(items.size(), fixed.size());
-  for (size_t i = 0; i < items.size(); ++i) {
-    ASSERT_EQ(items[i], fixed[i]);
-  }
-}
-
-TEST(IteratorConstructorTest, FromBidirectionalIteratorRange) {
-  int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
-  std::list<int> const items(kInput, kInput + ABSL_ARRAYSIZE(kInput));
-  absl::FixedArray<int> const fixed(items.begin(), items.end());
-  EXPECT_THAT(fixed, testing::ElementsAreArray(kInput));
-}
-
-TEST(InitListConstructorTest, InitListConstruction) {
-  absl::FixedArray<int> fixed = {1, 2, 3};
-  EXPECT_THAT(fixed, testing::ElementsAreArray({1, 2, 3}));
-}
-
-TEST(FillConstructorTest, NonEmptyArrays) {
-  absl::FixedArray<int> stack_array(4, 1);
-  EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
-
-  absl::FixedArray<int, 0> heap_array(4, 1);
-  EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
-}
-
-TEST(FillConstructorTest, EmptyArray) {
-  absl::FixedArray<int> empty_fill(0, 1);
-  absl::FixedArray<int> empty_size(0);
-  EXPECT_EQ(empty_fill, empty_size);
-}
-
-TEST(FillConstructorTest, NotTriviallyCopyable) {
-  std::string str = "abcd";
-  absl::FixedArray<std::string> strings = {str, str, str, str};
-
-  absl::FixedArray<std::string> array(4, str);
-  EXPECT_EQ(array, strings);
-}
-
-TEST(FillConstructorTest, Disambiguation) {
-  absl::FixedArray<size_t> a(1, 2);
-  EXPECT_THAT(a, testing::ElementsAre(2));
-}
-
-TEST(FixedArrayTest, ManySizedArrays) {
-  std::vector<int> sizes;
-  for (int i = 1; i < 100; i++) sizes.push_back(i);
-  for (int i = 100; i <= 1000; i += 100) sizes.push_back(i);
-  for (int n : sizes) {
-    TestArray<0>(n);
-    TestArray<1>(n);
-    TestArray<64>(n);
-    TestArray<1000>(n);
-  }
-}
-
-TEST(FixedArrayTest, ManySizedArraysOfArraysOf1) {
-  for (int n = 1; n < 1000; n++) {
-    ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 0>(n)));
-    ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1>(n)));
-    ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 64>(n)));
-    ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1000>(n)));
-  }
-}
-
-TEST(FixedArrayTest, ManySizedArraysOfArraysOf2) {
-  for (int n = 1; n < 1000; n++) {
-    TestArrayOfArrays<2, 0>(n);
-    TestArrayOfArrays<2, 1>(n);
-    TestArrayOfArrays<2, 64>(n);
-    TestArrayOfArrays<2, 1000>(n);
-  }
-}
-
-// If value_type is put inside of a struct container,
-// we might evoke this error in a hardened build unless data() is carefully
-// written, so check on that.
-//     error: call to int __builtin___sprintf_chk(etc...)
-//     will always overflow destination buffer [-Werror]
-TEST(FixedArrayTest, AvoidParanoidDiagnostics) {
-  absl::FixedArray<char, 32> buf(32);
-  sprintf(buf.data(), "foo");  // NOLINT(runtime/printf)
-}
-
-TEST(FixedArrayTest, TooBigInlinedSpace) {
-  struct TooBig {
-    char c[1 << 20];
-  };  // too big for even one on the stack
-
-  // Simulate the data members of absl::FixedArray, a pointer and a size_t.
-  struct Data {
-    TooBig* p;
-    size_t size;
-  };
-
-  // Make sure TooBig objects are not inlined for 0 or default size.
-  static_assert(sizeof(absl::FixedArray<TooBig, 0>) == sizeof(Data),
-                "0-sized absl::FixedArray should have same size as Data.");
-  static_assert(alignof(absl::FixedArray<TooBig, 0>) == alignof(Data),
-                "0-sized absl::FixedArray should have same alignment as Data.");
-  static_assert(sizeof(absl::FixedArray<TooBig>) == sizeof(Data),
-                "default-sized absl::FixedArray should have same size as Data");
-  static_assert(
-      alignof(absl::FixedArray<TooBig>) == alignof(Data),
-      "default-sized absl::FixedArray should have same alignment as Data.");
-}
-
-// PickyDelete EXPECTs its class-scope deallocation funcs are unused.
-struct PickyDelete {
-  PickyDelete() {}
-  ~PickyDelete() {}
-  void operator delete(void* p) {
-    EXPECT_TRUE(false) << __FUNCTION__;
-    ::operator delete(p);
-  }
-  void operator delete[](void* p) {
-    EXPECT_TRUE(false) << __FUNCTION__;
-    ::operator delete[](p);
-  }
-};
-
-TEST(FixedArrayTest, UsesGlobalAlloc) { absl::FixedArray<PickyDelete, 0> a(5); }
-
-TEST(FixedArrayTest, Data) {
-  static const int kInput[] = {2, 3, 5, 7, 11, 13, 17};
-  absl::FixedArray<int> fa(std::begin(kInput), std::end(kInput));
-  EXPECT_EQ(fa.data(), &*fa.begin());
-  EXPECT_EQ(fa.data(), &fa[0]);
-
-  const absl::FixedArray<int>& cfa = fa;
-  EXPECT_EQ(cfa.data(), &*cfa.begin());
-  EXPECT_EQ(cfa.data(), &cfa[0]);
-}
-
-TEST(FixedArrayTest, Empty) {
-  absl::FixedArray<int> empty(0);
-  absl::FixedArray<int> inline_filled(1);
-  absl::FixedArray<int, 0> heap_filled(1);
-  EXPECT_TRUE(empty.empty());
-  EXPECT_FALSE(inline_filled.empty());
-  EXPECT_FALSE(heap_filled.empty());
-}
-
-TEST(FixedArrayTest, FrontAndBack) {
-  absl::FixedArray<int, 3 * sizeof(int)> inlined = {1, 2, 3};
-  EXPECT_EQ(inlined.front(), 1);
-  EXPECT_EQ(inlined.back(), 3);
-
-  absl::FixedArray<int, 0> allocated = {1, 2, 3};
-  EXPECT_EQ(allocated.front(), 1);
-  EXPECT_EQ(allocated.back(), 3);
-
-  absl::FixedArray<int> one_element = {1};
-  EXPECT_EQ(one_element.front(), one_element.back());
-}
-
-TEST(FixedArrayTest, ReverseIteratorInlined) {
-  absl::FixedArray<int, 5 * sizeof(int)> a = {0, 1, 2, 3, 4};
-
-  int counter = 5;
-  for (absl::FixedArray<int>::reverse_iterator iter = a.rbegin();
-       iter != a.rend(); ++iter) {
-    counter--;
-    EXPECT_EQ(counter, *iter);
-  }
-  EXPECT_EQ(counter, 0);
-
-  counter = 5;
-  for (absl::FixedArray<int>::const_reverse_iterator iter = a.rbegin();
-       iter != a.rend(); ++iter) {
-    counter--;
-    EXPECT_EQ(counter, *iter);
-  }
-  EXPECT_EQ(counter, 0);
-
-  counter = 5;
-  for (auto iter = a.crbegin(); iter != a.crend(); ++iter) {
-    counter--;
-    EXPECT_EQ(counter, *iter);
-  }
-  EXPECT_EQ(counter, 0);
-}
-
-TEST(FixedArrayTest, ReverseIteratorAllocated) {
-  absl::FixedArray<int, 0> a = {0, 1, 2, 3, 4};
-
-  int counter = 5;
-  for (absl::FixedArray<int>::reverse_iterator iter = a.rbegin();
-       iter != a.rend(); ++iter) {
-    counter--;
-    EXPECT_EQ(counter, *iter);
-  }
-  EXPECT_EQ(counter, 0);
-
-  counter = 5;
-  for (absl::FixedArray<int>::const_reverse_iterator iter = a.rbegin();
-       iter != a.rend(); ++iter) {
-    counter--;
-    EXPECT_EQ(counter, *iter);
-  }
-  EXPECT_EQ(counter, 0);
-
-  counter = 5;
-  for (auto iter = a.crbegin(); iter != a.crend(); ++iter) {
-    counter--;
-    EXPECT_EQ(counter, *iter);
-  }
-  EXPECT_EQ(counter, 0);
-}
-
-TEST(FixedArrayTest, Fill) {
-  absl::FixedArray<int, 5 * sizeof(int)> inlined(5);
-  int fill_val = 42;
-  inlined.fill(fill_val);
-  for (int i : inlined) EXPECT_EQ(i, fill_val);
-
-  absl::FixedArray<int, 0> allocated(5);
-  allocated.fill(fill_val);
-  for (int i : allocated) EXPECT_EQ(i, fill_val);
-
-  // It doesn't do anything, just make sure this compiles.
-  absl::FixedArray<int> empty(0);
-  empty.fill(fill_val);
-}
-
-#ifndef __GNUC__
-TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) {
-  using T = char;
-  constexpr auto capacity = 10;
-  using FixedArrType = absl::FixedArray<T, capacity>;
-  constexpr auto scrubbed_bits = 0x95;
-  constexpr auto length = capacity / 2;
-
-  alignas(FixedArrType) unsigned char buff[sizeof(FixedArrType)];
-  std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrType));
-
-  FixedArrType* arr =
-      ::new (static_cast<void*>(std::addressof(buff))) FixedArrType(length);
-  EXPECT_THAT(*arr, testing::Each(scrubbed_bits));
-  arr->~FixedArrType();
-}
-#endif  // __GNUC__
-
-TEST(AllocatorSupportTest, CountInlineAllocations) {
-  constexpr size_t inlined_size = 4;
-  using Alloc = absl::container_internal::CountingAllocator<int>;
-  using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
-
-  int64_t allocated = 0;
-  int64_t active_instances = 0;
-
-  {
-    const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
-
-    Alloc alloc(&allocated, &active_instances);
-
-    AllocFxdArr arr(ia, ia + inlined_size, alloc);
-    static_cast<void>(arr);
-  }
-
-  EXPECT_EQ(allocated, 0);
-  EXPECT_EQ(active_instances, 0);
-}
-
-TEST(AllocatorSupportTest, CountOutoflineAllocations) {
-  constexpr size_t inlined_size = 4;
-  using Alloc = absl::container_internal::CountingAllocator<int>;
-  using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
-
-  int64_t allocated = 0;
-  int64_t active_instances = 0;
-
-  {
-    const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
-    Alloc alloc(&allocated, &active_instances);
-
-    AllocFxdArr arr(ia, ia + ABSL_ARRAYSIZE(ia), alloc);
-
-    EXPECT_EQ(allocated, arr.size() * sizeof(int));
-    static_cast<void>(arr);
-  }
-
-  EXPECT_EQ(active_instances, 0);
-}
-
-TEST(AllocatorSupportTest, CountCopyInlineAllocations) {
-  constexpr size_t inlined_size = 4;
-  using Alloc = absl::container_internal::CountingAllocator<int>;
-  using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
-
-  int64_t allocated1 = 0;
-  int64_t allocated2 = 0;
-  int64_t active_instances = 0;
-  Alloc alloc(&allocated1, &active_instances);
-  Alloc alloc2(&allocated2, &active_instances);
-
-  {
-    int initial_value = 1;
-
-    AllocFxdArr arr1(inlined_size / 2, initial_value, alloc);
-
-    EXPECT_EQ(allocated1, 0);
-
-    AllocFxdArr arr2(arr1, alloc2);
-
-    EXPECT_EQ(allocated2, 0);
-    static_cast<void>(arr1);
-    static_cast<void>(arr2);
-  }
-
-  EXPECT_EQ(active_instances, 0);
-}
-
-TEST(AllocatorSupportTest, CountCopyOutoflineAllocations) {
-  constexpr size_t inlined_size = 4;
-  using Alloc = absl::container_internal::CountingAllocator<int>;
-  using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
-
-  int64_t allocated1 = 0;
-  int64_t allocated2 = 0;
-  int64_t active_instances = 0;
-  Alloc alloc(&allocated1, &active_instances);
-  Alloc alloc2(&allocated2, &active_instances);
-
-  {
-    int initial_value = 1;
-
-    AllocFxdArr arr1(inlined_size * 2, initial_value, alloc);
-
-    EXPECT_EQ(allocated1, arr1.size() * sizeof(int));
-
-    AllocFxdArr arr2(arr1, alloc2);
-
-    EXPECT_EQ(allocated2, inlined_size * 2 * sizeof(int));
-    static_cast<void>(arr1);
-    static_cast<void>(arr2);
-  }
-
-  EXPECT_EQ(active_instances, 0);
-}
-
-TEST(AllocatorSupportTest, SizeValAllocConstructor) {
-  using testing::AllOf;
-  using testing::Each;
-  using testing::SizeIs;
-
-  constexpr size_t inlined_size = 4;
-  using Alloc = absl::container_internal::CountingAllocator<int>;
-  using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>;
-
-  {
-    auto len = inlined_size / 2;
-    auto val = 0;
-    int64_t allocated = 0;
-    AllocFxdArr arr(len, val, Alloc(&allocated));
-
-    EXPECT_EQ(allocated, 0);
-    EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
-  }
-
-  {
-    auto len = inlined_size * 2;
-    auto val = 0;
-    int64_t allocated = 0;
-    AllocFxdArr arr(len, val, Alloc(&allocated));
-
-    EXPECT_EQ(allocated, len * sizeof(int));
-    EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
-  }
-}
-
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-TEST(FixedArrayTest, AddressSanitizerAnnotations1) {
-  absl::FixedArray<int, 32> a(10);
-  int* raw = a.data();
-  raw[0] = 0;
-  raw[9] = 0;
-  EXPECT_DEATH_IF_SUPPORTED(raw[-2] = 0, "container-overflow");
-  EXPECT_DEATH_IF_SUPPORTED(raw[-1] = 0, "container-overflow");
-  EXPECT_DEATH_IF_SUPPORTED(raw[10] = 0, "container-overflow");
-  EXPECT_DEATH_IF_SUPPORTED(raw[31] = 0, "container-overflow");
-}
-
-TEST(FixedArrayTest, AddressSanitizerAnnotations2) {
-  absl::FixedArray<char, 17> a(12);
-  char* raw = a.data();
-  raw[0] = 0;
-  raw[11] = 0;
-  EXPECT_DEATH_IF_SUPPORTED(raw[-7] = 0, "container-overflow");
-  EXPECT_DEATH_IF_SUPPORTED(raw[-1] = 0, "container-overflow");
-  EXPECT_DEATH_IF_SUPPORTED(raw[12] = 0, "container-overflow");
-  EXPECT_DEATH_IF_SUPPORTED(raw[17] = 0, "container-overflow");
-}
-
-TEST(FixedArrayTest, AddressSanitizerAnnotations3) {
-  absl::FixedArray<uint64_t, 20> a(20);
-  uint64_t* raw = a.data();
-  raw[0] = 0;
-  raw[19] = 0;
-  EXPECT_DEATH_IF_SUPPORTED(raw[-1] = 0, "container-overflow");
-  EXPECT_DEATH_IF_SUPPORTED(raw[20] = 0, "container-overflow");
-}
-
-TEST(FixedArrayTest, AddressSanitizerAnnotations4) {
-  absl::FixedArray<ThreeInts> a(10);
-  ThreeInts* raw = a.data();
-  raw[0] = ThreeInts();
-  raw[9] = ThreeInts();
-  // Note: raw[-1] is pointing to 12 bytes before the container range. However,
-  // there is only a 8-byte red zone before the container range, so we only
-  // access the last 4 bytes of the struct to make sure it stays within the red
-  // zone.
-  EXPECT_DEATH_IF_SUPPORTED(raw[-1].z_ = 0, "container-overflow");
-  EXPECT_DEATH_IF_SUPPORTED(raw[10] = ThreeInts(), "container-overflow");
-  // The actual size of storage is kDefaultBytes=256, 21*12 = 252,
-  // so reading raw[21] should still trigger the correct warning.
-  EXPECT_DEATH_IF_SUPPORTED(raw[21] = ThreeInts(), "container-overflow");
-}
-#endif  // ABSL_HAVE_ADDRESS_SANITIZER
-
-TEST(FixedArrayTest, AbslHashValueWorks) {
-  using V = absl::FixedArray<int>;
-  std::vector<V> cases;
-
-  // Generate a variety of vectors some of these are small enough for the inline
-  // space but are stored out of line.
-  for (int i = 0; i < 10; ++i) {
-    V v(i);
-    for (int j = 0; j < i; ++j) {
-      v[j] = j;
-    }
-    cases.push_back(v);
-  }
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/container/flat_hash_map.h b/third_party/abseil_cpp/absl/container/flat_hash_map.h
deleted file mode 100644
index 74def0df0e..0000000000
--- a/third_party/abseil_cpp/absl/container/flat_hash_map.h
+++ /dev/null
@@ -1,606 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: flat_hash_map.h
-// -----------------------------------------------------------------------------
-//
-// An `absl::flat_hash_map<K, V>` is an unordered associative container of
-// unique keys and associated values designed to be a more efficient replacement
-// for `std::unordered_map`. Like `unordered_map`, search, insertion, and
-// deletion of map elements can be done as an `O(1)` operation. However,
-// `flat_hash_map` (and other unordered associative containers known as the
-// collection of Abseil "Swiss tables") contain other optimizations that result
-// in both memory and computation advantages.
-//
-// In most cases, your default choice for a hash map should be a map of type
-// `flat_hash_map`.
-
-#ifndef ABSL_CONTAINER_FLAT_HASH_MAP_H_
-#define ABSL_CONTAINER_FLAT_HASH_MAP_H_
-
-#include <cstddef>
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#include "absl/algorithm/container.h"
-#include "absl/container/internal/container_memory.h"
-#include "absl/container/internal/hash_function_defaults.h"  // IWYU pragma: export
-#include "absl/container/internal/raw_hash_map.h"  // IWYU pragma: export
-#include "absl/memory/memory.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-template <class K, class V>
-struct FlatHashMapPolicy;
-}  // namespace container_internal
-
-// -----------------------------------------------------------------------------
-// absl::flat_hash_map
-// -----------------------------------------------------------------------------
-//
-// An `absl::flat_hash_map<K, V>` is an unordered associative container which
-// has been optimized for both speed and memory footprint in most common use
-// cases. Its interface is similar to that of `std::unordered_map<K, V>` with
-// the following notable differences:
-//
-// * Requires keys that are CopyConstructible
-// * Requires values that are MoveConstructible
-// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
-//   `insert()`, provided that the map is provided a compatible heterogeneous
-//   hashing function and equality operator.
-// * Invalidates any references and pointers to elements within the table after
-//   `rehash()`.
-// * Contains a `capacity()` member function indicating the number of element
-//   slots (open, deleted, and empty) within the hash map.
-// * Returns `void` from the `erase(iterator)` overload.
-//
-// By default, `flat_hash_map` uses the `absl::Hash` hashing framework.
-// All fundamental and Abseil types that support the `absl::Hash` framework have
-// a compatible equality operator for comparing insertions into `flat_hash_map`.
-// If your type is not yet supported by the `absl::Hash` framework, see
-// absl/hash/hash.h for information on extending Abseil hashing to user-defined
-// types.
-//
-// NOTE: A `flat_hash_map` stores its value types directly inside its
-// implementation array to avoid memory indirection. Because a `flat_hash_map`
-// is designed to move data when rehashed, map values will not retain pointer
-// stability. If you require pointer stability, or if your values are large,
-// consider using `absl::flat_hash_map<Key, std::unique_ptr<Value>>` instead.
-// If your types are not moveable or you require pointer stability for keys,
-// consider `absl::node_hash_map`.
-//
-// Example:
-//
-//   // Create a flat hash map of three strings (that map to strings)
-//   absl::flat_hash_map<std::string, std::string> ducks =
-//     {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}};
-//
-//  // Insert a new element into the flat hash map
-//  ducks.insert({"d", "donald"});
-//
-//  // Force a rehash of the flat hash map
-//  ducks.rehash(0);
-//
-//  // Find the element with the key "b"
-//  std::string search_key = "b";
-//  auto result = ducks.find(search_key);
-//  if (result != ducks.end()) {
-//    std::cout << "Result: " << result->second << std::endl;
-//  }
-template <class K, class V,
-          class Hash = absl::container_internal::hash_default_hash<K>,
-          class Eq = absl::container_internal::hash_default_eq<K>,
-          class Allocator = std::allocator<std::pair<const K, V>>>
-class flat_hash_map : public absl::container_internal::raw_hash_map<
-                          absl::container_internal::FlatHashMapPolicy<K, V>,
-                          Hash, Eq, Allocator> {
-  using Base = typename flat_hash_map::raw_hash_map;
-
- public:
-  // Constructors and Assignment Operators
-  //
-  // A flat_hash_map supports the same overload set as `std::unordered_map`
-  // for construction and assignment:
-  //
-  // *  Default constructor
-  //
-  //    // No allocation for the table's elements is made.
-  //    absl::flat_hash_map<int, std::string> map1;
-  //
-  // * Initializer List constructor
-  //
-  //   absl::flat_hash_map<int, std::string> map2 =
-  //       {{1, "huey"}, {2, "dewey"}, {3, "louie"},};
-  //
-  // * Copy constructor
-  //
-  //   absl::flat_hash_map<int, std::string> map3(map2);
-  //
-  // * Copy assignment operator
-  //
-  //  // Hash functor and Comparator are copied as well
-  //  absl::flat_hash_map<int, std::string> map4;
-  //  map4 = map3;
-  //
-  // * Move constructor
-  //
-  //   // Move is guaranteed efficient
-  //   absl::flat_hash_map<int, std::string> map5(std::move(map4));
-  //
-  // * Move assignment operator
-  //
-  //   // May be efficient if allocators are compatible
-  //   absl::flat_hash_map<int, std::string> map6;
-  //   map6 = std::move(map5);
-  //
-  // * Range constructor
-  //
-  //   std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}};
-  //   absl::flat_hash_map<int, std::string> map7(v.begin(), v.end());
-  flat_hash_map() {}
-  using Base::Base;
-
-  // flat_hash_map::begin()
-  //
-  // Returns an iterator to the beginning of the `flat_hash_map`.
-  using Base::begin;
-
-  // flat_hash_map::cbegin()
-  //
-  // Returns a const iterator to the beginning of the `flat_hash_map`.
-  using Base::cbegin;
-
-  // flat_hash_map::cend()
-  //
-  // Returns a const iterator to the end of the `flat_hash_map`.
-  using Base::cend;
-
-  // flat_hash_map::end()
-  //
-  // Returns an iterator to the end of the `flat_hash_map`.
-  using Base::end;
-
-  // flat_hash_map::capacity()
-  //
-  // Returns the number of element slots (assigned, deleted, and empty)
-  // available within the `flat_hash_map`.
-  //
-  // NOTE: this member function is particular to `absl::flat_hash_map` and is
-  // not provided in the `std::unordered_map` API.
-  using Base::capacity;
-
-  // flat_hash_map::empty()
-  //
-  // Returns whether or not the `flat_hash_map` is empty.
-  using Base::empty;
-
-  // flat_hash_map::max_size()
-  //
-  // Returns the largest theoretical possible number of elements within a
-  // `flat_hash_map` under current memory constraints. This value can be thought
-  // of the largest value of `std::distance(begin(), end())` for a
-  // `flat_hash_map<K, V>`.
-  using Base::max_size;
-
-  // flat_hash_map::size()
-  //
-  // Returns the number of elements currently within the `flat_hash_map`.
-  using Base::size;
-
-  // flat_hash_map::clear()
-  //
-  // Removes all elements from the `flat_hash_map`. Invalidates any references,
-  // pointers, or iterators referring to contained elements.
-  //
-  // NOTE: this operation may shrink the underlying buffer. To avoid shrinking
-  // the underlying buffer call `erase(begin(), end())`.
-  using Base::clear;
-
-  // flat_hash_map::erase()
-  //
-  // Erases elements within the `flat_hash_map`. Erasing does not trigger a
-  // rehash. Overloads are listed below.
-  //
-  // void erase(const_iterator pos):
-  //
-  //   Erases the element at `position` of the `flat_hash_map`, returning
-  //   `void`.
-  //
-  //   NOTE: returning `void` in this case is different than that of STL
-  //   containers in general and `std::unordered_map` in particular (which
-  //   return an iterator to the element following the erased element). If that
-  //   iterator is needed, simply post increment the iterator:
-  //
-  //     map.erase(it++);
-  //
-  // iterator erase(const_iterator first, const_iterator last):
-  //
-  //   Erases the elements in the open interval [`first`, `last`), returning an
-  //   iterator pointing to `last`.
-  //
-  // size_type erase(const key_type& key):
-  //
-  //   Erases the element with the matching key, if it exists, returning the
-  //   number of elements erased (0 or 1).
-  using Base::erase;
-
-  // flat_hash_map::insert()
-  //
-  // Inserts an element of the specified value into the `flat_hash_map`,
-  // returning an iterator pointing to the newly inserted element, provided that
-  // an element with the given key does not already exist. If rehashing occurs
-  // due to the insertion, all iterators are invalidated. Overloads are listed
-  // below.
-  //
-  // std::pair<iterator,bool> insert(const init_type& value):
-  //
-  //   Inserts a value into the `flat_hash_map`. Returns a pair consisting of an
-  //   iterator to the inserted element (or to the element that prevented the
-  //   insertion) and a bool denoting whether the insertion took place.
-  //
-  // std::pair<iterator,bool> insert(T&& value):
-  // std::pair<iterator,bool> insert(init_type&& value):
-  //
-  //   Inserts a moveable value into the `flat_hash_map`. Returns a pair
-  //   consisting of an iterator to the inserted element (or to the element that
-  //   prevented the insertion) and a bool denoting whether the insertion took
-  //   place.
-  //
-  // iterator insert(const_iterator hint, const init_type& value):
-  // iterator insert(const_iterator hint, T&& value):
-  // iterator insert(const_iterator hint, init_type&& value);
-  //
-  //   Inserts a value, using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search. Returns an iterator to the
-  //   inserted element, or to the existing element that prevented the
-  //   insertion.
-  //
-  // void insert(InputIterator first, InputIterator last):
-  //
-  //   Inserts a range of values [`first`, `last`).
-  //
-  //   NOTE: Although the STL does not specify which element may be inserted if
-  //   multiple keys compare equivalently, for `flat_hash_map` we guarantee the
-  //   first match is inserted.
-  //
-  // void insert(std::initializer_list<init_type> ilist):
-  //
-  //   Inserts the elements within the initializer list `ilist`.
-  //
-  //   NOTE: Although the STL does not specify which element may be inserted if
-  //   multiple keys compare equivalently within the initializer list, for
-  //   `flat_hash_map` we guarantee the first match is inserted.
-  using Base::insert;
-
-  // flat_hash_map::insert_or_assign()
-  //
-  // Inserts an element of the specified value into the `flat_hash_map` provided
-  // that a value with the given key does not already exist, or replaces it with
-  // the element value if a key for that value already exists, returning an
-  // iterator pointing to the newly inserted element.  If rehashing occurs due
-  // to the insertion, all existing iterators are invalidated. Overloads are
-  // listed below.
-  //
-  // pair<iterator, bool> insert_or_assign(const init_type& k, T&& obj):
-  // pair<iterator, bool> insert_or_assign(init_type&& k, T&& obj):
-  //
-  //   Inserts/Assigns (or moves) the element of the specified key into the
-  //   `flat_hash_map`.
-  //
-  // iterator insert_or_assign(const_iterator hint,
-  //                           const init_type& k, T&& obj):
-  // iterator insert_or_assign(const_iterator hint, init_type&& k, T&& obj):
-  //
-  //   Inserts/Assigns (or moves) the element of the specified key into the
-  //   `flat_hash_map` using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search.
-  using Base::insert_or_assign;
-
-  // flat_hash_map::emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `flat_hash_map`, provided that no element with the given key
-  // already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately. Prefer `try_emplace()` unless your key is not
-  // copyable or moveable.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  using Base::emplace;
-
-  // flat_hash_map::emplace_hint()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `flat_hash_map`, using the position of `hint` as a non-binding
-  // suggestion for where to begin the insertion search, and only inserts
-  // provided that no element with the given key already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately. Prefer `try_emplace()` unless your key is not
-  // copyable or moveable.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  using Base::emplace_hint;
-
-  // flat_hash_map::try_emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `flat_hash_map`, provided that no element with the given key
-  // already exists. Unlike `emplace()`, if an element with the given key
-  // already exists, we guarantee that no element is constructed.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  // Overloads are listed below.
-  //
-  //   pair<iterator, bool> try_emplace(const key_type& k, Args&&... args):
-  //   pair<iterator, bool> try_emplace(key_type&& k, Args&&... args):
-  //
-  // Inserts (via copy or move) the element of the specified key into the
-  // `flat_hash_map`.
-  //
-  //   iterator try_emplace(const_iterator hint,
-  //                        const init_type& k, Args&&... args):
-  //   iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args):
-  //
-  // Inserts (via copy or move) the element of the specified key into the
-  // `flat_hash_map` using the position of `hint` as a non-binding suggestion
-  // for where to begin the insertion search.
-  //
-  // All `try_emplace()` overloads make the same guarantees regarding rvalue
-  // arguments as `std::unordered_map::try_emplace()`, namely that these
-  // functions will not move from rvalue arguments if insertions do not happen.
-  using Base::try_emplace;
-
-  // flat_hash_map::extract()
-  //
-  // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
-  //
-  // node_type extract(const_iterator position):
-  //
-  //   Extracts the key,value pair of the element at the indicated position and
-  //   returns a node handle owning that extracted data.
-  //
-  // node_type extract(const key_type& x):
-  //
-  //   Extracts the key,value pair of the element with a key matching the passed
-  //   key value and returns a node handle owning that extracted data. If the
-  //   `flat_hash_map` does not contain an element with a matching key, this
-  //   function returns an empty node handle.
-  //
-  // NOTE: when compiled in an earlier version of C++ than C++17,
-  // `node_type::key()` returns a const reference to the key instead of a
-  // mutable reference. We cannot safely return a mutable reference without
-  // std::launder (which is not available before C++17).
-  using Base::extract;
-
-  // flat_hash_map::merge()
-  //
-  // Extracts elements from a given `source` flat hash map into this
-  // `flat_hash_map`. If the destination `flat_hash_map` already contains an
-  // element with an equivalent key, that element is not extracted.
-  using Base::merge;
-
-  // flat_hash_map::swap(flat_hash_map& other)
-  //
-  // Exchanges the contents of this `flat_hash_map` with those of the `other`
-  // flat hash map, avoiding invocation of any move, copy, or swap operations on
-  // individual elements.
-  //
-  // All iterators and references on the `flat_hash_map` remain valid, excepting
-  // for the past-the-end iterator, which is invalidated.
-  //
-  // `swap()` requires that the flat hash map's hashing and key equivalence
-  // functions be Swappable, and are exchanged using unqualified calls to
-  // non-member `swap()`. If the map's allocator has
-  // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
-  // set to `true`, the allocators are also exchanged using an unqualified call
-  // to non-member `swap()`; otherwise, the allocators are not swapped.
-  using Base::swap;
-
-  // flat_hash_map::rehash(count)
-  //
-  // Rehashes the `flat_hash_map`, setting the number of slots to be at least
-  // the passed value. If the new number of slots increases the load factor more
-  // than the current maximum load factor
-  // (`count` < `size()` / `max_load_factor()`), then the new number of slots
-  // will be at least `size()` / `max_load_factor()`.
-  //
-  // To force a rehash, pass rehash(0).
-  //
-  // NOTE: unlike behavior in `std::unordered_map`, references are also
-  // invalidated upon a `rehash()`.
-  using Base::rehash;
-
-  // flat_hash_map::reserve(count)
-  //
-  // Sets the number of slots in the `flat_hash_map` to the number needed to
-  // accommodate at least `count` total elements without exceeding the current
-  // maximum load factor, and may rehash the container if needed.
-  using Base::reserve;
-
-  // flat_hash_map::at()
-  //
-  // Returns a reference to the mapped value of the element with key equivalent
-  // to the passed key.
-  using Base::at;
-
-  // flat_hash_map::contains()
-  //
-  // Determines whether an element with a key comparing equal to the given `key`
-  // exists within the `flat_hash_map`, returning `true` if so or `false`
-  // otherwise.
-  using Base::contains;
-
-  // flat_hash_map::count(const Key& key) const
-  //
-  // Returns the number of elements with a key comparing equal to the given
-  // `key` within the `flat_hash_map`. note that this function will return
-  // either `1` or `0` since duplicate keys are not allowed within a
-  // `flat_hash_map`.
-  using Base::count;
-
-  // flat_hash_map::equal_range()
-  //
-  // Returns a closed range [first, last], defined by a `std::pair` of two
-  // iterators, containing all elements with the passed key in the
-  // `flat_hash_map`.
-  using Base::equal_range;
-
-  // flat_hash_map::find()
-  //
-  // Finds an element with the passed `key` within the `flat_hash_map`.
-  using Base::find;
-
-  // flat_hash_map::operator[]()
-  //
-  // Returns a reference to the value mapped to the passed key within the
-  // `flat_hash_map`, performing an `insert()` if the key does not already
-  // exist.
-  //
-  // If an insertion occurs and results in a rehashing of the container, all
-  // iterators are invalidated. Otherwise iterators are not affected and
-  // references are not invalidated. Overloads are listed below.
-  //
-  // T& operator[](const Key& key):
-  //
-  //   Inserts an init_type object constructed in-place if the element with the
-  //   given key does not exist.
-  //
-  // T& operator[](Key&& key):
-  //
-  //   Inserts an init_type object constructed in-place provided that an element
-  //   with the given key does not exist.
-  using Base::operator[];
-
-  // flat_hash_map::bucket_count()
-  //
-  // Returns the number of "buckets" within the `flat_hash_map`. Note that
-  // because a flat hash map contains all elements within its internal storage,
-  // this value simply equals the current capacity of the `flat_hash_map`.
-  using Base::bucket_count;
-
-  // flat_hash_map::load_factor()
-  //
-  // Returns the current load factor of the `flat_hash_map` (the average number
-  // of slots occupied with a value within the hash map).
-  using Base::load_factor;
-
-  // flat_hash_map::max_load_factor()
-  //
-  // Manages the maximum load factor of the `flat_hash_map`. Overloads are
-  // listed below.
-  //
-  // float flat_hash_map::max_load_factor()
-  //
-  //   Returns the current maximum load factor of the `flat_hash_map`.
-  //
-  // void flat_hash_map::max_load_factor(float ml)
-  //
-  //   Sets the maximum load factor of the `flat_hash_map` to the passed value.
-  //
-  //   NOTE: This overload is provided only for API compatibility with the STL;
-  //   `flat_hash_map` will ignore any set load factor and manage its rehashing
-  //   internally as an implementation detail.
-  using Base::max_load_factor;
-
-  // flat_hash_map::get_allocator()
-  //
-  // Returns the allocator function associated with this `flat_hash_map`.
-  using Base::get_allocator;
-
-  // flat_hash_map::hash_function()
-  //
-  // Returns the hashing function used to hash the keys within this
-  // `flat_hash_map`.
-  using Base::hash_function;
-
-  // flat_hash_map::key_eq()
-  //
-  // Returns the function used for comparing keys equality.
-  using Base::key_eq;
-};
-
-// erase_if(flat_hash_map<>, Pred)
-//
-// Erases all elements that satisfy the predicate `pred` from the container `c`.
-template <typename K, typename V, typename H, typename E, typename A,
-          typename Predicate>
-void erase_if(flat_hash_map<K, V, H, E, A>& c, Predicate pred) {
-  container_internal::EraseIf(pred, &c);
-}
-
-namespace container_internal {
-
-template <class K, class V>
-struct FlatHashMapPolicy {
-  using slot_policy = container_internal::map_slot_policy<K, V>;
-  using slot_type = typename slot_policy::slot_type;
-  using key_type = K;
-  using mapped_type = V;
-  using init_type = std::pair</*non const*/ key_type, mapped_type>;
-
-  template <class Allocator, class... Args>
-  static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
-    slot_policy::construct(alloc, slot, std::forward<Args>(args)...);
-  }
-
-  template <class Allocator>
-  static void destroy(Allocator* alloc, slot_type* slot) {
-    slot_policy::destroy(alloc, slot);
-  }
-
-  template <class Allocator>
-  static void transfer(Allocator* alloc, slot_type* new_slot,
-                       slot_type* old_slot) {
-    slot_policy::transfer(alloc, new_slot, old_slot);
-  }
-
-  template <class F, class... Args>
-  static decltype(absl::container_internal::DecomposePair(
-      std::declval<F>(), std::declval<Args>()...))
-  apply(F&& f, Args&&... args) {
-    return absl::container_internal::DecomposePair(std::forward<F>(f),
-                                                   std::forward<Args>(args)...);
-  }
-
-  static size_t space_used(const slot_type*) { return 0; }
-
-  static std::pair<const K, V>& element(slot_type* slot) { return slot->value; }
-
-  static V& value(std::pair<const K, V>* kv) { return kv->second; }
-  static const V& value(const std::pair<const K, V>* kv) { return kv->second; }
-};
-
-}  // namespace container_internal
-
-namespace container_algorithm_internal {
-
-// Specialization of trait in absl/algorithm/container.h
-template <class Key, class T, class Hash, class KeyEqual, class Allocator>
-struct IsUnorderedContainer<
-    absl::flat_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
-
-}  // namespace container_algorithm_internal
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_FLAT_HASH_MAP_H_
diff --git a/third_party/abseil_cpp/absl/container/flat_hash_map_test.cc b/third_party/abseil_cpp/absl/container/flat_hash_map_test.cc
deleted file mode 100644
index 89ec60c916..0000000000
--- a/third_party/abseil_cpp/absl/container/flat_hash_map_test.cc
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/flat_hash_map.h"
-
-#include <memory>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/container/internal/hash_generator_testing.h"
-#include "absl/container/internal/unordered_map_constructor_test.h"
-#include "absl/container/internal/unordered_map_lookup_test.h"
-#include "absl/container/internal/unordered_map_members_test.h"
-#include "absl/container/internal/unordered_map_modifiers_test.h"
-#include "absl/types/any.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-using ::absl::container_internal::hash_internal::Enum;
-using ::absl::container_internal::hash_internal::EnumClass;
-using ::testing::_;
-using ::testing::IsEmpty;
-using ::testing::Pair;
-using ::testing::UnorderedElementsAre;
-
-// Check that absl::flat_hash_map works in a global constructor.
-struct BeforeMain {
-  BeforeMain() {
-    absl::flat_hash_map<int, int> x;
-    x.insert({1, 1});
-    ABSL_RAW_CHECK(x.find(0) == x.end(), "x should not contain 0");
-    auto it = x.find(1);
-    ABSL_RAW_CHECK(it != x.end(), "x should contain 1");
-    ABSL_RAW_CHECK(it->second, "1 should map to 1");
-  }
-};
-const BeforeMain before_main;
-
-template <class K, class V>
-using Map = flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual,
-                          Alloc<std::pair<const K, V>>>;
-
-static_assert(!std::is_standard_layout<NonStandardLayout>(), "");
-
-using MapTypes =
-    ::testing::Types<Map<int, int>, Map<std::string, int>,
-                     Map<Enum, std::string>, Map<EnumClass, int>,
-                     Map<int, NonStandardLayout>, Map<NonStandardLayout, int>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ConstructorTest, MapTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, LookupTest, MapTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, MembersTest, MapTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ModifiersTest, MapTypes);
-
-using UniquePtrMapTypes = ::testing::Types<Map<int, std::unique_ptr<int>>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, UniquePtrModifiersTest,
-                               UniquePtrMapTypes);
-
-TEST(FlatHashMap, StandardLayout) {
-  struct Int {
-    explicit Int(size_t value) : value(value) {}
-    Int() : value(0) { ADD_FAILURE(); }
-    Int(const Int& other) : value(other.value) { ADD_FAILURE(); }
-    Int(Int&&) = default;
-    bool operator==(const Int& other) const { return value == other.value; }
-    size_t value;
-  };
-  static_assert(std::is_standard_layout<Int>(), "");
-
-  struct Hash {
-    size_t operator()(const Int& obj) const { return obj.value; }
-  };
-
-  // Verify that neither the key nor the value get default-constructed or
-  // copy-constructed.
-  {
-    flat_hash_map<Int, Int, Hash> m;
-    m.try_emplace(Int(1), Int(2));
-    m.try_emplace(Int(3), Int(4));
-    m.erase(Int(1));
-    m.rehash(2 * m.bucket_count());
-  }
-  {
-    flat_hash_map<Int, Int, Hash> m;
-    m.try_emplace(Int(1), Int(2));
-    m.try_emplace(Int(3), Int(4));
-    m.erase(Int(1));
-    m.clear();
-  }
-}
-
-// gcc becomes unhappy if this is inside the method, so pull it out here.
-struct balast {};
-
-TEST(FlatHashMap, IteratesMsan) {
-  // Because SwissTable randomizes on pointer addresses, we keep old tables
-  // around to ensure we don't reuse old memory.
-  std::vector<absl::flat_hash_map<int, balast>> garbage;
-  for (int i = 0; i < 100; ++i) {
-    absl::flat_hash_map<int, balast> t;
-    for (int j = 0; j < 100; ++j) {
-      t[j];
-      for (const auto& p : t) EXPECT_THAT(p, Pair(_, _));
-    }
-    garbage.push_back(std::move(t));
-  }
-}
-
-// Demonstration of the "Lazy Key" pattern.  This uses heterogeneous insert to
-// avoid creating expensive key elements when the item is already present in the
-// map.
-struct LazyInt {
-  explicit LazyInt(size_t value, int* tracker)
-      : value(value), tracker(tracker) {}
-
-  explicit operator size_t() const {
-    ++*tracker;
-    return value;
-  }
-
-  size_t value;
-  int* tracker;
-};
-
-struct Hash {
-  using is_transparent = void;
-  int* tracker;
-  size_t operator()(size_t obj) const {
-    ++*tracker;
-    return obj;
-  }
-  size_t operator()(const LazyInt& obj) const {
-    ++*tracker;
-    return obj.value;
-  }
-};
-
-struct Eq {
-  using is_transparent = void;
-  bool operator()(size_t lhs, size_t rhs) const {
-    return lhs == rhs;
-  }
-  bool operator()(size_t lhs, const LazyInt& rhs) const {
-    return lhs == rhs.value;
-  }
-};
-
-TEST(FlatHashMap, LazyKeyPattern) {
-  // hashes are only guaranteed in opt mode, we use assertions to track internal
-  // state that can cause extra calls to hash.
-  int conversions = 0;
-  int hashes = 0;
-  flat_hash_map<size_t, size_t, Hash, Eq> m(0, Hash{&hashes});
-  m.reserve(3);
-
-  m[LazyInt(1, &conversions)] = 1;
-  EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1)));
-  EXPECT_EQ(conversions, 1);
-#ifdef NDEBUG
-  EXPECT_EQ(hashes, 1);
-#endif
-
-  m[LazyInt(1, &conversions)] = 2;
-  EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2)));
-  EXPECT_EQ(conversions, 1);
-#ifdef NDEBUG
-  EXPECT_EQ(hashes, 2);
-#endif
-
-  m.try_emplace(LazyInt(2, &conversions), 3);
-  EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
-  EXPECT_EQ(conversions, 2);
-#ifdef NDEBUG
-  EXPECT_EQ(hashes, 3);
-#endif
-
-  m.try_emplace(LazyInt(2, &conversions), 4);
-  EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
-  EXPECT_EQ(conversions, 2);
-#ifdef NDEBUG
-  EXPECT_EQ(hashes, 4);
-#endif
-}
-
-TEST(FlatHashMap, BitfieldArgument) {
-  union {
-    int n : 1;
-  };
-  n = 0;
-  flat_hash_map<int, int> m;
-  m.erase(n);
-  m.count(n);
-  m.prefetch(n);
-  m.find(n);
-  m.contains(n);
-  m.equal_range(n);
-  m.insert_or_assign(n, n);
-  m.insert_or_assign(m.end(), n, n);
-  m.try_emplace(n);
-  m.try_emplace(m.end(), n);
-  m.at(n);
-  m[n];
-}
-
-TEST(FlatHashMap, MergeExtractInsert) {
-  // We can't test mutable keys, or non-copyable keys with flat_hash_map.
-  // Test that the nodes have the proper API.
-  absl::flat_hash_map<int, int> m = {{1, 7}, {2, 9}};
-  auto node = m.extract(1);
-  EXPECT_TRUE(node);
-  EXPECT_EQ(node.key(), 1);
-  EXPECT_EQ(node.mapped(), 7);
-  EXPECT_THAT(m, UnorderedElementsAre(Pair(2, 9)));
-
-  node.mapped() = 17;
-  m.insert(std::move(node));
-  EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9)));
-}
-
-bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; }
-
-TEST(FlatHashMap, EraseIf) {
-  // Erase all elements.
-  {
-    flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s, [](std::pair<const int, int>) { return true; });
-    EXPECT_THAT(s, IsEmpty());
-  }
-  // Erase no elements.
-  {
-    flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s, [](std::pair<const int, int>) { return false; });
-    EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3),
-                                        Pair(4, 4), Pair(5, 5)));
-  }
-  // Erase specific elements.
-  {
-    flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s,
-             [](std::pair<const int, int> kvp) { return kvp.first % 2 == 1; });
-    EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4)));
-  }
-  // Predicate is function reference.
-  {
-    flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s, FirstIsEven);
-    EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
-  }
-  // Predicate is function pointer.
-  {
-    flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s, &FirstIsEven);
-    EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
-  }
-}
-
-// This test requires std::launder for mutable key access in node handles.
-#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
-TEST(FlatHashMap, NodeHandleMutableKeyAccess) {
-  flat_hash_map<std::string, std::string> map;
-
-  map["key1"] = "mapped";
-
-  auto nh = map.extract(map.begin());
-  nh.key().resize(3);
-  map.insert(std::move(nh));
-
-  EXPECT_THAT(map, testing::ElementsAre(Pair("key", "mapped")));
-}
-#endif
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/flat_hash_set.h b/third_party/abseil_cpp/absl/container/flat_hash_set.h
deleted file mode 100644
index 6b89da6571..0000000000
--- a/third_party/abseil_cpp/absl/container/flat_hash_set.h
+++ /dev/null
@@ -1,504 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: flat_hash_set.h
-// -----------------------------------------------------------------------------
-//
-// An `absl::flat_hash_set<T>` is an unordered associative container designed to
-// be a more efficient replacement for `std::unordered_set`. Like
-// `unordered_set`, search, insertion, and deletion of set elements can be done
-// as an `O(1)` operation. However, `flat_hash_set` (and other unordered
-// associative containers known as the collection of Abseil "Swiss tables")
-// contain other optimizations that result in both memory and computation
-// advantages.
-//
-// In most cases, your default choice for a hash set should be a set of type
-// `flat_hash_set`.
-#ifndef ABSL_CONTAINER_FLAT_HASH_SET_H_
-#define ABSL_CONTAINER_FLAT_HASH_SET_H_
-
-#include <type_traits>
-#include <utility>
-
-#include "absl/algorithm/container.h"
-#include "absl/base/macros.h"
-#include "absl/container/internal/container_memory.h"
-#include "absl/container/internal/hash_function_defaults.h"  // IWYU pragma: export
-#include "absl/container/internal/raw_hash_set.h"  // IWYU pragma: export
-#include "absl/memory/memory.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-template <typename T>
-struct FlatHashSetPolicy;
-}  // namespace container_internal
-
-// -----------------------------------------------------------------------------
-// absl::flat_hash_set
-// -----------------------------------------------------------------------------
-//
-// An `absl::flat_hash_set<T>` is an unordered associative container which has
-// been optimized for both speed and memory footprint in most common use cases.
-// Its interface is similar to that of `std::unordered_set<T>` with the
-// following notable differences:
-//
-// * Requires keys that are CopyConstructible
-// * Supports heterogeneous lookup, through `find()` and `insert()`, provided
-//   that the set is provided a compatible heterogeneous hashing function and
-//   equality operator.
-// * Invalidates any references and pointers to elements within the table after
-//   `rehash()`.
-// * Contains a `capacity()` member function indicating the number of element
-//   slots (open, deleted, and empty) within the hash set.
-// * Returns `void` from the `erase(iterator)` overload.
-//
-// By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All
-// fundamental and Abseil types that support the `absl::Hash` framework have a
-// compatible equality operator for comparing insertions into `flat_hash_map`.
-// If your type is not yet supported by the `absl::Hash` framework, see
-// absl/hash/hash.h for information on extending Abseil hashing to user-defined
-// types.
-//
-// NOTE: A `flat_hash_set` stores its keys directly inside its implementation
-// array to avoid memory indirection. Because a `flat_hash_set` is designed to
-// move data when rehashed, set keys will not retain pointer stability. If you
-// require pointer stability, consider using
-// `absl::flat_hash_set<std::unique_ptr<T>>`. If your type is not moveable and
-// you require pointer stability, consider `absl::node_hash_set` instead.
-//
-// Example:
-//
-//   // Create a flat hash set of three strings
-//   absl::flat_hash_set<std::string> ducks =
-//     {"huey", "dewey", "louie"};
-//
-//  // Insert a new element into the flat hash set
-//  ducks.insert("donald");
-//
-//  // Force a rehash of the flat hash set
-//  ducks.rehash(0);
-//
-//  // See if "dewey" is present
-//  if (ducks.contains("dewey")) {
-//    std::cout << "We found dewey!" << std::endl;
-//  }
-template <class T, class Hash = absl::container_internal::hash_default_hash<T>,
-          class Eq = absl::container_internal::hash_default_eq<T>,
-          class Allocator = std::allocator<T>>
-class flat_hash_set
-    : public absl::container_internal::raw_hash_set<
-          absl::container_internal::FlatHashSetPolicy<T>, Hash, Eq, Allocator> {
-  using Base = typename flat_hash_set::raw_hash_set;
-
- public:
-  // Constructors and Assignment Operators
-  //
-  // A flat_hash_set supports the same overload set as `std::unordered_map`
-  // for construction and assignment:
-  //
-  // *  Default constructor
-  //
-  //    // No allocation for the table's elements is made.
-  //    absl::flat_hash_set<std::string> set1;
-  //
-  // * Initializer List constructor
-  //
-  //   absl::flat_hash_set<std::string> set2 =
-  //       {{"huey"}, {"dewey"}, {"louie"},};
-  //
-  // * Copy constructor
-  //
-  //   absl::flat_hash_set<std::string> set3(set2);
-  //
-  // * Copy assignment operator
-  //
-  //  // Hash functor and Comparator are copied as well
-  //  absl::flat_hash_set<std::string> set4;
-  //  set4 = set3;
-  //
-  // * Move constructor
-  //
-  //   // Move is guaranteed efficient
-  //   absl::flat_hash_set<std::string> set5(std::move(set4));
-  //
-  // * Move assignment operator
-  //
-  //   // May be efficient if allocators are compatible
-  //   absl::flat_hash_set<std::string> set6;
-  //   set6 = std::move(set5);
-  //
-  // * Range constructor
-  //
-  //   std::vector<std::string> v = {"a", "b"};
-  //   absl::flat_hash_set<std::string> set7(v.begin(), v.end());
-  flat_hash_set() {}
-  using Base::Base;
-
-  // flat_hash_set::begin()
-  //
-  // Returns an iterator to the beginning of the `flat_hash_set`.
-  using Base::begin;
-
-  // flat_hash_set::cbegin()
-  //
-  // Returns a const iterator to the beginning of the `flat_hash_set`.
-  using Base::cbegin;
-
-  // flat_hash_set::cend()
-  //
-  // Returns a const iterator to the end of the `flat_hash_set`.
-  using Base::cend;
-
-  // flat_hash_set::end()
-  //
-  // Returns an iterator to the end of the `flat_hash_set`.
-  using Base::end;
-
-  // flat_hash_set::capacity()
-  //
-  // Returns the number of element slots (assigned, deleted, and empty)
-  // available within the `flat_hash_set`.
-  //
-  // NOTE: this member function is particular to `absl::flat_hash_set` and is
-  // not provided in the `std::unordered_map` API.
-  using Base::capacity;
-
-  // flat_hash_set::empty()
-  //
-  // Returns whether or not the `flat_hash_set` is empty.
-  using Base::empty;
-
-  // flat_hash_set::max_size()
-  //
-  // Returns the largest theoretical possible number of elements within a
-  // `flat_hash_set` under current memory constraints. This value can be thought
-  // of the largest value of `std::distance(begin(), end())` for a
-  // `flat_hash_set<T>`.
-  using Base::max_size;
-
-  // flat_hash_set::size()
-  //
-  // Returns the number of elements currently within the `flat_hash_set`.
-  using Base::size;
-
-  // flat_hash_set::clear()
-  //
-  // Removes all elements from the `flat_hash_set`. Invalidates any references,
-  // pointers, or iterators referring to contained elements.
-  //
-  // NOTE: this operation may shrink the underlying buffer. To avoid shrinking
-  // the underlying buffer call `erase(begin(), end())`.
-  using Base::clear;
-
-  // flat_hash_set::erase()
-  //
-  // Erases elements within the `flat_hash_set`. Erasing does not trigger a
-  // rehash. Overloads are listed below.
-  //
-  // void erase(const_iterator pos):
-  //
-  //   Erases the element at `position` of the `flat_hash_set`, returning
-  //   `void`.
-  //
-  //   NOTE: returning `void` in this case is different than that of STL
-  //   containers in general and `std::unordered_set` in particular (which
-  //   return an iterator to the element following the erased element). If that
-  //   iterator is needed, simply post increment the iterator:
-  //
-  //     set.erase(it++);
-  //
-  // iterator erase(const_iterator first, const_iterator last):
-  //
-  //   Erases the elements in the open interval [`first`, `last`), returning an
-  //   iterator pointing to `last`.
-  //
-  // size_type erase(const key_type& key):
-  //
-  //   Erases the element with the matching key, if it exists, returning the
-  //   number of elements erased (0 or 1).
-  using Base::erase;
-
-  // flat_hash_set::insert()
-  //
-  // Inserts an element of the specified value into the `flat_hash_set`,
-  // returning an iterator pointing to the newly inserted element, provided that
-  // an element with the given key does not already exist. If rehashing occurs
-  // due to the insertion, all iterators are invalidated. Overloads are listed
-  // below.
-  //
-  // std::pair<iterator,bool> insert(const T& value):
-  //
-  //   Inserts a value into the `flat_hash_set`. Returns a pair consisting of an
-  //   iterator to the inserted element (or to the element that prevented the
-  //   insertion) and a bool denoting whether the insertion took place.
-  //
-  // std::pair<iterator,bool> insert(T&& value):
-  //
-  //   Inserts a moveable value into the `flat_hash_set`. Returns a pair
-  //   consisting of an iterator to the inserted element (or to the element that
-  //   prevented the insertion) and a bool denoting whether the insertion took
-  //   place.
-  //
-  // iterator insert(const_iterator hint, const T& value):
-  // iterator insert(const_iterator hint, T&& value):
-  //
-  //   Inserts a value, using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search. Returns an iterator to the
-  //   inserted element, or to the existing element that prevented the
-  //   insertion.
-  //
-  // void insert(InputIterator first, InputIterator last):
-  //
-  //   Inserts a range of values [`first`, `last`).
-  //
-  //   NOTE: Although the STL does not specify which element may be inserted if
-  //   multiple keys compare equivalently, for `flat_hash_set` we guarantee the
-  //   first match is inserted.
-  //
-  // void insert(std::initializer_list<T> ilist):
-  //
-  //   Inserts the elements within the initializer list `ilist`.
-  //
-  //   NOTE: Although the STL does not specify which element may be inserted if
-  //   multiple keys compare equivalently within the initializer list, for
-  //   `flat_hash_set` we guarantee the first match is inserted.
-  using Base::insert;
-
-  // flat_hash_set::emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `flat_hash_set`, provided that no element with the given key
-  // already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  using Base::emplace;
-
-  // flat_hash_set::emplace_hint()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `flat_hash_set`, using the position of `hint` as a non-binding
-  // suggestion for where to begin the insertion search, and only inserts
-  // provided that no element with the given key already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  using Base::emplace_hint;
-
-  // flat_hash_set::extract()
-  //
-  // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
-  //
-  // node_type extract(const_iterator position):
-  //
-  //   Extracts the element at the indicated position and returns a node handle
-  //   owning that extracted data.
-  //
-  // node_type extract(const key_type& x):
-  //
-  //   Extracts the element with the key matching the passed key value and
-  //   returns a node handle owning that extracted data. If the `flat_hash_set`
-  //   does not contain an element with a matching key, this function returns an
-  //   empty node handle.
-  using Base::extract;
-
-  // flat_hash_set::merge()
-  //
-  // Extracts elements from a given `source` flat hash set into this
-  // `flat_hash_set`. If the destination `flat_hash_set` already contains an
-  // element with an equivalent key, that element is not extracted.
-  using Base::merge;
-
-  // flat_hash_set::swap(flat_hash_set& other)
-  //
-  // Exchanges the contents of this `flat_hash_set` with those of the `other`
-  // flat hash map, avoiding invocation of any move, copy, or swap operations on
-  // individual elements.
-  //
-  // All iterators and references on the `flat_hash_set` remain valid, excepting
-  // for the past-the-end iterator, which is invalidated.
-  //
-  // `swap()` requires that the flat hash set's hashing and key equivalence
-  // functions be Swappable, and are exchaged using unqualified calls to
-  // non-member `swap()`. If the map's allocator has
-  // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
-  // set to `true`, the allocators are also exchanged using an unqualified call
-  // to non-member `swap()`; otherwise, the allocators are not swapped.
-  using Base::swap;
-
-  // flat_hash_set::rehash(count)
-  //
-  // Rehashes the `flat_hash_set`, setting the number of slots to be at least
-  // the passed value. If the new number of slots increases the load factor more
-  // than the current maximum load factor
-  // (`count` < `size()` / `max_load_factor()`), then the new number of slots
-  // will be at least `size()` / `max_load_factor()`.
-  //
-  // To force a rehash, pass rehash(0).
-  //
-  // NOTE: unlike behavior in `std::unordered_set`, references are also
-  // invalidated upon a `rehash()`.
-  using Base::rehash;
-
-  // flat_hash_set::reserve(count)
-  //
-  // Sets the number of slots in the `flat_hash_set` to the number needed to
-  // accommodate at least `count` total elements without exceeding the current
-  // maximum load factor, and may rehash the container if needed.
-  using Base::reserve;
-
-  // flat_hash_set::contains()
-  //
-  // Determines whether an element comparing equal to the given `key` exists
-  // within the `flat_hash_set`, returning `true` if so or `false` otherwise.
-  using Base::contains;
-
-  // flat_hash_set::count(const Key& key) const
-  //
-  // Returns the number of elements comparing equal to the given `key` within
-  // the `flat_hash_set`. note that this function will return either `1` or `0`
-  // since duplicate elements are not allowed within a `flat_hash_set`.
-  using Base::count;
-
-  // flat_hash_set::equal_range()
-  //
-  // Returns a closed range [first, last], defined by a `std::pair` of two
-  // iterators, containing all elements with the passed key in the
-  // `flat_hash_set`.
-  using Base::equal_range;
-
-  // flat_hash_set::find()
-  //
-  // Finds an element with the passed `key` within the `flat_hash_set`.
-  using Base::find;
-
-  // flat_hash_set::bucket_count()
-  //
-  // Returns the number of "buckets" within the `flat_hash_set`. Note that
-  // because a flat hash map contains all elements within its internal storage,
-  // this value simply equals the current capacity of the `flat_hash_set`.
-  using Base::bucket_count;
-
-  // flat_hash_set::load_factor()
-  //
-  // Returns the current load factor of the `flat_hash_set` (the average number
-  // of slots occupied with a value within the hash map).
-  using Base::load_factor;
-
-  // flat_hash_set::max_load_factor()
-  //
-  // Manages the maximum load factor of the `flat_hash_set`. Overloads are
-  // listed below.
-  //
-  // float flat_hash_set::max_load_factor()
-  //
-  //   Returns the current maximum load factor of the `flat_hash_set`.
-  //
-  // void flat_hash_set::max_load_factor(float ml)
-  //
-  //   Sets the maximum load factor of the `flat_hash_set` to the passed value.
-  //
-  //   NOTE: This overload is provided only for API compatibility with the STL;
-  //   `flat_hash_set` will ignore any set load factor and manage its rehashing
-  //   internally as an implementation detail.
-  using Base::max_load_factor;
-
-  // flat_hash_set::get_allocator()
-  //
-  // Returns the allocator function associated with this `flat_hash_set`.
-  using Base::get_allocator;
-
-  // flat_hash_set::hash_function()
-  //
-  // Returns the hashing function used to hash the keys within this
-  // `flat_hash_set`.
-  using Base::hash_function;
-
-  // flat_hash_set::key_eq()
-  //
-  // Returns the function used for comparing keys equality.
-  using Base::key_eq;
-};
-
-// erase_if(flat_hash_set<>, Pred)
-//
-// Erases all elements that satisfy the predicate `pred` from the container `c`.
-template <typename T, typename H, typename E, typename A, typename Predicate>
-void erase_if(flat_hash_set<T, H, E, A>& c, Predicate pred) {
-  container_internal::EraseIf(pred, &c);
-}
-
-namespace container_internal {
-
-template <class T>
-struct FlatHashSetPolicy {
-  using slot_type = T;
-  using key_type = T;
-  using init_type = T;
-  using constant_iterators = std::true_type;
-
-  template <class Allocator, class... Args>
-  static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
-    absl::allocator_traits<Allocator>::construct(*alloc, slot,
-                                                 std::forward<Args>(args)...);
-  }
-
-  template <class Allocator>
-  static void destroy(Allocator* alloc, slot_type* slot) {
-    absl::allocator_traits<Allocator>::destroy(*alloc, slot);
-  }
-
-  template <class Allocator>
-  static void transfer(Allocator* alloc, slot_type* new_slot,
-                       slot_type* old_slot) {
-    construct(alloc, new_slot, std::move(*old_slot));
-    destroy(alloc, old_slot);
-  }
-
-  static T& element(slot_type* slot) { return *slot; }
-
-  template <class F, class... Args>
-  static decltype(absl::container_internal::DecomposeValue(
-      std::declval<F>(), std::declval<Args>()...))
-  apply(F&& f, Args&&... args) {
-    return absl::container_internal::DecomposeValue(
-        std::forward<F>(f), std::forward<Args>(args)...);
-  }
-
-  static size_t space_used(const T*) { return 0; }
-};
-}  // namespace container_internal
-
-namespace container_algorithm_internal {
-
-// Specialization of trait in absl/algorithm/container.h
-template <class Key, class Hash, class KeyEqual, class Allocator>
-struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>>
-    : std::true_type {};
-
-}  // namespace container_algorithm_internal
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_FLAT_HASH_SET_H_
diff --git a/third_party/abseil_cpp/absl/container/flat_hash_set_test.cc b/third_party/abseil_cpp/absl/container/flat_hash_set_test.cc
deleted file mode 100644
index 8f6f9944ca..0000000000
--- a/third_party/abseil_cpp/absl/container/flat_hash_set_test.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/flat_hash_set.h"
-
-#include <vector>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/container/internal/hash_generator_testing.h"
-#include "absl/container/internal/unordered_set_constructor_test.h"
-#include "absl/container/internal/unordered_set_lookup_test.h"
-#include "absl/container/internal/unordered_set_members_test.h"
-#include "absl/container/internal/unordered_set_modifiers_test.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using ::absl::container_internal::hash_internal::Enum;
-using ::absl::container_internal::hash_internal::EnumClass;
-using ::testing::IsEmpty;
-using ::testing::Pointee;
-using ::testing::UnorderedElementsAre;
-using ::testing::UnorderedElementsAreArray;
-
-// Check that absl::flat_hash_set works in a global constructor.
-struct BeforeMain {
-  BeforeMain() {
-    absl::flat_hash_set<int> x;
-    x.insert(1);
-    ABSL_RAW_CHECK(!x.contains(0), "x should not contain 0");
-    ABSL_RAW_CHECK(x.contains(1), "x should contain 1");
-  }
-};
-const BeforeMain before_main;
-
-template <class T>
-using Set =
-    absl::flat_hash_set<T, StatefulTestingHash, StatefulTestingEqual, Alloc<T>>;
-
-using SetTypes =
-    ::testing::Types<Set<int>, Set<std::string>, Set<Enum>, Set<EnumClass>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, ConstructorTest, SetTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, LookupTest, SetTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, MembersTest, SetTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, ModifiersTest, SetTypes);
-
-TEST(FlatHashSet, EmplaceString) {
-  std::vector<std::string> v = {"a", "b"};
-  absl::flat_hash_set<absl::string_view> hs(v.begin(), v.end());
-  EXPECT_THAT(hs, UnorderedElementsAreArray(v));
-}
-
-TEST(FlatHashSet, BitfieldArgument) {
-  union {
-    int n : 1;
-  };
-  n = 0;
-  absl::flat_hash_set<int> s = {n};
-  s.insert(n);
-  s.insert(s.end(), n);
-  s.insert({n});
-  s.erase(n);
-  s.count(n);
-  s.prefetch(n);
-  s.find(n);
-  s.contains(n);
-  s.equal_range(n);
-}
-
-TEST(FlatHashSet, MergeExtractInsert) {
-  struct Hash {
-    size_t operator()(const std::unique_ptr<int>& p) const { return *p; }
-  };
-  struct Eq {
-    bool operator()(const std::unique_ptr<int>& a,
-                    const std::unique_ptr<int>& b) const {
-      return *a == *b;
-    }
-  };
-  absl::flat_hash_set<std::unique_ptr<int>, Hash, Eq> set1, set2;
-  set1.insert(absl::make_unique<int>(7));
-  set1.insert(absl::make_unique<int>(17));
-
-  set2.insert(absl::make_unique<int>(7));
-  set2.insert(absl::make_unique<int>(19));
-
-  EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17)));
-  EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(19)));
-
-  set1.merge(set2);
-
-  EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17), Pointee(19)));
-  EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7)));
-
-  auto node = set1.extract(absl::make_unique<int>(7));
-  EXPECT_TRUE(node);
-  EXPECT_THAT(node.value(), Pointee(7));
-  EXPECT_THAT(set1, UnorderedElementsAre(Pointee(17), Pointee(19)));
-
-  auto insert_result = set2.insert(std::move(node));
-  EXPECT_FALSE(node);
-  EXPECT_FALSE(insert_result.inserted);
-  EXPECT_TRUE(insert_result.node);
-  EXPECT_THAT(insert_result.node.value(), Pointee(7));
-  EXPECT_EQ(**insert_result.position, 7);
-  EXPECT_NE(insert_result.position->get(), insert_result.node.value().get());
-  EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7)));
-
-  node = set1.extract(absl::make_unique<int>(17));
-  EXPECT_TRUE(node);
-  EXPECT_THAT(node.value(), Pointee(17));
-  EXPECT_THAT(set1, UnorderedElementsAre(Pointee(19)));
-
-  node.value() = absl::make_unique<int>(23);
-
-  insert_result = set2.insert(std::move(node));
-  EXPECT_FALSE(node);
-  EXPECT_TRUE(insert_result.inserted);
-  EXPECT_FALSE(insert_result.node);
-  EXPECT_EQ(**insert_result.position, 23);
-  EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23)));
-}
-
-bool IsEven(int k) { return k % 2 == 0; }
-
-TEST(FlatHashSet, EraseIf) {
-  // Erase all elements.
-  {
-    flat_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, [](int) { return true; });
-    EXPECT_THAT(s, IsEmpty());
-  }
-  // Erase no elements.
-  {
-    flat_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, [](int) { return false; });
-    EXPECT_THAT(s, UnorderedElementsAre(1, 2, 3, 4, 5));
-  }
-  // Erase specific elements.
-  {
-    flat_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, [](int k) { return k % 2 == 1; });
-    EXPECT_THAT(s, UnorderedElementsAre(2, 4));
-  }
-  // Predicate is function reference.
-  {
-    flat_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, IsEven);
-    EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5));
-  }
-  // Predicate is function pointer.
-  {
-    flat_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, &IsEven);
-    EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5));
-  }
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/inlined_vector.h b/third_party/abseil_cpp/absl/container/inlined_vector.h
deleted file mode 100644
index 90bb96e832..0000000000
--- a/third_party/abseil_cpp/absl/container/inlined_vector.h
+++ /dev/null
@@ -1,845 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: inlined_vector.h
-// -----------------------------------------------------------------------------
-//
-// This header file contains the declaration and definition of an "inlined
-// vector" which behaves in an equivalent fashion to a `std::vector`, except
-// that storage for small sequences of the vector are provided inline without
-// requiring any heap allocation.
-//
-// An `absl::InlinedVector<T, N>` specifies the default capacity `N` as one of
-// its template parameters. Instances where `size() <= N` hold contained
-// elements in inline space. Typically `N` is very small so that sequences that
-// are expected to be short do not require allocations.
-//
-// An `absl::InlinedVector` does not usually require a specific allocator. If
-// the inlined vector grows beyond its initial constraints, it will need to
-// allocate (as any normal `std::vector` would). This is usually performed with
-// the default allocator (defined as `std::allocator<T>`). Optionally, a custom
-// allocator type may be specified as `A` in `absl::InlinedVector<T, N, A>`.
-
-#ifndef ABSL_CONTAINER_INLINED_VECTOR_H_
-#define ABSL_CONTAINER_INLINED_VECTOR_H_
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstdlib>
-#include <cstring>
-#include <initializer_list>
-#include <iterator>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-#include "absl/algorithm/algorithm.h"
-#include "absl/base/internal/throw_delegate.h"
-#include "absl/base/macros.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"
-#include "absl/container/internal/inlined_vector.h"
-#include "absl/memory/memory.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-// -----------------------------------------------------------------------------
-// InlinedVector
-// -----------------------------------------------------------------------------
-//
-// An `absl::InlinedVector` is designed to be a drop-in replacement for
-// `std::vector` for use cases where the vector's size is sufficiently small
-// that it can be inlined. If the inlined vector does grow beyond its estimated
-// capacity, it will trigger an initial allocation on the heap, and will behave
-// as a `std::vector`. The API of the `absl::InlinedVector` within this file is
-// designed to cover the same API footprint as covered by `std::vector`.
-template <typename T, size_t N, typename A = std::allocator<T>>
-class InlinedVector {
-  static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity.");
-
-  using Storage = inlined_vector_internal::Storage<T, N, A>;
-
-  using AllocatorTraits = typename Storage::AllocatorTraits;
-  using RValueReference = typename Storage::RValueReference;
-  using MoveIterator = typename Storage::MoveIterator;
-  using IsMemcpyOk = typename Storage::IsMemcpyOk;
-
-  template <typename Iterator>
-  using IteratorValueAdapter =
-      typename Storage::template IteratorValueAdapter<Iterator>;
-  using CopyValueAdapter = typename Storage::CopyValueAdapter;
-  using DefaultValueAdapter = typename Storage::DefaultValueAdapter;
-
-  template <typename Iterator>
-  using EnableIfAtLeastForwardIterator = absl::enable_if_t<
-      inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>;
-  template <typename Iterator>
-  using DisableIfAtLeastForwardIterator = absl::enable_if_t<
-      !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>;
-
- public:
-  using allocator_type = typename Storage::allocator_type;
-  using value_type = typename Storage::value_type;
-  using pointer = typename Storage::pointer;
-  using const_pointer = typename Storage::const_pointer;
-  using size_type = typename Storage::size_type;
-  using difference_type = typename Storage::difference_type;
-  using reference = typename Storage::reference;
-  using const_reference = typename Storage::const_reference;
-  using iterator = typename Storage::iterator;
-  using const_iterator = typename Storage::const_iterator;
-  using reverse_iterator = typename Storage::reverse_iterator;
-  using const_reverse_iterator = typename Storage::const_reverse_iterator;
-
-  // ---------------------------------------------------------------------------
-  // InlinedVector Constructors and Destructor
-  // ---------------------------------------------------------------------------
-
-  // Creates an empty inlined vector with a value-initialized allocator.
-  InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {}
-
-  // Creates an empty inlined vector with a copy of `alloc`.
-  explicit InlinedVector(const allocator_type& alloc) noexcept
-      : storage_(alloc) {}
-
-  // Creates an inlined vector with `n` copies of `value_type()`.
-  explicit InlinedVector(size_type n,
-                         const allocator_type& alloc = allocator_type())
-      : storage_(alloc) {
-    storage_.Initialize(DefaultValueAdapter(), n);
-  }
-
-  // Creates an inlined vector with `n` copies of `v`.
-  InlinedVector(size_type n, const_reference v,
-                const allocator_type& alloc = allocator_type())
-      : storage_(alloc) {
-    storage_.Initialize(CopyValueAdapter(v), n);
-  }
-
-  // Creates an inlined vector with copies of the elements of `list`.
-  InlinedVector(std::initializer_list<value_type> list,
-                const allocator_type& alloc = allocator_type())
-      : InlinedVector(list.begin(), list.end(), alloc) {}
-
-  // Creates an inlined vector with elements constructed from the provided
-  // forward iterator range [`first`, `last`).
-  //
-  // NOTE: the `enable_if` prevents ambiguous interpretation between a call to
-  // this constructor with two integral arguments and a call to the above
-  // `InlinedVector(size_type, const_reference)` constructor.
-  template <typename ForwardIterator,
-            EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
-  InlinedVector(ForwardIterator first, ForwardIterator last,
-                const allocator_type& alloc = allocator_type())
-      : storage_(alloc) {
-    storage_.Initialize(IteratorValueAdapter<ForwardIterator>(first),
-                        std::distance(first, last));
-  }
-
-  // Creates an inlined vector with elements constructed from the provided input
-  // iterator range [`first`, `last`).
-  template <typename InputIterator,
-            DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
-  InlinedVector(InputIterator first, InputIterator last,
-                const allocator_type& alloc = allocator_type())
-      : storage_(alloc) {
-    std::copy(first, last, std::back_inserter(*this));
-  }
-
-  // Creates an inlined vector by copying the contents of `other` using
-  // `other`'s allocator.
-  InlinedVector(const InlinedVector& other)
-      : InlinedVector(other, *other.storage_.GetAllocPtr()) {}
-
-  // Creates an inlined vector by copying the contents of `other` using `alloc`.
-  InlinedVector(const InlinedVector& other, const allocator_type& alloc)
-      : storage_(alloc) {
-    if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) {
-      storage_.MemcpyFrom(other.storage_);
-    } else {
-      storage_.Initialize(IteratorValueAdapter<const_pointer>(other.data()),
-                          other.size());
-    }
-  }
-
-  // Creates an inlined vector by moving in the contents of `other` without
-  // allocating. If `other` contains allocated memory, the newly-created inlined
-  // vector will take ownership of that memory. However, if `other` does not
-  // contain allocated memory, the newly-created inlined vector will perform
-  // element-wise move construction of the contents of `other`.
-  //
-  // NOTE: since no allocation is performed for the inlined vector in either
-  // case, the `noexcept(...)` specification depends on whether moving the
-  // underlying objects can throw. It is assumed assumed that...
-  //  a) move constructors should only throw due to allocation failure.
-  //  b) if `value_type`'s move constructor allocates, it uses the same
-  //     allocation function as the inlined vector's allocator.
-  // Thus, the move constructor is non-throwing if the allocator is non-throwing
-  // or `value_type`'s move constructor is specified as `noexcept`.
-  InlinedVector(InlinedVector&& other) noexcept(
-      absl::allocator_is_nothrow<allocator_type>::value ||
-      std::is_nothrow_move_constructible<value_type>::value)
-      : storage_(*other.storage_.GetAllocPtr()) {
-    if (IsMemcpyOk::value) {
-      storage_.MemcpyFrom(other.storage_);
-
-      other.storage_.SetInlinedSize(0);
-    } else if (other.storage_.GetIsAllocated()) {
-      storage_.SetAllocatedData(other.storage_.GetAllocatedData(),
-                                other.storage_.GetAllocatedCapacity());
-      storage_.SetAllocatedSize(other.storage_.GetSize());
-
-      other.storage_.SetInlinedSize(0);
-    } else {
-      IteratorValueAdapter<MoveIterator> other_values(
-          MoveIterator(other.storage_.GetInlinedData()));
-
-      inlined_vector_internal::ConstructElements(
-          storage_.GetAllocPtr(), storage_.GetInlinedData(), &other_values,
-          other.storage_.GetSize());
-
-      storage_.SetInlinedSize(other.storage_.GetSize());
-    }
-  }
-
-  // Creates an inlined vector by moving in the contents of `other` with a copy
-  // of `alloc`.
-  //
-  // NOTE: if `other`'s allocator is not equal to `alloc`, even if `other`
-  // contains allocated memory, this move constructor will still allocate. Since
-  // allocation is performed, this constructor can only be `noexcept` if the
-  // specified allocator is also `noexcept`.
-  InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept(
-      absl::allocator_is_nothrow<allocator_type>::value)
-      : storage_(alloc) {
-    if (IsMemcpyOk::value) {
-      storage_.MemcpyFrom(other.storage_);
-
-      other.storage_.SetInlinedSize(0);
-    } else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) &&
-               other.storage_.GetIsAllocated()) {
-      storage_.SetAllocatedData(other.storage_.GetAllocatedData(),
-                                other.storage_.GetAllocatedCapacity());
-      storage_.SetAllocatedSize(other.storage_.GetSize());
-
-      other.storage_.SetInlinedSize(0);
-    } else {
-      storage_.Initialize(
-          IteratorValueAdapter<MoveIterator>(MoveIterator(other.data())),
-          other.size());
-    }
-  }
-
-  ~InlinedVector() {}
-
-  // ---------------------------------------------------------------------------
-  // InlinedVector Member Accessors
-  // ---------------------------------------------------------------------------
-
-  // `InlinedVector::empty()`
-  //
-  // Returns whether the inlined vector contains no elements.
-  bool empty() const noexcept { return !size(); }
-
-  // `InlinedVector::size()`
-  //
-  // Returns the number of elements in the inlined vector.
-  size_type size() const noexcept { return storage_.GetSize(); }
-
-  // `InlinedVector::max_size()`
-  //
-  // Returns the maximum number of elements the inlined vector can hold.
-  size_type max_size() const noexcept {
-    // One bit of the size storage is used to indicate whether the inlined
-    // vector contains allocated memory. As a result, the maximum size that the
-    // inlined vector can express is half of the max for `size_type`.
-    return (std::numeric_limits<size_type>::max)() / 2;
-  }
-
-  // `InlinedVector::capacity()`
-  //
-  // Returns the number of elements that could be stored in the inlined vector
-  // without requiring a reallocation.
-  //
-  // NOTE: for most inlined vectors, `capacity()` should be equal to the
-  // template parameter `N`. For inlined vectors which exceed this capacity,
-  // they will no longer be inlined and `capacity()` will equal the capactity of
-  // the allocated memory.
-  size_type capacity() const noexcept {
-    return storage_.GetIsAllocated() ? storage_.GetAllocatedCapacity()
-                                     : storage_.GetInlinedCapacity();
-  }
-
-  // `InlinedVector::data()`
-  //
-  // Returns a `pointer` to the elements of the inlined vector. This pointer
-  // can be used to access and modify the contained elements.
-  //
-  // NOTE: only elements within [`data()`, `data() + size()`) are valid.
-  pointer data() noexcept {
-    return storage_.GetIsAllocated() ? storage_.GetAllocatedData()
-                                     : storage_.GetInlinedData();
-  }
-
-  // Overload of `InlinedVector::data()` that returns a `const_pointer` to the
-  // elements of the inlined vector. This pointer can be used to access but not
-  // modify the contained elements.
-  //
-  // NOTE: only elements within [`data()`, `data() + size()`) are valid.
-  const_pointer data() const noexcept {
-    return storage_.GetIsAllocated() ? storage_.GetAllocatedData()
-                                     : storage_.GetInlinedData();
-  }
-
-  // `InlinedVector::operator[](...)`
-  //
-  // Returns a `reference` to the `i`th element of the inlined vector.
-  reference operator[](size_type i) {
-    ABSL_HARDENING_ASSERT(i < size());
-    return data()[i];
-  }
-
-  // Overload of `InlinedVector::operator[](...)` that returns a
-  // `const_reference` to the `i`th element of the inlined vector.
-  const_reference operator[](size_type i) const {
-    ABSL_HARDENING_ASSERT(i < size());
-    return data()[i];
-  }
-
-  // `InlinedVector::at(...)`
-  //
-  // Returns a `reference` to the `i`th element of the inlined vector.
-  //
-  // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`,
-  // in both debug and non-debug builds, `std::out_of_range` will be thrown.
-  reference at(size_type i) {
-    if (ABSL_PREDICT_FALSE(i >= size())) {
-      base_internal::ThrowStdOutOfRange(
-          "`InlinedVector::at(size_type)` failed bounds check");
-    }
-    return data()[i];
-  }
-
-  // Overload of `InlinedVector::at(...)` that returns a `const_reference` to
-  // the `i`th element of the inlined vector.
-  //
-  // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`,
-  // in both debug and non-debug builds, `std::out_of_range` will be thrown.
-  const_reference at(size_type i) const {
-    if (ABSL_PREDICT_FALSE(i >= size())) {
-      base_internal::ThrowStdOutOfRange(
-          "`InlinedVector::at(size_type) const` failed bounds check");
-    }
-    return data()[i];
-  }
-
-  // `InlinedVector::front()`
-  //
-  // Returns a `reference` to the first element of the inlined vector.
-  reference front() {
-    ABSL_HARDENING_ASSERT(!empty());
-    return data()[0];
-  }
-
-  // Overload of `InlinedVector::front()` that returns a `const_reference` to
-  // the first element of the inlined vector.
-  const_reference front() const {
-    ABSL_HARDENING_ASSERT(!empty());
-    return data()[0];
-  }
-
-  // `InlinedVector::back()`
-  //
-  // Returns a `reference` to the last element of the inlined vector.
-  reference back() {
-    ABSL_HARDENING_ASSERT(!empty());
-    return data()[size() - 1];
-  }
-
-  // Overload of `InlinedVector::back()` that returns a `const_reference` to the
-  // last element of the inlined vector.
-  const_reference back() const {
-    ABSL_HARDENING_ASSERT(!empty());
-    return data()[size() - 1];
-  }
-
-  // `InlinedVector::begin()`
-  //
-  // Returns an `iterator` to the beginning of the inlined vector.
-  iterator begin() noexcept { return data(); }
-
-  // Overload of `InlinedVector::begin()` that returns a `const_iterator` to
-  // the beginning of the inlined vector.
-  const_iterator begin() const noexcept { return data(); }
-
-  // `InlinedVector::end()`
-  //
-  // Returns an `iterator` to the end of the inlined vector.
-  iterator end() noexcept { return data() + size(); }
-
-  // Overload of `InlinedVector::end()` that returns a `const_iterator` to the
-  // end of the inlined vector.
-  const_iterator end() const noexcept { return data() + size(); }
-
-  // `InlinedVector::cbegin()`
-  //
-  // Returns a `const_iterator` to the beginning of the inlined vector.
-  const_iterator cbegin() const noexcept { return begin(); }
-
-  // `InlinedVector::cend()`
-  //
-  // Returns a `const_iterator` to the end of the inlined vector.
-  const_iterator cend() const noexcept { return end(); }
-
-  // `InlinedVector::rbegin()`
-  //
-  // Returns a `reverse_iterator` from the end of the inlined vector.
-  reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
-
-  // Overload of `InlinedVector::rbegin()` that returns a
-  // `const_reverse_iterator` from the end of the inlined vector.
-  const_reverse_iterator rbegin() const noexcept {
-    return const_reverse_iterator(end());
-  }
-
-  // `InlinedVector::rend()`
-  //
-  // Returns a `reverse_iterator` from the beginning of the inlined vector.
-  reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
-
-  // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator`
-  // from the beginning of the inlined vector.
-  const_reverse_iterator rend() const noexcept {
-    return const_reverse_iterator(begin());
-  }
-
-  // `InlinedVector::crbegin()`
-  //
-  // Returns a `const_reverse_iterator` from the end of the inlined vector.
-  const_reverse_iterator crbegin() const noexcept { return rbegin(); }
-
-  // `InlinedVector::crend()`
-  //
-  // Returns a `const_reverse_iterator` from the beginning of the inlined
-  // vector.
-  const_reverse_iterator crend() const noexcept { return rend(); }
-
-  // `InlinedVector::get_allocator()`
-  //
-  // Returns a copy of the inlined vector's allocator.
-  allocator_type get_allocator() const { return *storage_.GetAllocPtr(); }
-
-  // ---------------------------------------------------------------------------
-  // InlinedVector Member Mutators
-  // ---------------------------------------------------------------------------
-
-  // `InlinedVector::operator=(...)`
-  //
-  // Replaces the elements of the inlined vector with copies of the elements of
-  // `list`.
-  InlinedVector& operator=(std::initializer_list<value_type> list) {
-    assign(list.begin(), list.end());
-
-    return *this;
-  }
-
-  // Overload of `InlinedVector::operator=(...)` that replaces the elements of
-  // the inlined vector with copies of the elements of `other`.
-  InlinedVector& operator=(const InlinedVector& other) {
-    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
-      const_pointer other_data = other.data();
-      assign(other_data, other_data + other.size());
-    }
-
-    return *this;
-  }
-
-  // Overload of `InlinedVector::operator=(...)` that moves the elements of
-  // `other` into the inlined vector.
-  //
-  // NOTE: as a result of calling this overload, `other` is left in a valid but
-  // unspecified state.
-  InlinedVector& operator=(InlinedVector&& other) {
-    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
-      if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) {
-        inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(),
-                                                 size());
-        storage_.DeallocateIfAllocated();
-        storage_.MemcpyFrom(other.storage_);
-
-        other.storage_.SetInlinedSize(0);
-      } else {
-        storage_.Assign(IteratorValueAdapter<MoveIterator>(
-                            MoveIterator(other.storage_.GetInlinedData())),
-                        other.size());
-      }
-    }
-
-    return *this;
-  }
-
-  // `InlinedVector::assign(...)`
-  //
-  // Replaces the contents of the inlined vector with `n` copies of `v`.
-  void assign(size_type n, const_reference v) {
-    storage_.Assign(CopyValueAdapter(v), n);
-  }
-
-  // Overload of `InlinedVector::assign(...)` that replaces the contents of the
-  // inlined vector with copies of the elements of `list`.
-  void assign(std::initializer_list<value_type> list) {
-    assign(list.begin(), list.end());
-  }
-
-  // Overload of `InlinedVector::assign(...)` to replace the contents of the
-  // inlined vector with the range [`first`, `last`).
-  //
-  // NOTE: this overload is for iterators that are "forward" category or better.
-  template <typename ForwardIterator,
-            EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
-  void assign(ForwardIterator first, ForwardIterator last) {
-    storage_.Assign(IteratorValueAdapter<ForwardIterator>(first),
-                    std::distance(first, last));
-  }
-
-  // Overload of `InlinedVector::assign(...)` to replace the contents of the
-  // inlined vector with the range [`first`, `last`).
-  //
-  // NOTE: this overload is for iterators that are "input" category.
-  template <typename InputIterator,
-            DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
-  void assign(InputIterator first, InputIterator last) {
-    size_type i = 0;
-    for (; i < size() && first != last; ++i, static_cast<void>(++first)) {
-      data()[i] = *first;
-    }
-
-    erase(data() + i, data() + size());
-    std::copy(first, last, std::back_inserter(*this));
-  }
-
-  // `InlinedVector::resize(...)`
-  //
-  // Resizes the inlined vector to contain `n` elements.
-  //
-  // NOTE: If `n` is smaller than `size()`, extra elements are destroyed. If `n`
-  // is larger than `size()`, new elements are value-initialized.
-  void resize(size_type n) {
-    ABSL_HARDENING_ASSERT(n <= max_size());
-    storage_.Resize(DefaultValueAdapter(), n);
-  }
-
-  // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to
-  // contain `n` elements.
-  //
-  // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n`
-  // is larger than `size()`, new elements are copied-constructed from `v`.
-  void resize(size_type n, const_reference v) {
-    ABSL_HARDENING_ASSERT(n <= max_size());
-    storage_.Resize(CopyValueAdapter(v), n);
-  }
-
-  // `InlinedVector::insert(...)`
-  //
-  // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly
-  // inserted element.
-  iterator insert(const_iterator pos, const_reference v) {
-    return emplace(pos, v);
-  }
-
-  // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using
-  // move semantics, returning an `iterator` to the newly inserted element.
-  iterator insert(const_iterator pos, RValueReference v) {
-    return emplace(pos, std::move(v));
-  }
-
-  // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies
-  // of `v` starting at `pos`, returning an `iterator` pointing to the first of
-  // the newly inserted elements.
-  iterator insert(const_iterator pos, size_type n, const_reference v) {
-    ABSL_HARDENING_ASSERT(pos >= begin());
-    ABSL_HARDENING_ASSERT(pos <= end());
-
-    if (ABSL_PREDICT_TRUE(n != 0)) {
-      value_type dealias = v;
-      return storage_.Insert(pos, CopyValueAdapter(dealias), n);
-    } else {
-      return const_cast<iterator>(pos);
-    }
-  }
-
-  // Overload of `InlinedVector::insert(...)` that inserts copies of the
-  // elements of `list` starting at `pos`, returning an `iterator` pointing to
-  // the first of the newly inserted elements.
-  iterator insert(const_iterator pos, std::initializer_list<value_type> list) {
-    return insert(pos, list.begin(), list.end());
-  }
-
-  // Overload of `InlinedVector::insert(...)` that inserts the range [`first`,
-  // `last`) starting at `pos`, returning an `iterator` pointing to the first
-  // of the newly inserted elements.
-  //
-  // NOTE: this overload is for iterators that are "forward" category or better.
-  template <typename ForwardIterator,
-            EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
-  iterator insert(const_iterator pos, ForwardIterator first,
-                  ForwardIterator last) {
-    ABSL_HARDENING_ASSERT(pos >= begin());
-    ABSL_HARDENING_ASSERT(pos <= end());
-
-    if (ABSL_PREDICT_TRUE(first != last)) {
-      return storage_.Insert(pos, IteratorValueAdapter<ForwardIterator>(first),
-                             std::distance(first, last));
-    } else {
-      return const_cast<iterator>(pos);
-    }
-  }
-
-  // Overload of `InlinedVector::insert(...)` that inserts the range [`first`,
-  // `last`) starting at `pos`, returning an `iterator` pointing to the first
-  // of the newly inserted elements.
-  //
-  // NOTE: this overload is for iterators that are "input" category.
-  template <typename InputIterator,
-            DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
-  iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
-    ABSL_HARDENING_ASSERT(pos >= begin());
-    ABSL_HARDENING_ASSERT(pos <= end());
-
-    size_type index = std::distance(cbegin(), pos);
-    for (size_type i = index; first != last; ++i, static_cast<void>(++first)) {
-      insert(data() + i, *first);
-    }
-
-    return iterator(data() + index);
-  }
-
-  // `InlinedVector::emplace(...)`
-  //
-  // Constructs and inserts an element using `args...` in the inlined vector at
-  // `pos`, returning an `iterator` pointing to the newly emplaced element.
-  template <typename... Args>
-  iterator emplace(const_iterator pos, Args&&... args) {
-    ABSL_HARDENING_ASSERT(pos >= begin());
-    ABSL_HARDENING_ASSERT(pos <= end());
-
-    value_type dealias(std::forward<Args>(args)...);
-    return storage_.Insert(pos,
-                           IteratorValueAdapter<MoveIterator>(
-                               MoveIterator(std::addressof(dealias))),
-                           1);
-  }
-
-  // `InlinedVector::emplace_back(...)`
-  //
-  // Constructs and inserts an element using `args...` in the inlined vector at
-  // `end()`, returning a `reference` to the newly emplaced element.
-  template <typename... Args>
-  reference emplace_back(Args&&... args) {
-    return storage_.EmplaceBack(std::forward<Args>(args)...);
-  }
-
-  // `InlinedVector::push_back(...)`
-  //
-  // Inserts a copy of `v` in the inlined vector at `end()`.
-  void push_back(const_reference v) { static_cast<void>(emplace_back(v)); }
-
-  // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()`
-  // using move semantics.
-  void push_back(RValueReference v) {
-    static_cast<void>(emplace_back(std::move(v)));
-  }
-
-  // `InlinedVector::pop_back()`
-  //
-  // Destroys the element at `back()`, reducing the size by `1`.
-  void pop_back() noexcept {
-    ABSL_HARDENING_ASSERT(!empty());
-
-    AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1));
-    storage_.SubtractSize(1);
-  }
-
-  // `InlinedVector::erase(...)`
-  //
-  // Erases the element at `pos`, returning an `iterator` pointing to where the
-  // erased element was located.
-  //
-  // NOTE: may return `end()`, which is not dereferencable.
-  iterator erase(const_iterator pos) {
-    ABSL_HARDENING_ASSERT(pos >= begin());
-    ABSL_HARDENING_ASSERT(pos < end());
-
-    return storage_.Erase(pos, pos + 1);
-  }
-
-  // Overload of `InlinedVector::erase(...)` that erases every element in the
-  // range [`from`, `to`), returning an `iterator` pointing to where the first
-  // erased element was located.
-  //
-  // NOTE: may return `end()`, which is not dereferencable.
-  iterator erase(const_iterator from, const_iterator to) {
-    ABSL_HARDENING_ASSERT(from >= begin());
-    ABSL_HARDENING_ASSERT(from <= to);
-    ABSL_HARDENING_ASSERT(to <= end());
-
-    if (ABSL_PREDICT_TRUE(from != to)) {
-      return storage_.Erase(from, to);
-    } else {
-      return const_cast<iterator>(from);
-    }
-  }
-
-  // `InlinedVector::clear()`
-  //
-  // Destroys all elements in the inlined vector, setting the size to `0` and
-  // deallocating any held memory.
-  void clear() noexcept {
-    inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(),
-                                             size());
-    storage_.DeallocateIfAllocated();
-
-    storage_.SetInlinedSize(0);
-  }
-
-  // `InlinedVector::reserve(...)`
-  //
-  // Ensures that there is enough room for at least `n` elements.
-  void reserve(size_type n) { storage_.Reserve(n); }
-
-  // `InlinedVector::shrink_to_fit()`
-  //
-  // Reduces memory usage by freeing unused memory. After being called, calls to
-  // `capacity()` will be equal to `max(N, size())`.
-  //
-  // If `size() <= N` and the inlined vector contains allocated memory, the
-  // elements will all be moved to the inlined space and the allocated memory
-  // will be deallocated.
-  //
-  // If `size() > N` and `size() < capacity()`, the elements will be moved to a
-  // smaller allocation.
-  void shrink_to_fit() {
-    if (storage_.GetIsAllocated()) {
-      storage_.ShrinkToFit();
-    }
-  }
-
-  // `InlinedVector::swap(...)`
-  //
-  // Swaps the contents of the inlined vector with `other`.
-  void swap(InlinedVector& other) {
-    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
-      storage_.Swap(std::addressof(other.storage_));
-    }
-  }
-
- private:
-  template <typename H, typename TheT, size_t TheN, typename TheA>
-  friend H AbslHashValue(H h, const absl::InlinedVector<TheT, TheN, TheA>& a);
-
-  Storage storage_;
-};
-
-// -----------------------------------------------------------------------------
-// InlinedVector Non-Member Functions
-// -----------------------------------------------------------------------------
-
-// `swap(...)`
-//
-// Swaps the contents of two inlined vectors.
-template <typename T, size_t N, typename A>
-void swap(absl::InlinedVector<T, N, A>& a,
-          absl::InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) {
-  a.swap(b);
-}
-
-// `operator==(...)`
-//
-// Tests for value-equality of two inlined vectors.
-template <typename T, size_t N, typename A>
-bool operator==(const absl::InlinedVector<T, N, A>& a,
-                const absl::InlinedVector<T, N, A>& b) {
-  auto a_data = a.data();
-  auto b_data = b.data();
-  return absl::equal(a_data, a_data + a.size(), b_data, b_data + b.size());
-}
-
-// `operator!=(...)`
-//
-// Tests for value-inequality of two inlined vectors.
-template <typename T, size_t N, typename A>
-bool operator!=(const absl::InlinedVector<T, N, A>& a,
-                const absl::InlinedVector<T, N, A>& b) {
-  return !(a == b);
-}
-
-// `operator<(...)`
-//
-// Tests whether the value of an inlined vector is less than the value of
-// another inlined vector using a lexicographical comparison algorithm.
-template <typename T, size_t N, typename A>
-bool operator<(const absl::InlinedVector<T, N, A>& a,
-               const absl::InlinedVector<T, N, A>& b) {
-  auto a_data = a.data();
-  auto b_data = b.data();
-  return std::lexicographical_compare(a_data, a_data + a.size(), b_data,
-                                      b_data + b.size());
-}
-
-// `operator>(...)`
-//
-// Tests whether the value of an inlined vector is greater than the value of
-// another inlined vector using a lexicographical comparison algorithm.
-template <typename T, size_t N, typename A>
-bool operator>(const absl::InlinedVector<T, N, A>& a,
-               const absl::InlinedVector<T, N, A>& b) {
-  return b < a;
-}
-
-// `operator<=(...)`
-//
-// Tests whether the value of an inlined vector is less than or equal to the
-// value of another inlined vector using a lexicographical comparison algorithm.
-template <typename T, size_t N, typename A>
-bool operator<=(const absl::InlinedVector<T, N, A>& a,
-                const absl::InlinedVector<T, N, A>& b) {
-  return !(b < a);
-}
-
-// `operator>=(...)`
-//
-// Tests whether the value of an inlined vector is greater than or equal to the
-// value of another inlined vector using a lexicographical comparison algorithm.
-template <typename T, size_t N, typename A>
-bool operator>=(const absl::InlinedVector<T, N, A>& a,
-                const absl::InlinedVector<T, N, A>& b) {
-  return !(a < b);
-}
-
-// `AbslHashValue(...)`
-//
-// Provides `absl::Hash` support for `absl::InlinedVector`. It is uncommon to
-// call this directly.
-template <typename H, typename T, size_t N, typename A>
-H AbslHashValue(H h, const absl::InlinedVector<T, N, A>& a) {
-  auto size = a.size();
-  return H::combine(H::combine_contiguous(std::move(h), a.data(), size), size);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INLINED_VECTOR_H_
diff --git a/third_party/abseil_cpp/absl/container/inlined_vector_benchmark.cc b/third_party/abseil_cpp/absl/container/inlined_vector_benchmark.cc
deleted file mode 100644
index b8dafe9323..0000000000
--- a/third_party/abseil_cpp/absl/container/inlined_vector_benchmark.cc
+++ /dev/null
@@ -1,807 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <array>
-#include <string>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/container/inlined_vector.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-void BM_InlinedVectorFill(benchmark::State& state) {
-  const int len = state.range(0);
-  absl::InlinedVector<int, 8> v;
-  v.reserve(len);
-  for (auto _ : state) {
-    v.resize(0);  // Use resize(0) as InlinedVector releases storage on clear().
-    for (int i = 0; i < len; ++i) {
-      v.push_back(i);
-    }
-    benchmark::DoNotOptimize(v);
-  }
-}
-BENCHMARK(BM_InlinedVectorFill)->Range(1, 256);
-
-void BM_InlinedVectorFillRange(benchmark::State& state) {
-  const int len = state.range(0);
-  const std::vector<int> src(len, len);
-  absl::InlinedVector<int, 8> v;
-  v.reserve(len);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(src);
-    v.assign(src.begin(), src.end());
-    benchmark::DoNotOptimize(v);
-  }
-}
-BENCHMARK(BM_InlinedVectorFillRange)->Range(1, 256);
-
-void BM_StdVectorFill(benchmark::State& state) {
-  const int len = state.range(0);
-  std::vector<int> v;
-  v.reserve(len);
-  for (auto _ : state) {
-    v.clear();
-    for (int i = 0; i < len; ++i) {
-      v.push_back(i);
-    }
-    benchmark::DoNotOptimize(v);
-  }
-}
-BENCHMARK(BM_StdVectorFill)->Range(1, 256);
-
-// The purpose of the next two benchmarks is to verify that
-// absl::InlinedVector is efficient when moving is more efficent than
-// copying. To do so, we use strings that are larger than the short
-// string optimization.
-bool StringRepresentedInline(std::string s) {
-  const char* chars = s.data();
-  std::string s1 = std::move(s);
-  return s1.data() != chars;
-}
-
-int GetNonShortStringOptimizationSize() {
-  for (int i = 24; i <= 192; i *= 2) {
-    if (!StringRepresentedInline(std::string(i, 'A'))) {
-      return i;
-    }
-  }
-  ABSL_RAW_LOG(
-      FATAL,
-      "Failed to find a string larger than the short string optimization");
-  return -1;
-}
-
-void BM_InlinedVectorFillString(benchmark::State& state) {
-  const int len = state.range(0);
-  const int no_sso = GetNonShortStringOptimizationSize();
-  std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'),
-                            std::string(no_sso, 'C'), std::string(no_sso, 'D')};
-
-  for (auto _ : state) {
-    absl::InlinedVector<std::string, 8> v;
-    for (int i = 0; i < len; i++) {
-      v.push_back(strings[i & 3]);
-    }
-  }
-  state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
-}
-BENCHMARK(BM_InlinedVectorFillString)->Range(0, 1024);
-
-void BM_StdVectorFillString(benchmark::State& state) {
-  const int len = state.range(0);
-  const int no_sso = GetNonShortStringOptimizationSize();
-  std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'),
-                            std::string(no_sso, 'C'), std::string(no_sso, 'D')};
-
-  for (auto _ : state) {
-    std::vector<std::string> v;
-    for (int i = 0; i < len; i++) {
-      v.push_back(strings[i & 3]);
-    }
-  }
-  state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
-}
-BENCHMARK(BM_StdVectorFillString)->Range(0, 1024);
-
-struct Buffer {  // some arbitrary structure for benchmarking.
-  char* base;
-  int length;
-  int capacity;
-  void* user_data;
-};
-
-void BM_InlinedVectorAssignments(benchmark::State& state) {
-  const int len = state.range(0);
-  using BufferVec = absl::InlinedVector<Buffer, 2>;
-
-  BufferVec src;
-  src.resize(len);
-
-  BufferVec dst;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(dst);
-    benchmark::DoNotOptimize(src);
-    dst = src;
-  }
-}
-BENCHMARK(BM_InlinedVectorAssignments)
-    ->Arg(0)
-    ->Arg(1)
-    ->Arg(2)
-    ->Arg(3)
-    ->Arg(4)
-    ->Arg(20);
-
-void BM_CreateFromContainer(benchmark::State& state) {
-  for (auto _ : state) {
-    absl::InlinedVector<int, 4> src{1, 2, 3};
-    benchmark::DoNotOptimize(src);
-    absl::InlinedVector<int, 4> dst(std::move(src));
-    benchmark::DoNotOptimize(dst);
-  }
-}
-BENCHMARK(BM_CreateFromContainer);
-
-struct LargeCopyableOnly {
-  LargeCopyableOnly() : d(1024, 17) {}
-  LargeCopyableOnly(const LargeCopyableOnly& o) = default;
-  LargeCopyableOnly& operator=(const LargeCopyableOnly& o) = default;
-
-  std::vector<int> d;
-};
-
-struct LargeCopyableSwappable {
-  LargeCopyableSwappable() : d(1024, 17) {}
-
-  LargeCopyableSwappable(const LargeCopyableSwappable& o) = default;
-
-  LargeCopyableSwappable& operator=(LargeCopyableSwappable o) {
-    using std::swap;
-    swap(*this, o);
-    return *this;
-  }
-
-  friend void swap(LargeCopyableSwappable& a, LargeCopyableSwappable& b) {
-    using std::swap;
-    swap(a.d, b.d);
-  }
-
-  std::vector<int> d;
-};
-
-struct LargeCopyableMovable {
-  LargeCopyableMovable() : d(1024, 17) {}
-  // Use implicitly defined copy and move.
-
-  std::vector<int> d;
-};
-
-struct LargeCopyableMovableSwappable {
-  LargeCopyableMovableSwappable() : d(1024, 17) {}
-  LargeCopyableMovableSwappable(const LargeCopyableMovableSwappable& o) =
-      default;
-  LargeCopyableMovableSwappable(LargeCopyableMovableSwappable&& o) = default;
-
-  LargeCopyableMovableSwappable& operator=(LargeCopyableMovableSwappable o) {
-    using std::swap;
-    swap(*this, o);
-    return *this;
-  }
-  LargeCopyableMovableSwappable& operator=(LargeCopyableMovableSwappable&& o) =
-      default;
-
-  friend void swap(LargeCopyableMovableSwappable& a,
-                   LargeCopyableMovableSwappable& b) {
-    using std::swap;
-    swap(a.d, b.d);
-  }
-
-  std::vector<int> d;
-};
-
-template <typename ElementType>
-void BM_SwapElements(benchmark::State& state) {
-  const int len = state.range(0);
-  using Vec = absl::InlinedVector<ElementType, 32>;
-  Vec a(len);
-  Vec b;
-  for (auto _ : state) {
-    using std::swap;
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(b);
-    swap(a, b);
-  }
-}
-BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableOnly)->Range(0, 1024);
-BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableSwappable)->Range(0, 1024);
-BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableMovable)->Range(0, 1024);
-BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableMovableSwappable)
-    ->Range(0, 1024);
-
-// The following benchmark is meant to track the efficiency of the vector size
-// as a function of stored type via the benchmark label. It is not meant to
-// output useful sizeof operator performance. The loop is a dummy operation
-// to fulfill the requirement of running the benchmark.
-template <typename VecType>
-void BM_Sizeof(benchmark::State& state) {
-  int size = 0;
-  for (auto _ : state) {
-    VecType vec;
-    size = sizeof(vec);
-  }
-  state.SetLabel(absl::StrCat("sz=", size));
-}
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 1>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 4>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 7>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 8>);
-
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 1>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 4>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 7>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 8>);
-
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 1>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 4>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 7>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 8>);
-
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 1>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 4>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 7>);
-BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 8>);
-
-void BM_InlinedVectorIndexInlined(benchmark::State& state) {
-  absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v[4]);
-  }
-}
-BENCHMARK(BM_InlinedVectorIndexInlined);
-
-void BM_InlinedVectorIndexExternal(benchmark::State& state) {
-  absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v[4]);
-  }
-}
-BENCHMARK(BM_InlinedVectorIndexExternal);
-
-void BM_StdVectorIndex(benchmark::State& state) {
-  std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v[4]);
-  }
-}
-BENCHMARK(BM_StdVectorIndex);
-
-void BM_InlinedVectorDataInlined(benchmark::State& state) {
-  absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v.data());
-  }
-}
-BENCHMARK(BM_InlinedVectorDataInlined);
-
-void BM_InlinedVectorDataExternal(benchmark::State& state) {
-  absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v.data());
-  }
-  state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
-}
-BENCHMARK(BM_InlinedVectorDataExternal);
-
-void BM_StdVectorData(benchmark::State& state) {
-  std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v.data());
-  }
-  state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
-}
-BENCHMARK(BM_StdVectorData);
-
-void BM_InlinedVectorSizeInlined(benchmark::State& state) {
-  absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v.size());
-  }
-}
-BENCHMARK(BM_InlinedVectorSizeInlined);
-
-void BM_InlinedVectorSizeExternal(benchmark::State& state) {
-  absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v.size());
-  }
-}
-BENCHMARK(BM_InlinedVectorSizeExternal);
-
-void BM_StdVectorSize(benchmark::State& state) {
-  std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v.size());
-  }
-}
-BENCHMARK(BM_StdVectorSize);
-
-void BM_InlinedVectorEmptyInlined(benchmark::State& state) {
-  absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v.empty());
-  }
-}
-BENCHMARK(BM_InlinedVectorEmptyInlined);
-
-void BM_InlinedVectorEmptyExternal(benchmark::State& state) {
-  absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v.empty());
-  }
-}
-BENCHMARK(BM_InlinedVectorEmptyExternal);
-
-void BM_StdVectorEmpty(benchmark::State& state) {
-  std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(v);
-    benchmark::DoNotOptimize(v.empty());
-  }
-}
-BENCHMARK(BM_StdVectorEmpty);
-
-constexpr size_t kInlinedCapacity = 4;
-constexpr size_t kLargeSize = kInlinedCapacity * 2;
-constexpr size_t kSmallSize = kInlinedCapacity / 2;
-constexpr size_t kBatchSize = 100;
-
-#define ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_FunctionTemplate, T) \
-  BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize);        \
-  BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize)
-
-#define ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_FunctionTemplate, T)      \
-  BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize, kLargeSize); \
-  BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize, kSmallSize); \
-  BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize, kLargeSize); \
-  BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize, kSmallSize)
-
-template <typename T>
-using InlVec = absl::InlinedVector<T, kInlinedCapacity>;
-
-struct TrivialType {
-  size_t val;
-};
-
-class NontrivialType {
- public:
-  ABSL_ATTRIBUTE_NOINLINE NontrivialType() : val_() {
-    benchmark::DoNotOptimize(*this);
-  }
-
-  ABSL_ATTRIBUTE_NOINLINE NontrivialType(const NontrivialType& other)
-      : val_(other.val_) {
-    benchmark::DoNotOptimize(*this);
-  }
-
-  ABSL_ATTRIBUTE_NOINLINE NontrivialType& operator=(
-      const NontrivialType& other) {
-    val_ = other.val_;
-    benchmark::DoNotOptimize(*this);
-    return *this;
-  }
-
-  ABSL_ATTRIBUTE_NOINLINE ~NontrivialType() noexcept {
-    benchmark::DoNotOptimize(*this);
-  }
-
- private:
-  size_t val_;
-};
-
-template <typename T, typename PrepareVecFn, typename TestVecFn>
-void BatchedBenchmark(benchmark::State& state, PrepareVecFn prepare_vec,
-                      TestVecFn test_vec) {
-  std::array<InlVec<T>, kBatchSize> vector_batch{};
-
-  while (state.KeepRunningBatch(kBatchSize)) {
-    // Prepare batch
-    state.PauseTiming();
-    for (size_t i = 0; i < kBatchSize; ++i) {
-      prepare_vec(vector_batch.data() + i, i);
-    }
-    benchmark::DoNotOptimize(vector_batch);
-    state.ResumeTiming();
-
-    // Test batch
-    for (size_t i = 0; i < kBatchSize; ++i) {
-      test_vec(vector_batch.data() + i, i);
-    }
-  }
-}
-
-template <typename T, size_t ToSize>
-void BM_ConstructFromSize(benchmark::State& state) {
-  using VecT = InlVec<T>;
-  auto size = ToSize;
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->~VecT(); },
-      /* test_vec = */
-      [&](void* ptr, size_t) {
-        benchmark::DoNotOptimize(size);
-        ::new (ptr) VecT(size);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSize, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSize, NontrivialType);
-
-template <typename T, size_t ToSize>
-void BM_ConstructFromSizeRef(benchmark::State& state) {
-  using VecT = InlVec<T>;
-  auto size = ToSize;
-  auto ref = T();
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->~VecT(); },
-      /* test_vec = */
-      [&](void* ptr, size_t) {
-        benchmark::DoNotOptimize(size);
-        benchmark::DoNotOptimize(ref);
-        ::new (ptr) VecT(size, ref);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSizeRef, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSizeRef, NontrivialType);
-
-template <typename T, size_t ToSize>
-void BM_ConstructFromRange(benchmark::State& state) {
-  using VecT = InlVec<T>;
-  std::array<T, ToSize> arr{};
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->~VecT(); },
-      /* test_vec = */
-      [&](void* ptr, size_t) {
-        benchmark::DoNotOptimize(arr);
-        ::new (ptr) VecT(arr.begin(), arr.end());
-      });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromRange, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromRange, NontrivialType);
-
-template <typename T, size_t ToSize>
-void BM_ConstructFromCopy(benchmark::State& state) {
-  using VecT = InlVec<T>;
-  VecT other_vec(ToSize);
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) { vec->~VecT(); },
-      /* test_vec = */
-      [&](void* ptr, size_t) {
-        benchmark::DoNotOptimize(other_vec);
-        ::new (ptr) VecT(other_vec);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromCopy, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromCopy, NontrivialType);
-
-template <typename T, size_t ToSize>
-void BM_ConstructFromMove(benchmark::State& state) {
-  using VecT = InlVec<T>;
-  std::array<VecT, kBatchSize> vector_batch{};
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [&](InlVec<T>* vec, size_t i) {
-        vector_batch[i].clear();
-        vector_batch[i].resize(ToSize);
-        vec->~VecT();
-      },
-      /* test_vec = */
-      [&](void* ptr, size_t i) {
-        benchmark::DoNotOptimize(vector_batch[i]);
-        ::new (ptr) VecT(std::move(vector_batch[i]));
-      });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToSize>
-void BM_AssignSizeRef(benchmark::State& state) {
-  auto size = ToSize;
-  auto ref = T();
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->resize(FromSize); },
-      /* test_vec = */
-      [&](InlVec<T>* vec, size_t) {
-        benchmark::DoNotOptimize(size);
-        benchmark::DoNotOptimize(ref);
-        vec->assign(size, ref);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignSizeRef, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignSizeRef, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToSize>
-void BM_AssignRange(benchmark::State& state) {
-  std::array<T, ToSize> arr{};
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->resize(FromSize); },
-      /* test_vec = */
-      [&](InlVec<T>* vec, size_t) {
-        benchmark::DoNotOptimize(arr);
-        vec->assign(arr.begin(), arr.end());
-      });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignRange, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignRange, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToSize>
-void BM_AssignFromCopy(benchmark::State& state) {
-  InlVec<T> other_vec(ToSize);
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->resize(FromSize); },
-      /* test_vec = */
-      [&](InlVec<T>* vec, size_t) {
-        benchmark::DoNotOptimize(other_vec);
-        *vec = other_vec;
-      });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromCopy, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromCopy, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToSize>
-void BM_AssignFromMove(benchmark::State& state) {
-  using VecT = InlVec<T>;
-  std::array<VecT, kBatchSize> vector_batch{};
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [&](InlVec<T>* vec, size_t i) {
-        vector_batch[i].clear();
-        vector_batch[i].resize(ToSize);
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [&](InlVec<T>* vec, size_t i) {
-        benchmark::DoNotOptimize(vector_batch[i]);
-        *vec = std::move(vector_batch[i]);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromMove, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromMove, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToSize>
-void BM_ResizeSize(benchmark::State& state) {
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [](InlVec<T>* vec, size_t) { vec->resize(ToSize); });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSize, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSize, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToSize>
-void BM_ResizeSizeRef(benchmark::State& state) {
-  auto t = T();
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [&](InlVec<T>* vec, size_t) {
-        benchmark::DoNotOptimize(t);
-        vec->resize(ToSize, t);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSizeRef, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSizeRef, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToSize>
-void BM_InsertSizeRef(benchmark::State& state) {
-  auto t = T();
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [&](InlVec<T>* vec, size_t) {
-        benchmark::DoNotOptimize(t);
-        auto* pos = vec->data() + (vec->size() / 2);
-        vec->insert(pos, t);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertSizeRef, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertSizeRef, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToSize>
-void BM_InsertRange(benchmark::State& state) {
-  InlVec<T> other_vec(ToSize);
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [&](InlVec<T>* vec, size_t) {
-        benchmark::DoNotOptimize(other_vec);
-        auto* pos = vec->data() + (vec->size() / 2);
-        vec->insert(pos, other_vec.begin(), other_vec.end());
-      });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertRange, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertRange, NontrivialType);
-
-template <typename T, size_t FromSize>
-void BM_EmplaceBack(benchmark::State& state) {
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [](InlVec<T>* vec, size_t) { vec->emplace_back(); });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EmplaceBack, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EmplaceBack, NontrivialType);
-
-template <typename T, size_t FromSize>
-void BM_PopBack(benchmark::State& state) {
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [](InlVec<T>* vec, size_t) { vec->pop_back(); });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_PopBack, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_PopBack, NontrivialType);
-
-template <typename T, size_t FromSize>
-void BM_EraseOne(benchmark::State& state) {
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [](InlVec<T>* vec, size_t) {
-        auto* pos = vec->data() + (vec->size() / 2);
-        vec->erase(pos);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseOne, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseOne, NontrivialType);
-
-template <typename T, size_t FromSize>
-void BM_EraseRange(benchmark::State& state) {
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [](InlVec<T>* vec, size_t) {
-        auto* pos = vec->data() + (vec->size() / 2);
-        vec->erase(pos, pos + 1);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseRange, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseRange, NontrivialType);
-
-template <typename T, size_t FromSize>
-void BM_Clear(benchmark::State& state) {
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->resize(FromSize); },
-      /* test_vec = */ [](InlVec<T>* vec, size_t) { vec->clear(); });
-}
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, TrivialType);
-ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToCapacity>
-void BM_Reserve(benchmark::State& state) {
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [](InlVec<T>* vec, size_t) { vec->reserve(ToCapacity); });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Reserve, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Reserve, NontrivialType);
-
-template <typename T, size_t FromCapacity, size_t ToCapacity>
-void BM_ShrinkToFit(benchmark::State& state) {
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [](InlVec<T>* vec, size_t) {
-        vec->clear();
-        vec->resize(ToCapacity);
-        vec->reserve(FromCapacity);
-      },
-      /* test_vec = */ [](InlVec<T>* vec, size_t) { vec->shrink_to_fit(); });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ShrinkToFit, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ShrinkToFit, NontrivialType);
-
-template <typename T, size_t FromSize, size_t ToSize>
-void BM_Swap(benchmark::State& state) {
-  using VecT = InlVec<T>;
-  std::array<VecT, kBatchSize> vector_batch{};
-  BatchedBenchmark<T>(
-      state,
-      /* prepare_vec = */
-      [&](InlVec<T>* vec, size_t i) {
-        vector_batch[i].clear();
-        vector_batch[i].resize(ToSize);
-        vec->resize(FromSize);
-      },
-      /* test_vec = */
-      [&](InlVec<T>* vec, size_t i) {
-        using std::swap;
-        benchmark::DoNotOptimize(vector_batch[i]);
-        swap(*vec, vector_batch[i]);
-      });
-}
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Swap, TrivialType);
-ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Swap, NontrivialType);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/container/inlined_vector_exception_safety_test.cc b/third_party/abseil_cpp/absl/container/inlined_vector_exception_safety_test.cc
deleted file mode 100644
index 0e6a05b5f6..0000000000
--- a/third_party/abseil_cpp/absl/container/inlined_vector_exception_safety_test.cc
+++ /dev/null
@@ -1,508 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/inlined_vector.h"
-
-#include "absl/base/config.h"
-
-#if defined(ABSL_HAVE_EXCEPTIONS)
-
-#include <array>
-#include <initializer_list>
-#include <iterator>
-#include <memory>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/exception_safety_testing.h"
-
-namespace {
-
-constexpr size_t kInlinedCapacity = 4;
-constexpr size_t kLargeSize = kInlinedCapacity * 2;
-constexpr size_t kSmallSize = kInlinedCapacity / 2;
-
-using Thrower = testing::ThrowingValue<>;
-using MovableThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
-using ThrowAlloc = testing::ThrowingAllocator<Thrower>;
-
-using ThrowerVec = absl::InlinedVector<Thrower, kInlinedCapacity>;
-using MovableThrowerVec = absl::InlinedVector<MovableThrower, kInlinedCapacity>;
-
-using ThrowAllocThrowerVec =
-    absl::InlinedVector<Thrower, kInlinedCapacity, ThrowAlloc>;
-using ThrowAllocMovableThrowerVec =
-    absl::InlinedVector<MovableThrower, kInlinedCapacity, ThrowAlloc>;
-
-// In GCC, if an element of a `std::initializer_list` throws during construction
-// the elements that were constructed before it are not destroyed. This causes
-// incorrect exception safety test failures. Thus, `testing::nothrow_ctor` is
-// required. See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66139
-#define ABSL_INTERNAL_MAKE_INIT_LIST(T, N)                     \
-  (N > kInlinedCapacity                                        \
-       ? std::initializer_list<T>{T(0, testing::nothrow_ctor), \
-                                  T(1, testing::nothrow_ctor), \
-                                  T(2, testing::nothrow_ctor), \
-                                  T(3, testing::nothrow_ctor), \
-                                  T(4, testing::nothrow_ctor), \
-                                  T(5, testing::nothrow_ctor), \
-                                  T(6, testing::nothrow_ctor), \
-                                  T(7, testing::nothrow_ctor)} \
-                                                               \
-       : std::initializer_list<T>{T(0, testing::nothrow_ctor), \
-                                  T(1, testing::nothrow_ctor)})
-static_assert(kLargeSize == 8, "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)");
-static_assert(kSmallSize == 2, "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)");
-
-template <typename TheVecT, size_t... TheSizes>
-class TestParams {
- public:
-  using VecT = TheVecT;
-  constexpr static size_t GetSizeAt(size_t i) { return kSizes[1 + i]; }
-
- private:
-  constexpr static size_t kSizes[1 + sizeof...(TheSizes)] = {1, TheSizes...};
-};
-
-using NoSizeTestParams =
-    ::testing::Types<TestParams<ThrowerVec>, TestParams<MovableThrowerVec>,
-                     TestParams<ThrowAllocThrowerVec>,
-                     TestParams<ThrowAllocMovableThrowerVec>>;
-
-using OneSizeTestParams =
-    ::testing::Types<TestParams<ThrowerVec, kLargeSize>,
-                     TestParams<ThrowerVec, kSmallSize>,
-                     TestParams<MovableThrowerVec, kLargeSize>,
-                     TestParams<MovableThrowerVec, kSmallSize>,
-                     TestParams<ThrowAllocThrowerVec, kLargeSize>,
-                     TestParams<ThrowAllocThrowerVec, kSmallSize>,
-                     TestParams<ThrowAllocMovableThrowerVec, kLargeSize>,
-                     TestParams<ThrowAllocMovableThrowerVec, kSmallSize>>;
-
-using TwoSizeTestParams = ::testing::Types<
-    TestParams<ThrowerVec, kLargeSize, kLargeSize>,
-    TestParams<ThrowerVec, kLargeSize, kSmallSize>,
-    TestParams<ThrowerVec, kSmallSize, kLargeSize>,
-    TestParams<ThrowerVec, kSmallSize, kSmallSize>,
-    TestParams<MovableThrowerVec, kLargeSize, kLargeSize>,
-    TestParams<MovableThrowerVec, kLargeSize, kSmallSize>,
-    TestParams<MovableThrowerVec, kSmallSize, kLargeSize>,
-    TestParams<MovableThrowerVec, kSmallSize, kSmallSize>,
-    TestParams<ThrowAllocThrowerVec, kLargeSize, kLargeSize>,
-    TestParams<ThrowAllocThrowerVec, kLargeSize, kSmallSize>,
-    TestParams<ThrowAllocThrowerVec, kSmallSize, kLargeSize>,
-    TestParams<ThrowAllocThrowerVec, kSmallSize, kSmallSize>,
-    TestParams<ThrowAllocMovableThrowerVec, kLargeSize, kLargeSize>,
-    TestParams<ThrowAllocMovableThrowerVec, kLargeSize, kSmallSize>,
-    TestParams<ThrowAllocMovableThrowerVec, kSmallSize, kLargeSize>,
-    TestParams<ThrowAllocMovableThrowerVec, kSmallSize, kSmallSize>>;
-
-template <typename>
-struct NoSizeTest : ::testing::Test {};
-TYPED_TEST_SUITE(NoSizeTest, NoSizeTestParams);
-
-template <typename>
-struct OneSizeTest : ::testing::Test {};
-TYPED_TEST_SUITE(OneSizeTest, OneSizeTestParams);
-
-template <typename>
-struct TwoSizeTest : ::testing::Test {};
-TYPED_TEST_SUITE(TwoSizeTest, TwoSizeTestParams);
-
-template <typename VecT>
-bool InlinedVectorInvariants(VecT* vec) {
-  if (*vec != *vec) return false;
-  if (vec->size() > vec->capacity()) return false;
-  if (vec->size() > vec->max_size()) return false;
-  if (vec->capacity() > vec->max_size()) return false;
-  if (vec->data() != std::addressof(vec->at(0))) return false;
-  if (vec->data() != vec->begin()) return false;
-  if (*vec->data() != *vec->begin()) return false;
-  if (vec->begin() > vec->end()) return false;
-  if ((vec->end() - vec->begin()) != vec->size()) return false;
-  if (std::distance(vec->begin(), vec->end()) != vec->size()) return false;
-  return true;
-}
-
-// Function that always returns false is correct, but refactoring is required
-// for clarity. It's needed to express that, as a contract, certain operations
-// should not throw at all. Execution of this function means an exception was
-// thrown and thus the test should fail.
-// TODO(johnsoncj): Add `testing::NoThrowGuarantee` to the framework
-template <typename VecT>
-bool NoThrowGuarantee(VecT* /* vec */) {
-  return false;
-}
-
-TYPED_TEST(NoSizeTest, DefaultConstructor) {
-  using VecT = typename TypeParam::VecT;
-  using allocator_type = typename VecT::allocator_type;
-
-  testing::TestThrowingCtor<VecT>();
-
-  testing::TestThrowingCtor<VecT>(allocator_type{});
-}
-
-TYPED_TEST(OneSizeTest, SizeConstructor) {
-  using VecT = typename TypeParam::VecT;
-  using allocator_type = typename VecT::allocator_type;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  testing::TestThrowingCtor<VecT>(size);
-
-  testing::TestThrowingCtor<VecT>(size, allocator_type{});
-}
-
-TYPED_TEST(OneSizeTest, SizeRefConstructor) {
-  using VecT = typename TypeParam::VecT;
-  using value_type = typename VecT::value_type;
-  using allocator_type = typename VecT::allocator_type;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  testing::TestThrowingCtor<VecT>(size, value_type{});
-
-  testing::TestThrowingCtor<VecT>(size, value_type{}, allocator_type{});
-}
-
-TYPED_TEST(OneSizeTest, InitializerListConstructor) {
-  using VecT = typename TypeParam::VecT;
-  using value_type = typename VecT::value_type;
-  using allocator_type = typename VecT::allocator_type;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  testing::TestThrowingCtor<VecT>(
-      ABSL_INTERNAL_MAKE_INIT_LIST(value_type, size));
-
-  testing::TestThrowingCtor<VecT>(
-      ABSL_INTERNAL_MAKE_INIT_LIST(value_type, size), allocator_type{});
-}
-
-TYPED_TEST(OneSizeTest, RangeConstructor) {
-  using VecT = typename TypeParam::VecT;
-  using value_type = typename VecT::value_type;
-  using allocator_type = typename VecT::allocator_type;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  std::array<value_type, size> arr{};
-
-  testing::TestThrowingCtor<VecT>(arr.begin(), arr.end());
-
-  testing::TestThrowingCtor<VecT>(arr.begin(), arr.end(), allocator_type{});
-}
-
-TYPED_TEST(OneSizeTest, CopyConstructor) {
-  using VecT = typename TypeParam::VecT;
-  using allocator_type = typename VecT::allocator_type;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  VecT other_vec{size};
-
-  testing::TestThrowingCtor<VecT>(other_vec);
-
-  testing::TestThrowingCtor<VecT>(other_vec, allocator_type{});
-}
-
-TYPED_TEST(OneSizeTest, MoveConstructor) {
-  using VecT = typename TypeParam::VecT;
-  using allocator_type = typename VecT::allocator_type;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  if (!absl::allocator_is_nothrow<allocator_type>::value) {
-    testing::TestThrowingCtor<VecT>(VecT{size});
-
-    testing::TestThrowingCtor<VecT>(VecT{size}, allocator_type{});
-  }
-}
-
-TYPED_TEST(TwoSizeTest, Assign) {
-  using VecT = typename TypeParam::VecT;
-  using value_type = typename VecT::value_type;
-  constexpr static auto from_size = TypeParam::GetSizeAt(0);
-  constexpr static auto to_size = TypeParam::GetSizeAt(1);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{from_size})
-                    .WithContracts(InlinedVectorInvariants<VecT>);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    *vec = ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size);
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    VecT other_vec{to_size};
-    *vec = other_vec;
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    VecT other_vec{to_size};
-    *vec = std::move(other_vec);
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    value_type val{};
-    vec->assign(to_size, val);
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    vec->assign(ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size));
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    std::array<value_type, to_size> arr{};
-    vec->assign(arr.begin(), arr.end());
-  }));
-}
-
-TYPED_TEST(TwoSizeTest, Resize) {
-  using VecT = typename TypeParam::VecT;
-  using value_type = typename VecT::value_type;
-  constexpr static auto from_size = TypeParam::GetSizeAt(0);
-  constexpr static auto to_size = TypeParam::GetSizeAt(1);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{from_size})
-                    .WithContracts(InlinedVectorInvariants<VecT>,
-                                   testing::strong_guarantee);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    vec->resize(to_size);  //
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    vec->resize(to_size, value_type{});  //
-  }));
-}
-
-TYPED_TEST(OneSizeTest, Insert) {
-  using VecT = typename TypeParam::VecT;
-  using value_type = typename VecT::value_type;
-  constexpr static auto from_size = TypeParam::GetSizeAt(0);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{from_size})
-                    .WithContracts(InlinedVectorInvariants<VecT>);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin();
-    vec->insert(it, value_type{});
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() / 2);
-    vec->insert(it, value_type{});
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->end();
-    vec->insert(it, value_type{});
-  }));
-}
-
-TYPED_TEST(TwoSizeTest, Insert) {
-  using VecT = typename TypeParam::VecT;
-  using value_type = typename VecT::value_type;
-  constexpr static auto from_size = TypeParam::GetSizeAt(0);
-  constexpr static auto count = TypeParam::GetSizeAt(1);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{from_size})
-                    .WithContracts(InlinedVectorInvariants<VecT>);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin();
-    vec->insert(it, count, value_type{});
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() / 2);
-    vec->insert(it, count, value_type{});
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->end();
-    vec->insert(it, count, value_type{});
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin();
-    vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count));
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() / 2);
-    vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count));
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->end();
-    vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count));
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin();
-    std::array<value_type, count> arr{};
-    vec->insert(it, arr.begin(), arr.end());
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() / 2);
-    std::array<value_type, count> arr{};
-    vec->insert(it, arr.begin(), arr.end());
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->end();
-    std::array<value_type, count> arr{};
-    vec->insert(it, arr.begin(), arr.end());
-  }));
-}
-
-TYPED_TEST(OneSizeTest, EmplaceBack) {
-  using VecT = typename TypeParam::VecT;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  // For testing calls to `emplace_back(...)` that reallocate.
-  VecT full_vec{size};
-  full_vec.resize(full_vec.capacity());
-
-  // For testing calls to `emplace_back(...)` that don't reallocate.
-  VecT nonfull_vec{size};
-  nonfull_vec.reserve(size + 1);
-
-  auto tester = testing::MakeExceptionSafetyTester().WithContracts(
-      InlinedVectorInvariants<VecT>);
-
-  EXPECT_TRUE(tester.WithInitialValue(nonfull_vec).Test([](VecT* vec) {
-    vec->emplace_back();
-  }));
-
-  EXPECT_TRUE(tester.WithInitialValue(full_vec).Test(
-      [](VecT* vec) { vec->emplace_back(); }));
-}
-
-TYPED_TEST(OneSizeTest, PopBack) {
-  using VecT = typename TypeParam::VecT;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{size})
-                    .WithContracts(NoThrowGuarantee<VecT>);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    vec->pop_back();  //
-  }));
-}
-
-TYPED_TEST(OneSizeTest, Erase) {
-  using VecT = typename TypeParam::VecT;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{size})
-                    .WithContracts(InlinedVectorInvariants<VecT>);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin();
-    vec->erase(it);
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() / 2);
-    vec->erase(it);
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() - 1);
-    vec->erase(it);
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin();
-    vec->erase(it, it);
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() / 2);
-    vec->erase(it, it);
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() - 1);
-    vec->erase(it, it);
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin();
-    vec->erase(it, it + 1);
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() / 2);
-    vec->erase(it, it + 1);
-  }));
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    auto it = vec->begin() + (vec->size() - 1);
-    vec->erase(it, it + 1);
-  }));
-}
-
-TYPED_TEST(OneSizeTest, Clear) {
-  using VecT = typename TypeParam::VecT;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{size})
-                    .WithContracts(NoThrowGuarantee<VecT>);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    vec->clear();  //
-  }));
-}
-
-TYPED_TEST(TwoSizeTest, Reserve) {
-  using VecT = typename TypeParam::VecT;
-  constexpr static auto from_size = TypeParam::GetSizeAt(0);
-  constexpr static auto to_capacity = TypeParam::GetSizeAt(1);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{from_size})
-                    .WithContracts(InlinedVectorInvariants<VecT>);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) { vec->reserve(to_capacity); }));
-}
-
-TYPED_TEST(OneSizeTest, ShrinkToFit) {
-  using VecT = typename TypeParam::VecT;
-  constexpr static auto size = TypeParam::GetSizeAt(0);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{size})
-                    .WithContracts(InlinedVectorInvariants<VecT>);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    vec->shrink_to_fit();  //
-  }));
-}
-
-TYPED_TEST(TwoSizeTest, Swap) {
-  using VecT = typename TypeParam::VecT;
-  constexpr static auto from_size = TypeParam::GetSizeAt(0);
-  constexpr static auto to_size = TypeParam::GetSizeAt(1);
-
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(VecT{from_size})
-                    .WithContracts(InlinedVectorInvariants<VecT>);
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    VecT other_vec{to_size};
-    vec->swap(other_vec);
-  }));
-
-  EXPECT_TRUE(tester.Test([](VecT* vec) {
-    using std::swap;
-    VecT other_vec{to_size};
-    swap(*vec, other_vec);
-  }));
-}
-
-}  // namespace
-
-#endif  // defined(ABSL_HAVE_EXCEPTIONS)
diff --git a/third_party/abseil_cpp/absl/container/inlined_vector_test.cc b/third_party/abseil_cpp/absl/container/inlined_vector_test.cc
deleted file mode 100644
index 98aff33498..0000000000
--- a/third_party/abseil_cpp/absl/container/inlined_vector_test.cc
+++ /dev/null
@@ -1,1815 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/inlined_vector.h"
-
-#include <algorithm>
-#include <forward_list>
-#include <list>
-#include <memory>
-#include <scoped_allocator>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/internal/exception_testing.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/base/options.h"
-#include "absl/container/internal/counting_allocator.h"
-#include "absl/container/internal/test_instance_tracker.h"
-#include "absl/hash/hash_testing.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-using absl::container_internal::CountingAllocator;
-using absl::test_internal::CopyableMovableInstance;
-using absl::test_internal::CopyableOnlyInstance;
-using absl::test_internal::InstanceTracker;
-using testing::AllOf;
-using testing::Each;
-using testing::ElementsAre;
-using testing::ElementsAreArray;
-using testing::Eq;
-using testing::Gt;
-using testing::PrintToString;
-
-using IntVec = absl::InlinedVector<int, 8>;
-
-MATCHER_P(SizeIs, n, "") {
-  return testing::ExplainMatchResult(n, arg.size(), result_listener);
-}
-
-MATCHER_P(CapacityIs, n, "") {
-  return testing::ExplainMatchResult(n, arg.capacity(), result_listener);
-}
-
-MATCHER_P(ValueIs, e, "") {
-  return testing::ExplainMatchResult(e, arg.value(), result_listener);
-}
-
-// TODO(bsamwel): Add support for movable-only types.
-
-// Test fixture for typed tests on BaseCountedInstance derived classes, see
-// test_instance_tracker.h.
-template <typename T>
-class InstanceTest : public ::testing::Test {};
-TYPED_TEST_SUITE_P(InstanceTest);
-
-// A simple reference counted class to make sure that the proper elements are
-// destroyed in the erase(begin, end) test.
-class RefCounted {
- public:
-  RefCounted(int value, int* count) : value_(value), count_(count) { Ref(); }
-
-  RefCounted(const RefCounted& v) : value_(v.value_), count_(v.count_) {
-    Ref();
-  }
-
-  ~RefCounted() {
-    Unref();
-    count_ = nullptr;
-  }
-
-  friend void swap(RefCounted& a, RefCounted& b) {
-    using std::swap;
-    swap(a.value_, b.value_);
-    swap(a.count_, b.count_);
-  }
-
-  RefCounted& operator=(RefCounted v) {
-    using std::swap;
-    swap(*this, v);
-    return *this;
-  }
-
-  void Ref() const {
-    ABSL_RAW_CHECK(count_ != nullptr, "");
-    ++(*count_);
-  }
-
-  void Unref() const {
-    --(*count_);
-    ABSL_RAW_CHECK(*count_ >= 0, "");
-  }
-
-  int value_;
-  int* count_;
-};
-
-using RefCountedVec = absl::InlinedVector<RefCounted, 8>;
-
-// A class with a vtable pointer
-class Dynamic {
- public:
-  virtual ~Dynamic() {}
-};
-
-using DynamicVec = absl::InlinedVector<Dynamic, 8>;
-
-// Append 0..len-1 to *v
-template <typename Container>
-static void Fill(Container* v, int len, int offset = 0) {
-  for (int i = 0; i < len; i++) {
-    v->push_back(i + offset);
-  }
-}
-
-static IntVec Fill(int len, int offset = 0) {
-  IntVec v;
-  Fill(&v, len, offset);
-  return v;
-}
-
-TEST(IntVec, SimpleOps) {
-  for (int len = 0; len < 20; len++) {
-    IntVec v;
-    const IntVec& cv = v;  // const alias
-
-    Fill(&v, len);
-    EXPECT_EQ(len, v.size());
-    EXPECT_LE(len, v.capacity());
-
-    for (int i = 0; i < len; i++) {
-      EXPECT_EQ(i, v[i]);
-      EXPECT_EQ(i, v.at(i));
-    }
-    EXPECT_EQ(v.begin(), v.data());
-    EXPECT_EQ(cv.begin(), cv.data());
-
-    int counter = 0;
-    for (IntVec::iterator iter = v.begin(); iter != v.end(); ++iter) {
-      EXPECT_EQ(counter, *iter);
-      counter++;
-    }
-    EXPECT_EQ(counter, len);
-
-    counter = 0;
-    for (IntVec::const_iterator iter = v.begin(); iter != v.end(); ++iter) {
-      EXPECT_EQ(counter, *iter);
-      counter++;
-    }
-    EXPECT_EQ(counter, len);
-
-    counter = 0;
-    for (IntVec::const_iterator iter = v.cbegin(); iter != v.cend(); ++iter) {
-      EXPECT_EQ(counter, *iter);
-      counter++;
-    }
-    EXPECT_EQ(counter, len);
-
-    if (len > 0) {
-      EXPECT_EQ(0, v.front());
-      EXPECT_EQ(len - 1, v.back());
-      v.pop_back();
-      EXPECT_EQ(len - 1, v.size());
-      for (int i = 0; i < v.size(); ++i) {
-        EXPECT_EQ(i, v[i]);
-        EXPECT_EQ(i, v.at(i));
-      }
-    }
-  }
-}
-
-TEST(IntVec, PopBackNoOverflow) {
-  IntVec v = {1};
-  v.pop_back();
-  EXPECT_EQ(v.size(), 0);
-}
-
-TEST(IntVec, AtThrows) {
-  IntVec v = {1, 2, 3};
-  EXPECT_EQ(v.at(2), 3);
-  ABSL_BASE_INTERNAL_EXPECT_FAIL(v.at(3), std::out_of_range,
-                                 "failed bounds check");
-}
-
-TEST(IntVec, ReverseIterator) {
-  for (int len = 0; len < 20; len++) {
-    IntVec v;
-    Fill(&v, len);
-
-    int counter = len;
-    for (IntVec::reverse_iterator iter = v.rbegin(); iter != v.rend(); ++iter) {
-      counter--;
-      EXPECT_EQ(counter, *iter);
-    }
-    EXPECT_EQ(counter, 0);
-
-    counter = len;
-    for (IntVec::const_reverse_iterator iter = v.rbegin(); iter != v.rend();
-         ++iter) {
-      counter--;
-      EXPECT_EQ(counter, *iter);
-    }
-    EXPECT_EQ(counter, 0);
-
-    counter = len;
-    for (IntVec::const_reverse_iterator iter = v.crbegin(); iter != v.crend();
-         ++iter) {
-      counter--;
-      EXPECT_EQ(counter, *iter);
-    }
-    EXPECT_EQ(counter, 0);
-  }
-}
-
-TEST(IntVec, Erase) {
-  for (int len = 1; len < 20; len++) {
-    for (int i = 0; i < len; ++i) {
-      IntVec v;
-      Fill(&v, len);
-      v.erase(v.begin() + i);
-      EXPECT_EQ(len - 1, v.size());
-      for (int j = 0; j < i; ++j) {
-        EXPECT_EQ(j, v[j]);
-      }
-      for (int j = i; j < len - 1; ++j) {
-        EXPECT_EQ(j + 1, v[j]);
-      }
-    }
-  }
-}
-
-TEST(IntVec, Hardened) {
-  IntVec v;
-  Fill(&v, 10);
-  EXPECT_EQ(v[9], 9);
-#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
-  EXPECT_DEATH_IF_SUPPORTED(v[10], "");
-  EXPECT_DEATH_IF_SUPPORTED(v[-1], "");
-#endif
-}
-
-// At the end of this test loop, the elements between [erase_begin, erase_end)
-// should have reference counts == 0, and all others elements should have
-// reference counts == 1.
-TEST(RefCountedVec, EraseBeginEnd) {
-  for (int len = 1; len < 20; ++len) {
-    for (int erase_begin = 0; erase_begin < len; ++erase_begin) {
-      for (int erase_end = erase_begin; erase_end <= len; ++erase_end) {
-        std::vector<int> counts(len, 0);
-        RefCountedVec v;
-        for (int i = 0; i < len; ++i) {
-          v.push_back(RefCounted(i, &counts[i]));
-        }
-
-        int erase_len = erase_end - erase_begin;
-
-        v.erase(v.begin() + erase_begin, v.begin() + erase_end);
-
-        EXPECT_EQ(len - erase_len, v.size());
-
-        // Check the elements before the first element erased.
-        for (int i = 0; i < erase_begin; ++i) {
-          EXPECT_EQ(i, v[i].value_);
-        }
-
-        // Check the elements after the first element erased.
-        for (int i = erase_begin; i < v.size(); ++i) {
-          EXPECT_EQ(i + erase_len, v[i].value_);
-        }
-
-        // Check that the elements at the beginning are preserved.
-        for (int i = 0; i < erase_begin; ++i) {
-          EXPECT_EQ(1, counts[i]);
-        }
-
-        // Check that the erased elements are destroyed
-        for (int i = erase_begin; i < erase_end; ++i) {
-          EXPECT_EQ(0, counts[i]);
-        }
-
-        // Check that the elements at the end are preserved.
-        for (int i = erase_end; i < len; ++i) {
-          EXPECT_EQ(1, counts[i]);
-        }
-      }
-    }
-  }
-}
-
-struct NoDefaultCtor {
-  explicit NoDefaultCtor(int) {}
-};
-struct NoCopy {
-  NoCopy() {}
-  NoCopy(const NoCopy&) = delete;
-};
-struct NoAssign {
-  NoAssign() {}
-  NoAssign& operator=(const NoAssign&) = delete;
-};
-struct MoveOnly {
-  MoveOnly() {}
-  MoveOnly(MoveOnly&&) = default;
-  MoveOnly& operator=(MoveOnly&&) = default;
-};
-TEST(InlinedVectorTest, NoDefaultCtor) {
-  absl::InlinedVector<NoDefaultCtor, 1> v(10, NoDefaultCtor(2));
-  (void)v;
-}
-TEST(InlinedVectorTest, NoCopy) {
-  absl::InlinedVector<NoCopy, 1> v(10);
-  (void)v;
-}
-TEST(InlinedVectorTest, NoAssign) {
-  absl::InlinedVector<NoAssign, 1> v(10);
-  (void)v;
-}
-TEST(InlinedVectorTest, MoveOnly) {
-  absl::InlinedVector<MoveOnly, 2> v;
-  v.push_back(MoveOnly{});
-  v.push_back(MoveOnly{});
-  v.push_back(MoveOnly{});
-  v.erase(v.begin());
-  v.push_back(MoveOnly{});
-  v.erase(v.begin(), v.begin() + 1);
-  v.insert(v.begin(), MoveOnly{});
-  v.emplace(v.begin());
-  v.emplace(v.begin(), MoveOnly{});
-}
-TEST(InlinedVectorTest, Noexcept) {
-  EXPECT_TRUE(std::is_nothrow_move_constructible<IntVec>::value);
-  EXPECT_TRUE((std::is_nothrow_move_constructible<
-               absl::InlinedVector<MoveOnly, 2>>::value));
-
-  struct MoveCanThrow {
-    MoveCanThrow(MoveCanThrow&&) {}
-  };
-  EXPECT_EQ(absl::default_allocator_is_nothrow::value,
-            (std::is_nothrow_move_constructible<
-                absl::InlinedVector<MoveCanThrow, 2>>::value));
-}
-
-TEST(InlinedVectorTest, EmplaceBack) {
-  absl::InlinedVector<std::pair<std::string, int>, 1> v;
-
-  auto& inlined_element = v.emplace_back("answer", 42);
-  EXPECT_EQ(&inlined_element, &v[0]);
-  EXPECT_EQ(inlined_element.first, "answer");
-  EXPECT_EQ(inlined_element.second, 42);
-
-  auto& allocated_element = v.emplace_back("taxicab", 1729);
-  EXPECT_EQ(&allocated_element, &v[1]);
-  EXPECT_EQ(allocated_element.first, "taxicab");
-  EXPECT_EQ(allocated_element.second, 1729);
-}
-
-TEST(InlinedVectorTest, ShrinkToFitGrowingVector) {
-  absl::InlinedVector<std::pair<std::string, int>, 1> v;
-
-  v.shrink_to_fit();
-  EXPECT_EQ(v.capacity(), 1);
-
-  v.emplace_back("answer", 42);
-  v.shrink_to_fit();
-  EXPECT_EQ(v.capacity(), 1);
-
-  v.emplace_back("taxicab", 1729);
-  EXPECT_GE(v.capacity(), 2);
-  v.shrink_to_fit();
-  EXPECT_EQ(v.capacity(), 2);
-
-  v.reserve(100);
-  EXPECT_GE(v.capacity(), 100);
-  v.shrink_to_fit();
-  EXPECT_EQ(v.capacity(), 2);
-}
-
-TEST(InlinedVectorTest, ShrinkToFitEdgeCases) {
-  {
-    absl::InlinedVector<std::pair<std::string, int>, 1> v;
-    v.emplace_back("answer", 42);
-    v.emplace_back("taxicab", 1729);
-    EXPECT_GE(v.capacity(), 2);
-    v.pop_back();
-    v.shrink_to_fit();
-    EXPECT_EQ(v.capacity(), 1);
-    EXPECT_EQ(v[0].first, "answer");
-    EXPECT_EQ(v[0].second, 42);
-  }
-
-  {
-    absl::InlinedVector<std::string, 2> v(100);
-    v.resize(0);
-    v.shrink_to_fit();
-    EXPECT_EQ(v.capacity(), 2);  // inlined capacity
-  }
-
-  {
-    absl::InlinedVector<std::string, 2> v(100);
-    v.resize(1);
-    v.shrink_to_fit();
-    EXPECT_EQ(v.capacity(), 2);  // inlined capacity
-  }
-
-  {
-    absl::InlinedVector<std::string, 2> v(100);
-    v.resize(2);
-    v.shrink_to_fit();
-    EXPECT_EQ(v.capacity(), 2);
-  }
-
-  {
-    absl::InlinedVector<std::string, 2> v(100);
-    v.resize(3);
-    v.shrink_to_fit();
-    EXPECT_EQ(v.capacity(), 3);
-  }
-}
-
-TEST(IntVec, Insert) {
-  for (int len = 0; len < 20; len++) {
-    for (int pos = 0; pos <= len; pos++) {
-      {
-        // Single element
-        std::vector<int> std_v;
-        Fill(&std_v, len);
-        IntVec v;
-        Fill(&v, len);
-
-        std_v.insert(std_v.begin() + pos, 9999);
-        IntVec::iterator it = v.insert(v.cbegin() + pos, 9999);
-        EXPECT_THAT(v, ElementsAreArray(std_v));
-        EXPECT_EQ(it, v.cbegin() + pos);
-      }
-      {
-        // n elements
-        std::vector<int> std_v;
-        Fill(&std_v, len);
-        IntVec v;
-        Fill(&v, len);
-
-        IntVec::size_type n = 5;
-        std_v.insert(std_v.begin() + pos, n, 9999);
-        IntVec::iterator it = v.insert(v.cbegin() + pos, n, 9999);
-        EXPECT_THAT(v, ElementsAreArray(std_v));
-        EXPECT_EQ(it, v.cbegin() + pos);
-      }
-      {
-        // Iterator range (random access iterator)
-        std::vector<int> std_v;
-        Fill(&std_v, len);
-        IntVec v;
-        Fill(&v, len);
-
-        const std::vector<int> input = {9999, 8888, 7777};
-        std_v.insert(std_v.begin() + pos, input.cbegin(), input.cend());
-        IntVec::iterator it =
-            v.insert(v.cbegin() + pos, input.cbegin(), input.cend());
-        EXPECT_THAT(v, ElementsAreArray(std_v));
-        EXPECT_EQ(it, v.cbegin() + pos);
-      }
-      {
-        // Iterator range (forward iterator)
-        std::vector<int> std_v;
-        Fill(&std_v, len);
-        IntVec v;
-        Fill(&v, len);
-
-        const std::forward_list<int> input = {9999, 8888, 7777};
-        std_v.insert(std_v.begin() + pos, input.cbegin(), input.cend());
-        IntVec::iterator it =
-            v.insert(v.cbegin() + pos, input.cbegin(), input.cend());
-        EXPECT_THAT(v, ElementsAreArray(std_v));
-        EXPECT_EQ(it, v.cbegin() + pos);
-      }
-      {
-        // Iterator range (input iterator)
-        std::vector<int> std_v;
-        Fill(&std_v, len);
-        IntVec v;
-        Fill(&v, len);
-
-        std_v.insert(std_v.begin() + pos, {9999, 8888, 7777});
-        std::istringstream input("9999 8888 7777");
-        IntVec::iterator it =
-            v.insert(v.cbegin() + pos, std::istream_iterator<int>(input),
-                     std::istream_iterator<int>());
-        EXPECT_THAT(v, ElementsAreArray(std_v));
-        EXPECT_EQ(it, v.cbegin() + pos);
-      }
-      {
-        // Initializer list
-        std::vector<int> std_v;
-        Fill(&std_v, len);
-        IntVec v;
-        Fill(&v, len);
-
-        std_v.insert(std_v.begin() + pos, {9999, 8888});
-        IntVec::iterator it = v.insert(v.cbegin() + pos, {9999, 8888});
-        EXPECT_THAT(v, ElementsAreArray(std_v));
-        EXPECT_EQ(it, v.cbegin() + pos);
-      }
-    }
-  }
-}
-
-TEST(RefCountedVec, InsertConstructorDestructor) {
-  // Make sure the proper construction/destruction happen during insert
-  // operations.
-  for (int len = 0; len < 20; len++) {
-    SCOPED_TRACE(len);
-    for (int pos = 0; pos <= len; pos++) {
-      SCOPED_TRACE(pos);
-      std::vector<int> counts(len, 0);
-      int inserted_count = 0;
-      RefCountedVec v;
-      for (int i = 0; i < len; ++i) {
-        SCOPED_TRACE(i);
-        v.push_back(RefCounted(i, &counts[i]));
-      }
-
-      EXPECT_THAT(counts, Each(Eq(1)));
-
-      RefCounted insert_element(9999, &inserted_count);
-      EXPECT_EQ(1, inserted_count);
-      v.insert(v.begin() + pos, insert_element);
-      EXPECT_EQ(2, inserted_count);
-      // Check that the elements at the end are preserved.
-      EXPECT_THAT(counts, Each(Eq(1)));
-      EXPECT_EQ(2, inserted_count);
-    }
-  }
-}
-
-TEST(IntVec, Resize) {
-  for (int len = 0; len < 20; len++) {
-    IntVec v;
-    Fill(&v, len);
-
-    // Try resizing up and down by k elements
-    static const int kResizeElem = 1000000;
-    for (int k = 0; k < 10; k++) {
-      // Enlarging resize
-      v.resize(len + k, kResizeElem);
-      EXPECT_EQ(len + k, v.size());
-      EXPECT_LE(len + k, v.capacity());
-      for (int i = 0; i < len + k; i++) {
-        if (i < len) {
-          EXPECT_EQ(i, v[i]);
-        } else {
-          EXPECT_EQ(kResizeElem, v[i]);
-        }
-      }
-
-      // Shrinking resize
-      v.resize(len, kResizeElem);
-      EXPECT_EQ(len, v.size());
-      EXPECT_LE(len, v.capacity());
-      for (int i = 0; i < len; i++) {
-        EXPECT_EQ(i, v[i]);
-      }
-    }
-  }
-}
-
-TEST(IntVec, InitWithLength) {
-  for (int len = 0; len < 20; len++) {
-    IntVec v(len, 7);
-    EXPECT_EQ(len, v.size());
-    EXPECT_LE(len, v.capacity());
-    for (int i = 0; i < len; i++) {
-      EXPECT_EQ(7, v[i]);
-    }
-  }
-}
-
-TEST(IntVec, CopyConstructorAndAssignment) {
-  for (int len = 0; len < 20; len++) {
-    IntVec v;
-    Fill(&v, len);
-    EXPECT_EQ(len, v.size());
-    EXPECT_LE(len, v.capacity());
-
-    IntVec v2(v);
-    EXPECT_TRUE(v == v2) << PrintToString(v) << PrintToString(v2);
-
-    for (int start_len = 0; start_len < 20; start_len++) {
-      IntVec v3;
-      Fill(&v3, start_len, 99);  // Add dummy elements that should go away
-      v3 = v;
-      EXPECT_TRUE(v == v3) << PrintToString(v) << PrintToString(v3);
-    }
-  }
-}
-
-TEST(IntVec, AliasingCopyAssignment) {
-  for (int len = 0; len < 20; ++len) {
-    IntVec original;
-    Fill(&original, len);
-    IntVec dup = original;
-    dup = *&dup;
-    EXPECT_EQ(dup, original);
-  }
-}
-
-TEST(IntVec, MoveConstructorAndAssignment) {
-  for (int len = 0; len < 20; len++) {
-    IntVec v_in;
-    const int inlined_capacity = v_in.capacity();
-    Fill(&v_in, len);
-    EXPECT_EQ(len, v_in.size());
-    EXPECT_LE(len, v_in.capacity());
-
-    {
-      IntVec v_temp(v_in);
-      auto* old_data = v_temp.data();
-      IntVec v_out(std::move(v_temp));
-      EXPECT_TRUE(v_in == v_out) << PrintToString(v_in) << PrintToString(v_out);
-      if (v_in.size() > inlined_capacity) {
-        // Allocation is moved as a whole, data stays in place.
-        EXPECT_TRUE(v_out.data() == old_data);
-      } else {
-        EXPECT_FALSE(v_out.data() == old_data);
-      }
-    }
-    for (int start_len = 0; start_len < 20; start_len++) {
-      IntVec v_out;
-      Fill(&v_out, start_len, 99);  // Add dummy elements that should go away
-      IntVec v_temp(v_in);
-      auto* old_data = v_temp.data();
-      v_out = std::move(v_temp);
-      EXPECT_TRUE(v_in == v_out) << PrintToString(v_in) << PrintToString(v_out);
-      if (v_in.size() > inlined_capacity) {
-        // Allocation is moved as a whole, data stays in place.
-        EXPECT_TRUE(v_out.data() == old_data);
-      } else {
-        EXPECT_FALSE(v_out.data() == old_data);
-      }
-    }
-  }
-}
-
-class NotTriviallyDestructible {
- public:
-  NotTriviallyDestructible() : p_(new int(1)) {}
-  explicit NotTriviallyDestructible(int i) : p_(new int(i)) {}
-
-  NotTriviallyDestructible(const NotTriviallyDestructible& other)
-      : p_(new int(*other.p_)) {}
-
-  NotTriviallyDestructible& operator=(const NotTriviallyDestructible& other) {
-    p_ = absl::make_unique<int>(*other.p_);
-    return *this;
-  }
-
-  bool operator==(const NotTriviallyDestructible& other) const {
-    return *p_ == *other.p_;
-  }
-
- private:
-  std::unique_ptr<int> p_;
-};
-
-TEST(AliasingTest, Emplace) {
-  for (int i = 2; i < 20; ++i) {
-    absl::InlinedVector<NotTriviallyDestructible, 10> vec;
-    for (int j = 0; j < i; ++j) {
-      vec.push_back(NotTriviallyDestructible(j));
-    }
-    vec.emplace(vec.begin(), vec[0]);
-    EXPECT_EQ(vec[0], vec[1]);
-    vec.emplace(vec.begin() + i / 2, vec[i / 2]);
-    EXPECT_EQ(vec[i / 2], vec[i / 2 + 1]);
-    vec.emplace(vec.end() - 1, vec.back());
-    EXPECT_EQ(vec[vec.size() - 2], vec.back());
-  }
-}
-
-TEST(AliasingTest, InsertWithCount) {
-  for (int i = 1; i < 20; ++i) {
-    absl::InlinedVector<NotTriviallyDestructible, 10> vec;
-    for (int j = 0; j < i; ++j) {
-      vec.push_back(NotTriviallyDestructible(j));
-    }
-    for (int n = 0; n < 5; ++n) {
-      // We use back where we can because it's guaranteed to become invalidated
-      vec.insert(vec.begin(), n, vec.back());
-      auto b = vec.begin();
-      EXPECT_TRUE(
-          std::all_of(b, b + n, [&vec](const NotTriviallyDestructible& x) {
-            return x == vec.back();
-          }));
-
-      auto m_idx = vec.size() / 2;
-      vec.insert(vec.begin() + m_idx, n, vec.back());
-      auto m = vec.begin() + m_idx;
-      EXPECT_TRUE(
-          std::all_of(m, m + n, [&vec](const NotTriviallyDestructible& x) {
-            return x == vec.back();
-          }));
-
-      // We want distinct values so the equality test is meaningful,
-      // vec[vec.size() - 1] is also almost always invalidated.
-      auto old_e = vec.size() - 1;
-      auto val = vec[old_e];
-      vec.insert(vec.end(), n, vec[old_e]);
-      auto e = vec.begin() + old_e;
-      EXPECT_TRUE(std::all_of(
-          e, e + n,
-          [&val](const NotTriviallyDestructible& x) { return x == val; }));
-    }
-  }
-}
-
-TEST(OverheadTest, Storage) {
-  // Check for size overhead.
-  // In particular, ensure that std::allocator doesn't cost anything to store.
-  // The union should be absorbing some of the allocation bookkeeping overhead
-  // in the larger vectors, leaving only the size_ field as overhead.
-
-  struct T { void* val; };
-  size_t expected_overhead = sizeof(T);
-
-  EXPECT_EQ((2 * expected_overhead),
-            sizeof(absl::InlinedVector<T, 1>) - sizeof(T[1]));
-  EXPECT_EQ(expected_overhead,
-            sizeof(absl::InlinedVector<T, 2>) - sizeof(T[2]));
-  EXPECT_EQ(expected_overhead,
-            sizeof(absl::InlinedVector<T, 3>) - sizeof(T[3]));
-  EXPECT_EQ(expected_overhead,
-            sizeof(absl::InlinedVector<T, 4>) - sizeof(T[4]));
-  EXPECT_EQ(expected_overhead,
-            sizeof(absl::InlinedVector<T, 5>) - sizeof(T[5]));
-  EXPECT_EQ(expected_overhead,
-            sizeof(absl::InlinedVector<T, 6>) - sizeof(T[6]));
-  EXPECT_EQ(expected_overhead,
-            sizeof(absl::InlinedVector<T, 7>) - sizeof(T[7]));
-  EXPECT_EQ(expected_overhead,
-            sizeof(absl::InlinedVector<T, 8>) - sizeof(T[8]));
-}
-
-TEST(IntVec, Clear) {
-  for (int len = 0; len < 20; len++) {
-    SCOPED_TRACE(len);
-    IntVec v;
-    Fill(&v, len);
-    v.clear();
-    EXPECT_EQ(0, v.size());
-    EXPECT_EQ(v.begin(), v.end());
-  }
-}
-
-TEST(IntVec, Reserve) {
-  for (int len = 0; len < 20; len++) {
-    IntVec v;
-    Fill(&v, len);
-
-    for (int newlen = 0; newlen < 100; newlen++) {
-      const int* start_rep = v.data();
-      v.reserve(newlen);
-      const int* final_rep = v.data();
-      if (newlen <= len) {
-        EXPECT_EQ(start_rep, final_rep);
-      }
-      EXPECT_LE(newlen, v.capacity());
-
-      // Filling up to newlen should not change rep
-      while (v.size() < newlen) {
-        v.push_back(0);
-      }
-      EXPECT_EQ(final_rep, v.data());
-    }
-  }
-}
-
-TEST(StringVec, SelfRefPushBack) {
-  std::vector<std::string> std_v;
-  absl::InlinedVector<std::string, 4> v;
-  const std::string s = "A quite long string to ensure heap.";
-  std_v.push_back(s);
-  v.push_back(s);
-  for (int i = 0; i < 20; ++i) {
-    EXPECT_THAT(v, ElementsAreArray(std_v));
-
-    v.push_back(v.back());
-    std_v.push_back(std_v.back());
-  }
-  EXPECT_THAT(v, ElementsAreArray(std_v));
-}
-
-TEST(StringVec, SelfRefPushBackWithMove) {
-  std::vector<std::string> std_v;
-  absl::InlinedVector<std::string, 4> v;
-  const std::string s = "A quite long string to ensure heap.";
-  std_v.push_back(s);
-  v.push_back(s);
-  for (int i = 0; i < 20; ++i) {
-    EXPECT_EQ(v.back(), std_v.back());
-
-    v.push_back(std::move(v.back()));
-    std_v.push_back(std::move(std_v.back()));
-  }
-  EXPECT_EQ(v.back(), std_v.back());
-}
-
-TEST(StringVec, SelfMove) {
-  const std::string s = "A quite long string to ensure heap.";
-  for (int len = 0; len < 20; len++) {
-    SCOPED_TRACE(len);
-    absl::InlinedVector<std::string, 8> v;
-    for (int i = 0; i < len; ++i) {
-      SCOPED_TRACE(i);
-      v.push_back(s);
-    }
-    // Indirection necessary to avoid compiler warning.
-    v = std::move(*(&v));
-    // Ensure that the inlined vector is still in a valid state by copying it.
-    // We don't expect specific contents since a self-move results in an
-    // unspecified valid state.
-    std::vector<std::string> copy(v.begin(), v.end());
-  }
-}
-
-TEST(IntVec, Swap) {
-  for (int l1 = 0; l1 < 20; l1++) {
-    SCOPED_TRACE(l1);
-    for (int l2 = 0; l2 < 20; l2++) {
-      SCOPED_TRACE(l2);
-      IntVec a = Fill(l1, 0);
-      IntVec b = Fill(l2, 100);
-      {
-        using std::swap;
-        swap(a, b);
-      }
-      EXPECT_EQ(l1, b.size());
-      EXPECT_EQ(l2, a.size());
-      for (int i = 0; i < l1; i++) {
-        SCOPED_TRACE(i);
-        EXPECT_EQ(i, b[i]);
-      }
-      for (int i = 0; i < l2; i++) {
-        SCOPED_TRACE(i);
-        EXPECT_EQ(100 + i, a[i]);
-      }
-    }
-  }
-}
-
-TYPED_TEST_P(InstanceTest, Swap) {
-  using Instance = TypeParam;
-  using InstanceVec = absl::InlinedVector<Instance, 8>;
-  for (int l1 = 0; l1 < 20; l1++) {
-    SCOPED_TRACE(l1);
-    for (int l2 = 0; l2 < 20; l2++) {
-      SCOPED_TRACE(l2);
-      InstanceTracker tracker;
-      InstanceVec a, b;
-      const size_t inlined_capacity = a.capacity();
-      auto min_len = std::min(l1, l2);
-      auto max_len = std::max(l1, l2);
-      for (int i = 0; i < l1; i++) a.push_back(Instance(i));
-      for (int i = 0; i < l2; i++) b.push_back(Instance(100 + i));
-      EXPECT_EQ(tracker.instances(), l1 + l2);
-      tracker.ResetCopiesMovesSwaps();
-      {
-        using std::swap;
-        swap(a, b);
-      }
-      EXPECT_EQ(tracker.instances(), l1 + l2);
-      if (a.size() > inlined_capacity && b.size() > inlined_capacity) {
-        EXPECT_EQ(tracker.swaps(), 0);  // Allocations are swapped.
-        EXPECT_EQ(tracker.moves(), 0);
-      } else if (a.size() <= inlined_capacity && b.size() <= inlined_capacity) {
-        EXPECT_EQ(tracker.swaps(), min_len);
-        EXPECT_EQ((tracker.moves() ? tracker.moves() : tracker.copies()),
-                  max_len - min_len);
-      } else {
-        // One is allocated and the other isn't. The allocation is transferred
-        // without copying elements, and the inlined instances are copied/moved.
-        EXPECT_EQ(tracker.swaps(), 0);
-        EXPECT_EQ((tracker.moves() ? tracker.moves() : tracker.copies()),
-                  min_len);
-      }
-
-      EXPECT_EQ(l1, b.size());
-      EXPECT_EQ(l2, a.size());
-      for (int i = 0; i < l1; i++) {
-        EXPECT_EQ(i, b[i].value());
-      }
-      for (int i = 0; i < l2; i++) {
-        EXPECT_EQ(100 + i, a[i].value());
-      }
-    }
-  }
-}
-
-TEST(IntVec, EqualAndNotEqual) {
-  IntVec a, b;
-  EXPECT_TRUE(a == b);
-  EXPECT_FALSE(a != b);
-
-  a.push_back(3);
-  EXPECT_FALSE(a == b);
-  EXPECT_TRUE(a != b);
-
-  b.push_back(3);
-  EXPECT_TRUE(a == b);
-  EXPECT_FALSE(a != b);
-
-  b.push_back(7);
-  EXPECT_FALSE(a == b);
-  EXPECT_TRUE(a != b);
-
-  a.push_back(6);
-  EXPECT_FALSE(a == b);
-  EXPECT_TRUE(a != b);
-
-  a.clear();
-  b.clear();
-  for (int i = 0; i < 100; i++) {
-    a.push_back(i);
-    b.push_back(i);
-    EXPECT_TRUE(a == b);
-    EXPECT_FALSE(a != b);
-
-    b[i] = b[i] + 1;
-    EXPECT_FALSE(a == b);
-    EXPECT_TRUE(a != b);
-
-    b[i] = b[i] - 1;  // Back to before
-    EXPECT_TRUE(a == b);
-    EXPECT_FALSE(a != b);
-  }
-}
-
-TEST(IntVec, RelationalOps) {
-  IntVec a, b;
-  EXPECT_FALSE(a < b);
-  EXPECT_FALSE(b < a);
-  EXPECT_FALSE(a > b);
-  EXPECT_FALSE(b > a);
-  EXPECT_TRUE(a <= b);
-  EXPECT_TRUE(b <= a);
-  EXPECT_TRUE(a >= b);
-  EXPECT_TRUE(b >= a);
-  b.push_back(3);
-  EXPECT_TRUE(a < b);
-  EXPECT_FALSE(b < a);
-  EXPECT_FALSE(a > b);
-  EXPECT_TRUE(b > a);
-  EXPECT_TRUE(a <= b);
-  EXPECT_FALSE(b <= a);
-  EXPECT_FALSE(a >= b);
-  EXPECT_TRUE(b >= a);
-}
-
-TYPED_TEST_P(InstanceTest, CountConstructorsDestructors) {
-  using Instance = TypeParam;
-  using InstanceVec = absl::InlinedVector<Instance, 8>;
-  InstanceTracker tracker;
-  for (int len = 0; len < 20; len++) {
-    SCOPED_TRACE(len);
-    tracker.ResetCopiesMovesSwaps();
-
-    InstanceVec v;
-    const size_t inlined_capacity = v.capacity();
-    for (int i = 0; i < len; i++) {
-      v.push_back(Instance(i));
-    }
-    EXPECT_EQ(tracker.instances(), len);
-    EXPECT_GE(tracker.copies() + tracker.moves(),
-              len);  // More due to reallocation.
-    tracker.ResetCopiesMovesSwaps();
-
-    // Enlarging resize() must construct some objects
-    tracker.ResetCopiesMovesSwaps();
-    v.resize(len + 10, Instance(100));
-    EXPECT_EQ(tracker.instances(), len + 10);
-    if (len <= inlined_capacity && len + 10 > inlined_capacity) {
-      EXPECT_EQ(tracker.copies() + tracker.moves(), 10 + len);
-    } else {
-      // Only specify a minimum number of copies + moves. We don't want to
-      // depend on the reallocation policy here.
-      EXPECT_GE(tracker.copies() + tracker.moves(),
-                10);  // More due to reallocation.
-    }
-
-    // Shrinking resize() must destroy some objects
-    tracker.ResetCopiesMovesSwaps();
-    v.resize(len, Instance(100));
-    EXPECT_EQ(tracker.instances(), len);
-    EXPECT_EQ(tracker.copies(), 0);
-    EXPECT_EQ(tracker.moves(), 0);
-
-    // reserve() must not increase the number of initialized objects
-    SCOPED_TRACE("reserve");
-    v.reserve(len + 1000);
-    EXPECT_EQ(tracker.instances(), len);
-    EXPECT_EQ(tracker.copies() + tracker.moves(), len);
-
-    // pop_back() and erase() must destroy one object
-    if (len > 0) {
-      tracker.ResetCopiesMovesSwaps();
-      v.pop_back();
-      EXPECT_EQ(tracker.instances(), len - 1);
-      EXPECT_EQ(tracker.copies(), 0);
-      EXPECT_EQ(tracker.moves(), 0);
-
-      if (!v.empty()) {
-        tracker.ResetCopiesMovesSwaps();
-        v.erase(v.begin());
-        EXPECT_EQ(tracker.instances(), len - 2);
-        EXPECT_EQ(tracker.copies() + tracker.moves(), len - 2);
-      }
-    }
-
-    tracker.ResetCopiesMovesSwaps();
-    int instances_before_empty_erase = tracker.instances();
-    v.erase(v.begin(), v.begin());
-    EXPECT_EQ(tracker.instances(), instances_before_empty_erase);
-    EXPECT_EQ(tracker.copies() + tracker.moves(), 0);
-  }
-}
-
-TYPED_TEST_P(InstanceTest, CountConstructorsDestructorsOnCopyConstruction) {
-  using Instance = TypeParam;
-  using InstanceVec = absl::InlinedVector<Instance, 8>;
-  InstanceTracker tracker;
-  for (int len = 0; len < 20; len++) {
-    SCOPED_TRACE(len);
-    tracker.ResetCopiesMovesSwaps();
-
-    InstanceVec v;
-    for (int i = 0; i < len; i++) {
-      v.push_back(Instance(i));
-    }
-    EXPECT_EQ(tracker.instances(), len);
-    EXPECT_GE(tracker.copies() + tracker.moves(),
-              len);  // More due to reallocation.
-    tracker.ResetCopiesMovesSwaps();
-    {  // Copy constructor should create 'len' more instances.
-      InstanceVec v_copy(v);
-      EXPECT_EQ(tracker.instances(), len + len);
-      EXPECT_EQ(tracker.copies(), len);
-      EXPECT_EQ(tracker.moves(), 0);
-    }
-    EXPECT_EQ(tracker.instances(), len);
-  }
-}
-
-TYPED_TEST_P(InstanceTest, CountConstructorsDestructorsOnMoveConstruction) {
-  using Instance = TypeParam;
-  using InstanceVec = absl::InlinedVector<Instance, 8>;
-  InstanceTracker tracker;
-  for (int len = 0; len < 20; len++) {
-    SCOPED_TRACE(len);
-    tracker.ResetCopiesMovesSwaps();
-
-    InstanceVec v;
-    const size_t inlined_capacity = v.capacity();
-    for (int i = 0; i < len; i++) {
-      v.push_back(Instance(i));
-    }
-    EXPECT_EQ(tracker.instances(), len);
-    EXPECT_GE(tracker.copies() + tracker.moves(),
-              len);  // More due to reallocation.
-    tracker.ResetCopiesMovesSwaps();
-    {
-      InstanceVec v_copy(std::move(v));
-      if (len > inlined_capacity) {
-        // Allocation is moved as a whole.
-        EXPECT_EQ(tracker.instances(), len);
-        EXPECT_EQ(tracker.live_instances(), len);
-        // Tests an implementation detail, don't rely on this in your code.
-        EXPECT_EQ(v.size(), 0);  // NOLINT misc-use-after-move
-        EXPECT_EQ(tracker.copies(), 0);
-        EXPECT_EQ(tracker.moves(), 0);
-      } else {
-        EXPECT_EQ(tracker.instances(), len + len);
-        if (Instance::supports_move()) {
-          EXPECT_EQ(tracker.live_instances(), len);
-          EXPECT_EQ(tracker.copies(), 0);
-          EXPECT_EQ(tracker.moves(), len);
-        } else {
-          EXPECT_EQ(tracker.live_instances(), len + len);
-          EXPECT_EQ(tracker.copies(), len);
-          EXPECT_EQ(tracker.moves(), 0);
-        }
-      }
-      EXPECT_EQ(tracker.swaps(), 0);
-    }
-  }
-}
-
-TYPED_TEST_P(InstanceTest, CountConstructorsDestructorsOnAssignment) {
-  using Instance = TypeParam;
-  using InstanceVec = absl::InlinedVector<Instance, 8>;
-  InstanceTracker tracker;
-  for (int len = 0; len < 20; len++) {
-    SCOPED_TRACE(len);
-    for (int longorshort = 0; longorshort <= 1; ++longorshort) {
-      SCOPED_TRACE(longorshort);
-      tracker.ResetCopiesMovesSwaps();
-
-      InstanceVec longer, shorter;
-      for (int i = 0; i < len; i++) {
-        longer.push_back(Instance(i));
-        shorter.push_back(Instance(i));
-      }
-      longer.push_back(Instance(len));
-      EXPECT_EQ(tracker.instances(), len + len + 1);
-      EXPECT_GE(tracker.copies() + tracker.moves(),
-                len + len + 1);  // More due to reallocation.
-
-      tracker.ResetCopiesMovesSwaps();
-      if (longorshort) {
-        shorter = longer;
-        EXPECT_EQ(tracker.instances(), (len + 1) + (len + 1));
-        EXPECT_GE(tracker.copies() + tracker.moves(),
-                  len + 1);  // More due to reallocation.
-      } else {
-        longer = shorter;
-        EXPECT_EQ(tracker.instances(), len + len);
-        EXPECT_EQ(tracker.copies() + tracker.moves(), len);
-      }
-    }
-  }
-}
-
-TYPED_TEST_P(InstanceTest, CountConstructorsDestructorsOnMoveAssignment) {
-  using Instance = TypeParam;
-  using InstanceVec = absl::InlinedVector<Instance, 8>;
-  InstanceTracker tracker;
-  for (int len = 0; len < 20; len++) {
-    SCOPED_TRACE(len);
-    for (int longorshort = 0; longorshort <= 1; ++longorshort) {
-      SCOPED_TRACE(longorshort);
-      tracker.ResetCopiesMovesSwaps();
-
-      InstanceVec longer, shorter;
-      const int inlined_capacity = longer.capacity();
-      for (int i = 0; i < len; i++) {
-        longer.push_back(Instance(i));
-        shorter.push_back(Instance(i));
-      }
-      longer.push_back(Instance(len));
-      EXPECT_EQ(tracker.instances(), len + len + 1);
-      EXPECT_GE(tracker.copies() + tracker.moves(),
-                len + len + 1);  // More due to reallocation.
-
-      tracker.ResetCopiesMovesSwaps();
-      int src_len;
-      if (longorshort) {
-        src_len = len + 1;
-        shorter = std::move(longer);
-      } else {
-        src_len = len;
-        longer = std::move(shorter);
-      }
-      if (src_len > inlined_capacity) {
-        // Allocation moved as a whole.
-        EXPECT_EQ(tracker.instances(), src_len);
-        EXPECT_EQ(tracker.live_instances(), src_len);
-        EXPECT_EQ(tracker.copies(), 0);
-        EXPECT_EQ(tracker.moves(), 0);
-      } else {
-        // Elements are all copied.
-        EXPECT_EQ(tracker.instances(), src_len + src_len);
-        if (Instance::supports_move()) {
-          EXPECT_EQ(tracker.copies(), 0);
-          EXPECT_EQ(tracker.moves(), src_len);
-          EXPECT_EQ(tracker.live_instances(), src_len);
-        } else {
-          EXPECT_EQ(tracker.copies(), src_len);
-          EXPECT_EQ(tracker.moves(), 0);
-          EXPECT_EQ(tracker.live_instances(), src_len + src_len);
-        }
-      }
-      EXPECT_EQ(tracker.swaps(), 0);
-    }
-  }
-}
-
-TEST(CountElemAssign, SimpleTypeWithInlineBacking) {
-  for (size_t original_size = 0; original_size <= 5; ++original_size) {
-    SCOPED_TRACE(original_size);
-    // Original contents are [12345, 12345, ...]
-    std::vector<int> original_contents(original_size, 12345);
-
-    absl::InlinedVector<int, 2> v(original_contents.begin(),
-                                  original_contents.end());
-    v.assign(2, 123);
-    EXPECT_THAT(v, AllOf(SizeIs(2), ElementsAre(123, 123)));
-    if (original_size <= 2) {
-      // If the original had inline backing, it should stay inline.
-      EXPECT_EQ(2, v.capacity());
-    }
-  }
-}
-
-TEST(CountElemAssign, SimpleTypeWithAllocation) {
-  for (size_t original_size = 0; original_size <= 5; ++original_size) {
-    SCOPED_TRACE(original_size);
-    // Original contents are [12345, 12345, ...]
-    std::vector<int> original_contents(original_size, 12345);
-
-    absl::InlinedVector<int, 2> v(original_contents.begin(),
-                                  original_contents.end());
-    v.assign(3, 123);
-    EXPECT_THAT(v, AllOf(SizeIs(3), ElementsAre(123, 123, 123)));
-    EXPECT_LE(v.size(), v.capacity());
-  }
-}
-
-TYPED_TEST_P(InstanceTest, CountElemAssignInlineBacking) {
-  using Instance = TypeParam;
-  for (size_t original_size = 0; original_size <= 5; ++original_size) {
-    SCOPED_TRACE(original_size);
-    // Original contents are [12345, 12345, ...]
-    std::vector<Instance> original_contents(original_size, Instance(12345));
-
-    absl::InlinedVector<Instance, 2> v(original_contents.begin(),
-                                       original_contents.end());
-    v.assign(2, Instance(123));
-    EXPECT_THAT(v, AllOf(SizeIs(2), ElementsAre(ValueIs(123), ValueIs(123))));
-    if (original_size <= 2) {
-      // If the original had inline backing, it should stay inline.
-      EXPECT_EQ(2, v.capacity());
-    }
-  }
-}
-
-template <typename Instance>
-void InstanceCountElemAssignWithAllocationTest() {
-  for (size_t original_size = 0; original_size <= 5; ++original_size) {
-    SCOPED_TRACE(original_size);
-    // Original contents are [12345, 12345, ...]
-    std::vector<Instance> original_contents(original_size, Instance(12345));
-
-    absl::InlinedVector<Instance, 2> v(original_contents.begin(),
-                                       original_contents.end());
-    v.assign(3, Instance(123));
-    EXPECT_THAT(v, AllOf(SizeIs(3), ElementsAre(ValueIs(123), ValueIs(123),
-                                                ValueIs(123))));
-    EXPECT_LE(v.size(), v.capacity());
-  }
-}
-TEST(CountElemAssign, WithAllocationCopyableInstance) {
-  InstanceCountElemAssignWithAllocationTest<CopyableOnlyInstance>();
-}
-TEST(CountElemAssign, WithAllocationCopyableMovableInstance) {
-  InstanceCountElemAssignWithAllocationTest<CopyableMovableInstance>();
-}
-
-TEST(RangedConstructor, SimpleType) {
-  std::vector<int> source_v = {4, 5, 6};
-  // First try to fit in inline backing
-  absl::InlinedVector<int, 4> v(source_v.begin(), source_v.end());
-  EXPECT_EQ(3, v.size());
-  EXPECT_EQ(4, v.capacity());  // Indication that we're still on inlined storage
-  EXPECT_EQ(4, v[0]);
-  EXPECT_EQ(5, v[1]);
-  EXPECT_EQ(6, v[2]);
-
-  // Now, force a re-allocate
-  absl::InlinedVector<int, 2> realloc_v(source_v.begin(), source_v.end());
-  EXPECT_EQ(3, realloc_v.size());
-  EXPECT_LT(2, realloc_v.capacity());
-  EXPECT_EQ(4, realloc_v[0]);
-  EXPECT_EQ(5, realloc_v[1]);
-  EXPECT_EQ(6, realloc_v[2]);
-}
-
-// Test for ranged constructors using Instance as the element type and
-// SourceContainer as the source container type.
-template <typename Instance, typename SourceContainer, int inlined_capacity>
-void InstanceRangedConstructorTestForContainer() {
-  InstanceTracker tracker;
-  SourceContainer source_v = {Instance(0), Instance(1)};
-  tracker.ResetCopiesMovesSwaps();
-  absl::InlinedVector<Instance, inlined_capacity> v(source_v.begin(),
-                                                    source_v.end());
-  EXPECT_EQ(2, v.size());
-  EXPECT_LT(1, v.capacity());
-  EXPECT_EQ(0, v[0].value());
-  EXPECT_EQ(1, v[1].value());
-  EXPECT_EQ(tracker.copies(), 2);
-  EXPECT_EQ(tracker.moves(), 0);
-}
-
-template <typename Instance, int inlined_capacity>
-void InstanceRangedConstructorTestWithCapacity() {
-  // Test with const and non-const, random access and non-random-access sources.
-  // TODO(bsamwel): Test with an input iterator source.
-  {
-    SCOPED_TRACE("std::list");
-    InstanceRangedConstructorTestForContainer<Instance, std::list<Instance>,
-                                              inlined_capacity>();
-    {
-      SCOPED_TRACE("const std::list");
-      InstanceRangedConstructorTestForContainer<
-          Instance, const std::list<Instance>, inlined_capacity>();
-    }
-    {
-      SCOPED_TRACE("std::vector");
-      InstanceRangedConstructorTestForContainer<Instance, std::vector<Instance>,
-                                                inlined_capacity>();
-    }
-    {
-      SCOPED_TRACE("const std::vector");
-      InstanceRangedConstructorTestForContainer<
-          Instance, const std::vector<Instance>, inlined_capacity>();
-    }
-  }
-}
-
-TYPED_TEST_P(InstanceTest, RangedConstructor) {
-  using Instance = TypeParam;
-  SCOPED_TRACE("capacity=1");
-  InstanceRangedConstructorTestWithCapacity<Instance, 1>();
-  SCOPED_TRACE("capacity=2");
-  InstanceRangedConstructorTestWithCapacity<Instance, 2>();
-}
-
-TEST(RangedConstructor, ElementsAreConstructed) {
-  std::vector<std::string> source_v = {"cat", "dog"};
-
-  // Force expansion and re-allocation of v.  Ensures that when the vector is
-  // expanded that new elements are constructed.
-  absl::InlinedVector<std::string, 1> v(source_v.begin(), source_v.end());
-  EXPECT_EQ("cat", v[0]);
-  EXPECT_EQ("dog", v[1]);
-}
-
-TEST(RangedAssign, SimpleType) {
-  // Test for all combinations of original sizes (empty and non-empty inline,
-  // and out of line) and target sizes.
-  for (size_t original_size = 0; original_size <= 5; ++original_size) {
-    SCOPED_TRACE(original_size);
-    // Original contents are [12345, 12345, ...]
-    std::vector<int> original_contents(original_size, 12345);
-
-    for (size_t target_size = 0; target_size <= 5; ++target_size) {
-      SCOPED_TRACE(target_size);
-
-      // New contents are [3, 4, ...]
-      std::vector<int> new_contents;
-      for (size_t i = 0; i < target_size; ++i) {
-        new_contents.push_back(i + 3);
-      }
-
-      absl::InlinedVector<int, 3> v(original_contents.begin(),
-                                    original_contents.end());
-      v.assign(new_contents.begin(), new_contents.end());
-
-      EXPECT_EQ(new_contents.size(), v.size());
-      EXPECT_LE(new_contents.size(), v.capacity());
-      if (target_size <= 3 && original_size <= 3) {
-        // Storage should stay inline when target size is small.
-        EXPECT_EQ(3, v.capacity());
-      }
-      EXPECT_THAT(v, ElementsAreArray(new_contents));
-    }
-  }
-}
-
-// Returns true if lhs and rhs have the same value.
-template <typename Instance>
-static bool InstanceValuesEqual(const Instance& lhs, const Instance& rhs) {
-  return lhs.value() == rhs.value();
-}
-
-// Test for ranged assign() using Instance as the element type and
-// SourceContainer as the source container type.
-template <typename Instance, typename SourceContainer>
-void InstanceRangedAssignTestForContainer() {
-  // Test for all combinations of original sizes (empty and non-empty inline,
-  // and out of line) and target sizes.
-  for (size_t original_size = 0; original_size <= 5; ++original_size) {
-    SCOPED_TRACE(original_size);
-    // Original contents are [12345, 12345, ...]
-    std::vector<Instance> original_contents(original_size, Instance(12345));
-
-    for (size_t target_size = 0; target_size <= 5; ++target_size) {
-      SCOPED_TRACE(target_size);
-
-      // New contents are [3, 4, ...]
-      // Generate data using a non-const container, because SourceContainer
-      // itself may be const.
-      // TODO(bsamwel): Test with an input iterator.
-      std::vector<Instance> new_contents_in;
-      for (size_t i = 0; i < target_size; ++i) {
-        new_contents_in.push_back(Instance(i + 3));
-      }
-      SourceContainer new_contents(new_contents_in.begin(),
-                                   new_contents_in.end());
-
-      absl::InlinedVector<Instance, 3> v(original_contents.begin(),
-                                         original_contents.end());
-      v.assign(new_contents.begin(), new_contents.end());
-
-      EXPECT_EQ(new_contents.size(), v.size());
-      EXPECT_LE(new_contents.size(), v.capacity());
-      if (target_size <= 3 && original_size <= 3) {
-        // Storage should stay inline when target size is small.
-        EXPECT_EQ(3, v.capacity());
-      }
-      EXPECT_TRUE(std::equal(v.begin(), v.end(), new_contents.begin(),
-                             InstanceValuesEqual<Instance>));
-    }
-  }
-}
-
-TYPED_TEST_P(InstanceTest, RangedAssign) {
-  using Instance = TypeParam;
-  // Test with const and non-const, random access and non-random-access sources.
-  // TODO(bsamwel): Test with an input iterator source.
-  SCOPED_TRACE("std::list");
-  InstanceRangedAssignTestForContainer<Instance, std::list<Instance>>();
-  SCOPED_TRACE("const std::list");
-  InstanceRangedAssignTestForContainer<Instance, const std::list<Instance>>();
-  SCOPED_TRACE("std::vector");
-  InstanceRangedAssignTestForContainer<Instance, std::vector<Instance>>();
-  SCOPED_TRACE("const std::vector");
-  InstanceRangedAssignTestForContainer<Instance, const std::vector<Instance>>();
-}
-
-TEST(InitializerListConstructor, SimpleTypeWithInlineBacking) {
-  EXPECT_THAT((absl::InlinedVector<int, 4>{4, 5, 6}),
-              AllOf(SizeIs(3), CapacityIs(4), ElementsAre(4, 5, 6)));
-}
-
-TEST(InitializerListConstructor, SimpleTypeWithReallocationRequired) {
-  EXPECT_THAT((absl::InlinedVector<int, 2>{4, 5, 6}),
-              AllOf(SizeIs(3), CapacityIs(Gt(2)), ElementsAre(4, 5, 6)));
-}
-
-TEST(InitializerListConstructor, DisparateTypesInList) {
-  EXPECT_THAT((absl::InlinedVector<int, 2>{-7, 8ULL}), ElementsAre(-7, 8));
-
-  EXPECT_THAT((absl::InlinedVector<std::string, 2>{"foo", std::string("bar")}),
-              ElementsAre("foo", "bar"));
-}
-
-TEST(InitializerListConstructor, ComplexTypeWithInlineBacking) {
-  EXPECT_THAT((absl::InlinedVector<CopyableMovableInstance, 1>{
-                  CopyableMovableInstance(0)}),
-              AllOf(SizeIs(1), CapacityIs(1), ElementsAre(ValueIs(0))));
-}
-
-TEST(InitializerListConstructor, ComplexTypeWithReallocationRequired) {
-  EXPECT_THAT(
-      (absl::InlinedVector<CopyableMovableInstance, 1>{
-          CopyableMovableInstance(0), CopyableMovableInstance(1)}),
-      AllOf(SizeIs(2), CapacityIs(Gt(1)), ElementsAre(ValueIs(0), ValueIs(1))));
-}
-
-TEST(InitializerListAssign, SimpleTypeFitsInlineBacking) {
-  for (size_t original_size = 0; original_size <= 4; ++original_size) {
-    SCOPED_TRACE(original_size);
-
-    absl::InlinedVector<int, 2> v1(original_size, 12345);
-    const size_t original_capacity_v1 = v1.capacity();
-    v1.assign({3});
-    EXPECT_THAT(
-        v1, AllOf(SizeIs(1), CapacityIs(original_capacity_v1), ElementsAre(3)));
-
-    absl::InlinedVector<int, 2> v2(original_size, 12345);
-    const size_t original_capacity_v2 = v2.capacity();
-    v2 = {3};
-    EXPECT_THAT(
-        v2, AllOf(SizeIs(1), CapacityIs(original_capacity_v2), ElementsAre(3)));
-  }
-}
-
-TEST(InitializerListAssign, SimpleTypeDoesNotFitInlineBacking) {
-  for (size_t original_size = 0; original_size <= 4; ++original_size) {
-    SCOPED_TRACE(original_size);
-    absl::InlinedVector<int, 2> v1(original_size, 12345);
-    v1.assign({3, 4, 5});
-    EXPECT_THAT(v1, AllOf(SizeIs(3), ElementsAre(3, 4, 5)));
-    EXPECT_LE(3, v1.capacity());
-
-    absl::InlinedVector<int, 2> v2(original_size, 12345);
-    v2 = {3, 4, 5};
-    EXPECT_THAT(v2, AllOf(SizeIs(3), ElementsAre(3, 4, 5)));
-    EXPECT_LE(3, v2.capacity());
-  }
-}
-
-TEST(InitializerListAssign, DisparateTypesInList) {
-  absl::InlinedVector<int, 2> v_int1;
-  v_int1.assign({-7, 8ULL});
-  EXPECT_THAT(v_int1, ElementsAre(-7, 8));
-
-  absl::InlinedVector<int, 2> v_int2;
-  v_int2 = {-7, 8ULL};
-  EXPECT_THAT(v_int2, ElementsAre(-7, 8));
-
-  absl::InlinedVector<std::string, 2> v_string1;
-  v_string1.assign({"foo", std::string("bar")});
-  EXPECT_THAT(v_string1, ElementsAre("foo", "bar"));
-
-  absl::InlinedVector<std::string, 2> v_string2;
-  v_string2 = {"foo", std::string("bar")};
-  EXPECT_THAT(v_string2, ElementsAre("foo", "bar"));
-}
-
-TYPED_TEST_P(InstanceTest, InitializerListAssign) {
-  using Instance = TypeParam;
-  for (size_t original_size = 0; original_size <= 4; ++original_size) {
-    SCOPED_TRACE(original_size);
-    absl::InlinedVector<Instance, 2> v(original_size, Instance(12345));
-    const size_t original_capacity = v.capacity();
-    v.assign({Instance(3)});
-    EXPECT_THAT(v, AllOf(SizeIs(1), CapacityIs(original_capacity),
-                         ElementsAre(ValueIs(3))));
-  }
-  for (size_t original_size = 0; original_size <= 4; ++original_size) {
-    SCOPED_TRACE(original_size);
-    absl::InlinedVector<Instance, 2> v(original_size, Instance(12345));
-    v.assign({Instance(3), Instance(4), Instance(5)});
-    EXPECT_THAT(
-        v, AllOf(SizeIs(3), ElementsAre(ValueIs(3), ValueIs(4), ValueIs(5))));
-    EXPECT_LE(3, v.capacity());
-  }
-}
-
-REGISTER_TYPED_TEST_CASE_P(InstanceTest, Swap, CountConstructorsDestructors,
-                           CountConstructorsDestructorsOnCopyConstruction,
-                           CountConstructorsDestructorsOnMoveConstruction,
-                           CountConstructorsDestructorsOnAssignment,
-                           CountConstructorsDestructorsOnMoveAssignment,
-                           CountElemAssignInlineBacking, RangedConstructor,
-                           RangedAssign, InitializerListAssign);
-
-using InstanceTypes =
-    ::testing::Types<CopyableOnlyInstance, CopyableMovableInstance>;
-INSTANTIATE_TYPED_TEST_CASE_P(InstanceTestOnTypes, InstanceTest, InstanceTypes);
-
-TEST(DynamicVec, DynamicVecCompiles) {
-  DynamicVec v;
-  (void)v;
-}
-
-TEST(AllocatorSupportTest, Constructors) {
-  using MyAlloc = CountingAllocator<int>;
-  using AllocVec = absl::InlinedVector<int, 4, MyAlloc>;
-  const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
-  int64_t allocated = 0;
-  MyAlloc alloc(&allocated);
-  { AllocVec ABSL_ATTRIBUTE_UNUSED v; }
-  { AllocVec ABSL_ATTRIBUTE_UNUSED v(alloc); }
-  { AllocVec ABSL_ATTRIBUTE_UNUSED v(ia, ia + ABSL_ARRAYSIZE(ia), alloc); }
-  { AllocVec ABSL_ATTRIBUTE_UNUSED v({1, 2, 3}, alloc); }
-
-  AllocVec v2;
-  { AllocVec ABSL_ATTRIBUTE_UNUSED v(v2, alloc); }
-  { AllocVec ABSL_ATTRIBUTE_UNUSED v(std::move(v2), alloc); }
-}
-
-TEST(AllocatorSupportTest, CountAllocations) {
-  using MyAlloc = CountingAllocator<int>;
-  using AllocVec = absl::InlinedVector<int, 4, MyAlloc>;
-  const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
-  int64_t allocated = 0;
-  MyAlloc alloc(&allocated);
-  {
-    AllocVec ABSL_ATTRIBUTE_UNUSED v(ia, ia + 4, alloc);
-    EXPECT_THAT(allocated, 0);
-  }
-  EXPECT_THAT(allocated, 0);
-  {
-    AllocVec ABSL_ATTRIBUTE_UNUSED v(ia, ia + ABSL_ARRAYSIZE(ia), alloc);
-    EXPECT_THAT(allocated, v.size() * sizeof(int));
-  }
-  EXPECT_THAT(allocated, 0);
-  {
-    AllocVec v(4, 1, alloc);
-    EXPECT_THAT(allocated, 0);
-
-    int64_t allocated2 = 0;
-    MyAlloc alloc2(&allocated2);
-    AllocVec v2(v, alloc2);
-    EXPECT_THAT(allocated2, 0);
-
-    int64_t allocated3 = 0;
-    MyAlloc alloc3(&allocated3);
-    AllocVec v3(std::move(v), alloc3);
-    EXPECT_THAT(allocated3, 0);
-  }
-  EXPECT_THAT(allocated, 0);
-  {
-    AllocVec v(8, 2, alloc);
-    EXPECT_THAT(allocated, v.size() * sizeof(int));
-
-    int64_t allocated2 = 0;
-    MyAlloc alloc2(&allocated2);
-    AllocVec v2(v, alloc2);
-    EXPECT_THAT(allocated2, v2.size() * sizeof(int));
-
-    int64_t allocated3 = 0;
-    MyAlloc alloc3(&allocated3);
-    AllocVec v3(std::move(v), alloc3);
-    EXPECT_THAT(allocated3, v3.size() * sizeof(int));
-  }
-  EXPECT_EQ(allocated, 0);
-  {
-    // Test shrink_to_fit deallocations.
-    AllocVec v(8, 2, alloc);
-    EXPECT_EQ(allocated, 8 * sizeof(int));
-    v.resize(5);
-    EXPECT_EQ(allocated, 8 * sizeof(int));
-    v.shrink_to_fit();
-    EXPECT_EQ(allocated, 5 * sizeof(int));
-    v.resize(4);
-    EXPECT_EQ(allocated, 5 * sizeof(int));
-    v.shrink_to_fit();
-    EXPECT_EQ(allocated, 0);
-  }
-}
-
-TEST(AllocatorSupportTest, SwapBothAllocated) {
-  using MyAlloc = CountingAllocator<int>;
-  using AllocVec = absl::InlinedVector<int, 4, MyAlloc>;
-  int64_t allocated1 = 0;
-  int64_t allocated2 = 0;
-  {
-    const int ia1[] = {0, 1, 2, 3, 4, 5, 6, 7};
-    const int ia2[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
-    MyAlloc a1(&allocated1);
-    MyAlloc a2(&allocated2);
-    AllocVec v1(ia1, ia1 + ABSL_ARRAYSIZE(ia1), a1);
-    AllocVec v2(ia2, ia2 + ABSL_ARRAYSIZE(ia2), a2);
-    EXPECT_LT(v1.capacity(), v2.capacity());
-    EXPECT_THAT(allocated1, v1.capacity() * sizeof(int));
-    EXPECT_THAT(allocated2, v2.capacity() * sizeof(int));
-    v1.swap(v2);
-    EXPECT_THAT(v1, ElementsAreArray(ia2));
-    EXPECT_THAT(v2, ElementsAreArray(ia1));
-    EXPECT_THAT(allocated1, v2.capacity() * sizeof(int));
-    EXPECT_THAT(allocated2, v1.capacity() * sizeof(int));
-  }
-  EXPECT_THAT(allocated1, 0);
-  EXPECT_THAT(allocated2, 0);
-}
-
-TEST(AllocatorSupportTest, SwapOneAllocated) {
-  using MyAlloc = CountingAllocator<int>;
-  using AllocVec = absl::InlinedVector<int, 4, MyAlloc>;
-  int64_t allocated1 = 0;
-  int64_t allocated2 = 0;
-  {
-    const int ia1[] = {0, 1, 2, 3, 4, 5, 6, 7};
-    const int ia2[] = {0, 1, 2, 3};
-    MyAlloc a1(&allocated1);
-    MyAlloc a2(&allocated2);
-    AllocVec v1(ia1, ia1 + ABSL_ARRAYSIZE(ia1), a1);
-    AllocVec v2(ia2, ia2 + ABSL_ARRAYSIZE(ia2), a2);
-    EXPECT_THAT(allocated1, v1.capacity() * sizeof(int));
-    EXPECT_THAT(allocated2, 0);
-    v1.swap(v2);
-    EXPECT_THAT(v1, ElementsAreArray(ia2));
-    EXPECT_THAT(v2, ElementsAreArray(ia1));
-    EXPECT_THAT(allocated1, v2.capacity() * sizeof(int));
-    EXPECT_THAT(allocated2, 0);
-    EXPECT_TRUE(v2.get_allocator() == a1);
-    EXPECT_TRUE(v1.get_allocator() == a2);
-  }
-  EXPECT_THAT(allocated1, 0);
-  EXPECT_THAT(allocated2, 0);
-}
-
-TEST(AllocatorSupportTest, ScopedAllocatorWorksInlined) {
-  using StdVector = std::vector<int, CountingAllocator<int>>;
-  using Alloc = CountingAllocator<StdVector>;
-  using ScopedAlloc = std::scoped_allocator_adaptor<Alloc>;
-  using AllocVec = absl::InlinedVector<StdVector, 1, ScopedAlloc>;
-
-  int64_t total_allocated_byte_count = 0;
-
-  AllocVec inlined_case(ScopedAlloc(Alloc(+&total_allocated_byte_count)));
-
-  // Called only once to remain inlined
-  inlined_case.emplace_back();
-
-  int64_t absl_responsible_for_count = total_allocated_byte_count;
-
-  // MSVC's allocator preemptively allocates in debug mode
-#if !defined(_MSC_VER)
-  EXPECT_EQ(absl_responsible_for_count, 0);
-#endif  // !defined(_MSC_VER)
-
-  inlined_case[0].emplace_back();
-  EXPECT_GT(total_allocated_byte_count, absl_responsible_for_count);
-
-  inlined_case.clear();
-  inlined_case.shrink_to_fit();
-  EXPECT_EQ(total_allocated_byte_count, 0);
-}
-
-TEST(AllocatorSupportTest, ScopedAllocatorWorksAllocated) {
-  using StdVector = std::vector<int, CountingAllocator<int>>;
-  using Alloc = CountingAllocator<StdVector>;
-  using ScopedAlloc = std::scoped_allocator_adaptor<Alloc>;
-  using AllocVec = absl::InlinedVector<StdVector, 1, ScopedAlloc>;
-
-  int64_t total_allocated_byte_count = 0;
-
-  AllocVec allocated_case(ScopedAlloc(Alloc(+&total_allocated_byte_count)));
-
-  // Called twice to force into being allocated
-  allocated_case.emplace_back();
-  allocated_case.emplace_back();
-
-  int64_t absl_responsible_for_count = total_allocated_byte_count;
-  EXPECT_GT(absl_responsible_for_count, 0);
-
-  allocated_case[1].emplace_back();
-  EXPECT_GT(total_allocated_byte_count, absl_responsible_for_count);
-
-  allocated_case.clear();
-  allocated_case.shrink_to_fit();
-  EXPECT_EQ(total_allocated_byte_count, 0);
-}
-
-TEST(AllocatorSupportTest, SizeAllocConstructor) {
-  constexpr int inlined_size = 4;
-  using Alloc = CountingAllocator<int>;
-  using AllocVec = absl::InlinedVector<int, inlined_size, Alloc>;
-
-  {
-    auto len = inlined_size / 2;
-    int64_t allocated = 0;
-    auto v = AllocVec(len, Alloc(&allocated));
-
-    // Inline storage used; allocator should not be invoked
-    EXPECT_THAT(allocated, 0);
-    EXPECT_THAT(v, AllOf(SizeIs(len), Each(0)));
-  }
-
-  {
-    auto len = inlined_size * 2;
-    int64_t allocated = 0;
-    auto v = AllocVec(len, Alloc(&allocated));
-
-    // Out of line storage used; allocation of 8 elements expected
-    EXPECT_THAT(allocated, len * sizeof(int));
-    EXPECT_THAT(v, AllOf(SizeIs(len), Each(0)));
-  }
-}
-
-TEST(InlinedVectorTest, MinimumAllocatorCompilesUsingTraits) {
-  using T = int;
-  using A = std::allocator<T>;
-  using ATraits = absl::allocator_traits<A>;
-
-  struct MinimumAllocator {
-    using value_type = T;
-
-    value_type* allocate(size_t n) {
-      A a;
-      return ATraits::allocate(a, n);
-    }
-
-    void deallocate(value_type* p, size_t n) {
-      A a;
-      ATraits::deallocate(a, p, n);
-    }
-  };
-
-  absl::InlinedVector<T, 1, MinimumAllocator> vec;
-  vec.emplace_back();
-  vec.resize(0);
-}
-
-TEST(InlinedVectorTest, AbslHashValueWorks) {
-  using V = absl::InlinedVector<int, 4>;
-  std::vector<V> cases;
-
-  // Generate a variety of vectors some of these are small enough for the inline
-  // space but are stored out of line.
-  for (int i = 0; i < 10; ++i) {
-    V v;
-    for (int j = 0; j < i; ++j) {
-      v.push_back(j);
-    }
-    cases.push_back(v);
-    v.resize(i % 4);
-    cases.push_back(v);
-  }
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
-}
-
-}  // anonymous namespace
diff --git a/third_party/abseil_cpp/absl/container/internal/btree.h b/third_party/abseil_cpp/absl/container/internal/btree.h
deleted file mode 100644
index f2fc31df8d..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/btree.h
+++ /dev/null
@@ -1,2587 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// A btree implementation of the STL set and map interfaces. A btree is smaller
-// and generally also faster than STL set/map (refer to the benchmarks below).
-// The red-black tree implementation of STL set/map has an overhead of 3
-// pointers (left, right and parent) plus the node color information for each
-// stored value. So a set<int32_t> consumes 40 bytes for each value stored in
-// 64-bit mode. This btree implementation stores multiple values on fixed
-// size nodes (usually 256 bytes) and doesn't store child pointers for leaf
-// nodes. The result is that a btree_set<int32_t> may use much less memory per
-// stored value. For the random insertion benchmark in btree_bench.cc, a
-// btree_set<int32_t> with node-size of 256 uses 5.1 bytes per stored value.
-//
-// The packing of multiple values on to each node of a btree has another effect
-// besides better space utilization: better cache locality due to fewer cache
-// lines being accessed. Better cache locality translates into faster
-// operations.
-//
-// CAVEATS
-//
-// Insertions and deletions on a btree can cause splitting, merging or
-// rebalancing of btree nodes. And even without these operations, insertions
-// and deletions on a btree will move values around within a node. In both
-// cases, the result is that insertions and deletions can invalidate iterators
-// pointing to values other than the one being inserted/deleted. Therefore, this
-// container does not provide pointer stability. This is notably different from
-// STL set/map which takes care to not invalidate iterators on insert/erase
-// except, of course, for iterators pointing to the value being erased.  A
-// partial workaround when erasing is available: erase() returns an iterator
-// pointing to the item just after the one that was erased (or end() if none
-// exists).
-
-#ifndef ABSL_CONTAINER_INTERNAL_BTREE_H_
-#define ABSL_CONTAINER_INTERNAL_BTREE_H_
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <functional>
-#include <iterator>
-#include <limits>
-#include <new>
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/macros.h"
-#include "absl/container/internal/common.h"
-#include "absl/container/internal/compressed_tuple.h"
-#include "absl/container/internal/container_memory.h"
-#include "absl/container/internal/layout.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/cord.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/compare.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// A helper class that indicates if the Compare parameter is a key-compare-to
-// comparator.
-template <typename Compare, typename T>
-using btree_is_key_compare_to =
-    std::is_convertible<absl::result_of_t<Compare(const T &, const T &)>,
-                        absl::weak_ordering>;
-
-struct StringBtreeDefaultLess {
-  using is_transparent = void;
-
-  StringBtreeDefaultLess() = default;
-
-  // Compatibility constructor.
-  StringBtreeDefaultLess(std::less<std::string>) {}  // NOLINT
-  StringBtreeDefaultLess(std::less<string_view>) {}  // NOLINT
-
-  absl::weak_ordering operator()(absl::string_view lhs,
-                                 absl::string_view rhs) const {
-    return compare_internal::compare_result_as_ordering(lhs.compare(rhs));
-  }
-  StringBtreeDefaultLess(std::less<absl::Cord>) {}  // NOLINT
-  absl::weak_ordering operator()(const absl::Cord &lhs,
-                                 const absl::Cord &rhs) const {
-    return compare_internal::compare_result_as_ordering(lhs.Compare(rhs));
-  }
-  absl::weak_ordering operator()(const absl::Cord &lhs,
-                                 absl::string_view rhs) const {
-    return compare_internal::compare_result_as_ordering(lhs.Compare(rhs));
-  }
-  absl::weak_ordering operator()(absl::string_view lhs,
-                                 const absl::Cord &rhs) const {
-    return compare_internal::compare_result_as_ordering(-rhs.Compare(lhs));
-  }
-};
-
-struct StringBtreeDefaultGreater {
-  using is_transparent = void;
-
-  StringBtreeDefaultGreater() = default;
-
-  StringBtreeDefaultGreater(std::greater<std::string>) {}  // NOLINT
-  StringBtreeDefaultGreater(std::greater<string_view>) {}  // NOLINT
-
-  absl::weak_ordering operator()(absl::string_view lhs,
-                                 absl::string_view rhs) const {
-    return compare_internal::compare_result_as_ordering(rhs.compare(lhs));
-  }
-  StringBtreeDefaultGreater(std::greater<absl::Cord>) {}  // NOLINT
-  absl::weak_ordering operator()(const absl::Cord &lhs,
-                                 const absl::Cord &rhs) const {
-    return compare_internal::compare_result_as_ordering(rhs.Compare(lhs));
-  }
-  absl::weak_ordering operator()(const absl::Cord &lhs,
-                                 absl::string_view rhs) const {
-    return compare_internal::compare_result_as_ordering(-lhs.Compare(rhs));
-  }
-  absl::weak_ordering operator()(absl::string_view lhs,
-                                 const absl::Cord &rhs) const {
-    return compare_internal::compare_result_as_ordering(rhs.Compare(lhs));
-  }
-};
-
-// A helper class to convert a boolean comparison into a three-way "compare-to"
-// comparison that returns an `absl::weak_ordering`. This helper
-// class is specialized for less<std::string>, greater<std::string>,
-// less<string_view>, greater<string_view>, less<absl::Cord>, and
-// greater<absl::Cord>.
-//
-// key_compare_to_adapter is provided so that btree users
-// automatically get the more efficient compare-to code when using common
-// Abseil string types with common comparison functors.
-// These string-like specializations also turn on heterogeneous lookup by
-// default.
-template <typename Compare>
-struct key_compare_to_adapter {
-  using type = Compare;
-};
-
-template <>
-struct key_compare_to_adapter<std::less<std::string>> {
-  using type = StringBtreeDefaultLess;
-};
-
-template <>
-struct key_compare_to_adapter<std::greater<std::string>> {
-  using type = StringBtreeDefaultGreater;
-};
-
-template <>
-struct key_compare_to_adapter<std::less<absl::string_view>> {
-  using type = StringBtreeDefaultLess;
-};
-
-template <>
-struct key_compare_to_adapter<std::greater<absl::string_view>> {
-  using type = StringBtreeDefaultGreater;
-};
-
-template <>
-struct key_compare_to_adapter<std::less<absl::Cord>> {
-  using type = StringBtreeDefaultLess;
-};
-
-template <>
-struct key_compare_to_adapter<std::greater<absl::Cord>> {
-  using type = StringBtreeDefaultGreater;
-};
-
-// Detects an 'absl_btree_prefer_linear_node_search' member. This is
-// a protocol used as an opt-in or opt-out of linear search.
-//
-//  For example, this would be useful for key types that wrap an integer
-//  and define their own cheap operator<(). For example:
-//
-//   class K {
-//    public:
-//     using absl_btree_prefer_linear_node_search = std::true_type;
-//     ...
-//    private:
-//     friend bool operator<(K a, K b) { return a.k_ < b.k_; }
-//     int k_;
-//   };
-//
-//   btree_map<K, V> m;  // Uses linear search
-//
-// If T has the preference tag, then it has a preference.
-// Btree will use the tag's truth value.
-template <typename T, typename = void>
-struct has_linear_node_search_preference : std::false_type {};
-template <typename T, typename = void>
-struct prefers_linear_node_search : std::false_type {};
-template <typename T>
-struct has_linear_node_search_preference<
-    T, absl::void_t<typename T::absl_btree_prefer_linear_node_search>>
-    : std::true_type {};
-template <typename T>
-struct prefers_linear_node_search<
-    T, absl::void_t<typename T::absl_btree_prefer_linear_node_search>>
-    : T::absl_btree_prefer_linear_node_search {};
-
-template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
-          bool Multi, typename SlotPolicy>
-struct common_params {
-  // If Compare is a common comparator for a string-like type, then we adapt it
-  // to use heterogeneous lookup and to be a key-compare-to comparator.
-  using key_compare = typename key_compare_to_adapter<Compare>::type;
-  // True when key_compare has been adapted to StringBtreeDefault{Less,Greater}.
-  using is_key_compare_adapted =
-      absl::negation<std::is_same<key_compare, Compare>>;
-  // A type which indicates if we have a key-compare-to functor or a plain old
-  // key-compare functor.
-  using is_key_compare_to = btree_is_key_compare_to<key_compare, Key>;
-
-  using allocator_type = Alloc;
-  using key_type = Key;
-  using size_type = std::make_signed<size_t>::type;
-  using difference_type = ptrdiff_t;
-
-  // True if this is a multiset or multimap.
-  using is_multi_container = std::integral_constant<bool, Multi>;
-
-  using slot_policy = SlotPolicy;
-  using slot_type = typename slot_policy::slot_type;
-  using value_type = typename slot_policy::value_type;
-  using init_type = typename slot_policy::mutable_value_type;
-  using pointer = value_type *;
-  using const_pointer = const value_type *;
-  using reference = value_type &;
-  using const_reference = const value_type &;
-
-  enum {
-    kTargetNodeSize = TargetNodeSize,
-
-    // Upper bound for the available space for values. This is largest for leaf
-    // nodes, which have overhead of at least a pointer + 4 bytes (for storing
-    // 3 field_types and an enum).
-    kNodeValueSpace =
-        TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4),
-  };
-
-  // This is an integral type large enough to hold as many
-  // ValueSize-values as will fit a node of TargetNodeSize bytes.
-  using node_count_type =
-      absl::conditional_t<(kNodeValueSpace / sizeof(value_type) >
-                           (std::numeric_limits<uint8_t>::max)()),
-                          uint16_t, uint8_t>;  // NOLINT
-
-  // The following methods are necessary for passing this struct as PolicyTraits
-  // for node_handle and/or are used within btree.
-  static value_type &element(slot_type *slot) {
-    return slot_policy::element(slot);
-  }
-  static const value_type &element(const slot_type *slot) {
-    return slot_policy::element(slot);
-  }
-  template <class... Args>
-  static void construct(Alloc *alloc, slot_type *slot, Args &&... args) {
-    slot_policy::construct(alloc, slot, std::forward<Args>(args)...);
-  }
-  static void construct(Alloc *alloc, slot_type *slot, slot_type *other) {
-    slot_policy::construct(alloc, slot, other);
-  }
-  static void destroy(Alloc *alloc, slot_type *slot) {
-    slot_policy::destroy(alloc, slot);
-  }
-  static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) {
-    construct(alloc, new_slot, old_slot);
-    destroy(alloc, old_slot);
-  }
-  static void swap(Alloc *alloc, slot_type *a, slot_type *b) {
-    slot_policy::swap(alloc, a, b);
-  }
-  static void move(Alloc *alloc, slot_type *src, slot_type *dest) {
-    slot_policy::move(alloc, src, dest);
-  }
-};
-
-// A parameters structure for holding the type parameters for a btree_map.
-// Compare and Alloc should be nothrow copy-constructible.
-template <typename Key, typename Data, typename Compare, typename Alloc,
-          int TargetNodeSize, bool Multi>
-struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi,
-                                  map_slot_policy<Key, Data>> {
-  using super_type = typename map_params::common_params;
-  using mapped_type = Data;
-  // This type allows us to move keys when it is safe to do so. It is safe
-  // for maps in which value_type and mutable_value_type are layout compatible.
-  using slot_policy = typename super_type::slot_policy;
-  using slot_type = typename super_type::slot_type;
-  using value_type = typename super_type::value_type;
-  using init_type = typename super_type::init_type;
-
-  using key_compare = typename super_type::key_compare;
-  // Inherit from key_compare for empty base class optimization.
-  struct value_compare : private key_compare {
-    value_compare() = default;
-    explicit value_compare(const key_compare &cmp) : key_compare(cmp) {}
-
-    template <typename T, typename U>
-    auto operator()(const T &left, const U &right) const
-        -> decltype(std::declval<key_compare>()(left.first, right.first)) {
-      return key_compare::operator()(left.first, right.first);
-    }
-  };
-  using is_map_container = std::true_type;
-
-  template <typename V>
-  static auto key(const V &value) -> decltype(value.first) {
-    return value.first;
-  }
-  static const Key &key(const slot_type *s) { return slot_policy::key(s); }
-  static const Key &key(slot_type *s) { return slot_policy::key(s); }
-  // For use in node handle.
-  static auto mutable_key(slot_type *s)
-      -> decltype(slot_policy::mutable_key(s)) {
-    return slot_policy::mutable_key(s);
-  }
-  static mapped_type &value(value_type *value) { return value->second; }
-};
-
-// This type implements the necessary functions from the
-// absl::container_internal::slot_type interface.
-template <typename Key>
-struct set_slot_policy {
-  using slot_type = Key;
-  using value_type = Key;
-  using mutable_value_type = Key;
-
-  static value_type &element(slot_type *slot) { return *slot; }
-  static const value_type &element(const slot_type *slot) { return *slot; }
-
-  template <typename Alloc, class... Args>
-  static void construct(Alloc *alloc, slot_type *slot, Args &&... args) {
-    absl::allocator_traits<Alloc>::construct(*alloc, slot,
-                                             std::forward<Args>(args)...);
-  }
-
-  template <typename Alloc>
-  static void construct(Alloc *alloc, slot_type *slot, slot_type *other) {
-    absl::allocator_traits<Alloc>::construct(*alloc, slot, std::move(*other));
-  }
-
-  template <typename Alloc>
-  static void destroy(Alloc *alloc, slot_type *slot) {
-    absl::allocator_traits<Alloc>::destroy(*alloc, slot);
-  }
-
-  template <typename Alloc>
-  static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) {
-    using std::swap;
-    swap(*a, *b);
-  }
-
-  template <typename Alloc>
-  static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) {
-    *dest = std::move(*src);
-  }
-};
-
-// A parameters structure for holding the type parameters for a btree_set.
-// Compare and Alloc should be nothrow copy-constructible.
-template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
-          bool Multi>
-struct set_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi,
-                                  set_slot_policy<Key>> {
-  using value_type = Key;
-  using slot_type = typename set_params::common_params::slot_type;
-  using value_compare = typename set_params::common_params::key_compare;
-  using is_map_container = std::false_type;
-
-  template <typename V>
-  static const V &key(const V &value) { return value; }
-  static const Key &key(const slot_type *slot) { return *slot; }
-  static const Key &key(slot_type *slot) { return *slot; }
-};
-
-// An adapter class that converts a lower-bound compare into an upper-bound
-// compare. Note: there is no need to make a version of this adapter specialized
-// for key-compare-to functors because the upper-bound (the first value greater
-// than the input) is never an exact match.
-template <typename Compare>
-struct upper_bound_adapter {
-  explicit upper_bound_adapter(const Compare &c) : comp(c) {}
-  template <typename K1, typename K2>
-  bool operator()(const K1 &a, const K2 &b) const {
-    // Returns true when a is not greater than b.
-    return !compare_internal::compare_result_as_less_than(comp(b, a));
-  }
-
- private:
-  Compare comp;
-};
-
-enum class MatchKind : uint8_t { kEq, kNe };
-
-template <typename V, bool IsCompareTo>
-struct SearchResult {
-  V value;
-  MatchKind match;
-
-  static constexpr bool HasMatch() { return true; }
-  bool IsEq() const { return match == MatchKind::kEq; }
-};
-
-// When we don't use CompareTo, `match` is not present.
-// This ensures that callers can't use it accidentally when it provides no
-// useful information.
-template <typename V>
-struct SearchResult<V, false> {
-  SearchResult() {}
-  explicit SearchResult(V value) : value(value) {}
-  SearchResult(V value, MatchKind /*match*/) : value(value) {}
-
-  V value;
-
-  static constexpr bool HasMatch() { return false; }
-  static constexpr bool IsEq() { return false; }
-};
-
-// A node in the btree holding. The same node type is used for both internal
-// and leaf nodes in the btree, though the nodes are allocated in such a way
-// that the children array is only valid in internal nodes.
-template <typename Params>
-class btree_node {
-  using is_key_compare_to = typename Params::is_key_compare_to;
-  using is_multi_container = typename Params::is_multi_container;
-  using field_type = typename Params::node_count_type;
-  using allocator_type = typename Params::allocator_type;
-  using slot_type = typename Params::slot_type;
-
- public:
-  using params_type = Params;
-  using key_type = typename Params::key_type;
-  using value_type = typename Params::value_type;
-  using pointer = typename Params::pointer;
-  using const_pointer = typename Params::const_pointer;
-  using reference = typename Params::reference;
-  using const_reference = typename Params::const_reference;
-  using key_compare = typename Params::key_compare;
-  using size_type = typename Params::size_type;
-  using difference_type = typename Params::difference_type;
-
-  // Btree decides whether to use linear node search as follows:
-  //   - If the comparator expresses a preference, use that.
-  //   - If the key expresses a preference, use that.
-  //   - If the key is arithmetic and the comparator is std::less or
-  //     std::greater, choose linear.
-  //   - Otherwise, choose binary.
-  // TODO(ezb): Might make sense to add condition(s) based on node-size.
-  using use_linear_search = std::integral_constant<
-      bool,
-      has_linear_node_search_preference<key_compare>::value
-          ? prefers_linear_node_search<key_compare>::value
-          : has_linear_node_search_preference<key_type>::value
-                ? prefers_linear_node_search<key_type>::value
-                : std::is_arithmetic<key_type>::value &&
-                      (std::is_same<std::less<key_type>, key_compare>::value ||
-                       std::is_same<std::greater<key_type>,
-                                    key_compare>::value)>;
-
-  // This class is organized by gtl::Layout as if it had the following
-  // structure:
-  //   // A pointer to the node's parent.
-  //   btree_node *parent;
-  //
-  //   // The position of the node in the node's parent.
-  //   field_type position;
-  //   // The index of the first populated value in `values`.
-  //   // TODO(ezb): right now, `start` is always 0. Update insertion/merge
-  //   // logic to allow for floating storage within nodes.
-  //   field_type start;
-  //   // The index after the last populated value in `values`. Currently, this
-  //   // is the same as the count of values.
-  //   field_type finish;
-  //   // The maximum number of values the node can hold. This is an integer in
-  //   // [1, kNodeValues] for root leaf nodes, kNodeValues for non-root leaf
-  //   // nodes, and kInternalNodeMaxCount (as a sentinel value) for internal
-  //   // nodes (even though there are still kNodeValues values in the node).
-  //   // TODO(ezb): make max_count use only 4 bits and record log2(capacity)
-  //   // to free extra bits for is_root, etc.
-  //   field_type max_count;
-  //
-  //   // The array of values. The capacity is `max_count` for leaf nodes and
-  //   // kNodeValues for internal nodes. Only the values in
-  //   // [start, finish) have been initialized and are valid.
-  //   slot_type values[max_count];
-  //
-  //   // The array of child pointers. The keys in children[i] are all less
-  //   // than key(i). The keys in children[i + 1] are all greater than key(i).
-  //   // There are 0 children for leaf nodes and kNodeValues + 1 children for
-  //   // internal nodes.
-  //   btree_node *children[kNodeValues + 1];
-  //
-  // This class is only constructed by EmptyNodeType. Normally, pointers to the
-  // layout above are allocated, cast to btree_node*, and de-allocated within
-  // the btree implementation.
-  ~btree_node() = default;
-  btree_node(btree_node const &) = delete;
-  btree_node &operator=(btree_node const &) = delete;
-
-  // Public for EmptyNodeType.
-  constexpr static size_type Alignment() {
-    static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(),
-                  "Alignment of all nodes must be equal.");
-    return InternalLayout().Alignment();
-  }
-
- protected:
-  btree_node() = default;
-
- private:
-  using layout_type = absl::container_internal::Layout<btree_node *, field_type,
-                                                       slot_type, btree_node *>;
-  constexpr static size_type SizeWithNValues(size_type n) {
-    return layout_type(/*parent*/ 1,
-                       /*position, start, finish, max_count*/ 4,
-                       /*values*/ n,
-                       /*children*/ 0)
-        .AllocSize();
-  }
-  // A lower bound for the overhead of fields other than values in a leaf node.
-  constexpr static size_type MinimumOverhead() {
-    return SizeWithNValues(1) - sizeof(value_type);
-  }
-
-  // Compute how many values we can fit onto a leaf node taking into account
-  // padding.
-  constexpr static size_type NodeTargetValues(const int begin, const int end) {
-    return begin == end ? begin
-                        : SizeWithNValues((begin + end) / 2 + 1) >
-                                  params_type::kTargetNodeSize
-                              ? NodeTargetValues(begin, (begin + end) / 2)
-                              : NodeTargetValues((begin + end) / 2 + 1, end);
-  }
-
-  enum {
-    kTargetNodeSize = params_type::kTargetNodeSize,
-    kNodeTargetValues = NodeTargetValues(0, params_type::kTargetNodeSize),
-
-    // We need a minimum of 3 values per internal node in order to perform
-    // splitting (1 value for the two nodes involved in the split and 1 value
-    // propagated to the parent as the delimiter for the split).
-    kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3,
-
-    // The node is internal (i.e. is not a leaf node) if and only if `max_count`
-    // has this value.
-    kInternalNodeMaxCount = 0,
-  };
-
-  // Leaves can have less than kNodeValues values.
-  constexpr static layout_type LeafLayout(const int max_values = kNodeValues) {
-    return layout_type(/*parent*/ 1,
-                       /*position, start, finish, max_count*/ 4,
-                       /*values*/ max_values,
-                       /*children*/ 0);
-  }
-  constexpr static layout_type InternalLayout() {
-    return layout_type(/*parent*/ 1,
-                       /*position, start, finish, max_count*/ 4,
-                       /*values*/ kNodeValues,
-                       /*children*/ kNodeValues + 1);
-  }
-  constexpr static size_type LeafSize(const int max_values = kNodeValues) {
-    return LeafLayout(max_values).AllocSize();
-  }
-  constexpr static size_type InternalSize() {
-    return InternalLayout().AllocSize();
-  }
-
-  // N is the index of the type in the Layout definition.
-  // ElementType<N> is the Nth type in the Layout definition.
-  template <size_type N>
-  inline typename layout_type::template ElementType<N> *GetField() {
-    // We assert that we don't read from values that aren't there.
-    assert(N < 3 || !leaf());
-    return InternalLayout().template Pointer<N>(reinterpret_cast<char *>(this));
-  }
-  template <size_type N>
-  inline const typename layout_type::template ElementType<N> *GetField() const {
-    assert(N < 3 || !leaf());
-    return InternalLayout().template Pointer<N>(
-        reinterpret_cast<const char *>(this));
-  }
-  void set_parent(btree_node *p) { *GetField<0>() = p; }
-  field_type &mutable_finish() { return GetField<1>()[2]; }
-  slot_type *slot(int i) { return &GetField<2>()[i]; }
-  slot_type *start_slot() { return slot(start()); }
-  slot_type *finish_slot() { return slot(finish()); }
-  const slot_type *slot(int i) const { return &GetField<2>()[i]; }
-  void set_position(field_type v) { GetField<1>()[0] = v; }
-  void set_start(field_type v) { GetField<1>()[1] = v; }
-  void set_finish(field_type v) { GetField<1>()[2] = v; }
-  // This method is only called by the node init methods.
-  void set_max_count(field_type v) { GetField<1>()[3] = v; }
-
- public:
-  // Whether this is a leaf node or not. This value doesn't change after the
-  // node is created.
-  bool leaf() const { return GetField<1>()[3] != kInternalNodeMaxCount; }
-
-  // Getter for the position of this node in its parent.
-  field_type position() const { return GetField<1>()[0]; }
-
-  // Getter for the offset of the first value in the `values` array.
-  field_type start() const {
-    // TODO(ezb): when floating storage is implemented, return GetField<1>()[1];
-    assert(GetField<1>()[1] == 0);
-    return 0;
-  }
-
-  // Getter for the offset after the last value in the `values` array.
-  field_type finish() const { return GetField<1>()[2]; }
-
-  // Getters for the number of values stored in this node.
-  field_type count() const {
-    assert(finish() >= start());
-    return finish() - start();
-  }
-  field_type max_count() const {
-    // Internal nodes have max_count==kInternalNodeMaxCount.
-    // Leaf nodes have max_count in [1, kNodeValues].
-    const field_type max_count = GetField<1>()[3];
-    return max_count == field_type{kInternalNodeMaxCount}
-               ? field_type{kNodeValues}
-               : max_count;
-  }
-
-  // Getter for the parent of this node.
-  btree_node *parent() const { return *GetField<0>(); }
-  // Getter for whether the node is the root of the tree. The parent of the
-  // root of the tree is the leftmost node in the tree which is guaranteed to
-  // be a leaf.
-  bool is_root() const { return parent()->leaf(); }
-  void make_root() {
-    assert(parent()->is_root());
-    set_parent(parent()->parent());
-  }
-
-  // Getters for the key/value at position i in the node.
-  const key_type &key(int i) const { return params_type::key(slot(i)); }
-  reference value(int i) { return params_type::element(slot(i)); }
-  const_reference value(int i) const { return params_type::element(slot(i)); }
-
-  // Getters/setter for the child at position i in the node.
-  btree_node *child(int i) const { return GetField<3>()[i]; }
-  btree_node *start_child() const { return child(start()); }
-  btree_node *&mutable_child(int i) { return GetField<3>()[i]; }
-  void clear_child(int i) {
-    absl::container_internal::SanitizerPoisonObject(&mutable_child(i));
-  }
-  void set_child(int i, btree_node *c) {
-    absl::container_internal::SanitizerUnpoisonObject(&mutable_child(i));
-    mutable_child(i) = c;
-    c->set_position(i);
-  }
-  void init_child(int i, btree_node *c) {
-    set_child(i, c);
-    c->set_parent(this);
-  }
-
-  // Returns the position of the first value whose key is not less than k.
-  template <typename K>
-  SearchResult<int, is_key_compare_to::value> lower_bound(
-      const K &k, const key_compare &comp) const {
-    return use_linear_search::value ? linear_search(k, comp)
-                                    : binary_search(k, comp);
-  }
-  // Returns the position of the first value whose key is greater than k.
-  template <typename K>
-  int upper_bound(const K &k, const key_compare &comp) const {
-    auto upper_compare = upper_bound_adapter<key_compare>(comp);
-    return use_linear_search::value ? linear_search(k, upper_compare).value
-                                    : binary_search(k, upper_compare).value;
-  }
-
-  template <typename K, typename Compare>
-  SearchResult<int, btree_is_key_compare_to<Compare, key_type>::value>
-  linear_search(const K &k, const Compare &comp) const {
-    return linear_search_impl(k, start(), finish(), comp,
-                              btree_is_key_compare_to<Compare, key_type>());
-  }
-
-  template <typename K, typename Compare>
-  SearchResult<int, btree_is_key_compare_to<Compare, key_type>::value>
-  binary_search(const K &k, const Compare &comp) const {
-    return binary_search_impl(k, start(), finish(), comp,
-                              btree_is_key_compare_to<Compare, key_type>());
-  }
-
-  // Returns the position of the first value whose key is not less than k using
-  // linear search performed using plain compare.
-  template <typename K, typename Compare>
-  SearchResult<int, false> linear_search_impl(
-      const K &k, int s, const int e, const Compare &comp,
-      std::false_type /* IsCompareTo */) const {
-    while (s < e) {
-      if (!comp(key(s), k)) {
-        break;
-      }
-      ++s;
-    }
-    return SearchResult<int, false>{s};
-  }
-
-  // Returns the position of the first value whose key is not less than k using
-  // linear search performed using compare-to.
-  template <typename K, typename Compare>
-  SearchResult<int, true> linear_search_impl(
-      const K &k, int s, const int e, const Compare &comp,
-      std::true_type /* IsCompareTo */) const {
-    while (s < e) {
-      const absl::weak_ordering c = comp(key(s), k);
-      if (c == 0) {
-        return {s, MatchKind::kEq};
-      } else if (c > 0) {
-        break;
-      }
-      ++s;
-    }
-    return {s, MatchKind::kNe};
-  }
-
-  // Returns the position of the first value whose key is not less than k using
-  // binary search performed using plain compare.
-  template <typename K, typename Compare>
-  SearchResult<int, false> binary_search_impl(
-      const K &k, int s, int e, const Compare &comp,
-      std::false_type /* IsCompareTo */) const {
-    while (s != e) {
-      const int mid = (s + e) >> 1;
-      if (comp(key(mid), k)) {
-        s = mid + 1;
-      } else {
-        e = mid;
-      }
-    }
-    return SearchResult<int, false>{s};
-  }
-
-  // Returns the position of the first value whose key is not less than k using
-  // binary search performed using compare-to.
-  template <typename K, typename CompareTo>
-  SearchResult<int, true> binary_search_impl(
-      const K &k, int s, int e, const CompareTo &comp,
-      std::true_type /* IsCompareTo */) const {
-    if (is_multi_container::value) {
-      MatchKind exact_match = MatchKind::kNe;
-      while (s != e) {
-        const int mid = (s + e) >> 1;
-        const absl::weak_ordering c = comp(key(mid), k);
-        if (c < 0) {
-          s = mid + 1;
-        } else {
-          e = mid;
-          if (c == 0) {
-            // Need to return the first value whose key is not less than k,
-            // which requires continuing the binary search if this is a
-            // multi-container.
-            exact_match = MatchKind::kEq;
-          }
-        }
-      }
-      return {s, exact_match};
-    } else {  // Not a multi-container.
-      while (s != e) {
-        const int mid = (s + e) >> 1;
-        const absl::weak_ordering c = comp(key(mid), k);
-        if (c < 0) {
-          s = mid + 1;
-        } else if (c > 0) {
-          e = mid;
-        } else {
-          return {mid, MatchKind::kEq};
-        }
-      }
-      return {s, MatchKind::kNe};
-    }
-  }
-
-  // Emplaces a value at position i, shifting all existing values and
-  // children at positions >= i to the right by 1.
-  template <typename... Args>
-  void emplace_value(size_type i, allocator_type *alloc, Args &&... args);
-
-  // Removes the values at positions [i, i + to_erase), shifting all existing
-  // values and children after that range to the left by to_erase. Clears all
-  // children between [i, i + to_erase).
-  void remove_values(field_type i, field_type to_erase, allocator_type *alloc);
-
-  // Rebalances a node with its right sibling.
-  void rebalance_right_to_left(int to_move, btree_node *right,
-                               allocator_type *alloc);
-  void rebalance_left_to_right(int to_move, btree_node *right,
-                               allocator_type *alloc);
-
-  // Splits a node, moving a portion of the node's values to its right sibling.
-  void split(int insert_position, btree_node *dest, allocator_type *alloc);
-
-  // Merges a node with its right sibling, moving all of the values and the
-  // delimiting key in the parent node onto itself, and deleting the src node.
-  void merge(btree_node *src, allocator_type *alloc);
-
-  // Node allocation/deletion routines.
-  void init_leaf(btree_node *parent, int max_count) {
-    set_parent(parent);
-    set_position(0);
-    set_start(0);
-    set_finish(0);
-    set_max_count(max_count);
-    absl::container_internal::SanitizerPoisonMemoryRegion(
-        start_slot(), max_count * sizeof(slot_type));
-  }
-  void init_internal(btree_node *parent) {
-    init_leaf(parent, kNodeValues);
-    // Set `max_count` to a sentinel value to indicate that this node is
-    // internal.
-    set_max_count(kInternalNodeMaxCount);
-    absl::container_internal::SanitizerPoisonMemoryRegion(
-        &mutable_child(start()), (kNodeValues + 1) * sizeof(btree_node *));
-  }
-
-  static void deallocate(const size_type size, btree_node *node,
-                         allocator_type *alloc) {
-    absl::container_internal::Deallocate<Alignment()>(alloc, node, size);
-  }
-
-  // Deletes a node and all of its children.
-  static void clear_and_delete(btree_node *node, allocator_type *alloc);
-
- private:
-  template <typename... Args>
-  void value_init(const field_type i, allocator_type *alloc, Args &&... args) {
-    absl::container_internal::SanitizerUnpoisonObject(slot(i));
-    params_type::construct(alloc, slot(i), std::forward<Args>(args)...);
-  }
-  void value_destroy(const field_type i, allocator_type *alloc) {
-    params_type::destroy(alloc, slot(i));
-    absl::container_internal::SanitizerPoisonObject(slot(i));
-  }
-  void value_destroy_n(const field_type i, const field_type n,
-                       allocator_type *alloc) {
-    for (slot_type *s = slot(i), *end = slot(i + n); s != end; ++s) {
-      params_type::destroy(alloc, s);
-      absl::container_internal::SanitizerPoisonObject(s);
-    }
-  }
-
-  static void transfer(slot_type *dest, slot_type *src, allocator_type *alloc) {
-    absl::container_internal::SanitizerUnpoisonObject(dest);
-    params_type::transfer(alloc, dest, src);
-    absl::container_internal::SanitizerPoisonObject(src);
-  }
-
-  // Transfers value from slot `src_i` in `src_node` to slot `dest_i` in `this`.
-  void transfer(const size_type dest_i, const size_type src_i,
-                btree_node *src_node, allocator_type *alloc) {
-    transfer(slot(dest_i), src_node->slot(src_i), alloc);
-  }
-
-  // Transfers `n` values starting at value `src_i` in `src_node` into the
-  // values starting at value `dest_i` in `this`.
-  void transfer_n(const size_type n, const size_type dest_i,
-                  const size_type src_i, btree_node *src_node,
-                  allocator_type *alloc) {
-    for (slot_type *src = src_node->slot(src_i), *end = src + n,
-                   *dest = slot(dest_i);
-         src != end; ++src, ++dest) {
-      transfer(dest, src, alloc);
-    }
-  }
-
-  // Same as above, except that we start at the end and work our way to the
-  // beginning.
-  void transfer_n_backward(const size_type n, const size_type dest_i,
-                           const size_type src_i, btree_node *src_node,
-                           allocator_type *alloc) {
-    for (slot_type *src = src_node->slot(src_i + n - 1), *end = src - n,
-                   *dest = slot(dest_i + n - 1);
-         src != end; --src, --dest) {
-      transfer(dest, src, alloc);
-    }
-  }
-
-  template <typename P>
-  friend class btree;
-  template <typename N, typename R, typename P>
-  friend struct btree_iterator;
-  friend class BtreeNodePeer;
-};
-
-template <typename Node, typename Reference, typename Pointer>
-struct btree_iterator {
- private:
-  using key_type = typename Node::key_type;
-  using size_type = typename Node::size_type;
-  using params_type = typename Node::params_type;
-
-  using node_type = Node;
-  using normal_node = typename std::remove_const<Node>::type;
-  using const_node = const Node;
-  using normal_pointer = typename params_type::pointer;
-  using normal_reference = typename params_type::reference;
-  using const_pointer = typename params_type::const_pointer;
-  using const_reference = typename params_type::const_reference;
-  using slot_type = typename params_type::slot_type;
-
-  using iterator =
-      btree_iterator<normal_node, normal_reference, normal_pointer>;
-  using const_iterator =
-      btree_iterator<const_node, const_reference, const_pointer>;
-
- public:
-  // These aliases are public for std::iterator_traits.
-  using difference_type = typename Node::difference_type;
-  using value_type = typename params_type::value_type;
-  using pointer = Pointer;
-  using reference = Reference;
-  using iterator_category = std::bidirectional_iterator_tag;
-
-  btree_iterator() : node(nullptr), position(-1) {}
-  explicit btree_iterator(Node *n) : node(n), position(n->start()) {}
-  btree_iterator(Node *n, int p) : node(n), position(p) {}
-
-  // NOTE: this SFINAE allows for implicit conversions from iterator to
-  // const_iterator, but it specifically avoids defining copy constructors so
-  // that btree_iterator can be trivially copyable. This is for performance and
-  // binary size reasons.
-  template <typename N, typename R, typename P,
-            absl::enable_if_t<
-                std::is_same<btree_iterator<N, R, P>, iterator>::value &&
-                    std::is_same<btree_iterator, const_iterator>::value,
-                int> = 0>
-  btree_iterator(const btree_iterator<N, R, P> &other)  // NOLINT
-      : node(other.node), position(other.position) {}
-
- private:
-  // This SFINAE allows explicit conversions from const_iterator to
-  // iterator, but also avoids defining a copy constructor.
-  // NOTE: the const_cast is safe because this constructor is only called by
-  // non-const methods and the container owns the nodes.
-  template <typename N, typename R, typename P,
-            absl::enable_if_t<
-                std::is_same<btree_iterator<N, R, P>, const_iterator>::value &&
-                    std::is_same<btree_iterator, iterator>::value,
-                int> = 0>
-  explicit btree_iterator(const btree_iterator<N, R, P> &other)
-      : node(const_cast<node_type *>(other.node)), position(other.position) {}
-
-  // Increment/decrement the iterator.
-  void increment() {
-    if (node->leaf() && ++position < node->finish()) {
-      return;
-    }
-    increment_slow();
-  }
-  void increment_slow();
-
-  void decrement() {
-    if (node->leaf() && --position >= node->start()) {
-      return;
-    }
-    decrement_slow();
-  }
-  void decrement_slow();
-
- public:
-  bool operator==(const iterator &other) const {
-    return node == other.node && position == other.position;
-  }
-  bool operator==(const const_iterator &other) const {
-    return node == other.node && position == other.position;
-  }
-  bool operator!=(const iterator &other) const {
-    return node != other.node || position != other.position;
-  }
-  bool operator!=(const const_iterator &other) const {
-    return node != other.node || position != other.position;
-  }
-
-  // Accessors for the key/value the iterator is pointing at.
-  reference operator*() const {
-    ABSL_HARDENING_ASSERT(node != nullptr);
-    ABSL_HARDENING_ASSERT(node->start() <= position);
-    ABSL_HARDENING_ASSERT(node->finish() > position);
-    return node->value(position);
-  }
-  pointer operator->() const { return &operator*(); }
-
-  btree_iterator &operator++() {
-    increment();
-    return *this;
-  }
-  btree_iterator &operator--() {
-    decrement();
-    return *this;
-  }
-  btree_iterator operator++(int) {
-    btree_iterator tmp = *this;
-    ++*this;
-    return tmp;
-  }
-  btree_iterator operator--(int) {
-    btree_iterator tmp = *this;
-    --*this;
-    return tmp;
-  }
-
- private:
-  template <typename Params>
-  friend class btree;
-  template <typename Tree>
-  friend class btree_container;
-  template <typename Tree>
-  friend class btree_set_container;
-  template <typename Tree>
-  friend class btree_map_container;
-  template <typename Tree>
-  friend class btree_multiset_container;
-  template <typename N, typename R, typename P>
-  friend struct btree_iterator;
-  template <typename TreeType, typename CheckerType>
-  friend class base_checker;
-
-  const key_type &key() const { return node->key(position); }
-  slot_type *slot() { return node->slot(position); }
-
-  // The node in the tree the iterator is pointing at.
-  Node *node;
-  // The position within the node of the tree the iterator is pointing at.
-  // NOTE: this is an int rather than a field_type because iterators can point
-  // to invalid positions (such as -1) in certain circumstances.
-  int position;
-};
-
-template <typename Params>
-class btree {
-  using node_type = btree_node<Params>;
-  using is_key_compare_to = typename Params::is_key_compare_to;
-  using init_type = typename Params::init_type;
-  using field_type = typename node_type::field_type;
-  using is_multi_container = typename Params::is_multi_container;
-  using is_key_compare_adapted = typename Params::is_key_compare_adapted;
-
-  // We use a static empty node for the root/leftmost/rightmost of empty btrees
-  // in order to avoid branching in begin()/end().
-  struct alignas(node_type::Alignment()) EmptyNodeType : node_type {
-    using field_type = typename node_type::field_type;
-    node_type *parent;
-    field_type position = 0;
-    field_type start = 0;
-    field_type finish = 0;
-    // max_count must be != kInternalNodeMaxCount (so that this node is regarded
-    // as a leaf node). max_count() is never called when the tree is empty.
-    field_type max_count = node_type::kInternalNodeMaxCount + 1;
-
-#ifdef _MSC_VER
-    // MSVC has constexpr code generations bugs here.
-    EmptyNodeType() : parent(this) {}
-#else
-    constexpr EmptyNodeType(node_type *p) : parent(p) {}
-#endif
-  };
-
-  static node_type *EmptyNode() {
-#ifdef _MSC_VER
-    static EmptyNodeType *empty_node = new EmptyNodeType;
-    // This assert fails on some other construction methods.
-    assert(empty_node->parent == empty_node);
-    return empty_node;
-#else
-    static constexpr EmptyNodeType empty_node(
-        const_cast<EmptyNodeType *>(&empty_node));
-    return const_cast<EmptyNodeType *>(&empty_node);
-#endif
-  }
-
-  enum : uint32_t {
-    kNodeValues = node_type::kNodeValues,
-    kMinNodeValues = kNodeValues / 2,
-  };
-
-  struct node_stats {
-    using size_type = typename Params::size_type;
-
-    node_stats(size_type l, size_type i) : leaf_nodes(l), internal_nodes(i) {}
-
-    node_stats &operator+=(const node_stats &other) {
-      leaf_nodes += other.leaf_nodes;
-      internal_nodes += other.internal_nodes;
-      return *this;
-    }
-
-    size_type leaf_nodes;
-    size_type internal_nodes;
-  };
-
- public:
-  using key_type = typename Params::key_type;
-  using value_type = typename Params::value_type;
-  using size_type = typename Params::size_type;
-  using difference_type = typename Params::difference_type;
-  using key_compare = typename Params::key_compare;
-  using value_compare = typename Params::value_compare;
-  using allocator_type = typename Params::allocator_type;
-  using reference = typename Params::reference;
-  using const_reference = typename Params::const_reference;
-  using pointer = typename Params::pointer;
-  using const_pointer = typename Params::const_pointer;
-  using iterator = btree_iterator<node_type, reference, pointer>;
-  using const_iterator = typename iterator::const_iterator;
-  using reverse_iterator = std::reverse_iterator<iterator>;
-  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-  using node_handle_type = node_handle<Params, Params, allocator_type>;
-
-  // Internal types made public for use by btree_container types.
-  using params_type = Params;
-  using slot_type = typename Params::slot_type;
-
- private:
-  // For use in copy_or_move_values_in_order.
-  const value_type &maybe_move_from_iterator(const_iterator it) { return *it; }
-  value_type &&maybe_move_from_iterator(iterator it) { return std::move(*it); }
-
-  // Copies or moves (depending on the template parameter) the values in
-  // other into this btree in their order in other. This btree must be empty
-  // before this method is called. This method is used in copy construction,
-  // copy assignment, and move assignment.
-  template <typename Btree>
-  void copy_or_move_values_in_order(Btree &other);
-
-  // Validates that various assumptions/requirements are true at compile time.
-  constexpr static bool static_assert_validation();
-
- public:
-  btree(const key_compare &comp, const allocator_type &alloc)
-      : root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {}
-
-  btree(const btree &other) : btree(other, other.allocator()) {}
-  btree(const btree &other, const allocator_type &alloc)
-      : btree(other.key_comp(), alloc) {
-    copy_or_move_values_in_order(other);
-  }
-  btree(btree &&other) noexcept
-      : root_(std::move(other.root_)),
-        rightmost_(absl::exchange(other.rightmost_, EmptyNode())),
-        size_(absl::exchange(other.size_, 0)) {
-    other.mutable_root() = EmptyNode();
-  }
-  btree(btree &&other, const allocator_type &alloc)
-      : btree(other.key_comp(), alloc) {
-    if (alloc == other.allocator()) {
-      swap(other);
-    } else {
-      // Move values from `other` one at a time when allocators are different.
-      copy_or_move_values_in_order(other);
-    }
-  }
-
-  ~btree() {
-    // Put static_asserts in destructor to avoid triggering them before the type
-    // is complete.
-    static_assert(static_assert_validation(), "This call must be elided.");
-    clear();
-  }
-
-  // Assign the contents of other to *this.
-  btree &operator=(const btree &other);
-  btree &operator=(btree &&other) noexcept;
-
-  iterator begin() { return iterator(leftmost()); }
-  const_iterator begin() const { return const_iterator(leftmost()); }
-  iterator end() { return iterator(rightmost_, rightmost_->finish()); }
-  const_iterator end() const {
-    return const_iterator(rightmost_, rightmost_->finish());
-  }
-  reverse_iterator rbegin() { return reverse_iterator(end()); }
-  const_reverse_iterator rbegin() const {
-    return const_reverse_iterator(end());
-  }
-  reverse_iterator rend() { return reverse_iterator(begin()); }
-  const_reverse_iterator rend() const {
-    return const_reverse_iterator(begin());
-  }
-
-  // Finds the first element whose key is not less than key.
-  template <typename K>
-  iterator lower_bound(const K &key) {
-    return internal_end(internal_lower_bound(key).value);
-  }
-  template <typename K>
-  const_iterator lower_bound(const K &key) const {
-    return internal_end(internal_lower_bound(key).value);
-  }
-
-  // Finds the first element whose key is greater than key.
-  template <typename K>
-  iterator upper_bound(const K &key) {
-    return internal_end(internal_upper_bound(key));
-  }
-  template <typename K>
-  const_iterator upper_bound(const K &key) const {
-    return internal_end(internal_upper_bound(key));
-  }
-
-  // Finds the range of values which compare equal to key. The first member of
-  // the returned pair is equal to lower_bound(key). The second member of the
-  // pair is equal to upper_bound(key).
-  template <typename K>
-  std::pair<iterator, iterator> equal_range(const K &key);
-  template <typename K>
-  std::pair<const_iterator, const_iterator> equal_range(const K &key) const {
-    return const_cast<btree *>(this)->equal_range(key);
-  }
-
-  // Inserts a value into the btree only if it does not already exist. The
-  // boolean return value indicates whether insertion succeeded or failed.
-  // Requirement: if `key` already exists in the btree, does not consume `args`.
-  // Requirement: `key` is never referenced after consuming `args`.
-  template <typename K, typename... Args>
-  std::pair<iterator, bool> insert_unique(const K &key, Args &&... args);
-
-  // Inserts with hint. Checks to see if the value should be placed immediately
-  // before `position` in the tree. If so, then the insertion will take
-  // amortized constant time. If not, the insertion will take amortized
-  // logarithmic time as if a call to insert_unique() were made.
-  // Requirement: if `key` already exists in the btree, does not consume `args`.
-  // Requirement: `key` is never referenced after consuming `args`.
-  template <typename K, typename... Args>
-  std::pair<iterator, bool> insert_hint_unique(iterator position,
-                                               const K &key,
-                                               Args &&... args);
-
-  // Insert a range of values into the btree.
-  // Note: the first overload avoids constructing a value_type if the key
-  // already exists in the btree.
-  template <typename InputIterator,
-            typename = decltype(std::declval<const key_compare &>()(
-                params_type::key(*std::declval<InputIterator>()),
-                std::declval<const key_type &>()))>
-  void insert_iterator_unique(InputIterator b, InputIterator e, int);
-  // We need the second overload for cases in which we need to construct a
-  // value_type in order to compare it with the keys already in the btree.
-  template <typename InputIterator>
-  void insert_iterator_unique(InputIterator b, InputIterator e, char);
-
-  // Inserts a value into the btree.
-  template <typename ValueType>
-  iterator insert_multi(const key_type &key, ValueType &&v);
-
-  // Inserts a value into the btree.
-  template <typename ValueType>
-  iterator insert_multi(ValueType &&v) {
-    return insert_multi(params_type::key(v), std::forward<ValueType>(v));
-  }
-
-  // Insert with hint. Check to see if the value should be placed immediately
-  // before position in the tree. If it does, then the insertion will take
-  // amortized constant time. If not, the insertion will take amortized
-  // logarithmic time as if a call to insert_multi(v) were made.
-  template <typename ValueType>
-  iterator insert_hint_multi(iterator position, ValueType &&v);
-
-  // Insert a range of values into the btree.
-  template <typename InputIterator>
-  void insert_iterator_multi(InputIterator b, InputIterator e);
-
-  // Erase the specified iterator from the btree. The iterator must be valid
-  // (i.e. not equal to end()).  Return an iterator pointing to the node after
-  // the one that was erased (or end() if none exists).
-  // Requirement: does not read the value at `*iter`.
-  iterator erase(iterator iter);
-
-  // Erases range. Returns the number of keys erased and an iterator pointing
-  // to the element after the last erased element.
-  std::pair<size_type, iterator> erase_range(iterator begin, iterator end);
-
-  // Finds the iterator corresponding to a key or returns end() if the key is
-  // not present.
-  template <typename K>
-  iterator find(const K &key) {
-    return internal_end(internal_find(key));
-  }
-  template <typename K>
-  const_iterator find(const K &key) const {
-    return internal_end(internal_find(key));
-  }
-
-  // Clear the btree, deleting all of the values it contains.
-  void clear();
-
-  // Swaps the contents of `this` and `other`.
-  void swap(btree &other);
-
-  const key_compare &key_comp() const noexcept {
-    return root_.template get<0>();
-  }
-  template <typename K1, typename K2>
-  bool compare_keys(const K1 &a, const K2 &b) const {
-    return compare_internal::compare_result_as_less_than(key_comp()(a, b));
-  }
-
-  value_compare value_comp() const { return value_compare(key_comp()); }
-
-  // Verifies the structure of the btree.
-  void verify() const;
-
-  // Size routines.
-  size_type size() const { return size_; }
-  size_type max_size() const { return (std::numeric_limits<size_type>::max)(); }
-  bool empty() const { return size_ == 0; }
-
-  // The height of the btree. An empty tree will have height 0.
-  size_type height() const {
-    size_type h = 0;
-    if (!empty()) {
-      // Count the length of the chain from the leftmost node up to the
-      // root. We actually count from the root back around to the level below
-      // the root, but the calculation is the same because of the circularity
-      // of that traversal.
-      const node_type *n = root();
-      do {
-        ++h;
-        n = n->parent();
-      } while (n != root());
-    }
-    return h;
-  }
-
-  // The number of internal, leaf and total nodes used by the btree.
-  size_type leaf_nodes() const { return internal_stats(root()).leaf_nodes; }
-  size_type internal_nodes() const {
-    return internal_stats(root()).internal_nodes;
-  }
-  size_type nodes() const {
-    node_stats stats = internal_stats(root());
-    return stats.leaf_nodes + stats.internal_nodes;
-  }
-
-  // The total number of bytes used by the btree.
-  size_type bytes_used() const {
-    node_stats stats = internal_stats(root());
-    if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) {
-      return sizeof(*this) + node_type::LeafSize(root()->max_count());
-    } else {
-      return sizeof(*this) + stats.leaf_nodes * node_type::LeafSize() +
-             stats.internal_nodes * node_type::InternalSize();
-    }
-  }
-
-  // The average number of bytes used per value stored in the btree.
-  static double average_bytes_per_value() {
-    // Returns the number of bytes per value on a leaf node that is 75%
-    // full. Experimentally, this matches up nicely with the computed number of
-    // bytes per value in trees that had their values inserted in random order.
-    return node_type::LeafSize() / (kNodeValues * 0.75);
-  }
-
-  // The fullness of the btree. Computed as the number of elements in the btree
-  // divided by the maximum number of elements a tree with the current number
-  // of nodes could hold. A value of 1 indicates perfect space
-  // utilization. Smaller values indicate space wastage.
-  // Returns 0 for empty trees.
-  double fullness() const {
-    if (empty()) return 0.0;
-    return static_cast<double>(size()) / (nodes() * kNodeValues);
-  }
-  // The overhead of the btree structure in bytes per node. Computed as the
-  // total number of bytes used by the btree minus the number of bytes used for
-  // storing elements divided by the number of elements.
-  // Returns 0 for empty trees.
-  double overhead() const {
-    if (empty()) return 0.0;
-    return (bytes_used() - size() * sizeof(value_type)) /
-           static_cast<double>(size());
-  }
-
-  // The allocator used by the btree.
-  allocator_type get_allocator() const { return allocator(); }
-
- private:
-  // Internal accessor routines.
-  node_type *root() { return root_.template get<2>(); }
-  const node_type *root() const { return root_.template get<2>(); }
-  node_type *&mutable_root() noexcept { return root_.template get<2>(); }
-  key_compare *mutable_key_comp() noexcept { return &root_.template get<0>(); }
-
-  // The leftmost node is stored as the parent of the root node.
-  node_type *leftmost() { return root()->parent(); }
-  const node_type *leftmost() const { return root()->parent(); }
-
-  // Allocator routines.
-  allocator_type *mutable_allocator() noexcept {
-    return &root_.template get<1>();
-  }
-  const allocator_type &allocator() const noexcept {
-    return root_.template get<1>();
-  }
-
-  // Allocates a correctly aligned node of at least size bytes using the
-  // allocator.
-  node_type *allocate(const size_type size) {
-    return reinterpret_cast<node_type *>(
-        absl::container_internal::Allocate<node_type::Alignment()>(
-            mutable_allocator(), size));
-  }
-
-  // Node creation/deletion routines.
-  node_type *new_internal_node(node_type *parent) {
-    node_type *n = allocate(node_type::InternalSize());
-    n->init_internal(parent);
-    return n;
-  }
-  node_type *new_leaf_node(node_type *parent) {
-    node_type *n = allocate(node_type::LeafSize());
-    n->init_leaf(parent, kNodeValues);
-    return n;
-  }
-  node_type *new_leaf_root_node(const int max_count) {
-    node_type *n = allocate(node_type::LeafSize(max_count));
-    n->init_leaf(/*parent=*/n, max_count);
-    return n;
-  }
-
-  // Deletion helper routines.
-  iterator rebalance_after_delete(iterator iter);
-
-  // Rebalances or splits the node iter points to.
-  void rebalance_or_split(iterator *iter);
-
-  // Merges the values of left, right and the delimiting key on their parent
-  // onto left, removing the delimiting key and deleting right.
-  void merge_nodes(node_type *left, node_type *right);
-
-  // Tries to merge node with its left or right sibling, and failing that,
-  // rebalance with its left or right sibling. Returns true if a merge
-  // occurred, at which point it is no longer valid to access node. Returns
-  // false if no merging took place.
-  bool try_merge_or_rebalance(iterator *iter);
-
-  // Tries to shrink the height of the tree by 1.
-  void try_shrink();
-
-  iterator internal_end(iterator iter) {
-    return iter.node != nullptr ? iter : end();
-  }
-  const_iterator internal_end(const_iterator iter) const {
-    return iter.node != nullptr ? iter : end();
-  }
-
-  // Emplaces a value into the btree immediately before iter. Requires that
-  // key(v) <= iter.key() and (--iter).key() <= key(v).
-  template <typename... Args>
-  iterator internal_emplace(iterator iter, Args &&... args);
-
-  // Returns an iterator pointing to the first value >= the value "iter" is
-  // pointing at. Note that "iter" might be pointing to an invalid location such
-  // as iter.position == iter.node->finish(). This routine simply moves iter up
-  // in the tree to a valid location.
-  // Requires: iter.node is non-null.
-  template <typename IterType>
-  static IterType internal_last(IterType iter);
-
-  // Returns an iterator pointing to the leaf position at which key would
-  // reside in the tree, unless there is an exact match - in which case, the
-  // result may not be on a leaf. When there's a three-way comparator, we can
-  // return whether there was an exact match. This allows the caller to avoid a
-  // subsequent comparison to determine if an exact match was made, which is
-  // important for keys with expensive comparison, such as strings.
-  template <typename K>
-  SearchResult<iterator, is_key_compare_to::value> internal_locate(
-      const K &key) const;
-
-  // Internal routine which implements lower_bound().
-  template <typename K>
-  SearchResult<iterator, is_key_compare_to::value> internal_lower_bound(
-      const K &key) const;
-
-  // Internal routine which implements upper_bound().
-  template <typename K>
-  iterator internal_upper_bound(const K &key) const;
-
-  // Internal routine which implements find().
-  template <typename K>
-  iterator internal_find(const K &key) const;
-
-  // Verifies the tree structure of node.
-  int internal_verify(const node_type *node, const key_type *lo,
-                      const key_type *hi) const;
-
-  node_stats internal_stats(const node_type *node) const {
-    // The root can be a static empty node.
-    if (node == nullptr || (node == root() && empty())) {
-      return node_stats(0, 0);
-    }
-    if (node->leaf()) {
-      return node_stats(1, 0);
-    }
-    node_stats res(0, 1);
-    for (int i = node->start(); i <= node->finish(); ++i) {
-      res += internal_stats(node->child(i));
-    }
-    return res;
-  }
-
-  // We use compressed tuple in order to save space because key_compare and
-  // allocator_type are usually empty.
-  absl::container_internal::CompressedTuple<key_compare, allocator_type,
-                                            node_type *>
-      root_;
-
-  // A pointer to the rightmost node. Note that the leftmost node is stored as
-  // the root's parent.
-  node_type *rightmost_;
-
-  // Number of values.
-  size_type size_;
-};
-
-////
-// btree_node methods
-template <typename P>
-template <typename... Args>
-inline void btree_node<P>::emplace_value(const size_type i,
-                                         allocator_type *alloc,
-                                         Args &&... args) {
-  assert(i >= start());
-  assert(i <= finish());
-  // Shift old values to create space for new value and then construct it in
-  // place.
-  if (i < finish()) {
-    transfer_n_backward(finish() - i, /*dest_i=*/i + 1, /*src_i=*/i, this,
-                        alloc);
-  }
-  value_init(i, alloc, std::forward<Args>(args)...);
-  set_finish(finish() + 1);
-
-  if (!leaf() && finish() > i + 1) {
-    for (int j = finish(); j > i + 1; --j) {
-      set_child(j, child(j - 1));
-    }
-    clear_child(i + 1);
-  }
-}
-
-template <typename P>
-inline void btree_node<P>::remove_values(const field_type i,
-                                         const field_type to_erase,
-                                         allocator_type *alloc) {
-  // Transfer values after the removed range into their new places.
-  value_destroy_n(i, to_erase, alloc);
-  const field_type orig_finish = finish();
-  const field_type src_i = i + to_erase;
-  transfer_n(orig_finish - src_i, i, src_i, this, alloc);
-
-  if (!leaf()) {
-    // Delete all children between begin and end.
-    for (int j = 0; j < to_erase; ++j) {
-      clear_and_delete(child(i + j + 1), alloc);
-    }
-    // Rotate children after end into new positions.
-    for (int j = i + to_erase + 1; j <= orig_finish; ++j) {
-      set_child(j - to_erase, child(j));
-      clear_child(j);
-    }
-  }
-  set_finish(orig_finish - to_erase);
-}
-
-template <typename P>
-void btree_node<P>::rebalance_right_to_left(const int to_move,
-                                            btree_node *right,
-                                            allocator_type *alloc) {
-  assert(parent() == right->parent());
-  assert(position() + 1 == right->position());
-  assert(right->count() >= count());
-  assert(to_move >= 1);
-  assert(to_move <= right->count());
-
-  // 1) Move the delimiting value in the parent to the left node.
-  transfer(finish(), position(), parent(), alloc);
-
-  // 2) Move the (to_move - 1) values from the right node to the left node.
-  transfer_n(to_move - 1, finish() + 1, right->start(), right, alloc);
-
-  // 3) Move the new delimiting value to the parent from the right node.
-  parent()->transfer(position(), right->start() + to_move - 1, right, alloc);
-
-  // 4) Shift the values in the right node to their correct positions.
-  right->transfer_n(right->count() - to_move, right->start(),
-                    right->start() + to_move, right, alloc);
-
-  if (!leaf()) {
-    // Move the child pointers from the right to the left node.
-    for (int i = 0; i < to_move; ++i) {
-      init_child(finish() + i + 1, right->child(i));
-    }
-    for (int i = right->start(); i <= right->finish() - to_move; ++i) {
-      assert(i + to_move <= right->max_count());
-      right->init_child(i, right->child(i + to_move));
-      right->clear_child(i + to_move);
-    }
-  }
-
-  // Fixup `finish` on the left and right nodes.
-  set_finish(finish() + to_move);
-  right->set_finish(right->finish() - to_move);
-}
-
-template <typename P>
-void btree_node<P>::rebalance_left_to_right(const int to_move,
-                                            btree_node *right,
-                                            allocator_type *alloc) {
-  assert(parent() == right->parent());
-  assert(position() + 1 == right->position());
-  assert(count() >= right->count());
-  assert(to_move >= 1);
-  assert(to_move <= count());
-
-  // Values in the right node are shifted to the right to make room for the
-  // new to_move values. Then, the delimiting value in the parent and the
-  // other (to_move - 1) values in the left node are moved into the right node.
-  // Lastly, a new delimiting value is moved from the left node into the
-  // parent, and the remaining empty left node entries are destroyed.
-
-  // 1) Shift existing values in the right node to their correct positions.
-  right->transfer_n_backward(right->count(), right->start() + to_move,
-                             right->start(), right, alloc);
-
-  // 2) Move the delimiting value in the parent to the right node.
-  right->transfer(right->start() + to_move - 1, position(), parent(), alloc);
-
-  // 3) Move the (to_move - 1) values from the left node to the right node.
-  right->transfer_n(to_move - 1, right->start(), finish() - (to_move - 1), this,
-                    alloc);
-
-  // 4) Move the new delimiting value to the parent from the left node.
-  parent()->transfer(position(), finish() - to_move, this, alloc);
-
-  if (!leaf()) {
-    // Move the child pointers from the left to the right node.
-    for (int i = right->finish(); i >= right->start(); --i) {
-      right->init_child(i + to_move, right->child(i));
-      right->clear_child(i);
-    }
-    for (int i = 1; i <= to_move; ++i) {
-      right->init_child(i - 1, child(finish() - to_move + i));
-      clear_child(finish() - to_move + i);
-    }
-  }
-
-  // Fixup the counts on the left and right nodes.
-  set_finish(finish() - to_move);
-  right->set_finish(right->finish() + to_move);
-}
-
-template <typename P>
-void btree_node<P>::split(const int insert_position, btree_node *dest,
-                          allocator_type *alloc) {
-  assert(dest->count() == 0);
-  assert(max_count() == kNodeValues);
-
-  // We bias the split based on the position being inserted. If we're
-  // inserting at the beginning of the left node then bias the split to put
-  // more values on the right node. If we're inserting at the end of the
-  // right node then bias the split to put more values on the left node.
-  if (insert_position == start()) {
-    dest->set_finish(dest->start() + finish() - 1);
-  } else if (insert_position == kNodeValues) {
-    dest->set_finish(dest->start());
-  } else {
-    dest->set_finish(dest->start() + count() / 2);
-  }
-  set_finish(finish() - dest->count());
-  assert(count() >= 1);
-
-  // Move values from the left sibling to the right sibling.
-  dest->transfer_n(dest->count(), dest->start(), finish(), this, alloc);
-
-  // The split key is the largest value in the left sibling.
-  --mutable_finish();
-  parent()->emplace_value(position(), alloc, finish_slot());
-  value_destroy(finish(), alloc);
-  parent()->init_child(position() + 1, dest);
-
-  if (!leaf()) {
-    for (int i = dest->start(), j = finish() + 1; i <= dest->finish();
-         ++i, ++j) {
-      assert(child(j) != nullptr);
-      dest->init_child(i, child(j));
-      clear_child(j);
-    }
-  }
-}
-
-template <typename P>
-void btree_node<P>::merge(btree_node *src, allocator_type *alloc) {
-  assert(parent() == src->parent());
-  assert(position() + 1 == src->position());
-
-  // Move the delimiting value to the left node.
-  value_init(finish(), alloc, parent()->slot(position()));
-
-  // Move the values from the right to the left node.
-  transfer_n(src->count(), finish() + 1, src->start(), src, alloc);
-
-  if (!leaf()) {
-    // Move the child pointers from the right to the left node.
-    for (int i = src->start(), j = finish() + 1; i <= src->finish(); ++i, ++j) {
-      init_child(j, src->child(i));
-      src->clear_child(i);
-    }
-  }
-
-  // Fixup `finish` on the src and dest nodes.
-  set_finish(start() + 1 + count() + src->count());
-  src->set_finish(src->start());
-
-  // Remove the value on the parent node and delete the src node.
-  parent()->remove_values(position(), /*to_erase=*/1, alloc);
-}
-
-template <typename P>
-void btree_node<P>::clear_and_delete(btree_node *node, allocator_type *alloc) {
-  if (node->leaf()) {
-    node->value_destroy_n(node->start(), node->count(), alloc);
-    deallocate(LeafSize(node->max_count()), node, alloc);
-    return;
-  }
-  if (node->count() == 0) {
-    deallocate(InternalSize(), node, alloc);
-    return;
-  }
-
-  // The parent of the root of the subtree we are deleting.
-  btree_node *delete_root_parent = node->parent();
-
-  // Navigate to the leftmost leaf under node, and then delete upwards.
-  while (!node->leaf()) node = node->start_child();
-  // Use `int` because `pos` needs to be able to hold `kNodeValues+1`, which
-  // isn't guaranteed to be a valid `field_type`.
-  int pos = node->position();
-  btree_node *parent = node->parent();
-  for (;;) {
-    // In each iteration of the next loop, we delete one leaf node and go right.
-    assert(pos <= parent->finish());
-    do {
-      node = parent->child(pos);
-      if (!node->leaf()) {
-        // Navigate to the leftmost leaf under node.
-        while (!node->leaf()) node = node->start_child();
-        pos = node->position();
-        parent = node->parent();
-      }
-      node->value_destroy_n(node->start(), node->count(), alloc);
-      deallocate(LeafSize(node->max_count()), node, alloc);
-      ++pos;
-    } while (pos <= parent->finish());
-
-    // Once we've deleted all children of parent, delete parent and go up/right.
-    assert(pos > parent->finish());
-    do {
-      node = parent;
-      pos = node->position();
-      parent = node->parent();
-      node->value_destroy_n(node->start(), node->count(), alloc);
-      deallocate(InternalSize(), node, alloc);
-      if (parent == delete_root_parent) return;
-      ++pos;
-    } while (pos > parent->finish());
-  }
-}
-
-////
-// btree_iterator methods
-template <typename N, typename R, typename P>
-void btree_iterator<N, R, P>::increment_slow() {
-  if (node->leaf()) {
-    assert(position >= node->finish());
-    btree_iterator save(*this);
-    while (position == node->finish() && !node->is_root()) {
-      assert(node->parent()->child(node->position()) == node);
-      position = node->position();
-      node = node->parent();
-    }
-    // TODO(ezb): assert we aren't incrementing end() instead of handling.
-    if (position == node->finish()) {
-      *this = save;
-    }
-  } else {
-    assert(position < node->finish());
-    node = node->child(position + 1);
-    while (!node->leaf()) {
-      node = node->start_child();
-    }
-    position = node->start();
-  }
-}
-
-template <typename N, typename R, typename P>
-void btree_iterator<N, R, P>::decrement_slow() {
-  if (node->leaf()) {
-    assert(position <= -1);
-    btree_iterator save(*this);
-    while (position < node->start() && !node->is_root()) {
-      assert(node->parent()->child(node->position()) == node);
-      position = node->position() - 1;
-      node = node->parent();
-    }
-    // TODO(ezb): assert we aren't decrementing begin() instead of handling.
-    if (position < node->start()) {
-      *this = save;
-    }
-  } else {
-    assert(position >= node->start());
-    node = node->child(position);
-    while (!node->leaf()) {
-      node = node->child(node->finish());
-    }
-    position = node->finish() - 1;
-  }
-}
-
-////
-// btree methods
-template <typename P>
-template <typename Btree>
-void btree<P>::copy_or_move_values_in_order(Btree &other) {
-  static_assert(std::is_same<btree, Btree>::value ||
-                    std::is_same<const btree, Btree>::value,
-                "Btree type must be same or const.");
-  assert(empty());
-
-  // We can avoid key comparisons because we know the order of the
-  // values is the same order we'll store them in.
-  auto iter = other.begin();
-  if (iter == other.end()) return;
-  insert_multi(maybe_move_from_iterator(iter));
-  ++iter;
-  for (; iter != other.end(); ++iter) {
-    // If the btree is not empty, we can just insert the new value at the end
-    // of the tree.
-    internal_emplace(end(), maybe_move_from_iterator(iter));
-  }
-}
-
-template <typename P>
-constexpr bool btree<P>::static_assert_validation() {
-  static_assert(std::is_nothrow_copy_constructible<key_compare>::value,
-                "Key comparison must be nothrow copy constructible");
-  static_assert(std::is_nothrow_copy_constructible<allocator_type>::value,
-                "Allocator must be nothrow copy constructible");
-  static_assert(type_traits_internal::is_trivially_copyable<iterator>::value,
-                "iterator not trivially copyable.");
-
-  // Note: We assert that kTargetValues, which is computed from
-  // Params::kTargetNodeSize, must fit the node_type::field_type.
-  static_assert(
-      kNodeValues < (1 << (8 * sizeof(typename node_type::field_type))),
-      "target node size too large");
-
-  // Verify that key_compare returns an absl::{weak,strong}_ordering or bool.
-  using compare_result_type =
-      absl::result_of_t<key_compare(key_type, key_type)>;
-  static_assert(
-      std::is_same<compare_result_type, bool>::value ||
-          std::is_convertible<compare_result_type, absl::weak_ordering>::value,
-      "key comparison function must return absl::{weak,strong}_ordering or "
-      "bool.");
-
-  // Test the assumption made in setting kNodeValueSpace.
-  static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4,
-                "node space assumption incorrect");
-
-  return true;
-}
-
-template <typename P>
-template <typename K>
-auto btree<P>::equal_range(const K &key) -> std::pair<iterator, iterator> {
-  const SearchResult<iterator, is_key_compare_to::value> res =
-      internal_lower_bound(key);
-  const iterator lower = internal_end(res.value);
-  if (res.HasMatch() ? !res.IsEq()
-                     : lower == end() || compare_keys(key, lower.key())) {
-    return {lower, lower};
-  }
-
-  const iterator next = std::next(lower);
-  // When the comparator is heterogeneous, we can't assume that comparison with
-  // non-`key_type` will be equivalent to `key_type` comparisons so there
-  // could be multiple equivalent keys even in a unique-container. But for
-  // heterogeneous comparisons from the default string adapted comparators, we
-  // don't need to worry about this.
-  if (!is_multi_container::value &&
-      (std::is_same<K, key_type>::value || is_key_compare_adapted::value)) {
-    // The next iterator after lower must point to a key greater than `key`.
-    // Note: if this assert fails, then it may indicate that the comparator does
-    // not meet the equivalence requirements for Compare
-    // (see https://en.cppreference.com/w/cpp/named_req/Compare).
-    assert(next == end() || compare_keys(key, next.key()));
-    return {lower, next};
-  }
-  // Try once more to avoid the call to upper_bound() if there's only one
-  // equivalent key. This should prevent all calls to upper_bound() in cases of
-  // unique-containers with heterogeneous comparators in which all comparison
-  // operators have the same equivalence classes.
-  if (next == end() || compare_keys(key, next.key())) return {lower, next};
-
-  // In this case, we need to call upper_bound() to avoid worst case O(N)
-  // behavior if we were to iterate over equal keys.
-  return {lower, upper_bound(key)};
-}
-
-template <typename P>
-template <typename K, typename... Args>
-auto btree<P>::insert_unique(const K &key, Args &&... args)
-    -> std::pair<iterator, bool> {
-  if (empty()) {
-    mutable_root() = rightmost_ = new_leaf_root_node(1);
-  }
-
-  SearchResult<iterator, is_key_compare_to::value> res = internal_locate(key);
-  iterator iter = res.value;
-
-  if (res.HasMatch()) {
-    if (res.IsEq()) {
-      // The key already exists in the tree, do nothing.
-      return {iter, false};
-    }
-  } else {
-    iterator last = internal_last(iter);
-    if (last.node && !compare_keys(key, last.key())) {
-      // The key already exists in the tree, do nothing.
-      return {last, false};
-    }
-  }
-  return {internal_emplace(iter, std::forward<Args>(args)...), true};
-}
-
-template <typename P>
-template <typename K, typename... Args>
-inline auto btree<P>::insert_hint_unique(iterator position, const K &key,
-                                         Args &&... args)
-    -> std::pair<iterator, bool> {
-  if (!empty()) {
-    if (position == end() || compare_keys(key, position.key())) {
-      if (position == begin() || compare_keys(std::prev(position).key(), key)) {
-        // prev.key() < key < position.key()
-        return {internal_emplace(position, std::forward<Args>(args)...), true};
-      }
-    } else if (compare_keys(position.key(), key)) {
-      ++position;
-      if (position == end() || compare_keys(key, position.key())) {
-        // {original `position`}.key() < key < {current `position`}.key()
-        return {internal_emplace(position, std::forward<Args>(args)...), true};
-      }
-    } else {
-      // position.key() == key
-      return {position, false};
-    }
-  }
-  return insert_unique(key, std::forward<Args>(args)...);
-}
-
-template <typename P>
-template <typename InputIterator, typename>
-void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e, int) {
-  for (; b != e; ++b) {
-    insert_hint_unique(end(), params_type::key(*b), *b);
-  }
-}
-
-template <typename P>
-template <typename InputIterator>
-void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e, char) {
-  for (; b != e; ++b) {
-    init_type value(*b);
-    insert_hint_unique(end(), params_type::key(value), std::move(value));
-  }
-}
-
-template <typename P>
-template <typename ValueType>
-auto btree<P>::insert_multi(const key_type &key, ValueType &&v) -> iterator {
-  if (empty()) {
-    mutable_root() = rightmost_ = new_leaf_root_node(1);
-  }
-
-  iterator iter = internal_upper_bound(key);
-  if (iter.node == nullptr) {
-    iter = end();
-  }
-  return internal_emplace(iter, std::forward<ValueType>(v));
-}
-
-template <typename P>
-template <typename ValueType>
-auto btree<P>::insert_hint_multi(iterator position, ValueType &&v) -> iterator {
-  if (!empty()) {
-    const key_type &key = params_type::key(v);
-    if (position == end() || !compare_keys(position.key(), key)) {
-      if (position == begin() ||
-          !compare_keys(key, std::prev(position).key())) {
-        // prev.key() <= key <= position.key()
-        return internal_emplace(position, std::forward<ValueType>(v));
-      }
-    } else {
-      ++position;
-      if (position == end() || !compare_keys(position.key(), key)) {
-        // {original `position`}.key() < key < {current `position`}.key()
-        return internal_emplace(position, std::forward<ValueType>(v));
-      }
-    }
-  }
-  return insert_multi(std::forward<ValueType>(v));
-}
-
-template <typename P>
-template <typename InputIterator>
-void btree<P>::insert_iterator_multi(InputIterator b, InputIterator e) {
-  for (; b != e; ++b) {
-    insert_hint_multi(end(), *b);
-  }
-}
-
-template <typename P>
-auto btree<P>::operator=(const btree &other) -> btree & {
-  if (this != &other) {
-    clear();
-
-    *mutable_key_comp() = other.key_comp();
-    if (absl::allocator_traits<
-            allocator_type>::propagate_on_container_copy_assignment::value) {
-      *mutable_allocator() = other.allocator();
-    }
-
-    copy_or_move_values_in_order(other);
-  }
-  return *this;
-}
-
-template <typename P>
-auto btree<P>::operator=(btree &&other) noexcept -> btree & {
-  if (this != &other) {
-    clear();
-
-    using std::swap;
-    if (absl::allocator_traits<
-            allocator_type>::propagate_on_container_copy_assignment::value) {
-      // Note: `root_` also contains the allocator and the key comparator.
-      swap(root_, other.root_);
-      swap(rightmost_, other.rightmost_);
-      swap(size_, other.size_);
-    } else {
-      if (allocator() == other.allocator()) {
-        swap(mutable_root(), other.mutable_root());
-        swap(*mutable_key_comp(), *other.mutable_key_comp());
-        swap(rightmost_, other.rightmost_);
-        swap(size_, other.size_);
-      } else {
-        // We aren't allowed to propagate the allocator and the allocator is
-        // different so we can't take over its memory. We must move each element
-        // individually. We need both `other` and `this` to have `other`s key
-        // comparator while moving the values so we can't swap the key
-        // comparators.
-        *mutable_key_comp() = other.key_comp();
-        copy_or_move_values_in_order(other);
-      }
-    }
-  }
-  return *this;
-}
-
-template <typename P>
-auto btree<P>::erase(iterator iter) -> iterator {
-  bool internal_delete = false;
-  if (!iter.node->leaf()) {
-    // Deletion of a value on an internal node. First, move the largest value
-    // from our left child here, then delete that position (in remove_values()
-    // below). We can get to the largest value from our left child by
-    // decrementing iter.
-    iterator internal_iter(iter);
-    --iter;
-    assert(iter.node->leaf());
-    params_type::move(mutable_allocator(), iter.node->slot(iter.position),
-                      internal_iter.node->slot(internal_iter.position));
-    internal_delete = true;
-  }
-
-  // Delete the key from the leaf.
-  iter.node->remove_values(iter.position, /*to_erase=*/1, mutable_allocator());
-  --size_;
-
-  // We want to return the next value after the one we just erased. If we
-  // erased from an internal node (internal_delete == true), then the next
-  // value is ++(++iter). If we erased from a leaf node (internal_delete ==
-  // false) then the next value is ++iter. Note that ++iter may point to an
-  // internal node and the value in the internal node may move to a leaf node
-  // (iter.node) when rebalancing is performed at the leaf level.
-
-  iterator res = rebalance_after_delete(iter);
-
-  // If we erased from an internal node, advance the iterator.
-  if (internal_delete) {
-    ++res;
-  }
-  return res;
-}
-
-template <typename P>
-auto btree<P>::rebalance_after_delete(iterator iter) -> iterator {
-  // Merge/rebalance as we walk back up the tree.
-  iterator res(iter);
-  bool first_iteration = true;
-  for (;;) {
-    if (iter.node == root()) {
-      try_shrink();
-      if (empty()) {
-        return end();
-      }
-      break;
-    }
-    if (iter.node->count() >= kMinNodeValues) {
-      break;
-    }
-    bool merged = try_merge_or_rebalance(&iter);
-    // On the first iteration, we should update `res` with `iter` because `res`
-    // may have been invalidated.
-    if (first_iteration) {
-      res = iter;
-      first_iteration = false;
-    }
-    if (!merged) {
-      break;
-    }
-    iter.position = iter.node->position();
-    iter.node = iter.node->parent();
-  }
-
-  // Adjust our return value. If we're pointing at the end of a node, advance
-  // the iterator.
-  if (res.position == res.node->finish()) {
-    res.position = res.node->finish() - 1;
-    ++res;
-  }
-
-  return res;
-}
-
-template <typename P>
-auto btree<P>::erase_range(iterator begin, iterator end)
-    -> std::pair<size_type, iterator> {
-  difference_type count = std::distance(begin, end);
-  assert(count >= 0);
-
-  if (count == 0) {
-    return {0, begin};
-  }
-
-  if (count == size_) {
-    clear();
-    return {count, this->end()};
-  }
-
-  if (begin.node == end.node) {
-    assert(end.position > begin.position);
-    begin.node->remove_values(begin.position, end.position - begin.position,
-                              mutable_allocator());
-    size_ -= count;
-    return {count, rebalance_after_delete(begin)};
-  }
-
-  const size_type target_size = size_ - count;
-  while (size_ > target_size) {
-    if (begin.node->leaf()) {
-      const size_type remaining_to_erase = size_ - target_size;
-      const size_type remaining_in_node = begin.node->finish() - begin.position;
-      const size_type to_erase =
-          (std::min)(remaining_to_erase, remaining_in_node);
-      begin.node->remove_values(begin.position, to_erase, mutable_allocator());
-      size_ -= to_erase;
-      begin = rebalance_after_delete(begin);
-    } else {
-      begin = erase(begin);
-    }
-  }
-  return {count, begin};
-}
-
-template <typename P>
-void btree<P>::clear() {
-  if (!empty()) {
-    node_type::clear_and_delete(root(), mutable_allocator());
-  }
-  mutable_root() = EmptyNode();
-  rightmost_ = EmptyNode();
-  size_ = 0;
-}
-
-template <typename P>
-void btree<P>::swap(btree &other) {
-  using std::swap;
-  if (absl::allocator_traits<
-          allocator_type>::propagate_on_container_swap::value) {
-    // Note: `root_` also contains the allocator and the key comparator.
-    swap(root_, other.root_);
-  } else {
-    // It's undefined behavior if the allocators are unequal here.
-    assert(allocator() == other.allocator());
-    swap(mutable_root(), other.mutable_root());
-    swap(*mutable_key_comp(), *other.mutable_key_comp());
-  }
-  swap(rightmost_, other.rightmost_);
-  swap(size_, other.size_);
-}
-
-template <typename P>
-void btree<P>::verify() const {
-  assert(root() != nullptr);
-  assert(leftmost() != nullptr);
-  assert(rightmost_ != nullptr);
-  assert(empty() || size() == internal_verify(root(), nullptr, nullptr));
-  assert(leftmost() == (++const_iterator(root(), -1)).node);
-  assert(rightmost_ == (--const_iterator(root(), root()->finish())).node);
-  assert(leftmost()->leaf());
-  assert(rightmost_->leaf());
-}
-
-template <typename P>
-void btree<P>::rebalance_or_split(iterator *iter) {
-  node_type *&node = iter->node;
-  int &insert_position = iter->position;
-  assert(node->count() == node->max_count());
-  assert(kNodeValues == node->max_count());
-
-  // First try to make room on the node by rebalancing.
-  node_type *parent = node->parent();
-  if (node != root()) {
-    if (node->position() > parent->start()) {
-      // Try rebalancing with our left sibling.
-      node_type *left = parent->child(node->position() - 1);
-      assert(left->max_count() == kNodeValues);
-      if (left->count() < kNodeValues) {
-        // We bias rebalancing based on the position being inserted. If we're
-        // inserting at the end of the right node then we bias rebalancing to
-        // fill up the left node.
-        int to_move = (kNodeValues - left->count()) /
-                      (1 + (insert_position < static_cast<int>(kNodeValues)));
-        to_move = (std::max)(1, to_move);
-
-        if (insert_position - to_move >= node->start() ||
-            left->count() + to_move < static_cast<int>(kNodeValues)) {
-          left->rebalance_right_to_left(to_move, node, mutable_allocator());
-
-          assert(node->max_count() - node->count() == to_move);
-          insert_position = insert_position - to_move;
-          if (insert_position < node->start()) {
-            insert_position = insert_position + left->count() + 1;
-            node = left;
-          }
-
-          assert(node->count() < node->max_count());
-          return;
-        }
-      }
-    }
-
-    if (node->position() < parent->finish()) {
-      // Try rebalancing with our right sibling.
-      node_type *right = parent->child(node->position() + 1);
-      assert(right->max_count() == kNodeValues);
-      if (right->count() < kNodeValues) {
-        // We bias rebalancing based on the position being inserted. If we're
-        // inserting at the beginning of the left node then we bias rebalancing
-        // to fill up the right node.
-        int to_move = (static_cast<int>(kNodeValues) - right->count()) /
-                      (1 + (insert_position > node->start()));
-        to_move = (std::max)(1, to_move);
-
-        if (insert_position <= node->finish() - to_move ||
-            right->count() + to_move < static_cast<int>(kNodeValues)) {
-          node->rebalance_left_to_right(to_move, right, mutable_allocator());
-
-          if (insert_position > node->finish()) {
-            insert_position = insert_position - node->count() - 1;
-            node = right;
-          }
-
-          assert(node->count() < node->max_count());
-          return;
-        }
-      }
-    }
-
-    // Rebalancing failed, make sure there is room on the parent node for a new
-    // value.
-    assert(parent->max_count() == kNodeValues);
-    if (parent->count() == kNodeValues) {
-      iterator parent_iter(node->parent(), node->position());
-      rebalance_or_split(&parent_iter);
-    }
-  } else {
-    // Rebalancing not possible because this is the root node.
-    // Create a new root node and set the current root node as the child of the
-    // new root.
-    parent = new_internal_node(parent);
-    parent->init_child(parent->start(), root());
-    mutable_root() = parent;
-    // If the former root was a leaf node, then it's now the rightmost node.
-    assert(!parent->start_child()->leaf() ||
-           parent->start_child() == rightmost_);
-  }
-
-  // Split the node.
-  node_type *split_node;
-  if (node->leaf()) {
-    split_node = new_leaf_node(parent);
-    node->split(insert_position, split_node, mutable_allocator());
-    if (rightmost_ == node) rightmost_ = split_node;
-  } else {
-    split_node = new_internal_node(parent);
-    node->split(insert_position, split_node, mutable_allocator());
-  }
-
-  if (insert_position > node->finish()) {
-    insert_position = insert_position - node->count() - 1;
-    node = split_node;
-  }
-}
-
-template <typename P>
-void btree<P>::merge_nodes(node_type *left, node_type *right) {
-  left->merge(right, mutable_allocator());
-  if (rightmost_ == right) rightmost_ = left;
-}
-
-template <typename P>
-bool btree<P>::try_merge_or_rebalance(iterator *iter) {
-  node_type *parent = iter->node->parent();
-  if (iter->node->position() > parent->start()) {
-    // Try merging with our left sibling.
-    node_type *left = parent->child(iter->node->position() - 1);
-    assert(left->max_count() == kNodeValues);
-    if (1U + left->count() + iter->node->count() <= kNodeValues) {
-      iter->position += 1 + left->count();
-      merge_nodes(left, iter->node);
-      iter->node = left;
-      return true;
-    }
-  }
-  if (iter->node->position() < parent->finish()) {
-    // Try merging with our right sibling.
-    node_type *right = parent->child(iter->node->position() + 1);
-    assert(right->max_count() == kNodeValues);
-    if (1U + iter->node->count() + right->count() <= kNodeValues) {
-      merge_nodes(iter->node, right);
-      return true;
-    }
-    // Try rebalancing with our right sibling. We don't perform rebalancing if
-    // we deleted the first element from iter->node and the node is not
-    // empty. This is a small optimization for the common pattern of deleting
-    // from the front of the tree.
-    if (right->count() > kMinNodeValues &&
-        (iter->node->count() == 0 || iter->position > iter->node->start())) {
-      int to_move = (right->count() - iter->node->count()) / 2;
-      to_move = (std::min)(to_move, right->count() - 1);
-      iter->node->rebalance_right_to_left(to_move, right, mutable_allocator());
-      return false;
-    }
-  }
-  if (iter->node->position() > parent->start()) {
-    // Try rebalancing with our left sibling. We don't perform rebalancing if
-    // we deleted the last element from iter->node and the node is not
-    // empty. This is a small optimization for the common pattern of deleting
-    // from the back of the tree.
-    node_type *left = parent->child(iter->node->position() - 1);
-    if (left->count() > kMinNodeValues &&
-        (iter->node->count() == 0 || iter->position < iter->node->finish())) {
-      int to_move = (left->count() - iter->node->count()) / 2;
-      to_move = (std::min)(to_move, left->count() - 1);
-      left->rebalance_left_to_right(to_move, iter->node, mutable_allocator());
-      iter->position += to_move;
-      return false;
-    }
-  }
-  return false;
-}
-
-template <typename P>
-void btree<P>::try_shrink() {
-  node_type *orig_root = root();
-  if (orig_root->count() > 0) {
-    return;
-  }
-  // Deleted the last item on the root node, shrink the height of the tree.
-  if (orig_root->leaf()) {
-    assert(size() == 0);
-    mutable_root() = rightmost_ = EmptyNode();
-  } else {
-    node_type *child = orig_root->start_child();
-    child->make_root();
-    mutable_root() = child;
-  }
-  node_type::clear_and_delete(orig_root, mutable_allocator());
-}
-
-template <typename P>
-template <typename IterType>
-inline IterType btree<P>::internal_last(IterType iter) {
-  assert(iter.node != nullptr);
-  while (iter.position == iter.node->finish()) {
-    iter.position = iter.node->position();
-    iter.node = iter.node->parent();
-    if (iter.node->leaf()) {
-      iter.node = nullptr;
-      break;
-    }
-  }
-  return iter;
-}
-
-template <typename P>
-template <typename... Args>
-inline auto btree<P>::internal_emplace(iterator iter, Args &&... args)
-    -> iterator {
-  if (!iter.node->leaf()) {
-    // We can't insert on an internal node. Instead, we'll insert after the
-    // previous value which is guaranteed to be on a leaf node.
-    --iter;
-    ++iter.position;
-  }
-  const field_type max_count = iter.node->max_count();
-  allocator_type *alloc = mutable_allocator();
-  if (iter.node->count() == max_count) {
-    // Make room in the leaf for the new item.
-    if (max_count < kNodeValues) {
-      // Insertion into the root where the root is smaller than the full node
-      // size. Simply grow the size of the root node.
-      assert(iter.node == root());
-      iter.node =
-          new_leaf_root_node((std::min<int>)(kNodeValues, 2 * max_count));
-      // Transfer the values from the old root to the new root.
-      node_type *old_root = root();
-      node_type *new_root = iter.node;
-      new_root->transfer_n(old_root->count(), new_root->start(),
-                           old_root->start(), old_root, alloc);
-      new_root->set_finish(old_root->finish());
-      old_root->set_finish(old_root->start());
-      node_type::clear_and_delete(old_root, alloc);
-      mutable_root() = rightmost_ = new_root;
-    } else {
-      rebalance_or_split(&iter);
-    }
-  }
-  iter.node->emplace_value(iter.position, alloc, std::forward<Args>(args)...);
-  ++size_;
-  return iter;
-}
-
-template <typename P>
-template <typename K>
-inline auto btree<P>::internal_locate(const K &key) const
-    -> SearchResult<iterator, is_key_compare_to::value> {
-  iterator iter(const_cast<node_type *>(root()));
-  for (;;) {
-    SearchResult<int, is_key_compare_to::value> res =
-        iter.node->lower_bound(key, key_comp());
-    iter.position = res.value;
-    if (res.IsEq()) {
-      return {iter, MatchKind::kEq};
-    }
-    // Note: in the non-key-compare-to case, we don't need to walk all the way
-    // down the tree if the keys are equal, but determining equality would
-    // require doing an extra comparison on each node on the way down, and we
-    // will need to go all the way to the leaf node in the expected case.
-    if (iter.node->leaf()) {
-      break;
-    }
-    iter.node = iter.node->child(iter.position);
-  }
-  // Note: in the non-key-compare-to case, the key may actually be equivalent
-  // here (and the MatchKind::kNe is ignored).
-  return {iter, MatchKind::kNe};
-}
-
-template <typename P>
-template <typename K>
-auto btree<P>::internal_lower_bound(const K &key) const
-    -> SearchResult<iterator, is_key_compare_to::value> {
-  iterator iter(const_cast<node_type *>(root()));
-  SearchResult<int, is_key_compare_to::value> res;
-  bool seen_eq = false;
-  for (;;) {
-    res = iter.node->lower_bound(key, key_comp());
-    iter.position = res.value;
-    // TODO(ezb): we should be able to terminate early on IsEq() if there can't
-    // be multiple equivalent keys in container for this lookup type.
-    if (iter.node->leaf()) {
-      break;
-    }
-    seen_eq = seen_eq || res.IsEq();
-    iter.node = iter.node->child(iter.position);
-  }
-  if (res.IsEq()) return {iter, MatchKind::kEq};
-  return {internal_last(iter), seen_eq ? MatchKind::kEq : MatchKind::kNe};
-}
-
-template <typename P>
-template <typename K>
-auto btree<P>::internal_upper_bound(const K &key) const -> iterator {
-  iterator iter(const_cast<node_type *>(root()));
-  for (;;) {
-    iter.position = iter.node->upper_bound(key, key_comp());
-    if (iter.node->leaf()) {
-      break;
-    }
-    iter.node = iter.node->child(iter.position);
-  }
-  return internal_last(iter);
-}
-
-template <typename P>
-template <typename K>
-auto btree<P>::internal_find(const K &key) const -> iterator {
-  SearchResult<iterator, is_key_compare_to::value> res = internal_locate(key);
-  if (res.HasMatch()) {
-    if (res.IsEq()) {
-      return res.value;
-    }
-  } else {
-    const iterator iter = internal_last(res.value);
-    if (iter.node != nullptr && !compare_keys(key, iter.key())) {
-      return iter;
-    }
-  }
-  return {nullptr, 0};
-}
-
-template <typename P>
-int btree<P>::internal_verify(const node_type *node, const key_type *lo,
-                              const key_type *hi) const {
-  assert(node->count() > 0);
-  assert(node->count() <= node->max_count());
-  if (lo) {
-    assert(!compare_keys(node->key(node->start()), *lo));
-  }
-  if (hi) {
-    assert(!compare_keys(*hi, node->key(node->finish() - 1)));
-  }
-  for (int i = node->start() + 1; i < node->finish(); ++i) {
-    assert(!compare_keys(node->key(i), node->key(i - 1)));
-  }
-  int count = node->count();
-  if (!node->leaf()) {
-    for (int i = node->start(); i <= node->finish(); ++i) {
-      assert(node->child(i) != nullptr);
-      assert(node->child(i)->parent() == node);
-      assert(node->child(i)->position() == i);
-      count += internal_verify(node->child(i),
-                               i == node->start() ? lo : &node->key(i - 1),
-                               i == node->finish() ? hi : &node->key(i));
-    }
-  }
-  return count;
-}
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_BTREE_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/btree_container.h b/third_party/abseil_cpp/absl/container/internal/btree_container.h
deleted file mode 100644
index 887eda4122..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/btree_container.h
+++ /dev/null
@@ -1,683 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_
-#define ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_
-
-#include <algorithm>
-#include <initializer_list>
-#include <iterator>
-#include <utility>
-
-#include "absl/base/internal/throw_delegate.h"
-#include "absl/container/internal/btree.h"  // IWYU pragma: export
-#include "absl/container/internal/common.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// A common base class for btree_set, btree_map, btree_multiset, and
-// btree_multimap.
-template <typename Tree>
-class btree_container {
-  using params_type = typename Tree::params_type;
-
- protected:
-  // Alias used for heterogeneous lookup functions.
-  // `key_arg<K>` evaluates to `K` when the functors are transparent and to
-  // `key_type` otherwise. It permits template argument deduction on `K` for the
-  // transparent case.
-  template <class K>
-  using key_arg =
-      typename KeyArg<IsTransparent<typename Tree::key_compare>::value>::
-          template type<K, typename Tree::key_type>;
-
- public:
-  using key_type = typename Tree::key_type;
-  using value_type = typename Tree::value_type;
-  using size_type = typename Tree::size_type;
-  using difference_type = typename Tree::difference_type;
-  using key_compare = typename Tree::key_compare;
-  using value_compare = typename Tree::value_compare;
-  using allocator_type = typename Tree::allocator_type;
-  using reference = typename Tree::reference;
-  using const_reference = typename Tree::const_reference;
-  using pointer = typename Tree::pointer;
-  using const_pointer = typename Tree::const_pointer;
-  using iterator = typename Tree::iterator;
-  using const_iterator = typename Tree::const_iterator;
-  using reverse_iterator = typename Tree::reverse_iterator;
-  using const_reverse_iterator = typename Tree::const_reverse_iterator;
-  using node_type = typename Tree::node_handle_type;
-
-  // Constructors/assignments.
-  btree_container() : tree_(key_compare(), allocator_type()) {}
-  explicit btree_container(const key_compare &comp,
-                           const allocator_type &alloc = allocator_type())
-      : tree_(comp, alloc) {}
-  explicit btree_container(const allocator_type &alloc)
-      : tree_(key_compare(), alloc) {}
-
-  btree_container(const btree_container &other)
-      : btree_container(other, absl::allocator_traits<allocator_type>::
-                                   select_on_container_copy_construction(
-                                       other.get_allocator())) {}
-  btree_container(const btree_container &other, const allocator_type &alloc)
-      : tree_(other.tree_, alloc) {}
-
-  btree_container(btree_container &&other) noexcept(
-      std::is_nothrow_move_constructible<Tree>::value) = default;
-  btree_container(btree_container &&other, const allocator_type &alloc)
-      : tree_(std::move(other.tree_), alloc) {}
-
-  btree_container &operator=(const btree_container &other) = default;
-  btree_container &operator=(btree_container &&other) noexcept(
-      std::is_nothrow_move_assignable<Tree>::value) = default;
-
-  // Iterator routines.
-  iterator begin() { return tree_.begin(); }
-  const_iterator begin() const { return tree_.begin(); }
-  const_iterator cbegin() const { return tree_.begin(); }
-  iterator end() { return tree_.end(); }
-  const_iterator end() const { return tree_.end(); }
-  const_iterator cend() const { return tree_.end(); }
-  reverse_iterator rbegin() { return tree_.rbegin(); }
-  const_reverse_iterator rbegin() const { return tree_.rbegin(); }
-  const_reverse_iterator crbegin() const { return tree_.rbegin(); }
-  reverse_iterator rend() { return tree_.rend(); }
-  const_reverse_iterator rend() const { return tree_.rend(); }
-  const_reverse_iterator crend() const { return tree_.rend(); }
-
-  // Lookup routines.
-  template <typename K = key_type>
-  size_type count(const key_arg<K> &key) const {
-    auto equal_range = this->equal_range(key);
-    return std::distance(equal_range.first, equal_range.second);
-  }
-  template <typename K = key_type>
-  iterator find(const key_arg<K> &key) {
-    return tree_.find(key);
-  }
-  template <typename K = key_type>
-  const_iterator find(const key_arg<K> &key) const {
-    return tree_.find(key);
-  }
-  template <typename K = key_type>
-  bool contains(const key_arg<K> &key) const {
-    return find(key) != end();
-  }
-  template <typename K = key_type>
-  iterator lower_bound(const key_arg<K> &key) {
-    return tree_.lower_bound(key);
-  }
-  template <typename K = key_type>
-  const_iterator lower_bound(const key_arg<K> &key) const {
-    return tree_.lower_bound(key);
-  }
-  template <typename K = key_type>
-  iterator upper_bound(const key_arg<K> &key) {
-    return tree_.upper_bound(key);
-  }
-  template <typename K = key_type>
-  const_iterator upper_bound(const key_arg<K> &key) const {
-    return tree_.upper_bound(key);
-  }
-  template <typename K = key_type>
-  std::pair<iterator, iterator> equal_range(const key_arg<K> &key) {
-    return tree_.equal_range(key);
-  }
-  template <typename K = key_type>
-  std::pair<const_iterator, const_iterator> equal_range(
-      const key_arg<K> &key) const {
-    return tree_.equal_range(key);
-  }
-
-  // Deletion routines. Note that there is also a deletion routine that is
-  // specific to btree_set_container/btree_multiset_container.
-
-  // Erase the specified iterator from the btree. The iterator must be valid
-  // (i.e. not equal to end()).  Return an iterator pointing to the node after
-  // the one that was erased (or end() if none exists).
-  iterator erase(const_iterator iter) { return tree_.erase(iterator(iter)); }
-  iterator erase(iterator iter) { return tree_.erase(iter); }
-  iterator erase(const_iterator first, const_iterator last) {
-    return tree_.erase_range(iterator(first), iterator(last)).second;
-  }
-  template <typename K = key_type>
-  size_type erase(const key_arg<K> &key) {
-    auto equal_range = this->equal_range(key);
-    return tree_.erase_range(equal_range.first, equal_range.second).first;
-  }
-
-  // Extract routines.
-  node_type extract(iterator position) {
-    // Use Move instead of Transfer, because the rebalancing code expects to
-    // have a valid object to scribble metadata bits on top of.
-    auto node = CommonAccess::Move<node_type>(get_allocator(), position.slot());
-    erase(position);
-    return node;
-  }
-  node_type extract(const_iterator position) {
-    return extract(iterator(position));
-  }
-
-  // Utility routines.
-  void clear() { tree_.clear(); }
-  void swap(btree_container &other) { tree_.swap(other.tree_); }
-  void verify() const { tree_.verify(); }
-
-  // Size routines.
-  size_type size() const { return tree_.size(); }
-  size_type max_size() const { return tree_.max_size(); }
-  bool empty() const { return tree_.empty(); }
-
-  friend bool operator==(const btree_container &x, const btree_container &y) {
-    if (x.size() != y.size()) return false;
-    return std::equal(x.begin(), x.end(), y.begin());
-  }
-
-  friend bool operator!=(const btree_container &x, const btree_container &y) {
-    return !(x == y);
-  }
-
-  friend bool operator<(const btree_container &x, const btree_container &y) {
-    return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());
-  }
-
-  friend bool operator>(const btree_container &x, const btree_container &y) {
-    return y < x;
-  }
-
-  friend bool operator<=(const btree_container &x, const btree_container &y) {
-    return !(y < x);
-  }
-
-  friend bool operator>=(const btree_container &x, const btree_container &y) {
-    return !(x < y);
-  }
-
-  // The allocator used by the btree.
-  allocator_type get_allocator() const { return tree_.get_allocator(); }
-
-  // The key comparator used by the btree.
-  key_compare key_comp() const { return tree_.key_comp(); }
-  value_compare value_comp() const { return tree_.value_comp(); }
-
-  // Support absl::Hash.
-  template <typename State>
-  friend State AbslHashValue(State h, const btree_container &b) {
-    for (const auto &v : b) {
-      h = State::combine(std::move(h), v);
-    }
-    return State::combine(std::move(h), b.size());
-  }
-
- protected:
-  Tree tree_;
-};
-
-// A common base class for btree_set and btree_map.
-template <typename Tree>
-class btree_set_container : public btree_container<Tree> {
-  using super_type = btree_container<Tree>;
-  using params_type = typename Tree::params_type;
-  using init_type = typename params_type::init_type;
-  using is_key_compare_to = typename params_type::is_key_compare_to;
-  friend class BtreeNodePeer;
-
- protected:
-  template <class K>
-  using key_arg = typename super_type::template key_arg<K>;
-
- public:
-  using key_type = typename Tree::key_type;
-  using value_type = typename Tree::value_type;
-  using size_type = typename Tree::size_type;
-  using key_compare = typename Tree::key_compare;
-  using allocator_type = typename Tree::allocator_type;
-  using iterator = typename Tree::iterator;
-  using const_iterator = typename Tree::const_iterator;
-  using node_type = typename super_type::node_type;
-  using insert_return_type = InsertReturnType<iterator, node_type>;
-
-  // Inherit constructors.
-  using super_type::super_type;
-  btree_set_container() {}
-
-  // Range constructors.
-  template <class InputIterator>
-  btree_set_container(InputIterator b, InputIterator e,
-                      const key_compare &comp = key_compare(),
-                      const allocator_type &alloc = allocator_type())
-      : super_type(comp, alloc) {
-    insert(b, e);
-  }
-  template <class InputIterator>
-  btree_set_container(InputIterator b, InputIterator e,
-                      const allocator_type &alloc)
-      : btree_set_container(b, e, key_compare(), alloc) {}
-
-  // Initializer list constructors.
-  btree_set_container(std::initializer_list<init_type> init,
-                      const key_compare &comp = key_compare(),
-                      const allocator_type &alloc = allocator_type())
-      : btree_set_container(init.begin(), init.end(), comp, alloc) {}
-  btree_set_container(std::initializer_list<init_type> init,
-                      const allocator_type &alloc)
-      : btree_set_container(init.begin(), init.end(), alloc) {}
-
-  // Insertion routines.
-  std::pair<iterator, bool> insert(const value_type &v) {
-    return this->tree_.insert_unique(params_type::key(v), v);
-  }
-  std::pair<iterator, bool> insert(value_type &&v) {
-    return this->tree_.insert_unique(params_type::key(v), std::move(v));
-  }
-  template <typename... Args>
-  std::pair<iterator, bool> emplace(Args &&... args) {
-    init_type v(std::forward<Args>(args)...);
-    return this->tree_.insert_unique(params_type::key(v), std::move(v));
-  }
-  iterator insert(const_iterator hint, const value_type &v) {
-    return this->tree_
-        .insert_hint_unique(iterator(hint), params_type::key(v), v)
-        .first;
-  }
-  iterator insert(const_iterator hint, value_type &&v) {
-    return this->tree_
-        .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v))
-        .first;
-  }
-  template <typename... Args>
-  iterator emplace_hint(const_iterator hint, Args &&... args) {
-    init_type v(std::forward<Args>(args)...);
-    return this->tree_
-        .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v))
-        .first;
-  }
-  template <typename InputIterator>
-  void insert(InputIterator b, InputIterator e) {
-    this->tree_.insert_iterator_unique(b, e, 0);
-  }
-  void insert(std::initializer_list<init_type> init) {
-    this->tree_.insert_iterator_unique(init.begin(), init.end(), 0);
-  }
-  insert_return_type insert(node_type &&node) {
-    if (!node) return {this->end(), false, node_type()};
-    std::pair<iterator, bool> res =
-        this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)),
-                                  CommonAccess::GetSlot(node));
-    if (res.second) {
-      CommonAccess::Destroy(&node);
-      return {res.first, true, node_type()};
-    } else {
-      return {res.first, false, std::move(node)};
-    }
-  }
-  iterator insert(const_iterator hint, node_type &&node) {
-    if (!node) return this->end();
-    std::pair<iterator, bool> res = this->tree_.insert_hint_unique(
-        iterator(hint), params_type::key(CommonAccess::GetSlot(node)),
-        CommonAccess::GetSlot(node));
-    if (res.second) CommonAccess::Destroy(&node);
-    return res.first;
-  }
-
-  // Node extraction routines.
-  // TODO(ezb): when the comparator is heterogeneous and has different
-  // equivalence classes for different lookup types, we should extract the first
-  // equivalent value if there are multiple.
-  template <typename K = key_type>
-  node_type extract(const key_arg<K> &key) {
-    auto it = this->find(key);
-    return it == this->end() ? node_type() : extract(it);
-  }
-  using super_type::extract;
-
-  // Merge routines.
-  // Moves elements from `src` into `this`. If the element already exists in
-  // `this`, it is left unmodified in `src`.
-  template <
-      typename T,
-      typename absl::enable_if_t<
-          absl::conjunction<
-              std::is_same<value_type, typename T::value_type>,
-              std::is_same<allocator_type, typename T::allocator_type>,
-              std::is_same<typename params_type::is_map_container,
-                           typename T::params_type::is_map_container>>::value,
-          int> = 0>
-  void merge(btree_container<T> &src) {  // NOLINT
-    for (auto src_it = src.begin(); src_it != src.end();) {
-      if (insert(std::move(params_type::element(src_it.slot()))).second) {
-        src_it = src.erase(src_it);
-      } else {
-        ++src_it;
-      }
-    }
-  }
-
-  template <
-      typename T,
-      typename absl::enable_if_t<
-          absl::conjunction<
-              std::is_same<value_type, typename T::value_type>,
-              std::is_same<allocator_type, typename T::allocator_type>,
-              std::is_same<typename params_type::is_map_container,
-                           typename T::params_type::is_map_container>>::value,
-          int> = 0>
-  void merge(btree_container<T> &&src) {
-    merge(src);
-  }
-};
-
-// Base class for btree_map.
-template <typename Tree>
-class btree_map_container : public btree_set_container<Tree> {
-  using super_type = btree_set_container<Tree>;
-  using params_type = typename Tree::params_type;
-  friend class BtreeNodePeer;
-
- private:
-  template <class K>
-  using key_arg = typename super_type::template key_arg<K>;
-
- public:
-  using key_type = typename Tree::key_type;
-  using mapped_type = typename params_type::mapped_type;
-  using value_type = typename Tree::value_type;
-  using key_compare = typename Tree::key_compare;
-  using allocator_type = typename Tree::allocator_type;
-  using iterator = typename Tree::iterator;
-  using const_iterator = typename Tree::const_iterator;
-
-  // Inherit constructors.
-  using super_type::super_type;
-  btree_map_container() {}
-
-  // Insertion routines.
-  // Note: the nullptr template arguments and extra `const M&` overloads allow
-  // for supporting bitfield arguments.
-  template <typename K = key_type, class M>
-  std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k,
-                                             const M &obj) {
-    return insert_or_assign_impl(k, obj);
-  }
-  template <typename K = key_type, class M, K * = nullptr>
-  std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, const M &obj) {
-    return insert_or_assign_impl(std::forward<K>(k), obj);
-  }
-  template <typename K = key_type, class M, M * = nullptr>
-  std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k, M &&obj) {
-    return insert_or_assign_impl(k, std::forward<M>(obj));
-  }
-  template <typename K = key_type, class M, K * = nullptr, M * = nullptr>
-  std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, M &&obj) {
-    return insert_or_assign_impl(std::forward<K>(k), std::forward<M>(obj));
-  }
-  template <typename K = key_type, class M>
-  iterator insert_or_assign(const_iterator hint, const key_arg<K> &k,
-                            const M &obj) {
-    return insert_or_assign_hint_impl(hint, k, obj);
-  }
-  template <typename K = key_type, class M, K * = nullptr>
-  iterator insert_or_assign(const_iterator hint, key_arg<K> &&k, const M &obj) {
-    return insert_or_assign_hint_impl(hint, std::forward<K>(k), obj);
-  }
-  template <typename K = key_type, class M, M * = nullptr>
-  iterator insert_or_assign(const_iterator hint, const key_arg<K> &k, M &&obj) {
-    return insert_or_assign_hint_impl(hint, k, std::forward<M>(obj));
-  }
-  template <typename K = key_type, class M, K * = nullptr, M * = nullptr>
-  iterator insert_or_assign(const_iterator hint, key_arg<K> &&k, M &&obj) {
-    return insert_or_assign_hint_impl(hint, std::forward<K>(k),
-                                      std::forward<M>(obj));
-  }
-
-  template <typename K = key_type, typename... Args,
-            typename absl::enable_if_t<
-                !std::is_convertible<K, const_iterator>::value, int> = 0>
-  std::pair<iterator, bool> try_emplace(const key_arg<K> &k, Args &&... args) {
-    return try_emplace_impl(k, std::forward<Args>(args)...);
-  }
-  template <typename K = key_type, typename... Args,
-            typename absl::enable_if_t<
-                !std::is_convertible<K, const_iterator>::value, int> = 0>
-  std::pair<iterator, bool> try_emplace(key_arg<K> &&k, Args &&... args) {
-    return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...);
-  }
-  template <typename K = key_type, typename... Args>
-  iterator try_emplace(const_iterator hint, const key_arg<K> &k,
-                       Args &&... args) {
-    return try_emplace_hint_impl(hint, k, std::forward<Args>(args)...);
-  }
-  template <typename K = key_type, typename... Args>
-  iterator try_emplace(const_iterator hint, key_arg<K> &&k, Args &&... args) {
-    return try_emplace_hint_impl(hint, std::forward<K>(k),
-                                 std::forward<Args>(args)...);
-  }
-
-  template <typename K = key_type>
-  mapped_type &operator[](const key_arg<K> &k) {
-    return try_emplace(k).first->second;
-  }
-  template <typename K = key_type>
-  mapped_type &operator[](key_arg<K> &&k) {
-    return try_emplace(std::forward<K>(k)).first->second;
-  }
-
-  template <typename K = key_type>
-  mapped_type &at(const key_arg<K> &key) {
-    auto it = this->find(key);
-    if (it == this->end())
-      base_internal::ThrowStdOutOfRange("absl::btree_map::at");
-    return it->second;
-  }
-  template <typename K = key_type>
-  const mapped_type &at(const key_arg<K> &key) const {
-    auto it = this->find(key);
-    if (it == this->end())
-      base_internal::ThrowStdOutOfRange("absl::btree_map::at");
-    return it->second;
-  }
-
- private:
-  // Note: when we call `std::forward<M>(obj)` twice, it's safe because
-  // insert_unique/insert_hint_unique are guaranteed to not consume `obj` when
-  // `ret.second` is false.
-  template <class K, class M>
-  std::pair<iterator, bool> insert_or_assign_impl(K &&k, M &&obj) {
-    const std::pair<iterator, bool> ret =
-        this->tree_.insert_unique(k, std::forward<K>(k), std::forward<M>(obj));
-    if (!ret.second) ret.first->second = std::forward<M>(obj);
-    return ret;
-  }
-  template <class K, class M>
-  iterator insert_or_assign_hint_impl(const_iterator hint, K &&k, M &&obj) {
-    const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique(
-        iterator(hint), k, std::forward<K>(k), std::forward<M>(obj));
-    if (!ret.second) ret.first->second = std::forward<M>(obj);
-    return ret.first;
-  }
-
-  template <class K, class... Args>
-  std::pair<iterator, bool> try_emplace_impl(K &&k, Args &&... args) {
-    return this->tree_.insert_unique(
-        k, std::piecewise_construct, std::forward_as_tuple(std::forward<K>(k)),
-        std::forward_as_tuple(std::forward<Args>(args)...));
-  }
-  template <class K, class... Args>
-  iterator try_emplace_hint_impl(const_iterator hint, K &&k, Args &&... args) {
-    return this->tree_
-        .insert_hint_unique(iterator(hint), k, std::piecewise_construct,
-                            std::forward_as_tuple(std::forward<K>(k)),
-                            std::forward_as_tuple(std::forward<Args>(args)...))
-        .first;
-  }
-};
-
-// A common base class for btree_multiset and btree_multimap.
-template <typename Tree>
-class btree_multiset_container : public btree_container<Tree> {
-  using super_type = btree_container<Tree>;
-  using params_type = typename Tree::params_type;
-  using init_type = typename params_type::init_type;
-  using is_key_compare_to = typename params_type::is_key_compare_to;
-
-  template <class K>
-  using key_arg = typename super_type::template key_arg<K>;
-
- public:
-  using key_type = typename Tree::key_type;
-  using value_type = typename Tree::value_type;
-  using size_type = typename Tree::size_type;
-  using key_compare = typename Tree::key_compare;
-  using allocator_type = typename Tree::allocator_type;
-  using iterator = typename Tree::iterator;
-  using const_iterator = typename Tree::const_iterator;
-  using node_type = typename super_type::node_type;
-
-  // Inherit constructors.
-  using super_type::super_type;
-  btree_multiset_container() {}
-
-  // Range constructors.
-  template <class InputIterator>
-  btree_multiset_container(InputIterator b, InputIterator e,
-                           const key_compare &comp = key_compare(),
-                           const allocator_type &alloc = allocator_type())
-      : super_type(comp, alloc) {
-    insert(b, e);
-  }
-  template <class InputIterator>
-  btree_multiset_container(InputIterator b, InputIterator e,
-                           const allocator_type &alloc)
-      : btree_multiset_container(b, e, key_compare(), alloc) {}
-
-  // Initializer list constructors.
-  btree_multiset_container(std::initializer_list<init_type> init,
-                           const key_compare &comp = key_compare(),
-                           const allocator_type &alloc = allocator_type())
-      : btree_multiset_container(init.begin(), init.end(), comp, alloc) {}
-  btree_multiset_container(std::initializer_list<init_type> init,
-                           const allocator_type &alloc)
-      : btree_multiset_container(init.begin(), init.end(), alloc) {}
-
-  // Insertion routines.
-  iterator insert(const value_type &v) { return this->tree_.insert_multi(v); }
-  iterator insert(value_type &&v) {
-    return this->tree_.insert_multi(std::move(v));
-  }
-  iterator insert(const_iterator hint, const value_type &v) {
-    return this->tree_.insert_hint_multi(iterator(hint), v);
-  }
-  iterator insert(const_iterator hint, value_type &&v) {
-    return this->tree_.insert_hint_multi(iterator(hint), std::move(v));
-  }
-  template <typename InputIterator>
-  void insert(InputIterator b, InputIterator e) {
-    this->tree_.insert_iterator_multi(b, e);
-  }
-  void insert(std::initializer_list<init_type> init) {
-    this->tree_.insert_iterator_multi(init.begin(), init.end());
-  }
-  template <typename... Args>
-  iterator emplace(Args &&... args) {
-    return this->tree_.insert_multi(init_type(std::forward<Args>(args)...));
-  }
-  template <typename... Args>
-  iterator emplace_hint(const_iterator hint, Args &&... args) {
-    return this->tree_.insert_hint_multi(
-        iterator(hint), init_type(std::forward<Args>(args)...));
-  }
-  iterator insert(node_type &&node) {
-    if (!node) return this->end();
-    iterator res =
-        this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)),
-                                 CommonAccess::GetSlot(node));
-    CommonAccess::Destroy(&node);
-    return res;
-  }
-  iterator insert(const_iterator hint, node_type &&node) {
-    if (!node) return this->end();
-    iterator res = this->tree_.insert_hint_multi(
-        iterator(hint),
-        std::move(params_type::element(CommonAccess::GetSlot(node))));
-    CommonAccess::Destroy(&node);
-    return res;
-  }
-
-  // Node extraction routines.
-  // TODO(ezb): we are supposed to extract the first equivalent key if there are
-  // multiple, but this isn't guaranteed to extract the first one.
-  template <typename K = key_type>
-  node_type extract(const key_arg<K> &key) {
-    auto it = this->find(key);
-    return it == this->end() ? node_type() : extract(it);
-  }
-  using super_type::extract;
-
-  // Merge routines.
-  // Moves all elements from `src` into `this`.
-  template <
-      typename T,
-      typename absl::enable_if_t<
-          absl::conjunction<
-              std::is_same<value_type, typename T::value_type>,
-              std::is_same<allocator_type, typename T::allocator_type>,
-              std::is_same<typename params_type::is_map_container,
-                           typename T::params_type::is_map_container>>::value,
-          int> = 0>
-  void merge(btree_container<T> &src) {  // NOLINT
-    for (auto src_it = src.begin(), end = src.end(); src_it != end; ++src_it) {
-      insert(std::move(params_type::element(src_it.slot())));
-    }
-    src.clear();
-  }
-
-  template <
-      typename T,
-      typename absl::enable_if_t<
-          absl::conjunction<
-              std::is_same<value_type, typename T::value_type>,
-              std::is_same<allocator_type, typename T::allocator_type>,
-              std::is_same<typename params_type::is_map_container,
-                           typename T::params_type::is_map_container>>::value,
-          int> = 0>
-  void merge(btree_container<T> &&src) {
-    merge(src);
-  }
-};
-
-// A base class for btree_multimap.
-template <typename Tree>
-class btree_multimap_container : public btree_multiset_container<Tree> {
-  using super_type = btree_multiset_container<Tree>;
-  using params_type = typename Tree::params_type;
-
- public:
-  using mapped_type = typename params_type::mapped_type;
-
-  // Inherit constructors.
-  using super_type::super_type;
-  btree_multimap_container() {}
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/common.h b/third_party/abseil_cpp/absl/container/internal/common.h
deleted file mode 100644
index 030e9d4ab0..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/common.h
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_H_
-#define ABSL_CONTAINER_INTERNAL_CONTAINER_H_
-
-#include <cassert>
-#include <type_traits>
-
-#include "absl/meta/type_traits.h"
-#include "absl/types/optional.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class, class = void>
-struct IsTransparent : std::false_type {};
-template <class T>
-struct IsTransparent<T, absl::void_t<typename T::is_transparent>>
-    : std::true_type {};
-
-template <bool is_transparent>
-struct KeyArg {
-  // Transparent. Forward `K`.
-  template <typename K, typename key_type>
-  using type = K;
-};
-
-template <>
-struct KeyArg<false> {
-  // Not transparent. Always use `key_type`.
-  template <typename K, typename key_type>
-  using type = key_type;
-};
-
-// The node_handle concept from C++17.
-// We specialize node_handle for sets and maps. node_handle_base holds the
-// common API of both.
-template <typename PolicyTraits, typename Alloc>
-class node_handle_base {
- protected:
-  using slot_type = typename PolicyTraits::slot_type;
-
- public:
-  using allocator_type = Alloc;
-
-  constexpr node_handle_base() = default;
-  node_handle_base(node_handle_base&& other) noexcept {
-    *this = std::move(other);
-  }
-  ~node_handle_base() { destroy(); }
-  node_handle_base& operator=(node_handle_base&& other) noexcept {
-    destroy();
-    if (!other.empty()) {
-      alloc_ = other.alloc_;
-      PolicyTraits::transfer(alloc(), slot(), other.slot());
-      other.reset();
-    }
-    return *this;
-  }
-
-  bool empty() const noexcept { return !alloc_; }
-  explicit operator bool() const noexcept { return !empty(); }
-  allocator_type get_allocator() const { return *alloc_; }
-
- protected:
-  friend struct CommonAccess;
-
-  struct transfer_tag_t {};
-  node_handle_base(transfer_tag_t, const allocator_type& a, slot_type* s)
-      : alloc_(a) {
-    PolicyTraits::transfer(alloc(), slot(), s);
-  }
-
-  struct move_tag_t {};
-  node_handle_base(move_tag_t, const allocator_type& a, slot_type* s)
-      : alloc_(a) {
-    PolicyTraits::construct(alloc(), slot(), s);
-  }
-
-  void destroy() {
-    if (!empty()) {
-      PolicyTraits::destroy(alloc(), slot());
-      reset();
-    }
-  }
-
-  void reset() {
-    assert(alloc_.has_value());
-    alloc_ = absl::nullopt;
-  }
-
-  slot_type* slot() const {
-    assert(!empty());
-    return reinterpret_cast<slot_type*>(std::addressof(slot_space_));
-  }
-  allocator_type* alloc() { return std::addressof(*alloc_); }
-
- private:
-  absl::optional<allocator_type> alloc_ = {};
-  alignas(slot_type) mutable unsigned char slot_space_[sizeof(slot_type)] = {};
-};
-
-// For sets.
-template <typename Policy, typename PolicyTraits, typename Alloc,
-          typename = void>
-class node_handle : public node_handle_base<PolicyTraits, Alloc> {
-  using Base = node_handle_base<PolicyTraits, Alloc>;
-
- public:
-  using value_type = typename PolicyTraits::value_type;
-
-  constexpr node_handle() {}
-
-  value_type& value() const { return PolicyTraits::element(this->slot()); }
-
- private:
-  friend struct CommonAccess;
-
-  using Base::Base;
-};
-
-// For maps.
-template <typename Policy, typename PolicyTraits, typename Alloc>
-class node_handle<Policy, PolicyTraits, Alloc,
-                  absl::void_t<typename Policy::mapped_type>>
-    : public node_handle_base<PolicyTraits, Alloc> {
-  using Base = node_handle_base<PolicyTraits, Alloc>;
-  using slot_type = typename PolicyTraits::slot_type;
-
- public:
-  using key_type = typename Policy::key_type;
-  using mapped_type = typename Policy::mapped_type;
-
-  constexpr node_handle() {}
-
-  // When C++17 is available, we can use std::launder to provide mutable
-  // access to the key. Otherwise, we provide const access.
-  auto key() const
-      -> decltype(PolicyTraits::mutable_key(std::declval<slot_type*>())) {
-    return PolicyTraits::mutable_key(this->slot());
-  }
-
-  mapped_type& mapped() const {
-    return PolicyTraits::value(&PolicyTraits::element(this->slot()));
-  }
-
- private:
-  friend struct CommonAccess;
-
-  using Base::Base;
-};
-
-// Provide access to non-public node-handle functions.
-struct CommonAccess {
-  template <typename Node>
-  static auto GetSlot(const Node& node) -> decltype(node.slot()) {
-    return node.slot();
-  }
-
-  template <typename Node>
-  static void Destroy(Node* node) {
-    node->destroy();
-  }
-
-  template <typename Node>
-  static void Reset(Node* node) {
-    node->reset();
-  }
-
-  template <typename T, typename... Args>
-  static T Transfer(Args&&... args) {
-    return T(typename T::transfer_tag_t{}, std::forward<Args>(args)...);
-  }
-
-  template <typename T, typename... Args>
-  static T Move(Args&&... args) {
-    return T(typename T::move_tag_t{}, std::forward<Args>(args)...);
-  }
-};
-
-// Implement the insert_return_type<> concept of C++17.
-template <class Iterator, class NodeType>
-struct InsertReturnType {
-  Iterator position;
-  bool inserted;
-  NodeType node;
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_CONTAINER_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/compressed_tuple.h b/third_party/abseil_cpp/absl/container/internal/compressed_tuple.h
deleted file mode 100644
index 5ebe164942..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/compressed_tuple.h
+++ /dev/null
@@ -1,290 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Helper class to perform the Empty Base Optimization.
-// Ts can contain classes and non-classes, empty or not. For the ones that
-// are empty classes, we perform the optimization. If all types in Ts are empty
-// classes, then CompressedTuple<Ts...> is itself an empty class.
-//
-// To access the members, use member get<N>() function.
-//
-// Eg:
-//   absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2,
-//                                                                    t3);
-//   assert(value.get<0>() == 7);
-//   T1& t1 = value.get<1>();
-//   const T2& t2 = value.get<2>();
-//   ...
-//
-// https://en.cppreference.com/w/cpp/language/ebo
-
-#ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
-#define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
-
-#include <initializer_list>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "absl/utility/utility.h"
-
-#if defined(_MSC_VER) && !defined(__NVCC__)
-// We need to mark these classes with this declspec to ensure that
-// CompressedTuple happens.
-#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases)
-#else
-#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <typename... Ts>
-class CompressedTuple;
-
-namespace internal_compressed_tuple {
-
-template <typename D, size_t I>
-struct Elem;
-template <typename... B, size_t I>
-struct Elem<CompressedTuple<B...>, I>
-    : std::tuple_element<I, std::tuple<B...>> {};
-template <typename D, size_t I>
-using ElemT = typename Elem<D, I>::type;
-
-// Use the __is_final intrinsic if available. Where it's not available, classes
-// declared with the 'final' specifier cannot be used as CompressedTuple
-// elements.
-// TODO(sbenza): Replace this with std::is_final in C++14.
-template <typename T>
-constexpr bool IsFinal() {
-#if defined(__clang__) || defined(__GNUC__)
-  return __is_final(T);
-#else
-  return false;
-#endif
-}
-
-// We can't use EBCO on other CompressedTuples because that would mean that we
-// derive from multiple Storage<> instantiations with the same I parameter,
-// and potentially from multiple identical Storage<> instantiations.  So anytime
-// we use type inheritance rather than encapsulation, we mark
-// CompressedTupleImpl, to make this easy to detect.
-struct uses_inheritance {};
-
-template <typename T>
-constexpr bool ShouldUseBase() {
-  return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>() &&
-         !std::is_base_of<uses_inheritance, T>::value;
-}
-
-// The storage class provides two specializations:
-//  - For empty classes, it stores T as a base class.
-//  - For everything else, it stores T as a member.
-template <typename T, size_t I,
-#if defined(_MSC_VER)
-          bool UseBase =
-              ShouldUseBase<typename std::enable_if<true, T>::type>()>
-#else
-          bool UseBase = ShouldUseBase<T>()>
-#endif
-struct Storage {
-  T value;
-  constexpr Storage() = default;
-  template <typename V>
-  explicit constexpr Storage(absl::in_place_t, V&& v)
-      : value(absl::forward<V>(v)) {}
-  constexpr const T& get() const& { return value; }
-  T& get() & { return value; }
-  constexpr const T&& get() const&& { return absl::move(*this).value; }
-  T&& get() && { return std::move(*this).value; }
-};
-
-template <typename T, size_t I>
-struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<T, I, true> : T {
-  constexpr Storage() = default;
-
-  template <typename V>
-  explicit constexpr Storage(absl::in_place_t, V&& v)
-      : T(absl::forward<V>(v)) {}
-
-  constexpr const T& get() const& { return *this; }
-  T& get() & { return *this; }
-  constexpr const T&& get() const&& { return absl::move(*this); }
-  T&& get() && { return std::move(*this); }
-};
-
-template <typename D, typename I, bool ShouldAnyUseBase>
-struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl;
-
-template <typename... Ts, size_t... I, bool ShouldAnyUseBase>
-struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
-    CompressedTuple<Ts...>, absl::index_sequence<I...>, ShouldAnyUseBase>
-    // We use the dummy identity function through std::integral_constant to
-    // convince MSVC of accepting and expanding I in that context. Without it
-    // you would get:
-    //   error C3548: 'I': parameter pack cannot be used in this context
-    : uses_inheritance,
-      Storage<Ts, std::integral_constant<size_t, I>::value>... {
-  constexpr CompressedTupleImpl() = default;
-  template <typename... Vs>
-  explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args)
-      : Storage<Ts, I>(absl::in_place, absl::forward<Vs>(args))... {}
-  friend CompressedTuple<Ts...>;
-};
-
-template <typename... Ts, size_t... I>
-struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
-    CompressedTuple<Ts...>, absl::index_sequence<I...>, false>
-    // We use the dummy identity function as above...
-    : Storage<Ts, std::integral_constant<size_t, I>::value, false>... {
-  constexpr CompressedTupleImpl() = default;
-  template <typename... Vs>
-  explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args)
-      : Storage<Ts, I, false>(absl::in_place, absl::forward<Vs>(args))... {}
-  friend CompressedTuple<Ts...>;
-};
-
-std::false_type Or(std::initializer_list<std::false_type>);
-std::true_type Or(std::initializer_list<bool>);
-
-// MSVC requires this to be done separately rather than within the declaration
-// of CompressedTuple below.
-template <typename... Ts>
-constexpr bool ShouldAnyUseBase() {
-  return decltype(
-      Or({std::integral_constant<bool, ShouldUseBase<Ts>()>()...})){};
-}
-
-template <typename T, typename V>
-using TupleElementMoveConstructible =
-    typename std::conditional<std::is_reference<T>::value,
-                              std::is_convertible<V, T>,
-                              std::is_constructible<T, V&&>>::type;
-
-template <bool SizeMatches, class T, class... Vs>
-struct TupleMoveConstructible : std::false_type {};
-
-template <class... Ts, class... Vs>
-struct TupleMoveConstructible<true, CompressedTuple<Ts...>, Vs...>
-    : std::integral_constant<
-          bool, absl::conjunction<
-                    TupleElementMoveConstructible<Ts, Vs&&>...>::value> {};
-
-template <typename T>
-struct compressed_tuple_size;
-
-template <typename... Es>
-struct compressed_tuple_size<CompressedTuple<Es...>>
-    : public std::integral_constant<std::size_t, sizeof...(Es)> {};
-
-template <class T, class... Vs>
-struct TupleItemsMoveConstructible
-    : std::integral_constant<
-          bool, TupleMoveConstructible<compressed_tuple_size<T>::value ==
-                                           sizeof...(Vs),
-                                       T, Vs...>::value> {};
-
-}  // namespace internal_compressed_tuple
-
-// Helper class to perform the Empty Base Class Optimization.
-// Ts can contain classes and non-classes, empty or not. For the ones that
-// are empty classes, we perform the CompressedTuple. If all types in Ts are
-// empty classes, then CompressedTuple<Ts...> is itself an empty class.  (This
-// does not apply when one or more of those empty classes is itself an empty
-// CompressedTuple.)
-//
-// To access the members, use member .get<N>() function.
-//
-// Eg:
-//   absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2,
-//                                                                    t3);
-//   assert(value.get<0>() == 7);
-//   T1& t1 = value.get<1>();
-//   const T2& t2 = value.get<2>();
-//   ...
-//
-// https://en.cppreference.com/w/cpp/language/ebo
-template <typename... Ts>
-class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple
-    : private internal_compressed_tuple::CompressedTupleImpl<
-          CompressedTuple<Ts...>, absl::index_sequence_for<Ts...>,
-          internal_compressed_tuple::ShouldAnyUseBase<Ts...>()> {
- private:
-  template <int I>
-  using ElemT = internal_compressed_tuple::ElemT<CompressedTuple, I>;
-
-  template <int I>
-  using StorageT = internal_compressed_tuple::Storage<ElemT<I>, I>;
-
- public:
-  // There seems to be a bug in MSVC dealing in which using '=default' here will
-  // cause the compiler to ignore the body of other constructors. The work-
-  // around is to explicitly implement the default constructor.
-#if defined(_MSC_VER)
-  constexpr CompressedTuple() : CompressedTuple::CompressedTupleImpl() {}
-#else
-  constexpr CompressedTuple() = default;
-#endif
-  explicit constexpr CompressedTuple(const Ts&... base)
-      : CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {}
-
-  template <typename First, typename... Vs,
-            absl::enable_if_t<
-                absl::conjunction<
-                    // Ensure we are not hiding default copy/move constructors.
-                    absl::negation<std::is_same<void(CompressedTuple),
-                                                void(absl::decay_t<First>)>>,
-                    internal_compressed_tuple::TupleItemsMoveConstructible<
-                        CompressedTuple<Ts...>, First, Vs...>>::value,
-                bool> = true>
-  explicit constexpr CompressedTuple(First&& first, Vs&&... base)
-      : CompressedTuple::CompressedTupleImpl(absl::in_place,
-                                             absl::forward<First>(first),
-                                             absl::forward<Vs>(base)...) {}
-
-  template <int I>
-  ElemT<I>& get() & {
-    return StorageT<I>::get();
-  }
-
-  template <int I>
-  constexpr const ElemT<I>& get() const& {
-    return StorageT<I>::get();
-  }
-
-  template <int I>
-  ElemT<I>&& get() && {
-    return std::move(*this).StorageT<I>::get();
-  }
-
-  template <int I>
-  constexpr const ElemT<I>&& get() const&& {
-    return absl::move(*this).StorageT<I>::get();
-  }
-};
-
-// Explicit specialization for a zero-element tuple
-// (needed to avoid ambiguous overloads for the default constructor).
-template <>
-class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
-
-#endif  // ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/compressed_tuple_test.cc b/third_party/abseil_cpp/absl/container/internal/compressed_tuple_test.cc
deleted file mode 100644
index 62a7483ee3..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/compressed_tuple_test.cc
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/compressed_tuple.h"
-
-#include <memory>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/test_instance_tracker.h"
-#include "absl/memory/memory.h"
-#include "absl/types/any.h"
-#include "absl/types/optional.h"
-#include "absl/utility/utility.h"
-
-// These are declared at global scope purely so that error messages
-// are smaller and easier to understand.
-enum class CallType { kConstRef, kConstMove };
-
-template <int>
-struct Empty {
-  constexpr CallType value() const& { return CallType::kConstRef; }
-  constexpr CallType value() const&& { return CallType::kConstMove; }
-};
-
-template <typename T>
-struct NotEmpty {
-  T value;
-};
-
-template <typename T, typename U>
-struct TwoValues {
-  T value1;
-  U value2;
-};
-
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using absl::test_internal::CopyableMovableInstance;
-using absl::test_internal::InstanceTracker;
-
-TEST(CompressedTupleTest, Sizeof) {
-  EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>));
-  EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>));
-  EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>, Empty<1>>));
-  EXPECT_EQ(sizeof(int),
-            sizeof(CompressedTuple<int, Empty<0>, Empty<1>, Empty<2>>));
-
-  EXPECT_EQ(sizeof(TwoValues<int, double>),
-            sizeof(CompressedTuple<int, NotEmpty<double>>));
-  EXPECT_EQ(sizeof(TwoValues<int, double>),
-            sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>>));
-  EXPECT_EQ(sizeof(TwoValues<int, double>),
-            sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>, Empty<1>>));
-}
-
-TEST(CompressedTupleTest, OneMoveOnRValueConstructionTemp) {
-  InstanceTracker tracker;
-  CompressedTuple<CopyableMovableInstance> x1(CopyableMovableInstance(1));
-  EXPECT_EQ(tracker.instances(), 1);
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_LE(tracker.moves(), 1);
-  EXPECT_EQ(x1.get<0>().value(), 1);
-}
-
-TEST(CompressedTupleTest, OneMoveOnRValueConstructionMove) {
-  InstanceTracker tracker;
-
-  CopyableMovableInstance i1(1);
-  CompressedTuple<CopyableMovableInstance> x1(std::move(i1));
-  EXPECT_EQ(tracker.instances(), 2);
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_LE(tracker.moves(), 1);
-  EXPECT_EQ(x1.get<0>().value(), 1);
-}
-
-TEST(CompressedTupleTest, OneMoveOnRValueConstructionMixedTypes) {
-  InstanceTracker tracker;
-  CopyableMovableInstance i1(1);
-  CopyableMovableInstance i2(2);
-  Empty<0> empty;
-  CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
-      x1(std::move(i1), i2, empty);
-  EXPECT_EQ(x1.get<0>().value(), 1);
-  EXPECT_EQ(x1.get<1>().value(), 2);
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.moves(), 1);
-}
-
-struct IncompleteType;
-CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>
-MakeWithIncomplete(CopyableMovableInstance i1,
-                   IncompleteType& t,  // NOLINT
-                   Empty<0> empty) {
-  return CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>{
-      std::move(i1), t, empty};
-}
-
-struct IncompleteType {};
-TEST(CompressedTupleTest, OneMoveOnRValueConstructionWithIncompleteType) {
-  InstanceTracker tracker;
-  CopyableMovableInstance i1(1);
-  Empty<0> empty;
-  struct DerivedType : IncompleteType {int value = 0;};
-  DerivedType fd;
-  fd.value = 7;
-
-  CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>> x1 =
-      MakeWithIncomplete(std::move(i1), fd, empty);
-
-  EXPECT_EQ(x1.get<0>().value(), 1);
-  EXPECT_EQ(static_cast<DerivedType&>(x1.get<1>()).value, 7);
-
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.moves(), 2);
-}
-
-TEST(CompressedTupleTest,
-     OneMoveOnRValueConstructionMixedTypes_BraceInitPoisonPillExpected) {
-  InstanceTracker tracker;
-  CopyableMovableInstance i1(1);
-  CopyableMovableInstance i2(2);
-  CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
-      x1(std::move(i1), i2, {});  // NOLINT
-  EXPECT_EQ(x1.get<0>().value(), 1);
-  EXPECT_EQ(x1.get<1>().value(), 2);
-  EXPECT_EQ(tracker.instances(), 3);
-  // We are forced into the `const Ts&...` constructor (invoking copies)
-  // because we need it to deduce the type of `{}`.
-  // std::tuple also has this behavior.
-  // Note, this test is proof that this is expected behavior, but it is not
-  // _desired_ behavior.
-  EXPECT_EQ(tracker.copies(), 1);
-  EXPECT_EQ(tracker.moves(), 0);
-}
-
-TEST(CompressedTupleTest, OneCopyOnLValueConstruction) {
-  InstanceTracker tracker;
-  CopyableMovableInstance i1(1);
-
-  CompressedTuple<CopyableMovableInstance> x1(i1);
-  EXPECT_EQ(tracker.copies(), 1);
-  EXPECT_EQ(tracker.moves(), 0);
-
-  tracker.ResetCopiesMovesSwaps();
-
-  CopyableMovableInstance i2(2);
-  const CopyableMovableInstance& i2_ref = i2;
-  CompressedTuple<CopyableMovableInstance> x2(i2_ref);
-  EXPECT_EQ(tracker.copies(), 1);
-  EXPECT_EQ(tracker.moves(), 0);
-}
-
-TEST(CompressedTupleTest, OneMoveOnRValueAccess) {
-  InstanceTracker tracker;
-  CopyableMovableInstance i1(1);
-  CompressedTuple<CopyableMovableInstance> x(std::move(i1));
-  tracker.ResetCopiesMovesSwaps();
-
-  CopyableMovableInstance i2 = std::move(x).get<0>();
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.moves(), 1);
-}
-
-TEST(CompressedTupleTest, OneCopyOnLValueAccess) {
-  InstanceTracker tracker;
-
-  CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.moves(), 1);
-
-  CopyableMovableInstance t = x.get<0>();
-  EXPECT_EQ(tracker.copies(), 1);
-  EXPECT_EQ(tracker.moves(), 1);
-}
-
-TEST(CompressedTupleTest, ZeroCopyOnRefAccess) {
-  InstanceTracker tracker;
-
-  CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.moves(), 1);
-
-  CopyableMovableInstance& t1 = x.get<0>();
-  const CopyableMovableInstance& t2 = x.get<0>();
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.moves(), 1);
-  EXPECT_EQ(t1.value(), 0);
-  EXPECT_EQ(t2.value(), 0);
-}
-
-TEST(CompressedTupleTest, Access) {
-  struct S {
-    std::string x;
-  };
-  CompressedTuple<int, Empty<0>, S> x(7, {}, S{"ABC"});
-  EXPECT_EQ(sizeof(x), sizeof(TwoValues<int, S>));
-  EXPECT_EQ(7, x.get<0>());
-  EXPECT_EQ("ABC", x.get<2>().x);
-}
-
-TEST(CompressedTupleTest, NonClasses) {
-  CompressedTuple<int, const char*> x(7, "ABC");
-  EXPECT_EQ(7, x.get<0>());
-  EXPECT_STREQ("ABC", x.get<1>());
-}
-
-TEST(CompressedTupleTest, MixClassAndNonClass) {
-  CompressedTuple<int, const char*, Empty<0>, NotEmpty<double>> x(7, "ABC", {},
-                                                                  {1.25});
-  struct Mock {
-    int v;
-    const char* p;
-    double d;
-  };
-  EXPECT_EQ(sizeof(x), sizeof(Mock));
-  EXPECT_EQ(7, x.get<0>());
-  EXPECT_STREQ("ABC", x.get<1>());
-  EXPECT_EQ(1.25, x.get<3>().value);
-}
-
-TEST(CompressedTupleTest, Nested) {
-  CompressedTuple<int, CompressedTuple<int>,
-                  CompressedTuple<int, CompressedTuple<int>>>
-      x(1, CompressedTuple<int>(2),
-        CompressedTuple<int, CompressedTuple<int>>(3, CompressedTuple<int>(4)));
-  EXPECT_EQ(1, x.get<0>());
-  EXPECT_EQ(2, x.get<1>().get<0>());
-  EXPECT_EQ(3, x.get<2>().get<0>());
-  EXPECT_EQ(4, x.get<2>().get<1>().get<0>());
-
-  CompressedTuple<Empty<0>, Empty<0>,
-                  CompressedTuple<Empty<0>, CompressedTuple<Empty<0>>>>
-      y;
-  std::set<Empty<0>*> empties{&y.get<0>(), &y.get<1>(), &y.get<2>().get<0>(),
-                              &y.get<2>().get<1>().get<0>()};
-#ifdef _MSC_VER
-  // MSVC has a bug where many instances of the same base class are layed out in
-  // the same address when using __declspec(empty_bases).
-  // This will be fixed in a future version of MSVC.
-  int expected = 1;
-#else
-  int expected = 4;
-#endif
-  EXPECT_EQ(expected, sizeof(y));
-  EXPECT_EQ(expected, empties.size());
-  EXPECT_EQ(sizeof(y), sizeof(Empty<0>) * empties.size());
-
-  EXPECT_EQ(4 * sizeof(char),
-            sizeof(CompressedTuple<CompressedTuple<char, char>,
-                                   CompressedTuple<char, char>>));
-  EXPECT_TRUE((std::is_empty<CompressedTuple<Empty<0>, Empty<1>>>::value));
-
-  // Make sure everything still works when things are nested.
-  struct CT_Empty : CompressedTuple<Empty<0>> {};
-  CompressedTuple<Empty<0>, CT_Empty> nested_empty;
-  auto contained = nested_empty.get<0>();
-  auto nested = nested_empty.get<1>().get<0>();
-  EXPECT_TRUE((std::is_same<decltype(contained), decltype(nested)>::value));
-}
-
-TEST(CompressedTupleTest, Reference) {
-  int i = 7;
-  std::string s = "Very long string that goes in the heap";
-  CompressedTuple<int, int&, std::string, std::string&> x(i, i, s, s);
-
-  // Sanity check. We should have not moved from `s`
-  EXPECT_EQ(s, "Very long string that goes in the heap");
-
-  EXPECT_EQ(x.get<0>(), x.get<1>());
-  EXPECT_NE(&x.get<0>(), &x.get<1>());
-  EXPECT_EQ(&x.get<1>(), &i);
-
-  EXPECT_EQ(x.get<2>(), x.get<3>());
-  EXPECT_NE(&x.get<2>(), &x.get<3>());
-  EXPECT_EQ(&x.get<3>(), &s);
-}
-
-TEST(CompressedTupleTest, NoElements) {
-  CompressedTuple<> x;
-  static_cast<void>(x);  // Silence -Wunused-variable.
-  EXPECT_TRUE(std::is_empty<CompressedTuple<>>::value);
-}
-
-TEST(CompressedTupleTest, MoveOnlyElements) {
-  CompressedTuple<std::unique_ptr<std::string>> str_tup(
-      absl::make_unique<std::string>("str"));
-
-  CompressedTuple<CompressedTuple<std::unique_ptr<std::string>>,
-                  std::unique_ptr<int>>
-  x(std::move(str_tup), absl::make_unique<int>(5));
-
-  EXPECT_EQ(*x.get<0>().get<0>(), "str");
-  EXPECT_EQ(*x.get<1>(), 5);
-
-  std::unique_ptr<std::string> x0 = std::move(x.get<0>()).get<0>();
-  std::unique_ptr<int> x1 = std::move(x).get<1>();
-
-  EXPECT_EQ(*x0, "str");
-  EXPECT_EQ(*x1, 5);
-}
-
-TEST(CompressedTupleTest, MoveConstructionMoveOnlyElements) {
-  CompressedTuple<std::unique_ptr<std::string>> base(
-      absl::make_unique<std::string>("str"));
-  EXPECT_EQ(*base.get<0>(), "str");
-
-  CompressedTuple<std::unique_ptr<std::string>> copy(std::move(base));
-  EXPECT_EQ(*copy.get<0>(), "str");
-}
-
-TEST(CompressedTupleTest, AnyElements) {
-  any a(std::string("str"));
-  CompressedTuple<any, any&> x(any(5), a);
-  EXPECT_EQ(absl::any_cast<int>(x.get<0>()), 5);
-  EXPECT_EQ(absl::any_cast<std::string>(x.get<1>()), "str");
-
-  a = 0.5f;
-  EXPECT_EQ(absl::any_cast<float>(x.get<1>()), 0.5);
-}
-
-TEST(CompressedTupleTest, Constexpr) {
-  struct NonTrivialStruct {
-    constexpr NonTrivialStruct() = default;
-    constexpr int value() const { return v; }
-    int v = 5;
-  };
-  struct TrivialStruct {
-    TrivialStruct() = default;
-    constexpr int value() const { return v; }
-    int v;
-  };
-  constexpr CompressedTuple<int, double, CompressedTuple<int>, Empty<0>> x(
-      7, 1.25, CompressedTuple<int>(5), {});
-  constexpr int x0 = x.get<0>();
-  constexpr double x1 = x.get<1>();
-  constexpr int x2 = x.get<2>().get<0>();
-  constexpr CallType x3 = x.get<3>().value();
-
-  EXPECT_EQ(x0, 7);
-  EXPECT_EQ(x1, 1.25);
-  EXPECT_EQ(x2, 5);
-  EXPECT_EQ(x3, CallType::kConstRef);
-
-#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
-  constexpr CompressedTuple<Empty<0>, TrivialStruct, int> trivial = {};
-  constexpr CallType trivial0 = trivial.get<0>().value();
-  constexpr int trivial1 = trivial.get<1>().value();
-  constexpr int trivial2 = trivial.get<2>();
-
-  EXPECT_EQ(trivial0, CallType::kConstRef);
-  EXPECT_EQ(trivial1, 0);
-  EXPECT_EQ(trivial2, 0);
-#endif
-
-  constexpr CompressedTuple<Empty<0>, NonTrivialStruct, absl::optional<int>>
-      non_trivial = {};
-  constexpr CallType non_trivial0 = non_trivial.get<0>().value();
-  constexpr int non_trivial1 = non_trivial.get<1>().value();
-  constexpr absl::optional<int> non_trivial2 = non_trivial.get<2>();
-
-  EXPECT_EQ(non_trivial0, CallType::kConstRef);
-  EXPECT_EQ(non_trivial1, 5);
-  EXPECT_EQ(non_trivial2, absl::nullopt);
-
-  static constexpr char data[] = "DEF";
-  constexpr CompressedTuple<const char*> z(data);
-  constexpr const char* z1 = z.get<0>();
-  EXPECT_EQ(std::string(z1), std::string(data));
-
-#if defined(__clang__)
-  // An apparent bug in earlier versions of gcc claims these are ambiguous.
-  constexpr int x2m = absl::move(x.get<2>()).get<0>();
-  constexpr CallType x3m = absl::move(x).get<3>().value();
-  EXPECT_EQ(x2m, 5);
-  EXPECT_EQ(x3m, CallType::kConstMove);
-#endif
-}
-
-#if defined(__clang__) || defined(__GNUC__)
-TEST(CompressedTupleTest, EmptyFinalClass) {
-  struct S final {
-    int f() const { return 5; }
-  };
-  CompressedTuple<S> x;
-  EXPECT_EQ(x.get<0>().f(), 5);
-}
-#endif
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/container_memory.h b/third_party/abseil_cpp/absl/container/internal/container_memory.h
deleted file mode 100644
index e67529ecb6..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/container_memory.h
+++ /dev/null
@@ -1,460 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
-#define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
-
-#include <cassert>
-#include <cstddef>
-#include <memory>
-#include <new>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/config.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-#include "absl/utility/utility.h"
-
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-#include <sanitizer/asan_interface.h>
-#endif
-
-#ifdef ABSL_HAVE_MEMORY_SANITIZER
-#include <sanitizer/msan_interface.h>
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <size_t Alignment>
-struct alignas(Alignment) AlignedType {};
-
-// Allocates at least n bytes aligned to the specified alignment.
-// Alignment must be a power of 2. It must be positive.
-//
-// Note that many allocators don't honor alignment requirements above certain
-// threshold (usually either alignof(std::max_align_t) or alignof(void*)).
-// Allocate() doesn't apply alignment corrections. If the underlying allocator
-// returns insufficiently alignment pointer, that's what you are going to get.
-template <size_t Alignment, class Alloc>
-void* Allocate(Alloc* alloc, size_t n) {
-  static_assert(Alignment > 0, "");
-  assert(n && "n must be positive");
-  using M = AlignedType<Alignment>;
-  using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>;
-  using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>;
-  // On macOS, "mem_alloc" is a #define with one argument defined in
-  // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it
-  // with the "foo(bar)" syntax.
-  A my_mem_alloc(*alloc);
-  void* p = AT::allocate(my_mem_alloc, (n + sizeof(M) - 1) / sizeof(M));
-  assert(reinterpret_cast<uintptr_t>(p) % Alignment == 0 &&
-         "allocator does not respect alignment");
-  return p;
-}
-
-// The pointer must have been previously obtained by calling
-// Allocate<Alignment>(alloc, n).
-template <size_t Alignment, class Alloc>
-void Deallocate(Alloc* alloc, void* p, size_t n) {
-  static_assert(Alignment > 0, "");
-  assert(n && "n must be positive");
-  using M = AlignedType<Alignment>;
-  using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>;
-  using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>;
-  // On macOS, "mem_alloc" is a #define with one argument defined in
-  // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it
-  // with the "foo(bar)" syntax.
-  A my_mem_alloc(*alloc);
-  AT::deallocate(my_mem_alloc, static_cast<M*>(p),
-                 (n + sizeof(M) - 1) / sizeof(M));
-}
-
-namespace memory_internal {
-
-// Constructs T into uninitialized storage pointed by `ptr` using the args
-// specified in the tuple.
-template <class Alloc, class T, class Tuple, size_t... I>
-void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t,
-                            absl::index_sequence<I...>) {
-  absl::allocator_traits<Alloc>::construct(
-      *alloc, ptr, std::get<I>(std::forward<Tuple>(t))...);
-}
-
-template <class T, class F>
-struct WithConstructedImplF {
-  template <class... Args>
-  decltype(std::declval<F>()(std::declval<T>())) operator()(
-      Args&&... args) const {
-    return std::forward<F>(f)(T(std::forward<Args>(args)...));
-  }
-  F&& f;
-};
-
-template <class T, class Tuple, size_t... Is, class F>
-decltype(std::declval<F>()(std::declval<T>())) WithConstructedImpl(
-    Tuple&& t, absl::index_sequence<Is...>, F&& f) {
-  return WithConstructedImplF<T, F>{std::forward<F>(f)}(
-      std::get<Is>(std::forward<Tuple>(t))...);
-}
-
-template <class T, size_t... Is>
-auto TupleRefImpl(T&& t, absl::index_sequence<Is...>)
-    -> decltype(std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...)) {
-  return std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...);
-}
-
-// Returns a tuple of references to the elements of the input tuple. T must be a
-// tuple.
-template <class T>
-auto TupleRef(T&& t) -> decltype(
-    TupleRefImpl(std::forward<T>(t),
-                 absl::make_index_sequence<
-                     std::tuple_size<typename std::decay<T>::type>::value>())) {
-  return TupleRefImpl(
-      std::forward<T>(t),
-      absl::make_index_sequence<
-          std::tuple_size<typename std::decay<T>::type>::value>());
-}
-
-template <class F, class K, class V>
-decltype(std::declval<F>()(std::declval<const K&>(), std::piecewise_construct,
-                           std::declval<std::tuple<K>>(), std::declval<V>()))
-DecomposePairImpl(F&& f, std::pair<std::tuple<K>, V> p) {
-  const auto& key = std::get<0>(p.first);
-  return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
-                            std::move(p.second));
-}
-
-}  // namespace memory_internal
-
-// Constructs T into uninitialized storage pointed by `ptr` using the args
-// specified in the tuple.
-template <class Alloc, class T, class Tuple>
-void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) {
-  memory_internal::ConstructFromTupleImpl(
-      alloc, ptr, std::forward<Tuple>(t),
-      absl::make_index_sequence<
-          std::tuple_size<typename std::decay<Tuple>::type>::value>());
-}
-
-// Constructs T using the args specified in the tuple and calls F with the
-// constructed value.
-template <class T, class Tuple, class F>
-decltype(std::declval<F>()(std::declval<T>())) WithConstructed(
-    Tuple&& t, F&& f) {
-  return memory_internal::WithConstructedImpl<T>(
-      std::forward<Tuple>(t),
-      absl::make_index_sequence<
-          std::tuple_size<typename std::decay<Tuple>::type>::value>(),
-      std::forward<F>(f));
-}
-
-// Given arguments of an std::pair's consructor, PairArgs() returns a pair of
-// tuples with references to the passed arguments. The tuples contain
-// constructor arguments for the first and the second elements of the pair.
-//
-// The following two snippets are equivalent.
-//
-// 1. std::pair<F, S> p(args...);
-//
-// 2. auto a = PairArgs(args...);
-//    std::pair<F, S> p(std::piecewise_construct,
-//                      std::move(p.first), std::move(p.second));
-inline std::pair<std::tuple<>, std::tuple<>> PairArgs() { return {}; }
-template <class F, class S>
-std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(F&& f, S&& s) {
-  return {std::piecewise_construct, std::forward_as_tuple(std::forward<F>(f)),
-          std::forward_as_tuple(std::forward<S>(s))};
-}
-template <class F, class S>
-std::pair<std::tuple<const F&>, std::tuple<const S&>> PairArgs(
-    const std::pair<F, S>& p) {
-  return PairArgs(p.first, p.second);
-}
-template <class F, class S>
-std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(std::pair<F, S>&& p) {
-  return PairArgs(std::forward<F>(p.first), std::forward<S>(p.second));
-}
-template <class F, class S>
-auto PairArgs(std::piecewise_construct_t, F&& f, S&& s)
-    -> decltype(std::make_pair(memory_internal::TupleRef(std::forward<F>(f)),
-                               memory_internal::TupleRef(std::forward<S>(s)))) {
-  return std::make_pair(memory_internal::TupleRef(std::forward<F>(f)),
-                        memory_internal::TupleRef(std::forward<S>(s)));
-}
-
-// A helper function for implementing apply() in map policies.
-template <class F, class... Args>
-auto DecomposePair(F&& f, Args&&... args)
-    -> decltype(memory_internal::DecomposePairImpl(
-        std::forward<F>(f), PairArgs(std::forward<Args>(args)...))) {
-  return memory_internal::DecomposePairImpl(
-      std::forward<F>(f), PairArgs(std::forward<Args>(args)...));
-}
-
-// A helper function for implementing apply() in set policies.
-template <class F, class Arg>
-decltype(std::declval<F>()(std::declval<const Arg&>(), std::declval<Arg>()))
-DecomposeValue(F&& f, Arg&& arg) {
-  const auto& key = arg;
-  return std::forward<F>(f)(key, std::forward<Arg>(arg));
-}
-
-// Helper functions for asan and msan.
-inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) {
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-  ASAN_POISON_MEMORY_REGION(m, s);
-#endif
-#ifdef ABSL_HAVE_MEMORY_SANITIZER
-  __msan_poison(m, s);
-#endif
-  (void)m;
-  (void)s;
-}
-
-inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) {
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-  ASAN_UNPOISON_MEMORY_REGION(m, s);
-#endif
-#ifdef ABSL_HAVE_MEMORY_SANITIZER
-  __msan_unpoison(m, s);
-#endif
-  (void)m;
-  (void)s;
-}
-
-template <typename T>
-inline void SanitizerPoisonObject(const T* object) {
-  SanitizerPoisonMemoryRegion(object, sizeof(T));
-}
-
-template <typename T>
-inline void SanitizerUnpoisonObject(const T* object) {
-  SanitizerUnpoisonMemoryRegion(object, sizeof(T));
-}
-
-namespace memory_internal {
-
-// If Pair is a standard-layout type, OffsetOf<Pair>::kFirst and
-// OffsetOf<Pair>::kSecond are equivalent to offsetof(Pair, first) and
-// offsetof(Pair, second) respectively. Otherwise they are -1.
-//
-// The purpose of OffsetOf is to avoid calling offsetof() on non-standard-layout
-// type, which is non-portable.
-template <class Pair, class = std::true_type>
-struct OffsetOf {
-  static constexpr size_t kFirst = static_cast<size_t>(-1);
-  static constexpr size_t kSecond = static_cast<size_t>(-1);
-};
-
-template <class Pair>
-struct OffsetOf<Pair, typename std::is_standard_layout<Pair>::type> {
-  static constexpr size_t kFirst = offsetof(Pair, first);
-  static constexpr size_t kSecond = offsetof(Pair, second);
-};
-
-template <class K, class V>
-struct IsLayoutCompatible {
- private:
-  struct Pair {
-    K first;
-    V second;
-  };
-
-  // Is P layout-compatible with Pair?
-  template <class P>
-  static constexpr bool LayoutCompatible() {
-    return std::is_standard_layout<P>() && sizeof(P) == sizeof(Pair) &&
-           alignof(P) == alignof(Pair) &&
-           memory_internal::OffsetOf<P>::kFirst ==
-               memory_internal::OffsetOf<Pair>::kFirst &&
-           memory_internal::OffsetOf<P>::kSecond ==
-               memory_internal::OffsetOf<Pair>::kSecond;
-  }
-
- public:
-  // Whether pair<const K, V> and pair<K, V> are layout-compatible. If they are,
-  // then it is safe to store them in a union and read from either.
-  static constexpr bool value = std::is_standard_layout<K>() &&
-                                std::is_standard_layout<Pair>() &&
-                                memory_internal::OffsetOf<Pair>::kFirst == 0 &&
-                                LayoutCompatible<std::pair<K, V>>() &&
-                                LayoutCompatible<std::pair<const K, V>>();
-};
-
-}  // namespace memory_internal
-
-// The internal storage type for key-value containers like flat_hash_map.
-//
-// It is convenient for the value_type of a flat_hash_map<K, V> to be
-// pair<const K, V>; the "const K" prevents accidental modification of the key
-// when dealing with the reference returned from find() and similar methods.
-// However, this creates other problems; we want to be able to emplace(K, V)
-// efficiently with move operations, and similarly be able to move a
-// pair<K, V> in insert().
-//
-// The solution is this union, which aliases the const and non-const versions
-// of the pair. This also allows flat_hash_map<const K, V> to work, even though
-// that has the same efficiency issues with move in emplace() and insert() -
-// but people do it anyway.
-//
-// If kMutableKeys is false, only the value member can be accessed.
-//
-// If kMutableKeys is true, key can be accessed through all slots while value
-// and mutable_value must be accessed only via INITIALIZED slots. Slots are
-// created and destroyed via mutable_value so that the key can be moved later.
-//
-// Accessing one of the union fields while the other is active is safe as
-// long as they are layout-compatible, which is guaranteed by the definition of
-// kMutableKeys. For C++11, the relevant section of the standard is
-// https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19)
-template <class K, class V>
-union map_slot_type {
-  map_slot_type() {}
-  ~map_slot_type() = delete;
-  using value_type = std::pair<const K, V>;
-  using mutable_value_type =
-      std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>;
-
-  value_type value;
-  mutable_value_type mutable_value;
-  absl::remove_const_t<K> key;
-};
-
-template <class K, class V>
-struct map_slot_policy {
-  using slot_type = map_slot_type<K, V>;
-  using value_type = std::pair<const K, V>;
-  using mutable_value_type = std::pair<K, V>;
-
- private:
-  static void emplace(slot_type* slot) {
-    // The construction of union doesn't do anything at runtime but it allows us
-    // to access its members without violating aliasing rules.
-    new (slot) slot_type;
-  }
-  // If pair<const K, V> and pair<K, V> are layout-compatible, we can accept one
-  // or the other via slot_type. We are also free to access the key via
-  // slot_type::key in this case.
-  using kMutableKeys = memory_internal::IsLayoutCompatible<K, V>;
-
- public:
-  static value_type& element(slot_type* slot) { return slot->value; }
-  static const value_type& element(const slot_type* slot) {
-    return slot->value;
-  }
-
-  // When C++17 is available, we can use std::launder to provide mutable
-  // access to the key for use in node handle.
-#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
-  static K& mutable_key(slot_type* slot) {
-    // Still check for kMutableKeys so that we can avoid calling std::launder
-    // unless necessary because it can interfere with optimizations.
-    return kMutableKeys::value ? slot->key
-                               : *std::launder(const_cast<K*>(
-                                     std::addressof(slot->value.first)));
-  }
-#else  // !(defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606)
-  static const K& mutable_key(slot_type* slot) { return key(slot); }
-#endif
-
-  static const K& key(const slot_type* slot) {
-    return kMutableKeys::value ? slot->key : slot->value.first;
-  }
-
-  template <class Allocator, class... Args>
-  static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
-    emplace(slot);
-    if (kMutableKeys::value) {
-      absl::allocator_traits<Allocator>::construct(*alloc, &slot->mutable_value,
-                                                   std::forward<Args>(args)...);
-    } else {
-      absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
-                                                   std::forward<Args>(args)...);
-    }
-  }
-
-  // Construct this slot by moving from another slot.
-  template <class Allocator>
-  static void construct(Allocator* alloc, slot_type* slot, slot_type* other) {
-    emplace(slot);
-    if (kMutableKeys::value) {
-      absl::allocator_traits<Allocator>::construct(
-          *alloc, &slot->mutable_value, std::move(other->mutable_value));
-    } else {
-      absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
-                                                   std::move(other->value));
-    }
-  }
-
-  template <class Allocator>
-  static void destroy(Allocator* alloc, slot_type* slot) {
-    if (kMutableKeys::value) {
-      absl::allocator_traits<Allocator>::destroy(*alloc, &slot->mutable_value);
-    } else {
-      absl::allocator_traits<Allocator>::destroy(*alloc, &slot->value);
-    }
-  }
-
-  template <class Allocator>
-  static void transfer(Allocator* alloc, slot_type* new_slot,
-                       slot_type* old_slot) {
-    emplace(new_slot);
-    if (kMutableKeys::value) {
-      absl::allocator_traits<Allocator>::construct(
-          *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value));
-    } else {
-      absl::allocator_traits<Allocator>::construct(*alloc, &new_slot->value,
-                                                   std::move(old_slot->value));
-    }
-    destroy(alloc, old_slot);
-  }
-
-  template <class Allocator>
-  static void swap(Allocator* alloc, slot_type* a, slot_type* b) {
-    if (kMutableKeys::value) {
-      using std::swap;
-      swap(a->mutable_value, b->mutable_value);
-    } else {
-      value_type tmp = std::move(a->value);
-      absl::allocator_traits<Allocator>::destroy(*alloc, &a->value);
-      absl::allocator_traits<Allocator>::construct(*alloc, &a->value,
-                                                   std::move(b->value));
-      absl::allocator_traits<Allocator>::destroy(*alloc, &b->value);
-      absl::allocator_traits<Allocator>::construct(*alloc, &b->value,
-                                                   std::move(tmp));
-    }
-  }
-
-  template <class Allocator>
-  static void move(Allocator* alloc, slot_type* src, slot_type* dest) {
-    if (kMutableKeys::value) {
-      dest->mutable_value = std::move(src->mutable_value);
-    } else {
-      absl::allocator_traits<Allocator>::destroy(*alloc, &dest->value);
-      absl::allocator_traits<Allocator>::construct(*alloc, &dest->value,
-                                                   std::move(src->value));
-    }
-  }
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/container_memory_test.cc b/third_party/abseil_cpp/absl/container/internal/container_memory_test.cc
deleted file mode 100644
index 6a7fcd29ba..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/container_memory_test.cc
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/container_memory.h"
-
-#include <cstdint>
-#include <tuple>
-#include <typeindex>
-#include <typeinfo>
-#include <utility>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/test_instance_tracker.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using ::absl::test_internal::CopyableMovableInstance;
-using ::absl::test_internal::InstanceTracker;
-using ::testing::_;
-using ::testing::ElementsAre;
-using ::testing::Gt;
-using ::testing::Pair;
-
-TEST(Memory, AlignmentLargerThanBase) {
-  std::allocator<int8_t> alloc;
-  void* mem = Allocate<2>(&alloc, 3);
-  EXPECT_EQ(0, reinterpret_cast<uintptr_t>(mem) % 2);
-  memcpy(mem, "abc", 3);
-  Deallocate<2>(&alloc, mem, 3);
-}
-
-TEST(Memory, AlignmentSmallerThanBase) {
-  std::allocator<int64_t> alloc;
-  void* mem = Allocate<2>(&alloc, 3);
-  EXPECT_EQ(0, reinterpret_cast<uintptr_t>(mem) % 2);
-  memcpy(mem, "abc", 3);
-  Deallocate<2>(&alloc, mem, 3);
-}
-
-std::map<std::type_index, int>& AllocationMap() {
-  static auto* map = new std::map<std::type_index, int>;
-  return *map;
-}
-
-template <typename T>
-struct TypeCountingAllocator {
-  TypeCountingAllocator() = default;
-  template <typename U>
-  TypeCountingAllocator(const TypeCountingAllocator<U>&) {}  // NOLINT
-
-  using value_type = T;
-
-  T* allocate(size_t n, const void* = nullptr) {
-    AllocationMap()[typeid(T)] += n;
-    return std::allocator<T>().allocate(n);
-  }
-  void deallocate(T* p, std::size_t n) {
-    AllocationMap()[typeid(T)] -= n;
-    return std::allocator<T>().deallocate(p, n);
-  }
-};
-
-TEST(Memory, AllocateDeallocateMatchType) {
-  TypeCountingAllocator<int> alloc;
-  void* mem = Allocate<1>(&alloc, 1);
-  // Verify that it was allocated
-  EXPECT_THAT(AllocationMap(), ElementsAre(Pair(_, Gt(0))));
-  Deallocate<1>(&alloc, mem, 1);
-  // Verify that the deallocation matched.
-  EXPECT_THAT(AllocationMap(), ElementsAre(Pair(_, 0)));
-}
-
-class Fixture : public ::testing::Test {
-  using Alloc = std::allocator<std::string>;
-
- public:
-  Fixture() { ptr_ = std::allocator_traits<Alloc>::allocate(*alloc(), 1); }
-  ~Fixture() override {
-    std::allocator_traits<Alloc>::destroy(*alloc(), ptr_);
-    std::allocator_traits<Alloc>::deallocate(*alloc(), ptr_, 1);
-  }
-  std::string* ptr() { return ptr_; }
-  Alloc* alloc() { return &alloc_; }
-
- private:
-  Alloc alloc_;
-  std::string* ptr_;
-};
-
-TEST_F(Fixture, ConstructNoArgs) {
-  ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple());
-  EXPECT_EQ(*ptr(), "");
-}
-
-TEST_F(Fixture, ConstructOneArg) {
-  ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple("abcde"));
-  EXPECT_EQ(*ptr(), "abcde");
-}
-
-TEST_F(Fixture, ConstructTwoArg) {
-  ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple(5, 'a'));
-  EXPECT_EQ(*ptr(), "aaaaa");
-}
-
-TEST(PairArgs, NoArgs) {
-  EXPECT_THAT(PairArgs(),
-              Pair(std::forward_as_tuple(), std::forward_as_tuple()));
-}
-
-TEST(PairArgs, TwoArgs) {
-  EXPECT_EQ(
-      std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')),
-      PairArgs(1, 'A'));
-}
-
-TEST(PairArgs, Pair) {
-  EXPECT_EQ(
-      std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')),
-      PairArgs(std::make_pair(1, 'A')));
-}
-
-TEST(PairArgs, Piecewise) {
-  EXPECT_EQ(
-      std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')),
-      PairArgs(std::piecewise_construct, std::forward_as_tuple(1),
-               std::forward_as_tuple('A')));
-}
-
-TEST(WithConstructed, Simple) {
-  EXPECT_EQ(1, WithConstructed<absl::string_view>(
-                   std::make_tuple(std::string("a")),
-                   [](absl::string_view str) { return str.size(); }));
-}
-
-template <class F, class Arg>
-decltype(DecomposeValue(std::declval<F>(), std::declval<Arg>()))
-DecomposeValueImpl(int, F&& f, Arg&& arg) {
-  return DecomposeValue(std::forward<F>(f), std::forward<Arg>(arg));
-}
-
-template <class F, class Arg>
-const char* DecomposeValueImpl(char, F&& f, Arg&& arg) {
-  return "not decomposable";
-}
-
-template <class F, class Arg>
-decltype(DecomposeValueImpl(0, std::declval<F>(), std::declval<Arg>()))
-TryDecomposeValue(F&& f, Arg&& arg) {
-  return DecomposeValueImpl(0, std::forward<F>(f), std::forward<Arg>(arg));
-}
-
-TEST(DecomposeValue, Decomposable) {
-  auto f = [](const int& x, int&& y) {
-    EXPECT_EQ(&x, &y);
-    EXPECT_EQ(42, x);
-    return 'A';
-  };
-  EXPECT_EQ('A', TryDecomposeValue(f, 42));
-}
-
-TEST(DecomposeValue, NotDecomposable) {
-  auto f = [](void*) {
-    ADD_FAILURE() << "Must not be called";
-    return 'A';
-  };
-  EXPECT_STREQ("not decomposable", TryDecomposeValue(f, 42));
-}
-
-template <class F, class... Args>
-decltype(DecomposePair(std::declval<F>(), std::declval<Args>()...))
-DecomposePairImpl(int, F&& f, Args&&... args) {
-  return DecomposePair(std::forward<F>(f), std::forward<Args>(args)...);
-}
-
-template <class F, class... Args>
-const char* DecomposePairImpl(char, F&& f, Args&&... args) {
-  return "not decomposable";
-}
-
-template <class F, class... Args>
-decltype(DecomposePairImpl(0, std::declval<F>(), std::declval<Args>()...))
-TryDecomposePair(F&& f, Args&&... args) {
-  return DecomposePairImpl(0, std::forward<F>(f), std::forward<Args>(args)...);
-}
-
-TEST(DecomposePair, Decomposable) {
-  auto f = [](const int& x, std::piecewise_construct_t, std::tuple<int&&> k,
-              std::tuple<double>&& v) {
-    EXPECT_EQ(&x, &std::get<0>(k));
-    EXPECT_EQ(42, x);
-    EXPECT_EQ(0.5, std::get<0>(v));
-    return 'A';
-  };
-  EXPECT_EQ('A', TryDecomposePair(f, 42, 0.5));
-  EXPECT_EQ('A', TryDecomposePair(f, std::make_pair(42, 0.5)));
-  EXPECT_EQ('A', TryDecomposePair(f, std::piecewise_construct,
-                                  std::make_tuple(42), std::make_tuple(0.5)));
-}
-
-TEST(DecomposePair, NotDecomposable) {
-  auto f = [](...) {
-    ADD_FAILURE() << "Must not be called";
-    return 'A';
-  };
-  EXPECT_STREQ("not decomposable",
-               TryDecomposePair(f));
-  EXPECT_STREQ("not decomposable",
-               TryDecomposePair(f, std::piecewise_construct, std::make_tuple(),
-                                std::make_tuple(0.5)));
-}
-
-TEST(MapSlotPolicy, ConstKeyAndValue) {
-  using slot_policy = map_slot_policy<const CopyableMovableInstance,
-                                      const CopyableMovableInstance>;
-  using slot_type = typename slot_policy::slot_type;
-
-  union Slots {
-    Slots() {}
-    ~Slots() {}
-    slot_type slots[100];
-  } slots;
-
-  std::allocator<
-      std::pair<const CopyableMovableInstance, const CopyableMovableInstance>>
-      alloc;
-  InstanceTracker tracker;
-  slot_policy::construct(&alloc, &slots.slots[0], CopyableMovableInstance(1),
-                         CopyableMovableInstance(1));
-  for (int i = 0; i < 99; ++i) {
-    slot_policy::transfer(&alloc, &slots.slots[i + 1], &slots.slots[i]);
-  }
-  slot_policy::destroy(&alloc, &slots.slots[99]);
-
-  EXPECT_EQ(tracker.copies(), 0);
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/counting_allocator.h b/third_party/abseil_cpp/absl/container/internal/counting_allocator.h
deleted file mode 100644
index 927cf08255..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/counting_allocator.h
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
-#define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
-
-#include <cstdint>
-#include <memory>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// This is a stateful allocator, but the state lives outside of the
-// allocator (in whatever test is using the allocator). This is odd
-// but helps in tests where the allocator is propagated into nested
-// containers - that chain of allocators uses the same state and is
-// thus easier to query for aggregate allocation information.
-template <typename T>
-class CountingAllocator {
- public:
-  using Allocator = std::allocator<T>;
-  using AllocatorTraits = std::allocator_traits<Allocator>;
-  using value_type = typename AllocatorTraits::value_type;
-  using pointer = typename AllocatorTraits::pointer;
-  using const_pointer = typename AllocatorTraits::const_pointer;
-  using size_type = typename AllocatorTraits::size_type;
-  using difference_type = typename AllocatorTraits::difference_type;
-
-  CountingAllocator() = default;
-  explicit CountingAllocator(int64_t* bytes_used) : bytes_used_(bytes_used) {}
-  CountingAllocator(int64_t* bytes_used, int64_t* instance_count)
-      : bytes_used_(bytes_used), instance_count_(instance_count) {}
-
-  template <typename U>
-  CountingAllocator(const CountingAllocator<U>& x)
-      : bytes_used_(x.bytes_used_), instance_count_(x.instance_count_) {}
-
-  pointer allocate(
-      size_type n,
-      typename AllocatorTraits::const_void_pointer hint = nullptr) {
-    Allocator allocator;
-    pointer ptr = AllocatorTraits::allocate(allocator, n, hint);
-    if (bytes_used_ != nullptr) {
-      *bytes_used_ += n * sizeof(T);
-    }
-    return ptr;
-  }
-
-  void deallocate(pointer p, size_type n) {
-    Allocator allocator;
-    AllocatorTraits::deallocate(allocator, p, n);
-    if (bytes_used_ != nullptr) {
-      *bytes_used_ -= n * sizeof(T);
-    }
-  }
-
-  template <typename U, typename... Args>
-  void construct(U* p, Args&&... args) {
-    Allocator allocator;
-    AllocatorTraits::construct(allocator, p, std::forward<Args>(args)...);
-    if (instance_count_ != nullptr) {
-      *instance_count_ += 1;
-    }
-  }
-
-  template <typename U>
-  void destroy(U* p) {
-    Allocator allocator;
-    AllocatorTraits::destroy(allocator, p);
-    if (instance_count_ != nullptr) {
-      *instance_count_ -= 1;
-    }
-  }
-
-  template <typename U>
-  class rebind {
-   public:
-    using other = CountingAllocator<U>;
-  };
-
-  friend bool operator==(const CountingAllocator& a,
-                         const CountingAllocator& b) {
-    return a.bytes_used_ == b.bytes_used_ &&
-           a.instance_count_ == b.instance_count_;
-  }
-
-  friend bool operator!=(const CountingAllocator& a,
-                         const CountingAllocator& b) {
-    return !(a == b);
-  }
-
-  int64_t* bytes_used_ = nullptr;
-  int64_t* instance_count_ = nullptr;
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/hash_function_defaults.h b/third_party/abseil_cpp/absl/container/internal/hash_function_defaults.h
deleted file mode 100644
index 0683422ad8..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hash_function_defaults.h
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Define the default Hash and Eq functions for SwissTable containers.
-//
-// std::hash<T> and std::equal_to<T> are not appropriate hash and equal
-// functions for SwissTable containers. There are two reasons for this.
-//
-// SwissTable containers are power of 2 sized containers:
-//
-// This means they use the lower bits of the hash value to find the slot for
-// each entry. The typical hash function for integral types is the identity.
-// This is a very weak hash function for SwissTable and any power of 2 sized
-// hashtable implementation which will lead to excessive collisions. For
-// SwissTable we use murmur3 style mixing to reduce collisions to a minimum.
-//
-// SwissTable containers support heterogeneous lookup:
-//
-// In order to make heterogeneous lookup work, hash and equal functions must be
-// polymorphic. At the same time they have to satisfy the same requirements the
-// C++ standard imposes on hash functions and equality operators. That is:
-//
-//   if hash_default_eq<T>(a, b) returns true for any a and b of type T, then
-//   hash_default_hash<T>(a) must equal hash_default_hash<T>(b)
-//
-// For SwissTable containers this requirement is relaxed to allow a and b of
-// any and possibly different types. Note that like the standard the hash and
-// equal functions are still bound to T. This is important because some type U
-// can be hashed by/tested for equality differently depending on T. A notable
-// example is `const char*`. `const char*` is treated as a c-style string when
-// the hash function is hash<std::string> but as a pointer when the hash
-// function is hash<void*>.
-//
-#ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
-#define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
-
-#include <stdint.h>
-#include <cstddef>
-#include <memory>
-#include <string>
-#include <type_traits>
-
-#include "absl/base/config.h"
-#include "absl/hash/hash.h"
-#include "absl/strings/cord.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// The hash of an object of type T is computed by using absl::Hash.
-template <class T, class E = void>
-struct HashEq {
-  using Hash = absl::Hash<T>;
-  using Eq = std::equal_to<T>;
-};
-
-struct StringHash {
-  using is_transparent = void;
-
-  size_t operator()(absl::string_view v) const {
-    return absl::Hash<absl::string_view>{}(v);
-  }
-  size_t operator()(const absl::Cord& v) const {
-    return absl::Hash<absl::Cord>{}(v);
-  }
-};
-
-// Supports heterogeneous lookup for string-like elements.
-struct StringHashEq {
-  using Hash = StringHash;
-  struct Eq {
-    using is_transparent = void;
-    bool operator()(absl::string_view lhs, absl::string_view rhs) const {
-      return lhs == rhs;
-    }
-    bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const {
-      return lhs == rhs;
-    }
-    bool operator()(const absl::Cord& lhs, absl::string_view rhs) const {
-      return lhs == rhs;
-    }
-    bool operator()(absl::string_view lhs, const absl::Cord& rhs) const {
-      return lhs == rhs;
-    }
-  };
-};
-
-template <>
-struct HashEq<std::string> : StringHashEq {};
-template <>
-struct HashEq<absl::string_view> : StringHashEq {};
-template <>
-struct HashEq<absl::Cord> : StringHashEq {};
-
-// Supports heterogeneous lookup for pointers and smart pointers.
-template <class T>
-struct HashEq<T*> {
-  struct Hash {
-    using is_transparent = void;
-    template <class U>
-    size_t operator()(const U& ptr) const {
-      return absl::Hash<const T*>{}(HashEq::ToPtr(ptr));
-    }
-  };
-  struct Eq {
-    using is_transparent = void;
-    template <class A, class B>
-    bool operator()(const A& a, const B& b) const {
-      return HashEq::ToPtr(a) == HashEq::ToPtr(b);
-    }
-  };
-
- private:
-  static const T* ToPtr(const T* ptr) { return ptr; }
-  template <class U, class D>
-  static const T* ToPtr(const std::unique_ptr<U, D>& ptr) {
-    return ptr.get();
-  }
-  template <class U>
-  static const T* ToPtr(const std::shared_ptr<U>& ptr) {
-    return ptr.get();
-  }
-};
-
-template <class T, class D>
-struct HashEq<std::unique_ptr<T, D>> : HashEq<T*> {};
-template <class T>
-struct HashEq<std::shared_ptr<T>> : HashEq<T*> {};
-
-// This header's visibility is restricted.  If you need to access the default
-// hasher please use the container's ::hasher alias instead.
-//
-// Example: typename Hash = typename absl::flat_hash_map<K, V>::hasher
-template <class T>
-using hash_default_hash = typename container_internal::HashEq<T>::Hash;
-
-// This header's visibility is restricted.  If you need to access the default
-// key equal please use the container's ::key_equal alias instead.
-//
-// Example: typename Eq = typename absl::flat_hash_map<K, V, Hash>::key_equal
-template <class T>
-using hash_default_eq = typename container_internal::HashEq<T>::Eq;
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/hash_function_defaults_test.cc b/third_party/abseil_cpp/absl/container/internal/hash_function_defaults_test.cc
deleted file mode 100644
index 59576b8ede..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hash_function_defaults_test.cc
+++ /dev/null
@@ -1,383 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/hash_function_defaults.h"
-
-#include <functional>
-#include <type_traits>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/random/random.h"
-#include "absl/strings/cord.h"
-#include "absl/strings/cord_test_helpers.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using ::testing::Types;
-
-TEST(Eq, Int32) {
-  hash_default_eq<int32_t> eq;
-  EXPECT_TRUE(eq(1, 1u));
-  EXPECT_TRUE(eq(1, char{1}));
-  EXPECT_TRUE(eq(1, true));
-  EXPECT_TRUE(eq(1, double{1.1}));
-  EXPECT_FALSE(eq(1, char{2}));
-  EXPECT_FALSE(eq(1, 2u));
-  EXPECT_FALSE(eq(1, false));
-  EXPECT_FALSE(eq(1, 2.));
-}
-
-TEST(Hash, Int32) {
-  hash_default_hash<int32_t> hash;
-  auto h = hash(1);
-  EXPECT_EQ(h, hash(1u));
-  EXPECT_EQ(h, hash(char{1}));
-  EXPECT_EQ(h, hash(true));
-  EXPECT_EQ(h, hash(double{1.1}));
-  EXPECT_NE(h, hash(2u));
-  EXPECT_NE(h, hash(char{2}));
-  EXPECT_NE(h, hash(false));
-  EXPECT_NE(h, hash(2.));
-}
-
-enum class MyEnum { A, B, C, D };
-
-TEST(Eq, Enum) {
-  hash_default_eq<MyEnum> eq;
-  EXPECT_TRUE(eq(MyEnum::A, MyEnum::A));
-  EXPECT_FALSE(eq(MyEnum::A, MyEnum::B));
-}
-
-TEST(Hash, Enum) {
-  hash_default_hash<MyEnum> hash;
-
-  for (MyEnum e : {MyEnum::A, MyEnum::B, MyEnum::C}) {
-    auto h = hash(e);
-    EXPECT_EQ(h, hash_default_hash<int>{}(static_cast<int>(e)));
-    EXPECT_NE(h, hash(MyEnum::D));
-  }
-}
-
-using StringTypes = ::testing::Types<std::string, absl::string_view>;
-
-template <class T>
-struct EqString : ::testing::Test {
-  hash_default_eq<T> key_eq;
-};
-
-TYPED_TEST_SUITE(EqString, StringTypes);
-
-template <class T>
-struct HashString : ::testing::Test {
-  hash_default_hash<T> hasher;
-};
-
-TYPED_TEST_SUITE(HashString, StringTypes);
-
-TYPED_TEST(EqString, Works) {
-  auto eq = this->key_eq;
-  EXPECT_TRUE(eq("a", "a"));
-  EXPECT_TRUE(eq("a", absl::string_view("a")));
-  EXPECT_TRUE(eq("a", std::string("a")));
-  EXPECT_FALSE(eq("a", "b"));
-  EXPECT_FALSE(eq("a", absl::string_view("b")));
-  EXPECT_FALSE(eq("a", std::string("b")));
-}
-
-TYPED_TEST(HashString, Works) {
-  auto hash = this->hasher;
-  auto h = hash("a");
-  EXPECT_EQ(h, hash(absl::string_view("a")));
-  EXPECT_EQ(h, hash(std::string("a")));
-  EXPECT_NE(h, hash(absl::string_view("b")));
-  EXPECT_NE(h, hash(std::string("b")));
-}
-
-struct NoDeleter {
-  template <class T>
-  void operator()(const T* ptr) const {}
-};
-
-using PointerTypes =
-    ::testing::Types<const int*, int*, std::unique_ptr<const int>,
-                     std::unique_ptr<const int, NoDeleter>,
-                     std::unique_ptr<int>, std::unique_ptr<int, NoDeleter>,
-                     std::shared_ptr<const int>, std::shared_ptr<int>>;
-
-template <class T>
-struct EqPointer : ::testing::Test {
-  hash_default_eq<T> key_eq;
-};
-
-TYPED_TEST_SUITE(EqPointer, PointerTypes);
-
-template <class T>
-struct HashPointer : ::testing::Test {
-  hash_default_hash<T> hasher;
-};
-
-TYPED_TEST_SUITE(HashPointer, PointerTypes);
-
-TYPED_TEST(EqPointer, Works) {
-  int dummy;
-  auto eq = this->key_eq;
-  auto sptr = std::make_shared<int>();
-  std::shared_ptr<const int> csptr = sptr;
-  int* ptr = sptr.get();
-  const int* cptr = ptr;
-  std::unique_ptr<int, NoDeleter> uptr(ptr);
-  std::unique_ptr<const int, NoDeleter> cuptr(ptr);
-
-  EXPECT_TRUE(eq(ptr, cptr));
-  EXPECT_TRUE(eq(ptr, sptr));
-  EXPECT_TRUE(eq(ptr, uptr));
-  EXPECT_TRUE(eq(ptr, csptr));
-  EXPECT_TRUE(eq(ptr, cuptr));
-  EXPECT_FALSE(eq(&dummy, cptr));
-  EXPECT_FALSE(eq(&dummy, sptr));
-  EXPECT_FALSE(eq(&dummy, uptr));
-  EXPECT_FALSE(eq(&dummy, csptr));
-  EXPECT_FALSE(eq(&dummy, cuptr));
-}
-
-TEST(Hash, DerivedAndBase) {
-  struct Base {};
-  struct Derived : Base {};
-
-  hash_default_hash<Base*> hasher;
-
-  Base base;
-  Derived derived;
-  EXPECT_NE(hasher(&base), hasher(&derived));
-  EXPECT_EQ(hasher(static_cast<Base*>(&derived)), hasher(&derived));
-
-  auto dp = std::make_shared<Derived>();
-  EXPECT_EQ(hasher(static_cast<Base*>(dp.get())), hasher(dp));
-}
-
-TEST(Hash, FunctionPointer) {
-  using Func = int (*)();
-  hash_default_hash<Func> hasher;
-  hash_default_eq<Func> eq;
-
-  Func p1 = [] { return 1; }, p2 = [] { return 2; };
-  EXPECT_EQ(hasher(p1), hasher(p1));
-  EXPECT_TRUE(eq(p1, p1));
-
-  EXPECT_NE(hasher(p1), hasher(p2));
-  EXPECT_FALSE(eq(p1, p2));
-}
-
-TYPED_TEST(HashPointer, Works) {
-  int dummy;
-  auto hash = this->hasher;
-  auto sptr = std::make_shared<int>();
-  std::shared_ptr<const int> csptr = sptr;
-  int* ptr = sptr.get();
-  const int* cptr = ptr;
-  std::unique_ptr<int, NoDeleter> uptr(ptr);
-  std::unique_ptr<const int, NoDeleter> cuptr(ptr);
-
-  EXPECT_EQ(hash(ptr), hash(cptr));
-  EXPECT_EQ(hash(ptr), hash(sptr));
-  EXPECT_EQ(hash(ptr), hash(uptr));
-  EXPECT_EQ(hash(ptr), hash(csptr));
-  EXPECT_EQ(hash(ptr), hash(cuptr));
-  EXPECT_NE(hash(&dummy), hash(cptr));
-  EXPECT_NE(hash(&dummy), hash(sptr));
-  EXPECT_NE(hash(&dummy), hash(uptr));
-  EXPECT_NE(hash(&dummy), hash(csptr));
-  EXPECT_NE(hash(&dummy), hash(cuptr));
-}
-
-TEST(EqCord, Works) {
-  hash_default_eq<absl::Cord> eq;
-  const absl::string_view a_string_view = "a";
-  const absl::Cord a_cord(a_string_view);
-  const absl::string_view b_string_view = "b";
-  const absl::Cord b_cord(b_string_view);
-
-  EXPECT_TRUE(eq(a_cord, a_cord));
-  EXPECT_TRUE(eq(a_cord, a_string_view));
-  EXPECT_TRUE(eq(a_string_view, a_cord));
-  EXPECT_FALSE(eq(a_cord, b_cord));
-  EXPECT_FALSE(eq(a_cord, b_string_view));
-  EXPECT_FALSE(eq(b_string_view, a_cord));
-}
-
-TEST(HashCord, Works) {
-  hash_default_hash<absl::Cord> hash;
-  const absl::string_view a_string_view = "a";
-  const absl::Cord a_cord(a_string_view);
-  const absl::string_view b_string_view = "b";
-  const absl::Cord b_cord(b_string_view);
-
-  EXPECT_EQ(hash(a_cord), hash(a_cord));
-  EXPECT_EQ(hash(b_cord), hash(b_cord));
-  EXPECT_EQ(hash(a_string_view), hash(a_cord));
-  EXPECT_EQ(hash(b_string_view), hash(b_cord));
-  EXPECT_EQ(hash(absl::Cord("")), hash(""));
-  EXPECT_EQ(hash(absl::Cord()), hash(absl::string_view()));
-
-  EXPECT_NE(hash(a_cord), hash(b_cord));
-  EXPECT_NE(hash(a_cord), hash(b_string_view));
-  EXPECT_NE(hash(a_string_view), hash(b_cord));
-  EXPECT_NE(hash(a_string_view), hash(b_string_view));
-}
-
-void NoOpReleaser(absl::string_view data, void* arg) {}
-
-TEST(HashCord, FragmentedCordWorks) {
-  hash_default_hash<absl::Cord> hash;
-  absl::Cord c = absl::MakeFragmentedCord({"a", "b", "c"});
-  EXPECT_FALSE(c.TryFlat().has_value());
-  EXPECT_EQ(hash(c), hash("abc"));
-}
-
-TEST(HashCord, FragmentedLongCordWorks) {
-  hash_default_hash<absl::Cord> hash;
-  // Crete some large strings which do not fit on the stack.
-  std::string a(65536, 'a');
-  std::string b(65536, 'b');
-  absl::Cord c = absl::MakeFragmentedCord({a, b});
-  EXPECT_FALSE(c.TryFlat().has_value());
-  EXPECT_EQ(hash(c), hash(a + b));
-}
-
-TEST(HashCord, RandomCord) {
-  hash_default_hash<absl::Cord> hash;
-  auto bitgen = absl::BitGen();
-  for (int i = 0; i < 1000; ++i) {
-    const int number_of_segments = absl::Uniform(bitgen, 0, 10);
-    std::vector<std::string> pieces;
-    for (size_t s = 0; s < number_of_segments; ++s) {
-      std::string str;
-      str.resize(absl::Uniform(bitgen, 0, 4096));
-      // MSVC needed the explicit return type in the lambda.
-      std::generate(str.begin(), str.end(), [&]() -> char {
-        return static_cast<char>(absl::Uniform<unsigned char>(bitgen));
-      });
-      pieces.push_back(str);
-    }
-    absl::Cord c = absl::MakeFragmentedCord(pieces);
-    EXPECT_EQ(hash(c), hash(std::string(c)));
-  }
-}
-
-// Cartesian product of (std::string, absl::string_view)
-// with (std::string, absl::string_view, const char*, absl::Cord).
-using StringTypesCartesianProduct = Types<
-    // clang-format off
-    std::pair<absl::Cord, std::string>,
-    std::pair<absl::Cord, absl::string_view>,
-    std::pair<absl::Cord, absl::Cord>,
-    std::pair<absl::Cord, const char*>,
-
-    std::pair<std::string, absl::Cord>,
-    std::pair<absl::string_view, absl::Cord>,
-
-    std::pair<absl::string_view, std::string>,
-    std::pair<absl::string_view, absl::string_view>,
-    std::pair<absl::string_view, const char*>>;
-// clang-format on
-
-constexpr char kFirstString[] = "abc123";
-constexpr char kSecondString[] = "ijk456";
-
-template <typename T>
-struct StringLikeTest : public ::testing::Test {
-  typename T::first_type a1{kFirstString};
-  typename T::second_type b1{kFirstString};
-  typename T::first_type a2{kSecondString};
-  typename T::second_type b2{kSecondString};
-  hash_default_eq<typename T::first_type> eq;
-  hash_default_hash<typename T::first_type> hash;
-};
-
-TYPED_TEST_CASE_P(StringLikeTest);
-
-TYPED_TEST_P(StringLikeTest, Eq) {
-  EXPECT_TRUE(this->eq(this->a1, this->b1));
-  EXPECT_TRUE(this->eq(this->b1, this->a1));
-}
-
-TYPED_TEST_P(StringLikeTest, NotEq) {
-  EXPECT_FALSE(this->eq(this->a1, this->b2));
-  EXPECT_FALSE(this->eq(this->b2, this->a1));
-}
-
-TYPED_TEST_P(StringLikeTest, HashEq) {
-  EXPECT_EQ(this->hash(this->a1), this->hash(this->b1));
-  EXPECT_EQ(this->hash(this->a2), this->hash(this->b2));
-  // It would be a poor hash function which collides on these strings.
-  EXPECT_NE(this->hash(this->a1), this->hash(this->b2));
-}
-
-TYPED_TEST_SUITE(StringLikeTest, StringTypesCartesianProduct);
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-enum Hash : size_t {
-  kStd = 0x1,       // std::hash
-#ifdef _MSC_VER
-  kExtension = kStd,  // In MSVC, std::hash == ::hash
-#else                 // _MSC_VER
-  kExtension = 0x2,  // ::hash (GCC extension)
-#endif                // _MSC_VER
-};
-
-// H is a bitmask of Hash enumerations.
-// Hashable<H> is hashable via all means specified in H.
-template <int H>
-struct Hashable {
-  static constexpr bool HashableBy(Hash h) { return h & H; }
-};
-
-namespace std {
-template <int H>
-struct hash<Hashable<H>> {
-  template <class E = Hashable<H>,
-            class = typename std::enable_if<E::HashableBy(kStd)>::type>
-  size_t operator()(E) const {
-    return kStd;
-  }
-};
-}  // namespace std
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-template <class T>
-size_t Hash(const T& v) {
-  return hash_default_hash<T>()(v);
-}
-
-TEST(Delegate, HashDispatch) {
-  EXPECT_EQ(Hash(kStd), Hash(Hashable<kStd>()));
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/hash_generator_testing.cc b/third_party/abseil_cpp/absl/container/internal/hash_generator_testing.cc
deleted file mode 100644
index 59cc5aac7a..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hash_generator_testing.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/hash_generator_testing.h"
-
-#include <deque>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace hash_internal {
-namespace {
-
-class RandomDeviceSeedSeq {
- public:
-  using result_type = typename std::random_device::result_type;
-
-  template <class Iterator>
-  void generate(Iterator start, Iterator end) {
-    while (start != end) {
-      *start = gen_();
-      ++start;
-    }
-  }
-
- private:
-  std::random_device gen_;
-};
-
-}  // namespace
-
-std::mt19937_64* GetSharedRng() {
-  static auto* rng = [] {
-    RandomDeviceSeedSeq seed_seq;
-    return new std::mt19937_64(seed_seq);
-  }();
-  return rng;
-}
-
-std::string Generator<std::string>::operator()() const {
-  // NOLINTNEXTLINE(runtime/int)
-  std::uniform_int_distribution<short> chars(0x20, 0x7E);
-  std::string res;
-  res.resize(32);
-  std::generate(res.begin(), res.end(),
-                [&]() { return chars(*GetSharedRng()); });
-  return res;
-}
-
-absl::string_view Generator<absl::string_view>::operator()() const {
-  static auto* arena = new std::deque<std::string>();
-  // NOLINTNEXTLINE(runtime/int)
-  std::uniform_int_distribution<short> chars(0x20, 0x7E);
-  arena->emplace_back();
-  auto& res = arena->back();
-  res.resize(32);
-  std::generate(res.begin(), res.end(),
-                [&]() { return chars(*GetSharedRng()); });
-  return res;
-}
-
-}  // namespace hash_internal
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/hash_generator_testing.h b/third_party/abseil_cpp/absl/container/internal/hash_generator_testing.h
deleted file mode 100644
index 6869fe45e8..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hash_generator_testing.h
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Generates random values for testing. Specialized only for the few types we
-// care about.
-
-#ifndef ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
-#define ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
-
-#include <stdint.h>
-
-#include <algorithm>
-#include <iosfwd>
-#include <random>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "absl/container/internal/hash_policy_testing.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace hash_internal {
-namespace generator_internal {
-
-template <class Container, class = void>
-struct IsMap : std::false_type {};
-
-template <class Map>
-struct IsMap<Map, absl::void_t<typename Map::mapped_type>> : std::true_type {};
-
-}  // namespace generator_internal
-
-std::mt19937_64* GetSharedRng();
-
-enum Enum {
-  kEnumEmpty,
-  kEnumDeleted,
-};
-
-enum class EnumClass : uint64_t {
-  kEmpty,
-  kDeleted,
-};
-
-inline std::ostream& operator<<(std::ostream& o, const EnumClass& ec) {
-  return o << static_cast<uint64_t>(ec);
-}
-
-template <class T, class E = void>
-struct Generator;
-
-template <class T>
-struct Generator<T, typename std::enable_if<std::is_integral<T>::value>::type> {
-  T operator()() const {
-    std::uniform_int_distribution<T> dist;
-    return dist(*GetSharedRng());
-  }
-};
-
-template <>
-struct Generator<Enum> {
-  Enum operator()() const {
-    std::uniform_int_distribution<typename std::underlying_type<Enum>::type>
-        dist;
-    while (true) {
-      auto variate = dist(*GetSharedRng());
-      if (variate != kEnumEmpty && variate != kEnumDeleted)
-        return static_cast<Enum>(variate);
-    }
-  }
-};
-
-template <>
-struct Generator<EnumClass> {
-  EnumClass operator()() const {
-    std::uniform_int_distribution<
-        typename std::underlying_type<EnumClass>::type>
-        dist;
-    while (true) {
-      EnumClass variate = static_cast<EnumClass>(dist(*GetSharedRng()));
-      if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted)
-        return static_cast<EnumClass>(variate);
-    }
-  }
-};
-
-template <>
-struct Generator<std::string> {
-  std::string operator()() const;
-};
-
-template <>
-struct Generator<absl::string_view> {
-  absl::string_view operator()() const;
-};
-
-template <>
-struct Generator<NonStandardLayout> {
-  NonStandardLayout operator()() const {
-    return NonStandardLayout(Generator<std::string>()());
-  }
-};
-
-template <class K, class V>
-struct Generator<std::pair<K, V>> {
-  std::pair<K, V> operator()() const {
-    return std::pair<K, V>(Generator<typename std::decay<K>::type>()(),
-                           Generator<typename std::decay<V>::type>()());
-  }
-};
-
-template <class... Ts>
-struct Generator<std::tuple<Ts...>> {
-  std::tuple<Ts...> operator()() const {
-    return std::tuple<Ts...>(Generator<typename std::decay<Ts>::type>()()...);
-  }
-};
-
-template <class T>
-struct Generator<std::unique_ptr<T>> {
-  std::unique_ptr<T> operator()() const {
-    return absl::make_unique<T>(Generator<T>()());
-  }
-};
-
-template <class U>
-struct Generator<U, absl::void_t<decltype(std::declval<U&>().key()),
-                                decltype(std::declval<U&>().value())>>
-    : Generator<std::pair<
-          typename std::decay<decltype(std::declval<U&>().key())>::type,
-          typename std::decay<decltype(std::declval<U&>().value())>::type>> {};
-
-template <class Container>
-using GeneratedType = decltype(
-    std::declval<const Generator<
-        typename std::conditional<generator_internal::IsMap<Container>::value,
-                                  typename Container::value_type,
-                                  typename Container::key_type>::type>&>()());
-
-}  // namespace hash_internal
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/hash_policy_testing.h b/third_party/abseil_cpp/absl/container/internal/hash_policy_testing.h
deleted file mode 100644
index 01c40d2e5c..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hash_policy_testing.h
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Utilities to help tests verify that hash tables properly handle stateful
-// allocators and hash functions.
-
-#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_
-#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_
-
-#include <cstdlib>
-#include <limits>
-#include <memory>
-#include <ostream>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "absl/hash/hash.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace hash_testing_internal {
-
-template <class Derived>
-struct WithId {
-  WithId() : id_(next_id<Derived>()) {}
-  WithId(const WithId& that) : id_(that.id_) {}
-  WithId(WithId&& that) : id_(that.id_) { that.id_ = 0; }
-  WithId& operator=(const WithId& that) {
-    id_ = that.id_;
-    return *this;
-  }
-  WithId& operator=(WithId&& that) {
-    id_ = that.id_;
-    that.id_ = 0;
-    return *this;
-  }
-
-  size_t id() const { return id_; }
-
-  friend bool operator==(const WithId& a, const WithId& b) {
-    return a.id_ == b.id_;
-  }
-  friend bool operator!=(const WithId& a, const WithId& b) { return !(a == b); }
-
- protected:
-  explicit WithId(size_t id) : id_(id) {}
-
- private:
-  size_t id_;
-
-  template <class T>
-  static size_t next_id() {
-    // 0 is reserved for moved from state.
-    static size_t gId = 1;
-    return gId++;
-  }
-};
-
-}  // namespace hash_testing_internal
-
-struct NonStandardLayout {
-  NonStandardLayout() {}
-  explicit NonStandardLayout(std::string s) : value(std::move(s)) {}
-  virtual ~NonStandardLayout() {}
-
-  friend bool operator==(const NonStandardLayout& a,
-                         const NonStandardLayout& b) {
-    return a.value == b.value;
-  }
-  friend bool operator!=(const NonStandardLayout& a,
-                         const NonStandardLayout& b) {
-    return a.value != b.value;
-  }
-
-  template <typename H>
-  friend H AbslHashValue(H h, const NonStandardLayout& v) {
-    return H::combine(std::move(h), v.value);
-  }
-
-  std::string value;
-};
-
-struct StatefulTestingHash
-    : absl::container_internal::hash_testing_internal::WithId<
-          StatefulTestingHash> {
-  template <class T>
-  size_t operator()(const T& t) const {
-    return absl::Hash<T>{}(t);
-  }
-};
-
-struct StatefulTestingEqual
-    : absl::container_internal::hash_testing_internal::WithId<
-          StatefulTestingEqual> {
-  template <class T, class U>
-  bool operator()(const T& t, const U& u) const {
-    return t == u;
-  }
-};
-
-// It is expected that Alloc() == Alloc() for all allocators so we cannot use
-// WithId base. We need to explicitly assign ids.
-template <class T = int>
-struct Alloc : std::allocator<T> {
-  using propagate_on_container_swap = std::true_type;
-
-  // Using old paradigm for this to ensure compatibility.
-  explicit Alloc(size_t id = 0) : id_(id) {}
-
-  Alloc(const Alloc&) = default;
-  Alloc& operator=(const Alloc&) = default;
-
-  template <class U>
-  Alloc(const Alloc<U>& that) : std::allocator<T>(that), id_(that.id()) {}
-
-  template <class U>
-  struct rebind {
-    using other = Alloc<U>;
-  };
-
-  size_t id() const { return id_; }
-
-  friend bool operator==(const Alloc& a, const Alloc& b) {
-    return a.id_ == b.id_;
-  }
-  friend bool operator!=(const Alloc& a, const Alloc& b) { return !(a == b); }
-
- private:
-  size_t id_ = (std::numeric_limits<size_t>::max)();
-};
-
-template <class Map>
-auto items(const Map& m) -> std::vector<
-    std::pair<typename Map::key_type, typename Map::mapped_type>> {
-  using std::get;
-  std::vector<std::pair<typename Map::key_type, typename Map::mapped_type>> res;
-  res.reserve(m.size());
-  for (const auto& v : m) res.emplace_back(get<0>(v), get<1>(v));
-  return res;
-}
-
-template <class Set>
-auto keys(const Set& s)
-    -> std::vector<typename std::decay<typename Set::key_type>::type> {
-  std::vector<typename std::decay<typename Set::key_type>::type> res;
-  res.reserve(s.size());
-  for (const auto& v : s) res.emplace_back(v);
-  return res;
-}
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions
-// where the unordered containers are missing certain constructors that
-// take allocator arguments. This test is defined ad-hoc for the platforms
-// we care about (notably Crosstool 17) because libstdcxx's useless
-// versioning scheme precludes a more principled solution.
-// From GCC-4.9 Changelog: (src: https://gcc.gnu.org/gcc-4.9/changes.html)
-// "the unordered associative containers in <unordered_map> and <unordered_set>
-// meet the allocator-aware container requirements;"
-#if (defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425 ) || \
-( __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9 ))
-#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 0
-#else
-#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 1
-#endif
-
-#endif  // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/hash_policy_testing_test.cc b/third_party/abseil_cpp/absl/container/internal/hash_policy_testing_test.cc
deleted file mode 100644
index f0b20fe345..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hash_policy_testing_test.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/hash_policy_testing.h"
-
-#include "gtest/gtest.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-TEST(_, Hash) {
-  StatefulTestingHash h1;
-  EXPECT_EQ(1, h1.id());
-  StatefulTestingHash h2;
-  EXPECT_EQ(2, h2.id());
-  StatefulTestingHash h1c(h1);
-  EXPECT_EQ(1, h1c.id());
-  StatefulTestingHash h2m(std::move(h2));
-  EXPECT_EQ(2, h2m.id());
-  EXPECT_EQ(0, h2.id());
-  StatefulTestingHash h3;
-  EXPECT_EQ(3, h3.id());
-  h3 = StatefulTestingHash();
-  EXPECT_EQ(4, h3.id());
-  h3 = std::move(h1);
-  EXPECT_EQ(1, h3.id());
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/hash_policy_traits.h b/third_party/abseil_cpp/absl/container/internal/hash_policy_traits.h
deleted file mode 100644
index 46c97b18a2..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hash_policy_traits.h
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
-#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
-
-#include <cstddef>
-#include <memory>
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// Defines how slots are initialized/destroyed/moved.
-template <class Policy, class = void>
-struct hash_policy_traits {
-  // The type of the keys stored in the hashtable.
-  using key_type = typename Policy::key_type;
-
- private:
-  struct ReturnKey {
-    // When C++17 is available, we can use std::launder to provide mutable
-    // access to the key for use in node handle.
-#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
-    template <class Key,
-              absl::enable_if_t<std::is_lvalue_reference<Key>::value, int> = 0>
-    static key_type& Impl(Key&& k, int) {
-      return *std::launder(
-          const_cast<key_type*>(std::addressof(std::forward<Key>(k))));
-    }
-#endif
-
-    template <class Key>
-    static Key Impl(Key&& k, char) {
-      return std::forward<Key>(k);
-    }
-
-    // When Key=T&, we forward the lvalue reference.
-    // When Key=T, we return by value to avoid a dangling reference.
-    // eg, for string_hash_map.
-    template <class Key, class... Args>
-    auto operator()(Key&& k, const Args&...) const
-        -> decltype(Impl(std::forward<Key>(k), 0)) {
-      return Impl(std::forward<Key>(k), 0);
-    }
-  };
-
-  template <class P = Policy, class = void>
-  struct ConstantIteratorsImpl : std::false_type {};
-
-  template <class P>
-  struct ConstantIteratorsImpl<P, absl::void_t<typename P::constant_iterators>>
-      : P::constant_iterators {};
-
- public:
-  // The actual object stored in the hash table.
-  using slot_type = typename Policy::slot_type;
-
-  // The argument type for insertions into the hashtable. This is different
-  // from value_type for increased performance. See initializer_list constructor
-  // and insert() member functions for more details.
-  using init_type = typename Policy::init_type;
-
-  using reference = decltype(Policy::element(std::declval<slot_type*>()));
-  using pointer = typename std::remove_reference<reference>::type*;
-  using value_type = typename std::remove_reference<reference>::type;
-
-  // Policies can set this variable to tell raw_hash_set that all iterators
-  // should be constant, even `iterator`. This is useful for set-like
-  // containers.
-  // Defaults to false if not provided by the policy.
-  using constant_iterators = ConstantIteratorsImpl<>;
-
-  // PRECONDITION: `slot` is UNINITIALIZED
-  // POSTCONDITION: `slot` is INITIALIZED
-  template <class Alloc, class... Args>
-  static void construct(Alloc* alloc, slot_type* slot, Args&&... args) {
-    Policy::construct(alloc, slot, std::forward<Args>(args)...);
-  }
-
-  // PRECONDITION: `slot` is INITIALIZED
-  // POSTCONDITION: `slot` is UNINITIALIZED
-  template <class Alloc>
-  static void destroy(Alloc* alloc, slot_type* slot) {
-    Policy::destroy(alloc, slot);
-  }
-
-  // Transfers the `old_slot` to `new_slot`. Any memory allocated by the
-  // allocator inside `old_slot` to `new_slot` can be transferred.
-  //
-  // OPTIONAL: defaults to:
-  //
-  //     clone(new_slot, std::move(*old_slot));
-  //     destroy(old_slot);
-  //
-  // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED
-  // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is
-  //                UNINITIALIZED
-  template <class Alloc>
-  static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) {
-    transfer_impl(alloc, new_slot, old_slot, 0);
-  }
-
-  // PRECONDITION: `slot` is INITIALIZED
-  // POSTCONDITION: `slot` is INITIALIZED
-  template <class P = Policy>
-  static auto element(slot_type* slot) -> decltype(P::element(slot)) {
-    return P::element(slot);
-  }
-
-  // Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`.
-  //
-  // If `slot` is nullptr, returns the constant amount of memory owned by any
-  // full slot or -1 if slots own variable amounts of memory.
-  //
-  // PRECONDITION: `slot` is INITIALIZED or nullptr
-  template <class P = Policy>
-  static size_t space_used(const slot_type* slot) {
-    return P::space_used(slot);
-  }
-
-  // Provides generalized access to the key for elements, both for elements in
-  // the table and for elements that have not yet been inserted (or even
-  // constructed).  We would like an API that allows us to say: `key(args...)`
-  // but we cannot do that for all cases, so we use this more general API that
-  // can be used for many things, including the following:
-  //
-  //   - Given an element in a table, get its key.
-  //   - Given an element initializer, get its key.
-  //   - Given `emplace()` arguments, get the element key.
-  //
-  // Implementations of this must adhere to a very strict technical
-  // specification around aliasing and consuming arguments:
-  //
-  // Let `value_type` be the result type of `element()` without ref- and
-  // cv-qualifiers. The first argument is a functor, the rest are constructor
-  // arguments for `value_type`. Returns `std::forward<F>(f)(k, xs...)`, where
-  // `k` is the element key, and `xs...` are the new constructor arguments for
-  // `value_type`. It's allowed for `k` to alias `xs...`, and for both to alias
-  // `ts...`. The key won't be touched once `xs...` are used to construct an
-  // element; `ts...` won't be touched at all, which allows `apply()` to consume
-  // any rvalues among them.
-  //
-  // If `value_type` is constructible from `Ts&&...`, `Policy::apply()` must not
-  // trigger a hard compile error unless it originates from `f`. In other words,
-  // `Policy::apply()` must be SFINAE-friendly. If `value_type` is not
-  // constructible from `Ts&&...`, either SFINAE or a hard compile error is OK.
-  //
-  // If `Ts...` is `[cv] value_type[&]` or `[cv] init_type[&]`,
-  // `Policy::apply()` must work. A compile error is not allowed, SFINAE or not.
-  template <class F, class... Ts, class P = Policy>
-  static auto apply(F&& f, Ts&&... ts)
-      -> decltype(P::apply(std::forward<F>(f), std::forward<Ts>(ts)...)) {
-    return P::apply(std::forward<F>(f), std::forward<Ts>(ts)...);
-  }
-
-  // Returns the "key" portion of the slot.
-  // Used for node handle manipulation.
-  template <class P = Policy>
-  static auto mutable_key(slot_type* slot)
-      -> decltype(P::apply(ReturnKey(), element(slot))) {
-    return P::apply(ReturnKey(), element(slot));
-  }
-
-  // Returns the "value" (as opposed to the "key") portion of the element. Used
-  // by maps to implement `operator[]`, `at()` and `insert_or_assign()`.
-  template <class T, class P = Policy>
-  static auto value(T* elem) -> decltype(P::value(elem)) {
-    return P::value(elem);
-  }
-
- private:
-  // Use auto -> decltype as an enabler.
-  template <class Alloc, class P = Policy>
-  static auto transfer_impl(Alloc* alloc, slot_type* new_slot,
-                            slot_type* old_slot, int)
-      -> decltype((void)P::transfer(alloc, new_slot, old_slot)) {
-    P::transfer(alloc, new_slot, old_slot);
-  }
-  template <class Alloc>
-  static void transfer_impl(Alloc* alloc, slot_type* new_slot,
-                            slot_type* old_slot, char) {
-    construct(alloc, new_slot, std::move(element(old_slot)));
-    destroy(alloc, old_slot);
-  }
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/hash_policy_traits_test.cc b/third_party/abseil_cpp/absl/container/internal/hash_policy_traits_test.cc
deleted file mode 100644
index 6ef8b9e05f..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hash_policy_traits_test.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/hash_policy_traits.h"
-
-#include <functional>
-#include <memory>
-#include <new>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using ::testing::MockFunction;
-using ::testing::Return;
-using ::testing::ReturnRef;
-
-using Alloc = std::allocator<int>;
-using Slot = int;
-
-struct PolicyWithoutOptionalOps {
-  using slot_type = Slot;
-  using key_type = Slot;
-  using init_type = Slot;
-
-  static std::function<void(void*, Slot*, Slot)> construct;
-  static std::function<void(void*, Slot*)> destroy;
-
-  static std::function<Slot&(Slot*)> element;
-  static int apply(int v) { return apply_impl(v); }
-  static std::function<int(int)> apply_impl;
-  static std::function<Slot&(Slot*)> value;
-};
-
-std::function<void(void*, Slot*, Slot)> PolicyWithoutOptionalOps::construct;
-std::function<void(void*, Slot*)> PolicyWithoutOptionalOps::destroy;
-
-std::function<Slot&(Slot*)> PolicyWithoutOptionalOps::element;
-std::function<int(int)> PolicyWithoutOptionalOps::apply_impl;
-std::function<Slot&(Slot*)> PolicyWithoutOptionalOps::value;
-
-struct PolicyWithOptionalOps : PolicyWithoutOptionalOps {
-  static std::function<void(void*, Slot*, Slot*)> transfer;
-};
-
-std::function<void(void*, Slot*, Slot*)> PolicyWithOptionalOps::transfer;
-
-struct Test : ::testing::Test {
-  Test() {
-    PolicyWithoutOptionalOps::construct = [&](void* a1, Slot* a2, Slot a3) {
-      construct.Call(a1, a2, std::move(a3));
-    };
-    PolicyWithoutOptionalOps::destroy = [&](void* a1, Slot* a2) {
-      destroy.Call(a1, a2);
-    };
-
-    PolicyWithoutOptionalOps::element = [&](Slot* a1) -> Slot& {
-      return element.Call(a1);
-    };
-    PolicyWithoutOptionalOps::apply_impl = [&](int a1) -> int {
-      return apply.Call(a1);
-    };
-    PolicyWithoutOptionalOps::value = [&](Slot* a1) -> Slot& {
-      return value.Call(a1);
-    };
-
-    PolicyWithOptionalOps::transfer = [&](void* a1, Slot* a2, Slot* a3) {
-      return transfer.Call(a1, a2, a3);
-    };
-  }
-
-  std::allocator<int> alloc;
-  int a = 53;
-
-  MockFunction<void(void*, Slot*, Slot)> construct;
-  MockFunction<void(void*, Slot*)> destroy;
-
-  MockFunction<Slot&(Slot*)> element;
-  MockFunction<int(int)> apply;
-  MockFunction<Slot&(Slot*)> value;
-
-  MockFunction<void(void*, Slot*, Slot*)> transfer;
-};
-
-TEST_F(Test, construct) {
-  EXPECT_CALL(construct, Call(&alloc, &a, 53));
-  hash_policy_traits<PolicyWithoutOptionalOps>::construct(&alloc, &a, 53);
-}
-
-TEST_F(Test, destroy) {
-  EXPECT_CALL(destroy, Call(&alloc, &a));
-  hash_policy_traits<PolicyWithoutOptionalOps>::destroy(&alloc, &a);
-}
-
-TEST_F(Test, element) {
-  int b = 0;
-  EXPECT_CALL(element, Call(&a)).WillOnce(ReturnRef(b));
-  EXPECT_EQ(&b, &hash_policy_traits<PolicyWithoutOptionalOps>::element(&a));
-}
-
-TEST_F(Test, apply) {
-  EXPECT_CALL(apply, Call(42)).WillOnce(Return(1337));
-  EXPECT_EQ(1337, (hash_policy_traits<PolicyWithoutOptionalOps>::apply(42)));
-}
-
-TEST_F(Test, value) {
-  int b = 0;
-  EXPECT_CALL(value, Call(&a)).WillOnce(ReturnRef(b));
-  EXPECT_EQ(&b, &hash_policy_traits<PolicyWithoutOptionalOps>::value(&a));
-}
-
-TEST_F(Test, without_transfer) {
-  int b = 42;
-  EXPECT_CALL(element, Call(&b)).WillOnce(::testing::ReturnRef(b));
-  EXPECT_CALL(construct, Call(&alloc, &a, b));
-  EXPECT_CALL(destroy, Call(&alloc, &b));
-  hash_policy_traits<PolicyWithoutOptionalOps>::transfer(&alloc, &a, &b);
-}
-
-TEST_F(Test, with_transfer) {
-  int b = 42;
-  EXPECT_CALL(transfer, Call(&alloc, &a, &b));
-  hash_policy_traits<PolicyWithOptionalOps>::transfer(&alloc, &a, &b);
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/hashtable_debug.h b/third_party/abseil_cpp/absl/container/internal/hashtable_debug.h
deleted file mode 100644
index 19d52121d6..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hashtable_debug.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This library provides APIs to debug the probing behavior of hash tables.
-//
-// In general, the probing behavior is a black box for users and only the
-// side effects can be measured in the form of performance differences.
-// These APIs give a glimpse on the actual behavior of the probing algorithms in
-// these hashtables given a specified hash function and a set of elements.
-//
-// The probe count distribution can be used to assess the quality of the hash
-// function for that particular hash table. Note that a hash function that
-// performs well in one hash table implementation does not necessarily performs
-// well in a different one.
-//
-// This library supports std::unordered_{set,map}, dense_hash_{set,map} and
-// absl::{flat,node,string}_hash_{set,map}.
-
-#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_
-#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_
-
-#include <cstddef>
-#include <algorithm>
-#include <type_traits>
-#include <vector>
-
-#include "absl/container/internal/hashtable_debug_hooks.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// Returns the number of probes required to lookup `key`.  Returns 0 for a
-// search with no collisions.  Higher values mean more hash collisions occurred;
-// however, the exact meaning of this number varies according to the container
-// type.
-template <typename C>
-size_t GetHashtableDebugNumProbes(
-    const C& c, const typename C::key_type& key) {
-  return absl::container_internal::hashtable_debug_internal::
-      HashtableDebugAccess<C>::GetNumProbes(c, key);
-}
-
-// Gets a histogram of the number of probes for each elements in the container.
-// The sum of all the values in the vector is equal to container.size().
-template <typename C>
-std::vector<size_t> GetHashtableDebugNumProbesHistogram(const C& container) {
-  std::vector<size_t> v;
-  for (auto it = container.begin(); it != container.end(); ++it) {
-    size_t num_probes = GetHashtableDebugNumProbes(
-        container,
-        absl::container_internal::hashtable_debug_internal::GetKey<C>(*it, 0));
-    v.resize((std::max)(v.size(), num_probes + 1));
-    v[num_probes]++;
-  }
-  return v;
-}
-
-struct HashtableDebugProbeSummary {
-  size_t total_elements;
-  size_t total_num_probes;
-  double mean;
-};
-
-// Gets a summary of the probe count distribution for the elements in the
-// container.
-template <typename C>
-HashtableDebugProbeSummary GetHashtableDebugProbeSummary(const C& container) {
-  auto probes = GetHashtableDebugNumProbesHistogram(container);
-  HashtableDebugProbeSummary summary = {};
-  for (size_t i = 0; i < probes.size(); ++i) {
-    summary.total_elements += probes[i];
-    summary.total_num_probes += probes[i] * i;
-  }
-  summary.mean = 1.0 * summary.total_num_probes / summary.total_elements;
-  return summary;
-}
-
-// Returns the number of bytes requested from the allocator by the container
-// and not freed.
-template <typename C>
-size_t AllocatedByteSize(const C& c) {
-  return absl::container_internal::hashtable_debug_internal::
-      HashtableDebugAccess<C>::AllocatedByteSize(c);
-}
-
-// Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type `C`
-// and `c.size()` is equal to `num_elements`.
-template <typename C>
-size_t LowerBoundAllocatedByteSize(size_t num_elements) {
-  return absl::container_internal::hashtable_debug_internal::
-      HashtableDebugAccess<C>::LowerBoundAllocatedByteSize(num_elements);
-}
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/hashtable_debug_hooks.h b/third_party/abseil_cpp/absl/container/internal/hashtable_debug_hooks.h
deleted file mode 100644
index 3e9ea5954e..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hashtable_debug_hooks.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Provides the internal API for hashtable_debug.h.
-
-#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_
-#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_
-
-#include <cstddef>
-
-#include <algorithm>
-#include <type_traits>
-#include <vector>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace hashtable_debug_internal {
-
-// If it is a map, call get<0>().
-using std::get;
-template <typename T, typename = typename T::mapped_type>
-auto GetKey(const typename T::value_type& pair, int) -> decltype(get<0>(pair)) {
-  return get<0>(pair);
-}
-
-// If it is not a map, return the value directly.
-template <typename T>
-const typename T::key_type& GetKey(const typename T::key_type& key, char) {
-  return key;
-}
-
-// Containers should specialize this to provide debug information for that
-// container.
-template <class Container, typename Enabler = void>
-struct HashtableDebugAccess {
-  // Returns the number of probes required to find `key` in `c`.  The "number of
-  // probes" is a concept that can vary by container.  Implementations should
-  // return 0 when `key` was found in the minimum number of operations and
-  // should increment the result for each non-trivial operation required to find
-  // `key`.
-  //
-  // The default implementation uses the bucket api from the standard and thus
-  // works for `std::unordered_*` containers.
-  static size_t GetNumProbes(const Container& c,
-                             const typename Container::key_type& key) {
-    if (!c.bucket_count()) return {};
-    size_t num_probes = 0;
-    size_t bucket = c.bucket(key);
-    for (auto it = c.begin(bucket), e = c.end(bucket);; ++it, ++num_probes) {
-      if (it == e) return num_probes;
-      if (c.key_eq()(key, GetKey<Container>(*it, 0))) return num_probes;
-    }
-  }
-
-  // Returns the number of bytes requested from the allocator by the container
-  // and not freed.
-  //
-  // static size_t AllocatedByteSize(const Container& c);
-
-  // Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type
-  // `Container` and `c.size()` is equal to `num_elements`.
-  //
-  // static size_t LowerBoundAllocatedByteSize(size_t num_elements);
-};
-
-}  // namespace hashtable_debug_internal
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler.cc b/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler.cc
deleted file mode 100644
index e4484fbb1b..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler.cc
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/hashtablez_sampler.h"
-
-#include <atomic>
-#include <cassert>
-#include <cmath>
-#include <functional>
-#include <limits>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/exponential_biased.h"
-#include "absl/container/internal/have_sse.h"
-#include "absl/debugging/stacktrace.h"
-#include "absl/memory/memory.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-constexpr int HashtablezInfo::kMaxStackDepth;
-
-namespace {
-ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{
-    false
-};
-ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10};
-ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_max_samples{1 << 20};
-
-#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased
-    g_exponential_biased_generator;
-#endif
-
-}  // namespace
-
-#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0;
-#endif  // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-
-HashtablezSampler& HashtablezSampler::Global() {
-  static auto* sampler = new HashtablezSampler();
-  return *sampler;
-}
-
-HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback(
-    DisposeCallback f) {
-  return dispose_.exchange(f, std::memory_order_relaxed);
-}
-
-HashtablezInfo::HashtablezInfo() { PrepareForSampling(); }
-HashtablezInfo::~HashtablezInfo() = default;
-
-void HashtablezInfo::PrepareForSampling() {
-  capacity.store(0, std::memory_order_relaxed);
-  size.store(0, std::memory_order_relaxed);
-  num_erases.store(0, std::memory_order_relaxed);
-  num_rehashes.store(0, std::memory_order_relaxed);
-  max_probe_length.store(0, std::memory_order_relaxed);
-  total_probe_length.store(0, std::memory_order_relaxed);
-  hashes_bitwise_or.store(0, std::memory_order_relaxed);
-  hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed);
-
-  create_time = absl::Now();
-  // The inliner makes hardcoded skip_count difficult (especially when combined
-  // with LTO).  We use the ability to exclude stacks by regex when encoding
-  // instead.
-  depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth,
-                              /* skip_count= */ 0);
-  dead = nullptr;
-}
-
-HashtablezSampler::HashtablezSampler()
-    : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) {
-  absl::MutexLock l(&graveyard_.init_mu);
-  graveyard_.dead = &graveyard_;
-}
-
-HashtablezSampler::~HashtablezSampler() {
-  HashtablezInfo* s = all_.load(std::memory_order_acquire);
-  while (s != nullptr) {
-    HashtablezInfo* next = s->next;
-    delete s;
-    s = next;
-  }
-}
-
-void HashtablezSampler::PushNew(HashtablezInfo* sample) {
-  sample->next = all_.load(std::memory_order_relaxed);
-  while (!all_.compare_exchange_weak(sample->next, sample,
-                                     std::memory_order_release,
-                                     std::memory_order_relaxed)) {
-  }
-}
-
-void HashtablezSampler::PushDead(HashtablezInfo* sample) {
-  if (auto* dispose = dispose_.load(std::memory_order_relaxed)) {
-    dispose(*sample);
-  }
-
-  absl::MutexLock graveyard_lock(&graveyard_.init_mu);
-  absl::MutexLock sample_lock(&sample->init_mu);
-  sample->dead = graveyard_.dead;
-  graveyard_.dead = sample;
-}
-
-HashtablezInfo* HashtablezSampler::PopDead() {
-  absl::MutexLock graveyard_lock(&graveyard_.init_mu);
-
-  // The list is circular, so eventually it collapses down to
-  //   graveyard_.dead == &graveyard_
-  // when it is empty.
-  HashtablezInfo* sample = graveyard_.dead;
-  if (sample == &graveyard_) return nullptr;
-
-  absl::MutexLock sample_lock(&sample->init_mu);
-  graveyard_.dead = sample->dead;
-  sample->PrepareForSampling();
-  return sample;
-}
-
-HashtablezInfo* HashtablezSampler::Register() {
-  int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed);
-  if (size > g_hashtablez_max_samples.load(std::memory_order_relaxed)) {
-    size_estimate_.fetch_sub(1, std::memory_order_relaxed);
-    dropped_samples_.fetch_add(1, std::memory_order_relaxed);
-    return nullptr;
-  }
-
-  HashtablezInfo* sample = PopDead();
-  if (sample == nullptr) {
-    // Resurrection failed.  Hire a new warlock.
-    sample = new HashtablezInfo();
-    PushNew(sample);
-  }
-
-  return sample;
-}
-
-void HashtablezSampler::Unregister(HashtablezInfo* sample) {
-  PushDead(sample);
-  size_estimate_.fetch_sub(1, std::memory_order_relaxed);
-}
-
-int64_t HashtablezSampler::Iterate(
-    const std::function<void(const HashtablezInfo& stack)>& f) {
-  HashtablezInfo* s = all_.load(std::memory_order_acquire);
-  while (s != nullptr) {
-    absl::MutexLock l(&s->init_mu);
-    if (s->dead == nullptr) {
-      f(*s);
-    }
-    s = s->next;
-  }
-
-  return dropped_samples_.load(std::memory_order_relaxed);
-}
-
-static bool ShouldForceSampling() {
-  enum ForceState {
-    kDontForce,
-    kForce,
-    kUninitialized
-  };
-  ABSL_CONST_INIT static std::atomic<ForceState> global_state{
-      kUninitialized};
-  ForceState state = global_state.load(std::memory_order_relaxed);
-  if (ABSL_PREDICT_TRUE(state == kDontForce)) return false;
-
-  if (state == kUninitialized) {
-    state = AbslContainerInternalSampleEverything() ? kForce : kDontForce;
-    global_state.store(state, std::memory_order_relaxed);
-  }
-  return state == kForce;
-}
-
-HashtablezInfo* SampleSlow(int64_t* next_sample) {
-  if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
-    *next_sample = 1;
-    return HashtablezSampler::Global().Register();
-  }
-
-#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-  *next_sample = std::numeric_limits<int64_t>::max();
-  return nullptr;
-#else
-  bool first = *next_sample < 0;
-  *next_sample = g_exponential_biased_generator.GetStride(
-      g_hashtablez_sample_parameter.load(std::memory_order_relaxed));
-  // Small values of interval are equivalent to just sampling next time.
-  ABSL_ASSERT(*next_sample >= 1);
-
-  // g_hashtablez_enabled can be dynamically flipped, we need to set a threshold
-  // low enough that we will start sampling in a reasonable time, so we just use
-  // the default sampling rate.
-  if (!g_hashtablez_enabled.load(std::memory_order_relaxed)) return nullptr;
-
-  // We will only be negative on our first count, so we should just retry in
-  // that case.
-  if (first) {
-    if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr;
-    return SampleSlow(next_sample);
-  }
-
-  return HashtablezSampler::Global().Register();
-#endif
-}
-
-void UnsampleSlow(HashtablezInfo* info) {
-  HashtablezSampler::Global().Unregister(info);
-}
-
-void RecordInsertSlow(HashtablezInfo* info, size_t hash,
-                      size_t distance_from_desired) {
-  // SwissTables probe in groups of 16, so scale this to count items probes and
-  // not offset from desired.
-  size_t probe_length = distance_from_desired;
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
-  probe_length /= 16;
-#else
-  probe_length /= 8;
-#endif
-
-  info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed);
-  info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed);
-  info->max_probe_length.store(
-      std::max(info->max_probe_length.load(std::memory_order_relaxed),
-               probe_length),
-      std::memory_order_relaxed);
-  info->total_probe_length.fetch_add(probe_length, std::memory_order_relaxed);
-  info->size.fetch_add(1, std::memory_order_relaxed);
-}
-
-void SetHashtablezEnabled(bool enabled) {
-  g_hashtablez_enabled.store(enabled, std::memory_order_release);
-}
-
-void SetHashtablezSampleParameter(int32_t rate) {
-  if (rate > 0) {
-    g_hashtablez_sample_parameter.store(rate, std::memory_order_release);
-  } else {
-    ABSL_RAW_LOG(ERROR, "Invalid hashtablez sample rate: %lld",
-                 static_cast<long long>(rate));  // NOLINT(runtime/int)
-  }
-}
-
-void SetHashtablezMaxSamples(int32_t max) {
-  if (max > 0) {
-    g_hashtablez_max_samples.store(max, std::memory_order_release);
-  } else {
-    ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld",
-                 static_cast<long long>(max));  // NOLINT(runtime/int)
-  }
-}
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler.h b/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler.h
deleted file mode 100644
index 394348da58..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler.h
+++ /dev/null
@@ -1,321 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: hashtablez_sampler.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the API for a low level library to sample hashtables
-// and collect runtime statistics about them.
-//
-// `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which
-// store information about a single sample.
-//
-// `Record*` methods store information into samples.
-// `Sample()` and `Unsample()` make use of a single global sampler with
-// properties controlled by the flags hashtablez_enabled,
-// hashtablez_sample_rate, and hashtablez_max_samples.
-//
-// WARNING
-//
-// Using this sampling API may cause sampled Swiss tables to use the global
-// allocator (operator `new`) in addition to any custom allocator.  If you
-// are using a table in an unusual circumstance where allocation or calling a
-// linux syscall is unacceptable, this could interfere.
-//
-// This utility is internal-only. Use at your own risk.
-
-#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
-#define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
-
-#include <atomic>
-#include <functional>
-#include <memory>
-#include <vector>
-
-#include "absl/base/internal/per_thread_tls.h"
-#include "absl/base/optimization.h"
-#include "absl/container/internal/have_sse.h"
-#include "absl/synchronization/mutex.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// Stores information about a sampled hashtable.  All mutations to this *must*
-// be made through `Record*` functions below.  All reads from this *must* only
-// occur in the callback to `HashtablezSampler::Iterate`.
-struct HashtablezInfo {
-  // Constructs the object but does not fill in any fields.
-  HashtablezInfo();
-  ~HashtablezInfo();
-  HashtablezInfo(const HashtablezInfo&) = delete;
-  HashtablezInfo& operator=(const HashtablezInfo&) = delete;
-
-  // Puts the object into a clean state, fills in the logically `const` members,
-  // blocking for any readers that are currently sampling the object.
-  void PrepareForSampling() ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu);
-
-  // These fields are mutated by the various Record* APIs and need to be
-  // thread-safe.
-  std::atomic<size_t> capacity;
-  std::atomic<size_t> size;
-  std::atomic<size_t> num_erases;
-  std::atomic<size_t> num_rehashes;
-  std::atomic<size_t> max_probe_length;
-  std::atomic<size_t> total_probe_length;
-  std::atomic<size_t> hashes_bitwise_or;
-  std::atomic<size_t> hashes_bitwise_and;
-
-  // `HashtablezSampler` maintains intrusive linked lists for all samples.  See
-  // comments on `HashtablezSampler::all_` for details on these.  `init_mu`
-  // guards the ability to restore the sample to a pristine state.  This
-  // prevents races with sampling and resurrecting an object.
-  absl::Mutex init_mu;
-  HashtablezInfo* next;
-  HashtablezInfo* dead ABSL_GUARDED_BY(init_mu);
-
-  // All of the fields below are set by `PrepareForSampling`, they must not be
-  // mutated in `Record*` functions.  They are logically `const` in that sense.
-  // These are guarded by init_mu, but that is not externalized to clients, who
-  // can only read them during `HashtablezSampler::Iterate` which will hold the
-  // lock.
-  static constexpr int kMaxStackDepth = 64;
-  absl::Time create_time;
-  int32_t depth;
-  void* stack[kMaxStackDepth];
-};
-
-inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
-  total_probe_length /= 16;
-#else
-  total_probe_length /= 8;
-#endif
-  info->total_probe_length.store(total_probe_length, std::memory_order_relaxed);
-  info->num_erases.store(0, std::memory_order_relaxed);
-  // There is only one concurrent writer, so `load` then `store` is sufficient
-  // instead of using `fetch_add`.
-  info->num_rehashes.store(
-      1 + info->num_rehashes.load(std::memory_order_relaxed),
-      std::memory_order_relaxed);
-}
-
-inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
-                                     size_t capacity) {
-  info->size.store(size, std::memory_order_relaxed);
-  info->capacity.store(capacity, std::memory_order_relaxed);
-  if (size == 0) {
-    // This is a clear, reset the total/num_erases too.
-    info->total_probe_length.store(0, std::memory_order_relaxed);
-    info->num_erases.store(0, std::memory_order_relaxed);
-  }
-}
-
-void RecordInsertSlow(HashtablezInfo* info, size_t hash,
-                      size_t distance_from_desired);
-
-inline void RecordEraseSlow(HashtablezInfo* info) {
-  info->size.fetch_sub(1, std::memory_order_relaxed);
-  // There is only one concurrent writer, so `load` then `store` is sufficient
-  // instead of using `fetch_add`.
-  info->num_erases.store(
-      1 + info->num_erases.load(std::memory_order_relaxed),
-      std::memory_order_relaxed);
-}
-
-HashtablezInfo* SampleSlow(int64_t* next_sample);
-void UnsampleSlow(HashtablezInfo* info);
-
-#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-#error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set
-#endif  // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-
-#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-class HashtablezInfoHandle {
- public:
-  explicit HashtablezInfoHandle() : info_(nullptr) {}
-  explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {}
-  ~HashtablezInfoHandle() {
-    if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
-    UnsampleSlow(info_);
-  }
-
-  HashtablezInfoHandle(const HashtablezInfoHandle&) = delete;
-  HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete;
-
-  HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept
-      : info_(absl::exchange(o.info_, nullptr)) {}
-  HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept {
-    if (ABSL_PREDICT_FALSE(info_ != nullptr)) {
-      UnsampleSlow(info_);
-    }
-    info_ = absl::exchange(o.info_, nullptr);
-    return *this;
-  }
-
-  inline void RecordStorageChanged(size_t size, size_t capacity) {
-    if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
-    RecordStorageChangedSlow(info_, size, capacity);
-  }
-
-  inline void RecordRehash(size_t total_probe_length) {
-    if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
-    RecordRehashSlow(info_, total_probe_length);
-  }
-
-  inline void RecordInsert(size_t hash, size_t distance_from_desired) {
-    if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
-    RecordInsertSlow(info_, hash, distance_from_desired);
-  }
-
-  inline void RecordErase() {
-    if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
-    RecordEraseSlow(info_);
-  }
-
-  friend inline void swap(HashtablezInfoHandle& lhs,
-                          HashtablezInfoHandle& rhs) {
-    std::swap(lhs.info_, rhs.info_);
-  }
-
- private:
-  friend class HashtablezInfoHandlePeer;
-  HashtablezInfo* info_;
-};
-#else
-// Ensure that when Hashtablez is turned off at compile time, HashtablezInfo can
-// be removed by the linker, in order to reduce the binary size.
-class HashtablezInfoHandle {
- public:
-  explicit HashtablezInfoHandle() = default;
-  explicit HashtablezInfoHandle(std::nullptr_t) {}
-
-  inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {}
-  inline void RecordRehash(size_t /*total_probe_length*/) {}
-  inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/) {}
-  inline void RecordErase() {}
-
-  friend inline void swap(HashtablezInfoHandle& /*lhs*/,
-                          HashtablezInfoHandle& /*rhs*/) {}
-};
-#endif  // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-
-#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample;
-#endif  // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-
-// Returns an RAII sampling handle that manages registration and unregistation
-// with the global sampler.
-inline HashtablezInfoHandle Sample() {
-#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-  if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) {
-    return HashtablezInfoHandle(nullptr);
-  }
-  return HashtablezInfoHandle(SampleSlow(&global_next_sample));
-#else
-  return HashtablezInfoHandle(nullptr);
-#endif  // !ABSL_PER_THREAD_TLS
-}
-
-// Holds samples and their associated stack traces with a soft limit of
-// `SetHashtablezMaxSamples()`.
-//
-// Thread safe.
-class HashtablezSampler {
- public:
-  // Returns a global Sampler.
-  static HashtablezSampler& Global();
-
-  HashtablezSampler();
-  ~HashtablezSampler();
-
-  // Registers for sampling.  Returns an opaque registration info.
-  HashtablezInfo* Register();
-
-  // Unregisters the sample.
-  void Unregister(HashtablezInfo* sample);
-
-  // The dispose callback will be called on all samples the moment they are
-  // being unregistered. Only affects samples that are unregistered after the
-  // callback has been set.
-  // Returns the previous callback.
-  using DisposeCallback = void (*)(const HashtablezInfo&);
-  DisposeCallback SetDisposeCallback(DisposeCallback f);
-
-  // Iterates over all the registered `StackInfo`s.  Returning the number of
-  // samples that have been dropped.
-  int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& f);
-
- private:
-  void PushNew(HashtablezInfo* sample);
-  void PushDead(HashtablezInfo* sample);
-  HashtablezInfo* PopDead();
-
-  std::atomic<size_t> dropped_samples_;
-  std::atomic<size_t> size_estimate_;
-
-  // Intrusive lock free linked lists for tracking samples.
-  //
-  // `all_` records all samples (they are never removed from this list) and is
-  // terminated with a `nullptr`.
-  //
-  // `graveyard_.dead` is a circular linked list.  When it is empty,
-  // `graveyard_.dead == &graveyard`.  The list is circular so that
-  // every item on it (even the last) has a non-null dead pointer.  This allows
-  // `Iterate` to determine if a given sample is live or dead using only
-  // information on the sample itself.
-  //
-  // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead
-  // looks like this (G is the Graveyard):
-  //
-  //           +---+    +---+    +---+    +---+    +---+
-  //    all -->| A |--->| B |--->| C |--->| D |--->| E |
-  //           |   |    |   |    |   |    |   |    |   |
-  //   +---+   |   | +->|   |-+  |   | +->|   |-+  |   |
-  //   | G |   +---+ |  +---+ |  +---+ |  +---+ |  +---+
-  //   |   |         |        |        |        |
-  //   |   | --------+        +--------+        |
-  //   +---+                                    |
-  //     ^                                      |
-  //     +--------------------------------------+
-  //
-  std::atomic<HashtablezInfo*> all_;
-  HashtablezInfo graveyard_;
-
-  std::atomic<DisposeCallback> dispose_;
-};
-
-// Enables or disables sampling for Swiss tables.
-void SetHashtablezEnabled(bool enabled);
-
-// Sets the rate at which Swiss tables will be sampled.
-void SetHashtablezSampleParameter(int32_t rate);
-
-// Sets a soft max for the number of samples that will be kept.
-void SetHashtablezMaxSamples(int32_t max);
-
-// Configuration override.
-// This allows process-wide sampling without depending on order of
-// initialization of static storage duration objects.
-// The definition of this constant is weak, which allows us to inject a
-// different value for it at link time.
-extern "C" bool AbslContainerInternalSampleEverything();
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc b/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
deleted file mode 100644
index 78b9d362ac..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/hashtablez_sampler.h"
-
-#include "absl/base/attributes.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// See hashtablez_sampler.h for details.
-extern "C" ABSL_ATTRIBUTE_WEAK bool AbslContainerInternalSampleEverything() {
-  return false;
-}
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler_test.cc b/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler_test.cc
deleted file mode 100644
index 8d10a1e940..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/hashtablez_sampler_test.cc
+++ /dev/null
@@ -1,371 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/hashtablez_sampler.h"
-
-#include <atomic>
-#include <limits>
-#include <random>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/container/internal/have_sse.h"
-#include "absl/synchronization/blocking_counter.h"
-#include "absl/synchronization/internal/thread_pool.h"
-#include "absl/synchronization/mutex.h"
-#include "absl/synchronization/notification.h"
-#include "absl/time/clock.h"
-#include "absl/time/time.h"
-
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
-constexpr int kProbeLength = 16;
-#else
-constexpr int kProbeLength = 8;
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-class HashtablezInfoHandlePeer {
- public:
-  static bool IsSampled(const HashtablezInfoHandle& h) {
-    return h.info_ != nullptr;
-  }
-
-  static HashtablezInfo* GetInfo(HashtablezInfoHandle* h) { return h->info_; }
-};
-#else
-class HashtablezInfoHandlePeer {
- public:
-  static bool IsSampled(const HashtablezInfoHandle&) { return false; }
-  static HashtablezInfo* GetInfo(HashtablezInfoHandle*) { return nullptr; }
-};
-#endif  // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-
-namespace {
-using ::absl::synchronization_internal::ThreadPool;
-using ::testing::IsEmpty;
-using ::testing::UnorderedElementsAre;
-
-std::vector<size_t> GetSizes(HashtablezSampler* s) {
-  std::vector<size_t> res;
-  s->Iterate([&](const HashtablezInfo& info) {
-    res.push_back(info.size.load(std::memory_order_acquire));
-  });
-  return res;
-}
-
-HashtablezInfo* Register(HashtablezSampler* s, size_t size) {
-  auto* info = s->Register();
-  assert(info != nullptr);
-  info->size.store(size);
-  return info;
-}
-
-TEST(HashtablezInfoTest, PrepareForSampling) {
-  absl::Time test_start = absl::Now();
-  HashtablezInfo info;
-  absl::MutexLock l(&info.init_mu);
-  info.PrepareForSampling();
-
-  EXPECT_EQ(info.capacity.load(), 0);
-  EXPECT_EQ(info.size.load(), 0);
-  EXPECT_EQ(info.num_erases.load(), 0);
-  EXPECT_EQ(info.num_rehashes.load(), 0);
-  EXPECT_EQ(info.max_probe_length.load(), 0);
-  EXPECT_EQ(info.total_probe_length.load(), 0);
-  EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
-  EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
-  EXPECT_GE(info.create_time, test_start);
-
-  info.capacity.store(1, std::memory_order_relaxed);
-  info.size.store(1, std::memory_order_relaxed);
-  info.num_erases.store(1, std::memory_order_relaxed);
-  info.max_probe_length.store(1, std::memory_order_relaxed);
-  info.total_probe_length.store(1, std::memory_order_relaxed);
-  info.hashes_bitwise_or.store(1, std::memory_order_relaxed);
-  info.hashes_bitwise_and.store(1, std::memory_order_relaxed);
-  info.create_time = test_start - absl::Hours(20);
-
-  info.PrepareForSampling();
-  EXPECT_EQ(info.capacity.load(), 0);
-  EXPECT_EQ(info.size.load(), 0);
-  EXPECT_EQ(info.num_erases.load(), 0);
-  EXPECT_EQ(info.num_rehashes.load(), 0);
-  EXPECT_EQ(info.max_probe_length.load(), 0);
-  EXPECT_EQ(info.total_probe_length.load(), 0);
-  EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
-  EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
-  EXPECT_GE(info.create_time, test_start);
-}
-
-TEST(HashtablezInfoTest, RecordStorageChanged) {
-  HashtablezInfo info;
-  absl::MutexLock l(&info.init_mu);
-  info.PrepareForSampling();
-  RecordStorageChangedSlow(&info, 17, 47);
-  EXPECT_EQ(info.size.load(), 17);
-  EXPECT_EQ(info.capacity.load(), 47);
-  RecordStorageChangedSlow(&info, 20, 20);
-  EXPECT_EQ(info.size.load(), 20);
-  EXPECT_EQ(info.capacity.load(), 20);
-}
-
-TEST(HashtablezInfoTest, RecordInsert) {
-  HashtablezInfo info;
-  absl::MutexLock l(&info.init_mu);
-  info.PrepareForSampling();
-  EXPECT_EQ(info.max_probe_length.load(), 0);
-  RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
-  EXPECT_EQ(info.max_probe_length.load(), 6);
-  EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00);
-  EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00);
-  RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength);
-  EXPECT_EQ(info.max_probe_length.load(), 6);
-  EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000);
-  EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00);
-  RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength);
-  EXPECT_EQ(info.max_probe_length.load(), 12);
-  EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000);
-  EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00);
-}
-
-TEST(HashtablezInfoTest, RecordErase) {
-  HashtablezInfo info;
-  absl::MutexLock l(&info.init_mu);
-  info.PrepareForSampling();
-  EXPECT_EQ(info.num_erases.load(), 0);
-  EXPECT_EQ(info.size.load(), 0);
-  RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
-  EXPECT_EQ(info.size.load(), 1);
-  RecordEraseSlow(&info);
-  EXPECT_EQ(info.size.load(), 0);
-  EXPECT_EQ(info.num_erases.load(), 1);
-}
-
-TEST(HashtablezInfoTest, RecordRehash) {
-  HashtablezInfo info;
-  absl::MutexLock l(&info.init_mu);
-  info.PrepareForSampling();
-  RecordInsertSlow(&info, 0x1, 0);
-  RecordInsertSlow(&info, 0x2, kProbeLength);
-  RecordInsertSlow(&info, 0x4, kProbeLength);
-  RecordInsertSlow(&info, 0x8, 2 * kProbeLength);
-  EXPECT_EQ(info.size.load(), 4);
-  EXPECT_EQ(info.total_probe_length.load(), 4);
-
-  RecordEraseSlow(&info);
-  RecordEraseSlow(&info);
-  EXPECT_EQ(info.size.load(), 2);
-  EXPECT_EQ(info.total_probe_length.load(), 4);
-  EXPECT_EQ(info.num_erases.load(), 2);
-
-  RecordRehashSlow(&info, 3 * kProbeLength);
-  EXPECT_EQ(info.size.load(), 2);
-  EXPECT_EQ(info.total_probe_length.load(), 3);
-  EXPECT_EQ(info.num_erases.load(), 0);
-  EXPECT_EQ(info.num_rehashes.load(), 1);
-}
-
-#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-TEST(HashtablezSamplerTest, SmallSampleParameter) {
-  SetHashtablezEnabled(true);
-  SetHashtablezSampleParameter(100);
-
-  for (int i = 0; i < 1000; ++i) {
-    int64_t next_sample = 0;
-    HashtablezInfo* sample = SampleSlow(&next_sample);
-    EXPECT_GT(next_sample, 0);
-    EXPECT_NE(sample, nullptr);
-    UnsampleSlow(sample);
-  }
-}
-
-TEST(HashtablezSamplerTest, LargeSampleParameter) {
-  SetHashtablezEnabled(true);
-  SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
-
-  for (int i = 0; i < 1000; ++i) {
-    int64_t next_sample = 0;
-    HashtablezInfo* sample = SampleSlow(&next_sample);
-    EXPECT_GT(next_sample, 0);
-    EXPECT_NE(sample, nullptr);
-    UnsampleSlow(sample);
-  }
-}
-
-TEST(HashtablezSamplerTest, Sample) {
-  SetHashtablezEnabled(true);
-  SetHashtablezSampleParameter(100);
-  int64_t num_sampled = 0;
-  int64_t total = 0;
-  double sample_rate = 0.0;
-  for (int i = 0; i < 1000000; ++i) {
-    HashtablezInfoHandle h = Sample();
-    ++total;
-    if (HashtablezInfoHandlePeer::IsSampled(h)) {
-      ++num_sampled;
-    }
-    sample_rate = static_cast<double>(num_sampled) / total;
-    if (0.005 < sample_rate && sample_rate < 0.015) break;
-  }
-  EXPECT_NEAR(sample_rate, 0.01, 0.005);
-}
-
-TEST(HashtablezSamplerTest, Handle) {
-  auto& sampler = HashtablezSampler::Global();
-  HashtablezInfoHandle h(sampler.Register());
-  auto* info = HashtablezInfoHandlePeer::GetInfo(&h);
-  info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed);
-
-  bool found = false;
-  sampler.Iterate([&](const HashtablezInfo& h) {
-    if (&h == info) {
-      EXPECT_EQ(h.hashes_bitwise_and.load(), 0x12345678);
-      found = true;
-    }
-  });
-  EXPECT_TRUE(found);
-
-  h = HashtablezInfoHandle();
-  found = false;
-  sampler.Iterate([&](const HashtablezInfo& h) {
-    if (&h == info) {
-      // this will only happen if some other thread has resurrected the info
-      // the old handle was using.
-      if (h.hashes_bitwise_and.load() == 0x12345678) {
-        found = true;
-      }
-    }
-  });
-  EXPECT_FALSE(found);
-}
-#endif
-
-
-TEST(HashtablezSamplerTest, Registration) {
-  HashtablezSampler sampler;
-  auto* info1 = Register(&sampler, 1);
-  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1));
-
-  auto* info2 = Register(&sampler, 2);
-  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2));
-  info1->size.store(3);
-  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2));
-
-  sampler.Unregister(info1);
-  sampler.Unregister(info2);
-}
-
-TEST(HashtablezSamplerTest, Unregistration) {
-  HashtablezSampler sampler;
-  std::vector<HashtablezInfo*> infos;
-  for (size_t i = 0; i < 3; ++i) {
-    infos.push_back(Register(&sampler, i));
-  }
-  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2));
-
-  sampler.Unregister(infos[1]);
-  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2));
-
-  infos.push_back(Register(&sampler, 3));
-  infos.push_back(Register(&sampler, 4));
-  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4));
-  sampler.Unregister(infos[3]);
-  EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4));
-
-  sampler.Unregister(infos[0]);
-  sampler.Unregister(infos[2]);
-  sampler.Unregister(infos[4]);
-  EXPECT_THAT(GetSizes(&sampler), IsEmpty());
-}
-
-TEST(HashtablezSamplerTest, MultiThreaded) {
-  HashtablezSampler sampler;
-  Notification stop;
-  ThreadPool pool(10);
-
-  for (int i = 0; i < 10; ++i) {
-    pool.Schedule([&sampler, &stop]() {
-      std::random_device rd;
-      std::mt19937 gen(rd());
-
-      std::vector<HashtablezInfo*> infoz;
-      while (!stop.HasBeenNotified()) {
-        if (infoz.empty()) {
-          infoz.push_back(sampler.Register());
-        }
-        switch (std::uniform_int_distribution<>(0, 2)(gen)) {
-          case 0: {
-            infoz.push_back(sampler.Register());
-            break;
-          }
-          case 1: {
-            size_t p =
-                std::uniform_int_distribution<>(0, infoz.size() - 1)(gen);
-            HashtablezInfo* info = infoz[p];
-            infoz[p] = infoz.back();
-            infoz.pop_back();
-            sampler.Unregister(info);
-            break;
-          }
-          case 2: {
-            absl::Duration oldest = absl::ZeroDuration();
-            sampler.Iterate([&](const HashtablezInfo& info) {
-              oldest = std::max(oldest, absl::Now() - info.create_time);
-            });
-            ASSERT_GE(oldest, absl::ZeroDuration());
-            break;
-          }
-        }
-      }
-    });
-  }
-  // The threads will hammer away.  Give it a little bit of time for tsan to
-  // spot errors.
-  absl::SleepFor(absl::Seconds(3));
-  stop.Notify();
-}
-
-TEST(HashtablezSamplerTest, Callback) {
-  HashtablezSampler sampler;
-
-  auto* info1 = Register(&sampler, 1);
-  auto* info2 = Register(&sampler, 2);
-
-  static const HashtablezInfo* expected;
-
-  auto callback = [](const HashtablezInfo& info) {
-    // We can't use `info` outside of this callback because the object will be
-    // disposed as soon as we return from here.
-    EXPECT_EQ(&info, expected);
-  };
-
-  // Set the callback.
-  EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr);
-  expected = info1;
-  sampler.Unregister(info1);
-
-  // Unset the callback.
-  EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr));
-  expected = nullptr;  // no more calls.
-  sampler.Unregister(info2);
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/have_sse.h b/third_party/abseil_cpp/absl/container/internal/have_sse.h
deleted file mode 100644
index e75e1a16d3..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/have_sse.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Shared config probing for SSE instructions used in Swiss tables.
-#ifndef ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_
-#define ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_
-
-#ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
-#if defined(__SSE2__) ||  \
-    (defined(_MSC_VER) && \
-     (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2)))
-#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 1
-#else
-#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 0
-#endif
-#endif
-
-#ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3
-#ifdef __SSSE3__
-#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 1
-#else
-#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 0
-#endif
-#endif
-
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 && \
-    !ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
-#error "Bad configuration!"
-#endif
-
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
-#include <emmintrin.h>
-#endif
-
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3
-#include <tmmintrin.h>
-#endif
-
-#endif  // ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/inlined_vector.h b/third_party/abseil_cpp/absl/container/internal/inlined_vector.h
deleted file mode 100644
index c98c25c442..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/inlined_vector.h
+++ /dev/null
@@ -1,895 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_
-#define ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_
-
-#include <algorithm>
-#include <cstddef>
-#include <cstring>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <utility>
-
-#include "absl/base/macros.h"
-#include "absl/container/internal/compressed_tuple.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-#include "absl/types/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace inlined_vector_internal {
-
-template <typename Iterator>
-using IsAtLeastForwardIterator = std::is_convertible<
-    typename std::iterator_traits<Iterator>::iterator_category,
-    std::forward_iterator_tag>;
-
-template <typename AllocatorType,
-          typename ValueType =
-              typename absl::allocator_traits<AllocatorType>::value_type>
-using IsMemcpyOk =
-    absl::conjunction<std::is_same<AllocatorType, std::allocator<ValueType>>,
-                      absl::is_trivially_copy_constructible<ValueType>,
-                      absl::is_trivially_copy_assignable<ValueType>,
-                      absl::is_trivially_destructible<ValueType>>;
-
-template <typename AllocatorType, typename Pointer, typename SizeType>
-void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first,
-                     SizeType destroy_size) {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-
-  if (destroy_first != nullptr) {
-    for (auto i = destroy_size; i != 0;) {
-      --i;
-      AllocatorTraits::destroy(*alloc_ptr, destroy_first + i);
-    }
-
-#if !defined(NDEBUG)
-    {
-      using ValueType = typename AllocatorTraits::value_type;
-
-      // Overwrite unused memory with `0xab` so we can catch uninitialized
-      // usage.
-      //
-      // Cast to `void*` to tell the compiler that we don't care that we might
-      // be scribbling on a vtable pointer.
-      void* memory_ptr = destroy_first;
-      auto memory_size = destroy_size * sizeof(ValueType);
-      std::memset(memory_ptr, 0xab, memory_size);
-    }
-#endif  // !defined(NDEBUG)
-  }
-}
-
-template <typename AllocatorType, typename Pointer, typename ValueAdapter,
-          typename SizeType>
-void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first,
-                       ValueAdapter* values_ptr, SizeType construct_size) {
-  for (SizeType i = 0; i < construct_size; ++i) {
-    ABSL_INTERNAL_TRY {
-      values_ptr->ConstructNext(alloc_ptr, construct_first + i);
-    }
-    ABSL_INTERNAL_CATCH_ANY {
-      inlined_vector_internal::DestroyElements(alloc_ptr, construct_first, i);
-      ABSL_INTERNAL_RETHROW;
-    }
-  }
-}
-
-template <typename Pointer, typename ValueAdapter, typename SizeType>
-void AssignElements(Pointer assign_first, ValueAdapter* values_ptr,
-                    SizeType assign_size) {
-  for (SizeType i = 0; i < assign_size; ++i) {
-    values_ptr->AssignNext(assign_first + i);
-  }
-}
-
-template <typename AllocatorType>
-struct StorageView {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using Pointer = typename AllocatorTraits::pointer;
-  using SizeType = typename AllocatorTraits::size_type;
-
-  Pointer data;
-  SizeType size;
-  SizeType capacity;
-};
-
-template <typename AllocatorType, typename Iterator>
-class IteratorValueAdapter {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using Pointer = typename AllocatorTraits::pointer;
-
- public:
-  explicit IteratorValueAdapter(const Iterator& it) : it_(it) {}
-
-  void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) {
-    AllocatorTraits::construct(*alloc_ptr, construct_at, *it_);
-    ++it_;
-  }
-
-  void AssignNext(Pointer assign_at) {
-    *assign_at = *it_;
-    ++it_;
-  }
-
- private:
-  Iterator it_;
-};
-
-template <typename AllocatorType>
-class CopyValueAdapter {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using ValueType = typename AllocatorTraits::value_type;
-  using Pointer = typename AllocatorTraits::pointer;
-  using ConstPointer = typename AllocatorTraits::const_pointer;
-
- public:
-  explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {}
-
-  void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) {
-    AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_);
-  }
-
-  void AssignNext(Pointer assign_at) { *assign_at = *ptr_; }
-
- private:
-  ConstPointer ptr_;
-};
-
-template <typename AllocatorType>
-class DefaultValueAdapter {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using ValueType = typename AllocatorTraits::value_type;
-  using Pointer = typename AllocatorTraits::pointer;
-
- public:
-  explicit DefaultValueAdapter() {}
-
-  void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) {
-    AllocatorTraits::construct(*alloc_ptr, construct_at);
-  }
-
-  void AssignNext(Pointer assign_at) { *assign_at = ValueType(); }
-};
-
-template <typename AllocatorType>
-class AllocationTransaction {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using Pointer = typename AllocatorTraits::pointer;
-  using SizeType = typename AllocatorTraits::size_type;
-
- public:
-  explicit AllocationTransaction(AllocatorType* alloc_ptr)
-      : alloc_data_(*alloc_ptr, nullptr) {}
-
-  ~AllocationTransaction() {
-    if (DidAllocate()) {
-      AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity());
-    }
-  }
-
-  AllocationTransaction(const AllocationTransaction&) = delete;
-  void operator=(const AllocationTransaction&) = delete;
-
-  AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); }
-  Pointer& GetData() { return alloc_data_.template get<1>(); }
-  SizeType& GetCapacity() { return capacity_; }
-
-  bool DidAllocate() { return GetData() != nullptr; }
-  Pointer Allocate(SizeType capacity) {
-    GetData() = AllocatorTraits::allocate(GetAllocator(), capacity);
-    GetCapacity() = capacity;
-    return GetData();
-  }
-
-  void Reset() {
-    GetData() = nullptr;
-    GetCapacity() = 0;
-  }
-
- private:
-  container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_;
-  SizeType capacity_ = 0;
-};
-
-template <typename AllocatorType>
-class ConstructionTransaction {
-  using AllocatorTraits = absl::allocator_traits<AllocatorType>;
-  using Pointer = typename AllocatorTraits::pointer;
-  using SizeType = typename AllocatorTraits::size_type;
-
- public:
-  explicit ConstructionTransaction(AllocatorType* alloc_ptr)
-      : alloc_data_(*alloc_ptr, nullptr) {}
-
-  ~ConstructionTransaction() {
-    if (DidConstruct()) {
-      inlined_vector_internal::DestroyElements(std::addressof(GetAllocator()),
-                                               GetData(), GetSize());
-    }
-  }
-
-  ConstructionTransaction(const ConstructionTransaction&) = delete;
-  void operator=(const ConstructionTransaction&) = delete;
-
-  AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); }
-  Pointer& GetData() { return alloc_data_.template get<1>(); }
-  SizeType& GetSize() { return size_; }
-
-  bool DidConstruct() { return GetData() != nullptr; }
-  template <typename ValueAdapter>
-  void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) {
-    inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()),
-                                               data, values_ptr, size);
-    GetData() = data;
-    GetSize() = size;
-  }
-  void Commit() {
-    GetData() = nullptr;
-    GetSize() = 0;
-  }
-
- private:
-  container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_;
-  SizeType size_ = 0;
-};
-
-template <typename T, size_t N, typename A>
-class Storage {
- public:
-  using AllocatorTraits = absl::allocator_traits<A>;
-  using allocator_type = typename AllocatorTraits::allocator_type;
-  using value_type = typename AllocatorTraits::value_type;
-  using pointer = typename AllocatorTraits::pointer;
-  using const_pointer = typename AllocatorTraits::const_pointer;
-  using size_type = typename AllocatorTraits::size_type;
-  using difference_type = typename AllocatorTraits::difference_type;
-
-  using reference = value_type&;
-  using const_reference = const value_type&;
-  using RValueReference = value_type&&;
-  using iterator = pointer;
-  using const_iterator = const_pointer;
-  using reverse_iterator = std::reverse_iterator<iterator>;
-  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-  using MoveIterator = std::move_iterator<iterator>;
-  using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<allocator_type>;
-
-  using StorageView = inlined_vector_internal::StorageView<allocator_type>;
-
-  template <typename Iterator>
-  using IteratorValueAdapter =
-      inlined_vector_internal::IteratorValueAdapter<allocator_type, Iterator>;
-  using CopyValueAdapter =
-      inlined_vector_internal::CopyValueAdapter<allocator_type>;
-  using DefaultValueAdapter =
-      inlined_vector_internal::DefaultValueAdapter<allocator_type>;
-
-  using AllocationTransaction =
-      inlined_vector_internal::AllocationTransaction<allocator_type>;
-  using ConstructionTransaction =
-      inlined_vector_internal::ConstructionTransaction<allocator_type>;
-
-  static size_type NextCapacity(size_type current_capacity) {
-    return current_capacity * 2;
-  }
-
-  static size_type ComputeCapacity(size_type current_capacity,
-                                   size_type requested_capacity) {
-    return (std::max)(NextCapacity(current_capacity), requested_capacity);
-  }
-
-  // ---------------------------------------------------------------------------
-  // Storage Constructors and Destructor
-  // ---------------------------------------------------------------------------
-
-  Storage() : metadata_() {}
-
-  explicit Storage(const allocator_type& alloc) : metadata_(alloc, {}) {}
-
-  ~Storage() {
-    pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
-    inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize());
-    DeallocateIfAllocated();
-  }
-
-  // ---------------------------------------------------------------------------
-  // Storage Member Accessors
-  // ---------------------------------------------------------------------------
-
-  size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); }
-
-  const size_type& GetSizeAndIsAllocated() const {
-    return metadata_.template get<1>();
-  }
-
-  size_type GetSize() const { return GetSizeAndIsAllocated() >> 1; }
-
-  bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; }
-
-  pointer GetAllocatedData() { return data_.allocated.allocated_data; }
-
-  const_pointer GetAllocatedData() const {
-    return data_.allocated.allocated_data;
-  }
-
-  pointer GetInlinedData() {
-    return reinterpret_cast<pointer>(
-        std::addressof(data_.inlined.inlined_data[0]));
-  }
-
-  const_pointer GetInlinedData() const {
-    return reinterpret_cast<const_pointer>(
-        std::addressof(data_.inlined.inlined_data[0]));
-  }
-
-  size_type GetAllocatedCapacity() const {
-    return data_.allocated.allocated_capacity;
-  }
-
-  size_type GetInlinedCapacity() const { return static_cast<size_type>(N); }
-
-  StorageView MakeStorageView() {
-    return GetIsAllocated()
-               ? StorageView{GetAllocatedData(), GetSize(),
-                             GetAllocatedCapacity()}
-               : StorageView{GetInlinedData(), GetSize(), GetInlinedCapacity()};
-  }
-
-  allocator_type* GetAllocPtr() {
-    return std::addressof(metadata_.template get<0>());
-  }
-
-  const allocator_type* GetAllocPtr() const {
-    return std::addressof(metadata_.template get<0>());
-  }
-
-  // ---------------------------------------------------------------------------
-  // Storage Member Mutators
-  // ---------------------------------------------------------------------------
-
-  template <typename ValueAdapter>
-  void Initialize(ValueAdapter values, size_type new_size);
-
-  template <typename ValueAdapter>
-  void Assign(ValueAdapter values, size_type new_size);
-
-  template <typename ValueAdapter>
-  void Resize(ValueAdapter values, size_type new_size);
-
-  template <typename ValueAdapter>
-  iterator Insert(const_iterator pos, ValueAdapter values,
-                  size_type insert_count);
-
-  template <typename... Args>
-  reference EmplaceBack(Args&&... args);
-
-  iterator Erase(const_iterator from, const_iterator to);
-
-  void Reserve(size_type requested_capacity);
-
-  void ShrinkToFit();
-
-  void Swap(Storage* other_storage_ptr);
-
-  void SetIsAllocated() {
-    GetSizeAndIsAllocated() |= static_cast<size_type>(1);
-  }
-
-  void UnsetIsAllocated() {
-    GetSizeAndIsAllocated() &= ((std::numeric_limits<size_type>::max)() - 1);
-  }
-
-  void SetSize(size_type size) {
-    GetSizeAndIsAllocated() =
-        (size << 1) | static_cast<size_type>(GetIsAllocated());
-  }
-
-  void SetAllocatedSize(size_type size) {
-    GetSizeAndIsAllocated() = (size << 1) | static_cast<size_type>(1);
-  }
-
-  void SetInlinedSize(size_type size) {
-    GetSizeAndIsAllocated() = size << static_cast<size_type>(1);
-  }
-
-  void AddSize(size_type count) {
-    GetSizeAndIsAllocated() += count << static_cast<size_type>(1);
-  }
-
-  void SubtractSize(size_type count) {
-    assert(count <= GetSize());
-
-    GetSizeAndIsAllocated() -= count << static_cast<size_type>(1);
-  }
-
-  void SetAllocatedData(pointer data, size_type capacity) {
-    data_.allocated.allocated_data = data;
-    data_.allocated.allocated_capacity = capacity;
-  }
-
-  void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) {
-    SetAllocatedData(allocation_tx_ptr->GetData(),
-                     allocation_tx_ptr->GetCapacity());
-
-    allocation_tx_ptr->Reset();
-  }
-
-  void MemcpyFrom(const Storage& other_storage) {
-    assert(IsMemcpyOk::value || other_storage.GetIsAllocated());
-
-    GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated();
-    data_ = other_storage.data_;
-  }
-
-  void DeallocateIfAllocated() {
-    if (GetIsAllocated()) {
-      AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(),
-                                  GetAllocatedCapacity());
-    }
-  }
-
- private:
-  using Metadata =
-      container_internal::CompressedTuple<allocator_type, size_type>;
-
-  struct Allocated {
-    pointer allocated_data;
-    size_type allocated_capacity;
-  };
-
-  struct Inlined {
-    alignas(value_type) char inlined_data[sizeof(value_type[N])];
-  };
-
-  union Data {
-    Allocated allocated;
-    Inlined inlined;
-  };
-
-  template <typename... Args>
-  ABSL_ATTRIBUTE_NOINLINE reference EmplaceBackSlow(Args&&... args);
-
-  Metadata metadata_;
-  Data data_;
-};
-
-template <typename T, size_t N, typename A>
-template <typename ValueAdapter>
-auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size)
-    -> void {
-  // Only callable from constructors!
-  assert(!GetIsAllocated());
-  assert(GetSize() == 0);
-
-  pointer construct_data;
-  if (new_size > GetInlinedCapacity()) {
-    // Because this is only called from the `InlinedVector` constructors, it's
-    // safe to take on the allocation with size `0`. If `ConstructElements(...)`
-    // throws, deallocation will be automatically handled by `~Storage()`.
-    size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size);
-    construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity);
-    SetAllocatedData(construct_data, new_capacity);
-    SetIsAllocated();
-  } else {
-    construct_data = GetInlinedData();
-  }
-
-  inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data,
-                                             &values, new_size);
-
-  // Since the initial size was guaranteed to be `0` and the allocated bit is
-  // already correct for either case, *adding* `new_size` gives us the correct
-  // result faster than setting it directly.
-  AddSize(new_size);
-}
-
-template <typename T, size_t N, typename A>
-template <typename ValueAdapter>
-auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void {
-  StorageView storage_view = MakeStorageView();
-
-  AllocationTransaction allocation_tx(GetAllocPtr());
-
-  absl::Span<value_type> assign_loop;
-  absl::Span<value_type> construct_loop;
-  absl::Span<value_type> destroy_loop;
-
-  if (new_size > storage_view.capacity) {
-    size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size);
-    construct_loop = {allocation_tx.Allocate(new_capacity), new_size};
-    destroy_loop = {storage_view.data, storage_view.size};
-  } else if (new_size > storage_view.size) {
-    assign_loop = {storage_view.data, storage_view.size};
-    construct_loop = {storage_view.data + storage_view.size,
-                      new_size - storage_view.size};
-  } else {
-    assign_loop = {storage_view.data, new_size};
-    destroy_loop = {storage_view.data + new_size, storage_view.size - new_size};
-  }
-
-  inlined_vector_internal::AssignElements(assign_loop.data(), &values,
-                                          assign_loop.size());
-
-  inlined_vector_internal::ConstructElements(
-      GetAllocPtr(), construct_loop.data(), &values, construct_loop.size());
-
-  inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(),
-                                           destroy_loop.size());
-
-  if (allocation_tx.DidAllocate()) {
-    DeallocateIfAllocated();
-    AcquireAllocatedData(&allocation_tx);
-    SetIsAllocated();
-  }
-
-  SetSize(new_size);
-}
-
-template <typename T, size_t N, typename A>
-template <typename ValueAdapter>
-auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void {
-  StorageView storage_view = MakeStorageView();
-  auto* const base = storage_view.data;
-  const size_type size = storage_view.size;
-  auto* alloc = GetAllocPtr();
-  if (new_size <= size) {
-    // Destroy extra old elements.
-    inlined_vector_internal::DestroyElements(alloc, base + new_size,
-                                             size - new_size);
-  } else if (new_size <= storage_view.capacity) {
-    // Construct new elements in place.
-    inlined_vector_internal::ConstructElements(alloc, base + size, &values,
-                                               new_size - size);
-  } else {
-    // Steps:
-    //  a. Allocate new backing store.
-    //  b. Construct new elements in new backing store.
-    //  c. Move existing elements from old backing store to now.
-    //  d. Destroy all elements in old backing store.
-    // Use transactional wrappers for the first two steps so we can roll
-    // back if necessary due to exceptions.
-    AllocationTransaction allocation_tx(alloc);
-    size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size);
-    pointer new_data = allocation_tx.Allocate(new_capacity);
-
-    ConstructionTransaction construction_tx(alloc);
-    construction_tx.Construct(new_data + size, &values, new_size - size);
-
-    IteratorValueAdapter<MoveIterator> move_values((MoveIterator(base)));
-    inlined_vector_internal::ConstructElements(alloc, new_data, &move_values,
-                                               size);
-
-    inlined_vector_internal::DestroyElements(alloc, base, size);
-    construction_tx.Commit();
-    DeallocateIfAllocated();
-    AcquireAllocatedData(&allocation_tx);
-    SetIsAllocated();
-  }
-  SetSize(new_size);
-}
-
-template <typename T, size_t N, typename A>
-template <typename ValueAdapter>
-auto Storage<T, N, A>::Insert(const_iterator pos, ValueAdapter values,
-                              size_type insert_count) -> iterator {
-  StorageView storage_view = MakeStorageView();
-
-  size_type insert_index =
-      std::distance(const_iterator(storage_view.data), pos);
-  size_type insert_end_index = insert_index + insert_count;
-  size_type new_size = storage_view.size + insert_count;
-
-  if (new_size > storage_view.capacity) {
-    AllocationTransaction allocation_tx(GetAllocPtr());
-    ConstructionTransaction construction_tx(GetAllocPtr());
-    ConstructionTransaction move_construciton_tx(GetAllocPtr());
-
-    IteratorValueAdapter<MoveIterator> move_values(
-        MoveIterator(storage_view.data));
-
-    size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size);
-    pointer new_data = allocation_tx.Allocate(new_capacity);
-
-    construction_tx.Construct(new_data + insert_index, &values, insert_count);
-
-    move_construciton_tx.Construct(new_data, &move_values, insert_index);
-
-    inlined_vector_internal::ConstructElements(
-        GetAllocPtr(), new_data + insert_end_index, &move_values,
-        storage_view.size - insert_index);
-
-    inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
-                                             storage_view.size);
-
-    construction_tx.Commit();
-    move_construciton_tx.Commit();
-    DeallocateIfAllocated();
-    AcquireAllocatedData(&allocation_tx);
-
-    SetAllocatedSize(new_size);
-    return iterator(new_data + insert_index);
-  } else {
-    size_type move_construction_destination_index =
-        (std::max)(insert_end_index, storage_view.size);
-
-    ConstructionTransaction move_construction_tx(GetAllocPtr());
-
-    IteratorValueAdapter<MoveIterator> move_construction_values(
-        MoveIterator(storage_view.data +
-                     (move_construction_destination_index - insert_count)));
-    absl::Span<value_type> move_construction = {
-        storage_view.data + move_construction_destination_index,
-        new_size - move_construction_destination_index};
-
-    pointer move_assignment_values = storage_view.data + insert_index;
-    absl::Span<value_type> move_assignment = {
-        storage_view.data + insert_end_index,
-        move_construction_destination_index - insert_end_index};
-
-    absl::Span<value_type> insert_assignment = {move_assignment_values,
-                                                move_construction.size()};
-
-    absl::Span<value_type> insert_construction = {
-        insert_assignment.data() + insert_assignment.size(),
-        insert_count - insert_assignment.size()};
-
-    move_construction_tx.Construct(move_construction.data(),
-                                   &move_construction_values,
-                                   move_construction.size());
-
-    for (pointer destination = move_assignment.data() + move_assignment.size(),
-                 last_destination = move_assignment.data(),
-                 source = move_assignment_values + move_assignment.size();
-         ;) {
-      --destination;
-      --source;
-      if (destination < last_destination) break;
-      *destination = std::move(*source);
-    }
-
-    inlined_vector_internal::AssignElements(insert_assignment.data(), &values,
-                                            insert_assignment.size());
-
-    inlined_vector_internal::ConstructElements(
-        GetAllocPtr(), insert_construction.data(), &values,
-        insert_construction.size());
-
-    move_construction_tx.Commit();
-
-    AddSize(insert_count);
-    return iterator(storage_view.data + insert_index);
-  }
-}
-
-template <typename T, size_t N, typename A>
-template <typename... Args>
-auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference {
-  StorageView storage_view = MakeStorageView();
-  const auto n = storage_view.size;
-  if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) {
-    // Fast path; new element fits.
-    pointer last_ptr = storage_view.data + n;
-    AllocatorTraits::construct(*GetAllocPtr(), last_ptr,
-                               std::forward<Args>(args)...);
-    AddSize(1);
-    return *last_ptr;
-  }
-  // TODO(b/173712035): Annotate with musttail attribute to prevent regression.
-  return EmplaceBackSlow(std::forward<Args>(args)...);
-}
-
-template <typename T, size_t N, typename A>
-template <typename... Args>
-auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> reference {
-  StorageView storage_view = MakeStorageView();
-  AllocationTransaction allocation_tx(GetAllocPtr());
-  IteratorValueAdapter<MoveIterator> move_values(
-      MoveIterator(storage_view.data));
-  size_type new_capacity = NextCapacity(storage_view.capacity);
-  pointer construct_data = allocation_tx.Allocate(new_capacity);
-  pointer last_ptr = construct_data + storage_view.size;
-
-  // Construct new element.
-  AllocatorTraits::construct(*GetAllocPtr(), last_ptr,
-                             std::forward<Args>(args)...);
-  // Move elements from old backing store to new backing store.
-  ABSL_INTERNAL_TRY {
-    inlined_vector_internal::ConstructElements(
-        GetAllocPtr(), allocation_tx.GetData(), &move_values,
-        storage_view.size);
-  }
-  ABSL_INTERNAL_CATCH_ANY {
-    AllocatorTraits::destroy(*GetAllocPtr(), last_ptr);
-    ABSL_INTERNAL_RETHROW;
-  }
-  // Destroy elements in old backing store.
-  inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
-                                           storage_view.size);
-
-  DeallocateIfAllocated();
-  AcquireAllocatedData(&allocation_tx);
-  SetIsAllocated();
-  AddSize(1);
-  return *last_ptr;
-}
-
-template <typename T, size_t N, typename A>
-auto Storage<T, N, A>::Erase(const_iterator from, const_iterator to)
-    -> iterator {
-  StorageView storage_view = MakeStorageView();
-
-  size_type erase_size = std::distance(from, to);
-  size_type erase_index =
-      std::distance(const_iterator(storage_view.data), from);
-  size_type erase_end_index = erase_index + erase_size;
-
-  IteratorValueAdapter<MoveIterator> move_values(
-      MoveIterator(storage_view.data + erase_end_index));
-
-  inlined_vector_internal::AssignElements(storage_view.data + erase_index,
-                                          &move_values,
-                                          storage_view.size - erase_end_index);
-
-  inlined_vector_internal::DestroyElements(
-      GetAllocPtr(), storage_view.data + (storage_view.size - erase_size),
-      erase_size);
-
-  SubtractSize(erase_size);
-  return iterator(storage_view.data + erase_index);
-}
-
-template <typename T, size_t N, typename A>
-auto Storage<T, N, A>::Reserve(size_type requested_capacity) -> void {
-  StorageView storage_view = MakeStorageView();
-
-  if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return;
-
-  AllocationTransaction allocation_tx(GetAllocPtr());
-
-  IteratorValueAdapter<MoveIterator> move_values(
-      MoveIterator(storage_view.data));
-
-  size_type new_capacity =
-      ComputeCapacity(storage_view.capacity, requested_capacity);
-  pointer new_data = allocation_tx.Allocate(new_capacity);
-
-  inlined_vector_internal::ConstructElements(GetAllocPtr(), new_data,
-                                             &move_values, storage_view.size);
-
-  inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
-                                           storage_view.size);
-
-  DeallocateIfAllocated();
-  AcquireAllocatedData(&allocation_tx);
-  SetIsAllocated();
-}
-
-template <typename T, size_t N, typename A>
-auto Storage<T, N, A>::ShrinkToFit() -> void {
-  // May only be called on allocated instances!
-  assert(GetIsAllocated());
-
-  StorageView storage_view{GetAllocatedData(), GetSize(),
-                           GetAllocatedCapacity()};
-
-  if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return;
-
-  AllocationTransaction allocation_tx(GetAllocPtr());
-
-  IteratorValueAdapter<MoveIterator> move_values(
-      MoveIterator(storage_view.data));
-
-  pointer construct_data;
-  if (storage_view.size > GetInlinedCapacity()) {
-    size_type new_capacity = storage_view.size;
-    construct_data = allocation_tx.Allocate(new_capacity);
-  } else {
-    construct_data = GetInlinedData();
-  }
-
-  ABSL_INTERNAL_TRY {
-    inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data,
-                                               &move_values, storage_view.size);
-  }
-  ABSL_INTERNAL_CATCH_ANY {
-    SetAllocatedData(storage_view.data, storage_view.capacity);
-    ABSL_INTERNAL_RETHROW;
-  }
-
-  inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
-                                           storage_view.size);
-
-  AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data,
-                              storage_view.capacity);
-
-  if (allocation_tx.DidAllocate()) {
-    AcquireAllocatedData(&allocation_tx);
-  } else {
-    UnsetIsAllocated();
-  }
-}
-
-template <typename T, size_t N, typename A>
-auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void {
-  using std::swap;
-  assert(this != other_storage_ptr);
-
-  if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) {
-    swap(data_.allocated, other_storage_ptr->data_.allocated);
-  } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) {
-    Storage* small_ptr = this;
-    Storage* large_ptr = other_storage_ptr;
-    if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr);
-
-    for (size_type i = 0; i < small_ptr->GetSize(); ++i) {
-      swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]);
-    }
-
-    IteratorValueAdapter<MoveIterator> move_values(
-        MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize()));
-
-    inlined_vector_internal::ConstructElements(
-        large_ptr->GetAllocPtr(),
-        small_ptr->GetInlinedData() + small_ptr->GetSize(), &move_values,
-        large_ptr->GetSize() - small_ptr->GetSize());
-
-    inlined_vector_internal::DestroyElements(
-        large_ptr->GetAllocPtr(),
-        large_ptr->GetInlinedData() + small_ptr->GetSize(),
-        large_ptr->GetSize() - small_ptr->GetSize());
-  } else {
-    Storage* allocated_ptr = this;
-    Storage* inlined_ptr = other_storage_ptr;
-    if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr);
-
-    StorageView allocated_storage_view{allocated_ptr->GetAllocatedData(),
-                                       allocated_ptr->GetSize(),
-                                       allocated_ptr->GetAllocatedCapacity()};
-
-    IteratorValueAdapter<MoveIterator> move_values(
-        MoveIterator(inlined_ptr->GetInlinedData()));
-
-    ABSL_INTERNAL_TRY {
-      inlined_vector_internal::ConstructElements(
-          inlined_ptr->GetAllocPtr(), allocated_ptr->GetInlinedData(),
-          &move_values, inlined_ptr->GetSize());
-    }
-    ABSL_INTERNAL_CATCH_ANY {
-      allocated_ptr->SetAllocatedData(allocated_storage_view.data,
-                                      allocated_storage_view.capacity);
-      ABSL_INTERNAL_RETHROW;
-    }
-
-    inlined_vector_internal::DestroyElements(inlined_ptr->GetAllocPtr(),
-                                             inlined_ptr->GetInlinedData(),
-                                             inlined_ptr->GetSize());
-
-    inlined_ptr->SetAllocatedData(allocated_storage_view.data,
-                                  allocated_storage_view.capacity);
-  }
-
-  swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated());
-  swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr());
-}
-
-}  // namespace inlined_vector_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/layout.h b/third_party/abseil_cpp/absl/container/internal/layout.h
deleted file mode 100644
index 2336783315..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/layout.h
+++ /dev/null
@@ -1,743 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//                           MOTIVATION AND TUTORIAL
-//
-// If you want to put in a single heap allocation N doubles followed by M ints,
-// it's easy if N and M are known at compile time.
-//
-//   struct S {
-//     double a[N];
-//     int b[M];
-//   };
-//
-//   S* p = new S;
-//
-// But what if N and M are known only in run time? Class template Layout to the
-// rescue! It's a portable generalization of the technique known as struct hack.
-//
-//   // This object will tell us everything we need to know about the memory
-//   // layout of double[N] followed by int[M]. It's structurally identical to
-//   // size_t[2] that stores N and M. It's very cheap to create.
-//   const Layout<double, int> layout(N, M);
-//
-//   // Allocate enough memory for both arrays. `AllocSize()` tells us how much
-//   // memory is needed. We are free to use any allocation function we want as
-//   // long as it returns aligned memory.
-//   std::unique_ptr<unsigned char[]> p(new unsigned char[layout.AllocSize()]);
-//
-//   // Obtain the pointer to the array of doubles.
-//   // Equivalent to `reinterpret_cast<double*>(p.get())`.
-//   //
-//   // We could have written layout.Pointer<0>(p) instead. If all the types are
-//   // unique you can use either form, but if some types are repeated you must
-//   // use the index form.
-//   double* a = layout.Pointer<double>(p.get());
-//
-//   // Obtain the pointer to the array of ints.
-//   // Equivalent to `reinterpret_cast<int*>(p.get() + N * 8)`.
-//   int* b = layout.Pointer<int>(p);
-//
-// If we are unable to specify sizes of all fields, we can pass as many sizes as
-// we can to `Partial()`. In return, it'll allow us to access the fields whose
-// locations and sizes can be computed from the provided information.
-// `Partial()` comes in handy when the array sizes are embedded into the
-// allocation.
-//
-//   // size_t[1] containing N, size_t[1] containing M, double[N], int[M].
-//   using L = Layout<size_t, size_t, double, int>;
-//
-//   unsigned char* Allocate(size_t n, size_t m) {
-//     const L layout(1, 1, n, m);
-//     unsigned char* p = new unsigned char[layout.AllocSize()];
-//     *layout.Pointer<0>(p) = n;
-//     *layout.Pointer<1>(p) = m;
-//     return p;
-//   }
-//
-//   void Use(unsigned char* p) {
-//     // First, extract N and M.
-//     // Specify that the first array has only one element. Using `prefix` we
-//     // can access the first two arrays but not more.
-//     constexpr auto prefix = L::Partial(1);
-//     size_t n = *prefix.Pointer<0>(p);
-//     size_t m = *prefix.Pointer<1>(p);
-//
-//     // Now we can get pointers to the payload.
-//     const L layout(1, 1, n, m);
-//     double* a = layout.Pointer<double>(p);
-//     int* b = layout.Pointer<int>(p);
-//   }
-//
-// The layout we used above combines fixed-size with dynamically-sized fields.
-// This is quite common. Layout is optimized for this use case and generates
-// optimal code. All computations that can be performed at compile time are
-// indeed performed at compile time.
-//
-// Efficiency tip: The order of fields matters. In `Layout<T1, ..., TN>` try to
-// ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no
-// padding in between arrays.
-//
-// You can manually override the alignment of an array by wrapping the type in
-// `Aligned<T, N>`. `Layout<..., Aligned<T, N>, ...>` has exactly the same API
-// and behavior as `Layout<..., T, ...>` except that the first element of the
-// array of `T` is aligned to `N` (the rest of the elements follow without
-// padding). `N` cannot be less than `alignof(T)`.
-//
-// `AllocSize()` and `Pointer()` are the most basic methods for dealing with
-// memory layouts. Check out the reference or code below to discover more.
-//
-//                            EXAMPLE
-//
-//   // Immutable move-only string with sizeof equal to sizeof(void*). The
-//   // string size and the characters are kept in the same heap allocation.
-//   class CompactString {
-//    public:
-//     CompactString(const char* s = "") {
-//       const size_t size = strlen(s);
-//       // size_t[1] followed by char[size + 1].
-//       const L layout(1, size + 1);
-//       p_.reset(new unsigned char[layout.AllocSize()]);
-//       // If running under ASAN, mark the padding bytes, if any, to catch
-//       // memory errors.
-//       layout.PoisonPadding(p_.get());
-//       // Store the size in the allocation.
-//       *layout.Pointer<size_t>(p_.get()) = size;
-//       // Store the characters in the allocation.
-//       memcpy(layout.Pointer<char>(p_.get()), s, size + 1);
-//     }
-//
-//     size_t size() const {
-//       // Equivalent to reinterpret_cast<size_t&>(*p).
-//       return *L::Partial().Pointer<size_t>(p_.get());
-//     }
-//
-//     const char* c_str() const {
-//       // Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)).
-//       // The argument in Partial(1) specifies that we have size_t[1] in front
-//       // of the characters.
-//       return L::Partial(1).Pointer<char>(p_.get());
-//     }
-//
-//    private:
-//     // Our heap allocation contains a size_t followed by an array of chars.
-//     using L = Layout<size_t, char>;
-//     std::unique_ptr<unsigned char[]> p_;
-//   };
-//
-//   int main() {
-//     CompactString s = "hello";
-//     assert(s.size() == 5);
-//     assert(strcmp(s.c_str(), "hello") == 0);
-//   }
-//
-//                               DOCUMENTATION
-//
-// The interface exported by this file consists of:
-// - class `Layout<>` and its public members.
-// - The public members of class `internal_layout::LayoutImpl<>`. That class
-//   isn't intended to be used directly, and its name and template parameter
-//   list are internal implementation details, but the class itself provides
-//   most of the functionality in this file. See comments on its members for
-//   detailed documentation.
-//
-// `Layout<T1,... Tn>::Partial(count1,..., countm)` (where `m` <= `n`) returns a
-// `LayoutImpl<>` object. `Layout<T1,..., Tn> layout(count1,..., countn)`
-// creates a `Layout` object, which exposes the same functionality by inheriting
-// from `LayoutImpl<>`.
-
-#ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_
-#define ABSL_CONTAINER_INTERNAL_LAYOUT_H_
-
-#include <assert.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include <ostream>
-#include <string>
-#include <tuple>
-#include <type_traits>
-#include <typeinfo>
-#include <utility>
-
-#include "absl/base/config.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/str_cat.h"
-#include "absl/types/span.h"
-#include "absl/utility/utility.h"
-
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-#include <sanitizer/asan_interface.h>
-#endif
-
-#if defined(__GXX_RTTI)
-#define ABSL_INTERNAL_HAS_CXA_DEMANGLE
-#endif
-
-#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
-#include <cxxabi.h>
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// A type wrapper that instructs `Layout` to use the specific alignment for the
-// array. `Layout<..., Aligned<T, N>, ...>` has exactly the same API
-// and behavior as `Layout<..., T, ...>` except that the first element of the
-// array of `T` is aligned to `N` (the rest of the elements follow without
-// padding).
-//
-// Requires: `N >= alignof(T)` and `N` is a power of 2.
-template <class T, size_t N>
-struct Aligned;
-
-namespace internal_layout {
-
-template <class T>
-struct NotAligned {};
-
-template <class T, size_t N>
-struct NotAligned<const Aligned<T, N>> {
-  static_assert(sizeof(T) == 0, "Aligned<T, N> cannot be const-qualified");
-};
-
-template <size_t>
-using IntToSize = size_t;
-
-template <class>
-using TypeToSize = size_t;
-
-template <class T>
-struct Type : NotAligned<T> {
-  using type = T;
-};
-
-template <class T, size_t N>
-struct Type<Aligned<T, N>> {
-  using type = T;
-};
-
-template <class T>
-struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> {};
-
-template <class T, size_t N>
-struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {};
-
-// Note: workaround for https://gcc.gnu.org/PR88115
-template <class T>
-struct AlignOf : NotAligned<T> {
-  static constexpr size_t value = alignof(T);
-};
-
-template <class T, size_t N>
-struct AlignOf<Aligned<T, N>> {
-  static_assert(N % alignof(T) == 0,
-                "Custom alignment can't be lower than the type's alignment");
-  static constexpr size_t value = N;
-};
-
-// Does `Ts...` contain `T`?
-template <class T, class... Ts>
-using Contains = absl::disjunction<std::is_same<T, Ts>...>;
-
-template <class From, class To>
-using CopyConst =
-    typename std::conditional<std::is_const<From>::value, const To, To>::type;
-
-// Note: We're not qualifying this with absl:: because it doesn't compile under
-// MSVC.
-template <class T>
-using SliceType = Span<T>;
-
-// This namespace contains no types. It prevents functions defined in it from
-// being found by ADL.
-namespace adl_barrier {
-
-template <class Needle, class... Ts>
-constexpr size_t Find(Needle, Needle, Ts...) {
-  static_assert(!Contains<Needle, Ts...>(), "Duplicate element type");
-  return 0;
-}
-
-template <class Needle, class T, class... Ts>
-constexpr size_t Find(Needle, T, Ts...) {
-  return adl_barrier::Find(Needle(), Ts()...) + 1;
-}
-
-constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); }
-
-// Returns `q * m` for the smallest `q` such that `q * m >= n`.
-// Requires: `m` is a power of two. It's enforced by IsLegalElementType below.
-constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); }
-
-constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; }
-
-constexpr size_t Max(size_t a) { return a; }
-
-template <class... Ts>
-constexpr size_t Max(size_t a, size_t b, Ts... rest) {
-  return adl_barrier::Max(b < a ? a : b, rest...);
-}
-
-template <class T>
-std::string TypeName() {
-  std::string out;
-  int status = 0;
-  char* demangled = nullptr;
-#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
-  demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
-#endif
-  if (status == 0 && demangled != nullptr) {  // Demangling succeeded.
-    absl::StrAppend(&out, "<", demangled, ">");
-    free(demangled);
-  } else {
-#if defined(__GXX_RTTI) || defined(_CPPRTTI)
-    absl::StrAppend(&out, "<", typeid(T).name(), ">");
-#endif
-  }
-  return out;
-}
-
-}  // namespace adl_barrier
-
-template <bool C>
-using EnableIf = typename std::enable_if<C, int>::type;
-
-// Can `T` be a template argument of `Layout`?
-template <class T>
-using IsLegalElementType = std::integral_constant<
-    bool, !std::is_reference<T>::value && !std::is_volatile<T>::value &&
-              !std::is_reference<typename Type<T>::type>::value &&
-              !std::is_volatile<typename Type<T>::type>::value &&
-              adl_barrier::IsPow2(AlignOf<T>::value)>;
-
-template <class Elements, class SizeSeq, class OffsetSeq>
-class LayoutImpl;
-
-// Public base class of `Layout` and the result type of `Layout::Partial()`.
-//
-// `Elements...` contains all template arguments of `Layout` that created this
-// instance.
-//
-// `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments
-// passed to `Layout::Partial()` or `Layout::Layout()`.
-//
-// `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is
-// `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we
-// can compute offsets).
-template <class... Elements, size_t... SizeSeq, size_t... OffsetSeq>
-class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>,
-                 absl::index_sequence<OffsetSeq...>> {
- private:
-  static_assert(sizeof...(Elements) > 0, "At least one field is required");
-  static_assert(absl::conjunction<IsLegalElementType<Elements>...>::value,
-                "Invalid element type (see IsLegalElementType)");
-
-  enum {
-    NumTypes = sizeof...(Elements),
-    NumSizes = sizeof...(SizeSeq),
-    NumOffsets = sizeof...(OffsetSeq),
-  };
-
-  // These are guaranteed by `Layout`.
-  static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1),
-                "Internal error");
-  static_assert(NumTypes > 0, "Internal error");
-
-  // Returns the index of `T` in `Elements...`. Results in a compilation error
-  // if `Elements...` doesn't contain exactly one instance of `T`.
-  template <class T>
-  static constexpr size_t ElementIndex() {
-    static_assert(Contains<Type<T>, Type<typename Type<Elements>::type>...>(),
-                  "Type not found");
-    return adl_barrier::Find(Type<T>(),
-                             Type<typename Type<Elements>::type>()...);
-  }
-
-  template <size_t N>
-  using ElementAlignment =
-      AlignOf<typename std::tuple_element<N, std::tuple<Elements...>>::type>;
-
- public:
-  // Element types of all arrays packed in a tuple.
-  using ElementTypes = std::tuple<typename Type<Elements>::type...>;
-
-  // Element type of the Nth array.
-  template <size_t N>
-  using ElementType = typename std::tuple_element<N, ElementTypes>::type;
-
-  constexpr explicit LayoutImpl(IntToSize<SizeSeq>... sizes)
-      : size_{sizes...} {}
-
-  // Alignment of the layout, equal to the strictest alignment of all elements.
-  // All pointers passed to the methods of layout must be aligned to this value.
-  static constexpr size_t Alignment() {
-    return adl_barrier::Max(AlignOf<Elements>::value...);
-  }
-
-  // Offset in bytes of the Nth array.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   assert(x.Offset<0>() == 0);   // The ints starts from 0.
-  //   assert(x.Offset<1>() == 16);  // The doubles starts from 16.
-  //
-  // Requires: `N <= NumSizes && N < sizeof...(Ts)`.
-  template <size_t N, EnableIf<N == 0> = 0>
-  constexpr size_t Offset() const {
-    return 0;
-  }
-
-  template <size_t N, EnableIf<N != 0> = 0>
-  constexpr size_t Offset() const {
-    static_assert(N < NumOffsets, "Index out of bounds");
-    return adl_barrier::Align(
-        Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1],
-        ElementAlignment<N>::value);
-  }
-
-  // Offset in bytes of the array with the specified element type. There must
-  // be exactly one such array and its zero-based index must be at most
-  // `NumSizes`.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   assert(x.Offset<int>() == 0);      // The ints starts from 0.
-  //   assert(x.Offset<double>() == 16);  // The doubles starts from 16.
-  template <class T>
-  constexpr size_t Offset() const {
-    return Offset<ElementIndex<T>()>();
-  }
-
-  // Offsets in bytes of all arrays for which the offsets are known.
-  constexpr std::array<size_t, NumOffsets> Offsets() const {
-    return {{Offset<OffsetSeq>()...}};
-  }
-
-  // The number of elements in the Nth array. This is the Nth argument of
-  // `Layout::Partial()` or `Layout::Layout()` (zero-based).
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   assert(x.Size<0>() == 3);
-  //   assert(x.Size<1>() == 4);
-  //
-  // Requires: `N < NumSizes`.
-  template <size_t N>
-  constexpr size_t Size() const {
-    static_assert(N < NumSizes, "Index out of bounds");
-    return size_[N];
-  }
-
-  // The number of elements in the array with the specified element type.
-  // There must be exactly one such array and its zero-based index must be
-  // at most `NumSizes`.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   assert(x.Size<int>() == 3);
-  //   assert(x.Size<double>() == 4);
-  template <class T>
-  constexpr size_t Size() const {
-    return Size<ElementIndex<T>()>();
-  }
-
-  // The number of elements of all arrays for which they are known.
-  constexpr std::array<size_t, NumSizes> Sizes() const {
-    return {{Size<SizeSeq>()...}};
-  }
-
-  // Pointer to the beginning of the Nth array.
-  //
-  // `Char` must be `[const] [signed|unsigned] char`.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   unsigned char* p = new unsigned char[x.AllocSize()];
-  //   int* ints = x.Pointer<0>(p);
-  //   double* doubles = x.Pointer<1>(p);
-  //
-  // Requires: `N <= NumSizes && N < sizeof...(Ts)`.
-  // Requires: `p` is aligned to `Alignment()`.
-  template <size_t N, class Char>
-  CopyConst<Char, ElementType<N>>* Pointer(Char* p) const {
-    using C = typename std::remove_const<Char>::type;
-    static_assert(
-        std::is_same<C, char>() || std::is_same<C, unsigned char>() ||
-            std::is_same<C, signed char>(),
-        "The argument must be a pointer to [const] [signed|unsigned] char");
-    constexpr size_t alignment = Alignment();
-    (void)alignment;
-    assert(reinterpret_cast<uintptr_t>(p) % alignment == 0);
-    return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>());
-  }
-
-  // Pointer to the beginning of the array with the specified element type.
-  // There must be exactly one such array and its zero-based index must be at
-  // most `NumSizes`.
-  //
-  // `Char` must be `[const] [signed|unsigned] char`.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   unsigned char* p = new unsigned char[x.AllocSize()];
-  //   int* ints = x.Pointer<int>(p);
-  //   double* doubles = x.Pointer<double>(p);
-  //
-  // Requires: `p` is aligned to `Alignment()`.
-  template <class T, class Char>
-  CopyConst<Char, T>* Pointer(Char* p) const {
-    return Pointer<ElementIndex<T>()>(p);
-  }
-
-  // Pointers to all arrays for which pointers are known.
-  //
-  // `Char` must be `[const] [signed|unsigned] char`.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   unsigned char* p = new unsigned char[x.AllocSize()];
-  //
-  //   int* ints;
-  //   double* doubles;
-  //   std::tie(ints, doubles) = x.Pointers(p);
-  //
-  // Requires: `p` is aligned to `Alignment()`.
-  //
-  // Note: We're not using ElementType alias here because it does not compile
-  // under MSVC.
-  template <class Char>
-  std::tuple<CopyConst<
-      Char, typename std::tuple_element<OffsetSeq, ElementTypes>::type>*...>
-  Pointers(Char* p) const {
-    return std::tuple<CopyConst<Char, ElementType<OffsetSeq>>*...>(
-        Pointer<OffsetSeq>(p)...);
-  }
-
-  // The Nth array.
-  //
-  // `Char` must be `[const] [signed|unsigned] char`.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   unsigned char* p = new unsigned char[x.AllocSize()];
-  //   Span<int> ints = x.Slice<0>(p);
-  //   Span<double> doubles = x.Slice<1>(p);
-  //
-  // Requires: `N < NumSizes`.
-  // Requires: `p` is aligned to `Alignment()`.
-  template <size_t N, class Char>
-  SliceType<CopyConst<Char, ElementType<N>>> Slice(Char* p) const {
-    return SliceType<CopyConst<Char, ElementType<N>>>(Pointer<N>(p), Size<N>());
-  }
-
-  // The array with the specified element type. There must be exactly one
-  // such array and its zero-based index must be less than `NumSizes`.
-  //
-  // `Char` must be `[const] [signed|unsigned] char`.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   unsigned char* p = new unsigned char[x.AllocSize()];
-  //   Span<int> ints = x.Slice<int>(p);
-  //   Span<double> doubles = x.Slice<double>(p);
-  //
-  // Requires: `p` is aligned to `Alignment()`.
-  template <class T, class Char>
-  SliceType<CopyConst<Char, T>> Slice(Char* p) const {
-    return Slice<ElementIndex<T>()>(p);
-  }
-
-  // All arrays with known sizes.
-  //
-  // `Char` must be `[const] [signed|unsigned] char`.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   unsigned char* p = new unsigned char[x.AllocSize()];
-  //
-  //   Span<int> ints;
-  //   Span<double> doubles;
-  //   std::tie(ints, doubles) = x.Slices(p);
-  //
-  // Requires: `p` is aligned to `Alignment()`.
-  //
-  // Note: We're not using ElementType alias here because it does not compile
-  // under MSVC.
-  template <class Char>
-  std::tuple<SliceType<CopyConst<
-      Char, typename std::tuple_element<SizeSeq, ElementTypes>::type>>...>
-  Slices(Char* p) const {
-    // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63875 (fixed
-    // in 6.1).
-    (void)p;
-    return std::tuple<SliceType<CopyConst<Char, ElementType<SizeSeq>>>...>(
-        Slice<SizeSeq>(p)...);
-  }
-
-  // The size of the allocation that fits all arrays.
-  //
-  //   // int[3], 4 bytes of padding, double[4].
-  //   Layout<int, double> x(3, 4);
-  //   unsigned char* p = new unsigned char[x.AllocSize()];  // 48 bytes
-  //
-  // Requires: `NumSizes == sizeof...(Ts)`.
-  constexpr size_t AllocSize() const {
-    static_assert(NumTypes == NumSizes, "You must specify sizes of all fields");
-    return Offset<NumTypes - 1>() +
-           SizeOf<ElementType<NumTypes - 1>>() * size_[NumTypes - 1];
-  }
-
-  // If built with --config=asan, poisons padding bytes (if any) in the
-  // allocation. The pointer must point to a memory block at least
-  // `AllocSize()` bytes in length.
-  //
-  // `Char` must be `[const] [signed|unsigned] char`.
-  //
-  // Requires: `p` is aligned to `Alignment()`.
-  template <class Char, size_t N = NumOffsets - 1, EnableIf<N == 0> = 0>
-  void PoisonPadding(const Char* p) const {
-    Pointer<0>(p);  // verify the requirements on `Char` and `p`
-  }
-
-  template <class Char, size_t N = NumOffsets - 1, EnableIf<N != 0> = 0>
-  void PoisonPadding(const Char* p) const {
-    static_assert(N < NumOffsets, "Index out of bounds");
-    (void)p;
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-    PoisonPadding<Char, N - 1>(p);
-    // The `if` is an optimization. It doesn't affect the observable behaviour.
-    if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) {
-      size_t start =
-          Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1];
-      ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start);
-    }
-#endif
-  }
-
-  // Human-readable description of the memory layout. Useful for debugging.
-  // Slow.
-  //
-  //   // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed
-  //   // by an unknown number of doubles.
-  //   auto x = Layout<char, int, double>::Partial(5, 3);
-  //   assert(x.DebugString() ==
-  //          "@0<char>(1)[5]; @8<int>(4)[3]; @24<double>(8)");
-  //
-  // Each field is in the following format: @offset<type>(sizeof)[size] (<type>
-  // may be missing depending on the target platform). For example,
-  // @8<int>(4)[3] means that at offset 8 we have an array of ints, where each
-  // int is 4 bytes, and we have 3 of those ints. The size of the last field may
-  // be missing (as in the example above). Only fields with known offsets are
-  // described. Type names may differ across platforms: one compiler might
-  // produce "unsigned*" where another produces "unsigned int *".
-  std::string DebugString() const {
-    const auto offsets = Offsets();
-    const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>()...};
-    const std::string types[] = {
-        adl_barrier::TypeName<ElementType<OffsetSeq>>()...};
-    std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")");
-    for (size_t i = 0; i != NumOffsets - 1; ++i) {
-      absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1],
-                      "(", sizes[i + 1], ")");
-    }
-    // NumSizes is a constant that may be zero. Some compilers cannot see that
-    // inside the if statement "size_[NumSizes - 1]" must be valid.
-    int last = static_cast<int>(NumSizes) - 1;
-    if (NumTypes == NumSizes && last >= 0) {
-      absl::StrAppend(&res, "[", size_[last], "]");
-    }
-    return res;
-  }
-
- private:
-  // Arguments of `Layout::Partial()` or `Layout::Layout()`.
-  size_t size_[NumSizes > 0 ? NumSizes : 1];
-};
-
-template <size_t NumSizes, class... Ts>
-using LayoutType = LayoutImpl<
-    std::tuple<Ts...>, absl::make_index_sequence<NumSizes>,
-    absl::make_index_sequence<adl_barrier::Min(sizeof...(Ts), NumSizes + 1)>>;
-
-}  // namespace internal_layout
-
-// Descriptor of arrays of various types and sizes laid out in memory one after
-// another. See the top of the file for documentation.
-//
-// Check out the public API of internal_layout::LayoutImpl above. The type is
-// internal to the library but its methods are public, and they are inherited
-// by `Layout`.
-template <class... Ts>
-class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> {
- public:
-  static_assert(sizeof...(Ts) > 0, "At least one field is required");
-  static_assert(
-      absl::conjunction<internal_layout::IsLegalElementType<Ts>...>::value,
-      "Invalid element type (see IsLegalElementType)");
-
-  // The result type of `Partial()` with `NumSizes` arguments.
-  template <size_t NumSizes>
-  using PartialType = internal_layout::LayoutType<NumSizes, Ts...>;
-
-  // `Layout` knows the element types of the arrays we want to lay out in
-  // memory but not the number of elements in each array.
-  // `Partial(size1, ..., sizeN)` allows us to specify the latter. The
-  // resulting immutable object can be used to obtain pointers to the
-  // individual arrays.
-  //
-  // It's allowed to pass fewer array sizes than the number of arrays. E.g.,
-  // if all you need is to the offset of the second array, you only need to
-  // pass one argument -- the number of elements in the first array.
-  //
-  //   // int[3] followed by 4 bytes of padding and an unknown number of
-  //   // doubles.
-  //   auto x = Layout<int, double>::Partial(3);
-  //   // doubles start at byte 16.
-  //   assert(x.Offset<1>() == 16);
-  //
-  // If you know the number of elements in all arrays, you can still call
-  // `Partial()` but it's more convenient to use the constructor of `Layout`.
-  //
-  //   Layout<int, double> x(3, 5);
-  //
-  // Note: The sizes of the arrays must be specified in number of elements,
-  // not in bytes.
-  //
-  // Requires: `sizeof...(Sizes) <= sizeof...(Ts)`.
-  // Requires: all arguments are convertible to `size_t`.
-  template <class... Sizes>
-  static constexpr PartialType<sizeof...(Sizes)> Partial(Sizes&&... sizes) {
-    static_assert(sizeof...(Sizes) <= sizeof...(Ts), "");
-    return PartialType<sizeof...(Sizes)>(absl::forward<Sizes>(sizes)...);
-  }
-
-  // Creates a layout with the sizes of all arrays specified. If you know
-  // only the sizes of the first N arrays (where N can be zero), you can use
-  // `Partial()` defined above. The constructor is essentially equivalent to
-  // calling `Partial()` and passing in all array sizes; the constructor is
-  // provided as a convenient abbreviation.
-  //
-  // Note: The sizes of the arrays must be specified in number of elements,
-  // not in bytes.
-  constexpr explicit Layout(internal_layout::TypeToSize<Ts>... sizes)
-      : internal_layout::LayoutType<sizeof...(Ts), Ts...>(sizes...) {}
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_LAYOUT_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/layout_test.cc b/third_party/abseil_cpp/absl/container/internal/layout_test.cc
deleted file mode 100644
index 1d7158ffc0..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/layout_test.cc
+++ /dev/null
@@ -1,1635 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/layout.h"
-
-// We need ::max_align_t because some libstdc++ versions don't provide
-// std::max_align_t
-#include <stddef.h>
-
-#include <cstdint>
-#include <memory>
-#include <sstream>
-#include <type_traits>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/types/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using ::absl::Span;
-using ::testing::ElementsAre;
-
-size_t Distance(const void* from, const void* to) {
-  ABSL_RAW_CHECK(from <= to, "Distance must be non-negative");
-  return static_cast<const char*>(to) - static_cast<const char*>(from);
-}
-
-template <class Expected, class Actual>
-Expected Type(Actual val) {
-  static_assert(std::is_same<Expected, Actual>(), "");
-  return val;
-}
-
-// Helper classes to test different size and alignments.
-struct alignas(8) Int128 {
-  uint64_t a, b;
-  friend bool operator==(Int128 lhs, Int128 rhs) {
-    return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b);
-  }
-
-  static std::string Name() {
-    return internal_layout::adl_barrier::TypeName<Int128>();
-  }
-};
-
-// int64_t is *not* 8-byte aligned on all platforms!
-struct alignas(8) Int64 {
-  int64_t a;
-  friend bool operator==(Int64 lhs, Int64 rhs) {
-    return lhs.a == rhs.a;
-  }
-};
-
-// Properties of types that this test relies on.
-static_assert(sizeof(int8_t) == 1, "");
-static_assert(alignof(int8_t) == 1, "");
-static_assert(sizeof(int16_t) == 2, "");
-static_assert(alignof(int16_t) == 2, "");
-static_assert(sizeof(int32_t) == 4, "");
-static_assert(alignof(int32_t) == 4, "");
-static_assert(sizeof(Int64) == 8, "");
-static_assert(alignof(Int64) == 8, "");
-static_assert(sizeof(Int128) == 16, "");
-static_assert(alignof(Int128) == 8, "");
-
-template <class Expected, class Actual>
-void SameType() {
-  static_assert(std::is_same<Expected, Actual>(), "");
-}
-
-TEST(Layout, ElementType) {
-  {
-    using L = Layout<int32_t>;
-    SameType<int32_t, L::ElementType<0>>();
-    SameType<int32_t, decltype(L::Partial())::ElementType<0>>();
-    SameType<int32_t, decltype(L::Partial(0))::ElementType<0>>();
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    SameType<int32_t, L::ElementType<0>>();
-    SameType<int32_t, L::ElementType<1>>();
-    SameType<int32_t, decltype(L::Partial())::ElementType<0>>();
-    SameType<int32_t, decltype(L::Partial())::ElementType<1>>();
-    SameType<int32_t, decltype(L::Partial(0))::ElementType<0>>();
-    SameType<int32_t, decltype(L::Partial(0))::ElementType<1>>();
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    SameType<int8_t, L::ElementType<0>>();
-    SameType<int32_t, L::ElementType<1>>();
-    SameType<Int128, L::ElementType<2>>();
-    SameType<int8_t, decltype(L::Partial())::ElementType<0>>();
-    SameType<int8_t, decltype(L::Partial(0))::ElementType<0>>();
-    SameType<int32_t, decltype(L::Partial(0))::ElementType<1>>();
-    SameType<int8_t, decltype(L::Partial(0, 0))::ElementType<0>>();
-    SameType<int32_t, decltype(L::Partial(0, 0))::ElementType<1>>();
-    SameType<Int128, decltype(L::Partial(0, 0))::ElementType<2>>();
-    SameType<int8_t, decltype(L::Partial(0, 0, 0))::ElementType<0>>();
-    SameType<int32_t, decltype(L::Partial(0, 0, 0))::ElementType<1>>();
-    SameType<Int128, decltype(L::Partial(0, 0, 0))::ElementType<2>>();
-  }
-}
-
-TEST(Layout, ElementTypes) {
-  {
-    using L = Layout<int32_t>;
-    SameType<std::tuple<int32_t>, L::ElementTypes>();
-    SameType<std::tuple<int32_t>, decltype(L::Partial())::ElementTypes>();
-    SameType<std::tuple<int32_t>, decltype(L::Partial(0))::ElementTypes>();
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    SameType<std::tuple<int32_t, int32_t>, L::ElementTypes>();
-    SameType<std::tuple<int32_t, int32_t>,
-             decltype(L::Partial())::ElementTypes>();
-    SameType<std::tuple<int32_t, int32_t>,
-             decltype(L::Partial(0))::ElementTypes>();
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    SameType<std::tuple<int8_t, int32_t, Int128>, L::ElementTypes>();
-    SameType<std::tuple<int8_t, int32_t, Int128>,
-             decltype(L::Partial())::ElementTypes>();
-    SameType<std::tuple<int8_t, int32_t, Int128>,
-             decltype(L::Partial(0))::ElementTypes>();
-    SameType<std::tuple<int8_t, int32_t, Int128>,
-             decltype(L::Partial(0, 0))::ElementTypes>();
-    SameType<std::tuple<int8_t, int32_t, Int128>,
-             decltype(L::Partial(0, 0, 0))::ElementTypes>();
-  }
-}
-
-TEST(Layout, OffsetByIndex) {
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, L::Partial().Offset<0>());
-    EXPECT_EQ(0, L::Partial(3).Offset<0>());
-    EXPECT_EQ(0, L(3).Offset<0>());
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_EQ(0, L::Partial().Offset<0>());
-    EXPECT_EQ(0, L::Partial(3).Offset<0>());
-    EXPECT_EQ(12, L::Partial(3).Offset<1>());
-    EXPECT_EQ(0, L::Partial(3, 5).Offset<0>());
-    EXPECT_EQ(12, L::Partial(3, 5).Offset<1>());
-    EXPECT_EQ(0, L(3, 5).Offset<0>());
-    EXPECT_EQ(12, L(3, 5).Offset<1>());
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(0, L::Partial().Offset<0>());
-    EXPECT_EQ(0, L::Partial(0).Offset<0>());
-    EXPECT_EQ(0, L::Partial(0).Offset<1>());
-    EXPECT_EQ(0, L::Partial(1).Offset<0>());
-    EXPECT_EQ(4, L::Partial(1).Offset<1>());
-    EXPECT_EQ(0, L::Partial(5).Offset<0>());
-    EXPECT_EQ(8, L::Partial(5).Offset<1>());
-    EXPECT_EQ(0, L::Partial(0, 0).Offset<0>());
-    EXPECT_EQ(0, L::Partial(0, 0).Offset<1>());
-    EXPECT_EQ(0, L::Partial(0, 0).Offset<2>());
-    EXPECT_EQ(0, L::Partial(1, 0).Offset<0>());
-    EXPECT_EQ(4, L::Partial(1, 0).Offset<1>());
-    EXPECT_EQ(8, L::Partial(1, 0).Offset<2>());
-    EXPECT_EQ(0, L::Partial(5, 3).Offset<0>());
-    EXPECT_EQ(8, L::Partial(5, 3).Offset<1>());
-    EXPECT_EQ(24, L::Partial(5, 3).Offset<2>());
-    EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<0>());
-    EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<1>());
-    EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<2>());
-    EXPECT_EQ(0, L::Partial(1, 0, 0).Offset<0>());
-    EXPECT_EQ(4, L::Partial(1, 0, 0).Offset<1>());
-    EXPECT_EQ(8, L::Partial(1, 0, 0).Offset<2>());
-    EXPECT_EQ(0, L::Partial(5, 3, 1).Offset<0>());
-    EXPECT_EQ(24, L::Partial(5, 3, 1).Offset<2>());
-    EXPECT_EQ(8, L::Partial(5, 3, 1).Offset<1>());
-    EXPECT_EQ(0, L(5, 3, 1).Offset<0>());
-    EXPECT_EQ(24, L(5, 3, 1).Offset<2>());
-    EXPECT_EQ(8, L(5, 3, 1).Offset<1>());
-  }
-}
-
-TEST(Layout, OffsetByType) {
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, L::Partial().Offset<int32_t>());
-    EXPECT_EQ(0, L::Partial(3).Offset<int32_t>());
-    EXPECT_EQ(0, L(3).Offset<int32_t>());
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(0, L::Partial().Offset<int8_t>());
-    EXPECT_EQ(0, L::Partial(0).Offset<int8_t>());
-    EXPECT_EQ(0, L::Partial(0).Offset<int32_t>());
-    EXPECT_EQ(0, L::Partial(1).Offset<int8_t>());
-    EXPECT_EQ(4, L::Partial(1).Offset<int32_t>());
-    EXPECT_EQ(0, L::Partial(5).Offset<int8_t>());
-    EXPECT_EQ(8, L::Partial(5).Offset<int32_t>());
-    EXPECT_EQ(0, L::Partial(0, 0).Offset<int8_t>());
-    EXPECT_EQ(0, L::Partial(0, 0).Offset<int32_t>());
-    EXPECT_EQ(0, L::Partial(0, 0).Offset<Int128>());
-    EXPECT_EQ(0, L::Partial(1, 0).Offset<int8_t>());
-    EXPECT_EQ(4, L::Partial(1, 0).Offset<int32_t>());
-    EXPECT_EQ(8, L::Partial(1, 0).Offset<Int128>());
-    EXPECT_EQ(0, L::Partial(5, 3).Offset<int8_t>());
-    EXPECT_EQ(8, L::Partial(5, 3).Offset<int32_t>());
-    EXPECT_EQ(24, L::Partial(5, 3).Offset<Int128>());
-    EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<int8_t>());
-    EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<int32_t>());
-    EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<Int128>());
-    EXPECT_EQ(0, L::Partial(1, 0, 0).Offset<int8_t>());
-    EXPECT_EQ(4, L::Partial(1, 0, 0).Offset<int32_t>());
-    EXPECT_EQ(8, L::Partial(1, 0, 0).Offset<Int128>());
-    EXPECT_EQ(0, L::Partial(5, 3, 1).Offset<int8_t>());
-    EXPECT_EQ(24, L::Partial(5, 3, 1).Offset<Int128>());
-    EXPECT_EQ(8, L::Partial(5, 3, 1).Offset<int32_t>());
-    EXPECT_EQ(0, L(5, 3, 1).Offset<int8_t>());
-    EXPECT_EQ(24, L(5, 3, 1).Offset<Int128>());
-    EXPECT_EQ(8, L(5, 3, 1).Offset<int32_t>());
-  }
-}
-
-TEST(Layout, Offsets) {
-  {
-    using L = Layout<int32_t>;
-    EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0));
-    EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0));
-    EXPECT_THAT(L(3).Offsets(), ElementsAre(0));
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0));
-    EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0, 12));
-    EXPECT_THAT(L::Partial(3, 5).Offsets(), ElementsAre(0, 12));
-    EXPECT_THAT(L(3, 5).Offsets(), ElementsAre(0, 12));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0));
-    EXPECT_THAT(L::Partial(1).Offsets(), ElementsAre(0, 4));
-    EXPECT_THAT(L::Partial(5).Offsets(), ElementsAre(0, 8));
-    EXPECT_THAT(L::Partial(0, 0).Offsets(), ElementsAre(0, 0, 0));
-    EXPECT_THAT(L::Partial(1, 0).Offsets(), ElementsAre(0, 4, 8));
-    EXPECT_THAT(L::Partial(5, 3).Offsets(), ElementsAre(0, 8, 24));
-    EXPECT_THAT(L::Partial(0, 0, 0).Offsets(), ElementsAre(0, 0, 0));
-    EXPECT_THAT(L::Partial(1, 0, 0).Offsets(), ElementsAre(0, 4, 8));
-    EXPECT_THAT(L::Partial(5, 3, 1).Offsets(), ElementsAre(0, 8, 24));
-    EXPECT_THAT(L(5, 3, 1).Offsets(), ElementsAre(0, 8, 24));
-  }
-}
-
-TEST(Layout, AllocSize) {
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, L::Partial(0).AllocSize());
-    EXPECT_EQ(12, L::Partial(3).AllocSize());
-    EXPECT_EQ(12, L(3).AllocSize());
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_EQ(32, L::Partial(3, 5).AllocSize());
-    EXPECT_EQ(32, L(3, 5).AllocSize());
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(0, L::Partial(0, 0, 0).AllocSize());
-    EXPECT_EQ(8, L::Partial(1, 0, 0).AllocSize());
-    EXPECT_EQ(8, L::Partial(0, 1, 0).AllocSize());
-    EXPECT_EQ(16, L::Partial(0, 0, 1).AllocSize());
-    EXPECT_EQ(24, L::Partial(1, 1, 1).AllocSize());
-    EXPECT_EQ(136, L::Partial(3, 5, 7).AllocSize());
-    EXPECT_EQ(136, L(3, 5, 7).AllocSize());
-  }
-}
-
-TEST(Layout, SizeByIndex) {
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, L::Partial(0).Size<0>());
-    EXPECT_EQ(3, L::Partial(3).Size<0>());
-    EXPECT_EQ(3, L(3).Size<0>());
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_EQ(0, L::Partial(0).Size<0>());
-    EXPECT_EQ(3, L::Partial(3).Size<0>());
-    EXPECT_EQ(3, L::Partial(3, 5).Size<0>());
-    EXPECT_EQ(5, L::Partial(3, 5).Size<1>());
-    EXPECT_EQ(3, L(3, 5).Size<0>());
-    EXPECT_EQ(5, L(3, 5).Size<1>());
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(3, L::Partial(3).Size<0>());
-    EXPECT_EQ(3, L::Partial(3, 5).Size<0>());
-    EXPECT_EQ(5, L::Partial(3, 5).Size<1>());
-    EXPECT_EQ(3, L::Partial(3, 5, 7).Size<0>());
-    EXPECT_EQ(5, L::Partial(3, 5, 7).Size<1>());
-    EXPECT_EQ(7, L::Partial(3, 5, 7).Size<2>());
-    EXPECT_EQ(3, L(3, 5, 7).Size<0>());
-    EXPECT_EQ(5, L(3, 5, 7).Size<1>());
-    EXPECT_EQ(7, L(3, 5, 7).Size<2>());
-  }
-}
-
-TEST(Layout, SizeByType) {
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, L::Partial(0).Size<int32_t>());
-    EXPECT_EQ(3, L::Partial(3).Size<int32_t>());
-    EXPECT_EQ(3, L(3).Size<int32_t>());
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(3, L::Partial(3).Size<int8_t>());
-    EXPECT_EQ(3, L::Partial(3, 5).Size<int8_t>());
-    EXPECT_EQ(5, L::Partial(3, 5).Size<int32_t>());
-    EXPECT_EQ(3, L::Partial(3, 5, 7).Size<int8_t>());
-    EXPECT_EQ(5, L::Partial(3, 5, 7).Size<int32_t>());
-    EXPECT_EQ(7, L::Partial(3, 5, 7).Size<Int128>());
-    EXPECT_EQ(3, L(3, 5, 7).Size<int8_t>());
-    EXPECT_EQ(5, L(3, 5, 7).Size<int32_t>());
-    EXPECT_EQ(7, L(3, 5, 7).Size<Int128>());
-  }
-}
-
-TEST(Layout, Sizes) {
-  {
-    using L = Layout<int32_t>;
-    EXPECT_THAT(L::Partial().Sizes(), ElementsAre());
-    EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3));
-    EXPECT_THAT(L(3).Sizes(), ElementsAre(3));
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_THAT(L::Partial().Sizes(), ElementsAre());
-    EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3));
-    EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5));
-    EXPECT_THAT(L(3, 5).Sizes(), ElementsAre(3, 5));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_THAT(L::Partial().Sizes(), ElementsAre());
-    EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3));
-    EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5));
-    EXPECT_THAT(L::Partial(3, 5, 7).Sizes(), ElementsAre(3, 5, 7));
-    EXPECT_THAT(L(3, 5, 7).Sizes(), ElementsAre(3, 5, 7));
-  }
-}
-
-TEST(Layout, PointerByIndex) {
-  alignas(max_align_t) const unsigned char p[100] = {};
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<0>(p))));
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
-    EXPECT_EQ(12,
-              Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<1>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<0>(p))));
-    EXPECT_EQ(
-        12, Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<0>(p))));
-    EXPECT_EQ(12, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<1>(p))));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<0>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<0>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<0>(p))));
-    EXPECT_EQ(4,
-              Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<0>(p))));
-    EXPECT_EQ(8,
-              Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<1>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<0>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<1>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<2>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<0>(p))));
-    EXPECT_EQ(
-        4, Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<1>(p))));
-    EXPECT_EQ(8,
-              Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<2>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<0>(p))));
-    EXPECT_EQ(
-        8, Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<1>(p))));
-    EXPECT_EQ(24,
-              Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<2>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<const int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p))));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<const Int128*>(L::Partial(0, 0, 0).Pointer<2>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<const int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p))));
-    EXPECT_EQ(
-        4,
-        Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
-    EXPECT_EQ(
-        8, Distance(p, Type<const Int128*>(L::Partial(1, 0, 0).Pointer<2>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<const int8_t*>(L::Partial(5, 3, 1).Pointer<0>(p))));
-    EXPECT_EQ(
-        24,
-        Distance(p, Type<const Int128*>(L::Partial(5, 3, 1).Pointer<2>(p))));
-    EXPECT_EQ(
-        8,
-        Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L(5, 3, 1).Pointer<0>(p))));
-    EXPECT_EQ(24, Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<2>(p))));
-    EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<1>(p))));
-  }
-}
-
-TEST(Layout, PointerByType) {
-  alignas(max_align_t) const unsigned char p[100] = {};
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(
-        0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<int32_t>(p))));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<int32_t>(p))));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(
-        0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        4,
-        Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        8,
-        Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int32_t*>(
-                                 L::Partial(0, 0).Pointer<int32_t>(p))));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<Int128>(p))));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
-    EXPECT_EQ(4, Distance(p, Type<const int32_t*>(
-                                 L::Partial(1, 0).Pointer<int32_t>(p))));
-    EXPECT_EQ(
-        8,
-        Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<Int128>(p))));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
-    EXPECT_EQ(8, Distance(p, Type<const int32_t*>(
-                                 L::Partial(5, 3).Pointer<int32_t>(p))));
-    EXPECT_EQ(
-        24,
-        Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<Int128>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int8_t*>(
-                                 L::Partial(0, 0, 0).Pointer<int8_t>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int32_t*>(
-                                 L::Partial(0, 0, 0).Pointer<int32_t>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const Int128*>(
-                                 L::Partial(0, 0, 0).Pointer<Int128>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int8_t*>(
-                                 L::Partial(1, 0, 0).Pointer<int8_t>(p))));
-    EXPECT_EQ(4, Distance(p, Type<const int32_t*>(
-                                 L::Partial(1, 0, 0).Pointer<int32_t>(p))));
-    EXPECT_EQ(8, Distance(p, Type<const Int128*>(
-                                 L::Partial(1, 0, 0).Pointer<Int128>(p))));
-    EXPECT_EQ(0, Distance(p, Type<const int8_t*>(
-                                 L::Partial(5, 3, 1).Pointer<int8_t>(p))));
-    EXPECT_EQ(24, Distance(p, Type<const Int128*>(
-                                  L::Partial(5, 3, 1).Pointer<Int128>(p))));
-    EXPECT_EQ(8, Distance(p, Type<const int32_t*>(
-                                 L::Partial(5, 3, 1).Pointer<int32_t>(p))));
-    EXPECT_EQ(24,
-              Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<Int128>(p))));
-    EXPECT_EQ(
-        8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<int32_t>(p))));
-  }
-}
-
-TEST(Layout, MutablePointerByIndex) {
-  alignas(max_align_t) unsigned char p[100];
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<0>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<0>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3).Pointer<0>(p))));
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<0>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<0>(p))));
-    EXPECT_EQ(12, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3, 5).Pointer<0>(p))));
-    EXPECT_EQ(12, Distance(p, Type<int32_t*>(L::Partial(3, 5).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3, 5).Pointer<0>(p))));
-    EXPECT_EQ(12, Distance(p, Type<int32_t*>(L(3, 5).Pointer<1>(p))));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial().Pointer<0>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0).Pointer<0>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1).Pointer<0>(p))));
-    EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5).Pointer<0>(p))));
-    EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<0>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<Int128*>(L::Partial(0, 0).Pointer<2>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<0>(p))));
-    EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<1>(p))));
-    EXPECT_EQ(8, Distance(p, Type<Int128*>(L::Partial(1, 0).Pointer<2>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<0>(p))));
-    EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<1>(p))));
-    EXPECT_EQ(24, Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<2>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<2>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p))));
-    EXPECT_EQ(4,
-              Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
-    EXPECT_EQ(8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<2>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<0>(p))));
-    EXPECT_EQ(24,
-              Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<2>(p))));
-    EXPECT_EQ(8,
-              Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<0>(p))));
-    EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<2>(p))));
-    EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<1>(p))));
-  }
-}
-
-TEST(Layout, MutablePointerByType) {
-  alignas(max_align_t) unsigned char p[100];
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<int32_t>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3).Pointer<int32_t>(p))));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial().Pointer<int8_t>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0).Pointer<int8_t>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1).Pointer<int8_t>(p))));
-    EXPECT_EQ(4,
-              Distance(p, Type<int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5).Pointer<int8_t>(p))));
-    EXPECT_EQ(8,
-              Distance(p, Type<int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<Int128*>(L::Partial(0, 0).Pointer<Int128>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p))));
-    EXPECT_EQ(8,
-              Distance(p, Type<Int128*>(L::Partial(1, 0).Pointer<Int128>(p))));
-    EXPECT_EQ(0,
-              Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p))));
-    EXPECT_EQ(24,
-              Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<Int128>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<Int128>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        4,
-        Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p))));
-    EXPECT_EQ(
-        8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<Int128>(p))));
-    EXPECT_EQ(
-        0, Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p))));
-    EXPECT_EQ(
-        24, Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<Int128>(p))));
-    EXPECT_EQ(
-        8,
-        Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p))));
-    EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<int8_t>(p))));
-    EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<Int128>(p))));
-    EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<int32_t>(p))));
-  }
-}
-
-TEST(Layout, Pointers) {
-  alignas(max_align_t) const unsigned char p[100] = {};
-  using L = Layout<int8_t, int8_t, Int128>;
-  {
-    const auto x = L::Partial();
-    EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)),
-              Type<std::tuple<const int8_t*>>(x.Pointers(p)));
-  }
-  {
-    const auto x = L::Partial(1);
-    EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)),
-              (Type<std::tuple<const int8_t*, const int8_t*>>(x.Pointers(p))));
-  }
-  {
-    const auto x = L::Partial(1, 2);
-    EXPECT_EQ(
-        std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
-        (Type<std::tuple<const int8_t*, const int8_t*, const Int128*>>(
-            x.Pointers(p))));
-  }
-  {
-    const auto x = L::Partial(1, 2, 3);
-    EXPECT_EQ(
-        std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
-        (Type<std::tuple<const int8_t*, const int8_t*, const Int128*>>(
-            x.Pointers(p))));
-  }
-  {
-    const L x(1, 2, 3);
-    EXPECT_EQ(
-        std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
-        (Type<std::tuple<const int8_t*, const int8_t*, const Int128*>>(
-            x.Pointers(p))));
-  }
-}
-
-TEST(Layout, MutablePointers) {
-  alignas(max_align_t) unsigned char p[100];
-  using L = Layout<int8_t, int8_t, Int128>;
-  {
-    const auto x = L::Partial();
-    EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)),
-              Type<std::tuple<int8_t*>>(x.Pointers(p)));
-  }
-  {
-    const auto x = L::Partial(1);
-    EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)),
-              (Type<std::tuple<int8_t*, int8_t*>>(x.Pointers(p))));
-  }
-  {
-    const auto x = L::Partial(1, 2);
-    EXPECT_EQ(
-        std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
-        (Type<std::tuple<int8_t*, int8_t*, Int128*>>(x.Pointers(p))));
-  }
-  {
-    const auto x = L::Partial(1, 2, 3);
-    EXPECT_EQ(
-        std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
-        (Type<std::tuple<int8_t*, int8_t*, Int128*>>(x.Pointers(p))));
-  }
-  {
-    const L x(1, 2, 3);
-    EXPECT_EQ(
-        std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
-        (Type<std::tuple<int8_t*, int8_t*, Int128*>>(x.Pointers(p))));
-  }
-}
-
-TEST(Layout, SliceByIndexSize) {
-  alignas(max_align_t) const unsigned char p[100] = {};
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size());
-    EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
-    EXPECT_EQ(3, L(3).Slice<0>(p).size());
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
-    EXPECT_EQ(5, L(3, 5).Slice<1>(p).size());
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
-    EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
-    EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size());
-    EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size());
-    EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size());
-    EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size());
-    EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size());
-  }
-}
-
-TEST(Layout, SliceByTypeSize) {
-  alignas(max_align_t) const unsigned char p[100] = {};
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, L::Partial(0).Slice<int32_t>(p).size());
-    EXPECT_EQ(3, L::Partial(3).Slice<int32_t>(p).size());
-    EXPECT_EQ(3, L(3).Slice<int32_t>(p).size());
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(3, L::Partial(3).Slice<int8_t>(p).size());
-    EXPECT_EQ(3, L::Partial(3, 5).Slice<int8_t>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5).Slice<int32_t>(p).size());
-    EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<int8_t>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<int32_t>(p).size());
-    EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<Int128>(p).size());
-    EXPECT_EQ(3, L(3, 5, 7).Slice<int8_t>(p).size());
-    EXPECT_EQ(5, L(3, 5, 7).Slice<int32_t>(p).size());
-    EXPECT_EQ(7, L(3, 5, 7).Slice<Int128>(p).size());
-  }
-}
-
-TEST(Layout, MutableSliceByIndexSize) {
-  alignas(max_align_t) unsigned char p[100];
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size());
-    EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
-    EXPECT_EQ(3, L(3).Slice<0>(p).size());
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
-    EXPECT_EQ(5, L(3, 5).Slice<1>(p).size());
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
-    EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
-    EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size());
-    EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size());
-    EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size());
-    EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size());
-    EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size());
-  }
-}
-
-TEST(Layout, MutableSliceByTypeSize) {
-  alignas(max_align_t) unsigned char p[100];
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(0, L::Partial(0).Slice<int32_t>(p).size());
-    EXPECT_EQ(3, L::Partial(3).Slice<int32_t>(p).size());
-    EXPECT_EQ(3, L(3).Slice<int32_t>(p).size());
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(3, L::Partial(3).Slice<int8_t>(p).size());
-    EXPECT_EQ(3, L::Partial(3, 5).Slice<int8_t>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5).Slice<int32_t>(p).size());
-    EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<int8_t>(p).size());
-    EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<int32_t>(p).size());
-    EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<Int128>(p).size());
-    EXPECT_EQ(3, L(3, 5, 7).Slice<int8_t>(p).size());
-    EXPECT_EQ(5, L(3, 5, 7).Slice<int32_t>(p).size());
-    EXPECT_EQ(7, L(3, 5, 7).Slice<Int128>(p).size());
-  }
-}
-
-TEST(Layout, SliceByIndexData) {
-  alignas(max_align_t) const unsigned char p[100] = {};
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<const int32_t>>(L::Partial(0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
-    EXPECT_EQ(0,
-              Distance(p, Type<Span<const int32_t>>(L(3).Slice<0>(p)).data()));
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p, Type<Span<const int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        12,
-        Distance(
-            p, Type<Span<const int32_t>>(L::Partial(3, 5).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        12, Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<1>(p)).data()));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<const int8_t>>(L::Partial(0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<const int8_t>>(L::Partial(1).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<const int8_t>>(L::Partial(5).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p, Type<Span<const int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        4,
-        Distance(
-            p, Type<Span<const int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(
-            p, Type<Span<const int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const Int128>>(L::Partial(0, 0, 0).Slice<2>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        4,
-        Distance(
-            p,
-            Type<Span<const int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(
-            p,
-            Type<Span<const Int128>>(L::Partial(1, 0, 0).Slice<2>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        24,
-        Distance(
-            p,
-            Type<Span<const Int128>>(L::Partial(5, 3, 1).Slice<2>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(
-            p,
-            Type<Span<const int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        24,
-        Distance(p, Type<Span<const Int128>>(L(5, 3, 1).Slice<2>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
-  }
-}
-
-TEST(Layout, SliceByTypeData) {
-  alignas(max_align_t) const unsigned char p[100] = {};
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<const int32_t>>(L(3).Slice<int32_t>(p)).data()));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<const int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<int8_t>(p))
-                        .data()));
-    EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>(
-                                 L::Partial(0, 0).Slice<int32_t>(p))
-                                 .data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<int8_t>(p))
-                        .data()));
-    EXPECT_EQ(4, Distance(p, Type<Span<const int32_t>>(
-                                 L::Partial(1, 0).Slice<int32_t>(p))
-                                 .data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<int8_t>(p))
-                        .data()));
-    EXPECT_EQ(8, Distance(p, Type<Span<const int32_t>>(
-                                 L::Partial(5, 3).Slice<int32_t>(p))
-                                 .data()));
-    EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>(
-                                 L::Partial(0, 0, 0).Slice<int8_t>(p))
-                                 .data()));
-    EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>(
-                                 L::Partial(0, 0, 0).Slice<int32_t>(p))
-                                 .data()));
-    EXPECT_EQ(0, Distance(p, Type<Span<const Int128>>(
-                                 L::Partial(0, 0, 0).Slice<Int128>(p))
-                                 .data()));
-    EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>(
-                                 L::Partial(1, 0, 0).Slice<int8_t>(p))
-                                 .data()));
-    EXPECT_EQ(4, Distance(p, Type<Span<const int32_t>>(
-                                 L::Partial(1, 0, 0).Slice<int32_t>(p))
-                                 .data()));
-    EXPECT_EQ(8, Distance(p, Type<Span<const Int128>>(
-                                 L::Partial(1, 0, 0).Slice<Int128>(p))
-                                 .data()));
-    EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>(
-                                 L::Partial(5, 3, 1).Slice<int8_t>(p))
-                                 .data()));
-    EXPECT_EQ(24, Distance(p, Type<Span<const Int128>>(
-                                  L::Partial(5, 3, 1).Slice<Int128>(p))
-                                  .data()));
-    EXPECT_EQ(8, Distance(p, Type<Span<const int32_t>>(
-                                 L::Partial(5, 3, 1).Slice<int32_t>(p))
-                                 .data()));
-    EXPECT_EQ(
-        0,
-        Distance(p,
-                 Type<Span<const int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        24,
-        Distance(p,
-                 Type<Span<const Int128>>(L(5, 3, 1).Slice<Int128>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(
-            p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
-  }
-}
-
-TEST(Layout, MutableSliceByIndexData) {
-  alignas(max_align_t) unsigned char p[100];
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(
-        0, Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
-    EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3).Slice<0>(p)).data()));
-  }
-  {
-    using L = Layout<int32_t, int32_t>;
-    EXPECT_EQ(
-        0, Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        12,
-        Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<1>(p)).data()));
-    EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3, 5).Slice<0>(p)).data()));
-    EXPECT_EQ(12, Distance(p, Type<Span<int32_t>>(L(3, 5).Slice<1>(p)).data()));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(
-        0, Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        4,
-        Distance(p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<2>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        4, Distance(
-               p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data()));
-    EXPECT_EQ(
-        8, Distance(
-               p, Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<2>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
-    EXPECT_EQ(
-        24, Distance(
-                p, Type<Span<Int128>>(L::Partial(5, 3, 1).Slice<2>(p)).data()));
-    EXPECT_EQ(
-        8, Distance(
-               p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data()));
-    EXPECT_EQ(0,
-              Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
-    EXPECT_EQ(24,
-              Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<2>(p)).data()));
-    EXPECT_EQ(8,
-              Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
-  }
-}
-
-TEST(Layout, MutableSliceByTypeData) {
-  alignas(max_align_t) unsigned char p[100];
-  {
-    using L = Layout<int32_t>;
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(
-               p, Type<Span<int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(0,
-              Distance(p, Type<Span<int32_t>>(L(3).Slice<int32_t>(p)).data()));
-  }
-  {
-    using L = Layout<int8_t, int32_t, Int128>;
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p,
-                 Type<Span<int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p,
-                 Type<Span<int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        4,
-        Distance(
-            p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(p,
-                 Type<Span<int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(
-            p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<Int128>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        4,
-        Distance(
-            p,
-            Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(
-            p,
-            Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<Int128>(p)).data()));
-    EXPECT_EQ(
-        0,
-        Distance(
-            p,
-            Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        24,
-        Distance(
-            p,
-            Type<Span<Int128>>(L::Partial(5, 3, 1).Slice<Int128>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(
-            p,
-            Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p)).data()));
-    EXPECT_EQ(
-        0, Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
-    EXPECT_EQ(
-        24,
-        Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<Int128>(p)).data()));
-    EXPECT_EQ(
-        8,
-        Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
-  }
-}
-
-MATCHER_P(IsSameSlice, slice, "") {
-  return arg.size() == slice.size() && arg.data() == slice.data();
-}
-
-template <typename... M>
-class TupleMatcher {
- public:
-  explicit TupleMatcher(M... matchers) : matchers_(std::move(matchers)...) {}
-
-  template <typename Tuple>
-  bool MatchAndExplain(const Tuple& p,
-                       testing::MatchResultListener* /* listener */) const {
-    static_assert(std::tuple_size<Tuple>::value == sizeof...(M), "");
-    return MatchAndExplainImpl(
-        p, absl::make_index_sequence<std::tuple_size<Tuple>::value>{});
-  }
-
-  // For the matcher concept. Left empty as we don't really need the diagnostics
-  // right now.
-  void DescribeTo(::std::ostream* os) const {}
-  void DescribeNegationTo(::std::ostream* os) const {}
-
- private:
-  template <typename Tuple, size_t... Is>
-  bool MatchAndExplainImpl(const Tuple& p, absl::index_sequence<Is...>) const {
-    // Using std::min as a simple variadic "and".
-    return std::min(
-        {true, testing::SafeMatcherCast<
-                   const typename std::tuple_element<Is, Tuple>::type&>(
-                   std::get<Is>(matchers_))
-                   .Matches(std::get<Is>(p))...});
-  }
-
-  std::tuple<M...> matchers_;
-};
-
-template <typename... M>
-testing::PolymorphicMatcher<TupleMatcher<M...>> Tuple(M... matchers) {
-  return testing::MakePolymorphicMatcher(
-      TupleMatcher<M...>(std::move(matchers)...));
-}
-
-TEST(Layout, Slices) {
-  alignas(max_align_t) const unsigned char p[100] = {};
-  using L = Layout<int8_t, int8_t, Int128>;
-  {
-    const auto x = L::Partial();
-    EXPECT_THAT(Type<std::tuple<>>(x.Slices(p)), Tuple());
-  }
-  {
-    const auto x = L::Partial(1);
-    EXPECT_THAT(Type<std::tuple<Span<const int8_t>>>(x.Slices(p)),
-                Tuple(IsSameSlice(x.Slice<0>(p))));
-  }
-  {
-    const auto x = L::Partial(1, 2);
-    EXPECT_THAT(
-        (Type<std::tuple<Span<const int8_t>, Span<const int8_t>>>(x.Slices(p))),
-        Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p))));
-  }
-  {
-    const auto x = L::Partial(1, 2, 3);
-    EXPECT_THAT((Type<std::tuple<Span<const int8_t>, Span<const int8_t>,
-                                 Span<const Int128>>>(x.Slices(p))),
-                Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
-                      IsSameSlice(x.Slice<2>(p))));
-  }
-  {
-    const L x(1, 2, 3);
-    EXPECT_THAT((Type<std::tuple<Span<const int8_t>, Span<const int8_t>,
-                                 Span<const Int128>>>(x.Slices(p))),
-                Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
-                      IsSameSlice(x.Slice<2>(p))));
-  }
-}
-
-TEST(Layout, MutableSlices) {
-  alignas(max_align_t) unsigned char p[100] = {};
-  using L = Layout<int8_t, int8_t, Int128>;
-  {
-    const auto x = L::Partial();
-    EXPECT_THAT(Type<std::tuple<>>(x.Slices(p)), Tuple());
-  }
-  {
-    const auto x = L::Partial(1);
-    EXPECT_THAT(Type<std::tuple<Span<int8_t>>>(x.Slices(p)),
-                Tuple(IsSameSlice(x.Slice<0>(p))));
-  }
-  {
-    const auto x = L::Partial(1, 2);
-    EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>>>(x.Slices(p))),
-                Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p))));
-  }
-  {
-    const auto x = L::Partial(1, 2, 3);
-    EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(
-                    x.Slices(p))),
-                Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
-                      IsSameSlice(x.Slice<2>(p))));
-  }
-  {
-    const L x(1, 2, 3);
-    EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(
-                    x.Slices(p))),
-                Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
-                      IsSameSlice(x.Slice<2>(p))));
-  }
-}
-
-TEST(Layout, UnalignedTypes) {
-  constexpr Layout<unsigned char, unsigned char, unsigned char> x(1, 2, 3);
-  alignas(max_align_t) unsigned char p[x.AllocSize() + 1];
-  EXPECT_THAT(x.Pointers(p + 1), Tuple(p + 1, p + 2, p + 4));
-}
-
-TEST(Layout, CustomAlignment) {
-  constexpr Layout<unsigned char, Aligned<unsigned char, 8>> x(1, 2);
-  alignas(max_align_t) unsigned char p[x.AllocSize()];
-  EXPECT_EQ(10, x.AllocSize());
-  EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 8));
-}
-
-TEST(Layout, OverAligned) {
-  constexpr size_t M = alignof(max_align_t);
-  constexpr Layout<unsigned char, Aligned<unsigned char, 2 * M>> x(1, 3);
-  alignas(2 * M) unsigned char p[x.AllocSize()];
-  EXPECT_EQ(2 * M + 3, x.AllocSize());
-  EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 2 * M));
-}
-
-TEST(Layout, Alignment) {
-  static_assert(Layout<int8_t>::Alignment() == 1, "");
-  static_assert(Layout<int32_t>::Alignment() == 4, "");
-  static_assert(Layout<Int64>::Alignment() == 8, "");
-  static_assert(Layout<Aligned<int8_t, 64>>::Alignment() == 64, "");
-  static_assert(Layout<int8_t, int32_t, Int64>::Alignment() == 8, "");
-  static_assert(Layout<int8_t, Int64, int32_t>::Alignment() == 8, "");
-  static_assert(Layout<int32_t, int8_t, Int64>::Alignment() == 8, "");
-  static_assert(Layout<int32_t, Int64, int8_t>::Alignment() == 8, "");
-  static_assert(Layout<Int64, int8_t, int32_t>::Alignment() == 8, "");
-  static_assert(Layout<Int64, int32_t, int8_t>::Alignment() == 8, "");
-}
-
-TEST(Layout, ConstexprPartial) {
-  constexpr size_t M = alignof(max_align_t);
-  constexpr Layout<unsigned char, Aligned<unsigned char, 2 * M>> x(1, 3);
-  static_assert(x.Partial(1).template Offset<1>() == 2 * M, "");
-}
-// [from, to)
-struct Region {
-  size_t from;
-  size_t to;
-};
-
-void ExpectRegionPoisoned(const unsigned char* p, size_t n, bool poisoned) {
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-  for (size_t i = 0; i != n; ++i) {
-    EXPECT_EQ(poisoned, __asan_address_is_poisoned(p + i));
-  }
-#endif
-}
-
-template <size_t N>
-void ExpectPoisoned(const unsigned char (&buf)[N],
-                    std::initializer_list<Region> reg) {
-  size_t prev = 0;
-  for (const Region& r : reg) {
-    ExpectRegionPoisoned(buf + prev, r.from - prev, false);
-    ExpectRegionPoisoned(buf + r.from, r.to - r.from, true);
-    prev = r.to;
-  }
-  ExpectRegionPoisoned(buf + prev, N - prev, false);
-}
-
-TEST(Layout, PoisonPadding) {
-  using L = Layout<int8_t, Int64, int32_t, Int128>;
-
-  constexpr size_t n = L::Partial(1, 2, 3, 4).AllocSize();
-  {
-    constexpr auto x = L::Partial();
-    alignas(max_align_t) const unsigned char c[n] = {};
-    x.PoisonPadding(c);
-    EXPECT_EQ(x.Slices(c), x.Slices(c));
-    ExpectPoisoned(c, {});
-  }
-  {
-    constexpr auto x = L::Partial(1);
-    alignas(max_align_t) const unsigned char c[n] = {};
-    x.PoisonPadding(c);
-    EXPECT_EQ(x.Slices(c), x.Slices(c));
-    ExpectPoisoned(c, {{1, 8}});
-  }
-  {
-    constexpr auto x = L::Partial(1, 2);
-    alignas(max_align_t) const unsigned char c[n] = {};
-    x.PoisonPadding(c);
-    EXPECT_EQ(x.Slices(c), x.Slices(c));
-    ExpectPoisoned(c, {{1, 8}});
-  }
-  {
-    constexpr auto x = L::Partial(1, 2, 3);
-    alignas(max_align_t) const unsigned char c[n] = {};
-    x.PoisonPadding(c);
-    EXPECT_EQ(x.Slices(c), x.Slices(c));
-    ExpectPoisoned(c, {{1, 8}, {36, 40}});
-  }
-  {
-    constexpr auto x = L::Partial(1, 2, 3, 4);
-    alignas(max_align_t) const unsigned char c[n] = {};
-    x.PoisonPadding(c);
-    EXPECT_EQ(x.Slices(c), x.Slices(c));
-    ExpectPoisoned(c, {{1, 8}, {36, 40}});
-  }
-  {
-    constexpr L x(1, 2, 3, 4);
-    alignas(max_align_t) const unsigned char c[n] = {};
-    x.PoisonPadding(c);
-    EXPECT_EQ(x.Slices(c), x.Slices(c));
-    ExpectPoisoned(c, {{1, 8}, {36, 40}});
-  }
-}
-
-TEST(Layout, DebugString) {
-  {
-    constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial();
-    EXPECT_EQ("@0<signed char>(1)", x.DebugString());
-  }
-  {
-    constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1);
-    EXPECT_EQ("@0<signed char>(1)[1]; @4<int>(4)", x.DebugString());
-  }
-  {
-    constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2);
-    EXPECT_EQ("@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)",
-              x.DebugString());
-  }
-  {
-    constexpr auto x =
-        Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3);
-    EXPECT_EQ(
-        "@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
-        "@16" +
-            Int128::Name() + "(16)",
-        x.DebugString());
-  }
-  {
-    constexpr auto x =
-        Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3, 4);
-    EXPECT_EQ(
-        "@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
-        "@16" +
-            Int128::Name() + "(16)[4]",
-        x.DebugString());
-  }
-  {
-    constexpr Layout<int8_t, int32_t, int8_t, Int128> x(1, 2, 3, 4);
-    EXPECT_EQ(
-        "@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
-        "@16" +
-            Int128::Name() + "(16)[4]",
-        x.DebugString());
-  }
-}
-
-TEST(Layout, CharTypes) {
-  constexpr Layout<int32_t> x(1);
-  alignas(max_align_t) char c[x.AllocSize()] = {};
-  alignas(max_align_t) unsigned char uc[x.AllocSize()] = {};
-  alignas(max_align_t) signed char sc[x.AllocSize()] = {};
-  alignas(max_align_t) const char cc[x.AllocSize()] = {};
-  alignas(max_align_t) const unsigned char cuc[x.AllocSize()] = {};
-  alignas(max_align_t) const signed char csc[x.AllocSize()] = {};
-
-  Type<int32_t*>(x.Pointer<0>(c));
-  Type<int32_t*>(x.Pointer<0>(uc));
-  Type<int32_t*>(x.Pointer<0>(sc));
-  Type<const int32_t*>(x.Pointer<0>(cc));
-  Type<const int32_t*>(x.Pointer<0>(cuc));
-  Type<const int32_t*>(x.Pointer<0>(csc));
-
-  Type<int32_t*>(x.Pointer<int32_t>(c));
-  Type<int32_t*>(x.Pointer<int32_t>(uc));
-  Type<int32_t*>(x.Pointer<int32_t>(sc));
-  Type<const int32_t*>(x.Pointer<int32_t>(cc));
-  Type<const int32_t*>(x.Pointer<int32_t>(cuc));
-  Type<const int32_t*>(x.Pointer<int32_t>(csc));
-
-  Type<std::tuple<int32_t*>>(x.Pointers(c));
-  Type<std::tuple<int32_t*>>(x.Pointers(uc));
-  Type<std::tuple<int32_t*>>(x.Pointers(sc));
-  Type<std::tuple<const int32_t*>>(x.Pointers(cc));
-  Type<std::tuple<const int32_t*>>(x.Pointers(cuc));
-  Type<std::tuple<const int32_t*>>(x.Pointers(csc));
-
-  Type<Span<int32_t>>(x.Slice<0>(c));
-  Type<Span<int32_t>>(x.Slice<0>(uc));
-  Type<Span<int32_t>>(x.Slice<0>(sc));
-  Type<Span<const int32_t>>(x.Slice<0>(cc));
-  Type<Span<const int32_t>>(x.Slice<0>(cuc));
-  Type<Span<const int32_t>>(x.Slice<0>(csc));
-
-  Type<std::tuple<Span<int32_t>>>(x.Slices(c));
-  Type<std::tuple<Span<int32_t>>>(x.Slices(uc));
-  Type<std::tuple<Span<int32_t>>>(x.Slices(sc));
-  Type<std::tuple<Span<const int32_t>>>(x.Slices(cc));
-  Type<std::tuple<Span<const int32_t>>>(x.Slices(cuc));
-  Type<std::tuple<Span<const int32_t>>>(x.Slices(csc));
-}
-
-TEST(Layout, ConstElementType) {
-  constexpr Layout<const int32_t> x(1);
-  alignas(int32_t) char c[x.AllocSize()] = {};
-  const char* cc = c;
-  const int32_t* p = reinterpret_cast<const int32_t*>(cc);
-
-  EXPECT_EQ(alignof(int32_t), x.Alignment());
-
-  EXPECT_EQ(0, x.Offset<0>());
-  EXPECT_EQ(0, x.Offset<const int32_t>());
-
-  EXPECT_THAT(x.Offsets(), ElementsAre(0));
-
-  EXPECT_EQ(1, x.Size<0>());
-  EXPECT_EQ(1, x.Size<const int32_t>());
-
-  EXPECT_THAT(x.Sizes(), ElementsAre(1));
-
-  EXPECT_EQ(sizeof(int32_t), x.AllocSize());
-
-  EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<0>(c)));
-  EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<0>(cc)));
-
-  EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<const int32_t>(c)));
-  EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<const int32_t>(cc)));
-
-  EXPECT_THAT(Type<std::tuple<const int32_t*>>(x.Pointers(c)), Tuple(p));
-  EXPECT_THAT(Type<std::tuple<const int32_t*>>(x.Pointers(cc)), Tuple(p));
-
-  EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<0>(c)),
-              IsSameSlice(Span<const int32_t>(p, 1)));
-  EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<0>(cc)),
-              IsSameSlice(Span<const int32_t>(p, 1)));
-
-  EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<const int32_t>(c)),
-              IsSameSlice(Span<const int32_t>(p, 1)));
-  EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<const int32_t>(cc)),
-              IsSameSlice(Span<const int32_t>(p, 1)));
-
-  EXPECT_THAT(Type<std::tuple<Span<const int32_t>>>(x.Slices(c)),
-              Tuple(IsSameSlice(Span<const int32_t>(p, 1))));
-  EXPECT_THAT(Type<std::tuple<Span<const int32_t>>>(x.Slices(cc)),
-              Tuple(IsSameSlice(Span<const int32_t>(p, 1))));
-}
-
-namespace example {
-
-// Immutable move-only string with sizeof equal to sizeof(void*). The string
-// size and the characters are kept in the same heap allocation.
-class CompactString {
- public:
-  CompactString(const char* s = "") {  // NOLINT
-    const size_t size = strlen(s);
-    // size_t[1], followed by char[size + 1].
-    // This statement doesn't allocate memory.
-    const L layout(1, size + 1);
-    // AllocSize() tells us how much memory we need to allocate for all our
-    // data.
-    p_.reset(new unsigned char[layout.AllocSize()]);
-    // If running under ASAN, mark the padding bytes, if any, to catch memory
-    // errors.
-    layout.PoisonPadding(p_.get());
-    // Store the size in the allocation.
-    // Pointer<size_t>() is a synonym for Pointer<0>().
-    *layout.Pointer<size_t>(p_.get()) = size;
-    // Store the characters in the allocation.
-    memcpy(layout.Pointer<char>(p_.get()), s, size + 1);
-  }
-
-  size_t size() const {
-    // Equivalent to reinterpret_cast<size_t&>(*p).
-    return *L::Partial().Pointer<size_t>(p_.get());
-  }
-
-  const char* c_str() const {
-    // Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)).
-    // The argument in Partial(1) specifies that we have size_t[1] in front of
-    // the characters.
-    return L::Partial(1).Pointer<char>(p_.get());
-  }
-
- private:
-  // Our heap allocation contains a size_t followed by an array of chars.
-  using L = Layout<size_t, char>;
-  std::unique_ptr<unsigned char[]> p_;
-};
-
-TEST(CompactString, Works) {
-  CompactString s = "hello";
-  EXPECT_EQ(5, s.size());
-  EXPECT_STREQ("hello", s.c_str());
-}
-
-}  // namespace example
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/node_hash_policy.h b/third_party/abseil_cpp/absl/container/internal/node_hash_policy.h
deleted file mode 100644
index 4617162f0b..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/node_hash_policy.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Adapts a policy for nodes.
-//
-// The node policy should model:
-//
-// struct Policy {
-//   // Returns a new node allocated and constructed using the allocator, using
-//   // the specified arguments.
-//   template <class Alloc, class... Args>
-//   value_type* new_element(Alloc* alloc, Args&&... args) const;
-//
-//   // Destroys and deallocates node using the allocator.
-//   template <class Alloc>
-//   void delete_element(Alloc* alloc, value_type* node) const;
-// };
-//
-// It may also optionally define `value()` and `apply()`. For documentation on
-// these, see hash_policy_traits.h.
-
-#ifndef ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_
-#define ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_
-
-#include <cassert>
-#include <cstddef>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class Reference, class Policy>
-struct node_hash_policy {
-  static_assert(std::is_lvalue_reference<Reference>::value, "");
-
-  using slot_type = typename std::remove_cv<
-      typename std::remove_reference<Reference>::type>::type*;
-
-  template <class Alloc, class... Args>
-  static void construct(Alloc* alloc, slot_type* slot, Args&&... args) {
-    *slot = Policy::new_element(alloc, std::forward<Args>(args)...);
-  }
-
-  template <class Alloc>
-  static void destroy(Alloc* alloc, slot_type* slot) {
-    Policy::delete_element(alloc, *slot);
-  }
-
-  template <class Alloc>
-  static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) {
-    *new_slot = *old_slot;
-  }
-
-  static size_t space_used(const slot_type* slot) {
-    if (slot == nullptr) return Policy::element_space_used(nullptr);
-    return Policy::element_space_used(*slot);
-  }
-
-  static Reference element(slot_type* slot) { return **slot; }
-
-  template <class T, class P = Policy>
-  static auto value(T* elem) -> decltype(P::value(elem)) {
-    return P::value(elem);
-  }
-
-  template <class... Ts, class P = Policy>
-  static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward<Ts>(ts)...)) {
-    return P::apply(std::forward<Ts>(ts)...);
-  }
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/node_hash_policy_test.cc b/third_party/abseil_cpp/absl/container/internal/node_hash_policy_test.cc
deleted file mode 100644
index 84aabba968..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/node_hash_policy_test.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/node_hash_policy.h"
-
-#include <memory>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/hash_policy_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using ::testing::Pointee;
-
-struct Policy : node_hash_policy<int&, Policy> {
-  using key_type = int;
-  using init_type = int;
-
-  template <class Alloc>
-  static int* new_element(Alloc* alloc, int value) {
-    return new int(value);
-  }
-
-  template <class Alloc>
-  static void delete_element(Alloc* alloc, int* elem) {
-    delete elem;
-  }
-};
-
-using NodePolicy = hash_policy_traits<Policy>;
-
-struct NodeTest : ::testing::Test {
-  std::allocator<int> alloc;
-  int n = 53;
-  int* a = &n;
-};
-
-TEST_F(NodeTest, ConstructDestroy) {
-  NodePolicy::construct(&alloc, &a, 42);
-  EXPECT_THAT(a, Pointee(42));
-  NodePolicy::destroy(&alloc, &a);
-}
-
-TEST_F(NodeTest, transfer) {
-  int s = 42;
-  int* b = &s;
-  NodePolicy::transfer(&alloc, &a, &b);
-  EXPECT_EQ(&s, a);
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/raw_hash_map.h b/third_party/abseil_cpp/absl/container/internal/raw_hash_map.h
deleted file mode 100644
index 0a02757ddf..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/raw_hash_map.h
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_
-#define ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_
-
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/internal/throw_delegate.h"
-#include "absl/container/internal/container_memory.h"
-#include "absl/container/internal/raw_hash_set.h"  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class Policy, class Hash, class Eq, class Alloc>
-class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> {
-  // P is Policy. It's passed as a template argument to support maps that have
-  // incomplete types as values, as in unordered_map<K, IncompleteType>.
-  // MappedReference<> may be a non-reference type.
-  template <class P>
-  using MappedReference = decltype(P::value(
-      std::addressof(std::declval<typename raw_hash_map::reference>())));
-
-  // MappedConstReference<> may be a non-reference type.
-  template <class P>
-  using MappedConstReference = decltype(P::value(
-      std::addressof(std::declval<typename raw_hash_map::const_reference>())));
-
-  using KeyArgImpl =
-      KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
-
- public:
-  using key_type = typename Policy::key_type;
-  using mapped_type = typename Policy::mapped_type;
-  template <class K>
-  using key_arg = typename KeyArgImpl::template type<K, key_type>;
-
-  static_assert(!std::is_reference<key_type>::value, "");
-  // TODO(alkis): remove this assertion and verify that reference mapped_type is
-  // supported.
-  static_assert(!std::is_reference<mapped_type>::value, "");
-
-  using iterator = typename raw_hash_map::raw_hash_set::iterator;
-  using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator;
-
-  raw_hash_map() {}
-  using raw_hash_map::raw_hash_set::raw_hash_set;
-
-  // The last two template parameters ensure that both arguments are rvalues
-  // (lvalue arguments are handled by the overloads below). This is necessary
-  // for supporting bitfield arguments.
-  //
-  //   union { int n : 1; };
-  //   flat_hash_map<int, int> m;
-  //   m.insert_or_assign(n, n);
-  template <class K = key_type, class V = mapped_type, K* = nullptr,
-            V* = nullptr>
-  std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v) {
-    return insert_or_assign_impl(std::forward<K>(k), std::forward<V>(v));
-  }
-
-  template <class K = key_type, class V = mapped_type, K* = nullptr>
-  std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v) {
-    return insert_or_assign_impl(std::forward<K>(k), v);
-  }
-
-  template <class K = key_type, class V = mapped_type, V* = nullptr>
-  std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v) {
-    return insert_or_assign_impl(k, std::forward<V>(v));
-  }
-
-  template <class K = key_type, class V = mapped_type>
-  std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const V& v) {
-    return insert_or_assign_impl(k, v);
-  }
-
-  template <class K = key_type, class V = mapped_type, K* = nullptr,
-            V* = nullptr>
-  iterator insert_or_assign(const_iterator, key_arg<K>&& k, V&& v) {
-    return insert_or_assign(std::forward<K>(k), std::forward<V>(v)).first;
-  }
-
-  template <class K = key_type, class V = mapped_type, K* = nullptr>
-  iterator insert_or_assign(const_iterator, key_arg<K>&& k, const V& v) {
-    return insert_or_assign(std::forward<K>(k), v).first;
-  }
-
-  template <class K = key_type, class V = mapped_type, V* = nullptr>
-  iterator insert_or_assign(const_iterator, const key_arg<K>& k, V&& v) {
-    return insert_or_assign(k, std::forward<V>(v)).first;
-  }
-
-  template <class K = key_type, class V = mapped_type>
-  iterator insert_or_assign(const_iterator, const key_arg<K>& k, const V& v) {
-    return insert_or_assign(k, v).first;
-  }
-
-  // All `try_emplace()` overloads make the same guarantees regarding rvalue
-  // arguments as `std::unordered_map::try_emplace()`, namely that these
-  // functions will not move from rvalue arguments if insertions do not happen.
-  template <class K = key_type, class... Args,
-            typename std::enable_if<
-                !std::is_convertible<K, const_iterator>::value, int>::type = 0,
-            K* = nullptr>
-  std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args) {
-    return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...);
-  }
-
-  template <class K = key_type, class... Args,
-            typename std::enable_if<
-                !std::is_convertible<K, const_iterator>::value, int>::type = 0>
-  std::pair<iterator, bool> try_emplace(const key_arg<K>& k, Args&&... args) {
-    return try_emplace_impl(k, std::forward<Args>(args)...);
-  }
-
-  template <class K = key_type, class... Args, K* = nullptr>
-  iterator try_emplace(const_iterator, key_arg<K>&& k, Args&&... args) {
-    return try_emplace(std::forward<K>(k), std::forward<Args>(args)...).first;
-  }
-
-  template <class K = key_type, class... Args>
-  iterator try_emplace(const_iterator, const key_arg<K>& k, Args&&... args) {
-    return try_emplace(k, std::forward<Args>(args)...).first;
-  }
-
-  template <class K = key_type, class P = Policy>
-  MappedReference<P> at(const key_arg<K>& key) {
-    auto it = this->find(key);
-    if (it == this->end()) {
-      base_internal::ThrowStdOutOfRange(
-          "absl::container_internal::raw_hash_map<>::at");
-    }
-    return Policy::value(&*it);
-  }
-
-  template <class K = key_type, class P = Policy>
-  MappedConstReference<P> at(const key_arg<K>& key) const {
-    auto it = this->find(key);
-    if (it == this->end()) {
-      base_internal::ThrowStdOutOfRange(
-          "absl::container_internal::raw_hash_map<>::at");
-    }
-    return Policy::value(&*it);
-  }
-
-  template <class K = key_type, class P = Policy, K* = nullptr>
-  MappedReference<P> operator[](key_arg<K>&& key) {
-    return Policy::value(&*try_emplace(std::forward<K>(key)).first);
-  }
-
-  template <class K = key_type, class P = Policy>
-  MappedReference<P> operator[](const key_arg<K>& key) {
-    return Policy::value(&*try_emplace(key).first);
-  }
-
- private:
-  template <class K, class V>
-  std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {
-    auto res = this->find_or_prepare_insert(k);
-    if (res.second)
-      this->emplace_at(res.first, std::forward<K>(k), std::forward<V>(v));
-    else
-      Policy::value(&*this->iterator_at(res.first)) = std::forward<V>(v);
-    return {this->iterator_at(res.first), res.second};
-  }
-
-  template <class K = key_type, class... Args>
-  std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args) {
-    auto res = this->find_or_prepare_insert(k);
-    if (res.second)
-      this->emplace_at(res.first, std::piecewise_construct,
-                       std::forward_as_tuple(std::forward<K>(k)),
-                       std::forward_as_tuple(std::forward<Args>(args)...));
-    return {this->iterator_at(res.first), res.second};
-  }
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/raw_hash_set.cc b/third_party/abseil_cpp/absl/container/internal/raw_hash_set.cc
deleted file mode 100644
index bfef071f29..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/raw_hash_set.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/raw_hash_set.h"
-
-#include <atomic>
-#include <cstddef>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-constexpr size_t Group::kWidth;
-
-// Returns "random" seed.
-inline size_t RandomSeed() {
-#ifdef ABSL_HAVE_THREAD_LOCAL
-  static thread_local size_t counter = 0;
-  size_t value = ++counter;
-#else   // ABSL_HAVE_THREAD_LOCAL
-  static std::atomic<size_t> counter(0);
-  size_t value = counter.fetch_add(1, std::memory_order_relaxed);
-#endif  // ABSL_HAVE_THREAD_LOCAL
-  return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
-}
-
-bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) {
-  // To avoid problems with weak hashes and single bit tests, we use % 13.
-  // TODO(kfm,sbenza): revisit after we do unconditional mixing
-  return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6;
-}
-
-void ConvertDeletedToEmptyAndFullToDeleted(
-    ctrl_t* ctrl, size_t capacity) {
-  assert(ctrl[capacity] == kSentinel);
-  assert(IsValidCapacity(capacity));
-  for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) {
-    Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
-  }
-  // Copy the cloned ctrl bytes.
-  std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth);
-  ctrl[capacity] = kSentinel;
-}
-
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/raw_hash_set.h b/third_party/abseil_cpp/absl/container/internal/raw_hash_set.h
deleted file mode 100644
index 02158c4e08..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/raw_hash_set.h
+++ /dev/null
@@ -1,1903 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// An open-addressing
-// hashtable with quadratic probing.
-//
-// This is a low level hashtable on top of which different interfaces can be
-// implemented, like flat_hash_set, node_hash_set, string_hash_set, etc.
-//
-// The table interface is similar to that of std::unordered_set. Notable
-// differences are that most member functions support heterogeneous keys when
-// BOTH the hash and eq functions are marked as transparent. They do so by
-// providing a typedef called `is_transparent`.
-//
-// When heterogeneous lookup is enabled, functions that take key_type act as if
-// they have an overload set like:
-//
-//   iterator find(const key_type& key);
-//   template <class K>
-//   iterator find(const K& key);
-//
-//   size_type erase(const key_type& key);
-//   template <class K>
-//   size_type erase(const K& key);
-//
-//   std::pair<iterator, iterator> equal_range(const key_type& key);
-//   template <class K>
-//   std::pair<iterator, iterator> equal_range(const K& key);
-//
-// When heterogeneous lookup is disabled, only the explicit `key_type` overloads
-// exist.
-//
-// find() also supports passing the hash explicitly:
-//
-//   iterator find(const key_type& key, size_t hash);
-//   template <class U>
-//   iterator find(const U& key, size_t hash);
-//
-// In addition the pointer to element and iterator stability guarantees are
-// weaker: all iterators and pointers are invalidated after a new element is
-// inserted.
-//
-// IMPLEMENTATION DETAILS
-//
-// The table stores elements inline in a slot array. In addition to the slot
-// array the table maintains some control state per slot. The extra state is one
-// byte per slot and stores empty or deleted marks, or alternatively 7 bits from
-// the hash of an occupied slot. The table is split into logical groups of
-// slots, like so:
-//
-//      Group 1         Group 2        Group 3
-// +---------------+---------------+---------------+
-// | | | | | | | | | | | | | | | | | | | | | | | | |
-// +---------------+---------------+---------------+
-//
-// On lookup the hash is split into two parts:
-// - H2: 7 bits (those stored in the control bytes)
-// - H1: the rest of the bits
-// The groups are probed using H1. For each group the slots are matched to H2 in
-// parallel. Because H2 is 7 bits (128 states) and the number of slots per group
-// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit.
-//
-// On insert, once the right group is found (as in lookup), its slots are
-// filled in order.
-//
-// On erase a slot is cleared. In case the group did not have any empty slots
-// before the erase, the erased slot is marked as deleted.
-//
-// Groups without empty slots (but maybe with deleted slots) extend the probe
-// sequence. The probing algorithm is quadratic. Given N the number of groups,
-// the probing function for the i'th probe is:
-//
-//   P(0) = H1 % N
-//
-//   P(i) = (P(i - 1) + i) % N
-//
-// This probing function guarantees that after N probes, all the groups of the
-// table will be probed exactly once.
-
-#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
-#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
-
-#include <algorithm>
-#include <cmath>
-#include <cstdint>
-#include <cstring>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/internal/bits.h"
-#include "absl/base/internal/endian.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"
-#include "absl/container/internal/common.h"
-#include "absl/container/internal/compressed_tuple.h"
-#include "absl/container/internal/container_memory.h"
-#include "absl/container/internal/hash_policy_traits.h"
-#include "absl/container/internal/hashtable_debug_hooks.h"
-#include "absl/container/internal/hashtablez_sampler.h"
-#include "absl/container/internal/have_sse.h"
-#include "absl/container/internal/layout.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <typename AllocType>
-void SwapAlloc(AllocType& lhs, AllocType& rhs,
-               std::true_type /* propagate_on_container_swap */) {
-  using std::swap;
-  swap(lhs, rhs);
-}
-template <typename AllocType>
-void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/,
-               std::false_type /* propagate_on_container_swap */) {}
-
-template <size_t Width>
-class probe_seq {
- public:
-  probe_seq(size_t hash, size_t mask) {
-    assert(((mask + 1) & mask) == 0 && "not a mask");
-    mask_ = mask;
-    offset_ = hash & mask_;
-  }
-  size_t offset() const { return offset_; }
-  size_t offset(size_t i) const { return (offset_ + i) & mask_; }
-
-  void next() {
-    index_ += Width;
-    offset_ += index_;
-    offset_ &= mask_;
-  }
-  // 0-based probe index. The i-th probe in the probe sequence.
-  size_t index() const { return index_; }
-
- private:
-  size_t mask_;
-  size_t offset_;
-  size_t index_ = 0;
-};
-
-template <class ContainerKey, class Hash, class Eq>
-struct RequireUsableKey {
-  template <class PassedKey, class... Args>
-  std::pair<
-      decltype(std::declval<const Hash&>()(std::declval<const PassedKey&>())),
-      decltype(std::declval<const Eq&>()(std::declval<const ContainerKey&>(),
-                                         std::declval<const PassedKey&>()))>*
-  operator()(const PassedKey&, const Args&...) const;
-};
-
-template <class E, class Policy, class Hash, class Eq, class... Ts>
-struct IsDecomposable : std::false_type {};
-
-template <class Policy, class Hash, class Eq, class... Ts>
-struct IsDecomposable<
-    absl::void_t<decltype(
-        Policy::apply(RequireUsableKey<typename Policy::key_type, Hash, Eq>(),
-                      std::declval<Ts>()...))>,
-    Policy, Hash, Eq, Ts...> : std::true_type {};
-
-// TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it.
-template <class T>
-constexpr bool IsNoThrowSwappable(std::true_type = {} /* is_swappable */) {
-  using std::swap;
-  return noexcept(swap(std::declval<T&>(), std::declval<T&>()));
-}
-template <class T>
-constexpr bool IsNoThrowSwappable(std::false_type /* is_swappable */) {
-  return false;
-}
-
-template <typename T>
-int TrailingZeros(T x) {
-  return sizeof(T) == 8 ? base_internal::CountTrailingZerosNonZero64(
-                              static_cast<uint64_t>(x))
-                        : base_internal::CountTrailingZerosNonZero32(
-                              static_cast<uint32_t>(x));
-}
-
-template <typename T>
-int LeadingZeros(T x) {
-  return sizeof(T) == 8
-             ? base_internal::CountLeadingZeros64(static_cast<uint64_t>(x))
-             : base_internal::CountLeadingZeros32(static_cast<uint32_t>(x));
-}
-
-// An abstraction over a bitmask. It provides an easy way to iterate through the
-// indexes of the set bits of a bitmask.  When Shift=0 (platforms with SSE),
-// this is a true bitmask.  On non-SSE, platforms the arithematic used to
-// emulate the SSE behavior works in bytes (Shift=3) and leaves each bytes as
-// either 0x00 or 0x80.
-//
-// For example:
-//   for (int i : BitMask<uint32_t, 16>(0x5)) -> yields 0, 2
-//   for (int i : BitMask<uint64_t, 8, 3>(0x0000000080800000)) -> yields 2, 3
-template <class T, int SignificantBits, int Shift = 0>
-class BitMask {
-  static_assert(std::is_unsigned<T>::value, "");
-  static_assert(Shift == 0 || Shift == 3, "");
-
- public:
-  // These are useful for unit tests (gunit).
-  using value_type = int;
-  using iterator = BitMask;
-  using const_iterator = BitMask;
-
-  explicit BitMask(T mask) : mask_(mask) {}
-  BitMask& operator++() {
-    mask_ &= (mask_ - 1);
-    return *this;
-  }
-  explicit operator bool() const { return mask_ != 0; }
-  int operator*() const { return LowestBitSet(); }
-  int LowestBitSet() const {
-    return container_internal::TrailingZeros(mask_) >> Shift;
-  }
-  int HighestBitSet() const {
-    return (sizeof(T) * CHAR_BIT - container_internal::LeadingZeros(mask_) -
-            1) >>
-           Shift;
-  }
-
-  BitMask begin() const { return *this; }
-  BitMask end() const { return BitMask(0); }
-
-  int TrailingZeros() const {
-    return container_internal::TrailingZeros(mask_) >> Shift;
-  }
-
-  int LeadingZeros() const {
-    constexpr int total_significant_bits = SignificantBits << Shift;
-    constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
-    return container_internal::LeadingZeros(mask_ << extra_bits) >> Shift;
-  }
-
- private:
-  friend bool operator==(const BitMask& a, const BitMask& b) {
-    return a.mask_ == b.mask_;
-  }
-  friend bool operator!=(const BitMask& a, const BitMask& b) {
-    return a.mask_ != b.mask_;
-  }
-
-  T mask_;
-};
-
-using ctrl_t = signed char;
-using h2_t = uint8_t;
-
-// The values here are selected for maximum performance. See the static asserts
-// below for details.
-enum Ctrl : ctrl_t {
-  kEmpty = -128,   // 0b10000000
-  kDeleted = -2,   // 0b11111110
-  kSentinel = -1,  // 0b11111111
-};
-static_assert(
-    kEmpty & kDeleted & kSentinel & 0x80,
-    "Special markers need to have the MSB to make checking for them efficient");
-static_assert(kEmpty < kSentinel && kDeleted < kSentinel,
-              "kEmpty and kDeleted must be smaller than kSentinel to make the "
-              "SIMD test of IsEmptyOrDeleted() efficient");
-static_assert(kSentinel == -1,
-              "kSentinel must be -1 to elide loading it from memory into SIMD "
-              "registers (pcmpeqd xmm, xmm)");
-static_assert(kEmpty == -128,
-              "kEmpty must be -128 to make the SIMD check for its "
-              "existence efficient (psignb xmm, xmm)");
-static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F,
-              "kEmpty and kDeleted must share an unset bit that is not shared "
-              "by kSentinel to make the scalar test for MatchEmptyOrDeleted() "
-              "efficient");
-static_assert(kDeleted == -2,
-              "kDeleted must be -2 to make the implementation of "
-              "ConvertSpecialToEmptyAndFullToDeleted efficient");
-
-// A single block of empty control bytes for tables without any slots allocated.
-// This enables removing a branch in the hot path of find().
-inline ctrl_t* EmptyGroup() {
-  alignas(16) static constexpr ctrl_t empty_group[] = {
-      kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,
-      kEmpty,    kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty};
-  return const_cast<ctrl_t*>(empty_group);
-}
-
-// Mixes a randomly generated per-process seed with `hash` and `ctrl` to
-// randomize insertion order within groups.
-bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl);
-
-// Returns a hash seed.
-//
-// The seed consists of the ctrl_ pointer, which adds enough entropy to ensure
-// non-determinism of iteration order in most cases.
-inline size_t HashSeed(const ctrl_t* ctrl) {
-  // The low bits of the pointer have little or no entropy because of
-  // alignment. We shift the pointer to try to use higher entropy bits. A
-  // good number seems to be 12 bits, because that aligns with page size.
-  return reinterpret_cast<uintptr_t>(ctrl) >> 12;
-}
-
-inline size_t H1(size_t hash, const ctrl_t* ctrl) {
-  return (hash >> 7) ^ HashSeed(ctrl);
-}
-inline ctrl_t H2(size_t hash) { return hash & 0x7F; }
-
-inline bool IsEmpty(ctrl_t c) { return c == kEmpty; }
-inline bool IsFull(ctrl_t c) { return c >= 0; }
-inline bool IsDeleted(ctrl_t c) { return c == kDeleted; }
-inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; }
-
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
-
-// https://github.com/abseil/abseil-cpp/issues/209
-// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853
-// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char
-// Work around this by using the portable implementation of Group
-// when using -funsigned-char under GCC.
-inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) {
-#if defined(__GNUC__) && !defined(__clang__)
-  if (std::is_unsigned<char>::value) {
-    const __m128i mask = _mm_set1_epi8(0x80);
-    const __m128i diff = _mm_subs_epi8(b, a);
-    return _mm_cmpeq_epi8(_mm_and_si128(diff, mask), mask);
-  }
-#endif
-  return _mm_cmpgt_epi8(a, b);
-}
-
-struct GroupSse2Impl {
-  static constexpr size_t kWidth = 16;  // the number of slots per group
-
-  explicit GroupSse2Impl(const ctrl_t* pos) {
-    ctrl = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pos));
-  }
-
-  // Returns a bitmask representing the positions of slots that match hash.
-  BitMask<uint32_t, kWidth> Match(h2_t hash) const {
-    auto match = _mm_set1_epi8(hash);
-    return BitMask<uint32_t, kWidth>(
-        _mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)));
-  }
-
-  // Returns a bitmask representing the positions of empty slots.
-  BitMask<uint32_t, kWidth> MatchEmpty() const {
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3
-    // This only works because kEmpty is -128.
-    return BitMask<uint32_t, kWidth>(
-        _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)));
-#else
-    return Match(static_cast<h2_t>(kEmpty));
-#endif
-  }
-
-  // Returns a bitmask representing the positions of empty or deleted slots.
-  BitMask<uint32_t, kWidth> MatchEmptyOrDeleted() const {
-    auto special = _mm_set1_epi8(kSentinel);
-    return BitMask<uint32_t, kWidth>(
-        _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)));
-  }
-
-  // Returns the number of trailing empty or deleted elements in the group.
-  uint32_t CountLeadingEmptyOrDeleted() const {
-    auto special = _mm_set1_epi8(kSentinel);
-    return TrailingZeros(
-        _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1);
-  }
-
-  void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
-    auto msbs = _mm_set1_epi8(static_cast<char>(-128));
-    auto x126 = _mm_set1_epi8(126);
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3
-    auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs);
-#else
-    auto zero = _mm_setzero_si128();
-    auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl);
-    auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126));
-#endif
-    _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res);
-  }
-
-  __m128i ctrl;
-};
-#endif  // ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
-
-struct GroupPortableImpl {
-  static constexpr size_t kWidth = 8;
-
-  explicit GroupPortableImpl(const ctrl_t* pos)
-      : ctrl(little_endian::Load64(pos)) {}
-
-  BitMask<uint64_t, kWidth, 3> Match(h2_t hash) const {
-    // For the technique, see:
-    // http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord
-    // (Determine if a word has a byte equal to n).
-    //
-    // Caveat: there are false positives but:
-    // - they only occur if there is a real match
-    // - they never occur on kEmpty, kDeleted, kSentinel
-    // - they will be handled gracefully by subsequent checks in code
-    //
-    // Example:
-    //   v = 0x1716151413121110
-    //   hash = 0x12
-    //   retval = (v - lsbs) & ~v & msbs = 0x0000000080800000
-    constexpr uint64_t msbs = 0x8080808080808080ULL;
-    constexpr uint64_t lsbs = 0x0101010101010101ULL;
-    auto x = ctrl ^ (lsbs * hash);
-    return BitMask<uint64_t, kWidth, 3>((x - lsbs) & ~x & msbs);
-  }
-
-  BitMask<uint64_t, kWidth, 3> MatchEmpty() const {
-    constexpr uint64_t msbs = 0x8080808080808080ULL;
-    return BitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 6)) & msbs);
-  }
-
-  BitMask<uint64_t, kWidth, 3> MatchEmptyOrDeleted() const {
-    constexpr uint64_t msbs = 0x8080808080808080ULL;
-    return BitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 7)) & msbs);
-  }
-
-  uint32_t CountLeadingEmptyOrDeleted() const {
-    constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL;
-    return (TrailingZeros(((~ctrl & (ctrl >> 7)) | gaps) + 1) + 7) >> 3;
-  }
-
-  void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
-    constexpr uint64_t msbs = 0x8080808080808080ULL;
-    constexpr uint64_t lsbs = 0x0101010101010101ULL;
-    auto x = ctrl & msbs;
-    auto res = (~x + (x >> 7)) & ~lsbs;
-    little_endian::Store64(dst, res);
-  }
-
-  uint64_t ctrl;
-};
-
-#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
-using Group = GroupSse2Impl;
-#else
-using Group = GroupPortableImpl;
-#endif
-
-template <class Policy, class Hash, class Eq, class Alloc>
-class raw_hash_set;
-
-inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; }
-
-// PRECONDITION:
-//   IsValidCapacity(capacity)
-//   ctrl[capacity] == kSentinel
-//   ctrl[i] != kSentinel for all i < capacity
-// Applies mapping for every byte in ctrl:
-//   DELETED -> EMPTY
-//   EMPTY -> EMPTY
-//   FULL -> DELETED
-void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity);
-
-// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1.
-inline size_t NormalizeCapacity(size_t n) {
-  return n ? ~size_t{} >> LeadingZeros(n) : 1;
-}
-
-// We use 7/8th as maximum load factor.
-// For 16-wide groups, that gives an average of two empty slots per group.
-inline size_t CapacityToGrowth(size_t capacity) {
-  assert(IsValidCapacity(capacity));
-  // `capacity*7/8`
-  if (Group::kWidth == 8 && capacity == 7) {
-    // x-x/8 does not work when x==7.
-    return 6;
-  }
-  return capacity - capacity / 8;
-}
-// From desired "growth" to a lowerbound of the necessary capacity.
-// Might not be a valid one and required NormalizeCapacity().
-inline size_t GrowthToLowerboundCapacity(size_t growth) {
-  // `growth*8/7`
-  if (Group::kWidth == 8 && growth == 7) {
-    // x+(x-1)/7 does not work when x==7.
-    return 8;
-  }
-  return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7);
-}
-
-inline void AssertIsFull(ctrl_t* ctrl) {
-  ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) &&
-                        "Invalid operation on iterator. The element might have "
-                        "been erased, or the table might have rehashed.");
-}
-
-inline void AssertIsValid(ctrl_t* ctrl) {
-  ABSL_HARDENING_ASSERT((ctrl == nullptr || IsFull(*ctrl)) &&
-                        "Invalid operation on iterator. The element might have "
-                        "been erased, or the table might have rehashed.");
-}
-
-struct FindInfo {
-  size_t offset;
-  size_t probe_length;
-};
-
-// The representation of the object has two modes:
-//  - small: For capacities < kWidth-1
-//  - large: For the rest.
-//
-// Differences:
-//  - In small mode we are able to use the whole capacity. The extra control
-//  bytes give us at least one "empty" control byte to stop the iteration.
-//  This is important to make 1 a valid capacity.
-//
-//  - In small mode only the first `capacity()` control bytes after the
-//  sentinel are valid. The rest contain dummy kEmpty values that do not
-//  represent a real slot. This is important to take into account on
-//  find_first_non_full(), where we never try ShouldInsertBackwards() for
-//  small tables.
-inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; }
-
-inline probe_seq<Group::kWidth> probe(ctrl_t* ctrl, size_t hash,
-                                      size_t capacity) {
-  return probe_seq<Group::kWidth>(H1(hash, ctrl), capacity);
-}
-
-// Probes the raw_hash_set with the probe sequence for hash and returns the
-// pointer to the first empty or deleted slot.
-// NOTE: this function must work with tables having both kEmpty and kDelete
-// in one group. Such tables appears during drop_deletes_without_resize.
-//
-// This function is very useful when insertions happen and:
-// - the input is already a set
-// - there are enough slots
-// - the element with the hash is not in the table
-inline FindInfo find_first_non_full(ctrl_t* ctrl, size_t hash,
-                                    size_t capacity) {
-  auto seq = probe(ctrl, hash, capacity);
-  while (true) {
-    Group g{ctrl + seq.offset()};
-    auto mask = g.MatchEmptyOrDeleted();
-    if (mask) {
-#if !defined(NDEBUG)
-      // We want to add entropy even when ASLR is not enabled.
-      // In debug build we will randomly insert in either the front or back of
-      // the group.
-      // TODO(kfm,sbenza): revisit after we do unconditional mixing
-      if (!is_small(capacity) && ShouldInsertBackwards(hash, ctrl)) {
-        return {seq.offset(mask.HighestBitSet()), seq.index()};
-      }
-#endif
-      return {seq.offset(mask.LowestBitSet()), seq.index()};
-    }
-    seq.next();
-    assert(seq.index() < capacity && "full table!");
-  }
-}
-
-// Policy: a policy defines how to perform different operations on
-// the slots of the hashtable (see hash_policy_traits.h for the full interface
-// of policy).
-//
-// Hash: a (possibly polymorphic) functor that hashes keys of the hashtable. The
-// functor should accept a key and return size_t as hash. For best performance
-// it is important that the hash function provides high entropy across all bits
-// of the hash.
-//
-// Eq: a (possibly polymorphic) functor that compares two keys for equality. It
-// should accept two (of possibly different type) keys and return a bool: true
-// if they are equal, false if they are not. If two keys compare equal, then
-// their hash values as defined by Hash MUST be equal.
-//
-// Allocator: an Allocator
-// [https://en.cppreference.com/w/cpp/named_req/Allocator] with which
-// the storage of the hashtable will be allocated and the elements will be
-// constructed and destroyed.
-template <class Policy, class Hash, class Eq, class Alloc>
-class raw_hash_set {
-  using PolicyTraits = hash_policy_traits<Policy>;
-  using KeyArgImpl =
-      KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
-
- public:
-  using init_type = typename PolicyTraits::init_type;
-  using key_type = typename PolicyTraits::key_type;
-  // TODO(sbenza): Hide slot_type as it is an implementation detail. Needs user
-  // code fixes!
-  using slot_type = typename PolicyTraits::slot_type;
-  using allocator_type = Alloc;
-  using size_type = size_t;
-  using difference_type = ptrdiff_t;
-  using hasher = Hash;
-  using key_equal = Eq;
-  using policy_type = Policy;
-  using value_type = typename PolicyTraits::value_type;
-  using reference = value_type&;
-  using const_reference = const value_type&;
-  using pointer = typename absl::allocator_traits<
-      allocator_type>::template rebind_traits<value_type>::pointer;
-  using const_pointer = typename absl::allocator_traits<
-      allocator_type>::template rebind_traits<value_type>::const_pointer;
-
-  // Alias used for heterogeneous lookup functions.
-  // `key_arg<K>` evaluates to `K` when the functors are transparent and to
-  // `key_type` otherwise. It permits template argument deduction on `K` for the
-  // transparent case.
-  template <class K>
-  using key_arg = typename KeyArgImpl::template type<K, key_type>;
-
- private:
-  // Give an early error when key_type is not hashable/eq.
-  auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k));
-  auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k));
-
-  using Layout = absl::container_internal::Layout<ctrl_t, slot_type>;
-
-  static Layout MakeLayout(size_t capacity) {
-    assert(IsValidCapacity(capacity));
-    return Layout(capacity + Group::kWidth + 1, capacity);
-  }
-
-  using AllocTraits = absl::allocator_traits<allocator_type>;
-  using SlotAlloc = typename absl::allocator_traits<
-      allocator_type>::template rebind_alloc<slot_type>;
-  using SlotAllocTraits = typename absl::allocator_traits<
-      allocator_type>::template rebind_traits<slot_type>;
-
-  static_assert(std::is_lvalue_reference<reference>::value,
-                "Policy::element() must return a reference");
-
-  template <typename T>
-  struct SameAsElementReference
-      : std::is_same<typename std::remove_cv<
-                         typename std::remove_reference<reference>::type>::type,
-                     typename std::remove_cv<
-                         typename std::remove_reference<T>::type>::type> {};
-
-  // An enabler for insert(T&&): T must be convertible to init_type or be the
-  // same as [cv] value_type [ref].
-  // Note: we separate SameAsElementReference into its own type to avoid using
-  // reference unless we need to. MSVC doesn't seem to like it in some
-  // cases.
-  template <class T>
-  using RequiresInsertable = typename std::enable_if<
-      absl::disjunction<std::is_convertible<T, init_type>,
-                        SameAsElementReference<T>>::value,
-      int>::type;
-
-  // RequiresNotInit is a workaround for gcc prior to 7.1.
-  // See https://godbolt.org/g/Y4xsUh.
-  template <class T>
-  using RequiresNotInit =
-      typename std::enable_if<!std::is_same<T, init_type>::value, int>::type;
-
-  template <class... Ts>
-  using IsDecomposable = IsDecomposable<void, PolicyTraits, Hash, Eq, Ts...>;
-
- public:
-  static_assert(std::is_same<pointer, value_type*>::value,
-                "Allocators with custom pointer types are not supported");
-  static_assert(std::is_same<const_pointer, const value_type*>::value,
-                "Allocators with custom pointer types are not supported");
-
-  class iterator {
-    friend class raw_hash_set;
-
-   public:
-    using iterator_category = std::forward_iterator_tag;
-    using value_type = typename raw_hash_set::value_type;
-    using reference =
-        absl::conditional_t<PolicyTraits::constant_iterators::value,
-                            const value_type&, value_type&>;
-    using pointer = absl::remove_reference_t<reference>*;
-    using difference_type = typename raw_hash_set::difference_type;
-
-    iterator() {}
-
-    // PRECONDITION: not an end() iterator.
-    reference operator*() const {
-      AssertIsFull(ctrl_);
-      return PolicyTraits::element(slot_);
-    }
-
-    // PRECONDITION: not an end() iterator.
-    pointer operator->() const { return &operator*(); }
-
-    // PRECONDITION: not an end() iterator.
-    iterator& operator++() {
-      AssertIsFull(ctrl_);
-      ++ctrl_;
-      ++slot_;
-      skip_empty_or_deleted();
-      return *this;
-    }
-    // PRECONDITION: not an end() iterator.
-    iterator operator++(int) {
-      auto tmp = *this;
-      ++*this;
-      return tmp;
-    }
-
-    friend bool operator==(const iterator& a, const iterator& b) {
-      AssertIsValid(a.ctrl_);
-      AssertIsValid(b.ctrl_);
-      return a.ctrl_ == b.ctrl_;
-    }
-    friend bool operator!=(const iterator& a, const iterator& b) {
-      return !(a == b);
-    }
-
-   private:
-    iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {
-      // This assumption helps the compiler know that any non-end iterator is
-      // not equal to any end iterator.
-      ABSL_INTERNAL_ASSUME(ctrl != nullptr);
-    }
-
-    void skip_empty_or_deleted() {
-      while (IsEmptyOrDeleted(*ctrl_)) {
-        uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted();
-        ctrl_ += shift;
-        slot_ += shift;
-      }
-      if (ABSL_PREDICT_FALSE(*ctrl_ == kSentinel)) ctrl_ = nullptr;
-    }
-
-    ctrl_t* ctrl_ = nullptr;
-    // To avoid uninitialized member warnings, put slot_ in an anonymous union.
-    // The member is not initialized on singleton and end iterators.
-    union {
-      slot_type* slot_;
-    };
-  };
-
-  class const_iterator {
-    friend class raw_hash_set;
-
-   public:
-    using iterator_category = typename iterator::iterator_category;
-    using value_type = typename raw_hash_set::value_type;
-    using reference = typename raw_hash_set::const_reference;
-    using pointer = typename raw_hash_set::const_pointer;
-    using difference_type = typename raw_hash_set::difference_type;
-
-    const_iterator() {}
-    // Implicit construction from iterator.
-    const_iterator(iterator i) : inner_(std::move(i)) {}
-
-    reference operator*() const { return *inner_; }
-    pointer operator->() const { return inner_.operator->(); }
-
-    const_iterator& operator++() {
-      ++inner_;
-      return *this;
-    }
-    const_iterator operator++(int) { return inner_++; }
-
-    friend bool operator==(const const_iterator& a, const const_iterator& b) {
-      return a.inner_ == b.inner_;
-    }
-    friend bool operator!=(const const_iterator& a, const const_iterator& b) {
-      return !(a == b);
-    }
-
-   private:
-    const_iterator(const ctrl_t* ctrl, const slot_type* slot)
-        : inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot)) {}
-
-    iterator inner_;
-  };
-
-  using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>;
-  using insert_return_type = InsertReturnType<iterator, node_type>;
-
-  raw_hash_set() noexcept(
-      std::is_nothrow_default_constructible<hasher>::value&&
-          std::is_nothrow_default_constructible<key_equal>::value&&
-              std::is_nothrow_default_constructible<allocator_type>::value) {}
-
-  explicit raw_hash_set(size_t bucket_count, const hasher& hash = hasher(),
-                        const key_equal& eq = key_equal(),
-                        const allocator_type& alloc = allocator_type())
-      : ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) {
-    if (bucket_count) {
-      capacity_ = NormalizeCapacity(bucket_count);
-      initialize_slots();
-    }
-  }
-
-  raw_hash_set(size_t bucket_count, const hasher& hash,
-               const allocator_type& alloc)
-      : raw_hash_set(bucket_count, hash, key_equal(), alloc) {}
-
-  raw_hash_set(size_t bucket_count, const allocator_type& alloc)
-      : raw_hash_set(bucket_count, hasher(), key_equal(), alloc) {}
-
-  explicit raw_hash_set(const allocator_type& alloc)
-      : raw_hash_set(0, hasher(), key_equal(), alloc) {}
-
-  template <class InputIter>
-  raw_hash_set(InputIter first, InputIter last, size_t bucket_count = 0,
-               const hasher& hash = hasher(), const key_equal& eq = key_equal(),
-               const allocator_type& alloc = allocator_type())
-      : raw_hash_set(bucket_count, hash, eq, alloc) {
-    insert(first, last);
-  }
-
-  template <class InputIter>
-  raw_hash_set(InputIter first, InputIter last, size_t bucket_count,
-               const hasher& hash, const allocator_type& alloc)
-      : raw_hash_set(first, last, bucket_count, hash, key_equal(), alloc) {}
-
-  template <class InputIter>
-  raw_hash_set(InputIter first, InputIter last, size_t bucket_count,
-               const allocator_type& alloc)
-      : raw_hash_set(first, last, bucket_count, hasher(), key_equal(), alloc) {}
-
-  template <class InputIter>
-  raw_hash_set(InputIter first, InputIter last, const allocator_type& alloc)
-      : raw_hash_set(first, last, 0, hasher(), key_equal(), alloc) {}
-
-  // Instead of accepting std::initializer_list<value_type> as the first
-  // argument like std::unordered_set<value_type> does, we have two overloads
-  // that accept std::initializer_list<T> and std::initializer_list<init_type>.
-  // This is advantageous for performance.
-  //
-  //   // Turns {"abc", "def"} into std::initializer_list<std::string>, then
-  //   // copies the strings into the set.
-  //   std::unordered_set<std::string> s = {"abc", "def"};
-  //
-  //   // Turns {"abc", "def"} into std::initializer_list<const char*>, then
-  //   // copies the strings into the set.
-  //   absl::flat_hash_set<std::string> s = {"abc", "def"};
-  //
-  // The same trick is used in insert().
-  //
-  // The enabler is necessary to prevent this constructor from triggering where
-  // the copy constructor is meant to be called.
-  //
-  //   absl::flat_hash_set<int> a, b{a};
-  //
-  // RequiresNotInit<T> is a workaround for gcc prior to 7.1.
-  template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
-  raw_hash_set(std::initializer_list<T> init, size_t bucket_count = 0,
-               const hasher& hash = hasher(), const key_equal& eq = key_equal(),
-               const allocator_type& alloc = allocator_type())
-      : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {}
-
-  raw_hash_set(std::initializer_list<init_type> init, size_t bucket_count = 0,
-               const hasher& hash = hasher(), const key_equal& eq = key_equal(),
-               const allocator_type& alloc = allocator_type())
-      : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {}
-
-  template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
-  raw_hash_set(std::initializer_list<T> init, size_t bucket_count,
-               const hasher& hash, const allocator_type& alloc)
-      : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {}
-
-  raw_hash_set(std::initializer_list<init_type> init, size_t bucket_count,
-               const hasher& hash, const allocator_type& alloc)
-      : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {}
-
-  template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
-  raw_hash_set(std::initializer_list<T> init, size_t bucket_count,
-               const allocator_type& alloc)
-      : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {}
-
-  raw_hash_set(std::initializer_list<init_type> init, size_t bucket_count,
-               const allocator_type& alloc)
-      : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {}
-
-  template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
-  raw_hash_set(std::initializer_list<T> init, const allocator_type& alloc)
-      : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {}
-
-  raw_hash_set(std::initializer_list<init_type> init,
-               const allocator_type& alloc)
-      : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {}
-
-  raw_hash_set(const raw_hash_set& that)
-      : raw_hash_set(that, AllocTraits::select_on_container_copy_construction(
-                               that.alloc_ref())) {}
-
-  raw_hash_set(const raw_hash_set& that, const allocator_type& a)
-      : raw_hash_set(0, that.hash_ref(), that.eq_ref(), a) {
-    reserve(that.size());
-    // Because the table is guaranteed to be empty, we can do something faster
-    // than a full `insert`.
-    for (const auto& v : that) {
-      const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v);
-      auto target = find_first_non_full(ctrl_, hash, capacity_);
-      set_ctrl(target.offset, H2(hash));
-      emplace_at(target.offset, v);
-      infoz_.RecordInsert(hash, target.probe_length);
-    }
-    size_ = that.size();
-    growth_left() -= that.size();
-  }
-
-  raw_hash_set(raw_hash_set&& that) noexcept(
-      std::is_nothrow_copy_constructible<hasher>::value&&
-          std::is_nothrow_copy_constructible<key_equal>::value&&
-              std::is_nothrow_copy_constructible<allocator_type>::value)
-      : ctrl_(absl::exchange(that.ctrl_, EmptyGroup())),
-        slots_(absl::exchange(that.slots_, nullptr)),
-        size_(absl::exchange(that.size_, 0)),
-        capacity_(absl::exchange(that.capacity_, 0)),
-        infoz_(absl::exchange(that.infoz_, HashtablezInfoHandle())),
-        // Hash, equality and allocator are copied instead of moved because
-        // `that` must be left valid. If Hash is std::function<Key>, moving it
-        // would create a nullptr functor that cannot be called.
-        settings_(that.settings_) {
-    // growth_left was copied above, reset the one from `that`.
-    that.growth_left() = 0;
-  }
-
-  raw_hash_set(raw_hash_set&& that, const allocator_type& a)
-      : ctrl_(EmptyGroup()),
-        slots_(nullptr),
-        size_(0),
-        capacity_(0),
-        settings_(0, that.hash_ref(), that.eq_ref(), a) {
-    if (a == that.alloc_ref()) {
-      std::swap(ctrl_, that.ctrl_);
-      std::swap(slots_, that.slots_);
-      std::swap(size_, that.size_);
-      std::swap(capacity_, that.capacity_);
-      std::swap(growth_left(), that.growth_left());
-      std::swap(infoz_, that.infoz_);
-    } else {
-      reserve(that.size());
-      // Note: this will copy elements of dense_set and unordered_set instead of
-      // moving them. This can be fixed if it ever becomes an issue.
-      for (auto& elem : that) insert(std::move(elem));
-    }
-  }
-
-  raw_hash_set& operator=(const raw_hash_set& that) {
-    raw_hash_set tmp(that,
-                     AllocTraits::propagate_on_container_copy_assignment::value
-                         ? that.alloc_ref()
-                         : alloc_ref());
-    swap(tmp);
-    return *this;
-  }
-
-  raw_hash_set& operator=(raw_hash_set&& that) noexcept(
-      absl::allocator_traits<allocator_type>::is_always_equal::value&&
-          std::is_nothrow_move_assignable<hasher>::value&&
-              std::is_nothrow_move_assignable<key_equal>::value) {
-    // TODO(sbenza): We should only use the operations from the noexcept clause
-    // to make sure we actually adhere to that contract.
-    return move_assign(
-        std::move(that),
-        typename AllocTraits::propagate_on_container_move_assignment());
-  }
-
-  ~raw_hash_set() { destroy_slots(); }
-
-  iterator begin() {
-    auto it = iterator_at(0);
-    it.skip_empty_or_deleted();
-    return it;
-  }
-  iterator end() { return {}; }
-
-  const_iterator begin() const {
-    return const_cast<raw_hash_set*>(this)->begin();
-  }
-  const_iterator end() const { return {}; }
-  const_iterator cbegin() const { return begin(); }
-  const_iterator cend() const { return end(); }
-
-  bool empty() const { return !size(); }
-  size_t size() const { return size_; }
-  size_t capacity() const { return capacity_; }
-  size_t max_size() const { return (std::numeric_limits<size_t>::max)(); }
-
-  ABSL_ATTRIBUTE_REINITIALIZES void clear() {
-    // Iterating over this container is O(bucket_count()). When bucket_count()
-    // is much greater than size(), iteration becomes prohibitively expensive.
-    // For clear() it is more important to reuse the allocated array when the
-    // container is small because allocation takes comparatively long time
-    // compared to destruction of the elements of the container. So we pick the
-    // largest bucket_count() threshold for which iteration is still fast and
-    // past that we simply deallocate the array.
-    if (capacity_ > 127) {
-      destroy_slots();
-    } else if (capacity_) {
-      for (size_t i = 0; i != capacity_; ++i) {
-        if (IsFull(ctrl_[i])) {
-          PolicyTraits::destroy(&alloc_ref(), slots_ + i);
-        }
-      }
-      size_ = 0;
-      reset_ctrl();
-      reset_growth_left();
-    }
-    assert(empty());
-    infoz_.RecordStorageChanged(0, capacity_);
-  }
-
-  // This overload kicks in when the argument is an rvalue of insertable and
-  // decomposable type other than init_type.
-  //
-  //   flat_hash_map<std::string, int> m;
-  //   m.insert(std::make_pair("abc", 42));
-  // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc
-  // bug.
-  template <class T, RequiresInsertable<T> = 0,
-            class T2 = T,
-            typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0,
-            T* = nullptr>
-  std::pair<iterator, bool> insert(T&& value) {
-    return emplace(std::forward<T>(value));
-  }
-
-  // This overload kicks in when the argument is a bitfield or an lvalue of
-  // insertable and decomposable type.
-  //
-  //   union { int n : 1; };
-  //   flat_hash_set<int> s;
-  //   s.insert(n);
-  //
-  //   flat_hash_set<std::string> s;
-  //   const char* p = "hello";
-  //   s.insert(p);
-  //
-  // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
-  // RequiresInsertable<T> with RequiresInsertable<const T&>.
-  // We are hitting this bug: https://godbolt.org/g/1Vht4f.
-  template <
-      class T, RequiresInsertable<T> = 0,
-      typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
-  std::pair<iterator, bool> insert(const T& value) {
-    return emplace(value);
-  }
-
-  // This overload kicks in when the argument is an rvalue of init_type. Its
-  // purpose is to handle brace-init-list arguments.
-  //
-  //   flat_hash_map<std::string, int> s;
-  //   s.insert({"abc", 42});
-  std::pair<iterator, bool> insert(init_type&& value) {
-    return emplace(std::move(value));
-  }
-
-  // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc
-  // bug.
-  template <class T, RequiresInsertable<T> = 0, class T2 = T,
-            typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0,
-            T* = nullptr>
-  iterator insert(const_iterator, T&& value) {
-    return insert(std::forward<T>(value)).first;
-  }
-
-  // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
-  // RequiresInsertable<T> with RequiresInsertable<const T&>.
-  // We are hitting this bug: https://godbolt.org/g/1Vht4f.
-  template <
-      class T, RequiresInsertable<T> = 0,
-      typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
-  iterator insert(const_iterator, const T& value) {
-    return insert(value).first;
-  }
-
-  iterator insert(const_iterator, init_type&& value) {
-    return insert(std::move(value)).first;
-  }
-
-  template <class InputIt>
-  void insert(InputIt first, InputIt last) {
-    for (; first != last; ++first) insert(*first);
-  }
-
-  template <class T, RequiresNotInit<T> = 0, RequiresInsertable<const T&> = 0>
-  void insert(std::initializer_list<T> ilist) {
-    insert(ilist.begin(), ilist.end());
-  }
-
-  void insert(std::initializer_list<init_type> ilist) {
-    insert(ilist.begin(), ilist.end());
-  }
-
-  insert_return_type insert(node_type&& node) {
-    if (!node) return {end(), false, node_type()};
-    const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node));
-    auto res = PolicyTraits::apply(
-        InsertSlot<false>{*this, std::move(*CommonAccess::GetSlot(node))},
-        elem);
-    if (res.second) {
-      CommonAccess::Reset(&node);
-      return {res.first, true, node_type()};
-    } else {
-      return {res.first, false, std::move(node)};
-    }
-  }
-
-  iterator insert(const_iterator, node_type&& node) {
-    auto res = insert(std::move(node));
-    node = std::move(res.node);
-    return res.position;
-  }
-
-  // This overload kicks in if we can deduce the key from args. This enables us
-  // to avoid constructing value_type if an entry with the same key already
-  // exists.
-  //
-  // For example:
-  //
-  //   flat_hash_map<std::string, std::string> m = {{"abc", "def"}};
-  //   // Creates no std::string copies and makes no heap allocations.
-  //   m.emplace("abc", "xyz");
-  template <class... Args, typename std::enable_if<
-                               IsDecomposable<Args...>::value, int>::type = 0>
-  std::pair<iterator, bool> emplace(Args&&... args) {
-    return PolicyTraits::apply(EmplaceDecomposable{*this},
-                               std::forward<Args>(args)...);
-  }
-
-  // This overload kicks in if we cannot deduce the key from args. It constructs
-  // value_type unconditionally and then either moves it into the table or
-  // destroys.
-  template <class... Args, typename std::enable_if<
-                               !IsDecomposable<Args...>::value, int>::type = 0>
-  std::pair<iterator, bool> emplace(Args&&... args) {
-    alignas(slot_type) unsigned char raw[sizeof(slot_type)];
-    slot_type* slot = reinterpret_cast<slot_type*>(&raw);
-
-    PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
-    const auto& elem = PolicyTraits::element(slot);
-    return PolicyTraits::apply(InsertSlot<true>{*this, std::move(*slot)}, elem);
-  }
-
-  template <class... Args>
-  iterator emplace_hint(const_iterator, Args&&... args) {
-    return emplace(std::forward<Args>(args)...).first;
-  }
-
-  // Extension API: support for lazy emplace.
-  //
-  // Looks up key in the table. If found, returns the iterator to the element.
-  // Otherwise calls `f` with one argument of type `raw_hash_set::constructor`.
-  //
-  // `f` must abide by several restrictions:
-  //  - it MUST call `raw_hash_set::constructor` with arguments as if a
-  //    `raw_hash_set::value_type` is constructed,
-  //  - it MUST NOT access the container before the call to
-  //    `raw_hash_set::constructor`, and
-  //  - it MUST NOT erase the lazily emplaced element.
-  // Doing any of these is undefined behavior.
-  //
-  // For example:
-  //
-  //   std::unordered_set<ArenaString> s;
-  //   // Makes ArenaStr even if "abc" is in the map.
-  //   s.insert(ArenaString(&arena, "abc"));
-  //
-  //   flat_hash_set<ArenaStr> s;
-  //   // Makes ArenaStr only if "abc" is not in the map.
-  //   s.lazy_emplace("abc", [&](const constructor& ctor) {
-  //     ctor(&arena, "abc");
-  //   });
-  //
-  // WARNING: This API is currently experimental. If there is a way to implement
-  // the same thing with the rest of the API, prefer that.
-  class constructor {
-    friend class raw_hash_set;
-
-   public:
-    template <class... Args>
-    void operator()(Args&&... args) const {
-      assert(*slot_);
-      PolicyTraits::construct(alloc_, *slot_, std::forward<Args>(args)...);
-      *slot_ = nullptr;
-    }
-
-   private:
-    constructor(allocator_type* a, slot_type** slot) : alloc_(a), slot_(slot) {}
-
-    allocator_type* alloc_;
-    slot_type** slot_;
-  };
-
-  template <class K = key_type, class F>
-  iterator lazy_emplace(const key_arg<K>& key, F&& f) {
-    auto res = find_or_prepare_insert(key);
-    if (res.second) {
-      slot_type* slot = slots_ + res.first;
-      std::forward<F>(f)(constructor(&alloc_ref(), &slot));
-      assert(!slot);
-    }
-    return iterator_at(res.first);
-  }
-
-  // Extension API: support for heterogeneous keys.
-  //
-  //   std::unordered_set<std::string> s;
-  //   // Turns "abc" into std::string.
-  //   s.erase("abc");
-  //
-  //   flat_hash_set<std::string> s;
-  //   // Uses "abc" directly without copying it into std::string.
-  //   s.erase("abc");
-  template <class K = key_type>
-  size_type erase(const key_arg<K>& key) {
-    auto it = find(key);
-    if (it == end()) return 0;
-    erase(it);
-    return 1;
-  }
-
-  // Erases the element pointed to by `it`.  Unlike `std::unordered_set::erase`,
-  // this method returns void to reduce algorithmic complexity to O(1).  The
-  // iterator is invalidated, so any increment should be done before calling
-  // erase.  In order to erase while iterating across a map, use the following
-  // idiom (which also works for standard containers):
-  //
-  // for (auto it = m.begin(), end = m.end(); it != end;) {
-  //   // `erase()` will invalidate `it`, so advance `it` first.
-  //   auto copy_it = it++;
-  //   if (<pred>) {
-  //     m.erase(copy_it);
-  //   }
-  // }
-  void erase(const_iterator cit) { erase(cit.inner_); }
-
-  // This overload is necessary because otherwise erase<K>(const K&) would be
-  // a better match if non-const iterator is passed as an argument.
-  void erase(iterator it) {
-    AssertIsFull(it.ctrl_);
-    PolicyTraits::destroy(&alloc_ref(), it.slot_);
-    erase_meta_only(it);
-  }
-
-  iterator erase(const_iterator first, const_iterator last) {
-    while (first != last) {
-      erase(first++);
-    }
-    return last.inner_;
-  }
-
-  // Moves elements from `src` into `this`.
-  // If the element already exists in `this`, it is left unmodified in `src`.
-  template <typename H, typename E>
-  void merge(raw_hash_set<Policy, H, E, Alloc>& src) {  // NOLINT
-    assert(this != &src);
-    for (auto it = src.begin(), e = src.end(); it != e;) {
-      auto next = std::next(it);
-      if (PolicyTraits::apply(InsertSlot<false>{*this, std::move(*it.slot_)},
-                              PolicyTraits::element(it.slot_))
-              .second) {
-        src.erase_meta_only(it);
-      }
-      it = next;
-    }
-  }
-
-  template <typename H, typename E>
-  void merge(raw_hash_set<Policy, H, E, Alloc>&& src) {
-    merge(src);
-  }
-
-  node_type extract(const_iterator position) {
-    AssertIsFull(position.inner_.ctrl_);
-    auto node =
-        CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_);
-    erase_meta_only(position);
-    return node;
-  }
-
-  template <
-      class K = key_type,
-      typename std::enable_if<!std::is_same<K, iterator>::value, int>::type = 0>
-  node_type extract(const key_arg<K>& key) {
-    auto it = find(key);
-    return it == end() ? node_type() : extract(const_iterator{it});
-  }
-
-  void swap(raw_hash_set& that) noexcept(
-      IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() &&
-      IsNoThrowSwappable<allocator_type>(
-          typename AllocTraits::propagate_on_container_swap{})) {
-    using std::swap;
-    swap(ctrl_, that.ctrl_);
-    swap(slots_, that.slots_);
-    swap(size_, that.size_);
-    swap(capacity_, that.capacity_);
-    swap(growth_left(), that.growth_left());
-    swap(hash_ref(), that.hash_ref());
-    swap(eq_ref(), that.eq_ref());
-    swap(infoz_, that.infoz_);
-    SwapAlloc(alloc_ref(), that.alloc_ref(),
-              typename AllocTraits::propagate_on_container_swap{});
-  }
-
-  void rehash(size_t n) {
-    if (n == 0 && capacity_ == 0) return;
-    if (n == 0 && size_ == 0) {
-      destroy_slots();
-      infoz_.RecordStorageChanged(0, 0);
-      return;
-    }
-    // bitor is a faster way of doing `max` here. We will round up to the next
-    // power-of-2-minus-1, so bitor is good enough.
-    auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size()));
-    // n == 0 unconditionally rehashes as per the standard.
-    if (n == 0 || m > capacity_) {
-      resize(m);
-    }
-  }
-
-  void reserve(size_t n) {
-    size_t m = GrowthToLowerboundCapacity(n);
-    if (m > capacity_) {
-      resize(NormalizeCapacity(m));
-    }
-  }
-
-  // Extension API: support for heterogeneous keys.
-  //
-  //   std::unordered_set<std::string> s;
-  //   // Turns "abc" into std::string.
-  //   s.count("abc");
-  //
-  //   ch_set<std::string> s;
-  //   // Uses "abc" directly without copying it into std::string.
-  //   s.count("abc");
-  template <class K = key_type>
-  size_t count(const key_arg<K>& key) const {
-    return find(key) == end() ? 0 : 1;
-  }
-
-  // Issues CPU prefetch instructions for the memory needed to find or insert
-  // a key.  Like all lookup functions, this support heterogeneous keys.
-  //
-  // NOTE: This is a very low level operation and should not be used without
-  // specific benchmarks indicating its importance.
-  template <class K = key_type>
-  void prefetch(const key_arg<K>& key) const {
-    (void)key;
-#if defined(__GNUC__)
-    auto seq = probe(ctrl_, hash_ref()(key), capacity_);
-    __builtin_prefetch(static_cast<const void*>(ctrl_ + seq.offset()));
-    __builtin_prefetch(static_cast<const void*>(slots_ + seq.offset()));
-#endif  // __GNUC__
-  }
-
-  // The API of find() has two extensions.
-  //
-  // 1. The hash can be passed by the user. It must be equal to the hash of the
-  // key.
-  //
-  // 2. The type of the key argument doesn't have to be key_type. This is so
-  // called heterogeneous key support.
-  template <class K = key_type>
-  iterator find(const key_arg<K>& key, size_t hash) {
-    auto seq = probe(ctrl_, hash, capacity_);
-    while (true) {
-      Group g{ctrl_ + seq.offset()};
-      for (int i : g.Match(H2(hash))) {
-        if (ABSL_PREDICT_TRUE(PolicyTraits::apply(
-                EqualElement<K>{key, eq_ref()},
-                PolicyTraits::element(slots_ + seq.offset(i)))))
-          return iterator_at(seq.offset(i));
-      }
-      if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return end();
-      seq.next();
-      assert(seq.index() < capacity_ && "full table!");
-    }
-  }
-  template <class K = key_type>
-  iterator find(const key_arg<K>& key) {
-    return find(key, hash_ref()(key));
-  }
-
-  template <class K = key_type>
-  const_iterator find(const key_arg<K>& key, size_t hash) const {
-    return const_cast<raw_hash_set*>(this)->find(key, hash);
-  }
-  template <class K = key_type>
-  const_iterator find(const key_arg<K>& key) const {
-    return find(key, hash_ref()(key));
-  }
-
-  template <class K = key_type>
-  bool contains(const key_arg<K>& key) const {
-    return find(key) != end();
-  }
-
-  template <class K = key_type>
-  std::pair<iterator, iterator> equal_range(const key_arg<K>& key) {
-    auto it = find(key);
-    if (it != end()) return {it, std::next(it)};
-    return {it, it};
-  }
-  template <class K = key_type>
-  std::pair<const_iterator, const_iterator> equal_range(
-      const key_arg<K>& key) const {
-    auto it = find(key);
-    if (it != end()) return {it, std::next(it)};
-    return {it, it};
-  }
-
-  size_t bucket_count() const { return capacity_; }
-  float load_factor() const {
-    return capacity_ ? static_cast<double>(size()) / capacity_ : 0.0;
-  }
-  float max_load_factor() const { return 1.0f; }
-  void max_load_factor(float) {
-    // Does nothing.
-  }
-
-  hasher hash_function() const { return hash_ref(); }
-  key_equal key_eq() const { return eq_ref(); }
-  allocator_type get_allocator() const { return alloc_ref(); }
-
-  friend bool operator==(const raw_hash_set& a, const raw_hash_set& b) {
-    if (a.size() != b.size()) return false;
-    const raw_hash_set* outer = &a;
-    const raw_hash_set* inner = &b;
-    if (outer->capacity() > inner->capacity()) std::swap(outer, inner);
-    for (const value_type& elem : *outer)
-      if (!inner->has_element(elem)) return false;
-    return true;
-  }
-
-  friend bool operator!=(const raw_hash_set& a, const raw_hash_set& b) {
-    return !(a == b);
-  }
-
-  friend void swap(raw_hash_set& a,
-                   raw_hash_set& b) noexcept(noexcept(a.swap(b))) {
-    a.swap(b);
-  }
-
- private:
-  template <class Container, typename Enabler>
-  friend struct absl::container_internal::hashtable_debug_internal::
-      HashtableDebugAccess;
-
-  struct FindElement {
-    template <class K, class... Args>
-    const_iterator operator()(const K& key, Args&&...) const {
-      return s.find(key);
-    }
-    const raw_hash_set& s;
-  };
-
-  struct HashElement {
-    template <class K, class... Args>
-    size_t operator()(const K& key, Args&&...) const {
-      return h(key);
-    }
-    const hasher& h;
-  };
-
-  template <class K1>
-  struct EqualElement {
-    template <class K2, class... Args>
-    bool operator()(const K2& lhs, Args&&...) const {
-      return eq(lhs, rhs);
-    }
-    const K1& rhs;
-    const key_equal& eq;
-  };
-
-  struct EmplaceDecomposable {
-    template <class K, class... Args>
-    std::pair<iterator, bool> operator()(const K& key, Args&&... args) const {
-      auto res = s.find_or_prepare_insert(key);
-      if (res.second) {
-        s.emplace_at(res.first, std::forward<Args>(args)...);
-      }
-      return {s.iterator_at(res.first), res.second};
-    }
-    raw_hash_set& s;
-  };
-
-  template <bool do_destroy>
-  struct InsertSlot {
-    template <class K, class... Args>
-    std::pair<iterator, bool> operator()(const K& key, Args&&...) && {
-      auto res = s.find_or_prepare_insert(key);
-      if (res.second) {
-        PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot);
-      } else if (do_destroy) {
-        PolicyTraits::destroy(&s.alloc_ref(), &slot);
-      }
-      return {s.iterator_at(res.first), res.second};
-    }
-    raw_hash_set& s;
-    // Constructed slot. Either moved into place or destroyed.
-    slot_type&& slot;
-  };
-
-  // "erases" the object from the container, except that it doesn't actually
-  // destroy the object. It only updates all the metadata of the class.
-  // This can be used in conjunction with Policy::transfer to move the object to
-  // another place.
-  void erase_meta_only(const_iterator it) {
-    assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator");
-    --size_;
-    const size_t index = it.inner_.ctrl_ - ctrl_;
-    const size_t index_before = (index - Group::kWidth) & capacity_;
-    const auto empty_after = Group(it.inner_.ctrl_).MatchEmpty();
-    const auto empty_before = Group(ctrl_ + index_before).MatchEmpty();
-
-    // We count how many consecutive non empties we have to the right and to the
-    // left of `it`. If the sum is >= kWidth then there is at least one probe
-    // window that might have seen a full group.
-    bool was_never_full =
-        empty_before && empty_after &&
-        static_cast<size_t>(empty_after.TrailingZeros() +
-                            empty_before.LeadingZeros()) < Group::kWidth;
-
-    set_ctrl(index, was_never_full ? kEmpty : kDeleted);
-    growth_left() += was_never_full;
-    infoz_.RecordErase();
-  }
-
-  void initialize_slots() {
-    assert(capacity_);
-    // Folks with custom allocators often make unwarranted assumptions about the
-    // behavior of their classes vis-a-vis trivial destructability and what
-    // calls they will or wont make.  Avoid sampling for people with custom
-    // allocators to get us out of this mess.  This is not a hard guarantee but
-    // a workaround while we plan the exact guarantee we want to provide.
-    //
-    // People are often sloppy with the exact type of their allocator (sometimes
-    // it has an extra const or is missing the pair, but rebinds made it work
-    // anyway).  To avoid the ambiguity, we work off SlotAlloc which we have
-    // bound more carefully.
-    if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value &&
-        slots_ == nullptr) {
-      infoz_ = Sample();
-    }
-
-    auto layout = MakeLayout(capacity_);
-    char* mem = static_cast<char*>(
-        Allocate<Layout::Alignment()>(&alloc_ref(), layout.AllocSize()));
-    ctrl_ = reinterpret_cast<ctrl_t*>(layout.template Pointer<0>(mem));
-    slots_ = layout.template Pointer<1>(mem);
-    reset_ctrl();
-    reset_growth_left();
-    infoz_.RecordStorageChanged(size_, capacity_);
-  }
-
-  void destroy_slots() {
-    if (!capacity_) return;
-    for (size_t i = 0; i != capacity_; ++i) {
-      if (IsFull(ctrl_[i])) {
-        PolicyTraits::destroy(&alloc_ref(), slots_ + i);
-      }
-    }
-    auto layout = MakeLayout(capacity_);
-    // Unpoison before returning the memory to the allocator.
-    SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
-    Deallocate<Layout::Alignment()>(&alloc_ref(), ctrl_, layout.AllocSize());
-    ctrl_ = EmptyGroup();
-    slots_ = nullptr;
-    size_ = 0;
-    capacity_ = 0;
-    growth_left() = 0;
-  }
-
-  void resize(size_t new_capacity) {
-    assert(IsValidCapacity(new_capacity));
-    auto* old_ctrl = ctrl_;
-    auto* old_slots = slots_;
-    const size_t old_capacity = capacity_;
-    capacity_ = new_capacity;
-    initialize_slots();
-
-    size_t total_probe_length = 0;
-    for (size_t i = 0; i != old_capacity; ++i) {
-      if (IsFull(old_ctrl[i])) {
-        size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
-                                          PolicyTraits::element(old_slots + i));
-        auto target = find_first_non_full(ctrl_, hash, capacity_);
-        size_t new_i = target.offset;
-        total_probe_length += target.probe_length;
-        set_ctrl(new_i, H2(hash));
-        PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i);
-      }
-    }
-    if (old_capacity) {
-      SanitizerUnpoisonMemoryRegion(old_slots,
-                                    sizeof(slot_type) * old_capacity);
-      auto layout = MakeLayout(old_capacity);
-      Deallocate<Layout::Alignment()>(&alloc_ref(), old_ctrl,
-                                      layout.AllocSize());
-    }
-    infoz_.RecordRehash(total_probe_length);
-  }
-
-  void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE {
-    assert(IsValidCapacity(capacity_));
-    assert(!is_small(capacity_));
-    // Algorithm:
-    // - mark all DELETED slots as EMPTY
-    // - mark all FULL slots as DELETED
-    // - for each slot marked as DELETED
-    //     hash = Hash(element)
-    //     target = find_first_non_full(hash)
-    //     if target is in the same group
-    //       mark slot as FULL
-    //     else if target is EMPTY
-    //       transfer element to target
-    //       mark slot as EMPTY
-    //       mark target as FULL
-    //     else if target is DELETED
-    //       swap current element with target element
-    //       mark target as FULL
-    //       repeat procedure for current slot with moved from element (target)
-    ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_);
-    alignas(slot_type) unsigned char raw[sizeof(slot_type)];
-    size_t total_probe_length = 0;
-    slot_type* slot = reinterpret_cast<slot_type*>(&raw);
-    for (size_t i = 0; i != capacity_; ++i) {
-      if (!IsDeleted(ctrl_[i])) continue;
-      size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
-                                        PolicyTraits::element(slots_ + i));
-      auto target = find_first_non_full(ctrl_, hash, capacity_);
-      size_t new_i = target.offset;
-      total_probe_length += target.probe_length;
-
-      // Verify if the old and new i fall within the same group wrt the hash.
-      // If they do, we don't need to move the object as it falls already in the
-      // best probe we can.
-      const auto probe_index = [&](size_t pos) {
-        return ((pos - probe(ctrl_, hash, capacity_).offset()) & capacity_) /
-               Group::kWidth;
-      };
-
-      // Element doesn't move.
-      if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
-        set_ctrl(i, H2(hash));
-        continue;
-      }
-      if (IsEmpty(ctrl_[new_i])) {
-        // Transfer element to the empty spot.
-        // set_ctrl poisons/unpoisons the slots so we have to call it at the
-        // right time.
-        set_ctrl(new_i, H2(hash));
-        PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i);
-        set_ctrl(i, kEmpty);
-      } else {
-        assert(IsDeleted(ctrl_[new_i]));
-        set_ctrl(new_i, H2(hash));
-        // Until we are done rehashing, DELETED marks previously FULL slots.
-        // Swap i and new_i elements.
-        PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i);
-        PolicyTraits::transfer(&alloc_ref(), slots_ + i, slots_ + new_i);
-        PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slot);
-        --i;  // repeat
-      }
-    }
-    reset_growth_left();
-    infoz_.RecordRehash(total_probe_length);
-  }
-
-  void rehash_and_grow_if_necessary() {
-    if (capacity_ == 0) {
-      resize(1);
-    } else if (size() <= CapacityToGrowth(capacity()) / 2) {
-      // Squash DELETED without growing if there is enough capacity.
-      drop_deletes_without_resize();
-    } else {
-      // Otherwise grow the container.
-      resize(capacity_ * 2 + 1);
-    }
-  }
-
-  bool has_element(const value_type& elem) const {
-    size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem);
-    auto seq = probe(ctrl_, hash, capacity_);
-    while (true) {
-      Group g{ctrl_ + seq.offset()};
-      for (int i : g.Match(H2(hash))) {
-        if (ABSL_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset(i)) ==
-                              elem))
-          return true;
-      }
-      if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false;
-      seq.next();
-      assert(seq.index() < capacity_ && "full table!");
-    }
-    return false;
-  }
-
-  // TODO(alkis): Optimize this assuming *this and that don't overlap.
-  raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) {
-    raw_hash_set tmp(std::move(that));
-    swap(tmp);
-    return *this;
-  }
-  raw_hash_set& move_assign(raw_hash_set&& that, std::false_type) {
-    raw_hash_set tmp(std::move(that), alloc_ref());
-    swap(tmp);
-    return *this;
-  }
-
- protected:
-  template <class K>
-  std::pair<size_t, bool> find_or_prepare_insert(const K& key) {
-    auto hash = hash_ref()(key);
-    auto seq = probe(ctrl_, hash, capacity_);
-    while (true) {
-      Group g{ctrl_ + seq.offset()};
-      for (int i : g.Match(H2(hash))) {
-        if (ABSL_PREDICT_TRUE(PolicyTraits::apply(
-                EqualElement<K>{key, eq_ref()},
-                PolicyTraits::element(slots_ + seq.offset(i)))))
-          return {seq.offset(i), false};
-      }
-      if (ABSL_PREDICT_TRUE(g.MatchEmpty())) break;
-      seq.next();
-      assert(seq.index() < capacity_ && "full table!");
-    }
-    return {prepare_insert(hash), true};
-  }
-
-  size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE {
-    auto target = find_first_non_full(ctrl_, hash, capacity_);
-    if (ABSL_PREDICT_FALSE(growth_left() == 0 &&
-                           !IsDeleted(ctrl_[target.offset]))) {
-      rehash_and_grow_if_necessary();
-      target = find_first_non_full(ctrl_, hash, capacity_);
-    }
-    ++size_;
-    growth_left() -= IsEmpty(ctrl_[target.offset]);
-    set_ctrl(target.offset, H2(hash));
-    infoz_.RecordInsert(hash, target.probe_length);
-    return target.offset;
-  }
-
-  // Constructs the value in the space pointed by the iterator. This only works
-  // after an unsuccessful find_or_prepare_insert() and before any other
-  // modifications happen in the raw_hash_set.
-  //
-  // PRECONDITION: i is an index returned from find_or_prepare_insert(k), where
-  // k is the key decomposed from `forward<Args>(args)...`, and the bool
-  // returned by find_or_prepare_insert(k) was true.
-  // POSTCONDITION: *m.iterator_at(i) == value_type(forward<Args>(args)...).
-  template <class... Args>
-  void emplace_at(size_t i, Args&&... args) {
-    PolicyTraits::construct(&alloc_ref(), slots_ + i,
-                            std::forward<Args>(args)...);
-
-    assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) ==
-               iterator_at(i) &&
-           "constructed value does not match the lookup key");
-  }
-
-  iterator iterator_at(size_t i) { return {ctrl_ + i, slots_ + i}; }
-  const_iterator iterator_at(size_t i) const { return {ctrl_ + i, slots_ + i}; }
-
- private:
-  friend struct RawHashSetTestOnlyAccess;
-
-  // Reset all ctrl bytes back to kEmpty, except the sentinel.
-  void reset_ctrl() {
-    std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth);
-    ctrl_[capacity_] = kSentinel;
-    SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
-  }
-
-  void reset_growth_left() {
-    growth_left() = CapacityToGrowth(capacity()) - size_;
-  }
-
-  // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at
-  // the end too.
-  void set_ctrl(size_t i, ctrl_t h) {
-    assert(i < capacity_);
-
-    if (IsFull(h)) {
-      SanitizerUnpoisonObject(slots_ + i);
-    } else {
-      SanitizerPoisonObject(slots_ + i);
-    }
-
-    ctrl_[i] = h;
-    ctrl_[((i - Group::kWidth) & capacity_) + 1 +
-          ((Group::kWidth - 1) & capacity_)] = h;
-  }
-
-  size_t& growth_left() { return settings_.template get<0>(); }
-
-  hasher& hash_ref() { return settings_.template get<1>(); }
-  const hasher& hash_ref() const { return settings_.template get<1>(); }
-  key_equal& eq_ref() { return settings_.template get<2>(); }
-  const key_equal& eq_ref() const { return settings_.template get<2>(); }
-  allocator_type& alloc_ref() { return settings_.template get<3>(); }
-  const allocator_type& alloc_ref() const {
-    return settings_.template get<3>();
-  }
-
-  // TODO(alkis): Investigate removing some of these fields:
-  // - ctrl/slots can be derived from each other
-  // - size can be moved into the slot array
-  ctrl_t* ctrl_ = EmptyGroup();    // [(capacity + 1) * ctrl_t]
-  slot_type* slots_ = nullptr;     // [capacity * slot_type]
-  size_t size_ = 0;                // number of full slots
-  size_t capacity_ = 0;            // total number of slots
-  HashtablezInfoHandle infoz_;
-  absl::container_internal::CompressedTuple<size_t /* growth_left */, hasher,
-                                            key_equal, allocator_type>
-      settings_{0, hasher{}, key_equal{}, allocator_type{}};
-};
-
-// Erases all elements that satisfy the predicate `pred` from the container `c`.
-template <typename P, typename H, typename E, typename A, typename Predicate>
-void EraseIf(Predicate pred, raw_hash_set<P, H, E, A>* c) {
-  for (auto it = c->begin(), last = c->end(); it != last;) {
-    auto copy_it = it++;
-    if (pred(*copy_it)) {
-      c->erase(copy_it);
-    }
-  }
-}
-
-namespace hashtable_debug_internal {
-template <typename Set>
-struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> {
-  using Traits = typename Set::PolicyTraits;
-  using Slot = typename Traits::slot_type;
-
-  static size_t GetNumProbes(const Set& set,
-                             const typename Set::key_type& key) {
-    size_t num_probes = 0;
-    size_t hash = set.hash_ref()(key);
-    auto seq = probe(set.ctrl_, hash, set.capacity_);
-    while (true) {
-      container_internal::Group g{set.ctrl_ + seq.offset()};
-      for (int i : g.Match(container_internal::H2(hash))) {
-        if (Traits::apply(
-                typename Set::template EqualElement<typename Set::key_type>{
-                    key, set.eq_ref()},
-                Traits::element(set.slots_ + seq.offset(i))))
-          return num_probes;
-        ++num_probes;
-      }
-      if (g.MatchEmpty()) return num_probes;
-      seq.next();
-      ++num_probes;
-    }
-  }
-
-  static size_t AllocatedByteSize(const Set& c) {
-    size_t capacity = c.capacity_;
-    if (capacity == 0) return 0;
-    auto layout = Set::MakeLayout(capacity);
-    size_t m = layout.AllocSize();
-
-    size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
-    if (per_slot != ~size_t{}) {
-      m += per_slot * c.size();
-    } else {
-      for (size_t i = 0; i != capacity; ++i) {
-        if (container_internal::IsFull(c.ctrl_[i])) {
-          m += Traits::space_used(c.slots_ + i);
-        }
-      }
-    }
-    return m;
-  }
-
-  static size_t LowerBoundAllocatedByteSize(size_t size) {
-    size_t capacity = GrowthToLowerboundCapacity(size);
-    if (capacity == 0) return 0;
-    auto layout = Set::MakeLayout(NormalizeCapacity(capacity));
-    size_t m = layout.AllocSize();
-    size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
-    if (per_slot != ~size_t{}) {
-      m += per_slot * size;
-    }
-    return m;
-  }
-};
-
-}  // namespace hashtable_debug_internal
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/raw_hash_set_allocator_test.cc b/third_party/abseil_cpp/absl/container/internal/raw_hash_set_allocator_test.cc
deleted file mode 100644
index e73f53fd63..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/raw_hash_set_allocator_test.cc
+++ /dev/null
@@ -1,505 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <limits>
-#include <scoped_allocator>
-
-#include "gtest/gtest.h"
-#include "absl/container/internal/raw_hash_set.h"
-#include "absl/container/internal/tracked.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-enum AllocSpec {
-  kPropagateOnCopy = 1,
-  kPropagateOnMove = 2,
-  kPropagateOnSwap = 4,
-};
-
-struct AllocState {
-  size_t num_allocs = 0;
-  std::set<void*> owned;
-};
-
-template <class T,
-          int Spec = kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>
-class CheckedAlloc {
- public:
-  template <class, int>
-  friend class CheckedAlloc;
-
-  using value_type = T;
-
-  CheckedAlloc() {}
-  explicit CheckedAlloc(size_t id) : id_(id) {}
-  CheckedAlloc(const CheckedAlloc&) = default;
-  CheckedAlloc& operator=(const CheckedAlloc&) = default;
-
-  template <class U>
-  CheckedAlloc(const CheckedAlloc<U, Spec>& that)
-      : id_(that.id_), state_(that.state_) {}
-
-  template <class U>
-  struct rebind {
-    using other = CheckedAlloc<U, Spec>;
-  };
-
-  using propagate_on_container_copy_assignment =
-      std::integral_constant<bool, (Spec & kPropagateOnCopy) != 0>;
-
-  using propagate_on_container_move_assignment =
-      std::integral_constant<bool, (Spec & kPropagateOnMove) != 0>;
-
-  using propagate_on_container_swap =
-      std::integral_constant<bool, (Spec & kPropagateOnSwap) != 0>;
-
-  CheckedAlloc select_on_container_copy_construction() const {
-    if (Spec & kPropagateOnCopy) return *this;
-    return {};
-  }
-
-  T* allocate(size_t n) {
-    T* ptr = std::allocator<T>().allocate(n);
-    track_alloc(ptr);
-    return ptr;
-  }
-  void deallocate(T* ptr, size_t n) {
-    memset(ptr, 0, n * sizeof(T));  // The freed memory must be unpoisoned.
-    track_dealloc(ptr);
-    return std::allocator<T>().deallocate(ptr, n);
-  }
-
-  friend bool operator==(const CheckedAlloc& a, const CheckedAlloc& b) {
-    return a.id_ == b.id_;
-  }
-  friend bool operator!=(const CheckedAlloc& a, const CheckedAlloc& b) {
-    return !(a == b);
-  }
-
-  size_t num_allocs() const { return state_->num_allocs; }
-
-  void swap(CheckedAlloc& that) {
-    using std::swap;
-    swap(id_, that.id_);
-    swap(state_, that.state_);
-  }
-
-  friend void swap(CheckedAlloc& a, CheckedAlloc& b) { a.swap(b); }
-
-  friend std::ostream& operator<<(std::ostream& o, const CheckedAlloc& a) {
-    return o << "alloc(" << a.id_ << ")";
-  }
-
- private:
-  void track_alloc(void* ptr) {
-    AllocState* state = state_.get();
-    ++state->num_allocs;
-    if (!state->owned.insert(ptr).second)
-      ADD_FAILURE() << *this << " got previously allocated memory: " << ptr;
-  }
-  void track_dealloc(void* ptr) {
-    if (state_->owned.erase(ptr) != 1)
-      ADD_FAILURE() << *this
-                    << " deleting memory owned by another allocator: " << ptr;
-  }
-
-  size_t id_ = std::numeric_limits<size_t>::max();
-
-  std::shared_ptr<AllocState> state_ = std::make_shared<AllocState>();
-};
-
-struct Identity {
-  int32_t operator()(int32_t v) const { return v; }
-};
-
-struct Policy {
-  using slot_type = Tracked<int32_t>;
-  using init_type = Tracked<int32_t>;
-  using key_type = int32_t;
-
-  template <class allocator_type, class... Args>
-  static void construct(allocator_type* alloc, slot_type* slot,
-                        Args&&... args) {
-    std::allocator_traits<allocator_type>::construct(
-        *alloc, slot, std::forward<Args>(args)...);
-  }
-
-  template <class allocator_type>
-  static void destroy(allocator_type* alloc, slot_type* slot) {
-    std::allocator_traits<allocator_type>::destroy(*alloc, slot);
-  }
-
-  template <class allocator_type>
-  static void transfer(allocator_type* alloc, slot_type* new_slot,
-                       slot_type* old_slot) {
-    construct(alloc, new_slot, std::move(*old_slot));
-    destroy(alloc, old_slot);
-  }
-
-  template <class F>
-  static auto apply(F&& f, int32_t v) -> decltype(std::forward<F>(f)(v, v)) {
-    return std::forward<F>(f)(v, v);
-  }
-
-  template <class F>
-  static auto apply(F&& f, const slot_type& v)
-      -> decltype(std::forward<F>(f)(v.val(), v)) {
-    return std::forward<F>(f)(v.val(), v);
-  }
-
-  template <class F>
-  static auto apply(F&& f, slot_type&& v)
-      -> decltype(std::forward<F>(f)(v.val(), std::move(v))) {
-    return std::forward<F>(f)(v.val(), std::move(v));
-  }
-
-  static slot_type& element(slot_type* slot) { return *slot; }
-};
-
-template <int Spec>
-struct PropagateTest : public ::testing::Test {
-  using Alloc = CheckedAlloc<Tracked<int32_t>, Spec>;
-
-  using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, Alloc>;
-
-  PropagateTest() {
-    EXPECT_EQ(a1, t1.get_allocator());
-    EXPECT_NE(a2, t1.get_allocator());
-  }
-
-  Alloc a1 = Alloc(1);
-  Table t1 = Table(0, a1);
-  Alloc a2 = Alloc(2);
-};
-
-using PropagateOnAll =
-    PropagateTest<kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>;
-using NoPropagateOnCopy = PropagateTest<kPropagateOnMove | kPropagateOnSwap>;
-using NoPropagateOnMove = PropagateTest<kPropagateOnCopy | kPropagateOnSwap>;
-
-TEST_F(PropagateOnAll, Empty) { EXPECT_EQ(0, a1.num_allocs()); }
-
-TEST_F(PropagateOnAll, InsertAllocates) {
-  auto it = t1.insert(0).first;
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, InsertDecomposes) {
-  auto it = t1.insert(0).first;
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-
-  EXPECT_FALSE(t1.insert(0).second);
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, RehashMoves) {
-  auto it = t1.insert(0).first;
-  EXPECT_EQ(0, it->num_moves());
-  t1.rehash(2 * t1.capacity());
-  EXPECT_EQ(2, a1.num_allocs());
-  it = t1.find(0);
-  EXPECT_EQ(1, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, CopyConstructor) {
-  auto it = t1.insert(0).first;
-  Table u(t1);
-  EXPECT_EQ(2, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(NoPropagateOnCopy, CopyConstructor) {
-  auto it = t1.insert(0).first;
-  Table u(t1);
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, u.get_allocator().num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, CopyConstructorWithSameAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(t1, a1);
-  EXPECT_EQ(2, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(NoPropagateOnCopy, CopyConstructorWithSameAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(t1, a1);
-  EXPECT_EQ(2, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, CopyConstructorWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(t1, a2);
-  EXPECT_EQ(a2, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(NoPropagateOnCopy, CopyConstructorWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(t1, a2);
-  EXPECT_EQ(a2, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, MoveConstructor) {
-  auto it = t1.insert(0).first;
-  Table u(std::move(t1));
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(NoPropagateOnMove, MoveConstructor) {
-  auto it = t1.insert(0).first;
-  Table u(std::move(t1));
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, MoveConstructorWithSameAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(std::move(t1), a1);
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(NoPropagateOnMove, MoveConstructorWithSameAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(std::move(t1), a1);
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, MoveConstructorWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(std::move(t1), a2);
-  it = u.find(0);
-  EXPECT_EQ(a2, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(1, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(NoPropagateOnMove, MoveConstructorWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(std::move(t1), a2);
-  it = u.find(0);
-  EXPECT_EQ(a2, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(1, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, CopyAssignmentWithSameAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(0, a1);
-  u = t1;
-  EXPECT_EQ(2, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(NoPropagateOnCopy, CopyAssignmentWithSameAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(0, a1);
-  u = t1;
-  EXPECT_EQ(2, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, CopyAssignmentWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(0, a2);
-  u = t1;
-  EXPECT_EQ(a1, u.get_allocator());
-  EXPECT_EQ(2, a1.num_allocs());
-  EXPECT_EQ(0, a2.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(NoPropagateOnCopy, CopyAssignmentWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(0, a2);
-  u = t1;
-  EXPECT_EQ(a2, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(1, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, MoveAssignmentWithSameAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(0, a1);
-  u = std::move(t1);
-  EXPECT_EQ(a1, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(NoPropagateOnMove, MoveAssignmentWithSameAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(0, a1);
-  u = std::move(t1);
-  EXPECT_EQ(a1, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, MoveAssignmentWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(0, a2);
-  u = std::move(t1);
-  EXPECT_EQ(a1, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, a2.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(NoPropagateOnMove, MoveAssignmentWithDifferentAlloc) {
-  auto it = t1.insert(0).first;
-  Table u(0, a2);
-  u = std::move(t1);
-  it = u.find(0);
-  EXPECT_EQ(a2, u.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(1, a2.num_allocs());
-  EXPECT_EQ(1, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-TEST_F(PropagateOnAll, Swap) {
-  auto it = t1.insert(0).first;
-  Table u(0, a2);
-  u.swap(t1);
-  EXPECT_EQ(a1, u.get_allocator());
-  EXPECT_EQ(a2, t1.get_allocator());
-  EXPECT_EQ(1, a1.num_allocs());
-  EXPECT_EQ(0, a2.num_allocs());
-  EXPECT_EQ(0, it->num_moves());
-  EXPECT_EQ(0, it->num_copies());
-}
-
-// This allocator is similar to std::pmr::polymorphic_allocator.
-// Note the disabled assignment.
-template <class T>
-class PAlloc {
-  template <class>
-  friend class PAlloc;
-
- public:
-  // types
-  using value_type = T;
-
-  // traits
-  using propagate_on_container_swap = std::false_type;
-
-  PAlloc() noexcept = default;
-  explicit PAlloc(size_t id) noexcept : id_(id) {}
-  PAlloc(const PAlloc&) noexcept = default;
-  PAlloc& operator=(const PAlloc&) noexcept = delete;
-
-  template <class U>
-  PAlloc(const PAlloc<U>& that) noexcept : id_(that.id_) {}  // NOLINT
-
-  template <class U>
-  struct rebind {
-    using other = PAlloc<U>;
-  };
-
-  constexpr PAlloc select_on_container_copy_construction() const { return {}; }
-
-  // public member functions
-  T* allocate(size_t) { return new T; }
-  void deallocate(T* p, size_t) { delete p; }
-
-  friend bool operator==(const PAlloc& a, const PAlloc& b) {
-    return a.id_ == b.id_;
-  }
-  friend bool operator!=(const PAlloc& a, const PAlloc& b) { return !(a == b); }
-
- private:
-  size_t id_ = std::numeric_limits<size_t>::max();
-};
-
-// This doesn't compile with GCC 5.4 and 5.5 due to a bug in noexcept handing.
-#if !defined(__GNUC__) || __GNUC__ != 5 || (__GNUC_MINOR__ != 4 && \
-    __GNUC_MINOR__ != 5)
-TEST(NoPropagateOn, Swap) {
-  using PA = PAlloc<char>;
-  using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
-
-  Table t1(PA{1}), t2(PA{2});
-  swap(t1, t2);
-  EXPECT_EQ(t1.get_allocator(), PA(1));
-  EXPECT_EQ(t2.get_allocator(), PA(2));
-}
-#endif
-
-TEST(NoPropagateOn, CopyConstruct) {
-  using PA = PAlloc<char>;
-  using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
-
-  Table t1(PA{1}), t2(t1);
-  EXPECT_EQ(t1.get_allocator(), PA(1));
-  EXPECT_EQ(t2.get_allocator(), PA());
-}
-
-TEST(NoPropagateOn, Assignment) {
-  using PA = PAlloc<char>;
-  using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
-
-  Table t1(PA{1}), t2(PA{2});
-  t1 = t2;
-  EXPECT_EQ(t1.get_allocator(), PA(1));
-  EXPECT_EQ(t2.get_allocator(), PA(2));
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/raw_hash_set_test.cc b/third_party/abseil_cpp/absl/container/internal/raw_hash_set_test.cc
deleted file mode 100644
index 33d2773de3..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/raw_hash_set_test.cc
+++ /dev/null
@@ -1,1893 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/raw_hash_set.h"
-
-#include <cmath>
-#include <cstdint>
-#include <deque>
-#include <functional>
-#include <memory>
-#include <numeric>
-#include <random>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/cycleclock.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/container/internal/container_memory.h"
-#include "absl/container/internal/hash_function_defaults.h"
-#include "absl/container/internal/hash_policy_testing.h"
-#include "absl/container/internal/hashtable_debug.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-struct RawHashSetTestOnlyAccess {
-  template <typename C>
-  static auto GetSlots(const C& c) -> decltype(c.slots_) {
-    return c.slots_;
-  }
-};
-
-namespace {
-
-using ::testing::DoubleNear;
-using ::testing::ElementsAre;
-using ::testing::Ge;
-using ::testing::Lt;
-using ::testing::Optional;
-using ::testing::Pair;
-using ::testing::UnorderedElementsAre;
-
-TEST(Util, NormalizeCapacity) {
-  EXPECT_EQ(1, NormalizeCapacity(0));
-  EXPECT_EQ(1, NormalizeCapacity(1));
-  EXPECT_EQ(3, NormalizeCapacity(2));
-  EXPECT_EQ(3, NormalizeCapacity(3));
-  EXPECT_EQ(7, NormalizeCapacity(4));
-  EXPECT_EQ(7, NormalizeCapacity(7));
-  EXPECT_EQ(15, NormalizeCapacity(8));
-  EXPECT_EQ(15, NormalizeCapacity(15));
-  EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 1));
-  EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 2));
-}
-
-TEST(Util, GrowthAndCapacity) {
-  // Verify that GrowthToCapacity gives the minimum capacity that has enough
-  // growth.
-  for (size_t growth = 0; growth < 10000; ++growth) {
-    SCOPED_TRACE(growth);
-    size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth));
-    // The capacity is large enough for `growth`
-    EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth));
-    if (growth != 0 && capacity > 1) {
-      // There is no smaller capacity that works.
-      EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth));
-    }
-  }
-
-  for (size_t capacity = Group::kWidth - 1; capacity < 10000;
-       capacity = 2 * capacity + 1) {
-    SCOPED_TRACE(capacity);
-    size_t growth = CapacityToGrowth(capacity);
-    EXPECT_THAT(growth, Lt(capacity));
-    EXPECT_LE(GrowthToLowerboundCapacity(growth), capacity);
-    EXPECT_EQ(NormalizeCapacity(GrowthToLowerboundCapacity(growth)), capacity);
-  }
-}
-
-TEST(Util, probe_seq) {
-  probe_seq<16> seq(0, 127);
-  auto gen = [&]() {
-    size_t res = seq.offset();
-    seq.next();
-    return res;
-  };
-  std::vector<size_t> offsets(8);
-  std::generate_n(offsets.begin(), 8, gen);
-  EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
-  seq = probe_seq<16>(128, 127);
-  std::generate_n(offsets.begin(), 8, gen);
-  EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
-}
-
-TEST(BitMask, Smoke) {
-  EXPECT_FALSE((BitMask<uint8_t, 8>(0)));
-  EXPECT_TRUE((BitMask<uint8_t, 8>(5)));
-
-  EXPECT_THAT((BitMask<uint8_t, 8>(0)), ElementsAre());
-  EXPECT_THAT((BitMask<uint8_t, 8>(0x1)), ElementsAre(0));
-  EXPECT_THAT((BitMask<uint8_t, 8>(0x2)), ElementsAre(1));
-  EXPECT_THAT((BitMask<uint8_t, 8>(0x3)), ElementsAre(0, 1));
-  EXPECT_THAT((BitMask<uint8_t, 8>(0x4)), ElementsAre(2));
-  EXPECT_THAT((BitMask<uint8_t, 8>(0x5)), ElementsAre(0, 2));
-  EXPECT_THAT((BitMask<uint8_t, 8>(0x55)), ElementsAre(0, 2, 4, 6));
-  EXPECT_THAT((BitMask<uint8_t, 8>(0xAA)), ElementsAre(1, 3, 5, 7));
-}
-
-TEST(BitMask, WithShift) {
-  // See the non-SSE version of Group for details on what this math is for.
-  uint64_t ctrl = 0x1716151413121110;
-  uint64_t hash = 0x12;
-  constexpr uint64_t msbs = 0x8080808080808080ULL;
-  constexpr uint64_t lsbs = 0x0101010101010101ULL;
-  auto x = ctrl ^ (lsbs * hash);
-  uint64_t mask = (x - lsbs) & ~x & msbs;
-  EXPECT_EQ(0x0000000080800000, mask);
-
-  BitMask<uint64_t, 8, 3> b(mask);
-  EXPECT_EQ(*b, 2);
-}
-
-TEST(BitMask, LeadingTrailing) {
-  EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).LeadingZeros()), 3);
-  EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).TrailingZeros()), 6);
-
-  EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).LeadingZeros()), 15);
-  EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).TrailingZeros()), 0);
-
-  EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).LeadingZeros()), 0);
-  EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).TrailingZeros()), 15);
-
-  EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).LeadingZeros()), 3);
-  EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).TrailingZeros()), 1);
-
-  EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).LeadingZeros()), 7);
-  EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).TrailingZeros()), 0);
-
-  EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).LeadingZeros()), 0);
-  EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).TrailingZeros()), 7);
-}
-
-TEST(Group, EmptyGroup) {
-  for (h2_t h = 0; h != 128; ++h) EXPECT_FALSE(Group{EmptyGroup()}.Match(h));
-}
-
-TEST(Group, Match) {
-  if (Group::kWidth == 16) {
-    ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
-                      7,      5, 3,        1, 1,      1, 1,         1};
-    EXPECT_THAT(Group{group}.Match(0), ElementsAre());
-    EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
-    EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
-    EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
-    EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
-  } else if (Group::kWidth == 8) {
-    ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
-    EXPECT_THAT(Group{group}.Match(0), ElementsAre());
-    EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
-    EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
-  } else {
-    FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
-  }
-}
-
-TEST(Group, MatchEmpty) {
-  if (Group::kWidth == 16) {
-    ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
-                      7,      5, 3,        1, 1,      1, 1,         1};
-    EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4));
-  } else if (Group::kWidth == 8) {
-    ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
-    EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0));
-  } else {
-    FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
-  }
-}
-
-TEST(Group, MatchEmptyOrDeleted) {
-  if (Group::kWidth == 16) {
-    ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
-                      7,      5, 3,        1, 1,      1, 1,         1};
-    EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4));
-  } else if (Group::kWidth == 8) {
-    ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
-    EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3));
-  } else {
-    FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
-  }
-}
-
-TEST(Batch, DropDeletes) {
-  constexpr size_t kCapacity = 63;
-  constexpr size_t kGroupWidth = container_internal::Group::kWidth;
-  std::vector<ctrl_t> ctrl(kCapacity + 1 + kGroupWidth);
-  ctrl[kCapacity] = kSentinel;
-  std::vector<ctrl_t> pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted};
-  for (size_t i = 0; i != kCapacity; ++i) {
-    ctrl[i] = pattern[i % pattern.size()];
-    if (i < kGroupWidth - 1)
-      ctrl[i + kCapacity + 1] = pattern[i % pattern.size()];
-  }
-  ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity);
-  ASSERT_EQ(ctrl[kCapacity], kSentinel);
-  for (size_t i = 0; i < kCapacity + 1 + kGroupWidth; ++i) {
-    ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()];
-    if (i == kCapacity) expected = kSentinel;
-    if (expected == kDeleted) expected = kEmpty;
-    if (IsFull(expected)) expected = kDeleted;
-    EXPECT_EQ(ctrl[i], expected)
-        << i << " " << int{pattern[i % pattern.size()]};
-  }
-}
-
-TEST(Group, CountLeadingEmptyOrDeleted) {
-  const std::vector<ctrl_t> empty_examples = {kEmpty, kDeleted};
-  const std::vector<ctrl_t> full_examples = {0, 1, 2, 3, 5, 9, 127, kSentinel};
-
-  for (ctrl_t empty : empty_examples) {
-    std::vector<ctrl_t> e(Group::kWidth, empty);
-    EXPECT_EQ(Group::kWidth, Group{e.data()}.CountLeadingEmptyOrDeleted());
-    for (ctrl_t full : full_examples) {
-      for (size_t i = 0; i != Group::kWidth; ++i) {
-        std::vector<ctrl_t> f(Group::kWidth, empty);
-        f[i] = full;
-        EXPECT_EQ(i, Group{f.data()}.CountLeadingEmptyOrDeleted());
-      }
-      std::vector<ctrl_t> f(Group::kWidth, empty);
-      f[Group::kWidth * 2 / 3] = full;
-      f[Group::kWidth / 2] = full;
-      EXPECT_EQ(
-          Group::kWidth / 2, Group{f.data()}.CountLeadingEmptyOrDeleted());
-    }
-  }
-}
-
-struct IntPolicy {
-  using slot_type = int64_t;
-  using key_type = int64_t;
-  using init_type = int64_t;
-
-  static void construct(void*, int64_t* slot, int64_t v) { *slot = v; }
-  static void destroy(void*, int64_t*) {}
-  static void transfer(void*, int64_t* new_slot, int64_t* old_slot) {
-    *new_slot = *old_slot;
-  }
-
-  static int64_t& element(slot_type* slot) { return *slot; }
-
-  template <class F>
-  static auto apply(F&& f, int64_t x) -> decltype(std::forward<F>(f)(x, x)) {
-    return std::forward<F>(f)(x, x);
-  }
-};
-
-class StringPolicy {
-  template <class F, class K, class V,
-            class = typename std::enable_if<
-                std::is_convertible<const K&, absl::string_view>::value>::type>
-  decltype(std::declval<F>()(
-      std::declval<const absl::string_view&>(), std::piecewise_construct,
-      std::declval<std::tuple<K>>(),
-      std::declval<V>())) static apply_impl(F&& f,
-                                            std::pair<std::tuple<K>, V> p) {
-    const absl::string_view& key = std::get<0>(p.first);
-    return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
-                              std::move(p.second));
-  }
-
- public:
-  struct slot_type {
-    struct ctor {};
-
-    template <class... Ts>
-    slot_type(ctor, Ts&&... ts) : pair(std::forward<Ts>(ts)...) {}
-
-    std::pair<std::string, std::string> pair;
-  };
-
-  using key_type = std::string;
-  using init_type = std::pair<std::string, std::string>;
-
-  template <class allocator_type, class... Args>
-  static void construct(allocator_type* alloc, slot_type* slot, Args... args) {
-    std::allocator_traits<allocator_type>::construct(
-        *alloc, slot, typename slot_type::ctor(), std::forward<Args>(args)...);
-  }
-
-  template <class allocator_type>
-  static void destroy(allocator_type* alloc, slot_type* slot) {
-    std::allocator_traits<allocator_type>::destroy(*alloc, slot);
-  }
-
-  template <class allocator_type>
-  static void transfer(allocator_type* alloc, slot_type* new_slot,
-                       slot_type* old_slot) {
-    construct(alloc, new_slot, std::move(old_slot->pair));
-    destroy(alloc, old_slot);
-  }
-
-  static std::pair<std::string, std::string>& element(slot_type* slot) {
-    return slot->pair;
-  }
-
-  template <class F, class... Args>
-  static auto apply(F&& f, Args&&... args)
-      -> decltype(apply_impl(std::forward<F>(f),
-                             PairArgs(std::forward<Args>(args)...))) {
-    return apply_impl(std::forward<F>(f),
-                      PairArgs(std::forward<Args>(args)...));
-  }
-};
-
-struct StringHash : absl::Hash<absl::string_view> {
-  using is_transparent = void;
-};
-struct StringEq : std::equal_to<absl::string_view> {
-  using is_transparent = void;
-};
-
-struct StringTable
-    : raw_hash_set<StringPolicy, StringHash, StringEq, std::allocator<int>> {
-  using Base = typename StringTable::raw_hash_set;
-  StringTable() {}
-  using Base::Base;
-};
-
-struct IntTable
-    : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
-                   std::equal_to<int64_t>, std::allocator<int64_t>> {
-  using Base = typename IntTable::raw_hash_set;
-  using Base::Base;
-};
-
-template <typename T>
-struct CustomAlloc : std::allocator<T> {
-  CustomAlloc() {}
-
-  template <typename U>
-  CustomAlloc(const CustomAlloc<U>& other) {}
-
-  template<class U> struct rebind {
-    using other = CustomAlloc<U>;
-  };
-};
-
-struct CustomAllocIntTable
-    : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
-                   std::equal_to<int64_t>, CustomAlloc<int64_t>> {
-  using Base = typename CustomAllocIntTable::raw_hash_set;
-  using Base::Base;
-};
-
-struct BadFastHash {
-  template <class T>
-  size_t operator()(const T&) const {
-    return 0;
-  }
-};
-
-struct BadTable : raw_hash_set<IntPolicy, BadFastHash, std::equal_to<int>,
-                               std::allocator<int>> {
-  using Base = typename BadTable::raw_hash_set;
-  BadTable() {}
-  using Base::Base;
-};
-
-TEST(Table, EmptyFunctorOptimization) {
-  static_assert(std::is_empty<std::equal_to<absl::string_view>>::value, "");
-  static_assert(std::is_empty<std::allocator<int>>::value, "");
-
-  struct MockTable {
-    void* ctrl;
-    void* slots;
-    size_t size;
-    size_t capacity;
-    size_t growth_left;
-    void* infoz;
-  };
-  struct StatelessHash {
-    size_t operator()(absl::string_view) const { return 0; }
-  };
-  struct StatefulHash : StatelessHash {
-    size_t dummy;
-  };
-
-  EXPECT_EQ(
-      sizeof(MockTable),
-      sizeof(
-          raw_hash_set<StringPolicy, StatelessHash,
-                       std::equal_to<absl::string_view>, std::allocator<int>>));
-
-  EXPECT_EQ(
-      sizeof(MockTable) + sizeof(StatefulHash),
-      sizeof(
-          raw_hash_set<StringPolicy, StatefulHash,
-                       std::equal_to<absl::string_view>, std::allocator<int>>));
-}
-
-TEST(Table, Empty) {
-  IntTable t;
-  EXPECT_EQ(0, t.size());
-  EXPECT_TRUE(t.empty());
-}
-
-TEST(Table, LookupEmpty) {
-  IntTable t;
-  auto it = t.find(0);
-  EXPECT_TRUE(it == t.end());
-}
-
-TEST(Table, Insert1) {
-  IntTable t;
-  EXPECT_TRUE(t.find(0) == t.end());
-  auto res = t.emplace(0);
-  EXPECT_TRUE(res.second);
-  EXPECT_THAT(*res.first, 0);
-  EXPECT_EQ(1, t.size());
-  EXPECT_THAT(*t.find(0), 0);
-}
-
-TEST(Table, Insert2) {
-  IntTable t;
-  EXPECT_TRUE(t.find(0) == t.end());
-  auto res = t.emplace(0);
-  EXPECT_TRUE(res.second);
-  EXPECT_THAT(*res.first, 0);
-  EXPECT_EQ(1, t.size());
-  EXPECT_TRUE(t.find(1) == t.end());
-  res = t.emplace(1);
-  EXPECT_TRUE(res.second);
-  EXPECT_THAT(*res.first, 1);
-  EXPECT_EQ(2, t.size());
-  EXPECT_THAT(*t.find(0), 0);
-  EXPECT_THAT(*t.find(1), 1);
-}
-
-TEST(Table, InsertCollision) {
-  BadTable t;
-  EXPECT_TRUE(t.find(1) == t.end());
-  auto res = t.emplace(1);
-  EXPECT_TRUE(res.second);
-  EXPECT_THAT(*res.first, 1);
-  EXPECT_EQ(1, t.size());
-
-  EXPECT_TRUE(t.find(2) == t.end());
-  res = t.emplace(2);
-  EXPECT_THAT(*res.first, 2);
-  EXPECT_TRUE(res.second);
-  EXPECT_EQ(2, t.size());
-
-  EXPECT_THAT(*t.find(1), 1);
-  EXPECT_THAT(*t.find(2), 2);
-}
-
-// Test that we do not add existent element in case we need to search through
-// many groups with deleted elements
-TEST(Table, InsertCollisionAndFindAfterDelete) {
-  BadTable t;  // all elements go to the same group.
-  // Have at least 2 groups with Group::kWidth collisions
-  // plus some extra collisions in the last group.
-  constexpr size_t kNumInserts = Group::kWidth * 2 + 5;
-  for (size_t i = 0; i < kNumInserts; ++i) {
-    auto res = t.emplace(i);
-    EXPECT_TRUE(res.second);
-    EXPECT_THAT(*res.first, i);
-    EXPECT_EQ(i + 1, t.size());
-  }
-
-  // Remove elements one by one and check
-  // that we still can find all other elements.
-  for (size_t i = 0; i < kNumInserts; ++i) {
-    EXPECT_EQ(1, t.erase(i)) << i;
-    for (size_t j = i + 1; j < kNumInserts; ++j) {
-      EXPECT_THAT(*t.find(j), j);
-      auto res = t.emplace(j);
-      EXPECT_FALSE(res.second) << i << " " << j;
-      EXPECT_THAT(*res.first, j);
-      EXPECT_EQ(kNumInserts - i - 1, t.size());
-    }
-  }
-  EXPECT_TRUE(t.empty());
-}
-
-TEST(Table, LazyEmplace) {
-  StringTable t;
-  bool called = false;
-  auto it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
-    called = true;
-    f("abc", "ABC");
-  });
-  EXPECT_TRUE(called);
-  EXPECT_THAT(*it, Pair("abc", "ABC"));
-  called = false;
-  it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
-    called = true;
-    f("abc", "DEF");
-  });
-  EXPECT_FALSE(called);
-  EXPECT_THAT(*it, Pair("abc", "ABC"));
-}
-
-TEST(Table, ContainsEmpty) {
-  IntTable t;
-
-  EXPECT_FALSE(t.contains(0));
-}
-
-TEST(Table, Contains1) {
-  IntTable t;
-
-  EXPECT_TRUE(t.insert(0).second);
-  EXPECT_TRUE(t.contains(0));
-  EXPECT_FALSE(t.contains(1));
-
-  EXPECT_EQ(1, t.erase(0));
-  EXPECT_FALSE(t.contains(0));
-}
-
-TEST(Table, Contains2) {
-  IntTable t;
-
-  EXPECT_TRUE(t.insert(0).second);
-  EXPECT_TRUE(t.contains(0));
-  EXPECT_FALSE(t.contains(1));
-
-  t.clear();
-  EXPECT_FALSE(t.contains(0));
-}
-
-int decompose_constructed;
-struct DecomposeType {
-  DecomposeType(int i) : i(i) {  // NOLINT
-    ++decompose_constructed;
-  }
-
-  explicit DecomposeType(const char* d) : DecomposeType(*d) {}
-
-  int i;
-};
-
-struct DecomposeHash {
-  using is_transparent = void;
-  size_t operator()(DecomposeType a) const { return a.i; }
-  size_t operator()(int a) const { return a; }
-  size_t operator()(const char* a) const { return *a; }
-};
-
-struct DecomposeEq {
-  using is_transparent = void;
-  bool operator()(DecomposeType a, DecomposeType b) const { return a.i == b.i; }
-  bool operator()(DecomposeType a, int b) const { return a.i == b; }
-  bool operator()(DecomposeType a, const char* b) const { return a.i == *b; }
-};
-
-struct DecomposePolicy {
-  using slot_type = DecomposeType;
-  using key_type = DecomposeType;
-  using init_type = DecomposeType;
-
-  template <typename T>
-  static void construct(void*, DecomposeType* slot, T&& v) {
-    *slot = DecomposeType(std::forward<T>(v));
-  }
-  static void destroy(void*, DecomposeType*) {}
-  static DecomposeType& element(slot_type* slot) { return *slot; }
-
-  template <class F, class T>
-  static auto apply(F&& f, const T& x) -> decltype(std::forward<F>(f)(x, x)) {
-    return std::forward<F>(f)(x, x);
-  }
-};
-
-template <typename Hash, typename Eq>
-void TestDecompose(bool construct_three) {
-  DecomposeType elem{0};
-  const int one = 1;
-  const char* three_p = "3";
-  const auto& three = three_p;
-
-  raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>> set1;
-
-  decompose_constructed = 0;
-  int expected_constructed = 0;
-  EXPECT_EQ(expected_constructed, decompose_constructed);
-  set1.insert(elem);
-  EXPECT_EQ(expected_constructed, decompose_constructed);
-  set1.insert(1);
-  EXPECT_EQ(++expected_constructed, decompose_constructed);
-  set1.emplace("3");
-  EXPECT_EQ(++expected_constructed, decompose_constructed);
-  EXPECT_EQ(expected_constructed, decompose_constructed);
-
-  {  // insert(T&&)
-    set1.insert(1);
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-  }
-
-  {  // insert(const T&)
-    set1.insert(one);
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-  }
-
-  {  // insert(hint, T&&)
-    set1.insert(set1.begin(), 1);
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-  }
-
-  {  // insert(hint, const T&)
-    set1.insert(set1.begin(), one);
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-  }
-
-  {  // emplace(...)
-    set1.emplace(1);
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-    set1.emplace("3");
-    expected_constructed += construct_three;
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-    set1.emplace(one);
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-    set1.emplace(three);
-    expected_constructed += construct_three;
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-  }
-
-  {  // emplace_hint(...)
-    set1.emplace_hint(set1.begin(), 1);
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-    set1.emplace_hint(set1.begin(), "3");
-    expected_constructed += construct_three;
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-    set1.emplace_hint(set1.begin(), one);
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-    set1.emplace_hint(set1.begin(), three);
-    expected_constructed += construct_three;
-    EXPECT_EQ(expected_constructed, decompose_constructed);
-  }
-}
-
-TEST(Table, Decompose) {
-  TestDecompose<DecomposeHash, DecomposeEq>(false);
-
-  struct TransparentHashIntOverload {
-    size_t operator()(DecomposeType a) const { return a.i; }
-    size_t operator()(int a) const { return a; }
-  };
-  struct TransparentEqIntOverload {
-    bool operator()(DecomposeType a, DecomposeType b) const {
-      return a.i == b.i;
-    }
-    bool operator()(DecomposeType a, int b) const { return a.i == b; }
-  };
-  TestDecompose<TransparentHashIntOverload, DecomposeEq>(true);
-  TestDecompose<TransparentHashIntOverload, TransparentEqIntOverload>(true);
-  TestDecompose<DecomposeHash, TransparentEqIntOverload>(true);
-}
-
-// Returns the largest m such that a table with m elements has the same number
-// of buckets as a table with n elements.
-size_t MaxDensitySize(size_t n) {
-  IntTable t;
-  t.reserve(n);
-  for (size_t i = 0; i != n; ++i) t.emplace(i);
-  const size_t c = t.bucket_count();
-  while (c == t.bucket_count()) t.emplace(n++);
-  return t.size() - 1;
-}
-
-struct Modulo1000Hash {
-  size_t operator()(int x) const { return x % 1000; }
-};
-
-struct Modulo1000HashTable
-    : public raw_hash_set<IntPolicy, Modulo1000Hash, std::equal_to<int>,
-                          std::allocator<int>> {};
-
-// Test that rehash with no resize happen in case of many deleted slots.
-TEST(Table, RehashWithNoResize) {
-  Modulo1000HashTable t;
-  // Adding the same length (and the same hash) strings
-  // to have at least kMinFullGroups groups
-  // with Group::kWidth collisions. Then fill up to MaxDensitySize;
-  const size_t kMinFullGroups = 7;
-  std::vector<int> keys;
-  for (size_t i = 0; i < MaxDensitySize(Group::kWidth * kMinFullGroups); ++i) {
-    int k = i * 1000;
-    t.emplace(k);
-    keys.push_back(k);
-  }
-  const size_t capacity = t.capacity();
-
-  // Remove elements from all groups except the first and the last one.
-  // All elements removed from full groups will be marked as kDeleted.
-  const size_t erase_begin = Group::kWidth / 2;
-  const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth;
-  for (size_t i = erase_begin; i < erase_end; ++i) {
-    EXPECT_EQ(1, t.erase(keys[i])) << i;
-  }
-  keys.erase(keys.begin() + erase_begin, keys.begin() + erase_end);
-
-  auto last_key = keys.back();
-  size_t last_key_num_probes = GetHashtableDebugNumProbes(t, last_key);
-
-  // Make sure that we have to make a lot of probes for last key.
-  ASSERT_GT(last_key_num_probes, kMinFullGroups);
-
-  int x = 1;
-  // Insert and erase one element, before inplace rehash happen.
-  while (last_key_num_probes == GetHashtableDebugNumProbes(t, last_key)) {
-    t.emplace(x);
-    ASSERT_EQ(capacity, t.capacity());
-    // All elements should be there.
-    ASSERT_TRUE(t.find(x) != t.end()) << x;
-    for (const auto& k : keys) {
-      ASSERT_TRUE(t.find(k) != t.end()) << k;
-    }
-    t.erase(x);
-    ++x;
-  }
-}
-
-TEST(Table, InsertEraseStressTest) {
-  IntTable t;
-  const size_t kMinElementCount = 250;
-  std::deque<int> keys;
-  size_t i = 0;
-  for (; i < MaxDensitySize(kMinElementCount); ++i) {
-    t.emplace(i);
-    keys.push_back(i);
-  }
-  const size_t kNumIterations = 1000000;
-  for (; i < kNumIterations; ++i) {
-    ASSERT_EQ(1, t.erase(keys.front()));
-    keys.pop_front();
-    t.emplace(i);
-    keys.push_back(i);
-  }
-}
-
-TEST(Table, InsertOverloads) {
-  StringTable t;
-  // These should all trigger the insert(init_type) overload.
-  t.insert({{}, {}});
-  t.insert({"ABC", {}});
-  t.insert({"DEF", "!!!"});
-
-  EXPECT_THAT(t, UnorderedElementsAre(Pair("", ""), Pair("ABC", ""),
-                                      Pair("DEF", "!!!")));
-}
-
-TEST(Table, LargeTable) {
-  IntTable t;
-  for (int64_t i = 0; i != 100000; ++i) t.emplace(i << 40);
-  for (int64_t i = 0; i != 100000; ++i) ASSERT_EQ(i << 40, *t.find(i << 40));
-}
-
-// Timeout if copy is quadratic as it was in Rust.
-TEST(Table, EnsureNonQuadraticAsInRust) {
-  static const size_t kLargeSize = 1 << 15;
-
-  IntTable t;
-  for (size_t i = 0; i != kLargeSize; ++i) {
-    t.insert(i);
-  }
-
-  // If this is quadratic, the test will timeout.
-  IntTable t2;
-  for (const auto& entry : t) t2.insert(entry);
-}
-
-TEST(Table, ClearBug) {
-  IntTable t;
-  constexpr size_t capacity = container_internal::Group::kWidth - 1;
-  constexpr size_t max_size = capacity / 2 + 1;
-  for (size_t i = 0; i < max_size; ++i) {
-    t.insert(i);
-  }
-  ASSERT_EQ(capacity, t.capacity());
-  intptr_t original = reinterpret_cast<intptr_t>(&*t.find(2));
-  t.clear();
-  ASSERT_EQ(capacity, t.capacity());
-  for (size_t i = 0; i < max_size; ++i) {
-    t.insert(i);
-  }
-  ASSERT_EQ(capacity, t.capacity());
-  intptr_t second = reinterpret_cast<intptr_t>(&*t.find(2));
-  // We are checking that original and second are close enough to each other
-  // that they are probably still in the same group.  This is not strictly
-  // guaranteed.
-  EXPECT_LT(std::abs(original - second),
-            capacity * sizeof(IntTable::value_type));
-}
-
-TEST(Table, Erase) {
-  IntTable t;
-  EXPECT_TRUE(t.find(0) == t.end());
-  auto res = t.emplace(0);
-  EXPECT_TRUE(res.second);
-  EXPECT_EQ(1, t.size());
-  t.erase(res.first);
-  EXPECT_EQ(0, t.size());
-  EXPECT_TRUE(t.find(0) == t.end());
-}
-
-TEST(Table, EraseMaintainsValidIterator) {
-  IntTable t;
-  const int kNumElements = 100;
-  for (int i = 0; i < kNumElements; i ++) {
-    EXPECT_TRUE(t.emplace(i).second);
-  }
-  EXPECT_EQ(t.size(), kNumElements);
-
-  int num_erase_calls = 0;
-  auto it = t.begin();
-  while (it != t.end()) {
-    t.erase(it++);
-    num_erase_calls++;
-  }
-
-  EXPECT_TRUE(t.empty());
-  EXPECT_EQ(num_erase_calls, kNumElements);
-}
-
-// Collect N bad keys by following algorithm:
-// 1. Create an empty table and reserve it to 2 * N.
-// 2. Insert N random elements.
-// 3. Take first Group::kWidth - 1 to bad_keys array.
-// 4. Clear the table without resize.
-// 5. Go to point 2 while N keys not collected
-std::vector<int64_t> CollectBadMergeKeys(size_t N) {
-  static constexpr int kGroupSize = Group::kWidth - 1;
-
-  auto topk_range = [](size_t b, size_t e,
-                       IntTable* t) -> std::vector<int64_t> {
-    for (size_t i = b; i != e; ++i) {
-      t->emplace(i);
-    }
-    std::vector<int64_t> res;
-    res.reserve(kGroupSize);
-    auto it = t->begin();
-    for (size_t i = b; i != e && i != b + kGroupSize; ++i, ++it) {
-      res.push_back(*it);
-    }
-    return res;
-  };
-
-  std::vector<int64_t> bad_keys;
-  bad_keys.reserve(N);
-  IntTable t;
-  t.reserve(N * 2);
-
-  for (size_t b = 0; bad_keys.size() < N; b += N) {
-    auto keys = topk_range(b, b + N, &t);
-    bad_keys.insert(bad_keys.end(), keys.begin(), keys.end());
-    t.erase(t.begin(), t.end());
-    EXPECT_TRUE(t.empty());
-  }
-  return bad_keys;
-}
-
-struct ProbeStats {
-  // Number of elements with specific probe length over all tested tables.
-  std::vector<size_t> all_probes_histogram;
-  // Ratios total_probe_length/size for every tested table.
-  std::vector<double> single_table_ratios;
-
-  friend ProbeStats operator+(const ProbeStats& a, const ProbeStats& b) {
-    ProbeStats res = a;
-    res.all_probes_histogram.resize(std::max(res.all_probes_histogram.size(),
-                                             b.all_probes_histogram.size()));
-    std::transform(b.all_probes_histogram.begin(), b.all_probes_histogram.end(),
-                   res.all_probes_histogram.begin(),
-                   res.all_probes_histogram.begin(), std::plus<size_t>());
-    res.single_table_ratios.insert(res.single_table_ratios.end(),
-                                   b.single_table_ratios.begin(),
-                                   b.single_table_ratios.end());
-    return res;
-  }
-
-  // Average ratio total_probe_length/size over tables.
-  double AvgRatio() const {
-    return std::accumulate(single_table_ratios.begin(),
-                           single_table_ratios.end(), 0.0) /
-           single_table_ratios.size();
-  }
-
-  // Maximum ratio total_probe_length/size over tables.
-  double MaxRatio() const {
-    return *std::max_element(single_table_ratios.begin(),
-                             single_table_ratios.end());
-  }
-
-  // Percentile ratio total_probe_length/size over tables.
-  double PercentileRatio(double Percentile = 0.95) const {
-    auto r = single_table_ratios;
-    auto mid = r.begin() + static_cast<size_t>(r.size() * Percentile);
-    if (mid != r.end()) {
-      std::nth_element(r.begin(), mid, r.end());
-      return *mid;
-    } else {
-      return MaxRatio();
-    }
-  }
-
-  // Maximum probe length over all elements and all tables.
-  size_t MaxProbe() const { return all_probes_histogram.size(); }
-
-  // Fraction of elements with specified probe length.
-  std::vector<double> ProbeNormalizedHistogram() const {
-    double total_elements = std::accumulate(all_probes_histogram.begin(),
-                                            all_probes_histogram.end(), 0ull);
-    std::vector<double> res;
-    for (size_t p : all_probes_histogram) {
-      res.push_back(p / total_elements);
-    }
-    return res;
-  }
-
-  size_t PercentileProbe(double Percentile = 0.99) const {
-    size_t idx = 0;
-    for (double p : ProbeNormalizedHistogram()) {
-      if (Percentile > p) {
-        Percentile -= p;
-        ++idx;
-      } else {
-        return idx;
-      }
-    }
-    return idx;
-  }
-
-  friend std::ostream& operator<<(std::ostream& out, const ProbeStats& s) {
-    out << "{AvgRatio:" << s.AvgRatio() << ", MaxRatio:" << s.MaxRatio()
-        << ", PercentileRatio:" << s.PercentileRatio()
-        << ", MaxProbe:" << s.MaxProbe() << ", Probes=[";
-    for (double p : s.ProbeNormalizedHistogram()) {
-      out << p << ",";
-    }
-    out << "]}";
-
-    return out;
-  }
-};
-
-struct ExpectedStats {
-  double avg_ratio;
-  double max_ratio;
-  std::vector<std::pair<double, double>> pecentile_ratios;
-  std::vector<std::pair<double, double>> pecentile_probes;
-
-  friend std::ostream& operator<<(std::ostream& out, const ExpectedStats& s) {
-    out << "{AvgRatio:" << s.avg_ratio << ", MaxRatio:" << s.max_ratio
-        << ", PercentileRatios: [";
-    for (auto el : s.pecentile_ratios) {
-      out << el.first << ":" << el.second << ", ";
-    }
-    out << "], PercentileProbes: [";
-    for (auto el : s.pecentile_probes) {
-      out << el.first << ":" << el.second << ", ";
-    }
-    out << "]}";
-
-    return out;
-  }
-};
-
-void VerifyStats(size_t size, const ExpectedStats& exp,
-                 const ProbeStats& stats) {
-  EXPECT_LT(stats.AvgRatio(), exp.avg_ratio) << size << " " << stats;
-  EXPECT_LT(stats.MaxRatio(), exp.max_ratio) << size << " " << stats;
-  for (auto pr : exp.pecentile_ratios) {
-    EXPECT_LE(stats.PercentileRatio(pr.first), pr.second)
-        << size << " " << pr.first << " " << stats;
-  }
-
-  for (auto pr : exp.pecentile_probes) {
-    EXPECT_LE(stats.PercentileProbe(pr.first), pr.second)
-        << size << " " << pr.first << " " << stats;
-  }
-}
-
-using ProbeStatsPerSize = std::map<size_t, ProbeStats>;
-
-// Collect total ProbeStats on num_iters iterations of the following algorithm:
-// 1. Create new table and reserve it to keys.size() * 2
-// 2. Insert all keys xored with seed
-// 3. Collect ProbeStats from final table.
-ProbeStats CollectProbeStatsOnKeysXoredWithSeed(
-    const std::vector<int64_t>& keys, size_t num_iters) {
-  const size_t reserve_size = keys.size() * 2;
-
-  ProbeStats stats;
-
-  int64_t seed = 0x71b1a19b907d6e33;
-  while (num_iters--) {
-    seed = static_cast<int64_t>(static_cast<uint64_t>(seed) * 17 + 13);
-    IntTable t1;
-    t1.reserve(reserve_size);
-    for (const auto& key : keys) {
-      t1.emplace(key ^ seed);
-    }
-
-    auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
-    stats.all_probes_histogram.resize(
-        std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
-    std::transform(probe_histogram.begin(), probe_histogram.end(),
-                   stats.all_probes_histogram.begin(),
-                   stats.all_probes_histogram.begin(), std::plus<size_t>());
-
-    size_t total_probe_seq_length = 0;
-    for (size_t i = 0; i < probe_histogram.size(); ++i) {
-      total_probe_seq_length += i * probe_histogram[i];
-    }
-    stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
-                                        keys.size());
-    t1.erase(t1.begin(), t1.end());
-  }
-  return stats;
-}
-
-ExpectedStats XorSeedExpectedStats() {
-  constexpr bool kRandomizesInserts =
-#ifdef NDEBUG
-      false;
-#else   // NDEBUG
-      true;
-#endif  // NDEBUG
-
-  // The effective load factor is larger in non-opt mode because we insert
-  // elements out of order.
-  switch (container_internal::Group::kWidth) {
-    case 8:
-      if (kRandomizesInserts) {
-  return {0.05,
-          1.0,
-          {{0.95, 0.5}},
-          {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
-      } else {
-  return {0.05,
-          2.0,
-          {{0.95, 0.1}},
-          {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
-      }
-    case 16:
-      if (kRandomizesInserts) {
-        return {0.1,
-                1.0,
-                {{0.95, 0.1}},
-                {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
-      } else {
-        return {0.05,
-                1.0,
-                {{0.95, 0.05}},
-                {{0.95, 0}, {0.99, 1}, {0.999, 4}, {0.9999, 10}}};
-      }
-  }
-  ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
-  return {};
-}
-
-TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) {
-  ProbeStatsPerSize stats;
-  std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
-  for (size_t size : sizes) {
-    stats[size] =
-        CollectProbeStatsOnKeysXoredWithSeed(CollectBadMergeKeys(size), 200);
-  }
-  auto expected = XorSeedExpectedStats();
-  for (size_t size : sizes) {
-    auto& stat = stats[size];
-    VerifyStats(size, expected, stat);
-  }
-}
-
-// Collect total ProbeStats on num_iters iterations of the following algorithm:
-// 1. Create new table
-// 2. Select 10% of keys and insert 10 elements key * 17 + j * 13
-// 3. Collect ProbeStats from final table
-ProbeStats CollectProbeStatsOnLinearlyTransformedKeys(
-    const std::vector<int64_t>& keys, size_t num_iters) {
-  ProbeStats stats;
-
-  std::random_device rd;
-  std::mt19937 rng(rd());
-  auto linear_transform = [](size_t x, size_t y) { return x * 17 + y * 13; };
-  std::uniform_int_distribution<size_t> dist(0, keys.size()-1);
-  while (num_iters--) {
-    IntTable t1;
-    size_t num_keys = keys.size() / 10;
-    size_t start = dist(rng);
-    for (size_t i = 0; i != num_keys; ++i) {
-      for (size_t j = 0; j != 10; ++j) {
-        t1.emplace(linear_transform(keys[(i + start) % keys.size()], j));
-      }
-    }
-
-    auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
-    stats.all_probes_histogram.resize(
-        std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
-    std::transform(probe_histogram.begin(), probe_histogram.end(),
-                   stats.all_probes_histogram.begin(),
-                   stats.all_probes_histogram.begin(), std::plus<size_t>());
-
-    size_t total_probe_seq_length = 0;
-    for (size_t i = 0; i < probe_histogram.size(); ++i) {
-      total_probe_seq_length += i * probe_histogram[i];
-    }
-    stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
-                                        t1.size());
-    t1.erase(t1.begin(), t1.end());
-  }
-  return stats;
-}
-
-ExpectedStats LinearTransformExpectedStats() {
-  constexpr bool kRandomizesInserts =
-#ifdef NDEBUG
-      false;
-#else   // NDEBUG
-      true;
-#endif  // NDEBUG
-
-  // The effective load factor is larger in non-opt mode because we insert
-  // elements out of order.
-  switch (container_internal::Group::kWidth) {
-    case 8:
-      if (kRandomizesInserts) {
-        return {0.1,
-                0.5,
-                {{0.95, 0.3}},
-                {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
-      } else {
-        return {0.15,
-                0.5,
-                {{0.95, 0.3}},
-                {{0.95, 0}, {0.99, 3}, {0.999, 15}, {0.9999, 25}}};
-      }
-    case 16:
-      if (kRandomizesInserts) {
-        return {0.1,
-                0.4,
-                {{0.95, 0.3}},
-                {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
-      } else {
-        return {0.05,
-                0.2,
-                {{0.95, 0.1}},
-                {{0.95, 0}, {0.99, 1}, {0.999, 6}, {0.9999, 10}}};
-      }
-  }
-  ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
-  return {};
-}
-
-TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) {
-  ProbeStatsPerSize stats;
-  std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
-  for (size_t size : sizes) {
-    stats[size] = CollectProbeStatsOnLinearlyTransformedKeys(
-        CollectBadMergeKeys(size), 300);
-  }
-  auto expected = LinearTransformExpectedStats();
-  for (size_t size : sizes) {
-    auto& stat = stats[size];
-    VerifyStats(size, expected, stat);
-  }
-}
-
-TEST(Table, EraseCollision) {
-  BadTable t;
-
-  // 1 2 3
-  t.emplace(1);
-  t.emplace(2);
-  t.emplace(3);
-  EXPECT_THAT(*t.find(1), 1);
-  EXPECT_THAT(*t.find(2), 2);
-  EXPECT_THAT(*t.find(3), 3);
-  EXPECT_EQ(3, t.size());
-
-  // 1 DELETED 3
-  t.erase(t.find(2));
-  EXPECT_THAT(*t.find(1), 1);
-  EXPECT_TRUE(t.find(2) == t.end());
-  EXPECT_THAT(*t.find(3), 3);
-  EXPECT_EQ(2, t.size());
-
-  // DELETED DELETED 3
-  t.erase(t.find(1));
-  EXPECT_TRUE(t.find(1) == t.end());
-  EXPECT_TRUE(t.find(2) == t.end());
-  EXPECT_THAT(*t.find(3), 3);
-  EXPECT_EQ(1, t.size());
-
-  // DELETED DELETED DELETED
-  t.erase(t.find(3));
-  EXPECT_TRUE(t.find(1) == t.end());
-  EXPECT_TRUE(t.find(2) == t.end());
-  EXPECT_TRUE(t.find(3) == t.end());
-  EXPECT_EQ(0, t.size());
-}
-
-TEST(Table, EraseInsertProbing) {
-  BadTable t(100);
-
-  // 1 2 3 4
-  t.emplace(1);
-  t.emplace(2);
-  t.emplace(3);
-  t.emplace(4);
-
-  // 1 DELETED 3 DELETED
-  t.erase(t.find(2));
-  t.erase(t.find(4));
-
-  // 1 10 3 11 12
-  t.emplace(10);
-  t.emplace(11);
-  t.emplace(12);
-
-  EXPECT_EQ(5, t.size());
-  EXPECT_THAT(t, UnorderedElementsAre(1, 10, 3, 11, 12));
-}
-
-TEST(Table, Clear) {
-  IntTable t;
-  EXPECT_TRUE(t.find(0) == t.end());
-  t.clear();
-  EXPECT_TRUE(t.find(0) == t.end());
-  auto res = t.emplace(0);
-  EXPECT_TRUE(res.second);
-  EXPECT_EQ(1, t.size());
-  t.clear();
-  EXPECT_EQ(0, t.size());
-  EXPECT_TRUE(t.find(0) == t.end());
-}
-
-TEST(Table, Swap) {
-  IntTable t;
-  EXPECT_TRUE(t.find(0) == t.end());
-  auto res = t.emplace(0);
-  EXPECT_TRUE(res.second);
-  EXPECT_EQ(1, t.size());
-  IntTable u;
-  t.swap(u);
-  EXPECT_EQ(0, t.size());
-  EXPECT_EQ(1, u.size());
-  EXPECT_TRUE(t.find(0) == t.end());
-  EXPECT_THAT(*u.find(0), 0);
-}
-
-TEST(Table, Rehash) {
-  IntTable t;
-  EXPECT_TRUE(t.find(0) == t.end());
-  t.emplace(0);
-  t.emplace(1);
-  EXPECT_EQ(2, t.size());
-  t.rehash(128);
-  EXPECT_EQ(2, t.size());
-  EXPECT_THAT(*t.find(0), 0);
-  EXPECT_THAT(*t.find(1), 1);
-}
-
-TEST(Table, RehashDoesNotRehashWhenNotNecessary) {
-  IntTable t;
-  t.emplace(0);
-  t.emplace(1);
-  auto* p = &*t.find(0);
-  t.rehash(1);
-  EXPECT_EQ(p, &*t.find(0));
-}
-
-TEST(Table, RehashZeroDoesNotAllocateOnEmptyTable) {
-  IntTable t;
-  t.rehash(0);
-  EXPECT_EQ(0, t.bucket_count());
-}
-
-TEST(Table, RehashZeroDeallocatesEmptyTable) {
-  IntTable t;
-  t.emplace(0);
-  t.clear();
-  EXPECT_NE(0, t.bucket_count());
-  t.rehash(0);
-  EXPECT_EQ(0, t.bucket_count());
-}
-
-TEST(Table, RehashZeroForcesRehash) {
-  IntTable t;
-  t.emplace(0);
-  t.emplace(1);
-  auto* p = &*t.find(0);
-  t.rehash(0);
-  EXPECT_NE(p, &*t.find(0));
-}
-
-TEST(Table, ConstructFromInitList) {
-  using P = std::pair<std::string, std::string>;
-  struct Q {
-    operator P() const { return {}; }
-  };
-  StringTable t = {P(), Q(), {}, {{}, {}}};
-}
-
-TEST(Table, CopyConstruct) {
-  IntTable t;
-  t.emplace(0);
-  EXPECT_EQ(1, t.size());
-  {
-    IntTable u(t);
-    EXPECT_EQ(1, u.size());
-    EXPECT_THAT(*u.find(0), 0);
-  }
-  {
-    IntTable u{t};
-    EXPECT_EQ(1, u.size());
-    EXPECT_THAT(*u.find(0), 0);
-  }
-  {
-    IntTable u = t;
-    EXPECT_EQ(1, u.size());
-    EXPECT_THAT(*u.find(0), 0);
-  }
-}
-
-TEST(Table, CopyConstructWithAlloc) {
-  StringTable t;
-  t.emplace("a", "b");
-  EXPECT_EQ(1, t.size());
-  StringTable u(t, Alloc<std::pair<std::string, std::string>>());
-  EXPECT_EQ(1, u.size());
-  EXPECT_THAT(*u.find("a"), Pair("a", "b"));
-}
-
-struct ExplicitAllocIntTable
-    : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
-                   std::equal_to<int64_t>, Alloc<int64_t>> {
-  ExplicitAllocIntTable() {}
-};
-
-TEST(Table, AllocWithExplicitCtor) {
-  ExplicitAllocIntTable t;
-  EXPECT_EQ(0, t.size());
-}
-
-TEST(Table, MoveConstruct) {
-  {
-    StringTable t;
-    t.emplace("a", "b");
-    EXPECT_EQ(1, t.size());
-
-    StringTable u(std::move(t));
-    EXPECT_EQ(1, u.size());
-    EXPECT_THAT(*u.find("a"), Pair("a", "b"));
-  }
-  {
-    StringTable t;
-    t.emplace("a", "b");
-    EXPECT_EQ(1, t.size());
-
-    StringTable u{std::move(t)};
-    EXPECT_EQ(1, u.size());
-    EXPECT_THAT(*u.find("a"), Pair("a", "b"));
-  }
-  {
-    StringTable t;
-    t.emplace("a", "b");
-    EXPECT_EQ(1, t.size());
-
-    StringTable u = std::move(t);
-    EXPECT_EQ(1, u.size());
-    EXPECT_THAT(*u.find("a"), Pair("a", "b"));
-  }
-}
-
-TEST(Table, MoveConstructWithAlloc) {
-  StringTable t;
-  t.emplace("a", "b");
-  EXPECT_EQ(1, t.size());
-  StringTable u(std::move(t), Alloc<std::pair<std::string, std::string>>());
-  EXPECT_EQ(1, u.size());
-  EXPECT_THAT(*u.find("a"), Pair("a", "b"));
-}
-
-TEST(Table, CopyAssign) {
-  StringTable t;
-  t.emplace("a", "b");
-  EXPECT_EQ(1, t.size());
-  StringTable u;
-  u = t;
-  EXPECT_EQ(1, u.size());
-  EXPECT_THAT(*u.find("a"), Pair("a", "b"));
-}
-
-TEST(Table, CopySelfAssign) {
-  StringTable t;
-  t.emplace("a", "b");
-  EXPECT_EQ(1, t.size());
-  t = *&t;
-  EXPECT_EQ(1, t.size());
-  EXPECT_THAT(*t.find("a"), Pair("a", "b"));
-}
-
-TEST(Table, MoveAssign) {
-  StringTable t;
-  t.emplace("a", "b");
-  EXPECT_EQ(1, t.size());
-  StringTable u;
-  u = std::move(t);
-  EXPECT_EQ(1, u.size());
-  EXPECT_THAT(*u.find("a"), Pair("a", "b"));
-}
-
-TEST(Table, Equality) {
-  StringTable t;
-  std::vector<std::pair<std::string, std::string>> v = {{"a", "b"},
-                                                        {"aa", "bb"}};
-  t.insert(std::begin(v), std::end(v));
-  StringTable u = t;
-  EXPECT_EQ(u, t);
-}
-
-TEST(Table, Equality2) {
-  StringTable t;
-  std::vector<std::pair<std::string, std::string>> v1 = {{"a", "b"},
-                                                         {"aa", "bb"}};
-  t.insert(std::begin(v1), std::end(v1));
-  StringTable u;
-  std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
-                                                         {"aa", "aa"}};
-  u.insert(std::begin(v2), std::end(v2));
-  EXPECT_NE(u, t);
-}
-
-TEST(Table, Equality3) {
-  StringTable t;
-  std::vector<std::pair<std::string, std::string>> v1 = {{"b", "b"},
-                                                         {"bb", "bb"}};
-  t.insert(std::begin(v1), std::end(v1));
-  StringTable u;
-  std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
-                                                         {"aa", "aa"}};
-  u.insert(std::begin(v2), std::end(v2));
-  EXPECT_NE(u, t);
-}
-
-TEST(Table, NumDeletedRegression) {
-  IntTable t;
-  t.emplace(0);
-  t.erase(t.find(0));
-  // construct over a deleted slot.
-  t.emplace(0);
-  t.clear();
-}
-
-TEST(Table, FindFullDeletedRegression) {
-  IntTable t;
-  for (int i = 0; i < 1000; ++i) {
-    t.emplace(i);
-    t.erase(t.find(i));
-  }
-  EXPECT_EQ(0, t.size());
-}
-
-TEST(Table, ReplacingDeletedSlotDoesNotRehash) {
-  size_t n;
-  {
-    // Compute n such that n is the maximum number of elements before rehash.
-    IntTable t;
-    t.emplace(0);
-    size_t c = t.bucket_count();
-    for (n = 1; c == t.bucket_count(); ++n) t.emplace(n);
-    --n;
-  }
-  IntTable t;
-  t.rehash(n);
-  const size_t c = t.bucket_count();
-  for (size_t i = 0; i != n; ++i) t.emplace(i);
-  EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
-  t.erase(0);
-  t.emplace(0);
-  EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
-}
-
-TEST(Table, NoThrowMoveConstruct) {
-  ASSERT_TRUE(
-      std::is_nothrow_copy_constructible<absl::Hash<absl::string_view>>::value);
-  ASSERT_TRUE(std::is_nothrow_copy_constructible<
-              std::equal_to<absl::string_view>>::value);
-  ASSERT_TRUE(std::is_nothrow_copy_constructible<std::allocator<int>>::value);
-  EXPECT_TRUE(std::is_nothrow_move_constructible<StringTable>::value);
-}
-
-TEST(Table, NoThrowMoveAssign) {
-  ASSERT_TRUE(
-      std::is_nothrow_move_assignable<absl::Hash<absl::string_view>>::value);
-  ASSERT_TRUE(
-      std::is_nothrow_move_assignable<std::equal_to<absl::string_view>>::value);
-  ASSERT_TRUE(std::is_nothrow_move_assignable<std::allocator<int>>::value);
-  ASSERT_TRUE(
-      absl::allocator_traits<std::allocator<int>>::is_always_equal::value);
-  EXPECT_TRUE(std::is_nothrow_move_assignable<StringTable>::value);
-}
-
-TEST(Table, NoThrowSwappable) {
-  ASSERT_TRUE(
-      container_internal::IsNoThrowSwappable<absl::Hash<absl::string_view>>());
-  ASSERT_TRUE(container_internal::IsNoThrowSwappable<
-              std::equal_to<absl::string_view>>());
-  ASSERT_TRUE(container_internal::IsNoThrowSwappable<std::allocator<int>>());
-  EXPECT_TRUE(container_internal::IsNoThrowSwappable<StringTable>());
-}
-
-TEST(Table, HeterogeneousLookup) {
-  struct Hash {
-    size_t operator()(int64_t i) const { return i; }
-    size_t operator()(double i) const {
-      ADD_FAILURE();
-      return i;
-    }
-  };
-  struct Eq {
-    bool operator()(int64_t a, int64_t b) const { return a == b; }
-    bool operator()(double a, int64_t b) const {
-      ADD_FAILURE();
-      return a == b;
-    }
-    bool operator()(int64_t a, double b) const {
-      ADD_FAILURE();
-      return a == b;
-    }
-    bool operator()(double a, double b) const {
-      ADD_FAILURE();
-      return a == b;
-    }
-  };
-
-  struct THash {
-    using is_transparent = void;
-    size_t operator()(int64_t i) const { return i; }
-    size_t operator()(double i) const { return i; }
-  };
-  struct TEq {
-    using is_transparent = void;
-    bool operator()(int64_t a, int64_t b) const { return a == b; }
-    bool operator()(double a, int64_t b) const { return a == b; }
-    bool operator()(int64_t a, double b) const { return a == b; }
-    bool operator()(double a, double b) const { return a == b; }
-  };
-
-  raw_hash_set<IntPolicy, Hash, Eq, Alloc<int64_t>> s{0, 1, 2};
-  // It will convert to int64_t before the query.
-  EXPECT_EQ(1, *s.find(double{1.1}));
-
-  raw_hash_set<IntPolicy, THash, TEq, Alloc<int64_t>> ts{0, 1, 2};
-  // It will try to use the double, and fail to find the object.
-  EXPECT_TRUE(ts.find(1.1) == ts.end());
-}
-
-template <class Table>
-using CallFind = decltype(std::declval<Table&>().find(17));
-
-template <class Table>
-using CallErase = decltype(std::declval<Table&>().erase(17));
-
-template <class Table>
-using CallExtract = decltype(std::declval<Table&>().extract(17));
-
-template <class Table>
-using CallPrefetch = decltype(std::declval<Table&>().prefetch(17));
-
-template <class Table>
-using CallCount = decltype(std::declval<Table&>().count(17));
-
-template <template <typename> class C, class Table, class = void>
-struct VerifyResultOf : std::false_type {};
-
-template <template <typename> class C, class Table>
-struct VerifyResultOf<C, Table, absl::void_t<C<Table>>> : std::true_type {};
-
-TEST(Table, HeterogeneousLookupOverloads) {
-  using NonTransparentTable =
-      raw_hash_set<StringPolicy, absl::Hash<absl::string_view>,
-                   std::equal_to<absl::string_view>, std::allocator<int>>;
-
-  EXPECT_FALSE((VerifyResultOf<CallFind, NonTransparentTable>()));
-  EXPECT_FALSE((VerifyResultOf<CallErase, NonTransparentTable>()));
-  EXPECT_FALSE((VerifyResultOf<CallExtract, NonTransparentTable>()));
-  EXPECT_FALSE((VerifyResultOf<CallPrefetch, NonTransparentTable>()));
-  EXPECT_FALSE((VerifyResultOf<CallCount, NonTransparentTable>()));
-
-  using TransparentTable = raw_hash_set<
-      StringPolicy,
-      absl::container_internal::hash_default_hash<absl::string_view>,
-      absl::container_internal::hash_default_eq<absl::string_view>,
-      std::allocator<int>>;
-
-  EXPECT_TRUE((VerifyResultOf<CallFind, TransparentTable>()));
-  EXPECT_TRUE((VerifyResultOf<CallErase, TransparentTable>()));
-  EXPECT_TRUE((VerifyResultOf<CallExtract, TransparentTable>()));
-  EXPECT_TRUE((VerifyResultOf<CallPrefetch, TransparentTable>()));
-  EXPECT_TRUE((VerifyResultOf<CallCount, TransparentTable>()));
-}
-
-// TODO(alkis): Expand iterator tests.
-TEST(Iterator, IsDefaultConstructible) {
-  StringTable::iterator i;
-  EXPECT_TRUE(i == StringTable::iterator());
-}
-
-TEST(ConstIterator, IsDefaultConstructible) {
-  StringTable::const_iterator i;
-  EXPECT_TRUE(i == StringTable::const_iterator());
-}
-
-TEST(Iterator, ConvertsToConstIterator) {
-  StringTable::iterator i;
-  EXPECT_TRUE(i == StringTable::const_iterator());
-}
-
-TEST(Iterator, Iterates) {
-  IntTable t;
-  for (size_t i = 3; i != 6; ++i) EXPECT_TRUE(t.emplace(i).second);
-  EXPECT_THAT(t, UnorderedElementsAre(3, 4, 5));
-}
-
-TEST(Table, Merge) {
-  StringTable t1, t2;
-  t1.emplace("0", "-0");
-  t1.emplace("1", "-1");
-  t2.emplace("0", "~0");
-  t2.emplace("2", "~2");
-
-  EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1")));
-  EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0"), Pair("2", "~2")));
-
-  t1.merge(t2);
-  EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1"),
-                                       Pair("2", "~2")));
-  EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0")));
-}
-
-TEST(Nodes, EmptyNodeType) {
-  using node_type = StringTable::node_type;
-  node_type n;
-  EXPECT_FALSE(n);
-  EXPECT_TRUE(n.empty());
-
-  EXPECT_TRUE((std::is_same<node_type::allocator_type,
-                            StringTable::allocator_type>::value));
-}
-
-TEST(Nodes, ExtractInsert) {
-  constexpr char k0[] = "Very long string zero.";
-  constexpr char k1[] = "Very long string one.";
-  constexpr char k2[] = "Very long string two.";
-  StringTable t = {{k0, ""}, {k1, ""}, {k2, ""}};
-  EXPECT_THAT(t,
-              UnorderedElementsAre(Pair(k0, ""), Pair(k1, ""), Pair(k2, "")));
-
-  auto node = t.extract(k0);
-  EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
-  EXPECT_TRUE(node);
-  EXPECT_FALSE(node.empty());
-
-  StringTable t2;
-  StringTable::insert_return_type res = t2.insert(std::move(node));
-  EXPECT_TRUE(res.inserted);
-  EXPECT_THAT(*res.position, Pair(k0, ""));
-  EXPECT_FALSE(res.node);
-  EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
-
-  // Not there.
-  EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
-  node = t.extract("Not there!");
-  EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
-  EXPECT_FALSE(node);
-
-  // Inserting nothing.
-  res = t2.insert(std::move(node));
-  EXPECT_FALSE(res.inserted);
-  EXPECT_EQ(res.position, t2.end());
-  EXPECT_FALSE(res.node);
-  EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
-
-  t.emplace(k0, "1");
-  node = t.extract(k0);
-
-  // Insert duplicate.
-  res = t2.insert(std::move(node));
-  EXPECT_FALSE(res.inserted);
-  EXPECT_THAT(*res.position, Pair(k0, ""));
-  EXPECT_TRUE(res.node);
-  EXPECT_FALSE(node);
-}
-
-TEST(Nodes, HintInsert) {
-  IntTable t = {1, 2, 3};
-  auto node = t.extract(1);
-  EXPECT_THAT(t, UnorderedElementsAre(2, 3));
-  auto it = t.insert(t.begin(), std::move(node));
-  EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3));
-  EXPECT_EQ(*it, 1);
-  EXPECT_FALSE(node);
-
-  node = t.extract(2);
-  EXPECT_THAT(t, UnorderedElementsAre(1, 3));
-  // reinsert 2 to make the next insert fail.
-  t.insert(2);
-  EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3));
-  it = t.insert(t.begin(), std::move(node));
-  EXPECT_EQ(*it, 2);
-  // The node was not emptied by the insert call.
-  EXPECT_TRUE(node);
-}
-
-IntTable MakeSimpleTable(size_t size) {
-  IntTable t;
-  while (t.size() < size) t.insert(t.size());
-  return t;
-}
-
-std::vector<int> OrderOfIteration(const IntTable& t) {
-  return {t.begin(), t.end()};
-}
-
-// These IterationOrderChanges tests depend on non-deterministic behavior.
-// We are injecting non-determinism from the pointer of the table, but do so in
-// a way that only the page matters. We have to retry enough times to make sure
-// we are touching different memory pages to cause the ordering to change.
-// We also need to keep the old tables around to avoid getting the same memory
-// blocks over and over.
-TEST(Table, IterationOrderChangesByInstance) {
-  for (size_t size : {2, 6, 12, 20}) {
-    const auto reference_table = MakeSimpleTable(size);
-    const auto reference = OrderOfIteration(reference_table);
-
-    std::vector<IntTable> tables;
-    bool found_difference = false;
-    for (int i = 0; !found_difference && i < 5000; ++i) {
-      tables.push_back(MakeSimpleTable(size));
-      found_difference = OrderOfIteration(tables.back()) != reference;
-    }
-    if (!found_difference) {
-      FAIL()
-          << "Iteration order remained the same across many attempts with size "
-          << size;
-    }
-  }
-}
-
-TEST(Table, IterationOrderChangesOnRehash) {
-  std::vector<IntTable> garbage;
-  for (int i = 0; i < 5000; ++i) {
-    auto t = MakeSimpleTable(20);
-    const auto reference = OrderOfIteration(t);
-    // Force rehash to the same size.
-    t.rehash(0);
-    auto trial = OrderOfIteration(t);
-    if (trial != reference) {
-      // We are done.
-      return;
-    }
-    garbage.push_back(std::move(t));
-  }
-  FAIL() << "Iteration order remained the same across many attempts.";
-}
-
-// Verify that pointers are invalidated as soon as a second element is inserted.
-// This prevents dependency on pointer stability on small tables.
-TEST(Table, UnstablePointers) {
-  IntTable table;
-
-  const auto addr = [&](int i) {
-    return reinterpret_cast<uintptr_t>(&*table.find(i));
-  };
-
-  table.insert(0);
-  const uintptr_t old_ptr = addr(0);
-
-  // This causes a rehash.
-  table.insert(1);
-
-  EXPECT_NE(old_ptr, addr(0));
-}
-
-// Confirm that we assert if we try to erase() end().
-TEST(TableDeathTest, EraseOfEndAsserts) {
-  // Use an assert with side-effects to figure out if they are actually enabled.
-  bool assert_enabled = false;
-  assert([&]() {
-    assert_enabled = true;
-    return true;
-  }());
-  if (!assert_enabled) return;
-
-  IntTable t;
-  // Extra simple "regexp" as regexp support is highly varied across platforms.
-  constexpr char kDeathMsg[] = "Invalid operation on iterator";
-  EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg);
-}
-
-#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
-TEST(RawHashSamplerTest, Sample) {
-  // Enable the feature even if the prod default is off.
-  SetHashtablezEnabled(true);
-  SetHashtablezSampleParameter(100);
-
-  auto& sampler = HashtablezSampler::Global();
-  size_t start_size = 0;
-  start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
-
-  std::vector<IntTable> tables;
-  for (int i = 0; i < 1000000; ++i) {
-    tables.emplace_back();
-    tables.back().insert(1);
-  }
-  size_t end_size = 0;
-  end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; });
-
-  EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
-              0.01, 0.005);
-}
-#endif  // ABSL_INTERNAL_HASHTABLEZ_SAMPLE
-
-TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) {
-  // Enable the feature even if the prod default is off.
-  SetHashtablezEnabled(true);
-  SetHashtablezSampleParameter(100);
-
-  auto& sampler = HashtablezSampler::Global();
-  size_t start_size = 0;
-  start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
-
-  std::vector<CustomAllocIntTable> tables;
-  for (int i = 0; i < 1000000; ++i) {
-    tables.emplace_back();
-    tables.back().insert(1);
-  }
-  size_t end_size = 0;
-  end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; });
-
-  EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
-              0.00, 0.001);
-}
-
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-TEST(Sanitizer, PoisoningUnused) {
-  IntTable t;
-  t.reserve(5);
-  // Insert something to force an allocation.
-  int64_t& v1 = *t.insert(0).first;
-
-  // Make sure there is something to test.
-  ASSERT_GT(t.capacity(), 1);
-
-  int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t);
-  for (size_t i = 0; i < t.capacity(); ++i) {
-    EXPECT_EQ(slots + i != &v1, __asan_address_is_poisoned(slots + i));
-  }
-}
-
-TEST(Sanitizer, PoisoningOnErase) {
-  IntTable t;
-  int64_t& v = *t.insert(0).first;
-
-  EXPECT_FALSE(__asan_address_is_poisoned(&v));
-  t.erase(0);
-  EXPECT_TRUE(__asan_address_is_poisoned(&v));
-}
-#endif  // ABSL_HAVE_ADDRESS_SANITIZER
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/test_instance_tracker.cc b/third_party/abseil_cpp/absl/container/internal/test_instance_tracker.cc
deleted file mode 100644
index f9947f0475..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/test_instance_tracker.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/test_instance_tracker.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace test_internal {
-int BaseCountedInstance::num_instances_ = 0;
-int BaseCountedInstance::num_live_instances_ = 0;
-int BaseCountedInstance::num_moves_ = 0;
-int BaseCountedInstance::num_copies_ = 0;
-int BaseCountedInstance::num_swaps_ = 0;
-int BaseCountedInstance::num_comparisons_ = 0;
-
-}  // namespace test_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/test_instance_tracker.h b/third_party/abseil_cpp/absl/container/internal/test_instance_tracker.h
deleted file mode 100644
index 5ff6fd714e..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/test_instance_tracker.h
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
-#define ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
-
-#include <cstdlib>
-#include <ostream>
-
-#include "absl/types/compare.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace test_internal {
-
-// A type that counts number of occurrences of the type, the live occurrences of
-// the type, as well as the number of copies, moves, swaps, and comparisons that
-// have occurred on the type. This is used as a base class for the copyable,
-// copyable+movable, and movable types below that are used in actual tests. Use
-// InstanceTracker in tests to track the number of instances.
-class BaseCountedInstance {
- public:
-  explicit BaseCountedInstance(int x) : value_(x) {
-    ++num_instances_;
-    ++num_live_instances_;
-  }
-  BaseCountedInstance(const BaseCountedInstance& x)
-      : value_(x.value_), is_live_(x.is_live_) {
-    ++num_instances_;
-    if (is_live_) ++num_live_instances_;
-    ++num_copies_;
-  }
-  BaseCountedInstance(BaseCountedInstance&& x)
-      : value_(x.value_), is_live_(x.is_live_) {
-    x.is_live_ = false;
-    ++num_instances_;
-    ++num_moves_;
-  }
-  ~BaseCountedInstance() {
-    --num_instances_;
-    if (is_live_) --num_live_instances_;
-  }
-
-  BaseCountedInstance& operator=(const BaseCountedInstance& x) {
-    value_ = x.value_;
-    if (is_live_) --num_live_instances_;
-    is_live_ = x.is_live_;
-    if (is_live_) ++num_live_instances_;
-    ++num_copies_;
-    return *this;
-  }
-  BaseCountedInstance& operator=(BaseCountedInstance&& x) {
-    value_ = x.value_;
-    if (is_live_) --num_live_instances_;
-    is_live_ = x.is_live_;
-    x.is_live_ = false;
-    ++num_moves_;
-    return *this;
-  }
-
-  bool operator==(const BaseCountedInstance& x) const {
-    ++num_comparisons_;
-    return value_ == x.value_;
-  }
-
-  bool operator!=(const BaseCountedInstance& x) const {
-    ++num_comparisons_;
-    return value_ != x.value_;
-  }
-
-  bool operator<(const BaseCountedInstance& x) const {
-    ++num_comparisons_;
-    return value_ < x.value_;
-  }
-
-  bool operator>(const BaseCountedInstance& x) const {
-    ++num_comparisons_;
-    return value_ > x.value_;
-  }
-
-  bool operator<=(const BaseCountedInstance& x) const {
-    ++num_comparisons_;
-    return value_ <= x.value_;
-  }
-
-  bool operator>=(const BaseCountedInstance& x) const {
-    ++num_comparisons_;
-    return value_ >= x.value_;
-  }
-
-  absl::weak_ordering compare(const BaseCountedInstance& x) const {
-    ++num_comparisons_;
-    return value_ < x.value_
-               ? absl::weak_ordering::less
-               : value_ == x.value_ ? absl::weak_ordering::equivalent
-                                    : absl::weak_ordering::greater;
-  }
-
-  int value() const {
-    if (!is_live_) std::abort();
-    return value_;
-  }
-
-  friend std::ostream& operator<<(std::ostream& o,
-                                  const BaseCountedInstance& v) {
-    return o << "[value:" << v.value() << "]";
-  }
-
-  // Implementation of efficient swap() that counts swaps.
-  static void SwapImpl(
-      BaseCountedInstance& lhs,    // NOLINT(runtime/references)
-      BaseCountedInstance& rhs) {  // NOLINT(runtime/references)
-    using std::swap;
-    swap(lhs.value_, rhs.value_);
-    swap(lhs.is_live_, rhs.is_live_);
-    ++BaseCountedInstance::num_swaps_;
-  }
-
- private:
-  friend class InstanceTracker;
-
-  int value_;
-
-  // Indicates if the value is live, ie it hasn't been moved away from.
-  bool is_live_ = true;
-
-  // Number of instances.
-  static int num_instances_;
-
-  // Number of live instances (those that have not been moved away from.)
-  static int num_live_instances_;
-
-  // Number of times that BaseCountedInstance objects were moved.
-  static int num_moves_;
-
-  // Number of times that BaseCountedInstance objects were copied.
-  static int num_copies_;
-
-  // Number of times that BaseCountedInstance objects were swapped.
-  static int num_swaps_;
-
-  // Number of times that BaseCountedInstance objects were compared.
-  static int num_comparisons_;
-};
-
-// Helper to track the BaseCountedInstance instance counters. Expects that the
-// number of instances and live_instances are the same when it is constructed
-// and when it is destructed.
-class InstanceTracker {
- public:
-  InstanceTracker()
-      : start_instances_(BaseCountedInstance::num_instances_),
-        start_live_instances_(BaseCountedInstance::num_live_instances_) {
-    ResetCopiesMovesSwaps();
-  }
-  ~InstanceTracker() {
-    if (instances() != 0) std::abort();
-    if (live_instances() != 0) std::abort();
-  }
-
-  // Returns the number of BaseCountedInstance instances both containing valid
-  // values and those moved away from compared to when the InstanceTracker was
-  // constructed
-  int instances() const {
-    return BaseCountedInstance::num_instances_ - start_instances_;
-  }
-
-  // Returns the number of live BaseCountedInstance instances compared to when
-  // the InstanceTracker was constructed
-  int live_instances() const {
-    return BaseCountedInstance::num_live_instances_ - start_live_instances_;
-  }
-
-  // Returns the number of moves on BaseCountedInstance objects since
-  // construction or since the last call to ResetCopiesMovesSwaps().
-  int moves() const { return BaseCountedInstance::num_moves_ - start_moves_; }
-
-  // Returns the number of copies on BaseCountedInstance objects since
-  // construction or the last call to ResetCopiesMovesSwaps().
-  int copies() const {
-    return BaseCountedInstance::num_copies_ - start_copies_;
-  }
-
-  // Returns the number of swaps on BaseCountedInstance objects since
-  // construction or the last call to ResetCopiesMovesSwaps().
-  int swaps() const { return BaseCountedInstance::num_swaps_ - start_swaps_; }
-
-  // Returns the number of comparisons on BaseCountedInstance objects since
-  // construction or the last call to ResetCopiesMovesSwaps().
-  int comparisons() const {
-    return BaseCountedInstance::num_comparisons_ - start_comparisons_;
-  }
-
-  // Resets the base values for moves, copies, comparisons, and swaps to the
-  // current values, so that subsequent Get*() calls for moves, copies,
-  // comparisons, and swaps will compare to the situation at the point of this
-  // call.
-  void ResetCopiesMovesSwaps() {
-    start_moves_ = BaseCountedInstance::num_moves_;
-    start_copies_ = BaseCountedInstance::num_copies_;
-    start_swaps_ = BaseCountedInstance::num_swaps_;
-    start_comparisons_ = BaseCountedInstance::num_comparisons_;
-  }
-
- private:
-  int start_instances_;
-  int start_live_instances_;
-  int start_moves_;
-  int start_copies_;
-  int start_swaps_;
-  int start_comparisons_;
-};
-
-// Copyable, not movable.
-class CopyableOnlyInstance : public BaseCountedInstance {
- public:
-  explicit CopyableOnlyInstance(int x) : BaseCountedInstance(x) {}
-  CopyableOnlyInstance(const CopyableOnlyInstance& rhs) = default;
-  CopyableOnlyInstance& operator=(const CopyableOnlyInstance& rhs) = default;
-
-  friend void swap(CopyableOnlyInstance& lhs, CopyableOnlyInstance& rhs) {
-    BaseCountedInstance::SwapImpl(lhs, rhs);
-  }
-
-  static bool supports_move() { return false; }
-};
-
-// Copyable and movable.
-class CopyableMovableInstance : public BaseCountedInstance {
- public:
-  explicit CopyableMovableInstance(int x) : BaseCountedInstance(x) {}
-  CopyableMovableInstance(const CopyableMovableInstance& rhs) = default;
-  CopyableMovableInstance(CopyableMovableInstance&& rhs) = default;
-  CopyableMovableInstance& operator=(const CopyableMovableInstance& rhs) =
-      default;
-  CopyableMovableInstance& operator=(CopyableMovableInstance&& rhs) = default;
-
-  friend void swap(CopyableMovableInstance& lhs, CopyableMovableInstance& rhs) {
-    BaseCountedInstance::SwapImpl(lhs, rhs);
-  }
-
-  static bool supports_move() { return true; }
-};
-
-// Only movable, not default-constructible.
-class MovableOnlyInstance : public BaseCountedInstance {
- public:
-  explicit MovableOnlyInstance(int x) : BaseCountedInstance(x) {}
-  MovableOnlyInstance(MovableOnlyInstance&& other) = default;
-  MovableOnlyInstance& operator=(MovableOnlyInstance&& other) = default;
-
-  friend void swap(MovableOnlyInstance& lhs, MovableOnlyInstance& rhs) {
-    BaseCountedInstance::SwapImpl(lhs, rhs);
-  }
-
-  static bool supports_move() { return true; }
-};
-
-}  // namespace test_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/test_instance_tracker_test.cc b/third_party/abseil_cpp/absl/container/internal/test_instance_tracker_test.cc
deleted file mode 100644
index 1c6a4fa715..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/test_instance_tracker_test.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/internal/test_instance_tracker.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-using absl::test_internal::CopyableMovableInstance;
-using absl::test_internal::CopyableOnlyInstance;
-using absl::test_internal::InstanceTracker;
-using absl::test_internal::MovableOnlyInstance;
-
-TEST(TestInstanceTracker, CopyableMovable) {
-  InstanceTracker tracker;
-  CopyableMovableInstance src(1);
-  EXPECT_EQ(1, src.value()) << src;
-  CopyableMovableInstance copy(src);
-  CopyableMovableInstance move(std::move(src));
-  EXPECT_EQ(1, tracker.copies());
-  EXPECT_EQ(1, tracker.moves());
-  EXPECT_EQ(0, tracker.swaps());
-  EXPECT_EQ(3, tracker.instances());
-  EXPECT_EQ(2, tracker.live_instances());
-  tracker.ResetCopiesMovesSwaps();
-
-  CopyableMovableInstance copy_assign(1);
-  copy_assign = copy;
-  CopyableMovableInstance move_assign(1);
-  move_assign = std::move(move);
-  EXPECT_EQ(1, tracker.copies());
-  EXPECT_EQ(1, tracker.moves());
-  EXPECT_EQ(0, tracker.swaps());
-  EXPECT_EQ(5, tracker.instances());
-  EXPECT_EQ(3, tracker.live_instances());
-  tracker.ResetCopiesMovesSwaps();
-
-  {
-    using std::swap;
-    swap(move_assign, copy);
-    swap(copy, move_assign);
-    EXPECT_EQ(2, tracker.swaps());
-    EXPECT_EQ(0, tracker.copies());
-    EXPECT_EQ(0, tracker.moves());
-    EXPECT_EQ(5, tracker.instances());
-    EXPECT_EQ(3, tracker.live_instances());
-  }
-}
-
-TEST(TestInstanceTracker, CopyableOnly) {
-  InstanceTracker tracker;
-  CopyableOnlyInstance src(1);
-  EXPECT_EQ(1, src.value()) << src;
-  CopyableOnlyInstance copy(src);
-  CopyableOnlyInstance copy2(std::move(src));  // NOLINT
-  EXPECT_EQ(2, tracker.copies());
-  EXPECT_EQ(0, tracker.moves());
-  EXPECT_EQ(3, tracker.instances());
-  EXPECT_EQ(3, tracker.live_instances());
-  tracker.ResetCopiesMovesSwaps();
-
-  CopyableOnlyInstance copy_assign(1);
-  copy_assign = copy;
-  CopyableOnlyInstance copy_assign2(1);
-  copy_assign2 = std::move(copy2);  // NOLINT
-  EXPECT_EQ(2, tracker.copies());
-  EXPECT_EQ(0, tracker.moves());
-  EXPECT_EQ(5, tracker.instances());
-  EXPECT_EQ(5, tracker.live_instances());
-  tracker.ResetCopiesMovesSwaps();
-
-  {
-    using std::swap;
-    swap(src, copy);
-    swap(copy, src);
-    EXPECT_EQ(2, tracker.swaps());
-    EXPECT_EQ(0, tracker.copies());
-    EXPECT_EQ(0, tracker.moves());
-    EXPECT_EQ(5, tracker.instances());
-    EXPECT_EQ(5, tracker.live_instances());
-  }
-}
-
-TEST(TestInstanceTracker, MovableOnly) {
-  InstanceTracker tracker;
-  MovableOnlyInstance src(1);
-  EXPECT_EQ(1, src.value()) << src;
-  MovableOnlyInstance move(std::move(src));
-  MovableOnlyInstance move_assign(2);
-  move_assign = std::move(move);
-  EXPECT_EQ(3, tracker.instances());
-  EXPECT_EQ(1, tracker.live_instances());
-  EXPECT_EQ(2, tracker.moves());
-  EXPECT_EQ(0, tracker.copies());
-  tracker.ResetCopiesMovesSwaps();
-
-  {
-    using std::swap;
-    MovableOnlyInstance other(2);
-    swap(move_assign, other);
-    swap(other, move_assign);
-    EXPECT_EQ(2, tracker.swaps());
-    EXPECT_EQ(0, tracker.copies());
-    EXPECT_EQ(0, tracker.moves());
-    EXPECT_EQ(4, tracker.instances());
-    EXPECT_EQ(2, tracker.live_instances());
-  }
-}
-
-TEST(TestInstanceTracker, ExistingInstances) {
-  CopyableMovableInstance uncounted_instance(1);
-  CopyableMovableInstance uncounted_live_instance(
-      std::move(uncounted_instance));
-  InstanceTracker tracker;
-  EXPECT_EQ(0, tracker.instances());
-  EXPECT_EQ(0, tracker.live_instances());
-  EXPECT_EQ(0, tracker.copies());
-  {
-    CopyableMovableInstance instance1(1);
-    EXPECT_EQ(1, tracker.instances());
-    EXPECT_EQ(1, tracker.live_instances());
-    EXPECT_EQ(0, tracker.copies());
-    EXPECT_EQ(0, tracker.moves());
-    {
-      InstanceTracker tracker2;
-      CopyableMovableInstance instance2(instance1);
-      CopyableMovableInstance instance3(std::move(instance2));
-      EXPECT_EQ(3, tracker.instances());
-      EXPECT_EQ(2, tracker.live_instances());
-      EXPECT_EQ(1, tracker.copies());
-      EXPECT_EQ(1, tracker.moves());
-      EXPECT_EQ(2, tracker2.instances());
-      EXPECT_EQ(1, tracker2.live_instances());
-      EXPECT_EQ(1, tracker2.copies());
-      EXPECT_EQ(1, tracker2.moves());
-    }
-    EXPECT_EQ(1, tracker.instances());
-    EXPECT_EQ(1, tracker.live_instances());
-    EXPECT_EQ(1, tracker.copies());
-    EXPECT_EQ(1, tracker.moves());
-  }
-  EXPECT_EQ(0, tracker.instances());
-  EXPECT_EQ(0, tracker.live_instances());
-  EXPECT_EQ(1, tracker.copies());
-  EXPECT_EQ(1, tracker.moves());
-}
-
-TEST(TestInstanceTracker, Comparisons) {
-  InstanceTracker tracker;
-  MovableOnlyInstance one(1), two(2);
-
-  EXPECT_EQ(0, tracker.comparisons());
-  EXPECT_FALSE(one == two);
-  EXPECT_EQ(1, tracker.comparisons());
-  EXPECT_TRUE(one != two);
-  EXPECT_EQ(2, tracker.comparisons());
-  EXPECT_TRUE(one < two);
-  EXPECT_EQ(3, tracker.comparisons());
-  EXPECT_FALSE(one > two);
-  EXPECT_EQ(4, tracker.comparisons());
-  EXPECT_TRUE(one <= two);
-  EXPECT_EQ(5, tracker.comparisons());
-  EXPECT_FALSE(one >= two);
-  EXPECT_EQ(6, tracker.comparisons());
-  EXPECT_TRUE(one.compare(two) < 0);  // NOLINT
-  EXPECT_EQ(7, tracker.comparisons());
-
-  tracker.ResetCopiesMovesSwaps();
-  EXPECT_EQ(0, tracker.comparisons());
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/container/internal/tracked.h b/third_party/abseil_cpp/absl/container/internal/tracked.h
deleted file mode 100644
index 29f5829f71..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/tracked.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_TRACKED_H_
-#define ABSL_CONTAINER_INTERNAL_TRACKED_H_
-
-#include <stddef.h>
-
-#include <memory>
-#include <utility>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// A class that tracks its copies and moves so that it can be queried in tests.
-template <class T>
-class Tracked {
- public:
-  Tracked() {}
-  // NOLINTNEXTLINE(runtime/explicit)
-  Tracked(const T& val) : val_(val) {}
-  Tracked(const Tracked& that)
-      : val_(that.val_),
-        num_moves_(that.num_moves_),
-        num_copies_(that.num_copies_) {
-    ++(*num_copies_);
-  }
-  Tracked(Tracked&& that)
-      : val_(std::move(that.val_)),
-        num_moves_(std::move(that.num_moves_)),
-        num_copies_(std::move(that.num_copies_)) {
-    ++(*num_moves_);
-  }
-  Tracked& operator=(const Tracked& that) {
-    val_ = that.val_;
-    num_moves_ = that.num_moves_;
-    num_copies_ = that.num_copies_;
-    ++(*num_copies_);
-  }
-  Tracked& operator=(Tracked&& that) {
-    val_ = std::move(that.val_);
-    num_moves_ = std::move(that.num_moves_);
-    num_copies_ = std::move(that.num_copies_);
-    ++(*num_moves_);
-  }
-
-  const T& val() const { return val_; }
-
-  friend bool operator==(const Tracked& a, const Tracked& b) {
-    return a.val_ == b.val_;
-  }
-  friend bool operator!=(const Tracked& a, const Tracked& b) {
-    return !(a == b);
-  }
-
-  size_t num_copies() { return *num_copies_; }
-  size_t num_moves() { return *num_moves_; }
-
- private:
-  T val_;
-  std::shared_ptr<size_t> num_moves_ = std::make_shared<size_t>(0);
-  std::shared_ptr<size_t> num_copies_ = std::make_shared<size_t>(0);
-};
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_TRACKED_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_map_constructor_test.h b/third_party/abseil_cpp/absl/container/internal/unordered_map_constructor_test.h
deleted file mode 100644
index 76ee95e6ab..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_map_constructor_test.h
+++ /dev/null
@@ -1,489 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
-#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
-
-#include <algorithm>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/hash_generator_testing.h"
-#include "absl/container/internal/hash_policy_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class UnordMap>
-class ConstructorTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(ConstructorTest);
-
-TYPED_TEST_P(ConstructorTest, NoArgs) {
-  TypeParam m;
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(m, ::testing::UnorderedElementsAre());
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCount) {
-  TypeParam m(123);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(m, ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountHash) {
-  using H = typename TypeParam::hasher;
-  H hasher;
-  TypeParam m(123, hasher);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(m, ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountHashEqual) {
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  H hasher;
-  E equal;
-  TypeParam m(123, hasher, equal);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.key_eq(), equal);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(m, ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) {
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.key_eq(), equal);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(m, ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-template <typename T>
-struct is_std_unordered_map : std::false_type {};
-
-template <typename... T>
-struct is_std_unordered_map<std::unordered_map<T...>> : std::true_type {};
-
-#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
-using has_cxx14_std_apis = std::true_type;
-#else
-using has_cxx14_std_apis = std::false_type;
-#endif
-
-template <typename T>
-using expect_cxx14_apis =
-    absl::disjunction<absl::negation<is_std_unordered_map<T>>,
-                      has_cxx14_std_apis>;
-
-template <typename TypeParam>
-void BucketCountAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void BucketCountAllocTest(std::true_type) {
-  using A = typename TypeParam::allocator_type;
-  A alloc(0);
-  TypeParam m(123, alloc);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(m, ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
-  BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-template <typename TypeParam>
-void BucketCountHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void BucketCountHashAllocTest(std::true_type) {
-  using H = typename TypeParam::hasher;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  A alloc(0);
-  TypeParam m(123, hasher, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(m, ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
-  BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
-using has_alloc_std_constructors = std::true_type;
-#else
-using has_alloc_std_constructors = std::false_type;
-#endif
-
-template <typename T>
-using expect_alloc_constructors =
-    absl::disjunction<absl::negation<is_std_unordered_map<T>>,
-                      has_alloc_std_constructors>;
-
-template <typename TypeParam>
-void AllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void AllocTest(std::true_type) {
-  using A = typename TypeParam::allocator_type;
-  A alloc(0);
-  TypeParam m(alloc);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(m, ::testing::UnorderedElementsAre());
-}
-
-TYPED_TEST_P(ConstructorTest, Alloc) {
-  AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
-}
-
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.key_eq(), equal);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-template <typename TypeParam>
-void InputIteratorBucketAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InputIteratorBucketAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using A = typename TypeParam::allocator_type;
-  A alloc(0);
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end(), 123, alloc);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
-  InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-template <typename TypeParam>
-void InputIteratorBucketHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InputIteratorBucketHashAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  A alloc(0);
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end(), 123, hasher, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
-  InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-TYPED_TEST_P(ConstructorTest, CopyConstructor) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
-  TypeParam n(m);
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_EQ(m.get_allocator(), n.get_allocator());
-  EXPECT_EQ(m, n);
-}
-
-template <typename TypeParam>
-void CopyConstructorAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void CopyConstructorAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
-  TypeParam n(m, A(11));
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_NE(m.get_allocator(), n.get_allocator());
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
-  CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
-}
-
-// TODO(alkis): Test non-propagating allocators on copy constructors.
-
-TYPED_TEST_P(ConstructorTest, MoveConstructor) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
-  TypeParam t(m);
-  TypeParam n(std::move(t));
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_EQ(m.get_allocator(), n.get_allocator());
-  EXPECT_EQ(m, n);
-}
-
-template <typename TypeParam>
-void MoveConstructorAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void MoveConstructorAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
-  TypeParam t(m);
-  TypeParam n(std::move(t), A(1));
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_NE(m.get_allocator(), n.get_allocator());
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
-  MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
-}
-
-// TODO(alkis): Test non-propagating allocators on move constructors.
-
-TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(values, 123, hasher, equal, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.key_eq(), equal);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-template <typename TypeParam>
-void InitializerListBucketAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InitializerListBucketAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using A = typename TypeParam::allocator_type;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  A alloc(0);
-  TypeParam m(values, 123, alloc);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
-  InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-template <typename TypeParam>
-void InitializerListBucketHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InitializerListBucketHashAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  A alloc(0);
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  TypeParam m(values, 123, hasher, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
-  InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-TYPED_TEST_P(ConstructorTest, Assignment) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  hash_internal::Generator<T> gen;
-  TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
-  TypeParam n;
-  n = m;
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_EQ(m, n);
-}
-
-// TODO(alkis): Test [non-]propagating allocators on move/copy assignments
-// (it depends on traits).
-
-TYPED_TEST_P(ConstructorTest, MoveAssignment) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  hash_internal::Generator<T> gen;
-  TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
-  TypeParam t(m);
-  TypeParam n;
-  n = std::move(t);
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  TypeParam m;
-  m = values;
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-}
-
-TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  TypeParam m({gen(), gen(), gen()});
-  TypeParam n({gen()});
-  n = m;
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  TypeParam m({gen(), gen(), gen()});
-  TypeParam t(m);
-  TypeParam n({gen()});
-  n = std::move(t);
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  TypeParam m;
-  m = values;
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-}
-
-TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  TypeParam m(values);
-  m = *&m;  // Avoid -Wself-assign
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-}
-
-// We cannot test self move as standard states that it leaves standard
-// containers in unspecified state (and in practice in causes memory-leak
-// according to heap-checker!).
-
-REGISTER_TYPED_TEST_CASE_P(
-    ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual,
-    BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc,
-    InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
-    InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc,
-    MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc,
-    InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment,
-    MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting,
-    MoveAssignmentOverwritesExisting,
-    AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_map_lookup_test.h b/third_party/abseil_cpp/absl/container/internal/unordered_map_lookup_test.h
deleted file mode 100644
index e76421e508..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_map_lookup_test.h
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_
-#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/hash_generator_testing.h"
-#include "absl/container/internal/hash_policy_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class UnordMap>
-class LookupTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(LookupTest);
-
-TYPED_TEST_P(LookupTest, At) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end());
-  for (const auto& p : values) {
-    const auto& val = m.at(p.first);
-    EXPECT_EQ(p.second, val) << ::testing::PrintToString(p.first);
-  }
-}
-
-TYPED_TEST_P(LookupTest, OperatorBracket) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using V = typename TypeParam::mapped_type;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m;
-  for (const auto& p : values) {
-    auto& val = m[p.first];
-    EXPECT_EQ(V(), val) << ::testing::PrintToString(p.first);
-    val = p.second;
-  }
-  for (const auto& p : values)
-    EXPECT_EQ(p.second, m[p.first]) << ::testing::PrintToString(p.first);
-}
-
-TYPED_TEST_P(LookupTest, Count) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m;
-  for (const auto& p : values)
-    EXPECT_EQ(0, m.count(p.first)) << ::testing::PrintToString(p.first);
-  m.insert(values.begin(), values.end());
-  for (const auto& p : values)
-    EXPECT_EQ(1, m.count(p.first)) << ::testing::PrintToString(p.first);
-}
-
-TYPED_TEST_P(LookupTest, Find) {
-  using std::get;
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m;
-  for (const auto& p : values)
-    EXPECT_TRUE(m.end() == m.find(p.first))
-        << ::testing::PrintToString(p.first);
-  m.insert(values.begin(), values.end());
-  for (const auto& p : values) {
-    auto it = m.find(p.first);
-    EXPECT_TRUE(m.end() != it) << ::testing::PrintToString(p.first);
-    EXPECT_EQ(p.second, get<1>(*it)) << ::testing::PrintToString(p.first);
-  }
-}
-
-TYPED_TEST_P(LookupTest, EqualRange) {
-  using std::get;
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m;
-  for (const auto& p : values) {
-    auto r = m.equal_range(p.first);
-    ASSERT_EQ(0, std::distance(r.first, r.second));
-  }
-  m.insert(values.begin(), values.end());
-  for (const auto& p : values) {
-    auto r = m.equal_range(p.first);
-    ASSERT_EQ(1, std::distance(r.first, r.second));
-    EXPECT_EQ(p.second, get<1>(*r.first)) << ::testing::PrintToString(p.first);
-  }
-}
-
-REGISTER_TYPED_TEST_CASE_P(LookupTest, At, OperatorBracket, Count, Find,
-                           EqualRange);
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_map_members_test.h b/third_party/abseil_cpp/absl/container/internal/unordered_map_members_test.h
deleted file mode 100644
index 7d48cdb890..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_map_members_test.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
-#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
-
-#include <type_traits>
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class UnordMap>
-class MembersTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(MembersTest);
-
-template <typename T>
-void UseType() {}
-
-TYPED_TEST_P(MembersTest, Typedefs) {
-  EXPECT_TRUE((std::is_same<std::pair<const typename TypeParam::key_type,
-                                      typename TypeParam::mapped_type>,
-                            typename TypeParam::value_type>()));
-  EXPECT_TRUE((absl::conjunction<
-               absl::negation<std::is_signed<typename TypeParam::size_type>>,
-               std::is_integral<typename TypeParam::size_type>>()));
-  EXPECT_TRUE((absl::conjunction<
-               std::is_signed<typename TypeParam::difference_type>,
-               std::is_integral<typename TypeParam::difference_type>>()));
-  EXPECT_TRUE((std::is_convertible<
-               decltype(std::declval<const typename TypeParam::hasher&>()(
-                   std::declval<const typename TypeParam::key_type&>())),
-               size_t>()));
-  EXPECT_TRUE((std::is_convertible<
-               decltype(std::declval<const typename TypeParam::key_equal&>()(
-                   std::declval<const typename TypeParam::key_type&>(),
-                   std::declval<const typename TypeParam::key_type&>())),
-               bool>()));
-  EXPECT_TRUE((std::is_same<typename TypeParam::allocator_type::value_type,
-                            typename TypeParam::value_type>()));
-  EXPECT_TRUE((std::is_same<typename TypeParam::value_type&,
-                            typename TypeParam::reference>()));
-  EXPECT_TRUE((std::is_same<const typename TypeParam::value_type&,
-                            typename TypeParam::const_reference>()));
-  EXPECT_TRUE((std::is_same<typename std::allocator_traits<
-                                typename TypeParam::allocator_type>::pointer,
-                            typename TypeParam::pointer>()));
-  EXPECT_TRUE(
-      (std::is_same<typename std::allocator_traits<
-                        typename TypeParam::allocator_type>::const_pointer,
-                    typename TypeParam::const_pointer>()));
-}
-
-TYPED_TEST_P(MembersTest, SimpleFunctions) {
-  EXPECT_GT(TypeParam().max_size(), 0);
-}
-
-TYPED_TEST_P(MembersTest, BeginEnd) {
-  TypeParam t = {typename TypeParam::value_type{}};
-  EXPECT_EQ(t.begin(), t.cbegin());
-  EXPECT_EQ(t.end(), t.cend());
-  EXPECT_NE(t.begin(), t.end());
-  EXPECT_NE(t.cbegin(), t.cend());
-}
-
-REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd);
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_map_modifiers_test.h b/third_party/abseil_cpp/absl/container/internal/unordered_map_modifiers_test.h
deleted file mode 100644
index 8c9ca779a4..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_map_modifiers_test.h
+++ /dev/null
@@ -1,318 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_
-#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_
-
-#include <memory>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/hash_generator_testing.h"
-#include "absl/container/internal/hash_policy_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class UnordMap>
-class ModifiersTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(ModifiersTest);
-
-TYPED_TEST_P(ModifiersTest, Clear) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end());
-  ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  m.clear();
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAre());
-  EXPECT_TRUE(m.empty());
-}
-
-TYPED_TEST_P(ModifiersTest, Insert) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  auto p = m.insert(val);
-  EXPECT_TRUE(p.second);
-  EXPECT_EQ(val, *p.first);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
-  p = m.insert(val2);
-  EXPECT_FALSE(p.second);
-  EXPECT_EQ(val, *p.first);
-}
-
-TYPED_TEST_P(ModifiersTest, InsertHint) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  auto it = m.insert(m.end(), val);
-  EXPECT_TRUE(it != m.end());
-  EXPECT_EQ(val, *it);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
-  it = m.insert(it, val2);
-  EXPECT_TRUE(it != m.end());
-  EXPECT_EQ(val, *it);
-}
-
-TYPED_TEST_P(ModifiersTest, InsertRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m;
-  m.insert(values.begin(), values.end());
-  ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-}
-
-TYPED_TEST_P(ModifiersTest, InsertOrAssign) {
-#ifdef UNORDERED_MAP_CXX17
-  using std::get;
-  using K = typename TypeParam::key_type;
-  using V = typename TypeParam::mapped_type;
-  K k = hash_internal::Generator<K>()();
-  V val = hash_internal::Generator<V>()();
-  TypeParam m;
-  auto p = m.insert_or_assign(k, val);
-  EXPECT_TRUE(p.second);
-  EXPECT_EQ(k, get<0>(*p.first));
-  EXPECT_EQ(val, get<1>(*p.first));
-  V val2 = hash_internal::Generator<V>()();
-  p = m.insert_or_assign(k, val2);
-  EXPECT_FALSE(p.second);
-  EXPECT_EQ(k, get<0>(*p.first));
-  EXPECT_EQ(val2, get<1>(*p.first));
-#endif
-}
-
-TYPED_TEST_P(ModifiersTest, InsertOrAssignHint) {
-#ifdef UNORDERED_MAP_CXX17
-  using std::get;
-  using K = typename TypeParam::key_type;
-  using V = typename TypeParam::mapped_type;
-  K k = hash_internal::Generator<K>()();
-  V val = hash_internal::Generator<V>()();
-  TypeParam m;
-  auto it = m.insert_or_assign(m.end(), k, val);
-  EXPECT_TRUE(it != m.end());
-  EXPECT_EQ(k, get<0>(*it));
-  EXPECT_EQ(val, get<1>(*it));
-  V val2 = hash_internal::Generator<V>()();
-  it = m.insert_or_assign(it, k, val2);
-  EXPECT_EQ(k, get<0>(*it));
-  EXPECT_EQ(val2, get<1>(*it));
-#endif
-}
-
-TYPED_TEST_P(ModifiersTest, Emplace) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
-  // with test traits/policy.
-  auto p = m.emplace(val);
-  EXPECT_TRUE(p.second);
-  EXPECT_EQ(val, *p.first);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
-  p = m.emplace(val2);
-  EXPECT_FALSE(p.second);
-  EXPECT_EQ(val, *p.first);
-}
-
-TYPED_TEST_P(ModifiersTest, EmplaceHint) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
-  // with test traits/policy.
-  auto it = m.emplace_hint(m.end(), val);
-  EXPECT_EQ(val, *it);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
-  it = m.emplace_hint(it, val2);
-  EXPECT_EQ(val, *it);
-}
-
-TYPED_TEST_P(ModifiersTest, TryEmplace) {
-#ifdef UNORDERED_MAP_CXX17
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
-  // with test traits/policy.
-  auto p = m.try_emplace(val.first, val.second);
-  EXPECT_TRUE(p.second);
-  EXPECT_EQ(val, *p.first);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
-  p = m.try_emplace(val2.first, val2.second);
-  EXPECT_FALSE(p.second);
-  EXPECT_EQ(val, *p.first);
-#endif
-}
-
-TYPED_TEST_P(ModifiersTest, TryEmplaceHint) {
-#ifdef UNORDERED_MAP_CXX17
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
-  // with test traits/policy.
-  auto it = m.try_emplace(m.end(), val.first, val.second);
-  EXPECT_EQ(val, *it);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
-  it = m.try_emplace(it, val2.first, val2.second);
-  EXPECT_EQ(val, *it);
-#endif
-}
-
-template <class V>
-using IfNotVoid = typename std::enable_if<!std::is_void<V>::value, V>::type;
-
-// In openmap we chose not to return the iterator from erase because that's
-// more expensive. As such we adapt erase to return an iterator here.
-struct EraseFirst {
-  template <class Map>
-  auto operator()(Map* m, int) const
-      -> IfNotVoid<decltype(m->erase(m->begin()))> {
-    return m->erase(m->begin());
-  }
-  template <class Map>
-  typename Map::iterator operator()(Map* m, ...) const {
-    auto it = m->begin();
-    m->erase(it++);
-    return it;
-  }
-};
-
-TYPED_TEST_P(ModifiersTest, Erase) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using std::get;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end());
-  ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  auto& first = *m.begin();
-  std::vector<T> values2;
-  for (const auto& val : values)
-    if (get<0>(val) != get<0>(first)) values2.push_back(val);
-  auto it = EraseFirst()(&m, 0);
-  ASSERT_TRUE(it != m.end());
-  EXPECT_EQ(1, std::count(values2.begin(), values2.end(), *it));
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values2.begin(),
-                                                             values2.end()));
-}
-
-TYPED_TEST_P(ModifiersTest, EraseRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end());
-  ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  auto it = m.erase(m.begin(), m.end());
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAre());
-  EXPECT_TRUE(it == m.end());
-}
-
-TYPED_TEST_P(ModifiersTest, EraseKey) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end());
-  ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_EQ(1, m.erase(values[0].first));
-  EXPECT_EQ(0, std::count(m.begin(), m.end(), values[0]));
-  EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values.begin() + 1,
-                                                             values.end()));
-}
-
-TYPED_TEST_P(ModifiersTest, Swap) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> v1;
-  std::vector<T> v2;
-  std::generate_n(std::back_inserter(v1), 5, hash_internal::Generator<T>());
-  std::generate_n(std::back_inserter(v2), 5, hash_internal::Generator<T>());
-  TypeParam m1(v1.begin(), v1.end());
-  TypeParam m2(v2.begin(), v2.end());
-  EXPECT_THAT(items(m1), ::testing::UnorderedElementsAreArray(v1));
-  EXPECT_THAT(items(m2), ::testing::UnorderedElementsAreArray(v2));
-  m1.swap(m2);
-  EXPECT_THAT(items(m1), ::testing::UnorderedElementsAreArray(v2));
-  EXPECT_THAT(items(m2), ::testing::UnorderedElementsAreArray(v1));
-}
-
-// TODO(alkis): Write tests for extract.
-// TODO(alkis): Write tests for merge.
-
-REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
-                           InsertRange, InsertOrAssign, InsertOrAssignHint,
-                           Emplace, EmplaceHint, TryEmplace, TryEmplaceHint,
-                           Erase, EraseRange, EraseKey, Swap);
-
-template <typename Type>
-struct is_unique_ptr : std::false_type {};
-
-template <typename Type>
-struct is_unique_ptr<std::unique_ptr<Type>> : std::true_type {};
-
-template <class UnordMap>
-class UniquePtrModifiersTest : public ::testing::Test {
- protected:
-  UniquePtrModifiersTest() {
-    static_assert(is_unique_ptr<typename UnordMap::mapped_type>::value,
-                  "UniquePtrModifiersTyest may only be called with a "
-                  "std::unique_ptr value type.");
-  }
-};
-
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UniquePtrModifiersTest);
-
-TYPED_TEST_SUITE_P(UniquePtrModifiersTest);
-
-// Test that we do not move from rvalue arguments if an insertion does not
-// happen.
-TYPED_TEST_P(UniquePtrModifiersTest, TryEmplace) {
-#ifdef UNORDERED_MAP_CXX17
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using V = typename TypeParam::mapped_type;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  auto p = m.try_emplace(val.first, std::move(val.second));
-  EXPECT_TRUE(p.second);
-  // A moved from std::unique_ptr is guaranteed to be nullptr.
-  EXPECT_EQ(val.second, nullptr);
-  T val2 = {val.first, hash_internal::Generator<V>()()};
-  p = m.try_emplace(val2.first, std::move(val2.second));
-  EXPECT_FALSE(p.second);
-  EXPECT_NE(val2.second, nullptr);
-#endif
-}
-
-REGISTER_TYPED_TEST_SUITE_P(UniquePtrModifiersTest, TryEmplace);
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_map_test.cc b/third_party/abseil_cpp/absl/container/internal/unordered_map_test.cc
deleted file mode 100644
index 9cbf512f32..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_map_test.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <memory>
-#include <unordered_map>
-
-#include "absl/container/internal/unordered_map_constructor_test.h"
-#include "absl/container/internal/unordered_map_lookup_test.h"
-#include "absl/container/internal/unordered_map_members_test.h"
-#include "absl/container/internal/unordered_map_modifiers_test.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using MapTypes = ::testing::Types<
-    std::unordered_map<int, int, StatefulTestingHash, StatefulTestingEqual,
-                       Alloc<std::pair<const int, int>>>,
-    std::unordered_map<std::string, std::string, StatefulTestingHash,
-                       StatefulTestingEqual,
-                       Alloc<std::pair<const std::string, std::string>>>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ConstructorTest, MapTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, LookupTest, MapTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, MembersTest, MapTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ModifiersTest, MapTypes);
-
-using UniquePtrMapTypes = ::testing::Types<std::unordered_map<
-    int, std::unique_ptr<int>, StatefulTestingHash, StatefulTestingEqual,
-    Alloc<std::pair<const int, std::unique_ptr<int>>>>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, UniquePtrModifiersTest,
-                               UniquePtrMapTypes);
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_set_constructor_test.h b/third_party/abseil_cpp/absl/container/internal/unordered_set_constructor_test.h
deleted file mode 100644
index 41165b05e9..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_set_constructor_test.h
+++ /dev/null
@@ -1,496 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
-#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
-
-#include <algorithm>
-#include <unordered_set>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/hash_generator_testing.h"
-#include "absl/container/internal/hash_policy_testing.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class UnordMap>
-class ConstructorTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(ConstructorTest);
-
-TYPED_TEST_P(ConstructorTest, NoArgs) {
-  TypeParam m;
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCount) {
-  TypeParam m(123);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountHash) {
-  using H = typename TypeParam::hasher;
-  H hasher;
-  TypeParam m(123, hasher);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountHashEqual) {
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  H hasher;
-  E equal;
-  TypeParam m(123, hasher, equal);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.key_eq(), equal);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) {
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.key_eq(), equal);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-
-  const auto& cm = m;
-  EXPECT_EQ(cm.hash_function(), hasher);
-  EXPECT_EQ(cm.key_eq(), equal);
-  EXPECT_EQ(cm.get_allocator(), alloc);
-  EXPECT_TRUE(cm.empty());
-  EXPECT_THAT(keys(cm), ::testing::UnorderedElementsAre());
-  EXPECT_GE(cm.bucket_count(), 123);
-}
-
-template <typename T>
-struct is_std_unordered_set : std::false_type {};
-
-template <typename... T>
-struct is_std_unordered_set<std::unordered_set<T...>> : std::true_type {};
-
-#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
-using has_cxx14_std_apis = std::true_type;
-#else
-using has_cxx14_std_apis = std::false_type;
-#endif
-
-template <typename T>
-using expect_cxx14_apis =
-    absl::disjunction<absl::negation<is_std_unordered_set<T>>,
-                      has_cxx14_std_apis>;
-
-template <typename TypeParam>
-void BucketCountAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void BucketCountAllocTest(std::true_type) {
-  using A = typename TypeParam::allocator_type;
-  A alloc(0);
-  TypeParam m(123, alloc);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
-  BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-template <typename TypeParam>
-void BucketCountHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void BucketCountHashAllocTest(std::true_type) {
-  using H = typename TypeParam::hasher;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  A alloc(0);
-  TypeParam m(123, hasher, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
-  BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
-using has_alloc_std_constructors = std::true_type;
-#else
-using has_alloc_std_constructors = std::false_type;
-#endif
-
-template <typename T>
-using expect_alloc_constructors =
-    absl::disjunction<absl::negation<is_std_unordered_set<T>>,
-                      has_alloc_std_constructors>;
-
-template <typename TypeParam>
-void AllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void AllocTest(std::true_type) {
-  using A = typename TypeParam::allocator_type;
-  A alloc(0);
-  TypeParam m(alloc);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_TRUE(m.empty());
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-}
-
-TYPED_TEST_P(ConstructorTest, Alloc) {
-  AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
-}
-
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  std::vector<T> values;
-  for (size_t i = 0; i != 10; ++i)
-    values.push_back(hash_internal::Generator<T>()());
-  TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.key_eq(), equal);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-template <typename TypeParam>
-void InputIteratorBucketAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InputIteratorBucketAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using A = typename TypeParam::allocator_type;
-  A alloc(0);
-  std::vector<T> values;
-  for (size_t i = 0; i != 10; ++i)
-    values.push_back(hash_internal::Generator<T>()());
-  TypeParam m(values.begin(), values.end(), 123, alloc);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
-  InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-template <typename TypeParam>
-void InputIteratorBucketHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InputIteratorBucketHashAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  A alloc(0);
-  std::vector<T> values;
-  for (size_t i = 0; i != 10; ++i)
-    values.push_back(hash_internal::Generator<T>()());
-  TypeParam m(values.begin(), values.end(), 123, hasher, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
-  InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-TYPED_TEST_P(ConstructorTest, CopyConstructor) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
-  TypeParam n(m);
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_EQ(m.get_allocator(), n.get_allocator());
-  EXPECT_EQ(m, n);
-  EXPECT_NE(TypeParam(0, hasher, equal, alloc), n);
-}
-
-template <typename TypeParam>
-void CopyConstructorAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void CopyConstructorAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
-  TypeParam n(m, A(11));
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_NE(m.get_allocator(), n.get_allocator());
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
-  CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
-}
-
-// TODO(alkis): Test non-propagating allocators on copy constructors.
-
-TYPED_TEST_P(ConstructorTest, MoveConstructor) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
-  TypeParam t(m);
-  TypeParam n(std::move(t));
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_EQ(m.get_allocator(), n.get_allocator());
-  EXPECT_EQ(m, n);
-}
-
-template <typename TypeParam>
-void MoveConstructorAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void MoveConstructorAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(123, hasher, equal, alloc);
-  for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
-  TypeParam t(m);
-  TypeParam n(std::move(t), A(1));
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_NE(m.get_allocator(), n.get_allocator());
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
-  MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
-}
-
-// TODO(alkis): Test non-propagating allocators on move constructors.
-
-TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  TypeParam m(values, 123, hasher, equal, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.key_eq(), equal);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-template <typename TypeParam>
-void InitializerListBucketAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InitializerListBucketAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using A = typename TypeParam::allocator_type;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  A alloc(0);
-  TypeParam m(values, 123, alloc);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
-  InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-template <typename TypeParam>
-void InitializerListBucketHashAllocTest(std::false_type) {}
-
-template <typename TypeParam>
-void InitializerListBucketHashAllocTest(std::true_type) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  A alloc(0);
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  TypeParam m(values, 123, hasher, alloc);
-  EXPECT_EQ(m.hash_function(), hasher);
-  EXPECT_EQ(m.get_allocator(), alloc);
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_GE(m.bucket_count(), 123);
-}
-
-TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
-  InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
-}
-
-TYPED_TEST_P(ConstructorTest, CopyAssignment) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  hash_internal::Generator<T> gen;
-  TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
-  TypeParam n;
-  n = m;
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_EQ(m, n);
-}
-
-// TODO(alkis): Test [non-]propagating allocators on move/copy assignments
-// (it depends on traits).
-
-TYPED_TEST_P(ConstructorTest, MoveAssignment) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  using H = typename TypeParam::hasher;
-  using E = typename TypeParam::key_equal;
-  using A = typename TypeParam::allocator_type;
-  H hasher;
-  E equal;
-  A alloc(0);
-  hash_internal::Generator<T> gen;
-  TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
-  TypeParam t(m);
-  TypeParam n;
-  n = std::move(t);
-  EXPECT_EQ(m.hash_function(), n.hash_function());
-  EXPECT_EQ(m.key_eq(), n.key_eq());
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  TypeParam m;
-  m = values;
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-}
-
-TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  TypeParam m({gen(), gen(), gen()});
-  TypeParam n({gen()});
-  n = m;
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  TypeParam m({gen(), gen(), gen()});
-  TypeParam t(m);
-  TypeParam n({gen()});
-  n = std::move(t);
-  EXPECT_EQ(m, n);
-}
-
-TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  TypeParam m;
-  m = values;
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-}
-
-TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  hash_internal::Generator<T> gen;
-  std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
-  TypeParam m(values);
-  m = *&m;  // Avoid -Wself-assign.
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-}
-
-REGISTER_TYPED_TEST_CASE_P(
-    ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual,
-    BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc,
-    InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
-    InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc,
-    MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc,
-    InitializerListBucketAlloc, InitializerListBucketHashAlloc, CopyAssignment,
-    MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting,
-    MoveAssignmentOverwritesExisting,
-    AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_set_lookup_test.h b/third_party/abseil_cpp/absl/container/internal/unordered_set_lookup_test.h
deleted file mode 100644
index 8f2f4b207e..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_set_lookup_test.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_
-#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/hash_generator_testing.h"
-#include "absl/container/internal/hash_policy_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class UnordSet>
-class LookupTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(LookupTest);
-
-TYPED_TEST_P(LookupTest, Count) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m;
-  for (const auto& v : values)
-    EXPECT_EQ(0, m.count(v)) << ::testing::PrintToString(v);
-  m.insert(values.begin(), values.end());
-  for (const auto& v : values)
-    EXPECT_EQ(1, m.count(v)) << ::testing::PrintToString(v);
-}
-
-TYPED_TEST_P(LookupTest, Find) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m;
-  for (const auto& v : values)
-    EXPECT_TRUE(m.end() == m.find(v)) << ::testing::PrintToString(v);
-  m.insert(values.begin(), values.end());
-  for (const auto& v : values) {
-    typename TypeParam::iterator it = m.find(v);
-    static_assert(std::is_same<const typename TypeParam::value_type&,
-                               decltype(*it)>::value,
-                  "");
-    static_assert(std::is_same<const typename TypeParam::value_type*,
-                               decltype(it.operator->())>::value,
-                  "");
-    EXPECT_TRUE(m.end() != it) << ::testing::PrintToString(v);
-    EXPECT_EQ(v, *it) << ::testing::PrintToString(v);
-  }
-}
-
-TYPED_TEST_P(LookupTest, EqualRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m;
-  for (const auto& v : values) {
-    auto r = m.equal_range(v);
-    ASSERT_EQ(0, std::distance(r.first, r.second));
-  }
-  m.insert(values.begin(), values.end());
-  for (const auto& v : values) {
-    auto r = m.equal_range(v);
-    ASSERT_EQ(1, std::distance(r.first, r.second));
-    EXPECT_EQ(v, *r.first);
-  }
-}
-
-REGISTER_TYPED_TEST_CASE_P(LookupTest, Count, Find, EqualRange);
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_set_members_test.h b/third_party/abseil_cpp/absl/container/internal/unordered_set_members_test.h
deleted file mode 100644
index 4c5e104af2..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_set_members_test.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_
-#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_
-
-#include <type_traits>
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class UnordSet>
-class MembersTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(MembersTest);
-
-template <typename T>
-void UseType() {}
-
-TYPED_TEST_P(MembersTest, Typedefs) {
-  EXPECT_TRUE((std::is_same<typename TypeParam::key_type,
-                            typename TypeParam::value_type>()));
-  EXPECT_TRUE((absl::conjunction<
-               absl::negation<std::is_signed<typename TypeParam::size_type>>,
-               std::is_integral<typename TypeParam::size_type>>()));
-  EXPECT_TRUE((absl::conjunction<
-               std::is_signed<typename TypeParam::difference_type>,
-               std::is_integral<typename TypeParam::difference_type>>()));
-  EXPECT_TRUE((std::is_convertible<
-               decltype(std::declval<const typename TypeParam::hasher&>()(
-                   std::declval<const typename TypeParam::key_type&>())),
-               size_t>()));
-  EXPECT_TRUE((std::is_convertible<
-               decltype(std::declval<const typename TypeParam::key_equal&>()(
-                   std::declval<const typename TypeParam::key_type&>(),
-                   std::declval<const typename TypeParam::key_type&>())),
-               bool>()));
-  EXPECT_TRUE((std::is_same<typename TypeParam::allocator_type::value_type,
-                            typename TypeParam::value_type>()));
-  EXPECT_TRUE((std::is_same<typename TypeParam::value_type&,
-                            typename TypeParam::reference>()));
-  EXPECT_TRUE((std::is_same<const typename TypeParam::value_type&,
-                            typename TypeParam::const_reference>()));
-  EXPECT_TRUE((std::is_same<typename std::allocator_traits<
-                                typename TypeParam::allocator_type>::pointer,
-                            typename TypeParam::pointer>()));
-  EXPECT_TRUE(
-      (std::is_same<typename std::allocator_traits<
-                        typename TypeParam::allocator_type>::const_pointer,
-                    typename TypeParam::const_pointer>()));
-}
-
-TYPED_TEST_P(MembersTest, SimpleFunctions) {
-  EXPECT_GT(TypeParam().max_size(), 0);
-}
-
-TYPED_TEST_P(MembersTest, BeginEnd) {
-  TypeParam t = {typename TypeParam::value_type{}};
-  EXPECT_EQ(t.begin(), t.cbegin());
-  EXPECT_EQ(t.end(), t.cend());
-  EXPECT_NE(t.begin(), t.end());
-  EXPECT_NE(t.cbegin(), t.cend());
-}
-
-REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd);
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_set_modifiers_test.h b/third_party/abseil_cpp/absl/container/internal/unordered_set_modifiers_test.h
deleted file mode 100644
index 26be58d99f..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_set_modifiers_test.h
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_
-#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/hash_generator_testing.h"
-#include "absl/container/internal/hash_policy_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-template <class UnordSet>
-class ModifiersTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(ModifiersTest);
-
-TYPED_TEST_P(ModifiersTest, Clear) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end());
-  ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  m.clear();
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-  EXPECT_TRUE(m.empty());
-}
-
-TYPED_TEST_P(ModifiersTest, Insert) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  auto p = m.insert(val);
-  EXPECT_TRUE(p.second);
-  EXPECT_EQ(val, *p.first);
-  p = m.insert(val);
-  EXPECT_FALSE(p.second);
-}
-
-TYPED_TEST_P(ModifiersTest, InsertHint) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  auto it = m.insert(m.end(), val);
-  EXPECT_TRUE(it != m.end());
-  EXPECT_EQ(val, *it);
-  it = m.insert(it, val);
-  EXPECT_TRUE(it != m.end());
-  EXPECT_EQ(val, *it);
-}
-
-TYPED_TEST_P(ModifiersTest, InsertRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m;
-  m.insert(values.begin(), values.end());
-  ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-}
-
-TYPED_TEST_P(ModifiersTest, Emplace) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
-  // with test traits/policy.
-  auto p = m.emplace(val);
-  EXPECT_TRUE(p.second);
-  EXPECT_EQ(val, *p.first);
-  p = m.emplace(val);
-  EXPECT_FALSE(p.second);
-  EXPECT_EQ(val, *p.first);
-}
-
-TYPED_TEST_P(ModifiersTest, EmplaceHint) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  T val = hash_internal::Generator<T>()();
-  TypeParam m;
-  // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
-  // with test traits/policy.
-  auto it = m.emplace_hint(m.end(), val);
-  EXPECT_EQ(val, *it);
-  it = m.emplace_hint(it, val);
-  EXPECT_EQ(val, *it);
-}
-
-template <class V>
-using IfNotVoid = typename std::enable_if<!std::is_void<V>::value, V>::type;
-
-// In openmap we chose not to return the iterator from erase because that's
-// more expensive. As such we adapt erase to return an iterator here.
-struct EraseFirst {
-  template <class Map>
-  auto operator()(Map* m, int) const
-      -> IfNotVoid<decltype(m->erase(m->begin()))> {
-    return m->erase(m->begin());
-  }
-  template <class Map>
-  typename Map::iterator operator()(Map* m, ...) const {
-    auto it = m->begin();
-    m->erase(it++);
-    return it;
-  }
-};
-
-TYPED_TEST_P(ModifiersTest, Erase) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end());
-  ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  std::vector<T> values2;
-  for (const auto& val : values)
-    if (val != *m.begin()) values2.push_back(val);
-  auto it = EraseFirst()(&m, 0);
-  ASSERT_TRUE(it != m.end());
-  EXPECT_EQ(1, std::count(values2.begin(), values2.end(), *it));
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values2.begin(),
-                                                            values2.end()));
-}
-
-TYPED_TEST_P(ModifiersTest, EraseRange) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end());
-  ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  auto it = m.erase(m.begin(), m.end());
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
-  EXPECT_TRUE(it == m.end());
-}
-
-TYPED_TEST_P(ModifiersTest, EraseKey) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> values;
-  std::generate_n(std::back_inserter(values), 10,
-                  hash_internal::Generator<T>());
-  TypeParam m(values.begin(), values.end());
-  ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
-  EXPECT_EQ(1, m.erase(values[0]));
-  EXPECT_EQ(0, std::count(m.begin(), m.end(), values[0]));
-  EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values.begin() + 1,
-                                                            values.end()));
-}
-
-TYPED_TEST_P(ModifiersTest, Swap) {
-  using T = hash_internal::GeneratedType<TypeParam>;
-  std::vector<T> v1;
-  std::vector<T> v2;
-  std::generate_n(std::back_inserter(v1), 5, hash_internal::Generator<T>());
-  std::generate_n(std::back_inserter(v2), 5, hash_internal::Generator<T>());
-  TypeParam m1(v1.begin(), v1.end());
-  TypeParam m2(v2.begin(), v2.end());
-  EXPECT_THAT(keys(m1), ::testing::UnorderedElementsAreArray(v1));
-  EXPECT_THAT(keys(m2), ::testing::UnorderedElementsAreArray(v2));
-  m1.swap(m2);
-  EXPECT_THAT(keys(m1), ::testing::UnorderedElementsAreArray(v2));
-  EXPECT_THAT(keys(m2), ::testing::UnorderedElementsAreArray(v1));
-}
-
-// TODO(alkis): Write tests for extract.
-// TODO(alkis): Write tests for merge.
-
-REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
-                           InsertRange, Emplace, EmplaceHint, Erase, EraseRange,
-                           EraseKey, Swap);
-
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_
diff --git a/third_party/abseil_cpp/absl/container/internal/unordered_set_test.cc b/third_party/abseil_cpp/absl/container/internal/unordered_set_test.cc
deleted file mode 100644
index a134b53984..0000000000
--- a/third_party/abseil_cpp/absl/container/internal/unordered_set_test.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <unordered_set>
-
-#include "absl/container/internal/unordered_set_constructor_test.h"
-#include "absl/container/internal/unordered_set_lookup_test.h"
-#include "absl/container/internal/unordered_set_members_test.h"
-#include "absl/container/internal/unordered_set_modifiers_test.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using SetTypes = ::testing::Types<
-    std::unordered_set<int, StatefulTestingHash, StatefulTestingEqual,
-                       Alloc<int>>,
-    std::unordered_set<std::string, StatefulTestingHash, StatefulTestingEqual,
-                       Alloc<std::string>>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ConstructorTest, SetTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, LookupTest, SetTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, MembersTest, SetTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ModifiersTest, SetTypes);
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/node_hash_map.h b/third_party/abseil_cpp/absl/container/node_hash_map.h
deleted file mode 100644
index 7a39f6284c..0000000000
--- a/third_party/abseil_cpp/absl/container/node_hash_map.h
+++ /dev/null
@@ -1,597 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: node_hash_map.h
-// -----------------------------------------------------------------------------
-//
-// An `absl::node_hash_map<K, V>` is an unordered associative container of
-// unique keys and associated values designed to be a more efficient replacement
-// for `std::unordered_map`. Like `unordered_map`, search, insertion, and
-// deletion of map elements can be done as an `O(1)` operation. However,
-// `node_hash_map` (and other unordered associative containers known as the
-// collection of Abseil "Swiss tables") contain other optimizations that result
-// in both memory and computation advantages.
-//
-// In most cases, your default choice for a hash map should be a map of type
-// `flat_hash_map`. However, if you need pointer stability and cannot store
-// a `flat_hash_map` with `unique_ptr` elements, a `node_hash_map` may be a
-// valid alternative. As well, if you are migrating your code from using
-// `std::unordered_map`, a `node_hash_map` provides a more straightforward
-// migration, because it guarantees pointer stability. Consider migrating to
-// `node_hash_map` and perhaps converting to a more efficient `flat_hash_map`
-// upon further review.
-
-#ifndef ABSL_CONTAINER_NODE_HASH_MAP_H_
-#define ABSL_CONTAINER_NODE_HASH_MAP_H_
-
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "absl/algorithm/container.h"
-#include "absl/container/internal/container_memory.h"
-#include "absl/container/internal/hash_function_defaults.h"  // IWYU pragma: export
-#include "absl/container/internal/node_hash_policy.h"
-#include "absl/container/internal/raw_hash_map.h"  // IWYU pragma: export
-#include "absl/memory/memory.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-template <class Key, class Value>
-class NodeHashMapPolicy;
-}  // namespace container_internal
-
-// -----------------------------------------------------------------------------
-// absl::node_hash_map
-// -----------------------------------------------------------------------------
-//
-// An `absl::node_hash_map<K, V>` is an unordered associative container which
-// has been optimized for both speed and memory footprint in most common use
-// cases. Its interface is similar to that of `std::unordered_map<K, V>` with
-// the following notable differences:
-//
-// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
-//   `insert()`, provided that the map is provided a compatible heterogeneous
-//   hashing function and equality operator.
-// * Contains a `capacity()` member function indicating the number of element
-//   slots (open, deleted, and empty) within the hash map.
-// * Returns `void` from the `erase(iterator)` overload.
-//
-// By default, `node_hash_map` uses the `absl::Hash` hashing framework.
-// All fundamental and Abseil types that support the `absl::Hash` framework have
-// a compatible equality operator for comparing insertions into `node_hash_map`.
-// If your type is not yet supported by the `absl::Hash` framework, see
-// absl/hash/hash.h for information on extending Abseil hashing to user-defined
-// types.
-//
-// Example:
-//
-//   // Create a node hash map of three strings (that map to strings)
-//   absl::node_hash_map<std::string, std::string> ducks =
-//     {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}};
-//
-//  // Insert a new element into the node hash map
-//  ducks.insert({"d", "donald"}};
-//
-//  // Force a rehash of the node hash map
-//  ducks.rehash(0);
-//
-//  // Find the element with the key "b"
-//  std::string search_key = "b";
-//  auto result = ducks.find(search_key);
-//  if (result != ducks.end()) {
-//    std::cout << "Result: " << result->second << std::endl;
-//  }
-template <class Key, class Value,
-          class Hash = absl::container_internal::hash_default_hash<Key>,
-          class Eq = absl::container_internal::hash_default_eq<Key>,
-          class Alloc = std::allocator<std::pair<const Key, Value>>>
-class node_hash_map
-    : public absl::container_internal::raw_hash_map<
-          absl::container_internal::NodeHashMapPolicy<Key, Value>, Hash, Eq,
-          Alloc> {
-  using Base = typename node_hash_map::raw_hash_map;
-
- public:
-  // Constructors and Assignment Operators
-  //
-  // A node_hash_map supports the same overload set as `std::unordered_map`
-  // for construction and assignment:
-  //
-  // *  Default constructor
-  //
-  //    // No allocation for the table's elements is made.
-  //    absl::node_hash_map<int, std::string> map1;
-  //
-  // * Initializer List constructor
-  //
-  //   absl::node_hash_map<int, std::string> map2 =
-  //       {{1, "huey"}, {2, "dewey"}, {3, "louie"},};
-  //
-  // * Copy constructor
-  //
-  //   absl::node_hash_map<int, std::string> map3(map2);
-  //
-  // * Copy assignment operator
-  //
-  //  // Hash functor and Comparator are copied as well
-  //  absl::node_hash_map<int, std::string> map4;
-  //  map4 = map3;
-  //
-  // * Move constructor
-  //
-  //   // Move is guaranteed efficient
-  //   absl::node_hash_map<int, std::string> map5(std::move(map4));
-  //
-  // * Move assignment operator
-  //
-  //   // May be efficient if allocators are compatible
-  //   absl::node_hash_map<int, std::string> map6;
-  //   map6 = std::move(map5);
-  //
-  // * Range constructor
-  //
-  //   std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}};
-  //   absl::node_hash_map<int, std::string> map7(v.begin(), v.end());
-  node_hash_map() {}
-  using Base::Base;
-
-  // node_hash_map::begin()
-  //
-  // Returns an iterator to the beginning of the `node_hash_map`.
-  using Base::begin;
-
-  // node_hash_map::cbegin()
-  //
-  // Returns a const iterator to the beginning of the `node_hash_map`.
-  using Base::cbegin;
-
-  // node_hash_map::cend()
-  //
-  // Returns a const iterator to the end of the `node_hash_map`.
-  using Base::cend;
-
-  // node_hash_map::end()
-  //
-  // Returns an iterator to the end of the `node_hash_map`.
-  using Base::end;
-
-  // node_hash_map::capacity()
-  //
-  // Returns the number of element slots (assigned, deleted, and empty)
-  // available within the `node_hash_map`.
-  //
-  // NOTE: this member function is particular to `absl::node_hash_map` and is
-  // not provided in the `std::unordered_map` API.
-  using Base::capacity;
-
-  // node_hash_map::empty()
-  //
-  // Returns whether or not the `node_hash_map` is empty.
-  using Base::empty;
-
-  // node_hash_map::max_size()
-  //
-  // Returns the largest theoretical possible number of elements within a
-  // `node_hash_map` under current memory constraints. This value can be thought
-  // of as the largest value of `std::distance(begin(), end())` for a
-  // `node_hash_map<K, V>`.
-  using Base::max_size;
-
-  // node_hash_map::size()
-  //
-  // Returns the number of elements currently within the `node_hash_map`.
-  using Base::size;
-
-  // node_hash_map::clear()
-  //
-  // Removes all elements from the `node_hash_map`. Invalidates any references,
-  // pointers, or iterators referring to contained elements.
-  //
-  // NOTE: this operation may shrink the underlying buffer. To avoid shrinking
-  // the underlying buffer call `erase(begin(), end())`.
-  using Base::clear;
-
-  // node_hash_map::erase()
-  //
-  // Erases elements within the `node_hash_map`. Erasing does not trigger a
-  // rehash. Overloads are listed below.
-  //
-  // void erase(const_iterator pos):
-  //
-  //   Erases the element at `position` of the `node_hash_map`, returning
-  //   `void`.
-  //
-  //   NOTE: this return behavior is different than that of STL containers in
-  //   general and `std::unordered_map` in particular.
-  //
-  // iterator erase(const_iterator first, const_iterator last):
-  //
-  //   Erases the elements in the open interval [`first`, `last`), returning an
-  //   iterator pointing to `last`.
-  //
-  // size_type erase(const key_type& key):
-  //
-  //   Erases the element with the matching key, if it exists, returning the
-  //   number of elements erased (0 or 1).
-  using Base::erase;
-
-  // node_hash_map::insert()
-  //
-  // Inserts an element of the specified value into the `node_hash_map`,
-  // returning an iterator pointing to the newly inserted element, provided that
-  // an element with the given key does not already exist. If rehashing occurs
-  // due to the insertion, all iterators are invalidated. Overloads are listed
-  // below.
-  //
-  // std::pair<iterator,bool> insert(const init_type& value):
-  //
-  //   Inserts a value into the `node_hash_map`. Returns a pair consisting of an
-  //   iterator to the inserted element (or to the element that prevented the
-  //   insertion) and a `bool` denoting whether the insertion took place.
-  //
-  // std::pair<iterator,bool> insert(T&& value):
-  // std::pair<iterator,bool> insert(init_type&& value):
-  //
-  //   Inserts a moveable value into the `node_hash_map`. Returns a `std::pair`
-  //   consisting of an iterator to the inserted element (or to the element that
-  //   prevented the insertion) and a `bool` denoting whether the insertion took
-  //   place.
-  //
-  // iterator insert(const_iterator hint, const init_type& value):
-  // iterator insert(const_iterator hint, T&& value):
-  // iterator insert(const_iterator hint, init_type&& value);
-  //
-  //   Inserts a value, using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search. Returns an iterator to the
-  //   inserted element, or to the existing element that prevented the
-  //   insertion.
-  //
-  // void insert(InputIterator first, InputIterator last):
-  //
-  //   Inserts a range of values [`first`, `last`).
-  //
-  //   NOTE: Although the STL does not specify which element may be inserted if
-  //   multiple keys compare equivalently, for `node_hash_map` we guarantee the
-  //   first match is inserted.
-  //
-  // void insert(std::initializer_list<init_type> ilist):
-  //
-  //   Inserts the elements within the initializer list `ilist`.
-  //
-  //   NOTE: Although the STL does not specify which element may be inserted if
-  //   multiple keys compare equivalently within the initializer list, for
-  //   `node_hash_map` we guarantee the first match is inserted.
-  using Base::insert;
-
-  // node_hash_map::insert_or_assign()
-  //
-  // Inserts an element of the specified value into the `node_hash_map` provided
-  // that a value with the given key does not already exist, or replaces it with
-  // the element value if a key for that value already exists, returning an
-  // iterator pointing to the newly inserted element. If rehashing occurs due to
-  // the insertion, all iterators are invalidated. Overloads are listed
-  // below.
-  //
-  // std::pair<iterator, bool> insert_or_assign(const init_type& k, T&& obj):
-  // std::pair<iterator, bool> insert_or_assign(init_type&& k, T&& obj):
-  //
-  //   Inserts/Assigns (or moves) the element of the specified key into the
-  //   `node_hash_map`.
-  //
-  // iterator insert_or_assign(const_iterator hint,
-  //                           const init_type& k, T&& obj):
-  // iterator insert_or_assign(const_iterator hint, init_type&& k, T&& obj):
-  //
-  //   Inserts/Assigns (or moves) the element of the specified key into the
-  //   `node_hash_map` using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search.
-  using Base::insert_or_assign;
-
-  // node_hash_map::emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `node_hash_map`, provided that no element with the given key
-  // already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately. Prefer `try_emplace()` unless your key is not
-  // copyable or moveable.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  using Base::emplace;
-
-  // node_hash_map::emplace_hint()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `node_hash_map`, using the position of `hint` as a non-binding
-  // suggestion for where to begin the insertion search, and only inserts
-  // provided that no element with the given key already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately. Prefer `try_emplace()` unless your key is not
-  // copyable or moveable.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  using Base::emplace_hint;
-
-  // node_hash_map::try_emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `node_hash_map`, provided that no element with the given key
-  // already exists. Unlike `emplace()`, if an element with the given key
-  // already exists, we guarantee that no element is constructed.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  // Overloads are listed below.
-  //
-  //   std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args):
-  //   std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args):
-  //
-  // Inserts (via copy or move) the element of the specified key into the
-  // `node_hash_map`.
-  //
-  //   iterator try_emplace(const_iterator hint,
-  //                        const init_type& k, Args&&... args):
-  //   iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args):
-  //
-  // Inserts (via copy or move) the element of the specified key into the
-  // `node_hash_map` using the position of `hint` as a non-binding suggestion
-  // for where to begin the insertion search.
-  //
-  // All `try_emplace()` overloads make the same guarantees regarding rvalue
-  // arguments as `std::unordered_map::try_emplace()`, namely that these
-  // functions will not move from rvalue arguments if insertions do not happen.
-  using Base::try_emplace;
-
-  // node_hash_map::extract()
-  //
-  // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
-  //
-  // node_type extract(const_iterator position):
-  //
-  //   Extracts the key,value pair of the element at the indicated position and
-  //   returns a node handle owning that extracted data.
-  //
-  // node_type extract(const key_type& x):
-  //
-  //   Extracts the key,value pair of the element with a key matching the passed
-  //   key value and returns a node handle owning that extracted data. If the
-  //   `node_hash_map` does not contain an element with a matching key, this
-  //   function returns an empty node handle.
-  //
-  // NOTE: when compiled in an earlier version of C++ than C++17,
-  // `node_type::key()` returns a const reference to the key instead of a
-  // mutable reference. We cannot safely return a mutable reference without
-  // std::launder (which is not available before C++17).
-  using Base::extract;
-
-  // node_hash_map::merge()
-  //
-  // Extracts elements from a given `source` node hash map into this
-  // `node_hash_map`. If the destination `node_hash_map` already contains an
-  // element with an equivalent key, that element is not extracted.
-  using Base::merge;
-
-  // node_hash_map::swap(node_hash_map& other)
-  //
-  // Exchanges the contents of this `node_hash_map` with those of the `other`
-  // node hash map, avoiding invocation of any move, copy, or swap operations on
-  // individual elements.
-  //
-  // All iterators and references on the `node_hash_map` remain valid, excepting
-  // for the past-the-end iterator, which is invalidated.
-  //
-  // `swap()` requires that the node hash map's hashing and key equivalence
-  // functions be Swappable, and are exchaged using unqualified calls to
-  // non-member `swap()`. If the map's allocator has
-  // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
-  // set to `true`, the allocators are also exchanged using an unqualified call
-  // to non-member `swap()`; otherwise, the allocators are not swapped.
-  using Base::swap;
-
-  // node_hash_map::rehash(count)
-  //
-  // Rehashes the `node_hash_map`, setting the number of slots to be at least
-  // the passed value. If the new number of slots increases the load factor more
-  // than the current maximum load factor
-  // (`count` < `size()` / `max_load_factor()`), then the new number of slots
-  // will be at least `size()` / `max_load_factor()`.
-  //
-  // To force a rehash, pass rehash(0).
-  using Base::rehash;
-
-  // node_hash_map::reserve(count)
-  //
-  // Sets the number of slots in the `node_hash_map` to the number needed to
-  // accommodate at least `count` total elements without exceeding the current
-  // maximum load factor, and may rehash the container if needed.
-  using Base::reserve;
-
-  // node_hash_map::at()
-  //
-  // Returns a reference to the mapped value of the element with key equivalent
-  // to the passed key.
-  using Base::at;
-
-  // node_hash_map::contains()
-  //
-  // Determines whether an element with a key comparing equal to the given `key`
-  // exists within the `node_hash_map`, returning `true` if so or `false`
-  // otherwise.
-  using Base::contains;
-
-  // node_hash_map::count(const Key& key) const
-  //
-  // Returns the number of elements with a key comparing equal to the given
-  // `key` within the `node_hash_map`. note that this function will return
-  // either `1` or `0` since duplicate keys are not allowed within a
-  // `node_hash_map`.
-  using Base::count;
-
-  // node_hash_map::equal_range()
-  //
-  // Returns a closed range [first, last], defined by a `std::pair` of two
-  // iterators, containing all elements with the passed key in the
-  // `node_hash_map`.
-  using Base::equal_range;
-
-  // node_hash_map::find()
-  //
-  // Finds an element with the passed `key` within the `node_hash_map`.
-  using Base::find;
-
-  // node_hash_map::operator[]()
-  //
-  // Returns a reference to the value mapped to the passed key within the
-  // `node_hash_map`, performing an `insert()` if the key does not already
-  // exist. If an insertion occurs and results in a rehashing of the container,
-  // all iterators are invalidated. Otherwise iterators are not affected and
-  // references are not invalidated. Overloads are listed below.
-  //
-  // T& operator[](const Key& key):
-  //
-  //   Inserts an init_type object constructed in-place if the element with the
-  //   given key does not exist.
-  //
-  // T& operator[](Key&& key):
-  //
-  //   Inserts an init_type object constructed in-place provided that an element
-  //   with the given key does not exist.
-  using Base::operator[];
-
-  // node_hash_map::bucket_count()
-  //
-  // Returns the number of "buckets" within the `node_hash_map`.
-  using Base::bucket_count;
-
-  // node_hash_map::load_factor()
-  //
-  // Returns the current load factor of the `node_hash_map` (the average number
-  // of slots occupied with a value within the hash map).
-  using Base::load_factor;
-
-  // node_hash_map::max_load_factor()
-  //
-  // Manages the maximum load factor of the `node_hash_map`. Overloads are
-  // listed below.
-  //
-  // float node_hash_map::max_load_factor()
-  //
-  //   Returns the current maximum load factor of the `node_hash_map`.
-  //
-  // void node_hash_map::max_load_factor(float ml)
-  //
-  //   Sets the maximum load factor of the `node_hash_map` to the passed value.
-  //
-  //   NOTE: This overload is provided only for API compatibility with the STL;
-  //   `node_hash_map` will ignore any set load factor and manage its rehashing
-  //   internally as an implementation detail.
-  using Base::max_load_factor;
-
-  // node_hash_map::get_allocator()
-  //
-  // Returns the allocator function associated with this `node_hash_map`.
-  using Base::get_allocator;
-
-  // node_hash_map::hash_function()
-  //
-  // Returns the hashing function used to hash the keys within this
-  // `node_hash_map`.
-  using Base::hash_function;
-
-  // node_hash_map::key_eq()
-  //
-  // Returns the function used for comparing keys equality.
-  using Base::key_eq;
-};
-
-// erase_if(node_hash_map<>, Pred)
-//
-// Erases all elements that satisfy the predicate `pred` from the container `c`.
-template <typename K, typename V, typename H, typename E, typename A,
-          typename Predicate>
-void erase_if(node_hash_map<K, V, H, E, A>& c, Predicate pred) {
-  container_internal::EraseIf(pred, &c);
-}
-
-namespace container_internal {
-
-template <class Key, class Value>
-class NodeHashMapPolicy
-    : public absl::container_internal::node_hash_policy<
-          std::pair<const Key, Value>&, NodeHashMapPolicy<Key, Value>> {
-  using value_type = std::pair<const Key, Value>;
-
- public:
-  using key_type = Key;
-  using mapped_type = Value;
-  using init_type = std::pair</*non const*/ key_type, mapped_type>;
-
-  template <class Allocator, class... Args>
-  static value_type* new_element(Allocator* alloc, Args&&... args) {
-    using PairAlloc = typename absl::allocator_traits<
-        Allocator>::template rebind_alloc<value_type>;
-    PairAlloc pair_alloc(*alloc);
-    value_type* res =
-        absl::allocator_traits<PairAlloc>::allocate(pair_alloc, 1);
-    absl::allocator_traits<PairAlloc>::construct(pair_alloc, res,
-                                                 std::forward<Args>(args)...);
-    return res;
-  }
-
-  template <class Allocator>
-  static void delete_element(Allocator* alloc, value_type* pair) {
-    using PairAlloc = typename absl::allocator_traits<
-        Allocator>::template rebind_alloc<value_type>;
-    PairAlloc pair_alloc(*alloc);
-    absl::allocator_traits<PairAlloc>::destroy(pair_alloc, pair);
-    absl::allocator_traits<PairAlloc>::deallocate(pair_alloc, pair, 1);
-  }
-
-  template <class F, class... Args>
-  static decltype(absl::container_internal::DecomposePair(
-      std::declval<F>(), std::declval<Args>()...))
-  apply(F&& f, Args&&... args) {
-    return absl::container_internal::DecomposePair(std::forward<F>(f),
-                                                   std::forward<Args>(args)...);
-  }
-
-  static size_t element_space_used(const value_type*) {
-    return sizeof(value_type);
-  }
-
-  static Value& value(value_type* elem) { return elem->second; }
-  static const Value& value(const value_type* elem) { return elem->second; }
-};
-}  // namespace container_internal
-
-namespace container_algorithm_internal {
-
-// Specialization of trait in absl/algorithm/container.h
-template <class Key, class T, class Hash, class KeyEqual, class Allocator>
-struct IsUnorderedContainer<
-    absl::node_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
-
-}  // namespace container_algorithm_internal
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_NODE_HASH_MAP_H_
diff --git a/third_party/abseil_cpp/absl/container/node_hash_map_test.cc b/third_party/abseil_cpp/absl/container/node_hash_map_test.cc
deleted file mode 100644
index 8f59a1e4a2..0000000000
--- a/third_party/abseil_cpp/absl/container/node_hash_map_test.cc
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/node_hash_map.h"
-
-#include "absl/container/internal/tracked.h"
-#include "absl/container/internal/unordered_map_constructor_test.h"
-#include "absl/container/internal/unordered_map_lookup_test.h"
-#include "absl/container/internal/unordered_map_members_test.h"
-#include "absl/container/internal/unordered_map_modifiers_test.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-
-using ::testing::Field;
-using ::testing::IsEmpty;
-using ::testing::Pair;
-using ::testing::UnorderedElementsAre;
-
-using MapTypes = ::testing::Types<
-    absl::node_hash_map<int, int, StatefulTestingHash, StatefulTestingEqual,
-                        Alloc<std::pair<const int, int>>>,
-    absl::node_hash_map<std::string, std::string, StatefulTestingHash,
-                        StatefulTestingEqual,
-                        Alloc<std::pair<const std::string, std::string>>>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ConstructorTest, MapTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, LookupTest, MapTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, MembersTest, MapTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ModifiersTest, MapTypes);
-
-using M = absl::node_hash_map<std::string, Tracked<int>>;
-
-TEST(NodeHashMap, Emplace) {
-  M m;
-  Tracked<int> t(53);
-  m.emplace("a", t);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(1, t.num_copies());
-
-  m.emplace(std::string("a"), t);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(1, t.num_copies());
-
-  std::string a("a");
-  m.emplace(a, t);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(1, t.num_copies());
-
-  const std::string ca("a");
-  m.emplace(a, t);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(1, t.num_copies());
-
-  m.emplace(std::make_pair("a", t));
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(2, t.num_copies());
-
-  m.emplace(std::make_pair(std::string("a"), t));
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(3, t.num_copies());
-
-  std::pair<std::string, Tracked<int>> p("a", t);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(4, t.num_copies());
-  m.emplace(p);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(4, t.num_copies());
-
-  const std::pair<std::string, Tracked<int>> cp("a", t);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(5, t.num_copies());
-  m.emplace(cp);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(5, t.num_copies());
-
-  std::pair<const std::string, Tracked<int>> pc("a", t);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(6, t.num_copies());
-  m.emplace(pc);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(6, t.num_copies());
-
-  const std::pair<const std::string, Tracked<int>> cpc("a", t);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(7, t.num_copies());
-  m.emplace(cpc);
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(7, t.num_copies());
-
-  m.emplace(std::piecewise_construct, std::forward_as_tuple("a"),
-            std::forward_as_tuple(t));
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(7, t.num_copies());
-
-  m.emplace(std::piecewise_construct, std::forward_as_tuple(std::string("a")),
-            std::forward_as_tuple(t));
-  ASSERT_EQ(0, t.num_moves());
-  ASSERT_EQ(7, t.num_copies());
-}
-
-TEST(NodeHashMap, AssignRecursive) {
-  struct Tree {
-    // Verify that unordered_map<K, IncompleteType> can be instantiated.
-    absl::node_hash_map<int, Tree> children;
-  };
-  Tree root;
-  const Tree& child = root.children.emplace().first->second;
-  // Verify that `lhs = rhs` doesn't read rhs after clearing lhs.
-  root = child;
-}
-
-TEST(FlatHashMap, MoveOnlyKey) {
-  struct Key {
-    Key() = default;
-    Key(Key&&) = default;
-    Key& operator=(Key&&) = default;
-  };
-  struct Eq {
-    bool operator()(const Key&, const Key&) const { return true; }
-  };
-  struct Hash {
-    size_t operator()(const Key&) const { return 0; }
-  };
-  absl::node_hash_map<Key, int, Hash, Eq> m;
-  m[Key()];
-}
-
-struct NonMovableKey {
-  explicit NonMovableKey(int i) : i(i) {}
-  NonMovableKey(NonMovableKey&&) = delete;
-  int i;
-};
-struct NonMovableKeyHash {
-  using is_transparent = void;
-  size_t operator()(const NonMovableKey& k) const { return k.i; }
-  size_t operator()(int k) const { return k; }
-};
-struct NonMovableKeyEq {
-  using is_transparent = void;
-  bool operator()(const NonMovableKey& a, const NonMovableKey& b) const {
-    return a.i == b.i;
-  }
-  bool operator()(const NonMovableKey& a, int b) const { return a.i == b; }
-};
-
-TEST(NodeHashMap, MergeExtractInsert) {
-  absl::node_hash_map<NonMovableKey, int, NonMovableKeyHash, NonMovableKeyEq>
-      set1, set2;
-  set1.emplace(std::piecewise_construct, std::make_tuple(7),
-               std::make_tuple(-7));
-  set1.emplace(std::piecewise_construct, std::make_tuple(17),
-               std::make_tuple(-17));
-
-  set2.emplace(std::piecewise_construct, std::make_tuple(7),
-               std::make_tuple(-70));
-  set2.emplace(std::piecewise_construct, std::make_tuple(19),
-               std::make_tuple(-190));
-
-  auto Elem = [](int key, int value) {
-    return Pair(Field(&NonMovableKey::i, key), value);
-  };
-
-  EXPECT_THAT(set1, UnorderedElementsAre(Elem(7, -7), Elem(17, -17)));
-  EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70), Elem(19, -190)));
-
-  // NonMovableKey is neither copyable nor movable. We should still be able to
-  // move nodes around.
-  static_assert(!std::is_move_constructible<NonMovableKey>::value, "");
-  set1.merge(set2);
-
-  EXPECT_THAT(set1,
-              UnorderedElementsAre(Elem(7, -7), Elem(17, -17), Elem(19, -190)));
-  EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70)));
-
-  auto node = set1.extract(7);
-  EXPECT_TRUE(node);
-  EXPECT_EQ(node.key().i, 7);
-  EXPECT_EQ(node.mapped(), -7);
-  EXPECT_THAT(set1, UnorderedElementsAre(Elem(17, -17), Elem(19, -190)));
-
-  auto insert_result = set2.insert(std::move(node));
-  EXPECT_FALSE(node);
-  EXPECT_FALSE(insert_result.inserted);
-  EXPECT_TRUE(insert_result.node);
-  EXPECT_EQ(insert_result.node.key().i, 7);
-  EXPECT_EQ(insert_result.node.mapped(), -7);
-  EXPECT_THAT(*insert_result.position, Elem(7, -70));
-  EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70)));
-
-  node = set1.extract(17);
-  EXPECT_TRUE(node);
-  EXPECT_EQ(node.key().i, 17);
-  EXPECT_EQ(node.mapped(), -17);
-  EXPECT_THAT(set1, UnorderedElementsAre(Elem(19, -190)));
-
-  node.mapped() = 23;
-
-  insert_result = set2.insert(std::move(node));
-  EXPECT_FALSE(node);
-  EXPECT_TRUE(insert_result.inserted);
-  EXPECT_FALSE(insert_result.node);
-  EXPECT_THAT(*insert_result.position, Elem(17, 23));
-  EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70), Elem(17, 23)));
-}
-
-bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; }
-
-TEST(NodeHashMap, EraseIf) {
-  // Erase all elements.
-  {
-    node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s, [](std::pair<const int, int>) { return true; });
-    EXPECT_THAT(s, IsEmpty());
-  }
-  // Erase no elements.
-  {
-    node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s, [](std::pair<const int, int>) { return false; });
-    EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3),
-                                        Pair(4, 4), Pair(5, 5)));
-  }
-  // Erase specific elements.
-  {
-    node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s,
-             [](std::pair<const int, int> kvp) { return kvp.first % 2 == 1; });
-    EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4)));
-  }
-  // Predicate is function reference.
-  {
-    node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s, FirstIsEven);
-    EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
-  }
-  // Predicate is function pointer.
-  {
-    node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
-    erase_if(s, &FirstIsEven);
-    EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
-  }
-}
-
-// This test requires std::launder for mutable key access in node handles.
-#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
-TEST(NodeHashMap, NodeHandleMutableKeyAccess) {
-  node_hash_map<std::string, std::string> map;
-
-  map["key1"] = "mapped";
-
-  auto nh = map.extract(map.begin());
-  nh.key().resize(3);
-  map.insert(std::move(nh));
-
-  EXPECT_THAT(map, testing::ElementsAre(Pair("key", "mapped")));
-}
-#endif
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/container/node_hash_set.h b/third_party/abseil_cpp/absl/container/node_hash_set.h
deleted file mode 100644
index 56ce3b66c0..0000000000
--- a/third_party/abseil_cpp/absl/container/node_hash_set.h
+++ /dev/null
@@ -1,493 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: node_hash_set.h
-// -----------------------------------------------------------------------------
-//
-// An `absl::node_hash_set<T>` is an unordered associative container designed to
-// be a more efficient replacement for `std::unordered_set`. Like
-// `unordered_set`, search, insertion, and deletion of map elements can be done
-// as an `O(1)` operation. However, `node_hash_set` (and other unordered
-// associative containers known as the collection of Abseil "Swiss tables")
-// contain other optimizations that result in both memory and computation
-// advantages.
-//
-// In most cases, your default choice for a hash table should be a map of type
-// `flat_hash_map` or a set of type `flat_hash_set`. However, if you need
-// pointer stability, a `node_hash_set` should be your preferred choice. As
-// well, if you are migrating your code from using `std::unordered_set`, a
-// `node_hash_set` should be an easy migration. Consider migrating to
-// `node_hash_set` and perhaps converting to a more efficient `flat_hash_set`
-// upon further review.
-
-#ifndef ABSL_CONTAINER_NODE_HASH_SET_H_
-#define ABSL_CONTAINER_NODE_HASH_SET_H_
-
-#include <type_traits>
-
-#include "absl/algorithm/container.h"
-#include "absl/container/internal/hash_function_defaults.h"  // IWYU pragma: export
-#include "absl/container/internal/node_hash_policy.h"
-#include "absl/container/internal/raw_hash_set.h"  // IWYU pragma: export
-#include "absl/memory/memory.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-template <typename T>
-struct NodeHashSetPolicy;
-}  // namespace container_internal
-
-// -----------------------------------------------------------------------------
-// absl::node_hash_set
-// -----------------------------------------------------------------------------
-//
-// An `absl::node_hash_set<T>` is an unordered associative container which
-// has been optimized for both speed and memory footprint in most common use
-// cases. Its interface is similar to that of `std::unordered_set<T>` with the
-// following notable differences:
-//
-// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
-//   `insert()`, provided that the map is provided a compatible heterogeneous
-//   hashing function and equality operator.
-// * Contains a `capacity()` member function indicating the number of element
-//   slots (open, deleted, and empty) within the hash set.
-// * Returns `void` from the `erase(iterator)` overload.
-//
-// By default, `node_hash_set` uses the `absl::Hash` hashing framework.
-// All fundamental and Abseil types that support the `absl::Hash` framework have
-// a compatible equality operator for comparing insertions into `node_hash_set`.
-// If your type is not yet supported by the `absl::Hash` framework, see
-// absl/hash/hash.h for information on extending Abseil hashing to user-defined
-// types.
-//
-// Example:
-//
-//   // Create a node hash set of three strings
-//   absl::node_hash_map<std::string, std::string> ducks =
-//     {"huey", "dewey", "louie"};
-//
-//  // Insert a new element into the node hash map
-//  ducks.insert("donald"};
-//
-//  // Force a rehash of the node hash map
-//  ducks.rehash(0);
-//
-//  // See if "dewey" is present
-//  if (ducks.contains("dewey")) {
-//    std::cout << "We found dewey!" << std::endl;
-//  }
-template <class T, class Hash = absl::container_internal::hash_default_hash<T>,
-          class Eq = absl::container_internal::hash_default_eq<T>,
-          class Alloc = std::allocator<T>>
-class node_hash_set
-    : public absl::container_internal::raw_hash_set<
-          absl::container_internal::NodeHashSetPolicy<T>, Hash, Eq, Alloc> {
-  using Base = typename node_hash_set::raw_hash_set;
-
- public:
-  // Constructors and Assignment Operators
-  //
-  // A node_hash_set supports the same overload set as `std::unordered_map`
-  // for construction and assignment:
-  //
-  // *  Default constructor
-  //
-  //    // No allocation for the table's elements is made.
-  //    absl::node_hash_set<std::string> set1;
-  //
-  // * Initializer List constructor
-  //
-  //   absl::node_hash_set<std::string> set2 =
-  //       {{"huey"}, {"dewey"}, {"louie"}};
-  //
-  // * Copy constructor
-  //
-  //   absl::node_hash_set<std::string> set3(set2);
-  //
-  // * Copy assignment operator
-  //
-  //  // Hash functor and Comparator are copied as well
-  //  absl::node_hash_set<std::string> set4;
-  //  set4 = set3;
-  //
-  // * Move constructor
-  //
-  //   // Move is guaranteed efficient
-  //   absl::node_hash_set<std::string> set5(std::move(set4));
-  //
-  // * Move assignment operator
-  //
-  //   // May be efficient if allocators are compatible
-  //   absl::node_hash_set<std::string> set6;
-  //   set6 = std::move(set5);
-  //
-  // * Range constructor
-  //
-  //   std::vector<std::string> v = {"a", "b"};
-  //   absl::node_hash_set<std::string> set7(v.begin(), v.end());
-  node_hash_set() {}
-  using Base::Base;
-
-  // node_hash_set::begin()
-  //
-  // Returns an iterator to the beginning of the `node_hash_set`.
-  using Base::begin;
-
-  // node_hash_set::cbegin()
-  //
-  // Returns a const iterator to the beginning of the `node_hash_set`.
-  using Base::cbegin;
-
-  // node_hash_set::cend()
-  //
-  // Returns a const iterator to the end of the `node_hash_set`.
-  using Base::cend;
-
-  // node_hash_set::end()
-  //
-  // Returns an iterator to the end of the `node_hash_set`.
-  using Base::end;
-
-  // node_hash_set::capacity()
-  //
-  // Returns the number of element slots (assigned, deleted, and empty)
-  // available within the `node_hash_set`.
-  //
-  // NOTE: this member function is particular to `absl::node_hash_set` and is
-  // not provided in the `std::unordered_map` API.
-  using Base::capacity;
-
-  // node_hash_set::empty()
-  //
-  // Returns whether or not the `node_hash_set` is empty.
-  using Base::empty;
-
-  // node_hash_set::max_size()
-  //
-  // Returns the largest theoretical possible number of elements within a
-  // `node_hash_set` under current memory constraints. This value can be thought
-  // of the largest value of `std::distance(begin(), end())` for a
-  // `node_hash_set<T>`.
-  using Base::max_size;
-
-  // node_hash_set::size()
-  //
-  // Returns the number of elements currently within the `node_hash_set`.
-  using Base::size;
-
-  // node_hash_set::clear()
-  //
-  // Removes all elements from the `node_hash_set`. Invalidates any references,
-  // pointers, or iterators referring to contained elements.
-  //
-  // NOTE: this operation may shrink the underlying buffer. To avoid shrinking
-  // the underlying buffer call `erase(begin(), end())`.
-  using Base::clear;
-
-  // node_hash_set::erase()
-  //
-  // Erases elements within the `node_hash_set`. Erasing does not trigger a
-  // rehash. Overloads are listed below.
-  //
-  // void erase(const_iterator pos):
-  //
-  //   Erases the element at `position` of the `node_hash_set`, returning
-  //   `void`.
-  //
-  //   NOTE: this return behavior is different than that of STL containers in
-  //   general and `std::unordered_map` in particular.
-  //
-  // iterator erase(const_iterator first, const_iterator last):
-  //
-  //   Erases the elements in the open interval [`first`, `last`), returning an
-  //   iterator pointing to `last`.
-  //
-  // size_type erase(const key_type& key):
-  //
-  //   Erases the element with the matching key, if it exists, returning the
-  //   number of elements erased (0 or 1).
-  using Base::erase;
-
-  // node_hash_set::insert()
-  //
-  // Inserts an element of the specified value into the `node_hash_set`,
-  // returning an iterator pointing to the newly inserted element, provided that
-  // an element with the given key does not already exist. If rehashing occurs
-  // due to the insertion, all iterators are invalidated. Overloads are listed
-  // below.
-  //
-  // std::pair<iterator,bool> insert(const T& value):
-  //
-  //   Inserts a value into the `node_hash_set`. Returns a pair consisting of an
-  //   iterator to the inserted element (or to the element that prevented the
-  //   insertion) and a bool denoting whether the insertion took place.
-  //
-  // std::pair<iterator,bool> insert(T&& value):
-  //
-  //   Inserts a moveable value into the `node_hash_set`. Returns a pair
-  //   consisting of an iterator to the inserted element (or to the element that
-  //   prevented the insertion) and a bool denoting whether the insertion took
-  //   place.
-  //
-  // iterator insert(const_iterator hint, const T& value):
-  // iterator insert(const_iterator hint, T&& value):
-  //
-  //   Inserts a value, using the position of `hint` as a non-binding suggestion
-  //   for where to begin the insertion search. Returns an iterator to the
-  //   inserted element, or to the existing element that prevented the
-  //   insertion.
-  //
-  // void insert(InputIterator first, InputIterator last):
-  //
-  //   Inserts a range of values [`first`, `last`).
-  //
-  //   NOTE: Although the STL does not specify which element may be inserted if
-  //   multiple keys compare equivalently, for `node_hash_set` we guarantee the
-  //   first match is inserted.
-  //
-  // void insert(std::initializer_list<T> ilist):
-  //
-  //   Inserts the elements within the initializer list `ilist`.
-  //
-  //   NOTE: Although the STL does not specify which element may be inserted if
-  //   multiple keys compare equivalently within the initializer list, for
-  //   `node_hash_set` we guarantee the first match is inserted.
-  using Base::insert;
-
-  // node_hash_set::emplace()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `node_hash_set`, provided that no element with the given key
-  // already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  using Base::emplace;
-
-  // node_hash_set::emplace_hint()
-  //
-  // Inserts an element of the specified value by constructing it in-place
-  // within the `node_hash_set`, using the position of `hint` as a non-binding
-  // suggestion for where to begin the insertion search, and only inserts
-  // provided that no element with the given key already exists.
-  //
-  // The element may be constructed even if there already is an element with the
-  // key in the container, in which case the newly constructed element will be
-  // destroyed immediately.
-  //
-  // If rehashing occurs due to the insertion, all iterators are invalidated.
-  using Base::emplace_hint;
-
-  // node_hash_set::extract()
-  //
-  // Extracts the indicated element, erasing it in the process, and returns it
-  // as a C++17-compatible node handle. Overloads are listed below.
-  //
-  // node_type extract(const_iterator position):
-  //
-  //   Extracts the element at the indicated position and returns a node handle
-  //   owning that extracted data.
-  //
-  // node_type extract(const key_type& x):
-  //
-  //   Extracts the element with the key matching the passed key value and
-  //   returns a node handle owning that extracted data. If the `node_hash_set`
-  //   does not contain an element with a matching key, this function returns an
-  // empty node handle.
-  using Base::extract;
-
-  // node_hash_set::merge()
-  //
-  // Extracts elements from a given `source` flat hash map into this
-  // `node_hash_set`. If the destination `node_hash_set` already contains an
-  // element with an equivalent key, that element is not extracted.
-  using Base::merge;
-
-  // node_hash_set::swap(node_hash_set& other)
-  //
-  // Exchanges the contents of this `node_hash_set` with those of the `other`
-  // flat hash map, avoiding invocation of any move, copy, or swap operations on
-  // individual elements.
-  //
-  // All iterators and references on the `node_hash_set` remain valid, excepting
-  // for the past-the-end iterator, which is invalidated.
-  //
-  // `swap()` requires that the flat hash set's hashing and key equivalence
-  // functions be Swappable, and are exchaged using unqualified calls to
-  // non-member `swap()`. If the map's allocator has
-  // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
-  // set to `true`, the allocators are also exchanged using an unqualified call
-  // to non-member `swap()`; otherwise, the allocators are not swapped.
-  using Base::swap;
-
-  // node_hash_set::rehash(count)
-  //
-  // Rehashes the `node_hash_set`, setting the number of slots to be at least
-  // the passed value. If the new number of slots increases the load factor more
-  // than the current maximum load factor
-  // (`count` < `size()` / `max_load_factor()`), then the new number of slots
-  // will be at least `size()` / `max_load_factor()`.
-  //
-  // To force a rehash, pass rehash(0).
-  //
-  // NOTE: unlike behavior in `std::unordered_set`, references are also
-  // invalidated upon a `rehash()`.
-  using Base::rehash;
-
-  // node_hash_set::reserve(count)
-  //
-  // Sets the number of slots in the `node_hash_set` to the number needed to
-  // accommodate at least `count` total elements without exceeding the current
-  // maximum load factor, and may rehash the container if needed.
-  using Base::reserve;
-
-  // node_hash_set::contains()
-  //
-  // Determines whether an element comparing equal to the given `key` exists
-  // within the `node_hash_set`, returning `true` if so or `false` otherwise.
-  using Base::contains;
-
-  // node_hash_set::count(const Key& key) const
-  //
-  // Returns the number of elements comparing equal to the given `key` within
-  // the `node_hash_set`. note that this function will return either `1` or `0`
-  // since duplicate elements are not allowed within a `node_hash_set`.
-  using Base::count;
-
-  // node_hash_set::equal_range()
-  //
-  // Returns a closed range [first, last], defined by a `std::pair` of two
-  // iterators, containing all elements with the passed key in the
-  // `node_hash_set`.
-  using Base::equal_range;
-
-  // node_hash_set::find()
-  //
-  // Finds an element with the passed `key` within the `node_hash_set`.
-  using Base::find;
-
-  // node_hash_set::bucket_count()
-  //
-  // Returns the number of "buckets" within the `node_hash_set`. Note that
-  // because a flat hash map contains all elements within its internal storage,
-  // this value simply equals the current capacity of the `node_hash_set`.
-  using Base::bucket_count;
-
-  // node_hash_set::load_factor()
-  //
-  // Returns the current load factor of the `node_hash_set` (the average number
-  // of slots occupied with a value within the hash map).
-  using Base::load_factor;
-
-  // node_hash_set::max_load_factor()
-  //
-  // Manages the maximum load factor of the `node_hash_set`. Overloads are
-  // listed below.
-  //
-  // float node_hash_set::max_load_factor()
-  //
-  //   Returns the current maximum load factor of the `node_hash_set`.
-  //
-  // void node_hash_set::max_load_factor(float ml)
-  //
-  //   Sets the maximum load factor of the `node_hash_set` to the passed value.
-  //
-  //   NOTE: This overload is provided only for API compatibility with the STL;
-  //   `node_hash_set` will ignore any set load factor and manage its rehashing
-  //   internally as an implementation detail.
-  using Base::max_load_factor;
-
-  // node_hash_set::get_allocator()
-  //
-  // Returns the allocator function associated with this `node_hash_set`.
-  using Base::get_allocator;
-
-  // node_hash_set::hash_function()
-  //
-  // Returns the hashing function used to hash the keys within this
-  // `node_hash_set`.
-  using Base::hash_function;
-
-  // node_hash_set::key_eq()
-  //
-  // Returns the function used for comparing keys equality.
-  using Base::key_eq;
-};
-
-// erase_if(node_hash_set<>, Pred)
-//
-// Erases all elements that satisfy the predicate `pred` from the container `c`.
-template <typename T, typename H, typename E, typename A, typename Predicate>
-void erase_if(node_hash_set<T, H, E, A>& c, Predicate pred) {
-  container_internal::EraseIf(pred, &c);
-}
-
-namespace container_internal {
-
-template <class T>
-struct NodeHashSetPolicy
-    : absl::container_internal::node_hash_policy<T&, NodeHashSetPolicy<T>> {
-  using key_type = T;
-  using init_type = T;
-  using constant_iterators = std::true_type;
-
-  template <class Allocator, class... Args>
-  static T* new_element(Allocator* alloc, Args&&... args) {
-    using ValueAlloc =
-        typename absl::allocator_traits<Allocator>::template rebind_alloc<T>;
-    ValueAlloc value_alloc(*alloc);
-    T* res = absl::allocator_traits<ValueAlloc>::allocate(value_alloc, 1);
-    absl::allocator_traits<ValueAlloc>::construct(value_alloc, res,
-                                                  std::forward<Args>(args)...);
-    return res;
-  }
-
-  template <class Allocator>
-  static void delete_element(Allocator* alloc, T* elem) {
-    using ValueAlloc =
-        typename absl::allocator_traits<Allocator>::template rebind_alloc<T>;
-    ValueAlloc value_alloc(*alloc);
-    absl::allocator_traits<ValueAlloc>::destroy(value_alloc, elem);
-    absl::allocator_traits<ValueAlloc>::deallocate(value_alloc, elem, 1);
-  }
-
-  template <class F, class... Args>
-  static decltype(absl::container_internal::DecomposeValue(
-      std::declval<F>(), std::declval<Args>()...))
-  apply(F&& f, Args&&... args) {
-    return absl::container_internal::DecomposeValue(
-        std::forward<F>(f), std::forward<Args>(args)...);
-  }
-
-  static size_t element_space_used(const T*) { return sizeof(T); }
-};
-}  // namespace container_internal
-
-namespace container_algorithm_internal {
-
-// Specialization of trait in absl/algorithm/container.h
-template <class Key, class Hash, class KeyEqual, class Allocator>
-struct IsUnorderedContainer<absl::node_hash_set<Key, Hash, KeyEqual, Allocator>>
-    : std::true_type {};
-
-}  // namespace container_algorithm_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_CONTAINER_NODE_HASH_SET_H_
diff --git a/third_party/abseil_cpp/absl/container/node_hash_set_test.cc b/third_party/abseil_cpp/absl/container/node_hash_set_test.cc
deleted file mode 100644
index 7ddad2021d..0000000000
--- a/third_party/abseil_cpp/absl/container/node_hash_set_test.cc
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/container/node_hash_set.h"
-
-#include "absl/container/internal/unordered_set_constructor_test.h"
-#include "absl/container/internal/unordered_set_lookup_test.h"
-#include "absl/container/internal/unordered_set_members_test.h"
-#include "absl/container/internal/unordered_set_modifiers_test.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-namespace {
-using ::absl::container_internal::hash_internal::Enum;
-using ::absl::container_internal::hash_internal::EnumClass;
-using ::testing::IsEmpty;
-using ::testing::Pointee;
-using ::testing::UnorderedElementsAre;
-
-using SetTypes = ::testing::Types<
-    node_hash_set<int, StatefulTestingHash, StatefulTestingEqual, Alloc<int>>,
-    node_hash_set<std::string, StatefulTestingHash, StatefulTestingEqual,
-                  Alloc<std::string>>,
-    node_hash_set<Enum, StatefulTestingHash, StatefulTestingEqual, Alloc<Enum>>,
-    node_hash_set<EnumClass, StatefulTestingHash, StatefulTestingEqual,
-                  Alloc<EnumClass>>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, ConstructorTest, SetTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, LookupTest, SetTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, MembersTest, SetTypes);
-INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, ModifiersTest, SetTypes);
-
-TEST(NodeHashSet, MoveableNotCopyableCompiles) {
-  node_hash_set<std::unique_ptr<void*>> t;
-  node_hash_set<std::unique_ptr<void*>> u;
-  u = std::move(t);
-}
-
-TEST(NodeHashSet, MergeExtractInsert) {
-  struct Hash {
-    size_t operator()(const std::unique_ptr<int>& p) const { return *p; }
-  };
-  struct Eq {
-    bool operator()(const std::unique_ptr<int>& a,
-                    const std::unique_ptr<int>& b) const {
-      return *a == *b;
-    }
-  };
-  absl::node_hash_set<std::unique_ptr<int>, Hash, Eq> set1, set2;
-  set1.insert(absl::make_unique<int>(7));
-  set1.insert(absl::make_unique<int>(17));
-
-  set2.insert(absl::make_unique<int>(7));
-  set2.insert(absl::make_unique<int>(19));
-
-  EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17)));
-  EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(19)));
-
-  set1.merge(set2);
-
-  EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17), Pointee(19)));
-  EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7)));
-
-  auto node = set1.extract(absl::make_unique<int>(7));
-  EXPECT_TRUE(node);
-  EXPECT_THAT(node.value(), Pointee(7));
-  EXPECT_THAT(set1, UnorderedElementsAre(Pointee(17), Pointee(19)));
-
-  auto insert_result = set2.insert(std::move(node));
-  EXPECT_FALSE(node);
-  EXPECT_FALSE(insert_result.inserted);
-  EXPECT_TRUE(insert_result.node);
-  EXPECT_THAT(insert_result.node.value(), Pointee(7));
-  EXPECT_EQ(**insert_result.position, 7);
-  EXPECT_NE(insert_result.position->get(), insert_result.node.value().get());
-  EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7)));
-
-  node = set1.extract(absl::make_unique<int>(17));
-  EXPECT_TRUE(node);
-  EXPECT_THAT(node.value(), Pointee(17));
-  EXPECT_THAT(set1, UnorderedElementsAre(Pointee(19)));
-
-  node.value() = absl::make_unique<int>(23);
-
-  insert_result = set2.insert(std::move(node));
-  EXPECT_FALSE(node);
-  EXPECT_TRUE(insert_result.inserted);
-  EXPECT_FALSE(insert_result.node);
-  EXPECT_EQ(**insert_result.position, 23);
-  EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23)));
-}
-
-bool IsEven(int k) { return k % 2 == 0; }
-
-TEST(NodeHashSet, EraseIf) {
-  // Erase all elements.
-  {
-    node_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, [](int) { return true; });
-    EXPECT_THAT(s, IsEmpty());
-  }
-  // Erase no elements.
-  {
-    node_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, [](int) { return false; });
-    EXPECT_THAT(s, UnorderedElementsAre(1, 2, 3, 4, 5));
-  }
-  // Erase specific elements.
-  {
-    node_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, [](int k) { return k % 2 == 1; });
-    EXPECT_THAT(s, UnorderedElementsAre(2, 4));
-  }
-  // Predicate is function reference.
-  {
-    node_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, IsEven);
-    EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5));
-  }
-  // Predicate is function pointer.
-  {
-    node_hash_set<int> s = {1, 2, 3, 4, 5};
-    erase_if(s, &IsEven);
-    EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5));
-  }
-}
-
-}  // namespace
-}  // namespace container_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/copts/AbseilConfigureCopts.cmake b/third_party/abseil_cpp/absl/copts/AbseilConfigureCopts.cmake
deleted file mode 100644
index acd46d04aa..0000000000
--- a/third_party/abseil_cpp/absl/copts/AbseilConfigureCopts.cmake
+++ /dev/null
@@ -1,67 +0,0 @@
-# See absl/copts/copts.py and absl/copts/generate_copts.py
-include(GENERATED_AbseilCopts)
-
-set(ABSL_LSAN_LINKOPTS "")
-set(ABSL_HAVE_LSAN OFF)
-set(ABSL_DEFAULT_LINKOPTS "")
-
-if (BUILD_SHARED_LIBS AND MSVC)
-  set(ABSL_BUILD_DLL TRUE)
-  set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
-else()
-  set(ABSL_BUILD_DLL FALSE)
-endif()
-
-if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86_64|amd64|AMD64")
-  if (MSVC)
-    set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}")
-  else()
-    set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}")
-  endif()
-elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm.*|aarch64")
-  if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
-    set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM64_FLAGS}")
-  elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
-    set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM32_FLAGS}")
-  else()
-    message(WARNING "Value of CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) is not supported.")
-  endif()
-else()
-  message(WARNING "Value of CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) is unknown and cannot be used to set ABSL_RANDOM_RANDEN_COPTS")
-  set(ABSL_RANDOM_RANDEN_COPTS "")
-endif()
-
-
-if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
-  set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}")
-  set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}")
-elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
-  # MATCHES so we get both Clang and AppleClang
-  if(MSVC)
-    # clang-cl is half MSVC, half LLVM
-    set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}")
-    set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_FLAGS};${ABSL_CLANG_CL_TEST_FLAGS}")
-    set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")
-  else()
-    set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}")
-    set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}")
-    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
-      # AppleClang doesn't have lsan
-      # https://developer.apple.com/documentation/code_diagnostics
-      if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
-        set(ABSL_LSAN_LINKOPTS "-fsanitize=leak")
-        set(ABSL_HAVE_LSAN ON)
-      endif()
-    endif()
-  endif()
-elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
-  set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}")
-  set(ABSL_TEST_COPTS "${ABSL_MSVC_FLAGS};${ABSL_MSVC_TEST_FLAGS}")
-  set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")
-else()
-  message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER}.  Building with no default flags")
-  set(ABSL_DEFAULT_COPTS "")
-  set(ABSL_TEST_COPTS "")
-endif()
-
-set(ABSL_CXX_STANDARD "${CMAKE_CXX_STANDARD}")
diff --git a/third_party/abseil_cpp/absl/copts/GENERATED_AbseilCopts.cmake b/third_party/abseil_cpp/absl/copts/GENERATED_AbseilCopts.cmake
deleted file mode 100644
index 51742c9b6b..0000000000
--- a/third_party/abseil_cpp/absl/copts/GENERATED_AbseilCopts.cmake
+++ /dev/null
@@ -1,162 +0,0 @@
-# GENERATED! DO NOT MANUALLY EDIT THIS FILE.
-#
-# (1) Edit absl/copts/copts.py.
-# (2) Run `python <path_to_absl>/copts/generate_copts.py`.
-
-list(APPEND ABSL_CLANG_CL_FLAGS
-    "/W3"
-    "/DNOMINMAX"
-    "/DWIN32_LEAN_AND_MEAN"
-    "/D_CRT_SECURE_NO_WARNINGS"
-    "/D_SCL_SECURE_NO_WARNINGS"
-    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE"
-)
-
-list(APPEND ABSL_CLANG_CL_TEST_FLAGS
-    "-Wno-c99-extensions"
-    "-Wno-deprecated-declarations"
-    "-Wno-missing-noreturn"
-    "-Wno-missing-prototypes"
-    "-Wno-missing-variable-declarations"
-    "-Wno-null-conversion"
-    "-Wno-shadow"
-    "-Wno-shift-sign-overflow"
-    "-Wno-sign-compare"
-    "-Wno-unused-function"
-    "-Wno-unused-member-function"
-    "-Wno-unused-parameter"
-    "-Wno-unused-private-field"
-    "-Wno-unused-template"
-    "-Wno-used-but-marked-unused"
-    "-Wno-zero-as-null-pointer-constant"
-    "-Wno-gnu-zero-variadic-macro-arguments"
-)
-
-list(APPEND ABSL_GCC_FLAGS
-    "-Wall"
-    "-Wextra"
-    "-Wcast-qual"
-    "-Wconversion-null"
-    "-Wformat-security"
-    "-Wmissing-declarations"
-    "-Woverlength-strings"
-    "-Wpointer-arith"
-    "-Wundef"
-    "-Wunused-local-typedefs"
-    "-Wunused-result"
-    "-Wvarargs"
-    "-Wvla"
-    "-Wwrite-strings"
-    "-DNOMINMAX"
-)
-
-list(APPEND ABSL_GCC_TEST_FLAGS
-    "-Wno-conversion-null"
-    "-Wno-deprecated-declarations"
-    "-Wno-missing-declarations"
-    "-Wno-sign-compare"
-    "-Wno-unused-function"
-    "-Wno-unused-parameter"
-    "-Wno-unused-private-field"
-)
-
-list(APPEND ABSL_LLVM_FLAGS
-    "-Wall"
-    "-Wextra"
-    "-Wcast-qual"
-    "-Wconversion"
-    "-Wfloat-overflow-conversion"
-    "-Wfloat-zero-conversion"
-    "-Wfor-loop-analysis"
-    "-Wformat-security"
-    "-Wgnu-redeclared-enum"
-    "-Winfinite-recursion"
-    "-Wliteral-conversion"
-    "-Wmissing-declarations"
-    "-Woverlength-strings"
-    "-Wpointer-arith"
-    "-Wself-assign"
-    "-Wshadow"
-    "-Wstring-conversion"
-    "-Wtautological-overlap-compare"
-    "-Wundef"
-    "-Wuninitialized"
-    "-Wunreachable-code"
-    "-Wunused-comparison"
-    "-Wunused-local-typedefs"
-    "-Wunused-result"
-    "-Wvla"
-    "-Wwrite-strings"
-    "-Wno-float-conversion"
-    "-Wno-implicit-float-conversion"
-    "-Wno-implicit-int-float-conversion"
-    "-Wno-implicit-int-conversion"
-    "-Wno-shorten-64-to-32"
-    "-Wno-sign-conversion"
-    "-DNOMINMAX"
-)
-
-list(APPEND ABSL_LLVM_TEST_FLAGS
-    "-Wno-c99-extensions"
-    "-Wno-deprecated-declarations"
-    "-Wno-missing-noreturn"
-    "-Wno-missing-prototypes"
-    "-Wno-missing-variable-declarations"
-    "-Wno-null-conversion"
-    "-Wno-shadow"
-    "-Wno-shift-sign-overflow"
-    "-Wno-sign-compare"
-    "-Wno-unused-function"
-    "-Wno-unused-member-function"
-    "-Wno-unused-parameter"
-    "-Wno-unused-private-field"
-    "-Wno-unused-template"
-    "-Wno-used-but-marked-unused"
-    "-Wno-zero-as-null-pointer-constant"
-    "-Wno-gnu-zero-variadic-macro-arguments"
-)
-
-list(APPEND ABSL_MSVC_FLAGS
-    "/W3"
-    "/DNOMINMAX"
-    "/DWIN32_LEAN_AND_MEAN"
-    "/D_CRT_SECURE_NO_WARNINGS"
-    "/D_SCL_SECURE_NO_WARNINGS"
-    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE"
-    "/bigobj"
-    "/wd4005"
-    "/wd4068"
-    "/wd4180"
-    "/wd4244"
-    "/wd4267"
-    "/wd4503"
-    "/wd4800"
-)
-
-list(APPEND ABSL_MSVC_LINKOPTS
-    "-ignore:4221"
-)
-
-list(APPEND ABSL_MSVC_TEST_FLAGS
-    "/wd4018"
-    "/wd4101"
-    "/wd4503"
-    "/wd4996"
-    "/DNOMINMAX"
-)
-
-list(APPEND ABSL_RANDOM_HWAES_ARM32_FLAGS
-    "-mfpu=neon"
-)
-
-list(APPEND ABSL_RANDOM_HWAES_ARM64_FLAGS
-    "-march=armv8-a+crypto"
-)
-
-list(APPEND ABSL_RANDOM_HWAES_MSVC_X64_FLAGS
-)
-
-list(APPEND ABSL_RANDOM_HWAES_X64_FLAGS
-    "-maes"
-    "-msse4.1"
-)
diff --git a/third_party/abseil_cpp/absl/copts/GENERATED_copts.bzl b/third_party/abseil_cpp/absl/copts/GENERATED_copts.bzl
deleted file mode 100644
index 6707488f23..0000000000
--- a/third_party/abseil_cpp/absl/copts/GENERATED_copts.bzl
+++ /dev/null
@@ -1,163 +0,0 @@
-"""GENERATED! DO NOT MANUALLY EDIT THIS FILE.
-
-(1) Edit absl/copts/copts.py.
-(2) Run `python <path_to_absl>/copts/generate_copts.py`.
-"""
-
-ABSL_CLANG_CL_FLAGS = [
-    "/W3",
-    "/DNOMINMAX",
-    "/DWIN32_LEAN_AND_MEAN",
-    "/D_CRT_SECURE_NO_WARNINGS",
-    "/D_SCL_SECURE_NO_WARNINGS",
-    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE",
-]
-
-ABSL_CLANG_CL_TEST_FLAGS = [
-    "-Wno-c99-extensions",
-    "-Wno-deprecated-declarations",
-    "-Wno-missing-noreturn",
-    "-Wno-missing-prototypes",
-    "-Wno-missing-variable-declarations",
-    "-Wno-null-conversion",
-    "-Wno-shadow",
-    "-Wno-shift-sign-overflow",
-    "-Wno-sign-compare",
-    "-Wno-unused-function",
-    "-Wno-unused-member-function",
-    "-Wno-unused-parameter",
-    "-Wno-unused-private-field",
-    "-Wno-unused-template",
-    "-Wno-used-but-marked-unused",
-    "-Wno-zero-as-null-pointer-constant",
-    "-Wno-gnu-zero-variadic-macro-arguments",
-]
-
-ABSL_GCC_FLAGS = [
-    "-Wall",
-    "-Wextra",
-    "-Wcast-qual",
-    "-Wconversion-null",
-    "-Wformat-security",
-    "-Wmissing-declarations",
-    "-Woverlength-strings",
-    "-Wpointer-arith",
-    "-Wundef",
-    "-Wunused-local-typedefs",
-    "-Wunused-result",
-    "-Wvarargs",
-    "-Wvla",
-    "-Wwrite-strings",
-    "-DNOMINMAX",
-]
-
-ABSL_GCC_TEST_FLAGS = [
-    "-Wno-conversion-null",
-    "-Wno-deprecated-declarations",
-    "-Wno-missing-declarations",
-    "-Wno-sign-compare",
-    "-Wno-unused-function",
-    "-Wno-unused-parameter",
-    "-Wno-unused-private-field",
-]
-
-ABSL_LLVM_FLAGS = [
-    "-Wall",
-    "-Wextra",
-    "-Wcast-qual",
-    "-Wconversion",
-    "-Wfloat-overflow-conversion",
-    "-Wfloat-zero-conversion",
-    "-Wfor-loop-analysis",
-    "-Wformat-security",
-    "-Wgnu-redeclared-enum",
-    "-Winfinite-recursion",
-    "-Wliteral-conversion",
-    "-Wmissing-declarations",
-    "-Woverlength-strings",
-    "-Wpointer-arith",
-    "-Wself-assign",
-    "-Wshadow",
-    "-Wstring-conversion",
-    "-Wtautological-overlap-compare",
-    "-Wundef",
-    "-Wuninitialized",
-    "-Wunreachable-code",
-    "-Wunused-comparison",
-    "-Wunused-local-typedefs",
-    "-Wunused-result",
-    "-Wvla",
-    "-Wwrite-strings",
-    "-Wno-float-conversion",
-    "-Wno-implicit-float-conversion",
-    "-Wno-implicit-int-float-conversion",
-    "-Wno-implicit-int-conversion",
-    "-Wno-shorten-64-to-32",
-    "-Wno-sign-conversion",
-    "-DNOMINMAX",
-]
-
-ABSL_LLVM_TEST_FLAGS = [
-    "-Wno-c99-extensions",
-    "-Wno-deprecated-declarations",
-    "-Wno-missing-noreturn",
-    "-Wno-missing-prototypes",
-    "-Wno-missing-variable-declarations",
-    "-Wno-null-conversion",
-    "-Wno-shadow",
-    "-Wno-shift-sign-overflow",
-    "-Wno-sign-compare",
-    "-Wno-unused-function",
-    "-Wno-unused-member-function",
-    "-Wno-unused-parameter",
-    "-Wno-unused-private-field",
-    "-Wno-unused-template",
-    "-Wno-used-but-marked-unused",
-    "-Wno-zero-as-null-pointer-constant",
-    "-Wno-gnu-zero-variadic-macro-arguments",
-]
-
-ABSL_MSVC_FLAGS = [
-    "/W3",
-    "/DNOMINMAX",
-    "/DWIN32_LEAN_AND_MEAN",
-    "/D_CRT_SECURE_NO_WARNINGS",
-    "/D_SCL_SECURE_NO_WARNINGS",
-    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE",
-    "/bigobj",
-    "/wd4005",
-    "/wd4068",
-    "/wd4180",
-    "/wd4244",
-    "/wd4267",
-    "/wd4503",
-    "/wd4800",
-]
-
-ABSL_MSVC_LINKOPTS = [
-    "-ignore:4221",
-]
-
-ABSL_MSVC_TEST_FLAGS = [
-    "/wd4018",
-    "/wd4101",
-    "/wd4503",
-    "/wd4996",
-    "/DNOMINMAX",
-]
-
-ABSL_RANDOM_HWAES_ARM32_FLAGS = [
-    "-mfpu=neon",
-]
-
-ABSL_RANDOM_HWAES_ARM64_FLAGS = [
-    "-march=armv8-a+crypto",
-]
-
-ABSL_RANDOM_HWAES_MSVC_X64_FLAGS = [
-]
-
-ABSL_RANDOM_HWAES_X64_FLAGS = [
-    "-maes",
-    "-msse4.1",
-]
diff --git a/third_party/abseil_cpp/absl/copts/configure_copts.bzl b/third_party/abseil_cpp/absl/copts/configure_copts.bzl
deleted file mode 100644
index 4d34254443..0000000000
--- a/third_party/abseil_cpp/absl/copts/configure_copts.bzl
+++ /dev/null
@@ -1,76 +0,0 @@
-"""absl specific copts.
-
-This file simply selects the correct options from the generated files.  To
-change Abseil copts, edit absl/copts/copts.py
-"""
-
-load(
-    "//absl:copts/GENERATED_copts.bzl",
-    "ABSL_CLANG_CL_FLAGS",
-    "ABSL_CLANG_CL_TEST_FLAGS",
-    "ABSL_GCC_FLAGS",
-    "ABSL_GCC_TEST_FLAGS",
-    "ABSL_LLVM_FLAGS",
-    "ABSL_LLVM_TEST_FLAGS",
-    "ABSL_MSVC_FLAGS",
-    "ABSL_MSVC_LINKOPTS",
-    "ABSL_MSVC_TEST_FLAGS",
-    "ABSL_RANDOM_HWAES_ARM32_FLAGS",
-    "ABSL_RANDOM_HWAES_ARM64_FLAGS",
-    "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS",
-    "ABSL_RANDOM_HWAES_X64_FLAGS",
-)
-
-ABSL_DEFAULT_COPTS = select({
-    "//absl:windows": ABSL_MSVC_FLAGS,
-    "//absl:clang_compiler": ABSL_LLVM_FLAGS,
-    "//conditions:default": ABSL_GCC_FLAGS,
-})
-
-ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({
-    "//absl:windows": ABSL_MSVC_TEST_FLAGS,
-    "//absl:clang_compiler": ABSL_LLVM_TEST_FLAGS,
-    "//conditions:default": ABSL_GCC_TEST_FLAGS,
-})
-
-ABSL_DEFAULT_LINKOPTS = select({
-    "//absl:windows": ABSL_MSVC_LINKOPTS,
-    "//conditions:default": [],
-})
-
-# ABSL_RANDOM_RANDEN_COPTS blaze copts flags which are required by each
-# environment to build an accelerated RandenHwAes library.
-ABSL_RANDOM_RANDEN_COPTS = select({
-    # APPLE
-    ":cpu_darwin_x86_64": ABSL_RANDOM_HWAES_X64_FLAGS,
-    ":cpu_darwin": ABSL_RANDOM_HWAES_X64_FLAGS,
-    ":cpu_x64_windows_msvc": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS,
-    ":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS,
-    ":cpu_k8": ABSL_RANDOM_HWAES_X64_FLAGS,
-    ":cpu_ppc": ["-mcrypto"],
-
-    # Supported by default or unsupported.
-    "//conditions:default": [],
-})
-
-# absl_random_randen_copts_init:
-#  Initialize the config targets based on cpu, os, etc. used to select
-#  the required values for ABSL_RANDOM_RANDEN_COPTS
-def absl_random_randen_copts_init():
-    """Initialize the config_settings used by ABSL_RANDOM_RANDEN_COPTS."""
-
-    # CPU configs.
-    # These configs have consistent flags to enable HWAES intsructions.
-    cpu_configs = [
-        "ppc",
-        "k8",
-        "darwin_x86_64",
-        "darwin",
-        "x64_windows_msvc",
-        "x64_windows",
-    ]
-    for cpu in cpu_configs:
-        native.config_setting(
-            name = "cpu_%s" % cpu,
-            values = {"cpu": cpu},
-        )
diff --git a/third_party/abseil_cpp/absl/copts/copts.py b/third_party/abseil_cpp/absl/copts/copts.py
deleted file mode 100644
index cf52981c58..0000000000
--- a/third_party/abseil_cpp/absl/copts/copts.py
+++ /dev/null
@@ -1,162 +0,0 @@
-"""Abseil compiler options.
-
-This is the source of truth for Abseil compiler options.  To modify Abseil
-compilation options:
-
-  (1) Edit the appropriate list in this file based on the platform the flag is
-      needed on.
-  (2) Run `<path_to_absl>/copts/generate_copts.py`.
-
-The generated copts are consumed by configure_copts.bzl and
-AbseilConfigureCopts.cmake.
-"""
-
-# /Wall with msvc includes unhelpful warnings such as C4711, C4710, ...
-MSVC_BIG_WARNING_FLAGS = [
-    "/W3",
-]
-
-LLVM_TEST_DISABLE_WARNINGS_FLAGS = [
-    "-Wno-c99-extensions",
-    "-Wno-deprecated-declarations",
-    "-Wno-missing-noreturn",
-    "-Wno-missing-prototypes",
-    "-Wno-missing-variable-declarations",
-    "-Wno-null-conversion",
-    "-Wno-shadow",
-    "-Wno-shift-sign-overflow",
-    "-Wno-sign-compare",
-    "-Wno-unused-function",
-    "-Wno-unused-member-function",
-    "-Wno-unused-parameter",
-    "-Wno-unused-private-field",
-    "-Wno-unused-template",
-    "-Wno-used-but-marked-unused",
-    "-Wno-zero-as-null-pointer-constant",
-    # gtest depends on this GNU extension being offered.
-    "-Wno-gnu-zero-variadic-macro-arguments",
-]
-
-MSVC_DEFINES = [
-    "/DNOMINMAX",  # Don't define min and max macros (windows.h)
-    # Don't bloat namespace with incompatible winsock versions.
-    "/DWIN32_LEAN_AND_MEAN",
-    # Don't warn about usage of insecure C functions.
-    "/D_CRT_SECURE_NO_WARNINGS",
-    "/D_SCL_SECURE_NO_WARNINGS",
-    # Introduced in VS 2017 15.8, allow overaligned types in aligned_storage
-    "/D_ENABLE_EXTENDED_ALIGNED_STORAGE",
-]
-
-COPT_VARS = {
-    "ABSL_GCC_FLAGS": [
-        "-Wall",
-        "-Wextra",
-        "-Wcast-qual",
-        "-Wconversion-null",
-        "-Wformat-security",
-        "-Wmissing-declarations",
-        "-Woverlength-strings",
-        "-Wpointer-arith",
-        "-Wundef",
-        "-Wunused-local-typedefs",
-        "-Wunused-result",
-        "-Wvarargs",
-        "-Wvla",  # variable-length array
-        "-Wwrite-strings",
-        # Don't define min and max macros (Build on Windows using gcc)
-        "-DNOMINMAX",
-    ],
-    "ABSL_GCC_TEST_FLAGS": [
-        "-Wno-conversion-null",
-        "-Wno-deprecated-declarations",
-        "-Wno-missing-declarations",
-        "-Wno-sign-compare",
-        "-Wno-unused-function",
-        "-Wno-unused-parameter",
-        "-Wno-unused-private-field",
-    ],
-    "ABSL_LLVM_FLAGS": [
-        "-Wall",
-        "-Wextra",
-        "-Wcast-qual",
-        "-Wconversion",
-        "-Wfloat-overflow-conversion",
-        "-Wfloat-zero-conversion",
-        "-Wfor-loop-analysis",
-        "-Wformat-security",
-        "-Wgnu-redeclared-enum",
-        "-Winfinite-recursion",
-        "-Wliteral-conversion",
-        "-Wmissing-declarations",
-        "-Woverlength-strings",
-        "-Wpointer-arith",
-        "-Wself-assign",
-        "-Wshadow",
-        "-Wstring-conversion",
-        "-Wtautological-overlap-compare",
-        "-Wundef",
-        "-Wuninitialized",
-        "-Wunreachable-code",
-        "-Wunused-comparison",
-        "-Wunused-local-typedefs",
-        "-Wunused-result",
-        "-Wvla",
-        "-Wwrite-strings",
-        # Warnings that are enabled by group warning flags like -Wall that we
-        # explicitly disable.
-        "-Wno-float-conversion",
-        "-Wno-implicit-float-conversion",
-        "-Wno-implicit-int-float-conversion",
-        "-Wno-implicit-int-conversion",
-        "-Wno-shorten-64-to-32",
-        "-Wno-sign-conversion",
-        # Don't define min and max macros (Build on Windows using clang)
-        "-DNOMINMAX",
-    ],
-    "ABSL_LLVM_TEST_FLAGS":
-        LLVM_TEST_DISABLE_WARNINGS_FLAGS,
-    "ABSL_CLANG_CL_FLAGS":
-        (MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES),
-    "ABSL_CLANG_CL_TEST_FLAGS":
-        LLVM_TEST_DISABLE_WARNINGS_FLAGS,
-    "ABSL_MSVC_FLAGS":
-        MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES + [
-            # Increase the number of sections available in object files
-            "/bigobj",
-            "/wd4005",  # macro-redefinition
-            "/wd4068",  # unknown pragma
-            # qualifier applied to function type has no meaning; ignored
-            "/wd4180",
-            # conversion from 'type1' to 'type2', possible loss of data
-            "/wd4244",
-            # conversion from 'size_t' to 'type', possible loss of data
-            "/wd4267",
-            # The decorated name was longer than the compiler limit
-            "/wd4503",
-            # forcing value to bool 'true' or 'false' (performance warning)
-            "/wd4800",
-        ],
-    "ABSL_MSVC_TEST_FLAGS": [
-        "/wd4018",  # signed/unsigned mismatch
-        "/wd4101",  # unreferenced local variable
-        "/wd4503",  # decorated name length exceeded, name was truncated
-        "/wd4996",  # use of deprecated symbol
-        "/DNOMINMAX",  # disable the min() and max() macros from <windows.h>
-    ],
-    "ABSL_MSVC_LINKOPTS": [
-        # Object file doesn't export any previously undefined symbols
-        "-ignore:4221",
-    ],
-    # "HWAES" is an abbreviation for "hardware AES" (AES - Advanced Encryption
-    # Standard). These flags are used for detecting whether or not the target
-    # architecture has hardware support for AES instructions which can be used
-    # to improve performance of some random bit generators.
-    "ABSL_RANDOM_HWAES_ARM64_FLAGS": ["-march=armv8-a+crypto"],
-    "ABSL_RANDOM_HWAES_ARM32_FLAGS": ["-mfpu=neon"],
-    "ABSL_RANDOM_HWAES_X64_FLAGS": [
-        "-maes",
-        "-msse4.1",
-    ],
-    "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS": [],
-}
diff --git a/third_party/abseil_cpp/absl/copts/generate_copts.py b/third_party/abseil_cpp/absl/copts/generate_copts.py
deleted file mode 100755
index 0e5dc9fad2..0000000000
--- a/third_party/abseil_cpp/absl/copts/generate_copts.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/python
-"""Generate Abseil compile compile option configs.
-
-Usage: <path_to_absl>/copts/generate_copts.py
-
-The configs are generated from copts.py.
-"""
-
-from os import path
-import sys
-from copts import COPT_VARS
-
-
-# Helper functions
-def file_header_lines():
-  return [
-      "GENERATED! DO NOT MANUALLY EDIT THIS FILE.", "",
-      "(1) Edit absl/copts/copts.py.",
-      "(2) Run `python <path_to_absl>/copts/generate_copts.py`."
-  ]
-
-
-def flatten(*lists):
-  return [item for sublist in lists for item in sublist]
-
-
-def relative_filename(filename):
-  return path.join(path.dirname(__file__), filename)
-
-
-# Style classes.  These contain all the syntactic styling needed to generate a
-# copt file for different build tools.
-class CMakeStyle(object):
-  """Style object for CMake copts file."""
-
-  def separator(self):
-    return ""
-
-  def list_introducer(self, name):
-    return "list(APPEND " + name
-
-  def list_closer(self):
-    return ")\n"
-
-  def docstring(self):
-    return "\n".join((("# " + line).strip() for line in file_header_lines()))
-
-  def filename(self):
-    return "GENERATED_AbseilCopts.cmake"
-
-
-class StarlarkStyle(object):
-  """Style object for Starlark copts file."""
-
-  def separator(self):
-    return ","
-
-  def list_introducer(self, name):
-    return name + " = ["
-
-  def list_closer(self):
-    return "]\n"
-
-  def docstring(self):
-    docstring_quotes = "\"\"\""
-    return docstring_quotes + "\n".join(
-        flatten(file_header_lines(), [docstring_quotes]))
-
-  def filename(self):
-    return "GENERATED_copts.bzl"
-
-
-def copt_list(name, arg_list, style):
-  """Copt file generation."""
-
-  make_line = lambda s: "    \"" + s + "\"" + style.separator()
-  external_str_list = [make_line(s) for s in arg_list]
-
-  return "\n".join(
-      flatten(
-          [style.list_introducer(name)],
-          external_str_list,
-          [style.list_closer()]))
-
-
-def generate_copt_file(style):
-  """Creates a generated copt file using the given style object.
-
-  Args:
-    style: either StarlarkStyle() or CMakeStyle()
-  """
-  with open(relative_filename(style.filename()), "w") as f:
-    f.write(style.docstring())
-    f.write("\n")
-    for var_name, arg_list in sorted(COPT_VARS.items()):
-      f.write("\n")
-      f.write(copt_list(var_name, arg_list, style))
-
-
-def main(argv):
-  if len(argv) > 1:
-    raise RuntimeError("generate_copts needs no command line args")
-
-  generate_copt_file(StarlarkStyle())
-  generate_copt_file(CMakeStyle())
-
-
-if __name__ == "__main__":
-  main(sys.argv)
diff --git a/third_party/abseil_cpp/absl/debugging/BUILD.bazel b/third_party/abseil_cpp/absl/debugging/BUILD.bazel
deleted file mode 100644
index cd6e454396..0000000000
--- a/third_party/abseil_cpp/absl/debugging/BUILD.bazel
+++ /dev/null
@@ -1,347 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(
-    default_visibility = ["//visibility:public"],
-)
-
-licenses(["notice"])
-
-cc_library(
-    name = "stacktrace",
-    srcs = [
-        "internal/stacktrace_aarch64-inl.inc",
-        "internal/stacktrace_arm-inl.inc",
-        "internal/stacktrace_config.h",
-        "internal/stacktrace_generic-inl.inc",
-        "internal/stacktrace_powerpc-inl.inc",
-        "internal/stacktrace_unimplemented-inl.inc",
-        "internal/stacktrace_win32-inl.inc",
-        "internal/stacktrace_x86-inl.inc",
-        "stacktrace.cc",
-    ],
-    hdrs = ["stacktrace.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":debugging_internal",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-    ],
-)
-
-cc_library(
-    name = "symbolize",
-    srcs = [
-        "symbolize.cc",
-        "symbolize_darwin.inc",
-        "symbolize_elf.inc",
-        "symbolize_unimplemented.inc",
-        "symbolize_win32.inc",
-    ],
-    hdrs = [
-        "internal/symbolize.h",
-        "symbolize.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS + select({
-        "//absl:windows": ["-DEFAULTLIB:dbghelp.lib"],
-        "//conditions:default": [],
-    }),
-    deps = [
-        ":debugging_internal",
-        ":demangle_internal",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:dynamic_annotations",
-        "//absl/base:malloc_internal",
-        "//absl/base:raw_logging_internal",
-        "//absl/strings",
-    ],
-)
-
-cc_test(
-    name = "symbolize_test",
-    srcs = ["symbolize_test.cc"],
-    copts = ABSL_TEST_COPTS + select({
-        "//absl:windows": ["/Z7"],
-        "//conditions:default": [],
-    }),
-    linkopts = ABSL_DEFAULT_LINKOPTS + select({
-        "//absl:windows": ["/DEBUG"],
-        "//conditions:default": [],
-    }),
-    deps = [
-        ":stack_consumption",
-        ":symbolize",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/memory",
-        "//absl/strings",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "examine_stack",
-    srcs = [
-        "internal/examine_stack.cc",
-    ],
-    hdrs = [
-        "internal/examine_stack.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":stacktrace",
-        ":symbolize",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_library(
-    name = "failure_signal_handler",
-    srcs = ["failure_signal_handler.cc"],
-    hdrs = ["failure_signal_handler.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":examine_stack",
-        ":stacktrace",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:errno_saver",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_test(
-    name = "failure_signal_handler_test",
-    srcs = ["failure_signal_handler_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = select({
-        "//absl:windows": [],
-        "//absl:wasm": [],
-        "//conditions:default": ["-pthread"],
-    }) + ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":failure_signal_handler",
-        ":stacktrace",
-        ":symbolize",
-        "//absl/base:raw_logging_internal",
-        "//absl/strings",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "debugging_internal",
-    srcs = [
-        "internal/address_is_readable.cc",
-        "internal/elf_mem_image.cc",
-        "internal/vdso_support.cc",
-    ],
-    hdrs = [
-        "internal/address_is_readable.h",
-        "internal/elf_mem_image.h",
-        "internal/vdso_support.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:dynamic_annotations",
-        "//absl/base:errno_saver",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_library(
-    name = "demangle_internal",
-    srcs = ["internal/demangle.cc"],
-    hdrs = ["internal/demangle.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    deps = [
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-    ],
-)
-
-cc_test(
-    name = "demangle_test",
-    srcs = ["internal/demangle_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":demangle_internal",
-        ":stack_consumption",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/memory",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "leak_check",
-    srcs = ["leak_check.cc"],
-    hdrs = ["leak_check.h"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:core_headers",
-    ],
-)
-
-# Adding a dependency to leak_check_disable will disable
-# sanitizer leak checking (asan/lsan) in a test without
-# the need to mess around with build features.
-cc_library(
-    name = "leak_check_disable",
-    srcs = ["leak_check_disable.cc"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    linkstatic = 1,
-    deps = ["//absl/base:config"],
-    alwayslink = 1,
-)
-
-# These targets exists for use in tests only, explicitly configuring the
-# LEAK_SANITIZER macro. It must be linked with -fsanitize=leak for lsan.
-ABSL_LSAN_LINKOPTS = select({
-    "//absl:clang_compiler": ["-fsanitize=leak"],
-    "//conditions:default": [],
-})
-
-cc_library(
-    name = "leak_check_api_enabled_for_testing",
-    testonly = 1,
-    srcs = ["leak_check.cc"],
-    hdrs = ["leak_check.h"],
-    copts = select({
-        "//absl:clang_compiler": ["-DLEAK_SANITIZER"],
-        "//conditions:default": [],
-    }),
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        "//absl/base:config",
-    ],
-)
-
-cc_library(
-    name = "leak_check_api_disabled_for_testing",
-    testonly = 1,
-    srcs = ["leak_check.cc"],
-    hdrs = ["leak_check.h"],
-    copts = ["-ULEAK_SANITIZER"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        "//absl/base:config",
-    ],
-)
-
-cc_test(
-    name = "leak_check_test",
-    srcs = ["leak_check_test.cc"],
-    copts = select({
-        "//absl:clang_compiler": ["-DABSL_EXPECT_LEAK_SANITIZER"],
-        "//conditions:default": [],
-    }),
-    linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
-    tags = ["notsan"],
-    deps = [
-        ":leak_check_api_enabled_for_testing",
-        "//absl/base",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "leak_check_no_lsan_test",
-    srcs = ["leak_check_test.cc"],
-    copts = ["-UABSL_EXPECT_LEAK_SANITIZER"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["noasan"],
-    deps = [
-        ":leak_check_api_disabled_for_testing",
-        "//absl/base",  # for raw_logging
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-# Test that leak checking is skipped when lsan is enabled but
-# ":leak_check_disable" is linked in.
-#
-# This test should fail in the absence of a dependency on ":leak_check_disable"
-cc_test(
-    name = "disabled_leak_check_test",
-    srcs = ["leak_check_fail_test.cc"],
-    linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
-    tags = ["notsan"],
-    deps = [
-        ":leak_check_api_enabled_for_testing",
-        ":leak_check_disable",
-        "//absl/base",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "stack_consumption",
-    testonly = 1,
-    srcs = ["internal/stack_consumption.cc"],
-    hdrs = ["internal/stack_consumption.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_test(
-    name = "stack_consumption_test",
-    srcs = ["internal/stack_consumption_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":stack_consumption",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/debugging/CMakeLists.txt b/third_party/abseil_cpp/absl/debugging/CMakeLists.txt
deleted file mode 100644
index 074b44cf17..0000000000
--- a/third_party/abseil_cpp/absl/debugging/CMakeLists.txt
+++ /dev/null
@@ -1,338 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    stacktrace
-  HDRS
-    "stacktrace.h"
-    "internal/stacktrace_aarch64-inl.inc"
-    "internal/stacktrace_arm-inl.inc"
-    "internal/stacktrace_config.h"
-    "internal/stacktrace_generic-inl.inc"
-    "internal/stacktrace_powerpc-inl.inc"
-    "internal/stacktrace_unimplemented-inl.inc"
-    "internal/stacktrace_win32-inl.inc"
-    "internal/stacktrace_x86-inl.inc"
-  SRCS
-    "stacktrace.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::debugging_internal
-    absl::config
-    absl::core_headers
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    symbolize
-  HDRS
-    "symbolize.h"
-    "internal/symbolize.h"
-  SRCS
-    "symbolize.cc"
-    "symbolize_darwin.inc"
-    "symbolize_elf.inc"
-    "symbolize_unimplemented.inc"
-    "symbolize_win32.inc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-    $<$<BOOL:${MINGW}>:"dbghelp">
-  DEPS
-    absl::debugging_internal
-    absl::demangle_internal
-    absl::base
-    absl::config
-    absl::core_headers
-    absl::dynamic_annotations
-    absl::malloc_internal
-    absl::raw_logging_internal
-    absl::strings
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    symbolize_test
-  SRCS
-    "symbolize_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-    $<$<BOOL:${MSVC}>:-Z7>
-  LINKOPTS
-    $<$<BOOL:${MSVC}>:-DEBUG>
-  DEPS
-    absl::stack_consumption
-    absl::symbolize
-    absl::base
-    absl::config
-    absl::core_headers
-    absl::memory
-    absl::raw_logging_internal
-    absl::strings
-    gmock
-)
-
-absl_cc_library(
-  NAME
-    examine_stack
-  HDRS
-    "internal/examine_stack.h"
-  SRCS
-    "internal/examine_stack.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::stacktrace
-    absl::symbolize
-    absl::config
-    absl::core_headers
-    absl::raw_logging_internal
-)
-
-absl_cc_library(
-  NAME
-    failure_signal_handler
-  HDRS
-    "failure_signal_handler.h"
-  SRCS
-    "failure_signal_handler.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::examine_stack
-    absl::stacktrace
-    absl::base
-    absl::config
-    absl::core_headers
-    absl::errno_saver
-    absl::raw_logging_internal
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    failure_signal_handler_test
-  SRCS
-    "failure_signal_handler_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::failure_signal_handler
-    absl::stacktrace
-    absl::symbolize
-    absl::strings
-    absl::raw_logging_internal
-    Threads::Threads
-    gmock
-)
-
-absl_cc_library(
-  NAME
-    debugging_internal
-  HDRS
-    "internal/address_is_readable.h"
-    "internal/elf_mem_image.h"
-    "internal/vdso_support.h"
-  SRCS
-    "internal/address_is_readable.cc"
-    "internal/elf_mem_image.cc"
-    "internal/vdso_support.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::core_headers
-    absl::config
-    absl::dynamic_annotations
-    absl::errno_saver
-    absl::raw_logging_internal
-)
-
-absl_cc_library(
-  NAME
-    demangle_internal
-  HDRS
-    "internal/demangle.h"
-  SRCS
-    "internal/demangle.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base
-    absl::core_headers
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    demangle_test
-  SRCS
-    "internal/demangle_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::demangle_internal
-    absl::stack_consumption
-    absl::config
-    absl::core_headers
-    absl::memory
-    absl::raw_logging_internal
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    leak_check
-  HDRS
-    "leak_check.h"
-  SRCS
-    "leak_check.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    leak_check_disable
-  SRCS
-    "leak_check_disable.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    leak_check_api_enabled_for_testing
-  HDRS
-    "leak_check.h"
-  SRCS
-    "leak_check.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-    $<$<BOOL:${ABSL_HAVE_LSAN}>:-DLEAK_SANITIZER>
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    leak_check_api_disabled_for_testing
-  HDRS
-    "leak_check.h"
-  SRCS
-    "leak_check.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-    "-ULEAK_SANITIZER"
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    leak_check_test
-  SRCS
-    "leak_check_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-    "$<$<BOOL:${ABSL_HAVE_LSAN}>:-DABSL_EXPECT_LEAK_SANITIZER>"
-  LINKOPTS
-    "${ABSL_LSAN_LINKOPTS}"
-  DEPS
-    absl::leak_check_api_enabled_for_testing
-    absl::base
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    leak_check_no_lsan_test
-  SRCS
-    "leak_check_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-    "-UABSL_EXPECT_LEAK_SANITIZER"
-  DEPS
-    absl::leak_check_api_disabled_for_testing
-    absl::base
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    disabled_leak_check_test
-  SRCS
-    "leak_check_fail_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    "${ABSL_LSAN_LINKOPTS}"
-  DEPS
-    absl::leak_check_api_enabled_for_testing
-    absl::leak_check_disable
-    absl::base
-    absl::raw_logging_internal
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    stack_consumption
-  HDRS
-    "internal/stack_consumption.h"
-  SRCS
-    "internal/stack_consumption.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::raw_logging_internal
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    stack_consumption_test
-  SRCS
-    "internal/stack_consumption_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::stack_consumption
-    absl::core_headers
-    absl::raw_logging_internal
-    gmock_main
-)
-
-# component target
-absl_cc_library(
-  NAME
-    debugging
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::stacktrace
-    absl::leak_check
-  PUBLIC
-)
diff --git a/third_party/abseil_cpp/absl/debugging/failure_signal_handler.cc b/third_party/abseil_cpp/absl/debugging/failure_signal_handler.cc
deleted file mode 100644
index 5d13bdbbbd..0000000000
--- a/third_party/abseil_cpp/absl/debugging/failure_signal_handler.cc
+++ /dev/null
@@ -1,370 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "absl/debugging/failure_signal_handler.h"
-
-#include "absl/base/config.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#else
-#include <unistd.h>
-#endif
-
-#ifdef __APPLE__
-#include <TargetConditionals.h>
-#endif
-
-#ifdef ABSL_HAVE_MMAP
-#include <sys/mman.h>
-#endif
-
-#include <algorithm>
-#include <atomic>
-#include <cerrno>
-#include <csignal>
-#include <cstdio>
-#include <cstring>
-#include <ctime>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/errno_saver.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/sysinfo.h"
-#include "absl/debugging/internal/examine_stack.h"
-#include "absl/debugging/stacktrace.h"
-
-#ifndef _WIN32
-#define ABSL_HAVE_SIGACTION
-// Apple WatchOS and TVOS don't allow sigaltstack
-#if !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \
-    !(defined(TARGET_OS_TV) && TARGET_OS_TV)
-#define ABSL_HAVE_SIGALTSTACK
-#endif
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options;
-
-// Resets the signal handler for signo to the default action for that
-// signal, then raises the signal.
-static void RaiseToDefaultHandler(int signo) {
-  signal(signo, SIG_DFL);
-  raise(signo);
-}
-
-struct FailureSignalData {
-  const int signo;
-  const char* const as_string;
-#ifdef ABSL_HAVE_SIGACTION
-  struct sigaction previous_action;
-  // StructSigaction is used to silence -Wmissing-field-initializers.
-  using StructSigaction = struct sigaction;
-  #define FSD_PREVIOUS_INIT FailureSignalData::StructSigaction()
-#else
-  void (*previous_handler)(int);
-  #define FSD_PREVIOUS_INIT SIG_DFL
-#endif
-};
-
-ABSL_CONST_INIT static FailureSignalData failure_signal_data[] = {
-    {SIGSEGV, "SIGSEGV", FSD_PREVIOUS_INIT},
-    {SIGILL, "SIGILL", FSD_PREVIOUS_INIT},
-    {SIGFPE, "SIGFPE", FSD_PREVIOUS_INIT},
-    {SIGABRT, "SIGABRT", FSD_PREVIOUS_INIT},
-    {SIGTERM, "SIGTERM", FSD_PREVIOUS_INIT},
-#ifndef _WIN32
-    {SIGBUS, "SIGBUS", FSD_PREVIOUS_INIT},
-    {SIGTRAP, "SIGTRAP", FSD_PREVIOUS_INIT},
-#endif
-};
-
-#undef FSD_PREVIOUS_INIT
-
-static void RaiseToPreviousHandler(int signo) {
-  // Search for the previous handler.
-  for (const auto& it : failure_signal_data) {
-    if (it.signo == signo) {
-#ifdef ABSL_HAVE_SIGACTION
-      sigaction(signo, &it.previous_action, nullptr);
-#else
-      signal(signo, it.previous_handler);
-#endif
-      raise(signo);
-      return;
-    }
-  }
-
-  // Not found, use the default handler.
-  RaiseToDefaultHandler(signo);
-}
-
-namespace debugging_internal {
-
-const char* FailureSignalToString(int signo) {
-  for (const auto& it : failure_signal_data) {
-    if (it.signo == signo) {
-      return it.as_string;
-    }
-  }
-  return "";
-}
-
-}  // namespace debugging_internal
-
-#ifdef ABSL_HAVE_SIGALTSTACK
-
-static bool SetupAlternateStackOnce() {
-#if defined(__wasm__) || defined (__asjms__)
-  const size_t page_mask = getpagesize() - 1;
-#else
-  const size_t page_mask = sysconf(_SC_PAGESIZE) - 1;
-#endif
-  size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
-#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
-    defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
-  // Account for sanitizer instrumentation requiring additional stack space.
-  stack_size *= 5;
-#endif
-
-  stack_t sigstk;
-  memset(&sigstk, 0, sizeof(sigstk));
-  sigstk.ss_size = stack_size;
-
-#ifdef ABSL_HAVE_MMAP
-#ifndef MAP_STACK
-#define MAP_STACK 0
-#endif
-#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-  sigstk.ss_sp = mmap(nullptr, sigstk.ss_size, PROT_READ | PROT_WRITE,
-                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
-  if (sigstk.ss_sp == MAP_FAILED) {
-    ABSL_RAW_LOG(FATAL, "mmap() for alternate signal stack failed");
-  }
-#else
-  sigstk.ss_sp = malloc(sigstk.ss_size);
-  if (sigstk.ss_sp == nullptr) {
-    ABSL_RAW_LOG(FATAL, "malloc() for alternate signal stack failed");
-  }
-#endif
-
-  if (sigaltstack(&sigstk, nullptr) != 0) {
-    ABSL_RAW_LOG(FATAL, "sigaltstack() failed with errno=%d", errno);
-  }
-  return true;
-}
-
-#endif
-
-#ifdef ABSL_HAVE_SIGACTION
-
-// Sets up an alternate stack for signal handlers once.
-// Returns the appropriate flag for sig_action.sa_flags
-// if the system supports using an alternate stack.
-static int MaybeSetupAlternateStack() {
-#ifdef ABSL_HAVE_SIGALTSTACK
-  ABSL_ATTRIBUTE_UNUSED static const bool kOnce = SetupAlternateStackOnce();
-  return SA_ONSTACK;
-#else
-  return 0;
-#endif
-}
-
-static void InstallOneFailureHandler(FailureSignalData* data,
-                                     void (*handler)(int, siginfo_t*, void*)) {
-  struct sigaction act;
-  memset(&act, 0, sizeof(act));
-  sigemptyset(&act.sa_mask);
-  act.sa_flags |= SA_SIGINFO;
-  // SA_NODEFER is required to handle SIGABRT from
-  // ImmediateAbortSignalHandler().
-  act.sa_flags |= SA_NODEFER;
-  if (fsh_options.use_alternate_stack) {
-    act.sa_flags |= MaybeSetupAlternateStack();
-  }
-  act.sa_sigaction = handler;
-  ABSL_RAW_CHECK(sigaction(data->signo, &act, &data->previous_action) == 0,
-                 "sigaction() failed");
-}
-
-#else
-
-static void InstallOneFailureHandler(FailureSignalData* data,
-                                     void (*handler)(int)) {
-  data->previous_handler = signal(data->signo, handler);
-  ABSL_RAW_CHECK(data->previous_handler != SIG_ERR, "signal() failed");
-}
-
-#endif
-
-static void WriteToStderr(const char* data) {
-  absl::base_internal::ErrnoSaver errno_saver;
-  absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data));
-}
-
-static void WriteSignalMessage(int signo, void (*writerfn)(const char*)) {
-  char buf[64];
-  const char* const signal_string =
-      debugging_internal::FailureSignalToString(signo);
-  if (signal_string != nullptr && signal_string[0] != '\0') {
-    snprintf(buf, sizeof(buf), "*** %s received at time=%ld ***\n",
-             signal_string,
-             static_cast<long>(time(nullptr)));  // NOLINT(runtime/int)
-  } else {
-    snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld ***\n",
-             signo, static_cast<long>(time(nullptr)));  // NOLINT(runtime/int)
-  }
-  writerfn(buf);
-}
-
-// `void*` might not be big enough to store `void(*)(const char*)`.
-struct WriterFnStruct {
-  void (*writerfn)(const char*);
-};
-
-// Many of the absl::debugging_internal::Dump* functions in
-// examine_stack.h take a writer function pointer that has a void* arg
-// for historical reasons. failure_signal_handler_writer only takes a
-// data pointer. This function converts between these types.
-static void WriterFnWrapper(const char* data, void* arg) {
-  static_cast<WriterFnStruct*>(arg)->writerfn(data);
-}
-
-// Convenient wrapper around DumpPCAndFrameSizesAndStackTrace() for signal
-// handlers. "noinline" so that GetStackFrames() skips the top-most stack
-// frame for this function.
-ABSL_ATTRIBUTE_NOINLINE static void WriteStackTrace(
-    void* ucontext, bool symbolize_stacktrace,
-    void (*writerfn)(const char*, void*), void* writerfn_arg) {
-  constexpr int kNumStackFrames = 32;
-  void* stack[kNumStackFrames];
-  int frame_sizes[kNumStackFrames];
-  int min_dropped_frames;
-  int depth = absl::GetStackFramesWithContext(
-      stack, frame_sizes, kNumStackFrames,
-      1,  // Do not include this function in stack trace.
-      ucontext, &min_dropped_frames);
-  absl::debugging_internal::DumpPCAndFrameSizesAndStackTrace(
-      absl::debugging_internal::GetProgramCounter(ucontext), stack, frame_sizes,
-      depth, min_dropped_frames, symbolize_stacktrace, writerfn, writerfn_arg);
-}
-
-// Called by AbslFailureSignalHandler() to write the failure info. It is
-// called once with writerfn set to WriteToStderr() and then possibly
-// with writerfn set to the user provided function.
-static void WriteFailureInfo(int signo, void* ucontext,
-                             void (*writerfn)(const char*)) {
-  WriterFnStruct writerfn_struct{writerfn};
-  WriteSignalMessage(signo, writerfn);
-  WriteStackTrace(ucontext, fsh_options.symbolize_stacktrace, WriterFnWrapper,
-                  &writerfn_struct);
-}
-
-// absl::SleepFor() can't be used here since AbslInternalSleepFor()
-// may be overridden to do something that isn't async-signal-safe on
-// some platforms.
-static void PortableSleepForSeconds(int seconds) {
-#ifdef _WIN32
-  Sleep(seconds * 1000);
-#else
-  struct timespec sleep_time;
-  sleep_time.tv_sec = seconds;
-  sleep_time.tv_nsec = 0;
-  while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {}
-#endif
-}
-
-#ifdef ABSL_HAVE_ALARM
-// AbslFailureSignalHandler() installs this as a signal handler for
-// SIGALRM, then sets an alarm to be delivered to the program after a
-// set amount of time. If AbslFailureSignalHandler() hangs for more than
-// the alarm timeout, ImmediateAbortSignalHandler() will abort the
-// program.
-static void ImmediateAbortSignalHandler(int) {
-  RaiseToDefaultHandler(SIGABRT);
-}
-#endif
-
-// absl::base_internal::GetTID() returns pid_t on most platforms, but
-// returns absl::base_internal::pid_t on Windows.
-using GetTidType = decltype(absl::base_internal::GetTID());
-ABSL_CONST_INIT static std::atomic<GetTidType> failed_tid(0);
-
-#ifndef ABSL_HAVE_SIGACTION
-static void AbslFailureSignalHandler(int signo) {
-  void* ucontext = nullptr;
-#else
-static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) {
-#endif
-
-  const GetTidType this_tid = absl::base_internal::GetTID();
-  GetTidType previous_failed_tid = 0;
-  if (!failed_tid.compare_exchange_strong(
-          previous_failed_tid, static_cast<intptr_t>(this_tid),
-          std::memory_order_acq_rel, std::memory_order_relaxed)) {
-    ABSL_RAW_LOG(
-        ERROR,
-        "Signal %d raised at PC=%p while already in AbslFailureSignalHandler()",
-        signo, absl::debugging_internal::GetProgramCounter(ucontext));
-    if (this_tid != previous_failed_tid) {
-      // Another thread is already in AbslFailureSignalHandler(), so wait
-      // a bit for it to finish. If the other thread doesn't kill us,
-      // we do so after sleeping.
-      PortableSleepForSeconds(3);
-      RaiseToDefaultHandler(signo);
-      // The recursively raised signal may be blocked until we return.
-      return;
-    }
-  }
-
-#ifdef ABSL_HAVE_ALARM
-  // Set an alarm to abort the program in case this code hangs or deadlocks.
-  if (fsh_options.alarm_on_failure_secs > 0) {
-    alarm(0);  // Cancel any existing alarms.
-    signal(SIGALRM, ImmediateAbortSignalHandler);
-    alarm(fsh_options.alarm_on_failure_secs);
-  }
-#endif
-
-  // First write to stderr.
-  WriteFailureInfo(signo, ucontext, WriteToStderr);
-
-  // Riskier code (because it is less likely to be async-signal-safe)
-  // goes after this point.
-  if (fsh_options.writerfn != nullptr) {
-    WriteFailureInfo(signo, ucontext, fsh_options.writerfn);
-  }
-
-  if (fsh_options.call_previous_handler) {
-    RaiseToPreviousHandler(signo);
-  } else {
-    RaiseToDefaultHandler(signo);
-  }
-}
-
-void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) {
-  fsh_options = options;
-  for (auto& it : failure_signal_data) {
-    InstallOneFailureHandler(&it, AbslFailureSignalHandler);
-  }
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/debugging/failure_signal_handler.h b/third_party/abseil_cpp/absl/debugging/failure_signal_handler.h
deleted file mode 100644
index 0c0f585d0f..0000000000
--- a/third_party/abseil_cpp/absl/debugging/failure_signal_handler.h
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: failure_signal_handler.h
-// -----------------------------------------------------------------------------
-//
-// This file configures the Abseil *failure signal handler* to capture and dump
-// useful debugging information (such as a stacktrace) upon program failure.
-//
-// To use the failure signal handler, call `absl::InstallFailureSignalHandler()`
-// very early in your program, usually in the first few lines of main():
-//
-// int main(int argc, char** argv) {
-//   // Initialize the symbolizer to get a human-readable stack trace
-//   absl::InitializeSymbolizer(argv[0]);
-//
-//   absl::FailureSignalHandlerOptions options;
-//   absl::InstallFailureSignalHandler(options);
-//   DoSomethingInteresting();
-//   return 0;
-// }
-//
-// Any program that raises a fatal signal (such as `SIGSEGV`, `SIGILL`,
-// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP`) will call the
-// installed failure signal handler and provide debugging information to stderr.
-//
-// Note that you should *not* install the Abseil failure signal handler more
-// than once. You may, of course, have another (non-Abseil) failure signal
-// handler installed (which would be triggered if Abseil's failure signal
-// handler sets `call_previous_handler` to `true`).
-
-#ifndef ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
-#define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// FailureSignalHandlerOptions
-//
-// Struct for holding `absl::InstallFailureSignalHandler()` configuration
-// options.
-struct FailureSignalHandlerOptions {
-  // If true, try to symbolize the stacktrace emitted on failure, provided that
-  // you have initialized a symbolizer for that purpose. (See symbolize.h for
-  // more information.)
-  bool symbolize_stacktrace = true;
-
-  // If true, try to run signal handlers on an alternate stack (if supported on
-  // the given platform). An alternate stack is useful for program crashes due
-  // to a stack overflow; by running on a alternate stack, the signal handler
-  // may run even when normal stack space has been exausted. The downside of
-  // using an alternate stack is that extra memory for the alternate stack needs
-  // to be pre-allocated.
-  bool use_alternate_stack = true;
-
-  // If positive, indicates the number of seconds after which the failure signal
-  // handler is invoked to abort the program. Setting such an alarm is useful in
-  // cases where the failure signal handler itself may become hung or
-  // deadlocked.
-  int alarm_on_failure_secs = 3;
-
-  // If true, call the previously registered signal handler for the signal that
-  // was received (if one was registered) after the existing signal handler
-  // runs. This mechanism can be used to chain signal handlers together.
-  //
-  // If false, the signal is raised to the default handler for that signal
-  // (which normally terminates the program).
-  //
-  // IMPORTANT: If true, the chained fatal signal handlers must not try to
-  // recover from the fatal signal. Instead, they should terminate the program
-  // via some mechanism, like raising the default handler for the signal, or by
-  // calling `_exit()`. Note that the failure signal handler may put parts of
-  // the Abseil library into a state from which they cannot recover.
-  bool call_previous_handler = false;
-
-  // If non-null, indicates a pointer to a callback function that will be called
-  // upon failure, with a string argument containing failure data. This function
-  // may be used as a hook to write failure data to a secondary location, such
-  // as a log file. This function may also be called with null data, as a hint
-  // to flush any buffered data before the program may be terminated. Consider
-  // flushing any buffered data in all calls to this function.
-  //
-  // Since this function runs within a signal handler, it should be
-  // async-signal-safe if possible.
-  // See http://man7.org/linux/man-pages/man7/signal-safety.7.html
-  void (*writerfn)(const char*) = nullptr;
-};
-
-// InstallFailureSignalHandler()
-//
-// Installs a signal handler for the common failure signals `SIGSEGV`, `SIGILL`,
-// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP` (provided they exist
-// on the given platform). The failure signal handler dumps program failure data
-// useful for debugging in an unspecified format to stderr. This data may
-// include the program counter, a stacktrace, and register information on some
-// systems; do not rely on an exact format for the output, as it is subject to
-// change.
-void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options);
-
-namespace debugging_internal {
-const char* FailureSignalToString(int signo);
-}  // namespace debugging_internal
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
diff --git a/third_party/abseil_cpp/absl/debugging/failure_signal_handler_test.cc b/third_party/abseil_cpp/absl/debugging/failure_signal_handler_test.cc
deleted file mode 100644
index d8283b2f47..0000000000
--- a/third_party/abseil_cpp/absl/debugging/failure_signal_handler_test.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "absl/debugging/failure_signal_handler.h"
-
-#include <csignal>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <fstream>
-
-#include "gtest/gtest.h"
-#include "gmock/gmock.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/debugging/stacktrace.h"
-#include "absl/debugging/symbolize.h"
-#include "absl/strings/match.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-using testing::StartsWith;
-
-#if GTEST_HAS_DEATH_TEST
-
-// For the parameterized death tests. GetParam() returns the signal number.
-using FailureSignalHandlerDeathTest = ::testing::TestWithParam<int>;
-
-// This function runs in a fork()ed process on most systems.
-void InstallHandlerAndRaise(int signo) {
-  absl::InstallFailureSignalHandler(absl::FailureSignalHandlerOptions());
-  raise(signo);
-}
-
-TEST_P(FailureSignalHandlerDeathTest, AbslFailureSignal) {
-  const int signo = GetParam();
-  std::string exit_regex = absl::StrCat(
-      "\\*\\*\\* ", absl::debugging_internal::FailureSignalToString(signo),
-      " received at time=");
-#ifndef _WIN32
-  EXPECT_EXIT(InstallHandlerAndRaise(signo), testing::KilledBySignal(signo),
-              exit_regex);
-#else
-  // Windows doesn't have testing::KilledBySignal().
-  EXPECT_DEATH_IF_SUPPORTED(InstallHandlerAndRaise(signo), exit_regex);
-#endif
-}
-
-ABSL_CONST_INIT FILE* error_file = nullptr;
-
-void WriteToErrorFile(const char* msg) {
-  if (msg != nullptr) {
-    ABSL_RAW_CHECK(fwrite(msg, strlen(msg), 1, error_file) == 1,
-                   "fwrite() failed");
-  }
-  ABSL_RAW_CHECK(fflush(error_file) == 0, "fflush() failed");
-}
-
-std::string GetTmpDir() {
-  // TEST_TMPDIR is set by Bazel. Try the others when not running under Bazel.
-  static const char* const kTmpEnvVars[] = {"TEST_TMPDIR", "TMPDIR", "TEMP",
-                                            "TEMPDIR", "TMP"};
-  for (const char* const var : kTmpEnvVars) {
-    const char* tmp_dir = std::getenv(var);
-    if (tmp_dir != nullptr) {
-      return tmp_dir;
-    }
-  }
-
-  // Try something reasonable.
-  return "/tmp";
-}
-
-// This function runs in a fork()ed process on most systems.
-void InstallHandlerWithWriteToFileAndRaise(const char* file, int signo) {
-  error_file = fopen(file, "w");
-  ABSL_RAW_CHECK(error_file != nullptr, "Failed create error_file");
-  absl::FailureSignalHandlerOptions options;
-  options.writerfn = WriteToErrorFile;
-  absl::InstallFailureSignalHandler(options);
-  raise(signo);
-}
-
-TEST_P(FailureSignalHandlerDeathTest, AbslFatalSignalsWithWriterFn) {
-  const int signo = GetParam();
-  std::string tmp_dir = GetTmpDir();
-  std::string file = absl::StrCat(tmp_dir, "/signo_", signo);
-
-  std::string exit_regex = absl::StrCat(
-      "\\*\\*\\* ", absl::debugging_internal::FailureSignalToString(signo),
-      " received at time=");
-#ifndef _WIN32
-  EXPECT_EXIT(InstallHandlerWithWriteToFileAndRaise(file.c_str(), signo),
-              testing::KilledBySignal(signo), exit_regex);
-#else
-  // Windows doesn't have testing::KilledBySignal().
-  EXPECT_DEATH_IF_SUPPORTED(
-      InstallHandlerWithWriteToFileAndRaise(file.c_str(), signo), exit_regex);
-#endif
-
-  // Open the file in this process and check its contents.
-  std::fstream error_output(file);
-  ASSERT_TRUE(error_output.is_open()) << file;
-  std::string error_line;
-  std::getline(error_output, error_line);
-  EXPECT_THAT(
-      error_line,
-      StartsWith(absl::StrCat(
-          "*** ", absl::debugging_internal::FailureSignalToString(signo),
-          " received at ")));
-
-  if (absl::debugging_internal::StackTraceWorksForTest()) {
-    std::getline(error_output, error_line);
-    EXPECT_THAT(error_line, StartsWith("PC: "));
-  }
-}
-
-constexpr int kFailureSignals[] = {
-    SIGSEGV, SIGILL,  SIGFPE, SIGABRT, SIGTERM,
-#ifndef _WIN32
-    SIGBUS,  SIGTRAP,
-#endif
-};
-
-std::string SignalParamToString(const ::testing::TestParamInfo<int>& info) {
-  std::string result =
-      absl::debugging_internal::FailureSignalToString(info.param);
-  if (result.empty()) {
-    result = absl::StrCat(info.param);
-  }
-  return result;
-}
-
-INSTANTIATE_TEST_SUITE_P(AbslDeathTest, FailureSignalHandlerDeathTest,
-                         ::testing::ValuesIn(kFailureSignals),
-                         SignalParamToString);
-
-#endif  // GTEST_HAS_DEATH_TEST
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  absl::InitializeSymbolizer(argv[0]);
-  testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/third_party/abseil_cpp/absl/debugging/internal/address_is_readable.cc b/third_party/abseil_cpp/absl/debugging/internal/address_is_readable.cc
deleted file mode 100644
index 329c285f3b..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/address_is_readable.cc
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// base::AddressIsReadable() probes an address to see whether it is readable,
-// without faulting.
-
-#include "absl/debugging/internal/address_is_readable.h"
-
-#if !defined(__linux__) || defined(__ANDROID__)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// On platforms other than Linux, just return true.
-bool AddressIsReadable(const void* /* addr */) { return true; }
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else
-
-#include <fcntl.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#include <atomic>
-#include <cerrno>
-#include <cstdint>
-
-#include "absl/base/internal/errno_saver.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Pack a pid and two file descriptors into a 64-bit word,
-// using 16, 24, and 24 bits for each respectively.
-static uint64_t Pack(uint64_t pid, uint64_t read_fd, uint64_t write_fd) {
-  ABSL_RAW_CHECK((read_fd >> 24) == 0 && (write_fd >> 24) == 0,
-                 "fd out of range");
-  return (pid << 48) | ((read_fd & 0xffffff) << 24) | (write_fd & 0xffffff);
-}
-
-// Unpack x into a pid and two file descriptors, where x was created with
-// Pack().
-static void Unpack(uint64_t x, int *pid, int *read_fd, int *write_fd) {
-  *pid = x >> 48;
-  *read_fd = (x >> 24) & 0xffffff;
-  *write_fd = x & 0xffffff;
-}
-
-// Return whether the byte at *addr is readable, without faulting.
-// Save and restores errno.   Returns true on systems where
-// unimplemented.
-// This is a namespace-scoped variable for correct zero-initialization.
-static std::atomic<uint64_t> pid_and_fds;  // initially 0, an invalid pid.
-
-bool AddressIsReadable(const void *addr) {
-  absl::base_internal::ErrnoSaver errno_saver;
-  // We test whether a byte is readable by using write().  Normally, this would
-  // be done via a cached file descriptor to /dev/null, but linux fails to
-  // check whether the byte is readable when the destination is /dev/null, so
-  // we use a cached pipe.  We store the pid of the process that created the
-  // pipe to handle the case where a process forks, and the child closes all
-  // the file descriptors and then calls this routine.  This is not perfect:
-  // the child could use the routine, then close all file descriptors and then
-  // use this routine again.  But the likely use of this routine is when
-  // crashing, to test the validity of pages when dumping the stack.  Beware
-  // that we may leak file descriptors, but we're unlikely to leak many.
-  int bytes_written;
-  int current_pid = getpid() & 0xffff;   // we use only the low order 16 bits
-  do {  // until we do not get EBADF trying to use file descriptors
-    int pid;
-    int read_fd;
-    int write_fd;
-    uint64_t local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire);
-    Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd);
-    while (current_pid != pid) {
-      int p[2];
-      // new pipe
-      if (pipe(p) != 0) {
-        ABSL_RAW_LOG(FATAL, "Failed to create pipe, errno=%d", errno);
-      }
-      fcntl(p[0], F_SETFD, FD_CLOEXEC);
-      fcntl(p[1], F_SETFD, FD_CLOEXEC);
-      uint64_t new_pid_and_fds = Pack(current_pid, p[0], p[1]);
-      if (pid_and_fds.compare_exchange_strong(
-              local_pid_and_fds, new_pid_and_fds, std::memory_order_release,
-              std::memory_order_relaxed)) {
-        local_pid_and_fds = new_pid_and_fds;  // fds exposed to other threads
-      } else {  // fds not exposed to other threads; we can close them.
-        close(p[0]);
-        close(p[1]);
-        local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire);
-      }
-      Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd);
-    }
-    errno = 0;
-    // Use syscall(SYS_write, ...) instead of write() to prevent ASAN
-    // and other checkers from complaining about accesses to arbitrary
-    // memory.
-    do {
-      bytes_written = syscall(SYS_write, write_fd, addr, 1);
-    } while (bytes_written == -1 && errno == EINTR);
-    if (bytes_written == 1) {   // remove the byte from the pipe
-      char c;
-      while (read(read_fd, &c, 1) == -1 && errno == EINTR) {
-      }
-    }
-    if (errno == EBADF) {  // Descriptors invalid.
-      // If pid_and_fds contains the problematic file descriptors we just used,
-      // this call will forget them, and the loop will try again.
-      pid_and_fds.compare_exchange_strong(local_pid_and_fds, 0,
-                                          std::memory_order_release,
-                                          std::memory_order_relaxed);
-    }
-  } while (errno == EBADF);
-  return bytes_written == 1;
-}
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif
diff --git a/third_party/abseil_cpp/absl/debugging/internal/address_is_readable.h b/third_party/abseil_cpp/absl/debugging/internal/address_is_readable.h
deleted file mode 100644
index 4bbaf4d69b..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/address_is_readable.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_
-#define ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Return whether the byte at *addr is readable, without faulting.
-// Save and restores errno.
-bool AddressIsReadable(const void *addr);
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/demangle.cc b/third_party/abseil_cpp/absl/debugging/internal/demangle.cc
deleted file mode 100644
index 46cdb67b1f..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/demangle.cc
+++ /dev/null
@@ -1,1945 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// For reference check out:
-// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
-//
-// Note that we only have partial C++11 support yet.
-
-#include "absl/debugging/internal/demangle.h"
-
-#include <cstdint>
-#include <cstdio>
-#include <limits>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-typedef struct {
-  const char *abbrev;
-  const char *real_name;
-  // Number of arguments in <expression> context, or 0 if disallowed.
-  int arity;
-} AbbrevPair;
-
-// List of operators from Itanium C++ ABI.
-static const AbbrevPair kOperatorList[] = {
-    // New has special syntax (not currently supported).
-    {"nw", "new", 0},
-    {"na", "new[]", 0},
-
-    // Works except that the 'gs' prefix is not supported.
-    {"dl", "delete", 1},
-    {"da", "delete[]", 1},
-
-    {"ps", "+", 1},  // "positive"
-    {"ng", "-", 1},  // "negative"
-    {"ad", "&", 1},  // "address-of"
-    {"de", "*", 1},  // "dereference"
-    {"co", "~", 1},
-
-    {"pl", "+", 2},
-    {"mi", "-", 2},
-    {"ml", "*", 2},
-    {"dv", "/", 2},
-    {"rm", "%", 2},
-    {"an", "&", 2},
-    {"or", "|", 2},
-    {"eo", "^", 2},
-    {"aS", "=", 2},
-    {"pL", "+=", 2},
-    {"mI", "-=", 2},
-    {"mL", "*=", 2},
-    {"dV", "/=", 2},
-    {"rM", "%=", 2},
-    {"aN", "&=", 2},
-    {"oR", "|=", 2},
-    {"eO", "^=", 2},
-    {"ls", "<<", 2},
-    {"rs", ">>", 2},
-    {"lS", "<<=", 2},
-    {"rS", ">>=", 2},
-    {"eq", "==", 2},
-    {"ne", "!=", 2},
-    {"lt", "<", 2},
-    {"gt", ">", 2},
-    {"le", "<=", 2},
-    {"ge", ">=", 2},
-    {"nt", "!", 1},
-    {"aa", "&&", 2},
-    {"oo", "||", 2},
-    {"pp", "++", 1},
-    {"mm", "--", 1},
-    {"cm", ",", 2},
-    {"pm", "->*", 2},
-    {"pt", "->", 0},  // Special syntax
-    {"cl", "()", 0},  // Special syntax
-    {"ix", "[]", 2},
-    {"qu", "?", 3},
-    {"st", "sizeof", 0},  // Special syntax
-    {"sz", "sizeof", 1},  // Not a real operator name, but used in expressions.
-    {nullptr, nullptr, 0},
-};
-
-// List of builtin types from Itanium C++ ABI.
-//
-// Invariant: only one- or two-character type abbreviations here.
-static const AbbrevPair kBuiltinTypeList[] = {
-    {"v", "void", 0},
-    {"w", "wchar_t", 0},
-    {"b", "bool", 0},
-    {"c", "char", 0},
-    {"a", "signed char", 0},
-    {"h", "unsigned char", 0},
-    {"s", "short", 0},
-    {"t", "unsigned short", 0},
-    {"i", "int", 0},
-    {"j", "unsigned int", 0},
-    {"l", "long", 0},
-    {"m", "unsigned long", 0},
-    {"x", "long long", 0},
-    {"y", "unsigned long long", 0},
-    {"n", "__int128", 0},
-    {"o", "unsigned __int128", 0},
-    {"f", "float", 0},
-    {"d", "double", 0},
-    {"e", "long double", 0},
-    {"g", "__float128", 0},
-    {"z", "ellipsis", 0},
-
-    {"De", "decimal128", 0},      // IEEE 754r decimal floating point (128 bits)
-    {"Dd", "decimal64", 0},       // IEEE 754r decimal floating point (64 bits)
-    {"Dc", "decltype(auto)", 0},
-    {"Da", "auto", 0},
-    {"Dn", "std::nullptr_t", 0},  // i.e., decltype(nullptr)
-    {"Df", "decimal32", 0},       // IEEE 754r decimal floating point (32 bits)
-    {"Di", "char32_t", 0},
-    {"Du", "char8_t", 0},
-    {"Ds", "char16_t", 0},
-    {"Dh", "float16", 0},         // IEEE 754r half-precision float (16 bits)
-    {nullptr, nullptr, 0},
-};
-
-// List of substitutions Itanium C++ ABI.
-static const AbbrevPair kSubstitutionList[] = {
-    {"St", "", 0},
-    {"Sa", "allocator", 0},
-    {"Sb", "basic_string", 0},
-    // std::basic_string<char, std::char_traits<char>,std::allocator<char> >
-    {"Ss", "string", 0},
-    // std::basic_istream<char, std::char_traits<char> >
-    {"Si", "istream", 0},
-    // std::basic_ostream<char, std::char_traits<char> >
-    {"So", "ostream", 0},
-    // std::basic_iostream<char, std::char_traits<char> >
-    {"Sd", "iostream", 0},
-    {nullptr, nullptr, 0},
-};
-
-// State needed for demangling.  This struct is copied in almost every stack
-// frame, so every byte counts.
-typedef struct {
-  int mangled_idx;                   // Cursor of mangled name.
-  int out_cur_idx;                   // Cursor of output string.
-  int prev_name_idx;                 // For constructors/destructors.
-  signed int prev_name_length : 16;  // For constructors/destructors.
-  signed int nest_level : 15;        // For nested names.
-  unsigned int append : 1;           // Append flag.
-  // Note: for some reason MSVC can't pack "bool append : 1" into the same int
-  // with the above two fields, so we use an int instead.  Amusingly it can pack
-  // "signed bool" as expected, but relying on that to continue to be a legal
-  // type seems ill-advised (as it's illegal in at least clang).
-} ParseState;
-
-static_assert(sizeof(ParseState) == 4 * sizeof(int),
-              "unexpected size of ParseState");
-
-// One-off state for demangling that's not subject to backtracking -- either
-// constant data, data that's intentionally immune to backtracking (steps), or
-// data that would never be changed by backtracking anyway (recursion_depth).
-//
-// Only one copy of this exists for each call to Demangle, so the size of this
-// struct is nearly inconsequential.
-typedef struct {
-  const char *mangled_begin;  // Beginning of input string.
-  char *out;                  // Beginning of output string.
-  int out_end_idx;            // One past last allowed output character.
-  int recursion_depth;        // For stack exhaustion prevention.
-  int steps;               // Cap how much work we'll do, regardless of depth.
-  ParseState parse_state;  // Backtrackable state copied for most frames.
-} State;
-
-namespace {
-// Prevent deep recursion / stack exhaustion.
-// Also prevent unbounded handling of complex inputs.
-class ComplexityGuard {
- public:
-  explicit ComplexityGuard(State *state) : state_(state) {
-    ++state->recursion_depth;
-    ++state->steps;
-  }
-  ~ComplexityGuard() { --state_->recursion_depth; }
-
-  // 256 levels of recursion seems like a reasonable upper limit on depth.
-  // 128 is not enough to demagle synthetic tests from demangle_unittest.txt:
-  // "_ZaaZZZZ..." and "_ZaaZcvZcvZ..."
-  static constexpr int kRecursionDepthLimit = 256;
-
-  // We're trying to pick a charitable upper-limit on how many parse steps are
-  // necessary to handle something that a human could actually make use of.
-  // This is mostly in place as a bound on how much work we'll do if we are
-  // asked to demangle an mangled name from an untrusted source, so it should be
-  // much larger than the largest expected symbol, but much smaller than the
-  // amount of work we can do in, e.g., a second.
-  //
-  // Some real-world symbols from an arbitrary binary started failing between
-  // 2^12 and 2^13, so we multiply the latter by an extra factor of 16 to set
-  // the limit.
-  //
-  // Spending one second on 2^17 parse steps would require each step to take
-  // 7.6us, or ~30000 clock cycles, so it's safe to say this can be done in
-  // under a second.
-  static constexpr int kParseStepsLimit = 1 << 17;
-
-  bool IsTooComplex() const {
-    return state_->recursion_depth > kRecursionDepthLimit ||
-           state_->steps > kParseStepsLimit;
-  }
-
- private:
-  State *state_;
-};
-}  // namespace
-
-// We don't use strlen() in libc since it's not guaranteed to be async
-// signal safe.
-static size_t StrLen(const char *str) {
-  size_t len = 0;
-  while (*str != '\0') {
-    ++str;
-    ++len;
-  }
-  return len;
-}
-
-// Returns true if "str" has at least "n" characters remaining.
-static bool AtLeastNumCharsRemaining(const char *str, int n) {
-  for (int i = 0; i < n; ++i) {
-    if (str[i] == '\0') {
-      return false;
-    }
-  }
-  return true;
-}
-
-// Returns true if "str" has "prefix" as a prefix.
-static bool StrPrefix(const char *str, const char *prefix) {
-  size_t i = 0;
-  while (str[i] != '\0' && prefix[i] != '\0' && str[i] == prefix[i]) {
-    ++i;
-  }
-  return prefix[i] == '\0';  // Consumed everything in "prefix".
-}
-
-static void InitState(State *state, const char *mangled, char *out,
-                      int out_size) {
-  state->mangled_begin = mangled;
-  state->out = out;
-  state->out_end_idx = out_size;
-  state->recursion_depth = 0;
-  state->steps = 0;
-
-  state->parse_state.mangled_idx = 0;
-  state->parse_state.out_cur_idx = 0;
-  state->parse_state.prev_name_idx = 0;
-  state->parse_state.prev_name_length = -1;
-  state->parse_state.nest_level = -1;
-  state->parse_state.append = true;
-}
-
-static inline const char *RemainingInput(State *state) {
-  return &state->mangled_begin[state->parse_state.mangled_idx];
-}
-
-// Returns true and advances "mangled_idx" if we find "one_char_token"
-// at "mangled_idx" position.  It is assumed that "one_char_token" does
-// not contain '\0'.
-static bool ParseOneCharToken(State *state, const char one_char_token) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (RemainingInput(state)[0] == one_char_token) {
-    ++state->parse_state.mangled_idx;
-    return true;
-  }
-  return false;
-}
-
-// Returns true and advances "mangled_cur" if we find "two_char_token"
-// at "mangled_cur" position.  It is assumed that "two_char_token" does
-// not contain '\0'.
-static bool ParseTwoCharToken(State *state, const char *two_char_token) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (RemainingInput(state)[0] == two_char_token[0] &&
-      RemainingInput(state)[1] == two_char_token[1]) {
-    state->parse_state.mangled_idx += 2;
-    return true;
-  }
-  return false;
-}
-
-// Returns true and advances "mangled_cur" if we find any character in
-// "char_class" at "mangled_cur" position.
-static bool ParseCharClass(State *state, const char *char_class) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (RemainingInput(state)[0] == '\0') {
-    return false;
-  }
-  const char *p = char_class;
-  for (; *p != '\0'; ++p) {
-    if (RemainingInput(state)[0] == *p) {
-      ++state->parse_state.mangled_idx;
-      return true;
-    }
-  }
-  return false;
-}
-
-static bool ParseDigit(State *state, int *digit) {
-  char c = RemainingInput(state)[0];
-  if (ParseCharClass(state, "0123456789")) {
-    if (digit != nullptr) {
-      *digit = c - '0';
-    }
-    return true;
-  }
-  return false;
-}
-
-// This function is used for handling an optional non-terminal.
-static bool Optional(bool /*status*/) { return true; }
-
-// This function is used for handling <non-terminal>+ syntax.
-typedef bool (*ParseFunc)(State *);
-static bool OneOrMore(ParseFunc parse_func, State *state) {
-  if (parse_func(state)) {
-    while (parse_func(state)) {
-    }
-    return true;
-  }
-  return false;
-}
-
-// This function is used for handling <non-terminal>* syntax. The function
-// always returns true and must be followed by a termination token or a
-// terminating sequence not handled by parse_func (e.g.
-// ParseOneCharToken(state, 'E')).
-static bool ZeroOrMore(ParseFunc parse_func, State *state) {
-  while (parse_func(state)) {
-  }
-  return true;
-}
-
-// Append "str" at "out_cur_idx".  If there is an overflow, out_cur_idx is
-// set to out_end_idx+1.  The output string is ensured to
-// always terminate with '\0' as long as there is no overflow.
-static void Append(State *state, const char *const str, const int length) {
-  for (int i = 0; i < length; ++i) {
-    if (state->parse_state.out_cur_idx + 1 <
-        state->out_end_idx) {  // +1 for '\0'
-      state->out[state->parse_state.out_cur_idx++] = str[i];
-    } else {
-      // signal overflow
-      state->parse_state.out_cur_idx = state->out_end_idx + 1;
-      break;
-    }
-  }
-  if (state->parse_state.out_cur_idx < state->out_end_idx) {
-    state->out[state->parse_state.out_cur_idx] =
-        '\0';  // Terminate it with '\0'
-  }
-}
-
-// We don't use equivalents in libc to avoid locale issues.
-static bool IsLower(char c) { return c >= 'a' && c <= 'z'; }
-
-static bool IsAlpha(char c) {
-  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
-}
-
-static bool IsDigit(char c) { return c >= '0' && c <= '9'; }
-
-// Returns true if "str" is a function clone suffix.  These suffixes are used
-// by GCC 4.5.x and later versions (and our locally-modified version of GCC
-// 4.4.x) to indicate functions which have been cloned during optimization.
-// We treat any sequence (.<alpha>+.<digit>+)+ as a function clone suffix.
-static bool IsFunctionCloneSuffix(const char *str) {
-  size_t i = 0;
-  while (str[i] != '\0') {
-    // Consume a single .<alpha>+.<digit>+ sequence.
-    if (str[i] != '.' || !IsAlpha(str[i + 1])) {
-      return false;
-    }
-    i += 2;
-    while (IsAlpha(str[i])) {
-      ++i;
-    }
-    if (str[i] != '.' || !IsDigit(str[i + 1])) {
-      return false;
-    }
-    i += 2;
-    while (IsDigit(str[i])) {
-      ++i;
-    }
-  }
-  return true;  // Consumed everything in "str".
-}
-
-static bool EndsWith(State *state, const char chr) {
-  return state->parse_state.out_cur_idx > 0 &&
-         state->parse_state.out_cur_idx < state->out_end_idx &&
-         chr == state->out[state->parse_state.out_cur_idx - 1];
-}
-
-// Append "str" with some tweaks, iff "append" state is true.
-static void MaybeAppendWithLength(State *state, const char *const str,
-                                  const int length) {
-  if (state->parse_state.append && length > 0) {
-    // Append a space if the output buffer ends with '<' and "str"
-    // starts with '<' to avoid <<<.
-    if (str[0] == '<' && EndsWith(state, '<')) {
-      Append(state, " ", 1);
-    }
-    // Remember the last identifier name for ctors/dtors,
-    // but only if we haven't yet overflown the buffer.
-    if (state->parse_state.out_cur_idx < state->out_end_idx &&
-        (IsAlpha(str[0]) || str[0] == '_')) {
-      state->parse_state.prev_name_idx = state->parse_state.out_cur_idx;
-      state->parse_state.prev_name_length = length;
-    }
-    Append(state, str, length);
-  }
-}
-
-// Appends a positive decimal number to the output if appending is enabled.
-static bool MaybeAppendDecimal(State *state, unsigned int val) {
-  // Max {32-64}-bit unsigned int is 20 digits.
-  constexpr size_t kMaxLength = 20;
-  char buf[kMaxLength];
-
-  // We can't use itoa or sprintf as neither is specified to be
-  // async-signal-safe.
-  if (state->parse_state.append) {
-    // We can't have a one-before-the-beginning pointer, so instead start with
-    // one-past-the-end and manipulate one character before the pointer.
-    char *p = &buf[kMaxLength];
-    do {  // val=0 is the only input that should write a leading zero digit.
-      *--p = (val % 10) + '0';
-      val /= 10;
-    } while (p > buf && val != 0);
-
-    // 'p' landed on the last character we set.  How convenient.
-    Append(state, p, kMaxLength - (p - buf));
-  }
-
-  return true;
-}
-
-// A convenient wrapper around MaybeAppendWithLength().
-// Returns true so that it can be placed in "if" conditions.
-static bool MaybeAppend(State *state, const char *const str) {
-  if (state->parse_state.append) {
-    int length = StrLen(str);
-    MaybeAppendWithLength(state, str, length);
-  }
-  return true;
-}
-
-// This function is used for handling nested names.
-static bool EnterNestedName(State *state) {
-  state->parse_state.nest_level = 0;
-  return true;
-}
-
-// This function is used for handling nested names.
-static bool LeaveNestedName(State *state, int16_t prev_value) {
-  state->parse_state.nest_level = prev_value;
-  return true;
-}
-
-// Disable the append mode not to print function parameters, etc.
-static bool DisableAppend(State *state) {
-  state->parse_state.append = false;
-  return true;
-}
-
-// Restore the append mode to the previous state.
-static bool RestoreAppend(State *state, bool prev_value) {
-  state->parse_state.append = prev_value;
-  return true;
-}
-
-// Increase the nest level for nested names.
-static void MaybeIncreaseNestLevel(State *state) {
-  if (state->parse_state.nest_level > -1) {
-    ++state->parse_state.nest_level;
-  }
-}
-
-// Appends :: for nested names if necessary.
-static void MaybeAppendSeparator(State *state) {
-  if (state->parse_state.nest_level >= 1) {
-    MaybeAppend(state, "::");
-  }
-}
-
-// Cancel the last separator if necessary.
-static void MaybeCancelLastSeparator(State *state) {
-  if (state->parse_state.nest_level >= 1 && state->parse_state.append &&
-      state->parse_state.out_cur_idx >= 2) {
-    state->parse_state.out_cur_idx -= 2;
-    state->out[state->parse_state.out_cur_idx] = '\0';
-  }
-}
-
-// Returns true if the identifier of the given length pointed to by
-// "mangled_cur" is anonymous namespace.
-static bool IdentifierIsAnonymousNamespace(State *state, int length) {
-  // Returns true if "anon_prefix" is a proper prefix of "mangled_cur".
-  static const char anon_prefix[] = "_GLOBAL__N_";
-  return (length > static_cast<int>(sizeof(anon_prefix) - 1) &&
-          StrPrefix(RemainingInput(state), anon_prefix));
-}
-
-// Forward declarations of our parsing functions.
-static bool ParseMangledName(State *state);
-static bool ParseEncoding(State *state);
-static bool ParseName(State *state);
-static bool ParseUnscopedName(State *state);
-static bool ParseNestedName(State *state);
-static bool ParsePrefix(State *state);
-static bool ParseUnqualifiedName(State *state);
-static bool ParseSourceName(State *state);
-static bool ParseLocalSourceName(State *state);
-static bool ParseUnnamedTypeName(State *state);
-static bool ParseNumber(State *state, int *number_out);
-static bool ParseFloatNumber(State *state);
-static bool ParseSeqId(State *state);
-static bool ParseIdentifier(State *state, int length);
-static bool ParseOperatorName(State *state, int *arity);
-static bool ParseSpecialName(State *state);
-static bool ParseCallOffset(State *state);
-static bool ParseNVOffset(State *state);
-static bool ParseVOffset(State *state);
-static bool ParseCtorDtorName(State *state);
-static bool ParseDecltype(State *state);
-static bool ParseType(State *state);
-static bool ParseCVQualifiers(State *state);
-static bool ParseBuiltinType(State *state);
-static bool ParseFunctionType(State *state);
-static bool ParseBareFunctionType(State *state);
-static bool ParseClassEnumType(State *state);
-static bool ParseArrayType(State *state);
-static bool ParsePointerToMemberType(State *state);
-static bool ParseTemplateParam(State *state);
-static bool ParseTemplateTemplateParam(State *state);
-static bool ParseTemplateArgs(State *state);
-static bool ParseTemplateArg(State *state);
-static bool ParseBaseUnresolvedName(State *state);
-static bool ParseUnresolvedName(State *state);
-static bool ParseExpression(State *state);
-static bool ParseExprPrimary(State *state);
-static bool ParseExprCastValue(State *state);
-static bool ParseLocalName(State *state);
-static bool ParseLocalNameSuffix(State *state);
-static bool ParseDiscriminator(State *state);
-static bool ParseSubstitution(State *state, bool accept_std);
-
-// Implementation note: the following code is a straightforward
-// translation of the Itanium C++ ABI defined in BNF with a couple of
-// exceptions.
-//
-// - Support GNU extensions not defined in the Itanium C++ ABI
-// - <prefix> and <template-prefix> are combined to avoid infinite loop
-// - Reorder patterns to shorten the code
-// - Reorder patterns to give greedier functions precedence
-//   We'll mark "Less greedy than" for these cases in the code
-//
-// Each parsing function changes the parse state and returns true on
-// success, or returns false and doesn't change the parse state (note:
-// the parse-steps counter increases regardless of success or failure).
-// To ensure that the parse state isn't changed in the latter case, we
-// save the original state before we call multiple parsing functions
-// consecutively with &&, and restore it if unsuccessful.  See
-// ParseEncoding() as an example of this convention.  We follow the
-// convention throughout the code.
-//
-// Originally we tried to do demangling without following the full ABI
-// syntax but it turned out we needed to follow the full syntax to
-// parse complicated cases like nested template arguments.  Note that
-// implementing a full-fledged demangler isn't trivial (libiberty's
-// cp-demangle.c has +4300 lines).
-//
-// Note that (foo) in <(foo) ...> is a modifier to be ignored.
-//
-// Reference:
-// - Itanium C++ ABI
-//   <https://mentorembedded.github.io/cxx-abi/abi.html#mangling>
-
-// <mangled-name> ::= _Z <encoding>
-static bool ParseMangledName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  return ParseTwoCharToken(state, "_Z") && ParseEncoding(state);
-}
-
-// <encoding> ::= <(function) name> <bare-function-type>
-//            ::= <(data) name>
-//            ::= <special-name>
-static bool ParseEncoding(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  // Implementing the first two productions together as <name>
-  // [<bare-function-type>] avoids exponential blowup of backtracking.
-  //
-  // Since Optional(...) can't fail, there's no need to copy the state for
-  // backtracking.
-  if (ParseName(state) && Optional(ParseBareFunctionType(state))) {
-    return true;
-  }
-
-  if (ParseSpecialName(state)) {
-    return true;
-  }
-  return false;
-}
-
-// <name> ::= <nested-name>
-//        ::= <unscoped-template-name> <template-args>
-//        ::= <unscoped-name>
-//        ::= <local-name>
-static bool ParseName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (ParseNestedName(state) || ParseLocalName(state)) {
-    return true;
-  }
-
-  // We reorganize the productions to avoid re-parsing unscoped names.
-  // - Inline <unscoped-template-name> productions:
-  //   <name> ::= <substitution> <template-args>
-  //          ::= <unscoped-name> <template-args>
-  //          ::= <unscoped-name>
-  // - Merge the two productions that start with unscoped-name:
-  //   <name> ::= <unscoped-name> [<template-args>]
-
-  ParseState copy = state->parse_state;
-  // "std<...>" isn't a valid name.
-  if (ParseSubstitution(state, /*accept_std=*/false) &&
-      ParseTemplateArgs(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Note there's no need to restore state after this since only the first
-  // subparser can fail.
-  return ParseUnscopedName(state) && Optional(ParseTemplateArgs(state));
-}
-
-// <unscoped-name> ::= <unqualified-name>
-//                 ::= St <unqualified-name>
-static bool ParseUnscopedName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (ParseUnqualifiedName(state)) {
-    return true;
-  }
-
-  ParseState copy = state->parse_state;
-  if (ParseTwoCharToken(state, "St") && MaybeAppend(state, "std::") &&
-      ParseUnqualifiedName(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <ref-qualifer> ::= R // lvalue method reference qualifier
-//                ::= O // rvalue method reference qualifier
-static inline bool ParseRefQualifier(State *state) {
-  return ParseCharClass(state, "OR");
-}
-
-// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix>
-//                   <unqualified-name> E
-//               ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix>
-//                   <template-args> E
-static bool ParseNestedName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'N') && EnterNestedName(state) &&
-      Optional(ParseCVQualifiers(state)) &&
-      Optional(ParseRefQualifier(state)) && ParsePrefix(state) &&
-      LeaveNestedName(state, copy.nest_level) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// This part is tricky.  If we literally translate them to code, we'll
-// end up infinite loop.  Hence we merge them to avoid the case.
-//
-// <prefix> ::= <prefix> <unqualified-name>
-//          ::= <template-prefix> <template-args>
-//          ::= <template-param>
-//          ::= <substitution>
-//          ::= # empty
-// <template-prefix> ::= <prefix> <(template) unqualified-name>
-//                   ::= <template-param>
-//                   ::= <substitution>
-static bool ParsePrefix(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  bool has_something = false;
-  while (true) {
-    MaybeAppendSeparator(state);
-    if (ParseTemplateParam(state) ||
-        ParseSubstitution(state, /*accept_std=*/true) ||
-        ParseUnscopedName(state) ||
-        (ParseOneCharToken(state, 'M') && ParseUnnamedTypeName(state))) {
-      has_something = true;
-      MaybeIncreaseNestLevel(state);
-      continue;
-    }
-    MaybeCancelLastSeparator(state);
-    if (has_something && ParseTemplateArgs(state)) {
-      return ParsePrefix(state);
-    } else {
-      break;
-    }
-  }
-  return true;
-}
-
-// <unqualified-name> ::= <operator-name>
-//                    ::= <ctor-dtor-name>
-//                    ::= <source-name>
-//                    ::= <local-source-name> // GCC extension; see below.
-//                    ::= <unnamed-type-name>
-static bool ParseUnqualifiedName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  return (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) ||
-          ParseSourceName(state) || ParseLocalSourceName(state) ||
-          ParseUnnamedTypeName(state));
-}
-
-// <source-name> ::= <positive length number> <identifier>
-static bool ParseSourceName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  int length = -1;
-  if (ParseNumber(state, &length) && ParseIdentifier(state, length)) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <local-source-name> ::= L <source-name> [<discriminator>]
-//
-// References:
-//   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775
-//   https://gcc.gnu.org/viewcvs?view=rev&revision=124467
-static bool ParseLocalSourceName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'L') && ParseSourceName(state) &&
-      Optional(ParseDiscriminator(state))) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <unnamed-type-name> ::= Ut [<(nonnegative) number>] _
-//                     ::= <closure-type-name>
-// <closure-type-name> ::= Ul <lambda-sig> E [<(nonnegative) number>] _
-// <lambda-sig>        ::= <(parameter) type>+
-static bool ParseUnnamedTypeName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  // Type's 1-based index n is encoded as { "", n == 1; itoa(n-2), otherwise }.
-  // Optionally parse the encoded value into 'which' and add 2 to get the index.
-  int which = -1;
-
-  // Unnamed type local to function or class.
-  if (ParseTwoCharToken(state, "Ut") && Optional(ParseNumber(state, &which)) &&
-      which <= std::numeric_limits<int>::max() - 2 &&  // Don't overflow.
-      ParseOneCharToken(state, '_')) {
-    MaybeAppend(state, "{unnamed type#");
-    MaybeAppendDecimal(state, 2 + which);
-    MaybeAppend(state, "}");
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Closure type.
-  which = -1;
-  if (ParseTwoCharToken(state, "Ul") && DisableAppend(state) &&
-      OneOrMore(ParseType, state) && RestoreAppend(state, copy.append) &&
-      ParseOneCharToken(state, 'E') && Optional(ParseNumber(state, &which)) &&
-      which <= std::numeric_limits<int>::max() - 2 &&  // Don't overflow.
-      ParseOneCharToken(state, '_')) {
-    MaybeAppend(state, "{lambda()#");
-    MaybeAppendDecimal(state, 2 + which);
-    MaybeAppend(state, "}");
-    return true;
-  }
-  state->parse_state = copy;
-
-  return false;
-}
-
-// <number> ::= [n] <non-negative decimal integer>
-// If "number_out" is non-null, then *number_out is set to the value of the
-// parsed number on success.
-static bool ParseNumber(State *state, int *number_out) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  bool negative = false;
-  if (ParseOneCharToken(state, 'n')) {
-    negative = true;
-  }
-  const char *p = RemainingInput(state);
-  uint64_t number = 0;
-  for (; *p != '\0'; ++p) {
-    if (IsDigit(*p)) {
-      number = number * 10 + (*p - '0');
-    } else {
-      break;
-    }
-  }
-  // Apply the sign with uint64_t arithmetic so overflows aren't UB.  Gives
-  // "incorrect" results for out-of-range inputs, but negative values only
-  // appear for literals, which aren't printed.
-  if (negative) {
-    number = ~number + 1;
-  }
-  if (p != RemainingInput(state)) {  // Conversion succeeded.
-    state->parse_state.mangled_idx += p - RemainingInput(state);
-    if (number_out != nullptr) {
-      // Note: possibly truncate "number".
-      *number_out = number;
-    }
-    return true;
-  }
-  return false;
-}
-
-// Floating-point literals are encoded using a fixed-length lowercase
-// hexadecimal string.
-static bool ParseFloatNumber(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  const char *p = RemainingInput(state);
-  for (; *p != '\0'; ++p) {
-    if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) {
-      break;
-    }
-  }
-  if (p != RemainingInput(state)) {  // Conversion succeeded.
-    state->parse_state.mangled_idx += p - RemainingInput(state);
-    return true;
-  }
-  return false;
-}
-
-// The <seq-id> is a sequence number in base 36,
-// using digits and upper case letters
-static bool ParseSeqId(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  const char *p = RemainingInput(state);
-  for (; *p != '\0'; ++p) {
-    if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) {
-      break;
-    }
-  }
-  if (p != RemainingInput(state)) {  // Conversion succeeded.
-    state->parse_state.mangled_idx += p - RemainingInput(state);
-    return true;
-  }
-  return false;
-}
-
-// <identifier> ::= <unqualified source code identifier> (of given length)
-static bool ParseIdentifier(State *state, int length) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (length < 0 || !AtLeastNumCharsRemaining(RemainingInput(state), length)) {
-    return false;
-  }
-  if (IdentifierIsAnonymousNamespace(state, length)) {
-    MaybeAppend(state, "(anonymous namespace)");
-  } else {
-    MaybeAppendWithLength(state, RemainingInput(state), length);
-  }
-  state->parse_state.mangled_idx += length;
-  return true;
-}
-
-// <operator-name> ::= nw, and other two letters cases
-//                 ::= cv <type>  # (cast)
-//                 ::= v  <digit> <source-name> # vendor extended operator
-static bool ParseOperatorName(State *state, int *arity) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (!AtLeastNumCharsRemaining(RemainingInput(state), 2)) {
-    return false;
-  }
-  // First check with "cv" (cast) case.
-  ParseState copy = state->parse_state;
-  if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") &&
-      EnterNestedName(state) && ParseType(state) &&
-      LeaveNestedName(state, copy.nest_level)) {
-    if (arity != nullptr) {
-      *arity = 1;
-    }
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Then vendor extended operators.
-  if (ParseOneCharToken(state, 'v') && ParseDigit(state, arity) &&
-      ParseSourceName(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Other operator names should start with a lower alphabet followed
-  // by a lower/upper alphabet.
-  if (!(IsLower(RemainingInput(state)[0]) &&
-        IsAlpha(RemainingInput(state)[1]))) {
-    return false;
-  }
-  // We may want to perform a binary search if we really need speed.
-  const AbbrevPair *p;
-  for (p = kOperatorList; p->abbrev != nullptr; ++p) {
-    if (RemainingInput(state)[0] == p->abbrev[0] &&
-        RemainingInput(state)[1] == p->abbrev[1]) {
-      if (arity != nullptr) {
-        *arity = p->arity;
-      }
-      MaybeAppend(state, "operator");
-      if (IsLower(*p->real_name)) {  // new, delete, etc.
-        MaybeAppend(state, " ");
-      }
-      MaybeAppend(state, p->real_name);
-      state->parse_state.mangled_idx += 2;
-      return true;
-    }
-  }
-  return false;
-}
-
-// <special-name> ::= TV <type>
-//                ::= TT <type>
-//                ::= TI <type>
-//                ::= TS <type>
-//                ::= TH <type>  # thread-local
-//                ::= Tc <call-offset> <call-offset> <(base) encoding>
-//                ::= GV <(object) name>
-//                ::= T <call-offset> <(base) encoding>
-// G++ extensions:
-//                ::= TC <type> <(offset) number> _ <(base) type>
-//                ::= TF <type>
-//                ::= TJ <type>
-//                ::= GR <name>
-//                ::= GA <encoding>
-//                ::= Th <call-offset> <(base) encoding>
-//                ::= Tv <call-offset> <(base) encoding>
-//
-// Note: we don't care much about them since they don't appear in
-// stack traces.  The are special data.
-static bool ParseSpecialName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") &&
-      ParseType(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) &&
-      ParseCallOffset(state) && ParseEncoding(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseTwoCharToken(state, "GV") && ParseName(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) &&
-      ParseEncoding(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // G++ extensions
-  if (ParseTwoCharToken(state, "TC") && ParseType(state) &&
-      ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') &&
-      DisableAppend(state) && ParseType(state)) {
-    RestoreAppend(state, copy.append);
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") &&
-      ParseType(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseTwoCharToken(state, "GR") && ParseName(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") &&
-      ParseCallOffset(state) && ParseEncoding(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <call-offset> ::= h <nv-offset> _
-//               ::= v <v-offset> _
-static bool ParseCallOffset(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'h') && ParseNVOffset(state) &&
-      ParseOneCharToken(state, '_')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseOneCharToken(state, 'v') && ParseVOffset(state) &&
-      ParseOneCharToken(state, '_')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  return false;
-}
-
-// <nv-offset> ::= <(offset) number>
-static bool ParseNVOffset(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  return ParseNumber(state, nullptr);
-}
-
-// <v-offset>  ::= <(offset) number> _ <(virtual offset) number>
-static bool ParseVOffset(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') &&
-      ParseNumber(state, nullptr)) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <ctor-dtor-name> ::= C1 | C2 | C3 | CI1 <base-class-type> | CI2
-// <base-class-type>
-//                  ::= D0 | D1 | D2
-// # GCC extensions: "unified" constructor/destructor.  See
-// #
-// https://github.com/gcc-mirror/gcc/blob/7ad17b583c3643bd4557f29b8391ca7ef08391f5/gcc/cp/mangle.c#L1847
-//                  ::= C4 | D4
-static bool ParseCtorDtorName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'C')) {
-    if (ParseCharClass(state, "1234")) {
-      const char *const prev_name =
-          state->out + state->parse_state.prev_name_idx;
-      MaybeAppendWithLength(state, prev_name,
-                            state->parse_state.prev_name_length);
-      return true;
-    } else if (ParseOneCharToken(state, 'I') && ParseCharClass(state, "12") &&
-               ParseClassEnumType(state)) {
-      return true;
-    }
-  }
-  state->parse_state = copy;
-
-  if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "0124")) {
-    const char *const prev_name = state->out + state->parse_state.prev_name_idx;
-    MaybeAppend(state, "~");
-    MaybeAppendWithLength(state, prev_name,
-                          state->parse_state.prev_name_length);
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <decltype> ::= Dt <expression> E  # decltype of an id-expression or class
-//                                   # member access (C++0x)
-//            ::= DT <expression> E  # decltype of an expression (C++0x)
-static bool ParseDecltype(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") &&
-      ParseExpression(state) && ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  return false;
-}
-
-// <type> ::= <CV-qualifiers> <type>
-//        ::= P <type>   # pointer-to
-//        ::= R <type>   # reference-to
-//        ::= O <type>   # rvalue reference-to (C++0x)
-//        ::= C <type>   # complex pair (C 2000)
-//        ::= G <type>   # imaginary (C 2000)
-//        ::= U <source-name> <type>  # vendor extended type qualifier
-//        ::= <builtin-type>
-//        ::= <function-type>
-//        ::= <class-enum-type>  # note: just an alias for <name>
-//        ::= <array-type>
-//        ::= <pointer-to-member-type>
-//        ::= <template-template-param> <template-args>
-//        ::= <template-param>
-//        ::= <decltype>
-//        ::= <substitution>
-//        ::= Dp <type>          # pack expansion of (C++0x)
-//        ::= Dv <num-elems> _   # GNU vector extension
-//
-static bool ParseType(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-
-  // We should check CV-qualifers, and PRGC things first.
-  //
-  // CV-qualifiers overlap with some operator names, but an operator name is not
-  // valid as a type.  To avoid an ambiguity that can lead to exponential time
-  // complexity, refuse to backtrack the CV-qualifiers.
-  //
-  // _Z4aoeuIrMvvE
-  //  => _Z 4aoeuI        rM  v     v   E
-  //         aoeu<operator%=, void, void>
-  //  => _Z 4aoeuI r Mv v              E
-  //         aoeu<void void::* restrict>
-  //
-  // By consuming the CV-qualifiers first, the former parse is disabled.
-  if (ParseCVQualifiers(state)) {
-    const bool result = ParseType(state);
-    if (!result) state->parse_state = copy;
-    return result;
-  }
-  state->parse_state = copy;
-
-  // Similarly, these tag characters can overlap with other <name>s resulting in
-  // two different parse prefixes that land on <template-args> in the same
-  // place, such as "C3r1xI...".  So, disable the "ctor-name = C3" parse by
-  // refusing to backtrack the tag characters.
-  if (ParseCharClass(state, "OPRCG")) {
-    const bool result = ParseType(state);
-    if (!result) state->parse_state = copy;
-    return result;
-  }
-  state->parse_state = copy;
-
-  if (ParseTwoCharToken(state, "Dp") && ParseType(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseOneCharToken(state, 'U') && ParseSourceName(state) &&
-      ParseType(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseBuiltinType(state) || ParseFunctionType(state) ||
-      ParseClassEnumType(state) || ParseArrayType(state) ||
-      ParsePointerToMemberType(state) || ParseDecltype(state) ||
-      // "std" on its own isn't a type.
-      ParseSubstitution(state, /*accept_std=*/false)) {
-    return true;
-  }
-
-  if (ParseTemplateTemplateParam(state) && ParseTemplateArgs(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Less greedy than <template-template-param> <template-args>.
-  if (ParseTemplateParam(state)) {
-    return true;
-  }
-
-  if (ParseTwoCharToken(state, "Dv") && ParseNumber(state, nullptr) &&
-      ParseOneCharToken(state, '_')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  return false;
-}
-
-// <CV-qualifiers> ::= [r] [V] [K]
-// We don't allow empty <CV-qualifiers> to avoid infinite loop in
-// ParseType().
-static bool ParseCVQualifiers(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  int num_cv_qualifiers = 0;
-  num_cv_qualifiers += ParseOneCharToken(state, 'r');
-  num_cv_qualifiers += ParseOneCharToken(state, 'V');
-  num_cv_qualifiers += ParseOneCharToken(state, 'K');
-  return num_cv_qualifiers > 0;
-}
-
-// <builtin-type> ::= v, etc.  # single-character builtin types
-//                ::= u <source-name>
-//                ::= Dd, etc.  # two-character builtin types
-//
-// Not supported:
-//                ::= DF <number> _ # _FloatN (N bits)
-//
-static bool ParseBuiltinType(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  const AbbrevPair *p;
-  for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) {
-    // Guaranteed only 1- or 2-character strings in kBuiltinTypeList.
-    if (p->abbrev[1] == '\0') {
-      if (ParseOneCharToken(state, p->abbrev[0])) {
-        MaybeAppend(state, p->real_name);
-        return true;
-      }
-    } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) {
-      MaybeAppend(state, p->real_name);
-      return true;
-    }
-  }
-
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-//  <exception-spec> ::= Do                # non-throwing
-//                                           exception-specification (e.g.,
-//                                           noexcept, throw())
-//                   ::= DO <expression> E # computed (instantiation-dependent)
-//                                           noexcept
-//                   ::= Dw <type>+ E      # dynamic exception specification
-//                                           with instantiation-dependent types
-static bool ParseExceptionSpec(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-
-  if (ParseTwoCharToken(state, "Do")) return true;
-
-  ParseState copy = state->parse_state;
-  if (ParseTwoCharToken(state, "DO") && ParseExpression(state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-  if (ParseTwoCharToken(state, "Dw") && OneOrMore(ParseType, state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  return false;
-}
-
-// <function-type> ::= [exception-spec] F [Y] <bare-function-type> [O] E
-static bool ParseFunctionType(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') &&
-      Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) &&
-      Optional(ParseOneCharToken(state, 'O')) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <bare-function-type> ::= <(signature) type>+
-static bool ParseBareFunctionType(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  DisableAppend(state);
-  if (OneOrMore(ParseType, state)) {
-    RestoreAppend(state, copy.append);
-    MaybeAppend(state, "()");
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <class-enum-type> ::= <name>
-static bool ParseClassEnumType(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  return ParseName(state);
-}
-
-// <array-type> ::= A <(positive dimension) number> _ <(element) type>
-//              ::= A [<(dimension) expression>] _ <(element) type>
-static bool ParseArrayType(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'A') && ParseNumber(state, nullptr) &&
-      ParseOneCharToken(state, '_') && ParseType(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) &&
-      ParseOneCharToken(state, '_') && ParseType(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <pointer-to-member-type> ::= M <(class) type> <(member) type>
-static bool ParsePointerToMemberType(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <template-param> ::= T_
-//                  ::= T <parameter-2 non-negative number> _
-static bool ParseTemplateParam(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (ParseTwoCharToken(state, "T_")) {
-    MaybeAppend(state, "?");  // We don't support template substitutions.
-    return true;
-  }
-
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'T') && ParseNumber(state, nullptr) &&
-      ParseOneCharToken(state, '_')) {
-    MaybeAppend(state, "?");  // We don't support template substitutions.
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <template-template-param> ::= <template-param>
-//                           ::= <substitution>
-static bool ParseTemplateTemplateParam(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  return (ParseTemplateParam(state) ||
-          // "std" on its own isn't a template.
-          ParseSubstitution(state, /*accept_std=*/false));
-}
-
-// <template-args> ::= I <template-arg>+ E
-static bool ParseTemplateArgs(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  DisableAppend(state);
-  if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) &&
-      ParseOneCharToken(state, 'E')) {
-    RestoreAppend(state, copy.append);
-    MaybeAppend(state, "<>");
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <template-arg>  ::= <type>
-//                 ::= <expr-primary>
-//                 ::= J <template-arg>* E        # argument pack
-//                 ::= X <expression> E
-static bool ParseTemplateArg(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'J') && ZeroOrMore(ParseTemplateArg, state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // There can be significant overlap between the following leading to
-  // exponential backtracking:
-  //
-  //   <expr-primary> ::= L <type> <expr-cast-value> E
-  //                 e.g. L 2xxIvE 1                 E
-  //   <type>         ==> <local-source-name> <template-args>
-  //                 e.g. L 2xx               IvE
-  //
-  // This means parsing an entire <type> twice, and <type> can contain
-  // <template-arg>, so this can generate exponential backtracking.  There is
-  // only overlap when the remaining input starts with "L <source-name>", so
-  // parse all cases that can start this way jointly to share the common prefix.
-  //
-  // We have:
-  //
-  //   <template-arg> ::= <type>
-  //                  ::= <expr-primary>
-  //
-  // First, drop all the productions of <type> that must start with something
-  // other than 'L'.  All that's left is <class-enum-type>; inline it.
-  //
-  //   <type> ::= <nested-name> # starts with 'N'
-  //          ::= <unscoped-name>
-  //          ::= <unscoped-template-name> <template-args>
-  //          ::= <local-name> # starts with 'Z'
-  //
-  // Drop and inline again:
-  //
-  //   <type> ::= <unscoped-name>
-  //          ::= <unscoped-name> <template-args>
-  //          ::= <substitution> <template-args> # starts with 'S'
-  //
-  // Merge the first two, inline <unscoped-name>, drop last:
-  //
-  //   <type> ::= <unqualified-name> [<template-args>]
-  //          ::= St <unqualified-name> [<template-args>] # starts with 'S'
-  //
-  // Drop and inline:
-  //
-  //   <type> ::= <operator-name> [<template-args>] # starts with lowercase
-  //          ::= <ctor-dtor-name> [<template-args>] # starts with 'C' or 'D'
-  //          ::= <source-name> [<template-args>] # starts with digit
-  //          ::= <local-source-name> [<template-args>]
-  //          ::= <unnamed-type-name> [<template-args>] # starts with 'U'
-  //
-  // One more time:
-  //
-  //   <type> ::= L <source-name> [<template-args>]
-  //
-  // Likewise with <expr-primary>:
-  //
-  //   <expr-primary> ::= L <type> <expr-cast-value> E
-  //                  ::= LZ <encoding> E # cannot overlap; drop
-  //                  ::= L <mangled_name> E # cannot overlap; drop
-  //
-  // By similar reasoning as shown above, the only <type>s starting with
-  // <source-name> are "<source-name> [<template-args>]".  Inline this.
-  //
-  //   <expr-primary> ::= L <source-name> [<template-args>] <expr-cast-value> E
-  //
-  // Now inline both of these into <template-arg>:
-  //
-  //   <template-arg> ::= L <source-name> [<template-args>]
-  //                  ::= L <source-name> [<template-args>] <expr-cast-value> E
-  //
-  // Merge them and we're done:
-  //   <template-arg>
-  //     ::= L <source-name> [<template-args>] [<expr-cast-value> E]
-  if (ParseLocalSourceName(state) && Optional(ParseTemplateArgs(state))) {
-    copy = state->parse_state;
-    if (ParseExprCastValue(state) && ParseOneCharToken(state, 'E')) {
-      return true;
-    }
-    state->parse_state = copy;
-    return true;
-  }
-
-  // Now that the overlapping cases can't reach this code, we can safely call
-  // both of these.
-  if (ParseType(state) || ParseExprPrimary(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseOneCharToken(state, 'X') && ParseExpression(state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <unresolved-type> ::= <template-param> [<template-args>]
-//                   ::= <decltype>
-//                   ::= <substitution>
-static inline bool ParseUnresolvedType(State *state) {
-  // No ComplexityGuard because we don't copy the state in this stack frame.
-  return (ParseTemplateParam(state) && Optional(ParseTemplateArgs(state))) ||
-         ParseDecltype(state) || ParseSubstitution(state, /*accept_std=*/false);
-}
-
-// <simple-id> ::= <source-name> [<template-args>]
-static inline bool ParseSimpleId(State *state) {
-  // No ComplexityGuard because we don't copy the state in this stack frame.
-
-  // Note: <simple-id> cannot be followed by a parameter pack; see comment in
-  // ParseUnresolvedType.
-  return ParseSourceName(state) && Optional(ParseTemplateArgs(state));
-}
-
-// <base-unresolved-name> ::= <source-name> [<template-args>]
-//                        ::= on <operator-name> [<template-args>]
-//                        ::= dn <destructor-name>
-static bool ParseBaseUnresolvedName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-
-  if (ParseSimpleId(state)) {
-    return true;
-  }
-
-  ParseState copy = state->parse_state;
-  if (ParseTwoCharToken(state, "on") && ParseOperatorName(state, nullptr) &&
-      Optional(ParseTemplateArgs(state))) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseTwoCharToken(state, "dn") &&
-      (ParseUnresolvedType(state) || ParseSimpleId(state))) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  return false;
-}
-
-// <unresolved-name> ::= [gs] <base-unresolved-name>
-//                   ::= sr <unresolved-type> <base-unresolved-name>
-//                   ::= srN <unresolved-type> <unresolved-qualifier-level>+ E
-//                         <base-unresolved-name>
-//                   ::= [gs] sr <unresolved-qualifier-level>+ E
-//                         <base-unresolved-name>
-static bool ParseUnresolvedName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-
-  ParseState copy = state->parse_state;
-  if (Optional(ParseTwoCharToken(state, "gs")) &&
-      ParseBaseUnresolvedName(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseTwoCharToken(state, "sr") && ParseUnresolvedType(state) &&
-      ParseBaseUnresolvedName(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseTwoCharToken(state, "sr") && ParseOneCharToken(state, 'N') &&
-      ParseUnresolvedType(state) &&
-      OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) &&
-      ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (Optional(ParseTwoCharToken(state, "gs")) &&
-      ParseTwoCharToken(state, "sr") &&
-      OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) &&
-      ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  return false;
-}
-
-// <expression> ::= <1-ary operator-name> <expression>
-//              ::= <2-ary operator-name> <expression> <expression>
-//              ::= <3-ary operator-name> <expression> <expression> <expression>
-//              ::= cl <expression>+ E
-//              ::= cv <type> <expression>      # type (expression)
-//              ::= cv <type> _ <expression>* E # type (expr-list)
-//              ::= st <type>
-//              ::= <template-param>
-//              ::= <function-param>
-//              ::= <expr-primary>
-//              ::= dt <expression> <unresolved-name> # expr.name
-//              ::= pt <expression> <unresolved-name> # expr->name
-//              ::= sp <expression>         # argument pack expansion
-//              ::= sr <type> <unqualified-name> <template-args>
-//              ::= sr <type> <unqualified-name>
-// <function-param> ::= fp <(top-level) CV-qualifiers> _
-//                  ::= fp <(top-level) CV-qualifiers> <number> _
-//                  ::= fL <number> p <(top-level) CV-qualifiers> _
-//                  ::= fL <number> p <(top-level) CV-qualifiers> <number> _
-static bool ParseExpression(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (ParseTemplateParam(state) || ParseExprPrimary(state)) {
-    return true;
-  }
-
-  // Object/function call expression.
-  ParseState copy = state->parse_state;
-  if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Function-param expression (level 0).
-  if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) &&
-      Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Function-param expression (level 1+).
-  if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) &&
-      ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) &&
-      Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Parse the conversion expressions jointly to avoid re-parsing the <type> in
-  // their common prefix.  Parsed as:
-  // <expression> ::= cv <type> <conversion-args>
-  // <conversion-args> ::= _ <expression>* E
-  //                   ::= <expression>
-  //
-  // Also don't try ParseOperatorName after seeing "cv", since ParseOperatorName
-  // also needs to accept "cv <type>" in other contexts.
-  if (ParseTwoCharToken(state, "cv")) {
-    if (ParseType(state)) {
-      ParseState copy2 = state->parse_state;
-      if (ParseOneCharToken(state, '_') && ZeroOrMore(ParseExpression, state) &&
-          ParseOneCharToken(state, 'E')) {
-        return true;
-      }
-      state->parse_state = copy2;
-      if (ParseExpression(state)) {
-        return true;
-      }
-    }
-  } else {
-    // Parse unary, binary, and ternary operator expressions jointly, taking
-    // care not to re-parse subexpressions repeatedly. Parse like:
-    //   <expression> ::= <operator-name> <expression>
-    //                    [<one-to-two-expressions>]
-    //   <one-to-two-expressions> ::= <expression> [<expression>]
-    int arity = -1;
-    if (ParseOperatorName(state, &arity) &&
-        arity > 0 &&  // 0 arity => disabled.
-        (arity < 3 || ParseExpression(state)) &&
-        (arity < 2 || ParseExpression(state)) &&
-        (arity < 1 || ParseExpression(state))) {
-      return true;
-    }
-  }
-  state->parse_state = copy;
-
-  // sizeof type
-  if (ParseTwoCharToken(state, "st") && ParseType(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Object and pointer member access expressions.
-  if ((ParseTwoCharToken(state, "dt") || ParseTwoCharToken(state, "pt")) &&
-      ParseExpression(state) && ParseType(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Pointer-to-member access expressions.  This parses the same as a binary
-  // operator, but it's implemented separately because "ds" shouldn't be
-  // accepted in other contexts that parse an operator name.
-  if (ParseTwoCharToken(state, "ds") && ParseExpression(state) &&
-      ParseExpression(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Parameter pack expansion
-  if (ParseTwoCharToken(state, "sp") && ParseExpression(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  return ParseUnresolvedName(state);
-}
-
-// <expr-primary> ::= L <type> <(value) number> E
-//                ::= L <type> <(value) float> E
-//                ::= L <mangled-name> E
-//                // A bug in g++'s C++ ABI version 2 (-fabi-version=2).
-//                ::= LZ <encoding> E
-//
-// Warning, subtle: the "bug" LZ production above is ambiguous with the first
-// production where <type> starts with <local-name>, which can lead to
-// exponential backtracking in two scenarios:
-//
-// - When whatever follows the E in the <local-name> in the first production is
-//   not a name, we backtrack the whole <encoding> and re-parse the whole thing.
-//
-// - When whatever follows the <local-name> in the first production is not a
-//   number and this <expr-primary> may be followed by a name, we backtrack the
-//   <name> and re-parse it.
-//
-// Moreover this ambiguity isn't always resolved -- for example, the following
-// has two different parses:
-//
-//   _ZaaILZ4aoeuE1x1EvE
-//   => operator&&<aoeu, x, E, void>
-//   => operator&&<(aoeu::x)(1), void>
-//
-// To resolve this, we just do what GCC's demangler does, and refuse to parse
-// casts to <local-name> types.
-static bool ParseExprPrimary(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-
-  // The "LZ" special case: if we see LZ, we commit to accept "LZ <encoding> E"
-  // or fail, no backtracking.
-  if (ParseTwoCharToken(state, "LZ")) {
-    if (ParseEncoding(state) && ParseOneCharToken(state, 'E')) {
-      return true;
-    }
-
-    state->parse_state = copy;
-    return false;
-  }
-
-  // The merged cast production.
-  if (ParseOneCharToken(state, 'L') && ParseType(state) &&
-      ParseExprCastValue(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseOneCharToken(state, 'L') && ParseMangledName(state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  return false;
-}
-
-// <number> or <float>, followed by 'E', as described above ParseExprPrimary.
-static bool ParseExprCastValue(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  // We have to be able to backtrack after accepting a number because we could
-  // have e.g. "7fffE", which will accept "7" as a number but then fail to find
-  // the 'E'.
-  ParseState copy = state->parse_state;
-  if (ParseNumber(state, nullptr) && ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  if (ParseFloatNumber(state) && ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  state->parse_state = copy;
-
-  return false;
-}
-
-// <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>]
-//              ::= Z <(function) encoding> E s [<discriminator>]
-//
-// Parsing a common prefix of these two productions together avoids an
-// exponential blowup of backtracking.  Parse like:
-//   <local-name> := Z <encoding> E <local-name-suffix>
-//   <local-name-suffix> ::= s [<discriminator>]
-//                       ::= <name> [<discriminator>]
-
-static bool ParseLocalNameSuffix(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-
-  if (MaybeAppend(state, "::") && ParseName(state) &&
-      Optional(ParseDiscriminator(state))) {
-    return true;
-  }
-
-  // Since we're not going to overwrite the above "::" by re-parsing the
-  // <encoding> (whose trailing '\0' byte was in the byte now holding the
-  // first ':'), we have to rollback the "::" if the <name> parse failed.
-  if (state->parse_state.append) {
-    state->out[state->parse_state.out_cur_idx - 2] = '\0';
-  }
-
-  return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state));
-}
-
-static bool ParseLocalName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) &&
-      ParseOneCharToken(state, 'E') && ParseLocalNameSuffix(state)) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <discriminator> := _ <(non-negative) number>
-static bool ParseDiscriminator(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) {
-    return true;
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// <substitution> ::= S_
-//                ::= S <seq-id> _
-//                ::= St, etc.
-//
-// "St" is special in that it's not valid as a standalone name, and it *is*
-// allowed to precede a name without being wrapped in "N...E".  This means that
-// if we accept it on its own, we can accept "St1a" and try to parse
-// template-args, then fail and backtrack, accept "St" on its own, then "1a" as
-// an unqualified name and re-parse the same template-args.  To block this
-// exponential backtracking, we disable it with 'accept_std=false' in
-// problematic contexts.
-static bool ParseSubstitution(State *state, bool accept_std) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (ParseTwoCharToken(state, "S_")) {
-    MaybeAppend(state, "?");  // We don't support substitutions.
-    return true;
-  }
-
-  ParseState copy = state->parse_state;
-  if (ParseOneCharToken(state, 'S') && ParseSeqId(state) &&
-      ParseOneCharToken(state, '_')) {
-    MaybeAppend(state, "?");  // We don't support substitutions.
-    return true;
-  }
-  state->parse_state = copy;
-
-  // Expand abbreviations like "St" => "std".
-  if (ParseOneCharToken(state, 'S')) {
-    const AbbrevPair *p;
-    for (p = kSubstitutionList; p->abbrev != nullptr; ++p) {
-      if (RemainingInput(state)[0] == p->abbrev[1] &&
-          (accept_std || p->abbrev[1] != 't')) {
-        MaybeAppend(state, "std");
-        if (p->real_name[0] != '\0') {
-          MaybeAppend(state, "::");
-          MaybeAppend(state, p->real_name);
-        }
-        ++state->parse_state.mangled_idx;
-        return true;
-      }
-    }
-  }
-  state->parse_state = copy;
-  return false;
-}
-
-// Parse <mangled-name>, optionally followed by either a function-clone suffix
-// or version suffix.  Returns true only if all of "mangled_cur" was consumed.
-static bool ParseTopLevelMangledName(State *state) {
-  ComplexityGuard guard(state);
-  if (guard.IsTooComplex()) return false;
-  if (ParseMangledName(state)) {
-    if (RemainingInput(state)[0] != '\0') {
-      // Drop trailing function clone suffix, if any.
-      if (IsFunctionCloneSuffix(RemainingInput(state))) {
-        return true;
-      }
-      // Append trailing version suffix if any.
-      // ex. _Z3foo@@GLIBCXX_3.4
-      if (RemainingInput(state)[0] == '@') {
-        MaybeAppend(state, RemainingInput(state));
-        return true;
-      }
-      return false;  // Unconsumed suffix.
-    }
-    return true;
-  }
-  return false;
-}
-
-static bool Overflowed(const State *state) {
-  return state->parse_state.out_cur_idx >= state->out_end_idx;
-}
-
-// The demangler entry point.
-bool Demangle(const char *mangled, char *out, int out_size) {
-  State state;
-  InitState(&state, mangled, out, out_size);
-  return ParseTopLevelMangledName(&state) && !Overflowed(&state) &&
-         state.parse_state.out_cur_idx > 0;
-}
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/debugging/internal/demangle.h b/third_party/abseil_cpp/absl/debugging/internal/demangle.h
deleted file mode 100644
index c314d9bc23..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/demangle.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// An async-signal-safe and thread-safe demangler for Itanium C++ ABI
-// (aka G++ V3 ABI).
-//
-// The demangler is implemented to be used in async signal handlers to
-// symbolize stack traces.  We cannot use libstdc++'s
-// abi::__cxa_demangle() in such signal handlers since it's not async
-// signal safe (it uses malloc() internally).
-//
-// Note that this demangler doesn't support full demangling.  More
-// specifically, it doesn't print types of function parameters and
-// types of template arguments.  It just skips them.  However, it's
-// still very useful to extract basic information such as class,
-// function, constructor, destructor, and operator names.
-//
-// See the implementation note in demangle.cc if you are interested.
-//
-// Example:
-//
-// | Mangled Name  | The Demangler | abi::__cxa_demangle()
-// |---------------|---------------|-----------------------
-// | _Z1fv         | f()           | f()
-// | _Z1fi         | f()           | f(int)
-// | _Z3foo3bar    | foo()         | foo(bar)
-// | _Z1fIiEvi     | f<>()         | void f<int>(int)
-// | _ZN1N1fE      | N::f          | N::f
-// | _ZN3Foo3BarEv | Foo::Bar()    | Foo::Bar()
-// | _Zrm1XS_"     | operator%()   | operator%(X, X)
-// | _ZN3FooC1Ev   | Foo::Foo()    | Foo::Foo()
-// | _Z1fSs        | f()           | f(std::basic_string<char,
-// |               |               |   std::char_traits<char>,
-// |               |               |   std::allocator<char> >)
-//
-// See the unit test for more examples.
-//
-// Note: we might want to write demanglers for ABIs other than Itanium
-// C++ ABI in the future.
-//
-
-#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
-#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Demangle `mangled`.  On success, return true and write the
-// demangled symbol name to `out`.  Otherwise, return false.
-// `out` is modified even if demangling is unsuccessful.
-bool Demangle(const char *mangled, char *out, int out_size);
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/demangle_test.cc b/third_party/abseil_cpp/absl/debugging/internal/demangle_test.cc
deleted file mode 100644
index 0bed7359d8..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/demangle_test.cc
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/debugging/internal/demangle.h"
-
-#include <cstdlib>
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/debugging/internal/stack_consumption.h"
-#include "absl/memory/memory.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-namespace {
-
-// A wrapper function for Demangle() to make the unit test simple.
-static const char *DemangleIt(const char * const mangled) {
-  static char demangled[4096];
-  if (Demangle(mangled, demangled, sizeof(demangled))) {
-    return demangled;
-  } else {
-    return mangled;
-  }
-}
-
-// Test corner cases of bounary conditions.
-TEST(Demangle, CornerCases) {
-  char tmp[10];
-  EXPECT_TRUE(Demangle("_Z6foobarv", tmp, sizeof(tmp)));
-  // sizeof("foobar()") == 9
-  EXPECT_STREQ("foobar()", tmp);
-  EXPECT_TRUE(Demangle("_Z6foobarv", tmp, 9));
-  EXPECT_STREQ("foobar()", tmp);
-  EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 8));  // Not enough.
-  EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 1));
-  EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 0));
-  EXPECT_FALSE(Demangle("_Z6foobarv", nullptr, 0));  // Should not cause SEGV.
-  EXPECT_FALSE(Demangle("_Z1000000", tmp, 9));
-}
-
-// Test handling of functions suffixed with .clone.N, which is used
-// by GCC 4.5.x (and our locally-modified version of GCC 4.4.x), and
-// .constprop.N and .isra.N, which are used by GCC 4.6.x.  These
-// suffixes are used to indicate functions which have been cloned
-// during optimization.  We ignore these suffixes.
-TEST(Demangle, Clones) {
-  char tmp[20];
-  EXPECT_TRUE(Demangle("_ZL3Foov", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  EXPECT_TRUE(Demangle("_ZL3Foov.clone.3", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  EXPECT_TRUE(Demangle("_ZL3Foov.constprop.80", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  EXPECT_TRUE(Demangle("_ZL3Foov.isra.18", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  EXPECT_TRUE(Demangle("_ZL3Foov.isra.2.constprop.18", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  // Invalid (truncated), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.clo", tmp, sizeof(tmp)));
-  // Invalid (.clone. not followed by number), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.clone.", tmp, sizeof(tmp)));
-  // Invalid (.clone. followed by non-number), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.clone.foo", tmp, sizeof(tmp)));
-  // Invalid (.constprop. not followed by number), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.isra.2.constprop.", tmp, sizeof(tmp)));
-}
-
-// Tests that verify that Demangle footprint is within some limit.
-// They are not to be run under sanitizers as the sanitizers increase
-// stack consumption by about 4x.
-#if defined(ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION) && \
-    !defined(ABSL_HAVE_ADDRESS_SANITIZER) &&                   \
-    !defined(ABSL_HAVE_MEMORY_SANITIZER) &&                    \
-    !defined(ABSL_HAVE_THREAD_SANITIZER)
-
-static const char *g_mangled;
-static char g_demangle_buffer[4096];
-static char *g_demangle_result;
-
-static void DemangleSignalHandler(int signo) {
-  if (Demangle(g_mangled, g_demangle_buffer, sizeof(g_demangle_buffer))) {
-    g_demangle_result = g_demangle_buffer;
-  } else {
-    g_demangle_result = nullptr;
-  }
-}
-
-// Call Demangle and figure out the stack footprint of this call.
-static const char *DemangleStackConsumption(const char *mangled,
-                                            int *stack_consumed) {
-  g_mangled = mangled;
-  *stack_consumed = GetSignalHandlerStackConsumption(DemangleSignalHandler);
-  ABSL_RAW_LOG(INFO, "Stack consumption of Demangle: %d", *stack_consumed);
-  return g_demangle_result;
-}
-
-// Demangle stack consumption should be within 8kB for simple mangled names
-// with some level of nesting. With alternate signal stack we have 64K,
-// but some signal handlers run on thread stack, and could have arbitrarily
-// little space left (so we don't want to make this number too large).
-const int kStackConsumptionUpperLimit = 8192;
-
-// Returns a mangled name nested to the given depth.
-static std::string NestedMangledName(int depth) {
-  std::string mangled_name = "_Z1a";
-  if (depth > 0) {
-    mangled_name += "IXL";
-    mangled_name += NestedMangledName(depth - 1);
-    mangled_name += "EEE";
-  }
-  return mangled_name;
-}
-
-TEST(Demangle, DemangleStackConsumption) {
-  // Measure stack consumption of Demangle for nested mangled names of varying
-  // depth.  Since Demangle is implemented as a recursive descent parser,
-  // stack consumption will grow as the nesting depth increases.  By measuring
-  // the stack consumption for increasing depths, we can see the growing
-  // impact of any stack-saving changes made to the code for Demangle.
-  int stack_consumed = 0;
-
-  const char *demangled =
-      DemangleStackConsumption("_Z6foobarv", &stack_consumed);
-  EXPECT_STREQ("foobar()", demangled);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
-
-  const std::string nested_mangled_name0 = NestedMangledName(0);
-  demangled = DemangleStackConsumption(nested_mangled_name0.c_str(),
-                                       &stack_consumed);
-  EXPECT_STREQ("a", demangled);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
-
-  const std::string nested_mangled_name1 = NestedMangledName(1);
-  demangled = DemangleStackConsumption(nested_mangled_name1.c_str(),
-                                       &stack_consumed);
-  EXPECT_STREQ("a<>", demangled);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
-
-  const std::string nested_mangled_name2 = NestedMangledName(2);
-  demangled = DemangleStackConsumption(nested_mangled_name2.c_str(),
-                                       &stack_consumed);
-  EXPECT_STREQ("a<>", demangled);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
-
-  const std::string nested_mangled_name3 = NestedMangledName(3);
-  demangled = DemangleStackConsumption(nested_mangled_name3.c_str(),
-                                       &stack_consumed);
-  EXPECT_STREQ("a<>", demangled);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
-}
-
-#endif  // Stack consumption tests
-
-static void TestOnInput(const char* input) {
-  static const int kOutSize = 1048576;
-  auto out = absl::make_unique<char[]>(kOutSize);
-  Demangle(input, out.get(), kOutSize);
-}
-
-TEST(DemangleRegression, NegativeLength) {
-  TestOnInput("_ZZn4");
-}
-
-TEST(DemangleRegression, DeeplyNestedArrayType) {
-  const int depth = 100000;
-  std::string data = "_ZStI";
-  data.reserve(data.size() + 3 * depth + 1);
-  for (int i = 0; i < depth; i++) {
-    data += "A1_";
-  }
-  TestOnInput(data.c_str());
-}
-
-}  // namespace
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/debugging/internal/elf_mem_image.cc b/third_party/abseil_cpp/absl/debugging/internal/elf_mem_image.cc
deleted file mode 100644
index 24cc01302d..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/elf_mem_image.cc
+++ /dev/null
@@ -1,382 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Allow dynamic symbol lookup in an in-memory Elf image.
-//
-
-#include "absl/debugging/internal/elf_mem_image.h"
-
-#ifdef ABSL_HAVE_ELF_MEM_IMAGE  // defined in elf_mem_image.h
-
-#include <string.h>
-#include <cassert>
-#include <cstddef>
-#include "absl/base/internal/raw_logging.h"
-
-// From binutils/include/elf/common.h (this doesn't appear to be documented
-// anywhere else).
-//
-//   /* This flag appears in a Versym structure.  It means that the symbol
-//      is hidden, and is only visible with an explicit version number.
-//      This is a GNU extension.  */
-//   #define VERSYM_HIDDEN           0x8000
-//
-//   /* This is the mask for the rest of the Versym information.  */
-//   #define VERSYM_VERSION          0x7fff
-
-#define VERSYM_VERSION 0x7fff
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-namespace {
-
-#if __WORDSIZE == 32
-const int kElfClass = ELFCLASS32;
-int ElfBind(const ElfW(Sym) *symbol) { return ELF32_ST_BIND(symbol->st_info); }
-int ElfType(const ElfW(Sym) *symbol) { return ELF32_ST_TYPE(symbol->st_info); }
-#elif __WORDSIZE == 64
-const int kElfClass = ELFCLASS64;
-int ElfBind(const ElfW(Sym) *symbol) { return ELF64_ST_BIND(symbol->st_info); }
-int ElfType(const ElfW(Sym) *symbol) { return ELF64_ST_TYPE(symbol->st_info); }
-#else
-const int kElfClass = -1;
-int ElfBind(const ElfW(Sym) *) {
-  ABSL_RAW_LOG(FATAL, "Unexpected word size");
-  return 0;
-}
-int ElfType(const ElfW(Sym) *) {
-  ABSL_RAW_LOG(FATAL, "Unexpected word size");
-  return 0;
-}
-#endif
-
-// Extract an element from one of the ELF tables, cast it to desired type.
-// This is just a simple arithmetic and a glorified cast.
-// Callers are responsible for bounds checking.
-template <typename T>
-const T *GetTableElement(const ElfW(Ehdr) * ehdr, ElfW(Off) table_offset,
-                         ElfW(Word) element_size, size_t index) {
-  return reinterpret_cast<const T*>(reinterpret_cast<const char *>(ehdr)
-                                    + table_offset
-                                    + index * element_size);
-}
-
-}  // namespace
-
-// The value of this variable doesn't matter; it's used only for its
-// unique address.
-const int ElfMemImage::kInvalidBaseSentinel = 0;
-
-ElfMemImage::ElfMemImage(const void *base) {
-  ABSL_RAW_CHECK(base != kInvalidBase, "bad pointer");
-  Init(base);
-}
-
-int ElfMemImage::GetNumSymbols() const {
-  if (!hash_) {
-    return 0;
-  }
-  // See http://www.caldera.com/developers/gabi/latest/ch5.dynamic.html#hash
-  return hash_[1];
-}
-
-const ElfW(Sym) *ElfMemImage::GetDynsym(int index) const {
-  ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range");
-  return dynsym_ + index;
-}
-
-const ElfW(Versym) *ElfMemImage::GetVersym(int index) const {
-  ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range");
-  return versym_ + index;
-}
-
-const ElfW(Phdr) *ElfMemImage::GetPhdr(int index) const {
-  ABSL_RAW_CHECK(index < ehdr_->e_phnum, "index out of range");
-  return GetTableElement<ElfW(Phdr)>(ehdr_,
-                                     ehdr_->e_phoff,
-                                     ehdr_->e_phentsize,
-                                     index);
-}
-
-const char *ElfMemImage::GetDynstr(ElfW(Word) offset) const {
-  ABSL_RAW_CHECK(offset < strsize_, "offset out of range");
-  return dynstr_ + offset;
-}
-
-const void *ElfMemImage::GetSymAddr(const ElfW(Sym) *sym) const {
-  if (sym->st_shndx == SHN_UNDEF || sym->st_shndx >= SHN_LORESERVE) {
-    // Symbol corresponds to "special" (e.g. SHN_ABS) section.
-    return reinterpret_cast<const void *>(sym->st_value);
-  }
-  ABSL_RAW_CHECK(link_base_ < sym->st_value, "symbol out of range");
-  return GetTableElement<char>(ehdr_, 0, 1, sym->st_value - link_base_);
-}
-
-const ElfW(Verdef) *ElfMemImage::GetVerdef(int index) const {
-  ABSL_RAW_CHECK(0 <= index && static_cast<size_t>(index) <= verdefnum_,
-                 "index out of range");
-  const ElfW(Verdef) *version_definition = verdef_;
-  while (version_definition->vd_ndx < index && version_definition->vd_next) {
-    const char *const version_definition_as_char =
-        reinterpret_cast<const char *>(version_definition);
-    version_definition =
-        reinterpret_cast<const ElfW(Verdef) *>(version_definition_as_char +
-                                               version_definition->vd_next);
-  }
-  return version_definition->vd_ndx == index ? version_definition : nullptr;
-}
-
-const ElfW(Verdaux) *ElfMemImage::GetVerdefAux(
-    const ElfW(Verdef) *verdef) const {
-  return reinterpret_cast<const ElfW(Verdaux) *>(verdef+1);
-}
-
-const char *ElfMemImage::GetVerstr(ElfW(Word) offset) const {
-  ABSL_RAW_CHECK(offset < strsize_, "offset out of range");
-  return dynstr_ + offset;
-}
-
-void ElfMemImage::Init(const void *base) {
-  ehdr_      = nullptr;
-  dynsym_    = nullptr;
-  dynstr_    = nullptr;
-  versym_    = nullptr;
-  verdef_    = nullptr;
-  hash_      = nullptr;
-  strsize_   = 0;
-  verdefnum_ = 0;
-  link_base_ = ~0L;  // Sentinel: PT_LOAD .p_vaddr can't possibly be this.
-  if (!base) {
-    return;
-  }
-  const char *const base_as_char = reinterpret_cast<const char *>(base);
-  if (base_as_char[EI_MAG0] != ELFMAG0 || base_as_char[EI_MAG1] != ELFMAG1 ||
-      base_as_char[EI_MAG2] != ELFMAG2 || base_as_char[EI_MAG3] != ELFMAG3) {
-    assert(false);
-    return;
-  }
-  int elf_class = base_as_char[EI_CLASS];
-  if (elf_class != kElfClass) {
-    assert(false);
-    return;
-  }
-  switch (base_as_char[EI_DATA]) {
-    case ELFDATA2LSB: {
-      if (__LITTLE_ENDIAN != __BYTE_ORDER) {
-        assert(false);
-        return;
-      }
-      break;
-    }
-    case ELFDATA2MSB: {
-      if (__BIG_ENDIAN != __BYTE_ORDER) {
-        assert(false);
-        return;
-      }
-      break;
-    }
-    default: {
-      assert(false);
-      return;
-    }
-  }
-
-  ehdr_ = reinterpret_cast<const ElfW(Ehdr) *>(base);
-  const ElfW(Phdr) *dynamic_program_header = nullptr;
-  for (int i = 0; i < ehdr_->e_phnum; ++i) {
-    const ElfW(Phdr) *const program_header = GetPhdr(i);
-    switch (program_header->p_type) {
-      case PT_LOAD:
-        if (!~link_base_) {
-          link_base_ = program_header->p_vaddr;
-        }
-        break;
-      case PT_DYNAMIC:
-        dynamic_program_header = program_header;
-        break;
-    }
-  }
-  if (!~link_base_ || !dynamic_program_header) {
-    assert(false);
-    // Mark this image as not present. Can not recur infinitely.
-    Init(nullptr);
-    return;
-  }
-  ptrdiff_t relocation =
-      base_as_char - reinterpret_cast<const char *>(link_base_);
-  ElfW(Dyn) *dynamic_entry =
-      reinterpret_cast<ElfW(Dyn) *>(dynamic_program_header->p_vaddr +
-                                    relocation);
-  for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) {
-    const ElfW(Xword) value = dynamic_entry->d_un.d_val + relocation;
-    switch (dynamic_entry->d_tag) {
-      case DT_HASH:
-        hash_ = reinterpret_cast<ElfW(Word) *>(value);
-        break;
-      case DT_SYMTAB:
-        dynsym_ = reinterpret_cast<ElfW(Sym) *>(value);
-        break;
-      case DT_STRTAB:
-        dynstr_ = reinterpret_cast<const char *>(value);
-        break;
-      case DT_VERSYM:
-        versym_ = reinterpret_cast<ElfW(Versym) *>(value);
-        break;
-      case DT_VERDEF:
-        verdef_ = reinterpret_cast<ElfW(Verdef) *>(value);
-        break;
-      case DT_VERDEFNUM:
-        verdefnum_ = dynamic_entry->d_un.d_val;
-        break;
-      case DT_STRSZ:
-        strsize_ = dynamic_entry->d_un.d_val;
-        break;
-      default:
-        // Unrecognized entries explicitly ignored.
-        break;
-    }
-  }
-  if (!hash_ || !dynsym_ || !dynstr_ || !versym_ ||
-      !verdef_ || !verdefnum_ || !strsize_) {
-    assert(false);  // invalid VDSO
-    // Mark this image as not present. Can not recur infinitely.
-    Init(nullptr);
-    return;
-  }
-}
-
-bool ElfMemImage::LookupSymbol(const char *name,
-                               const char *version,
-                               int type,
-                               SymbolInfo *info_out) const {
-  for (const SymbolInfo& info : *this) {
-    if (strcmp(info.name, name) == 0 && strcmp(info.version, version) == 0 &&
-        ElfType(info.symbol) == type) {
-      if (info_out) {
-        *info_out = info;
-      }
-      return true;
-    }
-  }
-  return false;
-}
-
-bool ElfMemImage::LookupSymbolByAddress(const void *address,
-                                        SymbolInfo *info_out) const {
-  for (const SymbolInfo& info : *this) {
-    const char *const symbol_start =
-        reinterpret_cast<const char *>(info.address);
-    const char *const symbol_end = symbol_start + info.symbol->st_size;
-    if (symbol_start <= address && address < symbol_end) {
-      if (info_out) {
-        // Client wants to know details for that symbol (the usual case).
-        if (ElfBind(info.symbol) == STB_GLOBAL) {
-          // Strong symbol; just return it.
-          *info_out = info;
-          return true;
-        } else {
-          // Weak or local. Record it, but keep looking for a strong one.
-          *info_out = info;
-        }
-      } else {
-        // Client only cares if there is an overlapping symbol.
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-ElfMemImage::SymbolIterator::SymbolIterator(const void *const image, int index)
-    : index_(index), image_(image) {
-}
-
-const ElfMemImage::SymbolInfo *ElfMemImage::SymbolIterator::operator->() const {
-  return &info_;
-}
-
-const ElfMemImage::SymbolInfo& ElfMemImage::SymbolIterator::operator*() const {
-  return info_;
-}
-
-bool ElfMemImage::SymbolIterator::operator==(const SymbolIterator &rhs) const {
-  return this->image_ == rhs.image_ && this->index_ == rhs.index_;
-}
-
-bool ElfMemImage::SymbolIterator::operator!=(const SymbolIterator &rhs) const {
-  return !(*this == rhs);
-}
-
-ElfMemImage::SymbolIterator &ElfMemImage::SymbolIterator::operator++() {
-  this->Update(1);
-  return *this;
-}
-
-ElfMemImage::SymbolIterator ElfMemImage::begin() const {
-  SymbolIterator it(this, 0);
-  it.Update(0);
-  return it;
-}
-
-ElfMemImage::SymbolIterator ElfMemImage::end() const {
-  return SymbolIterator(this, GetNumSymbols());
-}
-
-void ElfMemImage::SymbolIterator::Update(int increment) {
-  const ElfMemImage *image = reinterpret_cast<const ElfMemImage *>(image_);
-  ABSL_RAW_CHECK(image->IsPresent() || increment == 0, "");
-  if (!image->IsPresent()) {
-    return;
-  }
-  index_ += increment;
-  if (index_ >= image->GetNumSymbols()) {
-    index_ = image->GetNumSymbols();
-    return;
-  }
-  const ElfW(Sym)    *symbol = image->GetDynsym(index_);
-  const ElfW(Versym) *version_symbol = image->GetVersym(index_);
-  ABSL_RAW_CHECK(symbol && version_symbol, "");
-  const char *const symbol_name = image->GetDynstr(symbol->st_name);
-  const ElfW(Versym) version_index = version_symbol[0] & VERSYM_VERSION;
-  const ElfW(Verdef) *version_definition = nullptr;
-  const char *version_name = "";
-  if (symbol->st_shndx == SHN_UNDEF) {
-    // Undefined symbols reference DT_VERNEED, not DT_VERDEF, and
-    // version_index could well be greater than verdefnum_, so calling
-    // GetVerdef(version_index) may trigger assertion.
-  } else {
-    version_definition = image->GetVerdef(version_index);
-  }
-  if (version_definition) {
-    // I am expecting 1 or 2 auxiliary entries: 1 for the version itself,
-    // optional 2nd if the version has a parent.
-    ABSL_RAW_CHECK(
-        version_definition->vd_cnt == 1 || version_definition->vd_cnt == 2,
-        "wrong number of entries");
-    const ElfW(Verdaux) *version_aux = image->GetVerdefAux(version_definition);
-    version_name = image->GetVerstr(version_aux->vda_name);
-  }
-  info_.name    = symbol_name;
-  info_.version = version_name;
-  info_.address = image->GetSymAddr(symbol);
-  info_.symbol  = symbol;
-}
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HAVE_ELF_MEM_IMAGE
diff --git a/third_party/abseil_cpp/absl/debugging/internal/elf_mem_image.h b/third_party/abseil_cpp/absl/debugging/internal/elf_mem_image.h
deleted file mode 100644
index 46bfade350..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/elf_mem_image.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2017 The Abseil Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Allow dynamic symbol lookup for in-memory Elf images.
-
-#ifndef ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_
-#define ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_
-
-// Including this will define the __GLIBC__ macro if glibc is being
-// used.
-#include <climits>
-
-#include "absl/base/config.h"
-
-// Maybe one day we can rewrite this file not to require the elf
-// symbol extensions in glibc, but for right now we need them.
-#ifdef ABSL_HAVE_ELF_MEM_IMAGE
-#error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set
-#endif
-
-#if defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \
-    !defined(__asmjs__) && !defined(__wasm__)
-#define ABSL_HAVE_ELF_MEM_IMAGE 1
-#endif
-
-#ifdef ABSL_HAVE_ELF_MEM_IMAGE
-
-#include <link.h>  // for ElfW
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// An in-memory ELF image (may not exist on disk).
-class ElfMemImage {
- private:
-  // Sentinel: there could never be an elf image at &kInvalidBaseSentinel.
-  static const int kInvalidBaseSentinel;
-
- public:
-  // Sentinel: there could never be an elf image at this address.
-  static constexpr const void *const kInvalidBase =
-    static_cast<const void*>(&kInvalidBaseSentinel);
-
-  // Information about a single vdso symbol.
-  // All pointers are into .dynsym, .dynstr, or .text of the VDSO.
-  // Do not free() them or modify through them.
-  struct SymbolInfo {
-    const char      *name;      // E.g. "__vdso_getcpu"
-    const char      *version;   // E.g. "LINUX_2.6", could be ""
-                                // for unversioned symbol.
-    const void      *address;   // Relocated symbol address.
-    const ElfW(Sym) *symbol;    // Symbol in the dynamic symbol table.
-  };
-
-  // Supports iteration over all dynamic symbols.
-  class SymbolIterator {
-   public:
-    friend class ElfMemImage;
-    const SymbolInfo *operator->() const;
-    const SymbolInfo &operator*() const;
-    SymbolIterator& operator++();
-    bool operator!=(const SymbolIterator &rhs) const;
-    bool operator==(const SymbolIterator &rhs) const;
-   private:
-    SymbolIterator(const void *const image, int index);
-    void Update(int incr);
-    SymbolInfo info_;
-    int index_;
-    const void *const image_;
-  };
-
-
-  explicit ElfMemImage(const void *base);
-  void                 Init(const void *base);
-  bool                 IsPresent() const { return ehdr_ != nullptr; }
-  const ElfW(Phdr)*    GetPhdr(int index) const;
-  const ElfW(Sym)*     GetDynsym(int index) const;
-  const ElfW(Versym)*  GetVersym(int index) const;
-  const ElfW(Verdef)*  GetVerdef(int index) const;
-  const ElfW(Verdaux)* GetVerdefAux(const ElfW(Verdef) *verdef) const;
-  const char*          GetDynstr(ElfW(Word) offset) const;
-  const void*          GetSymAddr(const ElfW(Sym) *sym) const;
-  const char*          GetVerstr(ElfW(Word) offset) const;
-  int                  GetNumSymbols() const;
-
-  SymbolIterator begin() const;
-  SymbolIterator end() const;
-
-  // Look up versioned dynamic symbol in the image.
-  // Returns false if image is not present, or doesn't contain given
-  // symbol/version/type combination.
-  // If info_out is non-null, additional details are filled in.
-  bool LookupSymbol(const char *name, const char *version,
-                    int symbol_type, SymbolInfo *info_out) const;
-
-  // Find info about symbol (if any) which overlaps given address.
-  // Returns true if symbol was found; false if image isn't present
-  // or doesn't have a symbol overlapping given address.
-  // If info_out is non-null, additional details are filled in.
-  bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const;
-
- private:
-  const ElfW(Ehdr) *ehdr_;
-  const ElfW(Sym) *dynsym_;
-  const ElfW(Versym) *versym_;
-  const ElfW(Verdef) *verdef_;
-  const ElfW(Word) *hash_;
-  const char *dynstr_;
-  size_t strsize_;
-  size_t verdefnum_;
-  ElfW(Addr) link_base_;     // Link-time base (p_vaddr of first PT_LOAD).
-};
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HAVE_ELF_MEM_IMAGE
-
-#endif  // ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/examine_stack.cc b/third_party/abseil_cpp/absl/debugging/internal/examine_stack.cc
deleted file mode 100644
index 6e5ff1fbd8..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/examine_stack.cc
+++ /dev/null
@@ -1,187 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "absl/debugging/internal/examine_stack.h"
-
-#ifndef _WIN32
-#include <unistd.h>
-#endif
-
-#ifdef __APPLE__
-#include <sys/ucontext.h>
-#endif
-
-#include <csignal>
-#include <cstdio>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/debugging/stacktrace.h"
-#include "absl/debugging/symbolize.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Returns the program counter from signal context, nullptr if
-// unknown. vuc is a ucontext_t*. We use void* to avoid the use of
-// ucontext_t on non-POSIX systems.
-void* GetProgramCounter(void* vuc) {
-#ifdef __linux__
-  if (vuc != nullptr) {
-    ucontext_t* context = reinterpret_cast<ucontext_t*>(vuc);
-#if defined(__aarch64__)
-    return reinterpret_cast<void*>(context->uc_mcontext.pc);
-#elif defined(__arm__)
-    return reinterpret_cast<void*>(context->uc_mcontext.arm_pc);
-#elif defined(__i386__)
-    if (14 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
-      return reinterpret_cast<void*>(context->uc_mcontext.gregs[14]);
-#elif defined(__mips__)
-    return reinterpret_cast<void*>(context->uc_mcontext.pc);
-#elif defined(__powerpc64__)
-    return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]);
-#elif defined(__powerpc__)
-    return reinterpret_cast<void*>(context->uc_mcontext.regs->nip);
-#elif defined(__riscv)
-    return reinterpret_cast<void*>(context->uc_mcontext.__gregs[REG_PC]);
-#elif defined(__s390__) && !defined(__s390x__)
-    return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff);
-#elif defined(__s390__) && defined(__s390x__)
-    return reinterpret_cast<void*>(context->uc_mcontext.psw.addr);
-#elif defined(__x86_64__)
-    if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
-      return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
-#else
-#error "Undefined Architecture."
-#endif
-  }
-#elif defined(__APPLE__)
-  if (vuc != nullptr) {
-    ucontext_t* signal_ucontext = reinterpret_cast<ucontext_t*>(vuc);
-#if defined(__aarch64__)
-    return reinterpret_cast<void*>(
-        __darwin_arm_thread_state64_get_pc(signal_ucontext->uc_mcontext->__ss));
-#elif defined(__arm__)
-#if __DARWIN_UNIX03
-    return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__pc);
-#else
-    return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.pc);
-#endif
-#elif defined(__i386__)
-#if __DARWIN_UNIX03
-    return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__eip);
-#else
-    return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.eip);
-#endif
-#elif defined(__x86_64__)
-#if __DARWIN_UNIX03
-    return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__rip);
-#else
-    return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.rip);
-#endif
-#endif
-  }
-#elif defined(__akaros__)
-  auto* ctx = reinterpret_cast<struct user_context*>(vuc);
-  return reinterpret_cast<void*>(get_user_ctx_pc(ctx));
-#endif
-  static_cast<void>(vuc);
-  return nullptr;
-}
-
-// The %p field width for printf() functions is two characters per byte,
-// and two extra for the leading "0x".
-static constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
-
-// Print a program counter, its stack frame size, and its symbol name.
-// Note that there is a separate symbolize_pc argument. Return addresses may be
-// at the end of the function, and this allows the caller to back up from pc if
-// appropriate.
-static void DumpPCAndFrameSizeAndSymbol(void (*writerfn)(const char*, void*),
-                                        void* writerfn_arg, void* pc,
-                                        void* symbolize_pc, int framesize,
-                                        const char* const prefix) {
-  char tmp[1024];
-  const char* symbol = "(unknown)";
-  if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) {
-    symbol = tmp;
-  }
-  char buf[1024];
-  if (framesize <= 0) {
-    snprintf(buf, sizeof(buf), "%s@ %*p  (unknown)  %s\n", prefix,
-             kPrintfPointerFieldWidth, pc, symbol);
-  } else {
-    snprintf(buf, sizeof(buf), "%s@ %*p  %9d  %s\n", prefix,
-             kPrintfPointerFieldWidth, pc, framesize, symbol);
-  }
-  writerfn(buf, writerfn_arg);
-}
-
-// Print a program counter and the corresponding stack frame size.
-static void DumpPCAndFrameSize(void (*writerfn)(const char*, void*),
-                               void* writerfn_arg, void* pc, int framesize,
-                               const char* const prefix) {
-  char buf[100];
-  if (framesize <= 0) {
-    snprintf(buf, sizeof(buf), "%s@ %*p  (unknown)\n", prefix,
-             kPrintfPointerFieldWidth, pc);
-  } else {
-    snprintf(buf, sizeof(buf), "%s@ %*p  %9d\n", prefix,
-             kPrintfPointerFieldWidth, pc, framesize);
-  }
-  writerfn(buf, writerfn_arg);
-}
-
-void DumpPCAndFrameSizesAndStackTrace(
-    void* pc, void* const stack[], int frame_sizes[], int depth,
-    int min_dropped_frames, bool symbolize_stacktrace,
-    void (*writerfn)(const char*, void*), void* writerfn_arg) {
-  if (pc != nullptr) {
-    // We don't know the stack frame size for PC, use 0.
-    if (symbolize_stacktrace) {
-      DumpPCAndFrameSizeAndSymbol(writerfn, writerfn_arg, pc, pc, 0, "PC: ");
-    } else {
-      DumpPCAndFrameSize(writerfn, writerfn_arg, pc, 0, "PC: ");
-    }
-  }
-  for (int i = 0; i < depth; i++) {
-    if (symbolize_stacktrace) {
-      // Pass the previous address of pc as the symbol address because pc is a
-      // return address, and an overrun may occur when the function ends with a
-      // call to a function annotated noreturn (e.g. CHECK). Note that we don't
-      // do this for pc above, as the adjustment is only correct for return
-      // addresses.
-      DumpPCAndFrameSizeAndSymbol(writerfn, writerfn_arg, stack[i],
-                                  reinterpret_cast<char*>(stack[i]) - 1,
-                                  frame_sizes[i], "    ");
-    } else {
-      DumpPCAndFrameSize(writerfn, writerfn_arg, stack[i], frame_sizes[i],
-                         "    ");
-    }
-  }
-  if (min_dropped_frames > 0) {
-    char buf[100];
-    snprintf(buf, sizeof(buf), "    @ ... and at least %d more frames\n",
-             min_dropped_frames);
-    writerfn(buf, writerfn_arg);
-  }
-}
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/debugging/internal/examine_stack.h b/third_party/abseil_cpp/absl/debugging/internal/examine_stack.h
deleted file mode 100644
index 393369131f..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/examine_stack.h
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
-#define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Returns the program counter from signal context, or nullptr if
-// unknown. `vuc` is a ucontext_t*. We use void* to avoid the use of
-// ucontext_t on non-POSIX systems.
-void* GetProgramCounter(void* vuc);
-
-// Uses `writerfn` to dump the program counter, stack trace, and stack
-// frame sizes.
-void DumpPCAndFrameSizesAndStackTrace(
-    void* pc, void* const stack[], int frame_sizes[], int depth,
-    int min_dropped_frames, bool symbolize_stacktrace,
-    void (*writerfn)(const char*, void*), void* writerfn_arg);
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stack_consumption.cc b/third_party/abseil_cpp/absl/debugging/internal/stack_consumption.cc
deleted file mode 100644
index e3dd51c355..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stack_consumption.cc
+++ /dev/null
@@ -1,185 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/debugging/internal/stack_consumption.h"
-
-#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
-
-#include <signal.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#include <string.h>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-namespace {
-
-// This code requires that we know the direction in which the stack
-// grows. It is commonly believed that this can be detected by putting
-// a variable on the stack and then passing its address to a function
-// that compares the address of this variable to the address of a
-// variable on the function's own stack. However, this is unspecified
-// behavior in C++: If two pointers p and q of the same type point to
-// different objects that are not members of the same object or
-// elements of the same array or to different functions, or if only
-// one of them is null, the results of p<q, p>q, p<=q, and p>=q are
-// unspecified. Therefore, instead we hardcode the direction of the
-// stack on platforms we know about.
-#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
-    defined(__aarch64__)
-constexpr bool kStackGrowsDown = true;
-#else
-#error Need to define kStackGrowsDown
-#endif
-
-// To measure the stack footprint of some code, we create a signal handler
-// (for SIGUSR2 say) that exercises this code on an alternate stack. This
-// alternate stack is initialized to some known pattern (0x55, 0x55, 0x55,
-// ...). We then self-send this signal, and after the signal handler returns,
-// look at the alternate stack buffer to see what portion has been touched.
-//
-// This trick gives us the the stack footprint of the signal handler.  But the
-// signal handler, even before the code for it is exercised, consumes some
-// stack already. We however only want the stack usage of the code inside the
-// signal handler. To measure this accurately, we install two signal handlers:
-// one that does nothing and just returns, and the user-provided signal
-// handler. The difference between the stack consumption of these two signals
-// handlers should give us the stack foorprint of interest.
-
-void EmptySignalHandler(int) {}
-
-// This is arbitrary value, and could be increase further, at the cost of
-// memset()ting it all to known sentinel value.
-constexpr int kAlternateStackSize = 64 << 10;  // 64KiB
-
-constexpr int kSafetyMargin = 32;
-constexpr char kAlternateStackFillValue = 0x55;
-
-// These helper functions look at the alternate stack buffer, and figure
-// out what portion of this buffer has been touched - this is the stack
-// consumption of the signal handler running on this alternate stack.
-// This function will return -1 if the alternate stack buffer has not been
-// touched. It will abort the program if the buffer has overflowed or is about
-// to overflow.
-int GetStackConsumption(const void* const altstack) {
-  const char* begin;
-  int increment;
-  if (kStackGrowsDown) {
-    begin = reinterpret_cast<const char*>(altstack);
-    increment = 1;
-  } else {
-    begin = reinterpret_cast<const char*>(altstack) + kAlternateStackSize - 1;
-    increment = -1;
-  }
-
-  for (int usage_count = kAlternateStackSize; usage_count > 0; --usage_count) {
-    if (*begin != kAlternateStackFillValue) {
-      ABSL_RAW_CHECK(usage_count <= kAlternateStackSize - kSafetyMargin,
-                     "Buffer has overflowed or is about to overflow");
-      return usage_count;
-    }
-    begin += increment;
-  }
-
-  ABSL_RAW_LOG(FATAL, "Unreachable code");
-  return -1;
-}
-
-}  // namespace
-
-int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) {
-  // The alt-signal-stack cannot be heap allocated because there is a
-  // bug in glibc-2.2 where some signal handler setup code looks at the
-  // current stack pointer to figure out what thread is currently running.
-  // Therefore, the alternate stack must be allocated from the main stack
-  // itself.
-  void* altstack = mmap(nullptr, kAlternateStackSize, PROT_READ | PROT_WRITE,
-                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  ABSL_RAW_CHECK(altstack != MAP_FAILED, "mmap() failed");
-
-  // Set up the alt-signal-stack (and save the older one).
-  stack_t sigstk;
-  memset(&sigstk, 0, sizeof(sigstk));
-  sigstk.ss_sp = altstack;
-  sigstk.ss_size = kAlternateStackSize;
-  sigstk.ss_flags = 0;
-  stack_t old_sigstk;
-  memset(&old_sigstk, 0, sizeof(old_sigstk));
-  ABSL_RAW_CHECK(sigaltstack(&sigstk, &old_sigstk) == 0,
-                 "sigaltstack() failed");
-
-  // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones).
-  struct sigaction sa;
-  memset(&sa, 0, sizeof(sa));
-  struct sigaction old_sa1, old_sa2;
-  sigemptyset(&sa.sa_mask);
-  sa.sa_flags = SA_ONSTACK;
-
-  // SIGUSR1 maps to EmptySignalHandler.
-  sa.sa_handler = EmptySignalHandler;
-  ABSL_RAW_CHECK(sigaction(SIGUSR1, &sa, &old_sa1) == 0, "sigaction() failed");
-
-  // SIGUSR2 maps to signal_handler.
-  sa.sa_handler = signal_handler;
-  ABSL_RAW_CHECK(sigaction(SIGUSR2, &sa, &old_sa2) == 0, "sigaction() failed");
-
-  // Send SIGUSR1 signal and measure the stack consumption of the empty
-  // signal handler.
-  // The first signal might use more stack space. Run once and ignore the
-  // results to get that out of the way.
-  ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed");
-
-  memset(altstack, kAlternateStackFillValue, kAlternateStackSize);
-  ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed");
-  int base_stack_consumption = GetStackConsumption(altstack);
-
-  // Send SIGUSR2 signal and measure the stack consumption of signal_handler.
-  ABSL_RAW_CHECK(kill(getpid(), SIGUSR2) == 0, "kill() failed");
-  int signal_handler_stack_consumption = GetStackConsumption(altstack);
-
-  // Now restore the old alt-signal-stack and signal handlers.
-  if (old_sigstk.ss_sp == nullptr && old_sigstk.ss_size == 0 &&
-      (old_sigstk.ss_flags & SS_DISABLE)) {
-    // https://git.musl-libc.org/cgit/musl/commit/src/signal/sigaltstack.c?id=7829f42a2c8944555439380498ab8b924d0f2070
-    // The original stack has ss_size==0 and ss_flags==SS_DISABLE, but some
-    // versions of musl have a bug that rejects ss_size==0. Work around this by
-    // setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel
-    // when SS_DISABLE is set.
-    old_sigstk.ss_size = MINSIGSTKSZ;
-  }
-  ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0,
-                 "sigaltstack() failed");
-  ABSL_RAW_CHECK(sigaction(SIGUSR1, &old_sa1, nullptr) == 0,
-                 "sigaction() failed");
-  ABSL_RAW_CHECK(sigaction(SIGUSR2, &old_sa2, nullptr) == 0,
-                 "sigaction() failed");
-
-  ABSL_RAW_CHECK(munmap(altstack, kAlternateStackSize) == 0, "munmap() failed");
-  if (signal_handler_stack_consumption != -1 && base_stack_consumption != -1) {
-    return signal_handler_stack_consumption - base_stack_consumption;
-  }
-  return -1;
-}
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stack_consumption.h b/third_party/abseil_cpp/absl/debugging/internal/stack_consumption.h
deleted file mode 100644
index 2b5e715166..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stack_consumption.h
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Helper function for measuring stack consumption of signal handlers.
-
-#ifndef ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_
-#define ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_
-
-#include "absl/base/config.h"
-
-// The code in this module is not portable.
-// Use this feature test macro to detect its availability.
-#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
-#error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly
-#elif !defined(__APPLE__) && !defined(_WIN32) &&                     \
-    (defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
-     defined(__aarch64__))
-#define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Returns the stack consumption in bytes for the code exercised by
-// signal_handler.  To measure stack consumption, signal_handler is registered
-// as a signal handler, so the code that it exercises must be async-signal
-// safe.  The argument of signal_handler is an implementation detail of signal
-// handlers and should ignored by the code for signal_handler.  Use global
-// variables to pass information between your test code and signal_handler.
-int GetSignalHandlerStackConsumption(void (*signal_handler)(int));
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
-
-#endif  // ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stack_consumption_test.cc b/third_party/abseil_cpp/absl/debugging/internal/stack_consumption_test.cc
deleted file mode 100644
index 80445bf43a..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stack_consumption_test.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/debugging/internal/stack_consumption.h"
-
-#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
-
-#include <string.h>
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-namespace {
-
-static void SimpleSignalHandler(int signo) {
-  char buf[100];
-  memset(buf, 'a', sizeof(buf));
-
-  // Never true, but prevents compiler from optimizing buf out.
-  if (signo == 0) {
-    ABSL_RAW_LOG(INFO, "%p", static_cast<void*>(buf));
-  }
-}
-
-TEST(SignalHandlerStackConsumptionTest, MeasuresStackConsumption) {
-  // Our handler should consume reasonable number of bytes.
-  EXPECT_GE(GetSignalHandlerStackConsumption(SimpleSignalHandler), 100);
-}
-
-}  // namespace
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc b/third_party/abseil_cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc
deleted file mode 100644
index f4859d7c21..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ /dev/null
@@ -1,199 +0,0 @@
-#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_
-#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_
-
-// Generate stack tracer for aarch64
-
-#if defined(__linux__)
-#include <sys/mman.h>
-#include <ucontext.h>
-#include <unistd.h>
-#endif
-
-#include <atomic>
-#include <cassert>
-#include <cstdint>
-#include <iostream>
-
-#include "absl/base/attributes.h"
-#include "absl/debugging/internal/address_is_readable.h"
-#include "absl/debugging/internal/vdso_support.h"  // a no-op on non-elf or non-glibc systems
-#include "absl/debugging/stacktrace.h"
-
-static const uintptr_t kUnknownFrameSize = 0;
-
-#if defined(__linux__)
-// Returns the address of the VDSO __kernel_rt_sigreturn function, if present.
-static const unsigned char* GetKernelRtSigreturnAddress() {
-  constexpr uintptr_t kImpossibleAddress = 1;
-  ABSL_CONST_INIT static std::atomic<uintptr_t> memoized{kImpossibleAddress};
-  uintptr_t address = memoized.load(std::memory_order_relaxed);
-  if (address != kImpossibleAddress) {
-    return reinterpret_cast<const unsigned char*>(address);
-  }
-
-  address = reinterpret_cast<uintptr_t>(nullptr);
-
-#ifdef ABSL_HAVE_VDSO_SUPPORT
-  absl::debugging_internal::VDSOSupport vdso;
-  if (vdso.IsPresent()) {
-    absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info;
-    auto lookup = [&](int type) {
-      return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", type,
-                               &symbol_info);
-    };
-    if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) ||
-        symbol_info.address == nullptr) {
-      // Unexpected: VDSO is present, yet the expected symbol is missing
-      // or null.
-      assert(false && "VDSO is present, but doesn't have expected symbol");
-    } else {
-      if (reinterpret_cast<uintptr_t>(symbol_info.address) !=
-          kImpossibleAddress) {
-        address = reinterpret_cast<uintptr_t>(symbol_info.address);
-      } else {
-        assert(false && "VDSO returned invalid address");
-      }
-    }
-  }
-#endif
-
-  memoized.store(address, std::memory_order_relaxed);
-  return reinterpret_cast<const unsigned char*>(address);
-}
-#endif  // __linux__
-
-// Compute the size of a stack frame in [low..high).  We assume that
-// low < high.  Return size of kUnknownFrameSize.
-template<typename T>
-static inline uintptr_t ComputeStackFrameSize(const T* low,
-                                              const T* high) {
-  const char* low_char_ptr = reinterpret_cast<const char *>(low);
-  const char* high_char_ptr = reinterpret_cast<const char *>(high);
-  return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize;
-}
-
-// Given a pointer to a stack frame, locate and return the calling
-// stackframe, or return null if no stackframe can be found. Perform sanity
-// checks (the strictness of which is controlled by the boolean parameter
-// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
-template<bool STRICT_UNWINDING, bool WITH_CONTEXT>
-ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
-ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
-static void **NextStackFrame(void **old_frame_pointer, const void *uc) {
-  void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer);
-  bool check_frame_size = true;
-
-#if defined(__linux__)
-  if (WITH_CONTEXT && uc != nullptr) {
-    // Check to see if next frame's return address is __kernel_rt_sigreturn.
-    if (old_frame_pointer[1] == GetKernelRtSigreturnAddress()) {
-      const ucontext_t *ucv = static_cast<const ucontext_t *>(uc);
-      // old_frame_pointer[0] is not suitable for unwinding, look at
-      // ucontext to discover frame pointer before signal.
-      void **const pre_signal_frame_pointer =
-          reinterpret_cast<void **>(ucv->uc_mcontext.regs[29]);
-
-      // Check that alleged frame pointer is actually readable. This is to
-      // prevent "double fault" in case we hit the first fault due to e.g.
-      // stack corruption.
-      if (!absl::debugging_internal::AddressIsReadable(
-              pre_signal_frame_pointer))
-        return nullptr;
-
-      // Alleged frame pointer is readable, use it for further unwinding.
-      new_frame_pointer = pre_signal_frame_pointer;
-
-      // Skip frame size check if we return from a signal. We may be using a
-      // an alternate stack for signals.
-      check_frame_size = false;
-    }
-  }
-#endif
-
-  // aarch64 ABI requires stack pointer to be 16-byte-aligned.
-  if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0)
-    return nullptr;
-
-  // Check frame size.  In strict mode, we assume frames to be under
-  // 100,000 bytes.  In non-strict mode, we relax the limit to 1MB.
-  if (check_frame_size) {
-    const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
-    const uintptr_t frame_size =
-        ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
-    if (frame_size == kUnknownFrameSize || frame_size > max_size)
-      return nullptr;
-  }
-
-  return new_frame_pointer;
-}
-
-template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
-ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
-ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
-static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
-                      const void *ucp, int *min_dropped_frames) {
-#ifdef __GNUC__
-  void **frame_pointer = reinterpret_cast<void**>(__builtin_frame_address(0));
-#else
-# error reading stack point not yet supported on this platform.
-#endif
-
-  skip_count++;    // Skip the frame for this function.
-  int n = 0;
-
-  // The frame pointer points to low address of a frame.  The first 64-bit
-  // word of a frame points to the next frame up the call chain, which normally
-  // is just after the high address of the current frame.  The second word of
-  // a frame contains return adress of to the caller.   To find a pc value
-  // associated with the current frame, we need to go down a level in the call
-  // chain.  So we remember return the address of the last frame seen.  This
-  // does not work for the first stack frame, which belongs to UnwindImp() but
-  // we skip the frame for UnwindImp() anyway.
-  void* prev_return_address = nullptr;
-
-  while (frame_pointer && n < max_depth) {
-    // The absl::GetStackFrames routine is called when we are in some
-    // informational context (the failure signal handler for example).
-    // Use the non-strict unwinding rules to produce a stack trace
-    // that is as complete as possible (even if it contains a few bogus
-    // entries in some rare cases).
-    void **next_frame_pointer =
-        NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
-
-    if (skip_count > 0) {
-      skip_count--;
-    } else {
-      result[n] = prev_return_address;
-      if (IS_STACK_FRAMES) {
-        sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
-      }
-      n++;
-    }
-    prev_return_address = frame_pointer[1];
-    frame_pointer = next_frame_pointer;
-  }
-  if (min_dropped_frames != nullptr) {
-    // Implementation detail: we clamp the max of frames we are willing to
-    // count, so as not to spend too much time in the loop below.
-    const int kMaxUnwind = 200;
-    int j = 0;
-    for (; frame_pointer != nullptr && j < kMaxUnwind; j++) {
-      frame_pointer =
-          NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
-    }
-    *min_dropped_frames = j;
-  }
-  return n;
-}
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-bool StackTraceWorksForTest() {
-  return true;
-}
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_arm-inl.inc b/third_party/abseil_cpp/absl/debugging/internal/stacktrace_arm-inl.inc
deleted file mode 100644
index 2a1bf2e886..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_arm-inl.inc
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This is inspired by Craig Silverstein's PowerPC stacktrace code.
-
-#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_
-#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_
-
-#include <cstdint>
-
-#include "absl/debugging/stacktrace.h"
-
-// WARNING:
-// This only works if all your code is in either ARM or THUMB mode.  With
-// interworking, the frame pointer of the caller can either be in r11 (ARM
-// mode) or r7 (THUMB mode).  A callee only saves the frame pointer of its
-// mode in a fixed location on its stack frame.  If the caller is a different
-// mode, there is no easy way to find the frame pointer.  It can either be
-// still in the designated register or saved on stack along with other callee
-// saved registers.
-
-// Given a pointer to a stack frame, locate and return the calling
-// stackframe, or return nullptr if no stackframe can be found. Perform sanity
-// checks (the strictness of which is controlled by the boolean parameter
-// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
-template<bool STRICT_UNWINDING>
-static void **NextStackFrame(void **old_sp) {
-  void **new_sp = (void**) old_sp[-1];
-
-  // Check that the transition from frame pointer old_sp to frame
-  // pointer new_sp isn't clearly bogus
-  if (STRICT_UNWINDING) {
-    // With the stack growing downwards, older stack frame must be
-    // at a greater address that the current one.
-    if (new_sp <= old_sp) return nullptr;
-    // Assume stack frames larger than 100,000 bytes are bogus.
-    if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return nullptr;
-  } else {
-    // In the non-strict mode, allow discontiguous stack frames.
-    // (alternate-signal-stacks for example).
-    if (new_sp == old_sp) return nullptr;
-    // And allow frames upto about 1MB.
-    if ((new_sp > old_sp)
-        && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return nullptr;
-  }
-  if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return nullptr;
-  return new_sp;
-}
-
-// This ensures that absl::GetStackTrace sets up the Link Register properly.
-#ifdef __GNUC__
-void StacktraceArmDummyFunction() __attribute__((noinline));
-void StacktraceArmDummyFunction() { __asm__ volatile(""); }
-#else
-# error StacktraceArmDummyFunction() needs to be ported to this platform.
-#endif
-
-template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
-static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
-                      const void * /* ucp */, int *min_dropped_frames) {
-#ifdef __GNUC__
-  void **sp = reinterpret_cast<void**>(__builtin_frame_address(0));
-#else
-# error reading stack point not yet supported on this platform.
-#endif
-
-  // On ARM, the return address is stored in the link register (r14).
-  // This is not saved on the stack frame of a leaf function.  To
-  // simplify code that reads return addresses, we call a dummy
-  // function so that the return address of this function is also
-  // stored in the stack frame.  This works at least for gcc.
-  StacktraceArmDummyFunction();
-
-  int n = 0;
-  while (sp && n < max_depth) {
-    // The absl::GetStackFrames routine is called when we are in some
-    // informational context (the failure signal handler for example).
-    // Use the non-strict unwinding rules to produce a stack trace
-    // that is as complete as possible (even if it contains a few bogus
-    // entries in some rare cases).
-    void **next_sp = NextStackFrame<!IS_STACK_FRAMES>(sp);
-
-    if (skip_count > 0) {
-      skip_count--;
-    } else {
-      result[n] = *sp;
-
-      if (IS_STACK_FRAMES) {
-        if (next_sp > sp) {
-          sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp;
-        } else {
-          // A frame-size of 0 is used to indicate unknown frame size.
-          sizes[n] = 0;
-        }
-      }
-      n++;
-    }
-    sp = next_sp;
-  }
-  if (min_dropped_frames != nullptr) {
-    // Implementation detail: we clamp the max of frames we are willing to
-    // count, so as not to spend too much time in the loop below.
-    const int kMaxUnwind = 200;
-    int j = 0;
-    for (; sp != nullptr && j < kMaxUnwind; j++) {
-      sp = NextStackFrame<!IS_STACK_FRAMES>(sp);
-    }
-    *min_dropped_frames = j;
-  }
-  return n;
-}
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-bool StackTraceWorksForTest() {
-  return false;
-}
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_config.h b/third_party/abseil_cpp/absl/debugging/internal/stacktrace_config.h
deleted file mode 100644
index 90af852818..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_config.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2017 The Abseil Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-
- * Defines ABSL_STACKTRACE_INL_HEADER to the *-inl.h containing
- * actual unwinder implementation.
- * This header is "private" to stacktrace.cc.
- * DO NOT include it into any other files.
-*/
-#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
-#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
-
-#if defined(ABSL_STACKTRACE_INL_HEADER)
-#error ABSL_STACKTRACE_INL_HEADER cannot be directly set
-
-#elif defined(_WIN32)
-#define ABSL_STACKTRACE_INL_HEADER \
-    "absl/debugging/internal/stacktrace_win32-inl.inc"
-
-#elif defined(__APPLE__)
-// Thread local support required for UnwindImpl.
-// Notes:
-// * Xcode's clang did not support `thread_local` until version 8, and
-//   even then not for all iOS < 9.0.
-// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
-//   targeting iOS 9.x.
-// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
-//   making __has_feature unreliable there.
-//
-// Otherwise, `__has_feature` is only supported by Clang so it has be inside
-// `defined(__APPLE__)` check.
-#if __has_feature(cxx_thread_local) && \
-    !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
-#define ABSL_STACKTRACE_INL_HEADER \
-  "absl/debugging/internal/stacktrace_generic-inl.inc"
-#endif
-
-#elif defined(__linux__) && !defined(__ANDROID__)
-
-#if defined(NO_FRAME_POINTER) && \
-    (defined(__i386__) || defined(__x86_64__) || defined(__aarch64__))
-// Note: The libunwind-based implementation is not available to open-source
-// users.
-#define ABSL_STACKTRACE_INL_HEADER \
-  "absl/debugging/internal/stacktrace_libunwind-inl.inc"
-#define STACKTRACE_USES_LIBUNWIND 1
-#elif defined(NO_FRAME_POINTER) && defined(__has_include)
-#if __has_include(<execinfo.h>)
-// Note: When using glibc this may require -funwind-tables to function properly.
-#define ABSL_STACKTRACE_INL_HEADER \
-  "absl/debugging/internal/stacktrace_generic-inl.inc"
-#endif
-#elif defined(__i386__) || defined(__x86_64__)
-#define ABSL_STACKTRACE_INL_HEADER \
-  "absl/debugging/internal/stacktrace_x86-inl.inc"
-#elif defined(__ppc__) || defined(__PPC__)
-#define ABSL_STACKTRACE_INL_HEADER \
-  "absl/debugging/internal/stacktrace_powerpc-inl.inc"
-#elif defined(__aarch64__)
-#define ABSL_STACKTRACE_INL_HEADER \
-  "absl/debugging/internal/stacktrace_aarch64-inl.inc"
-#elif defined(__has_include)
-#if __has_include(<execinfo.h>)
-// Note: When using glibc this may require -funwind-tables to function properly.
-#define ABSL_STACKTRACE_INL_HEADER \
-  "absl/debugging/internal/stacktrace_generic-inl.inc"
-#endif
-#endif
-
-#endif
-
-// Fallback to the empty implementation.
-#if !defined(ABSL_STACKTRACE_INL_HEADER)
-#define ABSL_STACKTRACE_INL_HEADER \
-  "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
-#endif
-
-#endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_generic-inl.inc b/third_party/abseil_cpp/absl/debugging/internal/stacktrace_generic-inl.inc
deleted file mode 100644
index b2792a1f3a..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_generic-inl.inc
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Portable implementation - just use glibc
-//
-// Note:  The glibc implementation may cause a call to malloc.
-// This can cause a deadlock in HeapProfiler.
-
-#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_
-#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_
-
-#include <execinfo.h>
-#include <atomic>
-#include <cstring>
-
-#include "absl/debugging/stacktrace.h"
-#include "absl/base/attributes.h"
-
-// Sometimes, we can try to get a stack trace from within a stack
-// trace, because we don't block signals inside this code (which would be too
-// expensive: the two extra system calls per stack trace do matter here).
-// That can cause a self-deadlock.
-// Protect against such reentrant call by failing to get a stack trace.
-//
-// We use __thread here because the code here is extremely low level -- it is
-// called while collecting stack traces from within malloc and mmap, and thus
-// can not call anything which might call malloc or mmap itself.
-static __thread int recursive = 0;
-
-// The stack trace function might be invoked very early in the program's
-// execution (e.g. from the very first malloc if using tcmalloc). Also, the
-// glibc implementation itself will trigger malloc the first time it is called.
-// As such, we suppress usage of backtrace during this early stage of execution.
-static std::atomic<bool> disable_stacktraces(true);  // Disabled until healthy.
-// Waiting until static initializers run seems to be late enough.
-// This file is included into stacktrace.cc so this will only run once.
-ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() {
-  void* unused_stack[1];
-  // Force the first backtrace to happen early to get the one-time shared lib
-  // loading (allocation) out of the way. After the first call it is much safer
-  // to use backtrace from a signal handler if we crash somewhere later.
-  backtrace(unused_stack, 1);
-  disable_stacktraces.store(false, std::memory_order_relaxed);
-  return 0;
-}();
-
-template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
-static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
-                      const void *ucp, int *min_dropped_frames) {
-  if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) {
-    return 0;
-  }
-  ++recursive;
-
-  static_cast<void>(ucp);  // Unused.
-  static const int kStackLength = 64;
-  void * stack[kStackLength];
-  int size;
-
-  size = backtrace(stack, kStackLength);
-  skip_count++;  // we want to skip the current frame as well
-  int result_count = size - skip_count;
-  if (result_count < 0)
-    result_count = 0;
-  if (result_count > max_depth)
-    result_count = max_depth;
-  for (int i = 0; i < result_count; i++)
-    result[i] = stack[i + skip_count];
-
-  if (IS_STACK_FRAMES) {
-    // No implementation for finding out the stack frame sizes yet.
-    memset(sizes, 0, sizeof(*sizes) * result_count);
-  }
-  if (min_dropped_frames != nullptr) {
-    if (size - skip_count - max_depth > 0) {
-      *min_dropped_frames = size - skip_count - max_depth;
-    } else {
-      *min_dropped_frames = 0;
-    }
-  }
-
-  --recursive;
-
-  return result_count;
-}
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-bool StackTraceWorksForTest() {
-  return true;
-}
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc b/third_party/abseil_cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc
deleted file mode 100644
index 2e7c2f404f..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Produce stack trace.  I'm guessing (hoping!) the code is much like
-// for x86.  For apple machines, at least, it seems to be; see
-//    https://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html
-//    https://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK
-// Linux has similar code: http://patchwork.ozlabs.org/linuxppc/patch?id=8882
-
-#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
-#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
-
-#if defined(__linux__)
-#include <asm/ptrace.h>   // for PT_NIP.
-#include <ucontext.h>     // for ucontext_t
-#endif
-
-#include <unistd.h>
-#include <cassert>
-#include <cstdint>
-#include <cstdio>
-
-#include "absl/base/attributes.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"
-#include "absl/debugging/stacktrace.h"
-#include "absl/debugging/internal/address_is_readable.h"
-#include "absl/debugging/internal/vdso_support.h"  // a no-op on non-elf or non-glibc systems
-
-// Given a stack pointer, return the saved link register value.
-// Note that this is the link register for a callee.
-static inline void *StacktracePowerPCGetLR(void **sp) {
-  // PowerPC has 3 main ABIs, which say where in the stack the
-  // Link Register is.  For DARWIN and AIX (used by apple and
-  // linux ppc64), it's in sp[2].  For SYSV (used by linux ppc),
-  // it's in sp[1].
-#if defined(_CALL_AIX) || defined(_CALL_DARWIN)
-  return *(sp+2);
-#elif defined(_CALL_SYSV)
-  return *(sp+1);
-#elif defined(__APPLE__) || defined(__FreeBSD__) || \
-    (defined(__linux__) && defined(__PPC64__))
-  // This check is in case the compiler doesn't define _CALL_AIX/etc.
-  return *(sp+2);
-#elif defined(__linux)
-  // This check is in case the compiler doesn't define _CALL_SYSV.
-  return *(sp+1);
-#else
-#error Need to specify the PPC ABI for your archiecture.
-#endif
-}
-
-// Given a pointer to a stack frame, locate and return the calling
-// stackframe, or return null if no stackframe can be found. Perform sanity
-// checks (the strictness of which is controlled by the boolean parameter
-// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
-template<bool STRICT_UNWINDING, bool IS_WITH_CONTEXT>
-ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
-ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
-static void **NextStackFrame(void **old_sp, const void *uc) {
-  void **new_sp = (void **) *old_sp;
-  enum { kStackAlignment = 16 };
-
-  // Check that the transition from frame pointer old_sp to frame
-  // pointer new_sp isn't clearly bogus
-  if (STRICT_UNWINDING) {
-    // With the stack growing downwards, older stack frame must be
-    // at a greater address that the current one.
-    if (new_sp <= old_sp) return nullptr;
-    // Assume stack frames larger than 100,000 bytes are bogus.
-    if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return nullptr;
-  } else {
-    // In the non-strict mode, allow discontiguous stack frames.
-    // (alternate-signal-stacks for example).
-    if (new_sp == old_sp) return nullptr;
-    // And allow frames upto about 1MB.
-    if ((new_sp > old_sp)
-        && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return nullptr;
-  }
-  if ((uintptr_t)new_sp % kStackAlignment != 0) return nullptr;
-
-#if defined(__linux__)
-  enum StackTraceKernelSymbolStatus {
-      kNotInitialized = 0, kAddressValid, kAddressInvalid };
-
-  if (IS_WITH_CONTEXT && uc != nullptr) {
-    static StackTraceKernelSymbolStatus kernel_symbol_status =
-        kNotInitialized;  // Sentinel: not computed yet.
-    // Initialize with sentinel value: __kernel_rt_sigtramp_rt64 can not
-    // possibly be there.
-    static const unsigned char *kernel_sigtramp_rt64_address = nullptr;
-    if (kernel_symbol_status == kNotInitialized) {
-      absl::debugging_internal::VDSOSupport vdso;
-      if (vdso.IsPresent()) {
-        absl::debugging_internal::VDSOSupport::SymbolInfo
-            sigtramp_rt64_symbol_info;
-        if (!vdso.LookupSymbol(
-                "__kernel_sigtramp_rt64", "LINUX_2.6.15",
-                absl::debugging_internal::VDSOSupport::kVDSOSymbolType,
-                &sigtramp_rt64_symbol_info) ||
-            sigtramp_rt64_symbol_info.address == nullptr) {
-          // Unexpected: VDSO is present, yet the expected symbol is missing
-          // or null.
-          assert(false && "VDSO is present, but doesn't have expected symbol");
-          kernel_symbol_status = kAddressInvalid;
-        } else {
-          kernel_sigtramp_rt64_address =
-              reinterpret_cast<const unsigned char *>(
-                  sigtramp_rt64_symbol_info.address);
-          kernel_symbol_status = kAddressValid;
-        }
-      } else {
-        kernel_symbol_status = kAddressInvalid;
-      }
-    }
-
-    if (new_sp != nullptr &&
-        kernel_symbol_status == kAddressValid &&
-        StacktracePowerPCGetLR(new_sp) == kernel_sigtramp_rt64_address) {
-      const ucontext_t* signal_context =
-          reinterpret_cast<const ucontext_t*>(uc);
-      void **const sp_before_signal =
-          reinterpret_cast<void**>(signal_context->uc_mcontext.gp_regs[PT_R1]);
-      // Check that alleged sp before signal is nonnull and is reasonably
-      // aligned.
-      if (sp_before_signal != nullptr &&
-          ((uintptr_t)sp_before_signal % kStackAlignment) == 0) {
-        // Check that alleged stack pointer is actually readable. This is to
-        // prevent a "double fault" in case we hit the first fault due to e.g.
-        // a stack corruption.
-        if (absl::debugging_internal::AddressIsReadable(sp_before_signal)) {
-          // Alleged stack pointer is readable, use it for further unwinding.
-          new_sp = sp_before_signal;
-        }
-      }
-    }
-  }
-#endif
-
-  return new_sp;
-}
-
-// This ensures that absl::GetStackTrace sets up the Link Register properly.
-ABSL_ATTRIBUTE_NOINLINE static void AbslStacktracePowerPCDummyFunction() {
-  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
-}
-
-template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
-ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
-ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
-static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
-                      const void *ucp, int *min_dropped_frames) {
-  void **sp;
-  // Apple macOS uses an old version of gnu as -- both Darwin 7.9.0 (Panther)
-  // and Darwin 8.8.1 (Tiger) use as 1.38.  This means we have to use a
-  // different asm syntax.  I don't know quite the best way to discriminate
-  // systems using the old as from the new one; I've gone with __APPLE__.
-#ifdef __APPLE__
-  __asm__ volatile ("mr %0,r1" : "=r" (sp));
-#else
-  __asm__ volatile ("mr %0,1" : "=r" (sp));
-#endif
-
-  // On PowerPC, the "Link Register" or "Link Record" (LR), is a stack
-  // entry that holds the return address of the subroutine call (what
-  // instruction we run after our function finishes).  This is the
-  // same as the stack-pointer of our parent routine, which is what we
-  // want here.  While the compiler will always(?) set up LR for
-  // subroutine calls, it may not for leaf functions (such as this one).
-  // This routine forces the compiler (at least gcc) to push it anyway.
-  AbslStacktracePowerPCDummyFunction();
-
-  // The LR save area is used by the callee, so the top entry is bogus.
-  skip_count++;
-
-  int n = 0;
-
-  // Unlike ABIs of X86 and ARM, PowerPC ABIs say that return address (in
-  // the link register) of a function call is stored in the caller's stack
-  // frame instead of the callee's.  When we look for the return address
-  // associated with a stack frame, we need to make sure that there is a
-  // caller frame before it.  So we call NextStackFrame before entering the
-  // loop below and check next_sp instead of sp for loop termination.
-  // The outermost frame is set up by runtimes and it does not have a
-  // caller frame, so it is skipped.
-
-  // The absl::GetStackFrames routine is called when we are in some
-  // informational context (the failure signal handler for example).
-  // Use the non-strict unwinding rules to produce a stack trace
-  // that is as complete as possible (even if it contains a few
-  // bogus entries in some rare cases).
-  void **next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(sp, ucp);
-
-  while (next_sp && n < max_depth) {
-    if (skip_count > 0) {
-      skip_count--;
-    } else {
-      result[n] = StacktracePowerPCGetLR(sp);
-      if (IS_STACK_FRAMES) {
-        if (next_sp > sp) {
-          sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp;
-        } else {
-          // A frame-size of 0 is used to indicate unknown frame size.
-          sizes[n] = 0;
-        }
-      }
-      n++;
-    }
-
-    sp = next_sp;
-    next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(sp, ucp);
-  }
-
-  if (min_dropped_frames != nullptr) {
-    // Implementation detail: we clamp the max of frames we are willing to
-    // count, so as not to spend too much time in the loop below.
-    const int kMaxUnwind = 1000;
-    int j = 0;
-    for (; next_sp != nullptr && j < kMaxUnwind; j++) {
-      next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(next_sp, ucp);
-    }
-    *min_dropped_frames = j;
-  }
-  return n;
-}
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-bool StackTraceWorksForTest() {
-  return true;
-}
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_unimplemented-inl.inc b/third_party/abseil_cpp/absl/debugging/internal/stacktrace_unimplemented-inl.inc
deleted file mode 100644
index 5b8fb191b6..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_unimplemented-inl.inc
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_
-#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_
-
-template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
-static int UnwindImpl(void** /* result */, int* /* sizes */,
-                      int /* max_depth */, int /* skip_count */,
-                      const void* /* ucp */, int *min_dropped_frames) {
-  if (min_dropped_frames != nullptr) {
-    *min_dropped_frames = 0;
-  }
-  return 0;
-}
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-bool StackTraceWorksForTest() {
-  return false;
-}
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_win32-inl.inc b/third_party/abseil_cpp/absl/debugging/internal/stacktrace_win32-inl.inc
deleted file mode 100644
index 1c666c8b56..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_win32-inl.inc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Produces a stack trace for Windows.  Normally, one could use
-// stacktrace_x86-inl.h or stacktrace_x86_64-inl.h -- and indeed, that
-// should work for binaries compiled using MSVC in "debug" mode.
-// However, in "release" mode, Windows uses frame-pointer
-// optimization, which makes getting a stack trace very difficult.
-//
-// There are several approaches one can take.  One is to use Windows
-// intrinsics like StackWalk64.  These can work, but have restrictions
-// on how successful they can be.  Another attempt is to write a
-// version of stacktrace_x86-inl.h that has heuristic support for
-// dealing with FPO, similar to what WinDbg does (see
-// http://www.nynaeve.net/?p=97).  There are (non-working) examples of
-// these approaches, complete with TODOs, in stacktrace_win32-inl.h#1
-//
-// The solution we've ended up doing is to call the undocumented
-// windows function RtlCaptureStackBackTrace, which probably doesn't
-// work with FPO but at least is fast, and doesn't require a symbol
-// server.
-//
-// This code is inspired by a patch from David Vitek:
-//   https://code.google.com/p/google-perftools/issues/detail?id=83
-
-#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
-#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
-
-#include <windows.h>    // for GetProcAddress and GetModuleHandle
-#include <cassert>
-
-typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
-    IN ULONG frames_to_skip,
-    IN ULONG frames_to_capture,
-    OUT PVOID *backtrace,
-    OUT PULONG backtrace_hash);
-
-// It is not possible to load RtlCaptureStackBackTrace at static init time in
-// UWP. CaptureStackBackTrace is the public version of RtlCaptureStackBackTrace
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
-    !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
-static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
-    &::CaptureStackBackTrace;
-#else
-// Load the function we need at static init time, where we don't have
-// to worry about someone else holding the loader's lock.
-static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
-    (RtlCaptureStackBackTrace_Function*)GetProcAddress(
-        GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace");
-#endif  // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
-
-template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
-static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
-                      const void*, int* min_dropped_frames) {
-  int n = 0;
-  if (!RtlCaptureStackBackTrace_fn) {
-    // can't find a stacktrace with no function to call
-  } else {
-    n = (int)RtlCaptureStackBackTrace_fn(skip_count + 2, max_depth, result, 0);
-  }
-  if (IS_STACK_FRAMES) {
-    // No implementation for finding out the stack frame sizes yet.
-    memset(sizes, 0, sizeof(*sizes) * n);
-  }
-  if (min_dropped_frames != nullptr) {
-    // Not implemented.
-    *min_dropped_frames = 0;
-  }
-  return n;
-}
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-bool StackTraceWorksForTest() {
-  return false;
-}
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_x86-inl.inc b/third_party/abseil_cpp/absl/debugging/internal/stacktrace_x86-inl.inc
deleted file mode 100644
index bc320ff75b..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/stacktrace_x86-inl.inc
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Produce stack trace
-
-#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_
-#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_
-
-#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
-#include <ucontext.h>  // for ucontext_t
-#endif
-
-#if !defined(_WIN32)
-#include <unistd.h>
-#endif
-
-#include <cassert>
-#include <cstdint>
-
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/debugging/internal/address_is_readable.h"
-#include "absl/debugging/internal/vdso_support.h"  // a no-op on non-elf or non-glibc systems
-#include "absl/debugging/stacktrace.h"
-
-#include "absl/base/internal/raw_logging.h"
-
-using absl::debugging_internal::AddressIsReadable;
-
-#if defined(__linux__) && defined(__i386__)
-// Count "push %reg" instructions in VDSO __kernel_vsyscall(),
-// preceeding "syscall" or "sysenter".
-// If __kernel_vsyscall uses frame pointer, answer 0.
-//
-// kMaxBytes tells how many instruction bytes of __kernel_vsyscall
-// to analyze before giving up. Up to kMaxBytes+1 bytes of
-// instructions could be accessed.
-//
-// Here are known __kernel_vsyscall instruction sequences:
-//
-// SYSENTER (linux-2.6.26/arch/x86/vdso/vdso32/sysenter.S).
-// Used on Intel.
-//  0xffffe400 <__kernel_vsyscall+0>:       push   %ecx
-//  0xffffe401 <__kernel_vsyscall+1>:       push   %edx
-//  0xffffe402 <__kernel_vsyscall+2>:       push   %ebp
-//  0xffffe403 <__kernel_vsyscall+3>:       mov    %esp,%ebp
-//  0xffffe405 <__kernel_vsyscall+5>:       sysenter
-//
-// SYSCALL (see linux-2.6.26/arch/x86/vdso/vdso32/syscall.S).
-// Used on AMD.
-//  0xffffe400 <__kernel_vsyscall+0>:       push   %ebp
-//  0xffffe401 <__kernel_vsyscall+1>:       mov    %ecx,%ebp
-//  0xffffe403 <__kernel_vsyscall+3>:       syscall
-//
-
-// The sequence below isn't actually expected in Google fleet,
-// here only for completeness. Remove this comment from OSS release.
-
-// i386 (see linux-2.6.26/arch/x86/vdso/vdso32/int80.S)
-//  0xffffe400 <__kernel_vsyscall+0>:       int $0x80
-//  0xffffe401 <__kernel_vsyscall+1>:       ret
-//
-static const int kMaxBytes = 10;
-
-// We use assert()s instead of DCHECK()s -- this is too low level
-// for DCHECK().
-
-static int CountPushInstructions(const unsigned char *const addr) {
-  int result = 0;
-  for (int i = 0; i < kMaxBytes; ++i) {
-    if (addr[i] == 0x89) {
-      // "mov reg,reg"
-      if (addr[i + 1] == 0xE5) {
-        // Found "mov %esp,%ebp".
-        return 0;
-      }
-      ++i;  // Skip register encoding byte.
-    } else if (addr[i] == 0x0F &&
-               (addr[i + 1] == 0x34 || addr[i + 1] == 0x05)) {
-      // Found "sysenter" or "syscall".
-      return result;
-    } else if ((addr[i] & 0xF0) == 0x50) {
-      // Found "push %reg".
-      ++result;
-    } else if (addr[i] == 0xCD && addr[i + 1] == 0x80) {
-      // Found "int $0x80"
-      assert(result == 0);
-      return 0;
-    } else {
-      // Unexpected instruction.
-      assert(false && "unexpected instruction in __kernel_vsyscall");
-      return 0;
-    }
-  }
-  // Unexpected: didn't find SYSENTER or SYSCALL in
-  // [__kernel_vsyscall, __kernel_vsyscall + kMaxBytes) interval.
-  assert(false && "did not find SYSENTER or SYSCALL in __kernel_vsyscall");
-  return 0;
-}
-#endif
-
-// Assume stack frames larger than 100,000 bytes are bogus.
-static const int kMaxFrameBytes = 100000;
-
-// Returns the stack frame pointer from signal context, 0 if unknown.
-// vuc is a ucontext_t *.  We use void* to avoid the use
-// of ucontext_t on non-POSIX systems.
-static uintptr_t GetFP(const void *vuc) {
-#if !defined(__linux__)
-  static_cast<void>(vuc);  // Avoid an unused argument compiler warning.
-#else
-  if (vuc != nullptr) {
-    auto *uc = reinterpret_cast<const ucontext_t *>(vuc);
-#if defined(__i386__)
-    const auto bp = uc->uc_mcontext.gregs[REG_EBP];
-    const auto sp = uc->uc_mcontext.gregs[REG_ESP];
-#elif defined(__x86_64__)
-    const auto bp = uc->uc_mcontext.gregs[REG_RBP];
-    const auto sp = uc->uc_mcontext.gregs[REG_RSP];
-#else
-    const uintptr_t bp = 0;
-    const uintptr_t sp = 0;
-#endif
-    // Sanity-check that the base pointer is valid.  It should be as long as
-    // SHRINK_WRAP_FRAME_POINTER is not set, but it's possible that some code in
-    // the process is compiled with --copt=-fomit-frame-pointer or
-    // --copt=-momit-leaf-frame-pointer.
-    //
-    // TODO(bcmills): -momit-leaf-frame-pointer is currently the default
-    // behavior when building with clang.  Talk to the C++ toolchain team about
-    // fixing that.
-    if (bp >= sp && bp - sp <= kMaxFrameBytes) return bp;
-
-    // If bp isn't a plausible frame pointer, return the stack pointer instead.
-    // If we're lucky, it points to the start of a stack frame; otherwise, we'll
-    // get one frame of garbage in the stack trace and fail the sanity check on
-    // the next iteration.
-    return sp;
-  }
-#endif
-  return 0;
-}
-
-// Given a pointer to a stack frame, locate and return the calling
-// stackframe, or return null if no stackframe can be found. Perform sanity
-// checks (the strictness of which is controlled by the boolean parameter
-// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
-template <bool STRICT_UNWINDING, bool WITH_CONTEXT>
-ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
-ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
-static void **NextStackFrame(void **old_fp, const void *uc) {
-  void **new_fp = (void **)*old_fp;
-
-#if defined(__linux__) && defined(__i386__)
-  if (WITH_CONTEXT && uc != nullptr) {
-    // How many "push %reg" instructions are there at __kernel_vsyscall?
-    // This is constant for a given kernel and processor, so compute
-    // it only once.
-    static int num_push_instructions = -1;  // Sentinel: not computed yet.
-    // Initialize with sentinel value: __kernel_rt_sigreturn can not possibly
-    // be there.
-    static const unsigned char *kernel_rt_sigreturn_address = nullptr;
-    static const unsigned char *kernel_vsyscall_address = nullptr;
-    if (num_push_instructions == -1) {
-#ifdef ABSL_HAVE_VDSO_SUPPORT
-      absl::debugging_internal::VDSOSupport vdso;
-      if (vdso.IsPresent()) {
-        absl::debugging_internal::VDSOSupport::SymbolInfo
-            rt_sigreturn_symbol_info;
-        absl::debugging_internal::VDSOSupport::SymbolInfo vsyscall_symbol_info;
-        if (!vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.5", STT_FUNC,
-                               &rt_sigreturn_symbol_info) ||
-            !vdso.LookupSymbol("__kernel_vsyscall", "LINUX_2.5", STT_FUNC,
-                               &vsyscall_symbol_info) ||
-            rt_sigreturn_symbol_info.address == nullptr ||
-            vsyscall_symbol_info.address == nullptr) {
-          // Unexpected: 32-bit VDSO is present, yet one of the expected
-          // symbols is missing or null.
-          assert(false && "VDSO is present, but doesn't have expected symbols");
-          num_push_instructions = 0;
-        } else {
-          kernel_rt_sigreturn_address =
-              reinterpret_cast<const unsigned char *>(
-                  rt_sigreturn_symbol_info.address);
-          kernel_vsyscall_address =
-              reinterpret_cast<const unsigned char *>(
-                  vsyscall_symbol_info.address);
-          num_push_instructions =
-              CountPushInstructions(kernel_vsyscall_address);
-        }
-      } else {
-        num_push_instructions = 0;
-      }
-#else  // ABSL_HAVE_VDSO_SUPPORT
-      num_push_instructions = 0;
-#endif  // ABSL_HAVE_VDSO_SUPPORT
-    }
-    if (num_push_instructions != 0 && kernel_rt_sigreturn_address != nullptr &&
-        old_fp[1] == kernel_rt_sigreturn_address) {
-      const ucontext_t *ucv = static_cast<const ucontext_t *>(uc);
-      // This kernel does not use frame pointer in its VDSO code,
-      // and so %ebp is not suitable for unwinding.
-      void **const reg_ebp =
-          reinterpret_cast<void **>(ucv->uc_mcontext.gregs[REG_EBP]);
-      const unsigned char *const reg_eip =
-          reinterpret_cast<unsigned char *>(ucv->uc_mcontext.gregs[REG_EIP]);
-      if (new_fp == reg_ebp && kernel_vsyscall_address <= reg_eip &&
-          reg_eip - kernel_vsyscall_address < kMaxBytes) {
-        // We "stepped up" to __kernel_vsyscall, but %ebp is not usable.
-        // Restore from 'ucv' instead.
-        void **const reg_esp =
-            reinterpret_cast<void **>(ucv->uc_mcontext.gregs[REG_ESP]);
-        // Check that alleged %esp is not null and is reasonably aligned.
-        if (reg_esp &&
-            ((uintptr_t)reg_esp & (sizeof(reg_esp) - 1)) == 0) {
-          // Check that alleged %esp is actually readable. This is to prevent
-          // "double fault" in case we hit the first fault due to e.g. stack
-          // corruption.
-          void *const reg_esp2 = reg_esp[num_push_instructions - 1];
-          if (AddressIsReadable(reg_esp2)) {
-            // Alleged %esp is readable, use it for further unwinding.
-            new_fp = reinterpret_cast<void **>(reg_esp2);
-          }
-        }
-      }
-    }
-  }
-#endif
-
-  const uintptr_t old_fp_u = reinterpret_cast<uintptr_t>(old_fp);
-  const uintptr_t new_fp_u = reinterpret_cast<uintptr_t>(new_fp);
-
-  // Check that the transition from frame pointer old_fp to frame
-  // pointer new_fp isn't clearly bogus.  Skip the checks if new_fp
-  // matches the signal context, so that we don't skip out early when
-  // using an alternate signal stack.
-  //
-  // TODO(bcmills): The GetFP call should be completely unnecessary when
-  // SHRINK_WRAP_FRAME_POINTER is set (because we should be back in the thread's
-  // stack by this point), but it is empirically still needed (e.g. when the
-  // stack includes a call to abort).  unw_get_reg returns UNW_EBADREG for some
-  // frames.  Figure out why GetValidFrameAddr and/or libunwind isn't doing what
-  // it's supposed to.
-  if (STRICT_UNWINDING &&
-      (!WITH_CONTEXT || uc == nullptr || new_fp_u != GetFP(uc))) {
-    // With the stack growing downwards, older stack frame must be
-    // at a greater address that the current one.
-    if (new_fp_u <= old_fp_u) return nullptr;
-    if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr;
-  } else {
-    if (new_fp == nullptr) return nullptr;  // skip AddressIsReadable() below
-    // In the non-strict mode, allow discontiguous stack frames.
-    // (alternate-signal-stacks for example).
-    if (new_fp == old_fp) return nullptr;
-  }
-
-  if (new_fp_u & (sizeof(void *) - 1)) return nullptr;
-#ifdef __i386__
-  // On 32-bit machines, the stack pointer can be very close to
-  // 0xffffffff, so we explicitly check for a pointer into the
-  // last two pages in the address space
-  if (new_fp_u >= 0xffffe000) return nullptr;
-#endif
-#if !defined(_WIN32)
-  if (!STRICT_UNWINDING) {
-    // Lax sanity checks cause a crash in 32-bit tcmalloc/crash_reason_test
-    // on AMD-based machines with VDSO-enabled kernels.
-    // Make an extra sanity check to insure new_fp is readable.
-    // Note: NextStackFrame<false>() is only called while the program
-    //       is already on its last leg, so it's ok to be slow here.
-
-    if (!AddressIsReadable(new_fp)) {
-      return nullptr;
-    }
-  }
-#endif
-  return new_fp;
-}
-
-template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
-ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
-ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
-ABSL_ATTRIBUTE_NOINLINE
-static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
-                      const void *ucp, int *min_dropped_frames) {
-  int n = 0;
-  void **fp = reinterpret_cast<void **>(__builtin_frame_address(0));
-
-  while (fp && n < max_depth) {
-    if (*(fp + 1) == reinterpret_cast<void *>(0)) {
-      // In 64-bit code, we often see a frame that
-      // points to itself and has a return address of 0.
-      break;
-    }
-    void **next_fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
-    if (skip_count > 0) {
-      skip_count--;
-    } else {
-      result[n] = *(fp + 1);
-      if (IS_STACK_FRAMES) {
-        if (next_fp > fp) {
-          sizes[n] = (uintptr_t)next_fp - (uintptr_t)fp;
-        } else {
-          // A frame-size of 0 is used to indicate unknown frame size.
-          sizes[n] = 0;
-        }
-      }
-      n++;
-    }
-    fp = next_fp;
-  }
-  if (min_dropped_frames != nullptr) {
-    // Implementation detail: we clamp the max of frames we are willing to
-    // count, so as not to spend too much time in the loop below.
-    const int kMaxUnwind = 1000;
-    int j = 0;
-    for (; fp != nullptr && j < kMaxUnwind; j++) {
-      fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
-    }
-    *min_dropped_frames = j;
-  }
-  return n;
-}
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-bool StackTraceWorksForTest() {
-  return true;
-}
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/symbolize.h b/third_party/abseil_cpp/absl/debugging/internal/symbolize.h
deleted file mode 100644
index 4f26130fbb..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/symbolize.h
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file contains internal parts of the Abseil symbolizer.
-// Do not depend on the anything in this file, it may change at anytime.
-
-#ifndef ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_
-#define ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_
-
-#ifdef __cplusplus
-
-#include <cstddef>
-#include <cstdint>
-
-#include "absl/base/config.h"
-#include "absl/strings/string_view.h"
-
-#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
-#error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set
-#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \
-    !defined(__asmjs__) && !defined(__wasm__)
-#define ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE 1
-
-#include <elf.h>
-#include <link.h>  // For ElfW() macro.
-#include <functional>
-#include <string>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Iterates over all sections, invoking callback on each with the section name
-// and the section header.
-//
-// Returns true on success; otherwise returns false in case of errors.
-//
-// This is not async-signal-safe.
-bool ForEachSection(int fd,
-                    const std::function<bool(absl::string_view name,
-                                             const ElfW(Shdr) &)>& callback);
-
-// Gets the section header for the given name, if it exists. Returns true on
-// success. Otherwise, returns false.
-bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
-                            ElfW(Shdr) *out);
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
-
-#ifdef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
-#error ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE cannot be directly set
-#elif defined(__APPLE__)
-#define ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE 1
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-struct SymbolDecoratorArgs {
-  // The program counter we are getting symbolic name for.
-  const void *pc;
-  // 0 for main executable, load address for shared libraries.
-  ptrdiff_t relocation;
-  // Read-only file descriptor for ELF image covering "pc",
-  // or -1 if no such ELF image exists in /proc/self/maps.
-  int fd;
-  // Output buffer, size.
-  // Note: the buffer may not be empty -- default symbolizer may have already
-  // produced some output, and earlier decorators may have adorned it in
-  // some way. You are free to replace or augment the contents (within the
-  // symbol_buf_size limit).
-  char *const symbol_buf;
-  size_t symbol_buf_size;
-  // Temporary scratch space, size.
-  // Use that space in preference to allocating your own stack buffer to
-  // conserve stack.
-  char *const tmp_buf;
-  size_t tmp_buf_size;
-  // User-provided argument
-  void* arg;
-};
-using SymbolDecorator = void (*)(const SymbolDecoratorArgs *);
-
-// Installs a function-pointer as a decorator. Returns a value less than zero
-// if the system cannot install the decorator. Otherwise, returns a unique
-// identifier corresponding to the decorator. This identifier can be used to
-// uninstall the decorator - See RemoveSymbolDecorator() below.
-int InstallSymbolDecorator(SymbolDecorator decorator, void* arg);
-
-// Removes a previously installed function-pointer decorator. Parameter "ticket"
-// is the return-value from calling InstallSymbolDecorator().
-bool RemoveSymbolDecorator(int ticket);
-
-// Remove all installed decorators.  Returns true if successful, false if
-// symbolization is currently in progress.
-bool RemoveAllSymbolDecorators(void);
-
-// Registers an address range to a file mapping.
-//
-// Preconditions:
-//   start <= end
-//   filename != nullptr
-//
-// Returns true if the file was successfully registered.
-bool RegisterFileMappingHint(const void* start, const void* end,
-                             uint64_t offset, const char* filename);
-
-// Looks up the file mapping registered by RegisterFileMappingHint for an
-// address range. If there is one, the file name is stored in *filename and
-// *start and *end are modified to reflect the registered mapping. Returns
-// whether any hint was found.
-bool GetFileMappingHint(const void** start, const void** end, uint64_t* offset,
-                        const char** filename);
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // __cplusplus
-
-#include <stdbool.h>
-
-#ifdef __cplusplus
-extern "C"
-#endif  // __cplusplus
-
-    bool
-    AbslInternalGetFileMappingHint(const void** start, const void** end,
-                                   uint64_t* offset, const char** filename);
-
-#endif  // ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_
diff --git a/third_party/abseil_cpp/absl/debugging/internal/vdso_support.cc b/third_party/abseil_cpp/absl/debugging/internal/vdso_support.cc
deleted file mode 100644
index 6be16d9072..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/vdso_support.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Allow dynamic symbol lookup in the kernel VDSO page.
-//
-// VDSOSupport -- a class representing kernel VDSO (if present).
-
-#include "absl/debugging/internal/vdso_support.h"
-
-#ifdef ABSL_HAVE_VDSO_SUPPORT     // defined in vdso_support.h
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#if __GLIBC_PREREQ(2, 16)  // GLIBC-2.16 implements getauxval.
-#include <sys/auxv.h>
-#endif
-
-#include "absl/base/dynamic_annotations.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/port.h"
-
-#ifndef AT_SYSINFO_EHDR
-#define AT_SYSINFO_EHDR 33  // for crosstoolv10
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-ABSL_CONST_INIT
-std::atomic<const void *> VDSOSupport::vdso_base_(
-    debugging_internal::ElfMemImage::kInvalidBase);
-
-std::atomic<VDSOSupport::GetCpuFn> VDSOSupport::getcpu_fn_(&InitAndGetCPU);
-VDSOSupport::VDSOSupport()
-    // If vdso_base_ is still set to kInvalidBase, we got here
-    // before VDSOSupport::Init has been called. Call it now.
-    : image_(vdso_base_.load(std::memory_order_relaxed) ==
-                     debugging_internal::ElfMemImage::kInvalidBase
-                 ? Init()
-                 : vdso_base_.load(std::memory_order_relaxed)) {}
-
-// NOTE: we can't use GoogleOnceInit() below, because we can be
-// called by tcmalloc, and none of the *once* stuff may be functional yet.
-//
-// In addition, we hope that the VDSOSupportHelper constructor
-// causes this code to run before there are any threads, and before
-// InitGoogle() has executed any chroot or setuid calls.
-//
-// Finally, even if there is a race here, it is harmless, because
-// the operation should be idempotent.
-const void *VDSOSupport::Init() {
-  const auto kInvalidBase = debugging_internal::ElfMemImage::kInvalidBase;
-#if __GLIBC_PREREQ(2, 16)
-  if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
-    errno = 0;
-    const void *const sysinfo_ehdr =
-        reinterpret_cast<const void *>(getauxval(AT_SYSINFO_EHDR));
-    if (errno == 0) {
-      vdso_base_.store(sysinfo_ehdr, std::memory_order_relaxed);
-    }
-  }
-#endif  // __GLIBC_PREREQ(2, 16)
-  if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
-    int fd = open("/proc/self/auxv", O_RDONLY);
-    if (fd == -1) {
-      // Kernel too old to have a VDSO.
-      vdso_base_.store(nullptr, std::memory_order_relaxed);
-      getcpu_fn_.store(&GetCPUViaSyscall, std::memory_order_relaxed);
-      return nullptr;
-    }
-    ElfW(auxv_t) aux;
-    while (read(fd, &aux, sizeof(aux)) == sizeof(aux)) {
-      if (aux.a_type == AT_SYSINFO_EHDR) {
-        vdso_base_.store(reinterpret_cast<void *>(aux.a_un.a_val),
-                         std::memory_order_relaxed);
-        break;
-      }
-    }
-    close(fd);
-    if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
-      // Didn't find AT_SYSINFO_EHDR in auxv[].
-      vdso_base_.store(nullptr, std::memory_order_relaxed);
-    }
-  }
-  GetCpuFn fn = &GetCPUViaSyscall;  // default if VDSO not present.
-  if (vdso_base_.load(std::memory_order_relaxed)) {
-    VDSOSupport vdso;
-    SymbolInfo info;
-    if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) {
-      fn = reinterpret_cast<GetCpuFn>(const_cast<void *>(info.address));
-    }
-  }
-  // Subtle: this code runs outside of any locks; prevent compiler
-  // from assigning to getcpu_fn_ more than once.
-  getcpu_fn_.store(fn, std::memory_order_relaxed);
-  return vdso_base_.load(std::memory_order_relaxed);
-}
-
-const void *VDSOSupport::SetBase(const void *base) {
-  ABSL_RAW_CHECK(base != debugging_internal::ElfMemImage::kInvalidBase,
-                 "internal error");
-  const void *old_base = vdso_base_.load(std::memory_order_relaxed);
-  vdso_base_.store(base, std::memory_order_relaxed);
-  image_.Init(base);
-  // Also reset getcpu_fn_, so GetCPU could be tested with simulated VDSO.
-  getcpu_fn_.store(&InitAndGetCPU, std::memory_order_relaxed);
-  return old_base;
-}
-
-bool VDSOSupport::LookupSymbol(const char *name,
-                               const char *version,
-                               int type,
-                               SymbolInfo *info) const {
-  return image_.LookupSymbol(name, version, type, info);
-}
-
-bool VDSOSupport::LookupSymbolByAddress(const void *address,
-                                        SymbolInfo *info_out) const {
-  return image_.LookupSymbolByAddress(address, info_out);
-}
-
-// NOLINT on 'long' because this routine mimics kernel api.
-long VDSOSupport::GetCPUViaSyscall(unsigned *cpu,  // NOLINT(runtime/int)
-                                   void *, void *) {
-#ifdef SYS_getcpu
-  return syscall(SYS_getcpu, cpu, nullptr, nullptr);
-#else
-  // x86_64 never implemented sys_getcpu(), except as a VDSO call.
-  static_cast<void>(cpu);  // Avoid an unused argument compiler warning.
-  errno = ENOSYS;
-  return -1;
-#endif
-}
-
-// Use fast __vdso_getcpu if available.
-long VDSOSupport::InitAndGetCPU(unsigned *cpu,  // NOLINT(runtime/int)
-                                void *x, void *y) {
-  Init();
-  GetCpuFn fn = getcpu_fn_.load(std::memory_order_relaxed);
-  ABSL_RAW_CHECK(fn != &InitAndGetCPU, "Init() did not set getcpu_fn_");
-  return (*fn)(cpu, x, y);
-}
-
-// This function must be very fast, and may be called from very
-// low level (e.g. tcmalloc). Hence I avoid things like
-// GoogleOnceInit() and ::operator new.
-ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
-int GetCPU() {
-  unsigned cpu;
-  int ret_code = (*VDSOSupport::getcpu_fn_)(&cpu, nullptr, nullptr);
-  return ret_code == 0 ? cpu : ret_code;
-}
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HAVE_VDSO_SUPPORT
diff --git a/third_party/abseil_cpp/absl/debugging/internal/vdso_support.h b/third_party/abseil_cpp/absl/debugging/internal/vdso_support.h
deleted file mode 100644
index 6562c6c235..0000000000
--- a/third_party/abseil_cpp/absl/debugging/internal/vdso_support.h
+++ /dev/null
@@ -1,158 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// Allow dynamic symbol lookup in the kernel VDSO page.
-//
-// VDSO stands for "Virtual Dynamic Shared Object" -- a page of
-// executable code, which looks like a shared library, but doesn't
-// necessarily exist anywhere on disk, and which gets mmap()ed into
-// every process by kernels which support VDSO, such as 2.6.x for 32-bit
-// executables, and 2.6.24 and above for 64-bit executables.
-//
-// More details could be found here:
-// http://www.trilithium.com/johan/2005/08/linux-gate/
-//
-// VDSOSupport -- a class representing kernel VDSO (if present).
-//
-// Example usage:
-//  VDSOSupport vdso;
-//  VDSOSupport::SymbolInfo info;
-//  typedef (*FN)(unsigned *, void *, void *);
-//  FN fn = nullptr;
-//  if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) {
-//     fn = reinterpret_cast<FN>(info.address);
-//  }
-
-#ifndef ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_
-#define ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_
-
-#include <atomic>
-
-#include "absl/base/attributes.h"
-#include "absl/debugging/internal/elf_mem_image.h"
-
-#ifdef ABSL_HAVE_ELF_MEM_IMAGE
-
-#ifdef ABSL_HAVE_VDSO_SUPPORT
-#error ABSL_HAVE_VDSO_SUPPORT cannot be directly set
-#else
-#define ABSL_HAVE_VDSO_SUPPORT 1
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// NOTE: this class may be used from within tcmalloc, and can not
-// use any memory allocation routines.
-class VDSOSupport {
- public:
-  VDSOSupport();
-
-  typedef ElfMemImage::SymbolInfo SymbolInfo;
-  typedef ElfMemImage::SymbolIterator SymbolIterator;
-
-  // On PowerPC64 VDSO symbols can either be of type STT_FUNC or STT_NOTYPE
-  // depending on how the kernel is built.  The kernel is normally built with
-  // STT_NOTYPE type VDSO symbols.  Let's make things simpler first by using a
-  // compile-time constant.
-#ifdef __powerpc64__
-  enum { kVDSOSymbolType = STT_NOTYPE };
-#else
-  enum { kVDSOSymbolType = STT_FUNC };
-#endif
-
-  // Answers whether we have a vdso at all.
-  bool IsPresent() const { return image_.IsPresent(); }
-
-  // Allow to iterate over all VDSO symbols.
-  SymbolIterator begin() const { return image_.begin(); }
-  SymbolIterator end() const { return image_.end(); }
-
-  // Look up versioned dynamic symbol in the kernel VDSO.
-  // Returns false if VDSO is not present, or doesn't contain given
-  // symbol/version/type combination.
-  // If info_out != nullptr, additional details are filled in.
-  bool LookupSymbol(const char *name, const char *version,
-                    int symbol_type, SymbolInfo *info_out) const;
-
-  // Find info about symbol (if any) which overlaps given address.
-  // Returns true if symbol was found; false if VDSO isn't present
-  // or doesn't have a symbol overlapping given address.
-  // If info_out != nullptr, additional details are filled in.
-  bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const;
-
-  // Used only for testing. Replace real VDSO base with a mock.
-  // Returns previous value of vdso_base_. After you are done testing,
-  // you are expected to call SetBase() with previous value, in order to
-  // reset state to the way it was.
-  const void *SetBase(const void *s);
-
-  // Computes vdso_base_ and returns it. Should be called as early as
-  // possible; before any thread creation, chroot or setuid.
-  static const void *Init();
-
- private:
-  // image_ represents VDSO ELF image in memory.
-  // image_.ehdr_ == nullptr implies there is no VDSO.
-  ElfMemImage image_;
-
-  // Cached value of auxv AT_SYSINFO_EHDR, computed once.
-  // This is a tri-state:
-  //   kInvalidBase   => value hasn't been determined yet.
-  //              0   => there is no VDSO.
-  //           else   => vma of VDSO Elf{32,64}_Ehdr.
-  //
-  // When testing with mock VDSO, low bit is set.
-  // The low bit is always available because vdso_base_ is
-  // page-aligned.
-  static std::atomic<const void *> vdso_base_;
-
-  // NOLINT on 'long' because these routines mimic kernel api.
-  // The 'cache' parameter may be used by some versions of the kernel,
-  // and should be nullptr or point to a static buffer containing at
-  // least two 'long's.
-  static long InitAndGetCPU(unsigned *cpu, void *cache,     // NOLINT 'long'.
-                            void *unused);
-  static long GetCPUViaSyscall(unsigned *cpu, void *cache,  // NOLINT 'long'.
-                               void *unused);
-  typedef long (*GetCpuFn)(unsigned *cpu, void *cache,      // NOLINT 'long'.
-                           void *unused);
-
-  // This function pointer may point to InitAndGetCPU,
-  // GetCPUViaSyscall, or __vdso_getcpu at different stages of initialization.
-  ABSL_CONST_INIT static std::atomic<GetCpuFn> getcpu_fn_;
-
-  friend int GetCPU(void);  // Needs access to getcpu_fn_.
-
-  VDSOSupport(const VDSOSupport&) = delete;
-  VDSOSupport& operator=(const VDSOSupport&) = delete;
-};
-
-// Same as sched_getcpu() on later glibc versions.
-// Return current CPU, using (fast) __vdso_getcpu@LINUX_2.6 if present,
-// otherwise use syscall(SYS_getcpu,...).
-// May return -1 with errno == ENOSYS if the kernel doesn't
-// support SYS_getcpu.
-int GetCPU();
-
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HAVE_ELF_MEM_IMAGE
-
-#endif  // ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_
diff --git a/third_party/abseil_cpp/absl/debugging/leak_check.cc b/third_party/abseil_cpp/absl/debugging/leak_check.cc
deleted file mode 100644
index ff9049559d..0000000000
--- a/third_party/abseil_cpp/absl/debugging/leak_check.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Wrappers around lsan_interface functions.
-// When lsan is not linked in, these functions are not available,
-// therefore Abseil code which depends on these functions is conditioned on the
-// definition of LEAK_SANITIZER.
-#include "absl/debugging/leak_check.h"
-
-#ifndef LEAK_SANITIZER
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-bool HaveLeakSanitizer() { return false; }
-void DoIgnoreLeak(const void*) { }
-void RegisterLivePointers(const void*, size_t) { }
-void UnRegisterLivePointers(const void*, size_t) { }
-LeakCheckDisabler::LeakCheckDisabler() { }
-LeakCheckDisabler::~LeakCheckDisabler() { }
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else
-
-#include <sanitizer/lsan_interface.h>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-bool HaveLeakSanitizer() { return true; }
-void DoIgnoreLeak(const void* ptr) { __lsan_ignore_object(ptr); }
-void RegisterLivePointers(const void* ptr, size_t size) {
-  __lsan_register_root_region(ptr, size);
-}
-void UnRegisterLivePointers(const void* ptr, size_t size) {
-  __lsan_unregister_root_region(ptr, size);
-}
-LeakCheckDisabler::LeakCheckDisabler() { __lsan_disable(); }
-LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); }
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // LEAK_SANITIZER
diff --git a/third_party/abseil_cpp/absl/debugging/leak_check.h b/third_party/abseil_cpp/absl/debugging/leak_check.h
deleted file mode 100644
index 7a5a22dd1c..0000000000
--- a/third_party/abseil_cpp/absl/debugging/leak_check.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: leak_check.h
-// -----------------------------------------------------------------------------
-//
-// This file contains functions that affect leak checking behavior within
-// targets built with the LeakSanitizer (LSan), a memory leak detector that is
-// integrated within the AddressSanitizer (ASan) as an additional component, or
-// which can be used standalone. LSan and ASan are included (or can be provided)
-// as additional components for most compilers such as Clang, gcc and MSVC.
-// Note: this leak checking API is not yet supported in MSVC.
-// Leak checking is enabled by default in all ASan builds.
-//
-// See https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer
-//
-// -----------------------------------------------------------------------------
-#ifndef ABSL_DEBUGGING_LEAK_CHECK_H_
-#define ABSL_DEBUGGING_LEAK_CHECK_H_
-
-#include <cstddef>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// HaveLeakSanitizer()
-//
-// Returns true if a leak-checking sanitizer (either ASan or standalone LSan) is
-// currently built into this target.
-bool HaveLeakSanitizer();
-
-// DoIgnoreLeak()
-//
-// Implements `IgnoreLeak()` below. This function should usually
-// not be called directly; calling `IgnoreLeak()` is preferred.
-void DoIgnoreLeak(const void* ptr);
-
-// IgnoreLeak()
-//
-// Instruct the leak sanitizer to ignore leak warnings on the object referenced
-// by the passed pointer, as well as all heap objects transitively referenced
-// by it. The passed object pointer can point to either the beginning of the
-// object or anywhere within it.
-//
-// Example:
-//
-//   static T* obj = IgnoreLeak(new T(...));
-//
-// If the passed `ptr` does not point to an actively allocated object at the
-// time `IgnoreLeak()` is called, the call is a no-op; if it is actively
-// allocated, the object must not get deallocated later.
-//
-template <typename T>
-T* IgnoreLeak(T* ptr) {
-  DoIgnoreLeak(ptr);
-  return ptr;
-}
-
-// LeakCheckDisabler
-//
-// This helper class indicates that any heap allocations done in the code block
-// covered by the scoped object, which should be allocated on the stack, will
-// not be reported as leaks. Leak check disabling will occur within the code
-// block and any nested function calls within the code block.
-//
-// Example:
-//
-//   void Foo() {
-//     LeakCheckDisabler disabler;
-//     ... code that allocates objects whose leaks should be ignored ...
-//   }
-//
-// REQUIRES: Destructor runs in same thread as constructor
-class LeakCheckDisabler {
- public:
-  LeakCheckDisabler();
-  LeakCheckDisabler(const LeakCheckDisabler&) = delete;
-  LeakCheckDisabler& operator=(const LeakCheckDisabler&) = delete;
-  ~LeakCheckDisabler();
-};
-
-// RegisterLivePointers()
-//
-// Registers `ptr[0,size-1]` as pointers to memory that is still actively being
-// referenced and for which leak checking should be ignored. This function is
-// useful if you store pointers in mapped memory, for memory ranges that we know
-// are correct but for which normal analysis would flag as leaked code.
-void RegisterLivePointers(const void* ptr, size_t size);
-
-// UnRegisterLivePointers()
-//
-// Deregisters the pointers previously marked as active in
-// `RegisterLivePointers()`, enabling leak checking of those pointers.
-void UnRegisterLivePointers(const void* ptr, size_t size);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_LEAK_CHECK_H_
diff --git a/third_party/abseil_cpp/absl/debugging/leak_check_disable.cc b/third_party/abseil_cpp/absl/debugging/leak_check_disable.cc
deleted file mode 100644
index 924d6e3d54..0000000000
--- a/third_party/abseil_cpp/absl/debugging/leak_check_disable.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Disable LeakSanitizer when this file is linked in.
-// This function overrides __lsan_is_turned_off from sanitizer/lsan_interface.h
-extern "C" int __lsan_is_turned_off();
-extern "C" int __lsan_is_turned_off() {
-  return 1;
-}
diff --git a/third_party/abseil_cpp/absl/debugging/leak_check_fail_test.cc b/third_party/abseil_cpp/absl/debugging/leak_check_fail_test.cc
deleted file mode 100644
index c49b81a9d9..0000000000
--- a/third_party/abseil_cpp/absl/debugging/leak_check_fail_test.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <memory>
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/debugging/leak_check.h"
-
-namespace {
-
-TEST(LeakCheckTest, LeakMemory) {
-  // This test is expected to cause lsan failures on program exit. Therefore the
-  // test will be run only by leak_check_test.sh, which will verify a
-  // failed exit code.
-
-  char* foo = strdup("lsan should complain about this leaked string");
-  ABSL_RAW_LOG(INFO, "Should detect leaked string %s", foo);
-}
-
-TEST(LeakCheckTest, LeakMemoryAfterDisablerScope) {
-  // This test is expected to cause lsan failures on program exit. Therefore the
-  // test will be run only by external_leak_check_test.sh, which will verify a
-  // failed exit code.
-  { absl::LeakCheckDisabler disabler; }
-  char* foo = strdup("lsan should also complain about this leaked string");
-  ABSL_RAW_LOG(INFO, "Re-enabled leak detection.Should detect leaked string %s",
-               foo);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/debugging/leak_check_test.cc b/third_party/abseil_cpp/absl/debugging/leak_check_test.cc
deleted file mode 100644
index b5cc487488..0000000000
--- a/third_party/abseil_cpp/absl/debugging/leak_check_test.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/debugging/leak_check.h"
-
-namespace {
-
-TEST(LeakCheckTest, DetectLeakSanitizer) {
-#ifdef ABSL_EXPECT_LEAK_SANITIZER
-  EXPECT_TRUE(absl::HaveLeakSanitizer());
-#else
-  EXPECT_FALSE(absl::HaveLeakSanitizer());
-#endif
-}
-
-TEST(LeakCheckTest, IgnoreLeakSuppressesLeakedMemoryErrors) {
-  auto foo = absl::IgnoreLeak(new std::string("some ignored leaked string"));
-  ABSL_RAW_LOG(INFO, "Ignoring leaked string %s", foo->c_str());
-}
-
-TEST(LeakCheckTest, LeakCheckDisablerIgnoresLeak) {
-  absl::LeakCheckDisabler disabler;
-  auto foo = new std::string("some string leaked while checks are disabled");
-  ABSL_RAW_LOG(INFO, "Ignoring leaked string %s", foo->c_str());
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/debugging/stacktrace.cc b/third_party/abseil_cpp/absl/debugging/stacktrace.cc
deleted file mode 100644
index 1f7c7d82b2..0000000000
--- a/third_party/abseil_cpp/absl/debugging/stacktrace.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Produce stack trace.
-//
-// There are three different ways we can try to get the stack trace:
-//
-// 1) Our hand-coded stack-unwinder.  This depends on a certain stack
-//    layout, which is used by gcc (and those systems using a
-//    gcc-compatible ABI) on x86 systems, at least since gcc 2.95.
-//    It uses the frame pointer to do its work.
-//
-// 2) The libunwind library.  This is still in development, and as a
-//    separate library adds a new dependency, but doesn't need a frame
-//    pointer.  It also doesn't call malloc.
-//
-// 3) The gdb unwinder -- also the one used by the c++ exception code.
-//    It's obviously well-tested, but has a fatal flaw: it can call
-//    malloc() from the unwinder.  This is a problem because we're
-//    trying to use the unwinder to instrument malloc().
-//
-// Note: if you add a new implementation here, make sure it works
-// correctly when absl::GetStackTrace() is called with max_depth == 0.
-// Some code may do that.
-
-#include "absl/debugging/stacktrace.h"
-
-#include <atomic>
-
-#include "absl/base/attributes.h"
-#include "absl/base/port.h"
-#include "absl/debugging/internal/stacktrace_config.h"
-
-#if defined(ABSL_STACKTRACE_INL_HEADER)
-#include ABSL_STACKTRACE_INL_HEADER
-#else
-# error Cannot calculate stack trace: will need to write for your environment
-
-# include "absl/debugging/internal/stacktrace_aarch64-inl.inc"
-# include "absl/debugging/internal/stacktrace_arm-inl.inc"
-# include "absl/debugging/internal/stacktrace_generic-inl.inc"
-# include "absl/debugging/internal/stacktrace_powerpc-inl.inc"
-# include "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
-# include "absl/debugging/internal/stacktrace_win32-inl.inc"
-# include "absl/debugging/internal/stacktrace_x86-inl.inc"
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-typedef int (*Unwinder)(void**, int*, int, int, const void*, int*);
-std::atomic<Unwinder> custom;
-
-template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
-ABSL_ATTRIBUTE_ALWAYS_INLINE inline int Unwind(void** result, int* sizes,
-                                               int max_depth, int skip_count,
-                                               const void* uc,
-                                               int* min_dropped_frames) {
-  Unwinder f = &UnwindImpl<IS_STACK_FRAMES, IS_WITH_CONTEXT>;
-  Unwinder g = custom.load(std::memory_order_acquire);
-  if (g != nullptr) f = g;
-
-  // Add 1 to skip count for the unwinder function itself
-  int size = (*f)(result, sizes, max_depth, skip_count + 1, uc,
-                  min_dropped_frames);
-  // To disable tail call to (*f)(...)
-  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
-  return size;
-}
-
-}  // anonymous namespace
-
-ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackFrames(
-    void** result, int* sizes, int max_depth, int skip_count) {
-  return Unwind<true, false>(result, sizes, max_depth, skip_count, nullptr,
-                             nullptr);
-}
-
-ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int
-GetStackFramesWithContext(void** result, int* sizes, int max_depth,
-                          int skip_count, const void* uc,
-                          int* min_dropped_frames) {
-  return Unwind<true, true>(result, sizes, max_depth, skip_count, uc,
-                            min_dropped_frames);
-}
-
-ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackTrace(
-    void** result, int max_depth, int skip_count) {
-  return Unwind<false, false>(result, nullptr, max_depth, skip_count, nullptr,
-                              nullptr);
-}
-
-ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int
-GetStackTraceWithContext(void** result, int max_depth, int skip_count,
-                         const void* uc, int* min_dropped_frames) {
-  return Unwind<false, true>(result, nullptr, max_depth, skip_count, uc,
-                             min_dropped_frames);
-}
-
-void SetStackUnwinder(Unwinder w) {
-  custom.store(w, std::memory_order_release);
-}
-
-int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip,
-                         const void* uc, int* min_dropped_frames) {
-  skip++;  // For this function
-  Unwinder f = nullptr;
-  if (sizes == nullptr) {
-    if (uc == nullptr) {
-      f = &UnwindImpl<false, false>;
-    } else {
-      f = &UnwindImpl<false, true>;
-    }
-  } else {
-    if (uc == nullptr) {
-      f = &UnwindImpl<true, false>;
-    } else {
-      f = &UnwindImpl<true, true>;
-    }
-  }
-  volatile int x = 0;
-  int n = (*f)(pcs, sizes, depth, skip, uc, min_dropped_frames);
-  x = 1; (void) x;  // To disable tail call to (*f)(...)
-  return n;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/debugging/stacktrace.h b/third_party/abseil_cpp/absl/debugging/stacktrace.h
deleted file mode 100644
index 0ec0ffdabd..0000000000
--- a/third_party/abseil_cpp/absl/debugging/stacktrace.h
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: stacktrace.h
-// -----------------------------------------------------------------------------
-//
-// This file contains routines to extract the current stack trace and associated
-// stack frames. These functions are thread-safe and async-signal-safe.
-//
-// Note that stack trace functionality is platform dependent and requires
-// additional support from the compiler/build system in most cases. (That is,
-// this functionality generally only works on platforms/builds that have been
-// specifically configured to support it.)
-//
-// Note: stack traces in Abseil that do not utilize a symbolizer will result in
-// frames consisting of function addresses rather than human-readable function
-// names. (See symbolize.h for information on symbolizing these values.)
-
-#ifndef ABSL_DEBUGGING_STACKTRACE_H_
-#define ABSL_DEBUGGING_STACKTRACE_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// GetStackFrames()
-//
-// Records program counter values for up to `max_depth` frames, skipping the
-// most recent `skip_count` stack frames, stores their corresponding values
-// and sizes in `results` and `sizes` buffers, and returns the number of frames
-// stored. (Note that the frame generated for the `absl::GetStackFrames()`
-// routine itself is also skipped.)
-//
-// Example:
-//
-//      main() { foo(); }
-//      foo() { bar(); }
-//      bar() {
-//        void* result[10];
-//        int sizes[10];
-//        int depth = absl::GetStackFrames(result, sizes, 10, 1);
-//      }
-//
-// The current stack frame would consist of three function calls: `bar()`,
-// `foo()`, and then `main()`; however, since the `GetStackFrames()` call sets
-// `skip_count` to `1`, it will skip the frame for `bar()`, the most recently
-// invoked function call. It will therefore return 2 and fill `result` with
-// program counters within the following functions:
-//
-//      result[0]       foo()
-//      result[1]       main()
-//
-// (Note: in practice, a few more entries after `main()` may be added to account
-// for startup processes.)
-//
-// Corresponding stack frame sizes will also be recorded:
-//
-//    sizes[0]       16
-//    sizes[1]       16
-//
-// (Stack frame sizes of `16` above are just for illustration purposes.)
-//
-// Stack frame sizes of 0 or less indicate that those frame sizes couldn't
-// be identified.
-//
-// This routine may return fewer stack frame entries than are
-// available. Also note that `result` and `sizes` must both be non-null.
-extern int GetStackFrames(void** result, int* sizes, int max_depth,
-                          int skip_count);
-
-// GetStackFramesWithContext()
-//
-// Records program counter values obtained from a signal handler. Records
-// program counter values for up to `max_depth` frames, skipping the most recent
-// `skip_count` stack frames, stores their corresponding values and sizes in
-// `results` and `sizes` buffers, and returns the number of frames stored. (Note
-// that the frame generated for the `absl::GetStackFramesWithContext()` routine
-// itself is also skipped.)
-//
-// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value
-// passed to a signal handler registered via the `sa_sigaction` field of a
-// `sigaction` struct. (See
-// http://man7.org/linux/man-pages/man2/sigaction.2.html.) The `uc` value may
-// help a stack unwinder to provide a better stack trace under certain
-// conditions. `uc` may safely be null.
-//
-// The `min_dropped_frames` output parameter, if non-null, points to the
-// location to note any dropped stack frames, if any, due to buffer limitations
-// or other reasons. (This value will be set to `0` if no frames were dropped.)
-// The number of total stack frames is guaranteed to be >= skip_count +
-// max_depth + *min_dropped_frames.
-extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
-                                     int skip_count, const void* uc,
-                                     int* min_dropped_frames);
-
-// GetStackTrace()
-//
-// Records program counter values for up to `max_depth` frames, skipping the
-// most recent `skip_count` stack frames, stores their corresponding values
-// in `results`, and returns the number of frames
-// stored. Note that this function is similar to `absl::GetStackFrames()`
-// except that it returns the stack trace only, and not stack frame sizes.
-//
-// Example:
-//
-//      main() { foo(); }
-//      foo() { bar(); }
-//      bar() {
-//        void* result[10];
-//        int depth = absl::GetStackTrace(result, 10, 1);
-//      }
-//
-// This produces:
-//
-//      result[0]       foo
-//      result[1]       main
-//           ....       ...
-//
-// `result` must not be null.
-extern int GetStackTrace(void** result, int max_depth, int skip_count);
-
-// GetStackTraceWithContext()
-//
-// Records program counter values obtained from a signal handler. Records
-// program counter values for up to `max_depth` frames, skipping the most recent
-// `skip_count` stack frames, stores their corresponding values in `results`,
-// and returns the number of frames stored. (Note that the frame generated for
-// the `absl::GetStackFramesWithContext()` routine itself is also skipped.)
-//
-// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value
-// passed to a signal handler registered via the `sa_sigaction` field of a
-// `sigaction` struct. (See
-// http://man7.org/linux/man-pages/man2/sigaction.2.html.) The `uc` value may
-// help a stack unwinder to provide a better stack trace under certain
-// conditions. `uc` may safely be null.
-//
-// The `min_dropped_frames` output parameter, if non-null, points to the
-// location to note any dropped stack frames, if any, due to buffer limitations
-// or other reasons. (This value will be set to `0` if no frames were dropped.)
-// The number of total stack frames is guaranteed to be >= skip_count +
-// max_depth + *min_dropped_frames.
-extern int GetStackTraceWithContext(void** result, int max_depth,
-                                    int skip_count, const void* uc,
-                                    int* min_dropped_frames);
-
-// SetStackUnwinder()
-//
-// Provides a custom function for unwinding stack frames that will be used in
-// place of the default stack unwinder when invoking the static
-// GetStack{Frames,Trace}{,WithContext}() functions above.
-//
-// The arguments passed to the unwinder function will match the
-// arguments passed to `absl::GetStackFramesWithContext()` except that sizes
-// will be non-null iff the caller is interested in frame sizes.
-//
-// If unwinder is set to null, we revert to the default stack-tracing behavior.
-//
-// *****************************************************************************
-// WARNING
-// *****************************************************************************
-//
-// absl::SetStackUnwinder is not suitable for general purpose use.  It is
-// provided for custom runtimes.
-// Some things to watch out for when calling `absl::SetStackUnwinder()`:
-//
-// (a) The unwinder may be called from within signal handlers and
-// therefore must be async-signal-safe.
-//
-// (b) Even after a custom stack unwinder has been unregistered, other
-// threads may still be in the process of using that unwinder.
-// Therefore do not clean up any state that may be needed by an old
-// unwinder.
-// *****************************************************************************
-extern void SetStackUnwinder(int (*unwinder)(void** pcs, int* sizes,
-                                             int max_depth, int skip_count,
-                                             const void* uc,
-                                             int* min_dropped_frames));
-
-// DefaultStackUnwinder()
-//
-// Records program counter values of up to `max_depth` frames, skipping the most
-// recent `skip_count` stack frames, and stores their corresponding values in
-// `pcs`. (Note that the frame generated for this call itself is also skipped.)
-// This function acts as a generic stack-unwinder; prefer usage of the more
-// specific `GetStack{Trace,Frames}{,WithContext}()` functions above.
-//
-// If you have set your own stack unwinder (with the `SetStackUnwinder()`
-// function above, you can still get the default stack unwinder by calling
-// `DefaultStackUnwinder()`, which will ignore any previously set stack unwinder
-// and use the default one instead.
-//
-// Because this function is generic, only `pcs` is guaranteed to be non-null
-// upon return. It is legal for `sizes`, `uc`, and `min_dropped_frames` to all
-// be null when called.
-//
-// The semantics are the same as the corresponding `GetStack*()` function in the
-// case where `absl::SetStackUnwinder()` was never called. Equivalents are:
-//
-//                       null sizes         |        non-nullptr sizes
-//             |==========================================================|
-//     null uc | GetStackTrace()            | GetStackFrames()            |
-// non-null uc | GetStackTraceWithContext() | GetStackFramesWithContext() |
-//             |==========================================================|
-extern int DefaultStackUnwinder(void** pcs, int* sizes, int max_depth,
-                                int skip_count, const void* uc,
-                                int* min_dropped_frames);
-
-namespace debugging_internal {
-// Returns true for platforms which are expected to have functioning stack trace
-// implementations. Intended to be used for tests which want to exclude
-// verification of logic known to be broken because stack traces are not
-// working.
-extern bool StackTraceWorksForTest();
-}  // namespace debugging_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_STACKTRACE_H_
diff --git a/third_party/abseil_cpp/absl/debugging/symbolize.cc b/third_party/abseil_cpp/absl/debugging/symbolize.cc
deleted file mode 100644
index 5e4a25d69d..0000000000
--- a/third_party/abseil_cpp/absl/debugging/symbolize.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/debugging/symbolize.h"
-
-#ifdef _WIN32
-#include <winapifamily.h>
-#if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)) || \
-    WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
-// UWP doesn't have access to win32 APIs.
-#define ABSL_INTERNAL_HAVE_SYMBOLIZE_WIN32
-#endif
-#endif
-
-#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE)
-#include "absl/debugging/symbolize_elf.inc"
-#elif defined(ABSL_INTERNAL_HAVE_SYMBOLIZE_WIN32)
-// The Windows Symbolizer only works if PDB files containing the debug info
-// are available to the program at runtime.
-#include "absl/debugging/symbolize_win32.inc"
-#elif defined(__APPLE__)
-#include "absl/debugging/symbolize_darwin.inc"
-#else
-#include "absl/debugging/symbolize_unimplemented.inc"
-#endif
diff --git a/third_party/abseil_cpp/absl/debugging/symbolize.h b/third_party/abseil_cpp/absl/debugging/symbolize.h
deleted file mode 100644
index 43d93a8682..0000000000
--- a/third_party/abseil_cpp/absl/debugging/symbolize.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: symbolize.h
-// -----------------------------------------------------------------------------
-//
-// This file configures the Abseil symbolizer for use in converting instruction
-// pointer addresses (program counters) into human-readable names (function
-// calls, etc.) within Abseil code.
-//
-// The symbolizer may be invoked from several sources:
-//
-//   * Implicitly, through the installation of an Abseil failure signal handler.
-//     (See failure_signal_handler.h for more information.)
-//   * By calling `Symbolize()` directly on a program counter you obtain through
-//     `absl::GetStackTrace()` or `absl::GetStackFrames()`. (See stacktrace.h
-//     for more information.
-//   * By calling `Symbolize()` directly on a program counter you obtain through
-//     other means (which would be platform-dependent).
-//
-// In all of the above cases, the symbolizer must first be initialized before
-// any program counter values can be symbolized. If you are installing a failure
-// signal handler, initialize the symbolizer before you do so.
-//
-// Example:
-//
-//   int main(int argc, char** argv) {
-//     // Initialize the Symbolizer before installing the failure signal handler
-//     absl::InitializeSymbolizer(argv[0]);
-//
-//     // Now you may install the failure signal handler
-//     absl::FailureSignalHandlerOptions options;
-//     absl::InstallFailureSignalHandler(options);
-//
-//     // Start running your main program
-//     ...
-//     return 0;
-//  }
-//
-#ifndef ABSL_DEBUGGING_SYMBOLIZE_H_
-#define ABSL_DEBUGGING_SYMBOLIZE_H_
-
-#include "absl/debugging/internal/symbolize.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// InitializeSymbolizer()
-//
-// Initializes the program counter symbolizer, given the path of the program
-// (typically obtained through `main()`s `argv[0]`). The Abseil symbolizer
-// allows you to read program counters (instruction pointer values) using their
-// human-readable names within output such as stack traces.
-//
-// Example:
-//
-// int main(int argc, char *argv[]) {
-//   absl::InitializeSymbolizer(argv[0]);
-//   // Now you can use the symbolizer
-// }
-void InitializeSymbolizer(const char* argv0);
-//
-// Symbolize()
-//
-// Symbolizes a program counter (instruction pointer value) `pc` and, on
-// success, writes the name to `out`. The symbol name is demangled, if possible.
-// Note that the symbolized name may be truncated and will be NUL-terminated.
-// Demangling is supported for symbols generated by GCC 3.x or newer). Returns
-// `false` on failure.
-//
-// Example:
-//
-//   // Print a program counter and its symbol name.
-//   static void DumpPCAndSymbol(void *pc) {
-//     char tmp[1024];
-//     const char *symbol = "(unknown)";
-//     if (absl::Symbolize(pc, tmp, sizeof(tmp))) {
-//       symbol = tmp;
-//     }
-//     absl::PrintF("%p  %s\n", pc, symbol);
-//  }
-bool Symbolize(const void *pc, char *out, int out_size);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_DEBUGGING_SYMBOLIZE_H_
diff --git a/third_party/abseil_cpp/absl/debugging/symbolize_darwin.inc b/third_party/abseil_cpp/absl/debugging/symbolize_darwin.inc
deleted file mode 100644
index 443ce9efc4..0000000000
--- a/third_party/abseil_cpp/absl/debugging/symbolize_darwin.inc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cxxabi.h>
-#include <execinfo.h>
-
-#include <algorithm>
-#include <cstring>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/debugging/internal/demangle.h"
-#include "absl/strings/numbers.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-void InitializeSymbolizer(const char*) {}
-
-namespace debugging_internal {
-namespace {
-
-static std::string GetSymbolString(absl::string_view backtrace_line) {
-  // Example Backtrace lines:
-  // 0   libimaging_shared.dylib             0x018c152a
-  // _ZNSt11_Deque_baseIN3nik7mediadb4PageESaIS2_EE17_M_initialize_mapEm + 3478
-  //
-  // or
-  // 0   libimaging_shared.dylib             0x0000000001895c39
-  // _ZN3nik4util19register_shared_ptrINS_3gpu7TextureEEEvPKvS5_ + 39
-  //
-  // or
-  // 0   mysterious_app                      0x0124000120120009 main + 17
-  auto address_pos = backtrace_line.find(" 0x");
-  if (address_pos == absl::string_view::npos) return std::string();
-  absl::string_view symbol_view = backtrace_line.substr(address_pos + 1);
-
-  auto space_pos = symbol_view.find(" ");
-  if (space_pos == absl::string_view::npos) return std::string();
-  symbol_view = symbol_view.substr(space_pos + 1);  // to mangled symbol
-
-  auto plus_pos = symbol_view.find(" + ");
-  if (plus_pos == absl::string_view::npos) return std::string();
-  symbol_view = symbol_view.substr(0, plus_pos);  // strip remainng
-
-  return std::string(symbol_view);
-}
-
-}  // namespace
-}  // namespace debugging_internal
-
-bool Symbolize(const void* pc, char* out, int out_size) {
-  if (out_size <= 0 || pc == nullptr) {
-    out = nullptr;
-    return false;
-  }
-
-  // This allocates a char* array.
-  char** frame_strings = backtrace_symbols(const_cast<void**>(&pc), 1);
-
-  if (frame_strings == nullptr) return false;
-
-  std::string symbol = debugging_internal::GetSymbolString(frame_strings[0]);
-  free(frame_strings);
-
-  char tmp_buf[1024];
-  if (debugging_internal::Demangle(symbol.c_str(), tmp_buf, sizeof(tmp_buf))) {
-    size_t len = strlen(tmp_buf);
-    if (len + 1 <= static_cast<size_t>(out_size)) {  // +1 for '\0'
-      assert(len < sizeof(tmp_buf));
-      memmove(out, tmp_buf, len + 1);
-    }
-  } else {
-    strncpy(out, symbol.c_str(), out_size);
-  }
-
-  if (out[out_size - 1] != '\0') {
-    // strncpy() does not '\0' terminate when it truncates.
-    static constexpr char kEllipsis[] = "...";
-    int ellipsis_size = std::min<int>(sizeof(kEllipsis) - 1, out_size - 1);
-    memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size);
-    out[out_size - 1] = '\0';
-  }
-
-  return true;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/debugging/symbolize_elf.inc b/third_party/abseil_cpp/absl/debugging/symbolize_elf.inc
deleted file mode 100644
index f4d5727bde..0000000000
--- a/third_party/abseil_cpp/absl/debugging/symbolize_elf.inc
+++ /dev/null
@@ -1,1560 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This library provides Symbolize() function that symbolizes program
-// counters to their corresponding symbol names on linux platforms.
-// This library has a minimal implementation of an ELF symbol table
-// reader (i.e. it doesn't depend on libelf, etc.).
-//
-// The algorithm used in Symbolize() is as follows.
-//
-//   1. Go through a list of maps in /proc/self/maps and find the map
-//   containing the program counter.
-//
-//   2. Open the mapped file and find a regular symbol table inside.
-//   Iterate over symbols in the symbol table and look for the symbol
-//   containing the program counter.  If such a symbol is found,
-//   obtain the symbol name, and demangle the symbol if possible.
-//   If the symbol isn't found in the regular symbol table (binary is
-//   stripped), try the same thing with a dynamic symbol table.
-//
-// Note that Symbolize() is originally implemented to be used in
-// signal handlers, hence it doesn't use malloc() and other unsafe
-// operations.  It should be both thread-safe and async-signal-safe.
-//
-// Implementation note:
-//
-// We don't use heaps but only use stacks.  We want to reduce the
-// stack consumption so that the symbolizer can run on small stacks.
-//
-// Here are some numbers collected with GCC 4.1.0 on x86:
-// - sizeof(Elf32_Sym)  = 16
-// - sizeof(Elf32_Shdr) = 40
-// - sizeof(Elf64_Sym)  = 24
-// - sizeof(Elf64_Shdr) = 64
-//
-// This implementation is intended to be async-signal-safe but uses some
-// functions which are not guaranteed to be so, such as memchr() and
-// memmove().  We assume they are async-signal-safe.
-
-#include <dlfcn.h>
-#include <elf.h>
-#include <fcntl.h>
-#include <link.h>  // For ElfW() macro.
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <array>
-#include <atomic>
-#include <cerrno>
-#include <cinttypes>
-#include <climits>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "absl/base/casts.h"
-#include "absl/base/dynamic_annotations.h"
-#include "absl/base/internal/low_level_alloc.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/base/port.h"
-#include "absl/debugging/internal/demangle.h"
-#include "absl/debugging/internal/vdso_support.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Value of argv[0]. Used by MaybeInitializeObjFile().
-static char *argv0_value = nullptr;
-
-void InitializeSymbolizer(const char *argv0) {
-#ifdef ABSL_HAVE_VDSO_SUPPORT
-  // We need to make sure VDSOSupport::Init() is called before any setuid or
-  // chroot calls, so InitializeSymbolizer() should be called very early in the
-  // life of a program.
-  absl::debugging_internal::VDSOSupport::Init();
-#endif
-  if (argv0_value != nullptr) {
-    free(argv0_value);
-    argv0_value = nullptr;
-  }
-  if (argv0 != nullptr && argv0[0] != '\0') {
-    argv0_value = strdup(argv0);
-  }
-}
-
-namespace debugging_internal {
-namespace {
-
-// Re-runs fn until it doesn't cause EINTR.
-#define NO_INTR(fn) \
-  do {              \
-  } while ((fn) < 0 && errno == EINTR)
-
-// On Linux, ELF_ST_* are defined in <linux/elf.h>.  To make this portable
-// we define our own ELF_ST_BIND and ELF_ST_TYPE if not available.
-#ifndef ELF_ST_BIND
-#define ELF_ST_BIND(info) (((unsigned char)(info)) >> 4)
-#endif
-
-#ifndef ELF_ST_TYPE
-#define ELF_ST_TYPE(info) (((unsigned char)(info)) & 0xF)
-#endif
-
-// Some platforms use a special .opd section to store function pointers.
-const char kOpdSectionName[] = ".opd";
-
-#if (defined(__powerpc__) && !(_CALL_ELF > 1)) || defined(__ia64)
-// Use opd section for function descriptors on these platforms, the function
-// address is the first word of the descriptor.
-enum { kPlatformUsesOPDSections = 1 };
-#else  // not PPC or IA64
-enum { kPlatformUsesOPDSections = 0 };
-#endif
-
-// This works for PowerPC & IA64 only.  A function descriptor consist of two
-// pointers and the first one is the function's entry.
-const size_t kFunctionDescriptorSize = sizeof(void *) * 2;
-
-const int kMaxDecorators = 10;  // Seems like a reasonable upper limit.
-
-struct InstalledSymbolDecorator {
-  SymbolDecorator fn;
-  void *arg;
-  int ticket;
-};
-
-int g_num_decorators;
-InstalledSymbolDecorator g_decorators[kMaxDecorators];
-
-struct FileMappingHint {
-  const void *start;
-  const void *end;
-  uint64_t offset;
-  const char *filename;
-};
-
-// Protects g_decorators.
-// We are using SpinLock and not a Mutex here, because we may be called
-// from inside Mutex::Lock itself, and it prohibits recursive calls.
-// This happens in e.g. base/stacktrace_syscall_unittest.
-// Moreover, we are using only TryLock(), if the decorator list
-// is being modified (is busy), we skip all decorators, and possibly
-// loose some info. Sorry, that's the best we could do.
-ABSL_CONST_INIT absl::base_internal::SpinLock g_decorators_mu(
-    absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY);
-
-const int kMaxFileMappingHints = 8;
-int g_num_file_mapping_hints;
-FileMappingHint g_file_mapping_hints[kMaxFileMappingHints];
-// Protects g_file_mapping_hints.
-ABSL_CONST_INIT absl::base_internal::SpinLock g_file_mapping_mu(
-    absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY);
-
-// Async-signal-safe function to zero a buffer.
-// memset() is not guaranteed to be async-signal-safe.
-static void SafeMemZero(void* p, size_t size) {
-  unsigned char *c = static_cast<unsigned char *>(p);
-  while (size--) {
-    *c++ = 0;
-  }
-}
-
-struct ObjFile {
-  ObjFile()
-      : filename(nullptr),
-        start_addr(nullptr),
-        end_addr(nullptr),
-        offset(0),
-        fd(-1),
-        elf_type(-1) {
-    SafeMemZero(&elf_header, sizeof(elf_header));
-    SafeMemZero(&phdr[0], sizeof(phdr));
-  }
-
-  char *filename;
-  const void *start_addr;
-  const void *end_addr;
-  uint64_t offset;
-
-  // The following fields are initialized on the first access to the
-  // object file.
-  int fd;
-  int elf_type;
-  ElfW(Ehdr) elf_header;
-
-  // PT_LOAD program header describing executable code.
-  // Normally we expect just one, but SWIFT binaries have two.
-  std::array<ElfW(Phdr), 2> phdr;
-};
-
-// Build 4-way associative cache for symbols. Within each cache line, symbols
-// are replaced in LRU order.
-enum {
-  ASSOCIATIVITY = 4,
-};
-struct SymbolCacheLine {
-  const void *pc[ASSOCIATIVITY];
-  char *name[ASSOCIATIVITY];
-
-  // age[i] is incremented when a line is accessed. it's reset to zero if the
-  // i'th entry is read.
-  uint32_t age[ASSOCIATIVITY];
-};
-
-// ---------------------------------------------------------------
-// An async-signal-safe arena for LowLevelAlloc
-static std::atomic<base_internal::LowLevelAlloc::Arena *> g_sig_safe_arena;
-
-static base_internal::LowLevelAlloc::Arena *SigSafeArena() {
-  return g_sig_safe_arena.load(std::memory_order_acquire);
-}
-
-static void InitSigSafeArena() {
-  if (SigSafeArena() == nullptr) {
-    base_internal::LowLevelAlloc::Arena *new_arena =
-        base_internal::LowLevelAlloc::NewArena(
-            base_internal::LowLevelAlloc::kAsyncSignalSafe);
-    base_internal::LowLevelAlloc::Arena *old_value = nullptr;
-    if (!g_sig_safe_arena.compare_exchange_strong(old_value, new_arena,
-                                                  std::memory_order_release,
-                                                  std::memory_order_relaxed)) {
-      // We lost a race to allocate an arena; deallocate.
-      base_internal::LowLevelAlloc::DeleteArena(new_arena);
-    }
-  }
-}
-
-// ---------------------------------------------------------------
-// An AddrMap is a vector of ObjFile, using SigSafeArena() for allocation.
-
-class AddrMap {
- public:
-  AddrMap() : size_(0), allocated_(0), obj_(nullptr) {}
-  ~AddrMap() { base_internal::LowLevelAlloc::Free(obj_); }
-  int Size() const { return size_; }
-  ObjFile *At(int i) { return &obj_[i]; }
-  ObjFile *Add();
-  void Clear();
-
- private:
-  int size_;       // count of valid elements (<= allocated_)
-  int allocated_;  // count of allocated elements
-  ObjFile *obj_;   // array of allocated_ elements
-  AddrMap(const AddrMap &) = delete;
-  AddrMap &operator=(const AddrMap &) = delete;
-};
-
-void AddrMap::Clear() {
-  for (int i = 0; i != size_; i++) {
-    At(i)->~ObjFile();
-  }
-  size_ = 0;
-}
-
-ObjFile *AddrMap::Add() {
-  if (size_ == allocated_) {
-    int new_allocated = allocated_ * 2 + 50;
-    ObjFile *new_obj_ =
-        static_cast<ObjFile *>(base_internal::LowLevelAlloc::AllocWithArena(
-            new_allocated * sizeof(*new_obj_), SigSafeArena()));
-    if (obj_) {
-      memcpy(new_obj_, obj_, allocated_ * sizeof(*new_obj_));
-      base_internal::LowLevelAlloc::Free(obj_);
-    }
-    obj_ = new_obj_;
-    allocated_ = new_allocated;
-  }
-  return new (&obj_[size_++]) ObjFile;
-}
-
-// ---------------------------------------------------------------
-
-enum FindSymbolResult { SYMBOL_NOT_FOUND = 1, SYMBOL_TRUNCATED, SYMBOL_FOUND };
-
-class Symbolizer {
- public:
-  Symbolizer();
-  ~Symbolizer();
-  const char *GetSymbol(const void *const pc);
-
- private:
-  char *CopyString(const char *s) {
-    int len = strlen(s);
-    char *dst = static_cast<char *>(
-        base_internal::LowLevelAlloc::AllocWithArena(len + 1, SigSafeArena()));
-    ABSL_RAW_CHECK(dst != nullptr, "out of memory");
-    memcpy(dst, s, len + 1);
-    return dst;
-  }
-  ObjFile *FindObjFile(const void *const start,
-                       size_t size) ABSL_ATTRIBUTE_NOINLINE;
-  static bool RegisterObjFile(const char *filename,
-                              const void *const start_addr,
-                              const void *const end_addr, uint64_t offset,
-                              void *arg);
-  SymbolCacheLine *GetCacheLine(const void *const pc);
-  const char *FindSymbolInCache(const void *const pc);
-  const char *InsertSymbolInCache(const void *const pc, const char *name);
-  void AgeSymbols(SymbolCacheLine *line);
-  void ClearAddrMap();
-  FindSymbolResult GetSymbolFromObjectFile(const ObjFile &obj,
-                                           const void *const pc,
-                                           const ptrdiff_t relocation,
-                                           char *out, int out_size,
-                                           char *tmp_buf, int tmp_buf_size);
-
-  enum {
-    SYMBOL_BUF_SIZE = 3072,
-    TMP_BUF_SIZE = 1024,
-    SYMBOL_CACHE_LINES = 128,
-  };
-
-  AddrMap addr_map_;
-
-  bool ok_;
-  bool addr_map_read_;
-
-  char symbol_buf_[SYMBOL_BUF_SIZE];
-
-  // tmp_buf_ will be used to store arrays of ElfW(Shdr) and ElfW(Sym)
-  // so we ensure that tmp_buf_ is properly aligned to store either.
-  alignas(16) char tmp_buf_[TMP_BUF_SIZE];
-  static_assert(alignof(ElfW(Shdr)) <= 16,
-                "alignment of tmp buf too small for Shdr");
-  static_assert(alignof(ElfW(Sym)) <= 16,
-                "alignment of tmp buf too small for Sym");
-
-  SymbolCacheLine symbol_cache_[SYMBOL_CACHE_LINES];
-};
-
-static std::atomic<Symbolizer *> g_cached_symbolizer;
-
-}  // namespace
-
-static int SymbolizerSize() {
-#if defined(__wasm__) || defined(__asmjs__)
-  int pagesize = getpagesize();
-#else
-  int pagesize = sysconf(_SC_PAGESIZE);
-#endif
-  return ((sizeof(Symbolizer) - 1) / pagesize + 1) * pagesize;
-}
-
-// Return (and set null) g_cached_symbolized_state if it is not null.
-// Otherwise return a new symbolizer.
-static Symbolizer *AllocateSymbolizer() {
-  InitSigSafeArena();
-  Symbolizer *symbolizer =
-      g_cached_symbolizer.exchange(nullptr, std::memory_order_acquire);
-  if (symbolizer != nullptr) {
-    return symbolizer;
-  }
-  return new (base_internal::LowLevelAlloc::AllocWithArena(
-      SymbolizerSize(), SigSafeArena())) Symbolizer();
-}
-
-// Set g_cached_symbolize_state to s if it is null, otherwise
-// delete s.
-static void FreeSymbolizer(Symbolizer *s) {
-  Symbolizer *old_cached_symbolizer = nullptr;
-  if (!g_cached_symbolizer.compare_exchange_strong(old_cached_symbolizer, s,
-                                                   std::memory_order_release,
-                                                   std::memory_order_relaxed)) {
-    s->~Symbolizer();
-    base_internal::LowLevelAlloc::Free(s);
-  }
-}
-
-Symbolizer::Symbolizer() : ok_(true), addr_map_read_(false) {
-  for (SymbolCacheLine &symbol_cache_line : symbol_cache_) {
-    for (size_t j = 0; j < ABSL_ARRAYSIZE(symbol_cache_line.name); ++j) {
-      symbol_cache_line.pc[j] = nullptr;
-      symbol_cache_line.name[j] = nullptr;
-      symbol_cache_line.age[j] = 0;
-    }
-  }
-}
-
-Symbolizer::~Symbolizer() {
-  for (SymbolCacheLine &symbol_cache_line : symbol_cache_) {
-    for (char *s : symbol_cache_line.name) {
-      base_internal::LowLevelAlloc::Free(s);
-    }
-  }
-  ClearAddrMap();
-}
-
-// We don't use assert() since it's not guaranteed to be
-// async-signal-safe.  Instead we define a minimal assertion
-// macro. So far, we don't need pretty printing for __FILE__, etc.
-#define SAFE_ASSERT(expr) ((expr) ? static_cast<void>(0) : abort())
-
-// Read up to "count" bytes from file descriptor "fd" into the buffer
-// starting at "buf" while handling short reads and EINTR.  On
-// success, return the number of bytes read.  Otherwise, return -1.
-static ssize_t ReadPersistent(int fd, void *buf, size_t count) {
-  SAFE_ASSERT(fd >= 0);
-  SAFE_ASSERT(count <= SSIZE_MAX);
-  char *buf0 = reinterpret_cast<char *>(buf);
-  size_t num_bytes = 0;
-  while (num_bytes < count) {
-    ssize_t len;
-    NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes));
-    if (len < 0) {  // There was an error other than EINTR.
-      ABSL_RAW_LOG(WARNING, "read failed: errno=%d", errno);
-      return -1;
-    }
-    if (len == 0) {  // Reached EOF.
-      break;
-    }
-    num_bytes += len;
-  }
-  SAFE_ASSERT(num_bytes <= count);
-  return static_cast<ssize_t>(num_bytes);
-}
-
-// Read up to "count" bytes from "offset" in the file pointed by file
-// descriptor "fd" into the buffer starting at "buf".  On success,
-// return the number of bytes read.  Otherwise, return -1.
-static ssize_t ReadFromOffset(const int fd, void *buf, const size_t count,
-                              const off_t offset) {
-  off_t off = lseek(fd, offset, SEEK_SET);
-  if (off == (off_t)-1) {
-    ABSL_RAW_LOG(WARNING, "lseek(%d, %ju, SEEK_SET) failed: errno=%d", fd,
-                 static_cast<uintmax_t>(offset), errno);
-    return -1;
-  }
-  return ReadPersistent(fd, buf, count);
-}
-
-// Try reading exactly "count" bytes from "offset" bytes in a file
-// pointed by "fd" into the buffer starting at "buf" while handling
-// short reads and EINTR.  On success, return true. Otherwise, return
-// false.
-static bool ReadFromOffsetExact(const int fd, void *buf, const size_t count,
-                                const off_t offset) {
-  ssize_t len = ReadFromOffset(fd, buf, count, offset);
-  return len >= 0 && static_cast<size_t>(len) == count;
-}
-
-// Returns elf_header.e_type if the file pointed by fd is an ELF binary.
-static int FileGetElfType(const int fd) {
-  ElfW(Ehdr) elf_header;
-  if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
-    return -1;
-  }
-  if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) {
-    return -1;
-  }
-  return elf_header.e_type;
-}
-
-// Read the section headers in the given ELF binary, and if a section
-// of the specified type is found, set the output to this section header
-// and return true.  Otherwise, return false.
-// To keep stack consumption low, we would like this function to not get
-// inlined.
-static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(
-    const int fd, ElfW(Half) sh_num, const off_t sh_offset, ElfW(Word) type,
-    ElfW(Shdr) * out, char *tmp_buf, int tmp_buf_size) {
-  ElfW(Shdr) *buf = reinterpret_cast<ElfW(Shdr) *>(tmp_buf);
-  const int buf_entries = tmp_buf_size / sizeof(buf[0]);
-  const int buf_bytes = buf_entries * sizeof(buf[0]);
-
-  for (int i = 0; i < sh_num;) {
-    const ssize_t num_bytes_left = (sh_num - i) * sizeof(buf[0]);
-    const ssize_t num_bytes_to_read =
-        (buf_bytes > num_bytes_left) ? num_bytes_left : buf_bytes;
-    const off_t offset = sh_offset + i * sizeof(buf[0]);
-    const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, offset);
-    if (len % sizeof(buf[0]) != 0) {
-      ABSL_RAW_LOG(
-          WARNING,
-          "Reading %zd bytes from offset %ju returned %zd which is not a "
-          "multiple of %zu.",
-          num_bytes_to_read, static_cast<uintmax_t>(offset), len,
-          sizeof(buf[0]));
-      return false;
-    }
-    const ssize_t num_headers_in_buf = len / sizeof(buf[0]);
-    SAFE_ASSERT(num_headers_in_buf <= buf_entries);
-    for (int j = 0; j < num_headers_in_buf; ++j) {
-      if (buf[j].sh_type == type) {
-        *out = buf[j];
-        return true;
-      }
-    }
-    i += num_headers_in_buf;
-  }
-  return false;
-}
-
-// There is no particular reason to limit section name to 63 characters,
-// but there has (as yet) been no need for anything longer either.
-const int kMaxSectionNameLen = 64;
-
-bool ForEachSection(int fd,
-                    const std::function<bool(absl::string_view name,
-                                             const ElfW(Shdr) &)> &callback) {
-  ElfW(Ehdr) elf_header;
-  if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
-    return false;
-  }
-
-  ElfW(Shdr) shstrtab;
-  off_t shstrtab_offset =
-      (elf_header.e_shoff + elf_header.e_shentsize * elf_header.e_shstrndx);
-  if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) {
-    return false;
-  }
-
-  for (int i = 0; i < elf_header.e_shnum; ++i) {
-    ElfW(Shdr) out;
-    off_t section_header_offset =
-        (elf_header.e_shoff + elf_header.e_shentsize * i);
-    if (!ReadFromOffsetExact(fd, &out, sizeof(out), section_header_offset)) {
-      return false;
-    }
-    off_t name_offset = shstrtab.sh_offset + out.sh_name;
-    char header_name[kMaxSectionNameLen];
-    ssize_t n_read =
-        ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset);
-    if (n_read == -1) {
-      return false;
-    } else if (n_read > kMaxSectionNameLen) {
-      // Long read?
-      return false;
-    }
-
-    absl::string_view name(header_name, strnlen(header_name, n_read));
-    if (!callback(name, out)) {
-      break;
-    }
-  }
-  return true;
-}
-
-// name_len should include terminating '\0'.
-bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
-                            ElfW(Shdr) * out) {
-  char header_name[kMaxSectionNameLen];
-  if (sizeof(header_name) < name_len) {
-    ABSL_RAW_LOG(WARNING,
-                 "Section name '%s' is too long (%zu); "
-                 "section will not be found (even if present).",
-                 name, name_len);
-    // No point in even trying.
-    return false;
-  }
-
-  ElfW(Ehdr) elf_header;
-  if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
-    return false;
-  }
-
-  ElfW(Shdr) shstrtab;
-  off_t shstrtab_offset =
-      (elf_header.e_shoff + elf_header.e_shentsize * elf_header.e_shstrndx);
-  if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) {
-    return false;
-  }
-
-  for (int i = 0; i < elf_header.e_shnum; ++i) {
-    off_t section_header_offset =
-        (elf_header.e_shoff + elf_header.e_shentsize * i);
-    if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) {
-      return false;
-    }
-    off_t name_offset = shstrtab.sh_offset + out->sh_name;
-    ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset);
-    if (n_read < 0) {
-      return false;
-    } else if (static_cast<size_t>(n_read) != name_len) {
-      // Short read -- name could be at end of file.
-      continue;
-    }
-    if (memcmp(header_name, name, name_len) == 0) {
-      return true;
-    }
-  }
-  return false;
-}
-
-// Compare symbols at in the same address.
-// Return true if we should pick symbol1.
-static bool ShouldPickFirstSymbol(const ElfW(Sym) & symbol1,
-                                  const ElfW(Sym) & symbol2) {
-  // If one of the symbols is weak and the other is not, pick the one
-  // this is not a weak symbol.
-  char bind1 = ELF_ST_BIND(symbol1.st_info);
-  char bind2 = ELF_ST_BIND(symbol1.st_info);
-  if (bind1 == STB_WEAK && bind2 != STB_WEAK) return false;
-  if (bind2 == STB_WEAK && bind1 != STB_WEAK) return true;
-
-  // If one of the symbols has zero size and the other is not, pick the
-  // one that has non-zero size.
-  if (symbol1.st_size != 0 && symbol2.st_size == 0) {
-    return true;
-  }
-  if (symbol1.st_size == 0 && symbol2.st_size != 0) {
-    return false;
-  }
-
-  // If one of the symbols has no type and the other is not, pick the
-  // one that has a type.
-  char type1 = ELF_ST_TYPE(symbol1.st_info);
-  char type2 = ELF_ST_TYPE(symbol1.st_info);
-  if (type1 != STT_NOTYPE && type2 == STT_NOTYPE) {
-    return true;
-  }
-  if (type1 == STT_NOTYPE && type2 != STT_NOTYPE) {
-    return false;
-  }
-
-  // Pick the first one, if we still cannot decide.
-  return true;
-}
-
-// Return true if an address is inside a section.
-static bool InSection(const void *address, const ElfW(Shdr) * section) {
-  const char *start = reinterpret_cast<const char *>(section->sh_addr);
-  size_t size = static_cast<size_t>(section->sh_size);
-  return start <= address && address < (start + size);
-}
-
-static const char *ComputeOffset(const char *base, ptrdiff_t offset) {
-  // Note: cast to uintptr_t to avoid undefined behavior when base evaluates to
-  // zero and offset is non-zero.
-  return reinterpret_cast<const char *>(
-      reinterpret_cast<uintptr_t>(base) + offset);
-}
-
-// Read a symbol table and look for the symbol containing the
-// pc. Iterate over symbols in a symbol table and look for the symbol
-// containing "pc".  If the symbol is found, and its name fits in
-// out_size, the name is written into out and SYMBOL_FOUND is returned.
-// If the name does not fit, truncated name is written into out,
-// and SYMBOL_TRUNCATED is returned. Out is NUL-terminated.
-// If the symbol is not found, SYMBOL_NOT_FOUND is returned;
-// To keep stack consumption low, we would like this function to not get
-// inlined.
-static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
-    const void *const pc, const int fd, char *out, int out_size,
-    ptrdiff_t relocation, const ElfW(Shdr) * strtab, const ElfW(Shdr) * symtab,
-    const ElfW(Shdr) * opd, char *tmp_buf, int tmp_buf_size) {
-  if (symtab == nullptr) {
-    return SYMBOL_NOT_FOUND;
-  }
-
-  // Read multiple symbols at once to save read() calls.
-  ElfW(Sym) *buf = reinterpret_cast<ElfW(Sym) *>(tmp_buf);
-  const int buf_entries = tmp_buf_size / sizeof(buf[0]);
-
-  const int num_symbols = symtab->sh_size / symtab->sh_entsize;
-
-  // On platforms using an .opd section (PowerPC & IA64), a function symbol
-  // has the address of a function descriptor, which contains the real
-  // starting address.  However, we do not always want to use the real
-  // starting address because we sometimes want to symbolize a function
-  // pointer into the .opd section, e.g. FindSymbol(&foo,...).
-  const bool pc_in_opd =
-      kPlatformUsesOPDSections && opd != nullptr && InSection(pc, opd);
-  const bool deref_function_descriptor_pointer =
-      kPlatformUsesOPDSections && opd != nullptr && !pc_in_opd;
-
-  ElfW(Sym) best_match;
-  SafeMemZero(&best_match, sizeof(best_match));
-  bool found_match = false;
-  for (int i = 0; i < num_symbols;) {
-    off_t offset = symtab->sh_offset + i * symtab->sh_entsize;
-    const int num_remaining_symbols = num_symbols - i;
-    const int entries_in_chunk = std::min(num_remaining_symbols, buf_entries);
-    const int bytes_in_chunk = entries_in_chunk * sizeof(buf[0]);
-    const ssize_t len = ReadFromOffset(fd, buf, bytes_in_chunk, offset);
-    SAFE_ASSERT(len % sizeof(buf[0]) == 0);
-    const ssize_t num_symbols_in_buf = len / sizeof(buf[0]);
-    SAFE_ASSERT(num_symbols_in_buf <= entries_in_chunk);
-    for (int j = 0; j < num_symbols_in_buf; ++j) {
-      const ElfW(Sym) &symbol = buf[j];
-
-      // For a DSO, a symbol address is relocated by the loading address.
-      // We keep the original address for opd redirection below.
-      const char *const original_start_address =
-          reinterpret_cast<const char *>(symbol.st_value);
-      const char *start_address =
-          ComputeOffset(original_start_address, relocation);
-
-      if (deref_function_descriptor_pointer &&
-          InSection(original_start_address, opd)) {
-        // The opd section is mapped into memory.  Just dereference
-        // start_address to get the first double word, which points to the
-        // function entry.
-        start_address = *reinterpret_cast<const char *const *>(start_address);
-      }
-
-      // If pc is inside the .opd section, it points to a function descriptor.
-      const size_t size = pc_in_opd ? kFunctionDescriptorSize : symbol.st_size;
-      const void *const end_address = ComputeOffset(start_address, size);
-      if (symbol.st_value != 0 &&  // Skip null value symbols.
-          symbol.st_shndx != 0 &&  // Skip undefined symbols.
-#ifdef STT_TLS
-          ELF_ST_TYPE(symbol.st_info) != STT_TLS &&  // Skip thread-local data.
-#endif                                               // STT_TLS
-          ((start_address <= pc && pc < end_address) ||
-           (start_address == pc && pc == end_address))) {
-        if (!found_match || ShouldPickFirstSymbol(symbol, best_match)) {
-          found_match = true;
-          best_match = symbol;
-        }
-      }
-    }
-    i += num_symbols_in_buf;
-  }
-
-  if (found_match) {
-    const size_t off = strtab->sh_offset + best_match.st_name;
-    const ssize_t n_read = ReadFromOffset(fd, out, out_size, off);
-    if (n_read <= 0) {
-      // This should never happen.
-      ABSL_RAW_LOG(WARNING,
-                   "Unable to read from fd %d at offset %zu: n_read = %zd", fd,
-                   off, n_read);
-      return SYMBOL_NOT_FOUND;
-    }
-    ABSL_RAW_CHECK(n_read <= out_size, "ReadFromOffset read too much data.");
-
-    // strtab->sh_offset points into .strtab-like section that contains
-    // NUL-terminated strings: '\0foo\0barbaz\0...".
-    //
-    // sh_offset+st_name points to the start of symbol name, but we don't know
-    // how long the symbol is, so we try to read as much as we have space for,
-    // and usually over-read (i.e. there is a NUL somewhere before n_read).
-    if (memchr(out, '\0', n_read) == nullptr) {
-      // Either out_size was too small (n_read == out_size and no NUL), or
-      // we tried to read past the EOF (n_read < out_size) and .strtab is
-      // corrupt (missing terminating NUL; should never happen for valid ELF).
-      out[n_read - 1] = '\0';
-      return SYMBOL_TRUNCATED;
-    }
-    return SYMBOL_FOUND;
-  }
-
-  return SYMBOL_NOT_FOUND;
-}
-
-// Get the symbol name of "pc" from the file pointed by "fd".  Process
-// both regular and dynamic symbol tables if necessary.
-// See FindSymbol() comment for description of return value.
-FindSymbolResult Symbolizer::GetSymbolFromObjectFile(
-    const ObjFile &obj, const void *const pc, const ptrdiff_t relocation,
-    char *out, int out_size, char *tmp_buf, int tmp_buf_size) {
-  ElfW(Shdr) symtab;
-  ElfW(Shdr) strtab;
-  ElfW(Shdr) opd;
-  ElfW(Shdr) *opd_ptr = nullptr;
-
-  // On platforms using an .opd sections for function descriptor, read
-  // the section header.  The .opd section is in data segment and should be
-  // loaded but we check that it is mapped just to be extra careful.
-  if (kPlatformUsesOPDSections) {
-    if (GetSectionHeaderByName(obj.fd, kOpdSectionName,
-                               sizeof(kOpdSectionName) - 1, &opd) &&
-        FindObjFile(reinterpret_cast<const char *>(opd.sh_addr) + relocation,
-                    opd.sh_size) != nullptr) {
-      opd_ptr = &opd;
-    } else {
-      return SYMBOL_NOT_FOUND;
-    }
-  }
-
-  // Consult a regular symbol table, then fall back to the dynamic symbol table.
-  for (const auto symbol_table_type : {SHT_SYMTAB, SHT_DYNSYM}) {
-    if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum,
-                                obj.elf_header.e_shoff, symbol_table_type,
-                                &symtab, tmp_buf, tmp_buf_size)) {
-      continue;
-    }
-    if (!ReadFromOffsetExact(
-            obj.fd, &strtab, sizeof(strtab),
-            obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) {
-      continue;
-    }
-    const FindSymbolResult rc =
-        FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab,
-                   opd_ptr, tmp_buf, tmp_buf_size);
-    if (rc != SYMBOL_NOT_FOUND) {
-      return rc;
-    }
-  }
-
-  return SYMBOL_NOT_FOUND;
-}
-
-namespace {
-// Thin wrapper around a file descriptor so that the file descriptor
-// gets closed for sure.
-class FileDescriptor {
- public:
-  explicit FileDescriptor(int fd) : fd_(fd) {}
-  FileDescriptor(const FileDescriptor &) = delete;
-  FileDescriptor &operator=(const FileDescriptor &) = delete;
-
-  ~FileDescriptor() {
-    if (fd_ >= 0) {
-      NO_INTR(close(fd_));
-    }
-  }
-
-  int get() const { return fd_; }
-
- private:
-  const int fd_;
-};
-
-// Helper class for reading lines from file.
-//
-// Note: we don't use ProcMapsIterator since the object is big (it has
-// a 5k array member) and uses async-unsafe functions such as sscanf()
-// and snprintf().
-class LineReader {
- public:
-  explicit LineReader(int fd, char *buf, int buf_len)
-      : fd_(fd),
-        buf_len_(buf_len),
-        buf_(buf),
-        bol_(buf),
-        eol_(buf),
-        eod_(buf) {}
-
-  LineReader(const LineReader &) = delete;
-  LineReader &operator=(const LineReader &) = delete;
-
-  // Read '\n'-terminated line from file.  On success, modify "bol"
-  // and "eol", then return true.  Otherwise, return false.
-  //
-  // Note: if the last line doesn't end with '\n', the line will be
-  // dropped.  It's an intentional behavior to make the code simple.
-  bool ReadLine(const char **bol, const char **eol) {
-    if (BufferIsEmpty()) {  // First time.
-      const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_);
-      if (num_bytes <= 0) {  // EOF or error.
-        return false;
-      }
-      eod_ = buf_ + num_bytes;
-      bol_ = buf_;
-    } else {
-      bol_ = eol_ + 1;            // Advance to the next line in the buffer.
-      SAFE_ASSERT(bol_ <= eod_);  // "bol_" can point to "eod_".
-      if (!HasCompleteLine()) {
-        const int incomplete_line_length = eod_ - bol_;
-        // Move the trailing incomplete line to the beginning.
-        memmove(buf_, bol_, incomplete_line_length);
-        // Read text from file and append it.
-        char *const append_pos = buf_ + incomplete_line_length;
-        const int capacity_left = buf_len_ - incomplete_line_length;
-        const ssize_t num_bytes =
-            ReadPersistent(fd_, append_pos, capacity_left);
-        if (num_bytes <= 0) {  // EOF or error.
-          return false;
-        }
-        eod_ = append_pos + num_bytes;
-        bol_ = buf_;
-      }
-    }
-    eol_ = FindLineFeed();
-    if (eol_ == nullptr) {  // '\n' not found.  Malformed line.
-      return false;
-    }
-    *eol_ = '\0';  // Replace '\n' with '\0'.
-
-    *bol = bol_;
-    *eol = eol_;
-    return true;
-  }
-
- private:
-  char *FindLineFeed() const {
-    return reinterpret_cast<char *>(memchr(bol_, '\n', eod_ - bol_));
-  }
-
-  bool BufferIsEmpty() const { return buf_ == eod_; }
-
-  bool HasCompleteLine() const {
-    return !BufferIsEmpty() && FindLineFeed() != nullptr;
-  }
-
-  const int fd_;
-  const int buf_len_;
-  char *const buf_;
-  char *bol_;
-  char *eol_;
-  const char *eod_;  // End of data in "buf_".
-};
-}  // namespace
-
-// Place the hex number read from "start" into "*hex".  The pointer to
-// the first non-hex character or "end" is returned.
-static const char *GetHex(const char *start, const char *end,
-                          uint64_t *const value) {
-  uint64_t hex = 0;
-  const char *p;
-  for (p = start; p < end; ++p) {
-    int ch = *p;
-    if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') ||
-        (ch >= 'a' && ch <= 'f')) {
-      hex = (hex << 4) | (ch < 'A' ? ch - '0' : (ch & 0xF) + 9);
-    } else {  // Encountered the first non-hex character.
-      break;
-    }
-  }
-  SAFE_ASSERT(p <= end);
-  *value = hex;
-  return p;
-}
-
-static const char *GetHex(const char *start, const char *end,
-                          const void **const addr) {
-  uint64_t hex = 0;
-  const char *p = GetHex(start, end, &hex);
-  *addr = reinterpret_cast<void *>(hex);
-  return p;
-}
-
-// Normally we are only interested in "r?x" maps.
-// On the PowerPC, function pointers point to descriptors in the .opd
-// section.  The descriptors themselves are not executable code, so
-// we need to relax the check below to "r??".
-static bool ShouldUseMapping(const char *const flags) {
-  return flags[0] == 'r' && (kPlatformUsesOPDSections || flags[2] == 'x');
-}
-
-// Read /proc/self/maps and run "callback" for each mmapped file found.  If
-// "callback" returns false, stop scanning and return true. Else continue
-// scanning /proc/self/maps. Return true if no parse error is found.
-static ABSL_ATTRIBUTE_NOINLINE bool ReadAddrMap(
-    bool (*callback)(const char *filename, const void *const start_addr,
-                     const void *const end_addr, uint64_t offset, void *arg),
-    void *arg, void *tmp_buf, int tmp_buf_size) {
-  // Use /proc/self/task/<pid>/maps instead of /proc/self/maps. The latter
-  // requires kernel to stop all threads, and is significantly slower when there
-  // are 1000s of threads.
-  char maps_path[80];
-  snprintf(maps_path, sizeof(maps_path), "/proc/self/task/%d/maps", getpid());
-
-  int maps_fd;
-  NO_INTR(maps_fd = open(maps_path, O_RDONLY));
-  FileDescriptor wrapped_maps_fd(maps_fd);
-  if (wrapped_maps_fd.get() < 0) {
-    ABSL_RAW_LOG(WARNING, "%s: errno=%d", maps_path, errno);
-    return false;
-  }
-
-  // Iterate over maps and look for the map containing the pc.  Then
-  // look into the symbol tables inside.
-  LineReader reader(wrapped_maps_fd.get(), static_cast<char *>(tmp_buf),
-                    tmp_buf_size);
-  while (true) {
-    const char *cursor;
-    const char *eol;
-    if (!reader.ReadLine(&cursor, &eol)) {  // EOF or malformed line.
-      break;
-    }
-
-    const char *line = cursor;
-    const void *start_address;
-    // Start parsing line in /proc/self/maps.  Here is an example:
-    //
-    // 08048000-0804c000 r-xp 00000000 08:01 2142121    /bin/cat
-    //
-    // We want start address (08048000), end address (0804c000), flags
-    // (r-xp) and file name (/bin/cat).
-
-    // Read start address.
-    cursor = GetHex(cursor, eol, &start_address);
-    if (cursor == eol || *cursor != '-') {
-      ABSL_RAW_LOG(WARNING, "Corrupt /proc/self/maps line: %s", line);
-      return false;
-    }
-    ++cursor;  // Skip '-'.
-
-    // Read end address.
-    const void *end_address;
-    cursor = GetHex(cursor, eol, &end_address);
-    if (cursor == eol || *cursor != ' ') {
-      ABSL_RAW_LOG(WARNING, "Corrupt /proc/self/maps line: %s", line);
-      return false;
-    }
-    ++cursor;  // Skip ' '.
-
-    // Read flags.  Skip flags until we encounter a space or eol.
-    const char *const flags_start = cursor;
-    while (cursor < eol && *cursor != ' ') {
-      ++cursor;
-    }
-    // We expect at least four letters for flags (ex. "r-xp").
-    if (cursor == eol || cursor < flags_start + 4) {
-      ABSL_RAW_LOG(WARNING, "Corrupt /proc/self/maps: %s", line);
-      return false;
-    }
-
-    // Check flags.
-    if (!ShouldUseMapping(flags_start)) {
-      continue;  // We skip this map.
-    }
-    ++cursor;  // Skip ' '.
-
-    // Read file offset.
-    uint64_t offset;
-    cursor = GetHex(cursor, eol, &offset);
-    ++cursor;  // Skip ' '.
-
-    // Skip to file name.  "cursor" now points to dev.  We need to skip at least
-    // two spaces for dev and inode.
-    int num_spaces = 0;
-    while (cursor < eol) {
-      if (*cursor == ' ') {
-        ++num_spaces;
-      } else if (num_spaces >= 2) {
-        // The first non-space character after  skipping two spaces
-        // is the beginning of the file name.
-        break;
-      }
-      ++cursor;
-    }
-
-    // Check whether this entry corresponds to our hint table for the true
-    // filename.
-    bool hinted =
-        GetFileMappingHint(&start_address, &end_address, &offset, &cursor);
-    if (!hinted && (cursor == eol || cursor[0] == '[')) {
-      // not an object file, typically [vdso] or [vsyscall]
-      continue;
-    }
-    if (!callback(cursor, start_address, end_address, offset, arg)) break;
-  }
-  return true;
-}
-
-// Find the objfile mapped in address region containing [addr, addr + len).
-ObjFile *Symbolizer::FindObjFile(const void *const addr, size_t len) {
-  for (int i = 0; i < 2; ++i) {
-    if (!ok_) return nullptr;
-
-    // Read /proc/self/maps if necessary
-    if (!addr_map_read_) {
-      addr_map_read_ = true;
-      if (!ReadAddrMap(RegisterObjFile, this, tmp_buf_, TMP_BUF_SIZE)) {
-        ok_ = false;
-        return nullptr;
-      }
-    }
-
-    int lo = 0;
-    int hi = addr_map_.Size();
-    while (lo < hi) {
-      int mid = (lo + hi) / 2;
-      if (addr < addr_map_.At(mid)->end_addr) {
-        hi = mid;
-      } else {
-        lo = mid + 1;
-      }
-    }
-    if (lo != addr_map_.Size()) {
-      ObjFile *obj = addr_map_.At(lo);
-      SAFE_ASSERT(obj->end_addr > addr);
-      if (addr >= obj->start_addr &&
-          reinterpret_cast<const char *>(addr) + len <= obj->end_addr)
-        return obj;
-    }
-
-    // The address mapping may have changed since it was last read.  Retry.
-    ClearAddrMap();
-  }
-  return nullptr;
-}
-
-void Symbolizer::ClearAddrMap() {
-  for (int i = 0; i != addr_map_.Size(); i++) {
-    ObjFile *o = addr_map_.At(i);
-    base_internal::LowLevelAlloc::Free(o->filename);
-    if (o->fd >= 0) {
-      NO_INTR(close(o->fd));
-    }
-  }
-  addr_map_.Clear();
-  addr_map_read_ = false;
-}
-
-// Callback for ReadAddrMap to register objfiles in an in-memory table.
-bool Symbolizer::RegisterObjFile(const char *filename,
-                                 const void *const start_addr,
-                                 const void *const end_addr, uint64_t offset,
-                                 void *arg) {
-  Symbolizer *impl = static_cast<Symbolizer *>(arg);
-
-  // Files are supposed to be added in the increasing address order.  Make
-  // sure that's the case.
-  int addr_map_size = impl->addr_map_.Size();
-  if (addr_map_size != 0) {
-    ObjFile *old = impl->addr_map_.At(addr_map_size - 1);
-    if (old->end_addr > end_addr) {
-      ABSL_RAW_LOG(ERROR,
-                   "Unsorted addr map entry: 0x%" PRIxPTR ": %s <-> 0x%" PRIxPTR
-                   ": %s",
-                   reinterpret_cast<uintptr_t>(end_addr), filename,
-                   reinterpret_cast<uintptr_t>(old->end_addr), old->filename);
-      return true;
-    } else if (old->end_addr == end_addr) {
-      // The same entry appears twice. This sometimes happens for [vdso].
-      if (old->start_addr != start_addr ||
-          strcmp(old->filename, filename) != 0) {
-        ABSL_RAW_LOG(ERROR,
-                     "Duplicate addr 0x%" PRIxPTR ": %s <-> 0x%" PRIxPTR ": %s",
-                     reinterpret_cast<uintptr_t>(end_addr), filename,
-                     reinterpret_cast<uintptr_t>(old->end_addr), old->filename);
-      }
-      return true;
-    }
-  }
-  ObjFile *obj = impl->addr_map_.Add();
-  obj->filename = impl->CopyString(filename);
-  obj->start_addr = start_addr;
-  obj->end_addr = end_addr;
-  obj->offset = offset;
-  obj->elf_type = -1;  // filled on demand
-  obj->fd = -1;        // opened on demand
-  return true;
-}
-
-// This function wraps the Demangle function to provide an interface
-// where the input symbol is demangled in-place.
-// To keep stack consumption low, we would like this function to not
-// get inlined.
-static ABSL_ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size,
-                                                    char *tmp_buf,
-                                                    int tmp_buf_size) {
-  if (Demangle(out, tmp_buf, tmp_buf_size)) {
-    // Demangling succeeded. Copy to out if the space allows.
-    int len = strlen(tmp_buf);
-    if (len + 1 <= out_size) {  // +1 for '\0'.
-      SAFE_ASSERT(len < tmp_buf_size);
-      memmove(out, tmp_buf, len + 1);
-    }
-  }
-}
-
-SymbolCacheLine *Symbolizer::GetCacheLine(const void *const pc) {
-  uintptr_t pc0 = reinterpret_cast<uintptr_t>(pc);
-  pc0 >>= 3;  // drop the low 3 bits
-
-  // Shuffle bits.
-  pc0 ^= (pc0 >> 6) ^ (pc0 >> 12) ^ (pc0 >> 18);
-  return &symbol_cache_[pc0 % SYMBOL_CACHE_LINES];
-}
-
-void Symbolizer::AgeSymbols(SymbolCacheLine *line) {
-  for (uint32_t &age : line->age) {
-    ++age;
-  }
-}
-
-const char *Symbolizer::FindSymbolInCache(const void *const pc) {
-  if (pc == nullptr) return nullptr;
-
-  SymbolCacheLine *line = GetCacheLine(pc);
-  for (size_t i = 0; i < ABSL_ARRAYSIZE(line->pc); ++i) {
-    if (line->pc[i] == pc) {
-      AgeSymbols(line);
-      line->age[i] = 0;
-      return line->name[i];
-    }
-  }
-  return nullptr;
-}
-
-const char *Symbolizer::InsertSymbolInCache(const void *const pc,
-                                            const char *name) {
-  SAFE_ASSERT(pc != nullptr);
-
-  SymbolCacheLine *line = GetCacheLine(pc);
-  uint32_t max_age = 0;
-  int oldest_index = -1;
-  for (size_t i = 0; i < ABSL_ARRAYSIZE(line->pc); ++i) {
-    if (line->pc[i] == nullptr) {
-      AgeSymbols(line);
-      line->pc[i] = pc;
-      line->name[i] = CopyString(name);
-      line->age[i] = 0;
-      return line->name[i];
-    }
-    if (line->age[i] >= max_age) {
-      max_age = line->age[i];
-      oldest_index = i;
-    }
-  }
-
-  AgeSymbols(line);
-  ABSL_RAW_CHECK(oldest_index >= 0, "Corrupt cache");
-  base_internal::LowLevelAlloc::Free(line->name[oldest_index]);
-  line->pc[oldest_index] = pc;
-  line->name[oldest_index] = CopyString(name);
-  line->age[oldest_index] = 0;
-  return line->name[oldest_index];
-}
-
-static void MaybeOpenFdFromSelfExe(ObjFile *obj) {
-  if (memcmp(obj->start_addr, ELFMAG, SELFMAG) != 0) {
-    return;
-  }
-  int fd = open("/proc/self/exe", O_RDONLY);
-  if (fd == -1) {
-    return;
-  }
-  // Verify that contents of /proc/self/exe matches in-memory image of
-  // the binary. This can fail if the "deleted" binary is in fact not
-  // the main executable, or for binaries that have the first PT_LOAD
-  // segment smaller than 4K. We do it in four steps so that the
-  // buffer is smaller and we don't consume too much stack space.
-  const char *mem = reinterpret_cast<const char *>(obj->start_addr);
-  for (int i = 0; i < 4; ++i) {
-    char buf[1024];
-    ssize_t n = read(fd, buf, sizeof(buf));
-    if (n != sizeof(buf) || memcmp(buf, mem, sizeof(buf)) != 0) {
-      close(fd);
-      return;
-    }
-    mem += sizeof(buf);
-  }
-  obj->fd = fd;
-}
-
-static bool MaybeInitializeObjFile(ObjFile *obj) {
-  if (obj->fd < 0) {
-    obj->fd = open(obj->filename, O_RDONLY);
-
-    if (obj->fd < 0) {
-      // Getting /proc/self/exe here means that we were hinted.
-      if (strcmp(obj->filename, "/proc/self/exe") == 0) {
-        // /proc/self/exe may be inaccessible (due to setuid, etc.), so try
-        // accessing the binary via argv0.
-        if (argv0_value != nullptr) {
-          obj->fd = open(argv0_value, O_RDONLY);
-        }
-      } else {
-        MaybeOpenFdFromSelfExe(obj);
-      }
-    }
-
-    if (obj->fd < 0) {
-      ABSL_RAW_LOG(WARNING, "%s: open failed: errno=%d", obj->filename, errno);
-      return false;
-    }
-    obj->elf_type = FileGetElfType(obj->fd);
-    if (obj->elf_type < 0) {
-      ABSL_RAW_LOG(WARNING, "%s: wrong elf type: %d", obj->filename,
-                   obj->elf_type);
-      return false;
-    }
-
-    if (!ReadFromOffsetExact(obj->fd, &obj->elf_header, sizeof(obj->elf_header),
-                             0)) {
-      ABSL_RAW_LOG(WARNING, "%s: failed to read elf header", obj->filename);
-      return false;
-    }
-    const int phnum = obj->elf_header.e_phnum;
-    const int phentsize = obj->elf_header.e_phentsize;
-    size_t phoff = obj->elf_header.e_phoff;
-    size_t num_executable_load_segments = 0;
-    for (int j = 0; j < phnum; j++) {
-      ElfW(Phdr) phdr;
-      if (!ReadFromOffsetExact(obj->fd, &phdr, sizeof(phdr), phoff)) {
-        ABSL_RAW_LOG(WARNING, "%s: failed to read program header %d",
-                     obj->filename, j);
-        return false;
-      }
-      phoff += phentsize;
-      constexpr int rx = PF_X | PF_R;
-      if (phdr.p_type != PT_LOAD || (phdr.p_flags & rx) != rx) {
-        // Not a LOAD segment, or not executable code.
-        continue;
-      }
-      if (num_executable_load_segments < obj->phdr.size()) {
-        memcpy(&obj->phdr[num_executable_load_segments++], &phdr, sizeof(phdr));
-      } else {
-        ABSL_RAW_LOG(WARNING, "%s: too many executable LOAD segments",
-                     obj->filename);
-        break;
-      }
-    }
-    if (num_executable_load_segments == 0) {
-      // This object has no "r-x" LOAD segments. That's unexpected.
-      ABSL_RAW_LOG(WARNING, "%s: no executable LOAD segments", obj->filename);
-      return false;
-    }
-  }
-  return true;
-}
-
-// The implementation of our symbolization routine.  If it
-// successfully finds the symbol containing "pc" and obtains the
-// symbol name, returns pointer to that symbol. Otherwise, returns nullptr.
-// If any symbol decorators have been installed via InstallSymbolDecorator(),
-// they are called here as well.
-// To keep stack consumption low, we would like this function to not
-// get inlined.
-const char *Symbolizer::GetSymbol(const void *const pc) {
-  const char *entry = FindSymbolInCache(pc);
-  if (entry != nullptr) {
-    return entry;
-  }
-  symbol_buf_[0] = '\0';
-
-  ObjFile *const obj = FindObjFile(pc, 1);
-  ptrdiff_t relocation = 0;
-  int fd = -1;
-  if (obj != nullptr) {
-    if (MaybeInitializeObjFile(obj)) {
-      const size_t start_addr = reinterpret_cast<size_t>(obj->start_addr);
-      if (obj->elf_type == ET_DYN && start_addr >= obj->offset) {
-        // This object was relocated.
-        //
-        // For obj->offset > 0, adjust the relocation since a mapping at offset
-        // X in the file will have a start address of [true relocation]+X.
-        relocation = start_addr - obj->offset;
-
-        // Note: some binaries have multiple "rx" LOAD segments. We must
-        // find the right one.
-        ElfW(Phdr) *phdr = nullptr;
-        for (size_t j = 0; j < obj->phdr.size(); j++) {
-          ElfW(Phdr) &p = obj->phdr[j];
-          if (p.p_type != PT_LOAD) {
-            // We only expect PT_LOADs. This must be PT_NULL that we didn't
-            // write over (i.e. we exhausted all interesting PT_LOADs).
-            ABSL_RAW_CHECK(p.p_type == PT_NULL, "unexpected p_type");
-            break;
-          }
-          if (pc < reinterpret_cast<void *>(start_addr + p.p_memsz)) {
-            phdr = &p;
-            break;
-          }
-        }
-        if (phdr == nullptr) {
-          // That's unexpected. Hope for the best.
-          ABSL_RAW_LOG(
-              WARNING,
-              "%s: unable to find LOAD segment for pc: %p, start_addr: %zx",
-              obj->filename, pc, start_addr);
-        } else {
-          // Adjust relocation in case phdr.p_vaddr != 0.
-          // This happens for binaries linked with `lld --rosegment`, and for
-          // binaries linked with BFD `ld -z separate-code`.
-          relocation -= phdr->p_vaddr - phdr->p_offset;
-        }
-      }
-
-      fd = obj->fd;
-      if (GetSymbolFromObjectFile(*obj, pc, relocation, symbol_buf_,
-                                  sizeof(symbol_buf_), tmp_buf_,
-                                  sizeof(tmp_buf_)) == SYMBOL_FOUND) {
-        // Only try to demangle the symbol name if it fit into symbol_buf_.
-        DemangleInplace(symbol_buf_, sizeof(symbol_buf_), tmp_buf_,
-                        sizeof(tmp_buf_));
-      }
-    }
-  } else {
-#if ABSL_HAVE_VDSO_SUPPORT
-    VDSOSupport vdso;
-    if (vdso.IsPresent()) {
-      VDSOSupport::SymbolInfo symbol_info;
-      if (vdso.LookupSymbolByAddress(pc, &symbol_info)) {
-        // All VDSO symbols are known to be short.
-        size_t len = strlen(symbol_info.name);
-        ABSL_RAW_CHECK(len + 1 < sizeof(symbol_buf_),
-                       "VDSO symbol unexpectedly long");
-        memcpy(symbol_buf_, symbol_info.name, len + 1);
-      }
-    }
-#endif
-  }
-
-  if (g_decorators_mu.TryLock()) {
-    if (g_num_decorators > 0) {
-      SymbolDecoratorArgs decorator_args = {
-          pc,       relocation,       fd,     symbol_buf_, sizeof(symbol_buf_),
-          tmp_buf_, sizeof(tmp_buf_), nullptr};
-      for (int i = 0; i < g_num_decorators; ++i) {
-        decorator_args.arg = g_decorators[i].arg;
-        g_decorators[i].fn(&decorator_args);
-      }
-    }
-    g_decorators_mu.Unlock();
-  }
-  if (symbol_buf_[0] == '\0') {
-    return nullptr;
-  }
-  symbol_buf_[sizeof(symbol_buf_) - 1] = '\0';  // Paranoia.
-  return InsertSymbolInCache(pc, symbol_buf_);
-}
-
-bool RemoveAllSymbolDecorators(void) {
-  if (!g_decorators_mu.TryLock()) {
-    // Someone else is using decorators. Get out.
-    return false;
-  }
-  g_num_decorators = 0;
-  g_decorators_mu.Unlock();
-  return true;
-}
-
-bool RemoveSymbolDecorator(int ticket) {
-  if (!g_decorators_mu.TryLock()) {
-    // Someone else is using decorators. Get out.
-    return false;
-  }
-  for (int i = 0; i < g_num_decorators; ++i) {
-    if (g_decorators[i].ticket == ticket) {
-      while (i < g_num_decorators - 1) {
-        g_decorators[i] = g_decorators[i + 1];
-        ++i;
-      }
-      g_num_decorators = i;
-      break;
-    }
-  }
-  g_decorators_mu.Unlock();
-  return true;  // Decorator is known to be removed.
-}
-
-int InstallSymbolDecorator(SymbolDecorator decorator, void *arg) {
-  static int ticket = 0;
-
-  if (!g_decorators_mu.TryLock()) {
-    // Someone else is using decorators. Get out.
-    return -2;
-  }
-  int ret = ticket;
-  if (g_num_decorators >= kMaxDecorators) {
-    ret = -1;
-  } else {
-    g_decorators[g_num_decorators] = {decorator, arg, ticket++};
-    ++g_num_decorators;
-  }
-  g_decorators_mu.Unlock();
-  return ret;
-}
-
-bool RegisterFileMappingHint(const void *start, const void *end, uint64_t offset,
-                             const char *filename) {
-  SAFE_ASSERT(start <= end);
-  SAFE_ASSERT(filename != nullptr);
-
-  InitSigSafeArena();
-
-  if (!g_file_mapping_mu.TryLock()) {
-    return false;
-  }
-
-  bool ret = true;
-  if (g_num_file_mapping_hints >= kMaxFileMappingHints) {
-    ret = false;
-  } else {
-    // TODO(ckennelly): Move this into a string copy routine.
-    int len = strlen(filename);
-    char *dst = static_cast<char *>(
-        base_internal::LowLevelAlloc::AllocWithArena(len + 1, SigSafeArena()));
-    ABSL_RAW_CHECK(dst != nullptr, "out of memory");
-    memcpy(dst, filename, len + 1);
-
-    auto &hint = g_file_mapping_hints[g_num_file_mapping_hints++];
-    hint.start = start;
-    hint.end = end;
-    hint.offset = offset;
-    hint.filename = dst;
-  }
-
-  g_file_mapping_mu.Unlock();
-  return ret;
-}
-
-bool GetFileMappingHint(const void **start, const void **end, uint64_t *offset,
-                        const char **filename) {
-  if (!g_file_mapping_mu.TryLock()) {
-    return false;
-  }
-  bool found = false;
-  for (int i = 0; i < g_num_file_mapping_hints; i++) {
-    if (g_file_mapping_hints[i].start <= *start &&
-        *end <= g_file_mapping_hints[i].end) {
-      // We assume that the start_address for the mapping is the base
-      // address of the ELF section, but when [start_address,end_address) is
-      // not strictly equal to [hint.start, hint.end), that assumption is
-      // invalid.
-      //
-      // This uses the hint's start address (even though hint.start is not
-      // necessarily equal to start_address) to ensure the correct
-      // relocation is computed later.
-      *start = g_file_mapping_hints[i].start;
-      *end = g_file_mapping_hints[i].end;
-      *offset = g_file_mapping_hints[i].offset;
-      *filename = g_file_mapping_hints[i].filename;
-      found = true;
-      break;
-    }
-  }
-  g_file_mapping_mu.Unlock();
-  return found;
-}
-
-}  // namespace debugging_internal
-
-bool Symbolize(const void *pc, char *out, int out_size) {
-  // Symbolization is very slow under tsan.
-  ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN();
-  SAFE_ASSERT(out_size >= 0);
-  debugging_internal::Symbolizer *s = debugging_internal::AllocateSymbolizer();
-  const char *name = s->GetSymbol(pc);
-  bool ok = false;
-  if (name != nullptr && out_size > 0) {
-    strncpy(out, name, out_size);
-    ok = true;
-    if (out[out_size - 1] != '\0') {
-      // strncpy() does not '\0' terminate when it truncates.  Do so, with
-      // trailing ellipsis.
-      static constexpr char kEllipsis[] = "...";
-      int ellipsis_size =
-          std::min(implicit_cast<int>(strlen(kEllipsis)), out_size - 1);
-      memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size);
-      out[out_size - 1] = '\0';
-    }
-  }
-  debugging_internal::FreeSymbolizer(s);
-  ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END();
-  return ok;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-extern "C" bool AbslInternalGetFileMappingHint(const void **start,
-                                               const void **end, uint64_t *offset,
-                                               const char **filename) {
-  return absl::debugging_internal::GetFileMappingHint(start, end, offset,
-                                                      filename);
-}
diff --git a/third_party/abseil_cpp/absl/debugging/symbolize_test.cc b/third_party/abseil_cpp/absl/debugging/symbolize_test.cc
deleted file mode 100644
index a2dd4956c4..0000000000
--- a/third_party/abseil_cpp/absl/debugging/symbolize_test.cc
+++ /dev/null
@@ -1,557 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/debugging/symbolize.h"
-
-#ifndef _WIN32
-#include <fcntl.h>
-#include <sys/mman.h>
-#endif
-
-#include <cstring>
-#include <iostream>
-#include <memory>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/casts.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/per_thread_tls.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/optimization.h"
-#include "absl/debugging/internal/stack_consumption.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/string_view.h"
-
-using testing::Contains;
-
-#ifdef _WIN32
-#define ABSL_SYMBOLIZE_TEST_NOINLINE __declspec(noinline)
-#else
-#define ABSL_SYMBOLIZE_TEST_NOINLINE ABSL_ATTRIBUTE_NOINLINE
-#endif
-
-// Functions to symbolize. Use C linkage to avoid mangled names.
-extern "C" {
-ABSL_SYMBOLIZE_TEST_NOINLINE void nonstatic_func() {
-  // The next line makes this a unique function to prevent the compiler from
-  // folding identical functions together.
-  volatile int x = __LINE__;
-  static_cast<void>(x);
-  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
-}
-
-ABSL_SYMBOLIZE_TEST_NOINLINE static void static_func() {
-  // The next line makes this a unique function to prevent the compiler from
-  // folding identical functions together.
-  volatile int x = __LINE__;
-  static_cast<void>(x);
-  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
-}
-}  // extern "C"
-
-struct Foo {
-  static void func(int x);
-};
-
-// A C++ method that should have a mangled name.
-ABSL_SYMBOLIZE_TEST_NOINLINE void Foo::func(int) {
-  // The next line makes this a unique function to prevent the compiler from
-  // folding identical functions together.
-  volatile int x = __LINE__;
-  static_cast<void>(x);
-  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
-}
-
-// Create functions that will remain in different text sections in the
-// final binary when linker option "-z,keep-text-section-prefix" is used.
-int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text.unlikely) unlikely_func() {
-  return 0;
-}
-
-int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text.hot) hot_func() {
-  return 0;
-}
-
-int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text.startup) startup_func() {
-  return 0;
-}
-
-int ABSL_ATTRIBUTE_SECTION_VARIABLE(.text.exit) exit_func() {
-  return 0;
-}
-
-int /*ABSL_ATTRIBUTE_SECTION_VARIABLE(.text)*/ regular_func() {
-  return 0;
-}
-
-// Thread-local data may confuse the symbolizer, ensure that it does not.
-// Variable sizes and order are important.
-#if ABSL_PER_THREAD_TLS
-static ABSL_PER_THREAD_TLS_KEYWORD char symbolize_test_thread_small[1];
-static ABSL_PER_THREAD_TLS_KEYWORD char
-    symbolize_test_thread_big[2 * 1024 * 1024];
-#endif
-
-#if !defined(__EMSCRIPTEN__)
-// Used below to hopefully inhibit some compiler/linker optimizations
-// that may remove kHpageTextPadding, kPadding0, and kPadding1 from
-// the binary.
-static volatile bool volatile_bool = false;
-
-// Force the binary to be large enough that a THP .text remap will succeed.
-static constexpr size_t kHpageSize = 1 << 21;
-const char kHpageTextPadding[kHpageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(
-    .text) = "";
-#endif  // !defined(__EMSCRIPTEN__)
-
-static char try_symbolize_buffer[4096];
-
-// A wrapper function for absl::Symbolize() to make the unit test simple.  The
-// limit must be < sizeof(try_symbolize_buffer).  Returns null if
-// absl::Symbolize() returns false, otherwise returns try_symbolize_buffer with
-// the result of absl::Symbolize().
-static const char *TrySymbolizeWithLimit(void *pc, int limit) {
-  ABSL_RAW_CHECK(limit <= sizeof(try_symbolize_buffer),
-                 "try_symbolize_buffer is too small");
-
-  // Use the heap to facilitate heap and buffer sanitizer tools.
-  auto heap_buffer = absl::make_unique<char[]>(sizeof(try_symbolize_buffer));
-  bool found = absl::Symbolize(pc, heap_buffer.get(), limit);
-  if (found) {
-    ABSL_RAW_CHECK(strnlen(heap_buffer.get(), limit) < limit,
-                   "absl::Symbolize() did not properly terminate the string");
-    strncpy(try_symbolize_buffer, heap_buffer.get(),
-            sizeof(try_symbolize_buffer) - 1);
-    try_symbolize_buffer[sizeof(try_symbolize_buffer) - 1] = '\0';
-  }
-
-  return found ? try_symbolize_buffer : nullptr;
-}
-
-// A wrapper for TrySymbolizeWithLimit(), with a large limit.
-static const char *TrySymbolize(void *pc) {
-  return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer));
-}
-
-#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \
-    defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)
-
-TEST(Symbolize, Cached) {
-  // Compilers should give us pointers to them.
-  EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
-
-  // The name of an internal linkage symbol is not specified; allow either a
-  // mangled or an unmangled name here.
-  const char *static_func_symbol = TrySymbolize((void *)(&static_func));
-  EXPECT_TRUE(strcmp("static_func", static_func_symbol) == 0 ||
-              strcmp("static_func()", static_func_symbol) == 0);
-
-  EXPECT_TRUE(nullptr == TrySymbolize(nullptr));
-}
-
-TEST(Symbolize, Truncation) {
-  constexpr char kNonStaticFunc[] = "nonstatic_func";
-  EXPECT_STREQ("nonstatic_func",
-               TrySymbolizeWithLimit((void *)(&nonstatic_func),
-                                     strlen(kNonStaticFunc) + 1));
-  EXPECT_STREQ("nonstatic_...",
-               TrySymbolizeWithLimit((void *)(&nonstatic_func),
-                                     strlen(kNonStaticFunc) + 0));
-  EXPECT_STREQ("nonstatic...",
-               TrySymbolizeWithLimit((void *)(&nonstatic_func),
-                                     strlen(kNonStaticFunc) - 1));
-  EXPECT_STREQ("n...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 5));
-  EXPECT_STREQ("...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 4));
-  EXPECT_STREQ("..", TrySymbolizeWithLimit((void *)(&nonstatic_func), 3));
-  EXPECT_STREQ(".", TrySymbolizeWithLimit((void *)(&nonstatic_func), 2));
-  EXPECT_STREQ("", TrySymbolizeWithLimit((void *)(&nonstatic_func), 1));
-  EXPECT_EQ(nullptr, TrySymbolizeWithLimit((void *)(&nonstatic_func), 0));
-}
-
-TEST(Symbolize, SymbolizeWithDemangling) {
-  Foo::func(100);
-  EXPECT_STREQ("Foo::func()", TrySymbolize((void *)(&Foo::func)));
-}
-
-TEST(Symbolize, SymbolizeSplitTextSections) {
-  EXPECT_STREQ("unlikely_func()", TrySymbolize((void *)(&unlikely_func)));
-  EXPECT_STREQ("hot_func()", TrySymbolize((void *)(&hot_func)));
-  EXPECT_STREQ("startup_func()", TrySymbolize((void *)(&startup_func)));
-  EXPECT_STREQ("exit_func()", TrySymbolize((void *)(&exit_func)));
-  EXPECT_STREQ("regular_func()", TrySymbolize((void *)(&regular_func)));
-}
-
-// Tests that verify that Symbolize stack footprint is within some limit.
-#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
-
-static void *g_pc_to_symbolize;
-static char g_symbolize_buffer[4096];
-static char *g_symbolize_result;
-
-static void SymbolizeSignalHandler(int signo) {
-  if (absl::Symbolize(g_pc_to_symbolize, g_symbolize_buffer,
-                      sizeof(g_symbolize_buffer))) {
-    g_symbolize_result = g_symbolize_buffer;
-  } else {
-    g_symbolize_result = nullptr;
-  }
-}
-
-// Call Symbolize and figure out the stack footprint of this call.
-static const char *SymbolizeStackConsumption(void *pc, int *stack_consumed) {
-  g_pc_to_symbolize = pc;
-  *stack_consumed = absl::debugging_internal::GetSignalHandlerStackConsumption(
-      SymbolizeSignalHandler);
-  return g_symbolize_result;
-}
-
-static int GetStackConsumptionUpperLimit() {
-  // Symbolize stack consumption should be within 2kB.
-  int stack_consumption_upper_limit = 2048;
-#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
-    defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
-  // Account for sanitizer instrumentation requiring additional stack space.
-  stack_consumption_upper_limit *= 5;
-#endif
-  return stack_consumption_upper_limit;
-}
-
-TEST(Symbolize, SymbolizeStackConsumption) {
-  int stack_consumed = 0;
-
-  const char *symbol =
-      SymbolizeStackConsumption((void *)(&nonstatic_func), &stack_consumed);
-  EXPECT_STREQ("nonstatic_func", symbol);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, GetStackConsumptionUpperLimit());
-
-  // The name of an internal linkage symbol is not specified; allow either a
-  // mangled or an unmangled name here.
-  symbol = SymbolizeStackConsumption((void *)(&static_func), &stack_consumed);
-  EXPECT_TRUE(strcmp("static_func", symbol) == 0 ||
-              strcmp("static_func()", symbol) == 0);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, GetStackConsumptionUpperLimit());
-}
-
-TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {
-  Foo::func(100);
-  int stack_consumed = 0;
-
-  const char *symbol =
-      SymbolizeStackConsumption((void *)(&Foo::func), &stack_consumed);
-
-  EXPECT_STREQ("Foo::func()", symbol);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, GetStackConsumptionUpperLimit());
-}
-
-#endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
-
-#ifndef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
-// Use a 64K page size for PPC.
-const size_t kPageSize = 64 << 10;
-// We place a read-only symbols into the .text section and verify that we can
-// symbolize them and other symbols after remapping them.
-const char kPadding0[kPageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(.text) =
-    "";
-const char kPadding1[kPageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(.text) =
-    "";
-
-static int FilterElfHeader(struct dl_phdr_info *info, size_t size, void *data) {
-  for (int i = 0; i < info->dlpi_phnum; i++) {
-    if (info->dlpi_phdr[i].p_type == PT_LOAD &&
-        info->dlpi_phdr[i].p_flags == (PF_R | PF_X)) {
-      const void *const vaddr =
-          absl::bit_cast<void *>(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
-      const auto segsize = info->dlpi_phdr[i].p_memsz;
-
-      const char *self_exe;
-      if (info->dlpi_name != nullptr && info->dlpi_name[0] != '\0') {
-        self_exe = info->dlpi_name;
-      } else {
-        self_exe = "/proc/self/exe";
-      }
-
-      absl::debugging_internal::RegisterFileMappingHint(
-          vaddr, reinterpret_cast<const char *>(vaddr) + segsize,
-          info->dlpi_phdr[i].p_offset, self_exe);
-
-      return 1;
-    }
-  }
-
-  return 1;
-}
-
-TEST(Symbolize, SymbolizeWithMultipleMaps) {
-  // Force kPadding0 and kPadding1 to be linked in.
-  if (volatile_bool) {
-    ABSL_RAW_LOG(INFO, "%s", kPadding0);
-    ABSL_RAW_LOG(INFO, "%s", kPadding1);
-  }
-
-  // Verify we can symbolize everything.
-  char buf[512];
-  memset(buf, 0, sizeof(buf));
-  absl::Symbolize(kPadding0, buf, sizeof(buf));
-  EXPECT_STREQ("kPadding0", buf);
-
-  memset(buf, 0, sizeof(buf));
-  absl::Symbolize(kPadding1, buf, sizeof(buf));
-  EXPECT_STREQ("kPadding1", buf);
-
-  // Specify a hint for the executable segment.
-  dl_iterate_phdr(FilterElfHeader, nullptr);
-
-  // Reload at least one page out of kPadding0, kPadding1
-  const char *ptrs[] = {kPadding0, kPadding1};
-
-  for (const char *ptr : ptrs) {
-    const int kMapFlags = MAP_ANONYMOUS | MAP_PRIVATE;
-    void *addr = mmap(nullptr, kPageSize, PROT_READ, kMapFlags, 0, 0);
-    ASSERT_NE(addr, MAP_FAILED);
-
-    // kPadding[0-1] is full of zeroes, so we can remap anywhere within it, but
-    // we ensure there is at least a full page of padding.
-    void *remapped = reinterpret_cast<void *>(
-        reinterpret_cast<uintptr_t>(ptr + kPageSize) & ~(kPageSize - 1ULL));
-
-    const int kMremapFlags = (MREMAP_MAYMOVE | MREMAP_FIXED);
-    void *ret = mremap(addr, kPageSize, kPageSize, kMremapFlags, remapped);
-    ASSERT_NE(ret, MAP_FAILED);
-  }
-
-  // Invalidate the symbolization cache so we are forced to rely on the hint.
-  absl::Symbolize(nullptr, buf, sizeof(buf));
-
-  // Verify we can still symbolize.
-  const char *expected[] = {"kPadding0", "kPadding1"};
-  const size_t offsets[] = {0, kPageSize, 2 * kPageSize, 3 * kPageSize};
-
-  for (int i = 0; i < 2; i++) {
-    for (size_t offset : offsets) {
-      memset(buf, 0, sizeof(buf));
-      absl::Symbolize(ptrs[i] + offset, buf, sizeof(buf));
-      EXPECT_STREQ(expected[i], buf);
-    }
-  }
-}
-
-// Appends string(*args->arg) to args->symbol_buf.
-static void DummySymbolDecorator(
-    const absl::debugging_internal::SymbolDecoratorArgs *args) {
-  std::string *message = static_cast<std::string *>(args->arg);
-  strncat(args->symbol_buf, message->c_str(),
-          args->symbol_buf_size - strlen(args->symbol_buf) - 1);
-}
-
-TEST(Symbolize, InstallAndRemoveSymbolDecorators) {
-  int ticket_a;
-  std::string a_message("a");
-  EXPECT_GE(ticket_a = absl::debugging_internal::InstallSymbolDecorator(
-                DummySymbolDecorator, &a_message),
-            0);
-
-  int ticket_b;
-  std::string b_message("b");
-  EXPECT_GE(ticket_b = absl::debugging_internal::InstallSymbolDecorator(
-                DummySymbolDecorator, &b_message),
-            0);
-
-  int ticket_c;
-  std::string c_message("c");
-  EXPECT_GE(ticket_c = absl::debugging_internal::InstallSymbolDecorator(
-                DummySymbolDecorator, &c_message),
-            0);
-
-  char *address = reinterpret_cast<char *>(1);
-  EXPECT_STREQ("abc", TrySymbolize(address++));
-
-  EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_b));
-
-  EXPECT_STREQ("ac", TrySymbolize(address++));
-
-  // Cleanup: remove all remaining decorators so other stack traces don't
-  // get mystery "ac" decoration.
-  EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_a));
-  EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_c));
-}
-
-// Some versions of Clang with optimizations enabled seem to be able
-// to optimize away the .data section if no variables live in the
-// section. This variable should get placed in the .data section, and
-// the test below checks for the existence of a .data section.
-static int in_data_section = 1;
-
-TEST(Symbolize, ForEachSection) {
-  int fd = TEMP_FAILURE_RETRY(open("/proc/self/exe", O_RDONLY));
-  ASSERT_NE(fd, -1);
-
-  std::vector<std::string> sections;
-  ASSERT_TRUE(absl::debugging_internal::ForEachSection(
-      fd, [&sections](const absl::string_view name, const ElfW(Shdr) &) {
-        sections.emplace_back(name);
-        return true;
-      }));
-
-  // Check for the presence of common section names.
-  EXPECT_THAT(sections, Contains(".text"));
-  EXPECT_THAT(sections, Contains(".rodata"));
-  EXPECT_THAT(sections, Contains(".bss"));
-  ++in_data_section;
-  EXPECT_THAT(sections, Contains(".data"));
-
-  close(fd);
-}
-#endif  // !ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE
-
-// x86 specific tests.  Uses some inline assembler.
-extern "C" {
-inline void *ABSL_ATTRIBUTE_ALWAYS_INLINE inline_func() {
-  void *pc = nullptr;
-#if defined(__i386__)
-  __asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc));
-#elif defined(__x86_64__)
-  __asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc));
-#endif
-  return pc;
-}
-
-void *ABSL_ATTRIBUTE_NOINLINE non_inline_func() {
-  void *pc = nullptr;
-#if defined(__i386__)
-  __asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc));
-#elif defined(__x86_64__)
-  __asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc));
-#endif
-  return pc;
-}
-
-void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideNonInlineFunction() {
-#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE) && \
-    (defined(__i386__) || defined(__x86_64__))
-  void *pc = non_inline_func();
-  const char *symbol = TrySymbolize(pc);
-  ABSL_RAW_CHECK(symbol != nullptr, "TestWithPCInsideNonInlineFunction failed");
-  ABSL_RAW_CHECK(strcmp(symbol, "non_inline_func") == 0,
-                 "TestWithPCInsideNonInlineFunction failed");
-  std::cout << "TestWithPCInsideNonInlineFunction passed" << std::endl;
-#endif
-}
-
-void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
-#if defined(ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE) && \
-    (defined(__i386__) || defined(__x86_64__))
-  void *pc = inline_func();  // Must be inlined.
-  const char *symbol = TrySymbolize(pc);
-  ABSL_RAW_CHECK(symbol != nullptr, "TestWithPCInsideInlineFunction failed");
-  ABSL_RAW_CHECK(strcmp(symbol, __FUNCTION__) == 0,
-                 "TestWithPCInsideInlineFunction failed");
-  std::cout << "TestWithPCInsideInlineFunction passed" << std::endl;
-#endif
-}
-}
-
-// Test with a return address.
-void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() {
-#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE)
-  void *return_address = __builtin_return_address(0);
-  const char *symbol = TrySymbolize(return_address);
-  ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed");
-  ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed");
-  std::cout << "TestWithReturnAddress passed" << std::endl;
-#endif
-}
-
-#elif defined(_WIN32)
-#if !defined(ABSL_CONSUME_DLL)
-
-TEST(Symbolize, Basics) {
-  EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
-
-  // The name of an internal linkage symbol is not specified; allow either a
-  // mangled or an unmangled name here.
-  const char *static_func_symbol = TrySymbolize((void *)(&static_func));
-  ASSERT_TRUE(static_func_symbol != nullptr);
-  EXPECT_TRUE(strstr(static_func_symbol, "static_func") != nullptr);
-
-  EXPECT_TRUE(nullptr == TrySymbolize(nullptr));
-}
-
-TEST(Symbolize, Truncation) {
-  constexpr char kNonStaticFunc[] = "nonstatic_func";
-  EXPECT_STREQ("nonstatic_func",
-               TrySymbolizeWithLimit((void *)(&nonstatic_func),
-                                     strlen(kNonStaticFunc) + 1));
-  EXPECT_STREQ("nonstatic_...",
-               TrySymbolizeWithLimit((void *)(&nonstatic_func),
-                                     strlen(kNonStaticFunc) + 0));
-  EXPECT_STREQ("nonstatic...",
-               TrySymbolizeWithLimit((void *)(&nonstatic_func),
-                                     strlen(kNonStaticFunc) - 1));
-  EXPECT_STREQ("n...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 5));
-  EXPECT_STREQ("...", TrySymbolizeWithLimit((void *)(&nonstatic_func), 4));
-  EXPECT_STREQ("..", TrySymbolizeWithLimit((void *)(&nonstatic_func), 3));
-  EXPECT_STREQ(".", TrySymbolizeWithLimit((void *)(&nonstatic_func), 2));
-  EXPECT_STREQ("", TrySymbolizeWithLimit((void *)(&nonstatic_func), 1));
-  EXPECT_EQ(nullptr, TrySymbolizeWithLimit((void *)(&nonstatic_func), 0));
-}
-
-TEST(Symbolize, SymbolizeWithDemangling) {
-  const char *result = TrySymbolize((void *)(&Foo::func));
-  ASSERT_TRUE(result != nullptr);
-  EXPECT_TRUE(strstr(result, "Foo::func") != nullptr) << result;
-}
-
-#endif  // !defined(ABSL_CONSUME_DLL)
-#else  // Symbolizer unimplemented
-
-TEST(Symbolize, Unimplemented) {
-  char buf[64];
-  EXPECT_FALSE(absl::Symbolize((void *)(&nonstatic_func), buf, sizeof(buf)));
-  EXPECT_FALSE(absl::Symbolize((void *)(&static_func), buf, sizeof(buf)));
-  EXPECT_FALSE(absl::Symbolize((void *)(&Foo::func), buf, sizeof(buf)));
-}
-
-#endif
-
-int main(int argc, char **argv) {
-#if !defined(__EMSCRIPTEN__)
-  // Make sure kHpageTextPadding is linked into the binary.
-  if (volatile_bool) {
-    ABSL_RAW_LOG(INFO, "%s", kHpageTextPadding);
-  }
-#endif  // !defined(__EMSCRIPTEN__)
-
-#if ABSL_PER_THREAD_TLS
-  // Touch the per-thread variables.
-  symbolize_test_thread_small[0] = 0;
-  symbolize_test_thread_big[0] = 0;
-#endif
-
-  absl::InitializeSymbolizer(argv[0]);
-  testing::InitGoogleTest(&argc, argv);
-
-#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \
-    defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE)
-  TestWithPCInsideInlineFunction();
-  TestWithPCInsideNonInlineFunction();
-  TestWithReturnAddress();
-#endif
-
-  return RUN_ALL_TESTS();
-}
diff --git a/third_party/abseil_cpp/absl/debugging/symbolize_unimplemented.inc b/third_party/abseil_cpp/absl/debugging/symbolize_unimplemented.inc
deleted file mode 100644
index db24456b0a..0000000000
--- a/third_party/abseil_cpp/absl/debugging/symbolize_unimplemented.inc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cstdint>
-
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace debugging_internal {
-
-int InstallSymbolDecorator(SymbolDecorator, void*) { return -1; }
-bool RemoveSymbolDecorator(int) { return false; }
-bool RemoveAllSymbolDecorators(void) { return false; }
-bool RegisterFileMappingHint(const void *, const void *, uint64_t, const char *) {
-  return false;
-}
-bool GetFileMappingHint(const void **, const void **, uint64_t *, const char **) {
-  return false;
-}
-
-}  // namespace debugging_internal
-
-void InitializeSymbolizer(const char*) {}
-bool Symbolize(const void *, char *, int) { return false; }
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/debugging/symbolize_win32.inc b/third_party/abseil_cpp/absl/debugging/symbolize_win32.inc
deleted file mode 100644
index c3df46f606..0000000000
--- a/third_party/abseil_cpp/absl/debugging/symbolize_win32.inc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// See "Retrieving Symbol Information by Address":
-// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx
-
-#include <windows.h>
-
-// MSVC header dbghelp.h has a warning for an ignored typedef.
-#pragma warning(push)
-#pragma warning(disable:4091)
-#include <dbghelp.h>
-#pragma warning(pop)
-
-#pragma comment(lib, "dbghelp.lib")
-
-#include <algorithm>
-#include <cstring>
-
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-static HANDLE process = NULL;
-
-void InitializeSymbolizer(const char*) {
-  if (process != nullptr) {
-    return;
-  }
-  process = GetCurrentProcess();
-
-  // Symbols are not loaded until a reference is made requiring the
-  // symbols be loaded. This is the fastest, most efficient way to use
-  // the symbol handler.
-  SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
-  if (!SymInitialize(process, nullptr, true)) {
-    // GetLastError() returns a Win32 DWORD, but we assign to
-    // unsigned long long to simplify the ABSL_RAW_LOG case below.  The uniform
-    // initialization guarantees this is not a narrowing conversion.
-    const unsigned long long error{GetLastError()};  // NOLINT(runtime/int)
-    ABSL_RAW_LOG(FATAL, "SymInitialize() failed: %llu", error);
-  }
-}
-
-bool Symbolize(const void* pc, char* out, int out_size) {
-  if (out_size <= 0) {
-    return false;
-  }
-  alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
-  SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(buf);
-  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
-  symbol->MaxNameLen = MAX_SYM_NAME;
-  if (!SymFromAddr(process, reinterpret_cast<DWORD64>(pc), nullptr, symbol)) {
-    return false;
-  }
-  strncpy(out, symbol->Name, out_size);
-  if (out[out_size - 1] != '\0') {
-    // strncpy() does not '\0' terminate when it truncates.
-    static constexpr char kEllipsis[] = "...";
-    int ellipsis_size =
-        std::min<int>(sizeof(kEllipsis) - 1, out_size - 1);
-    memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size);
-    out[out_size - 1] = '\0';
-  }
-  return true;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/BUILD.bazel b/third_party/abseil_cpp/absl/flags/BUILD.bazel
deleted file mode 100644
index 78d6da74d8..0000000000
--- a/third_party/abseil_cpp/absl/flags/BUILD.bazel
+++ /dev/null
@@ -1,514 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "path_util",
-    hdrs = [
-        "internal/path_util.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl/flags:__pkg__",
-    ],
-    deps = [
-        "//absl/base:config",
-        "//absl/strings",
-    ],
-)
-
-cc_library(
-    name = "program_name",
-    srcs = [
-        "internal/program_name.cc",
-    ],
-    hdrs = [
-        "internal/program_name.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl/flags:__pkg__",
-    ],
-    deps = [
-        ":path_util",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/strings",
-        "//absl/synchronization",
-    ],
-)
-
-cc_library(
-    name = "config",
-    srcs = [
-        "usage_config.cc",
-    ],
-    hdrs = [
-        "config.h",
-        "usage_config.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":path_util",
-        ":program_name",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/strings",
-        "//absl/synchronization",
-    ],
-)
-
-cc_library(
-    name = "marshalling",
-    srcs = [
-        "marshalling.cc",
-    ],
-    hdrs = [
-        "marshalling.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:log_severity",
-        "//absl/strings",
-        "//absl/strings:str_format",
-    ],
-)
-
-cc_library(
-    name = "commandlineflag_internal",
-    srcs = [
-        "internal/commandlineflag.cc",
-    ],
-    hdrs = [
-        "internal/commandlineflag.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:fast_type_id",
-    ],
-)
-
-cc_library(
-    name = "commandlineflag",
-    srcs = [
-        "commandlineflag.cc",
-    ],
-    hdrs = [
-        "commandlineflag.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":commandlineflag_internal",
-        "//absl/base:config",
-        "//absl/base:fast_type_id",
-        "//absl/strings",
-        "//absl/types:optional",
-    ],
-)
-
-cc_library(
-    name = "private_handle_accessor",
-    srcs = [
-        "internal/private_handle_accessor.cc",
-    ],
-    hdrs = [
-        "internal/private_handle_accessor.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl/flags:__pkg__",
-    ],
-    deps = [
-        ":commandlineflag",
-        ":commandlineflag_internal",
-        "//absl/base:config",
-        "//absl/strings",
-    ],
-)
-
-cc_library(
-    name = "reflection",
-    srcs = [
-        "reflection.cc",
-    ],
-    hdrs = [
-        "internal/registry.h",
-        "reflection.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":commandlineflag",
-        ":commandlineflag_internal",
-        ":config",
-        ":private_handle_accessor",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/container:flat_hash_map",
-        "//absl/strings",
-        "//absl/synchronization",
-    ],
-)
-
-cc_library(
-    name = "flag_internal",
-    srcs = [
-        "internal/flag.cc",
-    ],
-    hdrs = [
-        "internal/flag.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//absl/base:__subpackages__"],
-    deps = [
-        ":commandlineflag",
-        ":commandlineflag_internal",
-        ":config",
-        ":marshalling",
-        ":reflection",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "//absl/synchronization",
-        "//absl/utility",
-    ],
-)
-
-cc_library(
-    name = "flag",
-    srcs = [
-        "flag.cc",
-    ],
-    hdrs = [
-        "declare.h",
-        "flag.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":flag_internal",
-        ":reflection",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/strings",
-    ],
-)
-
-cc_library(
-    name = "usage_internal",
-    srcs = [
-        "internal/usage.cc",
-    ],
-    hdrs = [
-        "internal/usage.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl/flags:__pkg__",
-    ],
-    deps = [
-        ":commandlineflag",
-        ":config",
-        ":flag",
-        ":flag_internal",
-        ":path_util",
-        ":private_handle_accessor",
-        ":program_name",
-        ":reflection",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/strings",
-    ],
-)
-
-cc_library(
-    name = "usage",
-    srcs = [
-        "usage.cc",
-    ],
-    hdrs = [
-        "usage.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":usage_internal",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/strings",
-        "//absl/synchronization",
-    ],
-)
-
-cc_library(
-    name = "parse",
-    srcs = ["parse.cc"],
-    hdrs = [
-        "internal/parse.h",
-        "parse.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":commandlineflag",
-        ":commandlineflag_internal",
-        ":config",
-        ":flag",
-        ":flag_internal",
-        ":private_handle_accessor",
-        ":program_name",
-        ":reflection",
-        ":usage",
-        ":usage_internal",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/strings",
-        "//absl/synchronization",
-    ],
-)
-
-############################################################################
-# Unit tests in alphabetical order.
-
-cc_test(
-    name = "commandlineflag_test",
-    size = "small",
-    srcs = [
-        "commandlineflag_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":commandlineflag",
-        ":commandlineflag_internal",
-        ":config",
-        ":flag",
-        ":private_handle_accessor",
-        ":reflection",
-        "//absl/memory",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "config_test",
-    size = "small",
-    srcs = [
-        "config_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "flag_test",
-    size = "small",
-    srcs = [
-        "flag_test.cc",
-        "flag_test_defs.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":flag",
-        ":flag_internal",
-        ":marshalling",
-        ":reflection",
-        "//absl/base:core_headers",
-        "//absl/base:malloc_internal",
-        "//absl/strings",
-        "//absl/time",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_binary(
-    name = "flag_benchmark",
-    testonly = 1,
-    srcs = [
-        "flag_benchmark.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":flag",
-        ":marshalling",
-        ":parse",
-        ":reflection",
-        "//absl/strings",
-        "//absl/time",
-        "//absl/types:optional",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "marshalling_test",
-    size = "small",
-    srcs = [
-        "marshalling_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":marshalling",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "parse_test",
-    size = "small",
-    srcs = [
-        "parse_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":flag",
-        ":parse",
-        ":reflection",
-        ":usage_internal",
-        "//absl/base:raw_logging_internal",
-        "//absl/base:scoped_set_env",
-        "//absl/strings",
-        "//absl/types:span",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "path_util_test",
-    size = "small",
-    srcs = [
-        "internal/path_util_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":path_util",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "program_name_test",
-    size = "small",
-    srcs = [
-        "internal/program_name_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":program_name",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "reflection_test",
-    size = "small",
-    srcs = [
-        "reflection_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":commandlineflag_internal",
-        ":flag",
-        ":marshalling",
-        ":reflection",
-        ":usage_internal",
-        "//absl/memory",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "usage_config_test",
-    size = "small",
-    srcs = [
-        "usage_config_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":path_util",
-        ":program_name",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "usage_test",
-    size = "small",
-    srcs = [
-        "internal/usage_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":flag",
-        ":parse",
-        ":path_util",
-        ":program_name",
-        ":reflection",
-        ":usage",
-        ":usage_internal",
-        "//absl/strings",
-        "@com_google_googletest//:gtest",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/flags/CMakeLists.txt b/third_party/abseil_cpp/absl/flags/CMakeLists.txt
deleted file mode 100644
index e5083d7b9a..0000000000
--- a/third_party/abseil_cpp/absl/flags/CMakeLists.txt
+++ /dev/null
@@ -1,451 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    flags_path_util
-  HDRS
-    "internal/path_util.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::strings
-  PUBLIC
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    flags_program_name
-  SRCS
-    "internal/program_name.cc"
-  HDRS
-    "internal/program_name.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::flags_path_util
-    absl::strings
-    absl::synchronization
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    flags_config
-  SRCS
-    "usage_config.cc"
-  HDRS
-    "config.h"
-    "usage_config.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::flags_path_util
-    absl::flags_program_name
-    absl::core_headers
-    absl::strings
-    absl::synchronization
-)
-
-absl_cc_library(
-  NAME
-    flags_marshalling
-  SRCS
-    "marshalling.cc"
-  HDRS
-    "marshalling.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::log_severity
-    absl::strings
-    absl::str_format
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    flags_commandlineflag_internal
-  SRCS
-    "internal/commandlineflag.cc"
-  HDRS
-    "internal/commandlineflag.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::fast_type_id
-)
-
-absl_cc_library(
-  NAME
-    flags_commandlineflag
-  SRCS
-    "commandlineflag.cc"
-  HDRS
-    "commandlineflag.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::fast_type_id
-    absl::flags_commandlineflag_internal
-    absl::optional
-    absl::strings
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    flags_private_handle_accessor
-  SRCS
-    "internal/private_handle_accessor.cc"
-  HDRS
-    "internal/private_handle_accessor.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::flags_commandlineflag
-    absl::flags_commandlineflag_internal
-    absl::strings
-)
-
-absl_cc_library(
-  NAME
-    flags_reflection
-  SRCS
-    "reflection.cc"
-  HDRS
-    "reflection.h"
-    "internal/registry.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::flags_commandlineflag
-    absl::flags_private_handle_accessor
-    absl::flags_config
-    absl::strings
-    absl::synchronization
-    absl::flat_hash_map
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    flags_internal
-  SRCS
-    "internal/flag.cc"
-  HDRS
-    "internal/flag.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::base
-    absl::config
-    absl::flags_commandlineflag
-    absl::flags_commandlineflag_internal
-    absl::flags_config
-    absl::flags_marshalling
-    absl::synchronization
-    absl::meta
-    absl::utility
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    flags
-  SRCS
-    "flag.cc"
-  HDRS
-    "declare.h"
-    "flag.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::flags_commandlineflag
-    absl::flags_config
-    absl::flags_internal
-    absl::flags_reflection
-    absl::base
-    absl::core_headers
-    absl::strings
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    flags_usage_internal
-  SRCS
-    "internal/usage.cc"
-  HDRS
-    "internal/usage.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::flags_config
-    absl::flags
-    absl::flags_commandlineflag
-    absl::flags_internal
-    absl::flags_path_util
-    absl::flags_private_handle_accessor
-    absl::flags_program_name
-    absl::flags_reflection
-    absl::strings
-    absl::synchronization
-)
-
-absl_cc_library(
-  NAME
-    flags_usage
-  SRCS
-    "usage.cc"
-  HDRS
-    "usage.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::flags_usage_internal
-    absl::strings
-    absl::synchronization
-)
-
-absl_cc_library(
-  NAME
-    flags_parse
-  SRCS
-    "parse.cc"
-  HDRS
-    "internal/parse.h"
-    "parse.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::flags_config
-    absl::flags
-    absl::flags_commandlineflag
-    absl::flags_commandlineflag_internal
-    absl::flags_internal
-    absl::flags_private_handle_accessor
-    absl::flags_program_name
-    absl::flags_reflection
-    absl::flags_usage
-    absl::strings
-    absl::synchronization
-)
-
-############################################################################
-# Unit tests in alpahabetical order.
-
-absl_cc_test(
-  NAME
-    flags_commandlineflag_test
-  SRCS
-    "commandlineflag_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flags
-    absl::flags_commandlineflag
-    absl::flags_commandlineflag_internal
-    absl::flags_config
-    absl::flags_private_handle_accessor
-    absl::flags_reflection
-    absl::memory
-    absl::strings
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    flags_config_test
-  SRCS
-    "config_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flags_config
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    flags_flag_test
-  SRCS
-    "flag_test.cc"
-    "flag_test_defs.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::core_headers
-    absl::flags
-    absl::flags_config
-    absl::flags_internal
-    absl::flags_marshalling
-    absl::flags_reflection
-    absl::strings
-    absl::time
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    flags_marshalling_test
-  SRCS
-    "marshalling_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flags_marshalling
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    flags_parse_test
-  SRCS
-    "parse_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flags
-    absl::flags_parse
-    absl::flags_reflection
-    absl::flags_usage_internal
-    absl::raw_logging_internal
-    absl::scoped_set_env
-    absl::span
-    absl::strings
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    flags_path_util_test
-  SRCS
-    "internal/path_util_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flags_path_util
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    flags_program_name_test
-  SRCS
-    "internal/program_name_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flags_program_name
-    absl::strings
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    flags_reflection_test
-  SRCS
-    "reflection_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flags_commandlineflag_internal
-    absl::flags
-    absl::flags_reflection
-    absl::flags_usage
-    absl::memory
-    absl::strings
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    flags_usage_config_test
-  SRCS
-    "usage_config_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flags_config
-    absl::flags_path_util
-    absl::flags_program_name
-    absl::strings
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    flags_usage_test
-  SRCS
-    "internal/usage_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::flags_config
-    absl::flags
-    absl::flags_path_util
-    absl::flags_program_name
-    absl::flags_parse
-    absl::flags_reflection
-    absl::flags_usage
-    absl::strings
-    gtest
-)
diff --git a/third_party/abseil_cpp/absl/flags/commandlineflag.cc b/third_party/abseil_cpp/absl/flags/commandlineflag.cc
deleted file mode 100644
index 9f3b4a5a28..0000000000
--- a/third_party/abseil_cpp/absl/flags/commandlineflag.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/commandlineflag.h"
-
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-bool CommandLineFlag::IsRetired() const { return false; }
-bool CommandLineFlag::ParseFrom(absl::string_view value, std::string* error) {
-  return ParseFrom(value, flags_internal::SET_FLAGS_VALUE,
-                   flags_internal::kProgrammaticChange, *error);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/commandlineflag.h b/third_party/abseil_cpp/absl/flags/commandlineflag.h
deleted file mode 100644
index f2fa08977f..0000000000
--- a/third_party/abseil_cpp/absl/flags/commandlineflag.h
+++ /dev/null
@@ -1,200 +0,0 @@
-//
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: commandlineflag.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the `CommandLineFlag`, which acts as a type-erased
-// handle for accessing metadata about the Abseil Flag in question.
-//
-// Because an actual Abseil flag is of an unspecified type, you should not
-// manipulate or interact directly with objects of that type. Instead, use the
-// CommandLineFlag type as an intermediary.
-#ifndef ABSL_FLAGS_COMMANDLINEFLAG_H_
-#define ABSL_FLAGS_COMMANDLINEFLAG_H_
-
-#include <memory>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/fast_type_id.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/optional.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-class PrivateHandleAccessor;
-}  // namespace flags_internal
-
-// CommandLineFlag
-//
-// This type acts as a type-erased handle for an instance of an Abseil Flag and
-// holds reflection information pertaining to that flag. Use CommandLineFlag to
-// access a flag's name, location, help string etc.
-//
-// To obtain an absl::CommandLineFlag, invoke `absl::FindCommandLineFlag()`
-// passing it the flag name string.
-//
-// Example:
-//
-//   // Obtain reflection handle for a flag named "flagname".
-//   const absl::CommandLineFlag* my_flag_data =
-//        absl::FindCommandLineFlag("flagname");
-//
-//   // Now you can get flag info from that reflection handle.
-//   std::string flag_location = my_flag_data->Filename();
-//   ...
-class CommandLineFlag {
- public:
-  constexpr CommandLineFlag() = default;
-
-  // Not copyable/assignable.
-  CommandLineFlag(const CommandLineFlag&) = delete;
-  CommandLineFlag& operator=(const CommandLineFlag&) = delete;
-
-  // absl::CommandLineFlag::IsOfType()
-  //
-  // Return true iff flag has type T.
-  template <typename T>
-  inline bool IsOfType() const {
-    return TypeId() == base_internal::FastTypeId<T>();
-  }
-
-  // absl::CommandLineFlag::TryGet()
-  //
-  // Attempts to retrieve the flag value. Returns value on success,
-  // absl::nullopt otherwise.
-  template <typename T>
-  absl::optional<T> TryGet() const {
-    if (IsRetired() || !IsOfType<T>()) {
-      return absl::nullopt;
-    }
-
-    // Implementation notes:
-    //
-    // We are wrapping a union around the value of `T` to serve three purposes:
-    //
-    //  1. `U.value` has correct size and alignment for a value of type `T`
-    //  2. The `U.value` constructor is not invoked since U's constructor does
-    //     not do it explicitly.
-    //  3. The `U.value` destructor is invoked since U's destructor does it
-    //     explicitly. This makes `U` a kind of RAII wrapper around non default
-    //     constructible value of T, which is destructed when we leave the
-    //     scope. We do need to destroy U.value, which is constructed by
-    //     CommandLineFlag::Read even though we left it in a moved-from state
-    //     after std::move.
-    //
-    // All of this serves to avoid requiring `T` being default constructible.
-    union U {
-      T value;
-      U() {}
-      ~U() { value.~T(); }
-    };
-    U u;
-
-    Read(&u.value);
-    // allow retired flags to be "read", so we can report invalid access.
-    if (IsRetired()) {
-      return absl::nullopt;
-    }
-    return std::move(u.value);
-  }
-
-  // absl::CommandLineFlag::Name()
-  //
-  // Returns name of this flag.
-  virtual absl::string_view Name() const = 0;
-
-  // absl::CommandLineFlag::Filename()
-  //
-  // Returns name of the file where this flag is defined.
-  virtual std::string Filename() const = 0;
-
-  // absl::CommandLineFlag::Help()
-  //
-  // Returns help message associated with this flag.
-  virtual std::string Help() const = 0;
-
-  // absl::CommandLineFlag::IsRetired()
-  //
-  // Returns true iff this object corresponds to retired flag.
-  virtual bool IsRetired() const;
-
-  // absl::CommandLineFlag::DefaultValue()
-  //
-  // Returns the default value for this flag.
-  virtual std::string DefaultValue() const = 0;
-
-  // absl::CommandLineFlag::CurrentValue()
-  //
-  // Returns the current value for this flag.
-  virtual std::string CurrentValue() const = 0;
-
-  // absl::CommandLineFlag::ParseFrom()
-  //
-  // Sets the value of the flag based on specified string `value`. If the flag
-  // was successfully set to new value, it returns true. Otherwise, sets `error`
-  // to indicate the error, leaves the flag unchanged, and returns false.
-  bool ParseFrom(absl::string_view value, std::string* error);
-
- protected:
-  ~CommandLineFlag() = default;
-
- private:
-  friend class flags_internal::PrivateHandleAccessor;
-
-  // Sets the value of the flag based on specified string `value`. If the flag
-  // was successfully set to new value, it returns true. Otherwise, sets `error`
-  // to indicate the error, leaves the flag unchanged, and returns false. There
-  // are three ways to set the flag's value:
-  //  * Update the current flag value
-  //  * Update the flag's default value
-  //  * Update the current flag value if it was never set before
-  // The mode is selected based on `set_mode` parameter.
-  virtual bool ParseFrom(absl::string_view value,
-                         flags_internal::FlagSettingMode set_mode,
-                         flags_internal::ValueSource source,
-                         std::string& error) = 0;
-
-  // Returns id of the flag's value type.
-  virtual flags_internal::FlagFastTypeId TypeId() const = 0;
-
-  // Interface to save flag to some persistent state. Returns current flag state
-  // or nullptr if flag does not support saving and restoring a state.
-  virtual std::unique_ptr<flags_internal::FlagStateInterface> SaveState() = 0;
-
-  // Copy-construct a new value of the flag's type in a memory referenced by
-  // the dst based on the current flag's value.
-  virtual void Read(void* dst) const = 0;
-
-  // To be deleted. Used to return true if flag's current value originated from
-  // command line.
-  virtual bool IsSpecifiedOnCommandLine() const = 0;
-
-  // Validates supplied value usign validator or parseflag routine
-  virtual bool ValidateInputValue(absl::string_view value) const = 0;
-
-  // Checks that flags default value can be converted to string and back to the
-  // flag's value type.
-  virtual void CheckDefaultValueParsingRoundtrip() const = 0;
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_COMMANDLINEFLAG_H_
diff --git a/third_party/abseil_cpp/absl/flags/commandlineflag_test.cc b/third_party/abseil_cpp/absl/flags/commandlineflag_test.cc
deleted file mode 100644
index 585db4ba78..0000000000
--- a/third_party/abseil_cpp/absl/flags/commandlineflag_test.cc
+++ /dev/null
@@ -1,231 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/commandlineflag.h"
-
-#include <memory>
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/flags/flag.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/flags/internal/private_handle_accessor.h"
-#include "absl/flags/reflection.h"
-#include "absl/flags/usage_config.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/match.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-
-ABSL_FLAG(int, int_flag, 201, "int_flag help");
-ABSL_FLAG(std::string, string_flag, "dflt",
-          absl::StrCat("string_flag", " help"));
-ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help");
-
-// These are only used to test default values.
-ABSL_FLAG(int, int_flag2, 201, "");
-ABSL_FLAG(std::string, string_flag2, "dflt", "");
-
-namespace {
-
-namespace flags = absl::flags_internal;
-
-class CommandLineFlagTest : public testing::Test {
- protected:
-  static void SetUpTestSuite() {
-    // Install a function to normalize filenames before this test is run.
-    absl::FlagsUsageConfig default_config;
-    default_config.normalize_filename = &CommandLineFlagTest::NormalizeFileName;
-    absl::SetFlagsUsageConfig(default_config);
-  }
-
-  void SetUp() override { flag_saver_ = absl::make_unique<absl::FlagSaver>(); }
-  void TearDown() override { flag_saver_.reset(); }
-
- private:
-  static std::string NormalizeFileName(absl::string_view fname) {
-#ifdef _WIN32
-    std::string normalized(fname);
-    std::replace(normalized.begin(), normalized.end(), '\\', '/');
-    fname = normalized;
-#endif
-    return std::string(fname);
-  }
-
-  std::unique_ptr<absl::FlagSaver> flag_saver_;
-};
-
-TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
-  auto* flag_01 = absl::FindCommandLineFlag("int_flag");
-
-  ASSERT_TRUE(flag_01);
-  EXPECT_EQ(flag_01->Name(), "int_flag");
-  EXPECT_EQ(flag_01->Help(), "int_flag help");
-  EXPECT_TRUE(!flag_01->IsRetired());
-  EXPECT_TRUE(flag_01->IsOfType<int>());
-  EXPECT_TRUE(!flag_01->IsOfType<bool>());
-  EXPECT_TRUE(!flag_01->IsOfType<std::string>());
-  EXPECT_TRUE(absl::EndsWith(flag_01->Filename(),
-                             "absl/flags/commandlineflag_test.cc"))
-      << flag_01->Filename();
-
-  auto* flag_02 = absl::FindCommandLineFlag("string_flag");
-
-  ASSERT_TRUE(flag_02);
-  EXPECT_EQ(flag_02->Name(), "string_flag");
-  EXPECT_EQ(flag_02->Help(), "string_flag help");
-  EXPECT_TRUE(!flag_02->IsRetired());
-  EXPECT_TRUE(flag_02->IsOfType<std::string>());
-  EXPECT_TRUE(!flag_02->IsOfType<bool>());
-  EXPECT_TRUE(!flag_02->IsOfType<int>());
-  EXPECT_TRUE(absl::EndsWith(flag_02->Filename(),
-                             "absl/flags/commandlineflag_test.cc"))
-      << flag_02->Filename();
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(CommandLineFlagTest, TestValueAccessMethods) {
-  absl::SetFlag(&FLAGS_int_flag2, 301);
-  auto* flag_01 = absl::FindCommandLineFlag("int_flag2");
-
-  ASSERT_TRUE(flag_01);
-  EXPECT_EQ(flag_01->CurrentValue(), "301");
-  EXPECT_EQ(flag_01->DefaultValue(), "201");
-
-  absl::SetFlag(&FLAGS_string_flag2, "new_str_value");
-  auto* flag_02 = absl::FindCommandLineFlag("string_flag2");
-
-  ASSERT_TRUE(flag_02);
-  EXPECT_EQ(flag_02->CurrentValue(), "new_str_value");
-  EXPECT_EQ(flag_02->DefaultValue(), "dflt");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) {
-  std::string err;
-
-  auto* flag_01 = absl::FindCommandLineFlag("int_flag");
-  EXPECT_FALSE(
-      flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "11", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
-  EXPECT_FALSE(
-      flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "-123", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
-      err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
-  EXPECT_FALSE(
-      flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
-
-  EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
-      err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
-  EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'");
-  EXPECT_FALSE(
-      flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
-
-  EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "A1", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123);
-  EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'");
-  EXPECT_FALSE(
-      flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "0x10", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
-      err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16);
-  EXPECT_FALSE(
-      flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "011", flags::SET_FLAGS_VALUE, flags::kCommandLine, err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11);
-  EXPECT_TRUE(flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01));
-
-  EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err));
-  EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'");
-
-  auto* flag_02 = absl::FindCommandLineFlag("string_flag");
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_02, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
-      err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz");
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_02, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) {
-  std::string err;
-
-  auto* flag_01 = absl::FindCommandLineFlag("int_flag");
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "111", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
-      err));
-  EXPECT_EQ(flag_01->DefaultValue(), "111");
-
-  auto* flag_02 = absl::FindCommandLineFlag("string_flag");
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_02, "abc", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange,
-      err));
-  EXPECT_EQ(flag_02->DefaultValue(), "abc");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(CommandLineFlagTest, TestParseFromIfDefault) {
-  std::string err;
-
-  auto* flag_01 = absl::FindCommandLineFlag("int_flag");
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "22", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
-      err))
-      << err;
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22);
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
-      err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22);
-  // EXPECT_EQ(err, "ERROR: int_flag is already set to 22");
-
-  // Reset back to default value
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "201", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange,
-      err));
-
-  EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom(
-      *flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange,
-      err));
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201);
-  // EXPECT_EQ(err, "ERROR: int_flag is already set to 201");
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/flags/config.h b/third_party/abseil_cpp/absl/flags/config.h
deleted file mode 100644
index 813a925700..0000000000
--- a/third_party/abseil_cpp/absl/flags/config.h
+++ /dev/null
@@ -1,87 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_CONFIG_H_
-#define ABSL_FLAGS_CONFIG_H_
-
-// Determine if we should strip string literals from the Flag objects.
-// By default we strip string literals on mobile platforms.
-#if !defined(ABSL_FLAGS_STRIP_NAMES)
-
-#if defined(__ANDROID__)
-#define ABSL_FLAGS_STRIP_NAMES 1
-
-#elif defined(__APPLE__)
-#include <TargetConditionals.h>
-#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
-#define ABSL_FLAGS_STRIP_NAMES 1
-#elif defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED
-#define ABSL_FLAGS_STRIP_NAMES 1
-#endif  // TARGET_OS_*
-#endif
-
-#endif  // !defined(ABSL_FLAGS_STRIP_NAMES)
-
-#if !defined(ABSL_FLAGS_STRIP_NAMES)
-// If ABSL_FLAGS_STRIP_NAMES wasn't set on the command line or above,
-// the default is not to strip.
-#define ABSL_FLAGS_STRIP_NAMES 0
-#endif
-
-#if !defined(ABSL_FLAGS_STRIP_HELP)
-// By default, if we strip names, we also strip help.
-#define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES
-#endif
-
-// ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD macro is used for using atomics with
-// double words, e.g. absl::Duration.
-// For reasons in bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878, modern
-// versions of GCC do not support cmpxchg16b instruction in standard atomics.
-#ifdef ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD
-#error "ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD should not be defined."
-#elif defined(__clang__) && defined(__x86_64__) && \
-    defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
-#define ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD 1
-#endif
-
-// ABSL_FLAGS_INTERNAL_HAS_RTTI macro is used for selecting if we can use RTTI
-// for flag type identification.
-#ifdef ABSL_FLAGS_INTERNAL_HAS_RTTI
-#error ABSL_FLAGS_INTERNAL_HAS_RTTI cannot be directly set
-#elif !defined(__GNUC__) || defined(__GXX_RTTI)
-#define ABSL_FLAGS_INTERNAL_HAS_RTTI 1
-#endif  // !defined(__GNUC__) || defined(__GXX_RTTI)
-
-// These macros represent the "source of truth" for the list of supported
-// built-in types.
-#define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \
-  A(bool, bool)                              \
-  A(short, short)                            \
-  A(unsigned short, unsigned_short)          \
-  A(int, int)                                \
-  A(unsigned int, unsigned_int)              \
-  A(long, long)                              \
-  A(unsigned long, unsigned_long)            \
-  A(long long, long_long)                    \
-  A(unsigned long long, unsigned_long_long)  \
-  A(double, double)                          \
-  A(float, float)
-
-#define ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(A) \
-  ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A)         \
-  A(std::string, std_string)                   \
-  A(std::vector<std::string>, std_vector_of_string)
-
-#endif  // ABSL_FLAGS_CONFIG_H_
diff --git a/third_party/abseil_cpp/absl/flags/config_test.cc b/third_party/abseil_cpp/absl/flags/config_test.cc
deleted file mode 100644
index 638998667e..0000000000
--- a/third_party/abseil_cpp/absl/flags/config_test.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/config.h"
-
-#ifdef __APPLE__
-#include <TargetConditionals.h>
-#endif
-
-#include "gtest/gtest.h"
-
-#ifndef ABSL_FLAGS_STRIP_NAMES
-#error ABSL_FLAGS_STRIP_NAMES is not defined
-#endif
-
-#ifndef ABSL_FLAGS_STRIP_HELP
-#error ABSL_FLAGS_STRIP_HELP is not defined
-#endif
-
-namespace {
-
-// Test that ABSL_FLAGS_STRIP_NAMES and ABSL_FLAGS_STRIP_HELP are configured how
-// we expect them to be configured by default. If you override this
-// configuration, this test will fail, but the code should still be safe to use.
-TEST(FlagsConfigTest, Test) {
-#if defined(__ANDROID__)
-  EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 1);
-  EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 1);
-#elif defined(__myriad2__)
-  EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0);
-  EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0);
-#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
-  EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 1);
-  EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 1);
-#elif defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED
-  EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 1);
-  EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 1);
-#elif defined(__APPLE__)
-  EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0);
-  EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0);
-#elif defined(_WIN32)
-  EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0);
-  EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0);
-#elif defined(__linux__)
-  EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0);
-  EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0);
-#endif
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/flags/declare.h b/third_party/abseil_cpp/absl/flags/declare.h
deleted file mode 100644
index b9794d8b85..0000000000
--- a/third_party/abseil_cpp/absl/flags/declare.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: declare.h
-// -----------------------------------------------------------------------------
-//
-// This file defines the ABSL_DECLARE_FLAG macro, allowing you to declare an
-// `absl::Flag` for use within a translation unit. You should place this
-// declaration within the header file associated with the .cc file that defines
-// and owns the `Flag`.
-
-#ifndef ABSL_FLAGS_DECLARE_H_
-#define ABSL_FLAGS_DECLARE_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// absl::Flag<T> represents a flag of type 'T' created by ABSL_FLAG.
-template <typename T>
-class Flag;
-
-}  // namespace flags_internal
-
-// Flag
-//
-// Forward declaration of the `absl::Flag` type for use in defining the macro.
-#if defined(_MSC_VER) && !defined(__clang__)
-template <typename T>
-class Flag;
-#else
-template <typename T>
-using Flag = flags_internal::Flag<T>;
-#endif
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// ABSL_DECLARE_FLAG()
-//
-// This macro is a convenience for declaring use of an `absl::Flag` within a
-// translation unit. This macro should be used within a header file to
-// declare usage of the flag within any .cc file including that header file.
-//
-// The ABSL_DECLARE_FLAG(type, name) macro expands to:
-//
-//   extern absl::Flag<type> FLAGS_name;
-#define ABSL_DECLARE_FLAG(type, name) extern ::absl::Flag<type> FLAGS_##name
-
-#endif  // ABSL_FLAGS_DECLARE_H_
diff --git a/third_party/abseil_cpp/absl/flags/flag.cc b/third_party/abseil_cpp/absl/flags/flag.cc
deleted file mode 100644
index 531df1287a..0000000000
--- a/third_party/abseil_cpp/absl/flags/flag.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/flag.h"
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// This global mutex protects on-demand construction of flag objects in MSVC
-// builds.
-#if defined(_MSC_VER) && !defined(__clang__)
-
-namespace flags_internal {
-
-ABSL_CONST_INIT static absl::Mutex construction_guard(absl::kConstInit);
-
-absl::Mutex* GetGlobalConstructionGuard() { return &construction_guard; }
-
-}  // namespace flags_internal
-
-#endif
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/flag.h b/third_party/abseil_cpp/absl/flags/flag.h
deleted file mode 100644
index a9cb2b7994..0000000000
--- a/third_party/abseil_cpp/absl/flags/flag.h
+++ /dev/null
@@ -1,396 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: flag.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the `absl::Flag<T>` type for holding command-line
-// flag data, and abstractions to create, get and set such flag data.
-//
-// It is important to note that this type is **unspecified** (an implementation
-// detail) and you do not construct or manipulate actual `absl::Flag<T>`
-// instances. Instead, you define and declare flags using the
-// `ABSL_FLAG()` and `ABSL_DECLARE_FLAG()` macros, and get and set flag values
-// using the `absl::GetFlag()` and `absl::SetFlag()` functions.
-
-#ifndef ABSL_FLAGS_FLAG_H_
-#define ABSL_FLAGS_FLAG_H_
-
-#include <string>
-#include <type_traits>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/optimization.h"
-#include "absl/flags/config.h"
-#include "absl/flags/internal/flag.h"
-#include "absl/flags/internal/registry.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Flag
-//
-// An `absl::Flag` holds a command-line flag value, providing a runtime
-// parameter to a binary. Such flags should be defined in the global namespace
-// and (preferably) in the module containing the binary's `main()` function.
-//
-// You should not construct and cannot use the `absl::Flag` type directly;
-// instead, you should declare flags using the `ABSL_DECLARE_FLAG()` macro
-// within a header file, and define your flag using `ABSL_FLAG()` within your
-// header's associated `.cc` file. Such flags will be named `FLAGS_name`.
-//
-// Example:
-//
-//    .h file
-//
-//      // Declares usage of a flag named "FLAGS_count"
-//      ABSL_DECLARE_FLAG(int, count);
-//
-//    .cc file
-//
-//      // Defines a flag named "FLAGS_count" with a default `int` value of 0.
-//      ABSL_FLAG(int, count, 0, "Count of items to process");
-//
-// No public methods of `absl::Flag<T>` are part of the Abseil Flags API.
-#if !defined(_MSC_VER) || defined(__clang__)
-template <typename T>
-using Flag = flags_internal::Flag<T>;
-#else
-// MSVC debug builds do not implement initialization with constexpr constructors
-// correctly. To work around this we add a level of indirection, so that the
-// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias
-// to that class) and dynamically allocates an instance when necessary. We also
-// forward all calls to internal::Flag methods via trampoline methods. In this
-// setup the `absl::Flag` class does not have constructor and virtual methods,
-// all the data members are public and thus MSVC is able to initialize it at
-// link time. To deal with multiple threads accessing the flag for the first
-// time concurrently we use an atomic boolean indicating if flag object is
-// initialized. We also employ the double-checked locking pattern where the
-// second level of protection is a global Mutex, so if two threads attempt to
-// construct the flag concurrently only one wins.
-// This solution is based on a recomendation here:
-// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
-
-namespace flags_internal {
-absl::Mutex* GetGlobalConstructionGuard();
-}  // namespace flags_internal
-
-template <typename T>
-class Flag {
- public:
-  // No constructor and destructor to ensure this is an aggregate type.
-  // Visual Studio 2015 still requires the constructor for class to be
-  // constexpr initializable.
-#if _MSC_VER <= 1900
-  constexpr Flag(const char* name, const char* filename,
-                 const flags_internal::HelpGenFunc help_gen,
-                 const flags_internal::FlagDfltGenFunc default_value_gen)
-      : name_(name),
-        filename_(filename),
-        help_gen_(help_gen),
-        default_value_gen_(default_value_gen),
-        inited_(false),
-        impl_(nullptr) {}
-#endif
-
-  flags_internal::Flag<T>& GetImpl() const {
-    if (!inited_.load(std::memory_order_acquire)) {
-      absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
-
-      if (inited_.load(std::memory_order_acquire)) {
-        return *impl_;
-      }
-
-      impl_ = new flags_internal::Flag<T>(
-          name_, filename_,
-          {flags_internal::FlagHelpMsg(help_gen_),
-           flags_internal::FlagHelpKind::kGenFunc},
-          {flags_internal::FlagDefaultSrc(default_value_gen_),
-           flags_internal::FlagDefaultKind::kGenFunc});
-      inited_.store(true, std::memory_order_release);
-    }
-
-    return *impl_;
-  }
-
-  // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
-  // See https://abseil.io/docs/cpp/guides/flags
-  bool IsRetired() const { return GetImpl().IsRetired(); }
-  absl::string_view Name() const { return GetImpl().Name(); }
-  std::string Help() const { return GetImpl().Help(); }
-  bool IsModified() const { return GetImpl().IsModified(); }
-  bool IsSpecifiedOnCommandLine() const {
-    return GetImpl().IsSpecifiedOnCommandLine();
-  }
-  std::string Filename() const { return GetImpl().Filename(); }
-  std::string DefaultValue() const { return GetImpl().DefaultValue(); }
-  std::string CurrentValue() const { return GetImpl().CurrentValue(); }
-  template <typename U>
-  inline bool IsOfType() const {
-    return GetImpl().template IsOfType<U>();
-  }
-  T Get() const {
-    return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl());
-  }
-  void Set(const T& v) {
-    flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v);
-  }
-  void InvokeCallback() { GetImpl().InvokeCallback(); }
-
-  const CommandLineFlag& Reflect() const {
-    return flags_internal::FlagImplPeer::InvokeReflect(GetImpl());
-  }
-
-  // The data members are logically private, but they need to be public for
-  // this to be an aggregate type.
-  const char* name_;
-  const char* filename_;
-  const flags_internal::HelpGenFunc help_gen_;
-  const flags_internal::FlagDfltGenFunc default_value_gen_;
-
-  mutable std::atomic<bool> inited_;
-  mutable flags_internal::Flag<T>* impl_;
-};
-#endif
-
-// GetFlag()
-//
-// Returns the value (of type `T`) of an `absl::Flag<T>` instance, by value. Do
-// not construct an `absl::Flag<T>` directly and call `absl::GetFlag()`;
-// instead, refer to flag's constructed variable name (e.g. `FLAGS_name`).
-// Because this function returns by value and not by reference, it is
-// thread-safe, but note that the operation may be expensive; as a result, avoid
-// `absl::GetFlag()` within any tight loops.
-//
-// Example:
-//
-//   // FLAGS_count is a Flag of type `int`
-//   int my_count = absl::GetFlag(FLAGS_count);
-//
-//   // FLAGS_firstname is a Flag of type `std::string`
-//   std::string first_name = absl::GetFlag(FLAGS_firstname);
-template <typename T>
-ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag) {
-  return flags_internal::FlagImplPeer::InvokeGet<T>(flag);
-}
-
-// SetFlag()
-//
-// Sets the value of an `absl::Flag` to the value `v`. Do not construct an
-// `absl::Flag<T>` directly and call `absl::SetFlag()`; instead, use the
-// flag's variable name (e.g. `FLAGS_name`). This function is
-// thread-safe, but is potentially expensive. Avoid setting flags in general,
-// but especially within performance-critical code.
-template <typename T>
-void SetFlag(absl::Flag<T>* flag, const T& v) {
-  flags_internal::FlagImplPeer::InvokeSet(*flag, v);
-}
-
-// Overload of `SetFlag()` to allow callers to pass in a value that is
-// convertible to `T`. E.g., use this overload to pass a "const char*" when `T`
-// is `std::string`.
-template <typename T, typename V>
-void SetFlag(absl::Flag<T>* flag, const V& v) {
-  T value(v);
-  flags_internal::FlagImplPeer::InvokeSet(*flag, value);
-}
-
-// GetFlagReflectionHandle()
-//
-// Returns the reflection handle corresponding to specified Abseil Flag
-// instance. Use this handle to access flag's reflection information, like name,
-// location, default value etc.
-//
-// Example:
-//
-//   std::string = absl::GetFlagReflectionHandle(FLAGS_count).DefaultValue();
-
-template <typename T>
-const CommandLineFlag& GetFlagReflectionHandle(const absl::Flag<T>& f) {
-  return flags_internal::FlagImplPeer::InvokeReflect(f);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-
-// ABSL_FLAG()
-//
-// This macro defines an `absl::Flag<T>` instance of a specified type `T`:
-//
-//   ABSL_FLAG(T, name, default_value, help);
-//
-// where:
-//
-//   * `T` is a supported flag type (see the list of types in `marshalling.h`),
-//   * `name` designates the name of the flag (as a global variable
-//     `FLAGS_name`),
-//   * `default_value` is an expression holding the default value for this flag
-//     (which must be implicitly convertible to `T`),
-//   * `help` is the help text, which can also be an expression.
-//
-// This macro expands to a flag named 'FLAGS_name' of type 'T':
-//
-//   absl::Flag<T> FLAGS_name = ...;
-//
-// Note that all such instances are created as global variables.
-//
-// For `ABSL_FLAG()` values that you wish to expose to other translation units,
-// it is recommended to define those flags within the `.cc` file associated with
-// the header where the flag is declared.
-//
-// Note: do not construct objects of type `absl::Flag<T>` directly. Only use the
-// `ABSL_FLAG()` macro for such construction.
-#define ABSL_FLAG(Type, name, default_value, help) \
-  ABSL_FLAG_IMPL(Type, name, default_value, help)
-
-// ABSL_FLAG().OnUpdate()
-//
-// Defines a flag of type `T` with a callback attached:
-//
-//   ABSL_FLAG(T, name, default_value, help).OnUpdate(callback);
-//
-// After any setting of the flag value, the callback will be called at least
-// once. A rapid sequence of changes may be merged together into the same
-// callback. No concurrent calls to the callback will be made for the same
-// flag. Callbacks are allowed to read the current value of the flag but must
-// not mutate that flag.
-//
-// The update mechanism guarantees "eventual consistency"; if the callback
-// derives an auxiliary data structure from the flag value, it is guaranteed
-// that eventually the flag value and the derived data structure will be
-// consistent.
-//
-// Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this
-// comment serves as its API documentation.
-
-
-// -----------------------------------------------------------------------------
-// Implementation details below this section
-// -----------------------------------------------------------------------------
-
-// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_NAMES
-#if !defined(_MSC_VER) || defined(__clang__)
-#define ABSL_FLAG_IMPL_FLAG_PTR(flag) flag
-#define ABSL_FLAG_IMPL_HELP_ARG(name)                      \
-  absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>( \
-      FLAGS_help_storage_##name)
-#define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) \
-  absl::flags_internal::DefaultArg<Type, AbslFlagDefaultGenFor##name>(0)
-#else
-#define ABSL_FLAG_IMPL_FLAG_PTR(flag) flag.GetImpl()
-#define ABSL_FLAG_IMPL_HELP_ARG(name) &AbslFlagHelpGenFor##name::NonConst
-#define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) &AbslFlagDefaultGenFor##name::Gen
-#endif
-
-#if ABSL_FLAGS_STRIP_NAMES
-#define ABSL_FLAG_IMPL_FLAGNAME(txt) ""
-#define ABSL_FLAG_IMPL_FILENAME() ""
-#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
-  absl::flags_internal::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(flag))
-#else
-#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt
-#define ABSL_FLAG_IMPL_FILENAME() __FILE__
-#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
-  absl::flags_internal::FlagRegistrar<T, true>(ABSL_FLAG_IMPL_FLAG_PTR(flag))
-#endif
-
-// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP
-
-#if ABSL_FLAGS_STRIP_HELP
-#define ABSL_FLAG_IMPL_FLAGHELP(txt) absl::flags_internal::kStrippedFlagHelp
-#else
-#define ABSL_FLAG_IMPL_FLAGHELP(txt) txt
-#endif
-
-// AbslFlagHelpGenFor##name is used to encapsulate both immediate (method Const)
-// and lazy (method NonConst) evaluation of help message expression. We choose
-// between the two via the call to HelpArg in absl::Flag instantiation below.
-// If help message expression is constexpr evaluable compiler will optimize
-// away this whole struct.
-// TODO(rogeeff): place these generated structs into local namespace and apply
-// ABSL_INTERNAL_UNIQUE_SHORT_NAME.
-// TODO(rogeeff): Apply __attribute__((nodebug)) to FLAGS_help_storage_##name
-#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt)                       \
-  struct AbslFlagHelpGenFor##name {                                          \
-    /* The expression is run in the caller as part of the   */               \
-    /* default value argument. That keeps temporaries alive */               \
-    /* long enough for NonConst to work correctly.          */               \
-    static constexpr absl::string_view Value(                                \
-        absl::string_view v = ABSL_FLAG_IMPL_FLAGHELP(txt)) {                \
-      return v;                                                              \
-    }                                                                        \
-    static std::string NonConst() { return std::string(Value()); }           \
-  };                                                                         \
-  constexpr auto FLAGS_help_storage_##name ABSL_INTERNAL_UNIQUE_SMALL_NAME() \
-      ABSL_ATTRIBUTE_SECTION_VARIABLE(flags_help_cold) =                     \
-          absl::flags_internal::HelpStringAsArray<AbslFlagHelpGenFor##name>( \
-              0);
-
-#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value)     \
-  struct AbslFlagDefaultGenFor##name {                                        \
-    Type value = absl::flags_internal::InitDefaultValue<Type>(default_value); \
-    static void Gen(void* p) {                                                \
-      new (p) Type(AbslFlagDefaultGenFor##name{}.value);                      \
-    }                                                                         \
-  };
-
-// ABSL_FLAG_IMPL
-//
-// Note: Name of registrar object is not arbitrary. It is used to "grab"
-// global name for FLAGS_no<flag_name> symbol, thus preventing the possibility
-// of defining two flags with names foo and nofoo.
-#define ABSL_FLAG_IMPL(Type, name, default_value, help)                       \
-  namespace absl /* block flags in namespaces */ {}                           \
-  ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value)           \
-  ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help)                             \
-  ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{                              \
-      ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(),              \
-      ABSL_FLAG_IMPL_HELP_ARG(name), ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name)}; \
-  extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name;             \
-  absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name =                   \
-      ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name)
-
-// ABSL_RETIRED_FLAG
-//
-// Designates the flag (which is usually pre-existing) as "retired." A retired
-// flag is a flag that is now unused by the program, but may still be passed on
-// the command line, usually by production scripts. A retired flag is ignored
-// and code can't access it at runtime.
-//
-// This macro registers a retired flag with given name and type, with a name
-// identical to the name of the original flag you are retiring. The retired
-// flag's type can change over time, so that you can retire code to support a
-// custom flag type.
-//
-// This macro has the same signature as `ABSL_FLAG`. To retire a flag, simply
-// replace an `ABSL_FLAG` definition with `ABSL_RETIRED_FLAG`, leaving the
-// arguments unchanged (unless of course you actually want to retire the flag
-// type at this time as well).
-//
-// `default_value` is only used as a double check on the type. `explanation` is
-// unused.
-// TODO(rogeeff): replace RETIRED_FLAGS with FLAGS once forward declarations of
-// retired flags are cleaned up.
-#define ABSL_RETIRED_FLAG(type, name, default_value, explanation)      \
-  static absl::flags_internal::RetiredFlag<type> RETIRED_FLAGS_##name; \
-  ABSL_ATTRIBUTE_UNUSED static const auto RETIRED_FLAGS_REG_##name =   \
-      (RETIRED_FLAGS_##name.Retire(#name),                             \
-       ::absl::flags_internal::FlagRegistrarEmpty{})
-
-#endif  // ABSL_FLAGS_FLAG_H_
diff --git a/third_party/abseil_cpp/absl/flags/flag_benchmark.cc b/third_party/abseil_cpp/absl/flags/flag_benchmark.cc
deleted file mode 100644
index 9982b604b3..0000000000
--- a/third_party/abseil_cpp/absl/flags/flag_benchmark.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-//
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "absl/flags/flag.h"
-#include "absl/flags/marshalling.h"
-#include "absl/flags/parse.h"
-#include "absl/flags/reflection.h"
-#include "absl/strings/string_view.h"
-#include "absl/time/time.h"
-#include "absl/types/optional.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-using String = std::string;
-using VectorOfStrings = std::vector<std::string>;
-using AbslDuration = absl::Duration;
-
-// We do not want to take over marshalling for the types absl::optional<int>,
-// absl::optional<std::string> which we do not own. Instead we introduce unique
-// "aliases" to these types, which we do.
-using AbslOptionalInt = absl::optional<int>;
-struct OptionalInt : AbslOptionalInt {
-  using AbslOptionalInt::AbslOptionalInt;
-};
-// Next two functions represent Abseil Flags marshalling for OptionalInt.
-bool AbslParseFlag(absl::string_view src, OptionalInt* flag,
-                   std::string* error) {
-  int val;
-  if (src.empty())
-    flag->reset();
-  else if (!absl::ParseFlag(src, &val, error))
-    return false;
-  *flag = val;
-  return true;
-}
-std::string AbslUnparseFlag(const OptionalInt& flag) {
-  return !flag ? "" : absl::UnparseFlag(*flag);
-}
-
-using AbslOptionalString = absl::optional<std::string>;
-struct OptionalString : AbslOptionalString {
-  using AbslOptionalString::AbslOptionalString;
-};
-// Next two functions represent Abseil Flags marshalling for OptionalString.
-bool AbslParseFlag(absl::string_view src, OptionalString* flag,
-                   std::string* error) {
-  std::string val;
-  if (src.empty())
-    flag->reset();
-  else if (!absl::ParseFlag(src, &val, error))
-    return false;
-  *flag = val;
-  return true;
-}
-std::string AbslUnparseFlag(const OptionalString& flag) {
-  return !flag ? "" : absl::UnparseFlag(*flag);
-}
-
-struct UDT {
-  UDT() = default;
-  UDT(const UDT&) {}
-  UDT& operator=(const UDT&) { return *this; }
-};
-// Next two functions represent Abseil Flags marshalling for UDT.
-bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
-std::string AbslUnparseFlag(const UDT&) { return ""; }
-
-}  // namespace
-
-#define BENCHMARKED_TYPES(A) \
-  A(bool)                    \
-  A(int16_t)                 \
-  A(uint16_t)                \
-  A(int32_t)                 \
-  A(uint32_t)                \
-  A(int64_t)                 \
-  A(uint64_t)                \
-  A(double)                  \
-  A(float)                   \
-  A(String)                  \
-  A(VectorOfStrings)         \
-  A(OptionalInt)             \
-  A(OptionalString)          \
-  A(AbslDuration)            \
-  A(UDT)
-
-#define FLAG_DEF(T) ABSL_FLAG(T, T##_flag, {}, "");
-
-BENCHMARKED_TYPES(FLAG_DEF)
-
-// Register thousands of flags to bloat up the size of the registry.
-// This mimics real life production binaries.
-#define DEFINE_FLAG_0(name) ABSL_FLAG(int, name, 0, "");
-#define DEFINE_FLAG_1(name) DEFINE_FLAG_0(name##0) DEFINE_FLAG_0(name##1)
-#define DEFINE_FLAG_2(name) DEFINE_FLAG_1(name##0) DEFINE_FLAG_1(name##1)
-#define DEFINE_FLAG_3(name) DEFINE_FLAG_2(name##0) DEFINE_FLAG_2(name##1)
-#define DEFINE_FLAG_4(name) DEFINE_FLAG_3(name##0) DEFINE_FLAG_3(name##1)
-#define DEFINE_FLAG_5(name) DEFINE_FLAG_4(name##0) DEFINE_FLAG_4(name##1)
-#define DEFINE_FLAG_6(name) DEFINE_FLAG_5(name##0) DEFINE_FLAG_5(name##1)
-#define DEFINE_FLAG_7(name) DEFINE_FLAG_6(name##0) DEFINE_FLAG_6(name##1)
-#define DEFINE_FLAG_8(name) DEFINE_FLAG_7(name##0) DEFINE_FLAG_7(name##1)
-#define DEFINE_FLAG_9(name) DEFINE_FLAG_8(name##0) DEFINE_FLAG_8(name##1)
-#define DEFINE_FLAG_10(name) DEFINE_FLAG_9(name##0) DEFINE_FLAG_9(name##1)
-#define DEFINE_FLAG_11(name) DEFINE_FLAG_10(name##0) DEFINE_FLAG_10(name##1)
-#define DEFINE_FLAG_12(name) DEFINE_FLAG_11(name##0) DEFINE_FLAG_11(name##1)
-DEFINE_FLAG_12(bloat_flag_);
-
-namespace {
-
-#define BM_GetFlag(T)                                            \
-  void BM_GetFlag_##T(benchmark::State& state) {                 \
-    for (auto _ : state) {                                       \
-      benchmark::DoNotOptimize(absl::GetFlag(FLAGS_##T##_flag)); \
-    }                                                            \
-  }                                                              \
-  BENCHMARK(BM_GetFlag_##T);
-
-BENCHMARKED_TYPES(BM_GetFlag)
-
-void BM_ThreadedFindCommandLineFlag(benchmark::State& state) {
-  char dummy[] = "dummy";
-  char* argv[] = {dummy};
-  // We need to ensure that flags have been parsed. That is where the registry
-  // is finalized.
-  absl::ParseCommandLine(1, argv);
-
-  for (auto s : state) {
-    benchmark::DoNotOptimize(
-        absl::FindCommandLineFlag("bloat_flag_010101010101"));
-  }
-}
-BENCHMARK(BM_ThreadedFindCommandLineFlag)->ThreadRange(1, 16);
-
-}  // namespace
-
-#define InvokeGetFlag(T)                                               \
-  T AbslInvokeGetFlag##T() { return absl::GetFlag(FLAGS_##T##_flag); } \
-  int odr##T = (benchmark::DoNotOptimize(AbslInvokeGetFlag##T), 1);
-
-BENCHMARKED_TYPES(InvokeGetFlag)
-
-// To veiw disassembly use: gdb ${BINARY}  -batch -ex "disassemble /s $FUNC"
diff --git a/third_party/abseil_cpp/absl/flags/flag_test.cc b/third_party/abseil_cpp/absl/flags/flag_test.cc
deleted file mode 100644
index 654c812221..0000000000
--- a/third_party/abseil_cpp/absl/flags/flag_test.cc
+++ /dev/null
@@ -1,906 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/flag.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <cmath>
-#include <new>
-#include <string>
-#include <thread>  // NOLINT
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/flags/config.h"
-#include "absl/flags/declare.h"
-#include "absl/flags/internal/flag.h"
-#include "absl/flags/marshalling.h"
-#include "absl/flags/reflection.h"
-#include "absl/flags/usage_config.h"
-#include "absl/strings/match.h"
-#include "absl/strings/numbers.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_split.h"
-#include "absl/strings/string_view.h"
-#include "absl/time/time.h"
-
-ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag);
-ABSL_DECLARE_FLAG(std::vector<std::string>, mistyped_string_flag);
-
-namespace {
-
-namespace flags = absl::flags_internal;
-
-std::string TestHelpMsg() { return "dynamic help"; }
-#if defined(_MSC_VER) && !defined(__clang__)
-std::string TestLiteralHelpMsg() { return "literal help"; }
-#endif
-template <typename T>
-void TestMakeDflt(void* dst) {
-  new (dst) T{};
-}
-void TestCallback() {}
-
-struct UDT {
-  UDT() = default;
-  UDT(const UDT&) = default;
-};
-bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
-std::string AbslUnparseFlag(const UDT&) { return ""; }
-
-class FlagTest : public testing::Test {
- protected:
-  static void SetUpTestSuite() {
-    // Install a function to normalize filenames before this test is run.
-    absl::FlagsUsageConfig default_config;
-    default_config.normalize_filename = &FlagTest::NormalizeFileName;
-    absl::SetFlagsUsageConfig(default_config);
-  }
-
- private:
-  static std::string NormalizeFileName(absl::string_view fname) {
-#ifdef _WIN32
-    std::string normalized(fname);
-    std::replace(normalized.begin(), normalized.end(), '\\', '/');
-    fname = normalized;
-#endif
-    return std::string(fname);
-  }
-  absl::FlagSaver flag_saver_;
-};
-
-struct S1 {
-  S1() = default;
-  S1(const S1&) = default;
-  int32_t f1;
-  int64_t f2;
-};
-
-struct S2 {
-  S2() = default;
-  S2(const S2&) = default;
-  int64_t f1;
-  double f2;
-};
-
-TEST_F(FlagTest, Traits) {
-  EXPECT_EQ(flags::StorageKind<int>(),
-            flags::FlagValueStorageKind::kOneWordAtomic);
-  EXPECT_EQ(flags::StorageKind<bool>(),
-            flags::FlagValueStorageKind::kOneWordAtomic);
-  EXPECT_EQ(flags::StorageKind<double>(),
-            flags::FlagValueStorageKind::kOneWordAtomic);
-  EXPECT_EQ(flags::StorageKind<int64_t>(),
-            flags::FlagValueStorageKind::kOneWordAtomic);
-
-#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
-  EXPECT_EQ(flags::StorageKind<S1>(),
-            flags::FlagValueStorageKind::kTwoWordsAtomic);
-  EXPECT_EQ(flags::StorageKind<S2>(),
-            flags::FlagValueStorageKind::kTwoWordsAtomic);
-#else
-  EXPECT_EQ(flags::StorageKind<S1>(),
-            flags::FlagValueStorageKind::kAlignedBuffer);
-  EXPECT_EQ(flags::StorageKind<S2>(),
-            flags::FlagValueStorageKind::kAlignedBuffer);
-#endif
-
-  EXPECT_EQ(flags::StorageKind<std::string>(),
-            flags::FlagValueStorageKind::kAlignedBuffer);
-  EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(),
-            flags::FlagValueStorageKind::kAlignedBuffer);
-}
-
-// --------------------------------------------------------------------
-
-constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"),
-                                      flags::FlagHelpKind::kLiteral};
-
-using String = std::string;
-
-#if !defined(_MSC_VER) || defined(__clang__)
-#define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind)                        \
-  constexpr flags::FlagDefaultArg f1default##T{                            \
-      flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind};     \
-  constexpr absl::Flag<T> f1##T{"f1", "file", help_arg, f1default##T};     \
-  ABSL_CONST_INIT absl::Flag<T> f2##T {                                    \
-    "f2", "file",                                                          \
-        {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, \
-        flags::FlagDefaultArg {                                            \
-      flags::FlagDefaultSrc(&TestMakeDflt<T>),                             \
-          flags::FlagDefaultKind::kGenFunc                                 \
-    }                                                                      \
-  }
-#else
-#define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind)                    \
-  constexpr flags::FlagDefaultArg f1default##T{                        \
-      flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \
-  constexpr absl::Flag<T> f1##T{"f1", "file", &TestLiteralHelpMsg,     \
-                                &TestMakeDflt<T>};                     \
-  ABSL_CONST_INIT absl::Flag<T> f2##T {                                \
-    "f2", "file", &TestHelpMsg, &TestMakeDflt<T>                       \
-  }
-#endif
-
-DEFINE_CONSTRUCTED_FLAG(bool, true, kOneWord);
-DEFINE_CONSTRUCTED_FLAG(int16_t, 1, kOneWord);
-DEFINE_CONSTRUCTED_FLAG(uint16_t, 2, kOneWord);
-DEFINE_CONSTRUCTED_FLAG(int32_t, 3, kOneWord);
-DEFINE_CONSTRUCTED_FLAG(uint32_t, 4, kOneWord);
-DEFINE_CONSTRUCTED_FLAG(int64_t, 5, kOneWord);
-DEFINE_CONSTRUCTED_FLAG(uint64_t, 6, kOneWord);
-DEFINE_CONSTRUCTED_FLAG(float, 7.8, kOneWord);
-DEFINE_CONSTRUCTED_FLAG(double, 9.10, kOneWord);
-DEFINE_CONSTRUCTED_FLAG(String, &TestMakeDflt<String>, kGenFunc);
-DEFINE_CONSTRUCTED_FLAG(UDT, &TestMakeDflt<UDT>, kGenFunc);
-
-template <typename T>
-bool TestConstructionFor(const absl::Flag<T>& f1, absl::Flag<T>& f2) {
-  EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Name(), "f1");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Help(), "literal help");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Filename(), "file");
-
-  flags::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(f2))
-      .OnUpdate(TestCallback);
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Name(), "f2");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Help(), "dynamic help");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Filename(), "file");
-
-  return true;
-}
-
-#define TEST_CONSTRUCTED_FLAG(T) TestConstructionFor(f1##T, f2##T);
-
-TEST_F(FlagTest, TestConstruction) {
-  TEST_CONSTRUCTED_FLAG(bool);
-  TEST_CONSTRUCTED_FLAG(int16_t);
-  TEST_CONSTRUCTED_FLAG(uint16_t);
-  TEST_CONSTRUCTED_FLAG(int32_t);
-  TEST_CONSTRUCTED_FLAG(uint32_t);
-  TEST_CONSTRUCTED_FLAG(int64_t);
-  TEST_CONSTRUCTED_FLAG(uint64_t);
-  TEST_CONSTRUCTED_FLAG(float);
-  TEST_CONSTRUCTED_FLAG(double);
-  TEST_CONSTRUCTED_FLAG(String);
-  TEST_CONSTRUCTED_FLAG(UDT);
-}
-
-// --------------------------------------------------------------------
-
-}  // namespace
-
-ABSL_DECLARE_FLAG(bool, test_flag_01);
-ABSL_DECLARE_FLAG(int, test_flag_02);
-ABSL_DECLARE_FLAG(int16_t, test_flag_03);
-ABSL_DECLARE_FLAG(uint16_t, test_flag_04);
-ABSL_DECLARE_FLAG(int32_t, test_flag_05);
-ABSL_DECLARE_FLAG(uint32_t, test_flag_06);
-ABSL_DECLARE_FLAG(int64_t, test_flag_07);
-ABSL_DECLARE_FLAG(uint64_t, test_flag_08);
-ABSL_DECLARE_FLAG(double, test_flag_09);
-ABSL_DECLARE_FLAG(float, test_flag_10);
-ABSL_DECLARE_FLAG(std::string, test_flag_11);
-ABSL_DECLARE_FLAG(absl::Duration, test_flag_12);
-
-namespace {
-
-#if !ABSL_FLAGS_STRIP_NAMES
-
-TEST_F(FlagTest, TestFlagDeclaration) {
-  // test that we can access flag objects.
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Name(),
-            "test_flag_01");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Name(),
-            "test_flag_02");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Name(),
-            "test_flag_03");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Name(),
-            "test_flag_04");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Name(),
-            "test_flag_05");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Name(),
-            "test_flag_06");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Name(),
-            "test_flag_07");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Name(),
-            "test_flag_08");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Name(),
-            "test_flag_09");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Name(),
-            "test_flag_10");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Name(),
-            "test_flag_11");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Name(),
-            "test_flag_12");
-}
-#endif  // !ABSL_FLAGS_STRIP_NAMES
-
-// --------------------------------------------------------------------
-
-}  // namespace
-
-ABSL_FLAG(bool, test_flag_01, true, "test flag 01");
-ABSL_FLAG(int, test_flag_02, 1234, "test flag 02");
-ABSL_FLAG(int16_t, test_flag_03, -34, "test flag 03");
-ABSL_FLAG(uint16_t, test_flag_04, 189, "test flag 04");
-ABSL_FLAG(int32_t, test_flag_05, 10765, "test flag 05");
-ABSL_FLAG(uint32_t, test_flag_06, 40000, "test flag 06");
-ABSL_FLAG(int64_t, test_flag_07, -1234567, "test flag 07");
-ABSL_FLAG(uint64_t, test_flag_08, 9876543, "test flag 08");
-ABSL_FLAG(double, test_flag_09, -9.876e-50, "test flag 09");
-ABSL_FLAG(float, test_flag_10, 1.234e12f, "test flag 10");
-ABSL_FLAG(std::string, test_flag_11, "", "test flag 11");
-ABSL_FLAG(absl::Duration, test_flag_12, absl::Minutes(10), "test flag 12");
-
-namespace {
-
-#if !ABSL_FLAGS_STRIP_NAMES
-TEST_F(FlagTest, TestFlagDefinition) {
-  absl::string_view expected_file_name = "absl/flags/flag_test.cc";
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Name(),
-            "test_flag_01");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Help(),
-            "test flag 01");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Name(),
-            "test_flag_02");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Help(),
-            "test flag 02");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Name(),
-            "test_flag_03");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Help(),
-            "test flag 03");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Name(),
-            "test_flag_04");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Help(),
-            "test flag 04");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Name(),
-            "test_flag_05");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Help(),
-            "test flag 05");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Name(),
-            "test_flag_06");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Help(),
-            "test flag 06");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Name(),
-            "test_flag_07");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Help(),
-            "test flag 07");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Name(),
-            "test_flag_08");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Help(),
-            "test flag 08");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Name(),
-            "test_flag_09");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Help(),
-            "test flag 09");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Name(),
-            "test_flag_10");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Help(),
-            "test flag 10");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Name(),
-            "test_flag_11");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Help(),
-            "test flag 11");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Filename();
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Name(),
-            "test_flag_12");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Help(),
-            "test flag 12");
-  EXPECT_TRUE(absl::EndsWith(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Filename(),
-      expected_file_name))
-      << absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Filename();
-}
-#endif  // !ABSL_FLAGS_STRIP_NAMES
-
-// --------------------------------------------------------------------
-
-TEST_F(FlagTest, TestDefault) {
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).DefaultValue(),
-            "true");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).DefaultValue(),
-            "1234");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).DefaultValue(),
-            "-34");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).DefaultValue(),
-            "189");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).DefaultValue(),
-            "10765");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).DefaultValue(),
-            "40000");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).DefaultValue(),
-            "-1234567");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).DefaultValue(),
-            "9876543");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).DefaultValue(),
-            "-9.876e-50");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).DefaultValue(),
-            "1.234e+12");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).DefaultValue(),
-            "");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).DefaultValue(),
-            "10m");
-
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).CurrentValue(),
-            "true");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).CurrentValue(),
-            "1234");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).CurrentValue(),
-            "-34");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).CurrentValue(),
-            "189");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).CurrentValue(),
-            "10765");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).CurrentValue(),
-            "40000");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).CurrentValue(),
-            "-1234567");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).CurrentValue(),
-            "9876543");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).CurrentValue(),
-            "-9.876e-50");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).CurrentValue(),
-            "1.234e+12");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).CurrentValue(),
-            "");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).CurrentValue(),
-            "10m");
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543);
-  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55);
-  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "");
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10));
-}
-
-// --------------------------------------------------------------------
-
-struct NonTriviallyCopyableAggregate {
-  NonTriviallyCopyableAggregate() = default;
-  NonTriviallyCopyableAggregate(const NonTriviallyCopyableAggregate& rhs)
-      : value(rhs.value) {}
-  NonTriviallyCopyableAggregate& operator=(
-      const NonTriviallyCopyableAggregate& rhs) {
-    value = rhs.value;
-    return *this;
-  }
-
-  int value;
-};
-bool AbslParseFlag(absl::string_view src, NonTriviallyCopyableAggregate* f,
-                   std::string* e) {
-  return absl::ParseFlag(src, &f->value, e);
-}
-std::string AbslUnparseFlag(const NonTriviallyCopyableAggregate& ntc) {
-  return absl::StrCat(ntc.value);
-}
-
-bool operator==(const NonTriviallyCopyableAggregate& ntc1,
-                const NonTriviallyCopyableAggregate& ntc2) {
-  return ntc1.value == ntc2.value;
-}
-
-}  // namespace
-
-ABSL_FLAG(bool, test_flag_eb_01, {}, "");
-ABSL_FLAG(int32_t, test_flag_eb_02, {}, "");
-ABSL_FLAG(int64_t, test_flag_eb_03, {}, "");
-ABSL_FLAG(double, test_flag_eb_04, {}, "");
-ABSL_FLAG(std::string, test_flag_eb_05, {}, "");
-ABSL_FLAG(NonTriviallyCopyableAggregate, test_flag_eb_06, {}, "");
-
-namespace {
-
-TEST_F(FlagTest, TestEmptyBracesDefault) {
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_01).DefaultValue(),
-            "false");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_02).DefaultValue(),
-            "0");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_03).DefaultValue(),
-            "0");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_04).DefaultValue(),
-            "0");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_05).DefaultValue(),
-            "");
-  EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_06).DefaultValue(),
-            "0");
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_01), false);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_02), 0);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_03), 0);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_04), 0.0);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_05), "");
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_06),
-            NonTriviallyCopyableAggregate{});
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(FlagTest, TestGetSet) {
-  absl::SetFlag(&FLAGS_test_flag_01, false);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), false);
-
-  absl::SetFlag(&FLAGS_test_flag_02, 321);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 321);
-
-  absl::SetFlag(&FLAGS_test_flag_03, 67);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), 67);
-
-  absl::SetFlag(&FLAGS_test_flag_04, 1);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 1);
-
-  absl::SetFlag(&FLAGS_test_flag_05, -908);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), -908);
-
-  absl::SetFlag(&FLAGS_test_flag_06, 4001);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 4001);
-
-  absl::SetFlag(&FLAGS_test_flag_07, -23456);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -23456);
-
-  absl::SetFlag(&FLAGS_test_flag_08, 975310);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 975310);
-
-  absl::SetFlag(&FLAGS_test_flag_09, 1.00001);
-  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), 1.00001, 1e-10);
-
-  absl::SetFlag(&FLAGS_test_flag_10, -3.54f);
-  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), -3.54f, 1e-6f);
-
-  absl::SetFlag(&FLAGS_test_flag_11, "asdf");
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "asdf");
-
-  absl::SetFlag(&FLAGS_test_flag_12, absl::Seconds(110));
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Seconds(110));
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(FlagTest, TestGetViaReflection) {
-  auto* handle = absl::FindCommandLineFlag("test_flag_01");
-  EXPECT_EQ(*handle->TryGet<bool>(), true);
-  handle = absl::FindCommandLineFlag("test_flag_02");
-  EXPECT_EQ(*handle->TryGet<int>(), 1234);
-  handle = absl::FindCommandLineFlag("test_flag_03");
-  EXPECT_EQ(*handle->TryGet<int16_t>(), -34);
-  handle = absl::FindCommandLineFlag("test_flag_04");
-  EXPECT_EQ(*handle->TryGet<uint16_t>(), 189);
-  handle = absl::FindCommandLineFlag("test_flag_05");
-  EXPECT_EQ(*handle->TryGet<int32_t>(), 10765);
-  handle = absl::FindCommandLineFlag("test_flag_06");
-  EXPECT_EQ(*handle->TryGet<uint32_t>(), 40000);
-  handle = absl::FindCommandLineFlag("test_flag_07");
-  EXPECT_EQ(*handle->TryGet<int64_t>(), -1234567);
-  handle = absl::FindCommandLineFlag("test_flag_08");
-  EXPECT_EQ(*handle->TryGet<uint64_t>(), 9876543);
-  handle = absl::FindCommandLineFlag("test_flag_09");
-  EXPECT_NEAR(*handle->TryGet<double>(), -9.876e-50, 1e-55);
-  handle = absl::FindCommandLineFlag("test_flag_10");
-  EXPECT_NEAR(*handle->TryGet<float>(), 1.234e12f, 1e5f);
-  handle = absl::FindCommandLineFlag("test_flag_11");
-  EXPECT_EQ(*handle->TryGet<std::string>(), "");
-  handle = absl::FindCommandLineFlag("test_flag_12");
-  EXPECT_EQ(*handle->TryGet<absl::Duration>(), absl::Minutes(10));
-}
-
-// --------------------------------------------------------------------
-
-int GetDflt1() { return 1; }
-
-}  // namespace
-
-ABSL_FLAG(int, test_int_flag_with_non_const_default, GetDflt1(),
-          "test int flag non const default");
-ABSL_FLAG(std::string, test_string_flag_with_non_const_default,
-          absl::StrCat("AAA", "BBB"), "test string flag non const default");
-
-namespace {
-
-TEST_F(FlagTest, TestNonConstexprDefault) {
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_int_flag_with_non_const_default), 1);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_string_flag_with_non_const_default),
-            "AAABBB");
-}
-
-// --------------------------------------------------------------------
-
-}  // namespace
-
-ABSL_FLAG(bool, test_flag_with_non_const_help, true,
-          absl::StrCat("test ", "flag ", "non const help"));
-
-namespace {
-
-#if !ABSL_FLAGS_STRIP_HELP
-TEST_F(FlagTest, TestNonConstexprHelp) {
-  EXPECT_EQ(
-      absl::GetFlagReflectionHandle(FLAGS_test_flag_with_non_const_help).Help(),
-      "test flag non const help");
-}
-#endif  //! ABSL_FLAGS_STRIP_HELP
-
-// --------------------------------------------------------------------
-
-int cb_test_value = -1;
-void TestFlagCB();
-
-}  // namespace
-
-ABSL_FLAG(int, test_flag_with_cb, 100, "").OnUpdate(TestFlagCB);
-
-ABSL_FLAG(int, test_flag_with_lambda_cb, 200, "").OnUpdate([]() {
-  cb_test_value = absl::GetFlag(FLAGS_test_flag_with_lambda_cb) +
-                  absl::GetFlag(FLAGS_test_flag_with_cb);
-});
-
-namespace {
-
-void TestFlagCB() { cb_test_value = absl::GetFlag(FLAGS_test_flag_with_cb); }
-
-// Tests side-effects of callback invocation.
-TEST_F(FlagTest, CallbackInvocation) {
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_cb), 100);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_lambda_cb), 200);
-  EXPECT_EQ(cb_test_value, 300);
-
-  absl::SetFlag(&FLAGS_test_flag_with_cb, 1);
-  EXPECT_EQ(cb_test_value, 1);
-
-  absl::SetFlag(&FLAGS_test_flag_with_lambda_cb, 3);
-  EXPECT_EQ(cb_test_value, 4);
-}
-
-// --------------------------------------------------------------------
-
-struct CustomUDT {
-  CustomUDT() : a(1), b(1) {}
-  CustomUDT(int a_, int b_) : a(a_), b(b_) {}
-
-  friend bool operator==(const CustomUDT& f1, const CustomUDT& f2) {
-    return f1.a == f2.a && f1.b == f2.b;
-  }
-
-  int a;
-  int b;
-};
-bool AbslParseFlag(absl::string_view in, CustomUDT* f, std::string*) {
-  std::vector<absl::string_view> parts =
-      absl::StrSplit(in, ':', absl::SkipWhitespace());
-
-  if (parts.size() != 2) return false;
-
-  if (!absl::SimpleAtoi(parts[0], &f->a)) return false;
-
-  if (!absl::SimpleAtoi(parts[1], &f->b)) return false;
-
-  return true;
-}
-std::string AbslUnparseFlag(const CustomUDT& f) {
-  return absl::StrCat(f.a, ":", f.b);
-}
-
-}  // namespace
-
-ABSL_FLAG(CustomUDT, test_flag_custom_udt, CustomUDT(), "test flag custom UDT");
-
-namespace {
-
-TEST_F(FlagTest, TestCustomUDT) {
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(1, 1));
-  absl::SetFlag(&FLAGS_test_flag_custom_udt, CustomUDT(2, 3));
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(2, 3));
-}
-
-// MSVC produces link error on the type mismatch.
-// Linux does not have build errors and validations work as expected.
-#if !defined(_WIN32) && GTEST_HAS_DEATH_TEST
-
-using FlagDeathTest = FlagTest;
-
-TEST_F(FlagDeathTest, TestTypeMismatchValidations) {
-#if !defined(NDEBUG)
-  EXPECT_DEATH_IF_SUPPORTED(
-      static_cast<void>(absl::GetFlag(FLAGS_mistyped_int_flag)),
-      "Flag 'mistyped_int_flag' is defined as one type and declared "
-      "as another");
-  EXPECT_DEATH_IF_SUPPORTED(
-      static_cast<void>(absl::GetFlag(FLAGS_mistyped_string_flag)),
-      "Flag 'mistyped_string_flag' is defined as one type and "
-      "declared as another");
-#endif
-
-  EXPECT_DEATH_IF_SUPPORTED(
-      absl::SetFlag(&FLAGS_mistyped_int_flag, 1),
-      "Flag 'mistyped_int_flag' is defined as one type and declared "
-      "as another");
-  EXPECT_DEATH_IF_SUPPORTED(
-      absl::SetFlag(&FLAGS_mistyped_string_flag, std::vector<std::string>{}),
-      "Flag 'mistyped_string_flag' is defined as one type and declared as "
-      "another");
-}
-
-#endif
-
-// --------------------------------------------------------------------
-
-// A contrived type that offers implicit and explicit conversion from specific
-// source types.
-struct ConversionTestVal {
-  ConversionTestVal() = default;
-  explicit ConversionTestVal(int a_in) : a(a_in) {}
-
-  enum class ViaImplicitConv { kTen = 10, kEleven };
-  // NOLINTNEXTLINE
-  ConversionTestVal(ViaImplicitConv from) : a(static_cast<int>(from)) {}
-
-  int a;
-};
-
-bool AbslParseFlag(absl::string_view in, ConversionTestVal* val_out,
-                   std::string*) {
-  if (!absl::SimpleAtoi(in, &val_out->a)) {
-    return false;
-  }
-  return true;
-}
-std::string AbslUnparseFlag(const ConversionTestVal& val) {
-  return absl::StrCat(val.a);
-}
-
-}  // namespace
-
-// Flag default values can be specified with a value that converts to the flag
-// value type implicitly.
-ABSL_FLAG(ConversionTestVal, test_flag_implicit_conv,
-          ConversionTestVal::ViaImplicitConv::kTen,
-          "test flag init via implicit conversion");
-
-namespace {
-
-TEST_F(FlagTest, CanSetViaImplicitConversion) {
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 10);
-  absl::SetFlag(&FLAGS_test_flag_implicit_conv,
-                ConversionTestVal::ViaImplicitConv::kEleven);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 11);
-}
-
-// --------------------------------------------------------------------
-
-struct NonDfltConstructible {
- public:
-  // This constructor tests that we can initialize the flag with int value
-  NonDfltConstructible(int i) : value(i) {}  // NOLINT
-
-  // This constructor tests that we can't initialize the flag with char value
-  // but can with explicitly constructed NonDfltConstructible.
-  explicit NonDfltConstructible(char c) : value(100 + static_cast<int>(c)) {}
-
-  int value;
-};
-
-bool AbslParseFlag(absl::string_view in, NonDfltConstructible* ndc_out,
-                   std::string*) {
-  return absl::SimpleAtoi(in, &ndc_out->value);
-}
-std::string AbslUnparseFlag(const NonDfltConstructible& ndc) {
-  return absl::StrCat(ndc.value);
-}
-
-}  // namespace
-
-ABSL_FLAG(NonDfltConstructible, ndc_flag1, NonDfltConstructible('1'),
-          "Flag with non default constructible type");
-ABSL_FLAG(NonDfltConstructible, ndc_flag2, 0,
-          "Flag with non default constructible type");
-
-namespace {
-
-TEST_F(FlagTest, TestNonDefaultConstructibleType) {
-  EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, '1' + 100);
-  EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 0);
-
-  absl::SetFlag(&FLAGS_ndc_flag1, NonDfltConstructible('A'));
-  absl::SetFlag(&FLAGS_ndc_flag2, 25);
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, 'A' + 100);
-  EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 25);
-}
-
-}  // namespace
-
-// --------------------------------------------------------------------
-
-ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr");
-ABSL_RETIRED_FLAG(int, old_int_flag, (int)std::sqrt(10), "old descr");
-ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr"));
-
-bool initializaion_order_fiasco_test = [] {
-  // Iterate over all the flags during static initialization.
-  // This should not trigger ASan's initialization-order-fiasco.
-  auto* handle1 = absl::FindCommandLineFlag("flag_on_separate_file");
-  auto* handle2 = absl::FindCommandLineFlag("retired_flag_on_separate_file");
-  if (handle1 != nullptr && handle2 != nullptr) {
-    return handle1->Name() == handle2->Name();
-  }
-  return true;
-}();
-
-namespace {
-
-TEST_F(FlagTest, TestRetiredFlagRegistration) {
-  auto* handle = absl::FindCommandLineFlag("old_bool_flag");
-  EXPECT_TRUE(handle->IsOfType<bool>());
-  EXPECT_TRUE(handle->IsRetired());
-  handle = absl::FindCommandLineFlag("old_int_flag");
-  EXPECT_TRUE(handle->IsOfType<int>());
-  EXPECT_TRUE(handle->IsRetired());
-  handle = absl::FindCommandLineFlag("old_str_flag");
-  EXPECT_TRUE(handle->IsOfType<std::string>());
-  EXPECT_TRUE(handle->IsRetired());
-}
-
-}  // namespace
-
-// --------------------------------------------------------------------
-
-namespace {
-
-// User-defined type with small alignment, but size exceeding 16.
-struct SmallAlignUDT {
-  SmallAlignUDT() : c('A'), s(12) {}
-  char c;
-  int16_t s;
-  char bytes[14];
-};
-
-bool AbslParseFlag(absl::string_view, SmallAlignUDT*, std::string*) {
-  return true;
-}
-std::string AbslUnparseFlag(const SmallAlignUDT&) { return ""; }
-
-// User-defined type with small size, but not trivially copyable.
-struct NonTriviallyCopyableUDT {
-  NonTriviallyCopyableUDT() : c('A') {}
-  NonTriviallyCopyableUDT(const NonTriviallyCopyableUDT& rhs) : c(rhs.c) {}
-  NonTriviallyCopyableUDT& operator=(const NonTriviallyCopyableUDT& rhs) {
-    c = rhs.c;
-    return *this;
-  }
-
-  char c;
-};
-
-bool AbslParseFlag(absl::string_view, NonTriviallyCopyableUDT*, std::string*) {
-  return true;
-}
-std::string AbslUnparseFlag(const NonTriviallyCopyableUDT&) { return ""; }
-
-}  // namespace
-
-ABSL_FLAG(SmallAlignUDT, test_flag_sa_udt, {}, "help");
-ABSL_FLAG(NonTriviallyCopyableUDT, test_flag_ntc_udt, {}, "help");
-
-namespace {
-
-TEST_F(FlagTest, TestSmallAlignUDT) {
-  SmallAlignUDT value = absl::GetFlag(FLAGS_test_flag_sa_udt);
-  EXPECT_EQ(value.c, 'A');
-  EXPECT_EQ(value.s, 12);
-
-  value.c = 'B';
-  value.s = 45;
-  absl::SetFlag(&FLAGS_test_flag_sa_udt, value);
-  value = absl::GetFlag(FLAGS_test_flag_sa_udt);
-  EXPECT_EQ(value.c, 'B');
-  EXPECT_EQ(value.s, 45);
-}
-
-TEST_F(FlagTest, TestNonTriviallyCopyableUDT) {
-  NonTriviallyCopyableUDT value = absl::GetFlag(FLAGS_test_flag_ntc_udt);
-  EXPECT_EQ(value.c, 'A');
-
-  value.c = 'B';
-  absl::SetFlag(&FLAGS_test_flag_ntc_udt, value);
-  value = absl::GetFlag(FLAGS_test_flag_ntc_udt);
-  EXPECT_EQ(value.c, 'B');
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/flags/flag_test_defs.cc b/third_party/abseil_cpp/absl/flags/flag_test_defs.cc
deleted file mode 100644
index 4e1693cdb9..0000000000
--- a/third_party/abseil_cpp/absl/flags/flag_test_defs.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file is used to test the mismatch of the flag type between definition
-// and declaration. These are definitions. flag_test.cc contains declarations.
-#include <string>
-#include "absl/flags/flag.h"
-
-ABSL_FLAG(int, mistyped_int_flag, 0, "");
-ABSL_FLAG(std::string, mistyped_string_flag, "", "");
-ABSL_FLAG(bool, flag_on_separate_file, false, "");
-ABSL_RETIRED_FLAG(bool, retired_flag_on_separate_file, false, "");
diff --git a/third_party/abseil_cpp/absl/flags/internal/commandlineflag.cc b/third_party/abseil_cpp/absl/flags/internal/commandlineflag.cc
deleted file mode 100644
index 4482955c4c..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/commandlineflag.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/commandlineflag.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-FlagStateInterface::~FlagStateInterface() {}
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/internal/commandlineflag.h b/third_party/abseil_cpp/absl/flags/internal/commandlineflag.h
deleted file mode 100644
index cb46fe2e97..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/commandlineflag.h
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
-#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
-
-#include "absl/base/config.h"
-#include "absl/base/internal/fast_type_id.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// An alias for flag fast type id. This value identifies the flag value type
-// simialarly to typeid(T), without relying on RTTI being available. In most
-// cases this id is enough to uniquely identify the flag's value type. In a few
-// cases we'll have to resort to using actual RTTI implementation if it is
-// available.
-using FlagFastTypeId = absl::base_internal::FastTypeIdType;
-
-// Options that control SetCommandLineOptionWithMode.
-enum FlagSettingMode {
-  // update the flag's value unconditionally (can call this multiple times).
-  SET_FLAGS_VALUE,
-  // update the flag's value, but *only if* it has not yet been updated
-  // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef".
-  SET_FLAG_IF_DEFAULT,
-  // set the flag's default value to this.  If the flag has not been updated
-  // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef")
-  // change the flag's current value to the new default value as well.
-  SET_FLAGS_DEFAULT
-};
-
-// Options that control ParseFrom: Source of a value.
-enum ValueSource {
-  // Flag is being set by value specified on a command line.
-  kCommandLine,
-  // Flag is being set by value specified in the code.
-  kProgrammaticChange,
-};
-
-// Handle to FlagState objects. Specific flag state objects will restore state
-// of a flag produced this flag state from method CommandLineFlag::SaveState().
-class FlagStateInterface {
- public:
-  virtual ~FlagStateInterface();
-
-  // Restores the flag originated this object to the saved state.
-  virtual void Restore() const = 0;
-};
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
diff --git a/third_party/abseil_cpp/absl/flags/internal/flag.cc b/third_party/abseil_cpp/absl/flags/internal/flag.cc
deleted file mode 100644
index 1502e7f11d..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/flag.cc
+++ /dev/null
@@ -1,568 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/flag.h"
-
-#include <assert.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <string.h>
-
-#include <array>
-#include <atomic>
-#include <memory>
-#include <new>
-#include <string>
-#include <typeinfo>
-
-#include "absl/base/call_once.h"
-#include "absl/base/casts.h"
-#include "absl/base/config.h"
-#include "absl/base/optimization.h"
-#include "absl/flags/config.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/flags/usage_config.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// The help message indicating that the commandline flag has been
-// 'stripped'. It will not show up when doing "-help" and its
-// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1
-// before including absl/flags/flag.h
-const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
-
-namespace {
-
-// Currently we only validate flag values for user-defined flag types.
-bool ShouldValidateFlagValue(FlagFastTypeId flag_type_id) {
-#define DONT_VALIDATE(T, _) \
-  if (flag_type_id == base_internal::FastTypeId<T>()) return false;
-  ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(DONT_VALIDATE)
-#undef DONT_VALIDATE
-
-  return true;
-}
-
-// RAII helper used to temporarily unlock and relock `absl::Mutex`.
-// This is used when we need to ensure that locks are released while
-// invoking user supplied callbacks and then reacquired, since callbacks may
-// need to acquire these locks themselves.
-class MutexRelock {
- public:
-  explicit MutexRelock(absl::Mutex& mu) : mu_(mu) { mu_.Unlock(); }
-  ~MutexRelock() { mu_.Lock(); }
-
-  MutexRelock(const MutexRelock&) = delete;
-  MutexRelock& operator=(const MutexRelock&) = delete;
-
- private:
-  absl::Mutex& mu_;
-};
-
-}  // namespace
-
-///////////////////////////////////////////////////////////////////////////////
-// Persistent state of the flag data.
-
-class FlagImpl;
-
-class FlagState : public flags_internal::FlagStateInterface {
- public:
-  template <typename V>
-  FlagState(FlagImpl& flag_impl, const V& v, bool modified,
-            bool on_command_line, int64_t counter)
-      : flag_impl_(flag_impl),
-        value_(v),
-        modified_(modified),
-        on_command_line_(on_command_line),
-        counter_(counter) {}
-
-  ~FlagState() override {
-    if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer)
-      return;
-    flags_internal::Delete(flag_impl_.op_, value_.heap_allocated);
-  }
-
- private:
-  friend class FlagImpl;
-
-  // Restores the flag to the saved state.
-  void Restore() const override {
-    if (!flag_impl_.RestoreState(*this)) return;
-
-    ABSL_INTERNAL_LOG(INFO,
-                      absl::StrCat("Restore saved value of ", flag_impl_.Name(),
-                                   " to: ", flag_impl_.CurrentValue()));
-  }
-
-  // Flag and saved flag data.
-  FlagImpl& flag_impl_;
-  union SavedValue {
-    explicit SavedValue(void* v) : heap_allocated(v) {}
-    explicit SavedValue(int64_t v) : one_word(v) {}
-    explicit SavedValue(flags_internal::AlignedTwoWords v) : two_words(v) {}
-
-    void* heap_allocated;
-    int64_t one_word;
-    flags_internal::AlignedTwoWords two_words;
-  } value_;
-  bool modified_;
-  bool on_command_line_;
-  int64_t counter_;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Flag implementation, which does not depend on flag value type.
-
-DynValueDeleter::DynValueDeleter(FlagOpFn op_arg) : op(op_arg) {}
-
-void DynValueDeleter::operator()(void* ptr) const {
-  if (op == nullptr) return;
-
-  Delete(op, ptr);
-}
-
-void FlagImpl::Init() {
-  new (&data_guard_) absl::Mutex;
-
-  auto def_kind = static_cast<FlagDefaultKind>(def_kind_);
-
-  switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer:
-      // For this storage kind the default_value_ always points to gen_func
-      // during initialization.
-      assert(def_kind == FlagDefaultKind::kGenFunc);
-      (*default_value_.gen_func)(AlignedBufferValue());
-      break;
-    case FlagValueStorageKind::kOneWordAtomic: {
-      alignas(int64_t) std::array<char, sizeof(int64_t)> buf{};
-      if (def_kind == FlagDefaultKind::kGenFunc) {
-        (*default_value_.gen_func)(buf.data());
-      } else {
-        assert(def_kind != FlagDefaultKind::kDynamicValue);
-        std::memcpy(buf.data(), &default_value_, Sizeof(op_));
-      }
-      OneWordValue().store(absl::bit_cast<int64_t>(buf),
-                           std::memory_order_release);
-      break;
-    }
-    case FlagValueStorageKind::kTwoWordsAtomic: {
-      // For this storage kind the default_value_ always points to gen_func
-      // during initialization.
-      assert(def_kind == FlagDefaultKind::kGenFunc);
-      alignas(AlignedTwoWords) std::array<char, sizeof(AlignedTwoWords)> buf{};
-      (*default_value_.gen_func)(buf.data());
-      auto atomic_value = absl::bit_cast<AlignedTwoWords>(buf);
-      TwoWordsValue().store(atomic_value, std::memory_order_release);
-      break;
-    }
-  }
-}
-
-absl::Mutex* FlagImpl::DataGuard() const {
-  absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init,
-                  const_cast<FlagImpl*>(this));
-
-  // data_guard_ is initialized inside Init.
-  return reinterpret_cast<absl::Mutex*>(&data_guard_);
-}
-
-void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id,
-                               const std::type_info* (*gen_rtti)()) const {
-  FlagFastTypeId lhs_type_id = flags_internal::FastTypeId(op_);
-
-  // `rhs_type_id` is the fast type id corresponding to the declaration
-  // visibile at the call site. `lhs_type_id` is the fast type id
-  // corresponding to the type specified in flag definition. They must match
-  //  for this operation to be well-defined.
-  if (ABSL_PREDICT_TRUE(lhs_type_id == rhs_type_id)) return;
-
-  const std::type_info* lhs_runtime_type_id =
-      flags_internal::RuntimeTypeId(op_);
-  const std::type_info* rhs_runtime_type_id = (*gen_rtti)();
-
-  if (lhs_runtime_type_id == rhs_runtime_type_id) return;
-
-#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
-  if (*lhs_runtime_type_id == *rhs_runtime_type_id) return;
-#endif
-
-  ABSL_INTERNAL_LOG(
-      FATAL, absl::StrCat("Flag '", Name(),
-                          "' is defined as one type and declared as another"));
-}
-
-std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
-  void* res = nullptr;
-  switch (DefaultKind()) {
-    case FlagDefaultKind::kDynamicValue:
-      res = flags_internal::Clone(op_, default_value_.dynamic_value);
-      break;
-    case FlagDefaultKind::kGenFunc:
-      res = flags_internal::Alloc(op_);
-      (*default_value_.gen_func)(res);
-      break;
-    default:
-      res = flags_internal::Clone(op_, &default_value_);
-      break;
-  }
-  return {res, DynValueDeleter{op_}};
-}
-
-void FlagImpl::StoreValue(const void* src) {
-  switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer:
-      Copy(op_, src, AlignedBufferValue());
-      break;
-    case FlagValueStorageKind::kOneWordAtomic: {
-      int64_t one_word_val = 0;
-      std::memcpy(&one_word_val, src, Sizeof(op_));
-      OneWordValue().store(one_word_val, std::memory_order_release);
-      break;
-    }
-    case FlagValueStorageKind::kTwoWordsAtomic: {
-      AlignedTwoWords two_words_val{0, 0};
-      std::memcpy(&two_words_val, src, Sizeof(op_));
-      TwoWordsValue().store(two_words_val, std::memory_order_release);
-      break;
-    }
-  }
-
-  modified_ = true;
-  ++counter_;
-  InvokeCallback();
-}
-
-absl::string_view FlagImpl::Name() const { return name_; }
-
-std::string FlagImpl::Filename() const {
-  return flags_internal::GetUsageConfig().normalize_filename(filename_);
-}
-
-std::string FlagImpl::Help() const {
-  return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal
-                                                    : help_.gen_func();
-}
-
-FlagFastTypeId FlagImpl::TypeId() const {
-  return flags_internal::FastTypeId(op_);
-}
-
-bool FlagImpl::IsSpecifiedOnCommandLine() const {
-  absl::MutexLock l(DataGuard());
-  return on_command_line_;
-}
-
-std::string FlagImpl::DefaultValue() const {
-  absl::MutexLock l(DataGuard());
-
-  auto obj = MakeInitValue();
-  return flags_internal::Unparse(op_, obj.get());
-}
-
-std::string FlagImpl::CurrentValue() const {
-  auto* guard = DataGuard();  // Make sure flag initialized
-  switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer: {
-      absl::MutexLock l(guard);
-      return flags_internal::Unparse(op_, AlignedBufferValue());
-    }
-    case FlagValueStorageKind::kOneWordAtomic: {
-      const auto one_word_val =
-          absl::bit_cast<std::array<char, sizeof(int64_t)>>(
-              OneWordValue().load(std::memory_order_acquire));
-      return flags_internal::Unparse(op_, one_word_val.data());
-    }
-    case FlagValueStorageKind::kTwoWordsAtomic: {
-      const auto two_words_val =
-          absl::bit_cast<std::array<char, sizeof(AlignedTwoWords)>>(
-              TwoWordsValue().load(std::memory_order_acquire));
-      return flags_internal::Unparse(op_, two_words_val.data());
-    }
-  }
-
-  return "";
-}
-
-void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) {
-  absl::MutexLock l(DataGuard());
-
-  if (callback_ == nullptr) {
-    callback_ = new FlagCallback;
-  }
-  callback_->func = mutation_callback;
-
-  InvokeCallback();
-}
-
-void FlagImpl::InvokeCallback() const {
-  if (!callback_) return;
-
-  // Make a copy of the C-style function pointer that we are about to invoke
-  // before we release the lock guarding it.
-  FlagCallbackFunc cb = callback_->func;
-
-  // If the flag has a mutation callback this function invokes it. While the
-  // callback is being invoked the primary flag's mutex is unlocked and it is
-  // re-locked back after call to callback is completed. Callback invocation is
-  // guarded by flag's secondary mutex instead which prevents concurrent
-  // callback invocation. Note that it is possible for other thread to grab the
-  // primary lock and update flag's value at any time during the callback
-  // invocation. This is by design. Callback can get a value of the flag if
-  // necessary, but it might be different from the value initiated the callback
-  // and it also can be different by the time the callback invocation is
-  // completed. Requires that *primary_lock be held in exclusive mode; it may be
-  // released and reacquired by the implementation.
-  MutexRelock relock(*DataGuard());
-  absl::MutexLock lock(&callback_->guard);
-  cb();
-}
-
-std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
-  absl::MutexLock l(DataGuard());
-
-  bool modified = modified_;
-  bool on_command_line = on_command_line_;
-  switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer: {
-      return absl::make_unique<FlagState>(
-          *this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
-          on_command_line, counter_);
-    }
-    case FlagValueStorageKind::kOneWordAtomic: {
-      return absl::make_unique<FlagState>(
-          *this, OneWordValue().load(std::memory_order_acquire), modified,
-          on_command_line, counter_);
-    }
-    case FlagValueStorageKind::kTwoWordsAtomic: {
-      return absl::make_unique<FlagState>(
-          *this, TwoWordsValue().load(std::memory_order_acquire), modified,
-          on_command_line, counter_);
-    }
-  }
-  return nullptr;
-}
-
-bool FlagImpl::RestoreState(const FlagState& flag_state) {
-  absl::MutexLock l(DataGuard());
-
-  if (flag_state.counter_ == counter_) {
-    return false;
-  }
-
-  switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer:
-      StoreValue(flag_state.value_.heap_allocated);
-      break;
-    case FlagValueStorageKind::kOneWordAtomic:
-      StoreValue(&flag_state.value_.one_word);
-      break;
-    case FlagValueStorageKind::kTwoWordsAtomic:
-      StoreValue(&flag_state.value_.two_words);
-      break;
-  }
-
-  modified_ = flag_state.modified_;
-  on_command_line_ = flag_state.on_command_line_;
-
-  return true;
-}
-
-template <typename StorageT>
-StorageT* FlagImpl::OffsetValue() const {
-  char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this));
-  // The offset is deduced via Flag value type specific op_.
-  size_t offset = flags_internal::ValueOffset(op_);
-
-  return reinterpret_cast<StorageT*>(p + offset);
-}
-
-void* FlagImpl::AlignedBufferValue() const {
-  assert(ValueStorageKind() == FlagValueStorageKind::kAlignedBuffer);
-  return OffsetValue<void>();
-}
-
-std::atomic<int64_t>& FlagImpl::OneWordValue() const {
-  assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic);
-  return OffsetValue<FlagOneWordValue>()->value;
-}
-
-std::atomic<AlignedTwoWords>& FlagImpl::TwoWordsValue() const {
-  assert(ValueStorageKind() == FlagValueStorageKind::kTwoWordsAtomic);
-  return OffsetValue<FlagTwoWordsValue>()->value;
-}
-
-// Attempts to parse supplied `value` string using parsing routine in the `flag`
-// argument. If parsing successful, this function replaces the dst with newly
-// parsed value. In case if any error is encountered in either step, the error
-// message is stored in 'err'
-std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
-    absl::string_view value, std::string& err) const {
-  std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue();
-
-  std::string parse_err;
-  if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) {
-    absl::string_view err_sep = parse_err.empty() ? "" : "; ";
-    err = absl::StrCat("Illegal value '", value, "' specified for flag '",
-                       Name(), "'", err_sep, parse_err);
-    return nullptr;
-  }
-
-  return tentative_value;
-}
-
-void FlagImpl::Read(void* dst) const {
-  auto* guard = DataGuard();  // Make sure flag initialized
-  switch (ValueStorageKind()) {
-    case FlagValueStorageKind::kAlignedBuffer: {
-      absl::MutexLock l(guard);
-      flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst);
-      break;
-    }
-    case FlagValueStorageKind::kOneWordAtomic: {
-      const int64_t one_word_val =
-          OneWordValue().load(std::memory_order_acquire);
-      std::memcpy(dst, &one_word_val, Sizeof(op_));
-      break;
-    }
-    case FlagValueStorageKind::kTwoWordsAtomic: {
-      const AlignedTwoWords two_words_val =
-          TwoWordsValue().load(std::memory_order_acquire);
-      std::memcpy(dst, &two_words_val, Sizeof(op_));
-      break;
-    }
-  }
-}
-
-void FlagImpl::Write(const void* src) {
-  absl::MutexLock l(DataGuard());
-
-  if (ShouldValidateFlagValue(flags_internal::FastTypeId(op_))) {
-    std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src),
-                                               DynValueDeleter{op_}};
-    std::string ignored_error;
-    std::string src_as_str = flags_internal::Unparse(op_, src);
-    if (!flags_internal::Parse(op_, src_as_str, obj.get(), &ignored_error)) {
-      ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
-                                            "' to invalid value ", src_as_str));
-    }
-  }
-
-  StoreValue(src);
-}
-
-// Sets the value of the flag based on specified string `value`. If the flag
-// was successfully set to new value, it returns true. Otherwise, sets `err`
-// to indicate the error, leaves the flag unchanged, and returns false. There
-// are three ways to set the flag's value:
-//  * Update the current flag value
-//  * Update the flag's default value
-//  * Update the current flag value if it was never set before
-// The mode is selected based on 'set_mode' parameter.
-bool FlagImpl::ParseFrom(absl::string_view value, FlagSettingMode set_mode,
-                         ValueSource source, std::string& err) {
-  absl::MutexLock l(DataGuard());
-
-  switch (set_mode) {
-    case SET_FLAGS_VALUE: {
-      // set or modify the flag's value
-      auto tentative_value = TryParse(value, err);
-      if (!tentative_value) return false;
-
-      StoreValue(tentative_value.get());
-
-      if (source == kCommandLine) {
-        on_command_line_ = true;
-      }
-      break;
-    }
-    case SET_FLAG_IF_DEFAULT: {
-      // set the flag's value, but only if it hasn't been set by someone else
-      if (modified_) {
-        // TODO(rogeeff): review and fix this semantic. Currently we do not fail
-        // in this case if flag is modified. This is misleading since the flag's
-        // value is not updated even though we return true.
-        // *err = absl::StrCat(Name(), " is already set to ",
-        //                     CurrentValue(), "\n");
-        // return false;
-        return true;
-      }
-      auto tentative_value = TryParse(value, err);
-      if (!tentative_value) return false;
-
-      StoreValue(tentative_value.get());
-      break;
-    }
-    case SET_FLAGS_DEFAULT: {
-      auto tentative_value = TryParse(value, err);
-      if (!tentative_value) return false;
-
-      if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
-        void* old_value = default_value_.dynamic_value;
-        default_value_.dynamic_value = tentative_value.release();
-        tentative_value.reset(old_value);
-      } else {
-        default_value_.dynamic_value = tentative_value.release();
-        def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue);
-      }
-
-      if (!modified_) {
-        // Need to set both default value *and* current, in this case.
-        StoreValue(default_value_.dynamic_value);
-        modified_ = false;
-      }
-      break;
-    }
-  }
-
-  return true;
-}
-
-void FlagImpl::CheckDefaultValueParsingRoundtrip() const {
-  std::string v = DefaultValue();
-
-  absl::MutexLock lock(DataGuard());
-
-  auto dst = MakeInitValue();
-  std::string error;
-  if (!flags_internal::Parse(op_, v, dst.get(), &error)) {
-    ABSL_INTERNAL_LOG(
-        FATAL,
-        absl::StrCat("Flag ", Name(), " (from ", Filename(),
-                     "): string form of default value '", v,
-                     "' could not be parsed; error=", error));
-  }
-
-  // We do not compare dst to def since parsing/unparsing may make
-  // small changes, e.g., precision loss for floating point types.
-}
-
-bool FlagImpl::ValidateInputValue(absl::string_view value) const {
-  absl::MutexLock l(DataGuard());
-
-  auto obj = MakeInitValue();
-  std::string ignored_error;
-  return flags_internal::Parse(op_, value, obj.get(), &ignored_error);
-}
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/internal/flag.h b/third_party/abseil_cpp/absl/flags/internal/flag.h
deleted file mode 100644
index 370d8a02ef..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/flag.h
+++ /dev/null
@@ -1,775 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
-#define ABSL_FLAGS_INTERNAL_FLAG_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <atomic>
-#include <cstring>
-#include <memory>
-#include <new>
-#include <string>
-#include <type_traits>
-#include <typeinfo>
-
-#include "absl/base/attributes.h"
-#include "absl/base/call_once.h"
-#include "absl/base/config.h"
-#include "absl/base/optimization.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/flags/commandlineflag.h"
-#include "absl/flags/config.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/flags/internal/registry.h"
-#include "absl/flags/marshalling.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-#include "absl/synchronization/mutex.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-///////////////////////////////////////////////////////////////////////////////
-// Forward declaration of absl::Flag<T> public API.
-namespace flags_internal {
-template <typename T>
-class Flag;
-}  // namespace flags_internal
-
-#if defined(_MSC_VER) && !defined(__clang__)
-template <typename T>
-class Flag;
-#else
-template <typename T>
-using Flag = flags_internal::Flag<T>;
-#endif
-
-template <typename T>
-ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag);
-
-template <typename T>
-void SetFlag(absl::Flag<T>* flag, const T& v);
-
-template <typename T, typename V>
-void SetFlag(absl::Flag<T>* flag, const V& v);
-
-template <typename U>
-const CommandLineFlag& GetFlagReflectionHandle(const absl::Flag<U>& f);
-
-///////////////////////////////////////////////////////////////////////////////
-// Flag value type operations, eg., parsing, copying, etc. are provided
-// by function specific to that type with a signature matching FlagOpFn.
-
-namespace flags_internal {
-
-enum class FlagOp {
-  kAlloc,
-  kDelete,
-  kCopy,
-  kCopyConstruct,
-  kSizeof,
-  kFastTypeId,
-  kRuntimeTypeId,
-  kParse,
-  kUnparse,
-  kValueOffset,
-};
-using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*);
-
-// Forward declaration for Flag value specific operations.
-template <typename T>
-void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3);
-
-// Allocate aligned memory for a flag value.
-inline void* Alloc(FlagOpFn op) {
-  return op(FlagOp::kAlloc, nullptr, nullptr, nullptr);
-}
-// Deletes memory interpreting obj as flag value type pointer.
-inline void Delete(FlagOpFn op, void* obj) {
-  op(FlagOp::kDelete, nullptr, obj, nullptr);
-}
-// Copies src to dst interpreting as flag value type pointers.
-inline void Copy(FlagOpFn op, const void* src, void* dst) {
-  op(FlagOp::kCopy, src, dst, nullptr);
-}
-// Construct a copy of flag value in a location pointed by dst
-// based on src - pointer to the flag's value.
-inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
-  op(FlagOp::kCopyConstruct, src, dst, nullptr);
-}
-// Makes a copy of flag value pointed by obj.
-inline void* Clone(FlagOpFn op, const void* obj) {
-  void* res = flags_internal::Alloc(op);
-  flags_internal::CopyConstruct(op, obj, res);
-  return res;
-}
-// Returns true if parsing of input text is successfull.
-inline bool Parse(FlagOpFn op, absl::string_view text, void* dst,
-                  std::string* error) {
-  return op(FlagOp::kParse, &text, dst, error) != nullptr;
-}
-// Returns string representing supplied value.
-inline std::string Unparse(FlagOpFn op, const void* val) {
-  std::string result;
-  op(FlagOp::kUnparse, val, &result, nullptr);
-  return result;
-}
-// Returns size of flag value type.
-inline size_t Sizeof(FlagOpFn op) {
-  // This sequence of casts reverses the sequence from
-  // `flags_internal::FlagOps()`
-  return static_cast<size_t>(reinterpret_cast<intptr_t>(
-      op(FlagOp::kSizeof, nullptr, nullptr, nullptr)));
-}
-// Returns fast type id coresponding to the value type.
-inline FlagFastTypeId FastTypeId(FlagOpFn op) {
-  return reinterpret_cast<FlagFastTypeId>(
-      op(FlagOp::kFastTypeId, nullptr, nullptr, nullptr));
-}
-// Returns fast type id coresponding to the value type.
-inline const std::type_info* RuntimeTypeId(FlagOpFn op) {
-  return reinterpret_cast<const std::type_info*>(
-      op(FlagOp::kRuntimeTypeId, nullptr, nullptr, nullptr));
-}
-// Returns offset of the field value_ from the field impl_ inside of
-// absl::Flag<T> data. Given FlagImpl pointer p you can get the
-// location of the corresponding value as:
-//      reinterpret_cast<char*>(p) + ValueOffset().
-inline ptrdiff_t ValueOffset(FlagOpFn op) {
-  // This sequence of casts reverses the sequence from
-  // `flags_internal::FlagOps()`
-  return static_cast<ptrdiff_t>(reinterpret_cast<intptr_t>(
-      op(FlagOp::kValueOffset, nullptr, nullptr, nullptr)));
-}
-
-// Returns an address of RTTI's typeid(T).
-template <typename T>
-inline const std::type_info* GenRuntimeTypeId() {
-#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
-  return &typeid(T);
-#else
-  return nullptr;
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Flag help auxiliary structs.
-
-// This is help argument for absl::Flag encapsulating the string literal pointer
-// or pointer to function generating it as well as enum descriminating two
-// cases.
-using HelpGenFunc = std::string (*)();
-
-template <size_t N>
-struct FixedCharArray {
-  char value[N];
-
-  template <size_t... I>
-  static constexpr FixedCharArray<N> FromLiteralString(
-      absl::string_view str, absl::index_sequence<I...>) {
-    return (void)str, FixedCharArray<N>({{str[I]..., '\0'}});
-  }
-};
-
-template <typename Gen, size_t N = Gen::Value().size()>
-constexpr FixedCharArray<N + 1> HelpStringAsArray(int) {
-  return FixedCharArray<N + 1>::FromLiteralString(
-      Gen::Value(), absl::make_index_sequence<N>{});
-}
-
-template <typename Gen>
-constexpr std::false_type HelpStringAsArray(char) {
-  return std::false_type{};
-}
-
-union FlagHelpMsg {
-  constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {}
-  constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {}
-
-  const char* literal;
-  HelpGenFunc gen_func;
-};
-
-enum class FlagHelpKind : uint8_t { kLiteral = 0, kGenFunc = 1 };
-
-struct FlagHelpArg {
-  FlagHelpMsg source;
-  FlagHelpKind kind;
-};
-
-extern const char kStrippedFlagHelp[];
-
-// These two HelpArg overloads allows us to select at compile time one of two
-// way to pass Help argument to absl::Flag. We'll be passing
-// AbslFlagHelpGenFor##name as Gen and integer 0 as a single argument to prefer
-// first overload if possible. If help message is evaluatable on constexpr
-// context We'll be able to make FixedCharArray out of it and we'll choose first
-// overload. In this case the help message expression is immediately evaluated
-// and is used to construct the absl::Flag. No additionl code is generated by
-// ABSL_FLAG Otherwise SFINAE kicks in and first overload is dropped from the
-// consideration, in which case the second overload will be used. The second
-// overload does not attempt to evaluate the help message expression
-// immediately and instead delays the evaluation by returing the function
-// pointer (&T::NonConst) genering the help message when necessary. This is
-// evaluatable in constexpr context, but the cost is an extra function being
-// generated in the ABSL_FLAG code.
-template <typename Gen, size_t N>
-constexpr FlagHelpArg HelpArg(const FixedCharArray<N>& value) {
-  return {FlagHelpMsg(value.value), FlagHelpKind::kLiteral};
-}
-
-template <typename Gen>
-constexpr FlagHelpArg HelpArg(std::false_type) {
-  return {FlagHelpMsg(&Gen::NonConst), FlagHelpKind::kGenFunc};
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Flag default value auxiliary structs.
-
-// Signature for the function generating the initial flag value (usually
-// based on default value supplied in flag's definition)
-using FlagDfltGenFunc = void (*)(void*);
-
-union FlagDefaultSrc {
-  constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg)
-      : gen_func(gen_func_arg) {}
-
-#define ABSL_FLAGS_INTERNAL_DFLT_FOR_TYPE(T, name) \
-  T name##_value;                                  \
-  constexpr explicit FlagDefaultSrc(T value) : name##_value(value) {}  // NOLINT
-  ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(ABSL_FLAGS_INTERNAL_DFLT_FOR_TYPE)
-#undef ABSL_FLAGS_INTERNAL_DFLT_FOR_TYPE
-
-  void* dynamic_value;
-  FlagDfltGenFunc gen_func;
-};
-
-enum class FlagDefaultKind : uint8_t {
-  kDynamicValue = 0,
-  kGenFunc = 1,
-  kOneWord = 2  // for default values UP to one word in size
-};
-
-struct FlagDefaultArg {
-  FlagDefaultSrc source;
-  FlagDefaultKind kind;
-};
-
-// This struct and corresponding overload to InitDefaultValue are used to
-// facilitate usage of {} as default value in ABSL_FLAG macro.
-// TODO(rogeeff): Fix handling types with explicit constructors.
-struct EmptyBraces {};
-
-template <typename T>
-constexpr T InitDefaultValue(T t) {
-  return t;
-}
-
-template <typename T>
-constexpr T InitDefaultValue(EmptyBraces) {
-  return T{};
-}
-
-template <typename ValueT, typename GenT,
-          typename std::enable_if<std::is_integral<ValueT>::value, int>::type =
-              (GenT{}, 0)>
-constexpr FlagDefaultArg DefaultArg(int) {
-  return {FlagDefaultSrc(GenT{}.value), FlagDefaultKind::kOneWord};
-}
-
-template <typename ValueT, typename GenT>
-constexpr FlagDefaultArg DefaultArg(char) {
-  return {FlagDefaultSrc(&GenT::Gen), FlagDefaultKind::kGenFunc};
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Flag current value auxiliary structs.
-
-constexpr int64_t UninitializedFlagValue() { return 0xababababababababll; }
-
-template <typename T>
-using FlagUseOneWordStorage = std::integral_constant<
-    bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
-              (sizeof(T) <= 8)>;
-
-#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
-// Clang does not always produce cmpxchg16b instruction when alignment of a 16
-// bytes type is not 16.
-struct alignas(16) AlignedTwoWords {
-  int64_t first;
-  int64_t second;
-
-  bool IsInitialized() const {
-    return first != flags_internal::UninitializedFlagValue();
-  }
-};
-
-template <typename T>
-using FlagUseTwoWordsStorage = std::integral_constant<
-    bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
-              (sizeof(T) > 8) && (sizeof(T) <= 16)>;
-#else
-// This is actually unused and only here to avoid ifdefs in other palces.
-struct AlignedTwoWords {
-  constexpr AlignedTwoWords() noexcept : dummy() {}
-  constexpr AlignedTwoWords(int64_t, int64_t) noexcept : dummy() {}
-  char dummy;
-
-  bool IsInitialized() const {
-    std::abort();
-    return true;
-  }
-};
-
-// This trait should be type dependent, otherwise SFINAE below will fail
-template <typename T>
-using FlagUseTwoWordsStorage =
-    std::integral_constant<bool, sizeof(T) != sizeof(T)>;
-#endif
-
-template <typename T>
-using FlagUseBufferStorage =
-    std::integral_constant<bool, !FlagUseOneWordStorage<T>::value &&
-                                     !FlagUseTwoWordsStorage<T>::value>;
-
-enum class FlagValueStorageKind : uint8_t {
-  kAlignedBuffer = 0,
-  kOneWordAtomic = 1,
-  kTwoWordsAtomic = 2
-};
-
-template <typename T>
-static constexpr FlagValueStorageKind StorageKind() {
-  return FlagUseBufferStorage<T>::value
-             ? FlagValueStorageKind::kAlignedBuffer
-             : FlagUseOneWordStorage<T>::value
-                   ? FlagValueStorageKind::kOneWordAtomic
-                   : FlagValueStorageKind::kTwoWordsAtomic;
-}
-
-struct FlagOneWordValue {
-  constexpr FlagOneWordValue() : value(UninitializedFlagValue()) {}
-
-  std::atomic<int64_t> value;
-};
-
-struct FlagTwoWordsValue {
-  constexpr FlagTwoWordsValue()
-      : value(AlignedTwoWords{UninitializedFlagValue(), 0}) {}
-
-  std::atomic<AlignedTwoWords> value;
-};
-
-template <typename T,
-          FlagValueStorageKind Kind = flags_internal::StorageKind<T>()>
-struct FlagValue;
-
-template <typename T>
-struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
-  bool Get(T&) const { return false; }
-
-  alignas(T) char value[sizeof(T)];
-};
-
-template <typename T>
-struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue {
-  bool Get(T& dst) const {
-    int64_t one_word_val = value.load(std::memory_order_acquire);
-    if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) {
-      return false;
-    }
-    std::memcpy(&dst, static_cast<const void*>(&one_word_val), sizeof(T));
-    return true;
-  }
-};
-
-template <typename T>
-struct FlagValue<T, FlagValueStorageKind::kTwoWordsAtomic> : FlagTwoWordsValue {
-  bool Get(T& dst) const {
-    AlignedTwoWords two_words_val = value.load(std::memory_order_acquire);
-    if (ABSL_PREDICT_FALSE(!two_words_val.IsInitialized())) {
-      return false;
-    }
-    std::memcpy(&dst, static_cast<const void*>(&two_words_val), sizeof(T));
-    return true;
-  }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Flag callback auxiliary structs.
-
-// Signature for the mutation callback used by watched Flags
-// The callback is noexcept.
-// TODO(rogeeff): add noexcept after C++17 support is added.
-using FlagCallbackFunc = void (*)();
-
-struct FlagCallback {
-  FlagCallbackFunc func;
-  absl::Mutex guard;  // Guard for concurrent callback invocations.
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Flag implementation, which does not depend on flag value type.
-// The class encapsulates the Flag's data and access to it.
-
-struct DynValueDeleter {
-  explicit DynValueDeleter(FlagOpFn op_arg = nullptr);
-  void operator()(void* ptr) const;
-
-  FlagOpFn op;
-};
-
-class FlagState;
-
-class FlagImpl final : public CommandLineFlag {
- public:
-  constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op,
-                     FlagHelpArg help, FlagValueStorageKind value_kind,
-                     FlagDefaultArg default_arg)
-      : name_(name),
-        filename_(filename),
-        op_(op),
-        help_(help.source),
-        help_source_kind_(static_cast<uint8_t>(help.kind)),
-        value_storage_kind_(static_cast<uint8_t>(value_kind)),
-        def_kind_(static_cast<uint8_t>(default_arg.kind)),
-        modified_(false),
-        on_command_line_(false),
-        counter_(0),
-        callback_(nullptr),
-        default_value_(default_arg.source),
-        data_guard_{} {}
-
-  // Constant access methods
-  void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard());
-
-  // Mutating access methods
-  void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard());
-
-  // Interfaces to operate on callbacks.
-  void SetCallback(const FlagCallbackFunc mutation_callback)
-      ABSL_LOCKS_EXCLUDED(*DataGuard());
-  void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
-
-  // Used in read/write operations to validate source/target has correct type.
-  // For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to
-  // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed
-  // int. To do that we pass the "assumed" type id (which is deduced from type
-  // int) as an argument `type_id`, which is in turn is validated against the
-  // type id stored in flag object by flag definition statement.
-  void AssertValidType(FlagFastTypeId type_id,
-                       const std::type_info* (*gen_rtti)()) const;
-
- private:
-  template <typename T>
-  friend class Flag;
-  friend class FlagState;
-
-  // Ensures that `data_guard_` is initialized and returns it.
-  absl::Mutex* DataGuard() const
-      ABSL_LOCK_RETURNED(reinterpret_cast<absl::Mutex*>(data_guard_));
-  // Returns heap allocated value of type T initialized with default value.
-  std::unique_ptr<void, DynValueDeleter> MakeInitValue() const
-      ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
-  // Flag initialization called via absl::call_once.
-  void Init();
-
-  // Offset value access methods. One per storage kind. These methods to not
-  // respect const correctness, so be very carefull using them.
-
-  // This is a shared helper routine which encapsulates most of the magic. Since
-  // it is only used inside the three routines below, which are defined in
-  // flag.cc, we can define it in that file as well.
-  template <typename StorageT>
-  StorageT* OffsetValue() const;
-  // This is an accessor for a value stored in an aligned buffer storage.
-  // Returns a mutable pointer to the start of a buffer.
-  void* AlignedBufferValue() const;
-  // This is an accessor for a value stored as one word atomic. Returns a
-  // mutable reference to an atomic value.
-  std::atomic<int64_t>& OneWordValue() const;
-  // This is an accessor for a value stored as two words atomic. Returns a
-  // mutable reference to an atomic value.
-  std::atomic<AlignedTwoWords>& TwoWordsValue() const;
-
-  // Attempts to parse supplied `value` string. If parsing is successful,
-  // returns new value. Otherwise returns nullptr.
-  std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value,
-                                                  std::string& err) const
-      ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
-  // Stores the flag value based on the pointer to the source.
-  void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
-
-  FlagHelpKind HelpSourceKind() const {
-    return static_cast<FlagHelpKind>(help_source_kind_);
-  }
-  FlagValueStorageKind ValueStorageKind() const {
-    return static_cast<FlagValueStorageKind>(value_storage_kind_);
-  }
-  FlagDefaultKind DefaultKind() const
-      ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()) {
-    return static_cast<FlagDefaultKind>(def_kind_);
-  }
-
-  // CommandLineFlag interface implementation
-  absl::string_view Name() const override;
-  std::string Filename() const override;
-  std::string Help() const override;
-  FlagFastTypeId TypeId() const override;
-  bool IsSpecifiedOnCommandLine() const override
-      ABSL_LOCKS_EXCLUDED(*DataGuard());
-  std::string DefaultValue() const override ABSL_LOCKS_EXCLUDED(*DataGuard());
-  std::string CurrentValue() const override ABSL_LOCKS_EXCLUDED(*DataGuard());
-  bool ValidateInputValue(absl::string_view value) const override
-      ABSL_LOCKS_EXCLUDED(*DataGuard());
-  void CheckDefaultValueParsingRoundtrip() const override
-      ABSL_LOCKS_EXCLUDED(*DataGuard());
-
-  // Interfaces to save and restore flags to/from persistent state.
-  // Returns current flag state or nullptr if flag does not support
-  // saving and restoring a state.
-  std::unique_ptr<FlagStateInterface> SaveState() override
-      ABSL_LOCKS_EXCLUDED(*DataGuard());
-
-  // Restores the flag state to the supplied state object. If there is
-  // nothing to restore returns false. Otherwise returns true.
-  bool RestoreState(const FlagState& flag_state)
-      ABSL_LOCKS_EXCLUDED(*DataGuard());
-
-  bool ParseFrom(absl::string_view value, FlagSettingMode set_mode,
-                 ValueSource source, std::string& error) override
-      ABSL_LOCKS_EXCLUDED(*DataGuard());
-
-  // Immutable flag's state.
-
-  // Flags name passed to ABSL_FLAG as second arg.
-  const char* const name_;
-  // The file name where ABSL_FLAG resides.
-  const char* const filename_;
-  // Type-specific operations "vtable".
-  const FlagOpFn op_;
-  // Help message literal or function to generate it.
-  const FlagHelpMsg help_;
-  // Indicates if help message was supplied as literal or generator func.
-  const uint8_t help_source_kind_ : 1;
-  // Kind of storage this flag is using for the flag's value.
-  const uint8_t value_storage_kind_ : 2;
-
-  uint8_t : 0;  // The bytes containing the const bitfields must not be
-                // shared with bytes containing the mutable bitfields.
-
-  // Mutable flag's state (guarded by `data_guard_`).
-
-  // def_kind_ is not guard by DataGuard() since it is accessed in Init without
-  // locks.
-  uint8_t def_kind_ : 2;
-  // Has this flag's value been modified?
-  bool modified_ : 1 ABSL_GUARDED_BY(*DataGuard());
-  // Has this flag been specified on command line.
-  bool on_command_line_ : 1 ABSL_GUARDED_BY(*DataGuard());
-
-  // Unique tag for absl::call_once call to initialize this flag.
-  absl::once_flag init_control_;
-
-  // Mutation counter
-  int64_t counter_ ABSL_GUARDED_BY(*DataGuard());
-  // Optional flag's callback and absl::Mutex to guard the invocations.
-  FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard());
-  // Either a pointer to the function generating the default value based on the
-  // value specified in ABSL_FLAG or pointer to the dynamically set default
-  // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish
-  // these two cases.
-  FlagDefaultSrc default_value_;
-
-  // This is reserved space for an absl::Mutex to guard flag data. It will be
-  // initialized in FlagImpl::Init via placement new.
-  // We can't use "absl::Mutex data_guard_", since this class is not literal.
-  // We do not want to use "absl::Mutex* data_guard_", since this would require
-  // heap allocation during initialization, which is both slows program startup
-  // and can fail. Using reserved space + placement new allows us to avoid both
-  // problems.
-  alignas(absl::Mutex) mutable char data_guard_[sizeof(absl::Mutex)];
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// The Flag object parameterized by the flag's value type. This class implements
-// flag reflection handle interface.
-
-template <typename T>
-class Flag {
- public:
-  constexpr Flag(const char* name, const char* filename, FlagHelpArg help,
-                 const FlagDefaultArg default_arg)
-      : impl_(name, filename, &FlagOps<T>, help,
-              flags_internal::StorageKind<T>(), default_arg),
-        value_() {}
-
-  // CommandLineFlag interface
-  absl::string_view Name() const { return impl_.Name(); }
-  std::string Filename() const { return impl_.Filename(); }
-  std::string Help() const { return impl_.Help(); }
-  // Do not use. To be removed.
-  bool IsSpecifiedOnCommandLine() const {
-    return impl_.IsSpecifiedOnCommandLine();
-  }
-  std::string DefaultValue() const { return impl_.DefaultValue(); }
-  std::string CurrentValue() const { return impl_.CurrentValue(); }
-
- private:
-  template <typename, bool>
-  friend class FlagRegistrar;
-  friend class FlagImplPeer;
-
-  T Get() const {
-    // See implementation notes in CommandLineFlag::Get().
-    union U {
-      T value;
-      U() {}
-      ~U() { value.~T(); }
-    };
-    U u;
-
-#if !defined(NDEBUG)
-    impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>);
-#endif
-
-    if (!value_.Get(u.value)) impl_.Read(&u.value);
-    return std::move(u.value);
-  }
-  void Set(const T& v) {
-    impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>);
-    impl_.Write(&v);
-  }
-
-  // Access to the reflection.
-  const CommandLineFlag& Reflect() const { return impl_; }
-
-  // Flag's data
-  // The implementation depends on value_ field to be placed exactly after the
-  // impl_ field, so that impl_ can figure out the offset to the value and
-  // access it.
-  FlagImpl impl_;
-  FlagValue<T> value_;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Trampoline for friend access
-
-class FlagImplPeer {
- public:
-  template <typename T, typename FlagType>
-  static T InvokeGet(const FlagType& flag) {
-    return flag.Get();
-  }
-  template <typename FlagType, typename T>
-  static void InvokeSet(FlagType& flag, const T& v) {
-    flag.Set(v);
-  }
-  template <typename FlagType>
-  static const CommandLineFlag& InvokeReflect(const FlagType& f) {
-    return f.Reflect();
-  }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementation of Flag value specific operations routine.
-template <typename T>
-void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) {
-  switch (op) {
-    case FlagOp::kAlloc: {
-      std::allocator<T> alloc;
-      return std::allocator_traits<std::allocator<T>>::allocate(alloc, 1);
-    }
-    case FlagOp::kDelete: {
-      T* p = static_cast<T*>(v2);
-      p->~T();
-      std::allocator<T> alloc;
-      std::allocator_traits<std::allocator<T>>::deallocate(alloc, p, 1);
-      return nullptr;
-    }
-    case FlagOp::kCopy:
-      *static_cast<T*>(v2) = *static_cast<const T*>(v1);
-      return nullptr;
-    case FlagOp::kCopyConstruct:
-      new (v2) T(*static_cast<const T*>(v1));
-      return nullptr;
-    case FlagOp::kSizeof:
-      return reinterpret_cast<void*>(static_cast<uintptr_t>(sizeof(T)));
-    case FlagOp::kFastTypeId:
-      return const_cast<void*>(base_internal::FastTypeId<T>());
-    case FlagOp::kRuntimeTypeId:
-      return const_cast<std::type_info*>(GenRuntimeTypeId<T>());
-    case FlagOp::kParse: {
-      // Initialize the temporary instance of type T based on current value in
-      // destination (which is going to be flag's default value).
-      T temp(*static_cast<T*>(v2));
-      if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp,
-                              static_cast<std::string*>(v3))) {
-        return nullptr;
-      }
-      *static_cast<T*>(v2) = std::move(temp);
-      return v2;
-    }
-    case FlagOp::kUnparse:
-      *static_cast<std::string*>(v2) =
-          absl::UnparseFlag<T>(*static_cast<const T*>(v1));
-      return nullptr;
-    case FlagOp::kValueOffset: {
-      // Round sizeof(FlagImp) to a multiple of alignof(FlagValue<T>) to get the
-      // offset of the data.
-      ptrdiff_t round_to = alignof(FlagValue<T>);
-      ptrdiff_t offset =
-          (sizeof(FlagImpl) + round_to - 1) / round_to * round_to;
-      return reinterpret_cast<void*>(offset);
-    }
-  }
-  return nullptr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// This class facilitates Flag object registration and tail expression-based
-// flag definition, for example:
-// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
-struct FlagRegistrarEmpty {};
-template <typename T, bool do_register>
-class FlagRegistrar {
- public:
-  explicit FlagRegistrar(Flag<T>& flag) : flag_(flag) {
-    if (do_register) flags_internal::RegisterCommandLineFlag(flag_.impl_);
-  }
-
-  FlagRegistrar OnUpdate(FlagCallbackFunc cb) && {
-    flag_.impl_.SetCallback(cb);
-    return *this;
-  }
-
-  // Make the registrar "die" gracefully as an empty struct on a line where
-  // registration happens. Registrar objects are intended to live only as
-  // temporary.
-  operator FlagRegistrarEmpty() const { return {}; }  // NOLINT
-
- private:
-  Flag<T>& flag_;  // Flag being registered (not owned).
-};
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_INTERNAL_FLAG_H_
diff --git a/third_party/abseil_cpp/absl/flags/internal/parse.h b/third_party/abseil_cpp/absl/flags/internal/parse.h
deleted file mode 100644
index de706c8984..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/parse.h
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_INTERNAL_PARSE_H_
-#define ABSL_FLAGS_INTERNAL_PARSE_H_
-
-#include <string>
-#include <vector>
-
-#include "absl/base/config.h"
-#include "absl/flags/declare.h"
-#include "absl/strings/string_view.h"
-
-ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile);
-ABSL_DECLARE_FLAG(std::vector<std::string>, fromenv);
-ABSL_DECLARE_FLAG(std::vector<std::string>, tryfromenv);
-ABSL_DECLARE_FLAG(std::vector<std::string>, undefok);
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs };
-enum class UsageFlagsAction { kHandleUsage, kIgnoreUsage };
-enum class OnUndefinedFlag {
-  kIgnoreUndefined,
-  kReportUndefined,
-  kAbortIfUndefined
-};
-
-std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
-                                        ArgvListAction arg_list_act,
-                                        UsageFlagsAction usage_flag_act,
-                                        OnUndefinedFlag on_undef_flag);
-
-// --------------------------------------------------------------------
-// Inspect original command line
-
-// Returns true if flag with specified name was either present on the original
-// command line or specified in flag file present on the original command line.
-bool WasPresentOnCommandLine(absl::string_view flag_name);
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_INTERNAL_PARSE_H_
diff --git a/third_party/abseil_cpp/absl/flags/internal/path_util.h b/third_party/abseil_cpp/absl/flags/internal/path_util.h
deleted file mode 100644
index a6594d3347..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/path_util.h
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
-#define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
-
-#include "absl/base/config.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// A portable interface that returns the basename of the filename passed as an
-// argument. It is similar to basename(3)
-// <https://linux.die.net/man/3/basename>.
-// For example:
-//     flags_internal::Basename("a/b/prog/file.cc")
-// returns "file.cc"
-//     flags_internal::Basename("file.cc")
-// returns "file.cc"
-inline absl::string_view Basename(absl::string_view filename) {
-  auto last_slash_pos = filename.find_last_of("/\\");
-
-  return last_slash_pos == absl::string_view::npos
-             ? filename
-             : filename.substr(last_slash_pos + 1);
-}
-
-// A portable interface that returns the directory name of the filename
-// passed as an argument, including the trailing slash.
-// Returns the empty string if a slash is not found in the input file name.
-// For example:
-//      flags_internal::Package("a/b/prog/file.cc")
-// returns "a/b/prog/"
-//      flags_internal::Package("file.cc")
-// returns ""
-inline absl::string_view Package(absl::string_view filename) {
-  auto last_slash_pos = filename.find_last_of("/\\");
-
-  return last_slash_pos == absl::string_view::npos
-             ? absl::string_view()
-             : filename.substr(0, last_slash_pos + 1);
-}
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
diff --git a/third_party/abseil_cpp/absl/flags/internal/path_util_test.cc b/third_party/abseil_cpp/absl/flags/internal/path_util_test.cc
deleted file mode 100644
index 2091373c88..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/path_util_test.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/path_util.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-namespace flags = absl::flags_internal;
-
-TEST(FlagsPathUtilTest, TestBasename) {
-  EXPECT_EQ(flags::Basename(""), "");
-  EXPECT_EQ(flags::Basename("a.cc"), "a.cc");
-  EXPECT_EQ(flags::Basename("dir/a.cc"), "a.cc");
-  EXPECT_EQ(flags::Basename("dir1/dir2/a.cc"), "a.cc");
-  EXPECT_EQ(flags::Basename("../dir1/dir2/a.cc"), "a.cc");
-  EXPECT_EQ(flags::Basename("/dir1/dir2/a.cc"), "a.cc");
-  EXPECT_EQ(flags::Basename("/dir1/dir2/../dir3/a.cc"), "a.cc");
-}
-
-// --------------------------------------------------------------------
-
-TEST(FlagsPathUtilTest, TestPackage) {
-  EXPECT_EQ(flags::Package(""), "");
-  EXPECT_EQ(flags::Package("a.cc"), "");
-  EXPECT_EQ(flags::Package("dir/a.cc"), "dir/");
-  EXPECT_EQ(flags::Package("dir1/dir2/a.cc"), "dir1/dir2/");
-  EXPECT_EQ(flags::Package("../dir1/dir2/a.cc"), "../dir1/dir2/");
-  EXPECT_EQ(flags::Package("/dir1/dir2/a.cc"), "/dir1/dir2/");
-  EXPECT_EQ(flags::Package("/dir1/dir2/../dir3/a.cc"), "/dir1/dir2/../dir3/");
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/flags/internal/private_handle_accessor.cc b/third_party/abseil_cpp/absl/flags/internal/private_handle_accessor.cc
deleted file mode 100644
index a7eb58b6d4..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/private_handle_accessor.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/private_handle_accessor.h"
-
-#include <memory>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/flags/commandlineflag.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-FlagFastTypeId PrivateHandleAccessor::TypeId(const CommandLineFlag& flag) {
-  return flag.TypeId();
-}
-
-std::unique_ptr<FlagStateInterface> PrivateHandleAccessor::SaveState(
-    CommandLineFlag& flag) {
-  return flag.SaveState();
-}
-
-bool PrivateHandleAccessor::IsSpecifiedOnCommandLine(
-    const CommandLineFlag& flag) {
-  return flag.IsSpecifiedOnCommandLine();
-}
-
-bool PrivateHandleAccessor::ValidateInputValue(const CommandLineFlag& flag,
-                                               absl::string_view value) {
-  return flag.ValidateInputValue(value);
-}
-
-void PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip(
-    const CommandLineFlag& flag) {
-  flag.CheckDefaultValueParsingRoundtrip();
-}
-
-bool PrivateHandleAccessor::ParseFrom(CommandLineFlag& flag,
-                                      absl::string_view value,
-                                      flags_internal::FlagSettingMode set_mode,
-                                      flags_internal::ValueSource source,
-                                      std::string& error) {
-  return flag.ParseFrom(value, set_mode, source, error);
-}
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
diff --git a/third_party/abseil_cpp/absl/flags/internal/private_handle_accessor.h b/third_party/abseil_cpp/absl/flags/internal/private_handle_accessor.h
deleted file mode 100644
index c64435cd61..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/private_handle_accessor.h
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_
-#define ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_
-
-#include <memory>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/flags/commandlineflag.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// This class serves as a trampoline to access private methods of
-// CommandLineFlag. This class is intended for use exclusively internally inside
-// of the Abseil Flags implementation.
-class PrivateHandleAccessor {
- public:
-  // Access to CommandLineFlag::TypeId.
-  static FlagFastTypeId TypeId(const CommandLineFlag& flag);
-
-  // Access to CommandLineFlag::SaveState.
-  static std::unique_ptr<FlagStateInterface> SaveState(CommandLineFlag& flag);
-
-  // Access to CommandLineFlag::IsSpecifiedOnCommandLine.
-  static bool IsSpecifiedOnCommandLine(const CommandLineFlag& flag);
-
-  // Access to CommandLineFlag::ValidateInputValue.
-  static bool ValidateInputValue(const CommandLineFlag& flag,
-                                 absl::string_view value);
-
-  // Access to CommandLineFlag::CheckDefaultValueParsingRoundtrip.
-  static void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag);
-
-  static bool ParseFrom(CommandLineFlag& flag, absl::string_view value,
-                        flags_internal::FlagSettingMode set_mode,
-                        flags_internal::ValueSource source, std::string& error);
-};
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_
diff --git a/third_party/abseil_cpp/absl/flags/internal/program_name.cc b/third_party/abseil_cpp/absl/flags/internal/program_name.cc
deleted file mode 100644
index 51d698da8b..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/program_name.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/program_name.h"
-
-#include <string>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/const_init.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/flags/internal/path_util.h"
-#include "absl/strings/string_view.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-ABSL_CONST_INIT static absl::Mutex program_name_guard(absl::kConstInit);
-ABSL_CONST_INIT static std::string* program_name
-    ABSL_GUARDED_BY(program_name_guard) = nullptr;
-
-std::string ProgramInvocationName() {
-  absl::MutexLock l(&program_name_guard);
-
-  return program_name ? *program_name : "UNKNOWN";
-}
-
-std::string ShortProgramInvocationName() {
-  absl::MutexLock l(&program_name_guard);
-
-  return program_name ? std::string(flags_internal::Basename(*program_name))
-                      : "UNKNOWN";
-}
-
-void SetProgramInvocationName(absl::string_view prog_name_str) {
-  absl::MutexLock l(&program_name_guard);
-
-  if (!program_name)
-    program_name = new std::string(prog_name_str);
-  else
-    program_name->assign(prog_name_str.data(), prog_name_str.size());
-}
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/internal/program_name.h b/third_party/abseil_cpp/absl/flags/internal/program_name.h
deleted file mode 100644
index b99b94fe18..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/program_name.h
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_
-#define ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_
-
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/strings/string_view.h"
-
-// --------------------------------------------------------------------
-// Program name
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// Returns program invocation name or "UNKNOWN" if `SetProgramInvocationName()`
-// is never called. At the moment this is always set to argv[0] as part of
-// library initialization.
-std::string ProgramInvocationName();
-
-// Returns base name for program invocation name. For example, if
-//   ProgramInvocationName() == "a/b/mybinary"
-// then
-//   ShortProgramInvocationName() == "mybinary"
-std::string ShortProgramInvocationName();
-
-// Sets program invocation name to a new value. Should only be called once
-// during program initialization, before any threads are spawned.
-void SetProgramInvocationName(absl::string_view prog_name_str);
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_
diff --git a/third_party/abseil_cpp/absl/flags/internal/program_name_test.cc b/third_party/abseil_cpp/absl/flags/internal/program_name_test.cc
deleted file mode 100644
index aff9f6315e..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/program_name_test.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/program_name.h"
-
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/strings/match.h"
-#include "absl/strings/string_view.h"
-
-namespace {
-
-namespace flags = absl::flags_internal;
-
-TEST(FlagsPathUtilTest, TestProgamNameInterfaces) {
-  flags::SetProgramInvocationName("absl/flags/program_name_test");
-  std::string program_name = flags::ProgramInvocationName();
-  for (char& c : program_name)
-    if (c == '\\') c = '/';
-
-#if !defined(__wasm__) && !defined(__asmjs__)
-  const std::string expect_name = "absl/flags/program_name_test";
-  const std::string expect_basename = "program_name_test";
-#else
-  // For targets that generate javascript or webassembly the invocation name
-  // has the special value below.
-  const std::string expect_name = "this.program";
-  const std::string expect_basename = "this.program";
-#endif
-
-  EXPECT_TRUE(absl::EndsWith(program_name, expect_name)) << program_name;
-  EXPECT_EQ(flags::ShortProgramInvocationName(), expect_basename);
-
-  flags::SetProgramInvocationName("a/my_test");
-
-  EXPECT_EQ(flags::ProgramInvocationName(), "a/my_test");
-  EXPECT_EQ(flags::ShortProgramInvocationName(), "my_test");
-
-  absl::string_view not_null_terminated("absl/aaa/bbb");
-  not_null_terminated = not_null_terminated.substr(1, 10);
-
-  flags::SetProgramInvocationName(not_null_terminated);
-
-  EXPECT_EQ(flags::ProgramInvocationName(), "bsl/aaa/bb");
-  EXPECT_EQ(flags::ShortProgramInvocationName(), "bb");
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/flags/internal/registry.h b/third_party/abseil_cpp/absl/flags/internal/registry.h
deleted file mode 100644
index a8d9eb9cb0..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/registry.h
+++ /dev/null
@@ -1,97 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_INTERNAL_REGISTRY_H_
-#define ABSL_FLAGS_INTERNAL_REGISTRY_H_
-
-#include <functional>
-
-#include "absl/base/config.h"
-#include "absl/flags/commandlineflag.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/strings/string_view.h"
-
-// --------------------------------------------------------------------
-// Global flags registry API.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// Executes specified visitor for each non-retired flag in the registry. While
-// callback are executed, the registry is locked and can't be changed.
-void ForEachFlag(std::function<void(CommandLineFlag&)> visitor);
-
-//-----------------------------------------------------------------------------
-
-bool RegisterCommandLineFlag(CommandLineFlag&);
-
-void FinalizeRegistry();
-
-//-----------------------------------------------------------------------------
-// Retired registrations:
-//
-// Retired flag registrations are treated specially. A 'retired' flag is
-// provided only for compatibility with automated invocations that still
-// name it.  A 'retired' flag:
-//   - is not bound to a C++ FLAGS_ reference.
-//   - has a type and a value, but that value is intentionally inaccessible.
-//   - does not appear in --help messages.
-//   - is fully supported by _all_ flag parsing routines.
-//   - consumes args normally, and complains about type mismatches in its
-//     argument.
-//   - emits a complaint but does not die (e.g. LOG(ERROR)) if it is
-//     accessed by name through the flags API for parsing or otherwise.
-//
-// The registrations for a flag happen in an unspecified order as the
-// initializers for the namespace-scope objects of a program are run.
-// Any number of weak registrations for a flag can weakly define the flag.
-// One non-weak registration will upgrade the flag from weak to non-weak.
-// Further weak registrations of a non-weak flag are ignored.
-//
-// This mechanism is designed to support moving dead flags into a
-// 'graveyard' library.  An example migration:
-//
-//   0: Remove references to this FLAGS_flagname in the C++ codebase.
-//   1: Register as 'retired' in old_lib.
-//   2: Make old_lib depend on graveyard.
-//   3: Add a redundant 'retired' registration to graveyard.
-//   4: Remove the old_lib 'retired' registration.
-//   5: Eventually delete the graveyard registration entirely.
-//
-
-// Retire flag with name "name" and type indicated by ops.
-void Retire(const char* name, FlagFastTypeId type_id, char* buf);
-
-constexpr size_t kRetiredFlagObjSize = 3 * sizeof(void*);
-constexpr size_t kRetiredFlagObjAlignment = alignof(void*);
-
-// Registered a retired flag with name 'flag_name' and type 'T'.
-template <typename T>
-class RetiredFlag {
- public:
-  void Retire(const char* flag_name) {
-    flags_internal::Retire(flag_name, base_internal::FastTypeId<T>(), buf_);
-  }
-
- private:
-  alignas(kRetiredFlagObjAlignment) char buf_[kRetiredFlagObjSize];
-};
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_INTERNAL_REGISTRY_H_
diff --git a/third_party/abseil_cpp/absl/flags/internal/usage.cc b/third_party/abseil_cpp/absl/flags/internal/usage.cc
deleted file mode 100644
index f29d7c9b48..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/usage.cc
+++ /dev/null
@@ -1,524 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/usage.h"
-
-#include <stdint.h>
-
-#include <functional>
-#include <map>
-#include <ostream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "absl/base/config.h"
-#include "absl/flags/commandlineflag.h"
-#include "absl/flags/flag.h"
-#include "absl/flags/internal/flag.h"
-#include "absl/flags/internal/path_util.h"
-#include "absl/flags/internal/private_handle_accessor.h"
-#include "absl/flags/internal/program_name.h"
-#include "absl/flags/internal/registry.h"
-#include "absl/flags/usage_config.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_split.h"
-#include "absl/strings/string_view.h"
-
-// Dummy global variables to prevent anyone else defining these.
-bool FLAGS_help = false;
-bool FLAGS_helpfull = false;
-bool FLAGS_helpshort = false;
-bool FLAGS_helppackage = false;
-bool FLAGS_version = false;
-bool FLAGS_only_check_args = false;
-bool FLAGS_helpon = false;
-bool FLAGS_helpmatch = false;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-namespace {
-
-using PerFlagFilter = std::function<bool(const absl::CommandLineFlag&)>;
-
-// Maximum length size in a human readable format.
-constexpr size_t kHrfMaxLineLength = 80;
-
-// This class is used to emit an XML element with `tag` and `text`.
-// It adds opening and closing tags and escapes special characters in the text.
-// For example:
-// std::cout << XMLElement("title", "Milk & Cookies");
-// prints "<title>Milk &amp; Cookies</title>"
-class XMLElement {
- public:
-  XMLElement(absl::string_view tag, absl::string_view txt)
-      : tag_(tag), txt_(txt) {}
-
-  friend std::ostream& operator<<(std::ostream& out,
-                                  const XMLElement& xml_elem) {
-    out << "<" << xml_elem.tag_ << ">";
-
-    for (auto c : xml_elem.txt_) {
-      switch (c) {
-        case '"':
-          out << "&quot;";
-          break;
-        case '\'':
-          out << "&apos;";
-          break;
-        case '&':
-          out << "&amp;";
-          break;
-        case '<':
-          out << "&lt;";
-          break;
-        case '>':
-          out << "&gt;";
-          break;
-        default:
-          out << c;
-          break;
-      }
-    }
-
-    return out << "</" << xml_elem.tag_ << ">";
-  }
-
- private:
-  absl::string_view tag_;
-  absl::string_view txt_;
-};
-
-// --------------------------------------------------------------------
-// Helper class to pretty-print info about a flag.
-
-class FlagHelpPrettyPrinter {
- public:
-  // Pretty printer holds on to the std::ostream& reference to direct an output
-  // to that stream.
-  FlagHelpPrettyPrinter(size_t max_line_len, size_t min_line_len,
-                        size_t wrapped_line_indent, std::ostream& out)
-      : out_(out),
-        max_line_len_(max_line_len),
-        min_line_len_(min_line_len),
-        wrapped_line_indent_(wrapped_line_indent),
-        line_len_(0),
-        first_line_(true) {}
-
-  void Write(absl::string_view str, bool wrap_line = false) {
-    // Empty string - do nothing.
-    if (str.empty()) return;
-
-    std::vector<absl::string_view> tokens;
-    if (wrap_line) {
-      for (auto line : absl::StrSplit(str, absl::ByAnyChar("\n\r"))) {
-        if (!tokens.empty()) {
-          // Keep line separators in the input string.
-          tokens.push_back("\n");
-        }
-        for (auto token :
-             absl::StrSplit(line, absl::ByAnyChar(" \t"), absl::SkipEmpty())) {
-          tokens.push_back(token);
-        }
-      }
-    } else {
-      tokens.push_back(str);
-    }
-
-    for (auto token : tokens) {
-      bool new_line = (line_len_ == 0);
-
-      // Respect line separators in the input string.
-      if (token == "\n") {
-        EndLine();
-        continue;
-      }
-
-      // Write the token, ending the string first if necessary/possible.
-      if (!new_line &&
-          (line_len_ + static_cast<int>(token.size()) >= max_line_len_)) {
-        EndLine();
-        new_line = true;
-      }
-
-      if (new_line) {
-        StartLine();
-      } else {
-        out_ << ' ';
-        ++line_len_;
-      }
-
-      out_ << token;
-      line_len_ += token.size();
-    }
-  }
-
-  void StartLine() {
-    if (first_line_) {
-      line_len_ = min_line_len_;
-      first_line_ = false;
-    } else {
-      line_len_ = min_line_len_ + wrapped_line_indent_;
-    }
-    out_ << std::string(line_len_, ' ');
-  }
-  void EndLine() {
-    out_ << '\n';
-    line_len_ = 0;
-  }
-
- private:
-  std::ostream& out_;
-  const size_t max_line_len_;
-  const size_t min_line_len_;
-  const size_t wrapped_line_indent_;
-  size_t line_len_;
-  bool first_line_;
-};
-
-void FlagHelpHumanReadable(const CommandLineFlag& flag, std::ostream& out) {
-  FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 4, 2, out);
-
-  // Flag name.
-  printer.Write(absl::StrCat("--", flag.Name()));
-
-  // Flag help.
-  printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true);
-
-  // The listed default value will be the actual default from the flag
-  // definition in the originating source file, unless the value has
-  // subsequently been modified using SetCommandLineOption() with mode
-  // SET_FLAGS_DEFAULT.
-  std::string dflt_val = flag.DefaultValue();
-  std::string curr_val = flag.CurrentValue();
-  bool is_modified = curr_val != dflt_val;
-
-  if (flag.IsOfType<std::string>()) {
-    dflt_val = absl::StrCat("\"", dflt_val, "\"");
-  }
-  printer.Write(absl::StrCat("default: ", dflt_val, ";"));
-
-  if (is_modified) {
-    if (flag.IsOfType<std::string>()) {
-      curr_val = absl::StrCat("\"", curr_val, "\"");
-    }
-    printer.Write(absl::StrCat("currently: ", curr_val, ";"));
-  }
-
-  printer.EndLine();
-}
-
-// Shows help for every filename which matches any of the filters
-// If filters are empty, shows help for every file.
-// If a flag's help message has been stripped (e.g. by adding '#define
-// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
-// and its variants.
-void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb,
-                   HelpFormat format, absl::string_view program_usage_message) {
-  if (format == HelpFormat::kHumanReadable) {
-    out << flags_internal::ShortProgramInvocationName() << ": "
-        << program_usage_message << "\n\n";
-  } else {
-    // XML schema is not a part of our public API for now.
-    out << "<?xml version=\"1.0\"?>\n"
-        << "<!-- This output should be used with care. We do not report type "
-           "names for flags with user defined types -->\n"
-        << "<!-- Prefer flag only_check_args for validating flag inputs -->\n"
-        // The document.
-        << "<AllFlags>\n"
-        // The program name and usage.
-        << XMLElement("program", flags_internal::ShortProgramInvocationName())
-        << '\n'
-        << XMLElement("usage", program_usage_message) << '\n';
-  }
-
-  // Map of package name to
-  //   map of file name to
-  //     vector of flags in the file.
-  // This map is used to output matching flags grouped by package and file
-  // name.
-  std::map<std::string,
-           std::map<std::string, std::vector<const absl::CommandLineFlag*>>>
-      matching_flags;
-
-  flags_internal::ForEachFlag([&](absl::CommandLineFlag& flag) {
-    // Ignore retired flags.
-    if (flag.IsRetired()) return;
-
-    // If the flag has been stripped, pretend that it doesn't exist.
-    if (flag.Help() == flags_internal::kStrippedFlagHelp) return;
-
-    // Make sure flag satisfies the filter
-    if (!filter_cb(flag)) return;
-
-    std::string flag_filename = flag.Filename();
-
-    matching_flags[std::string(flags_internal::Package(flag_filename))]
-                  [flag_filename]
-                      .push_back(&flag);
-  });
-
-  absl::string_view package_separator;  // controls blank lines between packages
-  absl::string_view file_separator;     // controls blank lines between files
-  for (const auto& package : matching_flags) {
-    if (format == HelpFormat::kHumanReadable) {
-      out << package_separator;
-      package_separator = "\n\n";
-    }
-
-    file_separator = "";
-    for (const auto& flags_in_file : package.second) {
-      if (format == HelpFormat::kHumanReadable) {
-        out << file_separator << "  Flags from " << flags_in_file.first
-            << ":\n";
-        file_separator = "\n";
-      }
-
-      for (const auto* flag : flags_in_file.second) {
-        flags_internal::FlagHelp(out, *flag, format);
-      }
-    }
-  }
-
-  if (format == HelpFormat::kHumanReadable) {
-    FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 0, 0, out);
-
-    if (filter_cb && matching_flags.empty()) {
-      printer.Write("No flags matched.\n", true);
-    }
-    printer.EndLine();
-    printer.Write(
-        "Try --helpfull to get a list of all flags or --help=substring "
-        "shows help for flags which include specified substring in either "
-        "in the name, or description or path.\n",
-        true);
-  } else {
-    // The end of the document.
-    out << "</AllFlags>\n";
-  }
-}
-
-void FlagsHelpImpl(std::ostream& out,
-                   flags_internal::FlagKindFilter filename_filter_cb,
-                   HelpFormat format, absl::string_view program_usage_message) {
-  FlagsHelpImpl(
-      out,
-      [&](const absl::CommandLineFlag& flag) {
-        return filename_filter_cb && filename_filter_cb(flag.Filename());
-      },
-      format, program_usage_message);
-}
-
-}  // namespace
-
-// --------------------------------------------------------------------
-// Produces the help message describing specific flag.
-void FlagHelp(std::ostream& out, const CommandLineFlag& flag,
-              HelpFormat format) {
-  if (format == HelpFormat::kHumanReadable)
-    flags_internal::FlagHelpHumanReadable(flag, out);
-}
-
-// --------------------------------------------------------------------
-// Produces the help messages for all flags matching the filename filter.
-// If filter is empty produces help messages for all flags.
-void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,
-               absl::string_view program_usage_message) {
-  flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
-    return filter.empty() || filename.find(filter) != absl::string_view::npos;
-  };
-  flags_internal::FlagsHelpImpl(out, filter_cb, format, program_usage_message);
-}
-
-// --------------------------------------------------------------------
-// Checks all the 'usage' command line flags to see if any have been set.
-// If so, handles them appropriately.
-int HandleUsageFlags(std::ostream& out,
-                     absl::string_view program_usage_message) {
-  switch (GetFlagsHelpMode()) {
-    case HelpMode::kNone:
-      break;
-    case HelpMode::kImportant:
-      flags_internal::FlagsHelpImpl(
-          out, flags_internal::GetUsageConfig().contains_help_flags,
-          GetFlagsHelpFormat(), program_usage_message);
-      return 1;
-
-    case HelpMode::kShort:
-      flags_internal::FlagsHelpImpl(
-          out, flags_internal::GetUsageConfig().contains_helpshort_flags,
-          GetFlagsHelpFormat(), program_usage_message);
-      return 1;
-
-    case HelpMode::kFull:
-      flags_internal::FlagsHelp(out, "", GetFlagsHelpFormat(),
-                                program_usage_message);
-      return 1;
-
-    case HelpMode::kPackage:
-      flags_internal::FlagsHelpImpl(
-          out, flags_internal::GetUsageConfig().contains_helppackage_flags,
-          GetFlagsHelpFormat(), program_usage_message);
-
-      return 1;
-
-    case HelpMode::kMatch: {
-      std::string substr = GetFlagsHelpMatchSubstr();
-      if (substr.empty()) {
-        // show all options
-        flags_internal::FlagsHelp(out, substr, GetFlagsHelpFormat(),
-                                  program_usage_message);
-      } else {
-        auto filter_cb = [&substr](const absl::CommandLineFlag& flag) {
-          if (absl::StrContains(flag.Name(), substr)) return true;
-          if (absl::StrContains(flag.Filename(), substr)) return true;
-          if (absl::StrContains(flag.Help(), substr)) return true;
-
-          return false;
-        };
-        flags_internal::FlagsHelpImpl(
-            out, filter_cb, HelpFormat::kHumanReadable, program_usage_message);
-      }
-
-      return 1;
-    }
-    case HelpMode::kVersion:
-      if (flags_internal::GetUsageConfig().version_string)
-        out << flags_internal::GetUsageConfig().version_string();
-      // Unlike help, we may be asking for version in a script, so return 0
-      return 0;
-
-    case HelpMode::kOnlyCheckArgs:
-      return 0;
-  }
-
-  return -1;
-}
-
-// --------------------------------------------------------------------
-// Globals representing usage reporting flags
-
-namespace {
-
-ABSL_CONST_INIT absl::Mutex help_attributes_guard(absl::kConstInit);
-ABSL_CONST_INIT std::string* match_substr
-    ABSL_GUARDED_BY(help_attributes_guard) = nullptr;
-ABSL_CONST_INIT HelpMode help_mode ABSL_GUARDED_BY(help_attributes_guard) =
-    HelpMode::kNone;
-ABSL_CONST_INIT HelpFormat help_format ABSL_GUARDED_BY(help_attributes_guard) =
-    HelpFormat::kHumanReadable;
-
-}  // namespace
-
-std::string GetFlagsHelpMatchSubstr() {
-  absl::MutexLock l(&help_attributes_guard);
-  if (match_substr == nullptr) return "";
-  return *match_substr;
-}
-
-void SetFlagsHelpMatchSubstr(absl::string_view substr) {
-  absl::MutexLock l(&help_attributes_guard);
-  if (match_substr == nullptr) match_substr = new std::string;
-  match_substr->assign(substr.data(), substr.size());
-}
-
-HelpMode GetFlagsHelpMode() {
-  absl::MutexLock l(&help_attributes_guard);
-  // Refer to dummy variales to prevent linker dropping them
-  if (FLAGS_help || FLAGS_helpfull || FLAGS_helpshort || FLAGS_helppackage ||
-      FLAGS_version || FLAGS_only_check_args || FLAGS_helpon ||
-      FLAGS_helpmatch) {
-    help_mode = HelpMode::kNone;
-  }
-  return help_mode;
-}
-
-void SetFlagsHelpMode(HelpMode mode) {
-  absl::MutexLock l(&help_attributes_guard);
-  help_mode = mode;
-}
-
-HelpFormat GetFlagsHelpFormat() {
-  absl::MutexLock l(&help_attributes_guard);
-  return help_format;
-}
-
-void SetFlagsHelpFormat(HelpFormat format) {
-  absl::MutexLock l(&help_attributes_guard);
-  help_format = format;
-}
-
-// Deduces usage flags from the input argument in a form --name=value or
-// --name. argument is already split into name and value before we call this
-// function.
-bool DeduceUsageFlags(absl::string_view name, absl::string_view value) {
-  if (absl::ConsumePrefix(&name, "help")) {
-    if (name == "") {
-      if (value.empty()) {
-        SetFlagsHelpMode(HelpMode::kImportant);
-      } else {
-        SetFlagsHelpMode(HelpMode::kMatch);
-        SetFlagsHelpMatchSubstr(value);
-      }
-      return true;
-    }
-
-    if (name == "match") {
-      SetFlagsHelpMode(HelpMode::kMatch);
-      SetFlagsHelpMatchSubstr(value);
-      return true;
-    }
-
-    if (name == "on") {
-      SetFlagsHelpMode(HelpMode::kMatch);
-      SetFlagsHelpMatchSubstr(absl::StrCat("/", value, "."));
-      return true;
-    }
-
-    if (name == "full") {
-      SetFlagsHelpMode(HelpMode::kFull);
-      return true;
-    }
-
-    if (name == "short") {
-      SetFlagsHelpMode(HelpMode::kShort);
-      return true;
-    }
-
-    if (name == "package") {
-      SetFlagsHelpMode(HelpMode::kPackage);
-      return true;
-    }
-
-    return false;
-  }
-
-  if (name == "version") {
-    SetFlagsHelpMode(HelpMode::kVersion);
-    return true;
-  }
-
-  if (name == "only_check_args") {
-    SetFlagsHelpMode(HelpMode::kOnlyCheckArgs);
-    return true;
-  }
-
-  return false;
-}
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/internal/usage.h b/third_party/abseil_cpp/absl/flags/internal/usage.h
deleted file mode 100644
index c0bcac5762..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/usage.h
+++ /dev/null
@@ -1,104 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_INTERNAL_USAGE_H_
-#define ABSL_FLAGS_INTERNAL_USAGE_H_
-
-#include <iosfwd>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/flags/commandlineflag.h"
-#include "absl/flags/declare.h"
-#include "absl/strings/string_view.h"
-
-// --------------------------------------------------------------------
-// Usage reporting interfaces
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// The format to report the help messages in.
-enum class HelpFormat {
-  kHumanReadable,
-};
-
-// Streams the help message describing `flag` to `out`.
-// The default value for `flag` is included in the output.
-void FlagHelp(std::ostream& out, const CommandLineFlag& flag,
-              HelpFormat format = HelpFormat::kHumanReadable);
-
-// Produces the help messages for all flags matching the filter. A flag matches
-// the filter if it is defined in a file with a filename which includes
-// filter string as a substring. You can use '/' and '.' to restrict the
-// matching to a specific file names. For example:
-//   FlagsHelp(out, "/path/to/file.");
-// restricts help to only flags which resides in files named like:
-//  .../path/to/file.<ext>
-// for any extension 'ext'. If the filter is empty this function produces help
-// messages for all flags.
-void FlagsHelp(std::ostream& out, absl::string_view filter,
-               HelpFormat format, absl::string_view program_usage_message);
-
-// --------------------------------------------------------------------
-
-// If any of the 'usage' related command line flags (listed on the bottom of
-// this file) has been set this routine produces corresponding help message in
-// the specified output stream and returns:
-//  0 - if "version" or "only_check_flags" flags were set and handled.
-//  1 - if some other 'usage' related flag was set and handled.
-// -1 - if no usage flags were set on a commmand line.
-// Non negative return values are expected to be used as an exit code for a
-// binary.
-int HandleUsageFlags(std::ostream& out,
-                     absl::string_view program_usage_message);
-
-// --------------------------------------------------------------------
-// Globals representing usage reporting flags
-
-enum class HelpMode {
-  kNone,
-  kImportant,
-  kShort,
-  kFull,
-  kPackage,
-  kMatch,
-  kVersion,
-  kOnlyCheckArgs
-};
-
-// Returns substring to filter help output (--help=substr argument)
-std::string GetFlagsHelpMatchSubstr();
-// Returns the requested help mode.
-HelpMode GetFlagsHelpMode();
-// Returns the requested help format.
-HelpFormat GetFlagsHelpFormat();
-
-// These are corresponding setters to the attributes above.
-void SetFlagsHelpMatchSubstr(absl::string_view);
-void SetFlagsHelpMode(HelpMode);
-void SetFlagsHelpFormat(HelpFormat);
-
-// Deduces usage flags from the input argument in a form --name=value or
-// --name. argument is already split into name and value before we call this
-// function.
-bool DeduceUsageFlags(absl::string_view name, absl::string_view value);
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_INTERNAL_USAGE_H_
diff --git a/third_party/abseil_cpp/absl/flags/internal/usage_test.cc b/third_party/abseil_cpp/absl/flags/internal/usage_test.cc
deleted file mode 100644
index b5c2487da5..0000000000
--- a/third_party/abseil_cpp/absl/flags/internal/usage_test.cc
+++ /dev/null
@@ -1,493 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/usage.h"
-
-#include <stdint.h>
-
-#include <sstream>
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/flags/flag.h"
-#include "absl/flags/internal/parse.h"
-#include "absl/flags/internal/path_util.h"
-#include "absl/flags/internal/program_name.h"
-#include "absl/flags/reflection.h"
-#include "absl/flags/usage.h"
-#include "absl/flags/usage_config.h"
-#include "absl/strings/match.h"
-#include "absl/strings/string_view.h"
-
-ABSL_FLAG(int, usage_reporting_test_flag_01, 101,
-          "usage_reporting_test_flag_01 help message");
-ABSL_FLAG(bool, usage_reporting_test_flag_02, false,
-          "usage_reporting_test_flag_02 help message");
-ABSL_FLAG(double, usage_reporting_test_flag_03, 1.03,
-          "usage_reporting_test_flag_03 help message");
-ABSL_FLAG(int64_t, usage_reporting_test_flag_04, 1000000000000004L,
-          "usage_reporting_test_flag_04 help message");
-
-static const char kTestUsageMessage[] = "Custom usage message";
-
-struct UDT {
-  UDT() = default;
-  UDT(const UDT&) = default;
-};
-bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
-std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; }
-
-ABSL_FLAG(UDT, usage_reporting_test_flag_05, {},
-          "usage_reporting_test_flag_05 help message");
-
-ABSL_FLAG(
-    std::string, usage_reporting_test_flag_06, {},
-    "usage_reporting_test_flag_06 help message.\n"
-    "\n"
-    "Some more help.\n"
-    "Even more long long long long long long long long long long long long "
-    "help message.");
-
-namespace {
-
-namespace flags = absl::flags_internal;
-
-static std::string NormalizeFileName(absl::string_view fname) {
-#ifdef _WIN32
-  std::string normalized(fname);
-  std::replace(normalized.begin(), normalized.end(), '\\', '/');
-  fname = normalized;
-#endif
-
-  auto absl_pos = fname.rfind("absl/");
-  if (absl_pos != absl::string_view::npos) {
-    fname = fname.substr(absl_pos);
-  }
-  return std::string(fname);
-}
-
-class UsageReportingTest : public testing::Test {
- protected:
-  UsageReportingTest() {
-    // Install default config for the use on this unit test.
-    // Binary may install a custom config before tests are run.
-    absl::FlagsUsageConfig default_config;
-    default_config.normalize_filename = &NormalizeFileName;
-    absl::SetFlagsUsageConfig(default_config);
-  }
-  ~UsageReportingTest() override {
-    flags::SetFlagsHelpMode(flags::HelpMode::kNone);
-    flags::SetFlagsHelpMatchSubstr("");
-    flags::SetFlagsHelpFormat(flags::HelpFormat::kHumanReadable);
-  }
-
- private:
-  absl::FlagSaver flag_saver_;
-};
-
-// --------------------------------------------------------------------
-
-using UsageReportingDeathTest = UsageReportingTest;
-
-TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) {
-  EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage);
-
-#ifndef _WIN32
-  // TODO(rogeeff): figure out why this does not work on Windows.
-  EXPECT_DEATH_IF_SUPPORTED(
-      absl::SetProgramUsageMessage("custom usage message"),
-      ".*SetProgramUsageMessage\\(\\) called twice.*");
-#endif
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) {
-  const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_01");
-  std::stringstream test_buf;
-
-  flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
-  EXPECT_EQ(
-      test_buf.str(),
-      R"(    --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
-      default: 101;
-)");
-}
-
-TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) {
-  const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_02");
-  std::stringstream test_buf;
-
-  flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
-  EXPECT_EQ(
-      test_buf.str(),
-      R"(    --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
-      default: false;
-)");
-}
-
-TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) {
-  const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_03");
-  std::stringstream test_buf;
-
-  flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
-  EXPECT_EQ(
-      test_buf.str(),
-      R"(    --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
-      default: 1.03;
-)");
-}
-
-TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) {
-  const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_04");
-  std::stringstream test_buf;
-
-  flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
-  EXPECT_EQ(
-      test_buf.str(),
-      R"(    --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
-      default: 1000000000000004;
-)");
-}
-
-TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) {
-  const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_05");
-  std::stringstream test_buf;
-
-  flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
-  EXPECT_EQ(
-      test_buf.str(),
-      R"(    --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
-      default: UDT{};
-)");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
-  std::string usage_test_flags_out =
-      R"(usage_test: Custom usage message
-
-  Flags from absl/flags/internal/usage_test.cc:
-    --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
-      default: 101;
-    --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
-      default: false;
-    --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
-      default: 1.03;
-    --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
-      default: 1000000000000004;
-    --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
-      default: UDT{};
-    --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
-
-      Some more help.
-      Even more long long long long long long long long long long long long help
-      message.); default: "";
-
-Try --helpfull to get a list of all flags or --help=substring shows help for
-flags which include specified substring in either in the name, or description or
-path.
-)";
-
-  std::stringstream test_buf_01;
-  flags::FlagsHelp(test_buf_01, "usage_test.cc",
-                   flags::HelpFormat::kHumanReadable, kTestUsageMessage);
-  EXPECT_EQ(test_buf_01.str(), usage_test_flags_out);
-
-  std::stringstream test_buf_02;
-  flags::FlagsHelp(test_buf_02, "flags/internal/usage_test.cc",
-                   flags::HelpFormat::kHumanReadable, kTestUsageMessage);
-  EXPECT_EQ(test_buf_02.str(), usage_test_flags_out);
-
-  std::stringstream test_buf_03;
-  flags::FlagsHelp(test_buf_03, "usage_test", flags::HelpFormat::kHumanReadable,
-                   kTestUsageMessage);
-  EXPECT_EQ(test_buf_03.str(), usage_test_flags_out);
-
-  std::stringstream test_buf_04;
-  flags::FlagsHelp(test_buf_04, "flags/invalid_file_name.cc",
-                   flags::HelpFormat::kHumanReadable, kTestUsageMessage);
-  EXPECT_EQ(test_buf_04.str(),
-            R"(usage_test: Custom usage message
-
-No flags matched.
-
-Try --helpfull to get a list of all flags or --help=substring shows help for
-flags which include specified substring in either in the name, or description or
-path.
-)");
-
-  std::stringstream test_buf_05;
-  flags::FlagsHelp(test_buf_05, "", flags::HelpFormat::kHumanReadable,
-                   kTestUsageMessage);
-  std::string test_out = test_buf_05.str();
-  absl::string_view test_out_str(test_out);
-  EXPECT_TRUE(
-      absl::StartsWith(test_out_str, "usage_test: Custom usage message"));
-  EXPECT_TRUE(absl::StrContains(
-      test_out_str, "Flags from absl/flags/internal/usage_test.cc:"));
-  EXPECT_TRUE(
-      absl::StrContains(test_out_str, "-usage_reporting_test_flag_01 "));
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestNoUsageFlags) {
-  std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), -1);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
-  flags::SetFlagsHelpMode(flags::HelpMode::kShort);
-
-  std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
-  EXPECT_EQ(test_buf.str(),
-            R"(usage_test: Custom usage message
-
-  Flags from absl/flags/internal/usage_test.cc:
-    --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
-      default: 101;
-    --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
-      default: false;
-    --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
-      default: 1.03;
-    --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
-      default: 1000000000000004;
-    --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
-      default: UDT{};
-    --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
-
-      Some more help.
-      Even more long long long long long long long long long long long long help
-      message.); default: "";
-
-Try --helpfull to get a list of all flags or --help=substring shows help for
-flags which include specified substring in either in the name, or description or
-path.
-)");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestUsageFlag_help_simple) {
-  flags::SetFlagsHelpMode(flags::HelpMode::kImportant);
-
-  std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
-  EXPECT_EQ(test_buf.str(),
-            R"(usage_test: Custom usage message
-
-  Flags from absl/flags/internal/usage_test.cc:
-    --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
-      default: 101;
-    --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
-      default: false;
-    --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
-      default: 1.03;
-    --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
-      default: 1000000000000004;
-    --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
-      default: UDT{};
-    --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
-
-      Some more help.
-      Even more long long long long long long long long long long long long help
-      message.); default: "";
-
-Try --helpfull to get a list of all flags or --help=substring shows help for
-flags which include specified substring in either in the name, or description or
-path.
-)");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestUsageFlag_help_one_flag) {
-  flags::SetFlagsHelpMode(flags::HelpMode::kMatch);
-  flags::SetFlagsHelpMatchSubstr("usage_reporting_test_flag_06");
-
-  std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
-  EXPECT_EQ(test_buf.str(),
-            R"(usage_test: Custom usage message
-
-  Flags from absl/flags/internal/usage_test.cc:
-    --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
-
-      Some more help.
-      Even more long long long long long long long long long long long long help
-      message.); default: "";
-
-Try --helpfull to get a list of all flags or --help=substring shows help for
-flags which include specified substring in either in the name, or description or
-path.
-)");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestUsageFlag_help_multiple_flag) {
-  flags::SetFlagsHelpMode(flags::HelpMode::kMatch);
-  flags::SetFlagsHelpMatchSubstr("test_flag");
-
-  std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
-  EXPECT_EQ(test_buf.str(),
-            R"(usage_test: Custom usage message
-
-  Flags from absl/flags/internal/usage_test.cc:
-    --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
-      default: 101;
-    --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
-      default: false;
-    --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
-      default: 1.03;
-    --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
-      default: 1000000000000004;
-    --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
-      default: UDT{};
-    --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
-
-      Some more help.
-      Even more long long long long long long long long long long long long help
-      message.); default: "";
-
-Try --helpfull to get a list of all flags or --help=substring shows help for
-flags which include specified substring in either in the name, or description or
-path.
-)");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
-  flags::SetFlagsHelpMode(flags::HelpMode::kPackage);
-
-  std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
-  EXPECT_EQ(test_buf.str(),
-            R"(usage_test: Custom usage message
-
-  Flags from absl/flags/internal/usage_test.cc:
-    --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
-      default: 101;
-    --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
-      default: false;
-    --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
-      default: 1.03;
-    --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
-      default: 1000000000000004;
-    --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
-      default: UDT{};
-    --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
-
-      Some more help.
-      Even more long long long long long long long long long long long long help
-      message.); default: "";
-
-Try --helpfull to get a list of all flags or --help=substring shows help for
-flags which include specified substring in either in the name, or description or
-path.
-)");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestUsageFlag_version) {
-  flags::SetFlagsHelpMode(flags::HelpMode::kVersion);
-
-  std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
-#ifndef NDEBUG
-  EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n");
-#else
-  EXPECT_EQ(test_buf.str(), "usage_test\n");
-#endif
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) {
-  flags::SetFlagsHelpMode(flags::HelpMode::kOnlyCheckArgs);
-
-  std::stringstream test_buf;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
-  EXPECT_EQ(test_buf.str(), "");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
-  flags::SetFlagsHelpMode(flags::HelpMode::kMatch);
-  flags::SetFlagsHelpMatchSubstr("/bla-bla.");
-
-  std::stringstream test_buf_01;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1);
-  EXPECT_EQ(test_buf_01.str(),
-            R"(usage_test: Custom usage message
-
-No flags matched.
-
-Try --helpfull to get a list of all flags or --help=substring shows help for
-flags which include specified substring in either in the name, or description or
-path.
-)");
-
-  flags::SetFlagsHelpMatchSubstr("/usage_test.");
-
-  std::stringstream test_buf_02;
-  EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1);
-  EXPECT_EQ(test_buf_02.str(),
-            R"(usage_test: Custom usage message
-
-  Flags from absl/flags/internal/usage_test.cc:
-    --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
-      default: 101;
-    --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
-      default: false;
-    --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
-      default: 1.03;
-    --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
-      default: 1000000000000004;
-    --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
-      default: UDT{};
-    --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
-
-      Some more help.
-      Even more long long long long long long long long long long long long help
-      message.); default: "";
-
-Try --helpfull to get a list of all flags or --help=substring shows help for
-flags which include specified substring in either in the name, or description or
-path.
-)");
-}
-
-// --------------------------------------------------------------------
-
-}  // namespace
-
-int main(int argc, char* argv[]) {
-  (void)absl::GetFlag(FLAGS_undefok);  // Force linking of parse.cc
-  flags::SetProgramInvocationName("usage_test");
-  absl::SetProgramUsageMessage(kTestUsageMessage);
-  ::testing::InitGoogleTest(&argc, argv);
-
-  return RUN_ALL_TESTS();
-}
diff --git a/third_party/abseil_cpp/absl/flags/marshalling.cc b/third_party/abseil_cpp/absl/flags/marshalling.cc
deleted file mode 100644
index 81f9cebd6f..0000000000
--- a/third_party/abseil_cpp/absl/flags/marshalling.cc
+++ /dev/null
@@ -1,241 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/marshalling.h"
-
-#include <stddef.h>
-
-#include <cmath>
-#include <limits>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include "absl/base/config.h"
-#include "absl/base/log_severity.h"
-#include "absl/base/macros.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/match.h"
-#include "absl/strings/numbers.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_join.h"
-#include "absl/strings/str_split.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// --------------------------------------------------------------------
-// AbslParseFlag specializations for boolean type.
-
-bool AbslParseFlag(absl::string_view text, bool* dst, std::string*) {
-  const char* kTrue[] = {"1", "t", "true", "y", "yes"};
-  const char* kFalse[] = {"0", "f", "false", "n", "no"};
-  static_assert(sizeof(kTrue) == sizeof(kFalse), "true_false_equal");
-
-  text = absl::StripAsciiWhitespace(text);
-
-  for (size_t i = 0; i < ABSL_ARRAYSIZE(kTrue); ++i) {
-    if (absl::EqualsIgnoreCase(text, kTrue[i])) {
-      *dst = true;
-      return true;
-    } else if (absl::EqualsIgnoreCase(text, kFalse[i])) {
-      *dst = false;
-      return true;
-    }
-  }
-  return false;  // didn't match a legal input
-}
-
-// --------------------------------------------------------------------
-// AbslParseFlag for integral types.
-
-// Return the base to use for parsing text as an integer.  Leading 0x
-// puts us in base 16.  But leading 0 does not put us in base 8. It
-// caused too many bugs when we had that behavior.
-static int NumericBase(absl::string_view text) {
-  const bool hex = (text.size() >= 2 && text[0] == '0' &&
-                    (text[1] == 'x' || text[1] == 'X'));
-  return hex ? 16 : 10;
-}
-
-template <typename IntType>
-inline bool ParseFlagImpl(absl::string_view text, IntType& dst) {
-  text = absl::StripAsciiWhitespace(text);
-
-  return absl::numbers_internal::safe_strtoi_base(text, &dst,
-                                                  NumericBase(text));
-}
-
-bool AbslParseFlag(absl::string_view text, short* dst, std::string*) {
-  int val;
-  if (!ParseFlagImpl(text, val)) return false;
-  if (static_cast<short>(val) != val)  // worked, but number out of range
-    return false;
-  *dst = static_cast<short>(val);
-  return true;
-}
-
-bool AbslParseFlag(absl::string_view text, unsigned short* dst, std::string*) {
-  unsigned int val;
-  if (!ParseFlagImpl(text, val)) return false;
-  if (static_cast<unsigned short>(val) !=
-      val)  // worked, but number out of range
-    return false;
-  *dst = static_cast<unsigned short>(val);
-  return true;
-}
-
-bool AbslParseFlag(absl::string_view text, int* dst, std::string*) {
-  return ParseFlagImpl(text, *dst);
-}
-
-bool AbslParseFlag(absl::string_view text, unsigned int* dst, std::string*) {
-  return ParseFlagImpl(text, *dst);
-}
-
-bool AbslParseFlag(absl::string_view text, long* dst, std::string*) {
-  return ParseFlagImpl(text, *dst);
-}
-
-bool AbslParseFlag(absl::string_view text, unsigned long* dst, std::string*) {
-  return ParseFlagImpl(text, *dst);
-}
-
-bool AbslParseFlag(absl::string_view text, long long* dst, std::string*) {
-  return ParseFlagImpl(text, *dst);
-}
-
-bool AbslParseFlag(absl::string_view text, unsigned long long* dst,
-                   std::string*) {
-  return ParseFlagImpl(text, *dst);
-}
-
-// --------------------------------------------------------------------
-// AbslParseFlag for floating point types.
-
-bool AbslParseFlag(absl::string_view text, float* dst, std::string*) {
-  return absl::SimpleAtof(text, dst);
-}
-
-bool AbslParseFlag(absl::string_view text, double* dst, std::string*) {
-  return absl::SimpleAtod(text, dst);
-}
-
-// --------------------------------------------------------------------
-// AbslParseFlag for strings.
-
-bool AbslParseFlag(absl::string_view text, std::string* dst, std::string*) {
-  dst->assign(text.data(), text.size());
-  return true;
-}
-
-// --------------------------------------------------------------------
-// AbslParseFlag for vector of strings.
-
-bool AbslParseFlag(absl::string_view text, std::vector<std::string>* dst,
-                   std::string*) {
-  // An empty flag value corresponds to an empty vector, not a vector
-  // with a single, empty std::string.
-  if (text.empty()) {
-    dst->clear();
-    return true;
-  }
-  *dst = absl::StrSplit(text, ',', absl::AllowEmpty());
-  return true;
-}
-
-// --------------------------------------------------------------------
-// AbslUnparseFlag specializations for various builtin flag types.
-
-std::string Unparse(bool v) { return v ? "true" : "false"; }
-std::string Unparse(short v) { return absl::StrCat(v); }
-std::string Unparse(unsigned short v) { return absl::StrCat(v); }
-std::string Unparse(int v) { return absl::StrCat(v); }
-std::string Unparse(unsigned int v) { return absl::StrCat(v); }
-std::string Unparse(long v) { return absl::StrCat(v); }
-std::string Unparse(unsigned long v) { return absl::StrCat(v); }
-std::string Unparse(long long v) { return absl::StrCat(v); }
-std::string Unparse(unsigned long long v) { return absl::StrCat(v); }
-template <typename T>
-std::string UnparseFloatingPointVal(T v) {
-  // digits10 is guaranteed to roundtrip correctly in string -> value -> string
-  // conversions, but may not be enough to represent all the values correctly.
-  std::string digit10_str =
-      absl::StrFormat("%.*g", std::numeric_limits<T>::digits10, v);
-  if (std::isnan(v) || std::isinf(v)) return digit10_str;
-
-  T roundtrip_val = 0;
-  std::string err;
-  if (absl::ParseFlag(digit10_str, &roundtrip_val, &err) &&
-      roundtrip_val == v) {
-    return digit10_str;
-  }
-
-  // max_digits10 is the number of base-10 digits that are necessary to uniquely
-  // represent all distinct values.
-  return absl::StrFormat("%.*g", std::numeric_limits<T>::max_digits10, v);
-}
-std::string Unparse(float v) { return UnparseFloatingPointVal(v); }
-std::string Unparse(double v) { return UnparseFloatingPointVal(v); }
-std::string AbslUnparseFlag(absl::string_view v) { return std::string(v); }
-std::string AbslUnparseFlag(const std::vector<std::string>& v) {
-  return absl::StrJoin(v, ",");
-}
-
-}  // namespace flags_internal
-
-bool AbslParseFlag(absl::string_view text, absl::LogSeverity* dst,
-                   std::string* err) {
-  text = absl::StripAsciiWhitespace(text);
-  if (text.empty()) {
-    *err = "no value provided";
-    return false;
-  }
-  if (text.front() == 'k' || text.front() == 'K') text.remove_prefix(1);
-  if (absl::EqualsIgnoreCase(text, "info")) {
-    *dst = absl::LogSeverity::kInfo;
-    return true;
-  }
-  if (absl::EqualsIgnoreCase(text, "warning")) {
-    *dst = absl::LogSeverity::kWarning;
-    return true;
-  }
-  if (absl::EqualsIgnoreCase(text, "error")) {
-    *dst = absl::LogSeverity::kError;
-    return true;
-  }
-  if (absl::EqualsIgnoreCase(text, "fatal")) {
-    *dst = absl::LogSeverity::kFatal;
-    return true;
-  }
-  std::underlying_type<absl::LogSeverity>::type numeric_value;
-  if (absl::ParseFlag(text, &numeric_value, err)) {
-    *dst = static_cast<absl::LogSeverity>(numeric_value);
-    return true;
-  }
-  *err = "only integers and absl::LogSeverity enumerators are accepted";
-  return false;
-}
-
-std::string AbslUnparseFlag(absl::LogSeverity v) {
-  if (v == absl::NormalizeLogSeverity(v)) return absl::LogSeverityName(v);
-  return absl::UnparseFlag(static_cast<int>(v));
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/marshalling.h b/third_party/abseil_cpp/absl/flags/marshalling.h
deleted file mode 100644
index 0b5033547e..0000000000
--- a/third_party/abseil_cpp/absl/flags/marshalling.h
+++ /dev/null
@@ -1,264 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: marshalling.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the API for extending Abseil flag support to
-// custom types, and defines the set of overloads for fundamental types.
-//
-// Out of the box, the Abseil flags library supports the following types:
-//
-// * `bool`
-// * `int16_t`
-// * `uint16_t`
-// * `int32_t`
-// * `uint32_t`
-// * `int64_t`
-// * `uint64_t`
-// * `float`
-// * `double`
-// * `std::string`
-// * `std::vector<std::string>`
-// * `absl::LogSeverity` (provided natively for layering reasons)
-//
-// Note that support for integral types is implemented using overloads for
-// variable-width fundamental types (`short`, `int`, `long`, etc.). However,
-// you should prefer the fixed-width integral types (`int32_t`, `uint64_t`,
-// etc.) we've noted above within flag definitions.
-//
-// In addition, several Abseil libraries provide their own custom support for
-// Abseil flags. Documentation for these formats is provided in the type's
-// `AbslParseFlag()` definition.
-//
-// The Abseil time library provides the following support for civil time values:
-//
-// * `absl::CivilSecond`
-// * `absl::CivilMinute`
-// * `absl::CivilHour`
-// * `absl::CivilDay`
-// * `absl::CivilMonth`
-// * `absl::CivilYear`
-//
-// and also provides support for the following absolute time values:
-//
-// * `absl::Duration`
-// * `absl::Time`
-//
-// Additional support for Abseil types will be noted here as it is added.
-//
-// You can also provide your own custom flags by adding overloads for
-// `AbslParseFlag()` and `AbslUnparseFlag()` to your type definitions. (See
-// below.)
-//
-// -----------------------------------------------------------------------------
-// Adding Type Support for Abseil Flags
-// -----------------------------------------------------------------------------
-//
-// To add support for your user-defined type, add overloads of `AbslParseFlag()`
-// and `AbslUnparseFlag()` as free (non-member) functions to your type. If `T`
-// is a class type, these functions can be friend function definitions. These
-// overloads must be added to the same namespace where the type is defined, so
-// that they can be discovered by Argument-Dependent Lookup (ADL).
-//
-// Example:
-//
-//   namespace foo {
-//
-//   enum OutputMode { kPlainText, kHtml };
-//
-//   // AbslParseFlag converts from a string to OutputMode.
-//   // Must be in same namespace as OutputMode.
-//
-//   // Parses an OutputMode from the command line flag value `text. Returns
-//   // `true` and sets `*mode` on success; returns `false` and sets `*error`
-//   // on failure.
-//   bool AbslParseFlag(absl::string_view text,
-//                      OutputMode* mode,
-//                      std::string* error) {
-//     if (text == "plaintext") {
-//       *mode = kPlainText;
-//       return true;
-//     }
-//     if (text == "html") {
-//       *mode = kHtml;
-//      return true;
-//     }
-//     *error = "unknown value for enumeration";
-//     return false;
-//  }
-//
-//  // AbslUnparseFlag converts from an OutputMode to a string.
-//  // Must be in same namespace as OutputMode.
-//
-//  // Returns a textual flag value corresponding to the OutputMode `mode`.
-//  std::string AbslUnparseFlag(OutputMode mode) {
-//    switch (mode) {
-//      case kPlainText: return "plaintext";
-//      case kHtml: return "html";
-//    }
-//    return absl::StrCat(mode);
-//  }
-//
-// Notice that neither `AbslParseFlag()` nor `AbslUnparseFlag()` are class
-// members, but free functions. `AbslParseFlag/AbslUnparseFlag()` overloads
-// for a type should only be declared in the same file and namespace as said
-// type. The proper `AbslParseFlag/AbslUnparseFlag()` implementations for a
-// given type will be discovered via Argument-Dependent Lookup (ADL).
-//
-// `AbslParseFlag()` may need, in turn, to parse simpler constituent types
-// using `absl::ParseFlag()`. For example, a custom struct `MyFlagType`
-// consisting of a `std::pair<int, std::string>` would add an `AbslParseFlag()`
-// overload for its `MyFlagType` like so:
-//
-// Example:
-//
-//   namespace my_flag_type {
-//
-//   struct MyFlagType {
-//     std::pair<int, std::string> my_flag_data;
-//   };
-//
-//   bool AbslParseFlag(absl::string_view text, MyFlagType* flag,
-//                      std::string* err);
-//
-//   std::string AbslUnparseFlag(const MyFlagType&);
-//
-//   // Within the implementation, `AbslParseFlag()` will, in turn invoke
-//   // `absl::ParseFlag()` on its constituent `int` and `std::string` types
-//   // (which have built-in Abseil flag support.
-//
-//   bool AbslParseFlag(absl::string_view text, MyFlagType* flag,
-//                      std::string* err) {
-//     std::pair<absl::string_view, absl::string_view> tokens =
-//         absl::StrSplit(text, ',');
-//     if (!absl::ParseFlag(tokens.first, &flag->my_flag_data.first, err))
-//         return false;
-//     if (!absl::ParseFlag(tokens.second, &flag->my_flag_data.second, err))
-//         return false;
-//     return true;
-//   }
-//
-//   // Similarly, for unparsing, we can simply invoke `absl::UnparseFlag()` on
-//   // the constituent types.
-//   std::string AbslUnparseFlag(const MyFlagType& flag) {
-//     return absl::StrCat(absl::UnparseFlag(flag.my_flag_data.first),
-//                         ",",
-//                         absl::UnparseFlag(flag.my_flag_data.second));
-//   }
-#ifndef ABSL_FLAGS_MARSHALLING_H_
-#define ABSL_FLAGS_MARSHALLING_H_
-
-#include <string>
-#include <vector>
-
-#include "absl/base/config.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types.
-bool AbslParseFlag(absl::string_view, bool*, std::string*);
-bool AbslParseFlag(absl::string_view, short*, std::string*);           // NOLINT
-bool AbslParseFlag(absl::string_view, unsigned short*, std::string*);  // NOLINT
-bool AbslParseFlag(absl::string_view, int*, std::string*);             // NOLINT
-bool AbslParseFlag(absl::string_view, unsigned int*, std::string*);    // NOLINT
-bool AbslParseFlag(absl::string_view, long*, std::string*);            // NOLINT
-bool AbslParseFlag(absl::string_view, unsigned long*, std::string*);   // NOLINT
-bool AbslParseFlag(absl::string_view, long long*, std::string*);       // NOLINT
-bool AbslParseFlag(absl::string_view, unsigned long long*,             // NOLINT
-                   std::string*);
-bool AbslParseFlag(absl::string_view, float*, std::string*);
-bool AbslParseFlag(absl::string_view, double*, std::string*);
-bool AbslParseFlag(absl::string_view, std::string*, std::string*);
-bool AbslParseFlag(absl::string_view, std::vector<std::string>*, std::string*);
-
-template <typename T>
-bool InvokeParseFlag(absl::string_view input, T* dst, std::string* err) {
-  // Comment on next line provides a good compiler error message if T
-  // does not have AbslParseFlag(absl::string_view, T*, std::string*).
-  return AbslParseFlag(input, dst, err);  // Is T missing AbslParseFlag?
-}
-
-// Strings and std:: containers do not have the same overload resolution
-// considerations as fundamental types. Naming these 'AbslUnparseFlag' means we
-// can avoid the need for additional specializations of Unparse (below).
-std::string AbslUnparseFlag(absl::string_view v);
-std::string AbslUnparseFlag(const std::vector<std::string>&);
-
-template <typename T>
-std::string Unparse(const T& v) {
-  // Comment on next line provides a good compiler error message if T does not
-  // have UnparseFlag.
-  return AbslUnparseFlag(v);  // Is T missing AbslUnparseFlag?
-}
-
-// Overloads for builtin types.
-std::string Unparse(bool v);
-std::string Unparse(short v);               // NOLINT
-std::string Unparse(unsigned short v);      // NOLINT
-std::string Unparse(int v);                 // NOLINT
-std::string Unparse(unsigned int v);        // NOLINT
-std::string Unparse(long v);                // NOLINT
-std::string Unparse(unsigned long v);       // NOLINT
-std::string Unparse(long long v);           // NOLINT
-std::string Unparse(unsigned long long v);  // NOLINT
-std::string Unparse(float v);
-std::string Unparse(double v);
-
-}  // namespace flags_internal
-
-// ParseFlag()
-//
-// Parses a string value into a flag value of type `T`. Do not add overloads of
-// this function for your type directly; instead, add an `AbslParseFlag()`
-// free function as documented above.
-//
-// Some implementations of `AbslParseFlag()` for types which consist of other,
-// constituent types which already have Abseil flag support, may need to call
-// `absl::ParseFlag()` on those consituent string values. (See above.)
-template <typename T>
-inline bool ParseFlag(absl::string_view input, T* dst, std::string* error) {
-  return flags_internal::InvokeParseFlag(input, dst, error);
-}
-
-// UnparseFlag()
-//
-// Unparses a flag value of type `T` into a string value. Do not add overloads
-// of this function for your type directly; instead, add an `AbslUnparseFlag()`
-// free function as documented above.
-//
-// Some implementations of `AbslUnparseFlag()` for types which consist of other,
-// constituent types which already have Abseil flag support, may want to call
-// `absl::UnparseFlag()` on those constituent types. (See above.)
-template <typename T>
-inline std::string UnparseFlag(const T& v) {
-  return flags_internal::Unparse(v);
-}
-
-// Overloads for `absl::LogSeverity` can't (easily) appear alongside that type's
-// definition because it is layered below flags.  See proper documentation in
-// base/log_severity.h.
-enum class LogSeverity : int;
-bool AbslParseFlag(absl::string_view, absl::LogSeverity*, std::string*);
-std::string AbslUnparseFlag(absl::LogSeverity);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_MARSHALLING_H_
diff --git a/third_party/abseil_cpp/absl/flags/marshalling_test.cc b/third_party/abseil_cpp/absl/flags/marshalling_test.cc
deleted file mode 100644
index 4a64ce11a1..0000000000
--- a/third_party/abseil_cpp/absl/flags/marshalling_test.cc
+++ /dev/null
@@ -1,904 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/marshalling.h"
-
-#include <stdint.h>
-
-#include <cmath>
-#include <limits>
-#include <string>
-#include <vector>
-
-#include "gtest/gtest.h"
-
-namespace {
-
-TEST(MarshallingTest, TestBoolParsing) {
-  std::string err;
-  bool value;
-
-  // True values.
-  EXPECT_TRUE(absl::ParseFlag("True", &value, &err));
-  EXPECT_TRUE(value);
-  EXPECT_TRUE(absl::ParseFlag("true", &value, &err));
-  EXPECT_TRUE(value);
-  EXPECT_TRUE(absl::ParseFlag("TRUE", &value, &err));
-  EXPECT_TRUE(value);
-
-  EXPECT_TRUE(absl::ParseFlag("Yes", &value, &err));
-  EXPECT_TRUE(value);
-  EXPECT_TRUE(absl::ParseFlag("yes", &value, &err));
-  EXPECT_TRUE(value);
-  EXPECT_TRUE(absl::ParseFlag("YES", &value, &err));
-  EXPECT_TRUE(value);
-
-  EXPECT_TRUE(absl::ParseFlag("t", &value, &err));
-  EXPECT_TRUE(value);
-  EXPECT_TRUE(absl::ParseFlag("T", &value, &err));
-  EXPECT_TRUE(value);
-
-  EXPECT_TRUE(absl::ParseFlag("y", &value, &err));
-  EXPECT_TRUE(value);
-  EXPECT_TRUE(absl::ParseFlag("Y", &value, &err));
-  EXPECT_TRUE(value);
-
-  EXPECT_TRUE(absl::ParseFlag("1", &value, &err));
-  EXPECT_TRUE(value);
-
-  // False values.
-  EXPECT_TRUE(absl::ParseFlag("False", &value, &err));
-  EXPECT_FALSE(value);
-  EXPECT_TRUE(absl::ParseFlag("false", &value, &err));
-  EXPECT_FALSE(value);
-  EXPECT_TRUE(absl::ParseFlag("FALSE", &value, &err));
-  EXPECT_FALSE(value);
-
-  EXPECT_TRUE(absl::ParseFlag("No", &value, &err));
-  EXPECT_FALSE(value);
-  EXPECT_TRUE(absl::ParseFlag("no", &value, &err));
-  EXPECT_FALSE(value);
-  EXPECT_TRUE(absl::ParseFlag("NO", &value, &err));
-  EXPECT_FALSE(value);
-
-  EXPECT_TRUE(absl::ParseFlag("f", &value, &err));
-  EXPECT_FALSE(value);
-  EXPECT_TRUE(absl::ParseFlag("F", &value, &err));
-  EXPECT_FALSE(value);
-
-  EXPECT_TRUE(absl::ParseFlag("n", &value, &err));
-  EXPECT_FALSE(value);
-  EXPECT_TRUE(absl::ParseFlag("N", &value, &err));
-  EXPECT_FALSE(value);
-
-  EXPECT_TRUE(absl::ParseFlag("0", &value, &err));
-  EXPECT_FALSE(value);
-
-  // Whitespace handling.
-  EXPECT_TRUE(absl::ParseFlag("  true", &value, &err));
-  EXPECT_TRUE(value);
-  EXPECT_TRUE(absl::ParseFlag("true  ", &value, &err));
-  EXPECT_TRUE(value);
-  EXPECT_TRUE(absl::ParseFlag("  true   ", &value, &err));
-  EXPECT_TRUE(value);
-
-  // Invalid input.
-  EXPECT_FALSE(absl::ParseFlag("", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("  ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("2", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("11", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("tt", &value, &err));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestInt16Parsing) {
-  std::string err;
-  int16_t value;
-
-  // Decimal values.
-  EXPECT_TRUE(absl::ParseFlag("1", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0", &value, &err));
-  EXPECT_EQ(value, 0);
-  EXPECT_TRUE(absl::ParseFlag("-1", &value, &err));
-  EXPECT_EQ(value, -1);
-  EXPECT_TRUE(absl::ParseFlag("123", &value, &err));
-  EXPECT_EQ(value, 123);
-  EXPECT_TRUE(absl::ParseFlag("-18765", &value, &err));
-  EXPECT_EQ(value, -18765);
-  EXPECT_TRUE(absl::ParseFlag("+3", &value, &err));
-  EXPECT_EQ(value, 3);
-
-  // Leading zero values.
-  EXPECT_TRUE(absl::ParseFlag("01", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("-001", &value, &err));
-  EXPECT_EQ(value, -1);
-  EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err));
-  EXPECT_EQ(value, 100);
-
-  // Hex values.
-  EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err));
-  EXPECT_EQ(value, 16);
-  EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err));
-  EXPECT_EQ(value, 564);
-  // TODO(rogeeff): fix below validations
-  EXPECT_FALSE(absl::ParseFlag("-0x7FFD", &value, &err));
-  EXPECT_NE(value, -3);
-  EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err));
-  EXPECT_NE(value, 49);
-
-  // Whitespace handling
-  EXPECT_TRUE(absl::ParseFlag("10  ", &value, &err));
-  EXPECT_EQ(value, 10);
-  EXPECT_TRUE(absl::ParseFlag("  11", &value, &err));
-  EXPECT_EQ(value, 11);
-  EXPECT_TRUE(absl::ParseFlag("  012  ", &value, &err));
-  EXPECT_EQ(value, 12);
-  EXPECT_TRUE(absl::ParseFlag(" 0x22    ", &value, &err));
-  EXPECT_EQ(value, 34);
-
-  // Invalid values.
-  EXPECT_FALSE(absl::ParseFlag("", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag(" ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("  ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("40000", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("--1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("2U", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestUint16Parsing) {
-  std::string err;
-  uint16_t value;
-
-  // Decimal values.
-  EXPECT_TRUE(absl::ParseFlag("1", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0", &value, &err));
-  EXPECT_EQ(value, 0);
-  EXPECT_TRUE(absl::ParseFlag("123", &value, &err));
-  EXPECT_EQ(value, 123);
-  EXPECT_TRUE(absl::ParseFlag("+3", &value, &err));
-  EXPECT_EQ(value, 3);
-
-  // Leading zero values.
-  EXPECT_TRUE(absl::ParseFlag("01", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("001", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err));
-  EXPECT_EQ(value, 100);
-
-  // Hex values.
-  EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err));
-  EXPECT_EQ(value, 16);
-  EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err));
-  EXPECT_EQ(value, 564);
-  // TODO(rogeeff): fix below validations
-  EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err));
-  EXPECT_NE(value, 49);
-
-  // Whitespace handling
-  EXPECT_TRUE(absl::ParseFlag("10  ", &value, &err));
-  EXPECT_EQ(value, 10);
-  EXPECT_TRUE(absl::ParseFlag("  11", &value, &err));
-  EXPECT_EQ(value, 11);
-  EXPECT_TRUE(absl::ParseFlag("  012  ", &value, &err));
-  EXPECT_EQ(value, 12);
-  EXPECT_TRUE(absl::ParseFlag(" 0x22    ", &value, &err));
-  EXPECT_EQ(value, 34);
-
-  // Invalid values.
-  EXPECT_FALSE(absl::ParseFlag("", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag(" ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("  ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("70000", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("-1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("--1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("2U", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestInt32Parsing) {
-  std::string err;
-  int32_t value;
-
-  // Decimal values.
-  EXPECT_TRUE(absl::ParseFlag("1", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0", &value, &err));
-  EXPECT_EQ(value, 0);
-  EXPECT_TRUE(absl::ParseFlag("-1", &value, &err));
-  EXPECT_EQ(value, -1);
-  EXPECT_TRUE(absl::ParseFlag("123", &value, &err));
-  EXPECT_EQ(value, 123);
-  EXPECT_TRUE(absl::ParseFlag("-98765", &value, &err));
-  EXPECT_EQ(value, -98765);
-  EXPECT_TRUE(absl::ParseFlag("+3", &value, &err));
-  EXPECT_EQ(value, 3);
-
-  // Leading zero values.
-  EXPECT_TRUE(absl::ParseFlag("01", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("-001", &value, &err));
-  EXPECT_EQ(value, -1);
-  EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err));
-  EXPECT_EQ(value, 100);
-
-  // Hex values.
-  EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err));
-  EXPECT_EQ(value, 16);
-  EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err));
-  EXPECT_EQ(value, 564);
-  // TODO(rogeeff): fix below validations
-  EXPECT_FALSE(absl::ParseFlag("-0x7FFFFFFD", &value, &err));
-  EXPECT_NE(value, -3);
-  EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err));
-  EXPECT_NE(value, 49);
-
-  // Whitespace handling
-  EXPECT_TRUE(absl::ParseFlag("10  ", &value, &err));
-  EXPECT_EQ(value, 10);
-  EXPECT_TRUE(absl::ParseFlag("  11", &value, &err));
-  EXPECT_EQ(value, 11);
-  EXPECT_TRUE(absl::ParseFlag("  012  ", &value, &err));
-  EXPECT_EQ(value, 12);
-  EXPECT_TRUE(absl::ParseFlag(" 0x22    ", &value, &err));
-  EXPECT_EQ(value, 34);
-
-  // Invalid values.
-  EXPECT_FALSE(absl::ParseFlag("", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag(" ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("  ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("70000000000", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("--1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("2U", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestUint32Parsing) {
-  std::string err;
-  uint32_t value;
-
-  // Decimal values.
-  EXPECT_TRUE(absl::ParseFlag("1", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0", &value, &err));
-  EXPECT_EQ(value, 0);
-  EXPECT_TRUE(absl::ParseFlag("123", &value, &err));
-  EXPECT_EQ(value, 123);
-  EXPECT_TRUE(absl::ParseFlag("+3", &value, &err));
-  EXPECT_EQ(value, 3);
-
-  // Leading zero values.
-  EXPECT_TRUE(absl::ParseFlag("01", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err));
-  EXPECT_EQ(value, 100);
-
-  // Hex values.
-  EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err));
-  EXPECT_EQ(value, 16);
-  EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err));
-  EXPECT_EQ(value, 564);
-  EXPECT_TRUE(absl::ParseFlag("0xFFFFFFFD", &value, &err));
-  EXPECT_EQ(value, 4294967293);
-  // TODO(rogeeff): fix below validations
-  EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err));
-  EXPECT_NE(value, 49);
-
-  // Whitespace handling
-  EXPECT_TRUE(absl::ParseFlag("10  ", &value, &err));
-  EXPECT_EQ(value, 10);
-  EXPECT_TRUE(absl::ParseFlag("  11", &value, &err));
-  EXPECT_EQ(value, 11);
-  EXPECT_TRUE(absl::ParseFlag("  012  ", &value, &err));
-  EXPECT_EQ(value, 12);
-  EXPECT_TRUE(absl::ParseFlag(" 0x22    ", &value, &err));
-  EXPECT_EQ(value, 34);
-
-  // Invalid values.
-  EXPECT_FALSE(absl::ParseFlag("", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag(" ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("  ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("140000000000", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("-1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("--1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("2U", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestInt64Parsing) {
-  std::string err;
-  int64_t value;
-
-  // Decimal values.
-  EXPECT_TRUE(absl::ParseFlag("1", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0", &value, &err));
-  EXPECT_EQ(value, 0);
-  EXPECT_TRUE(absl::ParseFlag("-1", &value, &err));
-  EXPECT_EQ(value, -1);
-  EXPECT_TRUE(absl::ParseFlag("123", &value, &err));
-  EXPECT_EQ(value, 123);
-  EXPECT_TRUE(absl::ParseFlag("-98765", &value, &err));
-  EXPECT_EQ(value, -98765);
-  EXPECT_TRUE(absl::ParseFlag("+3", &value, &err));
-  EXPECT_EQ(value, 3);
-
-  // Leading zero values.
-  EXPECT_TRUE(absl::ParseFlag("01", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("001", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err));
-  EXPECT_EQ(value, 100);
-
-  // Hex values.
-  EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err));
-  EXPECT_EQ(value, 16);
-  EXPECT_TRUE(absl::ParseFlag("0XFFFAAABBBCCCDDD", &value, &err));
-  EXPECT_EQ(value, 1152827684197027293);
-  // TODO(rogeeff): fix below validation
-  EXPECT_FALSE(absl::ParseFlag("-0x7FFFFFFFFFFFFFFE", &value, &err));
-  EXPECT_NE(value, -2);
-  EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err));
-  EXPECT_NE(value, 49);
-
-  // Whitespace handling
-  EXPECT_TRUE(absl::ParseFlag("10  ", &value, &err));
-  EXPECT_EQ(value, 10);
-  EXPECT_TRUE(absl::ParseFlag("  11", &value, &err));
-  EXPECT_EQ(value, 11);
-  EXPECT_TRUE(absl::ParseFlag("  012  ", &value, &err));
-  EXPECT_EQ(value, 12);
-  EXPECT_TRUE(absl::ParseFlag(" 0x7F    ", &value, &err));
-  EXPECT_EQ(value, 127);
-
-  // Invalid values.
-  EXPECT_FALSE(absl::ParseFlag("", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag(" ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("  ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("0xFFFFFFFFFFFFFFFFFF", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("--1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("2U", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestUInt64Parsing) {
-  std::string err;
-  uint64_t value;
-
-  // Decimal values.
-  EXPECT_TRUE(absl::ParseFlag("1", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0", &value, &err));
-  EXPECT_EQ(value, 0);
-  EXPECT_TRUE(absl::ParseFlag("123", &value, &err));
-  EXPECT_EQ(value, 123);
-  EXPECT_TRUE(absl::ParseFlag("+13", &value, &err));
-  EXPECT_EQ(value, 13);
-
-  // Leading zero values.
-  EXPECT_TRUE(absl::ParseFlag("01", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("001", &value, &err));
-  EXPECT_EQ(value, 1);
-  EXPECT_TRUE(absl::ParseFlag("0000300", &value, &err));
-  EXPECT_EQ(value, 300);
-
-  // Hex values.
-  EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err));
-  EXPECT_EQ(value, 16);
-  EXPECT_TRUE(absl::ParseFlag("0XFFFF", &value, &err));
-  EXPECT_EQ(value, 65535);
-  // TODO(rogeeff): fix below validation
-  EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err));
-  EXPECT_NE(value, 49);
-
-  // Whitespace handling
-  EXPECT_TRUE(absl::ParseFlag("10  ", &value, &err));
-  EXPECT_EQ(value, 10);
-  EXPECT_TRUE(absl::ParseFlag("  11", &value, &err));
-  EXPECT_EQ(value, 11);
-  EXPECT_TRUE(absl::ParseFlag("  012  ", &value, &err));
-  EXPECT_EQ(value, 12);
-
-  // Invalid values.
-  EXPECT_FALSE(absl::ParseFlag("", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag(" ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("  ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("0xFFFFFFFFFFFFFFFFFF", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("-1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("--1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("2U", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestFloatParsing) {
-  std::string err;
-  float value;
-
-  // Ordinary values.
-  EXPECT_TRUE(absl::ParseFlag("1.3", &value, &err));
-  EXPECT_FLOAT_EQ(value, 1.3f);
-  EXPECT_TRUE(absl::ParseFlag("-0.1", &value, &err));
-  EXPECT_DOUBLE_EQ(value, -0.1f);
-  EXPECT_TRUE(absl::ParseFlag("+0.01", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 0.01f);
-
-  // Scientific values.
-  EXPECT_TRUE(absl::ParseFlag("1.2e3", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 1.2e3f);
-  EXPECT_TRUE(absl::ParseFlag("9.8765402e-37", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 9.8765402e-37f);
-  EXPECT_TRUE(absl::ParseFlag("0.11e+3", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 0.11e+3f);
-  EXPECT_TRUE(absl::ParseFlag("1.e-2300", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 0.f);
-  EXPECT_TRUE(absl::ParseFlag("1.e+2300", &value, &err));
-  EXPECT_TRUE(std::isinf(value));
-
-  // Leading zero values.
-  EXPECT_TRUE(absl::ParseFlag("01.6", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 1.6f);
-  EXPECT_TRUE(absl::ParseFlag("000.0001", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 0.0001f);
-
-  // Trailing zero values.
-  EXPECT_TRUE(absl::ParseFlag("-5.1000", &value, &err));
-  EXPECT_DOUBLE_EQ(value, -5.1f);
-
-  // Exceptional values.
-  EXPECT_TRUE(absl::ParseFlag("NaN", &value, &err));
-  EXPECT_TRUE(std::isnan(value));
-  EXPECT_TRUE(absl::ParseFlag("Inf", &value, &err));
-  EXPECT_TRUE(std::isinf(value));
-
-  // Hex values
-  EXPECT_TRUE(absl::ParseFlag("0x10.23p12", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 66096.f);
-  EXPECT_TRUE(absl::ParseFlag("-0xF1.A3p-2", &value, &err));
-  EXPECT_NEAR(value, -60.4092f, 5e-5f);
-  EXPECT_TRUE(absl::ParseFlag("+0x0.0AAp-12", &value, &err));
-  EXPECT_NEAR(value, 1.01328e-05f, 5e-11f);
-  EXPECT_TRUE(absl::ParseFlag("0x.01p1", &value, &err));
-  EXPECT_NEAR(value, 0.0078125f, 5e-8f);
-
-  // Whitespace handling
-  EXPECT_TRUE(absl::ParseFlag("10.1  ", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 10.1f);
-  EXPECT_TRUE(absl::ParseFlag("  2.34", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 2.34f);
-  EXPECT_TRUE(absl::ParseFlag("  5.7  ", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 5.7f);
-  EXPECT_TRUE(absl::ParseFlag("  -0xE0.F3p01  ", &value, &err));
-  EXPECT_NEAR(value, -449.8984375f, 5e-8f);
-
-  // Invalid values.
-  EXPECT_FALSE(absl::ParseFlag("", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag(" ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("  ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("--1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("2.3xxx", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("0x0.1pAA", &value, &err));
-  // TODO(rogeeff): below assertion should fail
-  EXPECT_TRUE(absl::ParseFlag("0x0.1", &value, &err));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestDoubleParsing) {
-  std::string err;
-  double value;
-
-  // Ordinary values.
-  EXPECT_TRUE(absl::ParseFlag("1.3", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 1.3);
-  EXPECT_TRUE(absl::ParseFlag("-0.1", &value, &err));
-  EXPECT_DOUBLE_EQ(value, -0.1);
-  EXPECT_TRUE(absl::ParseFlag("+0.01", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 0.01);
-
-  // Scientific values.
-  EXPECT_TRUE(absl::ParseFlag("1.2e3", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 1.2e3);
-  EXPECT_TRUE(absl::ParseFlag("9.00000002e-123", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 9.00000002e-123);
-  EXPECT_TRUE(absl::ParseFlag("0.11e+3", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 0.11e+3);
-  EXPECT_TRUE(absl::ParseFlag("1.e-2300", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 0);
-  EXPECT_TRUE(absl::ParseFlag("1.e+2300", &value, &err));
-  EXPECT_TRUE(std::isinf(value));
-
-  // Leading zero values.
-  EXPECT_TRUE(absl::ParseFlag("01.6", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 1.6);
-  EXPECT_TRUE(absl::ParseFlag("000.0001", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 0.0001);
-
-  // Trailing zero values.
-  EXPECT_TRUE(absl::ParseFlag("-5.1000", &value, &err));
-  EXPECT_DOUBLE_EQ(value, -5.1);
-
-  // Exceptional values.
-  EXPECT_TRUE(absl::ParseFlag("NaN", &value, &err));
-  EXPECT_TRUE(std::isnan(value));
-  EXPECT_TRUE(absl::ParseFlag("nan", &value, &err));
-  EXPECT_TRUE(std::isnan(value));
-  EXPECT_TRUE(absl::ParseFlag("Inf", &value, &err));
-  EXPECT_TRUE(std::isinf(value));
-  EXPECT_TRUE(absl::ParseFlag("inf", &value, &err));
-  EXPECT_TRUE(std::isinf(value));
-
-  // Hex values
-  EXPECT_TRUE(absl::ParseFlag("0x10.23p12", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 66096);
-  EXPECT_TRUE(absl::ParseFlag("-0xF1.A3p-2", &value, &err));
-  EXPECT_NEAR(value, -60.4092, 5e-5);
-  EXPECT_TRUE(absl::ParseFlag("+0x0.0AAp-12", &value, &err));
-  EXPECT_NEAR(value, 1.01328e-05, 5e-11);
-  EXPECT_TRUE(absl::ParseFlag("0x.01p1", &value, &err));
-  EXPECT_NEAR(value, 0.0078125, 5e-8);
-
-  // Whitespace handling
-  EXPECT_TRUE(absl::ParseFlag("10.1  ", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 10.1);
-  EXPECT_TRUE(absl::ParseFlag("  2.34", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 2.34);
-  EXPECT_TRUE(absl::ParseFlag("  5.7  ", &value, &err));
-  EXPECT_DOUBLE_EQ(value, 5.7);
-  EXPECT_TRUE(absl::ParseFlag("  -0xE0.F3p01  ", &value, &err));
-  EXPECT_NEAR(value, -449.8984375, 5e-8);
-
-  // Invalid values.
-  EXPECT_FALSE(absl::ParseFlag("", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag(" ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("  ", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("--1", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("2.3xxx", &value, &err));
-  EXPECT_FALSE(absl::ParseFlag("0x0.1pAA", &value, &err));
-  // TODO(rogeeff): below assertion should fail
-  EXPECT_TRUE(absl::ParseFlag("0x0.1", &value, &err));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestStringParsing) {
-  std::string err;
-  std::string value;
-
-  EXPECT_TRUE(absl::ParseFlag("", &value, &err));
-  EXPECT_EQ(value, "");
-  EXPECT_TRUE(absl::ParseFlag(" ", &value, &err));
-  EXPECT_EQ(value, " ");
-  EXPECT_TRUE(absl::ParseFlag("   ", &value, &err));
-  EXPECT_EQ(value, "   ");
-  EXPECT_TRUE(absl::ParseFlag("\n", &value, &err));
-  EXPECT_EQ(value, "\n");
-  EXPECT_TRUE(absl::ParseFlag("\t", &value, &err));
-  EXPECT_EQ(value, "\t");
-  EXPECT_TRUE(absl::ParseFlag("asdfg", &value, &err));
-  EXPECT_EQ(value, "asdfg");
-  EXPECT_TRUE(absl::ParseFlag("asdf ghjk", &value, &err));
-  EXPECT_EQ(value, "asdf ghjk");
-  EXPECT_TRUE(absl::ParseFlag("a\nb\nc", &value, &err));
-  EXPECT_EQ(value, "a\nb\nc");
-  EXPECT_TRUE(absl::ParseFlag("asd\0fgh", &value, &err));
-  EXPECT_EQ(value, "asd");
-  EXPECT_TRUE(absl::ParseFlag("\\\\", &value, &err));
-  EXPECT_EQ(value, "\\\\");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestVectorOfStringParsing) {
-  std::string err;
-  std::vector<std::string> value;
-
-  EXPECT_TRUE(absl::ParseFlag("", &value, &err));
-  EXPECT_EQ(value, std::vector<std::string>{});
-  EXPECT_TRUE(absl::ParseFlag("1", &value, &err));
-  EXPECT_EQ(value, std::vector<std::string>({"1"}));
-  EXPECT_TRUE(absl::ParseFlag("a,b", &value, &err));
-  EXPECT_EQ(value, std::vector<std::string>({"a", "b"}));
-  EXPECT_TRUE(absl::ParseFlag("a,b,c,", &value, &err));
-  EXPECT_EQ(value, std::vector<std::string>({"a", "b", "c", ""}));
-  EXPECT_TRUE(absl::ParseFlag("a,,", &value, &err));
-  EXPECT_EQ(value, std::vector<std::string>({"a", "", ""}));
-  EXPECT_TRUE(absl::ParseFlag(",", &value, &err));
-  EXPECT_EQ(value, std::vector<std::string>({"", ""}));
-  EXPECT_TRUE(absl::ParseFlag("a, b,c ", &value, &err));
-  EXPECT_EQ(value, std::vector<std::string>({"a", " b", "c "}));
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestBoolUnparsing) {
-  EXPECT_EQ(absl::UnparseFlag(true), "true");
-  EXPECT_EQ(absl::UnparseFlag(false), "false");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestInt16Unparsing) {
-  int16_t value;
-
-  value = 1;
-  EXPECT_EQ(absl::UnparseFlag(value), "1");
-  value = 0;
-  EXPECT_EQ(absl::UnparseFlag(value), "0");
-  value = -1;
-  EXPECT_EQ(absl::UnparseFlag(value), "-1");
-  value = 9876;
-  EXPECT_EQ(absl::UnparseFlag(value), "9876");
-  value = -987;
-  EXPECT_EQ(absl::UnparseFlag(value), "-987");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestUint16Unparsing) {
-  uint16_t value;
-
-  value = 1;
-  EXPECT_EQ(absl::UnparseFlag(value), "1");
-  value = 0;
-  EXPECT_EQ(absl::UnparseFlag(value), "0");
-  value = 19876;
-  EXPECT_EQ(absl::UnparseFlag(value), "19876");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestInt32Unparsing) {
-  int32_t value;
-
-  value = 1;
-  EXPECT_EQ(absl::UnparseFlag(value), "1");
-  value = 0;
-  EXPECT_EQ(absl::UnparseFlag(value), "0");
-  value = -1;
-  EXPECT_EQ(absl::UnparseFlag(value), "-1");
-  value = 12345;
-  EXPECT_EQ(absl::UnparseFlag(value), "12345");
-  value = -987;
-  EXPECT_EQ(absl::UnparseFlag(value), "-987");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestUint32Unparsing) {
-  uint32_t value;
-
-  value = 1;
-  EXPECT_EQ(absl::UnparseFlag(value), "1");
-  value = 0;
-  EXPECT_EQ(absl::UnparseFlag(value), "0");
-  value = 1234500;
-  EXPECT_EQ(absl::UnparseFlag(value), "1234500");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestInt64Unparsing) {
-  int64_t value;
-
-  value = 1;
-  EXPECT_EQ(absl::UnparseFlag(value), "1");
-  value = 0;
-  EXPECT_EQ(absl::UnparseFlag(value), "0");
-  value = -1;
-  EXPECT_EQ(absl::UnparseFlag(value), "-1");
-  value = 123456789L;
-  EXPECT_EQ(absl::UnparseFlag(value), "123456789");
-  value = -987654321L;
-  EXPECT_EQ(absl::UnparseFlag(value), "-987654321");
-  value = 0x7FFFFFFFFFFFFFFF;
-  EXPECT_EQ(absl::UnparseFlag(value), "9223372036854775807");
-  value = 0xFFFFFFFFFFFFFFFF;
-  EXPECT_EQ(absl::UnparseFlag(value), "-1");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestUint64Unparsing) {
-  uint64_t value;
-
-  value = 1;
-  EXPECT_EQ(absl::UnparseFlag(value), "1");
-  value = 0;
-  EXPECT_EQ(absl::UnparseFlag(value), "0");
-  value = 123456789L;
-  EXPECT_EQ(absl::UnparseFlag(value), "123456789");
-  value = 0xFFFFFFFFFFFFFFFF;
-  EXPECT_EQ(absl::UnparseFlag(value), "18446744073709551615");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestFloatUnparsing) {
-  float value;
-
-  value = 1.1f;
-  EXPECT_EQ(absl::UnparseFlag(value), "1.1");
-  value = 0.01f;
-  EXPECT_EQ(absl::UnparseFlag(value), "0.01");
-  value = 1.23e-2f;
-  EXPECT_EQ(absl::UnparseFlag(value), "0.0123");
-  value = -0.71f;
-  EXPECT_EQ(absl::UnparseFlag(value), "-0.71");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestDoubleUnparsing) {
-  double value;
-
-  value = 1.1;
-  EXPECT_EQ(absl::UnparseFlag(value), "1.1");
-  value = 0.01;
-  EXPECT_EQ(absl::UnparseFlag(value), "0.01");
-  value = 1.23e-2;
-  EXPECT_EQ(absl::UnparseFlag(value), "0.0123");
-  value = -0.71;
-  EXPECT_EQ(absl::UnparseFlag(value), "-0.71");
-  value = -0;
-  EXPECT_EQ(absl::UnparseFlag(value), "0");
-  value = std::nan("");
-  EXPECT_EQ(absl::UnparseFlag(value), "nan");
-  value = std::numeric_limits<double>::infinity();
-  EXPECT_EQ(absl::UnparseFlag(value), "inf");
-}
-
-// --------------------------------------------------------------------
-
-TEST(MarshallingTest, TestStringUnparsing) {
-  EXPECT_EQ(absl::UnparseFlag(""), "");
-  EXPECT_EQ(absl::UnparseFlag(" "), " ");
-  EXPECT_EQ(absl::UnparseFlag("qwerty"), "qwerty");
-  EXPECT_EQ(absl::UnparseFlag("ASDFGH"), "ASDFGH");
-  EXPECT_EQ(absl::UnparseFlag("\n\t  "), "\n\t  ");
-}
-
-// --------------------------------------------------------------------
-
-template <typename T>
-void TestRoundtrip(T v) {
-  T new_v;
-  std::string err;
-  EXPECT_TRUE(absl::ParseFlag(absl::UnparseFlag(v), &new_v, &err));
-  EXPECT_EQ(new_v, v);
-}
-
-TEST(MarshallingTest, TestFloatRoundTrip) {
-  TestRoundtrip(0.1f);
-  TestRoundtrip(0.12f);
-  TestRoundtrip(0.123f);
-  TestRoundtrip(0.1234f);
-  TestRoundtrip(0.12345f);
-  TestRoundtrip(0.123456f);
-  TestRoundtrip(0.1234567f);
-  TestRoundtrip(0.12345678f);
-
-  TestRoundtrip(0.1e20f);
-  TestRoundtrip(0.12e20f);
-  TestRoundtrip(0.123e20f);
-  TestRoundtrip(0.1234e20f);
-  TestRoundtrip(0.12345e20f);
-  TestRoundtrip(0.123456e20f);
-  TestRoundtrip(0.1234567e20f);
-  TestRoundtrip(0.12345678e20f);
-
-  TestRoundtrip(0.1e-20f);
-  TestRoundtrip(0.12e-20f);
-  TestRoundtrip(0.123e-20f);
-  TestRoundtrip(0.1234e-20f);
-  TestRoundtrip(0.12345e-20f);
-  TestRoundtrip(0.123456e-20f);
-  TestRoundtrip(0.1234567e-20f);
-  TestRoundtrip(0.12345678e-20f);
-}
-
-TEST(MarshallingTest, TestDoubleRoundTrip) {
-  TestRoundtrip(0.1);
-  TestRoundtrip(0.12);
-  TestRoundtrip(0.123);
-  TestRoundtrip(0.1234);
-  TestRoundtrip(0.12345);
-  TestRoundtrip(0.123456);
-  TestRoundtrip(0.1234567);
-  TestRoundtrip(0.12345678);
-  TestRoundtrip(0.123456789);
-  TestRoundtrip(0.1234567891);
-  TestRoundtrip(0.12345678912);
-  TestRoundtrip(0.123456789123);
-  TestRoundtrip(0.1234567891234);
-  TestRoundtrip(0.12345678912345);
-  TestRoundtrip(0.123456789123456);
-  TestRoundtrip(0.1234567891234567);
-  TestRoundtrip(0.12345678912345678);
-
-  TestRoundtrip(0.1e50);
-  TestRoundtrip(0.12e50);
-  TestRoundtrip(0.123e50);
-  TestRoundtrip(0.1234e50);
-  TestRoundtrip(0.12345e50);
-  TestRoundtrip(0.123456e50);
-  TestRoundtrip(0.1234567e50);
-  TestRoundtrip(0.12345678e50);
-  TestRoundtrip(0.123456789e50);
-  TestRoundtrip(0.1234567891e50);
-  TestRoundtrip(0.12345678912e50);
-  TestRoundtrip(0.123456789123e50);
-  TestRoundtrip(0.1234567891234e50);
-  TestRoundtrip(0.12345678912345e50);
-  TestRoundtrip(0.123456789123456e50);
-  TestRoundtrip(0.1234567891234567e50);
-  TestRoundtrip(0.12345678912345678e50);
-
-  TestRoundtrip(0.1e-50);
-  TestRoundtrip(0.12e-50);
-  TestRoundtrip(0.123e-50);
-  TestRoundtrip(0.1234e-50);
-  TestRoundtrip(0.12345e-50);
-  TestRoundtrip(0.123456e-50);
-  TestRoundtrip(0.1234567e-50);
-  TestRoundtrip(0.12345678e-50);
-  TestRoundtrip(0.123456789e-50);
-  TestRoundtrip(0.1234567891e-50);
-  TestRoundtrip(0.12345678912e-50);
-  TestRoundtrip(0.123456789123e-50);
-  TestRoundtrip(0.1234567891234e-50);
-  TestRoundtrip(0.12345678912345e-50);
-  TestRoundtrip(0.123456789123456e-50);
-  TestRoundtrip(0.1234567891234567e-50);
-  TestRoundtrip(0.12345678912345678e-50);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/flags/parse.cc b/third_party/abseil_cpp/absl/flags/parse.cc
deleted file mode 100644
index dd1a6796ca..0000000000
--- a/third_party/abseil_cpp/absl/flags/parse.cc
+++ /dev/null
@@ -1,823 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/parse.h"
-
-#include <stdlib.h>
-
-#include <algorithm>
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <string>
-#include <tuple>
-#include <utility>
-#include <vector>
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/const_init.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/flags/commandlineflag.h"
-#include "absl/flags/config.h"
-#include "absl/flags/flag.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/flags/internal/flag.h"
-#include "absl/flags/internal/parse.h"
-#include "absl/flags/internal/private_handle_accessor.h"
-#include "absl/flags/internal/program_name.h"
-#include "absl/flags/internal/usage.h"
-#include "absl/flags/reflection.h"
-#include "absl/flags/usage.h"
-#include "absl/flags/usage_config.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "absl/strings/strip.h"
-#include "absl/synchronization/mutex.h"
-
-// --------------------------------------------------------------------
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-namespace {
-
-ABSL_CONST_INIT absl::Mutex processing_checks_guard(absl::kConstInit);
-
-ABSL_CONST_INIT bool flagfile_needs_processing
-    ABSL_GUARDED_BY(processing_checks_guard) = false;
-ABSL_CONST_INIT bool fromenv_needs_processing
-    ABSL_GUARDED_BY(processing_checks_guard) = false;
-ABSL_CONST_INIT bool tryfromenv_needs_processing
-    ABSL_GUARDED_BY(processing_checks_guard) = false;
-
-ABSL_CONST_INIT absl::Mutex specified_flags_guard(absl::kConstInit);
-ABSL_CONST_INIT std::vector<const CommandLineFlag*>* specified_flags
-    ABSL_GUARDED_BY(specified_flags_guard) = nullptr;
-
-struct SpecifiedFlagsCompare {
-  bool operator()(const CommandLineFlag* a, const CommandLineFlag* b) const {
-    return a->Name() < b->Name();
-  }
-  bool operator()(const CommandLineFlag* a, absl::string_view b) const {
-    return a->Name() < b;
-  }
-  bool operator()(absl::string_view a, const CommandLineFlag* b) const {
-    return a < b->Name();
-  }
-};
-
-}  // namespace
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-ABSL_FLAG(std::vector<std::string>, flagfile, {},
-          "comma-separated list of files to load flags from")
-    .OnUpdate([]() {
-      if (absl::GetFlag(FLAGS_flagfile).empty()) return;
-
-      absl::MutexLock l(&absl::flags_internal::processing_checks_guard);
-
-      // Setting this flag twice before it is handled most likely an internal
-      // error and should be reviewed by developers.
-      if (absl::flags_internal::flagfile_needs_processing) {
-        ABSL_INTERNAL_LOG(WARNING, "flagfile set twice before it is handled");
-      }
-
-      absl::flags_internal::flagfile_needs_processing = true;
-    });
-ABSL_FLAG(std::vector<std::string>, fromenv, {},
-          "comma-separated list of flags to set from the environment"
-          " [use 'export FLAGS_flag1=value']")
-    .OnUpdate([]() {
-      if (absl::GetFlag(FLAGS_fromenv).empty()) return;
-
-      absl::MutexLock l(&absl::flags_internal::processing_checks_guard);
-
-      // Setting this flag twice before it is handled most likely an internal
-      // error and should be reviewed by developers.
-      if (absl::flags_internal::fromenv_needs_processing) {
-        ABSL_INTERNAL_LOG(WARNING, "fromenv set twice before it is handled.");
-      }
-
-      absl::flags_internal::fromenv_needs_processing = true;
-    });
-ABSL_FLAG(std::vector<std::string>, tryfromenv, {},
-          "comma-separated list of flags to try to set from the environment if "
-          "present")
-    .OnUpdate([]() {
-      if (absl::GetFlag(FLAGS_tryfromenv).empty()) return;
-
-      absl::MutexLock l(&absl::flags_internal::processing_checks_guard);
-
-      // Setting this flag twice before it is handled most likely an internal
-      // error and should be reviewed by developers.
-      if (absl::flags_internal::tryfromenv_needs_processing) {
-        ABSL_INTERNAL_LOG(WARNING,
-                          "tryfromenv set twice before it is handled.");
-      }
-
-      absl::flags_internal::tryfromenv_needs_processing = true;
-    });
-
-ABSL_FLAG(std::vector<std::string>, undefok, {},
-          "comma-separated list of flag names that it is okay to specify "
-          "on the command line even if the program does not define a flag "
-          "with that name");
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-namespace {
-
-class ArgsList {
- public:
-  ArgsList() : next_arg_(0) {}
-  ArgsList(int argc, char* argv[]) : args_(argv, argv + argc), next_arg_(0) {}
-  explicit ArgsList(const std::vector<std::string>& args)
-      : args_(args), next_arg_(0) {}
-
-  // Returns success status: true if parsing successful, false otherwise.
-  bool ReadFromFlagfile(const std::string& flag_file_name);
-
-  int Size() const { return args_.size() - next_arg_; }
-  int FrontIndex() const { return next_arg_; }
-  absl::string_view Front() const { return args_[next_arg_]; }
-  void PopFront() { next_arg_++; }
-
- private:
-  std::vector<std::string> args_;
-  int next_arg_;
-};
-
-bool ArgsList::ReadFromFlagfile(const std::string& flag_file_name) {
-  std::ifstream flag_file(flag_file_name);
-
-  if (!flag_file) {
-    flags_internal::ReportUsageError(
-        absl::StrCat("Can't open flagfile ", flag_file_name), true);
-
-    return false;
-  }
-
-  // This argument represents fake argv[0], which should be present in all arg
-  // lists.
-  args_.push_back("");
-
-  std::string line;
-  bool success = true;
-
-  while (std::getline(flag_file, line)) {
-    absl::string_view stripped = absl::StripLeadingAsciiWhitespace(line);
-
-    if (stripped.empty() || stripped[0] == '#') {
-      // Comment or empty line; just ignore.
-      continue;
-    }
-
-    if (stripped[0] == '-') {
-      if (stripped == "--") {
-        flags_internal::ReportUsageError(
-            "Flagfile can't contain position arguments or --", true);
-
-        success = false;
-        break;
-      }
-
-      args_.push_back(std::string(stripped));
-      continue;
-    }
-
-    flags_internal::ReportUsageError(
-        absl::StrCat("Unexpected line in the flagfile ", flag_file_name, ": ",
-                     line),
-        true);
-
-    success = false;
-  }
-
-  return success;
-}
-
-// --------------------------------------------------------------------
-
-// Reads the environment variable with name `name` and stores results in
-// `value`. If variable is not present in environment returns false, otherwise
-// returns true.
-bool GetEnvVar(const char* var_name, std::string& var_value) {
-#ifdef _WIN32
-  char buf[1024];
-  auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf));
-  if (get_res >= sizeof(buf)) {
-    return false;
-  }
-
-  if (get_res == 0) {
-    return false;
-  }
-
-  var_value = std::string(buf, get_res);
-#else
-  const char* val = ::getenv(var_name);
-  if (val == nullptr) {
-    return false;
-  }
-
-  var_value = val;
-#endif
-
-  return true;
-}
-
-// --------------------------------------------------------------------
-
-// Returns:
-//  Flag name or empty if arg= --
-//  Flag value after = in --flag=value (empty if --foo)
-//  "Is empty value" status. True if arg= --foo=, false otherwise. This is
-//  required to separate --foo from --foo=.
-// For example:
-//      arg           return values
-//   "--foo=bar" -> {"foo", "bar", false}.
-//   "--foo"     -> {"foo", "", false}.
-//   "--foo="    -> {"foo", "", true}.
-std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue(
-    absl::string_view arg) {
-  // Allow -foo and --foo
-  absl::ConsumePrefix(&arg, "-");
-
-  if (arg.empty()) {
-    return std::make_tuple("", "", false);
-  }
-
-  auto equal_sign_pos = arg.find("=");
-
-  absl::string_view flag_name = arg.substr(0, equal_sign_pos);
-
-  absl::string_view value;
-  bool is_empty_value = false;
-
-  if (equal_sign_pos != absl::string_view::npos) {
-    value = arg.substr(equal_sign_pos + 1);
-    is_empty_value = value.empty();
-  }
-
-  return std::make_tuple(flag_name, value, is_empty_value);
-}
-
-// --------------------------------------------------------------------
-
-// Returns:
-//  found flag or nullptr
-//  is negative in case of --nofoo
-std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) {
-  CommandLineFlag* flag = absl::FindCommandLineFlag(flag_name);
-  bool is_negative = false;
-
-  if (!flag && absl::ConsumePrefix(&flag_name, "no")) {
-    flag = absl::FindCommandLineFlag(flag_name);
-    is_negative = true;
-  }
-
-  return std::make_tuple(flag, is_negative);
-}
-
-// --------------------------------------------------------------------
-
-// Verify that default values of typed flags must be convertible to string and
-// back.
-void CheckDefaultValuesParsingRoundtrip() {
-#ifndef NDEBUG
-  flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
-    if (flag.IsRetired()) return;
-
-#define ABSL_FLAGS_INTERNAL_IGNORE_TYPE(T, _) \
-  if (flag.IsOfType<T>()) return;
-
-    ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(ABSL_FLAGS_INTERNAL_IGNORE_TYPE)
-#undef ABSL_FLAGS_INTERNAL_IGNORE_TYPE
-
-    flags_internal::PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip(
-        flag);
-  });
-#endif
-}
-
-// --------------------------------------------------------------------
-
-// Returns success status, which is true if we successfully read all flag files,
-// in which case new ArgLists are appended to the input_args in a reverse order
-// of file names in the input flagfiles list. This order ensures that flags from
-// the first flagfile in the input list are processed before the second flagfile
-// etc.
-bool ReadFlagfiles(const std::vector<std::string>& flagfiles,
-                   std::vector<ArgsList>& input_args) {
-  bool success = true;
-  for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) {
-    ArgsList al;
-
-    if (al.ReadFromFlagfile(*it)) {
-      input_args.push_back(al);
-    } else {
-      success = false;
-    }
-  }
-
-  return success;
-}
-
-// Returns success status, which is true if were able to locate all environment
-// variables correctly or if fail_on_absent_in_env is false. The environment
-// variable names are expected to be of the form `FLAGS_<flag_name>`, where
-// `flag_name` is a string from the input flag_names list. If successful we
-// append a single ArgList at the end of the input_args.
-bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names,
-                      std::vector<ArgsList>& input_args,
-                      bool fail_on_absent_in_env) {
-  bool success = true;
-  std::vector<std::string> args;
-
-  // This argument represents fake argv[0], which should be present in all arg
-  // lists.
-  args.push_back("");
-
-  for (const auto& flag_name : flag_names) {
-    // Avoid infinite recursion.
-    if (flag_name == "fromenv" || flag_name == "tryfromenv") {
-      flags_internal::ReportUsageError(
-          absl::StrCat("Infinite recursion on flag ", flag_name), true);
-
-      success = false;
-      continue;
-    }
-
-    const std::string envname = absl::StrCat("FLAGS_", flag_name);
-    std::string envval;
-    if (!GetEnvVar(envname.c_str(), envval)) {
-      if (fail_on_absent_in_env) {
-        flags_internal::ReportUsageError(
-            absl::StrCat(envname, " not found in environment"), true);
-
-        success = false;
-      }
-
-      continue;
-    }
-
-    args.push_back(absl::StrCat("--", flag_name, "=", envval));
-  }
-
-  if (success) {
-    input_args.emplace_back(args);
-  }
-
-  return success;
-}
-
-// --------------------------------------------------------------------
-
-// Returns success status, which is true if were able to handle all generator
-// flags (flagfile, fromenv, tryfromemv) successfully.
-bool HandleGeneratorFlags(std::vector<ArgsList>& input_args,
-                          std::vector<std::string>& flagfile_value) {
-  bool success = true;
-
-  absl::MutexLock l(&flags_internal::processing_checks_guard);
-
-  // flagfile could have been set either on a command line or
-  // programmatically before invoking ParseCommandLine. Note that we do not
-  // actually process arguments specified in the flagfile, but instead
-  // create a secondary arguments list to be processed along with the rest
-  // of the comamnd line arguments. Since we always the process most recently
-  // created list of arguments first, this will result in flagfile argument
-  // being processed before any other argument in the command line. If
-  // FLAGS_flagfile contains more than one file name we create multiple new
-  // levels of arguments in a reverse order of file names. Thus we always
-  // process arguments from first file before arguments containing in a
-  // second file, etc. If flagfile contains another
-  // --flagfile inside of it, it will produce new level of arguments and
-  // processed before the rest of the flagfile. We are also collecting all
-  // flagfiles set on original command line. Unlike the rest of the flags,
-  // this flag can be set multiple times and is expected to be handled
-  // multiple times. We are collecting them all into a single list and set
-  // the value of FLAGS_flagfile to that value at the end of the parsing.
-  if (flags_internal::flagfile_needs_processing) {
-    auto flagfiles = absl::GetFlag(FLAGS_flagfile);
-
-    if (input_args.size() == 1) {
-      flagfile_value.insert(flagfile_value.end(), flagfiles.begin(),
-                            flagfiles.end());
-    }
-
-    success &= ReadFlagfiles(flagfiles, input_args);
-
-    flags_internal::flagfile_needs_processing = false;
-  }
-
-  // Similar to flagfile fromenv/tryfromemv can be set both
-  // programmatically and at runtime on a command line. Unlike flagfile these
-  // can't be recursive.
-  if (flags_internal::fromenv_needs_processing) {
-    auto flags_list = absl::GetFlag(FLAGS_fromenv);
-
-    success &= ReadFlagsFromEnv(flags_list, input_args, true);
-
-    flags_internal::fromenv_needs_processing = false;
-  }
-
-  if (flags_internal::tryfromenv_needs_processing) {
-    auto flags_list = absl::GetFlag(FLAGS_tryfromenv);
-
-    success &= ReadFlagsFromEnv(flags_list, input_args, false);
-
-    flags_internal::tryfromenv_needs_processing = false;
-  }
-
-  return success;
-}
-
-// --------------------------------------------------------------------
-
-void ResetGeneratorFlags(const std::vector<std::string>& flagfile_value) {
-  // Setting flagfile to the value which collates all the values set on a
-  // command line and programmatically. So if command line looked like
-  // --flagfile=f1 --flagfile=f2 the final value of the FLAGS_flagfile flag is
-  // going to be {"f1", "f2"}
-  if (!flagfile_value.empty()) {
-    absl::SetFlag(&FLAGS_flagfile, flagfile_value);
-    absl::MutexLock l(&flags_internal::processing_checks_guard);
-    flags_internal::flagfile_needs_processing = false;
-  }
-
-  // fromenv/tryfromenv are set to <undefined> value.
-  if (!absl::GetFlag(FLAGS_fromenv).empty()) {
-    absl::SetFlag(&FLAGS_fromenv, {});
-  }
-  if (!absl::GetFlag(FLAGS_tryfromenv).empty()) {
-    absl::SetFlag(&FLAGS_tryfromenv, {});
-  }
-
-  absl::MutexLock l(&flags_internal::processing_checks_guard);
-  flags_internal::fromenv_needs_processing = false;
-  flags_internal::tryfromenv_needs_processing = false;
-}
-
-// --------------------------------------------------------------------
-
-// Returns:
-//  success status
-//  deduced value
-// We are also mutating curr_list in case if we need to get a hold of next
-// argument in the input.
-std::tuple<bool, absl::string_view> DeduceFlagValue(const CommandLineFlag& flag,
-                                                    absl::string_view value,
-                                                    bool is_negative,
-                                                    bool is_empty_value,
-                                                    ArgsList* curr_list) {
-  // Value is either an argument suffix after `=` in "--foo=<value>"
-  // or separate argument in case of "--foo" "<value>".
-
-  // boolean flags have these forms:
-  //   --foo
-  //   --nofoo
-  //   --foo=true
-  //   --foo=false
-  //   --nofoo=<value> is not supported
-  //   --foo <value> is not supported
-
-  // non boolean flags have these forms:
-  // --foo=<value>
-  // --foo <value>
-  // --nofoo is not supported
-
-  if (flag.IsOfType<bool>()) {
-    if (value.empty()) {
-      if (is_empty_value) {
-        // "--bool_flag=" case
-        flags_internal::ReportUsageError(
-            absl::StrCat(
-                "Missing the value after assignment for the boolean flag '",
-                flag.Name(), "'"),
-            true);
-        return std::make_tuple(false, "");
-      }
-
-      // "--bool_flag" case
-      value = is_negative ? "0" : "1";
-    } else if (is_negative) {
-      // "--nobool_flag=Y" case
-      flags_internal::ReportUsageError(
-          absl::StrCat("Negative form with assignment is not valid for the "
-                       "boolean flag '",
-                       flag.Name(), "'"),
-          true);
-      return std::make_tuple(false, "");
-    }
-  } else if (is_negative) {
-    // "--noint_flag=1" case
-    flags_internal::ReportUsageError(
-        absl::StrCat("Negative form is not valid for the flag '", flag.Name(),
-                     "'"),
-        true);
-    return std::make_tuple(false, "");
-  } else if (value.empty() && (!is_empty_value)) {
-    if (curr_list->Size() == 1) {
-      // "--int_flag" case
-      flags_internal::ReportUsageError(
-          absl::StrCat("Missing the value for the flag '", flag.Name(), "'"),
-          true);
-      return std::make_tuple(false, "");
-    }
-
-    // "--int_flag" "10" case
-    curr_list->PopFront();
-    value = curr_list->Front();
-
-    // Heuristic to detect the case where someone treats a string arg
-    // like a bool or just forgets to pass a value:
-    // --my_string_var --foo=bar
-    // We look for a flag of string type, whose value begins with a
-    // dash and corresponds to known flag or standalone --.
-    if (!value.empty() && value[0] == '-' && flag.IsOfType<std::string>()) {
-      auto maybe_flag_name = std::get<0>(SplitNameAndValue(value.substr(1)));
-
-      if (maybe_flag_name.empty() ||
-          std::get<0>(LocateFlag(maybe_flag_name)) != nullptr) {
-        // "--string_flag" "--known_flag" case
-        ABSL_INTERNAL_LOG(
-            WARNING,
-            absl::StrCat("Did you really mean to set flag '", flag.Name(),
-                         "' to the value '", value, "'?"));
-      }
-    }
-  }
-
-  return std::make_tuple(true, value);
-}
-
-// --------------------------------------------------------------------
-
-bool CanIgnoreUndefinedFlag(absl::string_view flag_name) {
-  auto undefok = absl::GetFlag(FLAGS_undefok);
-  if (std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) {
-    return true;
-  }
-
-  if (absl::ConsumePrefix(&flag_name, "no") &&
-      std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) {
-    return true;
-  }
-
-  return false;
-}
-
-}  // namespace
-
-// --------------------------------------------------------------------
-
-bool WasPresentOnCommandLine(absl::string_view flag_name) {
-  absl::MutexLock l(&specified_flags_guard);
-  ABSL_INTERNAL_CHECK(specified_flags != nullptr,
-                      "ParseCommandLine is not invoked yet");
-
-  return std::binary_search(specified_flags->begin(), specified_flags->end(),
-                            flag_name, SpecifiedFlagsCompare{});
-}
-
-// --------------------------------------------------------------------
-
-std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
-                                        ArgvListAction arg_list_act,
-                                        UsageFlagsAction usage_flag_act,
-                                        OnUndefinedFlag on_undef_flag) {
-  ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");
-
-  // Once parsing has started we will not have more flag registrations.
-  // If we did, they would be missing during parsing, which is a problem on
-  // itself.
-  flags_internal::FinalizeRegistry();
-
-  // This routine does not return anything since we abort on failure.
-  CheckDefaultValuesParsingRoundtrip();
-
-  std::vector<std::string> flagfile_value;
-
-  std::vector<ArgsList> input_args;
-  input_args.push_back(ArgsList(argc, argv));
-
-  std::vector<char*> output_args;
-  std::vector<char*> positional_args;
-  output_args.reserve(argc);
-
-  // This is the list of undefined flags. The element of the list is the pair
-  // consisting of boolean indicating if flag came from command line (vs from
-  // some flag file we've read) and flag name.
-  // TODO(rogeeff): Eliminate the first element in the pair after cleanup.
-  std::vector<std::pair<bool, std::string>> undefined_flag_names;
-
-  // Set program invocation name if it is not set before.
-  if (ProgramInvocationName() == "UNKNOWN") {
-    flags_internal::SetProgramInvocationName(argv[0]);
-  }
-  output_args.push_back(argv[0]);
-
-  absl::MutexLock l(&specified_flags_guard);
-  if (specified_flags == nullptr) {
-    specified_flags = new std::vector<const CommandLineFlag*>;
-  } else {
-    specified_flags->clear();
-  }
-
-  // Iterate through the list of the input arguments. First level are arguments
-  // originated from argc/argv. Following levels are arguments originated from
-  // recursive parsing of flagfile(s).
-  bool success = true;
-  while (!input_args.empty()) {
-    // 10. First we process the built-in generator flags.
-    success &= HandleGeneratorFlags(input_args, flagfile_value);
-
-    // 30. Select top-most (most recent) arguments list. If it is empty drop it
-    // and re-try.
-    ArgsList& curr_list = input_args.back();
-
-    curr_list.PopFront();
-
-    if (curr_list.Size() == 0) {
-      input_args.pop_back();
-      continue;
-    }
-
-    // 40. Pick up the front remaining argument in the current list. If current
-    // stack of argument lists contains only one element - we are processing an
-    // argument from the original argv.
-    absl::string_view arg(curr_list.Front());
-    bool arg_from_argv = input_args.size() == 1;
-
-    // 50. If argument does not start with - or is just "-" - this is
-    // positional argument.
-    if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) {
-      ABSL_INTERNAL_CHECK(arg_from_argv,
-                          "Flagfile cannot contain positional argument");
-
-      positional_args.push_back(argv[curr_list.FrontIndex()]);
-      continue;
-    }
-
-    if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs)) {
-      output_args.push_back(argv[curr_list.FrontIndex()]);
-    }
-
-    // 60. Split the current argument on '=' to figure out the argument
-    // name and value. If flag name is empty it means we've got "--". value
-    // can be empty either if there were no '=' in argument string at all or
-    // an argument looked like "--foo=". In a latter case is_empty_value is
-    // true.
-    absl::string_view flag_name;
-    absl::string_view value;
-    bool is_empty_value = false;
-
-    std::tie(flag_name, value, is_empty_value) = SplitNameAndValue(arg);
-
-    // 70. "--" alone means what it does for GNU: stop flags parsing. We do
-    // not support positional arguments in flagfiles, so we just drop them.
-    if (flag_name.empty()) {
-      ABSL_INTERNAL_CHECK(arg_from_argv,
-                          "Flagfile cannot contain positional argument");
-
-      curr_list.PopFront();
-      break;
-    }
-
-    // 80. Locate the flag based on flag name. Handle both --foo and --nofoo
-    CommandLineFlag* flag = nullptr;
-    bool is_negative = false;
-    std::tie(flag, is_negative) = LocateFlag(flag_name);
-
-    if (flag == nullptr) {
-      // Usage flags are not modeled as Abseil flags. Locate them separately.
-      if (flags_internal::DeduceUsageFlags(flag_name, value)) {
-        continue;
-      }
-
-      if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) {
-        undefined_flag_names.emplace_back(arg_from_argv,
-                                          std::string(flag_name));
-      }
-      continue;
-    }
-
-    // 90. Deduce flag's value (from this or next argument)
-    auto curr_index = curr_list.FrontIndex();
-    bool value_success = true;
-    std::tie(value_success, value) =
-        DeduceFlagValue(*flag, value, is_negative, is_empty_value, &curr_list);
-    success &= value_success;
-
-    // If above call consumed an argument, it was a standalone value
-    if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs) &&
-        (curr_index != curr_list.FrontIndex())) {
-      output_args.push_back(argv[curr_list.FrontIndex()]);
-    }
-
-    // 100. Set the located flag to a new new value, unless it is retired.
-    // Setting retired flag fails, but we ignoring it here while also reporting
-    // access to retired flag.
-    std::string error;
-    if (!flags_internal::PrivateHandleAccessor::ParseFrom(
-            *flag, value, SET_FLAGS_VALUE, kCommandLine, error)) {
-      if (flag->IsRetired()) continue;
-
-      flags_internal::ReportUsageError(error, true);
-      success = false;
-    } else {
-      specified_flags->push_back(flag);
-    }
-  }
-
-  for (const auto& flag_name : undefined_flag_names) {
-    if (CanIgnoreUndefinedFlag(flag_name.second)) continue;
-
-    flags_internal::ReportUsageError(
-        absl::StrCat("Unknown command line flag '", flag_name.second, "'"),
-        true);
-
-    success = false;
-  }
-
-#if ABSL_FLAGS_STRIP_NAMES
-  if (!success) {
-    flags_internal::ReportUsageError(
-        "NOTE: command line flags are disabled in this build", true);
-  }
-#endif
-
-  if (!success) {
-    flags_internal::HandleUsageFlags(std::cout,
-                                     ProgramUsageMessage());
-    std::exit(1);
-  }
-
-  if (usage_flag_act == UsageFlagsAction::kHandleUsage) {
-    int exit_code = flags_internal::HandleUsageFlags(
-        std::cout, ProgramUsageMessage());
-
-    if (exit_code != -1) {
-      std::exit(exit_code);
-    }
-  }
-
-  ResetGeneratorFlags(flagfile_value);
-
-  // Reinstate positional args which were intermixed with flags in the arguments
-  // list.
-  for (auto arg : positional_args) {
-    output_args.push_back(arg);
-  }
-
-  // All the remaining arguments are positional.
-  if (!input_args.empty()) {
-    for (int arg_index = input_args.back().FrontIndex(); arg_index < argc;
-         ++arg_index) {
-      output_args.push_back(argv[arg_index]);
-    }
-  }
-
-  // Trim and sort the vector.
-  specified_flags->shrink_to_fit();
-  std::sort(specified_flags->begin(), specified_flags->end(),
-            SpecifiedFlagsCompare{});
-  return output_args;
-}
-
-}  // namespace flags_internal
-
-// --------------------------------------------------------------------
-
-std::vector<char*> ParseCommandLine(int argc, char* argv[]) {
-  return flags_internal::ParseCommandLineImpl(
-      argc, argv, flags_internal::ArgvListAction::kRemoveParsedArgs,
-      flags_internal::UsageFlagsAction::kHandleUsage,
-      flags_internal::OnUndefinedFlag::kAbortIfUndefined);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/parse.h b/third_party/abseil_cpp/absl/flags/parse.h
deleted file mode 100644
index 929de2cb40..0000000000
--- a/third_party/abseil_cpp/absl/flags/parse.h
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: parse.h
-// -----------------------------------------------------------------------------
-//
-// This file defines the main parsing function for Abseil flags:
-// `absl::ParseCommandLine()`.
-
-#ifndef ABSL_FLAGS_PARSE_H_
-#define ABSL_FLAGS_PARSE_H_
-
-#include <vector>
-
-#include "absl/base/config.h"
-#include "absl/flags/internal/parse.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// ParseCommandLine()
-//
-// Parses the set of command-line arguments passed in the `argc` (argument
-// count) and `argv[]` (argument vector) parameters from `main()`, assigning
-// values to any defined Abseil flags. (Any arguments passed after the
-// flag-terminating delimiter (`--`) are treated as positional arguments and
-// ignored.)
-//
-// Any command-line flags (and arguments to those flags) are parsed into Abseil
-// Flag values, if those flags are defined. Any undefined flags will either
-// return an error, or be ignored if that flag is designated using `undefok` to
-// indicate "undefined is OK."
-//
-// Any command-line positional arguments not part of any command-line flag (or
-// arguments to a flag) are returned in a vector, with the program invocation
-// name at position 0 of that vector. (Note that this includes positional
-// arguments after the flag-terminating delimiter `--`.)
-//
-// After all flags and flag arguments are parsed, this function looks for any
-// built-in usage flags (e.g. `--help`), and if any were specified, it reports
-// help messages and then exits the program.
-std::vector<char*> ParseCommandLine(int argc, char* argv[]);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_PARSE_H_
diff --git a/third_party/abseil_cpp/absl/flags/parse_test.cc b/third_party/abseil_cpp/absl/flags/parse_test.cc
deleted file mode 100644
index 41bc0bc6b3..0000000000
--- a/third_party/abseil_cpp/absl/flags/parse_test.cc
+++ /dev/null
@@ -1,929 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/parse.h"
-
-#include <stdlib.h>
-
-#include <fstream>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/scoped_set_env.h"
-#include "absl/flags/declare.h"
-#include "absl/flags/flag.h"
-#include "absl/flags/internal/parse.h"
-#include "absl/flags/internal/usage.h"
-#include "absl/flags/reflection.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "absl/strings/substitute.h"
-#include "absl/types/span.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-namespace {
-
-using absl::base_internal::ScopedSetEnv;
-
-struct UDT {
-  UDT() = default;
-  UDT(const UDT&) = default;
-  UDT(int v) : value(v) {}  // NOLINT
-
-  int value;
-};
-
-bool AbslParseFlag(absl::string_view in, UDT* udt, std::string* err) {
-  if (in == "A") {
-    udt->value = 1;
-    return true;
-  }
-  if (in == "AAA") {
-    udt->value = 10;
-    return true;
-  }
-
-  *err = "Use values A, AAA instead";
-  return false;
-}
-std::string AbslUnparseFlag(const UDT& udt) {
-  return udt.value == 1 ? "A" : "AAA";
-}
-
-std::string GetTestTmpDirEnvVar(const char* const env_var_name) {
-#ifdef _WIN32
-  char buf[MAX_PATH];
-  auto get_res = GetEnvironmentVariableA(env_var_name, buf, sizeof(buf));
-  if (get_res >= sizeof(buf) || get_res == 0) {
-    return "";
-  }
-
-  return std::string(buf, get_res);
-#else
-  const char* val = ::getenv(env_var_name);
-  if (val == nullptr) {
-    return "";
-  }
-
-  return val;
-#endif
-}
-
-const std::string& GetTestTempDir() {
-  static std::string* temp_dir_name = []() -> std::string* {
-    std::string* res = new std::string(GetTestTmpDirEnvVar("TEST_TMPDIR"));
-
-    if (res->empty()) {
-      *res = GetTestTmpDirEnvVar("TMPDIR");
-    }
-
-    if (res->empty()) {
-#ifdef _WIN32
-      char temp_path_buffer[MAX_PATH];
-
-      auto len = GetTempPathA(MAX_PATH, temp_path_buffer);
-      if (len < MAX_PATH && len != 0) {
-        std::string temp_dir_name = temp_path_buffer;
-        if (!absl::EndsWith(temp_dir_name, "\\")) {
-          temp_dir_name.push_back('\\');
-        }
-        absl::StrAppend(&temp_dir_name, "parse_test.", GetCurrentProcessId());
-        if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) {
-          *res = temp_dir_name;
-        }
-      }
-#else
-      char temp_dir_template[] = "/tmp/parse_test.XXXXXX";
-      if (auto* unique_name = ::mkdtemp(temp_dir_template)) {
-        *res = unique_name;
-      }
-#endif
-    }
-
-    if (res->empty()) {
-      ABSL_INTERNAL_LOG(FATAL,
-                        "Failed to make temporary directory for data files");
-    }
-
-#ifdef _WIN32
-    *res += "\\";
-#else
-    *res += "/";
-#endif
-
-    return res;
-  }();
-
-  return *temp_dir_name;
-}
-
-struct FlagfileData {
-  const absl::string_view file_name;
-  const absl::Span<const char* const> file_lines;
-};
-
-// clang-format off
-constexpr const char* const ff1_data[] = {
-    "# comment    ",
-    "  # comment  ",
-    "",
-    "     ",
-    "--int_flag=-1",
-    "  --string_flag=q2w2  ",
-    "  ##   ",
-    "  --double_flag=0.1",
-    "--bool_flag=Y  "
-};
-
-constexpr const char* const ff2_data[] = {
-    "# Setting legacy flag",
-    "--legacy_int=1111",
-    "--legacy_bool",
-    "--nobool_flag",
-    "--legacy_str=aqsw",
-    "--int_flag=100",
-    "   ## ============="
-};
-// clang-format on
-
-// Builds flagfile flag in the flagfile_flag buffer and returns it. This
-// function also creates a temporary flagfile based on FlagfileData input.
-// We create a flagfile in a temporary directory with the name specified in
-// FlagfileData and populate it with lines specifed in FlagfileData. If $0 is
-// referenced in any of the lines in FlagfileData they are replaced with
-// temporary directory location. This way we can test inclusion of one flagfile
-// from another flagfile.
-const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd,
-                            std::string& flagfile_flag) {
-  flagfile_flag = "--flagfile=";
-  absl::string_view separator;
-  for (const auto& flagfile_data : ffd) {
-    std::string flagfile_name =
-        absl::StrCat(GetTestTempDir(), flagfile_data.file_name);
-
-    std::ofstream flagfile_out(flagfile_name);
-    for (auto line : flagfile_data.file_lines) {
-      flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n";
-    }
-
-    absl::StrAppend(&flagfile_flag, separator, flagfile_name);
-    separator = ",";
-  }
-
-  return flagfile_flag.c_str();
-}
-
-}  // namespace
-
-ABSL_FLAG(int, int_flag, 1, "");
-ABSL_FLAG(double, double_flag, 1.1, "");
-ABSL_FLAG(std::string, string_flag, "a", "");
-ABSL_FLAG(bool, bool_flag, false, "");
-ABSL_FLAG(UDT, udt_flag, -1, "");
-ABSL_RETIRED_FLAG(int, legacy_int, 1, "");
-ABSL_RETIRED_FLAG(bool, legacy_bool, false, "");
-ABSL_RETIRED_FLAG(std::string, legacy_str, "l", "");
-
-namespace {
-
-namespace flags = absl::flags_internal;
-using testing::ElementsAreArray;
-
-class ParseTest : public testing::Test {
- public:
-  ~ParseTest() override { flags::SetFlagsHelpMode(flags::HelpMode::kNone); }
-
- private:
-  absl::FlagSaver flag_saver_;
-};
-
-// --------------------------------------------------------------------
-
-template <int N>
-std::vector<char*> InvokeParse(const char* (&in_argv)[N]) {
-  return absl::ParseCommandLine(N, const_cast<char**>(in_argv));
-}
-
-// --------------------------------------------------------------------
-
-template <int N>
-void TestParse(const char* (&in_argv)[N], int int_flag_value,
-               double double_flag_val, absl::string_view string_flag_val,
-               bool bool_flag_val, int exp_position_args = 0) {
-  auto out_args = InvokeParse(in_argv);
-
-  EXPECT_EQ(out_args.size(), 1 + exp_position_args);
-  EXPECT_STREQ(out_args[0], "testbin");
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), int_flag_value);
-  EXPECT_NEAR(absl::GetFlag(FLAGS_double_flag), double_flag_val, 0.0001);
-  EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), string_flag_val);
-  EXPECT_EQ(absl::GetFlag(FLAGS_bool_flag), bool_flag_val);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestEmptyArgv) {
-  const char* in_argv[] = {"testbin"};
-
-  auto out_args = InvokeParse(in_argv);
-
-  EXPECT_EQ(out_args.size(), 1);
-  EXPECT_STREQ(out_args[0], "testbin");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestValidIntArg) {
-  const char* in_args1[] = {
-      "testbin",
-      "--int_flag=10",
-  };
-  TestParse(in_args1, 10, 1.1, "a", false);
-
-  const char* in_args2[] = {
-      "testbin",
-      "-int_flag=020",
-  };
-  TestParse(in_args2, 20, 1.1, "a", false);
-
-  const char* in_args3[] = {
-      "testbin",
-      "--int_flag",
-      "-30",
-  };
-  TestParse(in_args3, -30, 1.1, "a", false);
-
-  const char* in_args4[] = {
-      "testbin",
-      "-int_flag",
-      "0x21",
-  };
-  TestParse(in_args4, 33, 1.1, "a", false);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestValidDoubleArg) {
-  const char* in_args1[] = {
-      "testbin",
-      "--double_flag=2.3",
-  };
-  TestParse(in_args1, 1, 2.3, "a", false);
-
-  const char* in_args2[] = {
-      "testbin",
-      "--double_flag=0x1.2",
-  };
-  TestParse(in_args2, 1, 1.125, "a", false);
-
-  const char* in_args3[] = {
-      "testbin",
-      "--double_flag",
-      "99.7",
-  };
-  TestParse(in_args3, 1, 99.7, "a", false);
-
-  const char* in_args4[] = {
-      "testbin",
-      "--double_flag",
-      "0x20.1",
-  };
-  TestParse(in_args4, 1, 32.0625, "a", false);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestValidStringArg) {
-  const char* in_args1[] = {
-      "testbin",
-      "--string_flag=aqswde",
-  };
-  TestParse(in_args1, 1, 1.1, "aqswde", false);
-
-  const char* in_args2[] = {
-      "testbin",
-      "-string_flag=a=b=c",
-  };
-  TestParse(in_args2, 1, 1.1, "a=b=c", false);
-
-  const char* in_args3[] = {
-      "testbin",
-      "--string_flag",
-      "zaxscd",
-  };
-  TestParse(in_args3, 1, 1.1, "zaxscd", false);
-
-  const char* in_args4[] = {
-      "testbin",
-      "-string_flag",
-      "--int_flag",
-  };
-  TestParse(in_args4, 1, 1.1, "--int_flag", false);
-
-  const char* in_args5[] = {
-      "testbin",
-      "--string_flag",
-      "--no_a_flag=11",
-  };
-  TestParse(in_args5, 1, 1.1, "--no_a_flag=11", false);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestValidBoolArg) {
-  const char* in_args1[] = {
-      "testbin",
-      "--bool_flag",
-  };
-  TestParse(in_args1, 1, 1.1, "a", true);
-
-  const char* in_args2[] = {
-      "testbin",
-      "--nobool_flag",
-  };
-  TestParse(in_args2, 1, 1.1, "a", false);
-
-  const char* in_args3[] = {
-      "testbin",
-      "--bool_flag=true",
-  };
-  TestParse(in_args3, 1, 1.1, "a", true);
-
-  const char* in_args4[] = {
-      "testbin",
-      "-bool_flag=false",
-  };
-  TestParse(in_args4, 1, 1.1, "a", false);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestValidUDTArg) {
-  const char* in_args1[] = {
-      "testbin",
-      "--udt_flag=A",
-  };
-  InvokeParse(in_args1);
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 1);
-
-  const char* in_args2[] = {"testbin", "--udt_flag", "AAA"};
-  InvokeParse(in_args2);
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 10);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestValidMultipleArg) {
-  const char* in_args1[] = {
-      "testbin",           "--bool_flag",       "--int_flag=2",
-      "--double_flag=0.1", "--string_flag=asd",
-  };
-  TestParse(in_args1, 2, 0.1, "asd", true);
-
-  const char* in_args2[] = {
-      "testbin", "--string_flag=", "--nobool_flag", "--int_flag",
-      "-011",    "--double_flag",  "-1e-2",
-  };
-  TestParse(in_args2, -11, -0.01, "", false);
-
-  const char* in_args3[] = {
-      "testbin",          "--int_flag",         "-0", "--string_flag", "\"\"",
-      "--bool_flag=true", "--double_flag=1e18",
-  };
-  TestParse(in_args3, 0, 1e18, "\"\"", true);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestPositionalArgs) {
-  const char* in_args1[] = {
-      "testbin",
-      "p1",
-      "p2",
-  };
-  TestParse(in_args1, 1, 1.1, "a", false, 2);
-
-  auto out_args1 = InvokeParse(in_args1);
-
-  EXPECT_STREQ(out_args1[1], "p1");
-  EXPECT_STREQ(out_args1[2], "p2");
-
-  const char* in_args2[] = {
-      "testbin",
-      "--int_flag=2",
-      "p1",
-  };
-  TestParse(in_args2, 2, 1.1, "a", false, 1);
-
-  auto out_args2 = InvokeParse(in_args2);
-
-  EXPECT_STREQ(out_args2[1], "p1");
-
-  const char* in_args3[] = {"testbin", "p1",          "--int_flag=3",
-                            "p2",      "--bool_flag", "true"};
-  TestParse(in_args3, 3, 1.1, "a", true, 3);
-
-  auto out_args3 = InvokeParse(in_args3);
-
-  EXPECT_STREQ(out_args3[1], "p1");
-  EXPECT_STREQ(out_args3[2], "p2");
-  EXPECT_STREQ(out_args3[3], "true");
-
-  const char* in_args4[] = {
-      "testbin",
-      "--",
-      "p1",
-      "p2",
-  };
-  TestParse(in_args4, 3, 1.1, "a", true, 2);
-
-  auto out_args4 = InvokeParse(in_args4);
-
-  EXPECT_STREQ(out_args4[1], "p1");
-  EXPECT_STREQ(out_args4[2], "p2");
-
-  const char* in_args5[] = {
-      "testbin", "p1", "--int_flag=4", "--", "--bool_flag", "false", "p2",
-  };
-  TestParse(in_args5, 4, 1.1, "a", true, 4);
-
-  auto out_args5 = InvokeParse(in_args5);
-
-  EXPECT_STREQ(out_args5[1], "p1");
-  EXPECT_STREQ(out_args5[2], "--bool_flag");
-  EXPECT_STREQ(out_args5[3], "false");
-  EXPECT_STREQ(out_args5[4], "p2");
-}
-
-// --------------------------------------------------------------------
-
-using ParseDeathTest = ParseTest;
-
-TEST_F(ParseDeathTest, TestUndefinedArg) {
-  const char* in_args1[] = {
-      "testbin",
-      "--undefined_flag",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
-                            "Unknown command line flag 'undefined_flag'");
-
-  const char* in_args2[] = {
-      "testbin",
-      "--noprefixed_flag",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
-                            "Unknown command line flag 'noprefixed_flag'");
-
-  const char* in_args3[] = {
-      "testbin",
-      "--Int_flag=1",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3),
-                            "Unknown command line flag 'Int_flag'");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) {
-  const char* in_args1[] = {
-      "testbin",
-      "--bool_flag=",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(
-      InvokeParse(in_args1),
-      "Missing the value after assignment for the boolean flag 'bool_flag'");
-
-  const char* in_args2[] = {
-      "testbin",
-      "--nobool_flag=true",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
-               "Negative form with assignment is not valid for the boolean "
-               "flag 'bool_flag'");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseDeathTest, TestInvalidNonBoolFlagFormat) {
-  const char* in_args1[] = {
-      "testbin",
-      "--nostring_flag",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
-               "Negative form is not valid for the flag 'string_flag'");
-
-  const char* in_args2[] = {
-      "testbin",
-      "--int_flag",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
-               "Missing the value for the flag 'int_flag'");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) {
-  const char* in_args1[] = {
-      "testbin",
-      "--udt_flag=1",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
-               "Illegal value '1' specified for flag 'udt_flag'; Use values A, "
-               "AAA instead");
-
-  const char* in_args2[] = {
-      "testbin",
-      "--udt_flag",
-      "AA",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
-               "Illegal value 'AA' specified for flag 'udt_flag'; Use values "
-               "A, AAA instead");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestLegacyFlags) {
-  const char* in_args1[] = {
-      "testbin",
-      "--legacy_int=11",
-  };
-  TestParse(in_args1, 1, 1.1, "a", false);
-
-  const char* in_args2[] = {
-      "testbin",
-      "--legacy_bool",
-  };
-  TestParse(in_args2, 1, 1.1, "a", false);
-
-  const char* in_args3[] = {
-      "testbin",       "--legacy_int", "22",           "--int_flag=2",
-      "--legacy_bool", "true",         "--legacy_str", "--string_flag=qwe",
-  };
-  TestParse(in_args3, 2, 1.1, "a", false, 1);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestSimpleValidFlagfile) {
-  std::string flagfile_flag;
-
-  const char* in_args1[] = {
-      "testbin",
-      GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
-                      flagfile_flag),
-  };
-  TestParse(in_args1, -1, 0.1, "q2w2  ", true);
-
-  const char* in_args2[] = {
-      "testbin",
-      GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}},
-                      flagfile_flag),
-  };
-  TestParse(in_args2, 100, 0.1, "q2w2  ", false);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestValidMultiFlagfile) {
-  std::string flagfile_flag;
-
-  const char* in_args1[] = {
-      "testbin",
-      GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)},
-                       {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
-                      flagfile_flag),
-  };
-  TestParse(in_args1, -1, 0.1, "q2w2  ", true);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestFlagfileMixedWithRegularFlags) {
-  std::string flagfile_flag;
-
-  const char* in_args1[] = {
-      "testbin", "--int_flag=3",
-      GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
-                      flagfile_flag),
-      "-double_flag=0.2"};
-  TestParse(in_args1, -1, 0.2, "q2w2  ", true);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestFlagfileInFlagfile) {
-  std::string flagfile_flag;
-
-  constexpr const char* const ff3_data[] = {
-      "--flagfile=$0/parse_test.ff1",
-      "--flagfile=$0/parse_test.ff2",
-  };
-
-  GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)},
-                   {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
-                      flagfile_flag);
-
-  const char* in_args1[] = {
-      "testbin",
-      GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}},
-                      flagfile_flag),
-  };
-  TestParse(in_args1, 100, 0.1, "q2w2  ", false);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
-  std::string flagfile_flag;
-
-  constexpr const char* const ff4_data[] = {
-    "--unknown_flag=10"
-  };
-
-  const char* in_args1[] = {
-      "testbin",
-      GetFlagfileFlag({{"parse_test.ff4",
-                        absl::MakeConstSpan(ff4_data)}}, flagfile_flag),
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
-               "Unknown command line flag 'unknown_flag'");
-
-  constexpr const char* const ff5_data[] = {
-    "--int_flag 10",
-  };
-
-  const char* in_args2[] = {
-      "testbin",
-      GetFlagfileFlag({{"parse_test.ff5",
-                        absl::MakeConstSpan(ff5_data)}}, flagfile_flag),
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
-               "Unknown command line flag 'int_flag 10'");
-
-  constexpr const char* const ff6_data[] = {
-      "--int_flag=10", "--", "arg1", "arg2", "arg3",
-  };
-
-  const char* in_args3[] = {
-      "testbin",
-      GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}},
-                      flagfile_flag),
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3),
-               "Flagfile can't contain position arguments or --");
-
-  const char* in_args4[] = {
-      "testbin",
-      "--flagfile=invalid_flag_file",
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args4),
-                            "Can't open flagfile invalid_flag_file");
-
-  constexpr const char* const ff7_data[] = {
-      "--int_flag=10",
-      "*bin*",
-      "--str_flag=aqsw",
-  };
-
-  const char* in_args5[] = {
-      "testbin",
-      GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}},
-                      flagfile_flag),
-  };
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args5),
-               "Unexpected line in the flagfile .*: \\*bin\\*");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestReadingRequiredFlagsFromEnv) {
-  const char* in_args1[] = {"testbin",
-                            "--fromenv=int_flag,bool_flag,string_flag"};
-
-  ScopedSetEnv set_int_flag("FLAGS_int_flag", "33");
-  ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "True");
-  ScopedSetEnv set_string_flag("FLAGS_string_flag", "AQ12");
-
-  TestParse(in_args1, 33, 1.1, "AQ12", true);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseDeathTest, TestReadingUnsetRequiredFlagsFromEnv) {
-  const char* in_args1[] = {"testbin", "--fromenv=int_flag"};
-
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
-               "FLAGS_int_flag not found in environment");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseDeathTest, TestRecursiveFlagsFromEnv) {
-  const char* in_args1[] = {"testbin", "--fromenv=tryfromenv"};
-
-  ScopedSetEnv set_tryfromenv("FLAGS_tryfromenv", "int_flag");
-
-  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
-                            "Infinite recursion on flag tryfromenv");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestReadingOptionalFlagsFromEnv) {
-  const char* in_args1[] = {
-      "testbin", "--tryfromenv=int_flag,bool_flag,string_flag,other_flag"};
-
-  ScopedSetEnv set_int_flag("FLAGS_int_flag", "17");
-  ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "Y");
-
-  TestParse(in_args1, 17, 1.1, "a", true);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) {
-  const char* in_args1[] = {
-      "testbin",
-      "--bool_flag=T",
-      "--tryfromenv=int_flag,bool_flag",
-      "--int_flag=-21",
-  };
-
-  ScopedSetEnv set_int_flag("FLAGS_int_flag", "-15");
-  ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "F");
-
-  TestParse(in_args1, -21, 1.1, "a", false);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestKeepParsedArgs) {
-  const char* in_args1[] = {
-      "testbin",        "arg1", "--bool_flag",
-      "--int_flag=211", "arg2", "--double_flag=1.1",
-      "--string_flag",  "asd",  "--",
-      "arg3",           "arg4",
-  };
-
-  auto out_args1 = InvokeParse(in_args1);
-
-  EXPECT_THAT(
-      out_args1,
-      ElementsAreArray({absl::string_view("testbin"), absl::string_view("arg1"),
-                        absl::string_view("arg2"), absl::string_view("arg3"),
-                        absl::string_view("arg4")}));
-
-  auto out_args2 = flags::ParseCommandLineImpl(
-      11, const_cast<char**>(in_args1), flags::ArgvListAction::kKeepParsedArgs,
-      flags::UsageFlagsAction::kHandleUsage,
-      flags::OnUndefinedFlag::kAbortIfUndefined);
-
-  EXPECT_THAT(
-      out_args2,
-      ElementsAreArray({absl::string_view("testbin"),
-                        absl::string_view("--bool_flag"),
-                        absl::string_view("--int_flag=211"),
-                        absl::string_view("--double_flag=1.1"),
-                        absl::string_view("--string_flag"),
-                        absl::string_view("asd"), absl::string_view("--"),
-                        absl::string_view("arg1"), absl::string_view("arg2"),
-                        absl::string_view("arg3"), absl::string_view("arg4")}));
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, TestIgnoreUndefinedFlags) {
-  const char* in_args1[] = {
-      "testbin",
-      "arg1",
-      "--undef_flag=aa",
-      "--int_flag=21",
-  };
-
-  auto out_args1 = flags::ParseCommandLineImpl(
-      4, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
-      flags::UsageFlagsAction::kHandleUsage,
-      flags::OnUndefinedFlag::kIgnoreUndefined);
-
-  EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"),
-                                           absl::string_view("arg1")}));
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21);
-
-  const char* in_args2[] = {
-      "testbin",
-      "arg1",
-      "--undef_flag=aa",
-      "--string_flag=AA",
-  };
-
-  auto out_args2 = flags::ParseCommandLineImpl(
-      4, const_cast<char**>(in_args2), flags::ArgvListAction::kKeepParsedArgs,
-      flags::UsageFlagsAction::kHandleUsage,
-      flags::OnUndefinedFlag::kIgnoreUndefined);
-
-  EXPECT_THAT(
-      out_args2,
-      ElementsAreArray(
-          {absl::string_view("testbin"), absl::string_view("--undef_flag=aa"),
-           absl::string_view("--string_flag=AA"), absl::string_view("arg1")}));
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "AA");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
-  const char* in_args1[] = {
-      "testbin",
-      "--help",
-  };
-
-  EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1), "");
-
-  const char* in_args2[] = {
-      "testbin",
-      "--help",
-      "--int_flag=3",
-  };
-
-  auto out_args2 = flags::ParseCommandLineImpl(
-      3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
-      flags::UsageFlagsAction::kIgnoreUsage,
-      flags::OnUndefinedFlag::kAbortIfUndefined);
-
-  EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
-  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) {
-  const char* in_args1[] = {
-      "testbin",
-      "--help=abcd",
-  };
-
-  auto out_args1 = flags::ParseCommandLineImpl(
-      2, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
-      flags::UsageFlagsAction::kIgnoreUsage,
-      flags::OnUndefinedFlag::kAbortIfUndefined);
-
-  EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch);
-  EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd");
-
-  const char* in_args2[] = {"testbin", "--help", "some_positional_arg"};
-
-  auto out_args2 = flags::ParseCommandLineImpl(
-      3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
-      flags::UsageFlagsAction::kIgnoreUsage,
-      flags::OnUndefinedFlag::kAbortIfUndefined);
-
-  EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ParseTest, WasPresentOnCommandLine) {
-  const char* in_args1[] = {
-      "testbin",        "arg1", "--bool_flag",
-      "--int_flag=211", "arg2", "--double_flag=1.1",
-      "--string_flag",  "asd",  "--",
-      "--some_flag",    "arg4",
-  };
-
-  InvokeParse(in_args1);
-
-  EXPECT_TRUE(flags::WasPresentOnCommandLine("bool_flag"));
-  EXPECT_TRUE(flags::WasPresentOnCommandLine("int_flag"));
-  EXPECT_TRUE(flags::WasPresentOnCommandLine("double_flag"));
-  EXPECT_TRUE(flags::WasPresentOnCommandLine("string_flag"));
-  EXPECT_FALSE(flags::WasPresentOnCommandLine("some_flag"));
-  EXPECT_FALSE(flags::WasPresentOnCommandLine("another_flag"));
-}
-
-// --------------------------------------------------------------------
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/flags/reflection.cc b/third_party/abseil_cpp/absl/flags/reflection.cc
deleted file mode 100644
index c976d46796..0000000000
--- a/third_party/abseil_cpp/absl/flags/reflection.cc
+++ /dev/null
@@ -1,337 +0,0 @@
-//
-//  Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/reflection.h"
-
-#include <assert.h>
-
-#include <atomic>
-#include <map>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/flags/commandlineflag.h"
-#include "absl/flags/internal/private_handle_accessor.h"
-#include "absl/flags/internal/registry.h"
-#include "absl/flags/usage_config.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-// --------------------------------------------------------------------
-// FlagRegistry
-//    A FlagRegistry singleton object holds all flag objects indexed by their
-//    names so that if you know a flag's name, you can access or set it. If the
-//    function is named FooLocked(), you must own the registry lock before
-//    calling the function; otherwise, you should *not* hold the lock, and the
-//    function will acquire it itself if needed.
-// --------------------------------------------------------------------
-
-class FlagRegistry {
- public:
-  FlagRegistry() = default;
-  ~FlagRegistry() = default;
-
-  // Store a flag in this registry. Takes ownership of *flag.
-  void RegisterFlag(CommandLineFlag& flag);
-
-  void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
-  void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
-
-  // Returns the flag object for the specified name, or nullptr if not found.
-  // Will emit a warning if a 'retired' flag is specified.
-  CommandLineFlag* FindFlag(absl::string_view name);
-
-  static FlagRegistry& GlobalRegistry();  // returns a singleton registry
-
- private:
-  friend class flags_internal::FlagSaverImpl;  // reads all the flags in order
-                                               // to copy them
-  friend void ForEachFlag(std::function<void(CommandLineFlag&)> visitor);
-  friend void FinalizeRegistry();
-
-  // The map from name to flag, for FindFlag().
-  using FlagMap = std::map<absl::string_view, CommandLineFlag*>;
-  using FlagIterator = FlagMap::iterator;
-  using FlagConstIterator = FlagMap::const_iterator;
-  FlagMap flags_;
-  std::vector<CommandLineFlag*> flat_flags_;
-  std::atomic<bool> finalized_flags_{false};
-
-  absl::Mutex lock_;
-
-  // Disallow
-  FlagRegistry(const FlagRegistry&);
-  FlagRegistry& operator=(const FlagRegistry&);
-};
-
-namespace {
-
-class FlagRegistryLock {
- public:
-  explicit FlagRegistryLock(FlagRegistry& fr) : fr_(fr) { fr_.Lock(); }
-  ~FlagRegistryLock() { fr_.Unlock(); }
-
- private:
-  FlagRegistry& fr_;
-};
-
-}  // namespace
-
-CommandLineFlag* FlagRegistry::FindFlag(absl::string_view name) {
-  if (finalized_flags_.load(std::memory_order_acquire)) {
-    // We could save some gcus here if we make `Name()` be non-virtual.
-    // We could move the `const char*` name to the base class.
-    auto it = std::partition_point(
-        flat_flags_.begin(), flat_flags_.end(),
-        [=](CommandLineFlag* f) { return f->Name() < name; });
-    if (it != flat_flags_.end() && (*it)->Name() == name) return *it;
-  }
-
-  FlagRegistryLock frl(*this);
-  auto it = flags_.find(name);
-  return it != flags_.end() ? it->second : nullptr;
-}
-
-void FlagRegistry::RegisterFlag(CommandLineFlag& flag) {
-  FlagRegistryLock registry_lock(*this);
-
-  std::pair<FlagIterator, bool> ins =
-      flags_.insert(FlagMap::value_type(flag.Name(), &flag));
-  if (ins.second == false) {  // means the name was already in the map
-    CommandLineFlag& old_flag = *ins.first->second;
-    if (flag.IsRetired() != old_flag.IsRetired()) {
-      // All registrations must agree on the 'retired' flag.
-      flags_internal::ReportUsageError(
-          absl::StrCat(
-              "Retired flag '", flag.Name(), "' was defined normally in file '",
-              (flag.IsRetired() ? old_flag.Filename() : flag.Filename()), "'."),
-          true);
-    } else if (flags_internal::PrivateHandleAccessor::TypeId(flag) !=
-               flags_internal::PrivateHandleAccessor::TypeId(old_flag)) {
-      flags_internal::ReportUsageError(
-          absl::StrCat("Flag '", flag.Name(),
-                       "' was defined more than once but with "
-                       "differing types. Defined in files '",
-                       old_flag.Filename(), "' and '", flag.Filename(), "'."),
-          true);
-    } else if (old_flag.IsRetired()) {
-      return;
-    } else if (old_flag.Filename() != flag.Filename()) {
-      flags_internal::ReportUsageError(
-          absl::StrCat("Flag '", flag.Name(),
-                       "' was defined more than once (in files '",
-                       old_flag.Filename(), "' and '", flag.Filename(), "')."),
-          true);
-    } else {
-      flags_internal::ReportUsageError(
-          absl::StrCat(
-              "Something is wrong with flag '", flag.Name(), "' in file '",
-              flag.Filename(), "'. One possibility: file '", flag.Filename(),
-              "' is being linked both statically and dynamically into this "
-              "executable. e.g. some files listed as srcs to a test and also "
-              "listed as srcs of some shared lib deps of the same test."),
-          true);
-    }
-    // All cases above are fatal, except for the retired flags.
-    std::exit(1);
-  }
-}
-
-FlagRegistry& FlagRegistry::GlobalRegistry() {
-  static FlagRegistry* global_registry = new FlagRegistry;
-  return *global_registry;
-}
-
-// --------------------------------------------------------------------
-
-void ForEachFlag(std::function<void(CommandLineFlag&)> visitor) {
-  FlagRegistry& registry = FlagRegistry::GlobalRegistry();
-
-  if (registry.finalized_flags_.load(std::memory_order_acquire)) {
-    for (const auto& i : registry.flat_flags_) visitor(*i);
-  }
-
-  FlagRegistryLock frl(registry);
-  for (const auto& i : registry.flags_) visitor(*i.second);
-}
-
-// --------------------------------------------------------------------
-
-bool RegisterCommandLineFlag(CommandLineFlag& flag) {
-  FlagRegistry::GlobalRegistry().RegisterFlag(flag);
-  return true;
-}
-
-void FinalizeRegistry() {
-  auto& registry = FlagRegistry::GlobalRegistry();
-  FlagRegistryLock frl(registry);
-  if (registry.finalized_flags_.load(std::memory_order_relaxed)) {
-    // Was already finalized. Ignore the second time.
-    return;
-  }
-  registry.flat_flags_.reserve(registry.flags_.size());
-  for (const auto& f : registry.flags_) {
-    registry.flat_flags_.push_back(f.second);
-  }
-  registry.flags_.clear();
-  registry.finalized_flags_.store(true, std::memory_order_release);
-}
-
-// --------------------------------------------------------------------
-
-namespace {
-
-class RetiredFlagObj final : public CommandLineFlag {
- public:
-  constexpr RetiredFlagObj(const char* name, FlagFastTypeId type_id)
-      : name_(name), type_id_(type_id) {}
-
- private:
-  absl::string_view Name() const override { return name_; }
-  std::string Filename() const override {
-    OnAccess();
-    return "RETIRED";
-  }
-  FlagFastTypeId TypeId() const override { return type_id_; }
-  std::string Help() const override {
-    OnAccess();
-    return "";
-  }
-  bool IsRetired() const override { return true; }
-  bool IsSpecifiedOnCommandLine() const override {
-    OnAccess();
-    return false;
-  }
-  std::string DefaultValue() const override {
-    OnAccess();
-    return "";
-  }
-  std::string CurrentValue() const override {
-    OnAccess();
-    return "";
-  }
-
-  // Any input is valid
-  bool ValidateInputValue(absl::string_view) const override {
-    OnAccess();
-    return true;
-  }
-
-  std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
-    return nullptr;
-  }
-
-  bool ParseFrom(absl::string_view, flags_internal::FlagSettingMode,
-                 flags_internal::ValueSource, std::string&) override {
-    OnAccess();
-    return false;
-  }
-
-  void CheckDefaultValueParsingRoundtrip() const override { OnAccess(); }
-
-  void Read(void*) const override { OnAccess(); }
-
-  void OnAccess() const {
-    flags_internal::ReportUsageError(
-        absl::StrCat("Accessing retired flag '", name_, "'"), false);
-  }
-
-  // Data members
-  const char* const name_;
-  const FlagFastTypeId type_id_;
-};
-
-}  // namespace
-
-void Retire(const char* name, FlagFastTypeId type_id, char* buf) {
-  static_assert(sizeof(RetiredFlagObj) == kRetiredFlagObjSize, "");
-  static_assert(alignof(RetiredFlagObj) == kRetiredFlagObjAlignment, "");
-  auto* flag = ::new (static_cast<void*>(buf))
-      flags_internal::RetiredFlagObj(name, type_id);
-  FlagRegistry::GlobalRegistry().RegisterFlag(*flag);
-}
-
-// --------------------------------------------------------------------
-
-class FlagSaverImpl {
- public:
-  FlagSaverImpl() = default;
-  FlagSaverImpl(const FlagSaverImpl&) = delete;
-  void operator=(const FlagSaverImpl&) = delete;
-
-  // Saves the flag states from the flag registry into this object.
-  // It's an error to call this more than once.
-  void SaveFromRegistry() {
-    assert(backup_registry_.empty());  // call only once!
-    flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
-      if (auto flag_state =
-              flags_internal::PrivateHandleAccessor::SaveState(flag)) {
-        backup_registry_.emplace_back(std::move(flag_state));
-      }
-    });
-  }
-
-  // Restores the saved flag states into the flag registry.
-  void RestoreToRegistry() {
-    for (const auto& flag_state : backup_registry_) {
-      flag_state->Restore();
-    }
-  }
-
- private:
-  std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
-      backup_registry_;
-};
-
-}  // namespace flags_internal
-
-FlagSaver::FlagSaver() : impl_(new flags_internal::FlagSaverImpl) {
-  impl_->SaveFromRegistry();
-}
-
-FlagSaver::~FlagSaver() {
-  if (!impl_) return;
-
-  impl_->RestoreToRegistry();
-  delete impl_;
-}
-
-// --------------------------------------------------------------------
-
-CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
-  if (name.empty()) return nullptr;
-  flags_internal::FlagRegistry& registry =
-      flags_internal::FlagRegistry::GlobalRegistry();
-  return registry.FindFlag(name);
-}
-
-// --------------------------------------------------------------------
-
-absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> GetAllFlags() {
-  absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> res;
-  flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
-    if (!flag.IsRetired()) res.insert({flag.Name(), &flag});
-  });
-  return res;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/reflection.h b/third_party/abseil_cpp/absl/flags/reflection.h
deleted file mode 100644
index e6baf5de4b..0000000000
--- a/third_party/abseil_cpp/absl/flags/reflection.h
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: reflection.h
-// -----------------------------------------------------------------------------
-//
-// This file defines the routines to access and operate on an Abseil Flag's
-// reflection handle.
-
-#ifndef ABSL_FLAGS_REFLECTION_H_
-#define ABSL_FLAGS_REFLECTION_H_
-
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/container/flat_hash_map.h"
-#include "absl/flags/commandlineflag.h"
-#include "absl/flags/internal/commandlineflag.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-class FlagSaverImpl;
-}  // namespace flags_internal
-
-// FindCommandLineFlag()
-//
-// Returns the reflection handle of an Abseil flag of the specified name, or
-// `nullptr` if not found. This function will emit a warning if the name of a
-// 'retired' flag is specified.
-absl::CommandLineFlag* FindCommandLineFlag(absl::string_view name);
-
-// Returns current state of the Flags registry in a form of mapping from flag
-// name to a flag reflection handle.
-absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> GetAllFlags();
-
-//------------------------------------------------------------------------------
-// FlagSaver
-//------------------------------------------------------------------------------
-//
-// A FlagSaver object stores the state of flags in the scope where the FlagSaver
-// is defined, allowing modification of those flags within that scope and
-// automatic restoration of the flags to their previous state upon leaving the
-// scope.
-//
-// A FlagSaver can be used within tests to temporarily change the test
-// environment and restore the test case to its previous state.
-//
-// Example:
-//
-//   void MyFunc() {
-//    absl::FlagSaver fs;
-//    ...
-//    absl::SetFlag(&FLAGS_myFlag, otherValue);
-//    ...
-//  } // scope of FlagSaver left, flags return to previous state
-//
-// This class is thread-safe.
-
-class FlagSaver {
- public:
-  FlagSaver();
-  ~FlagSaver();
-
-  FlagSaver(const FlagSaver&) = delete;
-  void operator=(const FlagSaver&) = delete;
-
- private:
-  flags_internal::FlagSaverImpl* impl_;
-};
-
-//-----------------------------------------------------------------------------
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_REFLECTION_H_
diff --git a/third_party/abseil_cpp/absl/flags/reflection_test.cc b/third_party/abseil_cpp/absl/flags/reflection_test.cc
deleted file mode 100644
index 4c80900956..0000000000
--- a/third_party/abseil_cpp/absl/flags/reflection_test.cc
+++ /dev/null
@@ -1,267 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/reflection.h"
-
-#include <memory>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/flags/declare.h"
-#include "absl/flags/flag.h"
-#include "absl/flags/internal/commandlineflag.h"
-#include "absl/flags/marshalling.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_split.h"
-
-ABSL_FLAG(int, int_flag, 1, "int_flag help");
-ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help");
-ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help");
-
-namespace {
-
-namespace flags = absl::flags_internal;
-
-class ReflectionTest : public testing::Test {
- protected:
-  void SetUp() override { flag_saver_ = absl::make_unique<absl::FlagSaver>(); }
-  void TearDown() override { flag_saver_.reset(); }
-
- private:
-  std::unique_ptr<absl::FlagSaver> flag_saver_;
-};
-
-// --------------------------------------------------------------------
-
-TEST_F(ReflectionTest, TestFindCommandLineFlag) {
-  auto* handle = absl::FindCommandLineFlag("some_flag");
-  EXPECT_EQ(handle, nullptr);
-
-  handle = absl::FindCommandLineFlag("int_flag");
-  EXPECT_NE(handle, nullptr);
-
-  handle = absl::FindCommandLineFlag("string_flag");
-  EXPECT_NE(handle, nullptr);
-
-  handle = absl::FindCommandLineFlag("bool_retired_flag");
-  EXPECT_NE(handle, nullptr);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ReflectionTest, TestGetAllFlags) {
-  auto all_flags = absl::GetAllFlags();
-  EXPECT_NE(all_flags.find("int_flag"), all_flags.end());
-  EXPECT_EQ(all_flags.find("bool_retired_flag"), all_flags.end());
-  EXPECT_EQ(all_flags.find("some_undefined_flag"), all_flags.end());
-
-  std::vector<absl::string_view> flag_names_first_attempt;
-  auto all_flags_1 = absl::GetAllFlags();
-  for (auto f : all_flags_1) {
-    flag_names_first_attempt.push_back(f.first);
-  }
-
-  std::vector<absl::string_view> flag_names_second_attempt;
-  auto all_flags_2 = absl::GetAllFlags();
-  for (auto f : all_flags_2) {
-    flag_names_second_attempt.push_back(f.first);
-  }
-
-  EXPECT_THAT(flag_names_first_attempt,
-              ::testing::UnorderedElementsAreArray(flag_names_second_attempt));
-}
-
-// --------------------------------------------------------------------
-
-struct CustomUDT {
-  CustomUDT() : a(1), b(1) {}
-  CustomUDT(int a_, int b_) : a(a_), b(b_) {}
-
-  friend bool operator==(const CustomUDT& f1, const CustomUDT& f2) {
-    return f1.a == f2.a && f1.b == f2.b;
-  }
-
-  int a;
-  int b;
-};
-bool AbslParseFlag(absl::string_view in, CustomUDT* f, std::string*) {
-  std::vector<absl::string_view> parts =
-      absl::StrSplit(in, ':', absl::SkipWhitespace());
-
-  if (parts.size() != 2) return false;
-
-  if (!absl::SimpleAtoi(parts[0], &f->a)) return false;
-
-  if (!absl::SimpleAtoi(parts[1], &f->b)) return false;
-
-  return true;
-}
-std::string AbslUnparseFlag(const CustomUDT& f) {
-  return absl::StrCat(f.a, ":", f.b);
-}
-
-}  // namespace
-
-// --------------------------------------------------------------------
-
-ABSL_FLAG(bool, test_flag_01, true, "");
-ABSL_FLAG(int, test_flag_02, 1234, "");
-ABSL_FLAG(int16_t, test_flag_03, -34, "");
-ABSL_FLAG(uint16_t, test_flag_04, 189, "");
-ABSL_FLAG(int32_t, test_flag_05, 10765, "");
-ABSL_FLAG(uint32_t, test_flag_06, 40000, "");
-ABSL_FLAG(int64_t, test_flag_07, -1234567, "");
-ABSL_FLAG(uint64_t, test_flag_08, 9876543, "");
-ABSL_FLAG(double, test_flag_09, -9.876e-50, "");
-ABSL_FLAG(float, test_flag_10, 1.234e12f, "");
-ABSL_FLAG(std::string, test_flag_11, "", "");
-ABSL_FLAG(absl::Duration, test_flag_12, absl::Minutes(10), "");
-static int counter = 0;
-ABSL_FLAG(int, test_flag_13, 200, "").OnUpdate([]() { counter++; });
-ABSL_FLAG(CustomUDT, test_flag_14, {}, "");
-
-namespace {
-
-TEST_F(ReflectionTest, TestFlagSaverInScope) {
-  {
-    absl::FlagSaver s;
-    counter = 0;
-    absl::SetFlag(&FLAGS_test_flag_01, false);
-    absl::SetFlag(&FLAGS_test_flag_02, -1021);
-    absl::SetFlag(&FLAGS_test_flag_03, 6009);
-    absl::SetFlag(&FLAGS_test_flag_04, 44);
-    absl::SetFlag(&FLAGS_test_flag_05, +800);
-    absl::SetFlag(&FLAGS_test_flag_06, -40978756);
-    absl::SetFlag(&FLAGS_test_flag_07, 23405);
-    absl::SetFlag(&FLAGS_test_flag_08, 975310);
-    absl::SetFlag(&FLAGS_test_flag_09, 1.00001);
-    absl::SetFlag(&FLAGS_test_flag_10, -3.54f);
-    absl::SetFlag(&FLAGS_test_flag_11, "asdf");
-    absl::SetFlag(&FLAGS_test_flag_12, absl::Hours(20));
-    absl::SetFlag(&FLAGS_test_flag_13, 4);
-    absl::SetFlag(&FLAGS_test_flag_14, CustomUDT{-1, -2});
-  }
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543);
-  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55);
-  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "");
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10));
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), 200);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_14), CustomUDT{});
-  EXPECT_EQ(counter, 2);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ReflectionTest, TestFlagSaverVsUpdateViaReflection) {
-  {
-    absl::FlagSaver s;
-    counter = 0;
-    std::string error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_01")->ParseFrom("false", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_02")->ParseFrom("-4536", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_03")->ParseFrom("111", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_04")->ParseFrom("909", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_05")->ParseFrom("-2004", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_06")->ParseFrom("1000023", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_07")->ParseFrom("69305", &error))
-        << error;
-    EXPECT_TRUE(absl::FindCommandLineFlag("test_flag_08")
-                    ->ParseFrom("1000000001", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_09")->ParseFrom("2.09021", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_10")->ParseFrom("-33.1", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_11")->ParseFrom("ADD_FOO", &error))
-        << error;
-    EXPECT_TRUE(absl::FindCommandLineFlag("test_flag_12")
-                    ->ParseFrom("3h11m16s", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_13")->ParseFrom("0", &error))
-        << error;
-    EXPECT_TRUE(
-        absl::FindCommandLineFlag("test_flag_14")->ParseFrom("10:1", &error))
-        << error;
-  }
-
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543);
-  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55);
-  EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "");
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10));
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), 200);
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_14), CustomUDT{});
-  EXPECT_EQ(counter, 2);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(ReflectionTest, TestMultipleFlagSaversInEnclosedScopes) {
-  {
-    absl::FlagSaver s;
-    absl::SetFlag(&FLAGS_test_flag_08, 10);
-    EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 10);
-    {
-      absl::FlagSaver s;
-      absl::SetFlag(&FLAGS_test_flag_08, 20);
-      EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 20);
-      {
-        absl::FlagSaver s;
-        absl::SetFlag(&FLAGS_test_flag_08, -200);
-        EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), -200);
-      }
-      EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 20);
-    }
-    EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 10);
-  }
-  EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/flags/usage.cc b/third_party/abseil_cpp/absl/flags/usage.cc
deleted file mode 100644
index 452f667512..0000000000
--- a/third_party/abseil_cpp/absl/flags/usage.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#include "absl/flags/usage.h"
-
-#include <stdlib.h>
-
-#include <string>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/const_init.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/flags/internal/usage.h"
-#include "absl/strings/string_view.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-namespace {
-ABSL_CONST_INIT absl::Mutex usage_message_guard(absl::kConstInit);
-ABSL_CONST_INIT std::string* program_usage_message
-    ABSL_GUARDED_BY(usage_message_guard) = nullptr;
-}  // namespace
-}  // namespace flags_internal
-
-// --------------------------------------------------------------------
-// Sets the "usage" message to be used by help reporting routines.
-void SetProgramUsageMessage(absl::string_view new_usage_message) {
-  absl::MutexLock l(&flags_internal::usage_message_guard);
-
-  if (flags_internal::program_usage_message != nullptr) {
-    ABSL_INTERNAL_LOG(FATAL, "SetProgramUsageMessage() called twice.");
-    std::exit(1);
-  }
-
-  flags_internal::program_usage_message = new std::string(new_usage_message);
-}
-
-// --------------------------------------------------------------------
-// Returns the usage message set by SetProgramUsageMessage().
-// Note: We able to return string_view here only because calling
-// SetProgramUsageMessage twice is prohibited.
-absl::string_view ProgramUsageMessage() {
-  absl::MutexLock l(&flags_internal::usage_message_guard);
-
-  return flags_internal::program_usage_message != nullptr
-             ? absl::string_view(*flags_internal::program_usage_message)
-             : "Warning: SetProgramUsageMessage() never called";
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/usage.h b/third_party/abseil_cpp/absl/flags/usage.h
deleted file mode 100644
index ad12ab7ad9..0000000000
--- a/third_party/abseil_cpp/absl/flags/usage.h
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FLAGS_USAGE_H_
-#define ABSL_FLAGS_USAGE_H_
-
-#include "absl/base/config.h"
-#include "absl/strings/string_view.h"
-
-// --------------------------------------------------------------------
-// Usage reporting interfaces
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Sets the "usage" message to be used by help reporting routines.
-// For example:
-//  absl::SetProgramUsageMessage(
-//      absl::StrCat("This program does nothing.  Sample usage:\n", argv[0],
-//                   " <uselessarg1> <uselessarg2>"));
-// Do not include commandline flags in the usage: we do that for you!
-// Note: Calling SetProgramUsageMessage twice will trigger a call to std::exit.
-void SetProgramUsageMessage(absl::string_view new_usage_message);
-
-// Returns the usage message set by SetProgramUsageMessage().
-absl::string_view ProgramUsageMessage();
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FLAGS_USAGE_H_
diff --git a/third_party/abseil_cpp/absl/flags/usage_config.cc b/third_party/abseil_cpp/absl/flags/usage_config.cc
deleted file mode 100644
index ae2f548a57..0000000000
--- a/third_party/abseil_cpp/absl/flags/usage_config.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/usage_config.h"
-
-#include <functional>
-#include <iostream>
-#include <string>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/const_init.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/flags/internal/path_util.h"
-#include "absl/flags/internal/program_name.h"
-#include "absl/strings/match.h"
-#include "absl/strings/string_view.h"
-#include "absl/strings/strip.h"
-#include "absl/synchronization/mutex.h"
-
-extern "C" {
-
-// Additional report of fatal usage error message before we std::exit. Error is
-// fatal if is_fatal argument to ReportUsageError is true.
-ABSL_ATTRIBUTE_WEAK void AbslInternalReportFatalUsageError(absl::string_view) {}
-
-}  // extern "C"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace flags_internal {
-
-namespace {
-
-// --------------------------------------------------------------------
-// Returns true if flags defined in the filename should be reported with
-// -helpshort flag.
-
-bool ContainsHelpshortFlags(absl::string_view filename) {
-  // By default we only want flags in binary's main. We expect the main
-  // routine to reside in <program>.cc or <program>-main.cc or
-  // <program>_main.cc, where the <program> is the name of the binary
-  // (without .exe on Windows).
-  auto suffix = flags_internal::Basename(filename);
-  auto program_name = flags_internal::ShortProgramInvocationName();
-  absl::string_view program_name_ref = program_name;
-#if defined(_WIN32)
-  absl::ConsumeSuffix(&program_name_ref, ".exe");
-#endif
-  if (!absl::ConsumePrefix(&suffix, program_name_ref))
-    return false;
-  return absl::StartsWith(suffix, ".") || absl::StartsWith(suffix, "-main.") ||
-         absl::StartsWith(suffix, "_main.");
-}
-
-// --------------------------------------------------------------------
-// Returns true if flags defined in the filename should be reported with
-// -helppackage flag.
-
-bool ContainsHelppackageFlags(absl::string_view filename) {
-  // TODO(rogeeff): implement properly when registry is available.
-  return ContainsHelpshortFlags(filename);
-}
-
-// --------------------------------------------------------------------
-// Generates program version information into supplied output.
-
-std::string VersionString() {
-  std::string version_str(flags_internal::ShortProgramInvocationName());
-
-  version_str += "\n";
-
-#if !defined(NDEBUG)
-  version_str += "Debug build (NDEBUG not #defined)\n";
-#endif
-
-  return version_str;
-}
-
-// --------------------------------------------------------------------
-// Normalizes the filename specific to the build system/filesystem used.
-
-std::string NormalizeFilename(absl::string_view filename) {
-  // Skip any leading slashes
-  auto pos = filename.find_first_not_of("\\/");
-  if (pos == absl::string_view::npos) return "";
-
-  filename.remove_prefix(pos);
-  return std::string(filename);
-}
-
-// --------------------------------------------------------------------
-
-ABSL_CONST_INIT absl::Mutex custom_usage_config_guard(absl::kConstInit);
-ABSL_CONST_INIT FlagsUsageConfig* custom_usage_config
-    ABSL_GUARDED_BY(custom_usage_config_guard) = nullptr;
-
-}  // namespace
-
-FlagsUsageConfig GetUsageConfig() {
-  absl::MutexLock l(&custom_usage_config_guard);
-
-  if (custom_usage_config) return *custom_usage_config;
-
-  FlagsUsageConfig default_config;
-  default_config.contains_helpshort_flags = &ContainsHelpshortFlags;
-  default_config.contains_help_flags = &ContainsHelppackageFlags;
-  default_config.contains_helppackage_flags = &ContainsHelppackageFlags;
-  default_config.version_string = &VersionString;
-  default_config.normalize_filename = &NormalizeFilename;
-
-  return default_config;
-}
-
-void ReportUsageError(absl::string_view msg, bool is_fatal) {
-  std::cerr << "ERROR: " << msg << std::endl;
-
-  if (is_fatal) {
-    AbslInternalReportFatalUsageError(msg);
-  }
-}
-
-}  // namespace flags_internal
-
-void SetFlagsUsageConfig(FlagsUsageConfig usage_config) {
-  absl::MutexLock l(&flags_internal::custom_usage_config_guard);
-
-  if (!usage_config.contains_helpshort_flags)
-    usage_config.contains_helpshort_flags =
-        flags_internal::ContainsHelpshortFlags;
-
-  if (!usage_config.contains_help_flags)
-    usage_config.contains_help_flags = flags_internal::ContainsHelppackageFlags;
-
-  if (!usage_config.contains_helppackage_flags)
-    usage_config.contains_helppackage_flags =
-        flags_internal::ContainsHelppackageFlags;
-
-  if (!usage_config.version_string)
-    usage_config.version_string = flags_internal::VersionString;
-
-  if (!usage_config.normalize_filename)
-    usage_config.normalize_filename = flags_internal::NormalizeFilename;
-
-  if (flags_internal::custom_usage_config)
-    *flags_internal::custom_usage_config = usage_config;
-  else
-    flags_internal::custom_usage_config = new FlagsUsageConfig(usage_config);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/flags/usage_config.h b/third_party/abseil_cpp/absl/flags/usage_config.h
deleted file mode 100644
index 96eecea231..0000000000
--- a/third_party/abseil_cpp/absl/flags/usage_config.h
+++ /dev/null
@@ -1,134 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: usage_config.h
-// -----------------------------------------------------------------------------
-//
-// This file defines the main usage reporting configuration interfaces and
-// documents Abseil's supported built-in usage flags. If these flags are found
-// when parsing a command-line, Abseil will exit the program and display
-// appropriate help messages.
-#ifndef ABSL_FLAGS_USAGE_CONFIG_H_
-#define ABSL_FLAGS_USAGE_CONFIG_H_
-
-#include <functional>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/strings/string_view.h"
-
-// -----------------------------------------------------------------------------
-// Built-in Usage Flags
-// -----------------------------------------------------------------------------
-//
-// Abseil supports the following built-in usage flags. When passed, these flags
-// exit the program and :
-//
-// * --help
-//     Shows help on important flags for this binary
-// * --helpfull
-//     Shows help on all flags
-// * --helpshort
-//     Shows help on only the main module for this program
-// * --helppackage
-//     Shows help on all modules in the main package
-// * --version
-//     Shows the version and build info for this binary and exits
-// * --only_check_args
-//     Exits after checking all flags
-// * --helpon
-//     Shows help on the modules named by this flag value
-// * --helpmatch
-//     Shows help on modules whose name contains the specified substring
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace flags_internal {
-using FlagKindFilter = std::function<bool (absl::string_view)>;
-}  // namespace flags_internal
-
-// FlagsUsageConfig
-//
-// This structure contains the collection of callbacks for changing the behavior
-// of the usage reporting routines in Abseil Flags.
-struct FlagsUsageConfig {
-  // Returns true if flags defined in the given source code file should be
-  // reported with --helpshort flag. For example, if the file
-  // "path/to/my/code.cc" defines the flag "--my_flag", and
-  // contains_helpshort_flags("path/to/my/code.cc") returns true, invoking the
-  // program with --helpshort will include information about --my_flag in the
-  // program output.
-  flags_internal::FlagKindFilter contains_helpshort_flags;
-
-  // Returns true if flags defined in the filename should be reported with
-  // --help flag. For example, if the file
-  // "path/to/my/code.cc" defines the flag "--my_flag", and
-  // contains_help_flags("path/to/my/code.cc") returns true, invoking the
-  // program with --help will include information about --my_flag in the
-  // program output.
-  flags_internal::FlagKindFilter contains_help_flags;
-
-  // Returns true if flags defined in the filename should be reported with
-  // --helppackage flag. For example, if the file
-  // "path/to/my/code.cc" defines the flag "--my_flag", and
-  // contains_helppackage_flags("path/to/my/code.cc") returns true, invoking the
-  // program with --helppackage will include information about --my_flag in the
-  // program output.
-  flags_internal::FlagKindFilter contains_helppackage_flags;
-
-  // Generates string containing program version. This is the string reported
-  // when user specifies --version in a command line.
-  std::function<std::string()> version_string;
-
-  // Normalizes the filename specific to the build system/filesystem used. This
-  // routine is used when we report the information about the flag definition
-  // location. For instance, if your build resides at some location you do not
-  // want to expose in the usage output, you can trim it to show only relevant
-  // part.
-  // For example:
-  //   normalize_filename("/my_company/some_long_path/src/project/file.cc")
-  // might produce
-  //   "project/file.cc".
-  std::function<std::string(absl::string_view)> normalize_filename;
-};
-
-// SetFlagsUsageConfig()
-//
-// Sets the usage reporting configuration callbacks. If any of the callbacks are
-// not set in usage_config instance, then the default value of the callback is
-// used.
-void SetFlagsUsageConfig(FlagsUsageConfig usage_config);
-
-namespace flags_internal {
-
-FlagsUsageConfig GetUsageConfig();
-
-void ReportUsageError(absl::string_view msg, bool is_fatal);
-
-}  // namespace flags_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-extern "C" {
-
-// Additional report of fatal usage error message before we std::exit. Error is
-// fatal if is_fatal argument to ReportUsageError is true.
-void AbslInternalReportFatalUsageError(absl::string_view);
-
-}  // extern "C"
-
-#endif  // ABSL_FLAGS_USAGE_CONFIG_H_
diff --git a/third_party/abseil_cpp/absl/flags/usage_config_test.cc b/third_party/abseil_cpp/absl/flags/usage_config_test.cc
deleted file mode 100644
index e57a8832f6..0000000000
--- a/third_party/abseil_cpp/absl/flags/usage_config_test.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-//
-//  Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/usage_config.h"
-
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/flags/internal/path_util.h"
-#include "absl/flags/internal/program_name.h"
-#include "absl/strings/match.h"
-#include "absl/strings/string_view.h"
-
-namespace {
-
-class FlagsUsageConfigTest : public testing::Test {
- protected:
-  void SetUp() override {
-    // Install Default config for the use on this unit test.
-    // Binary may install a custom config before tests are run.
-    absl::FlagsUsageConfig default_config;
-    absl::SetFlagsUsageConfig(default_config);
-  }
-};
-
-namespace flags = absl::flags_internal;
-
-bool TstContainsHelpshortFlags(absl::string_view f) {
-  return absl::StartsWith(flags::Basename(f), "progname.");
-}
-
-bool TstContainsHelppackageFlags(absl::string_view f) {
-  return absl::EndsWith(flags::Package(f), "aaa/");
-}
-
-bool TstContainsHelpFlags(absl::string_view f) {
-  return absl::EndsWith(flags::Package(f), "zzz/");
-}
-
-std::string TstVersionString() { return "program 1.0.0"; }
-
-std::string TstNormalizeFilename(absl::string_view filename) {
-  return std::string(filename.substr(2));
-}
-
-void TstReportUsageMessage(absl::string_view msg) {}
-
-// --------------------------------------------------------------------
-
-TEST_F(FlagsUsageConfigTest, TestGetSetFlagsUsageConfig) {
-  EXPECT_TRUE(flags::GetUsageConfig().contains_helpshort_flags);
-  EXPECT_TRUE(flags::GetUsageConfig().contains_help_flags);
-  EXPECT_TRUE(flags::GetUsageConfig().contains_helppackage_flags);
-  EXPECT_TRUE(flags::GetUsageConfig().version_string);
-  EXPECT_TRUE(flags::GetUsageConfig().normalize_filename);
-
-  absl::FlagsUsageConfig empty_config;
-  empty_config.contains_helpshort_flags = &TstContainsHelpshortFlags;
-  empty_config.contains_help_flags = &TstContainsHelpFlags;
-  empty_config.contains_helppackage_flags = &TstContainsHelppackageFlags;
-  empty_config.version_string = &TstVersionString;
-  empty_config.normalize_filename = &TstNormalizeFilename;
-  absl::SetFlagsUsageConfig(empty_config);
-
-  EXPECT_TRUE(flags::GetUsageConfig().contains_helpshort_flags);
-  EXPECT_TRUE(flags::GetUsageConfig().contains_help_flags);
-  EXPECT_TRUE(flags::GetUsageConfig().contains_helppackage_flags);
-  EXPECT_TRUE(flags::GetUsageConfig().version_string);
-  EXPECT_TRUE(flags::GetUsageConfig().normalize_filename);
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(FlagsUsageConfigTest, TestContainsHelpshortFlags) {
-#if defined(_WIN32)
-  flags::SetProgramInvocationName("usage_config_test.exe");
-#else
-  flags::SetProgramInvocationName("usage_config_test");
-#endif
-
-  auto config = flags::GetUsageConfig();
-  EXPECT_TRUE(config.contains_helpshort_flags("adir/cd/usage_config_test.cc"));
-  EXPECT_TRUE(
-      config.contains_helpshort_flags("aaaa/usage_config_test-main.cc"));
-  EXPECT_TRUE(config.contains_helpshort_flags("abc/usage_config_test_main.cc"));
-  EXPECT_FALSE(config.contains_helpshort_flags("usage_config_main.cc"));
-
-  absl::FlagsUsageConfig empty_config;
-  empty_config.contains_helpshort_flags = &TstContainsHelpshortFlags;
-  absl::SetFlagsUsageConfig(empty_config);
-
-  EXPECT_TRUE(
-      flags::GetUsageConfig().contains_helpshort_flags("aaa/progname.cpp"));
-  EXPECT_FALSE(
-      flags::GetUsageConfig().contains_helpshort_flags("aaa/progmane.cpp"));
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(FlagsUsageConfigTest, TestContainsHelpFlags) {
-  flags::SetProgramInvocationName("usage_config_test");
-
-  auto config = flags::GetUsageConfig();
-  EXPECT_TRUE(config.contains_help_flags("zzz/usage_config_test.cc"));
-  EXPECT_TRUE(
-      config.contains_help_flags("bdir/a/zzz/usage_config_test-main.cc"));
-  EXPECT_TRUE(
-      config.contains_help_flags("//aqse/zzz/usage_config_test_main.cc"));
-  EXPECT_FALSE(config.contains_help_flags("zzz/aa/usage_config_main.cc"));
-
-  absl::FlagsUsageConfig empty_config;
-  empty_config.contains_help_flags = &TstContainsHelpFlags;
-  absl::SetFlagsUsageConfig(empty_config);
-
-  EXPECT_TRUE(flags::GetUsageConfig().contains_help_flags("zzz/main-body.c"));
-  EXPECT_FALSE(
-      flags::GetUsageConfig().contains_help_flags("zzz/dir/main-body.c"));
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(FlagsUsageConfigTest, TestContainsHelppackageFlags) {
-  flags::SetProgramInvocationName("usage_config_test");
-
-  auto config = flags::GetUsageConfig();
-  EXPECT_TRUE(config.contains_helppackage_flags("aaa/usage_config_test.cc"));
-  EXPECT_TRUE(
-      config.contains_helppackage_flags("bbdir/aaa/usage_config_test-main.cc"));
-  EXPECT_TRUE(config.contains_helppackage_flags(
-      "//aqswde/aaa/usage_config_test_main.cc"));
-  EXPECT_FALSE(config.contains_helppackage_flags("aadir/usage_config_main.cc"));
-
-  absl::FlagsUsageConfig empty_config;
-  empty_config.contains_helppackage_flags = &TstContainsHelppackageFlags;
-  absl::SetFlagsUsageConfig(empty_config);
-
-  EXPECT_TRUE(
-      flags::GetUsageConfig().contains_helppackage_flags("aaa/main-body.c"));
-  EXPECT_FALSE(
-      flags::GetUsageConfig().contains_helppackage_flags("aadir/main-body.c"));
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(FlagsUsageConfigTest, TestVersionString) {
-  flags::SetProgramInvocationName("usage_config_test");
-
-#ifdef NDEBUG
-  std::string expected_output = "usage_config_test\n";
-#else
-  std::string expected_output =
-      "usage_config_test\nDebug build (NDEBUG not #defined)\n";
-#endif
-
-  EXPECT_EQ(flags::GetUsageConfig().version_string(), expected_output);
-
-  absl::FlagsUsageConfig empty_config;
-  empty_config.version_string = &TstVersionString;
-  absl::SetFlagsUsageConfig(empty_config);
-
-  EXPECT_EQ(flags::GetUsageConfig().version_string(), "program 1.0.0");
-}
-
-// --------------------------------------------------------------------
-
-TEST_F(FlagsUsageConfigTest, TestNormalizeFilename) {
-  // This tests the default implementation.
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("a/a.cc"), "a/a.cc");
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("/a/a.cc"), "a/a.cc");
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("///a/a.cc"), "a/a.cc");
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("/"), "");
-
-  // This tests that the custom implementation is called.
-  absl::FlagsUsageConfig empty_config;
-  empty_config.normalize_filename = &TstNormalizeFilename;
-  absl::SetFlagsUsageConfig(empty_config);
-
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("a/a.cc"), "a.cc");
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("aaa/a.cc"), "a/a.cc");
-
-  // This tests that the default implementation is called.
-  empty_config.normalize_filename = nullptr;
-  absl::SetFlagsUsageConfig(empty_config);
-
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("a/a.cc"), "a/a.cc");
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("/a/a.cc"), "a/a.cc");
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("///a/a.cc"), "a/a.cc");
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("\\a\\a.cc"), "a\\a.cc");
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("//"), "");
-  EXPECT_EQ(flags::GetUsageConfig().normalize_filename("\\\\"), "");
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/functional/BUILD.bazel b/third_party/abseil_cpp/absl/functional/BUILD.bazel
deleted file mode 100644
index ebd9b99b78..0000000000
--- a/third_party/abseil_cpp/absl/functional/BUILD.bazel
+++ /dev/null
@@ -1,93 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "bind_front",
-    srcs = ["internal/front_binder.h"],
-    hdrs = ["bind_front.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:base_internal",
-        "//absl/container:compressed_tuple",
-        "//absl/meta:type_traits",
-        "//absl/utility",
-    ],
-)
-
-cc_test(
-    name = "bind_front_test",
-    srcs = ["bind_front_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":bind_front",
-        "//absl/memory",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "function_ref",
-    srcs = ["internal/function_ref.h"],
-    hdrs = ["function_ref.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:base_internal",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_test(
-    name = "function_ref_test",
-    size = "small",
-    srcs = ["function_ref_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":function_ref",
-        "//absl/container:test_instance_tracker",
-        "//absl/memory",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "function_ref_benchmark",
-    srcs = [
-        "function_ref_benchmark.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":function_ref",
-        "//absl/base:core_headers",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/functional/CMakeLists.txt b/third_party/abseil_cpp/absl/functional/CMakeLists.txt
deleted file mode 100644
index cda914f2cd..0000000000
--- a/third_party/abseil_cpp/absl/functional/CMakeLists.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    bind_front
-  SRCS
-    "internal/front_binder.h"
-  HDRS
-    "bind_front.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base_internal
-    absl::compressed_tuple
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    bind_front_test
-  SRCS
-    "bind_front_test.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::bind_front
-    absl::memory
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    function_ref
-  SRCS
-    "internal/function_ref.h"
-  HDRS
-    "function_ref.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base_internal
-    absl::meta
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    function_ref_test
-  SRCS
-    "function_ref_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::function_ref
-    absl::memory
-    absl::test_instance_tracker
-    gmock_main
-)
diff --git a/third_party/abseil_cpp/absl/functional/bind_front.h b/third_party/abseil_cpp/absl/functional/bind_front.h
deleted file mode 100644
index 5b47970e35..0000000000
--- a/third_party/abseil_cpp/absl/functional/bind_front.h
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: bind_front.h
-// -----------------------------------------------------------------------------
-//
-// `absl::bind_front()` returns a functor by binding a number of arguments to
-// the front of a provided (usually more generic) functor. Unlike `std::bind`,
-// it does not require the use of argument placeholders. The simpler syntax of
-// `absl::bind_front()` allows you to avoid known misuses with `std::bind()`.
-//
-// `absl::bind_front()` is meant as a drop-in replacement for C++20's upcoming
-// `std::bind_front()`, which similarly resolves these issues with
-// `std::bind()`. Both `bind_front()` alternatives, unlike `std::bind()`, allow
-// partial function application. (See
-// https://en.wikipedia.org/wiki/Partial_application).
-
-#ifndef ABSL_FUNCTIONAL_BIND_FRONT_H_
-#define ABSL_FUNCTIONAL_BIND_FRONT_H_
-
-#include "absl/functional/internal/front_binder.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// bind_front()
-//
-// Binds the first N arguments of an invocable object and stores them by value.
-//
-// Like `std::bind()`, `absl::bind_front()` is implicitly convertible to
-// `std::function`.  In particular, it may be used as a simpler replacement for
-// `std::bind()` in most cases, as it does not require  placeholders to be
-// specified. More importantly, it provides more reliable correctness guarantees
-// than `std::bind()`; while `std::bind()` will silently ignore passing more
-// parameters than expected, for example, `absl::bind_front()` will report such
-// mis-uses as errors.
-//
-// absl::bind_front(a...) can be seen as storing the results of
-// std::make_tuple(a...).
-//
-// Example: Binding a free function.
-//
-//   int Minus(int a, int b) { return a - b; }
-//
-//   assert(absl::bind_front(Minus)(3, 2) == 3 - 2);
-//   assert(absl::bind_front(Minus, 3)(2) == 3 - 2);
-//   assert(absl::bind_front(Minus, 3, 2)() == 3 - 2);
-//
-// Example: Binding a member function.
-//
-//   struct Math {
-//     int Double(int a) const { return 2 * a; }
-//   };
-//
-//   Math math;
-//
-//   assert(absl::bind_front(&Math::Double)(&math, 3) == 2 * 3);
-//   // Stores a pointer to math inside the functor.
-//   assert(absl::bind_front(&Math::Double, &math)(3) == 2 * 3);
-//   // Stores a copy of math inside the functor.
-//   assert(absl::bind_front(&Math::Double, math)(3) == 2 * 3);
-//   // Stores std::unique_ptr<Math> inside the functor.
-//   assert(absl::bind_front(&Math::Double,
-//                           std::unique_ptr<Math>(new Math))(3) == 2 * 3);
-//
-// Example: Using `absl::bind_front()`, instead of `std::bind()`, with
-//          `std::function`.
-//
-//   class FileReader {
-//    public:
-//     void ReadFileAsync(const std::string& filename, std::string* content,
-//                        const std::function<void()>& done) {
-//       // Calls Executor::Schedule(std::function<void()>).
-//       Executor::DefaultExecutor()->Schedule(
-//           absl::bind_front(&FileReader::BlockingRead, this,
-//                            filename, content, done));
-//     }
-//
-//    private:
-//     void BlockingRead(const std::string& filename, std::string* content,
-//                       const std::function<void()>& done) {
-//       CHECK_OK(file::GetContents(filename, content, {}));
-//       done();
-//     }
-//   };
-//
-// `absl::bind_front()` stores bound arguments explicitly using the type passed
-// rather than implicitly based on the type accepted by its functor.
-//
-// Example: Binding arguments explicitly.
-//
-//   void LogStringView(absl::string_view sv) {
-//     LOG(INFO) << sv;
-//   }
-//
-//   Executor* e = Executor::DefaultExecutor();
-//   std::string s = "hello";
-//   absl::string_view sv = s;
-//
-//   // absl::bind_front(LogStringView, arg) makes a copy of arg and stores it.
-//   e->Schedule(absl::bind_front(LogStringView, sv)); // ERROR: dangling
-//                                                     // string_view.
-//
-//   e->Schedule(absl::bind_front(LogStringView, s));  // OK: stores a copy of
-//                                                     // s.
-//
-// To store some of the arguments passed to `absl::bind_front()` by reference,
-//  use std::ref()` and `std::cref()`.
-//
-// Example: Storing some of the bound arguments by reference.
-//
-//   class Service {
-//    public:
-//     void Serve(const Request& req, std::function<void()>* done) {
-//       // The request protocol buffer won't be deleted until done is called.
-//       // It's safe to store a reference to it inside the functor.
-//       Executor::DefaultExecutor()->Schedule(
-//           absl::bind_front(&Service::BlockingServe, this, std::cref(req),
-//           done));
-//     }
-//
-//    private:
-//     void BlockingServe(const Request& req, std::function<void()>* done);
-//   };
-//
-// Example: Storing bound arguments by reference.
-//
-//   void Print(const std::string& a, const std::string& b) {
-//     std::cerr << a << b;
-//   }
-//
-//   std::string hi = "Hello, ";
-//   std::vector<std::string> names = {"Chuk", "Gek"};
-//   // Doesn't copy hi.
-//   for_each(names.begin(), names.end(),
-//            absl::bind_front(Print, std::ref(hi)));
-//
-//   // DO NOT DO THIS: the functor may outlive "hi", resulting in
-//   // dangling references.
-//   foo->DoInFuture(absl::bind_front(Print, std::ref(hi), "Guest"));  // BAD!
-//   auto f = absl::bind_front(Print, std::ref(hi), "Guest"); // BAD!
-//
-// Example: Storing reference-like types.
-//
-//   void Print(absl::string_view a, const std::string& b) {
-//     std::cerr << a << b;
-//   }
-//
-//   std::string hi = "Hello, ";
-//   // Copies "hi".
-//   absl::bind_front(Print, hi)("Chuk");
-//
-//   // Compile error: std::reference_wrapper<const string> is not implicitly
-//   // convertible to string_view.
-//   // absl::bind_front(Print, std::cref(hi))("Chuk");
-//
-//   // Doesn't copy "hi".
-//   absl::bind_front(Print, absl::string_view(hi))("Chuk");
-//
-template <class F, class... BoundArgs>
-constexpr functional_internal::bind_front_t<F, BoundArgs...> bind_front(
-    F&& func, BoundArgs&&... args) {
-  return functional_internal::bind_front_t<F, BoundArgs...>(
-      absl::in_place, absl::forward<F>(func),
-      absl::forward<BoundArgs>(args)...);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FUNCTIONAL_BIND_FRONT_H_
diff --git a/third_party/abseil_cpp/absl/functional/bind_front_test.cc b/third_party/abseil_cpp/absl/functional/bind_front_test.cc
deleted file mode 100644
index 4801a81caf..0000000000
--- a/third_party/abseil_cpp/absl/functional/bind_front_test.cc
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/functional/bind_front.h"
-
-#include <stddef.h>
-
-#include <functional>
-#include <memory>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/memory/memory.h"
-
-namespace {
-
-char CharAt(const char* s, size_t index) { return s[index]; }
-
-TEST(BindTest, Basics) {
-  EXPECT_EQ('C', absl::bind_front(CharAt)("ABC", 2));
-  EXPECT_EQ('C', absl::bind_front(CharAt, "ABC")(2));
-  EXPECT_EQ('C', absl::bind_front(CharAt, "ABC", 2)());
-}
-
-TEST(BindTest, Lambda) {
-  auto lambda = [](int x, int y, int z) { return x + y + z; };
-  EXPECT_EQ(6, absl::bind_front(lambda)(1, 2, 3));
-  EXPECT_EQ(6, absl::bind_front(lambda, 1)(2, 3));
-  EXPECT_EQ(6, absl::bind_front(lambda, 1, 2)(3));
-  EXPECT_EQ(6, absl::bind_front(lambda, 1, 2, 3)());
-}
-
-struct Functor {
-  std::string operator()() & { return "&"; }
-  std::string operator()() const& { return "const&"; }
-  std::string operator()() && { return "&&"; }
-  std::string operator()() const&& { return "const&&"; }
-};
-
-TEST(BindTest, PerfectForwardingOfBoundArgs) {
-  auto f = absl::bind_front(Functor());
-  const auto& cf = f;
-  EXPECT_EQ("&", f());
-  EXPECT_EQ("const&", cf());
-  EXPECT_EQ("&&", std::move(f)());
-  EXPECT_EQ("const&&", std::move(cf)());
-}
-
-struct ArgDescribe {
-  std::string operator()(int&) const { return "&"; }             // NOLINT
-  std::string operator()(const int&) const { return "const&"; }  // NOLINT
-  std::string operator()(int&&) const { return "&&"; }
-  std::string operator()(const int&&) const { return "const&&"; }
-};
-
-TEST(BindTest, PerfectForwardingOfFreeArgs) {
-  ArgDescribe f;
-  int i;
-  EXPECT_EQ("&", absl::bind_front(f)(static_cast<int&>(i)));
-  EXPECT_EQ("const&", absl::bind_front(f)(static_cast<const int&>(i)));
-  EXPECT_EQ("&&", absl::bind_front(f)(static_cast<int&&>(i)));
-  EXPECT_EQ("const&&", absl::bind_front(f)(static_cast<const int&&>(i)));
-}
-
-struct NonCopyableFunctor {
-  NonCopyableFunctor() = default;
-  NonCopyableFunctor(const NonCopyableFunctor&) = delete;
-  NonCopyableFunctor& operator=(const NonCopyableFunctor&) = delete;
-  const NonCopyableFunctor* operator()() const { return this; }
-};
-
-TEST(BindTest, RefToFunctor) {
-  // It won't copy/move the functor and use the original object.
-  NonCopyableFunctor ncf;
-  auto bound_ncf = absl::bind_front(std::ref(ncf));
-  auto bound_ncf_copy = bound_ncf;
-  EXPECT_EQ(&ncf, bound_ncf_copy());
-}
-
-struct Struct {
-  std::string value;
-};
-
-TEST(BindTest, StoreByCopy) {
-  Struct s = {"hello"};
-  auto f = absl::bind_front(&Struct::value, s);
-  auto g = f;
-  EXPECT_EQ("hello", f());
-  EXPECT_EQ("hello", g());
-  EXPECT_NE(&s.value, &f());
-  EXPECT_NE(&s.value, &g());
-  EXPECT_NE(&g(), &f());
-}
-
-struct NonCopyable {
-  explicit NonCopyable(const std::string& s) : value(s) {}
-  NonCopyable(const NonCopyable&) = delete;
-  NonCopyable& operator=(const NonCopyable&) = delete;
-
-  std::string value;
-};
-
-const std::string& GetNonCopyableValue(const NonCopyable& n) { return n.value; }
-
-TEST(BindTest, StoreByRef) {
-  NonCopyable s("hello");
-  auto f = absl::bind_front(&GetNonCopyableValue, std::ref(s));
-  EXPECT_EQ("hello", f());
-  EXPECT_EQ(&s.value, &f());
-  auto g = std::move(f);  // NOLINT
-  EXPECT_EQ("hello", g());
-  EXPECT_EQ(&s.value, &g());
-  s.value = "goodbye";
-  EXPECT_EQ("goodbye", g());
-}
-
-TEST(BindTest, StoreByCRef) {
-  NonCopyable s("hello");
-  auto f = absl::bind_front(&GetNonCopyableValue, std::cref(s));
-  EXPECT_EQ("hello", f());
-  EXPECT_EQ(&s.value, &f());
-  auto g = std::move(f);  // NOLINT
-  EXPECT_EQ("hello", g());
-  EXPECT_EQ(&s.value, &g());
-  s.value = "goodbye";
-  EXPECT_EQ("goodbye", g());
-}
-
-const std::string& GetNonCopyableValueByWrapper(
-    std::reference_wrapper<NonCopyable> n) {
-  return n.get().value;
-}
-
-TEST(BindTest, StoreByRefInvokeByWrapper) {
-  NonCopyable s("hello");
-  auto f = absl::bind_front(GetNonCopyableValueByWrapper, std::ref(s));
-  EXPECT_EQ("hello", f());
-  EXPECT_EQ(&s.value, &f());
-  auto g = std::move(f);
-  EXPECT_EQ("hello", g());
-  EXPECT_EQ(&s.value, &g());
-  s.value = "goodbye";
-  EXPECT_EQ("goodbye", g());
-}
-
-TEST(BindTest, StoreByPointer) {
-  NonCopyable s("hello");
-  auto f = absl::bind_front(&NonCopyable::value, &s);
-  EXPECT_EQ("hello", f());
-  EXPECT_EQ(&s.value, &f());
-  auto g = std::move(f);
-  EXPECT_EQ("hello", g());
-  EXPECT_EQ(&s.value, &g());
-}
-
-int Sink(std::unique_ptr<int> p) {
-  return *p;
-}
-
-std::unique_ptr<int> Factory(int n) { return absl::make_unique<int>(n); }
-
-TEST(BindTest, NonCopyableArg) {
-  EXPECT_EQ(42, absl::bind_front(Sink)(absl::make_unique<int>(42)));
-  EXPECT_EQ(42, absl::bind_front(Sink, absl::make_unique<int>(42))());
-}
-
-TEST(BindTest, NonCopyableResult) {
-  EXPECT_THAT(absl::bind_front(Factory)(42), ::testing::Pointee(42));
-  EXPECT_THAT(absl::bind_front(Factory, 42)(), ::testing::Pointee(42));
-}
-
-// is_copy_constructible<FalseCopyable<unique_ptr<T>> is true but an attempt to
-// instantiate the copy constructor leads to a compile error. This is similar
-// to how standard containers behave.
-template <class T>
-struct FalseCopyable {
-  FalseCopyable() {}
-  FalseCopyable(const FalseCopyable& other) : m(other.m) {}
-  FalseCopyable(FalseCopyable&& other) : m(std::move(other.m)) {}
-  T m;
-};
-
-int GetMember(FalseCopyable<std::unique_ptr<int>> x) { return *x.m; }
-
-TEST(BindTest, WrappedMoveOnly) {
-  FalseCopyable<std::unique_ptr<int>> x;
-  x.m = absl::make_unique<int>(42);
-  auto f = absl::bind_front(&GetMember, std::move(x));
-  EXPECT_EQ(42, std::move(f)());
-}
-
-int Plus(int a, int b) { return a + b; }
-
-TEST(BindTest, ConstExpr) {
-  constexpr auto f = absl::bind_front(CharAt);
-  EXPECT_EQ(f("ABC", 1), 'B');
-  static constexpr int five = 5;
-  constexpr auto plus5 = absl::bind_front(Plus, five);
-  EXPECT_EQ(plus5(1), 6);
-
-  // There seems to be a bug in MSVC dealing constexpr construction of
-  // char[]. Notice 'plus5' above; 'int' works just fine.
-#if !(defined(_MSC_VER) && _MSC_VER < 1910)
-  static constexpr char data[] = "DEF";
-  constexpr auto g = absl::bind_front(CharAt, data);
-  EXPECT_EQ(g(1), 'E');
-#endif
-}
-
-struct ManglingCall {
-  int operator()(int, double, std::string) const { return 0; }
-};
-
-TEST(BindTest, Mangling) {
-  // We just want to generate a particular instantiation to see its mangling.
-  absl::bind_front(ManglingCall{}, 1, 3.3)("A");
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/functional/function_ref.h b/third_party/abseil_cpp/absl/functional/function_ref.h
deleted file mode 100644
index 6e03ac2e04..0000000000
--- a/third_party/abseil_cpp/absl/functional/function_ref.h
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: function_ref.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the `absl::FunctionRef` type for holding a
-// non-owning reference to an object of any invocable type. This function
-// reference is typically most useful as a type-erased argument type for
-// accepting function types that neither take ownership nor copy the type; using
-// the reference type in this case avoids a copy and an allocation. Best
-// practices of other non-owning reference-like objects (such as
-// `absl::string_view`) apply here.
-//
-//  An `absl::FunctionRef` is similar in usage to a `std::function` but has the
-//  following differences:
-//
-//  * It doesn't own the underlying object.
-//  * It doesn't have a null or empty state.
-//  * It never performs deep copies or allocations.
-//  * It's much faster and cheaper to construct.
-//  * It's trivially copyable and destructable.
-//
-// Generally, `absl::FunctionRef` should not be used as a return value, data
-// member, or to initialize a `std::function`. Such usages will often lead to
-// problematic lifetime issues. Once you convert something to an
-// `absl::FunctionRef` you cannot make a deep copy later.
-//
-// This class is suitable for use wherever a "const std::function<>&"
-// would be used without making a copy. ForEach functions and other versions of
-// the visitor pattern are a good example of when this class should be used.
-//
-// This class is trivial to copy and should be passed by value.
-#ifndef ABSL_FUNCTIONAL_FUNCTION_REF_H_
-#define ABSL_FUNCTIONAL_FUNCTION_REF_H_
-
-#include <cassert>
-#include <functional>
-#include <type_traits>
-
-#include "absl/functional/internal/function_ref.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// FunctionRef
-//
-// Dummy class declaration to allow the partial specialization based on function
-// types below.
-template <typename T>
-class FunctionRef;
-
-// FunctionRef
-//
-// An `absl::FunctionRef` is a lightweight wrapper to any invokable object with
-// a compatible signature. Generally, an `absl::FunctionRef` should only be used
-// as an argument type and should be preferred as an argument over a const
-// reference to a `std::function`.
-//
-// Example:
-//
-//   // The following function takes a function callback by const reference
-//   bool Visitor(const std::function<void(my_proto&,
-//                                         absl::string_view)>& callback);
-//
-//   // Assuming that the function is not stored or otherwise copied, it can be
-//   // replaced by an `absl::FunctionRef`:
-//   bool Visitor(absl::FunctionRef<void(my_proto&, absl::string_view)>
-//                  callback);
-//
-// Note: the assignment operator within an `absl::FunctionRef` is intentionally
-// deleted to prevent misuse; because the `absl::FunctionRef` does not own the
-// underlying type, assignment likely indicates misuse.
-template <typename R, typename... Args>
-class FunctionRef<R(Args...)> {
- private:
-  // Used to disable constructors for objects that are not compatible with the
-  // signature of this FunctionRef.
-  template <typename F,
-            typename FR = absl::base_internal::invoke_result_t<F, Args&&...>>
-  using EnableIfCompatible =
-      typename std::enable_if<std::is_void<R>::value ||
-                              std::is_convertible<FR, R>::value>::type;
-
- public:
-  // Constructs a FunctionRef from any invokable type.
-  template <typename F, typename = EnableIfCompatible<const F&>>
-  FunctionRef(const F& f)  // NOLINT(runtime/explicit)
-      : invoker_(&absl::functional_internal::InvokeObject<F, R, Args...>) {
-    absl::functional_internal::AssertNonNull(f);
-    ptr_.obj = &f;
-  }
-
-  // Overload for function pointers. This eliminates a level of indirection that
-  // would happen if the above overload was used (it lets us store the pointer
-  // instead of a pointer to a pointer).
-  //
-  // This overload is also used for references to functions, since references to
-  // functions can decay to function pointers implicitly.
-  template <
-      typename F, typename = EnableIfCompatible<F*>,
-      absl::functional_internal::EnableIf<absl::is_function<F>::value> = 0>
-  FunctionRef(F* f)  // NOLINT(runtime/explicit)
-      : invoker_(&absl::functional_internal::InvokeFunction<F*, R, Args...>) {
-    assert(f != nullptr);
-    ptr_.fun = reinterpret_cast<decltype(ptr_.fun)>(f);
-  }
-
-  // To help prevent subtle lifetime bugs, FunctionRef is not assignable.
-  // Typically, it should only be used as an argument type.
-  FunctionRef& operator=(const FunctionRef& rhs) = delete;
-
-  // Call the underlying object.
-  R operator()(Args... args) const {
-    return invoker_(ptr_, std::forward<Args>(args)...);
-  }
-
- private:
-  absl::functional_internal::VoidPtr ptr_;
-  absl::functional_internal::Invoker<R, Args...> invoker_;
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FUNCTIONAL_FUNCTION_REF_H_
diff --git a/third_party/abseil_cpp/absl/functional/function_ref_benchmark.cc b/third_party/abseil_cpp/absl/functional/function_ref_benchmark.cc
deleted file mode 100644
index 045305bfef..0000000000
--- a/third_party/abseil_cpp/absl/functional/function_ref_benchmark.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/functional/function_ref.h"
-
-#include <memory>
-
-#include "benchmark/benchmark.h"
-#include "absl/base/attributes.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-int dummy = 0;
-
-void FreeFunction() { benchmark::DoNotOptimize(dummy); }
-
-struct TrivialFunctor {
-  void operator()() const { benchmark::DoNotOptimize(dummy); }
-};
-
-struct LargeFunctor {
-  void operator()() const { benchmark::DoNotOptimize(this); }
-  std::string a, b, c;
-};
-
-template <typename Function, typename... Args>
-void ABSL_ATTRIBUTE_NOINLINE CallFunction(Function f, Args&&... args) {
-  f(std::forward<Args>(args)...);
-}
-
-template <typename Function, typename Callable, typename... Args>
-void ConstructAndCallFunctionBenchmark(benchmark::State& state,
-                                       const Callable& c, Args&&... args) {
-  for (auto _ : state) {
-    CallFunction<Function>(c, std::forward<Args>(args)...);
-  }
-}
-
-void BM_TrivialStdFunction(benchmark::State& state) {
-  ConstructAndCallFunctionBenchmark<std::function<void()>>(state,
-                                                           TrivialFunctor{});
-}
-BENCHMARK(BM_TrivialStdFunction);
-
-void BM_TrivialFunctionRef(benchmark::State& state) {
-  ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state,
-                                                         TrivialFunctor{});
-}
-BENCHMARK(BM_TrivialFunctionRef);
-
-void BM_LargeStdFunction(benchmark::State& state) {
-  ConstructAndCallFunctionBenchmark<std::function<void()>>(state,
-                                                           LargeFunctor{});
-}
-BENCHMARK(BM_LargeStdFunction);
-
-void BM_LargeFunctionRef(benchmark::State& state) {
-  ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, LargeFunctor{});
-}
-BENCHMARK(BM_LargeFunctionRef);
-
-void BM_FunPtrStdFunction(benchmark::State& state) {
-  ConstructAndCallFunctionBenchmark<std::function<void()>>(state, FreeFunction);
-}
-BENCHMARK(BM_FunPtrStdFunction);
-
-void BM_FunPtrFunctionRef(benchmark::State& state) {
-  ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, FreeFunction);
-}
-BENCHMARK(BM_FunPtrFunctionRef);
-
-// Doesn't include construction or copy overhead in the loop.
-template <typename Function, typename Callable, typename... Args>
-void CallFunctionBenchmark(benchmark::State& state, const Callable& c,
-                           Args... args) {
-  Function f = c;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(&f);
-    f(args...);
-  }
-}
-
-struct FunctorWithTrivialArgs {
-  void operator()(int a, int b, int c) const {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(b);
-    benchmark::DoNotOptimize(c);
-  }
-};
-
-void BM_TrivialArgsStdFunction(benchmark::State& state) {
-  CallFunctionBenchmark<std::function<void(int, int, int)>>(
-      state, FunctorWithTrivialArgs{}, 1, 2, 3);
-}
-BENCHMARK(BM_TrivialArgsStdFunction);
-
-void BM_TrivialArgsFunctionRef(benchmark::State& state) {
-  CallFunctionBenchmark<FunctionRef<void(int, int, int)>>(
-      state, FunctorWithTrivialArgs{}, 1, 2, 3);
-}
-BENCHMARK(BM_TrivialArgsFunctionRef);
-
-struct FunctorWithNonTrivialArgs {
-  void operator()(std::string a, std::string b, std::string c) const {
-    benchmark::DoNotOptimize(&a);
-    benchmark::DoNotOptimize(&b);
-    benchmark::DoNotOptimize(&c);
-  }
-};
-
-void BM_NonTrivialArgsStdFunction(benchmark::State& state) {
-  std::string a, b, c;
-  CallFunctionBenchmark<
-      std::function<void(std::string, std::string, std::string)>>(
-      state, FunctorWithNonTrivialArgs{}, a, b, c);
-}
-BENCHMARK(BM_NonTrivialArgsStdFunction);
-
-void BM_NonTrivialArgsFunctionRef(benchmark::State& state) {
-  std::string a, b, c;
-  CallFunctionBenchmark<
-      FunctionRef<void(std::string, std::string, std::string)>>(
-      state, FunctorWithNonTrivialArgs{}, a, b, c);
-}
-BENCHMARK(BM_NonTrivialArgsFunctionRef);
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/functional/function_ref_test.cc b/third_party/abseil_cpp/absl/functional/function_ref_test.cc
deleted file mode 100644
index 3aa5974587..0000000000
--- a/third_party/abseil_cpp/absl/functional/function_ref_test.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/functional/function_ref.h"
-
-#include <memory>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/internal/test_instance_tracker.h"
-#include "absl/memory/memory.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-void RunFun(FunctionRef<void()> f) { f(); }
-
-TEST(FunctionRefTest, Lambda) {
-  bool ran = false;
-  RunFun([&] { ran = true; });
-  EXPECT_TRUE(ran);
-}
-
-int Function() { return 1337; }
-
-TEST(FunctionRefTest, Function1) {
-  FunctionRef<int()> ref(&Function);
-  EXPECT_EQ(1337, ref());
-}
-
-TEST(FunctionRefTest, Function2) {
-  FunctionRef<int()> ref(Function);
-  EXPECT_EQ(1337, ref());
-}
-
-int NoExceptFunction() noexcept { return 1337; }
-
-// TODO(jdennett): Add a test for noexcept member functions.
-TEST(FunctionRefTest, NoExceptFunction) {
-  FunctionRef<int()> ref(NoExceptFunction);
-  EXPECT_EQ(1337, ref());
-}
-
-TEST(FunctionRefTest, ForwardsArgs) {
-  auto l = [](std::unique_ptr<int> i) { return *i; };
-  FunctionRef<int(std::unique_ptr<int>)> ref(l);
-  EXPECT_EQ(42, ref(absl::make_unique<int>(42)));
-}
-
-TEST(FunctionRef, ReturnMoveOnly) {
-  auto l = [] { return absl::make_unique<int>(29); };
-  FunctionRef<std::unique_ptr<int>()> ref(l);
-  EXPECT_EQ(29, *ref());
-}
-
-TEST(FunctionRef, ManyArgs) {
-  auto l = [](int a, int b, int c) { return a + b + c; };
-  FunctionRef<int(int, int, int)> ref(l);
-  EXPECT_EQ(6, ref(1, 2, 3));
-}
-
-TEST(FunctionRef, VoidResultFromNonVoidFunctor) {
-  bool ran = false;
-  auto l = [&]() -> int {
-    ran = true;
-    return 2;
-  };
-  FunctionRef<void()> ref(l);
-  ref();
-  EXPECT_TRUE(ran);
-}
-
-TEST(FunctionRef, CastFromDerived) {
-  struct Base {};
-  struct Derived : public Base {};
-
-  Derived d;
-  auto l1 = [&](Base* b) { EXPECT_EQ(&d, b); };
-  FunctionRef<void(Derived*)> ref1(l1);
-  ref1(&d);
-
-  auto l2 = [&]() -> Derived* { return &d; };
-  FunctionRef<Base*()> ref2(l2);
-  EXPECT_EQ(&d, ref2());
-}
-
-TEST(FunctionRef, VoidResultFromNonVoidFuncton) {
-  FunctionRef<void()> ref(Function);
-  ref();
-}
-
-TEST(FunctionRef, MemberPtr) {
-  struct S {
-    int i;
-  };
-
-  S s{1100111};
-  auto mem_ptr = &S::i;
-  FunctionRef<int(const S& s)> ref(mem_ptr);
-  EXPECT_EQ(1100111, ref(s));
-}
-
-TEST(FunctionRef, MemberFun) {
-  struct S {
-    int i;
-    int get_i() const { return i; }
-  };
-
-  S s{22};
-  auto mem_fun_ptr = &S::get_i;
-  FunctionRef<int(const S& s)> ref(mem_fun_ptr);
-  EXPECT_EQ(22, ref(s));
-}
-
-TEST(FunctionRef, MemberFunRefqualified) {
-  struct S {
-    int i;
-    int get_i() && { return i; }
-  };
-  auto mem_fun_ptr = &S::get_i;
-  S s{22};
-  FunctionRef<int(S && s)> ref(mem_fun_ptr);
-  EXPECT_EQ(22, ref(std::move(s)));
-}
-
-#if !defined(_WIN32) && defined(GTEST_HAS_DEATH_TEST)
-
-TEST(FunctionRef, MemberFunRefqualifiedNull) {
-  struct S {
-    int i;
-    int get_i() && { return i; }
-  };
-  auto mem_fun_ptr = &S::get_i;
-  mem_fun_ptr = nullptr;
-  EXPECT_DEBUG_DEATH({ FunctionRef<int(S && s)> ref(mem_fun_ptr); }, "");
-}
-
-TEST(FunctionRef, NullMemberPtrAssertFails) {
-  struct S {
-    int i;
-  };
-  using MemberPtr = int S::*;
-  MemberPtr mem_ptr = nullptr;
-  EXPECT_DEBUG_DEATH({ FunctionRef<int(const S& s)> ref(mem_ptr); }, "");
-}
-
-#endif  // GTEST_HAS_DEATH_TEST
-
-TEST(FunctionRef, CopiesAndMovesPerPassByValue) {
-  absl::test_internal::InstanceTracker tracker;
-  absl::test_internal::CopyableMovableInstance instance(0);
-  auto l = [](absl::test_internal::CopyableMovableInstance) {};
-  FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l);
-  ref(instance);
-  EXPECT_EQ(tracker.copies(), 1);
-  EXPECT_EQ(tracker.moves(), 1);
-}
-
-TEST(FunctionRef, CopiesAndMovesPerPassByRef) {
-  absl::test_internal::InstanceTracker tracker;
-  absl::test_internal::CopyableMovableInstance instance(0);
-  auto l = [](const absl::test_internal::CopyableMovableInstance&) {};
-  FunctionRef<void(const absl::test_internal::CopyableMovableInstance&)> ref(l);
-  ref(instance);
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.moves(), 0);
-}
-
-TEST(FunctionRef, CopiesAndMovesPerPassByValueCallByMove) {
-  absl::test_internal::InstanceTracker tracker;
-  absl::test_internal::CopyableMovableInstance instance(0);
-  auto l = [](absl::test_internal::CopyableMovableInstance) {};
-  FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l);
-  ref(std::move(instance));
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.moves(), 2);
-}
-
-TEST(FunctionRef, CopiesAndMovesPerPassByValueToRef) {
-  absl::test_internal::InstanceTracker tracker;
-  absl::test_internal::CopyableMovableInstance instance(0);
-  auto l = [](const absl::test_internal::CopyableMovableInstance&) {};
-  FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l);
-  ref(std::move(instance));
-  EXPECT_EQ(tracker.copies(), 0);
-  EXPECT_EQ(tracker.moves(), 1);
-}
-
-TEST(FunctionRef, PassByValueTypes) {
-  using absl::functional_internal::Invoker;
-  using absl::functional_internal::VoidPtr;
-  using absl::test_internal::CopyableMovableInstance;
-  struct Trivial {
-    void* p[2];
-  };
-  struct LargeTrivial {
-    void* p[3];
-  };
-
-  static_assert(std::is_same<Invoker<void, int>, void (*)(VoidPtr, int)>::value,
-                "Scalar types should be passed by value");
-  static_assert(
-      std::is_same<Invoker<void, Trivial>, void (*)(VoidPtr, Trivial)>::value,
-      "Small trivial types should be passed by value");
-  static_assert(std::is_same<Invoker<void, LargeTrivial>,
-                             void (*)(VoidPtr, LargeTrivial &&)>::value,
-                "Large trivial types should be passed by rvalue reference");
-  static_assert(
-      std::is_same<Invoker<void, CopyableMovableInstance>,
-                   void (*)(VoidPtr, CopyableMovableInstance &&)>::value,
-      "Types with copy/move ctor should be passed by rvalue reference");
-
-  // References are passed as references.
-  static_assert(
-      std::is_same<Invoker<void, int&>, void (*)(VoidPtr, int&)>::value,
-      "Reference types should be preserved");
-  static_assert(
-      std::is_same<Invoker<void, CopyableMovableInstance&>,
-                   void (*)(VoidPtr, CopyableMovableInstance&)>::value,
-      "Reference types should be preserved");
-  static_assert(
-      std::is_same<Invoker<void, CopyableMovableInstance&&>,
-                   void (*)(VoidPtr, CopyableMovableInstance &&)>::value,
-      "Reference types should be preserved");
-
-  // Make sure the address of an object received by reference is the same as the
-  // addess of the object passed by the caller.
-  {
-    LargeTrivial obj;
-    auto test = [&obj](LargeTrivial& input) { ASSERT_EQ(&input, &obj); };
-    absl::FunctionRef<void(LargeTrivial&)> ref(test);
-    ref(obj);
-  }
-
-  {
-    Trivial obj;
-    auto test = [&obj](Trivial& input) { ASSERT_EQ(&input, &obj); };
-    absl::FunctionRef<void(Trivial&)> ref(test);
-    ref(obj);
-  }
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/functional/internal/front_binder.h b/third_party/abseil_cpp/absl/functional/internal/front_binder.h
deleted file mode 100644
index 45f52de73d..0000000000
--- a/third_party/abseil_cpp/absl/functional/internal/front_binder.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Implementation details for `absl::bind_front()`.
-
-#ifndef ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_
-#define ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_
-
-#include <cstddef>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/internal/invoke.h"
-#include "absl/container/internal/compressed_tuple.h"
-#include "absl/meta/type_traits.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace functional_internal {
-
-// Invoke the method, expanding the tuple of bound arguments.
-template <class R, class Tuple, size_t... Idx, class... Args>
-R Apply(Tuple&& bound, absl::index_sequence<Idx...>, Args&&... free) {
-  return base_internal::invoke(
-      absl::forward<Tuple>(bound).template get<Idx>()...,
-      absl::forward<Args>(free)...);
-}
-
-template <class F, class... BoundArgs>
-class FrontBinder {
-  using BoundArgsT = absl::container_internal::CompressedTuple<F, BoundArgs...>;
-  using Idx = absl::make_index_sequence<sizeof...(BoundArgs) + 1>;
-
-  BoundArgsT bound_args_;
-
- public:
-  template <class... Ts>
-  constexpr explicit FrontBinder(absl::in_place_t, Ts&&... ts)
-      : bound_args_(absl::forward<Ts>(ts)...) {}
-
-  template <class... FreeArgs, class R = base_internal::invoke_result_t<
-                                   F&, BoundArgs&..., FreeArgs&&...>>
-  R operator()(FreeArgs&&... free_args) & {
-    return functional_internal::Apply<R>(bound_args_, Idx(),
-                                         absl::forward<FreeArgs>(free_args)...);
-  }
-
-  template <class... FreeArgs,
-            class R = base_internal::invoke_result_t<
-                const F&, const BoundArgs&..., FreeArgs&&...>>
-  R operator()(FreeArgs&&... free_args) const& {
-    return functional_internal::Apply<R>(bound_args_, Idx(),
-                                         absl::forward<FreeArgs>(free_args)...);
-  }
-
-  template <class... FreeArgs, class R = base_internal::invoke_result_t<
-                                   F&&, BoundArgs&&..., FreeArgs&&...>>
-  R operator()(FreeArgs&&... free_args) && {
-    // This overload is called when *this is an rvalue. If some of the bound
-    // arguments are stored by value or rvalue reference, we move them.
-    return functional_internal::Apply<R>(absl::move(bound_args_), Idx(),
-                                         absl::forward<FreeArgs>(free_args)...);
-  }
-
-  template <class... FreeArgs,
-            class R = base_internal::invoke_result_t<
-                const F&&, const BoundArgs&&..., FreeArgs&&...>>
-  R operator()(FreeArgs&&... free_args) const&& {
-    // This overload is called when *this is an rvalue. If some of the bound
-    // arguments are stored by value or rvalue reference, we move them.
-    return functional_internal::Apply<R>(absl::move(bound_args_), Idx(),
-                                         absl::forward<FreeArgs>(free_args)...);
-  }
-};
-
-template <class F, class... BoundArgs>
-using bind_front_t = FrontBinder<decay_t<F>, absl::decay_t<BoundArgs>...>;
-
-}  // namespace functional_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_
diff --git a/third_party/abseil_cpp/absl/functional/internal/function_ref.h b/third_party/abseil_cpp/absl/functional/internal/function_ref.h
deleted file mode 100644
index b5bb8b430a..0000000000
--- a/third_party/abseil_cpp/absl/functional/internal/function_ref.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_
-#define ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_
-
-#include <cassert>
-#include <functional>
-#include <type_traits>
-
-#include "absl/base/internal/invoke.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace functional_internal {
-
-// Like a void* that can handle function pointers as well. The standard does not
-// allow function pointers to round-trip through void*, but void(*)() is fine.
-//
-// Note: It's important that this class remains trivial and is the same size as
-// a pointer, since this allows the compiler to perform tail-call optimizations
-// when the underlying function is a callable object with a matching signature.
-union VoidPtr {
-  const void* obj;
-  void (*fun)();
-};
-
-// Chooses the best type for passing T as an argument.
-// Attempt to be close to SystemV AMD64 ABI. Objects with trivial copy ctor are
-// passed by value.
-template <typename T>
-constexpr bool PassByValue() {
-  return !std::is_lvalue_reference<T>::value &&
-         absl::is_trivially_copy_constructible<T>::value &&
-         absl::is_trivially_copy_assignable<
-             typename std::remove_cv<T>::type>::value &&
-         std::is_trivially_destructible<T>::value &&
-         sizeof(T) <= 2 * sizeof(void*);
-}
-
-template <typename T>
-struct ForwardT : std::conditional<PassByValue<T>(), T, T&&> {};
-
-// An Invoker takes a pointer to the type-erased invokable object, followed by
-// the arguments that the invokable object expects.
-//
-// Note: The order of arguments here is an optimization, since member functions
-// have an implicit "this" pointer as their first argument, putting VoidPtr
-// first allows the compiler to perform tail-call optimization in many cases.
-template <typename R, typename... Args>
-using Invoker = R (*)(VoidPtr, typename ForwardT<Args>::type...);
-
-//
-// InvokeObject and InvokeFunction provide static "Invoke" functions that can be
-// used as Invokers for objects or functions respectively.
-//
-// static_cast<R> handles the case the return type is void.
-template <typename Obj, typename R, typename... Args>
-R InvokeObject(VoidPtr ptr, typename ForwardT<Args>::type... args) {
-  auto o = static_cast<const Obj*>(ptr.obj);
-  return static_cast<R>(
-      absl::base_internal::invoke(*o, std::forward<Args>(args)...));
-}
-
-template <typename Fun, typename R, typename... Args>
-R InvokeFunction(VoidPtr ptr, typename ForwardT<Args>::type... args) {
-  auto f = reinterpret_cast<Fun>(ptr.fun);
-  return static_cast<R>(
-      absl::base_internal::invoke(f, std::forward<Args>(args)...));
-}
-
-template <typename Sig>
-void AssertNonNull(const std::function<Sig>& f) {
-  assert(f != nullptr);
-  (void)f;
-}
-
-template <typename F>
-void AssertNonNull(const F&) {}
-
-template <typename F, typename C>
-void AssertNonNull(F C::*f) {
-  assert(f != nullptr);
-  (void)f;
-}
-
-template <bool C>
-using EnableIf = typename ::std::enable_if<C, int>::type;
-
-}  // namespace functional_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_
diff --git a/third_party/abseil_cpp/absl/hash/BUILD.bazel b/third_party/abseil_cpp/absl/hash/BUILD.bazel
deleted file mode 100644
index 5b1e2d01fd..0000000000
--- a/third_party/abseil_cpp/absl/hash/BUILD.bazel
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "hash",
-    srcs = [
-        "internal/hash.cc",
-        "internal/hash.h",
-    ],
-    hdrs = ["hash.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":city",
-        "//absl/base:core_headers",
-        "//absl/base:endian",
-        "//absl/container:fixed_array",
-        "//absl/meta:type_traits",
-        "//absl/numeric:int128",
-        "//absl/strings",
-        "//absl/types:optional",
-        "//absl/types:variant",
-        "//absl/utility",
-    ],
-)
-
-cc_library(
-    name = "hash_testing",
-    testonly = 1,
-    hdrs = ["hash_testing.h"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":spy_hash_state",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "//absl/types:variant",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "hash_test",
-    srcs = ["hash_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":hash",
-        ":hash_testing",
-        ":spy_hash_state",
-        "//absl/base:core_headers",
-        "//absl/container:flat_hash_set",
-        "//absl/meta:type_traits",
-        "//absl/numeric:int128",
-        "//absl/strings:cord_test_helpers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "spy_hash_state",
-    testonly = 1,
-    hdrs = ["internal/spy_hash_state.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":hash",
-        "//absl/strings",
-        "//absl/strings:str_format",
-    ],
-)
-
-cc_library(
-    name = "city",
-    srcs = ["internal/city.cc"],
-    hdrs = [
-        "internal/city.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:endian",
-    ],
-)
-
-cc_test(
-    name = "city_test",
-    srcs = ["internal/city_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":city",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/hash/CMakeLists.txt b/third_party/abseil_cpp/absl/hash/CMakeLists.txt
deleted file mode 100644
index 61365e9bb5..0000000000
--- a/third_party/abseil_cpp/absl/hash/CMakeLists.txt
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# Copyright 2018 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    hash
-  HDRS
-    "hash.h"
-  SRCS
-    "internal/hash.cc"
-    "internal/hash.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::core_headers
-    absl::endian
-    absl::fixed_array
-    absl::meta
-    absl::int128
-    absl::strings
-    absl::optional
-    absl::variant
-    absl::utility
-    absl::city
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    hash_testing
-  HDRS
-    "hash_testing.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::spy_hash_state
-    absl::meta
-    absl::strings
-    absl::variant
-    gmock
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    hash_test
-  SRCS
-    "hash_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::cord_test_helpers
-    absl::hash
-    absl::hash_testing
-    absl::core_headers
-    absl::flat_hash_set
-    absl::spy_hash_state
-    absl::meta
-    absl::int128
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    spy_hash_state
-  HDRS
-    "internal/spy_hash_state.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::hash
-    absl::strings
-    absl::str_format
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    city
-  HDRS
-    "internal/city.h"
-  SRCS
-    "internal/city.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::endian
-)
-
-absl_cc_test(
-  NAME
-    city_test
-  SRCS
-    "internal/city_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::city
-    gmock_main
-)
-
diff --git a/third_party/abseil_cpp/absl/hash/hash.h b/third_party/abseil_cpp/absl/hash/hash.h
deleted file mode 100644
index 5de132cac8..0000000000
--- a/third_party/abseil_cpp/absl/hash/hash.h
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: hash.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the Abseil `hash` library and the Abseil hashing
-// framework. This framework consists of the following:
-//
-//   * The `absl::Hash` functor, which is used to invoke the hasher within the
-//     Abseil hashing framework. `absl::Hash<T>` supports most basic types and
-//     a number of Abseil types out of the box.
-//   * `AbslHashValue`, an extension point that allows you to extend types to
-//     support Abseil hashing without requiring you to define a hashing
-//     algorithm.
-//   * `HashState`, a type-erased class which implements the manipulation of the
-//     hash state (H) itself, contains member functions `combine()` and
-//     `combine_contiguous()`, which you can use to contribute to an existing
-//     hash state when hashing your types.
-//
-// Unlike `std::hash` or other hashing frameworks, the Abseil hashing framework
-// provides most of its utility by abstracting away the hash algorithm (and its
-// implementation) entirely. Instead, a type invokes the Abseil hashing
-// framework by simply combining its state with the state of known, hashable
-// types. Hashing of that combined state is separately done by `absl::Hash`.
-//
-// One should assume that a hash algorithm is chosen randomly at the start of
-// each process.  E.g., `absl::Hash<int>{}(9)` in one process and
-// `absl::Hash<int>{}(9)` in another process are likely to differ.
-//
-// `absl::Hash` is intended to strongly mix input bits with a target of passing
-// an [Avalanche Test](https://en.wikipedia.org/wiki/Avalanche_effect).
-//
-// Example:
-//
-//   // Suppose we have a class `Circle` for which we want to add hashing:
-//   class Circle {
-//    public:
-//     ...
-//    private:
-//     std::pair<int, int> center_;
-//     int radius_;
-//   };
-//
-//   // To add hashing support to `Circle`, we simply need to add a free
-//   // (non-member) function `AbslHashValue()`, and return the combined hash
-//   // state of the existing hash state and the class state. You can add such a
-//   // free function using a friend declaration within the body of the class:
-//   class Circle {
-//    public:
-//     ...
-//     template <typename H>
-//     friend H AbslHashValue(H h, const Circle& c) {
-//       return H::combine(std::move(h), c.center_, c.radius_);
-//     }
-//     ...
-//   };
-//
-// For more information, see Adding Type Support to `absl::Hash` below.
-//
-#ifndef ABSL_HASH_HASH_H_
-#define ABSL_HASH_HASH_H_
-
-#include "absl/hash/internal/hash.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// `absl::Hash`
-// -----------------------------------------------------------------------------
-//
-// `absl::Hash<T>` is a convenient general-purpose hash functor for any type `T`
-// satisfying any of the following conditions (in order):
-//
-//  * T is an arithmetic or pointer type
-//  * T defines an overload for `AbslHashValue(H, const T&)` for an arbitrary
-//    hash state `H`.
-//  - T defines a specialization of `std::hash<T>`
-//
-// `absl::Hash` intrinsically supports the following types:
-//
-//   * All integral types (including bool)
-//   * All enum types
-//   * All floating-point types (although hashing them is discouraged)
-//   * All pointer types, including nullptr_t
-//   * std::pair<T1, T2>, if T1 and T2 are hashable
-//   * std::tuple<Ts...>, if all the Ts... are hashable
-//   * std::unique_ptr and std::shared_ptr
-//   * All string-like types including:
-//     * absl::Cord
-//     * std::string
-//     * std::string_view (as well as any instance of std::basic_string that
-//       uses char and std::char_traits)
-//  * All the standard sequence containers (provided the elements are hashable)
-//  * All the standard ordered associative containers (provided the elements are
-//    hashable)
-//  * absl types such as the following:
-//    * absl::string_view
-//    * absl::InlinedVector
-//    * absl::FixedArray
-//    * absl::uint128
-//    * absl::Time, absl::Duration, and absl::TimeZone
-//
-// Note: the list above is not meant to be exhaustive. Additional type support
-// may be added, in which case the above list will be updated.
-//
-// -----------------------------------------------------------------------------
-// absl::Hash Invocation Evaluation
-// -----------------------------------------------------------------------------
-//
-// When invoked, `absl::Hash<T>` searches for supplied hash functions in the
-// following order:
-//
-//   * Natively supported types out of the box (see above)
-//   * Types for which an `AbslHashValue()` overload is provided (such as
-//     user-defined types). See "Adding Type Support to `absl::Hash`" below.
-//   * Types which define a `std::hash<T>` specialization
-//
-// The fallback to legacy hash functions exists mainly for backwards
-// compatibility. If you have a choice, prefer defining an `AbslHashValue`
-// overload instead of specializing any legacy hash functors.
-//
-// -----------------------------------------------------------------------------
-// The Hash State Concept, and using `HashState` for Type Erasure
-// -----------------------------------------------------------------------------
-//
-// The `absl::Hash` framework relies on the Concept of a "hash state." Such a
-// hash state is used in several places:
-//
-// * Within existing implementations of `absl::Hash<T>` to store the hashed
-//   state of an object. Note that it is up to the implementation how it stores
-//   such state. A hash table, for example, may mix the state to produce an
-//   integer value; a testing framework may simply hold a vector of that state.
-// * Within implementations of `AbslHashValue()` used to extend user-defined
-//   types. (See "Adding Type Support to absl::Hash" below.)
-// * Inside a `HashState`, providing type erasure for the concept of a hash
-//   state, which you can use to extend the `absl::Hash` framework for types
-//   that are otherwise difficult to extend using `AbslHashValue()`. (See the
-//   `HashState` class below.)
-//
-// The "hash state" concept contains two member functions for mixing hash state:
-//
-// * `H::combine(state, values...)`
-//
-//   Combines an arbitrary number of values into a hash state, returning the
-//   updated state. Note that the existing hash state is move-only and must be
-//   passed by value.
-//
-//   Each of the value types T must be hashable by H.
-//
-//   NOTE:
-//
-//     state = H::combine(std::move(state), value1, value2, value3);
-//
-//   must be guaranteed to produce the same hash expansion as
-//
-//     state = H::combine(std::move(state), value1);
-//     state = H::combine(std::move(state), value2);
-//     state = H::combine(std::move(state), value3);
-//
-// * `H::combine_contiguous(state, data, size)`
-//
-//    Combines a contiguous array of `size` elements into a hash state,
-//    returning the updated state. Note that the existing hash state is
-//    move-only and must be passed by value.
-//
-//    NOTE:
-//
-//      state = H::combine_contiguous(std::move(state), data, size);
-//
-//    need NOT be guaranteed to produce the same hash expansion as a loop
-//    (it may perform internal optimizations). If you need this guarantee, use a
-//    loop instead.
-//
-// -----------------------------------------------------------------------------
-// Adding Type Support to `absl::Hash`
-// -----------------------------------------------------------------------------
-//
-// To add support for your user-defined type, add a proper `AbslHashValue()`
-// overload as a free (non-member) function. The overload will take an
-// existing hash state and should combine that state with state from the type.
-//
-// Example:
-//
-//   template <typename H>
-//   H AbslHashValue(H state, const MyType& v) {
-//     return H::combine(std::move(state), v.field1, ..., v.fieldN);
-//   }
-//
-// where `(field1, ..., fieldN)` are the members you would use on your
-// `operator==` to define equality.
-//
-// Notice that `AbslHashValue` is not a class member, but an ordinary function.
-// An `AbslHashValue` overload for a type should only be declared in the same
-// file and namespace as said type. The proper `AbslHashValue` implementation
-// for a given type will be discovered via ADL.
-//
-// Note: unlike `std::hash', `absl::Hash` should never be specialized. It must
-// only be extended by adding `AbslHashValue()` overloads.
-//
-template <typename T>
-using Hash = absl::hash_internal::Hash<T>;
-
-// HashState
-//
-// A type erased version of the hash state concept, for use in user-defined
-// `AbslHashValue` implementations that can't use templates (such as PImpl
-// classes, virtual functions, etc.). The type erasure adds overhead so it
-// should be avoided unless necessary.
-//
-// Note: This wrapper will only erase calls to:
-//     combine_contiguous(H, const unsigned char*, size_t)
-//
-// All other calls will be handled internally and will not invoke overloads
-// provided by the wrapped class.
-//
-// Users of this class should still define a template `AbslHashValue` function,
-// but can use `absl::HashState::Create(&state)` to erase the type of the hash
-// state and dispatch to their private hashing logic.
-//
-// This state can be used like any other hash state. In particular, you can call
-// `HashState::combine()` and `HashState::combine_contiguous()` on it.
-//
-// Example:
-//
-//   class Interface {
-//    public:
-//     template <typename H>
-//     friend H AbslHashValue(H state, const Interface& value) {
-//       state = H::combine(std::move(state), std::type_index(typeid(*this)));
-//       value.HashValue(absl::HashState::Create(&state));
-//       return state;
-//     }
-//    private:
-//     virtual void HashValue(absl::HashState state) const = 0;
-//   };
-//
-//   class Impl : Interface {
-//    private:
-//     void HashValue(absl::HashState state) const override {
-//       absl::HashState::combine(std::move(state), v1_, v2_);
-//     }
-//     int v1_;
-//     std::string v2_;
-//   };
-class HashState : public hash_internal::HashStateBase<HashState> {
- public:
-  // HashState::Create()
-  //
-  // Create a new `HashState` instance that wraps `state`. All calls to
-  // `combine()` and `combine_contiguous()` on the new instance will be
-  // redirected to the original `state` object. The `state` object must outlive
-  // the `HashState` instance.
-  template <typename T>
-  static HashState Create(T* state) {
-    HashState s;
-    s.Init(state);
-    return s;
-  }
-
-  HashState(const HashState&) = delete;
-  HashState& operator=(const HashState&) = delete;
-  HashState(HashState&&) = default;
-  HashState& operator=(HashState&&) = default;
-
-  // HashState::combine()
-  //
-  // Combines an arbitrary number of values into a hash state, returning the
-  // updated state.
-  using HashState::HashStateBase::combine;
-
-  // HashState::combine_contiguous()
-  //
-  // Combines a contiguous array of `size` elements into a hash state, returning
-  // the updated state.
-  static HashState combine_contiguous(HashState hash_state,
-                                      const unsigned char* first, size_t size) {
-    hash_state.combine_contiguous_(hash_state.state_, first, size);
-    return hash_state;
-  }
-  using HashState::HashStateBase::combine_contiguous;
-
- private:
-  HashState() = default;
-
-  template <typename T>
-  static void CombineContiguousImpl(void* p, const unsigned char* first,
-                                    size_t size) {
-    T& state = *static_cast<T*>(p);
-    state = T::combine_contiguous(std::move(state), first, size);
-  }
-
-  template <typename T>
-  void Init(T* state) {
-    state_ = state;
-    combine_contiguous_ = &CombineContiguousImpl<T>;
-  }
-
-  // Do not erase an already erased state.
-  void Init(HashState* state) {
-    state_ = state->state_;
-    combine_contiguous_ = state->combine_contiguous_;
-  }
-
-  void* state_;
-  void (*combine_contiguous_)(void*, const unsigned char*, size_t);
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HASH_HASH_H_
diff --git a/third_party/abseil_cpp/absl/hash/hash_test.cc b/third_party/abseil_cpp/absl/hash/hash_test.cc
deleted file mode 100644
index 1d2e6cf0df..0000000000
--- a/third_party/abseil_cpp/absl/hash/hash_test.cc
+++ /dev/null
@@ -1,976 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/hash/hash.h"
-
-#include <array>
-#include <bitset>
-#include <cstring>
-#include <deque>
-#include <forward_list>
-#include <functional>
-#include <iterator>
-#include <limits>
-#include <list>
-#include <map>
-#include <memory>
-#include <numeric>
-#include <random>
-#include <set>
-#include <string>
-#include <tuple>
-#include <type_traits>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/flat_hash_set.h"
-#include "absl/hash/hash_testing.h"
-#include "absl/hash/internal/spy_hash_state.h"
-#include "absl/meta/type_traits.h"
-#include "absl/numeric/int128.h"
-#include "absl/strings/cord_test_helpers.h"
-
-namespace {
-
-using absl::Hash;
-using absl::hash_internal::SpyHashState;
-
-template <typename T>
-class HashValueIntTest : public testing::Test {
-};
-TYPED_TEST_SUITE_P(HashValueIntTest);
-
-template <typename T>
-SpyHashState SpyHash(const T& value) {
-  return SpyHashState::combine(SpyHashState(), value);
-}
-
-// Helper trait to verify if T is hashable. We use absl::Hash's poison status to
-// detect it.
-template <typename T>
-using is_hashable = std::is_default_constructible<absl::Hash<T>>;
-
-TYPED_TEST_P(HashValueIntTest, BasicUsage) {
-  EXPECT_TRUE((is_hashable<TypeParam>::value));
-
-  TypeParam n = 42;
-  EXPECT_EQ(SpyHash(n), SpyHash(TypeParam{42}));
-  EXPECT_NE(SpyHash(n), SpyHash(TypeParam{0}));
-  EXPECT_NE(SpyHash(std::numeric_limits<TypeParam>::max()),
-            SpyHash(std::numeric_limits<TypeParam>::min()));
-}
-
-TYPED_TEST_P(HashValueIntTest, FastPath) {
-  // Test the fast-path to make sure the values are the same.
-  TypeParam n = 42;
-  EXPECT_EQ(absl::Hash<TypeParam>{}(n),
-            absl::Hash<std::tuple<TypeParam>>{}(std::tuple<TypeParam>(n)));
-}
-
-REGISTER_TYPED_TEST_CASE_P(HashValueIntTest, BasicUsage, FastPath);
-using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t,
-                                uint32_t, uint64_t, size_t>;
-INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueIntTest, IntTypes);
-
-enum LegacyEnum { kValue1, kValue2, kValue3 };
-
-enum class EnumClass { kValue4, kValue5, kValue6 };
-
-TEST(HashValueTest, EnumAndBool) {
-  EXPECT_TRUE((is_hashable<LegacyEnum>::value));
-  EXPECT_TRUE((is_hashable<EnumClass>::value));
-  EXPECT_TRUE((is_hashable<bool>::value));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      LegacyEnum::kValue1, LegacyEnum::kValue2, LegacyEnum::kValue3)));
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      EnumClass::kValue4, EnumClass::kValue5, EnumClass::kValue6)));
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(true, false)));
-}
-
-TEST(HashValueTest, FloatingPoint) {
-  EXPECT_TRUE((is_hashable<float>::value));
-  EXPECT_TRUE((is_hashable<double>::value));
-  EXPECT_TRUE((is_hashable<long double>::value));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(42.f, 0.f, -0.f, std::numeric_limits<float>::infinity(),
-                      -std::numeric_limits<float>::infinity())));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(42., 0., -0., std::numeric_limits<double>::infinity(),
-                      -std::numeric_limits<double>::infinity())));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      // Add some values with small exponent to test that NORMAL values also
-      // append their category.
-      .5L, 1.L, 2.L, 4.L, 42.L, 0.L, -0.L,
-      17 * static_cast<long double>(std::numeric_limits<double>::max()),
-      std::numeric_limits<long double>::infinity(),
-      -std::numeric_limits<long double>::infinity())));
-}
-
-TEST(HashValueTest, Pointer) {
-  EXPECT_TRUE((is_hashable<int*>::value));
-
-  int i;
-  int* ptr = &i;
-  int* n = nullptr;
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(&i, ptr, nullptr, ptr + 1, n)));
-}
-
-TEST(HashValueTest, PointerAlignment) {
-  // We want to make sure that pointer alignment will not cause bits to be
-  // stuck.
-
-  constexpr size_t kTotalSize = 1 << 20;
-  std::unique_ptr<char[]> data(new char[kTotalSize]);
-  constexpr size_t kLog2NumValues = 5;
-  constexpr size_t kNumValues = 1 << kLog2NumValues;
-
-  for (size_t align = 1; align < kTotalSize / kNumValues;
-       align < 8 ? align += 1 : align < 1024 ? align += 8 : align += 32) {
-    SCOPED_TRACE(align);
-    ASSERT_LE(align * kNumValues, kTotalSize);
-
-    size_t bits_or = 0;
-    size_t bits_and = ~size_t{};
-
-    for (size_t i = 0; i < kNumValues; ++i) {
-      size_t hash = absl::Hash<void*>()(data.get() + i * align);
-      bits_or |= hash;
-      bits_and &= hash;
-    }
-
-    // Limit the scope to the bits we would be using for Swisstable.
-    constexpr size_t kMask = (1 << (kLog2NumValues + 7)) - 1;
-    size_t stuck_bits = (~bits_or | bits_and) & kMask;
-    EXPECT_EQ(stuck_bits, 0) << "0x" << std::hex << stuck_bits;
-  }
-}
-
-TEST(HashValueTest, PairAndTuple) {
-  EXPECT_TRUE((is_hashable<std::pair<int, int>>::value));
-  EXPECT_TRUE((is_hashable<std::pair<const int&, const int&>>::value));
-  EXPECT_TRUE((is_hashable<std::tuple<int&, int&>>::value));
-  EXPECT_TRUE((is_hashable<std::tuple<int&&, int&&>>::value));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      std::make_pair(0, 42), std::make_pair(0, 42), std::make_pair(42, 0),
-      std::make_pair(0, 0), std::make_pair(42, 42), std::make_pair(1, 42))));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(std::make_tuple(0, 0, 0), std::make_tuple(0, 0, 42),
-                      std::make_tuple(0, 23, 0), std::make_tuple(17, 0, 0),
-                      std::make_tuple(42, 0, 0), std::make_tuple(3, 9, 9),
-                      std::make_tuple(0, 0, -42))));
-
-  // Test that tuples of lvalue references work (so we need a few lvalues):
-  int a = 0, b = 1, c = 17, d = 23;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      std::tie(a, a), std::tie(a, b), std::tie(b, c), std::tie(c, d))));
-
-  // Test that tuples of rvalue references work:
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      std::forward_as_tuple(0, 0, 0), std::forward_as_tuple(0, 0, 42),
-      std::forward_as_tuple(0, 23, 0), std::forward_as_tuple(17, 0, 0),
-      std::forward_as_tuple(42, 0, 0), std::forward_as_tuple(3, 9, 9),
-      std::forward_as_tuple(0, 0, -42))));
-}
-
-TEST(HashValueTest, CombineContiguousWorks) {
-  std::vector<std::tuple<int>> v1 = {std::make_tuple(1), std::make_tuple(3)};
-  std::vector<std::tuple<int>> v2 = {std::make_tuple(1), std::make_tuple(2)};
-
-  auto vh1 = SpyHash(v1);
-  auto vh2 = SpyHash(v2);
-  EXPECT_NE(vh1, vh2);
-}
-
-struct DummyDeleter {
-  template <typename T>
-  void operator() (T* ptr) {}
-};
-
-struct SmartPointerEq {
-  template <typename T, typename U>
-  bool operator()(const T& t, const U& u) const {
-    return GetPtr(t) == GetPtr(u);
-  }
-
-  template <typename T>
-  static auto GetPtr(const T& t) -> decltype(&*t) {
-    return t ? &*t : nullptr;
-  }
-
-  static std::nullptr_t GetPtr(std::nullptr_t) { return nullptr; }
-};
-
-TEST(HashValueTest, SmartPointers) {
-  EXPECT_TRUE((is_hashable<std::unique_ptr<int>>::value));
-  EXPECT_TRUE((is_hashable<std::unique_ptr<int, DummyDeleter>>::value));
-  EXPECT_TRUE((is_hashable<std::shared_ptr<int>>::value));
-
-  int i, j;
-  std::unique_ptr<int, DummyDeleter> unique1(&i);
-  std::unique_ptr<int, DummyDeleter> unique2(&i);
-  std::unique_ptr<int, DummyDeleter> unique_other(&j);
-  std::unique_ptr<int, DummyDeleter> unique_null;
-
-  std::shared_ptr<int> shared1(&i, DummyDeleter());
-  std::shared_ptr<int> shared2(&i, DummyDeleter());
-  std::shared_ptr<int> shared_other(&j, DummyDeleter());
-  std::shared_ptr<int> shared_null;
-
-  // Sanity check of the Eq function.
-  ASSERT_TRUE(SmartPointerEq{}(unique1, shared1));
-  ASSERT_FALSE(SmartPointerEq{}(unique1, shared_other));
-  ASSERT_TRUE(SmartPointerEq{}(unique_null, nullptr));
-  ASSERT_FALSE(SmartPointerEq{}(shared2, nullptr));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::forward_as_tuple(&i, nullptr,                    //
-                            unique1, unique2, unique_null,  //
-                            absl::make_unique<int>(),       //
-                            shared1, shared2, shared_null,  //
-                            std::make_shared<int>()),
-      SmartPointerEq{}));
-}
-
-TEST(HashValueTest, FunctionPointer) {
-  using Func = int (*)();
-  EXPECT_TRUE(is_hashable<Func>::value);
-
-  Func p1 = [] { return 2; }, p2 = [] { return 1; };
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(p1, p2, nullptr)));
-}
-
-struct WrapInTuple {
-  template <typename T>
-  std::tuple<int, T, size_t> operator()(const T& t) const {
-    return std::make_tuple(7, t, 0xdeadbeef);
-  }
-};
-
-absl::Cord FlatCord(absl::string_view sv) {
-  absl::Cord c(sv);
-  c.Flatten();
-  return c;
-}
-
-absl::Cord FragmentedCord(absl::string_view sv) {
-  if (sv.size() < 2) {
-    return absl::Cord(sv);
-  }
-  size_t halfway = sv.size() / 2;
-  std::vector<absl::string_view> parts = {sv.substr(0, halfway),
-                                          sv.substr(halfway)};
-  return absl::MakeFragmentedCord(parts);
-}
-
-TEST(HashValueTest, Strings) {
-  EXPECT_TRUE((is_hashable<std::string>::value));
-
-  const std::string small = "foo";
-  const std::string dup = "foofoo";
-  const std::string large = std::string(2048, 'x');  // multiple of chunk size
-  const std::string huge = std::string(5000, 'a');   // not a multiple
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(  //
-      std::string(), absl::string_view(), absl::Cord(),                     //
-      std::string(""), absl::string_view(""), absl::Cord(""),               //
-      std::string(small), absl::string_view(small), absl::Cord(small),      //
-      std::string(dup), absl::string_view(dup), absl::Cord(dup),            //
-      std::string(large), absl::string_view(large), absl::Cord(large),      //
-      std::string(huge), absl::string_view(huge), FlatCord(huge),           //
-      FragmentedCord(huge))));
-
-  // Also check that nested types maintain the same hash.
-  const WrapInTuple t{};
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(  //
-      t(std::string()), t(absl::string_view()), t(absl::Cord()),            //
-      t(std::string("")), t(absl::string_view("")), t(absl::Cord("")),      //
-      t(std::string(small)), t(absl::string_view(small)),                   //
-          t(absl::Cord(small)),                                             //
-      t(std::string(dup)), t(absl::string_view(dup)), t(absl::Cord(dup)),   //
-      t(std::string(large)), t(absl::string_view(large)),                   //
-          t(absl::Cord(large)),                                             //
-      t(std::string(huge)), t(absl::string_view(huge)),                     //
-          t(FlatCord(huge)), t(FragmentedCord(huge)))));
-
-  // Make sure that hashing a `const char*` does not use its string-value.
-  EXPECT_NE(SpyHash(static_cast<const char*>("ABC")),
-            SpyHash(absl::string_view("ABC")));
-}
-
-TEST(HashValueTest, WString) {
-  EXPECT_TRUE((is_hashable<std::wstring>::value));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      std::wstring(), std::wstring(L"ABC"), std::wstring(L"ABC"),
-      std::wstring(L"Some other different string"),
-      std::wstring(L"IñtërnÒtiônàlizætiøn"))));
-}
-
-TEST(HashValueTest, U16String) {
-  EXPECT_TRUE((is_hashable<std::u16string>::value));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      std::u16string(), std::u16string(u"ABC"), std::u16string(u"ABC"),
-      std::u16string(u"Some other different string"),
-      std::u16string(u"IñtërnÒtiônàlizætiøn"))));
-}
-
-TEST(HashValueTest, U32String) {
-  EXPECT_TRUE((is_hashable<std::u32string>::value));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      std::u32string(), std::u32string(U"ABC"), std::u32string(U"ABC"),
-      std::u32string(U"Some other different string"),
-      std::u32string(U"IñtërnÒtiônàlizætiøn"))));
-}
-
-TEST(HashValueTest, StdArray) {
-  EXPECT_TRUE((is_hashable<std::array<int, 3>>::value));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(std::array<int, 3>{}, std::array<int, 3>{{0, 23, 42}})));
-}
-
-TEST(HashValueTest, StdBitset) {
-  EXPECT_TRUE((is_hashable<std::bitset<257>>::value));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      {std::bitset<2>("00"), std::bitset<2>("01"), std::bitset<2>("10"),
-       std::bitset<2>("11")}));
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      {std::bitset<5>("10101"), std::bitset<5>("10001"), std::bitset<5>()}));
-
-  constexpr int kNumBits = 256;
-  std::array<std::string, 6> bit_strings;
-  bit_strings.fill(std::string(kNumBits, '1'));
-  bit_strings[1][0] = '0';
-  bit_strings[2][1] = '0';
-  bit_strings[3][kNumBits / 3] = '0';
-  bit_strings[4][kNumBits - 2] = '0';
-  bit_strings[5][kNumBits - 1] = '0';
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      {std::bitset<kNumBits>(bit_strings[0].c_str()),
-       std::bitset<kNumBits>(bit_strings[1].c_str()),
-       std::bitset<kNumBits>(bit_strings[2].c_str()),
-       std::bitset<kNumBits>(bit_strings[3].c_str()),
-       std::bitset<kNumBits>(bit_strings[4].c_str()),
-       std::bitset<kNumBits>(bit_strings[5].c_str())}));
-}  // namespace
-
-template <typename T>
-class HashValueSequenceTest : public testing::Test {
-};
-TYPED_TEST_SUITE_P(HashValueSequenceTest);
-
-TYPED_TEST_P(HashValueSequenceTest, BasicUsage) {
-  EXPECT_TRUE((is_hashable<TypeParam>::value));
-
-  using ValueType = typename TypeParam::value_type;
-  auto a = static_cast<ValueType>(0);
-  auto b = static_cast<ValueType>(23);
-  auto c = static_cast<ValueType>(42);
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(TypeParam(), TypeParam{}, TypeParam{a, b, c},
-                      TypeParam{a, b}, TypeParam{b, c})));
-}
-
-REGISTER_TYPED_TEST_CASE_P(HashValueSequenceTest, BasicUsage);
-using IntSequenceTypes =
-    testing::Types<std::deque<int>, std::forward_list<int>, std::list<int>,
-                   std::vector<int>, std::vector<bool>, std::set<int>,
-                   std::multiset<int>>;
-INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueSequenceTest, IntSequenceTypes);
-
-// Private type that only supports AbslHashValue to make sure our chosen hash
-// implementation is recursive within absl::Hash.
-// It uses std::abs() on the value to provide different bitwise representations
-// of the same logical value.
-struct Private {
-  int i;
-  template <typename H>
-  friend H AbslHashValue(H h, Private p) {
-    return H::combine(std::move(h), std::abs(p.i));
-  }
-
-  friend bool operator==(Private a, Private b) {
-    return std::abs(a.i) == std::abs(b.i);
-  }
-
-  friend std::ostream& operator<<(std::ostream& o, Private p) {
-    return o << p.i;
-  }
-};
-
-// Test helper for combine_piecewise_buffer.  It holds a string_view to the
-// buffer-to-be-hashed.  Its AbslHashValue specialization will split up its
-// contents at the character offsets requested.
-class PiecewiseHashTester {
- public:
-  // Create a hash view of a buffer to be hashed contiguously.
-  explicit PiecewiseHashTester(absl::string_view buf)
-      : buf_(buf), piecewise_(false), split_locations_() {}
-
-  // Create a hash view of a buffer to be hashed piecewise, with breaks at the
-  // given locations.
-  PiecewiseHashTester(absl::string_view buf, std::set<size_t> split_locations)
-      : buf_(buf),
-        piecewise_(true),
-        split_locations_(std::move(split_locations)) {}
-
-  template <typename H>
-  friend H AbslHashValue(H h, const PiecewiseHashTester& p) {
-    if (!p.piecewise_) {
-      return H::combine_contiguous(std::move(h), p.buf_.data(), p.buf_.size());
-    }
-    absl::hash_internal::PiecewiseCombiner combiner;
-    if (p.split_locations_.empty()) {
-      h = combiner.add_buffer(std::move(h), p.buf_.data(), p.buf_.size());
-      return combiner.finalize(std::move(h));
-    }
-    size_t begin = 0;
-    for (size_t next : p.split_locations_) {
-      absl::string_view chunk = p.buf_.substr(begin, next - begin);
-      h = combiner.add_buffer(std::move(h), chunk.data(), chunk.size());
-      begin = next;
-    }
-    absl::string_view last_chunk = p.buf_.substr(begin);
-    if (!last_chunk.empty()) {
-      h = combiner.add_buffer(std::move(h), last_chunk.data(),
-                              last_chunk.size());
-    }
-    return combiner.finalize(std::move(h));
-  }
-
- private:
-  absl::string_view buf_;
-  bool piecewise_;
-  std::set<size_t> split_locations_;
-};
-
-// Dummy object that hashes as two distinct contiguous buffers, "foo" followed
-// by "bar"
-struct DummyFooBar {
-  template <typename H>
-  friend H AbslHashValue(H h, const DummyFooBar&) {
-    const char* foo = "foo";
-    const char* bar = "bar";
-    h = H::combine_contiguous(std::move(h), foo, 3);
-    h = H::combine_contiguous(std::move(h), bar, 3);
-    return h;
-  }
-};
-
-TEST(HashValueTest, CombinePiecewiseBuffer) {
-  absl::Hash<PiecewiseHashTester> hash;
-
-  // Check that hashing an empty buffer through the piecewise API works.
-  EXPECT_EQ(hash(PiecewiseHashTester("")), hash(PiecewiseHashTester("", {})));
-
-  // Similarly, small buffers should give consistent results
-  EXPECT_EQ(hash(PiecewiseHashTester("foobar")),
-            hash(PiecewiseHashTester("foobar", {})));
-  EXPECT_EQ(hash(PiecewiseHashTester("foobar")),
-            hash(PiecewiseHashTester("foobar", {3})));
-
-  // But hashing "foobar" in pieces gives a different answer than hashing "foo"
-  // contiguously, then "bar" contiguously.
-  EXPECT_NE(hash(PiecewiseHashTester("foobar", {3})),
-            absl::Hash<DummyFooBar>()(DummyFooBar{}));
-
-  // Test hashing a large buffer incrementally, broken up in several different
-  // ways.  Arrange for breaks on and near the stride boundaries to look for
-  // off-by-one errors in the implementation.
-  //
-  // This test is run on a buffer that is a multiple of the stride size, and one
-  // that isn't.
-  for (size_t big_buffer_size : {1024 * 2 + 512, 1024 * 3}) {
-    SCOPED_TRACE(big_buffer_size);
-    std::string big_buffer;
-    for (int i = 0; i < big_buffer_size; ++i) {
-      // Arbitrary string
-      big_buffer.push_back(32 + (i * (i / 3)) % 64);
-    }
-    auto big_buffer_hash = hash(PiecewiseHashTester(big_buffer));
-
-    const int possible_breaks = 9;
-    size_t breaks[possible_breaks] = {1,    512,  1023, 1024, 1025,
-                                      1536, 2047, 2048, 2049};
-    for (unsigned test_mask = 0; test_mask < (1u << possible_breaks);
-         ++test_mask) {
-      SCOPED_TRACE(test_mask);
-      std::set<size_t> break_locations;
-      for (int j = 0; j < possible_breaks; ++j) {
-        if (test_mask & (1u << j)) {
-          break_locations.insert(breaks[j]);
-        }
-      }
-      EXPECT_EQ(
-          hash(PiecewiseHashTester(big_buffer, std::move(break_locations))),
-          big_buffer_hash);
-    }
-  }
-}
-
-TEST(HashValueTest, PrivateSanity) {
-  // Sanity check that Private is working as the tests below expect it to work.
-  EXPECT_TRUE(is_hashable<Private>::value);
-  EXPECT_NE(SpyHash(Private{0}), SpyHash(Private{1}));
-  EXPECT_EQ(SpyHash(Private{1}), SpyHash(Private{1}));
-}
-
-TEST(HashValueTest, Optional) {
-  EXPECT_TRUE(is_hashable<absl::optional<Private>>::value);
-
-  using O = absl::optional<Private>;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(O{}, O{{1}}, O{{-1}}, O{{10}})));
-}
-
-TEST(HashValueTest, Variant) {
-  using V = absl::variant<Private, std::string>;
-  EXPECT_TRUE(is_hashable<V>::value);
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      V(Private{1}), V(Private{-1}), V(Private{2}), V("ABC"), V("BCD"))));
-
-#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-  struct S {};
-  EXPECT_FALSE(is_hashable<absl::variant<S>>::value);
-#endif
-}
-
-TEST(HashValueTest, Maps) {
-  EXPECT_TRUE((is_hashable<std::map<int, std::string>>::value));
-
-  using M = std::map<int, std::string>;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      M{}, M{{0, "foo"}}, M{{1, "foo"}}, M{{0, "bar"}}, M{{1, "bar"}},
-      M{{0, "foo"}, {42, "bar"}}, M{{1, "foo"}, {42, "bar"}},
-      M{{1, "foo"}, {43, "bar"}}, M{{1, "foo"}, {43, "baz"}})));
-
-  using MM = std::multimap<int, std::string>;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      MM{}, MM{{0, "foo"}}, MM{{1, "foo"}}, MM{{0, "bar"}}, MM{{1, "bar"}},
-      MM{{0, "foo"}, {0, "bar"}}, MM{{0, "bar"}, {0, "foo"}},
-      MM{{0, "foo"}, {42, "bar"}}, MM{{1, "foo"}, {42, "bar"}},
-      MM{{1, "foo"}, {1, "foo"}, {43, "bar"}}, MM{{1, "foo"}, {43, "baz"}})));
-}
-
-TEST(HashValueTest, ReferenceWrapper) {
-  EXPECT_TRUE(is_hashable<std::reference_wrapper<Private>>::value);
-
-  Private p1{1}, p10{10};
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      p1, p10, std::ref(p1), std::ref(p10), std::cref(p1), std::cref(p10))));
-
-  EXPECT_TRUE(is_hashable<std::reference_wrapper<int>>::value);
-  int one = 1, ten = 10;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
-      one, ten, std::ref(one), std::ref(ten), std::cref(one), std::cref(ten))));
-
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      std::make_tuple(std::tuple<std::reference_wrapper<int>>(std::ref(one)),
-                      std::tuple<std::reference_wrapper<int>>(std::ref(ten)),
-                      std::tuple<int>(one), std::tuple<int>(ten))));
-}
-
-template <typename T, typename = void>
-struct IsHashCallable : std::false_type {};
-
-template <typename T>
-struct IsHashCallable<T, absl::void_t<decltype(std::declval<absl::Hash<T>>()(
-                            std::declval<const T&>()))>> : std::true_type {};
-
-template <typename T, typename = void>
-struct IsAggregateInitializable : std::false_type {};
-
-template <typename T>
-struct IsAggregateInitializable<T, absl::void_t<decltype(T{})>>
-    : std::true_type {};
-
-TEST(IsHashableTest, ValidHash) {
-  EXPECT_TRUE((is_hashable<int>::value));
-  EXPECT_TRUE(std::is_default_constructible<absl::Hash<int>>::value);
-  EXPECT_TRUE(std::is_copy_constructible<absl::Hash<int>>::value);
-  EXPECT_TRUE(std::is_move_constructible<absl::Hash<int>>::value);
-  EXPECT_TRUE(absl::is_copy_assignable<absl::Hash<int>>::value);
-  EXPECT_TRUE(absl::is_move_assignable<absl::Hash<int>>::value);
-  EXPECT_TRUE(IsHashCallable<int>::value);
-  EXPECT_TRUE(IsAggregateInitializable<absl::Hash<int>>::value);
-}
-
-#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-TEST(IsHashableTest, PoisonHash) {
-  struct X {};
-  EXPECT_FALSE((is_hashable<X>::value));
-  EXPECT_FALSE(std::is_default_constructible<absl::Hash<X>>::value);
-  EXPECT_FALSE(std::is_copy_constructible<absl::Hash<X>>::value);
-  EXPECT_FALSE(std::is_move_constructible<absl::Hash<X>>::value);
-  EXPECT_FALSE(absl::is_copy_assignable<absl::Hash<X>>::value);
-  EXPECT_FALSE(absl::is_move_assignable<absl::Hash<X>>::value);
-  EXPECT_FALSE(IsHashCallable<X>::value);
-#if !defined(__GNUC__) || __GNUC__ < 9
-  // This doesn't compile on GCC 9.
-  EXPECT_FALSE(IsAggregateInitializable<absl::Hash<X>>::value);
-#endif
-}
-#endif  // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-
-// Hashable types
-//
-// These types exist simply to exercise various AbslHashValue behaviors, so
-// they are named by what their AbslHashValue overload does.
-struct NoOp {
-  template <typename HashCode>
-  friend HashCode AbslHashValue(HashCode h, NoOp n) {
-    return h;
-  }
-};
-
-struct EmptyCombine {
-  template <typename HashCode>
-  friend HashCode AbslHashValue(HashCode h, EmptyCombine e) {
-    return HashCode::combine(std::move(h));
-  }
-};
-
-template <typename Int>
-struct CombineIterative {
-  template <typename HashCode>
-  friend HashCode AbslHashValue(HashCode h, CombineIterative c) {
-    for (int i = 0; i < 5; ++i) {
-      h = HashCode::combine(std::move(h), Int(i));
-    }
-    return h;
-  }
-};
-
-template <typename Int>
-struct CombineVariadic {
-  template <typename HashCode>
-  friend HashCode AbslHashValue(HashCode h, CombineVariadic c) {
-    return HashCode::combine(std::move(h), Int(0), Int(1), Int(2), Int(3),
-                             Int(4));
-  }
-};
-enum class InvokeTag {
-  kUniquelyRepresented,
-  kHashValue,
-#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
-  kLegacyHash,
-#endif  // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
-  kStdHash,
-  kNone
-};
-
-template <InvokeTag T>
-using InvokeTagConstant = std::integral_constant<InvokeTag, T>;
-
-template <InvokeTag... Tags>
-struct MinTag;
-
-template <InvokeTag a, InvokeTag b, InvokeTag... Tags>
-struct MinTag<a, b, Tags...> : MinTag<(a < b ? a : b), Tags...> {};
-
-template <InvokeTag a>
-struct MinTag<a> : InvokeTagConstant<a> {};
-
-template <InvokeTag... Tags>
-struct CustomHashType {
-  explicit CustomHashType(size_t val) : value(val) {}
-  size_t value;
-};
-
-template <InvokeTag allowed, InvokeTag... tags>
-struct EnableIfContained
-    : std::enable_if<absl::disjunction<
-          std::integral_constant<bool, allowed == tags>...>::value> {};
-
-template <
-    typename H, InvokeTag... Tags,
-    typename = typename EnableIfContained<InvokeTag::kHashValue, Tags...>::type>
-H AbslHashValue(H state, CustomHashType<Tags...> t) {
-  static_assert(MinTag<Tags...>::value == InvokeTag::kHashValue, "");
-  return H::combine(std::move(state),
-                    t.value + static_cast<int>(InvokeTag::kHashValue));
-}
-
-}  // namespace
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace hash_internal {
-template <InvokeTag... Tags>
-struct is_uniquely_represented<
-    CustomHashType<Tags...>,
-    typename EnableIfContained<InvokeTag::kUniquelyRepresented, Tags...>::type>
-    : std::true_type {};
-}  // namespace hash_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
-namespace ABSL_INTERNAL_LEGACY_HASH_NAMESPACE {
-template <InvokeTag... Tags>
-struct hash<CustomHashType<Tags...>> {
-  template <InvokeTag... TagsIn, typename = typename EnableIfContained<
-                                     InvokeTag::kLegacyHash, TagsIn...>::type>
-  size_t operator()(CustomHashType<TagsIn...> t) const {
-    static_assert(MinTag<Tags...>::value == InvokeTag::kLegacyHash, "");
-    return t.value + static_cast<int>(InvokeTag::kLegacyHash);
-  }
-};
-}  // namespace ABSL_INTERNAL_LEGACY_HASH_NAMESPACE
-#endif  // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
-
-namespace std {
-template <InvokeTag... Tags>  // NOLINT
-struct hash<CustomHashType<Tags...>> {
-  template <InvokeTag... TagsIn, typename = typename EnableIfContained<
-                                     InvokeTag::kStdHash, TagsIn...>::type>
-  size_t operator()(CustomHashType<TagsIn...> t) const {
-    static_assert(MinTag<Tags...>::value == InvokeTag::kStdHash, "");
-    return t.value + static_cast<int>(InvokeTag::kStdHash);
-  }
-};
-}  // namespace std
-
-namespace {
-
-template <typename... T>
-void TestCustomHashType(InvokeTagConstant<InvokeTag::kNone>, T...) {
-  using type = CustomHashType<T::value...>;
-  SCOPED_TRACE(testing::PrintToString(std::vector<InvokeTag>{T::value...}));
-  EXPECT_TRUE(is_hashable<type>());
-  EXPECT_TRUE(is_hashable<const type>());
-  EXPECT_TRUE(is_hashable<const type&>());
-
-  const size_t offset = static_cast<int>(std::min({T::value...}));
-  EXPECT_EQ(SpyHash(type(7)), SpyHash(size_t{7 + offset}));
-}
-
-void TestCustomHashType(InvokeTagConstant<InvokeTag::kNone>) {
-#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-  // is_hashable is false if we don't support any of the hooks.
-  using type = CustomHashType<>;
-  EXPECT_FALSE(is_hashable<type>());
-  EXPECT_FALSE(is_hashable<const type>());
-  EXPECT_FALSE(is_hashable<const type&>());
-#endif  // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-}
-
-template <InvokeTag Tag, typename... T>
-void TestCustomHashType(InvokeTagConstant<Tag> tag, T... t) {
-  constexpr auto next = static_cast<InvokeTag>(static_cast<int>(Tag) + 1);
-  TestCustomHashType(InvokeTagConstant<next>(), tag, t...);
-  TestCustomHashType(InvokeTagConstant<next>(), t...);
-}
-
-TEST(HashTest, CustomHashType) {
-  TestCustomHashType(InvokeTagConstant<InvokeTag{}>());
-}
-
-TEST(HashTest, NoOpsAreEquivalent) {
-  EXPECT_EQ(Hash<NoOp>()({}), Hash<NoOp>()({}));
-  EXPECT_EQ(Hash<NoOp>()({}), Hash<EmptyCombine>()({}));
-}
-
-template <typename T>
-class HashIntTest : public testing::Test {
-};
-TYPED_TEST_SUITE_P(HashIntTest);
-
-TYPED_TEST_P(HashIntTest, BasicUsage) {
-  EXPECT_NE(Hash<NoOp>()({}), Hash<TypeParam>()(0));
-  EXPECT_NE(Hash<NoOp>()({}),
-            Hash<TypeParam>()(std::numeric_limits<TypeParam>::max()));
-  if (std::numeric_limits<TypeParam>::min() != 0) {
-    EXPECT_NE(Hash<NoOp>()({}),
-              Hash<TypeParam>()(std::numeric_limits<TypeParam>::min()));
-  }
-
-  EXPECT_EQ(Hash<CombineIterative<TypeParam>>()({}),
-            Hash<CombineVariadic<TypeParam>>()({}));
-}
-
-REGISTER_TYPED_TEST_CASE_P(HashIntTest, BasicUsage);
-using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t,
-                                uint32_t, uint64_t, size_t>;
-INSTANTIATE_TYPED_TEST_CASE_P(My, HashIntTest, IntTypes);
-
-struct StructWithPadding {
-  char c;
-  int i;
-
-  template <typename H>
-  friend H AbslHashValue(H hash_state, const StructWithPadding& s) {
-    return H::combine(std::move(hash_state), s.c, s.i);
-  }
-};
-
-static_assert(sizeof(StructWithPadding) > sizeof(char) + sizeof(int),
-              "StructWithPadding doesn't have padding");
-static_assert(std::is_standard_layout<StructWithPadding>::value, "");
-
-// This check has to be disabled because libstdc++ doesn't support it.
-// static_assert(std::is_trivially_constructible<StructWithPadding>::value, "");
-
-template <typename T>
-struct ArraySlice {
-  T* begin;
-  T* end;
-
-  template <typename H>
-  friend H AbslHashValue(H hash_state, const ArraySlice& slice) {
-    for (auto t = slice.begin; t != slice.end; ++t) {
-      hash_state = H::combine(std::move(hash_state), *t);
-    }
-    return hash_state;
-  }
-};
-
-TEST(HashTest, HashNonUniquelyRepresentedType) {
-  // Create equal StructWithPadding objects that are known to have non-equal
-  // padding bytes.
-  static const size_t kNumStructs = 10;
-  unsigned char buffer1[kNumStructs * sizeof(StructWithPadding)];
-  std::memset(buffer1, 0, sizeof(buffer1));
-  auto* s1 = reinterpret_cast<StructWithPadding*>(buffer1);
-
-  unsigned char buffer2[kNumStructs * sizeof(StructWithPadding)];
-  std::memset(buffer2, 255, sizeof(buffer2));
-  auto* s2 = reinterpret_cast<StructWithPadding*>(buffer2);
-  for (int i = 0; i < kNumStructs; ++i) {
-    SCOPED_TRACE(i);
-    s1[i].c = s2[i].c = '0' + i;
-    s1[i].i = s2[i].i = i;
-    ASSERT_FALSE(memcmp(buffer1 + i * sizeof(StructWithPadding),
-                        buffer2 + i * sizeof(StructWithPadding),
-                        sizeof(StructWithPadding)) == 0)
-        << "Bug in test code: objects do not have unequal"
-        << " object representations";
-  }
-
-  EXPECT_EQ(Hash<StructWithPadding>()(s1[0]), Hash<StructWithPadding>()(s2[0]));
-  EXPECT_EQ(Hash<ArraySlice<StructWithPadding>>()({s1, s1 + kNumStructs}),
-            Hash<ArraySlice<StructWithPadding>>()({s2, s2 + kNumStructs}));
-}
-
-TEST(HashTest, StandardHashContainerUsage) {
-  std::unordered_map<int, std::string, Hash<int>> map = {{0, "foo"},
-                                                         {42, "bar"}};
-
-  EXPECT_NE(map.find(0), map.end());
-  EXPECT_EQ(map.find(1), map.end());
-  EXPECT_NE(map.find(0u), map.end());
-}
-
-struct ConvertibleFromNoOp {
-  ConvertibleFromNoOp(NoOp) {}  // NOLINT(runtime/explicit)
-
-  template <typename H>
-  friend H AbslHashValue(H hash_state, ConvertibleFromNoOp) {
-    return H::combine(std::move(hash_state), 1);
-  }
-};
-
-TEST(HashTest, HeterogeneousCall) {
-  EXPECT_NE(Hash<ConvertibleFromNoOp>()(NoOp()),
-            Hash<NoOp>()(NoOp()));
-}
-
-TEST(IsUniquelyRepresentedTest, SanityTest) {
-  using absl::hash_internal::is_uniquely_represented;
-
-  EXPECT_TRUE(is_uniquely_represented<unsigned char>::value);
-  EXPECT_TRUE(is_uniquely_represented<int>::value);
-  EXPECT_FALSE(is_uniquely_represented<bool>::value);
-  EXPECT_FALSE(is_uniquely_represented<int*>::value);
-}
-
-struct IntAndString {
-  int i;
-  std::string s;
-
-  template <typename H>
-  friend H AbslHashValue(H hash_state, IntAndString int_and_string) {
-    return H::combine(std::move(hash_state), int_and_string.s,
-                      int_and_string.i);
-  }
-};
-
-TEST(HashTest, SmallValueOn64ByteBoundary) {
-  Hash<IntAndString>()(IntAndString{0, std::string(63, '0')});
-}
-
-struct TypeErased {
-  size_t n;
-
-  template <typename H>
-  friend H AbslHashValue(H hash_state, const TypeErased& v) {
-    v.HashValue(absl::HashState::Create(&hash_state));
-    return hash_state;
-  }
-
-  void HashValue(absl::HashState state) const {
-    absl::HashState::combine(std::move(state), n);
-  }
-};
-
-TEST(HashTest, TypeErased) {
-  EXPECT_TRUE((is_hashable<TypeErased>::value));
-  EXPECT_TRUE((is_hashable<std::pair<TypeErased, int>>::value));
-
-  EXPECT_EQ(SpyHash(TypeErased{7}), SpyHash(size_t{7}));
-  EXPECT_NE(SpyHash(TypeErased{7}), SpyHash(size_t{13}));
-
-  EXPECT_EQ(SpyHash(std::make_pair(TypeErased{7}, 17)),
-            SpyHash(std::make_pair(size_t{7}, 17)));
-}
-
-struct ValueWithBoolConversion {
-  operator bool() const { return false; }
-  int i;
-};
-
-}  // namespace
-namespace std {
-template <>
-struct hash<ValueWithBoolConversion> {
-  size_t operator()(ValueWithBoolConversion v) { return v.i; }
-};
-}  // namespace std
-
-namespace {
-
-TEST(HashTest, DoesNotUseImplicitConversionsToBool) {
-  EXPECT_NE(absl::Hash<ValueWithBoolConversion>()(ValueWithBoolConversion{0}),
-            absl::Hash<ValueWithBoolConversion>()(ValueWithBoolConversion{1}));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/hash/hash_testing.h b/third_party/abseil_cpp/absl/hash/hash_testing.h
deleted file mode 100644
index 1e1c574149..0000000000
--- a/third_party/abseil_cpp/absl/hash/hash_testing.h
+++ /dev/null
@@ -1,378 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_HASH_HASH_TESTING_H_
-#define ABSL_HASH_HASH_TESTING_H_
-
-#include <initializer_list>
-#include <tuple>
-#include <type_traits>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/hash/internal/spy_hash_state.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/str_cat.h"
-#include "absl/types/variant.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Run the absl::Hash algorithm over all the elements passed in and verify that
-// their hash expansion is congruent with their `==` operator.
-//
-// It is used in conjunction with EXPECT_TRUE. Failures will output information
-// on what requirement failed and on which objects.
-//
-// Users should pass a collection of types as either an initializer list or a
-// container of cases.
-//
-//   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-//       {v1, v2, ..., vN}));
-//
-//   std::vector<MyType> cases;
-//   // Fill cases...
-//   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
-//
-// Users can pass a variety of types for testing heterogeneous lookup with
-// `std::make_tuple`:
-//
-//   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-//       std::make_tuple(v1, v2, ..., vN)));
-//
-//
-// Ideally, the values passed should provide enough coverage of the `==`
-// operator and the AbslHashValue implementations.
-// For dynamically sized types, the empty state should usually be included in
-// the values.
-//
-// The function accepts an optional comparator function, in case that `==` is
-// not enough for the values provided.
-//
-// Usage:
-//
-//   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-//       std::make_tuple(v1, v2, ..., vN), MyCustomEq{}));
-//
-// It checks the following requirements:
-//   1. The expansion for a value is deterministic.
-//   2. For any two objects `a` and `b` in the sequence, if `a == b` evaluates
-//      to true, then their hash expansion must be equal.
-//   3. If `a == b` evaluates to false their hash expansion must be unequal.
-//   4. If `a == b` evaluates to false neither hash expansion can be a
-//      suffix of the other.
-//   5. AbslHashValue overloads should not be called by the user. They are only
-//      meant to be called by the framework. Users should call H::combine() and
-//      H::combine_contiguous().
-//   6. No moved-from instance of the hash state is used in the implementation
-//      of AbslHashValue.
-//
-// The values do not have to have the same type. This can be useful for
-// equivalent types that support heterogeneous lookup.
-//
-// A possible reason for breaking (2) is combining state in the hash expansion
-// that was not used in `==`.
-// For example:
-//
-// struct Bad2 {
-//   int a, b;
-//   template <typename H>
-//   friend H AbslHashValue(H state, Bad2 x) {
-//     // Uses a and b.
-//     return H::combine(std::move(state), x.a, x.b);
-//   }
-//   friend bool operator==(Bad2 x, Bad2 y) {
-//     // Only uses a.
-//     return x.a == y.a;
-//   }
-// };
-//
-// As for (3), breaking this usually means that there is state being passed to
-// the `==` operator that is not used in the hash expansion.
-// For example:
-//
-// struct Bad3 {
-//   int a, b;
-//   template <typename H>
-//   friend H AbslHashValue(H state, Bad3 x) {
-//     // Only uses a.
-//     return H::combine(std::move(state), x.a);
-//   }
-//   friend bool operator==(Bad3 x, Bad3 y) {
-//     // Uses a and b.
-//     return x.a == y.a && x.b == y.b;
-//   }
-// };
-//
-// Finally, a common way to break 4 is by combining dynamic ranges without
-// combining the size of the range.
-// For example:
-//
-// struct Bad4 {
-//   int *p, size;
-//   template <typename H>
-//   friend H AbslHashValue(H state, Bad4 x) {
-//     return H::combine_contiguous(std::move(state), x.p, x.p + x.size);
-//   }
-//   friend bool operator==(Bad4 x, Bad4 y) {
-//    // Compare two ranges for equality. C++14 code can instead use std::equal.
-//     return absl::equal(x.p, x.p + x.size, y.p, y.p + y.size);
-//   }
-// };
-//
-// An easy solution to this is to combine the size after combining the range,
-// like so:
-// template <typename H>
-// friend H AbslHashValue(H state, Bad4 x) {
-//   return H::combine(
-//       H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size);
-// }
-//
-template <int&... ExplicitBarrier, typename Container>
-ABSL_MUST_USE_RESULT testing::AssertionResult
-VerifyTypeImplementsAbslHashCorrectly(const Container& values);
-
-template <int&... ExplicitBarrier, typename Container, typename Eq>
-ABSL_MUST_USE_RESULT testing::AssertionResult
-VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals);
-
-template <int&..., typename T>
-ABSL_MUST_USE_RESULT testing::AssertionResult
-VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values);
-
-template <int&..., typename T, typename Eq>
-ABSL_MUST_USE_RESULT testing::AssertionResult
-VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
-                                      Eq equals);
-
-namespace hash_internal {
-
-struct PrintVisitor {
-  size_t index;
-  template <typename T>
-  std::string operator()(const T* value) const {
-    return absl::StrCat("#", index, "(", testing::PrintToString(*value), ")");
-  }
-};
-
-template <typename Eq>
-struct EqVisitor {
-  Eq eq;
-  template <typename T, typename U>
-  bool operator()(const T* t, const U* u) const {
-    return eq(*t, *u);
-  }
-};
-
-struct ExpandVisitor {
-  template <typename T>
-  SpyHashState operator()(const T* value) const {
-    return SpyHashState::combine(SpyHashState(), *value);
-  }
-};
-
-template <typename Container, typename Eq>
-ABSL_MUST_USE_RESULT testing::AssertionResult
-VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
-  using V = typename Container::value_type;
-
-  struct Info {
-    const V& value;
-    size_t index;
-    std::string ToString() const {
-      return absl::visit(PrintVisitor{index}, value);
-    }
-    SpyHashState expand() const { return absl::visit(ExpandVisitor{}, value); }
-  };
-
-  using EqClass = std::vector<Info>;
-  std::vector<EqClass> classes;
-
-  // Gather the values in equivalence classes.
-  size_t i = 0;
-  for (const auto& value : values) {
-    EqClass* c = nullptr;
-    for (auto& eqclass : classes) {
-      if (absl::visit(EqVisitor<Eq>{equals}, value, eqclass[0].value)) {
-        c = &eqclass;
-        break;
-      }
-    }
-    if (c == nullptr) {
-      classes.emplace_back();
-      c = &classes.back();
-    }
-    c->push_back({value, i});
-    ++i;
-
-    // Verify potential errors captured by SpyHashState.
-    if (auto error = c->back().expand().error()) {
-      return testing::AssertionFailure() << *error;
-    }
-  }
-
-  if (classes.size() < 2) {
-    return testing::AssertionFailure()
-           << "At least two equivalence classes are expected.";
-  }
-
-  // We assume that equality is correctly implemented.
-  // Now we verify that AbslHashValue is also correctly implemented.
-
-  for (const auto& c : classes) {
-    // All elements of the equivalence class must have the same hash
-    // expansion.
-    const SpyHashState expected = c[0].expand();
-    for (const Info& v : c) {
-      if (v.expand() != v.expand()) {
-        return testing::AssertionFailure()
-               << "Hash expansion for " << v.ToString()
-               << " is non-deterministic.";
-      }
-      if (v.expand() != expected) {
-        return testing::AssertionFailure()
-               << "Values " << c[0].ToString() << " and " << v.ToString()
-               << " evaluate as equal but have an unequal hash expansion.";
-      }
-    }
-
-    // Elements from other classes must have different hash expansion.
-    for (const auto& c2 : classes) {
-      if (&c == &c2) continue;
-      const SpyHashState c2_hash = c2[0].expand();
-      switch (SpyHashState::Compare(expected, c2_hash)) {
-        case SpyHashState::CompareResult::kEqual:
-          return testing::AssertionFailure()
-                 << "Values " << c[0].ToString() << " and " << c2[0].ToString()
-                 << " evaluate as unequal but have an equal hash expansion.";
-        case SpyHashState::CompareResult::kBSuffixA:
-          return testing::AssertionFailure()
-                 << "Hash expansion of " << c2[0].ToString()
-                 << " is a suffix of the hash expansion of " << c[0].ToString()
-                 << ".";
-        case SpyHashState::CompareResult::kASuffixB:
-          return testing::AssertionFailure()
-                 << "Hash expansion of " << c[0].ToString()
-                 << " is a suffix of the hash expansion of " << c2[0].ToString()
-                 << ".";
-        case SpyHashState::CompareResult::kUnequal:
-          break;
-      }
-    }
-  }
-  return testing::AssertionSuccess();
-}
-
-template <typename... T>
-struct TypeSet {
-  template <typename U, bool = disjunction<std::is_same<T, U>...>::value>
-  struct Insert {
-    using type = TypeSet<U, T...>;
-  };
-  template <typename U>
-  struct Insert<U, true> {
-    using type = TypeSet;
-  };
-
-  template <template <typename...> class C>
-  using apply = C<T...>;
-};
-
-template <typename... T>
-struct MakeTypeSet : TypeSet<> {};
-template <typename T, typename... Ts>
-struct MakeTypeSet<T, Ts...> : MakeTypeSet<Ts...>::template Insert<T>::type {};
-
-template <typename... T>
-using VariantForTypes = typename MakeTypeSet<
-    const typename std::decay<T>::type*...>::template apply<absl::variant>;
-
-template <typename Container>
-struct ContainerAsVector {
-  using V = absl::variant<const typename Container::value_type*>;
-  using Out = std::vector<V>;
-
-  static Out Do(const Container& values) {
-    Out out;
-    for (const auto& v : values) out.push_back(&v);
-    return out;
-  }
-};
-
-template <typename... T>
-struct ContainerAsVector<std::tuple<T...>> {
-  using V = VariantForTypes<T...>;
-  using Out = std::vector<V>;
-
-  template <size_t... I>
-  static Out DoImpl(const std::tuple<T...>& tuple, absl::index_sequence<I...>) {
-    return Out{&std::get<I>(tuple)...};
-  }
-
-  static Out Do(const std::tuple<T...>& values) {
-    return DoImpl(values, absl::index_sequence_for<T...>());
-  }
-};
-
-template <>
-struct ContainerAsVector<std::tuple<>> {
-  static std::vector<VariantForTypes<int>> Do(std::tuple<>) { return {}; }
-};
-
-struct DefaultEquals {
-  template <typename T, typename U>
-  bool operator()(const T& t, const U& u) const {
-    return t == u;
-  }
-};
-
-}  // namespace hash_internal
-
-template <int&..., typename Container>
-ABSL_MUST_USE_RESULT testing::AssertionResult
-VerifyTypeImplementsAbslHashCorrectly(const Container& values) {
-  return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
-      hash_internal::ContainerAsVector<Container>::Do(values),
-      hash_internal::DefaultEquals{});
-}
-
-template <int&..., typename Container, typename Eq>
-ABSL_MUST_USE_RESULT testing::AssertionResult
-VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
-  return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
-      hash_internal::ContainerAsVector<Container>::Do(values), equals);
-}
-
-template <int&..., typename T>
-ABSL_MUST_USE_RESULT testing::AssertionResult
-VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values) {
-  return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
-      hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
-      hash_internal::DefaultEquals{});
-}
-
-template <int&..., typename T, typename Eq>
-ABSL_MUST_USE_RESULT testing::AssertionResult
-VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
-                                      Eq equals) {
-  return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
-      hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
-      equals);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HASH_HASH_TESTING_H_
diff --git a/third_party/abseil_cpp/absl/hash/internal/city.cc b/third_party/abseil_cpp/absl/hash/internal/city.cc
deleted file mode 100644
index 5460134e57..0000000000
--- a/third_party/abseil_cpp/absl/hash/internal/city.cc
+++ /dev/null
@@ -1,349 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This file provides CityHash64() and related functions.
-//
-// It's probably possible to create even faster hash functions by
-// writing a program that systematically explores some of the space of
-// possible hash functions, by using SIMD instructions, or by
-// compromising on hash quality.
-
-#include "absl/hash/internal/city.h"
-
-#include <string.h>  // for memcpy and memset
-#include <algorithm>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/endian.h"
-#include "absl/base/internal/unaligned_access.h"
-#include "absl/base/optimization.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace hash_internal {
-
-#ifdef ABSL_IS_BIG_ENDIAN
-#define uint32_in_expected_order(x) (absl::gbswap_32(x))
-#define uint64_in_expected_order(x) (absl::gbswap_64(x))
-#else
-#define uint32_in_expected_order(x) (x)
-#define uint64_in_expected_order(x) (x)
-#endif
-
-static uint64_t Fetch64(const char *p) {
-  return uint64_in_expected_order(ABSL_INTERNAL_UNALIGNED_LOAD64(p));
-}
-
-static uint32_t Fetch32(const char *p) {
-  return uint32_in_expected_order(ABSL_INTERNAL_UNALIGNED_LOAD32(p));
-}
-
-// Some primes between 2^63 and 2^64 for various uses.
-static const uint64_t k0 = 0xc3a5c85c97cb3127ULL;
-static const uint64_t k1 = 0xb492b66fbe98f273ULL;
-static const uint64_t k2 = 0x9ae16a3b2f90404fULL;
-
-// Magic numbers for 32-bit hashing.  Copied from Murmur3.
-static const uint32_t c1 = 0xcc9e2d51;
-static const uint32_t c2 = 0x1b873593;
-
-// A 32-bit to 32-bit integer hash copied from Murmur3.
-static uint32_t fmix(uint32_t h) {
-  h ^= h >> 16;
-  h *= 0x85ebca6b;
-  h ^= h >> 13;
-  h *= 0xc2b2ae35;
-  h ^= h >> 16;
-  return h;
-}
-
-static uint32_t Rotate32(uint32_t val, int shift) {
-  // Avoid shifting by 32: doing so yields an undefined result.
-  return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));
-}
-
-#undef PERMUTE3
-#define PERMUTE3(a, b, c) \
-  do {                    \
-    std::swap(a, b);      \
-    std::swap(a, c);      \
-  } while (0)
-
-static uint32_t Mur(uint32_t a, uint32_t h) {
-  // Helper from Murmur3 for combining two 32-bit values.
-  a *= c1;
-  a = Rotate32(a, 17);
-  a *= c2;
-  h ^= a;
-  h = Rotate32(h, 19);
-  return h * 5 + 0xe6546b64;
-}
-
-static uint32_t Hash32Len13to24(const char *s, size_t len) {
-  uint32_t a = Fetch32(s - 4 + (len >> 1));
-  uint32_t b = Fetch32(s + 4);
-  uint32_t c = Fetch32(s + len - 8);
-  uint32_t d = Fetch32(s + (len >> 1));
-  uint32_t e = Fetch32(s);
-  uint32_t f = Fetch32(s + len - 4);
-  uint32_t h = len;
-
-  return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));
-}
-
-static uint32_t Hash32Len0to4(const char *s, size_t len) {
-  uint32_t b = 0;
-  uint32_t c = 9;
-  for (size_t i = 0; i < len; i++) {
-    signed char v = s[i];
-    b = b * c1 + v;
-    c ^= b;
-  }
-  return fmix(Mur(b, Mur(len, c)));
-}
-
-static uint32_t Hash32Len5to12(const char *s, size_t len) {
-  uint32_t a = len, b = len * 5, c = 9, d = b;
-  a += Fetch32(s);
-  b += Fetch32(s + len - 4);
-  c += Fetch32(s + ((len >> 1) & 4));
-  return fmix(Mur(c, Mur(b, Mur(a, d))));
-}
-
-uint32_t CityHash32(const char *s, size_t len) {
-  if (len <= 24) {
-    return len <= 12
-               ? (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len))
-               : Hash32Len13to24(s, len);
-  }
-
-  // len > 24
-  uint32_t h = len, g = c1 * len, f = g;
-
-  uint32_t a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;
-  uint32_t a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;
-  uint32_t a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;
-  uint32_t a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;
-  uint32_t a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;
-  h ^= a0;
-  h = Rotate32(h, 19);
-  h = h * 5 + 0xe6546b64;
-  h ^= a2;
-  h = Rotate32(h, 19);
-  h = h * 5 + 0xe6546b64;
-  g ^= a1;
-  g = Rotate32(g, 19);
-  g = g * 5 + 0xe6546b64;
-  g ^= a3;
-  g = Rotate32(g, 19);
-  g = g * 5 + 0xe6546b64;
-  f += a4;
-  f = Rotate32(f, 19);
-  f = f * 5 + 0xe6546b64;
-  size_t iters = (len - 1) / 20;
-  do {
-    uint32_t b0 = Rotate32(Fetch32(s) * c1, 17) * c2;
-    uint32_t b1 = Fetch32(s + 4);
-    uint32_t b2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2;
-    uint32_t b3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2;
-    uint32_t b4 = Fetch32(s + 16);
-    h ^= b0;
-    h = Rotate32(h, 18);
-    h = h * 5 + 0xe6546b64;
-    f += b1;
-    f = Rotate32(f, 19);
-    f = f * c1;
-    g += b2;
-    g = Rotate32(g, 18);
-    g = g * 5 + 0xe6546b64;
-    h ^= b3 + b1;
-    h = Rotate32(h, 19);
-    h = h * 5 + 0xe6546b64;
-    g ^= b4;
-    g = absl::gbswap_32(g) * 5;
-    h += b4 * 5;
-    h = absl::gbswap_32(h);
-    f += b0;
-    PERMUTE3(f, h, g);
-    s += 20;
-  } while (--iters != 0);
-  g = Rotate32(g, 11) * c1;
-  g = Rotate32(g, 17) * c1;
-  f = Rotate32(f, 11) * c1;
-  f = Rotate32(f, 17) * c1;
-  h = Rotate32(h + g, 19);
-  h = h * 5 + 0xe6546b64;
-  h = Rotate32(h, 17) * c1;
-  h = Rotate32(h + f, 19);
-  h = h * 5 + 0xe6546b64;
-  h = Rotate32(h, 17) * c1;
-  return h;
-}
-
-// Bitwise right rotate.  Normally this will compile to a single
-// instruction, especially if the shift is a manifest constant.
-static uint64_t Rotate(uint64_t val, int shift) {
-  // Avoid shifting by 64: doing so yields an undefined result.
-  return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
-}
-
-static uint64_t ShiftMix(uint64_t val) { return val ^ (val >> 47); }
-
-static uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) {
-  // Murmur-inspired hashing.
-  uint64_t a = (u ^ v) * mul;
-  a ^= (a >> 47);
-  uint64_t b = (v ^ a) * mul;
-  b ^= (b >> 47);
-  b *= mul;
-  return b;
-}
-
-static uint64_t HashLen16(uint64_t u, uint64_t v) {
-  const uint64_t kMul = 0x9ddfea08eb382d69ULL;
-  return HashLen16(u, v, kMul);
-}
-
-static uint64_t HashLen0to16(const char *s, size_t len) {
-  if (len >= 8) {
-    uint64_t mul = k2 + len * 2;
-    uint64_t a = Fetch64(s) + k2;
-    uint64_t b = Fetch64(s + len - 8);
-    uint64_t c = Rotate(b, 37) * mul + a;
-    uint64_t d = (Rotate(a, 25) + b) * mul;
-    return HashLen16(c, d, mul);
-  }
-  if (len >= 4) {
-    uint64_t mul = k2 + len * 2;
-    uint64_t a = Fetch32(s);
-    return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
-  }
-  if (len > 0) {
-    uint8_t a = s[0];
-    uint8_t b = s[len >> 1];
-    uint8_t c = s[len - 1];
-    uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
-    uint32_t z = len + (static_cast<uint32_t>(c) << 2);
-    return ShiftMix(y * k2 ^ z * k0) * k2;
-  }
-  return k2;
-}
-
-// This probably works well for 16-byte strings as well, but it may be overkill
-// in that case.
-static uint64_t HashLen17to32(const char *s, size_t len) {
-  uint64_t mul = k2 + len * 2;
-  uint64_t a = Fetch64(s) * k1;
-  uint64_t b = Fetch64(s + 8);
-  uint64_t c = Fetch64(s + len - 8) * mul;
-  uint64_t d = Fetch64(s + len - 16) * k2;
-  return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d,
-                   a + Rotate(b + k2, 18) + c, mul);
-}
-
-// Return a 16-byte hash for 48 bytes.  Quick and dirty.
-// Callers do best to use "random-looking" values for a and b.
-static std::pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(
-    uint64_t w, uint64_t x, uint64_t y, uint64_t z, uint64_t a, uint64_t b) {
-  a += w;
-  b = Rotate(b + a + z, 21);
-  uint64_t c = a;
-  a += x;
-  a += y;
-  b += Rotate(a, 44);
-  return std::make_pair(a + z, b + c);
-}
-
-// Return a 16-byte hash for s[0] ... s[31], a, and b.  Quick and dirty.
-static std::pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(const char *s,
-                                                            uint64_t a,
-                                                            uint64_t b) {
-  return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16),
-                                Fetch64(s + 24), a, b);
-}
-
-// Return an 8-byte hash for 33 to 64 bytes.
-static uint64_t HashLen33to64(const char *s, size_t len) {
-  uint64_t mul = k2 + len * 2;
-  uint64_t a = Fetch64(s) * k2;
-  uint64_t b = Fetch64(s + 8);
-  uint64_t c = Fetch64(s + len - 24);
-  uint64_t d = Fetch64(s + len - 32);
-  uint64_t e = Fetch64(s + 16) * k2;
-  uint64_t f = Fetch64(s + 24) * 9;
-  uint64_t g = Fetch64(s + len - 8);
-  uint64_t h = Fetch64(s + len - 16) * mul;
-  uint64_t u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
-  uint64_t v = ((a + g) ^ d) + f + 1;
-  uint64_t w = absl::gbswap_64((u + v) * mul) + h;
-  uint64_t x = Rotate(e + f, 42) + c;
-  uint64_t y = (absl::gbswap_64((v + w) * mul) + g) * mul;
-  uint64_t z = e + f + c;
-  a = absl::gbswap_64((x + z) * mul + y) + b;
-  b = ShiftMix((z + a) * mul + d + h) * mul;
-  return b + x;
-}
-
-uint64_t CityHash64(const char *s, size_t len) {
-  if (len <= 32) {
-    if (len <= 16) {
-      return HashLen0to16(s, len);
-    } else {
-      return HashLen17to32(s, len);
-    }
-  } else if (len <= 64) {
-    return HashLen33to64(s, len);
-  }
-
-  // For strings over 64 bytes we hash the end first, and then as we
-  // loop we keep 56 bytes of state: v, w, x, y, and z.
-  uint64_t x = Fetch64(s + len - 40);
-  uint64_t y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
-  uint64_t z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
-  std::pair<uint64_t, uint64_t> v =
-      WeakHashLen32WithSeeds(s + len - 64, len, z);
-  std::pair<uint64_t, uint64_t> w =
-      WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
-  x = x * k1 + Fetch64(s);
-
-  // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
-  len = (len - 1) & ~static_cast<size_t>(63);
-  do {
-    x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
-    y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
-    x ^= w.second;
-    y += v.first + Fetch64(s + 40);
-    z = Rotate(z + w.first, 33) * k1;
-    v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
-    w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
-    std::swap(z, x);
-    s += 64;
-    len -= 64;
-  } while (len != 0);
-  return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z,
-                   HashLen16(v.second, w.second) + x);
-}
-
-uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed) {
-  return CityHash64WithSeeds(s, len, k2, seed);
-}
-
-uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0,
-                             uint64_t seed1) {
-  return HashLen16(CityHash64(s, len) - seed0, seed1);
-}
-
-}  // namespace hash_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/hash/internal/city.h b/third_party/abseil_cpp/absl/hash/internal/city.h
deleted file mode 100644
index 393da0b95d..0000000000
--- a/third_party/abseil_cpp/absl/hash/internal/city.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// https://code.google.com/p/cityhash/
-//
-// This file provides a few functions for hashing strings.  All of them are
-// high-quality functions in the sense that they pass standard tests such
-// as Austin Appleby's SMHasher.  They are also fast.
-//
-// For 64-bit x86 code, on short strings, we don't know of anything faster than
-// CityHash64 that is of comparable quality.  We believe our nearest competitor
-// is Murmur3.  For 64-bit x86 code, CityHash64 is an excellent choice for hash
-// tables and most other hashing (excluding cryptography).
-//
-// For 32-bit x86 code, we don't know of anything faster than CityHash32 that
-// is of comparable quality.  We believe our nearest competitor is Murmur3A.
-// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.)
-//
-// Functions in the CityHash family are not suitable for cryptography.
-//
-// Please see CityHash's README file for more details on our performance
-// measurements and so on.
-//
-// WARNING: This code has been only lightly tested on big-endian platforms!
-// It is known to work well on little-endian platforms that have a small penalty
-// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
-// It should work on all 32-bit and 64-bit platforms that allow unaligned reads;
-// bug reports are welcome.
-//
-// By the way, for some hash functions, given strings a and b, the hash
-// of a+b is easily derived from the hashes of a and b.  This property
-// doesn't hold for any hash functions in this file.
-
-#ifndef ABSL_HASH_INTERNAL_CITY_H_
-#define ABSL_HASH_INTERNAL_CITY_H_
-
-#include <stdint.h>
-#include <stdlib.h>  // for size_t.
-
-#include <utility>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace hash_internal {
-
-// Hash function for a byte array.
-uint64_t CityHash64(const char *s, size_t len);
-
-// Hash function for a byte array.  For convenience, a 64-bit seed is also
-// hashed into the result.
-uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed);
-
-// Hash function for a byte array.  For convenience, two seeds are also
-// hashed into the result.
-uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0,
-                             uint64_t seed1);
-
-// Hash function for a byte array.  Most useful in 32-bit binaries.
-uint32_t CityHash32(const char *s, size_t len);
-
-}  // namespace hash_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HASH_INTERNAL_CITY_H_
diff --git a/third_party/abseil_cpp/absl/hash/internal/city_test.cc b/third_party/abseil_cpp/absl/hash/internal/city_test.cc
deleted file mode 100644
index 251d381d73..0000000000
--- a/third_party/abseil_cpp/absl/hash/internal/city_test.cc
+++ /dev/null
@@ -1,595 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/hash/internal/city.h"
-
-#include <string.h>
-#include <cstdio>
-#include <iostream>
-#include "gtest/gtest.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace hash_internal {
-
-static const uint64_t k0 = 0xc3a5c85c97cb3127ULL;
-static const uint64_t kSeed0 = 1234567;
-static const uint64_t kSeed1 = k0;
-static const int kDataSize = 1 << 20;
-static const int kTestSize = 300;
-
-static char data[kDataSize];
-
-// Initialize data to pseudorandom values.
-void setup() {
-  uint64_t a = 9;
-  uint64_t b = 777;
-  for (int i = 0; i < kDataSize; i++) {
-    a += b;
-    b += a;
-    a = (a ^ (a >> 41)) * k0;
-    b = (b ^ (b >> 41)) * k0 + i;
-    uint8_t u = b >> 37;
-    memcpy(data + i, &u, 1);  // uint8_t -> char
-  }
-}
-
-#define C(x) 0x##x##ULL
-static const uint64_t testdata[kTestSize][4] = {
-    {C(9ae16a3b2f90404f), C(75106db890237a4a), C(3feac5f636039766),
-     C(dc56d17a)},
-    {C(541150e87f415e96), C(1aef0d24b3148a1a), C(bacc300e1e82345a),
-     C(99929334)},
-    {C(f3786a4b25827c1), C(34ee1a2bf767bd1c), C(2f15ca2ebfb631f2), C(4252edb7)},
-    {C(ef923a7a1af78eab), C(79163b1e1e9a9b18), C(df3b2aca6e1e4a30),
-     C(ebc34f3c)},
-    {C(11df592596f41d88), C(843ec0bce9042f9c), C(cce2ea1e08b1eb30),
-     C(26f2b463)},
-    {C(831f448bdc5600b3), C(62a24be3120a6919), C(1b44098a41e010da),
-     C(b042c047)},
-    {C(3eca803e70304894), C(d80de767e4a920a), C(a51cfbb292efd53d), C(e73bb0a8)},
-    {C(1b5a063fb4c7f9f1), C(318dbc24af66dee9), C(10ef7b32d5c719af),
-     C(91dfdd75)},
-    {C(a0f10149a0e538d6), C(69d008c20f87419f), C(41b36376185b3e9e),
-     C(c87f95de)},
-    {C(fb8d9c70660b910b), C(a45b0cc3476bff1b), C(b28d1996144f0207),
-     C(3f5538ef)},
-    {C(236827beae282a46), C(e43970221139c946), C(4f3ac6faa837a3aa),
-     C(70eb1a1f)},
-    {C(c385e435136ecf7c), C(d9d17368ff6c4a08), C(1b31eed4e5251a67),
-     C(cfd63b83)},
-    {C(e3f6828b6017086d), C(21b4d1900554b3b0), C(bef38be1809e24f1),
-     C(894a52ef)},
-    {C(851fff285561dca0), C(4d1277d73cdf416f), C(28ccffa61010ebe2),
-     C(9cde6a54)},
-    {C(61152a63595a96d9), C(d1a3a91ef3a7ba45), C(443b6bb4a493ad0c),
-     C(6c4898d5)},
-    {C(44473e03be306c88), C(30097761f872472a), C(9fd1b669bfad82d7),
-     C(13e1978e)},
-    {C(3ead5f21d344056), C(fb6420393cfb05c3), C(407932394cbbd303), C(51b4ba8)},
-    {C(6abbfde37ee03b5b), C(83febf188d2cc113), C(cda7b62d94d5b8ee),
-     C(b6b06e40)},
-    {C(943e7ed63b3c080), C(1ef207e9444ef7f8), C(ef4a9f9f8c6f9b4a), C(240a2f2)},
-    {C(d72ce05171ef8a1a), C(c6bd6bd869203894), C(c760e6396455d23a),
-     C(5dcefc30)},
-    {C(4182832b52d63735), C(337097e123eea414), C(b5a72ca0456df910),
-     C(7a48b105)},
-    {C(d6cdae892584a2cb), C(58de0fa4eca17dcd), C(43df30b8f5f1cb00),
-     C(fd55007b)},
-    {C(5c8e90bc267c5ee4), C(e9ae044075d992d9), C(f234cbfd1f0a1e59),
-     C(6b95894c)},
-    {C(bbd7f30ac310a6f3), C(b23b570d2666685f), C(fb13fb08c9814fe7),
-     C(3360e827)},
-    {C(36a097aa49519d97), C(8204380a73c4065), C(77c2004bdd9e276a), C(45177e0b)},
-    {C(dc78cb032c49217), C(112464083f83e03a), C(96ae53e28170c0f5), C(7c6fffe4)},
-    {C(441593e0da922dfe), C(936ef46061469b32), C(204a1921197ddd87),
-     C(bbc78da4)},
-    {C(2ba3883d71cc2133), C(72f2bbb32bed1a3c), C(27e1bd96d4843251),
-     C(c5c25d39)},
-    {C(f2b6d2adf8423600), C(7514e2f016a48722), C(43045743a50396ba),
-     C(b6e5d06e)},
-    {C(38fffe7f3680d63c), C(d513325255a7a6d1), C(31ed47790f6ca62f),
-     C(6178504e)},
-    {C(b7477bf0b9ce37c6), C(63b1c580a7fd02a4), C(f6433b9f10a5dac), C(bd4c3637)},
-    {C(55bdb0e71e3edebd), C(c7ab562bcf0568bc), C(43166332f9ee684f),
-     C(6e7ac474)},
-    {C(782fa1b08b475e7), C(fb7138951c61b23b), C(9829105e234fb11e), C(1fb4b518)},
-    {C(c5dc19b876d37a80), C(15ffcff666cfd710), C(e8c30c72003103e2),
-     C(31d13d6d)},
-    {C(5e1141711d2d6706), C(b537f6dee8de6933), C(3af0a1fbbe027c54),
-     C(26fa72e3)},
-    {C(782edf6da001234f), C(f48cbd5c66c48f3), C(808754d1e64e2a32), C(6a7433bf)},
-    {C(d26285842ff04d44), C(8f38d71341eacca9), C(5ca436f4db7a883c),
-     C(4e6df758)},
-    {C(c6ab830865a6bae6), C(6aa8e8dd4b98815c), C(efe3846713c371e5),
-     C(d57f63ea)},
-    {C(44b3a1929232892), C(61dca0e914fc217), C(a607cc142096b964), C(52ef73b3)},
-    {C(4b603d7932a8de4f), C(fae64c464b8a8f45), C(8fafab75661d602a), C(3cb36c3)},
-    {C(4ec0b54cf1566aff), C(30d2c7269b206bf4), C(77c22e82295e1061),
-     C(72c39bea)},
-    {C(ed8b7a4b34954ff7), C(56432de31f4ee757), C(85bd3abaa572b155),
-     C(a65aa25c)},
-    {C(5d28b43694176c26), C(714cc8bc12d060ae), C(3437726273a83fe6),
-     C(74740539)},
-    {C(6a1ef3639e1d202e), C(919bc1bd145ad928), C(30f3f7e48c28a773),
-     C(c3ae3c26)},
-    {C(159f4d9e0307b111), C(3e17914a5675a0c), C(af849bd425047b51), C(f29db8a2)},
-    {C(cc0a840725a7e25b), C(57c69454396e193a), C(976eaf7eee0b4540),
-     C(1ef4cbf4)},
-    {C(a2b27ee22f63c3f1), C(9ebde0ce1b3976b2), C(2fe6a92a257af308),
-     C(a9be6c41)},
-    {C(d8f2f234899bcab3), C(b10b037297c3a168), C(debea2c510ceda7f), C(fa31801)},
-    {C(584f28543864844f), C(d7cee9fc2d46f20d), C(a38dca5657387205),
-     C(8331c5d8)},
-    {C(a94be46dd9aa41af), C(a57e5b7723d3f9bd), C(34bf845a52fd2f), C(e9876db8)},
-    {C(9a87bea227491d20), C(a468657e2b9c43e7), C(af9ba60db8d89ef7),
-     C(27b0604e)},
-    {C(27688c24958d1a5c), C(e3b4a1c9429cf253), C(48a95811f70d64bc),
-     C(dcec07f2)},
-    {C(5d1d37790a1873ad), C(ed9cd4bcc5fa1090), C(ce51cde05d8cd96a),
-     C(cff0a82a)},
-    {C(1f03fd18b711eea9), C(566d89b1946d381a), C(6e96e83fc92563ab),
-     C(fec83621)},
-    {C(f0316f286cf527b6), C(f84c29538de1aa5a), C(7612ed3c923d4a71), C(743d8dc)},
-    {C(297008bcb3e3401d), C(61a8e407f82b0c69), C(a4a35bff0524fa0e),
-     C(64d41d26)},
-    {C(43c6252411ee3be), C(b4ca1b8077777168), C(2746dc3f7da1737f), C(acd90c81)},
-    {C(ce38a9a54fad6599), C(6d6f4a90b9e8755e), C(c3ecc79ff105de3f),
-     C(7c746a4b)},
-    {C(270a9305fef70cf), C(600193999d884f3a), C(f4d49eae09ed8a1), C(b1047e99)},
-    {C(e71be7c28e84d119), C(eb6ace59932736e6), C(70c4397807ba12c5),
-     C(d1fd1068)},
-    {C(b5b58c24b53aaa19), C(d2a6ab0773dd897f), C(ef762fe01ecb5b97),
-     C(56486077)},
-    {C(44dd59bd301995cf), C(3ccabd76493ada1a), C(540db4c87d55ef23),
-     C(6069be80)},
-    {C(b4d4789eb6f2630b), C(bf6973263ce8ef0e), C(d1c75c50844b9d3), C(2078359b)},
-    {C(12807833c463737c), C(58e927ea3b3776b4), C(72dd20ef1c2f8ad0),
-     C(9ea21004)},
-    {C(e88419922b87176f), C(bcf32f41a7ddbf6f), C(d6ebefd8085c1a0f),
-     C(9c9cfe88)},
-    {C(105191e0ec8f7f60), C(5918dbfcca971e79), C(6b285c8a944767b9),
-     C(b70a6ddd)},
-    {C(a5b88bf7399a9f07), C(fca3ddfd96461cc4), C(ebe738fdc0282fc6),
-     C(dea37298)},
-    {C(d08c3f5747d84f50), C(4e708b27d1b6f8ac), C(70f70fd734888606),
-     C(8f480819)},
-    {C(2f72d12a40044b4b), C(889689352fec53de), C(f03e6ad87eb2f36), C(30b3b16)},
-    {C(aa1f61fdc5c2e11e), C(c2c56cd11277ab27), C(a1e73069fdf1f94f),
-     C(f31bc4e8)},
-    {C(9489b36fe2246244), C(3355367033be74b8), C(5f57c2277cbce516),
-     C(419f953b)},
-    {C(358d7c0476a044cd), C(e0b7b47bcbd8854f), C(ffb42ec696705519),
-     C(20e9e76d)},
-    {C(b0c48df14275265a), C(9da4448975905efa), C(d716618e414ceb6d),
-     C(646f0ff8)},
-    {C(daa70bb300956588), C(410ea6883a240c6d), C(f5c8239fb5673eb3),
-     C(eeb7eca8)},
-    {C(4ec97a20b6c4c7c2), C(5913b1cd454f29fd), C(a9629f9daf06d685), C(8112bb9)},
-    {C(5c3323628435a2e8), C(1bea45ce9e72a6e3), C(904f0a7027ddb52e),
-     C(85a6d477)},
-    {C(c1ef26bea260abdb), C(6ee423f2137f9280), C(df2118b946ed0b43),
-     C(56f76c84)},
-    {C(6be7381b115d653a), C(ed046190758ea511), C(de6a45ffc3ed1159),
-     C(9af45d55)},
-    {C(ae3eece1711b2105), C(14fd3f4027f81a4a), C(abb7e45177d151db),
-     C(d1c33760)},
-    {C(376c28588b8fb389), C(6b045e84d8491ed2), C(4e857effb7d4e7dc),
-     C(c56bbf69)},
-    {C(58d943503bb6748f), C(419c6c8e88ac70f6), C(586760cbf3d3d368),
-     C(abecfb9b)},
-    {C(dfff5989f5cfd9a1), C(bcee2e7ea3a96f83), C(681c7874adb29017),
-     C(8de13255)},
-    {C(7fb19eb1a496e8f5), C(d49e5dfdb5c0833f), C(c0d5d7b2f7c48dc7),
-     C(a98ee299)},
-    {C(5dba5b0dadccdbaa), C(4ba8da8ded87fcdc), C(f693fdd25badf2f0),
-     C(3015f556)},
-    {C(688bef4b135a6829), C(8d31d82abcd54e8e), C(f95f8a30d55036d7),
-     C(5a430e29)},
-    {C(d8323be05433a412), C(8d48fa2b2b76141d), C(3d346f23978336a5),
-     C(2797add0)},
-    {C(3b5404278a55a7fc), C(23ca0b327c2d0a81), C(a6d65329571c892c),
-     C(27d55016)},
-    {C(2a96a3f96c5e9bbc), C(8caf8566e212dda8), C(904de559ca16e45e),
-     C(84945a82)},
-    {C(22bebfdcc26d18ff), C(4b4d8dcb10807ba1), C(40265eee30c6b896),
-     C(3ef7e224)},
-    {C(627a2249ec6bbcc2), C(c0578b462a46735a), C(4974b8ee1c2d4f1f),
-     C(35ed8dc8)},
-    {C(3abaf1667ba2f3e0), C(ee78476b5eeadc1), C(7e56ac0a6ca4f3f4), C(6a75e43d)},
-    {C(3931ac68c5f1b2c9), C(efe3892363ab0fb0), C(40b707268337cd36),
-     C(235d9805)},
-    {C(b98fb0606f416754), C(46a6e5547ba99c1e), C(c909d82112a8ed2), C(f7d69572)},
-    {C(7f7729a33e58fcc4), C(2e4bc1e7a023ead4), C(e707008ea7ca6222),
-     C(bacd0199)},
-    {C(42a0aa9ce82848b3), C(57232730e6bee175), C(f89bb3f370782031),
-     C(e428f50e)},
-    {C(6b2c6d38408a4889), C(de3ef6f68fb25885), C(20754f456c203361),
-     C(81eaaad3)},
-    {C(930380a3741e862a), C(348d28638dc71658), C(89dedcfd1654ea0d),
-     C(addbd3e3)},
-    {C(94808b5d2aa25f9a), C(cec72968128195e0), C(d9f4da2bdc1e130f),
-     C(e66dbca0)},
-    {C(b31abb08ae6e3d38), C(9eb9a95cbd9e8223), C(8019e79b7ee94ea9),
-     C(afe11fd5)},
-    {C(dccb5534a893ea1a), C(ce71c398708c6131), C(fe2396315457c164),
-     C(a71a406f)},
-    {C(6369163565814de6), C(8feb86fb38d08c2f), C(4976933485cc9a20),
-     C(9d90eaf5)},
-    {C(edee4ff253d9f9b3), C(96ef76fb279ef0ad), C(a4d204d179db2460),
-     C(6665db10)},
-    {C(941993df6e633214), C(929bc1beca5b72c6), C(141fc52b8d55572d),
-     C(9c977cbf)},
-    {C(859838293f64cd4c), C(484403b39d44ad79), C(bf674e64d64b9339),
-     C(ee83ddd4)},
-    {C(c19b5648e0d9f555), C(328e47b2b7562993), C(e756b92ba4bd6a51), C(26519cc)},
-    {C(f963b63b9006c248), C(9e9bf727ffaa00bc), C(c73bacc75b917e3a),
-     C(a485a53f)},
-    {C(6a8aa0852a8c1f3b), C(c8f1e5e206a21016), C(2aa554aed1ebb524),
-     C(f62bc412)},
-    {C(740428b4d45e5fb8), C(4c95a4ce922cb0a5), C(e99c3ba78feae796),
-     C(8975a436)},
-    {C(658b883b3a872b86), C(2f0e303f0f64827a), C(975337e23dc45e1), C(94ff7f41)},
-    {C(6df0a977da5d27d4), C(891dd0e7cb19508), C(fd65434a0b71e680), C(760aa031)},
-    {C(a900275464ae07ef), C(11f2cfda34beb4a3), C(9abf91e5a1c38e4), C(3bda76df)},
-    {C(810bc8aa0c40bcb0), C(448a019568d01441), C(f60ec52f60d3aeae),
-     C(498e2e65)},
-    {C(22036327deb59ed7), C(adc05ceb97026a02), C(48bff0654262672b),
-     C(d38deb48)},
-    {C(7d14dfa9772b00c8), C(595735efc7eeaed7), C(29872854f94c3507),
-     C(82b3fb6b)},
-    {C(2d777cddb912675d), C(278d7b10722a13f9), C(f5c02bfb7cc078af),
-     C(e500e25f)},
-    {C(f2ec98824e8aa613), C(5eb7e3fb53fe3bed), C(12c22860466e1dd4),
-     C(bd2bb07c)},
-    {C(5e763988e21f487f), C(24189de8065d8dc5), C(d1519d2403b62aa0),
-     C(3a2b431d)},
-    {C(48949dc327bb96ad), C(e1fd21636c5c50b4), C(3f6eb7f13a8712b4),
-     C(7322a83d)},
-    {C(b7c4209fb24a85c5), C(b35feb319c79ce10), C(f0d3de191833b922),
-     C(a645ca1c)},
-    {C(9c9e5be0943d4b05), C(b73dc69e45201cbb), C(aab17180bfe5083d),
-     C(8909a45a)},
-    {C(3898bca4dfd6638d), C(f911ff35efef0167), C(24bdf69e5091fc88),
-     C(bd30074c)},
-    {C(5b5d2557400e68e7), C(98d610033574cee), C(dfd08772ce385deb), C(c17cf001)},
-    {C(a927ed8b2bf09bb6), C(606e52f10ae94eca), C(71c2203feb35a9ee),
-     C(26ffd25a)},
-    {C(8d25746414aedf28), C(34b1629d28b33d3a), C(4d5394aea5f82d7b),
-     C(f1d8ce3c)},
-    {C(b5bbdb73458712f2), C(1ff887b3c2a35137), C(7f7231f702d0ace9),
-     C(3ee8fb17)},
-    {C(3d32a26e3ab9d254), C(fc4070574dc30d3a), C(f02629579c2b27c9),
-     C(a77acc2a)},
-    {C(9371d3c35fa5e9a5), C(42967cf4d01f30), C(652d1eeae704145c), C(f4556dee)},
-    {C(cbaa3cb8f64f54e0), C(76c3b48ee5c08417), C(9f7d24e87e61ce9), C(de287a64)},
-    {C(b2e23e8116c2ba9f), C(7e4d9c0060101151), C(3310da5e5028f367),
-     C(878e55b9)},
-    {C(8aa77f52d7868eb9), C(4d55bd587584e6e2), C(d2db37041f495f5), C(7648486)},
-    {C(858fea922c7fe0c3), C(cfe8326bf733bc6f), C(4e5e2018cf8f7dfc),
-     C(57ac0fb1)},
-    {C(46ef25fdec8392b1), C(e48d7b6d42a5cd35), C(56a6fe1c175299ca),
-     C(d01967ca)},
-    {C(8d078f726b2df464), C(b50ee71cdcabb299), C(f4af300106f9c7ba),
-     C(96ecdf74)},
-    {C(35ea86e6960ca950), C(34fe1fe234fc5c76), C(a00207a3dc2a72b7),
-     C(779f5506)},
-    {C(8aee9edbc15dd011), C(51f5839dc8462695), C(b2213e17c37dca2d),
-     C(3c94c2de)},
-    {C(c3e142ba98432dda), C(911d060cab126188), C(b753fbfa8365b844),
-     C(39f98faf)},
-    {C(123ba6b99c8cd8db), C(448e582672ee07c4), C(cebe379292db9e65),
-     C(7af31199)},
-    {C(ba87acef79d14f53), C(b3e0fcae63a11558), C(d5ac313a593a9f45),
-     C(e341a9d6)},
-    {C(bcd3957d5717dc3), C(2da746741b03a007), C(873816f4b1ece472), C(ca24aeeb)},
-    {C(61442ff55609168e), C(6447c5fc76e8c9cf), C(6a846de83ae15728),
-     C(b2252b57)},
-    {C(dbe4b1b2d174757f), C(506512da18712656), C(6857f3e0b8dd95f), C(72c81da1)},
-    {C(531e8e77b363161c), C(eece0b43e2dae030), C(8294b82c78f34ed1),
-     C(6b9fce95)},
-    {C(f71e9c926d711e2b), C(d77af2853a4ceaa1), C(9aa0d6d76a36fae7),
-     C(19399857)},
-    {C(cb20ac28f52df368), C(e6705ee7880996de), C(9b665cc3ec6972f2),
-     C(3c57a994)},
-    {C(e4a794b4acb94b55), C(89795358057b661b), C(9c4cdcec176d7a70),
-     C(c053e729)},
-    {C(cb942e91443e7208), C(e335de8125567c2a), C(d4d74d268b86df1f),
-     C(51cbbba7)},
-    {C(ecca7563c203f7ba), C(177ae2423ef34bb2), C(f60b7243400c5731),
-     C(1acde79a)},
-    {C(1652cb940177c8b5), C(8c4fe7d85d2a6d6d), C(f6216ad097e54e72),
-     C(2d160d13)},
-    {C(31fed0fc04c13ce8), C(3d5d03dbf7ff240a), C(727c5c9b51581203),
-     C(787f5801)},
-    {C(e7b668947590b9b3), C(baa41ad32938d3fa), C(abcbc8d4ca4b39e4),
-     C(c9629828)},
-    {C(1de2119923e8ef3c), C(6ab27c096cf2fe14), C(8c3658edca958891),
-     C(be139231)},
-    {C(1269df1e69e14fa7), C(992f9d58ac5041b7), C(e97fcf695a7cbbb4),
-     C(7df699ef)},
-    {C(820826d7aba567ff), C(1f73d28e036a52f3), C(41c4c5a73f3b0893),
-     C(8ce6b96d)},
-    {C(ffe0547e4923cef9), C(3534ed49b9da5b02), C(548a273700fba03d),
-     C(6f9ed99c)},
-    {C(72da8d1b11d8bc8b), C(ba94b56b91b681c6), C(4e8cc51bd9b0fc8c),
-     C(e0244796)},
-    {C(d62ab4e3f88fc797), C(ea86c7aeb6283ae4), C(b5b93e09a7fe465), C(4ccf7e75)},
-    {C(d0f06c28c7b36823), C(1008cb0874de4bb8), C(d6c7ff816c7a737b),
-     C(915cef86)},
-    {C(99b7042460d72ec6), C(2a53e5e2b8e795c2), C(53a78132d9e1b3e3),
-     C(5cb59482)},
-    {C(4f4dfcfc0ec2bae5), C(841233148268a1b8), C(9248a76ab8be0d3), C(6ca3f532)},
-    {C(fe86bf9d4422b9ae), C(ebce89c90641ef9c), C(1c84e2292c0b5659),
-     C(e24f3859)},
-    {C(a90d81060932dbb0), C(8acfaa88c5fbe92b), C(7c6f3447e90f7f3f),
-     C(adf5a9c7)},
-    {C(17938a1b0e7f5952), C(22cadd2f56f8a4be), C(84b0d1183d5ed7c1),
-     C(32264b75)},
-    {C(de9e0cb0e16f6e6d), C(238e6283aa4f6594), C(4fb9c914c2f0a13b),
-     C(a64b3376)},
-    {C(6d4b876d9b146d1a), C(aab2d64ce8f26739), C(d315f93600e83fe5), C(d33890e)},
-    {C(e698fa3f54e6ea22), C(bd28e20e7455358c), C(9ace161f6ea76e66),
-     C(926d4b63)},
-    {C(7bc0deed4fb349f7), C(1771aff25dc722fa), C(19ff0644d9681917),
-     C(d51ba539)},
-    {C(db4b15e88533f622), C(256d6d2419b41ce9), C(9d7c5378396765d5),
-     C(7f37636d)},
-    {C(922834735e86ecb2), C(363382685b88328e), C(e9c92960d7144630),
-     C(b98026c0)},
-    {C(30f1d72c812f1eb8), C(b567cd4a69cd8989), C(820b6c992a51f0bc),
-     C(b877767e)},
-    {C(168884267f3817e9), C(5b376e050f637645), C(1c18314abd34497a), C(aefae77)},
-    {C(82e78596ee3e56a7), C(25697d9c87f30d98), C(7600a8342834924d), C(f686911)},
-    {C(aa2d6cf22e3cc252), C(9b4dec4f5e179f16), C(76fb0fba1d99a99a),
-     C(3deadf12)},
-    {C(7bf5ffd7f69385c7), C(fc077b1d8bc82879), C(9c04e36f9ed83a24),
-     C(ccf02a4e)},
-    {C(e89c8ff9f9c6e34b), C(f54c0f669a49f6c4), C(fc3e46f5d846adef),
-     C(176c1722)},
-    {C(a18fbcdccd11e1f4), C(8248216751dfd65e), C(40c089f208d89d7c), C(26f82ad)},
-    {C(2d54f40cc4088b17), C(59d15633b0cd1399), C(a8cc04bb1bffd15b),
-     C(b5244f42)},
-    {C(69276946cb4e87c7), C(62bdbe6183be6fa9), C(3ba9773dac442a1a),
-     C(49a689e5)},
-    {C(668174a3f443df1d), C(407299392da1ce86), C(c2a3f7d7f2c5be28), C(59fcdd3)},
-    {C(5e29be847bd5046), C(b561c7f19c8f80c3), C(5e5abd5021ccaeaf), C(4f4b04e9)},
-    {C(cd0d79f2164da014), C(4c386bb5c5d6ca0c), C(8e771b03647c3b63),
-     C(8b00f891)},
-    {C(e0e6fc0b1628af1d), C(29be5fb4c27a2949), C(1c3f781a604d3630),
-     C(16e114f3)},
-    {C(2058927664adfd93), C(6e8f968c7963baa5), C(af3dced6fff7c394),
-     C(d6b6dadc)},
-    {C(dc107285fd8e1af7), C(a8641a0609321f3f), C(db06e89ffdc54466),
-     C(897e20ac)},
-    {C(fbba1afe2e3280f1), C(755a5f392f07fce), C(9e44a9a15402809a), C(f996e05d)},
-    {C(bfa10785ddc1011b), C(b6e1c4d2f670f7de), C(517d95604e4fcc1f),
-     C(c4306af6)},
-    {C(534cc35f0ee1eb4e), C(b703820f1f3b3dce), C(884aa164cf22363), C(6dcad433)},
-    {C(7ca6e3933995dac), C(fd118c77daa8188), C(3aceb7b5e7da6545), C(3c07374d)},
-    {C(f0d6044f6efd7598), C(e044d6ba4369856e), C(91968e4f8c8a1a4c),
-     C(f0f4602c)},
-    {C(3d69e52049879d61), C(76610636ea9f74fe), C(e9bf5602f89310c0),
-     C(3e1ea071)},
-    {C(79da242a16acae31), C(183c5f438e29d40), C(6d351710ae92f3de), C(67580f0c)},
-    {C(461c82656a74fb57), C(d84b491b275aa0f7), C(8f262cb29a6eb8b2),
-     C(4e109454)},
-    {C(53c1a66d0b13003), C(731f060e6fe797fc), C(daa56811791371e3), C(88a474a7)},
-    {C(d3a2efec0f047e9), C(1cabce58853e58ea), C(7a17b2eae3256be4), C(5b5bedd)},
-    {C(43c64d7484f7f9b2), C(5da002b64aafaeb7), C(b576c1e45800a716),
-     C(1aaddfa7)},
-    {C(a7dec6ad81cf7fa1), C(180c1ab708683063), C(95e0fd7008d67cff),
-     C(5be07fd8)},
-    {C(5408a1df99d4aff), C(b9565e588740f6bd), C(abf241813b08006e), C(cbca8606)},
-    {C(a8b27a6bcaeeed4b), C(aec1eeded6a87e39), C(9daf246d6fed8326),
-     C(bde64d01)},
-    {C(9a952a8246fdc269), C(d0dcfcac74ef278c), C(250f7139836f0f1f),
-     C(ee90cf33)},
-    {C(c930841d1d88684f), C(5eb66eb18b7f9672), C(e455d413008a2546),
-     C(4305c3ce)},
-    {C(94dc6971e3cf071a), C(994c7003b73b2b34), C(ea16e85978694e5), C(4b3a1d76)},
-    {C(7fc98006e25cac9), C(77fee0484cda86a7), C(376ec3d447060456), C(a8bb6d80)},
-    {C(bd781c4454103f6), C(612197322f49c931), C(b9cf17fd7e5462d5), C(1f9fa607)},
-    {C(da60e6b14479f9df), C(3bdccf69ece16792), C(18ebf45c4fecfdc9),
-     C(8d0e4ed2)},
-    {C(4ca56a348b6c4d3), C(60618537c3872514), C(2fbb9f0e65871b09), C(1bf31347)},
-    {C(ebd22d4b70946401), C(6863602bf7139017), C(c0b1ac4e11b00666),
-     C(1ae3fc5b)},
-    {C(3cc4693d6cbcb0c), C(501689ea1c70ffa), C(10a4353e9c89e364), C(459c3930)},
-    {C(38908e43f7ba5ef0), C(1ab035d4e7781e76), C(41d133e8c0a68ff7),
-     C(e00c4184)},
-    {C(34983ccc6aa40205), C(21802cad34e72bc4), C(1943e8fb3c17bb8), C(ffc7a781)},
-    {C(86215c45dcac9905), C(ea546afe851cae4b), C(d85b6457e489e374),
-     C(6a125480)},
-    {C(420fc255c38db175), C(d503cd0f3c1208d1), C(d4684e74c825a0bc),
-     C(88a1512b)},
-    {C(1d7a31f5bc8fe2f9), C(4763991092dcf836), C(ed695f55b97416f4),
-     C(549bbbe5)},
-    {C(94129a84c376a26e), C(c245e859dc231933), C(1b8f74fecf917453),
-     C(c133d38c)},
-    {C(1d3a9809dab05c8d), C(adddeb4f71c93e8), C(ef342eb36631edb), C(fcace348)},
-    {C(90fa3ccbd60848da), C(dfa6e0595b569e11), C(e585d067a1f5135d),
-     C(ed7b6f9a)},
-    {C(2dbb4fc71b554514), C(9650e04b86be0f82), C(60f2304fba9274d3),
-     C(6d907dda)},
-    {C(b98bf4274d18374a), C(1b669fd4c7f9a19a), C(b1f5972b88ba2b7a),
-     C(7a4d48d5)},
-    {C(d6781d0b5e18eb68), C(b992913cae09b533), C(58f6021caaee3a40),
-     C(e686f3db)},
-    {C(226651cf18f4884c), C(595052a874f0f51c), C(c9b75162b23bab42), C(cce7c55)},
-    {C(a734fb047d3162d6), C(e523170d240ba3a5), C(125a6972809730e8), C(f58b96b)},
-    {C(c6df6364a24f75a3), C(c294e2c84c4f5df8), C(a88df65c6a89313b),
-     C(1bbf6f60)},
-    {C(d8d1364c1fbcd10), C(2d7cc7f54832deaa), C(4e22c876a7c57625), C(ce5e0cc2)},
-    {C(aae06f9146db885f), C(3598736441e280d9), C(fba339b117083e55),
-     C(584cfd6f)},
-    {C(8955ef07631e3bcc), C(7d70965ea3926f83), C(39aed4134f8b2db6),
-     C(8f9bbc33)},
-    {C(ad611c609cfbe412), C(d3c00b18bf253877), C(90b2172e1f3d0bfd),
-     C(d7640d95)},
-    {C(d5339adc295d5d69), C(b633cc1dcb8b586a), C(ee84184cf5b1aeaf), C(3d12a2b)},
-    {C(40d0aeff521375a8), C(77ba1ad7ecebd506), C(547c6f1a7d9df427),
-     C(aaeafed0)},
-    {C(8b2d54ae1a3df769), C(11e7adaee3216679), C(3483781efc563e03),
-     C(95b9b814)},
-    {C(99c175819b4eae28), C(932e8ff9f7a40043), C(ec78dcab07ca9f7c),
-     C(45fbe66e)},
-    {C(2a418335779b82fc), C(af0295987849a76b), C(c12bc5ff0213f46e),
-     C(b4baa7a8)},
-    {C(3b1fc6a3d279e67d), C(70ea1e49c226396), C(25505adcf104697c), C(83e962fe)},
-    {C(d97eacdf10f1c3c9), C(b54f4654043a36e0), C(b128f6eb09d1234), C(aac3531c)},
-    {C(293a5c1c4e203cd4), C(6b3329f1c130cefe), C(f2e32f8ec76aac91),
-     C(2b1db7cc)},
-    {C(4290e018ffaedde7), C(a14948545418eb5e), C(72d851b202284636),
-     C(cf00cd31)},
-    {C(f919a59cbde8bf2f), C(a56d04203b2dc5a5), C(38b06753ac871e48),
-     C(7d3c43b8)},
-    {C(1d70a3f5521d7fa4), C(fb97b3fdc5891965), C(299d49bbbe3535af),
-     C(cbd5fac6)},
-    {C(6af98d7b656d0d7c), C(d2e99ae96d6b5c0c), C(f63bd1603ef80627),
-     C(76d0fec4)},
-    {C(395b7a8adb96ab75), C(582df7165b20f4a), C(e52bd30e9ff657f9), C(405e3402)},
-    {C(3822dd82c7df012f), C(b9029b40bd9f122b), C(fd25b988468266c4),
-     C(c732c481)},
-    {C(79f7efe4a80b951a), C(dd3a3fddfc6c9c41), C(ab4c812f9e27aa40),
-     C(a8d123c9)},
-    {C(ae6e59f5f055921a), C(e9d9b7bf68e82), C(5ce4e4a5b269cc59), C(1e80ad7d)},
-    {C(8959dbbf07387d36), C(b4658afce48ea35d), C(8f3f82437d8cb8d6),
-     C(52aeb863)},
-    {C(4739613234278a49), C(99ea5bcd340bf663), C(258640912e712b12),
-     C(ef7c0c18)},
-    {C(420e6c926bc54841), C(96dbbf6f4e7c75cd), C(d8d40fa70c3c67bb),
-     C(b6ad4b68)},
-    {C(c8601bab561bc1b7), C(72b26272a0ff869a), C(56fdfc986d6bc3c4),
-     C(c1e46b17)},
-    {C(b2d294931a0e20eb), C(284ffd9a0815bc38), C(1f8a103aac9bbe6), C(57b8df25)},
-    {C(7966f53c37b6c6d7), C(8e6abcfb3aa2b88f), C(7f2e5e0724e5f345),
-     C(e9fa36d6)},
-    {C(be9bb0abd03b7368), C(13bca93a3031be55), C(e864f4f52b55b472),
-     C(8f8daefc)},
-    {C(a08d128c5f1649be), C(a8166c3dbbe19aad), C(cb9f914f829ec62c), C(6e1bb7e)},
-    {C(7c386f0ffe0465ac), C(530419c9d843dbf3), C(7450e3a4f72b8d8c),
-     C(fd0076f0)},
-    {C(bb362094e7ef4f8), C(ff3c2a48966f9725), C(55152803acd4a7fe), C(899b17b6)},
-    {C(cd80dea24321eea4), C(52b4fdc8130c2b15), C(f3ea100b154bfb82),
-     C(e3e84e31)},
-    {C(d599a04125372c3a), C(313136c56a56f363), C(1e993c3677625832),
-     C(eef79b6b)},
-    {C(dbbf541e9dfda0a), C(1479fceb6db4f844), C(31ab576b59062534), C(868e3315)},
-    {C(c2ee3288be4fe2bf), C(c65d2f5ddf32b92), C(af6ecdf121ba5485), C(4639a426)},
-    {C(d86603ced1ed4730), C(f9de718aaada7709), C(db8b9755194c6535),
-     C(f3213646)},
-    {C(915263c671b28809), C(a815378e7ad762fd), C(abec6dc9b669f559),
-     C(17f148e9)},
-    {C(2b67cdd38c307a5e), C(cb1d45bb5c9fe1c), C(800baf2a02ec18ad), C(bfd94880)},
-    {C(2d107419073b9cd0), C(a96db0740cef8f54), C(ec41ee91b3ecdc1b),
-     C(bb1fa7f3)},
-    {C(f3e9487ec0e26dfc), C(1ab1f63224e837fa), C(119983bb5a8125d8), C(88816b1)},
-    {C(1160987c8fe86f7d), C(879e6db1481eb91b), C(d7dcb802bfe6885d),
-     C(5c2faeb3)},
-    {C(eab8112c560b967b), C(97f550b58e89dbae), C(846ed506d304051f),
-     C(51b5fc6f)},
-    {C(1addcf0386d35351), C(b5f436561f8f1484), C(85d38e22181c9bb1),
-     C(33d94752)},
-    {C(d445ba84bf803e09), C(1216c2497038f804), C(2293216ea2237207),
-     C(b0c92948)},
-    {C(37235a096a8be435), C(d9b73130493589c2), C(3b1024f59378d3be),
-     C(c7171590)},
-    {C(763ad6ea2fe1c99d), C(cf7af5368ac1e26b), C(4d5e451b3bb8d3d4),
-     C(240a67fb)},
-    {C(ea627fc84cd1b857), C(85e372494520071f), C(69ec61800845780b),
-     C(e1843cd5)},
-    {C(1f2ffd79f2cdc0c8), C(726a1bc31b337aaa), C(678b7f275ef96434),
-     C(fda1452b)},
-    {C(39a9e146ec4b3210), C(f63f75802a78b1ac), C(e2e22539c94741c3),
-     C(a2cad330)},
-    {C(74cba303e2dd9d6d), C(692699b83289fad1), C(dfb9aa7874678480),
-     C(53467e16)},
-    {C(4cbc2b73a43071e0), C(56c5db4c4ca4e0b7), C(1b275a162f46bd3d),
-     C(da14a8d0)},
-    {C(875638b9715d2221), C(d9ba0615c0c58740), C(616d4be2dfe825aa),
-     C(67333551)},
-    {C(fb686b2782994a8d), C(edee60693756bb48), C(e6bc3cae0ded2ef5),
-     C(a0ebd66e)},
-    {C(ab21d81a911e6723), C(4c31b07354852f59), C(835da384c9384744),
-     C(4b769593)},
-    {C(33d013cc0cd46ecf), C(3de726423aea122c), C(116af51117fe21a9),
-     C(6aa75624)},
-    {C(8ca92c7cd39fae5d), C(317e620e1bf20f1), C(4f0b33bf2194b97f), C(602a3f96)},
-    {C(fdde3b03f018f43e), C(38f932946c78660), C(c84084ce946851ee), C(cd183c4d)},
-    {C(9c8502050e9c9458), C(d6d2a1a69964beb9), C(1675766f480229b5),
-     C(960a4d07)},
-    {C(348176ca2fa2fdd2), C(3a89c514cc360c2d), C(9f90b8afb318d6d0),
-     C(9ae998c4)},
-    {C(4a3d3dfbbaea130b), C(4e221c920f61ed01), C(553fd6cd1304531f),
-     C(74e2179d)},
-    {C(b371f768cdf4edb9), C(bdef2ace6d2de0f0), C(e05b4100f7f1baec),
-     C(ee9bae25)},
-    {C(7a1d2e96934f61f), C(eb1760ae6af7d961), C(887eb0da063005df), C(b66edf10)},
-    {C(8be53d466d4728f2), C(86a5ac8e0d416640), C(984aa464cdb5c8bb),
-     C(d6209737)},
-    {C(829677eb03abf042), C(43cad004b6bc2c0), C(f2f224756803971a), C(b994a88)},
-    {C(754435bae3496fc), C(5707fc006f094dcf), C(8951c86ab19d8e40), C(a05d43c0)},
-    {C(fda9877ea8e3805f), C(31e868b6ffd521b7), C(b08c90681fb6a0fd),
-     C(c79f73a8)},
-    {C(2e36f523ca8f5eb5), C(8b22932f89b27513), C(331cd6ecbfadc1bb),
-     C(a490aff5)},
-    {C(21a378ef76828208), C(a5c13037fa841da2), C(506d22a53fbe9812),
-     C(dfad65b4)},
-    {C(ccdd5600054b16ca), C(f78846e84204cb7b), C(1f9faec82c24eac9), C(1d07dfb)},
-    {C(7854468f4e0cabd0), C(3a3f6b4f098d0692), C(ae2423ec7799d30d),
-     C(416df9a0)},
-    {C(7f88db5346d8f997), C(88eac9aacc653798), C(68a4d0295f8eefa1),
-     C(1f8fb9cc)},
-    {C(bb3fb5fb01d60fcf), C(1b7cc0847a215eb6), C(1246c994437990a1),
-     C(7abf48e3)},
-    {C(2e783e1761acd84d), C(39158042bac975a0), C(1cd21c5a8071188d),
-     C(dea4e3dd)},
-    {C(392058251cf22acc), C(944ec4475ead4620), C(b330a10b5cb94166),
-     C(c6064f22)},
-    {C(adf5c1e5d6419947), C(2a9747bc659d28aa), C(95c5b8cb1f5d62c), C(743bed9c)},
-    {C(6bc1db2c2bee5aba), C(e63b0ed635307398), C(7b2eca111f30dbbc),
-     C(fce254d5)},
-    {C(b00f898229efa508), C(83b7590ad7f6985c), C(2780e70a0592e41d),
-     C(e47ec9d1)},
-    {C(b56eb769ce0d9a8c), C(ce196117bfbcaf04), C(b26c3c3797d66165),
-     C(334a145c)},
-    {C(70c0637675b94150), C(259e1669305b0a15), C(46e1dd9fd387a58d),
-     C(adec1e3c)},
-    {C(74c0b8a6821faafe), C(abac39d7491370e7), C(faf0b2a48a4e6aed),
-     C(f6a9fbf8)},
-    {C(5fb5e48ac7b7fa4f), C(a96170f08f5acbc7), C(bbf5c63d4f52a1e5),
-     C(5398210c)},
-};
-
-void TestUnchanging(const uint64_t* expected, int offset, int len) {
-  EXPECT_EQ(expected[0], CityHash64(data + offset, len));
-  EXPECT_EQ(expected[3], CityHash32(data + offset, len));
-  EXPECT_EQ(expected[1], CityHash64WithSeed(data + offset, len, kSeed0));
-  EXPECT_EQ(expected[2],
-            CityHash64WithSeeds(data + offset, len, kSeed0, kSeed1));
-}
-
-TEST(CityHashTest, Unchanging) {
-  setup();
-  int i = 0;
-  for (; i < kTestSize - 1; i++) {
-    TestUnchanging(testdata[i], i * i, i);
-  }
-  TestUnchanging(testdata[i], 0, kDataSize);
-}
-
-}  // namespace hash_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/hash/internal/hash.cc b/third_party/abseil_cpp/absl/hash/internal/hash.cc
deleted file mode 100644
index b44ecb3a6b..0000000000
--- a/third_party/abseil_cpp/absl/hash/internal/hash.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/hash/internal/hash.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace hash_internal {
-
-uint64_t CityHashState::CombineLargeContiguousImpl32(uint64_t state,
-                                                     const unsigned char* first,
-                                                     size_t len) {
-  while (len >= PiecewiseChunkSize()) {
-    state =
-        Mix(state, absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first),
-                                         PiecewiseChunkSize()));
-    len -= PiecewiseChunkSize();
-    first += PiecewiseChunkSize();
-  }
-  // Handle the remainder.
-  return CombineContiguousImpl(state, first, len,
-                               std::integral_constant<int, 4>{});
-}
-
-uint64_t CityHashState::CombineLargeContiguousImpl64(uint64_t state,
-                                                     const unsigned char* first,
-                                                     size_t len) {
-  while (len >= PiecewiseChunkSize()) {
-    state =
-        Mix(state, absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first),
-                                         PiecewiseChunkSize()));
-    len -= PiecewiseChunkSize();
-    first += PiecewiseChunkSize();
-  }
-  // Handle the remainder.
-  return CombineContiguousImpl(state, first, len,
-                               std::integral_constant<int, 8>{});
-}
-
-ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed;
-
-}  // namespace hash_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/hash/internal/hash.h b/third_party/abseil_cpp/absl/hash/internal/hash.h
deleted file mode 100644
index b0132da206..0000000000
--- a/third_party/abseil_cpp/absl/hash/internal/hash.h
+++ /dev/null
@@ -1,1003 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: hash.h
-// -----------------------------------------------------------------------------
-//
-#ifndef ABSL_HASH_INTERNAL_HASH_H_
-#define ABSL_HASH_INTERNAL_HASH_H_
-
-#include <algorithm>
-#include <array>
-#include <cmath>
-#include <cstring>
-#include <deque>
-#include <forward_list>
-#include <functional>
-#include <iterator>
-#include <limits>
-#include <list>
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "absl/base/internal/endian.h"
-#include "absl/base/port.h"
-#include "absl/container/fixed_array.h"
-#include "absl/meta/type_traits.h"
-#include "absl/numeric/int128.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/optional.h"
-#include "absl/types/variant.h"
-#include "absl/utility/utility.h"
-#include "absl/hash/internal/city.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace hash_internal {
-
-// Internal detail: Large buffers are hashed in smaller chunks.  This function
-// returns the size of these chunks.
-constexpr size_t PiecewiseChunkSize() { return 1024; }
-
-// PiecewiseCombiner
-//
-// PiecewiseCombiner is an internal-only helper class for hashing a piecewise
-// buffer of `char` or `unsigned char` as though it were contiguous.  This class
-// provides two methods:
-//
-//   H add_buffer(state, data, size)
-//   H finalize(state)
-//
-// `add_buffer` can be called zero or more times, followed by a single call to
-// `finalize`.  This will produce the same hash expansion as concatenating each
-// buffer piece into a single contiguous buffer, and passing this to
-// `H::combine_contiguous`.
-//
-//  Example usage:
-//    PiecewiseCombiner combiner;
-//    for (const auto& piece : pieces) {
-//      state = combiner.add_buffer(std::move(state), piece.data, piece.size);
-//    }
-//    return combiner.finalize(std::move(state));
-class PiecewiseCombiner {
- public:
-  PiecewiseCombiner() : position_(0) {}
-  PiecewiseCombiner(const PiecewiseCombiner&) = delete;
-  PiecewiseCombiner& operator=(const PiecewiseCombiner&) = delete;
-
-  // PiecewiseCombiner::add_buffer()
-  //
-  // Appends the given range of bytes to the sequence to be hashed, which may
-  // modify the provided hash state.
-  template <typename H>
-  H add_buffer(H state, const unsigned char* data, size_t size);
-  template <typename H>
-  H add_buffer(H state, const char* data, size_t size) {
-    return add_buffer(std::move(state),
-                      reinterpret_cast<const unsigned char*>(data), size);
-  }
-
-  // PiecewiseCombiner::finalize()
-  //
-  // Finishes combining the hash sequence, which may may modify the provided
-  // hash state.
-  //
-  // Once finalize() is called, add_buffer() may no longer be called. The
-  // resulting hash state will be the same as if the pieces passed to
-  // add_buffer() were concatenated into a single flat buffer, and then provided
-  // to H::combine_contiguous().
-  template <typename H>
-  H finalize(H state);
-
- private:
-  unsigned char buf_[PiecewiseChunkSize()];
-  size_t position_;
-};
-
-// HashStateBase
-//
-// A hash state object represents an intermediate state in the computation
-// of an unspecified hash algorithm. `HashStateBase` provides a CRTP style
-// base class for hash state implementations. Developers adding type support
-// for `absl::Hash` should not rely on any parts of the state object other than
-// the following member functions:
-//
-//   * HashStateBase::combine()
-//   * HashStateBase::combine_contiguous()
-//
-// A derived hash state class of type `H` must provide a static member function
-// with a signature similar to the following:
-//
-//    `static H combine_contiguous(H state, const unsigned char*, size_t)`.
-//
-// `HashStateBase` will provide a complete implementation for a hash state
-// object in terms of this method.
-//
-// Example:
-//
-//   // Use CRTP to define your derived class.
-//   struct MyHashState : HashStateBase<MyHashState> {
-//       static H combine_contiguous(H state, const unsigned char*, size_t);
-//       using MyHashState::HashStateBase::combine;
-//       using MyHashState::HashStateBase::combine_contiguous;
-//   };
-template <typename H>
-class HashStateBase {
- public:
-  // HashStateBase::combine()
-  //
-  // Combines an arbitrary number of values into a hash state, returning the
-  // updated state.
-  //
-  // Each of the value types `T` must be separately hashable by the Abseil
-  // hashing framework.
-  //
-  // NOTE:
-  //
-  //   state = H::combine(std::move(state), value1, value2, value3);
-  //
-  // is guaranteed to produce the same hash expansion as:
-  //
-  //   state = H::combine(std::move(state), value1);
-  //   state = H::combine(std::move(state), value2);
-  //   state = H::combine(std::move(state), value3);
-  template <typename T, typename... Ts>
-  static H combine(H state, const T& value, const Ts&... values);
-  static H combine(H state) { return state; }
-
-  // HashStateBase::combine_contiguous()
-  //
-  // Combines a contiguous array of `size` elements into a hash state, returning
-  // the updated state.
-  //
-  // NOTE:
-  //
-  //   state = H::combine_contiguous(std::move(state), data, size);
-  //
-  // is NOT guaranteed to produce the same hash expansion as a for-loop (it may
-  // perform internal optimizations).  If you need this guarantee, use the
-  // for-loop instead.
-  template <typename T>
-  static H combine_contiguous(H state, const T* data, size_t size);
-
-  using AbslInternalPiecewiseCombiner = PiecewiseCombiner;
-};
-
-// is_uniquely_represented
-//
-// `is_uniquely_represented<T>` is a trait class that indicates whether `T`
-// is uniquely represented.
-//
-// A type is "uniquely represented" if two equal values of that type are
-// guaranteed to have the same bytes in their underlying storage. In other
-// words, if `a == b`, then `memcmp(&a, &b, sizeof(T))` is guaranteed to be
-// zero. This property cannot be detected automatically, so this trait is false
-// by default, but can be specialized by types that wish to assert that they are
-// uniquely represented. This makes them eligible for certain optimizations.
-//
-// If you have any doubt whatsoever, do not specialize this template.
-// The default is completely safe, and merely disables some optimizations
-// that will not matter for most types. Specializing this template,
-// on the other hand, can be very hazardous.
-//
-// To be uniquely represented, a type must not have multiple ways of
-// representing the same value; for example, float and double are not
-// uniquely represented, because they have distinct representations for
-// +0 and -0. Furthermore, the type's byte representation must consist
-// solely of user-controlled data, with no padding bits and no compiler-
-// controlled data such as vptrs or sanitizer metadata. This is usually
-// very difficult to guarantee, because in most cases the compiler can
-// insert data and padding bits at its own discretion.
-//
-// If you specialize this template for a type `T`, you must do so in the file
-// that defines that type (or in this file). If you define that specialization
-// anywhere else, `is_uniquely_represented<T>` could have different meanings
-// in different places.
-//
-// The Enable parameter is meaningless; it is provided as a convenience,
-// to support certain SFINAE techniques when defining specializations.
-template <typename T, typename Enable = void>
-struct is_uniquely_represented : std::false_type {};
-
-// is_uniquely_represented<unsigned char>
-//
-// unsigned char is a synonym for "byte", so it is guaranteed to be
-// uniquely represented.
-template <>
-struct is_uniquely_represented<unsigned char> : std::true_type {};
-
-// is_uniquely_represented for non-standard integral types
-//
-// Integral types other than bool should be uniquely represented on any
-// platform that this will plausibly be ported to.
-template <typename Integral>
-struct is_uniquely_represented<
-    Integral, typename std::enable_if<std::is_integral<Integral>::value>::type>
-    : std::true_type {};
-
-// is_uniquely_represented<bool>
-//
-//
-template <>
-struct is_uniquely_represented<bool> : std::false_type {};
-
-// hash_bytes()
-//
-// Convenience function that combines `hash_state` with the byte representation
-// of `value`.
-template <typename H, typename T>
-H hash_bytes(H hash_state, const T& value) {
-  const unsigned char* start = reinterpret_cast<const unsigned char*>(&value);
-  return H::combine_contiguous(std::move(hash_state), start, sizeof(value));
-}
-
-// -----------------------------------------------------------------------------
-// AbslHashValue for Basic Types
-// -----------------------------------------------------------------------------
-
-// Note: Default `AbslHashValue` implementations live in `hash_internal`. This
-// allows us to block lexical scope lookup when doing an unqualified call to
-// `AbslHashValue` below. User-defined implementations of `AbslHashValue` can
-// only be found via ADL.
-
-// AbslHashValue() for hashing bool values
-//
-// We use SFINAE to ensure that this overload only accepts bool, not types that
-// are convertible to bool.
-template <typename H, typename B>
-typename std::enable_if<std::is_same<B, bool>::value, H>::type AbslHashValue(
-    H hash_state, B value) {
-  return H::combine(std::move(hash_state),
-                    static_cast<unsigned char>(value ? 1 : 0));
-}
-
-// AbslHashValue() for hashing enum values
-template <typename H, typename Enum>
-typename std::enable_if<std::is_enum<Enum>::value, H>::type AbslHashValue(
-    H hash_state, Enum e) {
-  // In practice, we could almost certainly just invoke hash_bytes directly,
-  // but it's possible that a sanitizer might one day want to
-  // store data in the unused bits of an enum. To avoid that risk, we
-  // convert to the underlying type before hashing. Hopefully this will get
-  // optimized away; if not, we can reopen discussion with c-toolchain-team.
-  return H::combine(std::move(hash_state),
-                    static_cast<typename std::underlying_type<Enum>::type>(e));
-}
-// AbslHashValue() for hashing floating-point values
-template <typename H, typename Float>
-typename std::enable_if<std::is_same<Float, float>::value ||
-                            std::is_same<Float, double>::value,
-                        H>::type
-AbslHashValue(H hash_state, Float value) {
-  return hash_internal::hash_bytes(std::move(hash_state),
-                                   value == 0 ? 0 : value);
-}
-
-// Long double has the property that it might have extra unused bytes in it.
-// For example, in x86 sizeof(long double)==16 but it only really uses 80-bits
-// of it. This means we can't use hash_bytes on a long double and have to
-// convert it to something else first.
-template <typename H, typename LongDouble>
-typename std::enable_if<std::is_same<LongDouble, long double>::value, H>::type
-AbslHashValue(H hash_state, LongDouble value) {
-  const int category = std::fpclassify(value);
-  switch (category) {
-    case FP_INFINITE:
-      // Add the sign bit to differentiate between +Inf and -Inf
-      hash_state = H::combine(std::move(hash_state), std::signbit(value));
-      break;
-
-    case FP_NAN:
-    case FP_ZERO:
-    default:
-      // Category is enough for these.
-      break;
-
-    case FP_NORMAL:
-    case FP_SUBNORMAL:
-      // We can't convert `value` directly to double because this would have
-      // undefined behavior if the value is out of range.
-      // std::frexp gives us a value in the range (-1, -.5] or [.5, 1) that is
-      // guaranteed to be in range for `double`. The truncation is
-      // implementation defined, but that works as long as it is deterministic.
-      int exp;
-      auto mantissa = static_cast<double>(std::frexp(value, &exp));
-      hash_state = H::combine(std::move(hash_state), mantissa, exp);
-  }
-
-  return H::combine(std::move(hash_state), category);
-}
-
-// AbslHashValue() for hashing pointers
-template <typename H, typename T>
-H AbslHashValue(H hash_state, T* ptr) {
-  auto v = reinterpret_cast<uintptr_t>(ptr);
-  // Due to alignment, pointers tend to have low bits as zero, and the next few
-  // bits follow a pattern since they are also multiples of some base value.
-  // Mixing the pointer twice helps prevent stuck low bits for certain alignment
-  // values.
-  return H::combine(std::move(hash_state), v, v);
-}
-
-// AbslHashValue() for hashing nullptr_t
-template <typename H>
-H AbslHashValue(H hash_state, std::nullptr_t) {
-  return H::combine(std::move(hash_state), static_cast<void*>(nullptr));
-}
-
-// -----------------------------------------------------------------------------
-// AbslHashValue for Composite Types
-// -----------------------------------------------------------------------------
-
-// is_hashable()
-//
-// Trait class which returns true if T is hashable by the absl::Hash framework.
-// Used for the AbslHashValue implementations for composite types below.
-template <typename T>
-struct is_hashable;
-
-// AbslHashValue() for hashing pairs
-template <typename H, typename T1, typename T2>
-typename std::enable_if<is_hashable<T1>::value && is_hashable<T2>::value,
-                        H>::type
-AbslHashValue(H hash_state, const std::pair<T1, T2>& p) {
-  return H::combine(std::move(hash_state), p.first, p.second);
-}
-
-// hash_tuple()
-//
-// Helper function for hashing a tuple. The third argument should
-// be an index_sequence running from 0 to tuple_size<Tuple> - 1.
-template <typename H, typename Tuple, size_t... Is>
-H hash_tuple(H hash_state, const Tuple& t, absl::index_sequence<Is...>) {
-  return H::combine(std::move(hash_state), std::get<Is>(t)...);
-}
-
-// AbslHashValue for hashing tuples
-template <typename H, typename... Ts>
-#if defined(_MSC_VER)
-// This SFINAE gets MSVC confused under some conditions. Let's just disable it
-// for now.
-H
-#else  // _MSC_VER
-typename std::enable_if<absl::conjunction<is_hashable<Ts>...>::value, H>::type
-#endif  // _MSC_VER
-AbslHashValue(H hash_state, const std::tuple<Ts...>& t) {
-  return hash_internal::hash_tuple(std::move(hash_state), t,
-                                   absl::make_index_sequence<sizeof...(Ts)>());
-}
-
-// -----------------------------------------------------------------------------
-// AbslHashValue for Pointers
-// -----------------------------------------------------------------------------
-
-// AbslHashValue for hashing unique_ptr
-template <typename H, typename T, typename D>
-H AbslHashValue(H hash_state, const std::unique_ptr<T, D>& ptr) {
-  return H::combine(std::move(hash_state), ptr.get());
-}
-
-// AbslHashValue for hashing shared_ptr
-template <typename H, typename T>
-H AbslHashValue(H hash_state, const std::shared_ptr<T>& ptr) {
-  return H::combine(std::move(hash_state), ptr.get());
-}
-
-// -----------------------------------------------------------------------------
-// AbslHashValue for String-Like Types
-// -----------------------------------------------------------------------------
-
-// AbslHashValue for hashing strings
-//
-// All the string-like types supported here provide the same hash expansion for
-// the same character sequence. These types are:
-//
-//  - `absl::Cord`
-//  - `std::string` (and std::basic_string<char, std::char_traits<char>, A> for
-//      any allocator A)
-//  - `absl::string_view` and `std::string_view`
-//
-// For simplicity, we currently support only `char` strings. This support may
-// be broadened, if necessary, but with some caution - this overload would
-// misbehave in cases where the traits' `eq()` member isn't equivalent to `==`
-// on the underlying character type.
-template <typename H>
-H AbslHashValue(H hash_state, absl::string_view str) {
-  return H::combine(
-      H::combine_contiguous(std::move(hash_state), str.data(), str.size()),
-      str.size());
-}
-
-// Support std::wstring, std::u16string and std::u32string.
-template <typename Char, typename Alloc, typename H,
-          typename = absl::enable_if_t<std::is_same<Char, wchar_t>::value ||
-                                       std::is_same<Char, char16_t>::value ||
-                                       std::is_same<Char, char32_t>::value>>
-H AbslHashValue(
-    H hash_state,
-    const std::basic_string<Char, std::char_traits<Char>, Alloc>& str) {
-  return H::combine(
-      H::combine_contiguous(std::move(hash_state), str.data(), str.size()),
-      str.size());
-}
-
-// -----------------------------------------------------------------------------
-// AbslHashValue for Sequence Containers
-// -----------------------------------------------------------------------------
-
-// AbslHashValue for hashing std::array
-template <typename H, typename T, size_t N>
-typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
-    H hash_state, const std::array<T, N>& array) {
-  return H::combine_contiguous(std::move(hash_state), array.data(),
-                               array.size());
-}
-
-// AbslHashValue for hashing std::deque
-template <typename H, typename T, typename Allocator>
-typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
-    H hash_state, const std::deque<T, Allocator>& deque) {
-  // TODO(gromer): investigate a more efficient implementation taking
-  // advantage of the chunk structure.
-  for (const auto& t : deque) {
-    hash_state = H::combine(std::move(hash_state), t);
-  }
-  return H::combine(std::move(hash_state), deque.size());
-}
-
-// AbslHashValue for hashing std::forward_list
-template <typename H, typename T, typename Allocator>
-typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
-    H hash_state, const std::forward_list<T, Allocator>& list) {
-  size_t size = 0;
-  for (const T& t : list) {
-    hash_state = H::combine(std::move(hash_state), t);
-    ++size;
-  }
-  return H::combine(std::move(hash_state), size);
-}
-
-// AbslHashValue for hashing std::list
-template <typename H, typename T, typename Allocator>
-typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
-    H hash_state, const std::list<T, Allocator>& list) {
-  for (const auto& t : list) {
-    hash_state = H::combine(std::move(hash_state), t);
-  }
-  return H::combine(std::move(hash_state), list.size());
-}
-
-// AbslHashValue for hashing std::vector
-//
-// Do not use this for vector<bool>. It does not have a .data(), and a fallback
-// for std::hash<> is most likely faster.
-template <typename H, typename T, typename Allocator>
-typename std::enable_if<is_hashable<T>::value && !std::is_same<T, bool>::value,
-                        H>::type
-AbslHashValue(H hash_state, const std::vector<T, Allocator>& vector) {
-  return H::combine(H::combine_contiguous(std::move(hash_state), vector.data(),
-                                          vector.size()),
-                    vector.size());
-}
-
-// -----------------------------------------------------------------------------
-// AbslHashValue for Ordered Associative Containers
-// -----------------------------------------------------------------------------
-
-// AbslHashValue for hashing std::map
-template <typename H, typename Key, typename T, typename Compare,
-          typename Allocator>
-typename std::enable_if<is_hashable<Key>::value && is_hashable<T>::value,
-                        H>::type
-AbslHashValue(H hash_state, const std::map<Key, T, Compare, Allocator>& map) {
-  for (const auto& t : map) {
-    hash_state = H::combine(std::move(hash_state), t);
-  }
-  return H::combine(std::move(hash_state), map.size());
-}
-
-// AbslHashValue for hashing std::multimap
-template <typename H, typename Key, typename T, typename Compare,
-          typename Allocator>
-typename std::enable_if<is_hashable<Key>::value && is_hashable<T>::value,
-                        H>::type
-AbslHashValue(H hash_state,
-              const std::multimap<Key, T, Compare, Allocator>& map) {
-  for (const auto& t : map) {
-    hash_state = H::combine(std::move(hash_state), t);
-  }
-  return H::combine(std::move(hash_state), map.size());
-}
-
-// AbslHashValue for hashing std::set
-template <typename H, typename Key, typename Compare, typename Allocator>
-typename std::enable_if<is_hashable<Key>::value, H>::type AbslHashValue(
-    H hash_state, const std::set<Key, Compare, Allocator>& set) {
-  for (const auto& t : set) {
-    hash_state = H::combine(std::move(hash_state), t);
-  }
-  return H::combine(std::move(hash_state), set.size());
-}
-
-// AbslHashValue for hashing std::multiset
-template <typename H, typename Key, typename Compare, typename Allocator>
-typename std::enable_if<is_hashable<Key>::value, H>::type AbslHashValue(
-    H hash_state, const std::multiset<Key, Compare, Allocator>& set) {
-  for (const auto& t : set) {
-    hash_state = H::combine(std::move(hash_state), t);
-  }
-  return H::combine(std::move(hash_state), set.size());
-}
-
-// -----------------------------------------------------------------------------
-// AbslHashValue for Wrapper Types
-// -----------------------------------------------------------------------------
-
-// AbslHashValue for hashing std::reference_wrapper
-template <typename H, typename T>
-typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
-    H hash_state, std::reference_wrapper<T> opt) {
-  return H::combine(std::move(hash_state), opt.get());
-}
-
-// AbslHashValue for hashing absl::optional
-template <typename H, typename T>
-typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue(
-    H hash_state, const absl::optional<T>& opt) {
-  if (opt) hash_state = H::combine(std::move(hash_state), *opt);
-  return H::combine(std::move(hash_state), opt.has_value());
-}
-
-// VariantVisitor
-template <typename H>
-struct VariantVisitor {
-  H&& hash_state;
-  template <typename T>
-  H operator()(const T& t) const {
-    return H::combine(std::move(hash_state), t);
-  }
-};
-
-// AbslHashValue for hashing absl::variant
-template <typename H, typename... T>
-typename std::enable_if<conjunction<is_hashable<T>...>::value, H>::type
-AbslHashValue(H hash_state, const absl::variant<T...>& v) {
-  if (!v.valueless_by_exception()) {
-    hash_state = absl::visit(VariantVisitor<H>{std::move(hash_state)}, v);
-  }
-  return H::combine(std::move(hash_state), v.index());
-}
-
-// -----------------------------------------------------------------------------
-// AbslHashValue for Other Types
-// -----------------------------------------------------------------------------
-
-// AbslHashValue for hashing std::bitset is not defined, for the same reason as
-// for vector<bool> (see std::vector above): It does not expose the raw bytes,
-// and a fallback to std::hash<> is most likely faster.
-
-// -----------------------------------------------------------------------------
-
-// hash_range_or_bytes()
-//
-// Mixes all values in the range [data, data+size) into the hash state.
-// This overload accepts only uniquely-represented types, and hashes them by
-// hashing the entire range of bytes.
-template <typename H, typename T>
-typename std::enable_if<is_uniquely_represented<T>::value, H>::type
-hash_range_or_bytes(H hash_state, const T* data, size_t size) {
-  const auto* bytes = reinterpret_cast<const unsigned char*>(data);
-  return H::combine_contiguous(std::move(hash_state), bytes, sizeof(T) * size);
-}
-
-// hash_range_or_bytes()
-template <typename H, typename T>
-typename std::enable_if<!is_uniquely_represented<T>::value, H>::type
-hash_range_or_bytes(H hash_state, const T* data, size_t size) {
-  for (const auto end = data + size; data < end; ++data) {
-    hash_state = H::combine(std::move(hash_state), *data);
-  }
-  return hash_state;
-}
-
-#if defined(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE) && \
-    ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-#define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 1
-#else
-#define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 0
-#endif
-
-// HashSelect
-//
-// Type trait to select the appropriate hash implementation to use.
-// HashSelect::type<T> will give the proper hash implementation, to be invoked
-// as:
-//   HashSelect::type<T>::Invoke(state, value)
-// Also, HashSelect::type<T>::value is a boolean equal to `true` if there is a
-// valid `Invoke` function. Types that are not hashable will have a ::value of
-// `false`.
-struct HashSelect {
- private:
-  struct State : HashStateBase<State> {
-    static State combine_contiguous(State hash_state, const unsigned char*,
-                                    size_t);
-    using State::HashStateBase::combine_contiguous;
-  };
-
-  struct UniquelyRepresentedProbe {
-    template <typename H, typename T>
-    static auto Invoke(H state, const T& value)
-        -> absl::enable_if_t<is_uniquely_represented<T>::value, H> {
-      return hash_internal::hash_bytes(std::move(state), value);
-    }
-  };
-
-  struct HashValueProbe {
-    template <typename H, typename T>
-    static auto Invoke(H state, const T& value) -> absl::enable_if_t<
-        std::is_same<H,
-                     decltype(AbslHashValue(std::move(state), value))>::value,
-        H> {
-      return AbslHashValue(std::move(state), value);
-    }
-  };
-
-  struct LegacyHashProbe {
-#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
-    template <typename H, typename T>
-    static auto Invoke(H state, const T& value) -> absl::enable_if_t<
-        std::is_convertible<
-            decltype(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash<T>()(value)),
-            size_t>::value,
-        H> {
-      return hash_internal::hash_bytes(
-          std::move(state),
-          ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash<T>{}(value));
-    }
-#endif  // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
-  };
-
-  struct StdHashProbe {
-    template <typename H, typename T>
-    static auto Invoke(H state, const T& value)
-        -> absl::enable_if_t<type_traits_internal::IsHashable<T>::value, H> {
-      return hash_internal::hash_bytes(std::move(state), std::hash<T>{}(value));
-    }
-  };
-
-  template <typename Hash, typename T>
-  struct Probe : Hash {
-   private:
-    template <typename H, typename = decltype(H::Invoke(
-                              std::declval<State>(), std::declval<const T&>()))>
-    static std::true_type Test(int);
-    template <typename U>
-    static std::false_type Test(char);
-
-   public:
-    static constexpr bool value = decltype(Test<Hash>(0))::value;
-  };
-
- public:
-  // Probe each implementation in order.
-  // disjunction provides short circuiting wrt instantiation.
-  template <typename T>
-  using Apply = absl::disjunction<         //
-      Probe<UniquelyRepresentedProbe, T>,  //
-      Probe<HashValueProbe, T>,            //
-      Probe<LegacyHashProbe, T>,           //
-      Probe<StdHashProbe, T>,              //
-      std::false_type>;
-};
-
-template <typename T>
-struct is_hashable
-    : std::integral_constant<bool, HashSelect::template Apply<T>::value> {};
-
-// CityHashState
-class ABSL_DLL CityHashState
-    : public HashStateBase<CityHashState> {
-  // absl::uint128 is not an alias or a thin wrapper around the intrinsic.
-  // We use the intrinsic when available to improve performance.
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  using uint128 = __uint128_t;
-#else   // ABSL_HAVE_INTRINSIC_INT128
-  using uint128 = absl::uint128;
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-  static constexpr uint64_t kMul =
-      sizeof(size_t) == 4 ? uint64_t{0xcc9e2d51}
-                          : uint64_t{0x9ddfea08eb382d69};
-
-  template <typename T>
-  using IntegralFastPath =
-      conjunction<std::is_integral<T>, is_uniquely_represented<T>>;
-
- public:
-  // Move only
-  CityHashState(CityHashState&&) = default;
-  CityHashState& operator=(CityHashState&&) = default;
-
-  // CityHashState::combine_contiguous()
-  //
-  // Fundamental base case for hash recursion: mixes the given range of bytes
-  // into the hash state.
-  static CityHashState combine_contiguous(CityHashState hash_state,
-                                          const unsigned char* first,
-                                          size_t size) {
-    return CityHashState(
-        CombineContiguousImpl(hash_state.state_, first, size,
-                              std::integral_constant<int, sizeof(size_t)>{}));
-  }
-  using CityHashState::HashStateBase::combine_contiguous;
-
-  // CityHashState::hash()
-  //
-  // For performance reasons in non-opt mode, we specialize this for
-  // integral types.
-  // Otherwise we would be instantiating and calling dozens of functions for
-  // something that is just one multiplication and a couple xor's.
-  // The result should be the same as running the whole algorithm, but faster.
-  template <typename T, absl::enable_if_t<IntegralFastPath<T>::value, int> = 0>
-  static size_t hash(T value) {
-    return static_cast<size_t>(Mix(Seed(), static_cast<uint64_t>(value)));
-  }
-
-  // Overload of CityHashState::hash()
-  template <typename T, absl::enable_if_t<!IntegralFastPath<T>::value, int> = 0>
-  static size_t hash(const T& value) {
-    return static_cast<size_t>(combine(CityHashState{}, value).state_);
-  }
-
- private:
-  // Invoked only once for a given argument; that plus the fact that this is
-  // move-only ensures that there is only one non-moved-from object.
-  CityHashState() : state_(Seed()) {}
-
-  // Workaround for MSVC bug.
-  // We make the type copyable to fix the calling convention, even though we
-  // never actually copy it. Keep it private to not affect the public API of the
-  // type.
-  CityHashState(const CityHashState&) = default;
-
-  explicit CityHashState(uint64_t state) : state_(state) {}
-
-  // Implementation of the base case for combine_contiguous where we actually
-  // mix the bytes into the state.
-  // Dispatch to different implementations of the combine_contiguous depending
-  // on the value of `sizeof(size_t)`.
-  static uint64_t CombineContiguousImpl(uint64_t state,
-                                        const unsigned char* first, size_t len,
-                                        std::integral_constant<int, 4>
-                                        /* sizeof_size_t */);
-  static uint64_t CombineContiguousImpl(uint64_t state,
-                                        const unsigned char* first, size_t len,
-                                        std::integral_constant<int, 8>
-                                        /* sizeof_size_t*/);
-
-  // Slow dispatch path for calls to CombineContiguousImpl with a size argument
-  // larger than PiecewiseChunkSize().  Has the same effect as calling
-  // CombineContiguousImpl() repeatedly with the chunk stride size.
-  static uint64_t CombineLargeContiguousImpl32(uint64_t state,
-                                               const unsigned char* first,
-                                               size_t len);
-  static uint64_t CombineLargeContiguousImpl64(uint64_t state,
-                                               const unsigned char* first,
-                                               size_t len);
-
-  // Reads 9 to 16 bytes from p.
-  // The first 8 bytes are in .first, the rest (zero padded) bytes are in
-  // .second.
-  static std::pair<uint64_t, uint64_t> Read9To16(const unsigned char* p,
-                                                 size_t len) {
-    uint64_t high = little_endian::Load64(p + len - 8);
-    return {little_endian::Load64(p), high >> (128 - len * 8)};
-  }
-
-  // Reads 4 to 8 bytes from p. Zero pads to fill uint64_t.
-  static uint64_t Read4To8(const unsigned char* p, size_t len) {
-    return (static_cast<uint64_t>(little_endian::Load32(p + len - 4))
-            << (len - 4) * 8) |
-           little_endian::Load32(p);
-  }
-
-  // Reads 1 to 3 bytes from p. Zero pads to fill uint32_t.
-  static uint32_t Read1To3(const unsigned char* p, size_t len) {
-    return static_cast<uint32_t>((p[0]) |                         //
-                                 (p[len / 2] << (len / 2 * 8)) |  //
-                                 (p[len - 1] << ((len - 1) * 8)));
-  }
-
-  ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Mix(uint64_t state, uint64_t v) {
-    using MultType =
-        absl::conditional_t<sizeof(size_t) == 4, uint64_t, uint128>;
-    // We do the addition in 64-bit space to make sure the 128-bit
-    // multiplication is fast. If we were to do it as MultType the compiler has
-    // to assume that the high word is non-zero and needs to perform 2
-    // multiplications instead of one.
-    MultType m = state + v;
-    m *= kMul;
-    return static_cast<uint64_t>(m ^ (m >> (sizeof(m) * 8 / 2)));
-  }
-
-  // Seed()
-  //
-  // A non-deterministic seed.
-  //
-  // The current purpose of this seed is to generate non-deterministic results
-  // and prevent having users depend on the particular hash values.
-  // It is not meant as a security feature right now, but it leaves the door
-  // open to upgrade it to a true per-process random seed. A true random seed
-  // costs more and we don't need to pay for that right now.
-  //
-  // On platforms with ASLR, we take advantage of it to make a per-process
-  // random value.
-  // See https://en.wikipedia.org/wiki/Address_space_layout_randomization
-  //
-  // On other platforms this is still going to be non-deterministic but most
-  // probably per-build and not per-process.
-  ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Seed() {
-#if (!defined(__clang__) || __clang_major__ > 11) && \
-    !defined(__apple_build_version__)
-    return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(&kSeed));
-#else
-    // Workaround the absence of
-    // https://github.com/llvm/llvm-project/commit/bc15bf66dcca76cc06fe71fca35b74dc4d521021.
-    return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(kSeed));
-#endif
-  }
-  static const void* const kSeed;
-
-  uint64_t state_;
-};
-
-// CityHashState::CombineContiguousImpl()
-inline uint64_t CityHashState::CombineContiguousImpl(
-    uint64_t state, const unsigned char* first, size_t len,
-    std::integral_constant<int, 4> /* sizeof_size_t */) {
-  // For large values we use CityHash, for small ones we just use a
-  // multiplicative hash.
-  uint64_t v;
-  if (len > 8) {
-    if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) {
-      return CombineLargeContiguousImpl32(state, first, len);
-    }
-    v = absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), len);
-  } else if (len >= 4) {
-    v = Read4To8(first, len);
-  } else if (len > 0) {
-    v = Read1To3(first, len);
-  } else {
-    // Empty ranges have no effect.
-    return state;
-  }
-  return Mix(state, v);
-}
-
-// Overload of CityHashState::CombineContiguousImpl()
-inline uint64_t CityHashState::CombineContiguousImpl(
-    uint64_t state, const unsigned char* first, size_t len,
-    std::integral_constant<int, 8> /* sizeof_size_t */) {
-  // For large values we use CityHash, for small ones we just use a
-  // multiplicative hash.
-  uint64_t v;
-  if (len > 16) {
-    if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) {
-      return CombineLargeContiguousImpl64(state, first, len);
-    }
-    v = absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), len);
-  } else if (len > 8) {
-    auto p = Read9To16(first, len);
-    state = Mix(state, p.first);
-    v = p.second;
-  } else if (len >= 4) {
-    v = Read4To8(first, len);
-  } else if (len > 0) {
-    v = Read1To3(first, len);
-  } else {
-    // Empty ranges have no effect.
-    return state;
-  }
-  return Mix(state, v);
-}
-
-struct AggregateBarrier {};
-
-// HashImpl
-
-// Add a private base class to make sure this type is not an aggregate.
-// Aggregates can be aggregate initialized even if the default constructor is
-// deleted.
-struct PoisonedHash : private AggregateBarrier {
-  PoisonedHash() = delete;
-  PoisonedHash(const PoisonedHash&) = delete;
-  PoisonedHash& operator=(const PoisonedHash&) = delete;
-};
-
-template <typename T>
-struct HashImpl {
-  size_t operator()(const T& value) const { return CityHashState::hash(value); }
-};
-
-template <typename T>
-struct Hash
-    : absl::conditional_t<is_hashable<T>::value, HashImpl<T>, PoisonedHash> {};
-
-template <typename H>
-template <typename T, typename... Ts>
-H HashStateBase<H>::combine(H state, const T& value, const Ts&... values) {
-  return H::combine(hash_internal::HashSelect::template Apply<T>::Invoke(
-                        std::move(state), value),
-                    values...);
-}
-
-// HashStateBase::combine_contiguous()
-template <typename H>
-template <typename T>
-H HashStateBase<H>::combine_contiguous(H state, const T* data, size_t size) {
-  return hash_internal::hash_range_or_bytes(std::move(state), data, size);
-}
-
-// HashStateBase::PiecewiseCombiner::add_buffer()
-template <typename H>
-H PiecewiseCombiner::add_buffer(H state, const unsigned char* data,
-                                size_t size) {
-  if (position_ + size < PiecewiseChunkSize()) {
-    // This partial chunk does not fill our existing buffer
-    memcpy(buf_ + position_, data, size);
-    position_ += size;
-    return state;
-  }
-
-  // If the buffer is partially filled we need to complete the buffer
-  // and hash it.
-  if (position_ != 0) {
-    const size_t bytes_needed = PiecewiseChunkSize() - position_;
-    memcpy(buf_ + position_, data, bytes_needed);
-    state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize());
-    data += bytes_needed;
-    size -= bytes_needed;
-  }
-
-  // Hash whatever chunks we can without copying
-  while (size >= PiecewiseChunkSize()) {
-    state = H::combine_contiguous(std::move(state), data, PiecewiseChunkSize());
-    data += PiecewiseChunkSize();
-    size -= PiecewiseChunkSize();
-  }
-  // Fill the buffer with the remainder
-  memcpy(buf_, data, size);
-  position_ = size;
-  return state;
-}
-
-// HashStateBase::PiecewiseCombiner::finalize()
-template <typename H>
-H PiecewiseCombiner::finalize(H state) {
-  // Hash the remainder left in the buffer, which may be empty
-  return H::combine_contiguous(std::move(state), buf_, position_);
-}
-
-}  // namespace hash_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HASH_INTERNAL_HASH_H_
diff --git a/third_party/abseil_cpp/absl/hash/internal/print_hash_of.cc b/third_party/abseil_cpp/absl/hash/internal/print_hash_of.cc
deleted file mode 100644
index c392125a69..0000000000
--- a/third_party/abseil_cpp/absl/hash/internal/print_hash_of.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cstdlib>
-
-#include "absl/hash/hash.h"
-
-// Prints the hash of argv[1].
-int main(int argc, char** argv) {
-  if (argc < 2) return 1;
-  printf("%zu\n", absl::Hash<int>{}(std::atoi(argv[1])));  // NOLINT
-}
diff --git a/third_party/abseil_cpp/absl/hash/internal/spy_hash_state.h b/third_party/abseil_cpp/absl/hash/internal/spy_hash_state.h
deleted file mode 100644
index c083120811..0000000000
--- a/third_party/abseil_cpp/absl/hash/internal/spy_hash_state.h
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
-#define ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
-
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include "absl/hash/hash.h"
-#include "absl/strings/match.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_join.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace hash_internal {
-
-// SpyHashState is an implementation of the HashState API that simply
-// accumulates all input bytes in an internal buffer. This makes it useful
-// for testing AbslHashValue overloads (so long as they are templated on the
-// HashState parameter), since it can report the exact hash representation
-// that the AbslHashValue overload produces.
-//
-// Sample usage:
-// EXPECT_EQ(SpyHashState::combine(SpyHashState(), foo),
-//           SpyHashState::combine(SpyHashState(), bar));
-template <typename T>
-class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> {
- public:
-  SpyHashStateImpl() : error_(std::make_shared<absl::optional<std::string>>()) {
-    static_assert(std::is_void<T>::value, "");
-  }
-
-  // Move-only
-  SpyHashStateImpl(const SpyHashStateImpl&) = delete;
-  SpyHashStateImpl& operator=(const SpyHashStateImpl&) = delete;
-
-  SpyHashStateImpl(SpyHashStateImpl&& other) noexcept {
-    *this = std::move(other);
-  }
-
-  SpyHashStateImpl& operator=(SpyHashStateImpl&& other) noexcept {
-    hash_representation_ = std::move(other.hash_representation_);
-    error_ = other.error_;
-    moved_from_ = other.moved_from_;
-    other.moved_from_ = true;
-    return *this;
-  }
-
-  template <typename U>
-  SpyHashStateImpl(SpyHashStateImpl<U>&& other) {  // NOLINT
-    hash_representation_ = std::move(other.hash_representation_);
-    error_ = other.error_;
-    moved_from_ = other.moved_from_;
-    other.moved_from_ = true;
-  }
-
-  template <typename A, typename... Args>
-  static SpyHashStateImpl combine(SpyHashStateImpl s, const A& a,
-                                  const Args&... args) {
-    // Pass an instance of SpyHashStateImpl<A> when trying to combine `A`. This
-    // allows us to test that the user only uses this instance for combine calls
-    // and does not call AbslHashValue directly.
-    // See AbslHashValue implementation at the bottom.
-    s = SpyHashStateImpl<A>::HashStateBase::combine(std::move(s), a);
-    return SpyHashStateImpl::combine(std::move(s), args...);
-  }
-  static SpyHashStateImpl combine(SpyHashStateImpl s) {
-    if (direct_absl_hash_value_error_) {
-      *s.error_ = "AbslHashValue should not be invoked directly.";
-    } else if (s.moved_from_) {
-      *s.error_ = "Used moved-from instance of the hash state object.";
-    }
-    return s;
-  }
-
-  static void SetDirectAbslHashValueError() {
-    direct_absl_hash_value_error_ = true;
-  }
-
-  // Two SpyHashStateImpl objects are equal if they hold equal hash
-  // representations.
-  friend bool operator==(const SpyHashStateImpl& lhs,
-                         const SpyHashStateImpl& rhs) {
-    return lhs.hash_representation_ == rhs.hash_representation_;
-  }
-
-  friend bool operator!=(const SpyHashStateImpl& lhs,
-                         const SpyHashStateImpl& rhs) {
-    return !(lhs == rhs);
-  }
-
-  enum class CompareResult {
-    kEqual,
-    kASuffixB,
-    kBSuffixA,
-    kUnequal,
-  };
-
-  static CompareResult Compare(const SpyHashStateImpl& a,
-                               const SpyHashStateImpl& b) {
-    const std::string a_flat = absl::StrJoin(a.hash_representation_, "");
-    const std::string b_flat = absl::StrJoin(b.hash_representation_, "");
-    if (a_flat == b_flat) return CompareResult::kEqual;
-    if (absl::EndsWith(a_flat, b_flat)) return CompareResult::kBSuffixA;
-    if (absl::EndsWith(b_flat, a_flat)) return CompareResult::kASuffixB;
-    return CompareResult::kUnequal;
-  }
-
-  // operator<< prints the hash representation as a hex and ASCII dump, to
-  // facilitate debugging.
-  friend std::ostream& operator<<(std::ostream& out,
-                                  const SpyHashStateImpl& hash_state) {
-    out << "[\n";
-    for (auto& s : hash_state.hash_representation_) {
-      size_t offset = 0;
-      for (char c : s) {
-        if (offset % 16 == 0) {
-          out << absl::StreamFormat("\n0x%04x: ", offset);
-        }
-        if (offset % 2 == 0) {
-          out << " ";
-        }
-        out << absl::StreamFormat("%02x", c);
-        ++offset;
-      }
-      out << "\n";
-    }
-    return out << "]";
-  }
-
-  // The base case of the combine recursion, which writes raw bytes into the
-  // internal buffer.
-  static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state,
-                                             const unsigned char* begin,
-                                             size_t size) {
-    const size_t large_chunk_stride = PiecewiseChunkSize();
-    if (size > large_chunk_stride) {
-      // Combining a large contiguous buffer must have the same effect as
-      // doing it piecewise by the stride length, followed by the (possibly
-      // empty) remainder.
-      while (size >= large_chunk_stride) {
-        hash_state = SpyHashStateImpl::combine_contiguous(
-            std::move(hash_state), begin, large_chunk_stride);
-        begin += large_chunk_stride;
-        size -= large_chunk_stride;
-      }
-    }
-
-    hash_state.hash_representation_.emplace_back(
-        reinterpret_cast<const char*>(begin), size);
-    return hash_state;
-  }
-
-  using SpyHashStateImpl::HashStateBase::combine_contiguous;
-
-  absl::optional<std::string> error() const {
-    if (moved_from_) {
-      return "Returned a moved-from instance of the hash state object.";
-    }
-    return *error_;
-  }
-
- private:
-  template <typename U>
-  friend class SpyHashStateImpl;
-
-  // This is true if SpyHashStateImpl<T> has been passed to a call of
-  // AbslHashValue with the wrong type. This detects that the user called
-  // AbslHashValue directly (because the hash state type does not match).
-  static bool direct_absl_hash_value_error_;
-
-  std::vector<std::string> hash_representation_;
-  // This is a shared_ptr because we want all instances of the particular
-  // SpyHashState run to share the field. This way we can set the error for
-  // use-after-move and all the copies will see it.
-  std::shared_ptr<absl::optional<std::string>> error_;
-  bool moved_from_ = false;
-};
-
-template <typename T>
-bool SpyHashStateImpl<T>::direct_absl_hash_value_error_;
-
-template <bool& B>
-struct OdrUse {
-  constexpr OdrUse() {}
-  bool& b = B;
-};
-
-template <void (*)()>
-struct RunOnStartup {
-  static bool run;
-  static constexpr OdrUse<run> kOdrUse{};
-};
-
-template <void (*f)()>
-bool RunOnStartup<f>::run = (f(), true);
-
-template <
-    typename T, typename U,
-    // Only trigger for when (T != U),
-    typename = absl::enable_if_t<!std::is_same<T, U>::value>,
-    // This statement works in two ways:
-    //  - First, it instantiates RunOnStartup and forces the initialization of
-    //    `run`, which set the global variable.
-    //  - Second, it triggers a SFINAE error disabling the overload to prevent
-    //    compile time errors. If we didn't disable the overload we would get
-    //    ambiguous overload errors, which we don't want.
-    int = RunOnStartup<SpyHashStateImpl<T>::SetDirectAbslHashValueError>::run>
-void AbslHashValue(SpyHashStateImpl<T>, const U&);
-
-using SpyHashState = SpyHashStateImpl<void>;
-
-}  // namespace hash_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
diff --git a/third_party/abseil_cpp/absl/memory/BUILD.bazel b/third_party/abseil_cpp/absl/memory/BUILD.bazel
deleted file mode 100644
index d2824a05ad..0000000000
--- a/third_party/abseil_cpp/absl/memory/BUILD.bazel
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "memory",
-    hdrs = ["memory.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:core_headers",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_test(
-    name = "memory_test",
-    srcs = ["memory_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":memory",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "memory_exception_safety_test",
-    srcs = [
-        "memory_exception_safety_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":memory",
-        "//absl/base:config",
-        "//absl/base:exception_safety_testing",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/memory/CMakeLists.txt b/third_party/abseil_cpp/absl/memory/CMakeLists.txt
deleted file mode 100644
index 78fb7e1b31..0000000000
--- a/third_party/abseil_cpp/absl/memory/CMakeLists.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    memory
-  HDRS
-    "memory.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::core_headers
-    absl::meta
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    memory_test
-  SRCS
-    "memory_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::memory
-    absl::core_headers
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    memory_exception_safety_test
-  SRCS
-    "memory_exception_safety_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::memory
-    absl::config
-    absl::exception_safety_testing
-    gmock_main
-)
diff --git a/third_party/abseil_cpp/absl/memory/memory.h b/third_party/abseil_cpp/absl/memory/memory.h
deleted file mode 100644
index 2b5ff623d4..0000000000
--- a/third_party/abseil_cpp/absl/memory/memory.h
+++ /dev/null
@@ -1,699 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: memory.h
-// -----------------------------------------------------------------------------
-//
-// This header file contains utility functions for managing the creation and
-// conversion of smart pointers. This file is an extension to the C++
-// standard <memory> library header file.
-
-#ifndef ABSL_MEMORY_MEMORY_H_
-#define ABSL_MEMORY_MEMORY_H_
-
-#include <cstddef>
-#include <limits>
-#include <memory>
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/macros.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// Function Template: WrapUnique()
-// -----------------------------------------------------------------------------
-//
-// Adopts ownership from a raw pointer and transfers it to the returned
-// `std::unique_ptr`, whose type is deduced. Because of this deduction, *do not*
-// specify the template type `T` when calling `WrapUnique`.
-//
-// Example:
-//   X* NewX(int, int);
-//   auto x = WrapUnique(NewX(1, 2));  // 'x' is std::unique_ptr<X>.
-//
-// Do not call WrapUnique with an explicit type, as in
-// `WrapUnique<X>(NewX(1, 2))`.  The purpose of WrapUnique is to automatically
-// deduce the pointer type. If you wish to make the type explicit, just use
-// `std::unique_ptr` directly.
-//
-//   auto x = std::unique_ptr<X>(NewX(1, 2));
-//                  - or -
-//   std::unique_ptr<X> x(NewX(1, 2));
-//
-// While `absl::WrapUnique` is useful for capturing the output of a raw
-// pointer factory, prefer 'absl::make_unique<T>(args...)' over
-// 'absl::WrapUnique(new T(args...))'.
-//
-//   auto x = WrapUnique(new X(1, 2));  // works, but nonideal.
-//   auto x = make_unique<X>(1, 2);     // safer, standard, avoids raw 'new'.
-//
-// Note that `absl::WrapUnique(p)` is valid only if `delete p` is a valid
-// expression. In particular, `absl::WrapUnique()` cannot wrap pointers to
-// arrays, functions or void, and it must not be used to capture pointers
-// obtained from array-new expressions (even though that would compile!).
-template <typename T>
-std::unique_ptr<T> WrapUnique(T* ptr) {
-  static_assert(!std::is_array<T>::value, "array types are unsupported");
-  static_assert(std::is_object<T>::value, "non-object types are unsupported");
-  return std::unique_ptr<T>(ptr);
-}
-
-namespace memory_internal {
-
-// Traits to select proper overload and return type for `absl::make_unique<>`.
-template <typename T>
-struct MakeUniqueResult {
-  using scalar = std::unique_ptr<T>;
-};
-template <typename T>
-struct MakeUniqueResult<T[]> {
-  using array = std::unique_ptr<T[]>;
-};
-template <typename T, size_t N>
-struct MakeUniqueResult<T[N]> {
-  using invalid = void;
-};
-
-}  // namespace memory_internal
-
-// gcc 4.8 has __cplusplus at 201301 but the libstdc++ shipped with it doesn't
-// define make_unique.  Other supported compilers either just define __cplusplus
-// as 201103 but have make_unique (msvc), or have make_unique whenever
-// __cplusplus > 201103 (clang).
-#if (__cplusplus > 201103L || defined(_MSC_VER)) && \
-    !(defined(__GLIBCXX__) && !defined(__cpp_lib_make_unique))
-using std::make_unique;
-#else
-// -----------------------------------------------------------------------------
-// Function Template: make_unique<T>()
-// -----------------------------------------------------------------------------
-//
-// Creates a `std::unique_ptr<>`, while avoiding issues creating temporaries
-// during the construction process. `absl::make_unique<>` also avoids redundant
-// type declarations, by avoiding the need to explicitly use the `new` operator.
-//
-// This implementation of `absl::make_unique<>` is designed for C++11 code and
-// will be replaced in C++14 by the equivalent `std::make_unique<>` abstraction.
-// `absl::make_unique<>` is designed to be 100% compatible with
-// `std::make_unique<>` so that the eventual migration will involve a simple
-// rename operation.
-//
-// For more background on why `std::unique_ptr<T>(new T(a,b))` is problematic,
-// see Herb Sutter's explanation on
-// (Exception-Safe Function Calls)[https://herbsutter.com/gotw/_102/].
-// (In general, reviewers should treat `new T(a,b)` with scrutiny.)
-//
-// Example usage:
-//
-//    auto p = make_unique<X>(args...);  // 'p'  is a std::unique_ptr<X>
-//    auto pa = make_unique<X[]>(5);     // 'pa' is a std::unique_ptr<X[]>
-//
-// Three overloads of `absl::make_unique` are required:
-//
-//   - For non-array T:
-//
-//       Allocates a T with `new T(std::forward<Args> args...)`,
-//       forwarding all `args` to T's constructor.
-//       Returns a `std::unique_ptr<T>` owning that object.
-//
-//   - For an array of unknown bounds T[]:
-//
-//       `absl::make_unique<>` will allocate an array T of type U[] with
-//       `new U[n]()` and return a `std::unique_ptr<U[]>` owning that array.
-//
-//       Note that 'U[n]()' is different from 'U[n]', and elements will be
-//       value-initialized. Note as well that `std::unique_ptr` will perform its
-//       own destruction of the array elements upon leaving scope, even though
-//       the array [] does not have a default destructor.
-//
-//       NOTE: an array of unknown bounds T[] may still be (and often will be)
-//       initialized to have a size, and will still use this overload. E.g:
-//
-//         auto my_array = absl::make_unique<int[]>(10);
-//
-//   - For an array of known bounds T[N]:
-//
-//       `absl::make_unique<>` is deleted (like with `std::make_unique<>`) as
-//       this overload is not useful.
-//
-//       NOTE: an array of known bounds T[N] is not considered a useful
-//       construction, and may cause undefined behavior in templates. E.g:
-//
-//         auto my_array = absl::make_unique<int[10]>();
-//
-//       In those cases, of course, you can still use the overload above and
-//       simply initialize it to its desired size:
-//
-//         auto my_array = absl::make_unique<int[]>(10);
-
-// `absl::make_unique` overload for non-array types.
-template <typename T, typename... Args>
-typename memory_internal::MakeUniqueResult<T>::scalar make_unique(
-    Args&&... args) {
-  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
-// `absl::make_unique` overload for an array T[] of unknown bounds.
-// The array allocation needs to use the `new T[size]` form and cannot take
-// element constructor arguments. The `std::unique_ptr` will manage destructing
-// these array elements.
-template <typename T>
-typename memory_internal::MakeUniqueResult<T>::array make_unique(size_t n) {
-  return std::unique_ptr<T>(new typename absl::remove_extent_t<T>[n]());
-}
-
-// `absl::make_unique` overload for an array T[N] of known bounds.
-// This construction will be rejected.
-template <typename T, typename... Args>
-typename memory_internal::MakeUniqueResult<T>::invalid make_unique(
-    Args&&... /* args */) = delete;
-#endif
-
-// -----------------------------------------------------------------------------
-// Function Template: RawPtr()
-// -----------------------------------------------------------------------------
-//
-// Extracts the raw pointer from a pointer-like value `ptr`. `absl::RawPtr` is
-// useful within templates that need to handle a complement of raw pointers,
-// `std::nullptr_t`, and smart pointers.
-template <typename T>
-auto RawPtr(T&& ptr) -> decltype(std::addressof(*ptr)) {
-  // ptr is a forwarding reference to support Ts with non-const operators.
-  return (ptr != nullptr) ? std::addressof(*ptr) : nullptr;
-}
-inline std::nullptr_t RawPtr(std::nullptr_t) { return nullptr; }
-
-// -----------------------------------------------------------------------------
-// Function Template: ShareUniquePtr()
-// -----------------------------------------------------------------------------
-//
-// Adopts a `std::unique_ptr` rvalue and returns a `std::shared_ptr` of deduced
-// type. Ownership (if any) of the held value is transferred to the returned
-// shared pointer.
-//
-// Example:
-//
-//     auto up = absl::make_unique<int>(10);
-//     auto sp = absl::ShareUniquePtr(std::move(up));  // shared_ptr<int>
-//     CHECK_EQ(*sp, 10);
-//     CHECK(up == nullptr);
-//
-// Note that this conversion is correct even when T is an array type, and more
-// generally it works for *any* deleter of the `unique_ptr` (single-object
-// deleter, array deleter, or any custom deleter), since the deleter is adopted
-// by the shared pointer as well. The deleter is copied (unless it is a
-// reference).
-//
-// Implements the resolution of [LWG 2415](http://wg21.link/lwg2415), by which a
-// null shared pointer does not attempt to call the deleter.
-template <typename T, typename D>
-std::shared_ptr<T> ShareUniquePtr(std::unique_ptr<T, D>&& ptr) {
-  return ptr ? std::shared_ptr<T>(std::move(ptr)) : std::shared_ptr<T>();
-}
-
-// -----------------------------------------------------------------------------
-// Function Template: WeakenPtr()
-// -----------------------------------------------------------------------------
-//
-// Creates a weak pointer associated with a given shared pointer. The returned
-// value is a `std::weak_ptr` of deduced type.
-//
-// Example:
-//
-//    auto sp = std::make_shared<int>(10);
-//    auto wp = absl::WeakenPtr(sp);
-//    CHECK_EQ(sp.get(), wp.lock().get());
-//    sp.reset();
-//    CHECK(wp.lock() == nullptr);
-//
-template <typename T>
-std::weak_ptr<T> WeakenPtr(const std::shared_ptr<T>& ptr) {
-  return std::weak_ptr<T>(ptr);
-}
-
-namespace memory_internal {
-
-// ExtractOr<E, O, D>::type evaluates to E<O> if possible. Otherwise, D.
-template <template <typename> class Extract, typename Obj, typename Default,
-          typename>
-struct ExtractOr {
-  using type = Default;
-};
-
-template <template <typename> class Extract, typename Obj, typename Default>
-struct ExtractOr<Extract, Obj, Default, void_t<Extract<Obj>>> {
-  using type = Extract<Obj>;
-};
-
-template <template <typename> class Extract, typename Obj, typename Default>
-using ExtractOrT = typename ExtractOr<Extract, Obj, Default, void>::type;
-
-// Extractors for the features of allocators.
-template <typename T>
-using GetPointer = typename T::pointer;
-
-template <typename T>
-using GetConstPointer = typename T::const_pointer;
-
-template <typename T>
-using GetVoidPointer = typename T::void_pointer;
-
-template <typename T>
-using GetConstVoidPointer = typename T::const_void_pointer;
-
-template <typename T>
-using GetDifferenceType = typename T::difference_type;
-
-template <typename T>
-using GetSizeType = typename T::size_type;
-
-template <typename T>
-using GetPropagateOnContainerCopyAssignment =
-    typename T::propagate_on_container_copy_assignment;
-
-template <typename T>
-using GetPropagateOnContainerMoveAssignment =
-    typename T::propagate_on_container_move_assignment;
-
-template <typename T>
-using GetPropagateOnContainerSwap = typename T::propagate_on_container_swap;
-
-template <typename T>
-using GetIsAlwaysEqual = typename T::is_always_equal;
-
-template <typename T>
-struct GetFirstArg;
-
-template <template <typename...> class Class, typename T, typename... Args>
-struct GetFirstArg<Class<T, Args...>> {
-  using type = T;
-};
-
-template <typename Ptr, typename = void>
-struct ElementType {
-  using type = typename GetFirstArg<Ptr>::type;
-};
-
-template <typename T>
-struct ElementType<T, void_t<typename T::element_type>> {
-  using type = typename T::element_type;
-};
-
-template <typename T, typename U>
-struct RebindFirstArg;
-
-template <template <typename...> class Class, typename T, typename... Args,
-          typename U>
-struct RebindFirstArg<Class<T, Args...>, U> {
-  using type = Class<U, Args...>;
-};
-
-template <typename T, typename U, typename = void>
-struct RebindPtr {
-  using type = typename RebindFirstArg<T, U>::type;
-};
-
-template <typename T, typename U>
-struct RebindPtr<T, U, void_t<typename T::template rebind<U>>> {
-  using type = typename T::template rebind<U>;
-};
-
-template <typename T, typename U>
-constexpr bool HasRebindAlloc(...) {
-  return false;
-}
-
-template <typename T, typename U>
-constexpr bool HasRebindAlloc(typename T::template rebind<U>::other*) {
-  return true;
-}
-
-template <typename T, typename U, bool = HasRebindAlloc<T, U>(nullptr)>
-struct RebindAlloc {
-  using type = typename RebindFirstArg<T, U>::type;
-};
-
-template <typename T, typename U>
-struct RebindAlloc<T, U, true> {
-  using type = typename T::template rebind<U>::other;
-};
-
-}  // namespace memory_internal
-
-// -----------------------------------------------------------------------------
-// Class Template: pointer_traits
-// -----------------------------------------------------------------------------
-//
-// An implementation of C++11's std::pointer_traits.
-//
-// Provided for portability on toolchains that have a working C++11 compiler,
-// but the standard library is lacking in C++11 support. For example, some
-// version of the Android NDK.
-//
-
-template <typename Ptr>
-struct pointer_traits {
-  using pointer = Ptr;
-
-  // element_type:
-  // Ptr::element_type if present. Otherwise T if Ptr is a template
-  // instantiation Template<T, Args...>
-  using element_type = typename memory_internal::ElementType<Ptr>::type;
-
-  // difference_type:
-  // Ptr::difference_type if present, otherwise std::ptrdiff_t
-  using difference_type =
-      memory_internal::ExtractOrT<memory_internal::GetDifferenceType, Ptr,
-                                  std::ptrdiff_t>;
-
-  // rebind:
-  // Ptr::rebind<U> if exists, otherwise Template<U, Args...> if Ptr is a
-  // template instantiation Template<T, Args...>
-  template <typename U>
-  using rebind = typename memory_internal::RebindPtr<Ptr, U>::type;
-
-  // pointer_to:
-  // Calls Ptr::pointer_to(r)
-  static pointer pointer_to(element_type& r) {  // NOLINT(runtime/references)
-    return Ptr::pointer_to(r);
-  }
-};
-
-// Specialization for T*.
-template <typename T>
-struct pointer_traits<T*> {
-  using pointer = T*;
-  using element_type = T;
-  using difference_type = std::ptrdiff_t;
-
-  template <typename U>
-  using rebind = U*;
-
-  // pointer_to:
-  // Calls std::addressof(r)
-  static pointer pointer_to(
-      element_type& r) noexcept {  // NOLINT(runtime/references)
-    return std::addressof(r);
-  }
-};
-
-// -----------------------------------------------------------------------------
-// Class Template: allocator_traits
-// -----------------------------------------------------------------------------
-//
-// A C++11 compatible implementation of C++17's std::allocator_traits.
-//
-#if __cplusplus >= 201703L
-using std::allocator_traits;
-#else  // __cplusplus >= 201703L
-template <typename Alloc>
-struct allocator_traits {
-  using allocator_type = Alloc;
-
-  // value_type:
-  // Alloc::value_type
-  using value_type = typename Alloc::value_type;
-
-  // pointer:
-  // Alloc::pointer if present, otherwise value_type*
-  using pointer = memory_internal::ExtractOrT<memory_internal::GetPointer,
-                                              Alloc, value_type*>;
-
-  // const_pointer:
-  // Alloc::const_pointer if present, otherwise
-  // absl::pointer_traits<pointer>::rebind<const value_type>
-  using const_pointer =
-      memory_internal::ExtractOrT<memory_internal::GetConstPointer, Alloc,
-                                  typename absl::pointer_traits<pointer>::
-                                      template rebind<const value_type>>;
-
-  // void_pointer:
-  // Alloc::void_pointer if present, otherwise
-  // absl::pointer_traits<pointer>::rebind<void>
-  using void_pointer = memory_internal::ExtractOrT<
-      memory_internal::GetVoidPointer, Alloc,
-      typename absl::pointer_traits<pointer>::template rebind<void>>;
-
-  // const_void_pointer:
-  // Alloc::const_void_pointer if present, otherwise
-  // absl::pointer_traits<pointer>::rebind<const void>
-  using const_void_pointer = memory_internal::ExtractOrT<
-      memory_internal::GetConstVoidPointer, Alloc,
-      typename absl::pointer_traits<pointer>::template rebind<const void>>;
-
-  // difference_type:
-  // Alloc::difference_type if present, otherwise
-  // absl::pointer_traits<pointer>::difference_type
-  using difference_type = memory_internal::ExtractOrT<
-      memory_internal::GetDifferenceType, Alloc,
-      typename absl::pointer_traits<pointer>::difference_type>;
-
-  // size_type:
-  // Alloc::size_type if present, otherwise
-  // std::make_unsigned<difference_type>::type
-  using size_type = memory_internal::ExtractOrT<
-      memory_internal::GetSizeType, Alloc,
-      typename std::make_unsigned<difference_type>::type>;
-
-  // propagate_on_container_copy_assignment:
-  // Alloc::propagate_on_container_copy_assignment if present, otherwise
-  // std::false_type
-  using propagate_on_container_copy_assignment = memory_internal::ExtractOrT<
-      memory_internal::GetPropagateOnContainerCopyAssignment, Alloc,
-      std::false_type>;
-
-  // propagate_on_container_move_assignment:
-  // Alloc::propagate_on_container_move_assignment if present, otherwise
-  // std::false_type
-  using propagate_on_container_move_assignment = memory_internal::ExtractOrT<
-      memory_internal::GetPropagateOnContainerMoveAssignment, Alloc,
-      std::false_type>;
-
-  // propagate_on_container_swap:
-  // Alloc::propagate_on_container_swap if present, otherwise std::false_type
-  using propagate_on_container_swap =
-      memory_internal::ExtractOrT<memory_internal::GetPropagateOnContainerSwap,
-                                  Alloc, std::false_type>;
-
-  // is_always_equal:
-  // Alloc::is_always_equal if present, otherwise std::is_empty<Alloc>::type
-  using is_always_equal =
-      memory_internal::ExtractOrT<memory_internal::GetIsAlwaysEqual, Alloc,
-                                  typename std::is_empty<Alloc>::type>;
-
-  // rebind_alloc:
-  // Alloc::rebind<T>::other if present, otherwise Alloc<T, Args> if this Alloc
-  // is Alloc<U, Args>
-  template <typename T>
-  using rebind_alloc = typename memory_internal::RebindAlloc<Alloc, T>::type;
-
-  // rebind_traits:
-  // absl::allocator_traits<rebind_alloc<T>>
-  template <typename T>
-  using rebind_traits = absl::allocator_traits<rebind_alloc<T>>;
-
-  // allocate(Alloc& a, size_type n):
-  // Calls a.allocate(n)
-  static pointer allocate(Alloc& a,  // NOLINT(runtime/references)
-                          size_type n) {
-    return a.allocate(n);
-  }
-
-  // allocate(Alloc& a, size_type n, const_void_pointer hint):
-  // Calls a.allocate(n, hint) if possible.
-  // If not possible, calls a.allocate(n)
-  static pointer allocate(Alloc& a, size_type n,  // NOLINT(runtime/references)
-                          const_void_pointer hint) {
-    return allocate_impl(0, a, n, hint);
-  }
-
-  // deallocate(Alloc& a, pointer p, size_type n):
-  // Calls a.deallocate(p, n)
-  static void deallocate(Alloc& a, pointer p,  // NOLINT(runtime/references)
-                         size_type n) {
-    a.deallocate(p, n);
-  }
-
-  // construct(Alloc& a, T* p, Args&&... args):
-  // Calls a.construct(p, std::forward<Args>(args)...) if possible.
-  // If not possible, calls
-  //   ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)
-  template <typename T, typename... Args>
-  static void construct(Alloc& a, T* p,  // NOLINT(runtime/references)
-                        Args&&... args) {
-    construct_impl(0, a, p, std::forward<Args>(args)...);
-  }
-
-  // destroy(Alloc& a, T* p):
-  // Calls a.destroy(p) if possible. If not possible, calls p->~T().
-  template <typename T>
-  static void destroy(Alloc& a, T* p) {  // NOLINT(runtime/references)
-    destroy_impl(0, a, p);
-  }
-
-  // max_size(const Alloc& a):
-  // Returns a.max_size() if possible. If not possible, returns
-  //   std::numeric_limits<size_type>::max() / sizeof(value_type)
-  static size_type max_size(const Alloc& a) { return max_size_impl(0, a); }
-
-  // select_on_container_copy_construction(const Alloc& a):
-  // Returns a.select_on_container_copy_construction() if possible.
-  // If not possible, returns a.
-  static Alloc select_on_container_copy_construction(const Alloc& a) {
-    return select_on_container_copy_construction_impl(0, a);
-  }
-
- private:
-  template <typename A>
-  static auto allocate_impl(int, A& a,  // NOLINT(runtime/references)
-                            size_type n, const_void_pointer hint)
-      -> decltype(a.allocate(n, hint)) {
-    return a.allocate(n, hint);
-  }
-  static pointer allocate_impl(char, Alloc& a,  // NOLINT(runtime/references)
-                               size_type n, const_void_pointer) {
-    return a.allocate(n);
-  }
-
-  template <typename A, typename... Args>
-  static auto construct_impl(int, A& a,  // NOLINT(runtime/references)
-                             Args&&... args)
-      -> decltype(a.construct(std::forward<Args>(args)...)) {
-    a.construct(std::forward<Args>(args)...);
-  }
-
-  template <typename T, typename... Args>
-  static void construct_impl(char, Alloc&, T* p, Args&&... args) {
-    ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
-  }
-
-  template <typename A, typename T>
-  static auto destroy_impl(int, A& a,  // NOLINT(runtime/references)
-                           T* p) -> decltype(a.destroy(p)) {
-    a.destroy(p);
-  }
-  template <typename T>
-  static void destroy_impl(char, Alloc&, T* p) {
-    p->~T();
-  }
-
-  template <typename A>
-  static auto max_size_impl(int, const A& a) -> decltype(a.max_size()) {
-    return a.max_size();
-  }
-  static size_type max_size_impl(char, const Alloc&) {
-    return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
-  }
-
-  template <typename A>
-  static auto select_on_container_copy_construction_impl(int, const A& a)
-      -> decltype(a.select_on_container_copy_construction()) {
-    return a.select_on_container_copy_construction();
-  }
-  static Alloc select_on_container_copy_construction_impl(char,
-                                                          const Alloc& a) {
-    return a;
-  }
-};
-#endif  // __cplusplus >= 201703L
-
-namespace memory_internal {
-
-// This template alias transforms Alloc::is_nothrow into a metafunction with
-// Alloc as a parameter so it can be used with ExtractOrT<>.
-template <typename Alloc>
-using GetIsNothrow = typename Alloc::is_nothrow;
-
-}  // namespace memory_internal
-
-// ABSL_ALLOCATOR_NOTHROW is a build time configuration macro for user to
-// specify whether the default allocation function can throw or never throws.
-// If the allocation function never throws, user should define it to a non-zero
-// value (e.g. via `-DABSL_ALLOCATOR_NOTHROW`).
-// If the allocation function can throw, user should leave it undefined or
-// define it to zero.
-//
-// allocator_is_nothrow<Alloc> is a traits class that derives from
-// Alloc::is_nothrow if present, otherwise std::false_type. It's specialized
-// for Alloc = std::allocator<T> for any type T according to the state of
-// ABSL_ALLOCATOR_NOTHROW.
-//
-// default_allocator_is_nothrow is a class that derives from std::true_type
-// when the default allocator (global operator new) never throws, and
-// std::false_type when it can throw. It is a convenience shorthand for writing
-// allocator_is_nothrow<std::allocator<T>> (T can be any type).
-// NOTE: allocator_is_nothrow<std::allocator<T>> is guaranteed to derive from
-// the same type for all T, because users should specialize neither
-// allocator_is_nothrow nor std::allocator.
-template <typename Alloc>
-struct allocator_is_nothrow
-    : memory_internal::ExtractOrT<memory_internal::GetIsNothrow, Alloc,
-                                  std::false_type> {};
-
-#if defined(ABSL_ALLOCATOR_NOTHROW) && ABSL_ALLOCATOR_NOTHROW
-template <typename T>
-struct allocator_is_nothrow<std::allocator<T>> : std::true_type {};
-struct default_allocator_is_nothrow : std::true_type {};
-#else
-struct default_allocator_is_nothrow : std::false_type {};
-#endif
-
-namespace memory_internal {
-template <typename Allocator, typename Iterator, typename... Args>
-void ConstructRange(Allocator& alloc, Iterator first, Iterator last,
-                    const Args&... args) {
-  for (Iterator cur = first; cur != last; ++cur) {
-    ABSL_INTERNAL_TRY {
-      std::allocator_traits<Allocator>::construct(alloc, std::addressof(*cur),
-                                                  args...);
-    }
-    ABSL_INTERNAL_CATCH_ANY {
-      while (cur != first) {
-        --cur;
-        std::allocator_traits<Allocator>::destroy(alloc, std::addressof(*cur));
-      }
-      ABSL_INTERNAL_RETHROW;
-    }
-  }
-}
-
-template <typename Allocator, typename Iterator, typename InputIterator>
-void CopyRange(Allocator& alloc, Iterator destination, InputIterator first,
-               InputIterator last) {
-  for (Iterator cur = destination; first != last;
-       static_cast<void>(++cur), static_cast<void>(++first)) {
-    ABSL_INTERNAL_TRY {
-      std::allocator_traits<Allocator>::construct(alloc, std::addressof(*cur),
-                                                  *first);
-    }
-    ABSL_INTERNAL_CATCH_ANY {
-      while (cur != destination) {
-        --cur;
-        std::allocator_traits<Allocator>::destroy(alloc, std::addressof(*cur));
-      }
-      ABSL_INTERNAL_RETHROW;
-    }
-  }
-}
-}  // namespace memory_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_MEMORY_MEMORY_H_
diff --git a/third_party/abseil_cpp/absl/memory/memory_exception_safety_test.cc b/third_party/abseil_cpp/absl/memory/memory_exception_safety_test.cc
deleted file mode 100644
index 1df72614c0..0000000000
--- a/third_party/abseil_cpp/absl/memory/memory_exception_safety_test.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/memory/memory.h"
-
-#include "absl/base/config.h"
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/exception_safety_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-constexpr int kLength = 50;
-using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
-
-TEST(MakeUnique, CheckForLeaks) {
-  constexpr int kValue = 321;
-  auto tester = testing::MakeExceptionSafetyTester()
-                    .WithInitialValue(Thrower(kValue))
-                    // Ensures make_unique does not modify the input. The real
-                    // test, though, is ConstructorTracker checking for leaks.
-                    .WithContracts(testing::strong_guarantee);
-
-  EXPECT_TRUE(tester.Test([](Thrower* thrower) {
-    static_cast<void>(absl::make_unique<Thrower>(*thrower));
-  }));
-
-  EXPECT_TRUE(tester.Test([](Thrower* thrower) {
-    static_cast<void>(absl::make_unique<Thrower>(std::move(*thrower)));
-  }));
-
-  // Test T[n] overload
-  EXPECT_TRUE(tester.Test([&](Thrower*) {
-    static_cast<void>(absl::make_unique<Thrower[]>(kLength));
-  }));
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_HAVE_EXCEPTIONS
diff --git a/third_party/abseil_cpp/absl/memory/memory_test.cc b/third_party/abseil_cpp/absl/memory/memory_test.cc
deleted file mode 100644
index 1990c7ba47..0000000000
--- a/third_party/abseil_cpp/absl/memory/memory_test.cc
+++ /dev/null
@@ -1,650 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Tests for pointer utilities.
-
-#include "absl/memory/memory.h"
-
-#include <sys/types.h>
-
-#include <cstddef>
-#include <memory>
-#include <string>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-using ::testing::ElementsAre;
-using ::testing::Return;
-
-// This class creates observable behavior to verify that a destructor has
-// been called, via the instance_count variable.
-class DestructorVerifier {
- public:
-  DestructorVerifier() { ++instance_count_; }
-  DestructorVerifier(const DestructorVerifier&) = delete;
-  DestructorVerifier& operator=(const DestructorVerifier&) = delete;
-  ~DestructorVerifier() { --instance_count_; }
-
-  // The number of instances of this class currently active.
-  static int instance_count() { return instance_count_; }
-
- private:
-  // The number of instances of this class currently active.
-  static int instance_count_;
-};
-
-int DestructorVerifier::instance_count_ = 0;
-
-TEST(WrapUniqueTest, WrapUnique) {
-  // Test that the unique_ptr is constructed properly by verifying that the
-  // destructor for its payload gets called at the proper time.
-  {
-    auto dv = new DestructorVerifier;
-    EXPECT_EQ(1, DestructorVerifier::instance_count());
-    std::unique_ptr<DestructorVerifier> ptr = absl::WrapUnique(dv);
-    EXPECT_EQ(1, DestructorVerifier::instance_count());
-  }
-  EXPECT_EQ(0, DestructorVerifier::instance_count());
-}
-TEST(MakeUniqueTest, Basic) {
-  std::unique_ptr<std::string> p = absl::make_unique<std::string>();
-  EXPECT_EQ("", *p);
-  p = absl::make_unique<std::string>("hi");
-  EXPECT_EQ("hi", *p);
-}
-
-// InitializationVerifier fills in a pattern when allocated so we can
-// distinguish between its default and value initialized states (without
-// accessing truly uninitialized memory).
-struct InitializationVerifier {
-  static constexpr int kDefaultScalar = 0x43;
-  static constexpr int kDefaultArray = 0x4B;
-
-  static void* operator new(size_t n) {
-    void* ret = ::operator new(n);
-    memset(ret, kDefaultScalar, n);
-    return ret;
-  }
-
-  static void* operator new[](size_t n) {
-    void* ret = ::operator new[](n);
-    memset(ret, kDefaultArray, n);
-    return ret;
-  }
-
-  int a;
-  int b;
-};
-
-TEST(Initialization, MakeUnique) {
-  auto p = absl::make_unique<InitializationVerifier>();
-
-  EXPECT_EQ(0, p->a);
-  EXPECT_EQ(0, p->b);
-}
-
-TEST(Initialization, MakeUniqueArray) {
-  auto p = absl::make_unique<InitializationVerifier[]>(2);
-
-  EXPECT_EQ(0, p[0].a);
-  EXPECT_EQ(0, p[0].b);
-  EXPECT_EQ(0, p[1].a);
-  EXPECT_EQ(0, p[1].b);
-}
-
-struct MoveOnly {
-  MoveOnly() = default;
-  explicit MoveOnly(int i1) : ip1{new int{i1}} {}
-  MoveOnly(int i1, int i2) : ip1{new int{i1}}, ip2{new int{i2}} {}
-  std::unique_ptr<int> ip1;
-  std::unique_ptr<int> ip2;
-};
-
-struct AcceptMoveOnly {
-  explicit AcceptMoveOnly(MoveOnly m) : m_(std::move(m)) {}
-  MoveOnly m_;
-};
-
-TEST(MakeUniqueTest, MoveOnlyTypeAndValue) {
-  using ExpectedType = std::unique_ptr<MoveOnly>;
-  {
-    auto p = absl::make_unique<MoveOnly>();
-    static_assert(std::is_same<decltype(p), ExpectedType>::value,
-                  "unexpected return type");
-    EXPECT_TRUE(!p->ip1);
-    EXPECT_TRUE(!p->ip2);
-  }
-  {
-    auto p = absl::make_unique<MoveOnly>(1);
-    static_assert(std::is_same<decltype(p), ExpectedType>::value,
-                  "unexpected return type");
-    EXPECT_TRUE(p->ip1 && *p->ip1 == 1);
-    EXPECT_TRUE(!p->ip2);
-  }
-  {
-    auto p = absl::make_unique<MoveOnly>(1, 2);
-    static_assert(std::is_same<decltype(p), ExpectedType>::value,
-                  "unexpected return type");
-    EXPECT_TRUE(p->ip1 && *p->ip1 == 1);
-    EXPECT_TRUE(p->ip2 && *p->ip2 == 2);
-  }
-}
-
-TEST(MakeUniqueTest, AcceptMoveOnly) {
-  auto p = absl::make_unique<AcceptMoveOnly>(MoveOnly());
-  p = std::unique_ptr<AcceptMoveOnly>(new AcceptMoveOnly(MoveOnly()));
-}
-
-struct ArrayWatch {
-  void* operator new[](size_t n) {
-    allocs().push_back(n);
-    return ::operator new[](n);
-  }
-  void operator delete[](void* p) { return ::operator delete[](p); }
-  static std::vector<size_t>& allocs() {
-    static auto& v = *new std::vector<size_t>;
-    return v;
-  }
-};
-
-TEST(Make_UniqueTest, Array) {
-  // Ensure state is clean before we start so that these tests
-  // are order-agnostic.
-  ArrayWatch::allocs().clear();
-
-  auto p = absl::make_unique<ArrayWatch[]>(5);
-  static_assert(std::is_same<decltype(p), std::unique_ptr<ArrayWatch[]>>::value,
-                "unexpected return type");
-  EXPECT_THAT(ArrayWatch::allocs(), ElementsAre(5 * sizeof(ArrayWatch)));
-}
-
-TEST(Make_UniqueTest, NotAmbiguousWithStdMakeUnique) {
-  // Ensure that absl::make_unique is not ambiguous with std::make_unique.
-  // In C++14 mode, the below call to make_unique has both types as candidates.
-  struct TakesStdType {
-    explicit TakesStdType(const std::vector<int>& vec) {}
-  };
-  using absl::make_unique;
-  (void)make_unique<TakesStdType>(std::vector<int>());
-}
-
-#if 0
-// These tests shouldn't compile.
-TEST(MakeUniqueTestNC, AcceptMoveOnlyLvalue) {
-  auto m = MoveOnly();
-  auto p = absl::make_unique<AcceptMoveOnly>(m);
-}
-TEST(MakeUniqueTestNC, KnownBoundArray) {
-  auto p = absl::make_unique<ArrayWatch[5]>();
-}
-#endif
-
-TEST(RawPtrTest, RawPointer) {
-  int i = 5;
-  EXPECT_EQ(&i, absl::RawPtr(&i));
-}
-
-TEST(RawPtrTest, SmartPointer) {
-  int* o = new int(5);
-  std::unique_ptr<int> p(o);
-  EXPECT_EQ(o, absl::RawPtr(p));
-}
-
-class IntPointerNonConstDeref {
- public:
-  explicit IntPointerNonConstDeref(int* p) : p_(p) {}
-  friend bool operator!=(const IntPointerNonConstDeref& a, std::nullptr_t) {
-    return a.p_ != nullptr;
-  }
-  int& operator*() { return *p_; }
-
- private:
-  std::unique_ptr<int> p_;
-};
-
-TEST(RawPtrTest, SmartPointerNonConstDereference) {
-  int* o = new int(5);
-  IntPointerNonConstDeref p(o);
-  EXPECT_EQ(o, absl::RawPtr(p));
-}
-
-TEST(RawPtrTest, NullValuedRawPointer) {
-  int* p = nullptr;
-  EXPECT_EQ(nullptr, absl::RawPtr(p));
-}
-
-TEST(RawPtrTest, NullValuedSmartPointer) {
-  std::unique_ptr<int> p;
-  EXPECT_EQ(nullptr, absl::RawPtr(p));
-}
-
-TEST(RawPtrTest, Nullptr) {
-  auto p = absl::RawPtr(nullptr);
-  EXPECT_TRUE((std::is_same<std::nullptr_t, decltype(p)>::value));
-  EXPECT_EQ(nullptr, p);
-}
-
-TEST(RawPtrTest, Null) {
-  auto p = absl::RawPtr(nullptr);
-  EXPECT_TRUE((std::is_same<std::nullptr_t, decltype(p)>::value));
-  EXPECT_EQ(nullptr, p);
-}
-
-TEST(RawPtrTest, Zero) {
-  auto p = absl::RawPtr(nullptr);
-  EXPECT_TRUE((std::is_same<std::nullptr_t, decltype(p)>::value));
-  EXPECT_EQ(nullptr, p);
-}
-
-TEST(ShareUniquePtrTest, Share) {
-  auto up = absl::make_unique<int>();
-  int* rp = up.get();
-  auto sp = absl::ShareUniquePtr(std::move(up));
-  EXPECT_EQ(sp.get(), rp);
-}
-
-TEST(ShareUniquePtrTest, ShareNull) {
-  struct NeverDie {
-    using pointer = void*;
-    void operator()(pointer) {
-      ASSERT_TRUE(false) << "Deleter should not have been called.";
-    }
-  };
-
-  std::unique_ptr<void, NeverDie> up;
-  auto sp = absl::ShareUniquePtr(std::move(up));
-}
-
-TEST(WeakenPtrTest, Weak) {
-  auto sp = std::make_shared<int>();
-  auto wp = absl::WeakenPtr(sp);
-  EXPECT_EQ(sp.get(), wp.lock().get());
-  sp.reset();
-  EXPECT_TRUE(wp.expired());
-}
-
-// Should not compile.
-/*
-TEST(RawPtrTest, NotAPointer) {
-  absl::RawPtr(1.5);
-}
-*/
-
-template <typename T>
-struct SmartPointer {
-  using difference_type = char;
-};
-
-struct PointerWith {
-  using element_type = int32_t;
-  using difference_type = int16_t;
-  template <typename U>
-  using rebind = SmartPointer<U>;
-
-  static PointerWith pointer_to(
-      element_type& r) {  // NOLINT(runtime/references)
-    return PointerWith{&r};
-  }
-
-  element_type* ptr;
-};
-
-template <typename... Args>
-struct PointerWithout {};
-
-TEST(PointerTraits, Types) {
-  using TraitsWith = absl::pointer_traits<PointerWith>;
-  EXPECT_TRUE((std::is_same<TraitsWith::pointer, PointerWith>::value));
-  EXPECT_TRUE((std::is_same<TraitsWith::element_type, int32_t>::value));
-  EXPECT_TRUE((std::is_same<TraitsWith::difference_type, int16_t>::value));
-  EXPECT_TRUE((
-      std::is_same<TraitsWith::rebind<int64_t>, SmartPointer<int64_t>>::value));
-
-  using TraitsWithout = absl::pointer_traits<PointerWithout<double, int>>;
-  EXPECT_TRUE((std::is_same<TraitsWithout::pointer,
-                            PointerWithout<double, int>>::value));
-  EXPECT_TRUE((std::is_same<TraitsWithout::element_type, double>::value));
-  EXPECT_TRUE(
-      (std::is_same<TraitsWithout ::difference_type, std::ptrdiff_t>::value));
-  EXPECT_TRUE((std::is_same<TraitsWithout::rebind<int64_t>,
-                            PointerWithout<int64_t, int>>::value));
-
-  using TraitsRawPtr = absl::pointer_traits<char*>;
-  EXPECT_TRUE((std::is_same<TraitsRawPtr::pointer, char*>::value));
-  EXPECT_TRUE((std::is_same<TraitsRawPtr::element_type, char>::value));
-  EXPECT_TRUE(
-      (std::is_same<TraitsRawPtr::difference_type, std::ptrdiff_t>::value));
-  EXPECT_TRUE((std::is_same<TraitsRawPtr::rebind<int64_t>, int64_t*>::value));
-}
-
-TEST(PointerTraits, Functions) {
-  int i;
-  EXPECT_EQ(&i, absl::pointer_traits<PointerWith>::pointer_to(i).ptr);
-  EXPECT_EQ(&i, absl::pointer_traits<int*>::pointer_to(i));
-}
-
-TEST(AllocatorTraits, Typedefs) {
-  struct A {
-    struct value_type {};
-  };
-  EXPECT_TRUE((
-      std::is_same<A,
-                   typename absl::allocator_traits<A>::allocator_type>::value));
-  EXPECT_TRUE(
-      (std::is_same<A::value_type,
-                    typename absl::allocator_traits<A>::value_type>::value));
-
-  struct X {};
-  struct HasPointer {
-    using value_type = X;
-    using pointer = SmartPointer<X>;
-  };
-  EXPECT_TRUE((std::is_same<SmartPointer<X>, typename absl::allocator_traits<
-                                                 HasPointer>::pointer>::value));
-  EXPECT_TRUE(
-      (std::is_same<A::value_type*,
-                    typename absl::allocator_traits<A>::pointer>::value));
-
-  EXPECT_TRUE(
-      (std::is_same<
-          SmartPointer<const X>,
-          typename absl::allocator_traits<HasPointer>::const_pointer>::value));
-  EXPECT_TRUE(
-      (std::is_same<const A::value_type*,
-                    typename absl::allocator_traits<A>::const_pointer>::value));
-
-  struct HasVoidPointer {
-    using value_type = X;
-    struct void_pointer {};
-  };
-
-  EXPECT_TRUE((std::is_same<HasVoidPointer::void_pointer,
-                            typename absl::allocator_traits<
-                                HasVoidPointer>::void_pointer>::value));
-  EXPECT_TRUE(
-      (std::is_same<SmartPointer<void>, typename absl::allocator_traits<
-                                            HasPointer>::void_pointer>::value));
-
-  struct HasConstVoidPointer {
-    using value_type = X;
-    struct const_void_pointer {};
-  };
-
-  EXPECT_TRUE(
-      (std::is_same<HasConstVoidPointer::const_void_pointer,
-                    typename absl::allocator_traits<
-                        HasConstVoidPointer>::const_void_pointer>::value));
-  EXPECT_TRUE((std::is_same<SmartPointer<const void>,
-                            typename absl::allocator_traits<
-                                HasPointer>::const_void_pointer>::value));
-
-  struct HasDifferenceType {
-    using value_type = X;
-    using difference_type = int;
-  };
-  EXPECT_TRUE(
-      (std::is_same<int, typename absl::allocator_traits<
-                             HasDifferenceType>::difference_type>::value));
-  EXPECT_TRUE((std::is_same<char, typename absl::allocator_traits<
-                                      HasPointer>::difference_type>::value));
-
-  struct HasSizeType {
-    using value_type = X;
-    using size_type = unsigned int;
-  };
-  EXPECT_TRUE((std::is_same<unsigned int, typename absl::allocator_traits<
-                                              HasSizeType>::size_type>::value));
-  EXPECT_TRUE((std::is_same<unsigned char, typename absl::allocator_traits<
-                                               HasPointer>::size_type>::value));
-
-  struct HasPropagateOnCopy {
-    using value_type = X;
-    struct propagate_on_container_copy_assignment {};
-  };
-
-  EXPECT_TRUE(
-      (std::is_same<HasPropagateOnCopy::propagate_on_container_copy_assignment,
-                    typename absl::allocator_traits<HasPropagateOnCopy>::
-                        propagate_on_container_copy_assignment>::value));
-  EXPECT_TRUE(
-      (std::is_same<std::false_type,
-                    typename absl::allocator_traits<
-                        A>::propagate_on_container_copy_assignment>::value));
-
-  struct HasPropagateOnMove {
-    using value_type = X;
-    struct propagate_on_container_move_assignment {};
-  };
-
-  EXPECT_TRUE(
-      (std::is_same<HasPropagateOnMove::propagate_on_container_move_assignment,
-                    typename absl::allocator_traits<HasPropagateOnMove>::
-                        propagate_on_container_move_assignment>::value));
-  EXPECT_TRUE(
-      (std::is_same<std::false_type,
-                    typename absl::allocator_traits<
-                        A>::propagate_on_container_move_assignment>::value));
-
-  struct HasPropagateOnSwap {
-    using value_type = X;
-    struct propagate_on_container_swap {};
-  };
-
-  EXPECT_TRUE(
-      (std::is_same<HasPropagateOnSwap::propagate_on_container_swap,
-                    typename absl::allocator_traits<HasPropagateOnSwap>::
-                        propagate_on_container_swap>::value));
-  EXPECT_TRUE(
-      (std::is_same<std::false_type, typename absl::allocator_traits<A>::
-                                         propagate_on_container_swap>::value));
-
-  struct HasIsAlwaysEqual {
-    using value_type = X;
-    struct is_always_equal {};
-  };
-
-  EXPECT_TRUE((std::is_same<HasIsAlwaysEqual::is_always_equal,
-                            typename absl::allocator_traits<
-                                HasIsAlwaysEqual>::is_always_equal>::value));
-  EXPECT_TRUE((std::is_same<std::true_type, typename absl::allocator_traits<
-                                                A>::is_always_equal>::value));
-  struct NonEmpty {
-    using value_type = X;
-    int i;
-  };
-  EXPECT_TRUE(
-      (std::is_same<std::false_type,
-                    absl::allocator_traits<NonEmpty>::is_always_equal>::value));
-}
-
-template <typename T>
-struct AllocWithPrivateInheritance : private std::allocator<T> {
-  using value_type = T;
-};
-
-TEST(AllocatorTraits, RebindWithPrivateInheritance) {
-  // Regression test for some versions of gcc that do not like the sfinae we
-  // used in combination with private inheritance.
-  EXPECT_TRUE(
-      (std::is_same<AllocWithPrivateInheritance<int>,
-                    absl::allocator_traits<AllocWithPrivateInheritance<char>>::
-                        rebind_alloc<int>>::value));
-}
-
-template <typename T>
-struct Rebound {};
-
-struct AllocWithRebind {
-  using value_type = int;
-  template <typename T>
-  struct rebind {
-    using other = Rebound<T>;
-  };
-};
-
-template <typename T, typename U>
-struct AllocWithoutRebind {
-  using value_type = int;
-};
-
-TEST(AllocatorTraits, Rebind) {
-  EXPECT_TRUE(
-      (std::is_same<Rebound<int>,
-                    typename absl::allocator_traits<
-                        AllocWithRebind>::template rebind_alloc<int>>::value));
-  EXPECT_TRUE(
-      (std::is_same<absl::allocator_traits<Rebound<int>>,
-                    typename absl::allocator_traits<
-                        AllocWithRebind>::template rebind_traits<int>>::value));
-
-  EXPECT_TRUE(
-      (std::is_same<AllocWithoutRebind<double, char>,
-                    typename absl::allocator_traits<AllocWithoutRebind<
-                        int, char>>::template rebind_alloc<double>>::value));
-  EXPECT_TRUE(
-      (std::is_same<absl::allocator_traits<AllocWithoutRebind<double, char>>,
-                    typename absl::allocator_traits<AllocWithoutRebind<
-                        int, char>>::template rebind_traits<double>>::value));
-}
-
-struct TestValue {
-  TestValue() {}
-  explicit TestValue(int* trace) : trace(trace) { ++*trace; }
-  ~TestValue() {
-    if (trace) --*trace;
-  }
-  int* trace = nullptr;
-};
-
-struct MinimalMockAllocator {
-  MinimalMockAllocator() : value(0) {}
-  explicit MinimalMockAllocator(int value) : value(value) {}
-  MinimalMockAllocator(const MinimalMockAllocator& other)
-      : value(other.value) {}
-  using value_type = TestValue;
-  MOCK_METHOD(value_type*, allocate, (size_t));
-  MOCK_METHOD(void, deallocate, (value_type*, size_t));
-
-  int value;
-};
-
-TEST(AllocatorTraits, FunctionsMinimal) {
-  int trace = 0;
-  int hint;
-  TestValue x(&trace);
-  MinimalMockAllocator mock;
-  using Traits = absl::allocator_traits<MinimalMockAllocator>;
-  EXPECT_CALL(mock, allocate(7)).WillRepeatedly(Return(&x));
-  EXPECT_CALL(mock, deallocate(&x, 7));
-
-  EXPECT_EQ(&x, Traits::allocate(mock, 7));
-  static_cast<void>(Traits::allocate(mock, 7, static_cast<const void*>(&hint)));
-  EXPECT_EQ(&x, Traits::allocate(mock, 7, static_cast<const void*>(&hint)));
-  Traits::deallocate(mock, &x, 7);
-
-  EXPECT_EQ(1, trace);
-  Traits::construct(mock, &x, &trace);
-  EXPECT_EQ(2, trace);
-  Traits::destroy(mock, &x);
-  EXPECT_EQ(1, trace);
-
-  EXPECT_EQ(std::numeric_limits<size_t>::max() / sizeof(TestValue),
-            Traits::max_size(mock));
-
-  EXPECT_EQ(0, mock.value);
-  EXPECT_EQ(0, Traits::select_on_container_copy_construction(mock).value);
-}
-
-struct FullMockAllocator {
-  FullMockAllocator() : value(0) {}
-  explicit FullMockAllocator(int value) : value(value) {}
-  FullMockAllocator(const FullMockAllocator& other) : value(other.value) {}
-  using value_type = TestValue;
-  MOCK_METHOD(value_type*, allocate, (size_t));
-  MOCK_METHOD(value_type*, allocate, (size_t, const void*));
-  MOCK_METHOD(void, construct, (value_type*, int*));
-  MOCK_METHOD(void, destroy, (value_type*));
-  MOCK_METHOD(size_t, max_size, (),
-              (const));
-  MOCK_METHOD(FullMockAllocator, select_on_container_copy_construction, (),
-              (const));
-
-  int value;
-};
-
-TEST(AllocatorTraits, FunctionsFull) {
-  int trace = 0;
-  int hint;
-  TestValue x(&trace), y;
-  FullMockAllocator mock;
-  using Traits = absl::allocator_traits<FullMockAllocator>;
-  EXPECT_CALL(mock, allocate(7)).WillRepeatedly(Return(&x));
-  EXPECT_CALL(mock, allocate(13, &hint)).WillRepeatedly(Return(&y));
-  EXPECT_CALL(mock, construct(&x, &trace));
-  EXPECT_CALL(mock, destroy(&x));
-  EXPECT_CALL(mock, max_size()).WillRepeatedly(Return(17));
-  EXPECT_CALL(mock, select_on_container_copy_construction())
-      .WillRepeatedly(Return(FullMockAllocator(23)));
-
-  EXPECT_EQ(&x, Traits::allocate(mock, 7));
-  EXPECT_EQ(&y, Traits::allocate(mock, 13, static_cast<const void*>(&hint)));
-
-  EXPECT_EQ(1, trace);
-  Traits::construct(mock, &x, &trace);
-  EXPECT_EQ(1, trace);
-  Traits::destroy(mock, &x);
-  EXPECT_EQ(1, trace);
-
-  EXPECT_EQ(17, Traits::max_size(mock));
-
-  EXPECT_EQ(0, mock.value);
-  EXPECT_EQ(23, Traits::select_on_container_copy_construction(mock).value);
-}
-
-TEST(AllocatorNoThrowTest, DefaultAllocator) {
-#if defined(ABSL_ALLOCATOR_NOTHROW) && ABSL_ALLOCATOR_NOTHROW
-  EXPECT_TRUE(absl::default_allocator_is_nothrow::value);
-#else
-  EXPECT_FALSE(absl::default_allocator_is_nothrow::value);
-#endif
-}
-
-TEST(AllocatorNoThrowTest, StdAllocator) {
-#if defined(ABSL_ALLOCATOR_NOTHROW) && ABSL_ALLOCATOR_NOTHROW
-  EXPECT_TRUE(absl::allocator_is_nothrow<std::allocator<int>>::value);
-#else
-  EXPECT_FALSE(absl::allocator_is_nothrow<std::allocator<int>>::value);
-#endif
-}
-
-TEST(AllocatorNoThrowTest, CustomAllocator) {
-  struct NoThrowAllocator {
-    using is_nothrow = std::true_type;
-  };
-  struct CanThrowAllocator {
-    using is_nothrow = std::false_type;
-  };
-  struct UnspecifiedAllocator {};
-  EXPECT_TRUE(absl::allocator_is_nothrow<NoThrowAllocator>::value);
-  EXPECT_FALSE(absl::allocator_is_nothrow<CanThrowAllocator>::value);
-  EXPECT_FALSE(absl::allocator_is_nothrow<UnspecifiedAllocator>::value);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/meta/BUILD.bazel b/third_party/abseil_cpp/absl/meta/BUILD.bazel
deleted file mode 100644
index 5585fcca79..0000000000
--- a/third_party/abseil_cpp/absl/meta/BUILD.bazel
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "type_traits",
-    hdrs = ["type_traits.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-    ],
-)
-
-cc_test(
-    name = "type_traits_test",
-    srcs = ["type_traits_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":type_traits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/meta/CMakeLists.txt b/third_party/abseil_cpp/absl/meta/CMakeLists.txt
deleted file mode 100644
index 672ead2fd0..0000000000
--- a/third_party/abseil_cpp/absl/meta/CMakeLists.txt
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    type_traits
-  HDRS
-    "type_traits.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    type_traits_test
-  SRCS
-    "type_traits_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::type_traits
-    gmock_main
-)
-
-# component target
-absl_cc_library(
-  NAME
-    meta
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::type_traits
-  PUBLIC
-)
diff --git a/third_party/abseil_cpp/absl/meta/type_traits.h b/third_party/abseil_cpp/absl/meta/type_traits.h
deleted file mode 100644
index d5cb5f3be3..0000000000
--- a/third_party/abseil_cpp/absl/meta/type_traits.h
+++ /dev/null
@@ -1,767 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// type_traits.h
-// -----------------------------------------------------------------------------
-//
-// This file contains C++11-compatible versions of standard <type_traits> API
-// functions for determining the characteristics of types. Such traits can
-// support type inference, classification, and transformation, as well as
-// make it easier to write templates based on generic type behavior.
-//
-// See https://en.cppreference.com/w/cpp/header/type_traits
-//
-// WARNING: use of many of the constructs in this header will count as "complex
-// template metaprogramming", so before proceeding, please carefully consider
-// https://google.github.io/styleguide/cppguide.html#Template_metaprogramming
-//
-// WARNING: using template metaprogramming to detect or depend on API
-// features is brittle and not guaranteed. Neither the standard library nor
-// Abseil provides any guarantee that APIs are stable in the face of template
-// metaprogramming. Use with caution.
-#ifndef ABSL_META_TYPE_TRAITS_H_
-#define ABSL_META_TYPE_TRAITS_H_
-
-#include <stddef.h>
-#include <functional>
-#include <type_traits>
-
-#include "absl/base/config.h"
-
-// MSVC constructibility traits do not detect destructor properties and so our
-// implementations should not use them as a source-of-truth.
-#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)
-#define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Defined and documented later on in this file.
-template <typename T>
-struct is_trivially_destructible;
-
-// Defined and documented later on in this file.
-template <typename T>
-struct is_trivially_move_assignable;
-
-namespace type_traits_internal {
-
-// Silence MSVC warnings about the destructor being defined as deleted.
-#if defined(_MSC_VER) && !defined(__GNUC__)
-#pragma warning(push)
-#pragma warning(disable : 4624)
-#endif  // defined(_MSC_VER) && !defined(__GNUC__)
-
-template <class T>
-union SingleMemberUnion {
-  T t;
-};
-
-// Restore the state of the destructor warning that was silenced above.
-#if defined(_MSC_VER) && !defined(__GNUC__)
-#pragma warning(pop)
-#endif  // defined(_MSC_VER) && !defined(__GNUC__)
-
-template <class T>
-struct IsTriviallyMoveConstructibleObject
-    : std::integral_constant<
-          bool, std::is_move_constructible<
-                    type_traits_internal::SingleMemberUnion<T>>::value &&
-                    absl::is_trivially_destructible<T>::value> {};
-
-template <class T>
-struct IsTriviallyCopyConstructibleObject
-    : std::integral_constant<
-          bool, std::is_copy_constructible<
-                    type_traits_internal::SingleMemberUnion<T>>::value &&
-                    absl::is_trivially_destructible<T>::value> {};
-
-template <class T>
-struct IsTriviallyMoveAssignableReference : std::false_type {};
-
-template <class T>
-struct IsTriviallyMoveAssignableReference<T&>
-    : absl::is_trivially_move_assignable<T>::type {};
-
-template <class T>
-struct IsTriviallyMoveAssignableReference<T&&>
-    : absl::is_trivially_move_assignable<T>::type {};
-
-template <typename... Ts>
-struct VoidTImpl {
-  using type = void;
-};
-
-// This trick to retrieve a default alignment is necessary for our
-// implementation of aligned_storage_t to be consistent with any implementation
-// of std::aligned_storage.
-template <size_t Len, typename T = std::aligned_storage<Len>>
-struct default_alignment_of_aligned_storage;
-
-template <size_t Len, size_t Align>
-struct default_alignment_of_aligned_storage<Len,
-                                            std::aligned_storage<Len, Align>> {
-  static constexpr size_t value = Align;
-};
-
-////////////////////////////////
-// Library Fundamentals V2 TS //
-////////////////////////////////
-
-// NOTE: The `is_detected` family of templates here differ from the library
-// fundamentals specification in that for library fundamentals, `Op<Args...>` is
-// evaluated as soon as the type `is_detected<Op, Args...>` undergoes
-// substitution, regardless of whether or not the `::value` is accessed. That
-// is inconsistent with all other standard traits and prevents lazy evaluation
-// in larger contexts (such as if the `is_detected` check is a trailing argument
-// of a `conjunction`. This implementation opts to instead be lazy in the same
-// way that the standard traits are (this "defect" of the detection idiom
-// specifications has been reported).
-
-template <class Enabler, template <class...> class Op, class... Args>
-struct is_detected_impl {
-  using type = std::false_type;
-};
-
-template <template <class...> class Op, class... Args>
-struct is_detected_impl<typename VoidTImpl<Op<Args...>>::type, Op, Args...> {
-  using type = std::true_type;
-};
-
-template <template <class...> class Op, class... Args>
-struct is_detected : is_detected_impl<void, Op, Args...>::type {};
-
-template <class Enabler, class To, template <class...> class Op, class... Args>
-struct is_detected_convertible_impl {
-  using type = std::false_type;
-};
-
-template <class To, template <class...> class Op, class... Args>
-struct is_detected_convertible_impl<
-    typename std::enable_if<std::is_convertible<Op<Args...>, To>::value>::type,
-    To, Op, Args...> {
-  using type = std::true_type;
-};
-
-template <class To, template <class...> class Op, class... Args>
-struct is_detected_convertible
-    : is_detected_convertible_impl<void, To, Op, Args...>::type {};
-
-template <typename T>
-using IsCopyAssignableImpl =
-    decltype(std::declval<T&>() = std::declval<const T&>());
-
-template <typename T>
-using IsMoveAssignableImpl = decltype(std::declval<T&>() = std::declval<T&&>());
-
-}  // namespace type_traits_internal
-
-// MSVC 19.20 has a regression that causes our workarounds to fail, but their
-// std forms now appear to be compliant.
-#if defined(_MSC_VER) && !defined(__clang__) && (_MSC_VER >= 1920)
-
-template <typename T>
-using is_copy_assignable = std::is_copy_assignable<T>;
-
-template <typename T>
-using is_move_assignable = std::is_move_assignable<T>;
-
-#else
-
-template <typename T>
-struct is_copy_assignable : type_traits_internal::is_detected<
-                                type_traits_internal::IsCopyAssignableImpl, T> {
-};
-
-template <typename T>
-struct is_move_assignable : type_traits_internal::is_detected<
-                                type_traits_internal::IsMoveAssignableImpl, T> {
-};
-
-#endif
-
-// void_t()
-//
-// Ignores the type of any its arguments and returns `void`. In general, this
-// metafunction allows you to create a general case that maps to `void` while
-// allowing specializations that map to specific types.
-//
-// This metafunction is designed to be a drop-in replacement for the C++17
-// `std::void_t` metafunction.
-//
-// NOTE: `absl::void_t` does not use the standard-specified implementation so
-// that it can remain compatible with gcc < 5.1. This can introduce slightly
-// different behavior, such as when ordering partial specializations.
-template <typename... Ts>
-using void_t = typename type_traits_internal::VoidTImpl<Ts...>::type;
-
-// conjunction
-//
-// Performs a compile-time logical AND operation on the passed types (which
-// must have  `::value` members convertible to `bool`. Short-circuits if it
-// encounters any `false` members (and does not compare the `::value` members
-// of any remaining arguments).
-//
-// This metafunction is designed to be a drop-in replacement for the C++17
-// `std::conjunction` metafunction.
-template <typename... Ts>
-struct conjunction : std::true_type {};
-
-template <typename T, typename... Ts>
-struct conjunction<T, Ts...>
-    : std::conditional<T::value, conjunction<Ts...>, T>::type {};
-
-template <typename T>
-struct conjunction<T> : T {};
-
-// disjunction
-//
-// Performs a compile-time logical OR operation on the passed types (which
-// must have  `::value` members convertible to `bool`. Short-circuits if it
-// encounters any `true` members (and does not compare the `::value` members
-// of any remaining arguments).
-//
-// This metafunction is designed to be a drop-in replacement for the C++17
-// `std::disjunction` metafunction.
-template <typename... Ts>
-struct disjunction : std::false_type {};
-
-template <typename T, typename... Ts>
-struct disjunction<T, Ts...> :
-      std::conditional<T::value, T, disjunction<Ts...>>::type {};
-
-template <typename T>
-struct disjunction<T> : T {};
-
-// negation
-//
-// Performs a compile-time logical NOT operation on the passed type (which
-// must have  `::value` members convertible to `bool`.
-//
-// This metafunction is designed to be a drop-in replacement for the C++17
-// `std::negation` metafunction.
-template <typename T>
-struct negation : std::integral_constant<bool, !T::value> {};
-
-// is_function()
-//
-// Determines whether the passed type `T` is a function type.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_function()` metafunction for platforms that have incomplete C++11
-// support (such as libstdc++ 4.x).
-//
-// This metafunction works because appending `const` to a type does nothing to
-// function types and reference types (and forms a const-qualified type
-// otherwise).
-template <typename T>
-struct is_function
-    : std::integral_constant<
-          bool, !(std::is_reference<T>::value ||
-                  std::is_const<typename std::add_const<T>::type>::value)> {};
-
-// is_trivially_destructible()
-//
-// Determines whether the passed type `T` is trivially destructible.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_destructible()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: the extensions (__has_trivial_xxx) are implemented in gcc (version >=
-// 4.3) and clang. Since we are supporting libstdc++ > 4.7, they should always
-// be present. These  extensions are documented at
-// https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html#Type-Traits.
-template <typename T>
-struct is_trivially_destructible
-    : std::integral_constant<bool, __has_trivial_destructor(T) &&
-                                   std::is_destructible<T>::value> {
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
- private:
-  static constexpr bool compliant = std::is_trivially_destructible<T>::value ==
-                                    is_trivially_destructible::value;
-  static_assert(compliant || std::is_trivially_destructible<T>::value,
-                "Not compliant with std::is_trivially_destructible; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_destructible<T>::value,
-                "Not compliant with std::is_trivially_destructible; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
-};
-
-// is_trivially_default_constructible()
-//
-// Determines whether the passed type `T` is trivially default constructible.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_default_constructible()` metafunction for platforms that
-// have incomplete C++11 support (such as libstdc++ 4.x). On any platforms that
-// do fully support C++11, we check whether this yields the same result as the
-// std implementation.
-//
-// NOTE: according to the C++ standard, Section: 20.15.4.3 [meta.unary.prop]
-// "The predicate condition for a template specialization is_constructible<T,
-// Args...> shall be satisfied if and only if the following variable
-// definition would be well-formed for some invented variable t:
-//
-// T t(declval<Args>()...);
-//
-// is_trivially_constructible<T, Args...> additionally requires that the
-// variable definition does not call any operation that is not trivial.
-// For the purposes of this check, the call to std::declval is considered
-// trivial."
-//
-// Notes from https://en.cppreference.com/w/cpp/types/is_constructible:
-// In many implementations, is_nothrow_constructible also checks if the
-// destructor throws because it is effectively noexcept(T(arg)). Same
-// applies to is_trivially_constructible, which, in these implementations, also
-// requires that the destructor is trivial.
-// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452
-// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116.
-//
-// "T obj();" need to be well-formed and not call any nontrivial operation.
-// Nontrivially destructible types will cause the expression to be nontrivial.
-template <typename T>
-struct is_trivially_default_constructible
-    : std::integral_constant<bool, __has_trivial_constructor(T) &&
-                                   std::is_default_constructible<T>::value &&
-                                   is_trivially_destructible<T>::value> {
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
-    !defined(                                            \
-        ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
- private:
-  static constexpr bool compliant =
-      std::is_trivially_default_constructible<T>::value ==
-      is_trivially_default_constructible::value;
-  static_assert(compliant || std::is_trivially_default_constructible<T>::value,
-                "Not compliant with std::is_trivially_default_constructible; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_default_constructible<T>::value,
-                "Not compliant with std::is_trivially_default_constructible; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
-};
-
-// is_trivially_move_constructible()
-//
-// Determines whether the passed type `T` is trivially move constructible.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_move_constructible()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: `T obj(declval<T>());` needs to be well-formed and not call any
-// nontrivial operation.  Nontrivially destructible types will cause the
-// expression to be nontrivial.
-template <typename T>
-struct is_trivially_move_constructible
-    : std::conditional<
-          std::is_object<T>::value && !std::is_array<T>::value,
-          type_traits_internal::IsTriviallyMoveConstructibleObject<T>,
-          std::is_reference<T>>::type::type {
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
-    !defined(                                            \
-        ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
- private:
-  static constexpr bool compliant =
-      std::is_trivially_move_constructible<T>::value ==
-      is_trivially_move_constructible::value;
-  static_assert(compliant || std::is_trivially_move_constructible<T>::value,
-                "Not compliant with std::is_trivially_move_constructible; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_move_constructible<T>::value,
-                "Not compliant with std::is_trivially_move_constructible; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
-};
-
-// is_trivially_copy_constructible()
-//
-// Determines whether the passed type `T` is trivially copy constructible.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_copy_constructible()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: `T obj(declval<const T&>());` needs to be well-formed and not call any
-// nontrivial operation.  Nontrivially destructible types will cause the
-// expression to be nontrivial.
-template <typename T>
-struct is_trivially_copy_constructible
-    : std::conditional<
-          std::is_object<T>::value && !std::is_array<T>::value,
-          type_traits_internal::IsTriviallyCopyConstructibleObject<T>,
-          std::is_lvalue_reference<T>>::type::type {
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
-    !defined(                                            \
-        ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
- private:
-  static constexpr bool compliant =
-      std::is_trivially_copy_constructible<T>::value ==
-      is_trivially_copy_constructible::value;
-  static_assert(compliant || std::is_trivially_copy_constructible<T>::value,
-                "Not compliant with std::is_trivially_copy_constructible; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_copy_constructible<T>::value,
-                "Not compliant with std::is_trivially_copy_constructible; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
-};
-
-// is_trivially_move_assignable()
-//
-// Determines whether the passed type `T` is trivially move assignable.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_move_assignable()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: `is_assignable<T, U>::value` is `true` if the expression
-// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated
-// operand. `is_trivially_assignable<T, U>` requires the assignment to call no
-// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply
-// `is_trivially_assignable<T&, T>`.
-template <typename T>
-struct is_trivially_move_assignable
-    : std::conditional<
-          std::is_object<T>::value && !std::is_array<T>::value &&
-              std::is_move_assignable<T>::value,
-          std::is_move_assignable<type_traits_internal::SingleMemberUnion<T>>,
-          type_traits_internal::IsTriviallyMoveAssignableReference<T>>::type::
-          type {
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
- private:
-  static constexpr bool compliant =
-      std::is_trivially_move_assignable<T>::value ==
-      is_trivially_move_assignable::value;
-  static_assert(compliant || std::is_trivially_move_assignable<T>::value,
-                "Not compliant with std::is_trivially_move_assignable; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_move_assignable<T>::value,
-                "Not compliant with std::is_trivially_move_assignable; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
-};
-
-// is_trivially_copy_assignable()
-//
-// Determines whether the passed type `T` is trivially copy assignable.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_copy_assignable()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
-// fully support C++11, we check whether this yields the same result as the std
-// implementation.
-//
-// NOTE: `is_assignable<T, U>::value` is `true` if the expression
-// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated
-// operand. `is_trivially_assignable<T, U>` requires the assignment to call no
-// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply
-// `is_trivially_assignable<T&, const T&>`.
-template <typename T>
-struct is_trivially_copy_assignable
-    : std::integral_constant<
-          bool, __has_trivial_assign(typename std::remove_reference<T>::type) &&
-                    absl::is_copy_assignable<T>::value> {
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
- private:
-  static constexpr bool compliant =
-      std::is_trivially_copy_assignable<T>::value ==
-      is_trivially_copy_assignable::value;
-  static_assert(compliant || std::is_trivially_copy_assignable<T>::value,
-                "Not compliant with std::is_trivially_copy_assignable; "
-                "Standard: false, Implementation: true");
-  static_assert(compliant || !std::is_trivially_copy_assignable<T>::value,
-                "Not compliant with std::is_trivially_copy_assignable; "
-                "Standard: true, Implementation: false");
-#endif  // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
-};
-
-namespace type_traits_internal {
-// is_trivially_copyable()
-//
-// Determines whether the passed type `T` is trivially copyable.
-//
-// This metafunction is designed to be a drop-in replacement for the C++11
-// `std::is_trivially_copyable()` metafunction for platforms that have
-// incomplete C++11 support (such as libstdc++ 4.x). We use the C++17 definition
-// of TriviallyCopyable.
-//
-// NOTE: `is_trivially_copyable<T>::value` is `true` if all of T's copy/move
-// constructors/assignment operators are trivial or deleted, T has at least
-// one non-deleted copy/move constructor/assignment operator, and T is trivially
-// destructible. Arrays of trivially copyable types are trivially copyable.
-//
-// We expose this metafunction only for internal use within absl.
-template <typename T>
-class is_trivially_copyable_impl {
-  using ExtentsRemoved = typename std::remove_all_extents<T>::type;
-  static constexpr bool kIsCopyOrMoveConstructible =
-      std::is_copy_constructible<ExtentsRemoved>::value ||
-      std::is_move_constructible<ExtentsRemoved>::value;
-  static constexpr bool kIsCopyOrMoveAssignable =
-      absl::is_copy_assignable<ExtentsRemoved>::value ||
-      absl::is_move_assignable<ExtentsRemoved>::value;
-
- public:
-  static constexpr bool kValue =
-      (__has_trivial_copy(ExtentsRemoved) || !kIsCopyOrMoveConstructible) &&
-      (__has_trivial_assign(ExtentsRemoved) || !kIsCopyOrMoveAssignable) &&
-      (kIsCopyOrMoveConstructible || kIsCopyOrMoveAssignable) &&
-      is_trivially_destructible<ExtentsRemoved>::value &&
-      // We need to check for this explicitly because otherwise we'll say
-      // references are trivial copyable when compiled by MSVC.
-      !std::is_reference<ExtentsRemoved>::value;
-};
-
-template <typename T>
-struct is_trivially_copyable
-    : std::integral_constant<
-          bool, type_traits_internal::is_trivially_copyable_impl<T>::kValue> {};
-}  // namespace type_traits_internal
-
-// -----------------------------------------------------------------------------
-// C++14 "_t" trait aliases
-// -----------------------------------------------------------------------------
-
-template <typename T>
-using remove_cv_t = typename std::remove_cv<T>::type;
-
-template <typename T>
-using remove_const_t = typename std::remove_const<T>::type;
-
-template <typename T>
-using remove_volatile_t = typename std::remove_volatile<T>::type;
-
-template <typename T>
-using add_cv_t = typename std::add_cv<T>::type;
-
-template <typename T>
-using add_const_t = typename std::add_const<T>::type;
-
-template <typename T>
-using add_volatile_t = typename std::add_volatile<T>::type;
-
-template <typename T>
-using remove_reference_t = typename std::remove_reference<T>::type;
-
-template <typename T>
-using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type;
-
-template <typename T>
-using add_rvalue_reference_t = typename std::add_rvalue_reference<T>::type;
-
-template <typename T>
-using remove_pointer_t = typename std::remove_pointer<T>::type;
-
-template <typename T>
-using add_pointer_t = typename std::add_pointer<T>::type;
-
-template <typename T>
-using make_signed_t = typename std::make_signed<T>::type;
-
-template <typename T>
-using make_unsigned_t = typename std::make_unsigned<T>::type;
-
-template <typename T>
-using remove_extent_t = typename std::remove_extent<T>::type;
-
-template <typename T>
-using remove_all_extents_t = typename std::remove_all_extents<T>::type;
-
-template <size_t Len, size_t Align = type_traits_internal::
-                          default_alignment_of_aligned_storage<Len>::value>
-using aligned_storage_t = typename std::aligned_storage<Len, Align>::type;
-
-template <typename T>
-using decay_t = typename std::decay<T>::type;
-
-template <bool B, typename T = void>
-using enable_if_t = typename std::enable_if<B, T>::type;
-
-template <bool B, typename T, typename F>
-using conditional_t = typename std::conditional<B, T, F>::type;
-
-template <typename... T>
-using common_type_t = typename std::common_type<T...>::type;
-
-template <typename T>
-using underlying_type_t = typename std::underlying_type<T>::type;
-
-
-namespace type_traits_internal {
-
-#if __cplusplus >= 201703L
-// std::result_of is deprecated (C++17) or removed (C++20)
-template<typename> struct result_of;
-template<typename F, typename... Args>
-struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
-#else
-template<typename F> using result_of = std::result_of<F>;
-#endif
-
-}  // namespace type_traits_internal
-
-template<typename F>
-using result_of_t = typename type_traits_internal::result_of<F>::type;
-
-namespace type_traits_internal {
-// In MSVC we can't probe std::hash or stdext::hash because it triggers a
-// static_assert instead of failing substitution. Libc++ prior to 4.0
-// also used a static_assert.
-//
-#if defined(_MSC_VER) || (defined(_LIBCPP_VERSION) && \
-                          _LIBCPP_VERSION < 4000 && _LIBCPP_STD_VER > 11)
-#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 0
-#else
-#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 1
-#endif
-
-#if !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-template <typename Key, typename = size_t>
-struct IsHashable : std::true_type {};
-#else   // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-template <typename Key, typename = void>
-struct IsHashable : std::false_type {};
-
-template <typename Key>
-struct IsHashable<
-    Key,
-    absl::enable_if_t<std::is_convertible<
-        decltype(std::declval<std::hash<Key>&>()(std::declval<Key const&>())),
-        std::size_t>::value>> : std::true_type {};
-#endif  // !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-
-struct AssertHashEnabledHelper {
- private:
-  static void Sink(...) {}
-  struct NAT {};
-
-  template <class Key>
-  static auto GetReturnType(int)
-      -> decltype(std::declval<std::hash<Key>>()(std::declval<Key const&>()));
-  template <class Key>
-  static NAT GetReturnType(...);
-
-  template <class Key>
-  static std::nullptr_t DoIt() {
-    static_assert(IsHashable<Key>::value,
-                  "std::hash<Key> does not provide a call operator");
-    static_assert(
-        std::is_default_constructible<std::hash<Key>>::value,
-        "std::hash<Key> must be default constructible when it is enabled");
-    static_assert(
-        std::is_copy_constructible<std::hash<Key>>::value,
-        "std::hash<Key> must be copy constructible when it is enabled");
-    static_assert(absl::is_copy_assignable<std::hash<Key>>::value,
-                  "std::hash<Key> must be copy assignable when it is enabled");
-    // is_destructible is unchecked as it's implied by each of the
-    // is_constructible checks.
-    using ReturnType = decltype(GetReturnType<Key>(0));
-    static_assert(std::is_same<ReturnType, NAT>::value ||
-                      std::is_same<ReturnType, size_t>::value,
-                  "std::hash<Key> must return size_t");
-    return nullptr;
-  }
-
-  template <class... Ts>
-  friend void AssertHashEnabled();
-};
-
-template <class... Ts>
-inline void AssertHashEnabled() {
-  using Helper = AssertHashEnabledHelper;
-  Helper::Sink(Helper::DoIt<Ts>()...);
-}
-
-}  // namespace type_traits_internal
-
-// An internal namespace that is required to implement the C++17 swap traits.
-// It is not further nested in type_traits_internal to avoid long symbol names.
-namespace swap_internal {
-
-// Necessary for the traits.
-using std::swap;
-
-// This declaration prevents global `swap` and `absl::swap` overloads from being
-// considered unless ADL picks them up.
-void swap();
-
-template <class T>
-using IsSwappableImpl = decltype(swap(std::declval<T&>(), std::declval<T&>()));
-
-// NOTE: This dance with the default template parameter is for MSVC.
-template <class T,
-          class IsNoexcept = std::integral_constant<
-              bool, noexcept(swap(std::declval<T&>(), std::declval<T&>()))>>
-using IsNothrowSwappableImpl = typename std::enable_if<IsNoexcept::value>::type;
-
-// IsSwappable
-//
-// Determines whether the standard swap idiom is a valid expression for
-// arguments of type `T`.
-template <class T>
-struct IsSwappable
-    : absl::type_traits_internal::is_detected<IsSwappableImpl, T> {};
-
-// IsNothrowSwappable
-//
-// Determines whether the standard swap idiom is a valid expression for
-// arguments of type `T` and is noexcept.
-template <class T>
-struct IsNothrowSwappable
-    : absl::type_traits_internal::is_detected<IsNothrowSwappableImpl, T> {};
-
-// Swap()
-//
-// Performs the swap idiom from a namespace where valid candidates may only be
-// found in `std` or via ADL.
-template <class T, absl::enable_if_t<IsSwappable<T>::value, int> = 0>
-void Swap(T& lhs, T& rhs) noexcept(IsNothrowSwappable<T>::value) {
-  swap(lhs, rhs);
-}
-
-// StdSwapIsUnconstrained
-//
-// Some standard library implementations are broken in that they do not
-// constrain `std::swap`. This will effectively tell us if we are dealing with
-// one of those implementations.
-using StdSwapIsUnconstrained = IsSwappable<void()>;
-
-}  // namespace swap_internal
-
-namespace type_traits_internal {
-
-// Make the swap-related traits/function accessible from this namespace.
-using swap_internal::IsNothrowSwappable;
-using swap_internal::IsSwappable;
-using swap_internal::Swap;
-using swap_internal::StdSwapIsUnconstrained;
-
-}  // namespace type_traits_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_META_TYPE_TRAITS_H_
diff --git a/third_party/abseil_cpp/absl/meta/type_traits_test.cc b/third_party/abseil_cpp/absl/meta/type_traits_test.cc
deleted file mode 100644
index 1aafd0d49a..0000000000
--- a/third_party/abseil_cpp/absl/meta/type_traits_test.cc
+++ /dev/null
@@ -1,1368 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/meta/type_traits.h"
-
-#include <cstdint>
-#include <string>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "gtest/gtest.h"
-
-namespace {
-
-using ::testing::StaticAssertTypeEq;
-
-template <class T, class U>
-struct simple_pair {
-  T first;
-  U second;
-};
-
-struct Dummy {};
-
-struct ReturnType {};
-struct ConvertibleToReturnType {
-  operator ReturnType() const;  // NOLINT
-};
-
-// Unique types used as parameter types for testing the detection idiom.
-struct StructA {};
-struct StructB {};
-struct StructC {};
-
-struct TypeWithBarFunction {
-  template <class T,
-            absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0>
-  ReturnType bar(T&&, const StructB&, StructC&&) &&;  // NOLINT
-};
-
-struct TypeWithBarFunctionAndConvertibleReturnType {
-  template <class T,
-            absl::enable_if_t<std::is_same<T&&, StructA&>::value, int> = 0>
-  ConvertibleToReturnType bar(T&&, const StructB&, StructC&&) &&;  // NOLINT
-};
-
-template <class Class, class... Ts>
-using BarIsCallableImpl =
-    decltype(std::declval<Class>().bar(std::declval<Ts>()...));
-
-template <class Class, class... T>
-using BarIsCallable =
-    absl::type_traits_internal::is_detected<BarIsCallableImpl, Class, T...>;
-
-template <class Class, class... T>
-using BarIsCallableConv = absl::type_traits_internal::is_detected_convertible<
-    ReturnType, BarIsCallableImpl, Class, T...>;
-
-// NOTE: Test of detail type_traits_internal::is_detected.
-TEST(IsDetectedTest, BasicUsage) {
-  EXPECT_TRUE((BarIsCallable<TypeWithBarFunction, StructA&, const StructB&,
-                             StructC>::value));
-  EXPECT_TRUE(
-      (BarIsCallable<TypeWithBarFunction, StructA&, StructB&, StructC>::value));
-  EXPECT_TRUE(
-      (BarIsCallable<TypeWithBarFunction, StructA&, StructB, StructC>::value));
-
-  EXPECT_FALSE((BarIsCallable<int, StructA&, const StructB&, StructC>::value));
-  EXPECT_FALSE((BarIsCallable<TypeWithBarFunction&, StructA&, const StructB&,
-                              StructC>::value));
-  EXPECT_FALSE((BarIsCallable<TypeWithBarFunction, StructA, const StructB&,
-                              StructC>::value));
-}
-
-// NOTE: Test of detail type_traits_internal::is_detected_convertible.
-TEST(IsDetectedConvertibleTest, BasicUsage) {
-  EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunction, StructA&, const StructB&,
-                                 StructC>::value));
-  EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunction, StructA&, StructB&,
-                                 StructC>::value));
-  EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunction, StructA&, StructB,
-                                 StructC>::value));
-  EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
-                                 StructA&, const StructB&, StructC>::value));
-  EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
-                                 StructA&, StructB&, StructC>::value));
-  EXPECT_TRUE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
-                                 StructA&, StructB, StructC>::value));
-
-  EXPECT_FALSE(
-      (BarIsCallableConv<int, StructA&, const StructB&, StructC>::value));
-  EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunction&, StructA&,
-                                  const StructB&, StructC>::value));
-  EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunction, StructA, const StructB&,
-                                  StructC>::value));
-  EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType&,
-                                  StructA&, const StructB&, StructC>::value));
-  EXPECT_FALSE((BarIsCallableConv<TypeWithBarFunctionAndConvertibleReturnType,
-                                  StructA, const StructB&, StructC>::value));
-}
-
-TEST(VoidTTest, BasicUsage) {
-  StaticAssertTypeEq<void, absl::void_t<Dummy>>();
-  StaticAssertTypeEq<void, absl::void_t<Dummy, Dummy, Dummy>>();
-}
-
-TEST(ConjunctionTest, BasicBooleanLogic) {
-  EXPECT_TRUE(absl::conjunction<>::value);
-  EXPECT_TRUE(absl::conjunction<std::true_type>::value);
-  EXPECT_TRUE((absl::conjunction<std::true_type, std::true_type>::value));
-  EXPECT_FALSE((absl::conjunction<std::true_type, std::false_type>::value));
-  EXPECT_FALSE((absl::conjunction<std::false_type, std::true_type>::value));
-  EXPECT_FALSE((absl::conjunction<std::false_type, std::false_type>::value));
-}
-
-struct MyTrueType {
-  static constexpr bool value = true;
-};
-
-struct MyFalseType {
-  static constexpr bool value = false;
-};
-
-TEST(ConjunctionTest, ShortCircuiting) {
-  EXPECT_FALSE(
-      (absl::conjunction<std::true_type, std::false_type, Dummy>::value));
-  EXPECT_TRUE((std::is_base_of<MyFalseType,
-                               absl::conjunction<std::true_type, MyFalseType,
-                                                 std::false_type>>::value));
-  EXPECT_TRUE(
-      (std::is_base_of<MyTrueType,
-                       absl::conjunction<std::true_type, MyTrueType>>::value));
-}
-
-TEST(DisjunctionTest, BasicBooleanLogic) {
-  EXPECT_FALSE(absl::disjunction<>::value);
-  EXPECT_FALSE(absl::disjunction<std::false_type>::value);
-  EXPECT_TRUE((absl::disjunction<std::true_type, std::true_type>::value));
-  EXPECT_TRUE((absl::disjunction<std::true_type, std::false_type>::value));
-  EXPECT_TRUE((absl::disjunction<std::false_type, std::true_type>::value));
-  EXPECT_FALSE((absl::disjunction<std::false_type, std::false_type>::value));
-}
-
-TEST(DisjunctionTest, ShortCircuiting) {
-  EXPECT_TRUE(
-      (absl::disjunction<std::false_type, std::true_type, Dummy>::value));
-  EXPECT_TRUE((
-      std::is_base_of<MyTrueType, absl::disjunction<std::false_type, MyTrueType,
-                                                    std::true_type>>::value));
-  EXPECT_TRUE((
-      std::is_base_of<MyFalseType,
-                      absl::disjunction<std::false_type, MyFalseType>>::value));
-}
-
-TEST(NegationTest, BasicBooleanLogic) {
-  EXPECT_FALSE(absl::negation<std::true_type>::value);
-  EXPECT_FALSE(absl::negation<MyTrueType>::value);
-  EXPECT_TRUE(absl::negation<std::false_type>::value);
-  EXPECT_TRUE(absl::negation<MyFalseType>::value);
-}
-
-// all member functions are trivial
-class Trivial {
-  int n_;
-};
-
-struct TrivialDestructor {
-  ~TrivialDestructor() = default;
-};
-
-struct NontrivialDestructor {
-  ~NontrivialDestructor() {}
-};
-
-struct DeletedDestructor {
-  ~DeletedDestructor() = delete;
-};
-
-class TrivialDefaultCtor {
- public:
-  TrivialDefaultCtor() = default;
-  explicit TrivialDefaultCtor(int n) : n_(n) {}
-
- private:
-  int n_;
-};
-
-class NontrivialDefaultCtor {
- public:
-  NontrivialDefaultCtor() : n_(1) {}
-
- private:
-  int n_;
-};
-
-class DeletedDefaultCtor {
- public:
-  DeletedDefaultCtor() = delete;
-  explicit DeletedDefaultCtor(int n) : n_(n) {}
-
- private:
-  int n_;
-};
-
-class TrivialMoveCtor {
- public:
-  explicit TrivialMoveCtor(int n) : n_(n) {}
-  TrivialMoveCtor(TrivialMoveCtor&&) = default;
-  TrivialMoveCtor& operator=(const TrivialMoveCtor& t) {
-    n_ = t.n_;
-    return *this;
-  }
-
- private:
-  int n_;
-};
-
-class NontrivialMoveCtor {
- public:
-  explicit NontrivialMoveCtor(int n) : n_(n) {}
-  NontrivialMoveCtor(NontrivialMoveCtor&& t) noexcept : n_(t.n_) {}
-  NontrivialMoveCtor& operator=(const NontrivialMoveCtor&) = default;
-
- private:
-  int n_;
-};
-
-class TrivialCopyCtor {
- public:
-  explicit TrivialCopyCtor(int n) : n_(n) {}
-  TrivialCopyCtor(const TrivialCopyCtor&) = default;
-  TrivialCopyCtor& operator=(const TrivialCopyCtor& t) {
-    n_ = t.n_;
-    return *this;
-  }
-
- private:
-  int n_;
-};
-
-class NontrivialCopyCtor {
- public:
-  explicit NontrivialCopyCtor(int n) : n_(n) {}
-  NontrivialCopyCtor(const NontrivialCopyCtor& t) : n_(t.n_) {}
-  NontrivialCopyCtor& operator=(const NontrivialCopyCtor&) = default;
-
- private:
-  int n_;
-};
-
-class DeletedCopyCtor {
- public:
-  explicit DeletedCopyCtor(int n) : n_(n) {}
-  DeletedCopyCtor(const DeletedCopyCtor&) = delete;
-  DeletedCopyCtor& operator=(const DeletedCopyCtor&) = default;
-
- private:
-  int n_;
-};
-
-class TrivialMoveAssign {
- public:
-  explicit TrivialMoveAssign(int n) : n_(n) {}
-  TrivialMoveAssign(const TrivialMoveAssign& t) : n_(t.n_) {}
-  TrivialMoveAssign& operator=(TrivialMoveAssign&&) = default;
-  ~TrivialMoveAssign() {}  // can have nontrivial destructor
- private:
-  int n_;
-};
-
-class NontrivialMoveAssign {
- public:
-  explicit NontrivialMoveAssign(int n) : n_(n) {}
-  NontrivialMoveAssign(const NontrivialMoveAssign&) = default;
-  NontrivialMoveAssign& operator=(NontrivialMoveAssign&& t) noexcept {
-    n_ = t.n_;
-    return *this;
-  }
-
- private:
-  int n_;
-};
-
-class TrivialCopyAssign {
- public:
-  explicit TrivialCopyAssign(int n) : n_(n) {}
-  TrivialCopyAssign(const TrivialCopyAssign& t) : n_(t.n_) {}
-  TrivialCopyAssign& operator=(const TrivialCopyAssign& t) = default;
-  ~TrivialCopyAssign() {}  // can have nontrivial destructor
- private:
-  int n_;
-};
-
-class NontrivialCopyAssign {
- public:
-  explicit NontrivialCopyAssign(int n) : n_(n) {}
-  NontrivialCopyAssign(const NontrivialCopyAssign&) = default;
-  NontrivialCopyAssign& operator=(const NontrivialCopyAssign& t) {
-    n_ = t.n_;
-    return *this;
-  }
-
- private:
-  int n_;
-};
-
-class DeletedCopyAssign {
- public:
-  explicit DeletedCopyAssign(int n) : n_(n) {}
-  DeletedCopyAssign(const DeletedCopyAssign&) = default;
-  DeletedCopyAssign& operator=(const DeletedCopyAssign&) = delete;
-
- private:
-  int n_;
-};
-
-struct MovableNonCopyable {
-  MovableNonCopyable() = default;
-  MovableNonCopyable(const MovableNonCopyable&) = delete;
-  MovableNonCopyable(MovableNonCopyable&&) = default;
-  MovableNonCopyable& operator=(const MovableNonCopyable&) = delete;
-  MovableNonCopyable& operator=(MovableNonCopyable&&) = default;
-};
-
-struct NonCopyableOrMovable {
-  NonCopyableOrMovable() = default;
-  NonCopyableOrMovable(const NonCopyableOrMovable&) = delete;
-  NonCopyableOrMovable(NonCopyableOrMovable&&) = delete;
-  NonCopyableOrMovable& operator=(const NonCopyableOrMovable&) = delete;
-  NonCopyableOrMovable& operator=(NonCopyableOrMovable&&) = delete;
-};
-
-class Base {
- public:
-  virtual ~Base() {}
-};
-
-// Old versions of libc++, around Clang 3.5 to 3.6, consider deleted destructors
-// as also being trivial. With the resolution of CWG 1928 and CWG 1734, this
-// is no longer considered true and has thus been amended.
-// Compiler Explorer: https://godbolt.org/g/zT59ZL
-// CWG issue 1734: http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1734
-// CWG issue 1928: http://open-std.org/JTC1/SC22/WG21/docs/cwg_closed.html#1928
-#if !defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 3700
-#define ABSL_TRIVIALLY_DESTRUCTIBLE_CONSIDER_DELETED_DESTRUCTOR_NOT_TRIVIAL 1
-#endif
-
-// As of the moment, GCC versions >5.1 have a problem compiling for
-// std::is_trivially_default_constructible<NontrivialDestructor[10]>, where
-// NontrivialDestructor is a struct with a custom nontrivial destructor. Note
-// that this problem only occurs for arrays of a known size, so something like
-// std::is_trivially_default_constructible<NontrivialDestructor[]> does not
-// have any problems.
-// Compiler Explorer: https://godbolt.org/g/dXRbdK
-// GCC bug 83689: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83689
-#if defined(__clang__) || defined(_MSC_VER) || \
-    (defined(__GNUC__) && __GNUC__ < 5)
-#define ABSL_GCC_BUG_TRIVIALLY_CONSTRUCTIBLE_ON_ARRAY_OF_NONTRIVIAL 1
-#endif
-
-TEST(TypeTraitsTest, TestIsFunction) {
-  struct Callable {
-    void operator()() {}
-  };
-  EXPECT_TRUE(absl::is_function<void()>::value);
-  EXPECT_TRUE(absl::is_function<void()&>::value);
-  EXPECT_TRUE(absl::is_function<void() const>::value);
-  EXPECT_TRUE(absl::is_function<void() noexcept>::value);
-  EXPECT_TRUE(absl::is_function<void(...) noexcept>::value);
-
-  EXPECT_FALSE(absl::is_function<void(*)()>::value);
-  EXPECT_FALSE(absl::is_function<void(&)()>::value);
-  EXPECT_FALSE(absl::is_function<int>::value);
-  EXPECT_FALSE(absl::is_function<Callable>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialDestructor) {
-  // Verify that arithmetic types and pointers have trivial destructors.
-  EXPECT_TRUE(absl::is_trivially_destructible<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<char>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<int>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<float>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<double>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<Trivial**>::value);
-
-  // classes with destructors
-  EXPECT_TRUE(absl::is_trivially_destructible<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<TrivialDestructor>::value);
-
-  // Verify that types with a nontrivial or deleted destructor
-  // are marked as such.
-  EXPECT_FALSE(absl::is_trivially_destructible<NontrivialDestructor>::value);
-#ifdef ABSL_TRIVIALLY_DESTRUCTIBLE_CONSIDER_DELETED_DESTRUCTOR_NOT_TRIVIAL
-  EXPECT_FALSE(absl::is_trivially_destructible<DeletedDestructor>::value);
-#endif
-
-  // simple_pair of such types is trivial
-  EXPECT_TRUE((absl::is_trivially_destructible<simple_pair<int, int>>::value));
-  EXPECT_TRUE((absl::is_trivially_destructible<
-               simple_pair<Trivial, TrivialDestructor>>::value));
-
-  // Verify that types without trivial destructors are correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_destructible<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_destructible<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types without trivial destructors
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_destructible<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_destructible<
-                simple_pair<std::string, int>>::value));
-
-  // array of such types is trivial
-  using int10 = int[10];
-  EXPECT_TRUE(absl::is_trivially_destructible<int10>::value);
-  using Trivial10 = Trivial[10];
-  EXPECT_TRUE(absl::is_trivially_destructible<Trivial10>::value);
-  using TrivialDestructor10 = TrivialDestructor[10];
-  EXPECT_TRUE(absl::is_trivially_destructible<TrivialDestructor10>::value);
-
-  // Conversely, the opposite also holds.
-  using NontrivialDestructor10 = NontrivialDestructor[10];
-  EXPECT_FALSE(absl::is_trivially_destructible<NontrivialDestructor10>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialDefaultCtor) {
-  // arithmetic types and pointers have trivial default constructors.
-  EXPECT_TRUE(absl::is_trivially_default_constructible<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<char>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<int>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<float>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<double>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<Trivial*>::value);
-  EXPECT_TRUE(
-      absl::is_trivially_default_constructible<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<Trivial**>::value);
-
-  // types with compiler generated default ctors
-  EXPECT_TRUE(absl::is_trivially_default_constructible<Trivial>::value);
-  EXPECT_TRUE(
-      absl::is_trivially_default_constructible<TrivialDefaultCtor>::value);
-
-  // Verify that types without them are not.
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<NontrivialDefaultCtor>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<DeletedDefaultCtor>::value);
-
-  // types with nontrivial destructor are nontrivial
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<NontrivialDestructor>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_default_constructible<Base>::value);
-
-  // Verify that simple_pair has trivial constructors where applicable.
-  EXPECT_TRUE((absl::is_trivially_default_constructible<
-               simple_pair<int, char*>>::value));
-  EXPECT_TRUE((absl::is_trivially_default_constructible<
-               simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_default_constructible<
-               simple_pair<int, TrivialDefaultCtor>>::value));
-
-  // Verify that types without trivial constructors are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_default_constructible<std::string>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types without trivial constructors
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_default_constructible<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_default_constructible<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays of such types are trivially default constructible
-  using int10 = int[10];
-  EXPECT_TRUE(absl::is_trivially_default_constructible<int10>::value);
-  using Trivial10 = Trivial[10];
-  EXPECT_TRUE(absl::is_trivially_default_constructible<Trivial10>::value);
-  using TrivialDefaultCtor10 = TrivialDefaultCtor[10];
-  EXPECT_TRUE(
-      absl::is_trivially_default_constructible<TrivialDefaultCtor10>::value);
-
-  // Conversely, the opposite also holds.
-#ifdef ABSL_GCC_BUG_TRIVIALLY_CONSTRUCTIBLE_ON_ARRAY_OF_NONTRIVIAL
-  using NontrivialDefaultCtor10 = NontrivialDefaultCtor[10];
-  EXPECT_FALSE(
-      absl::is_trivially_default_constructible<NontrivialDefaultCtor10>::value);
-#endif
-}
-
-// GCC prior to 7.4 had a bug in its trivially-constructible traits
-// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80654).
-// This test makes sure that we do not depend on the trait in these cases when
-// implementing absl triviality traits.
-
-template <class T>
-struct BadConstructors {
-  BadConstructors() { static_assert(T::value, ""); }
-
-  BadConstructors(BadConstructors&&) { static_assert(T::value, ""); }
-
-  BadConstructors(const BadConstructors&) { static_assert(T::value, ""); }
-};
-
-TEST(TypeTraitsTest, TestTrivialityBadConstructors) {
-  using BadType = BadConstructors<int>;
-
-  EXPECT_FALSE(absl::is_trivially_default_constructible<BadType>::value);
-  EXPECT_FALSE(absl::is_trivially_move_constructible<BadType>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<BadType>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialMoveCtor) {
-  // Verify that arithmetic types and pointers have trivial move
-  // constructors.
-  EXPECT_TRUE(absl::is_trivially_move_constructible<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<float>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<double>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial**>::value);
-
-  // Reference types
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int&>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<int&&>::value);
-
-  // types with compiler generated move ctors
-  EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<TrivialMoveCtor>::value);
-
-  // Verify that types without them (i.e. nontrivial or deleted) are not.
-  EXPECT_FALSE(
-      absl::is_trivially_move_constructible<NontrivialCopyCtor>::value);
-  EXPECT_FALSE(absl::is_trivially_move_constructible<DeletedCopyCtor>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_move_constructible<NonCopyableOrMovable>::value);
-
-  // type with nontrivial destructor are nontrivial move construbtible
-  EXPECT_FALSE(
-      absl::is_trivially_move_constructible<NontrivialDestructor>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_move_constructible<Base>::value);
-
-  // Verify that simple_pair of such types is trivially move constructible
-  EXPECT_TRUE(
-      (absl::is_trivially_move_constructible<simple_pair<int, char*>>::value));
-  EXPECT_TRUE((
-      absl::is_trivially_move_constructible<simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_move_constructible<
-               simple_pair<int, TrivialMoveCtor>>::value));
-
-  // Verify that types without trivial move constructors are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_move_constructible<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_move_constructible<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types without trivial move constructors
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_move_constructible<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_move_constructible<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays are not
-  using int10 = int[10];
-  EXPECT_FALSE(absl::is_trivially_move_constructible<int10>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialCopyCtor) {
-  // Verify that arithmetic types and pointers have trivial copy
-  // constructors.
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<int>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<float>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<double>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial**>::value);
-
-  // Reference types
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<int&>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<int&&>::value);
-
-  // types with compiler generated copy ctors
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<TrivialCopyCtor>::value);
-
-  // Verify that types without them (i.e. nontrivial or deleted) are not.
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<NontrivialCopyCtor>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<DeletedCopyCtor>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<MovableNonCopyable>::value);
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<NonCopyableOrMovable>::value);
-
-  // type with nontrivial destructor are nontrivial copy construbtible
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<NontrivialDestructor>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<Base>::value);
-
-  // Verify that simple_pair of such types is trivially copy constructible
-  EXPECT_TRUE(
-      (absl::is_trivially_copy_constructible<simple_pair<int, char*>>::value));
-  EXPECT_TRUE((
-      absl::is_trivially_copy_constructible<simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_copy_constructible<
-               simple_pair<int, TrivialCopyCtor>>::value));
-
-  // Verify that types without trivial copy constructors are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types without trivial copy constructors
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_copy_constructible<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_copy_constructible<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays are not
-  using int10 = int[10];
-  EXPECT_FALSE(absl::is_trivially_copy_constructible<int10>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialMoveAssign) {
-  // Verify that arithmetic types and pointers have trivial move
-  // assignment operators.
-  EXPECT_TRUE(absl::is_trivially_move_assignable<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<int>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<float>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<double>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial**>::value);
-
-  // const qualified types are not assignable
-  EXPECT_FALSE(absl::is_trivially_move_assignable<const int>::value);
-
-  // types with compiler generated move assignment
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<TrivialMoveAssign>::value);
-
-  // Verify that types without them (i.e. nontrivial or deleted) are not.
-  EXPECT_FALSE(absl::is_trivially_move_assignable<NontrivialCopyAssign>::value);
-  EXPECT_FALSE(absl::is_trivially_move_assignable<DeletedCopyAssign>::value);
-  EXPECT_FALSE(absl::is_trivially_move_assignable<NonCopyableOrMovable>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_move_assignable<Base>::value);
-
-  // Verify that simple_pair is trivially assignable
-  EXPECT_TRUE(
-      (absl::is_trivially_move_assignable<simple_pair<int, char*>>::value));
-  EXPECT_TRUE(
-      (absl::is_trivially_move_assignable<simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_move_assignable<
-               simple_pair<int, TrivialMoveAssign>>::value));
-
-  // Verify that types not trivially move assignable are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_move_assignable<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_move_assignable<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types not trivially move assignable
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_move_assignable<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_move_assignable<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays are not trivially move assignable
-  using int10 = int[10];
-  EXPECT_FALSE(absl::is_trivially_move_assignable<int10>::value);
-
-  // Verify that references are handled correctly
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial&&>::value);
-  EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial&>::value);
-}
-
-TEST(TypeTraitsTest, TestTrivialCopyAssign) {
-  // Verify that arithmetic types and pointers have trivial copy
-  // assignment operators.
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<bool>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<unsigned char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<signed char>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<wchar_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<int>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<unsigned int>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<int16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<uint16_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<int64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<uint64_t>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<float>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<double>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<long double>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<const std::string*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<const Trivial*>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<std::string**>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial**>::value);
-
-  // const qualified types are not assignable
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<const int>::value);
-
-  // types with compiler generated copy assignment
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<TrivialCopyAssign>::value);
-
-  // Verify that types without them (i.e. nontrivial or deleted) are not.
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<NontrivialCopyAssign>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<DeletedCopyAssign>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<MovableNonCopyable>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<NonCopyableOrMovable>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<Base>::value);
-
-  // Verify that simple_pair is trivially assignable
-  EXPECT_TRUE(
-      (absl::is_trivially_copy_assignable<simple_pair<int, char*>>::value));
-  EXPECT_TRUE(
-      (absl::is_trivially_copy_assignable<simple_pair<int, Trivial>>::value));
-  EXPECT_TRUE((absl::is_trivially_copy_assignable<
-               simple_pair<int, TrivialCopyAssign>>::value));
-
-  // Verify that types not trivially copy assignable are
-  // correctly marked as such.
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<std::string>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<std::vector<int>>::value);
-
-  // Verify that simple_pairs of types not trivially copy assignable
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::is_trivially_copy_assignable<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::is_trivially_copy_assignable<
-                simple_pair<std::string, int>>::value));
-
-  // Verify that arrays are not trivially copy assignable
-  using int10 = int[10];
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<int10>::value);
-
-  // Verify that references are handled correctly
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial&&>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial&>::value);
-}
-
-TEST(TypeTraitsTest, TestTriviallyCopyable) {
-  // Verify that arithmetic types and pointers are trivially copyable.
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<bool>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<char>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<unsigned char>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<signed char>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<wchar_t>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<int>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<unsigned int>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<int16_t>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<uint16_t>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<int64_t>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<uint64_t>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<float>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<double>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<long double>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<std::string*>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<Trivial*>::value);
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<
-              const std::string*>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<const Trivial*>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<std::string**>::value);
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<Trivial**>::value);
-
-  // const qualified types are not assignable but are constructible
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<const int>::value);
-
-  // Trivial copy constructor/assignment and destructor.
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<Trivial>::value);
-  // Trivial copy assignment, but non-trivial copy constructor/destructor.
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               TrivialCopyAssign>::value);
-  // Trivial copy constructor, but non-trivial assignment.
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               TrivialCopyCtor>::value);
-
-  // Types with a non-trivial copy constructor/assignment
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               NontrivialCopyCtor>::value);
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               NontrivialCopyAssign>::value);
-
-  // Types without copy constructor/assignment, but with move
-  // MSVC disagrees with other compilers about this:
-  // EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<
-  //             MovableNonCopyable>::value);
-
-  // Types without copy/move constructor/assignment
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               NonCopyableOrMovable>::value);
-
-  // No copy assign, but has trivial copy constructor.
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<
-              DeletedCopyAssign>::value);
-
-  // types with vtables
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<Base>::value);
-
-  // Verify that simple_pair is trivially copyable if members are
-  EXPECT_TRUE((absl::type_traits_internal::is_trivially_copyable<
-               simple_pair<int, char*>>::value));
-  EXPECT_TRUE((absl::type_traits_internal::is_trivially_copyable<
-               simple_pair<int, Trivial>>::value));
-
-  // Verify that types not trivially copyable are
-  // correctly marked as such.
-  EXPECT_FALSE(
-      absl::type_traits_internal::is_trivially_copyable<std::string>::value);
-  EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<
-               std::vector<int>>::value);
-
-  // Verify that simple_pairs of types not trivially copyable
-  // are not marked as trivial.
-  EXPECT_FALSE((absl::type_traits_internal::is_trivially_copyable<
-                simple_pair<int, std::string>>::value));
-  EXPECT_FALSE((absl::type_traits_internal::is_trivially_copyable<
-                simple_pair<std::string, int>>::value));
-  EXPECT_FALSE((absl::type_traits_internal::is_trivially_copyable<
-                simple_pair<int, TrivialCopyAssign>>::value));
-
-  // Verify that arrays of trivially copyable types are trivially copyable
-  using int10 = int[10];
-  EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<int10>::value);
-  using int10x10 = int[10][10];
-  EXPECT_TRUE(
-      absl::type_traits_internal::is_trivially_copyable<int10x10>::value);
-
-  // Verify that references are handled correctly
-  EXPECT_FALSE(
-      absl::type_traits_internal::is_trivially_copyable<Trivial&&>::value);
-  EXPECT_FALSE(
-      absl::type_traits_internal::is_trivially_copyable<Trivial&>::value);
-}
-
-#define ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(trait_name, ...)          \
-  EXPECT_TRUE((std::is_same<typename std::trait_name<__VA_ARGS__>::type, \
-                            absl::trait_name##_t<__VA_ARGS__>>::value))
-
-TEST(TypeTraitsTest, TestRemoveCVAliases) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_cv, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_cv, const int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_cv, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_cv, const volatile int);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_const, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_const, const int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_const, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_const, const volatile int);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_volatile, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_volatile, const int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_volatile, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_volatile, const volatile int);
-}
-
-TEST(TypeTraitsTest, TestAddCVAliases) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_cv, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_cv, const int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_cv, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_cv, const volatile int);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_const, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_const, const int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_const, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_const, const volatile int);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_volatile, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_volatile, const int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_volatile, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_volatile, const volatile int);
-}
-
-TEST(TypeTraitsTest, TestReferenceAliases) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, volatile int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, int&&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_reference, volatile int&&);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, volatile int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, int&&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_lvalue_reference, volatile int&&);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, volatile int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, int&&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_rvalue_reference, volatile int&&);
-}
-
-TEST(TypeTraitsTest, TestPointerAliases) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_pointer, int*);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_pointer, volatile int*);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_pointer, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(add_pointer, volatile int);
-}
-
-TEST(TypeTraitsTest, TestSignednessAliases) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_signed, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_signed, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_signed, unsigned);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_signed, volatile unsigned);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_unsigned, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_unsigned, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_unsigned, unsigned);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(make_unsigned, volatile unsigned);
-}
-
-TEST(TypeTraitsTest, TestExtentAliases) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_extent, int[]);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_extent, int[1]);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_extent, int[1][1]);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_extent, int[][1]);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_all_extents, int[]);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_all_extents, int[1]);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_all_extents, int[1][1]);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(remove_all_extents, int[][1]);
-}
-
-TEST(TypeTraitsTest, TestAlignedStorageAlias) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 1);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 2);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 3);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 4);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 5);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 6);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 7);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 8);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 9);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 10);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 11);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 12);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 13);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 14);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 15);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 16);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 17);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 18);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 19);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 20);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 21);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 22);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 23);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 24);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 25);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 26);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 27);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 28);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 29);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 30);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 31);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 32);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 33);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 1, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 2, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 3, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 4, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 5, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 6, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 7, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 8, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 9, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 10, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 11, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 12, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 13, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 14, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 15, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 16, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 17, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 18, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 19, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 20, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 21, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 22, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 23, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 24, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 25, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 26, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 27, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 28, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 29, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 30, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 31, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 32, 128);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(aligned_storage, 33, 128);
-}
-
-TEST(TypeTraitsTest, TestDecay) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, volatile int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const volatile int);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, volatile int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const volatile int&);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, volatile int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, const volatile int&);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int[1]);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int[1][1]);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int[][1]);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int());
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(float));  // NOLINT
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(char, ...));  // NOLINT
-}
-
-struct TypeA {};
-struct TypeB {};
-struct TypeC {};
-struct TypeD {};
-
-template <typename T>
-struct Wrap {};
-
-enum class TypeEnum { A, B, C, D };
-
-struct GetTypeT {
-  template <typename T,
-            absl::enable_if_t<std::is_same<T, TypeA>::value, int> = 0>
-  TypeEnum operator()(Wrap<T>) const {
-    return TypeEnum::A;
-  }
-
-  template <typename T,
-            absl::enable_if_t<std::is_same<T, TypeB>::value, int> = 0>
-  TypeEnum operator()(Wrap<T>) const {
-    return TypeEnum::B;
-  }
-
-  template <typename T,
-            absl::enable_if_t<std::is_same<T, TypeC>::value, int> = 0>
-  TypeEnum operator()(Wrap<T>) const {
-    return TypeEnum::C;
-  }
-
-  // NOTE: TypeD is intentionally not handled
-} constexpr GetType = {};
-
-TEST(TypeTraitsTest, TestEnableIf) {
-  EXPECT_EQ(TypeEnum::A, GetType(Wrap<TypeA>()));
-  EXPECT_EQ(TypeEnum::B, GetType(Wrap<TypeB>()));
-  EXPECT_EQ(TypeEnum::C, GetType(Wrap<TypeC>()));
-}
-
-TEST(TypeTraitsTest, TestConditional) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(conditional, true, int, char);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(conditional, false, int, char);
-}
-
-// TODO(calabrese) Check with specialized std::common_type
-TEST(TypeTraitsTest, TestCommonType) {
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int, char);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int, char, int);
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int, char&);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(common_type, int, char, int&);
-}
-
-TEST(TypeTraitsTest, TestUnderlyingType) {
-  enum class enum_char : char {};
-  enum class enum_long_long : long long {};  // NOLINT(runtime/int)
-
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(underlying_type, enum_char);
-  ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(underlying_type, enum_long_long);
-}
-
-struct GetTypeExtT {
-  template <typename T>
-  absl::result_of_t<const GetTypeT&(T)> operator()(T&& arg) const {
-    return GetType(std::forward<T>(arg));
-  }
-
-  TypeEnum operator()(Wrap<TypeD>) const { return TypeEnum::D; }
-} constexpr GetTypeExt = {};
-
-TEST(TypeTraitsTest, TestResultOf) {
-  EXPECT_EQ(TypeEnum::A, GetTypeExt(Wrap<TypeA>()));
-  EXPECT_EQ(TypeEnum::B, GetTypeExt(Wrap<TypeB>()));
-  EXPECT_EQ(TypeEnum::C, GetTypeExt(Wrap<TypeC>()));
-  EXPECT_EQ(TypeEnum::D, GetTypeExt(Wrap<TypeD>()));
-}
-
-template <typename T>
-bool TestCopyAssign() {
-  return absl::is_copy_assignable<T>::value ==
-         std::is_copy_assignable<T>::value;
-}
-
-TEST(TypeTraitsTest, IsCopyAssignable) {
-  EXPECT_TRUE(TestCopyAssign<int>());
-  EXPECT_TRUE(TestCopyAssign<int&>());
-  EXPECT_TRUE(TestCopyAssign<int&&>());
-
-  struct S {};
-  EXPECT_TRUE(TestCopyAssign<S>());
-  EXPECT_TRUE(TestCopyAssign<S&>());
-  EXPECT_TRUE(TestCopyAssign<S&&>());
-
-  class C {
-   public:
-    explicit C(C* c) : c_(c) {}
-    ~C() { delete c_; }
-
-   private:
-    C* c_;
-  };
-  EXPECT_TRUE(TestCopyAssign<C>());
-  EXPECT_TRUE(TestCopyAssign<C&>());
-  EXPECT_TRUE(TestCopyAssign<C&&>());
-
-  // Reason for ifndef: add_lvalue_reference<T> in libc++ breaks for these cases
-#ifndef _LIBCPP_VERSION
-  EXPECT_TRUE(TestCopyAssign<int()>());
-  EXPECT_TRUE(TestCopyAssign<int(int) const>());
-  EXPECT_TRUE(TestCopyAssign<int(...) volatile&>());
-  EXPECT_TRUE(TestCopyAssign<int(int, ...) const volatile&&>());
-#endif  // _LIBCPP_VERSION
-}
-
-template <typename T>
-bool TestMoveAssign() {
-  return absl::is_move_assignable<T>::value ==
-         std::is_move_assignable<T>::value;
-}
-
-TEST(TypeTraitsTest, IsMoveAssignable) {
-  EXPECT_TRUE(TestMoveAssign<int>());
-  EXPECT_TRUE(TestMoveAssign<int&>());
-  EXPECT_TRUE(TestMoveAssign<int&&>());
-
-  struct S {};
-  EXPECT_TRUE(TestMoveAssign<S>());
-  EXPECT_TRUE(TestMoveAssign<S&>());
-  EXPECT_TRUE(TestMoveAssign<S&&>());
-
-  class C {
-   public:
-    explicit C(C* c) : c_(c) {}
-    ~C() { delete c_; }
-    void operator=(const C&) = delete;
-    void operator=(C&&) = delete;
-
-   private:
-    C* c_;
-  };
-  EXPECT_TRUE(TestMoveAssign<C>());
-  EXPECT_TRUE(TestMoveAssign<C&>());
-  EXPECT_TRUE(TestMoveAssign<C&&>());
-
-  // Reason for ifndef: add_lvalue_reference<T> in libc++ breaks for these cases
-#ifndef _LIBCPP_VERSION
-  EXPECT_TRUE(TestMoveAssign<int()>());
-  EXPECT_TRUE(TestMoveAssign<int(int) const>());
-  EXPECT_TRUE(TestMoveAssign<int(...) volatile&>());
-  EXPECT_TRUE(TestMoveAssign<int(int, ...) const volatile&&>());
-#endif  // _LIBCPP_VERSION
-}
-
-namespace adl_namespace {
-
-struct DeletedSwap {
-};
-
-void swap(DeletedSwap&, DeletedSwap&) = delete;
-
-struct SpecialNoexceptSwap {
-  SpecialNoexceptSwap(SpecialNoexceptSwap&&) {}
-  SpecialNoexceptSwap& operator=(SpecialNoexceptSwap&&) { return *this; }
-  ~SpecialNoexceptSwap() = default;
-};
-
-void swap(SpecialNoexceptSwap&, SpecialNoexceptSwap&) noexcept {}
-
-}  // namespace adl_namespace
-
-TEST(TypeTraitsTest, IsSwappable) {
-  using absl::type_traits_internal::IsSwappable;
-  using absl::type_traits_internal::StdSwapIsUnconstrained;
-
-  EXPECT_TRUE(IsSwappable<int>::value);
-
-  struct S {};
-  EXPECT_TRUE(IsSwappable<S>::value);
-
-  struct NoConstruct {
-    NoConstruct(NoConstruct&&) = delete;
-    NoConstruct& operator=(NoConstruct&&) { return *this; }
-    ~NoConstruct() = default;
-  };
-
-  EXPECT_EQ(IsSwappable<NoConstruct>::value, StdSwapIsUnconstrained::value);
-  struct NoAssign {
-    NoAssign(NoAssign&&) {}
-    NoAssign& operator=(NoAssign&&) = delete;
-    ~NoAssign() = default;
-  };
-
-  EXPECT_EQ(IsSwappable<NoAssign>::value, StdSwapIsUnconstrained::value);
-
-  EXPECT_FALSE(IsSwappable<adl_namespace::DeletedSwap>::value);
-
-  EXPECT_TRUE(IsSwappable<adl_namespace::SpecialNoexceptSwap>::value);
-}
-
-TEST(TypeTraitsTest, IsNothrowSwappable) {
-  using absl::type_traits_internal::IsNothrowSwappable;
-  using absl::type_traits_internal::StdSwapIsUnconstrained;
-
-  EXPECT_TRUE(IsNothrowSwappable<int>::value);
-
-  struct NonNoexceptMoves {
-    NonNoexceptMoves(NonNoexceptMoves&&) {}
-    NonNoexceptMoves& operator=(NonNoexceptMoves&&) { return *this; }
-    ~NonNoexceptMoves() = default;
-  };
-
-  EXPECT_FALSE(IsNothrowSwappable<NonNoexceptMoves>::value);
-
-  struct NoConstruct {
-    NoConstruct(NoConstruct&&) = delete;
-    NoConstruct& operator=(NoConstruct&&) { return *this; }
-    ~NoConstruct() = default;
-  };
-
-  EXPECT_FALSE(IsNothrowSwappable<NoConstruct>::value);
-
-  struct NoAssign {
-    NoAssign(NoAssign&&) {}
-    NoAssign& operator=(NoAssign&&) = delete;
-    ~NoAssign() = default;
-  };
-
-  EXPECT_FALSE(IsNothrowSwappable<NoAssign>::value);
-
-  EXPECT_FALSE(IsNothrowSwappable<adl_namespace::DeletedSwap>::value);
-
-  EXPECT_TRUE(IsNothrowSwappable<adl_namespace::SpecialNoexceptSwap>::value);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/numeric/BUILD.bazel b/third_party/abseil_cpp/absl/numeric/BUILD.bazel
deleted file mode 100644
index f808f5dab0..0000000000
--- a/third_party/abseil_cpp/absl/numeric/BUILD.bazel
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright 2018 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "int128",
-    srcs = [
-        "int128.cc",
-        "int128_have_intrinsic.inc",
-        "int128_no_intrinsic.inc",
-    ],
-    hdrs = ["int128.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:bits",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-    ],
-)
-
-cc_test(
-    name = "int128_test",
-    size = "small",
-    srcs = [
-        "int128_stream_test.cc",
-        "int128_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":int128",
-        "//absl/base",
-        "//absl/base:core_headers",
-        "//absl/hash:hash_testing",
-        "//absl/meta:type_traits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "int128_benchmark",
-    srcs = ["int128_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    deps = [
-        ":int128",
-        "//absl/base:config",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/numeric/CMakeLists.txt b/third_party/abseil_cpp/absl/numeric/CMakeLists.txt
deleted file mode 100644
index 1e12d80f7c..0000000000
--- a/third_party/abseil_cpp/absl/numeric/CMakeLists.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    int128
-  HDRS
-    "int128.h"
-  SRCS
-    "int128.cc"
-    "int128_have_intrinsic.inc"
-    "int128_no_intrinsic.inc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::bits
-    absl::config
-    absl::core_headers
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    int128_test
-  SRCS
-    "int128_stream_test.cc"
-    "int128_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::int128
-    absl::base
-    absl::core_headers
-    absl::hash_testing
-    absl::type_traits
-    gmock_main
-)
-
-# component target
-absl_cc_library(
-  NAME
-    numeric
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::int128
-  PUBLIC
-)
diff --git a/third_party/abseil_cpp/absl/numeric/int128.cc b/third_party/abseil_cpp/absl/numeric/int128.cc
deleted file mode 100644
index e21e5e9a4a..0000000000
--- a/third_party/abseil_cpp/absl/numeric/int128.cc
+++ /dev/null
@@ -1,390 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/numeric/int128.h"
-
-#include <stddef.h>
-
-#include <cassert>
-#include <iomanip>
-#include <ostream>  // NOLINT(readability/streams)
-#include <sstream>
-#include <string>
-#include <type_traits>
-
-#include "absl/base/internal/bits.h"
-#include "absl/base/optimization.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-ABSL_DLL const uint128 kuint128max = MakeUint128(
-    std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max());
-
-namespace {
-
-// Returns the 0-based position of the last set bit (i.e., most significant bit)
-// in the given uint128. The argument is not 0.
-//
-// For example:
-//   Given: 5 (decimal) == 101 (binary)
-//   Returns: 2
-inline ABSL_ATTRIBUTE_ALWAYS_INLINE int Fls128(uint128 n) {
-  if (uint64_t hi = Uint128High64(n)) {
-    ABSL_INTERNAL_ASSUME(hi != 0);
-    return 127 - base_internal::CountLeadingZeros64(hi);
-  }
-  const uint64_t low = Uint128Low64(n);
-  ABSL_INTERNAL_ASSUME(low != 0);
-  return 63 - base_internal::CountLeadingZeros64(low);
-}
-
-// Long division/modulo for uint128 implemented using the shift-subtract
-// division algorithm adapted from:
-// https://stackoverflow.com/questions/5386377/division-without-using
-inline void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret,
-                       uint128* remainder_ret) {
-  assert(divisor != 0);
-
-  if (divisor > dividend) {
-    *quotient_ret = 0;
-    *remainder_ret = dividend;
-    return;
-  }
-
-  if (divisor == dividend) {
-    *quotient_ret = 1;
-    *remainder_ret = 0;
-    return;
-  }
-
-  uint128 denominator = divisor;
-  uint128 quotient = 0;
-
-  // Left aligns the MSB of the denominator and the dividend.
-  const int shift = Fls128(dividend) - Fls128(denominator);
-  denominator <<= shift;
-
-  // Uses shift-subtract algorithm to divide dividend by denominator. The
-  // remainder will be left in dividend.
-  for (int i = 0; i <= shift; ++i) {
-    quotient <<= 1;
-    if (dividend >= denominator) {
-      dividend -= denominator;
-      quotient |= 1;
-    }
-    denominator >>= 1;
-  }
-
-  *quotient_ret = quotient;
-  *remainder_ret = dividend;
-}
-
-template <typename T>
-uint128 MakeUint128FromFloat(T v) {
-  static_assert(std::is_floating_point<T>::value, "");
-
-  // Rounding behavior is towards zero, same as for built-in types.
-
-  // Undefined behavior if v is NaN or cannot fit into uint128.
-  assert(std::isfinite(v) && v > -1 &&
-         (std::numeric_limits<T>::max_exponent <= 128 ||
-          v < std::ldexp(static_cast<T>(1), 128)));
-
-  if (v >= std::ldexp(static_cast<T>(1), 64)) {
-    uint64_t hi = static_cast<uint64_t>(std::ldexp(v, -64));
-    uint64_t lo = static_cast<uint64_t>(v - std::ldexp(static_cast<T>(hi), 64));
-    return MakeUint128(hi, lo);
-  }
-
-  return MakeUint128(0, static_cast<uint64_t>(v));
-}
-
-#if defined(__clang__) && !defined(__SSE3__)
-// Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289
-// Casting from long double to uint64_t is miscompiled and drops bits.
-// It is more work, so only use when we need the workaround.
-uint128 MakeUint128FromFloat(long double v) {
-  // Go 50 bits at a time, that fits in a double
-  static_assert(std::numeric_limits<double>::digits >= 50, "");
-  static_assert(std::numeric_limits<long double>::digits <= 150, "");
-  // Undefined behavior if v is not finite or cannot fit into uint128.
-  assert(std::isfinite(v) && v > -1 && v < std::ldexp(1.0L, 128));
-
-  v = std::ldexp(v, -100);
-  uint64_t w0 = static_cast<uint64_t>(static_cast<double>(std::trunc(v)));
-  v = std::ldexp(v - static_cast<double>(w0), 50);
-  uint64_t w1 = static_cast<uint64_t>(static_cast<double>(std::trunc(v)));
-  v = std::ldexp(v - static_cast<double>(w1), 50);
-  uint64_t w2 = static_cast<uint64_t>(static_cast<double>(std::trunc(v)));
-  return (static_cast<uint128>(w0) << 100) | (static_cast<uint128>(w1) << 50) |
-         static_cast<uint128>(w2);
-}
-#endif  // __clang__ && !__SSE3__
-}  // namespace
-
-uint128::uint128(float v) : uint128(MakeUint128FromFloat(v)) {}
-uint128::uint128(double v) : uint128(MakeUint128FromFloat(v)) {}
-uint128::uint128(long double v) : uint128(MakeUint128FromFloat(v)) {}
-
-uint128 operator/(uint128 lhs, uint128 rhs) {
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-  return static_cast<unsigned __int128>(lhs) /
-         static_cast<unsigned __int128>(rhs);
-#else  // ABSL_HAVE_INTRINSIC_INT128
-  uint128 quotient = 0;
-  uint128 remainder = 0;
-  DivModImpl(lhs, rhs, &quotient, &remainder);
-  return quotient;
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-}
-uint128 operator%(uint128 lhs, uint128 rhs) {
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-  return static_cast<unsigned __int128>(lhs) %
-         static_cast<unsigned __int128>(rhs);
-#else  // ABSL_HAVE_INTRINSIC_INT128
-  uint128 quotient = 0;
-  uint128 remainder = 0;
-  DivModImpl(lhs, rhs, &quotient, &remainder);
-  return remainder;
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-}
-
-namespace {
-
-std::string Uint128ToFormattedString(uint128 v, std::ios_base::fmtflags flags) {
-  // Select a divisor which is the largest power of the base < 2^64.
-  uint128 div;
-  int div_base_log;
-  switch (flags & std::ios::basefield) {
-    case std::ios::hex:
-      div = 0x1000000000000000;  // 16^15
-      div_base_log = 15;
-      break;
-    case std::ios::oct:
-      div = 01000000000000000000000;  // 8^21
-      div_base_log = 21;
-      break;
-    default:  // std::ios::dec
-      div = 10000000000000000000u;  // 10^19
-      div_base_log = 19;
-      break;
-  }
-
-  // Now piece together the uint128 representation from three chunks of the
-  // original value, each less than "div" and therefore representable as a
-  // uint64_t.
-  std::ostringstream os;
-  std::ios_base::fmtflags copy_mask =
-      std::ios::basefield | std::ios::showbase | std::ios::uppercase;
-  os.setf(flags & copy_mask, copy_mask);
-  uint128 high = v;
-  uint128 low;
-  DivModImpl(high, div, &high, &low);
-  uint128 mid;
-  DivModImpl(high, div, &high, &mid);
-  if (Uint128Low64(high) != 0) {
-    os << Uint128Low64(high);
-    os << std::noshowbase << std::setfill('0') << std::setw(div_base_log);
-    os << Uint128Low64(mid);
-    os << std::setw(div_base_log);
-  } else if (Uint128Low64(mid) != 0) {
-    os << Uint128Low64(mid);
-    os << std::noshowbase << std::setfill('0') << std::setw(div_base_log);
-  }
-  os << Uint128Low64(low);
-  return os.str();
-}
-
-}  // namespace
-
-std::ostream& operator<<(std::ostream& os, uint128 v) {
-  std::ios_base::fmtflags flags = os.flags();
-  std::string rep = Uint128ToFormattedString(v, flags);
-
-  // Add the requisite padding.
-  std::streamsize width = os.width(0);
-  if (static_cast<size_t>(width) > rep.size()) {
-    std::ios::fmtflags adjustfield = flags & std::ios::adjustfield;
-    if (adjustfield == std::ios::left) {
-      rep.append(width - rep.size(), os.fill());
-    } else if (adjustfield == std::ios::internal &&
-               (flags & std::ios::showbase) &&
-               (flags & std::ios::basefield) == std::ios::hex && v != 0) {
-      rep.insert(2, width - rep.size(), os.fill());
-    } else {
-      rep.insert(0, width - rep.size(), os.fill());
-    }
-  }
-
-  return os << rep;
-}
-
-namespace {
-
-uint128 UnsignedAbsoluteValue(int128 v) {
-  // Cast to uint128 before possibly negating because -Int128Min() is undefined.
-  return Int128High64(v) < 0 ? -uint128(v) : uint128(v);
-}
-
-}  // namespace
-
-#if !defined(ABSL_HAVE_INTRINSIC_INT128)
-namespace {
-
-template <typename T>
-int128 MakeInt128FromFloat(T v) {
-  // Conversion when v is NaN or cannot fit into int128 would be undefined
-  // behavior if using an intrinsic 128-bit integer.
-  assert(std::isfinite(v) && (std::numeric_limits<T>::max_exponent <= 127 ||
-                              (v >= -std::ldexp(static_cast<T>(1), 127) &&
-                               v < std::ldexp(static_cast<T>(1), 127))));
-
-  // We must convert the absolute value and then negate as needed, because
-  // floating point types are typically sign-magnitude. Otherwise, the
-  // difference between the high and low 64 bits when interpreted as two's
-  // complement overwhelms the precision of the mantissa.
-  uint128 result = v < 0 ? -MakeUint128FromFloat(-v) : MakeUint128FromFloat(v);
-  return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)),
-                    Uint128Low64(result));
-}
-
-}  // namespace
-
-int128::int128(float v) : int128(MakeInt128FromFloat(v)) {}
-int128::int128(double v) : int128(MakeInt128FromFloat(v)) {}
-int128::int128(long double v) : int128(MakeInt128FromFloat(v)) {}
-
-int128 operator/(int128 lhs, int128 rhs) {
-  assert(lhs != Int128Min() || rhs != -1);  // UB on two's complement.
-
-  uint128 quotient = 0;
-  uint128 remainder = 0;
-  DivModImpl(UnsignedAbsoluteValue(lhs), UnsignedAbsoluteValue(rhs),
-             &quotient, &remainder);
-  if ((Int128High64(lhs) < 0) != (Int128High64(rhs) < 0)) quotient = -quotient;
-  return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(quotient)),
-                    Uint128Low64(quotient));
-}
-
-int128 operator%(int128 lhs, int128 rhs) {
-  assert(lhs != Int128Min() || rhs != -1);  // UB on two's complement.
-
-  uint128 quotient = 0;
-  uint128 remainder = 0;
-  DivModImpl(UnsignedAbsoluteValue(lhs), UnsignedAbsoluteValue(rhs),
-             &quotient, &remainder);
-  if (Int128High64(lhs) < 0) remainder = -remainder;
-  return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(remainder)),
-                    Uint128Low64(remainder));
-}
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-std::ostream& operator<<(std::ostream& os, int128 v) {
-  std::ios_base::fmtflags flags = os.flags();
-  std::string rep;
-
-  // Add the sign if needed.
-  bool print_as_decimal =
-      (flags & std::ios::basefield) == std::ios::dec ||
-      (flags & std::ios::basefield) == std::ios_base::fmtflags();
-  if (print_as_decimal) {
-    if (Int128High64(v) < 0) {
-      rep = "-";
-    } else if (flags & std::ios::showpos) {
-      rep = "+";
-    }
-  }
-
-  rep.append(Uint128ToFormattedString(
-      print_as_decimal ? UnsignedAbsoluteValue(v) : uint128(v), os.flags()));
-
-  // Add the requisite padding.
-  std::streamsize width = os.width(0);
-  if (static_cast<size_t>(width) > rep.size()) {
-    switch (flags & std::ios::adjustfield) {
-      case std::ios::left:
-        rep.append(width - rep.size(), os.fill());
-        break;
-      case std::ios::internal:
-        if (print_as_decimal && (rep[0] == '+' || rep[0] == '-')) {
-          rep.insert(1, width - rep.size(), os.fill());
-        } else if ((flags & std::ios::basefield) == std::ios::hex &&
-                   (flags & std::ios::showbase) && v != 0) {
-          rep.insert(2, width - rep.size(), os.fill());
-        } else {
-          rep.insert(0, width - rep.size(), os.fill());
-        }
-        break;
-      default:  // std::ios::right
-        rep.insert(0, width - rep.size(), os.fill());
-        break;
-    }
-  }
-
-  return os << rep;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-namespace std {
-constexpr bool numeric_limits<absl::uint128>::is_specialized;
-constexpr bool numeric_limits<absl::uint128>::is_signed;
-constexpr bool numeric_limits<absl::uint128>::is_integer;
-constexpr bool numeric_limits<absl::uint128>::is_exact;
-constexpr bool numeric_limits<absl::uint128>::has_infinity;
-constexpr bool numeric_limits<absl::uint128>::has_quiet_NaN;
-constexpr bool numeric_limits<absl::uint128>::has_signaling_NaN;
-constexpr float_denorm_style numeric_limits<absl::uint128>::has_denorm;
-constexpr bool numeric_limits<absl::uint128>::has_denorm_loss;
-constexpr float_round_style numeric_limits<absl::uint128>::round_style;
-constexpr bool numeric_limits<absl::uint128>::is_iec559;
-constexpr bool numeric_limits<absl::uint128>::is_bounded;
-constexpr bool numeric_limits<absl::uint128>::is_modulo;
-constexpr int numeric_limits<absl::uint128>::digits;
-constexpr int numeric_limits<absl::uint128>::digits10;
-constexpr int numeric_limits<absl::uint128>::max_digits10;
-constexpr int numeric_limits<absl::uint128>::radix;
-constexpr int numeric_limits<absl::uint128>::min_exponent;
-constexpr int numeric_limits<absl::uint128>::min_exponent10;
-constexpr int numeric_limits<absl::uint128>::max_exponent;
-constexpr int numeric_limits<absl::uint128>::max_exponent10;
-constexpr bool numeric_limits<absl::uint128>::traps;
-constexpr bool numeric_limits<absl::uint128>::tinyness_before;
-
-constexpr bool numeric_limits<absl::int128>::is_specialized;
-constexpr bool numeric_limits<absl::int128>::is_signed;
-constexpr bool numeric_limits<absl::int128>::is_integer;
-constexpr bool numeric_limits<absl::int128>::is_exact;
-constexpr bool numeric_limits<absl::int128>::has_infinity;
-constexpr bool numeric_limits<absl::int128>::has_quiet_NaN;
-constexpr bool numeric_limits<absl::int128>::has_signaling_NaN;
-constexpr float_denorm_style numeric_limits<absl::int128>::has_denorm;
-constexpr bool numeric_limits<absl::int128>::has_denorm_loss;
-constexpr float_round_style numeric_limits<absl::int128>::round_style;
-constexpr bool numeric_limits<absl::int128>::is_iec559;
-constexpr bool numeric_limits<absl::int128>::is_bounded;
-constexpr bool numeric_limits<absl::int128>::is_modulo;
-constexpr int numeric_limits<absl::int128>::digits;
-constexpr int numeric_limits<absl::int128>::digits10;
-constexpr int numeric_limits<absl::int128>::max_digits10;
-constexpr int numeric_limits<absl::int128>::radix;
-constexpr int numeric_limits<absl::int128>::min_exponent;
-constexpr int numeric_limits<absl::int128>::min_exponent10;
-constexpr int numeric_limits<absl::int128>::max_exponent;
-constexpr int numeric_limits<absl::int128>::max_exponent10;
-constexpr bool numeric_limits<absl::int128>::traps;
-constexpr bool numeric_limits<absl::int128>::tinyness_before;
-}  // namespace std
diff --git a/third_party/abseil_cpp/absl/numeric/int128.h b/third_party/abseil_cpp/absl/numeric/int128.h
deleted file mode 100644
index 0dd814a890..0000000000
--- a/third_party/abseil_cpp/absl/numeric/int128.h
+++ /dev/null
@@ -1,1092 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: int128.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines 128-bit integer types, `uint128` and `int128`.
-
-#ifndef ABSL_NUMERIC_INT128_H_
-#define ABSL_NUMERIC_INT128_H_
-
-#include <cassert>
-#include <cmath>
-#include <cstdint>
-#include <cstring>
-#include <iosfwd>
-#include <limits>
-#include <utility>
-
-#include "absl/base/config.h"
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-
-#if defined(_MSC_VER)
-// In very old versions of MSVC and when the /Zc:wchar_t flag is off, wchar_t is
-// a typedef for unsigned short.  Otherwise wchar_t is mapped to the __wchar_t
-// builtin type.  We need to make sure not to define operator wchar_t()
-// alongside operator unsigned short() in these instances.
-#define ABSL_INTERNAL_WCHAR_T __wchar_t
-#if defined(_M_X64)
-#include <intrin.h>
-#pragma intrinsic(_umul128)
-#endif  // defined(_M_X64)
-#else   // defined(_MSC_VER)
-#define ABSL_INTERNAL_WCHAR_T wchar_t
-#endif  // defined(_MSC_VER)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class int128;
-
-// uint128
-//
-// An unsigned 128-bit integer type. The API is meant to mimic an intrinsic type
-// as closely as is practical, including exhibiting undefined behavior in
-// analogous cases (e.g. division by zero). This type is intended to be a
-// drop-in replacement once C++ supports an intrinsic `uint128_t` type; when
-// that occurs, existing well-behaved uses of `uint128` will continue to work
-// using that new type.
-//
-// Note: code written with this type will continue to compile once `uint128_t`
-// is introduced, provided the replacement helper functions
-// `Uint128(Low|High)64()` and `MakeUint128()` are made.
-//
-// A `uint128` supports the following:
-//
-//   * Implicit construction from integral types
-//   * Explicit conversion to integral types
-//
-// Additionally, if your compiler supports `__int128`, `uint128` is
-// interoperable with that type. (Abseil checks for this compatibility through
-// the `ABSL_HAVE_INTRINSIC_INT128` macro.)
-//
-// However, a `uint128` differs from intrinsic integral types in the following
-// ways:
-//
-//   * Errors on implicit conversions that do not preserve value (such as
-//     loss of precision when converting to float values).
-//   * Requires explicit construction from and conversion to floating point
-//     types.
-//   * Conversion to integral types requires an explicit static_cast() to
-//     mimic use of the `-Wnarrowing` compiler flag.
-//   * The alignment requirement of `uint128` may differ from that of an
-//     intrinsic 128-bit integer type depending on platform and build
-//     configuration.
-//
-// Example:
-//
-//     float y = absl::Uint128Max();  // Error. uint128 cannot be implicitly
-//                                    // converted to float.
-//
-//     absl::uint128 v;
-//     uint64_t i = v;                         // Error
-//     uint64_t i = static_cast<uint64_t>(v);  // OK
-//
-class
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-    alignas(unsigned __int128)
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-        uint128 {
- public:
-  uint128() = default;
-
-  // Constructors from arithmetic types
-  constexpr uint128(int v);                 // NOLINT(runtime/explicit)
-  constexpr uint128(unsigned int v);        // NOLINT(runtime/explicit)
-  constexpr uint128(long v);                // NOLINT(runtime/int)
-  constexpr uint128(unsigned long v);       // NOLINT(runtime/int)
-  constexpr uint128(long long v);           // NOLINT(runtime/int)
-  constexpr uint128(unsigned long long v);  // NOLINT(runtime/int)
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  constexpr uint128(__int128 v);           // NOLINT(runtime/explicit)
-  constexpr uint128(unsigned __int128 v);  // NOLINT(runtime/explicit)
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-  constexpr uint128(int128 v);  // NOLINT(runtime/explicit)
-  explicit uint128(float v);
-  explicit uint128(double v);
-  explicit uint128(long double v);
-
-  // Assignment operators from arithmetic types
-  uint128& operator=(int v);
-  uint128& operator=(unsigned int v);
-  uint128& operator=(long v);                // NOLINT(runtime/int)
-  uint128& operator=(unsigned long v);       // NOLINT(runtime/int)
-  uint128& operator=(long long v);           // NOLINT(runtime/int)
-  uint128& operator=(unsigned long long v);  // NOLINT(runtime/int)
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  uint128& operator=(__int128 v);
-  uint128& operator=(unsigned __int128 v);
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-  uint128& operator=(int128 v);
-
-  // Conversion operators to other arithmetic types
-  constexpr explicit operator bool() const;
-  constexpr explicit operator char() const;
-  constexpr explicit operator signed char() const;
-  constexpr explicit operator unsigned char() const;
-  constexpr explicit operator char16_t() const;
-  constexpr explicit operator char32_t() const;
-  constexpr explicit operator ABSL_INTERNAL_WCHAR_T() const;
-  constexpr explicit operator short() const;  // NOLINT(runtime/int)
-  // NOLINTNEXTLINE(runtime/int)
-  constexpr explicit operator unsigned short() const;
-  constexpr explicit operator int() const;
-  constexpr explicit operator unsigned int() const;
-  constexpr explicit operator long() const;  // NOLINT(runtime/int)
-  // NOLINTNEXTLINE(runtime/int)
-  constexpr explicit operator unsigned long() const;
-  // NOLINTNEXTLINE(runtime/int)
-  constexpr explicit operator long long() const;
-  // NOLINTNEXTLINE(runtime/int)
-  constexpr explicit operator unsigned long long() const;
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  constexpr explicit operator __int128() const;
-  constexpr explicit operator unsigned __int128() const;
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-  explicit operator float() const;
-  explicit operator double() const;
-  explicit operator long double() const;
-
-  // Trivial copy constructor, assignment operator and destructor.
-
-  // Arithmetic operators.
-  uint128& operator+=(uint128 other);
-  uint128& operator-=(uint128 other);
-  uint128& operator*=(uint128 other);
-  // Long division/modulo for uint128.
-  uint128& operator/=(uint128 other);
-  uint128& operator%=(uint128 other);
-  uint128 operator++(int);
-  uint128 operator--(int);
-  uint128& operator<<=(int);
-  uint128& operator>>=(int);
-  uint128& operator&=(uint128 other);
-  uint128& operator|=(uint128 other);
-  uint128& operator^=(uint128 other);
-  uint128& operator++();
-  uint128& operator--();
-
-  // Uint128Low64()
-  //
-  // Returns the lower 64-bit value of a `uint128` value.
-  friend constexpr uint64_t Uint128Low64(uint128 v);
-
-  // Uint128High64()
-  //
-  // Returns the higher 64-bit value of a `uint128` value.
-  friend constexpr uint64_t Uint128High64(uint128 v);
-
-  // MakeUInt128()
-  //
-  // Constructs a `uint128` numeric value from two 64-bit unsigned integers.
-  // Note that this factory function is the only way to construct a `uint128`
-  // from integer values greater than 2^64.
-  //
-  // Example:
-  //
-  //   absl::uint128 big = absl::MakeUint128(1, 0);
-  friend constexpr uint128 MakeUint128(uint64_t high, uint64_t low);
-
-  // Uint128Max()
-  //
-  // Returns the highest value for a 128-bit unsigned integer.
-  friend constexpr uint128 Uint128Max();
-
-  // Support for absl::Hash.
-  template <typename H>
-  friend H AbslHashValue(H h, uint128 v) {
-    return H::combine(std::move(h), Uint128High64(v), Uint128Low64(v));
-  }
-
- private:
-  constexpr uint128(uint64_t high, uint64_t low);
-
-  // TODO(strel) Update implementation to use __int128 once all users of
-  // uint128 are fixed to not depend on alignof(uint128) == 8. Also add
-  // alignas(16) to class definition to keep alignment consistent across
-  // platforms.
-#if defined(ABSL_IS_LITTLE_ENDIAN)
-  uint64_t lo_;
-  uint64_t hi_;
-#elif defined(ABSL_IS_BIG_ENDIAN)
-  uint64_t hi_;
-  uint64_t lo_;
-#else  // byte order
-#error "Unsupported byte order: must be little-endian or big-endian."
-#endif  // byte order
-};
-
-// Prefer to use the constexpr `Uint128Max()`.
-//
-// TODO(absl-team) deprecate kuint128max once migration tool is released.
-ABSL_DLL extern const uint128 kuint128max;
-
-// allow uint128 to be logged
-std::ostream& operator<<(std::ostream& os, uint128 v);
-
-// TODO(strel) add operator>>(std::istream&, uint128)
-
-constexpr uint128 Uint128Max() {
-  return uint128((std::numeric_limits<uint64_t>::max)(),
-                 (std::numeric_limits<uint64_t>::max)());
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// Specialized numeric_limits for uint128.
-namespace std {
-template <>
-class numeric_limits<absl::uint128> {
- public:
-  static constexpr bool is_specialized = true;
-  static constexpr bool is_signed = false;
-  static constexpr bool is_integer = true;
-  static constexpr bool is_exact = true;
-  static constexpr bool has_infinity = false;
-  static constexpr bool has_quiet_NaN = false;
-  static constexpr bool has_signaling_NaN = false;
-  static constexpr float_denorm_style has_denorm = denorm_absent;
-  static constexpr bool has_denorm_loss = false;
-  static constexpr float_round_style round_style = round_toward_zero;
-  static constexpr bool is_iec559 = false;
-  static constexpr bool is_bounded = true;
-  static constexpr bool is_modulo = true;
-  static constexpr int digits = 128;
-  static constexpr int digits10 = 38;
-  static constexpr int max_digits10 = 0;
-  static constexpr int radix = 2;
-  static constexpr int min_exponent = 0;
-  static constexpr int min_exponent10 = 0;
-  static constexpr int max_exponent = 0;
-  static constexpr int max_exponent10 = 0;
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  static constexpr bool traps = numeric_limits<unsigned __int128>::traps;
-#else   // ABSL_HAVE_INTRINSIC_INT128
-  static constexpr bool traps = numeric_limits<uint64_t>::traps;
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-  static constexpr bool tinyness_before = false;
-
-  static constexpr absl::uint128 (min)() { return 0; }
-  static constexpr absl::uint128 lowest() { return 0; }
-  static constexpr absl::uint128 (max)() { return absl::Uint128Max(); }
-  static constexpr absl::uint128 epsilon() { return 0; }
-  static constexpr absl::uint128 round_error() { return 0; }
-  static constexpr absl::uint128 infinity() { return 0; }
-  static constexpr absl::uint128 quiet_NaN() { return 0; }
-  static constexpr absl::uint128 signaling_NaN() { return 0; }
-  static constexpr absl::uint128 denorm_min() { return 0; }
-};
-}  // namespace std
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// int128
-//
-// A signed 128-bit integer type. The API is meant to mimic an intrinsic
-// integral type as closely as is practical, including exhibiting undefined
-// behavior in analogous cases (e.g. division by zero).
-//
-// An `int128` supports the following:
-//
-//   * Implicit construction from integral types
-//   * Explicit conversion to integral types
-//
-// However, an `int128` differs from intrinsic integral types in the following
-// ways:
-//
-//   * It is not implicitly convertible to other integral types.
-//   * Requires explicit construction from and conversion to floating point
-//     types.
-
-// Additionally, if your compiler supports `__int128`, `int128` is
-// interoperable with that type. (Abseil checks for this compatibility through
-// the `ABSL_HAVE_INTRINSIC_INT128` macro.)
-//
-// The design goal for `int128` is that it will be compatible with a future
-// `int128_t`, if that type becomes a part of the standard.
-//
-// Example:
-//
-//     float y = absl::int128(17);  // Error. int128 cannot be implicitly
-//                                  // converted to float.
-//
-//     absl::int128 v;
-//     int64_t i = v;                        // Error
-//     int64_t i = static_cast<int64_t>(v);  // OK
-//
-class int128 {
- public:
-  int128() = default;
-
-  // Constructors from arithmetic types
-  constexpr int128(int v);                 // NOLINT(runtime/explicit)
-  constexpr int128(unsigned int v);        // NOLINT(runtime/explicit)
-  constexpr int128(long v);                // NOLINT(runtime/int)
-  constexpr int128(unsigned long v);       // NOLINT(runtime/int)
-  constexpr int128(long long v);           // NOLINT(runtime/int)
-  constexpr int128(unsigned long long v);  // NOLINT(runtime/int)
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  constexpr int128(__int128 v);  // NOLINT(runtime/explicit)
-  constexpr explicit int128(unsigned __int128 v);
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-  constexpr explicit int128(uint128 v);
-  explicit int128(float v);
-  explicit int128(double v);
-  explicit int128(long double v);
-
-  // Assignment operators from arithmetic types
-  int128& operator=(int v);
-  int128& operator=(unsigned int v);
-  int128& operator=(long v);                // NOLINT(runtime/int)
-  int128& operator=(unsigned long v);       // NOLINT(runtime/int)
-  int128& operator=(long long v);           // NOLINT(runtime/int)
-  int128& operator=(unsigned long long v);  // NOLINT(runtime/int)
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  int128& operator=(__int128 v);
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-  // Conversion operators to other arithmetic types
-  constexpr explicit operator bool() const;
-  constexpr explicit operator char() const;
-  constexpr explicit operator signed char() const;
-  constexpr explicit operator unsigned char() const;
-  constexpr explicit operator char16_t() const;
-  constexpr explicit operator char32_t() const;
-  constexpr explicit operator ABSL_INTERNAL_WCHAR_T() const;
-  constexpr explicit operator short() const;  // NOLINT(runtime/int)
-  // NOLINTNEXTLINE(runtime/int)
-  constexpr explicit operator unsigned short() const;
-  constexpr explicit operator int() const;
-  constexpr explicit operator unsigned int() const;
-  constexpr explicit operator long() const;  // NOLINT(runtime/int)
-  // NOLINTNEXTLINE(runtime/int)
-  constexpr explicit operator unsigned long() const;
-  // NOLINTNEXTLINE(runtime/int)
-  constexpr explicit operator long long() const;
-  // NOLINTNEXTLINE(runtime/int)
-  constexpr explicit operator unsigned long long() const;
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  constexpr explicit operator __int128() const;
-  constexpr explicit operator unsigned __int128() const;
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-  explicit operator float() const;
-  explicit operator double() const;
-  explicit operator long double() const;
-
-  // Trivial copy constructor, assignment operator and destructor.
-
-  // Arithmetic operators
-  int128& operator+=(int128 other);
-  int128& operator-=(int128 other);
-  int128& operator*=(int128 other);
-  int128& operator/=(int128 other);
-  int128& operator%=(int128 other);
-  int128 operator++(int);  // postfix increment: i++
-  int128 operator--(int);  // postfix decrement: i--
-  int128& operator++();    // prefix increment:  ++i
-  int128& operator--();    // prefix decrement:  --i
-  int128& operator&=(int128 other);
-  int128& operator|=(int128 other);
-  int128& operator^=(int128 other);
-  int128& operator<<=(int amount);
-  int128& operator>>=(int amount);
-
-  // Int128Low64()
-  //
-  // Returns the lower 64-bit value of a `int128` value.
-  friend constexpr uint64_t Int128Low64(int128 v);
-
-  // Int128High64()
-  //
-  // Returns the higher 64-bit value of a `int128` value.
-  friend constexpr int64_t Int128High64(int128 v);
-
-  // MakeInt128()
-  //
-  // Constructs a `int128` numeric value from two 64-bit integers. Note that
-  // signedness is conveyed in the upper `high` value.
-  //
-  //   (absl::int128(1) << 64) * high + low
-  //
-  // Note that this factory function is the only way to construct a `int128`
-  // from integer values greater than 2^64 or less than -2^64.
-  //
-  // Example:
-  //
-  //   absl::int128 big = absl::MakeInt128(1, 0);
-  //   absl::int128 big_n = absl::MakeInt128(-1, 0);
-  friend constexpr int128 MakeInt128(int64_t high, uint64_t low);
-
-  // Int128Max()
-  //
-  // Returns the maximum value for a 128-bit signed integer.
-  friend constexpr int128 Int128Max();
-
-  // Int128Min()
-  //
-  // Returns the minimum value for a 128-bit signed integer.
-  friend constexpr int128 Int128Min();
-
-  // Support for absl::Hash.
-  template <typename H>
-  friend H AbslHashValue(H h, int128 v) {
-    return H::combine(std::move(h), Int128High64(v), Int128Low64(v));
-  }
-
- private:
-  constexpr int128(int64_t high, uint64_t low);
-
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-  __int128 v_;
-#else  // ABSL_HAVE_INTRINSIC_INT128
-#if defined(ABSL_IS_LITTLE_ENDIAN)
-  uint64_t lo_;
-  int64_t hi_;
-#elif defined(ABSL_IS_BIG_ENDIAN)
-  int64_t hi_;
-  uint64_t lo_;
-#else  // byte order
-#error "Unsupported byte order: must be little-endian or big-endian."
-#endif  // byte order
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-};
-
-std::ostream& operator<<(std::ostream& os, int128 v);
-
-// TODO(absl-team) add operator>>(std::istream&, int128)
-
-constexpr int128 Int128Max() {
-  return int128((std::numeric_limits<int64_t>::max)(),
-                (std::numeric_limits<uint64_t>::max)());
-}
-
-constexpr int128 Int128Min() {
-  return int128((std::numeric_limits<int64_t>::min)(), 0);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// Specialized numeric_limits for int128.
-namespace std {
-template <>
-class numeric_limits<absl::int128> {
- public:
-  static constexpr bool is_specialized = true;
-  static constexpr bool is_signed = true;
-  static constexpr bool is_integer = true;
-  static constexpr bool is_exact = true;
-  static constexpr bool has_infinity = false;
-  static constexpr bool has_quiet_NaN = false;
-  static constexpr bool has_signaling_NaN = false;
-  static constexpr float_denorm_style has_denorm = denorm_absent;
-  static constexpr bool has_denorm_loss = false;
-  static constexpr float_round_style round_style = round_toward_zero;
-  static constexpr bool is_iec559 = false;
-  static constexpr bool is_bounded = true;
-  static constexpr bool is_modulo = false;
-  static constexpr int digits = 127;
-  static constexpr int digits10 = 38;
-  static constexpr int max_digits10 = 0;
-  static constexpr int radix = 2;
-  static constexpr int min_exponent = 0;
-  static constexpr int min_exponent10 = 0;
-  static constexpr int max_exponent = 0;
-  static constexpr int max_exponent10 = 0;
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  static constexpr bool traps = numeric_limits<__int128>::traps;
-#else   // ABSL_HAVE_INTRINSIC_INT128
-  static constexpr bool traps = numeric_limits<uint64_t>::traps;
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-  static constexpr bool tinyness_before = false;
-
-  static constexpr absl::int128 (min)() { return absl::Int128Min(); }
-  static constexpr absl::int128 lowest() { return absl::Int128Min(); }
-  static constexpr absl::int128 (max)() { return absl::Int128Max(); }
-  static constexpr absl::int128 epsilon() { return 0; }
-  static constexpr absl::int128 round_error() { return 0; }
-  static constexpr absl::int128 infinity() { return 0; }
-  static constexpr absl::int128 quiet_NaN() { return 0; }
-  static constexpr absl::int128 signaling_NaN() { return 0; }
-  static constexpr absl::int128 denorm_min() { return 0; }
-};
-}  // namespace std
-
-// --------------------------------------------------------------------------
-//                      Implementation details follow
-// --------------------------------------------------------------------------
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-constexpr uint128 MakeUint128(uint64_t high, uint64_t low) {
-  return uint128(high, low);
-}
-
-// Assignment from integer types.
-
-inline uint128& uint128::operator=(int v) { return *this = uint128(v); }
-
-inline uint128& uint128::operator=(unsigned int v) {
-  return *this = uint128(v);
-}
-
-inline uint128& uint128::operator=(long v) {  // NOLINT(runtime/int)
-  return *this = uint128(v);
-}
-
-// NOLINTNEXTLINE(runtime/int)
-inline uint128& uint128::operator=(unsigned long v) {
-  return *this = uint128(v);
-}
-
-// NOLINTNEXTLINE(runtime/int)
-inline uint128& uint128::operator=(long long v) {
-  return *this = uint128(v);
-}
-
-// NOLINTNEXTLINE(runtime/int)
-inline uint128& uint128::operator=(unsigned long long v) {
-  return *this = uint128(v);
-}
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-inline uint128& uint128::operator=(__int128 v) {
-  return *this = uint128(v);
-}
-
-inline uint128& uint128::operator=(unsigned __int128 v) {
-  return *this = uint128(v);
-}
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-inline uint128& uint128::operator=(int128 v) {
-  return *this = uint128(v);
-}
-
-// Arithmetic operators.
-
-uint128 operator<<(uint128 lhs, int amount);
-uint128 operator>>(uint128 lhs, int amount);
-uint128 operator+(uint128 lhs, uint128 rhs);
-uint128 operator-(uint128 lhs, uint128 rhs);
-uint128 operator*(uint128 lhs, uint128 rhs);
-uint128 operator/(uint128 lhs, uint128 rhs);
-uint128 operator%(uint128 lhs, uint128 rhs);
-
-inline uint128& uint128::operator<<=(int amount) {
-  *this = *this << amount;
-  return *this;
-}
-
-inline uint128& uint128::operator>>=(int amount) {
-  *this = *this >> amount;
-  return *this;
-}
-
-inline uint128& uint128::operator+=(uint128 other) {
-  *this = *this + other;
-  return *this;
-}
-
-inline uint128& uint128::operator-=(uint128 other) {
-  *this = *this - other;
-  return *this;
-}
-
-inline uint128& uint128::operator*=(uint128 other) {
-  *this = *this * other;
-  return *this;
-}
-
-inline uint128& uint128::operator/=(uint128 other) {
-  *this = *this / other;
-  return *this;
-}
-
-inline uint128& uint128::operator%=(uint128 other) {
-  *this = *this % other;
-  return *this;
-}
-
-constexpr uint64_t Uint128Low64(uint128 v) { return v.lo_; }
-
-constexpr uint64_t Uint128High64(uint128 v) { return v.hi_; }
-
-// Constructors from integer types.
-
-#if defined(ABSL_IS_LITTLE_ENDIAN)
-
-constexpr uint128::uint128(uint64_t high, uint64_t low)
-    : lo_{low}, hi_{high} {}
-
-constexpr uint128::uint128(int v)
-    : lo_{static_cast<uint64_t>(v)},
-      hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0} {}
-constexpr uint128::uint128(long v)  // NOLINT(runtime/int)
-    : lo_{static_cast<uint64_t>(v)},
-      hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0} {}
-constexpr uint128::uint128(long long v)  // NOLINT(runtime/int)
-    : lo_{static_cast<uint64_t>(v)},
-      hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0} {}
-
-constexpr uint128::uint128(unsigned int v) : lo_{v}, hi_{0} {}
-// NOLINTNEXTLINE(runtime/int)
-constexpr uint128::uint128(unsigned long v) : lo_{v}, hi_{0} {}
-// NOLINTNEXTLINE(runtime/int)
-constexpr uint128::uint128(unsigned long long v) : lo_{v}, hi_{0} {}
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-constexpr uint128::uint128(__int128 v)
-    : lo_{static_cast<uint64_t>(v & ~uint64_t{0})},
-      hi_{static_cast<uint64_t>(static_cast<unsigned __int128>(v) >> 64)} {}
-constexpr uint128::uint128(unsigned __int128 v)
-    : lo_{static_cast<uint64_t>(v & ~uint64_t{0})},
-      hi_{static_cast<uint64_t>(v >> 64)} {}
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-constexpr uint128::uint128(int128 v)
-    : lo_{Int128Low64(v)}, hi_{static_cast<uint64_t>(Int128High64(v))} {}
-
-#elif defined(ABSL_IS_BIG_ENDIAN)
-
-constexpr uint128::uint128(uint64_t high, uint64_t low)
-    : hi_{high}, lo_{low} {}
-
-constexpr uint128::uint128(int v)
-    : hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0},
-      lo_{static_cast<uint64_t>(v)} {}
-constexpr uint128::uint128(long v)  // NOLINT(runtime/int)
-    : hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0},
-      lo_{static_cast<uint64_t>(v)} {}
-constexpr uint128::uint128(long long v)  // NOLINT(runtime/int)
-    : hi_{v < 0 ? (std::numeric_limits<uint64_t>::max)() : 0},
-      lo_{static_cast<uint64_t>(v)} {}
-
-constexpr uint128::uint128(unsigned int v) : hi_{0}, lo_{v} {}
-// NOLINTNEXTLINE(runtime/int)
-constexpr uint128::uint128(unsigned long v) : hi_{0}, lo_{v} {}
-// NOLINTNEXTLINE(runtime/int)
-constexpr uint128::uint128(unsigned long long v) : hi_{0}, lo_{v} {}
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-constexpr uint128::uint128(__int128 v)
-    : hi_{static_cast<uint64_t>(static_cast<unsigned __int128>(v) >> 64)},
-      lo_{static_cast<uint64_t>(v & ~uint64_t{0})} {}
-constexpr uint128::uint128(unsigned __int128 v)
-    : hi_{static_cast<uint64_t>(v >> 64)},
-      lo_{static_cast<uint64_t>(v & ~uint64_t{0})} {}
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-constexpr uint128::uint128(int128 v)
-    : hi_{static_cast<uint64_t>(Int128High64(v))}, lo_{Int128Low64(v)} {}
-
-#else  // byte order
-#error "Unsupported byte order: must be little-endian or big-endian."
-#endif  // byte order
-
-// Conversion operators to integer types.
-
-constexpr uint128::operator bool() const { return lo_ || hi_; }
-
-constexpr uint128::operator char() const { return static_cast<char>(lo_); }
-
-constexpr uint128::operator signed char() const {
-  return static_cast<signed char>(lo_);
-}
-
-constexpr uint128::operator unsigned char() const {
-  return static_cast<unsigned char>(lo_);
-}
-
-constexpr uint128::operator char16_t() const {
-  return static_cast<char16_t>(lo_);
-}
-
-constexpr uint128::operator char32_t() const {
-  return static_cast<char32_t>(lo_);
-}
-
-constexpr uint128::operator ABSL_INTERNAL_WCHAR_T() const {
-  return static_cast<ABSL_INTERNAL_WCHAR_T>(lo_);
-}
-
-// NOLINTNEXTLINE(runtime/int)
-constexpr uint128::operator short() const { return static_cast<short>(lo_); }
-
-constexpr uint128::operator unsigned short() const {  // NOLINT(runtime/int)
-  return static_cast<unsigned short>(lo_);            // NOLINT(runtime/int)
-}
-
-constexpr uint128::operator int() const { return static_cast<int>(lo_); }
-
-constexpr uint128::operator unsigned int() const {
-  return static_cast<unsigned int>(lo_);
-}
-
-// NOLINTNEXTLINE(runtime/int)
-constexpr uint128::operator long() const { return static_cast<long>(lo_); }
-
-constexpr uint128::operator unsigned long() const {  // NOLINT(runtime/int)
-  return static_cast<unsigned long>(lo_);            // NOLINT(runtime/int)
-}
-
-constexpr uint128::operator long long() const {  // NOLINT(runtime/int)
-  return static_cast<long long>(lo_);            // NOLINT(runtime/int)
-}
-
-constexpr uint128::operator unsigned long long() const {  // NOLINT(runtime/int)
-  return static_cast<unsigned long long>(lo_);            // NOLINT(runtime/int)
-}
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-constexpr uint128::operator __int128() const {
-  return (static_cast<__int128>(hi_) << 64) + lo_;
-}
-
-constexpr uint128::operator unsigned __int128() const {
-  return (static_cast<unsigned __int128>(hi_) << 64) + lo_;
-}
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-// Conversion operators to floating point types.
-
-inline uint128::operator float() const {
-  return static_cast<float>(lo_) + std::ldexp(static_cast<float>(hi_), 64);
-}
-
-inline uint128::operator double() const {
-  return static_cast<double>(lo_) + std::ldexp(static_cast<double>(hi_), 64);
-}
-
-inline uint128::operator long double() const {
-  return static_cast<long double>(lo_) +
-         std::ldexp(static_cast<long double>(hi_), 64);
-}
-
-// Comparison operators.
-
-inline bool operator==(uint128 lhs, uint128 rhs) {
-  return (Uint128Low64(lhs) == Uint128Low64(rhs) &&
-          Uint128High64(lhs) == Uint128High64(rhs));
-}
-
-inline bool operator!=(uint128 lhs, uint128 rhs) {
-  return !(lhs == rhs);
-}
-
-inline bool operator<(uint128 lhs, uint128 rhs) {
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  return static_cast<unsigned __int128>(lhs) <
-         static_cast<unsigned __int128>(rhs);
-#else
-  return (Uint128High64(lhs) == Uint128High64(rhs))
-             ? (Uint128Low64(lhs) < Uint128Low64(rhs))
-             : (Uint128High64(lhs) < Uint128High64(rhs));
-#endif
-}
-
-inline bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; }
-
-inline bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); }
-
-inline bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); }
-
-// Unary operators.
-
-inline uint128 operator-(uint128 val) {
-  uint64_t hi = ~Uint128High64(val);
-  uint64_t lo = ~Uint128Low64(val) + 1;
-  if (lo == 0) ++hi;  // carry
-  return MakeUint128(hi, lo);
-}
-
-inline bool operator!(uint128 val) {
-  return !Uint128High64(val) && !Uint128Low64(val);
-}
-
-// Logical operators.
-
-inline uint128 operator~(uint128 val) {
-  return MakeUint128(~Uint128High64(val), ~Uint128Low64(val));
-}
-
-inline uint128 operator|(uint128 lhs, uint128 rhs) {
-  return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs),
-                           Uint128Low64(lhs) | Uint128Low64(rhs));
-}
-
-inline uint128 operator&(uint128 lhs, uint128 rhs) {
-  return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs),
-                           Uint128Low64(lhs) & Uint128Low64(rhs));
-}
-
-inline uint128 operator^(uint128 lhs, uint128 rhs) {
-  return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs),
-                           Uint128Low64(lhs) ^ Uint128Low64(rhs));
-}
-
-inline uint128& uint128::operator|=(uint128 other) {
-  hi_ |= other.hi_;
-  lo_ |= other.lo_;
-  return *this;
-}
-
-inline uint128& uint128::operator&=(uint128 other) {
-  hi_ &= other.hi_;
-  lo_ &= other.lo_;
-  return *this;
-}
-
-inline uint128& uint128::operator^=(uint128 other) {
-  hi_ ^= other.hi_;
-  lo_ ^= other.lo_;
-  return *this;
-}
-
-// Arithmetic operators.
-
-inline uint128 operator<<(uint128 lhs, int amount) {
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  return static_cast<unsigned __int128>(lhs) << amount;
-#else
-  // uint64_t shifts of >= 64 are undefined, so we will need some
-  // special-casing.
-  if (amount < 64) {
-    if (amount != 0) {
-      return MakeUint128(
-          (Uint128High64(lhs) << amount) | (Uint128Low64(lhs) >> (64 - amount)),
-          Uint128Low64(lhs) << amount);
-    }
-    return lhs;
-  }
-  return MakeUint128(Uint128Low64(lhs) << (amount - 64), 0);
-#endif
-}
-
-inline uint128 operator>>(uint128 lhs, int amount) {
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  return static_cast<unsigned __int128>(lhs) >> amount;
-#else
-  // uint64_t shifts of >= 64 are undefined, so we will need some
-  // special-casing.
-  if (amount < 64) {
-    if (amount != 0) {
-      return MakeUint128(Uint128High64(lhs) >> amount,
-                         (Uint128Low64(lhs) >> amount) |
-                             (Uint128High64(lhs) << (64 - amount)));
-    }
-    return lhs;
-  }
-  return MakeUint128(0, Uint128High64(lhs) >> (amount - 64));
-#endif
-}
-
-inline uint128 operator+(uint128 lhs, uint128 rhs) {
-  uint128 result = MakeUint128(Uint128High64(lhs) + Uint128High64(rhs),
-                               Uint128Low64(lhs) + Uint128Low64(rhs));
-  if (Uint128Low64(result) < Uint128Low64(lhs)) {  // check for carry
-    return MakeUint128(Uint128High64(result) + 1, Uint128Low64(result));
-  }
-  return result;
-}
-
-inline uint128 operator-(uint128 lhs, uint128 rhs) {
-  uint128 result = MakeUint128(Uint128High64(lhs) - Uint128High64(rhs),
-                               Uint128Low64(lhs) - Uint128Low64(rhs));
-  if (Uint128Low64(lhs) < Uint128Low64(rhs)) {  // check for carry
-    return MakeUint128(Uint128High64(result) - 1, Uint128Low64(result));
-  }
-  return result;
-}
-
-inline uint128 operator*(uint128 lhs, uint128 rhs) {
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-  // TODO(strel) Remove once alignment issues are resolved and unsigned __int128
-  // can be used for uint128 storage.
-  return static_cast<unsigned __int128>(lhs) *
-         static_cast<unsigned __int128>(rhs);
-#elif defined(_MSC_VER) && defined(_M_X64)
-  uint64_t carry;
-  uint64_t low = _umul128(Uint128Low64(lhs), Uint128Low64(rhs), &carry);
-  return MakeUint128(Uint128Low64(lhs) * Uint128High64(rhs) +
-                         Uint128High64(lhs) * Uint128Low64(rhs) + carry,
-                     low);
-#else   // ABSL_HAVE_INTRINSIC128
-  uint64_t a32 = Uint128Low64(lhs) >> 32;
-  uint64_t a00 = Uint128Low64(lhs) & 0xffffffff;
-  uint64_t b32 = Uint128Low64(rhs) >> 32;
-  uint64_t b00 = Uint128Low64(rhs) & 0xffffffff;
-  uint128 result =
-      MakeUint128(Uint128High64(lhs) * Uint128Low64(rhs) +
-                      Uint128Low64(lhs) * Uint128High64(rhs) + a32 * b32,
-                  a00 * b00);
-  result += uint128(a32 * b00) << 32;
-  result += uint128(a00 * b32) << 32;
-  return result;
-#endif  // ABSL_HAVE_INTRINSIC128
-}
-
-// Increment/decrement operators.
-
-inline uint128 uint128::operator++(int) {
-  uint128 tmp(*this);
-  *this += 1;
-  return tmp;
-}
-
-inline uint128 uint128::operator--(int) {
-  uint128 tmp(*this);
-  *this -= 1;
-  return tmp;
-}
-
-inline uint128& uint128::operator++() {
-  *this += 1;
-  return *this;
-}
-
-inline uint128& uint128::operator--() {
-  *this -= 1;
-  return *this;
-}
-
-constexpr int128 MakeInt128(int64_t high, uint64_t low) {
-  return int128(high, low);
-}
-
-// Assignment from integer types.
-inline int128& int128::operator=(int v) {
-  return *this = int128(v);
-}
-
-inline int128& int128::operator=(unsigned int v) {
-  return *this = int128(v);
-}
-
-inline int128& int128::operator=(long v) {  // NOLINT(runtime/int)
-  return *this = int128(v);
-}
-
-// NOLINTNEXTLINE(runtime/int)
-inline int128& int128::operator=(unsigned long v) {
-  return *this = int128(v);
-}
-
-// NOLINTNEXTLINE(runtime/int)
-inline int128& int128::operator=(long long v) {
-  return *this = int128(v);
-}
-
-// NOLINTNEXTLINE(runtime/int)
-inline int128& int128::operator=(unsigned long long v) {
-  return *this = int128(v);
-}
-
-// Arithmetic operators.
-
-int128 operator+(int128 lhs, int128 rhs);
-int128 operator-(int128 lhs, int128 rhs);
-int128 operator*(int128 lhs, int128 rhs);
-int128 operator/(int128 lhs, int128 rhs);
-int128 operator%(int128 lhs, int128 rhs);
-int128 operator|(int128 lhs, int128 rhs);
-int128 operator&(int128 lhs, int128 rhs);
-int128 operator^(int128 lhs, int128 rhs);
-int128 operator<<(int128 lhs, int amount);
-int128 operator>>(int128 lhs, int amount);
-
-inline int128& int128::operator+=(int128 other) {
-  *this = *this + other;
-  return *this;
-}
-
-inline int128& int128::operator-=(int128 other) {
-  *this = *this - other;
-  return *this;
-}
-
-inline int128& int128::operator*=(int128 other) {
-  *this = *this * other;
-  return *this;
-}
-
-inline int128& int128::operator/=(int128 other) {
-  *this = *this / other;
-  return *this;
-}
-
-inline int128& int128::operator%=(int128 other) {
-  *this = *this % other;
-  return *this;
-}
-
-inline int128& int128::operator|=(int128 other) {
-  *this = *this | other;
-  return *this;
-}
-
-inline int128& int128::operator&=(int128 other) {
-  *this = *this & other;
-  return *this;
-}
-
-inline int128& int128::operator^=(int128 other) {
-  *this = *this ^ other;
-  return *this;
-}
-
-inline int128& int128::operator<<=(int amount) {
-  *this = *this << amount;
-  return *this;
-}
-
-inline int128& int128::operator>>=(int amount) {
-  *this = *this >> amount;
-  return *this;
-}
-
-namespace int128_internal {
-
-// Casts from unsigned to signed while preserving the underlying binary
-// representation.
-constexpr int64_t BitCastToSigned(uint64_t v) {
-  // Casting an unsigned integer to a signed integer of the same
-  // width is implementation defined behavior if the source value would not fit
-  // in the destination type. We step around it with a roundtrip bitwise not
-  // operation to make sure this function remains constexpr. Clang, GCC, and
-  // MSVC optimize this to a no-op on x86-64.
-  return v & (uint64_t{1} << 63) ? ~static_cast<int64_t>(~v)
-                                 : static_cast<int64_t>(v);
-}
-
-}  // namespace int128_internal
-
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-#include "absl/numeric/int128_have_intrinsic.inc"  // IWYU pragma: export
-#else  // ABSL_HAVE_INTRINSIC_INT128
-#include "absl/numeric/int128_no_intrinsic.inc"  // IWYU pragma: export
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#undef ABSL_INTERNAL_WCHAR_T
-
-#endif  // ABSL_NUMERIC_INT128_H_
diff --git a/third_party/abseil_cpp/absl/numeric/int128_benchmark.cc b/third_party/abseil_cpp/absl/numeric/int128_benchmark.cc
deleted file mode 100644
index eab1515c0a..0000000000
--- a/third_party/abseil_cpp/absl/numeric/int128_benchmark.cc
+++ /dev/null
@@ -1,282 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <algorithm>
-#include <cstdint>
-#include <limits>
-#include <random>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-#include "absl/base/config.h"
-#include "absl/numeric/int128.h"
-
-namespace {
-
-constexpr size_t kSampleSize = 1000000;
-
-std::mt19937 MakeRandomEngine() {
-  std::random_device r;
-  std::seed_seq seed({r(), r(), r(), r(), r(), r(), r(), r()});
-  return std::mt19937(seed);
-}
-
-template <typename T,
-          typename H = typename std::conditional<
-              std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type>
-std::vector<std::pair<T, T>> GetRandomClass128SampleUniformDivisor() {
-  std::vector<std::pair<T, T>> values;
-  std::mt19937 random = MakeRandomEngine();
-  std::uniform_int_distribution<H> uniform_h;
-  values.reserve(kSampleSize);
-  for (size_t i = 0; i < kSampleSize; ++i) {
-    T a{absl::MakeUint128(uniform_h(random), uniform_h(random))};
-    T b{absl::MakeUint128(uniform_h(random), uniform_h(random))};
-    values.emplace_back(std::max(a, b), std::max(T(2), std::min(a, b)));
-  }
-  return values;
-}
-
-template <typename T>
-void BM_DivideClass128UniformDivisor(benchmark::State& state) {
-  auto values = GetRandomClass128SampleUniformDivisor<T>();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first / pair.second);
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::uint128);
-BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::int128);
-
-template <typename T>
-void BM_RemainderClass128UniformDivisor(benchmark::State& state) {
-  auto values = GetRandomClass128SampleUniformDivisor<T>();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first % pair.second);
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::uint128);
-BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::int128);
-
-template <typename T,
-          typename H = typename std::conditional<
-              std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type>
-std::vector<std::pair<T, H>> GetRandomClass128SampleSmallDivisor() {
-  std::vector<std::pair<T, H>> values;
-  std::mt19937 random = MakeRandomEngine();
-  std::uniform_int_distribution<H> uniform_h;
-  values.reserve(kSampleSize);
-  for (size_t i = 0; i < kSampleSize; ++i) {
-    T a{absl::MakeUint128(uniform_h(random), uniform_h(random))};
-    H b{std::max(H{2}, uniform_h(random))};
-    values.emplace_back(std::max(a, T(b)), b);
-  }
-  return values;
-}
-
-template <typename T>
-void BM_DivideClass128SmallDivisor(benchmark::State& state) {
-  auto values = GetRandomClass128SampleSmallDivisor<T>();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first / pair.second);
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::uint128);
-BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::int128);
-
-template <typename T>
-void BM_RemainderClass128SmallDivisor(benchmark::State& state) {
-  auto values = GetRandomClass128SampleSmallDivisor<T>();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first % pair.second);
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::uint128);
-BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::int128);
-
-std::vector<std::pair<absl::uint128, absl::uint128>> GetRandomClass128Sample() {
-  std::vector<std::pair<absl::uint128, absl::uint128>> values;
-  std::mt19937 random = MakeRandomEngine();
-  std::uniform_int_distribution<uint64_t> uniform_uint64;
-  values.reserve(kSampleSize);
-  for (size_t i = 0; i < kSampleSize; ++i) {
-    values.emplace_back(
-        absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)),
-        absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)));
-  }
-  return values;
-}
-
-void BM_MultiplyClass128(benchmark::State& state) {
-  auto values = GetRandomClass128Sample();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first * pair.second);
-    }
-  }
-}
-BENCHMARK(BM_MultiplyClass128);
-
-void BM_AddClass128(benchmark::State& state) {
-  auto values = GetRandomClass128Sample();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first + pair.second);
-    }
-  }
-}
-BENCHMARK(BM_AddClass128);
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-
-// Some implementations of <random> do not support __int128 when it is
-// available, so we make our own uniform_int_distribution-like type.
-template <typename T,
-          typename H = typename std::conditional<
-              std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
-class UniformIntDistribution128 {
- public:
-  // NOLINTNEXTLINE: mimicking std::uniform_int_distribution API
-  T operator()(std::mt19937& generator) {
-    return (static_cast<T>(dist64_(generator)) << 64) | dist64_(generator);
-  }
-
- private:
-  std::uniform_int_distribution<H> dist64_;
-};
-
-template <typename T,
-          typename H = typename std::conditional<
-              std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
-std::vector<std::pair<T, T>> GetRandomIntrinsic128SampleUniformDivisor() {
-  std::vector<std::pair<T, T>> values;
-  std::mt19937 random = MakeRandomEngine();
-  UniformIntDistribution128<T> uniform_128;
-  values.reserve(kSampleSize);
-  for (size_t i = 0; i < kSampleSize; ++i) {
-    T a = uniform_128(random);
-    T b = uniform_128(random);
-    values.emplace_back(std::max(a, b),
-                        std::max(static_cast<T>(2), std::min(a, b)));
-  }
-  return values;
-}
-
-template <typename T>
-void BM_DivideIntrinsic128UniformDivisor(benchmark::State& state) {
-  auto values = GetRandomIntrinsic128SampleUniformDivisor<T>();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first / pair.second);
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, unsigned __int128);
-BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, __int128);
-
-template <typename T>
-void BM_RemainderIntrinsic128UniformDivisor(benchmark::State& state) {
-  auto values = GetRandomIntrinsic128SampleUniformDivisor<T>();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first % pair.second);
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, unsigned __int128);
-BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, __int128);
-
-template <typename T,
-          typename H = typename std::conditional<
-              std::is_same<T, __int128>::value, int64_t, uint64_t>::type>
-std::vector<std::pair<T, H>> GetRandomIntrinsic128SampleSmallDivisor() {
-  std::vector<std::pair<T, H>> values;
-  std::mt19937 random = MakeRandomEngine();
-  UniformIntDistribution128<T> uniform_int128;
-  std::uniform_int_distribution<H> uniform_int64;
-  values.reserve(kSampleSize);
-  for (size_t i = 0; i < kSampleSize; ++i) {
-    T a = uniform_int128(random);
-    H b = std::max(H{2}, uniform_int64(random));
-    values.emplace_back(std::max(a, static_cast<T>(b)), b);
-  }
-  return values;
-}
-
-template <typename T>
-void BM_DivideIntrinsic128SmallDivisor(benchmark::State& state) {
-  auto values = GetRandomIntrinsic128SampleSmallDivisor<T>();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first / pair.second);
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, unsigned __int128);
-BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, __int128);
-
-template <typename T>
-void BM_RemainderIntrinsic128SmallDivisor(benchmark::State& state) {
-  auto values = GetRandomIntrinsic128SampleSmallDivisor<T>();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first % pair.second);
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, unsigned __int128);
-BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, __int128);
-
-std::vector<std::pair<unsigned __int128, unsigned __int128>>
-      GetRandomIntrinsic128Sample() {
-  std::vector<std::pair<unsigned __int128, unsigned __int128>> values;
-  std::mt19937 random = MakeRandomEngine();
-  UniformIntDistribution128<unsigned __int128> uniform_uint128;
-  values.reserve(kSampleSize);
-  for (size_t i = 0; i < kSampleSize; ++i) {
-    values.emplace_back(uniform_uint128(random), uniform_uint128(random));
-  }
-  return values;
-}
-
-void BM_MultiplyIntrinsic128(benchmark::State& state) {
-  auto values = GetRandomIntrinsic128Sample();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first * pair.second);
-    }
-  }
-}
-BENCHMARK(BM_MultiplyIntrinsic128);
-
-void BM_AddIntrinsic128(benchmark::State& state) {
-  auto values = GetRandomIntrinsic128Sample();
-  while (state.KeepRunningBatch(values.size())) {
-    for (const auto& pair : values) {
-      benchmark::DoNotOptimize(pair.first + pair.second);
-    }
-  }
-}
-BENCHMARK(BM_AddIntrinsic128);
-
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/numeric/int128_have_intrinsic.inc b/third_party/abseil_cpp/absl/numeric/int128_have_intrinsic.inc
deleted file mode 100644
index d6c76dd320..0000000000
--- a/third_party/abseil_cpp/absl/numeric/int128_have_intrinsic.inc
+++ /dev/null
@@ -1,302 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file contains :int128 implementation details that depend on internal
-// representation when ABSL_HAVE_INTRINSIC_INT128 is defined. This file is
-// included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined.
-
-namespace int128_internal {
-
-// Casts from unsigned to signed while preserving the underlying binary
-// representation.
-constexpr __int128 BitCastToSigned(unsigned __int128 v) {
-  // Casting an unsigned integer to a signed integer of the same
-  // width is implementation defined behavior if the source value would not fit
-  // in the destination type. We step around it with a roundtrip bitwise not
-  // operation to make sure this function remains constexpr. Clang and GCC
-  // optimize this to a no-op on x86-64.
-  return v & (static_cast<unsigned __int128>(1) << 127)
-             ? ~static_cast<__int128>(~v)
-             : static_cast<__int128>(v);
-}
-
-}  // namespace int128_internal
-
-inline int128& int128::operator=(__int128 v) {
-  v_ = v;
-  return *this;
-}
-
-constexpr uint64_t Int128Low64(int128 v) {
-  return static_cast<uint64_t>(v.v_ & ~uint64_t{0});
-}
-
-constexpr int64_t Int128High64(int128 v) {
-  // Initially cast to unsigned to prevent a right shift on a negative value.
-  return int128_internal::BitCastToSigned(
-      static_cast<uint64_t>(static_cast<unsigned __int128>(v.v_) >> 64));
-}
-
-constexpr int128::int128(int64_t high, uint64_t low)
-    // Initially cast to unsigned to prevent a left shift that overflows.
-    : v_(int128_internal::BitCastToSigned(static_cast<unsigned __int128>(high)
-                                           << 64) |
-         low) {}
-
-
-constexpr int128::int128(int v) : v_{v} {}
-
-constexpr int128::int128(long v) : v_{v} {}       // NOLINT(runtime/int)
-
-constexpr int128::int128(long long v) : v_{v} {}  // NOLINT(runtime/int)
-
-constexpr int128::int128(__int128 v) : v_{v} {}
-
-constexpr int128::int128(unsigned int v) : v_{v} {}
-
-constexpr int128::int128(unsigned long v) : v_{v} {}  // NOLINT(runtime/int)
-
-// NOLINTNEXTLINE(runtime/int)
-constexpr int128::int128(unsigned long long v) : v_{v} {}
-
-constexpr int128::int128(unsigned __int128 v) : v_{static_cast<__int128>(v)} {}
-
-inline int128::int128(float v) {
-  v_ = static_cast<__int128>(v);
-}
-
-inline int128::int128(double v) {
-  v_ = static_cast<__int128>(v);
-}
-
-inline int128::int128(long double v) {
-  v_ = static_cast<__int128>(v);
-}
-
-constexpr int128::int128(uint128 v) : v_{static_cast<__int128>(v)} {}
-
-constexpr int128::operator bool() const { return static_cast<bool>(v_); }
-
-constexpr int128::operator char() const { return static_cast<char>(v_); }
-
-constexpr int128::operator signed char() const {
-  return static_cast<signed char>(v_);
-}
-
-constexpr int128::operator unsigned char() const {
-  return static_cast<unsigned char>(v_);
-}
-
-constexpr int128::operator char16_t() const {
-  return static_cast<char16_t>(v_);
-}
-
-constexpr int128::operator char32_t() const {
-  return static_cast<char32_t>(v_);
-}
-
-constexpr int128::operator ABSL_INTERNAL_WCHAR_T() const {
-  return static_cast<ABSL_INTERNAL_WCHAR_T>(v_);
-}
-
-constexpr int128::operator short() const {  // NOLINT(runtime/int)
-  return static_cast<short>(v_);            // NOLINT(runtime/int)
-}
-
-constexpr int128::operator unsigned short() const {  // NOLINT(runtime/int)
-  return static_cast<unsigned short>(v_);            // NOLINT(runtime/int)
-}
-
-constexpr int128::operator int() const {
-  return static_cast<int>(v_);
-}
-
-constexpr int128::operator unsigned int() const {
-  return static_cast<unsigned int>(v_);
-}
-
-constexpr int128::operator long() const {  // NOLINT(runtime/int)
-  return static_cast<long>(v_);            // NOLINT(runtime/int)
-}
-
-constexpr int128::operator unsigned long() const {  // NOLINT(runtime/int)
-  return static_cast<unsigned long>(v_);            // NOLINT(runtime/int)
-}
-
-constexpr int128::operator long long() const {  // NOLINT(runtime/int)
-  return static_cast<long long>(v_);            // NOLINT(runtime/int)
-}
-
-constexpr int128::operator unsigned long long() const {  // NOLINT(runtime/int)
-  return static_cast<unsigned long long>(v_);            // NOLINT(runtime/int)
-}
-
-constexpr int128::operator __int128() const { return v_; }
-
-constexpr int128::operator unsigned __int128() const {
-  return static_cast<unsigned __int128>(v_);
-}
-
-// Clang on PowerPC sometimes produces incorrect __int128 to floating point
-// conversions. In that case, we do the conversion with a similar implementation
-// to the conversion operators in int128_no_intrinsic.inc.
-#if defined(__clang__) && !defined(__ppc64__)
-inline int128::operator float() const { return static_cast<float>(v_); }
-
-inline int128::operator double () const { return static_cast<double>(v_); }
-
-inline int128::operator long double() const {
-  return static_cast<long double>(v_);
-}
-
-#else  // Clang on PowerPC
-// Forward declaration for conversion operators to floating point types.
-int128 operator-(int128 v);
-bool operator!=(int128 lhs, int128 rhs);
-
-inline int128::operator float() const {
-  // We must convert the absolute value and then negate as needed, because
-  // floating point types are typically sign-magnitude. Otherwise, the
-  // difference between the high and low 64 bits when interpreted as two's
-  // complement overwhelms the precision of the mantissa.
-  //
-  // Also check to make sure we don't negate Int128Min()
-  return v_ < 0 && *this != Int128Min()
-             ? -static_cast<float>(-*this)
-             : static_cast<float>(Int128Low64(*this)) +
-                   std::ldexp(static_cast<float>(Int128High64(*this)), 64);
-}
-
-inline int128::operator double() const {
-  // See comment in int128::operator float() above.
-  return v_ < 0 && *this != Int128Min()
-             ? -static_cast<double>(-*this)
-             : static_cast<double>(Int128Low64(*this)) +
-                   std::ldexp(static_cast<double>(Int128High64(*this)), 64);
-}
-
-inline int128::operator long double() const {
-  // See comment in int128::operator float() above.
-  return v_ < 0 && *this != Int128Min()
-             ? -static_cast<long double>(-*this)
-             : static_cast<long double>(Int128Low64(*this)) +
-                   std::ldexp(static_cast<long double>(Int128High64(*this)),
-                              64);
-}
-#endif  // Clang on PowerPC
-
-// Comparison operators.
-
-inline bool operator==(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) == static_cast<__int128>(rhs);
-}
-
-inline bool operator!=(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) != static_cast<__int128>(rhs);
-}
-
-inline bool operator<(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) < static_cast<__int128>(rhs);
-}
-
-inline bool operator>(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) > static_cast<__int128>(rhs);
-}
-
-inline bool operator<=(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) <= static_cast<__int128>(rhs);
-}
-
-inline bool operator>=(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) >= static_cast<__int128>(rhs);
-}
-
-// Unary operators.
-
-inline int128 operator-(int128 v) {
-  return -static_cast<__int128>(v);
-}
-
-inline bool operator!(int128 v) {
-  return !static_cast<__int128>(v);
-}
-
-inline int128 operator~(int128 val) {
-  return ~static_cast<__int128>(val);
-}
-
-// Arithmetic operators.
-
-inline int128 operator+(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) + static_cast<__int128>(rhs);
-}
-
-inline int128 operator-(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) - static_cast<__int128>(rhs);
-}
-
-inline int128 operator*(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) * static_cast<__int128>(rhs);
-}
-
-inline int128 operator/(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) / static_cast<__int128>(rhs);
-}
-
-inline int128 operator%(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) % static_cast<__int128>(rhs);
-}
-
-inline int128 int128::operator++(int) {
-  int128 tmp(*this);
-  ++v_;
-  return tmp;
-}
-
-inline int128 int128::operator--(int) {
-  int128 tmp(*this);
-  --v_;
-  return tmp;
-}
-
-inline int128& int128::operator++() {
-  ++v_;
-  return *this;
-}
-
-inline int128& int128::operator--() {
-  --v_;
-  return *this;
-}
-
-inline int128 operator|(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) | static_cast<__int128>(rhs);
-}
-
-inline int128 operator&(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) & static_cast<__int128>(rhs);
-}
-
-inline int128 operator^(int128 lhs, int128 rhs) {
-  return static_cast<__int128>(lhs) ^ static_cast<__int128>(rhs);
-}
-
-inline int128 operator<<(int128 lhs, int amount) {
-  return static_cast<__int128>(lhs) << amount;
-}
-
-inline int128 operator>>(int128 lhs, int amount) {
-  return static_cast<__int128>(lhs) >> amount;
-}
diff --git a/third_party/abseil_cpp/absl/numeric/int128_no_intrinsic.inc b/third_party/abseil_cpp/absl/numeric/int128_no_intrinsic.inc
deleted file mode 100644
index c753771ae7..0000000000
--- a/third_party/abseil_cpp/absl/numeric/int128_no_intrinsic.inc
+++ /dev/null
@@ -1,308 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file contains :int128 implementation details that depend on internal
-// representation when ABSL_HAVE_INTRINSIC_INT128 is *not* defined. This file
-// is included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined.
-
-constexpr uint64_t Int128Low64(int128 v) { return v.lo_; }
-
-constexpr int64_t Int128High64(int128 v) { return v.hi_; }
-
-#if defined(ABSL_IS_LITTLE_ENDIAN)
-
-constexpr int128::int128(int64_t high, uint64_t low) :
-    lo_(low), hi_(high) {}
-
-constexpr int128::int128(int v)
-    : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {}
-constexpr int128::int128(long v)  // NOLINT(runtime/int)
-    : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {}
-constexpr int128::int128(long long v)  // NOLINT(runtime/int)
-    : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {}
-
-constexpr int128::int128(unsigned int v) : lo_{v}, hi_{0} {}
-// NOLINTNEXTLINE(runtime/int)
-constexpr int128::int128(unsigned long v) : lo_{v}, hi_{0} {}
-// NOLINTNEXTLINE(runtime/int)
-constexpr int128::int128(unsigned long long v) : lo_{v}, hi_{0} {}
-
-constexpr int128::int128(uint128 v)
-    : lo_{Uint128Low64(v)}, hi_{static_cast<int64_t>(Uint128High64(v))} {}
-
-#elif defined(ABSL_IS_BIG_ENDIAN)
-
-constexpr int128::int128(int64_t high, uint64_t low) :
-    hi_{high}, lo_{low} {}
-
-constexpr int128::int128(int v)
-    : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {}
-constexpr int128::int128(long v)  // NOLINT(runtime/int)
-    : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {}
-constexpr int128::int128(long long v)  // NOLINT(runtime/int)
-    : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {}
-
-constexpr int128::int128(unsigned int v) : hi_{0}, lo_{v} {}
-// NOLINTNEXTLINE(runtime/int)
-constexpr int128::int128(unsigned long v) : hi_{0}, lo_{v} {}
-// NOLINTNEXTLINE(runtime/int)
-constexpr int128::int128(unsigned long long v) : hi_{0}, lo_{v} {}
-
-constexpr int128::int128(uint128 v)
-    : hi_{static_cast<int64_t>(Uint128High64(v))}, lo_{Uint128Low64(v)} {}
-
-#else  // byte order
-#error "Unsupported byte order: must be little-endian or big-endian."
-#endif  // byte order
-
-constexpr int128::operator bool() const { return lo_ || hi_; }
-
-constexpr int128::operator char() const {
-  // NOLINTNEXTLINE(runtime/int)
-  return static_cast<char>(static_cast<long long>(*this));
-}
-
-constexpr int128::operator signed char() const {
-  // NOLINTNEXTLINE(runtime/int)
-  return static_cast<signed char>(static_cast<long long>(*this));
-}
-
-constexpr int128::operator unsigned char() const {
-  return static_cast<unsigned char>(lo_);
-}
-
-constexpr int128::operator char16_t() const {
-  return static_cast<char16_t>(lo_);
-}
-
-constexpr int128::operator char32_t() const {
-  return static_cast<char32_t>(lo_);
-}
-
-constexpr int128::operator ABSL_INTERNAL_WCHAR_T() const {
-  // NOLINTNEXTLINE(runtime/int)
-  return static_cast<ABSL_INTERNAL_WCHAR_T>(static_cast<long long>(*this));
-}
-
-constexpr int128::operator short() const {  // NOLINT(runtime/int)
-  // NOLINTNEXTLINE(runtime/int)
-  return static_cast<short>(static_cast<long long>(*this));
-}
-
-constexpr int128::operator unsigned short() const {  // NOLINT(runtime/int)
-  return static_cast<unsigned short>(lo_);           // NOLINT(runtime/int)
-}
-
-constexpr int128::operator int() const {
-  // NOLINTNEXTLINE(runtime/int)
-  return static_cast<int>(static_cast<long long>(*this));
-}
-
-constexpr int128::operator unsigned int() const {
-  return static_cast<unsigned int>(lo_);
-}
-
-constexpr int128::operator long() const {  // NOLINT(runtime/int)
-  // NOLINTNEXTLINE(runtime/int)
-  return static_cast<long>(static_cast<long long>(*this));
-}
-
-constexpr int128::operator unsigned long() const {  // NOLINT(runtime/int)
-  return static_cast<unsigned long>(lo_);           // NOLINT(runtime/int)
-}
-
-constexpr int128::operator long long() const {  // NOLINT(runtime/int)
-  // We don't bother checking the value of hi_. If *this < 0, lo_'s high bit
-  // must be set in order for the value to fit into a long long. Conversely, if
-  // lo_'s high bit is set, *this must be < 0 for the value to fit.
-  return int128_internal::BitCastToSigned(lo_);
-}
-
-constexpr int128::operator unsigned long long() const {  // NOLINT(runtime/int)
-  return static_cast<unsigned long long>(lo_);           // NOLINT(runtime/int)
-}
-
-// Forward declaration for conversion operators to floating point types.
-int128 operator-(int128 v);
-bool operator!=(int128 lhs, int128 rhs);
-
-inline int128::operator float() const {
-  // We must convert the absolute value and then negate as needed, because
-  // floating point types are typically sign-magnitude. Otherwise, the
-  // difference between the high and low 64 bits when interpreted as two's
-  // complement overwhelms the precision of the mantissa.
-  //
-  // Also check to make sure we don't negate Int128Min()
-  return hi_ < 0 && *this != Int128Min()
-             ? -static_cast<float>(-*this)
-             : static_cast<float>(lo_) +
-                   std::ldexp(static_cast<float>(hi_), 64);
-}
-
-inline int128::operator double() const {
-  // See comment in int128::operator float() above.
-  return hi_ < 0 && *this != Int128Min()
-             ? -static_cast<double>(-*this)
-             : static_cast<double>(lo_) +
-                   std::ldexp(static_cast<double>(hi_), 64);
-}
-
-inline int128::operator long double() const {
-  // See comment in int128::operator float() above.
-  return hi_ < 0 && *this != Int128Min()
-             ? -static_cast<long double>(-*this)
-             : static_cast<long double>(lo_) +
-                   std::ldexp(static_cast<long double>(hi_), 64);
-}
-
-// Comparison operators.
-
-inline bool operator==(int128 lhs, int128 rhs) {
-  return (Int128Low64(lhs) == Int128Low64(rhs) &&
-          Int128High64(lhs) == Int128High64(rhs));
-}
-
-inline bool operator!=(int128 lhs, int128 rhs) {
-  return !(lhs == rhs);
-}
-
-inline bool operator<(int128 lhs, int128 rhs) {
-  return (Int128High64(lhs) == Int128High64(rhs))
-             ? (Int128Low64(lhs) < Int128Low64(rhs))
-             : (Int128High64(lhs) < Int128High64(rhs));
-}
-
-inline bool operator>(int128 lhs, int128 rhs) {
-  return (Int128High64(lhs) == Int128High64(rhs))
-             ? (Int128Low64(lhs) > Int128Low64(rhs))
-             : (Int128High64(lhs) > Int128High64(rhs));
-}
-
-inline bool operator<=(int128 lhs, int128 rhs) {
-  return !(lhs > rhs);
-}
-
-inline bool operator>=(int128 lhs, int128 rhs) {
-  return !(lhs < rhs);
-}
-
-// Unary operators.
-
-inline int128 operator-(int128 v) {
-  int64_t hi = ~Int128High64(v);
-  uint64_t lo = ~Int128Low64(v) + 1;
-  if (lo == 0) ++hi;  // carry
-  return MakeInt128(hi, lo);
-}
-
-inline bool operator!(int128 v) {
-  return !Int128Low64(v) && !Int128High64(v);
-}
-
-inline int128 operator~(int128 val) {
-  return MakeInt128(~Int128High64(val), ~Int128Low64(val));
-}
-
-// Arithmetic operators.
-
-inline int128 operator+(int128 lhs, int128 rhs) {
-  int128 result = MakeInt128(Int128High64(lhs) + Int128High64(rhs),
-                             Int128Low64(lhs) + Int128Low64(rhs));
-  if (Int128Low64(result) < Int128Low64(lhs)) {  // check for carry
-    return MakeInt128(Int128High64(result) + 1, Int128Low64(result));
-  }
-  return result;
-}
-
-inline int128 operator-(int128 lhs, int128 rhs) {
-  int128 result = MakeInt128(Int128High64(lhs) - Int128High64(rhs),
-                             Int128Low64(lhs) - Int128Low64(rhs));
-  if (Int128Low64(lhs) < Int128Low64(rhs)) {  // check for carry
-    return MakeInt128(Int128High64(result) - 1, Int128Low64(result));
-  }
-  return result;
-}
-
-inline int128 operator*(int128 lhs, int128 rhs) {
-  uint128 result = uint128(lhs) * rhs;
-  return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)),
-                    Uint128Low64(result));
-}
-
-inline int128 int128::operator++(int) {
-  int128 tmp(*this);
-  *this += 1;
-  return tmp;
-}
-
-inline int128 int128::operator--(int) {
-  int128 tmp(*this);
-  *this -= 1;
-  return tmp;
-}
-
-inline int128& int128::operator++() {
-  *this += 1;
-  return *this;
-}
-
-inline int128& int128::operator--() {
-  *this -= 1;
-  return *this;
-}
-
-inline int128 operator|(int128 lhs, int128 rhs) {
-  return MakeInt128(Int128High64(lhs) | Int128High64(rhs),
-                    Int128Low64(lhs) | Int128Low64(rhs));
-}
-
-inline int128 operator&(int128 lhs, int128 rhs) {
-  return MakeInt128(Int128High64(lhs) & Int128High64(rhs),
-                    Int128Low64(lhs) & Int128Low64(rhs));
-}
-
-inline int128 operator^(int128 lhs, int128 rhs) {
-  return MakeInt128(Int128High64(lhs) ^ Int128High64(rhs),
-                    Int128Low64(lhs) ^ Int128Low64(rhs));
-}
-
-inline int128 operator<<(int128 lhs, int amount) {
-  // uint64_t shifts of >= 64 are undefined, so we need some special-casing.
-  if (amount < 64) {
-    if (amount != 0) {
-      return MakeInt128(
-          (Int128High64(lhs) << amount) |
-              static_cast<int64_t>(Int128Low64(lhs) >> (64 - amount)),
-          Int128Low64(lhs) << amount);
-    }
-    return lhs;
-  }
-  return MakeInt128(static_cast<int64_t>(Int128Low64(lhs) << (amount - 64)), 0);
-}
-
-inline int128 operator>>(int128 lhs, int amount) {
-  // uint64_t shifts of >= 64 are undefined, so we need some special-casing.
-  if (amount < 64) {
-    if (amount != 0) {
-      return MakeInt128(
-          Int128High64(lhs) >> amount,
-          (Int128Low64(lhs) >> amount) |
-              (static_cast<uint64_t>(Int128High64(lhs)) << (64 - amount)));
-    }
-    return lhs;
-  }
-  return MakeInt128(0,
-                    static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64)));
-}
diff --git a/third_party/abseil_cpp/absl/numeric/int128_stream_test.cc b/third_party/abseil_cpp/absl/numeric/int128_stream_test.cc
deleted file mode 100644
index 479ad66cf4..0000000000
--- a/third_party/abseil_cpp/absl/numeric/int128_stream_test.cc
+++ /dev/null
@@ -1,1395 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/numeric/int128.h"
-
-#include <sstream>
-#include <string>
-
-#include "gtest/gtest.h"
-
-namespace {
-
-struct Uint128TestCase {
-  absl::uint128 value;
-  std::ios_base::fmtflags flags;
-  std::streamsize width;
-  const char* expected;
-};
-
-constexpr char kFill = '_';
-
-std::string StreamFormatToString(std::ios_base::fmtflags flags,
-                                 std::streamsize width) {
-  std::vector<const char*> flagstr;
-  switch (flags & std::ios::basefield) {
-    case std::ios::dec:
-      flagstr.push_back("std::ios::dec");
-      break;
-    case std::ios::oct:
-      flagstr.push_back("std::ios::oct");
-      break;
-    case std::ios::hex:
-      flagstr.push_back("std::ios::hex");
-      break;
-    default:  // basefield not specified
-      break;
-  }
-  switch (flags & std::ios::adjustfield) {
-    case std::ios::left:
-      flagstr.push_back("std::ios::left");
-      break;
-    case std::ios::internal:
-      flagstr.push_back("std::ios::internal");
-      break;
-    case std::ios::right:
-      flagstr.push_back("std::ios::right");
-      break;
-    default:  // adjustfield not specified
-      break;
-  }
-  if (flags & std::ios::uppercase) flagstr.push_back("std::ios::uppercase");
-  if (flags & std::ios::showbase) flagstr.push_back("std::ios::showbase");
-  if (flags & std::ios::showpos) flagstr.push_back("std::ios::showpos");
-
-  std::ostringstream msg;
-  msg << "\n  StreamFormatToString(test_case.flags, test_case.width)\n    "
-         "flags: ";
-  if (!flagstr.empty()) {
-    for (size_t i = 0; i < flagstr.size() - 1; ++i) msg << flagstr[i] << " | ";
-    msg << flagstr.back();
-  } else {
-    msg << "(default)";
-  }
-  msg << "\n    width: " << width << "\n    fill: '" << kFill << "'";
-  return msg.str();
-}
-
-void CheckUint128Case(const Uint128TestCase& test_case) {
-  std::ostringstream os;
-  os.flags(test_case.flags);
-  os.width(test_case.width);
-  os.fill(kFill);
-  os << test_case.value;
-  SCOPED_TRACE(StreamFormatToString(test_case.flags, test_case.width));
-  EXPECT_EQ(test_case.expected, os.str());
-}
-
-constexpr std::ios::fmtflags kDec = std::ios::dec;
-constexpr std::ios::fmtflags kOct = std::ios::oct;
-constexpr std::ios::fmtflags kHex = std::ios::hex;
-constexpr std::ios::fmtflags kLeft = std::ios::left;
-constexpr std::ios::fmtflags kInt = std::ios::internal;
-constexpr std::ios::fmtflags kRight = std::ios::right;
-constexpr std::ios::fmtflags kUpper = std::ios::uppercase;
-constexpr std::ios::fmtflags kBase = std::ios::showbase;
-constexpr std::ios::fmtflags kPos = std::ios::showpos;
-
-TEST(Uint128, OStreamValueTest) {
-  CheckUint128Case({1, kDec, /*width = */ 0, "1"});
-  CheckUint128Case({1, kOct, /*width = */ 0, "1"});
-  CheckUint128Case({1, kHex, /*width = */ 0, "1"});
-  CheckUint128Case({9, kDec, /*width = */ 0, "9"});
-  CheckUint128Case({9, kOct, /*width = */ 0, "11"});
-  CheckUint128Case({9, kHex, /*width = */ 0, "9"});
-  CheckUint128Case({12345, kDec, /*width = */ 0, "12345"});
-  CheckUint128Case({12345, kOct, /*width = */ 0, "30071"});
-  CheckUint128Case({12345, kHex, /*width = */ 0, "3039"});
-  CheckUint128Case(
-      {0x8000000000000000, kDec, /*width = */ 0, "9223372036854775808"});
-  CheckUint128Case(
-      {0x8000000000000000, kOct, /*width = */ 0, "1000000000000000000000"});
-  CheckUint128Case(
-      {0x8000000000000000, kHex, /*width = */ 0, "8000000000000000"});
-  CheckUint128Case({std::numeric_limits<uint64_t>::max(), kDec,
-                    /*width = */ 0, "18446744073709551615"});
-  CheckUint128Case({std::numeric_limits<uint64_t>::max(), kOct,
-                    /*width = */ 0, "1777777777777777777777"});
-  CheckUint128Case({std::numeric_limits<uint64_t>::max(), kHex,
-                    /*width = */ 0, "ffffffffffffffff"});
-  CheckUint128Case(
-      {absl::MakeUint128(1, 0), kDec, /*width = */ 0, "18446744073709551616"});
-  CheckUint128Case({absl::MakeUint128(1, 0), kOct, /*width = */ 0,
-                    "2000000000000000000000"});
-  CheckUint128Case(
-      {absl::MakeUint128(1, 0), kHex, /*width = */ 0, "10000000000000000"});
-  CheckUint128Case({absl::MakeUint128(0x8000000000000000, 0), kDec,
-                    /*width = */ 0, "170141183460469231731687303715884105728"});
-  CheckUint128Case({absl::MakeUint128(0x8000000000000000, 0), kOct,
-                    /*width = */ 0,
-                    "2000000000000000000000000000000000000000000"});
-  CheckUint128Case({absl::MakeUint128(0x8000000000000000, 0), kHex,
-                    /*width = */ 0, "80000000000000000000000000000000"});
-  CheckUint128Case({absl::kuint128max, kDec, /*width = */ 0,
-                    "340282366920938463463374607431768211455"});
-  CheckUint128Case({absl::kuint128max, kOct, /*width = */ 0,
-                    "3777777777777777777777777777777777777777777"});
-  CheckUint128Case({absl::kuint128max, kHex, /*width = */ 0,
-                    "ffffffffffffffffffffffffffffffff"});
-}
-
-std::vector<Uint128TestCase> GetUint128FormatCases();
-
-TEST(Uint128, OStreamFormatTest) {
-  for (const Uint128TestCase& test_case : GetUint128FormatCases()) {
-    CheckUint128Case(test_case);
-  }
-}
-
-struct Int128TestCase {
-  absl::int128 value;
-  std::ios_base::fmtflags flags;
-  std::streamsize width;
-  const char* expected;
-};
-
-void CheckInt128Case(const Int128TestCase& test_case) {
-  std::ostringstream os;
-  os.flags(test_case.flags);
-  os.width(test_case.width);
-  os.fill(kFill);
-  os << test_case.value;
-  SCOPED_TRACE(StreamFormatToString(test_case.flags, test_case.width));
-  EXPECT_EQ(test_case.expected, os.str());
-}
-
-TEST(Int128, OStreamValueTest) {
-  CheckInt128Case({1, kDec, /*width = */ 0, "1"});
-  CheckInt128Case({1, kOct, /*width = */ 0, "1"});
-  CheckInt128Case({1, kHex, /*width = */ 0, "1"});
-  CheckInt128Case({9, kDec, /*width = */ 0, "9"});
-  CheckInt128Case({9, kOct, /*width = */ 0, "11"});
-  CheckInt128Case({9, kHex, /*width = */ 0, "9"});
-  CheckInt128Case({12345, kDec, /*width = */ 0, "12345"});
-  CheckInt128Case({12345, kOct, /*width = */ 0, "30071"});
-  CheckInt128Case({12345, kHex, /*width = */ 0, "3039"});
-  CheckInt128Case(
-      {0x8000000000000000, kDec, /*width = */ 0, "9223372036854775808"});
-  CheckInt128Case(
-      {0x8000000000000000, kOct, /*width = */ 0, "1000000000000000000000"});
-  CheckInt128Case(
-      {0x8000000000000000, kHex, /*width = */ 0, "8000000000000000"});
-  CheckInt128Case({std::numeric_limits<uint64_t>::max(), kDec,
-                   /*width = */ 0, "18446744073709551615"});
-  CheckInt128Case({std::numeric_limits<uint64_t>::max(), kOct,
-                   /*width = */ 0, "1777777777777777777777"});
-  CheckInt128Case({std::numeric_limits<uint64_t>::max(), kHex,
-                   /*width = */ 0, "ffffffffffffffff"});
-  CheckInt128Case(
-      {absl::MakeInt128(1, 0), kDec, /*width = */ 0, "18446744073709551616"});
-  CheckInt128Case(
-      {absl::MakeInt128(1, 0), kOct, /*width = */ 0, "2000000000000000000000"});
-  CheckInt128Case(
-      {absl::MakeInt128(1, 0), kHex, /*width = */ 0, "10000000000000000"});
-  CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(),
-                                    std::numeric_limits<uint64_t>::max()),
-                   std::ios::dec, /*width = */ 0,
-                   "170141183460469231731687303715884105727"});
-  CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(),
-                                    std::numeric_limits<uint64_t>::max()),
-                   std::ios::oct, /*width = */ 0,
-                   "1777777777777777777777777777777777777777777"});
-  CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(),
-                                    std::numeric_limits<uint64_t>::max()),
-                   std::ios::hex, /*width = */ 0,
-                   "7fffffffffffffffffffffffffffffff"});
-  CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0),
-                   std::ios::dec, /*width = */ 0,
-                   "-170141183460469231731687303715884105728"});
-  CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0),
-                   std::ios::oct, /*width = */ 0,
-                   "2000000000000000000000000000000000000000000"});
-  CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0),
-                   std::ios::hex, /*width = */ 0,
-                   "80000000000000000000000000000000"});
-  CheckInt128Case({-1, std::ios::dec, /*width = */ 0, "-1"});
-  CheckInt128Case({-1, std::ios::oct, /*width = */ 0,
-                   "3777777777777777777777777777777777777777777"});
-  CheckInt128Case(
-      {-1, std::ios::hex, /*width = */ 0, "ffffffffffffffffffffffffffffffff"});
-  CheckInt128Case({-12345, std::ios::dec, /*width = */ 0, "-12345"});
-  CheckInt128Case({-12345, std::ios::oct, /*width = */ 0,
-                   "3777777777777777777777777777777777777747707"});
-  CheckInt128Case({-12345, std::ios::hex, /*width = */ 0,
-                   "ffffffffffffffffffffffffffffcfc7"});
-}
-
-std::vector<Int128TestCase> GetInt128FormatCases();
-TEST(Int128, OStreamFormatTest) {
-  for (const Int128TestCase& test_case : GetInt128FormatCases()) {
-    CheckInt128Case(test_case);
-  }
-}
-
-std::vector<Int128TestCase> GetInt128FormatCases() {
-  return {
-      {0, std::ios_base::fmtflags(), /*width = */ 0, "0"},
-      {0, std::ios_base::fmtflags(), /*width = */ 6, "_____0"},
-      {0, kPos, /*width = */ 0, "+0"},
-      {0, kPos, /*width = */ 6, "____+0"},
-      {0, kBase, /*width = */ 0, "0"},
-      {0, kBase, /*width = */ 6, "_____0"},
-      {0, kBase | kPos, /*width = */ 0, "+0"},
-      {0, kBase | kPos, /*width = */ 6, "____+0"},
-      {0, kUpper, /*width = */ 0, "0"},
-      {0, kUpper, /*width = */ 6, "_____0"},
-      {0, kUpper | kPos, /*width = */ 0, "+0"},
-      {0, kUpper | kPos, /*width = */ 6, "____+0"},
-      {0, kUpper | kBase, /*width = */ 0, "0"},
-      {0, kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kUpper | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kUpper | kBase | kPos, /*width = */ 6, "____+0"},
-      {0, kLeft, /*width = */ 0, "0"},
-      {0, kLeft, /*width = */ 6, "0_____"},
-      {0, kLeft | kPos, /*width = */ 0, "+0"},
-      {0, kLeft | kPos, /*width = */ 6, "+0____"},
-      {0, kLeft | kBase, /*width = */ 0, "0"},
-      {0, kLeft | kBase, /*width = */ 6, "0_____"},
-      {0, kLeft | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kLeft | kBase | kPos, /*width = */ 6, "+0____"},
-      {0, kLeft | kUpper, /*width = */ 0, "0"},
-      {0, kLeft | kUpper, /*width = */ 6, "0_____"},
-      {0, kLeft | kUpper | kPos, /*width = */ 0, "+0"},
-      {0, kLeft | kUpper | kPos, /*width = */ 6, "+0____"},
-      {0, kLeft | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
-      {0, kLeft | kUpper | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kLeft | kUpper | kBase | kPos, /*width = */ 6, "+0____"},
-      {0, kInt, /*width = */ 0, "0"},
-      {0, kInt, /*width = */ 6, "_____0"},
-      {0, kInt | kPos, /*width = */ 0, "+0"},
-      {0, kInt | kPos, /*width = */ 6, "+____0"},
-      {0, kInt | kBase, /*width = */ 0, "0"},
-      {0, kInt | kBase, /*width = */ 6, "_____0"},
-      {0, kInt | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kInt | kBase | kPos, /*width = */ 6, "+____0"},
-      {0, kInt | kUpper, /*width = */ 0, "0"},
-      {0, kInt | kUpper, /*width = */ 6, "_____0"},
-      {0, kInt | kUpper | kPos, /*width = */ 0, "+0"},
-      {0, kInt | kUpper | kPos, /*width = */ 6, "+____0"},
-      {0, kInt | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kInt | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kInt | kUpper | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kInt | kUpper | kBase | kPos, /*width = */ 6, "+____0"},
-      {0, kRight, /*width = */ 0, "0"},
-      {0, kRight, /*width = */ 6, "_____0"},
-      {0, kRight | kPos, /*width = */ 0, "+0"},
-      {0, kRight | kPos, /*width = */ 6, "____+0"},
-      {0, kRight | kBase, /*width = */ 0, "0"},
-      {0, kRight | kBase, /*width = */ 6, "_____0"},
-      {0, kRight | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kRight | kBase | kPos, /*width = */ 6, "____+0"},
-      {0, kRight | kUpper, /*width = */ 0, "0"},
-      {0, kRight | kUpper, /*width = */ 6, "_____0"},
-      {0, kRight | kUpper | kPos, /*width = */ 0, "+0"},
-      {0, kRight | kUpper | kPos, /*width = */ 6, "____+0"},
-      {0, kRight | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kRight | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kRight | kUpper | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kRight | kUpper | kBase | kPos, /*width = */ 6, "____+0"},
-      {0, kDec, /*width = */ 0, "0"},
-      {0, kDec, /*width = */ 6, "_____0"},
-      {0, kDec | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kPos, /*width = */ 6, "____+0"},
-      {0, kDec | kBase, /*width = */ 0, "0"},
-      {0, kDec | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kBase | kPos, /*width = */ 6, "____+0"},
-      {0, kDec | kUpper, /*width = */ 0, "0"},
-      {0, kDec | kUpper, /*width = */ 6, "_____0"},
-      {0, kDec | kUpper | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kUpper | kPos, /*width = */ 6, "____+0"},
-      {0, kDec | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kDec | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kUpper | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kUpper | kBase | kPos, /*width = */ 6, "____+0"},
-      {0, kDec | kLeft, /*width = */ 0, "0"},
-      {0, kDec | kLeft, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kLeft | kPos, /*width = */ 6, "+0____"},
-      {0, kDec | kLeft | kBase, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kBase, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kLeft | kBase | kPos, /*width = */ 6, "+0____"},
-      {0, kDec | kLeft | kUpper, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kUpper, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kUpper | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kLeft | kUpper | kPos, /*width = */ 6, "+0____"},
-      {0, kDec | kLeft | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "+0____"},
-      {0, kDec | kInt, /*width = */ 0, "0"},
-      {0, kDec | kInt, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kInt | kPos, /*width = */ 6, "+____0"},
-      {0, kDec | kInt | kBase, /*width = */ 0, "0"},
-      {0, kDec | kInt | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kInt | kBase | kPos, /*width = */ 6, "+____0"},
-      {0, kDec | kInt | kUpper, /*width = */ 0, "0"},
-      {0, kDec | kInt | kUpper, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kUpper | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kInt | kUpper | kPos, /*width = */ 6, "+____0"},
-      {0, kDec | kInt | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kDec | kInt | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "+____0"},
-      {0, kDec | kRight, /*width = */ 0, "0"},
-      {0, kDec | kRight, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kRight | kPos, /*width = */ 6, "____+0"},
-      {0, kDec | kRight | kBase, /*width = */ 0, "0"},
-      {0, kDec | kRight | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kRight | kBase | kPos, /*width = */ 6, "____+0"},
-      {0, kDec | kRight | kUpper, /*width = */ 0, "0"},
-      {0, kDec | kRight | kUpper, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kUpper | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kRight | kUpper | kPos, /*width = */ 6, "____+0"},
-      {0, kDec | kRight | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kDec | kRight | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "+0"},
-      {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "____+0"},
-      {0, kOct, /*width = */ 0, "0"},
-      {0, kOct, /*width = */ 6, "_____0"},
-      {0, kOct | kPos, /*width = */ 0, "0"},
-      {0, kOct | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kBase, /*width = */ 0, "0"},
-      {0, kOct | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kUpper, /*width = */ 0, "0"},
-      {0, kOct | kUpper, /*width = */ 6, "_____0"},
-      {0, kOct | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kOct | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kOct | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kLeft, /*width = */ 0, "0"},
-      {0, kOct | kLeft, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kPos, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kPos, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kBase, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kBase, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kUpper, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kUpper, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kUpper | kPos, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kOct | kInt, /*width = */ 0, "0"},
-      {0, kOct | kInt, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kPos, /*width = */ 0, "0"},
-      {0, kOct | kInt | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kBase, /*width = */ 0, "0"},
-      {0, kOct | kInt | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kInt | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kUpper, /*width = */ 0, "0"},
-      {0, kOct | kInt | kUpper, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kOct | kInt | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kOct | kInt | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kRight, /*width = */ 0, "0"},
-      {0, kOct | kRight, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kPos, /*width = */ 0, "0"},
-      {0, kOct | kRight | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kBase, /*width = */ 0, "0"},
-      {0, kOct | kRight | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kRight | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kUpper, /*width = */ 0, "0"},
-      {0, kOct | kRight | kUpper, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kOct | kRight | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kOct | kRight | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex, /*width = */ 0, "0"},
-      {0, kHex, /*width = */ 6, "_____0"},
-      {0, kHex | kPos, /*width = */ 0, "0"},
-      {0, kHex | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kBase, /*width = */ 0, "0"},
-      {0, kHex | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kUpper, /*width = */ 0, "0"},
-      {0, kHex | kUpper, /*width = */ 6, "_____0"},
-      {0, kHex | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kHex | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kHex | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kLeft, /*width = */ 0, "0"},
-      {0, kHex | kLeft, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kPos, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kPos, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kBase, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kBase, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kUpper, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kUpper, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kUpper | kPos, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kHex | kInt, /*width = */ 0, "0"},
-      {0, kHex | kInt, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kPos, /*width = */ 0, "0"},
-      {0, kHex | kInt | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kBase, /*width = */ 0, "0"},
-      {0, kHex | kInt | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kInt | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kUpper, /*width = */ 0, "0"},
-      {0, kHex | kInt | kUpper, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kHex | kInt | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kHex | kInt | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kRight, /*width = */ 0, "0"},
-      {0, kHex | kRight, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kPos, /*width = */ 0, "0"},
-      {0, kHex | kRight | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kBase, /*width = */ 0, "0"},
-      {0, kHex | kRight | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kRight | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kUpper, /*width = */ 0, "0"},
-      {0, kHex | kRight | kUpper, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kHex | kRight | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kHex | kRight | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {42, std::ios_base::fmtflags(), /*width = */ 0, "42"},
-      {42, std::ios_base::fmtflags(), /*width = */ 6, "____42"},
-      {42, kPos, /*width = */ 0, "+42"},
-      {42, kPos, /*width = */ 6, "___+42"},
-      {42, kBase, /*width = */ 0, "42"},
-      {42, kBase, /*width = */ 6, "____42"},
-      {42, kBase | kPos, /*width = */ 0, "+42"},
-      {42, kBase | kPos, /*width = */ 6, "___+42"},
-      {42, kUpper, /*width = */ 0, "42"},
-      {42, kUpper, /*width = */ 6, "____42"},
-      {42, kUpper | kPos, /*width = */ 0, "+42"},
-      {42, kUpper | kPos, /*width = */ 6, "___+42"},
-      {42, kUpper | kBase, /*width = */ 0, "42"},
-      {42, kUpper | kBase, /*width = */ 6, "____42"},
-      {42, kUpper | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kUpper | kBase | kPos, /*width = */ 6, "___+42"},
-      {42, kLeft, /*width = */ 0, "42"},
-      {42, kLeft, /*width = */ 6, "42____"},
-      {42, kLeft | kPos, /*width = */ 0, "+42"},
-      {42, kLeft | kPos, /*width = */ 6, "+42___"},
-      {42, kLeft | kBase, /*width = */ 0, "42"},
-      {42, kLeft | kBase, /*width = */ 6, "42____"},
-      {42, kLeft | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kLeft | kBase | kPos, /*width = */ 6, "+42___"},
-      {42, kLeft | kUpper, /*width = */ 0, "42"},
-      {42, kLeft | kUpper, /*width = */ 6, "42____"},
-      {42, kLeft | kUpper | kPos, /*width = */ 0, "+42"},
-      {42, kLeft | kUpper | kPos, /*width = */ 6, "+42___"},
-      {42, kLeft | kUpper | kBase, /*width = */ 0, "42"},
-      {42, kLeft | kUpper | kBase, /*width = */ 6, "42____"},
-      {42, kLeft | kUpper | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kLeft | kUpper | kBase | kPos, /*width = */ 6, "+42___"},
-      {42, kInt, /*width = */ 0, "42"},
-      {42, kInt, /*width = */ 6, "____42"},
-      {42, kInt | kPos, /*width = */ 0, "+42"},
-      {42, kInt | kPos, /*width = */ 6, "+___42"},
-      {42, kInt | kBase, /*width = */ 0, "42"},
-      {42, kInt | kBase, /*width = */ 6, "____42"},
-      {42, kInt | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kInt | kBase | kPos, /*width = */ 6, "+___42"},
-      {42, kInt | kUpper, /*width = */ 0, "42"},
-      {42, kInt | kUpper, /*width = */ 6, "____42"},
-      {42, kInt | kUpper | kPos, /*width = */ 0, "+42"},
-      {42, kInt | kUpper | kPos, /*width = */ 6, "+___42"},
-      {42, kInt | kUpper | kBase, /*width = */ 0, "42"},
-      {42, kInt | kUpper | kBase, /*width = */ 6, "____42"},
-      {42, kInt | kUpper | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kInt | kUpper | kBase | kPos, /*width = */ 6, "+___42"},
-      {42, kRight, /*width = */ 0, "42"},
-      {42, kRight, /*width = */ 6, "____42"},
-      {42, kRight | kPos, /*width = */ 0, "+42"},
-      {42, kRight | kPos, /*width = */ 6, "___+42"},
-      {42, kRight | kBase, /*width = */ 0, "42"},
-      {42, kRight | kBase, /*width = */ 6, "____42"},
-      {42, kRight | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kRight | kBase | kPos, /*width = */ 6, "___+42"},
-      {42, kRight | kUpper, /*width = */ 0, "42"},
-      {42, kRight | kUpper, /*width = */ 6, "____42"},
-      {42, kRight | kUpper | kPos, /*width = */ 0, "+42"},
-      {42, kRight | kUpper | kPos, /*width = */ 6, "___+42"},
-      {42, kRight | kUpper | kBase, /*width = */ 0, "42"},
-      {42, kRight | kUpper | kBase, /*width = */ 6, "____42"},
-      {42, kRight | kUpper | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kRight | kUpper | kBase | kPos, /*width = */ 6, "___+42"},
-      {42, kDec, /*width = */ 0, "42"},
-      {42, kDec, /*width = */ 6, "____42"},
-      {42, kDec | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kPos, /*width = */ 6, "___+42"},
-      {42, kDec | kBase, /*width = */ 0, "42"},
-      {42, kDec | kBase, /*width = */ 6, "____42"},
-      {42, kDec | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kBase | kPos, /*width = */ 6, "___+42"},
-      {42, kDec | kUpper, /*width = */ 0, "42"},
-      {42, kDec | kUpper, /*width = */ 6, "____42"},
-      {42, kDec | kUpper | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kUpper | kPos, /*width = */ 6, "___+42"},
-      {42, kDec | kUpper | kBase, /*width = */ 0, "42"},
-      {42, kDec | kUpper | kBase, /*width = */ 6, "____42"},
-      {42, kDec | kUpper | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kUpper | kBase | kPos, /*width = */ 6, "___+42"},
-      {42, kDec | kLeft, /*width = */ 0, "42"},
-      {42, kDec | kLeft, /*width = */ 6, "42____"},
-      {42, kDec | kLeft | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kLeft | kPos, /*width = */ 6, "+42___"},
-      {42, kDec | kLeft | kBase, /*width = */ 0, "42"},
-      {42, kDec | kLeft | kBase, /*width = */ 6, "42____"},
-      {42, kDec | kLeft | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kLeft | kBase | kPos, /*width = */ 6, "+42___"},
-      {42, kDec | kLeft | kUpper, /*width = */ 0, "42"},
-      {42, kDec | kLeft | kUpper, /*width = */ 6, "42____"},
-      {42, kDec | kLeft | kUpper | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kLeft | kUpper | kPos, /*width = */ 6, "+42___"},
-      {42, kDec | kLeft | kUpper | kBase, /*width = */ 0, "42"},
-      {42, kDec | kLeft | kUpper | kBase, /*width = */ 6, "42____"},
-      {42, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "+42___"},
-      {42, kDec | kInt, /*width = */ 0, "42"},
-      {42, kDec | kInt, /*width = */ 6, "____42"},
-      {42, kDec | kInt | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kInt | kPos, /*width = */ 6, "+___42"},
-      {42, kDec | kInt | kBase, /*width = */ 0, "42"},
-      {42, kDec | kInt | kBase, /*width = */ 6, "____42"},
-      {42, kDec | kInt | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kInt | kBase | kPos, /*width = */ 6, "+___42"},
-      {42, kDec | kInt | kUpper, /*width = */ 0, "42"},
-      {42, kDec | kInt | kUpper, /*width = */ 6, "____42"},
-      {42, kDec | kInt | kUpper | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kInt | kUpper | kPos, /*width = */ 6, "+___42"},
-      {42, kDec | kInt | kUpper | kBase, /*width = */ 0, "42"},
-      {42, kDec | kInt | kUpper | kBase, /*width = */ 6, "____42"},
-      {42, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "+___42"},
-      {42, kDec | kRight, /*width = */ 0, "42"},
-      {42, kDec | kRight, /*width = */ 6, "____42"},
-      {42, kDec | kRight | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kRight | kPos, /*width = */ 6, "___+42"},
-      {42, kDec | kRight | kBase, /*width = */ 0, "42"},
-      {42, kDec | kRight | kBase, /*width = */ 6, "____42"},
-      {42, kDec | kRight | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kRight | kBase | kPos, /*width = */ 6, "___+42"},
-      {42, kDec | kRight | kUpper, /*width = */ 0, "42"},
-      {42, kDec | kRight | kUpper, /*width = */ 6, "____42"},
-      {42, kDec | kRight | kUpper | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kRight | kUpper | kPos, /*width = */ 6, "___+42"},
-      {42, kDec | kRight | kUpper | kBase, /*width = */ 0, "42"},
-      {42, kDec | kRight | kUpper | kBase, /*width = */ 6, "____42"},
-      {42, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "+42"},
-      {42, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "___+42"},
-      {42, kOct, /*width = */ 0, "52"},
-      {42, kOct, /*width = */ 6, "____52"},
-      {42, kOct | kPos, /*width = */ 0, "52"},
-      {42, kOct | kPos, /*width = */ 6, "____52"},
-      {42, kOct | kBase, /*width = */ 0, "052"},
-      {42, kOct | kBase, /*width = */ 6, "___052"},
-      {42, kOct | kBase | kPos, /*width = */ 0, "052"},
-      {42, kOct | kBase | kPos, /*width = */ 6, "___052"},
-      {42, kOct | kUpper, /*width = */ 0, "52"},
-      {42, kOct | kUpper, /*width = */ 6, "____52"},
-      {42, kOct | kUpper | kPos, /*width = */ 0, "52"},
-      {42, kOct | kUpper | kPos, /*width = */ 6, "____52"},
-      {42, kOct | kUpper | kBase, /*width = */ 0, "052"},
-      {42, kOct | kUpper | kBase, /*width = */ 6, "___052"},
-      {42, kOct | kUpper | kBase | kPos, /*width = */ 0, "052"},
-      {42, kOct | kUpper | kBase | kPos, /*width = */ 6, "___052"},
-      {42, kOct | kLeft, /*width = */ 0, "52"},
-      {42, kOct | kLeft, /*width = */ 6, "52____"},
-      {42, kOct | kLeft | kPos, /*width = */ 0, "52"},
-      {42, kOct | kLeft | kPos, /*width = */ 6, "52____"},
-      {42, kOct | kLeft | kBase, /*width = */ 0, "052"},
-      {42, kOct | kLeft | kBase, /*width = */ 6, "052___"},
-      {42, kOct | kLeft | kBase | kPos, /*width = */ 0, "052"},
-      {42, kOct | kLeft | kBase | kPos, /*width = */ 6, "052___"},
-      {42, kOct | kLeft | kUpper, /*width = */ 0, "52"},
-      {42, kOct | kLeft | kUpper, /*width = */ 6, "52____"},
-      {42, kOct | kLeft | kUpper | kPos, /*width = */ 0, "52"},
-      {42, kOct | kLeft | kUpper | kPos, /*width = */ 6, "52____"},
-      {42, kOct | kLeft | kUpper | kBase, /*width = */ 0, "052"},
-      {42, kOct | kLeft | kUpper | kBase, /*width = */ 6, "052___"},
-      {42, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "052"},
-      {42, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "052___"},
-      {42, kOct | kInt, /*width = */ 0, "52"},
-      {42, kOct | kInt, /*width = */ 6, "____52"},
-      {42, kOct | kInt | kPos, /*width = */ 0, "52"},
-      {42, kOct | kInt | kPos, /*width = */ 6, "____52"},
-      {42, kOct | kInt | kBase, /*width = */ 0, "052"},
-      {42, kOct | kInt | kBase, /*width = */ 6, "___052"},
-      {42, kOct | kInt | kBase | kPos, /*width = */ 0, "052"},
-      {42, kOct | kInt | kBase | kPos, /*width = */ 6, "___052"},
-      {42, kOct | kInt | kUpper, /*width = */ 0, "52"},
-      {42, kOct | kInt | kUpper, /*width = */ 6, "____52"},
-      {42, kOct | kInt | kUpper | kPos, /*width = */ 0, "52"},
-      {42, kOct | kInt | kUpper | kPos, /*width = */ 6, "____52"},
-      {42, kOct | kInt | kUpper | kBase, /*width = */ 0, "052"},
-      {42, kOct | kInt | kUpper | kBase, /*width = */ 6, "___052"},
-      {42, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "052"},
-      {42, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "___052"},
-      {42, kOct | kRight, /*width = */ 0, "52"},
-      {42, kOct | kRight, /*width = */ 6, "____52"},
-      {42, kOct | kRight | kPos, /*width = */ 0, "52"},
-      {42, kOct | kRight | kPos, /*width = */ 6, "____52"},
-      {42, kOct | kRight | kBase, /*width = */ 0, "052"},
-      {42, kOct | kRight | kBase, /*width = */ 6, "___052"},
-      {42, kOct | kRight | kBase | kPos, /*width = */ 0, "052"},
-      {42, kOct | kRight | kBase | kPos, /*width = */ 6, "___052"},
-      {42, kOct | kRight | kUpper, /*width = */ 0, "52"},
-      {42, kOct | kRight | kUpper, /*width = */ 6, "____52"},
-      {42, kOct | kRight | kUpper | kPos, /*width = */ 0, "52"},
-      {42, kOct | kRight | kUpper | kPos, /*width = */ 6, "____52"},
-      {42, kOct | kRight | kUpper | kBase, /*width = */ 0, "052"},
-      {42, kOct | kRight | kUpper | kBase, /*width = */ 6, "___052"},
-      {42, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "052"},
-      {42, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "___052"},
-      {42, kHex, /*width = */ 0, "2a"},
-      {42, kHex, /*width = */ 6, "____2a"},
-      {42, kHex | kPos, /*width = */ 0, "2a"},
-      {42, kHex | kPos, /*width = */ 6, "____2a"},
-      {42, kHex | kBase, /*width = */ 0, "0x2a"},
-      {42, kHex | kBase, /*width = */ 6, "__0x2a"},
-      {42, kHex | kBase | kPos, /*width = */ 0, "0x2a"},
-      {42, kHex | kBase | kPos, /*width = */ 6, "__0x2a"},
-      {42, kHex | kUpper, /*width = */ 0, "2A"},
-      {42, kHex | kUpper, /*width = */ 6, "____2A"},
-      {42, kHex | kUpper | kPos, /*width = */ 0, "2A"},
-      {42, kHex | kUpper | kPos, /*width = */ 6, "____2A"},
-      {42, kHex | kUpper | kBase, /*width = */ 0, "0X2A"},
-      {42, kHex | kUpper | kBase, /*width = */ 6, "__0X2A"},
-      {42, kHex | kUpper | kBase | kPos, /*width = */ 0, "0X2A"},
-      {42, kHex | kUpper | kBase | kPos, /*width = */ 6, "__0X2A"},
-      {42, kHex | kLeft, /*width = */ 0, "2a"},
-      {42, kHex | kLeft, /*width = */ 6, "2a____"},
-      {42, kHex | kLeft | kPos, /*width = */ 0, "2a"},
-      {42, kHex | kLeft | kPos, /*width = */ 6, "2a____"},
-      {42, kHex | kLeft | kBase, /*width = */ 0, "0x2a"},
-      {42, kHex | kLeft | kBase, /*width = */ 6, "0x2a__"},
-      {42, kHex | kLeft | kBase | kPos, /*width = */ 0, "0x2a"},
-      {42, kHex | kLeft | kBase | kPos, /*width = */ 6, "0x2a__"},
-      {42, kHex | kLeft | kUpper, /*width = */ 0, "2A"},
-      {42, kHex | kLeft | kUpper, /*width = */ 6, "2A____"},
-      {42, kHex | kLeft | kUpper | kPos, /*width = */ 0, "2A"},
-      {42, kHex | kLeft | kUpper | kPos, /*width = */ 6, "2A____"},
-      {42, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0X2A"},
-      {42, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0X2A__"},
-      {42, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0X2A"},
-      {42, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0X2A__"},
-      {42, kHex | kInt, /*width = */ 0, "2a"},
-      {42, kHex | kInt, /*width = */ 6, "____2a"},
-      {42, kHex | kInt | kPos, /*width = */ 0, "2a"},
-      {42, kHex | kInt | kPos, /*width = */ 6, "____2a"},
-      {42, kHex | kInt | kBase, /*width = */ 0, "0x2a"},
-      {42, kHex | kInt | kBase, /*width = */ 6, "0x__2a"},
-      {42, kHex | kInt | kBase | kPos, /*width = */ 0, "0x2a"},
-      {42, kHex | kInt | kBase | kPos, /*width = */ 6, "0x__2a"},
-      {42, kHex | kInt | kUpper, /*width = */ 0, "2A"},
-      {42, kHex | kInt | kUpper, /*width = */ 6, "____2A"},
-      {42, kHex | kInt | kUpper | kPos, /*width = */ 0, "2A"},
-      {42, kHex | kInt | kUpper | kPos, /*width = */ 6, "____2A"},
-      {42, kHex | kInt | kUpper | kBase, /*width = */ 0, "0X2A"},
-      {42, kHex | kInt | kUpper | kBase, /*width = */ 6, "0X__2A"},
-      {42, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0X2A"},
-      {42, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "0X__2A"},
-      {42, kHex | kRight, /*width = */ 0, "2a"},
-      {42, kHex | kRight, /*width = */ 6, "____2a"},
-      {42, kHex | kRight | kPos, /*width = */ 0, "2a"},
-      {42, kHex | kRight | kPos, /*width = */ 6, "____2a"},
-      {42, kHex | kRight | kBase, /*width = */ 0, "0x2a"},
-      {42, kHex | kRight | kBase, /*width = */ 6, "__0x2a"},
-      {42, kHex | kRight | kBase | kPos, /*width = */ 0, "0x2a"},
-      {42, kHex | kRight | kBase | kPos, /*width = */ 6, "__0x2a"},
-      {42, kHex | kRight | kUpper, /*width = */ 0, "2A"},
-      {42, kHex | kRight | kUpper, /*width = */ 6, "____2A"},
-      {42, kHex | kRight | kUpper | kPos, /*width = */ 0, "2A"},
-      {42, kHex | kRight | kUpper | kPos, /*width = */ 6, "____2A"},
-      {42, kHex | kRight | kUpper | kBase, /*width = */ 0, "0X2A"},
-      {42, kHex | kRight | kUpper | kBase, /*width = */ 6, "__0X2A"},
-      {42, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0X2A"},
-      {42, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "__0X2A"},
-      {-321, std::ios_base::fmtflags(), /*width = */ 0, "-321"},
-      {-321, std::ios_base::fmtflags(), /*width = */ 6, "__-321"},
-      {-321, kPos, /*width = */ 0, "-321"},
-      {-321, kPos, /*width = */ 6, "__-321"},
-      {-321, kBase, /*width = */ 0, "-321"},
-      {-321, kBase, /*width = */ 6, "__-321"},
-      {-321, kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kBase | kPos, /*width = */ 6, "__-321"},
-      {-321, kUpper, /*width = */ 0, "-321"},
-      {-321, kUpper, /*width = */ 6, "__-321"},
-      {-321, kUpper | kPos, /*width = */ 0, "-321"},
-      {-321, kUpper | kPos, /*width = */ 6, "__-321"},
-      {-321, kUpper | kBase, /*width = */ 0, "-321"},
-      {-321, kUpper | kBase, /*width = */ 6, "__-321"},
-      {-321, kUpper | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kUpper | kBase | kPos, /*width = */ 6, "__-321"},
-      {-321, kLeft, /*width = */ 0, "-321"},
-      {-321, kLeft, /*width = */ 6, "-321__"},
-      {-321, kLeft | kPos, /*width = */ 0, "-321"},
-      {-321, kLeft | kPos, /*width = */ 6, "-321__"},
-      {-321, kLeft | kBase, /*width = */ 0, "-321"},
-      {-321, kLeft | kBase, /*width = */ 6, "-321__"},
-      {-321, kLeft | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kLeft | kBase | kPos, /*width = */ 6, "-321__"},
-      {-321, kLeft | kUpper, /*width = */ 0, "-321"},
-      {-321, kLeft | kUpper, /*width = */ 6, "-321__"},
-      {-321, kLeft | kUpper | kPos, /*width = */ 0, "-321"},
-      {-321, kLeft | kUpper | kPos, /*width = */ 6, "-321__"},
-      {-321, kLeft | kUpper | kBase, /*width = */ 0, "-321"},
-      {-321, kLeft | kUpper | kBase, /*width = */ 6, "-321__"},
-      {-321, kLeft | kUpper | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kLeft | kUpper | kBase | kPos, /*width = */ 6, "-321__"},
-      {-321, kInt, /*width = */ 0, "-321"},
-      {-321, kInt, /*width = */ 6, "-__321"},
-      {-321, kInt | kPos, /*width = */ 0, "-321"},
-      {-321, kInt | kPos, /*width = */ 6, "-__321"},
-      {-321, kInt | kBase, /*width = */ 0, "-321"},
-      {-321, kInt | kBase, /*width = */ 6, "-__321"},
-      {-321, kInt | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kInt | kBase | kPos, /*width = */ 6, "-__321"},
-      {-321, kInt | kUpper, /*width = */ 0, "-321"},
-      {-321, kInt | kUpper, /*width = */ 6, "-__321"},
-      {-321, kInt | kUpper | kPos, /*width = */ 0, "-321"},
-      {-321, kInt | kUpper | kPos, /*width = */ 6, "-__321"},
-      {-321, kInt | kUpper | kBase, /*width = */ 0, "-321"},
-      {-321, kInt | kUpper | kBase, /*width = */ 6, "-__321"},
-      {-321, kInt | kUpper | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kInt | kUpper | kBase | kPos, /*width = */ 6, "-__321"},
-      {-321, kRight, /*width = */ 0, "-321"},
-      {-321, kRight, /*width = */ 6, "__-321"},
-      {-321, kRight | kPos, /*width = */ 0, "-321"},
-      {-321, kRight | kPos, /*width = */ 6, "__-321"},
-      {-321, kRight | kBase, /*width = */ 0, "-321"},
-      {-321, kRight | kBase, /*width = */ 6, "__-321"},
-      {-321, kRight | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kRight | kBase | kPos, /*width = */ 6, "__-321"},
-      {-321, kRight | kUpper, /*width = */ 0, "-321"},
-      {-321, kRight | kUpper, /*width = */ 6, "__-321"},
-      {-321, kRight | kUpper | kPos, /*width = */ 0, "-321"},
-      {-321, kRight | kUpper | kPos, /*width = */ 6, "__-321"},
-      {-321, kRight | kUpper | kBase, /*width = */ 0, "-321"},
-      {-321, kRight | kUpper | kBase, /*width = */ 6, "__-321"},
-      {-321, kRight | kUpper | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kRight | kUpper | kBase | kPos, /*width = */ 6, "__-321"},
-      {-321, kDec, /*width = */ 0, "-321"},
-      {-321, kDec, /*width = */ 6, "__-321"},
-      {-321, kDec | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kPos, /*width = */ 6, "__-321"},
-      {-321, kDec | kBase, /*width = */ 0, "-321"},
-      {-321, kDec | kBase, /*width = */ 6, "__-321"},
-      {-321, kDec | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kBase | kPos, /*width = */ 6, "__-321"},
-      {-321, kDec | kUpper, /*width = */ 0, "-321"},
-      {-321, kDec | kUpper, /*width = */ 6, "__-321"},
-      {-321, kDec | kUpper | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kUpper | kPos, /*width = */ 6, "__-321"},
-      {-321, kDec | kUpper | kBase, /*width = */ 0, "-321"},
-      {-321, kDec | kUpper | kBase, /*width = */ 6, "__-321"},
-      {-321, kDec | kUpper | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kUpper | kBase | kPos, /*width = */ 6, "__-321"},
-      {-321, kDec | kLeft, /*width = */ 0, "-321"},
-      {-321, kDec | kLeft, /*width = */ 6, "-321__"},
-      {-321, kDec | kLeft | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kLeft | kPos, /*width = */ 6, "-321__"},
-      {-321, kDec | kLeft | kBase, /*width = */ 0, "-321"},
-      {-321, kDec | kLeft | kBase, /*width = */ 6, "-321__"},
-      {-321, kDec | kLeft | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kLeft | kBase | kPos, /*width = */ 6, "-321__"},
-      {-321, kDec | kLeft | kUpper, /*width = */ 0, "-321"},
-      {-321, kDec | kLeft | kUpper, /*width = */ 6, "-321__"},
-      {-321, kDec | kLeft | kUpper | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kLeft | kUpper | kPos, /*width = */ 6, "-321__"},
-      {-321, kDec | kLeft | kUpper | kBase, /*width = */ 0, "-321"},
-      {-321, kDec | kLeft | kUpper | kBase, /*width = */ 6, "-321__"},
-      {-321, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "-321__"},
-      {-321, kDec | kInt, /*width = */ 0, "-321"},
-      {-321, kDec | kInt, /*width = */ 6, "-__321"},
-      {-321, kDec | kInt | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kInt | kPos, /*width = */ 6, "-__321"},
-      {-321, kDec | kInt | kBase, /*width = */ 0, "-321"},
-      {-321, kDec | kInt | kBase, /*width = */ 6, "-__321"},
-      {-321, kDec | kInt | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kInt | kBase | kPos, /*width = */ 6, "-__321"},
-      {-321, kDec | kInt | kUpper, /*width = */ 0, "-321"},
-      {-321, kDec | kInt | kUpper, /*width = */ 6, "-__321"},
-      {-321, kDec | kInt | kUpper | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kInt | kUpper | kPos, /*width = */ 6, "-__321"},
-      {-321, kDec | kInt | kUpper | kBase, /*width = */ 0, "-321"},
-      {-321, kDec | kInt | kUpper | kBase, /*width = */ 6, "-__321"},
-      {-321, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "-__321"},
-      {-321, kDec | kRight, /*width = */ 0, "-321"},
-      {-321, kDec | kRight, /*width = */ 6, "__-321"},
-      {-321, kDec | kRight | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kRight | kPos, /*width = */ 6, "__-321"},
-      {-321, kDec | kRight | kBase, /*width = */ 0, "-321"},
-      {-321, kDec | kRight | kBase, /*width = */ 6, "__-321"},
-      {-321, kDec | kRight | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kRight | kBase | kPos, /*width = */ 6, "__-321"},
-      {-321, kDec | kRight | kUpper, /*width = */ 0, "-321"},
-      {-321, kDec | kRight | kUpper, /*width = */ 6, "__-321"},
-      {-321, kDec | kRight | kUpper | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kRight | kUpper | kPos, /*width = */ 6, "__-321"},
-      {-321, kDec | kRight | kUpper | kBase, /*width = */ 0, "-321"},
-      {-321, kDec | kRight | kUpper | kBase, /*width = */ 6, "__-321"},
-      {-321, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "-321"},
-      {-321, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "__-321"}};
-}
-
-std::vector<Uint128TestCase> GetUint128FormatCases() {
-  return {
-      {0, std::ios_base::fmtflags(), /*width = */ 0, "0"},
-      {0, std::ios_base::fmtflags(), /*width = */ 6, "_____0"},
-      {0, kPos, /*width = */ 0, "0"},
-      {0, kPos, /*width = */ 6, "_____0"},
-      {0, kBase, /*width = */ 0, "0"},
-      {0, kBase, /*width = */ 6, "_____0"},
-      {0, kBase | kPos, /*width = */ 0, "0"},
-      {0, kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kUpper, /*width = */ 0, "0"},
-      {0, kUpper, /*width = */ 6, "_____0"},
-      {0, kUpper | kPos, /*width = */ 0, "0"},
-      {0, kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kUpper | kBase, /*width = */ 0, "0"},
-      {0, kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kLeft, /*width = */ 0, "0"},
-      {0, kLeft, /*width = */ 6, "0_____"},
-      {0, kLeft | kPos, /*width = */ 0, "0"},
-      {0, kLeft | kPos, /*width = */ 6, "0_____"},
-      {0, kLeft | kBase, /*width = */ 0, "0"},
-      {0, kLeft | kBase, /*width = */ 6, "0_____"},
-      {0, kLeft | kBase | kPos, /*width = */ 0, "0"},
-      {0, kLeft | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kLeft | kUpper, /*width = */ 0, "0"},
-      {0, kLeft | kUpper, /*width = */ 6, "0_____"},
-      {0, kLeft | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kLeft | kUpper | kPos, /*width = */ 6, "0_____"},
-      {0, kLeft | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
-      {0, kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kInt, /*width = */ 0, "0"},
-      {0, kInt, /*width = */ 6, "_____0"},
-      {0, kInt | kPos, /*width = */ 0, "0"},
-      {0, kInt | kPos, /*width = */ 6, "_____0"},
-      {0, kInt | kBase, /*width = */ 0, "0"},
-      {0, kInt | kBase, /*width = */ 6, "_____0"},
-      {0, kInt | kBase | kPos, /*width = */ 0, "0"},
-      {0, kInt | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kInt | kUpper, /*width = */ 0, "0"},
-      {0, kInt | kUpper, /*width = */ 6, "_____0"},
-      {0, kInt | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kInt | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kInt | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kInt | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kInt | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kRight, /*width = */ 0, "0"},
-      {0, kRight, /*width = */ 6, "_____0"},
-      {0, kRight | kPos, /*width = */ 0, "0"},
-      {0, kRight | kPos, /*width = */ 6, "_____0"},
-      {0, kRight | kBase, /*width = */ 0, "0"},
-      {0, kRight | kBase, /*width = */ 6, "_____0"},
-      {0, kRight | kBase | kPos, /*width = */ 0, "0"},
-      {0, kRight | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kRight | kUpper, /*width = */ 0, "0"},
-      {0, kRight | kUpper, /*width = */ 6, "_____0"},
-      {0, kRight | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kRight | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kRight | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kRight | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kRight | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kDec, /*width = */ 0, "0"},
-      {0, kDec, /*width = */ 6, "_____0"},
-      {0, kDec | kPos, /*width = */ 0, "0"},
-      {0, kDec | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kBase, /*width = */ 0, "0"},
-      {0, kDec | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kBase | kPos, /*width = */ 0, "0"},
-      {0, kDec | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kUpper, /*width = */ 0, "0"},
-      {0, kDec | kUpper, /*width = */ 6, "_____0"},
-      {0, kDec | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kDec | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kDec | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kDec | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kLeft, /*width = */ 0, "0"},
-      {0, kDec | kLeft, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kPos, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kPos, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kBase, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kBase, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kBase | kPos, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kUpper, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kUpper, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kUpper | kPos, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
-      {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kDec | kInt, /*width = */ 0, "0"},
-      {0, kDec | kInt, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kPos, /*width = */ 0, "0"},
-      {0, kDec | kInt | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kBase, /*width = */ 0, "0"},
-      {0, kDec | kInt | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kBase | kPos, /*width = */ 0, "0"},
-      {0, kDec | kInt | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kUpper, /*width = */ 0, "0"},
-      {0, kDec | kInt | kUpper, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kDec | kInt | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kDec | kInt | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kRight, /*width = */ 0, "0"},
-      {0, kDec | kRight, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kPos, /*width = */ 0, "0"},
-      {0, kDec | kRight | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kBase, /*width = */ 0, "0"},
-      {0, kDec | kRight | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kBase | kPos, /*width = */ 0, "0"},
-      {0, kDec | kRight | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kUpper, /*width = */ 0, "0"},
-      {0, kDec | kRight | kUpper, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kDec | kRight | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kDec | kRight | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct, /*width = */ 0, "0"},
-      {0, kOct, /*width = */ 6, "_____0"},
-      {0, kOct | kPos, /*width = */ 0, "0"},
-      {0, kOct | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kBase, /*width = */ 0, "0"},
-      {0, kOct | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kUpper, /*width = */ 0, "0"},
-      {0, kOct | kUpper, /*width = */ 6, "_____0"},
-      {0, kOct | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kOct | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kOct | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kLeft, /*width = */ 0, "0"},
-      {0, kOct | kLeft, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kPos, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kPos, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kBase, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kBase, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kUpper, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kUpper, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kUpper | kPos, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
-      {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kOct | kInt, /*width = */ 0, "0"},
-      {0, kOct | kInt, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kPos, /*width = */ 0, "0"},
-      {0, kOct | kInt | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kBase, /*width = */ 0, "0"},
-      {0, kOct | kInt | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kInt | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kUpper, /*width = */ 0, "0"},
-      {0, kOct | kInt | kUpper, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kOct | kInt | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kOct | kInt | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kRight, /*width = */ 0, "0"},
-      {0, kOct | kRight, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kPos, /*width = */ 0, "0"},
-      {0, kOct | kRight | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kBase, /*width = */ 0, "0"},
-      {0, kOct | kRight | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kRight | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kUpper, /*width = */ 0, "0"},
-      {0, kOct | kRight | kUpper, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kOct | kRight | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kOct | kRight | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex, /*width = */ 0, "0"},
-      {0, kHex, /*width = */ 6, "_____0"},
-      {0, kHex | kPos, /*width = */ 0, "0"},
-      {0, kHex | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kBase, /*width = */ 0, "0"},
-      {0, kHex | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kUpper, /*width = */ 0, "0"},
-      {0, kHex | kUpper, /*width = */ 6, "_____0"},
-      {0, kHex | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kHex | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kHex | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kLeft, /*width = */ 0, "0"},
-      {0, kHex | kLeft, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kPos, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kPos, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kBase, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kBase, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kUpper, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kUpper, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kUpper | kPos, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
-      {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"},
-      {0, kHex | kInt, /*width = */ 0, "0"},
-      {0, kHex | kInt, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kPos, /*width = */ 0, "0"},
-      {0, kHex | kInt | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kBase, /*width = */ 0, "0"},
-      {0, kHex | kInt | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kInt | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kUpper, /*width = */ 0, "0"},
-      {0, kHex | kInt | kUpper, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kHex | kInt | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kHex | kInt | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kRight, /*width = */ 0, "0"},
-      {0, kHex | kRight, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kPos, /*width = */ 0, "0"},
-      {0, kHex | kRight | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kBase, /*width = */ 0, "0"},
-      {0, kHex | kRight | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kRight | kBase | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kUpper, /*width = */ 0, "0"},
-      {0, kHex | kRight | kUpper, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kUpper | kPos, /*width = */ 0, "0"},
-      {0, kHex | kRight | kUpper | kPos, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kUpper | kBase, /*width = */ 0, "0"},
-      {0, kHex | kRight | kUpper | kBase, /*width = */ 6, "_____0"},
-      {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"},
-      {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
-      {37, std::ios_base::fmtflags(), /*width = */ 0, "37"},
-      {37, std::ios_base::fmtflags(), /*width = */ 6, "____37"},
-      {37, kPos, /*width = */ 0, "37"},
-      {37, kPos, /*width = */ 6, "____37"},
-      {37, kBase, /*width = */ 0, "37"},
-      {37, kBase, /*width = */ 6, "____37"},
-      {37, kBase | kPos, /*width = */ 0, "37"},
-      {37, kBase | kPos, /*width = */ 6, "____37"},
-      {37, kUpper, /*width = */ 0, "37"},
-      {37, kUpper, /*width = */ 6, "____37"},
-      {37, kUpper | kPos, /*width = */ 0, "37"},
-      {37, kUpper | kPos, /*width = */ 6, "____37"},
-      {37, kUpper | kBase, /*width = */ 0, "37"},
-      {37, kUpper | kBase, /*width = */ 6, "____37"},
-      {37, kUpper | kBase | kPos, /*width = */ 0, "37"},
-      {37, kUpper | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kLeft, /*width = */ 0, "37"},
-      {37, kLeft, /*width = */ 6, "37____"},
-      {37, kLeft | kPos, /*width = */ 0, "37"},
-      {37, kLeft | kPos, /*width = */ 6, "37____"},
-      {37, kLeft | kBase, /*width = */ 0, "37"},
-      {37, kLeft | kBase, /*width = */ 6, "37____"},
-      {37, kLeft | kBase | kPos, /*width = */ 0, "37"},
-      {37, kLeft | kBase | kPos, /*width = */ 6, "37____"},
-      {37, kLeft | kUpper, /*width = */ 0, "37"},
-      {37, kLeft | kUpper, /*width = */ 6, "37____"},
-      {37, kLeft | kUpper | kPos, /*width = */ 0, "37"},
-      {37, kLeft | kUpper | kPos, /*width = */ 6, "37____"},
-      {37, kLeft | kUpper | kBase, /*width = */ 0, "37"},
-      {37, kLeft | kUpper | kBase, /*width = */ 6, "37____"},
-      {37, kLeft | kUpper | kBase | kPos, /*width = */ 0, "37"},
-      {37, kLeft | kUpper | kBase | kPos, /*width = */ 6, "37____"},
-      {37, kInt, /*width = */ 0, "37"},
-      {37, kInt, /*width = */ 6, "____37"},
-      {37, kInt | kPos, /*width = */ 0, "37"},
-      {37, kInt | kPos, /*width = */ 6, "____37"},
-      {37, kInt | kBase, /*width = */ 0, "37"},
-      {37, kInt | kBase, /*width = */ 6, "____37"},
-      {37, kInt | kBase | kPos, /*width = */ 0, "37"},
-      {37, kInt | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kInt | kUpper, /*width = */ 0, "37"},
-      {37, kInt | kUpper, /*width = */ 6, "____37"},
-      {37, kInt | kUpper | kPos, /*width = */ 0, "37"},
-      {37, kInt | kUpper | kPos, /*width = */ 6, "____37"},
-      {37, kInt | kUpper | kBase, /*width = */ 0, "37"},
-      {37, kInt | kUpper | kBase, /*width = */ 6, "____37"},
-      {37, kInt | kUpper | kBase | kPos, /*width = */ 0, "37"},
-      {37, kInt | kUpper | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kRight, /*width = */ 0, "37"},
-      {37, kRight, /*width = */ 6, "____37"},
-      {37, kRight | kPos, /*width = */ 0, "37"},
-      {37, kRight | kPos, /*width = */ 6, "____37"},
-      {37, kRight | kBase, /*width = */ 0, "37"},
-      {37, kRight | kBase, /*width = */ 6, "____37"},
-      {37, kRight | kBase | kPos, /*width = */ 0, "37"},
-      {37, kRight | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kRight | kUpper, /*width = */ 0, "37"},
-      {37, kRight | kUpper, /*width = */ 6, "____37"},
-      {37, kRight | kUpper | kPos, /*width = */ 0, "37"},
-      {37, kRight | kUpper | kPos, /*width = */ 6, "____37"},
-      {37, kRight | kUpper | kBase, /*width = */ 0, "37"},
-      {37, kRight | kUpper | kBase, /*width = */ 6, "____37"},
-      {37, kRight | kUpper | kBase | kPos, /*width = */ 0, "37"},
-      {37, kRight | kUpper | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kDec, /*width = */ 0, "37"},
-      {37, kDec, /*width = */ 6, "____37"},
-      {37, kDec | kPos, /*width = */ 0, "37"},
-      {37, kDec | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kBase, /*width = */ 0, "37"},
-      {37, kDec | kBase, /*width = */ 6, "____37"},
-      {37, kDec | kBase | kPos, /*width = */ 0, "37"},
-      {37, kDec | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kUpper, /*width = */ 0, "37"},
-      {37, kDec | kUpper, /*width = */ 6, "____37"},
-      {37, kDec | kUpper | kPos, /*width = */ 0, "37"},
-      {37, kDec | kUpper | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kUpper | kBase, /*width = */ 0, "37"},
-      {37, kDec | kUpper | kBase, /*width = */ 6, "____37"},
-      {37, kDec | kUpper | kBase | kPos, /*width = */ 0, "37"},
-      {37, kDec | kUpper | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kLeft, /*width = */ 0, "37"},
-      {37, kDec | kLeft, /*width = */ 6, "37____"},
-      {37, kDec | kLeft | kPos, /*width = */ 0, "37"},
-      {37, kDec | kLeft | kPos, /*width = */ 6, "37____"},
-      {37, kDec | kLeft | kBase, /*width = */ 0, "37"},
-      {37, kDec | kLeft | kBase, /*width = */ 6, "37____"},
-      {37, kDec | kLeft | kBase | kPos, /*width = */ 0, "37"},
-      {37, kDec | kLeft | kBase | kPos, /*width = */ 6, "37____"},
-      {37, kDec | kLeft | kUpper, /*width = */ 0, "37"},
-      {37, kDec | kLeft | kUpper, /*width = */ 6, "37____"},
-      {37, kDec | kLeft | kUpper | kPos, /*width = */ 0, "37"},
-      {37, kDec | kLeft | kUpper | kPos, /*width = */ 6, "37____"},
-      {37, kDec | kLeft | kUpper | kBase, /*width = */ 0, "37"},
-      {37, kDec | kLeft | kUpper | kBase, /*width = */ 6, "37____"},
-      {37, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "37"},
-      {37, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "37____"},
-      {37, kDec | kInt, /*width = */ 0, "37"},
-      {37, kDec | kInt, /*width = */ 6, "____37"},
-      {37, kDec | kInt | kPos, /*width = */ 0, "37"},
-      {37, kDec | kInt | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kInt | kBase, /*width = */ 0, "37"},
-      {37, kDec | kInt | kBase, /*width = */ 6, "____37"},
-      {37, kDec | kInt | kBase | kPos, /*width = */ 0, "37"},
-      {37, kDec | kInt | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kInt | kUpper, /*width = */ 0, "37"},
-      {37, kDec | kInt | kUpper, /*width = */ 6, "____37"},
-      {37, kDec | kInt | kUpper | kPos, /*width = */ 0, "37"},
-      {37, kDec | kInt | kUpper | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kInt | kUpper | kBase, /*width = */ 0, "37"},
-      {37, kDec | kInt | kUpper | kBase, /*width = */ 6, "____37"},
-      {37, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "37"},
-      {37, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kRight, /*width = */ 0, "37"},
-      {37, kDec | kRight, /*width = */ 6, "____37"},
-      {37, kDec | kRight | kPos, /*width = */ 0, "37"},
-      {37, kDec | kRight | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kRight | kBase, /*width = */ 0, "37"},
-      {37, kDec | kRight | kBase, /*width = */ 6, "____37"},
-      {37, kDec | kRight | kBase | kPos, /*width = */ 0, "37"},
-      {37, kDec | kRight | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kRight | kUpper, /*width = */ 0, "37"},
-      {37, kDec | kRight | kUpper, /*width = */ 6, "____37"},
-      {37, kDec | kRight | kUpper | kPos, /*width = */ 0, "37"},
-      {37, kDec | kRight | kUpper | kPos, /*width = */ 6, "____37"},
-      {37, kDec | kRight | kUpper | kBase, /*width = */ 0, "37"},
-      {37, kDec | kRight | kUpper | kBase, /*width = */ 6, "____37"},
-      {37, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "37"},
-      {37, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "____37"},
-      {37, kOct, /*width = */ 0, "45"},
-      {37, kOct, /*width = */ 6, "____45"},
-      {37, kOct | kPos, /*width = */ 0, "45"},
-      {37, kOct | kPos, /*width = */ 6, "____45"},
-      {37, kOct | kBase, /*width = */ 0, "045"},
-      {37, kOct | kBase, /*width = */ 6, "___045"},
-      {37, kOct | kBase | kPos, /*width = */ 0, "045"},
-      {37, kOct | kBase | kPos, /*width = */ 6, "___045"},
-      {37, kOct | kUpper, /*width = */ 0, "45"},
-      {37, kOct | kUpper, /*width = */ 6, "____45"},
-      {37, kOct | kUpper | kPos, /*width = */ 0, "45"},
-      {37, kOct | kUpper | kPos, /*width = */ 6, "____45"},
-      {37, kOct | kUpper | kBase, /*width = */ 0, "045"},
-      {37, kOct | kUpper | kBase, /*width = */ 6, "___045"},
-      {37, kOct | kUpper | kBase | kPos, /*width = */ 0, "045"},
-      {37, kOct | kUpper | kBase | kPos, /*width = */ 6, "___045"},
-      {37, kOct | kLeft, /*width = */ 0, "45"},
-      {37, kOct | kLeft, /*width = */ 6, "45____"},
-      {37, kOct | kLeft | kPos, /*width = */ 0, "45"},
-      {37, kOct | kLeft | kPos, /*width = */ 6, "45____"},
-      {37, kOct | kLeft | kBase, /*width = */ 0, "045"},
-      {37, kOct | kLeft | kBase, /*width = */ 6, "045___"},
-      {37, kOct | kLeft | kBase | kPos, /*width = */ 0, "045"},
-      {37, kOct | kLeft | kBase | kPos, /*width = */ 6, "045___"},
-      {37, kOct | kLeft | kUpper, /*width = */ 0, "45"},
-      {37, kOct | kLeft | kUpper, /*width = */ 6, "45____"},
-      {37, kOct | kLeft | kUpper | kPos, /*width = */ 0, "45"},
-      {37, kOct | kLeft | kUpper | kPos, /*width = */ 6, "45____"},
-      {37, kOct | kLeft | kUpper | kBase, /*width = */ 0, "045"},
-      {37, kOct | kLeft | kUpper | kBase, /*width = */ 6, "045___"},
-      {37, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "045"},
-      {37, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "045___"},
-      {37, kOct | kInt, /*width = */ 0, "45"},
-      {37, kOct | kInt, /*width = */ 6, "____45"},
-      {37, kOct | kInt | kPos, /*width = */ 0, "45"},
-      {37, kOct | kInt | kPos, /*width = */ 6, "____45"},
-      {37, kOct | kInt | kBase, /*width = */ 0, "045"},
-      {37, kOct | kInt | kBase, /*width = */ 6, "___045"},
-      {37, kOct | kInt | kBase | kPos, /*width = */ 0, "045"},
-      {37, kOct | kInt | kBase | kPos, /*width = */ 6, "___045"},
-      {37, kOct | kInt | kUpper, /*width = */ 0, "45"},
-      {37, kOct | kInt | kUpper, /*width = */ 6, "____45"},
-      {37, kOct | kInt | kUpper | kPos, /*width = */ 0, "45"},
-      {37, kOct | kInt | kUpper | kPos, /*width = */ 6, "____45"},
-      {37, kOct | kInt | kUpper | kBase, /*width = */ 0, "045"},
-      {37, kOct | kInt | kUpper | kBase, /*width = */ 6, "___045"},
-      {37, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "045"},
-      {37, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "___045"},
-      {37, kOct | kRight, /*width = */ 0, "45"},
-      {37, kOct | kRight, /*width = */ 6, "____45"},
-      {37, kOct | kRight | kPos, /*width = */ 0, "45"},
-      {37, kOct | kRight | kPos, /*width = */ 6, "____45"},
-      {37, kOct | kRight | kBase, /*width = */ 0, "045"},
-      {37, kOct | kRight | kBase, /*width = */ 6, "___045"},
-      {37, kOct | kRight | kBase | kPos, /*width = */ 0, "045"},
-      {37, kOct | kRight | kBase | kPos, /*width = */ 6, "___045"},
-      {37, kOct | kRight | kUpper, /*width = */ 0, "45"},
-      {37, kOct | kRight | kUpper, /*width = */ 6, "____45"},
-      {37, kOct | kRight | kUpper | kPos, /*width = */ 0, "45"},
-      {37, kOct | kRight | kUpper | kPos, /*width = */ 6, "____45"},
-      {37, kOct | kRight | kUpper | kBase, /*width = */ 0, "045"},
-      {37, kOct | kRight | kUpper | kBase, /*width = */ 6, "___045"},
-      {37, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "045"},
-      {37, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "___045"},
-      {37, kHex, /*width = */ 0, "25"},
-      {37, kHex, /*width = */ 6, "____25"},
-      {37, kHex | kPos, /*width = */ 0, "25"},
-      {37, kHex | kPos, /*width = */ 6, "____25"},
-      {37, kHex | kBase, /*width = */ 0, "0x25"},
-      {37, kHex | kBase, /*width = */ 6, "__0x25"},
-      {37, kHex | kBase | kPos, /*width = */ 0, "0x25"},
-      {37, kHex | kBase | kPos, /*width = */ 6, "__0x25"},
-      {37, kHex | kUpper, /*width = */ 0, "25"},
-      {37, kHex | kUpper, /*width = */ 6, "____25"},
-      {37, kHex | kUpper | kPos, /*width = */ 0, "25"},
-      {37, kHex | kUpper | kPos, /*width = */ 6, "____25"},
-      {37, kHex | kUpper | kBase, /*width = */ 0, "0X25"},
-      {37, kHex | kUpper | kBase, /*width = */ 6, "__0X25"},
-      {37, kHex | kUpper | kBase | kPos, /*width = */ 0, "0X25"},
-      {37, kHex | kUpper | kBase | kPos, /*width = */ 6, "__0X25"},
-      {37, kHex | kLeft, /*width = */ 0, "25"},
-      {37, kHex | kLeft, /*width = */ 6, "25____"},
-      {37, kHex | kLeft | kPos, /*width = */ 0, "25"},
-      {37, kHex | kLeft | kPos, /*width = */ 6, "25____"},
-      {37, kHex | kLeft | kBase, /*width = */ 0, "0x25"},
-      {37, kHex | kLeft | kBase, /*width = */ 6, "0x25__"},
-      {37, kHex | kLeft | kBase | kPos, /*width = */ 0, "0x25"},
-      {37, kHex | kLeft | kBase | kPos, /*width = */ 6, "0x25__"},
-      {37, kHex | kLeft | kUpper, /*width = */ 0, "25"},
-      {37, kHex | kLeft | kUpper, /*width = */ 6, "25____"},
-      {37, kHex | kLeft | kUpper | kPos, /*width = */ 0, "25"},
-      {37, kHex | kLeft | kUpper | kPos, /*width = */ 6, "25____"},
-      {37, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0X25"},
-      {37, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0X25__"},
-      {37, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0X25"},
-      {37, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0X25__"},
-      {37, kHex | kInt, /*width = */ 0, "25"},
-      {37, kHex | kInt, /*width = */ 6, "____25"},
-      {37, kHex | kInt | kPos, /*width = */ 0, "25"},
-      {37, kHex | kInt | kPos, /*width = */ 6, "____25"},
-      {37, kHex | kInt | kBase, /*width = */ 0, "0x25"},
-      {37, kHex | kInt | kBase, /*width = */ 6, "0x__25"},
-      {37, kHex | kInt | kBase | kPos, /*width = */ 0, "0x25"},
-      {37, kHex | kInt | kBase | kPos, /*width = */ 6, "0x__25"},
-      {37, kHex | kInt | kUpper, /*width = */ 0, "25"},
-      {37, kHex | kInt | kUpper, /*width = */ 6, "____25"},
-      {37, kHex | kInt | kUpper | kPos, /*width = */ 0, "25"},
-      {37, kHex | kInt | kUpper | kPos, /*width = */ 6, "____25"},
-      {37, kHex | kInt | kUpper | kBase, /*width = */ 0, "0X25"},
-      {37, kHex | kInt | kUpper | kBase, /*width = */ 6, "0X__25"},
-      {37, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0X25"},
-      {37, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "0X__25"},
-      {37, kHex | kRight, /*width = */ 0, "25"},
-      {37, kHex | kRight, /*width = */ 6, "____25"},
-      {37, kHex | kRight | kPos, /*width = */ 0, "25"},
-      {37, kHex | kRight | kPos, /*width = */ 6, "____25"},
-      {37, kHex | kRight | kBase, /*width = */ 0, "0x25"},
-      {37, kHex | kRight | kBase, /*width = */ 6, "__0x25"},
-      {37, kHex | kRight | kBase | kPos, /*width = */ 0, "0x25"},
-      {37, kHex | kRight | kBase | kPos, /*width = */ 6, "__0x25"},
-      {37, kHex | kRight | kUpper, /*width = */ 0, "25"},
-      {37, kHex | kRight | kUpper, /*width = */ 6, "____25"},
-      {37, kHex | kRight | kUpper | kPos, /*width = */ 0, "25"},
-      {37, kHex | kRight | kUpper | kPos, /*width = */ 6, "____25"},
-      {37, kHex | kRight | kUpper | kBase, /*width = */ 0, "0X25"},
-      {37, kHex | kRight | kUpper | kBase, /*width = */ 6, "__0X25"},
-      {37, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0X25"},
-      {37, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "__0X25"}};
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/numeric/int128_test.cc b/third_party/abseil_cpp/absl/numeric/int128_test.cc
deleted file mode 100644
index bc86c714ac..0000000000
--- a/third_party/abseil_cpp/absl/numeric/int128_test.cc
+++ /dev/null
@@ -1,1225 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/numeric/int128.h"
-
-#include <algorithm>
-#include <limits>
-#include <random>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/cycleclock.h"
-#include "absl/hash/hash_testing.h"
-#include "absl/meta/type_traits.h"
-
-#if defined(_MSC_VER) && _MSC_VER == 1900
-// Disable "unary minus operator applied to unsigned type" warnings in Microsoft
-// Visual C++ 14 (2015).
-#pragma warning(disable:4146)
-#endif
-
-namespace {
-
-template <typename T>
-class Uint128IntegerTraitsTest : public ::testing::Test {};
-typedef ::testing::Types<bool, char, signed char, unsigned char, char16_t,
-                         char32_t, wchar_t,
-                         short,           // NOLINT(runtime/int)
-                         unsigned short,  // NOLINT(runtime/int)
-                         int, unsigned int,
-                         long,                // NOLINT(runtime/int)
-                         unsigned long,       // NOLINT(runtime/int)
-                         long long,           // NOLINT(runtime/int)
-                         unsigned long long>  // NOLINT(runtime/int)
-    IntegerTypes;
-
-template <typename T>
-class Uint128FloatTraitsTest : public ::testing::Test {};
-typedef ::testing::Types<float, double, long double> FloatingPointTypes;
-
-TYPED_TEST_SUITE(Uint128IntegerTraitsTest, IntegerTypes);
-
-TYPED_TEST(Uint128IntegerTraitsTest, ConstructAssignTest) {
-  static_assert(std::is_constructible<absl::uint128, TypeParam>::value,
-                "absl::uint128 must be constructible from TypeParam");
-  static_assert(std::is_assignable<absl::uint128&, TypeParam>::value,
-                "absl::uint128 must be assignable from TypeParam");
-  static_assert(!std::is_assignable<TypeParam&, absl::uint128>::value,
-                "TypeParam must not be assignable from absl::uint128");
-}
-
-TYPED_TEST_SUITE(Uint128FloatTraitsTest, FloatingPointTypes);
-
-TYPED_TEST(Uint128FloatTraitsTest, ConstructAssignTest) {
-  static_assert(std::is_constructible<absl::uint128, TypeParam>::value,
-                "absl::uint128 must be constructible from TypeParam");
-  static_assert(!std::is_assignable<absl::uint128&, TypeParam>::value,
-                "absl::uint128 must not be assignable from TypeParam");
-  static_assert(!std::is_assignable<TypeParam&, absl::uint128>::value,
-                "TypeParam must not be assignable from absl::uint128");
-}
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-// These type traits done separately as TYPED_TEST requires typeinfo, and not
-// all platforms have this for __int128 even though they define the type.
-TEST(Uint128, IntrinsicTypeTraitsTest) {
-  static_assert(std::is_constructible<absl::uint128, __int128>::value,
-                "absl::uint128 must be constructible from __int128");
-  static_assert(std::is_assignable<absl::uint128&, __int128>::value,
-                "absl::uint128 must be assignable from __int128");
-  static_assert(!std::is_assignable<__int128&, absl::uint128>::value,
-                "__int128 must not be assignable from absl::uint128");
-
-  static_assert(std::is_constructible<absl::uint128, unsigned __int128>::value,
-                "absl::uint128 must be constructible from unsigned __int128");
-  static_assert(std::is_assignable<absl::uint128&, unsigned __int128>::value,
-                "absl::uint128 must be assignable from unsigned __int128");
-  static_assert(!std::is_assignable<unsigned __int128&, absl::uint128>::value,
-                "unsigned __int128 must not be assignable from absl::uint128");
-}
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-TEST(Uint128, TrivialTraitsTest) {
-  static_assert(absl::is_trivially_default_constructible<absl::uint128>::value,
-                "");
-  static_assert(absl::is_trivially_copy_constructible<absl::uint128>::value,
-                "");
-  static_assert(absl::is_trivially_copy_assignable<absl::uint128>::value, "");
-  static_assert(std::is_trivially_destructible<absl::uint128>::value, "");
-}
-
-TEST(Uint128, AllTests) {
-  absl::uint128 zero = 0;
-  absl::uint128 one = 1;
-  absl::uint128 one_2arg = absl::MakeUint128(0, 1);
-  absl::uint128 two = 2;
-  absl::uint128 three = 3;
-  absl::uint128 big = absl::MakeUint128(2000, 2);
-  absl::uint128 big_minus_one = absl::MakeUint128(2000, 1);
-  absl::uint128 bigger = absl::MakeUint128(2001, 1);
-  absl::uint128 biggest = absl::Uint128Max();
-  absl::uint128 high_low = absl::MakeUint128(1, 0);
-  absl::uint128 low_high =
-      absl::MakeUint128(0, std::numeric_limits<uint64_t>::max());
-  EXPECT_LT(one, two);
-  EXPECT_GT(two, one);
-  EXPECT_LT(one, big);
-  EXPECT_LT(one, big);
-  EXPECT_EQ(one, one_2arg);
-  EXPECT_NE(one, two);
-  EXPECT_GT(big, one);
-  EXPECT_GE(big, two);
-  EXPECT_GE(big, big_minus_one);
-  EXPECT_GT(big, big_minus_one);
-  EXPECT_LT(big_minus_one, big);
-  EXPECT_LE(big_minus_one, big);
-  EXPECT_NE(big_minus_one, big);
-  EXPECT_LT(big, biggest);
-  EXPECT_LE(big, biggest);
-  EXPECT_GT(biggest, big);
-  EXPECT_GE(biggest, big);
-  EXPECT_EQ(big, ~~big);
-  EXPECT_EQ(one, one | one);
-  EXPECT_EQ(big, big | big);
-  EXPECT_EQ(one, one | zero);
-  EXPECT_EQ(one, one & one);
-  EXPECT_EQ(big, big & big);
-  EXPECT_EQ(zero, one & zero);
-  EXPECT_EQ(zero, big & ~big);
-  EXPECT_EQ(zero, one ^ one);
-  EXPECT_EQ(zero, big ^ big);
-  EXPECT_EQ(one, one ^ zero);
-
-  // Shift operators.
-  EXPECT_EQ(big, big << 0);
-  EXPECT_EQ(big, big >> 0);
-  EXPECT_GT(big << 1, big);
-  EXPECT_LT(big >> 1, big);
-  EXPECT_EQ(big, (big << 10) >> 10);
-  EXPECT_EQ(big, (big >> 1) << 1);
-  EXPECT_EQ(one, (one << 80) >> 80);
-  EXPECT_EQ(zero, (one >> 80) << 80);
-
-  // Shift assignments.
-  absl::uint128 big_copy = big;
-  EXPECT_EQ(big << 0, big_copy <<= 0);
-  big_copy = big;
-  EXPECT_EQ(big >> 0, big_copy >>= 0);
-  big_copy = big;
-  EXPECT_EQ(big << 1, big_copy <<= 1);
-  big_copy = big;
-  EXPECT_EQ(big >> 1, big_copy >>= 1);
-  big_copy = big;
-  EXPECT_EQ(big << 10, big_copy <<= 10);
-  big_copy = big;
-  EXPECT_EQ(big >> 10, big_copy >>= 10);
-  big_copy = big;
-  EXPECT_EQ(big << 64, big_copy <<= 64);
-  big_copy = big;
-  EXPECT_EQ(big >> 64, big_copy >>= 64);
-  big_copy = big;
-  EXPECT_EQ(big << 73, big_copy <<= 73);
-  big_copy = big;
-  EXPECT_EQ(big >> 73, big_copy >>= 73);
-
-  EXPECT_EQ(absl::Uint128High64(biggest), std::numeric_limits<uint64_t>::max());
-  EXPECT_EQ(absl::Uint128Low64(biggest), std::numeric_limits<uint64_t>::max());
-  EXPECT_EQ(zero + one, one);
-  EXPECT_EQ(one + one, two);
-  EXPECT_EQ(big_minus_one + one, big);
-  EXPECT_EQ(one - one, zero);
-  EXPECT_EQ(one - zero, one);
-  EXPECT_EQ(zero - one, biggest);
-  EXPECT_EQ(big - big, zero);
-  EXPECT_EQ(big - one, big_minus_one);
-  EXPECT_EQ(big + std::numeric_limits<uint64_t>::max(), bigger);
-  EXPECT_EQ(biggest + 1, zero);
-  EXPECT_EQ(zero - 1, biggest);
-  EXPECT_EQ(high_low - one, low_high);
-  EXPECT_EQ(low_high + one, high_low);
-  EXPECT_EQ(absl::Uint128High64((absl::uint128(1) << 64) - 1), 0);
-  EXPECT_EQ(absl::Uint128Low64((absl::uint128(1) << 64) - 1),
-            std::numeric_limits<uint64_t>::max());
-  EXPECT_TRUE(!!one);
-  EXPECT_TRUE(!!high_low);
-  EXPECT_FALSE(!!zero);
-  EXPECT_FALSE(!one);
-  EXPECT_FALSE(!high_low);
-  EXPECT_TRUE(!zero);
-  EXPECT_TRUE(zero == 0);       // NOLINT(readability/check)
-  EXPECT_FALSE(zero != 0);      // NOLINT(readability/check)
-  EXPECT_FALSE(one == 0);       // NOLINT(readability/check)
-  EXPECT_TRUE(one != 0);        // NOLINT(readability/check)
-  EXPECT_FALSE(high_low == 0);  // NOLINT(readability/check)
-  EXPECT_TRUE(high_low != 0);   // NOLINT(readability/check)
-
-  absl::uint128 test = zero;
-  EXPECT_EQ(++test, one);
-  EXPECT_EQ(test, one);
-  EXPECT_EQ(test++, one);
-  EXPECT_EQ(test, two);
-  EXPECT_EQ(test -= 2, zero);
-  EXPECT_EQ(test, zero);
-  EXPECT_EQ(test += 2, two);
-  EXPECT_EQ(test, two);
-  EXPECT_EQ(--test, one);
-  EXPECT_EQ(test, one);
-  EXPECT_EQ(test--, one);
-  EXPECT_EQ(test, zero);
-  EXPECT_EQ(test |= three, three);
-  EXPECT_EQ(test &= one, one);
-  EXPECT_EQ(test ^= three, two);
-  EXPECT_EQ(test >>= 1, one);
-  EXPECT_EQ(test <<= 1, two);
-
-  EXPECT_EQ(big, -(-big));
-  EXPECT_EQ(two, -((-one) - 1));
-  EXPECT_EQ(absl::Uint128Max(), -one);
-  EXPECT_EQ(zero, -zero);
-
-  EXPECT_EQ(absl::Uint128Max(), absl::kuint128max);
-}
-
-TEST(Uint128, ConversionTests) {
-  EXPECT_TRUE(absl::MakeUint128(1, 0));
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-  unsigned __int128 intrinsic =
-      (static_cast<unsigned __int128>(0x3a5b76c209de76f6) << 64) +
-      0x1f25e1d63a2b46c5;
-  absl::uint128 custom =
-      absl::MakeUint128(0x3a5b76c209de76f6, 0x1f25e1d63a2b46c5);
-
-  EXPECT_EQ(custom, absl::uint128(intrinsic));
-  EXPECT_EQ(custom, absl::uint128(static_cast<__int128>(intrinsic)));
-  EXPECT_EQ(intrinsic, static_cast<unsigned __int128>(custom));
-  EXPECT_EQ(intrinsic, static_cast<__int128>(custom));
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-  // verify that an integer greater than 2**64 that can be stored precisely
-  // inside a double is converted to a absl::uint128 without loss of
-  // information.
-  double precise_double = 0x530e * std::pow(2.0, 64.0) + 0xda74000000000000;
-  absl::uint128 from_precise_double(precise_double);
-  absl::uint128 from_precise_ints =
-      absl::MakeUint128(0x530e, 0xda74000000000000);
-  EXPECT_EQ(from_precise_double, from_precise_ints);
-  EXPECT_DOUBLE_EQ(static_cast<double>(from_precise_ints), precise_double);
-
-  double approx_double = 0xffffeeeeddddcccc * std::pow(2.0, 64.0) +
-                         0xbbbbaaaa99998888;
-  absl::uint128 from_approx_double(approx_double);
-  EXPECT_DOUBLE_EQ(static_cast<double>(from_approx_double), approx_double);
-
-  double round_to_zero = 0.7;
-  double round_to_five = 5.8;
-  double round_to_nine = 9.3;
-  EXPECT_EQ(static_cast<absl::uint128>(round_to_zero), 0);
-  EXPECT_EQ(static_cast<absl::uint128>(round_to_five), 5);
-  EXPECT_EQ(static_cast<absl::uint128>(round_to_nine), 9);
-
-  absl::uint128 highest_precision_in_long_double =
-      ~absl::uint128{} >> (128 - std::numeric_limits<long double>::digits);
-  EXPECT_EQ(highest_precision_in_long_double,
-            static_cast<absl::uint128>(
-                static_cast<long double>(highest_precision_in_long_double)));
-  // Apply a mask just to make sure all the bits are the right place.
-  const absl::uint128 arbitrary_mask =
-      absl::MakeUint128(0xa29f622677ded751, 0xf8ca66add076f468);
-  EXPECT_EQ(highest_precision_in_long_double & arbitrary_mask,
-            static_cast<absl::uint128>(static_cast<long double>(
-                highest_precision_in_long_double & arbitrary_mask)));
-
-  EXPECT_EQ(static_cast<absl::uint128>(-0.1L), 0);
-}
-
-TEST(Uint128, OperatorAssignReturnRef) {
-  absl::uint128 v(1);
-  (v += 4) -= 3;
-  EXPECT_EQ(2, v);
-}
-
-TEST(Uint128, Multiply) {
-  absl::uint128 a, b, c;
-
-  // Zero test.
-  a = 0;
-  b = 0;
-  c = a * b;
-  EXPECT_EQ(0, c);
-
-  // Max carries.
-  a = absl::uint128(0) - 1;
-  b = absl::uint128(0) - 1;
-  c = a * b;
-  EXPECT_EQ(1, c);
-
-  // Self-operation with max carries.
-  c = absl::uint128(0) - 1;
-  c *= c;
-  EXPECT_EQ(1, c);
-
-  // 1-bit x 1-bit.
-  for (int i = 0; i < 64; ++i) {
-    for (int j = 0; j < 64; ++j) {
-      a = absl::uint128(1) << i;
-      b = absl::uint128(1) << j;
-      c = a * b;
-      EXPECT_EQ(absl::uint128(1) << (i + j), c);
-    }
-  }
-
-  // Verified with dc.
-  a = absl::MakeUint128(0xffffeeeeddddcccc, 0xbbbbaaaa99998888);
-  b = absl::MakeUint128(0x7777666655554444, 0x3333222211110000);
-  c = a * b;
-  EXPECT_EQ(absl::MakeUint128(0x530EDA741C71D4C3, 0xBF25975319080000), c);
-  EXPECT_EQ(0, c - b * a);
-  EXPECT_EQ(a*a - b*b, (a+b) * (a-b));
-
-  // Verified with dc.
-  a = absl::MakeUint128(0x0123456789abcdef, 0xfedcba9876543210);
-  b = absl::MakeUint128(0x02468ace13579bdf, 0xfdb97531eca86420);
-  c = a * b;
-  EXPECT_EQ(absl::MakeUint128(0x97a87f4f261ba3f2, 0x342d0bbf48948200), c);
-  EXPECT_EQ(0, c - b * a);
-  EXPECT_EQ(a*a - b*b, (a+b) * (a-b));
-}
-
-TEST(Uint128, AliasTests) {
-  absl::uint128 x1 = absl::MakeUint128(1, 2);
-  absl::uint128 x2 = absl::MakeUint128(2, 4);
-  x1 += x1;
-  EXPECT_EQ(x2, x1);
-
-  absl::uint128 x3 = absl::MakeUint128(1, static_cast<uint64_t>(1) << 63);
-  absl::uint128 x4 = absl::MakeUint128(3, 0);
-  x3 += x3;
-  EXPECT_EQ(x4, x3);
-}
-
-TEST(Uint128, DivideAndMod) {
-  using std::swap;
-
-  // a := q * b + r
-  absl::uint128 a, b, q, r;
-
-  // Zero test.
-  a = 0;
-  b = 123;
-  q = a / b;
-  r = a % b;
-  EXPECT_EQ(0, q);
-  EXPECT_EQ(0, r);
-
-  a = absl::MakeUint128(0x530eda741c71d4c3, 0xbf25975319080000);
-  q = absl::MakeUint128(0x4de2cab081, 0x14c34ab4676e4bab);
-  b = absl::uint128(0x1110001);
-  r = absl::uint128(0x3eb455);
-  ASSERT_EQ(a, q * b + r);  // Sanity-check.
-
-  absl::uint128 result_q, result_r;
-  result_q = a / b;
-  result_r = a % b;
-  EXPECT_EQ(q, result_q);
-  EXPECT_EQ(r, result_r);
-
-  // Try the other way around.
-  swap(q, b);
-  result_q = a / b;
-  result_r = a % b;
-  EXPECT_EQ(q, result_q);
-  EXPECT_EQ(r, result_r);
-  // Restore.
-  swap(b, q);
-
-  // Dividend < divisor; result should be q:0 r:<dividend>.
-  swap(a, b);
-  result_q = a / b;
-  result_r = a % b;
-  EXPECT_EQ(0, result_q);
-  EXPECT_EQ(a, result_r);
-  // Try the other way around.
-  swap(a, q);
-  result_q = a / b;
-  result_r = a % b;
-  EXPECT_EQ(0, result_q);
-  EXPECT_EQ(a, result_r);
-  // Restore.
-  swap(q, a);
-  swap(b, a);
-
-  // Try a large remainder.
-  b = a / 2 + 1;
-  absl::uint128 expected_r =
-      absl::MakeUint128(0x29876d3a0e38ea61, 0xdf92cba98c83ffff);
-  // Sanity checks.
-  ASSERT_EQ(a / 2 - 1, expected_r);
-  ASSERT_EQ(a, b + expected_r);
-  result_q = a / b;
-  result_r = a % b;
-  EXPECT_EQ(1, result_q);
-  EXPECT_EQ(expected_r, result_r);
-}
-
-TEST(Uint128, DivideAndModRandomInputs) {
-  const int kNumIters = 1 << 18;
-  std::minstd_rand random(testing::UnitTest::GetInstance()->random_seed());
-  std::uniform_int_distribution<uint64_t> uniform_uint64;
-  for (int i = 0; i < kNumIters; ++i) {
-    const absl::uint128 a =
-        absl::MakeUint128(uniform_uint64(random), uniform_uint64(random));
-    const absl::uint128 b =
-        absl::MakeUint128(uniform_uint64(random), uniform_uint64(random));
-    if (b == 0) {
-      continue;  // Avoid a div-by-zero.
-    }
-    const absl::uint128 q = a / b;
-    const absl::uint128 r = a % b;
-    ASSERT_EQ(a, b * q + r);
-  }
-}
-
-TEST(Uint128, ConstexprTest) {
-  constexpr absl::uint128 zero = absl::uint128();
-  constexpr absl::uint128 one = 1;
-  constexpr absl::uint128 minus_two = -2;
-  EXPECT_EQ(zero, absl::uint128(0));
-  EXPECT_EQ(one, absl::uint128(1));
-  EXPECT_EQ(minus_two, absl::MakeUint128(-1, -2));
-}
-
-TEST(Uint128, NumericLimitsTest) {
-  static_assert(std::numeric_limits<absl::uint128>::is_specialized, "");
-  static_assert(!std::numeric_limits<absl::uint128>::is_signed, "");
-  static_assert(std::numeric_limits<absl::uint128>::is_integer, "");
-  EXPECT_EQ(static_cast<int>(128 * std::log10(2)),
-            std::numeric_limits<absl::uint128>::digits10);
-  EXPECT_EQ(0, std::numeric_limits<absl::uint128>::min());
-  EXPECT_EQ(0, std::numeric_limits<absl::uint128>::lowest());
-  EXPECT_EQ(absl::Uint128Max(), std::numeric_limits<absl::uint128>::max());
-}
-
-TEST(Uint128, Hash) {
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
-      // Some simple values
-      absl::uint128{0},
-      absl::uint128{1},
-      ~absl::uint128{},
-      // 64 bit limits
-      absl::uint128{std::numeric_limits<int64_t>::max()},
-      absl::uint128{std::numeric_limits<uint64_t>::max()} + 0,
-      absl::uint128{std::numeric_limits<uint64_t>::max()} + 1,
-      absl::uint128{std::numeric_limits<uint64_t>::max()} + 2,
-      // Keeping high same
-      absl::uint128{1} << 62,
-      absl::uint128{1} << 63,
-      // Keeping low same
-      absl::uint128{1} << 64,
-      absl::uint128{1} << 65,
-      // 128 bit limits
-      std::numeric_limits<absl::uint128>::max(),
-      std::numeric_limits<absl::uint128>::max() - 1,
-      std::numeric_limits<absl::uint128>::min() + 1,
-      std::numeric_limits<absl::uint128>::min(),
-  }));
-}
-
-
-TEST(Int128Uint128, ConversionTest) {
-  absl::int128 nonnegative_signed_values[] = {
-      0,
-      1,
-      0xffeeddccbbaa9988,
-      absl::MakeInt128(0x7766554433221100, 0),
-      absl::MakeInt128(0x1234567890abcdef, 0xfedcba0987654321),
-      absl::Int128Max()};
-  for (absl::int128 value : nonnegative_signed_values) {
-    EXPECT_EQ(value, absl::int128(absl::uint128(value)));
-
-    absl::uint128 assigned_value;
-    assigned_value = value;
-    EXPECT_EQ(value, absl::int128(assigned_value));
-  }
-
-  absl::int128 negative_values[] = {
-      -1, -0x1234567890abcdef,
-      absl::MakeInt128(-0x5544332211ffeedd, 0),
-      -absl::MakeInt128(0x76543210fedcba98, 0xabcdef0123456789)};
-  for (absl::int128 value : negative_values) {
-    EXPECT_EQ(absl::uint128(-value), -absl::uint128(value));
-
-    absl::uint128 assigned_value;
-    assigned_value = value;
-    EXPECT_EQ(absl::uint128(-value), -assigned_value);
-  }
-}
-
-template <typename T>
-class Int128IntegerTraitsTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE(Int128IntegerTraitsTest, IntegerTypes);
-
-TYPED_TEST(Int128IntegerTraitsTest, ConstructAssignTest) {
-  static_assert(std::is_constructible<absl::int128, TypeParam>::value,
-                "absl::int128 must be constructible from TypeParam");
-  static_assert(std::is_assignable<absl::int128&, TypeParam>::value,
-                "absl::int128 must be assignable from TypeParam");
-  static_assert(!std::is_assignable<TypeParam&, absl::int128>::value,
-                "TypeParam must not be assignable from absl::int128");
-}
-
-template <typename T>
-class Int128FloatTraitsTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE(Int128FloatTraitsTest, FloatingPointTypes);
-
-TYPED_TEST(Int128FloatTraitsTest, ConstructAssignTest) {
-  static_assert(std::is_constructible<absl::int128, TypeParam>::value,
-                "absl::int128 must be constructible from TypeParam");
-  static_assert(!std::is_assignable<absl::int128&, TypeParam>::value,
-                "absl::int128 must not be assignable from TypeParam");
-  static_assert(!std::is_assignable<TypeParam&, absl::int128>::value,
-                "TypeParam must not be assignable from absl::int128");
-}
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-// These type traits done separately as TYPED_TEST requires typeinfo, and not
-// all platforms have this for __int128 even though they define the type.
-TEST(Int128, IntrinsicTypeTraitsTest) {
-  static_assert(std::is_constructible<absl::int128, __int128>::value,
-                "absl::int128 must be constructible from __int128");
-  static_assert(std::is_assignable<absl::int128&, __int128>::value,
-                "absl::int128 must be assignable from __int128");
-  static_assert(!std::is_assignable<__int128&, absl::int128>::value,
-                "__int128 must not be assignable from absl::int128");
-
-  static_assert(std::is_constructible<absl::int128, unsigned __int128>::value,
-                "absl::int128 must be constructible from unsigned __int128");
-  static_assert(!std::is_assignable<absl::int128&, unsigned __int128>::value,
-                "absl::int128 must be assignable from unsigned __int128");
-  static_assert(!std::is_assignable<unsigned __int128&, absl::int128>::value,
-                "unsigned __int128 must not be assignable from absl::int128");
-}
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-TEST(Int128, TrivialTraitsTest) {
-  static_assert(absl::is_trivially_default_constructible<absl::int128>::value,
-                "");
-  static_assert(absl::is_trivially_copy_constructible<absl::int128>::value, "");
-  static_assert(absl::is_trivially_copy_assignable<absl::int128>::value, "");
-  static_assert(std::is_trivially_destructible<absl::int128>::value, "");
-}
-
-TEST(Int128, BoolConversionTest) {
-  EXPECT_FALSE(absl::int128(0));
-  for (int i = 0; i < 64; ++i) {
-    EXPECT_TRUE(absl::MakeInt128(0, uint64_t{1} << i));
-  }
-  for (int i = 0; i < 63; ++i) {
-    EXPECT_TRUE(absl::MakeInt128(int64_t{1} << i, 0));
-  }
-  EXPECT_TRUE(absl::Int128Min());
-
-  EXPECT_EQ(absl::int128(1), absl::int128(true));
-  EXPECT_EQ(absl::int128(0), absl::int128(false));
-}
-
-template <typename T>
-class Int128IntegerConversionTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE(Int128IntegerConversionTest, IntegerTypes);
-
-TYPED_TEST(Int128IntegerConversionTest, RoundTripTest) {
-  EXPECT_EQ(TypeParam{0}, static_cast<TypeParam>(absl::int128(0)));
-  EXPECT_EQ(std::numeric_limits<TypeParam>::min(),
-            static_cast<TypeParam>(
-                absl::int128(std::numeric_limits<TypeParam>::min())));
-  EXPECT_EQ(std::numeric_limits<TypeParam>::max(),
-            static_cast<TypeParam>(
-                absl::int128(std::numeric_limits<TypeParam>::max())));
-}
-
-template <typename T>
-class Int128FloatConversionTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE(Int128FloatConversionTest, FloatingPointTypes);
-
-TYPED_TEST(Int128FloatConversionTest, ConstructAndCastTest) {
-  // Conversions where the floating point values should be exactly the same.
-  // 0x9f5b is a randomly chosen small value.
-  for (int i = 0; i < 110; ++i) {  // 110 = 126 - #bits in 0x9f5b
-    SCOPED_TRACE(::testing::Message() << "i = " << i);
-
-    TypeParam float_value = std::ldexp(static_cast<TypeParam>(0x9f5b), i);
-    absl::int128 int_value = absl::int128(0x9f5b) << i;
-
-    EXPECT_EQ(float_value, static_cast<TypeParam>(int_value));
-    EXPECT_EQ(-float_value, static_cast<TypeParam>(-int_value));
-    EXPECT_EQ(int_value, absl::int128(float_value));
-    EXPECT_EQ(-int_value, absl::int128(-float_value));
-  }
-
-  // Round trip conversions with a small sample of randomly generated uint64_t
-  // values (less than int64_t max so that value * 2^64 fits into int128).
-  uint64_t values[] = {0x6d4492c24fb86199, 0x26ead65e4cb359b5,
-                       0x2c43407433ba3fd1, 0x3b574ec668df6b55,
-                       0x1c750e55a29f4f0f};
-  for (uint64_t value : values) {
-    for (int i = 0; i <= 64; ++i) {
-      SCOPED_TRACE(::testing::Message()
-                   << "value = " << value << "; i = " << i);
-
-      TypeParam fvalue = std::ldexp(static_cast<TypeParam>(value), i);
-      EXPECT_DOUBLE_EQ(fvalue, static_cast<TypeParam>(absl::int128(fvalue)));
-      EXPECT_DOUBLE_EQ(-fvalue, static_cast<TypeParam>(-absl::int128(fvalue)));
-      EXPECT_DOUBLE_EQ(-fvalue, static_cast<TypeParam>(absl::int128(-fvalue)));
-      EXPECT_DOUBLE_EQ(fvalue, static_cast<TypeParam>(-absl::int128(-fvalue)));
-    }
-  }
-
-  // Round trip conversions with a small sample of random large positive values.
-  absl::int128 large_values[] = {
-      absl::MakeInt128(0x5b0640d96c7b3d9f, 0xb7a7189e51d18622),
-      absl::MakeInt128(0x34bed042c6f65270, 0x73b236570669a089),
-      absl::MakeInt128(0x43deba9e6da12724, 0xf7f0f83da686797d),
-      absl::MakeInt128(0x71e8d383be4e5589, 0x75c3f96fb00752b6)};
-  for (absl::int128 value : large_values) {
-    // Make value have as many significant bits as can be represented by
-    // the mantissa, also making sure the highest and lowest bit in the range
-    // are set.
-    value >>= (127 - std::numeric_limits<TypeParam>::digits);
-    value |= absl::int128(1) << (std::numeric_limits<TypeParam>::digits - 1);
-    value |= 1;
-    for (int i = 0; i < 127 - std::numeric_limits<TypeParam>::digits; ++i) {
-      absl::int128 int_value = value << i;
-      EXPECT_EQ(int_value,
-                static_cast<absl::int128>(static_cast<TypeParam>(int_value)));
-      EXPECT_EQ(-int_value,
-                static_cast<absl::int128>(static_cast<TypeParam>(-int_value)));
-    }
-  }
-
-  // Small sample of checks that rounding is toward zero
-  EXPECT_EQ(0, absl::int128(TypeParam(0.1)));
-  EXPECT_EQ(17, absl::int128(TypeParam(17.8)));
-  EXPECT_EQ(0, absl::int128(TypeParam(-0.8)));
-  EXPECT_EQ(-53, absl::int128(TypeParam(-53.1)));
-  EXPECT_EQ(0, absl::int128(TypeParam(0.5)));
-  EXPECT_EQ(0, absl::int128(TypeParam(-0.5)));
-  TypeParam just_lt_one = std::nexttoward(TypeParam(1), TypeParam(0));
-  EXPECT_EQ(0, absl::int128(just_lt_one));
-  TypeParam just_gt_minus_one = std::nexttoward(TypeParam(-1), TypeParam(0));
-  EXPECT_EQ(0, absl::int128(just_gt_minus_one));
-
-  // Check limits
-  EXPECT_DOUBLE_EQ(std::ldexp(static_cast<TypeParam>(1), 127),
-                   static_cast<TypeParam>(absl::Int128Max()));
-  EXPECT_DOUBLE_EQ(-std::ldexp(static_cast<TypeParam>(1), 127),
-                   static_cast<TypeParam>(absl::Int128Min()));
-}
-
-TEST(Int128, FactoryTest) {
-  EXPECT_EQ(absl::int128(-1), absl::MakeInt128(-1, -1));
-  EXPECT_EQ(absl::int128(-31), absl::MakeInt128(-1, -31));
-  EXPECT_EQ(absl::int128(std::numeric_limits<int64_t>::min()),
-            absl::MakeInt128(-1, std::numeric_limits<int64_t>::min()));
-  EXPECT_EQ(absl::int128(0), absl::MakeInt128(0, 0));
-  EXPECT_EQ(absl::int128(1), absl::MakeInt128(0, 1));
-  EXPECT_EQ(absl::int128(std::numeric_limits<int64_t>::max()),
-            absl::MakeInt128(0, std::numeric_limits<int64_t>::max()));
-}
-
-TEST(Int128, HighLowTest) {
-  struct HighLowPair {
-    int64_t high;
-    uint64_t low;
-  };
-  HighLowPair values[]{{0, 0}, {0, 1}, {1, 0}, {123, 456}, {-654, 321}};
-  for (const HighLowPair& pair : values) {
-    absl::int128 value = absl::MakeInt128(pair.high, pair.low);
-    EXPECT_EQ(pair.low, absl::Int128Low64(value));
-    EXPECT_EQ(pair.high, absl::Int128High64(value));
-  }
-}
-
-TEST(Int128, LimitsTest) {
-  EXPECT_EQ(absl::MakeInt128(0x7fffffffffffffff, 0xffffffffffffffff),
-            absl::Int128Max());
-  EXPECT_EQ(absl::Int128Max(), ~absl::Int128Min());
-}
-
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-TEST(Int128, IntrinsicConversionTest) {
-  __int128 intrinsic =
-      (static_cast<__int128>(0x3a5b76c209de76f6) << 64) + 0x1f25e1d63a2b46c5;
-  absl::int128 custom =
-      absl::MakeInt128(0x3a5b76c209de76f6, 0x1f25e1d63a2b46c5);
-
-  EXPECT_EQ(custom, absl::int128(intrinsic));
-  EXPECT_EQ(intrinsic, static_cast<__int128>(custom));
-}
-#endif  // ABSL_HAVE_INTRINSIC_INT128
-
-TEST(Int128, ConstexprTest) {
-  constexpr absl::int128 zero = absl::int128();
-  constexpr absl::int128 one = 1;
-  constexpr absl::int128 minus_two = -2;
-  constexpr absl::int128 min = absl::Int128Min();
-  constexpr absl::int128 max = absl::Int128Max();
-  EXPECT_EQ(zero, absl::int128(0));
-  EXPECT_EQ(one, absl::int128(1));
-  EXPECT_EQ(minus_two, absl::MakeInt128(-1, -2));
-  EXPECT_GT(max, one);
-  EXPECT_LT(min, minus_two);
-}
-
-TEST(Int128, ComparisonTest) {
-  struct TestCase {
-    absl::int128 smaller;
-    absl::int128 larger;
-  };
-  TestCase cases[] = {
-      {absl::int128(0), absl::int128(123)},
-      {absl::MakeInt128(-12, 34), absl::MakeInt128(12, 34)},
-      {absl::MakeInt128(1, 1000), absl::MakeInt128(1000, 1)},
-      {absl::MakeInt128(-1000, 1000), absl::MakeInt128(-1, 1)},
-  };
-  for (const TestCase& pair : cases) {
-    SCOPED_TRACE(::testing::Message() << "pair.smaller = " << pair.smaller
-                                      << "; pair.larger = " << pair.larger);
-
-    EXPECT_TRUE(pair.smaller == pair.smaller);  // NOLINT(readability/check)
-    EXPECT_TRUE(pair.larger == pair.larger);    // NOLINT(readability/check)
-    EXPECT_FALSE(pair.smaller == pair.larger);  // NOLINT(readability/check)
-
-    EXPECT_TRUE(pair.smaller != pair.larger);    // NOLINT(readability/check)
-    EXPECT_FALSE(pair.smaller != pair.smaller);  // NOLINT(readability/check)
-    EXPECT_FALSE(pair.larger != pair.larger);    // NOLINT(readability/check)
-
-    EXPECT_TRUE(pair.smaller < pair.larger);   // NOLINT(readability/check)
-    EXPECT_FALSE(pair.larger < pair.smaller);  // NOLINT(readability/check)
-
-    EXPECT_TRUE(pair.larger > pair.smaller);   // NOLINT(readability/check)
-    EXPECT_FALSE(pair.smaller > pair.larger);  // NOLINT(readability/check)
-
-    EXPECT_TRUE(pair.smaller <= pair.larger);   // NOLINT(readability/check)
-    EXPECT_FALSE(pair.larger <= pair.smaller);  // NOLINT(readability/check)
-    EXPECT_TRUE(pair.smaller <= pair.smaller);  // NOLINT(readability/check)
-    EXPECT_TRUE(pair.larger <= pair.larger);    // NOLINT(readability/check)
-
-    EXPECT_TRUE(pair.larger >= pair.smaller);   // NOLINT(readability/check)
-    EXPECT_FALSE(pair.smaller >= pair.larger);  // NOLINT(readability/check)
-    EXPECT_TRUE(pair.smaller >= pair.smaller);  // NOLINT(readability/check)
-    EXPECT_TRUE(pair.larger >= pair.larger);    // NOLINT(readability/check)
-  }
-}
-
-TEST(Int128, UnaryNegationTest) {
-  int64_t values64[] = {0, 1, 12345, 0x4000000000000000,
-                        std::numeric_limits<int64_t>::max()};
-  for (int64_t value : values64) {
-    SCOPED_TRACE(::testing::Message() << "value = " << value);
-
-    EXPECT_EQ(absl::int128(-value), -absl::int128(value));
-    EXPECT_EQ(absl::int128(value), -absl::int128(-value));
-    EXPECT_EQ(absl::MakeInt128(-value, 0), -absl::MakeInt128(value, 0));
-    EXPECT_EQ(absl::MakeInt128(value, 0), -absl::MakeInt128(-value, 0));
-  }
-}
-
-TEST(Int128, LogicalNotTest) {
-  EXPECT_TRUE(!absl::int128(0));
-  for (int i = 0; i < 64; ++i) {
-    EXPECT_FALSE(!absl::MakeInt128(0, uint64_t{1} << i));
-  }
-  for (int i = 0; i < 63; ++i) {
-    EXPECT_FALSE(!absl::MakeInt128(int64_t{1} << i, 0));
-  }
-}
-
-TEST(Int128, AdditionSubtractionTest) {
-  // 64 bit pairs that will not cause overflow / underflow. These test negative
-  // carry; positive carry must be checked separately.
-  std::pair<int64_t, int64_t> cases[]{
-      {0, 0},                              // 0, 0
-      {0, 2945781290834},                  // 0, +
-      {1908357619234, 0},                  // +, 0
-      {0, -1204895918245},                 // 0, -
-      {-2957928523560, 0},                 // -, 0
-      {89023982312461, 98346012567134},    // +, +
-      {-63454234568239, -23456235230773},  // -, -
-      {98263457263502, -21428561935925},   // +, -
-      {-88235237438467, 15923659234573},   // -, +
-  };
-  for (const auto& pair : cases) {
-    SCOPED_TRACE(::testing::Message()
-                 << "pair = {" << pair.first << ", " << pair.second << '}');
-
-    EXPECT_EQ(absl::int128(pair.first + pair.second),
-              absl::int128(pair.first) + absl::int128(pair.second));
-    EXPECT_EQ(absl::int128(pair.second + pair.first),
-              absl::int128(pair.second) += absl::int128(pair.first));
-
-    EXPECT_EQ(absl::int128(pair.first - pair.second),
-              absl::int128(pair.first) - absl::int128(pair.second));
-    EXPECT_EQ(absl::int128(pair.second - pair.first),
-              absl::int128(pair.second) -= absl::int128(pair.first));
-
-    EXPECT_EQ(
-        absl::MakeInt128(pair.second + pair.first, 0),
-        absl::MakeInt128(pair.second, 0) + absl::MakeInt128(pair.first, 0));
-    EXPECT_EQ(
-        absl::MakeInt128(pair.first + pair.second, 0),
-        absl::MakeInt128(pair.first, 0) += absl::MakeInt128(pair.second, 0));
-
-    EXPECT_EQ(
-        absl::MakeInt128(pair.second - pair.first, 0),
-        absl::MakeInt128(pair.second, 0) - absl::MakeInt128(pair.first, 0));
-    EXPECT_EQ(
-        absl::MakeInt128(pair.first - pair.second, 0),
-        absl::MakeInt128(pair.first, 0) -= absl::MakeInt128(pair.second, 0));
-  }
-
-  // check positive carry
-  EXPECT_EQ(absl::MakeInt128(31, 0),
-            absl::MakeInt128(20, 1) +
-                absl::MakeInt128(10, std::numeric_limits<uint64_t>::max()));
-}
-
-TEST(Int128, IncrementDecrementTest) {
-  absl::int128 value = 0;
-  EXPECT_EQ(0, value++);
-  EXPECT_EQ(1, value);
-  EXPECT_EQ(1, value--);
-  EXPECT_EQ(0, value);
-  EXPECT_EQ(-1, --value);
-  EXPECT_EQ(-1, value);
-  EXPECT_EQ(0, ++value);
-  EXPECT_EQ(0, value);
-}
-
-TEST(Int128, MultiplicationTest) {
-  // 1 bit x 1 bit, and negative combinations
-  for (int i = 0; i < 64; ++i) {
-    for (int j = 0; j < 127 - i; ++j) {
-      SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
-      absl::int128 a = absl::int128(1) << i;
-      absl::int128 b = absl::int128(1) << j;
-      absl::int128 c = absl::int128(1) << (i + j);
-
-      EXPECT_EQ(c, a * b);
-      EXPECT_EQ(-c, -a * b);
-      EXPECT_EQ(-c, a * -b);
-      EXPECT_EQ(c, -a * -b);
-
-      EXPECT_EQ(c, absl::int128(a) *= b);
-      EXPECT_EQ(-c, absl::int128(-a) *= b);
-      EXPECT_EQ(-c, absl::int128(a) *= -b);
-      EXPECT_EQ(c, absl::int128(-a) *= -b);
-    }
-  }
-
-  // Pairs of random values that will not overflow signed 64-bit multiplication
-  std::pair<int64_t, int64_t> small_values[] = {
-      {0x5e61, 0xf29f79ca14b4},    // +, +
-      {0x3e033b, -0x612c0ee549},   // +, -
-      {-0x052ce7e8, 0x7c728f0f},   // -, +
-      {-0x3af7054626, -0xfb1e1d},  // -, -
-  };
-  for (const std::pair<int64_t, int64_t>& pair : small_values) {
-    SCOPED_TRACE(::testing::Message()
-                 << "pair = {" << pair.first << ", " << pair.second << '}');
-
-    EXPECT_EQ(absl::int128(pair.first * pair.second),
-              absl::int128(pair.first) * absl::int128(pair.second));
-    EXPECT_EQ(absl::int128(pair.first * pair.second),
-              absl::int128(pair.first) *= absl::int128(pair.second));
-
-    EXPECT_EQ(absl::MakeInt128(pair.first * pair.second, 0),
-              absl::MakeInt128(pair.first, 0) * absl::int128(pair.second));
-    EXPECT_EQ(absl::MakeInt128(pair.first * pair.second, 0),
-              absl::MakeInt128(pair.first, 0) *= absl::int128(pair.second));
-  }
-
-  // Pairs of positive random values that will not overflow 64-bit
-  // multiplication and can be left shifted by 32 without overflow
-  std::pair<int64_t, int64_t> small_values2[] = {
-      {0x1bb0a110, 0x31487671},
-      {0x4792784e, 0x28add7d7},
-      {0x7b66553a, 0x11dff8ef},
-  };
-  for (const std::pair<int64_t, int64_t>& pair : small_values2) {
-    SCOPED_TRACE(::testing::Message()
-                 << "pair = {" << pair.first << ", " << pair.second << '}');
-
-    absl::int128 a = absl::int128(pair.first << 32);
-    absl::int128 b = absl::int128(pair.second << 32);
-    absl::int128 c = absl::MakeInt128(pair.first * pair.second, 0);
-
-    EXPECT_EQ(c, a * b);
-    EXPECT_EQ(-c, -a * b);
-    EXPECT_EQ(-c, a * -b);
-    EXPECT_EQ(c, -a * -b);
-
-    EXPECT_EQ(c, absl::int128(a) *= b);
-    EXPECT_EQ(-c, absl::int128(-a) *= b);
-    EXPECT_EQ(-c, absl::int128(a) *= -b);
-    EXPECT_EQ(c, absl::int128(-a) *= -b);
-  }
-
-  // check 0, 1, and -1 behavior with large values
-  absl::int128 large_values[] = {
-      {absl::MakeInt128(0xd66f061af02d0408, 0x727d2846cb475b53)},
-      {absl::MakeInt128(0x27b8d5ed6104452d, 0x03f8a33b0ee1df4f)},
-      {-absl::MakeInt128(0x621b6626b9e8d042, 0x27311ac99df00938)},
-      {-absl::MakeInt128(0x34e0656f1e95fb60, 0x4281cfd731257a47)},
-  };
-  for (absl::int128 value : large_values) {
-    EXPECT_EQ(0, 0 * value);
-    EXPECT_EQ(0, value * 0);
-    EXPECT_EQ(0, absl::int128(0) *= value);
-    EXPECT_EQ(0, value *= 0);
-
-    EXPECT_EQ(value, 1 * value);
-    EXPECT_EQ(value, value * 1);
-    EXPECT_EQ(value, absl::int128(1) *= value);
-    EXPECT_EQ(value, value *= 1);
-
-    EXPECT_EQ(-value, -1 * value);
-    EXPECT_EQ(-value, value * -1);
-    EXPECT_EQ(-value, absl::int128(-1) *= value);
-    EXPECT_EQ(-value, value *= -1);
-  }
-
-  // Manually calculated random large value cases
-  EXPECT_EQ(absl::MakeInt128(0xcd0efd3442219bb, 0xde47c05bcd9df6e1),
-            absl::MakeInt128(0x7c6448, 0x3bc4285c47a9d253) * 0x1a6037537b);
-  EXPECT_EQ(-absl::MakeInt128(0x1f8f149850b1e5e6, 0x1e50d6b52d272c3e),
-            -absl::MakeInt128(0x23, 0x2e68a513ca1b8859) * 0xe5a434cd14866e);
-  EXPECT_EQ(-absl::MakeInt128(0x55cae732029d1fce, 0xca6474b6423263e4),
-            0xa9b98a8ddf66bc * -absl::MakeInt128(0x81, 0x672e58231e2469d7));
-  EXPECT_EQ(absl::MakeInt128(0x19c8b7620b507dc4, 0xfec042b71a5f29a4),
-            -0x3e39341147 * -absl::MakeInt128(0x6a14b2, 0x5ed34cca42327b3c));
-
-  EXPECT_EQ(absl::MakeInt128(0xcd0efd3442219bb, 0xde47c05bcd9df6e1),
-            absl::MakeInt128(0x7c6448, 0x3bc4285c47a9d253) *= 0x1a6037537b);
-  EXPECT_EQ(-absl::MakeInt128(0x1f8f149850b1e5e6, 0x1e50d6b52d272c3e),
-            -absl::MakeInt128(0x23, 0x2e68a513ca1b8859) *= 0xe5a434cd14866e);
-  EXPECT_EQ(-absl::MakeInt128(0x55cae732029d1fce, 0xca6474b6423263e4),
-            absl::int128(0xa9b98a8ddf66bc) *=
-            -absl::MakeInt128(0x81, 0x672e58231e2469d7));
-  EXPECT_EQ(absl::MakeInt128(0x19c8b7620b507dc4, 0xfec042b71a5f29a4),
-            absl::int128(-0x3e39341147) *=
-            -absl::MakeInt128(0x6a14b2, 0x5ed34cca42327b3c));
-}
-
-TEST(Int128, DivisionAndModuloTest) {
-  // Check against 64 bit division and modulo operators with a sample of
-  // randomly generated pairs.
-  std::pair<int64_t, int64_t> small_pairs[] = {
-      {0x15f2a64138, 0x67da05},    {0x5e56d194af43045f, 0xcf1543fb99},
-      {0x15e61ed052036a, -0xc8e6}, {0x88125a341e85, -0xd23fb77683},
-      {-0xc06e20, 0x5a},           {-0x4f100219aea3e85d, 0xdcc56cb4efe993},
-      {-0x168d629105, -0xa7},      {-0x7b44e92f03ab2375, -0x6516},
-  };
-  for (const std::pair<int64_t, int64_t>& pair : small_pairs) {
-    SCOPED_TRACE(::testing::Message()
-                 << "pair = {" << pair.first << ", " << pair.second << '}');
-
-    absl::int128 dividend = pair.first;
-    absl::int128 divisor = pair.second;
-    int64_t quotient = pair.first / pair.second;
-    int64_t remainder = pair.first % pair.second;
-
-    EXPECT_EQ(quotient, dividend / divisor);
-    EXPECT_EQ(quotient, absl::int128(dividend) /= divisor);
-    EXPECT_EQ(remainder, dividend % divisor);
-    EXPECT_EQ(remainder, absl::int128(dividend) %= divisor);
-  }
-
-  // Test behavior with 0, 1, and -1 with a sample of randomly generated large
-  // values.
-  absl::int128 values[] = {
-      absl::MakeInt128(0x63d26ee688a962b2, 0x9e1411abda5c1d70),
-      absl::MakeInt128(0x152f385159d6f986, 0xbf8d48ef63da395d),
-      -absl::MakeInt128(0x3098d7567030038c, 0x14e7a8a098dc2164),
-      -absl::MakeInt128(0x49a037aca35c809f, 0xa6a87525480ef330),
-  };
-  for (absl::int128 value : values) {
-    SCOPED_TRACE(::testing::Message() << "value = " << value);
-
-    EXPECT_EQ(0, 0 / value);
-    EXPECT_EQ(0, absl::int128(0) /= value);
-    EXPECT_EQ(0, 0 % value);
-    EXPECT_EQ(0, absl::int128(0) %= value);
-
-    EXPECT_EQ(value, value / 1);
-    EXPECT_EQ(value, absl::int128(value) /= 1);
-    EXPECT_EQ(0, value % 1);
-    EXPECT_EQ(0, absl::int128(value) %= 1);
-
-    EXPECT_EQ(-value, value / -1);
-    EXPECT_EQ(-value, absl::int128(value) /= -1);
-    EXPECT_EQ(0, value % -1);
-    EXPECT_EQ(0, absl::int128(value) %= -1);
-  }
-
-  // Min and max values
-  EXPECT_EQ(0, absl::Int128Max() / absl::Int128Min());
-  EXPECT_EQ(absl::Int128Max(), absl::Int128Max() % absl::Int128Min());
-  EXPECT_EQ(-1, absl::Int128Min() / absl::Int128Max());
-  EXPECT_EQ(-1, absl::Int128Min() % absl::Int128Max());
-
-  // Power of two division and modulo of random large dividends
-  absl::int128 positive_values[] = {
-      absl::MakeInt128(0x21e1a1cc69574620, 0xe7ac447fab2fc869),
-      absl::MakeInt128(0x32c2ff3ab89e66e8, 0x03379a613fd1ce74),
-      absl::MakeInt128(0x6f32ca786184dcaf, 0x046f9c9ecb3a9ce1),
-      absl::MakeInt128(0x1aeb469dd990e0ee, 0xda2740f243cd37eb),
-  };
-  for (absl::int128 value : positive_values) {
-    for (int i = 0; i < 127; ++i) {
-      SCOPED_TRACE(::testing::Message()
-                   << "value = " << value << "; i = " << i);
-      absl::int128 power_of_two = absl::int128(1) << i;
-
-      EXPECT_EQ(value >> i, value / power_of_two);
-      EXPECT_EQ(value >> i, absl::int128(value) /= power_of_two);
-      EXPECT_EQ(value & (power_of_two - 1), value % power_of_two);
-      EXPECT_EQ(value & (power_of_two - 1),
-                absl::int128(value) %= power_of_two);
-    }
-  }
-
-  // Manually calculated cases with random large dividends
-  struct DivisionModCase {
-    absl::int128 dividend;
-    absl::int128 divisor;
-    absl::int128 quotient;
-    absl::int128 remainder;
-  };
-  DivisionModCase manual_cases[] = {
-      {absl::MakeInt128(0x6ada48d489007966, 0x3c9c5c98150d5d69),
-       absl::MakeInt128(0x8bc308fb, 0x8cb9cc9a3b803344), 0xc3b87e08,
-       absl::MakeInt128(0x1b7db5e1, 0xd9eca34b7af04b49)},
-      {absl::MakeInt128(0xd6946511b5b, 0x4886c5c96546bf5f),
-       -absl::MakeInt128(0x263b, 0xfd516279efcfe2dc), -0x59cbabf0,
-       absl::MakeInt128(0x622, 0xf462909155651d1f)},
-      {-absl::MakeInt128(0x33db734f9e8d1399, 0x8447ac92482bca4d), 0x37495078240,
-       -absl::MakeInt128(0xf01f1, 0xbc0368bf9a77eae8), -0x21a508f404d},
-      {-absl::MakeInt128(0x13f837b409a07e7d, 0x7fc8e248a7d73560), -0x1b9f,
-       absl::MakeInt128(0xb9157556d724, 0xb14f635714d7563e), -0x1ade},
-  };
-  for (const DivisionModCase test_case : manual_cases) {
-    EXPECT_EQ(test_case.quotient, test_case.dividend / test_case.divisor);
-    EXPECT_EQ(test_case.quotient,
-              absl::int128(test_case.dividend) /= test_case.divisor);
-    EXPECT_EQ(test_case.remainder, test_case.dividend % test_case.divisor);
-    EXPECT_EQ(test_case.remainder,
-              absl::int128(test_case.dividend) %= test_case.divisor);
-  }
-}
-
-TEST(Int128, BitwiseLogicTest) {
-  EXPECT_EQ(absl::int128(-1), ~absl::int128(0));
-
-  absl::int128 values[]{
-      0, -1, 0xde400bee05c3ff6b, absl::MakeInt128(0x7f32178dd81d634a, 0),
-      absl::MakeInt128(0xaf539057055613a9, 0x7d104d7d946c2e4d)};
-  for (absl::int128 value : values) {
-    EXPECT_EQ(value, ~~value);
-
-    EXPECT_EQ(value, value | value);
-    EXPECT_EQ(value, value & value);
-    EXPECT_EQ(0, value ^ value);
-
-    EXPECT_EQ(value, absl::int128(value) |= value);
-    EXPECT_EQ(value, absl::int128(value) &= value);
-    EXPECT_EQ(0, absl::int128(value) ^= value);
-
-    EXPECT_EQ(value, value | 0);
-    EXPECT_EQ(0, value & 0);
-    EXPECT_EQ(value, value ^ 0);
-
-    EXPECT_EQ(absl::int128(-1), value | absl::int128(-1));
-    EXPECT_EQ(value, value & absl::int128(-1));
-    EXPECT_EQ(~value, value ^ absl::int128(-1));
-  }
-
-  // small sample of randomly generated int64_t's
-  std::pair<int64_t, int64_t> pairs64[]{
-      {0x7f86797f5e991af4, 0x1ee30494fb007c97},
-      {0x0b278282bacf01af, 0x58780e0a57a49e86},
-      {0x059f266ccb93a666, 0x3d5b731bae9286f5},
-      {0x63c0c4820f12108c, 0x58166713c12e1c3a},
-      {0x381488bb2ed2a66e, 0x2220a3eb76a3698c},
-      {0x2a0a0dfb81e06f21, 0x4b60585927f5523c},
-      {0x555b1c3a03698537, 0x25478cd19d8e53cb},
-      {0x4750f6f27d779225, 0x16397553c6ff05fc},
-  };
-  for (const std::pair<int64_t, int64_t>& pair : pairs64) {
-    SCOPED_TRACE(::testing::Message()
-                 << "pair = {" << pair.first << ", " << pair.second << '}');
-
-    EXPECT_EQ(absl::MakeInt128(~pair.first, ~pair.second),
-              ~absl::MakeInt128(pair.first, pair.second));
-
-    EXPECT_EQ(absl::int128(pair.first & pair.second),
-              absl::int128(pair.first) & absl::int128(pair.second));
-    EXPECT_EQ(absl::int128(pair.first | pair.second),
-              absl::int128(pair.first) | absl::int128(pair.second));
-    EXPECT_EQ(absl::int128(pair.first ^ pair.second),
-              absl::int128(pair.first) ^ absl::int128(pair.second));
-
-    EXPECT_EQ(absl::int128(pair.first & pair.second),
-              absl::int128(pair.first) &= absl::int128(pair.second));
-    EXPECT_EQ(absl::int128(pair.first | pair.second),
-              absl::int128(pair.first) |= absl::int128(pair.second));
-    EXPECT_EQ(absl::int128(pair.first ^ pair.second),
-              absl::int128(pair.first) ^= absl::int128(pair.second));
-
-    EXPECT_EQ(
-        absl::MakeInt128(pair.first & pair.second, 0),
-        absl::MakeInt128(pair.first, 0) & absl::MakeInt128(pair.second, 0));
-    EXPECT_EQ(
-        absl::MakeInt128(pair.first | pair.second, 0),
-        absl::MakeInt128(pair.first, 0) | absl::MakeInt128(pair.second, 0));
-    EXPECT_EQ(
-        absl::MakeInt128(pair.first ^ pair.second, 0),
-        absl::MakeInt128(pair.first, 0) ^ absl::MakeInt128(pair.second, 0));
-
-    EXPECT_EQ(
-        absl::MakeInt128(pair.first & pair.second, 0),
-        absl::MakeInt128(pair.first, 0) &= absl::MakeInt128(pair.second, 0));
-    EXPECT_EQ(
-        absl::MakeInt128(pair.first | pair.second, 0),
-        absl::MakeInt128(pair.first, 0) |= absl::MakeInt128(pair.second, 0));
-    EXPECT_EQ(
-        absl::MakeInt128(pair.first ^ pair.second, 0),
-        absl::MakeInt128(pair.first, 0) ^= absl::MakeInt128(pair.second, 0));
-  }
-}
-
-TEST(Int128, BitwiseShiftTest) {
-  for (int i = 0; i < 64; ++i) {
-    for (int j = 0; j <= i; ++j) {
-      // Left shift from j-th bit to i-th bit.
-      SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
-      EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) << (i - j));
-      EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) <<= (i - j));
-    }
-  }
-  for (int i = 0; i < 63; ++i) {
-    for (int j = 0; j < 64; ++j) {
-      // Left shift from j-th bit to (i + 64)-th bit.
-      SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
-      EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
-                absl::int128(uint64_t{1} << j) << (i + 64 - j));
-      EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
-                absl::int128(uint64_t{1} << j) <<= (i + 64 - j));
-    }
-    for (int j = 0; j <= i; ++j) {
-      // Left shift from (j + 64)-th bit to (i + 64)-th bit.
-      SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
-      EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
-                absl::MakeInt128(uint64_t{1} << j, 0) << (i - j));
-      EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
-                absl::MakeInt128(uint64_t{1} << j, 0) <<= (i - j));
-    }
-  }
-
-  for (int i = 0; i < 64; ++i) {
-    for (int j = i; j < 64; ++j) {
-      // Right shift from j-th bit to i-th bit.
-      SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
-      EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) >> (j - i));
-      EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) >>= (j - i));
-    }
-    for (int j = 0; j < 63; ++j) {
-      // Right shift from (j + 64)-th bit to i-th bit.
-      SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
-      EXPECT_EQ(uint64_t{1} << i,
-                absl::MakeInt128(uint64_t{1} << j, 0) >> (j + 64 - i));
-      EXPECT_EQ(uint64_t{1} << i,
-                absl::MakeInt128(uint64_t{1} << j, 0) >>= (j + 64 - i));
-    }
-  }
-  for (int i = 0; i < 63; ++i) {
-    for (int j = i; j < 63; ++j) {
-      // Right shift from (j + 64)-th bit to (i + 64)-th bit.
-      SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
-      EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
-                absl::MakeInt128(uint64_t{1} << j, 0) >> (j - i));
-      EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
-                absl::MakeInt128(uint64_t{1} << j, 0) >>= (j - i));
-    }
-  }
-}
-
-TEST(Int128, NumericLimitsTest) {
-  static_assert(std::numeric_limits<absl::int128>::is_specialized, "");
-  static_assert(std::numeric_limits<absl::int128>::is_signed, "");
-  static_assert(std::numeric_limits<absl::int128>::is_integer, "");
-  EXPECT_EQ(static_cast<int>(127 * std::log10(2)),
-            std::numeric_limits<absl::int128>::digits10);
-  EXPECT_EQ(absl::Int128Min(), std::numeric_limits<absl::int128>::min());
-  EXPECT_EQ(absl::Int128Min(), std::numeric_limits<absl::int128>::lowest());
-  EXPECT_EQ(absl::Int128Max(), std::numeric_limits<absl::int128>::max());
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/BUILD.bazel b/third_party/abseil_cpp/absl/random/BUILD.bazel
deleted file mode 100644
index 81e150e62e..0000000000
--- a/third_party/abseil_cpp/absl/random/BUILD.bazel
+++ /dev/null
@@ -1,509 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# ABSL random-number generation libraries.
-
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "random",
-    hdrs = ["random.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":seed_sequences",
-        "//absl/random/internal:nonsecure_base",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:pool_urbg",
-        "//absl/random/internal:randen_engine",
-    ],
-)
-
-cc_library(
-    name = "distributions",
-    srcs = [
-        "discrete_distribution.cc",
-        "gaussian_distribution.cc",
-    ],
-    hdrs = [
-        "bernoulli_distribution.h",
-        "beta_distribution.h",
-        "discrete_distribution.h",
-        "distributions.h",
-        "exponential_distribution.h",
-        "gaussian_distribution.h",
-        "log_uniform_int_distribution.h",
-        "poisson_distribution.h",
-        "uniform_int_distribution.h",
-        "uniform_real_distribution.h",
-        "zipf_distribution.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:base_internal",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/meta:type_traits",
-        "//absl/random/internal:distribution_caller",
-        "//absl/random/internal:fast_uniform_bits",
-        "//absl/random/internal:fastmath",
-        "//absl/random/internal:generate_real",
-        "//absl/random/internal:iostream_state_saver",
-        "//absl/random/internal:traits",
-        "//absl/random/internal:uniform_helper",
-        "//absl/random/internal:wide_multiply",
-        "//absl/strings",
-    ],
-)
-
-cc_library(
-    name = "seed_gen_exception",
-    srcs = ["seed_gen_exception.cc"],
-    hdrs = ["seed_gen_exception.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = ["//absl/base:config"],
-)
-
-cc_library(
-    name = "seed_sequences",
-    srcs = ["seed_sequences.cc"],
-    hdrs = [
-        "seed_sequences.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":seed_gen_exception",
-        "//absl/container:inlined_vector",
-        "//absl/random/internal:nonsecure_base",
-        "//absl/random/internal:pool_urbg",
-        "//absl/random/internal:salted_seed_seq",
-        "//absl/random/internal:seed_material",
-        "//absl/types:span",
-    ],
-)
-
-cc_library(
-    name = "bit_gen_ref",
-    hdrs = ["bit_gen_ref.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":random",
-        "//absl/base:core_headers",
-        "//absl/base:fast_type_id",
-        "//absl/meta:type_traits",
-        "//absl/random/internal:distribution_caller",
-        "//absl/random/internal:fast_uniform_bits",
-    ],
-)
-
-cc_library(
-    name = "mock_distributions",
-    testonly = 1,
-    hdrs = ["mock_distributions.h"],
-    deps = [
-        ":distributions",
-        ":mocking_bit_gen",
-        "//absl/meta:type_traits",
-        "//absl/random/internal:mock_overload_set",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_library(
-    name = "mocking_bit_gen",
-    testonly = 1,
-    hdrs = [
-        "mocking_bit_gen.h",
-    ],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:fast_type_id",
-        "//absl/container:flat_hash_map",
-        "//absl/meta:type_traits",
-        "//absl/random/internal:distribution_caller",
-        "//absl/strings",
-        "//absl/types:span",
-        "//absl/types:variant",
-        "//absl/utility",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "bernoulli_distribution_test",
-    size = "small",
-    timeout = "eternal",  # Android can take a very long time
-    srcs = ["bernoulli_distribution_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:sequence_urbg",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "beta_distribution_test",
-    size = "small",
-    timeout = "eternal",  # Android can take a very long time
-    srcs = ["beta_distribution_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    flaky = 1,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:raw_logging_internal",
-        "//absl/random/internal:distribution_test_util",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:sequence_urbg",
-        "//absl/strings",
-        "//absl/strings:str_format",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "distributions_test",
-    size = "small",
-    timeout = "moderate",
-    srcs = [
-        "distributions_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/random/internal:distribution_test_util",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "generators_test",
-    size = "small",
-    srcs = ["generators_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "log_uniform_int_distribution_test",
-    size = "medium",
-    srcs = [
-        "log_uniform_int_distribution_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:raw_logging_internal",
-        "//absl/random/internal:distribution_test_util",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:sequence_urbg",
-        "//absl/strings",
-        "//absl/strings:str_format",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "discrete_distribution_test",
-    size = "medium",
-    srcs = [
-        "discrete_distribution_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:raw_logging_internal",
-        "//absl/random/internal:distribution_test_util",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:sequence_urbg",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "poisson_distribution_test",
-    size = "small",
-    timeout = "eternal",  # Android can take a very long time
-    srcs = [
-        "poisson_distribution_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = [
-        # Too Slow.
-        "no_test_android_arm",
-        "no_test_loonix",
-    ],
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/container:flat_hash_map",
-        "//absl/random/internal:distribution_test_util",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:sequence_urbg",
-        "//absl/strings",
-        "//absl/strings:str_format",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "exponential_distribution_test",
-    size = "small",
-    srcs = ["exponential_distribution_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/random/internal:distribution_test_util",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:sequence_urbg",
-        "//absl/strings",
-        "//absl/strings:str_format",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "gaussian_distribution_test",
-    size = "small",
-    timeout = "eternal",  # Android can take a very long time
-    srcs = [
-        "gaussian_distribution_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/random/internal:distribution_test_util",
-        "//absl/random/internal:sequence_urbg",
-        "//absl/strings",
-        "//absl/strings:str_format",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "uniform_int_distribution_test",
-    size = "medium",
-    timeout = "long",
-    srcs = [
-        "uniform_int_distribution_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:raw_logging_internal",
-        "//absl/random/internal:distribution_test_util",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:sequence_urbg",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "uniform_real_distribution_test",
-    size = "medium",
-    srcs = [
-        "uniform_real_distribution_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = [
-        "no_test_android_arm",
-        "no_test_android_arm64",
-        "no_test_android_x86",
-    ],
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:raw_logging_internal",
-        "//absl/random/internal:distribution_test_util",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:sequence_urbg",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "zipf_distribution_test",
-    size = "medium",
-    srcs = [
-        "zipf_distribution_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distributions",
-        ":random",
-        "//absl/base:raw_logging_internal",
-        "//absl/random/internal:distribution_test_util",
-        "//absl/random/internal:pcg_engine",
-        "//absl/random/internal:sequence_urbg",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "bit_gen_ref_test",
-    size = "small",
-    srcs = ["bit_gen_ref_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":bit_gen_ref",
-        ":random",
-        "//absl/base:fast_type_id",
-        "//absl/random/internal:sequence_urbg",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "mocking_bit_gen_test",
-    size = "small",
-    srcs = ["mocking_bit_gen_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":bit_gen_ref",
-        ":mock_distributions",
-        ":mocking_bit_gen",
-        ":random",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "mock_distributions_test",
-    size = "small",
-    srcs = ["mock_distributions_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":mock_distributions",
-        ":mocking_bit_gen",
-        ":random",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "examples_test",
-    size = "small",
-    srcs = ["examples_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":random",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "seed_sequences_test",
-    size = "small",
-    srcs = ["seed_sequences_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":random",
-        ":seed_sequences",
-        "//absl/random/internal:nonsecure_base",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-BENCHMARK_TAGS = [
-    "benchmark",
-    "no_test_android_arm",
-    "no_test_android_arm64",
-    "no_test_android_x86",
-    "no_test_darwin_x86_64",
-    "no_test_ios_x86_64",
-    "no_test_loonix",
-    "no_test_msvc_x64",
-    "no_test_wasm",
-]
-
-# Benchmarks for various methods / test utilities
-cc_binary(
-    name = "benchmarks",
-    testonly = 1,
-    srcs = [
-        "benchmarks.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = BENCHMARK_TAGS,
-    deps = [
-        ":distributions",
-        ":random",
-        ":seed_sequences",
-        "//absl/base:core_headers",
-        "//absl/meta:type_traits",
-        "//absl/random/internal:fast_uniform_bits",
-        "//absl/random/internal:randen_engine",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/random/CMakeLists.txt b/third_party/abseil_cpp/absl/random/CMakeLists.txt
deleted file mode 100644
index 7d7bec83d9..0000000000
--- a/third_party/abseil_cpp/absl/random/CMakeLists.txt
+++ /dev/null
@@ -1,1207 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    random_random
-  HDRS
-    "random.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_distributions
-    absl::random_internal_nonsecure_base
-    absl::random_internal_pcg_engine
-    absl::random_internal_pool_urbg
-    absl::random_internal_randen_engine
-    absl::random_seed_sequences
-)
-
-absl_cc_library(
-  NAME
-    random_bit_gen_ref
-  HDRS
-    "bit_gen_ref.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::core_headers
-    absl::random_internal_distribution_caller
-    absl::random_internal_fast_uniform_bits
-    absl::type_traits
-)
-
-absl_cc_test(
-  NAME
-    random_bit_gen_ref_test
-  SRCS
-    "bit_gen_ref_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_bit_gen_ref
-    absl::random_random
-    absl::random_internal_sequence_urbg
-    absl::fast_type_id
-    gmock
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_mock_helpers
-  HDRS
-    "internal/mock_helpers.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::fast_type_id
-    absl::optional
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_mock_overload_set
-  HDRS
-    "internal/mock_overload_set.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_mocking_bit_gen
-    absl::random_internal_mock_helpers
-  TESTONLY
-)
-
-absl_cc_library(
-  NAME
-    random_mocking_bit_gen
-  HDRS
-    "mock_distributions.h"
-    "mocking_bit_gen.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::flat_hash_map
-    absl::raw_logging_internal
-    absl::random_distributions
-    absl::random_internal_distribution_caller
-    absl::random_internal_mock_overload_set
-    absl::random_random
-    absl::strings
-    absl::span
-    absl::type_traits
-    absl::utility
-    absl::variant
-    gmock
-    gtest
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    random_mock_distributions_test
-  SRCS
-    "mock_distributions_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_mocking_bit_gen
-    absl::random_random
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_mocking_bit_gen_test
-  SRCS
-    "mocking_bit_gen_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_bit_gen_ref
-    absl::random_mocking_bit_gen
-    absl::random_random
-    gmock
-    gtest_main
-)
-
-absl_cc_library(
-  NAME
-    random_distributions
-  SRCS
-    "discrete_distribution.cc"
-    "gaussian_distribution.cc"
-  HDRS
-    "bernoulli_distribution.h"
-    "beta_distribution.h"
-    "discrete_distribution.h"
-    "distributions.h"
-    "exponential_distribution.h"
-    "gaussian_distribution.h"
-    "log_uniform_int_distribution.h"
-    "poisson_distribution.h"
-    "uniform_int_distribution.h"
-    "uniform_real_distribution.h"
-    "zipf_distribution.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::base_internal
-    absl::config
-    absl::core_headers
-    absl::random_internal_generate_real
-    absl::random_internal_distribution_caller
-    absl::random_internal_fast_uniform_bits
-    absl::random_internal_fastmath
-    absl::random_internal_iostream_state_saver
-    absl::random_internal_traits
-    absl::random_internal_uniform_helper
-    absl::random_internal_wide_multiply
-    absl::strings
-    absl::type_traits
-)
-
-absl_cc_library(
-  NAME
-    random_seed_gen_exception
-  SRCS
-    "seed_gen_exception.cc"
-  HDRS
-    "seed_gen_exception.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-)
-
-absl_cc_library(
-  NAME
-    random_seed_sequences
-  SRCS
-    "seed_sequences.cc"
-  HDRS
-    "seed_sequences.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::inlined_vector
-    absl::random_internal_nonsecure_base
-    absl::random_internal_pool_urbg
-    absl::random_internal_salted_seed_seq
-    absl::random_internal_seed_material
-    absl::random_seed_gen_exception
-    absl::span
-)
-
-absl_cc_test(
-  NAME
-    random_bernoulli_distribution_test
-  SRCS
-    "bernoulli_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_distributions
-    absl::random_random
-    absl::random_internal_sequence_urbg
-    absl::random_internal_pcg_engine
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_beta_distribution_test
-  SRCS
-    "beta_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_distributions
-    absl::random_random
-    absl::random_internal_distribution_test_util
-    absl::random_internal_sequence_urbg
-    absl::random_internal_pcg_engine
-    absl::raw_logging_internal
-    absl::strings
-    absl::str_format
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_distributions_test
-  SRCS
-    "distributions_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_distributions
-    absl::random_random
-    absl::random_internal_distribution_test_util
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_generators_test
-  SRCS
-    "generators_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-    absl::random_distributions
-    absl::random_random
-    absl::raw_logging_internal
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_log_uniform_int_distribution_test
-  SRCS
-    "log_uniform_int_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-    absl::random_distributions
-    absl::random_internal_distribution_test_util
-    absl::random_internal_pcg_engine
-    absl::random_internal_sequence_urbg
-    absl::random_random
-    absl::raw_logging_internal
-    absl::strings
-    absl::str_format
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_discrete_distribution_test
-  SRCS
-    "discrete_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_distributions
-    absl::random_internal_distribution_test_util
-    absl::random_internal_pcg_engine
-    absl::random_internal_sequence_urbg
-    absl::random_random
-    absl::raw_logging_internal
-    absl::strings
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_poisson_distribution_test
-  SRCS
-    "poisson_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_distributions
-    absl::random_random
-    absl::core_headers
-    absl::flat_hash_map
-    absl::random_internal_distribution_test_util
-    absl::random_internal_pcg_engine
-    absl::random_internal_sequence_urbg
-    absl::raw_logging_internal
-    absl::strings
-    absl::str_format
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_exponential_distribution_test
-  SRCS
-    "exponential_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::core_headers
-    absl::random_distributions
-    absl::random_internal_distribution_test_util
-    absl::random_internal_pcg_engine
-    absl::random_internal_sequence_urbg
-    absl::random_random
-    absl::raw_logging_internal
-    absl::strings
-    absl::str_format
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_gaussian_distribution_test
-  SRCS
-    "gaussian_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::core_headers
-    absl::random_distributions
-    absl::random_internal_distribution_test_util
-    absl::random_internal_sequence_urbg
-    absl::random_random
-    absl::raw_logging_internal
-    absl::strings
-    absl::str_format
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_uniform_int_distribution_test
-  SRCS
-    "uniform_int_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_distributions
-    absl::random_internal_distribution_test_util
-    absl::random_internal_pcg_engine
-    absl::random_internal_sequence_urbg
-    absl::random_random
-    absl::raw_logging_internal
-    absl::strings
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_uniform_real_distribution_test
-  SRCS
-    "uniform_real_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_distributions
-    absl::random_internal_distribution_test_util
-    absl::random_internal_pcg_engine
-    absl::random_internal_sequence_urbg
-    absl::random_random
-    absl::strings
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_zipf_distribution_test
-  SRCS
-    "zipf_distribution_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_distributions
-    absl::random_internal_distribution_test_util
-    absl::random_internal_pcg_engine
-    absl::random_internal_sequence_urbg
-    absl::random_random
-    absl::raw_logging_internal
-    absl::strings
-    gmock
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_examples_test
-  SRCS
-    "examples_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_random
-    gtest_main
-)
-
-absl_cc_test(
-  NAME
-    random_seed_sequences_test
-  SRCS
-    "seed_sequences_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_seed_sequences
-    absl::random_internal_nonsecure_base
-    absl::random_random
-    gmock
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_traits
-  HDRS
-    "internal/traits.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_distribution_caller
-  HDRS
-    "internal/distribution_caller.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::utility
-    absl::fast_type_id
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_fast_uniform_bits
-  HDRS
-    "internal/fast_uniform_bits.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_seed_material
-  SRCS
-    "internal/seed_material.cc"
-  HDRS
-    "internal/seed_material.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-    $<$<BOOL:${MINGW}>:"bcrypt">
-  DEPS
-    absl::core_headers
-    absl::optional
-    absl::random_internal_fast_uniform_bits
-    absl::raw_logging_internal
-    absl::span
-    absl::strings
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_pool_urbg
-  SRCS
-    "internal/pool_urbg.cc"
-  HDRS
-    "internal/pool_urbg.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::base
-    absl::config
-    absl::core_headers
-    absl::endian
-    absl::random_internal_randen
-    absl::random_internal_seed_material
-    absl::random_internal_traits
-    absl::random_seed_gen_exception
-    absl::raw_logging_internal
-    absl::span
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_explicit_seed_seq
-  HDRS
-      "internal/random_internal_explicit_seed_seq.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-  TESTONLY
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_sequence_urbg
-  HDRS
-    "internal/sequence_urbg.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-  TESTONLY
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_salted_seed_seq
-  HDRS
-    "internal/salted_seed_seq.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::inlined_vector
-    absl::optional
-    absl::span
-    absl::random_internal_seed_material
-    absl::type_traits
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_iostream_state_saver
-  HDRS
-    "internal/iostream_state_saver.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::int128
-    absl::type_traits
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_generate_real
-  HDRS
-    "internal/generate_real.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::bits
-    absl::random_internal_fastmath
-    absl::random_internal_traits
-    absl::type_traits
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_wide_multiply
-  HDRS
-    "internal/wide_multiply.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::bits
-    absl::config
-    absl::int128
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_fastmath
-  HDRS
-    "internal/fastmath.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::bits
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_nonsecure_base
-  HDRS
-    "internal/nonsecure_base.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::core_headers
-    absl::optional
-    absl::random_internal_pool_urbg
-    absl::random_internal_salted_seed_seq
-    absl::random_internal_seed_material
-    absl::span
-    absl::type_traits
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_pcg_engine
-  HDRS
-    "internal/pcg_engine.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::int128
-    absl::random_internal_fastmath
-    absl::random_internal_iostream_state_saver
-    absl::type_traits
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_randen_engine
-  HDRS
-    "internal/randen_engine.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_iostream_state_saver
-    absl::random_internal_randen
-    absl::raw_logging_internal
-    absl::type_traits
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_platform
-  HDRS
-    "internal/randen_traits.h"
-    "internal/platform.h"
-  SRCS
-    "internal/randen_round_keys.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_randen
-  SRCS
-    "internal/randen.cc"
-  HDRS
-    "internal/randen.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_platform
-    absl::random_internal_randen_hwaes
-    absl::random_internal_randen_slow
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_randen_slow
-  SRCS
-    "internal/randen_slow.cc"
-  HDRS
-    "internal/randen_slow.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_platform
-    absl::config
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_randen_hwaes
-  SRCS
-    "internal/randen_detect.cc"
-  HDRS
-    "internal/randen_detect.h"
-    "internal/randen_hwaes.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-    ${ABSL_RANDOM_RANDEN_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_platform
-    absl::random_internal_randen_hwaes_impl
-    absl::config
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_randen_hwaes_impl
-  SRCS
-    "internal/randen_hwaes.cc"
-    "internal/randen_hwaes.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-    ${ABSL_RANDOM_RANDEN_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_platform
-    absl::config
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_distribution_test_util
-  SRCS
-    "internal/chi_square.cc"
-    "internal/distribution_test_util.cc"
-  HDRS
-    "internal/chi_square.h"
-    "internal/distribution_test_util.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::raw_logging_internal
-    absl::strings
-    absl::str_format
-    absl::span
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_traits_test
-  SRCS
-    "internal/traits_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_traits
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_generate_real_test
-  SRCS
-    "internal/generate_real_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::bits
-    absl::flags
-    absl::random_internal_generate_real
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_distribution_test_util_test
-  SRCS
-    "internal/distribution_test_util_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_distribution_test_util
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_fastmath_test
-  SRCS
-    "internal/fastmath_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_fastmath
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_explicit_seed_seq_test
-  SRCS
-    "internal/explicit_seed_seq_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_explicit_seed_seq
-    absl::random_seed_sequences
-    gmock
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_salted_seed_seq_test
-  SRCS
-    "internal/salted_seed_seq_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_salted_seed_seq
-    gmock
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_chi_square_test
-  SRCS
-    "internal/chi_square_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::core_headers
-    absl::random_internal_distribution_test_util
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_fast_uniform_bits_test
-  SRCS
-    "internal/fast_uniform_bits_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_fast_uniform_bits
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_nonsecure_base_test
-  SRCS
-    "internal/nonsecure_base_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_nonsecure_base
-    absl::random_random
-    absl::random_distributions
-    absl::random_seed_sequences
-    absl::strings
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_seed_material_test
-  SRCS
-    "internal/seed_material_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_seed_material
-    gmock
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_pool_urbg_test
-  SRCS
-    "internal/pool_urbg_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_pool_urbg
-    absl::span
-    absl::type_traits
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_pcg_engine_test
-  SRCS
-    "internal/pcg_engine_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_explicit_seed_seq
-    absl::random_internal_pcg_engine
-    absl::time
-    gmock
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_randen_engine_test
-  SRCS
-    "internal/randen_engine_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_explicit_seed_seq
-    absl::random_internal_randen_engine
-    absl::raw_logging_internal
-    absl::strings
-    absl::time
-    gmock
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_randen_test
-  SRCS
-    "internal/randen_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_randen
-    absl::type_traits
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_randen_slow_test
-  SRCS
-    "internal/randen_slow_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_randen_slow
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_randen_hwaes_test
-  SRCS
-    "internal/randen_hwaes_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_platform
-    absl::random_internal_randen_hwaes
-    absl::random_internal_randen_hwaes_impl
-    absl::raw_logging_internal
-    absl::str_format
-    gmock
-    gtest
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
-  NAME
-    random_internal_uniform_helper
-  HDRS
-    "internal/uniform_helper.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::config
-    absl::random_internal_traits
-    absl::type_traits
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_uniform_helper_test
-  SRCS
-    "internal/uniform_helper_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_uniform_helper
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_iostream_state_saver_test
-  SRCS
-    "internal/iostream_state_saver_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_iostream_state_saver
-    gtest_main
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_test(
-  NAME
-    random_internal_wide_multiply_test
-  SRCS
-      internal/wide_multiply_test.cc
-  COPTS
-    ${ABSL_TEST_COPTS}
-  LINKOPTS
-    ${ABSL_DEFAULT_LINKOPTS}
-  DEPS
-    absl::random_internal_wide_multiply
-    absl::bits
-    absl::int128
-    gtest_main
-)
diff --git a/third_party/abseil_cpp/absl/random/benchmarks.cc b/third_party/abseil_cpp/absl/random/benchmarks.cc
deleted file mode 100644
index 87bbb9810a..0000000000
--- a/third_party/abseil_cpp/absl/random/benchmarks.cc
+++ /dev/null
@@ -1,383 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Benchmarks for absl random distributions as well as a selection of the
-// C++ standard library random distributions.
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <initializer_list>
-#include <iterator>
-#include <limits>
-#include <random>
-#include <type_traits>
-#include <vector>
-
-#include "absl/base/macros.h"
-#include "absl/meta/type_traits.h"
-#include "absl/random/bernoulli_distribution.h"
-#include "absl/random/beta_distribution.h"
-#include "absl/random/exponential_distribution.h"
-#include "absl/random/gaussian_distribution.h"
-#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/random/internal/randen_engine.h"
-#include "absl/random/log_uniform_int_distribution.h"
-#include "absl/random/poisson_distribution.h"
-#include "absl/random/random.h"
-#include "absl/random/uniform_int_distribution.h"
-#include "absl/random/uniform_real_distribution.h"
-#include "absl/random/zipf_distribution.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-// Seed data to avoid reading random_device() for benchmarks.
-uint32_t kSeedData[] = {
-    0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
-    0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6,
-    0xFF34052E, 0xC5855664, 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A,
-    0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D,
-    0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, 0x699A18FF, 0x5664526C, 0xC2B19EE1,
-    0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65,
-    0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
-    0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9,
-    0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0x13198A2E, 0x03707344,
-};
-
-// PrecompiledSeedSeq provides kSeedData to a conforming
-// random engine to speed initialization in the benchmarks.
-class PrecompiledSeedSeq {
- public:
-  using result_type = uint32_t;
-
-  PrecompiledSeedSeq() {}
-
-  template <typename Iterator>
-  PrecompiledSeedSeq(Iterator begin, Iterator end) {}
-
-  template <typename T>
-  PrecompiledSeedSeq(std::initializer_list<T> il) {}
-
-  template <typename OutIterator>
-  void generate(OutIterator begin, OutIterator end) {
-    static size_t idx = 0;
-    for (; begin != end; begin++) {
-      *begin = kSeedData[idx++];
-      if (idx >= ABSL_ARRAYSIZE(kSeedData)) {
-        idx = 0;
-      }
-    }
-  }
-
-  size_t size() const { return ABSL_ARRAYSIZE(kSeedData); }
-
-  template <typename OutIterator>
-  void param(OutIterator out) const {
-    std::copy(std::begin(kSeedData), std::end(kSeedData), out);
-  }
-};
-
-// use_default_initialization<T> indicates whether the random engine
-// T must be default initialized, or whether we may initialize it using
-// a seed sequence. This is used because some engines do not accept seed
-// sequence-based initialization.
-template <typename E>
-using use_default_initialization = std::false_type;
-
-// make_engine<T, SSeq> returns a random_engine which is initialized,
-// either via the default constructor, when use_default_initialization<T>
-// is true, or via the indicated seed sequence, SSeq.
-template <typename Engine, typename SSeq = PrecompiledSeedSeq>
-typename absl::enable_if_t<!use_default_initialization<Engine>::value, Engine>
-make_engine() {
-  // Initialize the random engine using the seed sequence SSeq, which
-  // is constructed from the precompiled seed data.
-  SSeq seq(std::begin(kSeedData), std::end(kSeedData));
-  return Engine(seq);
-}
-
-template <typename Engine, typename SSeq = PrecompiledSeedSeq>
-typename absl::enable_if_t<use_default_initialization<Engine>::value, Engine>
-make_engine() {
-  // Initialize the random engine using the default constructor.
-  return Engine();
-}
-
-template <typename Engine, typename SSeq>
-void BM_Construct(benchmark::State& state) {
-  for (auto _ : state) {
-    auto rng = make_engine<Engine, SSeq>();
-    benchmark::DoNotOptimize(rng());
-  }
-}
-
-template <typename Engine>
-void BM_Direct(benchmark::State& state) {
-  using value_type = typename Engine::result_type;
-  // Direct use of the URBG.
-  auto rng = make_engine<Engine>();
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(rng());
-  }
-  state.SetBytesProcessed(sizeof(value_type) * state.iterations());
-}
-
-template <typename Engine>
-void BM_Generate(benchmark::State& state) {
-  // std::generate makes a copy of the RNG; thus this tests the
-  // copy-constructor efficiency.
-  using value_type = typename Engine::result_type;
-  std::vector<value_type> v(64);
-  auto rng = make_engine<Engine>();
-  while (state.KeepRunningBatch(64)) {
-    std::generate(std::begin(v), std::end(v), rng);
-  }
-}
-
-template <typename Engine, size_t elems>
-void BM_Shuffle(benchmark::State& state) {
-  // Direct use of the Engine.
-  std::vector<uint32_t> v(elems);
-  while (state.KeepRunningBatch(elems)) {
-    auto rng = make_engine<Engine>();
-    std::shuffle(std::begin(v), std::end(v), rng);
-  }
-}
-
-template <typename Engine, size_t elems>
-void BM_ShuffleReuse(benchmark::State& state) {
-  // Direct use of the Engine.
-  std::vector<uint32_t> v(elems);
-  auto rng = make_engine<Engine>();
-  while (state.KeepRunningBatch(elems)) {
-    std::shuffle(std::begin(v), std::end(v), rng);
-  }
-}
-
-template <typename Engine, typename Dist, typename... Args>
-void BM_Dist(benchmark::State& state, Args&&... args) {
-  using value_type = typename Dist::result_type;
-  auto rng = make_engine<Engine>();
-  Dist dis{std::forward<Args>(args)...};
-  // Compare the following loop performance:
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(dis(rng));
-  }
-  state.SetBytesProcessed(sizeof(value_type) * state.iterations());
-}
-
-template <typename Engine, typename Dist>
-void BM_Large(benchmark::State& state) {
-  using value_type = typename Dist::result_type;
-  volatile value_type kMin = 0;
-  volatile value_type kMax = std::numeric_limits<value_type>::max() / 2 + 1;
-  BM_Dist<Engine, Dist>(state, kMin, kMax);
-}
-
-template <typename Engine, typename Dist>
-void BM_Small(benchmark::State& state) {
-  using value_type = typename Dist::result_type;
-  volatile value_type kMin = 0;
-  volatile value_type kMax = std::numeric_limits<value_type>::max() / 64 + 1;
-  BM_Dist<Engine, Dist>(state, kMin, kMax);
-}
-
-template <typename Engine, typename Dist, int A>
-void BM_Bernoulli(benchmark::State& state) {
-  volatile double a = static_cast<double>(A) / 1000000;
-  BM_Dist<Engine, Dist>(state, a);
-}
-
-template <typename Engine, typename Dist, int A, int B>
-void BM_Beta(benchmark::State& state) {
-  using value_type = typename Dist::result_type;
-  volatile value_type a = static_cast<value_type>(A) / 100;
-  volatile value_type b = static_cast<value_type>(B) / 100;
-  BM_Dist<Engine, Dist>(state, a, b);
-}
-
-template <typename Engine, typename Dist, int A>
-void BM_Gamma(benchmark::State& state) {
-  using value_type = typename Dist::result_type;
-  volatile value_type a = static_cast<value_type>(A) / 100;
-  BM_Dist<Engine, Dist>(state, a);
-}
-
-template <typename Engine, typename Dist, int A = 100>
-void BM_Poisson(benchmark::State& state) {
-  volatile double a = static_cast<double>(A) / 100;
-  BM_Dist<Engine, Dist>(state, a);
-}
-
-template <typename Engine, typename Dist, int Q = 2, int V = 1>
-void BM_Zipf(benchmark::State& state) {
-  using value_type = typename Dist::result_type;
-  volatile double q = Q;
-  volatile double v = V;
-  BM_Dist<Engine, Dist>(state, std::numeric_limits<value_type>::max(), q, v);
-}
-
-template <typename Engine, typename Dist>
-void BM_Thread(benchmark::State& state) {
-  using value_type = typename Dist::result_type;
-  auto rng = make_engine<Engine>();
-  Dist dis{};
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(dis(rng));
-  }
-  state.SetBytesProcessed(sizeof(value_type) * state.iterations());
-}
-
-// NOTES:
-//
-// std::geometric_distribution is similar to the zipf distributions.
-// The algorithm for the geometric_distribution is, basically,
-// floor(log(1-X) / log(1-p))
-
-// Normal benchmark suite
-#define BM_BASIC(Engine)                                                       \
-  BENCHMARK_TEMPLATE(BM_Construct, Engine, PrecompiledSeedSeq);                \
-  BENCHMARK_TEMPLATE(BM_Construct, Engine, std::seed_seq);                     \
-  BENCHMARK_TEMPLATE(BM_Direct, Engine);                                       \
-  BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 10);                                  \
-  BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 100);                                 \
-  BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 1000);                                \
-  BENCHMARK_TEMPLATE(BM_ShuffleReuse, Engine, 100);                            \
-  BENCHMARK_TEMPLATE(BM_ShuffleReuse, Engine, 1000);                           \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine,                                          \
-                     absl::random_internal::FastUniformBits<uint32_t>);        \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine,                                          \
-                     absl::random_internal::FastUniformBits<uint64_t>);        \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, std::uniform_int_distribution<int32_t>); \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, std::uniform_int_distribution<int64_t>); \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine,                                          \
-                     absl::uniform_int_distribution<int32_t>);                 \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine,                                          \
-                     absl::uniform_int_distribution<int64_t>);                 \
-  BENCHMARK_TEMPLATE(BM_Large, Engine,                                         \
-                     std::uniform_int_distribution<int32_t>);                  \
-  BENCHMARK_TEMPLATE(BM_Large, Engine,                                         \
-                     std::uniform_int_distribution<int64_t>);                  \
-  BENCHMARK_TEMPLATE(BM_Large, Engine,                                         \
-                     absl::uniform_int_distribution<int32_t>);                 \
-  BENCHMARK_TEMPLATE(BM_Large, Engine,                                         \
-                     absl::uniform_int_distribution<int64_t>);                 \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, std::uniform_real_distribution<float>);  \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, std::uniform_real_distribution<double>); \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, absl::uniform_real_distribution<float>); \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, absl::uniform_real_distribution<double>)
-
-#define BM_COPY(Engine) BENCHMARK_TEMPLATE(BM_Generate, Engine)
-
-#define BM_THREAD(Engine)                                           \
-  BENCHMARK_TEMPLATE(BM_Thread, Engine,                             \
-                     absl::uniform_int_distribution<int64_t>)       \
-      ->ThreadPerCpu();                                             \
-  BENCHMARK_TEMPLATE(BM_Thread, Engine,                             \
-                     absl::uniform_real_distribution<double>)       \
-      ->ThreadPerCpu();                                             \
-  BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 100)->ThreadPerCpu();      \
-  BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 1000)->ThreadPerCpu();     \
-  BENCHMARK_TEMPLATE(BM_ShuffleReuse, Engine, 100)->ThreadPerCpu(); \
-  BENCHMARK_TEMPLATE(BM_ShuffleReuse, Engine, 1000)->ThreadPerCpu();
-
-#define BM_EXTENDED(Engine)                                                    \
-  /* -------------- Extended Uniform -----------------------*/                 \
-  BENCHMARK_TEMPLATE(BM_Small, Engine,                                         \
-                     std::uniform_int_distribution<int32_t>);                  \
-  BENCHMARK_TEMPLATE(BM_Small, Engine,                                         \
-                     std::uniform_int_distribution<int64_t>);                  \
-  BENCHMARK_TEMPLATE(BM_Small, Engine,                                         \
-                     absl::uniform_int_distribution<int32_t>);                 \
-  BENCHMARK_TEMPLATE(BM_Small, Engine,                                         \
-                     absl::uniform_int_distribution<int64_t>);                 \
-  BENCHMARK_TEMPLATE(BM_Small, Engine, std::uniform_real_distribution<float>); \
-  BENCHMARK_TEMPLATE(BM_Small, Engine,                                         \
-                     std::uniform_real_distribution<double>);                  \
-  BENCHMARK_TEMPLATE(BM_Small, Engine,                                         \
-                     absl::uniform_real_distribution<float>);                  \
-  BENCHMARK_TEMPLATE(BM_Small, Engine,                                         \
-                     absl::uniform_real_distribution<double>);                 \
-  /* -------------- Other -----------------------*/                            \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, std::normal_distribution<double>);       \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, absl::gaussian_distribution<double>);    \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, std::exponential_distribution<double>);  \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, absl::exponential_distribution<double>); \
-  BENCHMARK_TEMPLATE(BM_Poisson, Engine, std::poisson_distribution<int64_t>,   \
-                     100);                                                     \
-  BENCHMARK_TEMPLATE(BM_Poisson, Engine, absl::poisson_distribution<int64_t>,  \
-                     100);                                                     \
-  BENCHMARK_TEMPLATE(BM_Poisson, Engine, std::poisson_distribution<int64_t>,   \
-                     10 * 100);                                                \
-  BENCHMARK_TEMPLATE(BM_Poisson, Engine, absl::poisson_distribution<int64_t>,  \
-                     10 * 100);                                                \
-  BENCHMARK_TEMPLATE(BM_Poisson, Engine, std::poisson_distribution<int64_t>,   \
-                     13 * 100);                                                \
-  BENCHMARK_TEMPLATE(BM_Poisson, Engine, absl::poisson_distribution<int64_t>,  \
-                     13 * 100);                                                \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine,                                          \
-                     absl::log_uniform_int_distribution<int32_t>);             \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine,                                          \
-                     absl::log_uniform_int_distribution<int64_t>);             \
-  BENCHMARK_TEMPLATE(BM_Dist, Engine, std::geometric_distribution<int64_t>);   \
-  BENCHMARK_TEMPLATE(BM_Zipf, Engine, absl::zipf_distribution<uint64_t>);      \
-  BENCHMARK_TEMPLATE(BM_Zipf, Engine, absl::zipf_distribution<uint64_t>, 2,    \
-                     3);                                                       \
-  BENCHMARK_TEMPLATE(BM_Bernoulli, Engine, std::bernoulli_distribution,        \
-                     257305);                                                  \
-  BENCHMARK_TEMPLATE(BM_Bernoulli, Engine, absl::bernoulli_distribution,       \
-                     257305);                                                  \
-  BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<double>, 65,     \
-                     41);                                                      \
-  BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<double>, 99,     \
-                     330);                                                     \
-  BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<double>, 150,    \
-                     150);                                                     \
-  BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<double>, 410,    \
-                     580);                                                     \
-  BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<float>, 65, 41); \
-  BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<float>, 99,      \
-                     330);                                                     \
-  BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<float>, 150,     \
-                     150);                                                     \
-  BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<float>, 410,     \
-                     580);                                                     \
-  BENCHMARK_TEMPLATE(BM_Gamma, Engine, std::gamma_distribution<float>, 199);   \
-  BENCHMARK_TEMPLATE(BM_Gamma, Engine, std::gamma_distribution<double>, 199);
-
-// ABSL Recommended interfaces.
-BM_BASIC(absl::InsecureBitGen);  // === pcg64_2018_engine
-BM_BASIC(absl::BitGen);    // === randen_engine<uint64_t>.
-BM_THREAD(absl::BitGen);
-BM_EXTENDED(absl::BitGen);
-
-// Instantiate benchmarks for multiple engines.
-using randen_engine_64 = absl::random_internal::randen_engine<uint64_t>;
-using randen_engine_32 = absl::random_internal::randen_engine<uint32_t>;
-
-// Comparison interfaces.
-BM_BASIC(std::mt19937_64);
-BM_COPY(std::mt19937_64);
-BM_EXTENDED(std::mt19937_64);
-BM_BASIC(randen_engine_64);
-BM_COPY(randen_engine_64);
-BM_EXTENDED(randen_engine_64);
-
-BM_BASIC(std::mt19937);
-BM_COPY(std::mt19937);
-BM_BASIC(randen_engine_32);
-BM_COPY(randen_engine_32);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/bernoulli_distribution.h b/third_party/abseil_cpp/absl/random/bernoulli_distribution.h
deleted file mode 100644
index 25bd0d5ca4..0000000000
--- a/third_party/abseil_cpp/absl/random/bernoulli_distribution.h
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_
-#define ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_
-
-#include <cstdint>
-#include <istream>
-#include <limits>
-
-#include "absl/base/optimization.h"
-#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/random/internal/iostream_state_saver.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::bernoulli_distribution is a drop in replacement for
-// std::bernoulli_distribution. It guarantees that (given a perfect
-// UniformRandomBitGenerator) the acceptance probability is *exactly* equal to
-// the given double.
-//
-// The implementation assumes that double is IEEE754
-class bernoulli_distribution {
- public:
-  using result_type = bool;
-
-  class param_type {
-   public:
-    using distribution_type = bernoulli_distribution;
-
-    explicit param_type(double p = 0.5) : prob_(p) {
-      assert(p >= 0.0 && p <= 1.0);
-    }
-
-    double p() const { return prob_; }
-
-    friend bool operator==(const param_type& p1, const param_type& p2) {
-      return p1.p() == p2.p();
-    }
-    friend bool operator!=(const param_type& p1, const param_type& p2) {
-      return p1.p() != p2.p();
-    }
-
-   private:
-    double prob_;
-  };
-
-  bernoulli_distribution() : bernoulli_distribution(0.5) {}
-
-  explicit bernoulli_distribution(double p) : param_(p) {}
-
-  explicit bernoulli_distribution(param_type p) : param_(p) {}
-
-  // no-op
-  void reset() {}
-
-  template <typename URBG>
-  bool operator()(URBG& g) {  // NOLINT(runtime/references)
-    return Generate(param_.p(), g);
-  }
-
-  template <typename URBG>
-  bool operator()(URBG& g,  // NOLINT(runtime/references)
-                  const param_type& param) {
-    return Generate(param.p(), g);
-  }
-
-  param_type param() const { return param_; }
-  void param(const param_type& param) { param_ = param; }
-
-  double p() const { return param_.p(); }
-
-  result_type(min)() const { return false; }
-  result_type(max)() const { return true; }
-
-  friend bool operator==(const bernoulli_distribution& d1,
-                         const bernoulli_distribution& d2) {
-    return d1.param_ == d2.param_;
-  }
-
-  friend bool operator!=(const bernoulli_distribution& d1,
-                         const bernoulli_distribution& d2) {
-    return d1.param_ != d2.param_;
-  }
-
- private:
-  static constexpr uint64_t kP32 = static_cast<uint64_t>(1) << 32;
-
-  template <typename URBG>
-  static bool Generate(double p, URBG& g);  // NOLINT(runtime/references)
-
-  param_type param_;
-};
-
-template <typename CharT, typename Traits>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    const bernoulli_distribution& x) {
-  auto saver = random_internal::make_ostream_state_saver(os);
-  os.precision(random_internal::stream_precision_helper<double>::kPrecision);
-  os << x.p();
-  return os;
-}
-
-template <typename CharT, typename Traits>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-    bernoulli_distribution& x) {            // NOLINT(runtime/references)
-  auto saver = random_internal::make_istream_state_saver(is);
-  auto p = random_internal::read_floating_point<double>(is);
-  if (!is.fail()) {
-    x.param(bernoulli_distribution::param_type(p));
-  }
-  return is;
-}
-
-template <typename URBG>
-bool bernoulli_distribution::Generate(double p,
-                                      URBG& g) {  // NOLINT(runtime/references)
-  random_internal::FastUniformBits<uint32_t> fast_u32;
-
-  while (true) {
-    // There are two aspects of the definition of `c` below that are worth
-    // commenting on.  First, because `p` is in the range [0, 1], `c` is in the
-    // range [0, 2^32] which does not fit in a uint32_t and therefore requires
-    // 64 bits.
-    //
-    // Second, `c` is constructed by first casting explicitly to a signed
-    // integer and then converting implicitly to an unsigned integer of the same
-    // size.  This is done because the hardware conversion instructions produce
-    // signed integers from double; if taken as a uint64_t the conversion would
-    // be wrong for doubles greater than 2^63 (not relevant in this use-case).
-    // If converted directly to an unsigned integer, the compiler would end up
-    // emitting code to handle such large values that are not relevant due to
-    // the known bounds on `c`.  To avoid these extra instructions this
-    // implementation converts first to the signed type and then use the
-    // implicit conversion to unsigned (which is a no-op).
-    const uint64_t c = static_cast<int64_t>(p * kP32);
-    const uint32_t v = fast_u32(g);
-    // FAST PATH: this path fails with probability 1/2^32.  Note that simply
-    // returning v <= c would approximate P very well (up to an absolute error
-    // of 1/2^32); the slow path (taken in that range of possible error, in the
-    // case of equality) eliminates the remaining error.
-    if (ABSL_PREDICT_TRUE(v != c)) return v < c;
-
-    // It is guaranteed that `q` is strictly less than 1, because if `q` were
-    // greater than or equal to 1, the same would be true for `p`. Certainly `p`
-    // cannot be greater than 1, and if `p == 1`, then the fast path would
-    // necessary have been taken already.
-    const double q = static_cast<double>(c) / kP32;
-
-    // The probability of acceptance on the fast path is `q` and so the
-    // probability of acceptance here should be `p - q`.
-    //
-    // Note that `q` is obtained from `p` via some shifts and conversions, the
-    // upshot of which is that `q` is simply `p` with some of the
-    // least-significant bits of its mantissa set to zero. This means that the
-    // difference `p - q` will not have any rounding errors. To see why, pretend
-    // that double has 10 bits of resolution and q is obtained from `p` in such
-    // a way that the 4 least-significant bits of its mantissa are set to zero.
-    // For example:
-    //   p   = 1.1100111011 * 2^-1
-    //   q   = 1.1100110000 * 2^-1
-    // p - q = 1.011        * 2^-8
-    // The difference `p - q` has exactly the nonzero mantissa bits that were
-    // "lost" in `q` producing a number which is certainly representable in a
-    // double.
-    const double left = p - q;
-
-    // By construction, the probability of being on this slow path is 1/2^32, so
-    // P(accept in slow path) = P(accept| in slow path) * P(slow path),
-    // which means the probability of acceptance here is `1 / (left * kP32)`:
-    const double here = left * kP32;
-
-    // The simplest way to compute the result of this trial is to repeat the
-    // whole algorithm with the new probability. This terminates because even
-    // given  arbitrarily unfriendly "random" bits, each iteration either
-    // multiplies a tiny probability by 2^32 (if c == 0) or strips off some
-    // number of nonzero mantissa bits. That process is bounded.
-    if (here == 0) return false;
-    p = here;
-  }
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/bernoulli_distribution_test.cc b/third_party/abseil_cpp/absl/random/bernoulli_distribution_test.cc
deleted file mode 100644
index b250f8787c..0000000000
--- a/third_party/abseil_cpp/absl/random/bernoulli_distribution_test.cc
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/bernoulli_distribution.h"
-
-#include <cmath>
-#include <cstddef>
-#include <random>
-#include <sstream>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/random/internal/pcg_engine.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-
-namespace {
-
-class BernoulliTest : public testing::TestWithParam<std::pair<double, size_t>> {
-};
-
-TEST_P(BernoulliTest, Serialize) {
-  const double d = GetParam().first;
-  absl::bernoulli_distribution before(d);
-
-  {
-    absl::bernoulli_distribution via_param{
-        absl::bernoulli_distribution::param_type(d)};
-    EXPECT_EQ(via_param, before);
-  }
-
-  std::stringstream ss;
-  ss << before;
-  absl::bernoulli_distribution after(0.6789);
-
-  EXPECT_NE(before.p(), after.p());
-  EXPECT_NE(before.param(), after.param());
-  EXPECT_NE(before, after);
-
-  ss >> after;
-
-  EXPECT_EQ(before.p(), after.p());
-  EXPECT_EQ(before.param(), after.param());
-  EXPECT_EQ(before, after);
-}
-
-TEST_P(BernoulliTest, Accuracy) {
-  // Sadly, the claim to fame for this implementation is precise accuracy, which
-  // is very, very hard to measure, the improvements come as trials approach the
-  // limit of double accuracy; thus the outcome differs from the
-  // std::bernoulli_distribution with a probability of approximately 1 in 2^-53.
-  const std::pair<double, size_t> para = GetParam();
-  size_t trials = para.second;
-  double p = para.first;
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng(0x2B7E151628AED2A6);
-
-  size_t yes = 0;
-  absl::bernoulli_distribution dist(p);
-  for (size_t i = 0; i < trials; ++i) {
-    if (dist(rng)) yes++;
-  }
-
-  // Compute the distribution parameters for a binomial test, using a normal
-  // approximation for the confidence interval, as there are a sufficiently
-  // large number of trials that the central limit theorem applies.
-  const double stddev_p = std::sqrt((p * (1.0 - p)) / trials);
-  const double expected = trials * p;
-  const double stddev = trials * stddev_p;
-
-  // 5 sigma, approved by Richard Feynman
-  EXPECT_NEAR(yes, expected, 5 * stddev)
-      << "@" << p << ", "
-      << std::abs(static_cast<double>(yes) - expected) / stddev << " stddev";
-}
-
-// There must be many more trials to make the mean approximately normal for `p`
-// closes to 0 or 1.
-INSTANTIATE_TEST_SUITE_P(
-    All, BernoulliTest,
-    ::testing::Values(
-        // Typical values.
-        std::make_pair(0, 30000), std::make_pair(1e-3, 30000000),
-        std::make_pair(0.1, 3000000), std::make_pair(0.5, 3000000),
-        std::make_pair(0.9, 30000000), std::make_pair(0.999, 30000000),
-        std::make_pair(1, 30000),
-        // Boundary cases.
-        std::make_pair(std::nextafter(1.0, 0.0), 1),  // ~1 - epsilon
-        std::make_pair(std::numeric_limits<double>::epsilon(), 1),
-        std::make_pair(std::nextafter(std::numeric_limits<double>::min(),
-                                      1.0),  // min + epsilon
-                       1),
-        std::make_pair(std::numeric_limits<double>::min(),  // smallest normal
-                       1),
-        std::make_pair(
-            std::numeric_limits<double>::denorm_min(),  // smallest denorm
-            1),
-        std::make_pair(std::numeric_limits<double>::min() / 2, 1),  // denorm
-        std::make_pair(std::nextafter(std::numeric_limits<double>::min(),
-                                      0.0),  // denorm_max
-                       1)));
-
-// NOTE: absl::bernoulli_distribution is not guaranteed to be stable.
-TEST(BernoulliTest, StabilityTest) {
-  // absl::bernoulli_distribution stability relies on FastUniformBits and
-  // integer arithmetic.
-  absl::random_internal::sequence_urbg urbg({
-      0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-      0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-      0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-      0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull,
-      0x4864f22c059bf29eull, 0x247856d8b862665cull, 0xe46e86e9a1337e10ull,
-      0xd8c8541f3519b133ull, 0xe75b5162c567b9e4ull, 0xf732e5ded7009c5bull,
-      0xb170b98353121eacull, 0x1ec2e8986d2362caull, 0x814c8e35fe9a961aull,
-      0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull, 0x1224e62c978bbc7full,
-      0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull, 0x1bbc23cfa8fac721ull,
-      0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull, 0x836d794457c08849ull,
-      0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull, 0xb12d74fdd718c8c5ull,
-      0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull, 0x5738341045ba0d85ull,
-      0xe3fd722dc65ad09eull, 0x5a14fd21ea2a5705ull, 0x14e6ea4d6edb0c73ull,
-      0x275b0dc7e0a18acfull, 0x36cebe0d2653682eull, 0x0361e9b23861596bull,
-  });
-
-  // Generate a string of '0' and '1' for the distribution output.
-  auto generate = [&urbg](absl::bernoulli_distribution& dist) {
-    std::string output;
-    output.reserve(36);
-    urbg.reset();
-    for (int i = 0; i < 35; i++) {
-      output.append(dist(urbg) ? "1" : "0");
-    }
-    return output;
-  };
-
-  const double kP = 0.0331289862362;
-  {
-    absl::bernoulli_distribution dist(kP);
-    auto v = generate(dist);
-    EXPECT_EQ(35, urbg.invocations());
-    EXPECT_EQ(v, "00000000000010000000000010000000000") << dist;
-  }
-  {
-    absl::bernoulli_distribution dist(kP * 10.0);
-    auto v = generate(dist);
-    EXPECT_EQ(35, urbg.invocations());
-    EXPECT_EQ(v, "00000100010010010010000011000011010") << dist;
-  }
-  {
-    absl::bernoulli_distribution dist(kP * 20.0);
-    auto v = generate(dist);
-    EXPECT_EQ(35, urbg.invocations());
-    EXPECT_EQ(v, "00011110010110110011011111110111011") << dist;
-  }
-  {
-    absl::bernoulli_distribution dist(1.0 - kP);
-    auto v = generate(dist);
-    EXPECT_EQ(35, urbg.invocations());
-    EXPECT_EQ(v, "11111111111111111111011111111111111") << dist;
-  }
-}
-
-TEST(BernoulliTest, StabilityTest2) {
-  absl::random_internal::sequence_urbg urbg(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  // Generate a string of '0' and '1' for the distribution output.
-  auto generate = [&urbg](absl::bernoulli_distribution& dist) {
-    std::string output;
-    output.reserve(13);
-    urbg.reset();
-    for (int i = 0; i < 12; i++) {
-      output.append(dist(urbg) ? "1" : "0");
-    }
-    return output;
-  };
-
-  constexpr double b0 = 1.0 / 13.0 / 0.2;
-  constexpr double b1 = 2.0 / 13.0 / 0.2;
-  constexpr double b3 = (5.0 / 13.0 / 0.2) - ((1 - b0) + (1 - b1) + (1 - b1));
-  {
-    absl::bernoulli_distribution dist(b0);
-    auto v = generate(dist);
-    EXPECT_EQ(12, urbg.invocations());
-    EXPECT_EQ(v, "000011100101") << dist;
-  }
-  {
-    absl::bernoulli_distribution dist(b1);
-    auto v = generate(dist);
-    EXPECT_EQ(12, urbg.invocations());
-    EXPECT_EQ(v, "001111101101") << dist;
-  }
-  {
-    absl::bernoulli_distribution dist(b3);
-    auto v = generate(dist);
-    EXPECT_EQ(12, urbg.invocations());
-    EXPECT_EQ(v, "001111101111") << dist;
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/beta_distribution.h b/third_party/abseil_cpp/absl/random/beta_distribution.h
deleted file mode 100644
index c154066fb8..0000000000
--- a/third_party/abseil_cpp/absl/random/beta_distribution.h
+++ /dev/null
@@ -1,427 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_BETA_DISTRIBUTION_H_
-#define ABSL_RANDOM_BETA_DISTRIBUTION_H_
-
-#include <cassert>
-#include <cmath>
-#include <istream>
-#include <limits>
-#include <ostream>
-#include <type_traits>
-
-#include "absl/meta/type_traits.h"
-#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/random/internal/fastmath.h"
-#include "absl/random/internal/generate_real.h"
-#include "absl/random/internal/iostream_state_saver.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::beta_distribution:
-// Generate a floating-point variate conforming to a Beta distribution:
-//   pdf(x) \propto x^(alpha-1) * (1-x)^(beta-1),
-// where the params alpha and beta are both strictly positive real values.
-//
-// The support is the open interval (0, 1), but the return value might be equal
-// to 0 or 1, due to numerical errors when alpha and beta are very different.
-//
-// Usage note: One usage is that alpha and beta are counts of number of
-// successes and failures. When the total number of trials are large, consider
-// approximating a beta distribution with a Gaussian distribution with the same
-// mean and variance. One could use the skewness, which depends only on the
-// smaller of alpha and beta when the number of trials are sufficiently large,
-// to quantify how far a beta distribution is from the normal distribution.
-template <typename RealType = double>
-class beta_distribution {
- public:
-  using result_type = RealType;
-
-  class param_type {
-   public:
-    using distribution_type = beta_distribution;
-
-    explicit param_type(result_type alpha, result_type beta)
-        : alpha_(alpha), beta_(beta) {
-      assert(alpha >= 0);
-      assert(beta >= 0);
-      assert(alpha <= (std::numeric_limits<result_type>::max)());
-      assert(beta <= (std::numeric_limits<result_type>::max)());
-      if (alpha == 0 || beta == 0) {
-        method_ = DEGENERATE_SMALL;
-        x_ = (alpha >= beta) ? 1 : 0;
-        return;
-      }
-      // a_ = min(beta, alpha), b_ = max(beta, alpha).
-      if (beta < alpha) {
-        inverted_ = true;
-        a_ = beta;
-        b_ = alpha;
-      } else {
-        inverted_ = false;
-        a_ = alpha;
-        b_ = beta;
-      }
-      if (a_ <= 1 && b_ >= ThresholdForLargeA()) {
-        method_ = DEGENERATE_SMALL;
-        x_ = inverted_ ? result_type(1) : result_type(0);
-        return;
-      }
-      // For threshold values, see also:
-      // Evaluation of Beta Generation Algorithms, Ying-Chao Hung, et. al.
-      // February, 2009.
-      if ((b_ < 1.0 && a_ + b_ <= 1.2) || a_ <= ThresholdForSmallA()) {
-        // Choose Joehnk over Cheng when it's faster or when Cheng encounters
-        // numerical issues.
-        method_ = JOEHNK;
-        a_ = result_type(1) / alpha_;
-        b_ = result_type(1) / beta_;
-        if (std::isinf(a_) || std::isinf(b_)) {
-          method_ = DEGENERATE_SMALL;
-          x_ = inverted_ ? result_type(1) : result_type(0);
-        }
-        return;
-      }
-      if (a_ >= ThresholdForLargeA()) {
-        method_ = DEGENERATE_LARGE;
-        // Note: on PPC for long double, evaluating
-        // `std::numeric_limits::max() / ThresholdForLargeA` results in NaN.
-        result_type r = a_ / b_;
-        x_ = (inverted_ ? result_type(1) : r) / (1 + r);
-        return;
-      }
-      x_ = a_ + b_;
-      log_x_ = std::log(x_);
-      if (a_ <= 1) {
-        method_ = CHENG_BA;
-        y_ = result_type(1) / a_;
-        gamma_ = a_ + a_;
-        return;
-      }
-      method_ = CHENG_BB;
-      result_type r = (a_ - 1) / (b_ - 1);
-      y_ = std::sqrt((1 + r) / (b_ * r * 2 - r + 1));
-      gamma_ = a_ + result_type(1) / y_;
-    }
-
-    result_type alpha() const { return alpha_; }
-    result_type beta() const { return beta_; }
-
-    friend bool operator==(const param_type& a, const param_type& b) {
-      return a.alpha_ == b.alpha_ && a.beta_ == b.beta_;
-    }
-
-    friend bool operator!=(const param_type& a, const param_type& b) {
-      return !(a == b);
-    }
-
-   private:
-    friend class beta_distribution;
-
-#ifdef _MSC_VER
-    // MSVC does not have constexpr implementations for std::log and std::exp
-    // so they are computed at runtime.
-#define ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR
-#else
-#define ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR constexpr
-#endif
-
-    // The threshold for whether std::exp(1/a) is finite.
-    // Note that this value is quite large, and a smaller a_ is NOT abnormal.
-    static ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR result_type
-    ThresholdForSmallA() {
-      return result_type(1) /
-             std::log((std::numeric_limits<result_type>::max)());
-    }
-
-    // The threshold for whether a * std::log(a) is finite.
-    static ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR result_type
-    ThresholdForLargeA() {
-      return std::exp(
-          std::log((std::numeric_limits<result_type>::max)()) -
-          std::log(std::log((std::numeric_limits<result_type>::max)())) -
-          ThresholdPadding());
-    }
-
-#undef ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR
-
-    // Pad the threshold for large A for long double on PPC. This is done via a
-    // template specialization below.
-    static constexpr result_type ThresholdPadding() { return 0; }
-
-    enum Method {
-      JOEHNK,    // Uses algorithm Joehnk
-      CHENG_BA,  // Uses algorithm BA in Cheng
-      CHENG_BB,  // Uses algorithm BB in Cheng
-
-      // Note: See also:
-      //   Hung et al. Evaluation of beta generation algorithms. Communications
-      //   in Statistics-Simulation and Computation 38.4 (2009): 750-770.
-      // especially:
-      //   Zechner, Heinz, and Ernst Stadlober. Generating beta variates via
-      //   patchwork rejection. Computing 50.1 (1993): 1-18.
-
-      DEGENERATE_SMALL,  // a_ is abnormally small.
-      DEGENERATE_LARGE,  // a_ is abnormally large.
-    };
-
-    result_type alpha_;
-    result_type beta_;
-
-    result_type a_;  // the smaller of {alpha, beta}, or 1.0/alpha_ in JOEHNK
-    result_type b_;  // the larger of {alpha, beta}, or 1.0/beta_ in JOEHNK
-    result_type x_;  // alpha + beta, or the result in degenerate cases
-    result_type log_x_;  // log(x_)
-    result_type y_;      // "beta" in Cheng
-    result_type gamma_;  // "gamma" in Cheng
-
-    Method method_;
-
-    // Placing this last for optimal alignment.
-    // Whether alpha_ != a_, i.e. true iff alpha_ > beta_.
-    bool inverted_;
-
-    static_assert(std::is_floating_point<RealType>::value,
-                  "Class-template absl::beta_distribution<> must be "
-                  "parameterized using a floating-point type.");
-  };
-
-  beta_distribution() : beta_distribution(1) {}
-
-  explicit beta_distribution(result_type alpha, result_type beta = 1)
-      : param_(alpha, beta) {}
-
-  explicit beta_distribution(const param_type& p) : param_(p) {}
-
-  void reset() {}
-
-  // Generating functions
-  template <typename URBG>
-  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
-    return (*this)(g, param_);
-  }
-
-  template <typename URBG>
-  result_type operator()(URBG& g,  // NOLINT(runtime/references)
-                         const param_type& p);
-
-  param_type param() const { return param_; }
-  void param(const param_type& p) { param_ = p; }
-
-  result_type(min)() const { return 0; }
-  result_type(max)() const { return 1; }
-
-  result_type alpha() const { return param_.alpha(); }
-  result_type beta() const { return param_.beta(); }
-
-  friend bool operator==(const beta_distribution& a,
-                         const beta_distribution& b) {
-    return a.param_ == b.param_;
-  }
-  friend bool operator!=(const beta_distribution& a,
-                         const beta_distribution& b) {
-    return a.param_ != b.param_;
-  }
-
- private:
-  template <typename URBG>
-  result_type AlgorithmJoehnk(URBG& g,  // NOLINT(runtime/references)
-                              const param_type& p);
-
-  template <typename URBG>
-  result_type AlgorithmCheng(URBG& g,  // NOLINT(runtime/references)
-                             const param_type& p);
-
-  template <typename URBG>
-  result_type DegenerateCase(URBG& g,  // NOLINT(runtime/references)
-                             const param_type& p) {
-    if (p.method_ == param_type::DEGENERATE_SMALL && p.alpha_ == p.beta_) {
-      // Returns 0 or 1 with equal probability.
-      random_internal::FastUniformBits<uint8_t> fast_u8;
-      return static_cast<result_type>((fast_u8(g) & 0x10) !=
-                                      0);  // pick any single bit.
-    }
-    return p.x_;
-  }
-
-  param_type param_;
-  random_internal::FastUniformBits<uint64_t> fast_u64_;
-};
-
-#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
-    defined(__ppc__) || defined(__PPC__)
-// PPC needs a more stringent boundary for long double.
-template <>
-constexpr long double
-beta_distribution<long double>::param_type::ThresholdPadding() {
-  return 10;
-}
-#endif
-
-template <typename RealType>
-template <typename URBG>
-typename beta_distribution<RealType>::result_type
-beta_distribution<RealType>::AlgorithmJoehnk(
-    URBG& g,  // NOLINT(runtime/references)
-    const param_type& p) {
-  using random_internal::GeneratePositiveTag;
-  using random_internal::GenerateRealFromBits;
-  using real_type =
-      absl::conditional_t<std::is_same<RealType, float>::value, float, double>;
-
-  // Based on Joehnk, M. D. Erzeugung von betaverteilten und gammaverteilten
-  // Zufallszahlen. Metrika 8.1 (1964): 5-15.
-  // This method is described in Knuth, Vol 2 (Third Edition), pp 134.
-
-  result_type u, v, x, y, z;
-  for (;;) {
-    u = GenerateRealFromBits<real_type, GeneratePositiveTag, false>(
-        fast_u64_(g));
-    v = GenerateRealFromBits<real_type, GeneratePositiveTag, false>(
-        fast_u64_(g));
-
-    // Direct method. std::pow is slow for float, so rely on the optimizer to
-    // remove the std::pow() path for that case.
-    if (!std::is_same<float, result_type>::value) {
-      x = std::pow(u, p.a_);
-      y = std::pow(v, p.b_);
-      z = x + y;
-      if (z > 1) {
-        // Reject if and only if `x + y > 1.0`
-        continue;
-      }
-      if (z > 0) {
-        // When both alpha and beta are small, x and y are both close to 0, so
-        // divide by (x+y) directly may result in nan.
-        return x / z;
-      }
-    }
-
-    // Log transform.
-    // x = log( pow(u, p.a_) ), y = log( pow(v, p.b_) )
-    // since u, v <= 1.0,  x, y < 0.
-    x = std::log(u) * p.a_;
-    y = std::log(v) * p.b_;
-    if (!std::isfinite(x) || !std::isfinite(y)) {
-      continue;
-    }
-    // z = log( pow(u, a) + pow(v, b) )
-    z = x > y ? (x + std::log(1 + std::exp(y - x)))
-              : (y + std::log(1 + std::exp(x - y)));
-    // Reject iff log(x+y) > 0.
-    if (z > 0) {
-      continue;
-    }
-    return std::exp(x - z);
-  }
-}
-
-template <typename RealType>
-template <typename URBG>
-typename beta_distribution<RealType>::result_type
-beta_distribution<RealType>::AlgorithmCheng(
-    URBG& g,  // NOLINT(runtime/references)
-    const param_type& p) {
-  using random_internal::GeneratePositiveTag;
-  using random_internal::GenerateRealFromBits;
-  using real_type =
-      absl::conditional_t<std::is_same<RealType, float>::value, float, double>;
-
-  // Based on Cheng, Russell CH. Generating beta variates with nonintegral
-  // shape parameters. Communications of the ACM 21.4 (1978): 317-322.
-  // (https://dl.acm.org/citation.cfm?id=359482).
-  static constexpr result_type kLogFour =
-      result_type(1.3862943611198906188344642429163531361);  // log(4)
-  static constexpr result_type kS =
-      result_type(2.6094379124341003746007593332261876);  // 1+log(5)
-
-  const bool use_algorithm_ba = (p.method_ == param_type::CHENG_BA);
-  result_type u1, u2, v, w, z, r, s, t, bw_inv, lhs;
-  for (;;) {
-    u1 = GenerateRealFromBits<real_type, GeneratePositiveTag, false>(
-        fast_u64_(g));
-    u2 = GenerateRealFromBits<real_type, GeneratePositiveTag, false>(
-        fast_u64_(g));
-    v = p.y_ * std::log(u1 / (1 - u1));
-    w = p.a_ * std::exp(v);
-    bw_inv = result_type(1) / (p.b_ + w);
-    r = p.gamma_ * v - kLogFour;
-    s = p.a_ + r - w;
-    z = u1 * u1 * u2;
-    if (!use_algorithm_ba && s + kS >= 5 * z) {
-      break;
-    }
-    t = std::log(z);
-    if (!use_algorithm_ba && s >= t) {
-      break;
-    }
-    lhs = p.x_ * (p.log_x_ + std::log(bw_inv)) + r;
-    if (lhs >= t) {
-      break;
-    }
-  }
-  return p.inverted_ ? (1 - w * bw_inv) : w * bw_inv;
-}
-
-template <typename RealType>
-template <typename URBG>
-typename beta_distribution<RealType>::result_type
-beta_distribution<RealType>::operator()(URBG& g,  // NOLINT(runtime/references)
-                                        const param_type& p) {
-  switch (p.method_) {
-    case param_type::JOEHNK:
-      return AlgorithmJoehnk(g, p);
-    case param_type::CHENG_BA:
-      ABSL_FALLTHROUGH_INTENDED;
-    case param_type::CHENG_BB:
-      return AlgorithmCheng(g, p);
-    default:
-      return DegenerateCase(g, p);
-  }
-}
-
-template <typename CharT, typename Traits, typename RealType>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    const beta_distribution<RealType>& x) {
-  auto saver = random_internal::make_ostream_state_saver(os);
-  os.precision(random_internal::stream_precision_helper<RealType>::kPrecision);
-  os << x.alpha() << os.fill() << x.beta();
-  return os;
-}
-
-template <typename CharT, typename Traits, typename RealType>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-    beta_distribution<RealType>& x) {       // NOLINT(runtime/references)
-  using result_type = typename beta_distribution<RealType>::result_type;
-  using param_type = typename beta_distribution<RealType>::param_type;
-  result_type alpha, beta;
-
-  auto saver = random_internal::make_istream_state_saver(is);
-  alpha = random_internal::read_floating_point<result_type>(is);
-  if (is.fail()) return is;
-  beta = random_internal::read_floating_point<result_type>(is);
-  if (!is.fail()) {
-    x.param(param_type(alpha, beta));
-  }
-  return is;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_BETA_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/beta_distribution_test.cc b/third_party/abseil_cpp/absl/random/beta_distribution_test.cc
deleted file mode 100644
index 277e4dc6ee..0000000000
--- a/third_party/abseil_cpp/absl/random/beta_distribution_test.cc
+++ /dev/null
@@ -1,619 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/beta_distribution.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <iterator>
-#include <random>
-#include <sstream>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/chi_square.h"
-#include "absl/random/internal/distribution_test_util.h"
-#include "absl/random/internal/pcg_engine.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_replace.h"
-#include "absl/strings/strip.h"
-
-namespace {
-
-template <typename IntType>
-class BetaDistributionInterfaceTest : public ::testing::Test {};
-
-using RealTypes = ::testing::Types<float, double, long double>;
-TYPED_TEST_CASE(BetaDistributionInterfaceTest, RealTypes);
-
-TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) {
-  // The threshold for whether std::exp(1/a) is finite.
-  const TypeParam kSmallA =
-      1.0f / std::log((std::numeric_limits<TypeParam>::max)());
-  // The threshold for whether a * std::log(a) is finite.
-  const TypeParam kLargeA =
-      std::exp(std::log((std::numeric_limits<TypeParam>::max)()) -
-               std::log(std::log((std::numeric_limits<TypeParam>::max)())));
-  const TypeParam kLargeAPPC = std::exp(
-      std::log((std::numeric_limits<TypeParam>::max)()) -
-      std::log(std::log((std::numeric_limits<TypeParam>::max)())) - 10.0f);
-  using param_type = typename absl::beta_distribution<TypeParam>::param_type;
-
-  constexpr int kCount = 1000;
-  absl::InsecureBitGen gen;
-  const TypeParam kValues[] = {
-      TypeParam(1e-20), TypeParam(1e-12), TypeParam(1e-8), TypeParam(1e-4),
-      TypeParam(1e-3), TypeParam(0.1), TypeParam(0.25),
-      std::nextafter(TypeParam(0.5), TypeParam(0)),  // 0.5 - epsilon
-      std::nextafter(TypeParam(0.5), TypeParam(1)),  // 0.5 + epsilon
-      TypeParam(0.5), TypeParam(1.0),                //
-      std::nextafter(TypeParam(1), TypeParam(0)),    // 1 - epsilon
-      std::nextafter(TypeParam(1), TypeParam(2)),    // 1 + epsilon
-      TypeParam(12.5), TypeParam(1e2), TypeParam(1e8), TypeParam(1e12),
-      TypeParam(1e20),                        //
-      kSmallA,                                //
-      std::nextafter(kSmallA, TypeParam(0)),  //
-      std::nextafter(kSmallA, TypeParam(1)),  //
-      kLargeA,                                //
-      std::nextafter(kLargeA, TypeParam(0)),  //
-      std::nextafter(kLargeA, std::numeric_limits<TypeParam>::max()),
-      kLargeAPPC,  //
-      std::nextafter(kLargeAPPC, TypeParam(0)),
-      std::nextafter(kLargeAPPC, std::numeric_limits<TypeParam>::max()),
-      // Boundary cases.
-      std::numeric_limits<TypeParam>::max(),
-      std::numeric_limits<TypeParam>::epsilon(),
-      std::nextafter(std::numeric_limits<TypeParam>::min(),
-                     TypeParam(1)),                  // min + epsilon
-      std::numeric_limits<TypeParam>::min(),         // smallest normal
-      std::numeric_limits<TypeParam>::denorm_min(),  // smallest denorm
-      std::numeric_limits<TypeParam>::min() / 2,     // denorm
-      std::nextafter(std::numeric_limits<TypeParam>::min(),
-                     TypeParam(0)),  // denorm_max
-  };
-  for (TypeParam alpha : kValues) {
-    for (TypeParam beta : kValues) {
-      ABSL_INTERNAL_LOG(
-          INFO, absl::StrFormat("Smoke test for Beta(%a, %a)", alpha, beta));
-
-      param_type param(alpha, beta);
-      absl::beta_distribution<TypeParam> before(alpha, beta);
-      EXPECT_EQ(before.alpha(), param.alpha());
-      EXPECT_EQ(before.beta(), param.beta());
-
-      {
-        absl::beta_distribution<TypeParam> via_param(param);
-        EXPECT_EQ(via_param, before);
-        EXPECT_EQ(via_param.param(), before.param());
-      }
-
-      // Smoke test.
-      for (int i = 0; i < kCount; ++i) {
-        auto sample = before(gen);
-        EXPECT_TRUE(std::isfinite(sample));
-        EXPECT_GE(sample, before.min());
-        EXPECT_LE(sample, before.max());
-      }
-
-      // Validate stream serialization.
-      std::stringstream ss;
-      ss << before;
-      absl::beta_distribution<TypeParam> after(3.8f, 1.43f);
-      EXPECT_NE(before.alpha(), after.alpha());
-      EXPECT_NE(before.beta(), after.beta());
-      EXPECT_NE(before.param(), after.param());
-      EXPECT_NE(before, after);
-
-      ss >> after;
-
-#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
-    defined(__ppc__) || defined(__PPC__)
-      if (std::is_same<TypeParam, long double>::value) {
-        // Roundtripping floating point values requires sufficient precision
-        // to reconstruct the exact value. It turns out that long double
-        // has some errors doing this on ppc.
-        if (alpha <= std::numeric_limits<double>::max() &&
-            alpha >= std::numeric_limits<double>::lowest()) {
-          EXPECT_EQ(static_cast<double>(before.alpha()),
-                    static_cast<double>(after.alpha()))
-              << ss.str();
-        }
-        if (beta <= std::numeric_limits<double>::max() &&
-            beta >= std::numeric_limits<double>::lowest()) {
-          EXPECT_EQ(static_cast<double>(before.beta()),
-                    static_cast<double>(after.beta()))
-              << ss.str();
-        }
-        continue;
-      }
-#endif
-
-      EXPECT_EQ(before.alpha(), after.alpha());
-      EXPECT_EQ(before.beta(), after.beta());
-      EXPECT_EQ(before, after)           //
-          << ss.str() << " "             //
-          << (ss.good() ? "good " : "")  //
-          << (ss.bad() ? "bad " : "")    //
-          << (ss.eof() ? "eof " : "")    //
-          << (ss.fail() ? "fail " : "");
-    }
-  }
-}
-
-TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) {
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng(0x2B7E151628AED2A6);
-
-  // Extreme cases when the params are abnormal.
-  constexpr int kCount = 1000;
-  const TypeParam kSmallValues[] = {
-      std::numeric_limits<TypeParam>::min(),
-      std::numeric_limits<TypeParam>::denorm_min(),
-      std::nextafter(std::numeric_limits<TypeParam>::min(),
-                     TypeParam(0)),  // denorm_max
-      std::numeric_limits<TypeParam>::epsilon(),
-  };
-  const TypeParam kLargeValues[] = {
-      std::numeric_limits<TypeParam>::max() * static_cast<TypeParam>(0.9999),
-      std::numeric_limits<TypeParam>::max() - 1,
-      std::numeric_limits<TypeParam>::max(),
-  };
-  {
-    // Small alpha and beta.
-    // Useful WolframAlpha plots:
-    //   * plot InverseBetaRegularized[x, 0.0001, 0.0001] from 0.495 to 0.505
-    //   * Beta[1.0, 0.0000001, 0.0000001]
-    //   * Beta[0.9999, 0.0000001, 0.0000001]
-    for (TypeParam alpha : kSmallValues) {
-      for (TypeParam beta : kSmallValues) {
-        int zeros = 0;
-        int ones = 0;
-        absl::beta_distribution<TypeParam> d(alpha, beta);
-        for (int i = 0; i < kCount; ++i) {
-          TypeParam x = d(rng);
-          if (x == 0.0) {
-            zeros++;
-          } else if (x == 1.0) {
-            ones++;
-          }
-        }
-        EXPECT_EQ(ones + zeros, kCount);
-        if (alpha == beta) {
-          EXPECT_NE(ones, 0);
-          EXPECT_NE(zeros, 0);
-        }
-      }
-    }
-  }
-  {
-    // Small alpha, large beta.
-    // Useful WolframAlpha plots:
-    //   * plot InverseBetaRegularized[x, 0.0001, 10000] from 0.995 to 1
-    //   * Beta[0, 0.0000001, 1000000]
-    //   * Beta[0.001, 0.0000001, 1000000]
-    //   * Beta[1, 0.0000001, 1000000]
-    for (TypeParam alpha : kSmallValues) {
-      for (TypeParam beta : kLargeValues) {
-        absl::beta_distribution<TypeParam> d(alpha, beta);
-        for (int i = 0; i < kCount; ++i) {
-          EXPECT_EQ(d(rng), 0.0);
-        }
-      }
-    }
-  }
-  {
-    // Large alpha, small beta.
-    // Useful WolframAlpha plots:
-    //   * plot InverseBetaRegularized[x, 10000, 0.0001] from 0 to 0.001
-    //   * Beta[0.99, 1000000, 0.0000001]
-    //   * Beta[1, 1000000, 0.0000001]
-    for (TypeParam alpha : kLargeValues) {
-      for (TypeParam beta : kSmallValues) {
-        absl::beta_distribution<TypeParam> d(alpha, beta);
-        for (int i = 0; i < kCount; ++i) {
-          EXPECT_EQ(d(rng), 1.0);
-        }
-      }
-    }
-  }
-  {
-    // Large alpha and beta.
-    absl::beta_distribution<TypeParam> d(std::numeric_limits<TypeParam>::max(),
-                                         std::numeric_limits<TypeParam>::max());
-    for (int i = 0; i < kCount; ++i) {
-      EXPECT_EQ(d(rng), 0.5);
-    }
-  }
-  {
-    // Large alpha and beta but unequal.
-    absl::beta_distribution<TypeParam> d(
-        std::numeric_limits<TypeParam>::max(),
-        std::numeric_limits<TypeParam>::max() * 0.9999);
-    for (int i = 0; i < kCount; ++i) {
-      TypeParam x = d(rng);
-      EXPECT_NE(x, 0.5f);
-      EXPECT_FLOAT_EQ(x, 0.500025f);
-    }
-  }
-}
-
-class BetaDistributionModel {
- public:
-  explicit BetaDistributionModel(::testing::tuple<double, double> p)
-      : alpha_(::testing::get<0>(p)), beta_(::testing::get<1>(p)) {}
-
-  double Mean() const { return alpha_ / (alpha_ + beta_); }
-
-  double Variance() const {
-    return alpha_ * beta_ / (alpha_ + beta_ + 1) / (alpha_ + beta_) /
-           (alpha_ + beta_);
-  }
-
-  double Kurtosis() const {
-    return 3 + 6 *
-                   ((alpha_ - beta_) * (alpha_ - beta_) * (alpha_ + beta_ + 1) -
-                    alpha_ * beta_ * (2 + alpha_ + beta_)) /
-                   alpha_ / beta_ / (alpha_ + beta_ + 2) / (alpha_ + beta_ + 3);
-  }
-
- protected:
-  const double alpha_;
-  const double beta_;
-};
-
-class BetaDistributionTest
-    : public ::testing::TestWithParam<::testing::tuple<double, double>>,
-      public BetaDistributionModel {
- public:
-  BetaDistributionTest() : BetaDistributionModel(GetParam()) {}
-
- protected:
-  template <class D>
-  bool SingleZTestOnMeanAndVariance(double p, size_t samples);
-
-  template <class D>
-  bool SingleChiSquaredTest(double p, size_t samples, size_t buckets);
-
-  absl::InsecureBitGen rng_;
-};
-
-template <class D>
-bool BetaDistributionTest::SingleZTestOnMeanAndVariance(double p,
-                                                        size_t samples) {
-  D dis(alpha_, beta_);
-
-  std::vector<double> data;
-  data.reserve(samples);
-  for (size_t i = 0; i < samples; i++) {
-    const double variate = dis(rng_);
-    EXPECT_FALSE(std::isnan(variate));
-    // Note that equality is allowed on both sides.
-    EXPECT_GE(variate, 0.0);
-    EXPECT_LE(variate, 1.0);
-    data.push_back(variate);
-  }
-
-  // We validate that the sample mean and sample variance are indeed from a
-  // Beta distribution with the given shape parameters.
-  const auto m = absl::random_internal::ComputeDistributionMoments(data);
-
-  // The variance of the sample mean is variance / n.
-  const double mean_stddev = std::sqrt(Variance() / static_cast<double>(m.n));
-
-  // The variance of the sample variance is (approximately):
-  //   (kurtosis - 1) * variance^2 / n
-  const double variance_stddev = std::sqrt(
-      (Kurtosis() - 1) * Variance() * Variance() / static_cast<double>(m.n));
-  // z score for the sample variance.
-  const double z_variance = (m.variance - Variance()) / variance_stddev;
-
-  const double max_err = absl::random_internal::MaxErrorTolerance(p);
-  const double z_mean = absl::random_internal::ZScore(Mean(), m);
-  const bool pass =
-      absl::random_internal::Near("z", z_mean, 0.0, max_err) &&
-      absl::random_internal::Near("z_variance", z_variance, 0.0, max_err);
-  if (!pass) {
-    ABSL_INTERNAL_LOG(
-        INFO,
-        absl::StrFormat(
-            "Beta(%f, %f), "
-            "mean: sample %f, expect %f, which is %f stddevs away, "
-            "variance: sample %f, expect %f, which is %f stddevs away.",
-            alpha_, beta_, m.mean, Mean(),
-            std::abs(m.mean - Mean()) / mean_stddev, m.variance, Variance(),
-            std::abs(m.variance - Variance()) / variance_stddev));
-  }
-  return pass;
-}
-
-template <class D>
-bool BetaDistributionTest::SingleChiSquaredTest(double p, size_t samples,
-                                                size_t buckets) {
-  constexpr double kErr = 1e-7;
-  std::vector<double> cutoffs, expected;
-  const double bucket_width = 1.0 / static_cast<double>(buckets);
-  int i = 1;
-  int unmerged_buckets = 0;
-  for (; i < buckets; ++i) {
-    const double p = bucket_width * static_cast<double>(i);
-    const double boundary =
-        absl::random_internal::BetaIncompleteInv(alpha_, beta_, p);
-    // The intention is to add `boundary` to the list of `cutoffs`. It becomes
-    // problematic, however, when the boundary values are not monotone, due to
-    // numerical issues when computing the inverse regularized incomplete
-    // Beta function. In these cases, we merge that bucket with its previous
-    // neighbor and merge their expected counts.
-    if ((cutoffs.empty() && boundary < kErr) ||
-        (!cutoffs.empty() && boundary <= cutoffs.back())) {
-      unmerged_buckets++;
-      continue;
-    }
-    if (boundary >= 1.0 - 1e-10) {
-      break;
-    }
-    cutoffs.push_back(boundary);
-    expected.push_back(static_cast<double>(1 + unmerged_buckets) *
-                       bucket_width * static_cast<double>(samples));
-    unmerged_buckets = 0;
-  }
-  cutoffs.push_back(std::numeric_limits<double>::infinity());
-  // Merge all remaining buckets.
-  expected.push_back(static_cast<double>(buckets - i + 1) * bucket_width *
-                     static_cast<double>(samples));
-  // Make sure that we don't merge all the buckets, making this test
-  // meaningless.
-  EXPECT_GE(cutoffs.size(), 3) << alpha_ << ", " << beta_;
-
-  D dis(alpha_, beta_);
-
-  std::vector<int32_t> counts(cutoffs.size(), 0);
-  for (int i = 0; i < samples; i++) {
-    const double x = dis(rng_);
-    auto it = std::upper_bound(cutoffs.begin(), cutoffs.end(), x);
-    counts[std::distance(cutoffs.begin(), it)]++;
-  }
-
-  // Null-hypothesis is that the distribution is beta distributed with the
-  // provided alpha, beta params (not estimated from the data).
-  const int dof = cutoffs.size() - 1;
-
-  const double chi_square = absl::random_internal::ChiSquare(
-      counts.begin(), counts.end(), expected.begin(), expected.end());
-  const bool pass =
-      (absl::random_internal::ChiSquarePValue(chi_square, dof) >= p);
-  if (!pass) {
-    for (int i = 0; i < cutoffs.size(); i++) {
-      ABSL_INTERNAL_LOG(
-          INFO, absl::StrFormat("cutoff[%d] = %f, actual count %d, expected %d",
-                                i, cutoffs[i], counts[i],
-                                static_cast<int>(expected[i])));
-    }
-
-    ABSL_INTERNAL_LOG(
-        INFO, absl::StrFormat(
-                  "Beta(%f, %f) %s %f, p = %f", alpha_, beta_,
-                  absl::random_internal::kChiSquared, chi_square,
-                  absl::random_internal::ChiSquarePValue(chi_square, dof)));
-  }
-  return pass;
-}
-
-TEST_P(BetaDistributionTest, TestSampleStatistics) {
-  static constexpr int kRuns = 20;
-  static constexpr double kPFail = 0.02;
-  const double p =
-      absl::random_internal::RequiredSuccessProbability(kPFail, kRuns);
-  static constexpr int kSampleCount = 10000;
-  static constexpr int kBucketCount = 100;
-  int failed = 0;
-  for (int i = 0; i < kRuns; ++i) {
-    if (!SingleZTestOnMeanAndVariance<absl::beta_distribution<double>>(
-            p, kSampleCount)) {
-      failed++;
-    }
-    if (!SingleChiSquaredTest<absl::beta_distribution<double>>(
-            0.005, kSampleCount, kBucketCount)) {
-      failed++;
-    }
-  }
-  // Set so that the test is not flaky at --runs_per_test=10000
-  EXPECT_LE(failed, 5);
-}
-
-std::string ParamName(
-    const ::testing::TestParamInfo<::testing::tuple<double, double>>& info) {
-  std::string name = absl::StrCat("alpha_", ::testing::get<0>(info.param),
-                                  "__beta_", ::testing::get<1>(info.param));
-  return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
-}
-
-INSTANTIATE_TEST_CASE_P(
-    TestSampleStatisticsCombinations, BetaDistributionTest,
-    ::testing::Combine(::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4),
-                       ::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4)),
-    ParamName);
-
-INSTANTIATE_TEST_CASE_P(
-    TestSampleStatistics_SelectedPairs, BetaDistributionTest,
-    ::testing::Values(std::make_pair(0.5, 1000), std::make_pair(1000, 0.5),
-                      std::make_pair(900, 1000), std::make_pair(10000, 20000),
-                      std::make_pair(4e5, 2e7), std::make_pair(1e7, 1e5)),
-    ParamName);
-
-// NOTE: absl::beta_distribution is not guaranteed to be stable.
-TEST(BetaDistributionTest, StabilityTest) {
-  // absl::beta_distribution stability relies on the stability of
-  // absl::random_interna::RandU64ToDouble, std::exp, std::log, std::pow,
-  // and std::sqrt.
-  //
-  // This test also depends on the stability of std::frexp.
-  using testing::ElementsAre;
-  absl::random_internal::sequence_urbg urbg({
-      0xffff00000000e6c8ull, 0xffff0000000006c8ull, 0x800003766295CFA9ull,
-      0x11C819684E734A41ull, 0x832603766295CFA9ull, 0x7fbe76c8b4395800ull,
-      0xB3472DCA7B14A94Aull, 0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull,
-      0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, 0x00035C904C70A239ull,
-      0x00009E0BCBAADE14ull, 0x0000000000622CA7ull, 0x4864f22c059bf29eull,
-      0x247856d8b862665cull, 0xe46e86e9a1337e10ull, 0xd8c8541f3519b133ull,
-      0xffe75b52c567b9e4ull, 0xfffff732e5709c5bull, 0xff1f7f0b983532acull,
-      0x1ec2e8986d2362caull, 0xC332DDEFBE6C5AA5ull, 0x6558218568AB9702ull,
-      0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, 0xECDD4775619F1510ull,
-      0x814c8e35fe9a961aull, 0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull,
-      0x1224e62c978bbc7full, 0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull,
-      0x1bbc23cfa8fac721ull, 0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull,
-      0x836d794457c08849ull, 0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull,
-      0xb12d74fdd718c8c5ull, 0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull,
-      0x5738341045ba0d85ull, 0xf3fd722dc65ad09eull, 0xfa14fd21ea2a5705ull,
-      0xffe6ea4d6edb0c73ull, 0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull,
-      0xEAAD8E716B93D5A0ull, 0xD08ED1D0AFC725E0ull, 0x8E3C5B2F8E7594B7ull,
-      0x8FF6E2FBF2122B64ull, 0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull,
-      0xD1CFF191B3A8C1ADull, 0x2F2F2218BE0E1777ull, 0xEA752DFE8B021FA1ull,
-  });
-
-  // Convert the real-valued result into a unit64 where we compare
-  // 5 (float) or 10 (double) decimal digits plus the base-2 exponent.
-  auto float_to_u64 = [](float d) {
-    int exp = 0;
-    auto f = std::frexp(d, &exp);
-    return (static_cast<uint64_t>(1e5 * f) * 10000) + std::abs(exp);
-  };
-  auto double_to_u64 = [](double d) {
-    int exp = 0;
-    auto f = std::frexp(d, &exp);
-    return (static_cast<uint64_t>(1e10 * f) * 10000) + std::abs(exp);
-  };
-
-  std::vector<uint64_t> output(20);
-  {
-    // Algorithm Joehnk (float)
-    absl::beta_distribution<float> dist(0.1f, 0.2f);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return float_to_u64(dist(urbg)); });
-    EXPECT_EQ(44, urbg.invocations());
-    EXPECT_THAT(output,  //
-                testing::ElementsAre(
-                    998340000, 619030004, 500000001, 999990000, 996280000,
-                    500000001, 844740004, 847210001, 999970000, 872320000,
-                    585480007, 933280000, 869080042, 647670031, 528240004,
-                    969980004, 626050008, 915930002, 833440033, 878040015));
-  }
-
-  urbg.reset();
-  {
-    // Algorithm Joehnk (double)
-    absl::beta_distribution<double> dist(0.1, 0.2);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return double_to_u64(dist(urbg)); });
-    EXPECT_EQ(44, urbg.invocations());
-    EXPECT_THAT(
-        output,  //
-        testing::ElementsAre(
-            99834713000000, 61903356870004, 50000000000001, 99999721170000,
-            99628374770000, 99999999990000, 84474397860004, 84721276240001,
-            99997407490000, 87232528120000, 58548364780007, 93328932910000,
-            86908237770042, 64767917930031, 52824581970004, 96998544140004,
-            62605946270008, 91593604380002, 83345031740033, 87804397230015));
-  }
-
-  urbg.reset();
-  {
-    // Algorithm Cheng 1
-    absl::beta_distribution<double> dist(0.9, 2.0);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return double_to_u64(dist(urbg)); });
-    EXPECT_EQ(62, urbg.invocations());
-    EXPECT_THAT(
-        output,  //
-        testing::ElementsAre(
-            62069004780001, 64433204450001, 53607416560000, 89644295430008,
-            61434586310019, 55172615890002, 62187161490000, 56433684810003,
-            80454622050005, 86418558710003, 92920514700001, 64645184680001,
-            58549183380000, 84881283650005, 71078728590002, 69949694970000,
-            73157461710001, 68592191300001, 70747623900000, 78584696930005));
-  }
-
-  urbg.reset();
-  {
-    // Algorithm Cheng 2
-    absl::beta_distribution<double> dist(1.5, 2.5);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return double_to_u64(dist(urbg)); });
-    EXPECT_EQ(54, urbg.invocations());
-    EXPECT_THAT(
-        output,  //
-        testing::ElementsAre(
-            75000029250001, 76751482860001, 53264575220000, 69193133650005,
-            78028324470013, 91573587560002, 59167523770000, 60658618560002,
-            80075870540000, 94141320460004, 63196592770003, 78883906300002,
-            96797992590001, 76907587800001, 56645167560000, 65408302280003,
-            53401156320001, 64731238570000, 83065573750001, 79788333820001));
-  }
-}
-
-// This is an implementation-specific test. If any part of the implementation
-// changes, then it is likely that this test will change as well.  Also, if
-// dependencies of the distribution change, such as RandU64ToDouble, then this
-// is also likely to change.
-TEST(BetaDistributionTest, AlgorithmBounds) {
-  {
-    absl::random_internal::sequence_urbg urbg(
-        {0x7fbe76c8b4395800ull, 0x8000000000000000ull});
-    // u=0.499, v=0.5
-    absl::beta_distribution<double> dist(1e-4, 1e-4);
-    double a = dist(urbg);
-    EXPECT_EQ(a, 2.0202860861567108529e-09);
-    EXPECT_EQ(2, urbg.invocations());
-  }
-
-  // Test that both the float & double algorithms appropriately reject the
-  // initial draw.
-  {
-    // 1/alpha = 1/beta = 2.
-    absl::beta_distribution<float> dist(0.5, 0.5);
-
-    // first two outputs are close to 1.0 - epsilon,
-    // thus:  (u ^ 2 + v ^ 2) > 1.0
-    absl::random_internal::sequence_urbg urbg(
-        {0xffff00000006e6c8ull, 0xffff00000007c7c8ull, 0x800003766295CFA9ull,
-         0x11C819684E734A41ull});
-    {
-      double y = absl::beta_distribution<double>(0.5, 0.5)(urbg);
-      EXPECT_EQ(4, urbg.invocations());
-      EXPECT_EQ(y, 0.9810668952633862) << y;
-    }
-
-    // ...and:  log(u) * a ~= log(v) * b ~= -0.02
-    // thus z ~= -0.02 + log(1 + e(~0))
-    //        ~= -0.02 + 0.69
-    // thus z > 0
-    urbg.reset();
-    {
-      float x = absl::beta_distribution<float>(0.5, 0.5)(urbg);
-      EXPECT_EQ(4, urbg.invocations());
-      EXPECT_NEAR(0.98106688261032104, x, 0.0000005) << x << "f";
-    }
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/bit_gen_ref.h b/third_party/abseil_cpp/absl/random/bit_gen_ref.h
deleted file mode 100644
index 9555460fd4..0000000000
--- a/third_party/abseil_cpp/absl/random/bit_gen_ref.h
+++ /dev/null
@@ -1,181 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: bit_gen_ref.h
-// -----------------------------------------------------------------------------
-//
-// This header defines a bit generator "reference" class, for use in interfaces
-// that take both Abseil (e.g. `absl::BitGen`) and standard library (e.g.
-// `std::mt19937`) bit generators.
-
-#ifndef ABSL_RANDOM_BIT_GEN_REF_H_
-#define ABSL_RANDOM_BIT_GEN_REF_H_
-
-#include "absl/base/internal/fast_type_id.h"
-#include "absl/base/macros.h"
-#include "absl/meta/type_traits.h"
-#include "absl/random/internal/distribution_caller.h"
-#include "absl/random/internal/fast_uniform_bits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-template <typename URBG, typename = void, typename = void, typename = void>
-struct is_urbg : std::false_type {};
-
-template <typename URBG>
-struct is_urbg<
-    URBG,
-    absl::enable_if_t<std::is_same<
-        typename URBG::result_type,
-        typename std::decay<decltype((URBG::min)())>::type>::value>,
-    absl::enable_if_t<std::is_same<
-        typename URBG::result_type,
-        typename std::decay<decltype((URBG::max)())>::type>::value>,
-    absl::enable_if_t<std::is_same<
-        typename URBG::result_type,
-        typename std::decay<decltype(std::declval<URBG>()())>::type>::value>>
-    : std::true_type {};
-
-template <typename>
-struct DistributionCaller;
-class MockHelpers;
-
-}  // namespace random_internal
-
-// -----------------------------------------------------------------------------
-// absl::BitGenRef
-// -----------------------------------------------------------------------------
-//
-// `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic
-// non-owning "reference" interface for use in place of any specific uniform
-// random bit generator (URBG). This class may be used for both Abseil
-// (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g
-// `std::mt19937`, `std::minstd_rand`) bit generators.
-//
-// Like other reference classes, `absl::BitGenRef` does not own the
-// underlying bit generator, and the underlying instance must outlive the
-// `absl::BitGenRef`.
-//
-// `absl::BitGenRef` is particularly useful when used with an
-// `absl::MockingBitGen` to test specific paths in functions which use random
-// values.
-//
-// Example:
-//    void TakesBitGenRef(absl::BitGenRef gen) {
-//      int x = absl::Uniform<int>(gen, 0, 1000);
-//    }
-//
-class BitGenRef {
-  // SFINAE to detect whether the URBG type includes a member matching
-  // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
-  //
-  // These live inside BitGenRef so that they have friend access
-  // to MockingBitGen. (see similar methods in DistributionCaller).
-  template <template <class...> class Trait, class AlwaysVoid, class... Args>
-  struct detector : std::false_type {};
-  template <template <class...> class Trait, class... Args>
-  struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
-      : std::true_type {};
-
-  template <class T>
-  using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
-      std::declval<base_internal::FastTypeIdType>(), std::declval<void*>(),
-      std::declval<void*>()));
-
-  template <typename T>
-  using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type;
-
- public:
-  BitGenRef(const BitGenRef&) = default;
-  BitGenRef(BitGenRef&&) = default;
-  BitGenRef& operator=(const BitGenRef&) = default;
-  BitGenRef& operator=(BitGenRef&&) = default;
-
-  template <typename URBG, typename absl::enable_if_t<
-                               (!std::is_same<URBG, BitGenRef>::value &&
-                                random_internal::is_urbg<URBG>::value &&
-                                !HasInvokeMock<URBG>::value)>* = nullptr>
-  BitGenRef(URBG& gen)  // NOLINT
-      : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
-        mock_call_(NotAMock),
-        generate_impl_fn_(ImplFn<URBG>) {}
-
-  template <typename URBG,
-            typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value &&
-                                        random_internal::is_urbg<URBG>::value &&
-                                        HasInvokeMock<URBG>::value)>* = nullptr>
-  BitGenRef(URBG& gen)  // NOLINT
-      : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
-        mock_call_(&MockCall<URBG>),
-        generate_impl_fn_(ImplFn<URBG>) {}
-
-  using result_type = uint64_t;
-
-  static constexpr result_type(min)() {
-    return (std::numeric_limits<result_type>::min)();
-  }
-
-  static constexpr result_type(max)() {
-    return (std::numeric_limits<result_type>::max)();
-  }
-
-  result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); }
-
- private:
-  using impl_fn = result_type (*)(uintptr_t);
-  using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*,
-                                void*);
-
-  template <typename URBG>
-  static result_type ImplFn(uintptr_t ptr) {
-    // Ensure that the return values from operator() fill the entire
-    // range promised by result_type, min() and max().
-    absl::random_internal::FastUniformBits<result_type> fast_uniform_bits;
-    return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr));
-  }
-
-  // Get a type-erased InvokeMock pointer.
-  template <typename URBG>
-  static bool MockCall(uintptr_t gen_ptr, base_internal::FastTypeIdType type,
-                       void* result, void* arg_tuple) {
-    return reinterpret_cast<URBG*>(gen_ptr)->InvokeMock(type, result,
-                                                        arg_tuple);
-  }
-  static bool NotAMock(uintptr_t, base_internal::FastTypeIdType, void*, void*) {
-    return false;
-  }
-
-  inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple,
-                         void* result) {
-    if (mock_call_ == NotAMock) return false;  // avoids an indirect call.
-    return mock_call_(t_erased_gen_ptr_, type, args_tuple, result);
-  }
-
-  uintptr_t t_erased_gen_ptr_;
-  mock_call_fn mock_call_;
-  impl_fn generate_impl_fn_;
-
-  template <typename>
-  friend struct ::absl::random_internal::DistributionCaller;  // for InvokeMock
-  friend class ::absl::random_internal::MockHelpers;          // for InvokeMock
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_BIT_GEN_REF_H_
diff --git a/third_party/abseil_cpp/absl/random/bit_gen_ref_test.cc b/third_party/abseil_cpp/absl/random/bit_gen_ref_test.cc
deleted file mode 100644
index 1135cf2da0..0000000000
--- a/third_party/abseil_cpp/absl/random/bit_gen_ref_test.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-#include "absl/random/bit_gen_ref.h"
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/fast_type_id.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class ConstBitGen {
- public:
-  // URBG interface
-  using result_type = absl::BitGen::result_type;
-
-  static constexpr result_type(min)() { return (absl::BitGen::min)(); }
-  static constexpr result_type(max)() { return (absl::BitGen::max)(); }
-  result_type operator()() { return 1; }
-
-  // InvokeMock method
-  bool InvokeMock(base_internal::FastTypeIdType index, void*, void* result) {
-    *static_cast<int*>(result) = 42;
-    return true;
-  }
-};
-
-namespace {
-
-int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); }
-
-template <typename T>
-class BitGenRefTest : public testing::Test {};
-
-using BitGenTypes =
-    ::testing::Types<absl::BitGen, absl::InsecureBitGen, std::mt19937,
-                     std::mt19937_64, std::minstd_rand>;
-TYPED_TEST_SUITE(BitGenRefTest, BitGenTypes);
-
-TYPED_TEST(BitGenRefTest, BasicTest) {
-  TypeParam gen;
-  auto x = FnTest(gen);
-  EXPECT_NEAR(x, 4, 3);
-}
-
-TYPED_TEST(BitGenRefTest, Copyable) {
-  TypeParam gen;
-  absl::BitGenRef gen_ref(gen);
-  FnTest(gen_ref);  // Copy
-}
-
-TEST(BitGenRefTest, PassThroughEquivalence) {
-  // sequence_urbg returns 64-bit results.
-  absl::random_internal::sequence_urbg urbg(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  std::vector<uint64_t> output(12);
-
-  {
-    absl::BitGenRef view(urbg);
-    for (auto& v : output) {
-      v = view();
-    }
-  }
-
-  std::vector<uint64_t> expected(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  EXPECT_THAT(output, testing::Eq(expected));
-}
-
-TEST(BitGenRefTest, MockingBitGenBaseOverrides) {
-  ConstBitGen const_gen;
-  EXPECT_EQ(FnTest(const_gen), 42);
-
-  absl::BitGenRef gen_ref(const_gen);
-  EXPECT_EQ(FnTest(gen_ref), 42);  // Copy
-}
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/discrete_distribution.cc b/third_party/abseil_cpp/absl/random/discrete_distribution.cc
deleted file mode 100644
index 081accee52..0000000000
--- a/third_party/abseil_cpp/absl/random/discrete_distribution.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/discrete_distribution.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// Initializes the distribution table for Walker's Aliasing algorithm, described
-// in Knuth, Vol 2. as well as in https://en.wikipedia.org/wiki/Alias_method
-std::vector<std::pair<double, size_t>> InitDiscreteDistribution(
-    std::vector<double>* probabilities) {
-  // The empty-case should already be handled by the constructor.
-  assert(probabilities);
-  assert(!probabilities->empty());
-
-  // Step 1. Normalize the input probabilities to 1.0.
-  double sum = std::accumulate(std::begin(*probabilities),
-                               std::end(*probabilities), 0.0);
-  if (std::fabs(sum - 1.0) > 1e-6) {
-    // Scale `probabilities` only when the sum is too far from 1.0.  Scaling
-    // unconditionally will alter the probabilities slightly.
-    for (double& item : *probabilities) {
-      item = item / sum;
-    }
-  }
-
-  // Step 2. At this point `probabilities` is set to the conditional
-  // probabilities of each element which sum to 1.0, to within reasonable error.
-  // These values are used to construct the proportional probability tables for
-  // the selection phases of Walker's Aliasing algorithm.
-  //
-  // To construct the table, pick an element which is under-full (i.e., an
-  // element for which `(*probabilities)[i] < 1.0/n`), and pair it with an
-  // element which is over-full (i.e., an element for which
-  // `(*probabilities)[i] > 1.0/n`). The smaller value can always be retired.
-  // The larger may still be greater than 1.0/n, or may now be less than 1.0/n,
-  // and put back onto the appropriate collection.
-  const size_t n = probabilities->size();
-  std::vector<std::pair<double, size_t>> q;
-  q.reserve(n);
-
-  std::vector<size_t> over;
-  std::vector<size_t> under;
-  size_t idx = 0;
-  for (const double item : *probabilities) {
-    assert(item >= 0);
-    const double v = item * n;
-    q.emplace_back(v, 0);
-    if (v < 1.0) {
-      under.push_back(idx++);
-    } else {
-      over.push_back(idx++);
-    }
-  }
-  while (!over.empty() && !under.empty()) {
-    auto lo = under.back();
-    under.pop_back();
-    auto hi = over.back();
-    over.pop_back();
-
-    q[lo].second = hi;
-    const double r = q[hi].first - (1.0 - q[lo].first);
-    q[hi].first = r;
-    if (r < 1.0) {
-      under.push_back(hi);
-    } else {
-      over.push_back(hi);
-    }
-  }
-
-  // Due to rounding errors, there may be un-paired elements in either
-  // collection; these should all be values near 1.0.  For these values, set `q`
-  // to 1.0 and set the alternate to the identity.
-  for (auto i : over) {
-    q[i] = {1.0, i};
-  }
-  for (auto i : under) {
-    q[i] = {1.0, i};
-  }
-  return q;
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/discrete_distribution.h b/third_party/abseil_cpp/absl/random/discrete_distribution.h
deleted file mode 100644
index 171aa11a1e..0000000000
--- a/third_party/abseil_cpp/absl/random/discrete_distribution.h
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_
-#define ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_
-
-#include <cassert>
-#include <cmath>
-#include <istream>
-#include <limits>
-#include <numeric>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "absl/random/bernoulli_distribution.h"
-#include "absl/random/internal/iostream_state_saver.h"
-#include "absl/random/uniform_int_distribution.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::discrete_distribution
-//
-// A discrete distribution produces random integers i, where 0 <= i < n
-// distributed according to the discrete probability function:
-//
-//     P(i|p0,...,pnβˆ’1)=pi
-//
-// This class is an implementation of discrete_distribution (see
-// [rand.dist.samp.discrete]).
-//
-// The algorithm used is Walker's Aliasing algorithm, described in Knuth, Vol 2.
-// absl::discrete_distribution takes O(N) time to precompute the probabilities
-// (where N is the number of possible outcomes in the distribution) at
-// construction, and then takes O(1) time for each variate generation.  Many
-// other implementations also take O(N) time to construct an ordered sequence of
-// partial sums, plus O(log N) time per variate to binary search.
-//
-template <typename IntType = int>
-class discrete_distribution {
- public:
-  using result_type = IntType;
-
-  class param_type {
-   public:
-    using distribution_type = discrete_distribution;
-
-    param_type() { init(); }
-
-    template <typename InputIterator>
-    explicit param_type(InputIterator begin, InputIterator end)
-        : p_(begin, end) {
-      init();
-    }
-
-    explicit param_type(std::initializer_list<double> weights) : p_(weights) {
-      init();
-    }
-
-    template <class UnaryOperation>
-    explicit param_type(size_t nw, double xmin, double xmax,
-                        UnaryOperation fw) {
-      if (nw > 0) {
-        p_.reserve(nw);
-        double delta = (xmax - xmin) / static_cast<double>(nw);
-        assert(delta > 0);
-        double t = delta * 0.5;
-        for (size_t i = 0; i < nw; ++i) {
-          p_.push_back(fw(xmin + i * delta + t));
-        }
-      }
-      init();
-    }
-
-    const std::vector<double>& probabilities() const { return p_; }
-    size_t n() const { return p_.size() - 1; }
-
-    friend bool operator==(const param_type& a, const param_type& b) {
-      return a.probabilities() == b.probabilities();
-    }
-
-    friend bool operator!=(const param_type& a, const param_type& b) {
-      return !(a == b);
-    }
-
-   private:
-    friend class discrete_distribution;
-
-    void init();
-
-    std::vector<double> p_;                     // normalized probabilities
-    std::vector<std::pair<double, size_t>> q_;  // (acceptance, alternate) pairs
-
-    static_assert(std::is_integral<result_type>::value,
-                  "Class-template absl::discrete_distribution<> must be "
-                  "parameterized using an integral type.");
-  };
-
-  discrete_distribution() : param_() {}
-
-  explicit discrete_distribution(const param_type& p) : param_(p) {}
-
-  template <typename InputIterator>
-  explicit discrete_distribution(InputIterator begin, InputIterator end)
-      : param_(begin, end) {}
-
-  explicit discrete_distribution(std::initializer_list<double> weights)
-      : param_(weights) {}
-
-  template <class UnaryOperation>
-  explicit discrete_distribution(size_t nw, double xmin, double xmax,
-                                 UnaryOperation fw)
-      : param_(nw, xmin, xmax, std::move(fw)) {}
-
-  void reset() {}
-
-  // generating functions
-  template <typename URBG>
-  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
-    return (*this)(g, param_);
-  }
-
-  template <typename URBG>
-  result_type operator()(URBG& g,  // NOLINT(runtime/references)
-                         const param_type& p);
-
-  const param_type& param() const { return param_; }
-  void param(const param_type& p) { param_ = p; }
-
-  result_type(min)() const { return 0; }
-  result_type(max)() const {
-    return static_cast<result_type>(param_.n());
-  }  // inclusive
-
-  // NOTE [rand.dist.sample.discrete] returns a std::vector<double> not a
-  // const std::vector<double>&.
-  const std::vector<double>& probabilities() const {
-    return param_.probabilities();
-  }
-
-  friend bool operator==(const discrete_distribution& a,
-                         const discrete_distribution& b) {
-    return a.param_ == b.param_;
-  }
-  friend bool operator!=(const discrete_distribution& a,
-                         const discrete_distribution& b) {
-    return a.param_ != b.param_;
-  }
-
- private:
-  param_type param_;
-};
-
-// --------------------------------------------------------------------------
-// Implementation details only below
-// --------------------------------------------------------------------------
-
-namespace random_internal {
-
-// Using the vector `*probabilities`, whose values are the weights or
-// probabilities of an element being selected, constructs the proportional
-// probabilities used by the discrete distribution.  `*probabilities` will be
-// scaled, if necessary, so that its entries sum to a value sufficiently close
-// to 1.0.
-std::vector<std::pair<double, size_t>> InitDiscreteDistribution(
-    std::vector<double>* probabilities);
-
-}  // namespace random_internal
-
-template <typename IntType>
-void discrete_distribution<IntType>::param_type::init() {
-  if (p_.empty()) {
-    p_.push_back(1.0);
-    q_.emplace_back(1.0, 0);
-  } else {
-    assert(n() <= (std::numeric_limits<IntType>::max)());
-    q_ = random_internal::InitDiscreteDistribution(&p_);
-  }
-}
-
-template <typename IntType>
-template <typename URBG>
-typename discrete_distribution<IntType>::result_type
-discrete_distribution<IntType>::operator()(
-    URBG& g,  // NOLINT(runtime/references)
-    const param_type& p) {
-  const auto idx = absl::uniform_int_distribution<result_type>(0, p.n())(g);
-  const auto& q = p.q_[idx];
-  const bool selected = absl::bernoulli_distribution(q.first)(g);
-  return selected ? idx : static_cast<result_type>(q.second);
-}
-
-template <typename CharT, typename Traits, typename IntType>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    const discrete_distribution<IntType>& x) {
-  auto saver = random_internal::make_ostream_state_saver(os);
-  const auto& probabilities = x.param().probabilities();
-  os << probabilities.size();
-
-  os.precision(random_internal::stream_precision_helper<double>::kPrecision);
-  for (const auto& p : probabilities) {
-    os << os.fill() << p;
-  }
-  return os;
-}
-
-template <typename CharT, typename Traits, typename IntType>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-    discrete_distribution<IntType>& x) {    // NOLINT(runtime/references)
-  using param_type = typename discrete_distribution<IntType>::param_type;
-  auto saver = random_internal::make_istream_state_saver(is);
-
-  size_t n;
-  std::vector<double> p;
-
-  is >> n;
-  if (is.fail()) return is;
-  if (n > 0) {
-    p.reserve(n);
-    for (IntType i = 0; i < n && !is.fail(); ++i) {
-      auto tmp = random_internal::read_floating_point<double>(is);
-      if (is.fail()) return is;
-      p.push_back(tmp);
-    }
-  }
-  x.param(param_type(p.begin(), p.end()));
-  return is;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/discrete_distribution_test.cc b/third_party/abseil_cpp/absl/random/discrete_distribution_test.cc
deleted file mode 100644
index 6d007006ef..0000000000
--- a/third_party/abseil_cpp/absl/random/discrete_distribution_test.cc
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/discrete_distribution.h"
-
-#include <cmath>
-#include <cstddef>
-#include <cstdint>
-#include <iterator>
-#include <numeric>
-#include <random>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/chi_square.h"
-#include "absl/random/internal/distribution_test_util.h"
-#include "absl/random/internal/pcg_engine.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/strip.h"
-
-namespace {
-
-template <typename IntType>
-class DiscreteDistributionTypeTest : public ::testing::Test {};
-
-using IntTypes = ::testing::Types<int8_t, uint8_t, int16_t, uint16_t, int32_t,
-                                  uint32_t, int64_t, uint64_t>;
-TYPED_TEST_SUITE(DiscreteDistributionTypeTest, IntTypes);
-
-TYPED_TEST(DiscreteDistributionTypeTest, ParamSerializeTest) {
-  using param_type =
-      typename absl::discrete_distribution<TypeParam>::param_type;
-
-  absl::discrete_distribution<TypeParam> empty;
-  EXPECT_THAT(empty.probabilities(), testing::ElementsAre(1.0));
-
-  absl::discrete_distribution<TypeParam> before({1.0, 2.0, 1.0});
-
-  // Validate that the probabilities sum to 1.0. We picked values which
-  // can be represented exactly to avoid floating-point roundoff error.
-  double s = 0;
-  for (const auto& x : before.probabilities()) {
-    s += x;
-  }
-  EXPECT_EQ(s, 1.0);
-  EXPECT_THAT(before.probabilities(), testing::ElementsAre(0.25, 0.5, 0.25));
-
-  // Validate the same data via an initializer list.
-  {
-    std::vector<double> data({1.0, 2.0, 1.0});
-
-    absl::discrete_distribution<TypeParam> via_param{
-        param_type(std::begin(data), std::end(data))};
-
-    EXPECT_EQ(via_param, before);
-  }
-
-  std::stringstream ss;
-  ss << before;
-  absl::discrete_distribution<TypeParam> after;
-
-  EXPECT_NE(before, after);
-
-  ss >> after;
-
-  EXPECT_EQ(before, after);
-}
-
-TYPED_TEST(DiscreteDistributionTypeTest, Constructor) {
-  auto fn = [](double x) { return x; };
-  {
-    absl::discrete_distribution<int> unary(0, 1.0, 9.0, fn);
-    EXPECT_THAT(unary.probabilities(), testing::ElementsAre(1.0));
-  }
-
-  {
-    absl::discrete_distribution<int> unary(2, 1.0, 9.0, fn);
-    // => fn(1.0 + 0 * 4 + 2) => 3
-    // => fn(1.0 + 1 * 4 + 2) => 7
-    EXPECT_THAT(unary.probabilities(), testing::ElementsAre(0.3, 0.7));
-  }
-}
-
-TEST(DiscreteDistributionTest, InitDiscreteDistribution) {
-  using testing::Pair;
-
-  {
-    std::vector<double> p({1.0, 2.0, 3.0});
-    std::vector<std::pair<double, size_t>> q =
-        absl::random_internal::InitDiscreteDistribution(&p);
-
-    EXPECT_THAT(p, testing::ElementsAre(1 / 6.0, 2 / 6.0, 3 / 6.0));
-
-    // Each bucket is p=1/3, so bucket 0 will send half it's traffic
-    // to bucket 2, while the rest will retain all of their traffic.
-    EXPECT_THAT(q, testing::ElementsAre(Pair(0.5, 2),  //
-                                        Pair(1.0, 1),  //
-                                        Pair(1.0, 2)));
-  }
-
-  {
-    std::vector<double> p({1.0, 2.0, 3.0, 5.0, 2.0});
-
-    std::vector<std::pair<double, size_t>> q =
-        absl::random_internal::InitDiscreteDistribution(&p);
-
-    EXPECT_THAT(p, testing::ElementsAre(1 / 13.0, 2 / 13.0, 3 / 13.0, 5 / 13.0,
-                                        2 / 13.0));
-
-    // A more complex bucketing solution: Each bucket has p=0.2
-    // So buckets 0, 1, 4 will send their alternate traffic elsewhere, which
-    // happens to be bucket 3.
-    // However, summing up that alternate traffic gives bucket 3 too much
-    // traffic, so it will send some traffic to bucket 2.
-    constexpr double b0 = 1.0 / 13.0 / 0.2;
-    constexpr double b1 = 2.0 / 13.0 / 0.2;
-    constexpr double b3 = (5.0 / 13.0 / 0.2) - ((1 - b0) + (1 - b1) + (1 - b1));
-
-    EXPECT_THAT(q, testing::ElementsAre(Pair(b0, 3),   //
-                                        Pair(b1, 3),   //
-                                        Pair(1.0, 2),  //
-                                        Pair(b3, 2),   //
-                                        Pair(b1, 3)));
-  }
-}
-
-TEST(DiscreteDistributionTest, ChiSquaredTest50) {
-  using absl::random_internal::kChiSquared;
-
-  constexpr size_t kTrials = 10000;
-  constexpr int kBuckets = 50;  // inclusive, so actally +1
-
-  // 1-in-100000 threshold, but remember, there are about 8 tests
-  // in this file. And the test could fail for other reasons.
-  // Empirically validated with --runs_per_test=10000.
-  const int kThreshold =
-      absl::random_internal::ChiSquareValue(kBuckets, 0.99999);
-
-  std::vector<double> weights(kBuckets, 0);
-  std::iota(std::begin(weights), std::end(weights), 1);
-  absl::discrete_distribution<int> dist(std::begin(weights), std::end(weights));
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng(0x2B7E151628AED2A6);
-
-  std::vector<int32_t> counts(kBuckets, 0);
-  for (size_t i = 0; i < kTrials; i++) {
-    auto x = dist(rng);
-    counts[x]++;
-  }
-
-  // Scale weights.
-  double sum = 0;
-  for (double x : weights) {
-    sum += x;
-  }
-  for (double& x : weights) {
-    x = kTrials * (x / sum);
-  }
-
-  double chi_square =
-      absl::random_internal::ChiSquare(std::begin(counts), std::end(counts),
-                                       std::begin(weights), std::end(weights));
-
-  if (chi_square > kThreshold) {
-    double p_value =
-        absl::random_internal::ChiSquarePValue(chi_square, kBuckets);
-
-    // Chi-squared test failed. Output does not appear to be uniform.
-    std::string msg;
-    for (size_t i = 0; i < counts.size(); i++) {
-      absl::StrAppend(&msg, i, ": ", counts[i], " vs ", weights[i], "\n");
-    }
-    absl::StrAppend(&msg, kChiSquared, " p-value ", p_value, "\n");
-    absl::StrAppend(&msg, "High ", kChiSquared, " value: ", chi_square, " > ",
-                    kThreshold);
-    ABSL_RAW_LOG(INFO, "%s", msg.c_str());
-    FAIL() << msg;
-  }
-}
-
-TEST(DiscreteDistributionTest, StabilityTest) {
-  // absl::discrete_distribution stabilitiy relies on
-  // absl::uniform_int_distribution and absl::bernoulli_distribution.
-  absl::random_internal::sequence_urbg urbg(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  std::vector<int> output(6);
-
-  {
-    absl::discrete_distribution<int32_t> dist({1.0, 2.0, 3.0, 5.0, 2.0});
-    EXPECT_EQ(0, dist.min());
-    EXPECT_EQ(4, dist.max());
-    for (auto& v : output) {
-      v = dist(urbg);
-    }
-    EXPECT_EQ(12, urbg.invocations());
-  }
-
-  // With 12 calls to urbg, each call into discrete_distribution consumes
-  // precisely 2 values: one for the uniform call, and a second for the
-  // bernoulli.
-  //
-  // Given the alt mapping: 0=>3, 1=>3, 2=>2, 3=>2, 4=>3, we can
-  //
-  // uniform:      443210143131
-  // bernoulli: b0 000011100101
-  // bernoulli: b1 001111101101
-  // bernoulli: b2 111111111111
-  // bernoulli: b3 001111101111
-  // bernoulli: b4 001111101101
-  // ...
-  EXPECT_THAT(output, testing::ElementsAre(3, 3, 1, 3, 3, 3));
-
-  {
-    urbg.reset();
-    absl::discrete_distribution<int64_t> dist({1.0, 2.0, 3.0, 5.0, 2.0});
-    EXPECT_EQ(0, dist.min());
-    EXPECT_EQ(4, dist.max());
-    for (auto& v : output) {
-      v = dist(urbg);
-    }
-    EXPECT_EQ(12, urbg.invocations());
-  }
-  EXPECT_THAT(output, testing::ElementsAre(3, 3, 0, 3, 0, 4));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/distributions.h b/third_party/abseil_cpp/absl/random/distributions.h
deleted file mode 100644
index 31c79694e5..0000000000
--- a/third_party/abseil_cpp/absl/random/distributions.h
+++ /dev/null
@@ -1,452 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: distributions.h
-// -----------------------------------------------------------------------------
-//
-// This header defines functions representing distributions, which you use in
-// combination with an Abseil random bit generator to produce random values
-// according to the rules of that distribution.
-//
-// The Abseil random library defines the following distributions within this
-// file:
-//
-//   * `absl::Uniform` for uniform (constant) distributions having constant
-//     probability
-//   * `absl::Bernoulli` for discrete distributions having exactly two outcomes
-//   * `absl::Beta` for continuous distributions parameterized through two
-//     free parameters
-//   * `absl::Exponential` for discrete distributions of events occurring
-//     continuously and independently at a constant average rate
-//   * `absl::Gaussian` (also known as "normal distributions") for continuous
-//     distributions using an associated quadratic function
-//   * `absl::LogUniform` for continuous uniform distributions where the log
-//     to the given base of all values is uniform
-//   * `absl::Poisson` for discrete probability distributions that express the
-//     probability of a given number of events occurring within a fixed interval
-//   * `absl::Zipf` for discrete probability distributions commonly used for
-//     modelling of rare events
-//
-// Prefer use of these distribution function classes over manual construction of
-// your own distribution classes, as it allows library maintainers greater
-// flexibility to change the underlying implementation in the future.
-
-#ifndef ABSL_RANDOM_DISTRIBUTIONS_H_
-#define ABSL_RANDOM_DISTRIBUTIONS_H_
-
-#include <algorithm>
-#include <cmath>
-#include <limits>
-#include <random>
-#include <type_traits>
-
-#include "absl/base/internal/inline_variable.h"
-#include "absl/random/bernoulli_distribution.h"
-#include "absl/random/beta_distribution.h"
-#include "absl/random/exponential_distribution.h"
-#include "absl/random/gaussian_distribution.h"
-#include "absl/random/internal/distribution_caller.h"  // IWYU pragma: export
-#include "absl/random/internal/uniform_helper.h"  // IWYU pragma: export
-#include "absl/random/log_uniform_int_distribution.h"
-#include "absl/random/poisson_distribution.h"
-#include "absl/random/uniform_int_distribution.h"
-#include "absl/random/uniform_real_distribution.h"
-#include "absl/random/zipf_distribution.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedClosedTag, IntervalClosedClosed,
-                               {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedClosedTag, IntervalClosed, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedOpenTag, IntervalClosedOpen, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenOpenTag, IntervalOpenOpen, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenOpenTag, IntervalOpen, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenClosedTag, IntervalOpenClosed, {});
-
-// -----------------------------------------------------------------------------
-// absl::Uniform<T>(tag, bitgen, lo, hi)
-// -----------------------------------------------------------------------------
-//
-// `absl::Uniform()` produces random values of type `T` uniformly distributed in
-// a defined interval {lo, hi}. The interval `tag` defines the type of interval
-// which should be one of the following possible values:
-//
-//   * `absl::IntervalOpenOpen`
-//   * `absl::IntervalOpenClosed`
-//   * `absl::IntervalClosedOpen`
-//   * `absl::IntervalClosedClosed`
-//
-// where "open" refers to an exclusive value (excluded) from the output, while
-// "closed" refers to an inclusive value (included) from the output.
-//
-// In the absence of an explicit return type `T`, `absl::Uniform()` will deduce
-// the return type based on the provided endpoint arguments {A lo, B hi}.
-// Given these endpoints, one of {A, B} will be chosen as the return type, if
-// a type can be implicitly converted into the other in a lossless way. The
-// lack of any such implicit conversion between {A, B} will produce a
-// compile-time error
-//
-// See https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)
-//
-// Example:
-//
-//   absl::BitGen bitgen;
-//
-//   // Produce a random float value between 0.0 and 1.0, inclusive
-//   auto x = absl::Uniform(absl::IntervalClosedClosed, bitgen, 0.0f, 1.0f);
-//
-//   // The most common interval of `absl::IntervalClosedOpen` is available by
-//   // default:
-//
-//   auto x = absl::Uniform(bitgen, 0.0f, 1.0f);
-//
-//   // Return-types are typically inferred from the arguments, however callers
-//   // can optionally provide an explicit return-type to the template.
-//
-//   auto x = absl::Uniform<float>(bitgen, 0, 1);
-//
-template <typename R = void, typename TagType, typename URBG>
-typename absl::enable_if_t<!std::is_same<R, void>::value, R>  //
-Uniform(TagType tag,
-        URBG&& urbg,  // NOLINT(runtime/references)
-        R lo, R hi) {
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = random_internal::UniformDistributionWrapper<R>;
-
-  auto a = random_internal::uniform_lower_bound(tag, lo, hi);
-  auto b = random_internal::uniform_upper_bound(tag, lo, hi);
-  if (!random_internal::is_uniform_range_valid(a, b)) return lo;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, tag, lo, hi);
-}
-
-// absl::Uniform<T>(bitgen, lo, hi)
-//
-// Overload of `Uniform()` using the default closed-open interval of [lo, hi),
-// and returning values of type `T`
-template <typename R = void, typename URBG>
-typename absl::enable_if_t<!std::is_same<R, void>::value, R>  //
-Uniform(URBG&& urbg,  // NOLINT(runtime/references)
-        R lo, R hi) {
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = random_internal::UniformDistributionWrapper<R>;
-  constexpr auto tag = absl::IntervalClosedOpen;
-
-  auto a = random_internal::uniform_lower_bound(tag, lo, hi);
-  auto b = random_internal::uniform_upper_bound(tag, lo, hi);
-  if (!random_internal::is_uniform_range_valid(a, b)) return lo;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, lo, hi);
-}
-
-// absl::Uniform(tag, bitgen, lo, hi)
-//
-// Overload of `Uniform()` using different (but compatible) lo, hi types. Note
-// that a compile-error will result if the return type cannot be deduced
-// correctly from the passed types.
-template <typename R = void, typename TagType, typename URBG, typename A,
-          typename B>
-typename absl::enable_if_t<std::is_same<R, void>::value,
-                           random_internal::uniform_inferred_return_t<A, B>>
-Uniform(TagType tag,
-        URBG&& urbg,  // NOLINT(runtime/references)
-        A lo, B hi) {
-  using gen_t = absl::decay_t<URBG>;
-  using return_t = typename random_internal::uniform_inferred_return_t<A, B>;
-  using distribution_t = random_internal::UniformDistributionWrapper<return_t>;
-
-  auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi);
-  auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi);
-  if (!random_internal::is_uniform_range_valid(a, b)) return lo;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, tag, static_cast<return_t>(lo),
-                                static_cast<return_t>(hi));
-}
-
-// absl::Uniform(bitgen, lo, hi)
-//
-// Overload of `Uniform()` using different (but compatible) lo, hi types and the
-// default closed-open interval of [lo, hi). Note that a compile-error will
-// result if the return type cannot be deduced correctly from the passed types.
-template <typename R = void, typename URBG, typename A, typename B>
-typename absl::enable_if_t<std::is_same<R, void>::value,
-                           random_internal::uniform_inferred_return_t<A, B>>
-Uniform(URBG&& urbg,  // NOLINT(runtime/references)
-        A lo, B hi) {
-  using gen_t = absl::decay_t<URBG>;
-  using return_t = typename random_internal::uniform_inferred_return_t<A, B>;
-  using distribution_t = random_internal::UniformDistributionWrapper<return_t>;
-
-  constexpr auto tag = absl::IntervalClosedOpen;
-  auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi);
-  auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi);
-  if (!random_internal::is_uniform_range_valid(a, b)) return lo;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, static_cast<return_t>(lo),
-                                static_cast<return_t>(hi));
-}
-
-// absl::Uniform<unsigned T>(bitgen)
-//
-// Overload of Uniform() using the minimum and maximum values of a given type
-// `T` (which must be unsigned), returning a value of type `unsigned T`
-template <typename R, typename URBG>
-typename absl::enable_if_t<!std::is_signed<R>::value, R>  //
-Uniform(URBG&& urbg) {  // NOLINT(runtime/references)
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = random_internal::UniformDistributionWrapper<R>;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg);
-}
-
-// -----------------------------------------------------------------------------
-// absl::Bernoulli(bitgen, p)
-// -----------------------------------------------------------------------------
-//
-// `absl::Bernoulli` produces a random boolean value, with probability `p`
-// (where 0.0 <= p <= 1.0) equaling `true`.
-//
-// Prefer `absl::Bernoulli` to produce boolean values over other alternatives
-// such as comparing an `absl::Uniform()` value to a specific output.
-//
-// See https://en.wikipedia.org/wiki/Bernoulli_distribution
-//
-// Example:
-//
-//   absl::BitGen bitgen;
-//   ...
-//   if (absl::Bernoulli(bitgen, 1.0/3721.0)) {
-//     std::cout << "Asteroid field navigation successful.";
-//   }
-//
-template <typename URBG>
-bool Bernoulli(URBG&& urbg,  // NOLINT(runtime/references)
-               double p) {
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = absl::bernoulli_distribution;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, p);
-}
-
-// -----------------------------------------------------------------------------
-// absl::Beta<T>(bitgen, alpha, beta)
-// -----------------------------------------------------------------------------
-//
-// `absl::Beta` produces a floating point number distributed in the closed
-// interval [0,1] and parameterized by two values `alpha` and `beta` as per a
-// Beta distribution. `T` must be a floating point type, but may be inferred
-// from the types of `alpha` and `beta`.
-//
-// See https://en.wikipedia.org/wiki/Beta_distribution.
-//
-// Example:
-//
-//   absl::BitGen bitgen;
-//   ...
-//   double sample = absl::Beta(bitgen, 3.0, 2.0);
-//
-template <typename RealType, typename URBG>
-RealType Beta(URBG&& urbg,  // NOLINT(runtime/references)
-              RealType alpha, RealType beta) {
-  static_assert(
-      std::is_floating_point<RealType>::value,
-      "Template-argument 'RealType' must be a floating-point type, in "
-      "absl::Beta<RealType, URBG>(...)");
-
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = typename absl::beta_distribution<RealType>;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, alpha, beta);
-}
-
-// -----------------------------------------------------------------------------
-// absl::Exponential<T>(bitgen, lambda = 1)
-// -----------------------------------------------------------------------------
-//
-// `absl::Exponential` produces a floating point number representing the
-// distance (time) between two consecutive events in a point process of events
-// occurring continuously and independently at a constant average rate. `T` must
-// be a floating point type, but may be inferred from the type of `lambda`.
-//
-// See https://en.wikipedia.org/wiki/Exponential_distribution.
-//
-// Example:
-//
-//   absl::BitGen bitgen;
-//   ...
-//   double call_length = absl::Exponential(bitgen, 7.0);
-//
-template <typename RealType, typename URBG>
-RealType Exponential(URBG&& urbg,  // NOLINT(runtime/references)
-                     RealType lambda = 1) {
-  static_assert(
-      std::is_floating_point<RealType>::value,
-      "Template-argument 'RealType' must be a floating-point type, in "
-      "absl::Exponential<RealType, URBG>(...)");
-
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = typename absl::exponential_distribution<RealType>;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, lambda);
-}
-
-// -----------------------------------------------------------------------------
-// absl::Gaussian<T>(bitgen, mean = 0, stddev = 1)
-// -----------------------------------------------------------------------------
-//
-// `absl::Gaussian` produces a floating point number selected from the Gaussian
-// (ie. "Normal") distribution. `T` must be a floating point type, but may be
-// inferred from the types of `mean` and `stddev`.
-//
-// See https://en.wikipedia.org/wiki/Normal_distribution
-//
-// Example:
-//
-//   absl::BitGen bitgen;
-//   ...
-//   double giraffe_height = absl::Gaussian(bitgen, 16.3, 3.3);
-//
-template <typename RealType, typename URBG>
-RealType Gaussian(URBG&& urbg,  // NOLINT(runtime/references)
-                  RealType mean = 0, RealType stddev = 1) {
-  static_assert(
-      std::is_floating_point<RealType>::value,
-      "Template-argument 'RealType' must be a floating-point type, in "
-      "absl::Gaussian<RealType, URBG>(...)");
-
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = typename absl::gaussian_distribution<RealType>;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, mean, stddev);
-}
-
-// -----------------------------------------------------------------------------
-// absl::LogUniform<T>(bitgen, lo, hi, base = 2)
-// -----------------------------------------------------------------------------
-//
-// `absl::LogUniform` produces random values distributed where the log to a
-// given base of all values is uniform in a closed interval [lo, hi]. `T` must
-// be an integral type, but may be inferred from the types of `lo` and `hi`.
-//
-// I.e., `LogUniform(0, n, b)` is uniformly distributed across buckets
-// [0], [1, b-1], [b, b^2-1] .. [b^(k-1), (b^k)-1] .. [b^floor(log(n, b)), n]
-// and is uniformly distributed within each bucket.
-//
-// The resulting probability density is inversely related to bucket size, though
-// values in the final bucket may be more likely than previous values. (In the
-// extreme case where n = b^i the final value will be tied with zero as the most
-// probable result.
-//
-// If `lo` is nonzero then this distribution is shifted to the desired interval,
-// so LogUniform(lo, hi, b) is equivalent to LogUniform(0, hi-lo, b)+lo.
-//
-// See http://ecolego.facilia.se/ecolego/show/Log-Uniform%20Distribution
-//
-// Example:
-//
-//   absl::BitGen bitgen;
-//   ...
-//   int v = absl::LogUniform(bitgen, 0, 1000);
-//
-template <typename IntType, typename URBG>
-IntType LogUniform(URBG&& urbg,  // NOLINT(runtime/references)
-                   IntType lo, IntType hi, IntType base = 2) {
-  static_assert(std::is_integral<IntType>::value,
-                "Template-argument 'IntType' must be an integral type, in "
-                "absl::LogUniform<IntType, URBG>(...)");
-
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = typename absl::log_uniform_int_distribution<IntType>;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, lo, hi, base);
-}
-
-// -----------------------------------------------------------------------------
-// absl::Poisson<T>(bitgen, mean = 1)
-// -----------------------------------------------------------------------------
-//
-// `absl::Poisson` produces discrete probabilities for a given number of events
-// occurring within a fixed interval within the closed interval [0, max]. `T`
-// must be an integral type.
-//
-// See https://en.wikipedia.org/wiki/Poisson_distribution
-//
-// Example:
-//
-//   absl::BitGen bitgen;
-//   ...
-//   int requests_per_minute = absl::Poisson<int>(bitgen, 3.2);
-//
-template <typename IntType, typename URBG>
-IntType Poisson(URBG&& urbg,  // NOLINT(runtime/references)
-                double mean = 1.0) {
-  static_assert(std::is_integral<IntType>::value,
-                "Template-argument 'IntType' must be an integral type, in "
-                "absl::Poisson<IntType, URBG>(...)");
-
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = typename absl::poisson_distribution<IntType>;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, mean);
-}
-
-// -----------------------------------------------------------------------------
-// absl::Zipf<T>(bitgen, hi = max, q = 2, v = 1)
-// -----------------------------------------------------------------------------
-//
-// `absl::Zipf` produces discrete probabilities commonly used for modelling of
-// rare events over the closed interval [0, hi]. The parameters `v` and `q`
-// determine the skew of the distribution. `T`  must be an integral type, but
-// may be inferred from the type of `hi`.
-//
-// See http://mathworld.wolfram.com/ZipfDistribution.html
-//
-// Example:
-//
-//   absl::BitGen bitgen;
-//   ...
-//   int term_rank = absl::Zipf<int>(bitgen);
-//
-template <typename IntType, typename URBG>
-IntType Zipf(URBG&& urbg,  // NOLINT(runtime/references)
-             IntType hi = (std::numeric_limits<IntType>::max)(), double q = 2.0,
-             double v = 1.0) {
-  static_assert(std::is_integral<IntType>::value,
-                "Template-argument 'IntType' must be an integral type, in "
-                "absl::Zipf<IntType, URBG>(...)");
-
-  using gen_t = absl::decay_t<URBG>;
-  using distribution_t = typename absl::zipf_distribution<IntType>;
-
-  return random_internal::DistributionCaller<gen_t>::template Call<
-      distribution_t>(&urbg, hi, q, v);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_DISTRIBUTIONS_H_
diff --git a/third_party/abseil_cpp/absl/random/distributions_test.cc b/third_party/abseil_cpp/absl/random/distributions_test.cc
deleted file mode 100644
index 5866a07257..0000000000
--- a/third_party/abseil_cpp/absl/random/distributions_test.cc
+++ /dev/null
@@ -1,455 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/distributions.h"
-
-#include <cmath>
-#include <cstdint>
-#include <random>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/random/internal/distribution_test_util.h"
-#include "absl/random/random.h"
-
-namespace {
-
-constexpr int kSize = 400000;
-
-class RandomDistributionsTest : public testing::Test {};
-
-
-struct Invalid {};
-
-template <typename A, typename B>
-auto InferredUniformReturnT(int)
-    -> decltype(absl::Uniform(std::declval<absl::InsecureBitGen&>(),
-                              std::declval<A>(), std::declval<B>()));
-
-template <typename, typename>
-Invalid InferredUniformReturnT(...);
-
-template <typename TagType, typename A, typename B>
-auto InferredTaggedUniformReturnT(int)
-    -> decltype(absl::Uniform(std::declval<TagType>(),
-                              std::declval<absl::InsecureBitGen&>(),
-                              std::declval<A>(), std::declval<B>()));
-
-template <typename, typename, typename>
-Invalid InferredTaggedUniformReturnT(...);
-
-// Given types <A, B, Expect>, CheckArgsInferType() verifies that
-//
-//   absl::Uniform(gen, A{}, B{})
-//
-// returns the type "Expect".
-//
-// This interface can also be used to assert that a given absl::Uniform()
-// overload does not exist / will not compile. Given types <A, B>, the
-// expression
-//
-//   decltype(absl::Uniform(..., std::declval<A>(), std::declval<B>()))
-//
-// will not compile, leaving the definition of InferredUniformReturnT<A, B> to
-// resolve (via SFINAE) to the overload which returns type "Invalid". This
-// allows tests to assert that an invocation such as
-//
-//   absl::Uniform(gen, 1.23f, std::numeric_limits<int>::max() - 1)
-//
-// should not compile, since neither type, float nor int, can precisely
-// represent both endpoint-values. Writing:
-//
-//   CheckArgsInferType<float, int, Invalid>()
-//
-// will assert that this overload does not exist.
-template <typename A, typename B, typename Expect>
-void CheckArgsInferType() {
-  static_assert(
-      absl::conjunction<
-          std::is_same<Expect, decltype(InferredUniformReturnT<A, B>(0))>,
-          std::is_same<Expect,
-                       decltype(InferredUniformReturnT<B, A>(0))>>::value,
-      "");
-  static_assert(
-      absl::conjunction<
-          std::is_same<Expect, decltype(InferredTaggedUniformReturnT<
-                                        absl::IntervalOpenOpenTag, A, B>(0))>,
-          std::is_same<Expect,
-                       decltype(InferredTaggedUniformReturnT<
-                                absl::IntervalOpenOpenTag, B, A>(0))>>::value,
-      "");
-}
-
-template <typename A, typename B, typename ExplicitRet>
-auto ExplicitUniformReturnT(int) -> decltype(
-    absl::Uniform<ExplicitRet>(*std::declval<absl::InsecureBitGen*>(),
-                               std::declval<A>(), std::declval<B>()));
-
-template <typename, typename, typename ExplicitRet>
-Invalid ExplicitUniformReturnT(...);
-
-template <typename TagType, typename A, typename B, typename ExplicitRet>
-auto ExplicitTaggedUniformReturnT(int) -> decltype(absl::Uniform<ExplicitRet>(
-    std::declval<TagType>(), *std::declval<absl::InsecureBitGen*>(),
-    std::declval<A>(), std::declval<B>()));
-
-template <typename, typename, typename, typename ExplicitRet>
-Invalid ExplicitTaggedUniformReturnT(...);
-
-// Given types <A, B, Expect>, CheckArgsReturnExpectedType() verifies that
-//
-//   absl::Uniform<Expect>(gen, A{}, B{})
-//
-// returns the type "Expect", and that the function-overload has the signature
-//
-//   Expect(URBG&, Expect, Expect)
-template <typename A, typename B, typename Expect>
-void CheckArgsReturnExpectedType() {
-  static_assert(
-      absl::conjunction<
-          std::is_same<Expect,
-                       decltype(ExplicitUniformReturnT<A, B, Expect>(0))>,
-          std::is_same<Expect, decltype(ExplicitUniformReturnT<B, A, Expect>(
-                                   0))>>::value,
-      "");
-  static_assert(
-      absl::conjunction<
-          std::is_same<Expect,
-                       decltype(ExplicitTaggedUniformReturnT<
-                                absl::IntervalOpenOpenTag, A, B, Expect>(0))>,
-          std::is_same<Expect, decltype(ExplicitTaggedUniformReturnT<
-                                        absl::IntervalOpenOpenTag, B, A,
-                                        Expect>(0))>>::value,
-      "");
-}
-
-TEST_F(RandomDistributionsTest, UniformTypeInference) {
-  // Infers common types.
-  CheckArgsInferType<uint16_t, uint16_t, uint16_t>();
-  CheckArgsInferType<uint32_t, uint32_t, uint32_t>();
-  CheckArgsInferType<uint64_t, uint64_t, uint64_t>();
-  CheckArgsInferType<int16_t, int16_t, int16_t>();
-  CheckArgsInferType<int32_t, int32_t, int32_t>();
-  CheckArgsInferType<int64_t, int64_t, int64_t>();
-  CheckArgsInferType<float, float, float>();
-  CheckArgsInferType<double, double, double>();
-
-  // Explicitly-specified return-values override inferences.
-  CheckArgsReturnExpectedType<int16_t, int16_t, int32_t>();
-  CheckArgsReturnExpectedType<uint16_t, uint16_t, int32_t>();
-  CheckArgsReturnExpectedType<int16_t, int16_t, int64_t>();
-  CheckArgsReturnExpectedType<int16_t, int32_t, int64_t>();
-  CheckArgsReturnExpectedType<int16_t, int32_t, double>();
-  CheckArgsReturnExpectedType<float, float, double>();
-  CheckArgsReturnExpectedType<int, int, int16_t>();
-
-  // Properly promotes uint16_t.
-  CheckArgsInferType<uint16_t, uint32_t, uint32_t>();
-  CheckArgsInferType<uint16_t, uint64_t, uint64_t>();
-  CheckArgsInferType<uint16_t, int32_t, int32_t>();
-  CheckArgsInferType<uint16_t, int64_t, int64_t>();
-  CheckArgsInferType<uint16_t, float, float>();
-  CheckArgsInferType<uint16_t, double, double>();
-
-  // Properly promotes int16_t.
-  CheckArgsInferType<int16_t, int32_t, int32_t>();
-  CheckArgsInferType<int16_t, int64_t, int64_t>();
-  CheckArgsInferType<int16_t, float, float>();
-  CheckArgsInferType<int16_t, double, double>();
-
-  // Invalid (u)int16_t-pairings do not compile.
-  // See "CheckArgsInferType" comments above, for how this is achieved.
-  CheckArgsInferType<uint16_t, int16_t, Invalid>();
-  CheckArgsInferType<int16_t, uint32_t, Invalid>();
-  CheckArgsInferType<int16_t, uint64_t, Invalid>();
-
-  // Properly promotes uint32_t.
-  CheckArgsInferType<uint32_t, uint64_t, uint64_t>();
-  CheckArgsInferType<uint32_t, int64_t, int64_t>();
-  CheckArgsInferType<uint32_t, double, double>();
-
-  // Properly promotes int32_t.
-  CheckArgsInferType<int32_t, int64_t, int64_t>();
-  CheckArgsInferType<int32_t, double, double>();
-
-  // Invalid (u)int32_t-pairings do not compile.
-  CheckArgsInferType<uint32_t, int32_t, Invalid>();
-  CheckArgsInferType<int32_t, uint64_t, Invalid>();
-  CheckArgsInferType<int32_t, float, Invalid>();
-  CheckArgsInferType<uint32_t, float, Invalid>();
-
-  // Invalid (u)int64_t-pairings do not compile.
-  CheckArgsInferType<uint64_t, int64_t, Invalid>();
-  CheckArgsInferType<int64_t, float, Invalid>();
-  CheckArgsInferType<int64_t, double, Invalid>();
-
-  // Properly promotes float.
-  CheckArgsInferType<float, double, double>();
-}
-
-TEST_F(RandomDistributionsTest, UniformExamples) {
-  // Examples.
-  absl::InsecureBitGen gen;
-  EXPECT_NE(1, absl::Uniform(gen, static_cast<uint16_t>(0), 1.0f));
-  EXPECT_NE(1, absl::Uniform(gen, 0, 1.0));
-  EXPECT_NE(1, absl::Uniform(absl::IntervalOpenOpen, gen,
-                             static_cast<uint16_t>(0), 1.0f));
-  EXPECT_NE(1, absl::Uniform(absl::IntervalOpenOpen, gen, 0, 1.0));
-  EXPECT_NE(1, absl::Uniform(absl::IntervalOpenOpen, gen, -1, 1.0));
-  EXPECT_NE(1, absl::Uniform<double>(absl::IntervalOpenOpen, gen, -1, 1));
-  EXPECT_NE(1, absl::Uniform<float>(absl::IntervalOpenOpen, gen, 0, 1));
-  EXPECT_NE(1, absl::Uniform<float>(gen, 0, 1));
-}
-
-TEST_F(RandomDistributionsTest, UniformNoBounds) {
-  absl::InsecureBitGen gen;
-
-  absl::Uniform<uint8_t>(gen);
-  absl::Uniform<uint16_t>(gen);
-  absl::Uniform<uint32_t>(gen);
-  absl::Uniform<uint64_t>(gen);
-}
-
-TEST_F(RandomDistributionsTest, UniformNonsenseRanges) {
-  // The ranges used in this test are undefined behavior.
-  // The results are arbitrary and subject to future changes.
-  absl::InsecureBitGen gen;
-
-  // <uint>
-  EXPECT_EQ(0, absl::Uniform<uint64_t>(gen, 0, 0));
-  EXPECT_EQ(1, absl::Uniform<uint64_t>(gen, 1, 0));
-  EXPECT_EQ(0, absl::Uniform<uint64_t>(absl::IntervalOpenOpen, gen, 0, 0));
-  EXPECT_EQ(1, absl::Uniform<uint64_t>(absl::IntervalOpenOpen, gen, 1, 0));
-
-  constexpr auto m = (std::numeric_limits<uint64_t>::max)();
-
-  EXPECT_EQ(m, absl::Uniform(gen, m, m));
-  EXPECT_EQ(m, absl::Uniform(gen, m, m - 1));
-  EXPECT_EQ(m - 1, absl::Uniform(gen, m - 1, m));
-  EXPECT_EQ(m, absl::Uniform(absl::IntervalOpenOpen, gen, m, m));
-  EXPECT_EQ(m, absl::Uniform(absl::IntervalOpenOpen, gen, m, m - 1));
-  EXPECT_EQ(m - 1, absl::Uniform(absl::IntervalOpenOpen, gen, m - 1, m));
-
-  // <int>
-  EXPECT_EQ(0, absl::Uniform<int64_t>(gen, 0, 0));
-  EXPECT_EQ(1, absl::Uniform<int64_t>(gen, 1, 0));
-  EXPECT_EQ(0, absl::Uniform<int64_t>(absl::IntervalOpenOpen, gen, 0, 0));
-  EXPECT_EQ(1, absl::Uniform<int64_t>(absl::IntervalOpenOpen, gen, 1, 0));
-
-  constexpr auto l = (std::numeric_limits<int64_t>::min)();
-  constexpr auto r = (std::numeric_limits<int64_t>::max)();
-
-  EXPECT_EQ(l, absl::Uniform(gen, l, l));
-  EXPECT_EQ(r, absl::Uniform(gen, r, r));
-  EXPECT_EQ(r, absl::Uniform(gen, r, r - 1));
-  EXPECT_EQ(r - 1, absl::Uniform(gen, r - 1, r));
-  EXPECT_EQ(l, absl::Uniform(absl::IntervalOpenOpen, gen, l, l));
-  EXPECT_EQ(r, absl::Uniform(absl::IntervalOpenOpen, gen, r, r));
-  EXPECT_EQ(r, absl::Uniform(absl::IntervalOpenOpen, gen, r, r - 1));
-  EXPECT_EQ(r - 1, absl::Uniform(absl::IntervalOpenOpen, gen, r - 1, r));
-
-  // <double>
-  const double e = std::nextafter(1.0, 2.0);  // 1 + epsilon
-  const double f = std::nextafter(1.0, 0.0);  // 1 - epsilon
-  const double g = std::numeric_limits<double>::denorm_min();
-
-  EXPECT_EQ(1.0, absl::Uniform(gen, 1.0, e));
-  EXPECT_EQ(1.0, absl::Uniform(gen, 1.0, f));
-  EXPECT_EQ(0.0, absl::Uniform(gen, 0.0, g));
-
-  EXPECT_EQ(e, absl::Uniform(absl::IntervalOpenOpen, gen, 1.0, e));
-  EXPECT_EQ(f, absl::Uniform(absl::IntervalOpenOpen, gen, 1.0, f));
-  EXPECT_EQ(g, absl::Uniform(absl::IntervalOpenOpen, gen, 0.0, g));
-}
-
-// TODO(lar): Validate properties of non-default interval-semantics.
-TEST_F(RandomDistributionsTest, UniformReal) {
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = absl::Uniform(gen, 0, 1.0);
-  }
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(0.5, moments.mean, 0.02);
-  EXPECT_NEAR(1 / 12.0, moments.variance, 0.02);
-  EXPECT_NEAR(0.0, moments.skewness, 0.02);
-  EXPECT_NEAR(9 / 5.0, moments.kurtosis, 0.02);
-}
-
-TEST_F(RandomDistributionsTest, UniformInt) {
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    const int64_t kMax = 1000000000000ll;
-    int64_t j = absl::Uniform(absl::IntervalClosedClosed, gen, 0, kMax);
-    // convert to double.
-    values[i] = static_cast<double>(j) / static_cast<double>(kMax);
-  }
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(0.5, moments.mean, 0.02);
-  EXPECT_NEAR(1 / 12.0, moments.variance, 0.02);
-  EXPECT_NEAR(0.0, moments.skewness, 0.02);
-  EXPECT_NEAR(9 / 5.0, moments.kurtosis, 0.02);
-
-  /*
-  // NOTE: These are not supported by absl::Uniform, which is specialized
-  // on integer and real valued types.
-
-  enum E { E0, E1 };    // enum
-  enum S : int { S0, S1 };    // signed enum
-  enum U : unsigned int { U0, U1 };  // unsigned enum
-
-  absl::Uniform(gen, E0, E1);
-  absl::Uniform(gen, S0, S1);
-  absl::Uniform(gen, U0, U1);
-  */
-}
-
-TEST_F(RandomDistributionsTest, Exponential) {
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = absl::Exponential<double>(gen);
-  }
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(1.0, moments.mean, 0.02);
-  EXPECT_NEAR(1.0, moments.variance, 0.025);
-  EXPECT_NEAR(2.0, moments.skewness, 0.1);
-  EXPECT_LT(5.0, moments.kurtosis);
-}
-
-TEST_F(RandomDistributionsTest, PoissonDefault) {
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = absl::Poisson<int64_t>(gen);
-  }
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(1.0, moments.mean, 0.02);
-  EXPECT_NEAR(1.0, moments.variance, 0.02);
-  EXPECT_NEAR(1.0, moments.skewness, 0.025);
-  EXPECT_LT(2.0, moments.kurtosis);
-}
-
-TEST_F(RandomDistributionsTest, PoissonLarge) {
-  constexpr double kMean = 100000000.0;
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = absl::Poisson<int64_t>(gen, kMean);
-  }
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(kMean, moments.mean, kMean * 0.015);
-  EXPECT_NEAR(kMean, moments.variance, kMean * 0.015);
-  EXPECT_NEAR(std::sqrt(kMean), moments.skewness, kMean * 0.02);
-  EXPECT_LT(2.0, moments.kurtosis);
-}
-
-TEST_F(RandomDistributionsTest, Bernoulli) {
-  constexpr double kP = 0.5151515151;
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = absl::Bernoulli(gen, kP);
-  }
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(kP, moments.mean, 0.01);
-}
-
-TEST_F(RandomDistributionsTest, Beta) {
-  constexpr double kAlpha = 2.0;
-  constexpr double kBeta = 3.0;
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = absl::Beta(gen, kAlpha, kBeta);
-  }
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(0.4, moments.mean, 0.01);
-}
-
-TEST_F(RandomDistributionsTest, Zipf) {
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = absl::Zipf<int64_t>(gen, 100);
-  }
-
-  // The mean of a zipf distribution is: H(N, s-1) / H(N,s).
-  // Given the parameter v = 1, this gives the following function:
-  // (Hn(100, 1) - Hn(1,1)) / (Hn(100,2) - Hn(1,2)) = 6.5944
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(6.5944, moments.mean, 2000) << moments;
-}
-
-TEST_F(RandomDistributionsTest, Gaussian) {
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = absl::Gaussian<double>(gen);
-  }
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(0.0, moments.mean, 0.02);
-  EXPECT_NEAR(1.0, moments.variance, 0.04);
-  EXPECT_NEAR(0, moments.skewness, 0.2);
-  EXPECT_NEAR(3.0, moments.kurtosis, 0.5);
-}
-
-TEST_F(RandomDistributionsTest, LogUniform) {
-  std::vector<double> values(kSize);
-
-  absl::InsecureBitGen gen;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = absl::LogUniform<int64_t>(gen, 0, (1 << 10) - 1);
-  }
-
-  // The mean is the sum of the fractional means of the uniform distributions:
-  // [0..0][1..1][2..3][4..7][8..15][16..31][32..63]
-  // [64..127][128..255][256..511][512..1023]
-  const double mean = (0 + 1 + 1 + 2 + 3 + 4 + 7 + 8 + 15 + 16 + 31 + 32 + 63 +
-                       64 + 127 + 128 + 255 + 256 + 511 + 512 + 1023) /
-                      (2.0 * 11.0);
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(mean, moments.mean, 2) << moments;
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/examples_test.cc b/third_party/abseil_cpp/absl/random/examples_test.cc
deleted file mode 100644
index 1dcb5146f3..0000000000
--- a/third_party/abseil_cpp/absl/random/examples_test.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cinttypes>
-#include <random>
-#include <sstream>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/random/random.h"
-
-template <typename T>
-void Use(T) {}
-
-TEST(Examples, Basic) {
-  absl::BitGen gen;
-  std::vector<int> objs = {10, 20, 30, 40, 50};
-
-  // Choose an element from a set.
-  auto elem = objs[absl::Uniform(gen, 0u, objs.size())];
-  Use(elem);
-
-  // Generate a uniform value between 1 and 6.
-  auto dice_roll = absl::Uniform<int>(absl::IntervalClosedClosed, gen, 1, 6);
-  Use(dice_roll);
-
-  // Generate a random byte.
-  auto byte = absl::Uniform<uint8_t>(gen);
-  Use(byte);
-
-  // Generate a fractional value from [0f, 1f).
-  auto fraction = absl::Uniform<float>(gen, 0, 1);
-  Use(fraction);
-
-  // Toss a fair coin; 50/50 probability.
-  bool coin_toss = absl::Bernoulli(gen, 0.5);
-  Use(coin_toss);
-
-  // Select a file size between 1k and 10MB, biased towards smaller file sizes.
-  auto file_size = absl::LogUniform<size_t>(gen, 1000, 10 * 1000 * 1000);
-  Use(file_size);
-
-  // Randomize (shuffle) a collection.
-  std::shuffle(std::begin(objs), std::end(objs), gen);
-}
-
-TEST(Examples, CreateingCorrelatedVariateSequences) {
-  // Unexpected PRNG correlation is often a source of bugs,
-  // so when using absl::BitGen it must be an intentional choice.
-  // NOTE: All of these only exhibit process-level stability.
-
-  // Create a correlated sequence from system entropy.
-  {
-    auto my_seed = absl::MakeSeedSeq();
-
-    absl::BitGen gen_1(my_seed);
-    absl::BitGen gen_2(my_seed);  // Produces same variates as gen_1.
-
-    EXPECT_EQ(absl::Bernoulli(gen_1, 0.5), absl::Bernoulli(gen_2, 0.5));
-    EXPECT_EQ(absl::Uniform<uint32_t>(gen_1), absl::Uniform<uint32_t>(gen_2));
-  }
-
-  // Create a correlated sequence from an existing URBG.
-  {
-    absl::BitGen gen;
-
-    auto my_seed = absl::CreateSeedSeqFrom(&gen);
-    absl::BitGen gen_1(my_seed);
-    absl::BitGen gen_2(my_seed);
-
-    EXPECT_EQ(absl::Bernoulli(gen_1, 0.5), absl::Bernoulli(gen_2, 0.5));
-    EXPECT_EQ(absl::Uniform<uint32_t>(gen_1), absl::Uniform<uint32_t>(gen_2));
-  }
-
-  // An alternate construction which uses user-supplied data
-  // instead of a random seed.
-  {
-    const char kData[] = "A simple seed string";
-    std::seed_seq my_seed(std::begin(kData), std::end(kData));
-
-    absl::BitGen gen_1(my_seed);
-    absl::BitGen gen_2(my_seed);
-
-    EXPECT_EQ(absl::Bernoulli(gen_1, 0.5), absl::Bernoulli(gen_2, 0.5));
-    EXPECT_EQ(absl::Uniform<uint32_t>(gen_1), absl::Uniform<uint32_t>(gen_2));
-  }
-}
-
diff --git a/third_party/abseil_cpp/absl/random/exponential_distribution.h b/third_party/abseil_cpp/absl/random/exponential_distribution.h
deleted file mode 100644
index b5caf8a1e1..0000000000
--- a/third_party/abseil_cpp/absl/random/exponential_distribution.h
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_
-#define ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_
-
-#include <cassert>
-#include <cmath>
-#include <istream>
-#include <limits>
-#include <type_traits>
-
-#include "absl/meta/type_traits.h"
-#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/random/internal/generate_real.h"
-#include "absl/random/internal/iostream_state_saver.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::exponential_distribution:
-// Generates a number conforming to an exponential distribution and is
-// equivalent to the standard [rand.dist.pois.exp] distribution.
-template <typename RealType = double>
-class exponential_distribution {
- public:
-  using result_type = RealType;
-
-  class param_type {
-   public:
-    using distribution_type = exponential_distribution;
-
-    explicit param_type(result_type lambda = 1) : lambda_(lambda) {
-      assert(lambda > 0);
-      neg_inv_lambda_ = -result_type(1) / lambda_;
-    }
-
-    result_type lambda() const { return lambda_; }
-
-    friend bool operator==(const param_type& a, const param_type& b) {
-      return a.lambda_ == b.lambda_;
-    }
-
-    friend bool operator!=(const param_type& a, const param_type& b) {
-      return !(a == b);
-    }
-
-   private:
-    friend class exponential_distribution;
-
-    result_type lambda_;
-    result_type neg_inv_lambda_;
-
-    static_assert(
-        std::is_floating_point<RealType>::value,
-        "Class-template absl::exponential_distribution<> must be parameterized "
-        "using a floating-point type.");
-  };
-
-  exponential_distribution() : exponential_distribution(1) {}
-
-  explicit exponential_distribution(result_type lambda) : param_(lambda) {}
-
-  explicit exponential_distribution(const param_type& p) : param_(p) {}
-
-  void reset() {}
-
-  // Generating functions
-  template <typename URBG>
-  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
-    return (*this)(g, param_);
-  }
-
-  template <typename URBG>
-  result_type operator()(URBG& g,  // NOLINT(runtime/references)
-                         const param_type& p);
-
-  param_type param() const { return param_; }
-  void param(const param_type& p) { param_ = p; }
-
-  result_type(min)() const { return 0; }
-  result_type(max)() const {
-    return std::numeric_limits<result_type>::infinity();
-  }
-
-  result_type lambda() const { return param_.lambda(); }
-
-  friend bool operator==(const exponential_distribution& a,
-                         const exponential_distribution& b) {
-    return a.param_ == b.param_;
-  }
-  friend bool operator!=(const exponential_distribution& a,
-                         const exponential_distribution& b) {
-    return a.param_ != b.param_;
-  }
-
- private:
-  param_type param_;
-  random_internal::FastUniformBits<uint64_t> fast_u64_;
-};
-
-// --------------------------------------------------------------------------
-// Implementation details follow
-// --------------------------------------------------------------------------
-
-template <typename RealType>
-template <typename URBG>
-typename exponential_distribution<RealType>::result_type
-exponential_distribution<RealType>::operator()(
-    URBG& g,  // NOLINT(runtime/references)
-    const param_type& p) {
-  using random_internal::GenerateNegativeTag;
-  using random_internal::GenerateRealFromBits;
-  using real_type =
-      absl::conditional_t<std::is_same<RealType, float>::value, float, double>;
-
-  const result_type u = GenerateRealFromBits<real_type, GenerateNegativeTag,
-                                             false>(fast_u64_(g));  // U(-1, 0)
-
-  // log1p(-x) is mathematically equivalent to log(1 - x) but has more
-  // accuracy for x near zero.
-  return p.neg_inv_lambda_ * std::log1p(u);
-}
-
-template <typename CharT, typename Traits, typename RealType>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    const exponential_distribution<RealType>& x) {
-  auto saver = random_internal::make_ostream_state_saver(os);
-  os.precision(random_internal::stream_precision_helper<RealType>::kPrecision);
-  os << x.lambda();
-  return os;
-}
-
-template <typename CharT, typename Traits, typename RealType>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,    // NOLINT(runtime/references)
-    exponential_distribution<RealType>& x) {  // NOLINT(runtime/references)
-  using result_type = typename exponential_distribution<RealType>::result_type;
-  using param_type = typename exponential_distribution<RealType>::param_type;
-  result_type lambda;
-
-  auto saver = random_internal::make_istream_state_saver(is);
-  lambda = random_internal::read_floating_point<result_type>(is);
-  if (!is.fail()) {
-    x.param(param_type(lambda));
-  }
-  return is;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/exponential_distribution_test.cc b/third_party/abseil_cpp/absl/random/exponential_distribution_test.cc
deleted file mode 100644
index 8e9e69b64b..0000000000
--- a/third_party/abseil_cpp/absl/random/exponential_distribution_test.cc
+++ /dev/null
@@ -1,430 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/exponential_distribution.h"
-
-#include <algorithm>
-#include <cmath>
-#include <cstddef>
-#include <cstdint>
-#include <iterator>
-#include <limits>
-#include <random>
-#include <sstream>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/random/internal/chi_square.h"
-#include "absl/random/internal/distribution_test_util.h"
-#include "absl/random/internal/pcg_engine.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_replace.h"
-#include "absl/strings/strip.h"
-
-namespace {
-
-using absl::random_internal::kChiSquared;
-
-template <typename RealType>
-class ExponentialDistributionTypedTest : public ::testing::Test {};
-
-#if defined(__EMSCRIPTEN__)
-using RealTypes = ::testing::Types<float, double>;
-#else
-using RealTypes = ::testing::Types<float, double, long double>;
-#endif  // defined(__EMSCRIPTEN__)
-TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes);
-
-TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) {
-  using param_type =
-      typename absl::exponential_distribution<TypeParam>::param_type;
-
-  const TypeParam kParams[] = {
-      // Cases around 1.
-      1,                                           //
-      std::nextafter(TypeParam(1), TypeParam(0)),  // 1 - epsilon
-      std::nextafter(TypeParam(1), TypeParam(2)),  // 1 + epsilon
-      // Typical cases.
-      TypeParam(1e-8), TypeParam(1e-4), TypeParam(1), TypeParam(2),
-      TypeParam(1e4), TypeParam(1e8), TypeParam(1e20), TypeParam(2.5),
-      // Boundary cases.
-      std::numeric_limits<TypeParam>::max(),
-      std::numeric_limits<TypeParam>::epsilon(),
-      std::nextafter(std::numeric_limits<TypeParam>::min(),
-                     TypeParam(1)),           // min + epsilon
-      std::numeric_limits<TypeParam>::min(),  // smallest normal
-      // There are some errors dealing with denorms on apple platforms.
-      std::numeric_limits<TypeParam>::denorm_min(),  // smallest denorm
-      std::numeric_limits<TypeParam>::min() / 2,     // denorm
-      std::nextafter(std::numeric_limits<TypeParam>::min(),
-                     TypeParam(0)),  // denorm_max
-  };
-
-  constexpr int kCount = 1000;
-  absl::InsecureBitGen gen;
-
-  for (const TypeParam lambda : kParams) {
-    // Some values may be invalid; skip those.
-    if (!std::isfinite(lambda)) continue;
-    ABSL_ASSERT(lambda > 0);
-
-    const param_type param(lambda);
-
-    absl::exponential_distribution<TypeParam> before(lambda);
-    EXPECT_EQ(before.lambda(), param.lambda());
-
-    {
-      absl::exponential_distribution<TypeParam> via_param(param);
-      EXPECT_EQ(via_param, before);
-      EXPECT_EQ(via_param.param(), before.param());
-    }
-
-    // Smoke test.
-    auto sample_min = before.max();
-    auto sample_max = before.min();
-    for (int i = 0; i < kCount; i++) {
-      auto sample = before(gen);
-      EXPECT_GE(sample, before.min()) << before;
-      EXPECT_LE(sample, before.max()) << before;
-      if (sample > sample_max) sample_max = sample;
-      if (sample < sample_min) sample_min = sample;
-    }
-    if (!std::is_same<TypeParam, long double>::value) {
-      ABSL_INTERNAL_LOG(INFO,
-                        absl::StrFormat("Range {%f}: %f, %f, lambda=%f", lambda,
-                                        sample_min, sample_max, lambda));
-    }
-
-    std::stringstream ss;
-    ss << before;
-
-    if (!std::isfinite(lambda)) {
-      // Streams do not deserialize inf/nan correctly.
-      continue;
-    }
-    // Validate stream serialization.
-    absl::exponential_distribution<TypeParam> after(34.56f);
-
-    EXPECT_NE(before.lambda(), after.lambda());
-    EXPECT_NE(before.param(), after.param());
-    EXPECT_NE(before, after);
-
-    ss >> after;
-
-#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
-    defined(__ppc__) || defined(__PPC__)
-    if (std::is_same<TypeParam, long double>::value) {
-      // Roundtripping floating point values requires sufficient precision to
-      // reconstruct the exact value. It turns out that long double has some
-      // errors doing this on ppc, particularly for values
-      // near {1.0 +/- epsilon}.
-      if (lambda <= std::numeric_limits<double>::max() &&
-          lambda >= std::numeric_limits<double>::lowest()) {
-        EXPECT_EQ(static_cast<double>(before.lambda()),
-                  static_cast<double>(after.lambda()))
-            << ss.str();
-      }
-      continue;
-    }
-#endif
-
-    EXPECT_EQ(before.lambda(), after.lambda())  //
-        << ss.str() << " "                      //
-        << (ss.good() ? "good " : "")           //
-        << (ss.bad() ? "bad " : "")             //
-        << (ss.eof() ? "eof " : "")             //
-        << (ss.fail() ? "fail " : "");
-  }
-}
-
-// http://www.itl.nist.gov/div898/handbook/eda/section3/eda3667.htm
-
-class ExponentialModel {
- public:
-  explicit ExponentialModel(double lambda)
-      : lambda_(lambda), beta_(1.0 / lambda) {}
-
-  double lambda() const { return lambda_; }
-
-  double mean() const { return beta_; }
-  double variance() const { return beta_ * beta_; }
-  double stddev() const { return std::sqrt(variance()); }
-  double skew() const { return 2; }
-  double kurtosis() const { return 6.0; }
-
-  double CDF(double x) { return 1.0 - std::exp(-lambda_ * x); }
-
-  // The inverse CDF, or PercentPoint function of the distribution
-  double InverseCDF(double p) {
-    ABSL_ASSERT(p >= 0.0);
-    ABSL_ASSERT(p < 1.0);
-    return -beta_ * std::log(1.0 - p);
-  }
-
- private:
-  const double lambda_;
-  const double beta_;
-};
-
-struct Param {
-  double lambda;
-  double p_fail;
-  int trials;
-};
-
-class ExponentialDistributionTests : public testing::TestWithParam<Param>,
-                                     public ExponentialModel {
- public:
-  ExponentialDistributionTests() : ExponentialModel(GetParam().lambda) {}
-
-  // SingleZTest provides a basic z-squared test of the mean vs. expected
-  // mean for data generated by the poisson distribution.
-  template <typename D>
-  bool SingleZTest(const double p, const size_t samples);
-
-  // SingleChiSquaredTest provides a basic chi-squared test of the normal
-  // distribution.
-  template <typename D>
-  double SingleChiSquaredTest();
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6};
-};
-
-template <typename D>
-bool ExponentialDistributionTests::SingleZTest(const double p,
-                                               const size_t samples) {
-  D dis(lambda());
-
-  std::vector<double> data;
-  data.reserve(samples);
-  for (size_t i = 0; i < samples; i++) {
-    const double x = dis(rng_);
-    data.push_back(x);
-  }
-
-  const auto m = absl::random_internal::ComputeDistributionMoments(data);
-  const double max_err = absl::random_internal::MaxErrorTolerance(p);
-  const double z = absl::random_internal::ZScore(mean(), m);
-  const bool pass = absl::random_internal::Near("z", z, 0.0, max_err);
-
-  if (!pass) {
-    ABSL_INTERNAL_LOG(
-        INFO, absl::StrFormat("p=%f max_err=%f\n"
-                              " lambda=%f\n"
-                              " mean=%f vs. %f\n"
-                              " stddev=%f vs. %f\n"
-                              " skewness=%f vs. %f\n"
-                              " kurtosis=%f vs. %f\n"
-                              " z=%f vs. 0",
-                              p, max_err, lambda(), m.mean, mean(),
-                              std::sqrt(m.variance), stddev(), m.skewness,
-                              skew(), m.kurtosis, kurtosis(), z));
-  }
-  return pass;
-}
-
-template <typename D>
-double ExponentialDistributionTests::SingleChiSquaredTest() {
-  const size_t kSamples = 10000;
-  const int kBuckets = 50;
-
-  // The InverseCDF is the percent point function of the distribution, and can
-  // be used to assign buckets roughly uniformly.
-  std::vector<double> cutoffs;
-  const double kInc = 1.0 / static_cast<double>(kBuckets);
-  for (double p = kInc; p < 1.0; p += kInc) {
-    cutoffs.push_back(InverseCDF(p));
-  }
-  if (cutoffs.back() != std::numeric_limits<double>::infinity()) {
-    cutoffs.push_back(std::numeric_limits<double>::infinity());
-  }
-
-  D dis(lambda());
-
-  std::vector<int32_t> counts(cutoffs.size(), 0);
-  for (int j = 0; j < kSamples; j++) {
-    const double x = dis(rng_);
-    auto it = std::upper_bound(cutoffs.begin(), cutoffs.end(), x);
-    counts[std::distance(cutoffs.begin(), it)]++;
-  }
-
-  // Null-hypothesis is that the distribution is exponentially distributed
-  // with the provided lambda (not estimated from the data).
-  const int dof = static_cast<int>(counts.size()) - 1;
-
-  // Our threshold for logging is 1-in-50.
-  const double threshold = absl::random_internal::ChiSquareValue(dof, 0.98);
-
-  const double expected =
-      static_cast<double>(kSamples) / static_cast<double>(counts.size());
-
-  double chi_square = absl::random_internal::ChiSquareWithExpected(
-      std::begin(counts), std::end(counts), expected);
-  double p = absl::random_internal::ChiSquarePValue(chi_square, dof);
-
-  if (chi_square > threshold) {
-    for (int i = 0; i < cutoffs.size(); i++) {
-      ABSL_INTERNAL_LOG(
-          INFO, absl::StrFormat("%d : (%f) = %d", i, cutoffs[i], counts[i]));
-    }
-
-    ABSL_INTERNAL_LOG(INFO,
-                      absl::StrCat("lambda ", lambda(), "\n",     //
-                                   " expected ", expected, "\n",  //
-                                   kChiSquared, " ", chi_square, " (", p, ")\n",
-                                   kChiSquared, " @ 0.98 = ", threshold));
-  }
-  return p;
-}
-
-TEST_P(ExponentialDistributionTests, ZTest) {
-  const size_t kSamples = 10000;
-  const auto& param = GetParam();
-  const int expected_failures =
-      std::max(1, static_cast<int>(std::ceil(param.trials * param.p_fail)));
-  const double p = absl::random_internal::RequiredSuccessProbability(
-      param.p_fail, param.trials);
-
-  int failures = 0;
-  for (int i = 0; i < param.trials; i++) {
-    failures += SingleZTest<absl::exponential_distribution<double>>(p, kSamples)
-                    ? 0
-                    : 1;
-  }
-  EXPECT_LE(failures, expected_failures);
-}
-
-TEST_P(ExponentialDistributionTests, ChiSquaredTest) {
-  const int kTrials = 20;
-  int failures = 0;
-
-  for (int i = 0; i < kTrials; i++) {
-    double p_value =
-        SingleChiSquaredTest<absl::exponential_distribution<double>>();
-    if (p_value < 0.005) {  // 1/200
-      failures++;
-    }
-  }
-
-  // There is a 0.10% chance of producing at least one failure, so raise the
-  // failure threshold high enough to allow for a flake rate < 10,000.
-  EXPECT_LE(failures, 4);
-}
-
-std::vector<Param> GenParams() {
-  return {
-      Param{1.0, 0.02, 100},
-      Param{2.5, 0.02, 100},
-      Param{10, 0.02, 100},
-      // large
-      Param{1e4, 0.02, 100},
-      Param{1e9, 0.02, 100},
-      // small
-      Param{0.1, 0.02, 100},
-      Param{1e-3, 0.02, 100},
-      Param{1e-5, 0.02, 100},
-  };
-}
-
-std::string ParamName(const ::testing::TestParamInfo<Param>& info) {
-  const auto& p = info.param;
-  std::string name = absl::StrCat("lambda_", absl::SixDigits(p.lambda));
-  return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
-}
-
-INSTANTIATE_TEST_CASE_P(All, ExponentialDistributionTests,
-                        ::testing::ValuesIn(GenParams()), ParamName);
-
-// NOTE: absl::exponential_distribution is not guaranteed to be stable.
-TEST(ExponentialDistributionTest, StabilityTest) {
-  // absl::exponential_distribution stability relies on std::log1p and
-  // absl::uniform_real_distribution.
-  absl::random_internal::sequence_urbg urbg(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  std::vector<int> output(14);
-
-  {
-    absl::exponential_distribution<double> dist;
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return static_cast<int>(10000.0 * dist(urbg)); });
-
-    EXPECT_EQ(14, urbg.invocations());
-    EXPECT_THAT(output,
-                testing::ElementsAre(0, 71913, 14375, 5039, 1835, 861, 25936,
-                                     804, 126, 12337, 17984, 27002, 0, 71913));
-  }
-
-  urbg.reset();
-  {
-    absl::exponential_distribution<float> dist;
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return static_cast<int>(10000.0f * dist(urbg)); });
-
-    EXPECT_EQ(14, urbg.invocations());
-    EXPECT_THAT(output,
-                testing::ElementsAre(0, 71913, 14375, 5039, 1835, 861, 25936,
-                                     804, 126, 12337, 17984, 27002, 0, 71913));
-  }
-}
-
-TEST(ExponentialDistributionTest, AlgorithmBounds) {
-  // Relies on absl::uniform_real_distribution, so some of these comments
-  // reference that.
-  absl::exponential_distribution<double> dist;
-
-  {
-    // This returns the smallest value >0 from absl::uniform_real_distribution.
-    absl::random_internal::sequence_urbg urbg({0x0000000000000001ull});
-    double a = dist(urbg);
-    EXPECT_EQ(a, 5.42101086242752217004e-20);
-  }
-
-  {
-    // This returns a value very near 0.5 from absl::uniform_real_distribution.
-    absl::random_internal::sequence_urbg urbg({0x7fffffffffffffefull});
-    double a = dist(urbg);
-    EXPECT_EQ(a, 0.693147180559945175204);
-  }
-
-  {
-    // This returns the largest value <1 from absl::uniform_real_distribution.
-    // WolframAlpha: ~39.1439465808987766283058547296341915292187253
-    absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFeFull});
-    double a = dist(urbg);
-    EXPECT_EQ(a, 36.7368005696771007251);
-  }
-  {
-    // This *ALSO* returns the largest value <1.
-    absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFFFull});
-    double a = dist(urbg);
-    EXPECT_EQ(a, 36.7368005696771007251);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/gaussian_distribution.cc b/third_party/abseil_cpp/absl/random/gaussian_distribution.cc
deleted file mode 100644
index c7a72cb2f6..0000000000
--- a/third_party/abseil_cpp/absl/random/gaussian_distribution.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// BEGIN GENERATED CODE; DO NOT EDIT
-// clang-format off
-
-#include "absl/random/gaussian_distribution.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-const gaussian_distribution_base::Tables
-    gaussian_distribution_base::zg_ = {
-    {3.7130862467425505, 3.442619855899000214, 3.223084984581141565,
-     3.083228858216868318, 2.978696252647779819, 2.894344007021528942,
-     2.82312535054891045, 2.761169372387176857, 2.706113573121819549,
-     2.656406411261359679, 2.610972248431847387, 2.56903362592493778,
-     2.530009672388827457, 2.493454522095372106, 2.459018177411830486,
-     2.426420645533749809, 2.395434278011062457, 2.365871370117638595,
-     2.337575241339236776, 2.310413683698762988, 2.284274059677471769,
-     2.25905957386919809, 2.234686395590979036, 2.21108140887870297,
-     2.188180432076048731, 2.165926793748921497, 2.144270182360394905,
-     2.123165708673976138, 2.102573135189237608, 2.082456237992015957,
-     2.062782274508307978, 2.043521536655067194, 2.02464697337738464,
-     2.006133869963471206, 1.987959574127619033, 1.970103260854325633,
-     1.952545729553555764, 1.935269228296621957, 1.918257300864508963,
-     1.901494653105150423, 1.884967035707758143, 1.868661140994487768,
-     1.852564511728090002, 1.836665460258444904, 1.820952996596124418,
-     1.805416764219227366, 1.790046982599857506, 1.77483439558606837,
-     1.759770224899592339, 1.744846128113799244, 1.730054160563729182,
-     1.71538674071366648, 1.700836618569915748, 1.686396846779167014,
-     1.6720607540975998, 1.657821920954023254, 1.643674156862867441,
-     1.629611479470633562, 1.615628095043159629, 1.601718380221376581,
-     1.587876864890574558, 1.574098216022999264, 1.560377222366167382,
-     1.546708779859908844, 1.533087877674041755, 1.519509584765938559,
-     1.505969036863201937, 1.492461423781352714, 1.478981976989922842,
-     1.465525957342709296, 1.452088642889222792, 1.438665316684561546,
-     1.425251254514058319, 1.411841712447055919, 1.398431914131003539,
-     1.385017037732650058, 1.371592202427340812, 1.358152454330141534,
-     1.34469275175354519, 1.331207949665625279, 1.317692783209412299,
-     1.304141850128615054, 1.290549591926194894, 1.27691027356015363,
-     1.263217961454619287, 1.249466499573066436, 1.23564948326336066,
-     1.221760230539994385, 1.207791750415947662, 1.193736707833126465,
-     1.17958738466398616, 1.165335636164750222, 1.150972842148865416,
-     1.136489852013158774, 1.121876922582540237, 1.107123647534034028,
-     1.092218876907275371, 1.077150624892893482, 1.061905963694822042,
-     1.046470900764042922, 1.030830236068192907, 1.014967395251327842,
-     0.9988642334929808131, 0.9825008035154263464, 0.9658550794011470098,
-     0.9489026255113034436, 0.9316161966151479401, 0.9139652510230292792,
-     0.8959153525809346874, 0.8774274291129204872, 0.8584568431938099931,
-     0.8389522142975741614, 0.8188539067003538507, 0.7980920606440534693,
-     0.7765839878947563557, 0.7542306644540520688, 0.7309119106424850631,
-     0.7064796113354325779, 0.6807479186691505202, 0.6534786387399710295,
-     0.6243585973360461505, 0.5929629424714434327, 0.5586921784081798625,
-     0.5206560387620546848, 0.4774378372966830431, 0.4265479863554152429,
-     0.3628714310970211909, 0.2723208648139477384, 0},
-    {0.001014352564120377413, 0.002669629083880922793, 0.005548995220771345792,
-     0.008624484412859888607, 0.01183947865788486861, 0.01516729801054656976,
-     0.01859210273701129151, 0.02210330461592709475, 0.02569329193593428151,
-     0.02935631744000685023, 0.03308788614622575758, 0.03688438878665621645,
-     0.04074286807444417458, 0.04466086220049143157, 0.04863629585986780496,
-     0.05266740190305100461, 0.05675266348104984759, 0.06089077034804041277,
-     0.06508058521306804567, 0.06932111739357792179, 0.07361150188411341722,
-     0.07795098251397346301, 0.08233889824223575293, 0.08677467189478028919,
-     0.09125780082683036809, 0.095787849121731522, 0.1003644410286559929,
-     0.1049872554094214289, 0.1096560210148404546, 0.1143705124488661323,
-     0.1191305467076509556, 0.1239359802028679736, 0.1287867061959434012,
-     0.1336826525834396151, 0.1386237799845948804, 0.1436100800906280339,
-     0.1486415742423425057, 0.1537183122081819397, 0.1588403711394795748,
-     0.1640078546834206341, 0.1692208922373653057, 0.1744796383307898324,
-     0.1797842721232958407, 0.1851349970089926078, 0.1905320403191375633,
-     0.1959756531162781534, 0.2014661100743140865, 0.2070037094399269362,
-     0.2125887730717307134, 0.2182216465543058426, 0.2239026993850088965,
-     0.229632325232116602, 0.2354109422634795556, 0.2412389935454402889,
-     0.2471169475123218551, 0.2530452985073261551, 0.2590245673962052742,
-     0.2650553022555897087, 0.271138079138385224, 0.2772735029191887857,
-     0.2834622082232336471, 0.2897048604429605656, 0.2960021568469337061,
-     0.3023548277864842593, 0.3087636380061818397, 0.3152293880650116065,
-     0.3217529158759855901, 0.3283350983728509642, 0.3349768533135899506,
-     0.3416791412315512977, 0.3484429675463274756, 0.355269384847918035,
-     0.3621594953693184626, 0.3691144536644731522, 0.376135469510563536,
-     0.3832238110559021416, 0.3903808082373155797, 0.3976078564938743676,
-     0.404906420807223999, 0.4122780401026620578, 0.4197243320495753771,
-     0.4272469983049970721, 0.4348478302499918513, 0.4425287152754694975,
-     0.4502916436820402768, 0.458138716267873114, 0.4660721526894572309,
-     0.4740943006930180559, 0.4822076463294863724, 0.4904148252838453348,
-     0.4987186354709807201, 0.5071220510755701794, 0.5156282382440030565,
-     0.5242405726729852944, 0.5329626593838373561, 0.5417983550254266145,
-     0.5507517931146057588, 0.5598274127040882009, 0.5690299910679523787,
-     0.5783646811197646898, 0.5878370544347081283, 0.5974531509445183408,
-     0.6072195366251219584, 0.6171433708188825973, 0.6272324852499290282,
-     0.6374954773350440806, 0.6479418211102242475, 0.6585820000500898219,
-     0.6694276673488921414, 0.6804918409973358395, 0.6917891434366769676,
-     0.7033360990161600101, 0.7151515074105005976, 0.7272569183441868201,
-     0.7396772436726493094, 0.7524415591746134169, 0.7655841738977066102,
-     0.7791460859296898134, 0.7931770117713072832, 0.8077382946829627652,
-     0.8229072113814113187, 0.8387836052959920519, 0.8555006078694531446,
-     0.873243048910072206, 0.8922816507840289901, 0.9130436479717434217,
-     0.9362826816850632339, 0.9635996931270905952, 1}};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// clang-format on
-// END GENERATED CODE
diff --git a/third_party/abseil_cpp/absl/random/gaussian_distribution.h b/third_party/abseil_cpp/absl/random/gaussian_distribution.h
deleted file mode 100644
index 4b07a5c0af..0000000000
--- a/third_party/abseil_cpp/absl/random/gaussian_distribution.h
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_
-#define ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_
-
-// absl::gaussian_distribution implements the Ziggurat algorithm
-// for generating random gaussian numbers.
-//
-// Implementation based on "The Ziggurat Method for Generating Random Variables"
-// by George Marsaglia and Wai Wan Tsang: http://www.jstatsoft.org/v05/i08/
-//
-
-#include <cmath>
-#include <cstdint>
-#include <istream>
-#include <limits>
-#include <type_traits>
-
-#include "absl/base/config.h"
-#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/random/internal/generate_real.h"
-#include "absl/random/internal/iostream_state_saver.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// absl::gaussian_distribution_base implements the underlying ziggurat algorithm
-// using the ziggurat tables generated by the gaussian_distribution_gentables
-// binary.
-//
-// The specific algorithm has some of the improvements suggested by the
-// 2005 paper, "An Improved Ziggurat Method to Generate Normal Random Samples",
-// Jurgen A Doornik.  (https://www.doornik.com/research/ziggurat.pdf)
-class ABSL_DLL gaussian_distribution_base {
- public:
-  template <typename URBG>
-  inline double zignor(URBG& g);  // NOLINT(runtime/references)
-
- private:
-  friend class TableGenerator;
-
-  template <typename URBG>
-  inline double zignor_fallback(URBG& g,  // NOLINT(runtime/references)
-                                bool neg);
-
-  // Constants used for the gaussian distribution.
-  static constexpr double kR = 3.442619855899;  // Start of the tail.
-  static constexpr double kRInv = 0.29047645161474317;  // ~= (1.0 / kR) .
-  static constexpr double kV = 9.91256303526217e-3;
-  static constexpr uint64_t kMask = 0x07f;
-
-  // The ziggurat tables store the pdf(f) and inverse-pdf(x) for equal-area
-  // points on one-half of the normal distribution, where the pdf function,
-  // pdf = e ^ (-1/2 *x^2), assumes that the mean = 0 & stddev = 1.
-  //
-  // These tables are just over 2kb in size; larger tables might improve the
-  // distributions, but also lead to more cache pollution.
-  //
-  // x = {3.71308, 3.44261, 3.22308, ..., 0}
-  // f = {0.00101, 0.00266, 0.00554, ..., 1}
-  struct Tables {
-    double x[kMask + 2];
-    double f[kMask + 2];
-  };
-  static const Tables zg_;
-  random_internal::FastUniformBits<uint64_t> fast_u64_;
-};
-
-}  // namespace random_internal
-
-// absl::gaussian_distribution:
-// Generates a number conforming to a Gaussian distribution.
-template <typename RealType = double>
-class gaussian_distribution : random_internal::gaussian_distribution_base {
- public:
-  using result_type = RealType;
-
-  class param_type {
-   public:
-    using distribution_type = gaussian_distribution;
-
-    explicit param_type(result_type mean = 0, result_type stddev = 1)
-        : mean_(mean), stddev_(stddev) {}
-
-    // Returns the mean distribution parameter.  The mean specifies the location
-    // of the peak.  The default value is 0.0.
-    result_type mean() const { return mean_; }
-
-    // Returns the deviation distribution parameter.  The default value is 1.0.
-    result_type stddev() const { return stddev_; }
-
-    friend bool operator==(const param_type& a, const param_type& b) {
-      return a.mean_ == b.mean_ && a.stddev_ == b.stddev_;
-    }
-
-    friend bool operator!=(const param_type& a, const param_type& b) {
-      return !(a == b);
-    }
-
-   private:
-    result_type mean_;
-    result_type stddev_;
-
-    static_assert(
-        std::is_floating_point<RealType>::value,
-        "Class-template absl::gaussian_distribution<> must be parameterized "
-        "using a floating-point type.");
-  };
-
-  gaussian_distribution() : gaussian_distribution(0) {}
-
-  explicit gaussian_distribution(result_type mean, result_type stddev = 1)
-      : param_(mean, stddev) {}
-
-  explicit gaussian_distribution(const param_type& p) : param_(p) {}
-
-  void reset() {}
-
-  // Generating functions
-  template <typename URBG>
-  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
-    return (*this)(g, param_);
-  }
-
-  template <typename URBG>
-  result_type operator()(URBG& g,  // NOLINT(runtime/references)
-                         const param_type& p);
-
-  param_type param() const { return param_; }
-  void param(const param_type& p) { param_ = p; }
-
-  result_type(min)() const {
-    return -std::numeric_limits<result_type>::infinity();
-  }
-  result_type(max)() const {
-    return std::numeric_limits<result_type>::infinity();
-  }
-
-  result_type mean() const { return param_.mean(); }
-  result_type stddev() const { return param_.stddev(); }
-
-  friend bool operator==(const gaussian_distribution& a,
-                         const gaussian_distribution& b) {
-    return a.param_ == b.param_;
-  }
-  friend bool operator!=(const gaussian_distribution& a,
-                         const gaussian_distribution& b) {
-    return a.param_ != b.param_;
-  }
-
- private:
-  param_type param_;
-};
-
-// --------------------------------------------------------------------------
-// Implementation details only below
-// --------------------------------------------------------------------------
-
-template <typename RealType>
-template <typename URBG>
-typename gaussian_distribution<RealType>::result_type
-gaussian_distribution<RealType>::operator()(
-    URBG& g,  // NOLINT(runtime/references)
-    const param_type& p) {
-  return p.mean() + p.stddev() * static_cast<result_type>(zignor(g));
-}
-
-template <typename CharT, typename Traits, typename RealType>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    const gaussian_distribution<RealType>& x) {
-  auto saver = random_internal::make_ostream_state_saver(os);
-  os.precision(random_internal::stream_precision_helper<RealType>::kPrecision);
-  os << x.mean() << os.fill() << x.stddev();
-  return os;
-}
-
-template <typename CharT, typename Traits, typename RealType>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-    gaussian_distribution<RealType>& x) {   // NOLINT(runtime/references)
-  using result_type = typename gaussian_distribution<RealType>::result_type;
-  using param_type = typename gaussian_distribution<RealType>::param_type;
-
-  auto saver = random_internal::make_istream_state_saver(is);
-  auto mean = random_internal::read_floating_point<result_type>(is);
-  if (is.fail()) return is;
-  auto stddev = random_internal::read_floating_point<result_type>(is);
-  if (!is.fail()) {
-    x.param(param_type(mean, stddev));
-  }
-  return is;
-}
-
-namespace random_internal {
-
-template <typename URBG>
-inline double gaussian_distribution_base::zignor_fallback(URBG& g, bool neg) {
-  using random_internal::GeneratePositiveTag;
-  using random_internal::GenerateRealFromBits;
-
-  // This fallback path happens approximately 0.05% of the time.
-  double x, y;
-  do {
-    // kRInv = 1/r, U(0, 1)
-    x = kRInv *
-        std::log(GenerateRealFromBits<double, GeneratePositiveTag, false>(
-            fast_u64_(g)));
-    y = -std::log(
-        GenerateRealFromBits<double, GeneratePositiveTag, false>(fast_u64_(g)));
-  } while ((y + y) < (x * x));
-  return neg ? (x - kR) : (kR - x);
-}
-
-template <typename URBG>
-inline double gaussian_distribution_base::zignor(
-    URBG& g) {  // NOLINT(runtime/references)
-  using random_internal::GeneratePositiveTag;
-  using random_internal::GenerateRealFromBits;
-  using random_internal::GenerateSignedTag;
-
-  while (true) {
-    // We use a single uint64_t to generate both a double and a strip.
-    // These bits are unused when the generated double is > 1/2^5.
-    // This may introduce some bias from the duplicated low bits of small
-    // values (those smaller than 1/2^5, which all end up on the left tail).
-    uint64_t bits = fast_u64_(g);
-    int i = static_cast<int>(bits & kMask);  // pick a random strip
-    double j = GenerateRealFromBits<double, GenerateSignedTag, false>(
-        bits);  // U(-1, 1)
-    const double x = j * zg_.x[i];
-
-    // Retangular box. Handles >97% of all cases.
-    // For any given box, this handles between 75% and 99% of values.
-    // Equivalent to U(01) < (x[i+1] / x[i]), and when i == 0, ~93.5%
-    if (std::abs(x) < zg_.x[i + 1]) {
-      return x;
-    }
-
-    // i == 0: Base box. Sample using a ratio of uniforms.
-    if (i == 0) {
-      // This path happens about 0.05% of the time.
-      return zignor_fallback(g, j < 0);
-    }
-
-    // i > 0: Wedge samples using precomputed values.
-    double v = GenerateRealFromBits<double, GeneratePositiveTag, false>(
-        fast_u64_(g));  // U(0, 1)
-    if ((zg_.f[i + 1] + v * (zg_.f[i] - zg_.f[i + 1])) <
-        std::exp(-0.5 * x * x)) {
-      return x;
-    }
-
-    // The wedge was missed; reject the value and try again.
-  }
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/gaussian_distribution_test.cc b/third_party/abseil_cpp/absl/random/gaussian_distribution_test.cc
deleted file mode 100644
index 02ac578a5c..0000000000
--- a/third_party/abseil_cpp/absl/random/gaussian_distribution_test.cc
+++ /dev/null
@@ -1,579 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/gaussian_distribution.h"
-
-#include <algorithm>
-#include <cmath>
-#include <cstddef>
-#include <ios>
-#include <iterator>
-#include <random>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/random/internal/chi_square.h"
-#include "absl/random/internal/distribution_test_util.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_replace.h"
-#include "absl/strings/strip.h"
-
-namespace {
-
-using absl::random_internal::kChiSquared;
-
-template <typename RealType>
-class GaussianDistributionInterfaceTest : public ::testing::Test {};
-
-using RealTypes = ::testing::Types<float, double, long double>;
-TYPED_TEST_CASE(GaussianDistributionInterfaceTest, RealTypes);
-
-TYPED_TEST(GaussianDistributionInterfaceTest, SerializeTest) {
-  using param_type =
-      typename absl::gaussian_distribution<TypeParam>::param_type;
-
-  const TypeParam kParams[] = {
-      // Cases around 1.
-      1,                                           //
-      std::nextafter(TypeParam(1), TypeParam(0)),  // 1 - epsilon
-      std::nextafter(TypeParam(1), TypeParam(2)),  // 1 + epsilon
-      // Arbitrary values.
-      TypeParam(1e-8), TypeParam(1e-4), TypeParam(2), TypeParam(1e4),
-      TypeParam(1e8), TypeParam(1e20), TypeParam(2.5),
-      // Boundary cases.
-      std::numeric_limits<TypeParam>::infinity(),
-      std::numeric_limits<TypeParam>::max(),
-      std::numeric_limits<TypeParam>::epsilon(),
-      std::nextafter(std::numeric_limits<TypeParam>::min(),
-                     TypeParam(1)),           // min + epsilon
-      std::numeric_limits<TypeParam>::min(),  // smallest normal
-      // There are some errors dealing with denorms on apple platforms.
-      std::numeric_limits<TypeParam>::denorm_min(),  // smallest denorm
-      std::numeric_limits<TypeParam>::min() / 2,
-      std::nextafter(std::numeric_limits<TypeParam>::min(),
-                     TypeParam(0)),  // denorm_max
-  };
-
-  constexpr int kCount = 1000;
-  absl::InsecureBitGen gen;
-
-  // Use a loop to generate the combinations of {+/-x, +/-y}, and assign x, y to
-  // all values in kParams,
-  for (const auto mod : {0, 1, 2, 3}) {
-    for (const auto x : kParams) {
-      if (!std::isfinite(x)) continue;
-      for (const auto y : kParams) {
-        const TypeParam mean = (mod & 0x1) ? -x : x;
-        const TypeParam stddev = (mod & 0x2) ? -y : y;
-        const param_type param(mean, stddev);
-
-        absl::gaussian_distribution<TypeParam> before(mean, stddev);
-        EXPECT_EQ(before.mean(), param.mean());
-        EXPECT_EQ(before.stddev(), param.stddev());
-
-        {
-          absl::gaussian_distribution<TypeParam> via_param(param);
-          EXPECT_EQ(via_param, before);
-          EXPECT_EQ(via_param.param(), before.param());
-        }
-
-        // Smoke test.
-        auto sample_min = before.max();
-        auto sample_max = before.min();
-        for (int i = 0; i < kCount; i++) {
-          auto sample = before(gen);
-          if (sample > sample_max) sample_max = sample;
-          if (sample < sample_min) sample_min = sample;
-          EXPECT_GE(sample, before.min()) << before;
-          EXPECT_LE(sample, before.max()) << before;
-        }
-        if (!std::is_same<TypeParam, long double>::value) {
-          ABSL_INTERNAL_LOG(
-              INFO, absl::StrFormat("Range{%f, %f}: %f, %f", mean, stddev,
-                                    sample_min, sample_max));
-        }
-
-        std::stringstream ss;
-        ss << before;
-
-        if (!std::isfinite(mean) || !std::isfinite(stddev)) {
-          // Streams do not parse inf/nan.
-          continue;
-        }
-
-        // Validate stream serialization.
-        absl::gaussian_distribution<TypeParam> after(-0.53f, 2.3456f);
-
-        EXPECT_NE(before.mean(), after.mean());
-        EXPECT_NE(before.stddev(), after.stddev());
-        EXPECT_NE(before.param(), after.param());
-        EXPECT_NE(before, after);
-
-        ss >> after;
-
-#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
-    defined(__ppc__) || defined(__PPC__) || defined(__EMSCRIPTEN__)
-        if (std::is_same<TypeParam, long double>::value) {
-          // Roundtripping floating point values requires sufficient precision
-          // to reconstruct the exact value.  It turns out that long double
-          // has some errors doing this on ppc, particularly for values
-          // near {1.0 +/- epsilon}.
-          //
-          // Emscripten is even worse, implementing long double as a 128-bit
-          // type, but shipping with a strtold() that doesn't support that.
-          if (mean <= std::numeric_limits<double>::max() &&
-              mean >= std::numeric_limits<double>::lowest()) {
-            EXPECT_EQ(static_cast<double>(before.mean()),
-                      static_cast<double>(after.mean()))
-                << ss.str();
-          }
-          if (stddev <= std::numeric_limits<double>::max() &&
-              stddev >= std::numeric_limits<double>::lowest()) {
-            EXPECT_EQ(static_cast<double>(before.stddev()),
-                      static_cast<double>(after.stddev()))
-                << ss.str();
-          }
-          continue;
-        }
-#endif
-
-        EXPECT_EQ(before.mean(), after.mean());
-        EXPECT_EQ(before.stddev(), after.stddev())  //
-            << ss.str() << " "                      //
-            << (ss.good() ? "good " : "")           //
-            << (ss.bad() ? "bad " : "")             //
-            << (ss.eof() ? "eof " : "")             //
-            << (ss.fail() ? "fail " : "");
-      }
-    }
-  }
-}
-
-// http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm
-
-class GaussianModel {
- public:
-  GaussianModel(double mean, double stddev) : mean_(mean), stddev_(stddev) {}
-
-  double mean() const { return mean_; }
-  double variance() const { return stddev() * stddev(); }
-  double stddev() const { return stddev_; }
-  double skew() const { return 0; }
-  double kurtosis() const { return 3.0; }
-
-  // The inverse CDF, or PercentPoint function.
-  double InverseCDF(double p) {
-    ABSL_ASSERT(p >= 0.0);
-    ABSL_ASSERT(p < 1.0);
-    return mean() + stddev() * -absl::random_internal::InverseNormalSurvival(p);
-  }
-
- private:
-  const double mean_;
-  const double stddev_;
-};
-
-struct Param {
-  double mean;
-  double stddev;
-  double p_fail;  // Z-Test probability of failure.
-  int trials;     // Z-Test trials.
-};
-
-// GaussianDistributionTests implements a z-test for the gaussian
-// distribution.
-class GaussianDistributionTests : public testing::TestWithParam<Param>,
-                                  public GaussianModel {
- public:
-  GaussianDistributionTests()
-      : GaussianModel(GetParam().mean, GetParam().stddev) {}
-
-  // SingleZTest provides a basic z-squared test of the mean vs. expected
-  // mean for data generated by the poisson distribution.
-  template <typename D>
-  bool SingleZTest(const double p, const size_t samples);
-
-  // SingleChiSquaredTest provides a basic chi-squared test of the normal
-  // distribution.
-  template <typename D>
-  double SingleChiSquaredTest();
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6};
-};
-
-template <typename D>
-bool GaussianDistributionTests::SingleZTest(const double p,
-                                            const size_t samples) {
-  D dis(mean(), stddev());
-
-  std::vector<double> data;
-  data.reserve(samples);
-  for (size_t i = 0; i < samples; i++) {
-    const double x = dis(rng_);
-    data.push_back(x);
-  }
-
-  const double max_err = absl::random_internal::MaxErrorTolerance(p);
-  const auto m = absl::random_internal::ComputeDistributionMoments(data);
-  const double z = absl::random_internal::ZScore(mean(), m);
-  const bool pass = absl::random_internal::Near("z", z, 0.0, max_err);
-
-  // NOTE: Informational statistical test:
-  //
-  // Compute the Jarque-Bera test statistic given the excess skewness
-  // and kurtosis. The statistic is drawn from a chi-square(2) distribution.
-  // https://en.wikipedia.org/wiki/Jarque%E2%80%93Bera_test
-  //
-  // The null-hypothesis (normal distribution) is rejected when
-  // (p = 0.05 => jb > 5.99)
-  // (p = 0.01 => jb > 9.21)
-  // NOTE: JB has a large type-I error rate, so it will reject the
-  // null-hypothesis even when it is true more often than the z-test.
-  //
-  const double jb =
-      static_cast<double>(m.n) / 6.0 *
-      (std::pow(m.skewness, 2.0) + std::pow(m.kurtosis - 3.0, 2.0) / 4.0);
-
-  if (!pass || jb > 9.21) {
-    ABSL_INTERNAL_LOG(
-        INFO, absl::StrFormat("p=%f max_err=%f\n"
-                              " mean=%f vs. %f\n"
-                              " stddev=%f vs. %f\n"
-                              " skewness=%f vs. %f\n"
-                              " kurtosis=%f vs. %f\n"
-                              " z=%f vs. 0\n"
-                              " jb=%f vs. 9.21",
-                              p, max_err, m.mean, mean(), std::sqrt(m.variance),
-                              stddev(), m.skewness, skew(), m.kurtosis,
-                              kurtosis(), z, jb));
-  }
-  return pass;
-}
-
-template <typename D>
-double GaussianDistributionTests::SingleChiSquaredTest() {
-  const size_t kSamples = 10000;
-  const int kBuckets = 50;
-
-  // The InverseCDF is the percent point function of the
-  // distribution, and can be used to assign buckets
-  // roughly uniformly.
-  std::vector<double> cutoffs;
-  const double kInc = 1.0 / static_cast<double>(kBuckets);
-  for (double p = kInc; p < 1.0; p += kInc) {
-    cutoffs.push_back(InverseCDF(p));
-  }
-  if (cutoffs.back() != std::numeric_limits<double>::infinity()) {
-    cutoffs.push_back(std::numeric_limits<double>::infinity());
-  }
-
-  D dis(mean(), stddev());
-
-  std::vector<int32_t> counts(cutoffs.size(), 0);
-  for (int j = 0; j < kSamples; j++) {
-    const double x = dis(rng_);
-    auto it = std::upper_bound(cutoffs.begin(), cutoffs.end(), x);
-    counts[std::distance(cutoffs.begin(), it)]++;
-  }
-
-  // Null-hypothesis is that the distribution is a gaussian distribution
-  // with the provided mean and stddev (not estimated from the data).
-  const int dof = static_cast<int>(counts.size()) - 1;
-
-  // Our threshold for logging is 1-in-50.
-  const double threshold = absl::random_internal::ChiSquareValue(dof, 0.98);
-
-  const double expected =
-      static_cast<double>(kSamples) / static_cast<double>(counts.size());
-
-  double chi_square = absl::random_internal::ChiSquareWithExpected(
-      std::begin(counts), std::end(counts), expected);
-  double p = absl::random_internal::ChiSquarePValue(chi_square, dof);
-
-  // Log if the chi_square value is above the threshold.
-  if (chi_square > threshold) {
-    for (int i = 0; i < cutoffs.size(); i++) {
-      ABSL_INTERNAL_LOG(
-          INFO, absl::StrFormat("%d : (%f) = %d", i, cutoffs[i], counts[i]));
-    }
-
-    ABSL_INTERNAL_LOG(
-        INFO, absl::StrCat("mean=", mean(), " stddev=", stddev(), "\n",   //
-                           " expected ", expected, "\n",                  //
-                           kChiSquared, " ", chi_square, " (", p, ")\n",  //
-                           kChiSquared, " @ 0.98 = ", threshold));
-  }
-  return p;
-}
-
-TEST_P(GaussianDistributionTests, ZTest) {
-  // TODO(absl-team): Run these tests against std::normal_distribution<double>
-  // to validate outcomes are similar.
-  const size_t kSamples = 10000;
-  const auto& param = GetParam();
-  const int expected_failures =
-      std::max(1, static_cast<int>(std::ceil(param.trials * param.p_fail)));
-  const double p = absl::random_internal::RequiredSuccessProbability(
-      param.p_fail, param.trials);
-
-  int failures = 0;
-  for (int i = 0; i < param.trials; i++) {
-    failures +=
-        SingleZTest<absl::gaussian_distribution<double>>(p, kSamples) ? 0 : 1;
-  }
-  EXPECT_LE(failures, expected_failures);
-}
-
-TEST_P(GaussianDistributionTests, ChiSquaredTest) {
-  const int kTrials = 20;
-  int failures = 0;
-
-  for (int i = 0; i < kTrials; i++) {
-    double p_value =
-        SingleChiSquaredTest<absl::gaussian_distribution<double>>();
-    if (p_value < 0.0025) {  // 1/400
-      failures++;
-    }
-  }
-  // There is a 0.05% chance of producing at least one failure, so raise the
-  // failure threshold high enough to allow for a flake rate of less than one in
-  // 10,000.
-  EXPECT_LE(failures, 4);
-}
-
-std::vector<Param> GenParams() {
-  return {
-      // Mean around 0.
-      Param{0.0, 1.0, 0.01, 100},
-      Param{0.0, 1e2, 0.01, 100},
-      Param{0.0, 1e4, 0.01, 100},
-      Param{0.0, 1e8, 0.01, 100},
-      Param{0.0, 1e16, 0.01, 100},
-      Param{0.0, 1e-3, 0.01, 100},
-      Param{0.0, 1e-5, 0.01, 100},
-      Param{0.0, 1e-9, 0.01, 100},
-      Param{0.0, 1e-17, 0.01, 100},
-
-      // Mean around 1.
-      Param{1.0, 1.0, 0.01, 100},
-      Param{1.0, 1e2, 0.01, 100},
-      Param{1.0, 1e-2, 0.01, 100},
-
-      // Mean around 100 / -100
-      Param{1e2, 1.0, 0.01, 100},
-      Param{-1e2, 1.0, 0.01, 100},
-      Param{1e2, 1e6, 0.01, 100},
-      Param{-1e2, 1e6, 0.01, 100},
-
-      // More extreme
-      Param{1e4, 1e4, 0.01, 100},
-      Param{1e8, 1e4, 0.01, 100},
-      Param{1e12, 1e4, 0.01, 100},
-  };
-}
-
-std::string ParamName(const ::testing::TestParamInfo<Param>& info) {
-  const auto& p = info.param;
-  std::string name = absl::StrCat("mean_", absl::SixDigits(p.mean), "__stddev_",
-                                  absl::SixDigits(p.stddev));
-  return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
-}
-
-INSTANTIATE_TEST_SUITE_P(All, GaussianDistributionTests,
-                         ::testing::ValuesIn(GenParams()), ParamName);
-
-// NOTE: absl::gaussian_distribution is not guaranteed to be stable.
-TEST(GaussianDistributionTest, StabilityTest) {
-  // absl::gaussian_distribution stability relies on the underlying zignor
-  // data, absl::random_interna::RandU64ToDouble, std::exp, std::log, and
-  // std::abs.
-  absl::random_internal::sequence_urbg urbg(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  std::vector<int> output(11);
-
-  {
-    absl::gaussian_distribution<double> dist;
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return static_cast<int>(10000000.0 * dist(urbg)); });
-
-    EXPECT_EQ(13, urbg.invocations());
-    EXPECT_THAT(output,  //
-                testing::ElementsAre(1494, 25518841, 9991550, 1351856,
-                                     -20373238, 3456682, 333530, -6804981,
-                                     -15279580, -16459654, 1494));
-  }
-
-  urbg.reset();
-  {
-    absl::gaussian_distribution<float> dist;
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return static_cast<int>(1000000.0f * dist(urbg)); });
-
-    EXPECT_EQ(13, urbg.invocations());
-    EXPECT_THAT(
-        output,  //
-        testing::ElementsAre(149, 2551884, 999155, 135185, -2037323, 345668,
-                             33353, -680498, -1527958, -1645965, 149));
-  }
-}
-
-// This is an implementation-specific test. If any part of the implementation
-// changes, then it is likely that this test will change as well.
-// Also, if dependencies of the distribution change, such as RandU64ToDouble,
-// then this is also likely to change.
-TEST(GaussianDistributionTest, AlgorithmBounds) {
-  absl::gaussian_distribution<double> dist;
-
-  // In ~95% of cases, a single value is used to generate the output.
-  // for all inputs where |x| < 0.750461021389 this should be the case.
-  //
-  // The exact constraints are based on the ziggurat tables, and any
-  // changes to the ziggurat tables may require adjusting these bounds.
-  //
-  // for i in range(0, len(X)-1):
-  //   print i, X[i+1]/X[i], (X[i+1]/X[i] > 0.984375)
-  //
-  // 0.125 <= |values| <= 0.75
-  const uint64_t kValues[] = {
-      0x1000000000000100ull, 0x2000000000000100ull, 0x3000000000000100ull,
-      0x4000000000000100ull, 0x5000000000000100ull, 0x6000000000000100ull,
-      // negative values
-      0x9000000000000100ull, 0xa000000000000100ull, 0xb000000000000100ull,
-      0xc000000000000100ull, 0xd000000000000100ull, 0xe000000000000100ull};
-
-  // 0.875 <= |values| <= 0.984375
-  const uint64_t kExtraValues[] = {
-      0x7000000000000100ull, 0x7800000000000100ull,  //
-      0x7c00000000000100ull, 0x7e00000000000100ull,  //
-      // negative values
-      0xf000000000000100ull, 0xf800000000000100ull,  //
-      0xfc00000000000100ull, 0xfe00000000000100ull};
-
-  auto make_box = [](uint64_t v, uint64_t box) {
-    return (v & 0xffffffffffffff80ull) | box;
-  };
-
-  // The box is the lower 7 bits of the value. When the box == 0, then
-  // the algorithm uses an escape hatch to select the result for large
-  // outputs.
-  for (uint64_t box = 0; box < 0x7f; box++) {
-    for (const uint64_t v : kValues) {
-      // Extra values are added to the sequence to attempt to avoid
-      // infinite loops from rejection sampling on bugs/errors.
-      absl::random_internal::sequence_urbg urbg(
-          {make_box(v, box), 0x0003eb76f6f7f755ull, 0x5FCEA50FDB2F953Bull});
-
-      auto a = dist(urbg);
-      EXPECT_EQ(1, urbg.invocations()) << box << " " << std::hex << v;
-      if (v & 0x8000000000000000ull) {
-        EXPECT_LT(a, 0.0) << box << " " << std::hex << v;
-      } else {
-        EXPECT_GT(a, 0.0) << box << " " << std::hex << v;
-      }
-    }
-    if (box > 10 && box < 100) {
-      // The center boxes use the fast algorithm for more
-      // than 98.4375% of values.
-      for (const uint64_t v : kExtraValues) {
-        absl::random_internal::sequence_urbg urbg(
-            {make_box(v, box), 0x0003eb76f6f7f755ull, 0x5FCEA50FDB2F953Bull});
-
-        auto a = dist(urbg);
-        EXPECT_EQ(1, urbg.invocations()) << box << " " << std::hex << v;
-        if (v & 0x8000000000000000ull) {
-          EXPECT_LT(a, 0.0) << box << " " << std::hex << v;
-        } else {
-          EXPECT_GT(a, 0.0) << box << " " << std::hex << v;
-        }
-      }
-    }
-  }
-
-  // When the box == 0, the fallback algorithm uses a ratio of uniforms,
-  // which consumes 2 additional values from the urbg.
-  // Fallback also requires that the initial value be > 0.9271586026096681.
-  auto make_fallback = [](uint64_t v) { return (v & 0xffffffffffffff80ull); };
-
-  double tail[2];
-  {
-    // 0.9375
-    absl::random_internal::sequence_urbg urbg(
-        {make_fallback(0x7800000000000000ull), 0x13CCA830EB61BD96ull,
-         0x00000076f6f7f755ull});
-    tail[0] = dist(urbg);
-    EXPECT_EQ(3, urbg.invocations());
-    EXPECT_GT(tail[0], 0);
-  }
-  {
-    // -0.9375
-    absl::random_internal::sequence_urbg urbg(
-        {make_fallback(0xf800000000000000ull), 0x13CCA830EB61BD96ull,
-         0x00000076f6f7f755ull});
-    tail[1] = dist(urbg);
-    EXPECT_EQ(3, urbg.invocations());
-    EXPECT_LT(tail[1], 0);
-  }
-  EXPECT_EQ(tail[0], -tail[1]);
-  EXPECT_EQ(418610, static_cast<int64_t>(tail[0] * 100000.0));
-
-  // When the box != 0, the fallback algorithm computes a wedge function.
-  // Depending on the box, the threshold for varies as high as
-  // 0.991522480228.
-  {
-    // 0.9921875, 0.875
-    absl::random_internal::sequence_urbg urbg(
-        {make_box(0x7f00000000000000ull, 120), 0xe000000000000001ull,
-         0x13CCA830EB61BD96ull});
-    tail[0] = dist(urbg);
-    EXPECT_EQ(2, urbg.invocations());
-    EXPECT_GT(tail[0], 0);
-  }
-  {
-    // -0.9921875, 0.875
-    absl::random_internal::sequence_urbg urbg(
-        {make_box(0xff00000000000000ull, 120), 0xe000000000000001ull,
-         0x13CCA830EB61BD96ull});
-    tail[1] = dist(urbg);
-    EXPECT_EQ(2, urbg.invocations());
-    EXPECT_LT(tail[1], 0);
-  }
-  EXPECT_EQ(tail[0], -tail[1]);
-  EXPECT_EQ(61948, static_cast<int64_t>(tail[0] * 100000.0));
-
-  // Fallback rejected, try again.
-  {
-    // -0.9921875, 0.0625
-    absl::random_internal::sequence_urbg urbg(
-        {make_box(0xff00000000000000ull, 120), 0x1000000000000001,
-         make_box(0x1000000000000100ull, 50), 0x13CCA830EB61BD96ull});
-    dist(urbg);
-    EXPECT_EQ(3, urbg.invocations());
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/generators_test.cc b/third_party/abseil_cpp/absl/random/generators_test.cc
deleted file mode 100644
index 41725f139c..0000000000
--- a/third_party/abseil_cpp/absl/random/generators_test.cc
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cstddef>
-#include <cstdint>
-#include <random>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/random/distributions.h"
-#include "absl/random/random.h"
-
-namespace {
-
-template <typename URBG>
-void TestUniform(URBG* gen) {
-  // [a, b) default-semantics, inferred types.
-  absl::Uniform(*gen, 0, 100);     // int
-  absl::Uniform(*gen, 0, 1.0);     // Promoted to double
-  absl::Uniform(*gen, 0.0f, 1.0);  // Promoted to double
-  absl::Uniform(*gen, 0.0, 1.0);   // double
-  absl::Uniform(*gen, -1, 1L);     // Promoted to long
-
-  // Roll a die.
-  absl::Uniform(absl::IntervalClosedClosed, *gen, 1, 6);
-
-  // Get a fraction.
-  absl::Uniform(absl::IntervalOpenOpen, *gen, 0.0, 1.0);
-
-  // Assign a value to a random element.
-  std::vector<int> elems = {10, 20, 30, 40, 50};
-  elems[absl::Uniform(*gen, 0u, elems.size())] = 5;
-  elems[absl::Uniform<size_t>(*gen, 0, elems.size())] = 3;
-
-  // Choose some epsilon around zero.
-  absl::Uniform(absl::IntervalOpenOpen, *gen, -1.0, 1.0);
-
-  // (a, b) semantics, inferred types.
-  absl::Uniform(absl::IntervalOpenOpen, *gen, 0, 1.0);  // Promoted to double
-
-  // Explict overriding of types.
-  absl::Uniform<int>(*gen, 0, 100);
-  absl::Uniform<int8_t>(*gen, 0, 100);
-  absl::Uniform<int16_t>(*gen, 0, 100);
-  absl::Uniform<uint16_t>(*gen, 0, 100);
-  absl::Uniform<int32_t>(*gen, 0, 1 << 10);
-  absl::Uniform<uint32_t>(*gen, 0, 1 << 10);
-  absl::Uniform<int64_t>(*gen, 0, 1 << 10);
-  absl::Uniform<uint64_t>(*gen, 0, 1 << 10);
-
-  absl::Uniform<float>(*gen, 0.0, 1.0);
-  absl::Uniform<float>(*gen, 0, 1);
-  absl::Uniform<float>(*gen, -1, 1);
-  absl::Uniform<double>(*gen, 0.0, 1.0);
-
-  absl::Uniform<float>(*gen, -1.0, 0);
-  absl::Uniform<double>(*gen, -1.0, 0);
-
-  // Tagged
-  absl::Uniform<double>(absl::IntervalClosedClosed, *gen, 0, 1);
-  absl::Uniform<double>(absl::IntervalClosedOpen, *gen, 0, 1);
-  absl::Uniform<double>(absl::IntervalOpenOpen, *gen, 0, 1);
-  absl::Uniform<double>(absl::IntervalOpenClosed, *gen, 0, 1);
-  absl::Uniform<double>(absl::IntervalClosedClosed, *gen, 0, 1);
-  absl::Uniform<double>(absl::IntervalOpenOpen, *gen, 0, 1);
-
-  absl::Uniform<int>(absl::IntervalClosedClosed, *gen, 0, 100);
-  absl::Uniform<int>(absl::IntervalClosedOpen, *gen, 0, 100);
-  absl::Uniform<int>(absl::IntervalOpenOpen, *gen, 0, 100);
-  absl::Uniform<int>(absl::IntervalOpenClosed, *gen, 0, 100);
-  absl::Uniform<int>(absl::IntervalClosedClosed, *gen, 0, 100);
-  absl::Uniform<int>(absl::IntervalOpenOpen, *gen, 0, 100);
-
-  // With *generator as an R-value reference.
-  absl::Uniform<int>(URBG(), 0, 100);
-  absl::Uniform<double>(URBG(), 0.0, 1.0);
-}
-
-template <typename URBG>
-void TestExponential(URBG* gen) {
-  absl::Exponential<float>(*gen);
-  absl::Exponential<double>(*gen);
-  absl::Exponential<double>(URBG());
-}
-
-template <typename URBG>
-void TestPoisson(URBG* gen) {
-  // [rand.dist.pois] Indicates that the std::poisson_distribution
-  // is parameterized by IntType, however MSVC does not allow 8-bit
-  // types.
-  absl::Poisson<int>(*gen);
-  absl::Poisson<int16_t>(*gen);
-  absl::Poisson<uint16_t>(*gen);
-  absl::Poisson<int32_t>(*gen);
-  absl::Poisson<uint32_t>(*gen);
-  absl::Poisson<int64_t>(*gen);
-  absl::Poisson<uint64_t>(*gen);
-  absl::Poisson<uint64_t>(URBG());
-}
-
-template <typename URBG>
-void TestBernoulli(URBG* gen) {
-  absl::Bernoulli(*gen, 0.5);
-  absl::Bernoulli(*gen, 0.5);
-}
-
-template <typename URBG>
-void TestZipf(URBG* gen) {
-  absl::Zipf<int>(*gen, 100);
-  absl::Zipf<int8_t>(*gen, 100);
-  absl::Zipf<int16_t>(*gen, 100);
-  absl::Zipf<uint16_t>(*gen, 100);
-  absl::Zipf<int32_t>(*gen, 1 << 10);
-  absl::Zipf<uint32_t>(*gen, 1 << 10);
-  absl::Zipf<int64_t>(*gen, 1 << 10);
-  absl::Zipf<uint64_t>(*gen, 1 << 10);
-  absl::Zipf<uint64_t>(URBG(), 1 << 10);
-}
-
-template <typename URBG>
-void TestGaussian(URBG* gen) {
-  absl::Gaussian<float>(*gen, 1.0, 1.0);
-  absl::Gaussian<double>(*gen, 1.0, 1.0);
-  absl::Gaussian<double>(URBG(), 1.0, 1.0);
-}
-
-template <typename URBG>
-void TestLogNormal(URBG* gen) {
-  absl::LogUniform<int>(*gen, 0, 100);
-  absl::LogUniform<int8_t>(*gen, 0, 100);
-  absl::LogUniform<int16_t>(*gen, 0, 100);
-  absl::LogUniform<uint16_t>(*gen, 0, 100);
-  absl::LogUniform<int32_t>(*gen, 0, 1 << 10);
-  absl::LogUniform<uint32_t>(*gen, 0, 1 << 10);
-  absl::LogUniform<int64_t>(*gen, 0, 1 << 10);
-  absl::LogUniform<uint64_t>(*gen, 0, 1 << 10);
-  absl::LogUniform<uint64_t>(URBG(), 0, 1 << 10);
-}
-
-template <typename URBG>
-void CompatibilityTest() {
-  URBG gen;
-
-  TestUniform(&gen);
-  TestExponential(&gen);
-  TestPoisson(&gen);
-  TestBernoulli(&gen);
-  TestZipf(&gen);
-  TestGaussian(&gen);
-  TestLogNormal(&gen);
-}
-
-TEST(std_mt19937_64, Compatibility) {
-  // Validate with std::mt19937_64
-  CompatibilityTest<std::mt19937_64>();
-}
-
-TEST(BitGen, Compatibility) {
-  // Validate with absl::BitGen
-  CompatibilityTest<absl::BitGen>();
-}
-
-TEST(InsecureBitGen, Compatibility) {
-  // Validate with absl::InsecureBitGen
-  CompatibilityTest<absl::InsecureBitGen>();
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/BUILD.bazel b/third_party/abseil_cpp/absl/random/internal/BUILD.bazel
deleted file mode 100644
index 8485e28b01..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/BUILD.bazel
+++ /dev/null
@@ -1,730 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
-
-# Internal-only implementation classes for Abseil Random
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_RANDOM_RANDEN_COPTS",
-    "ABSL_TEST_COPTS",
-    "absl_random_randen_copts_init",
-)
-
-package(default_visibility = [
-    "//absl/random:__pkg__",
-])
-
-licenses(["notice"])
-
-cc_library(
-    name = "traits",
-    hdrs = ["traits.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = ["//absl/base:config"],
-)
-
-cc_library(
-    name = "distribution_caller",
-    hdrs = ["distribution_caller.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:fast_type_id",
-        "//absl/utility",
-    ],
-)
-
-cc_library(
-    name = "fast_uniform_bits",
-    hdrs = [
-        "fast_uniform_bits.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_library(
-    name = "seed_material",
-    srcs = [
-        "seed_material.cc",
-    ],
-    hdrs = [
-        "seed_material.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS + select({
-        "//absl:windows": ["-DEFAULTLIB:bcrypt.lib"],
-        "//conditions:default": [],
-    }),
-    deps = [
-        ":fast_uniform_bits",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/strings",
-        "//absl/types:optional",
-        "//absl/types:span",
-    ],
-)
-
-cc_library(
-    name = "pool_urbg",
-    srcs = [
-        "pool_urbg.cc",
-    ],
-    hdrs = [
-        "pool_urbg.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = select({
-        "//absl:windows": [],
-        "//absl:wasm": [],
-        "//conditions:default": ["-pthread"],
-    }) + ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":randen",
-        ":seed_material",
-        ":traits",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:endian",
-        "//absl/base:raw_logging_internal",
-        "//absl/random:seed_gen_exception",
-        "//absl/types:span",
-    ],
-)
-
-cc_library(
-    name = "explicit_seed_seq",
-    testonly = 1,
-    hdrs = [
-        "explicit_seed_seq.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = ["//absl/base:config"],
-)
-
-cc_library(
-    name = "sequence_urbg",
-    testonly = 1,
-    hdrs = [
-        "sequence_urbg.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = ["//absl/base:config"],
-)
-
-cc_library(
-    name = "salted_seed_seq",
-    hdrs = [
-        "salted_seed_seq.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":seed_material",
-        "//absl/container:inlined_vector",
-        "//absl/meta:type_traits",
-        "//absl/types:optional",
-        "//absl/types:span",
-    ],
-)
-
-cc_library(
-    name = "iostream_state_saver",
-    hdrs = ["iostream_state_saver.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/meta:type_traits",
-        "//absl/numeric:int128",
-    ],
-)
-
-cc_library(
-    name = "generate_real",
-    hdrs = [
-        "generate_real.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":fastmath",
-        ":traits",
-        "//absl/base:bits",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_library(
-    name = "fastmath",
-    hdrs = [
-        "fastmath.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = ["//absl/base:bits"],
-)
-
-cc_library(
-    name = "wide_multiply",
-    hdrs = ["wide_multiply.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":traits",
-        "//absl/base:bits",
-        "//absl/base:config",
-        "//absl/numeric:int128",
-    ],
-)
-
-cc_library(
-    name = "nonsecure_base",
-    hdrs = ["nonsecure_base.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":pool_urbg",
-        ":salted_seed_seq",
-        ":seed_material",
-        "//absl/base:core_headers",
-        "//absl/meta:type_traits",
-        "//absl/types:optional",
-        "//absl/types:span",
-    ],
-)
-
-cc_library(
-    name = "pcg_engine",
-    hdrs = ["pcg_engine.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":fastmath",
-        ":iostream_state_saver",
-        "//absl/base:config",
-        "//absl/meta:type_traits",
-        "//absl/numeric:int128",
-    ],
-)
-
-cc_library(
-    name = "randen_engine",
-    hdrs = ["randen_engine.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":iostream_state_saver",
-        ":randen",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_library(
-    name = "platform",
-    srcs = [
-        "randen_round_keys.cc",
-    ],
-    hdrs = [
-        "randen_traits.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    textual_hdrs = [
-        "platform.h",
-    ],
-    deps = ["//absl/base:config"],
-)
-
-cc_library(
-    name = "randen",
-    srcs = [
-        "randen.cc",
-    ],
-    hdrs = [
-        "randen.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":platform",
-        ":randen_hwaes",
-        ":randen_slow",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_library(
-    name = "randen_slow",
-    srcs = ["randen_slow.cc"],
-    hdrs = ["randen_slow.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":platform",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-    ],
-)
-
-absl_random_randen_copts_init()
-
-cc_library(
-    name = "randen_hwaes",
-    srcs = [
-        "randen_detect.cc",
-    ],
-    hdrs = [
-        "randen_detect.h",
-        "randen_hwaes.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":platform",
-        ":randen_hwaes_impl",
-        "//absl/base:config",
-    ],
-)
-
-# build with --save_temps to see assembly language output.
-cc_library(
-    name = "randen_hwaes_impl",
-    srcs = [
-        "randen_hwaes.cc",
-        "randen_hwaes.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS + ABSL_RANDOM_RANDEN_COPTS + select({
-        "//absl:windows": [],
-        "//conditions:default": ["-Wno-pass-failed"],
-    }),
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":platform",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-    ],
-)
-
-cc_binary(
-    name = "gaussian_distribution_gentables",
-    srcs = [
-        "gaussian_distribution_gentables.cc",
-    ],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:core_headers",
-        "//absl/random:distributions",
-    ],
-)
-
-cc_library(
-    name = "distribution_test_util",
-    testonly = 1,
-    srcs = [
-        "chi_square.cc",
-        "distribution_test_util.cc",
-    ],
-    hdrs = [
-        "chi_square.h",
-        "distribution_test_util.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/strings",
-        "//absl/strings:str_format",
-        "//absl/types:span",
-    ],
-)
-
-# Common tags for tests, etc.
-ABSL_RANDOM_NONPORTABLE_TAGS = [
-    "no_test_android_arm",
-    "no_test_android_arm64",
-    "no_test_android_x86",
-    "no_test_darwin_x86_64",
-    "no_test_ios_x86_64",
-    "no_test_loonix",
-    "no_test_msvc_x64",
-    "no_test_wasm",
-]
-
-cc_test(
-    name = "traits_test",
-    size = "small",
-    srcs = ["traits_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":traits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "generate_real_test",
-    size = "small",
-    srcs = [
-        "generate_real_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":generate_real",
-        "//absl/base:bits",
-        "//absl/flags:flag",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "distribution_test_util_test",
-    size = "small",
-    srcs = ["distribution_test_util_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distribution_test_util",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "fastmath_test",
-    size = "small",
-    srcs = ["fastmath_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":fastmath",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "explicit_seed_seq_test",
-    size = "small",
-    srcs = ["explicit_seed_seq_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":explicit_seed_seq",
-        "//absl/random:seed_sequences",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "salted_seed_seq_test",
-    size = "small",
-    srcs = ["salted_seed_seq_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":salted_seed_seq",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "chi_square_test",
-    size = "small",
-    srcs = [
-        "chi_square_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":distribution_test_util",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "fast_uniform_bits_test",
-    size = "small",
-    srcs = [
-        "fast_uniform_bits_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":fast_uniform_bits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "mock_helpers",
-    hdrs = ["mock_helpers.h"],
-    deps = [
-        "//absl/base:fast_type_id",
-        "//absl/types:optional",
-    ],
-)
-
-cc_library(
-    name = "mock_overload_set",
-    testonly = 1,
-    hdrs = ["mock_overload_set.h"],
-    deps = [
-        ":mock_helpers",
-        "//absl/random:mocking_bit_gen",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "nonsecure_base_test",
-    size = "small",
-    srcs = [
-        "nonsecure_base_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":nonsecure_base",
-        "//absl/random",
-        "//absl/random:distributions",
-        "//absl/random:seed_sequences",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "seed_material_test",
-    size = "small",
-    srcs = ["seed_material_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":seed_material",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "pool_urbg_test",
-    size = "small",
-    srcs = [
-        "pool_urbg_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":pool_urbg",
-        "//absl/meta:type_traits",
-        "//absl/types:span",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "pcg_engine_test",
-    size = "medium",  # Trying to measure accuracy.
-    srcs = ["pcg_engine_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    flaky = 1,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":explicit_seed_seq",
-        ":pcg_engine",
-        "//absl/time",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "randen_engine_test",
-    size = "medium",
-    srcs = [
-        "randen_engine_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":explicit_seed_seq",
-        ":randen_engine",
-        "//absl/base:raw_logging_internal",
-        "//absl/strings",
-        "//absl/time",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "randen_test",
-    size = "small",
-    srcs = ["randen_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":randen",
-        "//absl/meta:type_traits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "randen_slow_test",
-    size = "small",
-    srcs = ["randen_slow_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":platform",
-        ":randen_slow",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "randen_hwaes_test",
-    size = "small",
-    srcs = ["randen_hwaes_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ABSL_RANDOM_NONPORTABLE_TAGS,
-    deps = [
-        ":platform",
-        ":randen_hwaes",
-        ":randen_hwaes_impl",  # build_cleaner: keep
-        "//absl/base:raw_logging_internal",
-        "//absl/strings:str_format",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "wide_multiply_test",
-    size = "small",
-    srcs = ["wide_multiply_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":wide_multiply",
-        "//absl/base:bits",
-        "//absl/numeric:int128",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "nanobenchmark",
-    srcs = ["nanobenchmark.cc"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    textual_hdrs = ["nanobenchmark.h"],
-    deps = [
-        ":platform",
-        ":randen_engine",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_library(
-    name = "uniform_helper",
-    hdrs = ["uniform_helper.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":traits",
-        "//absl/base:config",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_test(
-    name = "nanobenchmark_test",
-    size = "small",
-    srcs = ["nanobenchmark_test.cc"],
-    flaky = 1,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = [
-        "benchmark",
-        "no_test_ios_x86_64",
-        "no_test_loonix",  # Crashing.
-    ],
-    deps = [
-        ":nanobenchmark",
-        "//absl/base:raw_logging_internal",
-        "//absl/strings",
-    ],
-)
-
-cc_test(
-    name = "randen_benchmarks",
-    size = "medium",
-    timeout = "long",
-    srcs = ["randen_benchmarks.cc"],
-    copts = ABSL_TEST_COPTS + ABSL_RANDOM_RANDEN_COPTS,
-    flaky = 1,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ABSL_RANDOM_NONPORTABLE_TAGS + ["benchmark"],
-    deps = [
-        ":nanobenchmark",
-        ":platform",
-        ":randen",
-        ":randen_engine",
-        ":randen_hwaes",
-        ":randen_hwaes_impl",
-        ":randen_slow",
-        "//absl/base:raw_logging_internal",
-        "//absl/strings",
-    ],
-)
-
-cc_test(
-    name = "iostream_state_saver_test",
-    size = "small",
-    srcs = ["iostream_state_saver_test.cc"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":iostream_state_saver",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "uniform_helper_test",
-    size = "small",
-    srcs = ["uniform_helper_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":uniform_helper",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/random/internal/chi_square.cc b/third_party/abseil_cpp/absl/random/internal/chi_square.cc
deleted file mode 100644
index 640d48cea6..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/chi_square.cc
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/chi_square.h"
-
-#include <cmath>
-
-#include "absl/random/internal/distribution_test_util.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-namespace {
-
-#if defined(__EMSCRIPTEN__)
-// Workaround __EMSCRIPTEN__ error: llvm_fma_f64 not found.
-inline double fma(double x, double y, double z) {
-  return (x * y) + z;
-}
-#endif
-
-// Use Horner's method to evaluate a polynomial.
-template <typename T, unsigned N>
-inline T EvaluatePolynomial(T x, const T (&poly)[N]) {
-#if !defined(__EMSCRIPTEN__)
-  using std::fma;
-#endif
-  T p = poly[N - 1];
-  for (unsigned i = 2; i <= N; i++) {
-    p = fma(p, x, poly[N - i]);
-  }
-  return p;
-}
-
-static constexpr int kLargeDOF = 150;
-
-// Returns the probability of a normal z-value.
-//
-// Adapted from the POZ function in:
-//     Ibbetson D, Algorithm 209
-//     Collected Algorithms of the CACM 1963 p. 616
-//
-double POZ(double z) {
-  static constexpr double kP1[] = {
-      0.797884560593,  -0.531923007300, 0.319152932694,
-      -0.151968751364, 0.059054035642,  -0.019198292004,
-      0.005198775019,  -0.001075204047, 0.000124818987,
-  };
-  static constexpr double kP2[] = {
-      0.999936657524,  0.000535310849,  -0.002141268741, 0.005353579108,
-      -0.009279453341, 0.011630447319,  -0.010557625006, 0.006549791214,
-      -0.002034254874, -0.000794620820, 0.001390604284,  -0.000676904986,
-      -0.000019538132, 0.000152529290,  -0.000045255659,
-  };
-
-  const double kZMax = 6.0;  // Maximum meaningful z-value.
-  if (z == 0.0) {
-    return 0.5;
-  }
-  double x;
-  double y = 0.5 * std::fabs(z);
-  if (y >= (kZMax * 0.5)) {
-    x = 1.0;
-  } else if (y < 1.0) {
-    double w = y * y;
-    x = EvaluatePolynomial(w, kP1) * y * 2.0;
-  } else {
-    y -= 2.0;
-    x = EvaluatePolynomial(y, kP2);
-  }
-  return z > 0.0 ? ((x + 1.0) * 0.5) : ((1.0 - x) * 0.5);
-}
-
-// Approximates the survival function of the normal distribution.
-//
-// Algorithm 26.2.18, from:
-// [Abramowitz and Stegun, Handbook of Mathematical Functions,p.932]
-// http://people.math.sfu.ca/~cbm/aands/abramowitz_and_stegun.pdf
-//
-double normal_survival(double z) {
-  // Maybe replace with the alternate formulation.
-  // 0.5 * erfc((x - mean)/(sqrt(2) * sigma))
-  static constexpr double kR[] = {
-      1.0, 0.196854, 0.115194, 0.000344, 0.019527,
-  };
-  double r = EvaluatePolynomial(z, kR);
-  r *= r;
-  return 0.5 / (r * r);
-}
-
-}  // namespace
-
-// Calculates the critical chi-square value given degrees-of-freedom and a
-// p-value, usually using bisection. Also known by the name CRITCHI.
-double ChiSquareValue(int dof, double p) {
-  static constexpr double kChiEpsilon =
-      0.000001;  // Accuracy of the approximation.
-  static constexpr double kChiMax =
-      99999.0;  // Maximum chi-squared value.
-
-  const double p_value = 1.0 - p;
-  if (dof < 1 || p_value > 1.0) {
-    return 0.0;
-  }
-
-  if (dof > kLargeDOF) {
-    // For large degrees of freedom, use the normal approximation by
-    //     Wilson, E. B. and Hilferty, M. M. (1931)
-    //                     chi^2 - mean
-    //                Z = --------------
-    //                        stddev
-    const double z = InverseNormalSurvival(p_value);
-    const double mean = 1 - 2.0 / (9 * dof);
-    const double variance = 2.0 / (9 * dof);
-    // Cannot use this method if the variance is 0.
-    if (variance != 0) {
-      return std::pow(z * std::sqrt(variance) + mean, 3.0) * dof;
-    }
-  }
-
-  if (p_value <= 0.0) return kChiMax;
-
-  // Otherwise search for the p value by bisection
-  double min_chisq = 0.0;
-  double max_chisq = kChiMax;
-  double current = dof / std::sqrt(p_value);
-  while ((max_chisq - min_chisq) > kChiEpsilon) {
-    if (ChiSquarePValue(current, dof) < p_value) {
-      max_chisq = current;
-    } else {
-      min_chisq = current;
-    }
-    current = (max_chisq + min_chisq) * 0.5;
-  }
-  return current;
-}
-
-// Calculates the p-value (probability) of a given chi-square value
-// and degrees of freedom.
-//
-// Adapted from the POCHISQ function from:
-//     Hill, I. D. and Pike, M. C.  Algorithm 299
-//     Collected Algorithms of the CACM 1963 p. 243
-//
-double ChiSquarePValue(double chi_square, int dof) {
-  static constexpr double kLogSqrtPi =
-      0.5723649429247000870717135;  // Log[Sqrt[Pi]]
-  static constexpr double kInverseSqrtPi =
-      0.5641895835477562869480795;  // 1/(Sqrt[Pi])
-
-  // For large degrees of freedom, use the normal approximation by
-  //     Wilson, E. B. and Hilferty, M. M. (1931)
-  // Via Wikipedia:
-  //   By the Central Limit Theorem, because the chi-square distribution is the
-  //   sum of k independent random variables with finite mean and variance, it
-  //   converges to a normal distribution for large k.
-  if (dof > kLargeDOF) {
-    // Re-scale everything.
-    const double chi_square_scaled = std::pow(chi_square / dof, 1.0 / 3);
-    const double mean = 1 - 2.0 / (9 * dof);
-    const double variance = 2.0 / (9 * dof);
-    // If variance is 0, this method cannot be used.
-    if (variance != 0) {
-      const double z = (chi_square_scaled - mean) / std::sqrt(variance);
-      if (z > 0) {
-        return normal_survival(z);
-      } else if (z < 0) {
-        return 1.0 - normal_survival(-z);
-      } else {
-        return 0.5;
-      }
-    }
-  }
-
-  // The chi square function is >= 0 for any degrees of freedom.
-  // In other words, probability that the chi square function >= 0 is 1.
-  if (chi_square <= 0.0) return 1.0;
-
-  // If the degrees of freedom is zero, the chi square function is always 0 by
-  // definition. In other words, the probability that the chi square function
-  // is > 0 is zero (chi square values <= 0 have been filtered above).
-  if (dof < 1) return 0;
-
-  auto capped_exp = [](double x) { return x < -20 ? 0.0 : std::exp(x); };
-  static constexpr double kBigX = 20;
-
-  double a = 0.5 * chi_square;
-  const bool even = !(dof & 1);  // True if dof is an even number.
-  const double y = capped_exp(-a);
-  double s = even ? y : (2.0 * POZ(-std::sqrt(chi_square)));
-
-  if (dof <= 2) {
-    return s;
-  }
-
-  chi_square = 0.5 * (dof - 1.0);
-  double z = (even ? 1.0 : 0.5);
-  if (a > kBigX) {
-    double e = (even ? 0.0 : kLogSqrtPi);
-    double c = std::log(a);
-    while (z <= chi_square) {
-      e = std::log(z) + e;
-      s += capped_exp(c * z - a - e);
-      z += 1.0;
-    }
-    return s;
-  }
-
-  double e = (even ? 1.0 : (kInverseSqrtPi / std::sqrt(a)));
-  double c = 0.0;
-  while (z <= chi_square) {
-    e = e * (a / z);
-    c = c + e;
-    z += 1.0;
-  }
-  return c * y + s;
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/chi_square.h b/third_party/abseil_cpp/absl/random/internal/chi_square.h
deleted file mode 100644
index 07f4fbe522..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/chi_square.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
-#define ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
-
-// The chi-square statistic.
-//
-// Useful for evaluating if `D` independent random variables are behaving as
-// expected, or if two distributions are similar.  (`D` is the degrees of
-// freedom).
-//
-// Each bucket should have an expected count of 10 or more for the chi square to
-// be meaningful.
-
-#include <cassert>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-constexpr const char kChiSquared[] = "chi-squared";
-
-// Returns the measured chi square value, using a single expected value.  This
-// assumes that the values in [begin, end) are uniformly distributed.
-template <typename Iterator>
-double ChiSquareWithExpected(Iterator begin, Iterator end, double expected) {
-  // Compute the sum and the number of buckets.
-  assert(expected >= 10);  // require at least 10 samples per bucket.
-  double chi_square = 0;
-  for (auto it = begin; it != end; it++) {
-    double d = static_cast<double>(*it) - expected;
-    chi_square += d * d;
-  }
-  chi_square = chi_square / expected;
-  return chi_square;
-}
-
-// Returns the measured chi square value, taking the actual value of each bucket
-// from the first set of iterators, and the expected value of each bucket from
-// the second set of iterators.
-template <typename Iterator, typename Expected>
-double ChiSquare(Iterator it, Iterator end, Expected eit, Expected eend) {
-  double chi_square = 0;
-  for (; it != end && eit != eend; ++it, ++eit) {
-    if (*it > 0) {
-      assert(*eit > 0);
-    }
-    double e = static_cast<double>(*eit);
-    double d = static_cast<double>(*it - *eit);
-    if (d != 0) {
-      assert(e > 0);
-      chi_square += (d * d) / e;
-    }
-  }
-  assert(it == end && eit == eend);
-  return chi_square;
-}
-
-// ======================================================================
-// The following methods can be used for an arbitrary significance level.
-//
-
-// Calculates critical chi-square values to produce the given p-value using a
-// bisection search for a value within epsilon, relying on the monotonicity of
-// ChiSquarePValue().
-double ChiSquareValue(int dof, double p);
-
-// Calculates the p-value (probability) of a given chi-square value.
-double ChiSquarePValue(double chi_square, int dof);
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/chi_square_test.cc b/third_party/abseil_cpp/absl/random/internal/chi_square_test.cc
deleted file mode 100644
index 5025defac1..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/chi_square_test.cc
+++ /dev/null
@@ -1,365 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/chi_square.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <iterator>
-#include <numeric>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/macros.h"
-
-using absl::random_internal::ChiSquare;
-using absl::random_internal::ChiSquarePValue;
-using absl::random_internal::ChiSquareValue;
-using absl::random_internal::ChiSquareWithExpected;
-
-namespace {
-
-TEST(ChiSquare, Value) {
-  struct {
-    int line;
-    double chi_square;
-    int df;
-    double confidence;
-  } const specs[] = {
-      // Testing lookup at 1% confidence
-      {__LINE__, 0, 0, 0.01},
-      {__LINE__, 0.00016, 1, 0.01},
-      {__LINE__, 1.64650, 8, 0.01},
-      {__LINE__, 5.81221, 16, 0.01},
-      {__LINE__, 156.4319, 200, 0.01},
-      {__LINE__, 1121.3784, 1234, 0.01},
-      {__LINE__, 53557.1629, 54321, 0.01},
-      {__LINE__, 651662.6647, 654321, 0.01},
-
-      // Testing lookup at 99% confidence
-      {__LINE__, 0, 0, 0.99},
-      {__LINE__, 6.635, 1, 0.99},
-      {__LINE__, 20.090, 8, 0.99},
-      {__LINE__, 32.000, 16, 0.99},
-      {__LINE__, 249.4456, 200, 0.99},
-      {__LINE__, 1131.1573, 1023, 0.99},
-      {__LINE__, 1352.5038, 1234, 0.99},
-      {__LINE__, 55090.7356, 54321, 0.99},
-      {__LINE__, 656985.1514, 654321, 0.99},
-
-      // Testing lookup at 99.9% confidence
-      {__LINE__, 16.2659, 3, 0.999},
-      {__LINE__, 22.4580, 6, 0.999},
-      {__LINE__, 267.5409, 200, 0.999},
-      {__LINE__, 1168.5033, 1023, 0.999},
-      {__LINE__, 55345.1741, 54321, 0.999},
-      {__LINE__, 657861.7284, 654321, 0.999},
-      {__LINE__, 51.1772, 24, 0.999},
-      {__LINE__, 59.7003, 30, 0.999},
-      {__LINE__, 37.6984, 15, 0.999},
-      {__LINE__, 29.5898, 10, 0.999},
-      {__LINE__, 27.8776, 9, 0.999},
-
-      // Testing lookup at random confidences
-      {__LINE__, 0.000157088, 1, 0.01},
-      {__LINE__, 5.31852, 2, 0.93},
-      {__LINE__, 1.92256, 4, 0.25},
-      {__LINE__, 10.7709, 13, 0.37},
-      {__LINE__, 26.2514, 17, 0.93},
-      {__LINE__, 36.4799, 29, 0.84},
-      {__LINE__, 25.818, 31, 0.27},
-      {__LINE__, 63.3346, 64, 0.50},
-      {__LINE__, 196.211, 128, 0.9999},
-      {__LINE__, 215.21, 243, 0.10},
-      {__LINE__, 285.393, 256, 0.90},
-      {__LINE__, 984.504, 1024, 0.1923},
-      {__LINE__, 2043.85, 2048, 0.4783},
-      {__LINE__, 48004.6, 48273, 0.194},
-  };
-  for (const auto& spec : specs) {
-    SCOPED_TRACE(spec.line);
-    // Verify all values are have at most a 1% relative error.
-    const double val = ChiSquareValue(spec.df, spec.confidence);
-    const double err = std::max(5e-6, spec.chi_square / 5e3);  // 1 part in 5000
-    EXPECT_NEAR(spec.chi_square, val, err) << spec.line;
-  }
-
-  // Relaxed test for extreme values, from
-  //  http://www.ciphersbyritter.com/JAVASCRP/NORMCHIK.HTM#ChiSquare
-  EXPECT_NEAR(49.2680, ChiSquareValue(100, 1e-6), 5);  // 0.000'005 mark
-  EXPECT_NEAR(123.499, ChiSquareValue(200, 1e-6), 5);  // 0.000'005 mark
-
-  EXPECT_NEAR(149.449, ChiSquareValue(100, 0.999), 0.01);
-  EXPECT_NEAR(161.318, ChiSquareValue(100, 0.9999), 0.01);
-  EXPECT_NEAR(172.098, ChiSquareValue(100, 0.99999), 0.01);
-
-  EXPECT_NEAR(381.426, ChiSquareValue(300, 0.999), 0.05);
-  EXPECT_NEAR(399.756, ChiSquareValue(300, 0.9999), 0.1);
-  EXPECT_NEAR(416.126, ChiSquareValue(300, 0.99999), 0.2);
-}
-
-TEST(ChiSquareTest, PValue) {
-  struct {
-    int line;
-    double pval;
-    double chi_square;
-    int df;
-  } static const specs[] = {
-      {__LINE__, 1, 0, 0},
-      {__LINE__, 0, 0.001, 0},
-      {__LINE__, 1.000, 0, 453},
-      {__LINE__, 0.134471, 7972.52, 7834},
-      {__LINE__, 0.203922, 28.32, 23},
-      {__LINE__, 0.737171, 48274, 48472},
-      {__LINE__, 0.444146, 583.1234, 579},
-      {__LINE__, 0.294814, 138.2, 130},
-      {__LINE__, 0.0816532, 12.63, 7},
-      {__LINE__, 0, 682.32, 67},
-      {__LINE__, 0.49405, 999, 999},
-      {__LINE__, 1.000, 0, 9999},
-      {__LINE__, 0.997477, 0.00001, 1},
-      {__LINE__, 0, 5823.21, 5040},
-  };
-  for (const auto& spec : specs) {
-    SCOPED_TRACE(spec.line);
-    const double pval = ChiSquarePValue(spec.chi_square, spec.df);
-    EXPECT_NEAR(spec.pval, pval, 1e-3);
-  }
-}
-
-TEST(ChiSquareTest, CalcChiSquare) {
-  struct {
-    int line;
-    std::vector<int> expected;
-    std::vector<int> actual;
-  } const specs[] = {
-      {__LINE__,
-       {56, 234, 76, 1, 546, 1, 87, 345, 1, 234},
-       {2, 132, 4, 43, 234, 8, 345, 8, 236, 56}},
-      {__LINE__,
-       {123, 36, 234, 367, 345, 2, 456, 567, 234, 567},
-       {123, 56, 2345, 8, 345, 8, 2345, 23, 48, 267}},
-      {__LINE__,
-       {123, 234, 345, 456, 567, 678, 789, 890, 98, 76},
-       {123, 234, 345, 456, 567, 678, 789, 890, 98, 76}},
-      {__LINE__, {3, 675, 23, 86, 2, 8, 2}, {456, 675, 23, 86, 23, 65, 2}},
-      {__LINE__, {1}, {23}},
-  };
-  for (const auto& spec : specs) {
-    SCOPED_TRACE(spec.line);
-    double chi_square = 0;
-    for (int i = 0; i < spec.expected.size(); ++i) {
-      const double diff = spec.actual[i] - spec.expected[i];
-      chi_square += (diff * diff) / spec.expected[i];
-    }
-    EXPECT_NEAR(chi_square,
-                ChiSquare(std::begin(spec.actual), std::end(spec.actual),
-                          std::begin(spec.expected), std::end(spec.expected)),
-                1e-5);
-  }
-}
-
-TEST(ChiSquareTest, CalcChiSquareInt64) {
-  const int64_t data[3] = {910293487, 910292491, 910216780};
-  // $ python -c "import scipy.stats
-  // > print scipy.stats.chisquare([910293487, 910292491, 910216780])[0]"
-  // 4.25410123524
-  double sum = std::accumulate(std::begin(data), std::end(data), double{0});
-  size_t n = std::distance(std::begin(data), std::end(data));
-  double a = ChiSquareWithExpected(std::begin(data), std::end(data), sum / n);
-  EXPECT_NEAR(4.254101, a, 1e-6);
-
-  // ... Or with known values.
-  double b =
-      ChiSquareWithExpected(std::begin(data), std::end(data), 910267586.0);
-  EXPECT_NEAR(4.254101, b, 1e-6);
-}
-
-TEST(ChiSquareTest, TableData) {
-  // Test data from
-  // http://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm
-  //    0.90      0.95     0.975      0.99     0.999
-  const double data[100][5] = {
-      /* 1*/ {2.706, 3.841, 5.024, 6.635, 10.828},
-      /* 2*/ {4.605, 5.991, 7.378, 9.210, 13.816},
-      /* 3*/ {6.251, 7.815, 9.348, 11.345, 16.266},
-      /* 4*/ {7.779, 9.488, 11.143, 13.277, 18.467},
-      /* 5*/ {9.236, 11.070, 12.833, 15.086, 20.515},
-      /* 6*/ {10.645, 12.592, 14.449, 16.812, 22.458},
-      /* 7*/ {12.017, 14.067, 16.013, 18.475, 24.322},
-      /* 8*/ {13.362, 15.507, 17.535, 20.090, 26.125},
-      /* 9*/ {14.684, 16.919, 19.023, 21.666, 27.877},
-      /*10*/ {15.987, 18.307, 20.483, 23.209, 29.588},
-      /*11*/ {17.275, 19.675, 21.920, 24.725, 31.264},
-      /*12*/ {18.549, 21.026, 23.337, 26.217, 32.910},
-      /*13*/ {19.812, 22.362, 24.736, 27.688, 34.528},
-      /*14*/ {21.064, 23.685, 26.119, 29.141, 36.123},
-      /*15*/ {22.307, 24.996, 27.488, 30.578, 37.697},
-      /*16*/ {23.542, 26.296, 28.845, 32.000, 39.252},
-      /*17*/ {24.769, 27.587, 30.191, 33.409, 40.790},
-      /*18*/ {25.989, 28.869, 31.526, 34.805, 42.312},
-      /*19*/ {27.204, 30.144, 32.852, 36.191, 43.820},
-      /*20*/ {28.412, 31.410, 34.170, 37.566, 45.315},
-      /*21*/ {29.615, 32.671, 35.479, 38.932, 46.797},
-      /*22*/ {30.813, 33.924, 36.781, 40.289, 48.268},
-      /*23*/ {32.007, 35.172, 38.076, 41.638, 49.728},
-      /*24*/ {33.196, 36.415, 39.364, 42.980, 51.179},
-      /*25*/ {34.382, 37.652, 40.646, 44.314, 52.620},
-      /*26*/ {35.563, 38.885, 41.923, 45.642, 54.052},
-      /*27*/ {36.741, 40.113, 43.195, 46.963, 55.476},
-      /*28*/ {37.916, 41.337, 44.461, 48.278, 56.892},
-      /*29*/ {39.087, 42.557, 45.722, 49.588, 58.301},
-      /*30*/ {40.256, 43.773, 46.979, 50.892, 59.703},
-      /*31*/ {41.422, 44.985, 48.232, 52.191, 61.098},
-      /*32*/ {42.585, 46.194, 49.480, 53.486, 62.487},
-      /*33*/ {43.745, 47.400, 50.725, 54.776, 63.870},
-      /*34*/ {44.903, 48.602, 51.966, 56.061, 65.247},
-      /*35*/ {46.059, 49.802, 53.203, 57.342, 66.619},
-      /*36*/ {47.212, 50.998, 54.437, 58.619, 67.985},
-      /*37*/ {48.363, 52.192, 55.668, 59.893, 69.347},
-      /*38*/ {49.513, 53.384, 56.896, 61.162, 70.703},
-      /*39*/ {50.660, 54.572, 58.120, 62.428, 72.055},
-      /*40*/ {51.805, 55.758, 59.342, 63.691, 73.402},
-      /*41*/ {52.949, 56.942, 60.561, 64.950, 74.745},
-      /*42*/ {54.090, 58.124, 61.777, 66.206, 76.084},
-      /*43*/ {55.230, 59.304, 62.990, 67.459, 77.419},
-      /*44*/ {56.369, 60.481, 64.201, 68.710, 78.750},
-      /*45*/ {57.505, 61.656, 65.410, 69.957, 80.077},
-      /*46*/ {58.641, 62.830, 66.617, 71.201, 81.400},
-      /*47*/ {59.774, 64.001, 67.821, 72.443, 82.720},
-      /*48*/ {60.907, 65.171, 69.023, 73.683, 84.037},
-      /*49*/ {62.038, 66.339, 70.222, 74.919, 85.351},
-      /*50*/ {63.167, 67.505, 71.420, 76.154, 86.661},
-      /*51*/ {64.295, 68.669, 72.616, 77.386, 87.968},
-      /*52*/ {65.422, 69.832, 73.810, 78.616, 89.272},
-      /*53*/ {66.548, 70.993, 75.002, 79.843, 90.573},
-      /*54*/ {67.673, 72.153, 76.192, 81.069, 91.872},
-      /*55*/ {68.796, 73.311, 77.380, 82.292, 93.168},
-      /*56*/ {69.919, 74.468, 78.567, 83.513, 94.461},
-      /*57*/ {71.040, 75.624, 79.752, 84.733, 95.751},
-      /*58*/ {72.160, 76.778, 80.936, 85.950, 97.039},
-      /*59*/ {73.279, 77.931, 82.117, 87.166, 98.324},
-      /*60*/ {74.397, 79.082, 83.298, 88.379, 99.607},
-      /*61*/ {75.514, 80.232, 84.476, 89.591, 100.888},
-      /*62*/ {76.630, 81.381, 85.654, 90.802, 102.166},
-      /*63*/ {77.745, 82.529, 86.830, 92.010, 103.442},
-      /*64*/ {78.860, 83.675, 88.004, 93.217, 104.716},
-      /*65*/ {79.973, 84.821, 89.177, 94.422, 105.988},
-      /*66*/ {81.085, 85.965, 90.349, 95.626, 107.258},
-      /*67*/ {82.197, 87.108, 91.519, 96.828, 108.526},
-      /*68*/ {83.308, 88.250, 92.689, 98.028, 109.791},
-      /*69*/ {84.418, 89.391, 93.856, 99.228, 111.055},
-      /*70*/ {85.527, 90.531, 95.023, 100.425, 112.317},
-      /*71*/ {86.635, 91.670, 96.189, 101.621, 113.577},
-      /*72*/ {87.743, 92.808, 97.353, 102.816, 114.835},
-      /*73*/ {88.850, 93.945, 98.516, 104.010, 116.092},
-      /*74*/ {89.956, 95.081, 99.678, 105.202, 117.346},
-      /*75*/ {91.061, 96.217, 100.839, 106.393, 118.599},
-      /*76*/ {92.166, 97.351, 101.999, 107.583, 119.850},
-      /*77*/ {93.270, 98.484, 103.158, 108.771, 121.100},
-      /*78*/ {94.374, 99.617, 104.316, 109.958, 122.348},
-      /*79*/ {95.476, 100.749, 105.473, 111.144, 123.594},
-      /*80*/ {96.578, 101.879, 106.629, 112.329, 124.839},
-      /*81*/ {97.680, 103.010, 107.783, 113.512, 126.083},
-      /*82*/ {98.780, 104.139, 108.937, 114.695, 127.324},
-      /*83*/ {99.880, 105.267, 110.090, 115.876, 128.565},
-      /*84*/ {100.980, 106.395, 111.242, 117.057, 129.804},
-      /*85*/ {102.079, 107.522, 112.393, 118.236, 131.041},
-      /*86*/ {103.177, 108.648, 113.544, 119.414, 132.277},
-      /*87*/ {104.275, 109.773, 114.693, 120.591, 133.512},
-      /*88*/ {105.372, 110.898, 115.841, 121.767, 134.746},
-      /*89*/ {106.469, 112.022, 116.989, 122.942, 135.978},
-      /*90*/ {107.565, 113.145, 118.136, 124.116, 137.208},
-      /*91*/ {108.661, 114.268, 119.282, 125.289, 138.438},
-      /*92*/ {109.756, 115.390, 120.427, 126.462, 139.666},
-      /*93*/ {110.850, 116.511, 121.571, 127.633, 140.893},
-      /*94*/ {111.944, 117.632, 122.715, 128.803, 142.119},
-      /*95*/ {113.038, 118.752, 123.858, 129.973, 143.344},
-      /*96*/ {114.131, 119.871, 125.000, 131.141, 144.567},
-      /*97*/ {115.223, 120.990, 126.141, 132.309, 145.789},
-      /*98*/ {116.315, 122.108, 127.282, 133.476, 147.010},
-      /*99*/ {117.407, 123.225, 128.422, 134.642, 148.230},
-      /*100*/ {118.498, 124.342, 129.561, 135.807, 149.449}
-      /**/};
-
-  //    0.90      0.95     0.975      0.99     0.999
-  for (int i = 0; i < ABSL_ARRAYSIZE(data); i++) {
-    const double E = 0.0001;
-    EXPECT_NEAR(ChiSquarePValue(data[i][0], i + 1), 0.10, E)
-        << i << " " << data[i][0];
-    EXPECT_NEAR(ChiSquarePValue(data[i][1], i + 1), 0.05, E)
-        << i << " " << data[i][1];
-    EXPECT_NEAR(ChiSquarePValue(data[i][2], i + 1), 0.025, E)
-        << i << " " << data[i][2];
-    EXPECT_NEAR(ChiSquarePValue(data[i][3], i + 1), 0.01, E)
-        << i << " " << data[i][3];
-    EXPECT_NEAR(ChiSquarePValue(data[i][4], i + 1), 0.001, E)
-        << i << " " << data[i][4];
-
-    const double F = 0.1;
-    EXPECT_NEAR(ChiSquareValue(i + 1, 0.90), data[i][0], F) << i;
-    EXPECT_NEAR(ChiSquareValue(i + 1, 0.95), data[i][1], F) << i;
-    EXPECT_NEAR(ChiSquareValue(i + 1, 0.975), data[i][2], F) << i;
-    EXPECT_NEAR(ChiSquareValue(i + 1, 0.99), data[i][3], F) << i;
-    EXPECT_NEAR(ChiSquareValue(i + 1, 0.999), data[i][4], F) << i;
-  }
-}
-
-TEST(ChiSquareTest, ChiSquareTwoIterator) {
-  // Test data from http://www.stat.yale.edu/Courses/1997-98/101/chigf.htm
-  // Null-hypothesis: This data is normally distributed.
-  const int counts[10] = {6, 6, 18, 33, 38, 38, 28, 21, 9, 3};
-  const double expected[10] = {4.6,  8.8,  18.4, 30.0, 38.2,
-                               38.2, 30.0, 18.4, 8.8,  4.6};
-  double chi_square = ChiSquare(std::begin(counts), std::end(counts),
-                                std::begin(expected), std::end(expected));
-  EXPECT_NEAR(chi_square, 2.69, 0.001);
-
-  // Degrees of freedom: 10 bins. two estimated parameters. = 10 - 2 - 1.
-  const int dof = 7;
-  // The critical value of 7, 95% => 14.067 (see above test)
-  double p_value_05 = ChiSquarePValue(14.067, dof);
-  EXPECT_NEAR(p_value_05, 0.05, 0.001);  // 95%-ile p-value
-
-  double p_actual = ChiSquarePValue(chi_square, dof);
-  EXPECT_GT(p_actual, 0.05);  // Accept the null hypothesis.
-}
-
-TEST(ChiSquareTest, DiceRolls) {
-  // Assume we are testing 102 fair dice rolls.
-  // Null-hypothesis: This data is fairly distributed.
-  //
-  // The dof value of 4, @95% = 9.488 (see above test)
-  // The dof value of 5, @95% = 11.070
-  const int rolls[6] = {22, 11, 17, 14, 20, 18};
-  double sum = std::accumulate(std::begin(rolls), std::end(rolls), double{0});
-  size_t n = std::distance(std::begin(rolls), std::end(rolls));
-
-  double a = ChiSquareWithExpected(std::begin(rolls), std::end(rolls), sum / n);
-  EXPECT_NEAR(a, 4.70588, 1e-5);
-  EXPECT_LT(a, ChiSquareValue(4, 0.95));
-
-  double p_a = ChiSquarePValue(a, 4);
-  EXPECT_NEAR(p_a, 0.318828, 1e-5);  // Accept the null hypothesis.
-
-  double b = ChiSquareWithExpected(std::begin(rolls), std::end(rolls), 17.0);
-  EXPECT_NEAR(b, 4.70588, 1e-5);
-  EXPECT_LT(b, ChiSquareValue(5, 0.95));
-
-  double p_b = ChiSquarePValue(b, 5);
-  EXPECT_NEAR(p_b, 0.4528180, 1e-5);  // Accept the null hypothesis.
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/distribution_caller.h b/third_party/abseil_cpp/absl/random/internal/distribution_caller.h
deleted file mode 100644
index fc81b787eb..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/distribution_caller.h
+++ /dev/null
@@ -1,92 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
-#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
-
-#include <utility>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/fast_type_id.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// DistributionCaller provides an opportunity to overload the general
-// mechanism for calling a distribution, allowing for mock-RNG classes
-// to intercept such calls.
-template <typename URBG>
-struct DistributionCaller {
-  // SFINAE to detect whether the URBG type includes a member matching
-  // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
-  //
-  // These live inside BitGenRef so that they have friend access
-  // to MockingBitGen. (see similar methods in DistributionCaller).
-  template <template <class...> class Trait, class AlwaysVoid, class... Args>
-  struct detector : std::false_type {};
-  template <template <class...> class Trait, class... Args>
-  struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
-      : std::true_type {};
-
-  template <class T>
-  using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
-      std::declval<::absl::base_internal::FastTypeIdType>(),
-      std::declval<void*>(), std::declval<void*>()));
-
-  using HasInvokeMock = typename detector<invoke_mock_t, void, URBG>::type;
-
-  // Default implementation of distribution caller.
-  template <typename DistrT, typename... Args>
-  static typename DistrT::result_type Impl(std::false_type, URBG* urbg,
-                                           Args&&... args) {
-    DistrT dist(std::forward<Args>(args)...);
-    return dist(*urbg);
-  }
-
-  // Mock implementation of distribution caller.
-  // The underlying KeyT must match the KeyT constructed by MockOverloadSet.
-  template <typename DistrT, typename... Args>
-  static typename DistrT::result_type Impl(std::true_type, URBG* urbg,
-                                           Args&&... args) {
-    using ResultT = typename DistrT::result_type;
-    using ArgTupleT = std::tuple<absl::decay_t<Args>...>;
-    using KeyT = ResultT(DistrT, ArgTupleT);
-
-    ArgTupleT arg_tuple(std::forward<Args>(args)...);
-    ResultT result;
-    if (!urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple,
-                          &result)) {
-      auto dist = absl::make_from_tuple<DistrT>(arg_tuple);
-      result = dist(*urbg);
-    }
-    return result;
-  }
-
-  // Default implementation of distribution caller.
-  template <typename DistrT, typename... Args>
-  static typename DistrT::result_type Call(URBG* urbg, Args&&... args) {
-    return Impl<DistrT, Args...>(HasInvokeMock{}, urbg,
-                                 std::forward<Args>(args)...);
-  }
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/distribution_test_util.cc b/third_party/abseil_cpp/absl/random/internal/distribution_test_util.cc
deleted file mode 100644
index e9005658c0..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/distribution_test_util.cc
+++ /dev/null
@@ -1,418 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/distribution_test_util.h"
-
-#include <cassert>
-#include <cmath>
-#include <string>
-#include <vector>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-namespace {
-
-#if defined(__EMSCRIPTEN__)
-// Workaround __EMSCRIPTEN__ error: llvm_fma_f64 not found.
-inline double fma(double x, double y, double z) { return (x * y) + z; }
-#endif
-
-}  // namespace
-
-DistributionMoments ComputeDistributionMoments(
-    absl::Span<const double> data_points) {
-  DistributionMoments result;
-
-  // Compute m1
-  for (double x : data_points) {
-    result.n++;
-    result.mean += x;
-  }
-  result.mean /= static_cast<double>(result.n);
-
-  // Compute m2, m3, m4
-  for (double x : data_points) {
-    double v = x - result.mean;
-    result.variance += v * v;
-    result.skewness += v * v * v;
-    result.kurtosis += v * v * v * v;
-  }
-  result.variance /= static_cast<double>(result.n - 1);
-
-  result.skewness /= static_cast<double>(result.n);
-  result.skewness /= std::pow(result.variance, 1.5);
-
-  result.kurtosis /= static_cast<double>(result.n);
-  result.kurtosis /= std::pow(result.variance, 2.0);
-  return result;
-
-  // When validating the min/max count, the following confidence intervals may
-  // be of use:
-  // 3.291 * stddev = 99.9% CI
-  // 2.576 * stddev = 99% CI
-  // 1.96 * stddev  = 95% CI
-  // 1.65 * stddev  = 90% CI
-}
-
-std::ostream& operator<<(std::ostream& os, const DistributionMoments& moments) {
-  return os << absl::StrFormat("mean=%f, stddev=%f, skewness=%f, kurtosis=%f",
-                               moments.mean, std::sqrt(moments.variance),
-                               moments.skewness, moments.kurtosis);
-}
-
-double InverseNormalSurvival(double x) {
-  // inv_sf(u) = -sqrt(2) * erfinv(2u-1)
-  static constexpr double kSqrt2 = 1.4142135623730950488;
-  return -kSqrt2 * absl::random_internal::erfinv(2 * x - 1.0);
-}
-
-bool Near(absl::string_view msg, double actual, double expected, double bound) {
-  assert(bound > 0.0);
-  double delta = fabs(expected - actual);
-  if (delta < bound) {
-    return true;
-  }
-
-  std::string formatted = absl::StrCat(
-      msg, " actual=", actual, " expected=", expected, " err=", delta / bound);
-  ABSL_RAW_LOG(INFO, "%s", formatted.c_str());
-  return false;
-}
-
-// TODO(absl-team): Replace with an "ABSL_HAVE_SPECIAL_MATH" and try
-// to use std::beta().  As of this writing P0226R1 is not implemented
-// in libc++: http://libcxx.llvm.org/cxx1z_status.html
-double beta(double p, double q) {
-  // Beta(x, y) = Gamma(x) * Gamma(y) / Gamma(x+y)
-  double lbeta = std::lgamma(p) + std::lgamma(q) - std::lgamma(p + q);
-  return std::exp(lbeta);
-}
-
-// Approximation to inverse of the Error Function in double precision.
-// (http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf)
-double erfinv(double x) {
-#if !defined(__EMSCRIPTEN__)
-  using std::fma;
-#endif
-
-  double w = 0.0;
-  double p = 0.0;
-  w = -std::log((1.0 - x) * (1.0 + x));
-  if (w < 6.250000) {
-    w = w - 3.125000;
-    p = -3.6444120640178196996e-21;
-    p = fma(p, w, -1.685059138182016589e-19);
-    p = fma(p, w, 1.2858480715256400167e-18);
-    p = fma(p, w, 1.115787767802518096e-17);
-    p = fma(p, w, -1.333171662854620906e-16);
-    p = fma(p, w, 2.0972767875968561637e-17);
-    p = fma(p, w, 6.6376381343583238325e-15);
-    p = fma(p, w, -4.0545662729752068639e-14);
-    p = fma(p, w, -8.1519341976054721522e-14);
-    p = fma(p, w, 2.6335093153082322977e-12);
-    p = fma(p, w, -1.2975133253453532498e-11);
-    p = fma(p, w, -5.4154120542946279317e-11);
-    p = fma(p, w, 1.051212273321532285e-09);
-    p = fma(p, w, -4.1126339803469836976e-09);
-    p = fma(p, w, -2.9070369957882005086e-08);
-    p = fma(p, w, 4.2347877827932403518e-07);
-    p = fma(p, w, -1.3654692000834678645e-06);
-    p = fma(p, w, -1.3882523362786468719e-05);
-    p = fma(p, w, 0.0001867342080340571352);
-    p = fma(p, w, -0.00074070253416626697512);
-    p = fma(p, w, -0.0060336708714301490533);
-    p = fma(p, w, 0.24015818242558961693);
-    p = fma(p, w, 1.6536545626831027356);
-  } else if (w < 16.000000) {
-    w = std::sqrt(w) - 3.250000;
-    p = 2.2137376921775787049e-09;
-    p = fma(p, w, 9.0756561938885390979e-08);
-    p = fma(p, w, -2.7517406297064545428e-07);
-    p = fma(p, w, 1.8239629214389227755e-08);
-    p = fma(p, w, 1.5027403968909827627e-06);
-    p = fma(p, w, -4.013867526981545969e-06);
-    p = fma(p, w, 2.9234449089955446044e-06);
-    p = fma(p, w, 1.2475304481671778723e-05);
-    p = fma(p, w, -4.7318229009055733981e-05);
-    p = fma(p, w, 6.8284851459573175448e-05);
-    p = fma(p, w, 2.4031110387097893999e-05);
-    p = fma(p, w, -0.0003550375203628474796);
-    p = fma(p, w, 0.00095328937973738049703);
-    p = fma(p, w, -0.0016882755560235047313);
-    p = fma(p, w, 0.0024914420961078508066);
-    p = fma(p, w, -0.0037512085075692412107);
-    p = fma(p, w, 0.005370914553590063617);
-    p = fma(p, w, 1.0052589676941592334);
-    p = fma(p, w, 3.0838856104922207635);
-  } else {
-    w = std::sqrt(w) - 5.000000;
-    p = -2.7109920616438573243e-11;
-    p = fma(p, w, -2.5556418169965252055e-10);
-    p = fma(p, w, 1.5076572693500548083e-09);
-    p = fma(p, w, -3.7894654401267369937e-09);
-    p = fma(p, w, 7.6157012080783393804e-09);
-    p = fma(p, w, -1.4960026627149240478e-08);
-    p = fma(p, w, 2.9147953450901080826e-08);
-    p = fma(p, w, -6.7711997758452339498e-08);
-    p = fma(p, w, 2.2900482228026654717e-07);
-    p = fma(p, w, -9.9298272942317002539e-07);
-    p = fma(p, w, 4.5260625972231537039e-06);
-    p = fma(p, w, -1.9681778105531670567e-05);
-    p = fma(p, w, 7.5995277030017761139e-05);
-    p = fma(p, w, -0.00021503011930044477347);
-    p = fma(p, w, -0.00013871931833623122026);
-    p = fma(p, w, 1.0103004648645343977);
-    p = fma(p, w, 4.8499064014085844221);
-  }
-  return p * x;
-}
-
-namespace {
-
-// Direct implementation of AS63, BETAIN()
-// https://www.jstor.org/stable/2346797?seq=3#page_scan_tab_contents.
-//
-// BETAIN(x, p, q, beta)
-//  x:     the value of the upper limit x.
-//  p:     the value of the parameter p.
-//  q:     the value of the parameter q.
-//  beta:  the value of ln B(p, q)
-//
-double BetaIncompleteImpl(const double x, const double p, const double q,
-                          const double beta) {
-  if (p < (p + q) * x) {
-    // Incomplete beta function is symmetrical, so return the complement.
-    return 1. - BetaIncompleteImpl(1.0 - x, q, p, beta);
-  }
-
-  double psq = p + q;
-  const double kErr = 1e-14;
-  const double xc = 1. - x;
-  const double pre =
-      std::exp(p * std::log(x) + (q - 1.) * std::log(xc) - beta) / p;
-
-  double term = 1.;
-  double ai = 1.;
-  double result = 1.;
-  int ns = static_cast<int>(q + xc * psq);
-
-  // Use the soper reduction forumla.
-  double rx = (ns == 0) ? x : x / xc;
-  double temp = q - ai;
-  for (;;) {
-    term = term * temp * rx / (p + ai);
-    result = result + term;
-    temp = std::fabs(term);
-    if (temp < kErr && temp < kErr * result) {
-      return result * pre;
-    }
-    ai = ai + 1.;
-    --ns;
-    if (ns >= 0) {
-      temp = q - ai;
-      if (ns == 0) {
-        rx = x;
-      }
-    } else {
-      temp = psq;
-      psq = psq + 1.;
-    }
-  }
-
-  // NOTE: See also TOMS Alogrithm 708.
-  // http://www.netlib.org/toms/index.html
-  //
-  // NOTE: The NWSC library also includes BRATIO / ISUBX (p87)
-  // https://archive.org/details/DTIC_ADA261511/page/n75
-}
-
-// Direct implementation of AS109, XINBTA(p, q, beta, alpha)
-// https://www.jstor.org/stable/2346798?read-now=1&seq=4#page_scan_tab_contents
-// https://www.jstor.org/stable/2346887?seq=1#page_scan_tab_contents
-//
-// XINBTA(p, q, beta, alhpa)
-//  p:     the value of the parameter p.
-//  q:     the value of the parameter q.
-//  beta:  the value of ln B(p, q)
-//  alpha: the value of the lower tail area.
-//
-double BetaIncompleteInvImpl(const double p, const double q, const double beta,
-                             const double alpha) {
-  if (alpha < 0.5) {
-    // Inverse Incomplete beta function is symmetrical, return the complement.
-    return 1. - BetaIncompleteInvImpl(q, p, beta, 1. - alpha);
-  }
-  const double kErr = 1e-14;
-  double value = kErr;
-
-  // Compute the initial estimate.
-  {
-    double r = std::sqrt(-std::log(alpha * alpha));
-    double y =
-        r - fma(r, 0.27061, 2.30753) / fma(r, fma(r, 0.04481, 0.99229), 1.0);
-    if (p > 1. && q > 1.) {
-      r = (y * y - 3.) / 6.;
-      double s = 1. / (p + p - 1.);
-      double t = 1. / (q + q - 1.);
-      double h = 2. / s + t;
-      double w =
-          y * std::sqrt(h + r) / h - (t - s) * (r + 5. / 6. - t / (3. * h));
-      value = p / (p + q * std::exp(w + w));
-    } else {
-      r = q + q;
-      double t = 1.0 / (9. * q);
-      double u = 1.0 - t + y * std::sqrt(t);
-      t = r * (u * u * u);
-      if (t <= 0) {
-        value = 1.0 - std::exp((std::log((1.0 - alpha) * q) + beta) / q);
-      } else {
-        t = (4.0 * p + r - 2.0) / t;
-        if (t <= 1) {
-          value = std::exp((std::log(alpha * p) + beta) / p);
-        } else {
-          value = 1.0 - 2.0 / (t + 1.0);
-        }
-      }
-    }
-  }
-
-  // Solve for x using a modified newton-raphson method using the function
-  // BetaIncomplete.
-  {
-    value = std::max(value, kErr);
-    value = std::min(value, 1.0 - kErr);
-
-    const double r = 1.0 - p;
-    const double t = 1.0 - q;
-    double y;
-    double yprev = 0;
-    double sq = 1;
-    double prev = 1;
-    for (;;) {
-      if (value < 0 || value > 1.0) {
-        // Error case; value went infinite.
-        return std::numeric_limits<double>::infinity();
-      } else if (value == 0 || value == 1) {
-        y = value;
-      } else {
-        y = BetaIncompleteImpl(value, p, q, beta);
-        if (!std::isfinite(y)) {
-          return y;
-        }
-      }
-      y = (y - alpha) *
-          std::exp(beta + r * std::log(value) + t * std::log(1.0 - value));
-      if (y * yprev <= 0) {
-        prev = std::max(sq, std::numeric_limits<double>::min());
-      }
-      double g = 1.0;
-      for (;;) {
-        const double adj = g * y;
-        const double adj_sq = adj * adj;
-        if (adj_sq >= prev) {
-          g = g / 3.0;
-          continue;
-        }
-        const double tx = value - adj;
-        if (tx < 0 || tx > 1) {
-          g = g / 3.0;
-          continue;
-        }
-        if (prev < kErr) {
-          return value;
-        }
-        if (y * y < kErr) {
-          return value;
-        }
-        if (tx == value) {
-          return value;
-        }
-        if (tx == 0 || tx == 1) {
-          g = g / 3.0;
-          continue;
-        }
-        value = tx;
-        yprev = y;
-        break;
-      }
-    }
-  }
-
-  // NOTES: See also: Asymptotic inversion of the incomplete beta function.
-  // https://core.ac.uk/download/pdf/82140723.pdf
-  //
-  // NOTE: See the Boost library documentation as well:
-  // https://www.boost.org/doc/libs/1_52_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_beta/ibeta_function.html
-}
-
-}  // namespace
-
-double BetaIncomplete(const double x, const double p, const double q) {
-  // Error cases.
-  if (p < 0 || q < 0 || x < 0 || x > 1.0) {
-    return std::numeric_limits<double>::infinity();
-  }
-  if (x == 0 || x == 1) {
-    return x;
-  }
-  // ln(Beta(p, q))
-  double beta = std::lgamma(p) + std::lgamma(q) - std::lgamma(p + q);
-  return BetaIncompleteImpl(x, p, q, beta);
-}
-
-double BetaIncompleteInv(const double p, const double q, const double alpha) {
-  // Error cases.
-  if (p < 0 || q < 0 || alpha < 0 || alpha > 1.0) {
-    return std::numeric_limits<double>::infinity();
-  }
-  if (alpha == 0 || alpha == 1) {
-    return alpha;
-  }
-  // ln(Beta(p, q))
-  double beta = std::lgamma(p) + std::lgamma(q) - std::lgamma(p + q);
-  return BetaIncompleteInvImpl(p, q, beta, alpha);
-}
-
-// Given `num_trials` trials each with probability `p` of success, the
-// probability of no failures is `p^k`. To ensure the probability of a failure
-// is no more than `p_fail`, it must be that `p^k == 1 - p_fail`. This function
-// computes `p` from that equation.
-double RequiredSuccessProbability(const double p_fail, const int num_trials) {
-  double p = std::exp(std::log(1.0 - p_fail) / static_cast<double>(num_trials));
-  ABSL_ASSERT(p > 0);
-  return p;
-}
-
-double ZScore(double expected_mean, const DistributionMoments& moments) {
-  return (moments.mean - expected_mean) /
-         (std::sqrt(moments.variance) /
-          std::sqrt(static_cast<double>(moments.n)));
-}
-
-double MaxErrorTolerance(double acceptance_probability) {
-  double one_sided_pvalue = 0.5 * (1.0 - acceptance_probability);
-  const double max_err = InverseNormalSurvival(one_sided_pvalue);
-  ABSL_ASSERT(max_err > 0);
-  return max_err;
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/distribution_test_util.h b/third_party/abseil_cpp/absl/random/internal/distribution_test_util.h
deleted file mode 100644
index 6d94cf6c97..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/distribution_test_util.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
-#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
-
-#include <cstddef>
-#include <iostream>
-#include <vector>
-
-#include "absl/strings/string_view.h"
-#include "absl/types/span.h"
-
-// NOTE: The functions in this file are test only, and are should not be used in
-// non-test code.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// http://webspace.ship.edu/pgmarr/Geo441/Lectures/Lec%205%20-%20Normality%20Testing.pdf
-
-// Compute the 1st to 4th standard moments:
-// mean, variance, skewness, and kurtosis.
-// http://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm
-struct DistributionMoments {
-  size_t n = 0;
-  double mean = 0.0;
-  double variance = 0.0;
-  double skewness = 0.0;
-  double kurtosis = 0.0;
-};
-DistributionMoments ComputeDistributionMoments(
-    absl::Span<const double> data_points);
-
-std::ostream& operator<<(std::ostream& os, const DistributionMoments& moments);
-
-// Computes the Z-score for a set of data with the given distribution moments
-// compared against `expected_mean`.
-double ZScore(double expected_mean, const DistributionMoments& moments);
-
-// Returns the probability of success required for a single trial to ensure that
-// after `num_trials` trials, the probability of at least one failure is no more
-// than `p_fail`.
-double RequiredSuccessProbability(double p_fail, int num_trials);
-
-// Computes the maximum distance from the mean tolerable, for Z-Tests that are
-// expected to pass with `acceptance_probability`. Will terminate if the
-// resulting tolerance is zero (due to passing in 0.0 for
-// `acceptance_probability` or rounding errors).
-//
-// For example,
-// MaxErrorTolerance(0.001) = 0.0
-// MaxErrorTolerance(0.5) = ~0.47
-// MaxErrorTolerance(1.0) = inf
-double MaxErrorTolerance(double acceptance_probability);
-
-// Approximation to inverse of the Error Function in double precision.
-// (http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf)
-double erfinv(double x);
-
-// Beta(p, q) = Gamma(p) * Gamma(q) / Gamma(p+q)
-double beta(double p, double q);
-
-// The inverse of the normal survival function.
-double InverseNormalSurvival(double x);
-
-// Returns whether actual is "near" expected, based on the bound.
-bool Near(absl::string_view msg, double actual, double expected, double bound);
-
-// Implements the incomplete regularized beta function, AS63, BETAIN.
-//    https://www.jstor.org/stable/2346797
-//
-// BetaIncomplete(x, p, q), where
-//   `x` is the value of the upper limit
-//   `p` is beta parameter p, `q` is beta parameter q.
-//
-// NOTE: This is a test-only function which is only accurate to within, at most,
-// 1e-13 of the actual value.
-//
-double BetaIncomplete(double x, double p, double q);
-
-// Implements the inverse of the incomplete regularized beta function, AS109,
-// XINBTA.
-//   https://www.jstor.org/stable/2346798
-//   https://www.jstor.org/stable/2346887
-//
-// BetaIncompleteInv(p, q, beta, alhpa)
-//   `p` is beta parameter p, `q` is beta parameter q.
-//   `alpha` is the value of the lower tail area.
-//
-// NOTE: This is a test-only function and, when successful, is only accurate to
-// within ~1e-6 of the actual value; there are some cases where it diverges from
-// the actual value by much more than that.  The function uses Newton's method,
-// and thus the runtime is highly variable.
-double BetaIncompleteInv(double p, double q, double alpha);
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/distribution_test_util_test.cc b/third_party/abseil_cpp/absl/random/internal/distribution_test_util_test.cc
deleted file mode 100644
index c49d44fb47..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/distribution_test_util_test.cc
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/distribution_test_util.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-TEST(TestUtil, InverseErf) {
-  const struct {
-    const double z;
-    const double value;
-  } kErfInvTable[] = {
-      {0.0000001, 8.86227e-8},
-      {0.00001, 8.86227e-6},
-      {0.5, 0.4769362762044},
-      {0.6, 0.5951160814499},
-      {0.99999, 3.1234132743},
-      {0.9999999, 3.7665625816},
-      {0.999999944, 3.8403850690566985},  // = log((1-x) * (1+x)) =~ 16.004
-      {0.999999999, 4.3200053849134452},
-  };
-
-  for (const auto& data : kErfInvTable) {
-    auto value = absl::random_internal::erfinv(data.z);
-
-    // Log using the Wolfram-alpha function name & parameters.
-    EXPECT_NEAR(value, data.value, 1e-8)
-        << " InverseErf[" << data.z << "]  (expected=" << data.value << ")  -> "
-        << value;
-  }
-}
-
-const struct {
-  const double p;
-  const double q;
-  const double x;
-  const double alpha;
-} kBetaTable[] = {
-    {0.5, 0.5, 0.01, 0.06376856085851985},
-    {0.5, 0.5, 0.1, 0.2048327646991335},
-    {0.5, 0.5, 1, 1},
-    {1, 0.5, 0, 0},
-    {1, 0.5, 0.01, 0.005012562893380045},
-    {1, 0.5, 0.1, 0.0513167019494862},
-    {1, 0.5, 0.5, 0.2928932188134525},
-    {1, 1, 0.5, 0.5},
-    {2, 2, 0.1, 0.028},
-    {2, 2, 0.2, 0.104},
-    {2, 2, 0.3, 0.216},
-    {2, 2, 0.4, 0.352},
-    {2, 2, 0.5, 0.5},
-    {2, 2, 0.6, 0.648},
-    {2, 2, 0.7, 0.784},
-    {2, 2, 0.8, 0.896},
-    {2, 2, 0.9, 0.972},
-    {5.5, 5, 0.5, 0.4361908850559777},
-    {10, 0.5, 0.9, 0.1516409096346979},
-    {10, 5, 0.5, 0.08978271484375},
-    {10, 5, 1, 1},
-    {10, 10, 0.5, 0.5},
-    {20, 5, 0.8, 0.4598773297575791},
-    {20, 10, 0.6, 0.2146816102371739},
-    {20, 10, 0.8, 0.9507364826957875},
-    {20, 20, 0.5, 0.5},
-    {20, 20, 0.6, 0.8979413687105918},
-    {30, 10, 0.7, 0.2241297491808366},
-    {30, 10, 0.8, 0.7586405487192086},
-    {40, 20, 0.7, 0.7001783247477069},
-    {1, 0.5, 0.1, 0.0513167019494862},
-    {1, 0.5, 0.2, 0.1055728090000841},
-    {1, 0.5, 0.3, 0.1633399734659245},
-    {1, 0.5, 0.4, 0.2254033307585166},
-    {1, 2, 0.2, 0.36},
-    {1, 3, 0.2, 0.488},
-    {1, 4, 0.2, 0.5904},
-    {1, 5, 0.2, 0.67232},
-    {2, 2, 0.3, 0.216},
-    {3, 2, 0.3, 0.0837},
-    {4, 2, 0.3, 0.03078},
-    {5, 2, 0.3, 0.010935},
-
-    // These values test small & large points along the range of the Beta
-    // function.
-    //
-    // When selecting test points, remember that if BetaIncomplete(x, p, q)
-    // returns the same value to within the limits of precision over a large
-    // domain of the input, x, then BetaIncompleteInv(alpha, p, q) may return an
-    // essentially arbitrary value where BetaIncomplete(x, p, q) =~ alpha.
-
-    // BetaRegularized[x, 0.00001, 0.00001],
-    // For x in {~0.001 ... ~0.999}, => ~0.5
-    {1e-5, 1e-5, 1e-5, 0.4999424388184638311},
-    {1e-5, 1e-5, (1.0 - 1e-8), 0.5000920948389232964},
-
-    // BetaRegularized[x, 0.00001, 10000].
-    // For x in {~epsilon ... 1.0}, => ~1
-    {1e-5, 1e5, 1e-6, 0.9999817708130066936},
-    {1e-5, 1e5, (1.0 - 1e-7), 1.0},
-
-    // BetaRegularized[x, 10000, 0.00001].
-    // For x in {0 .. 1-epsilon}, => ~0
-    {1e5, 1e-5, 1e-6, 0},
-    {1e5, 1e-5, (1.0 - 1e-6), 1.8229186993306369e-5},
-};
-
-TEST(BetaTest, BetaIncomplete) {
-  for (const auto& data : kBetaTable) {
-    auto value = absl::random_internal::BetaIncomplete(data.x, data.p, data.q);
-
-    // Log using the Wolfram-alpha function name & parameters.
-    EXPECT_NEAR(value, data.alpha, 1e-12)
-        << " BetaRegularized[" << data.x << ", " << data.p << ", " << data.q
-        << "]  (expected=" << data.alpha << ")  -> " << value;
-  }
-}
-
-TEST(BetaTest, BetaIncompleteInv) {
-  for (const auto& data : kBetaTable) {
-    auto value =
-        absl::random_internal::BetaIncompleteInv(data.p, data.q, data.alpha);
-
-    // Log using the Wolfram-alpha function name & parameters.
-    EXPECT_NEAR(value, data.x, 1e-6)
-        << " InverseBetaRegularized[" << data.alpha << ", " << data.p << ", "
-        << data.q << "]  (expected=" << data.x << ")  -> " << value;
-  }
-}
-
-TEST(MaxErrorTolerance, MaxErrorTolerance) {
-  std::vector<std::pair<double, double>> cases = {
-      {0.0000001, 8.86227e-8 * 1.41421356237},
-      {0.00001, 8.86227e-6 * 1.41421356237},
-      {0.5, 0.4769362762044 * 1.41421356237},
-      {0.6, 0.5951160814499 * 1.41421356237},
-      {0.99999, 3.1234132743 * 1.41421356237},
-      {0.9999999, 3.7665625816 * 1.41421356237},
-      {0.999999944, 3.8403850690566985 * 1.41421356237},
-      {0.999999999, 4.3200053849134452 * 1.41421356237}};
-  for (auto entry : cases) {
-    EXPECT_NEAR(absl::random_internal::MaxErrorTolerance(entry.first),
-                entry.second, 1e-8);
-  }
-}
-
-TEST(ZScore, WithSameMean) {
-  absl::random_internal::DistributionMoments m;
-  m.n = 100;
-  m.mean = 5;
-  m.variance = 1;
-  EXPECT_NEAR(absl::random_internal::ZScore(5, m), 0, 1e-12);
-
-  m.n = 1;
-  m.mean = 0;
-  m.variance = 1;
-  EXPECT_NEAR(absl::random_internal::ZScore(0, m), 0, 1e-12);
-
-  m.n = 10000;
-  m.mean = -5;
-  m.variance = 100;
-  EXPECT_NEAR(absl::random_internal::ZScore(-5, m), 0, 1e-12);
-}
-
-TEST(ZScore, DifferentMean) {
-  absl::random_internal::DistributionMoments m;
-  m.n = 100;
-  m.mean = 5;
-  m.variance = 1;
-  EXPECT_NEAR(absl::random_internal::ZScore(4, m), 10, 1e-12);
-
-  m.n = 1;
-  m.mean = 0;
-  m.variance = 1;
-  EXPECT_NEAR(absl::random_internal::ZScore(-1, m), 1, 1e-12);
-
-  m.n = 10000;
-  m.mean = -5;
-  m.variance = 100;
-  EXPECT_NEAR(absl::random_internal::ZScore(-4, m), -10, 1e-12);
-}
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/explicit_seed_seq.h b/third_party/abseil_cpp/absl/random/internal/explicit_seed_seq.h
deleted file mode 100644
index 6a743eaf46..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/explicit_seed_seq.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
-#define ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <initializer_list>
-#include <iterator>
-#include <vector>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// This class conforms to the C++ Standard "Seed Sequence" concept
-// [rand.req.seedseq].
-//
-// An "ExplicitSeedSeq" is meant to provide a conformant interface for
-// forwarding pre-computed seed material to the constructor of a class
-// conforming to the "Uniform Random Bit Generator" concept. This class makes no
-// attempt to mutate the state provided by its constructor, and returns it
-// directly via ExplicitSeedSeq::generate().
-//
-// If this class is asked to generate more seed material than was provided to
-// the constructor, then the remaining bytes will be filled with deterministic,
-// nonrandom data.
-class ExplicitSeedSeq {
- public:
-  using result_type = uint32_t;
-
-  ExplicitSeedSeq() : state_() {}
-
-  // Copy and move both allowed.
-  ExplicitSeedSeq(const ExplicitSeedSeq& other) = default;
-  ExplicitSeedSeq& operator=(const ExplicitSeedSeq& other) = default;
-  ExplicitSeedSeq(ExplicitSeedSeq&& other) = default;
-  ExplicitSeedSeq& operator=(ExplicitSeedSeq&& other) = default;
-
-  template <typename Iterator>
-  ExplicitSeedSeq(Iterator begin, Iterator end) {
-    for (auto it = begin; it != end; it++) {
-      state_.push_back(*it & 0xffffffff);
-    }
-  }
-
-  template <typename T>
-  ExplicitSeedSeq(std::initializer_list<T> il)
-      : ExplicitSeedSeq(il.begin(), il.end()) {}
-
-  size_t size() const { return state_.size(); }
-
-  template <typename OutIterator>
-  void param(OutIterator out) const {
-    std::copy(std::begin(state_), std::end(state_), out);
-  }
-
-  template <typename OutIterator>
-  void generate(OutIterator begin, OutIterator end) {
-    for (size_t index = 0; begin != end; begin++) {
-      *begin = state_.empty() ? 0 : state_[index++];
-      if (index >= state_.size()) {
-        index = 0;
-      }
-    }
-  }
-
- protected:
-  std::vector<uint32_t> state_;
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/explicit_seed_seq_test.cc b/third_party/abseil_cpp/absl/random/internal/explicit_seed_seq_test.cc
deleted file mode 100644
index a55ad73948..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/explicit_seed_seq_test.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/explicit_seed_seq.h"
-
-#include <iterator>
-#include <random>
-#include <utility>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/random/seed_sequences.h"
-
-namespace {
-
-template <typename Sseq>
-bool ConformsToInterface() {
-  // Check that the SeedSequence can be default-constructed.
-  { Sseq default_constructed_seq; }
-  // Check that the SeedSequence can be constructed with two iterators.
-  {
-    uint32_t init_array[] = {1, 3, 5, 7, 9};
-    Sseq iterator_constructed_seq(init_array, &init_array[5]);
-  }
-  // Check that the SeedSequence can be std::initializer_list-constructed.
-  { Sseq list_constructed_seq = {1, 3, 5, 7, 9, 11, 13}; }
-  // Check that param() and size() return state provided to constructor.
-  {
-    uint32_t init_array[] = {1, 2, 3, 4, 5};
-    Sseq seq(init_array, &init_array[ABSL_ARRAYSIZE(init_array)]);
-    EXPECT_EQ(seq.size(), ABSL_ARRAYSIZE(init_array));
-
-    uint32_t state_array[ABSL_ARRAYSIZE(init_array)];
-    seq.param(state_array);
-
-    for (int i = 0; i < ABSL_ARRAYSIZE(state_array); i++) {
-      EXPECT_EQ(state_array[i], i + 1);
-    }
-  }
-  // Check for presence of generate() method.
-  {
-    Sseq seq;
-    uint32_t seeds[5];
-
-    seq.generate(seeds, &seeds[ABSL_ARRAYSIZE(seeds)]);
-  }
-  return true;
-}
-}  // namespace
-
-TEST(SeedSequences, CheckInterfaces) {
-  // Control case
-  EXPECT_TRUE(ConformsToInterface<std::seed_seq>());
-
-  // Abseil classes
-  EXPECT_TRUE(ConformsToInterface<absl::random_internal::ExplicitSeedSeq>());
-}
-
-TEST(ExplicitSeedSeq, DefaultConstructorGeneratesZeros) {
-  const size_t kNumBlocks = 128;
-
-  uint32_t outputs[kNumBlocks];
-  absl::random_internal::ExplicitSeedSeq seq;
-  seq.generate(outputs, &outputs[kNumBlocks]);
-
-  for (uint32_t& seed : outputs) {
-    EXPECT_EQ(seed, 0);
-  }
-}
-
-TEST(ExplicitSeeqSeq, SeedMaterialIsForwardedIdentically) {
-  const size_t kNumBlocks = 128;
-
-  uint32_t seed_material[kNumBlocks];
-  std::random_device urandom{"/dev/urandom"};
-  for (uint32_t& seed : seed_material) {
-    seed = urandom();
-  }
-  absl::random_internal::ExplicitSeedSeq seq(seed_material,
-                                             &seed_material[kNumBlocks]);
-
-  // Check that output is same as seed-material provided to constructor.
-  {
-    const size_t kNumGenerated = kNumBlocks / 2;
-    uint32_t outputs[kNumGenerated];
-    seq.generate(outputs, &outputs[kNumGenerated]);
-    for (size_t i = 0; i < kNumGenerated; i++) {
-      EXPECT_EQ(outputs[i], seed_material[i]);
-    }
-  }
-  // Check that SeedSequence is stateless between invocations: Despite the last
-  // invocation of generate() only consuming half of the input-entropy, the same
-  // entropy will be recycled for the next invocation.
-  {
-    const size_t kNumGenerated = kNumBlocks;
-    uint32_t outputs[kNumGenerated];
-    seq.generate(outputs, &outputs[kNumGenerated]);
-    for (size_t i = 0; i < kNumGenerated; i++) {
-      EXPECT_EQ(outputs[i], seed_material[i]);
-    }
-  }
-  // Check that when more seed-material is asked for than is provided, nonzero
-  // values are still written.
-  {
-    const size_t kNumGenerated = kNumBlocks * 2;
-    uint32_t outputs[kNumGenerated];
-    seq.generate(outputs, &outputs[kNumGenerated]);
-    for (size_t i = 0; i < kNumGenerated; i++) {
-      EXPECT_EQ(outputs[i], seed_material[i % kNumBlocks]);
-    }
-  }
-}
-
-TEST(ExplicitSeedSeq, CopyAndMoveConstructors) {
-  using testing::Each;
-  using testing::Eq;
-  using testing::Not;
-  using testing::Pointwise;
-
-  uint32_t entropy[4];
-  std::random_device urandom("/dev/urandom");
-  for (uint32_t& entry : entropy) {
-    entry = urandom();
-  }
-  absl::random_internal::ExplicitSeedSeq seq_from_entropy(std::begin(entropy),
-                                                          std::end(entropy));
-  // Copy constructor.
-  {
-    absl::random_internal::ExplicitSeedSeq seq_copy(seq_from_entropy);
-    EXPECT_EQ(seq_copy.size(), seq_from_entropy.size());
-
-    std::vector<uint32_t> seeds_1;
-    seeds_1.resize(1000, 0);
-    std::vector<uint32_t> seeds_2;
-    seeds_2.resize(1000, 1);
-
-    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
-    seq_copy.generate(seeds_2.begin(), seeds_2.end());
-
-    EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
-  }
-  // Assignment operator.
-  {
-    for (uint32_t& entry : entropy) {
-      entry = urandom();
-    }
-    absl::random_internal::ExplicitSeedSeq another_seq(std::begin(entropy),
-                                                       std::end(entropy));
-
-    std::vector<uint32_t> seeds_1;
-    seeds_1.resize(1000, 0);
-    std::vector<uint32_t> seeds_2;
-    seeds_2.resize(1000, 0);
-
-    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
-    another_seq.generate(seeds_2.begin(), seeds_2.end());
-
-    // Assert precondition: Sequences generated by seed-sequences are not equal.
-    EXPECT_THAT(seeds_1, Not(Pointwise(Eq(), seeds_2)));
-
-    // Apply the assignment-operator.
-    another_seq = seq_from_entropy;
-
-    // Re-generate seeds.
-    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
-    another_seq.generate(seeds_2.begin(), seeds_2.end());
-
-    // Seeds generated by seed-sequences should now be equal.
-    EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
-  }
-  // Move constructor.
-  {
-    // Get seeds from seed-sequence constructed from entropy.
-    std::vector<uint32_t> seeds_1;
-    seeds_1.resize(1000, 0);
-    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
-
-    // Apply move-constructor move the sequence to another instance.
-    absl::random_internal::ExplicitSeedSeq moved_seq(
-        std::move(seq_from_entropy));
-    std::vector<uint32_t> seeds_2;
-    seeds_2.resize(1000, 1);
-    moved_seq.generate(seeds_2.begin(), seeds_2.end());
-    // Verify that seeds produced by moved-instance are the same as original.
-    EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
-
-    // Verify that the moved-from instance now behaves like a
-    // default-constructed instance.
-    EXPECT_EQ(seq_from_entropy.size(), 0);
-    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
-    EXPECT_THAT(seeds_1, Each(Eq(0)));
-  }
-}
diff --git a/third_party/abseil_cpp/absl/random/internal/fast_uniform_bits.h b/third_party/abseil_cpp/absl/random/internal/fast_uniform_bits.h
deleted file mode 100644
index 425aaf7d83..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/fast_uniform_bits.h
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
-#define ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <limits>
-#include <type_traits>
-
-#include "absl/base/config.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-// Returns true if the input value is zero or a power of two. Useful for
-// determining if the range of output values in a URBG
-template <typename UIntType>
-constexpr bool IsPowerOfTwoOrZero(UIntType n) {
-  return (n == 0) || ((n & (n - 1)) == 0);
-}
-
-// Computes the length of the range of values producible by the URBG, or returns
-// zero if that would encompass the entire range of representable values in
-// URBG::result_type.
-template <typename URBG>
-constexpr typename URBG::result_type RangeSize() {
-  using result_type = typename URBG::result_type;
-  static_assert((URBG::max)() != (URBG::min)(), "URBG range cannot be 0.");
-  return ((URBG::max)() == (std::numeric_limits<result_type>::max)() &&
-          (URBG::min)() == std::numeric_limits<result_type>::lowest())
-             ? result_type{0}
-             : ((URBG::max)() - (URBG::min)() + result_type{1});
-}
-
-// Computes the floor of the log. (i.e., std::floor(std::log2(N));
-template <typename UIntType>
-constexpr UIntType IntegerLog2(UIntType n) {
-  return (n <= 1) ? 0 : 1 + IntegerLog2(n >> 1);
-}
-
-// Returns the number of bits of randomness returned through
-// `PowerOfTwoVariate(urbg)`.
-template <typename URBG>
-constexpr size_t NumBits() {
-  return RangeSize<URBG>() == 0
-             ? std::numeric_limits<typename URBG::result_type>::digits
-             : IntegerLog2(RangeSize<URBG>());
-}
-
-// Given a shift value `n`, constructs a mask with exactly the low `n` bits set.
-// If `n == 0`, all bits are set.
-template <typename UIntType>
-constexpr UIntType MaskFromShift(size_t n) {
-  return ((n % std::numeric_limits<UIntType>::digits) == 0)
-             ? ~UIntType{0}
-             : (UIntType{1} << n) - UIntType{1};
-}
-
-// Tags used to dispatch FastUniformBits::generate to the simple or more complex
-// entropy extraction algorithm.
-struct SimplifiedLoopTag {};
-struct RejectionLoopTag {};
-
-// FastUniformBits implements a fast path to acquire uniform independent bits
-// from a type which conforms to the [rand.req.urbg] concept.
-// Parameterized by:
-//  `UIntType`: the result (output) type
-//
-// The std::independent_bits_engine [rand.adapt.ibits] adaptor can be
-// instantiated from an existing generator through a copy or a move. It does
-// not, however, facilitate the production of pseudorandom bits from an un-owned
-// generator that will outlive the std::independent_bits_engine instance.
-template <typename UIntType = uint64_t>
-class FastUniformBits {
- public:
-  using result_type = UIntType;
-
-  static constexpr result_type(min)() { return 0; }
-  static constexpr result_type(max)() {
-    return (std::numeric_limits<result_type>::max)();
-  }
-
-  template <typename URBG>
-  result_type operator()(URBG& g);  // NOLINT(runtime/references)
-
- private:
-  static_assert(std::is_unsigned<UIntType>::value,
-                "Class-template FastUniformBits<> must be parameterized using "
-                "an unsigned type.");
-
-  // Generate() generates a random value, dispatched on whether
-  // the underlying URBG must use rejection sampling to generate a value,
-  // or whether a simplified loop will suffice.
-  template <typename URBG>
-  result_type Generate(URBG& g,  // NOLINT(runtime/references)
-                       SimplifiedLoopTag);
-
-  template <typename URBG>
-  result_type Generate(URBG& g,  // NOLINT(runtime/references)
-                       RejectionLoopTag);
-};
-
-template <typename UIntType>
-template <typename URBG>
-typename FastUniformBits<UIntType>::result_type
-FastUniformBits<UIntType>::operator()(URBG& g) {  // NOLINT(runtime/references)
-  // kRangeMask is the mask used when sampling variates from the URBG when the
-  // width of the URBG range is not a power of 2.
-  // Y = (2 ^ kRange) - 1
-  static_assert((URBG::max)() > (URBG::min)(),
-                "URBG::max and URBG::min may not be equal.");
-
-  using tag = absl::conditional_t<IsPowerOfTwoOrZero(RangeSize<URBG>()),
-                                  SimplifiedLoopTag, RejectionLoopTag>;
-  return Generate(g, tag{});
-}
-
-template <typename UIntType>
-template <typename URBG>
-typename FastUniformBits<UIntType>::result_type
-FastUniformBits<UIntType>::Generate(URBG& g,  // NOLINT(runtime/references)
-                                    SimplifiedLoopTag) {
-  // The simplified version of FastUniformBits works only on URBGs that have
-  // a range that is a power of 2. In this case we simply loop and shift without
-  // attempting to balance the bits across calls.
-  static_assert(IsPowerOfTwoOrZero(RangeSize<URBG>()),
-                "incorrect Generate tag for URBG instance");
-
-  static constexpr size_t kResultBits =
-      std::numeric_limits<result_type>::digits;
-  static constexpr size_t kUrbgBits = NumBits<URBG>();
-  static constexpr size_t kIters =
-      (kResultBits / kUrbgBits) + (kResultBits % kUrbgBits != 0);
-  static constexpr size_t kShift = (kIters == 1) ? 0 : kUrbgBits;
-  static constexpr auto kMin = (URBG::min)();
-
-  result_type r = static_cast<result_type>(g() - kMin);
-  for (size_t n = 1; n < kIters; ++n) {
-    r = (r << kShift) + static_cast<result_type>(g() - kMin);
-  }
-  return r;
-}
-
-template <typename UIntType>
-template <typename URBG>
-typename FastUniformBits<UIntType>::result_type
-FastUniformBits<UIntType>::Generate(URBG& g,  // NOLINT(runtime/references)
-                                    RejectionLoopTag) {
-  static_assert(!IsPowerOfTwoOrZero(RangeSize<URBG>()),
-                "incorrect Generate tag for URBG instance");
-  using urbg_result_type = typename URBG::result_type;
-
-  // See [rand.adapt.ibits] for more details on the constants calculated below.
-  //
-  // It is preferable to use roughly the same number of bits from each generator
-  // call, however this is only possible when the number of bits provided by the
-  // URBG is a divisor of the number of bits in `result_type`. In all other
-  // cases, the number of bits used cannot always be the same, but it can be
-  // guaranteed to be off by at most 1. Thus we run two loops, one with a
-  // smaller bit-width size (`kSmallWidth`) and one with a larger width size
-  // (satisfying `kLargeWidth == kSmallWidth + 1`). The loops are run
-  // `kSmallIters` and `kLargeIters` times respectively such
-  // that
-  //
-  //    `kResultBits == kSmallIters * kSmallBits
-  //                    + kLargeIters * kLargeBits`
-  //
-  // where `kResultBits` is the total number of bits in `result_type`.
-  //
-  static constexpr size_t kResultBits =
-      std::numeric_limits<result_type>::digits;                      // w
-  static constexpr urbg_result_type kUrbgRange = RangeSize<URBG>();  // R
-  static constexpr size_t kUrbgBits = NumBits<URBG>();               // m
-
-  // compute the initial estimate of the bits used.
-  // [rand.adapt.ibits] 2 (c)
-  static constexpr size_t kA =  // ceil(w/m)
-      (kResultBits / kUrbgBits) + ((kResultBits % kUrbgBits) != 0);  // n'
-
-  static constexpr size_t kABits = kResultBits / kA;  // w0'
-  static constexpr urbg_result_type kARejection =
-      ((kUrbgRange >> kABits) << kABits);  // y0'
-
-  // refine the selection to reduce the rejection frequency.
-  static constexpr size_t kTotalIters =
-      ((kUrbgRange - kARejection) <= (kARejection / kA)) ? kA : (kA + 1);  // n
-
-  // [rand.adapt.ibits] 2 (b)
-  static constexpr size_t kSmallIters =
-      kTotalIters - (kResultBits % kTotalIters);                   // n0
-  static constexpr size_t kSmallBits = kResultBits / kTotalIters;  // w0
-  static constexpr urbg_result_type kSmallRejection =
-      ((kUrbgRange >> kSmallBits) << kSmallBits);  // y0
-
-  static constexpr size_t kLargeBits = kSmallBits + 1;  // w0+1
-  static constexpr urbg_result_type kLargeRejection =
-      ((kUrbgRange >> kLargeBits) << kLargeBits);  // y1
-
-  //
-  // Because `kLargeBits == kSmallBits + 1`, it follows that
-  //
-  //     `kResultBits == kSmallIters * kSmallBits + kLargeIters`
-  //
-  // and therefore
-  //
-  //     `kLargeIters == kTotalWidth % kSmallWidth`
-  //
-  // Intuitively, each iteration with the large width accounts for one unit
-  // of the remainder when `kTotalWidth` is divided by `kSmallWidth`. As
-  // mentioned above, if the URBG width is a divisor of `kTotalWidth`, then
-  // there would be no need for any large iterations (i.e., one loop would
-  // suffice), and indeed, in this case, `kLargeIters` would be zero.
-  static_assert(kResultBits == kSmallIters * kSmallBits +
-                                   (kTotalIters - kSmallIters) * kLargeBits,
-                "Error in looping constant calculations.");
-
-  // The small shift is essentially small bits, but due to the potential
-  // of generating a smaller result_type from a larger urbg type, the actual
-  // shift might be 0.
-  static constexpr size_t kSmallShift = kSmallBits % kResultBits;
-  static constexpr auto kSmallMask =
-      MaskFromShift<urbg_result_type>(kSmallShift);
-  static constexpr size_t kLargeShift = kLargeBits % kResultBits;
-  static constexpr auto kLargeMask =
-      MaskFromShift<urbg_result_type>(kLargeShift);
-
-  static constexpr auto kMin = (URBG::min)();
-
-  result_type s = 0;
-  for (size_t n = 0; n < kSmallIters; ++n) {
-    urbg_result_type v;
-    do {
-      v = g() - kMin;
-    } while (v >= kSmallRejection);
-
-    s = (s << kSmallShift) + static_cast<result_type>(v & kSmallMask);
-  }
-
-  for (size_t n = kSmallIters; n < kTotalIters; ++n) {
-    urbg_result_type v;
-    do {
-      v = g() - kMin;
-    } while (v >= kLargeRejection);
-
-    s = (s << kLargeShift) + static_cast<result_type>(v & kLargeMask);
-  }
-  return s;
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/fast_uniform_bits_test.cc b/third_party/abseil_cpp/absl/random/internal/fast_uniform_bits_test.cc
deleted file mode 100644
index cee702df85..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/fast_uniform_bits_test.cc
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/fast_uniform_bits.h"
-
-#include <random>
-
-#include "gtest/gtest.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-namespace {
-
-template <typename IntType>
-class FastUniformBitsTypedTest : public ::testing::Test {};
-
-using IntTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t>;
-
-TYPED_TEST_SUITE(FastUniformBitsTypedTest, IntTypes);
-
-TYPED_TEST(FastUniformBitsTypedTest, BasicTest) {
-  using Limits = std::numeric_limits<TypeParam>;
-  using FastBits = FastUniformBits<TypeParam>;
-
-  EXPECT_EQ(0, (FastBits::min)());
-  EXPECT_EQ((Limits::max)(), (FastBits::max)());
-
-  constexpr int kIters = 10000;
-  std::random_device rd;
-  std::mt19937 gen(rd());
-  FastBits fast;
-  for (int i = 0; i < kIters; i++) {
-    const auto v = fast(gen);
-    EXPECT_LE(v, (FastBits::max)());
-    EXPECT_GE(v, (FastBits::min)());
-  }
-}
-
-template <typename UIntType, UIntType Lo, UIntType Hi, UIntType Val = Lo>
-struct FakeUrbg {
-  using result_type = UIntType;
-
-  FakeUrbg() = default;
-  explicit FakeUrbg(bool r) : reject(r) {}
-
-  static constexpr result_type(max)() { return Hi; }
-  static constexpr result_type(min)() { return Lo; }
-  result_type operator()() {
-    // when reject is set, return Hi half the time.
-    return ((++calls % 2) == 1 && reject) ? Hi : Val;
-  }
-
-  bool reject = false;
-  size_t calls = 0;
-};
-
-TEST(FastUniformBitsTest, IsPowerOfTwoOrZero) {
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{0}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{1}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{2}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{3}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{4}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{16}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{17}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint8_t>::max)()));
-
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{0}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{1}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{2}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{3}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{4}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{16}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{17}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint16_t>::max)()));
-
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{0}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{1}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{2}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero(uint32_t{3}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{32}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero(uint32_t{17}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint32_t>::max)()));
-
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{0}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{1}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{2}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{3}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{4}));
-  EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{64}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{17}));
-  EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint64_t>::max)()));
-}
-
-TEST(FastUniformBitsTest, IntegerLog2) {
-  EXPECT_EQ(0, IntegerLog2(uint16_t{0}));
-  EXPECT_EQ(0, IntegerLog2(uint16_t{1}));
-  EXPECT_EQ(1, IntegerLog2(uint16_t{2}));
-  EXPECT_EQ(1, IntegerLog2(uint16_t{3}));
-  EXPECT_EQ(2, IntegerLog2(uint16_t{4}));
-  EXPECT_EQ(2, IntegerLog2(uint16_t{5}));
-  EXPECT_EQ(2, IntegerLog2(uint16_t{7}));
-  EXPECT_EQ(3, IntegerLog2(uint16_t{8}));
-  EXPECT_EQ(63, IntegerLog2((std::numeric_limits<uint64_t>::max)()));
-}
-
-TEST(FastUniformBitsTest, RangeSize) {
-  EXPECT_EQ(2, (RangeSize<FakeUrbg<uint8_t, 0, 1>>()));
-  EXPECT_EQ(3, (RangeSize<FakeUrbg<uint8_t, 0, 2>>()));
-  EXPECT_EQ(4, (RangeSize<FakeUrbg<uint8_t, 0, 3>>()));
-  //  EXPECT_EQ(0, (RangeSize<FakeUrbg<uint8_t, 2, 2>>()));
-  EXPECT_EQ(4, (RangeSize<FakeUrbg<uint8_t, 2, 5>>()));
-  EXPECT_EQ(5, (RangeSize<FakeUrbg<uint8_t, 2, 6>>()));
-  EXPECT_EQ(9, (RangeSize<FakeUrbg<uint8_t, 2, 10>>()));
-  EXPECT_EQ(
-      0, (RangeSize<
-             FakeUrbg<uint8_t, 0, (std::numeric_limits<uint8_t>::max)()>>()));
-
-  EXPECT_EQ(4, (RangeSize<FakeUrbg<uint16_t, 0, 3>>()));
-  EXPECT_EQ(4, (RangeSize<FakeUrbg<uint16_t, 2, 5>>()));
-  EXPECT_EQ(5, (RangeSize<FakeUrbg<uint16_t, 2, 6>>()));
-  EXPECT_EQ(18, (RangeSize<FakeUrbg<uint16_t, 1000, 1017>>()));
-  EXPECT_EQ(
-      0, (RangeSize<
-             FakeUrbg<uint16_t, 0, (std::numeric_limits<uint16_t>::max)()>>()));
-
-  EXPECT_EQ(4, (RangeSize<FakeUrbg<uint32_t, 0, 3>>()));
-  EXPECT_EQ(4, (RangeSize<FakeUrbg<uint32_t, 2, 5>>()));
-  EXPECT_EQ(5, (RangeSize<FakeUrbg<uint32_t, 2, 6>>()));
-  EXPECT_EQ(18, (RangeSize<FakeUrbg<uint32_t, 1000, 1017>>()));
-  EXPECT_EQ(0, (RangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()));
-  EXPECT_EQ(0xffffffff, (RangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()));
-  EXPECT_EQ(0xfffffffe, (RangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()));
-  EXPECT_EQ(0xfffffffd, (RangeSize<FakeUrbg<uint32_t, 2, 0xfffffffe>>()));
-  EXPECT_EQ(
-      0, (RangeSize<
-             FakeUrbg<uint32_t, 0, (std::numeric_limits<uint32_t>::max)()>>()));
-
-  EXPECT_EQ(4, (RangeSize<FakeUrbg<uint64_t, 0, 3>>()));
-  EXPECT_EQ(4, (RangeSize<FakeUrbg<uint64_t, 2, 5>>()));
-  EXPECT_EQ(5, (RangeSize<FakeUrbg<uint64_t, 2, 6>>()));
-  EXPECT_EQ(18, (RangeSize<FakeUrbg<uint64_t, 1000, 1017>>()));
-  EXPECT_EQ(0x100000000, (RangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()));
-  EXPECT_EQ(0xffffffff, (RangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()));
-  EXPECT_EQ(0xfffffffe, (RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()));
-  EXPECT_EQ(0xfffffffd, (RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffe>>()));
-  EXPECT_EQ(0, (RangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffff>>()));
-  EXPECT_EQ(0xffffffffffffffff,
-            (RangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffff>>()));
-  EXPECT_EQ(0xfffffffffffffffe,
-            (RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffe>>()));
-  EXPECT_EQ(0xfffffffffffffffd,
-            (RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffffffffffe>>()));
-  EXPECT_EQ(
-      0, (RangeSize<
-             FakeUrbg<uint64_t, 0, (std::numeric_limits<uint64_t>::max)()>>()));
-}
-
-// The constants need to be choosen so that an infinite rejection loop doesn't
-// happen...
-using Urng1_5bit = FakeUrbg<uint8_t, 0, 2, 0>;  // ~1.5 bits (range 3)
-using Urng4bits = FakeUrbg<uint8_t, 1, 0x10, 2>;
-using Urng22bits = FakeUrbg<uint32_t, 0, 0x3fffff, 0x301020>;
-using Urng31bits = FakeUrbg<uint32_t, 1, 0xfffffffe, 0x60070f03>;  // ~31.9 bits
-using Urng32bits = FakeUrbg<uint32_t, 0, 0xffffffff, 0x74010f01>;
-using Urng33bits =
-    FakeUrbg<uint64_t, 1, 0x1ffffffff, 0x013301033>;  // ~32.9 bits
-using Urng63bits = FakeUrbg<uint64_t, 1, 0xfffffffffffffffe,
-                            0xfedcba9012345678>;  // ~63.9 bits
-using Urng64bits =
-    FakeUrbg<uint64_t, 0, 0xffffffffffffffff, 0x123456780fedcba9>;
-
-TEST(FastUniformBitsTest, OutputsUpTo32Bits) {
-  // Tests that how values are composed; the single-bit deltas should be spread
-  // across each invocation.
-  Urng1_5bit urng1_5;
-  Urng4bits urng4;
-  Urng22bits urng22;
-  Urng31bits urng31;
-  Urng32bits urng32;
-  Urng33bits urng33;
-  Urng63bits urng63;
-  Urng64bits urng64;
-
-  // 8-bit types
-  {
-    FastUniformBits<uint8_t> fast8;
-    EXPECT_EQ(0x0, fast8(urng1_5));
-    EXPECT_EQ(0x11, fast8(urng4));
-    EXPECT_EQ(0x20, fast8(urng22));
-    EXPECT_EQ(0x2, fast8(urng31));
-    EXPECT_EQ(0x1, fast8(urng32));
-    EXPECT_EQ(0x32, fast8(urng33));
-    EXPECT_EQ(0x77, fast8(urng63));
-    EXPECT_EQ(0xa9, fast8(urng64));
-  }
-
-  // 16-bit types
-  {
-    FastUniformBits<uint16_t> fast16;
-    EXPECT_EQ(0x0, fast16(urng1_5));
-    EXPECT_EQ(0x1111, fast16(urng4));
-    EXPECT_EQ(0x1020, fast16(urng22));
-    EXPECT_EQ(0x0f02, fast16(urng31));
-    EXPECT_EQ(0x0f01, fast16(urng32));
-    EXPECT_EQ(0x1032, fast16(urng33));
-    EXPECT_EQ(0x5677, fast16(urng63));
-    EXPECT_EQ(0xcba9, fast16(urng64));
-  }
-
-  // 32-bit types
-  {
-    FastUniformBits<uint32_t> fast32;
-    EXPECT_EQ(0x0, fast32(urng1_5));
-    EXPECT_EQ(0x11111111, fast32(urng4));
-    EXPECT_EQ(0x08301020, fast32(urng22));
-    EXPECT_EQ(0x0f020f02, fast32(urng31));
-    EXPECT_EQ(0x74010f01, fast32(urng32));
-    EXPECT_EQ(0x13301032, fast32(urng33));
-    EXPECT_EQ(0x12345677, fast32(urng63));
-    EXPECT_EQ(0x0fedcba9, fast32(urng64));
-  }
-}
-
-TEST(FastUniformBitsTest, Outputs64Bits) {
-  // Tests that how values are composed; the single-bit deltas should be spread
-  // across each invocation.
-  FastUniformBits<uint64_t> fast64;
-
-  {
-    FakeUrbg<uint8_t, 0, 1, 0> urng0;
-    FakeUrbg<uint8_t, 0, 1, 1> urng1;
-    Urng4bits urng4;
-    Urng22bits urng22;
-    Urng31bits urng31;
-    Urng32bits urng32;
-    Urng33bits urng33;
-    Urng63bits urng63;
-    Urng64bits urng64;
-
-    // somewhat degenerate cases only create a single bit.
-    EXPECT_EQ(0x0, fast64(urng0));
-    EXPECT_EQ(64, urng0.calls);
-    EXPECT_EQ(0xffffffffffffffff, fast64(urng1));
-    EXPECT_EQ(64, urng1.calls);
-
-    // less degenerate cases.
-    EXPECT_EQ(0x1111111111111111, fast64(urng4));
-    EXPECT_EQ(16, urng4.calls);
-    EXPECT_EQ(0x01020c0408301020, fast64(urng22));
-    EXPECT_EQ(3, urng22.calls);
-    EXPECT_EQ(0x387811c3c0870f02, fast64(urng31));
-    EXPECT_EQ(3, urng31.calls);
-    EXPECT_EQ(0x74010f0174010f01, fast64(urng32));
-    EXPECT_EQ(2, urng32.calls);
-    EXPECT_EQ(0x808194040cb01032, fast64(urng33));
-    EXPECT_EQ(3, urng33.calls);
-    EXPECT_EQ(0x1234567712345677, fast64(urng63));
-    EXPECT_EQ(2, urng63.calls);
-    EXPECT_EQ(0x123456780fedcba9, fast64(urng64));
-    EXPECT_EQ(1, urng64.calls);
-  }
-
-  // The 1.5 bit case is somewhat interesting in that the algorithm refinement
-  // causes one extra small sample. Comments here reference the names used in
-  // [rand.adapt.ibits] that correspond to this case.
-  {
-    Urng1_5bit urng1_5;
-
-    // w = 64
-    // R = 3
-    // m = 1
-    // n' = 64
-    // w0' = 1
-    // y0' = 2
-    // n = (1 <= 0) > 64 : 65 = 65
-    // n0 = 65 - (64%65) = 1
-    // n1 = 64
-    // w0 = 0
-    // y0 = 3
-    // w1 = 1
-    // y1 = 2
-    EXPECT_EQ(0x0, fast64(urng1_5));
-    EXPECT_EQ(65, urng1_5.calls);
-  }
-
-  // Validate rejections for non-power-of-2 cases.
-  {
-    Urng1_5bit urng1_5(true);
-    Urng31bits urng31(true);
-    Urng33bits urng33(true);
-    Urng63bits urng63(true);
-
-    // For 1.5 bits, there would be 1+2*64, except the first
-    // value was accepted and shifted off the end.
-    EXPECT_EQ(0, fast64(urng1_5));
-    EXPECT_EQ(128, urng1_5.calls);
-    EXPECT_EQ(0x387811c3c0870f02, fast64(urng31));
-    EXPECT_EQ(6, urng31.calls);
-    EXPECT_EQ(0x808194040cb01032, fast64(urng33));
-    EXPECT_EQ(6, urng33.calls);
-    EXPECT_EQ(0x1234567712345677, fast64(urng63));
-    EXPECT_EQ(4, urng63.calls);
-  }
-}
-
-TEST(FastUniformBitsTest, URBG32bitRegression) {
-  // Validate with deterministic 32-bit std::minstd_rand
-  // to ensure that operator() performs as expected.
-
-  EXPECT_EQ(2147483646, RangeSize<std::minstd_rand>());
-  EXPECT_EQ(30, IntegerLog2(RangeSize<std::minstd_rand>()));
-
-  std::minstd_rand gen(1);
-  FastUniformBits<uint64_t> fast64;
-
-  EXPECT_EQ(0x05e47095f8791f45, fast64(gen));
-  EXPECT_EQ(0x028be17e3c07c122, fast64(gen));
-  EXPECT_EQ(0x55d2847c1626e8c2, fast64(gen));
-}
-
-}  // namespace
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/fastmath.h b/third_party/abseil_cpp/absl/random/internal/fastmath.h
deleted file mode 100644
index 6baeb5a7c9..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/fastmath.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_FASTMATH_H_
-#define ABSL_RANDOM_INTERNAL_FASTMATH_H_
-
-// This file contains fast math functions (bitwise ops as well as some others)
-// which are implementation details of various absl random number distributions.
-
-#include <cassert>
-#include <cmath>
-#include <cstdint>
-
-#include "absl/base/internal/bits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// Returns the position of the first bit set.
-inline int LeadingSetBit(uint64_t n) {
-  return 64 - base_internal::CountLeadingZeros64(n);
-}
-
-// Compute log2(n) using integer operations.
-// While std::log2 is more accurate than std::log(n) / std::log(2), for
-// very large numbers--those close to std::numeric_limits<uint64_t>::max() - 2,
-// for instance--std::log2 rounds up rather than down, which introduces
-// definite skew in the results.
-inline int IntLog2Floor(uint64_t n) {
-  return (n <= 1) ? 0 : (63 - base_internal::CountLeadingZeros64(n));
-}
-inline int IntLog2Ceil(uint64_t n) {
-  return (n <= 1) ? 0 : (64 - base_internal::CountLeadingZeros64(n - 1));
-}
-
-inline double StirlingLogFactorial(double n) {
-  assert(n >= 1);
-  // Using Stirling's approximation.
-  constexpr double kLog2PI = 1.83787706640934548356;
-  const double logn = std::log(n);
-  const double ninv = 1.0 / static_cast<double>(n);
-  return n * logn - n + 0.5 * (kLog2PI + logn) + (1.0 / 12.0) * ninv -
-         (1.0 / 360.0) * ninv * ninv * ninv;
-}
-
-// Rotate value right.
-//
-// We only implement the uint32_t / uint64_t versions because
-// 1) those are the only ones we use, and
-// 2) those are the only ones where clang detects the rotate idiom correctly.
-inline constexpr uint32_t rotr(uint32_t value, uint8_t bits) {
-  return (value >> (bits & 31)) | (value << ((-bits) & 31));
-}
-inline constexpr uint64_t rotr(uint64_t value, uint8_t bits) {
-  return (value >> (bits & 63)) | (value << ((-bits) & 63));
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_FASTMATH_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/fastmath_test.cc b/third_party/abseil_cpp/absl/random/internal/fastmath_test.cc
deleted file mode 100644
index 65859c25de..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/fastmath_test.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/fastmath.h"
-
-#include "gtest/gtest.h"
-
-#if defined(__native_client__) || defined(__EMSCRIPTEN__)
-// NACL has a less accurate implementation of std::log2 than most of
-// the other platforms. For some values which should have integral results,
-// sometimes NACL returns slightly larger values.
-//
-// The MUSL libc used by emscripten also has a similar bug.
-#define ABSL_RANDOM_INACCURATE_LOG2
-#endif
-
-namespace {
-
-TEST(DistributionImplTest, LeadingSetBit) {
-  using absl::random_internal::LeadingSetBit;
-  constexpr uint64_t kZero = 0;
-  EXPECT_EQ(0, LeadingSetBit(kZero));
-  EXPECT_EQ(64, LeadingSetBit(~kZero));
-
-  for (int index = 0; index < 64; index++) {
-    uint64_t x = static_cast<uint64_t>(1) << index;
-    EXPECT_EQ(index + 1, LeadingSetBit(x)) << index;
-    EXPECT_EQ(index + 1, LeadingSetBit(x + x - 1)) << index;
-  }
-}
-
-TEST(FastMathTest, IntLog2FloorTest) {
-  using absl::random_internal::IntLog2Floor;
-  constexpr uint64_t kZero = 0;
-  EXPECT_EQ(0, IntLog2Floor(0));  // boundary. return 0.
-  EXPECT_EQ(0, IntLog2Floor(1));
-  EXPECT_EQ(1, IntLog2Floor(2));
-  EXPECT_EQ(63, IntLog2Floor(~kZero));
-
-  // A boundary case: Converting 0xffffffffffffffff requires > 53
-  // bits of precision, so the conversion to double rounds up,
-  // and the result of std::log2(x) > IntLog2Floor(x).
-  EXPECT_LT(IntLog2Floor(~kZero), static_cast<int>(std::log2(~kZero)));
-
-  for (int i = 0; i < 64; i++) {
-    const uint64_t i_pow_2 = static_cast<uint64_t>(1) << i;
-    EXPECT_EQ(i, IntLog2Floor(i_pow_2));
-    EXPECT_EQ(i, static_cast<int>(std::log2(i_pow_2)));
-
-    uint64_t y = i_pow_2;
-    for (int j = i - 1; j > 0; --j) {
-      y = y | (i_pow_2 >> j);
-      EXPECT_EQ(i, IntLog2Floor(y));
-    }
-  }
-}
-
-TEST(FastMathTest, IntLog2CeilTest) {
-  using absl::random_internal::IntLog2Ceil;
-  constexpr uint64_t kZero = 0;
-  EXPECT_EQ(0, IntLog2Ceil(0));  // boundary. return 0.
-  EXPECT_EQ(0, IntLog2Ceil(1));
-  EXPECT_EQ(1, IntLog2Ceil(2));
-  EXPECT_EQ(64, IntLog2Ceil(~kZero));
-
-  // A boundary case: Converting 0xffffffffffffffff requires > 53
-  // bits of precision, so the conversion to double rounds up,
-  // and the result of std::log2(x) > IntLog2Floor(x).
-  EXPECT_LE(IntLog2Ceil(~kZero), static_cast<int>(std::log2(~kZero)));
-
-  for (int i = 0; i < 64; i++) {
-    const uint64_t i_pow_2 = static_cast<uint64_t>(1) << i;
-    EXPECT_EQ(i, IntLog2Ceil(i_pow_2));
-#ifndef ABSL_RANDOM_INACCURATE_LOG2
-    EXPECT_EQ(i, static_cast<int>(std::ceil(std::log2(i_pow_2))));
-#endif
-
-    uint64_t y = i_pow_2;
-    for (int j = i - 1; j > 0; --j) {
-      y = y | (i_pow_2 >> j);
-      EXPECT_EQ(i + 1, IntLog2Ceil(y));
-    }
-  }
-}
-
-TEST(FastMathTest, StirlingLogFactorial) {
-  using absl::random_internal::StirlingLogFactorial;
-
-  EXPECT_NEAR(StirlingLogFactorial(1.0), 0, 1e-3);
-  EXPECT_NEAR(StirlingLogFactorial(1.50), 0.284683, 1e-3);
-  EXPECT_NEAR(StirlingLogFactorial(2.0), 0.69314718056, 1e-4);
-
-  for (int i = 2; i < 50; i++) {
-    double d = static_cast<double>(i);
-    EXPECT_NEAR(StirlingLogFactorial(d), std::lgamma(d + 1), 3e-5);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/gaussian_distribution_gentables.cc b/third_party/abseil_cpp/absl/random/internal/gaussian_distribution_gentables.cc
deleted file mode 100644
index a95333d55f..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/gaussian_distribution_gentables.cc
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Generates gaussian_distribution.cc
-//
-// $ blaze run :gaussian_distribution_gentables > gaussian_distribution.cc
-//
-#include "absl/random/gaussian_distribution.h"
-
-#include <cmath>
-#include <cstddef>
-#include <iostream>
-#include <limits>
-#include <string>
-
-#include "absl/base/macros.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-namespace {
-
-template <typename T, size_t N>
-void FormatArrayContents(std::ostream* os, T (&data)[N]) {
-  if (!std::numeric_limits<T>::is_exact) {
-    // Note: T is either an integer or a float.
-    // float requires higher precision to ensure that values are
-    // reproduced exactly.
-    // Trivia: C99 has hexadecimal floating point literals, but C++11 does not.
-    // Using them would remove all concern of precision loss.
-    os->precision(std::numeric_limits<T>::max_digits10 + 2);
-  }
-  *os << "    {";
-  std::string separator = "";
-  for (size_t i = 0; i < N; ++i) {
-    *os << separator << data[i];
-    if ((i + 1) % 3 != 0) {
-      separator = ", ";
-    } else {
-      separator = ",\n     ";
-    }
-  }
-  *os << "}";
-}
-
-}  // namespace
-
-class TableGenerator : public gaussian_distribution_base {
- public:
-  TableGenerator();
-  void Print(std::ostream* os);
-
-  using gaussian_distribution_base::kMask;
-  using gaussian_distribution_base::kR;
-  using gaussian_distribution_base::kV;
-
- private:
-  Tables tables_;
-};
-
-// Ziggurat gaussian initialization.  For an explanation of the algorithm, see
-// the Marsaglia paper, "The Ziggurat Method for Generating Random Variables".
-//   http://www.jstatsoft.org/v05/i08/
-//
-// Further details are available in the Doornik paper
-//   https://www.doornik.com/research/ziggurat.pdf
-//
-TableGenerator::TableGenerator() {
-  // The constants here should match the values in gaussian_distribution.h
-  static constexpr int kC = kMask + 1;
-
-  static_assert((ABSL_ARRAYSIZE(tables_.x) == kC + 1),
-                "xArray must be length kMask + 2");
-
-  static_assert((ABSL_ARRAYSIZE(tables_.x) == ABSL_ARRAYSIZE(tables_.f)),
-                "fx and x arrays must be identical length");
-
-  auto f = [](double x) { return std::exp(-0.5 * x * x); };
-  auto f_inv = [](double x) { return std::sqrt(-2.0 * std::log(x)); };
-
-  tables_.x[0] = kV / f(kR);
-  tables_.f[0] = f(tables_.x[0]);
-
-  tables_.x[1] = kR;
-  tables_.f[1] = f(tables_.x[1]);
-
-  tables_.x[kC] = 0.0;
-  tables_.f[kC] = f(tables_.x[kC]);  // 1.0
-
-  for (int i = 2; i < kC; i++) {
-    double v = (kV / tables_.x[i - 1]) + tables_.f[i - 1];
-    tables_.x[i] = f_inv(v);
-    tables_.f[i] = v;
-  }
-}
-
-void TableGenerator::Print(std::ostream* os) {
-  *os << "// BEGIN GENERATED CODE; DO NOT EDIT\n"
-         "// clang-format off\n"
-         "\n"
-         "#include \"absl/random/gaussian_distribution.h\"\n"
-         "\n"
-         "namespace absl {\n"
-         "ABSL_NAMESPACE_BEGIN\n"
-         "namespace random_internal {\n"
-         "\n"
-         "const gaussian_distribution_base::Tables\n"
-         "    gaussian_distribution_base::zg_ = {\n";
-  FormatArrayContents(os, tables_.x);
-  *os << ",\n";
-  FormatArrayContents(os, tables_.f);
-  *os << "};\n"
-         "\n"
-         "}  // namespace random_internal\n"
-         "ABSL_NAMESPACE_END\n"
-         "}  // namespace absl\n"
-         "\n"
-         "// clang-format on\n"
-         "// END GENERATED CODE";
-  *os << std::endl;
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-int main(int, char**) {
-  std::cerr << "\nCopy the output to gaussian_distribution.cc" << std::endl;
-  absl::random_internal::TableGenerator generator;
-  generator.Print(&std::cout);
-  return 0;
-}
diff --git a/third_party/abseil_cpp/absl/random/internal/generate_real.h b/third_party/abseil_cpp/absl/random/internal/generate_real.h
deleted file mode 100644
index 20f6d20807..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/generate_real.h
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
-#define ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
-
-// This file contains some implementation details which are used by one or more
-// of the absl random number distributions.
-
-#include <cstdint>
-#include <cstring>
-#include <limits>
-#include <type_traits>
-
-#include "absl/base/internal/bits.h"
-#include "absl/meta/type_traits.h"
-#include "absl/random/internal/fastmath.h"
-#include "absl/random/internal/traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// Tristate tag types controlling the output of GenerateRealFromBits.
-struct GeneratePositiveTag {};
-struct GenerateNegativeTag {};
-struct GenerateSignedTag {};
-
-// GenerateRealFromBits generates a single real value from a single 64-bit
-// `bits` with template fields controlling the output.
-//
-// The `SignedTag` parameter controls whether positive, negative,
-// or either signed/unsigned may be returned.
-//   When SignedTag == GeneratePositiveTag, range is U(0, 1)
-//   When SignedTag == GenerateNegativeTag, range is U(-1, 0)
-//   When SignedTag == GenerateSignedTag, range is U(-1, 1)
-//
-// When the `IncludeZero` parameter is true, the function may return 0 for some
-// inputs, otherwise it never returns 0.
-//
-// When a value in U(0,1) is required, use:
-//   Uniform64ToReal<double, PositiveValueT, true>;
-//
-// When a value in U(-1,1) is required, use:
-//   Uniform64ToReal<double, SignedValueT, false>;
-//
-//   This generates more distinct values than the mathematical equivalent
-//   `U(0, 1) * 2.0 - 1.0`.
-//
-// Scaling the result by powers of 2 (and avoiding a multiply) is also possible:
-//   GenerateRealFromBits<double>(..., -1);  => U(0, 0.5)
-//   GenerateRealFromBits<double>(..., 1);   => U(0, 2)
-//
-template <typename RealType,  // Real type, either float or double.
-          typename SignedTag = GeneratePositiveTag,  // Whether a positive,
-                                                     // negative, or signed
-                                                     // value is generated.
-          bool IncludeZero = true>
-inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) {
-  using real_type = RealType;
-  using uint_type = absl::conditional_t<std::is_same<real_type, float>::value,
-                                        uint32_t, uint64_t>;
-
-  static_assert(
-      (std::is_same<double, real_type>::value ||
-       std::is_same<float, real_type>::value),
-      "GenerateRealFromBits must be parameterized by either float or double.");
-
-  static_assert(sizeof(uint_type) == sizeof(real_type),
-                "Mismatched unsinged and real types.");
-
-  static_assert((std::numeric_limits<real_type>::is_iec559 &&
-                 std::numeric_limits<real_type>::radix == 2),
-                "RealType representation is not IEEE 754 binary.");
-
-  static_assert((std::is_same<SignedTag, GeneratePositiveTag>::value ||
-                 std::is_same<SignedTag, GenerateNegativeTag>::value ||
-                 std::is_same<SignedTag, GenerateSignedTag>::value),
-                "");
-
-  static constexpr int kExp = std::numeric_limits<real_type>::digits - 1;
-  static constexpr uint_type kMask = (static_cast<uint_type>(1) << kExp) - 1u;
-  static constexpr int kUintBits = sizeof(uint_type) * 8;
-
-  int exp = exp_bias + int{std::numeric_limits<real_type>::max_exponent - 2};
-
-  // Determine the sign bit.
-  // Depending on the SignedTag, this may use the left-most bit
-  // or it may be a constant value.
-  uint_type sign = std::is_same<SignedTag, GenerateNegativeTag>::value
-                       ? (static_cast<uint_type>(1) << (kUintBits - 1))
-                       : 0;
-  if (std::is_same<SignedTag, GenerateSignedTag>::value) {
-    if (std::is_same<uint_type, uint64_t>::value) {
-      sign = bits & uint64_t{0x8000000000000000};
-    }
-    if (std::is_same<uint_type, uint32_t>::value) {
-      const uint64_t tmp = bits & uint64_t{0x8000000000000000};
-      sign = static_cast<uint32_t>(tmp >> 32);
-    }
-    // adjust the bits and the exponent to account for removing
-    // the leading bit.
-    bits = bits & uint64_t{0x7FFFFFFFFFFFFFFF};
-    exp++;
-  }
-  if (IncludeZero) {
-    if (bits == 0u) return 0;
-  }
-
-  // Number of leading zeros is mapped to the exponent: 2^-clz
-  // bits is 0..01xxxxxx. After shifting, we're left with 1xxx...0..0
-  int clz = base_internal::CountLeadingZeros64(bits);
-  bits <<= (IncludeZero ? clz : (clz & 63));  // remove 0-bits.
-  exp -= clz;                                 // set the exponent.
-  bits >>= (63 - kExp);
-
-  // Construct the 32-bit or 64-bit IEEE 754 floating-point value from
-  // the individual fields: sign, exp, mantissa(bits).
-  uint_type val =
-      (std::is_same<SignedTag, GeneratePositiveTag>::value ? 0u : sign) |
-      (static_cast<uint_type>(exp) << kExp) |
-      (static_cast<uint_type>(bits) & kMask);
-
-  // bit_cast to the output-type
-  real_type result;
-  memcpy(static_cast<void*>(&result), static_cast<const void*>(&val),
-         sizeof(result));
-  return result;
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/generate_real_test.cc b/third_party/abseil_cpp/absl/random/internal/generate_real_test.cc
deleted file mode 100644
index 4bdc453483..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/generate_real_test.cc
+++ /dev/null
@@ -1,497 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/generate_real.h"
-
-#include <cfloat>
-#include <cstddef>
-#include <cstdint>
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/bits.h"
-#include "absl/flags/flag.h"
-
-ABSL_FLAG(int64_t, absl_random_test_trials, 50000,
-          "Number of trials for the probability tests.");
-
-using absl::random_internal::GenerateNegativeTag;
-using absl::random_internal::GeneratePositiveTag;
-using absl::random_internal::GenerateRealFromBits;
-using absl::random_internal::GenerateSignedTag;
-
-namespace {
-
-TEST(GenerateRealTest, U64ToFloat_Positive_NoZero_Test) {
-  auto ToFloat = [](uint64_t a) {
-    return GenerateRealFromBits<float, GeneratePositiveTag, false>(a);
-  };
-  EXPECT_EQ(ToFloat(0x0000000000000000), 2.710505431e-20f);
-  EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f);
-  EXPECT_EQ(ToFloat(0x8000000000000000), 0.5);
-  EXPECT_EQ(ToFloat(0x8000000000000001), 0.5);
-  EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f);
-}
-
-TEST(GenerateRealTest, U64ToFloat_Positive_Zero_Test) {
-  auto ToFloat = [](uint64_t a) {
-    return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
-  };
-  EXPECT_EQ(ToFloat(0x0000000000000000), 0.0);
-  EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f);
-  EXPECT_EQ(ToFloat(0x8000000000000000), 0.5);
-  EXPECT_EQ(ToFloat(0x8000000000000001), 0.5);
-  EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f);
-}
-
-TEST(GenerateRealTest, U64ToFloat_Negative_NoZero_Test) {
-  auto ToFloat = [](uint64_t a) {
-    return GenerateRealFromBits<float, GenerateNegativeTag, false>(a);
-  };
-  EXPECT_EQ(ToFloat(0x0000000000000000), -2.710505431e-20f);
-  EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f);
-  EXPECT_EQ(ToFloat(0x8000000000000000), -0.5);
-  EXPECT_EQ(ToFloat(0x8000000000000001), -0.5);
-  EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
-}
-
-TEST(GenerateRealTest, U64ToFloat_Negative_Zero_Test) {
-  auto ToFloat = [](uint64_t a) {
-    return GenerateRealFromBits<float, GenerateNegativeTag, true>(a);
-  };
-  EXPECT_EQ(ToFloat(0x0000000000000000), 0.0);
-  EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f);
-  EXPECT_EQ(ToFloat(0x8000000000000000), -0.5);
-  EXPECT_EQ(ToFloat(0x8000000000000001), -0.5);
-  EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
-}
-
-TEST(GenerateRealTest, U64ToFloat_Signed_NoZero_Test) {
-  auto ToFloat = [](uint64_t a) {
-    return GenerateRealFromBits<float, GenerateSignedTag, false>(a);
-  };
-  EXPECT_EQ(ToFloat(0x0000000000000000), 5.421010862e-20f);
-  EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f);
-  EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), 0.9999999404f);
-  EXPECT_EQ(ToFloat(0x8000000000000000), -5.421010862e-20f);
-  EXPECT_EQ(ToFloat(0x8000000000000001), -1.084202172e-19f);
-  EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
-}
-
-TEST(GenerateRealTest, U64ToFloat_Signed_Zero_Test) {
-  auto ToFloat = [](uint64_t a) {
-    return GenerateRealFromBits<float, GenerateSignedTag, true>(a);
-  };
-  EXPECT_EQ(ToFloat(0x0000000000000000), 0);
-  EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f);
-  EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), 0.9999999404f);
-  EXPECT_EQ(ToFloat(0x8000000000000000), 0);
-  EXPECT_EQ(ToFloat(0x8000000000000001), -1.084202172e-19f);
-  EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
-}
-
-TEST(GenerateRealTest, U64ToFloat_Signed_Bias_Test) {
-  auto ToFloat = [](uint64_t a) {
-    return GenerateRealFromBits<float, GenerateSignedTag, true>(a, 1);
-  };
-  EXPECT_EQ(ToFloat(0x0000000000000000), 0);
-  EXPECT_EQ(ToFloat(0x0000000000000001), 2 * 1.084202172e-19f);
-  EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), 2 * 0.9999999404f);
-  EXPECT_EQ(ToFloat(0x8000000000000000), 0);
-  EXPECT_EQ(ToFloat(0x8000000000000001), 2 * -1.084202172e-19f);
-  EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 2 * -0.9999999404f);
-}
-
-TEST(GenerateRealTest, U64ToFloatTest) {
-  auto ToFloat = [](uint64_t a) -> float {
-    return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
-  };
-
-  EXPECT_EQ(ToFloat(0x0000000000000000), 0.0f);
-
-  EXPECT_EQ(ToFloat(0x8000000000000000), 0.5f);
-  EXPECT_EQ(ToFloat(0x8000000000000001), 0.5f);
-  EXPECT_EQ(ToFloat(0x800000FFFFFFFFFF), 0.5f);
-  EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f);
-
-  EXPECT_GT(ToFloat(0x0000000000000001), 0.0f);
-
-  EXPECT_NE(ToFloat(0x7FFFFF0000000000), ToFloat(0x7FFFFEFFFFFFFFFF));
-
-  EXPECT_LT(ToFloat(0xFFFFFFFFFFFFFFFF), 1.0f);
-  int32_t two_to_24 = 1 << 24;
-  EXPECT_EQ(static_cast<int32_t>(ToFloat(0xFFFFFFFFFFFFFFFF) * two_to_24),
-            two_to_24 - 1);
-  EXPECT_NE(static_cast<int32_t>(ToFloat(0xFFFFFFFFFFFFFFFF) * two_to_24 * 2),
-            two_to_24 * 2 - 1);
-  EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), ToFloat(0xFFFFFF0000000000));
-  EXPECT_NE(ToFloat(0xFFFFFFFFFFFFFFFF), ToFloat(0xFFFFFEFFFFFFFFFF));
-  EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), ToFloat(0x7FFFFF8000000000));
-  EXPECT_NE(ToFloat(0x7FFFFFFFFFFFFFFF), ToFloat(0x7FFFFF7FFFFFFFFF));
-  EXPECT_EQ(ToFloat(0x3FFFFFFFFFFFFFFF), ToFloat(0x3FFFFFC000000000));
-  EXPECT_NE(ToFloat(0x3FFFFFFFFFFFFFFF), ToFloat(0x3FFFFFBFFFFFFFFF));
-
-  // For values where every bit counts, the values scale as multiples of the
-  // input.
-  for (int i = 0; i < 100; ++i) {
-    EXPECT_EQ(i * ToFloat(0x0000000000000001), ToFloat(i));
-  }
-
-  // For each i: value generated from (1 << i).
-  float exp_values[64];
-  exp_values[63] = 0.5f;
-  for (int i = 62; i >= 0; --i) exp_values[i] = 0.5f * exp_values[i + 1];
-  constexpr uint64_t one = 1;
-  for (int i = 0; i < 64; ++i) {
-    EXPECT_EQ(ToFloat(one << i), exp_values[i]);
-    for (int j = 1; j < FLT_MANT_DIG && i - j >= 0; ++j) {
-      EXPECT_NE(exp_values[i] + exp_values[i - j], exp_values[i]);
-      EXPECT_EQ(ToFloat((one << i) + (one << (i - j))),
-                exp_values[i] + exp_values[i - j]);
-    }
-    for (int j = FLT_MANT_DIG; i - j >= 0; ++j) {
-      EXPECT_EQ(exp_values[i] + exp_values[i - j], exp_values[i]);
-      EXPECT_EQ(ToFloat((one << i) + (one << (i - j))), exp_values[i]);
-    }
-  }
-}
-
-TEST(GenerateRealTest, U64ToDouble_Positive_NoZero_Test) {
-  auto ToDouble = [](uint64_t a) {
-    return GenerateRealFromBits<double, GeneratePositiveTag, false>(a);
-  };
-
-  EXPECT_EQ(ToDouble(0x0000000000000000), 2.710505431213761085e-20);
-  EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20);
-  EXPECT_EQ(ToDouble(0x0000000000000002), 1.084202172485504434e-19);
-  EXPECT_EQ(ToDouble(0x8000000000000000), 0.5);
-  EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978);
-}
-
-TEST(GenerateRealTest, U64ToDouble_Positive_Zero_Test) {
-  auto ToDouble = [](uint64_t a) {
-    return GenerateRealFromBits<double, GeneratePositiveTag, true>(a);
-  };
-
-  EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
-  EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20);
-  EXPECT_EQ(ToDouble(0x8000000000000000), 0.5);
-  EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978);
-}
-
-TEST(GenerateRealTest, U64ToDouble_Negative_NoZero_Test) {
-  auto ToDouble = [](uint64_t a) {
-    return GenerateRealFromBits<double, GenerateNegativeTag, false>(a);
-  };
-
-  EXPECT_EQ(ToDouble(0x0000000000000000), -2.710505431213761085e-20);
-  EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20);
-  EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19);
-  EXPECT_EQ(ToDouble(0x8000000000000000), -0.5);
-  EXPECT_EQ(ToDouble(0x8000000000000001), -0.5);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
-}
-
-TEST(GenerateRealTest, U64ToDouble_Negative_Zero_Test) {
-  auto ToDouble = [](uint64_t a) {
-    return GenerateRealFromBits<double, GenerateNegativeTag, true>(a);
-  };
-
-  EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
-  EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20);
-  EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19);
-  EXPECT_EQ(ToDouble(0x8000000000000000), -0.5);
-  EXPECT_EQ(ToDouble(0x8000000000000001), -0.5);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
-}
-
-TEST(GenerateRealTest, U64ToDouble_Signed_NoZero_Test) {
-  auto ToDouble = [](uint64_t a) {
-    return GenerateRealFromBits<double, GenerateSignedTag, false>(a);
-  };
-
-  EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20);
-  EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19);
-  EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), 0.999999999999999888978);
-  EXPECT_EQ(ToDouble(0x8000000000000000), -5.42101086242752217004e-20);
-  EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
-}
-
-TEST(GenerateRealTest, U64ToDouble_Signed_Zero_Test) {
-  auto ToDouble = [](uint64_t a) {
-    return GenerateRealFromBits<double, GenerateSignedTag, true>(a);
-  };
-  EXPECT_EQ(ToDouble(0x0000000000000000), 0);
-  EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19);
-  EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), 0.999999999999999888978);
-  EXPECT_EQ(ToDouble(0x8000000000000000), 0);
-  EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
-}
-
-TEST(GenerateRealTest, U64ToDouble_GenerateSignedTag_Bias_Test) {
-  auto ToDouble = [](uint64_t a) {
-    return GenerateRealFromBits<double, GenerateSignedTag, true>(a, -1);
-  };
-  EXPECT_EQ(ToDouble(0x0000000000000000), 0);
-  EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19 / 2);
-  EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), 0.999999999999999888978 / 2);
-  EXPECT_EQ(ToDouble(0x8000000000000000), 0);
-  EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19 / 2);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978 / 2);
-}
-
-TEST(GenerateRealTest, U64ToDoubleTest) {
-  auto ToDouble = [](uint64_t a) {
-    return GenerateRealFromBits<double, GeneratePositiveTag, true>(a);
-  };
-
-  EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
-  EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
-
-  EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20);
-  EXPECT_EQ(ToDouble(0x7fffffffffffffef), 0.499999999999999944489);
-  EXPECT_EQ(ToDouble(0x8000000000000000), 0.5);
-
-  // For values > 0.5, RandU64ToDouble discards up to 11 bits. (64-53).
-  EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
-  EXPECT_EQ(ToDouble(0x80000000000007FF), 0.5);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978);
-  EXPECT_NE(ToDouble(0x7FFFFFFFFFFFF800), ToDouble(0x7FFFFFFFFFFFF7FF));
-
-  EXPECT_LT(ToDouble(0xFFFFFFFFFFFFFFFF), 1.0);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), ToDouble(0xFFFFFFFFFFFFF800));
-  EXPECT_NE(ToDouble(0xFFFFFFFFFFFFFFFF), ToDouble(0xFFFFFFFFFFFFF7FF));
-  EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFFC00));
-  EXPECT_NE(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFFBFF));
-  EXPECT_EQ(ToDouble(0x3FFFFFFFFFFFFFFF), ToDouble(0x3FFFFFFFFFFFFE00));
-  EXPECT_NE(ToDouble(0x3FFFFFFFFFFFFFFF), ToDouble(0x3FFFFFFFFFFFFDFF));
-
-  EXPECT_EQ(ToDouble(0x1000000000000001), 0.0625);
-  EXPECT_EQ(ToDouble(0x2000000000000001), 0.125);
-  EXPECT_EQ(ToDouble(0x3000000000000001), 0.1875);
-  EXPECT_EQ(ToDouble(0x4000000000000001), 0.25);
-  EXPECT_EQ(ToDouble(0x5000000000000001), 0.3125);
-  EXPECT_EQ(ToDouble(0x6000000000000001), 0.375);
-  EXPECT_EQ(ToDouble(0x7000000000000001), 0.4375);
-  EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
-  EXPECT_EQ(ToDouble(0x9000000000000001), 0.5625);
-  EXPECT_EQ(ToDouble(0xa000000000000001), 0.625);
-  EXPECT_EQ(ToDouble(0xb000000000000001), 0.6875);
-  EXPECT_EQ(ToDouble(0xc000000000000001), 0.75);
-  EXPECT_EQ(ToDouble(0xd000000000000001), 0.8125);
-  EXPECT_EQ(ToDouble(0xe000000000000001), 0.875);
-  EXPECT_EQ(ToDouble(0xf000000000000001), 0.9375);
-
-  // Large powers of 2.
-  int64_t two_to_53 = int64_t{1} << 53;
-  EXPECT_EQ(static_cast<int64_t>(ToDouble(0xFFFFFFFFFFFFFFFF) * two_to_53),
-            two_to_53 - 1);
-  EXPECT_NE(static_cast<int64_t>(ToDouble(0xFFFFFFFFFFFFFFFF) * two_to_53 * 2),
-            two_to_53 * 2 - 1);
-
-  // For values where every bit counts, the values scale as multiples of the
-  // input.
-  for (int i = 0; i < 100; ++i) {
-    EXPECT_EQ(i * ToDouble(0x0000000000000001), ToDouble(i));
-  }
-
-  // For each i: value generated from (1 << i).
-  double exp_values[64];
-  exp_values[63] = 0.5;
-  for (int i = 62; i >= 0; --i) exp_values[i] = 0.5 * exp_values[i + 1];
-  constexpr uint64_t one = 1;
-  for (int i = 0; i < 64; ++i) {
-    EXPECT_EQ(ToDouble(one << i), exp_values[i]);
-    for (int j = 1; j < DBL_MANT_DIG && i - j >= 0; ++j) {
-      EXPECT_NE(exp_values[i] + exp_values[i - j], exp_values[i]);
-      EXPECT_EQ(ToDouble((one << i) + (one << (i - j))),
-                exp_values[i] + exp_values[i - j]);
-    }
-    for (int j = DBL_MANT_DIG; i - j >= 0; ++j) {
-      EXPECT_EQ(exp_values[i] + exp_values[i - j], exp_values[i]);
-      EXPECT_EQ(ToDouble((one << i) + (one << (i - j))), exp_values[i]);
-    }
-  }
-}
-
-TEST(GenerateRealTest, U64ToDoubleSignedTest) {
-  auto ToDouble = [](uint64_t a) {
-    return GenerateRealFromBits<double, GenerateSignedTag, false>(a);
-  };
-
-  EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20);
-  EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19);
-
-  EXPECT_EQ(ToDouble(0x8000000000000000), -5.42101086242752217004e-20);
-  EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19);
-
-  const double e_plus = ToDouble(0x0000000000000001);
-  const double e_minus = ToDouble(0x8000000000000001);
-  EXPECT_EQ(e_plus, 1.084202172485504434e-19);
-  EXPECT_EQ(e_minus, -1.084202172485504434e-19);
-
-  EXPECT_EQ(ToDouble(0x3fffffffffffffef), 0.499999999999999944489);
-  EXPECT_EQ(ToDouble(0xbfffffffffffffef), -0.499999999999999944489);
-
-  // For values > 0.5, RandU64ToDouble discards up to 10 bits. (63-53).
-  EXPECT_EQ(ToDouble(0x4000000000000000), 0.5);
-  EXPECT_EQ(ToDouble(0x4000000000000001), 0.5);
-  EXPECT_EQ(ToDouble(0x40000000000003FF), 0.5);
-
-  EXPECT_EQ(ToDouble(0xC000000000000000), -0.5);
-  EXPECT_EQ(ToDouble(0xC000000000000001), -0.5);
-  EXPECT_EQ(ToDouble(0xC0000000000003FF), -0.5);
-
-  EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFe), 0.999999999999999888978);
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFe), -0.999999999999999888978);
-
-  EXPECT_NE(ToDouble(0x7FFFFFFFFFFFF800), ToDouble(0x7FFFFFFFFFFFF7FF));
-
-  EXPECT_LT(ToDouble(0x7FFFFFFFFFFFFFFF), 1.0);
-  EXPECT_GT(ToDouble(0x7FFFFFFFFFFFFFFF), 0.9999999999);
-
-  EXPECT_GT(ToDouble(0xFFFFFFFFFFFFFFFe), -1.0);
-  EXPECT_LT(ToDouble(0xFFFFFFFFFFFFFFFe), -0.999999999);
-
-  EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFe), ToDouble(0xFFFFFFFFFFFFFC00));
-  EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFFC00));
-  EXPECT_NE(ToDouble(0xFFFFFFFFFFFFFFFe), ToDouble(0xFFFFFFFFFFFFF3FF));
-  EXPECT_NE(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFF3FF));
-
-  EXPECT_EQ(ToDouble(0x1000000000000001), 0.125);
-  EXPECT_EQ(ToDouble(0x2000000000000001), 0.25);
-  EXPECT_EQ(ToDouble(0x3000000000000001), 0.375);
-  EXPECT_EQ(ToDouble(0x4000000000000001), 0.5);
-  EXPECT_EQ(ToDouble(0x5000000000000001), 0.625);
-  EXPECT_EQ(ToDouble(0x6000000000000001), 0.75);
-  EXPECT_EQ(ToDouble(0x7000000000000001), 0.875);
-  EXPECT_EQ(ToDouble(0x7800000000000001), 0.9375);
-  EXPECT_EQ(ToDouble(0x7c00000000000001), 0.96875);
-  EXPECT_EQ(ToDouble(0x7e00000000000001), 0.984375);
-  EXPECT_EQ(ToDouble(0x7f00000000000001), 0.9921875);
-
-  // 0x8000000000000000 ~= 0
-  EXPECT_EQ(ToDouble(0x9000000000000001), -0.125);
-  EXPECT_EQ(ToDouble(0xa000000000000001), -0.25);
-  EXPECT_EQ(ToDouble(0xb000000000000001), -0.375);
-  EXPECT_EQ(ToDouble(0xc000000000000001), -0.5);
-  EXPECT_EQ(ToDouble(0xd000000000000001), -0.625);
-  EXPECT_EQ(ToDouble(0xe000000000000001), -0.75);
-  EXPECT_EQ(ToDouble(0xf000000000000001), -0.875);
-
-  // Large powers of 2.
-  int64_t two_to_53 = int64_t{1} << 53;
-  EXPECT_EQ(static_cast<int64_t>(ToDouble(0x7FFFFFFFFFFFFFFF) * two_to_53),
-            two_to_53 - 1);
-  EXPECT_EQ(static_cast<int64_t>(ToDouble(0xFFFFFFFFFFFFFFFF) * two_to_53),
-            -(two_to_53 - 1));
-
-  EXPECT_NE(static_cast<int64_t>(ToDouble(0x7FFFFFFFFFFFFFFF) * two_to_53 * 2),
-            two_to_53 * 2 - 1);
-
-  // For values where every bit counts, the values scale as multiples of the
-  // input.
-  for (int i = 1; i < 100; ++i) {
-    EXPECT_EQ(i * e_plus, ToDouble(i)) << i;
-    EXPECT_EQ(i * e_minus, ToDouble(0x8000000000000000 | i)) << i;
-  }
-}
-
-TEST(GenerateRealTest, ExhaustiveFloat) {
-  using absl::base_internal::CountLeadingZeros64;
-  auto ToFloat = [](uint64_t a) {
-    return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
-  };
-
-  // Rely on RandU64ToFloat generating values from greatest to least when
-  // supplied with uint64_t values from greatest (0xfff...) to least (0x0).
-  // Thus, this algorithm stores the previous value, and if the new value is at
-  // greater than or equal to the previous value, then there is a collision in
-  // the generation algorithm.
-  //
-  // Use the computation below to convert the random value into a result:
-  //   double res = a() * (1.0f - sample) + b() * sample;
-  float last_f = 1.0, last_g = 2.0;
-  uint64_t f_collisions = 0, g_collisions = 0;
-  uint64_t f_unique = 0, g_unique = 0;
-  uint64_t total = 0;
-  auto count = [&](const float r) {
-    total++;
-    // `f` is mapped to the range [0, 1) (default)
-    const float f = 0.0f * (1.0f - r) + 1.0f * r;
-    if (f >= last_f) {
-      f_collisions++;
-    } else {
-      f_unique++;
-      last_f = f;
-    }
-    // `g` is mapped to the range [1, 2)
-    const float g = 1.0f * (1.0f - r) + 2.0f * r;
-    if (g >= last_g) {
-      g_collisions++;
-    } else {
-      g_unique++;
-      last_g = g;
-    }
-  };
-
-  size_t limit = absl::GetFlag(FLAGS_absl_random_test_trials);
-
-  // Generate all uint64_t which have unique floating point values.
-  // Counting down from 0xFFFFFFFFFFFFFFFFu ... 0x0u
-  uint64_t x = ~uint64_t(0);
-  for (; x != 0 && limit > 0;) {
-    constexpr int kDig = (64 - FLT_MANT_DIG);
-    // Set a decrement value & the next point at which to change
-    // the decrement value. By default these are 1, 0.
-    uint64_t dec = 1;
-    uint64_t chk = 0;
-
-    // Adjust decrement and check value based on how many leading 0
-    // bits are set in the current value.
-    const int clz = CountLeadingZeros64(x);
-    if (clz < kDig) {
-      dec <<= (kDig - clz);
-      chk = (~uint64_t(0)) >> (clz + 1);
-    }
-    for (; x > chk && limit > 0; x -= dec) {
-      count(ToFloat(x));
-      --limit;
-    }
-  }
-
-  static_assert(FLT_MANT_DIG == 24,
-                "The float type is expected to have a 24 bit mantissa.");
-
-  if (limit != 0) {
-    // There are between 2^28 and 2^29 unique values in the range [0, 1).  For
-    // the low values of x, there are 2^24 -1 unique values.  Once x > 2^24,
-    // there are 40 * 2^24 unique values. Thus:
-    // (2 + 4 + 8 ... + 2^23) + 40 * 2^23
-    EXPECT_LT(1 << 28, f_unique);
-    EXPECT_EQ((1 << 24) + 40 * (1 << 23) - 1, f_unique);
-    EXPECT_EQ(total, f_unique);
-    EXPECT_EQ(0, f_collisions);
-
-    // Expect at least 2^23 unique values for the range [1, 2)
-    EXPECT_LE(1 << 23, g_unique);
-    EXPECT_EQ(total - g_unique, g_collisions);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/iostream_state_saver.h b/third_party/abseil_cpp/absl/random/internal/iostream_state_saver.h
deleted file mode 100644
index e6e242ee1e..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/iostream_state_saver.h
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_
-#define ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_
-
-#include <cmath>
-#include <iostream>
-#include <limits>
-#include <type_traits>
-
-#include "absl/meta/type_traits.h"
-#include "absl/numeric/int128.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// The null_state_saver does nothing.
-template <typename T>
-class null_state_saver {
- public:
-  using stream_type = T;
-  using flags_type = std::ios_base::fmtflags;
-
-  null_state_saver(T&, flags_type) {}
-  ~null_state_saver() {}
-};
-
-// ostream_state_saver is a RAII object to save and restore the common
-// basic_ostream flags used when implementing `operator <<()` on any of
-// the absl random distributions.
-template <typename OStream>
-class ostream_state_saver {
- public:
-  using ostream_type = OStream;
-  using flags_type = std::ios_base::fmtflags;
-  using fill_type = typename ostream_type::char_type;
-  using precision_type = std::streamsize;
-
-  ostream_state_saver(ostream_type& os,  // NOLINT(runtime/references)
-                      flags_type flags, fill_type fill)
-      : os_(os),
-        flags_(os.flags(flags)),
-        fill_(os.fill(fill)),
-        precision_(os.precision()) {
-    // Save state in initialized variables.
-  }
-
-  ~ostream_state_saver() {
-    // Restore saved state.
-    os_.precision(precision_);
-    os_.fill(fill_);
-    os_.flags(flags_);
-  }
-
- private:
-  ostream_type& os_;
-  const flags_type flags_;
-  const fill_type fill_;
-  const precision_type precision_;
-};
-
-#if defined(__NDK_MAJOR__) && __NDK_MAJOR__ < 16
-#define ABSL_RANDOM_INTERNAL_IOSTREAM_HEXFLOAT 1
-#else
-#define ABSL_RANDOM_INTERNAL_IOSTREAM_HEXFLOAT 0
-#endif
-
-template <typename CharT, typename Traits>
-ostream_state_saver<std::basic_ostream<CharT, Traits>> make_ostream_state_saver(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    std::ios_base::fmtflags flags = std::ios_base::dec | std::ios_base::left |
-#if ABSL_RANDOM_INTERNAL_IOSTREAM_HEXFLOAT
-                                    std::ios_base::fixed |
-#endif
-                                    std::ios_base::scientific) {
-  using result_type = ostream_state_saver<std::basic_ostream<CharT, Traits>>;
-  return result_type(os, flags, os.widen(' '));
-}
-
-template <typename T>
-typename absl::enable_if_t<!std::is_base_of<std::ios_base, T>::value,
-                           null_state_saver<T>>
-make_ostream_state_saver(T& is,  // NOLINT(runtime/references)
-                         std::ios_base::fmtflags flags = std::ios_base::dec) {
-  std::cerr << "null_state_saver";
-  using result_type = null_state_saver<T>;
-  return result_type(is, flags);
-}
-
-// stream_precision_helper<type>::kPrecision returns the base 10 precision
-// required to stream and reconstruct a real type exact binary value through
-// a binary->decimal->binary transition.
-template <typename T>
-struct stream_precision_helper {
-  // max_digits10 may be 0 on MSVC; if so, use digits10 + 3.
-  static constexpr int kPrecision =
-      (std::numeric_limits<T>::max_digits10 > std::numeric_limits<T>::digits10)
-          ? std::numeric_limits<T>::max_digits10
-          : (std::numeric_limits<T>::digits10 + 3);
-};
-
-template <>
-struct stream_precision_helper<float> {
-  static constexpr int kPrecision = 9;
-};
-template <>
-struct stream_precision_helper<double> {
-  static constexpr int kPrecision = 17;
-};
-template <>
-struct stream_precision_helper<long double> {
-  static constexpr int kPrecision = 36;  // assuming fp128
-};
-
-// istream_state_saver is a RAII object to save and restore the common
-// std::basic_istream<> flags used when implementing `operator >>()` on any of
-// the absl random distributions.
-template <typename IStream>
-class istream_state_saver {
- public:
-  using istream_type = IStream;
-  using flags_type = std::ios_base::fmtflags;
-
-  istream_state_saver(istream_type& is,  // NOLINT(runtime/references)
-                      flags_type flags)
-      : is_(is), flags_(is.flags(flags)) {}
-
-  ~istream_state_saver() { is_.flags(flags_); }
-
- private:
-  istream_type& is_;
-  flags_type flags_;
-};
-
-template <typename CharT, typename Traits>
-istream_state_saver<std::basic_istream<CharT, Traits>> make_istream_state_saver(
-    std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-    std::ios_base::fmtflags flags = std::ios_base::dec |
-                                    std::ios_base::scientific |
-                                    std::ios_base::skipws) {
-  using result_type = istream_state_saver<std::basic_istream<CharT, Traits>>;
-  return result_type(is, flags);
-}
-
-template <typename T>
-typename absl::enable_if_t<!std::is_base_of<std::ios_base, T>::value,
-                           null_state_saver<T>>
-make_istream_state_saver(T& is,  // NOLINT(runtime/references)
-                         std::ios_base::fmtflags flags = std::ios_base::dec) {
-  using result_type = null_state_saver<T>;
-  return result_type(is, flags);
-}
-
-// stream_format_type<T> is a helper struct to convert types which
-// basic_iostream cannot output as decimal numbers into types which
-// basic_iostream can output as decimal numbers. Specifically:
-// * signed/unsigned char-width types are converted to int.
-// * TODO(lar): __int128 => uint128, except there is no operator << yet.
-//
-template <typename T>
-struct stream_format_type
-    : public std::conditional<(sizeof(T) == sizeof(char)), int, T> {};
-
-// stream_u128_helper allows us to write out either absl::uint128 or
-// __uint128_t types in the same way, which enables their use as internal
-// state of PRNG engines.
-template <typename T>
-struct stream_u128_helper;
-
-template <>
-struct stream_u128_helper<absl::uint128> {
-  template <typename IStream>
-  inline absl::uint128 read(IStream& in) {
-    uint64_t h = 0;
-    uint64_t l = 0;
-    in >> h >> l;
-    return absl::MakeUint128(h, l);
-  }
-
-  template <typename OStream>
-  inline void write(absl::uint128 val, OStream& out) {
-    uint64_t h = absl::Uint128High64(val);
-    uint64_t l = absl::Uint128Low64(val);
-    out << h << out.fill() << l;
-  }
-};
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-template <>
-struct stream_u128_helper<__uint128_t> {
-  template <typename IStream>
-  inline __uint128_t read(IStream& in) {
-    uint64_t h = 0;
-    uint64_t l = 0;
-    in >> h >> l;
-    return (static_cast<__uint128_t>(h) << 64) | l;
-  }
-
-  template <typename OStream>
-  inline void write(__uint128_t val, OStream& out) {
-    uint64_t h = static_cast<uint64_t>(val >> 64u);
-    uint64_t l = static_cast<uint64_t>(val);
-    out << h << out.fill() << l;
-  }
-};
-#endif
-
-template <typename FloatType, typename IStream>
-inline FloatType read_floating_point(IStream& is) {
-  static_assert(std::is_floating_point<FloatType>::value, "");
-  FloatType dest;
-  is >> dest;
-  // Parsing a double value may report a subnormal value as an error
-  // despite being able to represent it.
-  // See https://stackoverflow.com/q/52410931/3286653
-  // It may also report an underflow when parsing DOUBLE_MIN as an
-  // ERANGE error, as the parsed value may be smaller than DOUBLE_MIN
-  // and rounded up.
-  // See: https://stackoverflow.com/q/42005462
-  if (is.fail() &&
-      (std::fabs(dest) == (std::numeric_limits<FloatType>::min)() ||
-       std::fpclassify(dest) == FP_SUBNORMAL)) {
-    is.clear(is.rdstate() & (~std::ios_base::failbit));
-  }
-  return dest;
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/iostream_state_saver_test.cc b/third_party/abseil_cpp/absl/random/internal/iostream_state_saver_test.cc
deleted file mode 100644
index 7bb8ad959c..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/iostream_state_saver_test.cc
+++ /dev/null
@@ -1,371 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/iostream_state_saver.h"
-
-#include <sstream>
-#include <string>
-
-#include "gtest/gtest.h"
-
-namespace {
-
-using absl::random_internal::make_istream_state_saver;
-using absl::random_internal::make_ostream_state_saver;
-using absl::random_internal::stream_precision_helper;
-
-template <typename T>
-typename absl::enable_if_t<std::is_integral<T>::value, T>  //
-StreamRoundTrip(T t) {
-  std::stringstream ss;
-  {
-    auto saver = make_ostream_state_saver(ss);
-    ss.precision(stream_precision_helper<T>::kPrecision);
-    ss << t;
-  }
-  T result = 0;
-  {
-    auto saver = make_istream_state_saver(ss);
-    ss >> result;
-  }
-  EXPECT_FALSE(ss.fail())            //
-      << ss.str() << " "             //
-      << (ss.good() ? "good " : "")  //
-      << (ss.bad() ? "bad " : "")    //
-      << (ss.eof() ? "eof " : "")    //
-      << (ss.fail() ? "fail " : "");
-
-  return result;
-}
-
-template <typename T>
-typename absl::enable_if_t<std::is_floating_point<T>::value, T>  //
-StreamRoundTrip(T t) {
-  std::stringstream ss;
-  {
-    auto saver = make_ostream_state_saver(ss);
-    ss.precision(stream_precision_helper<T>::kPrecision);
-    ss << t;
-  }
-  T result = 0;
-  {
-    auto saver = make_istream_state_saver(ss);
-    result = absl::random_internal::read_floating_point<T>(ss);
-  }
-  EXPECT_FALSE(ss.fail())            //
-      << ss.str() << " "             //
-      << (ss.good() ? "good " : "")  //
-      << (ss.bad() ? "bad " : "")    //
-      << (ss.eof() ? "eof " : "")    //
-      << (ss.fail() ? "fail " : "");
-
-  return result;
-}
-
-TEST(IOStreamStateSaver, BasicSaverState) {
-  std::stringstream ss;
-  ss.precision(2);
-  ss.fill('x');
-  ss.flags(std::ios_base::dec | std::ios_base::right);
-
-  {
-    auto saver = make_ostream_state_saver(ss);
-    ss.precision(10);
-    EXPECT_NE('x', ss.fill());
-    EXPECT_EQ(10, ss.precision());
-    EXPECT_NE(std::ios_base::dec | std::ios_base::right, ss.flags());
-
-    ss << 1.23;
-  }
-
-  EXPECT_EQ('x', ss.fill());
-  EXPECT_EQ(2, ss.precision());
-  EXPECT_EQ(std::ios_base::dec | std::ios_base::right, ss.flags());
-}
-
-TEST(IOStreamStateSaver, RoundTripInts) {
-  const uint64_t kUintValues[] = {
-      0,
-      1,
-      static_cast<uint64_t>(-1),
-      2,
-      static_cast<uint64_t>(-2),
-
-      1 << 7,
-      1 << 8,
-      1 << 16,
-      1ull << 32,
-      1ull << 50,
-      1ull << 62,
-      1ull << 63,
-
-      (1 << 7) - 1,
-      (1 << 8) - 1,
-      (1 << 16) - 1,
-      (1ull << 32) - 1,
-      (1ull << 50) - 1,
-      (1ull << 62) - 1,
-      (1ull << 63) - 1,
-
-      static_cast<uint64_t>(-(1 << 8)),
-      static_cast<uint64_t>(-(1 << 16)),
-      static_cast<uint64_t>(-(1ll << 32)),
-      static_cast<uint64_t>(-(1ll << 50)),
-      static_cast<uint64_t>(-(1ll << 62)),
-
-      static_cast<uint64_t>(-(1 << 8) - 1),
-      static_cast<uint64_t>(-(1 << 16) - 1),
-      static_cast<uint64_t>(-(1ll << 32) - 1),
-      static_cast<uint64_t>(-(1ll << 50) - 1),
-      static_cast<uint64_t>(-(1ll << 62) - 1),
-  };
-
-  for (const uint64_t u : kUintValues) {
-    EXPECT_EQ(u, StreamRoundTrip<uint64_t>(u));
-
-    int64_t x = static_cast<int64_t>(u);
-    EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
-
-    double d = static_cast<double>(x);
-    EXPECT_EQ(d, StreamRoundTrip<double>(d));
-
-    float f = d;
-    EXPECT_EQ(f, StreamRoundTrip<float>(f));
-  }
-}
-
-TEST(IOStreamStateSaver, RoundTripFloats) {
-  static_assert(
-      stream_precision_helper<float>::kPrecision >= 9,
-      "stream_precision_helper<float>::kPrecision should be at least 9");
-
-  const float kValues[] = {
-      1,
-      std::nextafter(1.0f, 0.0f),  // 1 - epsilon
-      std::nextafter(1.0f, 2.0f),  // 1 + epsilon
-
-      1.0e+1f,
-      1.0e-1f,
-      1.0e+2f,
-      1.0e-2f,
-      1.0e+10f,
-      1.0e-10f,
-
-      0.00000051110000111311111111f,
-      -0.00000051110000111211111111f,
-
-      1.234678912345678912345e+6f,
-      1.234678912345678912345e-6f,
-      1.234678912345678912345e+30f,
-      1.234678912345678912345e-30f,
-      1.234678912345678912345e+38f,
-      1.0234678912345678912345e-38f,
-
-      // Boundary cases.
-      std::numeric_limits<float>::max(),
-      std::numeric_limits<float>::lowest(),
-      std::numeric_limits<float>::epsilon(),
-      std::nextafter(std::numeric_limits<float>::min(),
-                     1.0f),               // min + epsilon
-      std::numeric_limits<float>::min(),  // smallest normal
-      // There are some errors dealing with denorms on apple platforms.
-      std::numeric_limits<float>::denorm_min(),  // smallest denorm
-      std::numeric_limits<float>::min() / 2,
-      std::nextafter(std::numeric_limits<float>::min(),
-                     0.0f),  // denorm_max
-      std::nextafter(std::numeric_limits<float>::denorm_min(), 1.0f),
-  };
-
-  for (const float f : kValues) {
-    EXPECT_EQ(f, StreamRoundTrip<float>(f));
-    EXPECT_EQ(-f, StreamRoundTrip<float>(-f));
-
-    double d = f;
-    EXPECT_EQ(d, StreamRoundTrip<double>(d));
-    EXPECT_EQ(-d, StreamRoundTrip<double>(-d));
-
-    // Avoid undefined behavior (overflow/underflow).
-    if (f <= static_cast<float>(std::numeric_limits<int64_t>::max()) &&
-        f >= static_cast<float>(std::numeric_limits<int64_t>::lowest())) {
-      int64_t x = static_cast<int64_t>(f);
-      EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
-    }
-  }
-}
-
-TEST(IOStreamStateSaver, RoundTripDoubles) {
-  static_assert(
-      stream_precision_helper<double>::kPrecision >= 17,
-      "stream_precision_helper<double>::kPrecision should be at least 17");
-
-  const double kValues[] = {
-      1,
-      std::nextafter(1.0, 0.0),  // 1 - epsilon
-      std::nextafter(1.0, 2.0),  // 1 + epsilon
-
-      1.0e+1,
-      1.0e-1,
-      1.0e+2,
-      1.0e-2,
-      1.0e+10,
-      1.0e-10,
-
-      0.00000051110000111311111111,
-      -0.00000051110000111211111111,
-
-      1.234678912345678912345e+6,
-      1.234678912345678912345e-6,
-      1.234678912345678912345e+30,
-      1.234678912345678912345e-30,
-      1.234678912345678912345e+38,
-      1.0234678912345678912345e-38,
-
-      1.0e+100,
-      1.0e-100,
-      1.234678912345678912345e+308,
-      1.0234678912345678912345e-308,
-      2.22507385850720138e-308,
-
-      // Boundary cases.
-      std::numeric_limits<double>::max(),
-      std::numeric_limits<double>::lowest(),
-      std::numeric_limits<double>::epsilon(),
-      std::nextafter(std::numeric_limits<double>::min(),
-                     1.0),                 // min + epsilon
-      std::numeric_limits<double>::min(),  // smallest normal
-      // There are some errors dealing with denorms on apple platforms.
-      std::numeric_limits<double>::denorm_min(),  // smallest denorm
-      std::numeric_limits<double>::min() / 2,
-      std::nextafter(std::numeric_limits<double>::min(),
-                     0.0),  // denorm_max
-      std::nextafter(std::numeric_limits<double>::denorm_min(), 1.0f),
-  };
-
-  for (const double d : kValues) {
-    EXPECT_EQ(d, StreamRoundTrip<double>(d));
-    EXPECT_EQ(-d, StreamRoundTrip<double>(-d));
-
-    // Avoid undefined behavior (overflow/underflow).
-    if (d <= std::numeric_limits<float>::max() &&
-        d >= std::numeric_limits<float>::lowest()) {
-      float f = static_cast<float>(d);
-      EXPECT_EQ(f, StreamRoundTrip<float>(f));
-    }
-
-    // Avoid undefined behavior (overflow/underflow).
-    if (d <= static_cast<double>(std::numeric_limits<int64_t>::max()) &&
-        d >= static_cast<double>(std::numeric_limits<int64_t>::lowest())) {
-      int64_t x = static_cast<int64_t>(d);
-      EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
-    }
-  }
-}
-
-#if !defined(__EMSCRIPTEN__)
-TEST(IOStreamStateSaver, RoundTripLongDoubles) {
-  // Technically, C++ only guarantees that long double is at least as large as a
-  // double.  Practically it varies from 64-bits to 128-bits.
-  //
-  // So it is best to consider long double a best-effort extended precision
-  // type.
-
-  static_assert(
-      stream_precision_helper<long double>::kPrecision >= 36,
-      "stream_precision_helper<long double>::kPrecision should be at least 36");
-
-  using real_type = long double;
-  const real_type kValues[] = {
-      1,
-      std::nextafter(1.0, 0.0),  // 1 - epsilon
-      std::nextafter(1.0, 2.0),  // 1 + epsilon
-
-      1.0e+1,
-      1.0e-1,
-      1.0e+2,
-      1.0e-2,
-      1.0e+10,
-      1.0e-10,
-
-      0.00000051110000111311111111,
-      -0.00000051110000111211111111,
-
-      1.2346789123456789123456789123456789e+6,
-      1.2346789123456789123456789123456789e-6,
-      1.2346789123456789123456789123456789e+30,
-      1.2346789123456789123456789123456789e-30,
-      1.2346789123456789123456789123456789e+38,
-      1.2346789123456789123456789123456789e-38,
-      1.2346789123456789123456789123456789e+308,
-      1.2346789123456789123456789123456789e-308,
-
-      1.0e+100,
-      1.0e-100,
-      1.234678912345678912345e+308,
-      1.0234678912345678912345e-308,
-
-      // Boundary cases.
-      std::numeric_limits<real_type>::max(),
-      std::numeric_limits<real_type>::lowest(),
-      std::numeric_limits<real_type>::epsilon(),
-      std::nextafter(std::numeric_limits<real_type>::min(),
-                     real_type(1)),           // min + epsilon
-      std::numeric_limits<real_type>::min(),  // smallest normal
-      // There are some errors dealing with denorms on apple platforms.
-      std::numeric_limits<real_type>::denorm_min(),  // smallest denorm
-      std::numeric_limits<real_type>::min() / 2,
-      std::nextafter(std::numeric_limits<real_type>::min(),
-                     0.0),  // denorm_max
-      std::nextafter(std::numeric_limits<real_type>::denorm_min(), 1.0f),
-  };
-
-  int index = -1;
-  for (const long double dd : kValues) {
-    index++;
-    EXPECT_EQ(dd, StreamRoundTrip<real_type>(dd)) << index;
-    EXPECT_EQ(-dd, StreamRoundTrip<real_type>(-dd)) << index;
-
-    // Avoid undefined behavior (overflow/underflow).
-    if (dd <= std::numeric_limits<double>::max() &&
-        dd >= std::numeric_limits<double>::lowest()) {
-      double d = static_cast<double>(dd);
-      EXPECT_EQ(d, StreamRoundTrip<double>(d));
-    }
-
-    // Avoid undefined behavior (overflow/underflow).
-    if (dd <= std::numeric_limits<int64_t>::max() &&
-        dd >= std::numeric_limits<int64_t>::lowest()) {
-      int64_t x = static_cast<int64_t>(dd);
-      EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
-    }
-  }
-}
-#endif  // !defined(__EMSCRIPTEN__)
-
-TEST(StrToDTest, DoubleMin) {
-  const char kV[] = "2.22507385850720138e-308";
-  char* end;
-  double x = std::strtod(kV, &end);
-  EXPECT_EQ(std::numeric_limits<double>::min(), x);
-  // errno may equal ERANGE.
-}
-
-TEST(StrToDTest, DoubleDenormMin) {
-  const char kV[] = "4.94065645841246544e-324";
-  char* end;
-  double x = std::strtod(kV, &end);
-  EXPECT_EQ(std::numeric_limits<double>::denorm_min(), x);
-  // errno may equal ERANGE.
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/mock_helpers.h b/third_party/abseil_cpp/absl/random/internal/mock_helpers.h
deleted file mode 100644
index 9af27ab3a2..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/mock_helpers.h
+++ /dev/null
@@ -1,127 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
-#define ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
-
-#include <tuple>
-#include <type_traits>
-
-#include "absl/base/internal/fast_type_id.h"
-#include "absl/types/optional.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// MockHelpers works in conjunction with MockOverloadSet, MockingBitGen, and
-// BitGenRef to enable the mocking capability for absl distribution functions.
-//
-// MockingBitGen registers mocks based on the typeid of a mock signature, KeyT,
-// which is used to generate a unique id.
-//
-// KeyT is a signature of the form:
-//   result_type(discriminator_type, std::tuple<args...>)
-// The mocked function signature will be composed from KeyT as:
-//   result_type(args...)
-//
-class MockHelpers {
-  using IdType = ::absl::base_internal::FastTypeIdType;
-
-  // Given a key signature type used to index the mock, extract the components.
-  // KeyT is expected to have the form:
-  //   result_type(discriminator_type, arg_tuple_type)
-  template <typename KeyT>
-  struct KeySignature;
-
-  template <typename ResultT, typename DiscriminatorT, typename ArgTupleT>
-  struct KeySignature<ResultT(DiscriminatorT, ArgTupleT)> {
-    using result_type = ResultT;
-    using discriminator_type = DiscriminatorT;
-    using arg_tuple_type = ArgTupleT;
-  };
-
-  // Detector for InvokeMock.
-  template <class T>
-  using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
-      std::declval<IdType>(), std::declval<void*>(), std::declval<void*>()));
-
-  // Empty implementation of InvokeMock.
-  template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG,
-            typename... Args>
-  static absl::optional<ReturnT> InvokeMockImpl(char, URBG*, Args&&...) {
-    return absl::nullopt;
-  }
-
-  // Non-empty implementation of InvokeMock.
-  template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG,
-            typename = invoke_mock_t<URBG>, typename... Args>
-  static absl::optional<ReturnT> InvokeMockImpl(int, URBG* urbg,
-                                                Args&&... args) {
-    ArgTupleT arg_tuple(std::forward<Args>(args)...);
-    ReturnT result;
-    if (urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple,
-                         &result)) {
-      return result;
-    }
-    return absl::nullopt;
-  }
-
- public:
-  // Invoke a mock for the KeyT (may or may not be a signature).
-  //
-  // KeyT is used to generate a typeid-based lookup key for the mock.
-  // KeyT is a signature of the form:
-  //   result_type(discriminator_type, std::tuple<args...>)
-  // The mocked function signature will be composed from KeyT as:
-  //   result_type(args...)
-  //
-  // An instance of arg_tuple_type must be constructable from Args..., since
-  // the underlying mechanism requires a pointer to an argument tuple.
-  template <typename KeyT, typename URBG, typename... Args>
-  static auto MaybeInvokeMock(URBG* urbg, Args&&... args)
-      -> absl::optional<typename KeySignature<KeyT>::result_type> {
-    // Use function overloading to dispatch to the implemenation since
-    // more modern patterns (e.g. require + constexpr) are not supported in all
-    // compiler configurations.
-    return InvokeMockImpl<KeyT, typename KeySignature<KeyT>::result_type,
-                          typename KeySignature<KeyT>::arg_tuple_type, URBG>(
-        0, urbg, std::forward<Args>(args)...);
-  }
-
-  // Acquire a mock for the KeyT (may or may not be a signature).
-  //
-  // KeyT is used to generate a typeid-based lookup for the mock.
-  // KeyT is a signature of the form:
-  //   result_type(discriminator_type, std::tuple<args...>)
-  // The mocked function signature will be composed from KeyT as:
-  //   result_type(args...)
-  template <typename KeyT, typename MockURBG>
-  static auto MockFor(MockURBG& m) -> decltype(
-      std::declval<MockURBG>()
-          .template RegisterMock<typename KeySignature<KeyT>::result_type,
-                                 typename KeySignature<KeyT>::arg_tuple_type>(
-              std::declval<IdType>())) {
-    return m.template RegisterMock<typename KeySignature<KeyT>::result_type,
-                                   typename KeySignature<KeyT>::arg_tuple_type>(
-        ::absl::base_internal::FastTypeId<KeyT>());
-  }
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/mock_overload_set.h b/third_party/abseil_cpp/absl/random/internal/mock_overload_set.h
deleted file mode 100644
index dccc6cee67..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/mock_overload_set.h
+++ /dev/null
@@ -1,97 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_
-#define ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_
-
-#include <type_traits>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/random/internal/mock_helpers.h"
-#include "absl/random/mocking_bit_gen.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-template <typename DistrT, typename Fn>
-struct MockSingleOverload;
-
-// MockSingleOverload
-//
-// MockSingleOverload hooks in to gMock's `ON_CALL` and `EXPECT_CALL` macros.
-// EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to
-// `mock_single_overload.gmock_Call(...)`. Because expectations are stored on
-// the MockingBitGen (an argument passed inside `Call(...)`), this forwards to
-// arguments to MockingBitGen::Register.
-//
-// The underlying KeyT must match the KeyT constructed by DistributionCaller.
-template <typename DistrT, typename Ret, typename... Args>
-struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> {
-  static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
-                "Overload signature must have return type matching the "
-                "distribution result_type.");
-  using KeyT = Ret(DistrT, std::tuple<Args...>);
-  auto gmock_Call(
-      absl::MockingBitGen& gen,  // NOLINT(google-runtime-references)
-      const ::testing::Matcher<Args>&... matchers)
-      -> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...)) {
-    return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...);
-  }
-};
-
-template <typename DistrT, typename Ret, typename Arg, typename... Args>
-struct MockSingleOverload<DistrT, Ret(Arg, MockingBitGen&, Args...)> {
-  static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
-                "Overload signature must have return type matching the "
-                "distribution result_type.");
-  using KeyT = Ret(DistrT, std::tuple<Arg, Args...>);
-  auto gmock_Call(
-      const ::testing::Matcher<Arg>& matcher,
-      absl::MockingBitGen& gen,  // NOLINT(google-runtime-references)
-      const ::testing::Matcher<Args>&... matchers)
-      -> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher,
-                                                             matchers...)) {
-    return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher, matchers...);
-  }
-};
-
-// MockOverloadSet
-//
-// MockOverloadSet takes a distribution and a collection of signatures and
-// performs overload resolution amongst all the overloads. This makes
-// `EXPECT_CALL(mock_overload_set, Call(...))` expand and do overload resolution
-// correctly.
-template <typename DistrT, typename... Signatures>
-struct MockOverloadSet;
-
-template <typename DistrT, typename Sig>
-struct MockOverloadSet<DistrT, Sig> : public MockSingleOverload<DistrT, Sig> {
-  using MockSingleOverload<DistrT, Sig>::gmock_Call;
-};
-
-template <typename DistrT, typename FirstSig, typename... Rest>
-struct MockOverloadSet<DistrT, FirstSig, Rest...>
-    : public MockSingleOverload<DistrT, FirstSig>,
-      public MockOverloadSet<DistrT, Rest...> {
-  using MockSingleOverload<DistrT, FirstSig>::gmock_Call;
-  using MockOverloadSet<DistrT, Rest...>::gmock_Call;
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-#endif  // ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/nanobenchmark.cc b/third_party/abseil_cpp/absl/random/internal/nanobenchmark.cc
deleted file mode 100644
index c9181813f7..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/nanobenchmark.cc
+++ /dev/null
@@ -1,804 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/nanobenchmark.h"
-
-#include <sys/types.h>
-
-#include <algorithm>  // sort
-#include <atomic>
-#include <cstddef>
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>  // memcpy
-#include <limits>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/platform.h"
-#include "absl/random/internal/randen_engine.h"
-
-// OS
-#if defined(_WIN32) || defined(_WIN64)
-#define ABSL_OS_WIN
-#include <windows.h>  // NOLINT
-
-#elif defined(__ANDROID__)
-#define ABSL_OS_ANDROID
-
-#elif defined(__linux__)
-#define ABSL_OS_LINUX
-#include <sched.h>        // NOLINT
-#include <sys/syscall.h>  // NOLINT
-#endif
-
-#if defined(ABSL_ARCH_X86_64) && !defined(ABSL_OS_WIN)
-#include <cpuid.h>  // NOLINT
-#endif
-
-// __ppc_get_timebase_freq
-#if defined(ABSL_ARCH_PPC)
-#include <sys/platform/ppc.h>  // NOLINT
-#endif
-
-// clock_gettime
-#if defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
-#include <time.h>  // NOLINT
-#endif
-
-// ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE prevents inlining of the method.
-#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __attribute__((noinline))
-#elif defined(_MSC_VER)
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __declspec(noinline)
-#else
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal_nanobenchmark {
-namespace {
-
-// For code folding.
-namespace platform {
-#if defined(ABSL_ARCH_X86_64)
-
-// TODO(janwas): Merge with the one in randen_hwaes.cc?
-void Cpuid(const uint32_t level, const uint32_t count,
-           uint32_t* ABSL_RANDOM_INTERNAL_RESTRICT abcd) {
-#if defined(ABSL_OS_WIN)
-  int regs[4];
-  __cpuidex(regs, level, count);
-  for (int i = 0; i < 4; ++i) {
-    abcd[i] = regs[i];
-  }
-#else
-  uint32_t a, b, c, d;
-  __cpuid_count(level, count, a, b, c, d);
-  abcd[0] = a;
-  abcd[1] = b;
-  abcd[2] = c;
-  abcd[3] = d;
-#endif
-}
-
-std::string BrandString() {
-  char brand_string[49];
-  uint32_t abcd[4];
-
-  // Check if brand string is supported (it is on all reasonable Intel/AMD)
-  Cpuid(0x80000000U, 0, abcd);
-  if (abcd[0] < 0x80000004U) {
-    return std::string();
-  }
-
-  for (int i = 0; i < 3; ++i) {
-    Cpuid(0x80000002U + i, 0, abcd);
-    memcpy(brand_string + i * 16, &abcd, sizeof(abcd));
-  }
-  brand_string[48] = 0;
-  return brand_string;
-}
-
-// Returns the frequency quoted inside the brand string. This does not
-// account for throttling nor Turbo Boost.
-double NominalClockRate() {
-  const std::string& brand_string = BrandString();
-  // Brand strings include the maximum configured frequency. These prefixes are
-  // defined by Intel CPUID documentation.
-  const char* prefixes[3] = {"MHz", "GHz", "THz"};
-  const double multipliers[3] = {1E6, 1E9, 1E12};
-  for (size_t i = 0; i < 3; ++i) {
-    const size_t pos_prefix = brand_string.find(prefixes[i]);
-    if (pos_prefix != std::string::npos) {
-      const size_t pos_space = brand_string.rfind(' ', pos_prefix - 1);
-      if (pos_space != std::string::npos) {
-        const std::string digits =
-            brand_string.substr(pos_space + 1, pos_prefix - pos_space - 1);
-        return std::stod(digits) * multipliers[i];
-      }
-    }
-  }
-
-  return 0.0;
-}
-
-#endif  // ABSL_ARCH_X86_64
-}  // namespace platform
-
-// Prevents the compiler from eliding the computations that led to "output".
-template <class T>
-inline void PreventElision(T&& output) {
-#ifndef ABSL_OS_WIN
-  // Works by indicating to the compiler that "output" is being read and
-  // modified. The +r constraint avoids unnecessary writes to memory, but only
-  // works for built-in types (typically FuncOutput).
-  asm volatile("" : "+r"(output) : : "memory");
-#else
-  // MSVC does not support inline assembly anymore (and never supported GCC's
-  // RTL constraints). Self-assignment with #pragma optimize("off") might be
-  // expected to prevent elision, but it does not with MSVC 2015. Type-punning
-  // with volatile pointers generates inefficient code on MSVC 2017.
-  static std::atomic<T> dummy(T{});
-  dummy.store(output, std::memory_order_relaxed);
-#endif
-}
-
-namespace timer {
-
-// Start/Stop return absolute timestamps and must be placed immediately before
-// and after the region to measure. We provide separate Start/Stop functions
-// because they use different fences.
-//
-// Background: RDTSC is not 'serializing'; earlier instructions may complete
-// after it, and/or later instructions may complete before it. 'Fences' ensure
-// regions' elapsed times are independent of such reordering. The only
-// documented unprivileged serializing instruction is CPUID, which acts as a
-// full fence (no reordering across it in either direction). Unfortunately
-// the latency of CPUID varies wildly (perhaps made worse by not initializing
-// its EAX input). Because it cannot reliably be deducted from the region's
-// elapsed time, it must not be included in the region to measure (i.e.
-// between the two RDTSC).
-//
-// The newer RDTSCP is sometimes described as serializing, but it actually
-// only serves as a half-fence with release semantics. Although all
-// instructions in the region will complete before the final timestamp is
-// captured, subsequent instructions may leak into the region and increase the
-// elapsed time. Inserting another fence after the final RDTSCP would prevent
-// such reordering without affecting the measured region.
-//
-// Fortunately, such a fence exists. The LFENCE instruction is only documented
-// to delay later loads until earlier loads are visible. However, Intel's
-// reference manual says it acts as a full fence (waiting until all earlier
-// instructions have completed, and delaying later instructions until it
-// completes). AMD assigns the same behavior to MFENCE.
-//
-// We need a fence before the initial RDTSC to prevent earlier instructions
-// from leaking into the region, and arguably another after RDTSC to avoid
-// region instructions from completing before the timestamp is recorded.
-// When surrounded by fences, the additional RDTSCP half-fence provides no
-// benefit, so the initial timestamp can be recorded via RDTSC, which has
-// lower overhead than RDTSCP because it does not read TSC_AUX. In summary,
-// we define Start = LFENCE/RDTSC/LFENCE; Stop = RDTSCP/LFENCE.
-//
-// Using Start+Start leads to higher variance and overhead than Stop+Stop.
-// However, Stop+Stop includes an LFENCE in the region measurements, which
-// adds a delay dependent on earlier loads. The combination of Start+Stop
-// is faster than Start+Start and more consistent than Stop+Stop because
-// the first LFENCE already delayed subsequent loads before the measured
-// region. This combination seems not to have been considered in prior work:
-// http://akaros.cs.berkeley.edu/lxr/akaros/kern/arch/x86/rdtsc_test.c
-//
-// Note: performance counters can measure 'exact' instructions-retired or
-// (unhalted) cycle counts. The RDPMC instruction is not serializing and also
-// requires fences. Unfortunately, it is not accessible on all OSes and we
-// prefer to avoid kernel-mode drivers. Performance counters are also affected
-// by several under/over-count errata, so we use the TSC instead.
-
-// Returns a 64-bit timestamp in unit of 'ticks'; to convert to seconds,
-// divide by InvariantTicksPerSecond.
-inline uint64_t Start64() {
-  uint64_t t;
-#if defined(ABSL_ARCH_PPC)
-  asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
-#elif defined(ABSL_ARCH_X86_64)
-#if defined(ABSL_OS_WIN)
-  _ReadWriteBarrier();
-  _mm_lfence();
-  _ReadWriteBarrier();
-  t = __rdtsc();
-  _ReadWriteBarrier();
-  _mm_lfence();
-  _ReadWriteBarrier();
-#else
-  asm volatile(
-      "lfence\n\t"
-      "rdtsc\n\t"
-      "shl $32, %%rdx\n\t"
-      "or %%rdx, %0\n\t"
-      "lfence"
-      : "=a"(t)
-      :
-      // "memory" avoids reordering. rdx = TSC >> 32.
-      // "cc" = flags modified by SHL.
-      : "rdx", "memory", "cc");
-#endif
-#else
-  // Fall back to OS - unsure how to reliably query cntvct_el0 frequency.
-  timespec ts;
-  clock_gettime(CLOCK_REALTIME, &ts);
-  t = ts.tv_sec * 1000000000LL + ts.tv_nsec;
-#endif
-  return t;
-}
-
-inline uint64_t Stop64() {
-  uint64_t t;
-#if defined(ABSL_ARCH_X86_64)
-#if defined(ABSL_OS_WIN)
-  _ReadWriteBarrier();
-  unsigned aux;
-  t = __rdtscp(&aux);
-  _ReadWriteBarrier();
-  _mm_lfence();
-  _ReadWriteBarrier();
-#else
-  // Use inline asm because __rdtscp generates code to store TSC_AUX (ecx).
-  asm volatile(
-      "rdtscp\n\t"
-      "shl $32, %%rdx\n\t"
-      "or %%rdx, %0\n\t"
-      "lfence"
-      : "=a"(t)
-      :
-      // "memory" avoids reordering. rcx = TSC_AUX. rdx = TSC >> 32.
-      // "cc" = flags modified by SHL.
-      : "rcx", "rdx", "memory", "cc");
-#endif
-#else
-  t = Start64();
-#endif
-  return t;
-}
-
-// Returns a 32-bit timestamp with about 4 cycles less overhead than
-// Start64. Only suitable for measuring very short regions because the
-// timestamp overflows about once a second.
-inline uint32_t Start32() {
-  uint32_t t;
-#if defined(ABSL_ARCH_X86_64)
-#if defined(ABSL_OS_WIN)
-  _ReadWriteBarrier();
-  _mm_lfence();
-  _ReadWriteBarrier();
-  t = static_cast<uint32_t>(__rdtsc());
-  _ReadWriteBarrier();
-  _mm_lfence();
-  _ReadWriteBarrier();
-#else
-  asm volatile(
-      "lfence\n\t"
-      "rdtsc\n\t"
-      "lfence"
-      : "=a"(t)
-      :
-      // "memory" avoids reordering. rdx = TSC >> 32.
-      : "rdx", "memory");
-#endif
-#else
-  t = static_cast<uint32_t>(Start64());
-#endif
-  return t;
-}
-
-inline uint32_t Stop32() {
-  uint32_t t;
-#if defined(ABSL_ARCH_X86_64)
-#if defined(ABSL_OS_WIN)
-  _ReadWriteBarrier();
-  unsigned aux;
-  t = static_cast<uint32_t>(__rdtscp(&aux));
-  _ReadWriteBarrier();
-  _mm_lfence();
-  _ReadWriteBarrier();
-#else
-  // Use inline asm because __rdtscp generates code to store TSC_AUX (ecx).
-  asm volatile(
-      "rdtscp\n\t"
-      "lfence"
-      : "=a"(t)
-      :
-      // "memory" avoids reordering. rcx = TSC_AUX. rdx = TSC >> 32.
-      : "rcx", "rdx", "memory");
-#endif
-#else
-  t = static_cast<uint32_t>(Stop64());
-#endif
-  return t;
-}
-
-}  // namespace timer
-
-namespace robust_statistics {
-
-// Sorts integral values in ascending order (e.g. for Mode). About 3x faster
-// than std::sort for input distributions with very few unique values.
-template <class T>
-void CountingSort(T* values, size_t num_values) {
-  // Unique values and their frequency (similar to flat_map).
-  using Unique = std::pair<T, int>;
-  std::vector<Unique> unique;
-  for (size_t i = 0; i < num_values; ++i) {
-    const T value = values[i];
-    const auto pos =
-        std::find_if(unique.begin(), unique.end(),
-                     [value](const Unique u) { return u.first == value; });
-    if (pos == unique.end()) {
-      unique.push_back(std::make_pair(value, 1));
-    } else {
-      ++pos->second;
-    }
-  }
-
-  // Sort in ascending order of value (pair.first).
-  std::sort(unique.begin(), unique.end());
-
-  // Write that many copies of each unique value to the array.
-  T* ABSL_RANDOM_INTERNAL_RESTRICT p = values;
-  for (const auto& value_count : unique) {
-    std::fill(p, p + value_count.second, value_count.first);
-    p += value_count.second;
-  }
-  ABSL_RAW_CHECK(p == values + num_values, "Did not produce enough output");
-}
-
-// @return i in [idx_begin, idx_begin + half_count) that minimizes
-// sorted[i + half_count] - sorted[i].
-template <typename T>
-size_t MinRange(const T* const ABSL_RANDOM_INTERNAL_RESTRICT sorted,
-                const size_t idx_begin, const size_t half_count) {
-  T min_range = (std::numeric_limits<T>::max)();
-  size_t min_idx = 0;
-
-  for (size_t idx = idx_begin; idx < idx_begin + half_count; ++idx) {
-    ABSL_RAW_CHECK(sorted[idx] <= sorted[idx + half_count], "Not sorted");
-    const T range = sorted[idx + half_count] - sorted[idx];
-    if (range < min_range) {
-      min_range = range;
-      min_idx = idx;
-    }
-  }
-
-  return min_idx;
-}
-
-// Returns an estimate of the mode by calling MinRange on successively
-// halved intervals. "sorted" must be in ascending order. This is the
-// Half Sample Mode estimator proposed by Bickel in "On a fast, robust
-// estimator of the mode", with complexity O(N log N). The mode is less
-// affected by outliers in highly-skewed distributions than the median.
-// The averaging operation below assumes "T" is an unsigned integer type.
-template <typename T>
-T ModeOfSorted(const T* const ABSL_RANDOM_INTERNAL_RESTRICT sorted,
-               const size_t num_values) {
-  size_t idx_begin = 0;
-  size_t half_count = num_values / 2;
-  while (half_count > 1) {
-    idx_begin = MinRange(sorted, idx_begin, half_count);
-    half_count >>= 1;
-  }
-
-  const T x = sorted[idx_begin + 0];
-  if (half_count == 0) {
-    return x;
-  }
-  ABSL_RAW_CHECK(half_count == 1, "Should stop at half_count=1");
-  const T average = (x + sorted[idx_begin + 1] + 1) / 2;
-  return average;
-}
-
-// Returns the mode. Side effect: sorts "values".
-template <typename T>
-T Mode(T* values, const size_t num_values) {
-  CountingSort(values, num_values);
-  return ModeOfSorted(values, num_values);
-}
-
-template <typename T, size_t N>
-T Mode(T (&values)[N]) {
-  return Mode(&values[0], N);
-}
-
-// Returns the median value. Side effect: sorts "values".
-template <typename T>
-T Median(T* values, const size_t num_values) {
-  ABSL_RAW_CHECK(num_values != 0, "Empty input");
-  std::sort(values, values + num_values);
-  const size_t half = num_values / 2;
-  // Odd count: return middle
-  if (num_values % 2) {
-    return values[half];
-  }
-  // Even count: return average of middle two.
-  return (values[half] + values[half - 1] + 1) / 2;
-}
-
-// Returns a robust measure of variability.
-template <typename T>
-T MedianAbsoluteDeviation(const T* values, const size_t num_values,
-                          const T median) {
-  ABSL_RAW_CHECK(num_values != 0, "Empty input");
-  std::vector<T> abs_deviations;
-  abs_deviations.reserve(num_values);
-  for (size_t i = 0; i < num_values; ++i) {
-    const int64_t abs = std::abs(int64_t(values[i]) - int64_t(median));
-    abs_deviations.push_back(static_cast<T>(abs));
-  }
-  return Median(abs_deviations.data(), num_values);
-}
-
-}  // namespace robust_statistics
-
-// Ticks := platform-specific timer values (CPU cycles on x86). Must be
-// unsigned to guarantee wraparound on overflow. 32 bit timers are faster to
-// read than 64 bit.
-using Ticks = uint32_t;
-
-// Returns timer overhead / minimum measurable difference.
-Ticks TimerResolution() {
-  // Nested loop avoids exceeding stack/L1 capacity.
-  Ticks repetitions[Params::kTimerSamples];
-  for (size_t rep = 0; rep < Params::kTimerSamples; ++rep) {
-    Ticks samples[Params::kTimerSamples];
-    for (size_t i = 0; i < Params::kTimerSamples; ++i) {
-      const Ticks t0 = timer::Start32();
-      const Ticks t1 = timer::Stop32();
-      samples[i] = t1 - t0;
-    }
-    repetitions[rep] = robust_statistics::Mode(samples);
-  }
-  return robust_statistics::Mode(repetitions);
-}
-
-static const Ticks timer_resolution = TimerResolution();
-
-// Estimates the expected value of "lambda" values with a variable number of
-// samples until the variability "rel_mad" is less than "max_rel_mad".
-template <class Lambda>
-Ticks SampleUntilStable(const double max_rel_mad, double* rel_mad,
-                        const Params& p, const Lambda& lambda) {
-  auto measure_duration = [&lambda]() -> Ticks {
-    const Ticks t0 = timer::Start32();
-    lambda();
-    const Ticks t1 = timer::Stop32();
-    return t1 - t0;
-  };
-
-  // Choose initial samples_per_eval based on a single estimated duration.
-  Ticks est = measure_duration();
-  static const double ticks_per_second = InvariantTicksPerSecond();
-  const size_t ticks_per_eval = ticks_per_second * p.seconds_per_eval;
-  size_t samples_per_eval = ticks_per_eval / est;
-  samples_per_eval = (std::max)(samples_per_eval, p.min_samples_per_eval);
-
-  std::vector<Ticks> samples;
-  samples.reserve(1 + samples_per_eval);
-  samples.push_back(est);
-
-  // Percentage is too strict for tiny differences, so also allow a small
-  // absolute "median absolute deviation".
-  const Ticks max_abs_mad = (timer_resolution + 99) / 100;
-  *rel_mad = 0.0;  // ensure initialized
-
-  for (size_t eval = 0; eval < p.max_evals; ++eval, samples_per_eval *= 2) {
-    samples.reserve(samples.size() + samples_per_eval);
-    for (size_t i = 0; i < samples_per_eval; ++i) {
-      const Ticks r = measure_duration();
-      samples.push_back(r);
-    }
-
-    if (samples.size() >= p.min_mode_samples) {
-      est = robust_statistics::Mode(samples.data(), samples.size());
-    } else {
-      // For "few" (depends also on the variance) samples, Median is safer.
-      est = robust_statistics::Median(samples.data(), samples.size());
-    }
-    ABSL_RAW_CHECK(est != 0, "Estimator returned zero duration");
-
-    // Median absolute deviation (mad) is a robust measure of 'variability'.
-    const Ticks abs_mad = robust_statistics::MedianAbsoluteDeviation(
-        samples.data(), samples.size(), est);
-    *rel_mad = static_cast<double>(static_cast<int>(abs_mad)) / est;
-
-    if (*rel_mad <= max_rel_mad || abs_mad <= max_abs_mad) {
-      if (p.verbose) {
-        ABSL_RAW_LOG(INFO,
-                     "%6zu samples => %5u (abs_mad=%4u, rel_mad=%4.2f%%)\n",
-                     samples.size(), est, abs_mad, *rel_mad * 100.0);
-      }
-      return est;
-    }
-  }
-
-  if (p.verbose) {
-    ABSL_RAW_LOG(WARNING,
-                 "rel_mad=%4.2f%% still exceeds %4.2f%% after %6zu samples.\n",
-                 *rel_mad * 100.0, max_rel_mad * 100.0, samples.size());
-  }
-  return est;
-}
-
-using InputVec = std::vector<FuncInput>;
-
-// Returns vector of unique input values.
-InputVec UniqueInputs(const FuncInput* inputs, const size_t num_inputs) {
-  InputVec unique(inputs, inputs + num_inputs);
-  std::sort(unique.begin(), unique.end());
-  unique.erase(std::unique(unique.begin(), unique.end()), unique.end());
-  return unique;
-}
-
-// Returns how often we need to call func for sufficient precision, or zero
-// on failure (e.g. the elapsed time is too long for a 32-bit tick count).
-size_t NumSkip(const Func func, const void* arg, const InputVec& unique,
-               const Params& p) {
-  // Min elapsed ticks for any input.
-  Ticks min_duration = ~0u;
-
-  for (const FuncInput input : unique) {
-    // Make sure a 32-bit timer is sufficient.
-    const uint64_t t0 = timer::Start64();
-    PreventElision(func(arg, input));
-    const uint64_t t1 = timer::Stop64();
-    const uint64_t elapsed = t1 - t0;
-    if (elapsed >= (1ULL << 30)) {
-      ABSL_RAW_LOG(WARNING,
-                   "Measurement failed: need 64-bit timer for input=%zu\n",
-                   static_cast<size_t>(input));
-      return 0;
-    }
-
-    double rel_mad;
-    const Ticks total = SampleUntilStable(
-        p.target_rel_mad, &rel_mad, p,
-        [func, arg, input]() { PreventElision(func(arg, input)); });
-    min_duration = (std::min)(min_duration, total - timer_resolution);
-  }
-
-  // Number of repetitions required to reach the target resolution.
-  const size_t max_skip = p.precision_divisor;
-  // Number of repetitions given the estimated duration.
-  const size_t num_skip =
-      min_duration == 0 ? 0 : (max_skip + min_duration - 1) / min_duration;
-  if (p.verbose) {
-    ABSL_RAW_LOG(INFO, "res=%u max_skip=%zu min_dur=%u num_skip=%zu\n",
-                 timer_resolution, max_skip, min_duration, num_skip);
-  }
-  return num_skip;
-}
-
-// Replicates inputs until we can omit "num_skip" occurrences of an input.
-InputVec ReplicateInputs(const FuncInput* inputs, const size_t num_inputs,
-                         const size_t num_unique, const size_t num_skip,
-                         const Params& p) {
-  InputVec full;
-  if (num_unique == 1) {
-    full.assign(p.subset_ratio * num_skip, inputs[0]);
-    return full;
-  }
-
-  full.reserve(p.subset_ratio * num_skip * num_inputs);
-  for (size_t i = 0; i < p.subset_ratio * num_skip; ++i) {
-    full.insert(full.end(), inputs, inputs + num_inputs);
-  }
-  absl::random_internal::randen_engine<uint32_t> rng;
-  std::shuffle(full.begin(), full.end(), rng);
-  return full;
-}
-
-// Copies the "full" to "subset" in the same order, but with "num_skip"
-// randomly selected occurrences of "input_to_skip" removed.
-void FillSubset(const InputVec& full, const FuncInput input_to_skip,
-                const size_t num_skip, InputVec* subset) {
-  const size_t count = std::count(full.begin(), full.end(), input_to_skip);
-  // Generate num_skip random indices: which occurrence to skip.
-  std::vector<uint32_t> omit;
-  // Replacement for std::iota, not yet available in MSVC builds.
-  omit.reserve(count);
-  for (size_t i = 0; i < count; ++i) {
-    omit.push_back(i);
-  }
-  // omit[] is the same on every call, but that's OK because they identify the
-  // Nth instance of input_to_skip, so the position within full[] differs.
-  absl::random_internal::randen_engine<uint32_t> rng;
-  std::shuffle(omit.begin(), omit.end(), rng);
-  omit.resize(num_skip);
-  std::sort(omit.begin(), omit.end());
-
-  uint32_t occurrence = ~0u;  // 0 after preincrement
-  size_t idx_omit = 0;        // cursor within omit[]
-  size_t idx_subset = 0;      // cursor within *subset
-  for (const FuncInput next : full) {
-    if (next == input_to_skip) {
-      ++occurrence;
-      // Haven't removed enough already
-      if (idx_omit < num_skip) {
-        // This one is up for removal
-        if (occurrence == omit[idx_omit]) {
-          ++idx_omit;
-          continue;
-        }
-      }
-    }
-    if (idx_subset < subset->size()) {
-      (*subset)[idx_subset++] = next;
-    }
-  }
-  ABSL_RAW_CHECK(idx_subset == subset->size(), "idx_subset not at end");
-  ABSL_RAW_CHECK(idx_omit == omit.size(), "idx_omit not at end");
-  ABSL_RAW_CHECK(occurrence == count - 1, "occurrence not at end");
-}
-
-// Returns total ticks elapsed for all inputs.
-Ticks TotalDuration(const Func func, const void* arg, const InputVec* inputs,
-                    const Params& p, double* max_rel_mad) {
-  double rel_mad;
-  const Ticks duration =
-      SampleUntilStable(p.target_rel_mad, &rel_mad, p, [func, arg, inputs]() {
-        for (const FuncInput input : *inputs) {
-          PreventElision(func(arg, input));
-        }
-      });
-  *max_rel_mad = (std::max)(*max_rel_mad, rel_mad);
-  return duration;
-}
-
-// (Nearly) empty Func for measuring timer overhead/resolution.
-ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE FuncOutput
-EmptyFunc(const void* arg, const FuncInput input) {
-  return input;
-}
-
-// Returns overhead of accessing inputs[] and calling a function; this will
-// be deducted from future TotalDuration return values.
-Ticks Overhead(const void* arg, const InputVec* inputs, const Params& p) {
-  double rel_mad;
-  // Zero tolerance because repeatability is crucial and EmptyFunc is fast.
-  return SampleUntilStable(0.0, &rel_mad, p, [arg, inputs]() {
-    for (const FuncInput input : *inputs) {
-      PreventElision(EmptyFunc(arg, input));
-    }
-  });
-}
-
-}  // namespace
-
-void PinThreadToCPU(int cpu) {
-  // We might migrate to another CPU before pinning below, but at least cpu
-  // will be one of the CPUs on which this thread ran.
-#if defined(ABSL_OS_WIN)
-  if (cpu < 0) {
-    cpu = static_cast<int>(GetCurrentProcessorNumber());
-    ABSL_RAW_CHECK(cpu >= 0, "PinThreadToCPU detect failed");
-    if (cpu >= 64) {
-      // NOTE: On wine, at least, GetCurrentProcessorNumber() sometimes returns
-      // a value > 64, which is out of range. When this happens, log a message
-      // and don't set a cpu affinity.
-      ABSL_RAW_LOG(ERROR, "Invalid CPU number: %d", cpu);
-      return;
-    }
-  } else if (cpu >= 64) {
-    // User specified an explicit CPU affinity > the valid range.
-    ABSL_RAW_LOG(FATAL, "Invalid CPU number: %d", cpu);
-  }
-  const DWORD_PTR prev = SetThreadAffinityMask(GetCurrentThread(), 1ULL << cpu);
-  ABSL_RAW_CHECK(prev != 0, "SetAffinity failed");
-#elif defined(ABSL_OS_LINUX) && !defined(ABSL_OS_ANDROID)
-  if (cpu < 0) {
-    cpu = sched_getcpu();
-    ABSL_RAW_CHECK(cpu >= 0, "PinThreadToCPU detect failed");
-  }
-  const pid_t pid = 0;  // current thread
-  cpu_set_t set;
-  CPU_ZERO(&set);
-  CPU_SET(cpu, &set);
-  const int err = sched_setaffinity(pid, sizeof(set), &set);
-  ABSL_RAW_CHECK(err == 0, "SetAffinity failed");
-#endif
-}
-
-// Returns tick rate. Invariant means the tick counter frequency is independent
-// of CPU throttling or sleep. May be expensive, caller should cache the result.
-double InvariantTicksPerSecond() {
-#if defined(ABSL_ARCH_PPC)
-  return __ppc_get_timebase_freq();
-#elif defined(ABSL_ARCH_X86_64)
-  // We assume the TSC is invariant; it is on all recent Intel/AMD CPUs.
-  return platform::NominalClockRate();
-#else
-  // Fall back to clock_gettime nanoseconds.
-  return 1E9;
-#endif
-}
-
-size_t MeasureImpl(const Func func, const void* arg, const size_t num_skip,
-                   const InputVec& unique, const InputVec& full,
-                   const Params& p, Result* results) {
-  const float mul = 1.0f / static_cast<int>(num_skip);
-
-  InputVec subset(full.size() - num_skip);
-  const Ticks overhead = Overhead(arg, &full, p);
-  const Ticks overhead_skip = Overhead(arg, &subset, p);
-  if (overhead < overhead_skip) {
-    ABSL_RAW_LOG(WARNING, "Measurement failed: overhead %u < %u\n", overhead,
-                 overhead_skip);
-    return 0;
-  }
-
-  if (p.verbose) {
-    ABSL_RAW_LOG(INFO, "#inputs=%5zu,%5zu overhead=%5u,%5u\n", full.size(),
-                 subset.size(), overhead, overhead_skip);
-  }
-
-  double max_rel_mad = 0.0;
-  const Ticks total = TotalDuration(func, arg, &full, p, &max_rel_mad);
-
-  for (size_t i = 0; i < unique.size(); ++i) {
-    FillSubset(full, unique[i], num_skip, &subset);
-    const Ticks total_skip = TotalDuration(func, arg, &subset, p, &max_rel_mad);
-
-    if (total < total_skip) {
-      ABSL_RAW_LOG(WARNING, "Measurement failed: total %u < %u\n", total,
-                   total_skip);
-      return 0;
-    }
-
-    const Ticks duration = (total - overhead) - (total_skip - overhead_skip);
-    results[i].input = unique[i];
-    results[i].ticks = duration * mul;
-    results[i].variability = max_rel_mad;
-  }
-
-  return unique.size();
-}
-
-size_t Measure(const Func func, const void* arg, const FuncInput* inputs,
-               const size_t num_inputs, Result* results, const Params& p) {
-  ABSL_RAW_CHECK(num_inputs != 0, "No inputs");
-
-  const InputVec unique = UniqueInputs(inputs, num_inputs);
-  const size_t num_skip = NumSkip(func, arg, unique, p);  // never 0
-  if (num_skip == 0) return 0;  // NumSkip already printed error message
-
-  const InputVec full =
-      ReplicateInputs(inputs, num_inputs, unique.size(), num_skip, p);
-
-  // MeasureImpl may fail up to p.max_measure_retries times.
-  for (size_t i = 0; i < p.max_measure_retries; i++) {
-    auto result = MeasureImpl(func, arg, num_skip, unique, full, p, results);
-    if (result != 0) {
-      return result;
-    }
-  }
-  // All retries failed. (Unusual)
-  return 0;
-}
-
-}  // namespace random_internal_nanobenchmark
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/nanobenchmark.h b/third_party/abseil_cpp/absl/random/internal/nanobenchmark.h
deleted file mode 100644
index a5097ba27b..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/nanobenchmark.h
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
-#define ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
-
-// Benchmarks functions of a single integer argument with realistic branch
-// prediction hit rates. Uses a robust estimator to summarize the measurements.
-// The precision is about 0.2%.
-//
-// Examples: see nanobenchmark_test.cc.
-//
-// Background: Microbenchmarks such as http://github.com/google/benchmark
-// can measure elapsed times on the order of a microsecond. Shorter functions
-// are typically measured by repeating them thousands of times and dividing
-// the total elapsed time by this count. Unfortunately, repetition (especially
-// with the same input parameter!) influences the runtime. In time-critical
-// code, it is reasonable to expect warm instruction/data caches and TLBs,
-// but a perfect record of which branches will be taken is unrealistic.
-// Unless the application also repeatedly invokes the measured function with
-// the same parameter, the benchmark is measuring something very different -
-// a best-case result, almost as if the parameter were made a compile-time
-// constant. This may lead to erroneous conclusions about branch-heavy
-// algorithms outperforming branch-free alternatives.
-//
-// Our approach differs in three ways. Adding fences to the timer functions
-// reduces variability due to instruction reordering, improving the timer
-// resolution to about 40 CPU cycles. However, shorter functions must still
-// be invoked repeatedly. For more realistic branch prediction performance,
-// we vary the input parameter according to a user-specified distribution.
-// Thus, instead of VaryInputs(Measure(Repeat(func))), we change the
-// loop nesting to Measure(Repeat(VaryInputs(func))). We also estimate the
-// central tendency of the measurement samples with the "half sample mode",
-// which is more robust to outliers and skewed data than the mean or median.
-
-// NOTE: for compatibility with multiple translation units compiled with
-// distinct flags, avoid #including headers that define functions.
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal_nanobenchmark {
-
-// Input influencing the function being measured (e.g. number of bytes to copy).
-using FuncInput = size_t;
-
-// "Proof of work" returned by Func to ensure the compiler does not elide it.
-using FuncOutput = uint64_t;
-
-// Function to measure: either 1) a captureless lambda or function with two
-// arguments or 2) a lambda with capture, in which case the first argument
-// is reserved for use by MeasureClosure.
-using Func = FuncOutput (*)(const void*, FuncInput);
-
-// Internal parameters that determine precision/resolution/measuring time.
-struct Params {
-  // For measuring timer overhead/resolution. Used in a nested loop =>
-  // quadratic time, acceptable because we know timer overhead is "low".
-  // constexpr because this is used to define array bounds.
-  static constexpr size_t kTimerSamples = 256;
-
-  // Best-case precision, expressed as a divisor of the timer resolution.
-  // Larger => more calls to Func and higher precision.
-  size_t precision_divisor = 1024;
-
-  // Ratio between full and subset input distribution sizes. Cannot be less
-  // than 2; larger values increase measurement time but more faithfully
-  // model the given input distribution.
-  size_t subset_ratio = 2;
-
-  // Together with the estimated Func duration, determines how many times to
-  // call Func before checking the sample variability. Larger values increase
-  // measurement time, memory/cache use and precision.
-  double seconds_per_eval = 4E-3;
-
-  // The minimum number of samples before estimating the central tendency.
-  size_t min_samples_per_eval = 7;
-
-  // The mode is better than median for estimating the central tendency of
-  // skewed/fat-tailed distributions, but it requires sufficient samples
-  // relative to the width of half-ranges.
-  size_t min_mode_samples = 64;
-
-  // Maximum permissible variability (= median absolute deviation / center).
-  double target_rel_mad = 0.002;
-
-  // Abort after this many evals without reaching target_rel_mad. This
-  // prevents infinite loops.
-  size_t max_evals = 9;
-
-  // Retry the measure loop up to this many times.
-  size_t max_measure_retries = 2;
-
-  // Whether to print additional statistics to stdout.
-  bool verbose = true;
-};
-
-// Measurement result for each unique input.
-struct Result {
-  FuncInput input;
-
-  // Robust estimate (mode or median) of duration.
-  float ticks;
-
-  // Measure of variability (median absolute deviation relative to "ticks").
-  float variability;
-};
-
-// Ensures the thread is running on the specified cpu, and no others.
-// Reduces noise due to desynchronized socket RDTSC and context switches.
-// If "cpu" is negative, pin to the currently running core.
-void PinThreadToCPU(const int cpu = -1);
-
-// Returns tick rate, useful for converting measurements to seconds. Invariant
-// means the tick counter frequency is independent of CPU throttling or sleep.
-// This call may be expensive, callers should cache the result.
-double InvariantTicksPerSecond();
-
-// Precisely measures the number of ticks elapsed when calling "func" with the
-// given inputs, shuffled to ensure realistic branch prediction hit rates.
-//
-// "func" returns a 'proof of work' to ensure its computations are not elided.
-// "arg" is passed to Func, or reserved for internal use by MeasureClosure.
-// "inputs" is an array of "num_inputs" (not necessarily unique) arguments to
-//   "func". The values should be chosen to maximize coverage of "func". This
-//   represents a distribution, so a value's frequency should reflect its
-//   probability in the real application. Order does not matter; for example, a
-//   uniform distribution over [0, 4) could be represented as {3,0,2,1}.
-// Returns how many Result were written to "results": one per unique input, or
-//   zero if the measurement failed (an error message goes to stderr).
-size_t Measure(const Func func, const void* arg, const FuncInput* inputs,
-               const size_t num_inputs, Result* results,
-               const Params& p = Params());
-
-// Calls operator() of the given closure (lambda function).
-template <class Closure>
-static FuncOutput CallClosure(const void* f, const FuncInput input) {
-  return (*reinterpret_cast<const Closure*>(f))(input);
-}
-
-// Same as Measure, except "closure" is typically a lambda function of
-// FuncInput -> FuncOutput with a capture list.
-template <class Closure>
-static inline size_t MeasureClosure(const Closure& closure,
-                                    const FuncInput* inputs,
-                                    const size_t num_inputs, Result* results,
-                                    const Params& p = Params()) {
-  return Measure(reinterpret_cast<Func>(&CallClosure<Closure>),
-                 reinterpret_cast<const void*>(&closure), inputs, num_inputs,
-                 results, p);
-}
-
-}  // namespace random_internal_nanobenchmark
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/nanobenchmark_test.cc b/third_party/abseil_cpp/absl/random/internal/nanobenchmark_test.cc
deleted file mode 100644
index f1571e269f..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/nanobenchmark_test.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/nanobenchmark.h"
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/numbers.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal_nanobenchmark {
-namespace {
-
-uint64_t Div(const void*, FuncInput in) {
-  // Here we're measuring the throughput because benchmark invocations are
-  // independent.
-  const int64_t d1 = 0xFFFFFFFFFFll / int64_t(in);  // IDIV
-  return d1;
-}
-
-template <size_t N>
-void MeasureDiv(const FuncInput (&inputs)[N]) {
-  Result results[N];
-  Params params;
-  params.max_evals = 6;  // avoid test timeout
-  const size_t num_results = Measure(&Div, nullptr, inputs, N, results, params);
-  if (num_results == 0) {
-    ABSL_RAW_LOG(
-        WARNING,
-        "WARNING: Measurement failed, should not happen when using "
-        "PinThreadToCPU unless the region to measure takes > 1 second.\n");
-    return;
-  }
-  for (size_t i = 0; i < num_results; ++i) {
-    ABSL_RAW_LOG(INFO, "%5zu: %6.2f ticks; MAD=%4.2f%%\n", results[i].input,
-                 results[i].ticks, results[i].variability * 100.0);
-    ABSL_RAW_CHECK(results[i].ticks != 0.0f, "Zero duration");
-  }
-}
-
-void RunAll(const int argc, char* argv[]) {
-  // Avoid migrating between cores - important on multi-socket systems.
-  int cpu = -1;
-  if (argc == 2) {
-    if (!absl::SimpleAtoi(argv[1], &cpu)) {
-      ABSL_RAW_LOG(FATAL, "The optional argument must be a CPU number >= 0.\n");
-    }
-  }
-  PinThreadToCPU(cpu);
-
-  // unpredictable == 1 but the compiler doesn't know that.
-  const FuncInput unpredictable = argc != 999;
-  static const FuncInput inputs[] = {unpredictable * 10, unpredictable * 100};
-
-  MeasureDiv(inputs);
-}
-
-}  // namespace
-}  // namespace random_internal_nanobenchmark
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-int main(int argc, char* argv[]) {
-  absl::random_internal_nanobenchmark::RunAll(argc, argv);
-  return 0;
-}
diff --git a/third_party/abseil_cpp/absl/random/internal/nonsecure_base.h b/third_party/abseil_cpp/absl/random/internal/nonsecure_base.h
deleted file mode 100644
index 730fa2ea12..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/nonsecure_base.h
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_
-#define ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_
-
-#include <algorithm>
-#include <cstdint>
-#include <iostream>
-#include <iterator>
-#include <random>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include "absl/base/macros.h"
-#include "absl/meta/type_traits.h"
-#include "absl/random/internal/pool_urbg.h"
-#include "absl/random/internal/salted_seed_seq.h"
-#include "absl/random/internal/seed_material.h"
-#include "absl/types/optional.h"
-#include "absl/types/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// Each instance of NonsecureURBGBase<URBG> will be seeded by variates produced
-// by a thread-unique URBG-instance.
-template <typename URBG>
-class NonsecureURBGBase {
- public:
-  using result_type = typename URBG::result_type;
-
-  // Default constructor
-  NonsecureURBGBase() : urbg_(ConstructURBG()) {}
-
-  // Copy disallowed, move allowed.
-  NonsecureURBGBase(const NonsecureURBGBase&) = delete;
-  NonsecureURBGBase& operator=(const NonsecureURBGBase&) = delete;
-  NonsecureURBGBase(NonsecureURBGBase&&) = default;
-  NonsecureURBGBase& operator=(NonsecureURBGBase&&) = default;
-
-  // Constructor using a seed
-  template <class SSeq, typename = typename absl::enable_if_t<
-                            !std::is_same<SSeq, NonsecureURBGBase>::value>>
-  explicit NonsecureURBGBase(SSeq&& seq)
-      : urbg_(ConstructURBG(std::forward<SSeq>(seq))) {}
-
-  // Note: on MSVC, min() or max() can be interpreted as MIN() or MAX(), so we
-  // enclose min() or max() in parens as (min)() and (max)().
-  // Additionally, clang-format requires no space before this construction.
-
-  // NonsecureURBGBase::min()
-  static constexpr result_type(min)() { return (URBG::min)(); }
-
-  // NonsecureURBGBase::max()
-  static constexpr result_type(max)() { return (URBG::max)(); }
-
-  // NonsecureURBGBase::operator()()
-  result_type operator()() { return urbg_(); }
-
-  // NonsecureURBGBase::discard()
-  void discard(unsigned long long values) {  // NOLINT(runtime/int)
-    urbg_.discard(values);
-  }
-
-  bool operator==(const NonsecureURBGBase& other) const {
-    return urbg_ == other.urbg_;
-  }
-
-  bool operator!=(const NonsecureURBGBase& other) const {
-    return !(urbg_ == other.urbg_);
-  }
-
- private:
-  // Seeder is a custom seed sequence type where generate() fills the provided
-  // buffer via the RandenPool entropy source.
-  struct Seeder {
-    using result_type = uint32_t;
-
-    size_t size() { return 0; }
-
-    template <typename OutIterator>
-    void param(OutIterator) const {}
-
-    template <typename RandomAccessIterator>
-    void generate(RandomAccessIterator begin, RandomAccessIterator end) {
-      if (begin != end) {
-        // begin, end must be random access iterators assignable from uint32_t.
-        generate_impl(
-            std::integral_constant<bool, sizeof(*begin) == sizeof(uint32_t)>{},
-            begin, end);
-      }
-    }
-
-    // Commonly, generate is invoked with a pointer to a buffer which
-    // can be cast to a uint32_t.
-    template <typename RandomAccessIterator>
-    void generate_impl(std::integral_constant<bool, true>,
-                       RandomAccessIterator begin, RandomAccessIterator end) {
-      auto buffer = absl::MakeSpan(begin, end);
-      auto target = absl::MakeSpan(reinterpret_cast<uint32_t*>(buffer.data()),
-                                   buffer.size());
-      RandenPool<uint32_t>::Fill(target);
-    }
-
-    // The non-uint32_t case should be uncommon, and involves an extra copy,
-    // filling the uint32_t buffer and then mixing into the output.
-    template <typename RandomAccessIterator>
-    void generate_impl(std::integral_constant<bool, false>,
-                       RandomAccessIterator begin, RandomAccessIterator end) {
-      const size_t n = std::distance(begin, end);
-      absl::InlinedVector<uint32_t, 8> data(n, 0);
-      RandenPool<uint32_t>::Fill(absl::MakeSpan(data.begin(), data.end()));
-      std::copy(std::begin(data), std::end(data), begin);
-    }
-  };
-
-  static URBG ConstructURBG() {
-    Seeder seeder;
-    return URBG(seeder);
-  }
-
-  template <typename SSeq>
-  static URBG ConstructURBG(SSeq&& seq) {  // NOLINT(runtime/references)
-    auto salted_seq =
-        random_internal::MakeSaltedSeedSeq(std::forward<SSeq>(seq));
-    return URBG(salted_seq);
-  }
-
-  URBG urbg_;
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/nonsecure_base_test.cc b/third_party/abseil_cpp/absl/random/internal/nonsecure_base_test.cc
deleted file mode 100644
index 698027fc6e..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/nonsecure_base_test.cc
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/nonsecure_base.h"
-
-#include <algorithm>
-#include <iostream>
-#include <memory>
-#include <random>
-#include <sstream>
-
-#include "gtest/gtest.h"
-#include "absl/random/distributions.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-using ExampleNonsecureURBG =
-    absl::random_internal::NonsecureURBGBase<std::mt19937>;
-
-template <typename T>
-void Use(const T&) {}
-
-}  // namespace
-
-TEST(NonsecureURBGBase, DefaultConstructorIsValid) {
-  ExampleNonsecureURBG urbg;
-}
-
-// Ensure that the recommended template-instantiations are valid.
-TEST(RecommendedTemplates, CanBeConstructed) {
-  absl::BitGen default_generator;
-  absl::InsecureBitGen insecure_generator;
-}
-
-TEST(RecommendedTemplates, CanDiscardValues) {
-  absl::BitGen default_generator;
-  absl::InsecureBitGen insecure_generator;
-
-  default_generator.discard(5);
-  insecure_generator.discard(5);
-}
-
-TEST(NonsecureURBGBase, StandardInterface) {
-  // Names after definition of [rand.req.urbg] in C++ standard.
-  // e us a value of E
-  // v is a lvalue of E
-  // x, y are possibly const values of E
-  // s is a value of T
-  // q is a value satisfying requirements of seed_sequence
-  // z is a value of type unsigned long long
-  // os is a some specialization of basic_ostream
-  // is is a some specialization of basic_istream
-
-  using E = absl::random_internal::NonsecureURBGBase<std::minstd_rand>;
-
-  using T = typename E::result_type;
-
-  static_assert(!std::is_copy_constructible<E>::value,
-                "NonsecureURBGBase should not be copy constructible");
-
-  static_assert(!absl::is_copy_assignable<E>::value,
-                "NonsecureURBGBase should not be copy assignable");
-
-  static_assert(std::is_move_constructible<E>::value,
-                "NonsecureURBGBase should be move constructible");
-
-  static_assert(absl::is_move_assignable<E>::value,
-                "NonsecureURBGBase should be move assignable");
-
-  static_assert(std::is_same<decltype(std::declval<E>()()), T>::value,
-                "return type of operator() must be result_type");
-
-  {
-    const E x, y;
-    Use(x);
-    Use(y);
-
-    static_assert(std::is_same<decltype(x == y), bool>::value,
-                  "return type of operator== must be bool");
-
-    static_assert(std::is_same<decltype(x != y), bool>::value,
-                  "return type of operator== must be bool");
-  }
-
-  E e;
-  std::seed_seq q{1, 2, 3};
-
-  E{};
-  E{q};
-
-  // Copy constructor not supported.
-  // E{x};
-
-  // result_type seed constructor not supported.
-  // E{T{1}};
-
-  // Move constructors are supported.
-  {
-    E tmp(q);
-    E m = std::move(tmp);
-    E n(std::move(m));
-    EXPECT_TRUE(e != n);
-  }
-
-  // Comparisons work.
-  {
-    // MSVC emits error 2718 when using EXPECT_EQ(e, x)
-    //  * actual parameter with __declspec(align('#')) won't be aligned
-    E a(q);
-    E b(q);
-
-    EXPECT_TRUE(a != e);
-    EXPECT_TRUE(a == b);
-
-    a();
-    EXPECT_TRUE(a != b);
-  }
-
-  // e.seed(s) not supported.
-
-  // [rand.req.eng] specifies the parameter as 'unsigned long long'
-  // e.discard(unsigned long long) is supported.
-  unsigned long long z = 1;  // NOLINT(runtime/int)
-  e.discard(z);
-}
-
-TEST(NonsecureURBGBase, SeedSeqConstructorIsValid) {
-  std::seed_seq seq;
-  ExampleNonsecureURBG rbg(seq);
-}
-
-TEST(NonsecureURBGBase, CompatibleWithDistributionUtils) {
-  ExampleNonsecureURBG rbg;
-
-  absl::Uniform(rbg, 0, 100);
-  absl::Uniform(rbg, 0.5, 0.7);
-  absl::Poisson<uint32_t>(rbg);
-  absl::Exponential<float>(rbg);
-}
-
-TEST(NonsecureURBGBase, CompatibleWithStdDistributions) {
-  ExampleNonsecureURBG rbg;
-
-  // Cast to void to suppress [[nodiscard]] warnings
-  static_cast<void>(std::uniform_int_distribution<uint32_t>(0, 100)(rbg));
-  static_cast<void>(std::uniform_real_distribution<float>()(rbg));
-  static_cast<void>(std::bernoulli_distribution(0.2)(rbg));
-}
-
-TEST(NonsecureURBGBase, ConsecutiveDefaultInstancesYieldUniqueVariates) {
-  const size_t kNumSamples = 128;
-
-  ExampleNonsecureURBG rbg1;
-  ExampleNonsecureURBG rbg2;
-
-  for (size_t i = 0; i < kNumSamples; i++) {
-    EXPECT_NE(rbg1(), rbg2());
-  }
-}
-
-TEST(NonsecureURBGBase, EqualSeedSequencesYieldEqualVariates) {
-  std::seed_seq seq;
-
-  ExampleNonsecureURBG rbg1(seq);
-  ExampleNonsecureURBG rbg2(seq);
-
-  // ExampleNonsecureURBG rbg3({1, 2, 3});  // Should not compile.
-
-  for (uint32_t i = 0; i < 1000; i++) {
-    EXPECT_EQ(rbg1(), rbg2());
-  }
-
-  rbg1.discard(100);
-  rbg2.discard(100);
-
-  // The sequences should continue after discarding
-  for (uint32_t i = 0; i < 1000; i++) {
-    EXPECT_EQ(rbg1(), rbg2());
-  }
-}
-
-// This is a PRNG-compatible type specifically designed to test
-// that NonsecureURBGBase::Seeder can correctly handle iterators
-// to arbitrary non-uint32_t size types.
-template <typename T>
-struct SeederTestEngine {
-  using result_type = T;
-
-  static constexpr result_type(min)() {
-    return (std::numeric_limits<result_type>::min)();
-  }
-  static constexpr result_type(max)() {
-    return (std::numeric_limits<result_type>::max)();
-  }
-
-  template <class SeedSequence,
-            typename = typename absl::enable_if_t<
-                !std::is_same<SeedSequence, SeederTestEngine>::value>>
-  explicit SeederTestEngine(SeedSequence&& seq) {
-    seed(seq);
-  }
-
-  SeederTestEngine(const SeederTestEngine&) = default;
-  SeederTestEngine& operator=(const SeederTestEngine&) = default;
-  SeederTestEngine(SeederTestEngine&&) = default;
-  SeederTestEngine& operator=(SeederTestEngine&&) = default;
-
-  result_type operator()() { return state[0]; }
-
-  template <class SeedSequence>
-  void seed(SeedSequence&& seq) {
-    std::fill(std::begin(state), std::end(state), T(0));
-    seq.generate(std::begin(state), std::end(state));
-  }
-
-  T state[2];
-};
-
-TEST(NonsecureURBGBase, SeederWorksForU32) {
-  using U32 =
-      absl::random_internal::NonsecureURBGBase<SeederTestEngine<uint32_t>>;
-  U32 x;
-  EXPECT_NE(0, x());
-}
-
-TEST(NonsecureURBGBase, SeederWorksForU64) {
-  using U64 =
-      absl::random_internal::NonsecureURBGBase<SeederTestEngine<uint64_t>>;
-
-  U64 x;
-  EXPECT_NE(0, x());
-}
diff --git a/third_party/abseil_cpp/absl/random/internal/pcg_engine.h b/third_party/abseil_cpp/absl/random/internal/pcg_engine.h
deleted file mode 100644
index 53c23fe1b4..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/pcg_engine.h
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_
-#define ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_
-
-#include <type_traits>
-
-#include "absl/base/config.h"
-#include "absl/meta/type_traits.h"
-#include "absl/numeric/int128.h"
-#include "absl/random/internal/fastmath.h"
-#include "absl/random/internal/iostream_state_saver.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// pcg_engine is a simplified implementation of Melissa O'Neil's PCG engine in
-// C++.  PCG combines a linear congruential generator (LCG) with output state
-// mixing functions to generate each random variate.  pcg_engine supports only a
-// single sequence (oneseq), and does not support streams.
-//
-// pcg_engine is parameterized by two types:
-//   Params, which provides the multiplier and increment values;
-//   Mix, which mixes the state into the result.
-//
-template <typename Params, typename Mix>
-class pcg_engine {
-  static_assert(std::is_same<typename Params::state_type,
-                             typename Mix::state_type>::value,
-                "Class-template absl::pcg_engine must be parameterized by "
-                "Params and Mix with identical state_type");
-
-  static_assert(std::is_unsigned<typename Mix::result_type>::value,
-                "Class-template absl::pcg_engine must be parameterized by "
-                "an unsigned Mix::result_type");
-
-  using params_type = Params;
-  using mix_type = Mix;
-  using state_type = typename Mix::state_type;
-
- public:
-  // C++11 URBG interface:
-  using result_type = typename Mix::result_type;
-
-  static constexpr result_type(min)() {
-    return (std::numeric_limits<result_type>::min)();
-  }
-
-  static constexpr result_type(max)() {
-    return (std::numeric_limits<result_type>::max)();
-  }
-
-  explicit pcg_engine(uint64_t seed_value = 0) { seed(seed_value); }
-
-  template <class SeedSequence,
-            typename = typename absl::enable_if_t<
-                !std::is_same<SeedSequence, pcg_engine>::value>>
-  explicit pcg_engine(SeedSequence&& seq) {
-    seed(seq);
-  }
-
-  pcg_engine(const pcg_engine&) = default;
-  pcg_engine& operator=(const pcg_engine&) = default;
-  pcg_engine(pcg_engine&&) = default;
-  pcg_engine& operator=(pcg_engine&&) = default;
-
-  result_type operator()() {
-    // Advance the LCG state, always using the new value to generate the output.
-    state_ = lcg(state_);
-    return Mix{}(state_);
-  }
-
-  void seed(uint64_t seed_value = 0) {
-    state_type tmp = seed_value;
-    state_ = lcg(tmp + Params::increment());
-  }
-
-  template <class SeedSequence>
-  typename absl::enable_if_t<
-      !std::is_convertible<SeedSequence, uint64_t>::value, void>
-  seed(SeedSequence&& seq) {
-    reseed(seq);
-  }
-
-  void discard(uint64_t count) { state_ = advance(state_, count); }
-
-  bool operator==(const pcg_engine& other) const {
-    return state_ == other.state_;
-  }
-
-  bool operator!=(const pcg_engine& other) const { return !(*this == other); }
-
-  template <class CharT, class Traits>
-  friend typename absl::enable_if_t<(sizeof(state_type) == 16),
-                                    std::basic_ostream<CharT, Traits>&>
-  operator<<(
-      std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-      const pcg_engine& engine) {
-    auto saver = random_internal::make_ostream_state_saver(os);
-    random_internal::stream_u128_helper<state_type> helper;
-    helper.write(pcg_engine::params_type::multiplier(), os);
-    os << os.fill();
-    helper.write(pcg_engine::params_type::increment(), os);
-    os << os.fill();
-    helper.write(engine.state_, os);
-    return os;
-  }
-
-  template <class CharT, class Traits>
-  friend typename absl::enable_if_t<(sizeof(state_type) <= 8),
-                                    std::basic_ostream<CharT, Traits>&>
-  operator<<(
-      std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-      const pcg_engine& engine) {
-    auto saver = random_internal::make_ostream_state_saver(os);
-    os << pcg_engine::params_type::multiplier() << os.fill();
-    os << pcg_engine::params_type::increment() << os.fill();
-    os << engine.state_;
-    return os;
-  }
-
-  template <class CharT, class Traits>
-  friend typename absl::enable_if_t<(sizeof(state_type) == 16),
-                                    std::basic_istream<CharT, Traits>&>
-  operator>>(
-      std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-      pcg_engine& engine) {                   // NOLINT(runtime/references)
-    random_internal::stream_u128_helper<state_type> helper;
-    auto mult = helper.read(is);
-    auto inc = helper.read(is);
-    auto tmp = helper.read(is);
-    if (mult != pcg_engine::params_type::multiplier() ||
-        inc != pcg_engine::params_type::increment()) {
-      // signal failure by setting the failbit.
-      is.setstate(is.rdstate() | std::ios_base::failbit);
-    }
-    if (!is.fail()) {
-      engine.state_ = tmp;
-    }
-    return is;
-  }
-
-  template <class CharT, class Traits>
-  friend typename absl::enable_if_t<(sizeof(state_type) <= 8),
-                                    std::basic_istream<CharT, Traits>&>
-  operator>>(
-      std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-      pcg_engine& engine) {                   // NOLINT(runtime/references)
-    state_type mult{}, inc{}, tmp{};
-    is >> mult >> inc >> tmp;
-    if (mult != pcg_engine::params_type::multiplier() ||
-        inc != pcg_engine::params_type::increment()) {
-      // signal failure by setting the failbit.
-      is.setstate(is.rdstate() | std::ios_base::failbit);
-    }
-    if (!is.fail()) {
-      engine.state_ = tmp;
-    }
-    return is;
-  }
-
- private:
-  state_type state_;
-
-  // Returns the linear-congruential generator next state.
-  static inline constexpr state_type lcg(state_type s) {
-    return s * Params::multiplier() + Params::increment();
-  }
-
-  // Returns the linear-congruential arbitrary seek state.
-  inline state_type advance(state_type s, uint64_t n) const {
-    state_type mult = Params::multiplier();
-    state_type inc = Params::increment();
-    state_type m = 1;
-    state_type i = 0;
-    while (n > 0) {
-      if (n & 1) {
-        m *= mult;
-        i = i * mult + inc;
-      }
-      inc = (mult + 1) * inc;
-      mult *= mult;
-      n >>= 1;
-    }
-    return m * s + i;
-  }
-
-  template <class SeedSequence>
-  void reseed(SeedSequence& seq) {
-    using sequence_result_type = typename SeedSequence::result_type;
-    constexpr size_t kBufferSize =
-        sizeof(state_type) / sizeof(sequence_result_type);
-    sequence_result_type buffer[kBufferSize];
-    seq.generate(std::begin(buffer), std::end(buffer));
-    // Convert the seed output to a single state value.
-    state_type tmp = buffer[0];
-    for (size_t i = 1; i < kBufferSize; i++) {
-      tmp <<= (sizeof(sequence_result_type) * 8);
-      tmp |= buffer[i];
-    }
-    state_ = lcg(tmp + params_type::increment());
-  }
-};
-
-// Parameterized implementation of the PCG 128-bit oneseq state.
-// This provides state_type, multiplier, and increment for pcg_engine.
-template <uint64_t kMultA, uint64_t kMultB, uint64_t kIncA, uint64_t kIncB>
-class pcg128_params {
- public:
-#if ABSL_HAVE_INTRINSIC_INT128
-  using state_type = __uint128_t;
-  static inline constexpr state_type make_u128(uint64_t a, uint64_t b) {
-    return (static_cast<__uint128_t>(a) << 64) | b;
-  }
-#else
-  using state_type = absl::uint128;
-  static inline constexpr state_type make_u128(uint64_t a, uint64_t b) {
-    return absl::MakeUint128(a, b);
-  }
-#endif
-
-  static inline constexpr state_type multiplier() {
-    return make_u128(kMultA, kMultB);
-  }
-  static inline constexpr state_type increment() {
-    return make_u128(kIncA, kIncB);
-  }
-};
-
-// Implementation of the PCG xsl_rr_128_64 128-bit mixing function, which
-// accepts an input of state_type and mixes it into an output of result_type.
-struct pcg_xsl_rr_128_64 {
-#if ABSL_HAVE_INTRINSIC_INT128
-  using state_type = __uint128_t;
-#else
-  using state_type = absl::uint128;
-#endif
-  using result_type = uint64_t;
-
-  inline uint64_t operator()(state_type state) {
-    // This is equivalent to the xsl_rr_128_64 mixing function.
-#if ABSL_HAVE_INTRINSIC_INT128
-    uint64_t rotate = static_cast<uint64_t>(state >> 122u);
-    state ^= state >> 64;
-    uint64_t s = static_cast<uint64_t>(state);
-#else
-    uint64_t h = Uint128High64(state);
-    uint64_t rotate = h >> 58u;
-    uint64_t s = Uint128Low64(state) ^ h;
-#endif
-    return random_internal::rotr(s, rotate);
-  }
-};
-
-// Parameterized implementation of the PCG 64-bit oneseq state.
-// This provides state_type, multiplier, and increment for pcg_engine.
-template <uint64_t kMult, uint64_t kInc>
-class pcg64_params {
- public:
-  using state_type = uint64_t;
-  static inline constexpr state_type multiplier() { return kMult; }
-  static inline constexpr state_type increment() { return kInc; }
-};
-
-// Implementation of the PCG xsh_rr_64_32 64-bit mixing function, which accepts
-// an input of state_type and mixes it into an output of result_type.
-struct pcg_xsh_rr_64_32 {
-  using state_type = uint64_t;
-  using result_type = uint32_t;
-  inline uint32_t operator()(uint64_t state) {
-    return random_internal::rotr(
-        static_cast<uint32_t>(((state >> 18) ^ state) >> 27), state >> 59);
-  }
-};
-
-// Stable pcg_engine implementations:
-// This is a 64-bit generator using 128-bits of state.
-// The output sequence is equivalent to Melissa O'Neil's pcg64_oneseq.
-using pcg64_2018_engine = pcg_engine<
-    random_internal::pcg128_params<0x2360ed051fc65da4ull, 0x4385df649fccf645ull,
-                                   0x5851f42d4c957f2d, 0x14057b7ef767814f>,
-    random_internal::pcg_xsl_rr_128_64>;
-
-// This is a 32-bit generator using 64-bits of state.
-// This is equivalent to Melissa O'Neil's pcg32_oneseq.
-using pcg32_2018_engine = pcg_engine<
-    random_internal::pcg64_params<0x5851f42d4c957f2dull, 0x14057b7ef767814full>,
-    random_internal::pcg_xsh_rr_64_32>;
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/pcg_engine_test.cc b/third_party/abseil_cpp/absl/random/internal/pcg_engine_test.cc
deleted file mode 100644
index 4d763e89eb..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/pcg_engine_test.cc
+++ /dev/null
@@ -1,638 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/pcg_engine.h"
-
-#include <algorithm>
-#include <bitset>
-#include <random>
-#include <sstream>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/random/internal/explicit_seed_seq.h"
-#include "absl/time/clock.h"
-
-#define UPDATE_GOLDEN 0
-
-namespace {
-
-using absl::random_internal::ExplicitSeedSeq;
-using absl::random_internal::pcg32_2018_engine;
-using absl::random_internal::pcg64_2018_engine;
-
-template <typename EngineType>
-class PCGEngineTest : public ::testing::Test {};
-
-using EngineTypes = ::testing::Types<pcg64_2018_engine, pcg32_2018_engine>;
-
-TYPED_TEST_SUITE(PCGEngineTest, EngineTypes);
-
-TYPED_TEST(PCGEngineTest, VerifyReseedChangesAllValues) {
-  using engine_type = TypeParam;
-  using result_type = typename engine_type::result_type;
-
-  const size_t kNumOutputs = 16;
-  engine_type engine;
-
-  // MSVC emits error 2719 without the use of std::ref below.
-  //  * formal parameter with __declspec(align('#')) won't be aligned
-
-  {
-    std::seed_seq seq1{1, 2, 3, 4, 5, 6, 7};
-    engine.seed(seq1);
-  }
-  result_type a[kNumOutputs];
-  std::generate(std::begin(a), std::end(a), std::ref(engine));
-
-  {
-    std::random_device rd;
-    std::seed_seq seq2{rd(), rd(), rd()};
-    engine.seed(seq2);
-  }
-  result_type b[kNumOutputs];
-  std::generate(std::begin(b), std::end(b), std::ref(engine));
-
-  // Verify that two uncorrelated values have ~50% of there bits in common. Use
-  // a 10% margin-of-error to reduce flakiness.
-  size_t changed_bits = 0;
-  size_t unchanged_bits = 0;
-  size_t total_set = 0;
-  size_t total_bits = 0;
-  size_t equal_count = 0;
-  for (size_t i = 0; i < kNumOutputs; ++i) {
-    equal_count += (a[i] == b[i]) ? 1 : 0;
-    std::bitset<sizeof(result_type) * 8> bitset(a[i] ^ b[i]);
-    changed_bits += bitset.count();
-    unchanged_bits += bitset.size() - bitset.count();
-
-    std::bitset<sizeof(result_type) * 8> a_set(a[i]);
-    std::bitset<sizeof(result_type) * 8> b_set(b[i]);
-    total_set += a_set.count() + b_set.count();
-    total_bits += 2 * 8 * sizeof(result_type);
-  }
-  // On average, half the bits are changed between two calls.
-  EXPECT_LE(changed_bits, 0.60 * (changed_bits + unchanged_bits));
-  EXPECT_GE(changed_bits, 0.40 * (changed_bits + unchanged_bits));
-
-  // verify using a quick normal-approximation to the binomial.
-  EXPECT_NEAR(total_set, total_bits * 0.5, 4 * std::sqrt(total_bits))
-      << "@" << total_set / static_cast<double>(total_bits);
-
-  // Also, A[i] == B[i] with probability (1/range) * N.
-  // Give this a pretty wide latitude, though.
-  const double kExpected = kNumOutputs / (1.0 * sizeof(result_type) * 8);
-  EXPECT_LE(equal_count, 1.0 + kExpected);
-}
-
-// Number of values that needs to be consumed to clean two sizes of buffer
-// and trigger third refresh. (slightly overestimates the actual state size).
-constexpr size_t kTwoBufferValues = 16;
-
-TYPED_TEST(PCGEngineTest, VerifyDiscard) {
-  using engine_type = TypeParam;
-
-  for (size_t num_used = 0; num_used < kTwoBufferValues; ++num_used) {
-    engine_type engine_used;
-    for (size_t i = 0; i < num_used; ++i) {
-      engine_used();
-    }
-
-    for (size_t num_discard = 0; num_discard < kTwoBufferValues;
-         ++num_discard) {
-      engine_type engine1 = engine_used;
-      engine_type engine2 = engine_used;
-      for (size_t i = 0; i < num_discard; ++i) {
-        engine1();
-      }
-      engine2.discard(num_discard);
-      for (size_t i = 0; i < kTwoBufferValues; ++i) {
-        const auto r1 = engine1();
-        const auto r2 = engine2();
-        ASSERT_EQ(r1, r2) << "used=" << num_used << " discard=" << num_discard;
-      }
-    }
-  }
-}
-
-TYPED_TEST(PCGEngineTest, StreamOperatorsResult) {
-  using engine_type = TypeParam;
-
-  std::wostringstream os;
-  std::wistringstream is;
-  engine_type engine;
-
-  EXPECT_EQ(&(os << engine), &os);
-  EXPECT_EQ(&(is >> engine), &is);
-}
-
-TYPED_TEST(PCGEngineTest, StreamSerialization) {
-  using engine_type = TypeParam;
-
-  for (size_t discard = 0; discard < kTwoBufferValues; ++discard) {
-    ExplicitSeedSeq seed_sequence{12, 34, 56};
-    engine_type engine(seed_sequence);
-    engine.discard(discard);
-
-    std::stringstream stream;
-    stream << engine;
-
-    engine_type new_engine;
-    stream >> new_engine;
-    for (size_t i = 0; i < 64; ++i) {
-      EXPECT_EQ(engine(), new_engine()) << " " << i;
-    }
-  }
-}
-
-constexpr size_t kNumGoldenOutputs = 127;
-
-// This test is checking if randen_engine is meets interface requirements
-// defined in [rand.req.urbg].
-TYPED_TEST(PCGEngineTest, RandomNumberEngineInterface) {
-  using engine_type = TypeParam;
-
-  using E = engine_type;
-  using T = typename E::result_type;
-
-  static_assert(std::is_copy_constructible<E>::value,
-                "engine_type must be copy constructible");
-
-  static_assert(absl::is_copy_assignable<E>::value,
-                "engine_type must be copy assignable");
-
-  static_assert(std::is_move_constructible<E>::value,
-                "engine_type must be move constructible");
-
-  static_assert(absl::is_move_assignable<E>::value,
-                "engine_type must be move assignable");
-
-  static_assert(std::is_same<decltype(std::declval<E>()()), T>::value,
-                "return type of operator() must be result_type");
-
-  // Names after definition of [rand.req.urbg] in C++ standard.
-  // e us a value of E
-  // v is a lvalue of E
-  // x, y are possibly const values of E
-  // s is a value of T
-  // q is a value satisfying requirements of seed_sequence
-  // z is a value of type unsigned long long
-  // os is a some specialization of basic_ostream
-  // is is a some specialization of basic_istream
-
-  E e, v;
-  const E x, y;
-  T s = 1;
-  std::seed_seq q{1, 2, 3};
-  unsigned long long z = 1;  // NOLINT(runtime/int)
-  std::wostringstream os;
-  std::wistringstream is;
-
-  E{};
-  E{x};
-  E{s};
-  E{q};
-
-  e.seed();
-
-  // MSVC emits error 2718 when using EXPECT_EQ(e, x)
-  //  * actual parameter with __declspec(align('#')) won't be aligned
-  EXPECT_TRUE(e == x);
-
-  e.seed(q);
-  {
-    E tmp(q);
-    EXPECT_TRUE(e == tmp);
-  }
-
-  e();
-  {
-    E tmp(q);
-    EXPECT_TRUE(e != tmp);
-  }
-
-  e.discard(z);
-
-  static_assert(std::is_same<decltype(x == y), bool>::value,
-                "return type of operator== must be bool");
-
-  static_assert(std::is_same<decltype(x != y), bool>::value,
-                "return type of operator== must be bool");
-}
-
-TYPED_TEST(PCGEngineTest, RandenEngineSFINAETest) {
-  using engine_type = TypeParam;
-  using result_type = typename engine_type::result_type;
-
-  {
-    engine_type engine(result_type(1));
-    engine.seed(result_type(1));
-  }
-
-  {
-    result_type n = 1;
-    engine_type engine(n);
-    engine.seed(n);
-  }
-
-  {
-    engine_type engine(1);
-    engine.seed(1);
-  }
-
-  {
-    int n = 1;
-    engine_type engine(n);
-    engine.seed(n);
-  }
-
-  {
-    std::seed_seq seed_seq;
-    engine_type engine(seed_seq);
-    engine.seed(seed_seq);
-  }
-
-  {
-    engine_type engine{std::seed_seq()};
-    engine.seed(std::seed_seq());
-  }
-}
-
-// ------------------------------------------------------------------
-// Stability tests for pcg64_2018_engine
-// ------------------------------------------------------------------
-TEST(PCG642018EngineTest, VerifyGolden) {
-  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
-      0x01070196e695f8f1, 0x703ec840c59f4493, 0xe54954914b3a44fa,
-      0x96130ff204b9285e, 0x7d9fdef535ceb21a, 0x666feed42e1219a0,
-      0x981f685721c8326f, 0xad80710d6eab4dda, 0xe202c480b037a029,
-      0x5d3390eaedd907e2, 0x0756befb39c6b8aa, 0x1fb44ba6634d62a3,
-      0x8d20423662426642, 0x34ea910167a39fb4, 0x93010b43a80d0ab6,
-      0x663db08a98fc568a, 0x720b0a1335956fae, 0x2c35483e31e1d3ba,
-      0x429f39776337409d, 0xb46d99e638687344, 0x105370b96aedcaee,
-      0x3999e92f811cff71, 0xd230f8bcb591cfc9, 0x0dce3db2ba7bdea5,
-      0xcf2f52c91eec99af, 0x2bc7c24a8b998a39, 0xbd8af1b0d599a19c,
-      0x56bc45abc66059f5, 0x170a46dc170f7f1e, 0xc25daf5277b85fad,
-      0xe629c2e0c948eadb, 0x1720a796915542ed, 0x22fb0caa4f909951,
-      0x7e0c0f4175acd83d, 0xd9fcab37ff2a860c, 0xab2280fb2054bad1,
-      0x58e8a06f37fa9e99, 0xc3a52a30b06528c7, 0x0175f773a13fc1bd,
-      0x731cfc584b00e840, 0x404cc7b2648069cb, 0x5bc29153b0b7f783,
-      0x771310a38cc999d1, 0x766a572f0a71a916, 0x90f450fb4fc48348,
-      0xf080ea3e1c7b1a0d, 0x15471a4507d66a44, 0x7d58e55a78f3df69,
-      0x0130a094576ac99c, 0x46669cb2d04b1d87, 0x17ab5bed20191840,
-      0x95b177d260adff3e, 0x025fb624b6ee4c07, 0xb35de4330154a95f,
-      0xe8510fff67e24c79, 0x132c3cbcd76ed2d3, 0x35e7cc145a093904,
-      0x9f5b5b5f81583b79, 0x3ee749a533966233, 0x4af85886cdeda8cd,
-      0x0ca5380ecb3ef3aa, 0x4f674eb7661d3192, 0x88a29aad00cd7733,
-      0x70b627ca045ffac6, 0x5912b43ea887623d, 0x95dc9fc6f62cf221,
-      0x926081a12a5c905b, 0x9c57d4cd7dfce651, 0x85ab2cbf23e3bb5d,
-      0xc5cd669f63023152, 0x3067be0fad5d898e, 0x12b56f444cb53d05,
-      0xbc2e5a640c3434fc, 0x9280bff0e4613fe1, 0x98819094c528743e,
-      0x999d1c98d829df33, 0x9ff82a012dc89242, 0xf99183ed39c8be94,
-      0xf0f59161cd421c55, 0x3c705730c2f6c48d, 0x66ad85c6e9278a61,
-      0x2a3428e4a428d5d0, 0x79207d68fd04940d, 0xea7f2b402edc8430,
-      0xa06b419ac857f63b, 0xcb1dd0e6fbc47e1c, 0x4f55229200ada6a4,
-      0x9647b5e6359c927f, 0x30bf8f9197c7efe5, 0xa79519529cc384d0,
-      0xbb22c4f339ad6497, 0xd7b9782f59d14175, 0x0dff12fff2ec0118,
-      0xa331ad8305343a7c, 0x48dad7e3f17e0862, 0x324c6fb3fd3c9665,
-      0xf0e4350e7933dfc4, 0x7ccda2f30b8b03b6, 0xa0afc6179005de40,
-      0xee65da6d063b3a30, 0xb9506f42f2bfe87a, 0xc9a2e26b0ef5baa0,
-      0x39fa9d4f495011d6, 0xbecc21a45d023948, 0x6bf484c6593f737f,
-      0x8065e0070cadc3b7, 0x9ef617ed8d419799, 0xac692cf8c233dd15,
-      0xd2ed87583c4ebb98, 0xad95ba1bebfedc62, 0x9b60b160a8264e43,
-      0x0bc8c45f71fcf25b, 0x4a78035cdf1c9931, 0x4602dc106667e029,
-      0xb335a3c250498ac8, 0x0256ebc4df20cab8, 0x0c61efd153f0c8d9,
-      0xe5d0150a4f806f88, 0x99d6521d351e7d87, 0x8d4888c9f80f4325,
-      0x106c5735c1ba868d, 0x73414881b880a878, 0x808a9a58a3064751,
-      0x339a29f3746de3d5, 0x5410d7fa4f873896, 0xd84623c81d7b8a03,
-      0x1f7c7e7a7f47f462,
-  };
-
-  pcg64_2018_engine engine(0);
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
-    printf("0x%016lx, ", engine());
-    if (i % 3 == 2) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-  engine.seed();
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-TEST(PCG642018EngineTest, VerifyGoldenSeeded) {
-  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
-      0xb03988f1e39691ee, 0xbd2a1eb5ac31e97a, 0x8f00d6d433634d02,
-      0x1823c28d483d5776, 0x000c3ee3e1aeb74a, 0xfa82ef27a4f3df9c,
-      0xc6f382308654e454, 0x414afb1a238996c2, 0x4703a4bc252eb411,
-      0x99d64f62c8f7f654, 0xbb07ebe11a34fa44, 0x79eb06a363c06131,
-      0xf66ad3756f1c6b21, 0x130c01d5e869f457, 0x5ca2b9963aecbc81,
-      0xfef7bebc1de27e6c, 0x1d174faa5ed2cdbf, 0xd75b7a773f2bb889,
-      0xc35c872327a170a5, 0x46da6d88646a42fe, 0x4622985e0442dae2,
-      0xbe3cbd67297f1f9b, 0xe7c37b4a4798bfd1, 0x173d5dfad15a25c3,
-      0x0eb6849ba2961522, 0xb0ff7246e6700d73, 0x88cb9c42d3afa577,
-      0xb609731dbd94d917, 0xd3941cda04b40081, 0x28d140f7409bea3a,
-      0x3c96699a920a124a, 0xdb28be521958b2fd, 0x0a3f44db3d4c5124,
-      0x7ac8e60ba13b70d2, 0x75f03a41ded5195a, 0xaed10ac7c4e4825d,
-      0xb92a3b18aadb7adc, 0xda45e0081f2bca46, 0x74d39ab3753143fc,
-      0xb686038018fac9ca, 0x4cc309fe99542dbb, 0xf3e1a4fcb311097c,
-      0x58763d6fa698d69d, 0xd11c365dbecd8d60, 0x2c15d55725b1dee7,
-      0x89805f254d85658c, 0x2374c44dfc62158b, 0x9a8350fa7995328d,
-      0x198f838970cf91da, 0x96aff569562c0e53, 0xd76c8c52b7ec6e3f,
-      0x23a01cd9ae4baa81, 0x3adb366b6d02a893, 0xb3313e2a4c5b333f,
-      0x04c11230b96a5425, 0x1f7f7af04787d571, 0xaddb019365275ec7,
-      0x5c960468ccb09f42, 0x8438db698c69a44a, 0x492be1e46111637e,
-      0x9c6c01e18100c610, 0xbfe48e75b7d0aceb, 0xb5e0b89ec1ce6a00,
-      0x9d280ecbc2fe8997, 0x290d9e991ba5fcab, 0xeec5bec7d9d2a4f0,
-      0x726e81488f19150e, 0x1a6df7955a7e462c, 0x37a12d174ba46bb5,
-      0x3cdcdffd96b1b5c5, 0x2c5d5ac10661a26e, 0xa742ed18f22e50c4,
-      0x00e0ed88ff0d8a35, 0x3d3c1718cb1efc0b, 0x1d70c51ffbccbf11,
-      0xfbbb895132a4092f, 0x619d27f2fb095f24, 0x69af68200985e5c4,
-      0xbee4885f57373f8d, 0x10b7a6bfe0587e40, 0xa885e6cf2f7e5f0a,
-      0x59f879464f767550, 0x24e805d69056990d, 0x860970b911095891,
-      0xca3189954f84170d, 0x6652a5edd4590134, 0x5e1008cef76174bf,
-      0xcbd417881f2bcfe5, 0xfd49fc9d706ecd17, 0xeebf540221ebd066,
-      0x46af7679464504cb, 0xd4028486946956f1, 0xd4f41864b86c2103,
-      0x7af090e751583372, 0x98cdaa09278cb642, 0xffd42b921215602f,
-      0x1d05bec8466b1740, 0xf036fa78a0132044, 0x787880589d1ecc78,
-      0x5644552cfef33230, 0x0a97e275fe06884b, 0x96d1b13333d470b5,
-      0xc8b3cdad52d3b034, 0x091357b9db7376fd, 0xa5fe4232555edf8c,
-      0x3371bc3b6ada76b5, 0x7deeb2300477c995, 0x6fc6d4244f2849c1,
-      0x750e8cc797ca340a, 0x81728613cd79899f, 0x3467f4ee6f9aeb93,
-      0x5ef0a905f58c640f, 0x432db85e5101c98a, 0x6488e96f46ac80c2,
-      0x22fddb282625048c, 0x15b287a0bc2d4c5d, 0xa7e2343ef1f28bce,
-      0xc87ee1aa89bed09e, 0x220610107812c5e9, 0xcbdab6fcd640f586,
-      0x8d41047970928784, 0x1aa431509ec1ade0, 0xac3f0be53f518ddc,
-      0x16f4428ad81d0cbb, 0x675b13c2736fc4bb, 0x6db073afdd87e32d,
-      0x572f3ca2f1a078c6,
-  };
-
-  ExplicitSeedSeq seed_sequence{12, 34, 56};
-  pcg64_2018_engine engine(seed_sequence);
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
-    printf("0x%016lx, ", engine());
-    if (i % 3 == 2) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-  engine.seed(seed_sequence);
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-TEST(PCG642018EngineTest, VerifyGoldenFromDeserializedEngine) {
-  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
-      0xdd425b47b4113dea, 0x1b07176479d444b0, 0x6b391027586f2e42,
-      0xa166f2b15f4a2143, 0xffb6dbd7a179ee97, 0xb2c00035365bf0b1,
-      0x8fbb518b45855521, 0xfc789a55ddf87c3b, 0x429531f0f17ff355,
-      0xbe708560d603d283, 0x5bff415175c5cb6b, 0xe813491f4ad45394,
-      0xa853f4506d55880d, 0x7e538453e568172e, 0xe101f1e098ddd0ec,
-      0x6ee31266ee4c766d, 0xa8786d92d66b39d7, 0xfee622a2acf5e5b0,
-      0x5fe8e82c102fa7b3, 0x01f10be4cdb53c9d, 0xbe0545366f857022,
-      0x12e74f010a339bca, 0xb10d85ca40d5ce34, 0xe80d6feba5054875,
-      0x2b7c1ee6d567d4ee, 0x2a9cd043bfd03b66, 0x5cfc531bd239f3f1,
-      0x1c4734e4647d70f5, 0x85a8f60f006b5760, 0x6a4239ce76dca387,
-      0x8da0f86d7339335c, 0xf055b0468551374d, 0x486e8567e9bea9a0,
-      0x4cb531b8405192dd, 0xf813b1ee3157110b, 0x214c2a664a875d8e,
-      0x74531237b29b35f7, 0xa6f0267bb77a771e, 0x64b552bff54184a4,
-      0xa2d6f7af2d75b6fc, 0x460a10018e03b5ab, 0x76fd1fdcb81d0800,
-      0x76f5f81805070d9d, 0x1fb75cb1a70b289a, 0x9dfd25a022c4b27f,
-      0x9a31a14a80528e9e, 0x910dc565ddc25820, 0xd6aef8e2b0936c10,
-      0xe1773c507fe70225, 0xe027fd7aadd632bc, 0xc1fecb427089c8b8,
-      0xb5c74c69fa9dbf26, 0x71bf9b0e4670227d, 0x25f48fad205dcfdd,
-      0x905248ec4d689c56, 0x5c2b7631b0de5c9d, 0x9f2ee0f8f485036c,
-      0xfd6ce4ebb90bf7ea, 0xd435d20046085574, 0x6b7eadcb0625f986,
-      0x679d7d44b48be89e, 0x49683b8e1cdc49de, 0x4366cf76e9a2f4ca,
-      0x54026ec1cdad7bed, 0xa9a04385207f28d3, 0xc8e66de4eba074b2,
-      0x40b08c42de0f4cc0, 0x1d4c5e0e93c5bbc0, 0x19b80792e470ae2d,
-      0x6fcaaeaa4c2a5bd9, 0xa92cb07c4238438e, 0x8bb5c918a007e298,
-      0x7cd671e944874cf4, 0x88166470b1ba3cac, 0xd013d476eaeeade6,
-      0xcee416947189b3c3, 0x5d7c16ab0dce6088, 0xd3578a5c32b13d27,
-      0x3875db5adc9cc973, 0xfbdaba01c5b5dc56, 0xffc4fdd391b231c3,
-      0x2334520ecb164fec, 0x361c115e7b6de1fa, 0xeee58106cc3563d7,
-      0x8b7f35a8db25ebb8, 0xb29d00211e2cafa6, 0x22a39fe4614b646b,
-      0x92ca6de8b998506d, 0x40922fe3d388d1db, 0x9da47f1e540f802a,
-      0x811dceebf16a25db, 0xf6524ae22e0e53a9, 0x52d9e780a16eb99d,
-      0x4f504286bb830207, 0xf6654d4786bd5cc3, 0x00bd98316003a7e1,
-      0xefda054a6ab8f5f3, 0x46cfb0f4c1872827, 0xc22b316965c0f3b2,
-      0xd1a28087c7e7562a, 0xaa4f6a094b7f5cff, 0xfe2bc853a041f7da,
-      0xe9d531402a83c3ba, 0xe545d8663d3ce4dd, 0xfa2dcd7d91a13fa8,
-      0xda1a080e52a127b8, 0x19c98f1f809c3d84, 0x2cef109af4678c88,
-      0x53462accab3b9132, 0x176b13a80415394e, 0xea70047ef6bc178b,
-      0x57bca80506d6dcdf, 0xd853ba09ff09f5c4, 0x75f4df3a7ddd4775,
-      0x209c367ade62f4fe, 0xa9a0bbc74d5f4682, 0x5dfe34bada86c21a,
-      0xc2c05bbcd38566d1, 0x6de8088e348c916a, 0x6a7001c6000c2196,
-      0xd9fb51865fc4a367, 0x12f320e444ece8ff, 0x6d56f7f793d65035,
-      0x138f31b7a865f8aa, 0x58fc68b4026b9adf, 0xcd48954b79fb6436,
-      0x27dfce4a0232af87,
-  };
-
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  std::seed_seq seed_sequence{1, 2, 3};
-  pcg64_2018_engine engine(seed_sequence);
-  std::ostringstream stream;
-  stream << engine;
-  auto str = stream.str();
-  printf("%s\n\n", str.c_str());
-  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
-    printf("0x%016lx, ", engine());
-    if (i % 3 == 2) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  pcg64_2018_engine engine;
-  std::istringstream stream(
-      "2549297995355413924 4865540595714422341 6364136223846793005 "
-      "1442695040888963407 18088519957565336995 4845369368158826708");
-  stream >> engine;
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-// ------------------------------------------------------------------
-// Stability tests for pcg32_2018_engine
-// ------------------------------------------------------------------
-TEST(PCG322018EngineTest, VerifyGolden) {
-  constexpr uint32_t kGolden[kNumGoldenOutputs] = {
-      0x7a7ecbd9, 0x89fd6c06, 0xae646aa8, 0xcd3cf945, 0x6204b303, 0x198c8585,
-      0x49fce611, 0xd1e9297a, 0x142d9440, 0xee75f56b, 0x473a9117, 0xe3a45903,
-      0xbce807a1, 0xe54e5f4d, 0x497d6c51, 0x61829166, 0xa740474b, 0x031912a8,
-      0x9de3defa, 0xd266dbf1, 0x0f38bebb, 0xec3c4f65, 0x07c5057d, 0xbbce03c8,
-      0xfd2ac7a8, 0xffcf4773, 0x5b10affb, 0xede1c842, 0xe22b01b7, 0xda133c8c,
-      0xaf89b0f4, 0x25d1b8bc, 0x9f625482, 0x7bfd6882, 0x2e2210c0, 0x2c8fb9a6,
-      0x42cb3b83, 0x40ce0dab, 0x644a3510, 0x36230ef2, 0xe2cb6d43, 0x1012b343,
-      0x746c6c9f, 0x36714cf8, 0xed1f5026, 0x8bbbf83e, 0xe98710f4, 0x8a2afa36,
-      0x09035349, 0x6dc1a487, 0x682b634b, 0xc106794f, 0x7dd78beb, 0x628c262b,
-      0x852fb232, 0xb153ac4c, 0x4f169d1b, 0xa69ab774, 0x4bd4b6f2, 0xdc351dd3,
-      0x93ff3c8c, 0xa30819ab, 0xff07758c, 0x5ab13c62, 0xd16d7fb5, 0xc4950ffa,
-      0xd309ae49, 0xb9677a87, 0x4464e317, 0x90dc44f1, 0xc694c1d4, 0x1d5e1168,
-      0xadf37a2d, 0xda38990d, 0x1ec4bd33, 0x36ca25ce, 0xfa0dc76a, 0x968a9d43,
-      0x6950ac39, 0xdd3276bc, 0x06d5a71e, 0x1f6f282d, 0x5c626c62, 0xdde3fc31,
-      0x152194ce, 0xc35ed14c, 0xb1f7224e, 0x47f76bb8, 0xb34fdd08, 0x7011395e,
-      0x162d2a49, 0x0d1bf09f, 0x9428a952, 0x03c5c344, 0xd3525616, 0x7816fff3,
-      0x6bceb8a8, 0x8345a081, 0x366420fd, 0x182abeda, 0x70f82745, 0xaf15ded8,
-      0xc7f52ca2, 0xa98db9c5, 0x919d99ba, 0x9c376c1c, 0xed8d34c2, 0x716ae9f5,
-      0xef062fa5, 0xee3b6c56, 0x52325658, 0x61afa9c3, 0xfdaf02f0, 0x961cf3ab,
-      0x9f291565, 0x4fbf3045, 0x0590c899, 0xde901385, 0x45005ffb, 0x509db162,
-      0x262fa941, 0x4c421653, 0x4b17c21e, 0xea0d1530, 0xde803845, 0x61bfd515,
-      0x438523ef,
-  };
-
-  pcg32_2018_engine engine(0);
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
-    printf("0x%08x, ", engine());
-    if (i % 6 == 5) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-  engine.seed();
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-TEST(PCG322018EngineTest, VerifyGoldenSeeded) {
-  constexpr uint32_t kGolden[kNumGoldenOutputs] = {
-      0x60b5a64c, 0x978502f9, 0x80a75f60, 0x241f1158, 0xa4cd1dbb, 0xe7284017,
-      0x3b678da5, 0x5223ec99, 0xe4bdd5d9, 0x72190e6d, 0xe6e702c9, 0xff80c768,
-      0xcf126ed3, 0x1fbd20ab, 0x60980489, 0xbc72bf89, 0x407ac6c0, 0x00bf3c51,
-      0xf9087897, 0x172e4eb6, 0xe9e4f443, 0x1a6098bf, 0xbf44f8c2, 0xdd84a0e5,
-      0xd9a52364, 0xc0e2e786, 0x061ae2ba, 0x9facb8e3, 0x6109432d, 0xd4e0a013,
-      0xbd8eb9a6, 0x7e86c3b6, 0x629c0e68, 0x05337430, 0xb495b9f4, 0x11ccd65d,
-      0xb578db25, 0x66f1246d, 0x6ef20a7f, 0x5e429812, 0x11772130, 0xb944b5c2,
-      0x01624128, 0xa2385ab7, 0xd3e10d35, 0xbe570ec3, 0xc951656f, 0xbe8944a0,
-      0x7be41062, 0x5709f919, 0xd745feda, 0x9870b9ae, 0xb44b8168, 0x19e7683b,
-      0xded8017f, 0xc6e4d544, 0x91ae4225, 0xd6745fba, 0xb992f284, 0x65b12b33,
-      0xa9d5fdb4, 0xf105ce1a, 0x35ca1a6e, 0x2ff70dd0, 0xd8335e49, 0xfb71ddf2,
-      0xcaeabb89, 0x5c6f5f84, 0x9a811a7d, 0xbcecbbd1, 0x0f661ba0, 0x9ad93b9d,
-      0xedd23e0b, 0x42062f48, 0xd38dd7e4, 0x6cd63c9c, 0x640b98ae, 0x4bff5653,
-      0x12626371, 0x13266017, 0xe7a698d8, 0x39c74667, 0xe8fdf2e3, 0x52803bf8,
-      0x2af6895b, 0x91335b7b, 0x699e4961, 0x00a40fff, 0x253ff2b6, 0x4a6cf672,
-      0x9584e85f, 0xf2a5000c, 0x4d58aba8, 0xb8513e6a, 0x767fad65, 0x8e326f9e,
-      0x182f15a1, 0x163dab52, 0xdf99c780, 0x047282a1, 0xee4f90dd, 0xd50394ae,
-      0x6c9fd5f0, 0xb06a9194, 0x387e3840, 0x04a9487b, 0xf678a4c2, 0xd0a78810,
-      0xd502c97e, 0xd6a9b12a, 0x4accc5dc, 0x416ed53e, 0x50411536, 0xeeb89c24,
-      0x813a7902, 0x034ebca6, 0xffa52e7c, 0x7ecd3d0e, 0xfa37a0d2, 0xb1fbe2c1,
-      0xb7efc6d1, 0xefa4ccee, 0xf6f80424, 0x2283f3d9, 0x68732284, 0x94f3b5c8,
-      0xbbdeceb9,
-  };
-
-  ExplicitSeedSeq seed_sequence{12, 34, 56};
-  pcg32_2018_engine engine(seed_sequence);
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
-    printf("0x%08x, ", engine());
-    if (i % 6 == 5) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-  engine.seed(seed_sequence);
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-TEST(PCG322018EngineTest, VerifyGoldenFromDeserializedEngine) {
-  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
-      0x780f7042, 0xba137215, 0x43ab6f22, 0x0cb55f46, 0x44b2627d, 0x835597af,
-      0xea973ea1, 0x0d2abd35, 0x4fdd601c, 0xac4342fe, 0x7db7e93c, 0xe56ebcaf,
-      0x3596470a, 0x7770a9ad, 0x9b893320, 0x57db3415, 0xb432de54, 0xa02baf71,
-      0xa256aadb, 0x88921fc7, 0xa35fa6b3, 0xde3eca46, 0x605739a7, 0xa890b82b,
-      0xe457b7ad, 0x335fb903, 0xeb06790c, 0xb3c54bf6, 0x6141e442, 0xa599a482,
-      0xb78987cc, 0xc61dfe9d, 0x0f1d6ace, 0x17460594, 0x8f6a5061, 0x083dc354,
-      0xe9c337fb, 0xcfd105f7, 0x926764b6, 0x638d24dc, 0xeaac650a, 0x67d2cb9c,
-      0xd807733c, 0x205fc52e, 0xf5399e2e, 0x6c46ddcc, 0xb603e875, 0xce113a25,
-      0x3c8d4813, 0xfb584db8, 0xf6d255ff, 0xea80954f, 0x42e8be85, 0xb2feee72,
-      0x62bd8d16, 0x1be4a142, 0x97dca1a4, 0xdd6e7333, 0xb2caa20e, 0xa12b1588,
-      0xeb3a5a1a, 0x6fa5ba89, 0x077ea931, 0x8ddb1713, 0x0dd03079, 0x2c2ba965,
-      0xa77fac17, 0xc8325742, 0x8bb893bf, 0xc2315741, 0xeaceee92, 0x81dd2ee2,
-      0xe5214216, 0x1b9b8fb2, 0x01646d03, 0x24facc25, 0xd8c0e0bb, 0xa33fe106,
-      0xf34fe976, 0xb3b4b44e, 0x65618fed, 0x032c6192, 0xa9dd72ce, 0xf391887b,
-      0xf41c6a6e, 0x05c4bd6d, 0x37fa260e, 0x46b05659, 0xb5f6348a, 0x62d26d89,
-      0x39f6452d, 0xb17b30a2, 0xbdd82743, 0x38ecae3b, 0xfe90f0a2, 0xcb2d226d,
-      0xcf8a0b1c, 0x0eed3d4d, 0xa1f69cfc, 0xd7ac3ba5, 0xce9d9a6b, 0x121deb4c,
-      0x4a0d03f3, 0xc1821ed1, 0x59c249ac, 0xc0abb474, 0x28149985, 0xfd9a82ba,
-      0x5960c3b2, 0xeff00cba, 0x6073aa17, 0x25dc0919, 0x9976626e, 0xdd2ccc33,
-      0x39ecb6ec, 0xc6e15d13, 0xfac94cfd, 0x28cfd34f, 0xf2d2c32d, 0x51c23d08,
-      0x4fdb2f48, 0x97baa807, 0xf2c1004c, 0xc4ae8136, 0x71f31c94, 0x8c92d601,
-      0x36caf5cd,
-  };
-
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  std::seed_seq seed_sequence{1, 2, 3};
-  pcg32_2018_engine engine(seed_sequence);
-  std::ostringstream stream;
-  stream << engine;
-  auto str = stream.str();
-  printf("%s\n\n", str.c_str());
-  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
-    printf("0x%08x, ", engine());
-    if (i % 6 == 5) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-
-  EXPECT_FALSE(true);
-#else
-  pcg32_2018_engine engine;
-  std::istringstream stream(
-      "6364136223846793005 1442695040888963407 6537028157270659894");
-  stream >> engine;
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/platform.h b/third_party/abseil_cpp/absl/random/internal/platform.h
deleted file mode 100644
index bbdb4e6208..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/platform.h
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_PLATFORM_H_
-#define ABSL_RANDOM_INTERNAL_PLATFORM_H_
-
-// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
-// symbols from arbitrary system and other headers, since it may be built
-// with different flags from other targets, using different levels of
-// optimization, potentially introducing ODR violations.
-
-// -----------------------------------------------------------------------------
-// Platform Feature Checks
-// -----------------------------------------------------------------------------
-
-// Currently supported operating systems and associated preprocessor
-// symbols:
-//
-//   Linux and Linux-derived           __linux__
-//   Android                           __ANDROID__ (implies __linux__)
-//   Linux (non-Android)               __linux__ && !__ANDROID__
-//   Darwin (macOS and iOS)            __APPLE__
-//   Akaros (http://akaros.org)        __ros__
-//   Windows                           _WIN32
-//   NaCL                              __native_client__
-//   AsmJS                             __asmjs__
-//   WebAssembly                       __wasm__
-//   Fuchsia                           __Fuchsia__
-//
-// Note that since Android defines both __ANDROID__ and __linux__, one
-// may probe for either Linux or Android by simply testing for __linux__.
-//
-// NOTE: For __APPLE__ platforms, we use #include <TargetConditionals.h>
-// to distinguish os variants.
-//
-// http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system
-
-#if defined(__APPLE__)
-#include <TargetConditionals.h>
-#endif
-
-// -----------------------------------------------------------------------------
-// Architecture Checks
-// -----------------------------------------------------------------------------
-
-// These preprocessor directives are trying to determine CPU architecture,
-// including necessary headers to support hardware AES.
-//
-// ABSL_ARCH_{X86/PPC/ARM} macros determine the platform.
-#if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || \
-    defined(_M_X64)
-#define ABSL_ARCH_X86_64
-#elif defined(__i386) || defined(_M_IX86)
-#define ABSL_ARCH_X86_32
-#elif defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64)
-#define ABSL_ARCH_AARCH64
-#elif defined(__arm__) || defined(__ARMEL__) || defined(_M_ARM)
-#define ABSL_ARCH_ARM
-#elif defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
-    defined(__ppc__) || defined(__PPC__)
-#define ABSL_ARCH_PPC
-#else
-// Unsupported architecture.
-//  * https://sourceforge.net/p/predef/wiki/Architectures/
-//  * https://msdn.microsoft.com/en-us/library/b0084kay.aspx
-//  * for gcc, clang: "echo | gcc -E -dM -"
-#endif
-
-// -----------------------------------------------------------------------------
-// Attribute Checks
-// -----------------------------------------------------------------------------
-
-// ABSL_RANDOM_INTERNAL_RESTRICT annotates whether pointers may be considered
-// to be unaliased.
-#if defined(__clang__) || defined(__GNUC__)
-#define ABSL_RANDOM_INTERNAL_RESTRICT __restrict__
-#elif defined(_MSC_VER)
-#define ABSL_RANDOM_INTERNAL_RESTRICT __restrict
-#else
-#define ABSL_RANDOM_INTERNAL_RESTRICT
-#endif
-
-// ABSL_HAVE_ACCELERATED_AES indicates whether the currently active compiler
-// flags (e.g. -maes) allow using hardware accelerated AES instructions, which
-// implies us assuming that the target platform supports them.
-#define ABSL_HAVE_ACCELERATED_AES 0
-
-#if defined(ABSL_ARCH_X86_64)
-
-#if defined(__AES__) || defined(__AVX__)
-#undef ABSL_HAVE_ACCELERATED_AES
-#define ABSL_HAVE_ACCELERATED_AES 1
-#endif
-
-#elif defined(ABSL_ARCH_PPC)
-
-// Rely on VSX and CRYPTO extensions for vcipher on PowerPC.
-#if (defined(__VEC__) || defined(__ALTIVEC__)) && defined(__VSX__) && \
-    defined(__CRYPTO__)
-#undef ABSL_HAVE_ACCELERATED_AES
-#define ABSL_HAVE_ACCELERATED_AES 1
-#endif
-
-#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
-
-// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0053c/IHI0053C_acle_2_0.pdf
-// Rely on NEON+CRYPTO extensions for ARM.
-#if defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO)
-#undef ABSL_HAVE_ACCELERATED_AES
-#define ABSL_HAVE_ACCELERATED_AES 1
-#endif
-
-#endif
-
-// NaCl does not allow AES.
-#if defined(__native_client__)
-#undef ABSL_HAVE_ACCELERATED_AES
-#define ABSL_HAVE_ACCELERATED_AES 0
-#endif
-
-// ABSL_RANDOM_INTERNAL_AES_DISPATCH indicates whether the currently active
-// platform has, or should use run-time dispatch for selecting the
-// acclerated Randen implementation.
-#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0
-
-#if defined(ABSL_ARCH_X86_64)
-// Dispatch is available on x86_64
-#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
-#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1
-#elif defined(__linux__) && defined(ABSL_ARCH_PPC)
-// Or when running linux PPC
-#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
-#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1
-#elif defined(__linux__) && defined(ABSL_ARCH_AARCH64)
-// Or when running linux AArch64
-#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
-#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1
-#elif defined(__linux__) && defined(ABSL_ARCH_ARM) && (__ARM_ARCH >= 8)
-// Or when running linux ARM v8 or higher.
-// (This captures a lot of Android configurations.)
-#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
-#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1
-#endif
-
-// NaCl does not allow dispatch.
-#if defined(__native_client__)
-#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
-#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0
-#endif
-
-// iOS does not support dispatch, even on x86, since applications
-// should be bundled as fat binaries, with a different build tailored for
-// each specific supported platform/architecture.
-#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \
-    (defined(TARGET_OS_IPHONE_SIMULATOR) && TARGET_OS_IPHONE_SIMULATOR)
-#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
-#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0
-#endif
-
-#endif  // ABSL_RANDOM_INTERNAL_PLATFORM_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/pool_urbg.cc b/third_party/abseil_cpp/absl/random/internal/pool_urbg.cc
deleted file mode 100644
index 5bee530770..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/pool_urbg.cc
+++ /dev/null
@@ -1,254 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/pool_urbg.h"
-
-#include <algorithm>
-#include <atomic>
-#include <cstdint>
-#include <cstring>
-#include <iterator>
-
-#include "absl/base/attributes.h"
-#include "absl/base/call_once.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/endian.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/base/internal/sysinfo.h"
-#include "absl/base/internal/unaligned_access.h"
-#include "absl/base/optimization.h"
-#include "absl/random/internal/randen.h"
-#include "absl/random/internal/seed_material.h"
-#include "absl/random/seed_gen_exception.h"
-
-using absl::base_internal::SpinLock;
-using absl::base_internal::SpinLockHolder;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-namespace {
-
-// RandenPoolEntry is a thread-safe pseudorandom bit generator, implementing a
-// single generator within a RandenPool<T>. It is an internal implementation
-// detail, and does not aim to conform to [rand.req.urng].
-//
-// NOTE: There are alignment issues when used on ARM, for instance.
-// See the allocation code in PoolAlignedAlloc().
-class RandenPoolEntry {
- public:
-  static constexpr size_t kState = RandenTraits::kStateBytes / sizeof(uint32_t);
-  static constexpr size_t kCapacity =
-      RandenTraits::kCapacityBytes / sizeof(uint32_t);
-
-  void Init(absl::Span<const uint32_t> data) {
-    SpinLockHolder l(&mu_);  // Always uncontested.
-    std::copy(data.begin(), data.end(), std::begin(state_));
-    next_ = kState;
-  }
-
-  // Copy bytes into out.
-  void Fill(uint8_t* out, size_t bytes) ABSL_LOCKS_EXCLUDED(mu_);
-
-  // Returns random bits from the buffer in units of T.
-  template <typename T>
-  inline T Generate() ABSL_LOCKS_EXCLUDED(mu_);
-
-  inline void MaybeRefill() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
-    if (next_ >= kState) {
-      next_ = kCapacity;
-      impl_.Generate(state_);
-    }
-  }
-
- private:
-  // Randen URBG state.
-  uint32_t state_[kState] ABSL_GUARDED_BY(mu_);  // First to satisfy alignment.
-  SpinLock mu_;
-  const Randen impl_;
-  size_t next_ ABSL_GUARDED_BY(mu_);
-};
-
-template <>
-inline uint8_t RandenPoolEntry::Generate<uint8_t>() {
-  SpinLockHolder l(&mu_);
-  MaybeRefill();
-  return static_cast<uint8_t>(state_[next_++]);
-}
-
-template <>
-inline uint16_t RandenPoolEntry::Generate<uint16_t>() {
-  SpinLockHolder l(&mu_);
-  MaybeRefill();
-  return static_cast<uint16_t>(state_[next_++]);
-}
-
-template <>
-inline uint32_t RandenPoolEntry::Generate<uint32_t>() {
-  SpinLockHolder l(&mu_);
-  MaybeRefill();
-  return state_[next_++];
-}
-
-template <>
-inline uint64_t RandenPoolEntry::Generate<uint64_t>() {
-  SpinLockHolder l(&mu_);
-  if (next_ >= kState - 1) {
-    next_ = kCapacity;
-    impl_.Generate(state_);
-  }
-  auto p = state_ + next_;
-  next_ += 2;
-
-  uint64_t result;
-  std::memcpy(&result, p, sizeof(result));
-  return result;
-}
-
-void RandenPoolEntry::Fill(uint8_t* out, size_t bytes) {
-  SpinLockHolder l(&mu_);
-  while (bytes > 0) {
-    MaybeRefill();
-    size_t remaining = (kState - next_) * sizeof(state_[0]);
-    size_t to_copy = std::min(bytes, remaining);
-    std::memcpy(out, &state_[next_], to_copy);
-    out += to_copy;
-    bytes -= to_copy;
-    next_ += (to_copy + sizeof(state_[0]) - 1) / sizeof(state_[0]);
-  }
-}
-
-// Number of pooled urbg entries.
-static constexpr int kPoolSize = 8;
-
-// Shared pool entries.
-static absl::once_flag pool_once;
-ABSL_CACHELINE_ALIGNED static RandenPoolEntry* shared_pools[kPoolSize];
-
-// Returns an id in the range [0 ... kPoolSize), which indexes into the
-// pool of random engines.
-//
-// Each thread to access the pool is assigned a sequential ID (without reuse)
-// from the pool-id space; the id is cached in a thread_local variable.
-// This id is assigned based on the arrival-order of the thread to the
-// GetPoolID call; this has no binary, CL, or runtime stability because
-// on subsequent runs the order within the same program may be significantly
-// different. However, as other thread IDs are not assigned sequentially,
-// this is not expected to matter.
-int GetPoolID() {
-  static_assert(kPoolSize >= 1,
-                "At least one urbg instance is required for PoolURBG");
-
-  ABSL_CONST_INIT static std::atomic<int64_t> sequence{0};
-
-#ifdef ABSL_HAVE_THREAD_LOCAL
-  static thread_local int my_pool_id = -1;
-  if (ABSL_PREDICT_FALSE(my_pool_id < 0)) {
-    my_pool_id = (sequence++ % kPoolSize);
-  }
-  return my_pool_id;
-#else
-  static pthread_key_t tid_key = [] {
-    pthread_key_t tmp_key;
-    int err = pthread_key_create(&tmp_key, nullptr);
-    if (err) {
-      ABSL_RAW_LOG(FATAL, "pthread_key_create failed with %d", err);
-    }
-    return tmp_key;
-  }();
-
-  // Store the value in the pthread_{get/set}specific. However an uninitialized
-  // value is 0, so add +1 to distinguish from the null value.
-  intptr_t my_pool_id =
-      reinterpret_cast<intptr_t>(pthread_getspecific(tid_key));
-  if (ABSL_PREDICT_FALSE(my_pool_id == 0)) {
-    // No allocated ID, allocate the next value, cache it, and return.
-    my_pool_id = (sequence++ % kPoolSize) + 1;
-    int err = pthread_setspecific(tid_key, reinterpret_cast<void*>(my_pool_id));
-    if (err) {
-      ABSL_RAW_LOG(FATAL, "pthread_setspecific failed with %d", err);
-    }
-  }
-  return my_pool_id - 1;
-#endif
-}
-
-// Allocate a RandenPoolEntry with at least 32-byte alignment, which is required
-// by ARM platform code.
-RandenPoolEntry* PoolAlignedAlloc() {
-  constexpr size_t kAlignment =
-      ABSL_CACHELINE_SIZE > 32 ? ABSL_CACHELINE_SIZE : 32;
-
-  // Not all the platforms that we build for have std::aligned_alloc, however
-  // since we never free these objects, we can over allocate and munge the
-  // pointers to the correct alignment.
-  void* memory = std::malloc(sizeof(RandenPoolEntry) + kAlignment);
-  auto x = reinterpret_cast<intptr_t>(memory);
-  auto y = x % kAlignment;
-  void* aligned =
-      (y == 0) ? memory : reinterpret_cast<void*>(x + kAlignment - y);
-  return new (aligned) RandenPoolEntry();
-}
-
-// Allocate and initialize kPoolSize objects of type RandenPoolEntry.
-//
-// The initialization strategy is to initialize one object directly from
-// OS entropy, then to use that object to seed all of the individual
-// pool instances.
-void InitPoolURBG() {
-  static constexpr size_t kSeedSize =
-      RandenTraits::kStateBytes / sizeof(uint32_t);
-  // Read the seed data from OS entropy once.
-  uint32_t seed_material[kPoolSize * kSeedSize];
-  if (!random_internal::ReadSeedMaterialFromOSEntropy(
-          absl::MakeSpan(seed_material))) {
-    random_internal::ThrowSeedGenException();
-  }
-  for (int i = 0; i < kPoolSize; i++) {
-    shared_pools[i] = PoolAlignedAlloc();
-    shared_pools[i]->Init(
-        absl::MakeSpan(&seed_material[i * kSeedSize], kSeedSize));
-  }
-}
-
-// Returns the pool entry for the current thread.
-RandenPoolEntry* GetPoolForCurrentThread() {
-  absl::call_once(pool_once, InitPoolURBG);
-  return shared_pools[GetPoolID()];
-}
-
-}  // namespace
-
-template <typename T>
-typename RandenPool<T>::result_type RandenPool<T>::Generate() {
-  auto* pool = GetPoolForCurrentThread();
-  return pool->Generate<T>();
-}
-
-template <typename T>
-void RandenPool<T>::Fill(absl::Span<result_type> data) {
-  auto* pool = GetPoolForCurrentThread();
-  pool->Fill(reinterpret_cast<uint8_t*>(data.data()),
-             data.size() * sizeof(result_type));
-}
-
-template class RandenPool<uint8_t>;
-template class RandenPool<uint16_t>;
-template class RandenPool<uint32_t>;
-template class RandenPool<uint64_t>;
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/pool_urbg.h b/third_party/abseil_cpp/absl/random/internal/pool_urbg.h
deleted file mode 100644
index 05721929f5..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/pool_urbg.h
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_POOL_URBG_H_
-#define ABSL_RANDOM_INTERNAL_POOL_URBG_H_
-
-#include <cinttypes>
-#include <limits>
-
-#include "absl/random/internal/traits.h"
-#include "absl/types/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// RandenPool is a thread-safe random number generator [random.req.urbg] that
-// uses an underlying pool of Randen generators to generate values.  Each thread
-// has affinity to one instance of the underlying pool generators.  Concurrent
-// access is guarded by a spin-lock.
-template <typename T>
-class RandenPool {
- public:
-  using result_type = T;
-  static_assert(std::is_unsigned<result_type>::value,
-                "RandenPool template argument must be a built-in unsigned "
-                "integer type");
-
-  static constexpr result_type(min)() {
-    return (std::numeric_limits<result_type>::min)();
-  }
-
-  static constexpr result_type(max)() {
-    return (std::numeric_limits<result_type>::max)();
-  }
-
-  RandenPool() {}
-
-  // Returns a single value.
-  inline result_type operator()() { return Generate(); }
-
-  // Fill data with random values.
-  static void Fill(absl::Span<result_type> data);
-
- protected:
-  // Generate returns a single value.
-  static result_type Generate();
-};
-
-extern template class RandenPool<uint8_t>;
-extern template class RandenPool<uint16_t>;
-extern template class RandenPool<uint32_t>;
-extern template class RandenPool<uint64_t>;
-
-// PoolURBG uses an underlying pool of random generators to implement a
-// thread-compatible [random.req.urbg] interface with an internal cache of
-// values.
-template <typename T, size_t kBufferSize>
-class PoolURBG {
-  // Inheritance to access the protected static members of RandenPool.
-  using unsigned_type = typename make_unsigned_bits<T>::type;
-  using PoolType = RandenPool<unsigned_type>;
-  using SpanType = absl::Span<unsigned_type>;
-
-  static constexpr size_t kInitialBuffer = kBufferSize + 1;
-  static constexpr size_t kHalfBuffer = kBufferSize / 2;
-
- public:
-  using result_type = T;
-
-  static_assert(std::is_unsigned<result_type>::value,
-                "PoolURBG must be parameterized by an unsigned integer type");
-
-  static_assert(kBufferSize > 1,
-                "PoolURBG must be parameterized by a buffer-size > 1");
-
-  static_assert(kBufferSize <= 256,
-                "PoolURBG must be parameterized by a buffer-size <= 256");
-
-  static constexpr result_type(min)() {
-    return (std::numeric_limits<result_type>::min)();
-  }
-
-  static constexpr result_type(max)() {
-    return (std::numeric_limits<result_type>::max)();
-  }
-
-  PoolURBG() : next_(kInitialBuffer) {}
-
-  // copy-constructor does not copy cache.
-  PoolURBG(const PoolURBG&) : next_(kInitialBuffer) {}
-  const PoolURBG& operator=(const PoolURBG&) {
-    next_ = kInitialBuffer;
-    return *this;
-  }
-
-  // move-constructor does move cache.
-  PoolURBG(PoolURBG&&) = default;
-  PoolURBG& operator=(PoolURBG&&) = default;
-
-  inline result_type operator()() {
-    if (next_ >= kBufferSize) {
-      next_ = (kBufferSize > 2 && next_ > kBufferSize) ? kHalfBuffer : 0;
-      PoolType::Fill(SpanType(reinterpret_cast<unsigned_type*>(state_ + next_),
-                              kBufferSize - next_));
-    }
-    return state_[next_++];
-  }
-
- private:
-  // Buffer size.
-  size_t next_;  // index within state_
-  result_type state_[kBufferSize];
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_POOL_URBG_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/pool_urbg_test.cc b/third_party/abseil_cpp/absl/random/internal/pool_urbg_test.cc
deleted file mode 100644
index 53f4eacf16..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/pool_urbg_test.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/pool_urbg.h"
-
-#include <algorithm>
-#include <bitset>
-#include <cmath>
-#include <cstdint>
-#include <iterator>
-
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-#include "absl/types/span.h"
-
-using absl::random_internal::PoolURBG;
-using absl::random_internal::RandenPool;
-
-namespace {
-
-// is_randen_pool trait is true when parameterized by an RandenPool
-template <typename T>
-using is_randen_pool = typename absl::disjunction<  //
-    std::is_same<T, RandenPool<uint8_t>>,           //
-    std::is_same<T, RandenPool<uint16_t>>,          //
-    std::is_same<T, RandenPool<uint32_t>>,          //
-    std::is_same<T, RandenPool<uint64_t>>>;         //
-
-// MyFill either calls RandenPool::Fill() or std::generate(..., rng)
-template <typename T, typename V>
-typename absl::enable_if_t<absl::negation<is_randen_pool<T>>::value, void>  //
-MyFill(T& rng, absl::Span<V> data) {  // NOLINT(runtime/references)
-  std::generate(std::begin(data), std::end(data), rng);
-}
-
-template <typename T, typename V>
-typename absl::enable_if_t<is_randen_pool<T>::value, void>  //
-MyFill(T& rng, absl::Span<V> data) {  // NOLINT(runtime/references)
-  rng.Fill(data);
-}
-
-template <typename EngineType>
-class PoolURBGTypedTest : public ::testing::Test {};
-
-using EngineTypes = ::testing::Types<  //
-    RandenPool<uint8_t>,               //
-    RandenPool<uint16_t>,              //
-    RandenPool<uint32_t>,              //
-    RandenPool<uint64_t>,              //
-    PoolURBG<uint8_t, 2>,              //
-    PoolURBG<uint16_t, 2>,             //
-    PoolURBG<uint32_t, 2>,             //
-    PoolURBG<uint64_t, 2>,             //
-    PoolURBG<unsigned int, 8>,         // NOLINT(runtime/int)
-    PoolURBG<unsigned long, 8>,        // NOLINT(runtime/int)
-    PoolURBG<unsigned long int, 4>,    // NOLINT(runtime/int)
-    PoolURBG<unsigned long long, 4>>;  // NOLINT(runtime/int)
-
-TYPED_TEST_SUITE(PoolURBGTypedTest, EngineTypes);
-
-// This test is checks that the engines meet the URBG interface requirements
-// defined in [rand.req.urbg].
-TYPED_TEST(PoolURBGTypedTest, URBGInterface) {
-  using E = TypeParam;
-  using T = typename E::result_type;
-
-  static_assert(std::is_copy_constructible<E>::value,
-                "engine must be copy constructible");
-
-  static_assert(absl::is_copy_assignable<E>::value,
-                "engine must be copy assignable");
-
-  E e;
-  const E x;
-
-  e();
-
-  static_assert(std::is_same<decltype(e()), T>::value,
-                "return type of operator() must be result_type");
-
-  E u0(x);
-  u0();
-
-  E u1 = e;
-  u1();
-}
-
-// This validates that sequences are independent.
-TYPED_TEST(PoolURBGTypedTest, VerifySequences) {
-  using E = TypeParam;
-  using result_type = typename E::result_type;
-
-  E rng;
-  (void)rng();  // Discard one value.
-
-  constexpr int kNumOutputs = 64;
-  result_type a[kNumOutputs];
-  result_type b[kNumOutputs];
-  std::fill(std::begin(b), std::end(b), 0);
-
-  // Fill a using Fill or generate, depending on the engine type.
-  {
-    E x = rng;
-    MyFill(x, absl::MakeSpan(a));
-  }
-
-  // Fill b using std::generate().
-  {
-    E x = rng;
-    std::generate(std::begin(b), std::end(b), x);
-  }
-
-  // Test that generated sequence changed as sequence of bits, i.e. if about
-  // half of the bites were flipped between two non-correlated values.
-  size_t changed_bits = 0;
-  size_t unchanged_bits = 0;
-  size_t total_set = 0;
-  size_t total_bits = 0;
-  size_t equal_count = 0;
-  for (size_t i = 0; i < kNumOutputs; ++i) {
-    equal_count += (a[i] == b[i]) ? 1 : 0;
-    std::bitset<sizeof(result_type) * 8> bitset(a[i] ^ b[i]);
-    changed_bits += bitset.count();
-    unchanged_bits += bitset.size() - bitset.count();
-
-    std::bitset<sizeof(result_type) * 8> a_set(a[i]);
-    std::bitset<sizeof(result_type) * 8> b_set(b[i]);
-    total_set += a_set.count() + b_set.count();
-    total_bits += 2 * 8 * sizeof(result_type);
-  }
-  // On average, half the bits are changed between two calls.
-  EXPECT_LE(changed_bits, 0.60 * (changed_bits + unchanged_bits));
-  EXPECT_GE(changed_bits, 0.40 * (changed_bits + unchanged_bits));
-
-  // verify using a quick normal-approximation to the binomial.
-  EXPECT_NEAR(total_set, total_bits * 0.5, 4 * std::sqrt(total_bits))
-      << "@" << total_set / static_cast<double>(total_bits);
-
-  // Also, A[i] == B[i] with probability (1/range) * N.
-  // Give this a pretty wide latitude, though.
-  const double kExpected = kNumOutputs / (1.0 * sizeof(result_type) * 8);
-  EXPECT_LE(equal_count, 1.0 + kExpected);
-}
-
-}  // namespace
-
-/*
-$ nanobenchmarks 1 RandenPool construct
-$ nanobenchmarks 1 PoolURBG construct
-
-RandenPool<uint32_t> | 1    | 1000 |    48482.00 ticks | 48.48 ticks | 13.9 ns
-RandenPool<uint32_t> | 10   | 2000 |  1028795.00 ticks | 51.44 ticks | 14.7 ns
-RandenPool<uint32_t> | 100  | 1000 |  5119968.00 ticks | 51.20 ticks | 14.6 ns
-RandenPool<uint32_t> | 1000 |  500 | 25867936.00 ticks | 51.74 ticks | 14.8 ns
-
-RandenPool<uint64_t> | 1    | 1000 |    49921.00 ticks | 49.92 ticks | 14.3 ns
-RandenPool<uint64_t> | 10   | 2000 |  1208269.00 ticks | 60.41 ticks | 17.3 ns
-RandenPool<uint64_t> | 100  | 1000 |  5844955.00 ticks | 58.45 ticks | 16.7 ns
-RandenPool<uint64_t> | 1000 |  500 | 28767404.00 ticks | 57.53 ticks | 16.4 ns
-
-PoolURBG<uint32_t,8> | 1    | 1000 |    86431.00 ticks | 86.43 ticks | 24.7 ns
-PoolURBG<uint32_t,8> | 10   | 1000 |   206191.00 ticks | 20.62 ticks |  5.9 ns
-PoolURBG<uint32_t,8> | 100  | 1000 |  1516049.00 ticks | 15.16 ticks |  4.3 ns
-PoolURBG<uint32_t,8> | 1000 |  500 |  7613936.00 ticks | 15.23 ticks |  4.4 ns
-
-PoolURBG<uint64_t,4> | 1    | 1000 |    96668.00 ticks | 96.67 ticks | 27.6 ns
-PoolURBG<uint64_t,4> | 10   | 1000 |   282423.00 ticks | 28.24 ticks |  8.1 ns
-PoolURBG<uint64_t,4> | 100  | 1000 |  2609587.00 ticks | 26.10 ticks |  7.5 ns
-PoolURBG<uint64_t,4> | 1000 |  500 | 12408757.00 ticks | 24.82 ticks |  7.1 ns
-
-*/
diff --git a/third_party/abseil_cpp/absl/random/internal/randen.cc b/third_party/abseil_cpp/absl/random/internal/randen.cc
deleted file mode 100644
index 78a1e00c08..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/randen.h"
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/randen_detect.h"
-
-// RANDen = RANDom generator or beetroots in Swiss German.
-// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
-// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
-//
-// High-level summary:
-// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is
-//    a sponge-like random generator that requires a cryptographic permutation.
-//    It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by
-//    achieving backtracking resistance with only one Permute() per buffer.
-//
-// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round
-//    Function" constructs up to 1024-bit permutations using an improved
-//    Generalized Feistel network with 2-round AES-128 functions. This Feistel
-//    block shuffle achieves diffusion faster and is less vulnerable to
-//    sliced-biclique attacks than the Type-2 cyclic shuffle.
-//
-// 3) "Improving the Generalized Feistel" and "New criterion for diffusion
-//    property" extends the same kind of improved Feistel block shuffle to 16
-//    branches, which enables a 2048-bit permutation.
-//
-// We combine these three ideas and also change Simpira's subround keys from
-// structured/low-entropy counters to digits of Pi.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-namespace {
-
-struct RandenState {
-  const void* keys;
-  bool has_crypto;
-};
-
-RandenState GetRandenState() {
-  static const RandenState state = []() {
-    RandenState tmp;
-#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
-    // HW AES Dispatch.
-    if (HasRandenHwAesImplementation() && CPUSupportsRandenHwAes()) {
-      tmp.has_crypto = true;
-      tmp.keys = RandenHwAes::GetKeys();
-    } else {
-      tmp.has_crypto = false;
-      tmp.keys = RandenSlow::GetKeys();
-    }
-#elif ABSL_HAVE_ACCELERATED_AES
-    // HW AES is enabled.
-    tmp.has_crypto = true;
-    tmp.keys = RandenHwAes::GetKeys();
-#else
-    // HW AES is disabled.
-    tmp.has_crypto = false;
-    tmp.keys = RandenSlow::GetKeys();
-#endif
-    return tmp;
-  }();
-  return state;
-}
-
-}  // namespace
-
-Randen::Randen() {
-  auto tmp = GetRandenState();
-  keys_ = tmp.keys;
-#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
-  has_crypto_ = tmp.has_crypto;
-#endif
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/randen.h b/third_party/abseil_cpp/absl/random/internal/randen.h
deleted file mode 100644
index c2834aaf3d..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_RANDEN_H_
-#define ABSL_RANDOM_INTERNAL_RANDEN_H_
-
-#include <cstddef>
-
-#include "absl/random/internal/platform.h"
-#include "absl/random/internal/randen_hwaes.h"
-#include "absl/random/internal/randen_slow.h"
-#include "absl/random/internal/randen_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// RANDen = RANDom generator or beetroots in Swiss German.
-// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
-// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
-//
-// Randen implements the basic state manipulation methods.
-class Randen {
- public:
-  static constexpr size_t kStateBytes = RandenTraits::kStateBytes;
-  static constexpr size_t kCapacityBytes = RandenTraits::kCapacityBytes;
-  static constexpr size_t kSeedBytes = RandenTraits::kSeedBytes;
-
-  ~Randen() = default;
-
-  Randen();
-
-  // Generate updates the randen sponge. The outer portion of the sponge
-  // (kCapacityBytes .. kStateBytes) may be consumed as PRNG state.
-  template <typename T, size_t N>
-  void Generate(T (&state)[N]) const {
-    static_assert(N * sizeof(T) == kStateBytes,
-                  "Randen::Generate() requires kStateBytes of state");
-#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
-    // HW AES Dispatch.
-    if (has_crypto_) {
-      RandenHwAes::Generate(keys_, state);
-    } else {
-      RandenSlow::Generate(keys_, state);
-    }
-#elif ABSL_HAVE_ACCELERATED_AES
-    // HW AES is enabled.
-    RandenHwAes::Generate(keys_, state);
-#else
-    // HW AES is disabled.
-    RandenSlow::Generate(keys_, state);
-#endif
-  }
-
-  // Absorb incorporates additional seed material into the randen sponge.  After
-  // absorb returns, Generate must be called before the state may be consumed.
-  template <typename S, size_t M, typename T, size_t N>
-  void Absorb(const S (&seed)[M], T (&state)[N]) const {
-    static_assert(M * sizeof(S) == RandenTraits::kSeedBytes,
-                  "Randen::Absorb() requires kSeedBytes of seed");
-
-    static_assert(N * sizeof(T) == RandenTraits::kStateBytes,
-                  "Randen::Absorb() requires kStateBytes of state");
-#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
-    // HW AES Dispatch.
-    if (has_crypto_) {
-      RandenHwAes::Absorb(seed, state);
-    } else {
-      RandenSlow::Absorb(seed, state);
-    }
-#elif ABSL_HAVE_ACCELERATED_AES
-    // HW AES is enabled.
-    RandenHwAes::Absorb(seed, state);
-#else
-    // HW AES is disabled.
-    RandenSlow::Absorb(seed, state);
-#endif
-  }
-
- private:
-  const void* keys_;
-#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
-  bool has_crypto_;
-#endif
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_RANDEN_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_benchmarks.cc b/third_party/abseil_cpp/absl/random/internal/randen_benchmarks.cc
deleted file mode 100644
index f589172c04..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_benchmarks.cc
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-#include "absl/random/internal/randen.h"
-
-#include <cstdint>
-#include <cstdio>
-#include <cstring>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/nanobenchmark.h"
-#include "absl/random/internal/platform.h"
-#include "absl/random/internal/randen_engine.h"
-#include "absl/random/internal/randen_hwaes.h"
-#include "absl/random/internal/randen_slow.h"
-#include "absl/strings/numbers.h"
-
-namespace {
-
-using absl::random_internal::Randen;
-using absl::random_internal::RandenHwAes;
-using absl::random_internal::RandenSlow;
-
-using absl::random_internal_nanobenchmark::FuncInput;
-using absl::random_internal_nanobenchmark::FuncOutput;
-using absl::random_internal_nanobenchmark::InvariantTicksPerSecond;
-using absl::random_internal_nanobenchmark::MeasureClosure;
-using absl::random_internal_nanobenchmark::Params;
-using absl::random_internal_nanobenchmark::PinThreadToCPU;
-using absl::random_internal_nanobenchmark::Result;
-
-// Local state parameters.
-static constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t);
-static constexpr size_t kSeedSizeT = Randen::kSeedBytes / sizeof(uint32_t);
-
-// Randen implementation benchmarks.
-template <typename T>
-struct AbsorbFn : public T {
-  mutable uint64_t state[kStateSizeT] = {};
-  mutable uint32_t seed[kSeedSizeT] = {};
-
-  static constexpr size_t bytes() { return sizeof(seed); }
-
-  FuncOutput operator()(const FuncInput num_iters) const {
-    for (size_t i = 0; i < num_iters; ++i) {
-      this->Absorb(seed, state);
-    }
-    return state[0];
-  }
-};
-
-template <typename T>
-struct GenerateFn : public T {
-  mutable uint64_t state[kStateSizeT];
-  GenerateFn() { std::memset(state, 0, sizeof(state)); }
-
-  static constexpr size_t bytes() { return sizeof(state); }
-
-  FuncOutput operator()(const FuncInput num_iters) const {
-    const auto* keys = this->GetKeys();
-    for (size_t i = 0; i < num_iters; ++i) {
-      this->Generate(keys, state);
-    }
-    return state[0];
-  }
-};
-
-template <typename UInt>
-struct Engine {
-  mutable absl::random_internal::randen_engine<UInt> rng;
-
-  static constexpr size_t bytes() { return sizeof(UInt); }
-
-  FuncOutput operator()(const FuncInput num_iters) const {
-    for (size_t i = 0; i < num_iters - 1; ++i) {
-      rng();
-    }
-    return rng();
-  }
-};
-
-template <size_t N>
-void Print(const char* name, const size_t n, const Result (&results)[N],
-           const size_t bytes) {
-  if (n == 0) {
-    ABSL_RAW_LOG(
-        WARNING,
-        "WARNING: Measurement failed, should not happen when using "
-        "PinThreadToCPU unless the region to measure takes > 1 second.\n");
-    return;
-  }
-
-  static const double ns_per_tick = 1e9 / InvariantTicksPerSecond();
-  static constexpr const double kNsPerS = 1e9;                 // ns/s
-  static constexpr const double kMBPerByte = 1.0 / 1048576.0;  // Mb / b
-  static auto header = [] {
-    return printf("%20s %8s: %12s ticks; %9s  (%9s) %8s\n", "Name", "Count",
-                  "Total", "Variance", "Time", "bytes/s");
-  }();
-  (void)header;
-
-  for (size_t i = 0; i < n; ++i) {
-    const double ticks_per_call = results[i].ticks / results[i].input;
-    const double ns_per_call = ns_per_tick * ticks_per_call;
-    const double bytes_per_ns = bytes / ns_per_call;
-    const double mb_per_s = bytes_per_ns * kNsPerS * kMBPerByte;
-    // Output
-    printf("%20s %8zu: %12.2f ticks; MAD=%4.2f%%  (%6.1f ns) %8.1f Mb/s\n",
-           name, results[i].input, results[i].ticks,
-           results[i].variability * 100.0, ns_per_call, mb_per_s);
-  }
-}
-
-// Fails here
-template <typename Op, size_t N>
-void Measure(const char* name, const FuncInput (&inputs)[N]) {
-  Op op;
-
-  Result results[N];
-  Params params;
-  params.verbose = false;
-  params.max_evals = 6;  // avoid test timeout
-  const size_t num_results = MeasureClosure(op, inputs, N, results, params);
-  Print(name, num_results, results, op.bytes());
-}
-
-// unpredictable == 1 but the compiler does not know that.
-void RunAll(const int argc, char* argv[]) {
-  if (argc == 2) {
-    int cpu = -1;
-    if (!absl::SimpleAtoi(argv[1], &cpu)) {
-      ABSL_RAW_LOG(FATAL, "The optional argument must be a CPU number >= 0.\n");
-    }
-    PinThreadToCPU(cpu);
-  }
-
-  // The compiler cannot reduce this to a constant.
-  const FuncInput unpredictable = (argc != 999);
-  static const FuncInput inputs[] = {unpredictable * 100, unpredictable * 1000};
-
-#if !defined(ABSL_INTERNAL_DISABLE_AES) && ABSL_HAVE_ACCELERATED_AES
-  Measure<AbsorbFn<RandenHwAes>>("Absorb (HwAes)", inputs);
-#endif
-  Measure<AbsorbFn<RandenSlow>>("Absorb (Slow)", inputs);
-
-#if !defined(ABSL_INTERNAL_DISABLE_AES) && ABSL_HAVE_ACCELERATED_AES
-  Measure<GenerateFn<RandenHwAes>>("Generate (HwAes)", inputs);
-#endif
-  Measure<GenerateFn<RandenSlow>>("Generate (Slow)", inputs);
-
-  // Measure the production engine.
-  static const FuncInput inputs1[] = {unpredictable * 1000,
-                                      unpredictable * 10000};
-  Measure<Engine<uint64_t>>("randen_engine<uint64_t>", inputs1);
-  Measure<Engine<uint32_t>>("randen_engine<uint32_t>", inputs1);
-}
-
-}  // namespace
-
-int main(int argc, char* argv[]) {
-  RunAll(argc, argv);
-  return 0;
-}
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_detect.cc b/third_party/abseil_cpp/absl/random/internal/randen_detect.cc
deleted file mode 100644
index bbe7b96532..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_detect.cc
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
-// symbols from arbitrary system and other headers, since it may be built
-// with different flags from other targets, using different levels of
-// optimization, potentially introducing ODR violations.
-
-#include "absl/random/internal/randen_detect.h"
-
-#include <cstdint>
-#include <cstring>
-
-#include "absl/random/internal/platform.h"
-
-#if defined(ABSL_ARCH_X86_64)
-#define ABSL_INTERNAL_USE_X86_CPUID
-#elif defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \
-    defined(ABSL_ARCH_AARCH64)
-#if defined(__ANDROID__)
-#define ABSL_INTERNAL_USE_ANDROID_GETAUXVAL
-#define ABSL_INTERNAL_USE_GETAUXVAL
-#elif defined(__linux__)
-#define ABSL_INTERNAL_USE_LINUX_GETAUXVAL
-#define ABSL_INTERNAL_USE_GETAUXVAL
-#endif
-#endif
-
-#if defined(ABSL_INTERNAL_USE_X86_CPUID)
-#if defined(_WIN32) || defined(_WIN64)
-#include <intrin.h>  // NOLINT(build/include_order)
-#pragma intrinsic(__cpuid)
-#else
-// MSVC-equivalent __cpuid intrinsic function.
-static void __cpuid(int cpu_info[4], int info_type) {
-  __asm__ volatile("cpuid \n\t"
-                   : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
-                     "=d"(cpu_info[3])
-                   : "a"(info_type), "c"(0));
-}
-#endif
-#endif  // ABSL_INTERNAL_USE_X86_CPUID
-
-// On linux, just use the c-library getauxval call.
-#if defined(ABSL_INTERNAL_USE_LINUX_GETAUXVAL)
-
-extern "C" unsigned long getauxval(unsigned long type);  // NOLINT(runtime/int)
-
-static uint32_t GetAuxval(uint32_t hwcap_type) {
-  return static_cast<uint32_t>(getauxval(hwcap_type));
-}
-
-#endif
-
-// On android, probe the system's C library for getauxval().
-// This is the same technique used by the android NDK cpu features library
-// as well as the google open-source cpu_features library.
-//
-// TODO(absl-team): Consider implementing a fallback of directly reading
-// /proc/self/auxval.
-#if defined(ABSL_INTERNAL_USE_ANDROID_GETAUXVAL)
-#include <dlfcn.h>
-
-static uint32_t GetAuxval(uint32_t hwcap_type) {
-  // NOLINTNEXTLINE(runtime/int)
-  typedef unsigned long (*getauxval_func_t)(unsigned long);
-
-  dlerror();  // Cleaning error state before calling dlopen.
-  void* libc_handle = dlopen("libc.so", RTLD_NOW);
-  if (!libc_handle) {
-    return 0;
-  }
-  uint32_t result = 0;
-  void* sym = dlsym(libc_handle, "getauxval");
-  if (sym) {
-    getauxval_func_t func;
-    memcpy(&func, &sym, sizeof(func));
-    result = static_cast<uint32_t>((*func)(hwcap_type));
-  }
-  dlclose(libc_handle);
-  return result;
-}
-
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// The default return at the end of the function might be unreachable depending
-// on the configuration. Ignore that warning.
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunreachable-code-return"
-#endif
-
-// CPUSupportsRandenHwAes returns whether the CPU is a microarchitecture
-// which supports the crpyto/aes instructions or extensions necessary to use the
-// accelerated RandenHwAes implementation.
-//
-// 1. For x86 it is sufficient to use the CPUID instruction to detect whether
-//    the cpu supports AES instructions. Done.
-//
-// Fon non-x86 it is much more complicated.
-//
-// 2. When ABSL_INTERNAL_USE_GETAUXVAL is defined, use getauxval() (either
-//    the direct c-library version, or the android probing version which loads
-//    libc), and read the hardware capability bits.
-//    This is based on the technique used by boringssl uses to detect
-//    cpu capabilities, and should allow us to enable crypto in the android
-//    builds where it is supported.
-//
-// 3. Use the default for the compiler architecture.
-//
-
-bool CPUSupportsRandenHwAes() {
-#if defined(ABSL_INTERNAL_USE_X86_CPUID)
-  // 1. For x86: Use CPUID to detect the required AES instruction set.
-  int regs[4];
-  __cpuid(reinterpret_cast<int*>(regs), 1);
-  return regs[2] & (1 << 25);  // AES
-
-#elif defined(ABSL_INTERNAL_USE_GETAUXVAL)
-  // 2. Use getauxval() to read the hardware bits and determine
-  // cpu capabilities.
-
-#define AT_HWCAP 16
-#define AT_HWCAP2 26
-#if defined(ABSL_ARCH_PPC)
-  // For Power / PPC: Expect that the cpu supports VCRYPTO
-  // See https://members.openpowerfoundation.org/document/dl/576
-  // VCRYPTO should be present in POWER8 >= 2.07.
-  // Uses Linux kernel constants from arch/powerpc/include/uapi/asm/cputable.h
-  static const uint32_t kVCRYPTO = 0x02000000;
-  const uint32_t hwcap = GetAuxval(AT_HWCAP2);
-  return (hwcap & kVCRYPTO) != 0;
-
-#elif defined(ABSL_ARCH_ARM)
-  // For ARM: Require crypto+neon
-  // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500f/CIHBIBBA.html
-  // Uses Linux kernel constants from arch/arm64/include/asm/hwcap.h
-  static const uint32_t kNEON = 1 << 12;
-  uint32_t hwcap = GetAuxval(AT_HWCAP);
-  if ((hwcap & kNEON) == 0) {
-    return false;
-  }
-
-  // And use it again to detect AES.
-  static const uint32_t kAES = 1 << 0;
-  const uint32_t hwcap2 = GetAuxval(AT_HWCAP2);
-  return (hwcap2 & kAES) != 0;
-
-#elif defined(ABSL_ARCH_AARCH64)
-  // For AARCH64: Require crypto+neon
-  // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500f/CIHBIBBA.html
-  static const uint32_t kNEON = 1 << 1;
-  static const uint32_t kAES = 1 << 3;
-  const uint32_t hwcap = GetAuxval(AT_HWCAP);
-  return ((hwcap & kNEON) != 0) && ((hwcap & kAES) != 0);
-#endif
-
-#else  // ABSL_INTERNAL_USE_GETAUXVAL
-  // 3. By default, assume that the compiler default.
-  return ABSL_HAVE_ACCELERATED_AES ? true : false;
-
-#endif
-  // NOTE: There are some other techniques that may be worth trying:
-  //
-  // * Use an environment variable: ABSL_RANDOM_USE_HWAES
-  //
-  // * Rely on compiler-generated target-based dispatch.
-  // Using x86/gcc it might look something like this:
-  //
-  // int __attribute__((target("aes"))) HasAes() { return 1; }
-  // int __attribute__((target("default"))) HasAes() { return 0; }
-  //
-  // This does not work on all architecture/compiler combinations.
-  //
-  // * On Linux consider reading /proc/cpuinfo and/or /proc/self/auxv.
-  // These files have lines which are easy to parse; for ARM/AARCH64 it is quite
-  // easy to find the Features: line and extract aes / neon. Likewise for
-  // PPC.
-  //
-  // * Fork a process and test for SIGILL:
-  //
-  // * Many architectures have instructions to read the ISA. Unfortunately
-  //   most of those require that the code is running in ring 0 /
-  //   protected-mode.
-  //
-  //   There are several examples. e.g. Valgrind detects PPC ISA 2.07:
-  //   https://github.com/lu-zero/valgrind/blob/master/none/tests/ppc64/test_isa_2_07_part1.c
-  //
-  //   MRS <Xt>, ID_AA64ISAR0_EL1 ; Read ID_AA64ISAR0_EL1 into Xt
-  //
-  //   uint64_t val;
-  //   __asm __volatile("mrs %0, id_aa64isar0_el1" :"=&r" (val));
-  //
-  // * Use a CPUID-style heuristic database.
-  //
-  // * On Apple (__APPLE__), AES is available on Arm v8.
-  //   https://stackoverflow.com/questions/45637888/how-to-determine-armv8-features-at-runtime-on-ios
-}
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_detect.h b/third_party/abseil_cpp/absl/random/internal/randen_detect.h
deleted file mode 100644
index f283f43226..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_detect.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_
-#define ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// Returns whether the current CPU supports RandenHwAes implementation.
-// This typically involves supporting cryptographic extensions on whichever
-// platform is currently running.
-bool CPUSupportsRandenHwAes();
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_engine.h b/third_party/abseil_cpp/absl/random/internal/randen_engine.h
deleted file mode 100644
index 6b33731336..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_engine.h
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_
-#define ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_
-
-#include <algorithm>
-#include <cinttypes>
-#include <cstdlib>
-#include <iostream>
-#include <iterator>
-#include <limits>
-#include <type_traits>
-
-#include "absl/meta/type_traits.h"
-#include "absl/random/internal/iostream_state_saver.h"
-#include "absl/random/internal/randen.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// Deterministic pseudorandom byte generator with backtracking resistance
-// (leaking the state does not compromise prior outputs). Based on Reverie
-// (see "A Robust and Sponge-Like PRNG with Improved Efficiency") instantiated
-// with an improved Simpira-like permutation.
-// Returns values of type "T" (must be a built-in unsigned integer type).
-//
-// RANDen = RANDom generator or beetroots in Swiss High German.
-// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
-// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
-template <typename T>
-class alignas(16) randen_engine {
- public:
-  // C++11 URBG interface:
-  using result_type = T;
-  static_assert(std::is_unsigned<result_type>::value,
-                "randen_engine template argument must be a built-in unsigned "
-                "integer type");
-
-  static constexpr result_type(min)() {
-    return (std::numeric_limits<result_type>::min)();
-  }
-
-  static constexpr result_type(max)() {
-    return (std::numeric_limits<result_type>::max)();
-  }
-
-  explicit randen_engine(result_type seed_value = 0) { seed(seed_value); }
-
-  template <class SeedSequence,
-            typename = typename absl::enable_if_t<
-                !std::is_same<SeedSequence, randen_engine>::value>>
-  explicit randen_engine(SeedSequence&& seq) {
-    seed(seq);
-  }
-
-  randen_engine(const randen_engine&) = default;
-
-  // Returns random bits from the buffer in units of result_type.
-  result_type operator()() {
-    // Refill the buffer if needed (unlikely).
-    if (next_ >= kStateSizeT) {
-      next_ = kCapacityT;
-      impl_.Generate(state_);
-    }
-
-    return state_[next_++];
-  }
-
-  template <class SeedSequence>
-  typename absl::enable_if_t<
-      !std::is_convertible<SeedSequence, result_type>::value>
-  seed(SeedSequence&& seq) {
-    // Zeroes the state.
-    seed();
-    reseed(seq);
-  }
-
-  void seed(result_type seed_value = 0) {
-    next_ = kStateSizeT;
-    // Zeroes the inner state and fills the outer state with seed_value to
-    // mimics behaviour of reseed
-    std::fill(std::begin(state_), std::begin(state_) + kCapacityT, 0);
-    std::fill(std::begin(state_) + kCapacityT, std::end(state_), seed_value);
-  }
-
-  // Inserts entropy into (part of) the state. Calling this periodically with
-  // sufficient entropy ensures prediction resistance (attackers cannot predict
-  // future outputs even if state is compromised).
-  template <class SeedSequence>
-  void reseed(SeedSequence& seq) {
-    using sequence_result_type = typename SeedSequence::result_type;
-    static_assert(sizeof(sequence_result_type) == 4,
-                  "SeedSequence::result_type must be 32-bit");
-
-    constexpr size_t kBufferSize =
-        Randen::kSeedBytes / sizeof(sequence_result_type);
-    alignas(16) sequence_result_type buffer[kBufferSize];
-
-    // Randen::Absorb XORs the seed into state, which is then mixed by a call
-    // to Randen::Generate. Seeding with only the provided entropy is preferred
-    // to using an arbitrary generate() call, so use [rand.req.seed_seq]
-    // size as a proxy for the number of entropy units that can be generated
-    // without relying on seed sequence mixing...
-    const size_t entropy_size = seq.size();
-    if (entropy_size < kBufferSize) {
-      // ... and only request that many values, or 256-bits, when unspecified.
-      const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size;
-      std::fill(std::begin(buffer) + requested_entropy, std::end(buffer), 0);
-      seq.generate(std::begin(buffer), std::begin(buffer) + requested_entropy);
-      // The Randen paper suggests preferentially initializing even-numbered
-      // 128-bit vectors of the randen state (there are 16 such vectors).
-      // The seed data is merged into the state offset by 128-bits, which
-      // implies prefering seed bytes [16..31, ..., 208..223]. Since the
-      // buffer is 32-bit values, we swap the corresponding buffer positions in
-      // 128-bit chunks.
-      size_t dst = kBufferSize;
-      while (dst > 7) {
-        // leave the odd bucket as-is.
-        dst -= 4;
-        size_t src = dst >> 1;
-        // swap 128-bits into the even bucket
-        std::swap(buffer[--dst], buffer[--src]);
-        std::swap(buffer[--dst], buffer[--src]);
-        std::swap(buffer[--dst], buffer[--src]);
-        std::swap(buffer[--dst], buffer[--src]);
-      }
-    } else {
-      seq.generate(std::begin(buffer), std::end(buffer));
-    }
-    impl_.Absorb(buffer, state_);
-
-    // Generate will be called when operator() is called
-    next_ = kStateSizeT;
-  }
-
-  void discard(uint64_t count) {
-    uint64_t step = std::min<uint64_t>(kStateSizeT - next_, count);
-    count -= step;
-
-    constexpr uint64_t kRateT = kStateSizeT - kCapacityT;
-    while (count > 0) {
-      next_ = kCapacityT;
-      impl_.Generate(state_);
-      step = std::min<uint64_t>(kRateT, count);
-      count -= step;
-    }
-    next_ += step;
-  }
-
-  bool operator==(const randen_engine& other) const {
-    return next_ == other.next_ &&
-           std::equal(std::begin(state_), std::end(state_),
-                      std::begin(other.state_));
-  }
-
-  bool operator!=(const randen_engine& other) const {
-    return !(*this == other);
-  }
-
-  template <class CharT, class Traits>
-  friend std::basic_ostream<CharT, Traits>& operator<<(
-      std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-      const randen_engine<T>& engine) {       // NOLINT(runtime/references)
-    using numeric_type =
-        typename random_internal::stream_format_type<result_type>::type;
-    auto saver = random_internal::make_ostream_state_saver(os);
-    for (const auto& elem : engine.state_) {
-      // In the case that `elem` is `uint8_t`, it must be cast to something
-      // larger so that it prints as an integer rather than a character. For
-      // simplicity, apply the cast all circumstances.
-      os << static_cast<numeric_type>(elem) << os.fill();
-    }
-    os << engine.next_;
-    return os;
-  }
-
-  template <class CharT, class Traits>
-  friend std::basic_istream<CharT, Traits>& operator>>(
-      std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-      randen_engine<T>& engine) {             // NOLINT(runtime/references)
-    using numeric_type =
-        typename random_internal::stream_format_type<result_type>::type;
-    result_type state[kStateSizeT];
-    size_t next;
-    for (auto& elem : state) {
-      // It is not possible to read uint8_t from wide streams, so it is
-      // necessary to read a wider type and then cast it to uint8_t.
-      numeric_type value;
-      is >> value;
-      elem = static_cast<result_type>(value);
-    }
-    is >> next;
-    if (is.fail()) {
-      return is;
-    }
-    std::memcpy(engine.state_, state, sizeof(engine.state_));
-    engine.next_ = next;
-    return is;
-  }
-
- private:
-  static constexpr size_t kStateSizeT =
-      Randen::kStateBytes / sizeof(result_type);
-  static constexpr size_t kCapacityT =
-      Randen::kCapacityBytes / sizeof(result_type);
-
-  // First kCapacityT are `inner', the others are accessible random bits.
-  alignas(16) result_type state_[kStateSizeT];
-  size_t next_;  // index within state_
-  Randen impl_;
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_engine_test.cc b/third_party/abseil_cpp/absl/random/internal/randen_engine_test.cc
deleted file mode 100644
index c8e7685bdd..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_engine_test.cc
+++ /dev/null
@@ -1,656 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/randen_engine.h"
-
-#include <algorithm>
-#include <bitset>
-#include <random>
-#include <sstream>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/explicit_seed_seq.h"
-#include "absl/strings/str_cat.h"
-#include "absl/time/clock.h"
-
-#define UPDATE_GOLDEN 0
-
-using randen_u64 = absl::random_internal::randen_engine<uint64_t>;
-using randen_u32 = absl::random_internal::randen_engine<uint32_t>;
-using absl::random_internal::ExplicitSeedSeq;
-
-namespace {
-
-template <typename UIntType>
-class RandenEngineTypedTest : public ::testing::Test {};
-
-using UIntTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t>;
-
-TYPED_TEST_SUITE(RandenEngineTypedTest, UIntTypes);
-
-TYPED_TEST(RandenEngineTypedTest, VerifyReseedChangesAllValues) {
-  using randen = typename absl::random_internal::randen_engine<TypeParam>;
-  using result_type = typename randen::result_type;
-
-  const size_t kNumOutputs = (sizeof(randen) * 2 / sizeof(TypeParam)) + 1;
-  randen engine;
-
-  // MSVC emits error 2719 without the use of std::ref below.
-  //  * formal parameter with __declspec(align('#')) won't be aligned
-
-  {
-    std::seed_seq seq1{1, 2, 3, 4, 5, 6, 7};
-    engine.seed(seq1);
-  }
-  result_type a[kNumOutputs];
-  std::generate(std::begin(a), std::end(a), std::ref(engine));
-
-  {
-    std::random_device rd;
-    std::seed_seq seq2{rd(), rd(), rd()};
-    engine.seed(seq2);
-  }
-  result_type b[kNumOutputs];
-  std::generate(std::begin(b), std::end(b), std::ref(engine));
-
-  // Test that generated sequence changed as sequence of bits, i.e. if about
-  // half of the bites were flipped between two non-correlated values.
-  size_t changed_bits = 0;
-  size_t unchanged_bits = 0;
-  size_t total_set = 0;
-  size_t total_bits = 0;
-  size_t equal_count = 0;
-  for (size_t i = 0; i < kNumOutputs; ++i) {
-    equal_count += (a[i] == b[i]) ? 1 : 0;
-    std::bitset<sizeof(result_type) * 8> bitset(a[i] ^ b[i]);
-    changed_bits += bitset.count();
-    unchanged_bits += bitset.size() - bitset.count();
-
-    std::bitset<sizeof(result_type) * 8> a_set(a[i]);
-    std::bitset<sizeof(result_type) * 8> b_set(b[i]);
-    total_set += a_set.count() + b_set.count();
-    total_bits += 2 * 8 * sizeof(result_type);
-  }
-  // On average, half the bits are changed between two calls.
-  EXPECT_LE(changed_bits, 0.60 * (changed_bits + unchanged_bits));
-  EXPECT_GE(changed_bits, 0.40 * (changed_bits + unchanged_bits));
-
-  // Verify using a quick normal-approximation to the binomial.
-  EXPECT_NEAR(total_set, total_bits * 0.5, 4 * std::sqrt(total_bits))
-      << "@" << total_set / static_cast<double>(total_bits);
-
-  // Also, A[i] == B[i] with probability (1/range) * N.
-  // Give this a pretty wide latitude, though.
-  const double kExpected = kNumOutputs / (1.0 * sizeof(result_type) * 8);
-  EXPECT_LE(equal_count, 1.0 + kExpected);
-}
-
-// Number of values that needs to be consumed to clean two sizes of buffer
-// and trigger third refresh. (slightly overestimates the actual state size).
-constexpr size_t kTwoBufferValues = sizeof(randen_u64) / sizeof(uint16_t) + 1;
-
-TYPED_TEST(RandenEngineTypedTest, VerifyDiscard) {
-  using randen = typename absl::random_internal::randen_engine<TypeParam>;
-
-  for (size_t num_used = 0; num_used < kTwoBufferValues; ++num_used) {
-    randen engine_used;
-    for (size_t i = 0; i < num_used; ++i) {
-      engine_used();
-    }
-
-    for (size_t num_discard = 0; num_discard < kTwoBufferValues;
-         ++num_discard) {
-      randen engine1 = engine_used;
-      randen engine2 = engine_used;
-      for (size_t i = 0; i < num_discard; ++i) {
-        engine1();
-      }
-      engine2.discard(num_discard);
-      for (size_t i = 0; i < kTwoBufferValues; ++i) {
-        const auto r1 = engine1();
-        const auto r2 = engine2();
-        ASSERT_EQ(r1, r2) << "used=" << num_used << " discard=" << num_discard;
-      }
-    }
-  }
-}
-
-TYPED_TEST(RandenEngineTypedTest, StreamOperatorsResult) {
-  using randen = typename absl::random_internal::randen_engine<TypeParam>;
-  std::wostringstream os;
-  std::wistringstream is;
-  randen engine;
-
-  EXPECT_EQ(&(os << engine), &os);
-  EXPECT_EQ(&(is >> engine), &is);
-}
-
-TYPED_TEST(RandenEngineTypedTest, StreamSerialization) {
-  using randen = typename absl::random_internal::randen_engine<TypeParam>;
-
-  for (size_t discard = 0; discard < kTwoBufferValues; ++discard) {
-    ExplicitSeedSeq seed_sequence{12, 34, 56};
-    randen engine(seed_sequence);
-    engine.discard(discard);
-
-    std::stringstream stream;
-    stream << engine;
-
-    randen new_engine;
-    stream >> new_engine;
-    for (size_t i = 0; i < 64; ++i) {
-      EXPECT_EQ(engine(), new_engine()) << " " << i;
-    }
-  }
-}
-
-constexpr size_t kNumGoldenOutputs = 127;
-
-// This test is checking if randen_engine is meets interface requirements
-// defined in [rand.req.urbg].
-TYPED_TEST(RandenEngineTypedTest, RandomNumberEngineInterface) {
-  using randen = typename absl::random_internal::randen_engine<TypeParam>;
-
-  using E = randen;
-  using T = typename E::result_type;
-
-  static_assert(std::is_copy_constructible<E>::value,
-                "randen_engine must be copy constructible");
-
-  static_assert(absl::is_copy_assignable<E>::value,
-                "randen_engine must be copy assignable");
-
-  static_assert(std::is_move_constructible<E>::value,
-                "randen_engine must be move constructible");
-
-  static_assert(absl::is_move_assignable<E>::value,
-                "randen_engine must be move assignable");
-
-  static_assert(std::is_same<decltype(std::declval<E>()()), T>::value,
-                "return type of operator() must be result_type");
-
-  // Names after definition of [rand.req.urbg] in C++ standard.
-  // e us a value of E
-  // v is a lvalue of E
-  // x, y are possibly const values of E
-  // s is a value of T
-  // q is a value satisfying requirements of seed_sequence
-  // z is a value of type unsigned long long
-  // os is a some specialization of basic_ostream
-  // is is a some specialization of basic_istream
-
-  E e, v;
-  const E x, y;
-  T s = 1;
-  std::seed_seq q{1, 2, 3};
-  unsigned long long z = 1;  // NOLINT(runtime/int)
-  std::wostringstream os;
-  std::wistringstream is;
-
-  E{};
-  E{x};
-  E{s};
-  E{q};
-
-  e.seed();
-
-  // MSVC emits error 2718 when using EXPECT_EQ(e, x)
-  //  * actual parameter with __declspec(align('#')) won't be aligned
-  EXPECT_TRUE(e == x);
-
-  e.seed(q);
-  {
-    E tmp(q);
-    EXPECT_TRUE(e == tmp);
-  }
-
-  e();
-  {
-    E tmp(q);
-    EXPECT_TRUE(e != tmp);
-  }
-
-  e.discard(z);
-
-  static_assert(std::is_same<decltype(x == y), bool>::value,
-                "return type of operator== must be bool");
-
-  static_assert(std::is_same<decltype(x != y), bool>::value,
-                "return type of operator== must be bool");
-}
-
-TYPED_TEST(RandenEngineTypedTest, RandenEngineSFINAETest) {
-  using randen = typename absl::random_internal::randen_engine<TypeParam>;
-  using result_type = typename randen::result_type;
-
-  {
-    randen engine(result_type(1));
-    engine.seed(result_type(1));
-  }
-
-  {
-    result_type n = 1;
-    randen engine(n);
-    engine.seed(n);
-  }
-
-  {
-    randen engine(1);
-    engine.seed(1);
-  }
-
-  {
-    int n = 1;
-    randen engine(n);
-    engine.seed(n);
-  }
-
-  {
-    std::seed_seq seed_seq;
-    randen engine(seed_seq);
-    engine.seed(seed_seq);
-  }
-
-  {
-    randen engine{std::seed_seq()};
-    engine.seed(std::seed_seq());
-  }
-}
-
-TEST(RandenTest, VerifyGoldenRanden64Default) {
-  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
-      0xc3c14f134e433977, 0xdda9f47cd90410ee, 0x887bf3087fd8ca10,
-      0xf0b780f545c72912, 0x15dbb1d37696599f, 0x30ec63baff3c6d59,
-      0xb29f73606f7f20a6, 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e,
-      0x9cbf605e3fd9de8a, 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5,
-      0xf4b327fe0fc73c37, 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d,
-      0xd5af05dd3eff9556, 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c,
-      0x58d3575834956d42, 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3,
-      0x497cabf3431154fc, 0x4e24370570029a8b, 0xd88b5749f090e5ea,
-      0xc651a582a970692f, 0x78fcec2cbb6342f5, 0x463cb745612f55db,
-      0x352ee4ad1816afe3, 0x026ff374c101da7e, 0x811ef0821c3de851,
-      0x6f7e616704c4fa59, 0xa0660379992d58fc, 0x04b0a374a3b795c7,
-      0x915f3445685da798, 0x26802a8ac76571ce, 0x4663352533ce1882,
-      0xb9fdefb4a24dc738, 0x5588ba3a4d6e6c51, 0xa2101a42d35f1956,
-      0x607195a5e200f5fd, 0x7e100308f3290764, 0xe1e5e03c759c0709,
-      0x082572cc5da6606f, 0xcbcf585399e432f1, 0xe8a2be4f8335d8f1,
-      0x0904469acbfee8f2, 0xf08bd31b6daecd51, 0x08e8a1f1a69da69a,
-      0x6542a20aad57bff5, 0x2e9705bb053d6b46, 0xda2fc9db0713c391,
-      0x78e3a810213b6ffb, 0xdc16a59cdd85f8a6, 0xc0932718cd55781f,
-      0xb9bfb29c2b20bfe5, 0xb97289c1be0f2f9c, 0xc0a2a0e403a892d4,
-      0x5524bb834771435b, 0x8265da3d39d1a750, 0xff4af3ab8d1b78c5,
-      0xf0ec5f424bcad77f, 0x66e455f627495189, 0xc82d3120b57e3270,
-      0x3424e47dc22596e3, 0xbc0c95129ccedcdd, 0xc191c595afc4dcbf,
-      0x120392bd2bb70939, 0x7f90650ea6cd6ab4, 0x7287491832695ad3,
-      0xa7c8fac5a7917eb0, 0xd088cb9418be0361, 0x7c1bf9839c7c1ce5,
-      0xe2e991fa58e1e79e, 0x78565cdefd28c4ad, 0x7351b9fef98bafad,
-      0x2a9eac28b08c96bf, 0x6c4f179696cb2225, 0x13a685861bab87e0,
-      0x64c6de5aa0501971, 0x30537425cac70991, 0x01590d9dc6c532b7,
-      0x7e05e3aa8ec720dc, 0x74a07d9c54e3e63f, 0x738184388f3bc1d2,
-      0x26ffdc5067be3acb, 0x6bcdf185561f255f, 0xa0eaf2e1cf99b1c6,
-      0x171df81934f68604, 0x7ea5a21665683e5a, 0x5d1cb02075ba1cea,
-      0x957f38cbd2123fdf, 0xba6364eff80de02f, 0x606e0a0e41d452ee,
-      0x892d8317de82f7a2, 0xe707b1db50f7b43e, 0x4eb28826766fcf5b,
-      0x5a362d56e80a0951, 0x6ee217df16527d78, 0xf6737962ba6b23dd,
-      0x443e63857d4076ca, 0x790d9a5f048adfeb, 0xd796b052151ee94d,
-      0x033ed95c12b04a03, 0x8b833ff84893da5d, 0x3d6724b1bb15eab9,
-      0x9877c4225061ca76, 0xd68d6810adf74fb3, 0x42e5352fe30ce989,
-      0x265b565a7431fde7, 0x3cdbf7e358df4b8b, 0x2922a47f6d3e8779,
-      0x52d2242f65b37f88, 0x5d836d6e2958d6b5, 0x29d40f00566d5e26,
-      0x288db0e1124b14a0, 0x6c056608b7d9c1b6, 0x0b9471bdb8f19d32,
-      0x8fb946504faa6c9d, 0x8943a9464540251c, 0xfd1fe27d144a09e0,
-      0xea6ac458da141bda, 0x8048f217633fce36, 0xfeda1384ade74d31,
-      0x4334b8b02ff7612f, 0xdbc8441f5227e216, 0x096d119a3605c85b,
-      0x2b72b31c21b7d7d0};
-
-  randen_u64 engine;
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
-    printf("0x%016lx, ", engine());
-    if (i % 3 == 2) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-  engine.seed();
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-TEST(RandenTest, VerifyGoldenRanden64Seeded) {
-  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
-      0x83a9e58f94d3dcd5, 0x70bbdff3d97949fb, 0x0438481f7471c1b4,
-      0x34fdc58ee5fb5930, 0xceee4f2d2a937d17, 0xb5a26a68e432aea9,
-      0x8b64774a3fb51740, 0xd89ac1fc74249c74, 0x03910d1d23fc3fdf,
-      0xd38f630878aa897f, 0x0ee8f0f5615f7e44, 0x98f5a53df8279d52,
-      0xb403f52c25938d0e, 0x240072996ea6e838, 0xd3a791246190fa61,
-      0xaaedd3df7a7b4f80, 0xc6eacabe05deaf6e, 0xb7967dd8790edf4d,
-      0x9a0a8e67e049d279, 0x0494f606aebc23e7, 0x598dcd687bc3e0ee,
-      0x010ac81802d452a1, 0x6407c87160aa2842, 0x5a56e276486f93a0,
-      0xc887a399d46a8f02, 0x9e1e6100fe93b740, 0x12d02e330f8901f6,
-      0xc39ca52b47e790b7, 0xb0b0a2fa11e82e61, 0x1542d841a303806a,
-      0x1fe659fd7d6e9d86, 0xb8c90d80746541ac, 0x239d56a5669ddc94,
-      0xd40db57c8123d13c, 0x3abc2414153a0db0, 0x9bad665630cb8d61,
-      0x0bd1fb90ee3f4bbc, 0x8f0b4d7e079b4e42, 0xfa0fb0e0ee59e793,
-      0x51080b283e071100, 0x2c4b9e715081cc15, 0xbe10ed49de4941df,
-      0xf8eaac9d4b1b0d37, 0x4bcce4b54605e139, 0xa64722b76765dda6,
-      0xb9377d738ca28ab5, 0x779fad81a8ccc1af, 0x65cb3ee61ffd3ba7,
-      0xd74e79087862836f, 0xd05b9c584c3f25bf, 0x2ba93a4693579827,
-      0xd81530aff05420ce, 0xec06cea215478621, 0x4b1798a6796d65ad,
-      0xf142f3fb3a6f6fa6, 0x002b7bf7e237b560, 0xf47f2605ef65b4f8,
-      0x9804ec5517effc18, 0xaed3d7f8b7d481cd, 0x5651c24c1ce338d1,
-      0x3e7a38208bf0a3c6, 0x6796a7b614534aed, 0x0d0f3b848358460f,
-      0x0fa5fe7600b19524, 0x2b0cf38253faaedc, 0x10df9188233a9fd6,
-      0x3a10033880138b59, 0x5fb0b0d23948e80f, 0x9e76f7b02fbf5350,
-      0x0816052304b1a985, 0x30c9880db41fd218, 0x14aa399b65e20f28,
-      0xe1454a8cace787b4, 0x325ac971b6c6f0f5, 0x716b1aa2784f3d36,
-      0x3d5ce14accfd144f, 0x6c0c97710f651792, 0xbc5b0f59fb333532,
-      0x2a90a7d2140470bc, 0x8da269f55c1e1c8d, 0xcfc37143895792ca,
-      0xbe21eab1f30b238f, 0x8c47229dee4d65fd, 0x5743614ed1ed7d54,
-      0x351372a99e9c476e, 0x2bd5ea15e5db085f, 0x6925fde46e0af4ca,
-      0xed3eda2bdc1f45bd, 0xdef68c68d460fa6e, 0xe42a0de76253e2b5,
-      0x4e5176dcbc29c305, 0xbfd85fba9f810f6e, 0x76a5a2a9beb815c6,
-      0x01edc4ddceaf414c, 0xa4e98904b4bb3b4b, 0x00bd63ac7d2f1ddd,
-      0xb8491fe6e998ddbb, 0xb386a3463dda6800, 0x0081887688871619,
-      0x33d394b3344e9a38, 0x815dba65a3a8baf9, 0x4232f6ec02c2fd1a,
-      0xb5cff603edd20834, 0x580189243f687663, 0xa8d5a2cbdc27fe99,
-      0x725d881693fa0131, 0xa2be2c13db2c7ac5, 0x7b6a9614b509fd78,
-      0xb6b136d71e717636, 0x660f1a71aff046ea, 0x0ba10ae346c8ec9e,
-      0xe66dde53e3145b41, 0x3b18288c88c26be6, 0x4d9d9d2ff02db933,
-      0x4167da8c70f46e8a, 0xf183beef8c6318b4, 0x4d889e1e71eeeef1,
-      0x7175c71ad6689b6b, 0xfb9e42beacd1b7dd, 0xc33d0e91b29b5e0d,
-      0xd39b83291ce47922, 0xc4d570fb8493d12e, 0x23d5a5724f424ae6,
-      0x5245f161876b6616, 0x38d77dbd21ab578d, 0x9c3423311f4ecbfe,
-      0x76fe31389bacd9d5,
-  };
-
-  ExplicitSeedSeq seed_sequence{12, 34, 56};
-  randen_u64 engine(seed_sequence);
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
-    printf("0x%016lx, ", engine());
-    if (i % 3 == 2) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-  engine.seed(seed_sequence);
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-TEST(RandenTest, VerifyGoldenRanden32Default) {
-  constexpr uint64_t kGolden[2 * kNumGoldenOutputs] = {
-      0x4e433977, 0xc3c14f13, 0xd90410ee, 0xdda9f47c, 0x7fd8ca10, 0x887bf308,
-      0x45c72912, 0xf0b780f5, 0x7696599f, 0x15dbb1d3, 0xff3c6d59, 0x30ec63ba,
-      0x6f7f20a6, 0xb29f7360, 0x6f49a54c, 0x02808a31, 0xd5c8e50e, 0x3b8feaf9,
-      0x3fd9de8a, 0x9cbf605e, 0x78183bbb, 0xc970ae1a, 0x56301ed5, 0xd8b2ffd3,
-      0x0fc73c37, 0xf4b327fe, 0xeb8f9a19, 0xcdfd8d76, 0x91420c9d, 0xc3a506eb,
-      0x3eff9556, 0xd5af05dd, 0x8f83c4a1, 0x48db1bb7, 0x0d6bfe8c, 0x7023920e,
-      0x34956d42, 0x58d35758, 0x6b87b840, 0xed1ef4c2, 0x3e0b2df3, 0x8eef32a2,
-      0x431154fc, 0x497cabf3, 0x70029a8b, 0x4e243705, 0xf090e5ea, 0xd88b5749,
-      0xa970692f, 0xc651a582, 0xbb6342f5, 0x78fcec2c, 0x612f55db, 0x463cb745,
-      0x1816afe3, 0x352ee4ad, 0xc101da7e, 0x026ff374, 0x1c3de851, 0x811ef082,
-      0x04c4fa59, 0x6f7e6167, 0x992d58fc, 0xa0660379, 0xa3b795c7, 0x04b0a374,
-      0x685da798, 0x915f3445, 0xc76571ce, 0x26802a8a, 0x33ce1882, 0x46633525,
-      0xa24dc738, 0xb9fdefb4, 0x4d6e6c51, 0x5588ba3a, 0xd35f1956, 0xa2101a42,
-      0xe200f5fd, 0x607195a5, 0xf3290764, 0x7e100308, 0x759c0709, 0xe1e5e03c,
-      0x5da6606f, 0x082572cc, 0x99e432f1, 0xcbcf5853, 0x8335d8f1, 0xe8a2be4f,
-      0xcbfee8f2, 0x0904469a, 0x6daecd51, 0xf08bd31b, 0xa69da69a, 0x08e8a1f1,
-      0xad57bff5, 0x6542a20a, 0x053d6b46, 0x2e9705bb, 0x0713c391, 0xda2fc9db,
-      0x213b6ffb, 0x78e3a810, 0xdd85f8a6, 0xdc16a59c, 0xcd55781f, 0xc0932718,
-      0x2b20bfe5, 0xb9bfb29c, 0xbe0f2f9c, 0xb97289c1, 0x03a892d4, 0xc0a2a0e4,
-      0x4771435b, 0x5524bb83, 0x39d1a750, 0x8265da3d, 0x8d1b78c5, 0xff4af3ab,
-      0x4bcad77f, 0xf0ec5f42, 0x27495189, 0x66e455f6, 0xb57e3270, 0xc82d3120,
-      0xc22596e3, 0x3424e47d, 0x9ccedcdd, 0xbc0c9512, 0xafc4dcbf, 0xc191c595,
-      0x2bb70939, 0x120392bd, 0xa6cd6ab4, 0x7f90650e, 0x32695ad3, 0x72874918,
-      0xa7917eb0, 0xa7c8fac5, 0x18be0361, 0xd088cb94, 0x9c7c1ce5, 0x7c1bf983,
-      0x58e1e79e, 0xe2e991fa, 0xfd28c4ad, 0x78565cde, 0xf98bafad, 0x7351b9fe,
-      0xb08c96bf, 0x2a9eac28, 0x96cb2225, 0x6c4f1796, 0x1bab87e0, 0x13a68586,
-      0xa0501971, 0x64c6de5a, 0xcac70991, 0x30537425, 0xc6c532b7, 0x01590d9d,
-      0x8ec720dc, 0x7e05e3aa, 0x54e3e63f, 0x74a07d9c, 0x8f3bc1d2, 0x73818438,
-      0x67be3acb, 0x26ffdc50, 0x561f255f, 0x6bcdf185, 0xcf99b1c6, 0xa0eaf2e1,
-      0x34f68604, 0x171df819, 0x65683e5a, 0x7ea5a216, 0x75ba1cea, 0x5d1cb020,
-      0xd2123fdf, 0x957f38cb, 0xf80de02f, 0xba6364ef, 0x41d452ee, 0x606e0a0e,
-      0xde82f7a2, 0x892d8317, 0x50f7b43e, 0xe707b1db, 0x766fcf5b, 0x4eb28826,
-      0xe80a0951, 0x5a362d56, 0x16527d78, 0x6ee217df, 0xba6b23dd, 0xf6737962,
-      0x7d4076ca, 0x443e6385, 0x048adfeb, 0x790d9a5f, 0x151ee94d, 0xd796b052,
-      0x12b04a03, 0x033ed95c, 0x4893da5d, 0x8b833ff8, 0xbb15eab9, 0x3d6724b1,
-      0x5061ca76, 0x9877c422, 0xadf74fb3, 0xd68d6810, 0xe30ce989, 0x42e5352f,
-      0x7431fde7, 0x265b565a, 0x58df4b8b, 0x3cdbf7e3, 0x6d3e8779, 0x2922a47f,
-      0x65b37f88, 0x52d2242f, 0x2958d6b5, 0x5d836d6e, 0x566d5e26, 0x29d40f00,
-      0x124b14a0, 0x288db0e1, 0xb7d9c1b6, 0x6c056608, 0xb8f19d32, 0x0b9471bd,
-      0x4faa6c9d, 0x8fb94650, 0x4540251c, 0x8943a946, 0x144a09e0, 0xfd1fe27d,
-      0xda141bda, 0xea6ac458, 0x633fce36, 0x8048f217, 0xade74d31, 0xfeda1384,
-      0x2ff7612f, 0x4334b8b0, 0x5227e216, 0xdbc8441f, 0x3605c85b, 0x096d119a,
-      0x21b7d7d0, 0x2b72b31c};
-
-  randen_u32 engine;
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  for (size_t i = 0; i < 2 * kNumGoldenOutputs; ++i) {
-    printf("0x%08x, ", engine());
-    if (i % 6 == 5) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-  engine.seed();
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-TEST(RandenTest, VerifyGoldenRanden32Seeded) {
-  constexpr uint64_t kGolden[2 * kNumGoldenOutputs] = {
-      0x94d3dcd5, 0x83a9e58f, 0xd97949fb, 0x70bbdff3, 0x7471c1b4, 0x0438481f,
-      0xe5fb5930, 0x34fdc58e, 0x2a937d17, 0xceee4f2d, 0xe432aea9, 0xb5a26a68,
-      0x3fb51740, 0x8b64774a, 0x74249c74, 0xd89ac1fc, 0x23fc3fdf, 0x03910d1d,
-      0x78aa897f, 0xd38f6308, 0x615f7e44, 0x0ee8f0f5, 0xf8279d52, 0x98f5a53d,
-      0x25938d0e, 0xb403f52c, 0x6ea6e838, 0x24007299, 0x6190fa61, 0xd3a79124,
-      0x7a7b4f80, 0xaaedd3df, 0x05deaf6e, 0xc6eacabe, 0x790edf4d, 0xb7967dd8,
-      0xe049d279, 0x9a0a8e67, 0xaebc23e7, 0x0494f606, 0x7bc3e0ee, 0x598dcd68,
-      0x02d452a1, 0x010ac818, 0x60aa2842, 0x6407c871, 0x486f93a0, 0x5a56e276,
-      0xd46a8f02, 0xc887a399, 0xfe93b740, 0x9e1e6100, 0x0f8901f6, 0x12d02e33,
-      0x47e790b7, 0xc39ca52b, 0x11e82e61, 0xb0b0a2fa, 0xa303806a, 0x1542d841,
-      0x7d6e9d86, 0x1fe659fd, 0x746541ac, 0xb8c90d80, 0x669ddc94, 0x239d56a5,
-      0x8123d13c, 0xd40db57c, 0x153a0db0, 0x3abc2414, 0x30cb8d61, 0x9bad6656,
-      0xee3f4bbc, 0x0bd1fb90, 0x079b4e42, 0x8f0b4d7e, 0xee59e793, 0xfa0fb0e0,
-      0x3e071100, 0x51080b28, 0x5081cc15, 0x2c4b9e71, 0xde4941df, 0xbe10ed49,
-      0x4b1b0d37, 0xf8eaac9d, 0x4605e139, 0x4bcce4b5, 0x6765dda6, 0xa64722b7,
-      0x8ca28ab5, 0xb9377d73, 0xa8ccc1af, 0x779fad81, 0x1ffd3ba7, 0x65cb3ee6,
-      0x7862836f, 0xd74e7908, 0x4c3f25bf, 0xd05b9c58, 0x93579827, 0x2ba93a46,
-      0xf05420ce, 0xd81530af, 0x15478621, 0xec06cea2, 0x796d65ad, 0x4b1798a6,
-      0x3a6f6fa6, 0xf142f3fb, 0xe237b560, 0x002b7bf7, 0xef65b4f8, 0xf47f2605,
-      0x17effc18, 0x9804ec55, 0xb7d481cd, 0xaed3d7f8, 0x1ce338d1, 0x5651c24c,
-      0x8bf0a3c6, 0x3e7a3820, 0x14534aed, 0x6796a7b6, 0x8358460f, 0x0d0f3b84,
-      0x00b19524, 0x0fa5fe76, 0x53faaedc, 0x2b0cf382, 0x233a9fd6, 0x10df9188,
-      0x80138b59, 0x3a100338, 0x3948e80f, 0x5fb0b0d2, 0x2fbf5350, 0x9e76f7b0,
-      0x04b1a985, 0x08160523, 0xb41fd218, 0x30c9880d, 0x65e20f28, 0x14aa399b,
-      0xace787b4, 0xe1454a8c, 0xb6c6f0f5, 0x325ac971, 0x784f3d36, 0x716b1aa2,
-      0xccfd144f, 0x3d5ce14a, 0x0f651792, 0x6c0c9771, 0xfb333532, 0xbc5b0f59,
-      0x140470bc, 0x2a90a7d2, 0x5c1e1c8d, 0x8da269f5, 0x895792ca, 0xcfc37143,
-      0xf30b238f, 0xbe21eab1, 0xee4d65fd, 0x8c47229d, 0xd1ed7d54, 0x5743614e,
-      0x9e9c476e, 0x351372a9, 0xe5db085f, 0x2bd5ea15, 0x6e0af4ca, 0x6925fde4,
-      0xdc1f45bd, 0xed3eda2b, 0xd460fa6e, 0xdef68c68, 0x6253e2b5, 0xe42a0de7,
-      0xbc29c305, 0x4e5176dc, 0x9f810f6e, 0xbfd85fba, 0xbeb815c6, 0x76a5a2a9,
-      0xceaf414c, 0x01edc4dd, 0xb4bb3b4b, 0xa4e98904, 0x7d2f1ddd, 0x00bd63ac,
-      0xe998ddbb, 0xb8491fe6, 0x3dda6800, 0xb386a346, 0x88871619, 0x00818876,
-      0x344e9a38, 0x33d394b3, 0xa3a8baf9, 0x815dba65, 0x02c2fd1a, 0x4232f6ec,
-      0xedd20834, 0xb5cff603, 0x3f687663, 0x58018924, 0xdc27fe99, 0xa8d5a2cb,
-      0x93fa0131, 0x725d8816, 0xdb2c7ac5, 0xa2be2c13, 0xb509fd78, 0x7b6a9614,
-      0x1e717636, 0xb6b136d7, 0xaff046ea, 0x660f1a71, 0x46c8ec9e, 0x0ba10ae3,
-      0xe3145b41, 0xe66dde53, 0x88c26be6, 0x3b18288c, 0xf02db933, 0x4d9d9d2f,
-      0x70f46e8a, 0x4167da8c, 0x8c6318b4, 0xf183beef, 0x71eeeef1, 0x4d889e1e,
-      0xd6689b6b, 0x7175c71a, 0xacd1b7dd, 0xfb9e42be, 0xb29b5e0d, 0xc33d0e91,
-      0x1ce47922, 0xd39b8329, 0x8493d12e, 0xc4d570fb, 0x4f424ae6, 0x23d5a572,
-      0x876b6616, 0x5245f161, 0x21ab578d, 0x38d77dbd, 0x1f4ecbfe, 0x9c342331,
-      0x9bacd9d5, 0x76fe3138,
-  };
-
-  ExplicitSeedSeq seed_sequence{12, 34, 56};
-  randen_u32 engine(seed_sequence);
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  for (size_t i = 0; i < 2 * kNumGoldenOutputs; ++i) {
-    printf("0x%08x, ", engine());
-    if (i % 6 == 5) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-  engine.seed(seed_sequence);
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-TEST(RandenTest, VerifyGoldenFromDeserializedEngine) {
-  constexpr uint64_t kGolden[kNumGoldenOutputs] = {
-      0x067f9f9ab919657a, 0x0534605912988583, 0x8a303f72feaa673f,
-      0x77b7fd747909185c, 0xd9af90403c56d891, 0xd939c6cb204d14b5,
-      0x7fbe6b954a47b483, 0x8b31a47cc34c768d, 0x3a9e546da2701a9c,
-      0x5246539046253e71, 0x417191ffb2a848a1, 0x7b1c7bf5a5001d09,
-      0x9489b15d194f2361, 0xfcebdeea3bcd2461, 0xd643027c854cec97,
-      0x5885397f91e0d21c, 0x53173b0efae30d58, 0x1c9c71168449fac1,
-      0xe358202b711ed8aa, 0x94e3918ed1d8227c, 0x5bb4e251450144cf,
-      0xb5c7a519b489af3b, 0x6f8b560b1f7b3469, 0xfde11dd4a1c74eef,
-      0x33383d2f76457dcf, 0x3060c0ec6db9fce1, 0x18f451fcddeec766,
-      0xe73c5d6b9f26da2a, 0x8d4cc566671b32a4, 0xb8189b73776bc9ff,
-      0x497a70f9caf0bc23, 0x23afcc509791dcea, 0x18af70dc4b27d306,
-      0xd3853f955a0ce5b9, 0x441db6c01a0afb17, 0xd0136c3fb8e1f13f,
-      0x5e4fd6fc2f33783c, 0xe0d24548adb5da51, 0x0f4d8362a7d3485a,
-      0x9f572d68270fa563, 0x6351fbc823024393, 0xa66dbfc61810e9ab,
-      0x0ff17fc14b651af8, 0xd74c55dafb99e623, 0x36303bc1ad85c6c2,
-      0x4920cd6a2af7e897, 0x0b8848addc30fecd, 0x9e1562eda6488e93,
-      0x197553807d607828, 0xbef5eaeda5e21235, 0x18d91d2616aca527,
-      0xb7821937f5c873cd, 0x2cd4ae5650dbeefc, 0xb35a64376f75ffdf,
-      0x9226d414d647fe07, 0x663f3db455bbb35e, 0xa829eead6ae93247,
-      0x7fd69c204dd0d25f, 0xbe1411f891c9acb1, 0xd476f34a506d5f11,
-      0xf423d2831649c5ca, 0x1e503962951abd75, 0xeccc9e8b1e34b537,
-      0xb11a147294044854, 0xc4cf27f0abf4929d, 0xe9193abf6fa24c8c,
-      0xa94a259e3aba8808, 0x21dc414197deffa3, 0xa2ae211d1ff622ae,
-      0xfe3995c46be5a4f4, 0xe9984c284bf11128, 0xcb1ce9d2f0851a80,
-      0x42fee17971d87cd8, 0xac76a98d177adc88, 0xa0973b3dedc4af6f,
-      0xdf56d6bbcb1b8e86, 0xf1e6485f407b11c9, 0x2c63de4deccb15c0,
-      0x6fe69db32ed4fad7, 0xaa51a65f84bca1f1, 0x242f2ee81d608afc,
-      0x8eb88b2b69fc153b, 0x22c20098baf73fd1, 0x57759466f576488c,
-      0x075ca562cea1be9d, 0x9a74814d73d28891, 0x73d1555fc02f4d3d,
-      0xc17f8f210ee89337, 0x46cca7999eaeafd4, 0x5db8d6a327a0d8ac,
-      0xb79b4f93c738d7a1, 0x9994512f0036ded1, 0xd3883026f38747f4,
-      0xf31f7458078d097c, 0x736ce4d480680669, 0x7a496f4c7e1033e3,
-      0xecf85bf297fbc68c, 0x9e37e1d0f24f3c4e, 0x15b6e067ca0746fc,
-      0xdd4a39905c5db81c, 0xb5dfafa7bcfdf7da, 0xca6646fb6f92a276,
-      0x1c6b35f363ef0efd, 0x6a33d06037ad9f76, 0x45544241afd8f80f,
-      0x83f8d83f859c90c5, 0x22aea9c5365e8c19, 0xfac35b11f20b6a6a,
-      0xd1acf49d1a27dd2f, 0xf281cd09c4fed405, 0x076000a42cd38e4f,
-      0x6ace300565070445, 0x463a62781bddc4db, 0x1477126b46b569ac,
-      0x127f2bb15035fbb8, 0xdfa30946049c04a8, 0x89072a586ba8dd3e,
-      0x62c809582bb7e74d, 0x22c0c3641406c28b, 0x9b66e36c47ff004d,
-      0xb9cd2c7519653330, 0x18608d79cd7a598d, 0x92c0bd1323e53e32,
-      0x887ff00de8524aa5, 0xa074410b787abd10, 0x18ab41b8057a2063,
-      0x1560abf26bc5f987};
-
-#if UPDATE_GOLDEN
-  (void)kGolden;  // Silence warning.
-  std::seed_seq seed_sequence{1, 2, 3, 4, 5};
-  randen_u64 engine(seed_sequence);
-  std::ostringstream stream;
-  stream << engine;
-  auto str = stream.str();
-  printf("%s\n\n", str.c_str());
-  for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
-    printf("0x%016lx, ", engine());
-    if (i % 3 == 2) {
-      printf("\n");
-    }
-  }
-  printf("\n\n\n");
-#else
-  randen_u64 engine;
-  std::istringstream stream(
-      "0 0 9824501439887287479 3242284395352394785 243836530774933777 "
-      "4047941804708365596 17165468127298385802 949276103645889255 "
-      "10659970394998657921 1657570836810929787 11697746266668051452 "
-      "9967209969299905230 14140390331161524430 7383014124183271684 "
-      "13146719127702337852 13983155220295807171 11121125587542359264 "
-      "195757810993252695 17138580243103178492 11326030747260920501 "
-      "8585097322474965590 18342582839328350995 15052982824209724634 "
-      "7321861343874683609 1806786911778767826 10100850842665572955 "
-      "9249328950653985078 13600624835326909759 11137960060943860251 "
-      "10208781341792329629 9282723971471525577 16373271619486811032 32");
-  stream >> engine;
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, engine());
-  }
-#endif
-}
-
-TEST(RandenTest, IsFastOrSlow) {
-  // randen_engine typically costs ~5ns per value for the optimized code paths,
-  // and the ~1000ns per value for slow code paths.  However when running under
-  // msan, asan, etc. it can take much longer.
-  //
-  // The estimated operation time is something like:
-  //
-  // linux, optimized ~5ns
-  // ppc, optimized ~7ns
-  // nacl (slow), ~1100ns
-  //
-  // `kCount` is chosen below so that, in debug builds and without hardware
-  // acceleration, the test (assuming ~1us per call) should finish in ~0.1s
-  static constexpr size_t kCount = 100000;
-  randen_u64 engine;
-  randen_u64::result_type sum = 0;
-  auto start = absl::GetCurrentTimeNanos();
-  for (int i = 0; i < kCount; i++) {
-    sum += engine();
-  }
-  auto duration = absl::GetCurrentTimeNanos() - start;
-
-  ABSL_INTERNAL_LOG(INFO, absl::StrCat(static_cast<double>(duration) /
-                                           static_cast<double>(kCount),
-                                       "ns"));
-
-  EXPECT_GT(sum, 0);
-  EXPECT_GE(duration, kCount);  // Should be slower than 1ns per call.
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_hwaes.cc b/third_party/abseil_cpp/absl/random/internal/randen_hwaes.cc
deleted file mode 100644
index b5a3f90aee..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_hwaes.cc
+++ /dev/null
@@ -1,573 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
-// symbols from arbitrary system and other headers, since it may be built
-// with different flags from other targets, using different levels of
-// optimization, potentially introducing ODR violations.
-
-#include "absl/random/internal/randen_hwaes.h"
-
-#include <cstdint>
-#include <cstring>
-
-#include "absl/base/attributes.h"
-#include "absl/random/internal/platform.h"
-#include "absl/random/internal/randen_traits.h"
-
-// ABSL_RANDEN_HWAES_IMPL indicates whether this file will contain
-// a hardware accelerated implementation of randen, or whether it
-// will contain stubs that exit the process.
-#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
-// The platform.h directives are sufficient to indicate whether
-// we should build accelerated implementations for x86.
-#if (ABSL_HAVE_ACCELERATED_AES || ABSL_RANDOM_INTERNAL_AES_DISPATCH)
-#define ABSL_RANDEN_HWAES_IMPL 1
-#endif
-#elif defined(ABSL_ARCH_PPC)
-// The platform.h directives are sufficient to indicate whether
-// we should build accelerated implementations for PPC.
-//
-// NOTE: This has mostly been tested on 64-bit Power variants,
-// and not embedded cpus such as powerpc32-8540
-#if ABSL_HAVE_ACCELERATED_AES
-#define ABSL_RANDEN_HWAES_IMPL 1
-#endif
-#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
-// ARM is somewhat more complicated. We might support crypto natively...
-#if ABSL_HAVE_ACCELERATED_AES || \
-    (defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO))
-#define ABSL_RANDEN_HWAES_IMPL 1
-
-#elif ABSL_RANDOM_INTERNAL_AES_DISPATCH && !defined(__APPLE__) && \
-    (defined(__GNUC__) && __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 9)
-// ...or, on GCC, we can use an ASM directive to
-// instruct the assember to allow crypto instructions.
-#define ABSL_RANDEN_HWAES_IMPL 1
-#define ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE 1
-#endif
-#else
-// HWAES is unsupported by these architectures / platforms:
-//   __myriad2__
-//   __mips__
-//
-// Other architectures / platforms are unknown.
-//
-// See the Abseil documentation on supported macros at:
-// https://abseil.io/docs/cpp/platforms/macros
-#endif
-
-#if !defined(ABSL_RANDEN_HWAES_IMPL)
-// No accelerated implementation is supported.
-// The RandenHwAes functions are stubs that print an error and exit.
-
-#include <cstdio>
-#include <cstdlib>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// No accelerated implementation.
-bool HasRandenHwAesImplementation() { return false; }
-
-// NOLINTNEXTLINE
-const void* RandenHwAes::GetKeys() {
-  // Attempted to dispatch to an unsupported dispatch target.
-  const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH;
-  fprintf(stderr, "AES Hardware detection failed (%d).\n", d);
-  exit(1);
-  return nullptr;
-}
-
-// NOLINTNEXTLINE
-void RandenHwAes::Absorb(const void*, void*) {
-  // Attempted to dispatch to an unsupported dispatch target.
-  const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH;
-  fprintf(stderr, "AES Hardware detection failed (%d).\n", d);
-  exit(1);
-}
-
-// NOLINTNEXTLINE
-void RandenHwAes::Generate(const void*, void*) {
-  // Attempted to dispatch to an unsupported dispatch target.
-  const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH;
-  fprintf(stderr, "AES Hardware detection failed (%d).\n", d);
-  exit(1);
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // defined(ABSL_RANDEN_HWAES_IMPL)
-//
-// Accelerated implementations are supported.
-// We need the per-architecture includes and defines.
-//
-namespace {
-
-using absl::random_internal::RandenTraits;
-
-// Randen operates on 128-bit vectors.
-struct alignas(16) u64x2 {
-  uint64_t data[2];
-};
-
-}  // namespace
-
-// TARGET_CRYPTO defines a crypto attribute for each architecture.
-//
-// NOTE: Evaluate whether we should eliminate ABSL_TARGET_CRYPTO.
-#if (defined(__clang__) || defined(__GNUC__))
-#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
-#define ABSL_TARGET_CRYPTO __attribute__((target("aes")))
-#elif defined(ABSL_ARCH_PPC)
-#define ABSL_TARGET_CRYPTO __attribute__((target("crypto")))
-#else
-#define ABSL_TARGET_CRYPTO
-#endif
-#else
-#define ABSL_TARGET_CRYPTO
-#endif
-
-#if defined(ABSL_ARCH_PPC)
-// NOTE: Keep in mind that PPC can operate in little-endian or big-endian mode,
-// however the PPC altivec vector registers (and thus the AES instructions)
-// always operate in big-endian mode.
-
-#include <altivec.h>
-// <altivec.h> #defines vector __vector; in C++, this is bad form.
-#undef vector
-#undef bool
-
-// Rely on the PowerPC AltiVec vector operations for accelerated AES
-// instructions. GCC support of the PPC vector types is described in:
-// https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/PowerPC-AltiVec_002fVSX-Built-in-Functions.html
-//
-// Already provides operator^=.
-using Vector128 = __vector unsigned long long;  // NOLINT(runtime/int)
-
-namespace {
-inline ABSL_TARGET_CRYPTO Vector128 ReverseBytes(const Vector128& v) {
-  // Reverses the bytes of the vector.
-  const __vector unsigned char perm = {15, 14, 13, 12, 11, 10, 9, 8,
-                                       7,  6,  5,  4,  3,  2,  1, 0};
-  return vec_perm(v, v, perm);
-}
-
-// WARNING: these load/store in native byte order. It is OK to load and then
-// store an unchanged vector, but interpreting the bits as a number or input
-// to AES will have undefined results.
-inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
-  return vec_vsx_ld(0, reinterpret_cast<const Vector128*>(from));
-}
-
-inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
-  vec_vsx_st(v, 0, reinterpret_cast<Vector128*>(to));
-}
-
-// One round of AES. "round_key" is a public constant for breaking the
-// symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
-                                             const Vector128& round_key) {
-  return Vector128(__builtin_crypto_vcipher(state, round_key));
-}
-
-// Enables native loads in the round loop by pre-swapping.
-inline ABSL_TARGET_CRYPTO void SwapEndian(u64x2* state) {
-  for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) {
-    Vector128Store(ReverseBytes(Vector128Load(state + block)), state + block);
-  }
-}
-
-}  // namespace
-
-#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
-
-// This asm directive will cause the file to be compiled with crypto extensions
-// whether or not the cpu-architecture supports it.
-#if ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE
-asm(".arch_extension  crypto\n");
-
-// Override missing defines.
-#if !defined(__ARM_NEON)
-#define __ARM_NEON 1
-#endif
-
-#if !defined(__ARM_FEATURE_CRYPTO)
-#define __ARM_FEATURE_CRYPTO 1
-#endif
-
-#endif
-
-// Rely on the ARM NEON+Crypto advanced simd types, defined in <arm_neon.h>.
-// uint8x16_t is the user alias for underlying __simd128_uint8_t type.
-// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf
-//
-// <arm_neon> defines the following
-//
-// typedef __attribute__((neon_vector_type(16))) uint8_t uint8x16_t;
-// typedef __attribute__((neon_vector_type(16))) int8_t int8x16_t;
-// typedef __attribute__((neon_polyvector_type(16))) int8_t poly8x16_t;
-//
-// vld1q_v
-// vst1q_v
-// vaeseq_v
-// vaesmcq_v
-#include <arm_neon.h>
-
-// Already provides operator^=.
-using Vector128 = uint8x16_t;
-
-namespace {
-
-inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
-  return vld1q_u8(reinterpret_cast<const uint8_t*>(from));
-}
-
-inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
-  vst1q_u8(reinterpret_cast<uint8_t*>(to), v);
-}
-
-// One round of AES. "round_key" is a public constant for breaking the
-// symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
-                                             const Vector128& round_key) {
-  // It is important to always use the full round function - omitting the
-  // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
-  // and does not help because we never decrypt.
-  //
-  // Note that ARM divides AES instructions differently than x86 / PPC,
-  // And we need to skip the first AddRoundKey step and add an extra
-  // AddRoundKey step to the end. Lucky for us this is just XOR.
-  return vaesmcq_u8(vaeseq_u8(state, uint8x16_t{})) ^ round_key;
-}
-
-inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {}
-
-}  // namespace
-
-#elif defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
-// On x86 we rely on the aesni instructions
-#include <wmmintrin.h>
-
-namespace {
-
-// Vector128 class is only wrapper for __m128i, benchmark indicates that it's
-// faster than using __m128i directly.
-class Vector128 {
- public:
-  // Convert from/to intrinsics.
-  inline explicit Vector128(const __m128i& Vector128) : data_(Vector128) {}
-
-  inline __m128i data() const { return data_; }
-
-  inline Vector128& operator^=(const Vector128& other) {
-    data_ = _mm_xor_si128(data_, other.data());
-    return *this;
-  }
-
- private:
-  __m128i data_;
-};
-
-inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
-  return Vector128(_mm_load_si128(reinterpret_cast<const __m128i*>(from)));
-}
-
-inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
-  _mm_store_si128(reinterpret_cast<__m128i*>(to), v.data());
-}
-
-// One round of AES. "round_key" is a public constant for breaking the
-// symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
-                                             const Vector128& round_key) {
-  // It is important to always use the full round function - omitting the
-  // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
-  // and does not help because we never decrypt.
-  return Vector128(_mm_aesenc_si128(state.data(), round_key.data()));
-}
-
-inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {}
-
-}  // namespace
-
-#endif
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunknown-pragmas"
-#endif
-
-// At this point, all of the platform-specific features have been defined /
-// implemented.
-//
-// REQUIRES: using Vector128 = ...
-// REQUIRES: Vector128 Vector128Load(void*) {...}
-// REQUIRES: void Vector128Store(Vector128, void*) {...}
-// REQUIRES: Vector128 AesRound(Vector128, Vector128) {...}
-// REQUIRES: void SwapEndian(uint64_t*) {...}
-//
-// PROVIDES: absl::random_internal::RandenHwAes::Absorb
-// PROVIDES: absl::random_internal::RandenHwAes::Generate
-namespace {
-
-// Block shuffles applies a shuffle to the entire state between AES rounds.
-// Improved odd-even shuffle from "New criterion for diffusion property".
-inline ABSL_TARGET_CRYPTO void BlockShuffle(u64x2* state) {
-  static_assert(RandenTraits::kFeistelBlocks == 16,
-                "Expecting 16 FeistelBlocks.");
-
-  constexpr size_t shuffle[RandenTraits::kFeistelBlocks] = {
-      7, 2, 13, 4, 11, 8, 3, 6, 15, 0, 9, 10, 1, 14, 5, 12};
-
-  const Vector128 v0 = Vector128Load(state + shuffle[0]);
-  const Vector128 v1 = Vector128Load(state + shuffle[1]);
-  const Vector128 v2 = Vector128Load(state + shuffle[2]);
-  const Vector128 v3 = Vector128Load(state + shuffle[3]);
-  const Vector128 v4 = Vector128Load(state + shuffle[4]);
-  const Vector128 v5 = Vector128Load(state + shuffle[5]);
-  const Vector128 v6 = Vector128Load(state + shuffle[6]);
-  const Vector128 v7 = Vector128Load(state + shuffle[7]);
-  const Vector128 w0 = Vector128Load(state + shuffle[8]);
-  const Vector128 w1 = Vector128Load(state + shuffle[9]);
-  const Vector128 w2 = Vector128Load(state + shuffle[10]);
-  const Vector128 w3 = Vector128Load(state + shuffle[11]);
-  const Vector128 w4 = Vector128Load(state + shuffle[12]);
-  const Vector128 w5 = Vector128Load(state + shuffle[13]);
-  const Vector128 w6 = Vector128Load(state + shuffle[14]);
-  const Vector128 w7 = Vector128Load(state + shuffle[15]);
-
-  Vector128Store(v0, state + 0);
-  Vector128Store(v1, state + 1);
-  Vector128Store(v2, state + 2);
-  Vector128Store(v3, state + 3);
-  Vector128Store(v4, state + 4);
-  Vector128Store(v5, state + 5);
-  Vector128Store(v6, state + 6);
-  Vector128Store(v7, state + 7);
-  Vector128Store(w0, state + 8);
-  Vector128Store(w1, state + 9);
-  Vector128Store(w2, state + 10);
-  Vector128Store(w3, state + 11);
-  Vector128Store(w4, state + 12);
-  Vector128Store(w5, state + 13);
-  Vector128Store(w6, state + 14);
-  Vector128Store(w7, state + 15);
-}
-
-// Feistel round function using two AES subrounds. Very similar to F()
-// from Simpira v2, but with independent subround keys. Uses 17 AES rounds
-// per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
-// parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
-// XORs are 'free' (included in the second AES instruction).
-inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound(
-    u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
-  static_assert(RandenTraits::kFeistelBlocks == 16,
-                "Expecting 16 FeistelBlocks.");
-
-  // MSVC does a horrible job at unrolling loops.
-  // So we unroll the loop by hand to improve the performance.
-  const Vector128 s0 = Vector128Load(state + 0);
-  const Vector128 s1 = Vector128Load(state + 1);
-  const Vector128 s2 = Vector128Load(state + 2);
-  const Vector128 s3 = Vector128Load(state + 3);
-  const Vector128 s4 = Vector128Load(state + 4);
-  const Vector128 s5 = Vector128Load(state + 5);
-  const Vector128 s6 = Vector128Load(state + 6);
-  const Vector128 s7 = Vector128Load(state + 7);
-  const Vector128 s8 = Vector128Load(state + 8);
-  const Vector128 s9 = Vector128Load(state + 9);
-  const Vector128 s10 = Vector128Load(state + 10);
-  const Vector128 s11 = Vector128Load(state + 11);
-  const Vector128 s12 = Vector128Load(state + 12);
-  const Vector128 s13 = Vector128Load(state + 13);
-  const Vector128 s14 = Vector128Load(state + 14);
-  const Vector128 s15 = Vector128Load(state + 15);
-
-  // Encode even blocks with keys.
-  const Vector128 e0 = AesRound(s0, Vector128Load(keys + 0));
-  const Vector128 e2 = AesRound(s2, Vector128Load(keys + 1));
-  const Vector128 e4 = AesRound(s4, Vector128Load(keys + 2));
-  const Vector128 e6 = AesRound(s6, Vector128Load(keys + 3));
-  const Vector128 e8 = AesRound(s8, Vector128Load(keys + 4));
-  const Vector128 e10 = AesRound(s10, Vector128Load(keys + 5));
-  const Vector128 e12 = AesRound(s12, Vector128Load(keys + 6));
-  const Vector128 e14 = AesRound(s14, Vector128Load(keys + 7));
-
-  // Encode odd blocks with even output from above.
-  const Vector128 o1 = AesRound(e0, s1);
-  const Vector128 o3 = AesRound(e2, s3);
-  const Vector128 o5 = AesRound(e4, s5);
-  const Vector128 o7 = AesRound(e6, s7);
-  const Vector128 o9 = AesRound(e8, s9);
-  const Vector128 o11 = AesRound(e10, s11);
-  const Vector128 o13 = AesRound(e12, s13);
-  const Vector128 o15 = AesRound(e14, s15);
-
-  // Store odd blocks. (These will be shuffled later).
-  Vector128Store(o1, state + 1);
-  Vector128Store(o3, state + 3);
-  Vector128Store(o5, state + 5);
-  Vector128Store(o7, state + 7);
-  Vector128Store(o9, state + 9);
-  Vector128Store(o11, state + 11);
-  Vector128Store(o13, state + 13);
-  Vector128Store(o15, state + 15);
-
-  return keys + 8;
-}
-
-// Cryptographic permutation based via type-2 Generalized Feistel Network.
-// Indistinguishable from ideal by chosen-ciphertext adversaries using less than
-// 2^64 queries if the round function is a PRF. This is similar to the b=8 case
-// of Simpira v2, but more efficient than its generic construction for b=16.
-inline ABSL_TARGET_CRYPTO void Permute(
-    u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
-  // (Successfully unrolled; the first iteration jumps into the second half)
-#ifdef __clang__
-#pragma clang loop unroll_count(2)
-#endif
-  for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) {
-    keys = FeistelRound(state, keys);
-    BlockShuffle(state);
-  }
-}
-
-}  // namespace
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-bool HasRandenHwAesImplementation() { return true; }
-
-const void* ABSL_TARGET_CRYPTO RandenHwAes::GetKeys() {
-  // Round keys for one AES per Feistel round and branch.
-  // The canonical implementation uses first digits of Pi.
-#if defined(ABSL_ARCH_PPC)
-  return kRandenRoundKeysBE;
-#else
-  return kRandenRoundKeys;
-#endif
-}
-
-// NOLINTNEXTLINE
-void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void,
-                                            void* state_void) {
-  static_assert(RandenTraits::kCapacityBytes / sizeof(Vector128) == 1,
-                "Unexpected Randen kCapacityBlocks");
-  static_assert(RandenTraits::kStateBytes / sizeof(Vector128) == 16,
-                "Unexpected Randen kStateBlocks");
-
-  auto* state =
-      reinterpret_cast<u64x2 * ABSL_RANDOM_INTERNAL_RESTRICT>(state_void);
-  const auto* seed =
-      reinterpret_cast<const u64x2 * ABSL_RANDOM_INTERNAL_RESTRICT>(seed_void);
-
-  Vector128 b1 = Vector128Load(state + 1);
-  b1 ^= Vector128Load(seed + 0);
-  Vector128Store(b1, state + 1);
-
-  Vector128 b2 = Vector128Load(state + 2);
-  b2 ^= Vector128Load(seed + 1);
-  Vector128Store(b2, state + 2);
-
-  Vector128 b3 = Vector128Load(state + 3);
-  b3 ^= Vector128Load(seed + 2);
-  Vector128Store(b3, state + 3);
-
-  Vector128 b4 = Vector128Load(state + 4);
-  b4 ^= Vector128Load(seed + 3);
-  Vector128Store(b4, state + 4);
-
-  Vector128 b5 = Vector128Load(state + 5);
-  b5 ^= Vector128Load(seed + 4);
-  Vector128Store(b5, state + 5);
-
-  Vector128 b6 = Vector128Load(state + 6);
-  b6 ^= Vector128Load(seed + 5);
-  Vector128Store(b6, state + 6);
-
-  Vector128 b7 = Vector128Load(state + 7);
-  b7 ^= Vector128Load(seed + 6);
-  Vector128Store(b7, state + 7);
-
-  Vector128 b8 = Vector128Load(state + 8);
-  b8 ^= Vector128Load(seed + 7);
-  Vector128Store(b8, state + 8);
-
-  Vector128 b9 = Vector128Load(state + 9);
-  b9 ^= Vector128Load(seed + 8);
-  Vector128Store(b9, state + 9);
-
-  Vector128 b10 = Vector128Load(state + 10);
-  b10 ^= Vector128Load(seed + 9);
-  Vector128Store(b10, state + 10);
-
-  Vector128 b11 = Vector128Load(state + 11);
-  b11 ^= Vector128Load(seed + 10);
-  Vector128Store(b11, state + 11);
-
-  Vector128 b12 = Vector128Load(state + 12);
-  b12 ^= Vector128Load(seed + 11);
-  Vector128Store(b12, state + 12);
-
-  Vector128 b13 = Vector128Load(state + 13);
-  b13 ^= Vector128Load(seed + 12);
-  Vector128Store(b13, state + 13);
-
-  Vector128 b14 = Vector128Load(state + 14);
-  b14 ^= Vector128Load(seed + 13);
-  Vector128Store(b14, state + 14);
-
-  Vector128 b15 = Vector128Load(state + 15);
-  b15 ^= Vector128Load(seed + 14);
-  Vector128Store(b15, state + 15);
-}
-
-// NOLINTNEXTLINE
-void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys_void,
-                                              void* state_void) {
-  static_assert(RandenTraits::kCapacityBytes == sizeof(Vector128),
-                "Capacity mismatch");
-
-  auto* state = reinterpret_cast<u64x2*>(state_void);
-  const auto* keys = reinterpret_cast<const u64x2*>(keys_void);
-
-  const Vector128 prev_inner = Vector128Load(state);
-
-  SwapEndian(state);
-
-  Permute(state, keys);
-
-  SwapEndian(state);
-
-  // Ensure backtracking resistance.
-  Vector128 inner = Vector128Load(state);
-  inner ^= prev_inner;
-  Vector128Store(inner, state);
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // (ABSL_RANDEN_HWAES_IMPL)
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_hwaes.h b/third_party/abseil_cpp/absl/random/internal/randen_hwaes.h
deleted file mode 100644
index bce36b5226..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_hwaes.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_
-#define ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_
-
-#include "absl/base/config.h"
-
-// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
-// symbols from arbitrary system and other headers, since it may be built
-// with different flags from other targets, using different levels of
-// optimization, potentially introducing ODR violations.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// RANDen = RANDom generator or beetroots in Swiss German.
-// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
-// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
-//
-// RandenHwAes implements the basic state manipulation methods.
-class RandenHwAes {
- public:
-  static void Generate(const void* keys, void* state_void);
-  static void Absorb(const void* seed_void, void* state_void);
-  static const void* GetKeys();
-};
-
-// HasRandenHwAesImplementation returns true when there is an accelerated
-// implementation, and false otherwise.  If there is no implementation,
-// then attempting to use it will abort the program.
-bool HasRandenHwAesImplementation();
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_hwaes_test.cc b/third_party/abseil_cpp/absl/random/internal/randen_hwaes_test.cc
deleted file mode 100644
index 66ddb43fd6..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_hwaes_test.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/randen_hwaes.h"
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/platform.h"
-#include "absl/random/internal/randen_detect.h"
-#include "absl/random/internal/randen_traits.h"
-#include "absl/strings/str_format.h"
-
-namespace {
-
-using absl::random_internal::RandenHwAes;
-using absl::random_internal::RandenTraits;
-
-// Local state parameters.
-constexpr size_t kSeedBytes =
-    RandenTraits::kStateBytes - RandenTraits::kCapacityBytes;
-constexpr size_t kStateSizeT = RandenTraits::kStateBytes / sizeof(uint64_t);
-constexpr size_t kSeedSizeT = kSeedBytes / sizeof(uint32_t);
-
-struct alignas(16) randen {
-  uint64_t state[kStateSizeT];
-  uint32_t seed[kSeedSizeT];
-};
-
-TEST(RandenHwAesTest, Default) {
-  EXPECT_TRUE(absl::random_internal::CPUSupportsRandenHwAes());
-
-  constexpr uint64_t kGolden[] = {
-      0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977,
-      0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912,
-      0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6,
-      0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a,
-      0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37,
-      0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556,
-      0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42,
-      0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc,
-      0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f,
-      0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3,
-      0x026ff374c101da7e, 0x811ef0821c3de851,
-  };
-
-  alignas(16) randen d;
-  memset(d.state, 0, sizeof(d.state));
-  RandenHwAes::Generate(RandenHwAes::GetKeys(), d.state);
-
-  uint64_t* id = d.state;
-  for (const auto& elem : kGolden) {
-    auto a = absl::StrFormat("%#x", elem);
-    auto b = absl::StrFormat("%#x", *id++);
-    EXPECT_EQ(a, b);
-  }
-}
-
-}  // namespace
-
-int main(int argc, char* argv[]) {
-  testing::InitGoogleTest(&argc, argv);
-
-  ABSL_RAW_LOG(INFO, "ABSL_HAVE_ACCELERATED_AES=%d", ABSL_HAVE_ACCELERATED_AES);
-  ABSL_RAW_LOG(INFO, "ABSL_RANDOM_INTERNAL_AES_DISPATCH=%d",
-               ABSL_RANDOM_INTERNAL_AES_DISPATCH);
-
-#if defined(ABSL_ARCH_X86_64)
-  ABSL_RAW_LOG(INFO, "ABSL_ARCH_X86_64");
-#elif defined(ABSL_ARCH_X86_32)
-  ABSL_RAW_LOG(INFO, "ABSL_ARCH_X86_32");
-#elif defined(ABSL_ARCH_AARCH64)
-  ABSL_RAW_LOG(INFO, "ABSL_ARCH_AARCH64");
-#elif defined(ABSL_ARCH_ARM)
-  ABSL_RAW_LOG(INFO, "ABSL_ARCH_ARM");
-#elif defined(ABSL_ARCH_PPC)
-  ABSL_RAW_LOG(INFO, "ABSL_ARCH_PPC");
-#else
-  ABSL_RAW_LOG(INFO, "ARCH Unknown");
-#endif
-
-  int x = absl::random_internal::HasRandenHwAesImplementation();
-  ABSL_RAW_LOG(INFO, "HasRandenHwAesImplementation = %d", x);
-
-  int y = absl::random_internal::CPUSupportsRandenHwAes();
-  ABSL_RAW_LOG(INFO, "CPUSupportsRandenHwAes = %d", x);
-
-  if (!x || !y) {
-    ABSL_RAW_LOG(INFO, "Skipping Randen HWAES tests.");
-    return 0;
-  }
-  return RUN_ALL_TESTS();
-}
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_round_keys.cc b/third_party/abseil_cpp/absl/random/internal/randen_round_keys.cc
deleted file mode 100644
index 5fb3ca556d..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_round_keys.cc
+++ /dev/null
@@ -1,462 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/randen_traits.h"
-
-// This file contains only the round keys for randen.
-//
-// "Nothing up my sleeve" numbers from the first hex digits of Pi, obtained
-// from http://hexpi.sourceforge.net/. The array was generated by following
-// Python script:
-
-/*
-python >tmp.cc << EOF
-"""Generates Randen round keys array from pi-hex.62500.txt file."""
-import binascii
-
-KEYS = 17 * 8
-
-def chunks(l, n):
-    """Yield successive n-sized chunks from l."""
-    for i in range(0, len(l), n):
-        yield l[i:i + n]
-
-def pairwise(t):
-    """Transforms sequence into sequence of pairs."""
-    it = iter(t)
-    return zip(it,it)
-
-def digits_from_pi():
-  """Reads digits from hexpi.sourceforge.net file."""
-  with open("pi-hex.62500.txt") as file:
-    return file.read()
-
-def digits_from_urandom():
-  """Reads digits from /dev/urandom."""
-  with open("/dev/urandom") as file:
-    return binascii.hexlify(file.read(KEYS * 16))
-
-def print_row(b)
-  print("  0x{0}, 0x{1}, 0x{2}, 0x{3}, 0x{4}, 0x{5}, 0x{6}, 0x{7}, 0x{8}, 0x{9},
-0x{10}, 0x{11}, 0x{12}, 0x{13}, 0x{14}, 0x{15},".format(*b))
-
-
-digits = digits_from_pi()
-#digits = digits_from_urandom()
-
-print("namespace {")
-print("static constexpr size_t kKeyBytes = {0};\n".format(KEYS * 16))
-print("}")
-
-print("alignas(16) const unsigned char kRandenRoundKeysBE[kKeyBytes] = {")
-
-for i, u16 in zip(range(KEYS), chunks(digits, 32)):
-  b = list(chunks(u16, 2))
-  print_row(b)
-
-print("};")
-
-print("alignas(16) const unsigned char kRandenRoundKeys[kKeyBytes] = {")
-
-for i, u16 in zip(range(KEYS), chunks(digits, 32)):
-  b = list(chunks(u16, 2))
-  b.reverse()
-  print_row(b)
-
-print("};")
-
-EOF
-
-*/
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-namespace {
-static constexpr size_t kKeyBytes = 2176;
-}
-
-alignas(16) const unsigned char kRandenRoundKeysBE[kKeyBytes] = {
-    0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, 0x13, 0x19, 0x8A, 0x2E,
-    0x03, 0x70, 0x73, 0x44, 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
-    0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89, 0x45, 0x28, 0x21, 0xE6,
-    0x38, 0xD0, 0x13, 0x77, 0xBE, 0x54, 0x66, 0xCF, 0x34, 0xE9, 0x0C, 0x6C,
-    0xC0, 0xAC, 0x29, 0xB7, 0xC9, 0x7C, 0x50, 0xDD, 0x3F, 0x84, 0xD5, 0xB5,
-    0xB5, 0x47, 0x09, 0x17, 0x92, 0x16, 0xD5, 0xD9, 0x89, 0x79, 0xFB, 0x1B,
-    0xD1, 0x31, 0x0B, 0xA6, 0x98, 0xDF, 0xB5, 0xAC, 0x2F, 0xFD, 0x72, 0xDB,
-    0xD0, 0x1A, 0xDF, 0xB7, 0xB8, 0xE1, 0xAF, 0xED, 0x6A, 0x26, 0x7E, 0x96,
-    0xBA, 0x7C, 0x90, 0x45, 0xF1, 0x2C, 0x7F, 0x99, 0x24, 0xA1, 0x99, 0x47,
-    0xB3, 0x91, 0x6C, 0xF7, 0x08, 0x01, 0xF2, 0xE2, 0x85, 0x8E, 0xFC, 0x16,
-    0x63, 0x69, 0x20, 0xD8, 0x71, 0x57, 0x4E, 0x69, 0xA4, 0x58, 0xFE, 0xA3,
-    0xF4, 0x93, 0x3D, 0x7E, 0x0D, 0x95, 0x74, 0x8F, 0x72, 0x8E, 0xB6, 0x58,
-    0x71, 0x8B, 0xCD, 0x58, 0x82, 0x15, 0x4A, 0xEE, 0x7B, 0x54, 0xA4, 0x1D,
-    0xC2, 0x5A, 0x59, 0xB5, 0x9C, 0x30, 0xD5, 0x39, 0x2A, 0xF2, 0x60, 0x13,
-    0xC5, 0xD1, 0xB0, 0x23, 0x28, 0x60, 0x85, 0xF0, 0xCA, 0x41, 0x79, 0x18,
-    0xB8, 0xDB, 0x38, 0xEF, 0x8E, 0x79, 0xDC, 0xB0, 0x60, 0x3A, 0x18, 0x0E,
-    0x6C, 0x9E, 0x0E, 0x8B, 0xB0, 0x1E, 0x8A, 0x3E, 0xD7, 0x15, 0x77, 0xC1,
-    0xBD, 0x31, 0x4B, 0x27, 0x78, 0xAF, 0x2F, 0xDA, 0x55, 0x60, 0x5C, 0x60,
-    0xE6, 0x55, 0x25, 0xF3, 0xAA, 0x55, 0xAB, 0x94, 0x57, 0x48, 0x98, 0x62,
-    0x63, 0xE8, 0x14, 0x40, 0x55, 0xCA, 0x39, 0x6A, 0x2A, 0xAB, 0x10, 0xB6,
-    0xB4, 0xCC, 0x5C, 0x34, 0x11, 0x41, 0xE8, 0xCE, 0xA1, 0x54, 0x86, 0xAF,
-    0x7C, 0x72, 0xE9, 0x93, 0xB3, 0xEE, 0x14, 0x11, 0x63, 0x6F, 0xBC, 0x2A,
-    0x2B, 0xA9, 0xC5, 0x5D, 0x74, 0x18, 0x31, 0xF6, 0xCE, 0x5C, 0x3E, 0x16,
-    0x9B, 0x87, 0x93, 0x1E, 0xAF, 0xD6, 0xBA, 0x33, 0x6C, 0x24, 0xCF, 0x5C,
-    0x7A, 0x32, 0x53, 0x81, 0x28, 0x95, 0x86, 0x77, 0x3B, 0x8F, 0x48, 0x98,
-    0x6B, 0x4B, 0xB9, 0xAF, 0xC4, 0xBF, 0xE8, 0x1B, 0x66, 0x28, 0x21, 0x93,
-    0x61, 0xD8, 0x09, 0xCC, 0xFB, 0x21, 0xA9, 0x91, 0x48, 0x7C, 0xAC, 0x60,
-    0x5D, 0xEC, 0x80, 0x32, 0xEF, 0x84, 0x5D, 0x5D, 0xE9, 0x85, 0x75, 0xB1,
-    0xDC, 0x26, 0x23, 0x02, 0xEB, 0x65, 0x1B, 0x88, 0x23, 0x89, 0x3E, 0x81,
-    0xD3, 0x96, 0xAC, 0xC5, 0x0F, 0x6D, 0x6F, 0xF3, 0x83, 0xF4, 0x42, 0x39,
-    0x2E, 0x0B, 0x44, 0x82, 0xA4, 0x84, 0x20, 0x04, 0x69, 0xC8, 0xF0, 0x4A,
-    0x9E, 0x1F, 0x9B, 0x5E, 0x21, 0xC6, 0x68, 0x42, 0xF6, 0xE9, 0x6C, 0x9A,
-    0x67, 0x0C, 0x9C, 0x61, 0xAB, 0xD3, 0x88, 0xF0, 0x6A, 0x51, 0xA0, 0xD2,
-    0xD8, 0x54, 0x2F, 0x68, 0x96, 0x0F, 0xA7, 0x28, 0xAB, 0x51, 0x33, 0xA3,
-    0x6E, 0xEF, 0x0B, 0x6C, 0x13, 0x7A, 0x3B, 0xE4, 0xBA, 0x3B, 0xF0, 0x50,
-    0x7E, 0xFB, 0x2A, 0x98, 0xA1, 0xF1, 0x65, 0x1D, 0x39, 0xAF, 0x01, 0x76,
-    0x66, 0xCA, 0x59, 0x3E, 0x82, 0x43, 0x0E, 0x88, 0x8C, 0xEE, 0x86, 0x19,
-    0x45, 0x6F, 0x9F, 0xB4, 0x7D, 0x84, 0xA5, 0xC3, 0x3B, 0x8B, 0x5E, 0xBE,
-    0xE0, 0x6F, 0x75, 0xD8, 0x85, 0xC1, 0x20, 0x73, 0x40, 0x1A, 0x44, 0x9F,
-    0x56, 0xC1, 0x6A, 0xA6, 0x4E, 0xD3, 0xAA, 0x62, 0x36, 0x3F, 0x77, 0x06,
-    0x1B, 0xFE, 0xDF, 0x72, 0x42, 0x9B, 0x02, 0x3D, 0x37, 0xD0, 0xD7, 0x24,
-    0xD0, 0x0A, 0x12, 0x48, 0xDB, 0x0F, 0xEA, 0xD3, 0x49, 0xF1, 0xC0, 0x9B,
-    0x07, 0x53, 0x72, 0xC9, 0x80, 0x99, 0x1B, 0x7B, 0x25, 0xD4, 0x79, 0xD8,
-    0xF6, 0xE8, 0xDE, 0xF7, 0xE3, 0xFE, 0x50, 0x1A, 0xB6, 0x79, 0x4C, 0x3B,
-    0x97, 0x6C, 0xE0, 0xBD, 0x04, 0xC0, 0x06, 0xBA, 0xC1, 0xA9, 0x4F, 0xB6,
-    0x40, 0x9F, 0x60, 0xC4, 0x5E, 0x5C, 0x9E, 0xC2, 0x19, 0x6A, 0x24, 0x63,
-    0x68, 0xFB, 0x6F, 0xAF, 0x3E, 0x6C, 0x53, 0xB5, 0x13, 0x39, 0xB2, 0xEB,
-    0x3B, 0x52, 0xEC, 0x6F, 0x6D, 0xFC, 0x51, 0x1F, 0x9B, 0x30, 0x95, 0x2C,
-    0xCC, 0x81, 0x45, 0x44, 0xAF, 0x5E, 0xBD, 0x09, 0xBE, 0xE3, 0xD0, 0x04,
-    0xDE, 0x33, 0x4A, 0xFD, 0x66, 0x0F, 0x28, 0x07, 0x19, 0x2E, 0x4B, 0xB3,
-    0xC0, 0xCB, 0xA8, 0x57, 0x45, 0xC8, 0x74, 0x0F, 0xD2, 0x0B, 0x5F, 0x39,
-    0xB9, 0xD3, 0xFB, 0xDB, 0x55, 0x79, 0xC0, 0xBD, 0x1A, 0x60, 0x32, 0x0A,
-    0xD6, 0xA1, 0x00, 0xC6, 0x40, 0x2C, 0x72, 0x79, 0x67, 0x9F, 0x25, 0xFE,
-    0xFB, 0x1F, 0xA3, 0xCC, 0x8E, 0xA5, 0xE9, 0xF8, 0xDB, 0x32, 0x22, 0xF8,
-    0x3C, 0x75, 0x16, 0xDF, 0xFD, 0x61, 0x6B, 0x15, 0x2F, 0x50, 0x1E, 0xC8,
-    0xAD, 0x05, 0x52, 0xAB, 0x32, 0x3D, 0xB5, 0xFA, 0xFD, 0x23, 0x87, 0x60,
-    0x53, 0x31, 0x7B, 0x48, 0x3E, 0x00, 0xDF, 0x82, 0x9E, 0x5C, 0x57, 0xBB,
-    0xCA, 0x6F, 0x8C, 0xA0, 0x1A, 0x87, 0x56, 0x2E, 0xDF, 0x17, 0x69, 0xDB,
-    0xD5, 0x42, 0xA8, 0xF6, 0x28, 0x7E, 0xFF, 0xC3, 0xAC, 0x67, 0x32, 0xC6,
-    0x8C, 0x4F, 0x55, 0x73, 0x69, 0x5B, 0x27, 0xB0, 0xBB, 0xCA, 0x58, 0xC8,
-    0xE1, 0xFF, 0xA3, 0x5D, 0xB8, 0xF0, 0x11, 0xA0, 0x10, 0xFA, 0x3D, 0x98,
-    0xFD, 0x21, 0x83, 0xB8, 0x4A, 0xFC, 0xB5, 0x6C, 0x2D, 0xD1, 0xD3, 0x5B,
-    0x9A, 0x53, 0xE4, 0x79, 0xB6, 0xF8, 0x45, 0x65, 0xD2, 0x8E, 0x49, 0xBC,
-    0x4B, 0xFB, 0x97, 0x90, 0xE1, 0xDD, 0xF2, 0xDA, 0xA4, 0xCB, 0x7E, 0x33,
-    0x62, 0xFB, 0x13, 0x41, 0xCE, 0xE4, 0xC6, 0xE8, 0xEF, 0x20, 0xCA, 0xDA,
-    0x36, 0x77, 0x4C, 0x01, 0xD0, 0x7E, 0x9E, 0xFE, 0x2B, 0xF1, 0x1F, 0xB4,
-    0x95, 0xDB, 0xDA, 0x4D, 0xAE, 0x90, 0x91, 0x98, 0xEA, 0xAD, 0x8E, 0x71,
-    0x6B, 0x93, 0xD5, 0xA0, 0xD0, 0x8E, 0xD1, 0xD0, 0xAF, 0xC7, 0x25, 0xE0,
-    0x8E, 0x3C, 0x5B, 0x2F, 0x8E, 0x75, 0x94, 0xB7, 0x8F, 0xF6, 0xE2, 0xFB,
-    0xF2, 0x12, 0x2B, 0x64, 0x88, 0x88, 0xB8, 0x12, 0x90, 0x0D, 0xF0, 0x1C,
-    0x4F, 0xAD, 0x5E, 0xA0, 0x68, 0x8F, 0xC3, 0x1C, 0xD1, 0xCF, 0xF1, 0x91,
-    0xB3, 0xA8, 0xC1, 0xAD, 0x2F, 0x2F, 0x22, 0x18, 0xBE, 0x0E, 0x17, 0x77,
-    0xEA, 0x75, 0x2D, 0xFE, 0x8B, 0x02, 0x1F, 0xA1, 0xE5, 0xA0, 0xCC, 0x0F,
-    0xB5, 0x6F, 0x74, 0xE8, 0x18, 0xAC, 0xF3, 0xD6, 0xCE, 0x89, 0xE2, 0x99,
-    0xB4, 0xA8, 0x4F, 0xE0, 0xFD, 0x13, 0xE0, 0xB7, 0x7C, 0xC4, 0x3B, 0x81,
-    0xD2, 0xAD, 0xA8, 0xD9, 0x16, 0x5F, 0xA2, 0x66, 0x80, 0x95, 0x77, 0x05,
-    0x93, 0xCC, 0x73, 0x14, 0x21, 0x1A, 0x14, 0x77, 0xE6, 0xAD, 0x20, 0x65,
-    0x77, 0xB5, 0xFA, 0x86, 0xC7, 0x54, 0x42, 0xF5, 0xFB, 0x9D, 0x35, 0xCF,
-    0xEB, 0xCD, 0xAF, 0x0C, 0x7B, 0x3E, 0x89, 0xA0, 0xD6, 0x41, 0x1B, 0xD3,
-    0xAE, 0x1E, 0x7E, 0x49, 0x00, 0x25, 0x0E, 0x2D, 0x20, 0x71, 0xB3, 0x5E,
-    0x22, 0x68, 0x00, 0xBB, 0x57, 0xB8, 0xE0, 0xAF, 0x24, 0x64, 0x36, 0x9B,
-    0xF0, 0x09, 0xB9, 0x1E, 0x55, 0x63, 0x91, 0x1D, 0x59, 0xDF, 0xA6, 0xAA,
-    0x78, 0xC1, 0x43, 0x89, 0xD9, 0x5A, 0x53, 0x7F, 0x20, 0x7D, 0x5B, 0xA2,
-    0x02, 0xE5, 0xB9, 0xC5, 0x83, 0x26, 0x03, 0x76, 0x62, 0x95, 0xCF, 0xA9,
-    0x11, 0xC8, 0x19, 0x68, 0x4E, 0x73, 0x4A, 0x41, 0xB3, 0x47, 0x2D, 0xCA,
-    0x7B, 0x14, 0xA9, 0x4A, 0x1B, 0x51, 0x00, 0x52, 0x9A, 0x53, 0x29, 0x15,
-    0xD6, 0x0F, 0x57, 0x3F, 0xBC, 0x9B, 0xC6, 0xE4, 0x2B, 0x60, 0xA4, 0x76,
-    0x81, 0xE6, 0x74, 0x00, 0x08, 0xBA, 0x6F, 0xB5, 0x57, 0x1B, 0xE9, 0x1F,
-    0xF2, 0x96, 0xEC, 0x6B, 0x2A, 0x0D, 0xD9, 0x15, 0xB6, 0x63, 0x65, 0x21,
-    0xE7, 0xB9, 0xF9, 0xB6, 0xFF, 0x34, 0x05, 0x2E, 0xC5, 0x85, 0x56, 0x64,
-    0x53, 0xB0, 0x2D, 0x5D, 0xA9, 0x9F, 0x8F, 0xA1, 0x08, 0xBA, 0x47, 0x99,
-    0x6E, 0x85, 0x07, 0x6A, 0x4B, 0x7A, 0x70, 0xE9, 0xB5, 0xB3, 0x29, 0x44,
-    0xDB, 0x75, 0x09, 0x2E, 0xC4, 0x19, 0x26, 0x23, 0xAD, 0x6E, 0xA6, 0xB0,
-    0x49, 0xA7, 0xDF, 0x7D, 0x9C, 0xEE, 0x60, 0xB8, 0x8F, 0xED, 0xB2, 0x66,
-    0xEC, 0xAA, 0x8C, 0x71, 0x69, 0x9A, 0x18, 0xFF, 0x56, 0x64, 0x52, 0x6C,
-    0xC2, 0xB1, 0x9E, 0xE1, 0x19, 0x36, 0x02, 0xA5, 0x75, 0x09, 0x4C, 0x29,
-    0xA0, 0x59, 0x13, 0x40, 0xE4, 0x18, 0x3A, 0x3E, 0x3F, 0x54, 0x98, 0x9A,
-    0x5B, 0x42, 0x9D, 0x65, 0x6B, 0x8F, 0xE4, 0xD6, 0x99, 0xF7, 0x3F, 0xD6,
-    0xA1, 0xD2, 0x9C, 0x07, 0xEF, 0xE8, 0x30, 0xF5, 0x4D, 0x2D, 0x38, 0xE6,
-    0xF0, 0x25, 0x5D, 0xC1, 0x4C, 0xDD, 0x20, 0x86, 0x84, 0x70, 0xEB, 0x26,
-    0x63, 0x82, 0xE9, 0xC6, 0x02, 0x1E, 0xCC, 0x5E, 0x09, 0x68, 0x6B, 0x3F,
-    0x3E, 0xBA, 0xEF, 0xC9, 0x3C, 0x97, 0x18, 0x14, 0x6B, 0x6A, 0x70, 0xA1,
-    0x68, 0x7F, 0x35, 0x84, 0x52, 0xA0, 0xE2, 0x86, 0xB7, 0x9C, 0x53, 0x05,
-    0xAA, 0x50, 0x07, 0x37, 0x3E, 0x07, 0x84, 0x1C, 0x7F, 0xDE, 0xAE, 0x5C,
-    0x8E, 0x7D, 0x44, 0xEC, 0x57, 0x16, 0xF2, 0xB8, 0xB0, 0x3A, 0xDA, 0x37,
-    0xF0, 0x50, 0x0C, 0x0D, 0xF0, 0x1C, 0x1F, 0x04, 0x02, 0x00, 0xB3, 0xFF,
-    0xAE, 0x0C, 0xF5, 0x1A, 0x3C, 0xB5, 0x74, 0xB2, 0x25, 0x83, 0x7A, 0x58,
-    0xDC, 0x09, 0x21, 0xBD, 0xD1, 0x91, 0x13, 0xF9, 0x7C, 0xA9, 0x2F, 0xF6,
-    0x94, 0x32, 0x47, 0x73, 0x22, 0xF5, 0x47, 0x01, 0x3A, 0xE5, 0xE5, 0x81,
-    0x37, 0xC2, 0xDA, 0xDC, 0xC8, 0xB5, 0x76, 0x34, 0x9A, 0xF3, 0xDD, 0xA7,
-    0xA9, 0x44, 0x61, 0x46, 0x0F, 0xD0, 0x03, 0x0E, 0xEC, 0xC8, 0xC7, 0x3E,
-    0xA4, 0x75, 0x1E, 0x41, 0xE2, 0x38, 0xCD, 0x99, 0x3B, 0xEA, 0x0E, 0x2F,
-    0x32, 0x80, 0xBB, 0xA1, 0x18, 0x3E, 0xB3, 0x31, 0x4E, 0x54, 0x8B, 0x38,
-    0x4F, 0x6D, 0xB9, 0x08, 0x6F, 0x42, 0x0D, 0x03, 0xF6, 0x0A, 0x04, 0xBF,
-    0x2C, 0xB8, 0x12, 0x90, 0x24, 0x97, 0x7C, 0x79, 0x56, 0x79, 0xB0, 0x72,
-    0xBC, 0xAF, 0x89, 0xAF, 0xDE, 0x9A, 0x77, 0x1F, 0xD9, 0x93, 0x08, 0x10,
-    0xB3, 0x8B, 0xAE, 0x12, 0xDC, 0xCF, 0x3F, 0x2E, 0x55, 0x12, 0x72, 0x1F,
-    0x2E, 0x6B, 0x71, 0x24, 0x50, 0x1A, 0xDD, 0xE6, 0x9F, 0x84, 0xCD, 0x87,
-    0x7A, 0x58, 0x47, 0x18, 0x74, 0x08, 0xDA, 0x17, 0xBC, 0x9F, 0x9A, 0xBC,
-    0xE9, 0x4B, 0x7D, 0x8C, 0xEC, 0x7A, 0xEC, 0x3A, 0xDB, 0x85, 0x1D, 0xFA,
-    0x63, 0x09, 0x43, 0x66, 0xC4, 0x64, 0xC3, 0xD2, 0xEF, 0x1C, 0x18, 0x47,
-    0x32, 0x15, 0xD8, 0x08, 0xDD, 0x43, 0x3B, 0x37, 0x24, 0xC2, 0xBA, 0x16,
-    0x12, 0xA1, 0x4D, 0x43, 0x2A, 0x65, 0xC4, 0x51, 0x50, 0x94, 0x00, 0x02,
-    0x13, 0x3A, 0xE4, 0xDD, 0x71, 0xDF, 0xF8, 0x9E, 0x10, 0x31, 0x4E, 0x55,
-    0x81, 0xAC, 0x77, 0xD6, 0x5F, 0x11, 0x19, 0x9B, 0x04, 0x35, 0x56, 0xF1,
-    0xD7, 0xA3, 0xC7, 0x6B, 0x3C, 0x11, 0x18, 0x3B, 0x59, 0x24, 0xA5, 0x09,
-    0xF2, 0x8F, 0xE6, 0xED, 0x97, 0xF1, 0xFB, 0xFA, 0x9E, 0xBA, 0xBF, 0x2C,
-    0x1E, 0x15, 0x3C, 0x6E, 0x86, 0xE3, 0x45, 0x70, 0xEA, 0xE9, 0x6F, 0xB1,
-    0x86, 0x0E, 0x5E, 0x0A, 0x5A, 0x3E, 0x2A, 0xB3, 0x77, 0x1F, 0xE7, 0x1C,
-    0x4E, 0x3D, 0x06, 0xFA, 0x29, 0x65, 0xDC, 0xB9, 0x99, 0xE7, 0x1D, 0x0F,
-    0x80, 0x3E, 0x89, 0xD6, 0x52, 0x66, 0xC8, 0x25, 0x2E, 0x4C, 0xC9, 0x78,
-    0x9C, 0x10, 0xB3, 0x6A, 0xC6, 0x15, 0x0E, 0xBA, 0x94, 0xE2, 0xEA, 0x78,
-    0xA6, 0xFC, 0x3C, 0x53, 0x1E, 0x0A, 0x2D, 0xF4, 0xF2, 0xF7, 0x4E, 0xA7,
-    0x36, 0x1D, 0x2B, 0x3D, 0x19, 0x39, 0x26, 0x0F, 0x19, 0xC2, 0x79, 0x60,
-    0x52, 0x23, 0xA7, 0x08, 0xF7, 0x13, 0x12, 0xB6, 0xEB, 0xAD, 0xFE, 0x6E,
-    0xEA, 0xC3, 0x1F, 0x66, 0xE3, 0xBC, 0x45, 0x95, 0xA6, 0x7B, 0xC8, 0x83,
-    0xB1, 0x7F, 0x37, 0xD1, 0x01, 0x8C, 0xFF, 0x28, 0xC3, 0x32, 0xDD, 0xEF,
-    0xBE, 0x6C, 0x5A, 0xA5, 0x65, 0x58, 0x21, 0x85, 0x68, 0xAB, 0x97, 0x02,
-    0xEE, 0xCE, 0xA5, 0x0F, 0xDB, 0x2F, 0x95, 0x3B, 0x2A, 0xEF, 0x7D, 0xAD,
-    0x5B, 0x6E, 0x2F, 0x84, 0x15, 0x21, 0xB6, 0x28, 0x29, 0x07, 0x61, 0x70,
-    0xEC, 0xDD, 0x47, 0x75, 0x61, 0x9F, 0x15, 0x10, 0x13, 0xCC, 0xA8, 0x30,
-    0xEB, 0x61, 0xBD, 0x96, 0x03, 0x34, 0xFE, 0x1E, 0xAA, 0x03, 0x63, 0xCF,
-    0xB5, 0x73, 0x5C, 0x90, 0x4C, 0x70, 0xA2, 0x39, 0xD5, 0x9E, 0x9E, 0x0B,
-    0xCB, 0xAA, 0xDE, 0x14, 0xEE, 0xCC, 0x86, 0xBC, 0x60, 0x62, 0x2C, 0xA7,
-    0x9C, 0xAB, 0x5C, 0xAB, 0xB2, 0xF3, 0x84, 0x6E, 0x64, 0x8B, 0x1E, 0xAF,
-    0x19, 0xBD, 0xF0, 0xCA, 0xA0, 0x23, 0x69, 0xB9, 0x65, 0x5A, 0xBB, 0x50,
-    0x40, 0x68, 0x5A, 0x32, 0x3C, 0x2A, 0xB4, 0xB3, 0x31, 0x9E, 0xE9, 0xD5,
-    0xC0, 0x21, 0xB8, 0xF7, 0x9B, 0x54, 0x0B, 0x19, 0x87, 0x5F, 0xA0, 0x99,
-    0x95, 0xF7, 0x99, 0x7E, 0x62, 0x3D, 0x7D, 0xA8, 0xF8, 0x37, 0x88, 0x9A,
-    0x97, 0xE3, 0x2D, 0x77, 0x11, 0xED, 0x93, 0x5F, 0x16, 0x68, 0x12, 0x81,
-    0x0E, 0x35, 0x88, 0x29, 0xC7, 0xE6, 0x1F, 0xD6, 0x96, 0xDE, 0xDF, 0xA1,
-    0x78, 0x58, 0xBA, 0x99, 0x57, 0xF5, 0x84, 0xA5, 0x1B, 0x22, 0x72, 0x63,
-    0x9B, 0x83, 0xC3, 0xFF, 0x1A, 0xC2, 0x46, 0x96, 0xCD, 0xB3, 0x0A, 0xEB,
-    0x53, 0x2E, 0x30, 0x54, 0x8F, 0xD9, 0x48, 0xE4, 0x6D, 0xBC, 0x31, 0x28,
-    0x58, 0xEB, 0xF2, 0xEF, 0x34, 0xC6, 0xFF, 0xEA, 0xFE, 0x28, 0xED, 0x61,
-    0xEE, 0x7C, 0x3C, 0x73, 0x5D, 0x4A, 0x14, 0xD9, 0xE8, 0x64, 0xB7, 0xE3,
-    0x42, 0x10, 0x5D, 0x14, 0x20, 0x3E, 0x13, 0xE0, 0x45, 0xEE, 0xE2, 0xB6,
-    0xA3, 0xAA, 0xAB, 0xEA, 0xDB, 0x6C, 0x4F, 0x15, 0xFA, 0xCB, 0x4F, 0xD0,
-    0xC7, 0x42, 0xF4, 0x42, 0xEF, 0x6A, 0xBB, 0xB5, 0x65, 0x4F, 0x3B, 0x1D,
-    0x41, 0xCD, 0x21, 0x05, 0xD8, 0x1E, 0x79, 0x9E, 0x86, 0x85, 0x4D, 0xC7,
-    0xE4, 0x4B, 0x47, 0x6A, 0x3D, 0x81, 0x62, 0x50, 0xCF, 0x62, 0xA1, 0xF2,
-    0x5B, 0x8D, 0x26, 0x46, 0xFC, 0x88, 0x83, 0xA0, 0xC1, 0xC7, 0xB6, 0xA3,
-    0x7F, 0x15, 0x24, 0xC3, 0x69, 0xCB, 0x74, 0x92, 0x47, 0x84, 0x8A, 0x0B,
-    0x56, 0x92, 0xB2, 0x85, 0x09, 0x5B, 0xBF, 0x00, 0xAD, 0x19, 0x48, 0x9D,
-    0x14, 0x62, 0xB1, 0x74, 0x23, 0x82, 0x0D, 0x00, 0x58, 0x42, 0x8D, 0x2A,
-    0x0C, 0x55, 0xF5, 0xEA, 0x1D, 0xAD, 0xF4, 0x3E, 0x23, 0x3F, 0x70, 0x61,
-    0x33, 0x72, 0xF0, 0x92, 0x8D, 0x93, 0x7E, 0x41, 0xD6, 0x5F, 0xEC, 0xF1,
-    0x6C, 0x22, 0x3B, 0xDB, 0x7C, 0xDE, 0x37, 0x59, 0xCB, 0xEE, 0x74, 0x60,
-    0x40, 0x85, 0xF2, 0xA7, 0xCE, 0x77, 0x32, 0x6E, 0xA6, 0x07, 0x80, 0x84,
-    0x19, 0xF8, 0x50, 0x9E, 0xE8, 0xEF, 0xD8, 0x55, 0x61, 0xD9, 0x97, 0x35,
-    0xA9, 0x69, 0xA7, 0xAA, 0xC5, 0x0C, 0x06, 0xC2, 0x5A, 0x04, 0xAB, 0xFC,
-    0x80, 0x0B, 0xCA, 0xDC, 0x9E, 0x44, 0x7A, 0x2E, 0xC3, 0x45, 0x34, 0x84,
-    0xFD, 0xD5, 0x67, 0x05, 0x0E, 0x1E, 0x9E, 0xC9, 0xDB, 0x73, 0xDB, 0xD3,
-    0x10, 0x55, 0x88, 0xCD, 0x67, 0x5F, 0xDA, 0x79, 0xE3, 0x67, 0x43, 0x40,
-    0xC5, 0xC4, 0x34, 0x65, 0x71, 0x3E, 0x38, 0xD8, 0x3D, 0x28, 0xF8, 0x9E,
-    0xF1, 0x6D, 0xFF, 0x20, 0x15, 0x3E, 0x21, 0xE7, 0x8F, 0xB0, 0x3D, 0x4A,
-    0xE6, 0xE3, 0x9F, 0x2B, 0xDB, 0x83, 0xAD, 0xF7, 0xE9, 0x3D, 0x5A, 0x68,
-    0x94, 0x81, 0x40, 0xF7, 0xF6, 0x4C, 0x26, 0x1C, 0x94, 0x69, 0x29, 0x34,
-    0x41, 0x15, 0x20, 0xF7, 0x76, 0x02, 0xD4, 0xF7, 0xBC, 0xF4, 0x6B, 0x2E,
-    0xD4, 0xA1, 0x00, 0x68, 0xD4, 0x08, 0x24, 0x71, 0x33, 0x20, 0xF4, 0x6A,
-    0x43, 0xB7, 0xD4, 0xB7, 0x50, 0x00, 0x61, 0xAF, 0x1E, 0x39, 0xF6, 0x2E,
-    0x97, 0x24, 0x45, 0x46,
-};
-
-alignas(16) const unsigned char kRandenRoundKeys[kKeyBytes] = {
-    0x44, 0x73, 0x70, 0x03, 0x2E, 0x8A, 0x19, 0x13, 0xD3, 0x08, 0xA3, 0x85,
-    0x88, 0x6A, 0x3F, 0x24, 0x89, 0x6C, 0x4E, 0xEC, 0x98, 0xFA, 0x2E, 0x08,
-    0xD0, 0x31, 0x9F, 0x29, 0x22, 0x38, 0x09, 0xA4, 0x6C, 0x0C, 0xE9, 0x34,
-    0xCF, 0x66, 0x54, 0xBE, 0x77, 0x13, 0xD0, 0x38, 0xE6, 0x21, 0x28, 0x45,
-    0x17, 0x09, 0x47, 0xB5, 0xB5, 0xD5, 0x84, 0x3F, 0xDD, 0x50, 0x7C, 0xC9,
-    0xB7, 0x29, 0xAC, 0xC0, 0xAC, 0xB5, 0xDF, 0x98, 0xA6, 0x0B, 0x31, 0xD1,
-    0x1B, 0xFB, 0x79, 0x89, 0xD9, 0xD5, 0x16, 0x92, 0x96, 0x7E, 0x26, 0x6A,
-    0xED, 0xAF, 0xE1, 0xB8, 0xB7, 0xDF, 0x1A, 0xD0, 0xDB, 0x72, 0xFD, 0x2F,
-    0xF7, 0x6C, 0x91, 0xB3, 0x47, 0x99, 0xA1, 0x24, 0x99, 0x7F, 0x2C, 0xF1,
-    0x45, 0x90, 0x7C, 0xBA, 0x69, 0x4E, 0x57, 0x71, 0xD8, 0x20, 0x69, 0x63,
-    0x16, 0xFC, 0x8E, 0x85, 0xE2, 0xF2, 0x01, 0x08, 0x58, 0xB6, 0x8E, 0x72,
-    0x8F, 0x74, 0x95, 0x0D, 0x7E, 0x3D, 0x93, 0xF4, 0xA3, 0xFE, 0x58, 0xA4,
-    0xB5, 0x59, 0x5A, 0xC2, 0x1D, 0xA4, 0x54, 0x7B, 0xEE, 0x4A, 0x15, 0x82,
-    0x58, 0xCD, 0x8B, 0x71, 0xF0, 0x85, 0x60, 0x28, 0x23, 0xB0, 0xD1, 0xC5,
-    0x13, 0x60, 0xF2, 0x2A, 0x39, 0xD5, 0x30, 0x9C, 0x0E, 0x18, 0x3A, 0x60,
-    0xB0, 0xDC, 0x79, 0x8E, 0xEF, 0x38, 0xDB, 0xB8, 0x18, 0x79, 0x41, 0xCA,
-    0x27, 0x4B, 0x31, 0xBD, 0xC1, 0x77, 0x15, 0xD7, 0x3E, 0x8A, 0x1E, 0xB0,
-    0x8B, 0x0E, 0x9E, 0x6C, 0x94, 0xAB, 0x55, 0xAA, 0xF3, 0x25, 0x55, 0xE6,
-    0x60, 0x5C, 0x60, 0x55, 0xDA, 0x2F, 0xAF, 0x78, 0xB6, 0x10, 0xAB, 0x2A,
-    0x6A, 0x39, 0xCA, 0x55, 0x40, 0x14, 0xE8, 0x63, 0x62, 0x98, 0x48, 0x57,
-    0x93, 0xE9, 0x72, 0x7C, 0xAF, 0x86, 0x54, 0xA1, 0xCE, 0xE8, 0x41, 0x11,
-    0x34, 0x5C, 0xCC, 0xB4, 0xF6, 0x31, 0x18, 0x74, 0x5D, 0xC5, 0xA9, 0x2B,
-    0x2A, 0xBC, 0x6F, 0x63, 0x11, 0x14, 0xEE, 0xB3, 0x5C, 0xCF, 0x24, 0x6C,
-    0x33, 0xBA, 0xD6, 0xAF, 0x1E, 0x93, 0x87, 0x9B, 0x16, 0x3E, 0x5C, 0xCE,
-    0xAF, 0xB9, 0x4B, 0x6B, 0x98, 0x48, 0x8F, 0x3B, 0x77, 0x86, 0x95, 0x28,
-    0x81, 0x53, 0x32, 0x7A, 0x91, 0xA9, 0x21, 0xFB, 0xCC, 0x09, 0xD8, 0x61,
-    0x93, 0x21, 0x28, 0x66, 0x1B, 0xE8, 0xBF, 0xC4, 0xB1, 0x75, 0x85, 0xE9,
-    0x5D, 0x5D, 0x84, 0xEF, 0x32, 0x80, 0xEC, 0x5D, 0x60, 0xAC, 0x7C, 0x48,
-    0xC5, 0xAC, 0x96, 0xD3, 0x81, 0x3E, 0x89, 0x23, 0x88, 0x1B, 0x65, 0xEB,
-    0x02, 0x23, 0x26, 0xDC, 0x04, 0x20, 0x84, 0xA4, 0x82, 0x44, 0x0B, 0x2E,
-    0x39, 0x42, 0xF4, 0x83, 0xF3, 0x6F, 0x6D, 0x0F, 0x9A, 0x6C, 0xE9, 0xF6,
-    0x42, 0x68, 0xC6, 0x21, 0x5E, 0x9B, 0x1F, 0x9E, 0x4A, 0xF0, 0xC8, 0x69,
-    0x68, 0x2F, 0x54, 0xD8, 0xD2, 0xA0, 0x51, 0x6A, 0xF0, 0x88, 0xD3, 0xAB,
-    0x61, 0x9C, 0x0C, 0x67, 0xE4, 0x3B, 0x7A, 0x13, 0x6C, 0x0B, 0xEF, 0x6E,
-    0xA3, 0x33, 0x51, 0xAB, 0x28, 0xA7, 0x0F, 0x96, 0x76, 0x01, 0xAF, 0x39,
-    0x1D, 0x65, 0xF1, 0xA1, 0x98, 0x2A, 0xFB, 0x7E, 0x50, 0xF0, 0x3B, 0xBA,
-    0xB4, 0x9F, 0x6F, 0x45, 0x19, 0x86, 0xEE, 0x8C, 0x88, 0x0E, 0x43, 0x82,
-    0x3E, 0x59, 0xCA, 0x66, 0x73, 0x20, 0xC1, 0x85, 0xD8, 0x75, 0x6F, 0xE0,
-    0xBE, 0x5E, 0x8B, 0x3B, 0xC3, 0xA5, 0x84, 0x7D, 0x06, 0x77, 0x3F, 0x36,
-    0x62, 0xAA, 0xD3, 0x4E, 0xA6, 0x6A, 0xC1, 0x56, 0x9F, 0x44, 0x1A, 0x40,
-    0x48, 0x12, 0x0A, 0xD0, 0x24, 0xD7, 0xD0, 0x37, 0x3D, 0x02, 0x9B, 0x42,
-    0x72, 0xDF, 0xFE, 0x1B, 0x7B, 0x1B, 0x99, 0x80, 0xC9, 0x72, 0x53, 0x07,
-    0x9B, 0xC0, 0xF1, 0x49, 0xD3, 0xEA, 0x0F, 0xDB, 0x3B, 0x4C, 0x79, 0xB6,
-    0x1A, 0x50, 0xFE, 0xE3, 0xF7, 0xDE, 0xE8, 0xF6, 0xD8, 0x79, 0xD4, 0x25,
-    0xC4, 0x60, 0x9F, 0x40, 0xB6, 0x4F, 0xA9, 0xC1, 0xBA, 0x06, 0xC0, 0x04,
-    0xBD, 0xE0, 0x6C, 0x97, 0xB5, 0x53, 0x6C, 0x3E, 0xAF, 0x6F, 0xFB, 0x68,
-    0x63, 0x24, 0x6A, 0x19, 0xC2, 0x9E, 0x5C, 0x5E, 0x2C, 0x95, 0x30, 0x9B,
-    0x1F, 0x51, 0xFC, 0x6D, 0x6F, 0xEC, 0x52, 0x3B, 0xEB, 0xB2, 0x39, 0x13,
-    0xFD, 0x4A, 0x33, 0xDE, 0x04, 0xD0, 0xE3, 0xBE, 0x09, 0xBD, 0x5E, 0xAF,
-    0x44, 0x45, 0x81, 0xCC, 0x0F, 0x74, 0xC8, 0x45, 0x57, 0xA8, 0xCB, 0xC0,
-    0xB3, 0x4B, 0x2E, 0x19, 0x07, 0x28, 0x0F, 0x66, 0x0A, 0x32, 0x60, 0x1A,
-    0xBD, 0xC0, 0x79, 0x55, 0xDB, 0xFB, 0xD3, 0xB9, 0x39, 0x5F, 0x0B, 0xD2,
-    0xCC, 0xA3, 0x1F, 0xFB, 0xFE, 0x25, 0x9F, 0x67, 0x79, 0x72, 0x2C, 0x40,
-    0xC6, 0x00, 0xA1, 0xD6, 0x15, 0x6B, 0x61, 0xFD, 0xDF, 0x16, 0x75, 0x3C,
-    0xF8, 0x22, 0x32, 0xDB, 0xF8, 0xE9, 0xA5, 0x8E, 0x60, 0x87, 0x23, 0xFD,
-    0xFA, 0xB5, 0x3D, 0x32, 0xAB, 0x52, 0x05, 0xAD, 0xC8, 0x1E, 0x50, 0x2F,
-    0xA0, 0x8C, 0x6F, 0xCA, 0xBB, 0x57, 0x5C, 0x9E, 0x82, 0xDF, 0x00, 0x3E,
-    0x48, 0x7B, 0x31, 0x53, 0xC3, 0xFF, 0x7E, 0x28, 0xF6, 0xA8, 0x42, 0xD5,
-    0xDB, 0x69, 0x17, 0xDF, 0x2E, 0x56, 0x87, 0x1A, 0xC8, 0x58, 0xCA, 0xBB,
-    0xB0, 0x27, 0x5B, 0x69, 0x73, 0x55, 0x4F, 0x8C, 0xC6, 0x32, 0x67, 0xAC,
-    0xB8, 0x83, 0x21, 0xFD, 0x98, 0x3D, 0xFA, 0x10, 0xA0, 0x11, 0xF0, 0xB8,
-    0x5D, 0xA3, 0xFF, 0xE1, 0x65, 0x45, 0xF8, 0xB6, 0x79, 0xE4, 0x53, 0x9A,
-    0x5B, 0xD3, 0xD1, 0x2D, 0x6C, 0xB5, 0xFC, 0x4A, 0x33, 0x7E, 0xCB, 0xA4,
-    0xDA, 0xF2, 0xDD, 0xE1, 0x90, 0x97, 0xFB, 0x4B, 0xBC, 0x49, 0x8E, 0xD2,
-    0x01, 0x4C, 0x77, 0x36, 0xDA, 0xCA, 0x20, 0xEF, 0xE8, 0xC6, 0xE4, 0xCE,
-    0x41, 0x13, 0xFB, 0x62, 0x98, 0x91, 0x90, 0xAE, 0x4D, 0xDA, 0xDB, 0x95,
-    0xB4, 0x1F, 0xF1, 0x2B, 0xFE, 0x9E, 0x7E, 0xD0, 0xE0, 0x25, 0xC7, 0xAF,
-    0xD0, 0xD1, 0x8E, 0xD0, 0xA0, 0xD5, 0x93, 0x6B, 0x71, 0x8E, 0xAD, 0xEA,
-    0x64, 0x2B, 0x12, 0xF2, 0xFB, 0xE2, 0xF6, 0x8F, 0xB7, 0x94, 0x75, 0x8E,
-    0x2F, 0x5B, 0x3C, 0x8E, 0x1C, 0xC3, 0x8F, 0x68, 0xA0, 0x5E, 0xAD, 0x4F,
-    0x1C, 0xF0, 0x0D, 0x90, 0x12, 0xB8, 0x88, 0x88, 0x77, 0x17, 0x0E, 0xBE,
-    0x18, 0x22, 0x2F, 0x2F, 0xAD, 0xC1, 0xA8, 0xB3, 0x91, 0xF1, 0xCF, 0xD1,
-    0xE8, 0x74, 0x6F, 0xB5, 0x0F, 0xCC, 0xA0, 0xE5, 0xA1, 0x1F, 0x02, 0x8B,
-    0xFE, 0x2D, 0x75, 0xEA, 0xB7, 0xE0, 0x13, 0xFD, 0xE0, 0x4F, 0xA8, 0xB4,
-    0x99, 0xE2, 0x89, 0xCE, 0xD6, 0xF3, 0xAC, 0x18, 0x05, 0x77, 0x95, 0x80,
-    0x66, 0xA2, 0x5F, 0x16, 0xD9, 0xA8, 0xAD, 0xD2, 0x81, 0x3B, 0xC4, 0x7C,
-    0x86, 0xFA, 0xB5, 0x77, 0x65, 0x20, 0xAD, 0xE6, 0x77, 0x14, 0x1A, 0x21,
-    0x14, 0x73, 0xCC, 0x93, 0xA0, 0x89, 0x3E, 0x7B, 0x0C, 0xAF, 0xCD, 0xEB,
-    0xCF, 0x35, 0x9D, 0xFB, 0xF5, 0x42, 0x54, 0xC7, 0x5E, 0xB3, 0x71, 0x20,
-    0x2D, 0x0E, 0x25, 0x00, 0x49, 0x7E, 0x1E, 0xAE, 0xD3, 0x1B, 0x41, 0xD6,
-    0x1E, 0xB9, 0x09, 0xF0, 0x9B, 0x36, 0x64, 0x24, 0xAF, 0xE0, 0xB8, 0x57,
-    0xBB, 0x00, 0x68, 0x22, 0x7F, 0x53, 0x5A, 0xD9, 0x89, 0x43, 0xC1, 0x78,
-    0xAA, 0xA6, 0xDF, 0x59, 0x1D, 0x91, 0x63, 0x55, 0xA9, 0xCF, 0x95, 0x62,
-    0x76, 0x03, 0x26, 0x83, 0xC5, 0xB9, 0xE5, 0x02, 0xA2, 0x5B, 0x7D, 0x20,
-    0x4A, 0xA9, 0x14, 0x7B, 0xCA, 0x2D, 0x47, 0xB3, 0x41, 0x4A, 0x73, 0x4E,
-    0x68, 0x19, 0xC8, 0x11, 0xE4, 0xC6, 0x9B, 0xBC, 0x3F, 0x57, 0x0F, 0xD6,
-    0x15, 0x29, 0x53, 0x9A, 0x52, 0x00, 0x51, 0x1B, 0x1F, 0xE9, 0x1B, 0x57,
-    0xB5, 0x6F, 0xBA, 0x08, 0x00, 0x74, 0xE6, 0x81, 0x76, 0xA4, 0x60, 0x2B,
-    0xB6, 0xF9, 0xB9, 0xE7, 0x21, 0x65, 0x63, 0xB6, 0x15, 0xD9, 0x0D, 0x2A,
-    0x6B, 0xEC, 0x96, 0xF2, 0xA1, 0x8F, 0x9F, 0xA9, 0x5D, 0x2D, 0xB0, 0x53,
-    0x64, 0x56, 0x85, 0xC5, 0x2E, 0x05, 0x34, 0xFF, 0x44, 0x29, 0xB3, 0xB5,
-    0xE9, 0x70, 0x7A, 0x4B, 0x6A, 0x07, 0x85, 0x6E, 0x99, 0x47, 0xBA, 0x08,
-    0x7D, 0xDF, 0xA7, 0x49, 0xB0, 0xA6, 0x6E, 0xAD, 0x23, 0x26, 0x19, 0xC4,
-    0x2E, 0x09, 0x75, 0xDB, 0xFF, 0x18, 0x9A, 0x69, 0x71, 0x8C, 0xAA, 0xEC,
-    0x66, 0xB2, 0xED, 0x8F, 0xB8, 0x60, 0xEE, 0x9C, 0x29, 0x4C, 0x09, 0x75,
-    0xA5, 0x02, 0x36, 0x19, 0xE1, 0x9E, 0xB1, 0xC2, 0x6C, 0x52, 0x64, 0x56,
-    0x65, 0x9D, 0x42, 0x5B, 0x9A, 0x98, 0x54, 0x3F, 0x3E, 0x3A, 0x18, 0xE4,
-    0x40, 0x13, 0x59, 0xA0, 0xF5, 0x30, 0xE8, 0xEF, 0x07, 0x9C, 0xD2, 0xA1,
-    0xD6, 0x3F, 0xF7, 0x99, 0xD6, 0xE4, 0x8F, 0x6B, 0x26, 0xEB, 0x70, 0x84,
-    0x86, 0x20, 0xDD, 0x4C, 0xC1, 0x5D, 0x25, 0xF0, 0xE6, 0x38, 0x2D, 0x4D,
-    0xC9, 0xEF, 0xBA, 0x3E, 0x3F, 0x6B, 0x68, 0x09, 0x5E, 0xCC, 0x1E, 0x02,
-    0xC6, 0xE9, 0x82, 0x63, 0x86, 0xE2, 0xA0, 0x52, 0x84, 0x35, 0x7F, 0x68,
-    0xA1, 0x70, 0x6A, 0x6B, 0x14, 0x18, 0x97, 0x3C, 0x5C, 0xAE, 0xDE, 0x7F,
-    0x1C, 0x84, 0x07, 0x3E, 0x37, 0x07, 0x50, 0xAA, 0x05, 0x53, 0x9C, 0xB7,
-    0x0D, 0x0C, 0x50, 0xF0, 0x37, 0xDA, 0x3A, 0xB0, 0xB8, 0xF2, 0x16, 0x57,
-    0xEC, 0x44, 0x7D, 0x8E, 0xB2, 0x74, 0xB5, 0x3C, 0x1A, 0xF5, 0x0C, 0xAE,
-    0xFF, 0xB3, 0x00, 0x02, 0x04, 0x1F, 0x1C, 0xF0, 0xF6, 0x2F, 0xA9, 0x7C,
-    0xF9, 0x13, 0x91, 0xD1, 0xBD, 0x21, 0x09, 0xDC, 0x58, 0x7A, 0x83, 0x25,
-    0xDC, 0xDA, 0xC2, 0x37, 0x81, 0xE5, 0xE5, 0x3A, 0x01, 0x47, 0xF5, 0x22,
-    0x73, 0x47, 0x32, 0x94, 0x0E, 0x03, 0xD0, 0x0F, 0x46, 0x61, 0x44, 0xA9,
-    0xA7, 0xDD, 0xF3, 0x9A, 0x34, 0x76, 0xB5, 0xC8, 0x2F, 0x0E, 0xEA, 0x3B,
-    0x99, 0xCD, 0x38, 0xE2, 0x41, 0x1E, 0x75, 0xA4, 0x3E, 0xC7, 0xC8, 0xEC,
-    0x08, 0xB9, 0x6D, 0x4F, 0x38, 0x8B, 0x54, 0x4E, 0x31, 0xB3, 0x3E, 0x18,
-    0xA1, 0xBB, 0x80, 0x32, 0x79, 0x7C, 0x97, 0x24, 0x90, 0x12, 0xB8, 0x2C,
-    0xBF, 0x04, 0x0A, 0xF6, 0x03, 0x0D, 0x42, 0x6F, 0x10, 0x08, 0x93, 0xD9,
-    0x1F, 0x77, 0x9A, 0xDE, 0xAF, 0x89, 0xAF, 0xBC, 0x72, 0xB0, 0x79, 0x56,
-    0x24, 0x71, 0x6B, 0x2E, 0x1F, 0x72, 0x12, 0x55, 0x2E, 0x3F, 0xCF, 0xDC,
-    0x12, 0xAE, 0x8B, 0xB3, 0x17, 0xDA, 0x08, 0x74, 0x18, 0x47, 0x58, 0x7A,
-    0x87, 0xCD, 0x84, 0x9F, 0xE6, 0xDD, 0x1A, 0x50, 0xFA, 0x1D, 0x85, 0xDB,
-    0x3A, 0xEC, 0x7A, 0xEC, 0x8C, 0x7D, 0x4B, 0xE9, 0xBC, 0x9A, 0x9F, 0xBC,
-    0x08, 0xD8, 0x15, 0x32, 0x47, 0x18, 0x1C, 0xEF, 0xD2, 0xC3, 0x64, 0xC4,
-    0x66, 0x43, 0x09, 0x63, 0x51, 0xC4, 0x65, 0x2A, 0x43, 0x4D, 0xA1, 0x12,
-    0x16, 0xBA, 0xC2, 0x24, 0x37, 0x3B, 0x43, 0xDD, 0x55, 0x4E, 0x31, 0x10,
-    0x9E, 0xF8, 0xDF, 0x71, 0xDD, 0xE4, 0x3A, 0x13, 0x02, 0x00, 0x94, 0x50,
-    0x6B, 0xC7, 0xA3, 0xD7, 0xF1, 0x56, 0x35, 0x04, 0x9B, 0x19, 0x11, 0x5F,
-    0xD6, 0x77, 0xAC, 0x81, 0xFA, 0xFB, 0xF1, 0x97, 0xED, 0xE6, 0x8F, 0xF2,
-    0x09, 0xA5, 0x24, 0x59, 0x3B, 0x18, 0x11, 0x3C, 0xB1, 0x6F, 0xE9, 0xEA,
-    0x70, 0x45, 0xE3, 0x86, 0x6E, 0x3C, 0x15, 0x1E, 0x2C, 0xBF, 0xBA, 0x9E,
-    0xFA, 0x06, 0x3D, 0x4E, 0x1C, 0xE7, 0x1F, 0x77, 0xB3, 0x2A, 0x3E, 0x5A,
-    0x0A, 0x5E, 0x0E, 0x86, 0x25, 0xC8, 0x66, 0x52, 0xD6, 0x89, 0x3E, 0x80,
-    0x0F, 0x1D, 0xE7, 0x99, 0xB9, 0xDC, 0x65, 0x29, 0x78, 0xEA, 0xE2, 0x94,
-    0xBA, 0x0E, 0x15, 0xC6, 0x6A, 0xB3, 0x10, 0x9C, 0x78, 0xC9, 0x4C, 0x2E,
-    0x3D, 0x2B, 0x1D, 0x36, 0xA7, 0x4E, 0xF7, 0xF2, 0xF4, 0x2D, 0x0A, 0x1E,
-    0x53, 0x3C, 0xFC, 0xA6, 0xB6, 0x12, 0x13, 0xF7, 0x08, 0xA7, 0x23, 0x52,
-    0x60, 0x79, 0xC2, 0x19, 0x0F, 0x26, 0x39, 0x19, 0x83, 0xC8, 0x7B, 0xA6,
-    0x95, 0x45, 0xBC, 0xE3, 0x66, 0x1F, 0xC3, 0xEA, 0x6E, 0xFE, 0xAD, 0xEB,
-    0xA5, 0x5A, 0x6C, 0xBE, 0xEF, 0xDD, 0x32, 0xC3, 0x28, 0xFF, 0x8C, 0x01,
-    0xD1, 0x37, 0x7F, 0xB1, 0x3B, 0x95, 0x2F, 0xDB, 0x0F, 0xA5, 0xCE, 0xEE,
-    0x02, 0x97, 0xAB, 0x68, 0x85, 0x21, 0x58, 0x65, 0x70, 0x61, 0x07, 0x29,
-    0x28, 0xB6, 0x21, 0x15, 0x84, 0x2F, 0x6E, 0x5B, 0xAD, 0x7D, 0xEF, 0x2A,
-    0x96, 0xBD, 0x61, 0xEB, 0x30, 0xA8, 0xCC, 0x13, 0x10, 0x15, 0x9F, 0x61,
-    0x75, 0x47, 0xDD, 0xEC, 0x39, 0xA2, 0x70, 0x4C, 0x90, 0x5C, 0x73, 0xB5,
-    0xCF, 0x63, 0x03, 0xAA, 0x1E, 0xFE, 0x34, 0x03, 0xA7, 0x2C, 0x62, 0x60,
-    0xBC, 0x86, 0xCC, 0xEE, 0x14, 0xDE, 0xAA, 0xCB, 0x0B, 0x9E, 0x9E, 0xD5,
-    0xCA, 0xF0, 0xBD, 0x19, 0xAF, 0x1E, 0x8B, 0x64, 0x6E, 0x84, 0xF3, 0xB2,
-    0xAB, 0x5C, 0xAB, 0x9C, 0xB3, 0xB4, 0x2A, 0x3C, 0x32, 0x5A, 0x68, 0x40,
-    0x50, 0xBB, 0x5A, 0x65, 0xB9, 0x69, 0x23, 0xA0, 0x99, 0xA0, 0x5F, 0x87,
-    0x19, 0x0B, 0x54, 0x9B, 0xF7, 0xB8, 0x21, 0xC0, 0xD5, 0xE9, 0x9E, 0x31,
-    0x77, 0x2D, 0xE3, 0x97, 0x9A, 0x88, 0x37, 0xF8, 0xA8, 0x7D, 0x3D, 0x62,
-    0x7E, 0x99, 0xF7, 0x95, 0xD6, 0x1F, 0xE6, 0xC7, 0x29, 0x88, 0x35, 0x0E,
-    0x81, 0x12, 0x68, 0x16, 0x5F, 0x93, 0xED, 0x11, 0x63, 0x72, 0x22, 0x1B,
-    0xA5, 0x84, 0xF5, 0x57, 0x99, 0xBA, 0x58, 0x78, 0xA1, 0xDF, 0xDE, 0x96,
-    0x54, 0x30, 0x2E, 0x53, 0xEB, 0x0A, 0xB3, 0xCD, 0x96, 0x46, 0xC2, 0x1A,
-    0xFF, 0xC3, 0x83, 0x9B, 0xEA, 0xFF, 0xC6, 0x34, 0xEF, 0xF2, 0xEB, 0x58,
-    0x28, 0x31, 0xBC, 0x6D, 0xE4, 0x48, 0xD9, 0x8F, 0xE3, 0xB7, 0x64, 0xE8,
-    0xD9, 0x14, 0x4A, 0x5D, 0x73, 0x3C, 0x7C, 0xEE, 0x61, 0xED, 0x28, 0xFE,
-    0xEA, 0xAB, 0xAA, 0xA3, 0xB6, 0xE2, 0xEE, 0x45, 0xE0, 0x13, 0x3E, 0x20,
-    0x14, 0x5D, 0x10, 0x42, 0xB5, 0xBB, 0x6A, 0xEF, 0x42, 0xF4, 0x42, 0xC7,
-    0xD0, 0x4F, 0xCB, 0xFA, 0x15, 0x4F, 0x6C, 0xDB, 0xC7, 0x4D, 0x85, 0x86,
-    0x9E, 0x79, 0x1E, 0xD8, 0x05, 0x21, 0xCD, 0x41, 0x1D, 0x3B, 0x4F, 0x65,
-    0x46, 0x26, 0x8D, 0x5B, 0xF2, 0xA1, 0x62, 0xCF, 0x50, 0x62, 0x81, 0x3D,
-    0x6A, 0x47, 0x4B, 0xE4, 0x92, 0x74, 0xCB, 0x69, 0xC3, 0x24, 0x15, 0x7F,
-    0xA3, 0xB6, 0xC7, 0xC1, 0xA0, 0x83, 0x88, 0xFC, 0x9D, 0x48, 0x19, 0xAD,
-    0x00, 0xBF, 0x5B, 0x09, 0x85, 0xB2, 0x92, 0x56, 0x0B, 0x8A, 0x84, 0x47,
-    0xEA, 0xF5, 0x55, 0x0C, 0x2A, 0x8D, 0x42, 0x58, 0x00, 0x0D, 0x82, 0x23,
-    0x74, 0xB1, 0x62, 0x14, 0x41, 0x7E, 0x93, 0x8D, 0x92, 0xF0, 0x72, 0x33,
-    0x61, 0x70, 0x3F, 0x23, 0x3E, 0xF4, 0xAD, 0x1D, 0x60, 0x74, 0xEE, 0xCB,
-    0x59, 0x37, 0xDE, 0x7C, 0xDB, 0x3B, 0x22, 0x6C, 0xF1, 0xEC, 0x5F, 0xD6,
-    0x9E, 0x50, 0xF8, 0x19, 0x84, 0x80, 0x07, 0xA6, 0x6E, 0x32, 0x77, 0xCE,
-    0xA7, 0xF2, 0x85, 0x40, 0xC2, 0x06, 0x0C, 0xC5, 0xAA, 0xA7, 0x69, 0xA9,
-    0x35, 0x97, 0xD9, 0x61, 0x55, 0xD8, 0xEF, 0xE8, 0x84, 0x34, 0x45, 0xC3,
-    0x2E, 0x7A, 0x44, 0x9E, 0xDC, 0xCA, 0x0B, 0x80, 0xFC, 0xAB, 0x04, 0x5A,
-    0xCD, 0x88, 0x55, 0x10, 0xD3, 0xDB, 0x73, 0xDB, 0xC9, 0x9E, 0x1E, 0x0E,
-    0x05, 0x67, 0xD5, 0xFD, 0xD8, 0x38, 0x3E, 0x71, 0x65, 0x34, 0xC4, 0xC5,
-    0x40, 0x43, 0x67, 0xE3, 0x79, 0xDA, 0x5F, 0x67, 0x4A, 0x3D, 0xB0, 0x8F,
-    0xE7, 0x21, 0x3E, 0x15, 0x20, 0xFF, 0x6D, 0xF1, 0x9E, 0xF8, 0x28, 0x3D,
-    0xF7, 0x40, 0x81, 0x94, 0x68, 0x5A, 0x3D, 0xE9, 0xF7, 0xAD, 0x83, 0xDB,
-    0x2B, 0x9F, 0xE3, 0xE6, 0xF7, 0xD4, 0x02, 0x76, 0xF7, 0x20, 0x15, 0x41,
-    0x34, 0x29, 0x69, 0x94, 0x1C, 0x26, 0x4C, 0xF6, 0x6A, 0xF4, 0x20, 0x33,
-    0x71, 0x24, 0x08, 0xD4, 0x68, 0x00, 0xA1, 0xD4, 0x2E, 0x6B, 0xF4, 0xBC,
-    0x46, 0x45, 0x24, 0x97, 0x2E, 0xF6, 0x39, 0x1E, 0xAF, 0x61, 0x00, 0x50,
-    0xB7, 0xD4, 0xB7, 0x43,
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_slow.cc b/third_party/abseil_cpp/absl/random/internal/randen_slow.cc
deleted file mode 100644
index 4e5f3dc1c7..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_slow.cc
+++ /dev/null
@@ -1,457 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/randen_slow.h"
-
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-
-#include "absl/base/attributes.h"
-#include "absl/random/internal/platform.h"
-#include "absl/random/internal/randen_traits.h"
-
-#if ABSL_HAVE_ATTRIBUTE(always_inline) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \
-  __attribute__((always_inline))
-#elif defined(_MSC_VER)
-// We can achieve something similar to attribute((always_inline)) with MSVC by
-// using the __forceinline keyword, however this is not perfect. MSVC is
-// much less aggressive about inlining, and even with the __forceinline keyword.
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __forceinline
-#else
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE
-#endif
-
-namespace {
-
-// AES portions based on rijndael-alg-fst.c,
-// https://fastcrypto.org/front/misc/rijndael-alg-fst.c
-//
-// Implementation of
-// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
-constexpr uint32_t te0[256] = {
-    0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd,
-    0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,
-    0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d,
-    0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
-    0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7,
-    0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
-    0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4,
-    0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
-    0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1,
-    0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
-    0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e,
-    0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
-    0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e,
-    0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,
-    0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46,
-    0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
-    0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7,
-    0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,
-    0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe,
-    0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
-    0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a,
-    0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
-    0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2,
-    0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
-    0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e,
-    0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
-    0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256,
-    0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
-    0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4,
-    0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
-    0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa,
-    0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
-    0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1,
-    0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
-    0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42,
-    0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
-    0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158,
-    0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
-    0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22,
-    0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
-    0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631,
-    0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
-    0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,
-};
-
-constexpr uint32_t te1[256] = {
-    0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b,
-    0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b,
-    0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282,
-    0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,
-    0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4,
-    0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626,
-    0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5,
-    0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
-    0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696,
-    0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2,
-    0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383,
-    0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
-    0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3,
-    0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded,
-    0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb,
-    0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
-    0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d,
-    0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f,
-    0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3,
-    0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,
-    0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff,
-    0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec,
-    0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7,
-    0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
-    0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a,
-    0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414,
-    0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232,
-    0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,
-    0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595,
-    0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d,
-    0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656,
-    0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
-    0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6,
-    0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f,
-    0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e,
-    0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,
-    0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1,
-    0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111,
-    0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e,
-    0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
-    0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6,
-    0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f,
-    0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616,
-};
-
-constexpr uint32_t te2[256] = {
-    0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b,
-    0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b,
-    0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82,
-    0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
-    0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4,
-    0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26,
-    0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5,
-    0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
-    0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796,
-    0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2,
-    0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83,
-    0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
-    0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3,
-    0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed,
-    0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb,
-    0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
-    0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d,
-    0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f,
-    0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3,
-    0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
-    0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff,
-    0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec,
-    0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7,
-    0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
-    0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a,
-    0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814,
-    0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432,
-    0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,
-    0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195,
-    0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d,
-    0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56,
-    0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,
-    0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6,
-    0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f,
-    0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e,
-    0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,
-    0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1,
-    0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211,
-    0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e,
-    0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
-    0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6,
-    0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f,
-    0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16,
-};
-
-constexpr uint32_t te3[256] = {
-    0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6,
-    0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56,
-    0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f,
-    0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
-    0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753,
-    0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c,
-    0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451,
-    0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
-    0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137,
-    0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf,
-    0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d,
-    0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
-    0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd,
-    0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1,
-    0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d,
-    0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
-    0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a,
-    0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe,
-    0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d,
-    0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
-    0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5,
-    0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3,
-    0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255,
-    0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
-    0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54,
-    0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28,
-    0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664,
-    0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,
-    0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431,
-    0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da,
-    0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac,
-    0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,
-    0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157,
-    0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e,
-    0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c,
-    0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,
-    0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899,
-    0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322,
-    0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c,
-    0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
-    0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7,
-    0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e,
-    0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c,
-};
-
-// Software implementation of the Vector128 class, using uint32_t
-// as an underlying vector register.
-struct alignas(16) Vector128 {
-  uint32_t s[4];
-};
-
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-Vector128Load(const void* from) {
-  Vector128 result;
-  const uint8_t* src = reinterpret_cast<const uint8_t*>(from);
-  result.s[0] = static_cast<uint32_t>(src[0]) << 24 |
-                static_cast<uint32_t>(src[1]) << 16 |
-                static_cast<uint32_t>(src[2]) << 8 |
-                static_cast<uint32_t>(src[3]);
-  result.s[1] = static_cast<uint32_t>(src[4]) << 24 |
-                static_cast<uint32_t>(src[5]) << 16 |
-                static_cast<uint32_t>(src[6]) << 8 |
-                static_cast<uint32_t>(src[7]);
-  result.s[2] = static_cast<uint32_t>(src[8]) << 24 |
-                static_cast<uint32_t>(src[9]) << 16 |
-                static_cast<uint32_t>(src[10]) << 8 |
-                static_cast<uint32_t>(src[11]);
-  result.s[3] = static_cast<uint32_t>(src[12]) << 24 |
-                static_cast<uint32_t>(src[13]) << 16 |
-                static_cast<uint32_t>(src[14]) << 8 |
-                static_cast<uint32_t>(src[15]);
-  return result;
-}
-
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Vector128Store(
-    const Vector128& v, void* to) {
-  uint8_t* dst = reinterpret_cast<uint8_t*>(to);
-  dst[0] = static_cast<uint8_t>(v.s[0] >> 24);
-  dst[1] = static_cast<uint8_t>(v.s[0] >> 16);
-  dst[2] = static_cast<uint8_t>(v.s[0] >> 8);
-  dst[3] = static_cast<uint8_t>(v.s[0]);
-  dst[4] = static_cast<uint8_t>(v.s[1] >> 24);
-  dst[5] = static_cast<uint8_t>(v.s[1] >> 16);
-  dst[6] = static_cast<uint8_t>(v.s[1] >> 8);
-  dst[7] = static_cast<uint8_t>(v.s[1]);
-  dst[8] = static_cast<uint8_t>(v.s[2] >> 24);
-  dst[9] = static_cast<uint8_t>(v.s[2] >> 16);
-  dst[10] = static_cast<uint8_t>(v.s[2] >> 8);
-  dst[11] = static_cast<uint8_t>(v.s[2]);
-  dst[12] = static_cast<uint8_t>(v.s[3] >> 24);
-  dst[13] = static_cast<uint8_t>(v.s[3] >> 16);
-  dst[14] = static_cast<uint8_t>(v.s[3] >> 8);
-  dst[15] = static_cast<uint8_t>(v.s[3]);
-}
-
-// One round of AES. "round_key" is a public constant for breaking the
-// symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-AesRound(const Vector128& state, const Vector128& round_key) {
-  Vector128 result;
-  result.s[0] = round_key.s[0] ^                  //
-                te0[uint8_t(state.s[0] >> 24)] ^  //
-                te1[uint8_t(state.s[1] >> 16)] ^  //
-                te2[uint8_t(state.s[2] >> 8)] ^   //
-                te3[uint8_t(state.s[3])];
-  result.s[1] = round_key.s[1] ^                  //
-                te0[uint8_t(state.s[1] >> 24)] ^  //
-                te1[uint8_t(state.s[2] >> 16)] ^  //
-                te2[uint8_t(state.s[3] >> 8)] ^   //
-                te3[uint8_t(state.s[0])];
-  result.s[2] = round_key.s[2] ^                  //
-                te0[uint8_t(state.s[2] >> 24)] ^  //
-                te1[uint8_t(state.s[3] >> 16)] ^  //
-                te2[uint8_t(state.s[0] >> 8)] ^   //
-                te3[uint8_t(state.s[1])];
-  result.s[3] = round_key.s[3] ^                  //
-                te0[uint8_t(state.s[3] >> 24)] ^  //
-                te1[uint8_t(state.s[0] >> 16)] ^  //
-                te2[uint8_t(state.s[1] >> 8)] ^   //
-                te3[uint8_t(state.s[2])];
-  return result;
-}
-
-using ::absl::random_internal::RandenTraits;
-
-// Randen operates on 128-bit vectors.
-struct alignas(16) u64x2 {
-  uint64_t data[2];
-};
-
-// The improved Feistel block shuffle function for 16 blocks.
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle(
-    u64x2* state) {
-  static_assert(RandenTraits::kFeistelBlocks == 16,
-                "Feistel block shuffle only works for 16 blocks.");
-
-  constexpr size_t shuffle[RandenTraits::kFeistelBlocks] = {
-      7, 2, 13, 4, 11, 8, 3, 6, 15, 0, 9, 10, 1, 14, 5, 12};
-
-  // The fully unrolled loop without the memcpy improves the speed by about
-  // 30% over the equivalent:
-#if 0
-  u64x2 source[RandenTraits::kFeistelBlocks];
-  std::memcpy(source, state, sizeof(source));
-  for (size_t i = 0; i < RandenTraits::kFeistelBlocks; i++) {
-    const u64x2 v0 = source[shuffle[i]];
-    state[i] = v0;
-  }
-  return;
-#endif
-
-  const u64x2 v0 = state[shuffle[0]];
-  const u64x2 v1 = state[shuffle[1]];
-  const u64x2 v2 = state[shuffle[2]];
-  const u64x2 v3 = state[shuffle[3]];
-  const u64x2 v4 = state[shuffle[4]];
-  const u64x2 v5 = state[shuffle[5]];
-  const u64x2 v6 = state[shuffle[6]];
-  const u64x2 v7 = state[shuffle[7]];
-  const u64x2 w0 = state[shuffle[8]];
-  const u64x2 w1 = state[shuffle[9]];
-  const u64x2 w2 = state[shuffle[10]];
-  const u64x2 w3 = state[shuffle[11]];
-  const u64x2 w4 = state[shuffle[12]];
-  const u64x2 w5 = state[shuffle[13]];
-  const u64x2 w6 = state[shuffle[14]];
-  const u64x2 w7 = state[shuffle[15]];
-  state[0] = v0;
-  state[1] = v1;
-  state[2] = v2;
-  state[3] = v3;
-  state[4] = v4;
-  state[5] = v5;
-  state[6] = v6;
-  state[7] = v7;
-  state[8] = w0;
-  state[9] = w1;
-  state[10] = w2;
-  state[11] = w3;
-  state[12] = w4;
-  state[13] = w5;
-  state[14] = w6;
-  state[15] = w7;
-}
-
-// Feistel round function using two AES subrounds. Very similar to F()
-// from Simpira v2, but with independent subround keys. Uses 17 AES rounds
-// per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
-// parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
-// XORs are 'free' (included in the second AES instruction).
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const u64x2* FeistelRound(
-    u64x2* ABSL_RANDOM_INTERNAL_RESTRICT state,
-    const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
-  for (size_t branch = 0; branch < RandenTraits::kFeistelBlocks; branch += 4) {
-    const Vector128 s0 = Vector128Load(state + branch);
-    const Vector128 s1 = Vector128Load(state + branch + 1);
-    const Vector128 f0 = AesRound(s0, Vector128Load(keys));
-    keys++;
-    const Vector128 o1 = AesRound(f0, s1);
-    Vector128Store(o1, state + branch + 1);
-
-    // Manually unroll this loop once. about 10% better than not unrolled.
-    const Vector128 s2 = Vector128Load(state + branch + 2);
-    const Vector128 s3 = Vector128Load(state + branch + 3);
-    const Vector128 f2 = AesRound(s2, Vector128Load(keys));
-    keys++;
-    const Vector128 o3 = AesRound(f2, s3);
-    Vector128Store(o3, state + branch + 3);
-  }
-  return keys;
-}
-
-// Cryptographic permutation based via type-2 Generalized Feistel Network.
-// Indistinguishable from ideal by chosen-ciphertext adversaries using less than
-// 2^64 queries if the round function is a PRF. This is similar to the b=8 case
-// of Simpira v2, but more efficient than its generic construction for b=16.
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute(
-    u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
-  for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) {
-    keys = FeistelRound(state, keys);
-    BlockShuffle(state);
-  }
-}
-
-}  // namespace
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-const void* RandenSlow::GetKeys() {
-  // Round keys for one AES per Feistel round and branch.
-  // The canonical implementation uses first digits of Pi.
-  return kRandenRoundKeys;
-}
-
-void RandenSlow::Absorb(const void* seed_void, void* state_void) {
-  auto* state =
-      reinterpret_cast<uint64_t * ABSL_RANDOM_INTERNAL_RESTRICT>(state_void);
-  const auto* seed =
-      reinterpret_cast<const uint64_t * ABSL_RANDOM_INTERNAL_RESTRICT>(
-          seed_void);
-
-  constexpr size_t kCapacityBlocks =
-      RandenTraits::kCapacityBytes / sizeof(uint64_t);
-  static_assert(
-      kCapacityBlocks * sizeof(uint64_t) == RandenTraits::kCapacityBytes,
-      "Not i*V");
-
-  for (size_t i = kCapacityBlocks;
-       i < RandenTraits::kStateBytes / sizeof(uint64_t); ++i) {
-    state[i] ^= seed[i - kCapacityBlocks];
-  }
-}
-
-void RandenSlow::Generate(const void* keys_void, void* state_void) {
-  static_assert(RandenTraits::kCapacityBytes == sizeof(u64x2),
-                "Capacity mismatch");
-
-  auto* state = reinterpret_cast<u64x2*>(state_void);
-  const auto* keys = reinterpret_cast<const u64x2*>(keys_void);
-
-  const u64x2 prev_inner = state[0];
-
-  Permute(state, keys);
-
-  // Ensure backtracking resistance.
-  state[0].data[0] ^= prev_inner.data[0];
-  state[0].data[1] ^= prev_inner.data[1];
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_slow.h b/third_party/abseil_cpp/absl/random/internal/randen_slow.h
deleted file mode 100644
index b6f137eb94..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_slow.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_
-#define ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_
-
-#include <cstddef>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// RANDen = RANDom generator or beetroots in Swiss German.
-// RandenSlow implements the basic state manipulation methods for
-// architectures lacking AES hardware acceleration intrinsics.
-class RandenSlow {
- public:
-  static void Generate(const void* keys, void* state_void);
-  static void Absorb(const void* seed_void, void* state_void);
-  static const void* GetKeys();
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_slow_test.cc b/third_party/abseil_cpp/absl/random/internal/randen_slow_test.cc
deleted file mode 100644
index 4a53583705..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_slow_test.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/randen_slow.h"
-
-#include <cstring>
-
-#include "gtest/gtest.h"
-#include "absl/random/internal/randen_traits.h"
-
-namespace {
-
-using absl::random_internal::RandenSlow;
-using absl::random_internal::RandenTraits;
-
-// Local state parameters.
-constexpr size_t kSeedBytes =
-    RandenTraits::kStateBytes - RandenTraits::kCapacityBytes;
-constexpr size_t kStateSizeT = RandenTraits::kStateBytes / sizeof(uint64_t);
-constexpr size_t kSeedSizeT = kSeedBytes / sizeof(uint32_t);
-
-struct alignas(16) randen {
-  uint64_t state[kStateSizeT];
-  uint32_t seed[kSeedSizeT];
-};
-
-TEST(RandenSlowTest, Default) {
-  constexpr uint64_t kGolden[] = {
-      0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977,
-      0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912,
-      0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6,
-      0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a,
-      0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37,
-      0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556,
-      0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42,
-      0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc,
-      0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f,
-      0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3,
-      0x026ff374c101da7e, 0x811ef0821c3de851,
-  };
-
-  alignas(16) randen d;
-  std::memset(d.state, 0, sizeof(d.state));
-  RandenSlow::Generate(RandenSlow::GetKeys(), d.state);
-
-  uint64_t* id = d.state;
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, *id++);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_test.cc b/third_party/abseil_cpp/absl/random/internal/randen_test.cc
deleted file mode 100644
index c186fe0d68..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_test.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/randen.h"
-
-#include <cstring>
-
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-
-namespace {
-
-using absl::random_internal::Randen;
-
-// Local state parameters.
-constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t);
-
-TEST(RandenTest, CopyAndMove) {
-  static_assert(std::is_copy_constructible<Randen>::value,
-                "Randen must be copy constructible");
-
-  static_assert(absl::is_copy_assignable<Randen>::value,
-                "Randen must be copy assignable");
-
-  static_assert(std::is_move_constructible<Randen>::value,
-                "Randen must be move constructible");
-
-  static_assert(absl::is_move_assignable<Randen>::value,
-                "Randen must be move assignable");
-}
-
-TEST(RandenTest, Default) {
-  constexpr uint64_t kGolden[] = {
-      0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977,
-      0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912,
-      0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6,
-      0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a,
-      0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37,
-      0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556,
-      0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42,
-      0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc,
-      0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f,
-      0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3,
-      0x026ff374c101da7e, 0x811ef0821c3de851,
-  };
-
-  alignas(16) uint64_t state[kStateSizeT];
-  std::memset(state, 0, sizeof(state));
-
-  Randen r;
-  r.Generate(state);
-
-  auto id = std::begin(state);
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, *id++);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/randen_traits.h b/third_party/abseil_cpp/absl/random/internal/randen_traits.h
deleted file mode 100644
index 53caa93614..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/randen_traits.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_
-#define ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_
-
-// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
-// symbols from arbitrary system and other headers, since it may be built
-// with different flags from other targets, using different levels of
-// optimization, potentially introducing ODR violations.
-
-#include <cstddef>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// RANDen = RANDom generator or beetroots in Swiss German.
-// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
-// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
-//
-// High-level summary:
-// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is
-//    a sponge-like random generator that requires a cryptographic permutation.
-//    It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by
-//    achieving backtracking resistance with only one Permute() per buffer.
-//
-// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round
-//    Function" constructs up to 1024-bit permutations using an improved
-//    Generalized Feistel network with 2-round AES-128 functions. This Feistel
-//    block shuffle achieves diffusion faster and is less vulnerable to
-//    sliced-biclique attacks than the Type-2 cyclic shuffle.
-//
-// 3) "Improving the Generalized Feistel" and "New criterion for diffusion
-//    property" extends the same kind of improved Feistel block shuffle to 16
-//    branches, which enables a 2048-bit permutation.
-//
-// Combine these three ideas and also change Simpira's subround keys from
-// structured/low-entropy counters to digits of Pi (or other random source).
-
-// RandenTraits contains the basic algorithm traits, such as the size of the
-// state, seed, sponge, etc.
-struct RandenTraits {
-  // Size of the entire sponge / state for the randen PRNG.
-  static constexpr size_t kStateBytes = 256;  // 2048-bit
-
-  // Size of the 'inner' (inaccessible) part of the sponge. Larger values would
-  // require more frequent calls to RandenGenerate.
-  static constexpr size_t kCapacityBytes = 16;  // 128-bit
-
-  // Size of the default seed consumed by the sponge.
-  static constexpr size_t kSeedBytes = kStateBytes - kCapacityBytes;
-
-  // Assuming 128-bit blocks, the number of blocks in the state.
-  // Largest size for which security proofs are known.
-  static constexpr size_t kFeistelBlocks = 16;
-
-  // Ensures SPRP security and two full subblock diffusions.
-  // Must be > 4 * log2(kFeistelBlocks).
-  static constexpr size_t kFeistelRounds = 16 + 1;
-
-  // Size of the key. A 128-bit key block is used for every-other
-  // feistel block (Type-2 generalized Feistel network) in each round.
-  static constexpr size_t kKeyBytes = 16 * kFeistelRounds * kFeistelBlocks / 2;
-};
-
-// Randen key arrays. In randen_round_keys.cc
-extern const unsigned char kRandenRoundKeys[RandenTraits::kKeyBytes];
-extern const unsigned char kRandenRoundKeysBE[RandenTraits::kKeyBytes];
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/salted_seed_seq.h b/third_party/abseil_cpp/absl/random/internal/salted_seed_seq.h
deleted file mode 100644
index 5953a090f8..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/salted_seed_seq.h
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_
-#define ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_
-
-#include <cstdint>
-#include <cstdlib>
-#include <initializer_list>
-#include <iterator>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-#include "absl/container/inlined_vector.h"
-#include "absl/meta/type_traits.h"
-#include "absl/random/internal/seed_material.h"
-#include "absl/types/optional.h"
-#include "absl/types/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// This class conforms to the C++ Standard "Seed Sequence" concept
-// [rand.req.seedseq].
-//
-// A `SaltedSeedSeq` is meant to wrap an existing seed sequence and modify
-// generated sequence by mixing with extra entropy. This entropy may be
-// build-dependent or process-dependent. The implementation may change to be
-// have either or both kinds of entropy. If salt is not available sequence is
-// not modified.
-template <typename SSeq>
-class SaltedSeedSeq {
- public:
-  using inner_sequence_type = SSeq;
-  using result_type = typename SSeq::result_type;
-
-  SaltedSeedSeq() : seq_(absl::make_unique<SSeq>()) {}
-
-  template <typename Iterator>
-  SaltedSeedSeq(Iterator begin, Iterator end)
-      : seq_(absl::make_unique<SSeq>(begin, end)) {}
-
-  template <typename T>
-  SaltedSeedSeq(std::initializer_list<T> il)
-      : SaltedSeedSeq(il.begin(), il.end()) {}
-
-  SaltedSeedSeq(const SaltedSeedSeq&) = delete;
-  SaltedSeedSeq& operator=(const SaltedSeedSeq&) = delete;
-
-  SaltedSeedSeq(SaltedSeedSeq&&) = default;
-  SaltedSeedSeq& operator=(SaltedSeedSeq&&) = default;
-
-  template <typename RandomAccessIterator>
-  void generate(RandomAccessIterator begin, RandomAccessIterator end) {
-    // The common case is that generate is called with ContiguousIterators
-    // to uint arrays. Such contiguous memory regions may be optimized,
-    // which we detect here.
-    using tag = absl::conditional_t<
-        (std::is_pointer<RandomAccessIterator>::value &&
-         std::is_same<absl::decay_t<decltype(*begin)>, uint32_t>::value),
-        ContiguousAndUint32Tag, DefaultTag>;
-    if (begin != end) {
-      generate_impl(begin, end, tag{});
-    }
-  }
-
-  template <typename OutIterator>
-  void param(OutIterator out) const {
-    seq_->param(out);
-  }
-
-  size_t size() const { return seq_->size(); }
-
- private:
-  struct ContiguousAndUint32Tag {};
-  struct DefaultTag {};
-
-  // Generate which requires the iterators are contiguous pointers to uint32_t.
-  void generate_impl(uint32_t* begin, uint32_t* end, ContiguousAndUint32Tag) {
-    generate_contiguous(absl::MakeSpan(begin, end));
-  }
-
-  // The uncommon case for generate is that it is called with iterators over
-  // some other buffer type which is assignable from a 32-bit value. In this
-  // case we allocate a temporary 32-bit buffer and then copy-assign back
-  // to the initial inputs.
-  template <typename RandomAccessIterator>
-  void generate_impl(RandomAccessIterator begin, RandomAccessIterator end,
-                     DefaultTag) {
-    return generate_and_copy(std::distance(begin, end), begin);
-  }
-
-  // Fills the initial seed buffer the underlying SSeq::generate() call,
-  // mixing in the salt material.
-  void generate_contiguous(absl::Span<uint32_t> buffer) {
-    seq_->generate(buffer.begin(), buffer.end());
-    const uint32_t salt = absl::random_internal::GetSaltMaterial().value_or(0);
-    MixIntoSeedMaterial(absl::MakeConstSpan(&salt, 1), buffer);
-  }
-
-  // Allocates a seed buffer of `n` elements, generates the seed, then
-  // copies the result into the `out` iterator.
-  template <typename Iterator>
-  void generate_and_copy(size_t n, Iterator out) {
-    // Allocate a temporary buffer, generate, and then copy.
-    absl::InlinedVector<uint32_t, 8> data(n, 0);
-    generate_contiguous(absl::MakeSpan(data.data(), data.size()));
-    std::copy(data.begin(), data.end(), out);
-  }
-
-  // Because [rand.req.seedseq] is not required to be copy-constructible,
-  // copy-assignable nor movable, we wrap it with unique pointer to be able
-  // to move SaltedSeedSeq.
-  std::unique_ptr<SSeq> seq_;
-};
-
-// is_salted_seed_seq indicates whether the type is a SaltedSeedSeq.
-template <typename T, typename = void>
-struct is_salted_seed_seq : public std::false_type {};
-
-template <typename T>
-struct is_salted_seed_seq<
-    T, typename std::enable_if<std::is_same<
-           T, SaltedSeedSeq<typename T::inner_sequence_type>>::value>::type>
-    : public std::true_type {};
-
-// MakeSaltedSeedSeq returns a salted variant of the seed sequence.
-// When provided with an existing SaltedSeedSeq, returns the input parameter,
-// otherwise constructs a new SaltedSeedSeq which embodies the original
-// non-salted seed parameters.
-template <
-    typename SSeq,  //
-    typename EnableIf = absl::enable_if_t<is_salted_seed_seq<SSeq>::value>>
-SSeq MakeSaltedSeedSeq(SSeq&& seq) {
-  return SSeq(std::forward<SSeq>(seq));
-}
-
-template <
-    typename SSeq,  //
-    typename EnableIf = absl::enable_if_t<!is_salted_seed_seq<SSeq>::value>>
-SaltedSeedSeq<typename std::decay<SSeq>::type> MakeSaltedSeedSeq(SSeq&& seq) {
-  using sseq_type = typename std::decay<SSeq>::type;
-  using result_type = typename sseq_type::result_type;
-
-  absl::InlinedVector<result_type, 8> data;
-  seq.param(std::back_inserter(data));
-  return SaltedSeedSeq<sseq_type>(data.begin(), data.end());
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/salted_seed_seq_test.cc b/third_party/abseil_cpp/absl/random/internal/salted_seed_seq_test.cc
deleted file mode 100644
index 0bf19a63ef..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/salted_seed_seq_test.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/salted_seed_seq.h"
-
-#include <iterator>
-#include <random>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-using absl::random_internal::GetSaltMaterial;
-using absl::random_internal::MakeSaltedSeedSeq;
-using absl::random_internal::SaltedSeedSeq;
-using testing::Eq;
-using testing::Pointwise;
-
-namespace {
-
-template <typename Sseq>
-void ConformsToInterface() {
-  // Check that the SeedSequence can be default-constructed.
-  { Sseq default_constructed_seq; }
-  // Check that the SeedSequence can be constructed with two iterators.
-  {
-    uint32_t init_array[] = {1, 3, 5, 7, 9};
-    Sseq iterator_constructed_seq(std::begin(init_array), std::end(init_array));
-  }
-  // Check that the SeedSequence can be std::initializer_list-constructed.
-  { Sseq list_constructed_seq = {1, 3, 5, 7, 9, 11, 13}; }
-  // Check that param() and size() return state provided to constructor.
-  {
-    uint32_t init_array[] = {1, 2, 3, 4, 5};
-    Sseq seq(std::begin(init_array), std::end(init_array));
-    EXPECT_EQ(seq.size(), ABSL_ARRAYSIZE(init_array));
-
-    std::vector<uint32_t> state_vector;
-    seq.param(std::back_inserter(state_vector));
-
-    EXPECT_EQ(state_vector.size(), ABSL_ARRAYSIZE(init_array));
-    for (int i = 0; i < state_vector.size(); i++) {
-      EXPECT_EQ(state_vector[i], i + 1);
-    }
-  }
-  // Check for presence of generate() method.
-  {
-    Sseq seq;
-    uint32_t seeds[5];
-
-    seq.generate(std::begin(seeds), std::end(seeds));
-  }
-}
-
-TEST(SaltedSeedSeq, CheckInterfaces) {
-  // Control case
-  ConformsToInterface<std::seed_seq>();
-
-  // Abseil classes
-  ConformsToInterface<SaltedSeedSeq<std::seed_seq>>();
-}
-
-TEST(SaltedSeedSeq, CheckConstructingFromOtherSequence) {
-  std::vector<uint32_t> seed_values(10, 1);
-  std::seed_seq seq(seed_values.begin(), seed_values.end());
-  auto salted_seq = MakeSaltedSeedSeq(std::move(seq));
-
-  EXPECT_EQ(seq.size(), salted_seq.size());
-
-  std::vector<uint32_t> param_result;
-  seq.param(std::back_inserter(param_result));
-
-  EXPECT_EQ(seed_values, param_result);
-}
-
-TEST(SaltedSeedSeq, SaltedSaltedSeedSeqIsNotDoubleSalted) {
-  uint32_t init[] = {1, 3, 5, 7, 9};
-
-  std::seed_seq seq(std::begin(init), std::end(init));
-
-  // The first salting.
-  SaltedSeedSeq<std::seed_seq> salted_seq = MakeSaltedSeedSeq(std::move(seq));
-  uint32_t a[16];
-  salted_seq.generate(std::begin(a), std::end(a));
-
-  // The second salting.
-  SaltedSeedSeq<std::seed_seq> salted_salted_seq =
-      MakeSaltedSeedSeq(std::move(salted_seq));
-  uint32_t b[16];
-  salted_salted_seq.generate(std::begin(b), std::end(b));
-
-  // ... both should be equal.
-  EXPECT_THAT(b, Pointwise(Eq(), a)) << "a[0] " << a[0];
-}
-
-TEST(SaltedSeedSeq, SeedMaterialIsSalted) {
-  const size_t kNumBlocks = 16;
-
-  uint32_t seed_material[kNumBlocks];
-  std::random_device urandom{"/dev/urandom"};
-  for (uint32_t& seed : seed_material) {
-    seed = urandom();
-  }
-
-  std::seed_seq seq(std::begin(seed_material), std::end(seed_material));
-  SaltedSeedSeq<std::seed_seq> salted_seq(std::begin(seed_material),
-                                          std::end(seed_material));
-
-  bool salt_is_available = GetSaltMaterial().has_value();
-
-  // If salt is available generated sequence should be different.
-  if (salt_is_available) {
-    uint32_t outputs[kNumBlocks];
-    uint32_t salted_outputs[kNumBlocks];
-
-    seq.generate(std::begin(outputs), std::end(outputs));
-    salted_seq.generate(std::begin(salted_outputs), std::end(salted_outputs));
-
-    EXPECT_THAT(outputs, Pointwise(testing::Ne(), salted_outputs));
-  }
-}
-
-TEST(SaltedSeedSeq, GenerateAcceptsDifferentTypes) {
-  const size_t kNumBlocks = 4;
-
-  SaltedSeedSeq<std::seed_seq> seq({1, 2, 3});
-
-  uint32_t expected[kNumBlocks];
-  seq.generate(std::begin(expected), std::end(expected));
-
-  // 32-bit outputs
-  {
-    unsigned long seed_material[kNumBlocks];  // NOLINT(runtime/int)
-    seq.generate(std::begin(seed_material), std::end(seed_material));
-    EXPECT_THAT(seed_material, Pointwise(Eq(), expected));
-  }
-  {
-    unsigned int seed_material[kNumBlocks];  // NOLINT(runtime/int)
-    seq.generate(std::begin(seed_material), std::end(seed_material));
-    EXPECT_THAT(seed_material, Pointwise(Eq(), expected));
-  }
-
-  // 64-bit outputs.
-  {
-    uint64_t seed_material[kNumBlocks];
-    seq.generate(std::begin(seed_material), std::end(seed_material));
-    EXPECT_THAT(seed_material, Pointwise(Eq(), expected));
-  }
-  {
-    int64_t seed_material[kNumBlocks];
-    seq.generate(std::begin(seed_material), std::end(seed_material));
-    EXPECT_THAT(seed_material, Pointwise(Eq(), expected));
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/seed_material.cc b/third_party/abseil_cpp/absl/random/internal/seed_material.cc
deleted file mode 100644
index 4d38a57419..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/seed_material.cc
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/seed_material.h"
-
-#include <fcntl.h>
-
-#ifndef _WIN32
-#include <unistd.h>
-#else
-#include <io.h>
-#endif
-
-#include <algorithm>
-#include <cerrno>
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/escaping.h"
-#include "absl/strings/string_view.h"
-#include "absl/strings/strip.h"
-
-#if defined(__native_client__)
-
-#include <nacl/nacl_random.h>
-#define ABSL_RANDOM_USE_NACL_SECURE_RANDOM 1
-
-#elif defined(_WIN32)
-
-#include <windows.h>
-#define ABSL_RANDOM_USE_BCRYPT 1
-#pragma comment(lib, "bcrypt.lib")
-
-#elif defined(__Fuchsia__)
-#include <zircon/syscalls.h>
-
-#endif
-
-#if defined(ABSL_RANDOM_USE_BCRYPT)
-#include <bcrypt.h>
-
-#ifndef BCRYPT_SUCCESS
-#define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
-#endif
-// Also link bcrypt; this can be done via linker options or:
-// #pragma comment(lib, "bcrypt.lib")
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-namespace {
-
-// Read OS Entropy for random number seeds.
-// TODO(absl-team): Possibly place a cap on how much entropy may be read at a
-// time.
-
-#if defined(ABSL_RANDOM_USE_BCRYPT)
-
-// On Windows potentially use the BCRYPT CNG API to read available entropy.
-bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
-  BCRYPT_ALG_HANDLE hProvider;
-  NTSTATUS ret;
-  ret = BCryptOpenAlgorithmProvider(&hProvider, BCRYPT_RNG_ALGORITHM,
-                                    MS_PRIMITIVE_PROVIDER, 0);
-  if (!(BCRYPT_SUCCESS(ret))) {
-    ABSL_RAW_LOG(ERROR, "Failed to open crypto provider.");
-    return false;
-  }
-  ret = BCryptGenRandom(
-      hProvider,                                             // provider
-      reinterpret_cast<UCHAR*>(values.data()),               // buffer
-      static_cast<ULONG>(sizeof(uint32_t) * values.size()),  // bytes
-      0);                                                    // flags
-  BCryptCloseAlgorithmProvider(hProvider, 0);
-  return BCRYPT_SUCCESS(ret);
-}
-
-#elif defined(ABSL_RANDOM_USE_NACL_SECURE_RANDOM)
-
-// On NaCL use nacl_secure_random to acquire bytes.
-bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
-  auto buffer = reinterpret_cast<uint8_t*>(values.data());
-  size_t buffer_size = sizeof(uint32_t) * values.size();
-
-  uint8_t* output_ptr = buffer;
-  while (buffer_size > 0) {
-    size_t nread = 0;
-    const int error = nacl_secure_random(output_ptr, buffer_size, &nread);
-    if (error != 0 || nread > buffer_size) {
-      ABSL_RAW_LOG(ERROR, "Failed to read secure_random seed data: %d", error);
-      return false;
-    }
-    output_ptr += nread;
-    buffer_size -= nread;
-  }
-  return true;
-}
-
-#elif defined(__Fuchsia__)
-
-bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
-  auto buffer = reinterpret_cast<uint8_t*>(values.data());
-  size_t buffer_size = sizeof(uint32_t) * values.size();
-  zx_cprng_draw(buffer, buffer_size);
-  return true;
-}
-
-#else
-
-// On *nix, read entropy from /dev/urandom.
-bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
-  const char kEntropyFile[] = "/dev/urandom";
-
-  auto buffer = reinterpret_cast<uint8_t*>(values.data());
-  size_t buffer_size = sizeof(uint32_t) * values.size();
-
-  int dev_urandom = open(kEntropyFile, O_RDONLY);
-  bool success = (-1 != dev_urandom);
-  if (!success) {
-    return false;
-  }
-
-  while (success && buffer_size > 0) {
-    int bytes_read = read(dev_urandom, buffer, buffer_size);
-    int read_error = errno;
-    success = (bytes_read > 0);
-    if (success) {
-      buffer += bytes_read;
-      buffer_size -= bytes_read;
-    } else if (bytes_read == -1 && read_error == EINTR) {
-      success = true;  // Need to try again.
-    }
-  }
-  close(dev_urandom);
-  return success;
-}
-
-#endif
-
-}  // namespace
-
-bool ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values) {
-  assert(values.data() != nullptr);
-  if (values.data() == nullptr) {
-    return false;
-  }
-  if (values.empty()) {
-    return true;
-  }
-  return ReadSeedMaterialFromOSEntropyImpl(values);
-}
-
-void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence,
-                         absl::Span<uint32_t> seed_material) {
-  // Algorithm is based on code available at
-  // https://gist.github.com/imneme/540829265469e673d045
-  constexpr uint32_t kInitVal = 0x43b0d7e5;
-  constexpr uint32_t kHashMul = 0x931e8875;
-  constexpr uint32_t kMixMulL = 0xca01f9dd;
-  constexpr uint32_t kMixMulR = 0x4973f715;
-  constexpr uint32_t kShiftSize = sizeof(uint32_t) * 8 / 2;
-
-  uint32_t hash_const = kInitVal;
-  auto hash = [&](uint32_t value) {
-    value ^= hash_const;
-    hash_const *= kHashMul;
-    value *= hash_const;
-    value ^= value >> kShiftSize;
-    return value;
-  };
-
-  auto mix = [&](uint32_t x, uint32_t y) {
-    uint32_t result = kMixMulL * x - kMixMulR * y;
-    result ^= result >> kShiftSize;
-    return result;
-  };
-
-  for (const auto& seq_val : sequence) {
-    for (auto& elem : seed_material) {
-      elem = mix(elem, hash(seq_val));
-    }
-  }
-}
-
-absl::optional<uint32_t> GetSaltMaterial() {
-  // Salt must be common for all generators within the same process so read it
-  // only once and store in static variable.
-  static const auto salt_material = []() -> absl::optional<uint32_t> {
-    uint32_t salt_value = 0;
-
-    if (random_internal::ReadSeedMaterialFromOSEntropy(
-            MakeSpan(&salt_value, 1))) {
-      return salt_value;
-    }
-
-    return absl::nullopt;
-  }();
-
-  return salt_material;
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/internal/seed_material.h b/third_party/abseil_cpp/absl/random/internal/seed_material.h
deleted file mode 100644
index 4be10e9256..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/seed_material.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_
-#define ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_
-
-#include <cassert>
-#include <cstdint>
-#include <cstdlib>
-#include <string>
-#include <vector>
-
-#include "absl/base/attributes.h"
-#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/types/optional.h"
-#include "absl/types/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// Returns the number of 32-bit blocks needed to contain the given number of
-// bits.
-constexpr size_t SeedBitsToBlocks(size_t seed_size) {
-  return (seed_size + 31) / 32;
-}
-
-// Amount of entropy (measured in bits) used to instantiate a Seed Sequence,
-// with which to create a URBG.
-constexpr size_t kEntropyBitsNeeded = 256;
-
-// Amount of entropy (measured in 32-bit blocks) used to instantiate a Seed
-// Sequence, with which to create a URBG.
-constexpr size_t kEntropyBlocksNeeded =
-    random_internal::SeedBitsToBlocks(kEntropyBitsNeeded);
-
-static_assert(kEntropyBlocksNeeded > 0,
-              "Entropy used to seed URBGs must be nonzero.");
-
-// Attempts to fill a span of uint32_t-values using an OS-provided source of
-// true entropy (eg. /dev/urandom) into an array of uint32_t blocks of data. The
-// resulting array may be used to initialize an instance of a class conforming
-// to the C++ Standard "Seed Sequence" concept [rand.req.seedseq].
-//
-// If values.data() == nullptr, the behavior is undefined.
-ABSL_MUST_USE_RESULT
-bool ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values);
-
-// Attempts to fill a span of uint32_t-values using variates generated by an
-// existing instance of a class conforming to the C++ Standard "Uniform Random
-// Bit Generator" concept [rand.req.urng]. The resulting data may be used to
-// initialize an instance of a class conforming to the C++ Standard
-// "Seed Sequence" concept [rand.req.seedseq].
-//
-// If urbg == nullptr or values.data() == nullptr, the behavior is undefined.
-template <typename URBG>
-ABSL_MUST_USE_RESULT bool ReadSeedMaterialFromURBG(
-    URBG* urbg, absl::Span<uint32_t> values) {
-  random_internal::FastUniformBits<uint32_t> distr;
-
-  assert(urbg != nullptr && values.data() != nullptr);
-  if (urbg == nullptr || values.data() == nullptr) {
-    return false;
-  }
-
-  for (uint32_t& seed_value : values) {
-    seed_value = distr(*urbg);
-  }
-  return true;
-}
-
-// Mixes given sequence of values with into given sequence of seed material.
-// Time complexity of this function is O(sequence.size() *
-// seed_material.size()).
-//
-// Algorithm is based on code available at
-// https://gist.github.com/imneme/540829265469e673d045
-// by Melissa O'Neill.
-void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence,
-                         absl::Span<uint32_t> seed_material);
-
-// Returns salt value.
-//
-// Salt is obtained only once and stored in static variable.
-//
-// May return empty value if optaining the salt was not possible.
-absl::optional<uint32_t> GetSaltMaterial();
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/seed_material_test.cc b/third_party/abseil_cpp/absl/random/internal/seed_material_test.cc
deleted file mode 100644
index 6db2820ec7..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/seed_material_test.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/seed_material.h"
-
-#include <bitset>
-#include <cstdlib>
-#include <cstring>
-#include <random>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-#ifdef __ANDROID__
-// Android assert messages only go to system log, so death tests cannot inspect
-// the message for matching.
-#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
-  EXPECT_DEATH_IF_SUPPORTED(statement, ".*")
-#else
-#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
-  EXPECT_DEATH_IF_SUPPORTED(statement, regex)
-#endif
-
-namespace {
-
-using testing::Each;
-using testing::ElementsAre;
-using testing::Eq;
-using testing::Ne;
-using testing::Pointwise;
-
-TEST(SeedBitsToBlocks, VerifyCases) {
-  EXPECT_EQ(0, absl::random_internal::SeedBitsToBlocks(0));
-  EXPECT_EQ(1, absl::random_internal::SeedBitsToBlocks(1));
-  EXPECT_EQ(1, absl::random_internal::SeedBitsToBlocks(31));
-  EXPECT_EQ(1, absl::random_internal::SeedBitsToBlocks(32));
-  EXPECT_EQ(2, absl::random_internal::SeedBitsToBlocks(33));
-  EXPECT_EQ(4, absl::random_internal::SeedBitsToBlocks(127));
-  EXPECT_EQ(4, absl::random_internal::SeedBitsToBlocks(128));
-  EXPECT_EQ(5, absl::random_internal::SeedBitsToBlocks(129));
-}
-
-TEST(ReadSeedMaterialFromOSEntropy, SuccessiveReadsAreDistinct) {
-  constexpr size_t kSeedMaterialSize = 64;
-  uint32_t seed_material_1[kSeedMaterialSize] = {};
-  uint32_t seed_material_2[kSeedMaterialSize] = {};
-
-  EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromOSEntropy(
-      absl::Span<uint32_t>(seed_material_1, kSeedMaterialSize)));
-  EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromOSEntropy(
-      absl::Span<uint32_t>(seed_material_2, kSeedMaterialSize)));
-
-  EXPECT_THAT(seed_material_1, Pointwise(Ne(), seed_material_2));
-}
-
-TEST(ReadSeedMaterialFromOSEntropy, ReadZeroBytesIsNoOp) {
-  uint32_t seed_material[32] = {};
-  std::memset(seed_material, 0xAA, sizeof(seed_material));
-  EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromOSEntropy(
-      absl::Span<uint32_t>(seed_material, 0)));
-
-  EXPECT_THAT(seed_material, Each(Eq(0xAAAAAAAA)));
-}
-
-TEST(ReadSeedMaterialFromOSEntropy, NullPtrVectorArgument) {
-#ifdef NDEBUG
-  EXPECT_FALSE(absl::random_internal::ReadSeedMaterialFromOSEntropy(
-      absl::Span<uint32_t>(nullptr, 32)));
-#else
-  bool result;
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(
-      result = absl::random_internal::ReadSeedMaterialFromOSEntropy(
-          absl::Span<uint32_t>(nullptr, 32)),
-      "!= nullptr");
-  (void)result;  // suppress unused-variable warning
-#endif
-}
-
-TEST(ReadSeedMaterialFromURBG, SeedMaterialEqualsVariateSequence) {
-  // Two default-constructed instances of std::mt19937_64 are guaranteed to
-  // produce equal variate-sequences.
-  std::mt19937 urbg_1;
-  std::mt19937 urbg_2;
-  constexpr size_t kSeedMaterialSize = 1024;
-  uint32_t seed_material[kSeedMaterialSize] = {};
-
-  EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromURBG(
-      &urbg_1, absl::Span<uint32_t>(seed_material, kSeedMaterialSize)));
-  for (uint32_t seed : seed_material) {
-    EXPECT_EQ(seed, urbg_2());
-  }
-}
-
-TEST(ReadSeedMaterialFromURBG, ReadZeroBytesIsNoOp) {
-  std::mt19937_64 urbg;
-  uint32_t seed_material[32];
-  std::memset(seed_material, 0xAA, sizeof(seed_material));
-  EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromURBG(
-      &urbg, absl::Span<uint32_t>(seed_material, 0)));
-
-  EXPECT_THAT(seed_material, Each(Eq(0xAAAAAAAA)));
-}
-
-TEST(ReadSeedMaterialFromURBG, NullUrbgArgument) {
-  constexpr size_t kSeedMaterialSize = 32;
-  uint32_t seed_material[kSeedMaterialSize];
-#ifdef NDEBUG
-  EXPECT_FALSE(absl::random_internal::ReadSeedMaterialFromURBG<std::mt19937_64>(
-      nullptr, absl::Span<uint32_t>(seed_material, kSeedMaterialSize)));
-#else
-  bool result;
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(
-      result = absl::random_internal::ReadSeedMaterialFromURBG<std::mt19937_64>(
-          nullptr, absl::Span<uint32_t>(seed_material, kSeedMaterialSize)),
-      "!= nullptr");
-  (void)result;  // suppress unused-variable warning
-#endif
-}
-
-TEST(ReadSeedMaterialFromURBG, NullPtrVectorArgument) {
-  std::mt19937_64 urbg;
-#ifdef NDEBUG
-  EXPECT_FALSE(absl::random_internal::ReadSeedMaterialFromURBG(
-      &urbg, absl::Span<uint32_t>(nullptr, 32)));
-#else
-  bool result;
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(
-      result = absl::random_internal::ReadSeedMaterialFromURBG(
-          &urbg, absl::Span<uint32_t>(nullptr, 32)),
-      "!= nullptr");
-  (void)result;  // suppress unused-variable warning
-#endif
-}
-
-// The avalanche effect is a desirable cryptographic property of hashes in which
-// changing a single bit in the input causes each bit of the output to be
-// changed with probability near 50%.
-//
-// https://en.wikipedia.org/wiki/Avalanche_effect
-
-TEST(MixSequenceIntoSeedMaterial, AvalancheEffectTestOneBitLong) {
-  std::vector<uint32_t> seed_material = {1, 2, 3, 4, 5, 6, 7, 8};
-
-  // For every 32-bit number with exactly one bit set, verify the avalanche
-  // effect holds.  In order to reduce flakiness of tests, accept values
-  // anywhere in the range of 30%-70%.
-  for (uint32_t v = 1; v != 0; v <<= 1) {
-    std::vector<uint32_t> seed_material_copy = seed_material;
-    absl::random_internal::MixIntoSeedMaterial(
-        absl::Span<uint32_t>(&v, 1),
-        absl::Span<uint32_t>(seed_material_copy.data(),
-                             seed_material_copy.size()));
-
-    uint32_t changed_bits = 0;
-    for (size_t i = 0; i < seed_material.size(); i++) {
-      std::bitset<sizeof(uint32_t) * 8> bitset(seed_material[i] ^
-                                               seed_material_copy[i]);
-      changed_bits += bitset.count();
-    }
-
-    EXPECT_LE(changed_bits, 0.7 * sizeof(uint32_t) * 8 * seed_material.size());
-    EXPECT_GE(changed_bits, 0.3 * sizeof(uint32_t) * 8 * seed_material.size());
-  }
-}
-
-TEST(MixSequenceIntoSeedMaterial, AvalancheEffectTestOneBitShort) {
-  std::vector<uint32_t> seed_material = {1};
-
-  // For every 32-bit number with exactly one bit set, verify the avalanche
-  // effect holds.  In order to reduce flakiness of tests, accept values
-  // anywhere in the range of 30%-70%.
-  for (uint32_t v = 1; v != 0; v <<= 1) {
-    std::vector<uint32_t> seed_material_copy = seed_material;
-    absl::random_internal::MixIntoSeedMaterial(
-        absl::Span<uint32_t>(&v, 1),
-        absl::Span<uint32_t>(seed_material_copy.data(),
-                             seed_material_copy.size()));
-
-    uint32_t changed_bits = 0;
-    for (size_t i = 0; i < seed_material.size(); i++) {
-      std::bitset<sizeof(uint32_t) * 8> bitset(seed_material[i] ^
-                                               seed_material_copy[i]);
-      changed_bits += bitset.count();
-    }
-
-    EXPECT_LE(changed_bits, 0.7 * sizeof(uint32_t) * 8 * seed_material.size());
-    EXPECT_GE(changed_bits, 0.3 * sizeof(uint32_t) * 8 * seed_material.size());
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/sequence_urbg.h b/third_party/abseil_cpp/absl/random/internal/sequence_urbg.h
deleted file mode 100644
index bc96a12cd2..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/sequence_urbg.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_
-#define ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_
-
-#include <cstdint>
-#include <cstring>
-#include <limits>
-#include <type_traits>
-#include <vector>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// `sequence_urbg` is a simple random number generator which meets the
-// requirements of [rand.req.urbg], and is solely for testing absl
-// distributions.
-class sequence_urbg {
- public:
-  using result_type = uint64_t;
-
-  static constexpr result_type(min)() {
-    return (std::numeric_limits<result_type>::min)();
-  }
-  static constexpr result_type(max)() {
-    return (std::numeric_limits<result_type>::max)();
-  }
-
-  sequence_urbg(std::initializer_list<result_type> data) : i_(0), data_(data) {}
-  void reset() { i_ = 0; }
-
-  result_type operator()() { return data_[i_++ % data_.size()]; }
-
-  size_t invocations() const { return i_; }
-
- private:
-  size_t i_;
-  std::vector<result_type> data_;
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/traits.h b/third_party/abseil_cpp/absl/random/internal/traits.h
deleted file mode 100644
index 75772bd9ab..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/traits.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_TRAITS_H_
-#define ABSL_RANDOM_INTERNAL_TRAITS_H_
-
-#include <cstdint>
-#include <limits>
-#include <type_traits>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// random_internal::is_widening_convertible<A, B>
-//
-// Returns whether a type A is widening-convertible to a type B.
-//
-// A is widening-convertible to B means:
-//   A a = <any number>;
-//   B b = a;
-//   A c = b;
-//   EXPECT_EQ(a, c);
-template <typename A, typename B>
-class is_widening_convertible {
-  // As long as there are enough bits in the exact part of a number:
-  // - unsigned can fit in float, signed, unsigned
-  // - signed can fit in float, signed
-  // - float can fit in float
-  // So we define rank to be:
-  // - rank(float) -> 2
-  // - rank(signed) -> 1
-  // - rank(unsigned) -> 0
-  template <class T>
-  static constexpr int rank() {
-    return !std::numeric_limits<T>::is_integer +
-           std::numeric_limits<T>::is_signed;
-  }
-
- public:
-  // If an arithmetic-type B can represent at least as many digits as a type A,
-  // and B belongs to a rank no lower than A, then A can be safely represented
-  // by B through a widening-conversion.
-  static constexpr bool value =
-      std::numeric_limits<A>::digits <= std::numeric_limits<B>::digits &&
-      rank<A>() <= rank<B>();
-};
-
-// unsigned_bits<N>::type returns the unsigned int type with the indicated
-// number of bits.
-template <size_t N>
-struct unsigned_bits;
-
-template <>
-struct unsigned_bits<8> {
-  using type = uint8_t;
-};
-template <>
-struct unsigned_bits<16> {
-  using type = uint16_t;
-};
-template <>
-struct unsigned_bits<32> {
-  using type = uint32_t;
-};
-template <>
-struct unsigned_bits<64> {
-  using type = uint64_t;
-};
-
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-template <>
-struct unsigned_bits<128> {
-  using type = __uint128_t;
-};
-#endif
-
-template <typename IntType>
-struct make_unsigned_bits {
-  using type = typename unsigned_bits<std::numeric_limits<
-      typename std::make_unsigned<IntType>::type>::digits>::type;
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_TRAITS_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/traits_test.cc b/third_party/abseil_cpp/absl/random/internal/traits_test.cc
deleted file mode 100644
index a844887d3e..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/traits_test.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/traits.h"
-
-#include <cstdint>
-#include <type_traits>
-
-#include "gtest/gtest.h"
-
-namespace {
-
-using absl::random_internal::is_widening_convertible;
-
-// CheckWideningConvertsToSelf<T1, T2, ...>()
-//
-// For each type T, checks:
-// - T IS widening-convertible to itself.
-//
-template <typename T>
-void CheckWideningConvertsToSelf() {
-  static_assert(is_widening_convertible<T, T>::value,
-                "Type is not convertible to self!");
-}
-
-template <typename T, typename Next, typename... Args>
-void CheckWideningConvertsToSelf() {
-  CheckWideningConvertsToSelf<T>();
-  CheckWideningConvertsToSelf<Next, Args...>();
-}
-
-// CheckNotWideningConvertibleWithSigned<T1, T2, ...>()
-//
-// For each unsigned-type T, checks that:
-// - T is NOT widening-convertible to Signed(T)
-// - Signed(T) is NOT widening-convertible to T
-//
-template <typename T>
-void CheckNotWideningConvertibleWithSigned() {
-  using signed_t = typename std::make_signed<T>::type;
-
-  static_assert(!is_widening_convertible<T, signed_t>::value,
-                "Unsigned type is convertible to same-sized signed-type!");
-  static_assert(!is_widening_convertible<signed_t, T>::value,
-                "Signed type is convertible to same-sized unsigned-type!");
-}
-
-template <typename T, typename Next, typename... Args>
-void CheckNotWideningConvertibleWithSigned() {
-  CheckNotWideningConvertibleWithSigned<T>();
-  CheckWideningConvertsToSelf<Next, Args...>();
-}
-
-// CheckWideningConvertsToLargerType<T1, T2, ...>()
-//
-// For each successive unsigned-types {Ti, Ti+1}, checks that:
-// - Ti IS widening-convertible to Ti+1
-// - Ti IS widening-convertible to Signed(Ti+1)
-// - Signed(Ti) is NOT widening-convertible to Ti
-// - Signed(Ti) IS widening-convertible to Ti+1
-template <typename T, typename Higher>
-void CheckWideningConvertsToLargerTypes() {
-  using signed_t = typename std::make_signed<T>::type;
-  using higher_t = Higher;
-  using signed_higher_t = typename std::make_signed<Higher>::type;
-
-  static_assert(is_widening_convertible<T, higher_t>::value,
-                "Type not embeddable into larger type!");
-  static_assert(is_widening_convertible<T, signed_higher_t>::value,
-                "Type not embeddable into larger signed type!");
-  static_assert(!is_widening_convertible<signed_t, higher_t>::value,
-                "Signed type is embeddable into larger unsigned type!");
-  static_assert(is_widening_convertible<signed_t, signed_higher_t>::value,
-                "Signed type not embeddable into larger signed type!");
-}
-
-template <typename T, typename Higher, typename Next, typename... Args>
-void CheckWideningConvertsToLargerTypes() {
-  CheckWideningConvertsToLargerTypes<T, Higher>();
-  CheckWideningConvertsToLargerTypes<Higher, Next, Args...>();
-}
-
-// CheckWideningConvertsTo<T, U, [expect]>
-//
-// Checks that T DOES widening-convert to U.
-// If "expect" is false, then asserts that T does NOT widening-convert to U.
-template <typename T, typename U, bool expect = true>
-void CheckWideningConvertsTo() {
-  static_assert(is_widening_convertible<T, U>::value == expect,
-                "Unexpected result for is_widening_convertible<T, U>!");
-}
-
-TEST(TraitsTest, IsWideningConvertibleTest) {
-  constexpr bool kInvalid = false;
-
-  CheckWideningConvertsToSelf<
-      uint8_t, uint16_t, uint32_t, uint64_t,
-      int8_t,  int16_t,  int32_t,  int64_t,
-      float,   double>();
-  CheckNotWideningConvertibleWithSigned<
-      uint8_t, uint16_t, uint32_t, uint64_t>();
-  CheckWideningConvertsToLargerTypes<
-      uint8_t, uint16_t, uint32_t, uint64_t>();
-
-  CheckWideningConvertsTo<float, double>();
-  CheckWideningConvertsTo<uint16_t, float>();
-  CheckWideningConvertsTo<uint32_t, double>();
-  CheckWideningConvertsTo<uint64_t, double, kInvalid>();
-  CheckWideningConvertsTo<double, float, kInvalid>();
-
-  CheckWideningConvertsTo<bool, int>();
-  CheckWideningConvertsTo<bool, float>();
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/uniform_helper.h b/third_party/abseil_cpp/absl/random/internal/uniform_helper.h
deleted file mode 100644
index 1243bc1c62..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/uniform_helper.h
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-#ifndef ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_
-#define ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_
-
-#include <cmath>
-#include <limits>
-#include <type_traits>
-
-#include "absl/base/config.h"
-#include "absl/meta/type_traits.h"
-#include "absl/random/internal/traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-template <typename IntType>
-class uniform_int_distribution;
-
-template <typename RealType>
-class uniform_real_distribution;
-
-// Interval tag types which specify whether the interval is open or closed
-// on either boundary.
-
-namespace random_internal {
-template <typename T>
-struct TagTypeCompare {};
-
-template <typename T>
-constexpr bool operator==(TagTypeCompare<T>, TagTypeCompare<T>) {
-  // Tags are mono-states. They always compare equal.
-  return true;
-}
-template <typename T>
-constexpr bool operator!=(TagTypeCompare<T>, TagTypeCompare<T>) {
-  return false;
-}
-
-}  // namespace random_internal
-
-struct IntervalClosedClosedTag
-    : public random_internal::TagTypeCompare<IntervalClosedClosedTag> {};
-struct IntervalClosedOpenTag
-    : public random_internal::TagTypeCompare<IntervalClosedOpenTag> {};
-struct IntervalOpenClosedTag
-    : public random_internal::TagTypeCompare<IntervalOpenClosedTag> {};
-struct IntervalOpenOpenTag
-    : public random_internal::TagTypeCompare<IntervalOpenOpenTag> {};
-
-namespace random_internal {
-
-// In the absence of an explicitly provided return-type, the template
-// "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on
-// the data-types of the endpoint-arguments {A lo, B hi}.
-//
-// Given endpoints {A lo, B hi}, one of {A, B} will be chosen as the
-// return-type, if one type can be implicitly converted into the other, in a
-// lossless way. The template "is_widening_convertible" implements the
-// compile-time logic for deciding if such a conversion is possible.
-//
-// If no such conversion between {A, B} exists, then the overload for
-// absl::Uniform() will be discarded, and the call will be ill-formed.
-// Return-type for absl::Uniform() when the return-type is inferred.
-template <typename A, typename B>
-using uniform_inferred_return_t =
-    absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>,
-                                        is_widening_convertible<B, A>>::value,
-                      typename std::conditional<
-                          is_widening_convertible<A, B>::value, B, A>::type>;
-
-// The functions
-//    uniform_lower_bound(tag, a, b)
-// and
-//    uniform_upper_bound(tag, a, b)
-// are used as implementation-details for absl::Uniform().
-//
-// Conceptually,
-//    [a, b] == [uniform_lower_bound(IntervalClosedClosed, a, b),
-//               uniform_upper_bound(IntervalClosedClosed, a, b)]
-//    (a, b) == [uniform_lower_bound(IntervalOpenOpen, a, b),
-//               uniform_upper_bound(IntervalOpenOpen, a, b)]
-//    [a, b) == [uniform_lower_bound(IntervalClosedOpen, a, b),
-//               uniform_upper_bound(IntervalClosedOpen, a, b)]
-//    (a, b] == [uniform_lower_bound(IntervalOpenClosed, a, b),
-//               uniform_upper_bound(IntervalOpenClosed, a, b)]
-//
-template <typename IntType, typename Tag>
-typename absl::enable_if_t<
-    absl::conjunction<
-        std::is_integral<IntType>,
-        absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>,
-                          std::is_same<Tag, IntervalOpenOpenTag>>>::value,
-    IntType>
-uniform_lower_bound(Tag, IntType a, IntType) {
-  return a < (std::numeric_limits<IntType>::max)() ? (a + 1) : a;
-}
-
-template <typename FloatType, typename Tag>
-typename absl::enable_if_t<
-    absl::conjunction<
-        std::is_floating_point<FloatType>,
-        absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>,
-                          std::is_same<Tag, IntervalOpenOpenTag>>>::value,
-    FloatType>
-uniform_lower_bound(Tag, FloatType a, FloatType b) {
-  return std::nextafter(a, b);
-}
-
-template <typename NumType, typename Tag>
-typename absl::enable_if_t<
-    absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
-                      std::is_same<Tag, IntervalClosedOpenTag>>::value,
-    NumType>
-uniform_lower_bound(Tag, NumType a, NumType) {
-  return a;
-}
-
-template <typename IntType, typename Tag>
-typename absl::enable_if_t<
-    absl::conjunction<
-        std::is_integral<IntType>,
-        absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>,
-                          std::is_same<Tag, IntervalOpenOpenTag>>>::value,
-    IntType>
-uniform_upper_bound(Tag, IntType, IntType b) {
-  return b > (std::numeric_limits<IntType>::min)() ? (b - 1) : b;
-}
-
-template <typename FloatType, typename Tag>
-typename absl::enable_if_t<
-    absl::conjunction<
-        std::is_floating_point<FloatType>,
-        absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>,
-                          std::is_same<Tag, IntervalOpenOpenTag>>>::value,
-    FloatType>
-uniform_upper_bound(Tag, FloatType, FloatType b) {
-  return b;
-}
-
-template <typename IntType, typename Tag>
-typename absl::enable_if_t<
-    absl::conjunction<
-        std::is_integral<IntType>,
-        absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
-                          std::is_same<Tag, IntervalOpenClosedTag>>>::value,
-    IntType>
-uniform_upper_bound(Tag, IntType, IntType b) {
-  return b;
-}
-
-template <typename FloatType, typename Tag>
-typename absl::enable_if_t<
-    absl::conjunction<
-        std::is_floating_point<FloatType>,
-        absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
-                          std::is_same<Tag, IntervalOpenClosedTag>>>::value,
-    FloatType>
-uniform_upper_bound(Tag, FloatType, FloatType b) {
-  return std::nextafter(b, (std::numeric_limits<FloatType>::max)());
-}
-
-// Returns whether the bounds are valid for the underlying distribution.
-// Inputs must have already been resolved via uniform_*_bound calls.
-//
-// The c++ standard constraints in [rand.dist.uni.int] are listed as:
-//    requires: lo <= hi.
-//
-// In the uniform_int_distrubtion, {lo, hi} are closed, closed. Thus:
-// [0, 0] is legal.
-// [0, 0) is not legal, but [0, 1) is, which translates to [0, 0].
-// (0, 1) is not legal, but (0, 2) is, which translates to [1, 1].
-// (0, 0] is not legal, but (0, 1] is, which translates to [1, 1].
-//
-// The c++ standard constraints in [rand.dist.uni.real] are listed as:
-//    requires: lo <= hi.
-//    requires: (hi - lo) <= numeric_limits<T>::max()
-//
-// In the uniform_real_distribution, {lo, hi} are closed, open, Thus:
-// [0, 0] is legal, which is [0, 0+epsilon).
-// [0, 0) is legal.
-// (0, 0) is not legal, but (0-epsilon, 0+epsilon) is.
-// (0, 0] is not legal, but (0, 0+epsilon] is.
-//
-template <typename FloatType>
-absl::enable_if_t<std::is_floating_point<FloatType>::value, bool>
-is_uniform_range_valid(FloatType a, FloatType b) {
-  return a <= b && std::isfinite(b - a);
-}
-
-template <typename IntType>
-absl::enable_if_t<std::is_integral<IntType>::value, bool>
-is_uniform_range_valid(IntType a, IntType b) {
-  return a <= b;
-}
-
-// UniformDistribution selects either absl::uniform_int_distribution
-// or absl::uniform_real_distribution depending on the NumType parameter.
-template <typename NumType>
-using UniformDistribution =
-    typename std::conditional<std::is_integral<NumType>::value,
-                              absl::uniform_int_distribution<NumType>,
-                              absl::uniform_real_distribution<NumType>>::type;
-
-// UniformDistributionWrapper is used as the underlying distribution type
-// by the absl::Uniform template function. It selects the proper Abseil
-// uniform distribution and provides constructor overloads that match the
-// expected parameter order as well as adjusting distribtuion bounds based
-// on the tag.
-template <typename NumType>
-struct UniformDistributionWrapper : public UniformDistribution<NumType> {
-  template <typename TagType>
-  explicit UniformDistributionWrapper(TagType, NumType lo, NumType hi)
-      : UniformDistribution<NumType>(
-            uniform_lower_bound<NumType>(TagType{}, lo, hi),
-            uniform_upper_bound<NumType>(TagType{}, lo, hi)) {}
-
-  explicit UniformDistributionWrapper(NumType lo, NumType hi)
-      : UniformDistribution<NumType>(
-            uniform_lower_bound<NumType>(IntervalClosedOpenTag(), lo, hi),
-            uniform_upper_bound<NumType>(IntervalClosedOpenTag(), lo, hi)) {}
-
-  explicit UniformDistributionWrapper()
-      : UniformDistribution<NumType>(std::numeric_limits<NumType>::lowest(),
-                                     (std::numeric_limits<NumType>::max)()) {}
-};
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/uniform_helper_test.cc b/third_party/abseil_cpp/absl/random/internal/uniform_helper_test.cc
deleted file mode 100644
index 173c49b0b7..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/uniform_helper_test.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/uniform_helper.h"
-
-#include <cmath>
-#include <cstdint>
-#include <random>
-
-#include "gtest/gtest.h"
-
-namespace {
-
-using absl::IntervalClosedClosedTag;
-using absl::IntervalClosedOpenTag;
-using absl::IntervalOpenClosedTag;
-using absl::IntervalOpenOpenTag;
-using absl::random_internal::uniform_inferred_return_t;
-using absl::random_internal::uniform_lower_bound;
-using absl::random_internal::uniform_upper_bound;
-
-class UniformHelperTest : public testing::Test {};
-
-TEST_F(UniformHelperTest, UniformBoundFunctionsGeneral) {
-  constexpr IntervalClosedClosedTag IntervalClosedClosed;
-  constexpr IntervalClosedOpenTag IntervalClosedOpen;
-  constexpr IntervalOpenClosedTag IntervalOpenClosed;
-  constexpr IntervalOpenOpenTag IntervalOpenOpen;
-
-  // absl::uniform_int_distribution natively assumes IntervalClosedClosed
-  // absl::uniform_real_distribution natively assumes IntervalClosedOpen
-
-  EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, 0, 100), 1);
-  EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, 0, 100), 1);
-  EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, 0, 1.0), 0);
-  EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, 0, 1.0), 0);
-  EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, 0, 1.0), 0);
-  EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, 0, 1.0), 0);
-
-  EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, 0, 100), 0);
-  EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, 0, 100), 0);
-  EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, 0, 1.0), 0);
-  EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, 0, 1.0), 0);
-  EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, 0, 1.0), 0);
-  EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, 0, 1.0), 0);
-
-  EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, 0, 100), 99);
-  EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, 0, 100), 99);
-  EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, 0, 1.0), 1.0);
-  EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, 0, 1.0), 1.0);
-  EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, 0, 1.0), 1.0);
-  EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, 0, 1.0), 1.0);
-
-  EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, 0, 100), 100);
-  EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0, 100), 100);
-  EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, 0, 1.0), 1.0);
-  EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, 0, 1.0), 1.0);
-  EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, 0, 1.0), 1.0);
-  EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, 0, 1.0), 1.0);
-
-  // Negative value tests
-  EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, -100, -1), -99);
-  EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, -100, -1), -99);
-  EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, -2.0, -1.0), -2.0);
-  EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, -2.0, -1.0), -2.0);
-  EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, -2.0, -1.0), -2.0);
-  EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, -2.0, -1.0), -2.0);
-
-  EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, -100, -1), -100);
-  EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, -100, -1), -100);
-  EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, -2.0, -1.0), -2.0);
-  EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, -2.0, -1.0), -2.0);
-  EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, -2.0, -1.0),
-            -2.0);
-  EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, -2.0, -1.0), -2.0);
-
-  EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, -100, -1), -2);
-  EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, -100, -1), -2);
-  EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, -2.0, -1.0), -1.0);
-  EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, -2.0, -1.0), -1.0);
-  EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, -2.0, -1.0), -1.0);
-  EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, -2.0, -1.0), -1.0);
-
-  EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, -100, -1), -1);
-  EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, -100, -1), -1);
-  EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, -2.0, -1.0), -1.0);
-  EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, -2.0, -1.0), -1.0);
-  EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, -2.0, -1.0), -1.0);
-  EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, -2.0, -1.0),
-            -1.0);
-
-  EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, 1.0, 2.0), 1.0);
-  EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, +0.0), 1.0);
-  EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -0.0), 1.0);
-  EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0), 1.0);
-}
-
-TEST_F(UniformHelperTest, UniformBoundFunctionsIntBounds) {
-  // Verifies the saturating nature of uniform_lower_bound and
-  // uniform_upper_bound
-  constexpr IntervalOpenOpenTag IntervalOpenOpen;
-
-  // uint max.
-  constexpr auto m = (std::numeric_limits<uint64_t>::max)();
-
-  EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0u, 0u));
-  EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m, m));
-  EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m - 1, m - 1));
-  EXPECT_EQ(0, uniform_upper_bound(IntervalOpenOpen, 0u, 0u));
-  EXPECT_EQ(m - 1, uniform_upper_bound(IntervalOpenOpen, m, m));
-
-  // int min/max
-  constexpr auto l = (std::numeric_limits<int64_t>::min)();
-  constexpr auto r = (std::numeric_limits<int64_t>::max)();
-  EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0, 0));
-  EXPECT_EQ(l + 1, uniform_lower_bound(IntervalOpenOpen, l, l));
-  EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r - 1, r - 1));
-  EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r, r));
-  EXPECT_EQ(-1, uniform_upper_bound(IntervalOpenOpen, 0, 0));
-  EXPECT_EQ(l, uniform_upper_bound(IntervalOpenOpen, l, l));
-  EXPECT_EQ(r - 1, uniform_upper_bound(IntervalOpenOpen, r, r));
-}
-
-TEST_F(UniformHelperTest, UniformBoundFunctionsRealBounds) {
-  // absl::uniform_real_distribution natively assumes IntervalClosedOpen;
-  // use the inverse here so each bound has to change.
-  constexpr IntervalOpenClosedTag IntervalOpenClosed;
-
-  // Edge cases: the next value toward itself is itself.
-  EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, 1.0, 1.0));
-  EXPECT_EQ(1.0f, uniform_lower_bound(IntervalOpenClosed, 1.0f, 1.0f));
-
-  // rightmost and leftmost finite values.
-  constexpr auto r = (std::numeric_limits<double>::max)();
-  const auto re = std::nexttoward(r, 0.0);
-  constexpr auto l = -r;
-  const auto le = std::nexttoward(l, 0.0);
-
-  EXPECT_EQ(l, uniform_lower_bound(IntervalOpenClosed, l, l));     // (l,l)
-  EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, r, r));     // (r,r)
-  EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, r));    // (l,r)
-  EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, 0.0));  // (l, 0)
-  EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, le));   // (l, le)
-  EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, re, r));    // (re, r)
-
-  EXPECT_EQ(le, uniform_upper_bound(IntervalOpenClosed, l, l));   // (l,l)
-  EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, r, r));    // (r,r)
-  EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, r));    // (l,r)
-  EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, re));   // (l,re)
-  EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, 0.0, r));  // (0, r)
-  EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, re, r));   // (re, r)
-  EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, le, re));  // (le, re)
-
-  const double e = std::nextafter(1.0, 2.0);  // 1 + epsilon
-  const double f = std::nextafter(1.0, 0.0);  // 1 - epsilon
-
-  // (1.0, 1.0 + epsilon)
-  EXPECT_EQ(e, uniform_lower_bound(IntervalOpenClosed, 1.0, e));
-  EXPECT_EQ(std::nextafter(e, 2.0),
-            uniform_upper_bound(IntervalOpenClosed, 1.0, e));
-
-  // (1.0-epsilon, 1.0)
-  EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, f, 1.0));
-  EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, f, 1.0));
-
-  // denorm cases.
-  const double g = std::numeric_limits<double>::denorm_min();
-  const double h = std::nextafter(g, 1.0);
-
-  // (0, denorm_min)
-  EXPECT_EQ(g, uniform_lower_bound(IntervalOpenClosed, 0.0, g));
-  EXPECT_EQ(h, uniform_upper_bound(IntervalOpenClosed, 0.0, g));
-
-  // (denorm_min, 1.0)
-  EXPECT_EQ(h, uniform_lower_bound(IntervalOpenClosed, g, 1.0));
-  EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, g, 1.0));
-
-  // Edge cases: invalid bounds.
-  EXPECT_EQ(f, uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0));
-}
-
-struct Invalid {};
-
-template <typename A, typename B>
-auto InferredUniformReturnT(int) -> uniform_inferred_return_t<A, B>;
-
-template <typename, typename>
-Invalid InferredUniformReturnT(...);
-
-// Given types <A, B, Expect>, CheckArgsInferType() verifies that
-//
-//   uniform_inferred_return_t<A, B> and
-//   uniform_inferred_return_t<B, A>
-//
-// returns the type "Expect".
-//
-// This interface can also be used to assert that a given inferred return types
-// are invalid. Writing:
-//
-//   CheckArgsInferType<float, int, Invalid>()
-//
-// will assert that this overload does not exist.
-template <typename A, typename B, typename Expect>
-void CheckArgsInferType() {
-  static_assert(
-      absl::conjunction<
-          std::is_same<Expect, decltype(InferredUniformReturnT<A, B>(0))>,
-          std::is_same<Expect,
-                       decltype(InferredUniformReturnT<B, A>(0))>>::value,
-      "");
-}
-
-TEST_F(UniformHelperTest, UniformTypeInference) {
-  // Infers common types.
-  CheckArgsInferType<uint16_t, uint16_t, uint16_t>();
-  CheckArgsInferType<uint32_t, uint32_t, uint32_t>();
-  CheckArgsInferType<uint64_t, uint64_t, uint64_t>();
-  CheckArgsInferType<int16_t, int16_t, int16_t>();
-  CheckArgsInferType<int32_t, int32_t, int32_t>();
-  CheckArgsInferType<int64_t, int64_t, int64_t>();
-  CheckArgsInferType<float, float, float>();
-  CheckArgsInferType<double, double, double>();
-
-  // Properly promotes uint16_t.
-  CheckArgsInferType<uint16_t, uint32_t, uint32_t>();
-  CheckArgsInferType<uint16_t, uint64_t, uint64_t>();
-  CheckArgsInferType<uint16_t, int32_t, int32_t>();
-  CheckArgsInferType<uint16_t, int64_t, int64_t>();
-  CheckArgsInferType<uint16_t, float, float>();
-  CheckArgsInferType<uint16_t, double, double>();
-
-  // Properly promotes int16_t.
-  CheckArgsInferType<int16_t, int32_t, int32_t>();
-  CheckArgsInferType<int16_t, int64_t, int64_t>();
-  CheckArgsInferType<int16_t, float, float>();
-  CheckArgsInferType<int16_t, double, double>();
-
-  // Invalid (u)int16_t-pairings do not compile.
-  // See "CheckArgsInferType" comments above, for how this is achieved.
-  CheckArgsInferType<uint16_t, int16_t, Invalid>();
-  CheckArgsInferType<int16_t, uint32_t, Invalid>();
-  CheckArgsInferType<int16_t, uint64_t, Invalid>();
-
-  // Properly promotes uint32_t.
-  CheckArgsInferType<uint32_t, uint64_t, uint64_t>();
-  CheckArgsInferType<uint32_t, int64_t, int64_t>();
-  CheckArgsInferType<uint32_t, double, double>();
-
-  // Properly promotes int32_t.
-  CheckArgsInferType<int32_t, int64_t, int64_t>();
-  CheckArgsInferType<int32_t, double, double>();
-
-  // Invalid (u)int32_t-pairings do not compile.
-  CheckArgsInferType<uint32_t, int32_t, Invalid>();
-  CheckArgsInferType<int32_t, uint64_t, Invalid>();
-  CheckArgsInferType<int32_t, float, Invalid>();
-  CheckArgsInferType<uint32_t, float, Invalid>();
-
-  // Invalid (u)int64_t-pairings do not compile.
-  CheckArgsInferType<uint64_t, int64_t, Invalid>();
-  CheckArgsInferType<int64_t, float, Invalid>();
-  CheckArgsInferType<int64_t, double, Invalid>();
-
-  // Properly promotes float.
-  CheckArgsInferType<float, double, double>();
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/internal/wide_multiply.h b/third_party/abseil_cpp/absl/random/internal/wide_multiply.h
deleted file mode 100644
index 0afcbe08e2..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/wide_multiply.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_
-#define ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_
-
-#include <cstdint>
-#include <limits>
-#include <type_traits>
-
-#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64)
-#include <intrin.h>  // NOLINT(build/include_order)
-#pragma intrinsic(_umul128)
-#define ABSL_INTERNAL_USE_UMUL128 1
-#endif
-
-#include "absl/base/config.h"
-#include "absl/base/internal/bits.h"
-#include "absl/numeric/int128.h"
-#include "absl/random/internal/traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-// Helper object to multiply two 64-bit values to a 128-bit value.
-// MultiplyU64ToU128 multiplies two 64-bit values to a 128-bit value.
-// If an intrinsic is available, it is used, otherwise use native 32-bit
-// multiplies to construct the result.
-inline absl::uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) {
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-  return absl::uint128(static_cast<__uint128_t>(a) * b);
-#elif defined(ABSL_INTERNAL_USE_UMUL128)
-  // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC.
-  uint64_t high = 0;
-  const uint64_t low = _umul128(a, b, &high);
-  return absl::MakeUint128(high, low);
-#else
-  // uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit
-  // multiply.  However there are many cases where that is not necessary, and it
-  // is only necessary to support a 64-bit x 64-bit = 128-bit multiply.  This is
-  // for those cases.
-  const uint64_t a00 = static_cast<uint32_t>(a);
-  const uint64_t a32 = a >> 32;
-  const uint64_t b00 = static_cast<uint32_t>(b);
-  const uint64_t b32 = b >> 32;
-
-  const uint64_t c00 = a00 * b00;
-  const uint64_t c32a = a00 * b32;
-  const uint64_t c32b = a32 * b00;
-  const uint64_t c64 = a32 * b32;
-
-  const uint32_t carry =
-      static_cast<uint32_t>(((c00 >> 32) + static_cast<uint32_t>(c32a) +
-                             static_cast<uint32_t>(c32b)) >>
-                            32);
-
-  return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry,
-                           c00 + (c32a << 32) + (c32b << 32));
-#endif
-}
-
-// wide_multiply<T> multiplies two N-bit values to a 2N-bit result.
-template <typename UIntType>
-struct wide_multiply {
-  static constexpr size_t kN = std::numeric_limits<UIntType>::digits;
-  using input_type = UIntType;
-  using result_type = typename random_internal::unsigned_bits<kN * 2>::type;
-
-  static result_type multiply(input_type a, input_type b) {
-    return static_cast<result_type>(a) * b;
-  }
-
-  static input_type hi(result_type r) { return r >> kN; }
-  static input_type lo(result_type r) { return r; }
-
-  static_assert(std::is_unsigned<UIntType>::value,
-                "Class-template wide_multiply<> argument must be unsigned.");
-};
-
-#ifndef ABSL_HAVE_INTRINSIC_INT128
-template <>
-struct wide_multiply<uint64_t> {
-  using input_type = uint64_t;
-  using result_type = absl::uint128;
-
-  static result_type multiply(uint64_t a, uint64_t b) {
-    return MultiplyU64ToU128(a, b);
-  }
-
-  static uint64_t hi(result_type r) { return absl::Uint128High64(r); }
-  static uint64_t lo(result_type r) { return absl::Uint128Low64(r); }
-};
-#endif
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_
diff --git a/third_party/abseil_cpp/absl/random/internal/wide_multiply_test.cc b/third_party/abseil_cpp/absl/random/internal/wide_multiply_test.cc
deleted file mode 100644
index ca8ce923b7..0000000000
--- a/third_party/abseil_cpp/absl/random/internal/wide_multiply_test.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/internal/wide_multiply.h"
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/bits.h"
-#include "absl/numeric/int128.h"
-
-using absl::random_internal::MultiplyU64ToU128;
-
-namespace {
-
-TEST(WideMultiplyTest, MultiplyU64ToU128Test) {
-  constexpr uint64_t k1 = 1;
-  constexpr uint64_t kMax = ~static_cast<uint64_t>(0);
-
-  EXPECT_EQ(absl::uint128(0), MultiplyU64ToU128(0, 0));
-
-  // Max uint64_t
-  EXPECT_EQ(MultiplyU64ToU128(kMax, kMax),
-            absl::MakeUint128(0xfffffffffffffffe, 0x0000000000000001));
-  EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(kMax, 1));
-  EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(1, kMax));
-  for (int i = 0; i < 64; ++i) {
-    EXPECT_EQ(absl::MakeUint128(0, kMax) << i,
-              MultiplyU64ToU128(kMax, k1 << i));
-    EXPECT_EQ(absl::MakeUint128(0, kMax) << i,
-              MultiplyU64ToU128(k1 << i, kMax));
-  }
-
-  // 1-bit x 1-bit.
-  for (int i = 0; i < 64; ++i) {
-    for (int j = 0; j < 64; ++j) {
-      EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j),
-                MultiplyU64ToU128(k1 << i, k1 << j));
-      EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j),
-                MultiplyU64ToU128(k1 << i, k1 << j));
-    }
-  }
-
-  // Verified multiplies
-  EXPECT_EQ(MultiplyU64ToU128(0xffffeeeeddddcccc, 0xbbbbaaaa99998888),
-            absl::MakeUint128(0xbbbb9e2692c5dddc, 0xc28f7531048d2c60));
-  EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfedcba9876543210),
-            absl::MakeUint128(0x0121fa00ad77d742, 0x2236d88fe5618cf0));
-  EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfdb97531eca86420),
-            absl::MakeUint128(0x0120ae99d26725fc, 0xce197f0ecac319e0));
-  EXPECT_EQ(MultiplyU64ToU128(0x97a87f4f261ba3f2, 0xfedcba9876543210),
-            absl::MakeUint128(0x96fbf1a8ae78d0ba, 0x5a6dd4b71f278320));
-  EXPECT_EQ(MultiplyU64ToU128(0xfedcba9876543210, 0xfdb97531eca86420),
-            absl::MakeUint128(0xfc98c6981a413e22, 0x342d0bbf48948200));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/log_uniform_int_distribution.h b/third_party/abseil_cpp/absl/random/log_uniform_int_distribution.h
deleted file mode 100644
index 960816e2f8..0000000000
--- a/third_party/abseil_cpp/absl/random/log_uniform_int_distribution.h
+++ /dev/null
@@ -1,254 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_
-#define ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_
-
-#include <algorithm>
-#include <cassert>
-#include <cmath>
-#include <istream>
-#include <limits>
-#include <ostream>
-#include <type_traits>
-
-#include "absl/random/internal/fastmath.h"
-#include "absl/random/internal/generate_real.h"
-#include "absl/random/internal/iostream_state_saver.h"
-#include "absl/random/internal/traits.h"
-#include "absl/random/uniform_int_distribution.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// log_uniform_int_distribution:
-//
-// Returns a random variate R in range [min, max] such that
-// floor(log(R-min, base)) is uniformly distributed.
-// We ensure uniformity by discretization using the
-// boundary sets [0, 1, base, base * base, ... min(base*n, max)]
-//
-template <typename IntType = int>
-class log_uniform_int_distribution {
- private:
-  using unsigned_type =
-      typename random_internal::make_unsigned_bits<IntType>::type;
-
- public:
-  using result_type = IntType;
-
-  class param_type {
-   public:
-    using distribution_type = log_uniform_int_distribution;
-
-    explicit param_type(
-        result_type min = 0,
-        result_type max = (std::numeric_limits<result_type>::max)(),
-        result_type base = 2)
-        : min_(min),
-          max_(max),
-          base_(base),
-          range_(static_cast<unsigned_type>(max_) -
-                 static_cast<unsigned_type>(min_)),
-          log_range_(0) {
-      assert(max_ >= min_);
-      assert(base_ > 1);
-
-      if (base_ == 2) {
-        // Determine where the first set bit is on range(), giving a log2(range)
-        // value which can be used to construct bounds.
-        log_range_ = (std::min)(random_internal::LeadingSetBit(range()),
-                                std::numeric_limits<unsigned_type>::digits);
-      } else {
-        // NOTE: Computing the logN(x) introduces error from 2 sources:
-        // 1. Conversion of int to double loses precision for values >=
-        // 2^53, which may cause some log() computations to operate on
-        // different values.
-        // 2. The error introduced by the division will cause the result
-        // to differ from the expected value.
-        //
-        // Thus a result which should equal K may equal K +/- epsilon,
-        // which can eliminate some values depending on where the bounds fall.
-        const double inv_log_base = 1.0 / std::log(base_);
-        const double log_range = std::log(static_cast<double>(range()) + 0.5);
-        log_range_ = static_cast<int>(std::ceil(inv_log_base * log_range));
-      }
-    }
-
-    result_type(min)() const { return min_; }
-    result_type(max)() const { return max_; }
-    result_type base() const { return base_; }
-
-    friend bool operator==(const param_type& a, const param_type& b) {
-      return a.min_ == b.min_ && a.max_ == b.max_ && a.base_ == b.base_;
-    }
-
-    friend bool operator!=(const param_type& a, const param_type& b) {
-      return !(a == b);
-    }
-
-   private:
-    friend class log_uniform_int_distribution;
-
-    int log_range() const { return log_range_; }
-    unsigned_type range() const { return range_; }
-
-    result_type min_;
-    result_type max_;
-    result_type base_;
-    unsigned_type range_;  // max - min
-    int log_range_;        // ceil(logN(range_))
-
-    static_assert(std::is_integral<IntType>::value,
-                  "Class-template absl::log_uniform_int_distribution<> must be "
-                  "parameterized using an integral type.");
-  };
-
-  log_uniform_int_distribution() : log_uniform_int_distribution(0) {}
-
-  explicit log_uniform_int_distribution(
-      result_type min,
-      result_type max = (std::numeric_limits<result_type>::max)(),
-      result_type base = 2)
-      : param_(min, max, base) {}
-
-  explicit log_uniform_int_distribution(const param_type& p) : param_(p) {}
-
-  void reset() {}
-
-  // generating functions
-  template <typename URBG>
-  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
-    return (*this)(g, param_);
-  }
-
-  template <typename URBG>
-  result_type operator()(URBG& g,  // NOLINT(runtime/references)
-                         const param_type& p) {
-    return (p.min)() + Generate(g, p);
-  }
-
-  result_type(min)() const { return (param_.min)(); }
-  result_type(max)() const { return (param_.max)(); }
-  result_type base() const { return param_.base(); }
-
-  param_type param() const { return param_; }
-  void param(const param_type& p) { param_ = p; }
-
-  friend bool operator==(const log_uniform_int_distribution& a,
-                         const log_uniform_int_distribution& b) {
-    return a.param_ == b.param_;
-  }
-  friend bool operator!=(const log_uniform_int_distribution& a,
-                         const log_uniform_int_distribution& b) {
-    return a.param_ != b.param_;
-  }
-
- private:
-  // Returns a log-uniform variate in the range [0, p.range()]. The caller
-  // should add min() to shift the result to the correct range.
-  template <typename URNG>
-  unsigned_type Generate(URNG& g,  // NOLINT(runtime/references)
-                         const param_type& p);
-
-  param_type param_;
-};
-
-template <typename IntType>
-template <typename URBG>
-typename log_uniform_int_distribution<IntType>::unsigned_type
-log_uniform_int_distribution<IntType>::Generate(
-    URBG& g,  // NOLINT(runtime/references)
-    const param_type& p) {
-  // sample e over [0, log_range]. Map the results of e to this:
-  // 0 => 0
-  // 1 => [1, b-1]
-  // 2 => [b, (b^2)-1]
-  // n => [b^(n-1)..(b^n)-1]
-  const int e = absl::uniform_int_distribution<int>(0, p.log_range())(g);
-  if (e == 0) {
-    return 0;
-  }
-  const int d = e - 1;
-
-  unsigned_type base_e, top_e;
-  if (p.base() == 2) {
-    base_e = static_cast<unsigned_type>(1) << d;
-
-    top_e = (e >= std::numeric_limits<unsigned_type>::digits)
-                ? (std::numeric_limits<unsigned_type>::max)()
-                : (static_cast<unsigned_type>(1) << e) - 1;
-  } else {
-    const double r = std::pow(p.base(), d);
-    const double s = (r * p.base()) - 1.0;
-
-    base_e =
-        (r > static_cast<double>((std::numeric_limits<unsigned_type>::max)()))
-            ? (std::numeric_limits<unsigned_type>::max)()
-            : static_cast<unsigned_type>(r);
-
-    top_e =
-        (s > static_cast<double>((std::numeric_limits<unsigned_type>::max)()))
-            ? (std::numeric_limits<unsigned_type>::max)()
-            : static_cast<unsigned_type>(s);
-  }
-
-  const unsigned_type lo = (base_e >= p.range()) ? p.range() : base_e;
-  const unsigned_type hi = (top_e >= p.range()) ? p.range() : top_e;
-
-  // choose uniformly over [lo, hi]
-  return absl::uniform_int_distribution<result_type>(lo, hi)(g);
-}
-
-template <typename CharT, typename Traits, typename IntType>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    const log_uniform_int_distribution<IntType>& x) {
-  using stream_type =
-      typename random_internal::stream_format_type<IntType>::type;
-  auto saver = random_internal::make_ostream_state_saver(os);
-  os << static_cast<stream_type>((x.min)()) << os.fill()
-     << static_cast<stream_type>((x.max)()) << os.fill()
-     << static_cast<stream_type>(x.base());
-  return os;
-}
-
-template <typename CharT, typename Traits, typename IntType>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,       // NOLINT(runtime/references)
-    log_uniform_int_distribution<IntType>& x) {  // NOLINT(runtime/references)
-  using param_type = typename log_uniform_int_distribution<IntType>::param_type;
-  using result_type =
-      typename log_uniform_int_distribution<IntType>::result_type;
-  using stream_type =
-      typename random_internal::stream_format_type<IntType>::type;
-
-  stream_type min;
-  stream_type max;
-  stream_type base;
-
-  auto saver = random_internal::make_istream_state_saver(is);
-  is >> min >> max >> base;
-  if (!is.fail()) {
-    x.param(param_type(static_cast<result_type>(min),
-                       static_cast<result_type>(max),
-                       static_cast<result_type>(base)));
-  }
-  return is;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/log_uniform_int_distribution_test.cc b/third_party/abseil_cpp/absl/random/log_uniform_int_distribution_test.cc
deleted file mode 100644
index 5e780d96d3..0000000000
--- a/third_party/abseil_cpp/absl/random/log_uniform_int_distribution_test.cc
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/log_uniform_int_distribution.h"
-
-#include <cstddef>
-#include <cstdint>
-#include <iterator>
-#include <random>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/chi_square.h"
-#include "absl/random/internal/distribution_test_util.h"
-#include "absl/random/internal/pcg_engine.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_replace.h"
-#include "absl/strings/strip.h"
-
-namespace {
-
-template <typename IntType>
-class LogUniformIntDistributionTypeTest : public ::testing::Test {};
-
-using IntTypes = ::testing::Types<int8_t, int16_t, int32_t, int64_t,  //
-                                  uint8_t, uint16_t, uint32_t, uint64_t>;
-TYPED_TEST_CASE(LogUniformIntDistributionTypeTest, IntTypes);
-
-TYPED_TEST(LogUniformIntDistributionTypeTest, SerializeTest) {
-  using param_type =
-      typename absl::log_uniform_int_distribution<TypeParam>::param_type;
-  using Limits = std::numeric_limits<TypeParam>;
-
-  constexpr int kCount = 1000;
-  absl::InsecureBitGen gen;
-  for (const auto& param : {
-           param_type(0, 1),                             //
-           param_type(0, 2),                             //
-           param_type(0, 2, 10),                         //
-           param_type(9, 32, 4),                         //
-           param_type(1, 101, 10),                       //
-           param_type(1, Limits::max() / 2),             //
-           param_type(0, Limits::max() - 1),             //
-           param_type(0, Limits::max(), 2),              //
-           param_type(0, Limits::max(), 10),             //
-           param_type(Limits::min(), 0),                 //
-           param_type(Limits::lowest(), Limits::max()),  //
-           param_type(Limits::min(), Limits::max()),     //
-       }) {
-    // Validate parameters.
-    const auto min = param.min();
-    const auto max = param.max();
-    const auto base = param.base();
-    absl::log_uniform_int_distribution<TypeParam> before(min, max, base);
-    EXPECT_EQ(before.min(), param.min());
-    EXPECT_EQ(before.max(), param.max());
-    EXPECT_EQ(before.base(), param.base());
-
-    {
-      absl::log_uniform_int_distribution<TypeParam> via_param(param);
-      EXPECT_EQ(via_param, before);
-    }
-
-    // Validate stream serialization.
-    std::stringstream ss;
-    ss << before;
-
-    absl::log_uniform_int_distribution<TypeParam> after(3, 6, 17);
-
-    EXPECT_NE(before.max(), after.max());
-    EXPECT_NE(before.base(), after.base());
-    EXPECT_NE(before.param(), after.param());
-    EXPECT_NE(before, after);
-
-    ss >> after;
-
-    EXPECT_EQ(before.min(), after.min());
-    EXPECT_EQ(before.max(), after.max());
-    EXPECT_EQ(before.base(), after.base());
-    EXPECT_EQ(before.param(), after.param());
-    EXPECT_EQ(before, after);
-
-    // Smoke test.
-    auto sample_min = after.max();
-    auto sample_max = after.min();
-    for (int i = 0; i < kCount; i++) {
-      auto sample = after(gen);
-      EXPECT_GE(sample, after.min());
-      EXPECT_LE(sample, after.max());
-      if (sample > sample_max) sample_max = sample;
-      if (sample < sample_min) sample_min = sample;
-    }
-    ABSL_INTERNAL_LOG(INFO,
-                      absl::StrCat("Range: ", +sample_min, ", ", +sample_max));
-  }
-}
-
-using log_uniform_i32 = absl::log_uniform_int_distribution<int32_t>;
-
-class LogUniformIntChiSquaredTest
-    : public testing::TestWithParam<log_uniform_i32::param_type> {
- public:
-  // The ChiSquaredTestImpl provides a chi-squared goodness of fit test for
-  // data generated by the log-uniform-int distribution.
-  double ChiSquaredTestImpl();
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6};
-};
-
-double LogUniformIntChiSquaredTest::ChiSquaredTestImpl() {
-  using absl::random_internal::kChiSquared;
-
-  const auto& param = GetParam();
-
-  // Check the distribution of L=log(log_uniform_int_distribution, base),
-  // expecting that L is roughly uniformly distributed, that is:
-  //
-  //   P[L=0] ~= P[L=1] ~= ... ~= P[L=log(max)]
-  //
-  // For a total of X entries, each bucket should contain some number of samples
-  // in the interval [X/k - a, X/k + a].
-  //
-  // Where `a` is approximately sqrt(X/k). This is validated by bucketing
-  // according to the log function and using a chi-squared test for uniformity.
-
-  const bool is_2 = (param.base() == 2);
-  const double base_log = 1.0 / std::log(param.base());
-  const auto bucket_index = [base_log, is_2, &param](int32_t x) {
-    uint64_t y = static_cast<uint64_t>(x) - param.min();
-    return (y == 0) ? 0
-                    : is_2 ? static_cast<int>(1 + std::log2(y))
-                           : static_cast<int>(1 + std::log(y) * base_log);
-  };
-  const int max_bucket = bucket_index(param.max());  // inclusive
-  const size_t trials = 15 + (max_bucket + 1) * 10;
-
-  log_uniform_i32 dist(param);
-
-  std::vector<int64_t> buckets(max_bucket + 1);
-  for (size_t i = 0; i < trials; ++i) {
-    const auto sample = dist(rng_);
-    // Check the bounds.
-    ABSL_ASSERT(sample <= dist.max());
-    ABSL_ASSERT(sample >= dist.min());
-    // Convert the output of the generator to one of num_bucket buckets.
-    int bucket = bucket_index(sample);
-    ABSL_ASSERT(bucket <= max_bucket);
-    ++buckets[bucket];
-  }
-
-  // The null-hypothesis is that the distribution is uniform with respect to
-  // log-uniform-int bucketization.
-  const int dof = buckets.size() - 1;
-  const double expected = trials / static_cast<double>(buckets.size());
-
-  const double threshold = absl::random_internal::ChiSquareValue(dof, 0.98);
-
-  double chi_square = absl::random_internal::ChiSquareWithExpected(
-      std::begin(buckets), std::end(buckets), expected);
-
-  const double p = absl::random_internal::ChiSquarePValue(chi_square, dof);
-
-  if (chi_square > threshold) {
-    ABSL_INTERNAL_LOG(INFO, "values");
-    for (size_t i = 0; i < buckets.size(); i++) {
-      ABSL_INTERNAL_LOG(INFO, absl::StrCat(i, ": ", buckets[i]));
-    }
-    ABSL_INTERNAL_LOG(INFO,
-                      absl::StrFormat("trials=%d\n"
-                                      "%s(data, %d) = %f (%f)\n"
-                                      "%s @ 0.98 = %f",
-                                      trials, kChiSquared, dof, chi_square, p,
-                                      kChiSquared, threshold));
-  }
-  return p;
-}
-
-TEST_P(LogUniformIntChiSquaredTest, MultiTest) {
-  const int kTrials = 5;
-  int failures = 0;
-  for (int i = 0; i < kTrials; i++) {
-    double p_value = ChiSquaredTestImpl();
-    if (p_value < 0.005) {
-      failures++;
-    }
-  }
-
-  // There is a 0.10% chance of producing at least one failure, so raise the
-  // failure threshold high enough to allow for a flake rate < 10,000.
-  EXPECT_LE(failures, 4);
-}
-
-// Generate the parameters for the test.
-std::vector<log_uniform_i32::param_type> GenParams() {
-  using Param = log_uniform_i32::param_type;
-  using Limits = std::numeric_limits<int32_t>;
-
-  return std::vector<Param>{
-      Param{0, 1, 2},
-      Param{1, 1, 2},
-      Param{0, 2, 2},
-      Param{0, 3, 2},
-      Param{0, 4, 2},
-      Param{0, 9, 10},
-      Param{0, 10, 10},
-      Param{0, 11, 10},
-      Param{1, 10, 10},
-      Param{0, (1 << 8) - 1, 2},
-      Param{0, (1 << 8), 2},
-      Param{0, (1 << 30) - 1, 2},
-      Param{-1000, 1000, 10},
-      Param{0, Limits::max(), 2},
-      Param{0, Limits::max(), 3},
-      Param{0, Limits::max(), 10},
-      Param{Limits::min(), 0},
-      Param{Limits::min(), Limits::max(), 2},
-  };
-}
-
-std::string ParamName(
-    const ::testing::TestParamInfo<log_uniform_i32::param_type>& info) {
-  const auto& p = info.param;
-  std::string name =
-      absl::StrCat("min_", p.min(), "__max_", p.max(), "__base_", p.base());
-  return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
-}
-
-INSTANTIATE_TEST_SUITE_P(All, LogUniformIntChiSquaredTest,
-                         ::testing::ValuesIn(GenParams()), ParamName);
-
-// NOTE: absl::log_uniform_int_distribution is not guaranteed to be stable.
-TEST(LogUniformIntDistributionTest, StabilityTest) {
-  using testing::ElementsAre;
-  // absl::uniform_int_distribution stability relies on
-  // absl::random_internal::LeadingSetBit, std::log, std::pow.
-  absl::random_internal::sequence_urbg urbg(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  std::vector<int> output(6);
-
-  {
-    absl::log_uniform_int_distribution<int32_t> dist(0, 256);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return dist(urbg); });
-    EXPECT_THAT(output, ElementsAre(256, 66, 4, 6, 57, 103));
-  }
-  urbg.reset();
-  {
-    absl::log_uniform_int_distribution<int32_t> dist(0, 256, 10);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return dist(urbg); });
-    EXPECT_THAT(output, ElementsAre(8, 4, 0, 0, 0, 69));
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/mock_distributions.h b/third_party/abseil_cpp/absl/random/mock_distributions.h
deleted file mode 100644
index 764ab370ab..0000000000
--- a/third_party/abseil_cpp/absl/random/mock_distributions.h
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: mock_distributions.h
-// -----------------------------------------------------------------------------
-//
-// This file contains mock distribution functions for use alongside an
-// `absl::MockingBitGen` object within the Googletest testing framework. Such
-// mocks are useful to provide deterministic values as return values within
-// (otherwise random) Abseil distribution functions.
-//
-// The return type of each function is a mock expectation object which
-// is used to set the match result.
-//
-// More information about the Googletest testing framework is available at
-// https://github.com/google/googletest
-//
-// EXPECT_CALL and ON_CALL need to be made within the same DLL component as
-// the call to absl::Uniform and related methods, otherwise mocking will fail
-// since the  underlying implementation creates a type-specific pointer which
-// will be distinct across different DLL boundaries.
-//
-// Example:
-//
-//   absl::MockingBitGen mock;
-//   EXPECT_CALL(absl::MockUniform<int>(), Call(mock, 1, 1000))
-//     .WillRepeatedly(testing::ReturnRoundRobin({20, 40}));
-//
-//   EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 20);
-//   EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 40);
-//   EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 20);
-//   EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 40);
-
-#ifndef ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_
-#define ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_
-
-#include <limits>
-#include <type_traits>
-#include <utility>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-#include "absl/random/distributions.h"
-#include "absl/random/internal/mock_overload_set.h"
-#include "absl/random/mocking_bit_gen.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// absl::MockUniform
-// -----------------------------------------------------------------------------
-//
-// Matches calls to absl::Uniform.
-//
-// `absl::MockUniform` is a class template used in conjunction with Googletest's
-// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an
-// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the
-// same way one would define mocks on a Googletest `MockFunction()`.
-//
-// Example:
-//
-//  absl::MockingBitGen mock;
-//  EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(mock))
-//     .WillOnce(Return(123456));
-//  auto x = absl::Uniform<uint32_t>(mock);
-//  assert(x == 123456)
-//
-template <typename R>
-using MockUniform = random_internal::MockOverloadSet<
-    random_internal::UniformDistributionWrapper<R>,
-    R(IntervalClosedOpenTag, MockingBitGen&, R, R),
-    R(IntervalClosedClosedTag, MockingBitGen&, R, R),
-    R(IntervalOpenOpenTag, MockingBitGen&, R, R),
-    R(IntervalOpenClosedTag, MockingBitGen&, R, R), R(MockingBitGen&, R, R),
-    R(MockingBitGen&)>;
-
-// -----------------------------------------------------------------------------
-// absl::MockBernoulli
-// -----------------------------------------------------------------------------
-//
-// Matches calls to absl::Bernoulli.
-//
-// `absl::MockBernoulli` is a class used in conjunction with Googletest's
-// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an
-// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the
-// same way one would define mocks on a Googletest `MockFunction()`.
-//
-// Example:
-//
-//  absl::MockingBitGen mock;
-//  EXPECT_CALL(absl::MockBernoulli(), Call(mock, testing::_))
-//     .WillOnce(Return(false));
-//  assert(absl::Bernoulli(mock, 0.5) == false);
-//
-using MockBernoulli =
-    random_internal::MockOverloadSet<absl::bernoulli_distribution,
-                                     bool(MockingBitGen&, double)>;
-
-// -----------------------------------------------------------------------------
-// absl::MockBeta
-// -----------------------------------------------------------------------------
-//
-// Matches calls to absl::Beta.
-//
-// `absl::MockBeta` is a class used in conjunction with Googletest's `ON_CALL()`
-// and `EXPECT_CALL()` macros. To use it, default-construct an instance of it
-// inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the same way one
-// would define mocks on a Googletest `MockFunction()`.
-//
-// Example:
-//
-//  absl::MockingBitGen mock;
-//  EXPECT_CALL(absl::MockBeta(), Call(mock, 3.0, 2.0))
-//     .WillOnce(Return(0.567));
-//  auto x = absl::Beta<double>(mock, 3.0, 2.0);
-//  assert(x == 0.567);
-//
-template <typename RealType>
-using MockBeta =
-    random_internal::MockOverloadSet<absl::beta_distribution<RealType>,
-                                     RealType(MockingBitGen&, RealType,
-                                              RealType)>;
-
-// -----------------------------------------------------------------------------
-// absl::MockExponential
-// -----------------------------------------------------------------------------
-//
-// Matches calls to absl::Exponential.
-//
-// `absl::MockExponential` is a class template used in conjunction with
-// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it,
-// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`,
-// and use `Call(...)` the same way one would define mocks on a
-// Googletest `MockFunction()`.
-//
-// Example:
-//
-//  absl::MockingBitGen mock;
-//  EXPECT_CALL(absl::MockExponential<double>(), Call(mock, 0.5))
-//     .WillOnce(Return(12.3456789));
-//  auto x = absl::Exponential<double>(mock, 0.5);
-//  assert(x == 12.3456789)
-//
-template <typename RealType>
-using MockExponential =
-    random_internal::MockOverloadSet<absl::exponential_distribution<RealType>,
-                                     RealType(MockingBitGen&, RealType)>;
-
-// -----------------------------------------------------------------------------
-// absl::MockGaussian
-// -----------------------------------------------------------------------------
-//
-// Matches calls to absl::Gaussian.
-//
-// `absl::MockGaussian` is a class template used in conjunction with
-// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it,
-// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`,
-// and use `Call(...)` the same way one would define mocks on a
-// Googletest `MockFunction()`.
-//
-// Example:
-//
-//  absl::MockingBitGen mock;
-//  EXPECT_CALL(absl::MockGaussian<double>(), Call(mock, 16.3, 3.3))
-//     .WillOnce(Return(12.3456789));
-//  auto x = absl::Gaussian<double>(mock, 16.3, 3.3);
-//  assert(x == 12.3456789)
-//
-template <typename RealType>
-using MockGaussian =
-    random_internal::MockOverloadSet<absl::gaussian_distribution<RealType>,
-                                     RealType(MockingBitGen&, RealType,
-                                              RealType)>;
-
-// -----------------------------------------------------------------------------
-// absl::MockLogUniform
-// -----------------------------------------------------------------------------
-//
-// Matches calls to absl::LogUniform.
-//
-// `absl::MockLogUniform` is a class template used in conjunction with
-// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it,
-// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`,
-// and use `Call(...)` the same way one would define mocks on a
-// Googletest `MockFunction()`.
-//
-// Example:
-//
-//  absl::MockingBitGen mock;
-//  EXPECT_CALL(absl::MockLogUniform<int>(), Call(mock, 10, 10000, 10))
-//     .WillOnce(Return(1221));
-//  auto x = absl::LogUniform<int>(mock, 10, 10000, 10);
-//  assert(x == 1221)
-//
-template <typename IntType>
-using MockLogUniform = random_internal::MockOverloadSet<
-    absl::log_uniform_int_distribution<IntType>,
-    IntType(MockingBitGen&, IntType, IntType, IntType)>;
-
-// -----------------------------------------------------------------------------
-// absl::MockPoisson
-// -----------------------------------------------------------------------------
-//
-// Matches calls to absl::Poisson.
-//
-// `absl::MockPoisson` is a class template used in conjunction with Googletest's
-// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an
-// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the
-// same way one would define mocks on a Googletest `MockFunction()`.
-//
-// Example:
-//
-//  absl::MockingBitGen mock;
-//  EXPECT_CALL(absl::MockPoisson<int>(), Call(mock, 2.0))
-//     .WillOnce(Return(1221));
-//  auto x = absl::Poisson<int>(mock, 2.0);
-//  assert(x == 1221)
-//
-template <typename IntType>
-using MockPoisson =
-    random_internal::MockOverloadSet<absl::poisson_distribution<IntType>,
-                                     IntType(MockingBitGen&, double)>;
-
-// -----------------------------------------------------------------------------
-// absl::MockZipf
-// -----------------------------------------------------------------------------
-//
-// Matches calls to absl::Zipf.
-//
-// `absl::MockZipf` is a class template used in conjunction with Googletest's
-// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an
-// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the
-// same way one would define mocks on a Googletest `MockFunction()`.
-//
-// Example:
-//
-//  absl::MockingBitGen mock;
-//  EXPECT_CALL(absl::MockZipf<int>(), Call(mock, 1000000, 2.0, 1.0))
-//     .WillOnce(Return(1221));
-//  auto x = absl::Zipf<int>(mock, 1000000, 2.0, 1.0);
-//  assert(x == 1221)
-//
-template <typename IntType>
-using MockZipf =
-    random_internal::MockOverloadSet<absl::zipf_distribution<IntType>,
-                                     IntType(MockingBitGen&, IntType, double,
-                                             double)>;
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_
diff --git a/third_party/abseil_cpp/absl/random/mock_distributions_test.cc b/third_party/abseil_cpp/absl/random/mock_distributions_test.cc
deleted file mode 100644
index de23bafe1e..0000000000
--- a/third_party/abseil_cpp/absl/random/mock_distributions_test.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/mock_distributions.h"
-
-#include "gtest/gtest.h"
-#include "absl/random/mocking_bit_gen.h"
-#include "absl/random/random.h"
-
-namespace {
-using ::testing::Return;
-
-TEST(MockDistributions, Examples) {
-  absl::MockingBitGen gen;
-
-  EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20);
-  EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
-      .WillOnce(Return(20));
-  EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20);
-
-  EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
-  EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0))
-      .WillOnce(Return(5.0));
-  EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
-
-  EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42);
-  EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
-      .WillOnce(Return(42));
-  EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42);
-
-  EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500);
-  EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(500));
-  EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500);
-
-  EXPECT_NE(absl::Bernoulli(gen, 0.000001), true);
-  EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001))
-      .WillOnce(Return(true));
-  EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true);
-
-  EXPECT_NE(absl::Beta<double>(gen, 3.0, 2.0), 0.567);
-  EXPECT_CALL(absl::MockBeta<double>(), Call(gen, 3.0, 2.0))
-      .WillOnce(Return(0.567));
-  EXPECT_EQ(absl::Beta<double>(gen, 3.0, 2.0), 0.567);
-
-  EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
-  EXPECT_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0))
-      .WillOnce(Return(1221));
-  EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
-
-  EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
-  EXPECT_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0))
-      .WillOnce(Return(0.001));
-  EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
-
-  EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040);
-  EXPECT_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2))
-      .WillOnce(Return(2040));
-  EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/mocking_bit_gen.h b/third_party/abseil_cpp/absl/random/mocking_bit_gen.h
deleted file mode 100644
index 6d2f2c8362..0000000000
--- a/third_party/abseil_cpp/absl/random/mocking_bit_gen.h
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// mocking_bit_gen.h
-// -----------------------------------------------------------------------------
-//
-// This file includes an `absl::MockingBitGen` class to use as a mock within the
-// Googletest testing framework. Such a mock is useful to provide deterministic
-// values as return values within (otherwise random) Abseil distribution
-// functions. Such determinism within a mock is useful within testing frameworks
-// to test otherwise indeterminate APIs.
-//
-// More information about the Googletest testing framework is available at
-// https://github.com/google/googletest
-
-#ifndef ABSL_RANDOM_MOCKING_BIT_GEN_H_
-#define ABSL_RANDOM_MOCKING_BIT_GEN_H_
-
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/fast_type_id.h"
-#include "absl/container/flat_hash_map.h"
-#include "absl/meta/type_traits.h"
-#include "absl/random/distributions.h"
-#include "absl/random/internal/distribution_caller.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_join.h"
-#include "absl/types/span.h"
-#include "absl/types/variant.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace random_internal {
-template <typename>
-struct DistributionCaller;
-class MockHelpers;
-
-}  // namespace random_internal
-class BitGenRef;
-
-// MockingBitGen
-//
-// `absl::MockingBitGen` is a mock Uniform Random Bit Generator (URBG) class
-// which can act in place of an `absl::BitGen` URBG within tests using the
-// Googletest testing framework.
-//
-// Usage:
-//
-// Use an `absl::MockingBitGen` along with a mock distribution object (within
-// mock_distributions.h) inside Googletest constructs such as ON_CALL(),
-// EXPECT_TRUE(), etc. to produce deterministic results conforming to the
-// distribution's API contract.
-//
-// Example:
-//
-//  // Mock a call to an `absl::Bernoulli` distribution using Googletest
-//   absl::MockingBitGen bitgen;
-//
-//   ON_CALL(absl::MockBernoulli(), Call(bitgen, 0.5))
-//       .WillByDefault(testing::Return(true));
-//   EXPECT_TRUE(absl::Bernoulli(bitgen, 0.5));
-//
-//  // Mock a call to an `absl::Uniform` distribution within Googletest
-//  absl::MockingBitGen bitgen;
-//
-//   ON_CALL(absl::MockUniform<int>(), Call(bitgen, testing::_, testing::_))
-//       .WillByDefault([] (int low, int high) {
-//           return (low + high) / 2;
-//       });
-//
-//   EXPECT_EQ(absl::Uniform<int>(gen, 0, 10), 5);
-//   EXPECT_EQ(absl::Uniform<int>(gen, 30, 40), 35);
-//
-// At this time, only mock distributions supplied within the Abseil random
-// library are officially supported.
-//
-// EXPECT_CALL and ON_CALL need to be made within the same DLL component as
-// the call to absl::Uniform and related methods, otherwise mocking will fail
-// since the  underlying implementation creates a type-specific pointer which
-// will be distinct across different DLL boundaries.
-//
-class MockingBitGen {
- public:
-  MockingBitGen() = default;
-
-  ~MockingBitGen() {
-    for (const auto& del : deleters_) del();
-  }
-
-  // URBG interface
-  using result_type = absl::BitGen::result_type;
-
-  static constexpr result_type(min)() { return (absl::BitGen::min)(); }
-  static constexpr result_type(max)() { return (absl::BitGen::max)(); }
-  result_type operator()() { return gen_(); }
-
- private:
-  using match_impl_fn = void (*)(void* mock_fn, void* t_erased_arg_tuple,
-                                 void* t_erased_result);
-
-  struct MockData {
-    void* mock_fn = nullptr;
-    match_impl_fn match_impl = nullptr;
-  };
-
-  // GetMockFnType returns the testing::MockFunction for a result and tuple.
-  // This method only exists for type deduction and is otherwise unimplemented.
-  template <typename ResultT, typename... Args>
-  static auto GetMockFnType(ResultT, std::tuple<Args...>)
-      -> ::testing::MockFunction<ResultT(Args...)>;
-
-  // MockFnCaller is a helper method for use with absl::apply to
-  // apply an ArgTupleT to a compatible MockFunction.
-  // NOTE: MockFnCaller is essentially equivalent to the lambda:
-  // [fn](auto... args) { return fn->Call(std::move(args)...)}
-  // however that fails to build on some supported platforms.
-  template <typename ResultT, typename MockFnType, typename Tuple>
-  struct MockFnCaller;
-  // specialization for std::tuple.
-  template <typename ResultT, typename MockFnType, typename... Args>
-  struct MockFnCaller<ResultT, MockFnType, std::tuple<Args...>> {
-    MockFnType* fn;
-    inline ResultT operator()(Args... args) {
-      return fn->Call(std::move(args)...);
-    }
-  };
-
-  // MockingBitGen::RegisterMock
-  //
-  // RegisterMock<ResultT, ArgTupleT>(FastTypeIdType) is the main extension
-  // point for extending the MockingBitGen framework. It provides a mechanism to
-  // install a mock expectation for a function like ResultT(Args...) keyed by
-  // type_idex onto the MockingBitGen context. The key is that the type_index
-  // used to register must match the type index used to call the mock.
-  //
-  // The returned MockFunction<...> type can be used to setup additional
-  // distribution parameters of the expectation.
-  template <typename ResultT, typename ArgTupleT>
-  auto RegisterMock(base_internal::FastTypeIdType type)
-      -> decltype(GetMockFnType(std::declval<ResultT>(),
-                                std::declval<ArgTupleT>()))& {
-    using MockFnType = decltype(
-        GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>()));
-    auto& mock = mocks_[type];
-    if (!mock.mock_fn) {
-      auto* mock_fn = new MockFnType;
-      mock.mock_fn = mock_fn;
-      mock.match_impl = &MatchImpl<ResultT, ArgTupleT>;
-      deleters_.emplace_back([mock_fn] { delete mock_fn; });
-    }
-    return *static_cast<MockFnType*>(mock.mock_fn);
-  }
-
-  // MockingBitGen::MatchImpl<> is a dispatch function which converts the
-  // generic type-erased parameters into a specific mock invocation call.
-  // Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...>
-  // used to invoke the mock function.
-  // Requires result to point to a ResultT, which is the result of the call.
-  template <typename ResultT, typename ArgTupleT>
-  static void MatchImpl(/*MockFnType<ResultT, Args...>*/ void* mock_fn,
-                        /*ArgTupleT*/ void* args_tuple,
-                        /*ResultT*/ void* result) {
-    using MockFnType = decltype(
-        GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>()));
-    *static_cast<ResultT*>(result) = absl::apply(
-        MockFnCaller<ResultT, MockFnType, ArgTupleT>{
-            static_cast<MockFnType*>(mock_fn)},
-        *static_cast<ArgTupleT*>(args_tuple));
-  }
-
-  // MockingBitGen::InvokeMock
-  //
-  // InvokeMock(FastTypeIdType, args, result) is the entrypoint for invoking
-  // mocks registered on MockingBitGen.
-  //
-  // When no mocks are registered on the provided FastTypeIdType, returns false.
-  // Otherwise attempts to invoke the mock function ResultT(Args...) that
-  // was previously registered via the type_index.
-  // Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...>
-  // used to invoke the mock function.
-  // Requires result to point to a ResultT, which is the result of the call.
-  inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple,
-                         void* result) {
-    // Trigger a mock, if there exists one that matches `param`.
-    auto it = mocks_.find(type);
-    if (it == mocks_.end()) return false;
-    auto* mock_data = static_cast<MockData*>(&it->second);
-    mock_data->match_impl(mock_data->mock_fn, args_tuple, result);
-    return true;
-  }
-
-  absl::flat_hash_map<base_internal::FastTypeIdType, MockData> mocks_;
-  std::vector<std::function<void()>> deleters_;
-  absl::BitGen gen_;
-
-  template <typename>
-  friend struct ::absl::random_internal::DistributionCaller;  // for InvokeMock
-  friend class ::absl::BitGenRef;                             // for InvokeMock
-  friend class ::absl::random_internal::MockHelpers;  // for RegisterMock,
-                                                      // InvokeMock
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_MOCKING_BIT_GEN_H_
diff --git a/third_party/abseil_cpp/absl/random/mocking_bit_gen_test.cc b/third_party/abseil_cpp/absl/random/mocking_bit_gen_test.cc
deleted file mode 100644
index f0ffc9ac92..0000000000
--- a/third_party/abseil_cpp/absl/random/mocking_bit_gen_test.cc
+++ /dev/null
@@ -1,347 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-#include "absl/random/mocking_bit_gen.h"
-
-#include <numeric>
-#include <random>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest-spi.h"
-#include "gtest/gtest.h"
-#include "absl/random/bit_gen_ref.h"
-#include "absl/random/mock_distributions.h"
-#include "absl/random/random.h"
-
-namespace {
-using ::testing::Ne;
-using ::testing::Return;
-
-TEST(BasicMocking, AllDistributionsAreOverridable) {
-  absl::MockingBitGen gen;
-
-  EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20);
-  EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
-      .WillOnce(Return(20));
-  EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20);
-
-  EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
-  EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0))
-      .WillOnce(Return(5.0));
-  EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
-
-  EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42);
-  EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
-      .WillOnce(Return(42));
-  EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42);
-
-  EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500);
-  EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(500));
-  EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500);
-
-  EXPECT_NE(absl::Bernoulli(gen, 0.000001), true);
-  EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001))
-      .WillOnce(Return(true));
-  EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true);
-
-  EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
-  EXPECT_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0))
-      .WillOnce(Return(1221));
-  EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
-
-  EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
-  EXPECT_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0))
-      .WillOnce(Return(0.001));
-  EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
-
-  EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 500000);
-  EXPECT_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2))
-      .WillOnce(Return(500000));
-  EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 500000);
-}
-
-TEST(BasicMocking, OnDistribution) {
-  absl::MockingBitGen gen;
-
-  EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20);
-  ON_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
-      .WillByDefault(Return(20));
-  EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20);
-
-  EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
-  ON_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0))
-      .WillByDefault(Return(5.0));
-  EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
-
-  EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42);
-  ON_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
-      .WillByDefault(Return(42));
-  EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42);
-
-  EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500);
-  ON_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillByDefault(Return(500));
-  EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500);
-
-  EXPECT_NE(absl::Bernoulli(gen, 0.000001), true);
-  ON_CALL(absl::MockBernoulli(), Call(gen, 0.000001))
-      .WillByDefault(Return(true));
-  EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true);
-
-  EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
-  ON_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0))
-      .WillByDefault(Return(1221));
-  EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
-
-  EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
-  ON_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0))
-      .WillByDefault(Return(0.001));
-  EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
-
-  EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040);
-  ON_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2))
-      .WillByDefault(Return(2040));
-  EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040);
-}
-
-TEST(BasicMocking, GMockMatchers) {
-  absl::MockingBitGen gen;
-
-  EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
-  ON_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0))
-      .WillByDefault(Return(1221));
-  EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
-}
-
-TEST(BasicMocking, OverridesWithMultipleGMockExpectations) {
-  absl::MockingBitGen gen;
-
-  EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 10000))
-      .WillOnce(Return(20))
-      .WillOnce(Return(40))
-      .WillOnce(Return(60));
-  EXPECT_EQ(absl::Uniform(gen, 1, 10000), 20);
-  EXPECT_EQ(absl::Uniform(gen, 1, 10000), 40);
-  EXPECT_EQ(absl::Uniform(gen, 1, 10000), 60);
-}
-
-TEST(BasicMocking, DefaultArgument) {
-  absl::MockingBitGen gen;
-
-  ON_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
-      .WillByDefault(Return(200));
-
-  EXPECT_EQ(absl::Exponential<double>(gen), 200);
-  EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 200);
-}
-
-TEST(BasicMocking, MultipleGenerators) {
-  auto get_value = [](absl::BitGenRef gen_ref) {
-    return absl::Uniform(gen_ref, 1, 1000000);
-  };
-  absl::MockingBitGen unmocked_generator;
-  absl::MockingBitGen mocked_with_3;
-  absl::MockingBitGen mocked_with_11;
-
-  EXPECT_CALL(absl::MockUniform<int>(), Call(mocked_with_3, 1, 1000000))
-      .WillOnce(Return(3))
-      .WillRepeatedly(Return(17));
-  EXPECT_CALL(absl::MockUniform<int>(), Call(mocked_with_11, 1, 1000000))
-      .WillOnce(Return(11))
-      .WillRepeatedly(Return(17));
-
-  // Ensure that unmocked generator generates neither value.
-  int unmocked_value = get_value(unmocked_generator);
-  EXPECT_NE(unmocked_value, 3);
-  EXPECT_NE(unmocked_value, 11);
-  // Mocked generators should generate their mocked values.
-  EXPECT_EQ(get_value(mocked_with_3), 3);
-  EXPECT_EQ(get_value(mocked_with_11), 11);
-  // Ensure that the mocks have expired.
-  EXPECT_NE(get_value(mocked_with_3), 3);
-  EXPECT_NE(get_value(mocked_with_11), 11);
-}
-
-TEST(BasicMocking, MocksNotTrigeredForIncorrectTypes) {
-  absl::MockingBitGen gen;
-  EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(gen)).WillOnce(Return(42));
-
-  EXPECT_NE(absl::Uniform<uint16_t>(gen), 42);  // Not mocked
-  EXPECT_EQ(absl::Uniform<uint32_t>(gen), 42);  // Mock triggered
-}
-
-TEST(BasicMocking, FailsOnUnsatisfiedMocks) {
-  EXPECT_NONFATAL_FAILURE(
-      []() {
-        absl::MockingBitGen gen;
-        EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
-            .WillOnce(Return(3.0));
-        // Does not call absl::Exponential().
-      }(),
-      "unsatisfied and active");
-}
-
-TEST(OnUniform, RespectsUniformIntervalSemantics) {
-  absl::MockingBitGen gen;
-
-  EXPECT_CALL(absl::MockUniform<int>(),
-              Call(absl::IntervalClosed, gen, 1, 1000000))
-      .WillOnce(Return(301));
-  EXPECT_NE(absl::Uniform(gen, 1, 1000000), 301);  // Not mocked
-  EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 1, 1000000), 301);
-}
-
-TEST(OnUniform, RespectsNoArgUnsignedShorthand) {
-  absl::MockingBitGen gen;
-  EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(gen)).WillOnce(Return(42));
-  EXPECT_EQ(absl::Uniform<uint32_t>(gen), 42);
-}
-
-TEST(RepeatedlyModifier, ForceSnakeEyesForManyDice) {
-  auto roll_some_dice = [](absl::BitGenRef gen_ref) {
-    std::vector<int> results(16);
-    for (auto& r : results) {
-      r = absl::Uniform(absl::IntervalClosed, gen_ref, 1, 6);
-    }
-    return results;
-  };
-  std::vector<int> results;
-  absl::MockingBitGen gen;
-
-  // Without any mocked calls, not all dice roll a "6".
-  results = roll_some_dice(gen);
-  EXPECT_LT(std::accumulate(std::begin(results), std::end(results), 0),
-            results.size() * 6);
-
-  // Verify that we can force all "6"-rolls, with mocking.
-  ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalClosed, gen, 1, 6))
-      .WillByDefault(Return(6));
-  results = roll_some_dice(gen);
-  EXPECT_EQ(std::accumulate(std::begin(results), std::end(results), 0),
-            results.size() * 6);
-}
-
-TEST(WillOnce, DistinctCounters) {
-  absl::MockingBitGen gen;
-  EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
-      .Times(3)
-      .WillRepeatedly(Return(0));
-  EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1000001, 2000000))
-      .Times(3)
-      .WillRepeatedly(Return(1));
-  EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1);
-  EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0);
-  EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1);
-  EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0);
-  EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1);
-  EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0);
-}
-
-TEST(TimesModifier, ModifierSaturatesAndExpires) {
-  EXPECT_NONFATAL_FAILURE(
-      []() {
-        absl::MockingBitGen gen;
-        EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
-            .Times(3)
-            .WillRepeatedly(Return(15))
-            .RetiresOnSaturation();
-
-        EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15);
-        EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15);
-        EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15);
-        // Times(3) has expired - Should get a different value now.
-
-        EXPECT_NE(absl::Uniform(gen, 1, 1000000), 15);
-      }(),
-      "");
-}
-
-TEST(TimesModifier, Times0) {
-  absl::MockingBitGen gen;
-  EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.0)).Times(0);
-  EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).Times(0);
-}
-
-TEST(AnythingMatcher, MatchesAnyArgument) {
-  using testing::_;
-
-  {
-    absl::MockingBitGen gen;
-    ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalClosed, gen, _, 1000))
-        .WillByDefault(Return(11));
-    ON_CALL(absl::MockUniform<int>(),
-            Call(absl::IntervalClosed, gen, _, Ne(1000)))
-        .WillByDefault(Return(99));
-
-    EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000000), 99);
-    EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000), 11);
-  }
-
-  {
-    absl::MockingBitGen gen;
-    ON_CALL(absl::MockUniform<int>(), Call(gen, 1, _))
-        .WillByDefault(Return(25));
-    ON_CALL(absl::MockUniform<int>(), Call(gen, Ne(1), _))
-        .WillByDefault(Return(99));
-    EXPECT_EQ(absl::Uniform(gen, 3, 1000000), 99);
-    EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 25);
-  }
-
-  {
-    absl::MockingBitGen gen;
-    ON_CALL(absl::MockUniform<int>(), Call(gen, _, _))
-        .WillByDefault(Return(145));
-    EXPECT_EQ(absl::Uniform(gen, 1, 1000), 145);
-    EXPECT_EQ(absl::Uniform(gen, 10, 1000), 145);
-    EXPECT_EQ(absl::Uniform(gen, 100, 1000), 145);
-  }
-}
-
-TEST(AnythingMatcher, WithWillByDefault) {
-  using testing::_;
-  absl::MockingBitGen gen;
-  std::vector<int> values = {11, 22, 33, 44, 55, 66, 77, 88, 99, 1010};
-
-  ON_CALL(absl::MockUniform<size_t>(), Call(gen, 0, _))
-      .WillByDefault(Return(0));
-  for (int i = 0; i < 100; i++) {
-    auto& elem = values[absl::Uniform(gen, 0u, values.size())];
-    EXPECT_EQ(elem, 11);
-  }
-}
-
-TEST(BasicMocking, WillByDefaultWithArgs) {
-  using testing::_;
-
-  absl::MockingBitGen gen;
-  ON_CALL(absl::MockPoisson<int>(), Call(gen, _))
-      .WillByDefault(
-          [](double lambda) { return static_cast<int>(lambda * 10); });
-  EXPECT_EQ(absl::Poisson<int>(gen, 1.7), 17);
-  EXPECT_EQ(absl::Poisson<int>(gen, 0.03), 0);
-}
-
-TEST(MockingBitGen, InSequenceSucceedsInOrder) {
-  absl::MockingBitGen gen;
-
-  testing::InSequence seq;
-
-  EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(3));
-  EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 2.0)).WillOnce(Return(4));
-
-  EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 3);
-  EXPECT_EQ(absl::Poisson<int>(gen, 2.0), 4);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/poisson_distribution.h b/third_party/abseil_cpp/absl/random/poisson_distribution.h
deleted file mode 100644
index cb5f5d5d0f..0000000000
--- a/third_party/abseil_cpp/absl/random/poisson_distribution.h
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_POISSON_DISTRIBUTION_H_
-#define ABSL_RANDOM_POISSON_DISTRIBUTION_H_
-
-#include <cassert>
-#include <cmath>
-#include <istream>
-#include <limits>
-#include <ostream>
-#include <type_traits>
-
-#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/random/internal/fastmath.h"
-#include "absl/random/internal/generate_real.h"
-#include "absl/random/internal/iostream_state_saver.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::poisson_distribution:
-// Generates discrete variates conforming to a Poisson distribution.
-//   p(n) = (mean^n / n!) exp(-mean)
-//
-// Depending on the parameter, the distribution selects one of the following
-// algorithms:
-// * The standard algorithm, attributed to Knuth, extended using a split method
-// for larger values
-// * The "Ratio of Uniforms as a convenient method for sampling from classical
-// discrete distributions", Stadlober, 1989.
-// http://www.sciencedirect.com/science/article/pii/0377042790903495
-//
-// NOTE: param_type.mean() is a double, which permits values larger than
-// poisson_distribution<IntType>::max(), however this should be avoided and
-// the distribution results are limited to the max() value.
-//
-// The goals of this implementation are to provide good performance while still
-// beig thread-safe: This limits the implementation to not using lgamma provided
-// by <math.h>.
-//
-template <typename IntType = int>
-class poisson_distribution {
- public:
-  using result_type = IntType;
-
-  class param_type {
-   public:
-    using distribution_type = poisson_distribution;
-    explicit param_type(double mean = 1.0);
-
-    double mean() const { return mean_; }
-
-    friend bool operator==(const param_type& a, const param_type& b) {
-      return a.mean_ == b.mean_;
-    }
-
-    friend bool operator!=(const param_type& a, const param_type& b) {
-      return !(a == b);
-    }
-
-   private:
-    friend class poisson_distribution;
-
-    double mean_;
-    double emu_;  // e ^ -mean_
-    double lmu_;  // ln(mean_)
-    double s_;
-    double log_k_;
-    int split_;
-
-    static_assert(std::is_integral<IntType>::value,
-                  "Class-template absl::poisson_distribution<> must be "
-                  "parameterized using an integral type.");
-  };
-
-  poisson_distribution() : poisson_distribution(1.0) {}
-
-  explicit poisson_distribution(double mean) : param_(mean) {}
-
-  explicit poisson_distribution(const param_type& p) : param_(p) {}
-
-  void reset() {}
-
-  // generating functions
-  template <typename URBG>
-  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
-    return (*this)(g, param_);
-  }
-
-  template <typename URBG>
-  result_type operator()(URBG& g,  // NOLINT(runtime/references)
-                         const param_type& p);
-
-  param_type param() const { return param_; }
-  void param(const param_type& p) { param_ = p; }
-
-  result_type(min)() const { return 0; }
-  result_type(max)() const { return (std::numeric_limits<result_type>::max)(); }
-
-  double mean() const { return param_.mean(); }
-
-  friend bool operator==(const poisson_distribution& a,
-                         const poisson_distribution& b) {
-    return a.param_ == b.param_;
-  }
-  friend bool operator!=(const poisson_distribution& a,
-                         const poisson_distribution& b) {
-    return a.param_ != b.param_;
-  }
-
- private:
-  param_type param_;
-  random_internal::FastUniformBits<uint64_t> fast_u64_;
-};
-
-// -----------------------------------------------------------------------------
-// Implementation details follow
-// -----------------------------------------------------------------------------
-
-template <typename IntType>
-poisson_distribution<IntType>::param_type::param_type(double mean)
-    : mean_(mean), split_(0) {
-  assert(mean >= 0);
-  assert(mean <= (std::numeric_limits<result_type>::max)());
-  // As a defensive measure, avoid large values of the mean.  The rejection
-  // algorithm used does not support very large values well.  It my be worth
-  // changing algorithms to better deal with these cases.
-  assert(mean <= 1e10);
-  if (mean_ < 10) {
-    // For small lambda, use the knuth method.
-    split_ = 1;
-    emu_ = std::exp(-mean_);
-  } else if (mean_ <= 50) {
-    // Use split-knuth method.
-    split_ = 1 + static_cast<int>(mean_ / 10.0);
-    emu_ = std::exp(-mean_ / static_cast<double>(split_));
-  } else {
-    // Use ratio of uniforms method.
-    constexpr double k2E = 0.7357588823428846;
-    constexpr double kSA = 0.4494580810294493;
-
-    lmu_ = std::log(mean_);
-    double a = mean_ + 0.5;
-    s_ = kSA + std::sqrt(k2E * a);
-    const double mode = std::ceil(mean_) - 1;
-    log_k_ = lmu_ * mode - absl::random_internal::StirlingLogFactorial(mode);
-  }
-}
-
-template <typename IntType>
-template <typename URBG>
-typename poisson_distribution<IntType>::result_type
-poisson_distribution<IntType>::operator()(
-    URBG& g,  // NOLINT(runtime/references)
-    const param_type& p) {
-  using random_internal::GeneratePositiveTag;
-  using random_internal::GenerateRealFromBits;
-  using random_internal::GenerateSignedTag;
-
-  if (p.split_ != 0) {
-    // Use Knuth's algorithm with range splitting to avoid floating-point
-    // errors. Knuth's algorithm is: Ui is a sequence of uniform variates on
-    // (0,1); return the number of variates required for product(Ui) <
-    // exp(-lambda).
-    //
-    // The expected number of variates required for Knuth's method can be
-    // computed as follows:
-    // The expected value of U is 0.5, so solving for 0.5^n < exp(-lambda) gives
-    // the expected number of uniform variates
-    // required for a given lambda, which is:
-    //  lambda = [2, 5,  9, 10, 11, 12, 13, 14, 15, 16, 17]
-    //  n      = [3, 8, 13, 15, 16, 18, 19, 21, 22, 24, 25]
-    //
-    result_type n = 0;
-    for (int split = p.split_; split > 0; --split) {
-      double r = 1.0;
-      do {
-        r *= GenerateRealFromBits<double, GeneratePositiveTag, true>(
-            fast_u64_(g));  // U(-1, 0)
-        ++n;
-      } while (r > p.emu_);
-      --n;
-    }
-    return n;
-  }
-
-  // Use ratio of uniforms method.
-  //
-  // Let u ~ Uniform(0, 1), v ~ Uniform(-1, 1),
-  //     a = lambda + 1/2,
-  //     s = 1.5 - sqrt(3/e) + sqrt(2(lambda + 1/2)/e),
-  //     x = s * v/u + a.
-  // P(floor(x) = k | u^2 < f(floor(x))/k), where
-  // f(m) = lambda^m exp(-lambda)/ m!, for 0 <= m, and f(m) = 0 otherwise,
-  // and k = max(f).
-  const double a = p.mean_ + 0.5;
-  for (;;) {
-    const double u = GenerateRealFromBits<double, GeneratePositiveTag, false>(
-        fast_u64_(g));  // U(0, 1)
-    const double v = GenerateRealFromBits<double, GenerateSignedTag, false>(
-        fast_u64_(g));  // U(-1, 1)
-
-    const double x = std::floor(p.s_ * v / u + a);
-    if (x < 0) continue;  // f(negative) = 0
-    const double rhs = x * p.lmu_;
-    // clang-format off
-    double s = (x <= 1.0) ? 0.0
-             : (x == 2.0) ? 0.693147180559945
-             : absl::random_internal::StirlingLogFactorial(x);
-    // clang-format on
-    const double lhs = 2.0 * std::log(u) + p.log_k_ + s;
-    if (lhs < rhs) {
-      return x > (max)() ? (max)()
-                         : static_cast<result_type>(x);  // f(x)/k >= u^2
-    }
-  }
-}
-
-template <typename CharT, typename Traits, typename IntType>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    const poisson_distribution<IntType>& x) {
-  auto saver = random_internal::make_ostream_state_saver(os);
-  os.precision(random_internal::stream_precision_helper<double>::kPrecision);
-  os << x.mean();
-  return os;
-}
-
-template <typename CharT, typename Traits, typename IntType>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-    poisson_distribution<IntType>& x) {     // NOLINT(runtime/references)
-  using param_type = typename poisson_distribution<IntType>::param_type;
-
-  auto saver = random_internal::make_istream_state_saver(is);
-  double mean = random_internal::read_floating_point<double>(is);
-  if (!is.fail()) {
-    x.param(param_type(mean));
-  }
-  return is;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_POISSON_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/poisson_distribution_test.cc b/third_party/abseil_cpp/absl/random/poisson_distribution_test.cc
deleted file mode 100644
index 8baabd1118..0000000000
--- a/third_party/abseil_cpp/absl/random/poisson_distribution_test.cc
+++ /dev/null
@@ -1,573 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/poisson_distribution.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <iterator>
-#include <random>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/container/flat_hash_map.h"
-#include "absl/random/internal/chi_square.h"
-#include "absl/random/internal/distribution_test_util.h"
-#include "absl/random/internal/pcg_engine.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_replace.h"
-#include "absl/strings/strip.h"
-
-// Notes about generating poisson variates:
-//
-// It is unlikely that any implementation of std::poisson_distribution
-// will be stable over time and across library implementations. For instance
-// the three different poisson variate generators listed below all differ:
-//
-// https://github.com/ampl/gsl/tree/master/randist/poisson.c
-// * GSL uses a gamma + binomial + knuth method to compute poisson variates.
-//
-// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/random.tcc
-// * GCC uses the Devroye rejection algorithm, based on
-// Devroye, L. Non-Uniform Random Variates Generation. Springer-Verlag,
-// New York, 1986, Ch. X, Sects. 3.3 & 3.4 (+ Errata!), ~p.511
-//   http://www.nrbook.com/devroye/
-//
-// https://github.com/llvm-mirror/libcxx/blob/master/include/random
-// * CLANG uses a different rejection method, which appears to include a
-// normal-distribution approximation and an exponential distribution to
-// compute the threshold, including a similar factorial approximation to this
-// one, but it is unclear where the algorithm comes from, exactly.
-//
-
-namespace {
-
-using absl::random_internal::kChiSquared;
-
-// The PoissonDistributionInterfaceTest provides a basic test that
-// absl::poisson_distribution conforms to the interface and serialization
-// requirements imposed by [rand.req.dist] for the common integer types.
-
-template <typename IntType>
-class PoissonDistributionInterfaceTest : public ::testing::Test {};
-
-using IntTypes = ::testing::Types<int, int8_t, int16_t, int32_t, int64_t,
-                                  uint8_t, uint16_t, uint32_t, uint64_t>;
-TYPED_TEST_CASE(PoissonDistributionInterfaceTest, IntTypes);
-
-TYPED_TEST(PoissonDistributionInterfaceTest, SerializeTest) {
-  using param_type = typename absl::poisson_distribution<TypeParam>::param_type;
-  const double kMax =
-      std::min(1e10 /* assertion limit */,
-               static_cast<double>(std::numeric_limits<TypeParam>::max()));
-
-  const double kParams[] = {
-      // Cases around 1.
-      1,                         //
-      std::nextafter(1.0, 0.0),  // 1 - epsilon
-      std::nextafter(1.0, 2.0),  // 1 + epsilon
-      // Arbitrary values.
-      1e-8, 1e-4,
-      0.0000005,  // ~7.2e-7
-      0.2,        // ~0.2x
-      0.5,        // 0.72
-      2,          // ~2.8
-      20,         // 3x ~9.6
-      100, 1e4, 1e8, 1.5e9, 1e20,
-      // Boundary cases.
-      std::numeric_limits<double>::max(),
-      std::numeric_limits<double>::epsilon(),
-      std::nextafter(std::numeric_limits<double>::min(),
-                     1.0),                        // min + epsilon
-      std::numeric_limits<double>::min(),         // smallest normal
-      std::numeric_limits<double>::denorm_min(),  // smallest denorm
-      std::numeric_limits<double>::min() / 2,     // denorm
-      std::nextafter(std::numeric_limits<double>::min(),
-                     0.0),  // denorm_max
-  };
-
-
-  constexpr int kCount = 1000;
-  absl::InsecureBitGen gen;
-  for (const double m : kParams) {
-    const double mean = std::min(kMax, m);
-    const param_type param(mean);
-
-    // Validate parameters.
-    absl::poisson_distribution<TypeParam> before(mean);
-    EXPECT_EQ(before.mean(), param.mean());
-
-    {
-      absl::poisson_distribution<TypeParam> via_param(param);
-      EXPECT_EQ(via_param, before);
-      EXPECT_EQ(via_param.param(), before.param());
-    }
-
-    // Smoke test.
-    auto sample_min = before.max();
-    auto sample_max = before.min();
-    for (int i = 0; i < kCount; i++) {
-      auto sample = before(gen);
-      EXPECT_GE(sample, before.min());
-      EXPECT_LE(sample, before.max());
-      if (sample > sample_max) sample_max = sample;
-      if (sample < sample_min) sample_min = sample;
-    }
-
-    ABSL_INTERNAL_LOG(INFO, absl::StrCat("Range {", param.mean(), "}: ",
-                                         +sample_min, ", ", +sample_max));
-
-    // Validate stream serialization.
-    std::stringstream ss;
-    ss << before;
-
-    absl::poisson_distribution<TypeParam> after(3.8);
-
-    EXPECT_NE(before.mean(), after.mean());
-    EXPECT_NE(before.param(), after.param());
-    EXPECT_NE(before, after);
-
-    ss >> after;
-
-    EXPECT_EQ(before.mean(), after.mean())  //
-        << ss.str() << " "                  //
-        << (ss.good() ? "good " : "")       //
-        << (ss.bad() ? "bad " : "")         //
-        << (ss.eof() ? "eof " : "")         //
-        << (ss.fail() ? "fail " : "");
-  }
-}
-
-// See http://www.itl.nist.gov/div898/handbook/eda/section3/eda366j.htm
-
-class PoissonModel {
- public:
-  explicit PoissonModel(double mean) : mean_(mean) {}
-
-  double mean() const { return mean_; }
-  double variance() const { return mean_; }
-  double stddev() const { return std::sqrt(variance()); }
-  double skew() const { return 1.0 / mean_; }
-  double kurtosis() const { return 3.0 + 1.0 / mean_; }
-
-  // InitCDF() initializes the CDF for the distribution parameters.
-  void InitCDF();
-
-  // The InverseCDF, or the Percent-point function returns x, P(x) < v.
-  struct CDF {
-    size_t index;
-    double pmf;
-    double cdf;
-  };
-  CDF InverseCDF(double p) {
-    CDF target{0, 0, p};
-    auto it = std::upper_bound(
-        std::begin(cdf_), std::end(cdf_), target,
-        [](const CDF& a, const CDF& b) { return a.cdf < b.cdf; });
-    return *it;
-  }
-
-  void LogCDF() {
-    ABSL_INTERNAL_LOG(INFO, absl::StrCat("CDF (mean = ", mean_, ")"));
-    for (const auto c : cdf_) {
-      ABSL_INTERNAL_LOG(INFO,
-                        absl::StrCat(c.index, ": pmf=", c.pmf, " cdf=", c.cdf));
-    }
-  }
-
- private:
-  const double mean_;
-
-  std::vector<CDF> cdf_;
-};
-
-// The goal is to compute an InverseCDF function, or percent point function for
-// the poisson distribution, and use that to partition our output into equal
-// range buckets.  However there is no closed form solution for the inverse cdf
-// for poisson distributions (the closest is the incomplete gamma function).
-// Instead, `InitCDF` iteratively computes the PMF and the CDF. This enables
-// searching for the bucket points.
-void PoissonModel::InitCDF() {
-  if (!cdf_.empty()) {
-    // State already initialized.
-    return;
-  }
-  ABSL_ASSERT(mean_ < 201.0);
-
-  const size_t max_i = 50 * stddev() + mean();
-  const double e_neg_mean = std::exp(-mean());
-  ABSL_ASSERT(e_neg_mean > 0);
-
-  double d = 1;
-  double last_result = e_neg_mean;
-  double cumulative = e_neg_mean;
-  if (e_neg_mean > 1e-10) {
-    cdf_.push_back({0, e_neg_mean, cumulative});
-  }
-  for (size_t i = 1; i < max_i; i++) {
-    d *= (mean() / i);
-    double result = e_neg_mean * d;
-    cumulative += result;
-    if (result < 1e-10 && result < last_result && cumulative > 0.999999) {
-      break;
-    }
-    if (result > 1e-7) {
-      cdf_.push_back({i, result, cumulative});
-    }
-    last_result = result;
-  }
-  ABSL_ASSERT(!cdf_.empty());
-}
-
-// PoissonDistributionZTest implements a z-test for the poisson distribution.
-
-struct ZParam {
-  double mean;
-  double p_fail;   // Z-Test probability of failure.
-  int trials;      // Z-Test trials.
-  size_t samples;  // Z-Test samples.
-};
-
-class PoissonDistributionZTest : public testing::TestWithParam<ZParam>,
-                                 public PoissonModel {
- public:
-  PoissonDistributionZTest() : PoissonModel(GetParam().mean) {}
-
-  // ZTestImpl provides a basic z-squared test of the mean vs. expected
-  // mean for data generated by the poisson distribution.
-  template <typename D>
-  bool SingleZTest(const double p, const size_t samples);
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6};
-};
-
-template <typename D>
-bool PoissonDistributionZTest::SingleZTest(const double p,
-                                           const size_t samples) {
-  D dis(mean());
-
-  absl::flat_hash_map<int32_t, int> buckets;
-  std::vector<double> data;
-  data.reserve(samples);
-  for (int j = 0; j < samples; j++) {
-    const auto x = dis(rng_);
-    buckets[x]++;
-    data.push_back(x);
-  }
-
-  // The null-hypothesis is that the distribution is a poisson distribution with
-  // the provided mean (not estimated from the data).
-  const auto m = absl::random_internal::ComputeDistributionMoments(data);
-  const double max_err = absl::random_internal::MaxErrorTolerance(p);
-  const double z = absl::random_internal::ZScore(mean(), m);
-  const bool pass = absl::random_internal::Near("z", z, 0.0, max_err);
-
-  if (!pass) {
-    ABSL_INTERNAL_LOG(
-        INFO, absl::StrFormat("p=%f max_err=%f\n"
-                              " mean=%f vs. %f\n"
-                              " stddev=%f vs. %f\n"
-                              " skewness=%f vs. %f\n"
-                              " kurtosis=%f vs. %f\n"
-                              " z=%f",
-                              p, max_err, m.mean, mean(), std::sqrt(m.variance),
-                              stddev(), m.skewness, skew(), m.kurtosis,
-                              kurtosis(), z));
-  }
-  return pass;
-}
-
-TEST_P(PoissonDistributionZTest, AbslPoissonDistribution) {
-  const auto& param = GetParam();
-  const int expected_failures =
-      std::max(1, static_cast<int>(std::ceil(param.trials * param.p_fail)));
-  const double p = absl::random_internal::RequiredSuccessProbability(
-      param.p_fail, param.trials);
-
-  int failures = 0;
-  for (int i = 0; i < param.trials; i++) {
-    failures +=
-        SingleZTest<absl::poisson_distribution<int32_t>>(p, param.samples) ? 0
-                                                                           : 1;
-  }
-  EXPECT_LE(failures, expected_failures);
-}
-
-std::vector<ZParam> GetZParams() {
-  // These values have been adjusted from the "exact" computed values to reduce
-  // failure rates.
-  //
-  // It turns out that the actual values are not as close to the expected values
-  // as would be ideal.
-  return std::vector<ZParam>({
-      // Knuth method.
-      ZParam{0.5, 0.01, 100, 1000},
-      ZParam{1.0, 0.01, 100, 1000},
-      ZParam{10.0, 0.01, 100, 5000},
-      // Split-knuth method.
-      ZParam{20.0, 0.01, 100, 10000},
-      ZParam{50.0, 0.01, 100, 10000},
-      // Ratio of gaussians method.
-      ZParam{51.0, 0.01, 100, 10000},
-      ZParam{200.0, 0.05, 10, 100000},
-      ZParam{100000.0, 0.05, 10, 1000000},
-  });
-}
-
-std::string ZParamName(const ::testing::TestParamInfo<ZParam>& info) {
-  const auto& p = info.param;
-  std::string name = absl::StrCat("mean_", absl::SixDigits(p.mean));
-  return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
-}
-
-INSTANTIATE_TEST_SUITE_P(All, PoissonDistributionZTest,
-                         ::testing::ValuesIn(GetZParams()), ZParamName);
-
-// The PoissonDistributionChiSquaredTest class provides a basic test framework
-// for variates generated by a conforming poisson_distribution.
-class PoissonDistributionChiSquaredTest : public testing::TestWithParam<double>,
-                                          public PoissonModel {
- public:
-  PoissonDistributionChiSquaredTest() : PoissonModel(GetParam()) {}
-
-  // The ChiSquaredTestImpl provides a chi-squared goodness of fit test for data
-  // generated by the poisson distribution.
-  template <typename D>
-  double ChiSquaredTestImpl();
-
- private:
-  void InitChiSquaredTest(const double buckets);
-
-  std::vector<size_t> cutoffs_;
-  std::vector<double> expected_;
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6};
-};
-
-void PoissonDistributionChiSquaredTest::InitChiSquaredTest(
-    const double buckets) {
-  if (!cutoffs_.empty() && !expected_.empty()) {
-    return;
-  }
-  InitCDF();
-
-  // The code below finds cuttoffs that yield approximately equally-sized
-  // buckets to the extent that it is possible. However for poisson
-  // distributions this is particularly challenging for small mean parameters.
-  // Track the expected proportion of items in each bucket.
-  double last_cdf = 0;
-  const double inc = 1.0 / buckets;
-  for (double p = inc; p <= 1.0; p += inc) {
-    auto result = InverseCDF(p);
-    if (!cutoffs_.empty() && cutoffs_.back() == result.index) {
-      continue;
-    }
-    double d = result.cdf - last_cdf;
-    cutoffs_.push_back(result.index);
-    expected_.push_back(d);
-    last_cdf = result.cdf;
-  }
-  cutoffs_.push_back(std::numeric_limits<size_t>::max());
-  expected_.push_back(std::max(0.0, 1.0 - last_cdf));
-}
-
-template <typename D>
-double PoissonDistributionChiSquaredTest::ChiSquaredTestImpl() {
-  const int kSamples = 2000;
-  const int kBuckets = 50;
-
-  // The poisson CDF fails for large mean values, since e^-mean exceeds the
-  // machine precision. For these cases, using a normal approximation would be
-  // appropriate.
-  ABSL_ASSERT(mean() <= 200);
-  InitChiSquaredTest(kBuckets);
-
-  D dis(mean());
-
-  std::vector<int32_t> counts(cutoffs_.size(), 0);
-  for (int j = 0; j < kSamples; j++) {
-    const size_t x = dis(rng_);
-    auto it = std::lower_bound(std::begin(cutoffs_), std::end(cutoffs_), x);
-    counts[std::distance(cutoffs_.begin(), it)]++;
-  }
-
-  // Normalize the counts.
-  std::vector<int32_t> e(expected_.size(), 0);
-  for (int i = 0; i < e.size(); i++) {
-    e[i] = kSamples * expected_[i];
-  }
-
-  // The null-hypothesis is that the distribution is a poisson distribution with
-  // the provided mean (not estimated from the data).
-  const int dof = static_cast<int>(counts.size()) - 1;
-
-  // The threshold for logging is 1-in-50.
-  const double threshold = absl::random_internal::ChiSquareValue(dof, 0.98);
-
-  const double chi_square = absl::random_internal::ChiSquare(
-      std::begin(counts), std::end(counts), std::begin(e), std::end(e));
-
-  const double p = absl::random_internal::ChiSquarePValue(chi_square, dof);
-
-  // Log if the chi_squared value is above the threshold.
-  if (chi_square > threshold) {
-    LogCDF();
-
-    ABSL_INTERNAL_LOG(INFO, absl::StrCat("VALUES  buckets=", counts.size(),
-                                         "  samples=", kSamples));
-    for (size_t i = 0; i < counts.size(); i++) {
-      ABSL_INTERNAL_LOG(
-          INFO, absl::StrCat(cutoffs_[i], ": ", counts[i], " vs. E=", e[i]));
-    }
-
-    ABSL_INTERNAL_LOG(
-        INFO,
-        absl::StrCat(kChiSquared, "(data, dof=", dof, ") = ", chi_square, " (",
-                     p, ")\n", " vs.\n", kChiSquared, " @ 0.98 = ", threshold));
-  }
-  return p;
-}
-
-TEST_P(PoissonDistributionChiSquaredTest, AbslPoissonDistribution) {
-  const int kTrials = 20;
-
-  // Large values are not yet supported -- this requires estimating the cdf
-  // using the normal distribution instead of the poisson in this case.
-  ASSERT_LE(mean(), 200.0);
-  if (mean() > 200.0) {
-    return;
-  }
-
-  int failures = 0;
-  for (int i = 0; i < kTrials; i++) {
-    double p_value = ChiSquaredTestImpl<absl::poisson_distribution<int32_t>>();
-    if (p_value < 0.005) {
-      failures++;
-    }
-  }
-  // There is a 0.10% chance of producing at least one failure, so raise the
-  // failure threshold high enough to allow for a flake rate < 10,000.
-  EXPECT_LE(failures, 4);
-}
-
-INSTANTIATE_TEST_SUITE_P(All, PoissonDistributionChiSquaredTest,
-                         ::testing::Values(0.5, 1.0, 2.0, 10.0, 50.0, 51.0,
-                                           200.0));
-
-// NOTE: absl::poisson_distribution is not guaranteed to be stable.
-TEST(PoissonDistributionTest, StabilityTest) {
-  using testing::ElementsAre;
-  // absl::poisson_distribution stability relies on stability of
-  // std::exp, std::log, std::sqrt, std::ceil, std::floor, and
-  // absl::FastUniformBits, absl::StirlingLogFactorial, absl::RandU64ToDouble.
-  absl::random_internal::sequence_urbg urbg({
-      0x035b0dc7e0a18acfull, 0x06cebe0d2653682eull, 0x0061e9b23861596bull,
-      0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-      0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-      0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-      0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull,
-      0x4864f22c059bf29eull, 0x247856d8b862665cull, 0xe46e86e9a1337e10ull,
-      0xd8c8541f3519b133ull, 0xe75b5162c567b9e4ull, 0xf732e5ded7009c5bull,
-      0xb170b98353121eacull, 0x1ec2e8986d2362caull, 0x814c8e35fe9a961aull,
-      0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull, 0x1224e62c978bbc7full,
-      0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull, 0x1bbc23cfa8fac721ull,
-      0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull, 0x836d794457c08849ull,
-      0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull, 0xb12d74fdd718c8c5ull,
-      0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull, 0x5738341045ba0d85ull,
-      0xf3fd722dc65ad09eull, 0xfa14fd21ea2a5705ull, 0xffe6ea4d6edb0c73ull,
-      0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull, 0xEAAD8E716B93D5A0ull,
-      0xD08ED1D0AFC725E0ull, 0x8E3C5B2F8E7594B7ull, 0x8FF6E2FBF2122B64ull,
-      0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull, 0xD1CFF191B3A8C1ADull,
-      0x2F2F2218BE0E1777ull, 0xEA752DFE8B021FA1ull, 0xE5A0CC0FB56F74E8ull,
-      0x18ACF3D6CE89E299ull, 0xB4A84FE0FD13E0B7ull, 0x7CC43B81D2ADA8D9ull,
-      0x165FA26680957705ull, 0x93CC7314211A1477ull, 0xE6AD206577B5FA86ull,
-      0xC75442F5FB9D35CFull, 0xEBCDAF0C7B3E89A0ull, 0xD6411BD3AE1E7E49ull,
-      0x00250E2D2071B35Eull, 0x226800BB57B8E0AFull, 0x2464369BF009B91Eull,
-      0x5563911D59DFA6AAull, 0x78C14389D95A537Full, 0x207D5BA202E5B9C5ull,
-      0x832603766295CFA9ull, 0x11C819684E734A41ull, 0xB3472DCA7B14A94Aull,
-  });
-
-  std::vector<int> output(10);
-
-  // Method 1.
-  {
-    absl::poisson_distribution<int> dist(5);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return dist(urbg); });
-  }
-  EXPECT_THAT(output,  // mean = 4.2
-              ElementsAre(1, 0, 0, 4, 2, 10, 3, 3, 7, 12));
-
-  // Method 2.
-  {
-    urbg.reset();
-    absl::poisson_distribution<int> dist(25);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return dist(urbg); });
-  }
-  EXPECT_THAT(output,  // mean = 19.8
-              ElementsAre(9, 35, 18, 10, 35, 18, 10, 35, 18, 10));
-
-  // Method 3.
-  {
-    urbg.reset();
-    absl::poisson_distribution<int> dist(121);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return dist(urbg); });
-  }
-  EXPECT_THAT(output,  // mean = 124.1
-              ElementsAre(161, 122, 129, 124, 112, 112, 117, 120, 130, 114));
-}
-
-TEST(PoissonDistributionTest, AlgorithmExpectedValue_1) {
-  // This tests small values of the Knuth method.
-  // The underlying uniform distribution will generate exactly 0.5.
-  absl::random_internal::sequence_urbg urbg({0x8000000000000001ull});
-  absl::poisson_distribution<int> dist(5);
-  EXPECT_EQ(7, dist(urbg));
-}
-
-TEST(PoissonDistributionTest, AlgorithmExpectedValue_2) {
-  // This tests larger values of the Knuth method.
-  // The underlying uniform distribution will generate exactly 0.5.
-  absl::random_internal::sequence_urbg urbg({0x8000000000000001ull});
-  absl::poisson_distribution<int> dist(25);
-  EXPECT_EQ(36, dist(urbg));
-}
-
-TEST(PoissonDistributionTest, AlgorithmExpectedValue_3) {
-  // This variant uses the ratio of uniforms method.
-  absl::random_internal::sequence_urbg urbg(
-      {0x7fffffffffffffffull, 0x8000000000000000ull});
-
-  absl::poisson_distribution<int> dist(121);
-  EXPECT_EQ(121, dist(urbg));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/random.h b/third_party/abseil_cpp/absl/random/random.h
deleted file mode 100644
index 71b6309288..0000000000
--- a/third_party/abseil_cpp/absl/random/random.h
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: random.h
-// -----------------------------------------------------------------------------
-//
-// This header defines the recommended Uniform Random Bit Generator (URBG)
-// types for use within the Abseil Random library. These types are not
-// suitable for security-related use-cases, but should suffice for most other
-// uses of generating random values.
-//
-// The Abseil random library provides the following URBG types:
-//
-//   * BitGen, a good general-purpose bit generator, optimized for generating
-//     random (but not cryptographically secure) values
-//   * InsecureBitGen, a slightly faster, though less random, bit generator, for
-//     cases where the existing BitGen is a drag on performance.
-
-#ifndef ABSL_RANDOM_RANDOM_H_
-#define ABSL_RANDOM_RANDOM_H_
-
-#include <random>
-
-#include "absl/random/distributions.h"  // IWYU pragma: export
-#include "absl/random/internal/nonsecure_base.h"  // IWYU pragma: export
-#include "absl/random/internal/pcg_engine.h"  // IWYU pragma: export
-#include "absl/random/internal/pool_urbg.h"
-#include "absl/random/internal/randen_engine.h"
-#include "absl/random/seed_sequences.h"  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// absl::BitGen
-// -----------------------------------------------------------------------------
-//
-// `absl::BitGen` is a general-purpose random bit generator for generating
-// random values for use within the Abseil random library. Typically, you use a
-// bit generator in combination with a distribution to provide random values.
-//
-// Example:
-//
-//   // Create an absl::BitGen. There is no need to seed this bit generator.
-//   absl::BitGen gen;
-//
-//   // Generate an integer value in the closed interval [1,6]
-//   int die_roll = absl::uniform_int_distribution<int>(1, 6)(gen);
-//
-// `absl::BitGen` is seeded by default with non-deterministic data to produce
-// different sequences of random values across different instances, including
-// different binary invocations. This behavior is different than the standard
-// library bit generators, which use golden values as their seeds. Default
-// construction intentionally provides no stability guarantees, to avoid
-// accidental dependence on such a property.
-//
-// `absl::BitGen` may be constructed with an optional seed sequence type,
-// conforming to [rand.req.seed_seq], which will be mixed with additional
-// non-deterministic data.
-//
-// Example:
-//
-//  // Create an absl::BitGen using an std::seed_seq seed sequence
-//  std::seed_seq seq{1,2,3};
-//  absl::BitGen gen_with_seed(seq);
-//
-//  // Generate an integer value in the closed interval [1,6]
-//  int die_roll2 = absl::uniform_int_distribution<int>(1, 6)(gen_with_seed);
-//
-// `absl::BitGen` meets the requirements of the Uniform Random Bit Generator
-// (URBG) concept as per the C++17 standard [rand.req.urng] though differs
-// slightly with [rand.req.eng]. Like its standard library equivalents (e.g.
-// `std::mersenne_twister_engine`) `absl::BitGen` is not cryptographically
-// secure.
-//
-// Constructing two `absl::BitGen`s with the same seed sequence in the same
-// binary will produce the same sequence of variates within the same binary, but
-// need not do so across multiple binary invocations.
-//
-// This type has been optimized to perform better than Mersenne Twister
-// (https://en.wikipedia.org/wiki/Mersenne_Twister) and many other complex URBG
-// types on modern x86, ARM, and PPC architectures.
-//
-// This type is thread-compatible, but not thread-safe.
-
-// ---------------------------------------------------------------------------
-// absl::BitGen member functions
-// ---------------------------------------------------------------------------
-
-// absl::BitGen::operator()()
-//
-// Calls the BitGen, returning a generated value.
-
-// absl::BitGen::min()
-//
-// Returns the smallest possible value from this bit generator.
-
-// absl::BitGen::max()
-//
-// Returns the largest possible value from this bit generator.
-
-// absl::BitGen::discard(num)
-//
-// Advances the internal state of this bit generator by `num` times, and
-// discards the intermediate results.
-// ---------------------------------------------------------------------------
-
-using BitGen = random_internal::NonsecureURBGBase<
-    random_internal::randen_engine<uint64_t>>;
-
-// -----------------------------------------------------------------------------
-// absl::InsecureBitGen
-// -----------------------------------------------------------------------------
-//
-// `absl::InsecureBitGen` is an efficient random bit generator for generating
-// random values, recommended only for performance-sensitive use cases where
-// `absl::BitGen` is not satisfactory when compute-bounded by bit generation
-// costs.
-//
-// Example:
-//
-//   // Create an absl::InsecureBitGen
-//   absl::InsecureBitGen gen;
-//   for (size_t i = 0; i < 1000000; i++) {
-//
-//     // Generate a bunch of random values from some complex distribution
-//     auto my_rnd = some_distribution(gen, 1, 1000);
-//   }
-//
-// Like `absl::BitGen`, `absl::InsecureBitGen` is seeded by default with
-// non-deterministic data to produce different sequences of random values across
-// different instances, including different binary invocations. (This behavior
-// is different than the standard library bit generators, which use golden
-// values as their seeds.)
-//
-// `absl::InsecureBitGen` may be constructed with an optional seed sequence
-// type, conforming to [rand.req.seed_seq], which will be mixed with additional
-// non-deterministic data. (See std_seed_seq.h for more information.)
-//
-// `absl::InsecureBitGen` meets the requirements of the Uniform Random Bit
-// Generator (URBG) concept as per the C++17 standard [rand.req.urng] though
-// its implementation differs slightly with [rand.req.eng]. Like its standard
-// library equivalents (e.g. `std::mersenne_twister_engine`)
-// `absl::InsecureBitGen` is not cryptographically secure.
-//
-// Prefer `absl::BitGen` over `absl::InsecureBitGen` as the general type is
-// often fast enough for the vast majority of applications.
-
-using InsecureBitGen =
-    random_internal::NonsecureURBGBase<random_internal::pcg64_2018_engine>;
-
-// ---------------------------------------------------------------------------
-// absl::InsecureBitGen member functions
-// ---------------------------------------------------------------------------
-
-// absl::InsecureBitGen::operator()()
-//
-// Calls the InsecureBitGen, returning a generated value.
-
-// absl::InsecureBitGen::min()
-//
-// Returns the smallest possible value from this bit generator.
-
-// absl::InsecureBitGen::max()
-//
-// Returns the largest possible value from this bit generator.
-
-// absl::InsecureBitGen::discard(num)
-//
-// Advances the internal state of this bit generator by `num` times, and
-// discards the intermediate results.
-// ---------------------------------------------------------------------------
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_RANDOM_H_
diff --git a/third_party/abseil_cpp/absl/random/seed_gen_exception.cc b/third_party/abseil_cpp/absl/random/seed_gen_exception.cc
deleted file mode 100644
index fdcb54a86c..0000000000
--- a/third_party/abseil_cpp/absl/random/seed_gen_exception.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/seed_gen_exception.h"
-
-#include <iostream>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-static constexpr const char kExceptionMessage[] =
-    "Failed generating seed-material for URBG.";
-
-SeedGenException::~SeedGenException() = default;
-
-const char* SeedGenException::what() const noexcept {
-  return kExceptionMessage;
-}
-
-namespace random_internal {
-
-void ThrowSeedGenException() {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  throw absl::SeedGenException();
-#else
-  std::cerr << kExceptionMessage << std::endl;
-  std::terminate();
-#endif
-}
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/seed_gen_exception.h b/third_party/abseil_cpp/absl/random/seed_gen_exception.h
deleted file mode 100644
index 5353900564..0000000000
--- a/third_party/abseil_cpp/absl/random/seed_gen_exception.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: seed_gen_exception.h
-// -----------------------------------------------------------------------------
-//
-// This header defines an exception class which may be thrown if unpredictable
-// events prevent the derivation of suitable seed-material for constructing a
-// bit generator conforming to [rand.req.urng] (eg. entropy cannot be read from
-// /dev/urandom on a Unix-based system).
-//
-// Note: if exceptions are disabled, `std::terminate()` is called instead.
-
-#ifndef ABSL_RANDOM_SEED_GEN_EXCEPTION_H_
-#define ABSL_RANDOM_SEED_GEN_EXCEPTION_H_
-
-#include <exception>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-//------------------------------------------------------------------------------
-// SeedGenException
-//------------------------------------------------------------------------------
-class SeedGenException : public std::exception {
- public:
-  SeedGenException() = default;
-  ~SeedGenException() override;
-  const char* what() const noexcept override;
-};
-
-namespace random_internal {
-
-// throw delegator
-[[noreturn]] void ThrowSeedGenException();
-
-}  // namespace random_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_SEED_GEN_EXCEPTION_H_
diff --git a/third_party/abseil_cpp/absl/random/seed_sequences.cc b/third_party/abseil_cpp/absl/random/seed_sequences.cc
deleted file mode 100644
index 426eafd3c8..0000000000
--- a/third_party/abseil_cpp/absl/random/seed_sequences.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/seed_sequences.h"
-
-#include "absl/random/internal/pool_urbg.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-SeedSeq MakeSeedSeq() {
-  SeedSeq::result_type seed_material[8];
-  random_internal::RandenPool<uint32_t>::Fill(absl::MakeSpan(seed_material));
-  return SeedSeq(std::begin(seed_material), std::end(seed_material));
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/random/seed_sequences.h b/third_party/abseil_cpp/absl/random/seed_sequences.h
deleted file mode 100644
index ff1340cc8e..0000000000
--- a/third_party/abseil_cpp/absl/random/seed_sequences.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: seed_sequences.h
-// -----------------------------------------------------------------------------
-//
-// This header contains utilities for creating and working with seed sequences
-// conforming to [rand.req.seedseq]. In general, direct construction of seed
-// sequences is discouraged, but use-cases for construction of identical bit
-// generators (using the same seed sequence) may be helpful (e.g. replaying a
-// simulation whose state is derived from variates of a bit generator).
-
-#ifndef ABSL_RANDOM_SEED_SEQUENCES_H_
-#define ABSL_RANDOM_SEED_SEQUENCES_H_
-
-#include <iterator>
-#include <random>
-
-#include "absl/random/internal/salted_seed_seq.h"
-#include "absl/random/internal/seed_material.h"
-#include "absl/random/seed_gen_exception.h"
-#include "absl/types/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// absl::SeedSeq
-// -----------------------------------------------------------------------------
-//
-// `absl::SeedSeq` constructs a seed sequence according to [rand.req.seedseq]
-// for use within bit generators. `absl::SeedSeq`, unlike `std::seed_seq`
-// additionally salts the generated seeds with extra implementation-defined
-// entropy. For that reason, you can use `absl::SeedSeq` in combination with
-// standard library bit generators (e.g. `std::mt19937`) to introduce
-// non-determinism in your seeds.
-//
-// Example:
-//
-//   absl::SeedSeq my_seed_seq({a, b, c});
-//   std::mt19937 my_bitgen(my_seed_seq);
-//
-using SeedSeq = random_internal::SaltedSeedSeq<std::seed_seq>;
-
-// -----------------------------------------------------------------------------
-// absl::CreateSeedSeqFrom(bitgen*)
-// -----------------------------------------------------------------------------
-//
-// Constructs a seed sequence conforming to [rand.req.seedseq] using variates
-// produced by a provided bit generator.
-//
-// You should generally avoid direct construction of seed sequences, but
-// use-cases for reuse of a seed sequence to construct identical bit generators
-// may be helpful (eg. replaying a simulation whose state is derived from bit
-// generator values).
-//
-// If bitgen == nullptr, then behavior is undefined.
-//
-// Example:
-//
-//   absl::BitGen my_bitgen;
-//   auto seed_seq = absl::CreateSeedSeqFrom(&my_bitgen);
-//   absl::BitGen new_engine(seed_seq); // derived from my_bitgen, but not
-//                                      // correlated.
-//
-template <typename URBG>
-SeedSeq CreateSeedSeqFrom(URBG* urbg) {
-  SeedSeq::result_type
-      seed_material[random_internal::kEntropyBlocksNeeded];
-
-  if (!random_internal::ReadSeedMaterialFromURBG(
-          urbg, absl::MakeSpan(seed_material))) {
-    random_internal::ThrowSeedGenException();
-  }
-  return SeedSeq(std::begin(seed_material), std::end(seed_material));
-}
-
-// -----------------------------------------------------------------------------
-// absl::MakeSeedSeq()
-// -----------------------------------------------------------------------------
-//
-// Constructs an `absl::SeedSeq` salting the generated values using
-// implementation-defined entropy. The returned sequence can be used to create
-// equivalent bit generators correlated using this sequence.
-//
-// Example:
-//
-//   auto my_seed_seq = absl::MakeSeedSeq();
-//   std::mt19937 rng1(my_seed_seq);
-//   std::mt19937 rng2(my_seed_seq);
-//   EXPECT_EQ(rng1(), rng2());
-//
-SeedSeq MakeSeedSeq();
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_SEED_SEQUENCES_H_
diff --git a/third_party/abseil_cpp/absl/random/seed_sequences_test.cc b/third_party/abseil_cpp/absl/random/seed_sequences_test.cc
deleted file mode 100644
index fe1100bda0..0000000000
--- a/third_party/abseil_cpp/absl/random/seed_sequences_test.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/seed_sequences.h"
-
-#include <iterator>
-#include <random>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/random/internal/nonsecure_base.h"
-#include "absl/random/random.h"
-namespace {
-
-TEST(SeedSequences, Examples) {
-  {
-    absl::SeedSeq seed_seq({1, 2, 3});
-    absl::BitGen bitgen(seed_seq);
-
-    EXPECT_NE(0, bitgen());
-  }
-  {
-    absl::BitGen engine;
-    auto seed_seq = absl::CreateSeedSeqFrom(&engine);
-    absl::BitGen bitgen(seed_seq);
-
-    EXPECT_NE(engine(), bitgen());
-  }
-  {
-    auto seed_seq = absl::MakeSeedSeq();
-    std::mt19937 random(seed_seq);
-
-    EXPECT_NE(0, random());
-  }
-}
-
-TEST(CreateSeedSeqFrom, CompatibleWithStdTypes) {
-  using ExampleNonsecureURBG =
-      absl::random_internal::NonsecureURBGBase<std::minstd_rand0>;
-
-  // Construct a URBG instance.
-  ExampleNonsecureURBG rng;
-
-  // Construct a Seed Sequence from its variates.
-  auto seq_from_rng = absl::CreateSeedSeqFrom(&rng);
-
-  // Ensure that another URBG can be validly constructed from the Seed Sequence.
-  std::mt19937_64{seq_from_rng};
-}
-
-TEST(CreateSeedSeqFrom, CompatibleWithBitGenerator) {
-  // Construct a URBG instance.
-  absl::BitGen rng;
-
-  // Construct a Seed Sequence from its variates.
-  auto seq_from_rng = absl::CreateSeedSeqFrom(&rng);
-
-  // Ensure that another URBG can be validly constructed from the Seed Sequence.
-  std::mt19937_64{seq_from_rng};
-}
-
-TEST(CreateSeedSeqFrom, CompatibleWithInsecureBitGen) {
-  // Construct a URBG instance.
-  absl::InsecureBitGen rng;
-
-  // Construct a Seed Sequence from its variates.
-  auto seq_from_rng = absl::CreateSeedSeqFrom(&rng);
-
-  // Ensure that another URBG can be validly constructed from the Seed Sequence.
-  std::mt19937_64{seq_from_rng};
-}
-
-TEST(CreateSeedSeqFrom, CompatibleWithRawURBG) {
-  // Construct a URBG instance.
-  std::random_device urandom;
-
-  // Construct a Seed Sequence from its variates, using 64b of seed-material.
-  auto seq_from_rng = absl::CreateSeedSeqFrom(&urandom);
-
-  // Ensure that another URBG can be validly constructed from the Seed Sequence.
-  std::mt19937_64{seq_from_rng};
-}
-
-template <typename URBG>
-void TestReproducibleVariateSequencesForNonsecureURBG() {
-  const size_t kNumVariates = 1000;
-
-  URBG rng;
-  // Reused for both RNG instances.
-  auto reusable_seed = absl::CreateSeedSeqFrom(&rng);
-
-  typename URBG::result_type variates[kNumVariates];
-  {
-    URBG child(reusable_seed);
-    for (auto& variate : variates) {
-      variate = child();
-    }
-  }
-  // Ensure that variate-sequence can be "replayed" by identical RNG.
-  {
-    URBG child(reusable_seed);
-    for (auto& variate : variates) {
-      ASSERT_EQ(variate, child());
-    }
-  }
-}
-
-TEST(CreateSeedSeqFrom, ReproducesVariateSequencesForInsecureBitGen) {
-  TestReproducibleVariateSequencesForNonsecureURBG<absl::InsecureBitGen>();
-}
-
-TEST(CreateSeedSeqFrom, ReproducesVariateSequencesForBitGenerator) {
-  TestReproducibleVariateSequencesForNonsecureURBG<absl::BitGen>();
-}
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/uniform_int_distribution.h b/third_party/abseil_cpp/absl/random/uniform_int_distribution.h
deleted file mode 100644
index c1f54ccebc..0000000000
--- a/third_party/abseil_cpp/absl/random/uniform_int_distribution.h
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: uniform_int_distribution.h
-// -----------------------------------------------------------------------------
-//
-// This header defines a class for representing a uniform integer distribution
-// over the closed (inclusive) interval [a,b]. You use this distribution in
-// combination with an Abseil random bit generator to produce random values
-// according to the rules of the distribution.
-//
-// `absl::uniform_int_distribution` is a drop-in replacement for the C++11
-// `std::uniform_int_distribution` [rand.dist.uni.int] but is considerably
-// faster than the libstdc++ implementation.
-
-#ifndef ABSL_RANDOM_UNIFORM_INT_DISTRIBUTION_H_
-#define ABSL_RANDOM_UNIFORM_INT_DISTRIBUTION_H_
-
-#include <cassert>
-#include <istream>
-#include <limits>
-#include <type_traits>
-
-#include "absl/base/optimization.h"
-#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/random/internal/iostream_state_saver.h"
-#include "absl/random/internal/traits.h"
-#include "absl/random/internal/wide_multiply.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::uniform_int_distribution<T>
-//
-// This distribution produces random integer values uniformly distributed in the
-// closed (inclusive) interval [a, b].
-//
-// Example:
-//
-//   absl::BitGen gen;
-//
-//   // Use the distribution to produce a value between 1 and 6, inclusive.
-//   int die_roll = absl::uniform_int_distribution<int>(1, 6)(gen);
-//
-template <typename IntType = int>
-class uniform_int_distribution {
- private:
-  using unsigned_type =
-      typename random_internal::make_unsigned_bits<IntType>::type;
-
- public:
-  using result_type = IntType;
-
-  class param_type {
-   public:
-    using distribution_type = uniform_int_distribution;
-
-    explicit param_type(
-        result_type lo = 0,
-        result_type hi = (std::numeric_limits<result_type>::max)())
-        : lo_(lo),
-          range_(static_cast<unsigned_type>(hi) -
-                 static_cast<unsigned_type>(lo)) {
-      // [rand.dist.uni.int] precondition 2
-      assert(lo <= hi);
-    }
-
-    result_type a() const { return lo_; }
-    result_type b() const {
-      return static_cast<result_type>(static_cast<unsigned_type>(lo_) + range_);
-    }
-
-    friend bool operator==(const param_type& a, const param_type& b) {
-      return a.lo_ == b.lo_ && a.range_ == b.range_;
-    }
-
-    friend bool operator!=(const param_type& a, const param_type& b) {
-      return !(a == b);
-    }
-
-   private:
-    friend class uniform_int_distribution;
-    unsigned_type range() const { return range_; }
-
-    result_type lo_;
-    unsigned_type range_;
-
-    static_assert(std::is_integral<result_type>::value,
-                  "Class-template absl::uniform_int_distribution<> must be "
-                  "parameterized using an integral type.");
-  };  // param_type
-
-  uniform_int_distribution() : uniform_int_distribution(0) {}
-
-  explicit uniform_int_distribution(
-      result_type lo,
-      result_type hi = (std::numeric_limits<result_type>::max)())
-      : param_(lo, hi) {}
-
-  explicit uniform_int_distribution(const param_type& param) : param_(param) {}
-
-  // uniform_int_distribution<T>::reset()
-  //
-  // Resets the uniform int distribution. Note that this function has no effect
-  // because the distribution already produces independent values.
-  void reset() {}
-
-  template <typename URBG>
-  result_type operator()(URBG& gen) {  // NOLINT(runtime/references)
-    return (*this)(gen, param());
-  }
-
-  template <typename URBG>
-  result_type operator()(
-      URBG& gen, const param_type& param) {  // NOLINT(runtime/references)
-    return param.a() + Generate(gen, param.range());
-  }
-
-  result_type a() const { return param_.a(); }
-  result_type b() const { return param_.b(); }
-
-  param_type param() const { return param_; }
-  void param(const param_type& params) { param_ = params; }
-
-  result_type(min)() const { return a(); }
-  result_type(max)() const { return b(); }
-
-  friend bool operator==(const uniform_int_distribution& a,
-                         const uniform_int_distribution& b) {
-    return a.param_ == b.param_;
-  }
-  friend bool operator!=(const uniform_int_distribution& a,
-                         const uniform_int_distribution& b) {
-    return !(a == b);
-  }
-
- private:
-  // Generates a value in the *closed* interval [0, R]
-  template <typename URBG>
-  unsigned_type Generate(URBG& g,  // NOLINT(runtime/references)
-                         unsigned_type R);
-  param_type param_;
-};
-
-// -----------------------------------------------------------------------------
-// Implementation details follow
-// -----------------------------------------------------------------------------
-template <typename CharT, typename Traits, typename IntType>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,
-    const uniform_int_distribution<IntType>& x) {
-  using stream_type =
-      typename random_internal::stream_format_type<IntType>::type;
-  auto saver = random_internal::make_ostream_state_saver(os);
-  os << static_cast<stream_type>(x.a()) << os.fill()
-     << static_cast<stream_type>(x.b());
-  return os;
-}
-
-template <typename CharT, typename Traits, typename IntType>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,
-    uniform_int_distribution<IntType>& x) {
-  using param_type = typename uniform_int_distribution<IntType>::param_type;
-  using result_type = typename uniform_int_distribution<IntType>::result_type;
-  using stream_type =
-      typename random_internal::stream_format_type<IntType>::type;
-
-  stream_type a;
-  stream_type b;
-
-  auto saver = random_internal::make_istream_state_saver(is);
-  is >> a >> b;
-  if (!is.fail()) {
-    x.param(
-        param_type(static_cast<result_type>(a), static_cast<result_type>(b)));
-  }
-  return is;
-}
-
-template <typename IntType>
-template <typename URBG>
-typename random_internal::make_unsigned_bits<IntType>::type
-uniform_int_distribution<IntType>::Generate(
-    URBG& g,  // NOLINT(runtime/references)
-    typename random_internal::make_unsigned_bits<IntType>::type R) {
-  random_internal::FastUniformBits<unsigned_type> fast_bits;
-  unsigned_type bits = fast_bits(g);
-  const unsigned_type Lim = R + 1;
-  if ((R & Lim) == 0) {
-    // If the interval's length is a power of two range, just take the low bits.
-    return bits & R;
-  }
-
-  // Generates a uniform variate on [0, Lim) using fixed-point multiplication.
-  // The above fast-path guarantees that Lim is representable in unsigned_type.
-  //
-  // Algorithm adapted from
-  // http://lemire.me/blog/2016/06/30/fast-random-shuffling/, with added
-  // explanation.
-  //
-  // The algorithm creates a uniform variate `bits` in the interval [0, 2^N),
-  // and treats it as the fractional part of a fixed-point real value in [0, 1),
-  // multiplied by 2^N.  For example, 0.25 would be represented as 2^(N - 2),
-  // because 2^N * 0.25 == 2^(N - 2).
-  //
-  // Next, `bits` and `Lim` are multiplied with a wide-multiply to bring the
-  // value into the range [0, Lim).  The integral part (the high word of the
-  // multiplication result) is then very nearly the desired result.  However,
-  // this is not quite accurate; viewing the multiplication result as one
-  // double-width integer, the resulting values for the sample are mapped as
-  // follows:
-  //
-  // If the result lies in this interval:       Return this value:
-  //        [0, 2^N)                                    0
-  //        [2^N, 2 * 2^N)                              1
-  //        ...                                         ...
-  //        [K * 2^N, (K + 1) * 2^N)                    K
-  //        ...                                         ...
-  //        [(Lim - 1) * 2^N, Lim * 2^N)                Lim - 1
-  //
-  // While all of these intervals have the same size, the result of `bits * Lim`
-  // must be a multiple of `Lim`, and not all of these intervals contain the
-  // same number of multiples of `Lim`.  In particular, some contain
-  // `F = floor(2^N / Lim)` and some contain `F + 1 = ceil(2^N / Lim)`.  This
-  // difference produces a small nonuniformity, which is corrected by applying
-  // rejection sampling to one of the values in the "larger intervals" (i.e.,
-  // the intervals containing `F + 1` multiples of `Lim`.
-  //
-  // An interval contains `F + 1` multiples of `Lim` if and only if its smallest
-  // value modulo 2^N is less than `2^N % Lim`.  The unique value satisfying
-  // this property is used as the one for rejection.  That is, a value of
-  // `bits * Lim` is rejected if `(bit * Lim) % 2^N < (2^N % Lim)`.
-
-  using helper = random_internal::wide_multiply<unsigned_type>;
-  auto product = helper::multiply(bits, Lim);
-
-  // Two optimizations here:
-  // * Rejection occurs with some probability less than 1/2, and for reasonable
-  //   ranges considerably less (in particular, less than 1/(F+1)), so
-  //   ABSL_PREDICT_FALSE is apt.
-  // * `Lim` is an overestimate of `threshold`, and doesn't require a divide.
-  if (ABSL_PREDICT_FALSE(helper::lo(product) < Lim)) {
-    // This quantity is exactly equal to `2^N % Lim`, but does not require high
-    // precision calculations: `2^N % Lim` is congruent to `(2^N - Lim) % Lim`.
-    // Ideally this could be expressed simply as `-X` rather than `2^N - X`, but
-    // for types smaller than int, this calculation is incorrect due to integer
-    // promotion rules.
-    const unsigned_type threshold =
-        ((std::numeric_limits<unsigned_type>::max)() - Lim + 1) % Lim;
-    while (helper::lo(product) < threshold) {
-      bits = fast_bits(g);
-      product = helper::multiply(bits, Lim);
-    }
-  }
-
-  return helper::hi(product);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_UNIFORM_INT_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/uniform_int_distribution_test.cc b/third_party/abseil_cpp/absl/random/uniform_int_distribution_test.cc
deleted file mode 100644
index 276d72ad20..0000000000
--- a/third_party/abseil_cpp/absl/random/uniform_int_distribution_test.cc
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/uniform_int_distribution.h"
-
-#include <cmath>
-#include <cstdint>
-#include <iterator>
-#include <random>
-#include <sstream>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/chi_square.h"
-#include "absl/random/internal/distribution_test_util.h"
-#include "absl/random/internal/pcg_engine.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-template <typename IntType>
-class UniformIntDistributionTest : public ::testing::Test {};
-
-using IntTypes = ::testing::Types<int8_t, uint8_t, int16_t, uint16_t, int32_t,
-                                  uint32_t, int64_t, uint64_t>;
-TYPED_TEST_SUITE(UniformIntDistributionTest, IntTypes);
-
-TYPED_TEST(UniformIntDistributionTest, ParamSerializeTest) {
-  // This test essentially ensures that the parameters serialize,
-  // not that the values generated cover the full range.
-  using Limits = std::numeric_limits<TypeParam>;
-  using param_type =
-      typename absl::uniform_int_distribution<TypeParam>::param_type;
-  const TypeParam kMin = std::is_unsigned<TypeParam>::value ? 37 : -105;
-  const TypeParam kNegOneOrZero = std::is_unsigned<TypeParam>::value ? 0 : -1;
-
-  constexpr int kCount = 1000;
-  absl::InsecureBitGen gen;
-  for (const auto& param : {
-           param_type(),
-           param_type(2, 2),  // Same
-           param_type(9, 32),
-           param_type(kMin, 115),
-           param_type(kNegOneOrZero, Limits::max()),
-           param_type(Limits::min(), Limits::max()),
-           param_type(Limits::lowest(), Limits::max()),
-           param_type(Limits::min() + 1, Limits::max() - 1),
-       }) {
-    const auto a = param.a();
-    const auto b = param.b();
-    absl::uniform_int_distribution<TypeParam> before(a, b);
-    EXPECT_EQ(before.a(), param.a());
-    EXPECT_EQ(before.b(), param.b());
-
-    {
-      // Initialize via param_type
-      absl::uniform_int_distribution<TypeParam> via_param(param);
-      EXPECT_EQ(via_param, before);
-    }
-
-    // Initialize via iostreams
-    std::stringstream ss;
-    ss << before;
-
-    absl::uniform_int_distribution<TypeParam> after(Limits::min() + 3,
-                                                    Limits::max() - 5);
-
-    EXPECT_NE(before.a(), after.a());
-    EXPECT_NE(before.b(), after.b());
-    EXPECT_NE(before.param(), after.param());
-    EXPECT_NE(before, after);
-
-    ss >> after;
-
-    EXPECT_EQ(before.a(), after.a());
-    EXPECT_EQ(before.b(), after.b());
-    EXPECT_EQ(before.param(), after.param());
-    EXPECT_EQ(before, after);
-
-    // Smoke test.
-    auto sample_min = after.max();
-    auto sample_max = after.min();
-    for (int i = 0; i < kCount; i++) {
-      auto sample = after(gen);
-      EXPECT_GE(sample, after.min());
-      EXPECT_LE(sample, after.max());
-      if (sample > sample_max) {
-        sample_max = sample;
-      }
-      if (sample < sample_min) {
-        sample_min = sample;
-      }
-    }
-    std::string msg = absl::StrCat("Range: ", +sample_min, ", ", +sample_max);
-    ABSL_RAW_LOG(INFO, "%s", msg.c_str());
-  }
-}
-
-TYPED_TEST(UniformIntDistributionTest, ViolatesPreconditionsDeathTest) {
-#if GTEST_HAS_DEATH_TEST
-  // Hi < Lo
-  EXPECT_DEBUG_DEATH({ absl::uniform_int_distribution<TypeParam> dist(10, 1); },
-                     "");
-#endif  // GTEST_HAS_DEATH_TEST
-#if defined(NDEBUG)
-  // opt-mode, for invalid parameters, will generate a garbage value,
-  // but should not enter an infinite loop.
-  absl::InsecureBitGen gen;
-  absl::uniform_int_distribution<TypeParam> dist(10, 1);
-  auto x = dist(gen);
-
-  // Any value will generate a non-empty string.
-  EXPECT_FALSE(absl::StrCat(+x).empty()) << x;
-#endif  // NDEBUG
-}
-
-TYPED_TEST(UniformIntDistributionTest, TestMoments) {
-  constexpr int kSize = 100000;
-  using Limits = std::numeric_limits<TypeParam>;
-  using param_type =
-      typename absl::uniform_int_distribution<TypeParam>::param_type;
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6};
-
-  std::vector<double> values(kSize);
-  for (const auto& param :
-       {param_type(0, Limits::max()), param_type(13, 127)}) {
-    absl::uniform_int_distribution<TypeParam> dist(param);
-    for (int i = 0; i < kSize; i++) {
-      const auto sample = dist(rng);
-      ASSERT_LE(dist.param().a(), sample);
-      ASSERT_GE(dist.param().b(), sample);
-      values[i] = sample;
-    }
-
-    auto moments = absl::random_internal::ComputeDistributionMoments(values);
-    const double a = dist.param().a();
-    const double b = dist.param().b();
-    const double n = (b - a + 1);
-    const double mean = (a + b) / 2;
-    const double var = ((b - a + 1) * (b - a + 1) - 1) / 12;
-    const double kurtosis = 3 - 6 * (n * n + 1) / (5 * (n * n - 1));
-
-    // TODO(ahh): this is not the right bound
-    // empirically validated with --runs_per_test=10000.
-    EXPECT_NEAR(mean, moments.mean, 0.01 * var);
-    EXPECT_NEAR(var, moments.variance, 0.015 * var);
-    EXPECT_NEAR(0.0, moments.skewness, 0.025);
-    EXPECT_NEAR(kurtosis, moments.kurtosis, 0.02 * kurtosis);
-  }
-}
-
-TYPED_TEST(UniformIntDistributionTest, ChiSquaredTest50) {
-  using absl::random_internal::kChiSquared;
-
-  constexpr size_t kTrials = 1000;
-  constexpr int kBuckets = 50;  // inclusive, so actally +1
-  constexpr double kExpected =
-      static_cast<double>(kTrials) / static_cast<double>(kBuckets);
-
-  // Empirically validated with --runs_per_test=10000.
-  const int kThreshold =
-      absl::random_internal::ChiSquareValue(kBuckets, 0.999999);
-
-  const TypeParam min = std::is_unsigned<TypeParam>::value ? 37 : -37;
-  const TypeParam max = min + kBuckets;
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6};
-
-  absl::uniform_int_distribution<TypeParam> dist(min, max);
-
-  std::vector<int32_t> counts(kBuckets + 1, 0);
-  for (size_t i = 0; i < kTrials; i++) {
-    auto x = dist(rng);
-    counts[x - min]++;
-  }
-  double chi_square = absl::random_internal::ChiSquareWithExpected(
-      std::begin(counts), std::end(counts), kExpected);
-  if (chi_square > kThreshold) {
-    double p_value =
-        absl::random_internal::ChiSquarePValue(chi_square, kBuckets);
-
-    // Chi-squared test failed. Output does not appear to be uniform.
-    std::string msg;
-    for (const auto& a : counts) {
-      absl::StrAppend(&msg, a, "\n");
-    }
-    absl::StrAppend(&msg, kChiSquared, " p-value ", p_value, "\n");
-    absl::StrAppend(&msg, "High ", kChiSquared, " value: ", chi_square, " > ",
-                    kThreshold);
-    ABSL_RAW_LOG(INFO, "%s", msg.c_str());
-    FAIL() << msg;
-  }
-}
-
-TEST(UniformIntDistributionTest, StabilityTest) {
-  // absl::uniform_int_distribution stability relies only on integer operations.
-  absl::random_internal::sequence_urbg urbg(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  std::vector<int> output(12);
-
-  {
-    absl::uniform_int_distribution<int32_t> dist(0, 4);
-    for (auto& v : output) {
-      v = dist(urbg);
-    }
-  }
-  EXPECT_EQ(12, urbg.invocations());
-  EXPECT_THAT(output, testing::ElementsAre(4, 4, 3, 2, 1, 0, 1, 4, 3, 1, 3, 1));
-
-  {
-    urbg.reset();
-    absl::uniform_int_distribution<int32_t> dist(0, 100);
-    for (auto& v : output) {
-      v = dist(urbg);
-    }
-  }
-  EXPECT_EQ(12, urbg.invocations());
-  EXPECT_THAT(output, testing::ElementsAre(97, 86, 75, 41, 36, 16, 38, 92, 67,
-                                           30, 80, 38));
-
-  {
-    urbg.reset();
-    absl::uniform_int_distribution<int32_t> dist(0, 10000);
-    for (auto& v : output) {
-      v = dist(urbg);
-    }
-  }
-  EXPECT_EQ(12, urbg.invocations());
-  EXPECT_THAT(output, testing::ElementsAre(9648, 8562, 7439, 4089, 3571, 1602,
-                                           3813, 9195, 6641, 2986, 7956, 3765));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/uniform_real_distribution.h b/third_party/abseil_cpp/absl/random/uniform_real_distribution.h
deleted file mode 100644
index 5ba17b2341..0000000000
--- a/third_party/abseil_cpp/absl/random/uniform_real_distribution.h
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: uniform_real_distribution.h
-// -----------------------------------------------------------------------------
-//
-// This header defines a class for representing a uniform floating-point
-// distribution over a half-open interval [a,b). You use this distribution in
-// combination with an Abseil random bit generator to produce random values
-// according to the rules of the distribution.
-//
-// `absl::uniform_real_distribution` is a drop-in replacement for the C++11
-// `std::uniform_real_distribution` [rand.dist.uni.real] but is considerably
-// faster than the libstdc++ implementation.
-//
-// Note: the standard-library version may occasionally return `1.0` when
-// default-initialized. See https://bugs.llvm.org//show_bug.cgi?id=18767
-// `absl::uniform_real_distribution` does not exhibit this behavior.
-
-#ifndef ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_
-#define ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_
-
-#include <cassert>
-#include <cmath>
-#include <cstdint>
-#include <istream>
-#include <limits>
-#include <type_traits>
-
-#include "absl/meta/type_traits.h"
-#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/random/internal/generate_real.h"
-#include "absl/random/internal/iostream_state_saver.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::uniform_real_distribution<T>
-//
-// This distribution produces random floating-point values uniformly distributed
-// over the half-open interval [a, b).
-//
-// Example:
-//
-//   absl::BitGen gen;
-//
-//   // Use the distribution to produce a value between 0.0 (inclusive)
-//   // and 1.0 (exclusive).
-//   double value = absl::uniform_real_distribution<double>(0, 1)(gen);
-//
-template <typename RealType = double>
-class uniform_real_distribution {
- public:
-  using result_type = RealType;
-
-  class param_type {
-   public:
-    using distribution_type = uniform_real_distribution;
-
-    explicit param_type(result_type lo = 0, result_type hi = 1)
-        : lo_(lo), hi_(hi), range_(hi - lo) {
-      // [rand.dist.uni.real] preconditions 2 & 3
-      assert(lo <= hi);
-      // NOTE: For integral types, we can promote the range to an unsigned type,
-      // which gives full width of the range. However for real (fp) types, this
-      // is not possible, so value generation cannot use the full range of the
-      // real type.
-      assert(range_ <= (std::numeric_limits<result_type>::max)());
-      assert(std::isfinite(range_));
-    }
-
-    result_type a() const { return lo_; }
-    result_type b() const { return hi_; }
-
-    friend bool operator==(const param_type& a, const param_type& b) {
-      return a.lo_ == b.lo_ && a.hi_ == b.hi_;
-    }
-
-    friend bool operator!=(const param_type& a, const param_type& b) {
-      return !(a == b);
-    }
-
-   private:
-    friend class uniform_real_distribution;
-    result_type lo_, hi_, range_;
-
-    static_assert(std::is_floating_point<RealType>::value,
-                  "Class-template absl::uniform_real_distribution<> must be "
-                  "parameterized using a floating-point type.");
-  };
-
-  uniform_real_distribution() : uniform_real_distribution(0) {}
-
-  explicit uniform_real_distribution(result_type lo, result_type hi = 1)
-      : param_(lo, hi) {}
-
-  explicit uniform_real_distribution(const param_type& param) : param_(param) {}
-
-  // uniform_real_distribution<T>::reset()
-  //
-  // Resets the uniform real distribution. Note that this function has no effect
-  // because the distribution already produces independent values.
-  void reset() {}
-
-  template <typename URBG>
-  result_type operator()(URBG& gen) {  // NOLINT(runtime/references)
-    return operator()(gen, param_);
-  }
-
-  template <typename URBG>
-  result_type operator()(URBG& gen,  // NOLINT(runtime/references)
-                         const param_type& p);
-
-  result_type a() const { return param_.a(); }
-  result_type b() const { return param_.b(); }
-
-  param_type param() const { return param_; }
-  void param(const param_type& params) { param_ = params; }
-
-  result_type(min)() const { return a(); }
-  result_type(max)() const { return b(); }
-
-  friend bool operator==(const uniform_real_distribution& a,
-                         const uniform_real_distribution& b) {
-    return a.param_ == b.param_;
-  }
-  friend bool operator!=(const uniform_real_distribution& a,
-                         const uniform_real_distribution& b) {
-    return a.param_ != b.param_;
-  }
-
- private:
-  param_type param_;
-  random_internal::FastUniformBits<uint64_t> fast_u64_;
-};
-
-// -----------------------------------------------------------------------------
-// Implementation details follow
-// -----------------------------------------------------------------------------
-template <typename RealType>
-template <typename URBG>
-typename uniform_real_distribution<RealType>::result_type
-uniform_real_distribution<RealType>::operator()(
-    URBG& gen, const param_type& p) {  // NOLINT(runtime/references)
-  using random_internal::GeneratePositiveTag;
-  using random_internal::GenerateRealFromBits;
-  using real_type =
-      absl::conditional_t<std::is_same<RealType, float>::value, float, double>;
-
-  while (true) {
-    const result_type sample =
-        GenerateRealFromBits<real_type, GeneratePositiveTag, true>(
-            fast_u64_(gen));
-    const result_type res = p.a() + (sample * p.range_);
-    if (res < p.b() || p.range_ <= 0 || !std::isfinite(p.range_)) {
-      return res;
-    }
-    // else sample rejected, try again.
-  }
-}
-
-template <typename CharT, typename Traits, typename RealType>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    const uniform_real_distribution<RealType>& x) {
-  auto saver = random_internal::make_ostream_state_saver(os);
-  os.precision(random_internal::stream_precision_helper<RealType>::kPrecision);
-  os << x.a() << os.fill() << x.b();
-  return os;
-}
-
-template <typename CharT, typename Traits, typename RealType>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,     // NOLINT(runtime/references)
-    uniform_real_distribution<RealType>& x) {  // NOLINT(runtime/references)
-  using param_type = typename uniform_real_distribution<RealType>::param_type;
-  using result_type = typename uniform_real_distribution<RealType>::result_type;
-  auto saver = random_internal::make_istream_state_saver(is);
-  auto a = random_internal::read_floating_point<result_type>(is);
-  if (is.fail()) return is;
-  auto b = random_internal::read_floating_point<result_type>(is);
-  if (!is.fail()) {
-    x.param(param_type(a, b));
-  }
-  return is;
-}
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/uniform_real_distribution_test.cc b/third_party/abseil_cpp/absl/random/uniform_real_distribution_test.cc
deleted file mode 100644
index be107cdde4..0000000000
--- a/third_party/abseil_cpp/absl/random/uniform_real_distribution_test.cc
+++ /dev/null
@@ -1,343 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/uniform_real_distribution.h"
-
-#include <cmath>
-#include <cstdint>
-#include <iterator>
-#include <random>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/chi_square.h"
-#include "absl/random/internal/distribution_test_util.h"
-#include "absl/random/internal/pcg_engine.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-
-// NOTES:
-// * Some documentation on generating random real values suggests that
-//   it is possible to use std::nextafter(b, DBL_MAX) to generate a value on
-//   the closed range [a, b]. Unfortunately, that technique is not universally
-//   reliable due to floating point quantization.
-//
-// * absl::uniform_real_distribution<float> generates between 2^28 and 2^29
-//   distinct floating point values in the range [0, 1).
-//
-// * absl::uniform_real_distribution<float> generates at least 2^23 distinct
-//   floating point values in the range [1, 2). This should be the same as
-//   any other range covered by a single exponent in IEEE 754.
-//
-// * absl::uniform_real_distribution<double> generates more than 2^52 distinct
-//   values in the range [0, 1), and should generate at least 2^52 distinct
-//   values in the range of [1, 2).
-//
-
-namespace {
-
-template <typename RealType>
-class UniformRealDistributionTest : public ::testing::Test {};
-
-#if defined(__EMSCRIPTEN__)
-using RealTypes = ::testing::Types<float, double>;
-#else
-using RealTypes = ::testing::Types<float, double, long double>;
-#endif  // defined(__EMSCRIPTEN__)
-
-TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes);
-
-TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) {
-  using param_type =
-      typename absl::uniform_real_distribution<TypeParam>::param_type;
-
-  constexpr const TypeParam a{1152921504606846976};
-
-  constexpr int kCount = 1000;
-  absl::InsecureBitGen gen;
-  for (const auto& param : {
-           param_type(),
-           param_type(TypeParam(2.0), TypeParam(2.0)),  // Same
-           param_type(TypeParam(-0.1), TypeParam(0.1)),
-           param_type(TypeParam(0.05), TypeParam(0.12)),
-           param_type(TypeParam(-0.05), TypeParam(0.13)),
-           param_type(TypeParam(-0.05), TypeParam(-0.02)),
-           // double range = 0
-           // 2^60 , 2^60 + 2^6
-           param_type(a, TypeParam(1152921504606847040)),
-           // 2^60 , 2^60 + 2^7
-           param_type(a, TypeParam(1152921504606847104)),
-           // double range = 2^8
-           // 2^60 , 2^60 + 2^8
-           param_type(a, TypeParam(1152921504606847232)),
-           // float range = 0
-           // 2^60 , 2^60 + 2^36
-           param_type(a, TypeParam(1152921573326323712)),
-           // 2^60 , 2^60 + 2^37
-           param_type(a, TypeParam(1152921642045800448)),
-           // float range = 2^38
-           // 2^60 , 2^60 + 2^38
-           param_type(a, TypeParam(1152921779484753920)),
-           // Limits
-           param_type(0, std::numeric_limits<TypeParam>::max()),
-           param_type(std::numeric_limits<TypeParam>::lowest(), 0),
-           param_type(0, std::numeric_limits<TypeParam>::epsilon()),
-           param_type(-std::numeric_limits<TypeParam>::epsilon(),
-                      std::numeric_limits<TypeParam>::epsilon()),
-           param_type(std::numeric_limits<TypeParam>::epsilon(),
-                      2 * std::numeric_limits<TypeParam>::epsilon()),
-       }) {
-    // Validate parameters.
-    const auto a = param.a();
-    const auto b = param.b();
-    absl::uniform_real_distribution<TypeParam> before(a, b);
-    EXPECT_EQ(before.a(), param.a());
-    EXPECT_EQ(before.b(), param.b());
-
-    {
-      absl::uniform_real_distribution<TypeParam> via_param(param);
-      EXPECT_EQ(via_param, before);
-    }
-
-    std::stringstream ss;
-    ss << before;
-    absl::uniform_real_distribution<TypeParam> after(TypeParam(1.0),
-                                                     TypeParam(3.1));
-
-    EXPECT_NE(before.a(), after.a());
-    EXPECT_NE(before.b(), after.b());
-    EXPECT_NE(before.param(), after.param());
-    EXPECT_NE(before, after);
-
-    ss >> after;
-
-    EXPECT_EQ(before.a(), after.a());
-    EXPECT_EQ(before.b(), after.b());
-    EXPECT_EQ(before.param(), after.param());
-    EXPECT_EQ(before, after);
-
-    // Smoke test.
-    auto sample_min = after.max();
-    auto sample_max = after.min();
-    for (int i = 0; i < kCount; i++) {
-      auto sample = after(gen);
-      // Failure here indicates a bug in uniform_real_distribution::operator(),
-      // or bad parameters--range too large, etc.
-      if (after.min() == after.max()) {
-        EXPECT_EQ(sample, after.min());
-      } else {
-        EXPECT_GE(sample, after.min());
-        EXPECT_LT(sample, after.max());
-      }
-      if (sample > sample_max) {
-        sample_max = sample;
-      }
-      if (sample < sample_min) {
-        sample_min = sample;
-      }
-    }
-
-    if (!std::is_same<TypeParam, long double>::value) {
-      // static_cast<double>(long double) can overflow.
-      std::string msg = absl::StrCat("Range: ", static_cast<double>(sample_min),
-                                     ", ", static_cast<double>(sample_max));
-      ABSL_RAW_LOG(INFO, "%s", msg.c_str());
-    }
-  }
-}
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4756)  // Constant arithmetic overflow.
-#endif
-TYPED_TEST(UniformRealDistributionTest, ViolatesPreconditionsDeathTest) {
-#if GTEST_HAS_DEATH_TEST
-  // Hi < Lo
-  EXPECT_DEBUG_DEATH(
-      { absl::uniform_real_distribution<TypeParam> dist(10.0, 1.0); }, "");
-
-  // Hi - Lo > numeric_limits<>::max()
-  EXPECT_DEBUG_DEATH(
-      {
-        absl::uniform_real_distribution<TypeParam> dist(
-            std::numeric_limits<TypeParam>::lowest(),
-            std::numeric_limits<TypeParam>::max());
-      },
-      "");
-#endif  // GTEST_HAS_DEATH_TEST
-#if defined(NDEBUG)
-  // opt-mode, for invalid parameters, will generate a garbage value,
-  // but should not enter an infinite loop.
-  absl::InsecureBitGen gen;
-  {
-    absl::uniform_real_distribution<TypeParam> dist(10.0, 1.0);
-    auto x = dist(gen);
-    EXPECT_FALSE(std::isnan(x)) << x;
-  }
-  {
-    absl::uniform_real_distribution<TypeParam> dist(
-        std::numeric_limits<TypeParam>::lowest(),
-        std::numeric_limits<TypeParam>::max());
-    auto x = dist(gen);
-    // Infinite result.
-    EXPECT_FALSE(std::isfinite(x)) << x;
-  }
-#endif  // NDEBUG
-}
-#ifdef _MSC_VER
-#pragma warning(pop)  // warning(disable:4756)
-#endif
-
-TYPED_TEST(UniformRealDistributionTest, TestMoments) {
-  constexpr int kSize = 1000000;
-  std::vector<double> values(kSize);
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6};
-
-  absl::uniform_real_distribution<TypeParam> dist;
-  for (int i = 0; i < kSize; i++) {
-    values[i] = dist(rng);
-  }
-
-  const auto moments =
-      absl::random_internal::ComputeDistributionMoments(values);
-  EXPECT_NEAR(0.5, moments.mean, 0.01);
-  EXPECT_NEAR(1 / 12.0, moments.variance, 0.015);
-  EXPECT_NEAR(0.0, moments.skewness, 0.02);
-  EXPECT_NEAR(9 / 5.0, moments.kurtosis, 0.015);
-}
-
-TYPED_TEST(UniformRealDistributionTest, ChiSquaredTest50) {
-  using absl::random_internal::kChiSquared;
-  using param_type =
-      typename absl::uniform_real_distribution<TypeParam>::param_type;
-
-  constexpr size_t kTrials = 100000;
-  constexpr int kBuckets = 50;
-  constexpr double kExpected =
-      static_cast<double>(kTrials) / static_cast<double>(kBuckets);
-
-  // 1-in-100000 threshold, but remember, there are about 8 tests
-  // in this file. And the test could fail for other reasons.
-  // Empirically validated with --runs_per_test=10000.
-  const int kThreshold =
-      absl::random_internal::ChiSquareValue(kBuckets - 1, 0.999999);
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6};
-
-  for (const auto& param : {param_type(0, 1), param_type(5, 12),
-                            param_type(-5, 13), param_type(-5, -2)}) {
-    const double min_val = param.a();
-    const double max_val = param.b();
-    const double factor = kBuckets / (max_val - min_val);
-
-    std::vector<int32_t> counts(kBuckets, 0);
-    absl::uniform_real_distribution<TypeParam> dist(param);
-    for (size_t i = 0; i < kTrials; i++) {
-      auto x = dist(rng);
-      auto bucket = static_cast<size_t>((x - min_val) * factor);
-      counts[bucket]++;
-    }
-
-    double chi_square = absl::random_internal::ChiSquareWithExpected(
-        std::begin(counts), std::end(counts), kExpected);
-    if (chi_square > kThreshold) {
-      double p_value =
-          absl::random_internal::ChiSquarePValue(chi_square, kBuckets);
-
-      // Chi-squared test failed. Output does not appear to be uniform.
-      std::string msg;
-      for (const auto& a : counts) {
-        absl::StrAppend(&msg, a, "\n");
-      }
-      absl::StrAppend(&msg, kChiSquared, " p-value ", p_value, "\n");
-      absl::StrAppend(&msg, "High ", kChiSquared, " value: ", chi_square, " > ",
-                      kThreshold);
-      ABSL_RAW_LOG(INFO, "%s", msg.c_str());
-      FAIL() << msg;
-    }
-  }
-}
-
-TYPED_TEST(UniformRealDistributionTest, StabilityTest) {
-  // absl::uniform_real_distribution stability relies only on
-  // random_internal::RandU64ToDouble and random_internal::RandU64ToFloat.
-  absl::random_internal::sequence_urbg urbg(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  std::vector<int> output(12);
-
-  absl::uniform_real_distribution<TypeParam> dist;
-  std::generate(std::begin(output), std::end(output), [&] {
-    return static_cast<int>(TypeParam(1000000) * dist(urbg));
-  });
-
-  EXPECT_THAT(
-      output,  //
-      testing::ElementsAre(59, 999246, 762494, 395876, 167716, 82545, 925251,
-                           77341, 12527, 708791, 834451, 932808));
-}
-
-TEST(UniformRealDistributionTest, AlgorithmBounds) {
-  absl::uniform_real_distribution<double> dist;
-
-  {
-    // This returns the smallest value >0 from absl::uniform_real_distribution.
-    absl::random_internal::sequence_urbg urbg({0x0000000000000001ull});
-    double a = dist(urbg);
-    EXPECT_EQ(a, 5.42101086242752217004e-20);
-  }
-
-  {
-    // This returns a value very near 0.5 from absl::uniform_real_distribution.
-    absl::random_internal::sequence_urbg urbg({0x7fffffffffffffefull});
-    double a = dist(urbg);
-    EXPECT_EQ(a, 0.499999999999999944489);
-  }
-  {
-    // This returns a value very near 0.5 from absl::uniform_real_distribution.
-    absl::random_internal::sequence_urbg urbg({0x8000000000000000ull});
-    double a = dist(urbg);
-    EXPECT_EQ(a, 0.5);
-  }
-
-  {
-    // This returns the largest value <1 from absl::uniform_real_distribution.
-    absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFEFull});
-    double a = dist(urbg);
-    EXPECT_EQ(a, 0.999999999999999888978);
-  }
-  {
-    // This *ALSO* returns the largest value <1.
-    absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFFFull});
-    double a = dist(urbg);
-    EXPECT_EQ(a, 0.999999999999999888978);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/random/zipf_distribution.h b/third_party/abseil_cpp/absl/random/zipf_distribution.h
deleted file mode 100644
index 22ebc756cf..0000000000
--- a/third_party/abseil_cpp/absl/random/zipf_distribution.h
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_ZIPF_DISTRIBUTION_H_
-#define ABSL_RANDOM_ZIPF_DISTRIBUTION_H_
-
-#include <cassert>
-#include <cmath>
-#include <istream>
-#include <limits>
-#include <ostream>
-#include <type_traits>
-
-#include "absl/random/internal/iostream_state_saver.h"
-#include "absl/random/uniform_real_distribution.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::zipf_distribution produces random integer-values in the range [0, k],
-// distributed according to the discrete probability function:
-//
-//  P(x) = (v + x) ^ -q
-//
-// The parameter `v` must be greater than 0 and the parameter `q` must be
-// greater than 1. If either of these parameters take invalid values then the
-// behavior is undefined.
-//
-// IntType is the result_type generated by the generator. It must be of integral
-// type; a static_assert ensures this is the case.
-//
-// The implementation is based on W.Hormann, G.Derflinger:
-//
-// "Rejection-Inversion to Generate Variates from Monotone Discrete
-// Distributions"
-//
-// http://eeyore.wu-wien.ac.at/papers/96-04-04.wh-der.ps.gz
-//
-template <typename IntType = int>
-class zipf_distribution {
- public:
-  using result_type = IntType;
-
-  class param_type {
-   public:
-    using distribution_type = zipf_distribution;
-
-    // Preconditions: k > 0, v > 0, q > 1
-    // The precondidtions are validated when NDEBUG is not defined via
-    // a pair of assert() directives.
-    // If NDEBUG is defined and either or both of these parameters take invalid
-    // values, the behavior of the class is undefined.
-    explicit param_type(result_type k = (std::numeric_limits<IntType>::max)(),
-                        double q = 2.0, double v = 1.0);
-
-    result_type k() const { return k_; }
-    double q() const { return q_; }
-    double v() const { return v_; }
-
-    friend bool operator==(const param_type& a, const param_type& b) {
-      return a.k_ == b.k_ && a.q_ == b.q_ && a.v_ == b.v_;
-    }
-    friend bool operator!=(const param_type& a, const param_type& b) {
-      return !(a == b);
-    }
-
-   private:
-    friend class zipf_distribution;
-    inline double h(double x) const;
-    inline double hinv(double x) const;
-    inline double compute_s() const;
-    inline double pow_negative_q(double x) const;
-
-    // Parameters here are exactly the same as the parameters of Algorithm ZRI
-    // in the paper.
-    IntType k_;
-    double q_;
-    double v_;
-
-    double one_minus_q_;  // 1-q
-    double s_;
-    double one_minus_q_inv_;  // 1 / 1-q
-    double hxm_;              // h(k + 0.5)
-    double hx0_minus_hxm_;    // h(x0) - h(k + 0.5)
-
-    static_assert(std::is_integral<IntType>::value,
-                  "Class-template absl::zipf_distribution<> must be "
-                  "parameterized using an integral type.");
-  };
-
-  zipf_distribution()
-      : zipf_distribution((std::numeric_limits<IntType>::max)()) {}
-
-  explicit zipf_distribution(result_type k, double q = 2.0, double v = 1.0)
-      : param_(k, q, v) {}
-
-  explicit zipf_distribution(const param_type& p) : param_(p) {}
-
-  void reset() {}
-
-  template <typename URBG>
-  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
-    return (*this)(g, param_);
-  }
-
-  template <typename URBG>
-  result_type operator()(URBG& g,  // NOLINT(runtime/references)
-                         const param_type& p);
-
-  result_type k() const { return param_.k(); }
-  double q() const { return param_.q(); }
-  double v() const { return param_.v(); }
-
-  param_type param() const { return param_; }
-  void param(const param_type& p) { param_ = p; }
-
-  result_type(min)() const { return 0; }
-  result_type(max)() const { return k(); }
-
-  friend bool operator==(const zipf_distribution& a,
-                         const zipf_distribution& b) {
-    return a.param_ == b.param_;
-  }
-  friend bool operator!=(const zipf_distribution& a,
-                         const zipf_distribution& b) {
-    return a.param_ != b.param_;
-  }
-
- private:
-  param_type param_;
-};
-
-// --------------------------------------------------------------------------
-// Implementation details follow
-// --------------------------------------------------------------------------
-
-template <typename IntType>
-zipf_distribution<IntType>::param_type::param_type(
-    typename zipf_distribution<IntType>::result_type k, double q, double v)
-    : k_(k), q_(q), v_(v), one_minus_q_(1 - q) {
-  assert(q > 1);
-  assert(v > 0);
-  assert(k > 0);
-  one_minus_q_inv_ = 1 / one_minus_q_;
-
-  // Setup for the ZRI algorithm (pg 17 of the paper).
-  // Compute: h(i max) => h(k + 0.5)
-  constexpr double kMax = 18446744073709549568.0;
-  double kd = static_cast<double>(k);
-  // TODO(absl-team): Determine if this check is needed, and if so, add a test
-  // that fails for k > kMax
-  if (kd > kMax) {
-    // Ensure that our maximum value is capped to a value which will
-    // round-trip back through double.
-    kd = kMax;
-  }
-  hxm_ = h(kd + 0.5);
-
-  // Compute: h(0)
-  const bool use_precomputed = (v == 1.0 && q == 2.0);
-  const double h0x5 = use_precomputed ? (-1.0 / 1.5)  // exp(-log(1.5))
-                                      : h(0.5);
-  const double elogv_q = (v_ == 1.0) ? 1 : pow_negative_q(v_);
-
-  // h(0) = h(0.5) - exp(log(v) * -q)
-  hx0_minus_hxm_ = (h0x5 - elogv_q) - hxm_;
-
-  // And s
-  s_ = use_precomputed ? 0.46153846153846123 : compute_s();
-}
-
-template <typename IntType>
-double zipf_distribution<IntType>::param_type::h(double x) const {
-  // std::exp(one_minus_q_ * std::log(v_ + x)) * one_minus_q_inv_;
-  x += v_;
-  return (one_minus_q_ == -1.0)
-             ? (-1.0 / x)  // -exp(-log(x))
-             : (std::exp(std::log(x) * one_minus_q_) * one_minus_q_inv_);
-}
-
-template <typename IntType>
-double zipf_distribution<IntType>::param_type::hinv(double x) const {
-  // std::exp(one_minus_q_inv_ * std::log(one_minus_q_ * x)) - v_;
-  return -v_ + ((one_minus_q_ == -1.0)
-                    ? (-1.0 / x)  // exp(-log(-x))
-                    : std::exp(one_minus_q_inv_ * std::log(one_minus_q_ * x)));
-}
-
-template <typename IntType>
-double zipf_distribution<IntType>::param_type::compute_s() const {
-  // 1 - hinv(h(1.5) - std::exp(std::log(v_ + 1) * -q_));
-  return 1.0 - hinv(h(1.5) - pow_negative_q(v_ + 1.0));
-}
-
-template <typename IntType>
-double zipf_distribution<IntType>::param_type::pow_negative_q(double x) const {
-  // std::exp(std::log(x) * -q_);
-  return q_ == 2.0 ? (1.0 / (x * x)) : std::exp(std::log(x) * -q_);
-}
-
-template <typename IntType>
-template <typename URBG>
-typename zipf_distribution<IntType>::result_type
-zipf_distribution<IntType>::operator()(
-    URBG& g, const param_type& p) {  // NOLINT(runtime/references)
-  absl::uniform_real_distribution<double> uniform_double;
-  double k;
-  for (;;) {
-    const double v = uniform_double(g);
-    const double u = p.hxm_ + v * p.hx0_minus_hxm_;
-    const double x = p.hinv(u);
-    k = rint(x);              // std::floor(x + 0.5);
-    if (k > p.k()) continue;  // reject k > max_k
-    if (k - x <= p.s_) break;
-    const double h = p.h(k + 0.5);
-    const double r = p.pow_negative_q(p.v_ + k);
-    if (u >= h - r) break;
-  }
-  IntType ki = static_cast<IntType>(k);
-  assert(ki <= p.k_);
-  return ki;
-}
-
-template <typename CharT, typename Traits, typename IntType>
-std::basic_ostream<CharT, Traits>& operator<<(
-    std::basic_ostream<CharT, Traits>& os,  // NOLINT(runtime/references)
-    const zipf_distribution<IntType>& x) {
-  using stream_type =
-      typename random_internal::stream_format_type<IntType>::type;
-  auto saver = random_internal::make_ostream_state_saver(os);
-  os.precision(random_internal::stream_precision_helper<double>::kPrecision);
-  os << static_cast<stream_type>(x.k()) << os.fill() << x.q() << os.fill()
-     << x.v();
-  return os;
-}
-
-template <typename CharT, typename Traits, typename IntType>
-std::basic_istream<CharT, Traits>& operator>>(
-    std::basic_istream<CharT, Traits>& is,  // NOLINT(runtime/references)
-    zipf_distribution<IntType>& x) {        // NOLINT(runtime/references)
-  using result_type = typename zipf_distribution<IntType>::result_type;
-  using param_type = typename zipf_distribution<IntType>::param_type;
-  using stream_type =
-      typename random_internal::stream_format_type<IntType>::type;
-  stream_type k;
-  double q;
-  double v;
-
-  auto saver = random_internal::make_istream_state_saver(is);
-  is >> k >> q >> v;
-  if (!is.fail()) {
-    x.param(param_type(static_cast<result_type>(k), q, v));
-  }
-  return is;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_RANDOM_ZIPF_DISTRIBUTION_H_
diff --git a/third_party/abseil_cpp/absl/random/zipf_distribution_test.cc b/third_party/abseil_cpp/absl/random/zipf_distribution_test.cc
deleted file mode 100644
index f8cf70e0dd..0000000000
--- a/third_party/abseil_cpp/absl/random/zipf_distribution_test.cc
+++ /dev/null
@@ -1,427 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/random/zipf_distribution.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <iterator>
-#include <random>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/internal/chi_square.h"
-#include "absl/random/internal/pcg_engine.h"
-#include "absl/random/internal/sequence_urbg.h"
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_replace.h"
-#include "absl/strings/strip.h"
-
-namespace {
-
-using ::absl::random_internal::kChiSquared;
-using ::testing::ElementsAre;
-
-template <typename IntType>
-class ZipfDistributionTypedTest : public ::testing::Test {};
-
-using IntTypes = ::testing::Types<int, int8_t, int16_t, int32_t, int64_t,
-                                  uint8_t, uint16_t, uint32_t, uint64_t>;
-TYPED_TEST_CASE(ZipfDistributionTypedTest, IntTypes);
-
-TYPED_TEST(ZipfDistributionTypedTest, SerializeTest) {
-  using param_type = typename absl::zipf_distribution<TypeParam>::param_type;
-
-  constexpr int kCount = 1000;
-  absl::InsecureBitGen gen;
-  for (const auto& param : {
-           param_type(),
-           param_type(32),
-           param_type(100, 3, 2),
-           param_type(std::numeric_limits<TypeParam>::max(), 4, 3),
-           param_type(std::numeric_limits<TypeParam>::max() / 2),
-       }) {
-    // Validate parameters.
-    const auto k = param.k();
-    const auto q = param.q();
-    const auto v = param.v();
-
-    absl::zipf_distribution<TypeParam> before(k, q, v);
-    EXPECT_EQ(before.k(), param.k());
-    EXPECT_EQ(before.q(), param.q());
-    EXPECT_EQ(before.v(), param.v());
-
-    {
-      absl::zipf_distribution<TypeParam> via_param(param);
-      EXPECT_EQ(via_param, before);
-    }
-
-    // Validate stream serialization.
-    std::stringstream ss;
-    ss << before;
-    absl::zipf_distribution<TypeParam> after(4, 5.5, 4.4);
-
-    EXPECT_NE(before.k(), after.k());
-    EXPECT_NE(before.q(), after.q());
-    EXPECT_NE(before.v(), after.v());
-    EXPECT_NE(before.param(), after.param());
-    EXPECT_NE(before, after);
-
-    ss >> after;
-
-    EXPECT_EQ(before.k(), after.k());
-    EXPECT_EQ(before.q(), after.q());
-    EXPECT_EQ(before.v(), after.v());
-    EXPECT_EQ(before.param(), after.param());
-    EXPECT_EQ(before, after);
-
-    // Smoke test.
-    auto sample_min = after.max();
-    auto sample_max = after.min();
-    for (int i = 0; i < kCount; i++) {
-      auto sample = after(gen);
-      EXPECT_GE(sample, after.min());
-      EXPECT_LE(sample, after.max());
-      if (sample > sample_max) sample_max = sample;
-      if (sample < sample_min) sample_min = sample;
-    }
-    ABSL_INTERNAL_LOG(INFO,
-                      absl::StrCat("Range: ", +sample_min, ", ", +sample_max));
-  }
-}
-
-class ZipfModel {
- public:
-  ZipfModel(size_t k, double q, double v) : k_(k), q_(q), v_(v) {}
-
-  double mean() const { return mean_; }
-
-  // For the other moments of the Zipf distribution, see, for example,
-  // http://mathworld.wolfram.com/ZipfDistribution.html
-
-  // PMF(k) = (1 / k^s) / H(N,s)
-  // Returns the probability that any single invocation returns k.
-  double PMF(size_t i) { return i >= hnq_.size() ? 0.0 : hnq_[i] / sum_hnq_; }
-
-  // CDF = H(k, s) / H(N,s)
-  double CDF(size_t i) {
-    if (i >= hnq_.size()) {
-      return 1.0;
-    }
-    auto it = std::begin(hnq_);
-    double h = 0.0;
-    for (const auto end = it; it != end; it++) {
-      h += *it;
-    }
-    return h / sum_hnq_;
-  }
-
-  // The InverseCDF returns the k values which bound p on the upper and lower
-  // bound. Since there is no closed-form solution, this is implemented as a
-  // bisction of the cdf.
-  std::pair<size_t, size_t> InverseCDF(double p) {
-    size_t min = 0;
-    size_t max = hnq_.size();
-    while (max > min + 1) {
-      size_t target = (max + min) >> 1;
-      double x = CDF(target);
-      if (x > p) {
-        max = target;
-      } else {
-        min = target;
-      }
-    }
-    return {min, max};
-  }
-
-  // Compute the probability totals, which are based on the generalized harmonic
-  // number, H(N,s).
-  //   H(N,s) == SUM(k=1..N, 1 / k^s)
-  //
-  // In the limit, H(N,s) == zetac(s) + 1.
-  //
-  // NOTE: The mean of a zipf distribution could be computed here as well.
-  // Mean :=  H(N, s-1) / H(N,s).
-  // Given the parameter v = 1, this gives the following function:
-  // (Hn(100, 1) - Hn(1,1)) / (Hn(100,2) - Hn(1,2)) = 6.5944
-  //
-  void Init() {
-    if (!hnq_.empty()) {
-      return;
-    }
-    hnq_.clear();
-    hnq_.reserve(std::min(k_, size_t{1000}));
-
-    sum_hnq_ = 0;
-    double qm1 = q_ - 1.0;
-    double sum_hnq_m1 = 0;
-    for (size_t i = 0; i < k_; i++) {
-      // Partial n-th generalized harmonic number
-      const double x = v_ + i;
-
-      // H(n, q-1)
-      const double hnqm1 =
-          (q_ == 2.0) ? (1.0 / x)
-                      : (q_ == 3.0) ? (1.0 / (x * x)) : std::pow(x, -qm1);
-      sum_hnq_m1 += hnqm1;
-
-      // H(n, q)
-      const double hnq =
-          (q_ == 2.0) ? (1.0 / (x * x))
-                      : (q_ == 3.0) ? (1.0 / (x * x * x)) : std::pow(x, -q_);
-      sum_hnq_ += hnq;
-      hnq_.push_back(hnq);
-      if (i > 1000 && hnq <= 1e-10) {
-        // The harmonic number is too small.
-        break;
-      }
-    }
-    assert(sum_hnq_ > 0);
-    mean_ = sum_hnq_m1 / sum_hnq_;
-  }
-
- private:
-  const size_t k_;
-  const double q_;
-  const double v_;
-
-  double mean_;
-  std::vector<double> hnq_;
-  double sum_hnq_;
-};
-
-using zipf_u64 = absl::zipf_distribution<uint64_t>;
-
-class ZipfTest : public testing::TestWithParam<zipf_u64::param_type>,
-                 public ZipfModel {
- public:
-  ZipfTest() : ZipfModel(GetParam().k(), GetParam().q(), GetParam().v()) {}
-
-  // We use a fixed bit generator for distribution accuracy tests.  This allows
-  // these tests to be deterministic, while still testing the qualify of the
-  // implementation.
-  absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6};
-};
-
-TEST_P(ZipfTest, ChiSquaredTest) {
-  const auto& param = GetParam();
-  Init();
-
-  size_t trials = 10000;
-
-  // Find the split-points for the buckets.
-  std::vector<size_t> points;
-  std::vector<double> expected;
-  {
-    double last_cdf = 0.0;
-    double min_p = 1.0;
-    for (double p = 0.01; p < 1.0; p += 0.01) {
-      auto x = InverseCDF(p);
-      if (points.empty() || points.back() < x.second) {
-        const double p = CDF(x.second);
-        points.push_back(x.second);
-        double q = p - last_cdf;
-        expected.push_back(q);
-        last_cdf = p;
-        if (q < min_p) {
-          min_p = q;
-        }
-      }
-    }
-    if (last_cdf < 0.999) {
-      points.push_back(std::numeric_limits<size_t>::max());
-      double q = 1.0 - last_cdf;
-      expected.push_back(q);
-      if (q < min_p) {
-        min_p = q;
-      }
-    } else {
-      points.back() = std::numeric_limits<size_t>::max();
-      expected.back() += (1.0 - last_cdf);
-    }
-    // The Chi-Squared score is not completely scale-invariant; it works best
-    // when the small values are in the small digits.
-    trials = static_cast<size_t>(8.0 / min_p);
-  }
-  ASSERT_GT(points.size(), 0);
-
-  // Generate n variates and fill the counts vector with the count of their
-  // occurrences.
-  std::vector<int64_t> buckets(points.size(), 0);
-  double avg = 0;
-  {
-    zipf_u64 dis(param);
-    for (size_t i = 0; i < trials; i++) {
-      uint64_t x = dis(rng_);
-      ASSERT_LE(x, dis.max());
-      ASSERT_GE(x, dis.min());
-      avg += static_cast<double>(x);
-      auto it = std::upper_bound(std::begin(points), std::end(points),
-                                 static_cast<size_t>(x));
-      buckets[std::distance(std::begin(points), it)]++;
-    }
-    avg = avg / static_cast<double>(trials);
-  }
-
-  // Validate the output using the Chi-Squared test.
-  for (auto& e : expected) {
-    e *= trials;
-  }
-
-  // The null-hypothesis is that the distribution is a poisson distribution with
-  // the provided mean (not estimated from the data).
-  const int dof = static_cast<int>(expected.size()) - 1;
-
-  // NOTE: This test runs about 15x per invocation, so a value of 0.9995 is
-  // approximately correct for a test suite failure rate of 1 in 100.  In
-  // practice we see failures slightly higher than that.
-  const double threshold = absl::random_internal::ChiSquareValue(dof, 0.9999);
-
-  const double chi_square = absl::random_internal::ChiSquare(
-      std::begin(buckets), std::end(buckets), std::begin(expected),
-      std::end(expected));
-
-  const double p_actual =
-      absl::random_internal::ChiSquarePValue(chi_square, dof);
-
-  // Log if the chi_squared value is above the threshold.
-  if (chi_square > threshold) {
-    ABSL_INTERNAL_LOG(INFO, "values");
-    for (size_t i = 0; i < expected.size(); i++) {
-      ABSL_INTERNAL_LOG(INFO, absl::StrCat(points[i], ": ", buckets[i],
-                                           " vs. E=", expected[i]));
-    }
-    ABSL_INTERNAL_LOG(INFO, absl::StrCat("trials ", trials));
-    ABSL_INTERNAL_LOG(INFO,
-                      absl::StrCat("mean ", avg, " vs. expected ", mean()));
-    ABSL_INTERNAL_LOG(INFO, absl::StrCat(kChiSquared, "(data, ", dof, ") = ",
-                                         chi_square, " (", p_actual, ")"));
-    ABSL_INTERNAL_LOG(INFO,
-                      absl::StrCat(kChiSquared, " @ 0.9995 = ", threshold));
-    FAIL() << kChiSquared << " value of " << chi_square
-           << " is above the threshold.";
-  }
-}
-
-std::vector<zipf_u64::param_type> GenParams() {
-  using param = zipf_u64::param_type;
-  const auto k = param().k();
-  const auto q = param().q();
-  const auto v = param().v();
-  const uint64_t k2 = 1 << 10;
-  return std::vector<zipf_u64::param_type>{
-      // Default
-      param(k, q, v),
-      // vary K
-      param(4, q, v), param(1 << 4, q, v), param(k2, q, v),
-      // vary V
-      param(k2, q, 0.5), param(k2, q, 1.5), param(k2, q, 2.5), param(k2, q, 10),
-      // vary Q
-      param(k2, 1.5, v), param(k2, 3, v), param(k2, 5, v), param(k2, 10, v),
-      // Vary V & Q
-      param(k2, 1.5, 0.5), param(k2, 3, 1.5), param(k, 10, 10)};
-}
-
-std::string ParamName(
-    const ::testing::TestParamInfo<zipf_u64::param_type>& info) {
-  const auto& p = info.param;
-  std::string name = absl::StrCat("k_", p.k(), "__q_", absl::SixDigits(p.q()),
-                                  "__v_", absl::SixDigits(p.v()));
-  return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
-}
-
-INSTANTIATE_TEST_SUITE_P(All, ZipfTest, ::testing::ValuesIn(GenParams()),
-                         ParamName);
-
-// NOTE: absl::zipf_distribution is not guaranteed to be stable.
-TEST(ZipfDistributionTest, StabilityTest) {
-  // absl::zipf_distribution stability relies on
-  // absl::uniform_real_distribution, std::log, std::exp, std::log1p
-  absl::random_internal::sequence_urbg urbg(
-      {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
-       0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
-       0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
-       0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
-
-  std::vector<int> output(10);
-
-  {
-    absl::zipf_distribution<int32_t> dist;
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return dist(urbg); });
-    EXPECT_THAT(output, ElementsAre(10031, 0, 0, 3, 6, 0, 7, 47, 0, 0));
-  }
-  urbg.reset();
-  {
-    absl::zipf_distribution<int32_t> dist(std::numeric_limits<int32_t>::max(),
-                                          3.3);
-    std::generate(std::begin(output), std::end(output),
-                  [&] { return dist(urbg); });
-    EXPECT_THAT(output, ElementsAre(44, 0, 0, 0, 0, 1, 0, 1, 3, 0));
-  }
-}
-
-TEST(ZipfDistributionTest, AlgorithmBounds) {
-  absl::zipf_distribution<int32_t> dist;
-
-  // Small values from absl::uniform_real_distribution map to larger Zipf
-  // distribution values.
-  const std::pair<uint64_t, int32_t> kInputs[] = {
-      {0xffffffffffffffff, 0x0}, {0x7fffffffffffffff, 0x0},
-      {0x3ffffffffffffffb, 0x1}, {0x1ffffffffffffffd, 0x4},
-      {0xffffffffffffffe, 0x9},  {0x7ffffffffffffff, 0x12},
-      {0x3ffffffffffffff, 0x25}, {0x1ffffffffffffff, 0x4c},
-      {0xffffffffffffff, 0x99},  {0x7fffffffffffff, 0x132},
-      {0x3fffffffffffff, 0x265}, {0x1fffffffffffff, 0x4cc},
-      {0xfffffffffffff, 0x999},  {0x7ffffffffffff, 0x1332},
-      {0x3ffffffffffff, 0x2665}, {0x1ffffffffffff, 0x4ccc},
-      {0xffffffffffff, 0x9998},  {0x7fffffffffff, 0x1332f},
-      {0x3fffffffffff, 0x2665a}, {0x1fffffffffff, 0x4cc9e},
-      {0xfffffffffff, 0x998e0},  {0x7ffffffffff, 0x133051},
-      {0x3ffffffffff, 0x265ae4}, {0x1ffffffffff, 0x4c9ed3},
-      {0xffffffffff, 0x98e223},  {0x7fffffffff, 0x13058c4},
-      {0x3fffffffff, 0x25b178e}, {0x1fffffffff, 0x4a062b2},
-      {0xfffffffff, 0x8ee23b8},  {0x7ffffffff, 0x10b21642},
-      {0x3ffffffff, 0x1d89d89d}, {0x1ffffffff, 0x2fffffff},
-      {0xffffffff, 0x45d1745d},  {0x7fffffff, 0x5a5a5a5a},
-      {0x3fffffff, 0x69ee5846},  {0x1fffffff, 0x73ecade3},
-      {0xfffffff, 0x79a9d260},   {0x7ffffff, 0x7cc0532b},
-      {0x3ffffff, 0x7e5ad146},   {0x1ffffff, 0x7f2c0bec},
-      {0xffffff, 0x7f95adef},    {0x7fffff, 0x7fcac0da},
-      {0x3fffff, 0x7fe55ae2},    {0x1fffff, 0x7ff2ac0e},
-      {0xfffff, 0x7ff955ae},     {0x7ffff, 0x7ffcaac1},
-      {0x3ffff, 0x7ffe555b},     {0x1ffff, 0x7fff2aac},
-      {0xffff, 0x7fff9556},      {0x7fff, 0x7fffcaab},
-      {0x3fff, 0x7fffe555},      {0x1fff, 0x7ffff2ab},
-      {0xfff, 0x7ffff955},       {0x7ff, 0x7ffffcab},
-      {0x3ff, 0x7ffffe55},       {0x1ff, 0x7fffff2b},
-      {0xff, 0x7fffff95},        {0x7f, 0x7fffffcb},
-      {0x3f, 0x7fffffe5},        {0x1f, 0x7ffffff3},
-      {0xf, 0x7ffffff9},         {0x7, 0x7ffffffd},
-      {0x3, 0x7ffffffe},         {0x1, 0x7fffffff},
-  };
-
-  for (const auto& instance : kInputs) {
-    absl::random_internal::sequence_urbg urbg({instance.first});
-    EXPECT_EQ(instance.second, dist(urbg));
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/status/BUILD.bazel b/third_party/abseil_cpp/absl/status/BUILD.bazel
deleted file mode 100644
index 189bd73d0e..0000000000
--- a/third_party/abseil_cpp/absl/status/BUILD.bazel
+++ /dev/null
@@ -1,103 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This package contains `absl::Status`.
-# It will expand later to have utilities around `Status` like `StatusOr`,
-# `StatusBuilder` and macros.
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "status",
-    srcs = [
-        "internal/status_internal.h",
-        "status.cc",
-        "status_payload_printer.cc",
-    ],
-    hdrs = [
-        "status.h",
-        "status_payload_printer.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    deps = [
-        "//absl/base:atomic_hook",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/container:inlined_vector",
-        "//absl/debugging:stacktrace",
-        "//absl/debugging:symbolize",
-        "//absl/strings",
-        "//absl/strings:cord",
-        "//absl/strings:str_format",
-        "//absl/types:optional",
-    ],
-)
-
-cc_test(
-    name = "status_test",
-    srcs = ["status_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":status",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "statusor",
-    srcs = [
-        "internal/statusor_internal.h",
-        "statusor.cc",
-    ],
-    hdrs = [
-        "statusor.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    deps = [
-        ":status",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "//absl/types:variant",
-        "//absl/utility",
-    ],
-)
-
-cc_test(
-    name = "statusor_test",
-    size = "small",
-    srcs = ["statusor_test.cc"],
-    deps = [
-        ":status",
-        ":statusor",
-        "//absl/base",
-        "//absl/memory",
-        "//absl/types:any",
-        "//absl/utility",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/status/CMakeLists.txt b/third_party/abseil_cpp/absl/status/CMakeLists.txt
deleted file mode 100644
index f0d798a373..0000000000
--- a/third_party/abseil_cpp/absl/status/CMakeLists.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-# Copyright 2020 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-absl_cc_library(
-  NAME
-    status
-  HDRS
-    "status.h"
-  SRCS
-    "internal/status_internal.h"
-    "status.cc"
-    "status_payload_printer.h"
-    "status_payload_printer.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::atomic_hook
-    absl::config
-    absl::core_headers
-    absl::raw_logging_internal
-    absl::inlined_vector
-    absl::stacktrace
-    absl::symbolize
-    absl::strings
-    absl::cord
-    absl::str_format
-    absl::optional
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    status_test
-  SRCS
-   "status_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::status
-    absl::strings
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    statusor
-  HDRS
-    "statusor.h"
-  SRCS
-    "statusor.cc"
-    "internal/statusor_internal.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::status
-    absl::core_headers
-    absl::raw_logging_internal
-    absl::type_traits
-    absl::strings
-    absl::utility
-    absl::variant
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    statusor_test
-  SRCS
-   "statusor_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::status
-    absl::statusor
-    gmock_main
-)
diff --git a/third_party/abseil_cpp/absl/status/internal/status_internal.h b/third_party/abseil_cpp/absl/status/internal/status_internal.h
deleted file mode 100644
index 279f8f55be..0000000000
--- a/third_party/abseil_cpp/absl/status/internal/status_internal.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#ifndef ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
-#define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
-
-#include <string>
-
-#include "absl/container/inlined_vector.h"
-#include "absl/strings/cord.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-enum class StatusCode : int;
-
-namespace status_internal {
-
-// Container for status payloads.
-struct Payload {
-  std::string type_url;
-  absl::Cord payload;
-};
-
-using Payloads = absl::InlinedVector<Payload, 1>;
-
-// Reference-counted representation of Status data.
-struct StatusRep {
-  StatusRep(absl::StatusCode code, std::string message,
-            std::unique_ptr<status_internal::Payloads> payloads)
-      : ref(int32_t{1}),
-        code(code),
-        message(std::move(message)),
-        payloads(std::move(payloads)) {}
-
-  std::atomic<int32_t> ref;
-  absl::StatusCode code;
-  std::string message;
-  std::unique_ptr<status_internal::Payloads> payloads;
-};
-
-absl::StatusCode MapToLocalCode(int value);
-}  // namespace status_internal
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
diff --git a/third_party/abseil_cpp/absl/status/internal/statusor_internal.h b/third_party/abseil_cpp/absl/status/internal/statusor_internal.h
deleted file mode 100644
index eaac2c0b14..0000000000
--- a/third_party/abseil_cpp/absl/status/internal/statusor_internal.h
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
-#define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
-
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/attributes.h"
-#include "absl/meta/type_traits.h"
-#include "absl/status/status.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-template <typename T>
-class ABSL_MUST_USE_RESULT StatusOr;
-
-namespace internal_statusor {
-
-// Detects whether `U` has conversion operator to `StatusOr<T>`, i.e. `operator
-// StatusOr<T>()`.
-template <typename T, typename U, typename = void>
-struct HasConversionOperatorToStatusOr : std::false_type {};
-
-template <typename T, typename U>
-void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]);
-
-template <typename T, typename U>
-struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))>
-    : std::true_type {};
-
-// Detects whether `T` is constructible or convertible from `StatusOr<U>`.
-template <typename T, typename U>
-using IsConstructibleOrConvertibleFromStatusOr =
-    absl::disjunction<std::is_constructible<T, StatusOr<U>&>,
-                      std::is_constructible<T, const StatusOr<U>&>,
-                      std::is_constructible<T, StatusOr<U>&&>,
-                      std::is_constructible<T, const StatusOr<U>&&>,
-                      std::is_convertible<StatusOr<U>&, T>,
-                      std::is_convertible<const StatusOr<U>&, T>,
-                      std::is_convertible<StatusOr<U>&&, T>,
-                      std::is_convertible<const StatusOr<U>&&, T>>;
-
-// Detects whether `T` is constructible or convertible or assignable from
-// `StatusOr<U>`.
-template <typename T, typename U>
-using IsConstructibleOrConvertibleOrAssignableFromStatusOr =
-    absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>,
-                      std::is_assignable<T&, StatusOr<U>&>,
-                      std::is_assignable<T&, const StatusOr<U>&>,
-                      std::is_assignable<T&, StatusOr<U>&&>,
-                      std::is_assignable<T&, const StatusOr<U>&&>>;
-
-// Detects whether direct initializing `StatusOr<T>` from `U` is ambiguous, i.e.
-// when `U` is `StatusOr<V>` and `T` is constructible or convertible from `V`.
-template <typename T, typename U>
-struct IsDirectInitializationAmbiguous
-    : public absl::conditional_t<
-          std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
-                       U>::value,
-          std::false_type,
-          IsDirectInitializationAmbiguous<
-              T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {};
-
-template <typename T, typename V>
-struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>>
-    : public IsConstructibleOrConvertibleFromStatusOr<T, V> {};
-
-// Checks against the constraints of the direction initialization, i.e. when
-// `StatusOr<T>::StatusOr(U&&)` should participate in overload resolution.
-template <typename T, typename U>
-using IsDirectInitializationValid = absl::disjunction<
-    // Short circuits if T is basically U.
-    std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
-    absl::negation<absl::disjunction<
-        std::is_same<absl::StatusOr<T>,
-                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
-        std::is_same<absl::Status,
-                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
-        std::is_same<absl::in_place_t,
-                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
-        IsDirectInitializationAmbiguous<T, U>>>>;
-
-// This trait detects whether `StatusOr<T>::operator=(U&&)` is ambiguous, which
-// is equivalent to whether all the following conditions are met:
-// 1. `U` is `StatusOr<V>`.
-// 2. `T` is constructible and assignable from `V`.
-// 3. `T` is constructible and assignable from `U` (i.e. `StatusOr<V>`).
-// For example, the following code is considered ambiguous:
-// (`T` is `bool`, `U` is `StatusOr<bool>`, `V` is `bool`)
-//   StatusOr<bool> s1 = true;  // s1.ok() && s1.ValueOrDie() == true
-//   StatusOr<bool> s2 = false;  // s2.ok() && s2.ValueOrDie() == false
-//   s1 = s2;  // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`?
-template <typename T, typename U>
-struct IsForwardingAssignmentAmbiguous
-    : public absl::conditional_t<
-          std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
-                       U>::value,
-          std::false_type,
-          IsForwardingAssignmentAmbiguous<
-              T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {};
-
-template <typename T, typename U>
-struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>>
-    : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {};
-
-// Checks against the constraints of the forwarding assignment, i.e. whether
-// `StatusOr<T>::operator(U&&)` should participate in overload resolution.
-template <typename T, typename U>
-using IsForwardingAssignmentValid = absl::disjunction<
-    // Short circuits if T is basically U.
-    std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
-    absl::negation<absl::disjunction<
-        std::is_same<absl::StatusOr<T>,
-                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
-        std::is_same<absl::Status,
-                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
-        std::is_same<absl::in_place_t,
-                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
-        IsForwardingAssignmentAmbiguous<T, U>>>>;
-
-class Helper {
- public:
-  // Move type-agnostic error handling to the .cc.
-  static void HandleInvalidStatusCtorArg(Status*);
-  ABSL_ATTRIBUTE_NORETURN static void Crash(const absl::Status& status);
-};
-
-// Construct an instance of T in `p` through placement new, passing Args... to
-// the constructor.
-// This abstraction is here mostly for the gcc performance fix.
-template <typename T, typename... Args>
-ABSL_ATTRIBUTE_NONNULL(1) void PlacementNew(void* p, Args&&... args) {
-  new (p) T(std::forward<Args>(args)...);
-}
-
-// Helper base class to hold the data and all operations.
-// We move all this to a base class to allow mixing with the appropriate
-// TraitsBase specialization.
-template <typename T>
-class StatusOrData {
-  template <typename U>
-  friend class StatusOrData;
-
- public:
-  StatusOrData() = delete;
-
-  StatusOrData(const StatusOrData& other) {
-    if (other.ok()) {
-      MakeValue(other.data_);
-      MakeStatus();
-    } else {
-      MakeStatus(other.status_);
-    }
-  }
-
-  StatusOrData(StatusOrData&& other) noexcept {
-    if (other.ok()) {
-      MakeValue(std::move(other.data_));
-      MakeStatus();
-    } else {
-      MakeStatus(std::move(other.status_));
-    }
-  }
-
-  template <typename U>
-  explicit StatusOrData(const StatusOrData<U>& other) {
-    if (other.ok()) {
-      MakeValue(other.data_);
-      MakeStatus();
-    } else {
-      MakeStatus(other.status_);
-    }
-  }
-
-  template <typename U>
-  explicit StatusOrData(StatusOrData<U>&& other) {
-    if (other.ok()) {
-      MakeValue(std::move(other.data_));
-      MakeStatus();
-    } else {
-      MakeStatus(std::move(other.status_));
-    }
-  }
-
-  template <typename... Args>
-  explicit StatusOrData(absl::in_place_t, Args&&... args)
-      : data_(std::forward<Args>(args)...) {
-    MakeStatus();
-  }
-
-  explicit StatusOrData(const T& value) : data_(value) {
-    MakeStatus();
-  }
-  explicit StatusOrData(T&& value) : data_(std::move(value)) {
-    MakeStatus();
-  }
-
-  template <typename U,
-            absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value,
-                              int> = 0>
-  explicit StatusOrData(U&& v) : status_(std::forward<U>(v)) {
-    EnsureNotOk();
-  }
-
-  StatusOrData& operator=(const StatusOrData& other) {
-    if (this == &other) return *this;
-    if (other.ok())
-      Assign(other.data_);
-    else
-      AssignStatus(other.status_);
-    return *this;
-  }
-
-  StatusOrData& operator=(StatusOrData&& other) {
-    if (this == &other) return *this;
-    if (other.ok())
-      Assign(std::move(other.data_));
-    else
-      AssignStatus(std::move(other.status_));
-    return *this;
-  }
-
-  ~StatusOrData() {
-    if (ok()) {
-      status_.~Status();
-      data_.~T();
-    } else {
-      status_.~Status();
-    }
-  }
-
-  template <typename U>
-  void Assign(U&& value) {
-    if (ok()) {
-      data_ = std::forward<U>(value);
-    } else {
-      MakeValue(std::forward<U>(value));
-      status_ = OkStatus();
-    }
-  }
-
-  template <typename U>
-  void AssignStatus(U&& v) {
-    Clear();
-    status_ = static_cast<absl::Status>(std::forward<U>(v));
-    EnsureNotOk();
-  }
-
-  bool ok() const { return status_.ok(); }
-
- protected:
-  // status_ will always be active after the constructor.
-  // We make it a union to be able to initialize exactly how we need without
-  // waste.
-  // Eg. in the copy constructor we use the default constructor of Status in
-  // the ok() path to avoid an extra Ref call.
-  union {
-    Status status_;
-  };
-
-  // data_ is active iff status_.ok()==true
-  struct Dummy {};
-  union {
-    // When T is const, we need some non-const object we can cast to void* for
-    // the placement new. dummy_ is that object.
-    Dummy dummy_;
-    T data_;
-  };
-
-  void Clear() {
-    if (ok()) data_.~T();
-  }
-
-  void EnsureOk() const {
-    if (ABSL_PREDICT_FALSE(!ok())) Helper::Crash(status_);
-  }
-
-  void EnsureNotOk() {
-    if (ABSL_PREDICT_FALSE(ok())) Helper::HandleInvalidStatusCtorArg(&status_);
-  }
-
-  // Construct the value (ie. data_) through placement new with the passed
-  // argument.
-  template <typename... Arg>
-  void MakeValue(Arg&&... arg) {
-    internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...);
-  }
-
-  // Construct the status (ie. status_) through placement new with the passed
-  // argument.
-  template <typename... Args>
-  void MakeStatus(Args&&... args) {
-    internal_statusor::PlacementNew<Status>(&status_,
-                                            std::forward<Args>(args)...);
-  }
-};
-
-// Helper base classes to allow implicitly deleted constructors and assignment
-// operators in `StatusOr`. For example, `CopyCtorBase` will explicitly delete
-// the copy constructor when T is not copy constructible and `StatusOr` will
-// inherit that behavior implicitly.
-template <typename T, bool = std::is_copy_constructible<T>::value>
-struct CopyCtorBase {
-  CopyCtorBase() = default;
-  CopyCtorBase(const CopyCtorBase&) = default;
-  CopyCtorBase(CopyCtorBase&&) = default;
-  CopyCtorBase& operator=(const CopyCtorBase&) = default;
-  CopyCtorBase& operator=(CopyCtorBase&&) = default;
-};
-
-template <typename T>
-struct CopyCtorBase<T, false> {
-  CopyCtorBase() = default;
-  CopyCtorBase(const CopyCtorBase&) = delete;
-  CopyCtorBase(CopyCtorBase&&) = default;
-  CopyCtorBase& operator=(const CopyCtorBase&) = default;
-  CopyCtorBase& operator=(CopyCtorBase&&) = default;
-};
-
-template <typename T, bool = std::is_move_constructible<T>::value>
-struct MoveCtorBase {
-  MoveCtorBase() = default;
-  MoveCtorBase(const MoveCtorBase&) = default;
-  MoveCtorBase(MoveCtorBase&&) = default;
-  MoveCtorBase& operator=(const MoveCtorBase&) = default;
-  MoveCtorBase& operator=(MoveCtorBase&&) = default;
-};
-
-template <typename T>
-struct MoveCtorBase<T, false> {
-  MoveCtorBase() = default;
-  MoveCtorBase(const MoveCtorBase&) = default;
-  MoveCtorBase(MoveCtorBase&&) = delete;
-  MoveCtorBase& operator=(const MoveCtorBase&) = default;
-  MoveCtorBase& operator=(MoveCtorBase&&) = default;
-};
-
-template <typename T, bool = std::is_copy_constructible<T>::value&&
-                          std::is_copy_assignable<T>::value>
-struct CopyAssignBase {
-  CopyAssignBase() = default;
-  CopyAssignBase(const CopyAssignBase&) = default;
-  CopyAssignBase(CopyAssignBase&&) = default;
-  CopyAssignBase& operator=(const CopyAssignBase&) = default;
-  CopyAssignBase& operator=(CopyAssignBase&&) = default;
-};
-
-template <typename T>
-struct CopyAssignBase<T, false> {
-  CopyAssignBase() = default;
-  CopyAssignBase(const CopyAssignBase&) = default;
-  CopyAssignBase(CopyAssignBase&&) = default;
-  CopyAssignBase& operator=(const CopyAssignBase&) = delete;
-  CopyAssignBase& operator=(CopyAssignBase&&) = default;
-};
-
-template <typename T, bool = std::is_move_constructible<T>::value&&
-                          std::is_move_assignable<T>::value>
-struct MoveAssignBase {
-  MoveAssignBase() = default;
-  MoveAssignBase(const MoveAssignBase&) = default;
-  MoveAssignBase(MoveAssignBase&&) = default;
-  MoveAssignBase& operator=(const MoveAssignBase&) = default;
-  MoveAssignBase& operator=(MoveAssignBase&&) = default;
-};
-
-template <typename T>
-struct MoveAssignBase<T, false> {
-  MoveAssignBase() = default;
-  MoveAssignBase(const MoveAssignBase&) = default;
-  MoveAssignBase(MoveAssignBase&&) = default;
-  MoveAssignBase& operator=(const MoveAssignBase&) = default;
-  MoveAssignBase& operator=(MoveAssignBase&&) = delete;
-};
-
-ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status);
-
-}  // namespace internal_statusor
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
diff --git a/third_party/abseil_cpp/absl/status/status.cc b/third_party/abseil_cpp/absl/status/status.cc
deleted file mode 100644
index c71de84682..0000000000
--- a/third_party/abseil_cpp/absl/status/status.cc
+++ /dev/null
@@ -1,442 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#include "absl/status/status.h"
-
-#include <cassert>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/debugging/stacktrace.h"
-#include "absl/debugging/symbolize.h"
-#include "absl/status/status_payload_printer.h"
-#include "absl/strings/escaping.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_split.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-std::string StatusCodeToString(StatusCode code) {
-  switch (code) {
-    case StatusCode::kOk:
-      return "OK";
-    case StatusCode::kCancelled:
-      return "CANCELLED";
-    case StatusCode::kUnknown:
-      return "UNKNOWN";
-    case StatusCode::kInvalidArgument:
-      return "INVALID_ARGUMENT";
-    case StatusCode::kDeadlineExceeded:
-      return "DEADLINE_EXCEEDED";
-    case StatusCode::kNotFound:
-      return "NOT_FOUND";
-    case StatusCode::kAlreadyExists:
-      return "ALREADY_EXISTS";
-    case StatusCode::kPermissionDenied:
-      return "PERMISSION_DENIED";
-    case StatusCode::kUnauthenticated:
-      return "UNAUTHENTICATED";
-    case StatusCode::kResourceExhausted:
-      return "RESOURCE_EXHAUSTED";
-    case StatusCode::kFailedPrecondition:
-      return "FAILED_PRECONDITION";
-    case StatusCode::kAborted:
-      return "ABORTED";
-    case StatusCode::kOutOfRange:
-      return "OUT_OF_RANGE";
-    case StatusCode::kUnimplemented:
-      return "UNIMPLEMENTED";
-    case StatusCode::kInternal:
-      return "INTERNAL";
-    case StatusCode::kUnavailable:
-      return "UNAVAILABLE";
-    case StatusCode::kDataLoss:
-      return "DATA_LOSS";
-    default:
-      return "";
-  }
-}
-
-std::ostream& operator<<(std::ostream& os, StatusCode code) {
-  return os << StatusCodeToString(code);
-}
-
-namespace status_internal {
-
-static int FindPayloadIndexByUrl(const Payloads* payloads,
-                                 absl::string_view type_url) {
-  if (payloads == nullptr) return -1;
-
-  for (size_t i = 0; i < payloads->size(); ++i) {
-    if ((*payloads)[i].type_url == type_url) return i;
-  }
-
-  return -1;
-}
-
-// Convert canonical code to a value known to this binary.
-absl::StatusCode MapToLocalCode(int value) {
-  absl::StatusCode code = static_cast<absl::StatusCode>(value);
-  switch (code) {
-    case absl::StatusCode::kOk:
-    case absl::StatusCode::kCancelled:
-    case absl::StatusCode::kUnknown:
-    case absl::StatusCode::kInvalidArgument:
-    case absl::StatusCode::kDeadlineExceeded:
-    case absl::StatusCode::kNotFound:
-    case absl::StatusCode::kAlreadyExists:
-    case absl::StatusCode::kPermissionDenied:
-    case absl::StatusCode::kResourceExhausted:
-    case absl::StatusCode::kFailedPrecondition:
-    case absl::StatusCode::kAborted:
-    case absl::StatusCode::kOutOfRange:
-    case absl::StatusCode::kUnimplemented:
-    case absl::StatusCode::kInternal:
-    case absl::StatusCode::kUnavailable:
-    case absl::StatusCode::kDataLoss:
-    case absl::StatusCode::kUnauthenticated:
-      return code;
-    default:
-      return absl::StatusCode::kUnknown;
-  }
-}
-}  // namespace status_internal
-
-absl::optional<absl::Cord> Status::GetPayload(
-    absl::string_view type_url) const {
-  const auto* payloads = GetPayloads();
-  int index = status_internal::FindPayloadIndexByUrl(payloads, type_url);
-  if (index != -1) return (*payloads)[index].payload;
-
-  return absl::nullopt;
-}
-
-void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
-  if (ok()) return;
-
-  PrepareToModify();
-
-  status_internal::StatusRep* rep = RepToPointer(rep_);
-  if (!rep->payloads) {
-    rep->payloads = absl::make_unique<status_internal::Payloads>();
-  }
-
-  int index =
-      status_internal::FindPayloadIndexByUrl(rep->payloads.get(), type_url);
-  if (index != -1) {
-    (*rep->payloads)[index].payload = std::move(payload);
-    return;
-  }
-
-  rep->payloads->push_back({std::string(type_url), std::move(payload)});
-}
-
-bool Status::ErasePayload(absl::string_view type_url) {
-  int index = status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url);
-  if (index != -1) {
-    PrepareToModify();
-    GetPayloads()->erase(GetPayloads()->begin() + index);
-    if (GetPayloads()->empty() && message().empty()) {
-      // Special case: If this can be represented inlined, it MUST be
-      // inlined (EqualsSlow depends on this behavior).
-      StatusCode c = static_cast<StatusCode>(raw_code());
-      Unref(rep_);
-      rep_ = CodeToInlinedRep(c);
-    }
-    return true;
-  }
-
-  return false;
-}
-
-void Status::ForEachPayload(
-    const std::function<void(absl::string_view, const absl::Cord&)>& visitor)
-    const {
-  if (auto* payloads = GetPayloads()) {
-    bool in_reverse =
-        payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
-
-    for (size_t index = 0; index < payloads->size(); ++index) {
-      const auto& elem =
-          (*payloads)[in_reverse ? payloads->size() - 1 - index : index];
-
-#ifdef NDEBUG
-      visitor(elem.type_url, elem.payload);
-#else
-      // In debug mode invalidate the type url to prevent users from relying on
-      // this string lifetime.
-
-      // NOLINTNEXTLINE intentional extra conversion to force temporary.
-      visitor(std::string(elem.type_url), elem.payload);
-#endif  // NDEBUG
-    }
-  }
-}
-
-const std::string* Status::EmptyString() {
-  static std::string* empty_string = new std::string();
-  return empty_string;
-}
-
-constexpr const char Status::kMovedFromString[];
-
-const std::string* Status::MovedFromString() {
-  static std::string* moved_from_string = new std::string(kMovedFromString);
-  return moved_from_string;
-}
-
-void Status::UnrefNonInlined(uintptr_t rep) {
-  status_internal::StatusRep* r = RepToPointer(rep);
-  // Fast path: if ref==1, there is no need for a RefCountDec (since
-  // this is the only reference and therefore no other thread is
-  // allowed to be mucking with r).
-  if (r->ref.load(std::memory_order_acquire) == 1 ||
-      r->ref.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) {
-    delete r;
-  }
-}
-
-uintptr_t Status::NewRep(absl::StatusCode code, absl::string_view msg,
-                         std::unique_ptr<status_internal::Payloads> payloads) {
-  status_internal::StatusRep* rep = new status_internal::StatusRep(
-      code, std::string(msg.data(), msg.size()), std::move(payloads));
-  return PointerToRep(rep);
-}
-
-Status::Status(absl::StatusCode code, absl::string_view msg)
-    : rep_(CodeToInlinedRep(code)) {
-  if (code != absl::StatusCode::kOk && !msg.empty()) {
-    rep_ = NewRep(code, msg, nullptr);
-  }
-}
-
-int Status::raw_code() const {
-  if (IsInlined(rep_)) {
-    return static_cast<int>(InlinedRepToCode(rep_));
-  }
-  status_internal::StatusRep* rep = RepToPointer(rep_);
-  return static_cast<int>(rep->code);
-}
-
-absl::StatusCode Status::code() const {
-  return status_internal::MapToLocalCode(raw_code());
-}
-
-void Status::PrepareToModify() {
-  ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status.");
-  if (IsInlined(rep_)) {
-    rep_ = NewRep(static_cast<absl::StatusCode>(raw_code()),
-                  absl::string_view(), nullptr);
-    return;
-  }
-
-  uintptr_t rep_i = rep_;
-  status_internal::StatusRep* rep = RepToPointer(rep_);
-  if (rep->ref.load(std::memory_order_acquire) != 1) {
-    std::unique_ptr<status_internal::Payloads> payloads;
-    if (rep->payloads) {
-      payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads);
-    }
-    rep_ = NewRep(rep->code, message(), std::move(payloads));
-    UnrefNonInlined(rep_i);
-  }
-}
-
-bool Status::EqualsSlow(const absl::Status& a, const absl::Status& b) {
-  if (IsInlined(a.rep_) != IsInlined(b.rep_)) return false;
-  if (a.message() != b.message()) return false;
-  if (a.raw_code() != b.raw_code()) return false;
-  if (a.GetPayloads() == b.GetPayloads()) return true;
-
-  const status_internal::Payloads no_payloads;
-  const status_internal::Payloads* larger_payloads =
-      a.GetPayloads() ? a.GetPayloads() : &no_payloads;
-  const status_internal::Payloads* smaller_payloads =
-      b.GetPayloads() ? b.GetPayloads() : &no_payloads;
-  if (larger_payloads->size() < smaller_payloads->size()) {
-    std::swap(larger_payloads, smaller_payloads);
-  }
-  if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false;
-  // Payloads can be ordered differently, so we can't just compare payload
-  // vectors.
-  for (const auto& payload : *larger_payloads) {
-
-    bool found = false;
-    for (const auto& other_payload : *smaller_payloads) {
-      if (payload.type_url == other_payload.type_url) {
-        if (payload.payload != other_payload.payload) {
-          return false;
-        }
-        found = true;
-        break;
-      }
-    }
-    if (!found) return false;
-  }
-  return true;
-}
-
-std::string Status::ToStringSlow() const {
-  std::string text;
-  absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
-  status_internal::StatusPayloadPrinter printer =
-      status_internal::GetStatusPayloadPrinter();
-  this->ForEachPayload([&](absl::string_view type_url,
-                           const absl::Cord& payload) {
-    absl::optional<std::string> result;
-    if (printer) result = printer(type_url, payload);
-    absl::StrAppend(
-        &text, " [", type_url, "='",
-        result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
-        "']");
-  });
-
-  return text;
-}
-
-std::ostream& operator<<(std::ostream& os, const Status& x) {
-  os << x.ToString();
-  return os;
-}
-
-Status AbortedError(absl::string_view message) {
-  return Status(absl::StatusCode::kAborted, message);
-}
-
-Status AlreadyExistsError(absl::string_view message) {
-  return Status(absl::StatusCode::kAlreadyExists, message);
-}
-
-Status CancelledError(absl::string_view message) {
-  return Status(absl::StatusCode::kCancelled, message);
-}
-
-Status DataLossError(absl::string_view message) {
-  return Status(absl::StatusCode::kDataLoss, message);
-}
-
-Status DeadlineExceededError(absl::string_view message) {
-  return Status(absl::StatusCode::kDeadlineExceeded, message);
-}
-
-Status FailedPreconditionError(absl::string_view message) {
-  return Status(absl::StatusCode::kFailedPrecondition, message);
-}
-
-Status InternalError(absl::string_view message) {
-  return Status(absl::StatusCode::kInternal, message);
-}
-
-Status InvalidArgumentError(absl::string_view message) {
-  return Status(absl::StatusCode::kInvalidArgument, message);
-}
-
-Status NotFoundError(absl::string_view message) {
-  return Status(absl::StatusCode::kNotFound, message);
-}
-
-Status OutOfRangeError(absl::string_view message) {
-  return Status(absl::StatusCode::kOutOfRange, message);
-}
-
-Status PermissionDeniedError(absl::string_view message) {
-  return Status(absl::StatusCode::kPermissionDenied, message);
-}
-
-Status ResourceExhaustedError(absl::string_view message) {
-  return Status(absl::StatusCode::kResourceExhausted, message);
-}
-
-Status UnauthenticatedError(absl::string_view message) {
-  return Status(absl::StatusCode::kUnauthenticated, message);
-}
-
-Status UnavailableError(absl::string_view message) {
-  return Status(absl::StatusCode::kUnavailable, message);
-}
-
-Status UnimplementedError(absl::string_view message) {
-  return Status(absl::StatusCode::kUnimplemented, message);
-}
-
-Status UnknownError(absl::string_view message) {
-  return Status(absl::StatusCode::kUnknown, message);
-}
-
-bool IsAborted(const Status& status) {
-  return status.code() == absl::StatusCode::kAborted;
-}
-
-bool IsAlreadyExists(const Status& status) {
-  return status.code() == absl::StatusCode::kAlreadyExists;
-}
-
-bool IsCancelled(const Status& status) {
-  return status.code() == absl::StatusCode::kCancelled;
-}
-
-bool IsDataLoss(const Status& status) {
-  return status.code() == absl::StatusCode::kDataLoss;
-}
-
-bool IsDeadlineExceeded(const Status& status) {
-  return status.code() == absl::StatusCode::kDeadlineExceeded;
-}
-
-bool IsFailedPrecondition(const Status& status) {
-  return status.code() == absl::StatusCode::kFailedPrecondition;
-}
-
-bool IsInternal(const Status& status) {
-  return status.code() == absl::StatusCode::kInternal;
-}
-
-bool IsInvalidArgument(const Status& status) {
-  return status.code() == absl::StatusCode::kInvalidArgument;
-}
-
-bool IsNotFound(const Status& status) {
-  return status.code() == absl::StatusCode::kNotFound;
-}
-
-bool IsOutOfRange(const Status& status) {
-  return status.code() == absl::StatusCode::kOutOfRange;
-}
-
-bool IsPermissionDenied(const Status& status) {
-  return status.code() == absl::StatusCode::kPermissionDenied;
-}
-
-bool IsResourceExhausted(const Status& status) {
-  return status.code() == absl::StatusCode::kResourceExhausted;
-}
-
-bool IsUnauthenticated(const Status& status) {
-  return status.code() == absl::StatusCode::kUnauthenticated;
-}
-
-bool IsUnavailable(const Status& status) {
-  return status.code() == absl::StatusCode::kUnavailable;
-}
-
-bool IsUnimplemented(const Status& status) {
-  return status.code() == absl::StatusCode::kUnimplemented;
-}
-
-bool IsUnknown(const Status& status) {
-  return status.code() == absl::StatusCode::kUnknown;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/status/status.h b/third_party/abseil_cpp/absl/status/status.h
deleted file mode 100644
index c4d6fce090..0000000000
--- a/third_party/abseil_cpp/absl/status/status.h
+++ /dev/null
@@ -1,817 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: status.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the Abseil `status` library, consisting of:
-//
-//   * An `absl::Status` class for holding error handling information
-//   * A set of canonical `absl::StatusCode` error codes, and associated
-//     utilities for generating and propagating status codes.
-//   * A set of helper functions for creating status codes and checking their
-//     values
-//
-// Within Google, `absl::Status` is the primary mechanism for gracefully
-// handling errors across API boundaries (and in particular across RPC
-// boundaries). Some of these errors may be recoverable, but others may not.
-// Most functions that can produce a recoverable error should be designed to
-// return an `absl::Status` (or `absl::StatusOr`).
-//
-// Example:
-//
-// absl::Status myFunction(absl::string_view fname, ...) {
-//   ...
-//   // encounter error
-//   if (error condition) {
-//     return absl::InvalidArgumentError("bad mode");
-//   }
-//   // else, return OK
-//   return absl::OkStatus();
-// }
-//
-// An `absl::Status` is designed to either return "OK" or one of a number of
-// different error codes, corresponding to typical error conditions.
-// In almost all cases, when using `absl::Status` you should use the canonical
-// error codes (of type `absl::StatusCode`) enumerated in this header file.
-// These canonical codes are understood across the codebase and will be
-// accepted across all API and RPC boundaries.
-#ifndef ABSL_STATUS_STATUS_H_
-#define ABSL_STATUS_STATUS_H_
-
-#include <iostream>
-#include <string>
-
-#include "absl/container/inlined_vector.h"
-#include "absl/status/internal/status_internal.h"
-#include "absl/strings/cord.h"
-#include "absl/types/optional.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::StatusCode
-//
-// An `absl::StatusCode` is an enumerated type indicating either no error ("OK")
-// or an error condition. In most cases, an `absl::Status` indicates a
-// recoverable error, and the purpose of signalling an error is to indicate what
-// action to take in response to that error. These error codes map to the proto
-// RPC error codes indicated in https://cloud.google.com/apis/design/errors.
-//
-// The errors listed below are the canonical errors associated with
-// `absl::Status` and are used throughout the codebase. As a result, these
-// error codes are somewhat generic.
-//
-// In general, try to return the most specific error that applies if more than
-// one error may pertain. For example, prefer `kOutOfRange` over
-// `kFailedPrecondition` if both codes apply. Similarly prefer `kNotFound` or
-// `kAlreadyExists` over `kFailedPrecondition`.
-//
-// Because these errors may travel RPC boundaries, these codes are tied to the
-// `google.rpc.Code` definitions within
-// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
-// The string value of these RPC codes is denoted within each enum below.
-//
-// If your error handling code requires more context, you can attach payloads
-// to your status. See `absl::Status::SetPayload()` and
-// `absl::Status::GetPayload()` below.
-enum class StatusCode : int {
-  // StatusCode::kOk
-  //
-  // kOK (gRPC code "OK") does not indicate an error; this value is returned on
-  // success. It is typical to check for this value before proceeding on any
-  // given call across an API or RPC boundary. To check this value, use the
-  // `absl::Status::ok()` member function rather than inspecting the raw code.
-  kOk = 0,
-
-  // StatusCode::kCancelled
-  //
-  // kCancelled (gRPC code "CANCELLED") indicates the operation was cancelled,
-  // typically by the caller.
-  kCancelled = 1,
-
-  // StatusCode::kUnknown
-  //
-  // kUnknown (gRPC code "UNKNOWN") indicates an unknown error occurred. In
-  // general, more specific errors should be raised, if possible. Errors raised
-  // by APIs that do not return enough error information may be converted to
-  // this error.
-  kUnknown = 2,
-
-  // StatusCode::kInvalidArgument
-  //
-  // kInvalidArgument (gRPC code "INVALID_ARGUMENT") indicates the caller
-  // specified an invalid argument, such a malformed filename. Note that such
-  // errors should be narrowly limited to indicate to the invalid nature of the
-  // arguments themselves. Errors with validly formed arguments that may cause
-  // errors with the state of the receiving system should be denoted with
-  // `kFailedPrecondition` instead.
-  kInvalidArgument = 3,
-
-  // StatusCode::kDeadlineExceeded
-  //
-  // kDeadlineExceeded (gRPC code "DEADLINE_EXCEEDED") indicates a deadline
-  // expired before the operation could complete. For operations that may change
-  // state within a system, this error may be returned even if the operation has
-  // completed successfully. For example, a successful response from a server
-  // could have been delayed long enough for the deadline to expire.
-  kDeadlineExceeded = 4,
-
-  // StatusCode::kNotFound
-  //
-  // kNotFound (gRPC code "NOT_FOUND") indicates some requested entity (such as
-  // a file or directory) was not found.
-  //
-  // `kNotFound` is useful if a request should be denied for an entire class of
-  // users, such as during a gradual feature rollout or undocumented allow list.
-  // If, instead, a request should be denied for specific sets of users, such as
-  // through user-based access control, use `kPermissionDenied` instead.
-  kNotFound = 5,
-
-  // StatusCode::kAlreadyExists
-  //
-  // kAlreadyExists (gRPC code "ALREADY_EXISTS") indicates the entity that a
-  // caller attempted to create (such as file or directory) is already present.
-  kAlreadyExists = 6,
-
-  // StatusCode::kPermissionDenied
-  //
-  // kPermissionDenied (gRPC code "PERMISSION_DENIED") indicates that the caller
-  // does not have permission to execute the specified operation. Note that this
-  // error is different than an error due to an *un*authenticated user. This
-  // error code does not imply the request is valid or the requested entity
-  // exists or satisfies any other pre-conditions.
-  //
-  // `kPermissionDenied` must not be used for rejections caused by exhausting
-  // some resource. Instead, use `kResourceExhausted` for those errors.
-  // `kPermissionDenied` must not be used if the caller cannot be identified.
-  // Instead, use `kUnauthenticated` for those errors.
-  kPermissionDenied = 7,
-
-  // StatusCode::kResourceExhausted
-  //
-  // kResourceExhausted (gRPC code "RESOURCE_EXHAUSTED") indicates some resource
-  // has been exhausted, perhaps a per-user quota, or perhaps the entire file
-  // system is out of space.
-  kResourceExhausted = 8,
-
-  // StatusCode::kFailedPrecondition
-  //
-  // kFailedPrecondition (gRPC code "FAILED_PRECONDITION") indicates that the
-  // operation was rejected because the system is not in a state required for
-  // the operation's execution. For example, a directory to be deleted may be
-  // non-empty, an "rmdir" operation is applied to a non-directory, etc.
-  //
-  // Some guidelines that may help a service implementer in deciding between
-  // `kFailedPrecondition`, `kAborted`, and `kUnavailable`:
-  //
-  //  (a) Use `kUnavailable` if the client can retry just the failing call.
-  //  (b) Use `kAborted` if the client should retry at a higher transaction
-  //      level (such as when a client-specified test-and-set fails, indicating
-  //      the client should restart a read-modify-write sequence).
-  //  (c) Use `kFailedPrecondition` if the client should not retry until
-  //      the system state has been explicitly fixed. For example, if an "rmdir"
-  //      fails because the directory is non-empty, `kFailedPrecondition`
-  //      should be returned since the client should not retry unless
-  //      the files are deleted from the directory.
-  kFailedPrecondition = 9,
-
-  // StatusCode::kAborted
-  //
-  // kAborted (gRPC code "ABORTED") indicates the operation was aborted,
-  // typically due to a concurrency issue such as a sequencer check failure or a
-  // failed transaction.
-  //
-  // See the guidelines above for deciding between `kFailedPrecondition`,
-  // `kAborted`, and `kUnavailable`.
-  kAborted = 10,
-
-  // StatusCode::kOutOfRange
-  //
-  // kOutOfRange (gRPC code "OUT_OF_RANGE") indicates the operation was
-  // attempted past the valid range, such as seeking or reading past an
-  // end-of-file.
-  //
-  // Unlike `kInvalidArgument`, this error indicates a problem that may
-  // be fixed if the system state changes. For example, a 32-bit file
-  // system will generate `kInvalidArgument` if asked to read at an
-  // offset that is not in the range [0,2^32-1], but it will generate
-  // `kOutOfRange` if asked to read from an offset past the current
-  // file size.
-  //
-  // There is a fair bit of overlap between `kFailedPrecondition` and
-  // `kOutOfRange`.  We recommend using `kOutOfRange` (the more specific
-  // error) when it applies so that callers who are iterating through
-  // a space can easily look for an `kOutOfRange` error to detect when
-  // they are done.
-  kOutOfRange = 11,
-
-  // StatusCode::kUnimplemented
-  //
-  // kUnimplemented (gRPC code "UNIMPLEMENTED") indicates the operation is not
-  // implemented or supported in this service. In this case, the operation
-  // should not be re-attempted.
-  kUnimplemented = 12,
-
-  // StatusCode::kInternal
-  //
-  // kInternal (gRPC code "INTERNAL") indicates an internal error has occurred
-  // and some invariants expected by the underlying system have not been
-  // satisfied. This error code is reserved for serious errors.
-  kInternal = 13,
-
-  // StatusCode::kUnavailable
-  //
-  // kUnavailable (gRPC code "UNAVAILABLE") indicates the service is currently
-  // unavailable and that this is most likely a transient condition. An error
-  // such as this can be corrected by retrying with a backoff scheme. Note that
-  // it is not always safe to retry non-idempotent operations.
-  //
-  // See the guidelines above for deciding between `kFailedPrecondition`,
-  // `kAborted`, and `kUnavailable`.
-  kUnavailable = 14,
-
-  // StatusCode::kDataLoss
-  //
-  // kDataLoss (gRPC code "DATA_LOSS") indicates that unrecoverable data loss or
-  // corruption has occurred. As this error is serious, proper alerting should
-  // be attached to errors such as this.
-  kDataLoss = 15,
-
-  // StatusCode::kUnauthenticated
-  //
-  // kUnauthenticated (gRPC code "UNAUTHENTICATED") indicates that the request
-  // does not have valid authentication credentials for the operation. Correct
-  // the authentication and try again.
-  kUnauthenticated = 16,
-
-  // StatusCode::DoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_
-  //
-  // NOTE: this error code entry should not be used and you should not rely on
-  // its value, which may change.
-  //
-  // The purpose of this enumerated value is to force people who handle status
-  // codes with `switch()` statements to *not* simply enumerate all possible
-  // values, but instead provide a "default:" case. Providing such a default
-  // case ensures that code will compile when new codes are added.
-  kDoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ = 20
-};
-
-// StatusCodeToString()
-//
-// Returns the name for the status code, or "" if it is an unknown value.
-std::string StatusCodeToString(StatusCode code);
-
-// operator<<
-//
-// Streams StatusCodeToString(code) to `os`.
-std::ostream& operator<<(std::ostream& os, StatusCode code);
-
-// absl::Status
-//
-// The `absl::Status` class is generally used to gracefully handle errors
-// across API boundaries (and in particular across RPC boundaries). Some of
-// these errors may be recoverable, but others may not. Most
-// functions which can produce a recoverable error should be designed to return
-// either an `absl::Status` (or the similar `absl::StatusOr<T>`, which holds
-// either an object of type `T` or an error).
-//
-// API developers should construct their functions to return `absl::OkStatus()`
-// upon success, or an `absl::StatusCode` upon another type of error (e.g
-// an `absl::StatusCode::kInvalidArgument` error). The API provides convenience
-// functions to constuct each status code.
-//
-// Example:
-//
-// absl::Status myFunction(absl::string_view fname, ...) {
-//   ...
-//   // encounter error
-//   if (error condition) {
-//     // Construct an absl::StatusCode::kInvalidArgument error
-//     return absl::InvalidArgumentError("bad mode");
-//   }
-//   // else, return OK
-//   return absl::OkStatus();
-// }
-//
-// Users handling status error codes should prefer checking for an OK status
-// using the `ok()` member function. Handling multiple error codes may justify
-// use of switch statement, but only check for error codes you know how to
-// handle; do not try to exhaustively match against all canonical error codes.
-// Errors that cannot be handled should be logged and/or propagated for higher
-// levels to deal with. If you do use a switch statement, make sure that you
-// also provide a `default:` switch case, so that code does not break as other
-// canonical codes are added to the API.
-//
-// Example:
-//
-//   absl::Status result = DoSomething();
-//   if (!result.ok()) {
-//     LOG(ERROR) << result;
-//   }
-//
-//   // Provide a default if switching on multiple error codes
-//   switch (result.code()) {
-//     // The user hasn't authenticated. Ask them to reauth
-//     case absl::StatusCode::kUnauthenticated:
-//       DoReAuth();
-//       break;
-//     // The user does not have permission. Log an error.
-//     case absl::StatusCode::kPermissionDenied:
-//       LOG(ERROR) << result;
-//       break;
-//     // Propagate the error otherwise.
-//     default:
-//       return true;
-//   }
-//
-// An `absl::Status` can optionally include a payload with more information
-// about the error. Typically, this payload serves one of several purposes:
-//
-//   * It may provide more fine-grained semantic information about the error to
-//     facilitate actionable remedies.
-//   * It may provide human-readable contexual information that is more
-//     appropriate to display to an end user.
-//
-// Example:
-//
-//   absl::Status result = DoSomething();
-//   // Inform user to retry after 30 seconds
-//   // See more error details in googleapis/google/rpc/error_details.proto
-//   if (absl::IsResourceExhausted(result)) {
-//     google::rpc::RetryInfo info;
-//     info.retry_delay().seconds() = 30;
-//     // Payloads require a unique key (a URL to ensure no collisions with
-//     // other payloads), and an `absl::Cord` to hold the encoded data.
-//     absl::string_view url = "type.googleapis.com/google.rpc.RetryInfo";
-//     result.SetPayload(url, info.SerializeAsCord());
-//     return result;
-//   }
-//
-class ABSL_MUST_USE_RESULT Status final {
- public:
-  // Constructors
-
-  // This default constructor creates an OK status with no message or payload.
-  // Avoid this constructor and prefer explicit construction of an OK status
-  // with `absl::OkStatus()`.
-  Status();
-
-  // Creates a status in the canonical error space with the specified
-  // `absl::StatusCode` and error message.  If `code == absl::StatusCode::kOk`,
-  // `msg` is ignored and an object identical to an OK status is constructed.
-  //
-  // The `msg` string must be in UTF-8. The implementation may complain (e.g.,
-  // by printing a warning) if it is not.
-  Status(absl::StatusCode code, absl::string_view msg);
-
-  Status(const Status&);
-  Status& operator=(const Status& x);
-
-  // Move operators
-
-  // The moved-from state is valid but unspecified.
-  Status(Status&&) noexcept;
-  Status& operator=(Status&&);
-
-  ~Status();
-
-  // Status::Update()
-  //
-  // Updates the existing status with `new_status` provided that `this->ok()`.
-  // If the existing status already contains a non-OK error, this update has no
-  // effect and preserves the current data. Note that this behavior may change
-  // in the future to augment a current non-ok status with additional
-  // information about `new_status`.
-  //
-  // `Update()` provides a convenient way of keeping track of the first error
-  // encountered.
-  //
-  // Example:
-  //   // Instead of "if (overall_status.ok()) overall_status = new_status"
-  //   overall_status.Update(new_status);
-  //
-  void Update(const Status& new_status);
-  void Update(Status&& new_status);
-
-  // Status::ok()
-  //
-  // Returns `true` if `this->ok()`. Prefer checking for an OK status using this
-  // member function.
-  ABSL_MUST_USE_RESULT bool ok() const;
-
-  // Status::code()
-  //
-  // Returns the canonical error code of type `absl::StatusCode` of this status.
-  absl::StatusCode code() const;
-
-  // Status::raw_code()
-  //
-  // Returns a raw (canonical) error code corresponding to the enum value of
-  // `google.rpc.Code` definitions within
-  // https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto.
-  // These values could be out of the range of canonical `absl::StatusCode`
-  // enum values.
-  //
-  // NOTE: This function should only be called when converting to an associated
-  // wire format. Use `Status::code()` for error handling.
-  int raw_code() const;
-
-  // Status::message()
-  //
-  // Returns the error message associated with this error code, if available.
-  // Note that this message rarely describes the error code.  It is not unusual
-  // for the error message to be the empty string. As a result, prefer
-  // `Status::ToString()` for debug logging.
-  absl::string_view message() const;
-
-  friend bool operator==(const Status&, const Status&);
-  friend bool operator!=(const Status&, const Status&);
-
-  // Status::ToString()
-  //
-  // Returns a combination of the error code name, the message and any
-  // associated payload messages. This string is designed simply to be human
-  // readable and its exact format should not be load bearing. Do not depend on
-  // the exact format of the result of `ToString()` which is subject to change.
-  //
-  // The printed code name and the message are generally substrings of the
-  // result, and the payloads to be printed use the status payload printer
-  // mechanism (which is internal).
-  std::string ToString() const;
-
-  // Status::IgnoreError()
-  //
-  // Ignores any errors. This method does nothing except potentially suppress
-  // complaints from any tools that are checking that errors are not dropped on
-  // the floor.
-  void IgnoreError() const;
-
-  // swap()
-  //
-  // Swap the contents of one status with another.
-  friend void swap(Status& a, Status& b);
-
-  //----------------------------------------------------------------------------
-  // Payload Management APIs
-  //----------------------------------------------------------------------------
-
-  // A payload may be attached to a status to provide additional context to an
-  // error that may not be satisifed by an existing `absl::StatusCode`.
-  // Typically, this payload serves one of several purposes:
-  //
-  //   * It may provide more fine-grained semantic information about the error
-  //     to facilitate actionable remedies.
-  //   * It may provide human-readable contexual information that is more
-  //     appropriate to display to an end user.
-  //
-  // A payload consists of a [key,value] pair, where the key is a string
-  // referring to a unique "type URL" and the value is an object of type
-  // `absl::Cord` to hold the contextual data.
-  //
-  // The "type URL" should be unique and follow the format of a URL
-  // (https://en.wikipedia.org/wiki/URL) and, ideally, provide some
-  // documentation or schema on how to interpret its associated data. For
-  // example, the default type URL for a protobuf message type is
-  // "type.googleapis.com/packagename.messagename". Other custom wire formats
-  // should define the format of type URL in a similar practice so as to
-  // minimize the chance of conflict between type URLs.
-  // Users should ensure that the type URL can be mapped to a concrete
-  // C++ type if they want to deserialize the payload and read it effectively.
-  //
-  // To attach a payload to a status object, call `Status::SetPayload()`,
-  // passing it the type URL and an `absl::Cord` of associated data. Similarly,
-  // to extract the payload from a status, call `Status::GetPayload()`. You
-  // may attach multiple payloads (with differing type URLs) to any given
-  // status object, provided that the status is currently exhibiting an error
-  // code (i.e. is not OK).
-
-  // Status::GetPayload()
-  //
-  // Gets the payload of a status given its unique `type_url` key, if present.
-  absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
-
-  // Status::SetPayload()
-  //
-  // Sets the payload for a non-ok status using a `type_url` key, overwriting
-  // any existing payload for that `type_url`.
-  //
-  // NOTE: This function does nothing if the Status is ok.
-  void SetPayload(absl::string_view type_url, absl::Cord payload);
-
-  // Status::ErasePayload()
-  //
-  // Erases the payload corresponding to the `type_url` key.  Returns `true` if
-  // the payload was present.
-  bool ErasePayload(absl::string_view type_url);
-
-  // Status::ForEachPayload()
-  //
-  // Iterates over the stored payloads and calls the
-  // `visitor(type_key, payload)` callable for each one.
-  //
-  // NOTE: The order of calls to `visitor()` is not specified and may change at
-  // any time.
-  //
-  // NOTE: Any mutation on the same 'absl::Status' object during visitation is
-  // forbidden and could result in undefined behavior.
-  void ForEachPayload(
-      const std::function<void(absl::string_view, const absl::Cord&)>& visitor)
-      const;
-
- private:
-  friend Status CancelledError();
-
-  // Creates a status in the canonical error space with the specified
-  // code, and an empty error message.
-  explicit Status(absl::StatusCode code);
-
-  static void UnrefNonInlined(uintptr_t rep);
-  static void Ref(uintptr_t rep);
-  static void Unref(uintptr_t rep);
-
-  // REQUIRES: !ok()
-  // Ensures rep_ is not shared with any other Status.
-  void PrepareToModify();
-
-  const status_internal::Payloads* GetPayloads() const;
-  status_internal::Payloads* GetPayloads();
-
-  // Takes ownership of payload.
-  static uintptr_t NewRep(absl::StatusCode code, absl::string_view msg,
-                          std::unique_ptr<status_internal::Payloads> payload);
-  static bool EqualsSlow(const absl::Status& a, const absl::Status& b);
-
-  // MSVC 14.0 limitation requires the const.
-  static constexpr const char kMovedFromString[] =
-      "Status accessed after move.";
-
-  static const std::string* EmptyString();
-  static const std::string* MovedFromString();
-
-  // Returns whether rep contains an inlined representation.
-  // See rep_ for details.
-  static bool IsInlined(uintptr_t rep);
-
-  // Indicates whether this Status was the rhs of a move operation. See rep_
-  // for details.
-  static bool IsMovedFrom(uintptr_t rep);
-  static uintptr_t MovedFromRep();
-
-  // Convert between error::Code and the inlined uintptr_t representation used
-  // by rep_. See rep_ for details.
-  static uintptr_t CodeToInlinedRep(absl::StatusCode code);
-  static absl::StatusCode InlinedRepToCode(uintptr_t rep);
-
-  // Converts between StatusRep* and the external uintptr_t representation used
-  // by rep_. See rep_ for details.
-  static uintptr_t PointerToRep(status_internal::StatusRep* r);
-  static status_internal::StatusRep* RepToPointer(uintptr_t r);
-
-  // Returns string for non-ok Status.
-  std::string ToStringSlow() const;
-
-  // Status supports two different representations.
-  //  - When the low bit is off it is an inlined representation.
-  //    It uses the canonical error space, no message or payload.
-  //    The error code is (rep_ >> 2).
-  //    The (rep_ & 2) bit is the "moved from" indicator, used in IsMovedFrom().
-  //  - When the low bit is on it is an external representation.
-  //    In this case all the data comes from a heap allocated Rep object.
-  //    (rep_ - 1) is a status_internal::StatusRep* pointer to that structure.
-  uintptr_t rep_;
-};
-
-// OkStatus()
-//
-// Returns an OK status, equivalent to a default constructed instance. Prefer
-// usage of `absl::OkStatus()` when constructing such an OK status.
-Status OkStatus();
-
-// operator<<()
-//
-// Prints a human-readable representation of `x` to `os`.
-std::ostream& operator<<(std::ostream& os, const Status& x);
-
-// IsAborted()
-// IsAlreadyExists()
-// IsCancelled()
-// IsDataLoss()
-// IsDeadlineExceeded()
-// IsFailedPrecondition()
-// IsInternal()
-// IsInvalidArgument()
-// IsNotFound()
-// IsOutOfRange()
-// IsPermissionDenied()
-// IsResourceExhausted()
-// IsUnauthenticated()
-// IsUnavailable()
-// IsUnimplemented()
-// IsUnknown()
-//
-// These convenience functions return `true` if a given status matches the
-// `absl::StatusCode` error code of its associated function.
-ABSL_MUST_USE_RESULT bool IsAborted(const Status& status);
-ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status);
-ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status);
-ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status);
-ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status);
-ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status);
-ABSL_MUST_USE_RESULT bool IsInternal(const Status& status);
-ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status);
-ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status);
-ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status);
-ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status);
-ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status);
-ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status);
-ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status);
-ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status);
-ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status);
-
-// AbortedError()
-// AlreadyExistsError()
-// CancelledError()
-// DataLossError()
-// DeadlineExceededError()
-// FailedPreconditionError()
-// InternalError()
-// InvalidArgumentError()
-// NotFoundError()
-// OutOfRangeError()
-// PermissionDeniedError()
-// ResourceExhaustedError()
-// UnauthenticatedError()
-// UnavailableError()
-// UnimplementedError()
-// UnknownError()
-//
-// These convenience functions create an `absl::Status` object with an error
-// code as indicated by the associated function name, using the error message
-// passed in `message`.
-Status AbortedError(absl::string_view message);
-Status AlreadyExistsError(absl::string_view message);
-Status CancelledError(absl::string_view message);
-Status DataLossError(absl::string_view message);
-Status DeadlineExceededError(absl::string_view message);
-Status FailedPreconditionError(absl::string_view message);
-Status InternalError(absl::string_view message);
-Status InvalidArgumentError(absl::string_view message);
-Status NotFoundError(absl::string_view message);
-Status OutOfRangeError(absl::string_view message);
-Status PermissionDeniedError(absl::string_view message);
-Status ResourceExhaustedError(absl::string_view message);
-Status UnauthenticatedError(absl::string_view message);
-Status UnavailableError(absl::string_view message);
-Status UnimplementedError(absl::string_view message);
-Status UnknownError(absl::string_view message);
-
-//------------------------------------------------------------------------------
-// Implementation details follow
-//------------------------------------------------------------------------------
-
-inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {}
-
-inline Status::Status(absl::StatusCode code) : rep_(CodeToInlinedRep(code)) {}
-
-inline Status::Status(const Status& x) : rep_(x.rep_) { Ref(rep_); }
-
-inline Status& Status::operator=(const Status& x) {
-  uintptr_t old_rep = rep_;
-  if (x.rep_ != old_rep) {
-    Ref(x.rep_);
-    rep_ = x.rep_;
-    Unref(old_rep);
-  }
-  return *this;
-}
-
-inline Status::Status(Status&& x) noexcept : rep_(x.rep_) {
-  x.rep_ = MovedFromRep();
-}
-
-inline Status& Status::operator=(Status&& x) {
-  uintptr_t old_rep = rep_;
-  rep_ = x.rep_;
-  x.rep_ = MovedFromRep();
-  Unref(old_rep);
-  return *this;
-}
-
-inline void Status::Update(const Status& new_status) {
-  if (ok()) {
-    *this = new_status;
-  }
-}
-
-inline void Status::Update(Status&& new_status) {
-  if (ok()) {
-    *this = std::move(new_status);
-  }
-}
-
-inline Status::~Status() { Unref(rep_); }
-
-inline bool Status::ok() const {
-  return rep_ == CodeToInlinedRep(absl::StatusCode::kOk);
-}
-
-inline absl::string_view Status::message() const {
-  return !IsInlined(rep_)
-             ? RepToPointer(rep_)->message
-             : (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString)
-                                  : absl::string_view());
-}
-
-inline bool operator==(const Status& lhs, const Status& rhs) {
-  return lhs.rep_ == rhs.rep_ || Status::EqualsSlow(lhs, rhs);
-}
-
-inline bool operator!=(const Status& lhs, const Status& rhs) {
-  return !(lhs == rhs);
-}
-
-inline std::string Status::ToString() const {
-  return ok() ? "OK" : ToStringSlow();
-}
-
-inline void Status::IgnoreError() const {
-  // no-op
-}
-
-inline void swap(absl::Status& a, absl::Status& b) {
-  using std::swap;
-  swap(a.rep_, b.rep_);
-}
-
-inline const status_internal::Payloads* Status::GetPayloads() const {
-  return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
-}
-
-inline status_internal::Payloads* Status::GetPayloads() {
-  return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
-}
-
-inline bool Status::IsInlined(uintptr_t rep) { return (rep & 1) == 0; }
-
-inline bool Status::IsMovedFrom(uintptr_t rep) {
-  return IsInlined(rep) && (rep & 2) != 0;
-}
-
-inline uintptr_t Status::MovedFromRep() {
-  return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
-}
-
-inline uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) {
-  return static_cast<uintptr_t>(code) << 2;
-}
-
-inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
-  assert(IsInlined(rep));
-  return static_cast<absl::StatusCode>(rep >> 2);
-}
-
-inline status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) {
-  assert(!IsInlined(rep));
-  return reinterpret_cast<status_internal::StatusRep*>(rep - 1);
-}
-
-inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) {
-  return reinterpret_cast<uintptr_t>(rep) + 1;
-}
-
-inline void Status::Ref(uintptr_t rep) {
-  if (!IsInlined(rep)) {
-    RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed);
-  }
-}
-
-inline void Status::Unref(uintptr_t rep) {
-  if (!IsInlined(rep)) {
-    UnrefNonInlined(rep);
-  }
-}
-
-inline Status OkStatus() { return Status(); }
-
-// Creates a `Status` object with the `absl::StatusCode::kCancelled` error code
-// and an empty message. It is provided only for efficiency, given that
-// message-less kCancelled errors are common in the infrastructure.
-inline Status CancelledError() { return Status(absl::StatusCode::kCancelled); }
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STATUS_STATUS_H_
diff --git a/third_party/abseil_cpp/absl/status/status_payload_printer.cc b/third_party/abseil_cpp/absl/status/status_payload_printer.cc
deleted file mode 100644
index a47aea11c2..0000000000
--- a/third_party/abseil_cpp/absl/status/status_payload_printer.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#include "absl/status/status_payload_printer.h"
-
-#include <atomic>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/atomic_hook.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace status_internal {
-
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
-static absl::base_internal::AtomicHook<StatusPayloadPrinter> storage;
-
-void SetStatusPayloadPrinter(StatusPayloadPrinter printer) {
-  storage.Store(printer);
-}
-
-StatusPayloadPrinter GetStatusPayloadPrinter() {
-  return storage.Load();
-}
-
-}  // namespace status_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/status/status_payload_printer.h b/third_party/abseil_cpp/absl/status/status_payload_printer.h
deleted file mode 100644
index 5e0937f67d..0000000000
--- a/third_party/abseil_cpp/absl/status/status_payload_printer.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#ifndef ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_
-#define ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_
-
-#include <string>
-
-#include "absl/strings/cord.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/optional.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace status_internal {
-
-// By default, `Status::ToString` and `operator<<(Status)` print a payload by
-// dumping the type URL and the raw bytes. To help debugging, we provide an
-// extension point, which is a global printer function that can be set by users
-// to specify how to print payloads. The function takes the type URL and the
-// payload as input, and should return a valid human-readable string on success
-// or `absl::nullopt` on failure (in which case it falls back to the default
-// approach of printing the raw bytes).
-// NOTE: This is an internal API and the design is subject to change in the
-// future in a non-backward-compatible way. Since it's only meant for debugging
-// purpose, you should not rely on it in any critical logic.
-using StatusPayloadPrinter = absl::optional<std::string> (*)(absl::string_view,
-                                                             const absl::Cord&);
-
-// Sets the global payload printer. Only one printer should be set per process.
-// If multiple printers are set, it's undefined which one will be used.
-void SetStatusPayloadPrinter(StatusPayloadPrinter);
-
-// Returns the global payload printer if previously set, otherwise `nullptr`.
-StatusPayloadPrinter GetStatusPayloadPrinter();
-
-}  // namespace status_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_
diff --git a/third_party/abseil_cpp/absl/status/status_test.cc b/third_party/abseil_cpp/absl/status/status_test.cc
deleted file mode 100644
index ca9488ad22..0000000000
--- a/third_party/abseil_cpp/absl/status/status_test.cc
+++ /dev/null
@@ -1,458 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/status/status.h"
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-using ::testing::Eq;
-using ::testing::HasSubstr;
-using ::testing::Optional;
-using ::testing::UnorderedElementsAreArray;
-
-TEST(StatusCode, InsertionOperator) {
-  const absl::StatusCode code = absl::StatusCode::kUnknown;
-  std::ostringstream oss;
-  oss << code;
-  EXPECT_EQ(oss.str(), absl::StatusCodeToString(code));
-}
-
-// This structure holds the details for testing a single error code,
-// its creator, and its classifier.
-struct ErrorTest {
-  absl::StatusCode code;
-  using Creator = absl::Status (*)(absl::string_view);
-  using Classifier = bool (*)(const absl::Status&);
-  Creator creator;
-  Classifier classifier;
-};
-
-constexpr ErrorTest kErrorTests[]{
-    {absl::StatusCode::kCancelled, absl::CancelledError, absl::IsCancelled},
-    {absl::StatusCode::kUnknown, absl::UnknownError, absl::IsUnknown},
-    {absl::StatusCode::kInvalidArgument, absl::InvalidArgumentError,
-     absl::IsInvalidArgument},
-    {absl::StatusCode::kDeadlineExceeded, absl::DeadlineExceededError,
-     absl::IsDeadlineExceeded},
-    {absl::StatusCode::kNotFound, absl::NotFoundError, absl::IsNotFound},
-    {absl::StatusCode::kAlreadyExists, absl::AlreadyExistsError,
-     absl::IsAlreadyExists},
-    {absl::StatusCode::kPermissionDenied, absl::PermissionDeniedError,
-     absl::IsPermissionDenied},
-    {absl::StatusCode::kResourceExhausted, absl::ResourceExhaustedError,
-     absl::IsResourceExhausted},
-    {absl::StatusCode::kFailedPrecondition, absl::FailedPreconditionError,
-     absl::IsFailedPrecondition},
-    {absl::StatusCode::kAborted, absl::AbortedError, absl::IsAborted},
-    {absl::StatusCode::kOutOfRange, absl::OutOfRangeError, absl::IsOutOfRange},
-    {absl::StatusCode::kUnimplemented, absl::UnimplementedError,
-     absl::IsUnimplemented},
-    {absl::StatusCode::kInternal, absl::InternalError, absl::IsInternal},
-    {absl::StatusCode::kUnavailable, absl::UnavailableError,
-     absl::IsUnavailable},
-    {absl::StatusCode::kDataLoss, absl::DataLossError, absl::IsDataLoss},
-    {absl::StatusCode::kUnauthenticated, absl::UnauthenticatedError,
-     absl::IsUnauthenticated},
-};
-
-TEST(Status, CreateAndClassify) {
-  for (const auto& test : kErrorTests) {
-    SCOPED_TRACE(absl::StatusCodeToString(test.code));
-
-    // Ensure that the creator does, in fact, create status objects with the
-    // expected error code and message.
-    std::string message =
-        absl::StrCat("error code ", test.code, " test message");
-    absl::Status status = test.creator(message);
-    EXPECT_EQ(test.code, status.code());
-    EXPECT_EQ(message, status.message());
-
-    // Ensure that the classifier returns true for a status produced by the
-    // creator.
-    EXPECT_TRUE(test.classifier(status));
-
-    // Ensure that the classifier returns false for status with a different
-    // code.
-    for (const auto& other : kErrorTests) {
-      if (other.code != test.code) {
-        EXPECT_FALSE(test.classifier(absl::Status(other.code, "")))
-            << " other.code = " << other.code;
-      }
-    }
-  }
-}
-
-TEST(Status, DefaultConstructor) {
-  absl::Status status;
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(absl::StatusCode::kOk, status.code());
-  EXPECT_EQ("", status.message());
-}
-
-TEST(Status, OkStatus) {
-  absl::Status status = absl::OkStatus();
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(absl::StatusCode::kOk, status.code());
-  EXPECT_EQ("", status.message());
-}
-
-TEST(Status, ConstructorWithCodeMessage) {
-  {
-    absl::Status status(absl::StatusCode::kCancelled, "");
-    EXPECT_FALSE(status.ok());
-    EXPECT_EQ(absl::StatusCode::kCancelled, status.code());
-    EXPECT_EQ("", status.message());
-  }
-  {
-    absl::Status status(absl::StatusCode::kInternal, "message");
-    EXPECT_FALSE(status.ok());
-    EXPECT_EQ(absl::StatusCode::kInternal, status.code());
-    EXPECT_EQ("message", status.message());
-  }
-}
-
-TEST(Status, ConstructOutOfRangeCode) {
-  const int kRawCode = 9999;
-  absl::Status status(static_cast<absl::StatusCode>(kRawCode), "");
-  EXPECT_EQ(absl::StatusCode::kUnknown, status.code());
-  EXPECT_EQ(kRawCode, status.raw_code());
-}
-
-constexpr char kUrl1[] = "url.payload.1";
-constexpr char kUrl2[] = "url.payload.2";
-constexpr char kUrl3[] = "url.payload.3";
-constexpr char kUrl4[] = "url.payload.xx";
-
-constexpr char kPayload1[] = "aaaaa";
-constexpr char kPayload2[] = "bbbbb";
-constexpr char kPayload3[] = "ccccc";
-
-using PayloadsVec = std::vector<std::pair<std::string, absl::Cord>>;
-
-TEST(Status, TestGetSetPayload) {
-  absl::Status ok_status = absl::OkStatus();
-  ok_status.SetPayload(kUrl1, absl::Cord(kPayload1));
-  ok_status.SetPayload(kUrl2, absl::Cord(kPayload2));
-
-  EXPECT_FALSE(ok_status.GetPayload(kUrl1));
-  EXPECT_FALSE(ok_status.GetPayload(kUrl2));
-
-  absl::Status bad_status(absl::StatusCode::kInternal, "fail");
-  bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
-  bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
-
-  EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload1)));
-  EXPECT_THAT(bad_status.GetPayload(kUrl2), Optional(Eq(kPayload2)));
-
-  EXPECT_FALSE(bad_status.GetPayload(kUrl3));
-
-  bad_status.SetPayload(kUrl1, absl::Cord(kPayload3));
-  EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload3)));
-
-  // Testing dynamically generated type_url
-  bad_status.SetPayload(absl::StrCat(kUrl1, ".1"), absl::Cord(kPayload1));
-  EXPECT_THAT(bad_status.GetPayload(absl::StrCat(kUrl1, ".1")),
-              Optional(Eq(kPayload1)));
-}
-
-TEST(Status, TestErasePayload) {
-  absl::Status bad_status(absl::StatusCode::kInternal, "fail");
-  bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
-  bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
-  bad_status.SetPayload(kUrl3, absl::Cord(kPayload3));
-
-  EXPECT_FALSE(bad_status.ErasePayload(kUrl4));
-
-  EXPECT_TRUE(bad_status.GetPayload(kUrl2));
-  EXPECT_TRUE(bad_status.ErasePayload(kUrl2));
-  EXPECT_FALSE(bad_status.GetPayload(kUrl2));
-  EXPECT_FALSE(bad_status.ErasePayload(kUrl2));
-
-  EXPECT_TRUE(bad_status.ErasePayload(kUrl1));
-  EXPECT_TRUE(bad_status.ErasePayload(kUrl3));
-
-  bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
-  EXPECT_TRUE(bad_status.ErasePayload(kUrl1));
-}
-
-TEST(Status, TestComparePayloads) {
-  absl::Status bad_status1(absl::StatusCode::kInternal, "fail");
-  bad_status1.SetPayload(kUrl1, absl::Cord(kPayload1));
-  bad_status1.SetPayload(kUrl2, absl::Cord(kPayload2));
-  bad_status1.SetPayload(kUrl3, absl::Cord(kPayload3));
-
-  absl::Status bad_status2(absl::StatusCode::kInternal, "fail");
-  bad_status2.SetPayload(kUrl2, absl::Cord(kPayload2));
-  bad_status2.SetPayload(kUrl3, absl::Cord(kPayload3));
-  bad_status2.SetPayload(kUrl1, absl::Cord(kPayload1));
-
-  EXPECT_EQ(bad_status1, bad_status2);
-}
-
-TEST(Status, TestComparePayloadsAfterErase) {
-  absl::Status payload_status(absl::StatusCode::kInternal, "");
-  payload_status.SetPayload(kUrl1, absl::Cord(kPayload1));
-  payload_status.SetPayload(kUrl2, absl::Cord(kPayload2));
-
-  absl::Status empty_status(absl::StatusCode::kInternal, "");
-
-  // Different payloads, not equal
-  EXPECT_NE(payload_status, empty_status);
-  EXPECT_TRUE(payload_status.ErasePayload(kUrl1));
-
-  // Still Different payloads, still not equal.
-  EXPECT_NE(payload_status, empty_status);
-  EXPECT_TRUE(payload_status.ErasePayload(kUrl2));
-
-  // Both empty payloads, should be equal
-  EXPECT_EQ(payload_status, empty_status);
-}
-
-PayloadsVec AllVisitedPayloads(const absl::Status& s) {
-  PayloadsVec result;
-
-  s.ForEachPayload([&](absl::string_view type_url, const absl::Cord& payload) {
-    result.push_back(std::make_pair(std::string(type_url), payload));
-  });
-
-  return result;
-}
-
-TEST(Status, TestForEachPayload) {
-  absl::Status bad_status(absl::StatusCode::kInternal, "fail");
-  bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
-  bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
-  bad_status.SetPayload(kUrl3, absl::Cord(kPayload3));
-
-  int count = 0;
-
-  bad_status.ForEachPayload(
-      [&count](absl::string_view, const absl::Cord&) { ++count; });
-
-  EXPECT_EQ(count, 3);
-
-  PayloadsVec expected_payloads = {{kUrl1, absl::Cord(kPayload1)},
-                                   {kUrl2, absl::Cord(kPayload2)},
-                                   {kUrl3, absl::Cord(kPayload3)}};
-
-  // Test that we visit all the payloads in the status.
-  PayloadsVec visited_payloads = AllVisitedPayloads(bad_status);
-  EXPECT_THAT(visited_payloads, UnorderedElementsAreArray(expected_payloads));
-
-  // Test that visitation order is not consistent between run.
-  std::vector<absl::Status> scratch;
-  while (true) {
-    scratch.emplace_back(absl::StatusCode::kInternal, "fail");
-
-    scratch.back().SetPayload(kUrl1, absl::Cord(kPayload1));
-    scratch.back().SetPayload(kUrl2, absl::Cord(kPayload2));
-    scratch.back().SetPayload(kUrl3, absl::Cord(kPayload3));
-
-    if (AllVisitedPayloads(scratch.back()) != visited_payloads) {
-      break;
-    }
-  }
-}
-
-TEST(Status, ToString) {
-  absl::Status s(absl::StatusCode::kInternal, "fail");
-  EXPECT_EQ("INTERNAL: fail", s.ToString());
-  s.SetPayload("foo", absl::Cord("bar"));
-  EXPECT_EQ("INTERNAL: fail [foo='bar']", s.ToString());
-  s.SetPayload("bar", absl::Cord("\377"));
-  EXPECT_THAT(s.ToString(),
-              AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
-                    HasSubstr("[bar='\\xff']")));
-}
-
-absl::Status EraseAndReturn(const absl::Status& base) {
-  absl::Status copy = base;
-  EXPECT_TRUE(copy.ErasePayload(kUrl1));
-  return copy;
-}
-
-TEST(Status, CopyOnWriteForErasePayload) {
-  {
-    absl::Status base(absl::StatusCode::kInvalidArgument, "fail");
-    base.SetPayload(kUrl1, absl::Cord(kPayload1));
-    EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
-    absl::Status copy = EraseAndReturn(base);
-    EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
-    EXPECT_FALSE(copy.GetPayload(kUrl1).has_value());
-  }
-  {
-    absl::Status base(absl::StatusCode::kInvalidArgument, "fail");
-    base.SetPayload(kUrl1, absl::Cord(kPayload1));
-    absl::Status copy = base;
-
-    EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
-    EXPECT_TRUE(copy.GetPayload(kUrl1).has_value());
-
-    EXPECT_TRUE(base.ErasePayload(kUrl1));
-
-    EXPECT_FALSE(base.GetPayload(kUrl1).has_value());
-    EXPECT_TRUE(copy.GetPayload(kUrl1).has_value());
-  }
-}
-
-TEST(Status, CopyConstructor) {
-  {
-    absl::Status status;
-    absl::Status copy(status);
-    EXPECT_EQ(copy, status);
-  }
-  {
-    absl::Status status(absl::StatusCode::kInvalidArgument, "message");
-    absl::Status copy(status);
-    EXPECT_EQ(copy, status);
-  }
-  {
-    absl::Status status(absl::StatusCode::kInvalidArgument, "message");
-    status.SetPayload(kUrl1, absl::Cord(kPayload1));
-    absl::Status copy(status);
-    EXPECT_EQ(copy, status);
-  }
-}
-
-TEST(Status, CopyAssignment) {
-  absl::Status assignee;
-  {
-    absl::Status status;
-    assignee = status;
-    EXPECT_EQ(assignee, status);
-  }
-  {
-    absl::Status status(absl::StatusCode::kInvalidArgument, "message");
-    assignee = status;
-    EXPECT_EQ(assignee, status);
-  }
-  {
-    absl::Status status(absl::StatusCode::kInvalidArgument, "message");
-    status.SetPayload(kUrl1, absl::Cord(kPayload1));
-    assignee = status;
-    EXPECT_EQ(assignee, status);
-  }
-}
-
-TEST(Status, CopyAssignmentIsNotRef) {
-  const absl::Status status_orig(absl::StatusCode::kInvalidArgument, "message");
-  absl::Status status_copy = status_orig;
-  EXPECT_EQ(status_orig, status_copy);
-  status_copy.SetPayload(kUrl1, absl::Cord(kPayload1));
-  EXPECT_NE(status_orig, status_copy);
-}
-
-TEST(Status, MoveConstructor) {
-  {
-    absl::Status status;
-    absl::Status copy(absl::Status{});
-    EXPECT_EQ(copy, status);
-  }
-  {
-    absl::Status status(absl::StatusCode::kInvalidArgument, "message");
-    absl::Status copy(
-        absl::Status(absl::StatusCode::kInvalidArgument, "message"));
-    EXPECT_EQ(copy, status);
-  }
-  {
-    absl::Status status(absl::StatusCode::kInvalidArgument, "message");
-    status.SetPayload(kUrl1, absl::Cord(kPayload1));
-    absl::Status copy1(status);
-    absl::Status copy2(std::move(status));
-    EXPECT_EQ(copy1, copy2);
-  }
-}
-
-TEST(Status, MoveAssignment) {
-  absl::Status assignee;
-  {
-    absl::Status status;
-    assignee = absl::Status();
-    EXPECT_EQ(assignee, status);
-  }
-  {
-    absl::Status status(absl::StatusCode::kInvalidArgument, "message");
-    assignee = absl::Status(absl::StatusCode::kInvalidArgument, "message");
-    EXPECT_EQ(assignee, status);
-  }
-  {
-    absl::Status status(absl::StatusCode::kInvalidArgument, "message");
-    status.SetPayload(kUrl1, absl::Cord(kPayload1));
-    absl::Status copy(status);
-    assignee = std::move(status);
-    EXPECT_EQ(assignee, copy);
-  }
-}
-
-TEST(Status, Update) {
-  absl::Status s;
-  s.Update(absl::OkStatus());
-  EXPECT_TRUE(s.ok());
-  const absl::Status a(absl::StatusCode::kCancelled, "message");
-  s.Update(a);
-  EXPECT_EQ(s, a);
-  const absl::Status b(absl::StatusCode::kInternal, "other message");
-  s.Update(b);
-  EXPECT_EQ(s, a);
-  s.Update(absl::OkStatus());
-  EXPECT_EQ(s, a);
-  EXPECT_FALSE(s.ok());
-}
-
-TEST(Status, Equality) {
-  absl::Status ok;
-  absl::Status no_payload = absl::CancelledError("no payload");
-  absl::Status one_payload = absl::InvalidArgumentError("one payload");
-  one_payload.SetPayload(kUrl1, absl::Cord(kPayload1));
-  absl::Status two_payloads = one_payload;
-  two_payloads.SetPayload(kUrl2, absl::Cord(kPayload2));
-  const std::array<absl::Status, 4> status_arr = {ok, no_payload, one_payload,
-                                                  two_payloads};
-  for (int i = 0; i < status_arr.size(); i++) {
-    for (int j = 0; j < status_arr.size(); j++) {
-      if (i == j) {
-        EXPECT_TRUE(status_arr[i] == status_arr[j]);
-        EXPECT_FALSE(status_arr[i] != status_arr[j]);
-      } else {
-        EXPECT_TRUE(status_arr[i] != status_arr[j]);
-        EXPECT_FALSE(status_arr[i] == status_arr[j]);
-      }
-    }
-  }
-}
-
-TEST(Status, Swap) {
-  auto test_swap = [](const absl::Status& s1, const absl::Status& s2) {
-    absl::Status copy1 = s1, copy2 = s2;
-    swap(copy1, copy2);
-    EXPECT_EQ(copy1, s2);
-    EXPECT_EQ(copy2, s1);
-  };
-  const absl::Status ok;
-  const absl::Status no_payload(absl::StatusCode::kAlreadyExists, "no payload");
-  absl::Status with_payload(absl::StatusCode::kInternal, "with payload");
-  with_payload.SetPayload(kUrl1, absl::Cord(kPayload1));
-  test_swap(ok, no_payload);
-  test_swap(no_payload, ok);
-  test_swap(ok, with_payload);
-  test_swap(with_payload, ok);
-  test_swap(no_payload, with_payload);
-  test_swap(with_payload, no_payload);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/status/statusor.cc b/third_party/abseil_cpp/absl/status/statusor.cc
deleted file mode 100644
index b954b45e32..0000000000
--- a/third_party/abseil_cpp/absl/status/statusor.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#include "absl/status/statusor.h"
-
-#include <cstdlib>
-#include <utility>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/status/status.h"
-#include "absl/strings/str_cat.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-BadStatusOrAccess::BadStatusOrAccess(absl::Status status)
-    : status_(std::move(status)) {}
-
-BadStatusOrAccess::~BadStatusOrAccess() = default;
-const char* BadStatusOrAccess::what() const noexcept {
-  return "Bad StatusOr access";
-}
-
-const absl::Status& BadStatusOrAccess::status() const { return status_; }
-
-namespace internal_statusor {
-
-void Helper::HandleInvalidStatusCtorArg(absl::Status* status) {
-  const char* kMessage =
-      "An OK status is not a valid constructor argument to StatusOr<T>";
-#ifdef NDEBUG
-  ABSL_INTERNAL_LOG(ERROR, kMessage);
-#else
-  ABSL_INTERNAL_LOG(FATAL, kMessage);
-#endif
-  // In optimized builds, we will fall back to InternalError.
-  *status = absl::InternalError(kMessage);
-}
-
-void Helper::Crash(const absl::Status& status) {
-  ABSL_INTERNAL_LOG(
-      FATAL,
-      absl::StrCat("Attempting to fetch value instead of handling error ",
-                   status.ToString()));
-}
-
-void ThrowBadStatusOrAccess(absl::Status status) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  throw absl::BadStatusOrAccess(std::move(status));
-#else
-  ABSL_INTERNAL_LOG(
-      FATAL,
-      absl::StrCat("Attempting to fetch value instead of handling error ",
-                   status.ToString()));
-  std::abort();
-#endif
-}
-
-}  // namespace internal_statusor
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/status/statusor.h b/third_party/abseil_cpp/absl/status/statusor.h
deleted file mode 100644
index 469d486fdd..0000000000
--- a/third_party/abseil_cpp/absl/status/statusor.h
+++ /dev/null
@@ -1,760 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: statusor.h
-// -----------------------------------------------------------------------------
-//
-// An `absl::StatusOr<T>` represents a union of an `absl::Status` object
-// and an object of type `T`. The `absl::StatusOr<T>` will either contain an
-// object of type `T` (indicating a successful operation), or an error (of type
-// `absl::Status`) explaining why such a value is not present.
-//
-// In general, check the success of an operation returning an
-// `absl::StatusOr<T>` like you would an `absl::Status` by using the `ok()`
-// member function.
-//
-// Example:
-//
-//   StatusOr<Foo> result = Calculation();
-//   if (result.ok()) {
-//     result->DoSomethingCool();
-//   } else {
-//     LOG(ERROR) << result.status();
-//   }
-#ifndef ABSL_STATUS_STATUSOR_H_
-#define ABSL_STATUS_STATUSOR_H_
-
-#include <exception>
-#include <initializer_list>
-#include <new>
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/attributes.h"
-#include "absl/meta/type_traits.h"
-#include "absl/status/internal/statusor_internal.h"
-#include "absl/status/status.h"
-#include "absl/types/variant.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// BadStatusOrAccess
-//
-// This class defines the type of object to throw (if exceptions are enabled),
-// when accessing the value of an `absl::StatusOr<T>` object that does not
-// contain a value. This behavior is analogous to that of
-// `std::bad_optional_access` in the case of accessing an invalid
-// `std::optional` value.
-//
-// Example:
-//
-// try {
-//   absl::StatusOr<int> v = FetchInt();
-//   DoWork(v.value());  // Accessing value() when not "OK" may throw
-// } catch (absl::BadStatusOrAccess& ex) {
-//   LOG(ERROR) << ex.status();
-// }
-class BadStatusOrAccess : public std::exception {
- public:
-  explicit BadStatusOrAccess(absl::Status status);
-  ~BadStatusOrAccess() override;
-
-  // BadStatusOrAccess::what()
-  //
-  // Returns the associated explanatory string of the `absl::StatusOr<T>`
-  // object's error code. This function only returns the string literal "Bad
-  // StatusOr Access" for cases when evaluating general exceptions.
-  //
-  // The pointer of this string is guaranteed to be valid until any non-const
-  // function is invoked on the exception object.
-  const char* what() const noexcept override;
-
-  // BadStatusOrAccess::status()
-  //
-  // Returns the associated `absl::Status` of the `absl::StatusOr<T>` object's
-  // error.
-  const absl::Status& status() const;
-
- private:
-  absl::Status status_;
-};
-
-// Returned StatusOr objects may not be ignored.
-template <typename T>
-class ABSL_MUST_USE_RESULT StatusOr;
-
-// absl::StatusOr<T>
-//
-// The `absl::StatusOr<T>` class template is a union of an `absl::Status` object
-// and an object of type `T`. The `absl::StatusOr<T>` models an object that is
-// either a usable object, or an error (of type `absl::Status`) explaining why
-// such an object is not present. An `absl::StatusOr<T>` is typically the return
-// value of a function which may fail.
-//
-// An `absl::StatusOr<T>` can never hold an "OK" status (an
-// `absl::StatusCode::kOk` value); instead, the presence of an object of type
-// `T` indicates success. Instead of checking for a `kOk` value, use the
-// `absl::StatusOr<T>::ok()` member function. (It is for this reason, and code
-// readability, that using the `ok()` function is preferred for `absl::Status`
-// as well.)
-//
-// Example:
-//
-//   StatusOr<Foo> result = DoBigCalculationThatCouldFail();
-//   if (result.ok()) {
-//     result->DoSomethingCool();
-//   } else {
-//     LOG(ERROR) << result.status();
-//   }
-//
-// Accessing the object held by an `absl::StatusOr<T>` should be performed via
-// `operator*` or `operator->`, after a call to `ok()` confirms that the
-// `absl::StatusOr<T>` holds an object of type `T`:
-//
-// Example:
-//
-//   absl::StatusOr<int> i = GetCount();
-//   if (i.ok()) {
-//     updated_total += *i
-//   }
-//
-// NOTE: using `absl::StatusOr<T>::value()` when no valid value is present will
-// throw an exception if exceptions are enabled or terminate the process when
-// execeptions are not enabled.
-//
-// Example:
-//
-//   StatusOr<Foo> result = DoBigCalculationThatCouldFail();
-//   const Foo& foo = result.value();    // Crash/exception if no value present
-//   foo.DoSomethingCool();
-//
-// A `absl::StatusOr<T*>` can be constructed from a null pointer like any other
-// pointer value, and the result will be that `ok()` returns `true` and
-// `value()` returns `nullptr`. Checking the value of pointer in an
-// `absl::StatusOr<T>` generally requires a bit more care, to ensure both that a
-// value is present and that value is not null:
-//
-//  StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
-//  if (!result.ok()) {
-//    LOG(ERROR) << result.status();
-//  } else if (*result == nullptr) {
-//    LOG(ERROR) << "Unexpected null pointer";
-//  } else {
-//    (*result)->DoSomethingCool();
-//  }
-//
-// Example factory implementation returning StatusOr<T>:
-//
-//  StatusOr<Foo> FooFactory::MakeFoo(int arg) {
-//    if (arg <= 0) {
-//      return absl::Status(absl::StatusCode::kInvalidArgument,
-//                          "Arg must be positive");
-//    }
-//    return Foo(arg);
-//  }
-template <typename T>
-class StatusOr : private internal_statusor::StatusOrData<T>,
-                 private internal_statusor::CopyCtorBase<T>,
-                 private internal_statusor::MoveCtorBase<T>,
-                 private internal_statusor::CopyAssignBase<T>,
-                 private internal_statusor::MoveAssignBase<T> {
-  template <typename U>
-  friend class StatusOr;
-
-  typedef internal_statusor::StatusOrData<T> Base;
-
- public:
-  // StatusOr<T>::value_type
-  //
-  // This instance data provides a generic `value_type` member for use within
-  // generic programming. This usage is analogous to that of
-  // `optional::value_type` in the case of `std::optional`.
-  typedef T value_type;
-
-  // Constructors
-
-  // Constructs a new `absl::StatusOr` with an `absl::StatusCode::kUnknown`
-  // status. This constructor is marked 'explicit' to prevent usages in return
-  // values such as 'return {};', under the misconception that
-  // `absl::StatusOr<std::vector<int>>` will be initialized with an empty
-  // vector, instead of an `absl::StatusCode::kUnknown` error code.
-  explicit StatusOr();
-
-  // `StatusOr<T>` is copy constructible if `T` is copy constructible.
-  StatusOr(const StatusOr&) = default;
-  // `StatusOr<T>` is copy assignable if `T` is copy constructible and copy
-  // assignable.
-  StatusOr& operator=(const StatusOr&) = default;
-
-  // `StatusOr<T>` is move constructible if `T` is move constructible.
-  StatusOr(StatusOr&&) = default;
-  // `StatusOr<T>` is moveAssignable if `T` is move constructible and move
-  // assignable.
-  StatusOr& operator=(StatusOr&&) = default;
-
-  // Converting Constructors
-
-  // Constructs a new `absl::StatusOr<T>` from an `absl::StatusOr<U>`, when `T`
-  // is constructible from `U`. To avoid ambiguity, these constructors are
-  // disabled if `T` is also constructible from `StatusOr<U>.`. This constructor
-  // is explicit if and only if the corresponding construction of `T` from `U`
-  // is explicit. (This constructor inherits its explicitness from the
-  // underlying constructor.)
-  template <
-      typename U,
-      absl::enable_if_t<
-          absl::conjunction<
-              absl::negation<std::is_same<T, U>>,
-              std::is_constructible<T, const U&>,
-              std::is_convertible<const U&, T>,
-              absl::negation<
-                  internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
-                      T, U>>>::value,
-          int> = 0>
-  StatusOr(const StatusOr<U>& other)  // NOLINT
-      : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
-  template <
-      typename U,
-      absl::enable_if_t<
-          absl::conjunction<
-              absl::negation<std::is_same<T, U>>,
-              std::is_constructible<T, const U&>,
-              absl::negation<std::is_convertible<const U&, T>>,
-              absl::negation<
-                  internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
-                      T, U>>>::value,
-          int> = 0>
-  explicit StatusOr(const StatusOr<U>& other)
-      : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
-
-  template <
-      typename U,
-      absl::enable_if_t<
-          absl::conjunction<
-              absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
-              std::is_convertible<U&&, T>,
-              absl::negation<
-                  internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
-                      T, U>>>::value,
-          int> = 0>
-  StatusOr(StatusOr<U>&& other)  // NOLINT
-      : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
-  template <
-      typename U,
-      absl::enable_if_t<
-          absl::conjunction<
-              absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
-              absl::negation<std::is_convertible<U&&, T>>,
-              absl::negation<
-                  internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
-                      T, U>>>::value,
-          int> = 0>
-  explicit StatusOr(StatusOr<U>&& other)
-      : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
-
-  // Converting Assignment Operators
-
-  // Creates an `absl::StatusOr<T>` through assignment from an
-  // `absl::StatusOr<U>` when:
-  //
-  //   * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` are OK by assigning
-  //     `U` to `T` directly.
-  //   * `absl::StatusOr<T>` is OK and `absl::StatusOr<U>` contains an error
-  //      code by destroying `absl::StatusOr<T>`'s value and assigning from
-  //      `absl::StatusOr<U>'
-  //   * `absl::StatusOr<T>` contains an error code and `absl::StatusOr<U>` is
-  //      OK by directly initializing `T` from `U`.
-  //   * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` contain an error
-  //     code by assigning the `Status` in `absl::StatusOr<U>` to
-  //     `absl::StatusOr<T>`
-  //
-  // These overloads only apply if `absl::StatusOr<T>` is constructible and
-  // assignable from `absl::StatusOr<U>` and `StatusOr<T>` cannot be directly
-  // assigned from `StatusOr<U>`.
-  template <
-      typename U,
-      absl::enable_if_t<
-          absl::conjunction<
-              absl::negation<std::is_same<T, U>>,
-              std::is_constructible<T, const U&>,
-              std::is_assignable<T, const U&>,
-              absl::negation<
-                  internal_statusor::
-                      IsConstructibleOrConvertibleOrAssignableFromStatusOr<
-                          T, U>>>::value,
-          int> = 0>
-  StatusOr& operator=(const StatusOr<U>& other) {
-    this->Assign(other);
-    return *this;
-  }
-  template <
-      typename U,
-      absl::enable_if_t<
-          absl::conjunction<
-              absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
-              std::is_assignable<T, U&&>,
-              absl::negation<
-                  internal_statusor::
-                      IsConstructibleOrConvertibleOrAssignableFromStatusOr<
-                          T, U>>>::value,
-          int> = 0>
-  StatusOr& operator=(StatusOr<U>&& other) {
-    this->Assign(std::move(other));
-    return *this;
-  }
-
-  // Constructs a new `absl::StatusOr<T>` with a non-ok status. After calling
-  // this constructor, `this->ok()` will be `false` and calls to `value()` will
-  // crash, or produce an exception if exceptions are enabled.
-  //
-  // The constructor also takes any type `U` that is convertible to
-  // `absl::Status`. This constructor is explicit if an only if `U` is not of
-  // type `absl::Status` and the conversion from `U` to `Status` is explicit.
-  //
-  // REQUIRES: !Status(std::forward<U>(v)).ok(). This requirement is DCHECKed.
-  // In optimized builds, passing absl::OkStatus() here will have the effect
-  // of passing absl::StatusCode::kInternal as a fallback.
-  template <
-      typename U = absl::Status,
-      absl::enable_if_t<
-          absl::conjunction<
-              std::is_convertible<U&&, absl::Status>,
-              std::is_constructible<absl::Status, U&&>,
-              absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>,
-              absl::negation<std::is_same<absl::decay_t<U>, T>>,
-              absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>,
-              absl::negation<internal_statusor::HasConversionOperatorToStatusOr<
-                  T, U&&>>>::value,
-          int> = 0>
-  StatusOr(U&& v) : Base(std::forward<U>(v)) {}
-
-  template <
-      typename U = absl::Status,
-      absl::enable_if_t<
-          absl::conjunction<
-              absl::negation<std::is_convertible<U&&, absl::Status>>,
-              std::is_constructible<absl::Status, U&&>,
-              absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>,
-              absl::negation<std::is_same<absl::decay_t<U>, T>>,
-              absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>,
-              absl::negation<internal_statusor::HasConversionOperatorToStatusOr<
-                  T, U&&>>>::value,
-          int> = 0>
-  explicit StatusOr(U&& v) : Base(std::forward<U>(v)) {}
-
-  template <
-      typename U = absl::Status,
-      absl::enable_if_t<
-          absl::conjunction<
-              std::is_convertible<U&&, absl::Status>,
-              std::is_constructible<absl::Status, U&&>,
-              absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>,
-              absl::negation<std::is_same<absl::decay_t<U>, T>>,
-              absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>,
-              absl::negation<internal_statusor::HasConversionOperatorToStatusOr<
-                  T, U&&>>>::value,
-          int> = 0>
-  StatusOr& operator=(U&& v) {
-    this->AssignStatus(std::forward<U>(v));
-    return *this;
-  }
-
-  // Perfect-forwarding value assignment operator.
-
-  // If `*this` contains a `T` value before the call, the contained value is
-  // assigned from `std::forward<U>(v)`; Otherwise, it is directly-initialized
-  // from `std::forward<U>(v)`.
-  // This function does not participate in overload unless:
-  // 1. `std::is_constructible_v<T, U>` is true,
-  // 2. `std::is_assignable_v<T&, U>` is true.
-  // 3. `std::is_same_v<StatusOr<T>, std::remove_cvref_t<U>>` is false.
-  // 4. Assigning `U` to `T` is not ambiguous:
-  //  If `U` is `StatusOr<V>` and `T` is constructible and assignable from
-  //  both `StatusOr<V>` and `V`, the assignment is considered bug-prone and
-  //  ambiguous thus will fail to compile. For example:
-  //    StatusOr<bool> s1 = true;  // s1.ok() && *s1 == true
-  //    StatusOr<bool> s2 = false;  // s2.ok() && *s2 == false
-  //    s1 = s2;  // ambiguous, `s1 = *s2` or `s1 = bool(s2)`?
-  template <
-      typename U = T,
-      typename = typename std::enable_if<absl::conjunction<
-          std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>,
-          absl::disjunction<
-              std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, T>,
-              absl::conjunction<
-                  absl::negation<std::is_convertible<U&&, absl::Status>>,
-                  absl::negation<internal_statusor::
-                                     HasConversionOperatorToStatusOr<T, U&&>>>>,
-          internal_statusor::IsForwardingAssignmentValid<T, U&&>>::value>::type>
-  StatusOr& operator=(U&& v) {
-    this->Assign(std::forward<U>(v));
-    return *this;
-  }
-
-  // Constructs the inner value `T` in-place using the provided args, using the
-  // `T(args...)` constructor.
-  template <typename... Args>
-  explicit StatusOr(absl::in_place_t, Args&&... args);
-  template <typename U, typename... Args>
-  explicit StatusOr(absl::in_place_t, std::initializer_list<U> ilist,
-                    Args&&... args);
-
-  // Constructs the inner value `T` in-place using the provided args, using the
-  // `T(U)` (direct-initialization) constructor. This constructor is only valid
-  // if `T` can be constructed from a `U`. Can accept move or copy constructors.
-  //
-  // This constructor is explicit if `U` is not convertible to `T`. To avoid
-  // ambiguity, this constuctor is disabled if `U` is a `StatusOr<J>`, where `J`
-  // is convertible to `T`.
-  template <
-      typename U = T,
-      absl::enable_if_t<
-          absl::conjunction<
-              internal_statusor::IsDirectInitializationValid<T, U&&>,
-              std::is_constructible<T, U&&>, std::is_convertible<U&&, T>,
-              absl::disjunction<
-                  std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
-                               T>,
-                  absl::conjunction<
-                      absl::negation<std::is_convertible<U&&, absl::Status>>,
-                      absl::negation<
-                          internal_statusor::HasConversionOperatorToStatusOr<
-                              T, U&&>>>>>::value,
-          int> = 0>
-  StatusOr(U&& u)  // NOLINT
-      : StatusOr(absl::in_place, std::forward<U>(u)) {
-  }
-
-  template <
-      typename U = T,
-      absl::enable_if_t<
-          absl::conjunction<
-              internal_statusor::IsDirectInitializationValid<T, U&&>,
-              absl::disjunction<
-                  std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
-                               T>,
-                  absl::conjunction<
-                      absl::negation<std::is_constructible<absl::Status, U&&>>,
-                      absl::negation<
-                          internal_statusor::HasConversionOperatorToStatusOr<
-                              T, U&&>>>>,
-              std::is_constructible<T, U&&>,
-              absl::negation<std::is_convertible<U&&, T>>>::value,
-          int> = 0>
-  explicit StatusOr(U&& u)  // NOLINT
-      : StatusOr(absl::in_place, std::forward<U>(u)) {
-  }
-
-  // StatusOr<T>::ok()
-  //
-  // Returns whether or not this `absl::StatusOr<T>` holds a `T` value. This
-  // member function is analagous to `absl::Status::ok()` and should be used
-  // similarly to check the status of return values.
-  //
-  // Example:
-  //
-  // StatusOr<Foo> result = DoBigCalculationThatCouldFail();
-  // if (result.ok()) {
-  //    // Handle result
-  // else {
-  //    // Handle error
-  // }
-  ABSL_MUST_USE_RESULT bool ok() const { return this->status_.ok(); }
-
-  // StatusOr<T>::status()
-  //
-  // Returns a reference to the current `absl::Status` contained within the
-  // `absl::StatusOr<T>`. If `absl::StatusOr<T>` contains a `T`, then this
-  // function returns `absl::OkStatus()`.
-  const Status& status() const &;
-  Status status() &&;
-
-  // StatusOr<T>::value()
-  //
-  // Returns a reference to the held value if `this->ok()`. Otherwise, throws
-  // `absl::BadStatusOrAccess` if exceptions are enabled, or is guaranteed to
-  // terminate the process if exceptions are disabled.
-  //
-  // If you have already checked the status using `this->ok()`, you probably
-  // want to use `operator*()` or `operator->()` to access the value instead of
-  // `value`.
-  //
-  // Note: for value types that are cheap to copy, prefer simple code:
-  //
-  //   T value = statusor.value();
-  //
-  // Otherwise, if the value type is expensive to copy, but can be left
-  // in the StatusOr, simply assign to a reference:
-  //
-  //   T& value = statusor.value();  // or `const T&`
-  //
-  // Otherwise, if the value type supports an efficient move, it can be
-  // used as follows:
-  //
-  //   T value = std::move(statusor).value();
-  //
-  // The `std::move` on statusor instead of on the whole expression enables
-  // warnings about possible uses of the statusor object after the move.
-  const T& value() const&;
-  T& value() &;
-  const T&& value() const&&;
-  T&& value() &&;
-
-  // StatusOr<T>:: operator*()
-  //
-  // Returns a reference to the current value.
-  //
-  // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined.
-  //
-  // Use `this->ok()` to verify that there is a current value within the
-  // `absl::StatusOr<T>`. Alternatively, see the `value()` member function for a
-  // similar API that guarantees crashing or throwing an exception if there is
-  // no current value.
-  const T& operator*() const&;
-  T& operator*() &;
-  const T&& operator*() const&&;
-  T&& operator*() &&;
-
-  // StatusOr<T>::operator->()
-  //
-  // Returns a pointer to the current value.
-  //
-  // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined.
-  //
-  // Use `this->ok()` to verify that there is a current value.
-  const T* operator->() const;
-  T* operator->();
-
-  // StatusOr<T>::value_or()
-  //
-  // Returns the current value if `this->ok() == true`. Otherwise constructs a
-  // value using the provided `default_value`.
-  //
-  // Unlike `value`, this function returns by value, copying the current value
-  // if necessary. If the value type supports an efficient move, it can be used
-  // as follows:
-  //
-  //   T value = std::move(statusor).value_or(def);
-  //
-  // Unlike with `value`, calling `std::move()` on the result of `value_or` will
-  // still trigger a copy.
-  template <typename U>
-  T value_or(U&& default_value) const&;
-  template <typename U>
-  T value_or(U&& default_value) &&;
-
-  // StatusOr<T>::IgnoreError()
-  //
-  // Ignores any errors. This method does nothing except potentially suppress
-  // complaints from any tools that are checking that errors are not dropped on
-  // the floor.
-  void IgnoreError() const;
-
-  // StatusOr<T>::emplace()
-  //
-  // Reconstructs the inner value T in-place using the provided args, using the
-  // T(args...) constructor. Returns reference to the reconstructed `T`.
-  template <typename... Args>
-  T& emplace(Args&&... args) {
-    if (ok()) {
-      this->Clear();
-      this->MakeValue(std::forward<Args>(args)...);
-    } else {
-      this->MakeValue(std::forward<Args>(args)...);
-      this->status_ = absl::OkStatus();
-    }
-    return this->data_;
-  }
-
-  template <
-      typename U, typename... Args,
-      absl::enable_if_t<
-          std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
-          int> = 0>
-  T& emplace(std::initializer_list<U> ilist, Args&&... args) {
-    if (ok()) {
-      this->Clear();
-      this->MakeValue(ilist, std::forward<Args>(args)...);
-    } else {
-      this->MakeValue(ilist, std::forward<Args>(args)...);
-      this->status_ = absl::OkStatus();
-    }
-    return this->data_;
-  }
-
- private:
-  using internal_statusor::StatusOrData<T>::Assign;
-  template <typename U>
-  void Assign(const absl::StatusOr<U>& other);
-  template <typename U>
-  void Assign(absl::StatusOr<U>&& other);
-};
-
-// operator==()
-//
-// This operator checks the equality of two `absl::StatusOr<T>` objects.
-template <typename T>
-bool operator==(const StatusOr<T>& lhs, const StatusOr<T>& rhs) {
-  if (lhs.ok() && rhs.ok()) return *lhs == *rhs;
-  return lhs.status() == rhs.status();
-}
-
-// operator!=()
-//
-// This operator checks the inequality of two `absl::StatusOr<T>` objects.
-template <typename T>
-bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) {
-  return !(lhs == rhs);
-}
-
-//------------------------------------------------------------------------------
-// Implementation details for StatusOr<T>
-//------------------------------------------------------------------------------
-
-// TODO(sbenza): avoid the string here completely.
-template <typename T>
-StatusOr<T>::StatusOr() : Base(Status(absl::StatusCode::kUnknown, "")) {}
-
-template <typename T>
-template <typename U>
-inline void StatusOr<T>::Assign(const StatusOr<U>& other) {
-  if (other.ok()) {
-    this->Assign(*other);
-  } else {
-    this->AssignStatus(other.status());
-  }
-}
-
-template <typename T>
-template <typename U>
-inline void StatusOr<T>::Assign(StatusOr<U>&& other) {
-  if (other.ok()) {
-    this->Assign(*std::move(other));
-  } else {
-    this->AssignStatus(std::move(other).status());
-  }
-}
-template <typename T>
-template <typename... Args>
-StatusOr<T>::StatusOr(absl::in_place_t, Args&&... args)
-    : Base(absl::in_place, std::forward<Args>(args)...) {}
-
-template <typename T>
-template <typename U, typename... Args>
-StatusOr<T>::StatusOr(absl::in_place_t, std::initializer_list<U> ilist,
-                      Args&&... args)
-    : Base(absl::in_place, ilist, std::forward<Args>(args)...) {}
-
-template <typename T>
-const Status& StatusOr<T>::status() const & { return this->status_; }
-template <typename T>
-Status StatusOr<T>::status() && {
-  return ok() ? OkStatus() : std::move(this->status_);
-}
-
-template <typename T>
-const T& StatusOr<T>::value() const& {
-  if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_);
-  return this->data_;
-}
-
-template <typename T>
-T& StatusOr<T>::value() & {
-  if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_);
-  return this->data_;
-}
-
-template <typename T>
-const T&& StatusOr<T>::value() const&& {
-  if (!this->ok()) {
-    internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_));
-  }
-  return std::move(this->data_);
-}
-
-template <typename T>
-T&& StatusOr<T>::value() && {
-  if (!this->ok()) {
-    internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_));
-  }
-  return std::move(this->data_);
-}
-
-template <typename T>
-const T& StatusOr<T>::operator*() const& {
-  this->EnsureOk();
-  return this->data_;
-}
-
-template <typename T>
-T& StatusOr<T>::operator*() & {
-  this->EnsureOk();
-  return this->data_;
-}
-
-template <typename T>
-const T&& StatusOr<T>::operator*() const&& {
-  this->EnsureOk();
-  return std::move(this->data_);
-}
-
-template <typename T>
-T&& StatusOr<T>::operator*() && {
-  this->EnsureOk();
-  return std::move(this->data_);
-}
-
-template <typename T>
-const T* StatusOr<T>::operator->() const {
-  this->EnsureOk();
-  return &this->data_;
-}
-
-template <typename T>
-T* StatusOr<T>::operator->() {
-  this->EnsureOk();
-  return &this->data_;
-}
-
-template <typename T>
-template <typename U>
-T StatusOr<T>::value_or(U&& default_value) const& {
-  if (ok()) {
-    return this->data_;
-  }
-  return std::forward<U>(default_value);
-}
-
-template <typename T>
-template <typename U>
-T StatusOr<T>::value_or(U&& default_value) && {
-  if (ok()) {
-    return std::move(this->data_);
-  }
-  return std::forward<U>(default_value);
-}
-
-template <typename T>
-void StatusOr<T>::IgnoreError() const {
-  // no-op
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STATUS_STATUSOR_H_
diff --git a/third_party/abseil_cpp/absl/status/statusor_internals.h b/third_party/abseil_cpp/absl/status/statusor_internals.h
deleted file mode 100644
index 5366c2840c..0000000000
--- a/third_party/abseil_cpp/absl/status/statusor_internals.h
+++ /dev/null
@@ -1,250 +0,0 @@
-/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-==============================================================================*/
-
-#ifndef ABSL_STATUS_STATUSOR_INTERNALS_H_
-#define ABSL_STATUS_STATUSOR_INTERNALS_H_
-
-#include "absl/status/status.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace internal_statusor {
-
-class Helper {
- public:
-  // Move type-agnostic error handling to the .cc.
-  static void HandleInvalidStatusCtorArg(Status*);
-  ABSL_ATTRIBUTE_NORETURN static void Crash(const Status& status);
-};
-
-// Construct an instance of T in `p` through placement new, passing Args... to
-// the constructor.
-// This abstraction is here mostly for the gcc performance fix.
-template <typename T, typename... Args>
-void PlacementNew(void* p, Args&&... args) {
-#if defined(__GNUC__) && !defined(__clang__)
-  // Teach gcc that 'p' cannot be null, fixing code size issues.
-  if (p == nullptr) __builtin_unreachable();
-#endif
-  new (p) T(std::forward<Args>(args)...);
-}
-
-// Helper base class to hold the data and all operations.
-// We move all this to a base class to allow mixing with the appropriate
-// TraitsBase specialization.
-template <typename T>
-class StatusOrData {
-  template <typename U>
-  friend class StatusOrData;
-
- public:
-  StatusOrData() = delete;
-
-  StatusOrData(const StatusOrData& other) {
-    if (other.ok()) {
-      MakeValue(other.data_);
-      MakeStatus();
-    } else {
-      MakeStatus(other.status_);
-    }
-  }
-
-  StatusOrData(StatusOrData&& other) noexcept {
-    if (other.ok()) {
-      MakeValue(std::move(other.data_));
-      MakeStatus();
-    } else {
-      MakeStatus(other.status_);
-    }
-  }
-
-  template <typename U>
-  StatusOrData(const StatusOrData<U>& other) {
-    if (other.ok()) {
-      MakeValue(other.data_);
-      MakeStatus();
-    } else {
-      MakeStatus(other.status_);
-    }
-  }
-
-  template <typename U>
-  StatusOrData(StatusOrData<U>&& other) {
-    if (other.ok()) {
-      MakeValue(std::move(other.data_));
-      MakeStatus();
-    } else {
-      MakeStatus(other.status_);
-    }
-  }
-
-  explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); }
-  explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); }
-
-  explicit StatusOrData(const Status& status) : status_(status) {
-    EnsureNotOk();
-  }
-  explicit StatusOrData(Status&& status) : status_(std::move(status)) {
-    EnsureNotOk();
-  }
-
-  StatusOrData& operator=(const StatusOrData& other) {
-    if (this == &other) return *this;
-    if (other.ok())
-      Assign(other.data_);
-    else
-      Assign(other.status_);
-    return *this;
-  }
-
-  StatusOrData& operator=(StatusOrData&& other) {
-    if (this == &other) return *this;
-    if (other.ok())
-      Assign(std::move(other.data_));
-    else
-      Assign(std::move(other.status_));
-    return *this;
-  }
-
-  ~StatusOrData() {
-    if (ok()) {
-      status_.~Status();
-      data_.~T();
-    } else {
-      status_.~Status();
-    }
-  }
-
-  void Assign(const T& value) {
-    if (ok()) {
-      data_.~T();
-      MakeValue(value);
-    } else {
-      MakeValue(value);
-      status_ = OkStatus();
-    }
-  }
-
-  void Assign(T&& value) {
-    if (ok()) {
-      data_.~T();
-      MakeValue(std::move(value));
-    } else {
-      MakeValue(std::move(value));
-      status_ = OkStatus();
-    }
-  }
-
-  void Assign(const Status& status) {
-    Clear();
-    status_ = status;
-    EnsureNotOk();
-  }
-
-  void Assign(Status&& status) {
-    Clear();
-    // Note that we copy instead of moving the status here so that
-    // status.~StatusOrData() can call ok() without invoking UB.
-    status_ = status;
-    EnsureNotOk();
-  }
-
-  bool ok() const { return status_.ok(); }
-
- protected:
-  // status_ will always be active after the constructor.
-  // We make it a union to be able to initialize exactly how we need without
-  // waste.
-  // Eg. in the copy constructor we use the default constructor of Status in
-  // the ok() path to avoid an extra Ref call.
-  union {
-    Status status_;
-  };
-
-  // data_ is active iff status_.ok()==true
-  struct Dummy {};
-  union {
-    // When T is const, we need some non-const object we can cast to void* for
-    // the placement new. dummy_ is that object.
-    Dummy dummy_;
-    T data_;
-  };
-
-  void Clear() {
-    if (ok()) data_.~T();
-  }
-
-  void EnsureOk() const {
-    if (!ok()) Helper::Crash(status_);
-  }
-
-  void EnsureNotOk() {
-    if (ok()) Helper::HandleInvalidStatusCtorArg(&status_);
-  }
-
-  // Construct the value (ie. data_) through placement new with the passed
-  // argument.
-  template <typename Arg>
-  void MakeValue(Arg&& arg) {
-    internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg));
-  }
-
-  // Construct the status (ie. status_) through placement new with the passed
-  // argument.
-  template <typename... Args>
-  void MakeStatus(Args&&... args) {
-    internal_statusor::PlacementNew<Status>(&status_,
-                                            std::forward<Args>(args)...);
-  }
-};
-
-// Helper base class to allow implicitly deleted constructors and assignment
-// operations in StatusOr.
-// TraitsBase will explicitly delete what it can't support and StatusOr will
-// inherit that behavior implicitly.
-template <bool Copy, bool Move>
-struct TraitsBase {
-  TraitsBase() = default;
-  TraitsBase(const TraitsBase&) = default;
-  TraitsBase(TraitsBase&&) = default;
-  TraitsBase& operator=(const TraitsBase&) = default;
-  TraitsBase& operator=(TraitsBase&&) = default;
-};
-
-template <>
-struct TraitsBase<false, true> {
-  TraitsBase() = default;
-  TraitsBase(const TraitsBase&) = delete;
-  TraitsBase(TraitsBase&&) = default;
-  TraitsBase& operator=(const TraitsBase&) = delete;
-  TraitsBase& operator=(TraitsBase&&) = default;
-};
-
-template <>
-struct TraitsBase<false, false> {
-  TraitsBase() = default;
-  TraitsBase(const TraitsBase&) = delete;
-  TraitsBase(TraitsBase&&) = delete;
-  TraitsBase& operator=(const TraitsBase&) = delete;
-  TraitsBase& operator=(TraitsBase&&) = delete;
-};
-
-}  // namespace internal_statusor
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STATUS_STATUSOR_INTERNALS_H_
diff --git a/third_party/abseil_cpp/absl/status/statusor_test.cc b/third_party/abseil_cpp/absl/status/statusor_test.cc
deleted file mode 100644
index c2e8fb7e35..0000000000
--- a/third_party/abseil_cpp/absl/status/statusor_test.cc
+++ /dev/null
@@ -1,1811 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/status/statusor.h"
-
-#include <array>
-#include <initializer_list>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/casts.h"
-#include "absl/memory/memory.h"
-#include "absl/status/status.h"
-#include "absl/types/any.h"
-#include "absl/utility/utility.h"
-
-namespace {
-
-using ::testing::AllOf;
-using ::testing::AnyWith;
-using ::testing::ElementsAre;
-using ::testing::Field;
-using ::testing::Ne;
-using ::testing::Not;
-using ::testing::Pointee;
-using ::testing::VariantWith;
-
-#ifdef GTEST_HAS_STATUS_MATCHERS
-using ::testing::status::IsOk;
-using ::testing::status::IsOkAndHolds;
-#else  // GTEST_HAS_STATUS_MATCHERS
-inline const ::absl::Status& GetStatus(const ::absl::Status& status) {
-  return status;
-}
-
-template <typename T>
-inline const ::absl::Status& GetStatus(const ::absl::StatusOr<T>& status) {
-  return status.status();
-}
-
-// Monomorphic implementation of matcher IsOkAndHolds(m).  StatusOrType is a
-// reference to StatusOr<T>.
-template <typename StatusOrType>
-class IsOkAndHoldsMatcherImpl
-    : public ::testing::MatcherInterface<StatusOrType> {
- public:
-  typedef
-      typename std::remove_reference<StatusOrType>::type::value_type value_type;
-
-  template <typename InnerMatcher>
-  explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher)
-      : inner_matcher_(::testing::SafeMatcherCast<const value_type&>(
-            std::forward<InnerMatcher>(inner_matcher))) {}
-
-  void DescribeTo(std::ostream* os) const override {
-    *os << "is OK and has a value that ";
-    inner_matcher_.DescribeTo(os);
-  }
-
-  void DescribeNegationTo(std::ostream* os) const override {
-    *os << "isn't OK or has a value that ";
-    inner_matcher_.DescribeNegationTo(os);
-  }
-
-  bool MatchAndExplain(
-      StatusOrType actual_value,
-      ::testing::MatchResultListener* result_listener) const override {
-    if (!actual_value.ok()) {
-      *result_listener << "which has status " << actual_value.status();
-      return false;
-    }
-
-    ::testing::StringMatchResultListener inner_listener;
-    const bool matches =
-        inner_matcher_.MatchAndExplain(*actual_value, &inner_listener);
-    const std::string inner_explanation = inner_listener.str();
-    if (!inner_explanation.empty()) {
-      *result_listener << "which contains value "
-                       << ::testing::PrintToString(*actual_value) << ", "
-                       << inner_explanation;
-    }
-    return matches;
-  }
-
- private:
-  const ::testing::Matcher<const value_type&> inner_matcher_;
-};
-
-// Implements IsOkAndHolds(m) as a polymorphic matcher.
-template <typename InnerMatcher>
-class IsOkAndHoldsMatcher {
- public:
-  explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher)
-      : inner_matcher_(std::move(inner_matcher)) {}
-
-  // Converts this polymorphic matcher to a monomorphic matcher of the
-  // given type.  StatusOrType can be either StatusOr<T> or a
-  // reference to StatusOr<T>.
-  template <typename StatusOrType>
-  operator ::testing::Matcher<StatusOrType>() const {  // NOLINT
-    return ::testing::Matcher<StatusOrType>(
-        new IsOkAndHoldsMatcherImpl<const StatusOrType&>(inner_matcher_));
-  }
-
- private:
-  const InnerMatcher inner_matcher_;
-};
-
-// Monomorphic implementation of matcher IsOk() for a given type T.
-// T can be Status, StatusOr<>, or a reference to either of them.
-template <typename T>
-class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> {
- public:
-  void DescribeTo(std::ostream* os) const override { *os << "is OK"; }
-  void DescribeNegationTo(std::ostream* os) const override {
-    *os << "is not OK";
-  }
-  bool MatchAndExplain(T actual_value,
-                       ::testing::MatchResultListener*) const override {
-    return GetStatus(actual_value).ok();
-  }
-};
-
-// Implements IsOk() as a polymorphic matcher.
-class IsOkMatcher {
- public:
-  template <typename T>
-  operator ::testing::Matcher<T>() const {  // NOLINT
-    return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<T>());
-  }
-};
-
-// Macros for testing the results of functions that return absl::Status or
-// absl::StatusOr<T> (for any type T).
-#define EXPECT_OK(expression) EXPECT_THAT(expression, IsOk())
-
-// Returns a gMock matcher that matches a StatusOr<> whose status is
-// OK and whose value matches the inner matcher.
-template <typename InnerMatcher>
-IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type> IsOkAndHolds(
-    InnerMatcher&& inner_matcher) {
-  return IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>(
-      std::forward<InnerMatcher>(inner_matcher));
-}
-
-// Returns a gMock matcher that matches a Status or StatusOr<> which is OK.
-inline IsOkMatcher IsOk() { return IsOkMatcher(); }
-#endif  // GTEST_HAS_STATUS_MATCHERS
-
-struct CopyDetector {
-  CopyDetector() = default;
-  explicit CopyDetector(int xx) : x(xx) {}
-  CopyDetector(CopyDetector&& d) noexcept
-      : x(d.x), copied(false), moved(true) {}
-  CopyDetector(const CopyDetector& d) : x(d.x), copied(true), moved(false) {}
-  CopyDetector& operator=(const CopyDetector& c) {
-    x = c.x;
-    copied = true;
-    moved = false;
-    return *this;
-  }
-  CopyDetector& operator=(CopyDetector&& c) noexcept {
-    x = c.x;
-    copied = false;
-    moved = true;
-    return *this;
-  }
-  int x = 0;
-  bool copied = false;
-  bool moved = false;
-};
-
-testing::Matcher<const CopyDetector&> CopyDetectorHas(int a, bool b, bool c) {
-  return AllOf(Field(&CopyDetector::x, a), Field(&CopyDetector::moved, b),
-               Field(&CopyDetector::copied, c));
-}
-
-class Base1 {
- public:
-  virtual ~Base1() {}
-  int pad;
-};
-
-class Base2 {
- public:
-  virtual ~Base2() {}
-  int yetotherpad;
-};
-
-class Derived : public Base1, public Base2 {
- public:
-  virtual ~Derived() {}
-  int evenmorepad;
-};
-
-class CopyNoAssign {
- public:
-  explicit CopyNoAssign(int value) : foo(value) {}
-  CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {}
-  int foo;
-
- private:
-  const CopyNoAssign& operator=(const CopyNoAssign&);
-};
-
-absl::StatusOr<std::unique_ptr<int>> ReturnUniquePtr() {
-  // Uses implicit constructor from T&&
-  return absl::make_unique<int>(0);
-}
-
-TEST(StatusOr, ElementType) {
-  static_assert(std::is_same<absl::StatusOr<int>::value_type, int>(), "");
-  static_assert(std::is_same<absl::StatusOr<char>::value_type, char>(), "");
-}
-
-TEST(StatusOr, TestMoveOnlyInitialization) {
-  absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr());
-  ASSERT_TRUE(thing.ok());
-  EXPECT_EQ(0, **thing);
-  int* previous = thing->get();
-
-  thing = ReturnUniquePtr();
-  EXPECT_TRUE(thing.ok());
-  EXPECT_EQ(0, **thing);
-  EXPECT_NE(previous, thing->get());
-}
-
-TEST(StatusOr, TestMoveOnlyValueExtraction) {
-  absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr());
-  ASSERT_TRUE(thing.ok());
-  std::unique_ptr<int> ptr = *std::move(thing);
-  EXPECT_EQ(0, *ptr);
-
-  thing = std::move(ptr);
-  ptr = std::move(*thing);
-  EXPECT_EQ(0, *ptr);
-}
-
-TEST(StatusOr, TestMoveOnlyInitializationFromTemporaryByValueOrDie) {
-  std::unique_ptr<int> ptr(*ReturnUniquePtr());
-  EXPECT_EQ(0, *ptr);
-}
-
-TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) {
-  static_assert(
-      std::is_same<const int&&,
-                   decltype(
-                       std::declval<const absl::StatusOr<int>&&>().value())>(),
-      "value() for const temporaries should return const T&&");
-}
-
-TEST(StatusOr, TestMoveOnlyConversion) {
-  absl::StatusOr<std::unique_ptr<const int>> const_thing(ReturnUniquePtr());
-  EXPECT_TRUE(const_thing.ok());
-  EXPECT_EQ(0, **const_thing);
-
-  // Test rvalue converting assignment
-  const int* const_previous = const_thing->get();
-  const_thing = ReturnUniquePtr();
-  EXPECT_TRUE(const_thing.ok());
-  EXPECT_EQ(0, **const_thing);
-  EXPECT_NE(const_previous, const_thing->get());
-}
-
-TEST(StatusOr, TestMoveOnlyVector) {
-  // Sanity check that absl::StatusOr<MoveOnly> works in vector.
-  std::vector<absl::StatusOr<std::unique_ptr<int>>> vec;
-  vec.push_back(ReturnUniquePtr());
-  vec.resize(2);
-  auto another_vec = std::move(vec);
-  EXPECT_EQ(0, **another_vec[0]);
-  EXPECT_EQ(absl::UnknownError(""), another_vec[1].status());
-}
-
-TEST(StatusOr, TestDefaultCtor) {
-  absl::StatusOr<int> thing;
-  EXPECT_FALSE(thing.ok());
-  EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown);
-}
-
-TEST(StatusOr, StatusCtorForwards) {
-  absl::Status status(absl::StatusCode::kInternal, "Some error");
-
-  EXPECT_EQ(absl::StatusOr<int>(status).status().message(), "Some error");
-  EXPECT_EQ(status.message(), "Some error");
-
-  EXPECT_EQ(absl::StatusOr<int>(std::move(status)).status().message(),
-            "Some error");
-  EXPECT_NE(status.message(), "Some error");
-}
-
-// Define `EXPECT_DEATH_OR_THROW` to test the behavior of `StatusOr::value`,
-// which either throws `BadStatusOrAccess` or `LOG(FATAL)` based on whether
-// exceptions are enabled.
-#ifdef ABSL_HAVE_EXCEPTIONS
-#define EXPECT_DEATH_OR_THROW(statement, status_)    \
-  EXPECT_THROW(                                      \
-      {                                              \
-        try {                                        \
-          statement;                                 \
-        } catch (const absl::BadStatusOrAccess& e) { \
-          EXPECT_EQ(e.status(), status_);            \
-          throw;                                     \
-        }                                            \
-      },                                             \
-      absl::BadStatusOrAccess);
-#else  // ABSL_HAVE_EXCEPTIONS
-#define EXPECT_DEATH_OR_THROW(statement, status) \
-  EXPECT_DEATH_IF_SUPPORTED(statement, status.ToString());
-#endif  // ABSL_HAVE_EXCEPTIONS
-
-TEST(StatusOrDeathTest, TestDefaultCtorValue) {
-  absl::StatusOr<int> thing;
-  EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError(""));
-  const absl::StatusOr<int> thing2;
-  EXPECT_DEATH_OR_THROW(thing2.value(), absl::UnknownError(""));
-}
-
-TEST(StatusOrDeathTest, TestValueNotOk) {
-  absl::StatusOr<int> thing(absl::CancelledError());
-  EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError());
-}
-
-TEST(StatusOrDeathTest, TestValueNotOkConst) {
-  const absl::StatusOr<int> thing(absl::UnknownError(""));
-  EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError(""));
-}
-
-TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) {
-  absl::StatusOr<int*> thing;
-  EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError(""));
-}
-
-TEST(StatusOrDeathTest, TestPointerValueNotOk) {
-  absl::StatusOr<int*> thing(absl::CancelledError());
-  EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError());
-}
-
-TEST(StatusOrDeathTest, TestPointerValueNotOkConst) {
-  const absl::StatusOr<int*> thing(absl::CancelledError());
-  EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError());
-}
-
-#if GTEST_HAS_DEATH_TEST
-TEST(StatusOrDeathTest, TestStatusCtorStatusOk) {
-  EXPECT_DEBUG_DEATH(
-      {
-        // This will DCHECK
-        absl::StatusOr<int> thing(absl::OkStatus());
-        // In optimized mode, we are actually going to get error::INTERNAL for
-        // status here, rather than crashing, so check that.
-        EXPECT_FALSE(thing.ok());
-        EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal);
-      },
-      "An OK status is not a valid constructor argument");
-}
-
-TEST(StatusOrDeathTest, TestPointerStatusCtorStatusOk) {
-  EXPECT_DEBUG_DEATH(
-      {
-        absl::StatusOr<int*> thing(absl::OkStatus());
-        // In optimized mode, we are actually going to get error::INTERNAL for
-        // status here, rather than crashing, so check that.
-        EXPECT_FALSE(thing.ok());
-        EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal);
-      },
-      "An OK status is not a valid constructor argument");
-}
-#endif
-
-TEST(StatusOr, ValueAccessor) {
-  const int kIntValue = 110;
-  {
-    absl::StatusOr<int> status_or(kIntValue);
-    EXPECT_EQ(kIntValue, status_or.value());
-    EXPECT_EQ(kIntValue, std::move(status_or).value());
-  }
-  {
-    absl::StatusOr<CopyDetector> status_or(kIntValue);
-    EXPECT_THAT(status_or,
-                IsOkAndHolds(CopyDetectorHas(kIntValue, false, false)));
-    CopyDetector copy_detector = status_or.value();
-    EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, false, true));
-    copy_detector = std::move(status_or).value();
-    EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, true, false));
-  }
-}
-
-TEST(StatusOr, BadValueAccess) {
-  const absl::Status kError = absl::CancelledError("message");
-  absl::StatusOr<int> status_or(kError);
-  EXPECT_DEATH_OR_THROW(status_or.value(), kError);
-}
-
-TEST(StatusOr, TestStatusCtor) {
-  absl::StatusOr<int> thing(absl::CancelledError());
-  EXPECT_FALSE(thing.ok());
-  EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled);
-}
-
-
-
-TEST(StatusOr, TestValueCtor) {
-  const int kI = 4;
-  const absl::StatusOr<int> thing(kI);
-  EXPECT_TRUE(thing.ok());
-  EXPECT_EQ(kI, *thing);
-}
-
-struct Foo {
-  const int x;
-  explicit Foo(int y) : x(y) {}
-};
-
-TEST(StatusOr, InPlaceConstruction) {
-  EXPECT_THAT(absl::StatusOr<Foo>(absl::in_place, 10),
-              IsOkAndHolds(Field(&Foo::x, 10)));
-}
-
-struct InPlaceHelper {
-  InPlaceHelper(std::initializer_list<int> xs, std::unique_ptr<int> yy)
-      : x(xs), y(std::move(yy)) {}
-  const std::vector<int> x;
-  std::unique_ptr<int> y;
-};
-
-TEST(StatusOr, InPlaceInitListConstruction) {
-  absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12},
-                                          absl::make_unique<int>(13));
-  EXPECT_THAT(status_or, IsOkAndHolds(AllOf(
-                             Field(&InPlaceHelper::x, ElementsAre(10, 11, 12)),
-                             Field(&InPlaceHelper::y, Pointee(13)))));
-}
-
-TEST(StatusOr, Emplace) {
-  absl::StatusOr<Foo> status_or_foo(10);
-  status_or_foo.emplace(20);
-  EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20)));
-  status_or_foo = absl::InvalidArgumentError("msg");
-  EXPECT_FALSE(status_or_foo.ok());
-  EXPECT_EQ(status_or_foo.status().code(), absl::StatusCode::kInvalidArgument);
-  EXPECT_EQ(status_or_foo.status().message(), "msg");
-  status_or_foo.emplace(20);
-  EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20)));
-}
-
-TEST(StatusOr, EmplaceInitializerList) {
-  absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12},
-                                          absl::make_unique<int>(13));
-  status_or.emplace({1, 2, 3}, absl::make_unique<int>(4));
-  EXPECT_THAT(status_or,
-              IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)),
-                                 Field(&InPlaceHelper::y, Pointee(4)))));
-  status_or = absl::InvalidArgumentError("msg");
-  EXPECT_FALSE(status_or.ok());
-  EXPECT_EQ(status_or.status().code(), absl::StatusCode::kInvalidArgument);
-  EXPECT_EQ(status_or.status().message(), "msg");
-  status_or.emplace({1, 2, 3}, absl::make_unique<int>(4));
-  EXPECT_THAT(status_or,
-              IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)),
-                                 Field(&InPlaceHelper::y, Pointee(4)))));
-}
-
-TEST(StatusOr, TestCopyCtorStatusOk) {
-  const int kI = 4;
-  const absl::StatusOr<int> original(kI);
-  const absl::StatusOr<int> copy(original);
-  EXPECT_OK(copy.status());
-  EXPECT_EQ(*original, *copy);
-}
-
-TEST(StatusOr, TestCopyCtorStatusNotOk) {
-  absl::StatusOr<int> original(absl::CancelledError());
-  absl::StatusOr<int> copy(original);
-  EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled);
-}
-
-TEST(StatusOr, TestCopyCtorNonAssignable) {
-  const int kI = 4;
-  CopyNoAssign value(kI);
-  absl::StatusOr<CopyNoAssign> original(value);
-  absl::StatusOr<CopyNoAssign> copy(original);
-  EXPECT_OK(copy.status());
-  EXPECT_EQ(original->foo, copy->foo);
-}
-
-TEST(StatusOr, TestCopyCtorStatusOKConverting) {
-  const int kI = 4;
-  absl::StatusOr<int> original(kI);
-  absl::StatusOr<double> copy(original);
-  EXPECT_OK(copy.status());
-  EXPECT_DOUBLE_EQ(*original, *copy);
-}
-
-TEST(StatusOr, TestCopyCtorStatusNotOkConverting) {
-  absl::StatusOr<int> original(absl::CancelledError());
-  absl::StatusOr<double> copy(original);
-  EXPECT_EQ(copy.status(), original.status());
-}
-
-TEST(StatusOr, TestAssignmentStatusOk) {
-  // Copy assignmment
-  {
-    const auto p = std::make_shared<int>(17);
-    absl::StatusOr<std::shared_ptr<int>> source(p);
-
-    absl::StatusOr<std::shared_ptr<int>> target;
-    target = source;
-
-    ASSERT_TRUE(target.ok());
-    EXPECT_OK(target.status());
-    EXPECT_EQ(p, *target);
-
-    ASSERT_TRUE(source.ok());
-    EXPECT_OK(source.status());
-    EXPECT_EQ(p, *source);
-  }
-
-  // Move asssignment
-  {
-    const auto p = std::make_shared<int>(17);
-    absl::StatusOr<std::shared_ptr<int>> source(p);
-
-    absl::StatusOr<std::shared_ptr<int>> target;
-    target = std::move(source);
-
-    ASSERT_TRUE(target.ok());
-    EXPECT_OK(target.status());
-    EXPECT_EQ(p, *target);
-
-    ASSERT_TRUE(source.ok());
-    EXPECT_OK(source.status());
-    EXPECT_EQ(nullptr, *source);
-  }
-}
-
-TEST(StatusOr, TestAssignmentStatusNotOk) {
-  // Copy assignment
-  {
-    const absl::Status expected = absl::CancelledError();
-    absl::StatusOr<int> source(expected);
-
-    absl::StatusOr<int> target;
-    target = source;
-
-    EXPECT_FALSE(target.ok());
-    EXPECT_EQ(expected, target.status());
-
-    EXPECT_FALSE(source.ok());
-    EXPECT_EQ(expected, source.status());
-  }
-
-  // Move assignment
-  {
-    const absl::Status expected = absl::CancelledError();
-    absl::StatusOr<int> source(expected);
-
-    absl::StatusOr<int> target;
-    target = std::move(source);
-
-    EXPECT_FALSE(target.ok());
-    EXPECT_EQ(expected, target.status());
-
-    EXPECT_FALSE(source.ok());
-    EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal);
-  }
-}
-
-TEST(StatusOr, TestAssignmentStatusOKConverting) {
-  // Copy assignment
-  {
-    const int kI = 4;
-    absl::StatusOr<int> source(kI);
-
-    absl::StatusOr<double> target;
-    target = source;
-
-    ASSERT_TRUE(target.ok());
-    EXPECT_OK(target.status());
-    EXPECT_DOUBLE_EQ(kI, *target);
-
-    ASSERT_TRUE(source.ok());
-    EXPECT_OK(source.status());
-    EXPECT_DOUBLE_EQ(kI, *source);
-  }
-
-  // Move assignment
-  {
-    const auto p = new int(17);
-    absl::StatusOr<std::unique_ptr<int>> source(absl::WrapUnique(p));
-
-    absl::StatusOr<std::shared_ptr<int>> target;
-    target = std::move(source);
-
-    ASSERT_TRUE(target.ok());
-    EXPECT_OK(target.status());
-    EXPECT_EQ(p, target->get());
-
-    ASSERT_TRUE(source.ok());
-    EXPECT_OK(source.status());
-    EXPECT_EQ(nullptr, source->get());
-  }
-}
-
-struct A {
-  int x;
-};
-
-struct ImplicitConstructibleFromA {
-  int x;
-  bool moved;
-  ImplicitConstructibleFromA(const A& a)  // NOLINT
-      : x(a.x), moved(false) {}
-  ImplicitConstructibleFromA(A&& a)  // NOLINT
-      : x(a.x), moved(true) {}
-};
-
-TEST(StatusOr, ImplicitConvertingConstructor) {
-  EXPECT_THAT(
-      absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>(
-          absl::StatusOr<A>(A{11})),
-      IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 11),
-                         Field(&ImplicitConstructibleFromA::moved, true))));
-  absl::StatusOr<A> a(A{12});
-  EXPECT_THAT(
-      absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>(a),
-      IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 12),
-                         Field(&ImplicitConstructibleFromA::moved, false))));
-}
-
-struct ExplicitConstructibleFromA {
-  int x;
-  bool moved;
-  explicit ExplicitConstructibleFromA(const A& a) : x(a.x), moved(false) {}
-  explicit ExplicitConstructibleFromA(A&& a) : x(a.x), moved(true) {}
-};
-
-TEST(StatusOr, ExplicitConvertingConstructor) {
-  EXPECT_FALSE(
-      (std::is_convertible<const absl::StatusOr<A>&,
-                           absl::StatusOr<ExplicitConstructibleFromA>>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::StatusOr<A>&&,
-                           absl::StatusOr<ExplicitConstructibleFromA>>::value));
-  EXPECT_THAT(
-      absl::StatusOr<ExplicitConstructibleFromA>(absl::StatusOr<A>(A{11})),
-      IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 11),
-                         Field(&ExplicitConstructibleFromA::moved, true))));
-  absl::StatusOr<A> a(A{12});
-  EXPECT_THAT(
-      absl::StatusOr<ExplicitConstructibleFromA>(a),
-      IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 12),
-                         Field(&ExplicitConstructibleFromA::moved, false))));
-}
-
-struct ImplicitConstructibleFromBool {
-  ImplicitConstructibleFromBool(bool y) : x(y) {}  // NOLINT
-  bool x = false;
-};
-
-struct ConvertibleToBool {
-  explicit ConvertibleToBool(bool y) : x(y) {}
-  operator bool() const { return x; }  // NOLINT
-  bool x = false;
-};
-
-TEST(StatusOr, ImplicitBooleanConstructionWithImplicitCasts) {
-  EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)),
-              IsOkAndHolds(true));
-  EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)),
-              IsOkAndHolds(false));
-  EXPECT_THAT(
-      absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromBool>>(
-          absl::StatusOr<bool>(false)),
-      IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false)));
-  EXPECT_FALSE((std::is_convertible<
-                absl::StatusOr<ConvertibleToBool>,
-                absl::StatusOr<ImplicitConstructibleFromBool>>::value));
-}
-
-TEST(StatusOr, BooleanConstructionWithImplicitCasts) {
-  EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)),
-              IsOkAndHolds(true));
-  EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)),
-              IsOkAndHolds(false));
-  EXPECT_THAT(
-      absl::StatusOr<ImplicitConstructibleFromBool>{
-          absl::StatusOr<bool>(false)},
-      IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false)));
-  EXPECT_THAT(
-      absl::StatusOr<ImplicitConstructibleFromBool>{
-          absl::StatusOr<bool>(absl::InvalidArgumentError(""))},
-      Not(IsOk()));
-
-  EXPECT_THAT(
-      absl::StatusOr<ImplicitConstructibleFromBool>{
-          absl::StatusOr<ConvertibleToBool>(ConvertibleToBool{false})},
-      IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false)));
-  EXPECT_THAT(
-      absl::StatusOr<ImplicitConstructibleFromBool>{
-          absl::StatusOr<ConvertibleToBool>(absl::InvalidArgumentError(""))},
-      Not(IsOk()));
-}
-
-TEST(StatusOr, ConstImplicitCast) {
-  EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>(
-                  absl::StatusOr<const bool>(true)),
-              IsOkAndHolds(true));
-  EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>(
-                  absl::StatusOr<const bool>(false)),
-              IsOkAndHolds(false));
-  EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>(
-                  absl::StatusOr<bool>(true)),
-              IsOkAndHolds(true));
-  EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>(
-                  absl::StatusOr<bool>(false)),
-              IsOkAndHolds(false));
-  EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const std::string>>(
-                  absl::StatusOr<std::string>("foo")),
-              IsOkAndHolds("foo"));
-  EXPECT_THAT(absl::implicit_cast<absl::StatusOr<std::string>>(
-                  absl::StatusOr<const std::string>("foo")),
-              IsOkAndHolds("foo"));
-  EXPECT_THAT(
-      absl::implicit_cast<absl::StatusOr<std::shared_ptr<const std::string>>>(
-          absl::StatusOr<std::shared_ptr<std::string>>(
-              std::make_shared<std::string>("foo"))),
-      IsOkAndHolds(Pointee(std::string("foo"))));
-}
-
-TEST(StatusOr, ConstExplicitConstruction) {
-  EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(true)),
-              IsOkAndHolds(true));
-  EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(false)),
-              IsOkAndHolds(false));
-  EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(true)),
-              IsOkAndHolds(true));
-  EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(false)),
-              IsOkAndHolds(false));
-}
-
-struct ExplicitConstructibleFromInt {
-  int x;
-  explicit ExplicitConstructibleFromInt(int y) : x(y) {}
-};
-
-TEST(StatusOr, ExplicitConstruction) {
-  EXPECT_THAT(absl::StatusOr<ExplicitConstructibleFromInt>(10),
-              IsOkAndHolds(Field(&ExplicitConstructibleFromInt::x, 10)));
-}
-
-TEST(StatusOr, ImplicitConstruction) {
-  // Check implicit casting works.
-  auto status_or =
-      absl::implicit_cast<absl::StatusOr<absl::variant<int, std::string>>>(10);
-  EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10)));
-}
-
-TEST(StatusOr, ImplicitConstructionFromInitliazerList) {
-  // Note: dropping the explicit std::initializer_list<int> is not supported
-  // by absl::StatusOr or absl::optional.
-  auto status_or =
-      absl::implicit_cast<absl::StatusOr<std::vector<int>>>({{10, 20, 30}});
-  EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30)));
-}
-
-TEST(StatusOr, UniquePtrImplicitConstruction) {
-  auto status_or = absl::implicit_cast<absl::StatusOr<std::unique_ptr<Base1>>>(
-      absl::make_unique<Derived>());
-  EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr)));
-}
-
-TEST(StatusOr, NestedStatusOrCopyAndMoveConstructorTests) {
-  absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10);
-  absl::StatusOr<absl::StatusOr<CopyDetector>> status_error =
-      absl::InvalidArgumentError("foo");
-  EXPECT_THAT(status_or,
-              IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false))));
-  absl::StatusOr<absl::StatusOr<CopyDetector>> a = status_or;
-  EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true))));
-  absl::StatusOr<absl::StatusOr<CopyDetector>> a_err = status_error;
-  EXPECT_THAT(a_err, Not(IsOk()));
-
-  const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or;
-  absl::StatusOr<absl::StatusOr<CopyDetector>> b = cref;  // NOLINT
-  EXPECT_THAT(b, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true))));
-  const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error;
-  absl::StatusOr<absl::StatusOr<CopyDetector>> b_err = cref_err;  // NOLINT
-  EXPECT_THAT(b_err, Not(IsOk()));
-
-  absl::StatusOr<absl::StatusOr<CopyDetector>> c = std::move(status_or);
-  EXPECT_THAT(c, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false))));
-  absl::StatusOr<absl::StatusOr<CopyDetector>> c_err = std::move(status_error);
-  EXPECT_THAT(c_err, Not(IsOk()));
-}
-
-TEST(StatusOr, NestedStatusOrCopyAndMoveAssignment) {
-  absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10);
-  absl::StatusOr<absl::StatusOr<CopyDetector>> status_error =
-      absl::InvalidArgumentError("foo");
-  absl::StatusOr<absl::StatusOr<CopyDetector>> a;
-  a = status_or;
-  EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true))));
-  a = status_error;
-  EXPECT_THAT(a, Not(IsOk()));
-
-  const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or;
-  a = cref;
-  EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true))));
-  const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error;
-  a = cref_err;
-  EXPECT_THAT(a, Not(IsOk()));
-  a = std::move(status_or);
-  EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false))));
-  a = std::move(status_error);
-  EXPECT_THAT(a, Not(IsOk()));
-}
-
-struct Copyable {
-  Copyable() {}
-  Copyable(const Copyable&) {}
-  Copyable& operator=(const Copyable&) { return *this; }
-};
-
-struct MoveOnly {
-  MoveOnly() {}
-  MoveOnly(MoveOnly&&) {}
-  MoveOnly& operator=(MoveOnly&&) { return *this; }
-};
-
-struct NonMovable {
-  NonMovable() {}
-  NonMovable(const NonMovable&) = delete;
-  NonMovable(NonMovable&&) = delete;
-  NonMovable& operator=(const NonMovable&) = delete;
-  NonMovable& operator=(NonMovable&&) = delete;
-};
-
-TEST(StatusOr, CopyAndMoveAbility) {
-  EXPECT_TRUE(std::is_copy_constructible<Copyable>::value);
-  EXPECT_TRUE(std::is_copy_assignable<Copyable>::value);
-  EXPECT_TRUE(std::is_move_constructible<Copyable>::value);
-  EXPECT_TRUE(std::is_move_assignable<Copyable>::value);
-  EXPECT_FALSE(std::is_copy_constructible<MoveOnly>::value);
-  EXPECT_FALSE(std::is_copy_assignable<MoveOnly>::value);
-  EXPECT_TRUE(std::is_move_constructible<MoveOnly>::value);
-  EXPECT_TRUE(std::is_move_assignable<MoveOnly>::value);
-  EXPECT_FALSE(std::is_copy_constructible<NonMovable>::value);
-  EXPECT_FALSE(std::is_copy_assignable<NonMovable>::value);
-  EXPECT_FALSE(std::is_move_constructible<NonMovable>::value);
-  EXPECT_FALSE(std::is_move_assignable<NonMovable>::value);
-}
-
-TEST(StatusOr, StatusOrAnyCopyAndMoveConstructorTests) {
-  absl::StatusOr<absl::any> status_or = CopyDetector(10);
-  absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo");
-  EXPECT_THAT(
-      status_or,
-      IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false))));
-  absl::StatusOr<absl::any> a = status_or;
-  EXPECT_THAT(
-      a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true))));
-  absl::StatusOr<absl::any> a_err = status_error;
-  EXPECT_THAT(a_err, Not(IsOk()));
-
-  const absl::StatusOr<absl::any>& cref = status_or;
-  // No lint for no-change copy.
-  absl::StatusOr<absl::any> b = cref;  // NOLINT
-  EXPECT_THAT(
-      b, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true))));
-  const absl::StatusOr<absl::any>& cref_err = status_error;
-  // No lint for no-change copy.
-  absl::StatusOr<absl::any> b_err = cref_err;  // NOLINT
-  EXPECT_THAT(b_err, Not(IsOk()));
-
-  absl::StatusOr<absl::any> c = std::move(status_or);
-  EXPECT_THAT(
-      c, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false))));
-  absl::StatusOr<absl::any> c_err = std::move(status_error);
-  EXPECT_THAT(c_err, Not(IsOk()));
-}
-
-TEST(StatusOr, StatusOrAnyCopyAndMoveAssignment) {
-  absl::StatusOr<absl::any> status_or = CopyDetector(10);
-  absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo");
-  absl::StatusOr<absl::any> a;
-  a = status_or;
-  EXPECT_THAT(
-      a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true))));
-  a = status_error;
-  EXPECT_THAT(a, Not(IsOk()));
-
-  const absl::StatusOr<absl::any>& cref = status_or;
-  a = cref;
-  EXPECT_THAT(
-      a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true))));
-  const absl::StatusOr<absl::any>& cref_err = status_error;
-  a = cref_err;
-  EXPECT_THAT(a, Not(IsOk()));
-  a = std::move(status_or);
-  EXPECT_THAT(
-      a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false))));
-  a = std::move(status_error);
-  EXPECT_THAT(a, Not(IsOk()));
-}
-
-TEST(StatusOr, StatusOrCopyAndMoveTestsConstructor) {
-  absl::StatusOr<CopyDetector> status_or(10);
-  ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false)));
-  absl::StatusOr<CopyDetector> a(status_or);
-  EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true)));
-  const absl::StatusOr<CopyDetector>& cref = status_or;
-  absl::StatusOr<CopyDetector> b(cref);  // NOLINT
-  EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true)));
-  absl::StatusOr<CopyDetector> c(std::move(status_or));
-  EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false)));
-}
-
-TEST(StatusOr, StatusOrCopyAndMoveTestsAssignment) {
-  absl::StatusOr<CopyDetector> status_or(10);
-  ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false)));
-  absl::StatusOr<CopyDetector> a;
-  a = status_or;
-  EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true)));
-  const absl::StatusOr<CopyDetector>& cref = status_or;
-  absl::StatusOr<CopyDetector> b;
-  b = cref;
-  EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true)));
-  absl::StatusOr<CopyDetector> c;
-  c = std::move(status_or);
-  EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false)));
-}
-
-TEST(StatusOr, AbslAnyAssignment) {
-  EXPECT_FALSE((std::is_assignable<absl::StatusOr<absl::any>,
-                                   absl::StatusOr<int>>::value));
-  absl::StatusOr<absl::any> status_or;
-  status_or = absl::InvalidArgumentError("foo");
-  EXPECT_THAT(status_or, Not(IsOk()));
-}
-
-TEST(StatusOr, ImplicitAssignment) {
-  absl::StatusOr<absl::variant<int, std::string>> status_or;
-  status_or = 10;
-  EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10)));
-}
-
-TEST(StatusOr, SelfDirectInitAssignment) {
-  absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}};
-  status_or = *status_or;
-  EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30)));
-}
-
-TEST(StatusOr, ImplicitCastFromInitializerList) {
-  absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}};
-  EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30)));
-}
-
-TEST(StatusOr, UniquePtrImplicitAssignment) {
-  absl::StatusOr<std::unique_ptr<Base1>> status_or;
-  status_or = absl::make_unique<Derived>();
-  EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr)));
-}
-
-TEST(StatusOr, Pointer) {
-  struct A {};
-  struct B : public A {};
-  struct C : private A {};
-
-  EXPECT_TRUE((std::is_constructible<absl::StatusOr<A*>, B*>::value));
-  EXPECT_TRUE((std::is_convertible<B*, absl::StatusOr<A*>>::value));
-  EXPECT_FALSE((std::is_constructible<absl::StatusOr<A*>, C*>::value));
-  EXPECT_FALSE((std::is_convertible<C*, absl::StatusOr<A*>>::value));
-}
-
-TEST(StatusOr, TestAssignmentStatusNotOkConverting) {
-  // Copy assignment
-  {
-    const absl::Status expected = absl::CancelledError();
-    absl::StatusOr<int> source(expected);
-
-    absl::StatusOr<double> target;
-    target = source;
-
-    EXPECT_FALSE(target.ok());
-    EXPECT_EQ(expected, target.status());
-
-    EXPECT_FALSE(source.ok());
-    EXPECT_EQ(expected, source.status());
-  }
-
-  // Move assignment
-  {
-    const absl::Status expected = absl::CancelledError();
-    absl::StatusOr<int> source(expected);
-
-    absl::StatusOr<double> target;
-    target = std::move(source);
-
-    EXPECT_FALSE(target.ok());
-    EXPECT_EQ(expected, target.status());
-
-    EXPECT_FALSE(source.ok());
-    EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal);
-  }
-}
-
-TEST(StatusOr, SelfAssignment) {
-  // Copy-assignment, status OK
-  {
-    // A string long enough that it's likely to defeat any inline representation
-    // optimization.
-    const std::string long_str(128, 'a');
-
-    absl::StatusOr<std::string> so = long_str;
-    so = *&so;
-
-    ASSERT_TRUE(so.ok());
-    EXPECT_OK(so.status());
-    EXPECT_EQ(long_str, *so);
-  }
-
-  // Copy-assignment, error status
-  {
-    absl::StatusOr<int> so = absl::NotFoundError("taco");
-    so = *&so;
-
-    EXPECT_FALSE(so.ok());
-    EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound);
-    EXPECT_EQ(so.status().message(), "taco");
-  }
-
-  // Move-assignment with copyable type, status OK
-  {
-    absl::StatusOr<int> so = 17;
-
-    // Fool the compiler, which otherwise complains.
-    auto& same = so;
-    so = std::move(same);
-
-    ASSERT_TRUE(so.ok());
-    EXPECT_OK(so.status());
-    EXPECT_EQ(17, *so);
-  }
-
-  // Move-assignment with copyable type, error status
-  {
-    absl::StatusOr<int> so = absl::NotFoundError("taco");
-
-    // Fool the compiler, which otherwise complains.
-    auto& same = so;
-    so = std::move(same);
-
-    EXPECT_FALSE(so.ok());
-    EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound);
-    EXPECT_EQ(so.status().message(), "taco");
-  }
-
-  // Move-assignment with non-copyable type, status OK
-  {
-    const auto raw = new int(17);
-    absl::StatusOr<std::unique_ptr<int>> so = absl::WrapUnique(raw);
-
-    // Fool the compiler, which otherwise complains.
-    auto& same = so;
-    so = std::move(same);
-
-    ASSERT_TRUE(so.ok());
-    EXPECT_OK(so.status());
-    EXPECT_EQ(raw, so->get());
-  }
-
-  // Move-assignment with non-copyable type, error status
-  {
-    absl::StatusOr<std::unique_ptr<int>> so = absl::NotFoundError("taco");
-
-    // Fool the compiler, which otherwise complains.
-    auto& same = so;
-    so = std::move(same);
-
-    EXPECT_FALSE(so.ok());
-    EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound);
-    EXPECT_EQ(so.status().message(), "taco");
-  }
-}
-
-// These types form the overload sets of the constructors and the assignment
-// operators of `MockValue`. They distinguish construction from assignment,
-// lvalue from rvalue.
-struct FromConstructibleAssignableLvalue {};
-struct FromConstructibleAssignableRvalue {};
-struct FromImplicitConstructibleOnly {};
-struct FromAssignableOnly {};
-
-// This class is for testing the forwarding value assignments of `StatusOr`.
-// `from_rvalue` indicates whether the constructor or the assignment taking
-// rvalue reference is called. `from_assignment` indicates whether any
-// assignment is called.
-struct MockValue {
-  // Constructs `MockValue` from `FromConstructibleAssignableLvalue`.
-  MockValue(const FromConstructibleAssignableLvalue&)  // NOLINT
-      : from_rvalue(false), assigned(false) {}
-  // Constructs `MockValue` from `FromConstructibleAssignableRvalue`.
-  MockValue(FromConstructibleAssignableRvalue&&)  // NOLINT
-      : from_rvalue(true), assigned(false) {}
-  // Constructs `MockValue` from `FromImplicitConstructibleOnly`.
-  // `MockValue` is not assignable from `FromImplicitConstructibleOnly`.
-  MockValue(const FromImplicitConstructibleOnly&)  // NOLINT
-      : from_rvalue(false), assigned(false) {}
-  // Assigns `FromConstructibleAssignableLvalue`.
-  MockValue& operator=(const FromConstructibleAssignableLvalue&) {
-    from_rvalue = false;
-    assigned = true;
-    return *this;
-  }
-  // Assigns `FromConstructibleAssignableRvalue` (rvalue only).
-  MockValue& operator=(FromConstructibleAssignableRvalue&&) {
-    from_rvalue = true;
-    assigned = true;
-    return *this;
-  }
-  // Assigns `FromAssignableOnly`, but not constructible from
-  // `FromAssignableOnly`.
-  MockValue& operator=(const FromAssignableOnly&) {
-    from_rvalue = false;
-    assigned = true;
-    return *this;
-  }
-  bool from_rvalue;
-  bool assigned;
-};
-
-// operator=(U&&)
-TEST(StatusOr, PerfectForwardingAssignment) {
-  // U == T
-  constexpr int kValue1 = 10, kValue2 = 20;
-  absl::StatusOr<CopyDetector> status_or;
-  CopyDetector lvalue(kValue1);
-  status_or = lvalue;
-  EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue1, false, true)));
-  status_or = CopyDetector(kValue2);
-  EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue2, true, false)));
-
-  // U != T
-  EXPECT_TRUE(
-      (std::is_assignable<absl::StatusOr<MockValue>&,
-                          const FromConstructibleAssignableLvalue&>::value));
-  EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&,
-                                  FromConstructibleAssignableLvalue&&>::value));
-  EXPECT_FALSE(
-      (std::is_assignable<absl::StatusOr<MockValue>&,
-                          const FromConstructibleAssignableRvalue&>::value));
-  EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&,
-                                  FromConstructibleAssignableRvalue&&>::value));
-  EXPECT_TRUE(
-      (std::is_assignable<absl::StatusOr<MockValue>&,
-                          const FromImplicitConstructibleOnly&>::value));
-  EXPECT_FALSE((std::is_assignable<absl::StatusOr<MockValue>&,
-                                   const FromAssignableOnly&>::value));
-
-  absl::StatusOr<MockValue> from_lvalue(FromConstructibleAssignableLvalue{});
-  EXPECT_FALSE(from_lvalue->from_rvalue);
-  EXPECT_FALSE(from_lvalue->assigned);
-  from_lvalue = FromConstructibleAssignableLvalue{};
-  EXPECT_FALSE(from_lvalue->from_rvalue);
-  EXPECT_TRUE(from_lvalue->assigned);
-
-  absl::StatusOr<MockValue> from_rvalue(FromConstructibleAssignableRvalue{});
-  EXPECT_TRUE(from_rvalue->from_rvalue);
-  EXPECT_FALSE(from_rvalue->assigned);
-  from_rvalue = FromConstructibleAssignableRvalue{};
-  EXPECT_TRUE(from_rvalue->from_rvalue);
-  EXPECT_TRUE(from_rvalue->assigned);
-
-  absl::StatusOr<MockValue> from_implicit_constructible(
-      FromImplicitConstructibleOnly{});
-  EXPECT_FALSE(from_implicit_constructible->from_rvalue);
-  EXPECT_FALSE(from_implicit_constructible->assigned);
-  // construct a temporary `StatusOr` object and invoke the `StatusOr` move
-  // assignment operator.
-  from_implicit_constructible = FromImplicitConstructibleOnly{};
-  EXPECT_FALSE(from_implicit_constructible->from_rvalue);
-  EXPECT_FALSE(from_implicit_constructible->assigned);
-}
-
-TEST(StatusOr, TestStatus) {
-  absl::StatusOr<int> good(4);
-  EXPECT_TRUE(good.ok());
-  absl::StatusOr<int> bad(absl::CancelledError());
-  EXPECT_FALSE(bad.ok());
-  EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled);
-}
-
-TEST(StatusOr, OperatorStarRefQualifiers) {
-  static_assert(
-      std::is_same<const int&,
-                   decltype(*std::declval<const absl::StatusOr<int>&>())>(),
-      "Unexpected ref-qualifiers");
-  static_assert(
-      std::is_same<int&, decltype(*std::declval<absl::StatusOr<int>&>())>(),
-      "Unexpected ref-qualifiers");
-  static_assert(
-      std::is_same<const int&&,
-                   decltype(*std::declval<const absl::StatusOr<int>&&>())>(),
-      "Unexpected ref-qualifiers");
-  static_assert(
-      std::is_same<int&&, decltype(*std::declval<absl::StatusOr<int>&&>())>(),
-      "Unexpected ref-qualifiers");
-}
-
-TEST(StatusOr, OperatorStar) {
-  const absl::StatusOr<std::string> const_lvalue("hello");
-  EXPECT_EQ("hello", *const_lvalue);
-
-  absl::StatusOr<std::string> lvalue("hello");
-  EXPECT_EQ("hello", *lvalue);
-
-  // Note: Recall that std::move() is equivalent to a static_cast to an rvalue
-  // reference type.
-  const absl::StatusOr<std::string> const_rvalue("hello");
-  EXPECT_EQ("hello", *std::move(const_rvalue));  // NOLINT
-
-  absl::StatusOr<std::string> rvalue("hello");
-  EXPECT_EQ("hello", *std::move(rvalue));
-}
-
-TEST(StatusOr, OperatorArrowQualifiers) {
-  static_assert(
-      std::is_same<
-          const int*,
-          decltype(std::declval<const absl::StatusOr<int>&>().operator->())>(),
-      "Unexpected qualifiers");
-  static_assert(
-      std::is_same<
-          int*, decltype(std::declval<absl::StatusOr<int>&>().operator->())>(),
-      "Unexpected qualifiers");
-  static_assert(
-      std::is_same<
-          const int*,
-          decltype(std::declval<const absl::StatusOr<int>&&>().operator->())>(),
-      "Unexpected qualifiers");
-  static_assert(
-      std::is_same<
-          int*, decltype(std::declval<absl::StatusOr<int>&&>().operator->())>(),
-      "Unexpected qualifiers");
-}
-
-TEST(StatusOr, OperatorArrow) {
-  const absl::StatusOr<std::string> const_lvalue("hello");
-  EXPECT_EQ(std::string("hello"), const_lvalue->c_str());
-
-  absl::StatusOr<std::string> lvalue("hello");
-  EXPECT_EQ(std::string("hello"), lvalue->c_str());
-}
-
-TEST(StatusOr, RValueStatus) {
-  absl::StatusOr<int> so(absl::NotFoundError("taco"));
-  const absl::Status s = std::move(so).status();
-
-  EXPECT_EQ(s.code(), absl::StatusCode::kNotFound);
-  EXPECT_EQ(s.message(), "taco");
-
-  // Check that !ok() still implies !status().ok(), even after moving out of the
-  // object. See the note on the rvalue ref-qualified status method.
-  EXPECT_FALSE(so.ok());  // NOLINT
-  EXPECT_FALSE(so.status().ok());
-  EXPECT_EQ(so.status().code(), absl::StatusCode::kInternal);
-  EXPECT_EQ(so.status().message(), "Status accessed after move.");
-}
-
-TEST(StatusOr, TestValue) {
-  const int kI = 4;
-  absl::StatusOr<int> thing(kI);
-  EXPECT_EQ(kI, *thing);
-}
-
-TEST(StatusOr, TestValueConst) {
-  const int kI = 4;
-  const absl::StatusOr<int> thing(kI);
-  EXPECT_EQ(kI, *thing);
-}
-
-TEST(StatusOr, TestPointerDefaultCtor) {
-  absl::StatusOr<int*> thing;
-  EXPECT_FALSE(thing.ok());
-  EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown);
-}
-
-
-
-TEST(StatusOr, TestPointerStatusCtor) {
-  absl::StatusOr<int*> thing(absl::CancelledError());
-  EXPECT_FALSE(thing.ok());
-  EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled);
-}
-
-TEST(StatusOr, TestPointerValueCtor) {
-  const int kI = 4;
-
-  // Construction from a non-null pointer
-  {
-    absl::StatusOr<const int*> so(&kI);
-    EXPECT_TRUE(so.ok());
-    EXPECT_OK(so.status());
-    EXPECT_EQ(&kI, *so);
-  }
-
-  // Construction from a null pointer constant
-  {
-    absl::StatusOr<const int*> so(nullptr);
-    EXPECT_TRUE(so.ok());
-    EXPECT_OK(so.status());
-    EXPECT_EQ(nullptr, *so);
-  }
-
-  // Construction from a non-literal null pointer
-  {
-    const int* const p = nullptr;
-
-    absl::StatusOr<const int*> so(p);
-    EXPECT_TRUE(so.ok());
-    EXPECT_OK(so.status());
-    EXPECT_EQ(nullptr, *so);
-  }
-}
-
-TEST(StatusOr, TestPointerCopyCtorStatusOk) {
-  const int kI = 0;
-  absl::StatusOr<const int*> original(&kI);
-  absl::StatusOr<const int*> copy(original);
-  EXPECT_OK(copy.status());
-  EXPECT_EQ(*original, *copy);
-}
-
-TEST(StatusOr, TestPointerCopyCtorStatusNotOk) {
-  absl::StatusOr<int*> original(absl::CancelledError());
-  absl::StatusOr<int*> copy(original);
-  EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled);
-}
-
-TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) {
-  Derived derived;
-  absl::StatusOr<Derived*> original(&derived);
-  absl::StatusOr<Base2*> copy(original);
-  EXPECT_OK(copy.status());
-  EXPECT_EQ(static_cast<const Base2*>(*original), *copy);
-}
-
-TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) {
-  absl::StatusOr<Derived*> original(absl::CancelledError());
-  absl::StatusOr<Base2*> copy(original);
-  EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled);
-}
-
-TEST(StatusOr, TestPointerAssignmentStatusOk) {
-  const int kI = 0;
-  absl::StatusOr<const int*> source(&kI);
-  absl::StatusOr<const int*> target;
-  target = source;
-  EXPECT_OK(target.status());
-  EXPECT_EQ(*source, *target);
-}
-
-TEST(StatusOr, TestPointerAssignmentStatusNotOk) {
-  absl::StatusOr<int*> source(absl::CancelledError());
-  absl::StatusOr<int*> target;
-  target = source;
-  EXPECT_EQ(target.status().code(), absl::StatusCode::kCancelled);
-}
-
-TEST(StatusOr, TestPointerAssignmentStatusOKConverting) {
-  Derived derived;
-  absl::StatusOr<Derived*> source(&derived);
-  absl::StatusOr<Base2*> target;
-  target = source;
-  EXPECT_OK(target.status());
-  EXPECT_EQ(static_cast<const Base2*>(*source), *target);
-}
-
-TEST(StatusOr, TestPointerAssignmentStatusNotOkConverting) {
-  absl::StatusOr<Derived*> source(absl::CancelledError());
-  absl::StatusOr<Base2*> target;
-  target = source;
-  EXPECT_EQ(target.status(), source.status());
-}
-
-TEST(StatusOr, TestPointerStatus) {
-  const int kI = 0;
-  absl::StatusOr<const int*> good(&kI);
-  EXPECT_TRUE(good.ok());
-  absl::StatusOr<const int*> bad(absl::CancelledError());
-  EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled);
-}
-
-TEST(StatusOr, TestPointerValue) {
-  const int kI = 0;
-  absl::StatusOr<const int*> thing(&kI);
-  EXPECT_EQ(&kI, *thing);
-}
-
-TEST(StatusOr, TestPointerValueConst) {
-  const int kI = 0;
-  const absl::StatusOr<const int*> thing(&kI);
-  EXPECT_EQ(&kI, *thing);
-}
-
-TEST(StatusOr, StatusOrVectorOfUniquePointerCanReserveAndResize) {
-  using EvilType = std::vector<std::unique_ptr<int>>;
-  static_assert(std::is_copy_constructible<EvilType>::value, "");
-  std::vector<::absl::StatusOr<EvilType>> v(5);
-  v.reserve(v.capacity() + 10);
-  v.resize(v.capacity() + 10);
-}
-
-TEST(StatusOr, ConstPayload) {
-  // A reduced version of a problematic type found in the wild. All of the
-  // operations below should compile.
-  absl::StatusOr<const int> a;
-
-  // Copy-construction
-  absl::StatusOr<const int> b(a);
-
-  // Copy-assignment
-  EXPECT_FALSE(std::is_copy_assignable<absl::StatusOr<const int>>::value);
-
-  // Move-construction
-  absl::StatusOr<const int> c(std::move(a));
-
-  // Move-assignment
-  EXPECT_FALSE(std::is_move_assignable<absl::StatusOr<const int>>::value);
-}
-
-TEST(StatusOr, MapToStatusOrUniquePtr) {
-  // A reduced version of a problematic type found in the wild. All of the
-  // operations below should compile.
-  using MapType = std::map<std::string, absl::StatusOr<std::unique_ptr<int>>>;
-
-  MapType a;
-
-  // Move-construction
-  MapType b(std::move(a));
-
-  // Move-assignment
-  a = std::move(b);
-}
-
-TEST(StatusOr, ValueOrOk) {
-  const absl::StatusOr<int> status_or = 0;
-  EXPECT_EQ(status_or.value_or(-1), 0);
-}
-
-TEST(StatusOr, ValueOrDefault) {
-  const absl::StatusOr<int> status_or = absl::CancelledError();
-  EXPECT_EQ(status_or.value_or(-1), -1);
-}
-
-TEST(StatusOr, MoveOnlyValueOrOk) {
-  EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(0))
-                  .value_or(absl::make_unique<int>(-1)),
-              Pointee(0));
-}
-
-TEST(StatusOr, MoveOnlyValueOrDefault) {
-  EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::CancelledError())
-                  .value_or(absl::make_unique<int>(-1)),
-              Pointee(-1));
-}
-
-static absl::StatusOr<int> MakeStatus() { return 100; }
-
-TEST(StatusOr, TestIgnoreError) { MakeStatus().IgnoreError(); }
-
-TEST(StatusOr, EqualityOperator) {
-  constexpr int kNumCases = 4;
-  std::array<absl::StatusOr<int>, kNumCases> group1 = {
-      absl::StatusOr<int>(1), absl::StatusOr<int>(2),
-      absl::StatusOr<int>(absl::InvalidArgumentError("msg")),
-      absl::StatusOr<int>(absl::InternalError("msg"))};
-  std::array<absl::StatusOr<int>, kNumCases> group2 = {
-      absl::StatusOr<int>(1), absl::StatusOr<int>(2),
-      absl::StatusOr<int>(absl::InvalidArgumentError("msg")),
-      absl::StatusOr<int>(absl::InternalError("msg"))};
-  for (int i = 0; i < kNumCases; ++i) {
-    for (int j = 0; j < kNumCases; ++j) {
-      if (i == j) {
-        EXPECT_TRUE(group1[i] == group2[j]);
-        EXPECT_FALSE(group1[i] != group2[j]);
-      } else {
-        EXPECT_FALSE(group1[i] == group2[j]);
-        EXPECT_TRUE(group1[i] != group2[j]);
-      }
-    }
-  }
-}
-
-struct MyType {
-  bool operator==(const MyType&) const { return true; }
-};
-
-enum class ConvTraits { kNone = 0, kImplicit = 1, kExplicit = 2 };
-
-// This class has conversion operator to `StatusOr<T>` based on value of
-// `conv_traits`.
-template <typename T, ConvTraits conv_traits = ConvTraits::kNone>
-struct StatusOrConversionBase {};
-
-template <typename T>
-struct StatusOrConversionBase<T, ConvTraits::kImplicit> {
-  operator absl::StatusOr<T>() const& {  // NOLINT
-    return absl::InvalidArgumentError("conversion to absl::StatusOr");
-  }
-  operator absl::StatusOr<T>() && {  // NOLINT
-    return absl::InvalidArgumentError("conversion to absl::StatusOr");
-  }
-};
-
-template <typename T>
-struct StatusOrConversionBase<T, ConvTraits::kExplicit> {
-  explicit operator absl::StatusOr<T>() const& {
-    return absl::InvalidArgumentError("conversion to absl::StatusOr");
-  }
-  explicit operator absl::StatusOr<T>() && {
-    return absl::InvalidArgumentError("conversion to absl::StatusOr");
-  }
-};
-
-// This class has conversion operator to `T` based on the value of
-// `conv_traits`.
-template <typename T, ConvTraits conv_traits = ConvTraits::kNone>
-struct ConversionBase {};
-
-template <typename T>
-struct ConversionBase<T, ConvTraits::kImplicit> {
-  operator T() const& { return t; }         // NOLINT
-  operator T() && { return std::move(t); }  // NOLINT
-  T t;
-};
-
-template <typename T>
-struct ConversionBase<T, ConvTraits::kExplicit> {
-  explicit operator T() const& { return t; }
-  explicit operator T() && { return std::move(t); }
-  T t;
-};
-
-// This class has conversion operator to `absl::Status` based on the value of
-// `conv_traits`.
-template <ConvTraits conv_traits = ConvTraits::kNone>
-struct StatusConversionBase {};
-
-template <>
-struct StatusConversionBase<ConvTraits::kImplicit> {
-  operator absl::Status() const& {  // NOLINT
-    return absl::InternalError("conversion to Status");
-  }
-  operator absl::Status() && {  // NOLINT
-    return absl::InternalError("conversion to Status");
-  }
-};
-
-template <>
-struct StatusConversionBase<ConvTraits::kExplicit> {
-  explicit operator absl::Status() const& {  // NOLINT
-    return absl::InternalError("conversion to Status");
-  }
-  explicit operator absl::Status() && {  // NOLINT
-    return absl::InternalError("conversion to Status");
-  }
-};
-
-static constexpr int kConvToStatus = 1;
-static constexpr int kConvToStatusOr = 2;
-static constexpr int kConvToT = 4;
-static constexpr int kConvExplicit = 8;
-
-constexpr ConvTraits GetConvTraits(int bit, int config) {
-  return (config & bit) == 0
-             ? ConvTraits::kNone
-             : ((config & kConvExplicit) == 0 ? ConvTraits::kImplicit
-                                              : ConvTraits::kExplicit);
-}
-
-// This class conditionally has conversion operator to `absl::Status`, `T`,
-// `StatusOr<T>`, based on values of the template parameters.
-template <typename T, int config>
-struct CustomType
-    : StatusOrConversionBase<T, GetConvTraits(kConvToStatusOr, config)>,
-      ConversionBase<T, GetConvTraits(kConvToT, config)>,
-      StatusConversionBase<GetConvTraits(kConvToStatus, config)> {};
-
-struct ConvertibleToAnyStatusOr {
-  template <typename T>
-  operator absl::StatusOr<T>() const {  // NOLINT
-    return absl::InvalidArgumentError("Conversion to absl::StatusOr");
-  }
-};
-
-// Test the rank of overload resolution for `StatusOr<T>` constructor and
-// assignment, from highest to lowest:
-// 1. T/Status
-// 2. U that has conversion operator to absl::StatusOr<T>
-// 3. U that is convertible to Status
-// 4. U that is convertible to T
-TEST(StatusOr, ConstructionFromT) {
-  // Construct absl::StatusOr<T> from T when T is convertible to
-  // absl::StatusOr<T>
-  {
-    ConvertibleToAnyStatusOr v;
-    absl::StatusOr<ConvertibleToAnyStatusOr> statusor(v);
-    EXPECT_TRUE(statusor.ok());
-  }
-  {
-    ConvertibleToAnyStatusOr v;
-    absl::StatusOr<ConvertibleToAnyStatusOr> statusor = v;
-    EXPECT_TRUE(statusor.ok());
-  }
-  // Construct absl::StatusOr<T> from T when T is explicitly convertible to
-  // Status
-  {
-    CustomType<MyType, kConvToStatus | kConvExplicit> v;
-    absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor(
-        v);
-    EXPECT_TRUE(statusor.ok());
-  }
-  {
-    CustomType<MyType, kConvToStatus | kConvExplicit> v;
-    absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor =
-        v;
-    EXPECT_TRUE(statusor.ok());
-  }
-}
-
-// Construct absl::StatusOr<T> from U when U is explicitly convertible to T
-TEST(StatusOr, ConstructionFromTypeConvertibleToT) {
-  {
-    CustomType<MyType, kConvToT | kConvExplicit> v;
-    absl::StatusOr<MyType> statusor(v);
-    EXPECT_TRUE(statusor.ok());
-  }
-  {
-    CustomType<MyType, kConvToT> v;
-    absl::StatusOr<MyType> statusor = v;
-    EXPECT_TRUE(statusor.ok());
-  }
-}
-
-// Construct absl::StatusOr<T> from U when U has explicit conversion operator to
-// absl::StatusOr<T>
-TEST(StatusOr, ConstructionFromTypeWithConversionOperatorToStatusOrT) {
-  {
-    CustomType<MyType, kConvToStatusOr | kConvExplicit> v;
-    absl::StatusOr<MyType> statusor(v);
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType, kConvToT | kConvToStatusOr | kConvExplicit> v;
-    absl::StatusOr<MyType> statusor(v);
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType, kConvToStatusOr | kConvToStatus | kConvExplicit> v;
-    absl::StatusOr<MyType> statusor(v);
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType,
-               kConvToT | kConvToStatusOr | kConvToStatus | kConvExplicit>
-        v;
-    absl::StatusOr<MyType> statusor(v);
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType, kConvToStatusOr> v;
-    absl::StatusOr<MyType> statusor = v;
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType, kConvToT | kConvToStatusOr> v;
-    absl::StatusOr<MyType> statusor = v;
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType, kConvToStatusOr | kConvToStatus> v;
-    absl::StatusOr<MyType> statusor = v;
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v;
-    absl::StatusOr<MyType> statusor = v;
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-}
-
-TEST(StatusOr, ConstructionFromTypeConvertibleToStatus) {
-  // Construction fails because conversion to `Status` is explicit.
-  {
-    CustomType<MyType, kConvToStatus | kConvExplicit> v;
-    absl::StatusOr<MyType> statusor(v);
-    EXPECT_FALSE(statusor.ok());
-    EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
-  }
-  {
-    CustomType<MyType, kConvToT | kConvToStatus | kConvExplicit> v;
-    absl::StatusOr<MyType> statusor(v);
-    EXPECT_FALSE(statusor.ok());
-    EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
-  }
-  {
-    CustomType<MyType, kConvToStatus> v;
-    absl::StatusOr<MyType> statusor = v;
-    EXPECT_FALSE(statusor.ok());
-    EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
-  }
-  {
-    CustomType<MyType, kConvToT | kConvToStatus> v;
-    absl::StatusOr<MyType> statusor = v;
-    EXPECT_FALSE(statusor.ok());
-    EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
-  }
-}
-
-TEST(StatusOr, AssignmentFromT) {
-  // Assign to absl::StatusOr<T> from T when T is convertible to
-  // absl::StatusOr<T>
-  {
-    ConvertibleToAnyStatusOr v;
-    absl::StatusOr<ConvertibleToAnyStatusOr> statusor;
-    statusor = v;
-    EXPECT_TRUE(statusor.ok());
-  }
-  // Assign to absl::StatusOr<T> from T when T is convertible to Status
-  {
-    CustomType<MyType, kConvToStatus> v;
-    absl::StatusOr<CustomType<MyType, kConvToStatus>> statusor;
-    statusor = v;
-    EXPECT_TRUE(statusor.ok());
-  }
-}
-
-TEST(StatusOr, AssignmentFromTypeConvertibleToT) {
-  // Assign to absl::StatusOr<T> from U when U is convertible to T
-  {
-    CustomType<MyType, kConvToT> v;
-    absl::StatusOr<MyType> statusor;
-    statusor = v;
-    EXPECT_TRUE(statusor.ok());
-  }
-}
-
-TEST(StatusOr, AssignmentFromTypeWithConversionOperatortoStatusOrT) {
-  // Assign to absl::StatusOr<T> from U when U has conversion operator to
-  // absl::StatusOr<T>
-  {
-    CustomType<MyType, kConvToStatusOr> v;
-    absl::StatusOr<MyType> statusor;
-    statusor = v;
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType, kConvToT | kConvToStatusOr> v;
-    absl::StatusOr<MyType> statusor;
-    statusor = v;
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType, kConvToStatusOr | kConvToStatus> v;
-    absl::StatusOr<MyType> statusor;
-    statusor = v;
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-  {
-    CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v;
-    absl::StatusOr<MyType> statusor;
-    statusor = v;
-    EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
-  }
-}
-
-TEST(StatusOr, AssignmentFromTypeConvertibleToStatus) {
-  // Assign to absl::StatusOr<T> from U when U is convertible to Status
-  {
-    CustomType<MyType, kConvToStatus> v;
-    absl::StatusOr<MyType> statusor;
-    statusor = v;
-    EXPECT_FALSE(statusor.ok());
-    EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
-  }
-  {
-    CustomType<MyType, kConvToT | kConvToStatus> v;
-    absl::StatusOr<MyType> statusor;
-    statusor = v;
-    EXPECT_FALSE(statusor.ok());
-    EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/BUILD.bazel b/third_party/abseil_cpp/absl/strings/BUILD.bazel
deleted file mode 100644
index 30a8dd28b2..0000000000
--- a/third_party/abseil_cpp/absl/strings/BUILD.bazel
+++ /dev/null
@@ -1,788 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(
-    default_visibility = ["//visibility:public"],
-    features = ["parse_headers"],
-)
-
-licenses(["notice"])
-
-cc_library(
-    name = "strings",
-    srcs = [
-        "ascii.cc",
-        "charconv.cc",
-        "escaping.cc",
-        "internal/charconv_bigint.cc",
-        "internal/charconv_bigint.h",
-        "internal/charconv_parse.cc",
-        "internal/charconv_parse.h",
-        "internal/memutil.cc",
-        "internal/memutil.h",
-        "internal/stl_type_traits.h",
-        "internal/str_join_internal.h",
-        "internal/str_split_internal.h",
-        "match.cc",
-        "numbers.cc",
-        "str_cat.cc",
-        "str_replace.cc",
-        "str_split.cc",
-        "string_view.cc",
-        "substitute.cc",
-    ],
-    hdrs = [
-        "ascii.h",
-        "charconv.h",
-        "escaping.h",
-        "internal/string_constant.h",
-        "match.h",
-        "numbers.h",
-        "str_cat.h",
-        "str_join.h",
-        "str_replace.h",
-        "str_split.h",
-        "string_view.h",
-        "strip.h",
-        "substitute.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    deps = [
-        ":internal",
-        "//absl/base",
-        "//absl/base:bits",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:endian",
-        "//absl/base:raw_logging_internal",
-        "//absl/base:throw_delegate",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/numeric:int128",
-    ],
-)
-
-cc_library(
-    name = "internal",
-    srcs = [
-        "internal/escaping.cc",
-        "internal/ostringstream.cc",
-        "internal/utf8.cc",
-    ],
-    hdrs = [
-        "internal/char_map.h",
-        "internal/escaping.h",
-        "internal/ostringstream.h",
-        "internal/resize_uninitialized.h",
-        "internal/utf8.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:endian",
-        "//absl/base:raw_logging_internal",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_test(
-    name = "match_test",
-    size = "small",
-    srcs = ["match_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "escaping_test",
-    size = "small",
-    srcs = [
-        "escaping_test.cc",
-        "internal/escaping_test_common.h",
-    ],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":cord",
-        ":strings",
-        "//absl/base:core_headers",
-        "//absl/container:fixed_array",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "escaping_benchmark",
-    srcs = [
-        "escaping_benchmark.cc",
-        "internal/escaping_test_common.h",
-    ],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:raw_logging_internal",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "ascii_test",
-    size = "small",
-    srcs = ["ascii_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "ascii_benchmark",
-    srcs = ["ascii_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "memutil_benchmark",
-    srcs = [
-        "internal/memutil.h",
-        "internal/memutil_benchmark.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:core_headers",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "memutil_test",
-    size = "small",
-    srcs = [
-        "internal/memutil.h",
-        "internal/memutil_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "utf8_test",
-    size = "small",
-    srcs = [
-        "internal/utf8_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":internal",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "string_constant_test",
-    size = "small",
-    srcs = ["internal/string_constant_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/meta:type_traits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "string_view_benchmark",
-    srcs = ["string_view_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "string_view_test",
-    size = "small",
-    srcs = ["string_view_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:dynamic_annotations",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "cord_internal",
-    hdrs = ["internal/cord_internal.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:base_internal",
-        "//absl/container:compressed_tuple",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_library(
-    name = "cord",
-    srcs = [
-        "cord.cc",
-    ],
-    hdrs = [
-        "cord.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    deps = [
-        ":cord_internal",
-        ":internal",
-        ":str_format",
-        ":strings",
-        "//absl/base",
-        "//absl/base:core_headers",
-        "//absl/base:endian",
-        "//absl/base:raw_logging_internal",
-        "//absl/container:fixed_array",
-        "//absl/container:inlined_vector",
-        "//absl/functional:function_ref",
-        "//absl/meta:type_traits",
-        "//absl/types:optional",
-    ],
-)
-
-cc_library(
-    name = "cord_test_helpers",
-    testonly = 1,
-    hdrs = [
-        "cord_test_helpers.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    deps = [
-        ":cord",
-    ],
-)
-
-cc_test(
-    name = "cord_test",
-    size = "medium",
-    srcs = ["cord_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":cord",
-        ":cord_test_helpers",
-        ":str_format",
-        ":strings",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:endian",
-        "//absl/base:raw_logging_internal",
-        "//absl/container:fixed_array",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "substitute_test",
-    size = "small",
-    srcs = ["substitute_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_replace_benchmark",
-    srcs = ["str_replace_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:raw_logging_internal",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "str_replace_test",
-    size = "small",
-    srcs = ["str_replace_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_split_test",
-    srcs = ["str_split_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:core_headers",
-        "//absl/base:dynamic_annotations",
-        "//absl/container:flat_hash_map",
-        "//absl/container:node_hash_map",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_split_benchmark",
-    srcs = ["str_split_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:raw_logging_internal",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "ostringstream_test",
-    size = "small",
-    srcs = ["internal/ostringstream_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "ostringstream_benchmark",
-    srcs = ["internal/ostringstream_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":internal",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "resize_uninitialized_test",
-    size = "small",
-    srcs = [
-        "internal/resize_uninitialized.h",
-        "internal/resize_uninitialized_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        "//absl/base:core_headers",
-        "//absl/meta:type_traits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_join_test",
-    size = "small",
-    srcs = ["str_join_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:core_headers",
-        "//absl/memory",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_join_benchmark",
-    srcs = ["str_join_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "str_cat_test",
-    size = "small",
-    srcs = ["str_cat_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_cat_benchmark",
-    srcs = ["str_cat_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "numbers_test",
-    size = "medium",
-    srcs = [
-        "internal/numbers_test_common.h",
-        "numbers_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":internal",
-        ":pow10_helper",
-        ":strings",
-        "//absl/base:config",
-        "//absl/base:raw_logging_internal",
-        "//absl/random",
-        "//absl/random:distributions",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "numbers_benchmark",
-    srcs = ["numbers_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:raw_logging_internal",
-        "//absl/random",
-        "//absl/random:distributions",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "strip_test",
-    size = "small",
-    srcs = ["strip_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "char_map_test",
-    srcs = ["internal/char_map_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "char_map_benchmark",
-    srcs = ["internal/char_map_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    tags = ["benchmark"],
-    deps = [
-        ":internal",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "charconv_test",
-    srcs = ["charconv_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":pow10_helper",
-        ":str_format",
-        ":strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "charconv_parse_test",
-    srcs = [
-        "internal/charconv_parse.h",
-        "internal/charconv_parse_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":strings",
-        "//absl/base:config",
-        "//absl/base:raw_logging_internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "charconv_bigint_test",
-    srcs = [
-        "internal/charconv_bigint.h",
-        "internal/charconv_bigint_test.cc",
-        "internal/charconv_parse.h",
-    ],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":strings",
-        "//absl/base:config",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "charconv_benchmark",
-    srcs = [
-        "charconv_benchmark.cc",
-    ],
-    tags = [
-        "benchmark",
-    ],
-    deps = [
-        ":strings",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_library(
-    name = "str_format",
-    hdrs = [
-        "str_format.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    deps = [
-        ":str_format_internal",
-    ],
-)
-
-cc_library(
-    name = "str_format_internal",
-    srcs = [
-        "internal/str_format/arg.cc",
-        "internal/str_format/bind.cc",
-        "internal/str_format/extension.cc",
-        "internal/str_format/float_conversion.cc",
-        "internal/str_format/output.cc",
-        "internal/str_format/parser.cc",
-    ],
-    hdrs = [
-        "internal/str_format/arg.h",
-        "internal/str_format/bind.h",
-        "internal/str_format/checker.h",
-        "internal/str_format/extension.h",
-        "internal/str_format/float_conversion.h",
-        "internal/str_format/output.h",
-        "internal/str_format/parser.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":strings",
-        "//absl/base:bits",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/functional:function_ref",
-        "//absl/meta:type_traits",
-        "//absl/numeric:int128",
-        "//absl/types:optional",
-        "//absl/types:span",
-    ],
-)
-
-cc_test(
-    name = "str_format_test",
-    srcs = ["str_format_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":cord",
-        ":str_format",
-        ":strings",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_format_extension_test",
-    srcs = [
-        "internal/str_format/extension_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":str_format",
-        ":str_format_internal",
-        ":strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_format_arg_test",
-    srcs = ["internal/str_format/arg_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":str_format",
-        ":str_format_internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_format_bind_test",
-    srcs = ["internal/str_format/bind_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":str_format_internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_format_checker_test",
-    srcs = ["internal/str_format/checker_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":str_format",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_format_convert_test",
-    size = "medium",
-    srcs = ["internal/str_format/convert_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":str_format_internal",
-        ":strings",
-        "//absl/base:raw_logging_internal",
-        "//absl/types:optional",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_format_output_test",
-    srcs = ["internal/str_format/output_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":cord",
-        ":str_format_internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "str_format_parser_test",
-    srcs = ["internal/str_format/parser_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":str_format_internal",
-        "//absl/base:core_headers",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "pow10_helper",
-    testonly = True,
-    srcs = ["internal/pow10_helper.cc"],
-    hdrs = ["internal/pow10_helper.h"],
-    visibility = ["//visibility:private"],
-    deps = ["//absl/base:config"],
-)
-
-cc_test(
-    name = "pow10_helper_test",
-    srcs = ["internal/pow10_helper_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":pow10_helper",
-        ":str_format",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/strings/CMakeLists.txt b/third_party/abseil_cpp/absl/strings/CMakeLists.txt
deleted file mode 100644
index 2b994a71c0..0000000000
--- a/third_party/abseil_cpp/absl/strings/CMakeLists.txt
+++ /dev/null
@@ -1,609 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    strings
-  HDRS
-    "ascii.h"
-    "charconv.h"
-    "escaping.h"
-    "internal/string_constant.h"
-    "match.h"
-    "numbers.h"
-    "str_cat.h"
-    "str_join.h"
-    "str_replace.h"
-    "str_split.h"
-    "string_view.h"
-    "strip.h"
-    "substitute.h"
-  SRCS
-    "ascii.cc"
-    "charconv.cc"
-    "escaping.cc"
-    "internal/charconv_bigint.cc"
-    "internal/charconv_bigint.h"
-    "internal/charconv_parse.cc"
-    "internal/charconv_parse.h"
-    "internal/memutil.cc"
-    "internal/memutil.h"
-    "internal/stl_type_traits.h"
-    "internal/str_join_internal.h"
-    "internal/str_split_internal.h"
-    "match.cc"
-    "numbers.cc"
-    "str_cat.cc"
-    "str_replace.cc"
-    "str_split.cc"
-    "string_view.cc"
-    "substitute.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::strings_internal
-    absl::base
-    absl::bits
-    absl::config
-    absl::core_headers
-    absl::endian
-    absl::int128
-    absl::memory
-    absl::raw_logging_internal
-    absl::throw_delegate
-    absl::type_traits
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    strings_internal
-  HDRS
-    "internal/char_map.h"
-    "internal/escaping.cc"
-    "internal/escaping.h"
-    "internal/ostringstream.h"
-    "internal/resize_uninitialized.h"
-    "internal/utf8.h"
-  SRCS
-    "internal/ostringstream.cc"
-    "internal/utf8.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::core_headers
-    absl::endian
-    absl::raw_logging_internal
-    absl::type_traits
-)
-
-absl_cc_test(
-  NAME
-    match_test
-  SRCS
-    "match_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::base
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    escaping_test
-  SRCS
-    "escaping_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::core_headers
-    absl::fixed_array
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    ascii_test
-  SRCS
-    "ascii_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::core_headers
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    memutil_test
-  SRCS
-    "internal/memutil.h"
-    "internal/memutil_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::core_headers
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    utf8_test
-  SRCS
-    "internal/utf8_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings_internal
-    absl::base
-    absl::core_headers
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    string_constant_test
-  SRCS
-    "internal/string_constant_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::type_traits
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    string_view_test
-  SRCS
-    "string_view_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::config
-    absl::core_headers
-    absl::dynamic_annotations
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    substitute_test
-  SRCS
-    "substitute_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::core_headers
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_replace_test
-  SRCS
-    "str_replace_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_split_test
-  SRCS
-    "str_split_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::base
-    absl::core_headers
-    absl::dynamic_annotations
-    absl::flat_hash_map
-    absl::node_hash_map
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    ostringstream_test
-  SRCS
-    "internal/ostringstream_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings_internal
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    resize_uninitialized_test
-  SRCS
-    "internal/resize_uninitialized.h"
-    "internal/resize_uninitialized_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::core_headers
-    absl::type_traits
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_join_test
-  SRCS
-    "str_join_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::base
-    absl::core_headers
-    absl::memory
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_cat_test
-  SRCS
-    "str_cat_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::core_headers
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    numbers_test
-  SRCS
-    "internal/numbers_test_common.h"
-    "numbers_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::core_headers
-    absl::pow10_helper
-    absl::config
-    absl::raw_logging_internal
-    absl::random_random
-    absl::random_distributions
-    absl::strings_internal
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    strip_test
-  SRCS
-    "strip_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::base
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    char_map_test
-  SRCS
-    "internal/char_map_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings_internal
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    charconv_test
-  SRCS
-    "charconv_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::str_format
-    absl::pow10_helper
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    charconv_parse_test
-  SRCS
-    "internal/charconv_parse.h"
-    "internal/charconv_parse_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::config
-    absl::raw_logging_internal
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    charconv_bigint_test
-  SRCS
-    "internal/charconv_bigint.h"
-    "internal/charconv_bigint_test.cc"
-    "internal/charconv_parse.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::config
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    str_format
-  HDRS
-    "str_format.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::str_format_internal
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    str_format_internal
-  HDRS
-    "internal/str_format/arg.h"
-    "internal/str_format/bind.h"
-    "internal/str_format/checker.h"
-    "internal/str_format/extension.h"
-    "internal/str_format/float_conversion.h"
-    "internal/str_format/output.h"
-    "internal/str_format/parser.h"
-  SRCS
-    "internal/str_format/arg.cc"
-    "internal/str_format/bind.cc"
-    "internal/str_format/extension.cc"
-    "internal/str_format/float_conversion.cc"
-    "internal/str_format/output.cc"
-    "internal/str_format/parser.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::bits
-    absl::strings
-    absl::config
-    absl::core_headers
-    absl::type_traits
-    absl::int128
-    absl::span
-)
-
-absl_cc_test(
-  NAME
-    str_format_test
-  SRCS
-    "str_format_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::str_format
-    absl::cord
-    absl::strings
-    absl::core_headers
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_format_extension_test
-  SRCS
-    "internal/str_format/extension_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::str_format
-    absl::str_format_internal
-    absl::strings
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_format_arg_test
-  SRCS
-    "internal/str_format/arg_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::str_format
-    absl::str_format_internal
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_format_bind_test
-  SRCS
-    "internal/str_format/bind_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::str_format_internal
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_format_checker_test
-  SRCS
-    "internal/str_format/checker_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::str_format
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_format_convert_test
-  SRCS
-    "internal/str_format/convert_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::strings
-    absl::str_format_internal
-    absl::raw_logging_internal
-    absl::int128
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_format_output_test
-  SRCS
-    "internal/str_format/output_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::str_format_internal
-    absl::cord
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    str_format_parser_test
-  SRCS
-    "internal/str_format/parser_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::str_format_internal
-    absl::core_headers
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    pow10_helper
-  HDRS
-    "internal/pow10_helper.h"
-  SRCS
-    "internal/pow10_helper.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::config
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    pow10_helper_test
-  SRCS
-    "internal/pow10_helper_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::pow10_helper
-    absl::str_format
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    cord
-  HDRS
-    "cord.h"
-  SRCS
-    "cord.cc"
-    "internal/cord_internal.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base
-    absl::base_internal
-    absl::compressed_tuple
-    absl::core_headers
-    absl::endian
-    absl::fixed_array
-    absl::function_ref
-    absl::inlined_vector
-    absl::optional
-    absl::raw_logging_internal
-    absl::strings
-    absl::strings_internal
-    absl::type_traits
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    cord_test_helpers
-  HDRS
-    "cord_test_helpers.h"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::cord
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    cord_test
-  SRCS
-    "cord_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::cord
-    absl::str_format
-    absl::strings
-    absl::base
-    absl::config
-    absl::core_headers
-    absl::endian
-    absl::raw_logging_internal
-    absl::fixed_array
-    gmock_main
-)
diff --git a/third_party/abseil_cpp/absl/strings/ascii.cc b/third_party/abseil_cpp/absl/strings/ascii.cc
deleted file mode 100644
index 93bb03e958..0000000000
--- a/third_party/abseil_cpp/absl/strings/ascii.cc
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/ascii.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace ascii_internal {
-
-// # Table generated by this Python code (bit 0x02 is currently unused):
-// TODO(mbar) Move Python code for generation of table to BUILD and link here.
-
-// NOTE: The kAsciiPropertyBits table used within this code was generated by
-// Python code of the following form. (Bit 0x02 is currently unused and
-// available.)
-//
-// def Hex2(n):
-//   return '0x' + hex(n/16)[2:] + hex(n%16)[2:]
-// def IsPunct(ch):
-//   return (ord(ch) >= 32 and ord(ch) < 127 and
-//           not ch.isspace() and not ch.isalnum())
-// def IsBlank(ch):
-//   return ch in ' \t'
-// def IsCntrl(ch):
-//   return ord(ch) < 32 or ord(ch) == 127
-// def IsXDigit(ch):
-//   return ch.isdigit() or ch.lower() in 'abcdef'
-// for i in range(128):
-//   ch = chr(i)
-//   mask = ((ch.isalpha() and 0x01 or 0) |
-//           (ch.isalnum() and 0x04 or 0) |
-//           (ch.isspace() and 0x08 or 0) |
-//           (IsPunct(ch) and 0x10 or 0) |
-//           (IsBlank(ch) and 0x20 or 0) |
-//           (IsCntrl(ch) and 0x40 or 0) |
-//           (IsXDigit(ch) and 0x80 or 0))
-//   print Hex2(mask) + ',',
-//   if i % 16 == 7:
-//     print ' //', Hex2(i & 0x78)
-//   elif i % 16 == 15:
-//     print
-
-// clang-format off
-// Array of bitfields holding character information. Each bit value corresponds
-// to a particular character feature. For readability, and because the value
-// of these bits is tightly coupled to this implementation, the individual bits
-// are not named. Note that bitfields for all characters above ASCII 127 are
-// zero-initialized.
-ABSL_DLL const unsigned char kPropertyBits[256] = {
-    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  // 0x00
-    0x40, 0x68, 0x48, 0x48, 0x48, 0x48, 0x40, 0x40,
-    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  // 0x10
-    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
-    0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,  // 0x20
-    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
-    0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,  // 0x30
-    0x84, 0x84, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
-    0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05,  // 0x40
-    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
-    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,  // 0x50
-    0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x10,
-    0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05,  // 0x60
-    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
-    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,  // 0x70
-    0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x40,
-};
-
-// Array of characters for the ascii_tolower() function. For values 'A'
-// through 'Z', return the lower-case character; otherwise, return the
-// identity of the passed character.
-ABSL_DLL const char kToLower[256] = {
-  '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
-  '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
-  '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
-  '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
-  '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
-  '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
-  '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
-  '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
-  '\x40',    'a',    'b',    'c',    'd',    'e',    'f',    'g',
-     'h',    'i',    'j',    'k',    'l',    'm',    'n',    'o',
-     'p',    'q',    'r',    's',    't',    'u',    'v',    'w',
-     'x',    'y',    'z', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
-  '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
-  '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
-  '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
-  '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f',
-  '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
-  '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
-  '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
-  '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
-  '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
-  '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
-  '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
-  '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
-  '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
-  '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
-  '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
-  '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
-  '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
-  '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
-  '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
-  '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff',
-};
-
-// Array of characters for the ascii_toupper() function. For values 'a'
-// through 'z', return the upper-case character; otherwise, return the
-// identity of the passed character.
-ABSL_DLL const char kToUpper[256] = {
-  '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
-  '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
-  '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
-  '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
-  '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
-  '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
-  '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
-  '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
-  '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
-  '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
-  '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
-  '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
-  '\x60',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
-     'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
-     'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
-     'X',    'Y',    'Z', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f',
-  '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
-  '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
-  '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
-  '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
-  '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
-  '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
-  '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
-  '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
-  '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
-  '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
-  '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
-  '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
-  '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
-  '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
-  '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
-  '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff',
-};
-// clang-format on
-
-}  // namespace ascii_internal
-
-void AsciiStrToLower(std::string* s) {
-  for (auto& ch : *s) {
-    ch = absl::ascii_tolower(ch);
-  }
-}
-
-void AsciiStrToUpper(std::string* s) {
-  for (auto& ch : *s) {
-    ch = absl::ascii_toupper(ch);
-  }
-}
-
-void RemoveExtraAsciiWhitespace(std::string* str) {
-  auto stripped = StripAsciiWhitespace(*str);
-
-  if (stripped.empty()) {
-    str->clear();
-    return;
-  }
-
-  auto input_it = stripped.begin();
-  auto input_end = stripped.end();
-  auto output_it = &(*str)[0];
-  bool is_ws = false;
-
-  for (; input_it < input_end; ++input_it) {
-    if (is_ws) {
-      // Consecutive whitespace?  Keep only the last.
-      is_ws = absl::ascii_isspace(*input_it);
-      if (is_ws) --output_it;
-    } else {
-      is_ws = absl::ascii_isspace(*input_it);
-    }
-
-    *output_it = *input_it;
-    ++output_it;
-  }
-
-  str->erase(output_it - &(*str)[0]);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/ascii.h b/third_party/abseil_cpp/absl/strings/ascii.h
deleted file mode 100644
index b46bc71f35..0000000000
--- a/third_party/abseil_cpp/absl/strings/ascii.h
+++ /dev/null
@@ -1,242 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: ascii.h
-// -----------------------------------------------------------------------------
-//
-// This package contains functions operating on characters and strings
-// restricted to standard ASCII. These include character classification
-// functions analogous to those found in the ANSI C Standard Library <ctype.h>
-// header file.
-//
-// C++ implementations provide <ctype.h> functionality based on their
-// C environment locale. In general, reliance on such a locale is not ideal, as
-// the locale standard is problematic (and may not return invariant information
-// for the same character set, for example). These `ascii_*()` functions are
-// hard-wired for standard ASCII, much faster, and guaranteed to behave
-// consistently.  They will never be overloaded, nor will their function
-// signature change.
-//
-// `ascii_isalnum()`, `ascii_isalpha()`, `ascii_isascii()`, `ascii_isblank()`,
-// `ascii_iscntrl()`, `ascii_isdigit()`, `ascii_isgraph()`, `ascii_islower()`,
-// `ascii_isprint()`, `ascii_ispunct()`, `ascii_isspace()`, `ascii_isupper()`,
-// `ascii_isxdigit()`
-//   Analogous to the <ctype.h> functions with similar names, these
-//   functions take an unsigned char and return a bool, based on whether the
-//   character matches the condition specified.
-//
-//   If the input character has a numerical value greater than 127, these
-//   functions return `false`.
-//
-// `ascii_tolower()`, `ascii_toupper()`
-//   Analogous to the <ctype.h> functions with similar names, these functions
-//   take an unsigned char and return a char.
-//
-//   If the input character is not an ASCII {lower,upper}-case letter (including
-//   numerical values greater than 127) then the functions return the same value
-//   as the input character.
-
-#ifndef ABSL_STRINGS_ASCII_H_
-#define ABSL_STRINGS_ASCII_H_
-
-#include <algorithm>
-#include <string>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace ascii_internal {
-
-// Declaration for an array of bitfields holding character information.
-ABSL_DLL extern const unsigned char kPropertyBits[256];
-
-// Declaration for the array of characters to upper-case characters.
-ABSL_DLL extern const char kToUpper[256];
-
-// Declaration for the array of characters to lower-case characters.
-ABSL_DLL extern const char kToLower[256];
-
-}  // namespace ascii_internal
-
-// ascii_isalpha()
-//
-// Determines whether the given character is an alphabetic character.
-inline bool ascii_isalpha(unsigned char c) {
-  return (ascii_internal::kPropertyBits[c] & 0x01) != 0;
-}
-
-// ascii_isalnum()
-//
-// Determines whether the given character is an alphanumeric character.
-inline bool ascii_isalnum(unsigned char c) {
-  return (ascii_internal::kPropertyBits[c] & 0x04) != 0;
-}
-
-// ascii_isspace()
-//
-// Determines whether the given character is a whitespace character (space,
-// tab, vertical tab, formfeed, linefeed, or carriage return).
-inline bool ascii_isspace(unsigned char c) {
-  return (ascii_internal::kPropertyBits[c] & 0x08) != 0;
-}
-
-// ascii_ispunct()
-//
-// Determines whether the given character is a punctuation character.
-inline bool ascii_ispunct(unsigned char c) {
-  return (ascii_internal::kPropertyBits[c] & 0x10) != 0;
-}
-
-// ascii_isblank()
-//
-// Determines whether the given character is a blank character (tab or space).
-inline bool ascii_isblank(unsigned char c) {
-  return (ascii_internal::kPropertyBits[c] & 0x20) != 0;
-}
-
-// ascii_iscntrl()
-//
-// Determines whether the given character is a control character.
-inline bool ascii_iscntrl(unsigned char c) {
-  return (ascii_internal::kPropertyBits[c] & 0x40) != 0;
-}
-
-// ascii_isxdigit()
-//
-// Determines whether the given character can be represented as a hexadecimal
-// digit character (i.e. {0-9} or {A-F}).
-inline bool ascii_isxdigit(unsigned char c) {
-  return (ascii_internal::kPropertyBits[c] & 0x80) != 0;
-}
-
-// ascii_isdigit()
-//
-// Determines whether the given character can be represented as a decimal
-// digit character (i.e. {0-9}).
-inline bool ascii_isdigit(unsigned char c) { return c >= '0' && c <= '9'; }
-
-// ascii_isprint()
-//
-// Determines whether the given character is printable, including whitespace.
-inline bool ascii_isprint(unsigned char c) { return c >= 32 && c < 127; }
-
-// ascii_isgraph()
-//
-// Determines whether the given character has a graphical representation.
-inline bool ascii_isgraph(unsigned char c) { return c > 32 && c < 127; }
-
-// ascii_isupper()
-//
-// Determines whether the given character is uppercase.
-inline bool ascii_isupper(unsigned char c) { return c >= 'A' && c <= 'Z'; }
-
-// ascii_islower()
-//
-// Determines whether the given character is lowercase.
-inline bool ascii_islower(unsigned char c) { return c >= 'a' && c <= 'z'; }
-
-// ascii_isascii()
-//
-// Determines whether the given character is ASCII.
-inline bool ascii_isascii(unsigned char c) { return c < 128; }
-
-// ascii_tolower()
-//
-// Returns an ASCII character, converting to lowercase if uppercase is
-// passed. Note that character values > 127 are simply returned.
-inline char ascii_tolower(unsigned char c) {
-  return ascii_internal::kToLower[c];
-}
-
-// Converts the characters in `s` to lowercase, changing the contents of `s`.
-void AsciiStrToLower(std::string* s);
-
-// Creates a lowercase string from a given absl::string_view.
-ABSL_MUST_USE_RESULT inline std::string AsciiStrToLower(absl::string_view s) {
-  std::string result(s);
-  absl::AsciiStrToLower(&result);
-  return result;
-}
-
-// ascii_toupper()
-//
-// Returns the ASCII character, converting to upper-case if lower-case is
-// passed. Note that characters values > 127 are simply returned.
-inline char ascii_toupper(unsigned char c) {
-  return ascii_internal::kToUpper[c];
-}
-
-// Converts the characters in `s` to uppercase, changing the contents of `s`.
-void AsciiStrToUpper(std::string* s);
-
-// Creates an uppercase string from a given absl::string_view.
-ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) {
-  std::string result(s);
-  absl::AsciiStrToUpper(&result);
-  return result;
-}
-
-// Returns absl::string_view with whitespace stripped from the beginning of the
-// given string_view.
-ABSL_MUST_USE_RESULT inline absl::string_view StripLeadingAsciiWhitespace(
-    absl::string_view str) {
-  auto it = std::find_if_not(str.begin(), str.end(), absl::ascii_isspace);
-  return str.substr(it - str.begin());
-}
-
-// Strips in place whitespace from the beginning of the given string.
-inline void StripLeadingAsciiWhitespace(std::string* str) {
-  auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace);
-  str->erase(str->begin(), it);
-}
-
-// Returns absl::string_view with whitespace stripped from the end of the given
-// string_view.
-ABSL_MUST_USE_RESULT inline absl::string_view StripTrailingAsciiWhitespace(
-    absl::string_view str) {
-  auto it = std::find_if_not(str.rbegin(), str.rend(), absl::ascii_isspace);
-  return str.substr(0, str.rend() - it);
-}
-
-// Strips in place whitespace from the end of the given string
-inline void StripTrailingAsciiWhitespace(std::string* str) {
-  auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace);
-  str->erase(str->rend() - it);
-}
-
-// Returns absl::string_view with whitespace stripped from both ends of the
-// given string_view.
-ABSL_MUST_USE_RESULT inline absl::string_view StripAsciiWhitespace(
-    absl::string_view str) {
-  return StripTrailingAsciiWhitespace(StripLeadingAsciiWhitespace(str));
-}
-
-// Strips in place whitespace from both ends of the given string
-inline void StripAsciiWhitespace(std::string* str) {
-  StripTrailingAsciiWhitespace(str);
-  StripLeadingAsciiWhitespace(str);
-}
-
-// Removes leading, trailing, and consecutive internal whitespace.
-void RemoveExtraAsciiWhitespace(std::string*);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_ASCII_H_
diff --git a/third_party/abseil_cpp/absl/strings/ascii_benchmark.cc b/third_party/abseil_cpp/absl/strings/ascii_benchmark.cc
deleted file mode 100644
index aca458c804..0000000000
--- a/third_party/abseil_cpp/absl/strings/ascii_benchmark.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/ascii.h"
-
-#include <cctype>
-#include <string>
-#include <array>
-#include <random>
-
-#include "benchmark/benchmark.h"
-
-namespace {
-
-std::array<unsigned char, 256> MakeShuffledBytes() {
-  std::array<unsigned char, 256> bytes;
-  for (size_t i = 0; i < 256; ++i) bytes[i] = static_cast<unsigned char>(i);
-  std::random_device rd;
-  std::seed_seq seed({rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()});
-  std::mt19937 g(seed);
-  std::shuffle(bytes.begin(), bytes.end(), g);
-  return bytes;
-}
-
-template <typename Function>
-void AsciiBenchmark(benchmark::State& state, Function f) {
-  std::array<unsigned char, 256> bytes = MakeShuffledBytes();
-  size_t sum = 0;
-  for (auto _ : state) {
-    for (unsigned char b : bytes) sum += f(b) ? 1 : 0;
-  }
-  // Make a copy of `sum` before calling `DoNotOptimize` to make sure that `sum`
-  // can be put in a CPU register and not degrade performance in the loop above.
-  size_t sum2 = sum;
-  benchmark::DoNotOptimize(sum2);
-  state.SetBytesProcessed(state.iterations() * bytes.size());
-}
-
-using StdAsciiFunction = int (*)(int);
-template <StdAsciiFunction f>
-void BM_Ascii(benchmark::State& state) {
-  AsciiBenchmark(state, f);
-}
-
-using AbslAsciiIsFunction = bool (*)(unsigned char);
-template <AbslAsciiIsFunction f>
-void BM_Ascii(benchmark::State& state) {
-  AsciiBenchmark(state, f);
-}
-
-using AbslAsciiToFunction = char (*)(unsigned char);
-template <AbslAsciiToFunction f>
-void BM_Ascii(benchmark::State& state) {
-  AsciiBenchmark(state, f);
-}
-
-inline char Noop(unsigned char b) { return static_cast<char>(b); }
-
-BENCHMARK_TEMPLATE(BM_Ascii, Noop);
-BENCHMARK_TEMPLATE(BM_Ascii, std::isalpha);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isalpha);
-BENCHMARK_TEMPLATE(BM_Ascii, std::isdigit);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isdigit);
-BENCHMARK_TEMPLATE(BM_Ascii, std::isalnum);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isalnum);
-BENCHMARK_TEMPLATE(BM_Ascii, std::isspace);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isspace);
-BENCHMARK_TEMPLATE(BM_Ascii, std::ispunct);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_ispunct);
-BENCHMARK_TEMPLATE(BM_Ascii, std::isblank);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isblank);
-BENCHMARK_TEMPLATE(BM_Ascii, std::iscntrl);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_iscntrl);
-BENCHMARK_TEMPLATE(BM_Ascii, std::isxdigit);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isxdigit);
-BENCHMARK_TEMPLATE(BM_Ascii, std::isprint);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isprint);
-BENCHMARK_TEMPLATE(BM_Ascii, std::isgraph);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isgraph);
-BENCHMARK_TEMPLATE(BM_Ascii, std::isupper);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isupper);
-BENCHMARK_TEMPLATE(BM_Ascii, std::islower);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_islower);
-BENCHMARK_TEMPLATE(BM_Ascii, isascii);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_isascii);
-BENCHMARK_TEMPLATE(BM_Ascii, std::tolower);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_tolower);
-BENCHMARK_TEMPLATE(BM_Ascii, std::toupper);
-BENCHMARK_TEMPLATE(BM_Ascii, absl::ascii_toupper);
-
-static void BM_StrToLower(benchmark::State& state) {
-  const int size = state.range(0);
-  std::string s(size, 'X');
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::AsciiStrToLower(s));
-  }
-}
-BENCHMARK(BM_StrToLower)->Range(1, 1 << 20);
-
-static void BM_StrToUpper(benchmark::State& state) {
-  const int size = state.range(0);
-  std::string s(size, 'x');
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::AsciiStrToUpper(s));
-  }
-}
-BENCHMARK(BM_StrToUpper)->Range(1, 1 << 20);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/ascii_test.cc b/third_party/abseil_cpp/absl/strings/ascii_test.cc
deleted file mode 100644
index 5ecd23f869..0000000000
--- a/third_party/abseil_cpp/absl/strings/ascii_test.cc
+++ /dev/null
@@ -1,361 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/ascii.h"
-
-#include <cctype>
-#include <clocale>
-#include <cstring>
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-
-namespace {
-
-TEST(AsciiIsFoo, All) {
-  for (int i = 0; i < 256; i++) {
-    if ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z'))
-      EXPECT_TRUE(absl::ascii_isalpha(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_isalpha(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if ((i >= '0' && i <= '9'))
-      EXPECT_TRUE(absl::ascii_isdigit(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_isdigit(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (absl::ascii_isalpha(i) || absl::ascii_isdigit(i))
-      EXPECT_TRUE(absl::ascii_isalnum(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_isalnum(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (i != '\0' && strchr(" \r\n\t\v\f", i))
-      EXPECT_TRUE(absl::ascii_isspace(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_isspace(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (i >= 32 && i < 127)
-      EXPECT_TRUE(absl::ascii_isprint(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_isprint(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (absl::ascii_isprint(i) && !absl::ascii_isspace(i) &&
-        !absl::ascii_isalnum(i))
-      EXPECT_TRUE(absl::ascii_ispunct(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_ispunct(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (i == ' ' || i == '\t')
-      EXPECT_TRUE(absl::ascii_isblank(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_isblank(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (i < 32 || i == 127)
-      EXPECT_TRUE(absl::ascii_iscntrl(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_iscntrl(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (absl::ascii_isdigit(i) || (i >= 'A' && i <= 'F') ||
-        (i >= 'a' && i <= 'f'))
-      EXPECT_TRUE(absl::ascii_isxdigit(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_isxdigit(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (i > 32 && i < 127)
-      EXPECT_TRUE(absl::ascii_isgraph(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_isgraph(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (i >= 'A' && i <= 'Z')
-      EXPECT_TRUE(absl::ascii_isupper(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_isupper(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 256; i++) {
-    if (i >= 'a' && i <= 'z')
-      EXPECT_TRUE(absl::ascii_islower(i)) << ": failed on " << i;
-    else
-      EXPECT_TRUE(!absl::ascii_islower(i)) << ": failed on " << i;
-  }
-  for (int i = 0; i < 128; i++) {
-    EXPECT_TRUE(absl::ascii_isascii(i)) << ": failed on " << i;
-  }
-  for (int i = 128; i < 256; i++) {
-    EXPECT_TRUE(!absl::ascii_isascii(i)) << ": failed on " << i;
-  }
-
-  // The official is* functions don't accept negative signed chars, but
-  // our absl::ascii_is* functions do.
-  for (int i = 0; i < 256; i++) {
-    signed char sc = static_cast<signed char>(static_cast<unsigned char>(i));
-    EXPECT_EQ(absl::ascii_isalpha(i), absl::ascii_isalpha(sc)) << i;
-    EXPECT_EQ(absl::ascii_isdigit(i), absl::ascii_isdigit(sc)) << i;
-    EXPECT_EQ(absl::ascii_isalnum(i), absl::ascii_isalnum(sc)) << i;
-    EXPECT_EQ(absl::ascii_isspace(i), absl::ascii_isspace(sc)) << i;
-    EXPECT_EQ(absl::ascii_ispunct(i), absl::ascii_ispunct(sc)) << i;
-    EXPECT_EQ(absl::ascii_isblank(i), absl::ascii_isblank(sc)) << i;
-    EXPECT_EQ(absl::ascii_iscntrl(i), absl::ascii_iscntrl(sc)) << i;
-    EXPECT_EQ(absl::ascii_isxdigit(i), absl::ascii_isxdigit(sc)) << i;
-    EXPECT_EQ(absl::ascii_isprint(i), absl::ascii_isprint(sc)) << i;
-    EXPECT_EQ(absl::ascii_isgraph(i), absl::ascii_isgraph(sc)) << i;
-    EXPECT_EQ(absl::ascii_isupper(i), absl::ascii_isupper(sc)) << i;
-    EXPECT_EQ(absl::ascii_islower(i), absl::ascii_islower(sc)) << i;
-    EXPECT_EQ(absl::ascii_isascii(i), absl::ascii_isascii(sc)) << i;
-  }
-}
-
-// Checks that absl::ascii_isfoo returns the same value as isfoo in the C
-// locale.
-TEST(AsciiIsFoo, SameAsIsFoo) {
-#ifndef __ANDROID__
-  // temporarily change locale to C. It should already be C, but just for safety
-  const char* old_locale = setlocale(LC_CTYPE, "C");
-  ASSERT_TRUE(old_locale != nullptr);
-#endif
-
-  for (int i = 0; i < 256; i++) {
-    EXPECT_EQ(isalpha(i) != 0, absl::ascii_isalpha(i)) << i;
-    EXPECT_EQ(isdigit(i) != 0, absl::ascii_isdigit(i)) << i;
-    EXPECT_EQ(isalnum(i) != 0, absl::ascii_isalnum(i)) << i;
-    EXPECT_EQ(isspace(i) != 0, absl::ascii_isspace(i)) << i;
-    EXPECT_EQ(ispunct(i) != 0, absl::ascii_ispunct(i)) << i;
-    EXPECT_EQ(isblank(i) != 0, absl::ascii_isblank(i)) << i;
-    EXPECT_EQ(iscntrl(i) != 0, absl::ascii_iscntrl(i)) << i;
-    EXPECT_EQ(isxdigit(i) != 0, absl::ascii_isxdigit(i)) << i;
-    EXPECT_EQ(isprint(i) != 0, absl::ascii_isprint(i)) << i;
-    EXPECT_EQ(isgraph(i) != 0, absl::ascii_isgraph(i)) << i;
-    EXPECT_EQ(isupper(i) != 0, absl::ascii_isupper(i)) << i;
-    EXPECT_EQ(islower(i) != 0, absl::ascii_islower(i)) << i;
-    EXPECT_EQ(isascii(i) != 0, absl::ascii_isascii(i)) << i;
-  }
-
-#ifndef __ANDROID__
-  // restore the old locale.
-  ASSERT_TRUE(setlocale(LC_CTYPE, old_locale));
-#endif
-}
-
-TEST(AsciiToFoo, All) {
-#ifndef __ANDROID__
-  // temporarily change locale to C. It should already be C, but just for safety
-  const char* old_locale = setlocale(LC_CTYPE, "C");
-  ASSERT_TRUE(old_locale != nullptr);
-#endif
-
-  for (int i = 0; i < 256; i++) {
-    if (absl::ascii_islower(i))
-      EXPECT_EQ(absl::ascii_toupper(i), 'A' + (i - 'a')) << i;
-    else
-      EXPECT_EQ(absl::ascii_toupper(i), static_cast<char>(i)) << i;
-
-    if (absl::ascii_isupper(i))
-      EXPECT_EQ(absl::ascii_tolower(i), 'a' + (i - 'A')) << i;
-    else
-      EXPECT_EQ(absl::ascii_tolower(i), static_cast<char>(i)) << i;
-
-    // These CHECKs only hold in a C locale.
-    EXPECT_EQ(static_cast<char>(tolower(i)), absl::ascii_tolower(i)) << i;
-    EXPECT_EQ(static_cast<char>(toupper(i)), absl::ascii_toupper(i)) << i;
-
-    // The official to* functions don't accept negative signed chars, but
-    // our absl::ascii_to* functions do.
-    signed char sc = static_cast<signed char>(static_cast<unsigned char>(i));
-    EXPECT_EQ(absl::ascii_tolower(i), absl::ascii_tolower(sc)) << i;
-    EXPECT_EQ(absl::ascii_toupper(i), absl::ascii_toupper(sc)) << i;
-  }
-#ifndef __ANDROID__
-  // restore the old locale.
-  ASSERT_TRUE(setlocale(LC_CTYPE, old_locale));
-#endif
-}
-
-TEST(AsciiStrTo, Lower) {
-  const char buf[] = "ABCDEF";
-  const std::string str("GHIJKL");
-  const std::string str2("MNOPQR");
-  const absl::string_view sp(str2);
-
-  EXPECT_EQ("abcdef", absl::AsciiStrToLower(buf));
-  EXPECT_EQ("ghijkl", absl::AsciiStrToLower(str));
-  EXPECT_EQ("mnopqr", absl::AsciiStrToLower(sp));
-
-  char mutable_buf[] = "Mutable";
-  std::transform(mutable_buf, mutable_buf + strlen(mutable_buf),
-                 mutable_buf, absl::ascii_tolower);
-  EXPECT_STREQ("mutable", mutable_buf);
-}
-
-TEST(AsciiStrTo, Upper) {
-  const char buf[] = "abcdef";
-  const std::string str("ghijkl");
-  const std::string str2("mnopqr");
-  const absl::string_view sp(str2);
-
-  EXPECT_EQ("ABCDEF", absl::AsciiStrToUpper(buf));
-  EXPECT_EQ("GHIJKL", absl::AsciiStrToUpper(str));
-  EXPECT_EQ("MNOPQR", absl::AsciiStrToUpper(sp));
-
-  char mutable_buf[] = "Mutable";
-  std::transform(mutable_buf, mutable_buf + strlen(mutable_buf),
-                 mutable_buf, absl::ascii_toupper);
-  EXPECT_STREQ("MUTABLE", mutable_buf);
-}
-
-TEST(StripLeadingAsciiWhitespace, FromStringView) {
-  EXPECT_EQ(absl::string_view{},
-            absl::StripLeadingAsciiWhitespace(absl::string_view{}));
-  EXPECT_EQ("foo", absl::StripLeadingAsciiWhitespace({"foo"}));
-  EXPECT_EQ("foo", absl::StripLeadingAsciiWhitespace({"\t  \n\f\r\n\vfoo"}));
-  EXPECT_EQ("foo foo\n ",
-            absl::StripLeadingAsciiWhitespace({"\t  \n\f\r\n\vfoo foo\n "}));
-  EXPECT_EQ(absl::string_view{}, absl::StripLeadingAsciiWhitespace(
-                                     {"\t  \n\f\r\v\n\t  \n\f\r\v\n"}));
-}
-
-TEST(StripLeadingAsciiWhitespace, InPlace) {
-  std::string str;
-
-  absl::StripLeadingAsciiWhitespace(&str);
-  EXPECT_EQ("", str);
-
-  str = "foo";
-  absl::StripLeadingAsciiWhitespace(&str);
-  EXPECT_EQ("foo", str);
-
-  str = "\t  \n\f\r\n\vfoo";
-  absl::StripLeadingAsciiWhitespace(&str);
-  EXPECT_EQ("foo", str);
-
-  str = "\t  \n\f\r\n\vfoo foo\n ";
-  absl::StripLeadingAsciiWhitespace(&str);
-  EXPECT_EQ("foo foo\n ", str);
-
-  str = "\t  \n\f\r\v\n\t  \n\f\r\v\n";
-  absl::StripLeadingAsciiWhitespace(&str);
-  EXPECT_EQ(absl::string_view{}, str);
-}
-
-TEST(StripTrailingAsciiWhitespace, FromStringView) {
-  EXPECT_EQ(absl::string_view{},
-            absl::StripTrailingAsciiWhitespace(absl::string_view{}));
-  EXPECT_EQ("foo", absl::StripTrailingAsciiWhitespace({"foo"}));
-  EXPECT_EQ("foo", absl::StripTrailingAsciiWhitespace({"foo\t  \n\f\r\n\v"}));
-  EXPECT_EQ(" \nfoo foo",
-            absl::StripTrailingAsciiWhitespace({" \nfoo foo\t  \n\f\r\n\v"}));
-  EXPECT_EQ(absl::string_view{}, absl::StripTrailingAsciiWhitespace(
-                                     {"\t  \n\f\r\v\n\t  \n\f\r\v\n"}));
-}
-
-TEST(StripTrailingAsciiWhitespace, InPlace) {
-  std::string str;
-
-  absl::StripTrailingAsciiWhitespace(&str);
-  EXPECT_EQ("", str);
-
-  str = "foo";
-  absl::StripTrailingAsciiWhitespace(&str);
-  EXPECT_EQ("foo", str);
-
-  str = "foo\t  \n\f\r\n\v";
-  absl::StripTrailingAsciiWhitespace(&str);
-  EXPECT_EQ("foo", str);
-
-  str = " \nfoo foo\t  \n\f\r\n\v";
-  absl::StripTrailingAsciiWhitespace(&str);
-  EXPECT_EQ(" \nfoo foo", str);
-
-  str = "\t  \n\f\r\v\n\t  \n\f\r\v\n";
-  absl::StripTrailingAsciiWhitespace(&str);
-  EXPECT_EQ(absl::string_view{}, str);
-}
-
-TEST(StripAsciiWhitespace, FromStringView) {
-  EXPECT_EQ(absl::string_view{},
-            absl::StripAsciiWhitespace(absl::string_view{}));
-  EXPECT_EQ("foo", absl::StripAsciiWhitespace({"foo"}));
-  EXPECT_EQ("foo",
-            absl::StripAsciiWhitespace({"\t  \n\f\r\n\vfoo\t  \n\f\r\n\v"}));
-  EXPECT_EQ("foo foo", absl::StripAsciiWhitespace(
-                           {"\t  \n\f\r\n\vfoo foo\t  \n\f\r\n\v"}));
-  EXPECT_EQ(absl::string_view{},
-            absl::StripAsciiWhitespace({"\t  \n\f\r\v\n\t  \n\f\r\v\n"}));
-}
-
-TEST(StripAsciiWhitespace, InPlace) {
-  std::string str;
-
-  absl::StripAsciiWhitespace(&str);
-  EXPECT_EQ("", str);
-
-  str = "foo";
-  absl::StripAsciiWhitespace(&str);
-  EXPECT_EQ("foo", str);
-
-  str = "\t  \n\f\r\n\vfoo\t  \n\f\r\n\v";
-  absl::StripAsciiWhitespace(&str);
-  EXPECT_EQ("foo", str);
-
-  str = "\t  \n\f\r\n\vfoo foo\t  \n\f\r\n\v";
-  absl::StripAsciiWhitespace(&str);
-  EXPECT_EQ("foo foo", str);
-
-  str = "\t  \n\f\r\v\n\t  \n\f\r\v\n";
-  absl::StripAsciiWhitespace(&str);
-  EXPECT_EQ(absl::string_view{}, str);
-}
-
-TEST(RemoveExtraAsciiWhitespace, InPlace) {
-  const char* inputs[] = {"No extra space",
-                          "  Leading whitespace",
-                          "Trailing whitespace  ",
-                          "  Leading and trailing  ",
-                          " Whitespace \t  in\v   middle  ",
-                          "'Eeeeep!  \n Newlines!\n",
-                          "nospaces",
-                          "",
-                          "\n\t a\t\n\nb \t\n"};
-
-  const char* outputs[] = {
-      "No extra space",
-      "Leading whitespace",
-      "Trailing whitespace",
-      "Leading and trailing",
-      "Whitespace in middle",
-      "'Eeeeep! Newlines!",
-      "nospaces",
-      "",
-      "a\nb",
-  };
-  const int NUM_TESTS = ABSL_ARRAYSIZE(inputs);
-
-  for (int i = 0; i < NUM_TESTS; i++) {
-    std::string s(inputs[i]);
-    absl::RemoveExtraAsciiWhitespace(&s);
-    EXPECT_EQ(outputs[i], s);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/charconv.cc b/third_party/abseil_cpp/absl/strings/charconv.cc
deleted file mode 100644
index 3613a65286..0000000000
--- a/third_party/abseil_cpp/absl/strings/charconv.cc
+++ /dev/null
@@ -1,984 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/charconv.h"
-
-#include <algorithm>
-#include <cassert>
-#include <cmath>
-#include <cstring>
-
-#include "absl/base/casts.h"
-#include "absl/base/internal/bits.h"
-#include "absl/numeric/int128.h"
-#include "absl/strings/internal/charconv_bigint.h"
-#include "absl/strings/internal/charconv_parse.h"
-
-// The macro ABSL_BIT_PACK_FLOATS is defined on x86-64, where IEEE floating
-// point numbers have the same endianness in memory as a bitfield struct
-// containing the corresponding parts.
-//
-// When set, we replace calls to ldexp() with manual bit packing, which is
-// faster and is unaffected by floating point environment.
-#ifdef ABSL_BIT_PACK_FLOATS
-#error ABSL_BIT_PACK_FLOATS cannot be directly set
-#elif defined(__x86_64__) || defined(_M_X64)
-#define ABSL_BIT_PACK_FLOATS 1
-#endif
-
-// A note about subnormals:
-//
-// The code below talks about "normals" and "subnormals".  A normal IEEE float
-// has a fixed-width mantissa and power of two exponent.  For example, a normal
-// `double` has a 53-bit mantissa.  Because the high bit is always 1, it is not
-// stored in the representation.  The implicit bit buys an extra bit of
-// resolution in the datatype.
-//
-// The downside of this scheme is that there is a large gap between DBL_MIN and
-// zero.  (Large, at least, relative to the different between DBL_MIN and the
-// next representable number).  This gap is softened by the "subnormal" numbers,
-// which have the same power-of-two exponent as DBL_MIN, but no implicit 53rd
-// bit.  An all-bits-zero exponent in the encoding represents subnormals.  (Zero
-// is represented as a subnormal with an all-bits-zero mantissa.)
-//
-// The code below, in calculations, represents the mantissa as a uint64_t.  The
-// end result normally has the 53rd bit set.  It represents subnormals by using
-// narrower mantissas.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-template <typename FloatType>
-struct FloatTraits;
-
-template <>
-struct FloatTraits<double> {
-  // The number of mantissa bits in the given float type.  This includes the
-  // implied high bit.
-  static constexpr int kTargetMantissaBits = 53;
-
-  // The largest supported IEEE exponent, in our integral mantissa
-  // representation.
-  //
-  // If `m` is the largest possible int kTargetMantissaBits bits wide, then
-  // m * 2**kMaxExponent is exactly equal to DBL_MAX.
-  static constexpr int kMaxExponent = 971;
-
-  // The smallest supported IEEE normal exponent, in our integral mantissa
-  // representation.
-  //
-  // If `m` is the smallest possible int kTargetMantissaBits bits wide, then
-  // m * 2**kMinNormalExponent is exactly equal to DBL_MIN.
-  static constexpr int kMinNormalExponent = -1074;
-
-  static double MakeNan(const char* tagp) {
-    // Support nan no matter which namespace it's in.  Some platforms
-    // incorrectly don't put it in namespace std.
-    using namespace std;  // NOLINT
-    return nan(tagp);
-  }
-
-  // Builds a nonzero floating point number out of the provided parts.
-  //
-  // This is intended to do the same operation as ldexp(mantissa, exponent),
-  // but using purely integer math, to avoid -ffastmath and floating
-  // point environment issues.  Using type punning is also faster. We fall back
-  // to ldexp on a per-platform basis for portability.
-  //
-  // `exponent` must be between kMinNormalExponent and kMaxExponent.
-  //
-  // `mantissa` must either be exactly kTargetMantissaBits wide, in which case
-  // a normal value is made, or it must be less narrow than that, in which case
-  // `exponent` must be exactly kMinNormalExponent, and a subnormal value is
-  // made.
-  static double Make(uint64_t mantissa, int exponent, bool sign) {
-#ifndef ABSL_BIT_PACK_FLOATS
-    // Support ldexp no matter which namespace it's in.  Some platforms
-    // incorrectly don't put it in namespace std.
-    using namespace std;  // NOLINT
-    return sign ? -ldexp(mantissa, exponent) : ldexp(mantissa, exponent);
-#else
-    constexpr uint64_t kMantissaMask =
-        (uint64_t(1) << (kTargetMantissaBits - 1)) - 1;
-    uint64_t dbl = static_cast<uint64_t>(sign) << 63;
-    if (mantissa > kMantissaMask) {
-      // Normal value.
-      // Adjust by 1023 for the exponent representation bias, and an additional
-      // 52 due to the implied decimal point in the IEEE mantissa represenation.
-      dbl += uint64_t{exponent + 1023u + kTargetMantissaBits - 1} << 52;
-      mantissa &= kMantissaMask;
-    } else {
-      // subnormal value
-      assert(exponent == kMinNormalExponent);
-    }
-    dbl += mantissa;
-    return absl::bit_cast<double>(dbl);
-#endif  // ABSL_BIT_PACK_FLOATS
-  }
-};
-
-// Specialization of floating point traits for the `float` type.  See the
-// FloatTraits<double> specialization above for meaning of each of the following
-// members and methods.
-template <>
-struct FloatTraits<float> {
-  static constexpr int kTargetMantissaBits = 24;
-  static constexpr int kMaxExponent = 104;
-  static constexpr int kMinNormalExponent = -149;
-  static float MakeNan(const char* tagp) {
-    // Support nanf no matter which namespace it's in.  Some platforms
-    // incorrectly don't put it in namespace std.
-    using namespace std;  // NOLINT
-    return nanf(tagp);
-  }
-  static float Make(uint32_t mantissa, int exponent, bool sign) {
-#ifndef ABSL_BIT_PACK_FLOATS
-    // Support ldexpf no matter which namespace it's in.  Some platforms
-    // incorrectly don't put it in namespace std.
-    using namespace std;  // NOLINT
-    return sign ? -ldexpf(mantissa, exponent) : ldexpf(mantissa, exponent);
-#else
-    constexpr uint32_t kMantissaMask =
-        (uint32_t(1) << (kTargetMantissaBits - 1)) - 1;
-    uint32_t flt = static_cast<uint32_t>(sign) << 31;
-    if (mantissa > kMantissaMask) {
-      // Normal value.
-      // Adjust by 127 for the exponent representation bias, and an additional
-      // 23 due to the implied decimal point in the IEEE mantissa represenation.
-      flt += uint32_t{exponent + 127u + kTargetMantissaBits - 1} << 23;
-      mantissa &= kMantissaMask;
-    } else {
-      // subnormal value
-      assert(exponent == kMinNormalExponent);
-    }
-    flt += mantissa;
-    return absl::bit_cast<float>(flt);
-#endif  // ABSL_BIT_PACK_FLOATS
-  }
-};
-
-// Decimal-to-binary conversions require coercing powers of 10 into a mantissa
-// and a power of 2.  The two helper functions Power10Mantissa(n) and
-// Power10Exponent(n) perform this task.  Together, these represent a hand-
-// rolled floating point value which is equal to or just less than 10**n.
-//
-// The return values satisfy two range guarantees:
-//
-//   Power10Mantissa(n) * 2**Power10Exponent(n) <= 10**n
-//     < (Power10Mantissa(n) + 1) * 2**Power10Exponent(n)
-//
-//   2**63 <= Power10Mantissa(n) < 2**64.
-//
-// Lookups into the power-of-10 table must first check the Power10Overflow() and
-// Power10Underflow() functions, to avoid out-of-bounds table access.
-//
-// Indexes into these tables are biased by -kPower10TableMin, and the table has
-// values in the range [kPower10TableMin, kPower10TableMax].
-extern const uint64_t kPower10MantissaTable[];
-extern const int16_t kPower10ExponentTable[];
-
-// The smallest allowed value for use with the Power10Mantissa() and
-// Power10Exponent() functions below.  (If a smaller exponent is needed in
-// calculations, the end result is guaranteed to underflow.)
-constexpr int kPower10TableMin = -342;
-
-// The largest allowed value for use with the Power10Mantissa() and
-// Power10Exponent() functions below.  (If a smaller exponent is needed in
-// calculations, the end result is guaranteed to overflow.)
-constexpr int kPower10TableMax = 308;
-
-uint64_t Power10Mantissa(int n) {
-  return kPower10MantissaTable[n - kPower10TableMin];
-}
-
-int Power10Exponent(int n) {
-  return kPower10ExponentTable[n - kPower10TableMin];
-}
-
-// Returns true if n is large enough that 10**n always results in an IEEE
-// overflow.
-bool Power10Overflow(int n) { return n > kPower10TableMax; }
-
-// Returns true if n is small enough that 10**n times a ParsedFloat mantissa
-// always results in an IEEE underflow.
-bool Power10Underflow(int n) { return n < kPower10TableMin; }
-
-// Returns true if Power10Mantissa(n) * 2**Power10Exponent(n) is exactly equal
-// to 10**n numerically.  Put another way, this returns true if there is no
-// truncation error in Power10Mantissa(n).
-bool Power10Exact(int n) { return n >= 0 && n <= 27; }
-
-// Sentinel exponent values for representing numbers too large or too close to
-// zero to represent in a double.
-constexpr int kOverflow = 99999;
-constexpr int kUnderflow = -99999;
-
-// Struct representing the calculated conversion result of a positive (nonzero)
-// floating point number.
-//
-// The calculated number is mantissa * 2**exponent (mantissa is treated as an
-// integer.)  `mantissa` is chosen to be the correct width for the IEEE float
-// representation being calculated.  (`mantissa` will always have the same bit
-// width for normal values, and narrower bit widths for subnormals.)
-//
-// If the result of conversion was an underflow or overflow, exponent is set
-// to kUnderflow or kOverflow.
-struct CalculatedFloat {
-  uint64_t mantissa = 0;
-  int exponent = 0;
-};
-
-// Returns the bit width of the given uint128.  (Equivalently, returns 128
-// minus the number of leading zero bits.)
-int BitWidth(uint128 value) {
-  if (Uint128High64(value) == 0) {
-    return 64 - base_internal::CountLeadingZeros64(Uint128Low64(value));
-  }
-  return 128 - base_internal::CountLeadingZeros64(Uint128High64(value));
-}
-
-// Calculates how far to the right a mantissa needs to be shifted to create a
-// properly adjusted mantissa for an IEEE floating point number.
-//
-// `mantissa_width` is the bit width of the mantissa to be shifted, and
-// `binary_exponent` is the exponent of the number before the shift.
-//
-// This accounts for subnormal values, and will return a larger-than-normal
-// shift if binary_exponent would otherwise be too low.
-template <typename FloatType>
-int NormalizedShiftSize(int mantissa_width, int binary_exponent) {
-  const int normal_shift =
-      mantissa_width - FloatTraits<FloatType>::kTargetMantissaBits;
-  const int minimum_shift =
-      FloatTraits<FloatType>::kMinNormalExponent - binary_exponent;
-  return std::max(normal_shift, minimum_shift);
-}
-
-// Right shifts a uint128 so that it has the requested bit width.  (The
-// resulting value will have 128 - bit_width leading zeroes.)  The initial
-// `value` must be wider than the requested bit width.
-//
-// Returns the number of bits shifted.
-int TruncateToBitWidth(int bit_width, uint128* value) {
-  const int current_bit_width = BitWidth(*value);
-  const int shift = current_bit_width - bit_width;
-  *value >>= shift;
-  return shift;
-}
-
-// Checks if the given ParsedFloat represents one of the edge cases that are
-// not dependent on number base: zero, infinity, or NaN.  If so, sets *value
-// the appropriate double, and returns true.
-template <typename FloatType>
-bool HandleEdgeCase(const strings_internal::ParsedFloat& input, bool negative,
-                    FloatType* value) {
-  if (input.type == strings_internal::FloatType::kNan) {
-    // A bug in both clang and gcc would cause the compiler to optimize away the
-    // buffer we are building below.  Declaring the buffer volatile avoids the
-    // issue, and has no measurable performance impact in microbenchmarks.
-    //
-    // https://bugs.llvm.org/show_bug.cgi?id=37778
-    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86113
-    constexpr ptrdiff_t kNanBufferSize = 128;
-    volatile char n_char_sequence[kNanBufferSize];
-    if (input.subrange_begin == nullptr) {
-      n_char_sequence[0] = '\0';
-    } else {
-      ptrdiff_t nan_size = input.subrange_end - input.subrange_begin;
-      nan_size = std::min(nan_size, kNanBufferSize - 1);
-      std::copy_n(input.subrange_begin, nan_size, n_char_sequence);
-      n_char_sequence[nan_size] = '\0';
-    }
-    char* nan_argument = const_cast<char*>(n_char_sequence);
-    *value = negative ? -FloatTraits<FloatType>::MakeNan(nan_argument)
-                      : FloatTraits<FloatType>::MakeNan(nan_argument);
-    return true;
-  }
-  if (input.type == strings_internal::FloatType::kInfinity) {
-    *value = negative ? -std::numeric_limits<FloatType>::infinity()
-                      : std::numeric_limits<FloatType>::infinity();
-    return true;
-  }
-  if (input.mantissa == 0) {
-    *value = negative ? -0.0 : 0.0;
-    return true;
-  }
-  return false;
-}
-
-// Given a CalculatedFloat result of a from_chars conversion, generate the
-// correct output values.
-//
-// CalculatedFloat can represent an underflow or overflow, in which case the
-// error code in *result is set.  Otherwise, the calculated floating point
-// number is stored in *value.
-template <typename FloatType>
-void EncodeResult(const CalculatedFloat& calculated, bool negative,
-                  absl::from_chars_result* result, FloatType* value) {
-  if (calculated.exponent == kOverflow) {
-    result->ec = std::errc::result_out_of_range;
-    *value = negative ? -std::numeric_limits<FloatType>::max()
-                      : std::numeric_limits<FloatType>::max();
-    return;
-  } else if (calculated.mantissa == 0 || calculated.exponent == kUnderflow) {
-    result->ec = std::errc::result_out_of_range;
-    *value = negative ? -0.0 : 0.0;
-    return;
-  }
-  *value = FloatTraits<FloatType>::Make(calculated.mantissa,
-                                        calculated.exponent, negative);
-}
-
-// Returns the given uint128 shifted to the right by `shift` bits, and rounds
-// the remaining bits using round_to_nearest logic.  The value is returned as a
-// uint64_t, since this is the type used by this library for storing calculated
-// floating point mantissas.
-//
-// It is expected that the width of the input value shifted by `shift` will
-// be the correct bit-width for the target mantissa, which is strictly narrower
-// than a uint64_t.
-//
-// If `input_exact` is false, then a nonzero error epsilon is assumed.  For
-// rounding purposes, the true value being rounded is strictly greater than the
-// input value.  The error may represent a single lost carry bit.
-//
-// When input_exact, shifted bits of the form 1000000... represent a tie, which
-// is broken by rounding to even -- the rounding direction is chosen so the low
-// bit of the returned value is 0.
-//
-// When !input_exact, shifted bits of the form 10000000... represent a value
-// strictly greater than one half (due to the error epsilon), and so ties are
-// always broken by rounding up.
-//
-// When !input_exact, shifted bits of the form 01111111... are uncertain;
-// the true value may or may not be greater than 10000000..., due to the
-// possible lost carry bit.  The correct rounding direction is unknown.  In this
-// case, the result is rounded down, and `output_exact` is set to false.
-//
-// Zero and negative values of `shift` are accepted, in which case the word is
-// shifted left, as necessary.
-uint64_t ShiftRightAndRound(uint128 value, int shift, bool input_exact,
-                            bool* output_exact) {
-  if (shift <= 0) {
-    *output_exact = input_exact;
-    return static_cast<uint64_t>(value << -shift);
-  }
-  if (shift >= 128) {
-    // Exponent is so small that we are shifting away all significant bits.
-    // Answer will not be representable, even as a subnormal, so return a zero
-    // mantissa (which represents underflow).
-    *output_exact = true;
-    return 0;
-  }
-
-  *output_exact = true;
-  const uint128 shift_mask = (uint128(1) << shift) - 1;
-  const uint128 halfway_point = uint128(1) << (shift - 1);
-
-  const uint128 shifted_bits = value & shift_mask;
-  value >>= shift;
-  if (shifted_bits > halfway_point) {
-    // Shifted bits greater than 10000... require rounding up.
-    return static_cast<uint64_t>(value + 1);
-  }
-  if (shifted_bits == halfway_point) {
-    // In exact mode, shifted bits of 10000... mean we're exactly halfway
-    // between two numbers, and we must round to even.  So only round up if
-    // the low bit of `value` is set.
-    //
-    // In inexact mode, the nonzero error means the actual value is greater
-    // than the halfway point and we must alway round up.
-    if ((value & 1) == 1 || !input_exact) {
-      ++value;
-    }
-    return static_cast<uint64_t>(value);
-  }
-  if (!input_exact && shifted_bits == halfway_point - 1) {
-    // Rounding direction is unclear, due to error.
-    *output_exact = false;
-  }
-  // Otherwise, round down.
-  return static_cast<uint64_t>(value);
-}
-
-// Checks if a floating point guess needs to be rounded up, using high precision
-// math.
-//
-// `guess_mantissa` and `guess_exponent` represent a candidate guess for the
-// number represented by `parsed_decimal`.
-//
-// The exact number represented by `parsed_decimal` must lie between the two
-// numbers:
-//   A = `guess_mantissa * 2**guess_exponent`
-//   B = `(guess_mantissa + 1) * 2**guess_exponent`
-//
-// This function returns false if `A` is the better guess, and true if `B` is
-// the better guess, with rounding ties broken by rounding to even.
-bool MustRoundUp(uint64_t guess_mantissa, int guess_exponent,
-                 const strings_internal::ParsedFloat& parsed_decimal) {
-  // 768 is the number of digits needed in the worst case.  We could determine a
-  // better limit dynamically based on the value of parsed_decimal.exponent.
-  // This would optimize pathological input cases only.  (Sane inputs won't have
-  // hundreds of digits of mantissa.)
-  absl::strings_internal::BigUnsigned<84> exact_mantissa;
-  int exact_exponent = exact_mantissa.ReadFloatMantissa(parsed_decimal, 768);
-
-  // Adjust the `guess` arguments to be halfway between A and B.
-  guess_mantissa = guess_mantissa * 2 + 1;
-  guess_exponent -= 1;
-
-  // In our comparison:
-  // lhs = exact = exact_mantissa * 10**exact_exponent
-  //             = exact_mantissa * 5**exact_exponent * 2**exact_exponent
-  // rhs = guess = guess_mantissa * 2**guess_exponent
-  //
-  // Because we are doing integer math, we can't directly deal with negative
-  // exponents.  We instead move these to the other side of the inequality.
-  absl::strings_internal::BigUnsigned<84>& lhs = exact_mantissa;
-  int comparison;
-  if (exact_exponent >= 0) {
-    lhs.MultiplyByFiveToTheNth(exact_exponent);
-    absl::strings_internal::BigUnsigned<84> rhs(guess_mantissa);
-    // There are powers of 2 on both sides of the inequality; reduce this to
-    // a single bit-shift.
-    if (exact_exponent > guess_exponent) {
-      lhs.ShiftLeft(exact_exponent - guess_exponent);
-    } else {
-      rhs.ShiftLeft(guess_exponent - exact_exponent);
-    }
-    comparison = Compare(lhs, rhs);
-  } else {
-    // Move the power of 5 to the other side of the equation, giving us:
-    // lhs = exact_mantissa * 2**exact_exponent
-    // rhs = guess_mantissa * 5**(-exact_exponent) * 2**guess_exponent
-    absl::strings_internal::BigUnsigned<84> rhs =
-        absl::strings_internal::BigUnsigned<84>::FiveToTheNth(-exact_exponent);
-    rhs.MultiplyBy(guess_mantissa);
-    if (exact_exponent > guess_exponent) {
-      lhs.ShiftLeft(exact_exponent - guess_exponent);
-    } else {
-      rhs.ShiftLeft(guess_exponent - exact_exponent);
-    }
-    comparison = Compare(lhs, rhs);
-  }
-  if (comparison < 0) {
-    return false;
-  } else if (comparison > 0) {
-    return true;
-  } else {
-    // When lhs == rhs, the decimal input is exactly between A and B.
-    // Round towards even -- round up only if the low bit of the initial
-    // `guess_mantissa` was a 1.  We shifted guess_mantissa left 1 bit at
-    // the beginning of this function, so test the 2nd bit here.
-    return (guess_mantissa & 2) == 2;
-  }
-}
-
-// Constructs a CalculatedFloat from a given mantissa and exponent, but
-// with the following normalizations applied:
-//
-// If rounding has caused mantissa to increase just past the allowed bit
-// width, shift and adjust exponent.
-//
-// If exponent is too high, sets kOverflow.
-//
-// If mantissa is zero (representing a non-zero value not representable, even
-// as a subnormal), sets kUnderflow.
-template <typename FloatType>
-CalculatedFloat CalculatedFloatFromRawValues(uint64_t mantissa, int exponent) {
-  CalculatedFloat result;
-  if (mantissa == uint64_t(1) << FloatTraits<FloatType>::kTargetMantissaBits) {
-    mantissa >>= 1;
-    exponent += 1;
-  }
-  if (exponent > FloatTraits<FloatType>::kMaxExponent) {
-    result.exponent = kOverflow;
-  } else if (mantissa == 0) {
-    result.exponent = kUnderflow;
-  } else {
-    result.exponent = exponent;
-    result.mantissa = mantissa;
-  }
-  return result;
-}
-
-template <typename FloatType>
-CalculatedFloat CalculateFromParsedHexadecimal(
-    const strings_internal::ParsedFloat& parsed_hex) {
-  uint64_t mantissa = parsed_hex.mantissa;
-  int exponent = parsed_hex.exponent;
-  int mantissa_width = 64 - base_internal::CountLeadingZeros64(mantissa);
-  const int shift = NormalizedShiftSize<FloatType>(mantissa_width, exponent);
-  bool result_exact;
-  exponent += shift;
-  mantissa = ShiftRightAndRound(mantissa, shift,
-                                /* input exact= */ true, &result_exact);
-  // ParseFloat handles rounding in the hexadecimal case, so we don't have to
-  // check `result_exact` here.
-  return CalculatedFloatFromRawValues<FloatType>(mantissa, exponent);
-}
-
-template <typename FloatType>
-CalculatedFloat CalculateFromParsedDecimal(
-    const strings_internal::ParsedFloat& parsed_decimal) {
-  CalculatedFloat result;
-
-  // Large or small enough decimal exponents will always result in overflow
-  // or underflow.
-  if (Power10Underflow(parsed_decimal.exponent)) {
-    result.exponent = kUnderflow;
-    return result;
-  } else if (Power10Overflow(parsed_decimal.exponent)) {
-    result.exponent = kOverflow;
-    return result;
-  }
-
-  // Otherwise convert our power of 10 into a power of 2 times an integer
-  // mantissa, and multiply this by our parsed decimal mantissa.
-  uint128 wide_binary_mantissa = parsed_decimal.mantissa;
-  wide_binary_mantissa *= Power10Mantissa(parsed_decimal.exponent);
-  int binary_exponent = Power10Exponent(parsed_decimal.exponent);
-
-  // Discard bits that are inaccurate due to truncation error.  The magic
-  // `mantissa_width` constants below are justified in
-  // https://abseil.io/about/design/charconv. They represent the number of bits
-  // in `wide_binary_mantissa` that are guaranteed to be unaffected by error
-  // propagation.
-  bool mantissa_exact;
-  int mantissa_width;
-  if (parsed_decimal.subrange_begin) {
-    // Truncated mantissa
-    mantissa_width = 58;
-    mantissa_exact = false;
-    binary_exponent +=
-        TruncateToBitWidth(mantissa_width, &wide_binary_mantissa);
-  } else if (!Power10Exact(parsed_decimal.exponent)) {
-    // Exact mantissa, truncated power of ten
-    mantissa_width = 63;
-    mantissa_exact = false;
-    binary_exponent +=
-        TruncateToBitWidth(mantissa_width, &wide_binary_mantissa);
-  } else {
-    // Product is exact
-    mantissa_width = BitWidth(wide_binary_mantissa);
-    mantissa_exact = true;
-  }
-
-  // Shift into an FloatType-sized mantissa, and round to nearest.
-  const int shift =
-      NormalizedShiftSize<FloatType>(mantissa_width, binary_exponent);
-  bool result_exact;
-  binary_exponent += shift;
-  uint64_t binary_mantissa = ShiftRightAndRound(wide_binary_mantissa, shift,
-                                                mantissa_exact, &result_exact);
-  if (!result_exact) {
-    // We could not determine the rounding direction using int128 math.  Use
-    // full resolution math instead.
-    if (MustRoundUp(binary_mantissa, binary_exponent, parsed_decimal)) {
-      binary_mantissa += 1;
-    }
-  }
-
-  return CalculatedFloatFromRawValues<FloatType>(binary_mantissa,
-                                                 binary_exponent);
-}
-
-template <typename FloatType>
-from_chars_result FromCharsImpl(const char* first, const char* last,
-                                FloatType& value, chars_format fmt_flags) {
-  from_chars_result result;
-  result.ptr = first;  // overwritten on successful parse
-  result.ec = std::errc();
-
-  bool negative = false;
-  if (first != last && *first == '-') {
-    ++first;
-    negative = true;
-  }
-  // If the `hex` flag is *not* set, then we will accept a 0x prefix and try
-  // to parse a hexadecimal float.
-  if ((fmt_flags & chars_format::hex) == chars_format{} && last - first >= 2 &&
-      *first == '0' && (first[1] == 'x' || first[1] == 'X')) {
-    const char* hex_first = first + 2;
-    strings_internal::ParsedFloat hex_parse =
-        strings_internal::ParseFloat<16>(hex_first, last, fmt_flags);
-    if (hex_parse.end == nullptr ||
-        hex_parse.type != strings_internal::FloatType::kNumber) {
-      // Either we failed to parse a hex float after the "0x", or we read
-      // "0xinf" or "0xnan" which we don't want to match.
-      //
-      // However, a string that begins with "0x" also begins with "0", which
-      // is normally a valid match for the number zero.  So we want these
-      // strings to match zero unless fmt_flags is `scientific`.  (This flag
-      // means an exponent is required, which the string "0" does not have.)
-      if (fmt_flags == chars_format::scientific) {
-        result.ec = std::errc::invalid_argument;
-      } else {
-        result.ptr = first + 1;
-        value = negative ? -0.0 : 0.0;
-      }
-      return result;
-    }
-    // We matched a value.
-    result.ptr = hex_parse.end;
-    if (HandleEdgeCase(hex_parse, negative, &value)) {
-      return result;
-    }
-    CalculatedFloat calculated =
-        CalculateFromParsedHexadecimal<FloatType>(hex_parse);
-    EncodeResult(calculated, negative, &result, &value);
-    return result;
-  }
-  // Otherwise, we choose the number base based on the flags.
-  if ((fmt_flags & chars_format::hex) == chars_format::hex) {
-    strings_internal::ParsedFloat hex_parse =
-        strings_internal::ParseFloat<16>(first, last, fmt_flags);
-    if (hex_parse.end == nullptr) {
-      result.ec = std::errc::invalid_argument;
-      return result;
-    }
-    result.ptr = hex_parse.end;
-    if (HandleEdgeCase(hex_parse, negative, &value)) {
-      return result;
-    }
-    CalculatedFloat calculated =
-        CalculateFromParsedHexadecimal<FloatType>(hex_parse);
-    EncodeResult(calculated, negative, &result, &value);
-    return result;
-  } else {
-    strings_internal::ParsedFloat decimal_parse =
-        strings_internal::ParseFloat<10>(first, last, fmt_flags);
-    if (decimal_parse.end == nullptr) {
-      result.ec = std::errc::invalid_argument;
-      return result;
-    }
-    result.ptr = decimal_parse.end;
-    if (HandleEdgeCase(decimal_parse, negative, &value)) {
-      return result;
-    }
-    CalculatedFloat calculated =
-        CalculateFromParsedDecimal<FloatType>(decimal_parse);
-    EncodeResult(calculated, negative, &result, &value);
-    return result;
-  }
-}
-}  // namespace
-
-from_chars_result from_chars(const char* first, const char* last, double& value,
-                             chars_format fmt) {
-  return FromCharsImpl(first, last, value, fmt);
-}
-
-from_chars_result from_chars(const char* first, const char* last, float& value,
-                             chars_format fmt) {
-  return FromCharsImpl(first, last, value, fmt);
-}
-
-namespace {
-
-// Table of powers of 10, from kPower10TableMin to kPower10TableMax.
-//
-// kPower10MantissaTable[i - kPower10TableMin] stores the 64-bit mantissa (high
-// bit always on), and kPower10ExponentTable[i - kPower10TableMin] stores the
-// power-of-two exponent.  For a given number i, this gives the unique mantissa
-// and exponent such that mantissa * 2**exponent <= 10**i < (mantissa + 1) *
-// 2**exponent.
-
-const uint64_t kPower10MantissaTable[] = {
-    0xeef453d6923bd65aU, 0x9558b4661b6565f8U, 0xbaaee17fa23ebf76U,
-    0xe95a99df8ace6f53U, 0x91d8a02bb6c10594U, 0xb64ec836a47146f9U,
-    0xe3e27a444d8d98b7U, 0x8e6d8c6ab0787f72U, 0xb208ef855c969f4fU,
-    0xde8b2b66b3bc4723U, 0x8b16fb203055ac76U, 0xaddcb9e83c6b1793U,
-    0xd953e8624b85dd78U, 0x87d4713d6f33aa6bU, 0xa9c98d8ccb009506U,
-    0xd43bf0effdc0ba48U, 0x84a57695fe98746dU, 0xa5ced43b7e3e9188U,
-    0xcf42894a5dce35eaU, 0x818995ce7aa0e1b2U, 0xa1ebfb4219491a1fU,
-    0xca66fa129f9b60a6U, 0xfd00b897478238d0U, 0x9e20735e8cb16382U,
-    0xc5a890362fddbc62U, 0xf712b443bbd52b7bU, 0x9a6bb0aa55653b2dU,
-    0xc1069cd4eabe89f8U, 0xf148440a256e2c76U, 0x96cd2a865764dbcaU,
-    0xbc807527ed3e12bcU, 0xeba09271e88d976bU, 0x93445b8731587ea3U,
-    0xb8157268fdae9e4cU, 0xe61acf033d1a45dfU, 0x8fd0c16206306babU,
-    0xb3c4f1ba87bc8696U, 0xe0b62e2929aba83cU, 0x8c71dcd9ba0b4925U,
-    0xaf8e5410288e1b6fU, 0xdb71e91432b1a24aU, 0x892731ac9faf056eU,
-    0xab70fe17c79ac6caU, 0xd64d3d9db981787dU, 0x85f0468293f0eb4eU,
-    0xa76c582338ed2621U, 0xd1476e2c07286faaU, 0x82cca4db847945caU,
-    0xa37fce126597973cU, 0xcc5fc196fefd7d0cU, 0xff77b1fcbebcdc4fU,
-    0x9faacf3df73609b1U, 0xc795830d75038c1dU, 0xf97ae3d0d2446f25U,
-    0x9becce62836ac577U, 0xc2e801fb244576d5U, 0xf3a20279ed56d48aU,
-    0x9845418c345644d6U, 0xbe5691ef416bd60cU, 0xedec366b11c6cb8fU,
-    0x94b3a202eb1c3f39U, 0xb9e08a83a5e34f07U, 0xe858ad248f5c22c9U,
-    0x91376c36d99995beU, 0xb58547448ffffb2dU, 0xe2e69915b3fff9f9U,
-    0x8dd01fad907ffc3bU, 0xb1442798f49ffb4aU, 0xdd95317f31c7fa1dU,
-    0x8a7d3eef7f1cfc52U, 0xad1c8eab5ee43b66U, 0xd863b256369d4a40U,
-    0x873e4f75e2224e68U, 0xa90de3535aaae202U, 0xd3515c2831559a83U,
-    0x8412d9991ed58091U, 0xa5178fff668ae0b6U, 0xce5d73ff402d98e3U,
-    0x80fa687f881c7f8eU, 0xa139029f6a239f72U, 0xc987434744ac874eU,
-    0xfbe9141915d7a922U, 0x9d71ac8fada6c9b5U, 0xc4ce17b399107c22U,
-    0xf6019da07f549b2bU, 0x99c102844f94e0fbU, 0xc0314325637a1939U,
-    0xf03d93eebc589f88U, 0x96267c7535b763b5U, 0xbbb01b9283253ca2U,
-    0xea9c227723ee8bcbU, 0x92a1958a7675175fU, 0xb749faed14125d36U,
-    0xe51c79a85916f484U, 0x8f31cc0937ae58d2U, 0xb2fe3f0b8599ef07U,
-    0xdfbdcece67006ac9U, 0x8bd6a141006042bdU, 0xaecc49914078536dU,
-    0xda7f5bf590966848U, 0x888f99797a5e012dU, 0xaab37fd7d8f58178U,
-    0xd5605fcdcf32e1d6U, 0x855c3be0a17fcd26U, 0xa6b34ad8c9dfc06fU,
-    0xd0601d8efc57b08bU, 0x823c12795db6ce57U, 0xa2cb1717b52481edU,
-    0xcb7ddcdda26da268U, 0xfe5d54150b090b02U, 0x9efa548d26e5a6e1U,
-    0xc6b8e9b0709f109aU, 0xf867241c8cc6d4c0U, 0x9b407691d7fc44f8U,
-    0xc21094364dfb5636U, 0xf294b943e17a2bc4U, 0x979cf3ca6cec5b5aU,
-    0xbd8430bd08277231U, 0xece53cec4a314ebdU, 0x940f4613ae5ed136U,
-    0xb913179899f68584U, 0xe757dd7ec07426e5U, 0x9096ea6f3848984fU,
-    0xb4bca50b065abe63U, 0xe1ebce4dc7f16dfbU, 0x8d3360f09cf6e4bdU,
-    0xb080392cc4349decU, 0xdca04777f541c567U, 0x89e42caaf9491b60U,
-    0xac5d37d5b79b6239U, 0xd77485cb25823ac7U, 0x86a8d39ef77164bcU,
-    0xa8530886b54dbdebU, 0xd267caa862a12d66U, 0x8380dea93da4bc60U,
-    0xa46116538d0deb78U, 0xcd795be870516656U, 0x806bd9714632dff6U,
-    0xa086cfcd97bf97f3U, 0xc8a883c0fdaf7df0U, 0xfad2a4b13d1b5d6cU,
-    0x9cc3a6eec6311a63U, 0xc3f490aa77bd60fcU, 0xf4f1b4d515acb93bU,
-    0x991711052d8bf3c5U, 0xbf5cd54678eef0b6U, 0xef340a98172aace4U,
-    0x9580869f0e7aac0eU, 0xbae0a846d2195712U, 0xe998d258869facd7U,
-    0x91ff83775423cc06U, 0xb67f6455292cbf08U, 0xe41f3d6a7377eecaU,
-    0x8e938662882af53eU, 0xb23867fb2a35b28dU, 0xdec681f9f4c31f31U,
-    0x8b3c113c38f9f37eU, 0xae0b158b4738705eU, 0xd98ddaee19068c76U,
-    0x87f8a8d4cfa417c9U, 0xa9f6d30a038d1dbcU, 0xd47487cc8470652bU,
-    0x84c8d4dfd2c63f3bU, 0xa5fb0a17c777cf09U, 0xcf79cc9db955c2ccU,
-    0x81ac1fe293d599bfU, 0xa21727db38cb002fU, 0xca9cf1d206fdc03bU,
-    0xfd442e4688bd304aU, 0x9e4a9cec15763e2eU, 0xc5dd44271ad3cdbaU,
-    0xf7549530e188c128U, 0x9a94dd3e8cf578b9U, 0xc13a148e3032d6e7U,
-    0xf18899b1bc3f8ca1U, 0x96f5600f15a7b7e5U, 0xbcb2b812db11a5deU,
-    0xebdf661791d60f56U, 0x936b9fcebb25c995U, 0xb84687c269ef3bfbU,
-    0xe65829b3046b0afaU, 0x8ff71a0fe2c2e6dcU, 0xb3f4e093db73a093U,
-    0xe0f218b8d25088b8U, 0x8c974f7383725573U, 0xafbd2350644eeacfU,
-    0xdbac6c247d62a583U, 0x894bc396ce5da772U, 0xab9eb47c81f5114fU,
-    0xd686619ba27255a2U, 0x8613fd0145877585U, 0xa798fc4196e952e7U,
-    0xd17f3b51fca3a7a0U, 0x82ef85133de648c4U, 0xa3ab66580d5fdaf5U,
-    0xcc963fee10b7d1b3U, 0xffbbcfe994e5c61fU, 0x9fd561f1fd0f9bd3U,
-    0xc7caba6e7c5382c8U, 0xf9bd690a1b68637bU, 0x9c1661a651213e2dU,
-    0xc31bfa0fe5698db8U, 0xf3e2f893dec3f126U, 0x986ddb5c6b3a76b7U,
-    0xbe89523386091465U, 0xee2ba6c0678b597fU, 0x94db483840b717efU,
-    0xba121a4650e4ddebU, 0xe896a0d7e51e1566U, 0x915e2486ef32cd60U,
-    0xb5b5ada8aaff80b8U, 0xe3231912d5bf60e6U, 0x8df5efabc5979c8fU,
-    0xb1736b96b6fd83b3U, 0xddd0467c64bce4a0U, 0x8aa22c0dbef60ee4U,
-    0xad4ab7112eb3929dU, 0xd89d64d57a607744U, 0x87625f056c7c4a8bU,
-    0xa93af6c6c79b5d2dU, 0xd389b47879823479U, 0x843610cb4bf160cbU,
-    0xa54394fe1eedb8feU, 0xce947a3da6a9273eU, 0x811ccc668829b887U,
-    0xa163ff802a3426a8U, 0xc9bcff6034c13052U, 0xfc2c3f3841f17c67U,
-    0x9d9ba7832936edc0U, 0xc5029163f384a931U, 0xf64335bcf065d37dU,
-    0x99ea0196163fa42eU, 0xc06481fb9bcf8d39U, 0xf07da27a82c37088U,
-    0x964e858c91ba2655U, 0xbbe226efb628afeaU, 0xeadab0aba3b2dbe5U,
-    0x92c8ae6b464fc96fU, 0xb77ada0617e3bbcbU, 0xe55990879ddcaabdU,
-    0x8f57fa54c2a9eab6U, 0xb32df8e9f3546564U, 0xdff9772470297ebdU,
-    0x8bfbea76c619ef36U, 0xaefae51477a06b03U, 0xdab99e59958885c4U,
-    0x88b402f7fd75539bU, 0xaae103b5fcd2a881U, 0xd59944a37c0752a2U,
-    0x857fcae62d8493a5U, 0xa6dfbd9fb8e5b88eU, 0xd097ad07a71f26b2U,
-    0x825ecc24c873782fU, 0xa2f67f2dfa90563bU, 0xcbb41ef979346bcaU,
-    0xfea126b7d78186bcU, 0x9f24b832e6b0f436U, 0xc6ede63fa05d3143U,
-    0xf8a95fcf88747d94U, 0x9b69dbe1b548ce7cU, 0xc24452da229b021bU,
-    0xf2d56790ab41c2a2U, 0x97c560ba6b0919a5U, 0xbdb6b8e905cb600fU,
-    0xed246723473e3813U, 0x9436c0760c86e30bU, 0xb94470938fa89bceU,
-    0xe7958cb87392c2c2U, 0x90bd77f3483bb9b9U, 0xb4ecd5f01a4aa828U,
-    0xe2280b6c20dd5232U, 0x8d590723948a535fU, 0xb0af48ec79ace837U,
-    0xdcdb1b2798182244U, 0x8a08f0f8bf0f156bU, 0xac8b2d36eed2dac5U,
-    0xd7adf884aa879177U, 0x86ccbb52ea94baeaU, 0xa87fea27a539e9a5U,
-    0xd29fe4b18e88640eU, 0x83a3eeeef9153e89U, 0xa48ceaaab75a8e2bU,
-    0xcdb02555653131b6U, 0x808e17555f3ebf11U, 0xa0b19d2ab70e6ed6U,
-    0xc8de047564d20a8bU, 0xfb158592be068d2eU, 0x9ced737bb6c4183dU,
-    0xc428d05aa4751e4cU, 0xf53304714d9265dfU, 0x993fe2c6d07b7fabU,
-    0xbf8fdb78849a5f96U, 0xef73d256a5c0f77cU, 0x95a8637627989aadU,
-    0xbb127c53b17ec159U, 0xe9d71b689dde71afU, 0x9226712162ab070dU,
-    0xb6b00d69bb55c8d1U, 0xe45c10c42a2b3b05U, 0x8eb98a7a9a5b04e3U,
-    0xb267ed1940f1c61cU, 0xdf01e85f912e37a3U, 0x8b61313bbabce2c6U,
-    0xae397d8aa96c1b77U, 0xd9c7dced53c72255U, 0x881cea14545c7575U,
-    0xaa242499697392d2U, 0xd4ad2dbfc3d07787U, 0x84ec3c97da624ab4U,
-    0xa6274bbdd0fadd61U, 0xcfb11ead453994baU, 0x81ceb32c4b43fcf4U,
-    0xa2425ff75e14fc31U, 0xcad2f7f5359a3b3eU, 0xfd87b5f28300ca0dU,
-    0x9e74d1b791e07e48U, 0xc612062576589ddaU, 0xf79687aed3eec551U,
-    0x9abe14cd44753b52U, 0xc16d9a0095928a27U, 0xf1c90080baf72cb1U,
-    0x971da05074da7beeU, 0xbce5086492111aeaU, 0xec1e4a7db69561a5U,
-    0x9392ee8e921d5d07U, 0xb877aa3236a4b449U, 0xe69594bec44de15bU,
-    0x901d7cf73ab0acd9U, 0xb424dc35095cd80fU, 0xe12e13424bb40e13U,
-    0x8cbccc096f5088cbU, 0xafebff0bcb24aafeU, 0xdbe6fecebdedd5beU,
-    0x89705f4136b4a597U, 0xabcc77118461cefcU, 0xd6bf94d5e57a42bcU,
-    0x8637bd05af6c69b5U, 0xa7c5ac471b478423U, 0xd1b71758e219652bU,
-    0x83126e978d4fdf3bU, 0xa3d70a3d70a3d70aU, 0xccccccccccccccccU,
-    0x8000000000000000U, 0xa000000000000000U, 0xc800000000000000U,
-    0xfa00000000000000U, 0x9c40000000000000U, 0xc350000000000000U,
-    0xf424000000000000U, 0x9896800000000000U, 0xbebc200000000000U,
-    0xee6b280000000000U, 0x9502f90000000000U, 0xba43b74000000000U,
-    0xe8d4a51000000000U, 0x9184e72a00000000U, 0xb5e620f480000000U,
-    0xe35fa931a0000000U, 0x8e1bc9bf04000000U, 0xb1a2bc2ec5000000U,
-    0xde0b6b3a76400000U, 0x8ac7230489e80000U, 0xad78ebc5ac620000U,
-    0xd8d726b7177a8000U, 0x878678326eac9000U, 0xa968163f0a57b400U,
-    0xd3c21bcecceda100U, 0x84595161401484a0U, 0xa56fa5b99019a5c8U,
-    0xcecb8f27f4200f3aU, 0x813f3978f8940984U, 0xa18f07d736b90be5U,
-    0xc9f2c9cd04674edeU, 0xfc6f7c4045812296U, 0x9dc5ada82b70b59dU,
-    0xc5371912364ce305U, 0xf684df56c3e01bc6U, 0x9a130b963a6c115cU,
-    0xc097ce7bc90715b3U, 0xf0bdc21abb48db20U, 0x96769950b50d88f4U,
-    0xbc143fa4e250eb31U, 0xeb194f8e1ae525fdU, 0x92efd1b8d0cf37beU,
-    0xb7abc627050305adU, 0xe596b7b0c643c719U, 0x8f7e32ce7bea5c6fU,
-    0xb35dbf821ae4f38bU, 0xe0352f62a19e306eU, 0x8c213d9da502de45U,
-    0xaf298d050e4395d6U, 0xdaf3f04651d47b4cU, 0x88d8762bf324cd0fU,
-    0xab0e93b6efee0053U, 0xd5d238a4abe98068U, 0x85a36366eb71f041U,
-    0xa70c3c40a64e6c51U, 0xd0cf4b50cfe20765U, 0x82818f1281ed449fU,
-    0xa321f2d7226895c7U, 0xcbea6f8ceb02bb39U, 0xfee50b7025c36a08U,
-    0x9f4f2726179a2245U, 0xc722f0ef9d80aad6U, 0xf8ebad2b84e0d58bU,
-    0x9b934c3b330c8577U, 0xc2781f49ffcfa6d5U, 0xf316271c7fc3908aU,
-    0x97edd871cfda3a56U, 0xbde94e8e43d0c8ecU, 0xed63a231d4c4fb27U,
-    0x945e455f24fb1cf8U, 0xb975d6b6ee39e436U, 0xe7d34c64a9c85d44U,
-    0x90e40fbeea1d3a4aU, 0xb51d13aea4a488ddU, 0xe264589a4dcdab14U,
-    0x8d7eb76070a08aecU, 0xb0de65388cc8ada8U, 0xdd15fe86affad912U,
-    0x8a2dbf142dfcc7abU, 0xacb92ed9397bf996U, 0xd7e77a8f87daf7fbU,
-    0x86f0ac99b4e8dafdU, 0xa8acd7c0222311bcU, 0xd2d80db02aabd62bU,
-    0x83c7088e1aab65dbU, 0xa4b8cab1a1563f52U, 0xcde6fd5e09abcf26U,
-    0x80b05e5ac60b6178U, 0xa0dc75f1778e39d6U, 0xc913936dd571c84cU,
-    0xfb5878494ace3a5fU, 0x9d174b2dcec0e47bU, 0xc45d1df942711d9aU,
-    0xf5746577930d6500U, 0x9968bf6abbe85f20U, 0xbfc2ef456ae276e8U,
-    0xefb3ab16c59b14a2U, 0x95d04aee3b80ece5U, 0xbb445da9ca61281fU,
-    0xea1575143cf97226U, 0x924d692ca61be758U, 0xb6e0c377cfa2e12eU,
-    0xe498f455c38b997aU, 0x8edf98b59a373fecU, 0xb2977ee300c50fe7U,
-    0xdf3d5e9bc0f653e1U, 0x8b865b215899f46cU, 0xae67f1e9aec07187U,
-    0xda01ee641a708de9U, 0x884134fe908658b2U, 0xaa51823e34a7eedeU,
-    0xd4e5e2cdc1d1ea96U, 0x850fadc09923329eU, 0xa6539930bf6bff45U,
-    0xcfe87f7cef46ff16U, 0x81f14fae158c5f6eU, 0xa26da3999aef7749U,
-    0xcb090c8001ab551cU, 0xfdcb4fa002162a63U, 0x9e9f11c4014dda7eU,
-    0xc646d63501a1511dU, 0xf7d88bc24209a565U, 0x9ae757596946075fU,
-    0xc1a12d2fc3978937U, 0xf209787bb47d6b84U, 0x9745eb4d50ce6332U,
-    0xbd176620a501fbffU, 0xec5d3fa8ce427affU, 0x93ba47c980e98cdfU,
-    0xb8a8d9bbe123f017U, 0xe6d3102ad96cec1dU, 0x9043ea1ac7e41392U,
-    0xb454e4a179dd1877U, 0xe16a1dc9d8545e94U, 0x8ce2529e2734bb1dU,
-    0xb01ae745b101e9e4U, 0xdc21a1171d42645dU, 0x899504ae72497ebaU,
-    0xabfa45da0edbde69U, 0xd6f8d7509292d603U, 0x865b86925b9bc5c2U,
-    0xa7f26836f282b732U, 0xd1ef0244af2364ffU, 0x8335616aed761f1fU,
-    0xa402b9c5a8d3a6e7U, 0xcd036837130890a1U, 0x802221226be55a64U,
-    0xa02aa96b06deb0fdU, 0xc83553c5c8965d3dU, 0xfa42a8b73abbf48cU,
-    0x9c69a97284b578d7U, 0xc38413cf25e2d70dU, 0xf46518c2ef5b8cd1U,
-    0x98bf2f79d5993802U, 0xbeeefb584aff8603U, 0xeeaaba2e5dbf6784U,
-    0x952ab45cfa97a0b2U, 0xba756174393d88dfU, 0xe912b9d1478ceb17U,
-    0x91abb422ccb812eeU, 0xb616a12b7fe617aaU, 0xe39c49765fdf9d94U,
-    0x8e41ade9fbebc27dU, 0xb1d219647ae6b31cU, 0xde469fbd99a05fe3U,
-    0x8aec23d680043beeU, 0xada72ccc20054ae9U, 0xd910f7ff28069da4U,
-    0x87aa9aff79042286U, 0xa99541bf57452b28U, 0xd3fa922f2d1675f2U,
-    0x847c9b5d7c2e09b7U, 0xa59bc234db398c25U, 0xcf02b2c21207ef2eU,
-    0x8161afb94b44f57dU, 0xa1ba1ba79e1632dcU, 0xca28a291859bbf93U,
-    0xfcb2cb35e702af78U, 0x9defbf01b061adabU, 0xc56baec21c7a1916U,
-    0xf6c69a72a3989f5bU, 0x9a3c2087a63f6399U, 0xc0cb28a98fcf3c7fU,
-    0xf0fdf2d3f3c30b9fU, 0x969eb7c47859e743U, 0xbc4665b596706114U,
-    0xeb57ff22fc0c7959U, 0x9316ff75dd87cbd8U, 0xb7dcbf5354e9beceU,
-    0xe5d3ef282a242e81U, 0x8fa475791a569d10U, 0xb38d92d760ec4455U,
-    0xe070f78d3927556aU, 0x8c469ab843b89562U, 0xaf58416654a6babbU,
-    0xdb2e51bfe9d0696aU, 0x88fcf317f22241e2U, 0xab3c2fddeeaad25aU,
-    0xd60b3bd56a5586f1U, 0x85c7056562757456U, 0xa738c6bebb12d16cU,
-    0xd106f86e69d785c7U, 0x82a45b450226b39cU, 0xa34d721642b06084U,
-    0xcc20ce9bd35c78a5U, 0xff290242c83396ceU, 0x9f79a169bd203e41U,
-    0xc75809c42c684dd1U, 0xf92e0c3537826145U, 0x9bbcc7a142b17ccbU,
-    0xc2abf989935ddbfeU, 0xf356f7ebf83552feU, 0x98165af37b2153deU,
-    0xbe1bf1b059e9a8d6U, 0xeda2ee1c7064130cU, 0x9485d4d1c63e8be7U,
-    0xb9a74a0637ce2ee1U, 0xe8111c87c5c1ba99U, 0x910ab1d4db9914a0U,
-    0xb54d5e4a127f59c8U, 0xe2a0b5dc971f303aU, 0x8da471a9de737e24U,
-    0xb10d8e1456105dadU, 0xdd50f1996b947518U, 0x8a5296ffe33cc92fU,
-    0xace73cbfdc0bfb7bU, 0xd8210befd30efa5aU, 0x8714a775e3e95c78U,
-    0xa8d9d1535ce3b396U, 0xd31045a8341ca07cU, 0x83ea2b892091e44dU,
-    0xa4e4b66b68b65d60U, 0xce1de40642e3f4b9U, 0x80d2ae83e9ce78f3U,
-    0xa1075a24e4421730U, 0xc94930ae1d529cfcU, 0xfb9b7cd9a4a7443cU,
-    0x9d412e0806e88aa5U, 0xc491798a08a2ad4eU, 0xf5b5d7ec8acb58a2U,
-    0x9991a6f3d6bf1765U, 0xbff610b0cc6edd3fU, 0xeff394dcff8a948eU,
-    0x95f83d0a1fb69cd9U, 0xbb764c4ca7a4440fU, 0xea53df5fd18d5513U,
-    0x92746b9be2f8552cU, 0xb7118682dbb66a77U, 0xe4d5e82392a40515U,
-    0x8f05b1163ba6832dU, 0xb2c71d5bca9023f8U, 0xdf78e4b2bd342cf6U,
-    0x8bab8eefb6409c1aU, 0xae9672aba3d0c320U, 0xda3c0f568cc4f3e8U,
-    0x8865899617fb1871U, 0xaa7eebfb9df9de8dU, 0xd51ea6fa85785631U,
-    0x8533285c936b35deU, 0xa67ff273b8460356U, 0xd01fef10a657842cU,
-    0x8213f56a67f6b29bU, 0xa298f2c501f45f42U, 0xcb3f2f7642717713U,
-    0xfe0efb53d30dd4d7U, 0x9ec95d1463e8a506U, 0xc67bb4597ce2ce48U,
-    0xf81aa16fdc1b81daU, 0x9b10a4e5e9913128U, 0xc1d4ce1f63f57d72U,
-    0xf24a01a73cf2dccfU, 0x976e41088617ca01U, 0xbd49d14aa79dbc82U,
-    0xec9c459d51852ba2U, 0x93e1ab8252f33b45U, 0xb8da1662e7b00a17U,
-    0xe7109bfba19c0c9dU, 0x906a617d450187e2U, 0xb484f9dc9641e9daU,
-    0xe1a63853bbd26451U, 0x8d07e33455637eb2U, 0xb049dc016abc5e5fU,
-    0xdc5c5301c56b75f7U, 0x89b9b3e11b6329baU, 0xac2820d9623bf429U,
-    0xd732290fbacaf133U, 0x867f59a9d4bed6c0U, 0xa81f301449ee8c70U,
-    0xd226fc195c6a2f8cU, 0x83585d8fd9c25db7U, 0xa42e74f3d032f525U,
-    0xcd3a1230c43fb26fU, 0x80444b5e7aa7cf85U, 0xa0555e361951c366U,
-    0xc86ab5c39fa63440U, 0xfa856334878fc150U, 0x9c935e00d4b9d8d2U,
-    0xc3b8358109e84f07U, 0xf4a642e14c6262c8U, 0x98e7e9cccfbd7dbdU,
-    0xbf21e44003acdd2cU, 0xeeea5d5004981478U, 0x95527a5202df0ccbU,
-    0xbaa718e68396cffdU, 0xe950df20247c83fdU, 0x91d28b7416cdd27eU,
-    0xb6472e511c81471dU, 0xe3d8f9e563a198e5U, 0x8e679c2f5e44ff8fU,
-};
-
-const int16_t kPower10ExponentTable[] = {
-    -1200, -1196, -1193, -1190, -1186, -1183, -1180, -1176, -1173, -1170, -1166,
-    -1163, -1160, -1156, -1153, -1150, -1146, -1143, -1140, -1136, -1133, -1130,
-    -1127, -1123, -1120, -1117, -1113, -1110, -1107, -1103, -1100, -1097, -1093,
-    -1090, -1087, -1083, -1080, -1077, -1073, -1070, -1067, -1063, -1060, -1057,
-    -1053, -1050, -1047, -1043, -1040, -1037, -1034, -1030, -1027, -1024, -1020,
-    -1017, -1014, -1010, -1007, -1004, -1000, -997,  -994,  -990,  -987,  -984,
-    -980,  -977,  -974,  -970,  -967,  -964,  -960,  -957,  -954,  -950,  -947,
-    -944,  -940,  -937,  -934,  -931,  -927,  -924,  -921,  -917,  -914,  -911,
-    -907,  -904,  -901,  -897,  -894,  -891,  -887,  -884,  -881,  -877,  -874,
-    -871,  -867,  -864,  -861,  -857,  -854,  -851,  -847,  -844,  -841,  -838,
-    -834,  -831,  -828,  -824,  -821,  -818,  -814,  -811,  -808,  -804,  -801,
-    -798,  -794,  -791,  -788,  -784,  -781,  -778,  -774,  -771,  -768,  -764,
-    -761,  -758,  -754,  -751,  -748,  -744,  -741,  -738,  -735,  -731,  -728,
-    -725,  -721,  -718,  -715,  -711,  -708,  -705,  -701,  -698,  -695,  -691,
-    -688,  -685,  -681,  -678,  -675,  -671,  -668,  -665,  -661,  -658,  -655,
-    -651,  -648,  -645,  -642,  -638,  -635,  -632,  -628,  -625,  -622,  -618,
-    -615,  -612,  -608,  -605,  -602,  -598,  -595,  -592,  -588,  -585,  -582,
-    -578,  -575,  -572,  -568,  -565,  -562,  -558,  -555,  -552,  -549,  -545,
-    -542,  -539,  -535,  -532,  -529,  -525,  -522,  -519,  -515,  -512,  -509,
-    -505,  -502,  -499,  -495,  -492,  -489,  -485,  -482,  -479,  -475,  -472,
-    -469,  -465,  -462,  -459,  -455,  -452,  -449,  -446,  -442,  -439,  -436,
-    -432,  -429,  -426,  -422,  -419,  -416,  -412,  -409,  -406,  -402,  -399,
-    -396,  -392,  -389,  -386,  -382,  -379,  -376,  -372,  -369,  -366,  -362,
-    -359,  -356,  -353,  -349,  -346,  -343,  -339,  -336,  -333,  -329,  -326,
-    -323,  -319,  -316,  -313,  -309,  -306,  -303,  -299,  -296,  -293,  -289,
-    -286,  -283,  -279,  -276,  -273,  -269,  -266,  -263,  -259,  -256,  -253,
-    -250,  -246,  -243,  -240,  -236,  -233,  -230,  -226,  -223,  -220,  -216,
-    -213,  -210,  -206,  -203,  -200,  -196,  -193,  -190,  -186,  -183,  -180,
-    -176,  -173,  -170,  -166,  -163,  -160,  -157,  -153,  -150,  -147,  -143,
-    -140,  -137,  -133,  -130,  -127,  -123,  -120,  -117,  -113,  -110,  -107,
-    -103,  -100,  -97,   -93,   -90,   -87,   -83,   -80,   -77,   -73,   -70,
-    -67,   -63,   -60,   -57,   -54,   -50,   -47,   -44,   -40,   -37,   -34,
-    -30,   -27,   -24,   -20,   -17,   -14,   -10,   -7,    -4,    0,     3,
-    6,     10,    13,    16,    20,    23,    26,    30,    33,    36,    39,
-    43,    46,    49,    53,    56,    59,    63,    66,    69,    73,    76,
-    79,    83,    86,    89,    93,    96,    99,    103,   106,   109,   113,
-    116,   119,   123,   126,   129,   132,   136,   139,   142,   146,   149,
-    152,   156,   159,   162,   166,   169,   172,   176,   179,   182,   186,
-    189,   192,   196,   199,   202,   206,   209,   212,   216,   219,   222,
-    226,   229,   232,   235,   239,   242,   245,   249,   252,   255,   259,
-    262,   265,   269,   272,   275,   279,   282,   285,   289,   292,   295,
-    299,   302,   305,   309,   312,   315,   319,   322,   325,   328,   332,
-    335,   338,   342,   345,   348,   352,   355,   358,   362,   365,   368,
-    372,   375,   378,   382,   385,   388,   392,   395,   398,   402,   405,
-    408,   412,   415,   418,   422,   425,   428,   431,   435,   438,   441,
-    445,   448,   451,   455,   458,   461,   465,   468,   471,   475,   478,
-    481,   485,   488,   491,   495,   498,   501,   505,   508,   511,   515,
-    518,   521,   524,   528,   531,   534,   538,   541,   544,   548,   551,
-    554,   558,   561,   564,   568,   571,   574,   578,   581,   584,   588,
-    591,   594,   598,   601,   604,   608,   611,   614,   617,   621,   624,
-    627,   631,   634,   637,   641,   644,   647,   651,   654,   657,   661,
-    664,   667,   671,   674,   677,   681,   684,   687,   691,   694,   697,
-    701,   704,   707,   711,   714,   717,   720,   724,   727,   730,   734,
-    737,   740,   744,   747,   750,   754,   757,   760,   764,   767,   770,
-    774,   777,   780,   784,   787,   790,   794,   797,   800,   804,   807,
-    810,   813,   817,   820,   823,   827,   830,   833,   837,   840,   843,
-    847,   850,   853,   857,   860,   863,   867,   870,   873,   877,   880,
-    883,   887,   890,   893,   897,   900,   903,   907,   910,   913,   916,
-    920,   923,   926,   930,   933,   936,   940,   943,   946,   950,   953,
-    956,   960,
-};
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/charconv.h b/third_party/abseil_cpp/absl/strings/charconv.h
deleted file mode 100644
index e04be32f95..0000000000
--- a/third_party/abseil_cpp/absl/strings/charconv.h
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_CHARCONV_H_
-#define ABSL_STRINGS_CHARCONV_H_
-
-#include <system_error>  // NOLINT(build/c++11)
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Workalike compatibilty version of std::chars_format from C++17.
-//
-// This is an bitfield enumerator which can be passed to absl::from_chars to
-// configure the string-to-float conversion.
-enum class chars_format {
-  scientific = 1,
-  fixed = 2,
-  hex = 4,
-  general = fixed | scientific,
-};
-
-// The return result of a string-to-number conversion.
-//
-// `ec` will be set to `invalid_argument` if a well-formed number was not found
-// at the start of the input range, `result_out_of_range` if a well-formed
-// number was found, but it was out of the representable range of the requested
-// type, or to std::errc() otherwise.
-//
-// If a well-formed number was found, `ptr` is set to one past the sequence of
-// characters that were successfully parsed.  If none was found, `ptr` is set
-// to the `first` argument to from_chars.
-struct from_chars_result {
-  const char* ptr;
-  std::errc ec;
-};
-
-// Workalike compatibilty version of std::from_chars from C++17.  Currently
-// this only supports the `double` and `float` types.
-//
-// This interface incorporates the proposed resolutions for library issues
-// DR 3080 and DR 3081.  If these are adopted with different wording,
-// Abseil's behavior will change to match the standard.  (The behavior most
-// likely to change is for DR 3081, which says what `value` will be set to in
-// the case of overflow and underflow.  Code that wants to avoid possible
-// breaking changes in this area should not depend on `value` when the returned
-// from_chars_result indicates a range error.)
-//
-// Searches the range [first, last) for the longest matching pattern beginning
-// at `first` that represents a floating point number.  If one is found, store
-// the result in `value`.
-//
-// The matching pattern format is almost the same as that of strtod(), except
-// that C locale is not respected, and an initial '+' character in the input
-// range will never be matched.
-//
-// If `fmt` is set, it must be one of the enumerator values of the chars_format.
-// (This is despite the fact that chars_format is a bitmask type.)  If set to
-// `scientific`, a matching number must contain an exponent.  If set to `fixed`,
-// then an exponent will never match.  (For example, the string "1e5" will be
-// parsed as "1".)  If set to `hex`, then a hexadecimal float is parsed in the
-// format that strtod() accepts, except that a "0x" prefix is NOT matched.
-// (In particular, in `hex` mode, the input "0xff" results in the largest
-// matching pattern "0".)
-absl::from_chars_result from_chars(const char* first, const char* last,
-                                   double& value,  // NOLINT
-                                   chars_format fmt = chars_format::general);
-
-absl::from_chars_result from_chars(const char* first, const char* last,
-                                   float& value,  // NOLINT
-                                   chars_format fmt = chars_format::general);
-
-// std::chars_format is specified as a bitmask type, which means the following
-// operations must be provided:
-inline constexpr chars_format operator&(chars_format lhs, chars_format rhs) {
-  return static_cast<chars_format>(static_cast<int>(lhs) &
-                                   static_cast<int>(rhs));
-}
-inline constexpr chars_format operator|(chars_format lhs, chars_format rhs) {
-  return static_cast<chars_format>(static_cast<int>(lhs) |
-                                   static_cast<int>(rhs));
-}
-inline constexpr chars_format operator^(chars_format lhs, chars_format rhs) {
-  return static_cast<chars_format>(static_cast<int>(lhs) ^
-                                   static_cast<int>(rhs));
-}
-inline constexpr chars_format operator~(chars_format arg) {
-  return static_cast<chars_format>(~static_cast<int>(arg));
-}
-inline chars_format& operator&=(chars_format& lhs, chars_format rhs) {
-  lhs = lhs & rhs;
-  return lhs;
-}
-inline chars_format& operator|=(chars_format& lhs, chars_format rhs) {
-  lhs = lhs | rhs;
-  return lhs;
-}
-inline chars_format& operator^=(chars_format& lhs, chars_format rhs) {
-  lhs = lhs ^ rhs;
-  return lhs;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_CHARCONV_H_
diff --git a/third_party/abseil_cpp/absl/strings/charconv_benchmark.cc b/third_party/abseil_cpp/absl/strings/charconv_benchmark.cc
deleted file mode 100644
index e8c7371d65..0000000000
--- a/third_party/abseil_cpp/absl/strings/charconv_benchmark.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/charconv.h"
-
-#include <cstdlib>
-#include <cstring>
-#include <string>
-
-#include "benchmark/benchmark.h"
-
-namespace {
-
-void BM_Strtod_Pi(benchmark::State& state) {
-  const char* pi = "3.14159";
-  for (auto s : state) {
-    benchmark::DoNotOptimize(pi);
-    benchmark::DoNotOptimize(strtod(pi, nullptr));
-  }
-}
-BENCHMARK(BM_Strtod_Pi);
-
-void BM_Absl_Pi(benchmark::State& state) {
-  const char* pi = "3.14159";
-  const char* pi_end = pi + strlen(pi);
-  for (auto s : state) {
-    benchmark::DoNotOptimize(pi);
-    double v;
-    absl::from_chars(pi, pi_end, v);
-    benchmark::DoNotOptimize(v);
-  }
-}
-BENCHMARK(BM_Absl_Pi);
-
-void BM_Strtod_Pi_float(benchmark::State& state) {
-  const char* pi = "3.14159";
-  for (auto s : state) {
-    benchmark::DoNotOptimize(pi);
-    benchmark::DoNotOptimize(strtof(pi, nullptr));
-  }
-}
-BENCHMARK(BM_Strtod_Pi_float);
-
-void BM_Absl_Pi_float(benchmark::State& state) {
-  const char* pi = "3.14159";
-  const char* pi_end = pi + strlen(pi);
-  for (auto s : state) {
-    benchmark::DoNotOptimize(pi);
-    float v;
-    absl::from_chars(pi, pi_end, v);
-    benchmark::DoNotOptimize(v);
-  }
-}
-BENCHMARK(BM_Absl_Pi_float);
-
-void BM_Strtod_HardLarge(benchmark::State& state) {
-  const char* num = "272104041512242479.e200";
-  for (auto s : state) {
-    benchmark::DoNotOptimize(num);
-    benchmark::DoNotOptimize(strtod(num, nullptr));
-  }
-}
-BENCHMARK(BM_Strtod_HardLarge);
-
-void BM_Absl_HardLarge(benchmark::State& state) {
-  const char* numstr = "272104041512242479.e200";
-  const char* numstr_end = numstr + strlen(numstr);
-  for (auto s : state) {
-    benchmark::DoNotOptimize(numstr);
-    double v;
-    absl::from_chars(numstr, numstr_end, v);
-    benchmark::DoNotOptimize(v);
-  }
-}
-BENCHMARK(BM_Absl_HardLarge);
-
-void BM_Strtod_HardSmall(benchmark::State& state) {
-  const char* num = "94080055902682397.e-242";
-  for (auto s : state) {
-    benchmark::DoNotOptimize(num);
-    benchmark::DoNotOptimize(strtod(num, nullptr));
-  }
-}
-BENCHMARK(BM_Strtod_HardSmall);
-
-void BM_Absl_HardSmall(benchmark::State& state) {
-  const char* numstr = "94080055902682397.e-242";
-  const char* numstr_end = numstr + strlen(numstr);
-  for (auto s : state) {
-    benchmark::DoNotOptimize(numstr);
-    double v;
-    absl::from_chars(numstr, numstr_end, v);
-    benchmark::DoNotOptimize(v);
-  }
-}
-BENCHMARK(BM_Absl_HardSmall);
-
-void BM_Strtod_HugeMantissa(benchmark::State& state) {
-  std::string huge(200, '3');
-  const char* num = huge.c_str();
-  for (auto s : state) {
-    benchmark::DoNotOptimize(num);
-    benchmark::DoNotOptimize(strtod(num, nullptr));
-  }
-}
-BENCHMARK(BM_Strtod_HugeMantissa);
-
-void BM_Absl_HugeMantissa(benchmark::State& state) {
-  std::string huge(200, '3');
-  const char* num = huge.c_str();
-  const char* num_end = num + 200;
-  for (auto s : state) {
-    benchmark::DoNotOptimize(num);
-    double v;
-    absl::from_chars(num, num_end, v);
-    benchmark::DoNotOptimize(v);
-  }
-}
-BENCHMARK(BM_Absl_HugeMantissa);
-
-std::string MakeHardCase(int length) {
-  // The number 1.1521...e-297 is exactly halfway between 12345 * 2**-1000 and
-  // the next larger representable number.  The digits of this number are in
-  // the string below.
-  const std::string digits =
-      "1."
-      "152113937042223790993097181572444900347587985074226836242307364987727724"
-      "831384300183638649152607195040591791364113930628852279348613864894524591"
-      "272746490313676832900762939595690019745859128071117417798540258114233761"
-      "012939937017879509401007964861774960297319002612457273148497158989073482"
-      "171377406078223015359818300988676687994537274548940612510414856761641652"
-      "513434981938564294004070500716200446656421722229202383105446378511678258"
-      "370570631774499359748259931676320916632111681001853983492795053244971606"
-      "922718923011680846577744433974087653954904214152517799883551075537146316"
-      "168973685866425605046988661997658648354773076621610279716804960009043764"
-      "038392994055171112475093876476783502487512538082706095923790634572014823"
-      "78877699375152587890625" +
-      std::string(5000, '0');
-  // generate the hard cases on either side for the given length.
-  // Lengths between 3 and 1000 are reasonable.
-  return digits.substr(0, length) + "1e-297";
-}
-
-void BM_Strtod_Big_And_Difficult(benchmark::State& state) {
-  std::string testcase = MakeHardCase(state.range(0));
-  const char* begin = testcase.c_str();
-  for (auto s : state) {
-    benchmark::DoNotOptimize(begin);
-    benchmark::DoNotOptimize(strtod(begin, nullptr));
-  }
-}
-BENCHMARK(BM_Strtod_Big_And_Difficult)->Range(3, 5000);
-
-void BM_Absl_Big_And_Difficult(benchmark::State& state) {
-  std::string testcase = MakeHardCase(state.range(0));
-  const char* begin = testcase.c_str();
-  const char* end = begin + testcase.size();
-  for (auto s : state) {
-    benchmark::DoNotOptimize(begin);
-    double v;
-    absl::from_chars(begin, end, v);
-    benchmark::DoNotOptimize(v);
-  }
-}
-BENCHMARK(BM_Absl_Big_And_Difficult)->Range(3, 5000);
-
-}  // namespace
-
-// ------------------------------------------------------------------------
-// Benchmark                                 Time           CPU Iterations
-// ------------------------------------------------------------------------
-// BM_Strtod_Pi                             96 ns         96 ns    6337454
-// BM_Absl_Pi                               35 ns         35 ns   20031996
-// BM_Strtod_Pi_float                       91 ns         91 ns    7745851
-// BM_Absl_Pi_float                         35 ns         35 ns   20430298
-// BM_Strtod_HardLarge                     133 ns        133 ns    5288341
-// BM_Absl_HardLarge                       181 ns        181 ns    3855615
-// BM_Strtod_HardSmall                     279 ns        279 ns    2517243
-// BM_Absl_HardSmall                       287 ns        287 ns    2458744
-// BM_Strtod_HugeMantissa                  433 ns        433 ns    1604293
-// BM_Absl_HugeMantissa                    160 ns        160 ns    4403671
-// BM_Strtod_Big_And_Difficult/3           236 ns        236 ns    2942496
-// BM_Strtod_Big_And_Difficult/8           232 ns        232 ns    2983796
-// BM_Strtod_Big_And_Difficult/64          437 ns        437 ns    1591951
-// BM_Strtod_Big_And_Difficult/512        1738 ns       1738 ns     402519
-// BM_Strtod_Big_And_Difficult/4096       3943 ns       3943 ns     176128
-// BM_Strtod_Big_And_Difficult/5000       4397 ns       4397 ns     157878
-// BM_Absl_Big_And_Difficult/3              39 ns         39 ns   17799583
-// BM_Absl_Big_And_Difficult/8              43 ns         43 ns   16096859
-// BM_Absl_Big_And_Difficult/64            550 ns        550 ns    1259717
-// BM_Absl_Big_And_Difficult/512          4167 ns       4167 ns     171414
-// BM_Absl_Big_And_Difficult/4096         9160 ns       9159 ns      76297
-// BM_Absl_Big_And_Difficult/5000         9738 ns       9738 ns      70140
diff --git a/third_party/abseil_cpp/absl/strings/charconv_test.cc b/third_party/abseil_cpp/absl/strings/charconv_test.cc
deleted file mode 100644
index 9090e9c89c..0000000000
--- a/third_party/abseil_cpp/absl/strings/charconv_test.cc
+++ /dev/null
@@ -1,780 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/charconv.h"
-
-#include <cstdlib>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/internal/pow10_helper.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-
-#ifdef _MSC_FULL_VER
-#define ABSL_COMPILER_DOES_EXACT_ROUNDING 0
-#define ABSL_STRTOD_HANDLES_NAN_CORRECTLY 0
-#else
-#define ABSL_COMPILER_DOES_EXACT_ROUNDING 1
-#define ABSL_STRTOD_HANDLES_NAN_CORRECTLY 1
-#endif
-
-namespace {
-
-using absl::strings_internal::Pow10;
-
-#if ABSL_COMPILER_DOES_EXACT_ROUNDING
-
-// Tests that the given string is accepted by absl::from_chars, and that it
-// converts exactly equal to the given number.
-void TestDoubleParse(absl::string_view str, double expected_number) {
-  SCOPED_TRACE(str);
-  double actual_number = 0.0;
-  absl::from_chars_result result =
-      absl::from_chars(str.data(), str.data() + str.length(), actual_number);
-  EXPECT_EQ(result.ec, std::errc());
-  EXPECT_EQ(result.ptr, str.data() + str.length());
-  EXPECT_EQ(actual_number, expected_number);
-}
-
-void TestFloatParse(absl::string_view str, float expected_number) {
-  SCOPED_TRACE(str);
-  float actual_number = 0.0;
-  absl::from_chars_result result =
-      absl::from_chars(str.data(), str.data() + str.length(), actual_number);
-  EXPECT_EQ(result.ec, std::errc());
-  EXPECT_EQ(result.ptr, str.data() + str.length());
-  EXPECT_EQ(actual_number, expected_number);
-}
-
-// Tests that the given double or single precision floating point literal is
-// parsed correctly by absl::from_chars.
-//
-// These convenience macros assume that the C++ compiler being used also does
-// fully correct decimal-to-binary conversions.
-#define FROM_CHARS_TEST_DOUBLE(number)     \
-  {                                        \
-    TestDoubleParse(#number, number);      \
-    TestDoubleParse("-" #number, -number); \
-  }
-
-#define FROM_CHARS_TEST_FLOAT(number)        \
-  {                                          \
-    TestFloatParse(#number, number##f);      \
-    TestFloatParse("-" #number, -number##f); \
-  }
-
-TEST(FromChars, NearRoundingCases) {
-  // Cases from "A Program for Testing IEEE Decimal-Binary Conversion"
-  // by Vern Paxson.
-
-  // Forms that should round towards zero.  (These are the hardest cases for
-  // each decimal mantissa size.)
-  FROM_CHARS_TEST_DOUBLE(5.e125);
-  FROM_CHARS_TEST_DOUBLE(69.e267);
-  FROM_CHARS_TEST_DOUBLE(999.e-026);
-  FROM_CHARS_TEST_DOUBLE(7861.e-034);
-  FROM_CHARS_TEST_DOUBLE(75569.e-254);
-  FROM_CHARS_TEST_DOUBLE(928609.e-261);
-  FROM_CHARS_TEST_DOUBLE(9210917.e080);
-  FROM_CHARS_TEST_DOUBLE(84863171.e114);
-  FROM_CHARS_TEST_DOUBLE(653777767.e273);
-  FROM_CHARS_TEST_DOUBLE(5232604057.e-298);
-  FROM_CHARS_TEST_DOUBLE(27235667517.e-109);
-  FROM_CHARS_TEST_DOUBLE(653532977297.e-123);
-  FROM_CHARS_TEST_DOUBLE(3142213164987.e-294);
-  FROM_CHARS_TEST_DOUBLE(46202199371337.e-072);
-  FROM_CHARS_TEST_DOUBLE(231010996856685.e-073);
-  FROM_CHARS_TEST_DOUBLE(9324754620109615.e212);
-  FROM_CHARS_TEST_DOUBLE(78459735791271921.e049);
-  FROM_CHARS_TEST_DOUBLE(272104041512242479.e200);
-  FROM_CHARS_TEST_DOUBLE(6802601037806061975.e198);
-  FROM_CHARS_TEST_DOUBLE(20505426358836677347.e-221);
-  FROM_CHARS_TEST_DOUBLE(836168422905420598437.e-234);
-  FROM_CHARS_TEST_DOUBLE(4891559871276714924261.e222);
-  FROM_CHARS_TEST_FLOAT(5.e-20);
-  FROM_CHARS_TEST_FLOAT(67.e14);
-  FROM_CHARS_TEST_FLOAT(985.e15);
-  FROM_CHARS_TEST_FLOAT(7693.e-42);
-  FROM_CHARS_TEST_FLOAT(55895.e-16);
-  FROM_CHARS_TEST_FLOAT(996622.e-44);
-  FROM_CHARS_TEST_FLOAT(7038531.e-32);
-  FROM_CHARS_TEST_FLOAT(60419369.e-46);
-  FROM_CHARS_TEST_FLOAT(702990899.e-20);
-  FROM_CHARS_TEST_FLOAT(6930161142.e-48);
-  FROM_CHARS_TEST_FLOAT(25933168707.e-13);
-  FROM_CHARS_TEST_FLOAT(596428896559.e20);
-
-  // Similarly, forms that should round away from zero.
-  FROM_CHARS_TEST_DOUBLE(9.e-265);
-  FROM_CHARS_TEST_DOUBLE(85.e-037);
-  FROM_CHARS_TEST_DOUBLE(623.e100);
-  FROM_CHARS_TEST_DOUBLE(3571.e263);
-  FROM_CHARS_TEST_DOUBLE(81661.e153);
-  FROM_CHARS_TEST_DOUBLE(920657.e-023);
-  FROM_CHARS_TEST_DOUBLE(4603285.e-024);
-  FROM_CHARS_TEST_DOUBLE(87575437.e-309);
-  FROM_CHARS_TEST_DOUBLE(245540327.e122);
-  FROM_CHARS_TEST_DOUBLE(6138508175.e120);
-  FROM_CHARS_TEST_DOUBLE(83356057653.e193);
-  FROM_CHARS_TEST_DOUBLE(619534293513.e124);
-  FROM_CHARS_TEST_DOUBLE(2335141086879.e218);
-  FROM_CHARS_TEST_DOUBLE(36167929443327.e-159);
-  FROM_CHARS_TEST_DOUBLE(609610927149051.e-255);
-  FROM_CHARS_TEST_DOUBLE(3743626360493413.e-165);
-  FROM_CHARS_TEST_DOUBLE(94080055902682397.e-242);
-  FROM_CHARS_TEST_DOUBLE(899810892172646163.e283);
-  FROM_CHARS_TEST_DOUBLE(7120190517612959703.e120);
-  FROM_CHARS_TEST_DOUBLE(25188282901709339043.e-252);
-  FROM_CHARS_TEST_DOUBLE(308984926168550152811.e-052);
-  FROM_CHARS_TEST_DOUBLE(6372891218502368041059.e064);
-  FROM_CHARS_TEST_FLOAT(3.e-23);
-  FROM_CHARS_TEST_FLOAT(57.e18);
-  FROM_CHARS_TEST_FLOAT(789.e-35);
-  FROM_CHARS_TEST_FLOAT(2539.e-18);
-  FROM_CHARS_TEST_FLOAT(76173.e28);
-  FROM_CHARS_TEST_FLOAT(887745.e-11);
-  FROM_CHARS_TEST_FLOAT(5382571.e-37);
-  FROM_CHARS_TEST_FLOAT(82381273.e-35);
-  FROM_CHARS_TEST_FLOAT(750486563.e-38);
-  FROM_CHARS_TEST_FLOAT(3752432815.e-39);
-  FROM_CHARS_TEST_FLOAT(75224575729.e-45);
-  FROM_CHARS_TEST_FLOAT(459926601011.e15);
-}
-
-#undef FROM_CHARS_TEST_DOUBLE
-#undef FROM_CHARS_TEST_FLOAT
-#endif
-
-float ToFloat(absl::string_view s) {
-  float f;
-  absl::from_chars(s.data(), s.data() + s.size(), f);
-  return f;
-}
-
-double ToDouble(absl::string_view s) {
-  double d;
-  absl::from_chars(s.data(), s.data() + s.size(), d);
-  return d;
-}
-
-// A duplication of the test cases in "NearRoundingCases" above, but with
-// expected values expressed with integers, using ldexp/ldexpf.  These test
-// cases will work even on compilers that do not accurately round floating point
-// literals.
-TEST(FromChars, NearRoundingCasesExplicit) {
-  EXPECT_EQ(ToDouble("5.e125"), ldexp(6653062250012735, 365));
-  EXPECT_EQ(ToDouble("69.e267"), ldexp(4705683757438170, 841));
-  EXPECT_EQ(ToDouble("999.e-026"), ldexp(6798841691080350, -129));
-  EXPECT_EQ(ToDouble("7861.e-034"), ldexp(8975675289889240, -153));
-  EXPECT_EQ(ToDouble("75569.e-254"), ldexp(6091718967192243, -880));
-  EXPECT_EQ(ToDouble("928609.e-261"), ldexp(7849264900213743, -900));
-  EXPECT_EQ(ToDouble("9210917.e080"), ldexp(8341110837370930, 236));
-  EXPECT_EQ(ToDouble("84863171.e114"), ldexp(4625202867375927, 353));
-  EXPECT_EQ(ToDouble("653777767.e273"), ldexp(5068902999763073, 884));
-  EXPECT_EQ(ToDouble("5232604057.e-298"), ldexp(5741343011915040, -1010));
-  EXPECT_EQ(ToDouble("27235667517.e-109"), ldexp(6707124626673586, -380));
-  EXPECT_EQ(ToDouble("653532977297.e-123"), ldexp(7078246407265384, -422));
-  EXPECT_EQ(ToDouble("3142213164987.e-294"), ldexp(8219991337640559, -988));
-  EXPECT_EQ(ToDouble("46202199371337.e-072"), ldexp(5224462102115359, -246));
-  EXPECT_EQ(ToDouble("231010996856685.e-073"), ldexp(5224462102115359, -247));
-  EXPECT_EQ(ToDouble("9324754620109615.e212"), ldexp(5539753864394442, 705));
-  EXPECT_EQ(ToDouble("78459735791271921.e049"), ldexp(8388176519442766, 166));
-  EXPECT_EQ(ToDouble("272104041512242479.e200"), ldexp(5554409530847367, 670));
-  EXPECT_EQ(ToDouble("6802601037806061975.e198"), ldexp(5554409530847367, 668));
-  EXPECT_EQ(ToDouble("20505426358836677347.e-221"),
-            ldexp(4524032052079546, -722));
-  EXPECT_EQ(ToDouble("836168422905420598437.e-234"),
-            ldexp(5070963299887562, -760));
-  EXPECT_EQ(ToDouble("4891559871276714924261.e222"),
-            ldexp(6452687840519111, 757));
-  EXPECT_EQ(ToFloat("5.e-20"), ldexpf(15474250, -88));
-  EXPECT_EQ(ToFloat("67.e14"), ldexpf(12479722, 29));
-  EXPECT_EQ(ToFloat("985.e15"), ldexpf(14333636, 36));
-  EXPECT_EQ(ToFloat("7693.e-42"), ldexpf(10979816, -150));
-  EXPECT_EQ(ToFloat("55895.e-16"), ldexpf(12888509, -61));
-  EXPECT_EQ(ToFloat("996622.e-44"), ldexpf(14224264, -150));
-  EXPECT_EQ(ToFloat("7038531.e-32"), ldexpf(11420669, -107));
-  EXPECT_EQ(ToFloat("60419369.e-46"), ldexpf(8623340, -150));
-  EXPECT_EQ(ToFloat("702990899.e-20"), ldexpf(16209866, -61));
-  EXPECT_EQ(ToFloat("6930161142.e-48"), ldexpf(9891056, -150));
-  EXPECT_EQ(ToFloat("25933168707.e-13"), ldexpf(11138211, -32));
-  EXPECT_EQ(ToFloat("596428896559.e20"), ldexpf(12333860, 82));
-
-
-  EXPECT_EQ(ToDouble("9.e-265"), ldexp(8168427841980010, -930));
-  EXPECT_EQ(ToDouble("85.e-037"), ldexp(6360455125664090, -169));
-  EXPECT_EQ(ToDouble("623.e100"), ldexp(6263531988747231, 289));
-  EXPECT_EQ(ToDouble("3571.e263"), ldexp(6234526311072170, 833));
-  EXPECT_EQ(ToDouble("81661.e153"), ldexp(6696636728760206, 472));
-  EXPECT_EQ(ToDouble("920657.e-023"), ldexp(5975405561110124, -109));
-  EXPECT_EQ(ToDouble("4603285.e-024"), ldexp(5975405561110124, -110));
-  EXPECT_EQ(ToDouble("87575437.e-309"), ldexp(8452160731874668, -1053));
-  EXPECT_EQ(ToDouble("245540327.e122"), ldexp(4985336549131723, 381));
-  EXPECT_EQ(ToDouble("6138508175.e120"), ldexp(4985336549131723, 379));
-  EXPECT_EQ(ToDouble("83356057653.e193"), ldexp(5986732817132056, 625));
-  EXPECT_EQ(ToDouble("619534293513.e124"), ldexp(4798406992060657, 399));
-  EXPECT_EQ(ToDouble("2335141086879.e218"), ldexp(5419088166961646, 713));
-  EXPECT_EQ(ToDouble("36167929443327.e-159"), ldexp(8135819834632444, -536));
-  EXPECT_EQ(ToDouble("609610927149051.e-255"), ldexp(4576664294594737, -850));
-  EXPECT_EQ(ToDouble("3743626360493413.e-165"), ldexp(6898586531774201, -549));
-  EXPECT_EQ(ToDouble("94080055902682397.e-242"), ldexp(6273271706052298, -800));
-  EXPECT_EQ(ToDouble("899810892172646163.e283"), ldexp(7563892574477827, 947));
-  EXPECT_EQ(ToDouble("7120190517612959703.e120"), ldexp(5385467232557565, 409));
-  EXPECT_EQ(ToDouble("25188282901709339043.e-252"),
-            ldexp(5635662608542340, -825));
-  EXPECT_EQ(ToDouble("308984926168550152811.e-052"),
-            ldexp(5644774693823803, -157));
-  EXPECT_EQ(ToDouble("6372891218502368041059.e064"),
-            ldexp(4616868614322430, 233));
-
-  EXPECT_EQ(ToFloat("3.e-23"), ldexpf(9507380, -98));
-  EXPECT_EQ(ToFloat("57.e18"), ldexpf(12960300, 42));
-  EXPECT_EQ(ToFloat("789.e-35"), ldexpf(10739312, -130));
-  EXPECT_EQ(ToFloat("2539.e-18"), ldexpf(11990089, -72));
-  EXPECT_EQ(ToFloat("76173.e28"), ldexpf(9845130, 86));
-  EXPECT_EQ(ToFloat("887745.e-11"), ldexpf(9760860, -40));
-  EXPECT_EQ(ToFloat("5382571.e-37"), ldexpf(11447463, -124));
-  EXPECT_EQ(ToFloat("82381273.e-35"), ldexpf(8554961, -113));
-  EXPECT_EQ(ToFloat("750486563.e-38"), ldexpf(9975678, -120));
-  EXPECT_EQ(ToFloat("3752432815.e-39"), ldexpf(9975678, -121));
-  EXPECT_EQ(ToFloat("75224575729.e-45"), ldexpf(13105970, -137));
-  EXPECT_EQ(ToFloat("459926601011.e15"), ldexpf(12466336, 65));
-}
-
-// Common test logic for converting a string which lies exactly halfway between
-// two target floats.
-//
-// mantissa and exponent represent the precise value between two floating point
-// numbers, `expected_low` and `expected_high`.  The floating point
-// representation to parse in `StrCat(mantissa, "e", exponent)`.
-//
-// This function checks that an input just slightly less than the exact value
-// is rounded down to `expected_low`, and an input just slightly greater than
-// the exact value is rounded up to `expected_high`.
-//
-// The exact value should round to `expected_half`, which must be either
-// `expected_low` or `expected_high`.
-template <typename FloatType>
-void TestHalfwayValue(const std::string& mantissa, int exponent,
-                      FloatType expected_low, FloatType expected_high,
-                      FloatType expected_half) {
-  std::string low_rep = mantissa;
-  low_rep[low_rep.size() - 1] -= 1;
-  absl::StrAppend(&low_rep, std::string(1000, '9'), "e", exponent);
-
-  FloatType actual_low = 0;
-  absl::from_chars(low_rep.data(), low_rep.data() + low_rep.size(), actual_low);
-  EXPECT_EQ(expected_low, actual_low);
-
-  std::string high_rep =
-      absl::StrCat(mantissa, std::string(1000, '0'), "1e", exponent);
-  FloatType actual_high = 0;
-  absl::from_chars(high_rep.data(), high_rep.data() + high_rep.size(),
-                   actual_high);
-  EXPECT_EQ(expected_high, actual_high);
-
-  std::string halfway_rep = absl::StrCat(mantissa, "e", exponent);
-  FloatType actual_half = 0;
-  absl::from_chars(halfway_rep.data(), halfway_rep.data() + halfway_rep.size(),
-                   actual_half);
-  EXPECT_EQ(expected_half, actual_half);
-}
-
-TEST(FromChars, DoubleRounding) {
-  const double zero = 0.0;
-  const double first_subnormal = nextafter(zero, 1.0);
-  const double second_subnormal = nextafter(first_subnormal, 1.0);
-
-  const double first_normal = DBL_MIN;
-  const double last_subnormal = nextafter(first_normal, 0.0);
-  const double second_normal = nextafter(first_normal, 1.0);
-
-  const double last_normal = DBL_MAX;
-  const double penultimate_normal = nextafter(last_normal, 0.0);
-
-  // Various test cases for numbers between two representable floats.  Each
-  // call to TestHalfwayValue tests a number just below and just above the
-  // halfway point, as well as the number exactly between them.
-
-  // Test between zero and first_subnormal.  Round-to-even tie rounds down.
-  TestHalfwayValue(
-      "2."
-      "470328229206232720882843964341106861825299013071623822127928412503377536"
-      "351043759326499181808179961898982823477228588654633283551779698981993873"
-      "980053909390631503565951557022639229085839244910518443593180284993653615"
-      "250031937045767824921936562366986365848075700158576926990370631192827955"
-      "855133292783433840935197801553124659726357957462276646527282722005637400"
-      "648549997709659947045402082816622623785739345073633900796776193057750674"
-      "017632467360096895134053553745851666113422376667860416215968046191446729"
-      "184030053005753084904876539171138659164623952491262365388187963623937328"
-      "042389101867234849766823508986338858792562830275599565752445550725518931"
-      "369083625477918694866799496832404970582102851318545139621383772282614543"
-      "7693412532098591327667236328125",
-      -324, zero, first_subnormal, zero);
-
-  // first_subnormal and second_subnormal.  Round-to-even tie rounds up.
-  TestHalfwayValue(
-      "7."
-      "410984687618698162648531893023320585475897039214871466383785237510132609"
-      "053131277979497545424539885696948470431685765963899850655339096945981621"
-      "940161728171894510697854671067917687257517734731555330779540854980960845"
-      "750095811137303474765809687100959097544227100475730780971111893578483867"
-      "565399878350301522805593404659373979179073872386829939581848166016912201"
-      "945649993128979841136206248449867871357218035220901702390328579173252022"
-      "052897402080290685402160661237554998340267130003581248647904138574340187"
-      "552090159017259254714629617513415977493871857473787096164563890871811984"
-      "127167305601704549300470526959016576377688490826798697257336652176556794"
-      "107250876433756084600398490497214911746308553955635418864151316847843631"
-      "3080237596295773983001708984375",
-      -324, first_subnormal, second_subnormal, second_subnormal);
-
-  // last_subnormal and first_normal.  Round-to-even tie rounds up.
-  TestHalfwayValue(
-      "2."
-      "225073858507201136057409796709131975934819546351645648023426109724822222"
-      "021076945516529523908135087914149158913039621106870086438694594645527657"
-      "207407820621743379988141063267329253552286881372149012981122451451889849"
-      "057222307285255133155755015914397476397983411801999323962548289017107081"
-      "850690630666655994938275772572015763062690663332647565300009245888316433"
-      "037779791869612049497390377829704905051080609940730262937128958950003583"
-      "799967207254304360284078895771796150945516748243471030702609144621572289"
-      "880258182545180325707018860872113128079512233426288368622321503775666622"
-      "503982534335974568884423900265498198385487948292206894721689831099698365"
-      "846814022854243330660339850886445804001034933970427567186443383770486037"
-      "86162277173854562306587467901408672332763671875",
-      -308, last_subnormal, first_normal, first_normal);
-
-  // first_normal and second_normal.  Round-to-even tie rounds down.
-  TestHalfwayValue(
-      "2."
-      "225073858507201630123055637955676152503612414573018013083228724049586647"
-      "606759446192036794116886953213985520549032000903434781884412325572184367"
-      "563347617020518175998922941393629966742598285899994830148971433555578567"
-      "693279306015978183162142425067962460785295885199272493577688320732492479"
-      "924816869232247165964934329258783950102250973957579510571600738343645738"
-      "494324192997092179207389919761694314131497173265255020084997973676783743"
-      "155205818804439163810572367791175177756227497413804253387084478193655533"
-      "073867420834526162513029462022730109054820067654020201547112002028139700"
-      "141575259123440177362244273712468151750189745559978653234255886219611516"
-      "335924167958029604477064946470184777360934300451421683607013647479513962"
-      "13837722826145437693412532098591327667236328125",
-      -308, first_normal, second_normal, first_normal);
-
-  // penultimate_normal and last_normal.  Round-to-even rounds down.
-  TestHalfwayValue(
-      "1."
-      "797693134862315608353258760581052985162070023416521662616611746258695532"
-      "672923265745300992879465492467506314903358770175220871059269879629062776"
-      "047355692132901909191523941804762171253349609463563872612866401980290377"
-      "995141836029815117562837277714038305214839639239356331336428021390916694"
-      "57927874464075218944",
-      308, penultimate_normal, last_normal, penultimate_normal);
-}
-
-// Same test cases as DoubleRounding, now with new and improved Much Smaller
-// Precision!
-TEST(FromChars, FloatRounding) {
-  const float zero = 0.0;
-  const float first_subnormal = nextafterf(zero, 1.0);
-  const float second_subnormal = nextafterf(first_subnormal, 1.0);
-
-  const float first_normal = FLT_MIN;
-  const float last_subnormal = nextafterf(first_normal, 0.0);
-  const float second_normal = nextafterf(first_normal, 1.0);
-
-  const float last_normal = FLT_MAX;
-  const float penultimate_normal = nextafterf(last_normal, 0.0);
-
-  // Test between zero and first_subnormal.  Round-to-even tie rounds down.
-  TestHalfwayValue(
-      "7."
-      "006492321624085354618647916449580656401309709382578858785341419448955413"
-      "42930300743319094181060791015625",
-      -46, zero, first_subnormal, zero);
-
-  // first_subnormal and second_subnormal.  Round-to-even tie rounds up.
-  TestHalfwayValue(
-      "2."
-      "101947696487225606385594374934874196920392912814773657635602425834686624"
-      "028790902229957282543182373046875",
-      -45, first_subnormal, second_subnormal, second_subnormal);
-
-  // last_subnormal and first_normal.  Round-to-even tie rounds up.
-  TestHalfwayValue(
-      "1."
-      "175494280757364291727882991035766513322858992758990427682963118425003064"
-      "9651730385585324256680905818939208984375",
-      -38, last_subnormal, first_normal, first_normal);
-
-  // first_normal and second_normal.  Round-to-even tie rounds down.
-  TestHalfwayValue(
-      "1."
-      "175494420887210724209590083408724842314472120785184615334540294131831453"
-      "9442813071445925743319094181060791015625",
-      -38, first_normal, second_normal, first_normal);
-
-  // penultimate_normal and last_normal.  Round-to-even rounds down.
-  TestHalfwayValue("3.40282336497324057985868971510891282432", 38,
-                   penultimate_normal, last_normal, penultimate_normal);
-}
-
-TEST(FromChars, Underflow) {
-  // Check that underflow is handled correctly, according to the specification
-  // in DR 3081.
-  double d;
-  float f;
-  absl::from_chars_result result;
-
-  std::string negative_underflow = "-1e-1000";
-  const char* begin = negative_underflow.data();
-  const char* end = begin + negative_underflow.size();
-  d = 100.0;
-  result = absl::from_chars(begin, end, d);
-  EXPECT_EQ(result.ptr, end);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-  EXPECT_TRUE(std::signbit(d));  // negative
-  EXPECT_GE(d, -std::numeric_limits<double>::min());
-  f = 100.0;
-  result = absl::from_chars(begin, end, f);
-  EXPECT_EQ(result.ptr, end);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-  EXPECT_TRUE(std::signbit(f));  // negative
-  EXPECT_GE(f, -std::numeric_limits<float>::min());
-
-  std::string positive_underflow = "1e-1000";
-  begin = positive_underflow.data();
-  end = begin + positive_underflow.size();
-  d = -100.0;
-  result = absl::from_chars(begin, end, d);
-  EXPECT_EQ(result.ptr, end);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-  EXPECT_FALSE(std::signbit(d));  // positive
-  EXPECT_LE(d, std::numeric_limits<double>::min());
-  f = -100.0;
-  result = absl::from_chars(begin, end, f);
-  EXPECT_EQ(result.ptr, end);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-  EXPECT_FALSE(std::signbit(f));  // positive
-  EXPECT_LE(f, std::numeric_limits<float>::min());
-}
-
-TEST(FromChars, Overflow) {
-  // Check that overflow is handled correctly, according to the specification
-  // in DR 3081.
-  double d;
-  float f;
-  absl::from_chars_result result;
-
-  std::string negative_overflow = "-1e1000";
-  const char* begin = negative_overflow.data();
-  const char* end = begin + negative_overflow.size();
-  d = 100.0;
-  result = absl::from_chars(begin, end, d);
-  EXPECT_EQ(result.ptr, end);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-  EXPECT_TRUE(std::signbit(d));  // negative
-  EXPECT_EQ(d, -std::numeric_limits<double>::max());
-  f = 100.0;
-  result = absl::from_chars(begin, end, f);
-  EXPECT_EQ(result.ptr, end);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-  EXPECT_TRUE(std::signbit(f));  // negative
-  EXPECT_EQ(f, -std::numeric_limits<float>::max());
-
-  std::string positive_overflow = "1e1000";
-  begin = positive_overflow.data();
-  end = begin + positive_overflow.size();
-  d = -100.0;
-  result = absl::from_chars(begin, end, d);
-  EXPECT_EQ(result.ptr, end);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-  EXPECT_FALSE(std::signbit(d));  // positive
-  EXPECT_EQ(d, std::numeric_limits<double>::max());
-  f = -100.0;
-  result = absl::from_chars(begin, end, f);
-  EXPECT_EQ(result.ptr, end);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-  EXPECT_FALSE(std::signbit(f));  // positive
-  EXPECT_EQ(f, std::numeric_limits<float>::max());
-}
-
-TEST(FromChars, RegressionTestsFromFuzzer) {
-  absl::string_view src = "0x21900000p00000000099";
-  float f;
-  auto result = absl::from_chars(src.data(), src.data() + src.size(), f);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-}
-
-TEST(FromChars, ReturnValuePtr) {
-  // Check that `ptr` points one past the number scanned, even if that number
-  // is not representable.
-  double d;
-  absl::from_chars_result result;
-
-  std::string normal = "3.14@#$%@#$%";
-  result = absl::from_chars(normal.data(), normal.data() + normal.size(), d);
-  EXPECT_EQ(result.ec, std::errc());
-  EXPECT_EQ(result.ptr - normal.data(), 4);
-
-  std::string overflow = "1e1000@#$%@#$%";
-  result = absl::from_chars(overflow.data(),
-                            overflow.data() + overflow.size(), d);
-  EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-  EXPECT_EQ(result.ptr - overflow.data(), 6);
-
-  std::string garbage = "#$%@#$%";
-  result = absl::from_chars(garbage.data(),
-                            garbage.data() + garbage.size(), d);
-  EXPECT_EQ(result.ec, std::errc::invalid_argument);
-  EXPECT_EQ(result.ptr - garbage.data(), 0);
-}
-
-// Check for a wide range of inputs that strtod() and absl::from_chars() exactly
-// agree on the conversion amount.
-//
-// This test assumes the platform's strtod() uses perfect round_to_nearest
-// rounding.
-TEST(FromChars, TestVersusStrtod) {
-  for (int mantissa = 1000000; mantissa <= 9999999; mantissa += 501) {
-    for (int exponent = -300; exponent < 300; ++exponent) {
-      std::string candidate = absl::StrCat(mantissa, "e", exponent);
-      double strtod_value = strtod(candidate.c_str(), nullptr);
-      double absl_value = 0;
-      absl::from_chars(candidate.data(), candidate.data() + candidate.size(),
-                       absl_value);
-      ASSERT_EQ(strtod_value, absl_value) << candidate;
-    }
-  }
-}
-
-// Check for a wide range of inputs that strtof() and absl::from_chars() exactly
-// agree on the conversion amount.
-//
-// This test assumes the platform's strtof() uses perfect round_to_nearest
-// rounding.
-TEST(FromChars, TestVersusStrtof) {
-  for (int mantissa = 1000000; mantissa <= 9999999; mantissa += 501) {
-    for (int exponent = -43; exponent < 32; ++exponent) {
-      std::string candidate = absl::StrCat(mantissa, "e", exponent);
-      float strtod_value = strtof(candidate.c_str(), nullptr);
-      float absl_value = 0;
-      absl::from_chars(candidate.data(), candidate.data() + candidate.size(),
-                       absl_value);
-      ASSERT_EQ(strtod_value, absl_value) << candidate;
-    }
-  }
-}
-
-// Tests if two floating point values have identical bit layouts.  (EXPECT_EQ
-// is not suitable for NaN testing, since NaNs are never equal.)
-template <typename Float>
-bool Identical(Float a, Float b) {
-  return 0 == memcmp(&a, &b, sizeof(Float));
-}
-
-// Check that NaNs are parsed correctly.  The spec requires that
-// std::from_chars on "NaN(123abc)" return the same value as std::nan("123abc").
-// How such an n-char-sequence affects the generated NaN is unspecified, so we
-// just test for symmetry with std::nan and strtod here.
-//
-// (In Linux, this parses the value as a number and stuffs that number into the
-// free bits of a quiet NaN.)
-TEST(FromChars, NaNDoubles) {
-  for (std::string n_char_sequence :
-       {"", "1", "2", "3", "fff", "FFF", "200000", "400000", "4000000000000",
-        "8000000000000", "abc123", "legal_but_unexpected",
-        "99999999999999999999999", "_"}) {
-    std::string input = absl::StrCat("nan(", n_char_sequence, ")");
-    SCOPED_TRACE(input);
-    double from_chars_double;
-    absl::from_chars(input.data(), input.data() + input.size(),
-                     from_chars_double);
-    double std_nan_double = std::nan(n_char_sequence.c_str());
-    EXPECT_TRUE(Identical(from_chars_double, std_nan_double));
-
-    // Also check that we match strtod()'s behavior.  This test assumes that the
-    // platform has a compliant strtod().
-#if ABSL_STRTOD_HANDLES_NAN_CORRECTLY
-    double strtod_double = strtod(input.c_str(), nullptr);
-    EXPECT_TRUE(Identical(from_chars_double, strtod_double));
-#endif  // ABSL_STRTOD_HANDLES_NAN_CORRECTLY
-
-    // Check that we can parse a negative NaN
-    std::string negative_input = "-" + input;
-    double negative_from_chars_double;
-    absl::from_chars(negative_input.data(),
-                     negative_input.data() + negative_input.size(),
-                     negative_from_chars_double);
-    EXPECT_TRUE(std::signbit(negative_from_chars_double));
-    EXPECT_FALSE(Identical(negative_from_chars_double, from_chars_double));
-    from_chars_double = std::copysign(from_chars_double, -1.0);
-    EXPECT_TRUE(Identical(negative_from_chars_double, from_chars_double));
-  }
-}
-
-TEST(FromChars, NaNFloats) {
-  for (std::string n_char_sequence :
-       {"", "1", "2", "3", "fff", "FFF", "200000", "400000", "4000000000000",
-        "8000000000000", "abc123", "legal_but_unexpected",
-        "99999999999999999999999", "_"}) {
-    std::string input = absl::StrCat("nan(", n_char_sequence, ")");
-    SCOPED_TRACE(input);
-    float from_chars_float;
-    absl::from_chars(input.data(), input.data() + input.size(),
-                     from_chars_float);
-    float std_nan_float = std::nanf(n_char_sequence.c_str());
-    EXPECT_TRUE(Identical(from_chars_float, std_nan_float));
-
-    // Also check that we match strtof()'s behavior.  This test assumes that the
-    // platform has a compliant strtof().
-#if ABSL_STRTOD_HANDLES_NAN_CORRECTLY
-    float strtof_float = strtof(input.c_str(), nullptr);
-    EXPECT_TRUE(Identical(from_chars_float, strtof_float));
-#endif  // ABSL_STRTOD_HANDLES_NAN_CORRECTLY
-
-    // Check that we can parse a negative NaN
-    std::string negative_input = "-" + input;
-    float negative_from_chars_float;
-    absl::from_chars(negative_input.data(),
-                     negative_input.data() + negative_input.size(),
-                     negative_from_chars_float);
-    EXPECT_TRUE(std::signbit(negative_from_chars_float));
-    EXPECT_FALSE(Identical(negative_from_chars_float, from_chars_float));
-    from_chars_float = std::copysign(from_chars_float, -1.0);
-    EXPECT_TRUE(Identical(negative_from_chars_float, from_chars_float));
-  }
-}
-
-// Returns an integer larger than step.  The values grow exponentially.
-int NextStep(int step) {
-  return step + (step >> 2) + 1;
-}
-
-// Test a conversion on a family of input strings, checking that the calculation
-// is correct for in-bounds values, and that overflow and underflow are done
-// correctly for out-of-bounds values.
-//
-// input_generator maps from an integer index to a string to test.
-// expected_generator maps from an integer index to an expected Float value.
-// from_chars conversion of input_generator(i) should result in
-// expected_generator(i).
-//
-// lower_bound and upper_bound denote the smallest and largest values for which
-// the conversion is expected to succeed.
-template <typename Float>
-void TestOverflowAndUnderflow(
-    const std::function<std::string(int)>& input_generator,
-    const std::function<Float(int)>& expected_generator, int lower_bound,
-    int upper_bound) {
-  // test legal values near lower_bound
-  int index, step;
-  for (index = lower_bound, step = 1; index < upper_bound;
-       index += step, step = NextStep(step)) {
-    std::string input = input_generator(index);
-    SCOPED_TRACE(input);
-    Float expected = expected_generator(index);
-    Float actual;
-    auto result =
-        absl::from_chars(input.data(), input.data() + input.size(), actual);
-    EXPECT_EQ(result.ec, std::errc());
-    EXPECT_EQ(expected, actual)
-        << absl::StrFormat("%a vs %a", expected, actual);
-  }
-  // test legal values near upper_bound
-  for (index = upper_bound, step = 1; index > lower_bound;
-       index -= step, step = NextStep(step)) {
-    std::string input = input_generator(index);
-    SCOPED_TRACE(input);
-    Float expected = expected_generator(index);
-    Float actual;
-    auto result =
-        absl::from_chars(input.data(), input.data() + input.size(), actual);
-    EXPECT_EQ(result.ec, std::errc());
-    EXPECT_EQ(expected, actual)
-        << absl::StrFormat("%a vs %a", expected, actual);
-  }
-  // Test underflow values below lower_bound
-  for (index = lower_bound - 1, step = 1; index > -1000000;
-       index -= step, step = NextStep(step)) {
-    std::string input = input_generator(index);
-    SCOPED_TRACE(input);
-    Float actual;
-    auto result =
-        absl::from_chars(input.data(), input.data() + input.size(), actual);
-    EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-    EXPECT_LT(actual, 1.0);  // check for underflow
-  }
-  // Test overflow values above upper_bound
-  for (index = upper_bound + 1, step = 1; index < 1000000;
-       index += step, step = NextStep(step)) {
-    std::string input = input_generator(index);
-    SCOPED_TRACE(input);
-    Float actual;
-    auto result =
-        absl::from_chars(input.data(), input.data() + input.size(), actual);
-    EXPECT_EQ(result.ec, std::errc::result_out_of_range);
-    EXPECT_GT(actual, 1.0);  // check for overflow
-  }
-}
-
-// Check that overflow and underflow are caught correctly for hex doubles.
-//
-// The largest representable double is 0x1.fffffffffffffp+1023, and the
-// smallest representable subnormal is 0x0.0000000000001p-1022, which equals
-// 0x1p-1074.  Therefore 1023 and -1074 are the limits of acceptable exponents
-// in this test.
-TEST(FromChars, HexdecimalDoubleLimits) {
-  auto input_gen = [](int index) { return absl::StrCat("0x1.0p", index); };
-  auto expected_gen = [](int index) { return std::ldexp(1.0, index); };
-  TestOverflowAndUnderflow<double>(input_gen, expected_gen, -1074, 1023);
-}
-
-// Check that overflow and underflow are caught correctly for hex floats.
-//
-// The largest representable float is 0x1.fffffep+127, and the smallest
-// representable subnormal is 0x0.000002p-126, which equals 0x1p-149.
-// Therefore 127 and -149 are the limits of acceptable exponents in this test.
-TEST(FromChars, HexdecimalFloatLimits) {
-  auto input_gen = [](int index) { return absl::StrCat("0x1.0p", index); };
-  auto expected_gen = [](int index) { return std::ldexp(1.0f, index); };
-  TestOverflowAndUnderflow<float>(input_gen, expected_gen, -149, 127);
-}
-
-// Check that overflow and underflow are caught correctly for decimal doubles.
-//
-// The largest representable double is about 1.8e308, and the smallest
-// representable subnormal is about 5e-324.  '1e-324' therefore rounds away from
-// the smallest representable positive value.  -323 and 308 are the limits of
-// acceptable exponents in this test.
-TEST(FromChars, DecimalDoubleLimits) {
-  auto input_gen = [](int index) { return absl::StrCat("1.0e", index); };
-  auto expected_gen = [](int index) { return Pow10(index); };
-  TestOverflowAndUnderflow<double>(input_gen, expected_gen, -323, 308);
-}
-
-// Check that overflow and underflow are caught correctly for decimal floats.
-//
-// The largest representable float is about 3.4e38, and the smallest
-// representable subnormal is about 1.45e-45.  '1e-45' therefore rounds towards
-// the smallest representable positive value.  -45 and 38 are the limits of
-// acceptable exponents in this test.
-TEST(FromChars, DecimalFloatLimits) {
-  auto input_gen = [](int index) { return absl::StrCat("1.0e", index); };
-  auto expected_gen = [](int index) { return Pow10(index); };
-  TestOverflowAndUnderflow<float>(input_gen, expected_gen, -45, 38);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/cord.cc b/third_party/abseil_cpp/absl/strings/cord.cc
deleted file mode 100644
index 9efd135750..0000000000
--- a/third_party/abseil_cpp/absl/strings/cord.cc
+++ /dev/null
@@ -1,1995 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/cord.h"
-
-#include <algorithm>
-#include <atomic>
-#include <cstddef>
-#include <cstdio>
-#include <cstdlib>
-#include <iomanip>
-#include <iostream>
-#include <limits>
-#include <ostream>
-#include <sstream>
-#include <type_traits>
-#include <unordered_set>
-#include <vector>
-
-#include "absl/base/casts.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/container/fixed_array.h"
-#include "absl/container/inlined_vector.h"
-#include "absl/strings/escaping.h"
-#include "absl/strings/internal/cord_internal.h"
-#include "absl/strings/internal/resize_uninitialized.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_join.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-using ::absl::cord_internal::CordRep;
-using ::absl::cord_internal::CordRepConcat;
-using ::absl::cord_internal::CordRepExternal;
-using ::absl::cord_internal::CordRepSubstring;
-
-using ::absl::cord_internal::CONCAT;
-using ::absl::cord_internal::EXTERNAL;
-using ::absl::cord_internal::FLAT;
-using ::absl::cord_internal::SUBSTRING;
-
-namespace cord_internal {
-
-inline CordRepConcat* CordRep::concat() {
-  assert(tag == CONCAT);
-  return static_cast<CordRepConcat*>(this);
-}
-
-inline const CordRepConcat* CordRep::concat() const {
-  assert(tag == CONCAT);
-  return static_cast<const CordRepConcat*>(this);
-}
-
-inline CordRepSubstring* CordRep::substring() {
-  assert(tag == SUBSTRING);
-  return static_cast<CordRepSubstring*>(this);
-}
-
-inline const CordRepSubstring* CordRep::substring() const {
-  assert(tag == SUBSTRING);
-  return static_cast<const CordRepSubstring*>(this);
-}
-
-inline CordRepExternal* CordRep::external() {
-  assert(tag == EXTERNAL);
-  return static_cast<CordRepExternal*>(this);
-}
-
-inline const CordRepExternal* CordRep::external() const {
-  assert(tag == EXTERNAL);
-  return static_cast<const CordRepExternal*>(this);
-}
-
-}  // namespace cord_internal
-
-static const size_t kFlatOverhead = offsetof(CordRep, data);
-
-// Largest and smallest flat node lengths we are willing to allocate
-// Flat allocation size is stored in tag, which currently can encode sizes up
-// to 4K, encoded as multiple of either 8 or 32 bytes.
-// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc.
-static constexpr size_t kMaxFlatSize = 4096;
-static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead;
-static constexpr size_t kMinFlatLength = 32 - kFlatOverhead;
-
-// Prefer copying blocks of at most this size, otherwise reference count.
-static const size_t kMaxBytesToCopy = 511;
-
-// Helper functions for rounded div, and rounding to exact sizes.
-static size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; }
-static size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; }
-
-// Returns the size to the nearest equal or larger value that can be
-// expressed exactly as a tag value.
-static size_t RoundUpForTag(size_t size) {
-  return RoundUp(size, (size <= 1024) ? 8 : 32);
-}
-
-// Converts the allocated size to a tag, rounding down if the size
-// does not exactly match a 'tag expressible' size value. The result is
-// undefined if the size exceeds the maximum size that can be encoded in
-// a tag, i.e., if size is larger than TagToAllocatedSize(<max tag>).
-static uint8_t AllocatedSizeToTag(size_t size) {
-  const size_t tag = (size <= 1024) ? size / 8 : 128 + size / 32 - 1024 / 32;
-  assert(tag <= std::numeric_limits<uint8_t>::max());
-  return tag;
-}
-
-// Converts the provided tag to the corresponding allocated size
-static constexpr size_t TagToAllocatedSize(uint8_t tag) {
-  return (tag <= 128) ? (tag * 8) : (1024 + (tag - 128) * 32);
-}
-
-// Converts the provided tag to the corresponding available data length
-static constexpr size_t TagToLength(uint8_t tag) {
-  return TagToAllocatedSize(tag) - kFlatOverhead;
-}
-
-// Enforce that kMaxFlatSize maps to a well-known exact tag value.
-static_assert(TagToAllocatedSize(224) == kMaxFlatSize, "Bad tag logic");
-
-constexpr uint64_t Fibonacci(unsigned char n, uint64_t a = 0, uint64_t b = 1) {
-  return n == 0 ? a : Fibonacci(n - 1, b, a + b);
-}
-
-static_assert(Fibonacci(63) == 6557470319842,
-              "Fibonacci values computed incorrectly");
-
-// Minimum length required for a given depth tree -- a tree is considered
-// balanced if
-//      length(t) >= min_length[depth(t)]
-// The root node depth is allowed to become twice as large to reduce rebalancing
-// for larger strings (see IsRootBalanced).
-static constexpr uint64_t min_length[] = {
-    Fibonacci(2),          Fibonacci(3),  Fibonacci(4),  Fibonacci(5),
-    Fibonacci(6),          Fibonacci(7),  Fibonacci(8),  Fibonacci(9),
-    Fibonacci(10),         Fibonacci(11), Fibonacci(12), Fibonacci(13),
-    Fibonacci(14),         Fibonacci(15), Fibonacci(16), Fibonacci(17),
-    Fibonacci(18),         Fibonacci(19), Fibonacci(20), Fibonacci(21),
-    Fibonacci(22),         Fibonacci(23), Fibonacci(24), Fibonacci(25),
-    Fibonacci(26),         Fibonacci(27), Fibonacci(28), Fibonacci(29),
-    Fibonacci(30),         Fibonacci(31), Fibonacci(32), Fibonacci(33),
-    Fibonacci(34),         Fibonacci(35), Fibonacci(36), Fibonacci(37),
-    Fibonacci(38),         Fibonacci(39), Fibonacci(40), Fibonacci(41),
-    Fibonacci(42),         Fibonacci(43), Fibonacci(44), Fibonacci(45),
-    Fibonacci(46),         Fibonacci(47),
-    0xffffffffffffffffull,  // Avoid overflow
-};
-
-static const int kMinLengthSize = ABSL_ARRAYSIZE(min_length);
-
-// The inlined size to use with absl::InlinedVector.
-//
-// Note: The InlinedVectors in this file (and in cord.h) do not need to use
-// the same value for their inlined size. The fact that they do is historical.
-// It may be desirable for each to use a different inlined size optimized for
-// that InlinedVector's usage.
-//
-// TODO(jgm): Benchmark to see if there's a more optimal value than 47 for
-// the inlined vector size (47 exists for backward compatibility).
-static const int kInlinedVectorSize = 47;
-
-static inline bool IsRootBalanced(CordRep* node) {
-  if (node->tag != CONCAT) {
-    return true;
-  } else if (node->concat()->depth() <= 15) {
-    return true;
-  } else if (node->concat()->depth() > kMinLengthSize) {
-    return false;
-  } else {
-    // Allow depth to become twice as large as implied by fibonacci rule to
-    // reduce rebalancing for larger strings.
-    return (node->length >= min_length[node->concat()->depth() / 2]);
-  }
-}
-
-static CordRep* Rebalance(CordRep* node);
-static void DumpNode(CordRep* rep, bool include_data, std::ostream* os);
-static bool VerifyNode(CordRep* root, CordRep* start_node,
-                       bool full_validation);
-
-static inline CordRep* VerifyTree(CordRep* node) {
-  // Verification is expensive, so only do it in debug mode.
-  // Even in debug mode we normally do only light validation.
-  // If you are debugging Cord itself, you should define the
-  // macro EXTRA_CORD_VALIDATION, e.g. by adding
-  // --copt=-DEXTRA_CORD_VALIDATION to the blaze line.
-#ifdef EXTRA_CORD_VALIDATION
-  assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/true));
-#else   // EXTRA_CORD_VALIDATION
-  assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/false));
-#endif  // EXTRA_CORD_VALIDATION
-  static_cast<void>(&VerifyNode);
-
-  return node;
-}
-
-// --------------------------------------------------------------------
-// Memory management
-
-inline CordRep* Ref(CordRep* rep) {
-  if (rep != nullptr) {
-    rep->refcount.Increment();
-  }
-  return rep;
-}
-
-// This internal routine is called from the cold path of Unref below. Keeping it
-// in a separate routine allows good inlining of Unref into many profitable call
-// sites. However, the call to this function can be highly disruptive to the
-// register pressure in those callers. To minimize the cost to callers, we use
-// a special LLVM calling convention that preserves most registers. This allows
-// the call to this routine in cold paths to not disrupt the caller's register
-// pressure. This calling convention is not available on all platforms; we
-// intentionally allow LLVM to ignore the attribute rather than attempting to
-// hardcode the list of supported platforms.
-#if defined(__clang__) && !defined(__i386__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wattributes"
-__attribute__((preserve_most))
-#pragma clang diagnostic pop
-#endif
-static void UnrefInternal(CordRep* rep) {
-  assert(rep != nullptr);
-
-  absl::InlinedVector<CordRep*, kInlinedVectorSize> pending;
-  while (true) {
-    assert(!rep->refcount.IsImmortal());
-    if (rep->tag == CONCAT) {
-      CordRepConcat* rep_concat = rep->concat();
-      CordRep* right = rep_concat->right;
-      if (!right->refcount.Decrement()) {
-        pending.push_back(right);
-      }
-      CordRep* left = rep_concat->left;
-      delete rep_concat;
-      rep = nullptr;
-      if (!left->refcount.Decrement()) {
-        rep = left;
-        continue;
-      }
-    } else if (rep->tag == EXTERNAL) {
-      CordRepExternal* rep_external = rep->external();
-      assert(rep_external->releaser_invoker != nullptr);
-      rep_external->releaser_invoker(rep_external);
-      rep = nullptr;
-    } else if (rep->tag == SUBSTRING) {
-      CordRepSubstring* rep_substring = rep->substring();
-      CordRep* child = rep_substring->child;
-      delete rep_substring;
-      rep = nullptr;
-      if (!child->refcount.Decrement()) {
-        rep = child;
-        continue;
-      }
-    } else {
-      // Flat CordReps are allocated and constructed with raw ::operator new
-      // and placement new, and must be destructed and deallocated
-      // accordingly.
-#if defined(__cpp_sized_deallocation)
-      size_t size = TagToAllocatedSize(rep->tag);
-      rep->~CordRep();
-      ::operator delete(rep, size);
-#else
-      rep->~CordRep();
-      ::operator delete(rep);
-#endif
-      rep = nullptr;
-    }
-
-    if (!pending.empty()) {
-      rep = pending.back();
-      pending.pop_back();
-    } else {
-      break;
-    }
-  }
-}
-
-inline void Unref(CordRep* rep) {
-  // Fast-path for two common, hot cases: a null rep and a shared root.
-  if (ABSL_PREDICT_TRUE(rep == nullptr ||
-                        rep->refcount.DecrementExpectHighRefcount())) {
-    return;
-  }
-
-  UnrefInternal(rep);
-}
-
-// Return the depth of a node
-static int Depth(const CordRep* rep) {
-  if (rep->tag == CONCAT) {
-    return rep->concat()->depth();
-  } else {
-    return 0;
-  }
-}
-
-static void SetConcatChildren(CordRepConcat* concat, CordRep* left,
-                              CordRep* right) {
-  concat->left = left;
-  concat->right = right;
-
-  concat->length = left->length + right->length;
-  concat->set_depth(1 + std::max(Depth(left), Depth(right)));
-}
-
-// Create a concatenation of the specified nodes.
-// Does not change the refcounts of "left" and "right".
-// The returned node has a refcount of 1.
-static CordRep* RawConcat(CordRep* left, CordRep* right) {
-  // Avoid making degenerate concat nodes (one child is empty)
-  if (left == nullptr || left->length == 0) {
-    Unref(left);
-    return right;
-  }
-  if (right == nullptr || right->length == 0) {
-    Unref(right);
-    return left;
-  }
-
-  CordRepConcat* rep = new CordRepConcat();
-  rep->tag = CONCAT;
-  SetConcatChildren(rep, left, right);
-
-  return rep;
-}
-
-static CordRep* Concat(CordRep* left, CordRep* right) {
-  CordRep* rep = RawConcat(left, right);
-  if (rep != nullptr && !IsRootBalanced(rep)) {
-    rep = Rebalance(rep);
-  }
-  return VerifyTree(rep);
-}
-
-// Make a balanced tree out of an array of leaf nodes.
-static CordRep* MakeBalancedTree(CordRep** reps, size_t n) {
-  // Make repeated passes over the array, merging adjacent pairs
-  // until we are left with just a single node.
-  while (n > 1) {
-    size_t dst = 0;
-    for (size_t src = 0; src < n; src += 2) {
-      if (src + 1 < n) {
-        reps[dst] = Concat(reps[src], reps[src + 1]);
-      } else {
-        reps[dst] = reps[src];
-      }
-      dst++;
-    }
-    n = dst;
-  }
-
-  return reps[0];
-}
-
-// Create a new flat node.
-static CordRep* NewFlat(size_t length_hint) {
-  if (length_hint <= kMinFlatLength) {
-    length_hint = kMinFlatLength;
-  } else if (length_hint > kMaxFlatLength) {
-    length_hint = kMaxFlatLength;
-  }
-
-  // Round size up so it matches a size we can exactly express in a tag.
-  const size_t size = RoundUpForTag(length_hint + kFlatOverhead);
-  void* const raw_rep = ::operator new(size);
-  CordRep* rep = new (raw_rep) CordRep();
-  rep->tag = AllocatedSizeToTag(size);
-  return VerifyTree(rep);
-}
-
-// Create a new tree out of the specified array.
-// The returned node has a refcount of 1.
-static CordRep* NewTree(const char* data,
-                        size_t length,
-                        size_t alloc_hint) {
-  if (length == 0) return nullptr;
-  absl::FixedArray<CordRep*> reps((length - 1) / kMaxFlatLength + 1);
-  size_t n = 0;
-  do {
-    const size_t len = std::min(length, kMaxFlatLength);
-    CordRep* rep = NewFlat(len + alloc_hint);
-    rep->length = len;
-    memcpy(rep->data, data, len);
-    reps[n++] = VerifyTree(rep);
-    data += len;
-    length -= len;
-  } while (length != 0);
-  return MakeBalancedTree(reps.data(), n);
-}
-
-namespace cord_internal {
-
-void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep) {
-  assert(!data.empty());
-  rep->length = data.size();
-  rep->tag = EXTERNAL;
-  rep->base = data.data();
-  VerifyTree(rep);
-}
-
-}  // namespace cord_internal
-
-static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) {
-  // Never create empty substring nodes
-  if (length == 0) {
-    Unref(child);
-    return nullptr;
-  } else {
-    CordRepSubstring* rep = new CordRepSubstring();
-    assert((offset + length) <= child->length);
-    rep->length = length;
-    rep->tag = SUBSTRING;
-    rep->start = offset;
-    rep->child = child;
-    return VerifyTree(rep);
-  }
-}
-
-// --------------------------------------------------------------------
-// Cord::InlineRep functions
-
-constexpr unsigned char Cord::InlineRep::kMaxInline;
-
-inline void Cord::InlineRep::set_data(const char* data, size_t n,
-                                      bool nullify_tail) {
-  static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15");
-
-  cord_internal::SmallMemmove(data_.as_chars, data, n, nullify_tail);
-  set_tagged_size(static_cast<char>(n));
-}
-
-inline char* Cord::InlineRep::set_data(size_t n) {
-  assert(n <= kMaxInline);
-  ResetToEmpty();
-  set_tagged_size(static_cast<char>(n));
-  return data_.as_chars;
-}
-
-inline CordRep* Cord::InlineRep::force_tree(size_t extra_hint) {
-  size_t len = tagged_size();
-  if (len > kMaxInline) {
-    return data_.as_tree.rep;
-  }
-
-  CordRep* result = NewFlat(len + extra_hint);
-  result->length = len;
-  static_assert(kMinFlatLength >= sizeof(data_.as_chars), "");
-  memcpy(result->data, data_.as_chars, sizeof(data_.as_chars));
-  set_tree(result);
-  return result;
-}
-
-inline void Cord::InlineRep::reduce_size(size_t n) {
-  size_t tag = tagged_size();
-  assert(tag <= kMaxInline);
-  assert(tag >= n);
-  tag -= n;
-  memset(data_.as_chars + tag, 0, n);
-  set_tagged_size(static_cast<char>(tag));
-}
-
-inline void Cord::InlineRep::remove_prefix(size_t n) {
-  cord_internal::SmallMemmove(data_.as_chars, data_.as_chars + n,
-                              tagged_size() - n);
-  reduce_size(n);
-}
-
-void Cord::InlineRep::AppendTree(CordRep* tree) {
-  if (tree == nullptr) return;
-  size_t len = tagged_size();
-  if (len == 0) {
-    set_tree(tree);
-  } else {
-    set_tree(Concat(force_tree(0), tree));
-  }
-}
-
-void Cord::InlineRep::PrependTree(CordRep* tree) {
-  assert(tree != nullptr);
-  size_t len = tagged_size();
-  if (len == 0) {
-    set_tree(tree);
-  } else {
-    set_tree(Concat(tree, force_tree(0)));
-  }
-}
-
-// Searches for a non-full flat node at the rightmost leaf of the tree. If a
-// suitable leaf is found, the function will update the length field for all
-// nodes to account for the size increase. The append region address will be
-// written to region and the actual size increase will be written to size.
-static inline bool PrepareAppendRegion(CordRep* root, char** region,
-                                       size_t* size, size_t max_length) {
-  // Search down the right-hand path for a non-full FLAT node.
-  CordRep* dst = root;
-  while (dst->tag == CONCAT && dst->refcount.IsOne()) {
-    dst = dst->concat()->right;
-  }
-
-  if (dst->tag < FLAT || !dst->refcount.IsOne()) {
-    *region = nullptr;
-    *size = 0;
-    return false;
-  }
-
-  const size_t in_use = dst->length;
-  const size_t capacity = TagToLength(dst->tag);
-  if (in_use == capacity) {
-    *region = nullptr;
-    *size = 0;
-    return false;
-  }
-
-  size_t size_increase = std::min(capacity - in_use, max_length);
-
-  // We need to update the length fields for all nodes, including the leaf node.
-  for (CordRep* rep = root; rep != dst; rep = rep->concat()->right) {
-    rep->length += size_increase;
-  }
-  dst->length += size_increase;
-
-  *region = dst->data + in_use;
-  *size = size_increase;
-  return true;
-}
-
-void Cord::InlineRep::GetAppendRegion(char** region, size_t* size,
-                                      size_t max_length) {
-  if (max_length == 0) {
-    *region = nullptr;
-    *size = 0;
-    return;
-  }
-
-  // Try to fit in the inline buffer if possible.
-  size_t inline_length = tagged_size();
-  if (inline_length < kMaxInline && max_length <= kMaxInline - inline_length) {
-    *region = data_.as_chars + inline_length;
-    *size = max_length;
-    set_tagged_size(static_cast<char>(inline_length + max_length));
-    return;
-  }
-
-  CordRep* root = force_tree(max_length);
-
-  if (PrepareAppendRegion(root, region, size, max_length)) {
-    return;
-  }
-
-  // Allocate new node.
-  CordRep* new_node =
-      NewFlat(std::max(static_cast<size_t>(root->length), max_length));
-  new_node->length =
-      std::min(static_cast<size_t>(TagToLength(new_node->tag)), max_length);
-  *region = new_node->data;
-  *size = new_node->length;
-  replace_tree(Concat(root, new_node));
-}
-
-void Cord::InlineRep::GetAppendRegion(char** region, size_t* size) {
-  const size_t max_length = std::numeric_limits<size_t>::max();
-
-  // Try to fit in the inline buffer if possible.
-  size_t inline_length = tagged_size();
-  if (inline_length < kMaxInline) {
-    *region = data_.as_chars + inline_length;
-    *size = kMaxInline - inline_length;
-    set_tagged_size(kMaxInline);
-    return;
-  }
-
-  CordRep* root = force_tree(max_length);
-
-  if (PrepareAppendRegion(root, region, size, max_length)) {
-    return;
-  }
-
-  // Allocate new node.
-  CordRep* new_node = NewFlat(root->length);
-  new_node->length = TagToLength(new_node->tag);
-  *region = new_node->data;
-  *size = new_node->length;
-  replace_tree(Concat(root, new_node));
-}
-
-// If the rep is a leaf, this will increment the value at total_mem_usage and
-// will return true.
-static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) {
-  if (rep->tag >= FLAT) {
-    *total_mem_usage += TagToAllocatedSize(rep->tag);
-    return true;
-  }
-  if (rep->tag == EXTERNAL) {
-    *total_mem_usage += sizeof(CordRepConcat) + rep->length;
-    return true;
-  }
-  return false;
-}
-
-void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) {
-  ClearSlow();
-
-  data_ = src.data_;
-  if (is_tree()) {
-    Ref(tree());
-  }
-}
-
-void Cord::InlineRep::ClearSlow() {
-  if (is_tree()) {
-    Unref(tree());
-  }
-  ResetToEmpty();
-}
-
-// --------------------------------------------------------------------
-// Constructors and destructors
-
-Cord::Cord(const Cord& src) : contents_(src.contents_) {
-  Ref(contents_.tree());  // Does nothing if contents_ has embedded data
-}
-
-Cord::Cord(absl::string_view src) {
-  const size_t n = src.size();
-  if (n <= InlineRep::kMaxInline) {
-    contents_.set_data(src.data(), n, false);
-  } else {
-    contents_.set_tree(NewTree(src.data(), n, 0));
-  }
-}
-
-template <typename T, Cord::EnableIfString<T>>
-Cord::Cord(T&& src) {
-  if (
-      // String is short: copy data to avoid external block overhead.
-      src.size() <= kMaxBytesToCopy ||
-      // String is wasteful: copy data to avoid pinning too much unused memory.
-      src.size() < src.capacity() / 2
-  ) {
-    if (src.size() <= InlineRep::kMaxInline) {
-      contents_.set_data(src.data(), src.size(), false);
-    } else {
-      contents_.set_tree(NewTree(src.data(), src.size(), 0));
-    }
-  } else {
-    struct StringReleaser {
-      void operator()(absl::string_view /* data */) {}
-      std::string data;
-    };
-    const absl::string_view original_data = src;
-    auto* rep = static_cast<
-        ::absl::cord_internal::CordRepExternalImpl<StringReleaser>*>(
-        absl::cord_internal::NewExternalRep(
-            original_data, StringReleaser{std::forward<T>(src)}));
-    // Moving src may have invalidated its data pointer, so adjust it.
-    rep->base = rep->template get<0>().data.data();
-    contents_.set_tree(rep);
-  }
-}
-
-template Cord::Cord(std::string&& src);
-
-// The destruction code is separate so that the compiler can determine
-// that it does not need to call the destructor on a moved-from Cord.
-void Cord::DestroyCordSlow() {
-  Unref(VerifyTree(contents_.tree()));
-}
-
-// --------------------------------------------------------------------
-// Mutators
-
-void Cord::Clear() {
-  Unref(contents_.clear());
-}
-
-Cord& Cord::operator=(absl::string_view src) {
-
-  const char* data = src.data();
-  size_t length = src.size();
-  CordRep* tree = contents_.tree();
-  if (length <= InlineRep::kMaxInline) {
-    // Embed into this->contents_
-    contents_.set_data(data, length, true);
-    Unref(tree);
-    return *this;
-  }
-  if (tree != nullptr && tree->tag >= FLAT &&
-      TagToLength(tree->tag) >= length && tree->refcount.IsOne()) {
-    // Copy in place if the existing FLAT node is reusable.
-    memmove(tree->data, data, length);
-    tree->length = length;
-    VerifyTree(tree);
-    return *this;
-  }
-  contents_.set_tree(NewTree(data, length, 0));
-  Unref(tree);
-  return *this;
-}
-
-template <typename T, Cord::EnableIfString<T>>
-Cord& Cord::operator=(T&& src) {
-  if (src.size() <= kMaxBytesToCopy) {
-    *this = absl::string_view(src);
-  } else {
-    *this = Cord(std::forward<T>(src));
-  }
-  return *this;
-}
-
-template Cord& Cord::operator=(std::string&& src);
-
-// TODO(sanjay): Move to Cord::InlineRep section of file.  For now,
-// we keep it here to make diffs easier.
-void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) {
-  if (src_size == 0) return;  // memcpy(_, nullptr, 0) is undefined.
-  // Try to fit in the inline buffer if possible.
-  size_t inline_length = tagged_size();
-  if (inline_length < kMaxInline && src_size <= kMaxInline - inline_length) {
-    // Append new data to embedded array
-    set_tagged_size(static_cast<char>(inline_length + src_size));
-    memcpy(data_.as_chars + inline_length, src_data, src_size);
-    return;
-  }
-
-  CordRep* root = tree();
-
-  size_t appended = 0;
-  if (root) {
-    char* region;
-    if (PrepareAppendRegion(root, &region, &appended, src_size)) {
-      memcpy(region, src_data, appended);
-    }
-  } else {
-    // It is possible that src_data == data_, but when we transition from an
-    // InlineRep to a tree we need to assign data_ = root via set_tree. To
-    // avoid corrupting the source data before we copy it, delay calling
-    // set_tree until after we've copied data.
-    // We are going from an inline size to beyond inline size. Make the new size
-    // either double the inlined size, or the added size + 10%.
-    const size_t size1 = inline_length * 2 + src_size;
-    const size_t size2 = inline_length + src_size / 10;
-    root = NewFlat(std::max<size_t>(size1, size2));
-    appended = std::min(src_size, TagToLength(root->tag) - inline_length);
-    memcpy(root->data, data_.as_chars, inline_length);
-    memcpy(root->data + inline_length, src_data, appended);
-    root->length = inline_length + appended;
-    set_tree(root);
-  }
-
-  src_data += appended;
-  src_size -= appended;
-  if (src_size == 0) {
-    return;
-  }
-
-  // Use new block(s) for any remaining bytes that were not handled above.
-  // Alloc extra memory only if the right child of the root of the new tree is
-  // going to be a FLAT node, which will permit further inplace appends.
-  size_t length = src_size;
-  if (src_size < kMaxFlatLength) {
-    // The new length is either
-    // - old size + 10%
-    // - old_size + src_size
-    // This will cause a reasonable conservative step-up in size that is still
-    // large enough to avoid excessive amounts of small fragments being added.
-    length = std::max<size_t>(root->length / 10, src_size);
-  }
-  set_tree(Concat(root, NewTree(src_data, src_size, length - src_size)));
-}
-
-inline CordRep* Cord::TakeRep() const& {
-  return Ref(contents_.tree());
-}
-
-inline CordRep* Cord::TakeRep() && {
-  CordRep* rep = contents_.tree();
-  contents_.clear();
-  return rep;
-}
-
-template <typename C>
-inline void Cord::AppendImpl(C&& src) {
-  if (empty()) {
-    // In case of an empty destination avoid allocating a new node, do not copy
-    // data.
-    *this = std::forward<C>(src);
-    return;
-  }
-
-  // For short cords, it is faster to copy data if there is room in dst.
-  const size_t src_size = src.contents_.size();
-  if (src_size <= kMaxBytesToCopy) {
-    CordRep* src_tree = src.contents_.tree();
-    if (src_tree == nullptr) {
-      // src has embedded data.
-      contents_.AppendArray(src.contents_.data(), src_size);
-      return;
-    }
-    if (src_tree->tag >= FLAT) {
-      // src tree just has one flat node.
-      contents_.AppendArray(src_tree->data, src_size);
-      return;
-    }
-    if (&src == this) {
-      // ChunkIterator below assumes that src is not modified during traversal.
-      Append(Cord(src));
-      return;
-    }
-    // TODO(mec): Should we only do this if "dst" has space?
-    for (absl::string_view chunk : src.Chunks()) {
-      Append(chunk);
-    }
-    return;
-  }
-
-  contents_.AppendTree(std::forward<C>(src).TakeRep());
-}
-
-void Cord::Append(const Cord& src) { AppendImpl(src); }
-
-void Cord::Append(Cord&& src) { AppendImpl(std::move(src)); }
-
-template <typename T, Cord::EnableIfString<T>>
-void Cord::Append(T&& src) {
-  if (src.size() <= kMaxBytesToCopy) {
-    Append(absl::string_view(src));
-  } else {
-    Append(Cord(std::forward<T>(src)));
-  }
-}
-
-template void Cord::Append(std::string&& src);
-
-void Cord::Prepend(const Cord& src) {
-  CordRep* src_tree = src.contents_.tree();
-  if (src_tree != nullptr) {
-    Ref(src_tree);
-    contents_.PrependTree(src_tree);
-    return;
-  }
-
-  // `src` cord is inlined.
-  absl::string_view src_contents(src.contents_.data(), src.contents_.size());
-  return Prepend(src_contents);
-}
-
-void Cord::Prepend(absl::string_view src) {
-  if (src.empty()) return;  // memcpy(_, nullptr, 0) is undefined.
-  size_t cur_size = contents_.size();
-  if (!contents_.is_tree() && cur_size + src.size() <= InlineRep::kMaxInline) {
-    // Use embedded storage.
-    char data[InlineRep::kMaxInline + 1] = {0};
-    data[InlineRep::kMaxInline] = cur_size + src.size();  // set size
-    memcpy(data, src.data(), src.size());
-    memcpy(data + src.size(), contents_.data(), cur_size);
-    memcpy(reinterpret_cast<void*>(&contents_), data,
-           InlineRep::kMaxInline + 1);
-  } else {
-    contents_.PrependTree(NewTree(src.data(), src.size(), 0));
-  }
-}
-
-template <typename T, Cord::EnableIfString<T>>
-inline void Cord::Prepend(T&& src) {
-  if (src.size() <= kMaxBytesToCopy) {
-    Prepend(absl::string_view(src));
-  } else {
-    Prepend(Cord(std::forward<T>(src)));
-  }
-}
-
-template void Cord::Prepend(std::string&& src);
-
-static CordRep* RemovePrefixFrom(CordRep* node, size_t n) {
-  if (n >= node->length) return nullptr;
-  if (n == 0) return Ref(node);
-  absl::InlinedVector<CordRep*, kInlinedVectorSize> rhs_stack;
-
-  while (node->tag == CONCAT) {
-    assert(n <= node->length);
-    if (n < node->concat()->left->length) {
-      // Push right to stack, descend left.
-      rhs_stack.push_back(node->concat()->right);
-      node = node->concat()->left;
-    } else {
-      // Drop left, descend right.
-      n -= node->concat()->left->length;
-      node = node->concat()->right;
-    }
-  }
-  assert(n <= node->length);
-
-  if (n == 0) {
-    Ref(node);
-  } else {
-    size_t start = n;
-    size_t len = node->length - n;
-    if (node->tag == SUBSTRING) {
-      // Consider in-place update of node, similar to in RemoveSuffixFrom().
-      start += node->substring()->start;
-      node = node->substring()->child;
-    }
-    node = NewSubstring(Ref(node), start, len);
-  }
-  while (!rhs_stack.empty()) {
-    node = Concat(node, Ref(rhs_stack.back()));
-    rhs_stack.pop_back();
-  }
-  return node;
-}
-
-// RemoveSuffixFrom() is very similar to RemovePrefixFrom(), with the
-// exception that removing a suffix has an optimization where a node may be
-// edited in place iff that node and all its ancestors have a refcount of 1.
-static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) {
-  if (n >= node->length) return nullptr;
-  if (n == 0) return Ref(node);
-  absl::InlinedVector<CordRep*, kInlinedVectorSize> lhs_stack;
-  bool inplace_ok = node->refcount.IsOne();
-
-  while (node->tag == CONCAT) {
-    assert(n <= node->length);
-    if (n < node->concat()->right->length) {
-      // Push left to stack, descend right.
-      lhs_stack.push_back(node->concat()->left);
-      node = node->concat()->right;
-    } else {
-      // Drop right, descend left.
-      n -= node->concat()->right->length;
-      node = node->concat()->left;
-    }
-    inplace_ok = inplace_ok && node->refcount.IsOne();
-  }
-  assert(n <= node->length);
-
-  if (n == 0) {
-    Ref(node);
-  } else if (inplace_ok && node->tag != EXTERNAL) {
-    // Consider making a new buffer if the current node capacity is much
-    // larger than the new length.
-    Ref(node);
-    node->length -= n;
-  } else {
-    size_t start = 0;
-    size_t len = node->length - n;
-    if (node->tag == SUBSTRING) {
-      start = node->substring()->start;
-      node = node->substring()->child;
-    }
-    node = NewSubstring(Ref(node), start, len);
-  }
-  while (!lhs_stack.empty()) {
-    node = Concat(Ref(lhs_stack.back()), node);
-    lhs_stack.pop_back();
-  }
-  return node;
-}
-
-void Cord::RemovePrefix(size_t n) {
-  ABSL_INTERNAL_CHECK(n <= size(),
-                      absl::StrCat("Requested prefix size ", n,
-                                   " exceeds Cord's size ", size()));
-  CordRep* tree = contents_.tree();
-  if (tree == nullptr) {
-    contents_.remove_prefix(n);
-  } else {
-    CordRep* newrep = RemovePrefixFrom(tree, n);
-    Unref(tree);
-    contents_.replace_tree(VerifyTree(newrep));
-  }
-}
-
-void Cord::RemoveSuffix(size_t n) {
-  ABSL_INTERNAL_CHECK(n <= size(),
-                      absl::StrCat("Requested suffix size ", n,
-                                   " exceeds Cord's size ", size()));
-  CordRep* tree = contents_.tree();
-  if (tree == nullptr) {
-    contents_.reduce_size(n);
-  } else {
-    CordRep* newrep = RemoveSuffixFrom(tree, n);
-    Unref(tree);
-    contents_.replace_tree(VerifyTree(newrep));
-  }
-}
-
-// Work item for NewSubRange().
-struct SubRange {
-  SubRange(CordRep* a_node, size_t a_pos, size_t a_n)
-      : node(a_node), pos(a_pos), n(a_n) {}
-  CordRep* node;  // nullptr means concat last 2 results.
-  size_t pos;
-  size_t n;
-};
-
-static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) {
-  absl::InlinedVector<CordRep*, kInlinedVectorSize> results;
-  absl::InlinedVector<SubRange, kInlinedVectorSize> todo;
-  todo.push_back(SubRange(node, pos, n));
-  do {
-    const SubRange& sr = todo.back();
-    node = sr.node;
-    pos = sr.pos;
-    n = sr.n;
-    todo.pop_back();
-
-    if (node == nullptr) {
-      assert(results.size() >= 2);
-      CordRep* right = results.back();
-      results.pop_back();
-      CordRep* left = results.back();
-      results.pop_back();
-      results.push_back(Concat(left, right));
-    } else if (pos == 0 && n == node->length) {
-      results.push_back(Ref(node));
-    } else if (node->tag != CONCAT) {
-      if (node->tag == SUBSTRING) {
-        pos += node->substring()->start;
-        node = node->substring()->child;
-      }
-      results.push_back(NewSubstring(Ref(node), pos, n));
-    } else if (pos + n <= node->concat()->left->length) {
-      todo.push_back(SubRange(node->concat()->left, pos, n));
-    } else if (pos >= node->concat()->left->length) {
-      pos -= node->concat()->left->length;
-      todo.push_back(SubRange(node->concat()->right, pos, n));
-    } else {
-      size_t left_n = node->concat()->left->length - pos;
-      todo.push_back(SubRange(nullptr, 0, 0));  // Concat()
-      todo.push_back(SubRange(node->concat()->right, 0, n - left_n));
-      todo.push_back(SubRange(node->concat()->left, pos, left_n));
-    }
-  } while (!todo.empty());
-  assert(results.size() == 1);
-  return results[0];
-}
-
-Cord Cord::Subcord(size_t pos, size_t new_size) const {
-  Cord sub_cord;
-  size_t length = size();
-  if (pos > length) pos = length;
-  if (new_size > length - pos) new_size = length - pos;
-  CordRep* tree = contents_.tree();
-  if (tree == nullptr) {
-    // sub_cord is newly constructed, no need to re-zero-out the tail of
-    // contents_ memory.
-    sub_cord.contents_.set_data(contents_.data() + pos, new_size, false);
-  } else if (new_size == 0) {
-    // We want to return empty subcord, so nothing to do.
-  } else if (new_size <= InlineRep::kMaxInline) {
-    Cord::ChunkIterator it = chunk_begin();
-    it.AdvanceBytes(pos);
-    char* dest = sub_cord.contents_.data_.as_chars;
-    size_t remaining_size = new_size;
-    while (remaining_size > it->size()) {
-      cord_internal::SmallMemmove(dest, it->data(), it->size());
-      remaining_size -= it->size();
-      dest += it->size();
-      ++it;
-    }
-    cord_internal::SmallMemmove(dest, it->data(), remaining_size);
-    sub_cord.contents_.set_tagged_size(new_size);
-  } else {
-    sub_cord.contents_.set_tree(NewSubRange(tree, pos, new_size));
-  }
-  return sub_cord;
-}
-
-// --------------------------------------------------------------------
-// Balancing
-
-class CordForest {
- public:
-  explicit CordForest(size_t length)
-      : root_length_(length), trees_(kMinLengthSize, nullptr) {}
-
-  void Build(CordRep* cord_root) {
-    std::vector<CordRep*> pending = {cord_root};
-
-    while (!pending.empty()) {
-      CordRep* node = pending.back();
-      pending.pop_back();
-      CheckNode(node);
-      if (ABSL_PREDICT_FALSE(node->tag != CONCAT)) {
-        AddNode(node);
-        continue;
-      }
-
-      CordRepConcat* concat_node = node->concat();
-      if (concat_node->depth() >= kMinLengthSize ||
-          concat_node->length < min_length[concat_node->depth()]) {
-        pending.push_back(concat_node->right);
-        pending.push_back(concat_node->left);
-
-        if (concat_node->refcount.IsOne()) {
-          concat_node->left = concat_freelist_;
-          concat_freelist_ = concat_node;
-        } else {
-          Ref(concat_node->right);
-          Ref(concat_node->left);
-          Unref(concat_node);
-        }
-      } else {
-        AddNode(node);
-      }
-    }
-  }
-
-  CordRep* ConcatNodes() {
-    CordRep* sum = nullptr;
-    for (auto* node : trees_) {
-      if (node == nullptr) continue;
-
-      sum = PrependNode(node, sum);
-      root_length_ -= node->length;
-      if (root_length_ == 0) break;
-    }
-    ABSL_INTERNAL_CHECK(sum != nullptr, "Failed to locate sum node");
-    return VerifyTree(sum);
-  }
-
- private:
-  CordRep* AppendNode(CordRep* node, CordRep* sum) {
-    return (sum == nullptr) ? node : MakeConcat(sum, node);
-  }
-
-  CordRep* PrependNode(CordRep* node, CordRep* sum) {
-    return (sum == nullptr) ? node : MakeConcat(node, sum);
-  }
-
-  void AddNode(CordRep* node) {
-    CordRep* sum = nullptr;
-
-    // Collect together everything with which we will merge with node
-    int i = 0;
-    for (; node->length > min_length[i + 1]; ++i) {
-      auto& tree_at_i = trees_[i];
-
-      if (tree_at_i == nullptr) continue;
-      sum = PrependNode(tree_at_i, sum);
-      tree_at_i = nullptr;
-    }
-
-    sum = AppendNode(node, sum);
-
-    // Insert sum into appropriate place in the forest
-    for (; sum->length >= min_length[i]; ++i) {
-      auto& tree_at_i = trees_[i];
-      if (tree_at_i == nullptr) continue;
-
-      sum = MakeConcat(tree_at_i, sum);
-      tree_at_i = nullptr;
-    }
-
-    // min_length[0] == 1, which means sum->length >= min_length[0]
-    assert(i > 0);
-    trees_[i - 1] = sum;
-  }
-
-  // Make concat node trying to resue existing CordRepConcat nodes we
-  // already collected in the concat_freelist_.
-  CordRep* MakeConcat(CordRep* left, CordRep* right) {
-    if (concat_freelist_ == nullptr) return RawConcat(left, right);
-
-    CordRepConcat* rep = concat_freelist_;
-    if (concat_freelist_->left == nullptr) {
-      concat_freelist_ = nullptr;
-    } else {
-      concat_freelist_ = concat_freelist_->left->concat();
-    }
-    SetConcatChildren(rep, left, right);
-
-    return rep;
-  }
-
-  static void CheckNode(CordRep* node) {
-    ABSL_INTERNAL_CHECK(node->length != 0u, "");
-    if (node->tag == CONCAT) {
-      ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, "");
-      ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, "");
-      ABSL_INTERNAL_CHECK(node->length == (node->concat()->left->length +
-                                           node->concat()->right->length),
-                          "");
-    }
-  }
-
-  size_t root_length_;
-
-  // use an inlined vector instead of a flat array to get bounds checking
-  absl::InlinedVector<CordRep*, kInlinedVectorSize> trees_;
-
-  // List of concat nodes we can re-use for Cord balancing.
-  CordRepConcat* concat_freelist_ = nullptr;
-};
-
-static CordRep* Rebalance(CordRep* node) {
-  VerifyTree(node);
-  assert(node->tag == CONCAT);
-
-  if (node->length == 0) {
-    return nullptr;
-  }
-
-  CordForest forest(node->length);
-  forest.Build(node);
-  return forest.ConcatNodes();
-}
-
-// --------------------------------------------------------------------
-// Comparators
-
-namespace {
-
-int ClampResult(int memcmp_res) {
-  return static_cast<int>(memcmp_res > 0) - static_cast<int>(memcmp_res < 0);
-}
-
-int CompareChunks(absl::string_view* lhs, absl::string_view* rhs,
-                  size_t* size_to_compare) {
-  size_t compared_size = std::min(lhs->size(), rhs->size());
-  assert(*size_to_compare >= compared_size);
-  *size_to_compare -= compared_size;
-
-  int memcmp_res = ::memcmp(lhs->data(), rhs->data(), compared_size);
-  if (memcmp_res != 0) return memcmp_res;
-
-  lhs->remove_prefix(compared_size);
-  rhs->remove_prefix(compared_size);
-
-  return 0;
-}
-
-// This overload set computes comparison results from memcmp result. This
-// interface is used inside GenericCompare below. Differet implementations
-// are specialized for int and bool. For int we clamp result to {-1, 0, 1}
-// set. For bool we just interested in "value == 0".
-template <typename ResultType>
-ResultType ComputeCompareResult(int memcmp_res) {
-  return ClampResult(memcmp_res);
-}
-template <>
-bool ComputeCompareResult<bool>(int memcmp_res) {
-  return memcmp_res == 0;
-}
-
-}  // namespace
-
-// Helper routine. Locates the first flat chunk of the Cord without
-// initializing the iterator.
-inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const {
-  size_t n = tagged_size();
-  if (n <= kMaxInline) {
-    return absl::string_view(data_.as_chars, n);
-  }
-
-  CordRep* node = tree();
-  if (node->tag >= FLAT) {
-    return absl::string_view(node->data, node->length);
-  }
-
-  if (node->tag == EXTERNAL) {
-    return absl::string_view(node->external()->base, node->length);
-  }
-
-  // Walk down the left branches until we hit a non-CONCAT node.
-  while (node->tag == CONCAT) {
-    node = node->concat()->left;
-  }
-
-  // Get the child node if we encounter a SUBSTRING.
-  size_t offset = 0;
-  size_t length = node->length;
-  assert(length != 0);
-
-  if (node->tag == SUBSTRING) {
-    offset = node->substring()->start;
-    node = node->substring()->child;
-  }
-
-  if (node->tag >= FLAT) {
-    return absl::string_view(node->data + offset, length);
-  }
-
-  assert((node->tag == EXTERNAL) && "Expect FLAT or EXTERNAL node here");
-
-  return absl::string_view(node->external()->base + offset, length);
-}
-
-inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size,
-                                 size_t size_to_compare) const {
-  auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) {
-    if (!chunk->empty()) return true;
-    ++*it;
-    if (it->bytes_remaining_ == 0) return false;
-    *chunk = **it;
-    return true;
-  };
-
-  Cord::ChunkIterator lhs_it = chunk_begin();
-
-  // compared_size is inside first chunk.
-  absl::string_view lhs_chunk =
-      (lhs_it.bytes_remaining_ != 0) ? *lhs_it : absl::string_view();
-  assert(compared_size <= lhs_chunk.size());
-  assert(compared_size <= rhs.size());
-  lhs_chunk.remove_prefix(compared_size);
-  rhs.remove_prefix(compared_size);
-  size_to_compare -= compared_size;  // skip already compared size.
-
-  while (advance(&lhs_it, &lhs_chunk) && !rhs.empty()) {
-    int comparison_result = CompareChunks(&lhs_chunk, &rhs, &size_to_compare);
-    if (comparison_result != 0) return comparison_result;
-    if (size_to_compare == 0) return 0;
-  }
-
-  return static_cast<int>(rhs.empty()) - static_cast<int>(lhs_chunk.empty());
-}
-
-inline int Cord::CompareSlowPath(const Cord& rhs, size_t compared_size,
-                                 size_t size_to_compare) const {
-  auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) {
-    if (!chunk->empty()) return true;
-    ++*it;
-    if (it->bytes_remaining_ == 0) return false;
-    *chunk = **it;
-    return true;
-  };
-
-  Cord::ChunkIterator lhs_it = chunk_begin();
-  Cord::ChunkIterator rhs_it = rhs.chunk_begin();
-
-  // compared_size is inside both first chunks.
-  absl::string_view lhs_chunk =
-      (lhs_it.bytes_remaining_ != 0) ? *lhs_it : absl::string_view();
-  absl::string_view rhs_chunk =
-      (rhs_it.bytes_remaining_ != 0) ? *rhs_it : absl::string_view();
-  assert(compared_size <= lhs_chunk.size());
-  assert(compared_size <= rhs_chunk.size());
-  lhs_chunk.remove_prefix(compared_size);
-  rhs_chunk.remove_prefix(compared_size);
-  size_to_compare -= compared_size;  // skip already compared size.
-
-  while (advance(&lhs_it, &lhs_chunk) && advance(&rhs_it, &rhs_chunk)) {
-    int memcmp_res = CompareChunks(&lhs_chunk, &rhs_chunk, &size_to_compare);
-    if (memcmp_res != 0) return memcmp_res;
-    if (size_to_compare == 0) return 0;
-  }
-
-  return static_cast<int>(rhs_chunk.empty()) -
-         static_cast<int>(lhs_chunk.empty());
-}
-
-inline absl::string_view Cord::GetFirstChunk(const Cord& c) {
-  return c.contents_.FindFlatStartPiece();
-}
-inline absl::string_view Cord::GetFirstChunk(absl::string_view sv) {
-  return sv;
-}
-
-// Compares up to 'size_to_compare' bytes of 'lhs' with 'rhs'. It is assumed
-// that 'size_to_compare' is greater that size of smallest of first chunks.
-template <typename ResultType, typename RHS>
-ResultType GenericCompare(const Cord& lhs, const RHS& rhs,
-                          size_t size_to_compare) {
-  absl::string_view lhs_chunk = Cord::GetFirstChunk(lhs);
-  absl::string_view rhs_chunk = Cord::GetFirstChunk(rhs);
-
-  size_t compared_size = std::min(lhs_chunk.size(), rhs_chunk.size());
-  assert(size_to_compare >= compared_size);
-  int memcmp_res = ::memcmp(lhs_chunk.data(), rhs_chunk.data(), compared_size);
-  if (compared_size == size_to_compare || memcmp_res != 0) {
-    return ComputeCompareResult<ResultType>(memcmp_res);
-  }
-
-  return ComputeCompareResult<ResultType>(
-      lhs.CompareSlowPath(rhs, compared_size, size_to_compare));
-}
-
-bool Cord::EqualsImpl(absl::string_view rhs, size_t size_to_compare) const {
-  return GenericCompare<bool>(*this, rhs, size_to_compare);
-}
-
-bool Cord::EqualsImpl(const Cord& rhs, size_t size_to_compare) const {
-  return GenericCompare<bool>(*this, rhs, size_to_compare);
-}
-
-template <typename RHS>
-inline int SharedCompareImpl(const Cord& lhs, const RHS& rhs) {
-  size_t lhs_size = lhs.size();
-  size_t rhs_size = rhs.size();
-  if (lhs_size == rhs_size) {
-    return GenericCompare<int>(lhs, rhs, lhs_size);
-  }
-  if (lhs_size < rhs_size) {
-    auto data_comp_res = GenericCompare<int>(lhs, rhs, lhs_size);
-    return data_comp_res == 0 ? -1 : data_comp_res;
-  }
-
-  auto data_comp_res = GenericCompare<int>(lhs, rhs, rhs_size);
-  return data_comp_res == 0 ? +1 : data_comp_res;
-}
-
-int Cord::Compare(absl::string_view rhs) const {
-  return SharedCompareImpl(*this, rhs);
-}
-
-int Cord::CompareImpl(const Cord& rhs) const {
-  return SharedCompareImpl(*this, rhs);
-}
-
-bool Cord::EndsWith(absl::string_view rhs) const {
-  size_t my_size = size();
-  size_t rhs_size = rhs.size();
-
-  if (my_size < rhs_size) return false;
-
-  Cord tmp(*this);
-  tmp.RemovePrefix(my_size - rhs_size);
-  return tmp.EqualsImpl(rhs, rhs_size);
-}
-
-bool Cord::EndsWith(const Cord& rhs) const {
-  size_t my_size = size();
-  size_t rhs_size = rhs.size();
-
-  if (my_size < rhs_size) return false;
-
-  Cord tmp(*this);
-  tmp.RemovePrefix(my_size - rhs_size);
-  return tmp.EqualsImpl(rhs, rhs_size);
-}
-
-// --------------------------------------------------------------------
-// Misc.
-
-Cord::operator std::string() const {
-  std::string s;
-  absl::CopyCordToString(*this, &s);
-  return s;
-}
-
-void CopyCordToString(const Cord& src, std::string* dst) {
-  if (!src.contents_.is_tree()) {
-    src.contents_.CopyTo(dst);
-  } else {
-    absl::strings_internal::STLStringResizeUninitialized(dst, src.size());
-    src.CopyToArraySlowPath(&(*dst)[0]);
-  }
-}
-
-void Cord::CopyToArraySlowPath(char* dst) const {
-  assert(contents_.is_tree());
-  absl::string_view fragment;
-  if (GetFlatAux(contents_.tree(), &fragment)) {
-    memcpy(dst, fragment.data(), fragment.size());
-    return;
-  }
-  for (absl::string_view chunk : Chunks()) {
-    memcpy(dst, chunk.data(), chunk.size());
-    dst += chunk.size();
-  }
-}
-
-Cord::ChunkIterator& Cord::ChunkIterator::operator++() {
-  ABSL_HARDENING_ASSERT(bytes_remaining_ > 0 &&
-                        "Attempted to iterate past `end()`");
-  assert(bytes_remaining_ >= current_chunk_.size());
-  bytes_remaining_ -= current_chunk_.size();
-
-  if (stack_of_right_children_.empty()) {
-    assert(!current_chunk_.empty());  // Called on invalid iterator.
-    // We have reached the end of the Cord.
-    return *this;
-  }
-
-  // Process the next node on the stack.
-  CordRep* node = stack_of_right_children_.back();
-  stack_of_right_children_.pop_back();
-
-  // Walk down the left branches until we hit a non-CONCAT node. Save the
-  // right children to the stack for subsequent traversal.
-  while (node->tag == CONCAT) {
-    stack_of_right_children_.push_back(node->concat()->right);
-    node = node->concat()->left;
-  }
-
-  // Get the child node if we encounter a SUBSTRING.
-  size_t offset = 0;
-  size_t length = node->length;
-  if (node->tag == SUBSTRING) {
-    offset = node->substring()->start;
-    node = node->substring()->child;
-  }
-
-  assert(node->tag == EXTERNAL || node->tag >= FLAT);
-  assert(length != 0);
-  const char* data =
-      node->tag == EXTERNAL ? node->external()->base : node->data;
-  current_chunk_ = absl::string_view(data + offset, length);
-  current_leaf_ = node;
-  return *this;
-}
-
-Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) {
-  ABSL_HARDENING_ASSERT(bytes_remaining_ >= n &&
-                        "Attempted to iterate past `end()`");
-  Cord subcord;
-
-  if (n <= InlineRep::kMaxInline) {
-    // Range to read fits in inline data. Flatten it.
-    char* data = subcord.contents_.set_data(n);
-    while (n > current_chunk_.size()) {
-      memcpy(data, current_chunk_.data(), current_chunk_.size());
-      data += current_chunk_.size();
-      n -= current_chunk_.size();
-      ++*this;
-    }
-    memcpy(data, current_chunk_.data(), n);
-    if (n < current_chunk_.size()) {
-      RemoveChunkPrefix(n);
-    } else if (n > 0) {
-      ++*this;
-    }
-    return subcord;
-  }
-  if (n < current_chunk_.size()) {
-    // Range to read is a proper subrange of the current chunk.
-    assert(current_leaf_ != nullptr);
-    CordRep* subnode = Ref(current_leaf_);
-    const char* data =
-        subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data;
-    subnode = NewSubstring(subnode, current_chunk_.data() - data, n);
-    subcord.contents_.set_tree(VerifyTree(subnode));
-    RemoveChunkPrefix(n);
-    return subcord;
-  }
-
-  // Range to read begins with a proper subrange of the current chunk.
-  assert(!current_chunk_.empty());
-  assert(current_leaf_ != nullptr);
-  CordRep* subnode = Ref(current_leaf_);
-  if (current_chunk_.size() < subnode->length) {
-    const char* data =
-        subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data;
-    subnode = NewSubstring(subnode, current_chunk_.data() - data,
-                           current_chunk_.size());
-  }
-  n -= current_chunk_.size();
-  bytes_remaining_ -= current_chunk_.size();
-
-  // Process the next node(s) on the stack, reading whole subtrees depending on
-  // their length and how many bytes we are advancing.
-  CordRep* node = nullptr;
-  while (!stack_of_right_children_.empty()) {
-    node = stack_of_right_children_.back();
-    stack_of_right_children_.pop_back();
-    if (node->length > n) break;
-    // TODO(qrczak): This might unnecessarily recreate existing concat nodes.
-    // Avoiding that would need pretty complicated logic (instead of
-    // current_leaf_, keep current_subtree_ which points to the highest node
-    // such that the current leaf can be found on the path of left children
-    // starting from current_subtree_; delay creating subnode while node is
-    // below current_subtree_; find the proper node along the path of left
-    // children starting from current_subtree_ if this loop exits while staying
-    // below current_subtree_; etc.; alternatively, push parents instead of
-    // right children on the stack).
-    subnode = Concat(subnode, Ref(node));
-    n -= node->length;
-    bytes_remaining_ -= node->length;
-    node = nullptr;
-  }
-
-  if (node == nullptr) {
-    // We have reached the end of the Cord.
-    assert(bytes_remaining_ == 0);
-    subcord.contents_.set_tree(VerifyTree(subnode));
-    return subcord;
-  }
-
-  // Walk down the appropriate branches until we hit a non-CONCAT node. Save the
-  // right children to the stack for subsequent traversal.
-  while (node->tag == CONCAT) {
-    if (node->concat()->left->length > n) {
-      // Push right, descend left.
-      stack_of_right_children_.push_back(node->concat()->right);
-      node = node->concat()->left;
-    } else {
-      // Read left, descend right.
-      subnode = Concat(subnode, Ref(node->concat()->left));
-      n -= node->concat()->left->length;
-      bytes_remaining_ -= node->concat()->left->length;
-      node = node->concat()->right;
-    }
-  }
-
-  // Get the child node if we encounter a SUBSTRING.
-  size_t offset = 0;
-  size_t length = node->length;
-  if (node->tag == SUBSTRING) {
-    offset = node->substring()->start;
-    node = node->substring()->child;
-  }
-
-  // Range to read ends with a proper (possibly empty) subrange of the current
-  // chunk.
-  assert(node->tag == EXTERNAL || node->tag >= FLAT);
-  assert(length > n);
-  if (n > 0) subnode = Concat(subnode, NewSubstring(Ref(node), offset, n));
-  const char* data =
-      node->tag == EXTERNAL ? node->external()->base : node->data;
-  current_chunk_ = absl::string_view(data + offset + n, length - n);
-  current_leaf_ = node;
-  bytes_remaining_ -= n;
-  subcord.contents_.set_tree(VerifyTree(subnode));
-  return subcord;
-}
-
-void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) {
-  assert(bytes_remaining_ >= n && "Attempted to iterate past `end()`");
-  assert(n >= current_chunk_.size());  // This should only be called when
-                                       // iterating to a new node.
-
-  n -= current_chunk_.size();
-  bytes_remaining_ -= current_chunk_.size();
-
-  // Process the next node(s) on the stack, skipping whole subtrees depending on
-  // their length and how many bytes we are advancing.
-  CordRep* node = nullptr;
-  while (!stack_of_right_children_.empty()) {
-    node = stack_of_right_children_.back();
-    stack_of_right_children_.pop_back();
-    if (node->length > n) break;
-    n -= node->length;
-    bytes_remaining_ -= node->length;
-    node = nullptr;
-  }
-
-  if (node == nullptr) {
-    // We have reached the end of the Cord.
-    assert(bytes_remaining_ == 0);
-    return;
-  }
-
-  // Walk down the appropriate branches until we hit a non-CONCAT node. Save the
-  // right children to the stack for subsequent traversal.
-  while (node->tag == CONCAT) {
-    if (node->concat()->left->length > n) {
-      // Push right, descend left.
-      stack_of_right_children_.push_back(node->concat()->right);
-      node = node->concat()->left;
-    } else {
-      // Skip left, descend right.
-      n -= node->concat()->left->length;
-      bytes_remaining_ -= node->concat()->left->length;
-      node = node->concat()->right;
-    }
-  }
-
-  // Get the child node if we encounter a SUBSTRING.
-  size_t offset = 0;
-  size_t length = node->length;
-  if (node->tag == SUBSTRING) {
-    offset = node->substring()->start;
-    node = node->substring()->child;
-  }
-
-  assert(node->tag == EXTERNAL || node->tag >= FLAT);
-  assert(length > n);
-  const char* data =
-      node->tag == EXTERNAL ? node->external()->base : node->data;
-  current_chunk_ = absl::string_view(data + offset + n, length - n);
-  current_leaf_ = node;
-  bytes_remaining_ -= n;
-}
-
-char Cord::operator[](size_t i) const {
-  ABSL_HARDENING_ASSERT(i < size());
-  size_t offset = i;
-  const CordRep* rep = contents_.tree();
-  if (rep == nullptr) {
-    return contents_.data()[i];
-  }
-  while (true) {
-    assert(rep != nullptr);
-    assert(offset < rep->length);
-    if (rep->tag >= FLAT) {
-      // Get the "i"th character directly from the flat array.
-      return rep->data[offset];
-    } else if (rep->tag == EXTERNAL) {
-      // Get the "i"th character from the external array.
-      return rep->external()->base[offset];
-    } else if (rep->tag == CONCAT) {
-      // Recursively branch to the side of the concatenation that the "i"th
-      // character is on.
-      size_t left_length = rep->concat()->left->length;
-      if (offset < left_length) {
-        rep = rep->concat()->left;
-      } else {
-        offset -= left_length;
-        rep = rep->concat()->right;
-      }
-    } else {
-      // This must be a substring a node, so bypass it to get to the child.
-      assert(rep->tag == SUBSTRING);
-      offset += rep->substring()->start;
-      rep = rep->substring()->child;
-    }
-  }
-}
-
-absl::string_view Cord::FlattenSlowPath() {
-  size_t total_size = size();
-  CordRep* new_rep;
-  char* new_buffer;
-
-  // Try to put the contents into a new flat rep. If they won't fit in the
-  // biggest possible flat node, use an external rep instead.
-  if (total_size <= kMaxFlatLength) {
-    new_rep = NewFlat(total_size);
-    new_rep->length = total_size;
-    new_buffer = new_rep->data;
-    CopyToArraySlowPath(new_buffer);
-  } else {
-    new_buffer = std::allocator<char>().allocate(total_size);
-    CopyToArraySlowPath(new_buffer);
-    new_rep = absl::cord_internal::NewExternalRep(
-        absl::string_view(new_buffer, total_size), [](absl::string_view s) {
-          std::allocator<char>().deallocate(const_cast<char*>(s.data()),
-                                            s.size());
-        });
-  }
-  Unref(contents_.tree());
-  contents_.set_tree(new_rep);
-  return absl::string_view(new_buffer, total_size);
-}
-
-/* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) {
-  assert(rep != nullptr);
-  if (rep->tag >= FLAT) {
-    *fragment = absl::string_view(rep->data, rep->length);
-    return true;
-  } else if (rep->tag == EXTERNAL) {
-    *fragment = absl::string_view(rep->external()->base, rep->length);
-    return true;
-  } else if (rep->tag == SUBSTRING) {
-    CordRep* child = rep->substring()->child;
-    if (child->tag >= FLAT) {
-      *fragment =
-          absl::string_view(child->data + rep->substring()->start, rep->length);
-      return true;
-    } else if (child->tag == EXTERNAL) {
-      *fragment = absl::string_view(
-          child->external()->base + rep->substring()->start, rep->length);
-      return true;
-    }
-  }
-  return false;
-}
-
-/* static */ void Cord::ForEachChunkAux(
-    absl::cord_internal::CordRep* rep,
-    absl::FunctionRef<void(absl::string_view)> callback) {
-  assert(rep != nullptr);
-  int stack_pos = 0;
-  constexpr int stack_max = 128;
-  // Stack of right branches for tree traversal
-  absl::cord_internal::CordRep* stack[stack_max];
-  absl::cord_internal::CordRep* current_node = rep;
-  while (true) {
-    if (current_node->tag == CONCAT) {
-      if (stack_pos == stack_max) {
-        // There's no more room on our stack array to add another right branch,
-        // and the idea is to avoid allocations, so call this function
-        // recursively to navigate this subtree further.  (This is not something
-        // we expect to happen in practice).
-        ForEachChunkAux(current_node, callback);
-
-        // Pop the next right branch and iterate.
-        current_node = stack[--stack_pos];
-        continue;
-      } else {
-        // Save the right branch for later traversal and continue down the left
-        // branch.
-        stack[stack_pos++] = current_node->concat()->right;
-        current_node = current_node->concat()->left;
-        continue;
-      }
-    }
-    // This is a leaf node, so invoke our callback.
-    absl::string_view chunk;
-    bool success = GetFlatAux(current_node, &chunk);
-    assert(success);
-    if (success) {
-      callback(chunk);
-    }
-    if (stack_pos == 0) {
-      // end of traversal
-      return;
-    }
-    current_node = stack[--stack_pos];
-  }
-}
-
-static void DumpNode(CordRep* rep, bool include_data, std::ostream* os) {
-  const int kIndentStep = 1;
-  int indent = 0;
-  absl::InlinedVector<CordRep*, kInlinedVectorSize> stack;
-  absl::InlinedVector<int, kInlinedVectorSize> indents;
-  for (;;) {
-    *os << std::setw(3) << rep->refcount.Get();
-    *os << " " << std::setw(7) << rep->length;
-    *os << " [";
-    if (include_data) *os << static_cast<void*>(rep);
-    *os << "]";
-    *os << " " << (IsRootBalanced(rep) ? 'b' : 'u');
-    *os << " " << std::setw(indent) << "";
-    if (rep->tag == CONCAT) {
-      *os << "CONCAT depth=" << Depth(rep) << "\n";
-      indent += kIndentStep;
-      indents.push_back(indent);
-      stack.push_back(rep->concat()->right);
-      rep = rep->concat()->left;
-    } else if (rep->tag == SUBSTRING) {
-      *os << "SUBSTRING @ " << rep->substring()->start << "\n";
-      indent += kIndentStep;
-      rep = rep->substring()->child;
-    } else {  // Leaf
-      if (rep->tag == EXTERNAL) {
-        *os << "EXTERNAL [";
-        if (include_data)
-          *os << absl::CEscape(std::string(rep->external()->base, rep->length));
-        *os << "]\n";
-      } else {
-        *os << "FLAT cap=" << TagToLength(rep->tag) << " [";
-        if (include_data)
-          *os << absl::CEscape(std::string(rep->data, rep->length));
-        *os << "]\n";
-      }
-      if (stack.empty()) break;
-      rep = stack.back();
-      stack.pop_back();
-      indent = indents.back();
-      indents.pop_back();
-    }
-  }
-  ABSL_INTERNAL_CHECK(indents.empty(), "");
-}
-
-static std::string ReportError(CordRep* root, CordRep* node) {
-  std::ostringstream buf;
-  buf << "Error at node " << node << " in:";
-  DumpNode(root, true, &buf);
-  return buf.str();
-}
-
-static bool VerifyNode(CordRep* root, CordRep* start_node,
-                       bool full_validation) {
-  absl::InlinedVector<CordRep*, 2> worklist;
-  worklist.push_back(start_node);
-  do {
-    CordRep* node = worklist.back();
-    worklist.pop_back();
-
-    ABSL_INTERNAL_CHECK(node != nullptr, ReportError(root, node));
-    if (node != root) {
-      ABSL_INTERNAL_CHECK(node->length != 0, ReportError(root, node));
-    }
-
-    if (node->tag == CONCAT) {
-      ABSL_INTERNAL_CHECK(node->concat()->left != nullptr,
-                          ReportError(root, node));
-      ABSL_INTERNAL_CHECK(node->concat()->right != nullptr,
-                          ReportError(root, node));
-      ABSL_INTERNAL_CHECK((node->length == node->concat()->left->length +
-                                               node->concat()->right->length),
-                          ReportError(root, node));
-      if (full_validation) {
-        worklist.push_back(node->concat()->right);
-        worklist.push_back(node->concat()->left);
-      }
-    } else if (node->tag >= FLAT) {
-      ABSL_INTERNAL_CHECK(node->length <= TagToLength(node->tag),
-                          ReportError(root, node));
-    } else if (node->tag == EXTERNAL) {
-      ABSL_INTERNAL_CHECK(node->external()->base != nullptr,
-                          ReportError(root, node));
-    } else if (node->tag == SUBSTRING) {
-      ABSL_INTERNAL_CHECK(
-          node->substring()->start < node->substring()->child->length,
-          ReportError(root, node));
-      ABSL_INTERNAL_CHECK(node->substring()->start + node->length <=
-                              node->substring()->child->length,
-                          ReportError(root, node));
-    }
-  } while (!worklist.empty());
-  return true;
-}
-
-// Traverses the tree and computes the total memory allocated.
-/* static */ size_t Cord::MemoryUsageAux(const CordRep* rep) {
-  size_t total_mem_usage = 0;
-
-  // Allow a quick exit for the common case that the root is a leaf.
-  if (RepMemoryUsageLeaf(rep, &total_mem_usage)) {
-    return total_mem_usage;
-  }
-
-  // Iterate over the tree. cur_node is never a leaf node and leaf nodes will
-  // never be appended to tree_stack. This reduces overhead from manipulating
-  // tree_stack.
-  absl::InlinedVector<const CordRep*, kInlinedVectorSize> tree_stack;
-  const CordRep* cur_node = rep;
-  while (true) {
-    const CordRep* next_node = nullptr;
-
-    if (cur_node->tag == CONCAT) {
-      total_mem_usage += sizeof(CordRepConcat);
-      const CordRep* left = cur_node->concat()->left;
-      if (!RepMemoryUsageLeaf(left, &total_mem_usage)) {
-        next_node = left;
-      }
-
-      const CordRep* right = cur_node->concat()->right;
-      if (!RepMemoryUsageLeaf(right, &total_mem_usage)) {
-        if (next_node) {
-          tree_stack.push_back(next_node);
-        }
-        next_node = right;
-      }
-    } else {
-      // Since cur_node is not a leaf or a concat node it must be a substring.
-      assert(cur_node->tag == SUBSTRING);
-      total_mem_usage += sizeof(CordRepSubstring);
-      next_node = cur_node->substring()->child;
-      if (RepMemoryUsageLeaf(next_node, &total_mem_usage)) {
-        next_node = nullptr;
-      }
-    }
-
-    if (!next_node) {
-      if (tree_stack.empty()) {
-        return total_mem_usage;
-      }
-      next_node = tree_stack.back();
-      tree_stack.pop_back();
-    }
-    cur_node = next_node;
-  }
-}
-
-std::ostream& operator<<(std::ostream& out, const Cord& cord) {
-  for (absl::string_view chunk : cord.Chunks()) {
-    out.write(chunk.data(), chunk.size());
-  }
-  return out;
-}
-
-namespace strings_internal {
-size_t CordTestAccess::FlatOverhead() { return kFlatOverhead; }
-size_t CordTestAccess::MaxFlatLength() { return kMaxFlatLength; }
-size_t CordTestAccess::FlatTagToLength(uint8_t tag) {
-  return TagToLength(tag);
-}
-uint8_t CordTestAccess::LengthToTag(size_t s) {
-  ABSL_INTERNAL_CHECK(s <= kMaxFlatLength, absl::StrCat("Invalid length ", s));
-  return AllocatedSizeToTag(s + kFlatOverhead);
-}
-size_t CordTestAccess::SizeofCordRepConcat() { return sizeof(CordRepConcat); }
-size_t CordTestAccess::SizeofCordRepExternal() {
-  return sizeof(CordRepExternal);
-}
-size_t CordTestAccess::SizeofCordRepSubstring() {
-  return sizeof(CordRepSubstring);
-}
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/cord.h b/third_party/abseil_cpp/absl/strings/cord.h
deleted file mode 100644
index 5d5c897e66..0000000000
--- a/third_party/abseil_cpp/absl/strings/cord.h
+++ /dev/null
@@ -1,1299 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: cord.h
-// -----------------------------------------------------------------------------
-//
-// This file defines the `absl::Cord` data structure and operations on that data
-// structure. A Cord is a string-like sequence of characters optimized for
-// specific use cases. Unlike a `std::string`, which stores an array of
-// contiguous characters, Cord data is stored in a structure consisting of
-// separate, reference-counted "chunks." (Currently, this implementation is a
-// tree structure, though that implementation may change.)
-//
-// Because a Cord consists of these chunks, data can be added to or removed from
-// a Cord during its lifetime. Chunks may also be shared between Cords. Unlike a
-// `std::string`, a Cord can therefore accomodate data that changes over its
-// lifetime, though it's not quite "mutable"; it can change only in the
-// attachment, detachment, or rearrangement of chunks of its constituent data.
-//
-// A Cord provides some benefit over `std::string` under the following (albeit
-// narrow) circumstances:
-//
-//   * Cord data is designed to grow and shrink over a Cord's lifetime. Cord
-//     provides efficient insertions and deletions at the start and end of the
-//     character sequences, avoiding copies in those cases. Static data should
-//     generally be stored as strings.
-//   * External memory consisting of string-like data can be directly added to
-//     a Cord without requiring copies or allocations.
-//   * Cord data may be shared and copied cheaply. Cord provides a copy-on-write
-//     implementation and cheap sub-Cord operations. Copying a Cord is an O(1)
-//     operation.
-//
-// As a consequence to the above, Cord data is generally large. Small data
-// should generally use strings, as construction of a Cord requires some
-// overhead. Small Cords (<= 15 bytes) are represented inline, but most small
-// Cords are expected to grow over their lifetimes.
-//
-// Note that because a Cord is made up of separate chunked data, random access
-// to character data within a Cord is slower than within a `std::string`.
-//
-// Thread Safety
-//
-// Cord has the same thread-safety properties as many other types like
-// std::string, std::vector<>, int, etc -- it is thread-compatible. In
-// particular, if threads do not call non-const methods, then it is safe to call
-// const methods without synchronization. Copying a Cord produces a new instance
-// that can be used concurrently with the original in arbitrary ways.
-
-#ifndef ABSL_STRINGS_CORD_H_
-#define ABSL_STRINGS_CORD_H_
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <iosfwd>
-#include <iterator>
-#include <string>
-#include <type_traits>
-
-#include "absl/base/internal/endian.h"
-#include "absl/base/internal/per_thread_tls.h"
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/container/inlined_vector.h"
-#include "absl/functional/function_ref.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/internal/cord_internal.h"
-#include "absl/strings/internal/resize_uninitialized.h"
-#include "absl/strings/internal/string_constant.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/optional.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-class Cord;
-class CordTestPeer;
-template <typename Releaser>
-Cord MakeCordFromExternal(absl::string_view, Releaser&&);
-void CopyCordToString(const Cord& src, std::string* dst);
-
-// Cord
-//
-// A Cord is a sequence of characters, designed to be more efficient than a
-// `std::string` in certain circumstances: namely, large string data that needs
-// to change over its lifetime or shared, especially when such data is shared
-// across API boundaries.
-//
-// A Cord stores its character data in a structure that allows efficient prepend
-// and append operations. This makes a Cord useful for large string data sent
-// over in a wire format that may need to be prepended or appended at some point
-// during the data exchange (e.g. HTTP, protocol buffers). For example, a
-// Cord is useful for storing an HTTP request, and prepending an HTTP header to
-// such a request.
-//
-// Cords should not be used for storing general string data, however. They
-// require overhead to construct and are slower than strings for random access.
-//
-// The Cord API provides the following common API operations:
-//
-// * Create or assign Cords out of existing string data, memory, or other Cords
-// * Append and prepend data to an existing Cord
-// * Create new Sub-Cords from existing Cord data
-// * Swap Cord data and compare Cord equality
-// * Write out Cord data by constructing a `std::string`
-//
-// Additionally, the API provides iterator utilities to iterate through Cord
-// data via chunks or character bytes.
-//
-class Cord {
- private:
-  template <typename T>
-  using EnableIfString =
-      absl::enable_if_t<std::is_same<T, std::string>::value, int>;
-
- public:
-  // Cord::Cord() Constructors.
-
-  // Creates an empty Cord.
-  constexpr Cord() noexcept;
-
-  // Creates a Cord from an existing Cord. Cord is copyable and efficiently
-  // movable. The moved-from state is valid but unspecified.
-  Cord(const Cord& src);
-  Cord(Cord&& src) noexcept;
-  Cord& operator=(const Cord& x);
-  Cord& operator=(Cord&& x) noexcept;
-
-  // Creates a Cord from a `src` string. This constructor is marked explicit to
-  // prevent implicit Cord constructions from arguments convertible to an
-  // `absl::string_view`.
-  explicit Cord(absl::string_view src);
-  Cord& operator=(absl::string_view src);
-
-  // Creates a Cord from a `std::string&&` rvalue. These constructors are
-  // templated to avoid ambiguities for types that are convertible to both
-  // `absl::string_view` and `std::string`, such as `const char*`.
-  template <typename T, EnableIfString<T> = 0>
-  explicit Cord(T&& src);
-  template <typename T, EnableIfString<T> = 0>
-  Cord& operator=(T&& src);
-
-  // Cord::~Cord()
-  //
-  // Destructs the Cord.
-  ~Cord() {
-    if (contents_.is_tree()) DestroyCordSlow();
-  }
-
-  // MakeCordFromExternal()
-  //
-  // Creates a Cord that takes ownership of external string memory. The
-  // contents of `data` are not copied to the Cord; instead, the external
-  // memory is added to the Cord and reference-counted. This data may not be
-  // changed for the life of the Cord, though it may be prepended or appended
-  // to.
-  //
-  // `MakeCordFromExternal()` takes a callable "releaser" that is invoked when
-  // the reference count for `data` reaches zero. As noted above, this data must
-  // remain live until the releaser is invoked. The callable releaser also must:
-  //
-  //   * be move constructible
-  //   * support `void operator()(absl::string_view) const` or `void operator()`
-  //
-  // Example:
-  //
-  // Cord MakeCord(BlockPool* pool) {
-  //   Block* block = pool->NewBlock();
-  //   FillBlock(block);
-  //   return absl::MakeCordFromExternal(
-  //       block->ToStringView(),
-  //       [pool, block](absl::string_view v) {
-  //         pool->FreeBlock(block, v);
-  //       });
-  // }
-  //
-  // WARNING: Because a Cord can be reference-counted, it's likely a bug if your
-  // releaser doesn't do anything. For example, consider the following:
-  //
-  // void Foo(const char* buffer, int len) {
-  //   auto c = absl::MakeCordFromExternal(absl::string_view(buffer, len),
-  //                                       [](absl::string_view) {});
-  //
-  //   // BUG: If Bar() copies its cord for any reason, including keeping a
-  //   // substring of it, the lifetime of buffer might be extended beyond
-  //   // when Foo() returns.
-  //   Bar(c);
-  // }
-  template <typename Releaser>
-  friend Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser);
-
-  // Cord::Clear()
-  //
-  // Releases the Cord data. Any nodes that share data with other Cords, if
-  // applicable, will have their reference counts reduced by 1.
-  void Clear();
-
-  // Cord::Append()
-  //
-  // Appends data to the Cord, which may come from another Cord or other string
-  // data.
-  void Append(const Cord& src);
-  void Append(Cord&& src);
-  void Append(absl::string_view src);
-  template <typename T, EnableIfString<T> = 0>
-  void Append(T&& src);
-
-  // Cord::Prepend()
-  //
-  // Prepends data to the Cord, which may come from another Cord or other string
-  // data.
-  void Prepend(const Cord& src);
-  void Prepend(absl::string_view src);
-  template <typename T, EnableIfString<T> = 0>
-  void Prepend(T&& src);
-
-  // Cord::RemovePrefix()
-  //
-  // Removes the first `n` bytes of a Cord.
-  void RemovePrefix(size_t n);
-  void RemoveSuffix(size_t n);
-
-  // Cord::Subcord()
-  //
-  // Returns a new Cord representing the subrange [pos, pos + new_size) of
-  // *this. If pos >= size(), the result is empty(). If
-  // (pos + new_size) >= size(), the result is the subrange [pos, size()).
-  Cord Subcord(size_t pos, size_t new_size) const;
-
-  // Cord::swap()
-  //
-  // Swaps the contents of the Cord with `other`.
-  void swap(Cord& other) noexcept;
-
-  // swap()
-  //
-  // Swaps the contents of two Cords.
-  friend void swap(Cord& x, Cord& y) noexcept {
-    x.swap(y);
-  }
-
-  // Cord::size()
-  //
-  // Returns the size of the Cord.
-  size_t size() const;
-
-  // Cord::empty()
-  //
-  // Determines whether the given Cord is empty, returning `true` is so.
-  bool empty() const;
-
-  // Cord::EstimatedMemoryUsage()
-  //
-  // Returns the *approximate* number of bytes held in full or in part by this
-  // Cord (which may not remain the same between invocations).  Note that Cords
-  // that share memory could each be "charged" independently for the same shared
-  // memory.
-  size_t EstimatedMemoryUsage() const;
-
-  // Cord::Compare()
-  //
-  // Compares 'this' Cord with rhs. This function and its relatives treat Cords
-  // as sequences of unsigned bytes. The comparison is a straightforward
-  // lexicographic comparison. `Cord::Compare()` returns values as follows:
-  //
-  //   -1  'this' Cord is smaller
-  //    0  two Cords are equal
-  //    1  'this' Cord is larger
-  int Compare(absl::string_view rhs) const;
-  int Compare(const Cord& rhs) const;
-
-  // Cord::StartsWith()
-  //
-  // Determines whether the Cord starts with the passed string data `rhs`.
-  bool StartsWith(const Cord& rhs) const;
-  bool StartsWith(absl::string_view rhs) const;
-
-  // Cord::EndsWidth()
-  //
-  // Determines whether the Cord ends with the passed string data `rhs`.
-  bool EndsWith(absl::string_view rhs) const;
-  bool EndsWith(const Cord& rhs) const;
-
-  // Cord::operator std::string()
-  //
-  // Converts a Cord into a `std::string()`. This operator is marked explicit to
-  // prevent unintended Cord usage in functions that take a string.
-  explicit operator std::string() const;
-
-  // CopyCordToString()
-  //
-  // Copies the contents of a `src` Cord into a `*dst` string.
-  //
-  // This function optimizes the case of reusing the destination string since it
-  // can reuse previously allocated capacity. However, this function does not
-  // guarantee that pointers previously returned by `dst->data()` remain valid
-  // even if `*dst` had enough capacity to hold `src`. If `*dst` is a new
-  // object, prefer to simply use the conversion operator to `std::string`.
-  friend void CopyCordToString(const Cord& src, std::string* dst);
-
-  class CharIterator;
-
-  //----------------------------------------------------------------------------
-  // Cord::ChunkIterator
-  //----------------------------------------------------------------------------
-  //
-  // A `Cord::ChunkIterator` allows iteration over the constituent chunks of its
-  // Cord. Such iteration allows you to perform non-const operatons on the data
-  // of a Cord without modifying it.
-  //
-  // Generally, you do not instantiate a `Cord::ChunkIterator` directly;
-  // instead, you create one implicitly through use of the `Cord::Chunks()`
-  // member function.
-  //
-  // The `Cord::ChunkIterator` has the following properties:
-  //
-  //   * The iterator is invalidated after any non-const operation on the
-  //     Cord object over which it iterates.
-  //   * The `string_view` returned by dereferencing a valid, non-`end()`
-  //     iterator is guaranteed to be non-empty.
-  //   * Two `ChunkIterator` objects can be compared equal if and only if they
-  //     remain valid and iterate over the same Cord.
-  //   * The iterator in this case is a proxy iterator; the `string_view`
-  //     returned by the iterator does not live inside the Cord, and its
-  //     lifetime is limited to the lifetime of the iterator itself. To help
-  //     prevent lifetime issues, `ChunkIterator::reference` is not a true
-  //     reference type and is equivalent to `value_type`.
-  //   * The iterator keeps state that can grow for Cords that contain many
-  //     nodes and are imbalanced due to sharing. Prefer to pass this type by
-  //     const reference instead of by value.
-  class ChunkIterator {
-   public:
-    using iterator_category = std::input_iterator_tag;
-    using value_type = absl::string_view;
-    using difference_type = ptrdiff_t;
-    using pointer = const value_type*;
-    using reference = value_type;
-
-    ChunkIterator() = default;
-
-    ChunkIterator& operator++();
-    ChunkIterator operator++(int);
-    bool operator==(const ChunkIterator& other) const;
-    bool operator!=(const ChunkIterator& other) const;
-    reference operator*() const;
-    pointer operator->() const;
-
-    friend class Cord;
-    friend class CharIterator;
-
-   private:
-    // Constructs a `begin()` iterator from `cord`.
-    explicit ChunkIterator(const Cord* cord);
-
-    // Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than
-    // `current_chunk_.size()`.
-    void RemoveChunkPrefix(size_t n);
-    Cord AdvanceAndReadBytes(size_t n);
-    void AdvanceBytes(size_t n);
-    // Iterates `n` bytes, where `n` is expected to be greater than or equal to
-    // `current_chunk_.size()`.
-    void AdvanceBytesSlowPath(size_t n);
-
-    // A view into bytes of the current `CordRep`. It may only be a view to a
-    // suffix of bytes if this is being used by `CharIterator`.
-    absl::string_view current_chunk_;
-    // The current leaf, or `nullptr` if the iterator points to short data.
-    // If the current chunk is a substring node, current_leaf_ points to the
-    // underlying flat or external node.
-    absl::cord_internal::CordRep* current_leaf_ = nullptr;
-    // The number of bytes left in the `Cord` over which we are iterating.
-    size_t bytes_remaining_ = 0;
-    absl::InlinedVector<absl::cord_internal::CordRep*, 4>
-        stack_of_right_children_;
-  };
-
-  // Cord::ChunkIterator::chunk_begin()
-  //
-  // Returns an iterator to the first chunk of the `Cord`.
-  //
-  // Generally, prefer using `Cord::Chunks()` within a range-based for loop for
-  // iterating over the chunks of a Cord. This method may be useful for getting
-  // a `ChunkIterator` where range-based for-loops are not useful.
-  //
-  // Example:
-  //
-  //   absl::Cord::ChunkIterator FindAsChunk(const absl::Cord& c,
-  //                                         absl::string_view s) {
-  //     return std::find(c.chunk_begin(), c.chunk_end(), s);
-  //   }
-  ChunkIterator chunk_begin() const;
-
-  // Cord::ChunkItertator::chunk_end()
-  //
-  // Returns an iterator one increment past the last chunk of the `Cord`.
-  //
-  // Generally, prefer using `Cord::Chunks()` within a range-based for loop for
-  // iterating over the chunks of a Cord. This method may be useful for getting
-  // a `ChunkIterator` where range-based for-loops may not be available.
-  ChunkIterator chunk_end() const;
-
-  //----------------------------------------------------------------------------
-  // Cord::ChunkIterator::ChunkRange
-  //----------------------------------------------------------------------------
-  //
-  // `ChunkRange` is a helper class for iterating over the chunks of the `Cord`,
-  // producing an iterator which can be used within a range-based for loop.
-  // Construction of a `ChunkRange` will return an iterator pointing to the
-  // first chunk of the Cord. Generally, do not construct a `ChunkRange`
-  // directly; instead, prefer to use the `Cord::Chunks()` method.
-  //
-  // Implementation note: `ChunkRange` is simply a convenience wrapper over
-  // `Cord::chunk_begin()` and `Cord::chunk_end()`.
-  class ChunkRange {
-   public:
-    explicit ChunkRange(const Cord* cord) : cord_(cord) {}
-
-    ChunkIterator begin() const;
-    ChunkIterator end() const;
-
-   private:
-    const Cord* cord_;
-  };
-
-  // Cord::Chunks()
-  //
-  // Returns a `Cord::ChunkIterator::ChunkRange` for iterating over the chunks
-  // of a `Cord` with a range-based for-loop. For most iteration tasks on a
-  // Cord, use `Cord::Chunks()` to retrieve this iterator.
-  //
-  // Example:
-  //
-  //   void ProcessChunks(const Cord& cord) {
-  //     for (absl::string_view chunk : cord.Chunks()) { ... }
-  //   }
-  //
-  // Note that the ordinary caveats of temporary lifetime extension apply:
-  //
-  //   void Process() {
-  //     for (absl::string_view chunk : CordFactory().Chunks()) {
-  //       // The temporary Cord returned by CordFactory has been destroyed!
-  //     }
-  //   }
-  ChunkRange Chunks() const;
-
-  //----------------------------------------------------------------------------
-  // Cord::CharIterator
-  //----------------------------------------------------------------------------
-  //
-  // A `Cord::CharIterator` allows iteration over the constituent characters of
-  // a `Cord`.
-  //
-  // Generally, you do not instantiate a `Cord::CharIterator` directly; instead,
-  // you create one implicitly through use of the `Cord::Chars()` member
-  // function.
-  //
-  // A `Cord::CharIterator` has the following properties:
-  //
-  //   * The iterator is invalidated after any non-const operation on the
-  //     Cord object over which it iterates.
-  //   * Two `CharIterator` objects can be compared equal if and only if they
-  //     remain valid and iterate over the same Cord.
-  //   * The iterator keeps state that can grow for Cords that contain many
-  //     nodes and are imbalanced due to sharing. Prefer to pass this type by
-  //     const reference instead of by value.
-  //   * This type cannot act as a forward iterator because a `Cord` can reuse
-  //     sections of memory. This fact violates the requirement for forward
-  //     iterators to compare equal if dereferencing them returns the same
-  //     object.
-  class CharIterator {
-   public:
-    using iterator_category = std::input_iterator_tag;
-    using value_type = char;
-    using difference_type = ptrdiff_t;
-    using pointer = const char*;
-    using reference = const char&;
-
-    CharIterator() = default;
-
-    CharIterator& operator++();
-    CharIterator operator++(int);
-    bool operator==(const CharIterator& other) const;
-    bool operator!=(const CharIterator& other) const;
-    reference operator*() const;
-    pointer operator->() const;
-
-    friend Cord;
-
-   private:
-    explicit CharIterator(const Cord* cord) : chunk_iterator_(cord) {}
-
-    ChunkIterator chunk_iterator_;
-  };
-
-  // Cord::CharIterator::AdvanceAndRead()
-  //
-  // Advances the `Cord::CharIterator` by `n_bytes` and returns the bytes
-  // advanced as a separate `Cord`. `n_bytes` must be less than or equal to the
-  // number of bytes within the Cord; otherwise, behavior is undefined. It is
-  // valid to pass `char_end()` and `0`.
-  static Cord AdvanceAndRead(CharIterator* it, size_t n_bytes);
-
-  // Cord::CharIterator::Advance()
-  //
-  // Advances the `Cord::CharIterator` by `n_bytes`. `n_bytes` must be less than
-  // or equal to the number of bytes remaining within the Cord; otherwise,
-  // behavior is undefined. It is valid to pass `char_end()` and `0`.
-  static void Advance(CharIterator* it, size_t n_bytes);
-
-  // Cord::CharIterator::ChunkRemaining()
-  //
-  // Returns the longest contiguous view starting at the iterator's position.
-  //
-  // `it` must be dereferenceable.
-  static absl::string_view ChunkRemaining(const CharIterator& it);
-
-  // Cord::CharIterator::char_begin()
-  //
-  // Returns an iterator to the first character of the `Cord`.
-  //
-  // Generally, prefer using `Cord::Chars()` within a range-based for loop for
-  // iterating over the chunks of a Cord. This method may be useful for getting
-  // a `CharIterator` where range-based for-loops may not be available.
-  CharIterator char_begin() const;
-
-  // Cord::CharIterator::char_end()
-  //
-  // Returns an iterator to one past the last character of the `Cord`.
-  //
-  // Generally, prefer using `Cord::Chars()` within a range-based for loop for
-  // iterating over the chunks of a Cord. This method may be useful for getting
-  // a `CharIterator` where range-based for-loops are not useful.
-  CharIterator char_end() const;
-
-  // Cord::CharIterator::CharRange
-  //
-  // `CharRange` is a helper class for iterating over the characters of a
-  // producing an iterator which can be used within a range-based for loop.
-  // Construction of a `CharRange` will return an iterator pointing to the first
-  // character of the Cord. Generally, do not construct a `CharRange` directly;
-  // instead, prefer to use the `Cord::Chars()` method show below.
-  //
-  // Implementation note: `CharRange` is simply a convenience wrapper over
-  // `Cord::char_begin()` and `Cord::char_end()`.
-  class CharRange {
-   public:
-    explicit CharRange(const Cord* cord) : cord_(cord) {}
-
-    CharIterator begin() const;
-    CharIterator end() const;
-
-   private:
-    const Cord* cord_;
-  };
-
-  // Cord::CharIterator::Chars()
-  //
-  // Returns a `Cord::CharIterator` for iterating over the characters of a
-  // `Cord` with a range-based for-loop. For most character-based iteration
-  // tasks on a Cord, use `Cord::Chars()` to retrieve this iterator.
-  //
-  // Example:
-  //
-  //   void ProcessCord(const Cord& cord) {
-  //     for (char c : cord.Chars()) { ... }
-  //   }
-  //
-  // Note that the ordinary caveats of temporary lifetime extension apply:
-  //
-  //   void Process() {
-  //     for (char c : CordFactory().Chars()) {
-  //       // The temporary Cord returned by CordFactory has been destroyed!
-  //     }
-  //   }
-  CharRange Chars() const;
-
-  // Cord::operator[]
-  //
-  // Gets the "i"th character of the Cord and returns it, provided that
-  // 0 <= i < Cord.size().
-  //
-  // NOTE: This routine is reasonably efficient. It is roughly
-  // logarithmic based on the number of chunks that make up the cord. Still,
-  // if you need to iterate over the contents of a cord, you should
-  // use a CharIterator/ChunkIterator rather than call operator[] or Get()
-  // repeatedly in a loop.
-  char operator[](size_t i) const;
-
-  // Cord::TryFlat()
-  //
-  // If this cord's representation is a single flat array, returns a
-  // string_view referencing that array.  Otherwise returns nullopt.
-  absl::optional<absl::string_view> TryFlat() const;
-
-  // Cord::Flatten()
-  //
-  // Flattens the cord into a single array and returns a view of the data.
-  //
-  // If the cord was already flat, the contents are not modified.
-  absl::string_view Flatten();
-
-  // Supports absl::Cord as a sink object for absl::Format().
-  friend void AbslFormatFlush(absl::Cord* cord, absl::string_view part) {
-    cord->Append(part);
-  }
-
-  template <typename H>
-  friend H AbslHashValue(H hash_state, const absl::Cord& c) {
-    absl::optional<absl::string_view> maybe_flat = c.TryFlat();
-    if (maybe_flat.has_value()) {
-      return H::combine(std::move(hash_state), *maybe_flat);
-    }
-    return c.HashFragmented(std::move(hash_state));
-  }
-
-  // Create a Cord with the contents of StringConstant<T>::value.
-  // No allocations will be done and no data will be copied.
-  // This is an INTERNAL API and subject to change or removal. This API can only
-  // be used by spelling absl::strings_internal::MakeStringConstant, which is
-  // also an internal API.
-  template <typename T>
-  explicit constexpr Cord(strings_internal::StringConstant<T>);
-
- private:
-  friend class CordTestPeer;
-  friend bool operator==(const Cord& lhs, const Cord& rhs);
-  friend bool operator==(const Cord& lhs, absl::string_view rhs);
-
-  // Calls the provided function once for each cord chunk, in order.  Unlike
-  // Chunks(), this API will not allocate memory.
-  void ForEachChunk(absl::FunctionRef<void(absl::string_view)>) const;
-
-  // Allocates new contiguous storage for the contents of the cord. This is
-  // called by Flatten() when the cord was not already flat.
-  absl::string_view FlattenSlowPath();
-
-  // Actual cord contents are hidden inside the following simple
-  // class so that we can isolate the bulk of cord.cc from changes
-  // to the representation.
-  //
-  // InlineRep holds either a tree pointer, or an array of kMaxInline bytes.
-  class InlineRep {
-   public:
-    static constexpr unsigned char kMaxInline = cord_internal::kMaxInline;
-    static_assert(kMaxInline >= sizeof(absl::cord_internal::CordRep*), "");
-    static constexpr unsigned char kTreeFlag = cord_internal::kTreeFlag;
-    static constexpr unsigned char kProfiledFlag = cord_internal::kProfiledFlag;
-
-    constexpr InlineRep() : data_() {}
-    InlineRep(const InlineRep& src);
-    InlineRep(InlineRep&& src);
-    InlineRep& operator=(const InlineRep& src);
-    InlineRep& operator=(InlineRep&& src) noexcept;
-
-    explicit constexpr InlineRep(cord_internal::InlineData data);
-
-    void Swap(InlineRep* rhs);
-    bool empty() const;
-    size_t size() const;
-    const char* data() const;  // Returns nullptr if holding pointer
-    void set_data(const char* data, size_t n,
-                  bool nullify_tail);  // Discards pointer, if any
-    char* set_data(size_t n);  // Write data to the result
-    // Returns nullptr if holding bytes
-    absl::cord_internal::CordRep* tree() const;
-    // Discards old pointer, if any
-    void set_tree(absl::cord_internal::CordRep* rep);
-    // Replaces a tree with a new root. This is faster than set_tree, but it
-    // should only be used when it's clear that the old rep was a tree.
-    void replace_tree(absl::cord_internal::CordRep* rep);
-    // Returns non-null iff was holding a pointer
-    absl::cord_internal::CordRep* clear();
-    // Converts to pointer if necessary.
-    absl::cord_internal::CordRep* force_tree(size_t extra_hint);
-    void reduce_size(size_t n);  // REQUIRES: holding data
-    void remove_prefix(size_t n);  // REQUIRES: holding data
-    void AppendArray(const char* src_data, size_t src_size);
-    absl::string_view FindFlatStartPiece() const;
-    void AppendTree(absl::cord_internal::CordRep* tree);
-    void PrependTree(absl::cord_internal::CordRep* tree);
-    void GetAppendRegion(char** region, size_t* size, size_t max_length);
-    void GetAppendRegion(char** region, size_t* size);
-    bool IsSame(const InlineRep& other) const {
-      return memcmp(&data_, &other.data_, sizeof(data_)) == 0;
-    }
-    int BitwiseCompare(const InlineRep& other) const {
-      uint64_t x, y;
-      // Use memcpy to avoid aliasing issues.
-      memcpy(&x, &data_, sizeof(x));
-      memcpy(&y, &other.data_, sizeof(y));
-      if (x == y) {
-        memcpy(&x, reinterpret_cast<const char*>(&data_) + 8, sizeof(x));
-        memcpy(&y, reinterpret_cast<const char*>(&other.data_) + 8, sizeof(y));
-        if (x == y) return 0;
-      }
-      return absl::big_endian::FromHost64(x) < absl::big_endian::FromHost64(y)
-                 ? -1
-                 : 1;
-    }
-    void CopyTo(std::string* dst) const {
-      // memcpy is much faster when operating on a known size. On most supported
-      // platforms, the small string optimization is large enough that resizing
-      // to 15 bytes does not cause a memory allocation.
-      absl::strings_internal::STLStringResizeUninitialized(dst,
-                                                           sizeof(data_) - 1);
-      memcpy(&(*dst)[0], &data_, sizeof(data_) - 1);
-      // erase is faster than resize because the logic for memory allocation is
-      // not needed.
-      dst->erase(tagged_size());
-    }
-
-    // Copies the inline contents into `dst`. Assumes the cord is not empty.
-    void CopyToArray(char* dst) const;
-
-    bool is_tree() const { return tagged_size() > kMaxInline; }
-
-   private:
-    friend class Cord;
-
-    void AssignSlow(const InlineRep& src);
-    // Unrefs the tree, stops profiling, and zeroes the contents
-    void ClearSlow();
-
-    void ResetToEmpty() { data_ = {}; }
-
-    // This uses reinterpret_cast instead of the union to avoid accessing the
-    // inactive union element. The tagged size is not a common prefix.
-    void set_tagged_size(char new_tag) {
-      reinterpret_cast<char*>(&data_)[kMaxInline] = new_tag;
-    }
-    char tagged_size() const {
-      return reinterpret_cast<const char*>(&data_)[kMaxInline];
-    }
-
-    cord_internal::InlineData data_;
-  };
-  InlineRep contents_;
-
-  // Helper for MemoryUsage().
-  static size_t MemoryUsageAux(const absl::cord_internal::CordRep* rep);
-
-  // Helper for GetFlat() and TryFlat().
-  static bool GetFlatAux(absl::cord_internal::CordRep* rep,
-                         absl::string_view* fragment);
-
-  // Helper for ForEachChunk().
-  static void ForEachChunkAux(
-      absl::cord_internal::CordRep* rep,
-      absl::FunctionRef<void(absl::string_view)> callback);
-
-  // The destructor for non-empty Cords.
-  void DestroyCordSlow();
-
-  // Out-of-line implementation of slower parts of logic.
-  void CopyToArraySlowPath(char* dst) const;
-  int CompareSlowPath(absl::string_view rhs, size_t compared_size,
-                      size_t size_to_compare) const;
-  int CompareSlowPath(const Cord& rhs, size_t compared_size,
-                      size_t size_to_compare) const;
-  bool EqualsImpl(absl::string_view rhs, size_t size_to_compare) const;
-  bool EqualsImpl(const Cord& rhs, size_t size_to_compare) const;
-  int CompareImpl(const Cord& rhs) const;
-
-  template <typename ResultType, typename RHS>
-  friend ResultType GenericCompare(const Cord& lhs, const RHS& rhs,
-                                   size_t size_to_compare);
-  static absl::string_view GetFirstChunk(const Cord& c);
-  static absl::string_view GetFirstChunk(absl::string_view sv);
-
-  // Returns a new reference to contents_.tree(), or steals an existing
-  // reference if called on an rvalue.
-  absl::cord_internal::CordRep* TakeRep() const&;
-  absl::cord_internal::CordRep* TakeRep() &&;
-
-  // Helper for Append().
-  template <typename C>
-  void AppendImpl(C&& src);
-
-  // Helper for AbslHashValue().
-  template <typename H>
-  H HashFragmented(H hash_state) const {
-    typename H::AbslInternalPiecewiseCombiner combiner;
-    ForEachChunk([&combiner, &hash_state](absl::string_view chunk) {
-      hash_state = combiner.add_buffer(std::move(hash_state), chunk.data(),
-                                       chunk.size());
-    });
-    return H::combine(combiner.finalize(std::move(hash_state)), size());
-  }
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// allow a Cord to be logged
-extern std::ostream& operator<<(std::ostream& out, const Cord& cord);
-
-// ------------------------------------------------------------------
-// Internal details follow.  Clients should ignore.
-
-namespace cord_internal {
-
-// Fast implementation of memmove for up to 15 bytes. This implementation is
-// safe for overlapping regions. If nullify_tail is true, the destination is
-// padded with '\0' up to 16 bytes.
-inline void SmallMemmove(char* dst, const char* src, size_t n,
-                         bool nullify_tail = false) {
-  if (n >= 8) {
-    assert(n <= 16);
-    uint64_t buf1;
-    uint64_t buf2;
-    memcpy(&buf1, src, 8);
-    memcpy(&buf2, src + n - 8, 8);
-    if (nullify_tail) {
-      memset(dst + 8, 0, 8);
-    }
-    memcpy(dst, &buf1, 8);
-    memcpy(dst + n - 8, &buf2, 8);
-  } else if (n >= 4) {
-    uint32_t buf1;
-    uint32_t buf2;
-    memcpy(&buf1, src, 4);
-    memcpy(&buf2, src + n - 4, 4);
-    if (nullify_tail) {
-      memset(dst + 4, 0, 4);
-      memset(dst + 8, 0, 8);
-    }
-    memcpy(dst, &buf1, 4);
-    memcpy(dst + n - 4, &buf2, 4);
-  } else {
-    if (n != 0) {
-      dst[0] = src[0];
-      dst[n / 2] = src[n / 2];
-      dst[n - 1] = src[n - 1];
-    }
-    if (nullify_tail) {
-      memset(dst + 8, 0, 8);
-      memset(dst + n, 0, 8);
-    }
-  }
-}
-
-// Does non-template-specific `CordRepExternal` initialization.
-// Expects `data` to be non-empty.
-void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep);
-
-// Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer
-// to it, or `nullptr` if `data` was empty.
-template <typename Releaser>
-// NOLINTNEXTLINE - suppress clang-tidy raw pointer return.
-CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) {
-  using ReleaserType = absl::decay_t<Releaser>;
-  if (data.empty()) {
-    // Never create empty external nodes.
-    InvokeReleaser(Rank0{}, ReleaserType(std::forward<Releaser>(releaser)),
-                   data);
-    return nullptr;
-  }
-
-  CordRepExternal* rep = new CordRepExternalImpl<ReleaserType>(
-      std::forward<Releaser>(releaser), 0);
-  InitializeCordRepExternal(data, rep);
-  return rep;
-}
-
-// Overload for function reference types that dispatches using a function
-// pointer because there are no `alignof()` or `sizeof()` a function reference.
-// NOLINTNEXTLINE - suppress clang-tidy raw pointer return.
-inline CordRep* NewExternalRep(absl::string_view data,
-                               void (&releaser)(absl::string_view)) {
-  return NewExternalRep(data, &releaser);
-}
-
-}  // namespace cord_internal
-
-template <typename Releaser>
-Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) {
-  Cord cord;
-  cord.contents_.set_tree(::absl::cord_internal::NewExternalRep(
-      data, std::forward<Releaser>(releaser)));
-  return cord;
-}
-
-constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data)
-    : data_(data) {}
-
-inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) {
-  data_ = src.data_;
-}
-
-inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) {
-  data_ = src.data_;
-  src.ResetToEmpty();
-}
-
-inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) {
-  if (this == &src) {
-    return *this;
-  }
-  if (!is_tree() && !src.is_tree()) {
-    data_ = src.data_;
-    return *this;
-  }
-  AssignSlow(src);
-  return *this;
-}
-
-inline Cord::InlineRep& Cord::InlineRep::operator=(
-    Cord::InlineRep&& src) noexcept {
-  if (is_tree()) {
-    ClearSlow();
-  }
-  data_ = src.data_;
-  src.ResetToEmpty();
-  return *this;
-}
-
-inline void Cord::InlineRep::Swap(Cord::InlineRep* rhs) {
-  if (rhs == this) {
-    return;
-  }
-
-  std::swap(data_, rhs->data_);
-}
-
-inline const char* Cord::InlineRep::data() const {
-  return is_tree() ? nullptr : data_.as_chars;
-}
-
-inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const {
-  if (is_tree()) {
-    return data_.as_tree.rep;
-  } else {
-    return nullptr;
-  }
-}
-
-inline bool Cord::InlineRep::empty() const { return tagged_size() == 0; }
-
-inline size_t Cord::InlineRep::size() const {
-  const char tag = tagged_size();
-  if (tag <= kMaxInline) return tag;
-  return static_cast<size_t>(tree()->length);
-}
-
-inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) {
-  if (rep == nullptr) {
-    ResetToEmpty();
-  } else {
-    bool was_tree = is_tree();
-    data_.as_tree = {rep, {}, tagged_size()};
-    if (!was_tree) {
-      // If we were not a tree already, set the tag.
-      // Otherwise, leave it alone because it might have the profile bit on.
-      set_tagged_size(kTreeFlag);
-    }
-  }
-}
-
-inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) {
-  ABSL_ASSERT(is_tree());
-  if (ABSL_PREDICT_FALSE(rep == nullptr)) {
-    set_tree(rep);
-    return;
-  }
-  data_.as_tree = {rep, {}, tagged_size()};
-}
-
-inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
-  absl::cord_internal::CordRep* result = tree();
-  ResetToEmpty();
-  return result;
-}
-
-inline void Cord::InlineRep::CopyToArray(char* dst) const {
-  assert(!is_tree());
-  size_t n = tagged_size();
-  assert(n != 0);
-  cord_internal::SmallMemmove(dst, data_.as_chars, n);
-}
-
-constexpr inline Cord::Cord() noexcept {}
-
-template <typename T>
-constexpr Cord::Cord(strings_internal::StringConstant<T>)
-    : contents_(strings_internal::StringConstant<T>::value.size() <=
-                        cord_internal::kMaxInline
-                    ? cord_internal::InlineData(
-                          strings_internal::StringConstant<T>::value)
-                    : cord_internal::InlineData(cord_internal::AsTree{
-                          &cord_internal::ConstInitExternalStorage<
-                              strings_internal::StringConstant<T>>::value,
-                          {},
-                          cord_internal::kTreeFlag})) {}
-
-inline Cord& Cord::operator=(const Cord& x) {
-  contents_ = x.contents_;
-  return *this;
-}
-
-inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {}
-
-inline void Cord::swap(Cord& other) noexcept {
-  contents_.Swap(&other.contents_);
-}
-
-inline Cord& Cord::operator=(Cord&& x) noexcept {
-  contents_ = std::move(x.contents_);
-  return *this;
-}
-
-extern template Cord::Cord(std::string&& src);
-extern template Cord& Cord::operator=(std::string&& src);
-
-inline size_t Cord::size() const {
-  // Length is 1st field in str.rep_
-  return contents_.size();
-}
-
-inline bool Cord::empty() const { return contents_.empty(); }
-
-inline size_t Cord::EstimatedMemoryUsage() const {
-  size_t result = sizeof(Cord);
-  if (const absl::cord_internal::CordRep* rep = contents_.tree()) {
-    result += MemoryUsageAux(rep);
-  }
-  return result;
-}
-
-inline absl::optional<absl::string_view> Cord::TryFlat() const {
-  absl::cord_internal::CordRep* rep = contents_.tree();
-  if (rep == nullptr) {
-    return absl::string_view(contents_.data(), contents_.size());
-  }
-  absl::string_view fragment;
-  if (GetFlatAux(rep, &fragment)) {
-    return fragment;
-  }
-  return absl::nullopt;
-}
-
-inline absl::string_view Cord::Flatten() {
-  absl::cord_internal::CordRep* rep = contents_.tree();
-  if (rep == nullptr) {
-    return absl::string_view(contents_.data(), contents_.size());
-  } else {
-    absl::string_view already_flat_contents;
-    if (GetFlatAux(rep, &already_flat_contents)) {
-      return already_flat_contents;
-    }
-  }
-  return FlattenSlowPath();
-}
-
-inline void Cord::Append(absl::string_view src) {
-  contents_.AppendArray(src.data(), src.size());
-}
-
-extern template void Cord::Append(std::string&& src);
-extern template void Cord::Prepend(std::string&& src);
-
-inline int Cord::Compare(const Cord& rhs) const {
-  if (!contents_.is_tree() && !rhs.contents_.is_tree()) {
-    return contents_.BitwiseCompare(rhs.contents_);
-  }
-
-  return CompareImpl(rhs);
-}
-
-// Does 'this' cord start/end with rhs
-inline bool Cord::StartsWith(const Cord& rhs) const {
-  if (contents_.IsSame(rhs.contents_)) return true;
-  size_t rhs_size = rhs.size();
-  if (size() < rhs_size) return false;
-  return EqualsImpl(rhs, rhs_size);
-}
-
-inline bool Cord::StartsWith(absl::string_view rhs) const {
-  size_t rhs_size = rhs.size();
-  if (size() < rhs_size) return false;
-  return EqualsImpl(rhs, rhs_size);
-}
-
-inline Cord::ChunkIterator::ChunkIterator(const Cord* cord)
-    : bytes_remaining_(cord->size()) {
-  if (cord->empty()) return;
-  if (cord->contents_.is_tree()) {
-    stack_of_right_children_.push_back(cord->contents_.tree());
-    operator++();
-  } else {
-    current_chunk_ = absl::string_view(cord->contents_.data(), cord->size());
-  }
-}
-
-inline Cord::ChunkIterator Cord::ChunkIterator::operator++(int) {
-  ChunkIterator tmp(*this);
-  operator++();
-  return tmp;
-}
-
-inline bool Cord::ChunkIterator::operator==(const ChunkIterator& other) const {
-  return bytes_remaining_ == other.bytes_remaining_;
-}
-
-inline bool Cord::ChunkIterator::operator!=(const ChunkIterator& other) const {
-  return !(*this == other);
-}
-
-inline Cord::ChunkIterator::reference Cord::ChunkIterator::operator*() const {
-  ABSL_HARDENING_ASSERT(bytes_remaining_ != 0);
-  return current_chunk_;
-}
-
-inline Cord::ChunkIterator::pointer Cord::ChunkIterator::operator->() const {
-  ABSL_HARDENING_ASSERT(bytes_remaining_ != 0);
-  return &current_chunk_;
-}
-
-inline void Cord::ChunkIterator::RemoveChunkPrefix(size_t n) {
-  assert(n < current_chunk_.size());
-  current_chunk_.remove_prefix(n);
-  bytes_remaining_ -= n;
-}
-
-inline void Cord::ChunkIterator::AdvanceBytes(size_t n) {
-  if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) {
-    RemoveChunkPrefix(n);
-  } else if (n != 0) {
-    AdvanceBytesSlowPath(n);
-  }
-}
-
-inline Cord::ChunkIterator Cord::chunk_begin() const {
-  return ChunkIterator(this);
-}
-
-inline Cord::ChunkIterator Cord::chunk_end() const { return ChunkIterator(); }
-
-inline Cord::ChunkIterator Cord::ChunkRange::begin() const {
-  return cord_->chunk_begin();
-}
-
-inline Cord::ChunkIterator Cord::ChunkRange::end() const {
-  return cord_->chunk_end();
-}
-
-inline Cord::ChunkRange Cord::Chunks() const { return ChunkRange(this); }
-
-inline Cord::CharIterator& Cord::CharIterator::operator++() {
-  if (ABSL_PREDICT_TRUE(chunk_iterator_->size() > 1)) {
-    chunk_iterator_.RemoveChunkPrefix(1);
-  } else {
-    ++chunk_iterator_;
-  }
-  return *this;
-}
-
-inline Cord::CharIterator Cord::CharIterator::operator++(int) {
-  CharIterator tmp(*this);
-  operator++();
-  return tmp;
-}
-
-inline bool Cord::CharIterator::operator==(const CharIterator& other) const {
-  return chunk_iterator_ == other.chunk_iterator_;
-}
-
-inline bool Cord::CharIterator::operator!=(const CharIterator& other) const {
-  return !(*this == other);
-}
-
-inline Cord::CharIterator::reference Cord::CharIterator::operator*() const {
-  return *chunk_iterator_->data();
-}
-
-inline Cord::CharIterator::pointer Cord::CharIterator::operator->() const {
-  return chunk_iterator_->data();
-}
-
-inline Cord Cord::AdvanceAndRead(CharIterator* it, size_t n_bytes) {
-  assert(it != nullptr);
-  return it->chunk_iterator_.AdvanceAndReadBytes(n_bytes);
-}
-
-inline void Cord::Advance(CharIterator* it, size_t n_bytes) {
-  assert(it != nullptr);
-  it->chunk_iterator_.AdvanceBytes(n_bytes);
-}
-
-inline absl::string_view Cord::ChunkRemaining(const CharIterator& it) {
-  return *it.chunk_iterator_;
-}
-
-inline Cord::CharIterator Cord::char_begin() const {
-  return CharIterator(this);
-}
-
-inline Cord::CharIterator Cord::char_end() const { return CharIterator(); }
-
-inline Cord::CharIterator Cord::CharRange::begin() const {
-  return cord_->char_begin();
-}
-
-inline Cord::CharIterator Cord::CharRange::end() const {
-  return cord_->char_end();
-}
-
-inline Cord::CharRange Cord::Chars() const { return CharRange(this); }
-
-inline void Cord::ForEachChunk(
-    absl::FunctionRef<void(absl::string_view)> callback) const {
-  absl::cord_internal::CordRep* rep = contents_.tree();
-  if (rep == nullptr) {
-    callback(absl::string_view(contents_.data(), contents_.size()));
-  } else {
-    return ForEachChunkAux(rep, callback);
-  }
-}
-
-// Nonmember Cord-to-Cord relational operarators.
-inline bool operator==(const Cord& lhs, const Cord& rhs) {
-  if (lhs.contents_.IsSame(rhs.contents_)) return true;
-  size_t rhs_size = rhs.size();
-  if (lhs.size() != rhs_size) return false;
-  return lhs.EqualsImpl(rhs, rhs_size);
-}
-
-inline bool operator!=(const Cord& x, const Cord& y) { return !(x == y); }
-inline bool operator<(const Cord& x, const Cord& y) {
-  return x.Compare(y) < 0;
-}
-inline bool operator>(const Cord& x, const Cord& y) {
-  return x.Compare(y) > 0;
-}
-inline bool operator<=(const Cord& x, const Cord& y) {
-  return x.Compare(y) <= 0;
-}
-inline bool operator>=(const Cord& x, const Cord& y) {
-  return x.Compare(y) >= 0;
-}
-
-// Nonmember Cord-to-absl::string_view relational operators.
-//
-// Due to implicit conversions, these also enable comparisons of Cord with
-// with std::string, ::string, and const char*.
-inline bool operator==(const Cord& lhs, absl::string_view rhs) {
-  size_t lhs_size = lhs.size();
-  size_t rhs_size = rhs.size();
-  if (lhs_size != rhs_size) return false;
-  return lhs.EqualsImpl(rhs, rhs_size);
-}
-
-inline bool operator==(absl::string_view x, const Cord& y) { return y == x; }
-inline bool operator!=(const Cord& x, absl::string_view y) { return !(x == y); }
-inline bool operator!=(absl::string_view x, const Cord& y) { return !(x == y); }
-inline bool operator<(const Cord& x, absl::string_view y) {
-  return x.Compare(y) < 0;
-}
-inline bool operator<(absl::string_view x, const Cord& y) {
-  return y.Compare(x) > 0;
-}
-inline bool operator>(const Cord& x, absl::string_view y) { return y < x; }
-inline bool operator>(absl::string_view x, const Cord& y) { return y < x; }
-inline bool operator<=(const Cord& x, absl::string_view y) { return !(y < x); }
-inline bool operator<=(absl::string_view x, const Cord& y) { return !(y < x); }
-inline bool operator>=(const Cord& x, absl::string_view y) { return !(x < y); }
-inline bool operator>=(absl::string_view x, const Cord& y) { return !(x < y); }
-
-// Some internals exposed to test code.
-namespace strings_internal {
-class CordTestAccess {
- public:
-  static size_t FlatOverhead();
-  static size_t MaxFlatLength();
-  static size_t SizeofCordRepConcat();
-  static size_t SizeofCordRepExternal();
-  static size_t SizeofCordRepSubstring();
-  static size_t FlatTagToLength(uint8_t tag);
-  static uint8_t LengthToTag(size_t s);
-};
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_CORD_H_
diff --git a/third_party/abseil_cpp/absl/strings/cord_test.cc b/third_party/abseil_cpp/absl/strings/cord_test.cc
deleted file mode 100644
index 7942bfc03c..0000000000
--- a/third_party/abseil_cpp/absl/strings/cord_test.cc
+++ /dev/null
@@ -1,1711 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/cord.h"
-
-#include <algorithm>
-#include <climits>
-#include <cstdio>
-#include <iterator>
-#include <map>
-#include <numeric>
-#include <random>
-#include <sstream>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/casts.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/endian.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/container/fixed_array.h"
-#include "absl/strings/cord_test_helpers.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/string_view.h"
-
-typedef std::mt19937_64 RandomEngine;
-
-static std::string RandomLowercaseString(RandomEngine* rng);
-static std::string RandomLowercaseString(RandomEngine* rng, size_t length);
-
-static int GetUniformRandomUpTo(RandomEngine* rng, int upper_bound) {
-  if (upper_bound > 0) {
-    std::uniform_int_distribution<int> uniform(0, upper_bound - 1);
-    return uniform(*rng);
-  } else {
-    return 0;
-  }
-}
-
-static size_t GetUniformRandomUpTo(RandomEngine* rng, size_t upper_bound) {
-  if (upper_bound > 0) {
-    std::uniform_int_distribution<size_t> uniform(0, upper_bound - 1);
-    return uniform(*rng);
-  } else {
-    return 0;
-  }
-}
-
-static int32_t GenerateSkewedRandom(RandomEngine* rng, int max_log) {
-  const uint32_t base = (*rng)() % (max_log + 1);
-  const uint32_t mask = ((base < 32) ? (1u << base) : 0u) - 1u;
-  return (*rng)() & mask;
-}
-
-static std::string RandomLowercaseString(RandomEngine* rng) {
-  int length;
-  std::bernoulli_distribution one_in_1k(0.001);
-  std::bernoulli_distribution one_in_10k(0.0001);
-  // With low probability, make a large fragment
-  if (one_in_10k(*rng)) {
-    length = GetUniformRandomUpTo(rng, 1048576);
-  } else if (one_in_1k(*rng)) {
-    length = GetUniformRandomUpTo(rng, 10000);
-  } else {
-    length = GenerateSkewedRandom(rng, 10);
-  }
-  return RandomLowercaseString(rng, length);
-}
-
-static std::string RandomLowercaseString(RandomEngine* rng, size_t length) {
-  std::string result(length, '\0');
-  std::uniform_int_distribution<int> chars('a', 'z');
-  std::generate(result.begin(), result.end(),
-                [&]() { return static_cast<char>(chars(*rng)); });
-  return result;
-}
-
-static void DoNothing(absl::string_view /* data */, void* /* arg */) {}
-
-static void DeleteExternalString(absl::string_view data, void* arg) {
-  std::string* s = reinterpret_cast<std::string*>(arg);
-  EXPECT_EQ(data, *s);
-  delete s;
-}
-
-// Add "s" to *dst via `MakeCordFromExternal`
-static void AddExternalMemory(absl::string_view s, absl::Cord* dst) {
-  std::string* str = new std::string(s.data(), s.size());
-  dst->Append(absl::MakeCordFromExternal(*str, [str](absl::string_view data) {
-    DeleteExternalString(data, str);
-  }));
-}
-
-static void DumpGrowth() {
-  absl::Cord str;
-  for (int i = 0; i < 1000; i++) {
-    char c = 'a' + i % 26;
-    str.Append(absl::string_view(&c, 1));
-  }
-}
-
-// Make a Cord with some number of fragments.  Return the size (in bytes)
-// of the smallest fragment.
-static size_t AppendWithFragments(const std::string& s, RandomEngine* rng,
-                                  absl::Cord* cord) {
-  size_t j = 0;
-  const size_t max_size = s.size() / 5;  // Make approx. 10 fragments
-  size_t min_size = max_size;            // size of smallest fragment
-  while (j < s.size()) {
-    size_t N = 1 + GetUniformRandomUpTo(rng, max_size);
-    if (N > (s.size() - j)) {
-      N = s.size() - j;
-    }
-    if (N < min_size) {
-      min_size = N;
-    }
-
-    std::bernoulli_distribution coin_flip(0.5);
-    if (coin_flip(*rng)) {
-      // Grow by adding an external-memory.
-      AddExternalMemory(absl::string_view(s.data() + j, N), cord);
-    } else {
-      cord->Append(absl::string_view(s.data() + j, N));
-    }
-    j += N;
-  }
-  return min_size;
-}
-
-// Add an external memory that contains the specified std::string to cord
-static void AddNewStringBlock(const std::string& str, absl::Cord* dst) {
-  char* data = new char[str.size()];
-  memcpy(data, str.data(), str.size());
-  dst->Append(absl::MakeCordFromExternal(
-      absl::string_view(data, str.size()),
-      [](absl::string_view s) { delete[] s.data(); }));
-}
-
-// Make a Cord out of many different types of nodes.
-static absl::Cord MakeComposite() {
-  absl::Cord cord;
-  cord.Append("the");
-  AddExternalMemory(" quick brown", &cord);
-  AddExternalMemory(" fox jumped", &cord);
-
-  absl::Cord full(" over");
-  AddExternalMemory(" the lazy", &full);
-  AddNewStringBlock(" dog slept the whole day away", &full);
-  absl::Cord substring = full.Subcord(0, 18);
-
-  // Make substring long enough to defeat the copying fast path in Append.
-  substring.Append(std::string(1000, '.'));
-  cord.Append(substring);
-  cord = cord.Subcord(0, cord.size() - 998);  // Remove most of extra junk
-
-  return cord;
-}
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class CordTestPeer {
- public:
-  static void ForEachChunk(
-      const Cord& c, absl::FunctionRef<void(absl::string_view)> callback) {
-    c.ForEachChunk(callback);
-  }
-
-  static bool IsTree(const Cord& c) { return c.contents_.is_tree(); }
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-TEST(Cord, AllFlatSizes) {
-  using absl::strings_internal::CordTestAccess;
-
-  for (size_t s = 0; s < CordTestAccess::MaxFlatLength(); s++) {
-    // Make a string of length s.
-    std::string src;
-    while (src.size() < s) {
-      src.push_back('a' + (src.size() % 26));
-    }
-
-    absl::Cord dst(src);
-    EXPECT_EQ(std::string(dst), src) << s;
-  }
-}
-
-// We create a Cord at least 128GB in size using the fact that Cords can
-// internally reference-count; thus the Cord is enormous without actually
-// consuming very much memory.
-TEST(GigabyteCord, FromExternal) {
-  const size_t one_gig = 1024U * 1024U * 1024U;
-  size_t max_size = 2 * one_gig;
-  if (sizeof(max_size) > 4) max_size = 128 * one_gig;
-
-  size_t length = 128 * 1024;
-  char* data = new char[length];
-  absl::Cord from = absl::MakeCordFromExternal(
-      absl::string_view(data, length),
-      [](absl::string_view sv) { delete[] sv.data(); });
-
-  // This loop may seem odd due to its combination of exponential doubling of
-  // size and incremental size increases.  We do it incrementally to be sure the
-  // Cord will need rebalancing and will exercise code that, in the past, has
-  // caused crashes in production.  We grow exponentially so that the code will
-  // execute in a reasonable amount of time.
-  absl::Cord c;
-  ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size());
-  c.Append(from);
-  while (c.size() < max_size) {
-    c.Append(c);
-    c.Append(from);
-    c.Append(from);
-    c.Append(from);
-    c.Append(from);
-  }
-
-  for (int i = 0; i < 1024; ++i) {
-    c.Append(from);
-  }
-  ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size());
-  // Note: on a 32-bit build, this comes out to   2,818,048,000 bytes.
-  // Note: on a 64-bit build, this comes out to 171,932,385,280 bytes.
-}
-
-static absl::Cord MakeExternalCord(int size) {
-  char* buffer = new char[size];
-  memset(buffer, 'x', size);
-  absl::Cord cord;
-  cord.Append(absl::MakeCordFromExternal(
-      absl::string_view(buffer, size),
-      [](absl::string_view s) { delete[] s.data(); }));
-  return cord;
-}
-
-// Extern to fool clang that this is not constant. Needed to suppress
-// a warning of unsafe code we want to test.
-extern bool my_unique_true_boolean;
-bool my_unique_true_boolean = true;
-
-TEST(Cord, Assignment) {
-  absl::Cord x(absl::string_view("hi there"));
-  absl::Cord y(x);
-  ASSERT_EQ(std::string(x), "hi there");
-  ASSERT_EQ(std::string(y), "hi there");
-  ASSERT_TRUE(x == y);
-  ASSERT_TRUE(x <= y);
-  ASSERT_TRUE(y <= x);
-
-  x = absl::string_view("foo");
-  ASSERT_EQ(std::string(x), "foo");
-  ASSERT_EQ(std::string(y), "hi there");
-  ASSERT_TRUE(x < y);
-  ASSERT_TRUE(y > x);
-  ASSERT_TRUE(x != y);
-  ASSERT_TRUE(x <= y);
-  ASSERT_TRUE(y >= x);
-
-  x = "foo";
-  ASSERT_EQ(x, "foo");
-
-  // Test that going from inline rep to tree we don't leak memory.
-  std::vector<std::pair<absl::string_view, absl::string_view>>
-      test_string_pairs = {{"hi there", "foo"},
-                           {"loooooong coooooord", "short cord"},
-                           {"short cord", "loooooong coooooord"},
-                           {"loooooong coooooord1", "loooooong coooooord2"}};
-  for (std::pair<absl::string_view, absl::string_view> test_strings :
-       test_string_pairs) {
-    absl::Cord tmp(test_strings.first);
-    absl::Cord z(std::move(tmp));
-    ASSERT_EQ(std::string(z), test_strings.first);
-    tmp = test_strings.second;
-    z = std::move(tmp);
-    ASSERT_EQ(std::string(z), test_strings.second);
-  }
-  {
-    // Test that self-move assignment doesn't crash/leak.
-    // Do not write such code!
-    absl::Cord my_small_cord("foo");
-    absl::Cord my_big_cord("loooooong coooooord");
-    // Bypass clang's warning on self move-assignment.
-    absl::Cord* my_small_alias =
-        my_unique_true_boolean ? &my_small_cord : &my_big_cord;
-    absl::Cord* my_big_alias =
-        !my_unique_true_boolean ? &my_small_cord : &my_big_cord;
-
-    *my_small_alias = std::move(my_small_cord);
-    *my_big_alias = std::move(my_big_cord);
-    // my_small_cord and my_big_cord are in an unspecified but valid
-    // state, and will be correctly destroyed here.
-  }
-}
-
-TEST(Cord, StartsEndsWith) {
-  absl::Cord x(absl::string_view("abcde"));
-  absl::Cord empty("");
-
-  ASSERT_TRUE(x.StartsWith(absl::Cord("abcde")));
-  ASSERT_TRUE(x.StartsWith(absl::Cord("abc")));
-  ASSERT_TRUE(x.StartsWith(absl::Cord("")));
-  ASSERT_TRUE(empty.StartsWith(absl::Cord("")));
-  ASSERT_TRUE(x.EndsWith(absl::Cord("abcde")));
-  ASSERT_TRUE(x.EndsWith(absl::Cord("cde")));
-  ASSERT_TRUE(x.EndsWith(absl::Cord("")));
-  ASSERT_TRUE(empty.EndsWith(absl::Cord("")));
-
-  ASSERT_TRUE(!x.StartsWith(absl::Cord("xyz")));
-  ASSERT_TRUE(!empty.StartsWith(absl::Cord("xyz")));
-  ASSERT_TRUE(!x.EndsWith(absl::Cord("xyz")));
-  ASSERT_TRUE(!empty.EndsWith(absl::Cord("xyz")));
-
-  ASSERT_TRUE(x.StartsWith("abcde"));
-  ASSERT_TRUE(x.StartsWith("abc"));
-  ASSERT_TRUE(x.StartsWith(""));
-  ASSERT_TRUE(empty.StartsWith(""));
-  ASSERT_TRUE(x.EndsWith("abcde"));
-  ASSERT_TRUE(x.EndsWith("cde"));
-  ASSERT_TRUE(x.EndsWith(""));
-  ASSERT_TRUE(empty.EndsWith(""));
-
-  ASSERT_TRUE(!x.StartsWith("xyz"));
-  ASSERT_TRUE(!empty.StartsWith("xyz"));
-  ASSERT_TRUE(!x.EndsWith("xyz"));
-  ASSERT_TRUE(!empty.EndsWith("xyz"));
-}
-
-TEST(Cord, Subcord) {
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
-  const std::string s = RandomLowercaseString(&rng, 1024);
-
-  absl::Cord a;
-  AppendWithFragments(s, &rng, &a);
-  ASSERT_EQ(s.size(), a.size());
-
-  // Check subcords of a, from a variety of interesting points.
-  std::set<size_t> positions;
-  for (int i = 0; i <= 32; ++i) {
-    positions.insert(i);
-    positions.insert(i * 32 - 1);
-    positions.insert(i * 32);
-    positions.insert(i * 32 + 1);
-    positions.insert(a.size() - i);
-  }
-  positions.insert(237);
-  positions.insert(732);
-  for (size_t pos : positions) {
-    if (pos > a.size()) continue;
-    for (size_t end_pos : positions) {
-      if (end_pos < pos || end_pos > a.size()) continue;
-      absl::Cord sa = a.Subcord(pos, end_pos - pos);
-      EXPECT_EQ(absl::string_view(s).substr(pos, end_pos - pos),
-                std::string(sa))
-          << a;
-    }
-  }
-
-  // Do the same thing for an inline cord.
-  const std::string sh = "short";
-  absl::Cord c(sh);
-  for (size_t pos = 0; pos <= sh.size(); ++pos) {
-    for (size_t n = 0; n <= sh.size() - pos; ++n) {
-      absl::Cord sc = c.Subcord(pos, n);
-      EXPECT_EQ(sh.substr(pos, n), std::string(sc)) << c;
-    }
-  }
-
-  // Check subcords of subcords.
-  absl::Cord sa = a.Subcord(0, a.size());
-  std::string ss = s.substr(0, s.size());
-  while (sa.size() > 1) {
-    sa = sa.Subcord(1, sa.size() - 2);
-    ss = ss.substr(1, ss.size() - 2);
-    EXPECT_EQ(ss, std::string(sa)) << a;
-    if (HasFailure()) break;  // halt cascade
-  }
-
-  // It is OK to ask for too much.
-  sa = a.Subcord(0, a.size() + 1);
-  EXPECT_EQ(s, std::string(sa));
-
-  // It is OK to ask for something beyond the end.
-  sa = a.Subcord(a.size() + 1, 0);
-  EXPECT_TRUE(sa.empty());
-  sa = a.Subcord(a.size() + 1, 1);
-  EXPECT_TRUE(sa.empty());
-}
-
-TEST(Cord, Swap) {
-  absl::string_view a("Dexter");
-  absl::string_view b("Mandark");
-  absl::Cord x(a);
-  absl::Cord y(b);
-  swap(x, y);
-  ASSERT_EQ(x, absl::Cord(b));
-  ASSERT_EQ(y, absl::Cord(a));
-  x.swap(y);
-  ASSERT_EQ(x, absl::Cord(a));
-  ASSERT_EQ(y, absl::Cord(b));
-}
-
-static void VerifyCopyToString(const absl::Cord& cord) {
-  std::string initially_empty;
-  absl::CopyCordToString(cord, &initially_empty);
-  EXPECT_EQ(initially_empty, cord);
-
-  constexpr size_t kInitialLength = 1024;
-  std::string has_initial_contents(kInitialLength, 'x');
-  const char* address_before_copy = has_initial_contents.data();
-  absl::CopyCordToString(cord, &has_initial_contents);
-  EXPECT_EQ(has_initial_contents, cord);
-
-  if (cord.size() <= kInitialLength) {
-    EXPECT_EQ(has_initial_contents.data(), address_before_copy)
-        << "CopyCordToString allocated new string storage; "
-           "has_initial_contents = \""
-        << has_initial_contents << "\"";
-  }
-}
-
-TEST(Cord, CopyToString) {
-  VerifyCopyToString(absl::Cord());
-  VerifyCopyToString(absl::Cord("small cord"));
-  VerifyCopyToString(
-      absl::MakeFragmentedCord({"fragmented ", "cord ", "to ", "test ",
-                                "copying ", "to ", "a ", "string."}));
-}
-
-TEST(TryFlat, Empty) {
-  absl::Cord c;
-  EXPECT_EQ(c.TryFlat(), "");
-}
-
-TEST(TryFlat, Flat) {
-  absl::Cord c("hello");
-  EXPECT_EQ(c.TryFlat(), "hello");
-}
-
-TEST(TryFlat, SubstrInlined) {
-  absl::Cord c("hello");
-  c.RemovePrefix(1);
-  EXPECT_EQ(c.TryFlat(), "ello");
-}
-
-TEST(TryFlat, SubstrFlat) {
-  absl::Cord c("longer than 15 bytes");
-  c.RemovePrefix(1);
-  EXPECT_EQ(c.TryFlat(), "onger than 15 bytes");
-}
-
-TEST(TryFlat, Concat) {
-  absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"});
-  EXPECT_EQ(c.TryFlat(), absl::nullopt);
-}
-
-TEST(TryFlat, External) {
-  absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
-  EXPECT_EQ(c.TryFlat(), "hell");
-}
-
-TEST(TryFlat, SubstrExternal) {
-  absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
-  c.RemovePrefix(1);
-  EXPECT_EQ(c.TryFlat(), "ell");
-}
-
-TEST(TryFlat, SubstrConcat) {
-  absl::Cord c = absl::MakeFragmentedCord({"hello", " world"});
-  c.RemovePrefix(1);
-  EXPECT_EQ(c.TryFlat(), absl::nullopt);
-}
-
-static bool IsFlat(const absl::Cord& c) {
-  return c.chunk_begin() == c.chunk_end() || ++c.chunk_begin() == c.chunk_end();
-}
-
-static void VerifyFlatten(absl::Cord c) {
-  std::string old_contents(c);
-  absl::string_view old_flat;
-  bool already_flat_and_non_empty = IsFlat(c) && !c.empty();
-  if (already_flat_and_non_empty) {
-    old_flat = *c.chunk_begin();
-  }
-  absl::string_view new_flat = c.Flatten();
-
-  // Verify that the contents of the flattened Cord are correct.
-  EXPECT_EQ(new_flat, old_contents);
-  EXPECT_EQ(std::string(c), old_contents);
-
-  // If the Cord contained data and was already flat, verify that the data
-  // wasn't copied.
-  if (already_flat_and_non_empty) {
-    EXPECT_EQ(old_flat.data(), new_flat.data())
-        << "Allocated new memory even though the Cord was already flat.";
-  }
-
-  // Verify that the flattened Cord is in fact flat.
-  EXPECT_TRUE(IsFlat(c));
-}
-
-TEST(Cord, Flatten) {
-  VerifyFlatten(absl::Cord());
-  VerifyFlatten(absl::Cord("small cord"));
-  VerifyFlatten(absl::Cord("larger than small buffer optimization"));
-  VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"}));
-
-  // Test with a cord that is longer than the largest flat buffer
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
-  VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192)));
-}
-
-// Test data
-namespace {
-class TestData {
- private:
-  std::vector<std::string> data_;
-
-  // Return a std::string of the specified length.
-  static std::string MakeString(int length) {
-    std::string result;
-    char buf[30];
-    snprintf(buf, sizeof(buf), "(%d)", length);
-    while (result.size() < length) {
-      result += buf;
-    }
-    result.resize(length);
-    return result;
-  }
-
- public:
-  TestData() {
-    // short strings increasing in length by one
-    for (int i = 0; i < 30; i++) {
-      data_.push_back(MakeString(i));
-    }
-
-    // strings around half kMaxFlatLength
-    static const int kMaxFlatLength = 4096 - 9;
-    static const int kHalf = kMaxFlatLength / 2;
-
-    for (int i = -10; i <= +10; i++) {
-      data_.push_back(MakeString(kHalf + i));
-    }
-
-    for (int i = -10; i <= +10; i++) {
-      data_.push_back(MakeString(kMaxFlatLength + i));
-    }
-  }
-
-  size_t size() const { return data_.size(); }
-  const std::string& data(size_t i) const { return data_[i]; }
-};
-}  // namespace
-
-TEST(Cord, MultipleLengths) {
-  TestData d;
-  for (size_t i = 0; i < d.size(); i++) {
-    std::string a = d.data(i);
-
-    {  // Construct from Cord
-      absl::Cord tmp(a);
-      absl::Cord x(tmp);
-      EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
-    }
-
-    {  // Construct from absl::string_view
-      absl::Cord x(a);
-      EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
-    }
-
-    {  // Append cord to self
-      absl::Cord self(a);
-      self.Append(self);
-      EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
-    }
-
-    {  // Prepend cord to self
-      absl::Cord self(a);
-      self.Prepend(self);
-      EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
-    }
-
-    // Try to append/prepend others
-    for (size_t j = 0; j < d.size(); j++) {
-      std::string b = d.data(j);
-
-      {  // CopyFrom Cord
-        absl::Cord x(a);
-        absl::Cord y(b);
-        x = y;
-        EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
-      }
-
-      {  // CopyFrom absl::string_view
-        absl::Cord x(a);
-        x = b;
-        EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
-      }
-
-      {  // Cord::Append(Cord)
-        absl::Cord x(a);
-        absl::Cord y(b);
-        x.Append(y);
-        EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
-      }
-
-      {  // Cord::Append(absl::string_view)
-        absl::Cord x(a);
-        x.Append(b);
-        EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
-      }
-
-      {  // Cord::Prepend(Cord)
-        absl::Cord x(a);
-        absl::Cord y(b);
-        x.Prepend(y);
-        EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
-      }
-
-      {  // Cord::Prepend(absl::string_view)
-        absl::Cord x(a);
-        x.Prepend(b);
-        EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
-      }
-    }
-  }
-}
-
-namespace {
-
-TEST(Cord, RemoveSuffixWithExternalOrSubstring) {
-  absl::Cord cord = absl::MakeCordFromExternal(
-      "foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); });
-
-  EXPECT_EQ("foo bar baz", std::string(cord));
-
-  // This RemoveSuffix() will wrap the EXTERNAL node in a SUBSTRING node.
-  cord.RemoveSuffix(4);
-  EXPECT_EQ("foo bar", std::string(cord));
-
-  // This RemoveSuffix() will adjust the SUBSTRING node in-place.
-  cord.RemoveSuffix(4);
-  EXPECT_EQ("foo", std::string(cord));
-}
-
-TEST(Cord, RemoveSuffixMakesZeroLengthNode) {
-  absl::Cord c;
-  c.Append(absl::Cord(std::string(100, 'x')));
-  absl::Cord other_ref = c;  // Prevent inplace appends
-  c.Append(absl::Cord(std::string(200, 'y')));
-  c.RemoveSuffix(200);
-  EXPECT_EQ(std::string(100, 'x'), std::string(c));
-}
-
-}  // namespace
-
-// CordSpliceTest contributed by hendrie.
-namespace {
-
-// Create a cord with an external memory block filled with 'z'
-absl::Cord CordWithZedBlock(size_t size) {
-  char* data = new char[size];
-  if (size > 0) {
-    memset(data, 'z', size);
-  }
-  absl::Cord cord = absl::MakeCordFromExternal(
-      absl::string_view(data, size),
-      [](absl::string_view s) { delete[] s.data(); });
-  return cord;
-}
-
-// Establish that ZedBlock does what we think it does.
-TEST(CordSpliceTest, ZedBlock) {
-  absl::Cord blob = CordWithZedBlock(10);
-  EXPECT_EQ(10, blob.size());
-  std::string s;
-  absl::CopyCordToString(blob, &s);
-  EXPECT_EQ("zzzzzzzzzz", s);
-}
-
-TEST(CordSpliceTest, ZedBlock0) {
-  absl::Cord blob = CordWithZedBlock(0);
-  EXPECT_EQ(0, blob.size());
-  std::string s;
-  absl::CopyCordToString(blob, &s);
-  EXPECT_EQ("", s);
-}
-
-TEST(CordSpliceTest, ZedBlockSuffix1) {
-  absl::Cord blob = CordWithZedBlock(10);
-  EXPECT_EQ(10, blob.size());
-  absl::Cord suffix(blob);
-  suffix.RemovePrefix(9);
-  EXPECT_EQ(1, suffix.size());
-  std::string s;
-  absl::CopyCordToString(suffix, &s);
-  EXPECT_EQ("z", s);
-}
-
-// Remove all of a prefix block
-TEST(CordSpliceTest, ZedBlockSuffix0) {
-  absl::Cord blob = CordWithZedBlock(10);
-  EXPECT_EQ(10, blob.size());
-  absl::Cord suffix(blob);
-  suffix.RemovePrefix(10);
-  EXPECT_EQ(0, suffix.size());
-  std::string s;
-  absl::CopyCordToString(suffix, &s);
-  EXPECT_EQ("", s);
-}
-
-absl::Cord BigCord(size_t len, char v) {
-  std::string s(len, v);
-  return absl::Cord(s);
-}
-
-// Splice block into cord.
-absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset,
-                      const absl::Cord& block) {
-  ABSL_RAW_CHECK(offset >= 0, "");
-  ABSL_RAW_CHECK(offset + block.size() <= blob.size(), "");
-  absl::Cord result(blob);
-  result.RemoveSuffix(blob.size() - offset);
-  result.Append(block);
-  absl::Cord suffix(blob);
-  suffix.RemovePrefix(offset + block.size());
-  result.Append(suffix);
-  ABSL_RAW_CHECK(blob.size() == result.size(), "");
-  return result;
-}
-
-// Taking an empty suffix of a block breaks appending.
-TEST(CordSpliceTest, RemoveEntireBlock1) {
-  absl::Cord zero = CordWithZedBlock(10);
-  absl::Cord suffix(zero);
-  suffix.RemovePrefix(10);
-  absl::Cord result;
-  result.Append(suffix);
-}
-
-TEST(CordSpliceTest, RemoveEntireBlock2) {
-  absl::Cord zero = CordWithZedBlock(10);
-  absl::Cord prefix(zero);
-  prefix.RemoveSuffix(10);
-  absl::Cord suffix(zero);
-  suffix.RemovePrefix(10);
-  absl::Cord result(prefix);
-  result.Append(suffix);
-}
-
-TEST(CordSpliceTest, RemoveEntireBlock3) {
-  absl::Cord blob = CordWithZedBlock(10);
-  absl::Cord block = BigCord(10, 'b');
-  blob = SpliceCord(blob, 0, block);
-}
-
-struct CordCompareTestCase {
-  template <typename LHS, typename RHS>
-  CordCompareTestCase(const LHS& lhs, const RHS& rhs)
-      : lhs_cord(lhs), rhs_cord(rhs) {}
-
-  absl::Cord lhs_cord;
-  absl::Cord rhs_cord;
-};
-
-const auto sign = [](int x) { return x == 0 ? 0 : (x > 0 ? 1 : -1); };
-
-void VerifyComparison(const CordCompareTestCase& test_case) {
-  std::string lhs_string(test_case.lhs_cord);
-  std::string rhs_string(test_case.rhs_cord);
-  int expected = sign(lhs_string.compare(rhs_string));
-  EXPECT_EQ(expected, test_case.lhs_cord.Compare(test_case.rhs_cord))
-      << "LHS=" << lhs_string << "; RHS=" << rhs_string;
-  EXPECT_EQ(expected, test_case.lhs_cord.Compare(rhs_string))
-      << "LHS=" << lhs_string << "; RHS=" << rhs_string;
-  EXPECT_EQ(-expected, test_case.rhs_cord.Compare(test_case.lhs_cord))
-      << "LHS=" << rhs_string << "; RHS=" << lhs_string;
-  EXPECT_EQ(-expected, test_case.rhs_cord.Compare(lhs_string))
-      << "LHS=" << rhs_string << "; RHS=" << lhs_string;
-}
-
-TEST(Cord, Compare) {
-  absl::Cord subcord("aaaaaBBBBBcccccDDDDD");
-  subcord = subcord.Subcord(3, 10);
-
-  absl::Cord tmp("aaaaaaaaaaaaaaaa");
-  tmp.Append("BBBBBBBBBBBBBBBB");
-  absl::Cord concat = absl::Cord("cccccccccccccccc");
-  concat.Append("DDDDDDDDDDDDDDDD");
-  concat.Prepend(tmp);
-
-  absl::Cord concat2("aaaaaaaaaaaaa");
-  concat2.Append("aaaBBBBBBBBBBBBBBBBccccc");
-  concat2.Append("cccccccccccDDDDDDDDDDDDDD");
-  concat2.Append("DD");
-
-  std::vector<CordCompareTestCase> test_cases = {{
-      // Inline cords
-      {"abcdef", "abcdef"},
-      {"abcdef", "abcdee"},
-      {"abcdef", "abcdeg"},
-      {"bbcdef", "abcdef"},
-      {"bbcdef", "abcdeg"},
-      {"abcdefa", "abcdef"},
-      {"abcdef", "abcdefa"},
-
-      // Small flat cords
-      {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDD"},
-      {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBxccccDDDDD"},
-      {"aaaaaBBBBBcxcccDDDDD", "aaaaaBBBBBcccccDDDDD"},
-      {"aaaaaBBBBBxccccDDDDD", "aaaaaBBBBBcccccDDDDX"},
-      {"aaaaaBBBBBcccccDDDDDa", "aaaaaBBBBBcccccDDDDD"},
-      {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDDa"},
-
-      // Subcords
-      {subcord, subcord},
-      {subcord, "aaBBBBBccc"},
-      {subcord, "aaBBBBBccd"},
-      {subcord, "aaBBBBBccb"},
-      {subcord, "aaBBBBBxcb"},
-      {subcord, "aaBBBBBccca"},
-      {subcord, "aaBBBBBcc"},
-
-      // Concats
-      {concat, concat},
-      {concat,
-       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD"},
-      {concat,
-       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD"},
-      {concat,
-       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD"},
-      {concat,
-       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD"},
-      {concat,
-       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe"},
-
-      {concat, concat2},
-  }};
-
-  for (const auto& tc : test_cases) {
-    VerifyComparison(tc);
-  }
-}
-
-TEST(Cord, CompareAfterAssign) {
-  absl::Cord a("aaaaaa1111111");
-  absl::Cord b("aaaaaa2222222");
-  a = "cccccc";
-  b = "cccccc";
-  EXPECT_EQ(a, b);
-  EXPECT_FALSE(a < b);
-
-  a = "aaaa";
-  b = "bbbbb";
-  a = "";
-  b = "";
-  EXPECT_EQ(a, b);
-  EXPECT_FALSE(a < b);
-}
-
-// Test CompareTo() and ComparePrefix() against string and substring
-// comparison methods from basic_string.
-static void TestCompare(const absl::Cord& c, const absl::Cord& d,
-                        RandomEngine* rng) {
-  typedef std::basic_string<uint8_t> ustring;
-  ustring cs(reinterpret_cast<const uint8_t*>(std::string(c).data()), c.size());
-  ustring ds(reinterpret_cast<const uint8_t*>(std::string(d).data()), d.size());
-  // ustring comparison is ideal because we expect Cord comparisons to be
-  // based on unsigned byte comparisons regardless of whether char is signed.
-  int expected = sign(cs.compare(ds));
-  EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d;
-}
-
-TEST(Compare, ComparisonIsUnsigned) {
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
-  std::uniform_int_distribution<uint32_t> uniform_uint8(0, 255);
-  char x = static_cast<char>(uniform_uint8(rng));
-  TestCompare(
-      absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x)),
-      absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x ^ 0x80)), &rng);
-}
-
-TEST(Compare, RandomComparisons) {
-  const int kIters = 5000;
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
-
-  int n = GetUniformRandomUpTo(&rng, 5000);
-  absl::Cord a[] = {MakeExternalCord(n),
-                    absl::Cord("ant"),
-                    absl::Cord("elephant"),
-                    absl::Cord("giraffe"),
-                    absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100),
-                                           GetUniformRandomUpTo(&rng, 100))),
-                    absl::Cord(""),
-                    absl::Cord("x"),
-                    absl::Cord("A"),
-                    absl::Cord("B"),
-                    absl::Cord("C")};
-  for (int i = 0; i < kIters; i++) {
-    absl::Cord c, d;
-    for (int j = 0; j < (i % 7) + 1; j++) {
-      c.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]);
-      d.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]);
-    }
-    std::bernoulli_distribution coin_flip(0.5);
-    TestCompare(coin_flip(rng) ? c : absl::Cord(std::string(c)),
-                coin_flip(rng) ? d : absl::Cord(std::string(d)), &rng);
-  }
-}
-
-template <typename T1, typename T2>
-void CompareOperators() {
-  const T1 a("a");
-  const T2 b("b");
-
-  EXPECT_TRUE(a == a);
-  // For pointer type (i.e. `const char*`), operator== compares the address
-  // instead of the string, so `a == const char*("a")` isn't necessarily true.
-  EXPECT_TRUE(std::is_pointer<T1>::value || a == T1("a"));
-  EXPECT_TRUE(std::is_pointer<T2>::value || a == T2("a"));
-  EXPECT_FALSE(a == b);
-
-  EXPECT_TRUE(a != b);
-  EXPECT_FALSE(a != a);
-
-  EXPECT_TRUE(a < b);
-  EXPECT_FALSE(b < a);
-
-  EXPECT_TRUE(b > a);
-  EXPECT_FALSE(a > b);
-
-  EXPECT_TRUE(a >= a);
-  EXPECT_TRUE(b >= a);
-  EXPECT_FALSE(a >= b);
-
-  EXPECT_TRUE(a <= a);
-  EXPECT_TRUE(a <= b);
-  EXPECT_FALSE(b <= a);
-}
-
-TEST(ComparisonOperators, Cord_Cord) {
-  CompareOperators<absl::Cord, absl::Cord>();
-}
-
-TEST(ComparisonOperators, Cord_StringPiece) {
-  CompareOperators<absl::Cord, absl::string_view>();
-}
-
-TEST(ComparisonOperators, StringPiece_Cord) {
-  CompareOperators<absl::string_view, absl::Cord>();
-}
-
-TEST(ComparisonOperators, Cord_string) {
-  CompareOperators<absl::Cord, std::string>();
-}
-
-TEST(ComparisonOperators, string_Cord) {
-  CompareOperators<std::string, absl::Cord>();
-}
-
-TEST(ComparisonOperators, stdstring_Cord) {
-  CompareOperators<std::string, absl::Cord>();
-}
-
-TEST(ComparisonOperators, Cord_stdstring) {
-  CompareOperators<absl::Cord, std::string>();
-}
-
-TEST(ComparisonOperators, charstar_Cord) {
-  CompareOperators<const char*, absl::Cord>();
-}
-
-TEST(ComparisonOperators, Cord_charstar) {
-  CompareOperators<absl::Cord, const char*>();
-}
-
-TEST(ConstructFromExternal, ReleaserInvoked) {
-  // Empty external memory means the releaser should be called immediately.
-  {
-    bool invoked = false;
-    auto releaser = [&invoked](absl::string_view) { invoked = true; };
-    {
-      auto c = absl::MakeCordFromExternal("", releaser);
-      EXPECT_TRUE(invoked);
-    }
-  }
-
-  // If the size of the data is small enough, a future constructor
-  // implementation may copy the bytes and immediately invoke the releaser
-  // instead of creating an external node. We make a large dummy std::string to
-  // make this test independent of such an optimization.
-  std::string large_dummy(2048, 'c');
-  {
-    bool invoked = false;
-    auto releaser = [&invoked](absl::string_view) { invoked = true; };
-    {
-      auto c = absl::MakeCordFromExternal(large_dummy, releaser);
-      EXPECT_FALSE(invoked);
-    }
-    EXPECT_TRUE(invoked);
-  }
-
-  {
-    bool invoked = false;
-    auto releaser = [&invoked](absl::string_view) { invoked = true; };
-    {
-      absl::Cord copy;
-      {
-        auto c = absl::MakeCordFromExternal(large_dummy, releaser);
-        copy = c;
-        EXPECT_FALSE(invoked);
-      }
-      EXPECT_FALSE(invoked);
-    }
-    EXPECT_TRUE(invoked);
-  }
-}
-
-TEST(ConstructFromExternal, CompareContents) {
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
-
-  for (int length = 1; length <= 2048; length *= 2) {
-    std::string data = RandomLowercaseString(&rng, length);
-    auto* external = new std::string(data);
-    auto cord =
-        absl::MakeCordFromExternal(*external, [external](absl::string_view sv) {
-          EXPECT_EQ(external->data(), sv.data());
-          EXPECT_EQ(external->size(), sv.size());
-          delete external;
-        });
-    EXPECT_EQ(data, cord);
-  }
-}
-
-TEST(ConstructFromExternal, LargeReleaser) {
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
-  constexpr size_t kLength = 256;
-  std::string data = RandomLowercaseString(&rng, kLength);
-  std::array<char, kLength> data_array;
-  for (size_t i = 0; i < kLength; ++i) data_array[i] = data[i];
-  bool invoked = false;
-  auto releaser = [data_array, &invoked](absl::string_view data) {
-    EXPECT_EQ(data, absl::string_view(data_array.data(), data_array.size()));
-    invoked = true;
-  };
-  (void)absl::MakeCordFromExternal(data, releaser);
-  EXPECT_TRUE(invoked);
-}
-
-TEST(ConstructFromExternal, FunctionPointerReleaser) {
-  static absl::string_view data("hello world");
-  static bool invoked;
-  auto* releaser =
-      static_cast<void (*)(absl::string_view)>([](absl::string_view sv) {
-        EXPECT_EQ(data, sv);
-        invoked = true;
-      });
-  invoked = false;
-  (void)absl::MakeCordFromExternal(data, releaser);
-  EXPECT_TRUE(invoked);
-
-  invoked = false;
-  (void)absl::MakeCordFromExternal(data, *releaser);
-  EXPECT_TRUE(invoked);
-}
-
-TEST(ConstructFromExternal, MoveOnlyReleaser) {
-  struct Releaser {
-    explicit Releaser(bool* invoked) : invoked(invoked) {}
-    Releaser(Releaser&& other) noexcept : invoked(other.invoked) {}
-    void operator()(absl::string_view) const { *invoked = true; }
-
-    bool* invoked;
-  };
-
-  bool invoked = false;
-  (void)absl::MakeCordFromExternal("dummy", Releaser(&invoked));
-  EXPECT_TRUE(invoked);
-}
-
-TEST(ConstructFromExternal, NoArgLambda) {
-  bool invoked = false;
-  (void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; });
-  EXPECT_TRUE(invoked);
-}
-
-TEST(ConstructFromExternal, StringViewArgLambda) {
-  bool invoked = false;
-  (void)absl::MakeCordFromExternal(
-      "dummy", [&invoked](absl::string_view) { invoked = true; });
-  EXPECT_TRUE(invoked);
-}
-
-TEST(ConstructFromExternal, NonTrivialReleaserDestructor) {
-  struct Releaser {
-    explicit Releaser(bool* destroyed) : destroyed(destroyed) {}
-    ~Releaser() { *destroyed = true; }
-    void operator()(absl::string_view) const {}
-
-    bool* destroyed;
-  };
-
-  bool destroyed = false;
-  Releaser releaser(&destroyed);
-  (void)absl::MakeCordFromExternal("dummy", releaser);
-  EXPECT_TRUE(destroyed);
-}
-
-TEST(ConstructFromExternal, ReferenceQualifierOverloads) {
-  struct Releaser {
-    void operator()(absl::string_view) & { *lvalue_invoked = true; }
-    void operator()(absl::string_view) && { *rvalue_invoked = true; }
-
-    bool* lvalue_invoked;
-    bool* rvalue_invoked;
-  };
-
-  bool lvalue_invoked = false;
-  bool rvalue_invoked = false;
-  Releaser releaser = {&lvalue_invoked, &rvalue_invoked};
-  (void)absl::MakeCordFromExternal("", releaser);
-  EXPECT_FALSE(lvalue_invoked);
-  EXPECT_TRUE(rvalue_invoked);
-  rvalue_invoked = false;
-
-  (void)absl::MakeCordFromExternal("dummy", releaser);
-  EXPECT_FALSE(lvalue_invoked);
-  EXPECT_TRUE(rvalue_invoked);
-  rvalue_invoked = false;
-
-  // NOLINTNEXTLINE: suppress clang-tidy std::move on trivially copyable type.
-  (void)absl::MakeCordFromExternal("dummy", std::move(releaser));
-  EXPECT_FALSE(lvalue_invoked);
-  EXPECT_TRUE(rvalue_invoked);
-}
-
-TEST(ExternalMemory, BasicUsage) {
-  static const char* strings[] = {"", "hello", "there"};
-  for (const char* str : strings) {
-    absl::Cord dst("(prefix)");
-    AddExternalMemory(str, &dst);
-    dst.Append("(suffix)");
-    EXPECT_EQ((std::string("(prefix)") + str + std::string("(suffix)")),
-              std::string(dst));
-  }
-}
-
-TEST(ExternalMemory, RemovePrefixSuffix) {
-  // Exhaustively try all sub-strings.
-  absl::Cord cord = MakeComposite();
-  std::string s = std::string(cord);
-  for (int offset = 0; offset <= s.size(); offset++) {
-    for (int length = 0; length <= s.size() - offset; length++) {
-      absl::Cord result(cord);
-      result.RemovePrefix(offset);
-      result.RemoveSuffix(result.size() - length);
-      EXPECT_EQ(s.substr(offset, length), std::string(result))
-          << offset << " " << length;
-    }
-  }
-}
-
-TEST(ExternalMemory, Get) {
-  absl::Cord cord("hello");
-  AddExternalMemory(" world!", &cord);
-  AddExternalMemory(" how are ", &cord);
-  cord.Append(" you?");
-  std::string s = std::string(cord);
-  for (int i = 0; i < s.size(); i++) {
-    EXPECT_EQ(s[i], cord[i]);
-  }
-}
-
-// CordMemoryUsage tests verify the correctness of the EstimatedMemoryUsage()
-// These tests take into account that the reported memory usage is approximate
-// and non-deterministic. For all tests, We verify that the reported memory
-// usage is larger than `size()`, and less than `size() * 1.5` as a cord should
-// never reserve more 'extra' capacity than half of its size as it grows.
-// Additionally we have some whiteboxed expectations based on our knowledge of
-// the layout and size of empty and inlined cords, and flat nodes.
-
-TEST(CordMemoryUsage, Empty) {
-  EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage());
-}
-
-TEST(CordMemoryUsage, Embedded) {
-  absl::Cord a("hello");
-  EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
-}
-
-TEST(CordMemoryUsage, EmbeddedAppend) {
-  absl::Cord a("a");
-  absl::Cord b("bcd");
-  EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord));
-  a.Append(b);
-  EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
-}
-
-TEST(CordMemoryUsage, ExternalMemory) {
-  static const int kLength = 1000;
-  absl::Cord cord;
-  AddExternalMemory(std::string(kLength, 'x'), &cord);
-  EXPECT_GT(cord.EstimatedMemoryUsage(), kLength);
-  EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5);
-}
-
-TEST(CordMemoryUsage, Flat) {
-  static const int kLength = 125;
-  absl::Cord a(std::string(kLength, 'a'));
-  EXPECT_GT(a.EstimatedMemoryUsage(), kLength);
-  EXPECT_LE(a.EstimatedMemoryUsage(), kLength * 1.5);
-}
-
-TEST(CordMemoryUsage, AppendFlat) {
-  using absl::strings_internal::CordTestAccess;
-  absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a'));
-  size_t length = a.EstimatedMemoryUsage();
-  a.Append(std::string(CordTestAccess::MaxFlatLength(), 'b'));
-  size_t delta = a.EstimatedMemoryUsage() - length;
-  EXPECT_GT(delta, CordTestAccess::MaxFlatLength());
-  EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 1.5);
-}
-
-// Regtest for a change that had to be rolled back because it expanded out
-// of the InlineRep too soon, which was observable through MemoryUsage().
-TEST(CordMemoryUsage, InlineRep) {
-  constexpr size_t kMaxInline = 15;  // Cord::InlineRep::N
-  const std::string small_string(kMaxInline, 'x');
-  absl::Cord c1(small_string);
-
-  absl::Cord c2;
-  c2.Append(small_string);
-  EXPECT_EQ(c1, c2);
-  EXPECT_EQ(c1.EstimatedMemoryUsage(), c2.EstimatedMemoryUsage());
-}
-
-}  // namespace
-
-// Regtest for 7510292 (fix a bug introduced by 7465150)
-TEST(Cord, Concat_Append) {
-  // Create a rep of type CONCAT
-  absl::Cord s1("foobarbarbarbarbar");
-  s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg");
-  size_t size = s1.size();
-
-  // Create a copy of s1 and append to it.
-  absl::Cord s2 = s1;
-  s2.Append("x");
-
-  // 7465150 modifies s1 when it shouldn't.
-  EXPECT_EQ(s1.size(), size);
-  EXPECT_EQ(s2.size(), size + 1);
-}
-
-TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) {
-  absl::Cord fragmented =
-      absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});
-
-  EXPECT_EQ("A fragmented Cord", fragmented);
-
-  auto chunk_it = fragmented.chunk_begin();
-
-  ASSERT_TRUE(chunk_it != fragmented.chunk_end());
-  EXPECT_EQ("A ", *chunk_it);
-
-  ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
-  EXPECT_EQ("fragmented ", *chunk_it);
-
-  ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
-  EXPECT_EQ("Cord", *chunk_it);
-
-  ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
-}
-
-TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) {
-  std::vector<absl::string_view> chunks = {"A ", "fragmented ", "Cord"};
-  absl::Cord fragmented = absl::MakeFragmentedCord(chunks);
-
-  EXPECT_EQ("A fragmented Cord", fragmented);
-
-  auto chunk_it = fragmented.chunk_begin();
-
-  ASSERT_TRUE(chunk_it != fragmented.chunk_end());
-  EXPECT_EQ("A ", *chunk_it);
-
-  ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
-  EXPECT_EQ("fragmented ", *chunk_it);
-
-  ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
-  EXPECT_EQ("Cord", *chunk_it);
-
-  ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
-}
-
-TEST(CordChunkIterator, Traits) {
-  static_assert(std::is_copy_constructible<absl::Cord::ChunkIterator>::value,
-                "");
-  static_assert(std::is_copy_assignable<absl::Cord::ChunkIterator>::value, "");
-
-  // Move semantics to satisfy swappable via std::swap
-  static_assert(std::is_move_constructible<absl::Cord::ChunkIterator>::value,
-                "");
-  static_assert(std::is_move_assignable<absl::Cord::ChunkIterator>::value, "");
-
-  static_assert(
-      std::is_same<
-          std::iterator_traits<absl::Cord::ChunkIterator>::iterator_category,
-          std::input_iterator_tag>::value,
-      "");
-  static_assert(
-      std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::value_type,
-                   absl::string_view>::value,
-      "");
-  static_assert(
-      std::is_same<
-          std::iterator_traits<absl::Cord::ChunkIterator>::difference_type,
-          ptrdiff_t>::value,
-      "");
-  static_assert(
-      std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::pointer,
-                   const absl::string_view*>::value,
-      "");
-  static_assert(
-      std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::reference,
-                   absl::string_view>::value,
-      "");
-}
-
-static void VerifyChunkIterator(const absl::Cord& cord,
-                                size_t expected_chunks) {
-  EXPECT_EQ(cord.chunk_begin() == cord.chunk_end(), cord.empty()) << cord;
-  EXPECT_EQ(cord.chunk_begin() != cord.chunk_end(), !cord.empty());
-
-  absl::Cord::ChunkRange range = cord.Chunks();
-  EXPECT_EQ(range.begin() == range.end(), cord.empty());
-  EXPECT_EQ(range.begin() != range.end(), !cord.empty());
-
-  std::string content(cord);
-  size_t pos = 0;
-  auto pre_iter = cord.chunk_begin(), post_iter = cord.chunk_begin();
-  size_t n_chunks = 0;
-  while (pre_iter != cord.chunk_end() && post_iter != cord.chunk_end()) {
-    EXPECT_FALSE(pre_iter == cord.chunk_end());   // NOLINT: explicitly test ==
-    EXPECT_FALSE(post_iter == cord.chunk_end());  // NOLINT
-
-    EXPECT_EQ(pre_iter, post_iter);
-    EXPECT_EQ(*pre_iter, *post_iter);
-
-    EXPECT_EQ(pre_iter->data(), (*pre_iter).data());
-    EXPECT_EQ(pre_iter->size(), (*pre_iter).size());
-
-    absl::string_view chunk = *pre_iter;
-    EXPECT_FALSE(chunk.empty());
-    EXPECT_LE(pos + chunk.size(), content.size());
-    EXPECT_EQ(absl::string_view(content.c_str() + pos, chunk.size()), chunk);
-
-    int n_equal_iterators = 0;
-    for (absl::Cord::ChunkIterator it = range.begin(); it != range.end();
-         ++it) {
-      n_equal_iterators += static_cast<int>(it == pre_iter);
-    }
-    EXPECT_EQ(n_equal_iterators, 1);
-
-    ++pre_iter;
-    EXPECT_EQ(*post_iter++, chunk);
-
-    pos += chunk.size();
-    ++n_chunks;
-  }
-  EXPECT_EQ(expected_chunks, n_chunks);
-  EXPECT_EQ(pos, content.size());
-  EXPECT_TRUE(pre_iter == cord.chunk_end());   // NOLINT: explicitly test ==
-  EXPECT_TRUE(post_iter == cord.chunk_end());  // NOLINT
-}
-
-TEST(CordChunkIterator, Operations) {
-  absl::Cord empty_cord;
-  VerifyChunkIterator(empty_cord, 0);
-
-  absl::Cord small_buffer_cord("small cord");
-  VerifyChunkIterator(small_buffer_cord, 1);
-
-  absl::Cord flat_node_cord("larger than small buffer optimization");
-  VerifyChunkIterator(flat_node_cord, 1);
-
-  VerifyChunkIterator(
-      absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ",
-                                "testing ", "chunk ", "iterations."}),
-      8);
-
-  absl::Cord reused_nodes_cord(std::string(40, 'c'));
-  reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'b')));
-  reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'a')));
-  size_t expected_chunks = 3;
-  for (int i = 0; i < 8; ++i) {
-    reused_nodes_cord.Prepend(reused_nodes_cord);
-    expected_chunks *= 2;
-    VerifyChunkIterator(reused_nodes_cord, expected_chunks);
-  }
-
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
-  absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
-  absl::Cord subcords;
-  for (int i = 0; i < 128; ++i) subcords.Prepend(flat_cord.Subcord(i, 128));
-  VerifyChunkIterator(subcords, 128);
-}
-
-TEST(CordCharIterator, Traits) {
-  static_assert(std::is_copy_constructible<absl::Cord::CharIterator>::value,
-                "");
-  static_assert(std::is_copy_assignable<absl::Cord::CharIterator>::value, "");
-
-  // Move semantics to satisfy swappable via std::swap
-  static_assert(std::is_move_constructible<absl::Cord::CharIterator>::value,
-                "");
-  static_assert(std::is_move_assignable<absl::Cord::CharIterator>::value, "");
-
-  static_assert(
-      std::is_same<
-          std::iterator_traits<absl::Cord::CharIterator>::iterator_category,
-          std::input_iterator_tag>::value,
-      "");
-  static_assert(
-      std::is_same<std::iterator_traits<absl::Cord::CharIterator>::value_type,
-                   char>::value,
-      "");
-  static_assert(
-      std::is_same<
-          std::iterator_traits<absl::Cord::CharIterator>::difference_type,
-          ptrdiff_t>::value,
-      "");
-  static_assert(
-      std::is_same<std::iterator_traits<absl::Cord::CharIterator>::pointer,
-                   const char*>::value,
-      "");
-  static_assert(
-      std::is_same<std::iterator_traits<absl::Cord::CharIterator>::reference,
-                   const char&>::value,
-      "");
-}
-
-static void VerifyCharIterator(const absl::Cord& cord) {
-  EXPECT_EQ(cord.char_begin() == cord.char_end(), cord.empty());
-  EXPECT_EQ(cord.char_begin() != cord.char_end(), !cord.empty());
-
-  absl::Cord::CharRange range = cord.Chars();
-  EXPECT_EQ(range.begin() == range.end(), cord.empty());
-  EXPECT_EQ(range.begin() != range.end(), !cord.empty());
-
-  size_t i = 0;
-  absl::Cord::CharIterator pre_iter = cord.char_begin();
-  absl::Cord::CharIterator post_iter = cord.char_begin();
-  std::string content(cord);
-  while (pre_iter != cord.char_end() && post_iter != cord.char_end()) {
-    EXPECT_FALSE(pre_iter == cord.char_end());   // NOLINT: explicitly test ==
-    EXPECT_FALSE(post_iter == cord.char_end());  // NOLINT
-
-    EXPECT_LT(i, cord.size());
-    EXPECT_EQ(content[i], *pre_iter);
-
-    EXPECT_EQ(pre_iter, post_iter);
-    EXPECT_EQ(*pre_iter, *post_iter);
-    EXPECT_EQ(&*pre_iter, &*post_iter);
-
-    EXPECT_EQ(&*pre_iter, pre_iter.operator->());
-
-    const char* character_address = &*pre_iter;
-    absl::Cord::CharIterator copy = pre_iter;
-    ++copy;
-    EXPECT_EQ(character_address, &*pre_iter);
-
-    int n_equal_iterators = 0;
-    for (absl::Cord::CharIterator it = range.begin(); it != range.end(); ++it) {
-      n_equal_iterators += static_cast<int>(it == pre_iter);
-    }
-    EXPECT_EQ(n_equal_iterators, 1);
-
-    absl::Cord::CharIterator advance_iter = range.begin();
-    absl::Cord::Advance(&advance_iter, i);
-    EXPECT_EQ(pre_iter, advance_iter);
-
-    advance_iter = range.begin();
-    EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, i), cord.Subcord(0, i));
-    EXPECT_EQ(pre_iter, advance_iter);
-
-    advance_iter = pre_iter;
-    absl::Cord::Advance(&advance_iter, cord.size() - i);
-    EXPECT_EQ(range.end(), advance_iter);
-
-    advance_iter = pre_iter;
-    EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, cord.size() - i),
-              cord.Subcord(i, cord.size() - i));
-    EXPECT_EQ(range.end(), advance_iter);
-
-    ++i;
-    ++pre_iter;
-    post_iter++;
-  }
-  EXPECT_EQ(i, cord.size());
-  EXPECT_TRUE(pre_iter == cord.char_end());   // NOLINT: explicitly test ==
-  EXPECT_TRUE(post_iter == cord.char_end());  // NOLINT
-
-  absl::Cord::CharIterator zero_advanced_end = cord.char_end();
-  absl::Cord::Advance(&zero_advanced_end, 0);
-  EXPECT_EQ(zero_advanced_end, cord.char_end());
-
-  absl::Cord::CharIterator it = cord.char_begin();
-  for (absl::string_view chunk : cord.Chunks()) {
-    while (!chunk.empty()) {
-      EXPECT_EQ(absl::Cord::ChunkRemaining(it), chunk);
-      chunk.remove_prefix(1);
-      ++it;
-    }
-  }
-}
-
-TEST(CordCharIterator, Operations) {
-  absl::Cord empty_cord;
-  VerifyCharIterator(empty_cord);
-
-  absl::Cord small_buffer_cord("small cord");
-  VerifyCharIterator(small_buffer_cord);
-
-  absl::Cord flat_node_cord("larger than small buffer optimization");
-  VerifyCharIterator(flat_node_cord);
-
-  VerifyCharIterator(
-      absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ",
-                                "testing ", "character ", "iteration."}));
-
-  absl::Cord reused_nodes_cord("ghi");
-  reused_nodes_cord.Prepend(absl::Cord("def"));
-  reused_nodes_cord.Prepend(absl::Cord("abc"));
-  for (int i = 0; i < 4; ++i) {
-    reused_nodes_cord.Prepend(reused_nodes_cord);
-    VerifyCharIterator(reused_nodes_cord);
-  }
-
-  RandomEngine rng(testing::GTEST_FLAG(random_seed));
-  absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
-  absl::Cord subcords;
-  for (int i = 0; i < 4; ++i) subcords.Prepend(flat_cord.Subcord(16 * i, 128));
-  VerifyCharIterator(subcords);
-}
-
-TEST(Cord, StreamingOutput) {
-  absl::Cord c =
-      absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."});
-  std::stringstream output;
-  output << c;
-  EXPECT_EQ("A small fragmented Cord.", output.str());
-}
-
-TEST(Cord, ForEachChunk) {
-  for (int num_elements : {1, 10, 200}) {
-    SCOPED_TRACE(num_elements);
-    std::vector<std::string> cord_chunks;
-    for (int i = 0; i < num_elements; ++i) {
-      cord_chunks.push_back(absl::StrCat("[", i, "]"));
-    }
-    absl::Cord c = absl::MakeFragmentedCord(cord_chunks);
-
-    std::vector<std::string> iterated_chunks;
-    absl::CordTestPeer::ForEachChunk(c,
-                                     [&iterated_chunks](absl::string_view sv) {
-                                       iterated_chunks.emplace_back(sv);
-                                     });
-    EXPECT_EQ(iterated_chunks, cord_chunks);
-  }
-}
-
-TEST(Cord, SmallBufferAssignFromOwnData) {
-  constexpr size_t kMaxInline = 15;
-  std::string contents = "small buff cord";
-  EXPECT_EQ(contents.size(), kMaxInline);
-  for (size_t pos = 0; pos < contents.size(); ++pos) {
-    for (size_t count = contents.size() - pos; count > 0; --count) {
-      absl::Cord c(contents);
-      absl::string_view flat = c.Flatten();
-      c = flat.substr(pos, count);
-      EXPECT_EQ(c, contents.substr(pos, count))
-          << "pos = " << pos << "; count = " << count;
-    }
-  }
-}
-
-TEST(Cord, Format) {
-  absl::Cord c;
-  absl::Format(&c, "There were %04d little %s.", 3, "pigs");
-  EXPECT_EQ(c, "There were 0003 little pigs.");
-  absl::Format(&c, "And %-3llx bad wolf!", 1);
-  EXPECT_EQ(c, "There were 0003 little pigs.And 1   bad wolf!");
-}
-
-TEST(CordDeathTest, Hardening) {
-  absl::Cord cord("hello");
-  // These statement should abort the program in all builds modes.
-  EXPECT_DEATH_IF_SUPPORTED(cord.RemovePrefix(6), "");
-  EXPECT_DEATH_IF_SUPPORTED(cord.RemoveSuffix(6), "");
-
-  bool test_hardening = false;
-  ABSL_HARDENING_ASSERT([&]() {
-    // This only runs when ABSL_HARDENING_ASSERT is active.
-    test_hardening = true;
-    return true;
-  }());
-  if (!test_hardening) return;
-
-  EXPECT_DEATH_IF_SUPPORTED(cord[5], "");
-  EXPECT_DEATH_IF_SUPPORTED(*cord.chunk_end(), "");
-  EXPECT_DEATH_IF_SUPPORTED(static_cast<void>(cord.chunk_end()->empty()), "");
-  EXPECT_DEATH_IF_SUPPORTED(++cord.chunk_end(), "");
-}
-
-class AfterExitCordTester {
- public:
-  bool Set(absl::Cord* cord, absl::string_view expected) {
-    cord_ = cord;
-    expected_ = expected;
-    return true;
-  }
-
-  ~AfterExitCordTester() {
-    EXPECT_EQ(*cord_, expected_);
-  }
- private:
-  absl::Cord* cord_;
-  absl::string_view expected_;
-};
-
-template <typename Str>
-void TestConstinitConstructor(Str) {
-  const auto expected = Str::value;
-  // Defined before `cord` to be destroyed after it.
-  static AfterExitCordTester exit_tester;  // NOLINT
-  ABSL_CONST_INIT static absl::Cord cord(Str{});  // NOLINT
-  static bool init_exit_tester = exit_tester.Set(&cord, expected);
-  (void)init_exit_tester;
-
-  EXPECT_EQ(cord, expected);
-  // Copy the object and test the copy, and the original.
-  {
-    absl::Cord copy = cord;
-    EXPECT_EQ(copy, expected);
-  }
-  // The original still works
-  EXPECT_EQ(cord, expected);
-
-  // Try making adding more structure to the tree.
-  {
-    absl::Cord copy = cord;
-    std::string expected_copy(expected);
-    for (int i = 0; i < 10; ++i) {
-      copy.Append(cord);
-      absl::StrAppend(&expected_copy, expected);
-      EXPECT_EQ(copy, expected_copy);
-    }
-  }
-
-  // Make sure we are using the right branch during constant evaluation.
-  EXPECT_EQ(absl::CordTestPeer::IsTree(cord), cord.size() >= 16);
-
-  for (int i = 0; i < 10; ++i) {
-    // Make a few more Cords from the same global rep.
-    // This tests what happens when the refcount for it gets below 1.
-    EXPECT_EQ(expected, absl::Cord(Str{}));
-  }
-}
-
-constexpr int SimpleStrlen(const char* p) {
-  return *p ? 1 + SimpleStrlen(p + 1) : 0;
-}
-
-struct ShortView {
-  constexpr absl::string_view operator()() const {
-    return absl::string_view("SSO string", SimpleStrlen("SSO string"));
-  }
-};
-
-struct LongView {
-  constexpr absl::string_view operator()() const {
-    return absl::string_view("String that does not fit SSO.",
-                             SimpleStrlen("String that does not fit SSO."));
-  }
-};
-
-
-TEST(Cord, ConstinitConstructor) {
-  TestConstinitConstructor(
-      absl::strings_internal::MakeStringConstant(ShortView{}));
-  TestConstinitConstructor(
-      absl::strings_internal::MakeStringConstant(LongView{}));
-}
diff --git a/third_party/abseil_cpp/absl/strings/cord_test_helpers.h b/third_party/abseil_cpp/absl/strings/cord_test_helpers.h
deleted file mode 100644
index f1036e3b13..0000000000
--- a/third_party/abseil_cpp/absl/strings/cord_test_helpers.h
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_STRINGS_CORD_TEST_HELPERS_H_
-#define ABSL_STRINGS_CORD_TEST_HELPERS_H_
-
-#include "absl/strings/cord.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Creates a multi-segment Cord from an iterable container of strings.  The
-// resulting Cord is guaranteed to have one segment for every string in the
-// container.  This allows code to be unit tested with multi-segment Cord
-// inputs.
-//
-// Example:
-//
-//   absl::Cord c = absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});
-//   EXPECT_FALSE(c.GetFlat(&unused));
-//
-// The mechanism by which this Cord is created is an implementation detail.  Any
-// implementation that produces a multi-segment Cord may produce a flat Cord in
-// the future as new optimizations are added to the Cord class.
-// MakeFragmentedCord will, however, always be updated to return a multi-segment
-// Cord.
-template <typename Container>
-Cord MakeFragmentedCord(const Container& c) {
-  Cord result;
-  for (const auto& s : c) {
-    auto* external = new std::string(s);
-    Cord tmp = absl::MakeCordFromExternal(
-        *external, [external](absl::string_view) { delete external; });
-    tmp.Prepend(result);
-    result = tmp;
-  }
-  return result;
-}
-
-inline Cord MakeFragmentedCord(std::initializer_list<absl::string_view> list) {
-  return MakeFragmentedCord<std::initializer_list<absl::string_view>>(list);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_CORD_TEST_HELPERS_H_
diff --git a/third_party/abseil_cpp/absl/strings/escaping.cc b/third_party/abseil_cpp/absl/strings/escaping.cc
deleted file mode 100644
index 18b20b83fd..0000000000
--- a/third_party/abseil_cpp/absl/strings/escaping.cc
+++ /dev/null
@@ -1,949 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/escaping.h"
-
-#include <algorithm>
-#include <cassert>
-#include <cstdint>
-#include <cstring>
-#include <iterator>
-#include <limits>
-#include <string>
-
-#include "absl/base/internal/endian.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/unaligned_access.h"
-#include "absl/strings/internal/char_map.h"
-#include "absl/strings/internal/escaping.h"
-#include "absl/strings/internal/resize_uninitialized.h"
-#include "absl/strings/internal/utf8.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_join.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-// These are used for the leave_nulls_escaped argument to CUnescapeInternal().
-constexpr bool kUnescapeNulls = false;
-
-inline bool is_octal_digit(char c) { return ('0' <= c) && (c <= '7'); }
-
-inline int hex_digit_to_int(char c) {
-  static_assert('0' == 0x30 && 'A' == 0x41 && 'a' == 0x61,
-                "Character set must be ASCII.");
-  assert(absl::ascii_isxdigit(c));
-  int x = static_cast<unsigned char>(c);
-  if (x > '9') {
-    x += 9;
-  }
-  return x & 0xf;
-}
-
-inline bool IsSurrogate(char32_t c, absl::string_view src, std::string* error) {
-  if (c >= 0xD800 && c <= 0xDFFF) {
-    if (error) {
-      *error = absl::StrCat("invalid surrogate character (0xD800-DFFF): \\",
-                            src);
-    }
-    return true;
-  }
-  return false;
-}
-
-// ----------------------------------------------------------------------
-// CUnescapeInternal()
-//    Implements both CUnescape() and CUnescapeForNullTerminatedString().
-//
-//    Unescapes C escape sequences and is the reverse of CEscape().
-//
-//    If 'source' is valid, stores the unescaped string and its size in
-//    'dest' and 'dest_len' respectively, and returns true. Otherwise
-//    returns false and optionally stores the error description in
-//    'error'. Set 'error' to nullptr to disable error reporting.
-//
-//    'dest' should point to a buffer that is at least as big as 'source'.
-//    'source' and 'dest' may be the same.
-//
-//     NOTE: any changes to this function must also be reflected in the older
-//     UnescapeCEscapeSequences().
-// ----------------------------------------------------------------------
-bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
-                       char* dest, ptrdiff_t* dest_len, std::string* error) {
-  char* d = dest;
-  const char* p = source.data();
-  const char* end = p + source.size();
-  const char* last_byte = end - 1;
-
-  // Small optimization for case where source = dest and there's no escaping
-  while (p == d && p < end && *p != '\\') p++, d++;
-
-  while (p < end) {
-    if (*p != '\\') {
-      *d++ = *p++;
-    } else {
-      if (++p > last_byte) {  // skip past the '\\'
-        if (error) *error = "String cannot end with \\";
-        return false;
-      }
-      switch (*p) {
-        case 'a':  *d++ = '\a';  break;
-        case 'b':  *d++ = '\b';  break;
-        case 'f':  *d++ = '\f';  break;
-        case 'n':  *d++ = '\n';  break;
-        case 'r':  *d++ = '\r';  break;
-        case 't':  *d++ = '\t';  break;
-        case 'v':  *d++ = '\v';  break;
-        case '\\': *d++ = '\\';  break;
-        case '?':  *d++ = '\?';  break;    // \?  Who knew?
-        case '\'': *d++ = '\'';  break;
-        case '"':  *d++ = '\"';  break;
-        case '0':
-        case '1':
-        case '2':
-        case '3':
-        case '4':
-        case '5':
-        case '6':
-        case '7': {
-          // octal digit: 1 to 3 digits
-          const char* octal_start = p;
-          unsigned int ch = *p - '0';
-          if (p < last_byte && is_octal_digit(p[1])) ch = ch * 8 + *++p - '0';
-          if (p < last_byte && is_octal_digit(p[1]))
-            ch = ch * 8 + *++p - '0';      // now points at last digit
-          if (ch > 0xff) {
-            if (error) {
-              *error = "Value of \\" +
-                       std::string(octal_start, p + 1 - octal_start) +
-                       " exceeds 0xff";
-            }
-            return false;
-          }
-          if ((ch == 0) && leave_nulls_escaped) {
-            // Copy the escape sequence for the null character
-            const ptrdiff_t octal_size = p + 1 - octal_start;
-            *d++ = '\\';
-            memmove(d, octal_start, octal_size);
-            d += octal_size;
-            break;
-          }
-          *d++ = ch;
-          break;
-        }
-        case 'x':
-        case 'X': {
-          if (p >= last_byte) {
-            if (error) *error = "String cannot end with \\x";
-            return false;
-          } else if (!absl::ascii_isxdigit(p[1])) {
-            if (error) *error = "\\x cannot be followed by a non-hex digit";
-            return false;
-          }
-          unsigned int ch = 0;
-          const char* hex_start = p;
-          while (p < last_byte && absl::ascii_isxdigit(p[1]))
-            // Arbitrarily many hex digits
-            ch = (ch << 4) + hex_digit_to_int(*++p);
-          if (ch > 0xFF) {
-            if (error) {
-              *error = "Value of \\" +
-                       std::string(hex_start, p + 1 - hex_start) +
-                       " exceeds 0xff";
-            }
-            return false;
-          }
-          if ((ch == 0) && leave_nulls_escaped) {
-            // Copy the escape sequence for the null character
-            const ptrdiff_t hex_size = p + 1 - hex_start;
-            *d++ = '\\';
-            memmove(d, hex_start, hex_size);
-            d += hex_size;
-            break;
-          }
-          *d++ = ch;
-          break;
-        }
-        case 'u': {
-          // \uhhhh => convert 4 hex digits to UTF-8
-          char32_t rune = 0;
-          const char* hex_start = p;
-          if (p + 4 >= end) {
-            if (error) {
-              *error = "\\u must be followed by 4 hex digits: \\" +
-                       std::string(hex_start, p + 1 - hex_start);
-            }
-            return false;
-          }
-          for (int i = 0; i < 4; ++i) {
-            // Look one char ahead.
-            if (absl::ascii_isxdigit(p[1])) {
-              rune = (rune << 4) + hex_digit_to_int(*++p);  // Advance p.
-            } else {
-              if (error) {
-                *error = "\\u must be followed by 4 hex digits: \\" +
-                         std::string(hex_start, p + 1 - hex_start);
-              }
-              return false;
-            }
-          }
-          if ((rune == 0) && leave_nulls_escaped) {
-            // Copy the escape sequence for the null character
-            *d++ = '\\';
-            memmove(d, hex_start, 5);  // u0000
-            d += 5;
-            break;
-          }
-          if (IsSurrogate(rune, absl::string_view(hex_start, 5), error)) {
-            return false;
-          }
-          d += strings_internal::EncodeUTF8Char(d, rune);
-          break;
-        }
-        case 'U': {
-          // \Uhhhhhhhh => convert 8 hex digits to UTF-8
-          char32_t rune = 0;
-          const char* hex_start = p;
-          if (p + 8 >= end) {
-            if (error) {
-              *error = "\\U must be followed by 8 hex digits: \\" +
-                       std::string(hex_start, p + 1 - hex_start);
-            }
-            return false;
-          }
-          for (int i = 0; i < 8; ++i) {
-            // Look one char ahead.
-            if (absl::ascii_isxdigit(p[1])) {
-              // Don't change rune until we're sure this
-              // is within the Unicode limit, but do advance p.
-              uint32_t newrune = (rune << 4) + hex_digit_to_int(*++p);
-              if (newrune > 0x10FFFF) {
-                if (error) {
-                  *error = "Value of \\" +
-                           std::string(hex_start, p + 1 - hex_start) +
-                           " exceeds Unicode limit (0x10FFFF)";
-                }
-                return false;
-              } else {
-                rune = newrune;
-              }
-            } else {
-              if (error) {
-                *error = "\\U must be followed by 8 hex digits: \\" +
-                         std::string(hex_start, p + 1 - hex_start);
-              }
-              return false;
-            }
-          }
-          if ((rune == 0) && leave_nulls_escaped) {
-            // Copy the escape sequence for the null character
-            *d++ = '\\';
-            memmove(d, hex_start, 9);  // U00000000
-            d += 9;
-            break;
-          }
-          if (IsSurrogate(rune, absl::string_view(hex_start, 9), error)) {
-            return false;
-          }
-          d += strings_internal::EncodeUTF8Char(d, rune);
-          break;
-        }
-        default: {
-          if (error) *error = std::string("Unknown escape sequence: \\") + *p;
-          return false;
-        }
-      }
-      p++;                                 // read past letter we escaped
-    }
-  }
-  *dest_len = d - dest;
-  return true;
-}
-
-// ----------------------------------------------------------------------
-// CUnescapeInternal()
-//
-//    Same as above but uses a std::string for output. 'source' and 'dest'
-//    may be the same.
-// ----------------------------------------------------------------------
-bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
-                       std::string* dest, std::string* error) {
-  strings_internal::STLStringResizeUninitialized(dest, source.size());
-
-  ptrdiff_t dest_size;
-  if (!CUnescapeInternal(source,
-                         leave_nulls_escaped,
-                         &(*dest)[0],
-                         &dest_size,
-                         error)) {
-    return false;
-  }
-  dest->erase(dest_size);
-  return true;
-}
-
-// ----------------------------------------------------------------------
-// CEscape()
-// CHexEscape()
-// Utf8SafeCEscape()
-// Utf8SafeCHexEscape()
-//    Escapes 'src' using C-style escape sequences.  This is useful for
-//    preparing query flags.  The 'Hex' version uses hexadecimal rather than
-//    octal sequences.  The 'Utf8Safe' version does not touch UTF-8 bytes.
-//
-//    Escaped chars: \n, \r, \t, ", ', \, and !absl::ascii_isprint().
-// ----------------------------------------------------------------------
-std::string CEscapeInternal(absl::string_view src, bool use_hex,
-                            bool utf8_safe) {
-  std::string dest;
-  bool last_hex_escape = false;  // true if last output char was \xNN.
-
-  for (unsigned char c : src) {
-    bool is_hex_escape = false;
-    switch (c) {
-      case '\n': dest.append("\\" "n"); break;
-      case '\r': dest.append("\\" "r"); break;
-      case '\t': dest.append("\\" "t"); break;
-      case '\"': dest.append("\\" "\""); break;
-      case '\'': dest.append("\\" "'"); break;
-      case '\\': dest.append("\\" "\\"); break;
-      default:
-        // Note that if we emit \xNN and the src character after that is a hex
-        // digit then that digit must be escaped too to prevent it being
-        // interpreted as part of the character code by C.
-        if ((!utf8_safe || c < 0x80) &&
-            (!absl::ascii_isprint(c) ||
-             (last_hex_escape && absl::ascii_isxdigit(c)))) {
-          if (use_hex) {
-            dest.append("\\" "x");
-            dest.push_back(numbers_internal::kHexChar[c / 16]);
-            dest.push_back(numbers_internal::kHexChar[c % 16]);
-            is_hex_escape = true;
-          } else {
-            dest.append("\\");
-            dest.push_back(numbers_internal::kHexChar[c / 64]);
-            dest.push_back(numbers_internal::kHexChar[(c % 64) / 8]);
-            dest.push_back(numbers_internal::kHexChar[c % 8]);
-          }
-        } else {
-          dest.push_back(c);
-          break;
-        }
-    }
-    last_hex_escape = is_hex_escape;
-  }
-
-  return dest;
-}
-
-/* clang-format off */
-constexpr char c_escaped_len[256] = {
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4,  // \t, \n, \r
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-    1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1,  // ", '
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // '0'..'9'
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 'A'..'O'
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,  // 'P'..'Z', '\'
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 'a'..'o'
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,  // 'p'..'z', DEL
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-};
-/* clang-format on */
-
-// Calculates the length of the C-style escaped version of 'src'.
-// Assumes that non-printable characters are escaped using octal sequences, and
-// that UTF-8 bytes are not handled specially.
-inline size_t CEscapedLength(absl::string_view src) {
-  size_t escaped_len = 0;
-  for (unsigned char c : src) escaped_len += c_escaped_len[c];
-  return escaped_len;
-}
-
-void CEscapeAndAppendInternal(absl::string_view src, std::string* dest) {
-  size_t escaped_len = CEscapedLength(src);
-  if (escaped_len == src.size()) {
-    dest->append(src.data(), src.size());
-    return;
-  }
-
-  size_t cur_dest_len = dest->size();
-  strings_internal::STLStringResizeUninitialized(dest,
-                                                 cur_dest_len + escaped_len);
-  char* append_ptr = &(*dest)[cur_dest_len];
-
-  for (unsigned char c : src) {
-    int char_len = c_escaped_len[c];
-    if (char_len == 1) {
-      *append_ptr++ = c;
-    } else if (char_len == 2) {
-      switch (c) {
-        case '\n':
-          *append_ptr++ = '\\';
-          *append_ptr++ = 'n';
-          break;
-        case '\r':
-          *append_ptr++ = '\\';
-          *append_ptr++ = 'r';
-          break;
-        case '\t':
-          *append_ptr++ = '\\';
-          *append_ptr++ = 't';
-          break;
-        case '\"':
-          *append_ptr++ = '\\';
-          *append_ptr++ = '\"';
-          break;
-        case '\'':
-          *append_ptr++ = '\\';
-          *append_ptr++ = '\'';
-          break;
-        case '\\':
-          *append_ptr++ = '\\';
-          *append_ptr++ = '\\';
-          break;
-      }
-    } else {
-      *append_ptr++ = '\\';
-      *append_ptr++ = '0' + c / 64;
-      *append_ptr++ = '0' + (c % 64) / 8;
-      *append_ptr++ = '0' + c % 8;
-    }
-  }
-}
-
-bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest,
-                            size_t szdest, const signed char* unbase64,
-                            size_t* len) {
-  static const char kPad64Equals = '=';
-  static const char kPad64Dot = '.';
-
-  size_t destidx = 0;
-  int decode = 0;
-  int state = 0;
-  unsigned int ch = 0;
-  unsigned int temp = 0;
-
-  // If "char" is signed by default, using *src as an array index results in
-  // accessing negative array elements. Treat the input as a pointer to
-  // unsigned char to avoid this.
-  const unsigned char* src = reinterpret_cast<const unsigned char*>(src_param);
-
-  // The GET_INPUT macro gets the next input character, skipping
-  // over any whitespace, and stopping when we reach the end of the
-  // string or when we read any non-data character.  The arguments are
-  // an arbitrary identifier (used as a label for goto) and the number
-  // of data bytes that must remain in the input to avoid aborting the
-  // loop.
-#define GET_INPUT(label, remain)                                \
-  label:                                                        \
-  --szsrc;                                                      \
-  ch = *src++;                                                  \
-  decode = unbase64[ch];                                        \
-  if (decode < 0) {                                             \
-    if (absl::ascii_isspace(ch) && szsrc >= remain) goto label; \
-    state = 4 - remain;                                         \
-    break;                                                      \
-  }
-
-  // if dest is null, we're just checking to see if it's legal input
-  // rather than producing output.  (I suspect this could just be done
-  // with a regexp...).  We duplicate the loop so this test can be
-  // outside it instead of in every iteration.
-
-  if (dest) {
-    // This loop consumes 4 input bytes and produces 3 output bytes
-    // per iteration.  We can't know at the start that there is enough
-    // data left in the string for a full iteration, so the loop may
-    // break out in the middle; if so 'state' will be set to the
-    // number of input bytes read.
-
-    while (szsrc >= 4) {
-      // We'll start by optimistically assuming that the next four
-      // bytes of the string (src[0..3]) are four good data bytes
-      // (that is, no nulls, whitespace, padding chars, or illegal
-      // chars).  We need to test src[0..2] for nulls individually
-      // before constructing temp to preserve the property that we
-      // never read past a null in the string (no matter how long
-      // szsrc claims the string is).
-
-      if (!src[0] || !src[1] || !src[2] ||
-          ((temp = ((unsigned(unbase64[src[0]]) << 18) |
-                    (unsigned(unbase64[src[1]]) << 12) |
-                    (unsigned(unbase64[src[2]]) << 6) |
-                    (unsigned(unbase64[src[3]])))) &
-           0x80000000)) {
-        // Iff any of those four characters was bad (null, illegal,
-        // whitespace, padding), then temp's high bit will be set
-        // (because unbase64[] is -1 for all bad characters).
-        //
-        // We'll back up and resort to the slower decoder, which knows
-        // how to handle those cases.
-
-        GET_INPUT(first, 4);
-        temp = decode;
-        GET_INPUT(second, 3);
-        temp = (temp << 6) | decode;
-        GET_INPUT(third, 2);
-        temp = (temp << 6) | decode;
-        GET_INPUT(fourth, 1);
-        temp = (temp << 6) | decode;
-      } else {
-        // We really did have four good data bytes, so advance four
-        // characters in the string.
-
-        szsrc -= 4;
-        src += 4;
-      }
-
-      // temp has 24 bits of input, so write that out as three bytes.
-
-      if (destidx + 3 > szdest) return false;
-      dest[destidx + 2] = temp;
-      temp >>= 8;
-      dest[destidx + 1] = temp;
-      temp >>= 8;
-      dest[destidx] = temp;
-      destidx += 3;
-    }
-  } else {
-    while (szsrc >= 4) {
-      if (!src[0] || !src[1] || !src[2] ||
-          ((temp = ((unsigned(unbase64[src[0]]) << 18) |
-                    (unsigned(unbase64[src[1]]) << 12) |
-                    (unsigned(unbase64[src[2]]) << 6) |
-                    (unsigned(unbase64[src[3]])))) &
-           0x80000000)) {
-        GET_INPUT(first_no_dest, 4);
-        GET_INPUT(second_no_dest, 3);
-        GET_INPUT(third_no_dest, 2);
-        GET_INPUT(fourth_no_dest, 1);
-      } else {
-        szsrc -= 4;
-        src += 4;
-      }
-      destidx += 3;
-    }
-  }
-
-#undef GET_INPUT
-
-  // if the loop terminated because we read a bad character, return
-  // now.
-  if (decode < 0 && ch != kPad64Equals && ch != kPad64Dot &&
-      !absl::ascii_isspace(ch))
-    return false;
-
-  if (ch == kPad64Equals || ch == kPad64Dot) {
-    // if we stopped by hitting an '=' or '.', un-read that character -- we'll
-    // look at it again when we count to check for the proper number of
-    // equals signs at the end.
-    ++szsrc;
-    --src;
-  } else {
-    // This loop consumes 1 input byte per iteration.  It's used to
-    // clean up the 0-3 input bytes remaining when the first, faster
-    // loop finishes.  'temp' contains the data from 'state' input
-    // characters read by the first loop.
-    while (szsrc > 0) {
-      --szsrc;
-      ch = *src++;
-      decode = unbase64[ch];
-      if (decode < 0) {
-        if (absl::ascii_isspace(ch)) {
-          continue;
-        } else if (ch == kPad64Equals || ch == kPad64Dot) {
-          // back up one character; we'll read it again when we check
-          // for the correct number of pad characters at the end.
-          ++szsrc;
-          --src;
-          break;
-        } else {
-          return false;
-        }
-      }
-
-      // Each input character gives us six bits of output.
-      temp = (temp << 6) | decode;
-      ++state;
-      if (state == 4) {
-        // If we've accumulated 24 bits of output, write that out as
-        // three bytes.
-        if (dest) {
-          if (destidx + 3 > szdest) return false;
-          dest[destidx + 2] = temp;
-          temp >>= 8;
-          dest[destidx + 1] = temp;
-          temp >>= 8;
-          dest[destidx] = temp;
-        }
-        destidx += 3;
-        state = 0;
-        temp = 0;
-      }
-    }
-  }
-
-  // Process the leftover data contained in 'temp' at the end of the input.
-  int expected_equals = 0;
-  switch (state) {
-    case 0:
-      // Nothing left over; output is a multiple of 3 bytes.
-      break;
-
-    case 1:
-      // Bad input; we have 6 bits left over.
-      return false;
-
-    case 2:
-      // Produce one more output byte from the 12 input bits we have left.
-      if (dest) {
-        if (destidx + 1 > szdest) return false;
-        temp >>= 4;
-        dest[destidx] = temp;
-      }
-      ++destidx;
-      expected_equals = 2;
-      break;
-
-    case 3:
-      // Produce two more output bytes from the 18 input bits we have left.
-      if (dest) {
-        if (destidx + 2 > szdest) return false;
-        temp >>= 2;
-        dest[destidx + 1] = temp;
-        temp >>= 8;
-        dest[destidx] = temp;
-      }
-      destidx += 2;
-      expected_equals = 1;
-      break;
-
-    default:
-      // state should have no other values at this point.
-      ABSL_RAW_LOG(FATAL, "This can't happen; base64 decoder state = %d",
-                   state);
-  }
-
-  // The remainder of the string should be all whitespace, mixed with
-  // exactly 0 equals signs, or exactly 'expected_equals' equals
-  // signs.  (Always accepting 0 equals signs is an Abseil extension
-  // not covered in the RFC, as is accepting dot as the pad character.)
-
-  int equals = 0;
-  while (szsrc > 0) {
-    if (*src == kPad64Equals || *src == kPad64Dot)
-      ++equals;
-    else if (!absl::ascii_isspace(*src))
-      return false;
-    --szsrc;
-    ++src;
-  }
-
-  const bool ok = (equals == 0 || equals == expected_equals);
-  if (ok) *len = destidx;
-  return ok;
-}
-
-// The arrays below were generated by the following code
-// #include <sys/time.h>
-// #include <stdlib.h>
-// #include <string.h>
-// main()
-// {
-//   static const char Base64[] =
-//     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-//   char* pos;
-//   int idx, i, j;
-//   printf("    ");
-//   for (i = 0; i < 255; i += 8) {
-//     for (j = i; j < i + 8; j++) {
-//       pos = strchr(Base64, j);
-//       if ((pos == nullptr) || (j == 0))
-//         idx = -1;
-//       else
-//         idx = pos - Base64;
-//       if (idx == -1)
-//         printf(" %2d,     ", idx);
-//       else
-//         printf(" %2d/*%c*/,", idx, j);
-//     }
-//     printf("\n    ");
-//   }
-// }
-//
-// where the value of "Base64[]" was replaced by one of the base-64 conversion
-// tables from the functions below.
-/* clang-format off */
-constexpr signed char kUnBase64[] = {
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      62/*+*/, -1,      -1,      -1,      63/*/ */,
-    52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
-    60/*8*/, 61/*9*/, -1,      -1,      -1,      -1,      -1,      -1,
-    -1,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
-    07/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
-    15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
-    23/*X*/, 24/*Y*/, 25/*Z*/, -1,      -1,      -1,      -1,      -1,
-    -1,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
-    33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
-    41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
-    49/*x*/, 50/*y*/, 51/*z*/, -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1
-};
-
-constexpr signed char kUnWebSafeBase64[] = {
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      62/*-*/, -1,      -1,
-    52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
-    60/*8*/, 61/*9*/, -1,      -1,      -1,      -1,      -1,      -1,
-    -1,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
-    07/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
-    15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
-    23/*X*/, 24/*Y*/, 25/*Z*/, -1,      -1,      -1,      -1,      63/*_*/,
-    -1,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
-    33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
-    41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
-    49/*x*/, 50/*y*/, 51/*z*/, -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,
-    -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1
-};
-/* clang-format on */
-
-constexpr char kWebSafeBase64Chars[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-
-template <typename String>
-bool Base64UnescapeInternal(const char* src, size_t slen, String* dest,
-                            const signed char* unbase64) {
-  // Determine the size of the output string.  Base64 encodes every 3 bytes into
-  // 4 characters.  any leftover chars are added directly for good measure.
-  // This is documented in the base64 RFC: http://tools.ietf.org/html/rfc3548
-  const size_t dest_len = 3 * (slen / 4) + (slen % 4);
-
-  strings_internal::STLStringResizeUninitialized(dest, dest_len);
-
-  // We are getting the destination buffer by getting the beginning of the
-  // string and converting it into a char *.
-  size_t len;
-  const bool ok =
-      Base64UnescapeInternal(src, slen, &(*dest)[0], dest_len, unbase64, &len);
-  if (!ok) {
-    dest->clear();
-    return false;
-  }
-
-  // could be shorter if there was padding
-  assert(len <= dest_len);
-  dest->erase(len);
-
-  return true;
-}
-
-/* clang-format off */
-constexpr char kHexValueLenient[256] = {
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  1,  2,  3,  4,  5,  6, 7, 8, 9, 0, 0, 0, 0, 0, 0,  // '0'..'9'
-    0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 'A'..'F'
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 'a'..'f'
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-
-/* clang-format on */
-
-// This is a templated function so that T can be either a char*
-// or a string.  This works because we use the [] operator to access
-// individual characters at a time.
-template <typename T>
-void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) {
-  for (int i = 0; i < num; i++) {
-    to[i] = (kHexValueLenient[from[i * 2] & 0xFF] << 4) +
-            (kHexValueLenient[from[i * 2 + 1] & 0xFF]);
-  }
-}
-
-// This is a templated function so that T can be either a char* or a
-// std::string.
-template <typename T>
-void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) {
-  auto dest_ptr = &dest[0];
-  for (auto src_ptr = src; src_ptr != (src + num); ++src_ptr, dest_ptr += 2) {
-    const char* hex_p = &numbers_internal::kHexTable[*src_ptr * 2];
-    std::copy(hex_p, hex_p + 2, dest_ptr);
-  }
-}
-
-}  // namespace
-
-// ----------------------------------------------------------------------
-// CUnescape()
-//
-// See CUnescapeInternal() for implementation details.
-// ----------------------------------------------------------------------
-bool CUnescape(absl::string_view source, std::string* dest,
-               std::string* error) {
-  return CUnescapeInternal(source, kUnescapeNulls, dest, error);
-}
-
-std::string CEscape(absl::string_view src) {
-  std::string dest;
-  CEscapeAndAppendInternal(src, &dest);
-  return dest;
-}
-
-std::string CHexEscape(absl::string_view src) {
-  return CEscapeInternal(src, true, false);
-}
-
-std::string Utf8SafeCEscape(absl::string_view src) {
-  return CEscapeInternal(src, false, true);
-}
-
-std::string Utf8SafeCHexEscape(absl::string_view src) {
-  return CEscapeInternal(src, true, true);
-}
-
-// ----------------------------------------------------------------------
-// Base64Unescape() - base64 decoder
-// Base64Escape() - base64 encoder
-// WebSafeBase64Unescape() - Google's variation of base64 decoder
-// WebSafeBase64Escape() - Google's variation of base64 encoder
-//
-// Check out
-// http://tools.ietf.org/html/rfc2045 for formal description, but what we
-// care about is that...
-//   Take the encoded stuff in groups of 4 characters and turn each
-//   character into a code 0 to 63 thus:
-//           A-Z map to 0 to 25
-//           a-z map to 26 to 51
-//           0-9 map to 52 to 61
-//           +(- for WebSafe) maps to 62
-//           /(_ for WebSafe) maps to 63
-//   There will be four numbers, all less than 64 which can be represented
-//   by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
-//   Arrange the 6 digit binary numbers into three bytes as such:
-//   aaaaaabb bbbbcccc ccdddddd
-//   Equals signs (one or two) are used at the end of the encoded block to
-//   indicate that the text was not an integer multiple of three bytes long.
-// ----------------------------------------------------------------------
-
-bool Base64Unescape(absl::string_view src, std::string* dest) {
-  return Base64UnescapeInternal(src.data(), src.size(), dest, kUnBase64);
-}
-
-bool WebSafeBase64Unescape(absl::string_view src, std::string* dest) {
-  return Base64UnescapeInternal(src.data(), src.size(), dest, kUnWebSafeBase64);
-}
-
-void Base64Escape(absl::string_view src, std::string* dest) {
-  strings_internal::Base64EscapeInternal(
-      reinterpret_cast<const unsigned char*>(src.data()), src.size(), dest,
-      true, strings_internal::kBase64Chars);
-}
-
-void WebSafeBase64Escape(absl::string_view src, std::string* dest) {
-  strings_internal::Base64EscapeInternal(
-      reinterpret_cast<const unsigned char*>(src.data()), src.size(), dest,
-      false, kWebSafeBase64Chars);
-}
-
-std::string Base64Escape(absl::string_view src) {
-  std::string dest;
-  strings_internal::Base64EscapeInternal(
-      reinterpret_cast<const unsigned char*>(src.data()), src.size(), &dest,
-      true, strings_internal::kBase64Chars);
-  return dest;
-}
-
-std::string WebSafeBase64Escape(absl::string_view src) {
-  std::string dest;
-  strings_internal::Base64EscapeInternal(
-      reinterpret_cast<const unsigned char*>(src.data()), src.size(), &dest,
-      false, kWebSafeBase64Chars);
-  return dest;
-}
-
-std::string HexStringToBytes(absl::string_view from) {
-  std::string result;
-  const auto num = from.size() / 2;
-  strings_internal::STLStringResizeUninitialized(&result, num);
-  absl::HexStringToBytesInternal<std::string&>(from.data(), result, num);
-  return result;
-}
-
-std::string BytesToHexString(absl::string_view from) {
-  std::string result;
-  strings_internal::STLStringResizeUninitialized(&result, 2 * from.size());
-  absl::BytesToHexStringInternal<std::string&>(
-      reinterpret_cast<const unsigned char*>(from.data()), result, from.size());
-  return result;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/escaping.h b/third_party/abseil_cpp/absl/strings/escaping.h
deleted file mode 100644
index f5ca26c5da..0000000000
--- a/third_party/abseil_cpp/absl/strings/escaping.h
+++ /dev/null
@@ -1,164 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: escaping.h
-// -----------------------------------------------------------------------------
-//
-// This header file contains string utilities involved in escaping and
-// unescaping strings in various ways.
-
-#ifndef ABSL_STRINGS_ESCAPING_H_
-#define ABSL_STRINGS_ESCAPING_H_
-
-#include <cstddef>
-#include <string>
-#include <vector>
-
-#include "absl/base/macros.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/str_join.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// CUnescape()
-//
-// Unescapes a `source` string and copies it into `dest`, rewriting C-style
-// escape sequences (https://en.cppreference.com/w/cpp/language/escape) into
-// their proper code point equivalents, returning `true` if successful.
-//
-// The following unescape sequences can be handled:
-//
-//   * ASCII escape sequences ('\n','\r','\\', etc.) to their ASCII equivalents
-//   * Octal escape sequences ('\nnn') to byte nnn. The unescaped value must
-//     resolve to a single byte or an error will occur. E.g. values greater than
-//     0xff will produce an error.
-//   * Hexadecimal escape sequences ('\xnn') to byte nn. While an arbitrary
-//     number of following digits are allowed, the unescaped value must resolve
-//     to a single byte or an error will occur. E.g. '\x0045' is equivalent to
-//     '\x45', but '\x1234' will produce an error.
-//   * Unicode escape sequences ('\unnnn' for exactly four hex digits or
-//     '\Unnnnnnnn' for exactly eight hex digits, which will be encoded in
-//     UTF-8. (E.g., `\u2019` unescapes to the three bytes 0xE2, 0x80, and
-//     0x99).
-//
-// If any errors are encountered, this function returns `false`, leaving the
-// `dest` output parameter in an unspecified state, and stores the first
-// encountered error in `error`. To disable error reporting, set `error` to
-// `nullptr` or use the overload with no error reporting below.
-//
-// Example:
-//
-//   std::string s = "foo\\rbar\\nbaz\\t";
-//   std::string unescaped_s;
-//   if (!absl::CUnescape(s, &unescaped_s) {
-//     ...
-//   }
-//   EXPECT_EQ(unescaped_s, "foo\rbar\nbaz\t");
-bool CUnescape(absl::string_view source, std::string* dest, std::string* error);
-
-// Overload of `CUnescape()` with no error reporting.
-inline bool CUnescape(absl::string_view source, std::string* dest) {
-  return CUnescape(source, dest, nullptr);
-}
-
-// CEscape()
-//
-// Escapes a 'src' string using C-style escapes sequences
-// (https://en.cppreference.com/w/cpp/language/escape), escaping other
-// non-printable/non-whitespace bytes as octal sequences (e.g. "\377").
-//
-// Example:
-//
-//   std::string s = "foo\rbar\tbaz\010\011\012\013\014\x0d\n";
-//   std::string escaped_s = absl::CEscape(s);
-//   EXPECT_EQ(escaped_s, "foo\\rbar\\tbaz\\010\\t\\n\\013\\014\\r\\n");
-std::string CEscape(absl::string_view src);
-
-// CHexEscape()
-//
-// Escapes a 'src' string using C-style escape sequences, escaping
-// other non-printable/non-whitespace bytes as hexadecimal sequences (e.g.
-// "\xFF").
-//
-// Example:
-//
-//   std::string s = "foo\rbar\tbaz\010\011\012\013\014\x0d\n";
-//   std::string escaped_s = absl::CHexEscape(s);
-//   EXPECT_EQ(escaped_s, "foo\\rbar\\tbaz\\x08\\t\\n\\x0b\\x0c\\r\\n");
-std::string CHexEscape(absl::string_view src);
-
-// Utf8SafeCEscape()
-//
-// Escapes a 'src' string using C-style escape sequences, escaping bytes as
-// octal sequences, and passing through UTF-8 characters without conversion.
-// I.e., when encountering any bytes with their high bit set, this function
-// will not escape those values, whether or not they are valid UTF-8.
-std::string Utf8SafeCEscape(absl::string_view src);
-
-// Utf8SafeCHexEscape()
-//
-// Escapes a 'src' string using C-style escape sequences, escaping bytes as
-// hexadecimal sequences, and passing through UTF-8 characters without
-// conversion.
-std::string Utf8SafeCHexEscape(absl::string_view src);
-
-// Base64Unescape()
-//
-// Converts a `src` string encoded in Base64 to its binary equivalent, writing
-// it to a `dest` buffer, returning `true` on success. If `src` contains invalid
-// characters, `dest` is cleared and returns `false`.
-bool Base64Unescape(absl::string_view src, std::string* dest);
-
-// WebSafeBase64Unescape()
-//
-// Converts a `src` string encoded in Base64 to its binary equivalent, writing
-// it to a `dest` buffer, but using '-' instead of '+', and '_' instead of '/'.
-// If `src` contains invalid characters, `dest` is cleared and returns `false`.
-bool WebSafeBase64Unescape(absl::string_view src, std::string* dest);
-
-// Base64Escape()
-//
-// Encodes a `src` string into a base64-encoded string, with padding characters.
-// This function conforms with RFC 4648 section 4 (base64).
-void Base64Escape(absl::string_view src, std::string* dest);
-std::string Base64Escape(absl::string_view src);
-
-// WebSafeBase64Escape()
-//
-// Encodes a `src` string into a base64-like string, using '-' instead of '+'
-// and '_' instead of '/', and without padding. This function conforms with RFC
-// 4648 section 5 (base64url).
-void WebSafeBase64Escape(absl::string_view src, std::string* dest);
-std::string WebSafeBase64Escape(absl::string_view src);
-
-// HexStringToBytes()
-//
-// Converts an ASCII hex string into bytes, returning binary data of length
-// `from.size()/2`.
-std::string HexStringToBytes(absl::string_view from);
-
-// BytesToHexString()
-//
-// Converts binary data into an ASCII text string, returning a string of size
-// `2*from.size()`.
-std::string BytesToHexString(absl::string_view from);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_ESCAPING_H_
diff --git a/third_party/abseil_cpp/absl/strings/escaping_benchmark.cc b/third_party/abseil_cpp/absl/strings/escaping_benchmark.cc
deleted file mode 100644
index 10d5b033c5..0000000000
--- a/third_party/abseil_cpp/absl/strings/escaping_benchmark.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/escaping.h"
-
-#include <cstdio>
-#include <cstring>
-#include <random>
-
-#include "benchmark/benchmark.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/internal/escaping_test_common.h"
-
-namespace {
-
-void BM_CUnescapeHexString(benchmark::State& state) {
-  std::string src;
-  for (int i = 0; i < 50; i++) {
-    src += "\\x55";
-  }
-  std::string dest;
-  for (auto _ : state) {
-    absl::CUnescape(src, &dest);
-  }
-}
-BENCHMARK(BM_CUnescapeHexString);
-
-void BM_WebSafeBase64Escape_string(benchmark::State& state) {
-  std::string raw;
-  for (int i = 0; i < 10; ++i) {
-    for (const auto& test_set : absl::strings_internal::base64_strings()) {
-      raw += std::string(test_set.plaintext);
-    }
-  }
-
-  // The actual benchmark loop is tiny...
-  std::string escaped;
-  for (auto _ : state) {
-    absl::WebSafeBase64Escape(raw, &escaped);
-  }
-
-  // We want to be sure the compiler doesn't throw away the loop above,
-  // and the easiest way to ensure that is to round-trip the results and verify
-  // them.
-  std::string round_trip;
-  absl::WebSafeBase64Unescape(escaped, &round_trip);
-  ABSL_RAW_CHECK(round_trip == raw, "");
-}
-BENCHMARK(BM_WebSafeBase64Escape_string);
-
-// Used for the CEscape benchmarks
-const char kStringValueNoEscape[] = "1234567890";
-const char kStringValueSomeEscaped[] = "123\n56789\xA1";
-const char kStringValueMostEscaped[] = "\xA1\xA2\ny\xA4\xA5\xA6z\b\r";
-
-void CEscapeBenchmarkHelper(benchmark::State& state, const char* string_value,
-                            int max_len) {
-  std::string src;
-  while (src.size() < max_len) {
-    absl::StrAppend(&src, string_value);
-  }
-
-  for (auto _ : state) {
-    absl::CEscape(src);
-  }
-}
-
-void BM_CEscape_NoEscape(benchmark::State& state) {
-  CEscapeBenchmarkHelper(state, kStringValueNoEscape, state.range(0));
-}
-BENCHMARK(BM_CEscape_NoEscape)->Range(1, 1 << 14);
-
-void BM_CEscape_SomeEscaped(benchmark::State& state) {
-  CEscapeBenchmarkHelper(state, kStringValueSomeEscaped, state.range(0));
-}
-BENCHMARK(BM_CEscape_SomeEscaped)->Range(1, 1 << 14);
-
-void BM_CEscape_MostEscaped(benchmark::State& state) {
-  CEscapeBenchmarkHelper(state, kStringValueMostEscaped, state.range(0));
-}
-BENCHMARK(BM_CEscape_MostEscaped)->Range(1, 1 << 14);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/escaping_test.cc b/third_party/abseil_cpp/absl/strings/escaping_test.cc
deleted file mode 100644
index 45671a0ed5..0000000000
--- a/third_party/abseil_cpp/absl/strings/escaping_test.cc
+++ /dev/null
@@ -1,664 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/escaping.h"
-
-#include <array>
-#include <cstdio>
-#include <cstring>
-#include <memory>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/container/fixed_array.h"
-#include "absl/strings/str_cat.h"
-
-#include "absl/strings/internal/escaping_test_common.h"
-
-namespace {
-
-struct epair {
-  std::string escaped;
-  std::string unescaped;
-};
-
-TEST(CEscape, EscapeAndUnescape) {
-  const std::string inputs[] = {
-      std::string("foo\nxx\r\b\0023"),
-      std::string(""),
-      std::string("abc"),
-      std::string("\1chad_rules"),
-      std::string("\1arnar_drools"),
-      std::string("xxxx\r\t'\"\\"),
-      std::string("\0xx\0", 4),
-      std::string("\x01\x31"),
-      std::string("abc\xb\x42\141bc"),
-      std::string("123\1\x31\x32\x33"),
-      std::string("\xc1\xca\x1b\x62\x19o\xcc\x04"),
-      std::string(
-          "\\\"\xe8\xb0\xb7\xe6\xad\x8c\\\" is Google\\\'s Chinese name"),
-  };
-  // Do this twice, once for octal escapes and once for hex escapes.
-  for (int kind = 0; kind < 4; kind++) {
-    for (const std::string& original : inputs) {
-      std::string escaped;
-      switch (kind) {
-        case 0:
-          escaped = absl::CEscape(original);
-          break;
-        case 1:
-          escaped = absl::CHexEscape(original);
-          break;
-        case 2:
-          escaped = absl::Utf8SafeCEscape(original);
-          break;
-        case 3:
-          escaped = absl::Utf8SafeCHexEscape(original);
-          break;
-      }
-      std::string unescaped_str;
-      EXPECT_TRUE(absl::CUnescape(escaped, &unescaped_str));
-      EXPECT_EQ(unescaped_str, original);
-
-      unescaped_str.erase();
-      std::string error;
-      EXPECT_TRUE(absl::CUnescape(escaped, &unescaped_str, &error));
-      EXPECT_EQ(error, "");
-
-      // Check in-place unescaping
-      std::string s = escaped;
-      EXPECT_TRUE(absl::CUnescape(s, &s));
-      ASSERT_EQ(s, original);
-    }
-  }
-  // Check that all possible two character strings can be escaped then
-  // unescaped successfully.
-  for (int char0 = 0; char0 < 256; char0++) {
-    for (int char1 = 0; char1 < 256; char1++) {
-      char chars[2];
-      chars[0] = char0;
-      chars[1] = char1;
-      std::string s(chars, 2);
-      std::string escaped = absl::CHexEscape(s);
-      std::string unescaped;
-      EXPECT_TRUE(absl::CUnescape(escaped, &unescaped));
-      EXPECT_EQ(s, unescaped);
-    }
-  }
-}
-
-TEST(CEscape, BasicEscaping) {
-  epair oct_values[] = {
-      {"foo\\rbar\\nbaz\\t", "foo\rbar\nbaz\t"},
-      {"\\'full of \\\"sound\\\" and \\\"fury\\\"\\'",
-       "'full of \"sound\" and \"fury\"'"},
-      {"signi\\\\fying\\\\ nothing\\\\", "signi\\fying\\ nothing\\"},
-      {"\\010\\t\\n\\013\\014\\r", "\010\011\012\013\014\015"}
-  };
-  epair hex_values[] = {
-      {"ubik\\rubik\\nubik\\t", "ubik\rubik\nubik\t"},
-      {"I\\\'ve just seen a \\\"face\\\"",
-       "I've just seen a \"face\""},
-      {"hel\\\\ter\\\\skel\\\\ter\\\\", "hel\\ter\\skel\\ter\\"},
-      {"\\x08\\t\\n\\x0b\\x0c\\r", "\010\011\012\013\014\015"}
-  };
-  epair utf8_oct_values[] = {
-      {"\xe8\xb0\xb7\xe6\xad\x8c\\r\xe8\xb0\xb7\xe6\xad\x8c\\nbaz\\t",
-       "\xe8\xb0\xb7\xe6\xad\x8c\r\xe8\xb0\xb7\xe6\xad\x8c\nbaz\t"},
-      {"\\\"\xe8\xb0\xb7\xe6\xad\x8c\\\" is Google\\\'s Chinese name",
-       "\"\xe8\xb0\xb7\xe6\xad\x8c\" is Google\'s Chinese name"},
-      {"\xe3\x83\xa1\xe3\x83\xbc\xe3\x83\xab\\\\are\\\\Japanese\\\\chars\\\\",
-       "\xe3\x83\xa1\xe3\x83\xbc\xe3\x83\xab\\are\\Japanese\\chars\\"},
-      {"\xed\x81\xac\xeb\xa1\xac\\010\\t\\n\\013\\014\\r",
-       "\xed\x81\xac\xeb\xa1\xac\010\011\012\013\014\015"}
-  };
-  epair utf8_hex_values[] = {
-      {"\x20\xe4\xbd\xa0\\t\xe5\xa5\xbd,\\r!\\n",
-       "\x20\xe4\xbd\xa0\t\xe5\xa5\xbd,\r!\n"},
-      {"\xe8\xa9\xa6\xe9\xa8\x93\\\' means \\\"test\\\"",
-       "\xe8\xa9\xa6\xe9\xa8\x93\' means \"test\""},
-      {"\\\\\xe6\x88\x91\\\\:\\\\\xe6\x9d\xa8\xe6\xac\xa2\\\\",
-       "\\\xe6\x88\x91\\:\\\xe6\x9d\xa8\xe6\xac\xa2\\"},
-      {"\xed\x81\xac\xeb\xa1\xac\\x08\\t\\n\\x0b\\x0c\\r",
-       "\xed\x81\xac\xeb\xa1\xac\010\011\012\013\014\015"}
-  };
-
-  for (const epair& val : oct_values) {
-    std::string escaped = absl::CEscape(val.unescaped);
-    EXPECT_EQ(escaped, val.escaped);
-  }
-  for (const epair& val : hex_values) {
-    std::string escaped = absl::CHexEscape(val.unescaped);
-    EXPECT_EQ(escaped, val.escaped);
-  }
-  for (const epair& val : utf8_oct_values) {
-    std::string escaped = absl::Utf8SafeCEscape(val.unescaped);
-    EXPECT_EQ(escaped, val.escaped);
-  }
-  for (const epair& val : utf8_hex_values) {
-    std::string escaped = absl::Utf8SafeCHexEscape(val.unescaped);
-    EXPECT_EQ(escaped, val.escaped);
-  }
-}
-
-TEST(Unescape, BasicFunction) {
-  epair tests[] =
-    {{"", ""},
-     {"\\u0030", "0"},
-     {"\\u00A3", "\xC2\xA3"},
-     {"\\u22FD", "\xE2\x8B\xBD"},
-     {"\\U00010000", "\xF0\x90\x80\x80"},
-     {"\\U0010FFFD", "\xF4\x8F\xBF\xBD"}};
-  for (const epair& val : tests) {
-    std::string out;
-    EXPECT_TRUE(absl::CUnescape(val.escaped, &out));
-    EXPECT_EQ(out, val.unescaped);
-  }
-  std::string bad[] = {"\\u1",         // too short
-                       "\\U1",         // too short
-                       "\\Uffffff",    // exceeds 0x10ffff (largest Unicode)
-                       "\\U00110000",  // exceeds 0x10ffff (largest Unicode)
-                       "\\uD835",      // surrogate character (D800-DFFF)
-                       "\\U0000DD04",  // surrogate character (D800-DFFF)
-                       "\\777",        // exceeds 0xff
-                       "\\xABCD"};     // exceeds 0xff
-  for (const std::string& e : bad) {
-    std::string error;
-    std::string out;
-    EXPECT_FALSE(absl::CUnescape(e, &out, &error));
-    EXPECT_FALSE(error.empty());
-
-    out.erase();
-    EXPECT_FALSE(absl::CUnescape(e, &out));
-  }
-}
-
-class CUnescapeTest : public testing::Test {
- protected:
-  static const char kStringWithMultipleOctalNulls[];
-  static const char kStringWithMultipleHexNulls[];
-  static const char kStringWithMultipleUnicodeNulls[];
-
-  std::string result_string_;
-};
-
-const char CUnescapeTest::kStringWithMultipleOctalNulls[] =
-    "\\0\\n"    // null escape \0 plus newline
-    "0\\n"      // just a number 0 (not a null escape) plus newline
-    "\\00\\12"  // null escape \00 plus octal newline code
-    "\\000";    // null escape \000
-
-// This has the same ingredients as kStringWithMultipleOctalNulls
-// but with \x hex escapes instead of octal escapes.
-const char CUnescapeTest::kStringWithMultipleHexNulls[] =
-    "\\x0\\n"
-    "0\\n"
-    "\\x00\\xa"
-    "\\x000";
-
-const char CUnescapeTest::kStringWithMultipleUnicodeNulls[] =
-    "\\u0000\\n"    // short-form (4-digit) null escape plus newline
-    "0\\n"          // just a number 0 (not a null escape) plus newline
-    "\\U00000000";  // long-form (8-digit) null escape
-
-TEST_F(CUnescapeTest, Unescapes1CharOctalNull) {
-  std::string original_string = "\\0";
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0", 1), result_string_);
-}
-
-TEST_F(CUnescapeTest, Unescapes2CharOctalNull) {
-  std::string original_string = "\\00";
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0", 1), result_string_);
-}
-
-TEST_F(CUnescapeTest, Unescapes3CharOctalNull) {
-  std::string original_string = "\\000";
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0", 1), result_string_);
-}
-
-TEST_F(CUnescapeTest, Unescapes1CharHexNull) {
-  std::string original_string = "\\x0";
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0", 1), result_string_);
-}
-
-TEST_F(CUnescapeTest, Unescapes2CharHexNull) {
-  std::string original_string = "\\x00";
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0", 1), result_string_);
-}
-
-TEST_F(CUnescapeTest, Unescapes3CharHexNull) {
-  std::string original_string = "\\x000";
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0", 1), result_string_);
-}
-
-TEST_F(CUnescapeTest, Unescapes4CharUnicodeNull) {
-  std::string original_string = "\\u0000";
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0", 1), result_string_);
-}
-
-TEST_F(CUnescapeTest, Unescapes8CharUnicodeNull) {
-  std::string original_string = "\\U00000000";
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0", 1), result_string_);
-}
-
-TEST_F(CUnescapeTest, UnescapesMultipleOctalNulls) {
-  std::string original_string(kStringWithMultipleOctalNulls);
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  // All escapes, including newlines and null escapes, should have been
-  // converted to the equivalent characters.
-  EXPECT_EQ(std::string("\0\n"
-                        "0\n"
-                        "\0\n"
-                        "\0",
-                        7),
-            result_string_);
-}
-
-
-TEST_F(CUnescapeTest, UnescapesMultipleHexNulls) {
-  std::string original_string(kStringWithMultipleHexNulls);
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0\n"
-                        "0\n"
-                        "\0\n"
-                        "\0",
-                        7),
-            result_string_);
-}
-
-TEST_F(CUnescapeTest, UnescapesMultipleUnicodeNulls) {
-  std::string original_string(kStringWithMultipleUnicodeNulls);
-  EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
-  EXPECT_EQ(std::string("\0\n"
-                        "0\n"
-                        "\0",
-                        5),
-            result_string_);
-}
-
-static struct {
-  absl::string_view plaintext;
-  absl::string_view cyphertext;
-} const base64_tests[] = {
-    // Empty string.
-    {{"", 0}, {"", 0}},
-    {{nullptr, 0},
-     {"", 0}},  // if length is zero, plaintext ptr must be ignored!
-
-    // Basic bit patterns;
-    // values obtained with "echo -n '...' | uuencode -m test"
-
-    {{"\000", 1}, "AA=="},
-    {{"\001", 1}, "AQ=="},
-    {{"\002", 1}, "Ag=="},
-    {{"\004", 1}, "BA=="},
-    {{"\010", 1}, "CA=="},
-    {{"\020", 1}, "EA=="},
-    {{"\040", 1}, "IA=="},
-    {{"\100", 1}, "QA=="},
-    {{"\200", 1}, "gA=="},
-
-    {{"\377", 1}, "/w=="},
-    {{"\376", 1}, "/g=="},
-    {{"\375", 1}, "/Q=="},
-    {{"\373", 1}, "+w=="},
-    {{"\367", 1}, "9w=="},
-    {{"\357", 1}, "7w=="},
-    {{"\337", 1}, "3w=="},
-    {{"\277", 1}, "vw=="},
-    {{"\177", 1}, "fw=="},
-    {{"\000\000", 2}, "AAA="},
-    {{"\000\001", 2}, "AAE="},
-    {{"\000\002", 2}, "AAI="},
-    {{"\000\004", 2}, "AAQ="},
-    {{"\000\010", 2}, "AAg="},
-    {{"\000\020", 2}, "ABA="},
-    {{"\000\040", 2}, "ACA="},
-    {{"\000\100", 2}, "AEA="},
-    {{"\000\200", 2}, "AIA="},
-    {{"\001\000", 2}, "AQA="},
-    {{"\002\000", 2}, "AgA="},
-    {{"\004\000", 2}, "BAA="},
-    {{"\010\000", 2}, "CAA="},
-    {{"\020\000", 2}, "EAA="},
-    {{"\040\000", 2}, "IAA="},
-    {{"\100\000", 2}, "QAA="},
-    {{"\200\000", 2}, "gAA="},
-
-    {{"\377\377", 2}, "//8="},
-    {{"\377\376", 2}, "//4="},
-    {{"\377\375", 2}, "//0="},
-    {{"\377\373", 2}, "//s="},
-    {{"\377\367", 2}, "//c="},
-    {{"\377\357", 2}, "/+8="},
-    {{"\377\337", 2}, "/98="},
-    {{"\377\277", 2}, "/78="},
-    {{"\377\177", 2}, "/38="},
-    {{"\376\377", 2}, "/v8="},
-    {{"\375\377", 2}, "/f8="},
-    {{"\373\377", 2}, "+/8="},
-    {{"\367\377", 2}, "9/8="},
-    {{"\357\377", 2}, "7/8="},
-    {{"\337\377", 2}, "3/8="},
-    {{"\277\377", 2}, "v/8="},
-    {{"\177\377", 2}, "f/8="},
-
-    {{"\000\000\000", 3}, "AAAA"},
-    {{"\000\000\001", 3}, "AAAB"},
-    {{"\000\000\002", 3}, "AAAC"},
-    {{"\000\000\004", 3}, "AAAE"},
-    {{"\000\000\010", 3}, "AAAI"},
-    {{"\000\000\020", 3}, "AAAQ"},
-    {{"\000\000\040", 3}, "AAAg"},
-    {{"\000\000\100", 3}, "AABA"},
-    {{"\000\000\200", 3}, "AACA"},
-    {{"\000\001\000", 3}, "AAEA"},
-    {{"\000\002\000", 3}, "AAIA"},
-    {{"\000\004\000", 3}, "AAQA"},
-    {{"\000\010\000", 3}, "AAgA"},
-    {{"\000\020\000", 3}, "ABAA"},
-    {{"\000\040\000", 3}, "ACAA"},
-    {{"\000\100\000", 3}, "AEAA"},
-    {{"\000\200\000", 3}, "AIAA"},
-    {{"\001\000\000", 3}, "AQAA"},
-    {{"\002\000\000", 3}, "AgAA"},
-    {{"\004\000\000", 3}, "BAAA"},
-    {{"\010\000\000", 3}, "CAAA"},
-    {{"\020\000\000", 3}, "EAAA"},
-    {{"\040\000\000", 3}, "IAAA"},
-    {{"\100\000\000", 3}, "QAAA"},
-    {{"\200\000\000", 3}, "gAAA"},
-
-    {{"\377\377\377", 3}, "////"},
-    {{"\377\377\376", 3}, "///+"},
-    {{"\377\377\375", 3}, "///9"},
-    {{"\377\377\373", 3}, "///7"},
-    {{"\377\377\367", 3}, "///3"},
-    {{"\377\377\357", 3}, "///v"},
-    {{"\377\377\337", 3}, "///f"},
-    {{"\377\377\277", 3}, "//+/"},
-    {{"\377\377\177", 3}, "//9/"},
-    {{"\377\376\377", 3}, "//7/"},
-    {{"\377\375\377", 3}, "//3/"},
-    {{"\377\373\377", 3}, "//v/"},
-    {{"\377\367\377", 3}, "//f/"},
-    {{"\377\357\377", 3}, "/+//"},
-    {{"\377\337\377", 3}, "/9//"},
-    {{"\377\277\377", 3}, "/7//"},
-    {{"\377\177\377", 3}, "/3//"},
-    {{"\376\377\377", 3}, "/v//"},
-    {{"\375\377\377", 3}, "/f//"},
-    {{"\373\377\377", 3}, "+///"},
-    {{"\367\377\377", 3}, "9///"},
-    {{"\357\377\377", 3}, "7///"},
-    {{"\337\377\377", 3}, "3///"},
-    {{"\277\377\377", 3}, "v///"},
-    {{"\177\377\377", 3}, "f///"},
-
-    // Random numbers: values obtained with
-    //
-    //  #! /bin/bash
-    //  dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random
-    //  od -N $1 -t o1 /tmp/bar.random
-    //  uuencode -m test < /tmp/bar.random
-    //
-    // where $1 is the number of bytes (2, 3)
-
-    {{"\243\361", 2}, "o/E="},
-    {{"\024\167", 2}, "FHc="},
-    {{"\313\252", 2}, "y6o="},
-    {{"\046\041", 2}, "JiE="},
-    {{"\145\236", 2}, "ZZ4="},
-    {{"\254\325", 2}, "rNU="},
-    {{"\061\330", 2}, "Mdg="},
-    {{"\245\032", 2}, "pRo="},
-    {{"\006\000", 2}, "BgA="},
-    {{"\375\131", 2}, "/Vk="},
-    {{"\303\210", 2}, "w4g="},
-    {{"\040\037", 2}, "IB8="},
-    {{"\261\372", 2}, "sfo="},
-    {{"\335\014", 2}, "3Qw="},
-    {{"\233\217", 2}, "m48="},
-    {{"\373\056", 2}, "+y4="},
-    {{"\247\232", 2}, "p5o="},
-    {{"\107\053", 2}, "Rys="},
-    {{"\204\077", 2}, "hD8="},
-    {{"\276\211", 2}, "vok="},
-    {{"\313\110", 2}, "y0g="},
-    {{"\363\376", 2}, "8/4="},
-    {{"\251\234", 2}, "qZw="},
-    {{"\103\262", 2}, "Q7I="},
-    {{"\142\312", 2}, "Yso="},
-    {{"\067\211", 2}, "N4k="},
-    {{"\220\001", 2}, "kAE="},
-    {{"\152\240", 2}, "aqA="},
-    {{"\367\061", 2}, "9zE="},
-    {{"\133\255", 2}, "W60="},
-    {{"\176\035", 2}, "fh0="},
-    {{"\032\231", 2}, "Gpk="},
-
-    {{"\013\007\144", 3}, "Cwdk"},
-    {{"\030\112\106", 3}, "GEpG"},
-    {{"\047\325\046", 3}, "J9Um"},
-    {{"\310\160\022", 3}, "yHAS"},
-    {{"\131\100\237", 3}, "WUCf"},
-    {{"\064\342\134", 3}, "NOJc"},
-    {{"\010\177\004", 3}, "CH8E"},
-    {{"\345\147\205", 3}, "5WeF"},
-    {{"\300\343\360", 3}, "wOPw"},
-    {{"\061\240\201", 3}, "MaCB"},
-    {{"\225\333\044", 3}, "ldsk"},
-    {{"\215\137\352", 3}, "jV/q"},
-    {{"\371\147\160", 3}, "+Wdw"},
-    {{"\030\320\051", 3}, "GNAp"},
-    {{"\044\174\241", 3}, "JHyh"},
-    {{"\260\127\037", 3}, "sFcf"},
-    {{"\111\045\033", 3}, "SSUb"},
-    {{"\202\114\107", 3}, "gkxH"},
-    {{"\057\371\042", 3}, "L/ki"},
-    {{"\223\247\244", 3}, "k6ek"},
-    {{"\047\216\144", 3}, "J45k"},
-    {{"\203\070\327", 3}, "gzjX"},
-    {{"\247\140\072", 3}, "p2A6"},
-    {{"\124\115\116", 3}, "VE1O"},
-    {{"\157\162\050", 3}, "b3Io"},
-    {{"\357\223\004", 3}, "75ME"},
-    {{"\052\117\156", 3}, "Kk9u"},
-    {{"\347\154\000", 3}, "52wA"},
-    {{"\303\012\142", 3}, "wwpi"},
-    {{"\060\035\362", 3}, "MB3y"},
-    {{"\130\226\361", 3}, "WJbx"},
-    {{"\173\013\071", 3}, "ews5"},
-    {{"\336\004\027", 3}, "3gQX"},
-    {{"\357\366\234", 3}, "7/ac"},
-    {{"\353\304\111", 3}, "68RJ"},
-    {{"\024\264\131", 3}, "FLRZ"},
-    {{"\075\114\251", 3}, "PUyp"},
-    {{"\315\031\225", 3}, "zRmV"},
-    {{"\154\201\276", 3}, "bIG+"},
-    {{"\200\066\072", 3}, "gDY6"},
-    {{"\142\350\267", 3}, "Yui3"},
-    {{"\033\000\166", 3}, "GwB2"},
-    {{"\210\055\077", 3}, "iC0/"},
-    {{"\341\037\124", 3}, "4R9U"},
-    {{"\161\103\152", 3}, "cUNq"},
-    {{"\270\142\131", 3}, "uGJZ"},
-    {{"\337\076\074", 3}, "3z48"},
-    {{"\375\106\362", 3}, "/Uby"},
-    {{"\227\301\127", 3}, "l8FX"},
-    {{"\340\002\234", 3}, "4AKc"},
-    {{"\121\064\033", 3}, "UTQb"},
-    {{"\157\134\143", 3}, "b1xj"},
-    {{"\247\055\327", 3}, "py3X"},
-    {{"\340\142\005", 3}, "4GIF"},
-    {{"\060\260\143", 3}, "MLBj"},
-    {{"\075\203\170", 3}, "PYN4"},
-    {{"\143\160\016", 3}, "Y3AO"},
-    {{"\313\013\063", 3}, "ywsz"},
-    {{"\174\236\135", 3}, "fJ5d"},
-    {{"\103\047\026", 3}, "QycW"},
-    {{"\365\005\343", 3}, "9QXj"},
-    {{"\271\160\223", 3}, "uXCT"},
-    {{"\362\255\172", 3}, "8q16"},
-    {{"\113\012\015", 3}, "SwoN"},
-
-    // various lengths, generated by this python script:
-    //
-    // from std::string import lowercase as lc
-    // for i in range(27):
-    //   print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i),
-    //                                     lc[:i].encode('base64').strip())
-
-    {{"", 0}, {"", 0}},
-    {"a", "YQ=="},
-    {"ab", "YWI="},
-    {"abc", "YWJj"},
-    {"abcd", "YWJjZA=="},
-    {"abcde", "YWJjZGU="},
-    {"abcdef", "YWJjZGVm"},
-    {"abcdefg", "YWJjZGVmZw=="},
-    {"abcdefgh", "YWJjZGVmZ2g="},
-    {"abcdefghi", "YWJjZGVmZ2hp"},
-    {"abcdefghij", "YWJjZGVmZ2hpag=="},
-    {"abcdefghijk", "YWJjZGVmZ2hpams="},
-    {"abcdefghijkl", "YWJjZGVmZ2hpamts"},
-    {"abcdefghijklm", "YWJjZGVmZ2hpamtsbQ=="},
-    {"abcdefghijklmn", "YWJjZGVmZ2hpamtsbW4="},
-    {"abcdefghijklmno", "YWJjZGVmZ2hpamtsbW5v"},
-    {"abcdefghijklmnop", "YWJjZGVmZ2hpamtsbW5vcA=="},
-    {"abcdefghijklmnopq", "YWJjZGVmZ2hpamtsbW5vcHE="},
-    {"abcdefghijklmnopqr", "YWJjZGVmZ2hpamtsbW5vcHFy"},
-    {"abcdefghijklmnopqrs", "YWJjZGVmZ2hpamtsbW5vcHFycw=="},
-    {"abcdefghijklmnopqrst", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q="},
-    {"abcdefghijklmnopqrstu", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1"},
-    {"abcdefghijklmnopqrstuv", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg=="},
-    {"abcdefghijklmnopqrstuvw", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc="},
-    {"abcdefghijklmnopqrstuvwx", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4"},
-    {"abcdefghijklmnopqrstuvwxy", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ=="},
-    {"abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="},
-};
-
-template <typename StringType>
-void TestEscapeAndUnescape() {
-  // Check the short strings; this tests the math (and boundaries)
-  for (const auto& tc : base64_tests) {
-    StringType encoded("this junk should be ignored");
-    absl::Base64Escape(tc.plaintext, &encoded);
-    EXPECT_EQ(encoded, tc.cyphertext);
-    EXPECT_EQ(absl::Base64Escape(tc.plaintext), tc.cyphertext);
-
-    StringType decoded("this junk should be ignored");
-    EXPECT_TRUE(absl::Base64Unescape(encoded, &decoded));
-    EXPECT_EQ(decoded, tc.plaintext);
-
-    StringType websafe(tc.cyphertext);
-    for (int c = 0; c < websafe.size(); ++c) {
-      if ('+' == websafe[c]) websafe[c] = '-';
-      if ('/' == websafe[c]) websafe[c] = '_';
-      if ('=' == websafe[c]) {
-        websafe.resize(c);
-        break;
-      }
-    }
-
-    encoded = "this junk should be ignored";
-    absl::WebSafeBase64Escape(tc.plaintext, &encoded);
-    EXPECT_EQ(encoded, websafe);
-    EXPECT_EQ(absl::WebSafeBase64Escape(tc.plaintext), websafe);
-
-    // Let's try the string version of the decoder
-    decoded = "this junk should be ignored";
-    EXPECT_TRUE(absl::WebSafeBase64Unescape(websafe, &decoded));
-    EXPECT_EQ(decoded, tc.plaintext);
-  }
-
-  // Now try the long strings, this tests the streaming
-  for (const auto& tc : absl::strings_internal::base64_strings()) {
-    StringType buffer;
-    absl::WebSafeBase64Escape(tc.plaintext, &buffer);
-    EXPECT_EQ(tc.cyphertext, buffer);
-    EXPECT_EQ(absl::WebSafeBase64Escape(tc.plaintext), tc.cyphertext);
-  }
-
-  // Verify the behavior when decoding bad data
-  {
-    absl::string_view data_set[] = {"ab-/", absl::string_view("\0bcd", 4),
-                                    absl::string_view("abc.\0", 5)};
-    for (absl::string_view bad_data : data_set) {
-      StringType buf;
-      EXPECT_FALSE(absl::Base64Unescape(bad_data, &buf));
-      EXPECT_FALSE(absl::WebSafeBase64Unescape(bad_data, &buf));
-      EXPECT_TRUE(buf.empty());
-    }
-  }
-}
-
-TEST(Base64, EscapeAndUnescape) {
-  TestEscapeAndUnescape<std::string>();
-}
-
-TEST(Base64, DISABLED_HugeData) {
-  const size_t kSize = size_t(3) * 1000 * 1000 * 1000;
-  static_assert(kSize % 3 == 0, "kSize must be divisible by 3");
-  const std::string huge(kSize, 'x');
-
-  std::string escaped;
-  absl::Base64Escape(huge, &escaped);
-
-  // Generates the string that should match a base64 encoded "xxx..." string.
-  // "xxx" in base64 is "eHh4".
-  std::string expected_encoding;
-  expected_encoding.reserve(kSize / 3 * 4);
-  for (size_t i = 0; i < kSize / 3; ++i) {
-    expected_encoding.append("eHh4");
-  }
-  EXPECT_EQ(expected_encoding, escaped);
-
-  std::string unescaped;
-  EXPECT_TRUE(absl::Base64Unescape(escaped, &unescaped));
-  EXPECT_EQ(huge, unescaped);
-}
-
-TEST(HexAndBack, HexStringToBytes_and_BytesToHexString) {
-  std::string hex_mixed = "0123456789abcdefABCDEF";
-  std::string bytes_expected = "\x01\x23\x45\x67\x89\xab\xcd\xef\xAB\xCD\xEF";
-  std::string hex_only_lower = "0123456789abcdefabcdef";
-
-  std::string bytes_result = absl::HexStringToBytes(hex_mixed);
-  EXPECT_EQ(bytes_expected, bytes_result);
-
-  std::string prefix_valid = hex_mixed + "?";
-  std::string prefix_valid_result = absl::HexStringToBytes(
-      absl::string_view(prefix_valid.data(), prefix_valid.size() - 1));
-  EXPECT_EQ(bytes_expected, prefix_valid_result);
-
-  std::string infix_valid = "?" + hex_mixed + "???";
-  std::string infix_valid_result = absl::HexStringToBytes(
-      absl::string_view(infix_valid.data() + 1, hex_mixed.size()));
-  EXPECT_EQ(bytes_expected, infix_valid_result);
-
-  std::string hex_result = absl::BytesToHexString(bytes_expected);
-  EXPECT_EQ(hex_only_lower, hex_result);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/char_map.h b/third_party/abseil_cpp/absl/strings/internal/char_map.h
deleted file mode 100644
index 61484de0b7..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/char_map.h
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Character Map Class
-//
-// A fast, bit-vector map for 8-bit unsigned characters.
-// This class is useful for non-character purposes as well.
-
-#ifndef ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
-#define ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-class Charmap {
- public:
-  constexpr Charmap() : m_() {}
-
-  // Initializes with a given char*.  Note that NUL is not treated as
-  // a terminator, but rather a char to be flicked.
-  Charmap(const char* str, int len) : m_() {
-    while (len--) SetChar(*str++);
-  }
-
-  // Initializes with a given char*.  NUL is treated as a terminator
-  // and will not be in the charmap.
-  explicit Charmap(const char* str) : m_() {
-    while (*str) SetChar(*str++);
-  }
-
-  constexpr bool contains(unsigned char c) const {
-    return (m_[c / 64] >> (c % 64)) & 0x1;
-  }
-
-  // Returns true if and only if a character exists in both maps.
-  bool IntersectsWith(const Charmap& c) const {
-    for (size_t i = 0; i < ABSL_ARRAYSIZE(m_); ++i) {
-      if ((m_[i] & c.m_[i]) != 0) return true;
-    }
-    return false;
-  }
-
-  bool IsZero() const {
-    for (uint64_t c : m_) {
-      if (c != 0) return false;
-    }
-    return true;
-  }
-
-  // Containing only a single specified char.
-  static constexpr Charmap Char(char x) {
-    return Charmap(CharMaskForWord(x, 0), CharMaskForWord(x, 1),
-                   CharMaskForWord(x, 2), CharMaskForWord(x, 3));
-  }
-
-  // Containing all the chars in the C-string 's'.
-  // Note that this is expensively recursive because of the C++11 constexpr
-  // formulation. Use only in constexpr initializers.
-  static constexpr Charmap FromString(const char* s) {
-    return *s == 0 ? Charmap() : (Char(*s) | FromString(s + 1));
-  }
-
-  // Containing all the chars in the closed interval [lo,hi].
-  static constexpr Charmap Range(char lo, char hi) {
-    return Charmap(RangeForWord(lo, hi, 0), RangeForWord(lo, hi, 1),
-                   RangeForWord(lo, hi, 2), RangeForWord(lo, hi, 3));
-  }
-
-  friend constexpr Charmap operator&(const Charmap& a, const Charmap& b) {
-    return Charmap(a.m_[0] & b.m_[0], a.m_[1] & b.m_[1], a.m_[2] & b.m_[2],
-                   a.m_[3] & b.m_[3]);
-  }
-
-  friend constexpr Charmap operator|(const Charmap& a, const Charmap& b) {
-    return Charmap(a.m_[0] | b.m_[0], a.m_[1] | b.m_[1], a.m_[2] | b.m_[2],
-                   a.m_[3] | b.m_[3]);
-  }
-
-  friend constexpr Charmap operator~(const Charmap& a) {
-    return Charmap(~a.m_[0], ~a.m_[1], ~a.m_[2], ~a.m_[3]);
-  }
-
- private:
-  constexpr Charmap(uint64_t b0, uint64_t b1, uint64_t b2, uint64_t b3)
-      : m_{b0, b1, b2, b3} {}
-
-  static constexpr uint64_t RangeForWord(unsigned char lo, unsigned char hi,
-                                         uint64_t word) {
-    return OpenRangeFromZeroForWord(hi + 1, word) &
-           ~OpenRangeFromZeroForWord(lo, word);
-  }
-
-  // All the chars in the specified word of the range [0, upper).
-  static constexpr uint64_t OpenRangeFromZeroForWord(uint64_t upper,
-                                                     uint64_t word) {
-    return (upper <= 64 * word)
-               ? 0
-               : (upper >= 64 * (word + 1))
-                     ? ~static_cast<uint64_t>(0)
-                     : (~static_cast<uint64_t>(0) >> (64 - upper % 64));
-  }
-
-  static constexpr uint64_t CharMaskForWord(unsigned char x, uint64_t word) {
-    return (x / 64 == word) ? (static_cast<uint64_t>(1) << (x % 64)) : 0;
-  }
-
- private:
-  void SetChar(unsigned char c) {
-    m_[c / 64] |= static_cast<uint64_t>(1) << (c % 64);
-  }
-
-  uint64_t m_[4];
-};
-
-// Mirror the char-classifying predicates in <cctype>
-constexpr Charmap UpperCharmap() { return Charmap::Range('A', 'Z'); }
-constexpr Charmap LowerCharmap() { return Charmap::Range('a', 'z'); }
-constexpr Charmap DigitCharmap() { return Charmap::Range('0', '9'); }
-constexpr Charmap AlphaCharmap() { return LowerCharmap() | UpperCharmap(); }
-constexpr Charmap AlnumCharmap() { return DigitCharmap() | AlphaCharmap(); }
-constexpr Charmap XDigitCharmap() {
-  return DigitCharmap() | Charmap::Range('A', 'F') | Charmap::Range('a', 'f');
-}
-constexpr Charmap PrintCharmap() { return Charmap::Range(0x20, 0x7e); }
-constexpr Charmap SpaceCharmap() { return Charmap::FromString("\t\n\v\f\r "); }
-constexpr Charmap CntrlCharmap() {
-  return Charmap::Range(0, 0x7f) & ~PrintCharmap();
-}
-constexpr Charmap BlankCharmap() { return Charmap::FromString("\t "); }
-constexpr Charmap GraphCharmap() { return PrintCharmap() & ~SpaceCharmap(); }
-constexpr Charmap PunctCharmap() { return GraphCharmap() & ~AlnumCharmap(); }
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/char_map_benchmark.cc b/third_party/abseil_cpp/absl/strings/internal/char_map_benchmark.cc
deleted file mode 100644
index 5cef967b30..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/char_map_benchmark.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/char_map.h"
-
-#include <cstdint>
-
-#include "benchmark/benchmark.h"
-
-namespace {
-
-absl::strings_internal::Charmap MakeBenchmarkMap() {
-  absl::strings_internal::Charmap m;
-  uint32_t x[] = {0x0, 0x1, 0x2, 0x3, 0xf, 0xe, 0xd, 0xc};
-  for (uint32_t& t : x) t *= static_cast<uint32_t>(0x11111111UL);
-  for (uint32_t i = 0; i < 256; ++i) {
-    if ((x[i / 32] >> (i % 32)) & 1)
-      m = m | absl::strings_internal::Charmap::Char(i);
-  }
-  return m;
-}
-
-// Micro-benchmark for Charmap::contains.
-void BM_Contains(benchmark::State& state) {
-  // Loop-body replicated 10 times to increase time per iteration.
-  // Argument continuously changed to avoid generating common subexpressions.
-  const absl::strings_internal::Charmap benchmark_map = MakeBenchmarkMap();
-  unsigned char c = 0;
-  int ops = 0;
-  for (auto _ : state) {
-    ops += benchmark_map.contains(c++);
-    ops += benchmark_map.contains(c++);
-    ops += benchmark_map.contains(c++);
-    ops += benchmark_map.contains(c++);
-    ops += benchmark_map.contains(c++);
-    ops += benchmark_map.contains(c++);
-    ops += benchmark_map.contains(c++);
-    ops += benchmark_map.contains(c++);
-    ops += benchmark_map.contains(c++);
-    ops += benchmark_map.contains(c++);
-  }
-  benchmark::DoNotOptimize(ops);
-}
-BENCHMARK(BM_Contains);
-
-// We don't bother benchmarking Charmap::IsZero or Charmap::IntersectsWith;
-// their running time is data-dependent and it is not worth characterizing
-// "typical" data.
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/char_map_test.cc b/third_party/abseil_cpp/absl/strings/internal/char_map_test.cc
deleted file mode 100644
index d3306241a4..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/char_map_test.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/char_map.h"
-
-#include <cctype>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-constexpr absl::strings_internal::Charmap everything_map =
-    ~absl::strings_internal::Charmap();
-constexpr absl::strings_internal::Charmap nothing_map{};
-
-TEST(Charmap, AllTests) {
-  const absl::strings_internal::Charmap also_nothing_map("", 0);
-  ASSERT_TRUE(everything_map.contains('\0'));
-  ASSERT_TRUE(!nothing_map.contains('\0'));
-  ASSERT_TRUE(!also_nothing_map.contains('\0'));
-  for (unsigned char ch = 1; ch != 0; ++ch) {
-    ASSERT_TRUE(everything_map.contains(ch));
-    ASSERT_TRUE(!nothing_map.contains(ch));
-    ASSERT_TRUE(!also_nothing_map.contains(ch));
-  }
-
-  const absl::strings_internal::Charmap symbols("&@#@^!@?", 5);
-  ASSERT_TRUE(symbols.contains('&'));
-  ASSERT_TRUE(symbols.contains('@'));
-  ASSERT_TRUE(symbols.contains('#'));
-  ASSERT_TRUE(symbols.contains('^'));
-  ASSERT_TRUE(!symbols.contains('!'));
-  ASSERT_TRUE(!symbols.contains('?'));
-  int cnt = 0;
-  for (unsigned char ch = 1; ch != 0; ++ch)
-    cnt += symbols.contains(ch);
-  ASSERT_EQ(cnt, 4);
-
-  const absl::strings_internal::Charmap lets("^abcde", 3);
-  const absl::strings_internal::Charmap lets2("fghij\0klmnop", 10);
-  const absl::strings_internal::Charmap lets3("fghij\0klmnop");
-  ASSERT_TRUE(lets2.contains('k'));
-  ASSERT_TRUE(!lets3.contains('k'));
-
-  ASSERT_TRUE(symbols.IntersectsWith(lets));
-  ASSERT_TRUE(!lets2.IntersectsWith(lets));
-  ASSERT_TRUE(lets.IntersectsWith(symbols));
-  ASSERT_TRUE(!lets.IntersectsWith(lets2));
-
-  ASSERT_TRUE(nothing_map.IsZero());
-  ASSERT_TRUE(!lets.IsZero());
-}
-
-namespace {
-std::string Members(const absl::strings_internal::Charmap& m) {
-  std::string r;
-  for (size_t i = 0; i < 256; ++i)
-    if (m.contains(i)) r.push_back(i);
-  return r;
-}
-
-std::string ClosedRangeString(unsigned char lo, unsigned char hi) {
-  // Don't depend on lo<hi. Just increment until lo==hi.
-  std::string s;
-  while (true) {
-    s.push_back(lo);
-    if (lo == hi) break;
-    ++lo;
-  }
-  return s;
-}
-
-}  // namespace
-
-TEST(Charmap, Constexpr) {
-  constexpr absl::strings_internal::Charmap kEmpty = nothing_map;
-  EXPECT_THAT(Members(kEmpty), "");
-  constexpr absl::strings_internal::Charmap kA =
-      absl::strings_internal::Charmap::Char('A');
-  EXPECT_THAT(Members(kA), "A");
-  constexpr absl::strings_internal::Charmap kAZ =
-      absl::strings_internal::Charmap::Range('A', 'Z');
-  EXPECT_THAT(Members(kAZ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-  constexpr absl::strings_internal::Charmap kIdentifier =
-      absl::strings_internal::Charmap::Range('0', '9') |
-      absl::strings_internal::Charmap::Range('A', 'Z') |
-      absl::strings_internal::Charmap::Range('a', 'z') |
-      absl::strings_internal::Charmap::Char('_');
-  EXPECT_THAT(Members(kIdentifier),
-              "0123456789"
-              "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-              "_"
-              "abcdefghijklmnopqrstuvwxyz");
-  constexpr absl::strings_internal::Charmap kAll = everything_map;
-  for (size_t i = 0; i < 256; ++i) {
-    EXPECT_TRUE(kAll.contains(i)) << i;
-  }
-  constexpr absl::strings_internal::Charmap kHello =
-      absl::strings_internal::Charmap::FromString("Hello, world!");
-  EXPECT_THAT(Members(kHello), " !,Hdelorw");
-
-  // test negation and intersection
-  constexpr absl::strings_internal::Charmap kABC =
-      absl::strings_internal::Charmap::Range('A', 'Z') &
-      ~absl::strings_internal::Charmap::Range('D', 'Z');
-  EXPECT_THAT(Members(kABC), "ABC");
-}
-
-TEST(Charmap, Range) {
-  // Exhaustive testing takes too long, so test some of the boundaries that
-  // are perhaps going to cause trouble.
-  std::vector<size_t> poi = {0,   1,   2,   3,   4,   7,   8,   9,  15,
-                             16,  17,  30,  31,  32,  33,  63,  64, 65,
-                             127, 128, 129, 223, 224, 225, 254, 255};
-  for (auto lo = poi.begin(); lo != poi.end(); ++lo) {
-    SCOPED_TRACE(*lo);
-    for (auto hi = lo; hi != poi.end(); ++hi) {
-      SCOPED_TRACE(*hi);
-      EXPECT_THAT(Members(absl::strings_internal::Charmap::Range(*lo, *hi)),
-                  ClosedRangeString(*lo, *hi));
-    }
-  }
-}
-
-bool AsBool(int x) { return static_cast<bool>(x); }
-
-TEST(CharmapCtype, Match) {
-  for (int c = 0; c < 256; ++c) {
-    SCOPED_TRACE(c);
-    SCOPED_TRACE(static_cast<char>(c));
-    EXPECT_EQ(AsBool(std::isupper(c)),
-              absl::strings_internal::UpperCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::islower(c)),
-              absl::strings_internal::LowerCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isdigit(c)),
-              absl::strings_internal::DigitCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isalpha(c)),
-              absl::strings_internal::AlphaCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isalnum(c)),
-              absl::strings_internal::AlnumCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isxdigit(c)),
-              absl::strings_internal::XDigitCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isprint(c)),
-              absl::strings_internal::PrintCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isspace(c)),
-              absl::strings_internal::SpaceCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::iscntrl(c)),
-              absl::strings_internal::CntrlCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isblank(c)),
-              absl::strings_internal::BlankCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::isgraph(c)),
-              absl::strings_internal::GraphCharmap().contains(c));
-    EXPECT_EQ(AsBool(std::ispunct(c)),
-              absl::strings_internal::PunctCharmap().contains(c));
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/charconv_bigint.cc b/third_party/abseil_cpp/absl/strings/internal/charconv_bigint.cc
deleted file mode 100644
index ebf8c0791a..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/charconv_bigint.cc
+++ /dev/null
@@ -1,359 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/charconv_bigint.h"
-
-#include <algorithm>
-#include <cassert>
-#include <string>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-namespace {
-
-// Table containing some large powers of 5, for fast computation.
-
-// Constant step size for entries in the kLargePowersOfFive table.  Each entry
-// is larger than the previous entry by a factor of 5**kLargePowerOfFiveStep
-// (or 5**27).
-//
-// In other words, the Nth entry in the table is 5**(27*N).
-//
-// 5**27 is the largest power of 5 that fits in 64 bits.
-constexpr int kLargePowerOfFiveStep = 27;
-
-// The largest legal index into the kLargePowersOfFive table.
-//
-// In other words, the largest precomputed power of 5 is 5**(27*20).
-constexpr int kLargestPowerOfFiveIndex = 20;
-
-// Table of powers of (5**27), up to (5**27)**20 == 5**540.
-//
-// Used to generate large powers of 5 while limiting the number of repeated
-// multiplications required.
-//
-// clang-format off
-const uint32_t kLargePowersOfFive[] = {
-// 5**27 (i=1), start=0, end=2
-  0xfa10079dU, 0x6765c793U,
-// 5**54 (i=2), start=2, end=6
-  0x97d9f649U, 0x6664242dU, 0x29939b14U, 0x29c30f10U,
-// 5**81 (i=3), start=6, end=12
-  0xc4f809c5U, 0x7bf3f22aU, 0x67bdae34U, 0xad340517U, 0x369d1b5fU, 0x10de1593U,
-// 5**108 (i=4), start=12, end=20
-  0x92b260d1U, 0x9efff7c7U, 0x81de0ec6U, 0xaeba5d56U, 0x410664a4U, 0x4f40737aU,
-  0x20d3846fU, 0x06d00f73U,
-// 5**135 (i=5), start=20, end=30
-  0xff1b172dU, 0x13a1d71cU, 0xefa07617U, 0x7f682d3dU, 0xff8c90c0U, 0x3f0131e7U,
-  0x3fdcb9feU, 0x917b0177U, 0x16c407a7U, 0x02c06b9dU,
-// 5**162 (i=6), start=30, end=42
-  0x960f7199U, 0x056667ecU, 0xe07aefd8U, 0x80f2b9ccU, 0x8273f5e3U, 0xeb9a214aU,
-  0x40b38005U, 0x0e477ad4U, 0x277d08e6U, 0xfa28b11eU, 0xd3f7d784U, 0x011c835bU,
-// 5**189 (i=7), start=42, end=56
-  0xf723d9d5U, 0x3282d3f3U, 0xe00857d1U, 0x69659d25U, 0x2cf117cfU, 0x24da6d07U,
-  0x954d1417U, 0x3e5d8cedU, 0x7a8bb766U, 0xfd785ae6U, 0x645436d2U, 0x40c78b34U,
-  0x94151217U, 0x0072e9f7U,
-// 5**216 (i=8), start=56, end=72
-  0x2b416aa1U, 0x7893c5a7U, 0xe37dc6d4U, 0x2bad2beaU, 0xf0fc846cU, 0x7575ae4bU,
-  0x62587b14U, 0x83b67a34U, 0x02110cdbU, 0xf7992f55U, 0x00deb022U, 0xa4a23becU,
-  0x8af5c5cdU, 0xb85b654fU, 0x818df38bU, 0x002e69d2U,
-// 5**243 (i=9), start=72, end=90
-  0x3518cbbdU, 0x20b0c15fU, 0x38756c2fU, 0xfb5dc3ddU, 0x22ad2d94U, 0xbf35a952U,
-  0xa699192aU, 0x9a613326U, 0xad2a9cedU, 0xd7f48968U, 0xe87dfb54U, 0xc8f05db6U,
-  0x5ef67531U, 0x31c1ab49U, 0xe202ac9fU, 0x9b2957b5U, 0xa143f6d3U, 0x0012bf07U,
-// 5**270 (i=10), start=90, end=110
-  0x8b971de9U, 0x21aba2e1U, 0x63944362U, 0x57172336U, 0xd9544225U, 0xfb534166U,
-  0x08c563eeU, 0x14640ee2U, 0x24e40d31U, 0x02b06537U, 0x03887f14U, 0x0285e533U,
-  0xb744ef26U, 0x8be3a6c4U, 0x266979b4U, 0x6761ece2U, 0xd9cb39e4U, 0xe67de319U,
-  0x0d39e796U, 0x00079250U,
-// 5**297 (i=11), start=110, end=132
-  0x260eb6e5U, 0xf414a796U, 0xee1a7491U, 0xdb9368ebU, 0xf50c105bU, 0x59157750U,
-  0x9ed2fb5cU, 0xf6e56d8bU, 0xeaee8d23U, 0x0f319f75U, 0x2aa134d6U, 0xac2908e9U,
-  0xd4413298U, 0x02f02a55U, 0x989d5a7aU, 0x70dde184U, 0xba8040a7U, 0x03200981U,
-  0xbe03b11cU, 0x3c1c2a18U, 0xd60427a1U, 0x00030ee0U,
-// 5**324 (i=12), start=132, end=156
-  0xce566d71U, 0xf1c4aa25U, 0x4e93ca53U, 0xa72283d0U, 0x551a73eaU, 0x3d0538e2U,
-  0x8da4303fU, 0x6a58de60U, 0x0e660221U, 0x49cf61a6U, 0x8d058fc1U, 0xb9d1a14cU,
-  0x4bab157dU, 0xc85c6932U, 0x518c8b9eU, 0x9b92b8d0U, 0x0d8a0e21U, 0xbd855df9U,
-  0xb3ea59a1U, 0x8da29289U, 0x4584d506U, 0x3752d80fU, 0xb72569c6U, 0x00013c33U,
-// 5**351 (i=13), start=156, end=182
-  0x190f354dU, 0x83695cfeU, 0xe5a4d0c7U, 0xb60fb7e8U, 0xee5bbcc4U, 0xb922054cU,
-  0xbb4f0d85U, 0x48394028U, 0x1d8957dbU, 0x0d7edb14U, 0x4ecc7587U, 0x505e9e02U,
-  0x4c87f36bU, 0x99e66bd6U, 0x44b9ed35U, 0x753037d4U, 0xe5fe5f27U, 0x2742c203U,
-  0x13b2ed2bU, 0xdc525d2cU, 0xe6fde59aU, 0x77ffb18fU, 0x13c5752cU, 0x08a84bccU,
-  0x859a4940U, 0x00007fb6U,
-// 5**378 (i=14), start=182, end=210
-  0x4f98cb39U, 0xa60edbbcU, 0x83b5872eU, 0xa501acffU, 0x9cc76f78U, 0xbadd4c73U,
-  0x43e989faU, 0xca7acf80U, 0x2e0c824fU, 0xb19f4ffcU, 0x092fd81cU, 0xe4eb645bU,
-  0xa1ff84c2U, 0x8a5a83baU, 0xa8a1fae9U, 0x1db43609U, 0xb0fed50bU, 0x0dd7d2bdU,
-  0x7d7accd8U, 0x91fa640fU, 0x37dcc6c5U, 0x1c417fd5U, 0xe4d462adU, 0xe8a43399U,
-  0x131bf9a5U, 0x8df54d29U, 0x36547dc1U, 0x00003395U,
-// 5**405 (i=15), start=210, end=240
-  0x5bd330f5U, 0x77d21967U, 0x1ac481b7U, 0x6be2f7ceU, 0x7f4792a9U, 0xe84c2c52U,
-  0x84592228U, 0x9dcaf829U, 0xdab44ce1U, 0x3d0c311bU, 0x532e297dU, 0x4704e8b4U,
-  0x9cdc32beU, 0x41e64d9dU, 0x7717bea1U, 0xa824c00dU, 0x08f50b27U, 0x0f198d77U,
-  0x49bbfdf0U, 0x025c6c69U, 0xd4e55cd3U, 0xf083602bU, 0xb9f0fecdU, 0xc0864aeaU,
-  0x9cb98681U, 0xaaf620e9U, 0xacb6df30U, 0x4faafe66U, 0x8af13c3bU, 0x000014d5U,
-// 5**432 (i=16), start=240, end=272
-  0x682bb941U, 0x89a9f297U, 0xcba75d7bU, 0x404217b1U, 0xb4e519e9U, 0xa1bc162bU,
-  0xf7f5910aU, 0x98715af5U, 0x2ff53e57U, 0xe3ef118cU, 0x490c4543U, 0xbc9b1734U,
-  0x2affbe4dU, 0x4cedcb4cU, 0xfb14e99eU, 0x35e34212U, 0xece39c24U, 0x07673ab3U,
-  0xe73115ddU, 0xd15d38e7U, 0x093eed3bU, 0xf8e7eac5U, 0x78a8cc80U, 0x25227aacU,
-  0x3f590551U, 0x413da1cbU, 0xdf643a55U, 0xab65ad44U, 0xd70b23d7U, 0xc672cd76U,
-  0x3364ea62U, 0x0000086aU,
-// 5**459 (i=17), start=272, end=306
-  0x22f163ddU, 0x23cf07acU, 0xbe2af6c2U, 0xf412f6f6U, 0xc3ff541eU, 0x6eeaf7deU,
-  0xa47047e0U, 0x408cda92U, 0x0f0eeb08U, 0x56deba9dU, 0xcfc6b090U, 0x8bbbdf04U,
-  0x3933cdb3U, 0x9e7bb67dU, 0x9f297035U, 0x38946244U, 0xee1d37bbU, 0xde898174U,
-  0x63f3559dU, 0x705b72fbU, 0x138d27d9U, 0xf8603a78U, 0x735eec44U, 0xe30987d5U,
-  0xc6d38070U, 0x9cfe548eU, 0x9ff01422U, 0x7c564aa8U, 0x91cc60baU, 0xcbc3565dU,
-  0x7550a50bU, 0x6909aeadU, 0x13234c45U, 0x00000366U,
-// 5**486 (i=18), start=306, end=342
-  0x17954989U, 0x3a7d7709U, 0x98042de5U, 0xa9011443U, 0x45e723c2U, 0x269ffd6fU,
-  0x58852a46U, 0xaaa1042aU, 0x2eee8153U, 0xb2b6c39eU, 0xaf845b65U, 0xf6c365d7U,
-  0xe4cffb2bU, 0xc840e90cU, 0xabea8abbU, 0x5c58f8d2U, 0x5c19fa3aU, 0x4670910aU,
-  0x4449f21cU, 0xefa645b3U, 0xcc427decU, 0x083c3d73U, 0x467cb413U, 0x6fe10ae4U,
-  0x3caffc72U, 0x9f8da55eU, 0x5e5c8ea7U, 0x490594bbU, 0xf0871b0bU, 0xdd89816cU,
-  0x8e931df8U, 0xe85ce1c9U, 0xcca090a5U, 0x575fa16bU, 0x6b9f106cU, 0x0000015fU,
-// 5**513 (i=19), start=342, end=380
-  0xee20d805U, 0x57bc3c07U, 0xcdea624eU, 0xd3f0f52dU, 0x9924b4f4U, 0xcf968640U,
-  0x61d41962U, 0xe87fb464U, 0xeaaf51c7U, 0x564c8b60U, 0xccda4028U, 0x529428bbU,
-  0x313a1fa8U, 0x96bd0f94U, 0x7a82ebaaU, 0xad99e7e9U, 0xf2668cd4U, 0xbe33a45eU,
-  0xfd0db669U, 0x87ee369fU, 0xd3ec20edU, 0x9c4d7db7U, 0xdedcf0d8U, 0x7cd2ca64U,
-  0xe25a6577U, 0x61003fd4U, 0xe56f54ccU, 0x10b7c748U, 0x40526e5eU, 0x7300ae87U,
-  0x5c439261U, 0x2c0ff469U, 0xbf723f12U, 0xb2379b61U, 0xbf59b4f5U, 0xc91b1c3fU,
-  0xf0046d27U, 0x0000008dU,
-// 5**540 (i=20), start=380, end=420
-  0x525c9e11U, 0xf4e0eb41U, 0xebb2895dU, 0x5da512f9U, 0x7d9b29d4U, 0x452f4edcU,
-  0x0b90bc37U, 0x341777cbU, 0x63d269afU, 0x1da77929U, 0x0a5c1826U, 0x77991898U,
-  0x5aeddf86U, 0xf853a877U, 0x538c31ccU, 0xe84896daU, 0xb7a0010bU, 0x17ef4de5U,
-  0xa52a2adeU, 0x029fd81cU, 0x987ce701U, 0x27fefd77U, 0xdb46c66fU, 0x5d301900U,
-  0x496998c0U, 0xbb6598b9U, 0x5eebb607U, 0xe547354aU, 0xdf4a2f7eU, 0xf06c4955U,
-  0x96242ffaU, 0x1775fb27U, 0xbecc58ceU, 0xebf2a53bU, 0x3eaad82aU, 0xf41137baU,
-  0x573e6fbaU, 0xfb4866b8U, 0x54002148U, 0x00000039U,
-};
-// clang-format on
-
-// Returns a pointer to the big integer data for (5**27)**i.  i must be
-// between 1 and 20, inclusive.
-const uint32_t* LargePowerOfFiveData(int i) {
-  return kLargePowersOfFive + i * (i - 1);
-}
-
-// Returns the size of the big integer data for (5**27)**i, in words.  i must be
-// between 1 and 20, inclusive.
-int LargePowerOfFiveSize(int i) { return 2 * i; }
-}  // namespace
-
-ABSL_DLL const uint32_t kFiveToNth[14] = {
-    1,     5,      25,      125,     625,      3125,      15625,
-    78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125,
-};
-
-ABSL_DLL const uint32_t kTenToNth[10] = {
-    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
-};
-
-template <int max_words>
-int BigUnsigned<max_words>::ReadFloatMantissa(const ParsedFloat& fp,
-                                              int significant_digits) {
-  SetToZero();
-  assert(fp.type == FloatType::kNumber);
-
-  if (fp.subrange_begin == nullptr) {
-    // We already exactly parsed the mantissa, so no more work is necessary.
-    words_[0] = fp.mantissa & 0xffffffffu;
-    words_[1] = fp.mantissa >> 32;
-    if (words_[1]) {
-      size_ = 2;
-    } else if (words_[0]) {
-      size_ = 1;
-    }
-    return fp.exponent;
-  }
-  int exponent_adjust =
-      ReadDigits(fp.subrange_begin, fp.subrange_end, significant_digits);
-  return fp.literal_exponent + exponent_adjust;
-}
-
-template <int max_words>
-int BigUnsigned<max_words>::ReadDigits(const char* begin, const char* end,
-                                       int significant_digits) {
-  assert(significant_digits <= Digits10() + 1);
-  SetToZero();
-
-  bool after_decimal_point = false;
-  // Discard any leading zeroes before the decimal point
-  while (begin < end && *begin == '0') {
-    ++begin;
-  }
-  int dropped_digits = 0;
-  // Discard any trailing zeroes.  These may or may not be after the decimal
-  // point.
-  while (begin < end && *std::prev(end) == '0') {
-    --end;
-    ++dropped_digits;
-  }
-  if (begin < end && *std::prev(end) == '.') {
-    // If the string ends in '.', either before or after dropping zeroes, then
-    // drop the decimal point and look for more digits to drop.
-    dropped_digits = 0;
-    --end;
-    while (begin < end && *std::prev(end) == '0') {
-      --end;
-      ++dropped_digits;
-    }
-  } else if (dropped_digits) {
-    // We dropped digits, and aren't sure if they're before or after the decimal
-    // point.  Figure that out now.
-    const char* dp = std::find(begin, end, '.');
-    if (dp != end) {
-      // The dropped trailing digits were after the decimal point, so don't
-      // count them.
-      dropped_digits = 0;
-    }
-  }
-  // Any non-fraction digits we dropped need to be accounted for in our exponent
-  // adjustment.
-  int exponent_adjust = dropped_digits;
-
-  uint32_t queued = 0;
-  int digits_queued = 0;
-  for (; begin != end && significant_digits > 0; ++begin) {
-    if (*begin == '.') {
-      after_decimal_point = true;
-      continue;
-    }
-    if (after_decimal_point) {
-      // For each fractional digit we emit in our parsed integer, adjust our
-      // decimal exponent to compensate.
-      --exponent_adjust;
-    }
-    int digit = (*begin - '0');
-    --significant_digits;
-    if (significant_digits == 0 && std::next(begin) != end &&
-        (digit == 0 || digit == 5)) {
-      // If this is the very last significant digit, but insignificant digits
-      // remain, we know that the last of those remaining significant digits is
-      // nonzero.  (If it wasn't, we would have stripped it before we got here.)
-      // So if this final digit is a 0 or 5, adjust it upward by 1.
-      //
-      // This adjustment is what allows incredibly large mantissas ending in
-      // 500000...000000000001 to correctly round up, rather than to nearest.
-      ++digit;
-    }
-    queued = 10 * queued + digit;
-    ++digits_queued;
-    if (digits_queued == kMaxSmallPowerOfTen) {
-      MultiplyBy(kTenToNth[kMaxSmallPowerOfTen]);
-      AddWithCarry(0, queued);
-      queued = digits_queued = 0;
-    }
-  }
-  // Encode any remaining digits.
-  if (digits_queued) {
-    MultiplyBy(kTenToNth[digits_queued]);
-    AddWithCarry(0, queued);
-  }
-
-  // If any insignificant digits remain, we will drop them.  But if we have not
-  // yet read the decimal point, then we have to adjust the exponent to account
-  // for the dropped digits.
-  if (begin < end && !after_decimal_point) {
-    // This call to std::find will result in a pointer either to the decimal
-    // point, or to the end of our buffer if there was none.
-    //
-    // Either way, [begin, decimal_point) will contain the set of dropped digits
-    // that require an exponent adjustment.
-    const char* decimal_point = std::find(begin, end, '.');
-    exponent_adjust += (decimal_point - begin);
-  }
-  return exponent_adjust;
-}
-
-template <int max_words>
-/* static */ BigUnsigned<max_words> BigUnsigned<max_words>::FiveToTheNth(
-    int n) {
-  BigUnsigned answer(1u);
-
-  // Seed from the table of large powers, if possible.
-  bool first_pass = true;
-  while (n >= kLargePowerOfFiveStep) {
-    int big_power =
-        std::min(n / kLargePowerOfFiveStep, kLargestPowerOfFiveIndex);
-    if (first_pass) {
-      // just copy, rather than multiplying by 1
-      std::copy(
-          LargePowerOfFiveData(big_power),
-          LargePowerOfFiveData(big_power) + LargePowerOfFiveSize(big_power),
-          answer.words_);
-      answer.size_ = LargePowerOfFiveSize(big_power);
-      first_pass = false;
-    } else {
-      answer.MultiplyBy(LargePowerOfFiveSize(big_power),
-                        LargePowerOfFiveData(big_power));
-    }
-    n -= kLargePowerOfFiveStep * big_power;
-  }
-  answer.MultiplyByFiveToTheNth(n);
-  return answer;
-}
-
-template <int max_words>
-void BigUnsigned<max_words>::MultiplyStep(int original_size,
-                                          const uint32_t* other_words,
-                                          int other_size, int step) {
-  int this_i = std::min(original_size - 1, step);
-  int other_i = step - this_i;
-
-  uint64_t this_word = 0;
-  uint64_t carry = 0;
-  for (; this_i >= 0 && other_i < other_size; --this_i, ++other_i) {
-    uint64_t product = words_[this_i];
-    product *= other_words[other_i];
-    this_word += product;
-    carry += (this_word >> 32);
-    this_word &= 0xffffffff;
-  }
-  AddWithCarry(step + 1, carry);
-  words_[step] = this_word & 0xffffffff;
-  if (this_word > 0 && size_ <= step) {
-    size_ = step + 1;
-  }
-}
-
-template <int max_words>
-std::string BigUnsigned<max_words>::ToString() const {
-  BigUnsigned<max_words> copy = *this;
-  std::string result;
-  // Build result in reverse order
-  while (copy.size() > 0) {
-    int next_digit = copy.DivMod<10>();
-    result.push_back('0' + next_digit);
-  }
-  if (result.empty()) {
-    result.push_back('0');
-  }
-  std::reverse(result.begin(), result.end());
-  return result;
-}
-
-template class BigUnsigned<4>;
-template class BigUnsigned<84>;
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/charconv_bigint.h b/third_party/abseil_cpp/absl/strings/internal/charconv_bigint.h
deleted file mode 100644
index 8f702976a8..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/charconv_bigint.h
+++ /dev/null
@@ -1,423 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_
-#define ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_
-
-#include <algorithm>
-#include <cstdint>
-#include <iostream>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/internal/charconv_parse.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-// The largest power that 5 that can be raised to, and still fit in a uint32_t.
-constexpr int kMaxSmallPowerOfFive = 13;
-// The largest power that 10 that can be raised to, and still fit in a uint32_t.
-constexpr int kMaxSmallPowerOfTen = 9;
-
-ABSL_DLL extern const uint32_t
-    kFiveToNth[kMaxSmallPowerOfFive + 1];
-ABSL_DLL extern const uint32_t kTenToNth[kMaxSmallPowerOfTen + 1];
-
-// Large, fixed-width unsigned integer.
-//
-// Exact rounding for decimal-to-binary floating point conversion requires very
-// large integer math, but a design goal of absl::from_chars is to avoid
-// allocating memory.  The integer precision needed for decimal-to-binary
-// conversions is large but bounded, so a huge fixed-width integer class
-// suffices.
-//
-// This is an intentionally limited big integer class.  Only needed operations
-// are implemented.  All storage lives in an array data member, and all
-// arithmetic is done in-place, to avoid requiring separate storage for operand
-// and result.
-//
-// This is an internal class.  Some methods live in the .cc file, and are
-// instantiated only for the values of max_words we need.
-template <int max_words>
-class BigUnsigned {
- public:
-  static_assert(max_words == 4 || max_words == 84,
-                "unsupported max_words value");
-
-  BigUnsigned() : size_(0), words_{} {}
-  explicit constexpr BigUnsigned(uint64_t v)
-      : size_((v >> 32) ? 2 : v ? 1 : 0),
-        words_{static_cast<uint32_t>(v & 0xffffffffu),
-               static_cast<uint32_t>(v >> 32)} {}
-
-  // Constructs a BigUnsigned from the given string_view containing a decimal
-  // value.  If the input string is not a decimal integer, constructs a 0
-  // instead.
-  explicit BigUnsigned(absl::string_view sv) : size_(0), words_{} {
-    // Check for valid input, returning a 0 otherwise.  This is reasonable
-    // behavior only because this constructor is for unit tests.
-    if (std::find_if_not(sv.begin(), sv.end(), ascii_isdigit) != sv.end() ||
-        sv.empty()) {
-      return;
-    }
-    int exponent_adjust =
-        ReadDigits(sv.data(), sv.data() + sv.size(), Digits10() + 1);
-    if (exponent_adjust > 0) {
-      MultiplyByTenToTheNth(exponent_adjust);
-    }
-  }
-
-  // Loads the mantissa value of a previously-parsed float.
-  //
-  // Returns the associated decimal exponent.  The value of the parsed float is
-  // exactly *this * 10**exponent.
-  int ReadFloatMantissa(const ParsedFloat& fp, int significant_digits);
-
-  // Returns the number of decimal digits of precision this type provides.  All
-  // numbers with this many decimal digits or fewer are representable by this
-  // type.
-  //
-  // Analagous to std::numeric_limits<BigUnsigned>::digits10.
-  static constexpr int Digits10() {
-    // 9975007/1035508 is very slightly less than log10(2**32).
-    return static_cast<uint64_t>(max_words) * 9975007 / 1035508;
-  }
-
-  // Shifts left by the given number of bits.
-  void ShiftLeft(int count) {
-    if (count > 0) {
-      const int word_shift = count / 32;
-      if (word_shift >= max_words) {
-        SetToZero();
-        return;
-      }
-      size_ = (std::min)(size_ + word_shift, max_words);
-      count %= 32;
-      if (count == 0) {
-        std::copy_backward(words_, words_ + size_ - word_shift, words_ + size_);
-      } else {
-        for (int i = (std::min)(size_, max_words - 1); i > word_shift; --i) {
-          words_[i] = (words_[i - word_shift] << count) |
-                      (words_[i - word_shift - 1] >> (32 - count));
-        }
-        words_[word_shift] = words_[0] << count;
-        // Grow size_ if necessary.
-        if (size_ < max_words && words_[size_]) {
-          ++size_;
-        }
-      }
-      std::fill(words_, words_ + word_shift, 0u);
-    }
-  }
-
-
-  // Multiplies by v in-place.
-  void MultiplyBy(uint32_t v) {
-    if (size_ == 0 || v == 1) {
-      return;
-    }
-    if (v == 0) {
-      SetToZero();
-      return;
-    }
-    const uint64_t factor = v;
-    uint64_t window = 0;
-    for (int i = 0; i < size_; ++i) {
-      window += factor * words_[i];
-      words_[i] = window & 0xffffffff;
-      window >>= 32;
-    }
-    // If carry bits remain and there's space for them, grow size_.
-    if (window && size_ < max_words) {
-      words_[size_] = window & 0xffffffff;
-      ++size_;
-    }
-  }
-
-  void MultiplyBy(uint64_t v) {
-    uint32_t words[2];
-    words[0] = static_cast<uint32_t>(v);
-    words[1] = static_cast<uint32_t>(v >> 32);
-    if (words[1] == 0) {
-      MultiplyBy(words[0]);
-    } else {
-      MultiplyBy(2, words);
-    }
-  }
-
-  // Multiplies in place by 5 to the power of n.  n must be non-negative.
-  void MultiplyByFiveToTheNth(int n) {
-    while (n >= kMaxSmallPowerOfFive) {
-      MultiplyBy(kFiveToNth[kMaxSmallPowerOfFive]);
-      n -= kMaxSmallPowerOfFive;
-    }
-    if (n > 0) {
-      MultiplyBy(kFiveToNth[n]);
-    }
-  }
-
-  // Multiplies in place by 10 to the power of n.  n must be non-negative.
-  void MultiplyByTenToTheNth(int n) {
-    if (n > kMaxSmallPowerOfTen) {
-      // For large n, raise to a power of 5, then shift left by the same amount.
-      // (10**n == 5**n * 2**n.)  This requires fewer multiplications overall.
-      MultiplyByFiveToTheNth(n);
-      ShiftLeft(n);
-    } else if (n > 0) {
-      // We can do this more quickly for very small N by using a single
-      // multiplication.
-      MultiplyBy(kTenToNth[n]);
-    }
-  }
-
-  // Returns the value of 5**n, for non-negative n.  This implementation uses
-  // a lookup table, and is faster then seeding a BigUnsigned with 1 and calling
-  // MultiplyByFiveToTheNth().
-  static BigUnsigned FiveToTheNth(int n);
-
-  // Multiplies by another BigUnsigned, in-place.
-  template <int M>
-  void MultiplyBy(const BigUnsigned<M>& other) {
-    MultiplyBy(other.size(), other.words());
-  }
-
-  void SetToZero() {
-    std::fill(words_, words_ + size_, 0u);
-    size_ = 0;
-  }
-
-  // Returns the value of the nth word of this BigUnsigned.  This is
-  // range-checked, and returns 0 on out-of-bounds accesses.
-  uint32_t GetWord(int index) const {
-    if (index < 0 || index >= size_) {
-      return 0;
-    }
-    return words_[index];
-  }
-
-  // Returns this integer as a decimal string.  This is not used in the decimal-
-  // to-binary conversion; it is intended to aid in testing.
-  std::string ToString() const;
-
-  int size() const { return size_; }
-  const uint32_t* words() const { return words_; }
-
- private:
-  // Reads the number between [begin, end), possibly containing a decimal point,
-  // into this BigUnsigned.
-  //
-  // Callers are required to ensure [begin, end) contains a valid number, with
-  // one or more decimal digits and at most one decimal point.  This routine
-  // will behave unpredictably if these preconditions are not met.
-  //
-  // Only the first `significant_digits` digits are read.  Digits beyond this
-  // limit are "sticky": If the final significant digit is 0 or 5, and if any
-  // dropped digit is nonzero, then that final significant digit is adjusted up
-  // to 1 or 6.  This adjustment allows for precise rounding.
-  //
-  // Returns `exponent_adjustment`, a power-of-ten exponent adjustment to
-  // account for the decimal point and for dropped significant digits.  After
-  // this function returns,
-  //   actual_value_of_parsed_string ~= *this * 10**exponent_adjustment.
-  int ReadDigits(const char* begin, const char* end, int significant_digits);
-
-  // Performs a step of big integer multiplication.  This computes the full
-  // (64-bit-wide) values that should be added at the given index (step), and
-  // adds to that location in-place.
-  //
-  // Because our math all occurs in place, we must multiply starting from the
-  // highest word working downward.  (This is a bit more expensive due to the
-  // extra carries involved.)
-  //
-  // This must be called in steps, for each word to be calculated, starting from
-  // the high end and working down to 0.  The first value of `step` should be
-  //   `std::min(original_size + other.size_ - 2, max_words - 1)`.
-  // The reason for this expression is that multiplying the i'th word from one
-  // multiplicand and the j'th word of another multiplicand creates a
-  // two-word-wide value to be stored at the (i+j)'th element.  The highest
-  // word indices we will access are `original_size - 1` from this object, and
-  // `other.size_ - 1` from our operand.  Therefore,
-  // `original_size + other.size_ - 2` is the first step we should calculate,
-  // but limited on an upper bound by max_words.
-
-  // Working from high-to-low ensures that we do not overwrite the portions of
-  // the initial value of *this which are still needed for later steps.
-  //
-  // Once called with step == 0, *this contains the result of the
-  // multiplication.
-  //
-  // `original_size` is the size_ of *this before the first call to
-  // MultiplyStep().  `other_words` and `other_size` are the contents of our
-  // operand.  `step` is the step to perform, as described above.
-  void MultiplyStep(int original_size, const uint32_t* other_words,
-                    int other_size, int step);
-
-  void MultiplyBy(int other_size, const uint32_t* other_words) {
-    const int original_size = size_;
-    const int first_step =
-        (std::min)(original_size + other_size - 2, max_words - 1);
-    for (int step = first_step; step >= 0; --step) {
-      MultiplyStep(original_size, other_words, other_size, step);
-    }
-  }
-
-  // Adds a 32-bit value to the index'th word, with carry.
-  void AddWithCarry(int index, uint32_t value) {
-    if (value) {
-      while (index < max_words && value > 0) {
-        words_[index] += value;
-        // carry if we overflowed in this word:
-        if (value > words_[index]) {
-          value = 1;
-          ++index;
-        } else {
-          value = 0;
-        }
-      }
-      size_ = (std::min)(max_words, (std::max)(index + 1, size_));
-    }
-  }
-
-  void AddWithCarry(int index, uint64_t value) {
-    if (value && index < max_words) {
-      uint32_t high = value >> 32;
-      uint32_t low = value & 0xffffffff;
-      words_[index] += low;
-      if (words_[index] < low) {
-        ++high;
-        if (high == 0) {
-          // Carry from the low word caused our high word to overflow.
-          // Short circuit here to do the right thing.
-          AddWithCarry(index + 2, static_cast<uint32_t>(1));
-          return;
-        }
-      }
-      if (high > 0) {
-        AddWithCarry(index + 1, high);
-      } else {
-        // Normally 32-bit AddWithCarry() sets size_, but since we don't call
-        // it when `high` is 0, do it ourselves here.
-        size_ = (std::min)(max_words, (std::max)(index + 1, size_));
-      }
-    }
-  }
-
-  // Divide this in place by a constant divisor.  Returns the remainder of the
-  // division.
-  template <uint32_t divisor>
-  uint32_t DivMod() {
-    uint64_t accumulator = 0;
-    for (int i = size_ - 1; i >= 0; --i) {
-      accumulator <<= 32;
-      accumulator += words_[i];
-      // accumulator / divisor will never overflow an int32_t in this loop
-      words_[i] = static_cast<uint32_t>(accumulator / divisor);
-      accumulator = accumulator % divisor;
-    }
-    while (size_ > 0 && words_[size_ - 1] == 0) {
-      --size_;
-    }
-    return static_cast<uint32_t>(accumulator);
-  }
-
-  // The number of elements in words_ that may carry significant values.
-  // All elements beyond this point are 0.
-  //
-  // When size_ is 0, this BigUnsigned stores the value 0.
-  // When size_ is nonzero, is *not* guaranteed that words_[size_ - 1] is
-  // nonzero.  This can occur due to overflow truncation.
-  // In particular, x.size_ != y.size_ does *not* imply x != y.
-  int size_;
-  uint32_t words_[max_words];
-};
-
-// Compares two big integer instances.
-//
-// Returns -1 if lhs < rhs, 0 if lhs == rhs, and 1 if lhs > rhs.
-template <int N, int M>
-int Compare(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
-  int limit = (std::max)(lhs.size(), rhs.size());
-  for (int i = limit - 1; i >= 0; --i) {
-    const uint32_t lhs_word = lhs.GetWord(i);
-    const uint32_t rhs_word = rhs.GetWord(i);
-    if (lhs_word < rhs_word) {
-      return -1;
-    } else if (lhs_word > rhs_word) {
-      return 1;
-    }
-  }
-  return 0;
-}
-
-template <int N, int M>
-bool operator==(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
-  int limit = (std::max)(lhs.size(), rhs.size());
-  for (int i = 0; i < limit; ++i) {
-    if (lhs.GetWord(i) != rhs.GetWord(i)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-template <int N, int M>
-bool operator!=(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
-  return !(lhs == rhs);
-}
-
-template <int N, int M>
-bool operator<(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
-  return Compare(lhs, rhs) == -1;
-}
-
-template <int N, int M>
-bool operator>(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
-  return rhs < lhs;
-}
-template <int N, int M>
-bool operator<=(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
-  return !(rhs < lhs);
-}
-template <int N, int M>
-bool operator>=(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) {
-  return !(lhs < rhs);
-}
-
-// Output operator for BigUnsigned, for testing purposes only.
-template <int N>
-std::ostream& operator<<(std::ostream& os, const BigUnsigned<N>& num) {
-  return os << num.ToString();
-}
-
-// Explicit instantiation declarations for the sizes of BigUnsigned that we
-// are using.
-//
-// For now, the choices of 4 and 84 are arbitrary; 4 is a small value that is
-// still bigger than an int128, and 84 is a large value we will want to use
-// in the from_chars implementation.
-//
-// Comments justifying the use of 84 belong in the from_chars implementation,
-// and will be added in a follow-up CL.
-extern template class BigUnsigned<4>;
-extern template class BigUnsigned<84>;
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/charconv_bigint_test.cc b/third_party/abseil_cpp/absl/strings/internal/charconv_bigint_test.cc
deleted file mode 100644
index a8b9945829..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/charconv_bigint_test.cc
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/charconv_bigint.h"
-
-#include <string>
-
-#include "gtest/gtest.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-TEST(BigUnsigned, ShiftLeft) {
-  {
-    // Check that 3 * 2**100 is calculated correctly
-    BigUnsigned<4> num(3u);
-    num.ShiftLeft(100);
-    EXPECT_EQ(num, BigUnsigned<4>("3802951800684688204490109616128"));
-  }
-  {
-    // Test that overflow is truncated properly.
-    // 15 is 4 bits long, and BigUnsigned<4> is a 128-bit bigint.
-    // Shifting left by 125 bits should truncate off the high bit, so that
-    //   15 << 125 == 7 << 125
-    // after truncation.
-    BigUnsigned<4> a(15u);
-    BigUnsigned<4> b(7u);
-    BigUnsigned<4> c(3u);
-    a.ShiftLeft(125);
-    b.ShiftLeft(125);
-    c.ShiftLeft(125);
-    EXPECT_EQ(a, b);
-    EXPECT_NE(a, c);
-  }
-  {
-    // Same test, larger bigint:
-    BigUnsigned<84> a(15u);
-    BigUnsigned<84> b(7u);
-    BigUnsigned<84> c(3u);
-    a.ShiftLeft(84 * 32 - 3);
-    b.ShiftLeft(84 * 32 - 3);
-    c.ShiftLeft(84 * 32 - 3);
-    EXPECT_EQ(a, b);
-    EXPECT_NE(a, c);
-  }
-  {
-    // Check that incrementally shifting has the same result as doing it all at
-    // once (attempting to capture corner cases.)
-    const std::string seed = "1234567890123456789012345678901234567890";
-    BigUnsigned<84> a(seed);
-    for (int i = 1; i <= 84 * 32; ++i) {
-      a.ShiftLeft(1);
-      BigUnsigned<84> b(seed);
-      b.ShiftLeft(i);
-      EXPECT_EQ(a, b);
-    }
-    // And we should have fully rotated all bits off by now:
-    EXPECT_EQ(a, BigUnsigned<84>(0u));
-  }
-  {
-    // Bit shifting large and small numbers by large and small offsets.
-    // Intended to exercise bounds-checking corner on ShiftLeft() (directly
-    // and under asan).
-
-    // 2**(32*84)-1
-    const BigUnsigned<84> all_bits_one(
-        "1474444211396924248063325089479706787923460402125687709454567433186613"
-        "6228083464060749874845919674257665016359189106695900028098437021384227"
-        "3285029708032466536084583113729486015826557532750465299832071590813090"
-        "2011853039837649252477307070509704043541368002938784757296893793903797"
-        "8180292336310543540677175225040919704702800559606097685920595947397024"
-        "8303316808753252115729411497720357971050627997031988036134171378490368"
-        "6008000778741115399296162550786288457245180872759047016734959330367829"
-        "5235612397427686310674725251378116268607113017720538636924549612987647"
-        "5767411074510311386444547332882472126067840027882117834454260409440463"
-        "9345147252664893456053258463203120637089916304618696601333953616715125"
-        "2115882482473279040772264257431663818610405673876655957323083702713344"
-        "4201105427930770976052393421467136557055");
-    const BigUnsigned<84> zero(0u);
-    const BigUnsigned<84> one(1u);
-    // in bounds shifts
-    for (int i = 1; i < 84*32; ++i) {
-      // shifting all_bits_one to the left should result in a smaller number,
-      // since the high bits rotate off and the low bits are replaced with
-      // zeroes.
-      BigUnsigned<84> big_shifted = all_bits_one;
-      big_shifted.ShiftLeft(i);
-      EXPECT_GT(all_bits_one, big_shifted);
-      // Shifting 1 to the left should instead result in a larger number.
-      BigUnsigned<84> small_shifted = one;
-      small_shifted.ShiftLeft(i);
-      EXPECT_LT(one, small_shifted);
-    }
-    // Shifting by zero or a negative number has no effect
-    for (int no_op_shift : {0, -1, -84 * 32, std::numeric_limits<int>::min()}) {
-      BigUnsigned<84> big_shifted = all_bits_one;
-      big_shifted.ShiftLeft(no_op_shift);
-      EXPECT_EQ(all_bits_one, big_shifted);
-      BigUnsigned<84> small_shifted = one;
-      big_shifted.ShiftLeft(no_op_shift);
-      EXPECT_EQ(one, small_shifted);
-    }
-    // Shifting by an amount greater than the number of bits should result in
-    // zero.
-    for (int out_of_bounds_shift :
-         {84 * 32, 84 * 32 + 1, std::numeric_limits<int>::max()}) {
-      BigUnsigned<84> big_shifted = all_bits_one;
-      big_shifted.ShiftLeft(out_of_bounds_shift);
-      EXPECT_EQ(zero, big_shifted);
-      BigUnsigned<84> small_shifted = one;
-      small_shifted.ShiftLeft(out_of_bounds_shift);
-      EXPECT_EQ(zero, small_shifted);
-    }
-  }
-}
-
-TEST(BigUnsigned, MultiplyByUint32) {
-  const BigUnsigned<84> factorial_100(
-      "933262154439441526816992388562667004907159682643816214685929638952175999"
-      "932299156089414639761565182862536979208272237582511852109168640000000000"
-      "00000000000000");
-  BigUnsigned<84> a(1u);
-  for (uint32_t i = 1; i <= 100; ++i) {
-    a.MultiplyBy(i);
-  }
-  EXPECT_EQ(a, BigUnsigned<84>(factorial_100));
-}
-
-TEST(BigUnsigned, MultiplyByBigUnsigned) {
-  {
-    // Put the terms of factorial_200 into two bigints, and multiply them
-    // together.
-    const BigUnsigned<84> factorial_200(
-        "7886578673647905035523632139321850622951359776871732632947425332443594"
-        "4996340334292030428401198462390417721213891963883025764279024263710506"
-        "1926624952829931113462857270763317237396988943922445621451664240254033"
-        "2918641312274282948532775242424075739032403212574055795686602260319041"
-        "7032406235170085879617892222278962370389737472000000000000000000000000"
-        "0000000000000000000000000");
-    BigUnsigned<84> evens(1u);
-    BigUnsigned<84> odds(1u);
-    for (uint32_t i = 1; i < 200; i += 2) {
-      odds.MultiplyBy(i);
-      evens.MultiplyBy(i + 1);
-    }
-    evens.MultiplyBy(odds);
-    EXPECT_EQ(evens, factorial_200);
-  }
-  {
-    // Multiply various powers of 10 together.
-    for (int a = 0 ; a < 700; a += 25) {
-      SCOPED_TRACE(a);
-      BigUnsigned<84> a_value("3" + std::string(a, '0'));
-      for (int b = 0; b < (700 - a); b += 25) {
-        SCOPED_TRACE(b);
-        BigUnsigned<84> b_value("2" + std::string(b, '0'));
-        BigUnsigned<84> expected_product("6" + std::string(a + b, '0'));
-        b_value.MultiplyBy(a_value);
-        EXPECT_EQ(b_value, expected_product);
-      }
-    }
-  }
-}
-
-TEST(BigUnsigned, MultiplyByOverflow) {
-  {
-    // Check that multiplcation overflow predictably truncates.
-
-    // A big int with all bits on.
-    BigUnsigned<4> all_bits_on("340282366920938463463374607431768211455");
-    // Modulo 2**128, this is equal to -1.  Therefore the square of this,
-    // modulo 2**128, should be 1.
-    all_bits_on.MultiplyBy(all_bits_on);
-    EXPECT_EQ(all_bits_on, BigUnsigned<4>(1u));
-  }
-  {
-    // Try multiplying a large bigint by 2**50, and compare the result to
-    // shifting.
-    BigUnsigned<4> value_1("12345678901234567890123456789012345678");
-    BigUnsigned<4> value_2("12345678901234567890123456789012345678");
-    BigUnsigned<4> two_to_fiftieth(1u);
-    two_to_fiftieth.ShiftLeft(50);
-
-    value_1.ShiftLeft(50);
-    value_2.MultiplyBy(two_to_fiftieth);
-    EXPECT_EQ(value_1, value_2);
-  }
-}
-
-TEST(BigUnsigned, FiveToTheNth) {
-  {
-    // Sanity check that MultiplyByFiveToTheNth gives consistent answers, up to
-    // and including overflow.
-    for (int i = 0; i < 1160; ++i) {
-      SCOPED_TRACE(i);
-      BigUnsigned<84> value_1(123u);
-      BigUnsigned<84> value_2(123u);
-      value_1.MultiplyByFiveToTheNth(i);
-      for (int j = 0; j < i; j++) {
-        value_2.MultiplyBy(5u);
-      }
-      EXPECT_EQ(value_1, value_2);
-    }
-  }
-  {
-    // Check that the faster, table-lookup-based static method returns the same
-    // result that multiplying in-place would return, up to and including
-    // overflow.
-    for (int i = 0; i < 1160; ++i) {
-      SCOPED_TRACE(i);
-      BigUnsigned<84> value_1(1u);
-      value_1.MultiplyByFiveToTheNth(i);
-      BigUnsigned<84> value_2 = BigUnsigned<84>::FiveToTheNth(i);
-      EXPECT_EQ(value_1, value_2);
-    }
-  }
-}
-
-TEST(BigUnsigned, TenToTheNth) {
-  {
-    // Sanity check MultiplyByTenToTheNth.
-    for (int i = 0; i < 800; ++i) {
-      SCOPED_TRACE(i);
-      BigUnsigned<84> value_1(123u);
-      BigUnsigned<84> value_2(123u);
-      value_1.MultiplyByTenToTheNth(i);
-      for (int j = 0; j < i; j++) {
-        value_2.MultiplyBy(10u);
-      }
-      EXPECT_EQ(value_1, value_2);
-    }
-  }
-  {
-    // Alternate testing approach, taking advantage of the decimal parser.
-    for (int i = 0; i < 200; ++i) {
-      SCOPED_TRACE(i);
-      BigUnsigned<84> value_1(135u);
-      value_1.MultiplyByTenToTheNth(i);
-      BigUnsigned<84> value_2("135" + std::string(i, '0'));
-      EXPECT_EQ(value_1, value_2);
-    }
-  }
-}
-
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/charconv_parse.cc b/third_party/abseil_cpp/absl/strings/internal/charconv_parse.cc
deleted file mode 100644
index 8b11868c88..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/charconv_parse.cc
+++ /dev/null
@@ -1,504 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/charconv_parse.h"
-#include "absl/strings/charconv.h"
-
-#include <cassert>
-#include <cstdint>
-#include <limits>
-
-#include "absl/strings/internal/memutil.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-// ParseFloat<10> will read the first 19 significant digits of the mantissa.
-// This number was chosen for multiple reasons.
-//
-// (a) First, for whatever integer type we choose to represent the mantissa, we
-// want to choose the largest possible number of decimal digits for that integer
-// type.  We are using uint64_t, which can express any 19-digit unsigned
-// integer.
-//
-// (b) Second, we need to parse enough digits that the binary value of any
-// mantissa we capture has more bits of resolution than the mantissa
-// representation in the target float.  Our algorithm requires at least 3 bits
-// of headway, but 19 decimal digits give a little more than that.
-//
-// The following static assertions verify the above comments:
-constexpr int kDecimalMantissaDigitsMax = 19;
-
-static_assert(std::numeric_limits<uint64_t>::digits10 ==
-                  kDecimalMantissaDigitsMax,
-              "(a) above");
-
-// IEEE doubles, which we assume in Abseil, have 53 binary bits of mantissa.
-static_assert(std::numeric_limits<double>::is_iec559, "IEEE double assumed");
-static_assert(std::numeric_limits<double>::radix == 2, "IEEE double fact");
-static_assert(std::numeric_limits<double>::digits == 53, "IEEE double fact");
-
-// The lowest valued 19-digit decimal mantissa we can read still contains
-// sufficient information to reconstruct a binary mantissa.
-static_assert(1000000000000000000u > (uint64_t(1) << (53 + 3)), "(b) above");
-
-// ParseFloat<16> will read the first 15 significant digits of the mantissa.
-//
-// Because a base-16-to-base-2 conversion can be done exactly, we do not need
-// to maximize the number of scanned hex digits to improve our conversion.  What
-// is required is to scan two more bits than the mantissa can represent, so that
-// we always round correctly.
-//
-// (One extra bit does not suffice to perform correct rounding, since a number
-// exactly halfway between two representable floats has unique rounding rules,
-// so we need to differentiate between a "halfway between" number and a "closer
-// to the larger value" number.)
-constexpr int kHexadecimalMantissaDigitsMax = 15;
-
-// The minimum number of significant bits that will be read from
-// kHexadecimalMantissaDigitsMax hex digits.  We must subtract by three, since
-// the most significant digit can be a "1", which only contributes a single
-// significant bit.
-constexpr int kGuaranteedHexadecimalMantissaBitPrecision =
-    4 * kHexadecimalMantissaDigitsMax - 3;
-
-static_assert(kGuaranteedHexadecimalMantissaBitPrecision >
-                  std::numeric_limits<double>::digits + 2,
-              "kHexadecimalMantissaDigitsMax too small");
-
-// We also impose a limit on the number of significant digits we will read from
-// an exponent, to avoid having to deal with integer overflow.  We use 9 for
-// this purpose.
-//
-// If we read a 9 digit exponent, the end result of the conversion will
-// necessarily be infinity or zero, depending on the sign of the exponent.
-// Therefore we can just drop extra digits on the floor without any extra
-// logic.
-constexpr int kDecimalExponentDigitsMax = 9;
-static_assert(std::numeric_limits<int>::digits10 >= kDecimalExponentDigitsMax,
-              "int type too small");
-
-// To avoid incredibly large inputs causing integer overflow for our exponent,
-// we impose an arbitrary but very large limit on the number of significant
-// digits we will accept.  The implementation refuses to match a string with
-// more consecutive significant mantissa digits than this.
-constexpr int kDecimalDigitLimit = 50000000;
-
-// Corresponding limit for hexadecimal digit inputs.  This is one fourth the
-// amount of kDecimalDigitLimit, since each dropped hexadecimal digit requires
-// a binary exponent adjustment of 4.
-constexpr int kHexadecimalDigitLimit = kDecimalDigitLimit / 4;
-
-// The largest exponent we can read is 999999999 (per
-// kDecimalExponentDigitsMax), and the largest exponent adjustment we can get
-// from dropped mantissa digits is 2 * kDecimalDigitLimit, and the sum of these
-// comfortably fits in an integer.
-//
-// We count kDecimalDigitLimit twice because there are independent limits for
-// numbers before and after the decimal point.  (In the case where there are no
-// significant digits before the decimal point, there are independent limits for
-// post-decimal-point leading zeroes and for significant digits.)
-static_assert(999999999 + 2 * kDecimalDigitLimit <
-                  std::numeric_limits<int>::max(),
-              "int type too small");
-static_assert(999999999 + 2 * (4 * kHexadecimalDigitLimit) <
-                  std::numeric_limits<int>::max(),
-              "int type too small");
-
-// Returns true if the provided bitfield allows parsing an exponent value
-// (e.g., "1.5e100").
-bool AllowExponent(chars_format flags) {
-  bool fixed = (flags & chars_format::fixed) == chars_format::fixed;
-  bool scientific =
-      (flags & chars_format::scientific) == chars_format::scientific;
-  return scientific || !fixed;
-}
-
-// Returns true if the provided bitfield requires an exponent value be present.
-bool RequireExponent(chars_format flags) {
-  bool fixed = (flags & chars_format::fixed) == chars_format::fixed;
-  bool scientific =
-      (flags & chars_format::scientific) == chars_format::scientific;
-  return scientific && !fixed;
-}
-
-const int8_t kAsciiToInt[256] = {
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,  1,  2,  3,  4,  5,  6,  7,  8,
-    9,  -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1};
-
-// Returns true if `ch` is a digit in the given base
-template <int base>
-bool IsDigit(char ch);
-
-// Converts a valid `ch` to its digit value in the given base.
-template <int base>
-unsigned ToDigit(char ch);
-
-// Returns true if `ch` is the exponent delimiter for the given base.
-template <int base>
-bool IsExponentCharacter(char ch);
-
-// Returns the maximum number of significant digits we will read for a float
-// in the given base.
-template <int base>
-constexpr int MantissaDigitsMax();
-
-// Returns the largest consecutive run of digits we will accept when parsing a
-// number in the given base.
-template <int base>
-constexpr int DigitLimit();
-
-// Returns the amount the exponent must be adjusted by for each dropped digit.
-// (For decimal this is 1, since the digits are in base 10 and the exponent base
-// is also 10, but for hexadecimal this is 4, since the digits are base 16 but
-// the exponent base is 2.)
-template <int base>
-constexpr int DigitMagnitude();
-
-template <>
-bool IsDigit<10>(char ch) {
-  return ch >= '0' && ch <= '9';
-}
-template <>
-bool IsDigit<16>(char ch) {
-  return kAsciiToInt[static_cast<unsigned char>(ch)] >= 0;
-}
-
-template <>
-unsigned ToDigit<10>(char ch) {
-  return ch - '0';
-}
-template <>
-unsigned ToDigit<16>(char ch) {
-  return kAsciiToInt[static_cast<unsigned char>(ch)];
-}
-
-template <>
-bool IsExponentCharacter<10>(char ch) {
-  return ch == 'e' || ch == 'E';
-}
-
-template <>
-bool IsExponentCharacter<16>(char ch) {
-  return ch == 'p' || ch == 'P';
-}
-
-template <>
-constexpr int MantissaDigitsMax<10>() {
-  return kDecimalMantissaDigitsMax;
-}
-template <>
-constexpr int MantissaDigitsMax<16>() {
-  return kHexadecimalMantissaDigitsMax;
-}
-
-template <>
-constexpr int DigitLimit<10>() {
-  return kDecimalDigitLimit;
-}
-template <>
-constexpr int DigitLimit<16>() {
-  return kHexadecimalDigitLimit;
-}
-
-template <>
-constexpr int DigitMagnitude<10>() {
-  return 1;
-}
-template <>
-constexpr int DigitMagnitude<16>() {
-  return 4;
-}
-
-// Reads decimal digits from [begin, end) into *out.  Returns the number of
-// digits consumed.
-//
-// After max_digits has been read, keeps consuming characters, but no longer
-// adjusts *out.  If a nonzero digit is dropped this way, *dropped_nonzero_digit
-// is set; otherwise, it is left unmodified.
-//
-// If no digits are matched, returns 0 and leaves *out unchanged.
-//
-// ConsumeDigits does not protect against overflow on *out; max_digits must
-// be chosen with respect to type T to avoid the possibility of overflow.
-template <int base, typename T>
-int ConsumeDigits(const char* begin, const char* end, int max_digits, T* out,
-                  bool* dropped_nonzero_digit) {
-  if (base == 10) {
-    assert(max_digits <= std::numeric_limits<T>::digits10);
-  } else if (base == 16) {
-    assert(max_digits * 4 <= std::numeric_limits<T>::digits);
-  }
-  const char* const original_begin = begin;
-
-  // Skip leading zeros, but only if *out is zero.
-  // They don't cause an overflow so we don't have to count them for
-  // `max_digits`.
-  while (!*out && end != begin && *begin == '0') ++begin;
-
-  T accumulator = *out;
-  const char* significant_digits_end =
-      (end - begin > max_digits) ? begin + max_digits : end;
-  while (begin < significant_digits_end && IsDigit<base>(*begin)) {
-    // Do not guard against *out overflow; max_digits was chosen to avoid this.
-    // Do assert against it, to detect problems in debug builds.
-    auto digit = static_cast<T>(ToDigit<base>(*begin));
-    assert(accumulator * base >= accumulator);
-    accumulator *= base;
-    assert(accumulator + digit >= accumulator);
-    accumulator += digit;
-    ++begin;
-  }
-  bool dropped_nonzero = false;
-  while (begin < end && IsDigit<base>(*begin)) {
-    dropped_nonzero = dropped_nonzero || (*begin != '0');
-    ++begin;
-  }
-  if (dropped_nonzero && dropped_nonzero_digit != nullptr) {
-    *dropped_nonzero_digit = true;
-  }
-  *out = accumulator;
-  return static_cast<int>(begin - original_begin);
-}
-
-// Returns true if `v` is one of the chars allowed inside parentheses following
-// a NaN.
-bool IsNanChar(char v) {
-  return (v == '_') || (v >= '0' && v <= '9') || (v >= 'a' && v <= 'z') ||
-         (v >= 'A' && v <= 'Z');
-}
-
-// Checks the range [begin, end) for a strtod()-formatted infinity or NaN.  If
-// one is found, sets `out` appropriately and returns true.
-bool ParseInfinityOrNan(const char* begin, const char* end,
-                        strings_internal::ParsedFloat* out) {
-  if (end - begin < 3) {
-    return false;
-  }
-  switch (*begin) {
-    case 'i':
-    case 'I': {
-      // An infinity string consists of the characters "inf" or "infinity",
-      // case insensitive.
-      if (strings_internal::memcasecmp(begin + 1, "nf", 2) != 0) {
-        return false;
-      }
-      out->type = strings_internal::FloatType::kInfinity;
-      if (end - begin >= 8 &&
-          strings_internal::memcasecmp(begin + 3, "inity", 5) == 0) {
-        out->end = begin + 8;
-      } else {
-        out->end = begin + 3;
-      }
-      return true;
-    }
-    case 'n':
-    case 'N': {
-      // A NaN consists of the characters "nan", case insensitive, optionally
-      // followed by a parenthesized sequence of zero or more alphanumeric
-      // characters and/or underscores.
-      if (strings_internal::memcasecmp(begin + 1, "an", 2) != 0) {
-        return false;
-      }
-      out->type = strings_internal::FloatType::kNan;
-      out->end = begin + 3;
-      // NaN is allowed to be followed by a parenthesized string, consisting of
-      // only the characters [a-zA-Z0-9_].  Match that if it's present.
-      begin += 3;
-      if (begin < end && *begin == '(') {
-        const char* nan_begin = begin + 1;
-        while (nan_begin < end && IsNanChar(*nan_begin)) {
-          ++nan_begin;
-        }
-        if (nan_begin < end && *nan_begin == ')') {
-          // We found an extra NaN specifier range
-          out->subrange_begin = begin + 1;
-          out->subrange_end = nan_begin;
-          out->end = nan_begin + 1;
-        }
-      }
-      return true;
-    }
-    default:
-      return false;
-  }
-}
-}  // namespace
-
-namespace strings_internal {
-
-template <int base>
-strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end,
-                                         chars_format format_flags) {
-  strings_internal::ParsedFloat result;
-
-  // Exit early if we're given an empty range.
-  if (begin == end) return result;
-
-  // Handle the infinity and NaN cases.
-  if (ParseInfinityOrNan(begin, end, &result)) {
-    return result;
-  }
-
-  const char* const mantissa_begin = begin;
-  while (begin < end && *begin == '0') {
-    ++begin;  // skip leading zeros
-  }
-  uint64_t mantissa = 0;
-
-  int exponent_adjustment = 0;
-  bool mantissa_is_inexact = false;
-  int pre_decimal_digits = ConsumeDigits<base>(
-      begin, end, MantissaDigitsMax<base>(), &mantissa, &mantissa_is_inexact);
-  begin += pre_decimal_digits;
-  int digits_left;
-  if (pre_decimal_digits >= DigitLimit<base>()) {
-    // refuse to parse pathological inputs
-    return result;
-  } else if (pre_decimal_digits > MantissaDigitsMax<base>()) {
-    // We dropped some non-fraction digits on the floor.  Adjust our exponent
-    // to compensate.
-    exponent_adjustment =
-        static_cast<int>(pre_decimal_digits - MantissaDigitsMax<base>());
-    digits_left = 0;
-  } else {
-    digits_left =
-        static_cast<int>(MantissaDigitsMax<base>() - pre_decimal_digits);
-  }
-  if (begin < end && *begin == '.') {
-    ++begin;
-    if (mantissa == 0) {
-      // If we haven't seen any nonzero digits yet, keep skipping zeros.  We
-      // have to adjust the exponent to reflect the changed place value.
-      const char* begin_zeros = begin;
-      while (begin < end && *begin == '0') {
-        ++begin;
-      }
-      int zeros_skipped = static_cast<int>(begin - begin_zeros);
-      if (zeros_skipped >= DigitLimit<base>()) {
-        // refuse to parse pathological inputs
-        return result;
-      }
-      exponent_adjustment -= static_cast<int>(zeros_skipped);
-    }
-    int post_decimal_digits = ConsumeDigits<base>(
-        begin, end, digits_left, &mantissa, &mantissa_is_inexact);
-    begin += post_decimal_digits;
-
-    // Since `mantissa` is an integer, each significant digit we read after
-    // the decimal point requires an adjustment to the exponent. "1.23e0" will
-    // be stored as `mantissa` == 123 and `exponent` == -2 (that is,
-    // "123e-2").
-    if (post_decimal_digits >= DigitLimit<base>()) {
-      // refuse to parse pathological inputs
-      return result;
-    } else if (post_decimal_digits > digits_left) {
-      exponent_adjustment -= digits_left;
-    } else {
-      exponent_adjustment -= post_decimal_digits;
-    }
-  }
-  // If we've found no mantissa whatsoever, this isn't a number.
-  if (mantissa_begin == begin) {
-    return result;
-  }
-  // A bare "." doesn't count as a mantissa either.
-  if (begin - mantissa_begin == 1 && *mantissa_begin == '.') {
-    return result;
-  }
-
-  if (mantissa_is_inexact) {
-    // We dropped significant digits on the floor.  Handle this appropriately.
-    if (base == 10) {
-      // If we truncated significant decimal digits, store the full range of the
-      // mantissa for future big integer math for exact rounding.
-      result.subrange_begin = mantissa_begin;
-      result.subrange_end = begin;
-    } else if (base == 16) {
-      // If we truncated hex digits, reflect this fact by setting the low
-      // ("sticky") bit.  This allows for correct rounding in all cases.
-      mantissa |= 1;
-    }
-  }
-  result.mantissa = mantissa;
-
-  const char* const exponent_begin = begin;
-  result.literal_exponent = 0;
-  bool found_exponent = false;
-  if (AllowExponent(format_flags) && begin < end &&
-      IsExponentCharacter<base>(*begin)) {
-    bool negative_exponent = false;
-    ++begin;
-    if (begin < end && *begin == '-') {
-      negative_exponent = true;
-      ++begin;
-    } else if (begin < end && *begin == '+') {
-      ++begin;
-    }
-    const char* const exponent_digits_begin = begin;
-    // Exponent is always expressed in decimal, even for hexadecimal floats.
-    begin += ConsumeDigits<10>(begin, end, kDecimalExponentDigitsMax,
-                               &result.literal_exponent, nullptr);
-    if (begin == exponent_digits_begin) {
-      // there were no digits where we expected an exponent.  We failed to read
-      // an exponent and should not consume the 'e' after all.  Rewind 'begin'.
-      found_exponent = false;
-      begin = exponent_begin;
-    } else {
-      found_exponent = true;
-      if (negative_exponent) {
-        result.literal_exponent = -result.literal_exponent;
-      }
-    }
-  }
-
-  if (!found_exponent && RequireExponent(format_flags)) {
-    // Provided flags required an exponent, but none was found.  This results
-    // in a failure to scan.
-    return result;
-  }
-
-  // Success!
-  result.type = strings_internal::FloatType::kNumber;
-  if (result.mantissa > 0) {
-    result.exponent = result.literal_exponent +
-                      (DigitMagnitude<base>() * exponent_adjustment);
-  } else {
-    result.exponent = 0;
-  }
-  result.end = begin;
-  return result;
-}
-
-template ParsedFloat ParseFloat<10>(const char* begin, const char* end,
-                                    chars_format format_flags);
-template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
-                                    chars_format format_flags);
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/charconv_parse.h b/third_party/abseil_cpp/absl/strings/internal/charconv_parse.h
deleted file mode 100644
index 505998b539..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/charconv_parse.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_
-#define ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_
-
-#include <cstdint>
-
-#include "absl/base/config.h"
-#include "absl/strings/charconv.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-// Enum indicating whether a parsed float is a number or special value.
-enum class FloatType { kNumber, kInfinity, kNan };
-
-// The decomposed parts of a parsed `float` or `double`.
-struct ParsedFloat {
-  // Representation of the parsed mantissa, with the decimal point adjusted to
-  // make it an integer.
-  //
-  // During decimal scanning, this contains 19 significant digits worth of
-  // mantissa value.  If digits beyond this point are found, they
-  // are truncated, and if any of these dropped digits are nonzero, then
-  // `mantissa` is inexact, and the full mantissa is stored in [subrange_begin,
-  // subrange_end).
-  //
-  // During hexadecimal scanning, this contains 15 significant hex digits worth
-  // of mantissa value.  Digits beyond this point are sticky -- they are
-  // truncated, but if any dropped digits are nonzero, the low bit of mantissa
-  // will be set.  (This allows for precise rounding, and avoids the need
-  // to store the full mantissa in [subrange_begin, subrange_end).)
-  uint64_t mantissa = 0;
-
-  // Floating point expontent.  This reflects any decimal point adjustments and
-  // any truncated digits from the mantissa.  The absolute value of the parsed
-  // number is represented by mantissa * (base ** exponent), where base==10 for
-  // decimal floats, and base==2 for hexadecimal floats.
-  int exponent = 0;
-
-  // The literal exponent value scanned from the input, or 0 if none was
-  // present.  This does not reflect any adjustments applied to mantissa.
-  int literal_exponent = 0;
-
-  // The type of number scanned.
-  FloatType type = FloatType::kNumber;
-
-  // When non-null, [subrange_begin, subrange_end) marks a range of characters
-  // that require further processing.  The meaning is dependent on float type.
-  // If type == kNumber and this is set, this is a "wide input": the input
-  // mantissa contained more than 19 digits.  The range contains the full
-  // mantissa.  It plus `literal_exponent` need to be examined to find the best
-  // floating point match.
-  // If type == kNan and this is set, the range marks the contents of a
-  // matched parenthesized character region after the NaN.
-  const char* subrange_begin = nullptr;
-  const char* subrange_end = nullptr;
-
-  // One-past-the-end of the successfully parsed region, or nullptr if no
-  // matching pattern was found.
-  const char* end = nullptr;
-};
-
-// Read the floating point number in the provided range, and populate
-// ParsedFloat accordingly.
-//
-// format_flags is a bitmask value specifying what patterns this API will match.
-// `scientific` and `fixed`  are honored per std::from_chars rules
-// ([utility.from.chars], C++17): if exactly one of these bits is set, then an
-// exponent is required, or dislallowed, respectively.
-//
-// Template parameter `base` must be either 10 or 16.  For base 16, a "0x" is
-// *not* consumed.  The `hex` bit from format_flags is ignored by ParseFloat.
-template <int base>
-ParsedFloat ParseFloat(const char* begin, const char* end,
-                       absl::chars_format format_flags);
-
-extern template ParsedFloat ParseFloat<10>(const char* begin, const char* end,
-                                           absl::chars_format format_flags);
-extern template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
-                                           absl::chars_format format_flags);
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-#endif  // ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/charconv_parse_test.cc b/third_party/abseil_cpp/absl/strings/internal/charconv_parse_test.cc
deleted file mode 100644
index bc2d111876..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/charconv_parse_test.cc
+++ /dev/null
@@ -1,357 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/charconv_parse.h"
-
-#include <string>
-#include <utility>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/str_cat.h"
-
-using absl::chars_format;
-using absl::strings_internal::FloatType;
-using absl::strings_internal::ParsedFloat;
-using absl::strings_internal::ParseFloat;
-
-namespace {
-
-// Check that a given string input is parsed to the expected mantissa and
-// exponent.
-//
-// Input string `s` must contain a '$' character.  It marks the end of the
-// characters that should be consumed by the match.  It is stripped from the
-// input to ParseFloat.
-//
-// If input string `s` contains '[' and ']' characters, these mark the region
-// of characters that should be marked as the "subrange".  For NaNs, this is
-// the location of the extended NaN string.  For numbers, this is the location
-// of the full, over-large mantissa.
-template <int base>
-void ExpectParsedFloat(std::string s, absl::chars_format format_flags,
-                       FloatType expected_type, uint64_t expected_mantissa,
-                       int expected_exponent,
-                       int expected_literal_exponent = -999) {
-  SCOPED_TRACE(s);
-
-  int begin_subrange = -1;
-  int end_subrange = -1;
-  // If s contains '[' and ']', then strip these characters and set the subrange
-  // indices appropriately.
-  std::string::size_type open_bracket_pos = s.find('[');
-  if (open_bracket_pos != std::string::npos) {
-    begin_subrange = static_cast<int>(open_bracket_pos);
-    s.replace(open_bracket_pos, 1, "");
-    std::string::size_type close_bracket_pos = s.find(']');
-    ABSL_RAW_CHECK(close_bracket_pos != absl::string_view::npos,
-                   "Test input contains [ without matching ]");
-    end_subrange = static_cast<int>(close_bracket_pos);
-    s.replace(close_bracket_pos, 1, "");
-  }
-  const std::string::size_type expected_characters_matched = s.find('$');
-  ABSL_RAW_CHECK(expected_characters_matched != std::string::npos,
-                 "Input string must contain $");
-  s.replace(expected_characters_matched, 1, "");
-
-  ParsedFloat parsed =
-      ParseFloat<base>(s.data(), s.data() + s.size(), format_flags);
-
-  EXPECT_NE(parsed.end, nullptr);
-  if (parsed.end == nullptr) {
-    return;  // The following tests are not useful if we fully failed to parse
-  }
-  EXPECT_EQ(parsed.type, expected_type);
-  if (begin_subrange == -1) {
-    EXPECT_EQ(parsed.subrange_begin, nullptr);
-    EXPECT_EQ(parsed.subrange_end, nullptr);
-  } else {
-    EXPECT_EQ(parsed.subrange_begin, s.data() + begin_subrange);
-    EXPECT_EQ(parsed.subrange_end, s.data() + end_subrange);
-  }
-  if (parsed.type == FloatType::kNumber) {
-    EXPECT_EQ(parsed.mantissa, expected_mantissa);
-    EXPECT_EQ(parsed.exponent, expected_exponent);
-    if (expected_literal_exponent != -999) {
-      EXPECT_EQ(parsed.literal_exponent, expected_literal_exponent);
-    }
-  }
-  auto characters_matched = static_cast<int>(parsed.end - s.data());
-  EXPECT_EQ(characters_matched, expected_characters_matched);
-}
-
-// Check that a given string input is parsed to the expected mantissa and
-// exponent.
-//
-// Input string `s` must contain a '$' character.  It marks the end of the
-// characters that were consumed by the match.
-template <int base>
-void ExpectNumber(std::string s, absl::chars_format format_flags,
-                  uint64_t expected_mantissa, int expected_exponent,
-                  int expected_literal_exponent = -999) {
-  ExpectParsedFloat<base>(std::move(s), format_flags, FloatType::kNumber,
-                          expected_mantissa, expected_exponent,
-                          expected_literal_exponent);
-}
-
-// Check that a given string input is parsed to the given special value.
-//
-// This tests against both number bases, since infinities and NaNs have
-// identical representations in both modes.
-void ExpectSpecial(const std::string& s, absl::chars_format format_flags,
-                   FloatType type) {
-  ExpectParsedFloat<10>(s, format_flags, type, 0, 0);
-  ExpectParsedFloat<16>(s, format_flags, type, 0, 0);
-}
-
-// Check that a given input string is not matched by Float.
-template <int base>
-void ExpectFailedParse(absl::string_view s, absl::chars_format format_flags) {
-  ParsedFloat parsed =
-      ParseFloat<base>(s.data(), s.data() + s.size(), format_flags);
-  EXPECT_EQ(parsed.end, nullptr);
-}
-
-TEST(ParseFloat, SimpleValue) {
-  // Test that various forms of floating point numbers all parse correctly.
-  ExpectNumber<10>("1.23456789e5$", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("1.23456789e+5$", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("1.23456789E5$", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("1.23456789e05$", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("123.456789e3$", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("0.000123456789e9$", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("123456.789$", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("123456789e-3$", chars_format::general, 123456789, -3);
-
-  ExpectNumber<16>("1.234abcdefp28$", chars_format::general, 0x1234abcdef, -8);
-  ExpectNumber<16>("1.234abcdefp+28$", chars_format::general, 0x1234abcdef, -8);
-  ExpectNumber<16>("1.234ABCDEFp28$", chars_format::general, 0x1234abcdef, -8);
-  ExpectNumber<16>("1.234AbCdEfP0028$", chars_format::general, 0x1234abcdef,
-                   -8);
-  ExpectNumber<16>("123.4abcdefp20$", chars_format::general, 0x1234abcdef, -8);
-  ExpectNumber<16>("0.0001234abcdefp44$", chars_format::general, 0x1234abcdef,
-                   -8);
-  ExpectNumber<16>("1234abcd.ef$", chars_format::general, 0x1234abcdef, -8);
-  ExpectNumber<16>("1234abcdefp-8$", chars_format::general, 0x1234abcdef, -8);
-
-  // ExpectNumber does not attempt to drop trailing zeroes.
-  ExpectNumber<10>("0001.2345678900e005$", chars_format::general, 12345678900,
-                   -5);
-  ExpectNumber<16>("0001.234abcdef000p28$", chars_format::general,
-                   0x1234abcdef000, -20);
-
-  // Ensure non-matching characters after a number are ignored, even when they
-  // look like potentially matching characters.
-  ExpectNumber<10>("1.23456789e5$   ", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("1.23456789e5$e5e5", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("1.23456789e5$.25", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("1.23456789e5$-", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("1.23456789e5$PUPPERS!!!", chars_format::general, 123456789,
-                   -3);
-  ExpectNumber<10>("123456.789$efghij", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("123456.789$e", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("123456.789$p5", chars_format::general, 123456789, -3);
-  ExpectNumber<10>("123456.789$.10", chars_format::general, 123456789, -3);
-
-  ExpectNumber<16>("1.234abcdefp28$   ", chars_format::general, 0x1234abcdef,
-                   -8);
-  ExpectNumber<16>("1.234abcdefp28$p28", chars_format::general, 0x1234abcdef,
-                   -8);
-  ExpectNumber<16>("1.234abcdefp28$.125", chars_format::general, 0x1234abcdef,
-                   -8);
-  ExpectNumber<16>("1.234abcdefp28$-", chars_format::general, 0x1234abcdef, -8);
-  ExpectNumber<16>("1.234abcdefp28$KITTEHS!!!", chars_format::general,
-                   0x1234abcdef, -8);
-  ExpectNumber<16>("1234abcd.ef$ghijk", chars_format::general, 0x1234abcdef,
-                   -8);
-  ExpectNumber<16>("1234abcd.ef$p", chars_format::general, 0x1234abcdef, -8);
-  ExpectNumber<16>("1234abcd.ef$.10", chars_format::general, 0x1234abcdef, -8);
-
-  // Ensure we can read a full resolution mantissa without overflow.
-  ExpectNumber<10>("9999999999999999999$", chars_format::general,
-                   9999999999999999999u, 0);
-  ExpectNumber<16>("fffffffffffffff$", chars_format::general,
-                   0xfffffffffffffffu, 0);
-
-  // Check that zero is consistently read.
-  ExpectNumber<10>("0$", chars_format::general, 0, 0);
-  ExpectNumber<16>("0$", chars_format::general, 0, 0);
-  ExpectNumber<10>("000000000000000000000000000000000000000$",
-                   chars_format::general, 0, 0);
-  ExpectNumber<16>("000000000000000000000000000000000000000$",
-                   chars_format::general, 0, 0);
-  ExpectNumber<10>("0000000000000000000000.000000000000000000$",
-                   chars_format::general, 0, 0);
-  ExpectNumber<16>("0000000000000000000000.000000000000000000$",
-                   chars_format::general, 0, 0);
-  ExpectNumber<10>("0.00000000000000000000000000000000e123456$",
-                   chars_format::general, 0, 0);
-  ExpectNumber<16>("0.00000000000000000000000000000000p123456$",
-                   chars_format::general, 0, 0);
-}
-
-TEST(ParseFloat, LargeDecimalMantissa) {
-  // After 19 significant decimal digits in the mantissa, ParsedFloat will
-  // truncate additional digits.  We need to test that:
-  //   1) the truncation to 19 digits happens
-  //   2) the returned exponent reflects the dropped significant digits
-  //   3) a correct literal_exponent is set
-  //
-  // If and only if a significant digit is found after 19 digits, then the
-  // entirety of the mantissa in case the exact value is needed to make a
-  // rounding decision.  The [ and ] characters below denote where such a
-  // subregion was marked by by ParseFloat.  They are not part of the input.
-
-  // Mark a capture group only if a dropped digit is significant (nonzero).
-  ExpectNumber<10>("100000000000000000000000000$", chars_format::general,
-                   1000000000000000000,
-                   /* adjusted exponent */ 8);
-
-  ExpectNumber<10>("123456789123456789100000000$", chars_format::general,
-                   1234567891234567891,
-                   /* adjusted exponent */ 8);
-
-  ExpectNumber<10>("[123456789123456789123456789]$", chars_format::general,
-                   1234567891234567891,
-                   /* adjusted exponent */ 8,
-                   /* literal exponent */ 0);
-
-  ExpectNumber<10>("[123456789123456789100000009]$", chars_format::general,
-                   1234567891234567891,
-                   /* adjusted exponent */ 8,
-                   /* literal exponent */ 0);
-
-  ExpectNumber<10>("[123456789123456789120000000]$", chars_format::general,
-                   1234567891234567891,
-                   /* adjusted exponent */ 8,
-                   /* literal exponent */ 0);
-
-  // Leading zeroes should not count towards the 19 significant digit limit
-  ExpectNumber<10>("[00000000123456789123456789123456789]$",
-                   chars_format::general, 1234567891234567891,
-                   /* adjusted exponent */ 8,
-                   /* literal exponent */ 0);
-
-  ExpectNumber<10>("00000000123456789123456789100000000$",
-                   chars_format::general, 1234567891234567891,
-                   /* adjusted exponent */ 8);
-
-  // Truncated digits after the decimal point should not cause a further
-  // exponent adjustment.
-  ExpectNumber<10>("1.234567891234567891e123$", chars_format::general,
-                   1234567891234567891, 105);
-  ExpectNumber<10>("[1.23456789123456789123456789]e123$", chars_format::general,
-                   1234567891234567891,
-                   /* adjusted exponent */ 105,
-                   /* literal exponent */ 123);
-
-  // Ensure we truncate, and not round.  (The from_chars algorithm we use
-  // depends on our guess missing low, if it misses, so we need the rounding
-  // error to be downward.)
-  ExpectNumber<10>("[1999999999999999999999]$", chars_format::general,
-                   1999999999999999999,
-                   /* adjusted exponent */ 3,
-                   /* literal exponent */ 0);
-}
-
-TEST(ParseFloat, LargeHexadecimalMantissa) {
-  // After 15 significant hex digits in the mantissa, ParsedFloat will treat
-  // additional digits as sticky,  We need to test that:
-  //   1) The truncation to 15 digits happens
-  //   2) The returned exponent reflects the dropped significant digits
-  //   3) If a nonzero digit is dropped, the low bit of mantissa is set.
-
-  ExpectNumber<16>("123456789abcdef123456789abcdef$", chars_format::general,
-                   0x123456789abcdef, 60);
-
-  // Leading zeroes should not count towards the 15 significant digit limit
-  ExpectNumber<16>("000000123456789abcdef123456789abcdef$",
-                   chars_format::general, 0x123456789abcdef, 60);
-
-  // Truncated digits after the radix point should not cause a further
-  // exponent adjustment.
-  ExpectNumber<16>("1.23456789abcdefp100$", chars_format::general,
-                   0x123456789abcdef, 44);
-  ExpectNumber<16>("1.23456789abcdef123456789abcdefp100$",
-                   chars_format::general, 0x123456789abcdef, 44);
-
-  // test sticky digit behavior.  The low bit should be set iff any dropped
-  // digit is nonzero.
-  ExpectNumber<16>("123456789abcdee123456789abcdee$", chars_format::general,
-                   0x123456789abcdef, 60);
-  ExpectNumber<16>("123456789abcdee000000000000001$", chars_format::general,
-                   0x123456789abcdef, 60);
-  ExpectNumber<16>("123456789abcdee000000000000000$", chars_format::general,
-                   0x123456789abcdee, 60);
-}
-
-TEST(ParseFloat, ScientificVsFixed) {
-  // In fixed mode, an exponent is never matched (but the remainder of the
-  // number will be matched.)
-  ExpectNumber<10>("1.23456789$e5", chars_format::fixed, 123456789, -8);
-  ExpectNumber<10>("123456.789$", chars_format::fixed, 123456789, -3);
-  ExpectNumber<16>("1.234abcdef$p28", chars_format::fixed, 0x1234abcdef, -36);
-  ExpectNumber<16>("1234abcd.ef$", chars_format::fixed, 0x1234abcdef, -8);
-
-  // In scientific mode, numbers don't match *unless* they have an exponent.
-  ExpectNumber<10>("1.23456789e5$", chars_format::scientific, 123456789, -3);
-  ExpectFailedParse<10>("-123456.789$", chars_format::scientific);
-  ExpectNumber<16>("1.234abcdefp28$", chars_format::scientific, 0x1234abcdef,
-                   -8);
-  ExpectFailedParse<16>("1234abcd.ef$", chars_format::scientific);
-}
-
-TEST(ParseFloat, Infinity) {
-  ExpectFailedParse<10>("in", chars_format::general);
-  ExpectFailedParse<16>("in", chars_format::general);
-  ExpectFailedParse<10>("inx", chars_format::general);
-  ExpectFailedParse<16>("inx", chars_format::general);
-  ExpectSpecial("inf$", chars_format::general, FloatType::kInfinity);
-  ExpectSpecial("Inf$", chars_format::general, FloatType::kInfinity);
-  ExpectSpecial("INF$", chars_format::general, FloatType::kInfinity);
-  ExpectSpecial("inf$inite", chars_format::general, FloatType::kInfinity);
-  ExpectSpecial("iNfInItY$", chars_format::general, FloatType::kInfinity);
-  ExpectSpecial("infinity$!!!", chars_format::general, FloatType::kInfinity);
-}
-
-TEST(ParseFloat, NaN) {
-  ExpectFailedParse<10>("na", chars_format::general);
-  ExpectFailedParse<16>("na", chars_format::general);
-  ExpectFailedParse<10>("nah", chars_format::general);
-  ExpectFailedParse<16>("nah", chars_format::general);
-  ExpectSpecial("nan$", chars_format::general, FloatType::kNan);
-  ExpectSpecial("NaN$", chars_format::general, FloatType::kNan);
-  ExpectSpecial("nAn$", chars_format::general, FloatType::kNan);
-  ExpectSpecial("NAN$", chars_format::general, FloatType::kNan);
-  ExpectSpecial("NaN$aNaNaNaNaBatman!", chars_format::general, FloatType::kNan);
-
-  // A parenthesized sequence of the characters [a-zA-Z0-9_] is allowed to
-  // appear after an NaN.  Check that this is allowed, and that the correct
-  // characters are grouped.
-  //
-  // (The characters [ and ] in the pattern below delimit the expected matched
-  // subgroup; they are not part of the input passed to ParseFloat.)
-  ExpectSpecial("nan([0xabcdef])$", chars_format::general, FloatType::kNan);
-  ExpectSpecial("nan([0xabcdef])$...", chars_format::general, FloatType::kNan);
-  ExpectSpecial("nan([0xabcdef])$)...", chars_format::general, FloatType::kNan);
-  ExpectSpecial("nan([])$", chars_format::general, FloatType::kNan);
-  ExpectSpecial("nan([aAzZ09_])$", chars_format::general, FloatType::kNan);
-  // If the subgroup contains illegal characters, don't match it at all.
-  ExpectSpecial("nan$(bad-char)", chars_format::general, FloatType::kNan);
-  // Also cope with a missing close paren.
-  ExpectSpecial("nan$(0xabcdef", chars_format::general, FloatType::kNan);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/cord_internal.h b/third_party/abseil_cpp/absl/strings/internal/cord_internal.h
deleted file mode 100644
index aa91a691b9..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/cord_internal.h
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
-#define ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
-
-#include <atomic>
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <type_traits>
-
-#include "absl/base/internal/invoke.h"
-#include "absl/container/internal/compressed_tuple.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace cord_internal {
-
-// Wraps std::atomic for reference counting.
-class Refcount {
- public:
-  constexpr Refcount() : count_{kRefIncrement} {}
-  struct Immortal {};
-  explicit constexpr Refcount(Immortal) : count_(kImmortalTag) {}
-
-  // Increments the reference count. Imposes no memory ordering.
-  inline void Increment() {
-    count_.fetch_add(kRefIncrement, std::memory_order_relaxed);
-  }
-
-  // Asserts that the current refcount is greater than 0. If the refcount is
-  // greater than 1, decrements the reference count.
-  //
-  // Returns false if there are no references outstanding; true otherwise.
-  // Inserts barriers to ensure that state written before this method returns
-  // false will be visible to a thread that just observed this method returning
-  // false.
-  inline bool Decrement() {
-    int32_t refcount = count_.load(std::memory_order_acquire);
-    assert(refcount > 0 || refcount & kImmortalTag);
-    return refcount != kRefIncrement &&
-           count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) !=
-               kRefIncrement;
-  }
-
-  // Same as Decrement but expect that refcount is greater than 1.
-  inline bool DecrementExpectHighRefcount() {
-    int32_t refcount =
-        count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel);
-    assert(refcount > 0 || refcount & kImmortalTag);
-    return refcount != kRefIncrement;
-  }
-
-  // Returns the current reference count using acquire semantics.
-  inline int32_t Get() const {
-    return count_.load(std::memory_order_acquire) >> kImmortalShift;
-  }
-
-  // Returns whether the atomic integer is 1.
-  // If the reference count is used in the conventional way, a
-  // reference count of 1 implies that the current thread owns the
-  // reference and no other thread shares it.
-  // This call performs the test for a reference count of one, and
-  // performs the memory barrier needed for the owning thread
-  // to act on the object, knowing that it has exclusive access to the
-  // object.
-  inline bool IsOne() {
-    return count_.load(std::memory_order_acquire) == kRefIncrement;
-  }
-
-  bool IsImmortal() const {
-    return (count_.load(std::memory_order_relaxed) & kImmortalTag) != 0;
-  }
-
- private:
-  // We reserve the bottom bit to tag a reference count as immortal.
-  // By making it `1` we ensure that we never reach `0` when adding/subtracting
-  // `2`, thus it never looks as if it should be destroyed.
-  // These are used for the StringConstant constructor where we do not increase
-  // the refcount at construction time (due to constinit requirements) but we
-  // will still decrease it at destruction time to avoid branching on Unref.
-  enum {
-    kImmortalShift = 1,
-    kRefIncrement = 1 << kImmortalShift,
-    kImmortalTag = kRefIncrement - 1
-  };
-
-  std::atomic<int32_t> count_;
-};
-
-// The overhead of a vtable is too much for Cord, so we roll our own subclasses
-// using only a single byte to differentiate classes from each other - the "tag"
-// byte.  Define the subclasses first so we can provide downcasting helper
-// functions in the base class.
-
-struct CordRepConcat;
-struct CordRepSubstring;
-struct CordRepExternal;
-
-// Various representations that we allow
-enum CordRepKind {
-  CONCAT        = 0,
-  EXTERNAL      = 1,
-  SUBSTRING     = 2,
-
-  // We have different tags for different sized flat arrays,
-  // starting with FLAT
-  FLAT          = 3,
-};
-
-struct CordRep {
-  CordRep() = default;
-  constexpr CordRep(Refcount::Immortal immortal, size_t l)
-      : length(l), refcount(immortal), tag(EXTERNAL), data{} {}
-
-  // The following three fields have to be less than 32 bytes since
-  // that is the smallest supported flat node size.
-  size_t length;
-  Refcount refcount;
-  // If tag < FLAT, it represents CordRepKind and indicates the type of node.
-  // Otherwise, the node type is CordRepFlat and the tag is the encoded size.
-  uint8_t tag;
-  char data[1];  // Starting point for flat array: MUST BE LAST FIELD of CordRep
-
-  inline CordRepConcat* concat();
-  inline const CordRepConcat* concat() const;
-  inline CordRepSubstring* substring();
-  inline const CordRepSubstring* substring() const;
-  inline CordRepExternal* external();
-  inline const CordRepExternal* external() const;
-};
-
-struct CordRepConcat : public CordRep {
-  CordRep* left;
-  CordRep* right;
-
-  uint8_t depth() const { return static_cast<uint8_t>(data[0]); }
-  void set_depth(uint8_t depth) { data[0] = static_cast<char>(depth); }
-};
-
-struct CordRepSubstring : public CordRep {
-  size_t start;  // Starting offset of substring in child
-  CordRep* child;
-};
-
-// Type for function pointer that will invoke the releaser function and also
-// delete the `CordRepExternalImpl` corresponding to the passed in
-// `CordRepExternal`.
-using ExternalReleaserInvoker = void (*)(CordRepExternal*);
-
-// External CordReps are allocated together with a type erased releaser. The
-// releaser is stored in the memory directly following the CordRepExternal.
-struct CordRepExternal : public CordRep {
-  CordRepExternal() = default;
-  explicit constexpr CordRepExternal(absl::string_view str)
-      : CordRep(Refcount::Immortal{}, str.size()),
-        base(str.data()),
-        releaser_invoker(nullptr) {}
-
-  const char* base;
-  // Pointer to function that knows how to call and destroy the releaser.
-  ExternalReleaserInvoker releaser_invoker;
-};
-
-struct Rank1 {};
-struct Rank0 : Rank1 {};
-
-template <typename Releaser, typename = ::absl::base_internal::invoke_result_t<
-                                 Releaser, absl::string_view>>
-void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view data) {
-  ::absl::base_internal::invoke(std::forward<Releaser>(releaser), data);
-}
-
-template <typename Releaser,
-          typename = ::absl::base_internal::invoke_result_t<Releaser>>
-void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) {
-  ::absl::base_internal::invoke(std::forward<Releaser>(releaser));
-}
-
-// We use CompressedTuple so that we can benefit from EBCO.
-template <typename Releaser>
-struct CordRepExternalImpl
-    : public CordRepExternal,
-      public ::absl::container_internal::CompressedTuple<Releaser> {
-  // The extra int arg is so that we can avoid interfering with copy/move
-  // constructors while still benefitting from perfect forwarding.
-  template <typename T>
-  CordRepExternalImpl(T&& releaser, int)
-      : CordRepExternalImpl::CompressedTuple(std::forward<T>(releaser)) {
-    this->releaser_invoker = &Release;
-  }
-
-  ~CordRepExternalImpl() {
-    InvokeReleaser(Rank0{}, std::move(this->template get<0>()),
-                   absl::string_view(base, length));
-  }
-
-  static void Release(CordRepExternal* rep) {
-    delete static_cast<CordRepExternalImpl*>(rep);
-  }
-};
-
-template <typename Str>
-struct ConstInitExternalStorage {
-  ABSL_CONST_INIT static CordRepExternal value;
-};
-
-template <typename Str>
-CordRepExternal ConstInitExternalStorage<Str>::value(Str::value);
-
-enum {
-  kMaxInline = 15,
-  // Tag byte & kMaxInline means we are storing a pointer.
-  kTreeFlag = 1 << 4,
-  // Tag byte & kProfiledFlag means we are profiling the Cord.
-  kProfiledFlag = 1 << 5
-};
-
-// If the data has length <= kMaxInline, we store it in `as_chars`, and
-// store the size in `tagged_size`.
-// Else we store it in a tree and store a pointer to that tree in
-// `as_tree.rep` and store a tag in `tagged_size`.
-struct AsTree {
-  absl::cord_internal::CordRep* rep;
-  char padding[kMaxInline + 1 - sizeof(absl::cord_internal::CordRep*) - 1];
-  char tagged_size;
-};
-
-constexpr char GetOrNull(absl::string_view data, size_t pos) {
-  return pos < data.size() ? data[pos] : '\0';
-}
-
-union InlineData {
-  constexpr InlineData() : as_chars{} {}
-  explicit constexpr InlineData(AsTree tree) : as_tree(tree) {}
-  explicit constexpr InlineData(absl::string_view chars)
-      : as_chars{GetOrNull(chars, 0),  GetOrNull(chars, 1),
-                 GetOrNull(chars, 2),  GetOrNull(chars, 3),
-                 GetOrNull(chars, 4),  GetOrNull(chars, 5),
-                 GetOrNull(chars, 6),  GetOrNull(chars, 7),
-                 GetOrNull(chars, 8),  GetOrNull(chars, 9),
-                 GetOrNull(chars, 10), GetOrNull(chars, 11),
-                 GetOrNull(chars, 12), GetOrNull(chars, 13),
-                 GetOrNull(chars, 14), static_cast<char>(chars.size())} {}
-
-  AsTree as_tree;
-  char as_chars[kMaxInline + 1];
-};
-static_assert(sizeof(InlineData) == kMaxInline + 1, "");
-static_assert(sizeof(AsTree) == sizeof(InlineData), "");
-static_assert(offsetof(AsTree, tagged_size) == kMaxInline, "");
-
-}  // namespace cord_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-#endif  // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/escaping.cc b/third_party/abseil_cpp/absl/strings/internal/escaping.cc
deleted file mode 100644
index c5271286ad..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/escaping.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/escaping.h"
-
-#include "absl/base/internal/endian.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-const char kBase64Chars[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) {
-  // Base64 encodes three bytes of input at a time. If the input is not
-  // divisible by three, we pad as appropriate.
-  //
-  // (from https://tools.ietf.org/html/rfc3548)
-  // Special processing is performed if fewer than 24 bits are available
-  // at the end of the data being encoded.  A full encoding quantum is
-  // always completed at the end of a quantity.  When fewer than 24 input
-  // bits are available in an input group, zero bits are added (on the
-  // right) to form an integral number of 6-bit groups.  Padding at the
-  // end of the data is performed using the '=' character.  Since all base
-  // 64 input is an integral number of octets, only the following cases
-  // can arise:
-
-  // Base64 encodes each three bytes of input into four bytes of output.
-  size_t len = (input_len / 3) * 4;
-
-  if (input_len % 3 == 0) {
-    // (from https://tools.ietf.org/html/rfc3548)
-    // (1) the final quantum of encoding input is an integral multiple of 24
-    // bits; here, the final unit of encoded output will be an integral
-    // multiple of 4 characters with no "=" padding,
-  } else if (input_len % 3 == 1) {
-    // (from https://tools.ietf.org/html/rfc3548)
-    // (2) the final quantum of encoding input is exactly 8 bits; here, the
-    // final unit of encoded output will be two characters followed by two
-    // "=" padding characters, or
-    len += 2;
-    if (do_padding) {
-      len += 2;
-    }
-  } else {  // (input_len % 3 == 2)
-    // (from https://tools.ietf.org/html/rfc3548)
-    // (3) the final quantum of encoding input is exactly 16 bits; here, the
-    // final unit of encoded output will be three characters followed by one
-    // "=" padding character.
-    len += 3;
-    if (do_padding) {
-      len += 1;
-    }
-  }
-
-  assert(len >= input_len);  // make sure we didn't overflow
-  return len;
-}
-
-size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest,
-                            size_t szdest, const char* base64,
-                            bool do_padding) {
-  static const char kPad64 = '=';
-
-  if (szsrc * 4 > szdest * 3) return 0;
-
-  char* cur_dest = dest;
-  const unsigned char* cur_src = src;
-
-  char* const limit_dest = dest + szdest;
-  const unsigned char* const limit_src = src + szsrc;
-
-  // Three bytes of data encodes to four characters of cyphertext.
-  // So we can pump through three-byte chunks atomically.
-  if (szsrc >= 3) {                    // "limit_src - 3" is UB if szsrc < 3.
-    while (cur_src < limit_src - 3) {  // While we have >= 32 bits.
-      uint32_t in = absl::big_endian::Load32(cur_src) >> 8;
-
-      cur_dest[0] = base64[in >> 18];
-      in &= 0x3FFFF;
-      cur_dest[1] = base64[in >> 12];
-      in &= 0xFFF;
-      cur_dest[2] = base64[in >> 6];
-      in &= 0x3F;
-      cur_dest[3] = base64[in];
-
-      cur_dest += 4;
-      cur_src += 3;
-    }
-  }
-  // To save time, we didn't update szdest or szsrc in the loop.  So do it now.
-  szdest = limit_dest - cur_dest;
-  szsrc = limit_src - cur_src;
-
-  /* now deal with the tail (<=3 bytes) */
-  switch (szsrc) {
-    case 0:
-      // Nothing left; nothing more to do.
-      break;
-    case 1: {
-      // One byte left: this encodes to two characters, and (optionally)
-      // two pad characters to round out the four-character cypherblock.
-      if (szdest < 2) return 0;
-      uint32_t in = cur_src[0];
-      cur_dest[0] = base64[in >> 2];
-      in &= 0x3;
-      cur_dest[1] = base64[in << 4];
-      cur_dest += 2;
-      szdest -= 2;
-      if (do_padding) {
-        if (szdest < 2) return 0;
-        cur_dest[0] = kPad64;
-        cur_dest[1] = kPad64;
-        cur_dest += 2;
-        szdest -= 2;
-      }
-      break;
-    }
-    case 2: {
-      // Two bytes left: this encodes to three characters, and (optionally)
-      // one pad character to round out the four-character cypherblock.
-      if (szdest < 3) return 0;
-      uint32_t in = absl::big_endian::Load16(cur_src);
-      cur_dest[0] = base64[in >> 10];
-      in &= 0x3FF;
-      cur_dest[1] = base64[in >> 4];
-      in &= 0x00F;
-      cur_dest[2] = base64[in << 2];
-      cur_dest += 3;
-      szdest -= 3;
-      if (do_padding) {
-        if (szdest < 1) return 0;
-        cur_dest[0] = kPad64;
-        cur_dest += 1;
-        szdest -= 1;
-      }
-      break;
-    }
-    case 3: {
-      // Three bytes left: same as in the big loop above.  We can't do this in
-      // the loop because the loop above always reads 4 bytes, and the fourth
-      // byte is past the end of the input.
-      if (szdest < 4) return 0;
-      uint32_t in = (cur_src[0] << 16) + absl::big_endian::Load16(cur_src + 1);
-      cur_dest[0] = base64[in >> 18];
-      in &= 0x3FFFF;
-      cur_dest[1] = base64[in >> 12];
-      in &= 0xFFF;
-      cur_dest[2] = base64[in >> 6];
-      in &= 0x3F;
-      cur_dest[3] = base64[in];
-      cur_dest += 4;
-      szdest -= 4;
-      break;
-    }
-    default:
-      // Should not be reached: blocks of 4 bytes are handled
-      // in the while loop before this switch statement.
-      ABSL_RAW_LOG(FATAL, "Logic problem? szsrc = %zu", szsrc);
-      break;
-  }
-  return (cur_dest - dest);
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/escaping.h b/third_party/abseil_cpp/absl/strings/internal/escaping.h
deleted file mode 100644
index 6a9ce602d9..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/escaping.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_ESCAPING_H_
-#define ABSL_STRINGS_INTERNAL_ESCAPING_H_
-
-#include <cassert>
-
-#include "absl/strings/internal/resize_uninitialized.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-ABSL_CONST_INIT extern const char kBase64Chars[];
-
-// Calculates how long a string will be when it is base64 encoded given its
-// length and whether or not the result should be padded.
-size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding);
-
-// Base64-encodes `src` using the alphabet provided in `base64` and writes the
-// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars
-// until its length is a multiple of 3. Returns the length of `dest`.
-size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest,
-                            size_t szdest, const char* base64, bool do_padding);
-
-// Base64-encodes `src` using the alphabet provided in `base64` and writes the
-// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars
-// until its length is a multiple of 3.
-template <typename String>
-void Base64EscapeInternal(const unsigned char* src, size_t szsrc, String* dest,
-                          bool do_padding, const char* base64_chars) {
-  const size_t calc_escaped_size =
-      CalculateBase64EscapedLenInternal(szsrc, do_padding);
-  STLStringResizeUninitialized(dest, calc_escaped_size);
-
-  const size_t escaped_len = Base64EscapeInternal(
-      src, szsrc, &(*dest)[0], dest->size(), base64_chars, do_padding);
-  assert(calc_escaped_size == escaped_len);
-  dest->erase(escaped_len);
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_ESCAPING_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/escaping_test_common.h b/third_party/abseil_cpp/absl/strings/internal/escaping_test_common.h
deleted file mode 100644
index 7b18017a08..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/escaping_test_common.h
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This test contains common things needed by both escaping_test.cc and
-// escaping_benchmark.cc.
-
-#ifndef ABSL_STRINGS_INTERNAL_ESCAPING_TEST_COMMON_H_
-#define ABSL_STRINGS_INTERNAL_ESCAPING_TEST_COMMON_H_
-
-#include <array>
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-struct base64_testcase {
-  absl::string_view plaintext;
-  absl::string_view cyphertext;
-};
-
-inline const std::array<base64_testcase, 5>& base64_strings() {
-  static const std::array<base64_testcase, 5> testcase{{
-      // Some google quotes
-      // Cyphertext created with "uuencode (GNU sharutils) 4.6.3"
-      // (Note that we're testing the websafe encoding, though, so if
-      // you add messages, be sure to run "tr -- '+/' '-_'" on the output)
-      { "I was always good at math and science, and I never realized "
-        "that was unusual or somehow undesirable. So one of the things "
-        "I care a lot about is helping to remove that stigma, "
-        "to show girls that you can be feminine, you can like the things "
-        "that girls like, but you can also be really good at technology. "
-        "You can be really good at building things."
-        " - Marissa Meyer, Newsweek, 2010-12-22" "\n",
-
-        "SSB3YXMgYWx3YXlzIGdvb2QgYXQgbWF0aCBhbmQgc2NpZW5jZSwgYW5kIEkg"
-        "bmV2ZXIgcmVhbGl6ZWQgdGhhdCB3YXMgdW51c3VhbCBvciBzb21laG93IHVu"
-        "ZGVzaXJhYmxlLiBTbyBvbmUgb2YgdGhlIHRoaW5ncyBJIGNhcmUgYSBsb3Qg"
-        "YWJvdXQgaXMgaGVscGluZyB0byByZW1vdmUgdGhhdCBzdGlnbWEsIHRvIHNo"
-        "b3cgZ2lybHMgdGhhdCB5b3UgY2FuIGJlIGZlbWluaW5lLCB5b3UgY2FuIGxp"
-        "a2UgdGhlIHRoaW5ncyB0aGF0IGdpcmxzIGxpa2UsIGJ1dCB5b3UgY2FuIGFs"
-        "c28gYmUgcmVhbGx5IGdvb2QgYXQgdGVjaG5vbG9neS4gWW91IGNhbiBiZSBy"
-        "ZWFsbHkgZ29vZCBhdCBidWlsZGluZyB0aGluZ3MuIC0gTWFyaXNzYSBNZXll"
-        "ciwgTmV3c3dlZWssIDIwMTAtMTItMjIK" },
-
-      { "Typical first year for a new cluster: "
-        "~0.5 overheating "
-        "~1 PDU failure "
-        "~1 rack-move "
-        "~1 network rewiring "
-        "~20 rack failures "
-        "~5 racks go wonky "
-        "~8 network maintenances "
-        "~12 router reloads "
-        "~3 router failures "
-        "~dozens of minor 30-second blips for dns "
-        "~1000 individual machine failures "
-        "~thousands of hard drive failures "
-        "slow disks, bad memory, misconfigured machines, flaky machines, etc."
-        " - Jeff Dean, The Joys of Real Hardware" "\n",
-
-        "VHlwaWNhbCBmaXJzdCB5ZWFyIGZvciBhIG5ldyBjbHVzdGVyOiB-MC41IG92"
-        "ZXJoZWF0aW5nIH4xIFBEVSBmYWlsdXJlIH4xIHJhY2stbW92ZSB-MSBuZXR3"
-        "b3JrIHJld2lyaW5nIH4yMCByYWNrIGZhaWx1cmVzIH41IHJhY2tzIGdvIHdv"
-        "bmt5IH44IG5ldHdvcmsgbWFpbnRlbmFuY2VzIH4xMiByb3V0ZXIgcmVsb2Fk"
-        "cyB-MyByb3V0ZXIgZmFpbHVyZXMgfmRvemVucyBvZiBtaW5vciAzMC1zZWNv"
-        "bmQgYmxpcHMgZm9yIGRucyB-MTAwMCBpbmRpdmlkdWFsIG1hY2hpbmUgZmFp"
-        "bHVyZXMgfnRob3VzYW5kcyBvZiBoYXJkIGRyaXZlIGZhaWx1cmVzIHNsb3cg"
-        "ZGlza3MsIGJhZCBtZW1vcnksIG1pc2NvbmZpZ3VyZWQgbWFjaGluZXMsIGZs"
-        "YWt5IG1hY2hpbmVzLCBldGMuIC0gSmVmZiBEZWFuLCBUaGUgSm95cyBvZiBS"
-        "ZWFsIEhhcmR3YXJlCg" },
-
-      { "I'm the head of the webspam team at Google.  "
-        "That means that if you type your name into Google and get porn back, "
-        "it's my fault. Unless you're a porn star, in which case porn is a "
-        "completely reasonable response."
-        " - Matt Cutts, Google Plus" "\n",
-
-        "SSdtIHRoZSBoZWFkIG9mIHRoZSB3ZWJzcGFtIHRlYW0gYXQgR29vZ2xlLiAg"
-        "VGhhdCBtZWFucyB0aGF0IGlmIHlvdSB0eXBlIHlvdXIgbmFtZSBpbnRvIEdv"
-        "b2dsZSBhbmQgZ2V0IHBvcm4gYmFjaywgaXQncyBteSBmYXVsdC4gVW5sZXNz"
-        "IHlvdSdyZSBhIHBvcm4gc3RhciwgaW4gd2hpY2ggY2FzZSBwb3JuIGlzIGEg"
-        "Y29tcGxldGVseSByZWFzb25hYmxlIHJlc3BvbnNlLiAtIE1hdHQgQ3V0dHMs"
-        "IEdvb2dsZSBQbHVzCg" },
-
-      { "It will still be a long time before machines approach human "
-        "intelligence. "
-        "But luckily, machines don't actually have to be intelligent; "
-        "they just have to fake it. Access to a wealth of information, "
-        "combined with a rudimentary decision-making capacity, "
-        "can often be almost as useful. Of course, the results are better yet "
-        "when coupled with intelligence. A reference librarian with access to "
-        "a good search engine is a formidable tool."
-        " - Craig Silverstein, Siemens Pictures of the Future, Spring 2004"
-        "\n",
-
-        "SXQgd2lsbCBzdGlsbCBiZSBhIGxvbmcgdGltZSBiZWZvcmUgbWFjaGluZXMg"
-        "YXBwcm9hY2ggaHVtYW4gaW50ZWxsaWdlbmNlLiBCdXQgbHVja2lseSwgbWFj"
-        "aGluZXMgZG9uJ3QgYWN0dWFsbHkgaGF2ZSB0byBiZSBpbnRlbGxpZ2VudDsg"
-        "dGhleSBqdXN0IGhhdmUgdG8gZmFrZSBpdC4gQWNjZXNzIHRvIGEgd2VhbHRo"
-        "IG9mIGluZm9ybWF0aW9uLCBjb21iaW5lZCB3aXRoIGEgcnVkaW1lbnRhcnkg"
-        "ZGVjaXNpb24tbWFraW5nIGNhcGFjaXR5LCBjYW4gb2Z0ZW4gYmUgYWxtb3N0"
-        "IGFzIHVzZWZ1bC4gT2YgY291cnNlLCB0aGUgcmVzdWx0cyBhcmUgYmV0dGVy"
-        "IHlldCB3aGVuIGNvdXBsZWQgd2l0aCBpbnRlbGxpZ2VuY2UuIEEgcmVmZXJl"
-        "bmNlIGxpYnJhcmlhbiB3aXRoIGFjY2VzcyB0byBhIGdvb2Qgc2VhcmNoIGVu"
-        "Z2luZSBpcyBhIGZvcm1pZGFibGUgdG9vbC4gLSBDcmFpZyBTaWx2ZXJzdGVp"
-        "biwgU2llbWVucyBQaWN0dXJlcyBvZiB0aGUgRnV0dXJlLCBTcHJpbmcgMjAw"
-        "NAo" },
-
-      // Degenerate edge case
-      { "",
-        "" },
-  }};
-
-  return testcase;
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_ESCAPING_TEST_COMMON_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/memutil.cc b/third_party/abseil_cpp/absl/strings/internal/memutil.cc
deleted file mode 100644
index 2519c6881e..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/memutil.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/memutil.h"
-
-#include <cstdlib>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-int memcasecmp(const char* s1, const char* s2, size_t len) {
-  const unsigned char* us1 = reinterpret_cast<const unsigned char*>(s1);
-  const unsigned char* us2 = reinterpret_cast<const unsigned char*>(s2);
-
-  for (size_t i = 0; i < len; i++) {
-    const int diff =
-        int{static_cast<unsigned char>(absl::ascii_tolower(us1[i]))} -
-        int{static_cast<unsigned char>(absl::ascii_tolower(us2[i]))};
-    if (diff != 0) return diff;
-  }
-  return 0;
-}
-
-char* memdup(const char* s, size_t slen) {
-  void* copy;
-  if ((copy = malloc(slen)) == nullptr) return nullptr;
-  memcpy(copy, s, slen);
-  return reinterpret_cast<char*>(copy);
-}
-
-char* memrchr(const char* s, int c, size_t slen) {
-  for (const char* e = s + slen - 1; e >= s; e--) {
-    if (*e == c) return const_cast<char*>(e);
-  }
-  return nullptr;
-}
-
-size_t memspn(const char* s, size_t slen, const char* accept) {
-  const char* p = s;
-  const char* spanp;
-  char c, sc;
-
-cont:
-  c = *p++;
-  if (slen-- == 0) return p - 1 - s;
-  for (spanp = accept; (sc = *spanp++) != '\0';)
-    if (sc == c) goto cont;
-  return p - 1 - s;
-}
-
-size_t memcspn(const char* s, size_t slen, const char* reject) {
-  const char* p = s;
-  const char* spanp;
-  char c, sc;
-
-  while (slen-- != 0) {
-    c = *p++;
-    for (spanp = reject; (sc = *spanp++) != '\0';)
-      if (sc == c) return p - 1 - s;
-  }
-  return p - s;
-}
-
-char* mempbrk(const char* s, size_t slen, const char* accept) {
-  const char* scanp;
-  int sc;
-
-  for (; slen; ++s, --slen) {
-    for (scanp = accept; (sc = *scanp++) != '\0';)
-      if (sc == *s) return const_cast<char*>(s);
-  }
-  return nullptr;
-}
-
-// This is significantly faster for case-sensitive matches with very
-// few possible matches.  See unit test for benchmarks.
-const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
-                     size_t neelen) {
-  if (0 == neelen) {
-    return phaystack;  // even if haylen is 0
-  }
-  if (haylen < neelen) return nullptr;
-
-  const char* match;
-  const char* hayend = phaystack + haylen - neelen + 1;
-  // A static cast is used here to work around the fact that memchr returns
-  // a void* on Posix-compliant systems and const void* on Windows.
-  while ((match = static_cast<const char*>(
-              memchr(phaystack, pneedle[0], hayend - phaystack)))) {
-    if (memcmp(match, pneedle, neelen) == 0)
-      return match;
-    else
-      phaystack = match + 1;
-  }
-  return nullptr;
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/memutil.h b/third_party/abseil_cpp/absl/strings/internal/memutil.h
deleted file mode 100644
index 9ad0535808..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/memutil.h
+++ /dev/null
@@ -1,148 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// These routines provide mem versions of standard C string routines,
-// such as strpbrk.  They function exactly the same as the str versions,
-// so if you wonder what they are, replace the word "mem" by
-// "str" and check out the man page.  I could return void*, as the
-// strutil.h mem*() routines tend to do, but I return char* instead
-// since this is by far the most common way these functions are called.
-//
-// The difference between the mem and str versions is the mem version
-// takes a pointer and a length, rather than a '\0'-terminated string.
-// The memcase* routines defined here assume the locale is "C"
-// (they use absl::ascii_tolower instead of tolower).
-//
-// These routines are based on the BSD library.
-//
-// Here's a list of routines from string.h, and their mem analogues.
-// Functions in lowercase are defined in string.h; those in UPPERCASE
-// are defined here:
-//
-// strlen                  --
-// strcat strncat          MEMCAT
-// strcpy strncpy          memcpy
-// --                      memccpy   (very cool function, btw)
-// --                      memmove
-// --                      memset
-// strcmp strncmp          memcmp
-// strcasecmp strncasecmp  MEMCASECMP
-// strchr                  memchr
-// strcoll                 --
-// strxfrm                 --
-// strdup strndup          MEMDUP
-// strrchr                 MEMRCHR
-// strspn                  MEMSPN
-// strcspn                 MEMCSPN
-// strpbrk                 MEMPBRK
-// strstr                  MEMSTR MEMMEM
-// (g)strcasestr           MEMCASESTR MEMCASEMEM
-// strtok                  --
-// strprefix               MEMPREFIX      (strprefix is from strutil.h)
-// strcaseprefix           MEMCASEPREFIX  (strcaseprefix is from strutil.h)
-// strsuffix               MEMSUFFIX      (strsuffix is from strutil.h)
-// strcasesuffix           MEMCASESUFFIX  (strcasesuffix is from strutil.h)
-// --                      MEMIS
-// --                      MEMCASEIS
-// strcount                MEMCOUNT       (strcount is from strutil.h)
-
-#ifndef ABSL_STRINGS_INTERNAL_MEMUTIL_H_
-#define ABSL_STRINGS_INTERNAL_MEMUTIL_H_
-
-#include <cstddef>
-#include <cstring>
-
-#include "absl/base/port.h"  // disable some warnings on Windows
-#include "absl/strings/ascii.h"  // for absl::ascii_tolower
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-inline char* memcat(char* dest, size_t destlen, const char* src,
-                    size_t srclen) {
-  return reinterpret_cast<char*>(memcpy(dest + destlen, src, srclen));
-}
-
-int memcasecmp(const char* s1, const char* s2, size_t len);
-char* memdup(const char* s, size_t slen);
-char* memrchr(const char* s, int c, size_t slen);
-size_t memspn(const char* s, size_t slen, const char* accept);
-size_t memcspn(const char* s, size_t slen, const char* reject);
-char* mempbrk(const char* s, size_t slen, const char* accept);
-
-// This is for internal use only.  Don't call this directly
-template <bool case_sensitive>
-const char* int_memmatch(const char* haystack, size_t haylen,
-                         const char* needle, size_t neelen) {
-  if (0 == neelen) {
-    return haystack;  // even if haylen is 0
-  }
-  const char* hayend = haystack + haylen;
-  const char* needlestart = needle;
-  const char* needleend = needlestart + neelen;
-
-  for (; haystack < hayend; ++haystack) {
-    char hay = case_sensitive
-                   ? *haystack
-                   : absl::ascii_tolower(static_cast<unsigned char>(*haystack));
-    char nee = case_sensitive
-                   ? *needle
-                   : absl::ascii_tolower(static_cast<unsigned char>(*needle));
-    if (hay == nee) {
-      if (++needle == needleend) {
-        return haystack + 1 - neelen;
-      }
-    } else if (needle != needlestart) {
-      // must back up haystack in case a prefix matched (find "aab" in "aaab")
-      haystack -= needle - needlestart;  // for loop will advance one more
-      needle = needlestart;
-    }
-  }
-  return nullptr;
-}
-
-// These are the guys you can call directly
-inline const char* memstr(const char* phaystack, size_t haylen,
-                          const char* pneedle) {
-  return int_memmatch<true>(phaystack, haylen, pneedle, strlen(pneedle));
-}
-
-inline const char* memcasestr(const char* phaystack, size_t haylen,
-                              const char* pneedle) {
-  return int_memmatch<false>(phaystack, haylen, pneedle, strlen(pneedle));
-}
-
-inline const char* memmem(const char* phaystack, size_t haylen,
-                          const char* pneedle, size_t needlelen) {
-  return int_memmatch<true>(phaystack, haylen, pneedle, needlelen);
-}
-
-inline const char* memcasemem(const char* phaystack, size_t haylen,
-                              const char* pneedle, size_t needlelen) {
-  return int_memmatch<false>(phaystack, haylen, pneedle, needlelen);
-}
-
-// This is significantly faster for case-sensitive matches with very
-// few possible matches.  See unit test for benchmarks.
-const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
-                     size_t neelen);
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_MEMUTIL_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/memutil_benchmark.cc b/third_party/abseil_cpp/absl/strings/internal/memutil_benchmark.cc
deleted file mode 100644
index dc95c3e5e5..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/memutil_benchmark.cc
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/memutil.h"
-
-#include <algorithm>
-#include <cstdlib>
-
-#include "benchmark/benchmark.h"
-#include "absl/strings/ascii.h"
-
-// We fill the haystack with aaaaaaaaaaaaaaaaaa...aaaab.
-// That gives us:
-// - an easy search: 'b'
-// - a medium search: 'ab'.  That means every letter is a possible match.
-// - a pathological search: 'aaaaaa.......aaaaab' (half as many a's as haytack)
-// We benchmark case-sensitive and case-insensitive versions of
-// three memmem implementations:
-// - memmem() from memutil.h
-// - search() from STL
-// - memmatch(), a custom implementation using memchr and memcmp.
-// Here are sample results:
-//
-// Run on (12 X 3800 MHz CPU s)
-// CPU Caches:
-//   L1 Data 32K (x6)
-//   L1 Instruction 32K (x6)
-//   L2 Unified 256K (x6)
-//   L3 Unified 15360K (x1)
-// ----------------------------------------------------------------
-// Benchmark                           Time          CPU Iterations
-// ----------------------------------------------------------------
-// BM_Memmem                        3583 ns      3582 ns     196469  2.59966GB/s
-// BM_MemmemMedium                 13743 ns     13742 ns      50901  693.986MB/s
-// BM_MemmemPathological        13695030 ns  13693977 ns         51  713.133kB/s
-// BM_Memcasemem                    3299 ns      3299 ns     212942  2.82309GB/s
-// BM_MemcasememMedium             16407 ns     16406 ns      42170  581.309MB/s
-// BM_MemcasememPathological    17267745 ns  17266030 ns         41  565.598kB/s
-// BM_Search                        1610 ns      1609 ns     431321  5.78672GB/s
-// BM_SearchMedium                 11111 ns     11110 ns      63001  858.414MB/s
-// BM_SearchPathological        12117390 ns  12116397 ns         58  805.984kB/s
-// BM_Searchcase                    3081 ns      3081 ns     229949  3.02313GB/s
-// BM_SearchcaseMedium             16003 ns     16001 ns      44170  595.998MB/s
-// BM_SearchcasePathological    15823413 ns  15821909 ns         44  617.222kB/s
-// BM_Memmatch                       197 ns       197 ns    3584225  47.2951GB/s
-// BM_MemmatchMedium               52333 ns     52329 ns      13280  182.244MB/s
-// BM_MemmatchPathological        659799 ns    659727 ns       1058  14.4556MB/s
-// BM_Memcasematch                  5460 ns      5460 ns     127606  1.70586GB/s
-// BM_MemcasematchMedium           32861 ns     32857 ns      21258  290.248MB/s
-// BM_MemcasematchPathological  15154243 ns  15153089 ns         46  644.464kB/s
-// BM_MemmemStartup                    5 ns         5 ns  150821500
-// BM_SearchStartup                    5 ns         5 ns  150644203
-// BM_MemmatchStartup                  7 ns         7 ns   97068802
-//
-// Conclusions:
-//
-// The following recommendations are based on the sample results above. However,
-// we have found that the performance of STL search can vary significantly
-// depending on compiler and standard library implementation. We recommend you
-// run the benchmarks for yourself on relevant platforms.
-//
-// If you need case-insensitive, STL search is slightly better than memmem for
-// all cases.
-//
-// Case-sensitive is more subtle:
-// Custom memmatch is _very_ fast at scanning, so if you have very few possible
-// matches in your haystack, that's the way to go. Performance drops
-// significantly with more matches.
-//
-// STL search is slightly faster than memmem in the medium and pathological
-// benchmarks. However, the performance of memmem is currently more dependable
-// across platforms and build configurations.
-
-namespace {
-
-constexpr int kHaystackSize = 10000;
-constexpr int64_t kHaystackSize64 = kHaystackSize;
-const char* MakeHaystack() {
-  char* haystack = new char[kHaystackSize];
-  for (int i = 0; i < kHaystackSize - 1; ++i) haystack[i] = 'a';
-  haystack[kHaystackSize - 1] = 'b';
-  return haystack;
-}
-const char* const kHaystack = MakeHaystack();
-
-void BM_Memmem(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::strings_internal::memmem(kHaystack, kHaystackSize, "b", 1));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_Memmem);
-
-void BM_MemmemMedium(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::strings_internal::memmem(kHaystack, kHaystackSize, "ab", 2));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_MemmemMedium);
-
-void BM_MemmemPathological(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::strings_internal::memmem(
-        kHaystack, kHaystackSize, kHaystack + kHaystackSize / 2,
-        kHaystackSize - kHaystackSize / 2));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_MemmemPathological);
-
-void BM_Memcasemem(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::strings_internal::memcasemem(kHaystack, kHaystackSize, "b", 1));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_Memcasemem);
-
-void BM_MemcasememMedium(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::strings_internal::memcasemem(kHaystack, kHaystackSize, "ab", 2));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_MemcasememMedium);
-
-void BM_MemcasememPathological(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::strings_internal::memcasemem(
-        kHaystack, kHaystackSize, kHaystack + kHaystackSize / 2,
-        kHaystackSize - kHaystackSize / 2));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_MemcasememPathological);
-
-bool case_eq(const char a, const char b) {
-  return absl::ascii_tolower(a) == absl::ascii_tolower(b);
-}
-
-void BM_Search(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
-                                         kHaystack + kHaystackSize - 1,
-                                         kHaystack + kHaystackSize));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_Search);
-
-void BM_SearchMedium(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
-                                         kHaystack + kHaystackSize - 2,
-                                         kHaystack + kHaystackSize));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_SearchMedium);
-
-void BM_SearchPathological(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
-                                         kHaystack + kHaystackSize / 2,
-                                         kHaystack + kHaystackSize));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_SearchPathological);
-
-void BM_Searchcase(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
-                                         kHaystack + kHaystackSize - 1,
-                                         kHaystack + kHaystackSize, case_eq));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_Searchcase);
-
-void BM_SearchcaseMedium(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
-                                         kHaystack + kHaystackSize - 2,
-                                         kHaystack + kHaystackSize, case_eq));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_SearchcaseMedium);
-
-void BM_SearchcasePathological(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
-                                         kHaystack + kHaystackSize / 2,
-                                         kHaystack + kHaystackSize, case_eq));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_SearchcasePathological);
-
-char* memcasechr(const char* s, int c, size_t slen) {
-  c = absl::ascii_tolower(c);
-  for (; slen; ++s, --slen) {
-    if (absl::ascii_tolower(*s) == c) return const_cast<char*>(s);
-  }
-  return nullptr;
-}
-
-const char* memcasematch(const char* phaystack, size_t haylen,
-                         const char* pneedle, size_t neelen) {
-  if (0 == neelen) {
-    return phaystack;  // even if haylen is 0
-  }
-  if (haylen < neelen) return nullptr;
-
-  const char* match;
-  const char* hayend = phaystack + haylen - neelen + 1;
-  while ((match = static_cast<char*>(
-              memcasechr(phaystack, pneedle[0], hayend - phaystack)))) {
-    if (absl::strings_internal::memcasecmp(match, pneedle, neelen) == 0)
-      return match;
-    else
-      phaystack = match + 1;
-  }
-  return nullptr;
-}
-
-void BM_Memmatch(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::strings_internal::memmatch(kHaystack, kHaystackSize, "b", 1));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_Memmatch);
-
-void BM_MemmatchMedium(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::strings_internal::memmatch(kHaystack, kHaystackSize, "ab", 2));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_MemmatchMedium);
-
-void BM_MemmatchPathological(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::strings_internal::memmatch(
-        kHaystack, kHaystackSize, kHaystack + kHaystackSize / 2,
-        kHaystackSize - kHaystackSize / 2));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_MemmatchPathological);
-
-void BM_Memcasematch(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(memcasematch(kHaystack, kHaystackSize, "b", 1));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_Memcasematch);
-
-void BM_MemcasematchMedium(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(memcasematch(kHaystack, kHaystackSize, "ab", 2));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_MemcasematchMedium);
-
-void BM_MemcasematchPathological(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(memcasematch(kHaystack, kHaystackSize,
-                                          kHaystack + kHaystackSize / 2,
-                                          kHaystackSize - kHaystackSize / 2));
-  }
-  state.SetBytesProcessed(kHaystackSize64 * state.iterations());
-}
-BENCHMARK(BM_MemcasematchPathological);
-
-void BM_MemmemStartup(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::strings_internal::memmem(
-        kHaystack + kHaystackSize - 10, 10, kHaystack + kHaystackSize - 1, 1));
-  }
-}
-BENCHMARK(BM_MemmemStartup);
-
-void BM_SearchStartup(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        std::search(kHaystack + kHaystackSize - 10, kHaystack + kHaystackSize,
-                    kHaystack + kHaystackSize - 1, kHaystack + kHaystackSize));
-  }
-}
-BENCHMARK(BM_SearchStartup);
-
-void BM_MemmatchStartup(benchmark::State& state) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(absl::strings_internal::memmatch(
-        kHaystack + kHaystackSize - 10, 10, kHaystack + kHaystackSize - 1, 1));
-  }
-}
-BENCHMARK(BM_MemmatchStartup);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/memutil_test.cc b/third_party/abseil_cpp/absl/strings/internal/memutil_test.cc
deleted file mode 100644
index d8681ddf4e..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/memutil_test.cc
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Unit test for memutil.cc
-
-#include "absl/strings/internal/memutil.h"
-
-#include <cstdlib>
-
-#include "gtest/gtest.h"
-#include "absl/strings/ascii.h"
-
-namespace {
-
-static char* memcasechr(const char* s, int c, size_t slen) {
-  c = absl::ascii_tolower(c);
-  for (; slen; ++s, --slen) {
-    if (absl::ascii_tolower(*s) == c) return const_cast<char*>(s);
-  }
-  return nullptr;
-}
-
-static const char* memcasematch(const char* phaystack, size_t haylen,
-                                const char* pneedle, size_t neelen) {
-  if (0 == neelen) {
-    return phaystack;  // even if haylen is 0
-  }
-  if (haylen < neelen) return nullptr;
-
-  const char* match;
-  const char* hayend = phaystack + haylen - neelen + 1;
-  while ((match = static_cast<char*>(
-              memcasechr(phaystack, pneedle[0], hayend - phaystack)))) {
-    if (absl::strings_internal::memcasecmp(match, pneedle, neelen) == 0)
-      return match;
-    else
-      phaystack = match + 1;
-  }
-  return nullptr;
-}
-
-TEST(MemUtilTest, AllTests) {
-  // check memutil functions
-  char a[1000];
-  absl::strings_internal::memcat(a, 0, "hello", sizeof("hello") - 1);
-  absl::strings_internal::memcat(a, 5, " there", sizeof(" there") - 1);
-
-  EXPECT_EQ(absl::strings_internal::memcasecmp(a, "heLLO there",
-                                               sizeof("hello there") - 1),
-            0);
-  EXPECT_EQ(absl::strings_internal::memcasecmp(a, "heLLO therf",
-                                               sizeof("hello there") - 1),
-            -1);
-  EXPECT_EQ(absl::strings_internal::memcasecmp(a, "heLLO therf",
-                                               sizeof("hello there") - 2),
-            0);
-  EXPECT_EQ(absl::strings_internal::memcasecmp(a, "whatever", 0), 0);
-
-  char* p = absl::strings_internal::memdup("hello", 5);
-  free(p);
-
-  p = absl::strings_internal::memrchr("hello there", 'e',
-                                      sizeof("hello there") - 1);
-  EXPECT_TRUE(p && p[-1] == 'r');
-  p = absl::strings_internal::memrchr("hello there", 'e',
-                                      sizeof("hello there") - 2);
-  EXPECT_TRUE(p && p[-1] == 'h');
-  p = absl::strings_internal::memrchr("hello there", 'u',
-                                      sizeof("hello there") - 1);
-  EXPECT_TRUE(p == nullptr);
-
-  int len = absl::strings_internal::memspn("hello there",
-                                           sizeof("hello there") - 1, "hole");
-  EXPECT_EQ(len, sizeof("hello") - 1);
-  len = absl::strings_internal::memspn("hello there", sizeof("hello there") - 1,
-                                       "u");
-  EXPECT_EQ(len, 0);
-  len = absl::strings_internal::memspn("hello there", sizeof("hello there") - 1,
-                                       "");
-  EXPECT_EQ(len, 0);
-  len = absl::strings_internal::memspn("hello there", sizeof("hello there") - 1,
-                                       "trole h");
-  EXPECT_EQ(len, sizeof("hello there") - 1);
-  len = absl::strings_internal::memspn("hello there!",
-                                       sizeof("hello there!") - 1, "trole h");
-  EXPECT_EQ(len, sizeof("hello there") - 1);
-  len = absl::strings_internal::memspn("hello there!",
-                                       sizeof("hello there!") - 2, "trole h!");
-  EXPECT_EQ(len, sizeof("hello there!") - 2);
-
-  len = absl::strings_internal::memcspn("hello there",
-                                        sizeof("hello there") - 1, "leho");
-  EXPECT_EQ(len, 0);
-  len = absl::strings_internal::memcspn("hello there",
-                                        sizeof("hello there") - 1, "u");
-  EXPECT_EQ(len, sizeof("hello there") - 1);
-  len = absl::strings_internal::memcspn("hello there",
-                                        sizeof("hello there") - 1, "");
-  EXPECT_EQ(len, sizeof("hello there") - 1);
-  len = absl::strings_internal::memcspn("hello there",
-                                        sizeof("hello there") - 1, " ");
-  EXPECT_EQ(len, 5);
-
-  p = absl::strings_internal::mempbrk("hello there", sizeof("hello there") - 1,
-                                      "leho");
-  EXPECT_TRUE(p && p[1] == 'e' && p[2] == 'l');
-  p = absl::strings_internal::mempbrk("hello there", sizeof("hello there") - 1,
-                                      "nu");
-  EXPECT_TRUE(p == nullptr);
-  p = absl::strings_internal::mempbrk("hello there!",
-                                      sizeof("hello there!") - 2, "!");
-  EXPECT_TRUE(p == nullptr);
-  p = absl::strings_internal::mempbrk("hello there", sizeof("hello there") - 1,
-                                      " t ");
-  EXPECT_TRUE(p && p[-1] == 'o' && p[1] == 't');
-
-  {
-    const char kHaystack[] = "0123456789";
-    EXPECT_EQ(absl::strings_internal::memmem(kHaystack, 0, "", 0), kHaystack);
-    EXPECT_EQ(absl::strings_internal::memmem(kHaystack, 10, "012", 3),
-              kHaystack);
-    EXPECT_EQ(absl::strings_internal::memmem(kHaystack, 10, "0xx", 1),
-              kHaystack);
-    EXPECT_EQ(absl::strings_internal::memmem(kHaystack, 10, "789", 3),
-              kHaystack + 7);
-    EXPECT_EQ(absl::strings_internal::memmem(kHaystack, 10, "9xx", 1),
-              kHaystack + 9);
-    EXPECT_TRUE(absl::strings_internal::memmem(kHaystack, 10, "9xx", 3) ==
-                nullptr);
-    EXPECT_TRUE(absl::strings_internal::memmem(kHaystack, 10, "xxx", 1) ==
-                nullptr);
-  }
-  {
-    const char kHaystack[] = "aBcDeFgHiJ";
-    EXPECT_EQ(absl::strings_internal::memcasemem(kHaystack, 0, "", 0),
-              kHaystack);
-    EXPECT_EQ(absl::strings_internal::memcasemem(kHaystack, 10, "Abc", 3),
-              kHaystack);
-    EXPECT_EQ(absl::strings_internal::memcasemem(kHaystack, 10, "Axx", 1),
-              kHaystack);
-    EXPECT_EQ(absl::strings_internal::memcasemem(kHaystack, 10, "hIj", 3),
-              kHaystack + 7);
-    EXPECT_EQ(absl::strings_internal::memcasemem(kHaystack, 10, "jxx", 1),
-              kHaystack + 9);
-    EXPECT_TRUE(absl::strings_internal::memcasemem(kHaystack, 10, "jxx", 3) ==
-                nullptr);
-    EXPECT_TRUE(absl::strings_internal::memcasemem(kHaystack, 10, "xxx", 1) ==
-                nullptr);
-  }
-  {
-    const char kHaystack[] = "0123456789";
-    EXPECT_EQ(absl::strings_internal::memmatch(kHaystack, 0, "", 0), kHaystack);
-    EXPECT_EQ(absl::strings_internal::memmatch(kHaystack, 10, "012", 3),
-              kHaystack);
-    EXPECT_EQ(absl::strings_internal::memmatch(kHaystack, 10, "0xx", 1),
-              kHaystack);
-    EXPECT_EQ(absl::strings_internal::memmatch(kHaystack, 10, "789", 3),
-              kHaystack + 7);
-    EXPECT_EQ(absl::strings_internal::memmatch(kHaystack, 10, "9xx", 1),
-              kHaystack + 9);
-    EXPECT_TRUE(absl::strings_internal::memmatch(kHaystack, 10, "9xx", 3) ==
-                nullptr);
-    EXPECT_TRUE(absl::strings_internal::memmatch(kHaystack, 10, "xxx", 1) ==
-                nullptr);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/numbers_test_common.h b/third_party/abseil_cpp/absl/strings/internal/numbers_test_common.h
deleted file mode 100644
index eaa88a8897..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/numbers_test_common.h
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This file contains common things needed by numbers_test.cc,
-// numbers_legacy_test.cc and numbers_benchmark.cc.
-
-#ifndef ABSL_STRINGS_INTERNAL_NUMBERS_TEST_COMMON_H_
-#define ABSL_STRINGS_INTERNAL_NUMBERS_TEST_COMMON_H_
-
-#include <array>
-#include <cstdint>
-#include <limits>
-#include <string>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-template <typename IntType>
-inline bool Itoa(IntType value, int base, std::string* destination) {
-  destination->clear();
-  if (base <= 1 || base > 36) {
-    return false;
-  }
-
-  if (value == 0) {
-    destination->push_back('0');
-    return true;
-  }
-
-  bool negative = value < 0;
-  while (value != 0) {
-    const IntType next_value = value / base;
-    // Can't use std::abs here because of problems when IntType is unsigned.
-    int remainder =
-        static_cast<int>(value > next_value * base ? value - next_value * base
-                                                   : next_value * base - value);
-    char c = remainder < 10 ? '0' + remainder : 'A' + remainder - 10;
-    destination->insert(0, 1, c);
-    value = next_value;
-  }
-
-  if (negative) {
-    destination->insert(0, 1, '-');
-  }
-  return true;
-}
-
-struct uint32_test_case {
-  const char* str;
-  bool expect_ok;
-  int base;  // base to pass to the conversion function
-  uint32_t expected;
-};
-
-inline const std::array<uint32_test_case, 27>& strtouint32_test_cases() {
-  static const std::array<uint32_test_case, 27> test_cases{{
-      {"0xffffffff", true, 16, (std::numeric_limits<uint32_t>::max)()},
-      {"0x34234324", true, 16, 0x34234324},
-      {"34234324", true, 16, 0x34234324},
-      {"0", true, 16, 0},
-      {" \t\n 0xffffffff", true, 16, (std::numeric_limits<uint32_t>::max)()},
-      {" \f\v 46", true, 10, 46},  // must accept weird whitespace
-      {" \t\n 72717222", true, 8, 072717222},
-      {" \t\n 072717222", true, 8, 072717222},
-      {" \t\n 072717228", false, 8, 07271722},
-      {"0", true, 0, 0},
-
-      // Base-10 version.
-      {"34234324", true, 0, 34234324},
-      {"4294967295", true, 0, (std::numeric_limits<uint32_t>::max)()},
-      {"34234324 \n\t", true, 10, 34234324},
-
-      // Unusual base
-      {"0", true, 3, 0},
-      {"2", true, 3, 2},
-      {"11", true, 3, 4},
-
-      // Invalid uints.
-      {"", false, 0, 0},
-      {"  ", false, 0, 0},
-      {"abc", false, 0, 0},  // would be valid hex, but prefix is missing
-      {"34234324a", false, 0, 34234324},
-      {"34234.3", false, 0, 34234},
-      {"-1", false, 0, 0},
-      {"   -123", false, 0, 0},
-      {" \t\n -123", false, 0, 0},
-
-      // Out of bounds.
-      {"4294967296", false, 0, (std::numeric_limits<uint32_t>::max)()},
-      {"0x100000000", false, 0, (std::numeric_limits<uint32_t>::max)()},
-      {nullptr, false, 0, 0},
-  }};
-  return test_cases;
-}
-
-struct uint64_test_case {
-  const char* str;
-  bool expect_ok;
-  int base;
-  uint64_t expected;
-};
-
-inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
-  static const std::array<uint64_test_case, 34> test_cases{{
-      {"0x3423432448783446", true, 16, int64_t{0x3423432448783446}},
-      {"3423432448783446", true, 16, int64_t{0x3423432448783446}},
-
-      {"0", true, 16, 0},
-      {"000", true, 0, 0},
-      {"0", true, 0, 0},
-      {" \t\n 0xffffffffffffffff", true, 16,
-       (std::numeric_limits<uint64_t>::max)()},
-
-      {"012345670123456701234", true, 8, int64_t{012345670123456701234}},
-      {"12345670123456701234", true, 8, int64_t{012345670123456701234}},
-
-      {"12845670123456701234", false, 8, 0},
-
-      // Base-10 version.
-      {"34234324487834466", true, 0, int64_t{34234324487834466}},
-
-      {" \t\n 18446744073709551615", true, 0,
-       (std::numeric_limits<uint64_t>::max)()},
-
-      {"34234324487834466 \n\t ", true, 0, int64_t{34234324487834466}},
-
-      {" \f\v 46", true, 10, 46},  // must accept weird whitespace
-
-      // Unusual base
-      {"0", true, 3, 0},
-      {"2", true, 3, 2},
-      {"11", true, 3, 4},
-
-      {"0", true, 0, 0},
-
-      // Invalid uints.
-      {"", false, 0, 0},
-      {"  ", false, 0, 0},
-      {"abc", false, 0, 0},
-      {"34234324487834466a", false, 0, 0},
-      {"34234487834466.3", false, 0, 0},
-      {"-1", false, 0, 0},
-      {"   -123", false, 0, 0},
-      {" \t\n -123", false, 0, 0},
-
-      // Out of bounds.
-      {"18446744073709551616", false, 10, 0},
-      {"18446744073709551616", false, 0, 0},
-      {"0x10000000000000000", false, 16,
-       (std::numeric_limits<uint64_t>::max)()},
-      {"0X10000000000000000", false, 16,
-       (std::numeric_limits<uint64_t>::max)()},  // 0X versus 0x.
-      {"0x10000000000000000", false, 0, (std::numeric_limits<uint64_t>::max)()},
-      {"0X10000000000000000", false, 0,
-       (std::numeric_limits<uint64_t>::max)()},  // 0X versus 0x.
-
-      {"0x1234", true, 16, 0x1234},
-
-      // Base-10 string version.
-      {"1234", true, 0, 1234},
-      {nullptr, false, 0, 0},
-  }};
-  return test_cases;
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_NUMBERS_TEST_COMMON_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/ostringstream.cc b/third_party/abseil_cpp/absl/strings/internal/ostringstream.cc
deleted file mode 100644
index 05324c780c..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/ostringstream.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/ostringstream.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-OStringStream::Buf::int_type OStringStream::overflow(int c) {
-  assert(s_);
-  if (!Buf::traits_type::eq_int_type(c, Buf::traits_type::eof()))
-    s_->push_back(static_cast<char>(c));
-  return 1;
-}
-
-std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) {
-  assert(s_);
-  s_->append(s, n);
-  return n;
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/ostringstream.h b/third_party/abseil_cpp/absl/strings/internal/ostringstream.h
deleted file mode 100644
index d25d60473f..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/ostringstream.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_
-#define ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_
-
-#include <cassert>
-#include <ostream>
-#include <streambuf>
-#include <string>
-
-#include "absl/base/port.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-// The same as std::ostringstream but appends to a user-specified std::string,
-// and is faster. It is ~70% faster to create, ~50% faster to write to, and
-// completely free to extract the result std::string.
-//
-//   std::string s;
-//   OStringStream strm(&s);
-//   strm << 42 << ' ' << 3.14;  // appends to `s`
-//
-// The stream object doesn't have to be named. Starting from C++11 operator<<
-// works with rvalues of std::ostream.
-//
-//   std::string s;
-//   OStringStream(&s) << 42 << ' ' << 3.14;  // appends to `s`
-//
-// OStringStream is faster to create than std::ostringstream but it's still
-// relatively slow. Avoid creating multiple streams where a single stream will
-// do.
-//
-// Creates unnecessary instances of OStringStream: slow.
-//
-//   std::string s;
-//   OStringStream(&s) << 42;
-//   OStringStream(&s) << ' ';
-//   OStringStream(&s) << 3.14;
-//
-// Creates a single instance of OStringStream and reuses it: fast.
-//
-//   std::string s;
-//   OStringStream strm(&s);
-//   strm << 42;
-//   strm << ' ';
-//   strm << 3.14;
-//
-// Note: flush() has no effect. No reason to call it.
-class OStringStream : private std::basic_streambuf<char>, public std::ostream {
- public:
-  // The argument can be null, in which case you'll need to call str(p) with a
-  // non-null argument before you can write to the stream.
-  //
-  // The destructor of OStringStream doesn't use the std::string. It's OK to
-  // destroy the std::string before the stream.
-  explicit OStringStream(std::string* s) : std::ostream(this), s_(s) {}
-
-  std::string* str() { return s_; }
-  const std::string* str() const { return s_; }
-  void str(std::string* s) { s_ = s; }
-
- private:
-  using Buf = std::basic_streambuf<char>;
-
-  Buf::int_type overflow(int c) override;
-  std::streamsize xsputn(const char* s, std::streamsize n) override;
-
-  std::string* s_;
-};
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/ostringstream_benchmark.cc b/third_party/abseil_cpp/absl/strings/internal/ostringstream_benchmark.cc
deleted file mode 100644
index 5979f18236..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/ostringstream_benchmark.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/ostringstream.h"
-
-#include <sstream>
-#include <string>
-
-#include "benchmark/benchmark.h"
-
-namespace {
-
-enum StringType {
-  kNone,
-  kStdString,
-};
-
-// Benchmarks for std::ostringstream.
-template <StringType kOutput>
-void BM_StdStream(benchmark::State& state) {
-  const int num_writes = state.range(0);
-  const int bytes_per_write = state.range(1);
-  const std::string payload(bytes_per_write, 'x');
-  for (auto _ : state) {
-    std::ostringstream strm;
-    benchmark::DoNotOptimize(strm);
-    for (int i = 0; i != num_writes; ++i) {
-      strm << payload;
-    }
-    switch (kOutput) {
-      case kNone: {
-        break;
-      }
-      case kStdString: {
-        std::string s = strm.str();
-        benchmark::DoNotOptimize(s);
-        break;
-      }
-    }
-  }
-}
-
-// Create the stream, optionally write to it, then destroy it.
-BENCHMARK_TEMPLATE(BM_StdStream, kNone)
-    ->ArgPair(0, 0)
-    ->ArgPair(1, 16)   // 16 bytes is small enough for SSO
-    ->ArgPair(1, 256)  // 256 bytes requires heap allocation
-    ->ArgPair(1024, 256);
-// Create the stream, write to it, get std::string out, then destroy.
-BENCHMARK_TEMPLATE(BM_StdStream, kStdString)
-    ->ArgPair(1, 16)   // 16 bytes is small enough for SSO
-    ->ArgPair(1, 256)  // 256 bytes requires heap allocation
-    ->ArgPair(1024, 256);
-
-// Benchmarks for OStringStream.
-template <StringType kOutput>
-void BM_CustomStream(benchmark::State& state) {
-  const int num_writes = state.range(0);
-  const int bytes_per_write = state.range(1);
-  const std::string payload(bytes_per_write, 'x');
-  for (auto _ : state) {
-    std::string out;
-    absl::strings_internal::OStringStream strm(&out);
-    benchmark::DoNotOptimize(strm);
-    for (int i = 0; i != num_writes; ++i) {
-      strm << payload;
-    }
-    switch (kOutput) {
-      case kNone: {
-        break;
-      }
-      case kStdString: {
-        std::string s = out;
-        benchmark::DoNotOptimize(s);
-        break;
-      }
-    }
-  }
-}
-
-// Create the stream, optionally write to it, then destroy it.
-BENCHMARK_TEMPLATE(BM_CustomStream, kNone)
-    ->ArgPair(0, 0)
-    ->ArgPair(1, 16)   // 16 bytes is small enough for SSO
-    ->ArgPair(1, 256)  // 256 bytes requires heap allocation
-    ->ArgPair(1024, 256);
-// Create the stream, write to it, get std::string out, then destroy.
-// It's not useful in practice to extract std::string from OStringStream; we
-// measure it for completeness.
-BENCHMARK_TEMPLATE(BM_CustomStream, kStdString)
-    ->ArgPair(1, 16)   // 16 bytes is small enough for SSO
-    ->ArgPair(1, 256)  // 256 bytes requires heap allocation
-    ->ArgPair(1024, 256);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/ostringstream_test.cc b/third_party/abseil_cpp/absl/strings/internal/ostringstream_test.cc
deleted file mode 100644
index 2879e50eb3..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/ostringstream_test.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/ostringstream.h"
-
-#include <memory>
-#include <ostream>
-#include <string>
-#include <type_traits>
-
-#include "gtest/gtest.h"
-
-namespace {
-
-TEST(OStringStream, IsOStream) {
-  static_assert(
-      std::is_base_of<std::ostream, absl::strings_internal::OStringStream>(),
-      "");
-}
-
-TEST(OStringStream, ConstructDestroy) {
-  {
-    absl::strings_internal::OStringStream strm(nullptr);
-    EXPECT_EQ(nullptr, strm.str());
-  }
-  {
-    std::string s = "abc";
-    {
-      absl::strings_internal::OStringStream strm(&s);
-      EXPECT_EQ(&s, strm.str());
-    }
-    EXPECT_EQ("abc", s);
-  }
-  {
-    std::unique_ptr<std::string> s(new std::string);
-    absl::strings_internal::OStringStream strm(s.get());
-    s.reset();
-  }
-}
-
-TEST(OStringStream, Str) {
-  std::string s1;
-  absl::strings_internal::OStringStream strm(&s1);
-  const absl::strings_internal::OStringStream& c_strm(strm);
-
-  static_assert(std::is_same<decltype(strm.str()), std::string*>(), "");
-  static_assert(std::is_same<decltype(c_strm.str()), const std::string*>(), "");
-
-  EXPECT_EQ(&s1, strm.str());
-  EXPECT_EQ(&s1, c_strm.str());
-
-  strm.str(&s1);
-  EXPECT_EQ(&s1, strm.str());
-  EXPECT_EQ(&s1, c_strm.str());
-
-  std::string s2;
-  strm.str(&s2);
-  EXPECT_EQ(&s2, strm.str());
-  EXPECT_EQ(&s2, c_strm.str());
-
-  strm.str(nullptr);
-  EXPECT_EQ(nullptr, strm.str());
-  EXPECT_EQ(nullptr, c_strm.str());
-}
-
-TEST(OStreamStream, WriteToLValue) {
-  std::string s = "abc";
-  {
-    absl::strings_internal::OStringStream strm(&s);
-    EXPECT_EQ("abc", s);
-    strm << "";
-    EXPECT_EQ("abc", s);
-    strm << 42;
-    EXPECT_EQ("abc42", s);
-    strm << 'x' << 'y';
-    EXPECT_EQ("abc42xy", s);
-  }
-  EXPECT_EQ("abc42xy", s);
-}
-
-TEST(OStreamStream, WriteToRValue) {
-  std::string s = "abc";
-  absl::strings_internal::OStringStream(&s) << "";
-  EXPECT_EQ("abc", s);
-  absl::strings_internal::OStringStream(&s) << 42;
-  EXPECT_EQ("abc42", s);
-  absl::strings_internal::OStringStream(&s) << 'x' << 'y';
-  EXPECT_EQ("abc42xy", s);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/pow10_helper.cc b/third_party/abseil_cpp/absl/strings/internal/pow10_helper.cc
deleted file mode 100644
index 42e96c3425..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/pow10_helper.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/pow10_helper.h"
-
-#include <cmath>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-namespace {
-
-// The exact value of 1e23 falls precisely halfway between two representable
-// doubles. Furthermore, the rounding rules we prefer (break ties by rounding
-// to the nearest even) dictate in this case that the number should be rounded
-// down, but this is not completely specified for floating-point literals in
-// C++. (It just says to use the default rounding mode of the standard
-// library.) We ensure the result we want by using a number that has an
-// unambiguous correctly rounded answer.
-constexpr double k1e23 = 9999999999999999e7;
-
-constexpr double kPowersOfTen[] = {
-    0.0,    1e-323, 1e-322, 1e-321, 1e-320, 1e-319, 1e-318, 1e-317, 1e-316,
-    1e-315, 1e-314, 1e-313, 1e-312, 1e-311, 1e-310, 1e-309, 1e-308, 1e-307,
-    1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299, 1e-298,
-    1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, 1e-289,
-    1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, 1e-280,
-    1e-279, 1e-278, 1e-277, 1e-276, 1e-275, 1e-274, 1e-273, 1e-272, 1e-271,
-    1e-270, 1e-269, 1e-268, 1e-267, 1e-266, 1e-265, 1e-264, 1e-263, 1e-262,
-    1e-261, 1e-260, 1e-259, 1e-258, 1e-257, 1e-256, 1e-255, 1e-254, 1e-253,
-    1e-252, 1e-251, 1e-250, 1e-249, 1e-248, 1e-247, 1e-246, 1e-245, 1e-244,
-    1e-243, 1e-242, 1e-241, 1e-240, 1e-239, 1e-238, 1e-237, 1e-236, 1e-235,
-    1e-234, 1e-233, 1e-232, 1e-231, 1e-230, 1e-229, 1e-228, 1e-227, 1e-226,
-    1e-225, 1e-224, 1e-223, 1e-222, 1e-221, 1e-220, 1e-219, 1e-218, 1e-217,
-    1e-216, 1e-215, 1e-214, 1e-213, 1e-212, 1e-211, 1e-210, 1e-209, 1e-208,
-    1e-207, 1e-206, 1e-205, 1e-204, 1e-203, 1e-202, 1e-201, 1e-200, 1e-199,
-    1e-198, 1e-197, 1e-196, 1e-195, 1e-194, 1e-193, 1e-192, 1e-191, 1e-190,
-    1e-189, 1e-188, 1e-187, 1e-186, 1e-185, 1e-184, 1e-183, 1e-182, 1e-181,
-    1e-180, 1e-179, 1e-178, 1e-177, 1e-176, 1e-175, 1e-174, 1e-173, 1e-172,
-    1e-171, 1e-170, 1e-169, 1e-168, 1e-167, 1e-166, 1e-165, 1e-164, 1e-163,
-    1e-162, 1e-161, 1e-160, 1e-159, 1e-158, 1e-157, 1e-156, 1e-155, 1e-154,
-    1e-153, 1e-152, 1e-151, 1e-150, 1e-149, 1e-148, 1e-147, 1e-146, 1e-145,
-    1e-144, 1e-143, 1e-142, 1e-141, 1e-140, 1e-139, 1e-138, 1e-137, 1e-136,
-    1e-135, 1e-134, 1e-133, 1e-132, 1e-131, 1e-130, 1e-129, 1e-128, 1e-127,
-    1e-126, 1e-125, 1e-124, 1e-123, 1e-122, 1e-121, 1e-120, 1e-119, 1e-118,
-    1e-117, 1e-116, 1e-115, 1e-114, 1e-113, 1e-112, 1e-111, 1e-110, 1e-109,
-    1e-108, 1e-107, 1e-106, 1e-105, 1e-104, 1e-103, 1e-102, 1e-101, 1e-100,
-    1e-99,  1e-98,  1e-97,  1e-96,  1e-95,  1e-94,  1e-93,  1e-92,  1e-91,
-    1e-90,  1e-89,  1e-88,  1e-87,  1e-86,  1e-85,  1e-84,  1e-83,  1e-82,
-    1e-81,  1e-80,  1e-79,  1e-78,  1e-77,  1e-76,  1e-75,  1e-74,  1e-73,
-    1e-72,  1e-71,  1e-70,  1e-69,  1e-68,  1e-67,  1e-66,  1e-65,  1e-64,
-    1e-63,  1e-62,  1e-61,  1e-60,  1e-59,  1e-58,  1e-57,  1e-56,  1e-55,
-    1e-54,  1e-53,  1e-52,  1e-51,  1e-50,  1e-49,  1e-48,  1e-47,  1e-46,
-    1e-45,  1e-44,  1e-43,  1e-42,  1e-41,  1e-40,  1e-39,  1e-38,  1e-37,
-    1e-36,  1e-35,  1e-34,  1e-33,  1e-32,  1e-31,  1e-30,  1e-29,  1e-28,
-    1e-27,  1e-26,  1e-25,  1e-24,  1e-23,  1e-22,  1e-21,  1e-20,  1e-19,
-    1e-18,  1e-17,  1e-16,  1e-15,  1e-14,  1e-13,  1e-12,  1e-11,  1e-10,
-    1e-9,   1e-8,   1e-7,   1e-6,   1e-5,   1e-4,   1e-3,   1e-2,   1e-1,
-    1e+0,   1e+1,   1e+2,   1e+3,   1e+4,   1e+5,   1e+6,   1e+7,   1e+8,
-    1e+9,   1e+10,  1e+11,  1e+12,  1e+13,  1e+14,  1e+15,  1e+16,  1e+17,
-    1e+18,  1e+19,  1e+20,  1e+21,  1e+22,  k1e23,  1e+24,  1e+25,  1e+26,
-    1e+27,  1e+28,  1e+29,  1e+30,  1e+31,  1e+32,  1e+33,  1e+34,  1e+35,
-    1e+36,  1e+37,  1e+38,  1e+39,  1e+40,  1e+41,  1e+42,  1e+43,  1e+44,
-    1e+45,  1e+46,  1e+47,  1e+48,  1e+49,  1e+50,  1e+51,  1e+52,  1e+53,
-    1e+54,  1e+55,  1e+56,  1e+57,  1e+58,  1e+59,  1e+60,  1e+61,  1e+62,
-    1e+63,  1e+64,  1e+65,  1e+66,  1e+67,  1e+68,  1e+69,  1e+70,  1e+71,
-    1e+72,  1e+73,  1e+74,  1e+75,  1e+76,  1e+77,  1e+78,  1e+79,  1e+80,
-    1e+81,  1e+82,  1e+83,  1e+84,  1e+85,  1e+86,  1e+87,  1e+88,  1e+89,
-    1e+90,  1e+91,  1e+92,  1e+93,  1e+94,  1e+95,  1e+96,  1e+97,  1e+98,
-    1e+99,  1e+100, 1e+101, 1e+102, 1e+103, 1e+104, 1e+105, 1e+106, 1e+107,
-    1e+108, 1e+109, 1e+110, 1e+111, 1e+112, 1e+113, 1e+114, 1e+115, 1e+116,
-    1e+117, 1e+118, 1e+119, 1e+120, 1e+121, 1e+122, 1e+123, 1e+124, 1e+125,
-    1e+126, 1e+127, 1e+128, 1e+129, 1e+130, 1e+131, 1e+132, 1e+133, 1e+134,
-    1e+135, 1e+136, 1e+137, 1e+138, 1e+139, 1e+140, 1e+141, 1e+142, 1e+143,
-    1e+144, 1e+145, 1e+146, 1e+147, 1e+148, 1e+149, 1e+150, 1e+151, 1e+152,
-    1e+153, 1e+154, 1e+155, 1e+156, 1e+157, 1e+158, 1e+159, 1e+160, 1e+161,
-    1e+162, 1e+163, 1e+164, 1e+165, 1e+166, 1e+167, 1e+168, 1e+169, 1e+170,
-    1e+171, 1e+172, 1e+173, 1e+174, 1e+175, 1e+176, 1e+177, 1e+178, 1e+179,
-    1e+180, 1e+181, 1e+182, 1e+183, 1e+184, 1e+185, 1e+186, 1e+187, 1e+188,
-    1e+189, 1e+190, 1e+191, 1e+192, 1e+193, 1e+194, 1e+195, 1e+196, 1e+197,
-    1e+198, 1e+199, 1e+200, 1e+201, 1e+202, 1e+203, 1e+204, 1e+205, 1e+206,
-    1e+207, 1e+208, 1e+209, 1e+210, 1e+211, 1e+212, 1e+213, 1e+214, 1e+215,
-    1e+216, 1e+217, 1e+218, 1e+219, 1e+220, 1e+221, 1e+222, 1e+223, 1e+224,
-    1e+225, 1e+226, 1e+227, 1e+228, 1e+229, 1e+230, 1e+231, 1e+232, 1e+233,
-    1e+234, 1e+235, 1e+236, 1e+237, 1e+238, 1e+239, 1e+240, 1e+241, 1e+242,
-    1e+243, 1e+244, 1e+245, 1e+246, 1e+247, 1e+248, 1e+249, 1e+250, 1e+251,
-    1e+252, 1e+253, 1e+254, 1e+255, 1e+256, 1e+257, 1e+258, 1e+259, 1e+260,
-    1e+261, 1e+262, 1e+263, 1e+264, 1e+265, 1e+266, 1e+267, 1e+268, 1e+269,
-    1e+270, 1e+271, 1e+272, 1e+273, 1e+274, 1e+275, 1e+276, 1e+277, 1e+278,
-    1e+279, 1e+280, 1e+281, 1e+282, 1e+283, 1e+284, 1e+285, 1e+286, 1e+287,
-    1e+288, 1e+289, 1e+290, 1e+291, 1e+292, 1e+293, 1e+294, 1e+295, 1e+296,
-    1e+297, 1e+298, 1e+299, 1e+300, 1e+301, 1e+302, 1e+303, 1e+304, 1e+305,
-    1e+306, 1e+307, 1e+308,
-};
-
-}  // namespace
-
-double Pow10(int exp) {
-  if (exp < -324) {
-    return 0.0;
-  } else if (exp > 308) {
-    return INFINITY;
-  } else {
-    return kPowersOfTen[exp + 324];
-  }
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/pow10_helper.h b/third_party/abseil_cpp/absl/strings/internal/pow10_helper.h
deleted file mode 100644
index c37c2c3ffe..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/pow10_helper.h
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This test helper library contains a table of powers of 10, to guarantee
-// precise values are computed across the full range of doubles. We can't rely
-// on the pow() function, because not all standard libraries ship a version
-// that is precise.
-#ifndef ABSL_STRINGS_INTERNAL_POW10_HELPER_H_
-#define ABSL_STRINGS_INTERNAL_POW10_HELPER_H_
-
-#include <vector>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-// Computes the precise value of 10^exp. (I.e. the nearest representable
-// double to the exact value, rounding to nearest-even in the (single) case of
-// being exactly halfway between.)
-double Pow10(int exp);
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_POW10_HELPER_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/pow10_helper_test.cc b/third_party/abseil_cpp/absl/strings/internal/pow10_helper_test.cc
deleted file mode 100644
index a4ff76d31e..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/pow10_helper_test.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/pow10_helper.h"
-
-#include <cmath>
-
-#include "gtest/gtest.h"
-#include "absl/strings/str_format.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-namespace {
-
-struct TestCase {
-  int power;           // Testing Pow10(power)
-  uint64_t significand;  // Raw bits of the expected value
-  int radix;           // significand is adjusted by 2^radix
-};
-
-TEST(Pow10HelperTest, Works) {
-  // The logic in pow10_helper.cc is so simple that theoretically we don't even
-  // need a test. However, we're paranoid and believe that there may be
-  // compilers that don't round floating-point literals correctly, even though
-  // it is specified by the standard. We check various edge cases, just to be
-  // sure.
-  constexpr TestCase kTestCases[] = {
-      // Subnormals
-      {-323, 0x2, -1074},
-      {-322, 0x14, -1074},
-      {-321, 0xca, -1074},
-      {-320, 0x7e8, -1074},
-      {-319, 0x4f10, -1074},
-      {-318, 0x316a2, -1074},
-      {-317, 0x1ee257, -1074},
-      {-316, 0x134d761, -1074},
-      {-315, 0xc1069cd, -1074},
-      {-314, 0x78a42205, -1074},
-      {-313, 0x4b6695433, -1074},
-      {-312, 0x2f201d49fb, -1074},
-      {-311, 0x1d74124e3d1, -1074},
-      {-310, 0x12688b70e62b, -1074},
-      {-309, 0xb8157268fdaf, -1074},
-      {-308, 0x730d67819e8d2, -1074},
-      // Values that are very close to rounding the other way.
-      // Comment shows difference of significand from the true value.
-      {-307, 0x11fa182c40c60d, -1072},  // -.4588
-      {-290, 0x18f2b061aea072, -1016},  //  .4854
-      {-276, 0x11BA03F5B21000, -969},   //  .4709
-      {-259, 0x1899C2F6732210, -913},   //  .4830
-      {-252, 0x1D53844EE47DD1, -890},   // -.4743
-      {-227, 0x1E5297287C2F45, -807},   // -.4708
-      {-198, 0x1322E220A5B17E, -710},   // -.4714
-      {-195, 0x12B010D3E1CF56, -700},   //  .4928
-      {-192, 0x123FF06EEA847A, -690},   //  .4968
-      {-163, 0x1708D0F84D3DE7, -594},   // -.4977
-      {-145, 0x13FAAC3E3FA1F3, -534},   // -.4785
-      {-111, 0x133D4032C2C7F5, -421},   //  .4774
-      {-106, 0x1D5B561574765B, -405},   // -.4869
-      {-104, 0x16EF5B40C2FC77, -398},   // -.4741
-      {-88, 0x197683DF2F268D, -345},    // -.4738
-      {-86, 0x13E497065CD61F, -338},    //  .4736
-      {-76, 0x17288E1271F513, -305},    // -.4761
-      {-63, 0x1A53FC9631D10D, -262},    //  .4929
-      {-30, 0x14484BFEEBC2A0, -152},    //  .4758
-      {-21, 0x12E3B40A0E9B4F, -122},    // -.4916
-      {-5, 0x14F8B588E368F1, -69},      //  .4829
-      {23, 0x152D02C7E14AF6, 24},       // -.5000 (exactly, round-to-even)
-      {29, 0x1431E0FAE6D721, 44},       // -.4870
-      {34, 0x1ED09BEAD87C03, 60},       // -.4721
-      {70, 0x172EBAD6DDC73D, 180},      //  .4733
-      {105, 0x1BE7ABD3781ECA, 296},     // -.4850
-      {126, 0x17A2ECC414A03F, 366},     // -.4999
-      {130, 0x1CDA62055B2D9E, 379},     //  .4855
-      {165, 0x115D847AD00087, 496},     // -.4913
-      {172, 0x14B378469B6732, 519},     //  .4818
-      {187, 0x1262DFEEBBB0F9, 569},     // -.4805
-      {210, 0x18557F31326BBB, 645},     // -.4992
-      {212, 0x1302CB5E6F642A, 652},     // -.4838
-      {215, 0x1290BA9A38C7D1, 662},     // -.4881
-      {236, 0x1F736F9B3494E9, 731},     //  .4707
-      {244, 0x176EC98994F489, 758},     //  .4924
-      {250, 0x1658E3AB795204, 778},     // -.4963
-      {252, 0x117571DDF6C814, 785},     //  .4873
-      {254, 0x1B4781EAD1989E, 791},     // -.4887
-      {260, 0x1A03FDE214CAF1, 811},     //  .4784
-      {284, 0x1585041B2C477F, 891},     //  .4798
-      {304, 0x1D2A1BE4048F90, 957},     // -.4987
-      // Out-of-range values
-      {-324, 0x0, 0},
-      {-325, 0x0, 0},
-      {-326, 0x0, 0},
-      {309, 1, 2000},
-      {310, 1, 2000},
-      {311, 1, 2000},
-  };
-  for (const TestCase& test_case : kTestCases) {
-    EXPECT_EQ(Pow10(test_case.power),
-              std::ldexp(test_case.significand, test_case.radix))
-        << absl::StrFormat("Failure for Pow10(%d): %a vs %a", test_case.power,
-                           Pow10(test_case.power),
-                           std::ldexp(test_case.significand, test_case.radix));
-  }
-}
-
-}  // namespace
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/resize_uninitialized.h b/third_party/abseil_cpp/absl/strings/internal/resize_uninitialized.h
deleted file mode 100644
index e42628e394..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/resize_uninitialized.h
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
-#define ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
-
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/port.h"
-#include "absl/meta/type_traits.h"  //  for void_t
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-// Is a subclass of true_type or false_type, depending on whether or not
-// T has a __resize_default_init member.
-template <typename string_type, typename = void>
-struct ResizeUninitializedTraits {
-  using HasMember = std::false_type;
-  static void Resize(string_type* s, size_t new_size) { s->resize(new_size); }
-};
-
-// __resize_default_init is provided by libc++ >= 8.0
-template <typename string_type>
-struct ResizeUninitializedTraits<
-    string_type, absl::void_t<decltype(std::declval<string_type&>()
-                                           .__resize_default_init(237))> > {
-  using HasMember = std::true_type;
-  static void Resize(string_type* s, size_t new_size) {
-    s->__resize_default_init(new_size);
-  }
-};
-
-// Returns true if the std::string implementation supports a resize where
-// the new characters added to the std::string are left untouched.
-//
-// (A better name might be "STLStringSupportsUninitializedResize", alluding to
-// the previous function.)
-template <typename string_type>
-inline constexpr bool STLStringSupportsNontrashingResize(string_type*) {
-  return ResizeUninitializedTraits<string_type>::HasMember::value;
-}
-
-// Like str->resize(new_size), except any new characters added to "*str" as a
-// result of resizing may be left uninitialized, rather than being filled with
-// '0' bytes. Typically used when code is then going to overwrite the backing
-// store of the std::string with known data.
-template <typename string_type, typename = void>
-inline void STLStringResizeUninitialized(string_type* s, size_t new_size) {
-  ResizeUninitializedTraits<string_type>::Resize(s, new_size);
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/resize_uninitialized_test.cc b/third_party/abseil_cpp/absl/strings/internal/resize_uninitialized_test.cc
deleted file mode 100644
index 0f8b3c2a95..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/resize_uninitialized_test.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/resize_uninitialized.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-int resize_call_count = 0;
-
-// A mock string class whose only purpose is to track how many times its
-// resize() method has been called.
-struct resizable_string {
-  size_t size() const { return 0; }
-  char& operator[](size_t) {
-    static char c = '\0';
-    return c;
-  }
-  void resize(size_t) { resize_call_count += 1; }
-};
-
-int resize_default_init_call_count = 0;
-
-// A mock string class whose only purpose is to track how many times its
-// resize() and __resize_default_init() methods have been called.
-struct resize_default_init_string {
-  size_t size() const { return 0; }
-  char& operator[](size_t) {
-    static char c = '\0';
-    return c;
-  }
-  void resize(size_t) { resize_call_count += 1; }
-  void __resize_default_init(size_t) { resize_default_init_call_count += 1; }
-};
-
-TEST(ResizeUninit, WithAndWithout) {
-  resize_call_count = 0;
-  resize_default_init_call_count = 0;
-  {
-    resizable_string rs;
-
-    EXPECT_EQ(resize_call_count, 0);
-    EXPECT_EQ(resize_default_init_call_count, 0);
-    EXPECT_FALSE(
-        absl::strings_internal::STLStringSupportsNontrashingResize(&rs));
-    EXPECT_EQ(resize_call_count, 0);
-    EXPECT_EQ(resize_default_init_call_count, 0);
-    absl::strings_internal::STLStringResizeUninitialized(&rs, 237);
-    EXPECT_EQ(resize_call_count, 1);
-    EXPECT_EQ(resize_default_init_call_count, 0);
-  }
-
-  resize_call_count = 0;
-  resize_default_init_call_count = 0;
-  {
-    resize_default_init_string rus;
-
-    EXPECT_EQ(resize_call_count, 0);
-    EXPECT_EQ(resize_default_init_call_count, 0);
-    EXPECT_TRUE(
-        absl::strings_internal::STLStringSupportsNontrashingResize(&rus));
-    EXPECT_EQ(resize_call_count, 0);
-    EXPECT_EQ(resize_default_init_call_count, 0);
-    absl::strings_internal::STLStringResizeUninitialized(&rus, 237);
-    EXPECT_EQ(resize_call_count, 0);
-    EXPECT_EQ(resize_default_init_call_count, 1);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/stl_type_traits.h b/third_party/abseil_cpp/absl/strings/internal/stl_type_traits.h
deleted file mode 100644
index 6035ca45cb..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/stl_type_traits.h
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// Thie file provides the IsStrictlyBaseOfAndConvertibleToSTLContainer type
-// trait metafunction to assist in working with the _GLIBCXX_DEBUG debug
-// wrappers of STL containers.
-//
-// DO NOT INCLUDE THIS FILE DIRECTLY. Use this file by including
-// absl/strings/str_split.h.
-//
-// IWYU pragma: private, include "absl/strings/str_split.h"
-
-#ifndef ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
-#define ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
-
-#include <array>
-#include <bitset>
-#include <deque>
-#include <forward_list>
-#include <list>
-#include <map>
-#include <set>
-#include <type_traits>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-template <typename C, template <typename...> class T>
-struct IsSpecializationImpl : std::false_type {};
-template <template <typename...> class T, typename... Args>
-struct IsSpecializationImpl<T<Args...>, T> : std::true_type {};
-template <typename C, template <typename...> class T>
-using IsSpecialization = IsSpecializationImpl<absl::decay_t<C>, T>;
-
-template <typename C>
-struct IsArrayImpl : std::false_type {};
-template <template <typename, size_t> class A, typename T, size_t N>
-struct IsArrayImpl<A<T, N>> : std::is_same<A<T, N>, std::array<T, N>> {};
-template <typename C>
-using IsArray = IsArrayImpl<absl::decay_t<C>>;
-
-template <typename C>
-struct IsBitsetImpl : std::false_type {};
-template <template <size_t> class B, size_t N>
-struct IsBitsetImpl<B<N>> : std::is_same<B<N>, std::bitset<N>> {};
-template <typename C>
-using IsBitset = IsBitsetImpl<absl::decay_t<C>>;
-
-template <typename C>
-struct IsSTLContainer
-    : absl::disjunction<
-          IsArray<C>, IsBitset<C>, IsSpecialization<C, std::deque>,
-          IsSpecialization<C, std::forward_list>,
-          IsSpecialization<C, std::list>, IsSpecialization<C, std::map>,
-          IsSpecialization<C, std::multimap>, IsSpecialization<C, std::set>,
-          IsSpecialization<C, std::multiset>,
-          IsSpecialization<C, std::unordered_map>,
-          IsSpecialization<C, std::unordered_multimap>,
-          IsSpecialization<C, std::unordered_set>,
-          IsSpecialization<C, std::unordered_multiset>,
-          IsSpecialization<C, std::vector>> {};
-
-template <typename C, template <typename...> class T, typename = void>
-struct IsBaseOfSpecializationImpl : std::false_type {};
-// IsBaseOfSpecializationImpl needs multiple partial specializations to SFINAE
-// on the existence of container dependent types and plug them into the STL
-// template.
-template <typename C, template <typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T, absl::void_t<typename C::value_type, typename C::allocator_type>>
-    : std::is_base_of<C,
-                      T<typename C::value_type, typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T,
-    absl::void_t<typename C::key_type, typename C::key_compare,
-                 typename C::allocator_type>>
-    : std::is_base_of<C, T<typename C::key_type, typename C::key_compare,
-                           typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T,
-    absl::void_t<typename C::key_type, typename C::mapped_type,
-                 typename C::key_compare, typename C::allocator_type>>
-    : std::is_base_of<C,
-                      T<typename C::key_type, typename C::mapped_type,
-                        typename C::key_compare, typename C::allocator_type>> {
-};
-template <typename C, template <typename, typename, typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T,
-    absl::void_t<typename C::key_type, typename C::hasher,
-                 typename C::key_equal, typename C::allocator_type>>
-    : std::is_base_of<C, T<typename C::key_type, typename C::hasher,
-                           typename C::key_equal, typename C::allocator_type>> {
-};
-template <typename C,
-          template <typename, typename, typename, typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T,
-    absl::void_t<typename C::key_type, typename C::mapped_type,
-                 typename C::hasher, typename C::key_equal,
-                 typename C::allocator_type>>
-    : std::is_base_of<C, T<typename C::key_type, typename C::mapped_type,
-                           typename C::hasher, typename C::key_equal,
-                           typename C::allocator_type>> {};
-template <typename C, template <typename...> class T>
-using IsBaseOfSpecialization = IsBaseOfSpecializationImpl<absl::decay_t<C>, T>;
-
-template <typename C>
-struct IsBaseOfArrayImpl : std::false_type {};
-template <template <typename, size_t> class A, typename T, size_t N>
-struct IsBaseOfArrayImpl<A<T, N>> : std::is_base_of<A<T, N>, std::array<T, N>> {
-};
-template <typename C>
-using IsBaseOfArray = IsBaseOfArrayImpl<absl::decay_t<C>>;
-
-template <typename C>
-struct IsBaseOfBitsetImpl : std::false_type {};
-template <template <size_t> class B, size_t N>
-struct IsBaseOfBitsetImpl<B<N>> : std::is_base_of<B<N>, std::bitset<N>> {};
-template <typename C>
-using IsBaseOfBitset = IsBaseOfBitsetImpl<absl::decay_t<C>>;
-
-template <typename C>
-struct IsBaseOfSTLContainer
-    : absl::disjunction<IsBaseOfArray<C>, IsBaseOfBitset<C>,
-                        IsBaseOfSpecialization<C, std::deque>,
-                        IsBaseOfSpecialization<C, std::forward_list>,
-                        IsBaseOfSpecialization<C, std::list>,
-                        IsBaseOfSpecialization<C, std::map>,
-                        IsBaseOfSpecialization<C, std::multimap>,
-                        IsBaseOfSpecialization<C, std::set>,
-                        IsBaseOfSpecialization<C, std::multiset>,
-                        IsBaseOfSpecialization<C, std::unordered_map>,
-                        IsBaseOfSpecialization<C, std::unordered_multimap>,
-                        IsBaseOfSpecialization<C, std::unordered_set>,
-                        IsBaseOfSpecialization<C, std::unordered_multiset>,
-                        IsBaseOfSpecialization<C, std::vector>> {};
-
-template <typename C, template <typename...> class T, typename = void>
-struct IsConvertibleToSpecializationImpl : std::false_type {};
-// IsConvertibleToSpecializationImpl needs multiple partial specializations to
-// SFINAE on the existence of container dependent types and plug them into the
-// STL template.
-template <typename C, template <typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T, absl::void_t<typename C::value_type, typename C::allocator_type>>
-    : std::is_convertible<
-          C, T<typename C::value_type, typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T,
-    absl::void_t<typename C::key_type, typename C::key_compare,
-                 typename C::allocator_type>>
-    : std::is_convertible<C, T<typename C::key_type, typename C::key_compare,
-                               typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T,
-    absl::void_t<typename C::key_type, typename C::mapped_type,
-                 typename C::key_compare, typename C::allocator_type>>
-    : std::is_convertible<
-          C, T<typename C::key_type, typename C::mapped_type,
-               typename C::key_compare, typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T,
-    absl::void_t<typename C::key_type, typename C::hasher,
-                 typename C::key_equal, typename C::allocator_type>>
-    : std::is_convertible<
-          C, T<typename C::key_type, typename C::hasher, typename C::key_equal,
-               typename C::allocator_type>> {};
-template <typename C,
-          template <typename, typename, typename, typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T,
-    absl::void_t<typename C::key_type, typename C::mapped_type,
-                 typename C::hasher, typename C::key_equal,
-                 typename C::allocator_type>>
-    : std::is_convertible<C, T<typename C::key_type, typename C::mapped_type,
-                               typename C::hasher, typename C::key_equal,
-                               typename C::allocator_type>> {};
-template <typename C, template <typename...> class T>
-using IsConvertibleToSpecialization =
-    IsConvertibleToSpecializationImpl<absl::decay_t<C>, T>;
-
-template <typename C>
-struct IsConvertibleToArrayImpl : std::false_type {};
-template <template <typename, size_t> class A, typename T, size_t N>
-struct IsConvertibleToArrayImpl<A<T, N>>
-    : std::is_convertible<A<T, N>, std::array<T, N>> {};
-template <typename C>
-using IsConvertibleToArray = IsConvertibleToArrayImpl<absl::decay_t<C>>;
-
-template <typename C>
-struct IsConvertibleToBitsetImpl : std::false_type {};
-template <template <size_t> class B, size_t N>
-struct IsConvertibleToBitsetImpl<B<N>>
-    : std::is_convertible<B<N>, std::bitset<N>> {};
-template <typename C>
-using IsConvertibleToBitset = IsConvertibleToBitsetImpl<absl::decay_t<C>>;
-
-template <typename C>
-struct IsConvertibleToSTLContainer
-    : absl::disjunction<
-          IsConvertibleToArray<C>, IsConvertibleToBitset<C>,
-          IsConvertibleToSpecialization<C, std::deque>,
-          IsConvertibleToSpecialization<C, std::forward_list>,
-          IsConvertibleToSpecialization<C, std::list>,
-          IsConvertibleToSpecialization<C, std::map>,
-          IsConvertibleToSpecialization<C, std::multimap>,
-          IsConvertibleToSpecialization<C, std::set>,
-          IsConvertibleToSpecialization<C, std::multiset>,
-          IsConvertibleToSpecialization<C, std::unordered_map>,
-          IsConvertibleToSpecialization<C, std::unordered_multimap>,
-          IsConvertibleToSpecialization<C, std::unordered_set>,
-          IsConvertibleToSpecialization<C, std::unordered_multiset>,
-          IsConvertibleToSpecialization<C, std::vector>> {};
-
-template <typename C>
-struct IsStrictlyBaseOfAndConvertibleToSTLContainer
-    : absl::conjunction<absl::negation<IsSTLContainer<C>>,
-                        IsBaseOfSTLContainer<C>,
-                        IsConvertibleToSTLContainer<C>> {};
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-#endif  // ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/arg.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/arg.cc
deleted file mode 100644
index e28a29b171..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/arg.cc
+++ /dev/null
@@ -1,488 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//
-// POSIX spec:
-//   http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html
-//
-#include "absl/strings/internal/str_format/arg.h"
-
-#include <cassert>
-#include <cerrno>
-#include <cstdlib>
-#include <string>
-#include <type_traits>
-
-#include "absl/base/port.h"
-#include "absl/strings/internal/str_format/float_conversion.h"
-#include "absl/strings/numbers.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-namespace {
-
-// Reduce *capacity by s.size(), clipped to a 0 minimum.
-void ReducePadding(string_view s, size_t *capacity) {
-  *capacity = Excess(s.size(), *capacity);
-}
-
-// Reduce *capacity by n, clipped to a 0 minimum.
-void ReducePadding(size_t n, size_t *capacity) {
-  *capacity = Excess(n, *capacity);
-}
-
-template <typename T>
-struct MakeUnsigned : std::make_unsigned<T> {};
-template <>
-struct MakeUnsigned<absl::int128> {
-  using type = absl::uint128;
-};
-template <>
-struct MakeUnsigned<absl::uint128> {
-  using type = absl::uint128;
-};
-
-template <typename T>
-struct IsSigned : std::is_signed<T> {};
-template <>
-struct IsSigned<absl::int128> : std::true_type {};
-template <>
-struct IsSigned<absl::uint128> : std::false_type {};
-
-// Integral digit printer.
-// Call one of the PrintAs* routines after construction once.
-// Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results.
-class IntDigits {
- public:
-  // Print the unsigned integer as octal.
-  // Supports unsigned integral types and uint128.
-  template <typename T>
-  void PrintAsOct(T v) {
-    static_assert(!IsSigned<T>::value, "");
-    char *p = storage_ + sizeof(storage_);
-    do {
-      *--p = static_cast<char>('0' + (static_cast<size_t>(v) & 7));
-      v >>= 3;
-    } while (v);
-    start_ = p;
-    size_ = storage_ + sizeof(storage_) - p;
-  }
-
-  // Print the signed or unsigned integer as decimal.
-  // Supports all integral types.
-  template <typename T>
-  void PrintAsDec(T v) {
-    static_assert(std::is_integral<T>::value, "");
-    start_ = storage_;
-    size_ = numbers_internal::FastIntToBuffer(v, storage_) - storage_;
-  }
-
-  void PrintAsDec(int128 v) {
-    auto u = static_cast<uint128>(v);
-    bool add_neg = false;
-    if (v < 0) {
-      add_neg = true;
-      u = uint128{} - u;
-    }
-    PrintAsDec(u, add_neg);
-  }
-
-  void PrintAsDec(uint128 v, bool add_neg = false) {
-    // This function can be sped up if needed. We can call FastIntToBuffer
-    // twice, or fix FastIntToBuffer to support uint128.
-    char *p = storage_ + sizeof(storage_);
-    do {
-      p -= 2;
-      numbers_internal::PutTwoDigits(static_cast<size_t>(v % 100), p);
-      v /= 100;
-    } while (v);
-    if (p[0] == '0') {
-      // We printed one too many hexits.
-      ++p;
-    }
-    if (add_neg) {
-      *--p = '-';
-    }
-    size_ = storage_ + sizeof(storage_) - p;
-    start_ = p;
-  }
-
-  // Print the unsigned integer as hex using lowercase.
-  // Supports unsigned integral types and uint128.
-  template <typename T>
-  void PrintAsHexLower(T v) {
-    static_assert(!IsSigned<T>::value, "");
-    char *p = storage_ + sizeof(storage_);
-
-    do {
-      p -= 2;
-      constexpr const char* table = numbers_internal::kHexTable;
-      std::memcpy(p, table + 2 * (static_cast<size_t>(v) & 0xFF), 2);
-      if (sizeof(T) == 1) break;
-      v >>= 8;
-    } while (v);
-    if (p[0] == '0') {
-      // We printed one too many digits.
-      ++p;
-    }
-    start_ = p;
-    size_ = storage_ + sizeof(storage_) - p;
-  }
-
-  // Print the unsigned integer as hex using uppercase.
-  // Supports unsigned integral types and uint128.
-  template <typename T>
-  void PrintAsHexUpper(T v) {
-    static_assert(!IsSigned<T>::value, "");
-    char *p = storage_ + sizeof(storage_);
-
-    // kHexTable is only lowercase, so do it manually for uppercase.
-    do {
-      *--p = "0123456789ABCDEF"[static_cast<size_t>(v) & 15];
-      v >>= 4;
-    } while (v);
-    start_ = p;
-    size_ = storage_ + sizeof(storage_) - p;
-  }
-
-  // The printed value including the '-' sign if available.
-  // For inputs of value `0`, this will return "0"
-  string_view with_neg_and_zero() const { return {start_, size_}; }
-
-  // The printed value not including the '-' sign.
-  // For inputs of value `0`, this will return "".
-  string_view without_neg_or_zero() const {
-    static_assert('-' < '0', "The check below verifies both.");
-    size_t advance = start_[0] <= '0' ? 1 : 0;
-    return {start_ + advance, size_ - advance};
-  }
-
-  bool is_negative() const { return start_[0] == '-'; }
-
- private:
-  const char *start_;
-  size_t size_;
-  // Max size: 128 bit value as octal -> 43 digits, plus sign char
-  char storage_[128 / 3 + 1 + 1];
-};
-
-// Note: 'o' conversions do not have a base indicator, it's just that
-// the '#' flag is specified to modify the precision for 'o' conversions.
-string_view BaseIndicator(const IntDigits &as_digits,
-                          const FormatConversionSpecImpl conv) {
-  // always show 0x for %p.
-  bool alt = conv.has_alt_flag() ||
-             conv.conversion_char() == FormatConversionCharInternal::p;
-  bool hex = (conv.conversion_char() == FormatConversionCharInternal::x ||
-              conv.conversion_char() == FormatConversionCharInternal::X ||
-              conv.conversion_char() == FormatConversionCharInternal::p);
-  // From the POSIX description of '#' flag:
-  //   "For x or X conversion specifiers, a non-zero result shall have
-  //   0x (or 0X) prefixed to it."
-  if (alt && hex && !as_digits.without_neg_or_zero().empty()) {
-    return conv.conversion_char() == FormatConversionCharInternal::X ? "0X"
-                                                                     : "0x";
-  }
-  return {};
-}
-
-string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) {
-  if (conv.conversion_char() == FormatConversionCharInternal::d ||
-      conv.conversion_char() == FormatConversionCharInternal::i) {
-    if (neg) return "-";
-    if (conv.has_show_pos_flag()) return "+";
-    if (conv.has_sign_col_flag()) return " ";
-  }
-  return {};
-}
-
-bool ConvertCharImpl(unsigned char v, const FormatConversionSpecImpl conv,
-                     FormatSinkImpl *sink) {
-  size_t fill = 0;
-  if (conv.width() >= 0) fill = conv.width();
-  ReducePadding(1, &fill);
-  if (!conv.has_left_flag()) sink->Append(fill, ' ');
-  sink->Append(1, v);
-  if (conv.has_left_flag()) sink->Append(fill, ' ');
-  return true;
-}
-
-bool ConvertIntImplInnerSlow(const IntDigits &as_digits,
-                             const FormatConversionSpecImpl conv,
-                             FormatSinkImpl *sink) {
-  // Print as a sequence of Substrings:
-  //   [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
-  size_t fill = 0;
-  if (conv.width() >= 0) fill = conv.width();
-
-  string_view formatted = as_digits.without_neg_or_zero();
-  ReducePadding(formatted, &fill);
-
-  string_view sign = SignColumn(as_digits.is_negative(), conv);
-  ReducePadding(sign, &fill);
-
-  string_view base_indicator = BaseIndicator(as_digits, conv);
-  ReducePadding(base_indicator, &fill);
-
-  int precision = conv.precision();
-  bool precision_specified = precision >= 0;
-  if (!precision_specified)
-    precision = 1;
-
-  if (conv.has_alt_flag() &&
-      conv.conversion_char() == FormatConversionCharInternal::o) {
-    // From POSIX description of the '#' (alt) flag:
-    //   "For o conversion, it increases the precision (if necessary) to
-    //   force the first digit of the result to be zero."
-    if (formatted.empty() || *formatted.begin() != '0') {
-      int needed = static_cast<int>(formatted.size()) + 1;
-      precision = std::max(precision, needed);
-    }
-  }
-
-  size_t num_zeroes = Excess(formatted.size(), precision);
-  ReducePadding(num_zeroes, &fill);
-
-  size_t num_left_spaces = !conv.has_left_flag() ? fill : 0;
-  size_t num_right_spaces = conv.has_left_flag() ? fill : 0;
-
-  // From POSIX description of the '0' (zero) flag:
-  //   "For d, i, o, u, x, and X conversion specifiers, if a precision
-  //   is specified, the '0' flag is ignored."
-  if (!precision_specified && conv.has_zero_flag()) {
-    num_zeroes += num_left_spaces;
-    num_left_spaces = 0;
-  }
-
-  sink->Append(num_left_spaces, ' ');
-  sink->Append(sign);
-  sink->Append(base_indicator);
-  sink->Append(num_zeroes, '0');
-  sink->Append(formatted);
-  sink->Append(num_right_spaces, ' ');
-  return true;
-}
-
-template <typename T>
-bool ConvertIntArg(T v, const FormatConversionSpecImpl conv,
-                   FormatSinkImpl *sink) {
-  using U = typename MakeUnsigned<T>::type;
-  IntDigits as_digits;
-
-  // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes
-  // it to complain about a switch/case type mismatch, even though both are
-  // FormatConverionChar.  Likely this is because at this point
-  // FormatConversionChar is declared, but not defined.
-  switch (static_cast<uint8_t>(conv.conversion_char())) {
-    case static_cast<uint8_t>(FormatConversionCharInternal::c):
-      return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink);
-
-    case static_cast<uint8_t>(FormatConversionCharInternal::o):
-      as_digits.PrintAsOct(static_cast<U>(v));
-      break;
-
-    case static_cast<uint8_t>(FormatConversionCharInternal::x):
-      as_digits.PrintAsHexLower(static_cast<U>(v));
-      break;
-    case static_cast<uint8_t>(FormatConversionCharInternal::X):
-      as_digits.PrintAsHexUpper(static_cast<U>(v));
-      break;
-
-    case static_cast<uint8_t>(FormatConversionCharInternal::u):
-      as_digits.PrintAsDec(static_cast<U>(v));
-      break;
-
-    case static_cast<uint8_t>(FormatConversionCharInternal::d):
-    case static_cast<uint8_t>(FormatConversionCharInternal::i):
-      as_digits.PrintAsDec(v);
-      break;
-
-    case static_cast<uint8_t>(FormatConversionCharInternal::a):
-    case static_cast<uint8_t>(FormatConversionCharInternal::e):
-    case static_cast<uint8_t>(FormatConversionCharInternal::f):
-    case static_cast<uint8_t>(FormatConversionCharInternal::g):
-    case static_cast<uint8_t>(FormatConversionCharInternal::A):
-    case static_cast<uint8_t>(FormatConversionCharInternal::E):
-    case static_cast<uint8_t>(FormatConversionCharInternal::F):
-    case static_cast<uint8_t>(FormatConversionCharInternal::G):
-      return ConvertFloatImpl(static_cast<double>(v), conv, sink);
-
-    default:
-       ABSL_INTERNAL_ASSUME(false);
-  }
-
-  if (conv.is_basic()) {
-    sink->Append(as_digits.with_neg_and_zero());
-    return true;
-  }
-  return ConvertIntImplInnerSlow(as_digits, conv, sink);
-}
-
-template <typename T>
-bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv,
-                     FormatSinkImpl *sink) {
-  return FormatConversionCharIsFloat(conv.conversion_char()) &&
-         ConvertFloatImpl(v, conv, sink);
-}
-
-inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv,
-                             FormatSinkImpl *sink) {
-  if (conv.is_basic()) {
-    sink->Append(v);
-    return true;
-  }
-  return sink->PutPaddedString(v, conv.width(), conv.precision(),
-                               conv.has_left_flag());
-}
-
-}  // namespace
-
-// ==================== Strings ====================
-StringConvertResult FormatConvertImpl(const std::string &v,
-                                      const FormatConversionSpecImpl conv,
-                                      FormatSinkImpl *sink) {
-  return {ConvertStringArg(v, conv, sink)};
-}
-
-StringConvertResult FormatConvertImpl(string_view v,
-                                      const FormatConversionSpecImpl conv,
-                                      FormatSinkImpl *sink) {
-  return {ConvertStringArg(v, conv, sink)};
-}
-
-ArgConvertResult<FormatConversionCharSetUnion(
-    FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)>
-FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv,
-                  FormatSinkImpl *sink) {
-  if (conv.conversion_char() == FormatConversionCharInternal::p)
-    return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
-  size_t len;
-  if (v == nullptr) {
-    len = 0;
-  } else if (conv.precision() < 0) {
-    len = std::strlen(v);
-  } else {
-    // If precision is set, we look for the NUL-terminator on the valid range.
-    len = std::find(v, v + conv.precision(), '\0') - v;
-  }
-  return {ConvertStringArg(string_view(v, len), conv, sink)};
-}
-
-// ==================== Raw pointers ====================
-ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
-    VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
-  if (!v.value) {
-    sink->Append("(nil)");
-    return {true};
-  }
-  IntDigits as_digits;
-  as_digits.PrintAsHexLower(v.value);
-  return {ConvertIntImplInnerSlow(as_digits, conv, sink)};
-}
-
-// ==================== Floats ====================
-FloatingConvertResult FormatConvertImpl(float v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertFloatArg(v, conv, sink)};
-}
-FloatingConvertResult FormatConvertImpl(double v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertFloatArg(v, conv, sink)};
-}
-FloatingConvertResult FormatConvertImpl(long double v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertFloatArg(v, conv, sink)};
-}
-
-// ==================== Chars ====================
-IntegralConvertResult FormatConvertImpl(char v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(signed char v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(unsigned char v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-
-// ==================== Ints ====================
-IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(int v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(unsigned v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(absl::int128 v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-IntegralConvertResult FormatConvertImpl(absl::uint128 v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
-  return {ConvertIntArg(v, conv, sink)};
-}
-
-ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
-
-
-
-}  // namespace str_format_internal
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/arg.h b/third_party/abseil_cpp/absl/strings/internal/str_format/arg.h
deleted file mode 100644
index 7040c86677..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/arg.h
+++ /dev/null
@@ -1,518 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
-#define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
-
-#include <string.h>
-#include <wchar.h>
-
-#include <cstdio>
-#include <iomanip>
-#include <limits>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <type_traits>
-
-#include "absl/base/port.h"
-#include "absl/meta/type_traits.h"
-#include "absl/numeric/int128.h"
-#include "absl/strings/internal/str_format/extension.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class Cord;
-class FormatCountCapture;
-class FormatSink;
-
-template <absl::FormatConversionCharSet C>
-struct FormatConvertResult;
-class FormatConversionSpec;
-
-namespace str_format_internal {
-
-template <typename T, typename = void>
-struct HasUserDefinedConvert : std::false_type {};
-
-template <typename T>
-struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert(
-                                    std::declval<const T&>(),
-                                    std::declval<const FormatConversionSpec&>(),
-                                    std::declval<FormatSink*>()))>>
-    : std::true_type {};
-
-void AbslFormatConvert();  // Stops the lexical name lookup
-template <typename T>
-auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
-                       FormatSinkImpl* sink)
-    -> decltype(AbslFormatConvert(v,
-                                  std::declval<const FormatConversionSpec&>(),
-                                  std::declval<FormatSink*>())) {
-  using FormatConversionSpecT =
-      absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>;
-  using FormatSinkT =
-      absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>;
-  auto fcs = conv.Wrap<FormatConversionSpecT>();
-  auto fs = sink->Wrap<FormatSinkT>();
-  return AbslFormatConvert(v, fcs, &fs);
-}
-
-template <typename T>
-class StreamedWrapper;
-
-// If 'v' can be converted (in the printf sense) according to 'conv',
-// then convert it, appending to `sink` and return `true`.
-// Otherwise fail and return `false`.
-
-// AbslFormatConvert(v, conv, sink) is intended to be found by ADL on 'v'
-// as an extension mechanism. These FormatConvertImpl functions are the default
-// implementations.
-// The ADL search is augmented via the 'Sink*' parameter, which also
-// serves as a disambiguator to reject possible unintended 'AbslFormatConvert'
-// functions in the namespaces associated with 'v'.
-
-// Raw pointers.
-struct VoidPtr {
-  VoidPtr() = default;
-  template <typename T,
-            decltype(reinterpret_cast<uintptr_t>(std::declval<T*>())) = 0>
-  VoidPtr(T* ptr)  // NOLINT
-      : value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {}
-  uintptr_t value;
-};
-
-template <FormatConversionCharSet C>
-struct ArgConvertResult {
-  bool value;
-};
-
-template <FormatConversionCharSet C>
-constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) {
-  return C;
-}
-
-template <FormatConversionCharSet C>
-constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) {
-  return C;
-}
-
-using StringConvertResult =
-    ArgConvertResult<FormatConversionCharSetInternal::s>;
-ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
-    VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
-
-// Strings.
-StringConvertResult FormatConvertImpl(const std::string& v,
-                                      FormatConversionSpecImpl conv,
-                                      FormatSinkImpl* sink);
-StringConvertResult FormatConvertImpl(string_view v,
-                                      FormatConversionSpecImpl conv,
-                                      FormatSinkImpl* sink);
-ArgConvertResult<FormatConversionCharSetUnion(
-    FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)>
-FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv,
-                  FormatSinkImpl* sink);
-
-template <class AbslCord, typename std::enable_if<std::is_same<
-                              AbslCord, absl::Cord>::value>::type* = nullptr>
-StringConvertResult FormatConvertImpl(const AbslCord& value,
-                                      FormatConversionSpecImpl conv,
-                                      FormatSinkImpl* sink) {
-  bool is_left = conv.has_left_flag();
-  size_t space_remaining = 0;
-
-  int width = conv.width();
-  if (width >= 0) space_remaining = width;
-
-  size_t to_write = value.size();
-
-  int precision = conv.precision();
-  if (precision >= 0)
-    to_write = (std::min)(to_write, static_cast<size_t>(precision));
-
-  space_remaining = Excess(to_write, space_remaining);
-
-  if (space_remaining > 0 && !is_left) sink->Append(space_remaining, ' ');
-
-  for (string_view piece : value.Chunks()) {
-    if (piece.size() > to_write) {
-      piece.remove_suffix(piece.size() - to_write);
-      to_write = 0;
-    } else {
-      to_write -= piece.size();
-    }
-    sink->Append(piece);
-    if (to_write == 0) {
-      break;
-    }
-  }
-
-  if (space_remaining > 0 && is_left) sink->Append(space_remaining, ' ');
-  return {true};
-}
-
-using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
-    FormatConversionCharSetInternal::c,
-    FormatConversionCharSetInternal::kNumeric,
-    FormatConversionCharSetInternal::kStar)>;
-using FloatingConvertResult =
-    ArgConvertResult<FormatConversionCharSetInternal::kFloating>;
-
-// Floats.
-FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-FloatingConvertResult FormatConvertImpl(double v, FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-FloatingConvertResult FormatConvertImpl(long double v,
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-
-// Chars.
-IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(signed char v,
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned char v,
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-
-// Ints.
-IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(int v, FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned v,
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(uint128 v,
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0>
-IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink) {
-  return FormatConvertImpl(static_cast<int>(v), conv, sink);
-}
-
-// We provide this function to help the checker, but it is never defined.
-// FormatArgImpl will use the underlying Convert functions instead.
-template <typename T>
-typename std::enable_if<std::is_enum<T>::value &&
-                            !HasUserDefinedConvert<T>::value,
-                        IntegralConvertResult>::type
-FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
-
-template <typename T>
-StringConvertResult FormatConvertImpl(const StreamedWrapper<T>& v,
-                                      FormatConversionSpecImpl conv,
-                                      FormatSinkImpl* out) {
-  std::ostringstream oss;
-  oss << v.v_;
-  if (!oss) return {false};
-  return str_format_internal::FormatConvertImpl(oss.str(), conv, out);
-}
-
-// Use templates and dependent types to delay evaluation of the function
-// until after FormatCountCapture is fully defined.
-struct FormatCountCaptureHelper {
-  template <class T = int>
-  static ArgConvertResult<FormatConversionCharSetInternal::n> ConvertHelper(
-      const FormatCountCapture& v, FormatConversionSpecImpl conv,
-      FormatSinkImpl* sink) {
-    const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v;
-
-    if (conv.conversion_char() !=
-        str_format_internal::FormatConversionCharInternal::n) {
-      return {false};
-    }
-    *v2.p_ = static_cast<int>(sink->size());
-    return {true};
-  }
-};
-
-template <class T = int>
-ArgConvertResult<FormatConversionCharSetInternal::n> FormatConvertImpl(
-    const FormatCountCapture& v, FormatConversionSpecImpl conv,
-    FormatSinkImpl* sink) {
-  return FormatCountCaptureHelper::ConvertHelper(v, conv, sink);
-}
-
-// Helper friend struct to hide implementation details from the public API of
-// FormatArgImpl.
-struct FormatArgImplFriend {
-  template <typename Arg>
-  static bool ToInt(Arg arg, int* out) {
-    // A value initialized FormatConversionSpecImpl has a `none` conv, which
-    // tells the dispatcher to run the `int` conversion.
-    return arg.dispatcher_(arg.data_, {}, out);
-  }
-
-  template <typename Arg>
-  static bool Convert(Arg arg, FormatConversionSpecImpl conv,
-                      FormatSinkImpl* out) {
-    return arg.dispatcher_(arg.data_, conv, out);
-  }
-
-  template <typename Arg>
-  static typename Arg::Dispatcher GetVTablePtrForTest(Arg arg) {
-    return arg.dispatcher_;
-  }
-};
-
-template <typename Arg>
-constexpr FormatConversionCharSet ArgumentToConv() {
-  return absl::str_format_internal::ExtractCharSet(
-      decltype(str_format_internal::FormatConvertImpl(
-          std::declval<const Arg&>(),
-          std::declval<const FormatConversionSpecImpl&>(),
-          std::declval<FormatSinkImpl*>())){});
-}
-
-// A type-erased handle to a format argument.
-class FormatArgImpl {
- private:
-  enum { kInlinedSpace = 8 };
-
-  using VoidPtr = str_format_internal::VoidPtr;
-
-  union Data {
-    const void* ptr;
-    const volatile void* volatile_ptr;
-    char buf[kInlinedSpace];
-  };
-
-  using Dispatcher = bool (*)(Data, FormatConversionSpecImpl, void* out);
-
-  template <typename T>
-  struct store_by_value
-      : std::integral_constant<bool, (sizeof(T) <= kInlinedSpace) &&
-                                         (std::is_integral<T>::value ||
-                                          std::is_floating_point<T>::value ||
-                                          std::is_pointer<T>::value ||
-                                          std::is_same<VoidPtr, T>::value)> {};
-
-  enum StoragePolicy { ByPointer, ByVolatilePointer, ByValue };
-  template <typename T>
-  struct storage_policy
-      : std::integral_constant<StoragePolicy,
-                               (std::is_volatile<T>::value
-                                    ? ByVolatilePointer
-                                    : (store_by_value<T>::value ? ByValue
-                                                                : ByPointer))> {
-  };
-
-  // To reduce the number of vtables we will decay values before hand.
-  // Anything with a user-defined Convert will get its own vtable.
-  // For everything else:
-  //   - Decay char* and char arrays into `const char*`
-  //   - Decay any other pointer to `const void*`
-  //   - Decay all enums to their underlying type.
-  //   - Decay function pointers to void*.
-  template <typename T, typename = void>
-  struct DecayType {
-    static constexpr bool kHasUserDefined =
-        str_format_internal::HasUserDefinedConvert<T>::value;
-    using type = typename std::conditional<
-        !kHasUserDefined && std::is_convertible<T, const char*>::value,
-        const char*,
-        typename std::conditional<!kHasUserDefined &&
-                                      std::is_convertible<T, VoidPtr>::value,
-                                  VoidPtr, const T&>::type>::type;
-  };
-  template <typename T>
-  struct DecayType<T,
-                   typename std::enable_if<
-                       !str_format_internal::HasUserDefinedConvert<T>::value &&
-                       std::is_enum<T>::value>::type> {
-    using type = typename std::underlying_type<T>::type;
-  };
-
- public:
-  template <typename T>
-  explicit FormatArgImpl(const T& value) {
-    using D = typename DecayType<T>::type;
-    static_assert(
-        std::is_same<D, const T&>::value || storage_policy<D>::value == ByValue,
-        "Decayed types must be stored by value");
-    Init(static_cast<D>(value));
-  }
-
- private:
-  friend struct str_format_internal::FormatArgImplFriend;
-  template <typename T, StoragePolicy = storage_policy<T>::value>
-  struct Manager;
-
-  template <typename T>
-  struct Manager<T, ByPointer> {
-    static Data SetValue(const T& value) {
-      Data data;
-      data.ptr = std::addressof(value);
-      return data;
-    }
-
-    static const T& Value(Data arg) { return *static_cast<const T*>(arg.ptr); }
-  };
-
-  template <typename T>
-  struct Manager<T, ByVolatilePointer> {
-    static Data SetValue(const T& value) {
-      Data data;
-      data.volatile_ptr = &value;
-      return data;
-    }
-
-    static const T& Value(Data arg) {
-      return *static_cast<const T*>(arg.volatile_ptr);
-    }
-  };
-
-  template <typename T>
-  struct Manager<T, ByValue> {
-    static Data SetValue(const T& value) {
-      Data data;
-      memcpy(data.buf, &value, sizeof(value));
-      return data;
-    }
-
-    static T Value(Data arg) {
-      T value;
-      memcpy(&value, arg.buf, sizeof(T));
-      return value;
-    }
-  };
-
-  template <typename T>
-  void Init(const T& value) {
-    data_ = Manager<T>::SetValue(value);
-    dispatcher_ = &Dispatch<T>;
-  }
-
-  template <typename T>
-  static int ToIntVal(const T& val) {
-    using CommonType = typename std::conditional<std::is_signed<T>::value,
-                                                 int64_t, uint64_t>::type;
-    if (static_cast<CommonType>(val) >
-        static_cast<CommonType>((std::numeric_limits<int>::max)())) {
-      return (std::numeric_limits<int>::max)();
-    } else if (std::is_signed<T>::value &&
-               static_cast<CommonType>(val) <
-                   static_cast<CommonType>((std::numeric_limits<int>::min)())) {
-      return (std::numeric_limits<int>::min)();
-    }
-    return static_cast<int>(val);
-  }
-
-  template <typename T>
-  static bool ToInt(Data arg, int* out, std::true_type /* is_integral */,
-                    std::false_type) {
-    *out = ToIntVal(Manager<T>::Value(arg));
-    return true;
-  }
-
-  template <typename T>
-  static bool ToInt(Data arg, int* out, std::false_type,
-                    std::true_type /* is_enum */) {
-    *out = ToIntVal(static_cast<typename std::underlying_type<T>::type>(
-        Manager<T>::Value(arg)));
-    return true;
-  }
-
-  template <typename T>
-  static bool ToInt(Data, int*, std::false_type, std::false_type) {
-    return false;
-  }
-
-  template <typename T>
-  static bool Dispatch(Data arg, FormatConversionSpecImpl spec, void* out) {
-    // A `none` conv indicates that we want the `int` conversion.
-    if (ABSL_PREDICT_FALSE(spec.conversion_char() ==
-                           FormatConversionCharInternal::kNone)) {
-      return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(),
-                      std::is_enum<T>());
-    }
-    if (ABSL_PREDICT_FALSE(!Contains(ArgumentToConv<T>(),
-                                     spec.conversion_char()))) {
-      return false;
-    }
-    return str_format_internal::FormatConvertImpl(
-               Manager<T>::Value(arg), spec,
-               static_cast<FormatSinkImpl*>(out))
-        .value;
-  }
-
-  Data data_;
-  Dispatcher dispatcher_;
-};
-
-#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E)                     \
-  E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \
-                                             void*)
-
-#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...)                   \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr,     \
-                                             __VA_ARGS__);                     \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__);               \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(char, __VA_ARGS__);               \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(signed char, __VA_ARGS__);        \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned char, __VA_ARGS__);      \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(short, __VA_ARGS__); /* NOLINT */ \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned short,      /* NOLINT */ \
-                                             __VA_ARGS__);                     \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int, __VA_ARGS__);                \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned int, __VA_ARGS__);       \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long, __VA_ARGS__); /* NOLINT */  \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long,      /* NOLINT */  \
-                                             __VA_ARGS__);                     \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long long, /* NOLINT */           \
-                                             __VA_ARGS__);                     \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */  \
-                                             __VA_ARGS__);                     \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int128, __VA_ARGS__);             \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__);            \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__);              \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__);             \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__);        \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__);        \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__);        \
-  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__)
-
-ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
-
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/arg_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/arg_test.cc
deleted file mode 100644
index 1261937c30..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/arg_test.cc
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/str_format/arg.h"
-
-#include <ostream>
-#include <string>
-#include "gtest/gtest.h"
-#include "absl/strings/str_format.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-namespace {
-
-class FormatArgImplTest : public ::testing::Test {
- public:
-  enum Color { kRed, kGreen, kBlue };
-
-  static const char *hi() { return "hi"; }
-
-  struct X {};
-
-  X x_;
-};
-
-inline FormatConvertResult<FormatConversionCharSet{}> AbslFormatConvert(
-    const FormatArgImplTest::X &, const FormatConversionSpec &, FormatSink *) {
-  return {false};
-}
-
-TEST_F(FormatArgImplTest, ToInt) {
-  int out = 0;
-  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(1), &out));
-  EXPECT_EQ(1, out);
-  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(-1), &out));
-  EXPECT_EQ(-1, out);
-  EXPECT_TRUE(
-      FormatArgImplFriend::ToInt(FormatArgImpl(static_cast<char>(64)), &out));
-  EXPECT_EQ(64, out);
-  EXPECT_TRUE(FormatArgImplFriend::ToInt(
-      FormatArgImpl(static_cast<unsigned long long>(123456)), &out));  // NOLINT
-  EXPECT_EQ(123456, out);
-  EXPECT_TRUE(FormatArgImplFriend::ToInt(
-      FormatArgImpl(static_cast<unsigned long long>(  // NOLINT
-                        std::numeric_limits<int>::max()) +
-                    1),
-      &out));
-  EXPECT_EQ(std::numeric_limits<int>::max(), out);
-  EXPECT_TRUE(FormatArgImplFriend::ToInt(
-      FormatArgImpl(static_cast<long long>(  // NOLINT
-                        std::numeric_limits<int>::min()) -
-                    10),
-      &out));
-  EXPECT_EQ(std::numeric_limits<int>::min(), out);
-  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(false), &out));
-  EXPECT_EQ(0, out);
-  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(true), &out));
-  EXPECT_EQ(1, out);
-  EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(2.2), &out));
-  EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(3.2f), &out));
-  EXPECT_FALSE(FormatArgImplFriend::ToInt(
-      FormatArgImpl(static_cast<int *>(nullptr)), &out));
-  EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(hi()), &out));
-  EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl("hi"), &out));
-  EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(x_), &out));
-  EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(kBlue), &out));
-  EXPECT_EQ(2, out);
-}
-
-extern const char kMyArray[];
-
-TEST_F(FormatArgImplTest, CharArraysDecayToCharPtr) {
-  const char* a = "";
-  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
-            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("")));
-  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
-            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("A")));
-  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
-            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("ABC")));
-  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
-            FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyArray)));
-}
-
-TEST_F(FormatArgImplTest, OtherPtrDecayToVoidPtr) {
-  auto expected = FormatArgImplFriend::GetVTablePtrForTest(
-      FormatArgImpl(static_cast<void *>(nullptr)));
-  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(
-                FormatArgImpl(static_cast<int *>(nullptr))),
-            expected);
-  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(
-                FormatArgImpl(static_cast<volatile int *>(nullptr))),
-            expected);
-
-  auto p = static_cast<void (*)()>([] {});
-  EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(p)),
-            expected);
-}
-
-TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) {
-  std::string s;
-  FormatSinkImpl sink(&s);
-  FormatConversionSpecImpl conv;
-  FormatConversionSpecImplFriend::SetConversionChar(
-      FormatConversionCharInternal::s, &conv);
-  FormatConversionSpecImplFriend::SetFlags(Flags(), &conv);
-  FormatConversionSpecImplFriend::SetWidth(-1, &conv);
-  FormatConversionSpecImplFriend::SetPrecision(-1, &conv);
-  EXPECT_TRUE(
-      FormatArgImplFriend::Convert(FormatArgImpl(kMyArray), conv, &sink));
-  sink.Flush();
-  EXPECT_EQ("ABCDE", s);
-}
-const char kMyArray[] = "ABCDE";
-
-}  // namespace
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/bind.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/bind.cc
deleted file mode 100644
index 4e68b90b5c..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/bind.cc
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/str_format/bind.h"
-
-#include <cerrno>
-#include <limits>
-#include <sstream>
-#include <string>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-namespace {
-
-inline bool BindFromPosition(int position, int* value,
-                             absl::Span<const FormatArgImpl> pack) {
-  assert(position > 0);
-  if (static_cast<size_t>(position) > pack.size()) {
-    return false;
-  }
-  // -1 because positions are 1-based
-  return FormatArgImplFriend::ToInt(pack[position - 1], value);
-}
-
-class ArgContext {
- public:
-  explicit ArgContext(absl::Span<const FormatArgImpl> pack) : pack_(pack) {}
-
-  // Fill 'bound' with the results of applying the context's argument pack
-  // to the specified 'unbound'. We synthesize a BoundConversion by
-  // lining up a UnboundConversion with a user argument. We also
-  // resolve any '*' specifiers for width and precision, so after
-  // this call, 'bound' has all the information it needs to be formatted.
-  // Returns false on failure.
-  bool Bind(const UnboundConversion* unbound, BoundConversion* bound);
-
- private:
-  absl::Span<const FormatArgImpl> pack_;
-};
-
-inline bool ArgContext::Bind(const UnboundConversion* unbound,
-                             BoundConversion* bound) {
-  const FormatArgImpl* arg = nullptr;
-  int arg_position = unbound->arg_position;
-  if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false;
-  arg = &pack_[arg_position - 1];  // 1-based
-
-  if (!unbound->flags.basic) {
-    int width = unbound->width.value();
-    bool force_left = false;
-    if (unbound->width.is_from_arg()) {
-      if (!BindFromPosition(unbound->width.get_from_arg(), &width, pack_))
-        return false;
-      if (width < 0) {
-        // "A negative field width is taken as a '-' flag followed by a
-        // positive field width."
-        force_left = true;
-        // Make sure we don't overflow the width when negating it.
-        width = -std::max(width, -std::numeric_limits<int>::max());
-      }
-    }
-
-    int precision = unbound->precision.value();
-    if (unbound->precision.is_from_arg()) {
-      if (!BindFromPosition(unbound->precision.get_from_arg(), &precision,
-                            pack_))
-        return false;
-    }
-
-    FormatConversionSpecImplFriend::SetWidth(width, bound);
-    FormatConversionSpecImplFriend::SetPrecision(precision, bound);
-
-    if (force_left) {
-      Flags flags = unbound->flags;
-      flags.left = true;
-      FormatConversionSpecImplFriend::SetFlags(flags, bound);
-    } else {
-      FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
-    }
-  } else {
-    FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
-    FormatConversionSpecImplFriend::SetWidth(-1, bound);
-    FormatConversionSpecImplFriend::SetPrecision(-1, bound);
-  }
-  FormatConversionSpecImplFriend::SetConversionChar(unbound->conv, bound);
-  bound->set_arg(arg);
-  return true;
-}
-
-template <typename Converter>
-class ConverterConsumer {
- public:
-  ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> pack)
-      : converter_(converter), arg_context_(pack) {}
-
-  bool Append(string_view s) {
-    converter_.Append(s);
-    return true;
-  }
-  bool ConvertOne(const UnboundConversion& conv, string_view conv_string) {
-    BoundConversion bound;
-    if (!arg_context_.Bind(&conv, &bound)) return false;
-    return converter_.ConvertOne(bound, conv_string);
-  }
-
- private:
-  Converter converter_;
-  ArgContext arg_context_;
-};
-
-template <typename Converter>
-bool ConvertAll(const UntypedFormatSpecImpl format,
-                absl::Span<const FormatArgImpl> args, Converter converter) {
-  if (format.has_parsed_conversion()) {
-    return format.parsed_conversion()->ProcessFormat(
-        ConverterConsumer<Converter>(converter, args));
-  } else {
-    return ParseFormatString(format.str(),
-                             ConverterConsumer<Converter>(converter, args));
-  }
-}
-
-class DefaultConverter {
- public:
-  explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {}
-
-  void Append(string_view s) const { sink_->Append(s); }
-
-  bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
-    return FormatArgImplFriend::Convert(*bound.arg(), bound, sink_);
-  }
-
- private:
-  FormatSinkImpl* sink_;
-};
-
-class SummarizingConverter {
- public:
-  explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {}
-
-  void Append(string_view s) const { sink_->Append(s); }
-
-  bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
-    UntypedFormatSpecImpl spec("%d");
-
-    std::ostringstream ss;
-    ss << "{" << Streamable(spec, {*bound.arg()}) << ":"
-       << FormatConversionSpecImplFriend::FlagsToString(bound);
-    if (bound.width() >= 0) ss << bound.width();
-    if (bound.precision() >= 0) ss << "." << bound.precision();
-    ss << bound.conversion_char() << "}";
-    Append(ss.str());
-    return true;
-  }
-
- private:
-  FormatSinkImpl* sink_;
-};
-
-}  // namespace
-
-bool BindWithPack(const UnboundConversion* props,
-                  absl::Span<const FormatArgImpl> pack,
-                  BoundConversion* bound) {
-  return ArgContext(pack).Bind(props, bound);
-}
-
-std::string Summarize(const UntypedFormatSpecImpl format,
-                      absl::Span<const FormatArgImpl> args) {
-  typedef SummarizingConverter Converter;
-  std::string out;
-  {
-    // inner block to destroy sink before returning out. It ensures a last
-    // flush.
-    FormatSinkImpl sink(&out);
-    if (!ConvertAll(format, args, Converter(&sink))) {
-      return "";
-    }
-  }
-  return out;
-}
-
-bool FormatUntyped(FormatRawSinkImpl raw_sink,
-                   const UntypedFormatSpecImpl format,
-                   absl::Span<const FormatArgImpl> args) {
-  FormatSinkImpl sink(raw_sink);
-  using Converter = DefaultConverter;
-  return ConvertAll(format, args, Converter(&sink));
-}
-
-std::ostream& Streamable::Print(std::ostream& os) const {
-  if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit);
-  return os;
-}
-
-std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format,
-                        absl::Span<const FormatArgImpl> args) {
-  size_t orig = out->size();
-  if (ABSL_PREDICT_FALSE(!FormatUntyped(out, format, args))) {
-    out->erase(orig);
-  }
-  return *out;
-}
-
-std::string FormatPack(const UntypedFormatSpecImpl format,
-                       absl::Span<const FormatArgImpl> args) {
-  std::string out;
-  if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) {
-    out.clear();
-  }
-  return out;
-}
-
-int FprintF(std::FILE* output, const UntypedFormatSpecImpl format,
-            absl::Span<const FormatArgImpl> args) {
-  FILERawSink sink(output);
-  if (!FormatUntyped(&sink, format, args)) {
-    errno = EINVAL;
-    return -1;
-  }
-  if (sink.error()) {
-    errno = sink.error();
-    return -1;
-  }
-  if (sink.count() > static_cast<size_t>(std::numeric_limits<int>::max())) {
-    errno = EFBIG;
-    return -1;
-  }
-  return static_cast<int>(sink.count());
-}
-
-int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format,
-             absl::Span<const FormatArgImpl> args) {
-  BufferRawSink sink(output, size ? size - 1 : 0);
-  if (!FormatUntyped(&sink, format, args)) {
-    errno = EINVAL;
-    return -1;
-  }
-  size_t total = sink.total_written();
-  if (size) output[std::min(total, size - 1)] = 0;
-  return static_cast<int>(total);
-}
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/bind.h b/third_party/abseil_cpp/absl/strings/internal/str_format/bind.h
deleted file mode 100644
index 267cc0ef69..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/bind.h
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
-#define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
-
-#include <array>
-#include <cstdio>
-#include <sstream>
-#include <string>
-
-#include "absl/base/port.h"
-#include "absl/strings/internal/str_format/arg.h"
-#include "absl/strings/internal/str_format/checker.h"
-#include "absl/strings/internal/str_format/parser.h"
-#include "absl/types/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class UntypedFormatSpec;
-
-namespace str_format_internal {
-
-class BoundConversion : public FormatConversionSpecImpl {
- public:
-  const FormatArgImpl* arg() const { return arg_; }
-  void set_arg(const FormatArgImpl* a) { arg_ = a; }
-
- private:
-  const FormatArgImpl* arg_;
-};
-
-// This is the type-erased class that the implementation uses.
-class UntypedFormatSpecImpl {
- public:
-  UntypedFormatSpecImpl() = delete;
-
-  explicit UntypedFormatSpecImpl(string_view s)
-      : data_(s.data()), size_(s.size()) {}
-  explicit UntypedFormatSpecImpl(
-      const str_format_internal::ParsedFormatBase* pc)
-      : data_(pc), size_(~size_t{}) {}
-
-  bool has_parsed_conversion() const { return size_ == ~size_t{}; }
-
-  string_view str() const {
-    assert(!has_parsed_conversion());
-    return string_view(static_cast<const char*>(data_), size_);
-  }
-  const str_format_internal::ParsedFormatBase* parsed_conversion() const {
-    assert(has_parsed_conversion());
-    return static_cast<const str_format_internal::ParsedFormatBase*>(data_);
-  }
-
-  template <typename T>
-  static const UntypedFormatSpecImpl& Extract(const T& s) {
-    return s.spec_;
-  }
-
- private:
-  const void* data_;
-  size_t size_;
-};
-
-template <typename T, FormatConversionCharSet...>
-struct MakeDependent {
-  using type = T;
-};
-
-// Implicitly convertible from `const char*`, `string_view`, and the
-// `ExtendedParsedFormat` type. This abstraction allows all format functions to
-// operate on any without providing too many overloads.
-template <FormatConversionCharSet... Args>
-class FormatSpecTemplate
-    : public MakeDependent<UntypedFormatSpec, Args...>::type {
-  using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type;
-
- public:
-#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-
-  // Honeypot overload for when the string is not constexpr.
-  // We use the 'unavailable' attribute to give a better compiler error than
-  // just 'method is deleted'.
-  FormatSpecTemplate(...)  // NOLINT
-      __attribute__((unavailable("Format string is not constexpr.")));
-
-  // Honeypot overload for when the format is constexpr and invalid.
-  // We use the 'unavailable' attribute to give a better compiler error than
-  // just 'method is deleted'.
-  // To avoid checking the format twice, we just check that the format is
-  // constexpr. If is it valid, then the overload below will kick in.
-  // We add the template here to make this overload have lower priority.
-  template <typename = void>
-  FormatSpecTemplate(const char* s)  // NOLINT
-      __attribute__((
-          enable_if(str_format_internal::EnsureConstexpr(s), "constexpr trap"),
-          unavailable(
-              "Format specified does not match the arguments passed.")));
-
-  template <typename T = void>
-  FormatSpecTemplate(string_view s)  // NOLINT
-      __attribute__((enable_if(str_format_internal::EnsureConstexpr(s),
-                               "constexpr trap"))) {
-    static_assert(sizeof(T*) == 0,
-                  "Format specified does not match the arguments passed.");
-  }
-
-  // Good format overload.
-  FormatSpecTemplate(const char* s)  // NOLINT
-      __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap")))
-      : Base(s) {}
-
-  FormatSpecTemplate(string_view s)  // NOLINT
-      __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap")))
-      : Base(s) {}
-
-#else  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-
-  FormatSpecTemplate(const char* s) : Base(s) {}  // NOLINT
-  FormatSpecTemplate(string_view s) : Base(s) {}  // NOLINT
-
-#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-
-  template <
-      FormatConversionCharSet... C,
-      typename = typename std::enable_if<sizeof...(C) == sizeof...(Args)>::type,
-      typename = typename std::enable_if<AllOf(Contains(Args,
-                                                        C)...)>::type>
-  FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc)  // NOLINT
-      : Base(&pc) {}
-};
-
-class Streamable {
- public:
-  Streamable(const UntypedFormatSpecImpl& format,
-             absl::Span<const FormatArgImpl> args)
-      : format_(format) {
-    if (args.size() <= ABSL_ARRAYSIZE(few_args_)) {
-      for (size_t i = 0; i < args.size(); ++i) {
-        few_args_[i] = args[i];
-      }
-      args_ = absl::MakeSpan(few_args_, args.size());
-    } else {
-      many_args_.assign(args.begin(), args.end());
-      args_ = many_args_;
-    }
-  }
-
-  std::ostream& Print(std::ostream& os) const;
-
-  friend std::ostream& operator<<(std::ostream& os, const Streamable& l) {
-    return l.Print(os);
-  }
-
- private:
-  const UntypedFormatSpecImpl& format_;
-  absl::Span<const FormatArgImpl> args_;
-  // if args_.size() is 4 or less:
-  FormatArgImpl few_args_[4] = {FormatArgImpl(0), FormatArgImpl(0),
-                                FormatArgImpl(0), FormatArgImpl(0)};
-  // if args_.size() is more than 4:
-  std::vector<FormatArgImpl> many_args_;
-};
-
-// for testing
-std::string Summarize(UntypedFormatSpecImpl format,
-                      absl::Span<const FormatArgImpl> args);
-bool BindWithPack(const UnboundConversion* props,
-                  absl::Span<const FormatArgImpl> pack, BoundConversion* bound);
-
-bool FormatUntyped(FormatRawSinkImpl raw_sink,
-                   UntypedFormatSpecImpl format,
-                   absl::Span<const FormatArgImpl> args);
-
-std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format,
-                        absl::Span<const FormatArgImpl> args);
-
-std::string FormatPack(const UntypedFormatSpecImpl format,
-                       absl::Span<const FormatArgImpl> args);
-
-int FprintF(std::FILE* output, UntypedFormatSpecImpl format,
-            absl::Span<const FormatArgImpl> args);
-int SnprintF(char* output, size_t size, UntypedFormatSpecImpl format,
-             absl::Span<const FormatArgImpl> args);
-
-// Returned by Streamed(v). Converts via '%s' to the std::string created
-// by std::ostream << v.
-template <typename T>
-class StreamedWrapper {
- public:
-  explicit StreamedWrapper(const T& v) : v_(v) { }
-
- private:
-  template <typename S>
-  friend ArgConvertResult<FormatConversionCharSetInternal::s> FormatConvertImpl(
-      const StreamedWrapper<S>& v, FormatConversionSpecImpl conv,
-      FormatSinkImpl* out);
-  const T& v_;
-};
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/bind_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/bind_test.cc
deleted file mode 100644
index 1eef9c4326..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/bind_test.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/str_format/bind.h"
-
-#include <string.h>
-#include <limits>
-
-#include "gtest/gtest.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-namespace {
-
-class FormatBindTest : public ::testing::Test {
- public:
-  bool Extract(const char *s, UnboundConversion *props, int *next) const {
-    return ConsumeUnboundConversion(s, s + strlen(s), props, next) ==
-           s + strlen(s);
-  }
-};
-
-TEST_F(FormatBindTest, BindSingle) {
-  struct Expectation {
-    int line;
-    const char *fmt;
-    int ok_phases;
-    const FormatArgImpl *arg;
-    int width;
-    int precision;
-    int next_arg;
-  };
-  const int no = -1;
-  const int ia[] = { 10, 20, 30, 40};
-  const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]),
-                                FormatArgImpl(ia[2]), FormatArgImpl(ia[3])};
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-  const Expectation kExpect[] = {
-    {__LINE__, "d",          2, &args[0], no, no, 2},
-    {__LINE__, "4d",         2, &args[0],  4, no, 2},
-    {__LINE__, ".5d",        2, &args[0], no,  5, 2},
-    {__LINE__, "4.5d",       2, &args[0],  4,  5, 2},
-    {__LINE__, "*d",         2, &args[1], 10, no, 3},
-    {__LINE__, ".*d",        2, &args[1], no, 10, 3},
-    {__LINE__, "*.*d",       2, &args[2], 10, 20, 4},
-    {__LINE__, "1$d",        2, &args[0], no, no, 0},
-    {__LINE__, "2$d",        2, &args[1], no, no, 0},
-    {__LINE__, "3$d",        2, &args[2], no, no, 0},
-    {__LINE__, "4$d",        2, &args[3], no, no, 0},
-    {__LINE__, "2$*1$d",     2, &args[1], 10, no, 0},
-    {__LINE__, "2$*2$d",     2, &args[1], 20, no, 0},
-    {__LINE__, "2$*3$d",     2, &args[1], 30, no, 0},
-    {__LINE__, "2$.*1$d",    2, &args[1], no, 10, 0},
-    {__LINE__, "2$.*2$d",    2, &args[1], no, 20, 0},
-    {__LINE__, "2$.*3$d",    2, &args[1], no, 30, 0},
-    {__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0},
-    {__LINE__, "2$*2$.*2$d", 2, &args[1], 20, 20, 0},
-    {__LINE__, "2$*1$.*3$d", 2, &args[1], 10, 30, 0},
-    {__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0},
-    {__LINE__, "1$*d",       0},  // indexed, then positional
-    {__LINE__, "*2$d",       0},  // positional, then indexed
-    {__LINE__, "6$d",        1},  // arg position out of bounds
-    {__LINE__, "1$6$d",      0},  // width position incorrectly specified
-    {__LINE__, "1$.6$d",     0},  // precision position incorrectly specified
-    {__LINE__, "1$*6$d",     1},  // width position out of bounds
-    {__LINE__, "1$.*6$d",    1},  // precision position out of bounds
-  };
-#pragma GCC diagnostic pop
-  for (const Expectation &e : kExpect) {
-    SCOPED_TRACE(e.line);
-    SCOPED_TRACE(e.fmt);
-    UnboundConversion props;
-    BoundConversion bound;
-    int ok_phases = 0;
-    int next = 0;
-    if (Extract(e.fmt, &props, &next)) {
-      ++ok_phases;
-      if (BindWithPack(&props, args, &bound)) {
-        ++ok_phases;
-      }
-    }
-    EXPECT_EQ(e.ok_phases, ok_phases);
-    if (e.ok_phases < 2) continue;
-    if (e.arg != nullptr) {
-      EXPECT_EQ(e.arg, bound.arg());
-    }
-    EXPECT_EQ(e.width, bound.width());
-    EXPECT_EQ(e.precision, bound.precision());
-  }
-}
-
-TEST_F(FormatBindTest, WidthUnderflowRegression) {
-  UnboundConversion props;
-  BoundConversion bound;
-  int next = 0;
-  const int args_i[] = {std::numeric_limits<int>::min(), 17};
-  const FormatArgImpl args[] = {FormatArgImpl(args_i[0]),
-                                FormatArgImpl(args_i[1])};
-  ASSERT_TRUE(Extract("*d", &props, &next));
-  ASSERT_TRUE(BindWithPack(&props, args, &bound));
-
-  EXPECT_EQ(bound.width(), std::numeric_limits<int>::max());
-  EXPECT_EQ(bound.arg(), args + 1);
-}
-
-TEST_F(FormatBindTest, FormatPack) {
-  struct Expectation {
-    int line;
-    const char *fmt;
-    const char *summary;
-  };
-  const int ia[] = { 10, 20, 30, 40, -10 };
-  const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]),
-                                FormatArgImpl(ia[2]), FormatArgImpl(ia[3]),
-                                FormatArgImpl(ia[4])};
-  const Expectation kExpect[] = {
-      {__LINE__, "a%4db%dc", "a{10:4d}b{20:d}c"},
-      {__LINE__, "a%.4db%dc", "a{10:.4d}b{20:d}c"},
-      {__LINE__, "a%4.5db%dc", "a{10:4.5d}b{20:d}c"},
-      {__LINE__, "a%db%4.5dc", "a{10:d}b{20:4.5d}c"},
-      {__LINE__, "a%db%*.*dc", "a{10:d}b{40:20.30d}c"},
-      {__LINE__, "a%.*fb", "a{20:.10f}b"},
-      {__LINE__, "a%1$db%2$*3$.*4$dc", "a{10:d}b{20:30.40d}c"},
-      {__LINE__, "a%4$db%3$*2$.*1$dc", "a{40:d}b{30:20.10d}c"},
-      {__LINE__, "a%04ldb", "a{10:04d}b"},
-      {__LINE__, "a%-#04lldb", "a{10:-#04d}b"},
-      {__LINE__, "a%1$*5$db", "a{10:-10d}b"},
-      {__LINE__, "a%1$.*5$db", "a{10:d}b"},
-  };
-  for (const Expectation &e : kExpect) {
-    absl::string_view fmt = e.fmt;
-    SCOPED_TRACE(e.line);
-    SCOPED_TRACE(e.fmt);
-    UntypedFormatSpecImpl format(fmt);
-    EXPECT_EQ(e.summary,
-              str_format_internal::Summarize(format, absl::MakeSpan(args)))
-        << "line:" << e.line;
-  }
-}
-
-}  // namespace
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/checker.h b/third_party/abseil_cpp/absl/strings/internal/str_format/checker.h
deleted file mode 100644
index 2a2601eccf..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/checker.h
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
-#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
-
-#include "absl/base/attributes.h"
-#include "absl/strings/internal/str_format/arg.h"
-#include "absl/strings/internal/str_format/extension.h"
-
-// Compile time check support for entry points.
-
-#ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__)
-#define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
-#endif  // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__)
-#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-constexpr bool AllOf() { return true; }
-
-template <typename... T>
-constexpr bool AllOf(bool b, T... t) {
-  return b && AllOf(t...);
-}
-
-#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-
-constexpr bool ContainsChar(const char* chars, char c) {
-  return *chars == c || (*chars && ContainsChar(chars + 1, c));
-}
-
-// A constexpr compatible list of Convs.
-struct ConvList {
-  const FormatConversionCharSet* array;
-  int count;
-
-  // We do the bound check here to avoid having to do it on the callers.
-  // Returning an empty FormatConversionCharSet has the same effect as
-  // short circuiting because it will never match any conversion.
-  constexpr FormatConversionCharSet operator[](int i) const {
-    return i < count ? array[i] : FormatConversionCharSet{};
-  }
-
-  constexpr ConvList without_front() const {
-    return count != 0 ? ConvList{array + 1, count - 1} : *this;
-  }
-};
-
-template <size_t count>
-struct ConvListT {
-  // Make sure the array has size > 0.
-  FormatConversionCharSet list[count ? count : 1];
-};
-
-constexpr char GetChar(string_view str, size_t index) {
-  return index < str.size() ? str[index] : char{};
-}
-
-constexpr string_view ConsumeFront(string_view str, size_t len = 1) {
-  return len <= str.size() ? string_view(str.data() + len, str.size() - len)
-                           : string_view();
-}
-
-constexpr string_view ConsumeAnyOf(string_view format, const char* chars) {
-  return ContainsChar(chars, GetChar(format, 0))
-             ? ConsumeAnyOf(ConsumeFront(format), chars)
-             : format;
-}
-
-constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; }
-
-// Helper class for the ParseDigits function.
-// It encapsulates the two return values we need there.
-struct Integer {
-  string_view format;
-  int value;
-
-  // If the next character is a '$', consume it.
-  // Otherwise, make `this` an invalid positional argument.
-  constexpr Integer ConsumePositionalDollar() const {
-    return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value}
-                                     : Integer{format, 0};
-  }
-};
-
-constexpr Integer ParseDigits(string_view format, int value = 0) {
-  return IsDigit(GetChar(format, 0))
-             ? ParseDigits(ConsumeFront(format),
-                           10 * value + GetChar(format, 0) - '0')
-             : Integer{format, value};
-}
-
-// Parse digits for a positional argument.
-// The parsing also consumes the '$'.
-constexpr Integer ParsePositional(string_view format) {
-  return ParseDigits(format).ConsumePositionalDollar();
-}
-
-// Parses a single conversion specifier.
-// See ConvParser::Run() for post conditions.
-class ConvParser {
-  constexpr ConvParser SetFormat(string_view format) const {
-    return ConvParser(format, args_, error_, arg_position_, is_positional_);
-  }
-
-  constexpr ConvParser SetArgs(ConvList args) const {
-    return ConvParser(format_, args, error_, arg_position_, is_positional_);
-  }
-
-  constexpr ConvParser SetError(bool error) const {
-    return ConvParser(format_, args_, error_ || error, arg_position_,
-                      is_positional_);
-  }
-
-  constexpr ConvParser SetArgPosition(int arg_position) const {
-    return ConvParser(format_, args_, error_, arg_position, is_positional_);
-  }
-
-  // Consumes the next arg and verifies that it matches `conv`.
-  // `error_` is set if there is no next arg or if it doesn't match `conv`.
-  constexpr ConvParser ConsumeNextArg(char conv) const {
-    return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv));
-  }
-
-  // Verify that positional argument `i.value` matches `conv`.
-  // `error_` is set if `i.value` is not a valid argument or if it doesn't
-  // match.
-  constexpr ConvParser VerifyPositional(Integer i, char conv) const {
-    return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv));
-  }
-
-  // Parse the position of the arg and store it in `arg_position_`.
-  constexpr ConvParser ParseArgPosition(Integer arg) const {
-    return SetFormat(arg.format).SetArgPosition(arg.value);
-  }
-
-  // Consume the flags.
-  constexpr ConvParser ParseFlags() const {
-    return SetFormat(ConsumeAnyOf(format_, "-+ #0"));
-  }
-
-  // Consume the width.
-  // If it is '*', we verify that it matches `args_`. `error_` is set if it
-  // doesn't match.
-  constexpr ConvParser ParseWidth() const {
-    return IsDigit(GetChar(format_, 0))
-               ? SetFormat(ParseDigits(format_).format)
-               : GetChar(format_, 0) == '*'
-                     ? is_positional_
-                           ? VerifyPositional(
-                                 ParsePositional(ConsumeFront(format_)), '*')
-                           : SetFormat(ConsumeFront(format_))
-                                 .ConsumeNextArg('*')
-                     : *this;
-  }
-
-  // Consume the precision.
-  // If it is '*', we verify that it matches `args_`. `error_` is set if it
-  // doesn't match.
-  constexpr ConvParser ParsePrecision() const {
-    return GetChar(format_, 0) != '.'
-               ? *this
-               : GetChar(format_, 1) == '*'
-                     ? is_positional_
-                           ? VerifyPositional(
-                                 ParsePositional(ConsumeFront(format_, 2)), '*')
-                           : SetFormat(ConsumeFront(format_, 2))
-                                 .ConsumeNextArg('*')
-                     : SetFormat(ParseDigits(ConsumeFront(format_)).format);
-  }
-
-  // Consume the length characters.
-  constexpr ConvParser ParseLength() const {
-    return SetFormat(ConsumeAnyOf(format_, "lLhjztq"));
-  }
-
-  // Consume the conversion character and verify that it matches `args_`.
-  // `error_` is set if it doesn't match.
-  constexpr ConvParser ParseConversion() const {
-    return is_positional_
-               ? VerifyPositional({ConsumeFront(format_), arg_position_},
-                                  GetChar(format_, 0))
-               : ConsumeNextArg(GetChar(format_, 0))
-                     .SetFormat(ConsumeFront(format_));
-  }
-
-  constexpr ConvParser(string_view format, ConvList args, bool error,
-                       int arg_position, bool is_positional)
-      : format_(format),
-        args_(args),
-        error_(error),
-        arg_position_(arg_position),
-        is_positional_(is_positional) {}
-
- public:
-  constexpr ConvParser(string_view format, ConvList args, bool is_positional)
-      : format_(format),
-        args_(args),
-        error_(false),
-        arg_position_(0),
-        is_positional_(is_positional) {}
-
-  // Consume the whole conversion specifier.
-  // `format()` will be set to the character after the conversion character.
-  // `error()` will be set if any of the arguments do not match.
-  constexpr ConvParser Run() const {
-    return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this)
-        .ParseFlags()
-        .ParseWidth()
-        .ParsePrecision()
-        .ParseLength()
-        .ParseConversion();
-  }
-
-  constexpr string_view format() const { return format_; }
-  constexpr ConvList args() const { return args_; }
-  constexpr bool error() const { return error_; }
-  constexpr bool is_positional() const { return is_positional_; }
-
- private:
-  string_view format_;
-  // Current list of arguments. If we are not in positional mode we will consume
-  // from the front.
-  ConvList args_;
-  bool error_;
-  // Holds the argument position of the conversion character, if we are in
-  // positional mode. Otherwise, it is unspecified.
-  int arg_position_;
-  // Whether we are in positional mode.
-  // It changes the behavior of '*' and where to find the converted argument.
-  bool is_positional_;
-};
-
-// Parses a whole format expression.
-// See FormatParser::Run().
-class FormatParser {
-  static constexpr bool FoundPercent(string_view format) {
-    return format.empty() ||
-           (GetChar(format, 0) == '%' && GetChar(format, 1) != '%');
-  }
-
-  // We use an inner function to increase the recursion limit.
-  // The inner function consumes up to `limit` characters on every run.
-  // This increases the limit from 512 to ~512*limit.
-  static constexpr string_view ConsumeNonPercentInner(string_view format,
-                                                      int limit = 20) {
-    return FoundPercent(format) || !limit
-               ? format
-               : ConsumeNonPercentInner(
-                     ConsumeFront(format, GetChar(format, 0) == '%' &&
-                                                  GetChar(format, 1) == '%'
-                                              ? 2
-                                              : 1),
-                     limit - 1);
-  }
-
-  // Consume characters until the next conversion spec %.
-  // It skips %%.
-  static constexpr string_view ConsumeNonPercent(string_view format) {
-    return FoundPercent(format)
-               ? format
-               : ConsumeNonPercent(ConsumeNonPercentInner(format));
-  }
-
-  static constexpr bool IsPositional(string_view format) {
-    return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format))
-                                       : GetChar(format, 0) == '$';
-  }
-
-  constexpr bool RunImpl(bool is_positional) const {
-    // In non-positional mode we require all arguments to be consumed.
-    // In positional mode just reaching the end of the format without errors is
-    // enough.
-    return (format_.empty() && (is_positional || args_.count == 0)) ||
-           (!format_.empty() &&
-            ValidateArg(
-                ConvParser(ConsumeFront(format_), args_, is_positional).Run()));
-  }
-
-  constexpr bool ValidateArg(ConvParser conv) const {
-    return !conv.error() && FormatParser(conv.format(), conv.args())
-                                .RunImpl(conv.is_positional());
-  }
-
- public:
-  constexpr FormatParser(string_view format, ConvList args)
-      : format_(ConsumeNonPercent(format)), args_(args) {}
-
-  // Runs the parser for `format` and `args`.
-  // It verifies that the format is valid and that all conversion specifiers
-  // match the arguments passed.
-  // In non-positional mode it also verfies that all arguments are consumed.
-  constexpr bool Run() const {
-    return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_)));
-  }
-
- private:
-  string_view format_;
-  // Current list of arguments.
-  // If we are not in positional mode we will consume from the front and will
-  // have to be empty in the end.
-  ConvList args_;
-};
-
-template <FormatConversionCharSet... C>
-constexpr bool ValidFormatImpl(string_view format) {
-  return FormatParser(format,
-                      {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
-      .Run();
-}
-
-#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/checker_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/checker_test.cc
deleted file mode 100644
index 7c70f47d68..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/checker_test.cc
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/str_format.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-namespace {
-
-std::string ConvToString(FormatConversionCharSet conv) {
-  std::string out;
-#define CONV_SET_CASE(c)                                    \
-  if (Contains(conv, FormatConversionCharSetInternal::c)) { \
-    out += #c;                                              \
-  }
-  ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
-#undef CONV_SET_CASE
-  if (Contains(conv, FormatConversionCharSetInternal::kStar)) {
-    out += "*";
-  }
-  return out;
-}
-
-TEST(StrFormatChecker, ArgumentToConv) {
-  FormatConversionCharSet conv = ArgumentToConv<std::string>();
-  EXPECT_EQ(ConvToString(conv), "s");
-
-  conv = ArgumentToConv<const char*>();
-  EXPECT_EQ(ConvToString(conv), "sp");
-
-  conv = ArgumentToConv<double>();
-  EXPECT_EQ(ConvToString(conv), "fFeEgGaA");
-
-  conv = ArgumentToConv<int>();
-  EXPECT_EQ(ConvToString(conv), "cdiouxXfFeEgGaA*");
-
-  conv = ArgumentToConv<std::string*>();
-  EXPECT_EQ(ConvToString(conv), "p");
-}
-
-#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-
-struct Case {
-  bool result;
-  const char* format;
-};
-
-template <typename... Args>
-constexpr Case ValidFormat(const char* format) {
-  return {ValidFormatImpl<ArgumentToConv<Args>()...>(format), format};
-}
-
-TEST(StrFormatChecker, ValidFormat) {
-  // We want to make sure these expressions are constexpr and they have the
-  // expected value.
-  // If they are not constexpr the attribute will just ignore them and not give
-  // a compile time error.
-  enum e {};
-  enum class e2 {};
-  constexpr Case trues[] = {
-      ValidFormat<>("abc"),  //
-
-      ValidFormat<e>("%d"),                             //
-      ValidFormat<e2>("%d"),                            //
-      ValidFormat<int>("%% %d"),                        //
-      ValidFormat<int>("%ld"),                          //
-      ValidFormat<int>("%lld"),                         //
-      ValidFormat<std::string>("%s"),                   //
-      ValidFormat<std::string>("%10s"),                 //
-      ValidFormat<int>("%.10x"),                        //
-      ValidFormat<int, int>("%*.3x"),                   //
-      ValidFormat<int>("%1.d"),                         //
-      ValidFormat<int>("%.d"),                          //
-      ValidFormat<int, double>("%d %g"),                //
-      ValidFormat<int, std::string>("%*s"),             //
-      ValidFormat<int, double>("%.*f"),                 //
-      ValidFormat<void (*)(), volatile int*>("%p %p"),  //
-      ValidFormat<string_view, const char*, double, void*>(
-          "string_view=%s const char*=%s double=%f void*=%p)"),
-
-      ValidFormat<int>("%% %1$d"),               //
-      ValidFormat<int>("%1$ld"),                 //
-      ValidFormat<int>("%1$lld"),                //
-      ValidFormat<std::string>("%1$s"),          //
-      ValidFormat<std::string>("%1$10s"),        //
-      ValidFormat<int>("%1$.10x"),               //
-      ValidFormat<int>("%1$*1$.*1$d"),           //
-      ValidFormat<int, int>("%1$*2$.3x"),        //
-      ValidFormat<int>("%1$1.d"),                //
-      ValidFormat<int>("%1$.d"),                 //
-      ValidFormat<double, int>("%2$d %1$g"),     //
-      ValidFormat<int, std::string>("%2$*1$s"),  //
-      ValidFormat<int, double>("%2$.*1$f"),      //
-      ValidFormat<void*, string_view, const char*, double>(
-          "string_view=%2$s const char*=%3$s double=%4$f void*=%1$p "
-          "repeat=%3$s)")};
-
-  for (Case c : trues) {
-    EXPECT_TRUE(c.result) << c.format;
-  }
-
-  constexpr Case falses[] = {
-      ValidFormat<int>(""),  //
-
-      ValidFormat<e>("%s"),                  //
-      ValidFormat<e2>("%s"),                 //
-      ValidFormat<>("%s"),                   //
-      ValidFormat<>("%r"),                   //
-      ValidFormat<int>("%s"),                //
-      ValidFormat<int>("%.1.d"),             //
-      ValidFormat<int>("%*1d"),              //
-      ValidFormat<int>("%1-d"),              //
-      ValidFormat<std::string, int>("%*s"),  //
-      ValidFormat<int>("%*d"),               //
-      ValidFormat<std::string>("%p"),        //
-      ValidFormat<int (*)(int)>("%d"),       //
-
-      ValidFormat<>("%3$d"),                     //
-      ValidFormat<>("%1$r"),                     //
-      ValidFormat<int>("%1$s"),                  //
-      ValidFormat<int>("%1$.1.d"),               //
-      ValidFormat<int>("%1$*2$1d"),              //
-      ValidFormat<int>("%1$1-d"),                //
-      ValidFormat<std::string, int>("%2$*1$s"),  //
-      ValidFormat<std::string>("%1$p"),
-
-      ValidFormat<int, int>("%d %2$d"),  //
-  };
-
-  for (Case c : falses) {
-    EXPECT_FALSE(c.result) << c.format;
-  }
-}
-
-TEST(StrFormatChecker, LongFormat) {
-#define CHARS_X_40 "1234567890123456789012345678901234567890"
-#define CHARS_X_400                                                            \
-  CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 \
-      CHARS_X_40 CHARS_X_40 CHARS_X_40
-#define CHARS_X_4000                                                      \
-  CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 \
-      CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400
-  constexpr char long_format[] =
-      CHARS_X_4000 "%d" CHARS_X_4000 "%s" CHARS_X_4000;
-  constexpr bool is_valid = ValidFormat<int, std::string>(long_format).result;
-  EXPECT_TRUE(is_valid);
-}
-
-#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-
-}  // namespace
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/convert_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/convert_test.cc
deleted file mode 100644
index 375db0a059..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/convert_test.cc
+++ /dev/null
@@ -1,1242 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#include <cctype>
-#include <cmath>
-#include <limits>
-#include <string>
-#include <thread>  // NOLINT
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/internal/str_format/bind.h"
-#include "absl/strings/match.h"
-#include "absl/types/optional.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-namespace {
-
-struct NativePrintfTraits {
-  bool hex_float_has_glibc_rounding;
-  bool hex_float_prefers_denormal_repr;
-  bool hex_float_uses_minimal_precision_when_not_specified;
-  bool hex_float_optimizes_leading_digit_bit_count;
-};
-
-template <typename T, size_t N>
-size_t ArraySize(T (&)[N]) {
-  return N;
-}
-
-std::string LengthModFor(float) { return ""; }
-std::string LengthModFor(double) { return ""; }
-std::string LengthModFor(long double) { return "L"; }
-std::string LengthModFor(char) { return "hh"; }
-std::string LengthModFor(signed char) { return "hh"; }
-std::string LengthModFor(unsigned char) { return "hh"; }
-std::string LengthModFor(short) { return "h"; }           // NOLINT
-std::string LengthModFor(unsigned short) { return "h"; }  // NOLINT
-std::string LengthModFor(int) { return ""; }
-std::string LengthModFor(unsigned) { return ""; }
-std::string LengthModFor(long) { return "l"; }                 // NOLINT
-std::string LengthModFor(unsigned long) { return "l"; }        // NOLINT
-std::string LengthModFor(long long) { return "ll"; }           // NOLINT
-std::string LengthModFor(unsigned long long) { return "ll"; }  // NOLINT
-
-std::string EscCharImpl(int v) {
-  if (std::isprint(static_cast<unsigned char>(v))) {
-    return std::string(1, static_cast<char>(v));
-  }
-  char buf[64];
-  int n = snprintf(buf, sizeof(buf), "\\%#.2x",
-                   static_cast<unsigned>(v & 0xff));
-  assert(n > 0 && n < sizeof(buf));
-  return std::string(buf, n);
-}
-
-std::string Esc(char v) { return EscCharImpl(v); }
-std::string Esc(signed char v) { return EscCharImpl(v); }
-std::string Esc(unsigned char v) { return EscCharImpl(v); }
-
-template <typename T>
-std::string Esc(const T &v) {
-  std::ostringstream oss;
-  oss << v;
-  return oss.str();
-}
-
-void StrAppendV(std::string *dst, const char *format, va_list ap) {
-  // First try with a small fixed size buffer
-  static const int kSpaceLength = 1024;
-  char space[kSpaceLength];
-
-  // It's possible for methods that use a va_list to invalidate
-  // the data in it upon use.  The fix is to make a copy
-  // of the structure before using it and use that copy instead.
-  va_list backup_ap;
-  va_copy(backup_ap, ap);
-  int result = vsnprintf(space, kSpaceLength, format, backup_ap);
-  va_end(backup_ap);
-  if (result < kSpaceLength) {
-    if (result >= 0) {
-      // Normal case -- everything fit.
-      dst->append(space, result);
-      return;
-    }
-    if (result < 0) {
-      // Just an error.
-      return;
-    }
-  }
-
-  // Increase the buffer size to the size requested by vsnprintf,
-  // plus one for the closing \0.
-  int length = result + 1;
-  char *buf = new char[length];
-
-  // Restore the va_list before we use it again
-  va_copy(backup_ap, ap);
-  result = vsnprintf(buf, length, format, backup_ap);
-  va_end(backup_ap);
-
-  if (result >= 0 && result < length) {
-    // It fit
-    dst->append(buf, result);
-  }
-  delete[] buf;
-}
-
-void StrAppend(std::string *out, const char *format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  StrAppendV(out, format, ap);
-  va_end(ap);
-}
-
-std::string StrPrint(const char *format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  std::string result;
-  StrAppendV(&result, format, ap);
-  va_end(ap);
-  return result;
-}
-
-NativePrintfTraits VerifyNativeImplementationImpl() {
-  NativePrintfTraits result;
-
-  // >>> hex_float_has_glibc_rounding. To have glibc's rounding behavior we need
-  // to meet three requirements:
-  //
-  //   - The threshold for rounding up is 8 (for e.g. MSVC uses 9).
-  //   - If the digits lower than than the 8 are non-zero then we round up.
-  //   - If the digits lower than the 8 are all zero then we round toward even.
-  //
-  // The numbers below represent all the cases covering {below,at,above} the
-  // threshold (8) with both {zero,non-zero} lower bits and both {even,odd}
-  // preceding digits.
-  const double d0079 = 65657.0;  // 0x1.0079p+16
-  const double d0179 = 65913.0;  // 0x1.0179p+16
-  const double d0080 = 65664.0;  // 0x1.0080p+16
-  const double d0180 = 65920.0;  // 0x1.0180p+16
-  const double d0081 = 65665.0;  // 0x1.0081p+16
-  const double d0181 = 65921.0;  // 0x1.0181p+16
-  result.hex_float_has_glibc_rounding =
-      StartsWith(StrPrint("%.2a", d0079), "0x1.00") &&
-      StartsWith(StrPrint("%.2a", d0179), "0x1.01") &&
-      StartsWith(StrPrint("%.2a", d0080), "0x1.00") &&
-      StartsWith(StrPrint("%.2a", d0180), "0x1.02") &&
-      StartsWith(StrPrint("%.2a", d0081), "0x1.01") &&
-      StartsWith(StrPrint("%.2a", d0181), "0x1.02");
-
-  // >>> hex_float_prefers_denormal_repr. Formatting `denormal` on glibc yields
-  // "0x0.0000000000001p-1022", whereas on std libs that don't use denormal
-  // representation it would either be 0x1p-1074 or 0x1.0000000000000-1074.
-  const double denormal = std::numeric_limits<double>::denorm_min();
-  result.hex_float_prefers_denormal_repr =
-      StartsWith(StrPrint("%a", denormal), "0x0.0000000000001");
-
-  // >>> hex_float_uses_minimal_precision_when_not_specified. Some (non-glibc)
-  // libs will format the following as "0x1.0079000000000p+16".
-  result.hex_float_uses_minimal_precision_when_not_specified =
-      (StrPrint("%a", d0079) == "0x1.0079p+16");
-
-  // >>> hex_float_optimizes_leading_digit_bit_count. The number 1.5, when
-  // formatted by glibc should yield "0x1.8p+0" for `double` and "0xcp-3" for
-  // `long double`, i.e., number of bits in the leading digit is adapted to the
-  // number of bits in the mantissa.
-  const double d_15 = 1.5;
-  const long double ld_15 = 1.5;
-  result.hex_float_optimizes_leading_digit_bit_count =
-      StartsWith(StrPrint("%a", d_15), "0x1.8") &&
-      StartsWith(StrPrint("%La", ld_15), "0xc");
-
-  return result;
-}
-
-const NativePrintfTraits &VerifyNativeImplementation() {
-  static NativePrintfTraits native_traits = VerifyNativeImplementationImpl();
-  return native_traits;
-}
-
-class FormatConvertTest : public ::testing::Test { };
-
-template <typename T>
-void TestStringConvert(const T& str) {
-  const FormatArgImpl args[] = {FormatArgImpl(str)};
-  struct Expectation {
-    const char *out;
-    const char *fmt;
-  };
-  const Expectation kExpect[] = {
-    {"hello",  "%1$s"      },
-    {"",       "%1$.s"     },
-    {"",       "%1$.0s"    },
-    {"h",      "%1$.1s"    },
-    {"he",     "%1$.2s"    },
-    {"hello",  "%1$.10s"   },
-    {" hello", "%1$6s"     },
-    {"   he",  "%1$5.2s"   },
-    {"he   ",  "%1$-5.2s"  },
-    {"hello ", "%1$-6.10s" },
-  };
-  for (const Expectation &e : kExpect) {
-    UntypedFormatSpecImpl format(e.fmt);
-    EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
-  }
-}
-
-TEST_F(FormatConvertTest, BasicString) {
-  TestStringConvert("hello");  // As char array.
-  TestStringConvert(static_cast<const char*>("hello"));
-  TestStringConvert(std::string("hello"));
-  TestStringConvert(string_view("hello"));
-}
-
-TEST_F(FormatConvertTest, NullString) {
-  const char* p = nullptr;
-  UntypedFormatSpecImpl format("%s");
-  EXPECT_EQ("", FormatPack(format, {FormatArgImpl(p)}));
-}
-
-TEST_F(FormatConvertTest, StringPrecision) {
-  // We cap at the precision.
-  char c = 'a';
-  const char* p = &c;
-  UntypedFormatSpecImpl format("%.1s");
-  EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)}));
-
-  // We cap at the NUL-terminator.
-  p = "ABC";
-  UntypedFormatSpecImpl format2("%.10s");
-  EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)}));
-}
-
-// Pointer formatting is implementation defined. This checks that the argument
-// can be matched to `ptr`.
-MATCHER_P(MatchesPointerString, ptr, "") {
-  if (ptr == nullptr && arg == "(nil)") {
-    return true;
-  }
-  void* parsed = nullptr;
-  if (sscanf(arg.c_str(), "%p", &parsed) != 1) {
-    ABSL_RAW_LOG(FATAL, "Could not parse %s", arg.c_str());
-  }
-  return ptr == parsed;
-}
-
-TEST_F(FormatConvertTest, Pointer) {
-  static int x = 0;
-  const int *xp = &x;
-  char c = 'h';
-  char *mcp = &c;
-  const char *cp = "hi";
-  const char *cnil = nullptr;
-  const int *inil = nullptr;
-  using VoidF = void (*)();
-  VoidF fp = [] {}, fnil = nullptr;
-  volatile char vc;
-  volatile char *vcp = &vc;
-  volatile char *vcnil = nullptr;
-  const FormatArgImpl args_array[] = {
-      FormatArgImpl(xp),   FormatArgImpl(cp),  FormatArgImpl(inil),
-      FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp),
-      FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil),
-  };
-  auto args = absl::MakeConstSpan(args_array);
-
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%p"), args),
-              MatchesPointerString(&x));
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%20p"), args),
-              MatchesPointerString(&x));
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.1p"), args),
-              MatchesPointerString(&x));
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
-              MatchesPointerString(&x));
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%30.20p"), args),
-              MatchesPointerString(&x));
-
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-p"), args),
-              MatchesPointerString(&x));
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-20p"), args),
-              MatchesPointerString(&x));
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-.1p"), args),
-              MatchesPointerString(&x));
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
-              MatchesPointerString(&x));
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args),
-              MatchesPointerString(&x));
-
-  // const char*
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args),
-              MatchesPointerString(cp));
-  // null const int*
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args),
-              MatchesPointerString(nullptr));
-  // null const char*
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args),
-              MatchesPointerString(nullptr));
-  // nonconst char*
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args),
-              MatchesPointerString(mcp));
-
-  // function pointers
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args),
-              MatchesPointerString(reinterpret_cast<const void*>(fp)));
-  EXPECT_THAT(
-      FormatPack(UntypedFormatSpecImpl("%8$p"), args),
-      MatchesPointerString(reinterpret_cast<volatile const void *>(vcp)));
-
-  // null function pointers
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args),
-              MatchesPointerString(nullptr));
-  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args),
-              MatchesPointerString(nullptr));
-}
-
-struct Cardinal {
-  enum Pos { k1 = 1, k2 = 2, k3 = 3 };
-  enum Neg { kM1 = -1, kM2 = -2, kM3 = -3 };
-};
-
-TEST_F(FormatConvertTest, Enum) {
-  const Cardinal::Pos k3 = Cardinal::k3;
-  const Cardinal::Neg km3 = Cardinal::kM3;
-  const FormatArgImpl args[] = {FormatArgImpl(k3), FormatArgImpl(km3)};
-  UntypedFormatSpecImpl format("%1$d");
-  UntypedFormatSpecImpl format2("%2$d");
-  EXPECT_EQ("3", FormatPack(format, absl::MakeSpan(args)));
-  EXPECT_EQ("-3", FormatPack(format2, absl::MakeSpan(args)));
-}
-
-template <typename T>
-class TypedFormatConvertTest : public FormatConvertTest { };
-
-TYPED_TEST_SUITE_P(TypedFormatConvertTest);
-
-std::vector<std::string> AllFlagCombinations() {
-  const char kFlags[] = {'-', '#', '0', '+', ' '};
-  std::vector<std::string> result;
-  for (size_t fsi = 0; fsi < (1ull << ArraySize(kFlags)); ++fsi) {
-    std::string flag_set;
-    for (size_t fi = 0; fi < ArraySize(kFlags); ++fi)
-      if (fsi & (1ull << fi))
-        flag_set += kFlags[fi];
-    result.push_back(flag_set);
-  }
-  return result;
-}
-
-TYPED_TEST_P(TypedFormatConvertTest, AllIntsWithFlags) {
-  typedef TypeParam T;
-  typedef typename std::make_unsigned<T>::type UnsignedT;
-  using remove_volatile_t = typename std::remove_volatile<T>::type;
-  const T kMin = std::numeric_limits<remove_volatile_t>::min();
-  const T kMax = std::numeric_limits<remove_volatile_t>::max();
-  const T kVals[] = {
-      remove_volatile_t(1),
-      remove_volatile_t(2),
-      remove_volatile_t(3),
-      remove_volatile_t(123),
-      remove_volatile_t(-1),
-      remove_volatile_t(-2),
-      remove_volatile_t(-3),
-      remove_volatile_t(-123),
-      remove_volatile_t(0),
-      kMax - remove_volatile_t(1),
-      kMax,
-      kMin + remove_volatile_t(1),
-      kMin,
-  };
-  const char kConvChars[] = {'d', 'i', 'u', 'o', 'x', 'X'};
-  const std::string kWid[] = {"", "4", "10"};
-  const std::string kPrec[] = {"", ".", ".0", ".4", ".10"};
-
-  const std::vector<std::string> flag_sets = AllFlagCombinations();
-
-  for (size_t vi = 0; vi < ArraySize(kVals); ++vi) {
-    const T val = kVals[vi];
-    SCOPED_TRACE(Esc(val));
-    const FormatArgImpl args[] = {FormatArgImpl(val)};
-    for (size_t ci = 0; ci < ArraySize(kConvChars); ++ci) {
-      const char conv_char = kConvChars[ci];
-      for (size_t fsi = 0; fsi < flag_sets.size(); ++fsi) {
-        const std::string &flag_set = flag_sets[fsi];
-        for (size_t wi = 0; wi < ArraySize(kWid); ++wi) {
-          const std::string &wid = kWid[wi];
-          for (size_t pi = 0; pi < ArraySize(kPrec); ++pi) {
-            const std::string &prec = kPrec[pi];
-
-            const bool is_signed_conv = (conv_char == 'd' || conv_char == 'i');
-            const bool is_unsigned_to_signed =
-                !std::is_signed<T>::value && is_signed_conv;
-            // Don't consider sign-related flags '+' and ' ' when doing
-            // unsigned to signed conversions.
-            if (is_unsigned_to_signed &&
-                flag_set.find_first_of("+ ") != std::string::npos) {
-              continue;
-            }
-
-            std::string new_fmt("%");
-            new_fmt += flag_set;
-            new_fmt += wid;
-            new_fmt += prec;
-            // old and new always agree up to here.
-            std::string old_fmt = new_fmt;
-            new_fmt += conv_char;
-            std::string old_result;
-            if (is_unsigned_to_signed) {
-              // don't expect agreement on unsigned formatted as signed,
-              // as printf can't do that conversion properly. For those
-              // cases, we do expect agreement with printf with a "%u"
-              // and the unsigned equivalent of 'val'.
-              UnsignedT uval = val;
-              old_fmt += LengthModFor(uval);
-              old_fmt += "u";
-              old_result = StrPrint(old_fmt.c_str(), uval);
-            } else {
-              old_fmt += LengthModFor(val);
-              old_fmt += conv_char;
-              old_result = StrPrint(old_fmt.c_str(), val);
-            }
-
-            SCOPED_TRACE(std::string() + " old_fmt: \"" + old_fmt +
-                         "\"'"
-                         " new_fmt: \"" +
-                         new_fmt + "\"");
-            UntypedFormatSpecImpl format(new_fmt);
-            EXPECT_EQ(old_result, FormatPack(format, absl::MakeSpan(args)));
-          }
-        }
-      }
-    }
-  }
-}
-
-TYPED_TEST_P(TypedFormatConvertTest, Char) {
-  typedef TypeParam T;
-  using remove_volatile_t = typename std::remove_volatile<T>::type;
-  static const T kMin = std::numeric_limits<remove_volatile_t>::min();
-  static const T kMax = std::numeric_limits<remove_volatile_t>::max();
-  T kVals[] = {
-    remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10),
-    remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10),
-    remove_volatile_t(0),
-    kMin + remove_volatile_t(1), kMin,
-    kMax - remove_volatile_t(1), kMax
-  };
-  for (const T &c : kVals) {
-    const FormatArgImpl args[] = {FormatArgImpl(c)};
-    UntypedFormatSpecImpl format("%c");
-    EXPECT_EQ(StrPrint("%c", c), FormatPack(format, absl::MakeSpan(args)));
-  }
-}
-
-REGISTER_TYPED_TEST_CASE_P(TypedFormatConvertTest, AllIntsWithFlags, Char);
-
-typedef ::testing::Types<
-    int, unsigned, volatile int,
-    short, unsigned short,
-    long, unsigned long,
-    long long, unsigned long long,
-    signed char, unsigned char, char>
-    AllIntTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes,
-                              TypedFormatConvertTest, AllIntTypes);
-TEST_F(FormatConvertTest, VectorBool) {
-  // Make sure vector<bool>'s values behave as bools.
-  std::vector<bool> v = {true, false};
-  const std::vector<bool> cv = {true, false};
-  EXPECT_EQ("1,0,1,0",
-            FormatPack(UntypedFormatSpecImpl("%d,%d,%d,%d"),
-                       absl::Span<const FormatArgImpl>(
-                           {FormatArgImpl(v[0]), FormatArgImpl(v[1]),
-                            FormatArgImpl(cv[0]), FormatArgImpl(cv[1])})));
-}
-
-
-TEST_F(FormatConvertTest, Int128) {
-  absl::int128 positive = static_cast<absl::int128>(0x1234567890abcdef) * 1979;
-  absl::int128 negative = -positive;
-  absl::int128 max = absl::Int128Max(), min = absl::Int128Min();
-  const FormatArgImpl args[] = {FormatArgImpl(positive),
-                                FormatArgImpl(negative), FormatArgImpl(max),
-                                FormatArgImpl(min)};
-
-  struct Case {
-    const char* format;
-    const char* expected;
-  } cases[] = {
-      {"%1$d", "2595989796776606496405"},
-      {"%1$30d", "        2595989796776606496405"},
-      {"%1$-30d", "2595989796776606496405        "},
-      {"%1$u", "2595989796776606496405"},
-      {"%1$x", "8cba9876066020f695"},
-      {"%2$d", "-2595989796776606496405"},
-      {"%2$30d", "       -2595989796776606496405"},
-      {"%2$-30d", "-2595989796776606496405       "},
-      {"%2$u", "340282366920938460867384810655161715051"},
-      {"%2$x", "ffffffffffffff73456789f99fdf096b"},
-      {"%3$d", "170141183460469231731687303715884105727"},
-      {"%3$u", "170141183460469231731687303715884105727"},
-      {"%3$x", "7fffffffffffffffffffffffffffffff"},
-      {"%4$d", "-170141183460469231731687303715884105728"},
-      {"%4$x", "80000000000000000000000000000000"},
-  };
-
-  for (auto c : cases) {
-    UntypedFormatSpecImpl format(c.format);
-    EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args)));
-  }
-}
-
-TEST_F(FormatConvertTest, Uint128) {
-  absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979;
-  absl::uint128 max = absl::Uint128Max();
-  const FormatArgImpl args[] = {FormatArgImpl(v), FormatArgImpl(max)};
-
-  struct Case {
-    const char* format;
-    const char* expected;
-  } cases[] = {
-      {"%1$d", "2595989796776606496405"},
-      {"%1$30d", "        2595989796776606496405"},
-      {"%1$-30d", "2595989796776606496405        "},
-      {"%1$u", "2595989796776606496405"},
-      {"%1$x", "8cba9876066020f695"},
-      {"%2$d", "340282366920938463463374607431768211455"},
-      {"%2$u", "340282366920938463463374607431768211455"},
-      {"%2$x", "ffffffffffffffffffffffffffffffff"},
-  };
-
-  for (auto c : cases) {
-    UntypedFormatSpecImpl format(c.format);
-    EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args)));
-  }
-}
-
-template <typename Floating>
-void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats) {
-  const NativePrintfTraits &native_traits = VerifyNativeImplementation();
-  // Reserve the space to ensure we don't allocate memory in the output itself.
-  std::string str_format_result;
-  str_format_result.reserve(1 << 20);
-  std::string string_printf_result;
-  string_printf_result.reserve(1 << 20);
-
-  const char *const kFormats[] = {
-      "%",  "%.3", "%8.5", "%500",   "%.5000", "%.60", "%.30",   "%03",
-      "%+", "% ",  "%-10", "%#15.3", "%#.0",   "%.0",  "%1$*2$", "%1$.*2$"};
-
-  for (const char *fmt : kFormats) {
-    for (char f : {'f', 'F',  //
-                   'g', 'G',  //
-                   'a', 'A',  //
-                   'e', 'E'}) {
-      std::string fmt_str = std::string(fmt) + f;
-
-      if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F' &&
-          f != 'a' && f != 'A') {
-        // This particular test takes way too long with snprintf.
-        // Disable for the case we are not implementing natively.
-        continue;
-      }
-
-      if ((f == 'a' || f == 'A') &&
-          !native_traits.hex_float_has_glibc_rounding) {
-        continue;
-      }
-
-      for (Floating d : floats) {
-        if (!native_traits.hex_float_prefers_denormal_repr &&
-            (f == 'a' || f == 'A') && std::fpclassify(d) == FP_SUBNORMAL) {
-          continue;
-        }
-        int i = -10;
-        FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)};
-        UntypedFormatSpecImpl format(fmt_str);
-
-        string_printf_result.clear();
-        StrAppend(&string_printf_result, fmt_str.c_str(), d, i);
-        str_format_result.clear();
-
-        {
-          AppendPack(&str_format_result, format, absl::MakeSpan(args));
-        }
-
-        if (string_printf_result != str_format_result) {
-          // We use ASSERT_EQ here because failures are usually correlated and a
-          // bug would print way too many failed expectations causing the test
-          // to time out.
-          ASSERT_EQ(string_printf_result, str_format_result)
-              << fmt_str << " " << StrPrint("%.18g", d) << " "
-              << StrPrint("%a", d) << " " << StrPrint("%.50f", d);
-        }
-      }
-    }
-  }
-}
-
-TEST_F(FormatConvertTest, Float) {
-#ifdef _MSC_VER
-  // MSVC has a different rounding policy than us so we can't test our
-  // implementation against the native one there.
-  return;
-#endif  // _MSC_VER
-
-  std::vector<float> floats = {0.0f,
-                               -0.0f,
-                               .9999999f,
-                               9999999.f,
-                               std::numeric_limits<float>::max(),
-                               -std::numeric_limits<float>::max(),
-                               std::numeric_limits<float>::min(),
-                               -std::numeric_limits<float>::min(),
-                               std::numeric_limits<float>::lowest(),
-                               -std::numeric_limits<float>::lowest(),
-                               std::numeric_limits<float>::epsilon(),
-                               std::numeric_limits<float>::epsilon() + 1.0f,
-                               std::numeric_limits<float>::infinity(),
-                               -std::numeric_limits<float>::infinity()};
-
-  // Some regression tests.
-  floats.push_back(0.999999989f);
-
-  if (std::numeric_limits<float>::has_denorm != std::denorm_absent) {
-    floats.push_back(std::numeric_limits<float>::denorm_min());
-    floats.push_back(-std::numeric_limits<float>::denorm_min());
-  }
-
-  for (float base :
-       {1.f, 12.f, 123.f, 1234.f, 12345.f, 123456.f, 1234567.f, 12345678.f,
-        123456789.f, 1234567890.f, 12345678901.f, 12345678.f, 12345678.f}) {
-    for (int exp = -123; exp <= 123; ++exp) {
-      for (int sign : {1, -1}) {
-        floats.push_back(sign * std::ldexp(base, exp));
-      }
-    }
-  }
-
-  for (int exp = -300; exp <= 300; ++exp) {
-    const float all_ones_mantissa = 0xffffff;
-    floats.push_back(std::ldexp(all_ones_mantissa, exp));
-  }
-
-  // Remove duplicates to speed up the logic below.
-  std::sort(floats.begin(), floats.end());
-  floats.erase(std::unique(floats.begin(), floats.end()), floats.end());
-
-#ifndef __APPLE__
-  // Apple formats NaN differently (+nan) vs. (nan)
-  floats.push_back(std::nan(""));
-#endif
-
-  TestWithMultipleFormatsHelper(floats);
-}
-
-TEST_F(FormatConvertTest, Double) {
-#ifdef _MSC_VER
-  // MSVC has a different rounding policy than us so we can't test our
-  // implementation against the native one there.
-  return;
-#endif  // _MSC_VER
-
-  std::vector<double> doubles = {0.0,
-                                 -0.0,
-                                 .99999999999999,
-                                 99999999999999.,
-                                 std::numeric_limits<double>::max(),
-                                 -std::numeric_limits<double>::max(),
-                                 std::numeric_limits<double>::min(),
-                                 -std::numeric_limits<double>::min(),
-                                 std::numeric_limits<double>::lowest(),
-                                 -std::numeric_limits<double>::lowest(),
-                                 std::numeric_limits<double>::epsilon(),
-                                 std::numeric_limits<double>::epsilon() + 1,
-                                 std::numeric_limits<double>::infinity(),
-                                 -std::numeric_limits<double>::infinity()};
-
-  // Some regression tests.
-  doubles.push_back(0.99999999999999989);
-
-  if (std::numeric_limits<double>::has_denorm != std::denorm_absent) {
-    doubles.push_back(std::numeric_limits<double>::denorm_min());
-    doubles.push_back(-std::numeric_limits<double>::denorm_min());
-  }
-
-  for (double base :
-       {1., 12., 123., 1234., 12345., 123456., 1234567., 12345678., 123456789.,
-        1234567890., 12345678901., 123456789012., 1234567890123.}) {
-    for (int exp = -123; exp <= 123; ++exp) {
-      for (int sign : {1, -1}) {
-        doubles.push_back(sign * std::ldexp(base, exp));
-      }
-    }
-  }
-
-  // Workaround libc bug.
-  // https://sourceware.org/bugzilla/show_bug.cgi?id=22142
-  const bool gcc_bug_22142 =
-      StrPrint("%f", std::numeric_limits<double>::max()) !=
-      "1797693134862315708145274237317043567980705675258449965989174768031"
-      "5726078002853876058955863276687817154045895351438246423432132688946"
-      "4182768467546703537516986049910576551282076245490090389328944075868"
-      "5084551339423045832369032229481658085593321233482747978262041447231"
-      "68738177180919299881250404026184124858368.000000";
-
-  if (!gcc_bug_22142) {
-    for (int exp = -300; exp <= 300; ++exp) {
-      const double all_ones_mantissa = 0x1fffffffffffff;
-      doubles.push_back(std::ldexp(all_ones_mantissa, exp));
-    }
-  }
-
-  if (gcc_bug_22142) {
-    for (auto &d : doubles) {
-      using L = std::numeric_limits<double>;
-      double d2 = std::abs(d);
-      if (d2 == L::max() || d2 == L::min() || d2 == L::denorm_min()) {
-        d = 0;
-      }
-    }
-  }
-
-  // Remove duplicates to speed up the logic below.
-  std::sort(doubles.begin(), doubles.end());
-  doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end());
-
-#ifndef __APPLE__
-  // Apple formats NaN differently (+nan) vs. (nan)
-  doubles.push_back(std::nan(""));
-#endif
-
-  TestWithMultipleFormatsHelper(doubles);
-}
-
-TEST_F(FormatConvertTest, DoubleRound) {
-  std::string s;
-  const auto format = [&](const char *fmt, double d) -> std::string & {
-    s.clear();
-    FormatArgImpl args[1] = {FormatArgImpl(d)};
-    AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
-#if !defined(_MSC_VER)
-    // MSVC has a different rounding policy than us so we can't test our
-    // implementation against the native one there.
-    EXPECT_EQ(StrPrint(fmt, d), s);
-#endif  // _MSC_VER
-
-    return s;
-  };
-  // All of these values have to be exactly represented.
-  // Otherwise we might not be testing what we think we are testing.
-
-  // These values can fit in a 64bit "fast" representation.
-  const double exact_value = 0.00000000000005684341886080801486968994140625;
-  assert(exact_value == std::pow(2, -44));
-  // Round up at a 5xx.
-  EXPECT_EQ(format("%.13f", exact_value), "0.0000000000001");
-  // Round up at a >5
-  EXPECT_EQ(format("%.14f", exact_value), "0.00000000000006");
-  // Round down at a <5
-  EXPECT_EQ(format("%.16f", exact_value), "0.0000000000000568");
-  // Nine handling
-  EXPECT_EQ(format("%.35f", exact_value),
-            "0.00000000000005684341886080801486969");
-  EXPECT_EQ(format("%.36f", exact_value),
-            "0.000000000000056843418860808014869690");
-  // Round down the last nine.
-  EXPECT_EQ(format("%.37f", exact_value),
-            "0.0000000000000568434188608080148696899");
-  EXPECT_EQ(format("%.10f", 0.000003814697265625), "0.0000038147");
-  // Round up the last nine
-  EXPECT_EQ(format("%.11f", 0.000003814697265625), "0.00000381470");
-  EXPECT_EQ(format("%.12f", 0.000003814697265625), "0.000003814697");
-
-  // Round to even (down)
-  EXPECT_EQ(format("%.43f", exact_value),
-            "0.0000000000000568434188608080148696899414062");
-  // Exact
-  EXPECT_EQ(format("%.44f", exact_value),
-            "0.00000000000005684341886080801486968994140625");
-  // Round to even (up), let make the last digits 75 instead of 25
-  EXPECT_EQ(format("%.43f", exact_value + std::pow(2, -43)),
-            "0.0000000000001705302565824240446090698242188");
-  // Exact, just to check.
-  EXPECT_EQ(format("%.44f", exact_value + std::pow(2, -43)),
-            "0.00000000000017053025658242404460906982421875");
-
-  // This value has to be small enough that it won't fit in the uint128
-  // representation for printing.
-  const double small_exact_value =
-      0.000000000000000000000000000000000000752316384526264005099991383822237233803945956334136013765601092018187046051025390625;  // NOLINT
-  assert(small_exact_value == std::pow(2, -120));
-  // Round up at a 5xx.
-  EXPECT_EQ(format("%.37f", small_exact_value),
-            "0.0000000000000000000000000000000000008");
-  // Round down at a <5
-  EXPECT_EQ(format("%.38f", small_exact_value),
-            "0.00000000000000000000000000000000000075");
-  // Round up at a >5
-  EXPECT_EQ(format("%.41f", small_exact_value),
-            "0.00000000000000000000000000000000000075232");
-  // Nine handling
-  EXPECT_EQ(format("%.55f", small_exact_value),
-            "0.0000000000000000000000000000000000007523163845262640051");
-  EXPECT_EQ(format("%.56f", small_exact_value),
-            "0.00000000000000000000000000000000000075231638452626400510");
-  EXPECT_EQ(format("%.57f", small_exact_value),
-            "0.000000000000000000000000000000000000752316384526264005100");
-  EXPECT_EQ(format("%.58f", small_exact_value),
-            "0.0000000000000000000000000000000000007523163845262640051000");
-  // Round down the last nine
-  EXPECT_EQ(format("%.59f", small_exact_value),
-            "0.00000000000000000000000000000000000075231638452626400509999");
-  // Round up the last nine
-  EXPECT_EQ(format("%.79f", small_exact_value),
-            "0.000000000000000000000000000000000000"
-            "7523163845262640050999913838222372338039460");
-
-  // Round to even (down)
-  EXPECT_EQ(format("%.119f", small_exact_value),
-            "0.000000000000000000000000000000000000"
-            "75231638452626400509999138382223723380"
-            "394595633413601376560109201818704605102539062");
-  // Exact
-  EXPECT_EQ(format("%.120f", small_exact_value),
-            "0.000000000000000000000000000000000000"
-            "75231638452626400509999138382223723380"
-            "3945956334136013765601092018187046051025390625");
-  // Round to even (up), let make the last digits 75 instead of 25
-  EXPECT_EQ(format("%.119f", small_exact_value + std::pow(2, -119)),
-            "0.000000000000000000000000000000000002"
-            "25694915357879201529997415146671170141"
-            "183786900240804129680327605456113815307617188");
-  // Exact, just to check.
-  EXPECT_EQ(format("%.120f", small_exact_value + std::pow(2, -119)),
-            "0.000000000000000000000000000000000002"
-            "25694915357879201529997415146671170141"
-            "1837869002408041296803276054561138153076171875");
-}
-
-TEST_F(FormatConvertTest, DoubleRoundA) {
-  const NativePrintfTraits &native_traits = VerifyNativeImplementation();
-  std::string s;
-  const auto format = [&](const char *fmt, double d) -> std::string & {
-    s.clear();
-    FormatArgImpl args[1] = {FormatArgImpl(d)};
-    AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
-    if (native_traits.hex_float_has_glibc_rounding) {
-      EXPECT_EQ(StrPrint(fmt, d), s);
-    }
-    return s;
-  };
-
-  // 0x1.00018000p+100
-  const double on_boundary_odd = 1267679614447900152596896153600.0;
-  EXPECT_EQ(format("%.0a", on_boundary_odd), "0x1p+100");
-  EXPECT_EQ(format("%.1a", on_boundary_odd), "0x1.0p+100");
-  EXPECT_EQ(format("%.2a", on_boundary_odd), "0x1.00p+100");
-  EXPECT_EQ(format("%.3a", on_boundary_odd), "0x1.000p+100");
-  EXPECT_EQ(format("%.4a", on_boundary_odd), "0x1.0002p+100");  // round
-  EXPECT_EQ(format("%.5a", on_boundary_odd), "0x1.00018p+100");
-  EXPECT_EQ(format("%.6a", on_boundary_odd), "0x1.000180p+100");
-
-  // 0x1.00028000p-2
-  const double on_boundary_even = 0.250009536743164062500;
-  EXPECT_EQ(format("%.0a", on_boundary_even), "0x1p-2");
-  EXPECT_EQ(format("%.1a", on_boundary_even), "0x1.0p-2");
-  EXPECT_EQ(format("%.2a", on_boundary_even), "0x1.00p-2");
-  EXPECT_EQ(format("%.3a", on_boundary_even), "0x1.000p-2");
-  EXPECT_EQ(format("%.4a", on_boundary_even), "0x1.0002p-2");  // no round
-  EXPECT_EQ(format("%.5a", on_boundary_even), "0x1.00028p-2");
-  EXPECT_EQ(format("%.6a", on_boundary_even), "0x1.000280p-2");
-
-  // 0x1.00018001p+1
-  const double slightly_over = 2.00004577683284878730773925781250;
-  EXPECT_EQ(format("%.0a", slightly_over), "0x1p+1");
-  EXPECT_EQ(format("%.1a", slightly_over), "0x1.0p+1");
-  EXPECT_EQ(format("%.2a", slightly_over), "0x1.00p+1");
-  EXPECT_EQ(format("%.3a", slightly_over), "0x1.000p+1");
-  EXPECT_EQ(format("%.4a", slightly_over), "0x1.0002p+1");
-  EXPECT_EQ(format("%.5a", slightly_over), "0x1.00018p+1");
-  EXPECT_EQ(format("%.6a", slightly_over), "0x1.000180p+1");
-
-  // 0x1.00017fffp+0
-  const double slightly_under = 1.000022887950763106346130371093750;
-  EXPECT_EQ(format("%.0a", slightly_under), "0x1p+0");
-  EXPECT_EQ(format("%.1a", slightly_under), "0x1.0p+0");
-  EXPECT_EQ(format("%.2a", slightly_under), "0x1.00p+0");
-  EXPECT_EQ(format("%.3a", slightly_under), "0x1.000p+0");
-  EXPECT_EQ(format("%.4a", slightly_under), "0x1.0001p+0");
-  EXPECT_EQ(format("%.5a", slightly_under), "0x1.00018p+0");
-  EXPECT_EQ(format("%.6a", slightly_under), "0x1.000180p+0");
-  EXPECT_EQ(format("%.7a", slightly_under), "0x1.0001800p+0");
-
-  // 0x1.1b3829ac28058p+3
-  const double hex_value = 8.85060580848964661981881363317370414733886718750;
-  EXPECT_EQ(format("%.0a", hex_value), "0x1p+3");
-  EXPECT_EQ(format("%.1a", hex_value), "0x1.2p+3");
-  EXPECT_EQ(format("%.2a", hex_value), "0x1.1bp+3");
-  EXPECT_EQ(format("%.3a", hex_value), "0x1.1b4p+3");
-  EXPECT_EQ(format("%.4a", hex_value), "0x1.1b38p+3");
-  EXPECT_EQ(format("%.5a", hex_value), "0x1.1b383p+3");
-  EXPECT_EQ(format("%.6a", hex_value), "0x1.1b382ap+3");
-  EXPECT_EQ(format("%.7a", hex_value), "0x1.1b3829bp+3");
-  EXPECT_EQ(format("%.8a", hex_value), "0x1.1b3829acp+3");
-  EXPECT_EQ(format("%.9a", hex_value), "0x1.1b3829ac3p+3");
-  EXPECT_EQ(format("%.10a", hex_value), "0x1.1b3829ac28p+3");
-  EXPECT_EQ(format("%.11a", hex_value), "0x1.1b3829ac280p+3");
-  EXPECT_EQ(format("%.12a", hex_value), "0x1.1b3829ac2806p+3");
-  EXPECT_EQ(format("%.13a", hex_value), "0x1.1b3829ac28058p+3");
-  EXPECT_EQ(format("%.14a", hex_value), "0x1.1b3829ac280580p+3");
-  EXPECT_EQ(format("%.15a", hex_value), "0x1.1b3829ac2805800p+3");
-  EXPECT_EQ(format("%.16a", hex_value), "0x1.1b3829ac28058000p+3");
-  EXPECT_EQ(format("%.17a", hex_value), "0x1.1b3829ac280580000p+3");
-  EXPECT_EQ(format("%.18a", hex_value), "0x1.1b3829ac2805800000p+3");
-  EXPECT_EQ(format("%.19a", hex_value), "0x1.1b3829ac28058000000p+3");
-  EXPECT_EQ(format("%.20a", hex_value), "0x1.1b3829ac280580000000p+3");
-  EXPECT_EQ(format("%.21a", hex_value), "0x1.1b3829ac2805800000000p+3");
-
-  // 0x1.0818283848586p+3
-  const double hex_value2 = 8.2529488658208371987257123691961169242858886718750;
-  EXPECT_EQ(format("%.0a", hex_value2), "0x1p+3");
-  EXPECT_EQ(format("%.1a", hex_value2), "0x1.1p+3");
-  EXPECT_EQ(format("%.2a", hex_value2), "0x1.08p+3");
-  EXPECT_EQ(format("%.3a", hex_value2), "0x1.082p+3");
-  EXPECT_EQ(format("%.4a", hex_value2), "0x1.0818p+3");
-  EXPECT_EQ(format("%.5a", hex_value2), "0x1.08183p+3");
-  EXPECT_EQ(format("%.6a", hex_value2), "0x1.081828p+3");
-  EXPECT_EQ(format("%.7a", hex_value2), "0x1.0818284p+3");
-  EXPECT_EQ(format("%.8a", hex_value2), "0x1.08182838p+3");
-  EXPECT_EQ(format("%.9a", hex_value2), "0x1.081828385p+3");
-  EXPECT_EQ(format("%.10a", hex_value2), "0x1.0818283848p+3");
-  EXPECT_EQ(format("%.11a", hex_value2), "0x1.08182838486p+3");
-  EXPECT_EQ(format("%.12a", hex_value2), "0x1.081828384858p+3");
-  EXPECT_EQ(format("%.13a", hex_value2), "0x1.0818283848586p+3");
-  EXPECT_EQ(format("%.14a", hex_value2), "0x1.08182838485860p+3");
-  EXPECT_EQ(format("%.15a", hex_value2), "0x1.081828384858600p+3");
-  EXPECT_EQ(format("%.16a", hex_value2), "0x1.0818283848586000p+3");
-  EXPECT_EQ(format("%.17a", hex_value2), "0x1.08182838485860000p+3");
-  EXPECT_EQ(format("%.18a", hex_value2), "0x1.081828384858600000p+3");
-  EXPECT_EQ(format("%.19a", hex_value2), "0x1.0818283848586000000p+3");
-  EXPECT_EQ(format("%.20a", hex_value2), "0x1.08182838485860000000p+3");
-  EXPECT_EQ(format("%.21a", hex_value2), "0x1.081828384858600000000p+3");
-}
-
-TEST_F(FormatConvertTest, LongDoubleRoundA) {
-  if (std::numeric_limits<long double>::digits % 4 != 0) {
-    // This test doesn't really make sense to run on platforms where a long
-    // double has a different mantissa size (mod 4) than Prod, since then the
-    // leading digit will be formatted differently.
-    return;
-  }
-  const NativePrintfTraits &native_traits = VerifyNativeImplementation();
-  std::string s;
-  const auto format = [&](const char *fmt, long double d) -> std::string & {
-    s.clear();
-    FormatArgImpl args[1] = {FormatArgImpl(d)};
-    AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
-    if (native_traits.hex_float_has_glibc_rounding &&
-        native_traits.hex_float_optimizes_leading_digit_bit_count) {
-      EXPECT_EQ(StrPrint(fmt, d), s);
-    }
-    return s;
-  };
-
-  // 0x8.8p+4
-  const long double on_boundary_even = 136.0;
-  EXPECT_EQ(format("%.0La", on_boundary_even), "0x8p+4");
-  EXPECT_EQ(format("%.1La", on_boundary_even), "0x8.8p+4");
-  EXPECT_EQ(format("%.2La", on_boundary_even), "0x8.80p+4");
-  EXPECT_EQ(format("%.3La", on_boundary_even), "0x8.800p+4");
-  EXPECT_EQ(format("%.4La", on_boundary_even), "0x8.8000p+4");
-  EXPECT_EQ(format("%.5La", on_boundary_even), "0x8.80000p+4");
-  EXPECT_EQ(format("%.6La", on_boundary_even), "0x8.800000p+4");
-
-  // 0x9.8p+4
-  const long double on_boundary_odd = 152.0;
-  EXPECT_EQ(format("%.0La", on_boundary_odd), "0xap+4");
-  EXPECT_EQ(format("%.1La", on_boundary_odd), "0x9.8p+4");
-  EXPECT_EQ(format("%.2La", on_boundary_odd), "0x9.80p+4");
-  EXPECT_EQ(format("%.3La", on_boundary_odd), "0x9.800p+4");
-  EXPECT_EQ(format("%.4La", on_boundary_odd), "0x9.8000p+4");
-  EXPECT_EQ(format("%.5La", on_boundary_odd), "0x9.80000p+4");
-  EXPECT_EQ(format("%.6La", on_boundary_odd), "0x9.800000p+4");
-
-  // 0x8.80001p+24
-  const long double slightly_over = 142606352.0;
-  EXPECT_EQ(format("%.0La", slightly_over), "0x9p+24");
-  EXPECT_EQ(format("%.1La", slightly_over), "0x8.8p+24");
-  EXPECT_EQ(format("%.2La", slightly_over), "0x8.80p+24");
-  EXPECT_EQ(format("%.3La", slightly_over), "0x8.800p+24");
-  EXPECT_EQ(format("%.4La", slightly_over), "0x8.8000p+24");
-  EXPECT_EQ(format("%.5La", slightly_over), "0x8.80001p+24");
-  EXPECT_EQ(format("%.6La", slightly_over), "0x8.800010p+24");
-
-  // 0x8.7ffffp+24
-  const long double slightly_under = 142606320.0;
-  EXPECT_EQ(format("%.0La", slightly_under), "0x8p+24");
-  EXPECT_EQ(format("%.1La", slightly_under), "0x8.8p+24");
-  EXPECT_EQ(format("%.2La", slightly_under), "0x8.80p+24");
-  EXPECT_EQ(format("%.3La", slightly_under), "0x8.800p+24");
-  EXPECT_EQ(format("%.4La", slightly_under), "0x8.8000p+24");
-  EXPECT_EQ(format("%.5La", slightly_under), "0x8.7ffffp+24");
-  EXPECT_EQ(format("%.6La", slightly_under), "0x8.7ffff0p+24");
-  EXPECT_EQ(format("%.7La", slightly_under), "0x8.7ffff00p+24");
-
-  // 0xc.0828384858688000p+128
-  const long double eights = 4094231060438608800781871108094404067328.0;
-  EXPECT_EQ(format("%.0La", eights), "0xcp+128");
-  EXPECT_EQ(format("%.1La", eights), "0xc.1p+128");
-  EXPECT_EQ(format("%.2La", eights), "0xc.08p+128");
-  EXPECT_EQ(format("%.3La", eights), "0xc.083p+128");
-  EXPECT_EQ(format("%.4La", eights), "0xc.0828p+128");
-  EXPECT_EQ(format("%.5La", eights), "0xc.08284p+128");
-  EXPECT_EQ(format("%.6La", eights), "0xc.082838p+128");
-  EXPECT_EQ(format("%.7La", eights), "0xc.0828385p+128");
-  EXPECT_EQ(format("%.8La", eights), "0xc.08283848p+128");
-  EXPECT_EQ(format("%.9La", eights), "0xc.082838486p+128");
-  EXPECT_EQ(format("%.10La", eights), "0xc.0828384858p+128");
-  EXPECT_EQ(format("%.11La", eights), "0xc.08283848587p+128");
-  EXPECT_EQ(format("%.12La", eights), "0xc.082838485868p+128");
-  EXPECT_EQ(format("%.13La", eights), "0xc.0828384858688p+128");
-  EXPECT_EQ(format("%.14La", eights), "0xc.08283848586880p+128");
-  EXPECT_EQ(format("%.15La", eights), "0xc.082838485868800p+128");
-  EXPECT_EQ(format("%.16La", eights), "0xc.0828384858688000p+128");
-}
-
-// We don't actually store the results. This is just to exercise the rest of the
-// machinery.
-struct NullSink {
-  friend void AbslFormatFlush(NullSink *sink, string_view str) {}
-};
-
-template <typename... T>
-bool FormatWithNullSink(absl::string_view fmt, const T &... a) {
-  NullSink sink;
-  FormatArgImpl args[] = {FormatArgImpl(a)...};
-  return FormatUntyped(&sink, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
-}
-
-TEST_F(FormatConvertTest, ExtremeWidthPrecision) {
-  for (const char *fmt : {"f"}) {
-    for (double d : {1e-100, 1.0, 1e100}) {
-      constexpr int max = std::numeric_limits<int>::max();
-      EXPECT_TRUE(FormatWithNullSink(std::string("%.*") + fmt, max, d));
-      EXPECT_TRUE(FormatWithNullSink(std::string("%1.*") + fmt, max, d));
-      EXPECT_TRUE(FormatWithNullSink(std::string("%*") + fmt, max, d));
-      EXPECT_TRUE(FormatWithNullSink(std::string("%*.*") + fmt, max, max, d));
-    }
-  }
-}
-
-TEST_F(FormatConvertTest, LongDouble) {
-#ifdef _MSC_VER
-  // MSVC has a different rounding policy than us so we can't test our
-  // implementation against the native one there.
-  return;
-#endif  // _MSC_VER
-  const NativePrintfTraits &native_traits = VerifyNativeImplementation();
-  const char *const kFormats[] = {"%",    "%.3", "%8.5", "%9",  "%.5000",
-                                  "%.60", "%+",  "% ",   "%-10"};
-
-  std::vector<long double> doubles = {
-      0.0,
-      -0.0,
-      std::numeric_limits<long double>::max(),
-      -std::numeric_limits<long double>::max(),
-      std::numeric_limits<long double>::min(),
-      -std::numeric_limits<long double>::min(),
-      std::numeric_limits<long double>::infinity(),
-      -std::numeric_limits<long double>::infinity()};
-
-  for (long double base : {1.L, 12.L, 123.L, 1234.L, 12345.L, 123456.L,
-                           1234567.L, 12345678.L, 123456789.L, 1234567890.L,
-                           12345678901.L, 123456789012.L, 1234567890123.L,
-                           // This value is not representable in double, but it
-                           // is in long double that uses the extended format.
-                           // This is to verify that we are not truncating the
-                           // value mistakenly through a double.
-                           10000000000000000.25L}) {
-    for (int exp : {-1000, -500, 0, 500, 1000}) {
-      for (int sign : {1, -1}) {
-        doubles.push_back(sign * std::ldexp(base, exp));
-        doubles.push_back(sign / std::ldexp(base, exp));
-      }
-    }
-  }
-
-  // Regression tests
-  //
-  // Using a string literal because not all platforms support hex literals or it
-  // might be out of range.
-  doubles.push_back(std::strtold("-0xf.ffffffb5feafffbp-16324L", nullptr));
-
-  for (const char *fmt : kFormats) {
-    for (char f : {'f', 'F',  //
-                   'g', 'G',  //
-                   'a', 'A',  //
-                   'e', 'E'}) {
-      std::string fmt_str = std::string(fmt) + 'L' + f;
-
-      if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F' &&
-          f != 'a' && f != 'A') {
-        // This particular test takes way too long with snprintf.
-        // Disable for the case we are not implementing natively.
-        continue;
-      }
-
-      if (f == 'a' || f == 'A') {
-        if (!native_traits.hex_float_has_glibc_rounding ||
-            !native_traits.hex_float_optimizes_leading_digit_bit_count) {
-          continue;
-        }
-      }
-
-      for (auto d : doubles) {
-        FormatArgImpl arg(d);
-        UntypedFormatSpecImpl format(fmt_str);
-        // We use ASSERT_EQ here because failures are usually correlated and a
-        // bug would print way too many failed expectations causing the test to
-        // time out.
-        ASSERT_EQ(StrPrint(fmt_str.c_str(), d), FormatPack(format, {&arg, 1}))
-            << fmt_str << " " << StrPrint("%.18Lg", d) << " "
-            << StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d);
-      }
-    }
-  }
-}
-
-TEST_F(FormatConvertTest, IntAsDouble) {
-  const NativePrintfTraits &native_traits = VerifyNativeImplementation();
-  const int kMin = std::numeric_limits<int>::min();
-  const int kMax = std::numeric_limits<int>::max();
-  const int ia[] = {
-    1, 2, 3, 123,
-    -1, -2, -3, -123,
-    0, kMax - 1, kMax, kMin + 1, kMin };
-  for (const int fx : ia) {
-    SCOPED_TRACE(fx);
-    const FormatArgImpl args[] = {FormatArgImpl(fx)};
-    struct Expectation {
-      int line;
-      std::string out;
-      const char *fmt;
-    };
-    const double dx = static_cast<double>(fx);
-    std::vector<Expectation> expect = {
-        {__LINE__, StrPrint("%f", dx), "%f"},
-        {__LINE__, StrPrint("%12f", dx), "%12f"},
-        {__LINE__, StrPrint("%.12f", dx), "%.12f"},
-        {__LINE__, StrPrint("%.12a", dx), "%.12a"},
-    };
-    if (native_traits.hex_float_uses_minimal_precision_when_not_specified) {
-      Expectation ex = {__LINE__, StrPrint("%12a", dx), "%12a"};
-      expect.push_back(ex);
-    }
-    for (const Expectation &e : expect) {
-      SCOPED_TRACE(e.line);
-      SCOPED_TRACE(e.fmt);
-      UntypedFormatSpecImpl format(e.fmt);
-      EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
-    }
-  }
-}
-
-template <typename T>
-bool FormatFails(const char* test_format, T value) {
-  std::string format_string = std::string("<<") + test_format + ">>";
-  UntypedFormatSpecImpl format(format_string);
-
-  int one = 1;
-  const FormatArgImpl args[] = {FormatArgImpl(value), FormatArgImpl(one)};
-  EXPECT_EQ(FormatPack(format, absl::MakeSpan(args)), "")
-      << "format=" << test_format << " value=" << value;
-  return FormatPack(format, absl::MakeSpan(args)).empty();
-}
-
-TEST_F(FormatConvertTest, ExpectedFailures) {
-  // Int input
-  EXPECT_TRUE(FormatFails("%p", 1));
-  EXPECT_TRUE(FormatFails("%s", 1));
-  EXPECT_TRUE(FormatFails("%n", 1));
-
-  // Double input
-  EXPECT_TRUE(FormatFails("%p", 1.));
-  EXPECT_TRUE(FormatFails("%s", 1.));
-  EXPECT_TRUE(FormatFails("%n", 1.));
-  EXPECT_TRUE(FormatFails("%c", 1.));
-  EXPECT_TRUE(FormatFails("%d", 1.));
-  EXPECT_TRUE(FormatFails("%x", 1.));
-  EXPECT_TRUE(FormatFails("%*d", 1.));
-
-  // String input
-  EXPECT_TRUE(FormatFails("%n", ""));
-  EXPECT_TRUE(FormatFails("%c", ""));
-  EXPECT_TRUE(FormatFails("%d", ""));
-  EXPECT_TRUE(FormatFails("%x", ""));
-  EXPECT_TRUE(FormatFails("%f", ""));
-  EXPECT_TRUE(FormatFails("%*d", ""));
-}
-
-// Sanity check to make sure that we are testing what we think we're testing on
-// e.g. the x86_64+glibc platform.
-TEST_F(FormatConvertTest, GlibcHasCorrectTraits) {
-#if !defined(__GLIBC__) || !defined(__x86_64__)
-  return;
-#endif
-  const NativePrintfTraits &native_traits = VerifyNativeImplementation();
-  // If one of the following tests break then it is either because the above PP
-  // macro guards failed to exclude a new platform (likely) or because something
-  // has changed in the implemention of glibc sprintf float formatting behavior.
-  // If the latter, then the code that computes these flags needs to be
-  // revisited and/or possibly the StrFormat implementation.
-  EXPECT_TRUE(native_traits.hex_float_has_glibc_rounding);
-  EXPECT_TRUE(native_traits.hex_float_prefers_denormal_repr);
-  EXPECT_TRUE(
-      native_traits.hex_float_uses_minimal_precision_when_not_specified);
-  EXPECT_TRUE(native_traits.hex_float_optimizes_leading_digit_bit_count);
-}
-
-}  // namespace
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/extension.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/extension.cc
deleted file mode 100644
index bb0d96cf32..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/extension.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/str_format/extension.h"
-
-#include <errno.h>
-#include <algorithm>
-#include <string>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-std::string Flags::ToString() const {
-  std::string s;
-  s.append(left     ? "-" : "");
-  s.append(show_pos ? "+" : "");
-  s.append(sign_col ? " " : "");
-  s.append(alt      ? "#" : "");
-  s.append(zero     ? "0" : "");
-  return s;
-}
-
-#define ABSL_INTERNAL_X_VAL(id) \
-  constexpr absl::FormatConversionChar FormatConversionCharInternal::id;
-ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
-#undef ABSL_INTERNAL_X_VAL
-// NOLINTNEXTLINE(readability-redundant-declaration)
-constexpr absl::FormatConversionChar FormatConversionCharInternal::kNone;
-
-#define ABSL_INTERNAL_CHAR_SET_CASE(c) \
-  constexpr FormatConversionCharSet FormatConversionCharSetInternal::c;
-ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
-#undef ABSL_INTERNAL_CHAR_SET_CASE
-
-// NOLINTNEXTLINE(readability-redundant-declaration)
-constexpr FormatConversionCharSet FormatConversionCharSetInternal::kStar;
-// NOLINTNEXTLINE(readability-redundant-declaration)
-constexpr FormatConversionCharSet FormatConversionCharSetInternal::kIntegral;
-// NOLINTNEXTLINE(readability-redundant-declaration)
-constexpr FormatConversionCharSet FormatConversionCharSetInternal::kFloating;
-// NOLINTNEXTLINE(readability-redundant-declaration)
-constexpr FormatConversionCharSet FormatConversionCharSetInternal::kNumeric;
-// NOLINTNEXTLINE(readability-redundant-declaration)
-constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer;
-
-bool FormatSinkImpl::PutPaddedString(string_view value, int width,
-                                     int precision, bool left) {
-  size_t space_remaining = 0;
-  if (width >= 0) space_remaining = width;
-  size_t n = value.size();
-  if (precision >= 0) n = std::min(n, static_cast<size_t>(precision));
-  string_view shown(value.data(), n);
-  space_remaining = Excess(shown.size(), space_remaining);
-  if (!left) Append(space_remaining, ' ');
-  Append(shown);
-  if (left) Append(space_remaining, ' ');
-  return true;
-}
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/extension.h b/third_party/abseil_cpp/absl/strings/internal/str_format/extension.h
deleted file mode 100644
index a9b9e137de..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/extension.h
+++ /dev/null
@@ -1,427 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
-#define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
-
-#include <limits.h>
-
-#include <cstddef>
-#include <cstring>
-#include <ostream>
-
-#include "absl/base/config.h"
-#include "absl/base/port.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/internal/str_format/output.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-enum class FormatConversionChar : uint8_t;
-enum class FormatConversionCharSet : uint64_t;
-
-namespace str_format_internal {
-
-class FormatRawSinkImpl {
- public:
-  // Implicitly convert from any type that provides the hook function as
-  // described above.
-  template <typename T, decltype(str_format_internal::InvokeFlush(
-                            std::declval<T*>(), string_view()))* = nullptr>
-  FormatRawSinkImpl(T* raw)  // NOLINT
-      : sink_(raw), write_(&FormatRawSinkImpl::Flush<T>) {}
-
-  void Write(string_view s) { write_(sink_, s); }
-
-  template <typename T>
-  static FormatRawSinkImpl Extract(T s) {
-    return s.sink_;
-  }
-
- private:
-  template <typename T>
-  static void Flush(void* r, string_view s) {
-    str_format_internal::InvokeFlush(static_cast<T*>(r), s);
-  }
-
-  void* sink_;
-  void (*write_)(void*, string_view);
-};
-
-// An abstraction to which conversions write their string data.
-class FormatSinkImpl {
- public:
-  explicit FormatSinkImpl(FormatRawSinkImpl raw) : raw_(raw) {}
-
-  ~FormatSinkImpl() { Flush(); }
-
-  void Flush() {
-    raw_.Write(string_view(buf_, pos_ - buf_));
-    pos_ = buf_;
-  }
-
-  void Append(size_t n, char c) {
-    if (n == 0) return;
-    size_ += n;
-    auto raw_append = [&](size_t count) {
-      memset(pos_, c, count);
-      pos_ += count;
-    };
-    while (n > Avail()) {
-      n -= Avail();
-      if (Avail() > 0) {
-        raw_append(Avail());
-      }
-      Flush();
-    }
-    raw_append(n);
-  }
-
-  void Append(string_view v) {
-    size_t n = v.size();
-    if (n == 0) return;
-    size_ += n;
-    if (n >= Avail()) {
-      Flush();
-      raw_.Write(v);
-      return;
-    }
-    memcpy(pos_, v.data(), n);
-    pos_ += n;
-  }
-
-  size_t size() const { return size_; }
-
-  // Put 'v' to 'sink' with specified width, precision, and left flag.
-  bool PutPaddedString(string_view v, int width, int precision, bool left);
-
-  template <typename T>
-  T Wrap() {
-    return T(this);
-  }
-
-  template <typename T>
-  static FormatSinkImpl* Extract(T* s) {
-    return s->sink_;
-  }
-
- private:
-  size_t Avail() const { return buf_ + sizeof(buf_) - pos_; }
-
-  FormatRawSinkImpl raw_;
-  size_t size_ = 0;
-  char* pos_ = buf_;
-  char buf_[1024];
-};
-
-struct Flags {
-  bool basic : 1;     // fastest conversion: no flags, width, or precision
-  bool left : 1;      // "-"
-  bool show_pos : 1;  // "+"
-  bool sign_col : 1;  // " "
-  bool alt : 1;       // "#"
-  bool zero : 1;      // "0"
-  std::string ToString() const;
-  friend std::ostream& operator<<(std::ostream& os, const Flags& v) {
-    return os << v.ToString();
-  }
-};
-
-// clang-format off
-#define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \
-  /* text */ \
-  X_VAL(c) X_SEP X_VAL(s) X_SEP \
-  /* ints */ \
-  X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \
-  X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \
-  /* floats */ \
-  X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \
-  X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \
-  /* misc */ \
-  X_VAL(n) X_SEP X_VAL(p)
-// clang-format on
-
-// This type should not be referenced, it exists only to provide labels
-// internally that match the values declared in FormatConversionChar in
-// str_format.h. This is meant to allow internal libraries to use the same
-// declared interface type as the public interface
-// (absl::StrFormatConversionChar) while keeping the definition in a public
-// header.
-// Internal libraries should use the form
-// `FormatConversionCharInternal::c`, `FormatConversionCharInternal::kNone` for
-// comparisons.  Use in switch statements is not recommended due to a bug in how
-// gcc 4.9 -Wswitch handles declared but undefined enums.
-struct FormatConversionCharInternal {
-  FormatConversionCharInternal() = delete;
-
- private:
-  // clang-format off
-  enum class Enum : uint8_t {
-    c, s,                    // text
-    d, i, o, u, x, X,        // int
-    f, F, e, E, g, G, a, A,  // float
-    n, p,                    // misc
-    kNone
-  };
-  // clang-format on
- public:
-#define ABSL_INTERNAL_X_VAL(id)              \
-  static constexpr FormatConversionChar id = \
-      static_cast<FormatConversionChar>(Enum::id);
-  ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
-#undef ABSL_INTERNAL_X_VAL
-  static constexpr FormatConversionChar kNone =
-      static_cast<FormatConversionChar>(Enum::kNone);
-};
-// clang-format on
-
-inline FormatConversionChar FormatConversionCharFromChar(char c) {
-  switch (c) {
-#define ABSL_INTERNAL_X_VAL(id) \
-  case #id[0]:                  \
-    return FormatConversionCharInternal::id;
-    ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
-#undef ABSL_INTERNAL_X_VAL
-  }
-  return FormatConversionCharInternal::kNone;
-}
-
-inline bool FormatConversionCharIsUpper(FormatConversionChar c) {
-  if (c == FormatConversionCharInternal::X ||
-      c == FormatConversionCharInternal::F ||
-      c == FormatConversionCharInternal::E ||
-      c == FormatConversionCharInternal::G ||
-      c == FormatConversionCharInternal::A) {
-    return true;
-  } else {
-    return false;
-  }
-}
-
-inline bool FormatConversionCharIsFloat(FormatConversionChar c) {
-  if (c == FormatConversionCharInternal::a ||
-      c == FormatConversionCharInternal::e ||
-      c == FormatConversionCharInternal::f ||
-      c == FormatConversionCharInternal::g ||
-      c == FormatConversionCharInternal::A ||
-      c == FormatConversionCharInternal::E ||
-      c == FormatConversionCharInternal::F ||
-      c == FormatConversionCharInternal::G) {
-    return true;
-  } else {
-    return false;
-  }
-}
-
-inline char FormatConversionCharToChar(FormatConversionChar c) {
-  if (c == FormatConversionCharInternal::kNone) {
-    return '\0';
-
-#define ABSL_INTERNAL_X_VAL(e)                       \
-  } else if (c == FormatConversionCharInternal::e) { \
-    return #e[0];
-#define ABSL_INTERNAL_X_SEP
-  ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL,
-                                         ABSL_INTERNAL_X_SEP)
-  } else {
-    return '\0';
-  }
-
-#undef ABSL_INTERNAL_X_VAL
-#undef ABSL_INTERNAL_X_SEP
-}
-
-// The associated char.
-inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) {
-  char c = FormatConversionCharToChar(v);
-  if (!c) c = '?';
-  return os << c;
-}
-
-struct FormatConversionSpecImplFriend;
-
-class FormatConversionSpecImpl {
- public:
-  // Width and precison are not specified, no flags are set.
-  bool is_basic() const { return flags_.basic; }
-  bool has_left_flag() const { return flags_.left; }
-  bool has_show_pos_flag() const { return flags_.show_pos; }
-  bool has_sign_col_flag() const { return flags_.sign_col; }
-  bool has_alt_flag() const { return flags_.alt; }
-  bool has_zero_flag() const { return flags_.zero; }
-
-  FormatConversionChar conversion_char() const {
-    // Keep this field first in the struct . It generates better code when
-    // accessing it when ConversionSpec is passed by value in registers.
-    static_assert(offsetof(FormatConversionSpecImpl, conv_) == 0, "");
-    return conv_;
-  }
-
-  // Returns the specified width. If width is unspecfied, it returns a negative
-  // value.
-  int width() const { return width_; }
-  // Returns the specified precision. If precision is unspecfied, it returns a
-  // negative value.
-  int precision() const { return precision_; }
-
-  template <typename T>
-  T Wrap() {
-    return T(*this);
-  }
-
- private:
-  friend struct str_format_internal::FormatConversionSpecImplFriend;
-  FormatConversionChar conv_ = FormatConversionCharInternal::kNone;
-  Flags flags_;
-  int width_;
-  int precision_;
-};
-
-struct FormatConversionSpecImplFriend final {
-  static void SetFlags(Flags f, FormatConversionSpecImpl* conv) {
-    conv->flags_ = f;
-  }
-  static void SetConversionChar(FormatConversionChar c,
-                                FormatConversionSpecImpl* conv) {
-    conv->conv_ = c;
-  }
-  static void SetWidth(int w, FormatConversionSpecImpl* conv) {
-    conv->width_ = w;
-  }
-  static void SetPrecision(int p, FormatConversionSpecImpl* conv) {
-    conv->precision_ = p;
-  }
-  static std::string FlagsToString(const FormatConversionSpecImpl& spec) {
-    return spec.flags_.ToString();
-  }
-};
-
-// Type safe OR operator.
-// We need this for two reasons:
-//  1. operator| on enums makes them decay to integers and the result is an
-//     integer. We need the result to stay as an enum.
-//  2. We use "enum class" which would not work even if we accepted the decay.
-constexpr FormatConversionCharSet FormatConversionCharSetUnion(
-    FormatConversionCharSet a) {
-  return a;
-}
-
-template <typename... CharSet>
-constexpr FormatConversionCharSet FormatConversionCharSetUnion(
-    FormatConversionCharSet a, CharSet... rest) {
-  return static_cast<FormatConversionCharSet>(
-      static_cast<uint64_t>(a) |
-      static_cast<uint64_t>(FormatConversionCharSetUnion(rest...)));
-}
-
-constexpr uint64_t FormatConversionCharToConvInt(FormatConversionChar c) {
-  return uint64_t{1} << (1 + static_cast<uint8_t>(c));
-}
-
-constexpr uint64_t FormatConversionCharToConvInt(char conv) {
-  return
-#define ABSL_INTERNAL_CHAR_SET_CASE(c)                                 \
-  conv == #c[0]                                                        \
-      ? FormatConversionCharToConvInt(FormatConversionCharInternal::c) \
-      :
-      ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
-#undef ABSL_INTERNAL_CHAR_SET_CASE
-                  conv == '*'
-          ? 1
-          : 0;
-}
-
-constexpr FormatConversionCharSet FormatConversionCharToConvValue(char conv) {
-  return static_cast<FormatConversionCharSet>(
-      FormatConversionCharToConvInt(conv));
-}
-
-struct FormatConversionCharSetInternal {
-#define ABSL_INTERNAL_CHAR_SET_CASE(c)         \
-  static constexpr FormatConversionCharSet c = \
-      FormatConversionCharToConvValue(#c[0]);
-  ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
-#undef ABSL_INTERNAL_CHAR_SET_CASE
-
-  // Used for width/precision '*' specification.
-  static constexpr FormatConversionCharSet kStar =
-      FormatConversionCharToConvValue('*');
-
-  static constexpr FormatConversionCharSet kIntegral =
-      FormatConversionCharSetUnion(d, i, u, o, x, X);
-  static constexpr FormatConversionCharSet kFloating =
-      FormatConversionCharSetUnion(a, e, f, g, A, E, F, G);
-  static constexpr FormatConversionCharSet kNumeric =
-      FormatConversionCharSetUnion(kIntegral, kFloating);
-  static constexpr FormatConversionCharSet kPointer = p;
-};
-
-// Type safe OR operator.
-// We need this for two reasons:
-//  1. operator| on enums makes them decay to integers and the result is an
-//     integer. We need the result to stay as an enum.
-//  2. We use "enum class" which would not work even if we accepted the decay.
-constexpr FormatConversionCharSet operator|(FormatConversionCharSet a,
-                                            FormatConversionCharSet b) {
-  return FormatConversionCharSetUnion(a, b);
-}
-
-// Overloaded conversion functions to support absl::ParsedFormat.
-// Get a conversion with a single character in it.
-constexpr FormatConversionCharSet ToFormatConversionCharSet(char c) {
-  return static_cast<FormatConversionCharSet>(
-      FormatConversionCharToConvValue(c));
-}
-
-// Get a conversion with a single character in it.
-constexpr FormatConversionCharSet ToFormatConversionCharSet(
-    FormatConversionCharSet c) {
-  return c;
-}
-
-template <typename T>
-void ToFormatConversionCharSet(T) = delete;
-
-// Checks whether `c` exists in `set`.
-constexpr bool Contains(FormatConversionCharSet set, char c) {
-  return (static_cast<uint64_t>(set) &
-          static_cast<uint64_t>(FormatConversionCharToConvValue(c))) != 0;
-}
-
-// Checks whether all the characters in `c` are contained in `set`
-constexpr bool Contains(FormatConversionCharSet set,
-                        FormatConversionCharSet c) {
-  return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) ==
-         static_cast<uint64_t>(c);
-}
-
-// Checks whether all the characters in `c` are contained in `set`
-constexpr bool Contains(FormatConversionCharSet set, FormatConversionChar c) {
-  return (static_cast<uint64_t>(set) & FormatConversionCharToConvInt(c)) != 0;
-}
-
-// Return capacity - used, clipped to a minimum of 0.
-inline size_t Excess(size_t used, size_t capacity) {
-  return used < capacity ? capacity - used : 0;
-}
-
-}  // namespace str_format_internal
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/extension_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/extension_test.cc
deleted file mode 100644
index 1c93fdb1c7..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/extension_test.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "absl/strings/internal/str_format/extension.h"
-
-#include <random>
-#include <string>
-
-#include "gtest/gtest.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/string_view.h"
-
-namespace my_namespace {
-class UserDefinedType {
- public:
-  UserDefinedType() = default;
-
-  void Append(absl::string_view str) { value_.append(str.data(), str.size()); }
-  const std::string& Value() const { return value_; }
-
-  friend void AbslFormatFlush(UserDefinedType* x, absl::string_view str) {
-    x->Append(str);
-  }
-
- private:
-  std::string value_;
-};
-}  // namespace my_namespace
-
-namespace {
-
-std::string MakeRandomString(size_t len) {
-  std::random_device rd;
-  std::mt19937 gen(rd());
-  std::uniform_int_distribution<> dis('a', 'z');
-  std::string s(len, '0');
-  for (char& c : s) {
-    c = dis(gen);
-  }
-  return s;
-}
-
-TEST(FormatExtensionTest, SinkAppendSubstring) {
-  for (size_t chunk_size : {1, 10, 100, 1000, 10000}) {
-    std::string expected, actual;
-    absl::str_format_internal::FormatSinkImpl sink(&actual);
-    for (size_t chunks = 0; chunks < 10; ++chunks) {
-      std::string rand = MakeRandomString(chunk_size);
-      expected += rand;
-      sink.Append(rand);
-    }
-    sink.Flush();
-    EXPECT_EQ(actual, expected);
-  }
-}
-
-TEST(FormatExtensionTest, SinkAppendChars) {
-  for (size_t chunk_size : {1, 10, 100, 1000, 10000}) {
-    std::string expected, actual;
-    absl::str_format_internal::FormatSinkImpl sink(&actual);
-    for (size_t chunks = 0; chunks < 10; ++chunks) {
-      std::string rand = MakeRandomString(1);
-      expected.append(chunk_size, rand[0]);
-      sink.Append(chunk_size, rand[0]);
-    }
-    sink.Flush();
-    EXPECT_EQ(actual, expected);
-  }
-}
-
-TEST(FormatExtensionTest, VerifyEnumEquality) {
-#define X_VAL(id)                           \
-  EXPECT_EQ(absl::FormatConversionChar::id, \
-            absl::str_format_internal::FormatConversionCharInternal::id);
-  ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, );
-#undef X_VAL
-
-#define X_VAL(id)                              \
-  EXPECT_EQ(absl::FormatConversionCharSet::id, \
-            absl::str_format_internal::FormatConversionCharSetInternal::id);
-  ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, );
-#undef X_VAL
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.cc
deleted file mode 100644
index 0ded0a66af..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.cc
+++ /dev/null
@@ -1,1419 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/str_format/float_conversion.h"
-
-#include <string.h>
-
-#include <algorithm>
-#include <cassert>
-#include <cmath>
-#include <limits>
-#include <string>
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/bits.h"
-#include "absl/base/optimization.h"
-#include "absl/functional/function_ref.h"
-#include "absl/meta/type_traits.h"
-#include "absl/numeric/int128.h"
-#include "absl/strings/numbers.h"
-#include "absl/types/optional.h"
-#include "absl/types/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-namespace {
-
-// The code below wants to avoid heap allocations.
-// To do so it needs to allocate memory on the stack.
-// `StackArray` will allocate memory on the stack in the form of a uint32_t
-// array and call the provided callback with said memory.
-// It will allocate memory in increments of 512 bytes. We could allocate the
-// largest needed unconditionally, but that is more than we need in most of
-// cases. This way we use less stack in the common cases.
-class StackArray {
-  using Func = absl::FunctionRef<void(absl::Span<uint32_t>)>;
-  static constexpr size_t kStep = 512 / sizeof(uint32_t);
-  // 5 steps is 2560 bytes, which is enough to hold a long double with the
-  // largest/smallest exponents.
-  // The operations below will static_assert their particular maximum.
-  static constexpr size_t kNumSteps = 5;
-
-  // We do not want this function to be inlined.
-  // Otherwise the caller will allocate the stack space unnecessarily for all
-  // the variants even though it only calls one.
-  template <size_t steps>
-  ABSL_ATTRIBUTE_NOINLINE static void RunWithCapacityImpl(Func f) {
-    uint32_t values[steps * kStep]{};
-    f(absl::MakeSpan(values));
-  }
-
- public:
-  static constexpr size_t kMaxCapacity = kStep * kNumSteps;
-
-  static void RunWithCapacity(size_t capacity, Func f) {
-    assert(capacity <= kMaxCapacity);
-    const size_t step = (capacity + kStep - 1) / kStep;
-    assert(step <= kNumSteps);
-    switch (step) {
-      case 1:
-        return RunWithCapacityImpl<1>(f);
-      case 2:
-        return RunWithCapacityImpl<2>(f);
-      case 3:
-        return RunWithCapacityImpl<3>(f);
-      case 4:
-        return RunWithCapacityImpl<4>(f);
-      case 5:
-        return RunWithCapacityImpl<5>(f);
-    }
-
-    assert(false && "Invalid capacity");
-  }
-};
-
-// Calculates `10 * (*v) + carry` and stores the result in `*v` and returns
-// the carry.
-template <typename Int>
-inline Int MultiplyBy10WithCarry(Int *v, Int carry) {
-  using BiggerInt = absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>;
-  BiggerInt tmp = 10 * static_cast<BiggerInt>(*v) + carry;
-  *v = static_cast<Int>(tmp);
-  return static_cast<Int>(tmp >> (sizeof(Int) * 8));
-}
-
-// Calculates `(2^64 * carry + *v) / 10`.
-// Stores the quotient in `*v` and returns the remainder.
-// Requires: `0 <= carry <= 9`
-inline uint64_t DivideBy10WithCarry(uint64_t *v, uint64_t carry) {
-  constexpr uint64_t divisor = 10;
-  // 2^64 / divisor = chunk_quotient + chunk_remainder / divisor
-  constexpr uint64_t chunk_quotient = (uint64_t{1} << 63) / (divisor / 2);
-  constexpr uint64_t chunk_remainder = uint64_t{} - chunk_quotient * divisor;
-
-  const uint64_t mod = *v % divisor;
-  const uint64_t next_carry = chunk_remainder * carry + mod;
-  *v = *v / divisor + carry * chunk_quotient + next_carry / divisor;
-  return next_carry % divisor;
-}
-
-// Generates the decimal representation for an integer of the form `v * 2^exp`,
-// where `v` and `exp` are both positive integers.
-// It generates the digits from the left (ie the most significant digit first)
-// to allow for direct printing into the sink.
-//
-// Requires `0 <= exp` and `exp <= numeric_limits<long double>::max_exponent`.
-class BinaryToDecimal {
-  static constexpr int ChunksNeeded(int exp) {
-    // We will left shift a uint128 by `exp` bits, so we need `128+exp` total
-    // bits. Round up to 32.
-    // See constructor for details about adding `10%` to the value.
-    return (128 + exp + 31) / 32 * 11 / 10;
-  }
-
- public:
-  // Run the conversion for `v * 2^exp` and call `f(binary_to_decimal)`.
-  // This function will allocate enough stack space to perform the conversion.
-  static void RunConversion(uint128 v, int exp,
-                            absl::FunctionRef<void(BinaryToDecimal)> f) {
-    assert(exp > 0);
-    assert(exp <= std::numeric_limits<long double>::max_exponent);
-    static_assert(
-        static_cast<int>(StackArray::kMaxCapacity) >=
-            ChunksNeeded(std::numeric_limits<long double>::max_exponent),
-        "");
-
-    StackArray::RunWithCapacity(
-        ChunksNeeded(exp),
-        [=](absl::Span<uint32_t> input) { f(BinaryToDecimal(input, v, exp)); });
-  }
-
-  int TotalDigits() const {
-    return static_cast<int>((decimal_end_ - decimal_start_) * kDigitsPerChunk +
-                            CurrentDigits().size());
-  }
-
-  // See the current block of digits.
-  absl::string_view CurrentDigits() const {
-    return absl::string_view(digits_ + kDigitsPerChunk - size_, size_);
-  }
-
-  // Advance the current view of digits.
-  // Returns `false` when no more digits are available.
-  bool AdvanceDigits() {
-    if (decimal_start_ >= decimal_end_) return false;
-
-    uint32_t w = data_[decimal_start_++];
-    for (size_ = 0; size_ < kDigitsPerChunk; w /= 10) {
-      digits_[kDigitsPerChunk - ++size_] = w % 10 + '0';
-    }
-    return true;
-  }
-
- private:
-  BinaryToDecimal(absl::Span<uint32_t> data, uint128 v, int exp) : data_(data) {
-    // We need to print the digits directly into the sink object without
-    // buffering them all first. To do this we need two things:
-    // - to know the total number of digits to do padding when necessary
-    // - to generate the decimal digits from the left.
-    //
-    // In order to do this, we do a two pass conversion.
-    // On the first pass we convert the binary representation of the value into
-    // a decimal representation in which each uint32_t chunk holds up to 9
-    // decimal digits.  In the second pass we take each decimal-holding-uint32_t
-    // value and generate the ascii decimal digits into `digits_`.
-    //
-    // The binary and decimal representations actually share the same memory
-    // region. As we go converting the chunks from binary to decimal we free
-    // them up and reuse them for the decimal representation. One caveat is that
-    // the decimal representation is around 7% less efficient in space than the
-    // binary one. We allocate an extra 10% memory to account for this. See
-    // ChunksNeeded for this calculation.
-    int chunk_index = exp / 32;
-    decimal_start_ = decimal_end_ = ChunksNeeded(exp);
-    const int offset = exp % 32;
-    // Left shift v by exp bits.
-    data_[chunk_index] = static_cast<uint32_t>(v << offset);
-    for (v >>= (32 - offset); v; v >>= 32)
-      data_[++chunk_index] = static_cast<uint32_t>(v);
-
-    while (chunk_index >= 0) {
-      // While we have more than one chunk available, go in steps of 1e9.
-      // `data_[chunk_index]` holds the highest non-zero binary chunk, so keep
-      // the variable updated.
-      uint32_t carry = 0;
-      for (int i = chunk_index; i >= 0; --i) {
-        uint64_t tmp = uint64_t{data_[i]} + (uint64_t{carry} << 32);
-        data_[i] = static_cast<uint32_t>(tmp / uint64_t{1000000000});
-        carry = static_cast<uint32_t>(tmp % uint64_t{1000000000});
-      }
-
-      // If the highest chunk is now empty, remove it from view.
-      if (data_[chunk_index] == 0) --chunk_index;
-
-      --decimal_start_;
-      assert(decimal_start_ != chunk_index);
-      data_[decimal_start_] = carry;
-    }
-
-    // Fill the first set of digits. The first chunk might not be complete, so
-    // handle differently.
-    for (uint32_t first = data_[decimal_start_++]; first != 0; first /= 10) {
-      digits_[kDigitsPerChunk - ++size_] = first % 10 + '0';
-    }
-  }
-
- private:
-  static constexpr int kDigitsPerChunk = 9;
-
-  int decimal_start_;
-  int decimal_end_;
-
-  char digits_[kDigitsPerChunk];
-  int size_ = 0;
-
-  absl::Span<uint32_t> data_;
-};
-
-// Converts a value of the form `x * 2^-exp` into a sequence of decimal digits.
-// Requires `-exp < 0` and
-// `-exp >= limits<long double>::min_exponent - limits<long double>::digits`.
-class FractionalDigitGenerator {
- public:
-  // Run the conversion for `v * 2^exp` and call `f(generator)`.
-  // This function will allocate enough stack space to perform the conversion.
-  static void RunConversion(
-      uint128 v, int exp, absl::FunctionRef<void(FractionalDigitGenerator)> f) {
-    using Limits = std::numeric_limits<long double>;
-    assert(-exp < 0);
-    assert(-exp >= Limits::min_exponent - 128);
-    static_assert(StackArray::kMaxCapacity >=
-                      (Limits::digits + 128 - Limits::min_exponent + 31) / 32,
-                  "");
-    StackArray::RunWithCapacity((Limits::digits + exp + 31) / 32,
-                                [=](absl::Span<uint32_t> input) {
-                                  f(FractionalDigitGenerator(input, v, exp));
-                                });
-  }
-
-  // Returns true if there are any more non-zero digits left.
-  bool HasMoreDigits() const { return next_digit_ != 0 || chunk_index_ >= 0; }
-
-  // Returns true if the remainder digits are greater than 5000...
-  bool IsGreaterThanHalf() const {
-    return next_digit_ > 5 || (next_digit_ == 5 && chunk_index_ >= 0);
-  }
-  // Returns true if the remainder digits are exactly 5000...
-  bool IsExactlyHalf() const { return next_digit_ == 5 && chunk_index_ < 0; }
-
-  struct Digits {
-    int digit_before_nine;
-    int num_nines;
-  };
-
-  // Get the next set of digits.
-  // They are composed by a non-9 digit followed by a runs of zero or more 9s.
-  Digits GetDigits() {
-    Digits digits{next_digit_, 0};
-
-    next_digit_ = GetOneDigit();
-    while (next_digit_ == 9) {
-      ++digits.num_nines;
-      next_digit_ = GetOneDigit();
-    }
-
-    return digits;
-  }
-
- private:
-  // Return the next digit.
-  int GetOneDigit() {
-    if (chunk_index_ < 0) return 0;
-
-    uint32_t carry = 0;
-    for (int i = chunk_index_; i >= 0; --i) {
-      carry = MultiplyBy10WithCarry(&data_[i], carry);
-    }
-    // If the lowest chunk is now empty, remove it from view.
-    if (data_[chunk_index_] == 0) --chunk_index_;
-    return carry;
-  }
-
-  FractionalDigitGenerator(absl::Span<uint32_t> data, uint128 v, int exp)
-      : chunk_index_(exp / 32), data_(data) {
-    const int offset = exp % 32;
-    // Right shift `v` by `exp` bits.
-    data_[chunk_index_] = static_cast<uint32_t>(v << (32 - offset));
-    v >>= offset;
-    // Make sure we don't overflow the data. We already calculated that
-    // non-zero bits fit, so we might not have space for leading zero bits.
-    for (int pos = chunk_index_; v; v >>= 32)
-      data_[--pos] = static_cast<uint32_t>(v);
-
-    // Fill next_digit_, as GetDigits expects it to be populated always.
-    next_digit_ = GetOneDigit();
-  }
-
-  int next_digit_;
-  int chunk_index_;
-  absl::Span<uint32_t> data_;
-};
-
-// Count the number of leading zero bits.
-int LeadingZeros(uint64_t v) { return base_internal::CountLeadingZeros64(v); }
-int LeadingZeros(uint128 v) {
-  auto high = static_cast<uint64_t>(v >> 64);
-  auto low = static_cast<uint64_t>(v);
-  return high != 0 ? base_internal::CountLeadingZeros64(high)
-                   : 64 + base_internal::CountLeadingZeros64(low);
-}
-
-// Round up the text digits starting at `p`.
-// The buffer must have an extra digit that is known to not need rounding.
-// This is done below by having an extra '0' digit on the left.
-void RoundUp(char *p) {
-  while (*p == '9' || *p == '.') {
-    if (*p == '9') *p = '0';
-    --p;
-  }
-  ++*p;
-}
-
-// Check the previous digit and round up or down to follow the round-to-even
-// policy.
-void RoundToEven(char *p) {
-  if (*p == '.') --p;
-  if (*p % 2 == 1) RoundUp(p);
-}
-
-// Simple integral decimal digit printing for values that fit in 64-bits.
-// Returns the pointer to the last written digit.
-char *PrintIntegralDigitsFromRightFast(uint64_t v, char *p) {
-  do {
-    *--p = DivideBy10WithCarry(&v, 0) + '0';
-  } while (v != 0);
-  return p;
-}
-
-// Simple integral decimal digit printing for values that fit in 128-bits.
-// Returns the pointer to the last written digit.
-char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) {
-  auto high = static_cast<uint64_t>(v >> 64);
-  auto low = static_cast<uint64_t>(v);
-
-  while (high != 0) {
-    uint64_t carry = DivideBy10WithCarry(&high, 0);
-    carry = DivideBy10WithCarry(&low, carry);
-    *--p = carry + '0';
-  }
-  return PrintIntegralDigitsFromRightFast(low, p);
-}
-
-// Simple fractional decimal digit printing for values that fir in 64-bits after
-// shifting.
-// Performs rounding if necessary to fit within `precision`.
-// Returns the pointer to one after the last character written.
-char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp,
-                                int precision) {
-  char *p = start;
-  v <<= (64 - exp);
-  while (precision > 0) {
-    if (!v) return p;
-    *p++ = MultiplyBy10WithCarry(&v, uint64_t{0}) + '0';
-    --precision;
-  }
-
-  // We need to round.
-  if (v < 0x8000000000000000) {
-    // We round down, so nothing to do.
-  } else if (v > 0x8000000000000000) {
-    // We round up.
-    RoundUp(p - 1);
-  } else {
-    RoundToEven(p - 1);
-  }
-
-  assert(precision == 0);
-  // Precision can only be zero here.
-  return p;
-}
-
-// Simple fractional decimal digit printing for values that fir in 128-bits
-// after shifting.
-// Performs rounding if necessary to fit within `precision`.
-// Returns the pointer to one after the last character written.
-char *PrintFractionalDigitsFast(uint128 v, char *start, int exp,
-                                int precision) {
-  char *p = start;
-  v <<= (128 - exp);
-  auto high = static_cast<uint64_t>(v >> 64);
-  auto low = static_cast<uint64_t>(v);
-
-  // While we have digits to print and `low` is not empty, do the long
-  // multiplication.
-  while (precision > 0 && low != 0) {
-    uint64_t carry = MultiplyBy10WithCarry(&low, uint64_t{0});
-    carry = MultiplyBy10WithCarry(&high, carry);
-
-    *p++ = carry + '0';
-    --precision;
-  }
-
-  // Now `low` is empty, so use a faster approach for the rest of the digits.
-  // This block is pretty much the same as the main loop for the 64-bit case
-  // above.
-  while (precision > 0) {
-    if (!high) return p;
-    *p++ = MultiplyBy10WithCarry(&high, uint64_t{0}) + '0';
-    --precision;
-  }
-
-  // We need to round.
-  if (high < 0x8000000000000000) {
-    // We round down, so nothing to do.
-  } else if (high > 0x8000000000000000 || low != 0) {
-    // We round up.
-    RoundUp(p - 1);
-  } else {
-    RoundToEven(p - 1);
-  }
-
-  assert(precision == 0);
-  // Precision can only be zero here.
-  return p;
-}
-
-struct FormatState {
-  char sign_char;
-  int precision;
-  const FormatConversionSpecImpl &conv;
-  FormatSinkImpl *sink;
-
-  // In `alt` mode (flag #) we keep the `.` even if there are no fractional
-  // digits. In non-alt mode, we strip it.
-  bool ShouldPrintDot() const { return precision != 0 || conv.has_alt_flag(); }
-};
-
-struct Padding {
-  int left_spaces;
-  int zeros;
-  int right_spaces;
-};
-
-Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) {
-  if (state.conv.width() < 0 ||
-      static_cast<size_t>(state.conv.width()) <= total_size) {
-    return {0, 0, 0};
-  }
-  int missing_chars = state.conv.width() - total_size;
-  if (state.conv.has_left_flag()) {
-    return {0, 0, missing_chars};
-  } else if (state.conv.has_zero_flag()) {
-    return {0, missing_chars, 0};
-  } else {
-    return {missing_chars, 0, 0};
-  }
-}
-
-void FinalPrint(const FormatState &state, absl::string_view data,
-                int padding_offset, int trailing_zeros,
-                absl::string_view data_postfix) {
-  if (state.conv.width() < 0) {
-    // No width specified. Fast-path.
-    if (state.sign_char != '\0') state.sink->Append(1, state.sign_char);
-    state.sink->Append(data);
-    state.sink->Append(trailing_zeros, '0');
-    state.sink->Append(data_postfix);
-    return;
-  }
-
-  auto padding = ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) +
-                                         data.size() + data_postfix.size() +
-                                         static_cast<size_t>(trailing_zeros),
-                                     state);
-
-  state.sink->Append(padding.left_spaces, ' ');
-  if (state.sign_char != '\0') state.sink->Append(1, state.sign_char);
-  // Padding in general needs to be inserted somewhere in the middle of `data`.
-  state.sink->Append(data.substr(0, padding_offset));
-  state.sink->Append(padding.zeros, '0');
-  state.sink->Append(data.substr(padding_offset));
-  state.sink->Append(trailing_zeros, '0');
-  state.sink->Append(data_postfix);
-  state.sink->Append(padding.right_spaces, ' ');
-}
-
-// Fastpath %f formatter for when the shifted value fits in a simple integral
-// type.
-// Prints `v*2^exp` with the options from `state`.
-template <typename Int>
-void FormatFFast(Int v, int exp, const FormatState &state) {
-  constexpr int input_bits = sizeof(Int) * 8;
-
-  static constexpr size_t integral_size =
-      /* in case we need to round up an extra digit */ 1 +
-      /* decimal digits for uint128 */ 40 + 1;
-  char buffer[integral_size + /* . */ 1 + /* max digits uint128 */ 128];
-  buffer[integral_size] = '.';
-  char *const integral_digits_end = buffer + integral_size;
-  char *integral_digits_start;
-  char *const fractional_digits_start = buffer + integral_size + 1;
-  char *fractional_digits_end = fractional_digits_start;
-
-  if (exp >= 0) {
-    const int total_bits = input_bits - LeadingZeros(v) + exp;
-    integral_digits_start =
-        total_bits <= 64
-            ? PrintIntegralDigitsFromRightFast(static_cast<uint64_t>(v) << exp,
-                                               integral_digits_end)
-            : PrintIntegralDigitsFromRightFast(static_cast<uint128>(v) << exp,
-                                               integral_digits_end);
-  } else {
-    exp = -exp;
-
-    integral_digits_start = PrintIntegralDigitsFromRightFast(
-        exp < input_bits ? v >> exp : 0, integral_digits_end);
-    // PrintFractionalDigits may pull a carried 1 all the way up through the
-    // integral portion.
-    integral_digits_start[-1] = '0';
-
-    fractional_digits_end =
-        exp <= 64 ? PrintFractionalDigitsFast(v, fractional_digits_start, exp,
-                                              state.precision)
-                  : PrintFractionalDigitsFast(static_cast<uint128>(v),
-                                              fractional_digits_start, exp,
-                                              state.precision);
-    // There was a carry, so include the first digit too.
-    if (integral_digits_start[-1] != '0') --integral_digits_start;
-  }
-
-  size_t size = fractional_digits_end - integral_digits_start;
-
-  // In `alt` mode (flag #) we keep the `.` even if there are no fractional
-  // digits. In non-alt mode, we strip it.
-  if (!state.ShouldPrintDot()) --size;
-  FinalPrint(state, absl::string_view(integral_digits_start, size),
-             /*padding_offset=*/0,
-             static_cast<int>(state.precision - (fractional_digits_end -
-                                                 fractional_digits_start)),
-             /*data_postfix=*/"");
-}
-
-// Slow %f formatter for when the shifted value does not fit in a uint128, and
-// `exp > 0`.
-// Prints `v*2^exp` with the options from `state`.
-// This one is guaranteed to not have fractional digits, so we don't have to
-// worry about anything after the `.`.
-void FormatFPositiveExpSlow(uint128 v, int exp, const FormatState &state) {
-  BinaryToDecimal::RunConversion(v, exp, [&](BinaryToDecimal btd) {
-    const size_t total_digits =
-        btd.TotalDigits() +
-        (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0);
-
-    const auto padding = ExtraWidthToPadding(
-        total_digits + (state.sign_char != '\0' ? 1 : 0), state);
-
-    state.sink->Append(padding.left_spaces, ' ');
-    if (state.sign_char != '\0') state.sink->Append(1, state.sign_char);
-    state.sink->Append(padding.zeros, '0');
-
-    do {
-      state.sink->Append(btd.CurrentDigits());
-    } while (btd.AdvanceDigits());
-
-    if (state.ShouldPrintDot()) state.sink->Append(1, '.');
-    state.sink->Append(state.precision, '0');
-    state.sink->Append(padding.right_spaces, ' ');
-  });
-}
-
-// Slow %f formatter for when the shifted value does not fit in a uint128, and
-// `exp < 0`.
-// Prints `v*2^exp` with the options from `state`.
-// This one is guaranteed to be < 1.0, so we don't have to worry about integral
-// digits.
-void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) {
-  const size_t total_digits =
-      /* 0 */ 1 +
-      (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0);
-  auto padding =
-      ExtraWidthToPadding(total_digits + (state.sign_char ? 1 : 0), state);
-  padding.zeros += 1;
-  state.sink->Append(padding.left_spaces, ' ');
-  if (state.sign_char != '\0') state.sink->Append(1, state.sign_char);
-  state.sink->Append(padding.zeros, '0');
-
-  if (state.ShouldPrintDot()) state.sink->Append(1, '.');
-
-  // Print digits
-  int digits_to_go = state.precision;
-
-  FractionalDigitGenerator::RunConversion(
-      v, exp, [&](FractionalDigitGenerator digit_gen) {
-        // There are no digits to print here.
-        if (state.precision == 0) return;
-
-        // We go one digit at a time, while keeping track of runs of nines.
-        // The runs of nines are used to perform rounding when necessary.
-
-        while (digits_to_go > 0 && digit_gen.HasMoreDigits()) {
-          auto digits = digit_gen.GetDigits();
-
-          // Now we have a digit and a run of nines.
-          // See if we can print them all.
-          if (digits.num_nines + 1 < digits_to_go) {
-            // We don't have to round yet, so print them.
-            state.sink->Append(1, digits.digit_before_nine + '0');
-            state.sink->Append(digits.num_nines, '9');
-            digits_to_go -= digits.num_nines + 1;
-
-          } else {
-            // We can't print all the nines, see where we have to truncate.
-
-            bool round_up = false;
-            if (digits.num_nines + 1 > digits_to_go) {
-              // We round up at a nine. No need to print them.
-              round_up = true;
-            } else {
-              // We can fit all the nines, but truncate just after it.
-              if (digit_gen.IsGreaterThanHalf()) {
-                round_up = true;
-              } else if (digit_gen.IsExactlyHalf()) {
-                // Round to even
-                round_up =
-                    digits.num_nines != 0 || digits.digit_before_nine % 2 == 1;
-              }
-            }
-
-            if (round_up) {
-              state.sink->Append(1, digits.digit_before_nine + '1');
-              --digits_to_go;
-              // The rest will be zeros.
-            } else {
-              state.sink->Append(1, digits.digit_before_nine + '0');
-              state.sink->Append(digits_to_go - 1, '9');
-              digits_to_go = 0;
-            }
-            return;
-          }
-        }
-      });
-
-  state.sink->Append(digits_to_go, '0');
-  state.sink->Append(padding.right_spaces, ' ');
-}
-
-template <typename Int>
-void FormatF(Int mantissa, int exp, const FormatState &state) {
-  if (exp >= 0) {
-    const int total_bits = sizeof(Int) * 8 - LeadingZeros(mantissa) + exp;
-
-    // Fallback to the slow stack-based approach if we can't do it in a 64 or
-    // 128 bit state.
-    if (ABSL_PREDICT_FALSE(total_bits > 128)) {
-      return FormatFPositiveExpSlow(mantissa, exp, state);
-    }
-  } else {
-    // Fallback to the slow stack-based approach if we can't do it in a 64 or
-    // 128 bit state.
-    if (ABSL_PREDICT_FALSE(exp < -128)) {
-      return FormatFNegativeExpSlow(mantissa, -exp, state);
-    }
-  }
-  return FormatFFast(mantissa, exp, state);
-}
-
-// Grab the group of four bits (nibble) from `n`. E.g., nibble 1 corresponds to
-// bits 4-7.
-template <typename Int>
-uint8_t GetNibble(Int n, int nibble_index) {
-  constexpr Int mask_low_nibble = Int{0xf};
-  int shift = nibble_index * 4;
-  n &= mask_low_nibble << shift;
-  return static_cast<uint8_t>((n >> shift) & 0xf);
-}
-
-// Add one to the given nibble, applying carry to higher nibbles. Returns true
-// if overflow, false otherwise.
-template <typename Int>
-bool IncrementNibble(int nibble_index, Int *n) {
-  constexpr int kShift = sizeof(Int) * 8 - 1;
-  constexpr int kNumNibbles = sizeof(Int) * 8 / 4;
-  Int before = *n >> kShift;
-  // Here we essentially want to take the number 1 and move it into the requsted
-  // nibble, then add it to *n to effectively increment the nibble. However,
-  // ASan will complain if we try to shift the 1 beyond the limits of the Int,
-  // i.e., if the nibble_index is out of range. So therefore we check for this
-  // and if we are out of range we just add 0 which leaves *n unchanged, which
-  // seems like the reasonable thing to do in that case.
-  *n += ((nibble_index >= kNumNibbles) ? 0 : (Int{1} << (nibble_index * 4)));
-  Int after = *n >> kShift;
-  return (before && !after) || (nibble_index >= kNumNibbles);
-}
-
-// Return a mask with 1's in the given nibble and all lower nibbles.
-template <typename Int>
-Int MaskUpToNibbleInclusive(int nibble_index) {
-  constexpr int kNumNibbles = sizeof(Int) * 8 / 4;
-  static const Int ones = ~Int{0};
-  return ones >> std::max(0, 4 * (kNumNibbles - nibble_index - 1));
-}
-
-// Return a mask with 1's below the given nibble.
-template <typename Int>
-Int MaskUpToNibbleExclusive(int nibble_index) {
-  return nibble_index <= 0 ? 0 : MaskUpToNibbleInclusive<Int>(nibble_index - 1);
-}
-
-template <typename Int>
-Int MoveToNibble(uint8_t nibble, int nibble_index) {
-  return Int{nibble} << (4 * nibble_index);
-}
-
-// Given mantissa size, find optimal # of mantissa bits to put in initial digit.
-//
-// In the hex representation we keep a single hex digit to the left of the dot.
-// However, the question as to how many bits of the mantissa should be put into
-// that hex digit in theory is arbitrary, but in practice it is optimal to
-// choose based on the size of the mantissa. E.g., for a `double`, there are 53
-// mantissa bits, so that means that we should put 1 bit to the left of the dot,
-// thereby leaving 52 bits to the right, which is evenly divisible by four and
-// thus all fractional digits represent actual precision. For a `long double`,
-// on the other hand, there are 64 bits of mantissa, thus we can use all four
-// bits for the initial hex digit and still have a number left over (60) that is
-// a multiple of four. Once again, the goal is to have all fractional digits
-// represent real precision.
-template <typename Float>
-constexpr int HexFloatLeadingDigitSizeInBits() {
-  return std::numeric_limits<Float>::digits % 4 > 0
-             ? std::numeric_limits<Float>::digits % 4
-             : 4;
-}
-
-// This function captures the rounding behavior of glibc for hex float
-// representations. E.g. when rounding 0x1.ab800000 to a precision of .2
-// ("%.2a") glibc will round up because it rounds toward the even number (since
-// 0xb is an odd number, it will round up to 0xc). However, when rounding at a
-// point that is not followed by 800000..., it disregards the parity and rounds
-// up if > 8 and rounds down if < 8.
-template <typename Int>
-bool HexFloatNeedsRoundUp(Int mantissa, int final_nibble_displayed,
-                          uint8_t leading) {
-  // If the last nibble (hex digit) to be displayed is the lowest on in the
-  // mantissa then that means that we don't have any further nibbles to inform
-  // rounding, so don't round.
-  if (final_nibble_displayed <= 0) {
-    return false;
-  }
-  int rounding_nibble_idx = final_nibble_displayed - 1;
-  constexpr int kTotalNibbles = sizeof(Int) * 8 / 4;
-  assert(final_nibble_displayed <= kTotalNibbles);
-  Int mantissa_up_to_rounding_nibble_inclusive =
-      mantissa & MaskUpToNibbleInclusive<Int>(rounding_nibble_idx);
-  Int eight = MoveToNibble<Int>(8, rounding_nibble_idx);
-  if (mantissa_up_to_rounding_nibble_inclusive != eight) {
-    return mantissa_up_to_rounding_nibble_inclusive > eight;
-  }
-  // Nibble in question == 8.
-  uint8_t round_if_odd = (final_nibble_displayed == kTotalNibbles)
-                             ? leading
-                             : GetNibble(mantissa, final_nibble_displayed);
-  return round_if_odd % 2 == 1;
-}
-
-// Stores values associated with a Float type needed by the FormatA
-// implementation in order to avoid templatizing that function by the Float
-// type.
-struct HexFloatTypeParams {
-  template <typename Float>
-  explicit HexFloatTypeParams(Float)
-      : min_exponent(std::numeric_limits<Float>::min_exponent - 1),
-        leading_digit_size_bits(HexFloatLeadingDigitSizeInBits<Float>()) {
-    assert(leading_digit_size_bits >= 1 && leading_digit_size_bits <= 4);
-  }
-
-  int min_exponent;
-  int leading_digit_size_bits;
-};
-
-// Hex Float Rounding. First check if we need to round; if so, then we do that
-// by manipulating (incrementing) the mantissa, that way we can later print the
-// mantissa digits by iterating through them in the same way regardless of
-// whether a rounding happened.
-template <typename Int>
-void FormatARound(bool precision_specified, const FormatState &state,
-                  uint8_t *leading, Int *mantissa, int *exp) {
-  constexpr int kTotalNibbles = sizeof(Int) * 8 / 4;
-  // Index of the last nibble that we could display given precision.
-  int final_nibble_displayed =
-      precision_specified ? std::max(0, (kTotalNibbles - state.precision)) : 0;
-  if (HexFloatNeedsRoundUp(*mantissa, final_nibble_displayed, *leading)) {
-    // Need to round up.
-    bool overflow = IncrementNibble(final_nibble_displayed, mantissa);
-    *leading += (overflow ? 1 : 0);
-    if (ABSL_PREDICT_FALSE(*leading > 15)) {
-      // We have overflowed the leading digit. This would mean that we would
-      // need two hex digits to the left of the dot, which is not allowed. So
-      // adjust the mantissa and exponent so that the result is always 1.0eXXX.
-      *leading = 1;
-      *mantissa = 0;
-      *exp += 4;
-    }
-  }
-  // Now that we have handled a possible round-up we can go ahead and zero out
-  // all the nibbles of the mantissa that we won't need.
-  if (precision_specified) {
-    *mantissa &= ~MaskUpToNibbleExclusive<Int>(final_nibble_displayed);
-  }
-}
-
-template <typename Int>
-void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading,
-                      Int *mantissa, int *exp) {
-  constexpr int kIntBits = sizeof(Int) * 8;
-  static const Int kHighIntBit = Int{1} << (kIntBits - 1);
-  const int kLeadDigitBitsCount = float_traits.leading_digit_size_bits;
-  // Normalize mantissa so that highest bit set is in MSB position, unless we
-  // get interrupted by the exponent threshold.
-  while (*mantissa && !(*mantissa & kHighIntBit)) {
-    if (ABSL_PREDICT_FALSE(*exp - 1 < float_traits.min_exponent)) {
-      *mantissa >>= (float_traits.min_exponent - *exp);
-      *exp = float_traits.min_exponent;
-      return;
-    }
-    *mantissa <<= 1;
-    --*exp;
-  }
-  // Extract bits for leading digit then shift them away leaving the
-  // fractional part.
-  *leading =
-      static_cast<uint8_t>(*mantissa >> (kIntBits - kLeadDigitBitsCount));
-  *exp -= (*mantissa != 0) ? kLeadDigitBitsCount : *exp;
-  *mantissa <<= kLeadDigitBitsCount;
-}
-
-template <typename Int>
-void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp,
-             bool uppercase, const FormatState &state) {
-  // Int properties.
-  constexpr int kIntBits = sizeof(Int) * 8;
-  constexpr int kTotalNibbles = sizeof(Int) * 8 / 4;
-  // Did the user specify a precision explicitly?
-  const bool precision_specified = state.conv.precision() >= 0;
-
-  // ========== Normalize/Denormalize ==========
-  exp += kIntBits;  // make all digits fractional digits.
-  // This holds the (up to four) bits of leading digit, i.e., the '1' in the
-  // number 0x1.e6fp+2. It's always > 0 unless number is zero or denormal.
-  uint8_t leading = 0;
-  FormatANormalize(float_traits, &leading, &mantissa, &exp);
-
-  // =============== Rounding ==================
-  // Check if we need to round; if so, then we do that by manipulating
-  // (incrementing) the mantissa before beginning to print characters.
-  FormatARound(precision_specified, state, &leading, &mantissa, &exp);
-
-  // ============= Format Result ===============
-  // This buffer holds the "0x1.ab1de3" portion of "0x1.ab1de3pe+2". Compute the
-  // size with long double which is the largest of the floats.
-  constexpr size_t kBufSizeForHexFloatRepr =
-      2                                               // 0x
-      + std::numeric_limits<long double>::digits / 4  // number of hex digits
-      + 1                                             // round up
-      + 1;                                            // "." (dot)
-  char digits_buffer[kBufSizeForHexFloatRepr];
-  char *digits_iter = digits_buffer;
-  const char *const digits =
-      static_cast<const char *>("0123456789ABCDEF0123456789abcdef") +
-      (uppercase ? 0 : 16);
-
-  // =============== Hex Prefix ================
-  *digits_iter++ = '0';
-  *digits_iter++ = uppercase ? 'X' : 'x';
-
-  // ========== Non-Fractional Digit ===========
-  *digits_iter++ = digits[leading];
-
-  // ================== Dot ====================
-  // There are three reasons we might need a dot. Keep in mind that, at this
-  // point, the mantissa holds only the fractional part.
-  if ((precision_specified && state.precision > 0) ||
-      (!precision_specified && mantissa > 0) || state.conv.has_alt_flag()) {
-    *digits_iter++ = '.';
-  }
-
-  // ============ Fractional Digits ============
-  int digits_emitted = 0;
-  while (mantissa > 0) {
-    *digits_iter++ = digits[GetNibble(mantissa, kTotalNibbles - 1)];
-    mantissa <<= 4;
-    ++digits_emitted;
-  }
-  int trailing_zeros =
-      precision_specified ? state.precision - digits_emitted : 0;
-  assert(trailing_zeros >= 0);
-  auto digits_result = string_view(digits_buffer, digits_iter - digits_buffer);
-
-  // =============== Exponent ==================
-  constexpr size_t kBufSizeForExpDecRepr =
-      numbers_internal::kFastToBufferSize  // requred for FastIntToBuffer
-      + 1                                  // 'p' or 'P'
-      + 1;                                 // '+' or '-'
-  char exp_buffer[kBufSizeForExpDecRepr];
-  exp_buffer[0] = uppercase ? 'P' : 'p';
-  exp_buffer[1] = exp >= 0 ? '+' : '-';
-  numbers_internal::FastIntToBuffer(exp < 0 ? -exp : exp, exp_buffer + 2);
-
-  // ============ Assemble Result ==============
-  FinalPrint(state,           //
-             digits_result,   // 0xN.NNN...
-             2,               // offset in `data` to start padding if needed.
-             trailing_zeros,  // num remaining mantissa padding zeros
-             exp_buffer);     // exponent
-}
-
-char *CopyStringTo(absl::string_view v, char *out) {
-  std::memcpy(out, v.data(), v.size());
-  return out + v.size();
-}
-
-template <typename Float>
-bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv,
-                        FormatSinkImpl *sink) {
-  int w = conv.width() >= 0 ? conv.width() : 0;
-  int p = conv.precision() >= 0 ? conv.precision() : -1;
-  char fmt[32];
-  {
-    char *fp = fmt;
-    *fp++ = '%';
-    fp = CopyStringTo(FormatConversionSpecImplFriend::FlagsToString(conv), fp);
-    fp = CopyStringTo("*.*", fp);
-    if (std::is_same<long double, Float>()) {
-      *fp++ = 'L';
-    }
-    *fp++ = FormatConversionCharToChar(conv.conversion_char());
-    *fp = 0;
-    assert(fp < fmt + sizeof(fmt));
-  }
-  std::string space(512, '\0');
-  absl::string_view result;
-  while (true) {
-    int n = snprintf(&space[0], space.size(), fmt, w, p, v);
-    if (n < 0) return false;
-    if (static_cast<size_t>(n) < space.size()) {
-      result = absl::string_view(space.data(), n);
-      break;
-    }
-    space.resize(n + 1);
-  }
-  sink->Append(result);
-  return true;
-}
-
-// 128-bits in decimal: ceil(128*log(2)/log(10))
-//   or std::numeric_limits<__uint128_t>::digits10
-constexpr int kMaxFixedPrecision = 39;
-
-constexpr int kBufferLength = /*sign*/ 1 +
-                              /*integer*/ kMaxFixedPrecision +
-                              /*point*/ 1 +
-                              /*fraction*/ kMaxFixedPrecision +
-                              /*exponent e+123*/ 5;
-
-struct Buffer {
-  void push_front(char c) {
-    assert(begin > data);
-    *--begin = c;
-  }
-  void push_back(char c) {
-    assert(end < data + sizeof(data));
-    *end++ = c;
-  }
-  void pop_back() {
-    assert(begin < end);
-    --end;
-  }
-
-  char &back() {
-    assert(begin < end);
-    return end[-1];
-  }
-
-  char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; }
-
-  int size() const { return static_cast<int>(end - begin); }
-
-  char data[kBufferLength];
-  char *begin;
-  char *end;
-};
-
-enum class FormatStyle { Fixed, Precision };
-
-// If the value is Inf or Nan, print it and return true.
-// Otherwise, return false.
-template <typename Float>
-bool ConvertNonNumericFloats(char sign_char, Float v,
-                             const FormatConversionSpecImpl &conv,
-                             FormatSinkImpl *sink) {
-  char text[4], *ptr = text;
-  if (sign_char != '\0') *ptr++ = sign_char;
-  if (std::isnan(v)) {
-    ptr = std::copy_n(
-        FormatConversionCharIsUpper(conv.conversion_char()) ? "NAN" : "nan", 3,
-        ptr);
-  } else if (std::isinf(v)) {
-    ptr = std::copy_n(
-        FormatConversionCharIsUpper(conv.conversion_char()) ? "INF" : "inf", 3,
-        ptr);
-  } else {
-    return false;
-  }
-
-  return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1,
-                               conv.has_left_flag());
-}
-
-// Round up the last digit of the value.
-// It will carry over and potentially overflow. 'exp' will be adjusted in that
-// case.
-template <FormatStyle mode>
-void RoundUp(Buffer *buffer, int *exp) {
-  char *p = &buffer->back();
-  while (p >= buffer->begin && (*p == '9' || *p == '.')) {
-    if (*p == '9') *p = '0';
-    --p;
-  }
-
-  if (p < buffer->begin) {
-    *p = '1';
-    buffer->begin = p;
-    if (mode == FormatStyle::Precision) {
-      std::swap(p[1], p[2]);  // move the .
-      ++*exp;
-      buffer->pop_back();
-    }
-  } else {
-    ++*p;
-  }
-}
-
-void PrintExponent(int exp, char e, Buffer *out) {
-  out->push_back(e);
-  if (exp < 0) {
-    out->push_back('-');
-    exp = -exp;
-  } else {
-    out->push_back('+');
-  }
-  // Exponent digits.
-  if (exp > 99) {
-    out->push_back(exp / 100 + '0');
-    out->push_back(exp / 10 % 10 + '0');
-    out->push_back(exp % 10 + '0');
-  } else {
-    out->push_back(exp / 10 + '0');
-    out->push_back(exp % 10 + '0');
-  }
-}
-
-template <typename Float, typename Int>
-constexpr bool CanFitMantissa() {
-  return
-#if defined(__clang__) && !defined(__SSE3__)
-      // Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289
-      // Casting from long double to uint64_t is miscompiled and drops bits.
-      (!std::is_same<Float, long double>::value ||
-       !std::is_same<Int, uint64_t>::value) &&
-#endif
-      std::numeric_limits<Float>::digits <= std::numeric_limits<Int>::digits;
-}
-
-template <typename Float>
-struct Decomposed {
-  using MantissaType =
-      absl::conditional_t<std::is_same<long double, Float>::value, uint128,
-                          uint64_t>;
-  static_assert(std::numeric_limits<Float>::digits <= sizeof(MantissaType) * 8,
-                "");
-  MantissaType mantissa;
-  int exponent;
-};
-
-// Decompose the double into an integer mantissa and an exponent.
-template <typename Float>
-Decomposed<Float> Decompose(Float v) {
-  int exp;
-  Float m = std::frexp(v, &exp);
-  m = std::ldexp(m, std::numeric_limits<Float>::digits);
-  exp -= std::numeric_limits<Float>::digits;
-
-  return {static_cast<typename Decomposed<Float>::MantissaType>(m), exp};
-}
-
-// Print 'digits' as decimal.
-// In Fixed mode, we add a '.' at the end.
-// In Precision mode, we add a '.' after the first digit.
-template <FormatStyle mode, typename Int>
-int PrintIntegralDigits(Int digits, Buffer *out) {
-  int printed = 0;
-  if (digits) {
-    for (; digits; digits /= 10) out->push_front(digits % 10 + '0');
-    printed = out->size();
-    if (mode == FormatStyle::Precision) {
-      out->push_front(*out->begin);
-      out->begin[1] = '.';
-    } else {
-      out->push_back('.');
-    }
-  } else if (mode == FormatStyle::Fixed) {
-    out->push_front('0');
-    out->push_back('.');
-    printed = 1;
-  }
-  return printed;
-}
-
-// Back out 'extra_digits' digits and round up if necessary.
-bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value,
-                          Buffer *out, int *exp_out) {
-  if (extra_digits <= 0) return false;
-
-  // Back out the extra digits
-  out->end -= extra_digits;
-
-  bool needs_to_round_up = [&] {
-    // We look at the digit just past the end.
-    // There must be 'extra_digits' extra valid digits after end.
-    if (*out->end > '5') return true;
-    if (*out->end < '5') return false;
-    if (has_leftover_value || std::any_of(out->end + 1, out->end + extra_digits,
-                                          [](char c) { return c != '0'; }))
-      return true;
-
-    // Ends in ...50*, round to even.
-    return out->last_digit() % 2 == 1;
-  }();
-
-  if (needs_to_round_up) {
-    RoundUp<FormatStyle::Precision>(out, exp_out);
-  }
-  return true;
-}
-
-// Print the value into the buffer.
-// This will not include the exponent, which will be returned in 'exp_out' for
-// Precision mode.
-template <typename Int, typename Float, FormatStyle mode>
-bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
-                       int *exp_out) {
-  assert((CanFitMantissa<Float, Int>()));
-
-  const int int_bits = std::numeric_limits<Int>::digits;
-
-  // In precision mode, we start printing one char to the right because it will
-  // also include the '.'
-  // In fixed mode we put the dot afterwards on the right.
-  out->begin = out->end =
-      out->data + 1 + kMaxFixedPrecision + (mode == FormatStyle::Precision);
-
-  if (exp >= 0) {
-    if (std::numeric_limits<Float>::digits + exp > int_bits) {
-      // The value will overflow the Int
-      return false;
-    }
-    int digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out);
-    int digits_to_zero_pad = precision;
-    if (mode == FormatStyle::Precision) {
-      *exp_out = digits_printed - 1;
-      digits_to_zero_pad -= digits_printed - 1;
-      if (RemoveExtraPrecision(-digits_to_zero_pad, false, out, exp_out)) {
-        return true;
-      }
-    }
-    for (; digits_to_zero_pad-- > 0;) out->push_back('0');
-    return true;
-  }
-
-  exp = -exp;
-  // We need at least 4 empty bits for the next decimal digit.
-  // We will multiply by 10.
-  if (exp > int_bits - 4) return false;
-
-  const Int mask = (Int{1} << exp) - 1;
-
-  // Print the integral part first.
-  int digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out);
-  int_mantissa &= mask;
-
-  int fractional_count = precision;
-  if (mode == FormatStyle::Precision) {
-    if (digits_printed == 0) {
-      // Find the first non-zero digit, when in Precision mode.
-      *exp_out = 0;
-      if (int_mantissa) {
-        while (int_mantissa <= mask) {
-          int_mantissa *= 10;
-          --*exp_out;
-        }
-      }
-      out->push_front(static_cast<char>(int_mantissa >> exp) + '0');
-      out->push_back('.');
-      int_mantissa &= mask;
-    } else {
-      // We already have a digit, and a '.'
-      *exp_out = digits_printed - 1;
-      fractional_count -= *exp_out;
-      if (RemoveExtraPrecision(-fractional_count, int_mantissa != 0, out,
-                               exp_out)) {
-        // If we had enough digits, return right away.
-        // The code below will try to round again otherwise.
-        return true;
-      }
-    }
-  }
-
-  auto get_next_digit = [&] {
-    int_mantissa *= 10;
-    int digit = static_cast<int>(int_mantissa >> exp);
-    int_mantissa &= mask;
-    return digit;
-  };
-
-  // Print fractional_count more digits, if available.
-  for (; fractional_count > 0; --fractional_count) {
-    out->push_back(get_next_digit() + '0');
-  }
-
-  int next_digit = get_next_digit();
-  if (next_digit > 5 ||
-      (next_digit == 5 && (int_mantissa || out->last_digit() % 2 == 1))) {
-    RoundUp<mode>(out, exp_out);
-  }
-
-  return true;
-}
-
-template <FormatStyle mode, typename Float>
-bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out,
-                   int *exp) {
-  if (precision > kMaxFixedPrecision) return false;
-
-  // Try with uint64_t.
-  if (CanFitMantissa<Float, std::uint64_t>() &&
-      FloatToBufferImpl<std::uint64_t, Float, mode>(
-          static_cast<std::uint64_t>(decomposed.mantissa),
-          static_cast<std::uint64_t>(decomposed.exponent), precision, out, exp))
-    return true;
-
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
-  // If that is not enough, try with __uint128_t.
-  return CanFitMantissa<Float, __uint128_t>() &&
-         FloatToBufferImpl<__uint128_t, Float, mode>(
-             static_cast<__uint128_t>(decomposed.mantissa),
-             static_cast<__uint128_t>(decomposed.exponent), precision, out,
-             exp);
-#endif
-  return false;
-}
-
-void WriteBufferToSink(char sign_char, absl::string_view str,
-                       const FormatConversionSpecImpl &conv,
-                       FormatSinkImpl *sink) {
-  int left_spaces = 0, zeros = 0, right_spaces = 0;
-  int missing_chars =
-      conv.width() >= 0 ? std::max(conv.width() - static_cast<int>(str.size()) -
-                                       static_cast<int>(sign_char != 0),
-                                   0)
-                        : 0;
-  if (conv.has_left_flag()) {
-    right_spaces = missing_chars;
-  } else if (conv.has_zero_flag()) {
-    zeros = missing_chars;
-  } else {
-    left_spaces = missing_chars;
-  }
-
-  sink->Append(left_spaces, ' ');
-  if (sign_char != '\0') sink->Append(1, sign_char);
-  sink->Append(zeros, '0');
-  sink->Append(str);
-  sink->Append(right_spaces, ' ');
-}
-
-template <typename Float>
-bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv,
-                 FormatSinkImpl *sink) {
-  // Print the sign or the sign column.
-  Float abs_v = v;
-  char sign_char = 0;
-  if (std::signbit(abs_v)) {
-    sign_char = '-';
-    abs_v = -abs_v;
-  } else if (conv.has_show_pos_flag()) {
-    sign_char = '+';
-  } else if (conv.has_sign_col_flag()) {
-    sign_char = ' ';
-  }
-
-  // Print nan/inf.
-  if (ConvertNonNumericFloats(sign_char, abs_v, conv, sink)) {
-    return true;
-  }
-
-  int precision = conv.precision() < 0 ? 6 : conv.precision();
-
-  int exp = 0;
-
-  auto decomposed = Decompose(abs_v);
-
-  Buffer buffer;
-
-  FormatConversionChar c = conv.conversion_char();
-
-  if (c == FormatConversionCharInternal::f ||
-      c == FormatConversionCharInternal::F) {
-    FormatF(decomposed.mantissa, decomposed.exponent,
-            {sign_char, precision, conv, sink});
-    return true;
-  } else if (c == FormatConversionCharInternal::e ||
-             c == FormatConversionCharInternal::E) {
-    if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
-                                               &exp)) {
-      return FallbackToSnprintf(v, conv, sink);
-    }
-    if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back();
-    PrintExponent(
-        exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e',
-        &buffer);
-  } else if (c == FormatConversionCharInternal::g ||
-             c == FormatConversionCharInternal::G) {
-    precision = std::max(0, precision - 1);
-    if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
-                                               &exp)) {
-      return FallbackToSnprintf(v, conv, sink);
-    }
-    if (precision + 1 > exp && exp >= -4) {
-      if (exp < 0) {
-        // Have 1.23456, needs 0.00123456
-        // Move the first digit
-        buffer.begin[1] = *buffer.begin;
-        // Add some zeros
-        for (; exp < -1; ++exp) *buffer.begin-- = '0';
-        *buffer.begin-- = '.';
-        *buffer.begin = '0';
-      } else if (exp > 0) {
-        // Have 1.23456, needs 1234.56
-        // Move the '.' exp positions to the right.
-        std::rotate(buffer.begin + 1, buffer.begin + 2, buffer.begin + exp + 2);
-      }
-      exp = 0;
-    }
-    if (!conv.has_alt_flag()) {
-      while (buffer.back() == '0') buffer.pop_back();
-      if (buffer.back() == '.') buffer.pop_back();
-    }
-    if (exp) {
-      PrintExponent(
-          exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e',
-          &buffer);
-    }
-  } else if (c == FormatConversionCharInternal::a ||
-             c == FormatConversionCharInternal::A) {
-    bool uppercase = (c == FormatConversionCharInternal::A);
-    FormatA(HexFloatTypeParams(Float{}), decomposed.mantissa,
-            decomposed.exponent, uppercase, {sign_char, precision, conv, sink});
-    return true;
-  } else {
-    return false;
-  }
-
-  WriteBufferToSink(sign_char,
-                    absl::string_view(buffer.begin, buffer.end - buffer.begin),
-                    conv, sink);
-
-  return true;
-}
-
-}  // namespace
-
-bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv,
-                      FormatSinkImpl *sink) {
-  if (std::numeric_limits<long double>::digits ==
-      2 * std::numeric_limits<double>::digits) {
-    // This is the `double-double` representation of `long double`.
-    // We do not handle it natively. Fallback to snprintf.
-    return FallbackToSnprintf(v, conv, sink);
-  }
-
-  return FloatToSink(v, conv, sink);
-}
-
-bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv,
-                      FormatSinkImpl *sink) {
-  return FloatToSink(static_cast<double>(v), conv, sink);
-}
-
-bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv,
-                      FormatSinkImpl *sink) {
-  return FloatToSink(v, conv, sink);
-}
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.h b/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.h
deleted file mode 100644
index 71100e7142..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/float_conversion.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
-#define ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
-
-#include "absl/strings/internal/str_format/extension.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv,
-                      FormatSinkImpl *sink);
-
-bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv,
-                      FormatSinkImpl *sink);
-
-bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv,
-                      FormatSinkImpl *sink);
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/output.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/output.cc
deleted file mode 100644
index c4b2470613..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/output.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/str_format/output.h"
-
-#include <errno.h>
-#include <cstring>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-namespace {
-struct ClearErrnoGuard {
-  ClearErrnoGuard() : old_value(errno) { errno = 0; }
-  ~ClearErrnoGuard() {
-    if (!errno) errno = old_value;
-  }
-  int old_value;
-};
-}  // namespace
-
-void BufferRawSink::Write(string_view v) {
-  size_t to_write = std::min(v.size(), size_);
-  std::memcpy(buffer_, v.data(), to_write);
-  buffer_ += to_write;
-  size_ -= to_write;
-  total_written_ += v.size();
-}
-
-void FILERawSink::Write(string_view v) {
-  while (!v.empty() && !error_) {
-    // Reset errno to zero in case the libc implementation doesn't set errno
-    // when a failure occurs.
-    ClearErrnoGuard guard;
-
-    if (size_t result = std::fwrite(v.data(), 1, v.size(), output_)) {
-      // Some progress was made.
-      count_ += result;
-      v.remove_prefix(result);
-    } else {
-      if (errno == EINTR) {
-        continue;
-      } else if (errno) {
-        error_ = errno;
-      } else if (std::ferror(output_)) {
-        // Non-POSIX compliant libc implementations may not set errno, so we
-        // have check the streams error indicator.
-        error_ = EBADF;
-      } else {
-        // We're likely on a non-POSIX system that encountered EINTR but had no
-        // way of reporting it.
-        continue;
-      }
-    }
-  }
-}
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/output.h b/third_party/abseil_cpp/absl/strings/internal/str_format/output.h
deleted file mode 100644
index 8030dae00f..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/output.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Output extension hooks for the Format library.
-// `internal::InvokeFlush` calls the appropriate flush function for the
-// specified output argument.
-// `BufferRawSink` is a simple output sink for a char buffer. Used by SnprintF.
-// `FILERawSink` is a std::FILE* based sink. Used by PrintF and FprintF.
-
-#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
-#define ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
-
-#include <cstdio>
-#include <ostream>
-#include <string>
-
-#include "absl/base/port.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-// RawSink implementation that writes into a char* buffer.
-// It will not overflow the buffer, but will keep the total count of chars
-// that would have been written.
-class BufferRawSink {
- public:
-  BufferRawSink(char* buffer, size_t size) : buffer_(buffer), size_(size) {}
-
-  size_t total_written() const { return total_written_; }
-  void Write(string_view v);
-
- private:
-  char* buffer_;
-  size_t size_;
-  size_t total_written_ = 0;
-};
-
-// RawSink implementation that writes into a FILE*.
-// It keeps track of the total number of bytes written and any error encountered
-// during the writes.
-class FILERawSink {
- public:
-  explicit FILERawSink(std::FILE* output) : output_(output) {}
-
-  void Write(string_view v);
-
-  size_t count() const { return count_; }
-  int error() const { return error_; }
-
- private:
-  std::FILE* output_;
-  int error_ = 0;
-  size_t count_ = 0;
-};
-
-// Provide RawSink integration with common types from the STL.
-inline void AbslFormatFlush(std::string* out, string_view s) {
-  out->append(s.data(), s.size());
-}
-inline void AbslFormatFlush(std::ostream* out, string_view s) {
-  out->write(s.data(), s.size());
-}
-
-inline void AbslFormatFlush(FILERawSink* sink, string_view v) {
-  sink->Write(v);
-}
-
-inline void AbslFormatFlush(BufferRawSink* sink, string_view v) {
-  sink->Write(v);
-}
-
-// This is a SFINAE to get a better compiler error message when the type
-// is not supported.
-template <typename T>
-auto InvokeFlush(T* out, string_view s) -> decltype(AbslFormatFlush(out, s)) {
-  AbslFormatFlush(out, s);
-}
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/output_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/output_test.cc
deleted file mode 100644
index ce2e91a0bb..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/output_test.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/str_format/output.h"
-
-#include <sstream>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/cord.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-TEST(InvokeFlush, String) {
-  std::string str = "ABC";
-  str_format_internal::InvokeFlush(&str, "DEF");
-  EXPECT_EQ(str, "ABCDEF");
-}
-
-TEST(InvokeFlush, Stream) {
-  std::stringstream str;
-  str << "ABC";
-  str_format_internal::InvokeFlush(&str, "DEF");
-  EXPECT_EQ(str.str(), "ABCDEF");
-}
-
-TEST(InvokeFlush, Cord) {
-  absl::Cord str("ABC");
-  str_format_internal::InvokeFlush(&str, "DEF");
-  EXPECT_EQ(str, "ABCDEF");
-}
-
-TEST(BufferRawSink, Limits) {
-  char buf[16];
-  {
-    std::fill(std::begin(buf), std::end(buf), 'x');
-    str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
-    str_format_internal::InvokeFlush(&bufsink, "Hello World237");
-    EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx");
-  }
-  {
-    std::fill(std::begin(buf), std::end(buf), 'x');
-    str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
-    str_format_internal::InvokeFlush(&bufsink, "Hello World237237");
-    EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x");
-  }
-  {
-    std::fill(std::begin(buf), std::end(buf), 'x');
-    str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
-    str_format_internal::InvokeFlush(&bufsink, "Hello World");
-    str_format_internal::InvokeFlush(&bufsink, "237");
-    EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx");
-  }
-  {
-    std::fill(std::begin(buf), std::end(buf), 'x');
-    str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
-    str_format_internal::InvokeFlush(&bufsink, "Hello World");
-    str_format_internal::InvokeFlush(&bufsink, "237237");
-    EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x");
-  }
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/parser.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/parser.cc
deleted file mode 100644
index f308d02351..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/parser.cc
+++ /dev/null
@@ -1,350 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/str_format/parser.h"
-
-#include <assert.h>
-#include <string.h>
-#include <wchar.h>
-#include <cctype>
-#include <cstdint>
-
-#include <algorithm>
-#include <initializer_list>
-#include <limits>
-#include <ostream>
-#include <string>
-#include <unordered_set>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-using CC = FormatConversionCharInternal;
-using LM = LengthMod;
-
-ABSL_CONST_INIT const ConvTag kTags[256] = {
-    {},    {},    {},    {},    {},    {},    {},    {},     // 00-07
-    {},    {},    {},    {},    {},    {},    {},    {},     // 08-0f
-    {},    {},    {},    {},    {},    {},    {},    {},     // 10-17
-    {},    {},    {},    {},    {},    {},    {},    {},     // 18-1f
-    {},    {},    {},    {},    {},    {},    {},    {},     // 20-27
-    {},    {},    {},    {},    {},    {},    {},    {},     // 28-2f
-    {},    {},    {},    {},    {},    {},    {},    {},     // 30-37
-    {},    {},    {},    {},    {},    {},    {},    {},     // 38-3f
-    {},    CC::A, {},    {},    {},    CC::E, CC::F, CC::G,  // @ABCDEFG
-    {},    {},    {},    {},    LM::L, {},    {},    {},     // HIJKLMNO
-    {},    {},    {},    {},    {},    {},    {},    {},     // PQRSTUVW
-    CC::X, {},    {},    {},    {},    {},    {},    {},     // XYZ[\]^_
-    {},    CC::a, {},    CC::c, CC::d, CC::e, CC::f, CC::g,  // `abcdefg
-    LM::h, CC::i, LM::j, {},    LM::l, {},    CC::n, CC::o,  // hijklmno
-    CC::p, LM::q, {},    CC::s, LM::t, CC::u, {},    {},     // pqrstuvw
-    CC::x, {},    LM::z, {},    {},    {},    {},    {},     // xyz{|}!
-    {},    {},    {},    {},    {},    {},    {},    {},     // 80-87
-    {},    {},    {},    {},    {},    {},    {},    {},     // 88-8f
-    {},    {},    {},    {},    {},    {},    {},    {},     // 90-97
-    {},    {},    {},    {},    {},    {},    {},    {},     // 98-9f
-    {},    {},    {},    {},    {},    {},    {},    {},     // a0-a7
-    {},    {},    {},    {},    {},    {},    {},    {},     // a8-af
-    {},    {},    {},    {},    {},    {},    {},    {},     // b0-b7
-    {},    {},    {},    {},    {},    {},    {},    {},     // b8-bf
-    {},    {},    {},    {},    {},    {},    {},    {},     // c0-c7
-    {},    {},    {},    {},    {},    {},    {},    {},     // c8-cf
-    {},    {},    {},    {},    {},    {},    {},    {},     // d0-d7
-    {},    {},    {},    {},    {},    {},    {},    {},     // d8-df
-    {},    {},    {},    {},    {},    {},    {},    {},     // e0-e7
-    {},    {},    {},    {},    {},    {},    {},    {},     // e8-ef
-    {},    {},    {},    {},    {},    {},    {},    {},     // f0-f7
-    {},    {},    {},    {},    {},    {},    {},    {},     // f8-ff
-};
-
-namespace {
-
-bool CheckFastPathSetting(const UnboundConversion& conv) {
-  bool should_be_basic = !conv.flags.left &&      //
-                         !conv.flags.show_pos &&  //
-                         !conv.flags.sign_col &&  //
-                         !conv.flags.alt &&       //
-                         !conv.flags.zero &&      //
-                         (conv.width.value() == -1) &&
-                         (conv.precision.value() == -1);
-  if (should_be_basic != conv.flags.basic) {
-    fprintf(stderr,
-            "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d "
-            "width=%d precision=%d\n",
-            conv.flags.basic, conv.flags.left, conv.flags.show_pos,
-            conv.flags.sign_col, conv.flags.alt, conv.flags.zero,
-            conv.width.value(), conv.precision.value());
-  }
-  return should_be_basic == conv.flags.basic;
-}
-
-template <bool is_positional>
-const char *ConsumeConversion(const char *pos, const char *const end,
-                              UnboundConversion *conv, int *next_arg) {
-  const char* const original_pos = pos;
-  char c;
-  // Read the next char into `c` and update `pos`. Returns false if there are
-  // no more chars to read.
-#define ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR()          \
-  do {                                                  \
-    if (ABSL_PREDICT_FALSE(pos == end)) return nullptr; \
-    c = *pos++;                                         \
-  } while (0)
-
-  const auto parse_digits = [&] {
-    int digits = c - '0';
-    // We do not want to overflow `digits` so we consume at most digits10
-    // digits. If there are more digits the parsing will fail later on when the
-    // digit doesn't match the expected characters.
-    int num_digits = std::numeric_limits<int>::digits10;
-    for (;;) {
-      if (ABSL_PREDICT_FALSE(pos == end)) break;
-      c = *pos++;
-      if (!std::isdigit(c)) break;
-      --num_digits;
-      if (ABSL_PREDICT_FALSE(!num_digits)) break;
-      digits = 10 * digits + c - '0';
-    }
-    return digits;
-  };
-
-  if (is_positional) {
-    ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-    if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
-    conv->arg_position = parse_digits();
-    assert(conv->arg_position > 0);
-    if (ABSL_PREDICT_FALSE(c != '$')) return nullptr;
-  }
-
-  ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-
-  // We should start with the basic flag on.
-  assert(conv->flags.basic);
-
-  // Any non alpha character makes this conversion not basic.
-  // This includes flags (-+ #0), width (1-9, *) or precision (.).
-  // All conversion characters and length modifiers are alpha characters.
-  if (c < 'A') {
-    conv->flags.basic = false;
-
-    for (; c <= '0';) {
-      // FIXME: We might be able to speed this up reusing the lookup table from
-      // above. It might require changing Flags to be a plain integer where we
-      // can |= a value.
-      switch (c) {
-        case '-':
-          conv->flags.left = true;
-          break;
-        case '+':
-          conv->flags.show_pos = true;
-          break;
-        case ' ':
-          conv->flags.sign_col = true;
-          break;
-        case '#':
-          conv->flags.alt = true;
-          break;
-        case '0':
-          conv->flags.zero = true;
-          break;
-        default:
-          goto flags_done;
-      }
-      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-    }
-flags_done:
-
-    if (c <= '9') {
-      if (c >= '0') {
-        int maybe_width = parse_digits();
-        if (!is_positional && c == '$') {
-          if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr;
-          // Positional conversion.
-          *next_arg = -1;
-          conv->flags = Flags();
-          conv->flags.basic = true;
-          return ConsumeConversion<true>(original_pos, end, conv, next_arg);
-        }
-        conv->width.set_value(maybe_width);
-      } else if (c == '*') {
-        ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-        if (is_positional) {
-          if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
-          conv->width.set_from_arg(parse_digits());
-          if (ABSL_PREDICT_FALSE(c != '$')) return nullptr;
-          ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-        } else {
-          conv->width.set_from_arg(++*next_arg);
-        }
-      }
-    }
-
-    if (c == '.') {
-      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-      if (std::isdigit(c)) {
-        conv->precision.set_value(parse_digits());
-      } else if (c == '*') {
-        ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-        if (is_positional) {
-          if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr;
-          conv->precision.set_from_arg(parse_digits());
-          if (c != '$') return nullptr;
-          ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-        } else {
-          conv->precision.set_from_arg(++*next_arg);
-        }
-      } else {
-        conv->precision.set_value(0);
-      }
-    }
-  }
-
-  auto tag = GetTagForChar(c);
-
-  if (ABSL_PREDICT_FALSE(!tag.is_conv())) {
-    if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr;
-
-    // It is a length modifier.
-    using str_format_internal::LengthMod;
-    LengthMod length_mod = tag.as_length();
-    ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-    if (c == 'h' && length_mod == LengthMod::h) {
-      conv->length_mod = LengthMod::hh;
-      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-    } else if (c == 'l' && length_mod == LengthMod::l) {
-      conv->length_mod = LengthMod::ll;
-      ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
-    } else {
-      conv->length_mod = length_mod;
-    }
-    tag = GetTagForChar(c);
-    if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr;
-  }
-
-  assert(CheckFastPathSetting(*conv));
-  (void)(&CheckFastPathSetting);
-
-  conv->conv = tag.as_conv();
-  if (!is_positional) conv->arg_position = ++*next_arg;
-  return pos;
-}
-
-}  // namespace
-
-std::string LengthModToString(LengthMod v) {
-  switch (v) {
-    case LengthMod::h:
-      return "h";
-    case LengthMod::hh:
-      return "hh";
-    case LengthMod::l:
-      return "l";
-    case LengthMod::ll:
-      return "ll";
-    case LengthMod::L:
-      return "L";
-    case LengthMod::j:
-      return "j";
-    case LengthMod::z:
-      return "z";
-    case LengthMod::t:
-      return "t";
-    case LengthMod::q:
-      return "q";
-    case LengthMod::none:
-      return "";
-  }
-  return "";
-}
-
-const char *ConsumeUnboundConversion(const char *p, const char *end,
-                                     UnboundConversion *conv, int *next_arg) {
-  if (*next_arg < 0) return ConsumeConversion<true>(p, end, conv, next_arg);
-  return ConsumeConversion<false>(p, end, conv, next_arg);
-}
-
-struct ParsedFormatBase::ParsedFormatConsumer {
-  explicit ParsedFormatConsumer(ParsedFormatBase *parsedformat)
-      : parsed(parsedformat), data_pos(parsedformat->data_.get()) {}
-
-  bool Append(string_view s) {
-    if (s.empty()) return true;
-
-    size_t text_end = AppendText(s);
-
-    if (!parsed->items_.empty() && !parsed->items_.back().is_conversion) {
-      // Let's extend the existing text run.
-      parsed->items_.back().text_end = text_end;
-    } else {
-      // Let's make a new text run.
-      parsed->items_.push_back({false, text_end, {}});
-    }
-    return true;
-  }
-
-  bool ConvertOne(const UnboundConversion &conv, string_view s) {
-    size_t text_end = AppendText(s);
-    parsed->items_.push_back({true, text_end, conv});
-    return true;
-  }
-
-  size_t AppendText(string_view s) {
-    memcpy(data_pos, s.data(), s.size());
-    data_pos += s.size();
-    return static_cast<size_t>(data_pos - parsed->data_.get());
-  }
-
-  ParsedFormatBase *parsed;
-  char* data_pos;
-};
-
-ParsedFormatBase::ParsedFormatBase(
-    string_view format, bool allow_ignored,
-    std::initializer_list<FormatConversionCharSet> convs)
-    : data_(format.empty() ? nullptr : new char[format.size()]) {
-  has_error_ = !ParseFormatString(format, ParsedFormatConsumer(this)) ||
-               !MatchesConversions(allow_ignored, convs);
-}
-
-bool ParsedFormatBase::MatchesConversions(
-    bool allow_ignored,
-    std::initializer_list<FormatConversionCharSet> convs) const {
-  std::unordered_set<int> used;
-  auto add_if_valid_conv = [&](int pos, char c) {
-      if (static_cast<size_t>(pos) > convs.size() ||
-          !Contains(convs.begin()[pos - 1], c))
-        return false;
-      used.insert(pos);
-      return true;
-  };
-  for (const ConversionItem &item : items_) {
-    if (!item.is_conversion) continue;
-    auto &conv = item.conv;
-    if (conv.precision.is_from_arg() &&
-        !add_if_valid_conv(conv.precision.get_from_arg(), '*'))
-      return false;
-    if (conv.width.is_from_arg() &&
-        !add_if_valid_conv(conv.width.get_from_arg(), '*'))
-      return false;
-    if (!add_if_valid_conv(conv.arg_position,
-                           FormatConversionCharToChar(conv.conv)))
-      return false;
-  }
-  return used.size() == convs.size() || allow_ignored;
-}
-
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/parser.h b/third_party/abseil_cpp/absl/strings/internal/str_format/parser.h
deleted file mode 100644
index 6504dd3ddc..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/parser.h
+++ /dev/null
@@ -1,349 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
-#define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
-
-#include <limits.h>
-#include <stddef.h>
-#include <stdlib.h>
-
-#include <cassert>
-#include <cstdint>
-#include <initializer_list>
-#include <iosfwd>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "absl/strings/internal/str_format/checker.h"
-#include "absl/strings/internal/str_format/extension.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
-
-std::string LengthModToString(LengthMod v);
-
-// The analyzed properties of a single specified conversion.
-struct UnboundConversion {
-  UnboundConversion()
-      : flags() /* This is required to zero all the fields of flags. */ {
-    flags.basic = true;
-  }
-
-  class InputValue {
-   public:
-    void set_value(int value) {
-      assert(value >= 0);
-      value_ = value;
-    }
-    int value() const { return value_; }
-
-    // Marks the value as "from arg". aka the '*' format.
-    // Requires `value >= 1`.
-    // When set, is_from_arg() return true and get_from_arg() returns the
-    // original value.
-    // `value()`'s return value is unspecfied in this state.
-    void set_from_arg(int value) {
-      assert(value > 0);
-      value_ = -value - 1;
-    }
-    bool is_from_arg() const { return value_ < -1; }
-    int get_from_arg() const {
-      assert(is_from_arg());
-      return -value_ - 1;
-    }
-
-   private:
-    int value_ = -1;
-  };
-
-  // No need to initialize. It will always be set in the parser.
-  int arg_position;
-
-  InputValue width;
-  InputValue precision;
-
-  Flags flags;
-  LengthMod length_mod = LengthMod::none;
-  FormatConversionChar conv = FormatConversionCharInternal::kNone;
-};
-
-// Consume conversion spec prefix (not including '%') of [p, end) if valid.
-// Examples of valid specs would be e.g.: "s", "d", "-12.6f".
-// If valid, it returns the first character following the conversion spec,
-// and the spec part is broken down and returned in 'conv'.
-// If invalid, returns nullptr.
-const char* ConsumeUnboundConversion(const char* p, const char* end,
-                                     UnboundConversion* conv, int* next_arg);
-
-// Helper tag class for the table below.
-// It allows fast `char -> ConversionChar/LengthMod` checking and
-// conversions.
-class ConvTag {
- public:
-  constexpr ConvTag(FormatConversionChar conversion_char)  // NOLINT
-      : tag_(static_cast<int8_t>(conversion_char)) {}
-  // We invert the length modifiers to make them negative so that we can easily
-  // test for them.
-  constexpr ConvTag(LengthMod length_mod)  // NOLINT
-      : tag_(~static_cast<std::int8_t>(length_mod)) {}
-  // Everything else is -128, which is negative to make is_conv() simpler.
-  constexpr ConvTag() : tag_(-128) {}
-
-  bool is_conv() const { return tag_ >= 0; }
-  bool is_length() const { return tag_ < 0 && tag_ != -128; }
-  FormatConversionChar as_conv() const {
-    assert(is_conv());
-    return static_cast<FormatConversionChar>(tag_);
-  }
-  LengthMod as_length() const {
-    assert(is_length());
-    return static_cast<LengthMod>(~tag_);
-  }
-
- private:
-  std::int8_t tag_;
-};
-
-extern const ConvTag kTags[256];
-// Keep a single table for all the conversion chars and length modifiers.
-inline ConvTag GetTagForChar(char c) {
-  return kTags[static_cast<unsigned char>(c)];
-}
-
-// Parse the format string provided in 'src' and pass the identified items into
-// 'consumer'.
-// Text runs will be passed by calling
-//   Consumer::Append(string_view);
-// ConversionItems will be passed by calling
-//   Consumer::ConvertOne(UnboundConversion, string_view);
-// In the case of ConvertOne, the string_view that is passed is the
-// portion of the format string corresponding to the conversion, not including
-// the leading %. On success, it returns true. On failure, it stops and returns
-// false.
-template <typename Consumer>
-bool ParseFormatString(string_view src, Consumer consumer) {
-  int next_arg = 0;
-  const char* p = src.data();
-  const char* const end = p + src.size();
-  while (p != end) {
-    const char* percent = static_cast<const char*>(memchr(p, '%', end - p));
-    if (!percent) {
-      // We found the last substring.
-      return consumer.Append(string_view(p, end - p));
-    }
-    // We found a percent, so push the text run then process the percent.
-    if (ABSL_PREDICT_FALSE(!consumer.Append(string_view(p, percent - p)))) {
-      return false;
-    }
-    if (ABSL_PREDICT_FALSE(percent + 1 >= end)) return false;
-
-    auto tag = GetTagForChar(percent[1]);
-    if (tag.is_conv()) {
-      if (ABSL_PREDICT_FALSE(next_arg < 0)) {
-        // This indicates an error in the format string.
-        // The only way to get `next_arg < 0` here is to have a positional
-        // argument first which sets next_arg to -1 and then a non-positional
-        // argument.
-        return false;
-      }
-      p = percent + 2;
-
-      // Keep this case separate from the one below.
-      // ConvertOne is more efficient when the compiler can see that the `basic`
-      // flag is set.
-      UnboundConversion conv;
-      conv.conv = tag.as_conv();
-      conv.arg_position = ++next_arg;
-      if (ABSL_PREDICT_FALSE(
-              !consumer.ConvertOne(conv, string_view(percent + 1, 1)))) {
-        return false;
-      }
-    } else if (percent[1] != '%') {
-      UnboundConversion conv;
-      p = ConsumeUnboundConversion(percent + 1, end, &conv, &next_arg);
-      if (ABSL_PREDICT_FALSE(p == nullptr)) return false;
-      if (ABSL_PREDICT_FALSE(!consumer.ConvertOne(
-          conv, string_view(percent + 1, p - (percent + 1))))) {
-        return false;
-      }
-    } else {
-      if (ABSL_PREDICT_FALSE(!consumer.Append("%"))) return false;
-      p = percent + 2;
-      continue;
-    }
-  }
-  return true;
-}
-
-// Always returns true, or fails to compile in a constexpr context if s does not
-// point to a constexpr char array.
-constexpr bool EnsureConstexpr(string_view s) {
-  return s.empty() || s[0] == s[0];
-}
-
-class ParsedFormatBase {
- public:
-  explicit ParsedFormatBase(
-      string_view format, bool allow_ignored,
-      std::initializer_list<FormatConversionCharSet> convs);
-
-  ParsedFormatBase(const ParsedFormatBase& other) { *this = other; }
-
-  ParsedFormatBase(ParsedFormatBase&& other) { *this = std::move(other); }
-
-  ParsedFormatBase& operator=(const ParsedFormatBase& other) {
-    if (this == &other) return *this;
-    has_error_ = other.has_error_;
-    items_ = other.items_;
-    size_t text_size = items_.empty() ? 0 : items_.back().text_end;
-    data_.reset(new char[text_size]);
-    memcpy(data_.get(), other.data_.get(), text_size);
-    return *this;
-  }
-
-  ParsedFormatBase& operator=(ParsedFormatBase&& other) {
-    if (this == &other) return *this;
-    has_error_ = other.has_error_;
-    data_ = std::move(other.data_);
-    items_ = std::move(other.items_);
-    // Reset the vector to make sure the invariants hold.
-    other.items_.clear();
-    return *this;
-  }
-
-  template <typename Consumer>
-  bool ProcessFormat(Consumer consumer) const {
-    const char* const base = data_.get();
-    string_view text(base, 0);
-    for (const auto& item : items_) {
-      const char* const end = text.data() + text.size();
-      text = string_view(end, (base + item.text_end) - end);
-      if (item.is_conversion) {
-        if (!consumer.ConvertOne(item.conv, text)) return false;
-      } else {
-        if (!consumer.Append(text)) return false;
-      }
-    }
-    return !has_error_;
-  }
-
-  bool has_error() const { return has_error_; }
-
- private:
-  // Returns whether the conversions match and if !allow_ignored it verifies
-  // that all conversions are used by the format.
-  bool MatchesConversions(
-      bool allow_ignored,
-      std::initializer_list<FormatConversionCharSet> convs) const;
-
-  struct ParsedFormatConsumer;
-
-  struct ConversionItem {
-    bool is_conversion;
-    // Points to the past-the-end location of this element in the data_ array.
-    size_t text_end;
-    UnboundConversion conv;
-  };
-
-  bool has_error_;
-  std::unique_ptr<char[]> data_;
-  std::vector<ConversionItem> items_;
-};
-
-
-// A value type representing a preparsed format.  These can be created, copied
-// around, and reused to speed up formatting loops.
-// The user must specify through the template arguments the conversion
-// characters used in the format. This will be checked at compile time.
-//
-// This class uses Conv enum values to specify each argument.
-// This allows for more flexibility as you can specify multiple possible
-// conversion characters for each argument.
-// ParsedFormat<char...> is a simplified alias for when the user only
-// needs to specify a single conversion character for each argument.
-//
-// Example:
-//   // Extended format supports multiple characters per argument:
-//   using MyFormat = ExtendedParsedFormat<Conv::d | Conv::x>;
-//   MyFormat GetFormat(bool use_hex) {
-//     if (use_hex) return MyFormat("foo %x bar");
-//     return MyFormat("foo %d bar");
-//   }
-//   // 'format' can be used with any value that supports 'd' and 'x',
-//   // like `int`.
-//   auto format = GetFormat(use_hex);
-//   value = StringF(format, i);
-//
-// This class also supports runtime format checking with the ::New() and
-// ::NewAllowIgnored() factory functions.
-// This is the only API that allows the user to pass a runtime specified format
-// string. These factory functions will return NULL if the format does not match
-// the conversions requested by the user.
-template <FormatConversionCharSet... C>
-class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase {
- public:
-  explicit ExtendedParsedFormat(string_view format)
-#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-      __attribute__((
-          enable_if(str_format_internal::EnsureConstexpr(format),
-                    "Format string is not constexpr."),
-          enable_if(str_format_internal::ValidFormatImpl<C...>(format),
-                    "Format specified does not match the template arguments.")))
-#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-      : ExtendedParsedFormat(format, false) {
-  }
-
-  // ExtendedParsedFormat factory function.
-  // The user still has to specify the conversion characters, but they will not
-  // be checked at compile time. Instead, it will be checked at runtime.
-  // This delays the checking to runtime, but allows the user to pass
-  // dynamically sourced formats.
-  // It returns NULL if the format does not match the conversion characters.
-  // The user is responsible for checking the return value before using it.
-  //
-  // The 'New' variant will check that all the specified arguments are being
-  // consumed by the format and return NULL if any argument is being ignored.
-  // The 'NewAllowIgnored' variant will not verify this and will allow formats
-  // that ignore arguments.
-  static std::unique_ptr<ExtendedParsedFormat> New(string_view format) {
-    return New(format, false);
-  }
-  static std::unique_ptr<ExtendedParsedFormat> NewAllowIgnored(
-      string_view format) {
-    return New(format, true);
-  }
-
- private:
-  static std::unique_ptr<ExtendedParsedFormat> New(string_view format,
-                                                   bool allow_ignored) {
-    std::unique_ptr<ExtendedParsedFormat> conv(
-        new ExtendedParsedFormat(format, allow_ignored));
-    if (conv->has_error()) return nullptr;
-    return conv;
-  }
-
-  ExtendedParsedFormat(string_view s, bool allow_ignored)
-      : ParsedFormatBase(s, allow_ignored, {C...}) {}
-};
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_format/parser_test.cc b/third_party/abseil_cpp/absl/strings/internal/str_format/parser_test.cc
deleted file mode 100644
index a5fa1c79aa..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_format/parser_test.cc
+++ /dev/null
@@ -1,427 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/str_format/parser.h"
-
-#include <string.h>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/macros.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace str_format_internal {
-
-namespace {
-
-using testing::Pair;
-
-TEST(LengthModTest, Names) {
-  struct Expectation {
-    int line;
-    LengthMod mod;
-    const char *name;
-  };
-  const Expectation kExpect[] = {
-    {__LINE__, LengthMod::none, ""  },
-    {__LINE__, LengthMod::h,    "h" },
-    {__LINE__, LengthMod::hh,   "hh"},
-    {__LINE__, LengthMod::l,    "l" },
-    {__LINE__, LengthMod::ll,   "ll"},
-    {__LINE__, LengthMod::L,    "L" },
-    {__LINE__, LengthMod::j,    "j" },
-    {__LINE__, LengthMod::z,    "z" },
-    {__LINE__, LengthMod::t,    "t" },
-    {__LINE__, LengthMod::q,    "q" },
-  };
-  EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), 10);
-  for (auto e : kExpect) {
-    SCOPED_TRACE(e.line);
-    EXPECT_EQ(e.name, LengthModToString(e.mod));
-  }
-}
-
-TEST(ConversionCharTest, Names) {
-  struct Expectation {
-    FormatConversionChar id;
-    char name;
-  };
-  // clang-format off
-  const Expectation kExpect[] = {
-#define X(c) {FormatConversionCharInternal::c, #c[0]}
-    X(c), X(s),                                      // text
-    X(d), X(i), X(o), X(u), X(x), X(X),              // int
-    X(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A),  // float
-    X(n), X(p),                                      // misc
-#undef X
-    {FormatConversionCharInternal::kNone, '\0'},
-  };
-  // clang-format on
-  for (auto e : kExpect) {
-    SCOPED_TRACE(e.name);
-    FormatConversionChar v = e.id;
-    EXPECT_EQ(e.name, FormatConversionCharToChar(v));
-  }
-}
-
-class ConsumeUnboundConversionTest : public ::testing::Test {
- public:
-  std::pair<string_view, string_view> Consume(string_view src) {
-    int next = 0;
-    o = UnboundConversion();  // refresh
-    const char* p = ConsumeUnboundConversion(
-        src.data(), src.data() + src.size(), &o, &next);
-    if (!p) return {{}, src};
-    return {string_view(src.data(), p - src.data()),
-            string_view(p, src.data() + src.size() - p)};
-  }
-
-  bool Run(const char *fmt, bool force_positional = false) {
-    int next = force_positional ? -1 : 0;
-    o = UnboundConversion();  // refresh
-    return ConsumeUnboundConversion(fmt, fmt + strlen(fmt), &o, &next) ==
-           fmt + strlen(fmt);
-  }
-  UnboundConversion o;
-};
-
-TEST_F(ConsumeUnboundConversionTest, ConsumeSpecification) {
-  struct Expectation {
-    int line;
-    string_view src;
-    string_view out;
-    string_view src_post;
-  };
-  const Expectation kExpect[] = {
-    {__LINE__, "",     "",     ""  },
-    {__LINE__, "b",    "",     "b" },  // 'b' is invalid
-    {__LINE__, "ba",   "",     "ba"},  // 'b' is invalid
-    {__LINE__, "l",    "",     "l" },  // just length mod isn't okay
-    {__LINE__, "d",    "d",    ""  },  // basic
-    {__LINE__, "d ",   "d",    " " },  // leave suffix
-    {__LINE__, "dd",   "d",    "d" },  // don't be greedy
-    {__LINE__, "d9",   "d",    "9" },  // leave non-space suffix
-    {__LINE__, "dzz",  "d",    "zz"},  // length mod as suffix
-    {__LINE__, "1$*2$d", "1$*2$d", ""  },  // arg indexing and * allowed.
-    {__LINE__, "0-14.3hhd", "0-14.3hhd", ""},  // precision, width
-    {__LINE__, " 0-+#14.3hhd", " 0-+#14.3hhd", ""},  // flags
-  };
-  for (const auto& e : kExpect) {
-    SCOPED_TRACE(e.line);
-    EXPECT_THAT(Consume(e.src), Pair(e.out, e.src_post));
-  }
-}
-
-TEST_F(ConsumeUnboundConversionTest, BasicConversion) {
-  EXPECT_FALSE(Run(""));
-  EXPECT_FALSE(Run("z"));
-
-  EXPECT_FALSE(Run("dd"));  // no excess allowed
-
-  EXPECT_TRUE(Run("d"));
-  EXPECT_EQ('d', FormatConversionCharToChar(o.conv));
-  EXPECT_FALSE(o.width.is_from_arg());
-  EXPECT_LT(o.width.value(), 0);
-  EXPECT_FALSE(o.precision.is_from_arg());
-  EXPECT_LT(o.precision.value(), 0);
-  EXPECT_EQ(1, o.arg_position);
-}
-
-TEST_F(ConsumeUnboundConversionTest, ArgPosition) {
-  EXPECT_TRUE(Run("d"));
-  EXPECT_EQ(1, o.arg_position);
-  EXPECT_TRUE(Run("3$d"));
-  EXPECT_EQ(3, o.arg_position);
-  EXPECT_TRUE(Run("1$d"));
-  EXPECT_EQ(1, o.arg_position);
-  EXPECT_TRUE(Run("1$d", true));
-  EXPECT_EQ(1, o.arg_position);
-  EXPECT_TRUE(Run("123$d"));
-  EXPECT_EQ(123, o.arg_position);
-  EXPECT_TRUE(Run("123$d", true));
-  EXPECT_EQ(123, o.arg_position);
-  EXPECT_TRUE(Run("10$d"));
-  EXPECT_EQ(10, o.arg_position);
-  EXPECT_TRUE(Run("10$d", true));
-  EXPECT_EQ(10, o.arg_position);
-
-  // Position can't be zero.
-  EXPECT_FALSE(Run("0$d"));
-  EXPECT_FALSE(Run("0$d", true));
-  EXPECT_FALSE(Run("1$*0$d"));
-  EXPECT_FALSE(Run("1$.*0$d"));
-
-  // Position can't start with a zero digit at all. That is not a 'decimal'.
-  EXPECT_FALSE(Run("01$p"));
-  EXPECT_FALSE(Run("01$p", true));
-  EXPECT_FALSE(Run("1$*01$p"));
-  EXPECT_FALSE(Run("1$.*01$p"));
-}
-
-TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) {
-  EXPECT_TRUE(Run("14d"));
-  EXPECT_EQ('d', FormatConversionCharToChar(o.conv));
-  EXPECT_FALSE(o.width.is_from_arg());
-  EXPECT_EQ(14, o.width.value());
-  EXPECT_FALSE(o.precision.is_from_arg());
-  EXPECT_LT(o.precision.value(), 0);
-
-  EXPECT_TRUE(Run("14.d"));
-  EXPECT_FALSE(o.width.is_from_arg());
-  EXPECT_FALSE(o.precision.is_from_arg());
-  EXPECT_EQ(14, o.width.value());
-  EXPECT_EQ(0, o.precision.value());
-
-  EXPECT_TRUE(Run(".d"));
-  EXPECT_FALSE(o.width.is_from_arg());
-  EXPECT_LT(o.width.value(), 0);
-  EXPECT_FALSE(o.precision.is_from_arg());
-  EXPECT_EQ(0, o.precision.value());
-
-  EXPECT_TRUE(Run(".5d"));
-  EXPECT_FALSE(o.width.is_from_arg());
-  EXPECT_LT(o.width.value(), 0);
-  EXPECT_FALSE(o.precision.is_from_arg());
-  EXPECT_EQ(5, o.precision.value());
-
-  EXPECT_TRUE(Run(".0d"));
-  EXPECT_FALSE(o.width.is_from_arg());
-  EXPECT_LT(o.width.value(), 0);
-  EXPECT_FALSE(o.precision.is_from_arg());
-  EXPECT_EQ(0, o.precision.value());
-
-  EXPECT_TRUE(Run("14.5d"));
-  EXPECT_FALSE(o.width.is_from_arg());
-  EXPECT_FALSE(o.precision.is_from_arg());
-  EXPECT_EQ(14, o.width.value());
-  EXPECT_EQ(5, o.precision.value());
-
-  EXPECT_TRUE(Run("*.*d"));
-  EXPECT_TRUE(o.width.is_from_arg());
-  EXPECT_EQ(1, o.width.get_from_arg());
-  EXPECT_TRUE(o.precision.is_from_arg());
-  EXPECT_EQ(2, o.precision.get_from_arg());
-  EXPECT_EQ(3, o.arg_position);
-
-  EXPECT_TRUE(Run("*d"));
-  EXPECT_TRUE(o.width.is_from_arg());
-  EXPECT_EQ(1, o.width.get_from_arg());
-  EXPECT_FALSE(o.precision.is_from_arg());
-  EXPECT_LT(o.precision.value(), 0);
-  EXPECT_EQ(2, o.arg_position);
-
-  EXPECT_TRUE(Run(".*d"));
-  EXPECT_FALSE(o.width.is_from_arg());
-  EXPECT_LT(o.width.value(), 0);
-  EXPECT_TRUE(o.precision.is_from_arg());
-  EXPECT_EQ(1, o.precision.get_from_arg());
-  EXPECT_EQ(2, o.arg_position);
-
-  // mixed implicit and explicit: didn't specify arg position.
-  EXPECT_FALSE(Run("*23$.*34$d"));
-
-  EXPECT_TRUE(Run("12$*23$.*34$d"));
-  EXPECT_EQ(12, o.arg_position);
-  EXPECT_TRUE(o.width.is_from_arg());
-  EXPECT_EQ(23, o.width.get_from_arg());
-  EXPECT_TRUE(o.precision.is_from_arg());
-  EXPECT_EQ(34, o.precision.get_from_arg());
-
-  EXPECT_TRUE(Run("2$*5$.*9$d"));
-  EXPECT_EQ(2, o.arg_position);
-  EXPECT_TRUE(o.width.is_from_arg());
-  EXPECT_EQ(5, o.width.get_from_arg());
-  EXPECT_TRUE(o.precision.is_from_arg());
-  EXPECT_EQ(9, o.precision.get_from_arg());
-
-  EXPECT_FALSE(Run(".*0$d")) << "no arg 0";
-
-  // Large values
-  EXPECT_TRUE(Run("999999999.999999999d"));
-  EXPECT_FALSE(o.width.is_from_arg());
-  EXPECT_EQ(999999999, o.width.value());
-  EXPECT_FALSE(o.precision.is_from_arg());
-  EXPECT_EQ(999999999, o.precision.value());
-
-  EXPECT_FALSE(Run("1000000000.999999999d"));
-  EXPECT_FALSE(Run("999999999.1000000000d"));
-  EXPECT_FALSE(Run("9999999999d"));
-  EXPECT_FALSE(Run(".9999999999d"));
-}
-
-TEST_F(ConsumeUnboundConversionTest, Flags) {
-  static const char kAllFlags[] = "-+ #0";
-  static const int kNumFlags = ABSL_ARRAYSIZE(kAllFlags) - 1;
-  for (int rev = 0; rev < 2; ++rev) {
-    for (int i = 0; i < 1 << kNumFlags; ++i) {
-      std::string fmt;
-      for (int k = 0; k < kNumFlags; ++k)
-        if ((i >> k) & 1) fmt += kAllFlags[k];
-      // flag order shouldn't matter
-      if (rev == 1) { std::reverse(fmt.begin(), fmt.end()); }
-      fmt += 'd';
-      SCOPED_TRACE(fmt);
-      EXPECT_TRUE(Run(fmt.c_str()));
-      EXPECT_EQ(fmt.find('-') == std::string::npos, !o.flags.left);
-      EXPECT_EQ(fmt.find('+') == std::string::npos, !o.flags.show_pos);
-      EXPECT_EQ(fmt.find(' ') == std::string::npos, !o.flags.sign_col);
-      EXPECT_EQ(fmt.find('#') == std::string::npos, !o.flags.alt);
-      EXPECT_EQ(fmt.find('0') == std::string::npos, !o.flags.zero);
-    }
-  }
-}
-
-TEST_F(ConsumeUnboundConversionTest, BasicFlag) {
-  // Flag is on
-  for (const char* fmt : {"d", "llx", "G", "1$X"}) {
-    SCOPED_TRACE(fmt);
-    EXPECT_TRUE(Run(fmt));
-    EXPECT_TRUE(o.flags.basic);
-  }
-
-  // Flag is off
-  for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) {
-    SCOPED_TRACE(fmt);
-    EXPECT_TRUE(Run(fmt));
-    EXPECT_FALSE(o.flags.basic);
-  }
-}
-
-TEST_F(ConsumeUnboundConversionTest, LengthMod) {
-  EXPECT_TRUE(Run("d"));
-  EXPECT_EQ(LengthMod::none, o.length_mod);
-  EXPECT_TRUE(Run("hd"));
-  EXPECT_EQ(LengthMod::h, o.length_mod);
-  EXPECT_TRUE(Run("hhd"));
-  EXPECT_EQ(LengthMod::hh, o.length_mod);
-  EXPECT_TRUE(Run("ld"));
-  EXPECT_EQ(LengthMod::l, o.length_mod);
-  EXPECT_TRUE(Run("lld"));
-  EXPECT_EQ(LengthMod::ll, o.length_mod);
-  EXPECT_TRUE(Run("Lf"));
-  EXPECT_EQ(LengthMod::L, o.length_mod);
-  EXPECT_TRUE(Run("qf"));
-  EXPECT_EQ(LengthMod::q, o.length_mod);
-  EXPECT_TRUE(Run("jd"));
-  EXPECT_EQ(LengthMod::j, o.length_mod);
-  EXPECT_TRUE(Run("zd"));
-  EXPECT_EQ(LengthMod::z, o.length_mod);
-  EXPECT_TRUE(Run("td"));
-  EXPECT_EQ(LengthMod::t, o.length_mod);
-}
-
-struct SummarizeConsumer {
-  std::string* out;
-  explicit SummarizeConsumer(std::string* out) : out(out) {}
-
-  bool Append(string_view s) {
-    *out += "[" + std::string(s) + "]";
-    return true;
-  }
-
-  bool ConvertOne(const UnboundConversion& conv, string_view s) {
-    *out += "{";
-    *out += std::string(s);
-    *out += ":";
-    *out += std::to_string(conv.arg_position) + "$";
-    if (conv.width.is_from_arg()) {
-      *out += std::to_string(conv.width.get_from_arg()) + "$*";
-    }
-    if (conv.precision.is_from_arg()) {
-      *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
-    }
-    *out += FormatConversionCharToChar(conv.conv);
-    *out += "}";
-    return true;
-  }
-};
-
-std::string SummarizeParsedFormat(const ParsedFormatBase& pc) {
-  std::string out;
-  if (!pc.ProcessFormat(SummarizeConsumer(&out))) out += "!";
-  return out;
-}
-
-class ParsedFormatTest : public testing::Test {};
-
-TEST_F(ParsedFormatTest, ValueSemantics) {
-  ParsedFormatBase p1({}, true, {});  // empty format
-  EXPECT_EQ("", SummarizeParsedFormat(p1));
-
-  ParsedFormatBase p2 = p1;  // copy construct (empty)
-  EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
-
-  p1 = ParsedFormatBase("hello%s", true,
-                        {FormatConversionCharSetInternal::s});  // move assign
-  EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p1));
-
-  ParsedFormatBase p3 = p1;  // copy construct (nonempty)
-  EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p3));
-
-  using std::swap;
-  swap(p1, p2);
-  EXPECT_EQ("", SummarizeParsedFormat(p1));
-  EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p2));
-  swap(p1, p2);  // undo
-
-  p2 = p1;  // copy assign
-  EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2));
-}
-
-struct ExpectParse {
-  const char* in;
-  std::initializer_list<FormatConversionCharSet> conv_set;
-  const char* out;
-};
-
-TEST_F(ParsedFormatTest, Parsing) {
-  // Parse should be equivalent to that obtained by ConversionParseIterator.
-  // No need to retest the parsing edge cases here.
-  const ExpectParse kExpect[] = {
-      {"", {}, ""},
-      {"ab", {}, "[ab]"},
-      {"a%d", {FormatConversionCharSetInternal::d}, "[a]{d:1$d}"},
-      {"a%+d", {FormatConversionCharSetInternal::d}, "[a]{+d:1$d}"},
-      {"a% d", {FormatConversionCharSetInternal::d}, "[a]{ d:1$d}"},
-      {"a%b %d", {}, "[a]!"},  // stop after error
-  };
-  for (const auto& e : kExpect) {
-    SCOPED_TRACE(e.in);
-    EXPECT_EQ(e.out,
-              SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
-  }
-}
-
-TEST_F(ParsedFormatTest, ParsingFlagOrder) {
-  const ExpectParse kExpect[] = {
-      {"a%+ 0d", {FormatConversionCharSetInternal::d}, "[a]{+ 0d:1$d}"},
-      {"a%+0 d", {FormatConversionCharSetInternal::d}, "[a]{+0 d:1$d}"},
-      {"a%0+ d", {FormatConversionCharSetInternal::d}, "[a]{0+ d:1$d}"},
-      {"a% +0d", {FormatConversionCharSetInternal::d}, "[a]{ +0d:1$d}"},
-      {"a%0 +d", {FormatConversionCharSetInternal::d}, "[a]{0 +d:1$d}"},
-      {"a% 0+d", {FormatConversionCharSetInternal::d}, "[a]{ 0+d:1$d}"},
-      {"a%+   0+d", {FormatConversionCharSetInternal::d}, "[a]{+   0+d:1$d}"},
-  };
-  for (const auto& e : kExpect) {
-    SCOPED_TRACE(e.in);
-    EXPECT_EQ(e.out,
-              SummarizeParsedFormat(ParsedFormatBase(e.in, false, e.conv_set)));
-  }
-}
-
-}  // namespace
-}  // namespace str_format_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_join_internal.h b/third_party/abseil_cpp/absl/strings/internal/str_join_internal.h
deleted file mode 100644
index 31dbf672f0..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_join_internal.h
+++ /dev/null
@@ -1,314 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// This file declares INTERNAL parts of the Join API that are inlined/templated
-// or otherwise need to be available at compile time. The main abstractions
-// defined in this file are:
-//
-//   - A handful of default Formatters
-//   - JoinAlgorithm() overloads
-//   - JoinRange() overloads
-//   - JoinTuple()
-//
-// DO NOT INCLUDE THIS FILE DIRECTLY. Use this file by including
-// absl/strings/str_join.h
-//
-// IWYU pragma: private, include "absl/strings/str_join.h"
-
-#ifndef ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
-#define ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
-
-#include <cstring>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "absl/strings/internal/ostringstream.h"
-#include "absl/strings/internal/resize_uninitialized.h"
-#include "absl/strings/str_cat.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-//
-// Formatter objects
-//
-// The following are implementation classes for standard Formatter objects. The
-// factory functions that users will call to create and use these formatters are
-// defined and documented in strings/join.h.
-//
-
-// The default formatter. Converts alpha-numeric types to strings.
-struct AlphaNumFormatterImpl {
-  // This template is needed in order to support passing in a dereferenced
-  // vector<bool>::iterator
-  template <typename T>
-  void operator()(std::string* out, const T& t) const {
-    StrAppend(out, AlphaNum(t));
-  }
-
-  void operator()(std::string* out, const AlphaNum& t) const {
-    StrAppend(out, t);
-  }
-};
-
-// A type that's used to overload the JoinAlgorithm() function (defined below)
-// for ranges that do not require additional formatting (e.g., a range of
-// strings).
-
-struct NoFormatter : public AlphaNumFormatterImpl {};
-
-// Formats types to strings using the << operator.
-class StreamFormatterImpl {
- public:
-  // The method isn't const because it mutates state. Making it const will
-  // render StreamFormatterImpl thread-hostile.
-  template <typename T>
-  void operator()(std::string* out, const T& t) {
-    // The stream is created lazily to avoid paying the relatively high cost
-    // of its construction when joining an empty range.
-    if (strm_) {
-      strm_->clear();  // clear the bad, fail and eof bits in case they were set
-      strm_->str(out);
-    } else {
-      strm_.reset(new strings_internal::OStringStream(out));
-    }
-    *strm_ << t;
-  }
-
- private:
-  std::unique_ptr<strings_internal::OStringStream> strm_;
-};
-
-// Formats a std::pair<>. The 'first' member is formatted using f1_ and the
-// 'second' member is formatted using f2_. sep_ is the separator.
-template <typename F1, typename F2>
-class PairFormatterImpl {
- public:
-  PairFormatterImpl(F1 f1, absl::string_view sep, F2 f2)
-      : f1_(std::move(f1)), sep_(sep), f2_(std::move(f2)) {}
-
-  template <typename T>
-  void operator()(std::string* out, const T& p) {
-    f1_(out, p.first);
-    out->append(sep_);
-    f2_(out, p.second);
-  }
-
-  template <typename T>
-  void operator()(std::string* out, const T& p) const {
-    f1_(out, p.first);
-    out->append(sep_);
-    f2_(out, p.second);
-  }
-
- private:
-  F1 f1_;
-  std::string sep_;
-  F2 f2_;
-};
-
-// Wraps another formatter and dereferences the argument to operator() then
-// passes the dereferenced argument to the wrapped formatter. This can be
-// useful, for example, to join a std::vector<int*>.
-template <typename Formatter>
-class DereferenceFormatterImpl {
- public:
-  DereferenceFormatterImpl() : f_() {}
-  explicit DereferenceFormatterImpl(Formatter&& f)
-      : f_(std::forward<Formatter>(f)) {}
-
-  template <typename T>
-  void operator()(std::string* out, const T& t) {
-    f_(out, *t);
-  }
-
-  template <typename T>
-  void operator()(std::string* out, const T& t) const {
-    f_(out, *t);
-  }
-
- private:
-  Formatter f_;
-};
-
-// DefaultFormatter<T> is a traits class that selects a default Formatter to use
-// for the given type T. The ::Type member names the Formatter to use. This is
-// used by the strings::Join() functions that do NOT take a Formatter argument,
-// in which case a default Formatter must be chosen.
-//
-// AlphaNumFormatterImpl is the default in the base template, followed by
-// specializations for other types.
-template <typename ValueType>
-struct DefaultFormatter {
-  typedef AlphaNumFormatterImpl Type;
-};
-template <>
-struct DefaultFormatter<const char*> {
-  typedef AlphaNumFormatterImpl Type;
-};
-template <>
-struct DefaultFormatter<char*> {
-  typedef AlphaNumFormatterImpl Type;
-};
-template <>
-struct DefaultFormatter<std::string> {
-  typedef NoFormatter Type;
-};
-template <>
-struct DefaultFormatter<absl::string_view> {
-  typedef NoFormatter Type;
-};
-template <typename ValueType>
-struct DefaultFormatter<ValueType*> {
-  typedef DereferenceFormatterImpl<typename DefaultFormatter<ValueType>::Type>
-      Type;
-};
-
-template <typename ValueType>
-struct DefaultFormatter<std::unique_ptr<ValueType>>
-    : public DefaultFormatter<ValueType*> {};
-
-//
-// JoinAlgorithm() functions
-//
-
-// The main joining algorithm. This simply joins the elements in the given
-// iterator range, each separated by the given separator, into an output string,
-// and formats each element using the provided Formatter object.
-template <typename Iterator, typename Formatter>
-std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
-                          Formatter&& f) {
-  std::string result;
-  absl::string_view sep("");
-  for (Iterator it = start; it != end; ++it) {
-    result.append(sep.data(), sep.size());
-    f(&result, *it);
-    sep = s;
-  }
-  return result;
-}
-
-// A joining algorithm that's optimized for a forward iterator range of
-// string-like objects that do not need any additional formatting. This is to
-// optimize the common case of joining, say, a std::vector<string> or a
-// std::vector<absl::string_view>.
-//
-// This is an overload of the previous JoinAlgorithm() function. Here the
-// Formatter argument is of type NoFormatter. Since NoFormatter is an internal
-// type, this overload is only invoked when strings::Join() is called with a
-// range of string-like objects (e.g., std::string, absl::string_view), and an
-// explicit Formatter argument was NOT specified.
-//
-// The optimization is that the needed space will be reserved in the output
-// string to avoid the need to resize while appending. To do this, the iterator
-// range will be traversed twice: once to calculate the total needed size, and
-// then again to copy the elements and delimiters to the output string.
-template <typename Iterator,
-          typename = typename std::enable_if<std::is_convertible<
-              typename std::iterator_traits<Iterator>::iterator_category,
-              std::forward_iterator_tag>::value>::type>
-std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
-                          NoFormatter) {
-  std::string result;
-  if (start != end) {
-    // Sums size
-    size_t result_size = start->size();
-    for (Iterator it = start; ++it != end;) {
-      result_size += s.size();
-      result_size += it->size();
-    }
-
-    if (result_size > 0) {
-      STLStringResizeUninitialized(&result, result_size);
-
-      // Joins strings
-      char* result_buf = &*result.begin();
-      memcpy(result_buf, start->data(), start->size());
-      result_buf += start->size();
-      for (Iterator it = start; ++it != end;) {
-        memcpy(result_buf, s.data(), s.size());
-        result_buf += s.size();
-        memcpy(result_buf, it->data(), it->size());
-        result_buf += it->size();
-      }
-    }
-  }
-
-  return result;
-}
-
-// JoinTupleLoop implements a loop over the elements of a std::tuple, which
-// are heterogeneous. The primary template matches the tuple interior case. It
-// continues the iteration after appending a separator (for nonzero indices)
-// and formatting an element of the tuple. The specialization for the I=N case
-// matches the end-of-tuple, and terminates the iteration.
-template <size_t I, size_t N>
-struct JoinTupleLoop {
-  template <typename Tup, typename Formatter>
-  void operator()(std::string* out, const Tup& tup, absl::string_view sep,
-                  Formatter&& fmt) {
-    if (I > 0) out->append(sep.data(), sep.size());
-    fmt(out, std::get<I>(tup));
-    JoinTupleLoop<I + 1, N>()(out, tup, sep, fmt);
-  }
-};
-template <size_t N>
-struct JoinTupleLoop<N, N> {
-  template <typename Tup, typename Formatter>
-  void operator()(std::string*, const Tup&, absl::string_view, Formatter&&) {}
-};
-
-template <typename... T, typename Formatter>
-std::string JoinAlgorithm(const std::tuple<T...>& tup, absl::string_view sep,
-                          Formatter&& fmt) {
-  std::string result;
-  JoinTupleLoop<0, sizeof...(T)>()(&result, tup, sep, fmt);
-  return result;
-}
-
-template <typename Iterator>
-std::string JoinRange(Iterator first, Iterator last,
-                      absl::string_view separator) {
-  // No formatter was explicitly given, so a default must be chosen.
-  typedef typename std::iterator_traits<Iterator>::value_type ValueType;
-  typedef typename DefaultFormatter<ValueType>::Type Formatter;
-  return JoinAlgorithm(first, last, separator, Formatter());
-}
-
-template <typename Range, typename Formatter>
-std::string JoinRange(const Range& range, absl::string_view separator,
-                      Formatter&& fmt) {
-  using std::begin;
-  using std::end;
-  return JoinAlgorithm(begin(range), end(range), separator, fmt);
-}
-
-template <typename Range>
-std::string JoinRange(const Range& range, absl::string_view separator) {
-  using std::begin;
-  using std::end;
-  return JoinRange(begin(range), end(range), separator);
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/str_split_internal.h b/third_party/abseil_cpp/absl/strings/internal/str_split_internal.h
deleted file mode 100644
index a2f41c1531..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/str_split_internal.h
+++ /dev/null
@@ -1,430 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// This file declares INTERNAL parts of the Split API that are inline/templated
-// or otherwise need to be available at compile time. The main abstractions
-// defined in here are
-//
-//   - ConvertibleToStringView
-//   - SplitIterator<>
-//   - Splitter<>
-//
-// DO NOT INCLUDE THIS FILE DIRECTLY. Use this file by including
-// absl/strings/str_split.h.
-//
-// IWYU pragma: private, include "absl/strings/str_split.h"
-
-#ifndef ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_
-#define ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_
-
-#include <array>
-#include <initializer_list>
-#include <iterator>
-#include <map>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-
-#ifdef _GLIBCXX_DEBUG
-#include "absl/strings/internal/stl_type_traits.h"
-#endif  // _GLIBCXX_DEBUG
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-// This class is implicitly constructible from everything that absl::string_view
-// is implicitly constructible from, except for rvalue strings.  This means it
-// can be used as a function parameter in places where passing a temporary
-// string might cause memory lifetime issues.
-class ConvertibleToStringView {
- public:
-  ConvertibleToStringView(const char* s)  // NOLINT(runtime/explicit)
-      : value_(s) {}
-  ConvertibleToStringView(char* s) : value_(s) {}  // NOLINT(runtime/explicit)
-  ConvertibleToStringView(absl::string_view s)     // NOLINT(runtime/explicit)
-      : value_(s) {}
-  ConvertibleToStringView(const std::string& s)  // NOLINT(runtime/explicit)
-      : value_(s) {}
-
-  // Matches rvalue strings and moves their data to a member.
-  ConvertibleToStringView(std::string&& s) = delete;
-  ConvertibleToStringView(const std::string&& s) = delete;
-
-  absl::string_view value() const { return value_; }
-
- private:
-  absl::string_view value_;
-};
-
-// An iterator that enumerates the parts of a string from a Splitter. The text
-// to be split, the Delimiter, and the Predicate are all taken from the given
-// Splitter object. Iterators may only be compared if they refer to the same
-// Splitter instance.
-//
-// This class is NOT part of the public splitting API.
-template <typename Splitter>
-class SplitIterator {
- public:
-  using iterator_category = std::input_iterator_tag;
-  using value_type = absl::string_view;
-  using difference_type = ptrdiff_t;
-  using pointer = const value_type*;
-  using reference = const value_type&;
-
-  enum State { kInitState, kLastState, kEndState };
-  SplitIterator(State state, const Splitter* splitter)
-      : pos_(0),
-        state_(state),
-        splitter_(splitter),
-        delimiter_(splitter->delimiter()),
-        predicate_(splitter->predicate()) {
-    // Hack to maintain backward compatibility. This one block makes it so an
-    // empty absl::string_view whose .data() happens to be nullptr behaves
-    // *differently* from an otherwise empty absl::string_view whose .data() is
-    // not nullptr. This is an undesirable difference in general, but this
-    // behavior is maintained to avoid breaking existing code that happens to
-    // depend on this old behavior/bug. Perhaps it will be fixed one day. The
-    // difference in behavior is as follows:
-    //   Split(absl::string_view(""), '-');  // {""}
-    //   Split(absl::string_view(), '-');    // {}
-    if (splitter_->text().data() == nullptr) {
-      state_ = kEndState;
-      pos_ = splitter_->text().size();
-      return;
-    }
-
-    if (state_ == kEndState) {
-      pos_ = splitter_->text().size();
-    } else {
-      ++(*this);
-    }
-  }
-
-  bool at_end() const { return state_ == kEndState; }
-
-  reference operator*() const { return curr_; }
-  pointer operator->() const { return &curr_; }
-
-  SplitIterator& operator++() {
-    do {
-      if (state_ == kLastState) {
-        state_ = kEndState;
-        return *this;
-      }
-      const absl::string_view text = splitter_->text();
-      const absl::string_view d = delimiter_.Find(text, pos_);
-      if (d.data() == text.data() + text.size()) state_ = kLastState;
-      curr_ = text.substr(pos_, d.data() - (text.data() + pos_));
-      pos_ += curr_.size() + d.size();
-    } while (!predicate_(curr_));
-    return *this;
-  }
-
-  SplitIterator operator++(int) {
-    SplitIterator old(*this);
-    ++(*this);
-    return old;
-  }
-
-  friend bool operator==(const SplitIterator& a, const SplitIterator& b) {
-    return a.state_ == b.state_ && a.pos_ == b.pos_;
-  }
-
-  friend bool operator!=(const SplitIterator& a, const SplitIterator& b) {
-    return !(a == b);
-  }
-
- private:
-  size_t pos_;
-  State state_;
-  absl::string_view curr_;
-  const Splitter* splitter_;
-  typename Splitter::DelimiterType delimiter_;
-  typename Splitter::PredicateType predicate_;
-};
-
-// HasMappedType<T>::value is true iff there exists a type T::mapped_type.
-template <typename T, typename = void>
-struct HasMappedType : std::false_type {};
-template <typename T>
-struct HasMappedType<T, absl::void_t<typename T::mapped_type>>
-    : std::true_type {};
-
-// HasValueType<T>::value is true iff there exists a type T::value_type.
-template <typename T, typename = void>
-struct HasValueType : std::false_type {};
-template <typename T>
-struct HasValueType<T, absl::void_t<typename T::value_type>> : std::true_type {
-};
-
-// HasConstIterator<T>::value is true iff there exists a type T::const_iterator.
-template <typename T, typename = void>
-struct HasConstIterator : std::false_type {};
-template <typename T>
-struct HasConstIterator<T, absl::void_t<typename T::const_iterator>>
-    : std::true_type {};
-
-// IsInitializerList<T>::value is true iff T is an std::initializer_list. More
-// details below in Splitter<> where this is used.
-std::false_type IsInitializerListDispatch(...);  // default: No
-template <typename T>
-std::true_type IsInitializerListDispatch(std::initializer_list<T>*);
-template <typename T>
-struct IsInitializerList
-    : decltype(IsInitializerListDispatch(static_cast<T*>(nullptr))) {};
-
-// A SplitterIsConvertibleTo<C>::type alias exists iff the specified condition
-// is true for type 'C'.
-//
-// Restricts conversion to container-like types (by testing for the presence of
-// a const_iterator member type) and also to disable conversion to an
-// std::initializer_list (which also has a const_iterator). Otherwise, code
-// compiled in C++11 will get an error due to ambiguous conversion paths (in
-// C++11 std::vector<T>::operator= is overloaded to take either a std::vector<T>
-// or an std::initializer_list<T>).
-
-template <typename C, bool has_value_type, bool has_mapped_type>
-struct SplitterIsConvertibleToImpl : std::false_type {};
-
-template <typename C>
-struct SplitterIsConvertibleToImpl<C, true, false>
-    : std::is_constructible<typename C::value_type, absl::string_view> {};
-
-template <typename C>
-struct SplitterIsConvertibleToImpl<C, true, true>
-    : absl::conjunction<
-          std::is_constructible<typename C::key_type, absl::string_view>,
-          std::is_constructible<typename C::mapped_type, absl::string_view>> {};
-
-template <typename C>
-struct SplitterIsConvertibleTo
-    : SplitterIsConvertibleToImpl<
-          C,
-#ifdef _GLIBCXX_DEBUG
-          !IsStrictlyBaseOfAndConvertibleToSTLContainer<C>::value &&
-#endif  // _GLIBCXX_DEBUG
-              !IsInitializerList<
-                  typename std::remove_reference<C>::type>::value &&
-              HasValueType<C>::value && HasConstIterator<C>::value,
-          HasMappedType<C>::value> {
-};
-
-// This class implements the range that is returned by absl::StrSplit(). This
-// class has templated conversion operators that allow it to be implicitly
-// converted to a variety of types that the caller may have specified on the
-// left-hand side of an assignment.
-//
-// The main interface for interacting with this class is through its implicit
-// conversion operators. However, this class may also be used like a container
-// in that it has .begin() and .end() member functions. It may also be used
-// within a range-for loop.
-//
-// Output containers can be collections of any type that is constructible from
-// an absl::string_view.
-//
-// An Predicate functor may be supplied. This predicate will be used to filter
-// the split strings: only strings for which the predicate returns true will be
-// kept. A Predicate object is any unary functor that takes an absl::string_view
-// and returns bool.
-//
-// The StringType parameter can be either string_view or string, depending on
-// whether the Splitter refers to a string stored elsewhere, or if the string
-// resides inside the Splitter itself.
-template <typename Delimiter, typename Predicate, typename StringType>
-class Splitter {
- public:
-  using DelimiterType = Delimiter;
-  using PredicateType = Predicate;
-  using const_iterator = strings_internal::SplitIterator<Splitter>;
-  using value_type = typename std::iterator_traits<const_iterator>::value_type;
-
-  Splitter(StringType input_text, Delimiter d, Predicate p)
-      : text_(std::move(input_text)),
-        delimiter_(std::move(d)),
-        predicate_(std::move(p)) {}
-
-  absl::string_view text() const { return text_; }
-  const Delimiter& delimiter() const { return delimiter_; }
-  const Predicate& predicate() const { return predicate_; }
-
-  // Range functions that iterate the split substrings as absl::string_view
-  // objects. These methods enable a Splitter to be used in a range-based for
-  // loop.
-  const_iterator begin() const { return {const_iterator::kInitState, this}; }
-  const_iterator end() const { return {const_iterator::kEndState, this}; }
-
-  // An implicit conversion operator that is restricted to only those containers
-  // that the splitter is convertible to.
-  template <typename Container,
-            typename = typename std::enable_if<
-                SplitterIsConvertibleTo<Container>::value>::type>
-  operator Container() const {  // NOLINT(runtime/explicit)
-    return ConvertToContainer<Container, typename Container::value_type,
-                              HasMappedType<Container>::value>()(*this);
-  }
-
-  // Returns a pair with its .first and .second members set to the first two
-  // strings returned by the begin() iterator. Either/both of .first and .second
-  // will be constructed with empty strings if the iterator doesn't have a
-  // corresponding value.
-  template <typename First, typename Second>
-  operator std::pair<First, Second>() const {  // NOLINT(runtime/explicit)
-    absl::string_view first, second;
-    auto it = begin();
-    if (it != end()) {
-      first = *it;
-      if (++it != end()) {
-        second = *it;
-      }
-    }
-    return {First(first), Second(second)};
-  }
-
- private:
-  // ConvertToContainer is a functor converting a Splitter to the requested
-  // Container of ValueType. It is specialized below to optimize splitting to
-  // certain combinations of Container and ValueType.
-  //
-  // This base template handles the generic case of storing the split results in
-  // the requested non-map-like container and converting the split substrings to
-  // the requested type.
-  template <typename Container, typename ValueType, bool is_map = false>
-  struct ConvertToContainer {
-    Container operator()(const Splitter& splitter) const {
-      Container c;
-      auto it = std::inserter(c, c.end());
-      for (const auto& sp : splitter) {
-        *it++ = ValueType(sp);
-      }
-      return c;
-    }
-  };
-
-  // Partial specialization for a std::vector<absl::string_view>.
-  //
-  // Optimized for the common case of splitting to a
-  // std::vector<absl::string_view>. In this case we first split the results to
-  // a small array of absl::string_view on the stack, to reduce reallocations.
-  template <typename A>
-  struct ConvertToContainer<std::vector<absl::string_view, A>,
-                            absl::string_view, false> {
-    std::vector<absl::string_view, A> operator()(
-        const Splitter& splitter) const {
-      struct raw_view {
-        const char* data;
-        size_t size;
-        operator absl::string_view() const {  // NOLINT(runtime/explicit)
-          return {data, size};
-        }
-      };
-      std::vector<absl::string_view, A> v;
-      std::array<raw_view, 16> ar;
-      for (auto it = splitter.begin(); !it.at_end();) {
-        size_t index = 0;
-        do {
-          ar[index].data = it->data();
-          ar[index].size = it->size();
-          ++it;
-        } while (++index != ar.size() && !it.at_end());
-        v.insert(v.end(), ar.begin(), ar.begin() + index);
-      }
-      return v;
-    }
-  };
-
-  // Partial specialization for a std::vector<std::string>.
-  //
-  // Optimized for the common case of splitting to a std::vector<std::string>.
-  // In this case we first split the results to a std::vector<absl::string_view>
-  // so the returned std::vector<std::string> can have space reserved to avoid
-  // std::string moves.
-  template <typename A>
-  struct ConvertToContainer<std::vector<std::string, A>, std::string, false> {
-    std::vector<std::string, A> operator()(const Splitter& splitter) const {
-      const std::vector<absl::string_view> v = splitter;
-      return std::vector<std::string, A>(v.begin(), v.end());
-    }
-  };
-
-  // Partial specialization for containers of pairs (e.g., maps).
-  //
-  // The algorithm is to insert a new pair into the map for each even-numbered
-  // item, with the even-numbered item as the key with a default-constructed
-  // value. Each odd-numbered item will then be assigned to the last pair's
-  // value.
-  template <typename Container, typename First, typename Second>
-  struct ConvertToContainer<Container, std::pair<const First, Second>, true> {
-    Container operator()(const Splitter& splitter) const {
-      Container m;
-      typename Container::iterator it;
-      bool insert = true;
-      for (const auto& sp : splitter) {
-        if (insert) {
-          it = Inserter<Container>::Insert(&m, First(sp), Second());
-        } else {
-          it->second = Second(sp);
-        }
-        insert = !insert;
-      }
-      return m;
-    }
-
-    // Inserts the key and value into the given map, returning an iterator to
-    // the inserted item. Specialized for std::map and std::multimap to use
-    // emplace() and adapt emplace()'s return value.
-    template <typename Map>
-    struct Inserter {
-      using M = Map;
-      template <typename... Args>
-      static typename M::iterator Insert(M* m, Args&&... args) {
-        return m->insert(std::make_pair(std::forward<Args>(args)...)).first;
-      }
-    };
-
-    template <typename... Ts>
-    struct Inserter<std::map<Ts...>> {
-      using M = std::map<Ts...>;
-      template <typename... Args>
-      static typename M::iterator Insert(M* m, Args&&... args) {
-        return m->emplace(std::make_pair(std::forward<Args>(args)...)).first;
-      }
-    };
-
-    template <typename... Ts>
-    struct Inserter<std::multimap<Ts...>> {
-      using M = std::multimap<Ts...>;
-      template <typename... Args>
-      static typename M::iterator Insert(M* m, Args&&... args) {
-        return m->emplace(std::make_pair(std::forward<Args>(args)...));
-      }
-    };
-  };
-
-  StringType text_;
-  Delimiter delimiter_;
-  Predicate predicate_;
-};
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/string_constant.h b/third_party/abseil_cpp/absl/strings/internal/string_constant.h
deleted file mode 100644
index b15f1d9bcf..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/string_constant.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_
-#define ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_
-
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-// StringConstant<T> represents a compile time string constant.
-// It can be accessed via its `absl::string_view value` static member.
-// It is guaranteed that the `string_view` returned has constant `.data()`,
-// constant `.size()` and constant `value[i]` for all `0 <= i < .size()`
-//
-// The `T` is an opaque type. It is guaranteed that different string constants
-// will have different values of `T`. This allows users to associate the string
-// constant with other static state at compile time.
-//
-// Instances should be made using the `MakeStringConstant()` factory function
-// below.
-template <typename T>
-struct StringConstant {
- private:
-  // Returns true if `view` points to constant data.
-  // Otherwise, it can't be constant evaluated.
-  static constexpr bool ValidateConstant(absl::string_view view) {
-    return view.empty() || 2 * view[0] != 1;
-  }
-
- public:
-  static constexpr absl::string_view value = T{}();
-  constexpr absl::string_view operator()() const { return value; }
-
-  static_assert(ValidateConstant(value),
-                "The input string_view must point to constant data.");
-};
-
-template <typename T>
-constexpr absl::string_view StringConstant<T>::value;  // NOLINT
-
-// Factory function for `StringConstant` instances.
-// It supports callables that have a constexpr default constructor and a
-// constexpr operator().
-// It must return an `absl::string_view` or `const char*` pointing to constant
-// data. This is validated at compile time.
-template <typename T>
-constexpr StringConstant<T> MakeStringConstant(T) {
-  return {};
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/string_constant_test.cc b/third_party/abseil_cpp/absl/strings/internal/string_constant_test.cc
deleted file mode 100644
index 392833cf15..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/string_constant_test.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/string_constant.h"
-
-#include "absl/meta/type_traits.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-using absl::strings_internal::MakeStringConstant;
-
-struct Callable {
-  constexpr absl::string_view operator()() const {
-    return absl::string_view("Callable", 8);
-  }
-};
-
-TEST(StringConstant, Traits) {
-  constexpr auto str = MakeStringConstant(Callable{});
-  using T = decltype(str);
-
-  EXPECT_TRUE(std::is_empty<T>::value);
-  EXPECT_TRUE(std::is_trivial<T>::value);
-  EXPECT_TRUE(absl::is_trivially_default_constructible<T>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<T>::value);
-  EXPECT_TRUE(absl::is_trivially_move_constructible<T>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<T>::value);
-}
-
-TEST(StringConstant, MakeFromCallable) {
-  constexpr auto str = MakeStringConstant(Callable{});
-  using T = decltype(str);
-  EXPECT_EQ(Callable{}(), T::value);
-  EXPECT_EQ(Callable{}(), str());
-}
-
-TEST(StringConstant, MakeFromStringConstant) {
-  // We want to make sure the StringConstant itself is a valid input to the
-  // factory function.
-  constexpr auto str = MakeStringConstant(Callable{});
-  constexpr auto str2 = MakeStringConstant(str);
-  using T = decltype(str2);
-  EXPECT_EQ(Callable{}(), T::value);
-  EXPECT_EQ(Callable{}(), str2());
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/internal/utf8.cc b/third_party/abseil_cpp/absl/strings/internal/utf8.cc
deleted file mode 100644
index 8fd8edc1ec..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/utf8.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// UTF8 utilities, implemented to reduce dependencies.
-
-#include "absl/strings/internal/utf8.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) {
-  if (utf8_char <= 0x7F) {
-    *buffer = static_cast<char>(utf8_char);
-    return 1;
-  } else if (utf8_char <= 0x7FF) {
-    buffer[1] = 0x80 | (utf8_char & 0x3F);
-    utf8_char >>= 6;
-    buffer[0] = 0xC0 | utf8_char;
-    return 2;
-  } else if (utf8_char <= 0xFFFF) {
-    buffer[2] = 0x80 | (utf8_char & 0x3F);
-    utf8_char >>= 6;
-    buffer[1] = 0x80 | (utf8_char & 0x3F);
-    utf8_char >>= 6;
-    buffer[0] = 0xE0 | utf8_char;
-    return 3;
-  } else {
-    buffer[3] = 0x80 | (utf8_char & 0x3F);
-    utf8_char >>= 6;
-    buffer[2] = 0x80 | (utf8_char & 0x3F);
-    utf8_char >>= 6;
-    buffer[1] = 0x80 | (utf8_char & 0x3F);
-    utf8_char >>= 6;
-    buffer[0] = 0xF0 | utf8_char;
-    return 4;
-  }
-}
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/internal/utf8.h b/third_party/abseil_cpp/absl/strings/internal/utf8.h
deleted file mode 100644
index 32fb1093be..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/utf8.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// UTF8 utilities, implemented to reduce dependencies.
-
-#ifndef ABSL_STRINGS_INTERNAL_UTF8_H_
-#define ABSL_STRINGS_INTERNAL_UTF8_H_
-
-#include <cstddef>
-#include <cstdint>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-// For Unicode code points 0 through 0x10FFFF, EncodeUTF8Char writes
-// out the UTF-8 encoding into buffer, and returns the number of chars
-// it wrote.
-//
-// As described in https://tools.ietf.org/html/rfc3629#section-3 , the encodings
-// are:
-//    00 -     7F : 0xxxxxxx
-//    80 -    7FF : 110xxxxx 10xxxxxx
-//   800 -   FFFF : 1110xxxx 10xxxxxx 10xxxxxx
-// 10000 - 10FFFF : 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
-//
-// Values greater than 0x10FFFF are not supported and may or may not write
-// characters into buffer, however never will more than kMaxEncodedUTF8Size
-// bytes be written, regardless of the value of utf8_char.
-enum { kMaxEncodedUTF8Size = 4 };
-size_t EncodeUTF8Char(char *buffer, char32_t utf8_char);
-
-}  // namespace strings_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_INTERNAL_UTF8_H_
diff --git a/third_party/abseil_cpp/absl/strings/internal/utf8_test.cc b/third_party/abseil_cpp/absl/strings/internal/utf8_test.cc
deleted file mode 100644
index 88dd5036e3..0000000000
--- a/third_party/abseil_cpp/absl/strings/internal/utf8_test.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/internal/utf8.h"
-
-#include <cstdint>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/base/port.h"
-
-namespace {
-
-#if !defined(__cpp_char8_t)
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wc++2a-compat"
-#endif
-TEST(EncodeUTF8Char, BasicFunction) {
-  std::pair<char32_t, std::string> tests[] = {{0x0030, u8"\u0030"},
-                                              {0x00A3, u8"\u00A3"},
-                                              {0x00010000, u8"\U00010000"},
-                                              {0x0000FFFF, u8"\U0000FFFF"},
-                                              {0x0010FFFD, u8"\U0010FFFD"}};
-  for (auto &test : tests) {
-    char buf0[7] = {'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'};
-    char buf1[7] = {'\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF'};
-    char *buf0_written =
-        &buf0[absl::strings_internal::EncodeUTF8Char(buf0, test.first)];
-    char *buf1_written =
-        &buf1[absl::strings_internal::EncodeUTF8Char(buf1, test.first)];
-    int apparent_length = 7;
-    while (buf0[apparent_length - 1] == '\x00' &&
-           buf1[apparent_length - 1] == '\xFF') {
-      if (--apparent_length == 0) break;
-    }
-    EXPECT_EQ(apparent_length, buf0_written - buf0);
-    EXPECT_EQ(apparent_length, buf1_written - buf1);
-    EXPECT_EQ(apparent_length, test.second.length());
-    EXPECT_EQ(std::string(buf0, apparent_length), test.second);
-    EXPECT_EQ(std::string(buf1, apparent_length), test.second);
-  }
-  char buf[32] = "Don't Tread On Me";
-  EXPECT_LE(absl::strings_internal::EncodeUTF8Char(buf, 0x00110000),
-            absl::strings_internal::kMaxEncodedUTF8Size);
-  char buf2[32] = "Negative is invalid but sane";
-  EXPECT_LE(absl::strings_internal::EncodeUTF8Char(buf2, -1),
-            absl::strings_internal::kMaxEncodedUTF8Size);
-}
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-#endif  // !defined(__cpp_char8_t)
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/match.cc b/third_party/abseil_cpp/absl/strings/match.cc
deleted file mode 100644
index 8127cb0c5e..0000000000
--- a/third_party/abseil_cpp/absl/strings/match.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/match.h"
-
-#include "absl/strings/internal/memutil.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) {
-  return (piece1.size() == piece2.size() &&
-          0 == absl::strings_internal::memcasecmp(piece1.data(), piece2.data(),
-                                                  piece1.size()));
-  // memcasecmp uses absl::ascii_tolower().
-}
-
-bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix) {
-  return (text.size() >= prefix.size()) &&
-         EqualsIgnoreCase(text.substr(0, prefix.size()), prefix);
-}
-
-bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) {
-  return (text.size() >= suffix.size()) &&
-         EqualsIgnoreCase(text.substr(text.size() - suffix.size()), suffix);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/match.h b/third_party/abseil_cpp/absl/strings/match.h
deleted file mode 100644
index 90fca98ad2..0000000000
--- a/third_party/abseil_cpp/absl/strings/match.h
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: match.h
-// -----------------------------------------------------------------------------
-//
-// This file contains simple utilities for performing string matching checks.
-// All of these function parameters are specified as `absl::string_view`,
-// meaning that these functions can accept `std::string`, `absl::string_view` or
-// NUL-terminated C-style strings.
-//
-// Examples:
-//   std::string s = "foo";
-//   absl::string_view sv = "f";
-//   assert(absl::StrContains(s, sv));
-//
-// Note: The order of parameters in these functions is designed to mimic the
-// order an equivalent member function would exhibit;
-// e.g. `s.Contains(x)` ==> `absl::StrContains(s, x).
-#ifndef ABSL_STRINGS_MATCH_H_
-#define ABSL_STRINGS_MATCH_H_
-
-#include <cstring>
-
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// StrContains()
-//
-// Returns whether a given string `haystack` contains the substring `needle`.
-inline bool StrContains(absl::string_view haystack, absl::string_view needle) {
-  return haystack.find(needle, 0) != haystack.npos;
-}
-
-// StartsWith()
-//
-// Returns whether a given string `text` begins with `prefix`.
-inline bool StartsWith(absl::string_view text, absl::string_view prefix) {
-  return prefix.empty() ||
-         (text.size() >= prefix.size() &&
-          memcmp(text.data(), prefix.data(), prefix.size()) == 0);
-}
-
-// EndsWith()
-//
-// Returns whether a given string `text` ends with `suffix`.
-inline bool EndsWith(absl::string_view text, absl::string_view suffix) {
-  return suffix.empty() ||
-         (text.size() >= suffix.size() &&
-          memcmp(text.data() + (text.size() - suffix.size()), suffix.data(),
-                 suffix.size()) == 0);
-}
-
-// EqualsIgnoreCase()
-//
-// Returns whether given ASCII strings `piece1` and `piece2` are equal, ignoring
-// case in the comparison.
-bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2);
-
-// StartsWithIgnoreCase()
-//
-// Returns whether a given ASCII string `text` starts with `prefix`,
-// ignoring case in the comparison.
-bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix);
-
-// EndsWithIgnoreCase()
-//
-// Returns whether a given ASCII string `text` ends with `suffix`, ignoring
-// case in the comparison.
-bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_MATCH_H_
diff --git a/third_party/abseil_cpp/absl/strings/match_test.cc b/third_party/abseil_cpp/absl/strings/match_test.cc
deleted file mode 100644
index 4c313dda14..0000000000
--- a/third_party/abseil_cpp/absl/strings/match_test.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/match.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-TEST(MatchTest, StartsWith) {
-  const std::string s1("123\0abc", 7);
-  const absl::string_view a("foobar");
-  const absl::string_view b(s1);
-  const absl::string_view e;
-  EXPECT_TRUE(absl::StartsWith(a, a));
-  EXPECT_TRUE(absl::StartsWith(a, "foo"));
-  EXPECT_TRUE(absl::StartsWith(a, e));
-  EXPECT_TRUE(absl::StartsWith(b, s1));
-  EXPECT_TRUE(absl::StartsWith(b, b));
-  EXPECT_TRUE(absl::StartsWith(b, e));
-  EXPECT_TRUE(absl::StartsWith(e, ""));
-  EXPECT_FALSE(absl::StartsWith(a, b));
-  EXPECT_FALSE(absl::StartsWith(b, a));
-  EXPECT_FALSE(absl::StartsWith(e, a));
-}
-
-TEST(MatchTest, EndsWith) {
-  const std::string s1("123\0abc", 7);
-  const absl::string_view a("foobar");
-  const absl::string_view b(s1);
-  const absl::string_view e;
-  EXPECT_TRUE(absl::EndsWith(a, a));
-  EXPECT_TRUE(absl::EndsWith(a, "bar"));
-  EXPECT_TRUE(absl::EndsWith(a, e));
-  EXPECT_TRUE(absl::EndsWith(b, s1));
-  EXPECT_TRUE(absl::EndsWith(b, b));
-  EXPECT_TRUE(absl::EndsWith(b, e));
-  EXPECT_TRUE(absl::EndsWith(e, ""));
-  EXPECT_FALSE(absl::EndsWith(a, b));
-  EXPECT_FALSE(absl::EndsWith(b, a));
-  EXPECT_FALSE(absl::EndsWith(e, a));
-}
-
-TEST(MatchTest, Contains) {
-  absl::string_view a("abcdefg");
-  absl::string_view b("abcd");
-  absl::string_view c("efg");
-  absl::string_view d("gh");
-  EXPECT_TRUE(absl::StrContains(a, a));
-  EXPECT_TRUE(absl::StrContains(a, b));
-  EXPECT_TRUE(absl::StrContains(a, c));
-  EXPECT_FALSE(absl::StrContains(a, d));
-  EXPECT_TRUE(absl::StrContains("", ""));
-  EXPECT_TRUE(absl::StrContains("abc", ""));
-  EXPECT_FALSE(absl::StrContains("", "a"));
-}
-
-TEST(MatchTest, ContainsNull) {
-  const std::string s = "foo";
-  const char* cs = "foo";
-  const absl::string_view sv("foo");
-  const absl::string_view sv2("foo\0bar", 4);
-  EXPECT_EQ(s, "foo");
-  EXPECT_EQ(sv, "foo");
-  EXPECT_NE(sv2, "foo");
-  EXPECT_TRUE(absl::EndsWith(s, sv));
-  EXPECT_TRUE(absl::StartsWith(cs, sv));
-  EXPECT_TRUE(absl::StrContains(cs, sv));
-  EXPECT_FALSE(absl::StrContains(cs, sv2));
-}
-
-TEST(MatchTest, EqualsIgnoreCase) {
-  std::string text = "the";
-  absl::string_view data(text);
-
-  EXPECT_TRUE(absl::EqualsIgnoreCase(data, "The"));
-  EXPECT_TRUE(absl::EqualsIgnoreCase(data, "THE"));
-  EXPECT_TRUE(absl::EqualsIgnoreCase(data, "the"));
-  EXPECT_FALSE(absl::EqualsIgnoreCase(data, "Quick"));
-  EXPECT_FALSE(absl::EqualsIgnoreCase(data, "then"));
-}
-
-TEST(MatchTest, StartsWithIgnoreCase) {
-  EXPECT_TRUE(absl::StartsWithIgnoreCase("foo", "foo"));
-  EXPECT_TRUE(absl::StartsWithIgnoreCase("foo", "Fo"));
-  EXPECT_TRUE(absl::StartsWithIgnoreCase("foo", ""));
-  EXPECT_FALSE(absl::StartsWithIgnoreCase("foo", "fooo"));
-  EXPECT_FALSE(absl::StartsWithIgnoreCase("", "fo"));
-}
-
-TEST(MatchTest, EndsWithIgnoreCase) {
-  EXPECT_TRUE(absl::EndsWithIgnoreCase("foo", "foo"));
-  EXPECT_TRUE(absl::EndsWithIgnoreCase("foo", "Oo"));
-  EXPECT_TRUE(absl::EndsWithIgnoreCase("foo", ""));
-  EXPECT_FALSE(absl::EndsWithIgnoreCase("foo", "fooo"));
-  EXPECT_FALSE(absl::EndsWithIgnoreCase("", "fo"));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/numbers.cc b/third_party/abseil_cpp/absl/strings/numbers.cc
deleted file mode 100644
index 3da1059c90..0000000000
--- a/third_party/abseil_cpp/absl/strings/numbers.cc
+++ /dev/null
@@ -1,1083 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file contains string processing functions related to
-// numeric values.
-
-#include "absl/strings/numbers.h"
-
-#include <algorithm>
-#include <cassert>
-#include <cfloat>  // for DBL_DIG and FLT_DIG
-#include <cmath>   // for HUGE_VAL
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <utility>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/bits.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/charconv.h"
-#include "absl/strings/escaping.h"
-#include "absl/strings/internal/memutil.h"
-#include "absl/strings/match.h"
-#include "absl/strings/str_cat.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-bool SimpleAtof(absl::string_view str, float* out) {
-  *out = 0.0;
-  str = StripAsciiWhitespace(str);
-  if (!str.empty() && str[0] == '+') {
-    str.remove_prefix(1);
-  }
-  auto result = absl::from_chars(str.data(), str.data() + str.size(), *out);
-  if (result.ec == std::errc::invalid_argument) {
-    return false;
-  }
-  if (result.ptr != str.data() + str.size()) {
-    // not all non-whitespace characters consumed
-    return false;
-  }
-  // from_chars() with DR 3081's current wording will return max() on
-  // overflow.  SimpleAtof returns infinity instead.
-  if (result.ec == std::errc::result_out_of_range) {
-    if (*out > 1.0) {
-      *out = std::numeric_limits<float>::infinity();
-    } else if (*out < -1.0) {
-      *out = -std::numeric_limits<float>::infinity();
-    }
-  }
-  return true;
-}
-
-bool SimpleAtod(absl::string_view str, double* out) {
-  *out = 0.0;
-  str = StripAsciiWhitespace(str);
-  if (!str.empty() && str[0] == '+') {
-    str.remove_prefix(1);
-  }
-  auto result = absl::from_chars(str.data(), str.data() + str.size(), *out);
-  if (result.ec == std::errc::invalid_argument) {
-    return false;
-  }
-  if (result.ptr != str.data() + str.size()) {
-    // not all non-whitespace characters consumed
-    return false;
-  }
-  // from_chars() with DR 3081's current wording will return max() on
-  // overflow.  SimpleAtod returns infinity instead.
-  if (result.ec == std::errc::result_out_of_range) {
-    if (*out > 1.0) {
-      *out = std::numeric_limits<double>::infinity();
-    } else if (*out < -1.0) {
-      *out = -std::numeric_limits<double>::infinity();
-    }
-  }
-  return true;
-}
-
-bool SimpleAtob(absl::string_view str, bool* out) {
-  ABSL_RAW_CHECK(out != nullptr, "Output pointer must not be nullptr.");
-  if (EqualsIgnoreCase(str, "true") || EqualsIgnoreCase(str, "t") ||
-      EqualsIgnoreCase(str, "yes") || EqualsIgnoreCase(str, "y") ||
-      EqualsIgnoreCase(str, "1")) {
-    *out = true;
-    return true;
-  }
-  if (EqualsIgnoreCase(str, "false") || EqualsIgnoreCase(str, "f") ||
-      EqualsIgnoreCase(str, "no") || EqualsIgnoreCase(str, "n") ||
-      EqualsIgnoreCase(str, "0")) {
-    *out = false;
-    return true;
-  }
-  return false;
-}
-
-// ----------------------------------------------------------------------
-// FastIntToBuffer() overloads
-//
-// Like the Fast*ToBuffer() functions above, these are intended for speed.
-// Unlike the Fast*ToBuffer() functions, however, these functions write
-// their output to the beginning of the buffer.  The caller is responsible
-// for ensuring that the buffer has enough space to hold the output.
-//
-// Returns a pointer to the end of the string (i.e. the null character
-// terminating the string).
-// ----------------------------------------------------------------------
-
-namespace {
-
-// Used to optimize printing a decimal number's final digit.
-const char one_ASCII_final_digits[10][2] {
-  {'0', 0}, {'1', 0}, {'2', 0}, {'3', 0}, {'4', 0},
-  {'5', 0}, {'6', 0}, {'7', 0}, {'8', 0}, {'9', 0},
-};
-
-}  // namespace
-
-char* numbers_internal::FastIntToBuffer(uint32_t i, char* buffer) {
-  uint32_t digits;
-  // The idea of this implementation is to trim the number of divides to as few
-  // as possible, and also reducing memory stores and branches, by going in
-  // steps of two digits at a time rather than one whenever possible.
-  // The huge-number case is first, in the hopes that the compiler will output
-  // that case in one branch-free block of code, and only output conditional
-  // branches into it from below.
-  if (i >= 1000000000) {     // >= 1,000,000,000
-    digits = i / 100000000;  //      100,000,000
-    i -= digits * 100000000;
-    PutTwoDigits(digits, buffer);
-    buffer += 2;
-  lt100_000_000:
-    digits = i / 1000000;  // 1,000,000
-    i -= digits * 1000000;
-    PutTwoDigits(digits, buffer);
-    buffer += 2;
-  lt1_000_000:
-    digits = i / 10000;  // 10,000
-    i -= digits * 10000;
-    PutTwoDigits(digits, buffer);
-    buffer += 2;
-  lt10_000:
-    digits = i / 100;
-    i -= digits * 100;
-    PutTwoDigits(digits, buffer);
-    buffer += 2;
- lt100:
-    digits = i;
-    PutTwoDigits(digits, buffer);
-    buffer += 2;
-    *buffer = 0;
-    return buffer;
-  }
-
-  if (i < 100) {
-    digits = i;
-    if (i >= 10) goto lt100;
-    memcpy(buffer, one_ASCII_final_digits[i], 2);
-    return buffer + 1;
-  }
-  if (i < 10000) {  //    10,000
-    if (i >= 1000) goto lt10_000;
-    digits = i / 100;
-    i -= digits * 100;
-    *buffer++ = '0' + digits;
-    goto lt100;
-  }
-  if (i < 1000000) {  //    1,000,000
-    if (i >= 100000) goto lt1_000_000;
-    digits = i / 10000;  //    10,000
-    i -= digits * 10000;
-    *buffer++ = '0' + digits;
-    goto lt10_000;
-  }
-  if (i < 100000000) {  //    100,000,000
-    if (i >= 10000000) goto lt100_000_000;
-    digits = i / 1000000;  //   1,000,000
-    i -= digits * 1000000;
-    *buffer++ = '0' + digits;
-    goto lt1_000_000;
-  }
-  // we already know that i < 1,000,000,000
-  digits = i / 100000000;  //   100,000,000
-  i -= digits * 100000000;
-  *buffer++ = '0' + digits;
-  goto lt100_000_000;
-}
-
-char* numbers_internal::FastIntToBuffer(int32_t i, char* buffer) {
-  uint32_t u = i;
-  if (i < 0) {
-    *buffer++ = '-';
-    // We need to do the negation in modular (i.e., "unsigned")
-    // arithmetic; MSVC++ apprently warns for plain "-u", so
-    // we write the equivalent expression "0 - u" instead.
-    u = 0 - u;
-  }
-  return numbers_internal::FastIntToBuffer(u, buffer);
-}
-
-char* numbers_internal::FastIntToBuffer(uint64_t i, char* buffer) {
-  uint32_t u32 = static_cast<uint32_t>(i);
-  if (u32 == i) return numbers_internal::FastIntToBuffer(u32, buffer);
-
-  // Here we know i has at least 10 decimal digits.
-  uint64_t top_1to11 = i / 1000000000;
-  u32 = static_cast<uint32_t>(i - top_1to11 * 1000000000);
-  uint32_t top_1to11_32 = static_cast<uint32_t>(top_1to11);
-
-  if (top_1to11_32 == top_1to11) {
-    buffer = numbers_internal::FastIntToBuffer(top_1to11_32, buffer);
-  } else {
-    // top_1to11 has more than 32 bits too; print it in two steps.
-    uint32_t top_8to9 = static_cast<uint32_t>(top_1to11 / 100);
-    uint32_t mid_2 = static_cast<uint32_t>(top_1to11 - top_8to9 * 100);
-    buffer = numbers_internal::FastIntToBuffer(top_8to9, buffer);
-    PutTwoDigits(mid_2, buffer);
-    buffer += 2;
-  }
-
-  // We have only 9 digits now, again the maximum uint32_t can handle fully.
-  uint32_t digits = u32 / 10000000;  // 10,000,000
-  u32 -= digits * 10000000;
-  PutTwoDigits(digits, buffer);
-  buffer += 2;
-  digits = u32 / 100000;  // 100,000
-  u32 -= digits * 100000;
-  PutTwoDigits(digits, buffer);
-  buffer += 2;
-  digits = u32 / 1000;  // 1,000
-  u32 -= digits * 1000;
-  PutTwoDigits(digits, buffer);
-  buffer += 2;
-  digits = u32 / 10;
-  u32 -= digits * 10;
-  PutTwoDigits(digits, buffer);
-  buffer += 2;
-  memcpy(buffer, one_ASCII_final_digits[u32], 2);
-  return buffer + 1;
-}
-
-char* numbers_internal::FastIntToBuffer(int64_t i, char* buffer) {
-  uint64_t u = i;
-  if (i < 0) {
-    *buffer++ = '-';
-    u = 0 - u;
-  }
-  return numbers_internal::FastIntToBuffer(u, buffer);
-}
-
-// Given a 128-bit number expressed as a pair of uint64_t, high half first,
-// return that number multiplied by the given 32-bit value.  If the result is
-// too large to fit in a 128-bit number, divide it by 2 until it fits.
-static std::pair<uint64_t, uint64_t> Mul32(std::pair<uint64_t, uint64_t> num,
-                                           uint32_t mul) {
-  uint64_t bits0_31 = num.second & 0xFFFFFFFF;
-  uint64_t bits32_63 = num.second >> 32;
-  uint64_t bits64_95 = num.first & 0xFFFFFFFF;
-  uint64_t bits96_127 = num.first >> 32;
-
-  // The picture so far: each of these 64-bit values has only the lower 32 bits
-  // filled in.
-  // bits96_127:          [ 00000000 xxxxxxxx ]
-  // bits64_95:                    [ 00000000 xxxxxxxx ]
-  // bits32_63:                             [ 00000000 xxxxxxxx ]
-  // bits0_31:                                       [ 00000000 xxxxxxxx ]
-
-  bits0_31 *= mul;
-  bits32_63 *= mul;
-  bits64_95 *= mul;
-  bits96_127 *= mul;
-
-  // Now the top halves may also have value, though all 64 of their bits will
-  // never be set at the same time, since they are a result of a 32x32 bit
-  // multiply.  This makes the carry calculation slightly easier.
-  // bits96_127:          [ mmmmmmmm | mmmmmmmm ]
-  // bits64_95:                    [ | mmmmmmmm mmmmmmmm | ]
-  // bits32_63:                      |        [ mmmmmmmm | mmmmmmmm ]
-  // bits0_31:                       |                 [ | mmmmmmmm mmmmmmmm ]
-  // eventually:        [ bits128_up | ...bits64_127.... | ..bits0_63... ]
-
-  uint64_t bits0_63 = bits0_31 + (bits32_63 << 32);
-  uint64_t bits64_127 = bits64_95 + (bits96_127 << 32) + (bits32_63 >> 32) +
-                        (bits0_63 < bits0_31);
-  uint64_t bits128_up = (bits96_127 >> 32) + (bits64_127 < bits64_95);
-  if (bits128_up == 0) return {bits64_127, bits0_63};
-
-  int shift = 64 - base_internal::CountLeadingZeros64(bits128_up);
-  uint64_t lo = (bits0_63 >> shift) + (bits64_127 << (64 - shift));
-  uint64_t hi = (bits64_127 >> shift) + (bits128_up << (64 - shift));
-  return {hi, lo};
-}
-
-// Compute num * 5 ^ expfive, and return the first 128 bits of the result,
-// where the first bit is always a one.  So PowFive(1, 0) starts 0b100000,
-// PowFive(1, 1) starts 0b101000, PowFive(1, 2) starts 0b110010, etc.
-static std::pair<uint64_t, uint64_t> PowFive(uint64_t num, int expfive) {
-  std::pair<uint64_t, uint64_t> result = {num, 0};
-  while (expfive >= 13) {
-    // 5^13 is the highest power of five that will fit in a 32-bit integer.
-    result = Mul32(result, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5);
-    expfive -= 13;
-  }
-  constexpr int powers_of_five[13] = {
-      1,
-      5,
-      5 * 5,
-      5 * 5 * 5,
-      5 * 5 * 5 * 5,
-      5 * 5 * 5 * 5 * 5,
-      5 * 5 * 5 * 5 * 5 * 5,
-      5 * 5 * 5 * 5 * 5 * 5 * 5,
-      5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-      5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-      5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-      5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-      5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5};
-  result = Mul32(result, powers_of_five[expfive & 15]);
-  int shift = base_internal::CountLeadingZeros64(result.first);
-  if (shift != 0) {
-    result.first = (result.first << shift) + (result.second >> (64 - shift));
-    result.second = (result.second << shift);
-  }
-  return result;
-}
-
-struct ExpDigits {
-  int32_t exponent;
-  char digits[6];
-};
-
-// SplitToSix converts value, a positive double-precision floating-point number,
-// into a base-10 exponent and 6 ASCII digits, where the first digit is never
-// zero.  For example, SplitToSix(1) returns an exponent of zero and a digits
-// array of {'1', '0', '0', '0', '0', '0'}.  If value is exactly halfway between
-// two possible representations, e.g. value = 100000.5, then "round to even" is
-// performed.
-static ExpDigits SplitToSix(const double value) {
-  ExpDigits exp_dig;
-  int exp = 5;
-  double d = value;
-  // First step: calculate a close approximation of the output, where the
-  // value d will be between 100,000 and 999,999, representing the digits
-  // in the output ASCII array, and exp is the base-10 exponent.  It would be
-  // faster to use a table here, and to look up the base-2 exponent of value,
-  // however value is an IEEE-754 64-bit number, so the table would have 2,000
-  // entries, which is not cache-friendly.
-  if (d >= 999999.5) {
-    if (d >= 1e+261) exp += 256, d *= 1e-256;
-    if (d >= 1e+133) exp += 128, d *= 1e-128;
-    if (d >= 1e+69) exp += 64, d *= 1e-64;
-    if (d >= 1e+37) exp += 32, d *= 1e-32;
-    if (d >= 1e+21) exp += 16, d *= 1e-16;
-    if (d >= 1e+13) exp += 8, d *= 1e-8;
-    if (d >= 1e+9) exp += 4, d *= 1e-4;
-    if (d >= 1e+7) exp += 2, d *= 1e-2;
-    if (d >= 1e+6) exp += 1, d *= 1e-1;
-  } else {
-    if (d < 1e-250) exp -= 256, d *= 1e256;
-    if (d < 1e-122) exp -= 128, d *= 1e128;
-    if (d < 1e-58) exp -= 64, d *= 1e64;
-    if (d < 1e-26) exp -= 32, d *= 1e32;
-    if (d < 1e-10) exp -= 16, d *= 1e16;
-    if (d < 1e-2) exp -= 8, d *= 1e8;
-    if (d < 1e+2) exp -= 4, d *= 1e4;
-    if (d < 1e+4) exp -= 2, d *= 1e2;
-    if (d < 1e+5) exp -= 1, d *= 1e1;
-  }
-  // At this point, d is in the range [99999.5..999999.5) and exp is in the
-  // range [-324..308]. Since we need to round d up, we want to add a half
-  // and truncate.
-  // However, the technique above may have lost some precision, due to its
-  // repeated multiplication by constants that each may be off by half a bit
-  // of precision.  This only matters if we're close to the edge though.
-  // Since we'd like to know if the fractional part of d is close to a half,
-  // we multiply it by 65536 and see if the fractional part is close to 32768.
-  // (The number doesn't have to be a power of two,but powers of two are faster)
-  uint64_t d64k = d * 65536;
-  int dddddd;  // A 6-digit decimal integer.
-  if ((d64k % 65536) == 32767 || (d64k % 65536) == 32768) {
-    // OK, it's fairly likely that precision was lost above, which is
-    // not a surprise given only 52 mantissa bits are available.  Therefore
-    // redo the calculation using 128-bit numbers.  (64 bits are not enough).
-
-    // Start out with digits rounded down; maybe add one below.
-    dddddd = static_cast<int>(d64k / 65536);
-
-    // mantissa is a 64-bit integer representing M.mmm... * 2^63.  The actual
-    // value we're representing, of course, is M.mmm... * 2^exp2.
-    int exp2;
-    double m = std::frexp(value, &exp2);
-    uint64_t mantissa = m * (32768.0 * 65536.0 * 65536.0 * 65536.0);
-    // std::frexp returns an m value in the range [0.5, 1.0), however we
-    // can't multiply it by 2^64 and convert to an integer because some FPUs
-    // throw an exception when converting an number higher than 2^63 into an
-    // integer - even an unsigned 64-bit integer!  Fortunately it doesn't matter
-    // since m only has 52 significant bits anyway.
-    mantissa <<= 1;
-    exp2 -= 64;  // not needed, but nice for debugging
-
-    // OK, we are here to compare:
-    //     (dddddd + 0.5) * 10^(exp-5)  vs.  mantissa * 2^exp2
-    // so we can round up dddddd if appropriate.  Those values span the full
-    // range of 600 orders of magnitude of IEE 64-bit floating-point.
-    // Fortunately, we already know they are very close, so we don't need to
-    // track the base-2 exponent of both sides.  This greatly simplifies the
-    // the math since the 2^exp2 calculation is unnecessary and the power-of-10
-    // calculation can become a power-of-5 instead.
-
-    std::pair<uint64_t, uint64_t> edge, val;
-    if (exp >= 6) {
-      // Compare (dddddd + 0.5) * 5 ^ (exp - 5) to mantissa
-      // Since we're tossing powers of two, 2 * dddddd + 1 is the
-      // same as dddddd + 0.5
-      edge = PowFive(2 * dddddd + 1, exp - 5);
-
-      val.first = mantissa;
-      val.second = 0;
-    } else {
-      // We can't compare (dddddd + 0.5) * 5 ^ (exp - 5) to mantissa as we did
-      // above because (exp - 5) is negative.  So we compare (dddddd + 0.5) to
-      // mantissa * 5 ^ (5 - exp)
-      edge = PowFive(2 * dddddd + 1, 0);
-
-      val = PowFive(mantissa, 5 - exp);
-    }
-    // printf("exp=%d %016lx %016lx vs %016lx %016lx\n", exp, val.first,
-    //        val.second, edge.first, edge.second);
-    if (val > edge) {
-      dddddd++;
-    } else if (val == edge) {
-      dddddd += (dddddd & 1);
-    }
-  } else {
-    // Here, we are not close to the edge.
-    dddddd = static_cast<int>((d64k + 32768) / 65536);
-  }
-  if (dddddd == 1000000) {
-    dddddd = 100000;
-    exp += 1;
-  }
-  exp_dig.exponent = exp;
-
-  int two_digits = dddddd / 10000;
-  dddddd -= two_digits * 10000;
-  numbers_internal::PutTwoDigits(two_digits, &exp_dig.digits[0]);
-
-  two_digits = dddddd / 100;
-  dddddd -= two_digits * 100;
-  numbers_internal::PutTwoDigits(two_digits, &exp_dig.digits[2]);
-
-  numbers_internal::PutTwoDigits(dddddd, &exp_dig.digits[4]);
-  return exp_dig;
-}
-
-// Helper function for fast formatting of floating-point.
-// The result is the same as "%g", a.k.a. "%.6g".
-size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) {
-  static_assert(std::numeric_limits<float>::is_iec559,
-                "IEEE-754/IEC-559 support only");
-
-  char* out = buffer;  // we write data to out, incrementing as we go, but
-                       // FloatToBuffer always returns the address of the buffer
-                       // passed in.
-
-  if (std::isnan(d)) {
-    strcpy(out, "nan");  // NOLINT(runtime/printf)
-    return 3;
-  }
-  if (d == 0) {  // +0 and -0 are handled here
-    if (std::signbit(d)) *out++ = '-';
-    *out++ = '0';
-    *out = 0;
-    return out - buffer;
-  }
-  if (d < 0) {
-    *out++ = '-';
-    d = -d;
-  }
-  if (std::isinf(d)) {
-    strcpy(out, "inf");  // NOLINT(runtime/printf)
-    return out + 3 - buffer;
-  }
-
-  auto exp_dig = SplitToSix(d);
-  int exp = exp_dig.exponent;
-  const char* digits = exp_dig.digits;
-  out[0] = '0';
-  out[1] = '.';
-  switch (exp) {
-    case 5:
-      memcpy(out, &digits[0], 6), out += 6;
-      *out = 0;
-      return out - buffer;
-    case 4:
-      memcpy(out, &digits[0], 5), out += 5;
-      if (digits[5] != '0') {
-        *out++ = '.';
-        *out++ = digits[5];
-      }
-      *out = 0;
-      return out - buffer;
-    case 3:
-      memcpy(out, &digits[0], 4), out += 4;
-      if ((digits[5] | digits[4]) != '0') {
-        *out++ = '.';
-        *out++ = digits[4];
-        if (digits[5] != '0') *out++ = digits[5];
-      }
-      *out = 0;
-      return out - buffer;
-    case 2:
-      memcpy(out, &digits[0], 3), out += 3;
-      *out++ = '.';
-      memcpy(out, &digits[3], 3);
-      out += 3;
-      while (out[-1] == '0') --out;
-      if (out[-1] == '.') --out;
-      *out = 0;
-      return out - buffer;
-    case 1:
-      memcpy(out, &digits[0], 2), out += 2;
-      *out++ = '.';
-      memcpy(out, &digits[2], 4);
-      out += 4;
-      while (out[-1] == '0') --out;
-      if (out[-1] == '.') --out;
-      *out = 0;
-      return out - buffer;
-    case 0:
-      memcpy(out, &digits[0], 1), out += 1;
-      *out++ = '.';
-      memcpy(out, &digits[1], 5);
-      out += 5;
-      while (out[-1] == '0') --out;
-      if (out[-1] == '.') --out;
-      *out = 0;
-      return out - buffer;
-    case -4:
-      out[2] = '0';
-      ++out;
-      ABSL_FALLTHROUGH_INTENDED;
-    case -3:
-      out[2] = '0';
-      ++out;
-      ABSL_FALLTHROUGH_INTENDED;
-    case -2:
-      out[2] = '0';
-      ++out;
-      ABSL_FALLTHROUGH_INTENDED;
-    case -1:
-      out += 2;
-      memcpy(out, &digits[0], 6);
-      out += 6;
-      while (out[-1] == '0') --out;
-      *out = 0;
-      return out - buffer;
-  }
-  assert(exp < -4 || exp >= 6);
-  out[0] = digits[0];
-  assert(out[1] == '.');
-  out += 2;
-  memcpy(out, &digits[1], 5), out += 5;
-  while (out[-1] == '0') --out;
-  if (out[-1] == '.') --out;
-  *out++ = 'e';
-  if (exp > 0) {
-    *out++ = '+';
-  } else {
-    *out++ = '-';
-    exp = -exp;
-  }
-  if (exp > 99) {
-    int dig1 = exp / 100;
-    exp -= dig1 * 100;
-    *out++ = '0' + dig1;
-  }
-  PutTwoDigits(exp, out);
-  out += 2;
-  *out = 0;
-  return out - buffer;
-}
-
-namespace {
-// Represents integer values of digits.
-// Uses 36 to indicate an invalid character since we support
-// bases up to 36.
-static const int8_t kAsciiToInt[256] = {
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,  // 16 36s.
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 0,  1,  2,  3,  4,  5,
-    6,  7,  8,  9,  36, 36, 36, 36, 36, 36, 36, 10, 11, 12, 13, 14, 15, 16, 17,
-    18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
-    36, 36, 36, 36, 36, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
-    24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36,
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
-    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36};
-
-// Parse the sign and optional hex or oct prefix in text.
-inline bool safe_parse_sign_and_base(absl::string_view* text /*inout*/,
-                                     int* base_ptr /*inout*/,
-                                     bool* negative_ptr /*output*/) {
-  if (text->data() == nullptr) {
-    return false;
-  }
-
-  const char* start = text->data();
-  const char* end = start + text->size();
-  int base = *base_ptr;
-
-  // Consume whitespace.
-  while (start < end && absl::ascii_isspace(start[0])) {
-    ++start;
-  }
-  while (start < end && absl::ascii_isspace(end[-1])) {
-    --end;
-  }
-  if (start >= end) {
-    return false;
-  }
-
-  // Consume sign.
-  *negative_ptr = (start[0] == '-');
-  if (*negative_ptr || start[0] == '+') {
-    ++start;
-    if (start >= end) {
-      return false;
-    }
-  }
-
-  // Consume base-dependent prefix.
-  //  base 0: "0x" -> base 16, "0" -> base 8, default -> base 10
-  //  base 16: "0x" -> base 16
-  // Also validate the base.
-  if (base == 0) {
-    if (end - start >= 2 && start[0] == '0' &&
-        (start[1] == 'x' || start[1] == 'X')) {
-      base = 16;
-      start += 2;
-      if (start >= end) {
-        // "0x" with no digits after is invalid.
-        return false;
-      }
-    } else if (end - start >= 1 && start[0] == '0') {
-      base = 8;
-      start += 1;
-    } else {
-      base = 10;
-    }
-  } else if (base == 16) {
-    if (end - start >= 2 && start[0] == '0' &&
-        (start[1] == 'x' || start[1] == 'X')) {
-      start += 2;
-      if (start >= end) {
-        // "0x" with no digits after is invalid.
-        return false;
-      }
-    }
-  } else if (base >= 2 && base <= 36) {
-    // okay
-  } else {
-    return false;
-  }
-  *text = absl::string_view(start, end - start);
-  *base_ptr = base;
-  return true;
-}
-
-// Consume digits.
-//
-// The classic loop:
-//
-//   for each digit
-//     value = value * base + digit
-//   value *= sign
-//
-// The classic loop needs overflow checking.  It also fails on the most
-// negative integer, -2147483648 in 32-bit two's complement representation.
-//
-// My improved loop:
-//
-//  if (!negative)
-//    for each digit
-//      value = value * base
-//      value = value + digit
-//  else
-//    for each digit
-//      value = value * base
-//      value = value - digit
-//
-// Overflow checking becomes simple.
-
-// Lookup tables per IntType:
-// vmax/base and vmin/base are precomputed because division costs at least 8ns.
-// TODO(junyer): Doing this per base instead (i.e. an array of structs, not a
-// struct of arrays) would probably be better in terms of d-cache for the most
-// commonly used bases.
-template <typename IntType>
-struct LookupTables {
-  ABSL_CONST_INIT static const IntType kVmaxOverBase[];
-  ABSL_CONST_INIT static const IntType kVminOverBase[];
-};
-
-// An array initializer macro for X/base where base in [0, 36].
-// However, note that lookups for base in [0, 1] should never happen because
-// base has been validated to be in [2, 36] by safe_parse_sign_and_base().
-#define X_OVER_BASE_INITIALIZER(X)                                        \
-  {                                                                       \
-    0, 0, X / 2, X / 3, X / 4, X / 5, X / 6, X / 7, X / 8, X / 9, X / 10, \
-        X / 11, X / 12, X / 13, X / 14, X / 15, X / 16, X / 17, X / 18,   \
-        X / 19, X / 20, X / 21, X / 22, X / 23, X / 24, X / 25, X / 26,   \
-        X / 27, X / 28, X / 29, X / 30, X / 31, X / 32, X / 33, X / 34,   \
-        X / 35, X / 36,                                                   \
-  }
-
-// This kVmaxOverBase is generated with
-//  for (int base = 2; base < 37; ++base) {
-//    absl::uint128 max = std::numeric_limits<absl::uint128>::max();
-//    auto result = max / base;
-//    std::cout << "    MakeUint128(" << absl::Uint128High64(result) << "u, "
-//              << absl::Uint128Low64(result) << "u),\n";
-//  }
-// See https://godbolt.org/z/aneYsb
-//
-// uint128& operator/=(uint128) is not constexpr, so hardcode the resulting
-// array to avoid a static initializer.
-template<>
-const uint128 LookupTables<uint128>::kVmaxOverBase[] = {
-    0,
-    0,
-    MakeUint128(9223372036854775807u, 18446744073709551615u),
-    MakeUint128(6148914691236517205u, 6148914691236517205u),
-    MakeUint128(4611686018427387903u, 18446744073709551615u),
-    MakeUint128(3689348814741910323u, 3689348814741910323u),
-    MakeUint128(3074457345618258602u, 12297829382473034410u),
-    MakeUint128(2635249153387078802u, 5270498306774157604u),
-    MakeUint128(2305843009213693951u, 18446744073709551615u),
-    MakeUint128(2049638230412172401u, 14347467612885206812u),
-    MakeUint128(1844674407370955161u, 11068046444225730969u),
-    MakeUint128(1676976733973595601u, 8384883669867978007u),
-    MakeUint128(1537228672809129301u, 6148914691236517205u),
-    MakeUint128(1418980313362273201u, 4256940940086819603u),
-    MakeUint128(1317624576693539401u, 2635249153387078802u),
-    MakeUint128(1229782938247303441u, 1229782938247303441u),
-    MakeUint128(1152921504606846975u, 18446744073709551615u),
-    MakeUint128(1085102592571150095u, 1085102592571150095u),
-    MakeUint128(1024819115206086200u, 16397105843297379214u),
-    MakeUint128(970881267037344821u, 16504981539634861972u),
-    MakeUint128(922337203685477580u, 14757395258967641292u),
-    MakeUint128(878416384462359600u, 14054662151397753612u),
-    MakeUint128(838488366986797800u, 13415813871788764811u),
-    MakeUint128(802032351030850070u, 4812194106185100421u),
-    MakeUint128(768614336404564650u, 12297829382473034410u),
-    MakeUint128(737869762948382064u, 11805916207174113034u),
-    MakeUint128(709490156681136600u, 11351842506898185609u),
-    MakeUint128(683212743470724133u, 17080318586768103348u),
-    MakeUint128(658812288346769700u, 10540996613548315209u),
-    MakeUint128(636094623231363848u, 15266270957552732371u),
-    MakeUint128(614891469123651720u, 9838263505978427528u),
-    MakeUint128(595056260442243600u, 9520900167075897608u),
-    MakeUint128(576460752303423487u, 18446744073709551615u),
-    MakeUint128(558992244657865200u, 8943875914525843207u),
-    MakeUint128(542551296285575047u, 9765923333140350855u),
-    MakeUint128(527049830677415760u, 8432797290838652167u),
-    MakeUint128(512409557603043100u, 8198552921648689607u),
-};
-
-// This kVmaxOverBase generated with
-//   for (int base = 2; base < 37; ++base) {
-//    absl::int128 max = std::numeric_limits<absl::int128>::max();
-//    auto result = max / base;
-//    std::cout << "\tMakeInt128(" << absl::Int128High64(result) << ", "
-//              << absl::Int128Low64(result) << "u),\n";
-//  }
-// See https://godbolt.org/z/7djYWz
-//
-// int128& operator/=(int128) is not constexpr, so hardcode the resulting array
-// to avoid a static initializer.
-template<>
-const int128 LookupTables<int128>::kVmaxOverBase[] = {
-    0,
-    0,
-    MakeInt128(4611686018427387903, 18446744073709551615u),
-    MakeInt128(3074457345618258602, 12297829382473034410u),
-    MakeInt128(2305843009213693951, 18446744073709551615u),
-    MakeInt128(1844674407370955161, 11068046444225730969u),
-    MakeInt128(1537228672809129301, 6148914691236517205u),
-    MakeInt128(1317624576693539401, 2635249153387078802u),
-    MakeInt128(1152921504606846975, 18446744073709551615u),
-    MakeInt128(1024819115206086200, 16397105843297379214u),
-    MakeInt128(922337203685477580, 14757395258967641292u),
-    MakeInt128(838488366986797800, 13415813871788764811u),
-    MakeInt128(768614336404564650, 12297829382473034410u),
-    MakeInt128(709490156681136600, 11351842506898185609u),
-    MakeInt128(658812288346769700, 10540996613548315209u),
-    MakeInt128(614891469123651720, 9838263505978427528u),
-    MakeInt128(576460752303423487, 18446744073709551615u),
-    MakeInt128(542551296285575047, 9765923333140350855u),
-    MakeInt128(512409557603043100, 8198552921648689607u),
-    MakeInt128(485440633518672410, 17475862806672206794u),
-    MakeInt128(461168601842738790, 7378697629483820646u),
-    MakeInt128(439208192231179800, 7027331075698876806u),
-    MakeInt128(419244183493398900, 6707906935894382405u),
-    MakeInt128(401016175515425035, 2406097053092550210u),
-    MakeInt128(384307168202282325, 6148914691236517205u),
-    MakeInt128(368934881474191032, 5902958103587056517u),
-    MakeInt128(354745078340568300, 5675921253449092804u),
-    MakeInt128(341606371735362066, 17763531330238827482u),
-    MakeInt128(329406144173384850, 5270498306774157604u),
-    MakeInt128(318047311615681924, 7633135478776366185u),
-    MakeInt128(307445734561825860, 4919131752989213764u),
-    MakeInt128(297528130221121800, 4760450083537948804u),
-    MakeInt128(288230376151711743, 18446744073709551615u),
-    MakeInt128(279496122328932600, 4471937957262921603u),
-    MakeInt128(271275648142787523, 14106333703424951235u),
-    MakeInt128(263524915338707880, 4216398645419326083u),
-    MakeInt128(256204778801521550, 4099276460824344803u),
-};
-
-// This kVminOverBase generated with
-//  for (int base = 2; base < 37; ++base) {
-//    absl::int128 min = std::numeric_limits<absl::int128>::min();
-//    auto result = min / base;
-//    std::cout << "\tMakeInt128(" << absl::Int128High64(result) << ", "
-//              << absl::Int128Low64(result) << "u),\n";
-//  }
-//
-// See https://godbolt.org/z/7djYWz
-//
-// int128& operator/=(int128) is not constexpr, so hardcode the resulting array
-// to avoid a static initializer.
-template<>
-const int128 LookupTables<int128>::kVminOverBase[] = {
-    0,
-    0,
-    MakeInt128(-4611686018427387904, 0u),
-    MakeInt128(-3074457345618258603, 6148914691236517206u),
-    MakeInt128(-2305843009213693952, 0u),
-    MakeInt128(-1844674407370955162, 7378697629483820647u),
-    MakeInt128(-1537228672809129302, 12297829382473034411u),
-    MakeInt128(-1317624576693539402, 15811494920322472814u),
-    MakeInt128(-1152921504606846976, 0u),
-    MakeInt128(-1024819115206086201, 2049638230412172402u),
-    MakeInt128(-922337203685477581, 3689348814741910324u),
-    MakeInt128(-838488366986797801, 5030930201920786805u),
-    MakeInt128(-768614336404564651, 6148914691236517206u),
-    MakeInt128(-709490156681136601, 7094901566811366007u),
-    MakeInt128(-658812288346769701, 7905747460161236407u),
-    MakeInt128(-614891469123651721, 8608480567731124088u),
-    MakeInt128(-576460752303423488, 0u),
-    MakeInt128(-542551296285575048, 8680820740569200761u),
-    MakeInt128(-512409557603043101, 10248191152060862009u),
-    MakeInt128(-485440633518672411, 970881267037344822u),
-    MakeInt128(-461168601842738791, 11068046444225730970u),
-    MakeInt128(-439208192231179801, 11419412998010674810u),
-    MakeInt128(-419244183493398901, 11738837137815169211u),
-    MakeInt128(-401016175515425036, 16040647020617001406u),
-    MakeInt128(-384307168202282326, 12297829382473034411u),
-    MakeInt128(-368934881474191033, 12543785970122495099u),
-    MakeInt128(-354745078340568301, 12770822820260458812u),
-    MakeInt128(-341606371735362067, 683212743470724134u),
-    MakeInt128(-329406144173384851, 13176245766935394012u),
-    MakeInt128(-318047311615681925, 10813608594933185431u),
-    MakeInt128(-307445734561825861, 13527612320720337852u),
-    MakeInt128(-297528130221121801, 13686293990171602812u),
-    MakeInt128(-288230376151711744, 0u),
-    MakeInt128(-279496122328932601, 13974806116446630013u),
-    MakeInt128(-271275648142787524, 4340410370284600381u),
-    MakeInt128(-263524915338707881, 14230345428290225533u),
-    MakeInt128(-256204778801521551, 14347467612885206813u),
-};
-
-template <typename IntType>
-const IntType LookupTables<IntType>::kVmaxOverBase[] =
-    X_OVER_BASE_INITIALIZER(std::numeric_limits<IntType>::max());
-
-template <typename IntType>
-const IntType LookupTables<IntType>::kVminOverBase[] =
-    X_OVER_BASE_INITIALIZER(std::numeric_limits<IntType>::min());
-
-#undef X_OVER_BASE_INITIALIZER
-
-template <typename IntType>
-inline bool safe_parse_positive_int(absl::string_view text, int base,
-                                    IntType* value_p) {
-  IntType value = 0;
-  const IntType vmax = std::numeric_limits<IntType>::max();
-  assert(vmax > 0);
-  assert(base >= 0);
-  assert(vmax >= static_cast<IntType>(base));
-  const IntType vmax_over_base = LookupTables<IntType>::kVmaxOverBase[base];
-  assert(base < 2 ||
-         std::numeric_limits<IntType>::max() / base == vmax_over_base);
-  const char* start = text.data();
-  const char* end = start + text.size();
-  // loop over digits
-  for (; start < end; ++start) {
-    unsigned char c = static_cast<unsigned char>(start[0]);
-    int digit = kAsciiToInt[c];
-    if (digit >= base) {
-      *value_p = value;
-      return false;
-    }
-    if (value > vmax_over_base) {
-      *value_p = vmax;
-      return false;
-    }
-    value *= base;
-    if (value > vmax - digit) {
-      *value_p = vmax;
-      return false;
-    }
-    value += digit;
-  }
-  *value_p = value;
-  return true;
-}
-
-template <typename IntType>
-inline bool safe_parse_negative_int(absl::string_view text, int base,
-                                    IntType* value_p) {
-  IntType value = 0;
-  const IntType vmin = std::numeric_limits<IntType>::min();
-  assert(vmin < 0);
-  assert(vmin <= 0 - base);
-  IntType vmin_over_base = LookupTables<IntType>::kVminOverBase[base];
-  assert(base < 2 ||
-         std::numeric_limits<IntType>::min() / base == vmin_over_base);
-  // 2003 c++ standard [expr.mul]
-  // "... the sign of the remainder is implementation-defined."
-  // Although (vmin/base)*base + vmin%base is always vmin.
-  // 2011 c++ standard tightens the spec but we cannot rely on it.
-  // TODO(junyer): Handle this in the lookup table generation.
-  if (vmin % base > 0) {
-    vmin_over_base += 1;
-  }
-  const char* start = text.data();
-  const char* end = start + text.size();
-  // loop over digits
-  for (; start < end; ++start) {
-    unsigned char c = static_cast<unsigned char>(start[0]);
-    int digit = kAsciiToInt[c];
-    if (digit >= base) {
-      *value_p = value;
-      return false;
-    }
-    if (value < vmin_over_base) {
-      *value_p = vmin;
-      return false;
-    }
-    value *= base;
-    if (value < vmin + digit) {
-      *value_p = vmin;
-      return false;
-    }
-    value -= digit;
-  }
-  *value_p = value;
-  return true;
-}
-
-// Input format based on POSIX.1-2008 strtol
-// http://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html
-template <typename IntType>
-inline bool safe_int_internal(absl::string_view text, IntType* value_p,
-                              int base) {
-  *value_p = 0;
-  bool negative;
-  if (!safe_parse_sign_and_base(&text, &base, &negative)) {
-    return false;
-  }
-  if (!negative) {
-    return safe_parse_positive_int(text, base, value_p);
-  } else {
-    return safe_parse_negative_int(text, base, value_p);
-  }
-}
-
-template <typename IntType>
-inline bool safe_uint_internal(absl::string_view text, IntType* value_p,
-                               int base) {
-  *value_p = 0;
-  bool negative;
-  if (!safe_parse_sign_and_base(&text, &base, &negative) || negative) {
-    return false;
-  }
-  return safe_parse_positive_int(text, base, value_p);
-}
-}  // anonymous namespace
-
-namespace numbers_internal {
-
-// Digit conversion.
-ABSL_CONST_INIT ABSL_DLL const char kHexChar[] =
-    "0123456789abcdef";
-
-ABSL_CONST_INIT ABSL_DLL const char kHexTable[513] =
-    "000102030405060708090a0b0c0d0e0f"
-    "101112131415161718191a1b1c1d1e1f"
-    "202122232425262728292a2b2c2d2e2f"
-    "303132333435363738393a3b3c3d3e3f"
-    "404142434445464748494a4b4c4d4e4f"
-    "505152535455565758595a5b5c5d5e5f"
-    "606162636465666768696a6b6c6d6e6f"
-    "707172737475767778797a7b7c7d7e7f"
-    "808182838485868788898a8b8c8d8e8f"
-    "909192939495969798999a9b9c9d9e9f"
-    "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
-    "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
-    "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
-    "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
-    "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
-    "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
-
-ABSL_CONST_INIT ABSL_DLL const char two_ASCII_digits[100][2] = {
-    {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
-    {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
-    {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
-    {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
-    {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
-    {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
-    {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
-    {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
-    {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
-    {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
-    {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
-    {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
-    {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
-    {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
-    {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
-    {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
-    {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
-
-bool safe_strto32_base(absl::string_view text, int32_t* value, int base) {
-  return safe_int_internal<int32_t>(text, value, base);
-}
-
-bool safe_strto64_base(absl::string_view text, int64_t* value, int base) {
-  return safe_int_internal<int64_t>(text, value, base);
-}
-
-bool safe_strto128_base(absl::string_view text, int128* value, int base) {
-  return safe_int_internal<absl::int128>(text, value, base);
-}
-
-bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base) {
-  return safe_uint_internal<uint32_t>(text, value, base);
-}
-
-bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) {
-  return safe_uint_internal<uint64_t>(text, value, base);
-}
-
-bool safe_strtou128_base(absl::string_view text, uint128* value, int base) {
-  return safe_uint_internal<absl::uint128>(text, value, base);
-}
-
-}  // namespace numbers_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/numbers.h b/third_party/abseil_cpp/absl/strings/numbers.h
deleted file mode 100644
index 2e004b44f8..0000000000
--- a/third_party/abseil_cpp/absl/strings/numbers.h
+++ /dev/null
@@ -1,273 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: numbers.h
-// -----------------------------------------------------------------------------
-//
-// This package contains functions for converting strings to numbers. For
-// converting numbers to strings, use `StrCat()` or `StrAppend()` in str_cat.h,
-// which automatically detect and convert most number values appropriately.
-
-#ifndef ABSL_STRINGS_NUMBERS_H_
-#define ABSL_STRINGS_NUMBERS_H_
-
-#ifdef __SSE4_2__
-#include <x86intrin.h>
-#endif
-
-#include <cstddef>
-#include <cstdlib>
-#include <cstring>
-#include <ctime>
-#include <limits>
-#include <string>
-#include <type_traits>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/bits.h"
-#ifdef __SSE4_2__
-// TODO(jorg): Remove this when we figure out the right way
-// to swap bytes on SSE 4.2 that works with the compilers
-// we claim to support.  Also, add tests for the compiler
-// that doesn't support the Intel _bswap64 intrinsic but
-// does support all the SSE 4.2 intrinsics
-#include "absl/base/internal/endian.h"
-#endif
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/numeric/int128.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// SimpleAtoi()
-//
-// Converts the given string (optionally followed or preceded by ASCII
-// whitespace) into an integer value, returning `true` if successful. The string
-// must reflect a base-10 integer whose value falls within the range of the
-// integer type (optionally preceded by a `+` or `-`). If any errors are
-// encountered, this function returns `false`, leaving `out` in an unspecified
-// state.
-template <typename int_type>
-ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out);
-
-// SimpleAtof()
-//
-// Converts the given string (optionally followed or preceded by ASCII
-// whitespace) into a float, which may be rounded on overflow or underflow,
-// returning `true` if successful.
-// See https://en.cppreference.com/w/c/string/byte/strtof for details about the
-// allowed formats for `str`, except SimpleAtof() is locale-independent and will
-// always use the "C" locale. If any errors are encountered, this function
-// returns `false`, leaving `out` in an unspecified state.
-ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out);
-
-// SimpleAtod()
-//
-// Converts the given string (optionally followed or preceded by ASCII
-// whitespace) into a double, which may be rounded on overflow or underflow,
-// returning `true` if successful.
-// See https://en.cppreference.com/w/c/string/byte/strtof for details about the
-// allowed formats for `str`, except SimpleAtod is locale-independent and will
-// always use the "C" locale. If any errors are encountered, this function
-// returns `false`, leaving `out` in an unspecified state.
-ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out);
-
-// SimpleAtob()
-//
-// Converts the given string into a boolean, returning `true` if successful.
-// The following case-insensitive strings are interpreted as boolean `true`:
-// "true", "t", "yes", "y", "1". The following case-insensitive strings
-// are interpreted as boolean `false`: "false", "f", "no", "n", "0". If any
-// errors are encountered, this function returns `false`, leaving `out` in an
-// unspecified state.
-ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// End of public API.  Implementation details follow.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace numbers_internal {
-
-// Digit conversion.
-ABSL_DLL extern const char kHexChar[17];  // 0123456789abcdef
-ABSL_DLL extern const char
-    kHexTable[513];  // 000102030405060708090a0b0c0d0e0f1011...
-ABSL_DLL extern const char
-    two_ASCII_digits[100][2];  // 00, 01, 02, 03...
-
-// Writes a two-character representation of 'i' to 'buf'. 'i' must be in the
-// range 0 <= i < 100, and buf must have space for two characters. Example:
-//   char buf[2];
-//   PutTwoDigits(42, buf);
-//   // buf[0] == '4'
-//   // buf[1] == '2'
-inline void PutTwoDigits(size_t i, char* buf) {
-  assert(i < 100);
-  memcpy(buf, two_ASCII_digits[i], 2);
-}
-
-// safe_strto?() functions for implementing SimpleAtoi()
-bool safe_strto32_base(absl::string_view text, int32_t* value, int base);
-bool safe_strto64_base(absl::string_view text, int64_t* value, int base);
-bool safe_strto128_base(absl::string_view text, absl::int128* value,
-                         int base);
-bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base);
-bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base);
-bool safe_strtou128_base(absl::string_view text, absl::uint128* value,
-                         int base);
-
-static const int kFastToBufferSize = 32;
-static const int kSixDigitsToBufferSize = 16;
-
-// Helper function for fast formatting of floating-point values.
-// The result is the same as printf's "%g", a.k.a. "%.6g"; that is, six
-// significant digits are returned, trailing zeros are removed, and numbers
-// outside the range 0.0001-999999 are output using scientific notation
-// (1.23456e+06). This routine is heavily optimized.
-// Required buffer size is `kSixDigitsToBufferSize`.
-size_t SixDigitsToBuffer(double d, char* buffer);
-
-// These functions are intended for speed. All functions take an output buffer
-// as an argument and return a pointer to the last byte they wrote, which is the
-// terminating '\0'. At most `kFastToBufferSize` bytes are written.
-char* FastIntToBuffer(int32_t, char*);
-char* FastIntToBuffer(uint32_t, char*);
-char* FastIntToBuffer(int64_t, char*);
-char* FastIntToBuffer(uint64_t, char*);
-
-// For enums and integer types that are not an exact match for the types above,
-// use templates to call the appropriate one of the four overloads above.
-template <typename int_type>
-char* FastIntToBuffer(int_type i, char* buffer) {
-  static_assert(sizeof(i) <= 64 / 8,
-                "FastIntToBuffer works only with 64-bit-or-less integers.");
-  // TODO(jorg): This signed-ness check is used because it works correctly
-  // with enums, and it also serves to check that int_type is not a pointer.
-  // If one day something like std::is_signed<enum E> works, switch to it.
-  if (static_cast<int_type>(1) - 2 < 0) {  // Signed
-    if (sizeof(i) > 32 / 8) {           // 33-bit to 64-bit
-      return FastIntToBuffer(static_cast<int64_t>(i), buffer);
-    } else {  // 32-bit or less
-      return FastIntToBuffer(static_cast<int32_t>(i), buffer);
-    }
-  } else {                     // Unsigned
-    if (sizeof(i) > 32 / 8) {  // 33-bit to 64-bit
-      return FastIntToBuffer(static_cast<uint64_t>(i), buffer);
-    } else {  // 32-bit or less
-      return FastIntToBuffer(static_cast<uint32_t>(i), buffer);
-    }
-  }
-}
-
-// Implementation of SimpleAtoi, generalized to support arbitrary base (used
-// with base different from 10 elsewhere in Abseil implementation).
-template <typename int_type>
-ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out,
-                                           int base) {
-  static_assert(sizeof(*out) == 4 || sizeof(*out) == 8,
-                "SimpleAtoi works only with 32-bit or 64-bit integers.");
-  static_assert(!std::is_floating_point<int_type>::value,
-                "Use SimpleAtof or SimpleAtod instead.");
-  bool parsed;
-  // TODO(jorg): This signed-ness check is used because it works correctly
-  // with enums, and it also serves to check that int_type is not a pointer.
-  // If one day something like std::is_signed<enum E> works, switch to it.
-  if (static_cast<int_type>(1) - 2 < 0) {  // Signed
-    if (sizeof(*out) == 64 / 8) {       // 64-bit
-      int64_t val;
-      parsed = numbers_internal::safe_strto64_base(s, &val, base);
-      *out = static_cast<int_type>(val);
-    } else {  // 32-bit
-      int32_t val;
-      parsed = numbers_internal::safe_strto32_base(s, &val, base);
-      *out = static_cast<int_type>(val);
-    }
-  } else {                         // Unsigned
-    if (sizeof(*out) == 64 / 8) {  // 64-bit
-      uint64_t val;
-      parsed = numbers_internal::safe_strtou64_base(s, &val, base);
-      *out = static_cast<int_type>(val);
-    } else {  // 32-bit
-      uint32_t val;
-      parsed = numbers_internal::safe_strtou32_base(s, &val, base);
-      *out = static_cast<int_type>(val);
-    }
-  }
-  return parsed;
-}
-
-// FastHexToBufferZeroPad16()
-//
-// Outputs `val` into `out` as if by `snprintf(out, 17, "%016x", val)` but
-// without the terminating null character. Thus `out` must be of length >= 16.
-// Returns the number of non-pad digits of the output (it can never be zero
-// since 0 has one digit).
-inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) {
-#ifdef __SSE4_2__
-  uint64_t be = absl::big_endian::FromHost64(val);
-  const auto kNibbleMask = _mm_set1_epi8(0xf);
-  const auto kHexDigits = _mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7',
-                                        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
-  auto v = _mm_loadl_epi64(reinterpret_cast<__m128i*>(&be));  // load lo dword
-  auto v4 = _mm_srli_epi64(v, 4);                            // shift 4 right
-  auto il = _mm_unpacklo_epi8(v4, v);                        // interleave bytes
-  auto m = _mm_and_si128(il, kNibbleMask);                   // mask out nibbles
-  auto hexchars = _mm_shuffle_epi8(kHexDigits, m);           // hex chars
-  _mm_storeu_si128(reinterpret_cast<__m128i*>(out), hexchars);
-#else
-  for (int i = 0; i < 8; ++i) {
-    auto byte = (val >> (56 - 8 * i)) & 0xFF;
-    auto* hex = &absl::numbers_internal::kHexTable[byte * 2];
-    std::memcpy(out + 2 * i, hex, 2);
-  }
-#endif
-  // | 0x1 so that even 0 has 1 digit.
-  return 16 - absl::base_internal::CountLeadingZeros64(val | 0x1) / 4;
-}
-
-}  // namespace numbers_internal
-
-// SimpleAtoi()
-//
-// Converts a string to an integer, using `safe_strto?()` functions for actual
-// parsing, returning `true` if successful. The `safe_strto?()` functions apply
-// strict checking; the string must be a base-10 integer, optionally followed or
-// preceded by ASCII whitespace, with a value in the range of the corresponding
-// integer type.
-template <typename int_type>
-ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) {
-  return numbers_internal::safe_strtoi_base(str, out, 10);
-}
-
-ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str,
-                                            absl::int128* out) {
-  return numbers_internal::safe_strto128_base(str, out, 10);
-}
-
-ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str,
-                                            absl::uint128* out) {
-  return numbers_internal::safe_strtou128_base(str, out, 10);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_NUMBERS_H_
diff --git a/third_party/abseil_cpp/absl/strings/numbers_benchmark.cc b/third_party/abseil_cpp/absl/strings/numbers_benchmark.cc
deleted file mode 100644
index 6e79b3e811..0000000000
--- a/third_party/abseil_cpp/absl/strings/numbers_benchmark.cc
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cstdint>
-#include <random>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/distributions.h"
-#include "absl/random/random.h"
-#include "absl/strings/numbers.h"
-
-namespace {
-
-template <typename T>
-void BM_FastIntToBuffer(benchmark::State& state) {
-  const int inc = state.range(0);
-  char buf[absl::numbers_internal::kFastToBufferSize];
-  // Use the unsigned type to increment to take advantage of well-defined
-  // modular arithmetic.
-  typename std::make_unsigned<T>::type x = 0;
-  for (auto _ : state) {
-    absl::numbers_internal::FastIntToBuffer(static_cast<T>(x), buf);
-    x += inc;
-  }
-}
-BENCHMARK_TEMPLATE(BM_FastIntToBuffer, int32_t)->Range(0, 1 << 15);
-BENCHMARK_TEMPLATE(BM_FastIntToBuffer, int64_t)->Range(0, 1 << 30);
-
-// Creates an integer that would be printed as `num_digits` repeated 7s in the
-// given `base`. `base` must be greater than or equal to 8.
-int64_t RepeatedSevens(int num_digits, int base) {
-  ABSL_RAW_CHECK(base >= 8, "");
-  int64_t num = 7;
-  while (--num_digits) num = base * num + 7;
-  return num;
-}
-
-void BM_safe_strto32_string(benchmark::State& state) {
-  const int digits = state.range(0);
-  const int base = state.range(1);
-  std::string str(digits, '7');  // valid in octal, decimal and hex
-  int32_t value = 0;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::numbers_internal::safe_strto32_base(str, &value, base));
-  }
-  ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
-}
-BENCHMARK(BM_safe_strto32_string)
-    ->ArgPair(1, 8)
-    ->ArgPair(1, 10)
-    ->ArgPair(1, 16)
-    ->ArgPair(2, 8)
-    ->ArgPair(2, 10)
-    ->ArgPair(2, 16)
-    ->ArgPair(4, 8)
-    ->ArgPair(4, 10)
-    ->ArgPair(4, 16)
-    ->ArgPair(8, 8)
-    ->ArgPair(8, 10)
-    ->ArgPair(8, 16)
-    ->ArgPair(10, 8)
-    ->ArgPair(9, 10);
-
-void BM_safe_strto64_string(benchmark::State& state) {
-  const int digits = state.range(0);
-  const int base = state.range(1);
-  std::string str(digits, '7');  // valid in octal, decimal and hex
-  int64_t value = 0;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::numbers_internal::safe_strto64_base(str, &value, base));
-  }
-  ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
-}
-BENCHMARK(BM_safe_strto64_string)
-    ->ArgPair(1, 8)
-    ->ArgPair(1, 10)
-    ->ArgPair(1, 16)
-    ->ArgPair(2, 8)
-    ->ArgPair(2, 10)
-    ->ArgPair(2, 16)
-    ->ArgPair(4, 8)
-    ->ArgPair(4, 10)
-    ->ArgPair(4, 16)
-    ->ArgPair(8, 8)
-    ->ArgPair(8, 10)
-    ->ArgPair(8, 16)
-    ->ArgPair(16, 8)
-    ->ArgPair(16, 10)
-    ->ArgPair(16, 16);
-
-void BM_safe_strtou32_string(benchmark::State& state) {
-  const int digits = state.range(0);
-  const int base = state.range(1);
-  std::string str(digits, '7');  // valid in octal, decimal and hex
-  uint32_t value = 0;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::numbers_internal::safe_strtou32_base(str, &value, base));
-  }
-  ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
-}
-BENCHMARK(BM_safe_strtou32_string)
-    ->ArgPair(1, 8)
-    ->ArgPair(1, 10)
-    ->ArgPair(1, 16)
-    ->ArgPair(2, 8)
-    ->ArgPair(2, 10)
-    ->ArgPair(2, 16)
-    ->ArgPair(4, 8)
-    ->ArgPair(4, 10)
-    ->ArgPair(4, 16)
-    ->ArgPair(8, 8)
-    ->ArgPair(8, 10)
-    ->ArgPair(8, 16)
-    ->ArgPair(10, 8)
-    ->ArgPair(9, 10);
-
-void BM_safe_strtou64_string(benchmark::State& state) {
-  const int digits = state.range(0);
-  const int base = state.range(1);
-  std::string str(digits, '7');  // valid in octal, decimal and hex
-  uint64_t value = 0;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(
-        absl::numbers_internal::safe_strtou64_base(str, &value, base));
-  }
-  ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), "");
-}
-BENCHMARK(BM_safe_strtou64_string)
-    ->ArgPair(1, 8)
-    ->ArgPair(1, 10)
-    ->ArgPair(1, 16)
-    ->ArgPair(2, 8)
-    ->ArgPair(2, 10)
-    ->ArgPair(2, 16)
-    ->ArgPair(4, 8)
-    ->ArgPair(4, 10)
-    ->ArgPair(4, 16)
-    ->ArgPair(8, 8)
-    ->ArgPair(8, 10)
-    ->ArgPair(8, 16)
-    ->ArgPair(16, 8)
-    ->ArgPair(16, 10)
-    ->ArgPair(16, 16);
-
-// Returns a vector of `num_strings` strings. Each string represents a
-// floating point number with `num_digits` digits before the decimal point and
-// another `num_digits` digits after.
-std::vector<std::string> MakeFloatStrings(int num_strings, int num_digits) {
-  // For convenience, use a random number generator to generate the test data.
-  // We don't actually need random properties, so use a fixed seed.
-  std::minstd_rand0 rng(1);
-  std::uniform_int_distribution<int> random_digit('0', '9');
-
-  std::vector<std::string> float_strings(num_strings);
-  for (std::string& s : float_strings) {
-    s.reserve(2 * num_digits + 1);
-    for (int i = 0; i < num_digits; ++i) {
-      s.push_back(static_cast<char>(random_digit(rng)));
-    }
-    s.push_back('.');
-    for (int i = 0; i < num_digits; ++i) {
-      s.push_back(static_cast<char>(random_digit(rng)));
-    }
-  }
-  return float_strings;
-}
-
-template <typename StringType>
-StringType GetStringAs(const std::string& s) {
-  return static_cast<StringType>(s);
-}
-template <>
-const char* GetStringAs<const char*>(const std::string& s) {
-  return s.c_str();
-}
-
-template <typename StringType>
-std::vector<StringType> GetStringsAs(const std::vector<std::string>& strings) {
-  std::vector<StringType> result;
-  result.reserve(strings.size());
-  for (const std::string& s : strings) {
-    result.push_back(GetStringAs<StringType>(s));
-  }
-  return result;
-}
-
-template <typename T>
-void BM_SimpleAtof(benchmark::State& state) {
-  const int num_strings = state.range(0);
-  const int num_digits = state.range(1);
-  std::vector<std::string> backing_strings =
-      MakeFloatStrings(num_strings, num_digits);
-  std::vector<T> inputs = GetStringsAs<T>(backing_strings);
-  float value;
-  for (auto _ : state) {
-    for (const T& input : inputs) {
-      benchmark::DoNotOptimize(absl::SimpleAtof(input, &value));
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_SimpleAtof, absl::string_view)
-    ->ArgPair(10, 1)
-    ->ArgPair(10, 2)
-    ->ArgPair(10, 4)
-    ->ArgPair(10, 8);
-BENCHMARK_TEMPLATE(BM_SimpleAtof, const char*)
-    ->ArgPair(10, 1)
-    ->ArgPair(10, 2)
-    ->ArgPair(10, 4)
-    ->ArgPair(10, 8);
-BENCHMARK_TEMPLATE(BM_SimpleAtof, std::string)
-    ->ArgPair(10, 1)
-    ->ArgPair(10, 2)
-    ->ArgPair(10, 4)
-    ->ArgPair(10, 8);
-
-template <typename T>
-void BM_SimpleAtod(benchmark::State& state) {
-  const int num_strings = state.range(0);
-  const int num_digits = state.range(1);
-  std::vector<std::string> backing_strings =
-      MakeFloatStrings(num_strings, num_digits);
-  std::vector<T> inputs = GetStringsAs<T>(backing_strings);
-  double value;
-  for (auto _ : state) {
-    for (const T& input : inputs) {
-      benchmark::DoNotOptimize(absl::SimpleAtod(input, &value));
-    }
-  }
-}
-BENCHMARK_TEMPLATE(BM_SimpleAtod, absl::string_view)
-    ->ArgPair(10, 1)
-    ->ArgPair(10, 2)
-    ->ArgPair(10, 4)
-    ->ArgPair(10, 8);
-BENCHMARK_TEMPLATE(BM_SimpleAtod, const char*)
-    ->ArgPair(10, 1)
-    ->ArgPair(10, 2)
-    ->ArgPair(10, 4)
-    ->ArgPair(10, 8);
-BENCHMARK_TEMPLATE(BM_SimpleAtod, std::string)
-    ->ArgPair(10, 1)
-    ->ArgPair(10, 2)
-    ->ArgPair(10, 4)
-    ->ArgPair(10, 8);
-
-void BM_FastHexToBufferZeroPad16(benchmark::State& state) {
-  absl::BitGen rng;
-  std::vector<uint64_t> nums;
-  nums.resize(1000);
-  auto min = std::numeric_limits<uint64_t>::min();
-  auto max = std::numeric_limits<uint64_t>::max();
-  for (auto& num : nums) {
-    num = absl::LogUniform(rng, min, max);
-  }
-
-  char buf[16];
-  while (state.KeepRunningBatch(nums.size())) {
-    for (auto num : nums) {
-      auto digits = absl::numbers_internal::FastHexToBufferZeroPad16(num, buf);
-      benchmark::DoNotOptimize(digits);
-      benchmark::DoNotOptimize(buf);
-    }
-  }
-}
-BENCHMARK(BM_FastHexToBufferZeroPad16);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/numbers_test.cc b/third_party/abseil_cpp/absl/strings/numbers_test.cc
deleted file mode 100644
index 4ab67fb669..0000000000
--- a/third_party/abseil_cpp/absl/strings/numbers_test.cc
+++ /dev/null
@@ -1,1356 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file tests string processing functions related to numeric values.
-
-#include "absl/strings/numbers.h"
-
-#include <sys/types.h>
-
-#include <cfenv>  // NOLINT(build/c++11)
-#include <cinttypes>
-#include <climits>
-#include <cmath>
-#include <cstddef>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <limits>
-#include <numeric>
-#include <random>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/random/distributions.h"
-#include "absl/random/random.h"
-#include "absl/strings/internal/numbers_test_common.h"
-#include "absl/strings/internal/ostringstream.h"
-#include "absl/strings/internal/pow10_helper.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-using absl::numbers_internal::kSixDigitsToBufferSize;
-using absl::numbers_internal::safe_strto32_base;
-using absl::numbers_internal::safe_strto64_base;
-using absl::numbers_internal::safe_strtou32_base;
-using absl::numbers_internal::safe_strtou64_base;
-using absl::numbers_internal::SixDigitsToBuffer;
-using absl::strings_internal::Itoa;
-using absl::strings_internal::strtouint32_test_cases;
-using absl::strings_internal::strtouint64_test_cases;
-using absl::SimpleAtoi;
-using testing::Eq;
-using testing::MatchesRegex;
-
-// Number of floats to test with.
-// 5,000,000 is a reasonable default for a test that only takes a few seconds.
-// 1,000,000,000+ triggers checking for all possible mantissa values for
-// double-precision tests. 2,000,000,000+ triggers checking for every possible
-// single-precision float.
-const int kFloatNumCases = 5000000;
-
-// This is a slow, brute-force routine to compute the exact base-10
-// representation of a double-precision floating-point number.  It
-// is useful for debugging only.
-std::string PerfectDtoa(double d) {
-  if (d == 0) return "0";
-  if (d < 0) return "-" + PerfectDtoa(-d);
-
-  // Basic theory: decompose d into mantissa and exp, where
-  // d = mantissa * 2^exp, and exp is as close to zero as possible.
-  int64_t mantissa, exp = 0;
-  while (d >= 1ULL << 63) ++exp, d *= 0.5;
-  while ((mantissa = d) != d) --exp, d *= 2.0;
-
-  // Then convert mantissa to ASCII, and either double it (if
-  // exp > 0) or halve it (if exp < 0) repeatedly.  "halve it"
-  // in this case means multiplying it by five and dividing by 10.
-  constexpr int maxlen = 1100;  // worst case is actually 1030 or so.
-  char buf[maxlen + 5];
-  for (int64_t num = mantissa, pos = maxlen; --pos >= 0;) {
-    buf[pos] = '0' + (num % 10);
-    num /= 10;
-  }
-  char* begin = &buf[0];
-  char* end = buf + maxlen;
-  for (int i = 0; i != exp; i += (exp > 0) ? 1 : -1) {
-    int carry = 0;
-    for (char* p = end; --p != begin;) {
-      int dig = *p - '0';
-      dig = dig * (exp > 0 ? 2 : 5) + carry;
-      carry = dig / 10;
-      dig %= 10;
-      *p = '0' + dig;
-    }
-  }
-  if (exp < 0) {
-    // "dividing by 10" above means we have to add the decimal point.
-    memmove(end + 1 + exp, end + exp, 1 - exp);
-    end[exp] = '.';
-    ++end;
-  }
-  while (*begin == '0' && begin[1] != '.') ++begin;
-  return {begin, end};
-}
-
-TEST(ToString, PerfectDtoa) {
-  EXPECT_THAT(PerfectDtoa(1), Eq("1"));
-  EXPECT_THAT(PerfectDtoa(0.1),
-              Eq("0.1000000000000000055511151231257827021181583404541015625"));
-  EXPECT_THAT(PerfectDtoa(1e24), Eq("999999999999999983222784"));
-  EXPECT_THAT(PerfectDtoa(5e-324), MatchesRegex("0.0000.*625"));
-  for (int i = 0; i < 100; ++i) {
-    for (double multiplier :
-         {1e-300, 1e-200, 1e-100, 0.1, 1.0, 10.0, 1e100, 1e300}) {
-      double d = multiplier * i;
-      std::string s = PerfectDtoa(d);
-      EXPECT_DOUBLE_EQ(d, strtod(s.c_str(), nullptr));
-    }
-  }
-}
-
-template <typename integer>
-struct MyInteger {
-  integer i;
-  explicit constexpr MyInteger(integer i) : i(i) {}
-  constexpr operator integer() const { return i; }
-
-  constexpr MyInteger operator+(MyInteger other) const { return i + other.i; }
-  constexpr MyInteger operator-(MyInteger other) const { return i - other.i; }
-  constexpr MyInteger operator*(MyInteger other) const { return i * other.i; }
-  constexpr MyInteger operator/(MyInteger other) const { return i / other.i; }
-
-  constexpr bool operator<(MyInteger other) const { return i < other.i; }
-  constexpr bool operator<=(MyInteger other) const { return i <= other.i; }
-  constexpr bool operator==(MyInteger other) const { return i == other.i; }
-  constexpr bool operator>=(MyInteger other) const { return i >= other.i; }
-  constexpr bool operator>(MyInteger other) const { return i > other.i; }
-  constexpr bool operator!=(MyInteger other) const { return i != other.i; }
-
-  integer as_integer() const { return i; }
-};
-
-typedef MyInteger<int64_t> MyInt64;
-typedef MyInteger<uint64_t> MyUInt64;
-
-void CheckInt32(int32_t x) {
-  char buffer[absl::numbers_internal::kFastToBufferSize];
-  char* actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
-  std::string expected = std::to_string(x);
-  EXPECT_EQ(expected, std::string(buffer, actual)) << " Input " << x;
-
-  char* generic_actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
-  EXPECT_EQ(expected, std::string(buffer, generic_actual)) << " Input " << x;
-}
-
-void CheckInt64(int64_t x) {
-  char buffer[absl::numbers_internal::kFastToBufferSize + 3];
-  buffer[0] = '*';
-  buffer[23] = '*';
-  buffer[24] = '*';
-  char* actual = absl::numbers_internal::FastIntToBuffer(x, &buffer[1]);
-  std::string expected = std::to_string(x);
-  EXPECT_EQ(expected, std::string(&buffer[1], actual)) << " Input " << x;
-  EXPECT_EQ(buffer[0], '*');
-  EXPECT_EQ(buffer[23], '*');
-  EXPECT_EQ(buffer[24], '*');
-
-  char* my_actual =
-      absl::numbers_internal::FastIntToBuffer(MyInt64(x), &buffer[1]);
-  EXPECT_EQ(expected, std::string(&buffer[1], my_actual)) << " Input " << x;
-}
-
-void CheckUInt32(uint32_t x) {
-  char buffer[absl::numbers_internal::kFastToBufferSize];
-  char* actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
-  std::string expected = std::to_string(x);
-  EXPECT_EQ(expected, std::string(buffer, actual)) << " Input " << x;
-
-  char* generic_actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
-  EXPECT_EQ(expected, std::string(buffer, generic_actual)) << " Input " << x;
-}
-
-void CheckUInt64(uint64_t x) {
-  char buffer[absl::numbers_internal::kFastToBufferSize + 1];
-  char* actual = absl::numbers_internal::FastIntToBuffer(x, &buffer[1]);
-  std::string expected = std::to_string(x);
-  EXPECT_EQ(expected, std::string(&buffer[1], actual)) << " Input " << x;
-
-  char* generic_actual = absl::numbers_internal::FastIntToBuffer(x, &buffer[1]);
-  EXPECT_EQ(expected, std::string(&buffer[1], generic_actual))
-      << " Input " << x;
-
-  char* my_actual =
-      absl::numbers_internal::FastIntToBuffer(MyUInt64(x), &buffer[1]);
-  EXPECT_EQ(expected, std::string(&buffer[1], my_actual)) << " Input " << x;
-}
-
-void CheckHex64(uint64_t v) {
-  char expected[16 + 1];
-  std::string actual = absl::StrCat(absl::Hex(v, absl::kZeroPad16));
-  snprintf(expected, sizeof(expected), "%016" PRIx64, static_cast<uint64_t>(v));
-  EXPECT_EQ(expected, actual) << " Input " << v;
-  actual = absl::StrCat(absl::Hex(v, absl::kSpacePad16));
-  snprintf(expected, sizeof(expected), "%16" PRIx64, static_cast<uint64_t>(v));
-  EXPECT_EQ(expected, actual) << " Input " << v;
-}
-
-TEST(Numbers, TestFastPrints) {
-  for (int i = -100; i <= 100; i++) {
-    CheckInt32(i);
-    CheckInt64(i);
-  }
-  for (int i = 0; i <= 100; i++) {
-    CheckUInt32(i);
-    CheckUInt64(i);
-  }
-  // Test min int to make sure that works
-  CheckInt32(INT_MIN);
-  CheckInt32(INT_MAX);
-  CheckInt64(LONG_MIN);
-  CheckInt64(uint64_t{1000000000});
-  CheckInt64(uint64_t{9999999999});
-  CheckInt64(uint64_t{100000000000000});
-  CheckInt64(uint64_t{999999999999999});
-  CheckInt64(uint64_t{1000000000000000000});
-  CheckInt64(uint64_t{1199999999999999999});
-  CheckInt64(int64_t{-700000000000000000});
-  CheckInt64(LONG_MAX);
-  CheckUInt32(std::numeric_limits<uint32_t>::max());
-  CheckUInt64(uint64_t{1000000000});
-  CheckUInt64(uint64_t{9999999999});
-  CheckUInt64(uint64_t{100000000000000});
-  CheckUInt64(uint64_t{999999999999999});
-  CheckUInt64(uint64_t{1000000000000000000});
-  CheckUInt64(uint64_t{1199999999999999999});
-  CheckUInt64(std::numeric_limits<uint64_t>::max());
-
-  for (int i = 0; i < 10000; i++) {
-    CheckHex64(i);
-  }
-  CheckHex64(uint64_t{0x123456789abcdef0});
-}
-
-template <typename int_type, typename in_val_type>
-void VerifySimpleAtoiGood(in_val_type in_value, int_type exp_value) {
-  std::string s;
-  // (u)int128 can be streamed but not StrCat'd.
-  absl::strings_internal::OStringStream(&s) << in_value;
-  int_type x = static_cast<int_type>(~exp_value);
-  EXPECT_TRUE(SimpleAtoi(s, &x))
-      << "in_value=" << in_value << " s=" << s << " x=" << x;
-  EXPECT_EQ(exp_value, x);
-  x = static_cast<int_type>(~exp_value);
-  EXPECT_TRUE(SimpleAtoi(s.c_str(), &x));
-  EXPECT_EQ(exp_value, x);
-}
-
-template <typename int_type, typename in_val_type>
-void VerifySimpleAtoiBad(in_val_type in_value) {
-  std::string s;
-  // (u)int128 can be streamed but not StrCat'd.
-  absl::strings_internal::OStringStream(&s) << in_value;
-  int_type x;
-  EXPECT_FALSE(SimpleAtoi(s, &x));
-  EXPECT_FALSE(SimpleAtoi(s.c_str(), &x));
-}
-
-TEST(NumbersTest, Atoi) {
-  // SimpleAtoi(absl::string_view, int32_t)
-  VerifySimpleAtoiGood<int32_t>(0, 0);
-  VerifySimpleAtoiGood<int32_t>(42, 42);
-  VerifySimpleAtoiGood<int32_t>(-42, -42);
-
-  VerifySimpleAtoiGood<int32_t>(std::numeric_limits<int32_t>::min(),
-                                std::numeric_limits<int32_t>::min());
-  VerifySimpleAtoiGood<int32_t>(std::numeric_limits<int32_t>::max(),
-                                std::numeric_limits<int32_t>::max());
-
-  // SimpleAtoi(absl::string_view, uint32_t)
-  VerifySimpleAtoiGood<uint32_t>(0, 0);
-  VerifySimpleAtoiGood<uint32_t>(42, 42);
-  VerifySimpleAtoiBad<uint32_t>(-42);
-
-  VerifySimpleAtoiBad<uint32_t>(std::numeric_limits<int32_t>::min());
-  VerifySimpleAtoiGood<uint32_t>(std::numeric_limits<int32_t>::max(),
-                                 std::numeric_limits<int32_t>::max());
-  VerifySimpleAtoiGood<uint32_t>(std::numeric_limits<uint32_t>::max(),
-                                 std::numeric_limits<uint32_t>::max());
-  VerifySimpleAtoiBad<uint32_t>(std::numeric_limits<int64_t>::min());
-  VerifySimpleAtoiBad<uint32_t>(std::numeric_limits<int64_t>::max());
-  VerifySimpleAtoiBad<uint32_t>(std::numeric_limits<uint64_t>::max());
-
-  // SimpleAtoi(absl::string_view, int64_t)
-  VerifySimpleAtoiGood<int64_t>(0, 0);
-  VerifySimpleAtoiGood<int64_t>(42, 42);
-  VerifySimpleAtoiGood<int64_t>(-42, -42);
-
-  VerifySimpleAtoiGood<int64_t>(std::numeric_limits<int32_t>::min(),
-                                std::numeric_limits<int32_t>::min());
-  VerifySimpleAtoiGood<int64_t>(std::numeric_limits<int32_t>::max(),
-                                std::numeric_limits<int32_t>::max());
-  VerifySimpleAtoiGood<int64_t>(std::numeric_limits<uint32_t>::max(),
-                                std::numeric_limits<uint32_t>::max());
-  VerifySimpleAtoiGood<int64_t>(std::numeric_limits<int64_t>::min(),
-                                std::numeric_limits<int64_t>::min());
-  VerifySimpleAtoiGood<int64_t>(std::numeric_limits<int64_t>::max(),
-                                std::numeric_limits<int64_t>::max());
-  VerifySimpleAtoiBad<int64_t>(std::numeric_limits<uint64_t>::max());
-
-  // SimpleAtoi(absl::string_view, uint64_t)
-  VerifySimpleAtoiGood<uint64_t>(0, 0);
-  VerifySimpleAtoiGood<uint64_t>(42, 42);
-  VerifySimpleAtoiBad<uint64_t>(-42);
-
-  VerifySimpleAtoiBad<uint64_t>(std::numeric_limits<int32_t>::min());
-  VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<int32_t>::max(),
-                                 std::numeric_limits<int32_t>::max());
-  VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<uint32_t>::max(),
-                                 std::numeric_limits<uint32_t>::max());
-  VerifySimpleAtoiBad<uint64_t>(std::numeric_limits<int64_t>::min());
-  VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<int64_t>::max(),
-                                 std::numeric_limits<int64_t>::max());
-  VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<uint64_t>::max(),
-                                 std::numeric_limits<uint64_t>::max());
-
-  // SimpleAtoi(absl::string_view, absl::uint128)
-  VerifySimpleAtoiGood<absl::uint128>(0, 0);
-  VerifySimpleAtoiGood<absl::uint128>(42, 42);
-  VerifySimpleAtoiBad<absl::uint128>(-42);
-
-  VerifySimpleAtoiBad<absl::uint128>(std::numeric_limits<int32_t>::min());
-  VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<int32_t>::max(),
-                                      std::numeric_limits<int32_t>::max());
-  VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<uint32_t>::max(),
-                                      std::numeric_limits<uint32_t>::max());
-  VerifySimpleAtoiBad<absl::uint128>(std::numeric_limits<int64_t>::min());
-  VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<int64_t>::max(),
-                                      std::numeric_limits<int64_t>::max());
-  VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<uint64_t>::max(),
-                                      std::numeric_limits<uint64_t>::max());
-  VerifySimpleAtoiGood<absl::uint128>(
-      std::numeric_limits<absl::uint128>::max(),
-      std::numeric_limits<absl::uint128>::max());
-
-  // SimpleAtoi(absl::string_view, absl::int128)
-  VerifySimpleAtoiGood<absl::int128>(0, 0);
-  VerifySimpleAtoiGood<absl::int128>(42, 42);
-  VerifySimpleAtoiGood<absl::int128>(-42, -42);
-
-  VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<int32_t>::min(),
-                                      std::numeric_limits<int32_t>::min());
-  VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<int32_t>::max(),
-                                      std::numeric_limits<int32_t>::max());
-  VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<uint32_t>::max(),
-                                      std::numeric_limits<uint32_t>::max());
-  VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<int64_t>::min(),
-                                      std::numeric_limits<int64_t>::min());
-  VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<int64_t>::max(),
-                                      std::numeric_limits<int64_t>::max());
-  VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<uint64_t>::max(),
-                                      std::numeric_limits<uint64_t>::max());
-  VerifySimpleAtoiGood<absl::int128>(
-      std::numeric_limits<absl::int128>::min(),
-      std::numeric_limits<absl::int128>::min());
-  VerifySimpleAtoiGood<absl::int128>(
-      std::numeric_limits<absl::int128>::max(),
-      std::numeric_limits<absl::int128>::max());
-  VerifySimpleAtoiBad<absl::int128>(std::numeric_limits<absl::uint128>::max());
-
-  // Some other types
-  VerifySimpleAtoiGood<int>(-42, -42);
-  VerifySimpleAtoiGood<int32_t>(-42, -42);
-  VerifySimpleAtoiGood<uint32_t>(42, 42);
-  VerifySimpleAtoiGood<unsigned int>(42, 42);
-  VerifySimpleAtoiGood<int64_t>(-42, -42);
-  VerifySimpleAtoiGood<long>(-42, -42);  // NOLINT(runtime/int)
-  VerifySimpleAtoiGood<uint64_t>(42, 42);
-  VerifySimpleAtoiGood<size_t>(42, 42);
-  VerifySimpleAtoiGood<std::string::size_type>(42, 42);
-}
-
-TEST(NumbersTest, Atod) {
-  double d;
-  EXPECT_TRUE(absl::SimpleAtod("nan", &d));
-  EXPECT_TRUE(std::isnan(d));
-}
-
-TEST(NumbersTest, Atoenum) {
-  enum E01 {
-    E01_zero = 0,
-    E01_one = 1,
-  };
-
-  VerifySimpleAtoiGood<E01>(E01_zero, E01_zero);
-  VerifySimpleAtoiGood<E01>(E01_one, E01_one);
-
-  enum E_101 {
-    E_101_minusone = -1,
-    E_101_zero = 0,
-    E_101_one = 1,
-  };
-
-  VerifySimpleAtoiGood<E_101>(E_101_minusone, E_101_minusone);
-  VerifySimpleAtoiGood<E_101>(E_101_zero, E_101_zero);
-  VerifySimpleAtoiGood<E_101>(E_101_one, E_101_one);
-
-  enum E_bigint {
-    E_bigint_zero = 0,
-    E_bigint_one = 1,
-    E_bigint_max31 = static_cast<int32_t>(0x7FFFFFFF),
-  };
-
-  VerifySimpleAtoiGood<E_bigint>(E_bigint_zero, E_bigint_zero);
-  VerifySimpleAtoiGood<E_bigint>(E_bigint_one, E_bigint_one);
-  VerifySimpleAtoiGood<E_bigint>(E_bigint_max31, E_bigint_max31);
-
-  enum E_fullint {
-    E_fullint_zero = 0,
-    E_fullint_one = 1,
-    E_fullint_max31 = static_cast<int32_t>(0x7FFFFFFF),
-    E_fullint_min32 = INT32_MIN,
-  };
-
-  VerifySimpleAtoiGood<E_fullint>(E_fullint_zero, E_fullint_zero);
-  VerifySimpleAtoiGood<E_fullint>(E_fullint_one, E_fullint_one);
-  VerifySimpleAtoiGood<E_fullint>(E_fullint_max31, E_fullint_max31);
-  VerifySimpleAtoiGood<E_fullint>(E_fullint_min32, E_fullint_min32);
-
-  enum E_biguint {
-    E_biguint_zero = 0,
-    E_biguint_one = 1,
-    E_biguint_max31 = static_cast<uint32_t>(0x7FFFFFFF),
-    E_biguint_max32 = static_cast<uint32_t>(0xFFFFFFFF),
-  };
-
-  VerifySimpleAtoiGood<E_biguint>(E_biguint_zero, E_biguint_zero);
-  VerifySimpleAtoiGood<E_biguint>(E_biguint_one, E_biguint_one);
-  VerifySimpleAtoiGood<E_biguint>(E_biguint_max31, E_biguint_max31);
-  VerifySimpleAtoiGood<E_biguint>(E_biguint_max32, E_biguint_max32);
-}
-
-TEST(stringtest, safe_strto32_base) {
-  int32_t value;
-  EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16));
-  EXPECT_EQ(0x34234324, value);
-
-  EXPECT_TRUE(safe_strto32_base("0X34234324", &value, 16));
-  EXPECT_EQ(0x34234324, value);
-
-  EXPECT_TRUE(safe_strto32_base("34234324", &value, 16));
-  EXPECT_EQ(0x34234324, value);
-
-  EXPECT_TRUE(safe_strto32_base("0", &value, 16));
-  EXPECT_EQ(0, value);
-
-  EXPECT_TRUE(safe_strto32_base(" \t\n -0x34234324", &value, 16));
-  EXPECT_EQ(-0x34234324, value);
-
-  EXPECT_TRUE(safe_strto32_base(" \t\n -34234324", &value, 16));
-  EXPECT_EQ(-0x34234324, value);
-
-  EXPECT_TRUE(safe_strto32_base("7654321", &value, 8));
-  EXPECT_EQ(07654321, value);
-
-  EXPECT_TRUE(safe_strto32_base("-01234", &value, 8));
-  EXPECT_EQ(-01234, value);
-
-  EXPECT_FALSE(safe_strto32_base("1834", &value, 8));
-
-  // Autodetect base.
-  EXPECT_TRUE(safe_strto32_base("0", &value, 0));
-  EXPECT_EQ(0, value);
-
-  EXPECT_TRUE(safe_strto32_base("077", &value, 0));
-  EXPECT_EQ(077, value);  // Octal interpretation
-
-  // Leading zero indicates octal, but then followed by invalid digit.
-  EXPECT_FALSE(safe_strto32_base("088", &value, 0));
-
-  // Leading 0x indicated hex, but then followed by invalid digit.
-  EXPECT_FALSE(safe_strto32_base("0xG", &value, 0));
-
-  // Base-10 version.
-  EXPECT_TRUE(safe_strto32_base("34234324", &value, 10));
-  EXPECT_EQ(34234324, value);
-
-  EXPECT_TRUE(safe_strto32_base("0", &value, 10));
-  EXPECT_EQ(0, value);
-
-  EXPECT_TRUE(safe_strto32_base(" \t\n -34234324", &value, 10));
-  EXPECT_EQ(-34234324, value);
-
-  EXPECT_TRUE(safe_strto32_base("34234324 \n\t ", &value, 10));
-  EXPECT_EQ(34234324, value);
-
-  // Invalid ints.
-  EXPECT_FALSE(safe_strto32_base("", &value, 10));
-  EXPECT_FALSE(safe_strto32_base("  ", &value, 10));
-  EXPECT_FALSE(safe_strto32_base("abc", &value, 10));
-  EXPECT_FALSE(safe_strto32_base("34234324a", &value, 10));
-  EXPECT_FALSE(safe_strto32_base("34234.3", &value, 10));
-
-  // Out of bounds.
-  EXPECT_FALSE(safe_strto32_base("2147483648", &value, 10));
-  EXPECT_FALSE(safe_strto32_base("-2147483649", &value, 10));
-
-  // String version.
-  EXPECT_TRUE(safe_strto32_base(std::string("0x1234"), &value, 16));
-  EXPECT_EQ(0x1234, value);
-
-  // Base-10 string version.
-  EXPECT_TRUE(safe_strto32_base("1234", &value, 10));
-  EXPECT_EQ(1234, value);
-}
-
-TEST(stringtest, safe_strto32_range) {
-  // These tests verify underflow/overflow behaviour.
-  int32_t value;
-  EXPECT_FALSE(safe_strto32_base("2147483648", &value, 10));
-  EXPECT_EQ(std::numeric_limits<int32_t>::max(), value);
-
-  EXPECT_TRUE(safe_strto32_base("-2147483648", &value, 10));
-  EXPECT_EQ(std::numeric_limits<int32_t>::min(), value);
-
-  EXPECT_FALSE(safe_strto32_base("-2147483649", &value, 10));
-  EXPECT_EQ(std::numeric_limits<int32_t>::min(), value);
-}
-
-TEST(stringtest, safe_strto64_range) {
-  // These tests verify underflow/overflow behaviour.
-  int64_t value;
-  EXPECT_FALSE(safe_strto64_base("9223372036854775808", &value, 10));
-  EXPECT_EQ(std::numeric_limits<int64_t>::max(), value);
-
-  EXPECT_TRUE(safe_strto64_base("-9223372036854775808", &value, 10));
-  EXPECT_EQ(std::numeric_limits<int64_t>::min(), value);
-
-  EXPECT_FALSE(safe_strto64_base("-9223372036854775809", &value, 10));
-  EXPECT_EQ(std::numeric_limits<int64_t>::min(), value);
-}
-
-TEST(stringtest, safe_strto32_leading_substring) {
-  // These tests verify this comment in numbers.h:
-  // On error, returns false, and sets *value to: [...]
-  //   conversion of leading substring if available ("123@@@" -> 123)
-  //   0 if no leading substring available
-  int32_t value;
-  EXPECT_FALSE(safe_strto32_base("04069@@@", &value, 10));
-  EXPECT_EQ(4069, value);
-
-  EXPECT_FALSE(safe_strto32_base("04069@@@", &value, 8));
-  EXPECT_EQ(0406, value);
-
-  EXPECT_FALSE(safe_strto32_base("04069balloons", &value, 10));
-  EXPECT_EQ(4069, value);
-
-  EXPECT_FALSE(safe_strto32_base("04069balloons", &value, 16));
-  EXPECT_EQ(0x4069ba, value);
-
-  EXPECT_FALSE(safe_strto32_base("@@@", &value, 10));
-  EXPECT_EQ(0, value);  // there was no leading substring
-}
-
-TEST(stringtest, safe_strto64_leading_substring) {
-  // These tests verify this comment in numbers.h:
-  // On error, returns false, and sets *value to: [...]
-  //   conversion of leading substring if available ("123@@@" -> 123)
-  //   0 if no leading substring available
-  int64_t value;
-  EXPECT_FALSE(safe_strto64_base("04069@@@", &value, 10));
-  EXPECT_EQ(4069, value);
-
-  EXPECT_FALSE(safe_strto64_base("04069@@@", &value, 8));
-  EXPECT_EQ(0406, value);
-
-  EXPECT_FALSE(safe_strto64_base("04069balloons", &value, 10));
-  EXPECT_EQ(4069, value);
-
-  EXPECT_FALSE(safe_strto64_base("04069balloons", &value, 16));
-  EXPECT_EQ(0x4069ba, value);
-
-  EXPECT_FALSE(safe_strto64_base("@@@", &value, 10));
-  EXPECT_EQ(0, value);  // there was no leading substring
-}
-
-TEST(stringtest, safe_strto64_base) {
-  int64_t value;
-  EXPECT_TRUE(safe_strto64_base("0x3423432448783446", &value, 16));
-  EXPECT_EQ(int64_t{0x3423432448783446}, value);
-
-  EXPECT_TRUE(safe_strto64_base("3423432448783446", &value, 16));
-  EXPECT_EQ(int64_t{0x3423432448783446}, value);
-
-  EXPECT_TRUE(safe_strto64_base("0", &value, 16));
-  EXPECT_EQ(0, value);
-
-  EXPECT_TRUE(safe_strto64_base(" \t\n -0x3423432448783446", &value, 16));
-  EXPECT_EQ(int64_t{-0x3423432448783446}, value);
-
-  EXPECT_TRUE(safe_strto64_base(" \t\n -3423432448783446", &value, 16));
-  EXPECT_EQ(int64_t{-0x3423432448783446}, value);
-
-  EXPECT_TRUE(safe_strto64_base("123456701234567012", &value, 8));
-  EXPECT_EQ(int64_t{0123456701234567012}, value);
-
-  EXPECT_TRUE(safe_strto64_base("-017777777777777", &value, 8));
-  EXPECT_EQ(int64_t{-017777777777777}, value);
-
-  EXPECT_FALSE(safe_strto64_base("19777777777777", &value, 8));
-
-  // Autodetect base.
-  EXPECT_TRUE(safe_strto64_base("0", &value, 0));
-  EXPECT_EQ(0, value);
-
-  EXPECT_TRUE(safe_strto64_base("077", &value, 0));
-  EXPECT_EQ(077, value);  // Octal interpretation
-
-  // Leading zero indicates octal, but then followed by invalid digit.
-  EXPECT_FALSE(safe_strto64_base("088", &value, 0));
-
-  // Leading 0x indicated hex, but then followed by invalid digit.
-  EXPECT_FALSE(safe_strto64_base("0xG", &value, 0));
-
-  // Base-10 version.
-  EXPECT_TRUE(safe_strto64_base("34234324487834466", &value, 10));
-  EXPECT_EQ(int64_t{34234324487834466}, value);
-
-  EXPECT_TRUE(safe_strto64_base("0", &value, 10));
-  EXPECT_EQ(0, value);
-
-  EXPECT_TRUE(safe_strto64_base(" \t\n -34234324487834466", &value, 10));
-  EXPECT_EQ(int64_t{-34234324487834466}, value);
-
-  EXPECT_TRUE(safe_strto64_base("34234324487834466 \n\t ", &value, 10));
-  EXPECT_EQ(int64_t{34234324487834466}, value);
-
-  // Invalid ints.
-  EXPECT_FALSE(safe_strto64_base("", &value, 10));
-  EXPECT_FALSE(safe_strto64_base("  ", &value, 10));
-  EXPECT_FALSE(safe_strto64_base("abc", &value, 10));
-  EXPECT_FALSE(safe_strto64_base("34234324487834466a", &value, 10));
-  EXPECT_FALSE(safe_strto64_base("34234487834466.3", &value, 10));
-
-  // Out of bounds.
-  EXPECT_FALSE(safe_strto64_base("9223372036854775808", &value, 10));
-  EXPECT_FALSE(safe_strto64_base("-9223372036854775809", &value, 10));
-
-  // String version.
-  EXPECT_TRUE(safe_strto64_base(std::string("0x1234"), &value, 16));
-  EXPECT_EQ(0x1234, value);
-
-  // Base-10 string version.
-  EXPECT_TRUE(safe_strto64_base("1234", &value, 10));
-  EXPECT_EQ(1234, value);
-}
-
-const size_t kNumRandomTests = 10000;
-
-template <typename IntType>
-void test_random_integer_parse_base(bool (*parse_func)(absl::string_view,
-                                                       IntType* value,
-                                                       int base)) {
-  using RandomEngine = std::minstd_rand0;
-  std::random_device rd;
-  RandomEngine rng(rd());
-  std::uniform_int_distribution<IntType> random_int(
-      std::numeric_limits<IntType>::min());
-  std::uniform_int_distribution<int> random_base(2, 35);
-  for (size_t i = 0; i < kNumRandomTests; i++) {
-    IntType value = random_int(rng);
-    int base = random_base(rng);
-    std::string str_value;
-    EXPECT_TRUE(Itoa<IntType>(value, base, &str_value));
-    IntType parsed_value;
-
-    // Test successful parse
-    EXPECT_TRUE(parse_func(str_value, &parsed_value, base));
-    EXPECT_EQ(parsed_value, value);
-
-    // Test overflow
-    EXPECT_FALSE(
-        parse_func(absl::StrCat(std::numeric_limits<IntType>::max(), value),
-                   &parsed_value, base));
-
-    // Test underflow
-    if (std::numeric_limits<IntType>::min() < 0) {
-      EXPECT_FALSE(
-          parse_func(absl::StrCat(std::numeric_limits<IntType>::min(), value),
-                     &parsed_value, base));
-    } else {
-      EXPECT_FALSE(parse_func(absl::StrCat("-", value), &parsed_value, base));
-    }
-  }
-}
-
-TEST(stringtest, safe_strto32_random) {
-  test_random_integer_parse_base<int32_t>(&safe_strto32_base);
-}
-TEST(stringtest, safe_strto64_random) {
-  test_random_integer_parse_base<int64_t>(&safe_strto64_base);
-}
-TEST(stringtest, safe_strtou32_random) {
-  test_random_integer_parse_base<uint32_t>(&safe_strtou32_base);
-}
-TEST(stringtest, safe_strtou64_random) {
-  test_random_integer_parse_base<uint64_t>(&safe_strtou64_base);
-}
-TEST(stringtest, safe_strtou128_random) {
-  // random number generators don't work for uint128, and
-  // uint128 can be streamed but not StrCat'd, so this code must be custom
-  // implemented for uint128, but is generally the same as what's above.
-  // test_random_integer_parse_base<absl::uint128>(
-  //     &absl::numbers_internal::safe_strtou128_base);
-  using RandomEngine = std::minstd_rand0;
-  using IntType = absl::uint128;
-  constexpr auto parse_func = &absl::numbers_internal::safe_strtou128_base;
-
-  std::random_device rd;
-  RandomEngine rng(rd());
-  std::uniform_int_distribution<uint64_t> random_uint64(
-      std::numeric_limits<uint64_t>::min());
-  std::uniform_int_distribution<int> random_base(2, 35);
-
-  for (size_t i = 0; i < kNumRandomTests; i++) {
-    IntType value = random_uint64(rng);
-    value = (value << 64) + random_uint64(rng);
-    int base = random_base(rng);
-    std::string str_value;
-    EXPECT_TRUE(Itoa<IntType>(value, base, &str_value));
-    IntType parsed_value;
-
-    // Test successful parse
-    EXPECT_TRUE(parse_func(str_value, &parsed_value, base));
-    EXPECT_EQ(parsed_value, value);
-
-    // Test overflow
-    std::string s;
-    absl::strings_internal::OStringStream(&s)
-        << std::numeric_limits<IntType>::max() << value;
-    EXPECT_FALSE(parse_func(s, &parsed_value, base));
-
-    // Test underflow
-    s.clear();
-    absl::strings_internal::OStringStream(&s) << "-" << value;
-    EXPECT_FALSE(parse_func(s, &parsed_value, base));
-  }
-}
-TEST(stringtest, safe_strto128_random) {
-  // random number generators don't work for int128, and
-  // int128 can be streamed but not StrCat'd, so this code must be custom
-  // implemented for int128, but is generally the same as what's above.
-  // test_random_integer_parse_base<absl::int128>(
-  //     &absl::numbers_internal::safe_strto128_base);
-  using RandomEngine = std::minstd_rand0;
-  using IntType = absl::int128;
-  constexpr auto parse_func = &absl::numbers_internal::safe_strto128_base;
-
-  std::random_device rd;
-  RandomEngine rng(rd());
-  std::uniform_int_distribution<int64_t> random_int64(
-      std::numeric_limits<int64_t>::min());
-  std::uniform_int_distribution<uint64_t> random_uint64(
-      std::numeric_limits<uint64_t>::min());
-  std::uniform_int_distribution<int> random_base(2, 35);
-
-  for (size_t i = 0; i < kNumRandomTests; ++i) {
-    int64_t high = random_int64(rng);
-    uint64_t low = random_uint64(rng);
-    IntType value = absl::MakeInt128(high, low);
-
-    int base = random_base(rng);
-    std::string str_value;
-    EXPECT_TRUE(Itoa<IntType>(value, base, &str_value));
-    IntType parsed_value;
-
-    // Test successful parse
-    EXPECT_TRUE(parse_func(str_value, &parsed_value, base));
-    EXPECT_EQ(parsed_value, value);
-
-    // Test overflow
-    std::string s;
-    absl::strings_internal::OStringStream(&s)
-        << std::numeric_limits<IntType>::max() << value;
-    EXPECT_FALSE(parse_func(s, &parsed_value, base));
-
-    // Test underflow
-    s.clear();
-    absl::strings_internal::OStringStream(&s)
-        << std::numeric_limits<IntType>::min() << value;
-    EXPECT_FALSE(parse_func(s, &parsed_value, base));
-  }
-}
-
-TEST(stringtest, safe_strtou32_base) {
-  for (int i = 0; strtouint32_test_cases()[i].str != nullptr; ++i) {
-    const auto& e = strtouint32_test_cases()[i];
-    uint32_t value;
-    EXPECT_EQ(e.expect_ok, safe_strtou32_base(e.str, &value, e.base))
-        << "str=\"" << e.str << "\" base=" << e.base;
-    if (e.expect_ok) {
-      EXPECT_EQ(e.expected, value) << "i=" << i << " str=\"" << e.str
-                                   << "\" base=" << e.base;
-    }
-  }
-}
-
-TEST(stringtest, safe_strtou32_base_length_delimited) {
-  for (int i = 0; strtouint32_test_cases()[i].str != nullptr; ++i) {
-    const auto& e = strtouint32_test_cases()[i];
-    std::string tmp(e.str);
-    tmp.append("12");  // Adds garbage at the end.
-
-    uint32_t value;
-    EXPECT_EQ(e.expect_ok,
-              safe_strtou32_base(absl::string_view(tmp.data(), strlen(e.str)),
-                                 &value, e.base))
-        << "str=\"" << e.str << "\" base=" << e.base;
-    if (e.expect_ok) {
-      EXPECT_EQ(e.expected, value) << "i=" << i << " str=" << e.str
-                                   << " base=" << e.base;
-    }
-  }
-}
-
-TEST(stringtest, safe_strtou64_base) {
-  for (int i = 0; strtouint64_test_cases()[i].str != nullptr; ++i) {
-    const auto& e = strtouint64_test_cases()[i];
-    uint64_t value;
-    EXPECT_EQ(e.expect_ok, safe_strtou64_base(e.str, &value, e.base))
-        << "str=\"" << e.str << "\" base=" << e.base;
-    if (e.expect_ok) {
-      EXPECT_EQ(e.expected, value) << "str=" << e.str << " base=" << e.base;
-    }
-  }
-}
-
-TEST(stringtest, safe_strtou64_base_length_delimited) {
-  for (int i = 0; strtouint64_test_cases()[i].str != nullptr; ++i) {
-    const auto& e = strtouint64_test_cases()[i];
-    std::string tmp(e.str);
-    tmp.append("12");  // Adds garbage at the end.
-
-    uint64_t value;
-    EXPECT_EQ(e.expect_ok,
-              safe_strtou64_base(absl::string_view(tmp.data(), strlen(e.str)),
-                                 &value, e.base))
-        << "str=\"" << e.str << "\" base=" << e.base;
-    if (e.expect_ok) {
-      EXPECT_EQ(e.expected, value) << "str=\"" << e.str << "\" base=" << e.base;
-    }
-  }
-}
-
-// feenableexcept() and fedisableexcept() are extensions supported by some libc
-// implementations.
-#if defined(__GLIBC__) || defined(__BIONIC__)
-#define ABSL_HAVE_FEENABLEEXCEPT 1
-#define ABSL_HAVE_FEDISABLEEXCEPT 1
-#endif
-
-class SimpleDtoaTest : public testing::Test {
- protected:
-  void SetUp() override {
-    // Store the current floating point env & clear away any pending exceptions.
-    feholdexcept(&fp_env_);
-#ifdef ABSL_HAVE_FEENABLEEXCEPT
-    // Turn on floating point exceptions.
-    feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
-#endif
-  }
-
-  void TearDown() override {
-    // Restore the floating point environment to the original state.
-    // In theory fedisableexcept is unnecessary; fesetenv will also do it.
-    // In practice, our toolchains have subtle bugs.
-#ifdef ABSL_HAVE_FEDISABLEEXCEPT
-    fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
-#endif
-    fesetenv(&fp_env_);
-  }
-
-  std::string ToNineDigits(double value) {
-    char buffer[16];  // more than enough for %.9g
-    snprintf(buffer, sizeof(buffer), "%.9g", value);
-    return buffer;
-  }
-
-  fenv_t fp_env_;
-};
-
-// Run the given runnable functor for "cases" test cases, chosen over the
-// available range of float.  pi and e and 1/e are seeded, and then all
-// available integer powers of 2 and 10 are multiplied against them.  In
-// addition to trying all those values, we try the next higher and next lower
-// float, and then we add additional test cases evenly distributed between them.
-// Each test case is passed to runnable as both a positive and negative value.
-template <typename R>
-void ExhaustiveFloat(uint32_t cases, R&& runnable) {
-  runnable(0.0f);
-  runnable(-0.0f);
-  if (cases >= 2e9) {  // more than 2 billion?  Might as well run them all.
-    for (float f = 0; f < std::numeric_limits<float>::max(); ) {
-      f = nextafterf(f, std::numeric_limits<float>::max());
-      runnable(-f);
-      runnable(f);
-    }
-    return;
-  }
-  std::set<float> floats = {3.4028234e38f};
-  for (float f : {1.0, 3.14159265, 2.718281828, 1 / 2.718281828}) {
-    for (float testf = f; testf != 0; testf *= 0.1f) floats.insert(testf);
-    for (float testf = f; testf != 0; testf *= 0.5f) floats.insert(testf);
-    for (float testf = f; testf < 3e38f / 2; testf *= 2.0f)
-      floats.insert(testf);
-    for (float testf = f; testf < 3e38f / 10; testf *= 10) floats.insert(testf);
-  }
-
-  float last = *floats.begin();
-
-  runnable(last);
-  runnable(-last);
-  int iters_per_float = cases / floats.size();
-  if (iters_per_float == 0) iters_per_float = 1;
-  for (float f : floats) {
-    if (f == last) continue;
-    float testf = std::nextafter(last, std::numeric_limits<float>::max());
-    runnable(testf);
-    runnable(-testf);
-    last = testf;
-    if (f == last) continue;
-    double step = (double{f} - last) / iters_per_float;
-    for (double d = last + step; d < f; d += step) {
-      testf = d;
-      if (testf != last) {
-        runnable(testf);
-        runnable(-testf);
-        last = testf;
-      }
-    }
-    testf = std::nextafter(f, 0.0f);
-    if (testf > last) {
-      runnable(testf);
-      runnable(-testf);
-      last = testf;
-    }
-    if (f != last) {
-      runnable(f);
-      runnable(-f);
-      last = f;
-    }
-  }
-}
-
-TEST_F(SimpleDtoaTest, ExhaustiveDoubleToSixDigits) {
-  uint64_t test_count = 0;
-  std::vector<double> mismatches;
-  auto checker = [&](double d) {
-    if (d != d) return;  // rule out NaNs
-    ++test_count;
-    char sixdigitsbuf[kSixDigitsToBufferSize] = {0};
-    SixDigitsToBuffer(d, sixdigitsbuf);
-    char snprintfbuf[kSixDigitsToBufferSize] = {0};
-    snprintf(snprintfbuf, kSixDigitsToBufferSize, "%g", d);
-    if (strcmp(sixdigitsbuf, snprintfbuf) != 0) {
-      mismatches.push_back(d);
-      if (mismatches.size() < 10) {
-        ABSL_RAW_LOG(ERROR, "%s",
-                     absl::StrCat("Six-digit failure with double.  ", "d=", d,
-                                  "=", d, " sixdigits=", sixdigitsbuf,
-                                  " printf(%g)=", snprintfbuf)
-                         .c_str());
-      }
-    }
-  };
-  // Some quick sanity checks...
-  checker(5e-324);
-  checker(1e-308);
-  checker(1.0);
-  checker(1.000005);
-  checker(1.7976931348623157e308);
-  checker(0.00390625);
-#ifndef _MSC_VER
-  // on MSVC, snprintf() rounds it to 0.00195313. SixDigitsToBuffer() rounds it
-  // to 0.00195312 (round half to even).
-  checker(0.001953125);
-#endif
-  checker(0.005859375);
-  // Some cases where the rounding is very very close
-  checker(1.089095e-15);
-  checker(3.274195e-55);
-  checker(6.534355e-146);
-  checker(2.920845e+234);
-
-  if (mismatches.empty()) {
-    test_count = 0;
-    ExhaustiveFloat(kFloatNumCases, checker);
-
-    test_count = 0;
-    std::vector<int> digit_testcases{
-        100000, 100001, 100002, 100005, 100010, 100020, 100050, 100100,  // misc
-        195312, 195313,  // 1.953125 is a case where we round down, just barely.
-        200000, 500000, 800000,  // misc mid-range cases
-        585937, 585938,  // 5.859375 is a case where we round up, just barely.
-        900000, 990000, 999000, 999900, 999990, 999996, 999997, 999998, 999999};
-    if (kFloatNumCases >= 1e9) {
-      // If at least 1 billion test cases were requested, user wants an
-      // exhaustive test. So let's test all mantissas, too.
-      constexpr int min_mantissa = 100000, max_mantissa = 999999;
-      digit_testcases.resize(max_mantissa - min_mantissa + 1);
-      std::iota(digit_testcases.begin(), digit_testcases.end(), min_mantissa);
-    }
-
-    for (int exponent = -324; exponent <= 308; ++exponent) {
-      double powten = absl::strings_internal::Pow10(exponent);
-      if (powten == 0) powten = 5e-324;
-      if (kFloatNumCases >= 1e9) {
-        // The exhaustive test takes a very long time, so log progress.
-        char buf[kSixDigitsToBufferSize];
-        ABSL_RAW_LOG(
-            INFO, "%s",
-            absl::StrCat("Exp ", exponent, " powten=", powten, "(", powten,
-                         ") (",
-                         std::string(buf, SixDigitsToBuffer(powten, buf)), ")")
-                .c_str());
-      }
-      for (int digits : digit_testcases) {
-        if (exponent == 308 && digits >= 179769) break;  // don't overflow!
-        double digiform = (digits + 0.5) * 0.00001;
-        double testval = digiform * powten;
-        double pretestval = nextafter(testval, 0);
-        double posttestval = nextafter(testval, 1.7976931348623157e308);
-        checker(testval);
-        checker(pretestval);
-        checker(posttestval);
-      }
-    }
-  } else {
-    EXPECT_EQ(mismatches.size(), 0);
-    for (size_t i = 0; i < mismatches.size(); ++i) {
-      if (i > 100) i = mismatches.size() - 1;
-      double d = mismatches[i];
-      char sixdigitsbuf[kSixDigitsToBufferSize] = {0};
-      SixDigitsToBuffer(d, sixdigitsbuf);
-      char snprintfbuf[kSixDigitsToBufferSize] = {0};
-      snprintf(snprintfbuf, kSixDigitsToBufferSize, "%g", d);
-      double before = nextafter(d, 0.0);
-      double after = nextafter(d, 1.7976931348623157e308);
-      char b1[32], b2[kSixDigitsToBufferSize];
-      ABSL_RAW_LOG(
-          ERROR, "%s",
-          absl::StrCat(
-              "Mismatch #", i, "  d=", d, " (", ToNineDigits(d), ")",
-              " sixdigits='", sixdigitsbuf, "'", " snprintf='", snprintfbuf,
-              "'", " Before.=", PerfectDtoa(before), " ",
-              (SixDigitsToBuffer(before, b2), b2),
-              " vs snprintf=", (snprintf(b1, sizeof(b1), "%g", before), b1),
-              " Perfect=", PerfectDtoa(d), " ", (SixDigitsToBuffer(d, b2), b2),
-              " vs snprintf=", (snprintf(b1, sizeof(b1), "%g", d), b1),
-              " After.=.", PerfectDtoa(after), " ",
-              (SixDigitsToBuffer(after, b2), b2),
-              " vs snprintf=", (snprintf(b1, sizeof(b1), "%g", after), b1))
-              .c_str());
-    }
-  }
-}
-
-TEST(StrToInt32, Partial) {
-  struct Int32TestLine {
-    std::string input;
-    bool status;
-    int32_t value;
-  };
-  const int32_t int32_min = std::numeric_limits<int32_t>::min();
-  const int32_t int32_max = std::numeric_limits<int32_t>::max();
-  Int32TestLine int32_test_line[] = {
-      {"", false, 0},
-      {" ", false, 0},
-      {"-", false, 0},
-      {"123@@@", false, 123},
-      {absl::StrCat(int32_min, int32_max), false, int32_min},
-      {absl::StrCat(int32_max, int32_max), false, int32_max},
-  };
-
-  for (const Int32TestLine& test_line : int32_test_line) {
-    int32_t value = -2;
-    bool status = safe_strto32_base(test_line.input, &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-    value = -2;
-    status = safe_strto32_base(test_line.input, &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-    value = -2;
-    status = safe_strto32_base(absl::string_view(test_line.input), &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-  }
-}
-
-TEST(StrToUint32, Partial) {
-  struct Uint32TestLine {
-    std::string input;
-    bool status;
-    uint32_t value;
-  };
-  const uint32_t uint32_max = std::numeric_limits<uint32_t>::max();
-  Uint32TestLine uint32_test_line[] = {
-      {"", false, 0},
-      {" ", false, 0},
-      {"-", false, 0},
-      {"123@@@", false, 123},
-      {absl::StrCat(uint32_max, uint32_max), false, uint32_max},
-  };
-
-  for (const Uint32TestLine& test_line : uint32_test_line) {
-    uint32_t value = 2;
-    bool status = safe_strtou32_base(test_line.input, &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-    value = 2;
-    status = safe_strtou32_base(test_line.input, &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-    value = 2;
-    status = safe_strtou32_base(absl::string_view(test_line.input), &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-  }
-}
-
-TEST(StrToInt64, Partial) {
-  struct Int64TestLine {
-    std::string input;
-    bool status;
-    int64_t value;
-  };
-  const int64_t int64_min = std::numeric_limits<int64_t>::min();
-  const int64_t int64_max = std::numeric_limits<int64_t>::max();
-  Int64TestLine int64_test_line[] = {
-      {"", false, 0},
-      {" ", false, 0},
-      {"-", false, 0},
-      {"123@@@", false, 123},
-      {absl::StrCat(int64_min, int64_max), false, int64_min},
-      {absl::StrCat(int64_max, int64_max), false, int64_max},
-  };
-
-  for (const Int64TestLine& test_line : int64_test_line) {
-    int64_t value = -2;
-    bool status = safe_strto64_base(test_line.input, &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-    value = -2;
-    status = safe_strto64_base(test_line.input, &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-    value = -2;
-    status = safe_strto64_base(absl::string_view(test_line.input), &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-  }
-}
-
-TEST(StrToUint64, Partial) {
-  struct Uint64TestLine {
-    std::string input;
-    bool status;
-    uint64_t value;
-  };
-  const uint64_t uint64_max = std::numeric_limits<uint64_t>::max();
-  Uint64TestLine uint64_test_line[] = {
-      {"", false, 0},
-      {" ", false, 0},
-      {"-", false, 0},
-      {"123@@@", false, 123},
-      {absl::StrCat(uint64_max, uint64_max), false, uint64_max},
-  };
-
-  for (const Uint64TestLine& test_line : uint64_test_line) {
-    uint64_t value = 2;
-    bool status = safe_strtou64_base(test_line.input, &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-    value = 2;
-    status = safe_strtou64_base(test_line.input, &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-    value = 2;
-    status = safe_strtou64_base(absl::string_view(test_line.input), &value, 10);
-    EXPECT_EQ(test_line.status, status) << test_line.input;
-    EXPECT_EQ(test_line.value, value) << test_line.input;
-  }
-}
-
-TEST(StrToInt32Base, PrefixOnly) {
-  struct Int32TestLine {
-    std::string input;
-    bool status;
-    int32_t value;
-  };
-  Int32TestLine int32_test_line[] = {
-    { "", false, 0 },
-    { "-", false, 0 },
-    { "-0", true, 0 },
-    { "0", true, 0 },
-    { "0x", false, 0 },
-    { "-0x", false, 0 },
-  };
-  const int base_array[] = { 0, 2, 8, 10, 16 };
-
-  for (const Int32TestLine& line : int32_test_line) {
-    for (const int base : base_array) {
-      int32_t value = 2;
-      bool status = safe_strto32_base(line.input.c_str(), &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-      value = 2;
-      status = safe_strto32_base(line.input, &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-      value = 2;
-      status = safe_strto32_base(absl::string_view(line.input), &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-    }
-  }
-}
-
-TEST(StrToUint32Base, PrefixOnly) {
-  struct Uint32TestLine {
-    std::string input;
-    bool status;
-    uint32_t value;
-  };
-  Uint32TestLine uint32_test_line[] = {
-    { "", false, 0 },
-    { "0", true, 0 },
-    { "0x", false, 0 },
-  };
-  const int base_array[] = { 0, 2, 8, 10, 16 };
-
-  for (const Uint32TestLine& line : uint32_test_line) {
-    for (const int base : base_array) {
-      uint32_t value = 2;
-      bool status = safe_strtou32_base(line.input.c_str(), &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-      value = 2;
-      status = safe_strtou32_base(line.input, &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-      value = 2;
-      status = safe_strtou32_base(absl::string_view(line.input), &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-    }
-  }
-}
-
-TEST(StrToInt64Base, PrefixOnly) {
-  struct Int64TestLine {
-    std::string input;
-    bool status;
-    int64_t value;
-  };
-  Int64TestLine int64_test_line[] = {
-    { "", false, 0 },
-    { "-", false, 0 },
-    { "-0", true, 0 },
-    { "0", true, 0 },
-    { "0x", false, 0 },
-    { "-0x", false, 0 },
-  };
-  const int base_array[] = { 0, 2, 8, 10, 16 };
-
-  for (const Int64TestLine& line : int64_test_line) {
-    for (const int base : base_array) {
-      int64_t value = 2;
-      bool status = safe_strto64_base(line.input.c_str(), &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-      value = 2;
-      status = safe_strto64_base(line.input, &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-      value = 2;
-      status = safe_strto64_base(absl::string_view(line.input), &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-    }
-  }
-}
-
-TEST(StrToUint64Base, PrefixOnly) {
-  struct Uint64TestLine {
-    std::string input;
-    bool status;
-    uint64_t value;
-  };
-  Uint64TestLine uint64_test_line[] = {
-    { "", false, 0 },
-    { "0", true, 0 },
-    { "0x", false, 0 },
-  };
-  const int base_array[] = { 0, 2, 8, 10, 16 };
-
-  for (const Uint64TestLine& line : uint64_test_line) {
-    for (const int base : base_array) {
-      uint64_t value = 2;
-      bool status = safe_strtou64_base(line.input.c_str(), &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-      value = 2;
-      status = safe_strtou64_base(line.input, &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-      value = 2;
-      status = safe_strtou64_base(absl::string_view(line.input), &value, base);
-      EXPECT_EQ(line.status, status) << line.input << " " << base;
-      EXPECT_EQ(line.value, value) << line.input << " " << base;
-    }
-  }
-}
-
-void TestFastHexToBufferZeroPad16(uint64_t v) {
-  char buf[16];
-  auto digits = absl::numbers_internal::FastHexToBufferZeroPad16(v, buf);
-  absl::string_view res(buf, 16);
-  char buf2[17];
-  snprintf(buf2, sizeof(buf2), "%016" PRIx64, v);
-  EXPECT_EQ(res, buf2) << v;
-  size_t expected_digits = snprintf(buf2, sizeof(buf2), "%" PRIx64, v);
-  EXPECT_EQ(digits, expected_digits) << v;
-}
-
-TEST(FastHexToBufferZeroPad16, Smoke) {
-  TestFastHexToBufferZeroPad16(std::numeric_limits<uint64_t>::min());
-  TestFastHexToBufferZeroPad16(std::numeric_limits<uint64_t>::max());
-  TestFastHexToBufferZeroPad16(std::numeric_limits<int64_t>::min());
-  TestFastHexToBufferZeroPad16(std::numeric_limits<int64_t>::max());
-  absl::BitGen rng;
-  for (int i = 0; i < 100000; ++i) {
-    TestFastHexToBufferZeroPad16(
-        absl::LogUniform(rng, std::numeric_limits<uint64_t>::min(),
-                         std::numeric_limits<uint64_t>::max()));
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/str_cat.cc b/third_party/abseil_cpp/absl/strings/str_cat.cc
deleted file mode 100644
index dd5d25b0d6..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_cat.cc
+++ /dev/null
@@ -1,246 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_cat.h"
-
-#include <assert.h>
-
-#include <algorithm>
-#include <cstdint>
-#include <cstring>
-
-#include "absl/strings/ascii.h"
-#include "absl/strings/internal/resize_uninitialized.h"
-#include "absl/strings/numbers.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-AlphaNum::AlphaNum(Hex hex) {
-  static_assert(numbers_internal::kFastToBufferSize >= 32,
-                "This function only works when output buffer >= 32 bytes long");
-  char* const end = &digits_[numbers_internal::kFastToBufferSize];
-  auto real_width =
-      absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
-  if (real_width >= hex.width) {
-    piece_ = absl::string_view(end - real_width, real_width);
-  } else {
-    // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
-    // max pad width can be up to 20.
-    std::memset(end - 32, hex.fill, 16);
-    // Patch up everything else up to the real_width.
-    std::memset(end - real_width - 16, hex.fill, 16);
-    piece_ = absl::string_view(end - hex.width, hex.width);
-  }
-}
-
-AlphaNum::AlphaNum(Dec dec) {
-  assert(dec.width <= numbers_internal::kFastToBufferSize);
-  char* const end = &digits_[numbers_internal::kFastToBufferSize];
-  char* const minfill = end - dec.width;
-  char* writer = end;
-  uint64_t value = dec.value;
-  bool neg = dec.neg;
-  while (value > 9) {
-    *--writer = '0' + (value % 10);
-    value /= 10;
-  }
-  *--writer = '0' + value;
-  if (neg) *--writer = '-';
-
-  ptrdiff_t fillers = writer - minfill;
-  if (fillers > 0) {
-    // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
-    // But...: if the fill character is '0', then it's <+/-><fill><digits>
-    bool add_sign_again = false;
-    if (neg && dec.fill == '0') {  // If filling with '0',
-      ++writer;                    // ignore the sign we just added
-      add_sign_again = true;       // and re-add the sign later.
-    }
-    writer -= fillers;
-    std::fill_n(writer, fillers, dec.fill);
-    if (add_sign_again) *--writer = '-';
-  }
-
-  piece_ = absl::string_view(writer, end - writer);
-}
-
-// ----------------------------------------------------------------------
-// StrCat()
-//    This merges the given strings or integers, with no delimiter. This
-//    is designed to be the fastest possible way to construct a string out
-//    of a mix of raw C strings, string_views, strings, and integer values.
-// ----------------------------------------------------------------------
-
-// Append is merely a version of memcpy that returns the address of the byte
-// after the area just overwritten.
-static char* Append(char* out, const AlphaNum& x) {
-  // memcpy is allowed to overwrite arbitrary memory, so doing this after the
-  // call would force an extra fetch of x.size().
-  char* after = out + x.size();
-  if (x.size() != 0) {
-    memcpy(out, x.data(), x.size());
-  }
-  return after;
-}
-
-std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
-  std::string result;
-  absl::strings_internal::STLStringResizeUninitialized(&result,
-                                                       a.size() + b.size());
-  char* const begin = &result[0];
-  char* out = begin;
-  out = Append(out, a);
-  out = Append(out, b);
-  assert(out == begin + result.size());
-  return result;
-}
-
-std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
-  std::string result;
-  strings_internal::STLStringResizeUninitialized(
-      &result, a.size() + b.size() + c.size());
-  char* const begin = &result[0];
-  char* out = begin;
-  out = Append(out, a);
-  out = Append(out, b);
-  out = Append(out, c);
-  assert(out == begin + result.size());
-  return result;
-}
-
-std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
-                   const AlphaNum& d) {
-  std::string result;
-  strings_internal::STLStringResizeUninitialized(
-      &result, a.size() + b.size() + c.size() + d.size());
-  char* const begin = &result[0];
-  char* out = begin;
-  out = Append(out, a);
-  out = Append(out, b);
-  out = Append(out, c);
-  out = Append(out, d);
-  assert(out == begin + result.size());
-  return result;
-}
-
-namespace strings_internal {
-
-// Do not call directly - these are not part of the public API.
-std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
-  std::string result;
-  size_t total_size = 0;
-  for (const absl::string_view& piece : pieces) total_size += piece.size();
-  strings_internal::STLStringResizeUninitialized(&result, total_size);
-
-  char* const begin = &result[0];
-  char* out = begin;
-  for (const absl::string_view& piece : pieces) {
-    const size_t this_size = piece.size();
-    if (this_size != 0) {
-      memcpy(out, piece.data(), this_size);
-      out += this_size;
-    }
-  }
-  assert(out == begin + result.size());
-  return result;
-}
-
-// It's possible to call StrAppend with an absl::string_view that is itself a
-// fragment of the string we're appending to.  However the results of this are
-// random. Therefore, check for this in debug mode.  Use unsigned math so we
-// only have to do one comparison. Note, there's an exception case: appending an
-// empty string is always allowed.
-#define ASSERT_NO_OVERLAP(dest, src) \
-  assert(((src).size() == 0) ||      \
-         (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
-
-void AppendPieces(std::string* dest,
-                  std::initializer_list<absl::string_view> pieces) {
-  size_t old_size = dest->size();
-  size_t total_size = old_size;
-  for (const absl::string_view& piece : pieces) {
-    ASSERT_NO_OVERLAP(*dest, piece);
-    total_size += piece.size();
-  }
-  strings_internal::STLStringResizeUninitialized(dest, total_size);
-
-  char* const begin = &(*dest)[0];
-  char* out = begin + old_size;
-  for (const absl::string_view& piece : pieces) {
-    const size_t this_size = piece.size();
-    if (this_size != 0) {
-      memcpy(out, piece.data(), this_size);
-      out += this_size;
-    }
-  }
-  assert(out == begin + dest->size());
-}
-
-}  // namespace strings_internal
-
-void StrAppend(std::string* dest, const AlphaNum& a) {
-  ASSERT_NO_OVERLAP(*dest, a);
-  dest->append(a.data(), a.size());
-}
-
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) {
-  ASSERT_NO_OVERLAP(*dest, a);
-  ASSERT_NO_OVERLAP(*dest, b);
-  std::string::size_type old_size = dest->size();
-  strings_internal::STLStringResizeUninitialized(
-      dest, old_size + a.size() + b.size());
-  char* const begin = &(*dest)[0];
-  char* out = begin + old_size;
-  out = Append(out, a);
-  out = Append(out, b);
-  assert(out == begin + dest->size());
-}
-
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-               const AlphaNum& c) {
-  ASSERT_NO_OVERLAP(*dest, a);
-  ASSERT_NO_OVERLAP(*dest, b);
-  ASSERT_NO_OVERLAP(*dest, c);
-  std::string::size_type old_size = dest->size();
-  strings_internal::STLStringResizeUninitialized(
-      dest, old_size + a.size() + b.size() + c.size());
-  char* const begin = &(*dest)[0];
-  char* out = begin + old_size;
-  out = Append(out, a);
-  out = Append(out, b);
-  out = Append(out, c);
-  assert(out == begin + dest->size());
-}
-
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-               const AlphaNum& c, const AlphaNum& d) {
-  ASSERT_NO_OVERLAP(*dest, a);
-  ASSERT_NO_OVERLAP(*dest, b);
-  ASSERT_NO_OVERLAP(*dest, c);
-  ASSERT_NO_OVERLAP(*dest, d);
-  std::string::size_type old_size = dest->size();
-  strings_internal::STLStringResizeUninitialized(
-      dest, old_size + a.size() + b.size() + c.size() + d.size());
-  char* const begin = &(*dest)[0];
-  char* out = begin + old_size;
-  out = Append(out, a);
-  out = Append(out, b);
-  out = Append(out, c);
-  out = Append(out, d);
-  assert(out == begin + dest->size());
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/str_cat.h b/third_party/abseil_cpp/absl/strings/str_cat.h
deleted file mode 100644
index a8a85c7322..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_cat.h
+++ /dev/null
@@ -1,408 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: str_cat.h
-// -----------------------------------------------------------------------------
-//
-// This package contains functions for efficiently concatenating and appending
-// strings: `StrCat()` and `StrAppend()`. Most of the work within these routines
-// is actually handled through use of a special AlphaNum type, which was
-// designed to be used as a parameter type that efficiently manages conversion
-// to strings and avoids copies in the above operations.
-//
-// Any routine accepting either a string or a number may accept `AlphaNum`.
-// The basic idea is that by accepting a `const AlphaNum &` as an argument
-// to your function, your callers will automagically convert bools, integers,
-// and floating point values to strings for you.
-//
-// NOTE: Use of `AlphaNum` outside of the //absl/strings package is unsupported
-// except for the specific case of function parameters of type `AlphaNum` or
-// `const AlphaNum &`. In particular, instantiating `AlphaNum` directly as a
-// stack variable is not supported.
-//
-// Conversion from 8-bit values is not accepted because, if it were, then an
-// attempt to pass ':' instead of ":" might result in a 58 ending up in your
-// result.
-//
-// Bools convert to "0" or "1". Pointers to types other than `char *` are not
-// valid inputs. No output is generated for null `char *` pointers.
-//
-// Floating point numbers are formatted with six-digit precision, which is
-// the default for "std::cout <<" or printf "%g" (the same as "%.6g").
-//
-// You can convert to hexadecimal output rather than decimal output using the
-// `Hex` type contained here. To do so, pass `Hex(my_int)` as a parameter to
-// `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using
-// a `PadSpec` enum.
-//
-// -----------------------------------------------------------------------------
-
-#ifndef ABSL_STRINGS_STR_CAT_H_
-#define ABSL_STRINGS_STR_CAT_H_
-
-#include <array>
-#include <cstdint>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include "absl/base/port.h"
-#include "absl/strings/numbers.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace strings_internal {
-// AlphaNumBuffer allows a way to pass a string to StrCat without having to do
-// memory allocation.  It is simply a pair of a fixed-size character array, and
-// a size.  Please don't use outside of absl, yet.
-template <size_t max_size>
-struct AlphaNumBuffer {
-  std::array<char, max_size> data;
-  size_t size;
-};
-
-}  // namespace strings_internal
-
-// Enum that specifies the number of significant digits to return in a `Hex` or
-// `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
-// would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value
-// would produce hexadecimal strings such as "    a","    f".
-enum PadSpec : uint8_t {
-  kNoPad = 1,
-  kZeroPad2,
-  kZeroPad3,
-  kZeroPad4,
-  kZeroPad5,
-  kZeroPad6,
-  kZeroPad7,
-  kZeroPad8,
-  kZeroPad9,
-  kZeroPad10,
-  kZeroPad11,
-  kZeroPad12,
-  kZeroPad13,
-  kZeroPad14,
-  kZeroPad15,
-  kZeroPad16,
-  kZeroPad17,
-  kZeroPad18,
-  kZeroPad19,
-  kZeroPad20,
-
-  kSpacePad2 = kZeroPad2 + 64,
-  kSpacePad3,
-  kSpacePad4,
-  kSpacePad5,
-  kSpacePad6,
-  kSpacePad7,
-  kSpacePad8,
-  kSpacePad9,
-  kSpacePad10,
-  kSpacePad11,
-  kSpacePad12,
-  kSpacePad13,
-  kSpacePad14,
-  kSpacePad15,
-  kSpacePad16,
-  kSpacePad17,
-  kSpacePad18,
-  kSpacePad19,
-  kSpacePad20,
-};
-
-// -----------------------------------------------------------------------------
-// Hex
-// -----------------------------------------------------------------------------
-//
-// `Hex` stores a set of hexadecimal string conversion parameters for use
-// within `AlphaNum` string conversions.
-struct Hex {
-  uint64_t value;
-  uint8_t width;
-  char fill;
-
-  template <typename Int>
-  explicit Hex(
-      Int v, PadSpec spec = absl::kNoPad,
-      typename std::enable_if<sizeof(Int) == 1 &&
-                              !std::is_pointer<Int>::value>::type* = nullptr)
-      : Hex(spec, static_cast<uint8_t>(v)) {}
-  template <typename Int>
-  explicit Hex(
-      Int v, PadSpec spec = absl::kNoPad,
-      typename std::enable_if<sizeof(Int) == 2 &&
-                              !std::is_pointer<Int>::value>::type* = nullptr)
-      : Hex(spec, static_cast<uint16_t>(v)) {}
-  template <typename Int>
-  explicit Hex(
-      Int v, PadSpec spec = absl::kNoPad,
-      typename std::enable_if<sizeof(Int) == 4 &&
-                              !std::is_pointer<Int>::value>::type* = nullptr)
-      : Hex(spec, static_cast<uint32_t>(v)) {}
-  template <typename Int>
-  explicit Hex(
-      Int v, PadSpec spec = absl::kNoPad,
-      typename std::enable_if<sizeof(Int) == 8 &&
-                              !std::is_pointer<Int>::value>::type* = nullptr)
-      : Hex(spec, static_cast<uint64_t>(v)) {}
-  template <typename Pointee>
-  explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad)
-      : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
-
- private:
-  Hex(PadSpec spec, uint64_t v)
-      : value(v),
-        width(spec == absl::kNoPad
-                  ? 1
-                  : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
-                                             : spec - absl::kZeroPad2 + 2),
-        fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
-};
-
-// -----------------------------------------------------------------------------
-// Dec
-// -----------------------------------------------------------------------------
-//
-// `Dec` stores a set of decimal string conversion parameters for use
-// within `AlphaNum` string conversions.  Dec is slower than the default
-// integer conversion, so use it only if you need padding.
-struct Dec {
-  uint64_t value;
-  uint8_t width;
-  char fill;
-  bool neg;
-
-  template <typename Int>
-  explicit Dec(Int v, PadSpec spec = absl::kNoPad,
-               typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
-      : value(v >= 0 ? static_cast<uint64_t>(v)
-                     : uint64_t{0} - static_cast<uint64_t>(v)),
-        width(spec == absl::kNoPad
-                  ? 1
-                  : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
-                                             : spec - absl::kZeroPad2 + 2),
-        fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
-        neg(v < 0) {}
-};
-
-// -----------------------------------------------------------------------------
-// AlphaNum
-// -----------------------------------------------------------------------------
-//
-// The `AlphaNum` class acts as the main parameter type for `StrCat()` and
-// `StrAppend()`, providing efficient conversion of numeric, boolean, and
-// hexadecimal values (through the `Hex` type) into strings.
-
-class AlphaNum {
- public:
-  // No bool ctor -- bools convert to an integral type.
-  // A bool ctor would also convert incoming pointers (bletch).
-
-  AlphaNum(int x)  // NOLINT(runtime/explicit)
-      : piece_(digits_,
-               numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
-  AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
-      : piece_(digits_,
-               numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
-  AlphaNum(long x)  // NOLINT(*)
-      : piece_(digits_,
-               numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
-  AlphaNum(unsigned long x)  // NOLINT(*)
-      : piece_(digits_,
-               numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
-  AlphaNum(long long x)  // NOLINT(*)
-      : piece_(digits_,
-               numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
-  AlphaNum(unsigned long long x)  // NOLINT(*)
-      : piece_(digits_,
-               numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
-
-  AlphaNum(float f)  // NOLINT(runtime/explicit)
-      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
-  AlphaNum(double f)  // NOLINT(runtime/explicit)
-      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
-
-  AlphaNum(Hex hex);  // NOLINT(runtime/explicit)
-  AlphaNum(Dec dec);  // NOLINT(runtime/explicit)
-
-  template <size_t size>
-  AlphaNum(  // NOLINT(runtime/explicit)
-      const strings_internal::AlphaNumBuffer<size>& buf)
-      : piece_(&buf.data[0], buf.size) {}
-
-  AlphaNum(const char* c_str) : piece_(c_str) {}  // NOLINT(runtime/explicit)
-  AlphaNum(absl::string_view pc) : piece_(pc) {}  // NOLINT(runtime/explicit)
-
-  template <typename Allocator>
-  AlphaNum(  // NOLINT(runtime/explicit)
-      const std::basic_string<char, std::char_traits<char>, Allocator>& str)
-      : piece_(str) {}
-
-  // Use string literals ":" instead of character literals ':'.
-  AlphaNum(char c) = delete;  // NOLINT(runtime/explicit)
-
-  AlphaNum(const AlphaNum&) = delete;
-  AlphaNum& operator=(const AlphaNum&) = delete;
-
-  absl::string_view::size_type size() const { return piece_.size(); }
-  const char* data() const { return piece_.data(); }
-  absl::string_view Piece() const { return piece_; }
-
-  // Normal enums are already handled by the integer formatters.
-  // This overload matches only scoped enums.
-  template <typename T,
-            typename = typename std::enable_if<
-                std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type>
-  AlphaNum(T e)  // NOLINT(runtime/explicit)
-      : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {}
-
-  // vector<bool>::reference and const_reference require special help to
-  // convert to `AlphaNum` because it requires two user defined conversions.
-  template <
-      typename T,
-      typename std::enable_if<
-          std::is_class<T>::value &&
-          (std::is_same<T, std::vector<bool>::reference>::value ||
-           std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
-          nullptr>
-  AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}  // NOLINT(runtime/explicit)
-
- private:
-  absl::string_view piece_;
-  char digits_[numbers_internal::kFastToBufferSize];
-};
-
-// -----------------------------------------------------------------------------
-// StrCat()
-// -----------------------------------------------------------------------------
-//
-// Merges given strings or numbers, using no delimiter(s), returning the merged
-// result as a string.
-//
-// `StrCat()` is designed to be the fastest possible way to construct a string
-// out of a mix of raw C strings, string_views, strings, bool values,
-// and numeric values.
-//
-// Don't use `StrCat()` for user-visible strings. The localization process
-// works poorly on strings built up out of fragments.
-//
-// For clarity and performance, don't use `StrCat()` when appending to a
-// string. Use `StrAppend()` instead. In particular, avoid using any of these
-// (anti-)patterns:
-//
-//   str.append(StrCat(...))
-//   str += StrCat(...)
-//   str = StrCat(str, ...)
-//
-// The last case is the worst, with a potential to change a loop
-// from a linear time operation with O(1) dynamic allocations into a
-// quadratic time operation with O(n) dynamic allocations.
-//
-// See `StrAppend()` below for more information.
-
-namespace strings_internal {
-
-// Do not call directly - this is not part of the public API.
-std::string CatPieces(std::initializer_list<absl::string_view> pieces);
-void AppendPieces(std::string* dest,
-                  std::initializer_list<absl::string_view> pieces);
-
-}  // namespace strings_internal
-
-ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
-
-ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
-  return std::string(a.data(), a.size());
-}
-
-ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b);
-ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
-                                        const AlphaNum& c);
-ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
-                                        const AlphaNum& c, const AlphaNum& d);
-
-// Support 5 or more arguments
-template <typename... AV>
-ABSL_MUST_USE_RESULT inline std::string StrCat(
-    const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
-    const AlphaNum& e, const AV&... args) {
-  return strings_internal::CatPieces(
-      {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
-       static_cast<const AlphaNum&>(args).Piece()...});
-}
-
-// -----------------------------------------------------------------------------
-// StrAppend()
-// -----------------------------------------------------------------------------
-//
-// Appends a string or set of strings to an existing string, in a similar
-// fashion to `StrCat()`.
-//
-// WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
-// a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
-// not try to check each of its input arguments to be sure that they are not
-// a subset of the string being appended to. That is, while this will work:
-//
-//   std::string s = "foo";
-//   s += s;
-//
-// This output is undefined:
-//
-//   std::string s = "foo";
-//   StrAppend(&s, s);
-//
-// This output is undefined as well, since `absl::string_view` does not own its
-// data:
-//
-//   std::string s = "foobar";
-//   absl::string_view p = s;
-//   StrAppend(&s, p);
-
-inline void StrAppend(std::string*) {}
-void StrAppend(std::string* dest, const AlphaNum& a);
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b);
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-               const AlphaNum& c);
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-               const AlphaNum& c, const AlphaNum& d);
-
-// Support 5 or more arguments
-template <typename... AV>
-inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
-                      const AlphaNum& c, const AlphaNum& d, const AlphaNum& e,
-                      const AV&... args) {
-  strings_internal::AppendPieces(
-      dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
-             static_cast<const AlphaNum&>(args).Piece()...});
-}
-
-// Helper function for the future StrCat default floating-point format, %.6g
-// This is fast.
-inline strings_internal::AlphaNumBuffer<
-    numbers_internal::kSixDigitsToBufferSize>
-SixDigits(double d) {
-  strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
-      result;
-  result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
-  return result;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_STR_CAT_H_
diff --git a/third_party/abseil_cpp/absl/strings/str_cat_benchmark.cc b/third_party/abseil_cpp/absl/strings/str_cat_benchmark.cc
deleted file mode 100644
index 02c4dbe6d8..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_cat_benchmark.cc
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_cat.h"
-
-#include <cstdint>
-#include <string>
-
-#include "benchmark/benchmark.h"
-#include "absl/strings/substitute.h"
-
-namespace {
-
-const char kStringOne[] = "Once Upon A Time, ";
-const char kStringTwo[] = "There was a string benchmark";
-
-// We want to include negative numbers in the benchmark, so this function
-// is used to count 0, 1, -1, 2, -2, 3, -3, ...
-inline int IncrementAlternatingSign(int i) {
-  return i > 0 ? -i : 1 - i;
-}
-
-void BM_Sum_By_StrCat(benchmark::State& state) {
-  int i = 0;
-  char foo[100];
-  for (auto _ : state) {
-    // NOLINTNEXTLINE(runtime/printf)
-    strcpy(foo, absl::StrCat(kStringOne, i, kStringTwo, i * 65536ULL).c_str());
-    int sum = 0;
-    for (char* f = &foo[0]; *f != 0; ++f) {
-      sum += *f;
-    }
-    benchmark::DoNotOptimize(sum);
-    i = IncrementAlternatingSign(i);
-  }
-}
-BENCHMARK(BM_Sum_By_StrCat);
-
-void BM_StrCat_By_snprintf(benchmark::State& state) {
-  int i = 0;
-  char on_stack[1000];
-  for (auto _ : state) {
-    snprintf(on_stack, sizeof(on_stack), "%s %s:%d", kStringOne, kStringTwo, i);
-    i = IncrementAlternatingSign(i);
-  }
-}
-BENCHMARK(BM_StrCat_By_snprintf);
-
-void BM_StrCat_By_Strings(benchmark::State& state) {
-  int i = 0;
-  for (auto _ : state) {
-    std::string result =
-        std::string(kStringOne) + " " + kStringTwo + ":" + absl::StrCat(i);
-    benchmark::DoNotOptimize(result);
-    i = IncrementAlternatingSign(i);
-  }
-}
-BENCHMARK(BM_StrCat_By_Strings);
-
-void BM_StrCat_By_StringOpPlus(benchmark::State& state) {
-  int i = 0;
-  for (auto _ : state) {
-    std::string result = kStringOne;
-    result += " ";
-    result += kStringTwo;
-    result += ":";
-    result += absl::StrCat(i);
-    benchmark::DoNotOptimize(result);
-    i = IncrementAlternatingSign(i);
-  }
-}
-BENCHMARK(BM_StrCat_By_StringOpPlus);
-
-void BM_StrCat_By_StrCat(benchmark::State& state) {
-  int i = 0;
-  for (auto _ : state) {
-    std::string result = absl::StrCat(kStringOne, " ", kStringTwo, ":", i);
-    benchmark::DoNotOptimize(result);
-    i = IncrementAlternatingSign(i);
-  }
-}
-BENCHMARK(BM_StrCat_By_StrCat);
-
-void BM_HexCat_By_StrCat(benchmark::State& state) {
-  int i = 0;
-  for (auto _ : state) {
-    std::string result =
-        absl::StrCat(kStringOne, " ", absl::Hex(int64_t{i} + 0x10000000));
-    benchmark::DoNotOptimize(result);
-    i = IncrementAlternatingSign(i);
-  }
-}
-BENCHMARK(BM_HexCat_By_StrCat);
-
-void BM_HexCat_By_Substitute(benchmark::State& state) {
-  int i = 0;
-  for (auto _ : state) {
-    std::string result = absl::Substitute(
-        "$0 $1", kStringOne, reinterpret_cast<void*>(int64_t{i} + 0x10000000));
-    benchmark::DoNotOptimize(result);
-    i = IncrementAlternatingSign(i);
-  }
-}
-BENCHMARK(BM_HexCat_By_Substitute);
-
-void BM_FloatToString_By_StrCat(benchmark::State& state) {
-  int i = 0;
-  float foo = 0.0f;
-  for (auto _ : state) {
-    std::string result = absl::StrCat(foo += 1.001f, " != ", int64_t{i});
-    benchmark::DoNotOptimize(result);
-    i = IncrementAlternatingSign(i);
-  }
-}
-BENCHMARK(BM_FloatToString_By_StrCat);
-
-void BM_DoubleToString_By_SixDigits(benchmark::State& state) {
-  int i = 0;
-  double foo = 0.0;
-  for (auto _ : state) {
-    std::string result =
-        absl::StrCat(absl::SixDigits(foo += 1.001), " != ", int64_t{i});
-    benchmark::DoNotOptimize(result);
-    i = IncrementAlternatingSign(i);
-  }
-}
-BENCHMARK(BM_DoubleToString_By_SixDigits);
-
-template <typename... Chunks>
-void BM_StrAppendImpl(benchmark::State& state, size_t total_bytes,
-                      Chunks... chunks) {
-  for (auto s : state) {
-    std::string result;
-    while (result.size() < total_bytes) {
-      absl::StrAppend(&result, chunks...);
-      benchmark::DoNotOptimize(result);
-    }
-  }
-}
-
-void BM_StrAppend(benchmark::State& state) {
-  const int total_bytes = state.range(0);
-  const int chunks_at_a_time = state.range(1);
-  const absl::string_view kChunk = "0123456789";
-
-  switch (chunks_at_a_time) {
-    case 1:
-      return BM_StrAppendImpl(state, total_bytes, kChunk);
-    case 2:
-      return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk);
-    case 4:
-      return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk, kChunk,
-                              kChunk);
-    case 8:
-      return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk, kChunk,
-                              kChunk, kChunk, kChunk, kChunk, kChunk);
-    default:
-      std::abort();
-  }
-}
-
-template <typename B>
-void StrAppendConfig(B* benchmark) {
-  for (int bytes : {10, 100, 1000, 10000}) {
-    for (int chunks : {1, 2, 4, 8}) {
-      // Only add the ones that divide properly. Otherwise we are over counting.
-      if (bytes % (10 * chunks) == 0) {
-        benchmark->Args({bytes, chunks});
-      }
-    }
-  }
-}
-
-BENCHMARK(BM_StrAppend)->Apply(StrAppendConfig);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/str_cat_test.cc b/third_party/abseil_cpp/absl/strings/str_cat_test.cc
deleted file mode 100644
index f3770dc076..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_cat_test.cc
+++ /dev/null
@@ -1,610 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Unit tests for all str_cat.h functions
-
-#include "absl/strings/str_cat.h"
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/strings/substitute.h"
-
-#ifdef __ANDROID__
-// Android assert messages only go to system log, so death tests cannot inspect
-// the message for matching.
-#define ABSL_EXPECT_DEBUG_DEATH(statement, regex) \
-  EXPECT_DEBUG_DEATH(statement, ".*")
-#else
-#define ABSL_EXPECT_DEBUG_DEATH(statement, regex) \
-  EXPECT_DEBUG_DEATH(statement, regex)
-#endif
-
-namespace {
-
-// Test absl::StrCat of ints and longs of various sizes and signdedness.
-TEST(StrCat, Ints) {
-  const short s = -1;  // NOLINT(runtime/int)
-  const uint16_t us = 2;
-  const int i = -3;
-  const unsigned int ui = 4;
-  const long l = -5;                 // NOLINT(runtime/int)
-  const unsigned long ul = 6;        // NOLINT(runtime/int)
-  const long long ll = -7;           // NOLINT(runtime/int)
-  const unsigned long long ull = 8;  // NOLINT(runtime/int)
-  const ptrdiff_t ptrdiff = -9;
-  const size_t size = 10;
-  const intptr_t intptr = -12;
-  const uintptr_t uintptr = 13;
-  std::string answer;
-  answer = absl::StrCat(s, us);
-  EXPECT_EQ(answer, "-12");
-  answer = absl::StrCat(i, ui);
-  EXPECT_EQ(answer, "-34");
-  answer = absl::StrCat(l, ul);
-  EXPECT_EQ(answer, "-56");
-  answer = absl::StrCat(ll, ull);
-  EXPECT_EQ(answer, "-78");
-  answer = absl::StrCat(ptrdiff, size);
-  EXPECT_EQ(answer, "-910");
-  answer = absl::StrCat(ptrdiff, intptr);
-  EXPECT_EQ(answer, "-9-12");
-  answer = absl::StrCat(uintptr, 0);
-  EXPECT_EQ(answer, "130");
-}
-
-TEST(StrCat, Enums) {
-  enum SmallNumbers { One = 1, Ten = 10 } e = Ten;
-  EXPECT_EQ("10", absl::StrCat(e));
-  EXPECT_EQ("-5", absl::StrCat(SmallNumbers(-5)));
-
-  enum class Option { Boxers = 1, Briefs = -1 };
-
-  EXPECT_EQ("-1", absl::StrCat(Option::Briefs));
-
-  enum class Airplane : uint64_t {
-    Airbus = 1,
-    Boeing = 1000,
-    Canary = 10000000000  // too big for "int"
-  };
-
-  EXPECT_EQ("10000000000", absl::StrCat(Airplane::Canary));
-
-  enum class TwoGig : int32_t {
-    TwoToTheZero = 1,
-    TwoToTheSixteenth = 1 << 16,
-    TwoToTheThirtyFirst = INT32_MIN
-  };
-  EXPECT_EQ("65536", absl::StrCat(TwoGig::TwoToTheSixteenth));
-  EXPECT_EQ("-2147483648", absl::StrCat(TwoGig::TwoToTheThirtyFirst));
-  EXPECT_EQ("-1", absl::StrCat(static_cast<TwoGig>(-1)));
-
-  enum class FourGig : uint32_t {
-    TwoToTheZero = 1,
-    TwoToTheSixteenth = 1 << 16,
-    TwoToTheThirtyFirst = 1U << 31  // too big for "int"
-  };
-  EXPECT_EQ("65536", absl::StrCat(FourGig::TwoToTheSixteenth));
-  EXPECT_EQ("2147483648", absl::StrCat(FourGig::TwoToTheThirtyFirst));
-  EXPECT_EQ("4294967295", absl::StrCat(static_cast<FourGig>(-1)));
-
-  EXPECT_EQ("10000000000", absl::StrCat(Airplane::Canary));
-}
-
-TEST(StrCat, Basics) {
-  std::string result;
-
-  std::string strs[] = {"Hello", "Cruel", "World"};
-
-  std::string stdstrs[] = {
-    "std::Hello",
-    "std::Cruel",
-    "std::World"
-  };
-
-  absl::string_view pieces[] = {"Hello", "Cruel", "World"};
-
-  const char* c_strs[] = {
-    "Hello",
-    "Cruel",
-    "World"
-  };
-
-  int32_t i32s[] = {'H', 'C', 'W'};
-  uint64_t ui64s[] = {12345678910LL, 10987654321LL};
-
-  EXPECT_EQ(absl::StrCat(), "");
-
-  result = absl::StrCat(false, true, 2, 3);
-  EXPECT_EQ(result, "0123");
-
-  result = absl::StrCat(-1);
-  EXPECT_EQ(result, "-1");
-
-  result = absl::StrCat(absl::SixDigits(0.5));
-  EXPECT_EQ(result, "0.5");
-
-  result = absl::StrCat(strs[1], pieces[2]);
-  EXPECT_EQ(result, "CruelWorld");
-
-  result = absl::StrCat(stdstrs[1], " ", stdstrs[2]);
-  EXPECT_EQ(result, "std::Cruel std::World");
-
-  result = absl::StrCat(strs[0], ", ", pieces[2]);
-  EXPECT_EQ(result, "Hello, World");
-
-  result = absl::StrCat(strs[0], ", ", strs[1], " ", strs[2], "!");
-  EXPECT_EQ(result, "Hello, Cruel World!");
-
-  result = absl::StrCat(pieces[0], ", ", pieces[1], " ", pieces[2]);
-  EXPECT_EQ(result, "Hello, Cruel World");
-
-  result = absl::StrCat(c_strs[0], ", ", c_strs[1], " ", c_strs[2]);
-  EXPECT_EQ(result, "Hello, Cruel World");
-
-  result = absl::StrCat("ASCII ", i32s[0], ", ", i32s[1], " ", i32s[2], "!");
-  EXPECT_EQ(result, "ASCII 72, 67 87!");
-
-  result = absl::StrCat(ui64s[0], ", ", ui64s[1], "!");
-  EXPECT_EQ(result, "12345678910, 10987654321!");
-
-  std::string one =
-      "1";  // Actually, it's the size of this string that we want; a
-            // 64-bit build distinguishes between size_t and uint64_t,
-            // even though they're both unsigned 64-bit values.
-  result = absl::StrCat("And a ", one.size(), " and a ",
-                        &result[2] - &result[0], " and a ", one, " 2 3 4", "!");
-  EXPECT_EQ(result, "And a 1 and a 2 and a 1 2 3 4!");
-
-  // result = absl::StrCat("Single chars won't compile", '!');
-  // result = absl::StrCat("Neither will nullptrs", nullptr);
-  result =
-      absl::StrCat("To output a char by ASCII/numeric value, use +: ", '!' + 0);
-  EXPECT_EQ(result, "To output a char by ASCII/numeric value, use +: 33");
-
-  float f = 100000.5;
-  result = absl::StrCat("A hundred K and a half is ", absl::SixDigits(f));
-  EXPECT_EQ(result, "A hundred K and a half is 100000");
-
-  f = 100001.5;
-  result =
-      absl::StrCat("A hundred K and one and a half is ", absl::SixDigits(f));
-  EXPECT_EQ(result, "A hundred K and one and a half is 100002");
-
-  double d = 100000.5;
-  d *= d;
-  result =
-      absl::StrCat("A hundred K and a half squared is ", absl::SixDigits(d));
-  EXPECT_EQ(result, "A hundred K and a half squared is 1.00001e+10");
-
-  result = absl::StrCat(1, 2, 333, 4444, 55555, 666666, 7777777, 88888888,
-                        999999999);
-  EXPECT_EQ(result, "12333444455555666666777777788888888999999999");
-}
-
-TEST(StrCat, CornerCases) {
-  std::string result;
-
-  result = absl::StrCat("");  // NOLINT
-  EXPECT_EQ(result, "");
-  result = absl::StrCat("", "");
-  EXPECT_EQ(result, "");
-  result = absl::StrCat("", "", "");
-  EXPECT_EQ(result, "");
-  result = absl::StrCat("", "", "", "");
-  EXPECT_EQ(result, "");
-  result = absl::StrCat("", "", "", "", "");
-  EXPECT_EQ(result, "");
-}
-
-// A minimal allocator that uses malloc().
-template <typename T>
-struct Mallocator {
-  typedef T value_type;
-  typedef size_t size_type;
-  typedef ptrdiff_t difference_type;
-  typedef T* pointer;
-  typedef const T* const_pointer;
-  typedef T& reference;
-  typedef const T& const_reference;
-
-  size_type max_size() const {
-    return size_t(std::numeric_limits<size_type>::max()) / sizeof(value_type);
-  }
-  template <typename U>
-  struct rebind {
-    typedef Mallocator<U> other;
-  };
-  Mallocator() = default;
-  template <class U>
-  Mallocator(const Mallocator<U>&) {}  // NOLINT(runtime/explicit)
-
-  T* allocate(size_t n) { return static_cast<T*>(std::malloc(n * sizeof(T))); }
-  void deallocate(T* p, size_t) { std::free(p); }
-};
-template <typename T, typename U>
-bool operator==(const Mallocator<T>&, const Mallocator<U>&) {
-  return true;
-}
-template <typename T, typename U>
-bool operator!=(const Mallocator<T>&, const Mallocator<U>&) {
-  return false;
-}
-
-TEST(StrCat, CustomAllocator) {
-  using mstring =
-      std::basic_string<char, std::char_traits<char>, Mallocator<char>>;
-  const mstring str1("PARACHUTE OFF A BLIMP INTO MOSCONE!!");
-
-  const mstring str2("Read this book about coffee tables");
-
-  std::string result = absl::StrCat(str1, str2);
-  EXPECT_EQ(result,
-            "PARACHUTE OFF A BLIMP INTO MOSCONE!!"
-            "Read this book about coffee tables");
-}
-
-TEST(StrCat, MaxArgs) {
-  std::string result;
-  // Test 10 up to 26 arguments, the old maximum
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a");
-  EXPECT_EQ(result, "123456789a");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b");
-  EXPECT_EQ(result, "123456789ab");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c");
-  EXPECT_EQ(result, "123456789abc");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d");
-  EXPECT_EQ(result, "123456789abcd");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e");
-  EXPECT_EQ(result, "123456789abcde");
-  result =
-      absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f");
-  EXPECT_EQ(result, "123456789abcdef");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g");
-  EXPECT_EQ(result, "123456789abcdefg");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h");
-  EXPECT_EQ(result, "123456789abcdefgh");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h", "i");
-  EXPECT_EQ(result, "123456789abcdefghi");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h", "i", "j");
-  EXPECT_EQ(result, "123456789abcdefghij");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h", "i", "j", "k");
-  EXPECT_EQ(result, "123456789abcdefghijk");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h", "i", "j", "k", "l");
-  EXPECT_EQ(result, "123456789abcdefghijkl");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h", "i", "j", "k", "l", "m");
-  EXPECT_EQ(result, "123456789abcdefghijklm");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h", "i", "j", "k", "l", "m", "n");
-  EXPECT_EQ(result, "123456789abcdefghijklmn");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h", "i", "j", "k", "l", "m", "n", "o");
-  EXPECT_EQ(result, "123456789abcdefghijklmno");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h", "i", "j", "k", "l", "m", "n", "o", "p");
-  EXPECT_EQ(result, "123456789abcdefghijklmnop");
-  result = absl::StrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
-                        "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q");
-  EXPECT_EQ(result, "123456789abcdefghijklmnopq");
-  // No limit thanks to C++11's variadic templates
-  result = absl::StrCat(
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "a", "b", "c", "d", "e", "f", "g", "h",
-      "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w",
-      "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L",
-      "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z");
-  EXPECT_EQ(result,
-            "12345678910abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
-}
-
-TEST(StrAppend, Basics) {
-  std::string result = "existing text";
-
-  std::string strs[] = {"Hello", "Cruel", "World"};
-
-  std::string stdstrs[] = {
-    "std::Hello",
-    "std::Cruel",
-    "std::World"
-  };
-
-  absl::string_view pieces[] = {"Hello", "Cruel", "World"};
-
-  const char* c_strs[] = {
-    "Hello",
-    "Cruel",
-    "World"
-  };
-
-  int32_t i32s[] = {'H', 'C', 'W'};
-  uint64_t ui64s[] = {12345678910LL, 10987654321LL};
-
-  std::string::size_type old_size = result.size();
-  absl::StrAppend(&result);
-  EXPECT_EQ(result.size(), old_size);
-
-  old_size = result.size();
-  absl::StrAppend(&result, strs[0]);
-  EXPECT_EQ(result.substr(old_size), "Hello");
-
-  old_size = result.size();
-  absl::StrAppend(&result, strs[1], pieces[2]);
-  EXPECT_EQ(result.substr(old_size), "CruelWorld");
-
-  old_size = result.size();
-  absl::StrAppend(&result, stdstrs[0], ", ", pieces[2]);
-  EXPECT_EQ(result.substr(old_size), "std::Hello, World");
-
-  old_size = result.size();
-  absl::StrAppend(&result, strs[0], ", ", stdstrs[1], " ", strs[2], "!");
-  EXPECT_EQ(result.substr(old_size), "Hello, std::Cruel World!");
-
-  old_size = result.size();
-  absl::StrAppend(&result, pieces[0], ", ", pieces[1], " ", pieces[2]);
-  EXPECT_EQ(result.substr(old_size), "Hello, Cruel World");
-
-  old_size = result.size();
-  absl::StrAppend(&result, c_strs[0], ", ", c_strs[1], " ", c_strs[2]);
-  EXPECT_EQ(result.substr(old_size), "Hello, Cruel World");
-
-  old_size = result.size();
-  absl::StrAppend(&result, "ASCII ", i32s[0], ", ", i32s[1], " ", i32s[2], "!");
-  EXPECT_EQ(result.substr(old_size), "ASCII 72, 67 87!");
-
-  old_size = result.size();
-  absl::StrAppend(&result, ui64s[0], ", ", ui64s[1], "!");
-  EXPECT_EQ(result.substr(old_size), "12345678910, 10987654321!");
-
-  std::string one =
-      "1";  // Actually, it's the size of this string that we want; a
-            // 64-bit build distinguishes between size_t and uint64_t,
-            // even though they're both unsigned 64-bit values.
-  old_size = result.size();
-  absl::StrAppend(&result, "And a ", one.size(), " and a ",
-                  &result[2] - &result[0], " and a ", one, " 2 3 4", "!");
-  EXPECT_EQ(result.substr(old_size), "And a 1 and a 2 and a 1 2 3 4!");
-
-  // result = absl::StrCat("Single chars won't compile", '!');
-  // result = absl::StrCat("Neither will nullptrs", nullptr);
-  old_size = result.size();
-  absl::StrAppend(&result,
-                  "To output a char by ASCII/numeric value, use +: ", '!' + 0);
-  EXPECT_EQ(result.substr(old_size),
-            "To output a char by ASCII/numeric value, use +: 33");
-
-  // Test 9 arguments, the old maximum
-  old_size = result.size();
-  absl::StrAppend(&result, 1, 22, 333, 4444, 55555, 666666, 7777777, 88888888,
-                  9);
-  EXPECT_EQ(result.substr(old_size), "1223334444555556666667777777888888889");
-
-  // No limit thanks to C++11's variadic templates
-  old_size = result.size();
-  absl::StrAppend(
-      &result, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,                           //
-      "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",  //
-      "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",  //
-      "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",  //
-      "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",  //
-      "No limit thanks to C++11's variadic templates");
-  EXPECT_EQ(result.substr(old_size),
-            "12345678910abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
-            "No limit thanks to C++11's variadic templates");
-}
-
-TEST(StrCat, VectorBoolReferenceTypes) {
-  std::vector<bool> v;
-  v.push_back(true);
-  v.push_back(false);
-  std::vector<bool> const& cv = v;
-  // Test that vector<bool>::reference and vector<bool>::const_reference
-  // are handled as if the were really bool types and not the proxy types
-  // they really are.
-  std::string result = absl::StrCat(v[0], v[1], cv[0], cv[1]); // NOLINT
-  EXPECT_EQ(result, "1010");
-}
-
-// Passing nullptr to memcpy is undefined behavior and this test
-// provides coverage of codepaths that handle empty strings with nullptrs.
-TEST(StrCat, AvoidsMemcpyWithNullptr) {
-  EXPECT_EQ(absl::StrCat(42, absl::string_view{}), "42");
-
-  // Cover CatPieces code.
-  EXPECT_EQ(absl::StrCat(1, 2, 3, 4, 5, absl::string_view{}), "12345");
-
-  // Cover AppendPieces.
-  std::string result;
-  absl::StrAppend(&result, 1, 2, 3, 4, 5, absl::string_view{});
-  EXPECT_EQ(result, "12345");
-}
-
-#ifdef GTEST_HAS_DEATH_TEST
-TEST(StrAppend, Death) {
-  std::string s = "self";
-  // on linux it's "assertion", on mac it's "Assertion",
-  // on chromiumos it's "Assertion ... failed".
-  ABSL_EXPECT_DEBUG_DEATH(absl::StrAppend(&s, s.c_str() + 1),
-                          "ssertion.*failed");
-  ABSL_EXPECT_DEBUG_DEATH(absl::StrAppend(&s, s), "ssertion.*failed");
-}
-#endif  // GTEST_HAS_DEATH_TEST
-
-TEST(StrAppend, CornerCases) {
-  std::string result;
-  absl::StrAppend(&result, "");
-  EXPECT_EQ(result, "");
-  absl::StrAppend(&result, "", "");
-  EXPECT_EQ(result, "");
-  absl::StrAppend(&result, "", "", "");
-  EXPECT_EQ(result, "");
-  absl::StrAppend(&result, "", "", "", "");
-  EXPECT_EQ(result, "");
-  absl::StrAppend(&result, "", "", "", "", "");
-  EXPECT_EQ(result, "");
-}
-
-TEST(StrAppend, CornerCasesNonEmptyAppend) {
-  for (std::string result : {"hello", "a string too long to fit in the SSO"}) {
-    const std::string expected = result;
-    absl::StrAppend(&result, "");
-    EXPECT_EQ(result, expected);
-    absl::StrAppend(&result, "", "");
-    EXPECT_EQ(result, expected);
-    absl::StrAppend(&result, "", "", "");
-    EXPECT_EQ(result, expected);
-    absl::StrAppend(&result, "", "", "", "");
-    EXPECT_EQ(result, expected);
-    absl::StrAppend(&result, "", "", "", "", "");
-    EXPECT_EQ(result, expected);
-  }
-}
-
-template <typename IntType>
-void CheckHex(IntType v, const char* nopad_format, const char* zeropad_format,
-              const char* spacepad_format) {
-  char expected[256];
-
-  std::string actual = absl::StrCat(absl::Hex(v, absl::kNoPad));
-  snprintf(expected, sizeof(expected), nopad_format, v);
-  EXPECT_EQ(expected, actual) << " decimal value " << v;
-
-  for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad20; ++spec) {
-    std::string actual =
-        absl::StrCat(absl::Hex(v, static_cast<absl::PadSpec>(spec)));
-    snprintf(expected, sizeof(expected), zeropad_format,
-             spec - absl::kZeroPad2 + 2, v);
-    EXPECT_EQ(expected, actual) << " decimal value " << v;
-  }
-
-  for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad20; ++spec) {
-    std::string actual =
-        absl::StrCat(absl::Hex(v, static_cast<absl::PadSpec>(spec)));
-    snprintf(expected, sizeof(expected), spacepad_format,
-             spec - absl::kSpacePad2 + 2, v);
-    EXPECT_EQ(expected, actual) << " decimal value " << v;
-  }
-}
-
-template <typename IntType>
-void CheckDec(IntType v, const char* nopad_format, const char* zeropad_format,
-              const char* spacepad_format) {
-  char expected[256];
-
-  std::string actual = absl::StrCat(absl::Dec(v, absl::kNoPad));
-  snprintf(expected, sizeof(expected), nopad_format, v);
-  EXPECT_EQ(expected, actual) << " decimal value " << v;
-
-  for (int spec = absl::kZeroPad2; spec <= absl::kZeroPad20; ++spec) {
-    std::string actual =
-        absl::StrCat(absl::Dec(v, static_cast<absl::PadSpec>(spec)));
-    snprintf(expected, sizeof(expected), zeropad_format,
-             spec - absl::kZeroPad2 + 2, v);
-    EXPECT_EQ(expected, actual)
-        << " decimal value " << v << " format '" << zeropad_format
-        << "' digits " << (spec - absl::kZeroPad2 + 2);
-  }
-
-  for (int spec = absl::kSpacePad2; spec <= absl::kSpacePad20; ++spec) {
-    std::string actual =
-        absl::StrCat(absl::Dec(v, static_cast<absl::PadSpec>(spec)));
-    snprintf(expected, sizeof(expected), spacepad_format,
-             spec - absl::kSpacePad2 + 2, v);
-    EXPECT_EQ(expected, actual)
-        << " decimal value " << v << " format '" << spacepad_format
-        << "' digits " << (spec - absl::kSpacePad2 + 2);
-  }
-}
-
-void CheckHexDec64(uint64_t v) {
-  unsigned long long ullv = v;  // NOLINT(runtime/int)
-
-  CheckHex(ullv, "%llx", "%0*llx", "%*llx");
-  CheckDec(ullv, "%llu", "%0*llu", "%*llu");
-
-  long long llv = static_cast<long long>(ullv);  // NOLINT(runtime/int)
-  CheckDec(llv, "%lld", "%0*lld", "%*lld");
-
-  if (sizeof(v) == sizeof(&v)) {
-    auto uintptr = static_cast<uintptr_t>(v);
-    void* ptr = reinterpret_cast<void*>(uintptr);
-    CheckHex(ptr, "%llx", "%0*llx", "%*llx");
-  }
-}
-
-void CheckHexDec32(uint32_t uv) {
-  CheckHex(uv, "%x", "%0*x", "%*x");
-  CheckDec(uv, "%u", "%0*u", "%*u");
-  int32_t v = static_cast<int32_t>(uv);
-  CheckDec(v, "%d", "%0*d", "%*d");
-
-  if (sizeof(v) == sizeof(&v)) {
-    auto uintptr = static_cast<uintptr_t>(v);
-    void* ptr = reinterpret_cast<void*>(uintptr);
-    CheckHex(ptr, "%x", "%0*x", "%*x");
-  }
-}
-
-void CheckAll(uint64_t v) {
-  CheckHexDec64(v);
-  CheckHexDec32(static_cast<uint32_t>(v));
-}
-
-void TestFastPrints() {
-  // Test all small ints; there aren't many and they're common.
-  for (int i = 0; i < 10000; i++) {
-    CheckAll(i);
-  }
-
-  CheckAll(std::numeric_limits<uint64_t>::max());
-  CheckAll(std::numeric_limits<uint64_t>::max() - 1);
-  CheckAll(std::numeric_limits<int64_t>::min());
-  CheckAll(std::numeric_limits<int64_t>::min() + 1);
-  CheckAll(std::numeric_limits<uint32_t>::max());
-  CheckAll(std::numeric_limits<uint32_t>::max() - 1);
-  CheckAll(std::numeric_limits<int32_t>::min());
-  CheckAll(std::numeric_limits<int32_t>::min() + 1);
-  CheckAll(999999999);              // fits in 32 bits
-  CheckAll(1000000000);             // fits in 32 bits
-  CheckAll(9999999999);             // doesn't fit in 32 bits
-  CheckAll(10000000000);            // doesn't fit in 32 bits
-  CheckAll(999999999999999999);     // fits in signed 64-bit
-  CheckAll(9999999999999999999u);   // fits in unsigned 64-bit, but not signed.
-  CheckAll(1000000000000000000);    // fits in signed 64-bit
-  CheckAll(10000000000000000000u);  // fits in unsigned 64-bit, but not signed.
-
-  CheckAll(999999999876543210);    // check all decimal digits, signed
-  CheckAll(9999999999876543210u);  // check all decimal digits, unsigned.
-  CheckAll(0x123456789abcdef0);    // check all hex digits
-  CheckAll(0x12345678);
-
-  int8_t minus_one_8bit = -1;
-  EXPECT_EQ("ff", absl::StrCat(absl::Hex(minus_one_8bit)));
-
-  int16_t minus_one_16bit = -1;
-  EXPECT_EQ("ffff", absl::StrCat(absl::Hex(minus_one_16bit)));
-}
-
-TEST(Numbers, TestFunctionsMovedOverFromNumbersMain) {
-  TestFastPrints();
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/str_format.h b/third_party/abseil_cpp/absl/strings/str_format.h
deleted file mode 100644
index 01465107e1..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_format.h
+++ /dev/null
@@ -1,813 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: str_format.h
-// -----------------------------------------------------------------------------
-//
-// The `str_format` library is a typesafe replacement for the family of
-// `printf()` string formatting routines within the `<cstdio>` standard library
-// header. Like the `printf` family, `str_format` uses a "format string" to
-// perform argument substitutions based on types. See the `FormatSpec` section
-// below for format string documentation.
-//
-// Example:
-//
-//   std::string s = absl::StrFormat(
-//                      "%s %s You have $%d!", "Hello", name, dollars);
-//
-// The library consists of the following basic utilities:
-//
-//   * `absl::StrFormat()`, a type-safe replacement for `std::sprintf()`, to
-//     write a format string to a `string` value.
-//   * `absl::StrAppendFormat()` to append a format string to a `string`
-//   * `absl::StreamFormat()` to more efficiently write a format string to a
-//     stream, such as`std::cout`.
-//   * `absl::PrintF()`, `absl::FPrintF()` and `absl::SNPrintF()` as
-//     replacements for `std::printf()`, `std::fprintf()` and `std::snprintf()`.
-//
-//     Note: a version of `std::sprintf()` is not supported as it is
-//     generally unsafe due to buffer overflows.
-//
-// Additionally, you can provide a format string (and its associated arguments)
-// using one of the following abstractions:
-//
-//   * A `FormatSpec` class template fully encapsulates a format string and its
-//     type arguments and is usually provided to `str_format` functions as a
-//     variadic argument of type `FormatSpec<Arg...>`. The `FormatSpec<Args...>`
-//     template is evaluated at compile-time, providing type safety.
-//   * A `ParsedFormat` instance, which encapsulates a specific, pre-compiled
-//     format string for a specific set of type(s), and which can be passed
-//     between API boundaries. (The `FormatSpec` type should not be used
-//     directly except as an argument type for wrapper functions.)
-//
-// The `str_format` library provides the ability to output its format strings to
-// arbitrary sink types:
-//
-//   * A generic `Format()` function to write outputs to arbitrary sink types,
-//     which must implement a `FormatRawSink` interface.
-//
-//   * A `FormatUntyped()` function that is similar to `Format()` except it is
-//     loosely typed. `FormatUntyped()` is not a template and does not perform
-//     any compile-time checking of the format string; instead, it returns a
-//     boolean from a runtime check.
-//
-// In addition, the `str_format` library provides extension points for
-// augmenting formatting to new types.  See "StrFormat Extensions" below.
-
-#ifndef ABSL_STRINGS_STR_FORMAT_H_
-#define ABSL_STRINGS_STR_FORMAT_H_
-
-#include <cstdio>
-#include <string>
-
-#include "absl/strings/internal/str_format/arg.h"  // IWYU pragma: export
-#include "absl/strings/internal/str_format/bind.h"  // IWYU pragma: export
-#include "absl/strings/internal/str_format/checker.h"  // IWYU pragma: export
-#include "absl/strings/internal/str_format/extension.h"  // IWYU pragma: export
-#include "absl/strings/internal/str_format/parser.h"  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// UntypedFormatSpec
-//
-// A type-erased class that can be used directly within untyped API entry
-// points. An `UntypedFormatSpec` is specifically used as an argument to
-// `FormatUntyped()`.
-//
-// Example:
-//
-//   absl::UntypedFormatSpec format("%d");
-//   std::string out;
-//   CHECK(absl::FormatUntyped(&out, format, {absl::FormatArg(1)}));
-class UntypedFormatSpec {
- public:
-  UntypedFormatSpec() = delete;
-  UntypedFormatSpec(const UntypedFormatSpec&) = delete;
-  UntypedFormatSpec& operator=(const UntypedFormatSpec&) = delete;
-
-  explicit UntypedFormatSpec(string_view s) : spec_(s) {}
-
- protected:
-  explicit UntypedFormatSpec(const str_format_internal::ParsedFormatBase* pc)
-      : spec_(pc) {}
-
- private:
-  friend str_format_internal::UntypedFormatSpecImpl;
-  str_format_internal::UntypedFormatSpecImpl spec_;
-};
-
-// FormatStreamed()
-//
-// Takes a streamable argument and returns an object that can print it
-// with '%s'. Allows printing of types that have an `operator<<` but no
-// intrinsic type support within `StrFormat()` itself.
-//
-// Example:
-//
-//   absl::StrFormat("%s", absl::FormatStreamed(obj));
-template <typename T>
-str_format_internal::StreamedWrapper<T> FormatStreamed(const T& v) {
-  return str_format_internal::StreamedWrapper<T>(v);
-}
-
-// FormatCountCapture
-//
-// This class provides a way to safely wrap `StrFormat()` captures of `%n`
-// conversions, which denote the number of characters written by a formatting
-// operation to this point, into an integer value.
-//
-// This wrapper is designed to allow safe usage of `%n` within `StrFormat(); in
-// the `printf()` family of functions, `%n` is not safe to use, as the `int *`
-// buffer can be used to capture arbitrary data.
-//
-// Example:
-//
-//   int n = 0;
-//   std::string s = absl::StrFormat("%s%d%n", "hello", 123,
-//                       absl::FormatCountCapture(&n));
-//   EXPECT_EQ(8, n);
-class FormatCountCapture {
- public:
-  explicit FormatCountCapture(int* p) : p_(p) {}
-
- private:
-  // FormatCountCaptureHelper is used to define FormatConvertImpl() for this
-  // class.
-  friend struct str_format_internal::FormatCountCaptureHelper;
-  // Unused() is here because of the false positive from -Wunused-private-field
-  // p_ is used in the templated function of the friend FormatCountCaptureHelper
-  // class.
-  int* Unused() { return p_; }
-  int* p_;
-};
-
-// FormatSpec
-//
-// The `FormatSpec` type defines the makeup of a format string within the
-// `str_format` library. It is a variadic class template that is evaluated at
-// compile-time, according to the format string and arguments that are passed to
-// it.
-//
-// You should not need to manipulate this type directly. You should only name it
-// if you are writing wrapper functions which accept format arguments that will
-// be provided unmodified to functions in this library. Such a wrapper function
-// might be a class method that provides format arguments and/or internally uses
-// the result of formatting.
-//
-// For a `FormatSpec` to be valid at compile-time, it must be provided as
-// either:
-//
-// * A `constexpr` literal or `absl::string_view`, which is how it most often
-//   used.
-// * A `ParsedFormat` instantiation, which ensures the format string is
-//   valid before use. (See below.)
-//
-// Example:
-//
-//   // Provided as a string literal.
-//   absl::StrFormat("Welcome to %s, Number %d!", "The Village", 6);
-//
-//   // Provided as a constexpr absl::string_view.
-//   constexpr absl::string_view formatString = "Welcome to %s, Number %d!";
-//   absl::StrFormat(formatString, "The Village", 6);
-//
-//   // Provided as a pre-compiled ParsedFormat object.
-//   // Note that this example is useful only for illustration purposes.
-//   absl::ParsedFormat<'s', 'd'> formatString("Welcome to %s, Number %d!");
-//   absl::StrFormat(formatString, "TheVillage", 6);
-//
-// A format string generally follows the POSIX syntax as used within the POSIX
-// `printf` specification.
-//
-// (See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html.)
-//
-// In specific, the `FormatSpec` supports the following type specifiers:
-//   * `c` for characters
-//   * `s` for strings
-//   * `d` or `i` for integers
-//   * `o` for unsigned integer conversions into octal
-//   * `x` or `X` for unsigned integer conversions into hex
-//   * `u` for unsigned integers
-//   * `f` or `F` for floating point values into decimal notation
-//   * `e` or `E` for floating point values into exponential notation
-//   * `a` or `A` for floating point values into hex exponential notation
-//   * `g` or `G` for floating point values into decimal or exponential
-//     notation based on their precision
-//   * `p` for pointer address values
-//   * `n` for the special case of writing out the number of characters
-//     written to this point. The resulting value must be captured within an
-//     `absl::FormatCountCapture` type.
-//
-// Implementation-defined behavior:
-//   * A null pointer provided to "%s" or "%p" is output as "(nil)".
-//   * A non-null pointer provided to "%p" is output in hex as if by %#x or
-//     %#lx.
-//
-// NOTE: `o`, `x\X` and `u` will convert signed values to their unsigned
-// counterpart before formatting.
-//
-// Examples:
-//     "%c", 'a'                -> "a"
-//     "%c", 32                 -> " "
-//     "%s", "C"                -> "C"
-//     "%s", std::string("C++") -> "C++"
-//     "%d", -10                -> "-10"
-//     "%o", 10                 -> "12"
-//     "%x", 16                 -> "10"
-//     "%f", 123456789          -> "123456789.000000"
-//     "%e", .01                -> "1.00000e-2"
-//     "%a", -3.0               -> "-0x1.8p+1"
-//     "%g", .01                -> "1e-2"
-//     "%p", (void*)&value      -> "0x7ffdeb6ad2a4"
-//
-//     int n = 0;
-//     std::string s = absl::StrFormat(
-//         "%s%d%n", "hello", 123, absl::FormatCountCapture(&n));
-//     EXPECT_EQ(8, n);
-//
-// The `FormatSpec` intrinsically supports all of these fundamental C++ types:
-//
-// *   Characters: `char`, `signed char`, `unsigned char`
-// *   Integers: `int`, `short`, `unsigned short`, `unsigned`, `long`,
-//         `unsigned long`, `long long`, `unsigned long long`
-// *   Floating-point: `float`, `double`, `long double`
-//
-// However, in the `str_format` library, a format conversion specifies a broader
-// C++ conceptual category instead of an exact type. For example, `%s` binds to
-// any string-like argument, so `std::string`, `absl::string_view`, and
-// `const char*` are all accepted. Likewise, `%d` accepts any integer-like
-// argument, etc.
-
-template <typename... Args>
-using FormatSpec = str_format_internal::FormatSpecTemplate<
-    str_format_internal::ArgumentToConv<Args>()...>;
-
-// ParsedFormat
-//
-// A `ParsedFormat` is a class template representing a preparsed `FormatSpec`,
-// with template arguments specifying the conversion characters used within the
-// format string. Such characters must be valid format type specifiers, and
-// these type specifiers are checked at compile-time.
-//
-// Instances of `ParsedFormat` can be created, copied, and reused to speed up
-// formatting loops. A `ParsedFormat` may either be constructed statically, or
-// dynamically through its `New()` factory function, which only constructs a
-// runtime object if the format is valid at that time.
-//
-// Example:
-//
-//   // Verified at compile time.
-//   absl::ParsedFormat<'s', 'd'> formatString("Welcome to %s, Number %d!");
-//   absl::StrFormat(formatString, "TheVillage", 6);
-//
-//   // Verified at runtime.
-//   auto format_runtime = absl::ParsedFormat<'d'>::New(format_string);
-//   if (format_runtime) {
-//     value = absl::StrFormat(*format_runtime, i);
-//   } else {
-//     ... error case ...
-//   }
-
-#if defined(__cpp_nontype_template_parameter_auto)
-// If C++17 is available, an 'extended' format is also allowed that can specify
-// multiple conversion characters per format argument, using a combination of
-// `absl::FormatConversionCharSet` enum values (logically a set union)
-//  via the `|` operator. (Single character-based arguments are still accepted,
-// but cannot be combined). Some common conversions also have predefined enum
-// values, such as `absl::FormatConversionCharSet::kIntegral`.
-//
-// Example:
-//   // Extended format supports multiple conversion characters per argument,
-//   // specified via a combination of `FormatConversionCharSet` enums.
-//   using MyFormat = absl::ParsedFormat<absl::FormatConversionCharSet::d |
-//                                       absl::FormatConversionCharSet::x>;
-//   MyFormat GetFormat(bool use_hex) {
-//     if (use_hex) return MyFormat("foo %x bar");
-//     return MyFormat("foo %d bar");
-//   }
-//   // `format` can be used with any value that supports 'd' and 'x',
-//   // like `int`.
-//   auto format = GetFormat(use_hex);
-//   value = StringF(format, i);
-template <auto... Conv>
-using ParsedFormat = absl::str_format_internal::ExtendedParsedFormat<
-    absl::str_format_internal::ToFormatConversionCharSet(Conv)...>;
-#else
-template <char... Conv>
-using ParsedFormat = str_format_internal::ExtendedParsedFormat<
-    absl::str_format_internal::ToFormatConversionCharSet(Conv)...>;
-#endif  // defined(__cpp_nontype_template_parameter_auto)
-
-// StrFormat()
-//
-// Returns a `string` given a `printf()`-style format string and zero or more
-// additional arguments. Use it as you would `sprintf()`. `StrFormat()` is the
-// primary formatting function within the `str_format` library, and should be
-// used in most cases where you need type-safe conversion of types into
-// formatted strings.
-//
-// The format string generally consists of ordinary character data along with
-// one or more format conversion specifiers (denoted by the `%` character).
-// Ordinary character data is returned unchanged into the result string, while
-// each conversion specification performs a type substitution from
-// `StrFormat()`'s other arguments. See the comments for `FormatSpec` for full
-// information on the makeup of this format string.
-//
-// Example:
-//
-//   std::string s = absl::StrFormat(
-//       "Welcome to %s, Number %d!", "The Village", 6);
-//   EXPECT_EQ("Welcome to The Village, Number 6!", s);
-//
-// Returns an empty string in case of error.
-template <typename... Args>
-ABSL_MUST_USE_RESULT std::string StrFormat(const FormatSpec<Args...>& format,
-                                           const Args&... args) {
-  return str_format_internal::FormatPack(
-      str_format_internal::UntypedFormatSpecImpl::Extract(format),
-      {str_format_internal::FormatArgImpl(args)...});
-}
-
-// StrAppendFormat()
-//
-// Appends to a `dst` string given a format string, and zero or more additional
-// arguments, returning `*dst` as a convenience for chaining purposes. Appends
-// nothing in case of error (but possibly alters its capacity).
-//
-// Example:
-//
-//   std::string orig("For example PI is approximately ");
-//   std::cout << StrAppendFormat(&orig, "%12.6f", 3.14);
-template <typename... Args>
-std::string& StrAppendFormat(std::string* dst,
-                             const FormatSpec<Args...>& format,
-                             const Args&... args) {
-  return str_format_internal::AppendPack(
-      dst, str_format_internal::UntypedFormatSpecImpl::Extract(format),
-      {str_format_internal::FormatArgImpl(args)...});
-}
-
-// StreamFormat()
-//
-// Writes to an output stream given a format string and zero or more arguments,
-// generally in a manner that is more efficient than streaming the result of
-// `absl:: StrFormat()`. The returned object must be streamed before the full
-// expression ends.
-//
-// Example:
-//
-//   std::cout << StreamFormat("%12.6f", 3.14);
-template <typename... Args>
-ABSL_MUST_USE_RESULT str_format_internal::Streamable StreamFormat(
-    const FormatSpec<Args...>& format, const Args&... args) {
-  return str_format_internal::Streamable(
-      str_format_internal::UntypedFormatSpecImpl::Extract(format),
-      {str_format_internal::FormatArgImpl(args)...});
-}
-
-// PrintF()
-//
-// Writes to stdout given a format string and zero or more arguments. This
-// function is functionally equivalent to `std::printf()` (and type-safe);
-// prefer `absl::PrintF()` over `std::printf()`.
-//
-// Example:
-//
-//   std::string_view s = "Ulaanbaatar";
-//   absl::PrintF("The capital of Mongolia is %s", s);
-//
-//   Outputs: "The capital of Mongolia is Ulaanbaatar"
-//
-template <typename... Args>
-int PrintF(const FormatSpec<Args...>& format, const Args&... args) {
-  return str_format_internal::FprintF(
-      stdout, str_format_internal::UntypedFormatSpecImpl::Extract(format),
-      {str_format_internal::FormatArgImpl(args)...});
-}
-
-// FPrintF()
-//
-// Writes to a file given a format string and zero or more arguments. This
-// function is functionally equivalent to `std::fprintf()` (and type-safe);
-// prefer `absl::FPrintF()` over `std::fprintf()`.
-//
-// Example:
-//
-//   std::string_view s = "Ulaanbaatar";
-//   absl::FPrintF(stdout, "The capital of Mongolia is %s", s);
-//
-//   Outputs: "The capital of Mongolia is Ulaanbaatar"
-//
-template <typename... Args>
-int FPrintF(std::FILE* output, const FormatSpec<Args...>& format,
-            const Args&... args) {
-  return str_format_internal::FprintF(
-      output, str_format_internal::UntypedFormatSpecImpl::Extract(format),
-      {str_format_internal::FormatArgImpl(args)...});
-}
-
-// SNPrintF()
-//
-// Writes to a sized buffer given a format string and zero or more arguments.
-// This function is functionally equivalent to `std::snprintf()` (and
-// type-safe); prefer `absl::SNPrintF()` over `std::snprintf()`.
-//
-// In particular, a successful call to `absl::SNPrintF()` writes at most `size`
-// bytes of the formatted output to `output`, including a NUL-terminator, and
-// returns the number of bytes that would have been written if truncation did
-// not occur. In the event of an error, a negative value is returned and `errno`
-// is set.
-//
-// Example:
-//
-//   std::string_view s = "Ulaanbaatar";
-//   char output[128];
-//   absl::SNPrintF(output, sizeof(output),
-//                  "The capital of Mongolia is %s", s);
-//
-//   Post-condition: output == "The capital of Mongolia is Ulaanbaatar"
-//
-template <typename... Args>
-int SNPrintF(char* output, std::size_t size, const FormatSpec<Args...>& format,
-             const Args&... args) {
-  return str_format_internal::SnprintF(
-      output, size, str_format_internal::UntypedFormatSpecImpl::Extract(format),
-      {str_format_internal::FormatArgImpl(args)...});
-}
-
-// -----------------------------------------------------------------------------
-// Custom Output Formatting Functions
-// -----------------------------------------------------------------------------
-
-// FormatRawSink
-//
-// FormatRawSink is a type erased wrapper around arbitrary sink objects
-// specifically used as an argument to `Format()`.
-//
-// All the object has to do define an overload of `AbslFormatFlush()` for the
-// sink, usually by adding a ADL-based free function in the same namespace as
-// the sink:
-//
-//   void AbslFormatFlush(MySink* dest, absl::string_view part);
-//
-// where `dest` is the pointer passed to `absl::Format()`. The function should
-// append `part` to `dest`.
-//
-// FormatRawSink does not own the passed sink object. The passed object must
-// outlive the FormatRawSink.
-class FormatRawSink {
- public:
-  // Implicitly convert from any type that provides the hook function as
-  // described above.
-  template <typename T,
-            typename = typename std::enable_if<std::is_constructible<
-                str_format_internal::FormatRawSinkImpl, T*>::value>::type>
-  FormatRawSink(T* raw)  // NOLINT
-      : sink_(raw) {}
-
- private:
-  friend str_format_internal::FormatRawSinkImpl;
-  str_format_internal::FormatRawSinkImpl sink_;
-};
-
-// Format()
-//
-// Writes a formatted string to an arbitrary sink object (implementing the
-// `absl::FormatRawSink` interface), using a format string and zero or more
-// additional arguments.
-//
-// By default, `std::string`, `std::ostream`, and `absl::Cord` are supported as
-// destination objects. If a `std::string` is used the formatted string is
-// appended to it.
-//
-// `absl::Format()` is a generic version of `absl::StrAppendFormat()`, for
-// custom sinks. The format string, like format strings for `StrFormat()`, is
-// checked at compile-time.
-//
-// On failure, this function returns `false` and the state of the sink is
-// unspecified.
-template <typename... Args>
-bool Format(FormatRawSink raw_sink, const FormatSpec<Args...>& format,
-            const Args&... args) {
-  return str_format_internal::FormatUntyped(
-      str_format_internal::FormatRawSinkImpl::Extract(raw_sink),
-      str_format_internal::UntypedFormatSpecImpl::Extract(format),
-      {str_format_internal::FormatArgImpl(args)...});
-}
-
-// FormatArg
-//
-// A type-erased handle to a format argument specifically used as an argument to
-// `FormatUntyped()`. You may construct `FormatArg` by passing
-// reference-to-const of any printable type. `FormatArg` is both copyable and
-// assignable. The source data must outlive the `FormatArg` instance. See
-// example below.
-//
-using FormatArg = str_format_internal::FormatArgImpl;
-
-// FormatUntyped()
-//
-// Writes a formatted string to an arbitrary sink object (implementing the
-// `absl::FormatRawSink` interface), using an `UntypedFormatSpec` and zero or
-// more additional arguments.
-//
-// This function acts as the most generic formatting function in the
-// `str_format` library. The caller provides a raw sink, an unchecked format
-// string, and (usually) a runtime specified list of arguments; no compile-time
-// checking of formatting is performed within this function. As a result, a
-// caller should check the return value to verify that no error occurred.
-// On failure, this function returns `false` and the state of the sink is
-// unspecified.
-//
-// The arguments are provided in an `absl::Span<const absl::FormatArg>`.
-// Each `absl::FormatArg` object binds to a single argument and keeps a
-// reference to it. The values used to create the `FormatArg` objects must
-// outlive this function call. (See `str_format_arg.h` for information on
-// the `FormatArg` class.)_
-//
-// Example:
-//
-//   std::optional<std::string> FormatDynamic(
-//       const std::string& in_format,
-//       const vector<std::string>& in_args) {
-//     std::string out;
-//     std::vector<absl::FormatArg> args;
-//     for (const auto& v : in_args) {
-//       // It is important that 'v' is a reference to the objects in in_args.
-//       // The values we pass to FormatArg must outlive the call to
-//       // FormatUntyped.
-//       args.emplace_back(v);
-//     }
-//     absl::UntypedFormatSpec format(in_format);
-//     if (!absl::FormatUntyped(&out, format, args)) {
-//       return std::nullopt;
-//     }
-//     return std::move(out);
-//   }
-//
-ABSL_MUST_USE_RESULT inline bool FormatUntyped(
-    FormatRawSink raw_sink, const UntypedFormatSpec& format,
-    absl::Span<const FormatArg> args) {
-  return str_format_internal::FormatUntyped(
-      str_format_internal::FormatRawSinkImpl::Extract(raw_sink),
-      str_format_internal::UntypedFormatSpecImpl::Extract(format), args);
-}
-
-//------------------------------------------------------------------------------
-// StrFormat Extensions
-//------------------------------------------------------------------------------
-//
-// AbslFormatConvert()
-//
-// The StrFormat library provides a customization API for formatting
-// user-defined types using absl::StrFormat(). The API relies on detecting an
-// overload in the user-defined type's namespace of a free (non-member)
-// `AbslFormatConvert()` function, usually as a friend definition with the
-// following signature:
-//
-// absl::FormatConvertResult<...> AbslFormatConvert(
-//     const X& value,
-//     const absl::FormatConversionSpec& spec,
-//     absl::FormatSink *sink);
-//
-// An `AbslFormatConvert()` overload for a type should only be declared in the
-// same file and namespace as said type.
-//
-// The abstractions within this definition include:
-//
-// * An `absl::FormatConversionSpec` to specify the fields to pull from a
-//   user-defined type's format string
-// * An `absl::FormatSink` to hold the converted string data during the
-//   conversion process.
-// * An `absl::FormatConvertResult` to hold the status of the returned
-//   formatting operation
-//
-// The return type encodes all the conversion characters that your
-// AbslFormatConvert() routine accepts.  The return value should be {true}.
-// A return value of {false} will result in `StrFormat()` returning
-// an empty string.  This result will be propagated to the result of
-// `FormatUntyped`.
-//
-// Example:
-//
-// struct Point {
-//   // To add formatting support to `Point`, we simply need to add a free
-//   // (non-member) function `AbslFormatConvert()`.  This method interprets
-//   // `spec` to print in the request format. The allowed conversion characters
-//   // can be restricted via the type of the result, in this example
-//   // string and integral formatting are allowed (but not, for instance
-//   // floating point characters like "%f").  You can add such a free function
-//   // using a friend declaration within the body of the class:
-//   friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString |
-//                                    absl::FormatConversionCharSet::kIntegral>
-//   AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec,
-//                     absl::FormatSink* s) {
-//     if (spec.conversion_char() == absl::FormatConversionChar::s) {
-//       s->Append(absl::StrCat("x=", p.x, " y=", p.y));
-//     } else {
-//       s->Append(absl::StrCat(p.x, ",", p.y));
-//     }
-//     return {true};
-//   }
-//
-//   int x;
-//   int y;
-// };
-
-// clang-format off
-
-// FormatConversionChar
-//
-// Specifies the formatting character provided in the format string
-// passed to `StrFormat()`.
-enum class FormatConversionChar : uint8_t {
-  c, s,                    // text
-  d, i, o, u, x, X,        // int
-  f, F, e, E, g, G, a, A,  // float
-  n, p                     // misc
-};
-// clang-format on
-
-// FormatConversionSpec
-//
-// Specifies modifications to the conversion of the format string, through use
-// of one or more format flags in the source format string.
-class FormatConversionSpec {
- public:
-  // FormatConversionSpec::is_basic()
-  //
-  // Indicates that width and precision are not specified, and no additional
-  // flags are set for this conversion character in the format string.
-  bool is_basic() const { return impl_.is_basic(); }
-
-  // FormatConversionSpec::has_left_flag()
-  //
-  // Indicates whether the result should be left justified for this conversion
-  // character in the format string. This flag is set through use of a '-'
-  // character in the format string. E.g. "%-s"
-  bool has_left_flag() const { return impl_.has_left_flag(); }
-
-  // FormatConversionSpec::has_show_pos_flag()
-  //
-  // Indicates whether a sign column is prepended to the result for this
-  // conversion character in the format string, even if the result is positive.
-  // This flag is set through use of a '+' character in the format string.
-  // E.g. "%+d"
-  bool has_show_pos_flag() const { return impl_.has_show_pos_flag(); }
-
-  // FormatConversionSpec::has_sign_col_flag()
-  //
-  // Indicates whether a mandatory sign column is added to the result for this
-  // conversion character. This flag is set through use of a space character
-  // (' ') in the format string. E.g. "% i"
-  bool has_sign_col_flag() const { return impl_.has_sign_col_flag(); }
-
-  // FormatConversionSpec::has_alt_flag()
-  //
-  // Indicates whether an "alternate" format is applied to the result for this
-  // conversion character. Alternative forms depend on the type of conversion
-  // character, and unallowed alternatives are undefined. This flag is set
-  // through use of a '#' character in the format string. E.g. "%#h"
-  bool has_alt_flag() const { return impl_.has_alt_flag(); }
-
-  // FormatConversionSpec::has_zero_flag()
-  //
-  // Indicates whether zeroes should be prepended to the result for this
-  // conversion character instead of spaces. This flag is set through use of the
-  // '0' character in the format string. E.g. "%0f"
-  bool has_zero_flag() const { return impl_.has_zero_flag(); }
-
-  // FormatConversionSpec::conversion_char()
-  //
-  // Returns the underlying conversion character.
-  FormatConversionChar conversion_char() const {
-    return impl_.conversion_char();
-  }
-
-  // FormatConversionSpec::width()
-  //
-  // Returns the specified width (indicated through use of a non-zero integer
-  // value or '*' character) of the conversion character. If width is
-  // unspecified, it returns a negative value.
-  int width() const { return impl_.width(); }
-
-  // FormatConversionSpec::precision()
-  //
-  // Returns the specified precision (through use of the '.' character followed
-  // by a non-zero integer value or '*' character) of the conversion character.
-  // If precision is unspecified, it returns a negative value.
-  int precision() const { return impl_.precision(); }
-
- private:
-  explicit FormatConversionSpec(
-      str_format_internal::FormatConversionSpecImpl impl)
-      : impl_(impl) {}
-
-  friend str_format_internal::FormatConversionSpecImpl;
-
-  absl::str_format_internal::FormatConversionSpecImpl impl_;
-};
-
-// Type safe OR operator for FormatConversionCharSet to allow accepting multiple
-// conversion chars in custom format converters.
-constexpr FormatConversionCharSet operator|(FormatConversionCharSet a,
-                                            FormatConversionCharSet b) {
-  return static_cast<FormatConversionCharSet>(static_cast<uint64_t>(a) |
-                                              static_cast<uint64_t>(b));
-}
-
-// FormatConversionCharSet
-//
-// Specifies the _accepted_ conversion types as a template parameter to
-// FormatConvertResult for custom implementations of `AbslFormatConvert`.
-// Note the helper predefined alias definitions (kIntegral, etc.) below.
-enum class FormatConversionCharSet : uint64_t {
-  // text
-  c = str_format_internal::FormatConversionCharToConvInt('c'),
-  s = str_format_internal::FormatConversionCharToConvInt('s'),
-  // integer
-  d = str_format_internal::FormatConversionCharToConvInt('d'),
-  i = str_format_internal::FormatConversionCharToConvInt('i'),
-  o = str_format_internal::FormatConversionCharToConvInt('o'),
-  u = str_format_internal::FormatConversionCharToConvInt('u'),
-  x = str_format_internal::FormatConversionCharToConvInt('x'),
-  X = str_format_internal::FormatConversionCharToConvInt('X'),
-  // Float
-  f = str_format_internal::FormatConversionCharToConvInt('f'),
-  F = str_format_internal::FormatConversionCharToConvInt('F'),
-  e = str_format_internal::FormatConversionCharToConvInt('e'),
-  E = str_format_internal::FormatConversionCharToConvInt('E'),
-  g = str_format_internal::FormatConversionCharToConvInt('g'),
-  G = str_format_internal::FormatConversionCharToConvInt('G'),
-  a = str_format_internal::FormatConversionCharToConvInt('a'),
-  A = str_format_internal::FormatConversionCharToConvInt('A'),
-  // misc
-  n = str_format_internal::FormatConversionCharToConvInt('n'),
-  p = str_format_internal::FormatConversionCharToConvInt('p'),
-
-  // Used for width/precision '*' specification.
-  kStar = static_cast<uint64_t>(
-      absl::str_format_internal::FormatConversionCharSetInternal::kStar),
-  // Some predefined values:
-  kIntegral = d | i | u | o | x | X,
-  kFloating = a | e | f | g | A | E | F | G,
-  kNumeric = kIntegral | kFloating,
-  kString = s,
-  kPointer = p,
-};
-
-// FormatSink
-//
-// An abstraction to which conversions write their string data.
-//
-class FormatSink {
- public:
-  // Appends `count` copies of `ch`.
-  void Append(size_t count, char ch) { sink_->Append(count, ch); }
-
-  void Append(string_view v) { sink_->Append(v); }
-
-  // Appends the first `precision` bytes of `v`. If this is less than
-  // `width`, spaces will be appended first (if `left` is false), or
-  // after (if `left` is true) to ensure the total amount appended is
-  // at least `width`.
-  bool PutPaddedString(string_view v, int width, int precision, bool left) {
-    return sink_->PutPaddedString(v, width, precision, left);
-  }
-
- private:
-  friend str_format_internal::FormatSinkImpl;
-  explicit FormatSink(str_format_internal::FormatSinkImpl* s) : sink_(s) {}
-  str_format_internal::FormatSinkImpl* sink_;
-};
-
-// FormatConvertResult
-//
-// Indicates whether a call to AbslFormatConvert() was successful.
-// This return type informs the StrFormat extension framework (through
-// ADL but using the return type) of what conversion characters are supported.
-// It is strongly discouraged to return {false}, as this will result in an
-// empty string in StrFormat.
-template <FormatConversionCharSet C>
-struct FormatConvertResult {
-  bool value;
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_STR_FORMAT_H_
diff --git a/third_party/abseil_cpp/absl/strings/str_format_test.cc b/third_party/abseil_cpp/absl/strings/str_format_test.cc
deleted file mode 100644
index c60027ad29..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_format_test.cc
+++ /dev/null
@@ -1,774 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_format.h"
-
-#include <cstdarg>
-#include <cstdint>
-#include <cstdio>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/cord.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-using str_format_internal::FormatArgImpl;
-
-using FormatEntryPointTest = ::testing::Test;
-
-TEST_F(FormatEntryPointTest, Format) {
-  std::string sink;
-  EXPECT_TRUE(Format(&sink, "A format %d", 123));
-  EXPECT_EQ("A format 123", sink);
-  sink.clear();
-
-  ParsedFormat<'d'> pc("A format %d");
-  EXPECT_TRUE(Format(&sink, pc, 123));
-  EXPECT_EQ("A format 123", sink);
-}
-TEST_F(FormatEntryPointTest, UntypedFormat) {
-  constexpr const char* formats[] = {
-    "",
-    "a",
-    "%80d",
-#if !defined(_MSC_VER) && !defined(__ANDROID__) && !defined(__native_client__)
-    // MSVC, NaCL and Android don't support positional syntax.
-    "complicated multipart %% %1$d format %1$0999d",
-#endif  // _MSC_VER
-  };
-  for (const char* fmt : formats) {
-    std::string actual;
-    int i = 123;
-    FormatArgImpl arg_123(i);
-    absl::Span<const FormatArgImpl> args(&arg_123, 1);
-    UntypedFormatSpec format(fmt);
-
-    EXPECT_TRUE(FormatUntyped(&actual, format, args));
-    char buf[4096]{};
-    snprintf(buf, sizeof(buf), fmt, 123);
-    EXPECT_EQ(
-        str_format_internal::FormatPack(
-            str_format_internal::UntypedFormatSpecImpl::Extract(format), args),
-        buf);
-    EXPECT_EQ(actual, buf);
-  }
-  // The internal version works with a preparsed format.
-  ParsedFormat<'d'> pc("A format %d");
-  int i = 345;
-  FormatArg arg(i);
-  std::string out;
-  EXPECT_TRUE(str_format_internal::FormatUntyped(
-      &out, str_format_internal::UntypedFormatSpecImpl(&pc), {&arg, 1}));
-  EXPECT_EQ("A format 345", out);
-}
-
-TEST_F(FormatEntryPointTest, StringFormat) {
-  EXPECT_EQ("123", StrFormat("%d", 123));
-  constexpr absl::string_view view("=%d=", 4);
-  EXPECT_EQ("=123=", StrFormat(view, 123));
-}
-
-TEST_F(FormatEntryPointTest, AppendFormat) {
-  std::string s;
-  std::string& r = StrAppendFormat(&s, "%d", 123);
-  EXPECT_EQ(&s, &r);  // should be same object
-  EXPECT_EQ("123", r);
-}
-
-TEST_F(FormatEntryPointTest, AppendFormatFail) {
-  std::string s = "orig";
-
-  UntypedFormatSpec format(" more %d");
-  FormatArgImpl arg("not an int");
-
-  EXPECT_EQ("orig",
-            str_format_internal::AppendPack(
-                &s, str_format_internal::UntypedFormatSpecImpl::Extract(format),
-                {&arg, 1}));
-}
-
-
-TEST_F(FormatEntryPointTest, ManyArgs) {
-  EXPECT_EQ("24", StrFormat("%24$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
-                            14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24));
-  EXPECT_EQ("60", StrFormat("%60$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
-                            14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
-                            27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
-                            40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
-                            53, 54, 55, 56, 57, 58, 59, 60));
-}
-
-TEST_F(FormatEntryPointTest, Preparsed) {
-  ParsedFormat<'d'> pc("%d");
-  EXPECT_EQ("123", StrFormat(pc, 123));
-  // rvalue ok?
-  EXPECT_EQ("123", StrFormat(ParsedFormat<'d'>("%d"), 123));
-  constexpr absl::string_view view("=%d=", 4);
-  EXPECT_EQ("=123=", StrFormat(ParsedFormat<'d'>(view), 123));
-}
-
-TEST_F(FormatEntryPointTest, FormatCountCapture) {
-  int n = 0;
-  EXPECT_EQ("", StrFormat("%n", FormatCountCapture(&n)));
-  EXPECT_EQ(0, n);
-  EXPECT_EQ("123", StrFormat("%d%n", 123, FormatCountCapture(&n)));
-  EXPECT_EQ(3, n);
-}
-
-TEST_F(FormatEntryPointTest, FormatCountCaptureWrongType) {
-  // Should reject int*.
-  int n = 0;
-  UntypedFormatSpec format("%d%n");
-  int i = 123, *ip = &n;
-  FormatArgImpl args[2] = {FormatArgImpl(i), FormatArgImpl(ip)};
-
-  EXPECT_EQ("", str_format_internal::FormatPack(
-                    str_format_internal::UntypedFormatSpecImpl::Extract(format),
-                    absl::MakeSpan(args)));
-}
-
-TEST_F(FormatEntryPointTest, FormatCountCaptureMultiple) {
-  int n1 = 0;
-  int n2 = 0;
-  EXPECT_EQ("    1         2",
-            StrFormat("%5d%n%10d%n", 1, FormatCountCapture(&n1), 2,
-                      FormatCountCapture(&n2)));
-  EXPECT_EQ(5, n1);
-  EXPECT_EQ(15, n2);
-}
-
-TEST_F(FormatEntryPointTest, FormatCountCaptureExample) {
-  int n;
-  std::string s;
-  StrAppendFormat(&s, "%s: %n%s\n", "(1,1)", FormatCountCapture(&n), "(1,2)");
-  StrAppendFormat(&s, "%*s%s\n", n, "", "(2,2)");
-  EXPECT_EQ(7, n);
-  EXPECT_EQ(
-      "(1,1): (1,2)\n"
-      "       (2,2)\n",
-      s);
-}
-
-TEST_F(FormatEntryPointTest, Stream) {
-  const std::string formats[] = {
-    "",
-    "a",
-    "%80d",
-    "%d %u %c %s %f %g",
-#if !defined(_MSC_VER) && !defined(__ANDROID__) && !defined(__native_client__)
-    // MSVC, NaCL and Android don't support positional syntax.
-    "complicated multipart %% %1$d format %1$080d",
-#endif  // _MSC_VER
-  };
-  std::string buf(4096, '\0');
-  for (const auto& fmt : formats) {
-    const auto parsed =
-        ParsedFormat<'d', 'u', 'c', 's', 'f', 'g'>::NewAllowIgnored(fmt);
-    std::ostringstream oss;
-    oss << StreamFormat(*parsed, 123, 3, 49, "multistreaming!!!", 1.01, 1.01);
-    int fmt_result = snprintf(&*buf.begin(), buf.size(), fmt.c_str(),  //
-                                 123, 3, 49, "multistreaming!!!", 1.01, 1.01);
-    ASSERT_TRUE(oss) << fmt;
-    ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size())
-        << fmt_result;
-    EXPECT_EQ(buf.c_str(), oss.str());
-  }
-}
-
-TEST_F(FormatEntryPointTest, StreamOk) {
-  std::ostringstream oss;
-  oss << StreamFormat("hello %d", 123);
-  EXPECT_EQ("hello 123", oss.str());
-  EXPECT_TRUE(oss.good());
-}
-
-TEST_F(FormatEntryPointTest, StreamFail) {
-  std::ostringstream oss;
-  UntypedFormatSpec format("hello %d");
-  FormatArgImpl arg("non-numeric");
-  oss << str_format_internal::Streamable(
-      str_format_internal::UntypedFormatSpecImpl::Extract(format), {&arg, 1});
-  EXPECT_EQ("hello ", oss.str());  // partial write
-  EXPECT_TRUE(oss.fail());
-}
-
-std::string WithSnprintf(const char* fmt, ...) {
-  std::string buf;
-  buf.resize(128);
-  va_list va;
-  va_start(va, fmt);
-  int r = vsnprintf(&*buf.begin(), buf.size(), fmt, va);
-  va_end(va);
-  EXPECT_GE(r, 0);
-  EXPECT_LT(r, buf.size());
-  buf.resize(r);
-  return buf;
-}
-
-TEST_F(FormatEntryPointTest, FloatPrecisionArg) {
-  // Test that positional parameters for width and precision
-  // are indexed to precede the value.
-  // Also sanity check the same formats against snprintf.
-  EXPECT_EQ("0.1", StrFormat("%.1f", 0.1));
-  EXPECT_EQ("0.1", WithSnprintf("%.1f", 0.1));
-  EXPECT_EQ("  0.1", StrFormat("%*.1f", 5, 0.1));
-  EXPECT_EQ("  0.1", WithSnprintf("%*.1f", 5, 0.1));
-  EXPECT_EQ("0.1", StrFormat("%.*f", 1, 0.1));
-  EXPECT_EQ("0.1", WithSnprintf("%.*f", 1, 0.1));
-  EXPECT_EQ("  0.1", StrFormat("%*.*f", 5, 1, 0.1));
-  EXPECT_EQ("  0.1", WithSnprintf("%*.*f", 5, 1, 0.1));
-}
-namespace streamed_test {
-struct X {};
-std::ostream& operator<<(std::ostream& os, const X&) {
-  return os << "X";
-}
-}  // streamed_test
-
-TEST_F(FormatEntryPointTest, FormatStreamed) {
-  EXPECT_EQ("123", StrFormat("%s", FormatStreamed(123)));
-  EXPECT_EQ("  123", StrFormat("%5s", FormatStreamed(123)));
-  EXPECT_EQ("123  ", StrFormat("%-5s", FormatStreamed(123)));
-  EXPECT_EQ("X", StrFormat("%s", FormatStreamed(streamed_test::X())));
-  EXPECT_EQ("123", StrFormat("%s", FormatStreamed(StreamFormat("%d", 123))));
-}
-
-// Helper class that creates a temporary file and exposes a FILE* to it.
-// It will close the file on destruction.
-class TempFile {
- public:
-  TempFile() : file_(std::tmpfile()) {}
-  ~TempFile() { std::fclose(file_); }
-
-  std::FILE* file() const { return file_; }
-
-  // Read the file into a string.
-  std::string ReadFile() {
-    std::fseek(file_, 0, SEEK_END);
-    int size = std::ftell(file_);
-    EXPECT_GT(size, 0);
-    std::rewind(file_);
-    std::string str(2 * size, ' ');
-    int read_bytes = std::fread(&str[0], 1, str.size(), file_);
-    EXPECT_EQ(read_bytes, size);
-    str.resize(read_bytes);
-    EXPECT_TRUE(std::feof(file_));
-    return str;
-  }
-
- private:
-  std::FILE* file_;
-};
-
-TEST_F(FormatEntryPointTest, FPrintF) {
-  TempFile tmp;
-  int result =
-      FPrintF(tmp.file(), "STRING: %s NUMBER: %010d", std::string("ABC"), -19);
-  EXPECT_EQ(result, 30);
-  EXPECT_EQ(tmp.ReadFile(), "STRING: ABC NUMBER: -000000019");
-}
-
-TEST_F(FormatEntryPointTest, FPrintFError) {
-  errno = 0;
-  int result = FPrintF(stdin, "ABC");
-  EXPECT_LT(result, 0);
-  EXPECT_EQ(errno, EBADF);
-}
-
-#ifdef __GLIBC__
-TEST_F(FormatEntryPointTest, FprintfTooLarge) {
-  std::FILE* f = std::fopen("/dev/null", "w");
-  int width = 2000000000;
-  errno = 0;
-  int result = FPrintF(f, "%*d %*d", width, 0, width, 0);
-  EXPECT_LT(result, 0);
-  EXPECT_EQ(errno, EFBIG);
-  std::fclose(f);
-}
-
-TEST_F(FormatEntryPointTest, PrintF) {
-  int stdout_tmp = dup(STDOUT_FILENO);
-
-  TempFile tmp;
-  std::fflush(stdout);
-  dup2(fileno(tmp.file()), STDOUT_FILENO);
-
-  int result = PrintF("STRING: %s NUMBER: %010d", std::string("ABC"), -19);
-
-  std::fflush(stdout);
-  dup2(stdout_tmp, STDOUT_FILENO);
-  close(stdout_tmp);
-
-  EXPECT_EQ(result, 30);
-  EXPECT_EQ(tmp.ReadFile(), "STRING: ABC NUMBER: -000000019");
-}
-#endif  // __GLIBC__
-
-TEST_F(FormatEntryPointTest, SNPrintF) {
-  char buffer[16];
-  int result =
-      SNPrintF(buffer, sizeof(buffer), "STRING: %s", std::string("ABC"));
-  EXPECT_EQ(result, 11);
-  EXPECT_EQ(std::string(buffer), "STRING: ABC");
-
-  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456);
-  EXPECT_EQ(result, 14);
-  EXPECT_EQ(std::string(buffer), "NUMBER: 123456");
-
-  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 1234567);
-  EXPECT_EQ(result, 15);
-  EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
-
-  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 12345678);
-  EXPECT_EQ(result, 16);
-  EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
-
-  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456789);
-  EXPECT_EQ(result, 17);
-  EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
-
-  result = SNPrintF(nullptr, 0, "Just checking the %s of the output.", "size");
-  EXPECT_EQ(result, 37);
-}
-
-TEST(StrFormat, BehavesAsDocumented) {
-  std::string s = absl::StrFormat("%s, %d!", "Hello", 123);
-  EXPECT_EQ("Hello, 123!", s);
-  // The format of a replacement is
-  // '%'[position][flags][width['.'precision]][length_modifier][format]
-  EXPECT_EQ(absl::StrFormat("%1$+3.2Lf", 1.1), "+1.10");
-  // Text conversion:
-  //     "c" - Character.              Eg: 'a' -> "A", 20 -> " "
-  EXPECT_EQ(StrFormat("%c", 'a'), "a");
-  EXPECT_EQ(StrFormat("%c", 0x20), " ");
-  //           Formats char and integral types: int, long, uint64_t, etc.
-  EXPECT_EQ(StrFormat("%c", int{'a'}), "a");
-  EXPECT_EQ(StrFormat("%c", long{'a'}), "a");  // NOLINT
-  EXPECT_EQ(StrFormat("%c", uint64_t{'a'}), "a");
-  //     "s" - string       Eg: "C" -> "C", std::string("C++") -> "C++"
-  //           Formats std::string, char*, string_view, and Cord.
-  EXPECT_EQ(StrFormat("%s", "C"), "C");
-  EXPECT_EQ(StrFormat("%s", std::string("C++")), "C++");
-  EXPECT_EQ(StrFormat("%s", string_view("view")), "view");
-  EXPECT_EQ(StrFormat("%s", absl::Cord("cord")), "cord");
-  // Integral Conversion
-  //     These format integral types: char, int, long, uint64_t, etc.
-  EXPECT_EQ(StrFormat("%d", char{10}), "10");
-  EXPECT_EQ(StrFormat("%d", int{10}), "10");
-  EXPECT_EQ(StrFormat("%d", long{10}), "10");  // NOLINT
-  EXPECT_EQ(StrFormat("%d", uint64_t{10}), "10");
-  //     d,i - signed decimal          Eg: -10 -> "-10"
-  EXPECT_EQ(StrFormat("%d", -10), "-10");
-  EXPECT_EQ(StrFormat("%i", -10), "-10");
-  //      o  - octal                   Eg:  10 -> "12"
-  EXPECT_EQ(StrFormat("%o", 10), "12");
-  //      u  - unsigned decimal        Eg:  10 -> "10"
-  EXPECT_EQ(StrFormat("%u", 10), "10");
-  //     x/X - lower,upper case hex    Eg:  10 -> "a"/"A"
-  EXPECT_EQ(StrFormat("%x", 10), "a");
-  EXPECT_EQ(StrFormat("%X", 10), "A");
-  // Floating-point, with upper/lower-case output.
-  //     These format floating points types: float, double, long double, etc.
-  EXPECT_EQ(StrFormat("%.1f", float{1}), "1.0");
-  EXPECT_EQ(StrFormat("%.1f", double{1}), "1.0");
-  const long double long_double = 1.0;
-  EXPECT_EQ(StrFormat("%.1f", long_double), "1.0");
-  //     These also format integral types: char, int, long, uint64_t, etc.:
-  EXPECT_EQ(StrFormat("%.1f", char{1}), "1.0");
-  EXPECT_EQ(StrFormat("%.1f", int{1}), "1.0");
-  EXPECT_EQ(StrFormat("%.1f", long{1}), "1.0");  // NOLINT
-  EXPECT_EQ(StrFormat("%.1f", uint64_t{1}), "1.0");
-  //     f/F - decimal.                Eg: 123456789 -> "123456789.000000"
-  EXPECT_EQ(StrFormat("%f", 123456789), "123456789.000000");
-  EXPECT_EQ(StrFormat("%F", 123456789), "123456789.000000");
-  //     e/E - exponentiated           Eg: .01 -> "1.00000e-2"/"1.00000E-2"
-  EXPECT_EQ(StrFormat("%e", .01), "1.000000e-02");
-  EXPECT_EQ(StrFormat("%E", .01), "1.000000E-02");
-  //     g/G - exponentiate to fit     Eg: .01 -> "0.01", 1e10 ->"1e+10"/"1E+10"
-  EXPECT_EQ(StrFormat("%g", .01), "0.01");
-  EXPECT_EQ(StrFormat("%g", 1e10), "1e+10");
-  EXPECT_EQ(StrFormat("%G", 1e10), "1E+10");
-  //     a/A - lower,upper case hex    Eg: -3.0 -> "-0x1.8p+1"/"-0X1.8P+1"
-
-// On Android platform <=21, there is a regression in hexfloat formatting.
-#if !defined(__ANDROID_API__) || __ANDROID_API__ > 21
-  EXPECT_EQ(StrFormat("%.1a", -3.0), "-0x1.8p+1");  // .1 to fix MSVC output
-  EXPECT_EQ(StrFormat("%.1A", -3.0), "-0X1.8P+1");  // .1 to fix MSVC output
-#endif
-
-  // Other conversion
-  int64_t value = 0x7ffdeb4;
-  auto ptr_value = static_cast<uintptr_t>(value);
-  const int& something = *reinterpret_cast<const int*>(ptr_value);
-  EXPECT_EQ(StrFormat("%p", &something), StrFormat("0x%x", ptr_value));
-
-  // Output widths are supported, with optional flags.
-  EXPECT_EQ(StrFormat("%3d", 1), "  1");
-  EXPECT_EQ(StrFormat("%3d", 123456), "123456");
-  EXPECT_EQ(StrFormat("%06.2f", 1.234), "001.23");
-  EXPECT_EQ(StrFormat("%+d", 1), "+1");
-  EXPECT_EQ(StrFormat("% d", 1), " 1");
-  EXPECT_EQ(StrFormat("%-4d", -1), "-1  ");
-  EXPECT_EQ(StrFormat("%#o", 10), "012");
-  EXPECT_EQ(StrFormat("%#x", 15), "0xf");
-  EXPECT_EQ(StrFormat("%04d", 8), "0008");
-  // Posix positional substitution.
-  EXPECT_EQ(absl::StrFormat("%2$s, %3$s, %1$s!", "vici", "veni", "vidi"),
-            "veni, vidi, vici!");
-  // Length modifiers are ignored.
-  EXPECT_EQ(StrFormat("%hhd", int{1}), "1");
-  EXPECT_EQ(StrFormat("%hd", int{1}), "1");
-  EXPECT_EQ(StrFormat("%ld", int{1}), "1");
-  EXPECT_EQ(StrFormat("%lld", int{1}), "1");
-  EXPECT_EQ(StrFormat("%Ld", int{1}), "1");
-  EXPECT_EQ(StrFormat("%jd", int{1}), "1");
-  EXPECT_EQ(StrFormat("%zd", int{1}), "1");
-  EXPECT_EQ(StrFormat("%td", int{1}), "1");
-  EXPECT_EQ(StrFormat("%qd", int{1}), "1");
-}
-
-using str_format_internal::ExtendedParsedFormat;
-using str_format_internal::ParsedFormatBase;
-
-struct SummarizeConsumer {
-  std::string* out;
-  explicit SummarizeConsumer(std::string* out) : out(out) {}
-
-  bool Append(string_view s) {
-    *out += "[" + std::string(s) + "]";
-    return true;
-  }
-
-  bool ConvertOne(const str_format_internal::UnboundConversion& conv,
-                  string_view s) {
-    *out += "{";
-    *out += std::string(s);
-    *out += ":";
-    *out += std::to_string(conv.arg_position) + "$";
-    if (conv.width.is_from_arg()) {
-      *out += std::to_string(conv.width.get_from_arg()) + "$*";
-    }
-    if (conv.precision.is_from_arg()) {
-      *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
-    }
-    *out += str_format_internal::FormatConversionCharToChar(conv.conv);
-    *out += "}";
-    return true;
-  }
-};
-
-std::string SummarizeParsedFormat(const ParsedFormatBase& pc) {
-  std::string out;
-  if (!pc.ProcessFormat(SummarizeConsumer(&out))) out += "!";
-  return out;
-}
-
-using ParsedFormatTest = ::testing::Test;
-
-TEST_F(ParsedFormatTest, SimpleChecked) {
-  EXPECT_EQ("[ABC]{d:1$d}[DEF]",
-            SummarizeParsedFormat(ParsedFormat<'d'>("ABC%dDEF")));
-  EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}",
-            SummarizeParsedFormat(ParsedFormat<'s', 'd', 'f'>("%sFFF%dZZZ%f")));
-  EXPECT_EQ("{s:1$s}[ ]{.*d:3$.2$*d}",
-            SummarizeParsedFormat(ParsedFormat<'s', '*', 'd'>("%s %.*d")));
-}
-
-TEST_F(ParsedFormatTest, SimpleUncheckedCorrect) {
-  auto f = ParsedFormat<'d'>::New("ABC%dDEF");
-  ASSERT_TRUE(f);
-  EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
-
-  std::string format = "%sFFF%dZZZ%f";
-  auto f2 = ParsedFormat<'s', 'd', 'f'>::New(format);
-
-  ASSERT_TRUE(f2);
-  EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
-
-  f2 = ParsedFormat<'s', 'd', 'f'>::New("%s %d %f");
-
-  ASSERT_TRUE(f2);
-  EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
-
-  auto star = ParsedFormat<'*', 'd'>::New("%*d");
-  ASSERT_TRUE(star);
-  EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star));
-
-  auto dollar = ParsedFormat<'d', 's'>::New("%2$s %1$d");
-  ASSERT_TRUE(dollar);
-  EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar));
-  // with reuse
-  dollar = ParsedFormat<'d', 's'>::New("%2$s %1$d %1$d");
-  ASSERT_TRUE(dollar);
-  EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}",
-            SummarizeParsedFormat(*dollar));
-}
-
-TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgs) {
-  EXPECT_FALSE((ParsedFormat<'d', 's'>::New("ABC")));
-  EXPECT_FALSE((ParsedFormat<'d', 's'>::New("%dABC")));
-  EXPECT_FALSE((ParsedFormat<'d', 's'>::New("ABC%2$s")));
-  auto f = ParsedFormat<'d', 's'>::NewAllowIgnored("ABC");
-  ASSERT_TRUE(f);
-  EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
-  f = ParsedFormat<'d', 's'>::NewAllowIgnored("%dABC");
-  ASSERT_TRUE(f);
-  EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f));
-  f = ParsedFormat<'d', 's'>::NewAllowIgnored("ABC%2$s");
-  ASSERT_TRUE(f);
-  EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
-}
-
-TEST_F(ParsedFormatTest, SimpleUncheckedUnsupported) {
-  EXPECT_FALSE(ParsedFormat<'d'>::New("%1$d %1$x"));
-  EXPECT_FALSE(ParsedFormat<'x'>::New("%1$d %1$x"));
-}
-
-TEST_F(ParsedFormatTest, SimpleUncheckedIncorrect) {
-  EXPECT_FALSE(ParsedFormat<'d'>::New(""));
-
-  EXPECT_FALSE(ParsedFormat<'d'>::New("ABC%dDEF%d"));
-
-  std::string format = "%sFFF%dZZZ%f";
-  EXPECT_FALSE((ParsedFormat<'s', 'd', 'g'>::New(format)));
-}
-
-#if defined(__cpp_nontype_template_parameter_auto)
-
-template <auto T>
-std::true_type IsValidParsedFormatArgTest(ParsedFormat<T>*);
-
-template <auto T>
-std::false_type IsValidParsedFormatArgTest(...);
-
-template <auto T>
-using IsValidParsedFormatArg = decltype(IsValidParsedFormatArgTest<T>(nullptr));
-
-TEST_F(ParsedFormatTest, OnlyValidTypesAllowed) {
-  ASSERT_TRUE(IsValidParsedFormatArg<'c'>::value);
-
-  ASSERT_TRUE(IsValidParsedFormatArg<FormatConversionCharSet::d>::value);
-
-  ASSERT_TRUE(IsValidParsedFormatArg<absl::FormatConversionCharSet::d |
-                                     absl::FormatConversionCharSet::x>::value);
-  ASSERT_TRUE(
-      IsValidParsedFormatArg<absl::FormatConversionCharSet::kIntegral>::value);
-
-  // This is an easy mistake to make, however, this will reduce to an integer
-  // which has no meaning, so we need to ensure it doesn't compile.
-  ASSERT_FALSE(IsValidParsedFormatArg<'x' | 'd'>::value);
-
-  // For now, we disallow construction based on ConversionChar (rather than
-  // CharSet)
-  ASSERT_FALSE(IsValidParsedFormatArg<absl::FormatConversionChar::d>::value);
-}
-
-TEST_F(ParsedFormatTest, ExtendedTyping) {
-  EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::d>::New(""));
-  ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::d>::New("%d"));
-  auto v1 = ParsedFormat<'d', absl::FormatConversionCharSet::s>::New("%d%s");
-  ASSERT_TRUE(v1);
-  auto v2 = ParsedFormat<absl::FormatConversionCharSet::d, 's'>::New("%d%s");
-  ASSERT_TRUE(v2);
-  auto v3 = ParsedFormat<absl::FormatConversionCharSet::d |
-                             absl::FormatConversionCharSet::s,
-                         's'>::New("%d%s");
-  ASSERT_TRUE(v3);
-  auto v4 = ParsedFormat<absl::FormatConversionCharSet::d |
-                             absl::FormatConversionCharSet::s,
-                         's'>::New("%s%s");
-  ASSERT_TRUE(v4);
-}
-#endif
-
-TEST_F(ParsedFormatTest, UncheckedCorrect) {
-  auto f =
-      ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New("ABC%dDEF");
-  ASSERT_TRUE(f);
-  EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
-
-  std::string format = "%sFFF%dZZZ%f";
-  auto f2 = ExtendedParsedFormat<
-      absl::FormatConversionCharSet::kString, absl::FormatConversionCharSet::d,
-      absl::FormatConversionCharSet::kFloating>::New(format);
-
-  ASSERT_TRUE(f2);
-  EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
-
-  f2 = ExtendedParsedFormat<
-      absl::FormatConversionCharSet::kString, absl::FormatConversionCharSet::d,
-      absl::FormatConversionCharSet::kFloating>::New("%s %d %f");
-
-  ASSERT_TRUE(f2);
-  EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
-
-  auto star =
-      ExtendedParsedFormat<absl::FormatConversionCharSet::kStar,
-                           absl::FormatConversionCharSet::d>::New("%*d");
-  ASSERT_TRUE(star);
-  EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star));
-
-  auto dollar =
-      ExtendedParsedFormat<absl::FormatConversionCharSet::d,
-                           absl::FormatConversionCharSet::s>::New("%2$s %1$d");
-  ASSERT_TRUE(dollar);
-  EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar));
-  // with reuse
-  dollar = ExtendedParsedFormat<
-      absl::FormatConversionCharSet::d,
-      absl::FormatConversionCharSet::s>::New("%2$s %1$d %1$d");
-  ASSERT_TRUE(dollar);
-  EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}",
-            SummarizeParsedFormat(*dollar));
-}
-
-TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) {
-  EXPECT_FALSE(
-      (ExtendedParsedFormat<absl::FormatConversionCharSet::d,
-                            absl::FormatConversionCharSet::s>::New("ABC")));
-  EXPECT_FALSE(
-      (ExtendedParsedFormat<absl::FormatConversionCharSet::d,
-                            absl::FormatConversionCharSet::s>::New("%dABC")));
-  EXPECT_FALSE(
-      (ExtendedParsedFormat<absl::FormatConversionCharSet::d,
-                            absl::FormatConversionCharSet::s>::New("ABC%2$s")));
-  auto f = ExtendedParsedFormat<
-      absl::FormatConversionCharSet::d,
-      absl::FormatConversionCharSet::s>::NewAllowIgnored("ABC");
-  ASSERT_TRUE(f);
-  EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
-  f = ExtendedParsedFormat<
-      absl::FormatConversionCharSet::d,
-      absl::FormatConversionCharSet::s>::NewAllowIgnored("%dABC");
-  ASSERT_TRUE(f);
-  EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f));
-  f = ExtendedParsedFormat<
-      absl::FormatConversionCharSet::d,
-      absl::FormatConversionCharSet::s>::NewAllowIgnored("ABC%2$s");
-  ASSERT_TRUE(f);
-  EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
-}
-
-TEST_F(ParsedFormatTest, UncheckedMultipleTypes) {
-  auto dx =
-      ExtendedParsedFormat<absl::FormatConversionCharSet::d |
-                           absl::FormatConversionCharSet::x>::New("%1$d %1$x");
-  EXPECT_TRUE(dx);
-  EXPECT_EQ("{1$d:1$d}[ ]{1$x:1$x}", SummarizeParsedFormat(*dx));
-
-  dx = ExtendedParsedFormat<absl::FormatConversionCharSet::d |
-                            absl::FormatConversionCharSet::x>::New("%1$d");
-  EXPECT_TRUE(dx);
-  EXPECT_EQ("{1$d:1$d}", SummarizeParsedFormat(*dx));
-}
-
-TEST_F(ParsedFormatTest, UncheckedIncorrect) {
-  EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New(""));
-
-  EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New(
-      "ABC%dDEF%d"));
-
-  std::string format = "%sFFF%dZZZ%f";
-  EXPECT_FALSE(
-      (ExtendedParsedFormat<absl::FormatConversionCharSet::s,
-                            absl::FormatConversionCharSet::d,
-                            absl::FormatConversionCharSet::g>::New(format)));
-}
-
-TEST_F(ParsedFormatTest, RegressionMixPositional) {
-  EXPECT_FALSE(
-      (ExtendedParsedFormat<absl::FormatConversionCharSet::d,
-                            absl::FormatConversionCharSet::o>::New("%1$d %o")));
-}
-
-using FormatWrapperTest = ::testing::Test;
-
-// Plain wrapper for StrFormat.
-template <typename... Args>
-std::string WrappedFormat(const absl::FormatSpec<Args...>& format,
-                          const Args&... args) {
-  return StrFormat(format, args...);
-}
-
-TEST_F(FormatWrapperTest, ConstexprStringFormat) {
-  EXPECT_EQ(WrappedFormat("%s there", "hello"), "hello there");
-}
-
-TEST_F(FormatWrapperTest, ParsedFormat) {
-  ParsedFormat<'s'> format("%s there");
-  EXPECT_EQ(WrappedFormat(format, "hello"), "hello there");
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-using FormatExtensionTest = ::testing::Test;
-
-struct Point {
-  friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString |
-                                   absl::FormatConversionCharSet::kIntegral>
-  AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec,
-                    absl::FormatSink* s) {
-    if (spec.conversion_char() == absl::FormatConversionChar::s) {
-      s->Append(absl::StrCat("x=", p.x, " y=", p.y));
-    } else {
-      s->Append(absl::StrCat(p.x, ",", p.y));
-    }
-    return {true};
-  }
-
-  int x = 10;
-  int y = 20;
-};
-
-TEST_F(FormatExtensionTest, AbslFormatConvertExample) {
-  Point p;
-  EXPECT_EQ(absl::StrFormat("a %s z", p), "a x=10 y=20 z");
-  EXPECT_EQ(absl::StrFormat("a %d z", p), "a 10,20 z");
-
-  // Typed formatting will fail to compile an invalid format.
-  // StrFormat("%f", p);  // Does not compile.
-  std::string actual;
-  absl::UntypedFormatSpec f1("%f");
-  // FormatUntyped will return false for bad character.
-  EXPECT_FALSE(absl::FormatUntyped(&actual, f1, {absl::FormatArg(p)}));
-}
-
-// Some codegen thunks that we can use to easily dump the generated assembly for
-// different StrFormat calls.
-
-std::string CodegenAbslStrFormatInt(int i) {  // NOLINT
-  return absl::StrFormat("%d", i);
-}
-
-std::string CodegenAbslStrFormatIntStringInt64(int i, const std::string& s,
-                                               int64_t i64) {  // NOLINT
-  return absl::StrFormat("%d %s %d", i, s, i64);
-}
-
-void CodegenAbslStrAppendFormatInt(std::string* out, int i) {  // NOLINT
-  absl::StrAppendFormat(out, "%d", i);
-}
-
-void CodegenAbslStrAppendFormatIntStringInt64(std::string* out, int i,
-                                              const std::string& s,
-                                              int64_t i64) {  // NOLINT
-  absl::StrAppendFormat(out, "%d %s %d", i, s, i64);
-}
diff --git a/third_party/abseil_cpp/absl/strings/str_join.h b/third_party/abseil_cpp/absl/strings/str_join.h
deleted file mode 100644
index ae5731a42b..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_join.h
+++ /dev/null
@@ -1,293 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: str_join.h
-// -----------------------------------------------------------------------------
-//
-// This header file contains functions for joining a range of elements and
-// returning the result as a std::string. StrJoin operations are specified by
-// passing a range, a separator string to use between the elements joined, and
-// an optional Formatter responsible for converting each argument in the range
-// to a string. If omitted, a default `AlphaNumFormatter()` is called on the
-// elements to be joined, using the same formatting that `absl::StrCat()` uses.
-// This package defines a number of default formatters, and you can define your
-// own implementations.
-//
-// Ranges are specified by passing a container with `std::begin()` and
-// `std::end()` iterators, container-specific `begin()` and `end()` iterators, a
-// brace-initialized `std::initializer_list`, or a `std::tuple` of heterogeneous
-// objects. The separator string is specified as an `absl::string_view`.
-//
-// Because the default formatter uses the `absl::AlphaNum` class,
-// `absl::StrJoin()`, like `absl::StrCat()`, will work out-of-the-box on
-// collections of strings, ints, floats, doubles, etc.
-//
-// Example:
-//
-//   std::vector<std::string> v = {"foo", "bar", "baz"};
-//   std::string s = absl::StrJoin(v, "-");
-//   EXPECT_EQ("foo-bar-baz", s);
-//
-// See comments on the `absl::StrJoin()` function for more examples.
-
-#ifndef ABSL_STRINGS_STR_JOIN_H_
-#define ABSL_STRINGS_STR_JOIN_H_
-
-#include <cstdio>
-#include <cstring>
-#include <initializer_list>
-#include <iterator>
-#include <string>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/macros.h"
-#include "absl/strings/internal/str_join_internal.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// Concept: Formatter
-// -----------------------------------------------------------------------------
-//
-// A Formatter is a function object that is responsible for formatting its
-// argument as a string and appending it to a given output std::string.
-// Formatters may be implemented as function objects, lambdas, or normal
-// functions. You may provide your own Formatter to enable `absl::StrJoin()` to
-// work with arbitrary types.
-//
-// The following is an example of a custom Formatter that simply uses
-// `std::to_string()` to format an integer as a std::string.
-//
-//   struct MyFormatter {
-//     void operator()(std::string* out, int i) const {
-//       out->append(std::to_string(i));
-//     }
-//   };
-//
-// You would use the above formatter by passing an instance of it as the final
-// argument to `absl::StrJoin()`:
-//
-//   std::vector<int> v = {1, 2, 3, 4};
-//   std::string s = absl::StrJoin(v, "-", MyFormatter());
-//   EXPECT_EQ("1-2-3-4", s);
-//
-// The following standard formatters are provided within this file:
-//
-// - `AlphaNumFormatter()` (the default)
-// - `StreamFormatter()`
-// - `PairFormatter()`
-// - `DereferenceFormatter()`
-
-// AlphaNumFormatter()
-//
-// Default formatter used if none is specified. Uses `absl::AlphaNum` to convert
-// numeric arguments to strings.
-inline strings_internal::AlphaNumFormatterImpl AlphaNumFormatter() {
-  return strings_internal::AlphaNumFormatterImpl();
-}
-
-// StreamFormatter()
-//
-// Formats its argument using the << operator.
-inline strings_internal::StreamFormatterImpl StreamFormatter() {
-  return strings_internal::StreamFormatterImpl();
-}
-
-// Function Template: PairFormatter(Formatter, absl::string_view, Formatter)
-//
-// Formats a `std::pair` by putting a given separator between the pair's
-// `.first` and `.second` members. This formatter allows you to specify
-// custom Formatters for both the first and second member of each pair.
-template <typename FirstFormatter, typename SecondFormatter>
-inline strings_internal::PairFormatterImpl<FirstFormatter, SecondFormatter>
-PairFormatter(FirstFormatter f1, absl::string_view sep, SecondFormatter f2) {
-  return strings_internal::PairFormatterImpl<FirstFormatter, SecondFormatter>(
-      std::move(f1), sep, std::move(f2));
-}
-
-// Function overload of PairFormatter() for using a default
-// `AlphaNumFormatter()` for each Formatter in the pair.
-inline strings_internal::PairFormatterImpl<
-    strings_internal::AlphaNumFormatterImpl,
-    strings_internal::AlphaNumFormatterImpl>
-PairFormatter(absl::string_view sep) {
-  return PairFormatter(AlphaNumFormatter(), sep, AlphaNumFormatter());
-}
-
-// Function Template: DereferenceFormatter(Formatter)
-//
-// Formats its argument by dereferencing it and then applying the given
-// formatter. This formatter is useful for formatting a container of
-// pointer-to-T. This pattern often shows up when joining repeated fields in
-// protocol buffers.
-template <typename Formatter>
-strings_internal::DereferenceFormatterImpl<Formatter> DereferenceFormatter(
-    Formatter&& f) {
-  return strings_internal::DereferenceFormatterImpl<Formatter>(
-      std::forward<Formatter>(f));
-}
-
-// Function overload of `DererefenceFormatter()` for using a default
-// `AlphaNumFormatter()`.
-inline strings_internal::DereferenceFormatterImpl<
-    strings_internal::AlphaNumFormatterImpl>
-DereferenceFormatter() {
-  return strings_internal::DereferenceFormatterImpl<
-      strings_internal::AlphaNumFormatterImpl>(AlphaNumFormatter());
-}
-
-// -----------------------------------------------------------------------------
-// StrJoin()
-// -----------------------------------------------------------------------------
-//
-// Joins a range of elements and returns the result as a std::string.
-// `absl::StrJoin()` takes a range, a separator string to use between the
-// elements joined, and an optional Formatter responsible for converting each
-// argument in the range to a string.
-//
-// If omitted, the default `AlphaNumFormatter()` is called on the elements to be
-// joined.
-//
-// Example 1:
-//   // Joins a collection of strings. This pattern also works with a collection
-//   // of `absl::string_view` or even `const char*`.
-//   std::vector<std::string> v = {"foo", "bar", "baz"};
-//   std::string s = absl::StrJoin(v, "-");
-//   EXPECT_EQ("foo-bar-baz", s);
-//
-// Example 2:
-//   // Joins the values in the given `std::initializer_list<>` specified using
-//   // brace initialization. This pattern also works with an initializer_list
-//   // of ints or `absl::string_view` -- any `AlphaNum`-compatible type.
-//   std::string s = absl::StrJoin({"foo", "bar", "baz"}, "-");
-//   EXPECT_EQ("foo-bar-baz", s);
-//
-// Example 3:
-//   // Joins a collection of ints. This pattern also works with floats,
-//   // doubles, int64s -- any `StrCat()`-compatible type.
-//   std::vector<int> v = {1, 2, 3, -4};
-//   std::string s = absl::StrJoin(v, "-");
-//   EXPECT_EQ("1-2-3--4", s);
-//
-// Example 4:
-//   // Joins a collection of pointer-to-int. By default, pointers are
-//   // dereferenced and the pointee is formatted using the default format for
-//   // that type; such dereferencing occurs for all levels of indirection, so
-//   // this pattern works just as well for `std::vector<int**>` as for
-//   // `std::vector<int*>`.
-//   int x = 1, y = 2, z = 3;
-//   std::vector<int*> v = {&x, &y, &z};
-//   std::string s = absl::StrJoin(v, "-");
-//   EXPECT_EQ("1-2-3", s);
-//
-// Example 5:
-//   // Dereferencing of `std::unique_ptr<>` is also supported:
-//   std::vector<std::unique_ptr<int>> v
-//   v.emplace_back(new int(1));
-//   v.emplace_back(new int(2));
-//   v.emplace_back(new int(3));
-//   std::string s = absl::StrJoin(v, "-");
-//   EXPECT_EQ("1-2-3", s);
-//
-// Example 6:
-//   // Joins a `std::map`, with each key-value pair separated by an equals
-//   // sign. This pattern would also work with, say, a
-//   // `std::vector<std::pair<>>`.
-//   std::map<std::string, int> m = {
-//       std::make_pair("a", 1),
-//       std::make_pair("b", 2),
-//       std::make_pair("c", 3)};
-//   std::string s = absl::StrJoin(m, ",", absl::PairFormatter("="));
-//   EXPECT_EQ("a=1,b=2,c=3", s);
-//
-// Example 7:
-//   // These examples show how `absl::StrJoin()` handles a few common edge
-//   // cases:
-//   std::vector<std::string> v_empty;
-//   EXPECT_EQ("", absl::StrJoin(v_empty, "-"));
-//
-//   std::vector<std::string> v_one_item = {"foo"};
-//   EXPECT_EQ("foo", absl::StrJoin(v_one_item, "-"));
-//
-//   std::vector<std::string> v_empty_string = {""};
-//   EXPECT_EQ("", absl::StrJoin(v_empty_string, "-"));
-//
-//   std::vector<std::string> v_one_item_empty_string = {"a", ""};
-//   EXPECT_EQ("a-", absl::StrJoin(v_one_item_empty_string, "-"));
-//
-//   std::vector<std::string> v_two_empty_string = {"", ""};
-//   EXPECT_EQ("-", absl::StrJoin(v_two_empty_string, "-"));
-//
-// Example 8:
-//   // Joins a `std::tuple<T...>` of heterogeneous types, converting each to
-//   // a std::string using the `absl::AlphaNum` class.
-//   std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-");
-//   EXPECT_EQ("123-abc-0.456", s);
-
-template <typename Iterator, typename Formatter>
-std::string StrJoin(Iterator start, Iterator end, absl::string_view sep,
-                    Formatter&& fmt) {
-  return strings_internal::JoinAlgorithm(start, end, sep, fmt);
-}
-
-template <typename Range, typename Formatter>
-std::string StrJoin(const Range& range, absl::string_view separator,
-                    Formatter&& fmt) {
-  return strings_internal::JoinRange(range, separator, fmt);
-}
-
-template <typename T, typename Formatter>
-std::string StrJoin(std::initializer_list<T> il, absl::string_view separator,
-                    Formatter&& fmt) {
-  return strings_internal::JoinRange(il, separator, fmt);
-}
-
-template <typename... T, typename Formatter>
-std::string StrJoin(const std::tuple<T...>& value, absl::string_view separator,
-                    Formatter&& fmt) {
-  return strings_internal::JoinAlgorithm(value, separator, fmt);
-}
-
-template <typename Iterator>
-std::string StrJoin(Iterator start, Iterator end, absl::string_view separator) {
-  return strings_internal::JoinRange(start, end, separator);
-}
-
-template <typename Range>
-std::string StrJoin(const Range& range, absl::string_view separator) {
-  return strings_internal::JoinRange(range, separator);
-}
-
-template <typename T>
-std::string StrJoin(std::initializer_list<T> il,
-                    absl::string_view separator) {
-  return strings_internal::JoinRange(il, separator);
-}
-
-template <typename... T>
-std::string StrJoin(const std::tuple<T...>& value,
-                    absl::string_view separator) {
-  return strings_internal::JoinAlgorithm(value, separator, AlphaNumFormatter());
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_STR_JOIN_H_
diff --git a/third_party/abseil_cpp/absl/strings/str_join_benchmark.cc b/third_party/abseil_cpp/absl/strings/str_join_benchmark.cc
deleted file mode 100644
index d6f689ff30..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_join_benchmark.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-//
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_join.h"
-
-#include <string>
-#include <vector>
-#include <utility>
-
-#include "benchmark/benchmark.h"
-
-namespace {
-
-void BM_Join2_Strings(benchmark::State& state) {
-  const int string_len = state.range(0);
-  const int num_strings = state.range(1);
-  const std::string s(string_len, 'x');
-  const std::vector<std::string> v(num_strings, s);
-  for (auto _ : state) {
-    std::string s = absl::StrJoin(v, "-");
-    benchmark::DoNotOptimize(s);
-  }
-}
-BENCHMARK(BM_Join2_Strings)
-    ->ArgPair(1 << 0, 1 << 3)
-    ->ArgPair(1 << 10, 1 << 3)
-    ->ArgPair(1 << 13, 1 << 3)
-    ->ArgPair(1 << 0, 1 << 10)
-    ->ArgPair(1 << 10, 1 << 10)
-    ->ArgPair(1 << 13, 1 << 10)
-    ->ArgPair(1 << 0, 1 << 13)
-    ->ArgPair(1 << 10, 1 << 13)
-    ->ArgPair(1 << 13, 1 << 13);
-
-void BM_Join2_Ints(benchmark::State& state) {
-  const int num_ints = state.range(0);
-  const std::vector<int> v(num_ints, 42);
-  for (auto _ : state) {
-    std::string s = absl::StrJoin(v, "-");
-    benchmark::DoNotOptimize(s);
-  }
-}
-BENCHMARK(BM_Join2_Ints)->Range(0, 1 << 13);
-
-void BM_Join2_KeysAndValues(benchmark::State& state) {
-  const int string_len = state.range(0);
-  const int num_pairs = state.range(1);
-  const std::string s(string_len, 'x');
-  const std::vector<std::pair<std::string, int>> v(num_pairs,
-                                                   std::make_pair(s, 42));
-  for (auto _ : state) {
-    std::string s = absl::StrJoin(v, ",", absl::PairFormatter("="));
-    benchmark::DoNotOptimize(s);
-  }
-}
-BENCHMARK(BM_Join2_KeysAndValues)
-    ->ArgPair(1 << 0, 1 << 3)
-    ->ArgPair(1 << 10, 1 << 3)
-    ->ArgPair(1 << 13, 1 << 3)
-    ->ArgPair(1 << 0, 1 << 10)
-    ->ArgPair(1 << 10, 1 << 10)
-    ->ArgPair(1 << 13, 1 << 10)
-    ->ArgPair(1 << 0, 1 << 13)
-    ->ArgPair(1 << 10, 1 << 13)
-    ->ArgPair(1 << 13, 1 << 13);
-
-void BM_JoinStreamable(benchmark::State& state) {
-  const int string_len = state.range(0);
-  const int num_strings = state.range(1);
-  const std::vector<std::string> v(num_strings, std::string(string_len, 'x'));
-  for (auto _ : state) {
-    std::string s = absl::StrJoin(v, "", absl::StreamFormatter());
-    benchmark::DoNotOptimize(s);
-  }
-}
-BENCHMARK(BM_JoinStreamable)
-    ->ArgPair(0, 0)
-    ->ArgPair(16, 1)
-    ->ArgPair(256, 1)
-    ->ArgPair(16, 16)
-    ->ArgPair(256, 16)
-    ->ArgPair(16, 256)
-    ->ArgPair(256, 256);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/str_join_test.cc b/third_party/abseil_cpp/absl/strings/str_join_test.cc
deleted file mode 100644
index 2be6256e43..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_join_test.cc
+++ /dev/null
@@ -1,474 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Unit tests for all join.h functions
-
-#include "absl/strings/str_join.h"
-
-#include <cstddef>
-#include <cstdint>
-#include <cstdio>
-#include <functional>
-#include <initializer_list>
-#include <map>
-#include <memory>
-#include <ostream>
-#include <tuple>
-#include <type_traits>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/macros.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_split.h"
-
-namespace {
-
-TEST(StrJoin, APIExamples) {
-  {
-    // Collection of strings
-    std::vector<std::string> v = {"foo", "bar", "baz"};
-    EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // Collection of absl::string_view
-    std::vector<absl::string_view> v = {"foo", "bar", "baz"};
-    EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // Collection of const char*
-    std::vector<const char*> v = {"foo", "bar", "baz"};
-    EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // Collection of non-const char*
-    std::string a = "foo", b = "bar", c = "baz";
-    std::vector<char*> v = {&a[0], &b[0], &c[0]};
-    EXPECT_EQ("foo-bar-baz", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // Collection of ints
-    std::vector<int> v = {1, 2, 3, -4};
-    EXPECT_EQ("1-2-3--4", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // Literals passed as a std::initializer_list
-    std::string s = absl::StrJoin({"a", "b", "c"}, "-");
-    EXPECT_EQ("a-b-c", s);
-  }
-  {
-    // Join a std::tuple<T...>.
-    std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-");
-    EXPECT_EQ("123-abc-0.456", s);
-  }
-
-  {
-    // Collection of unique_ptrs
-    std::vector<std::unique_ptr<int>> v;
-    v.emplace_back(new int(1));
-    v.emplace_back(new int(2));
-    v.emplace_back(new int(3));
-    EXPECT_EQ("1-2-3", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // Array of ints
-    const int a[] = {1, 2, 3, -4};
-    EXPECT_EQ("1-2-3--4", absl::StrJoin(a, a + ABSL_ARRAYSIZE(a), "-"));
-  }
-
-  {
-    // Collection of pointers
-    int x = 1, y = 2, z = 3;
-    std::vector<int*> v = {&x, &y, &z};
-    EXPECT_EQ("1-2-3", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // Collection of pointers to pointers
-    int x = 1, y = 2, z = 3;
-    int *px = &x, *py = &y, *pz = &z;
-    std::vector<int**> v = {&px, &py, &pz};
-    EXPECT_EQ("1-2-3", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // Collection of pointers to std::string
-    std::string a("a"), b("b");
-    std::vector<std::string*> v = {&a, &b};
-    EXPECT_EQ("a-b", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // A std::map, which is a collection of std::pair<>s.
-    std::map<std::string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}};
-    EXPECT_EQ("a=1,b=2,c=3", absl::StrJoin(m, ",", absl::PairFormatter("=")));
-  }
-
-  {
-    // Shows absl::StrSplit and absl::StrJoin working together. This example is
-    // equivalent to s/=/-/g.
-    const std::string s = "a=b=c=d";
-    EXPECT_EQ("a-b-c-d", absl::StrJoin(absl::StrSplit(s, "="), "-"));
-  }
-
-  //
-  // A few examples of edge cases
-  //
-
-  {
-    // Empty range yields an empty string.
-    std::vector<std::string> v;
-    EXPECT_EQ("", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // A range of 1 element gives a string with that element but no
-    // separator.
-    std::vector<std::string> v = {"foo"};
-    EXPECT_EQ("foo", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // A range with a single empty string element
-    std::vector<std::string> v = {""};
-    EXPECT_EQ("", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // A range with 2 elements, one of which is an empty string
-    std::vector<std::string> v = {"a", ""};
-    EXPECT_EQ("a-", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // A range with 2 empty elements.
-    std::vector<std::string> v = {"", ""};
-    EXPECT_EQ("-", absl::StrJoin(v, "-"));
-  }
-
-  {
-    // A std::vector of bool.
-    std::vector<bool> v = {true, false, true};
-    EXPECT_EQ("1-0-1", absl::StrJoin(v, "-"));
-  }
-}
-
-TEST(StrJoin, CustomFormatter) {
-  std::vector<std::string> v{"One", "Two", "Three"};
-  {
-    std::string joined =
-        absl::StrJoin(v, "", [](std::string* out, const std::string& in) {
-          absl::StrAppend(out, "(", in, ")");
-        });
-    EXPECT_EQ("(One)(Two)(Three)", joined);
-  }
-  {
-    class ImmovableFormatter {
-     public:
-      void operator()(std::string* out, const std::string& in) {
-        absl::StrAppend(out, "(", in, ")");
-      }
-      ImmovableFormatter() {}
-      ImmovableFormatter(const ImmovableFormatter&) = delete;
-    };
-    EXPECT_EQ("(One)(Two)(Three)", absl::StrJoin(v, "", ImmovableFormatter()));
-  }
-  {
-    class OverloadedFormatter {
-     public:
-      void operator()(std::string* out, const std::string& in) {
-        absl::StrAppend(out, "(", in, ")");
-      }
-      void operator()(std::string* out, const std::string& in) const {
-        absl::StrAppend(out, "[", in, "]");
-      }
-    };
-    EXPECT_EQ("(One)(Two)(Three)", absl::StrJoin(v, "", OverloadedFormatter()));
-    const OverloadedFormatter fmt = {};
-    EXPECT_EQ("[One][Two][Three]", absl::StrJoin(v, "", fmt));
-  }
-}
-
-//
-// Tests the Formatters
-//
-
-TEST(AlphaNumFormatter, FormatterAPI) {
-  // Not an exhaustive test. See strings/strcat_test.h for the exhaustive test
-  // of what AlphaNum can convert.
-  auto f = absl::AlphaNumFormatter();
-  std::string s;
-  f(&s, "Testing: ");
-  f(&s, static_cast<int>(1));
-  f(&s, static_cast<int16_t>(2));
-  f(&s, static_cast<int64_t>(3));
-  f(&s, static_cast<float>(4));
-  f(&s, static_cast<double>(5));
-  f(&s, static_cast<unsigned>(6));
-  f(&s, static_cast<size_t>(7));
-  f(&s, absl::string_view(" OK"));
-  EXPECT_EQ("Testing: 1234567 OK", s);
-}
-
-// Make sure people who are mistakenly using std::vector<bool> even though
-// they're not memory-constrained can use absl::AlphaNumFormatter().
-TEST(AlphaNumFormatter, VectorOfBool) {
-  auto f = absl::AlphaNumFormatter();
-  std::string s;
-  std::vector<bool> v = {true, false, true};
-  f(&s, *v.cbegin());
-  f(&s, *v.begin());
-  f(&s, v[1]);
-  EXPECT_EQ("110", s);
-}
-
-TEST(AlphaNumFormatter, AlphaNum) {
-  auto f = absl::AlphaNumFormatter();
-  std::string s;
-  f(&s, absl::AlphaNum("hello"));
-  EXPECT_EQ("hello", s);
-}
-
-struct StreamableType {
-  std::string contents;
-};
-inline std::ostream& operator<<(std::ostream& os, const StreamableType& t) {
-  os << "Streamable:" << t.contents;
-  return os;
-}
-
-TEST(StreamFormatter, FormatterAPI) {
-  auto f = absl::StreamFormatter();
-  std::string s;
-  f(&s, "Testing: ");
-  f(&s, static_cast<int>(1));
-  f(&s, static_cast<int16_t>(2));
-  f(&s, static_cast<int64_t>(3));
-  f(&s, static_cast<float>(4));
-  f(&s, static_cast<double>(5));
-  f(&s, static_cast<unsigned>(6));
-  f(&s, static_cast<size_t>(7));
-  f(&s, absl::string_view(" OK "));
-  StreamableType streamable = {"object"};
-  f(&s, streamable);
-  EXPECT_EQ("Testing: 1234567 OK Streamable:object", s);
-}
-
-// A dummy formatter that wraps each element in parens. Used in some tests
-// below.
-struct TestingParenFormatter {
-  template <typename T>
-  void operator()(std::string* s, const T& t) {
-    absl::StrAppend(s, "(", t, ")");
-  }
-};
-
-TEST(PairFormatter, FormatterAPI) {
-  {
-    // Tests default PairFormatter(sep) that uses AlphaNumFormatter for the
-    // 'first' and 'second' members.
-    const auto f = absl::PairFormatter("=");
-    std::string s;
-    f(&s, std::make_pair("a", "b"));
-    f(&s, std::make_pair(1, 2));
-    EXPECT_EQ("a=b1=2", s);
-  }
-
-  {
-    // Tests using a custom formatter for the 'first' and 'second' members.
-    auto f = absl::PairFormatter(TestingParenFormatter(), "=",
-                                 TestingParenFormatter());
-    std::string s;
-    f(&s, std::make_pair("a", "b"));
-    f(&s, std::make_pair(1, 2));
-    EXPECT_EQ("(a)=(b)(1)=(2)", s);
-  }
-}
-
-TEST(DereferenceFormatter, FormatterAPI) {
-  {
-    // Tests wrapping the default AlphaNumFormatter.
-    const absl::strings_internal::DereferenceFormatterImpl<
-        absl::strings_internal::AlphaNumFormatterImpl>
-        f;
-    int x = 1, y = 2, z = 3;
-    std::string s;
-    f(&s, &x);
-    f(&s, &y);
-    f(&s, &z);
-    EXPECT_EQ("123", s);
-  }
-
-  {
-    // Tests wrapping std::string's default formatter.
-    absl::strings_internal::DereferenceFormatterImpl<
-        absl::strings_internal::DefaultFormatter<std::string>::Type>
-        f;
-
-    std::string x = "x";
-    std::string y = "y";
-    std::string z = "z";
-    std::string s;
-    f(&s, &x);
-    f(&s, &y);
-    f(&s, &z);
-    EXPECT_EQ(s, "xyz");
-  }
-
-  {
-    // Tests wrapping a custom formatter.
-    auto f = absl::DereferenceFormatter(TestingParenFormatter());
-    int x = 1, y = 2, z = 3;
-    std::string s;
-    f(&s, &x);
-    f(&s, &y);
-    f(&s, &z);
-    EXPECT_EQ("(1)(2)(3)", s);
-  }
-
-  {
-    absl::strings_internal::DereferenceFormatterImpl<
-        absl::strings_internal::AlphaNumFormatterImpl>
-        f;
-    auto x = std::unique_ptr<int>(new int(1));
-    auto y = std::unique_ptr<int>(new int(2));
-    auto z = std::unique_ptr<int>(new int(3));
-    std::string s;
-    f(&s, x);
-    f(&s, y);
-    f(&s, z);
-    EXPECT_EQ("123", s);
-  }
-}
-
-//
-// Tests the interfaces for the 4 public Join function overloads. The semantics
-// of the algorithm is covered in the above APIExamples test.
-//
-TEST(StrJoin, PublicAPIOverloads) {
-  std::vector<std::string> v = {"a", "b", "c"};
-
-  // Iterators + formatter
-  EXPECT_EQ("a-b-c",
-            absl::StrJoin(v.begin(), v.end(), "-", absl::AlphaNumFormatter()));
-  // Range + formatter
-  EXPECT_EQ("a-b-c", absl::StrJoin(v, "-", absl::AlphaNumFormatter()));
-  // Iterators, no formatter
-  EXPECT_EQ("a-b-c", absl::StrJoin(v.begin(), v.end(), "-"));
-  // Range, no formatter
-  EXPECT_EQ("a-b-c", absl::StrJoin(v, "-"));
-}
-
-TEST(StrJoin, Array) {
-  const absl::string_view a[] = {"a", "b", "c"};
-  EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
-}
-
-TEST(StrJoin, InitializerList) {
-  { EXPECT_EQ("a-b-c", absl::StrJoin({"a", "b", "c"}, "-")); }
-
-  {
-    auto a = {"a", "b", "c"};
-    EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
-  }
-
-  {
-    std::initializer_list<const char*> a = {"a", "b", "c"};
-    EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
-  }
-
-  {
-    std::initializer_list<std::string> a = {"a", "b", "c"};
-    EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
-  }
-
-  {
-    std::initializer_list<absl::string_view> a = {"a", "b", "c"};
-    EXPECT_EQ("a-b-c", absl::StrJoin(a, "-"));
-  }
-
-  {
-    // Tests initializer_list with a non-default formatter
-    auto a = {"a", "b", "c"};
-    TestingParenFormatter f;
-    EXPECT_EQ("(a)-(b)-(c)", absl::StrJoin(a, "-", f));
-  }
-
-  {
-    // initializer_list of ints
-    EXPECT_EQ("1-2-3", absl::StrJoin({1, 2, 3}, "-"));
-  }
-
-  {
-    // Tests initializer_list of ints with a non-default formatter
-    auto a = {1, 2, 3};
-    TestingParenFormatter f;
-    EXPECT_EQ("(1)-(2)-(3)", absl::StrJoin(a, "-", f));
-  }
-}
-
-TEST(StrJoin, Tuple) {
-  EXPECT_EQ("", absl::StrJoin(std::make_tuple(), "-"));
-  EXPECT_EQ("hello", absl::StrJoin(std::make_tuple("hello"), "-"));
-
-  int x(10);
-  std::string y("hello");
-  double z(3.14);
-  EXPECT_EQ("10-hello-3.14", absl::StrJoin(std::make_tuple(x, y, z), "-"));
-
-  // Faster! Faster!!
-  EXPECT_EQ("10-hello-3.14",
-            absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-"));
-
-  struct TestFormatter {
-    char buffer[128];
-    void operator()(std::string* out, int v) {
-      snprintf(buffer, sizeof(buffer), "%#.8x", v);
-      out->append(buffer);
-    }
-    void operator()(std::string* out, double v) {
-      snprintf(buffer, sizeof(buffer), "%#.0f", v);
-      out->append(buffer);
-    }
-    void operator()(std::string* out, const std::string& v) {
-      snprintf(buffer, sizeof(buffer), "%.4s", v.c_str());
-      out->append(buffer);
-    }
-  };
-  EXPECT_EQ("0x0000000a-hell-3.",
-            absl::StrJoin(std::make_tuple(x, y, z), "-", TestFormatter()));
-  EXPECT_EQ(
-      "0x0000000a-hell-3.",
-      absl::StrJoin(std::make_tuple(x, std::cref(y), z), "-", TestFormatter()));
-  EXPECT_EQ("0x0000000a-hell-3.",
-            absl::StrJoin(std::make_tuple(&x, &y, &z), "-",
-                          absl::DereferenceFormatter(TestFormatter())));
-  EXPECT_EQ("0x0000000a-hell-3.",
-            absl::StrJoin(std::make_tuple(absl::make_unique<int>(x),
-                                          absl::make_unique<std::string>(y),
-                                          absl::make_unique<double>(z)),
-                          "-", absl::DereferenceFormatter(TestFormatter())));
-  EXPECT_EQ("0x0000000a-hell-3.",
-            absl::StrJoin(std::make_tuple(absl::make_unique<int>(x), &y, &z),
-                          "-", absl::DereferenceFormatter(TestFormatter())));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/str_replace.cc b/third_party/abseil_cpp/absl/strings/str_replace.cc
deleted file mode 100644
index 2bd5fa9821..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_replace.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_replace.h"
-
-#include "absl/strings/str_cat.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-using FixedMapping =
-    std::initializer_list<std::pair<absl::string_view, absl::string_view>>;
-
-// Applies the ViableSubstitutions in subs_ptr to the absl::string_view s, and
-// stores the result in *result_ptr. Returns the number of substitutions that
-// occurred.
-int ApplySubstitutions(
-    absl::string_view s,
-    std::vector<strings_internal::ViableSubstitution>* subs_ptr,
-    std::string* result_ptr) {
-  auto& subs = *subs_ptr;
-  int substitutions = 0;
-  size_t pos = 0;
-  while (!subs.empty()) {
-    auto& sub = subs.back();
-    if (sub.offset >= pos) {
-      if (pos <= s.size()) {
-        StrAppend(result_ptr, s.substr(pos, sub.offset - pos), sub.replacement);
-      }
-      pos = sub.offset + sub.old.size();
-      substitutions += 1;
-    }
-    sub.offset = s.find(sub.old, pos);
-    if (sub.offset == s.npos) {
-      subs.pop_back();
-    } else {
-      // Insertion sort to ensure the last ViableSubstitution continues to be
-      // before all the others.
-      size_t index = subs.size();
-      while (--index && subs[index - 1].OccursBefore(subs[index])) {
-        std::swap(subs[index], subs[index - 1]);
-      }
-    }
-  }
-  result_ptr->append(s.data() + pos, s.size() - pos);
-  return substitutions;
-}
-
-}  // namespace strings_internal
-
-// We can implement this in terms of the generic StrReplaceAll, but
-// we must specify the template overload because C++ cannot deduce the type
-// of an initializer_list parameter to a function, and also if we don't specify
-// the type, we just call ourselves.
-//
-// Note that we implement them here, rather than in the header, so that they
-// aren't inlined.
-
-std::string StrReplaceAll(absl::string_view s,
-                          strings_internal::FixedMapping replacements) {
-  return StrReplaceAll<strings_internal::FixedMapping>(s, replacements);
-}
-
-int StrReplaceAll(strings_internal::FixedMapping replacements,
-                  std::string* target) {
-  return StrReplaceAll<strings_internal::FixedMapping>(replacements, target);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/str_replace.h b/third_party/abseil_cpp/absl/strings/str_replace.h
deleted file mode 100644
index 273c707735..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_replace.h
+++ /dev/null
@@ -1,219 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: str_replace.h
-// -----------------------------------------------------------------------------
-//
-// This file defines `absl::StrReplaceAll()`, a general-purpose string
-// replacement function designed for large, arbitrary text substitutions,
-// especially on strings which you are receiving from some other system for
-// further processing (e.g. processing regular expressions, escaping HTML
-// entities, etc.). `StrReplaceAll` is designed to be efficient even when only
-// one substitution is being performed, or when substitution is rare.
-//
-// If the string being modified is known at compile-time, and the substitutions
-// vary, `absl::Substitute()` may be a better choice.
-//
-// Example:
-//
-// std::string html_escaped = absl::StrReplaceAll(user_input, {
-//                                                {"&", "&amp;"},
-//                                                {"<", "&lt;"},
-//                                                {">", "&gt;"},
-//                                                {"\"", "&quot;"},
-//                                                {"'", "&#39;"}});
-#ifndef ABSL_STRINGS_STR_REPLACE_H_
-#define ABSL_STRINGS_STR_REPLACE_H_
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "absl/base/attributes.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// StrReplaceAll()
-//
-// Replaces character sequences within a given string with replacements provided
-// within an initializer list of key/value pairs. Candidate replacements are
-// considered in order as they occur within the string, with earlier matches
-// taking precedence, and longer matches taking precedence for candidates
-// starting at the same position in the string. Once a substitution is made, the
-// replaced text is not considered for any further substitutions.
-//
-// Example:
-//
-//   std::string s = absl::StrReplaceAll(
-//       "$who bought $count #Noun. Thanks $who!",
-//       {{"$count", absl::StrCat(5)},
-//        {"$who", "Bob"},
-//        {"#Noun", "Apples"}});
-//   EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
-ABSL_MUST_USE_RESULT std::string StrReplaceAll(
-    absl::string_view s,
-    std::initializer_list<std::pair<absl::string_view, absl::string_view>>
-        replacements);
-
-// Overload of `StrReplaceAll()` to accept a container of key/value replacement
-// pairs (typically either an associative map or a `std::vector` of `std::pair`
-// elements). A vector of pairs is generally more efficient.
-//
-// Examples:
-//
-//   std::map<const absl::string_view, const absl::string_view> replacements;
-//   replacements["$who"] = "Bob";
-//   replacements["$count"] = "5";
-//   replacements["#Noun"] = "Apples";
-//   std::string s = absl::StrReplaceAll(
-//       "$who bought $count #Noun. Thanks $who!",
-//       replacements);
-//   EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
-//
-//   // A std::vector of std::pair elements can be more efficient.
-//   std::vector<std::pair<const absl::string_view, std::string>> replacements;
-//   replacements.push_back({"&", "&amp;"});
-//   replacements.push_back({"<", "&lt;"});
-//   replacements.push_back({">", "&gt;"});
-//   std::string s = absl::StrReplaceAll("if (ptr < &foo)",
-//                                  replacements);
-//   EXPECT_EQ("if (ptr &lt; &amp;foo)", s);
-template <typename StrToStrMapping>
-std::string StrReplaceAll(absl::string_view s,
-                          const StrToStrMapping& replacements);
-
-// Overload of `StrReplaceAll()` to replace character sequences within a given
-// output string *in place* with replacements provided within an initializer
-// list of key/value pairs, returning the number of substitutions that occurred.
-//
-// Example:
-//
-//   std::string s = std::string("$who bought $count #Noun. Thanks $who!");
-//   int count;
-//   count = absl::StrReplaceAll({{"$count", absl::StrCat(5)},
-//                               {"$who", "Bob"},
-//                               {"#Noun", "Apples"}}, &s);
-//  EXPECT_EQ(count, 4);
-//  EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
-int StrReplaceAll(
-    std::initializer_list<std::pair<absl::string_view, absl::string_view>>
-        replacements,
-    std::string* target);
-
-// Overload of `StrReplaceAll()` to replace patterns within a given output
-// string *in place* with replacements provided within a container of key/value
-// pairs.
-//
-// Example:
-//
-//   std::string s = std::string("if (ptr < &foo)");
-//   int count = absl::StrReplaceAll({{"&", "&amp;"},
-//                                    {"<", "&lt;"},
-//                                    {">", "&gt;"}}, &s);
-//  EXPECT_EQ(count, 2);
-//  EXPECT_EQ("if (ptr &lt; &amp;foo)", s);
-template <typename StrToStrMapping>
-int StrReplaceAll(const StrToStrMapping& replacements, std::string* target);
-
-// Implementation details only, past this point.
-namespace strings_internal {
-
-struct ViableSubstitution {
-  absl::string_view old;
-  absl::string_view replacement;
-  size_t offset;
-
-  ViableSubstitution(absl::string_view old_str,
-                     absl::string_view replacement_str, size_t offset_val)
-      : old(old_str), replacement(replacement_str), offset(offset_val) {}
-
-  // One substitution occurs "before" another (takes priority) if either
-  // it has the lowest offset, or it has the same offset but a larger size.
-  bool OccursBefore(const ViableSubstitution& y) const {
-    if (offset != y.offset) return offset < y.offset;
-    return old.size() > y.old.size();
-  }
-};
-
-// Build a vector of ViableSubstitutions based on the given list of
-// replacements. subs can be implemented as a priority_queue. However, it turns
-// out that most callers have small enough a list of substitutions that the
-// overhead of such a queue isn't worth it.
-template <typename StrToStrMapping>
-std::vector<ViableSubstitution> FindSubstitutions(
-    absl::string_view s, const StrToStrMapping& replacements) {
-  std::vector<ViableSubstitution> subs;
-  subs.reserve(replacements.size());
-
-  for (const auto& rep : replacements) {
-    using std::get;
-    absl::string_view old(get<0>(rep));
-
-    size_t pos = s.find(old);
-    if (pos == s.npos) continue;
-
-    // Ignore attempts to replace "". This condition is almost never true,
-    // but above condition is frequently true. That's why we test for this
-    // now and not before.
-    if (old.empty()) continue;
-
-    subs.emplace_back(old, get<1>(rep), pos);
-
-    // Insertion sort to ensure the last ViableSubstitution comes before
-    // all the others.
-    size_t index = subs.size();
-    while (--index && subs[index - 1].OccursBefore(subs[index])) {
-      std::swap(subs[index], subs[index - 1]);
-    }
-  }
-  return subs;
-}
-
-int ApplySubstitutions(absl::string_view s,
-                       std::vector<ViableSubstitution>* subs_ptr,
-                       std::string* result_ptr);
-
-}  // namespace strings_internal
-
-template <typename StrToStrMapping>
-std::string StrReplaceAll(absl::string_view s,
-                          const StrToStrMapping& replacements) {
-  auto subs = strings_internal::FindSubstitutions(s, replacements);
-  std::string result;
-  result.reserve(s.size());
-  strings_internal::ApplySubstitutions(s, &subs, &result);
-  return result;
-}
-
-template <typename StrToStrMapping>
-int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) {
-  auto subs = strings_internal::FindSubstitutions(*target, replacements);
-  if (subs.empty()) return 0;
-
-  std::string result;
-  result.reserve(target->size());
-  int substitutions =
-      strings_internal::ApplySubstitutions(*target, &subs, &result);
-  target->swap(result);
-  return substitutions;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_STR_REPLACE_H_
diff --git a/third_party/abseil_cpp/absl/strings/str_replace_benchmark.cc b/third_party/abseil_cpp/absl/strings/str_replace_benchmark.cc
deleted file mode 100644
index 01331da29f..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_replace_benchmark.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_replace.h"
-
-#include <cstring>
-#include <string>
-
-#include "benchmark/benchmark.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace {
-
-std::string* big_string;
-std::string* after_replacing_the;
-std::string* after_replacing_many;
-
-struct Replacement {
-  const char* needle;
-  const char* replacement;
-} replacements[] = {
-    {"the", "box"},          //
-    {"brown", "quick"},      //
-    {"jumped", "liquored"},  //
-    {"dozen", "brown"},      //
-    {"lazy", "pack"},        //
-    {"liquor", "shakes"},    //
-};
-
-// Here, we set up a string for use in global-replace benchmarks.
-// We started with a million blanks, and then deterministically insert
-// 10,000 copies each of two pangrams.  The result is a string that is
-// 40% blank space and 60% these words.  'the' occurs 18,247 times and
-// all the substitutions together occur 49,004 times.
-//
-// We then create "after_replacing_the" to be a string that is a result of
-// replacing "the" with "box" in big_string.
-//
-// And then we create "after_replacing_many" to be a string that is result
-// of preferring several substitutions.
-void SetUpStrings() {
-  if (big_string == nullptr) {
-    size_t r = 0;
-    big_string = new std::string(1000 * 1000, ' ');
-    for (std::string phrase : {"the quick brown fox jumped over the lazy dogs",
-                               "pack my box with the five dozen liquor jugs"}) {
-      for (int i = 0; i < 10 * 1000; ++i) {
-        r = r * 237 + 41;  // not very random.
-        memcpy(&(*big_string)[r % (big_string->size() - phrase.size())],
-               phrase.data(), phrase.size());
-      }
-    }
-    // big_string->resize(50);
-    // OK, we've set up the string, now let's set up expectations - first by
-    // just replacing "the" with "box"
-    after_replacing_the = new std::string(*big_string);
-    for (size_t pos = 0;
-         (pos = after_replacing_the->find("the", pos)) != std::string::npos;) {
-      memcpy(&(*after_replacing_the)[pos], "box", 3);
-    }
-    // And then with all the replacements.
-    after_replacing_many = new std::string(*big_string);
-    for (size_t pos = 0;;) {
-      size_t next_pos = static_cast<size_t>(-1);
-      const char* needle_string = nullptr;
-      const char* replacement_string = nullptr;
-      for (const auto& r : replacements) {
-        auto needlepos = after_replacing_many->find(r.needle, pos);
-        if (needlepos != std::string::npos && needlepos < next_pos) {
-          next_pos = needlepos;
-          needle_string = r.needle;
-          replacement_string = r.replacement;
-        }
-      }
-      if (next_pos > after_replacing_many->size()) break;
-      after_replacing_many->replace(next_pos, strlen(needle_string),
-                                    replacement_string);
-      next_pos += strlen(replacement_string);
-      pos = next_pos;
-    }
-  }
-}
-
-void BM_StrReplaceAllOneReplacement(benchmark::State& state) {
-  SetUpStrings();
-  std::string src = *big_string;
-  for (auto _ : state) {
-    std::string dest = absl::StrReplaceAll(src, {{"the", "box"}});
-    ABSL_RAW_CHECK(dest == *after_replacing_the,
-                   "not benchmarking intended behavior");
-  }
-}
-BENCHMARK(BM_StrReplaceAllOneReplacement);
-
-void BM_StrReplaceAll(benchmark::State& state) {
-  SetUpStrings();
-  std::string src = *big_string;
-  for (auto _ : state) {
-    std::string dest = absl::StrReplaceAll(src, {{"the", "box"},
-                                                 {"brown", "quick"},
-                                                 {"jumped", "liquored"},
-                                                 {"dozen", "brown"},
-                                                 {"lazy", "pack"},
-                                                 {"liquor", "shakes"}});
-    ABSL_RAW_CHECK(dest == *after_replacing_many,
-                   "not benchmarking intended behavior");
-  }
-}
-BENCHMARK(BM_StrReplaceAll);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/str_replace_test.cc b/third_party/abseil_cpp/absl/strings/str_replace_test.cc
deleted file mode 100644
index 9d8c7f75b5..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_replace_test.cc
+++ /dev/null
@@ -1,341 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_replace.h"
-
-#include <list>
-#include <map>
-#include <tuple>
-
-#include "gtest/gtest.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_split.h"
-
-TEST(StrReplaceAll, OneReplacement) {
-  std::string s;
-
-  // Empty string.
-  s = absl::StrReplaceAll(s, {{"", ""}});
-  EXPECT_EQ(s, "");
-  s = absl::StrReplaceAll(s, {{"x", ""}});
-  EXPECT_EQ(s, "");
-  s = absl::StrReplaceAll(s, {{"", "y"}});
-  EXPECT_EQ(s, "");
-  s = absl::StrReplaceAll(s, {{"x", "y"}});
-  EXPECT_EQ(s, "");
-
-  // Empty substring.
-  s = absl::StrReplaceAll("abc", {{"", ""}});
-  EXPECT_EQ(s, "abc");
-  s = absl::StrReplaceAll("abc", {{"", "y"}});
-  EXPECT_EQ(s, "abc");
-  s = absl::StrReplaceAll("abc", {{"x", ""}});
-  EXPECT_EQ(s, "abc");
-
-  // Substring not found.
-  s = absl::StrReplaceAll("abc", {{"xyz", "123"}});
-  EXPECT_EQ(s, "abc");
-
-  // Replace entire string.
-  s = absl::StrReplaceAll("abc", {{"abc", "xyz"}});
-  EXPECT_EQ(s, "xyz");
-
-  // Replace once at the start.
-  s = absl::StrReplaceAll("abc", {{"a", "x"}});
-  EXPECT_EQ(s, "xbc");
-
-  // Replace once in the middle.
-  s = absl::StrReplaceAll("abc", {{"b", "x"}});
-  EXPECT_EQ(s, "axc");
-
-  // Replace once at the end.
-  s = absl::StrReplaceAll("abc", {{"c", "x"}});
-  EXPECT_EQ(s, "abx");
-
-  // Replace multiple times with varying lengths of original/replacement.
-  s = absl::StrReplaceAll("ababa", {{"a", "xxx"}});
-  EXPECT_EQ(s, "xxxbxxxbxxx");
-
-  s = absl::StrReplaceAll("ababa", {{"b", "xxx"}});
-  EXPECT_EQ(s, "axxxaxxxa");
-
-  s = absl::StrReplaceAll("aaabaaabaaa", {{"aaa", "x"}});
-  EXPECT_EQ(s, "xbxbx");
-
-  s = absl::StrReplaceAll("abbbabbba", {{"bbb", "x"}});
-  EXPECT_EQ(s, "axaxa");
-
-  // Overlapping matches are replaced greedily.
-  s = absl::StrReplaceAll("aaa", {{"aa", "x"}});
-  EXPECT_EQ(s, "xa");
-
-  // The replacements are not recursive.
-  s = absl::StrReplaceAll("aaa", {{"aa", "a"}});
-  EXPECT_EQ(s, "aa");
-}
-
-TEST(StrReplaceAll, ManyReplacements) {
-  std::string s;
-
-  // Empty string.
-  s = absl::StrReplaceAll("", {{"", ""}, {"x", ""}, {"", "y"}, {"x", "y"}});
-  EXPECT_EQ(s, "");
-
-  // Empty substring.
-  s = absl::StrReplaceAll("abc", {{"", ""}, {"", "y"}, {"x", ""}});
-  EXPECT_EQ(s, "abc");
-
-  // Replace entire string, one char at a time
-  s = absl::StrReplaceAll("abc", {{"a", "x"}, {"b", "y"}, {"c", "z"}});
-  EXPECT_EQ(s, "xyz");
-  s = absl::StrReplaceAll("zxy", {{"z", "x"}, {"x", "y"}, {"y", "z"}});
-  EXPECT_EQ(s, "xyz");
-
-  // Replace once at the start (longer matches take precedence)
-  s = absl::StrReplaceAll("abc", {{"a", "x"}, {"ab", "xy"}, {"abc", "xyz"}});
-  EXPECT_EQ(s, "xyz");
-
-  // Replace once in the middle.
-  s = absl::StrReplaceAll(
-      "Abc!", {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc", "yz"}, {"c", "z"}});
-  EXPECT_EQ(s, "Ayz!");
-
-  // Replace once at the end.
-  s = absl::StrReplaceAll(
-      "Abc!",
-      {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc!", "yz?"}, {"c!", "z;"}});
-  EXPECT_EQ(s, "Ayz?");
-
-  // Replace multiple times with varying lengths of original/replacement.
-  s = absl::StrReplaceAll("ababa", {{"a", "xxx"}, {"b", "XXXX"}});
-  EXPECT_EQ(s, "xxxXXXXxxxXXXXxxx");
-
-  // Overlapping matches are replaced greedily.
-  s = absl::StrReplaceAll("aaa", {{"aa", "x"}, {"a", "X"}});
-  EXPECT_EQ(s, "xX");
-  s = absl::StrReplaceAll("aaa", {{"a", "X"}, {"aa", "x"}});
-  EXPECT_EQ(s, "xX");
-
-  // Two well-known sentences
-  s = absl::StrReplaceAll("the quick brown fox jumped over the lazy dogs",
-                          {
-                              {"brown", "box"},
-                              {"dogs", "jugs"},
-                              {"fox", "with"},
-                              {"jumped", "five"},
-                              {"over", "dozen"},
-                              {"quick", "my"},
-                              {"the", "pack"},
-                              {"the lazy", "liquor"},
-                          });
-  EXPECT_EQ(s, "pack my box with five dozen liquor jugs");
-}
-
-TEST(StrReplaceAll, ManyReplacementsInMap) {
-  std::map<const char *, const char *> replacements;
-  replacements["$who"] = "Bob";
-  replacements["$count"] = "5";
-  replacements["#Noun"] = "Apples";
-  std::string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!",
-                                      replacements);
-  EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
-}
-
-TEST(StrReplaceAll, ReplacementsInPlace) {
-  std::string s = std::string("$who bought $count #Noun. Thanks $who!");
-  int count;
-  count = absl::StrReplaceAll({{"$count", absl::StrCat(5)},
-                              {"$who", "Bob"},
-                              {"#Noun", "Apples"}}, &s);
-  EXPECT_EQ(count, 4);
-  EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
-}
-
-TEST(StrReplaceAll, ReplacementsInPlaceInMap) {
-  std::string s = std::string("$who bought $count #Noun. Thanks $who!");
-  std::map<absl::string_view, absl::string_view> replacements;
-  replacements["$who"] = "Bob";
-  replacements["$count"] = "5";
-  replacements["#Noun"] = "Apples";
-  int count;
-  count = absl::StrReplaceAll(replacements, &s);
-  EXPECT_EQ(count, 4);
-  EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
-}
-
-struct Cont {
-  Cont() {}
-  explicit Cont(absl::string_view src) : data(src) {}
-
-  absl::string_view data;
-};
-
-template <int index>
-absl::string_view get(const Cont& c) {
-  auto splitter = absl::StrSplit(c.data, ':');
-  auto it = splitter.begin();
-  for (int i = 0; i < index; ++i) ++it;
-
-  return *it;
-}
-
-TEST(StrReplaceAll, VariableNumber) {
-  std::string s;
-  {
-    std::vector<std::pair<std::string, std::string>> replacements;
-
-    s = "abc";
-    EXPECT_EQ(0, absl::StrReplaceAll(replacements, &s));
-    EXPECT_EQ("abc", s);
-
-    s = "abc";
-    replacements.push_back({"a", "A"});
-    EXPECT_EQ(1, absl::StrReplaceAll(replacements, &s));
-    EXPECT_EQ("Abc", s);
-
-    s = "abc";
-    replacements.push_back({"b", "B"});
-    EXPECT_EQ(2, absl::StrReplaceAll(replacements, &s));
-    EXPECT_EQ("ABc", s);
-
-    s = "abc";
-    replacements.push_back({"d", "D"});
-    EXPECT_EQ(2, absl::StrReplaceAll(replacements, &s));
-    EXPECT_EQ("ABc", s);
-
-    EXPECT_EQ("ABcABc", absl::StrReplaceAll("abcabc", replacements));
-  }
-
-  {
-    std::map<const char*, const char*> replacements;
-    replacements["aa"] = "x";
-    replacements["a"] = "X";
-    s = "aaa";
-    EXPECT_EQ(2, absl::StrReplaceAll(replacements, &s));
-    EXPECT_EQ("xX", s);
-
-    EXPECT_EQ("xxX", absl::StrReplaceAll("aaaaa", replacements));
-  }
-
-  {
-    std::list<std::pair<absl::string_view, absl::string_view>> replacements = {
-        {"a", "x"}, {"b", "y"}, {"c", "z"}};
-
-    std::string s = absl::StrReplaceAll("abc", replacements);
-    EXPECT_EQ(s, "xyz");
-  }
-
-  {
-    using X = std::tuple<absl::string_view, std::string, int>;
-    std::vector<X> replacements(3);
-    replacements[0] = X{"a", "x", 1};
-    replacements[1] = X{"b", "y", 0};
-    replacements[2] = X{"c", "z", -1};
-
-    std::string s = absl::StrReplaceAll("abc", replacements);
-    EXPECT_EQ(s, "xyz");
-  }
-
-  {
-    std::vector<Cont> replacements(3);
-    replacements[0] = Cont{"a:x"};
-    replacements[1] = Cont{"b:y"};
-    replacements[2] = Cont{"c:z"};
-
-    std::string s = absl::StrReplaceAll("abc", replacements);
-    EXPECT_EQ(s, "xyz");
-  }
-}
-
-// Same as above, but using the in-place variant of absl::StrReplaceAll,
-// that returns the # of replacements performed.
-TEST(StrReplaceAll, Inplace) {
-  std::string s;
-  int reps;
-
-  // Empty string.
-  s = "";
-  reps = absl::StrReplaceAll({{"", ""}, {"x", ""}, {"", "y"}, {"x", "y"}}, &s);
-  EXPECT_EQ(reps, 0);
-  EXPECT_EQ(s, "");
-
-  // Empty substring.
-  s = "abc";
-  reps = absl::StrReplaceAll({{"", ""}, {"", "y"}, {"x", ""}}, &s);
-  EXPECT_EQ(reps, 0);
-  EXPECT_EQ(s, "abc");
-
-  // Replace entire string, one char at a time
-  s = "abc";
-  reps = absl::StrReplaceAll({{"a", "x"}, {"b", "y"}, {"c", "z"}}, &s);
-  EXPECT_EQ(reps, 3);
-  EXPECT_EQ(s, "xyz");
-  s = "zxy";
-  reps = absl::StrReplaceAll({{"z", "x"}, {"x", "y"}, {"y", "z"}}, &s);
-  EXPECT_EQ(reps, 3);
-  EXPECT_EQ(s, "xyz");
-
-  // Replace once at the start (longer matches take precedence)
-  s = "abc";
-  reps = absl::StrReplaceAll({{"a", "x"}, {"ab", "xy"}, {"abc", "xyz"}}, &s);
-  EXPECT_EQ(reps, 1);
-  EXPECT_EQ(s, "xyz");
-
-  // Replace once in the middle.
-  s = "Abc!";
-  reps = absl::StrReplaceAll(
-      {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc", "yz"}, {"c", "z"}}, &s);
-  EXPECT_EQ(reps, 1);
-  EXPECT_EQ(s, "Ayz!");
-
-  // Replace once at the end.
-  s = "Abc!";
-  reps = absl::StrReplaceAll(
-      {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc!", "yz?"}, {"c!", "z;"}}, &s);
-  EXPECT_EQ(reps, 1);
-  EXPECT_EQ(s, "Ayz?");
-
-  // Replace multiple times with varying lengths of original/replacement.
-  s = "ababa";
-  reps = absl::StrReplaceAll({{"a", "xxx"}, {"b", "XXXX"}}, &s);
-  EXPECT_EQ(reps, 5);
-  EXPECT_EQ(s, "xxxXXXXxxxXXXXxxx");
-
-  // Overlapping matches are replaced greedily.
-  s = "aaa";
-  reps = absl::StrReplaceAll({{"aa", "x"}, {"a", "X"}}, &s);
-  EXPECT_EQ(reps, 2);
-  EXPECT_EQ(s, "xX");
-  s = "aaa";
-  reps = absl::StrReplaceAll({{"a", "X"}, {"aa", "x"}}, &s);
-  EXPECT_EQ(reps, 2);
-  EXPECT_EQ(s, "xX");
-
-  // Two well-known sentences
-  s = "the quick brown fox jumped over the lazy dogs";
-  reps = absl::StrReplaceAll(
-      {
-          {"brown", "box"},
-          {"dogs", "jugs"},
-          {"fox", "with"},
-          {"jumped", "five"},
-          {"over", "dozen"},
-          {"quick", "my"},
-          {"the", "pack"},
-          {"the lazy", "liquor"},
-      },
-      &s);
-  EXPECT_EQ(reps, 8);
-  EXPECT_EQ(s, "pack my box with five dozen liquor jugs");
-}
diff --git a/third_party/abseil_cpp/absl/strings/str_split.cc b/third_party/abseil_cpp/absl/strings/str_split.cc
deleted file mode 100644
index e08c26b6bb..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_split.cc
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_split.h"
-
-#include <algorithm>
-#include <cassert>
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-#include <iterator>
-#include <limits>
-#include <memory>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/ascii.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace {
-
-// This GenericFind() template function encapsulates the finding algorithm
-// shared between the ByString and ByAnyChar delimiters. The FindPolicy
-// template parameter allows each delimiter to customize the actual find
-// function to use and the length of the found delimiter. For example, the
-// Literal delimiter will ultimately use absl::string_view::find(), and the
-// AnyOf delimiter will use absl::string_view::find_first_of().
-template <typename FindPolicy>
-absl::string_view GenericFind(absl::string_view text,
-                              absl::string_view delimiter, size_t pos,
-                              FindPolicy find_policy) {
-  if (delimiter.empty() && text.length() > 0) {
-    // Special case for empty string delimiters: always return a zero-length
-    // absl::string_view referring to the item at position 1 past pos.
-    return absl::string_view(text.data() + pos + 1, 0);
-  }
-  size_t found_pos = absl::string_view::npos;
-  absl::string_view found(text.data() + text.size(),
-                          0);  // By default, not found
-  found_pos = find_policy.Find(text, delimiter, pos);
-  if (found_pos != absl::string_view::npos) {
-    found = absl::string_view(text.data() + found_pos,
-                              find_policy.Length(delimiter));
-  }
-  return found;
-}
-
-// Finds using absl::string_view::find(), therefore the length of the found
-// delimiter is delimiter.length().
-struct LiteralPolicy {
-  size_t Find(absl::string_view text, absl::string_view delimiter, size_t pos) {
-    return text.find(delimiter, pos);
-  }
-  size_t Length(absl::string_view delimiter) { return delimiter.length(); }
-};
-
-// Finds using absl::string_view::find_first_of(), therefore the length of the
-// found delimiter is 1.
-struct AnyOfPolicy {
-  size_t Find(absl::string_view text, absl::string_view delimiter, size_t pos) {
-    return text.find_first_of(delimiter, pos);
-  }
-  size_t Length(absl::string_view /* delimiter */) { return 1; }
-};
-
-}  // namespace
-
-//
-// ByString
-//
-
-ByString::ByString(absl::string_view sp) : delimiter_(sp) {}
-
-absl::string_view ByString::Find(absl::string_view text, size_t pos) const {
-  if (delimiter_.length() == 1) {
-    // Much faster to call find on a single character than on an
-    // absl::string_view.
-    size_t found_pos = text.find(delimiter_[0], pos);
-    if (found_pos == absl::string_view::npos)
-      return absl::string_view(text.data() + text.size(), 0);
-    return text.substr(found_pos, 1);
-  }
-  return GenericFind(text, delimiter_, pos, LiteralPolicy());
-}
-
-//
-// ByChar
-//
-
-absl::string_view ByChar::Find(absl::string_view text, size_t pos) const {
-  size_t found_pos = text.find(c_, pos);
-  if (found_pos == absl::string_view::npos)
-    return absl::string_view(text.data() + text.size(), 0);
-  return text.substr(found_pos, 1);
-}
-
-//
-// ByAnyChar
-//
-
-ByAnyChar::ByAnyChar(absl::string_view sp) : delimiters_(sp) {}
-
-absl::string_view ByAnyChar::Find(absl::string_view text, size_t pos) const {
-  return GenericFind(text, delimiters_, pos, AnyOfPolicy());
-}
-
-//
-// ByLength
-//
-ByLength::ByLength(ptrdiff_t length) : length_(length) {
-  ABSL_RAW_CHECK(length > 0, "");
-}
-
-absl::string_view ByLength::Find(absl::string_view text,
-                                      size_t pos) const {
-  pos = std::min(pos, text.size());  // truncate `pos`
-  absl::string_view substr = text.substr(pos);
-  // If the string is shorter than the chunk size we say we
-  // "can't find the delimiter" so this will be the last chunk.
-  if (substr.length() <= static_cast<size_t>(length_))
-    return absl::string_view(text.data() + text.size(), 0);
-
-  return absl::string_view(substr.data() + length_, 0);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/str_split.h b/third_party/abseil_cpp/absl/strings/str_split.h
deleted file mode 100644
index bfbca422a8..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_split.h
+++ /dev/null
@@ -1,548 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: str_split.h
-// -----------------------------------------------------------------------------
-//
-// This file contains functions for splitting strings. It defines the main
-// `StrSplit()` function, several delimiters for determining the boundaries on
-// which to split the string, and predicates for filtering delimited results.
-// `StrSplit()` adapts the returned collection to the type specified by the
-// caller.
-//
-// Example:
-//
-//   // Splits the given string on commas. Returns the results in a
-//   // vector of strings.
-//   std::vector<std::string> v = absl::StrSplit("a,b,c", ',');
-//   // Can also use ","
-//   // v[0] == "a", v[1] == "b", v[2] == "c"
-//
-// See StrSplit() below for more information.
-#ifndef ABSL_STRINGS_STR_SPLIT_H_
-#define ABSL_STRINGS_STR_SPLIT_H_
-
-#include <algorithm>
-#include <cstddef>
-#include <map>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/strings/internal/str_split_internal.h"
-#include "absl/strings/string_view.h"
-#include "absl/strings/strip.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-//------------------------------------------------------------------------------
-// Delimiters
-//------------------------------------------------------------------------------
-//
-// `StrSplit()` uses delimiters to define the boundaries between elements in the
-// provided input. Several `Delimiter` types are defined below. If a string
-// (`const char*`, `std::string`, or `absl::string_view`) is passed in place of
-// an explicit `Delimiter` object, `StrSplit()` treats it the same way as if it
-// were passed a `ByString` delimiter.
-//
-// A `Delimiter` is an object with a `Find()` function that knows how to find
-// the first occurrence of itself in a given `absl::string_view`.
-//
-// The following `Delimiter` types are available for use within `StrSplit()`:
-//
-//   - `ByString` (default for string arguments)
-//   - `ByChar` (default for a char argument)
-//   - `ByAnyChar`
-//   - `ByLength`
-//   - `MaxSplits`
-//
-// A Delimiter's `Find()` member function will be passed an input `text` that is
-// to be split and a position (`pos`) to begin searching for the next delimiter
-// in `text`. The returned absl::string_view should refer to the next occurrence
-// (after `pos`) of the represented delimiter; this returned absl::string_view
-// represents the next location where the input `text` should be broken.
-//
-// The returned absl::string_view may be zero-length if the Delimiter does not
-// represent a part of the string (e.g., a fixed-length delimiter). If no
-// delimiter is found in the input `text`, a zero-length absl::string_view
-// referring to `text.end()` should be returned (e.g.,
-// `text.substr(text.size())`). It is important that the returned
-// absl::string_view always be within the bounds of the input `text` given as an
-// argument--it must not refer to a string that is physically located outside of
-// the given string.
-//
-// The following example is a simple Delimiter object that is created with a
-// single char and will look for that char in the text passed to the `Find()`
-// function:
-//
-//   struct SimpleDelimiter {
-//     const char c_;
-//     explicit SimpleDelimiter(char c) : c_(c) {}
-//     absl::string_view Find(absl::string_view text, size_t pos) {
-//       auto found = text.find(c_, pos);
-//       if (found == absl::string_view::npos)
-//         return text.substr(text.size());
-//
-//       return text.substr(found, 1);
-//     }
-//   };
-
-// ByString
-//
-// A sub-string delimiter. If `StrSplit()` is passed a string in place of a
-// `Delimiter` object, the string will be implicitly converted into a
-// `ByString` delimiter.
-//
-// Example:
-//
-//   // Because a string literal is converted to an `absl::ByString`,
-//   // the following two splits are equivalent.
-//
-//   std::vector<std::string> v1 = absl::StrSplit("a, b, c", ", ");
-//
-//   using absl::ByString;
-//   std::vector<std::string> v2 = absl::StrSplit("a, b, c",
-//                                                ByString(", "));
-//   // v[0] == "a", v[1] == "b", v[2] == "c"
-class ByString {
- public:
-  explicit ByString(absl::string_view sp);
-  absl::string_view Find(absl::string_view text, size_t pos) const;
-
- private:
-  const std::string delimiter_;
-};
-
-// ByChar
-//
-// A single character delimiter. `ByChar` is functionally equivalent to a
-// 1-char string within a `ByString` delimiter, but slightly more efficient.
-//
-// Example:
-//
-//   // Because a char literal is converted to a absl::ByChar,
-//   // the following two splits are equivalent.
-//   std::vector<std::string> v1 = absl::StrSplit("a,b,c", ',');
-//   using absl::ByChar;
-//   std::vector<std::string> v2 = absl::StrSplit("a,b,c", ByChar(','));
-//   // v[0] == "a", v[1] == "b", v[2] == "c"
-//
-// `ByChar` is also the default delimiter if a single character is given
-// as the delimiter to `StrSplit()`. For example, the following calls are
-// equivalent:
-//
-//   std::vector<std::string> v = absl::StrSplit("a-b", '-');
-//
-//   using absl::ByChar;
-//   std::vector<std::string> v = absl::StrSplit("a-b", ByChar('-'));
-//
-class ByChar {
- public:
-  explicit ByChar(char c) : c_(c) {}
-  absl::string_view Find(absl::string_view text, size_t pos) const;
-
- private:
-  char c_;
-};
-
-// ByAnyChar
-//
-// A delimiter that will match any of the given byte-sized characters within
-// its provided string.
-//
-// Note: this delimiter works with single-byte string data, but does not work
-// with variable-width encodings, such as UTF-8.
-//
-// Example:
-//
-//   using absl::ByAnyChar;
-//   std::vector<std::string> v = absl::StrSplit("a,b=c", ByAnyChar(",="));
-//   // v[0] == "a", v[1] == "b", v[2] == "c"
-//
-// If `ByAnyChar` is given the empty string, it behaves exactly like
-// `ByString` and matches each individual character in the input string.
-//
-class ByAnyChar {
- public:
-  explicit ByAnyChar(absl::string_view sp);
-  absl::string_view Find(absl::string_view text, size_t pos) const;
-
- private:
-  const std::string delimiters_;
-};
-
-// ByLength
-//
-// A delimiter for splitting into equal-length strings. The length argument to
-// the constructor must be greater than 0.
-//
-// Note: this delimiter works with single-byte string data, but does not work
-// with variable-width encodings, such as UTF-8.
-//
-// Example:
-//
-//   using absl::ByLength;
-//   std::vector<std::string> v = absl::StrSplit("123456789", ByLength(3));
-
-//   // v[0] == "123", v[1] == "456", v[2] == "789"
-//
-// Note that the string does not have to be a multiple of the fixed split
-// length. In such a case, the last substring will be shorter.
-//
-//   using absl::ByLength;
-//   std::vector<std::string> v = absl::StrSplit("12345", ByLength(2));
-//
-//   // v[0] == "12", v[1] == "34", v[2] == "5"
-class ByLength {
- public:
-  explicit ByLength(ptrdiff_t length);
-  absl::string_view Find(absl::string_view text, size_t pos) const;
-
- private:
-  const ptrdiff_t length_;
-};
-
-namespace strings_internal {
-
-// A traits-like metafunction for selecting the default Delimiter object type
-// for a particular Delimiter type. The base case simply exposes type Delimiter
-// itself as the delimiter's Type. However, there are specializations for
-// string-like objects that map them to the ByString delimiter object.
-// This allows functions like absl::StrSplit() and absl::MaxSplits() to accept
-// string-like objects (e.g., ',') as delimiter arguments but they will be
-// treated as if a ByString delimiter was given.
-template <typename Delimiter>
-struct SelectDelimiter {
-  using type = Delimiter;
-};
-
-template <>
-struct SelectDelimiter<char> {
-  using type = ByChar;
-};
-template <>
-struct SelectDelimiter<char*> {
-  using type = ByString;
-};
-template <>
-struct SelectDelimiter<const char*> {
-  using type = ByString;
-};
-template <>
-struct SelectDelimiter<absl::string_view> {
-  using type = ByString;
-};
-template <>
-struct SelectDelimiter<std::string> {
-  using type = ByString;
-};
-
-// Wraps another delimiter and sets a max number of matches for that delimiter.
-template <typename Delimiter>
-class MaxSplitsImpl {
- public:
-  MaxSplitsImpl(Delimiter delimiter, int limit)
-      : delimiter_(delimiter), limit_(limit), count_(0) {}
-  absl::string_view Find(absl::string_view text, size_t pos) {
-    if (count_++ == limit_) {
-      return absl::string_view(text.data() + text.size(),
-                               0);  // No more matches.
-    }
-    return delimiter_.Find(text, pos);
-  }
-
- private:
-  Delimiter delimiter_;
-  const int limit_;
-  int count_;
-};
-
-}  // namespace strings_internal
-
-// MaxSplits()
-//
-// A delimiter that limits the number of matches which can occur to the passed
-// `limit`. The last element in the returned collection will contain all
-// remaining unsplit pieces, which may contain instances of the delimiter.
-// The collection will contain at most `limit` + 1 elements.
-// Example:
-//
-//   using absl::MaxSplits;
-//   std::vector<std::string> v = absl::StrSplit("a,b,c", MaxSplits(',', 1));
-//
-//   // v[0] == "a", v[1] == "b,c"
-template <typename Delimiter>
-inline strings_internal::MaxSplitsImpl<
-    typename strings_internal::SelectDelimiter<Delimiter>::type>
-MaxSplits(Delimiter delimiter, int limit) {
-  typedef
-      typename strings_internal::SelectDelimiter<Delimiter>::type DelimiterType;
-  return strings_internal::MaxSplitsImpl<DelimiterType>(
-      DelimiterType(delimiter), limit);
-}
-
-//------------------------------------------------------------------------------
-// Predicates
-//------------------------------------------------------------------------------
-//
-// Predicates filter the results of a `StrSplit()` by determining whether or not
-// a resultant element is included in the result set. A predicate may be passed
-// as an optional third argument to the `StrSplit()` function.
-//
-// Predicates are unary functions (or functors) that take a single
-// `absl::string_view` argument and return a bool indicating whether the
-// argument should be included (`true`) or excluded (`false`).
-//
-// Predicates are useful when filtering out empty substrings. By default, empty
-// substrings may be returned by `StrSplit()`, which is similar to the way split
-// functions work in other programming languages.
-
-// AllowEmpty()
-//
-// Always returns `true`, indicating that all strings--including empty
-// strings--should be included in the split output. This predicate is not
-// strictly needed because this is the default behavior of `StrSplit()`;
-// however, it might be useful at some call sites to make the intent explicit.
-//
-// Example:
-//
-//  std::vector<std::string> v = absl::StrSplit(" a , ,,b,", ',', AllowEmpty());
-//
-//  // v[0] == " a ", v[1] == " ", v[2] == "", v[3] = "b", v[4] == ""
-struct AllowEmpty {
-  bool operator()(absl::string_view) const { return true; }
-};
-
-// SkipEmpty()
-//
-// Returns `false` if the given `absl::string_view` is empty, indicating that
-// `StrSplit()` should omit the empty string.
-//
-// Example:
-//
-//   std::vector<std::string> v = absl::StrSplit(",a,,b,", ',', SkipEmpty());
-//
-//   // v[0] == "a", v[1] == "b"
-//
-// Note: `SkipEmpty()` does not consider a string containing only whitespace
-// to be empty. To skip such whitespace as well, use the `SkipWhitespace()`
-// predicate.
-struct SkipEmpty {
-  bool operator()(absl::string_view sp) const { return !sp.empty(); }
-};
-
-// SkipWhitespace()
-//
-// Returns `false` if the given `absl::string_view` is empty *or* contains only
-// whitespace, indicating that `StrSplit()` should omit the string.
-//
-// Example:
-//
-//   std::vector<std::string> v = absl::StrSplit(" a , ,,b,",
-//                                               ',', SkipWhitespace());
-//   // v[0] == " a ", v[1] == "b"
-//
-//   // SkipEmpty() would return whitespace elements
-//   std::vector<std::string> v = absl::StrSplit(" a , ,,b,", ',', SkipEmpty());
-//   // v[0] == " a ", v[1] == " ", v[2] == "b"
-struct SkipWhitespace {
-  bool operator()(absl::string_view sp) const {
-    sp = absl::StripAsciiWhitespace(sp);
-    return !sp.empty();
-  }
-};
-
-template <typename T>
-using EnableSplitIfString =
-    typename std::enable_if<std::is_same<T, std::string>::value ||
-                            std::is_same<T, const std::string>::value,
-                            int>::type;
-
-//------------------------------------------------------------------------------
-//                                  StrSplit()
-//------------------------------------------------------------------------------
-
-// StrSplit()
-//
-// Splits a given string based on the provided `Delimiter` object, returning the
-// elements within the type specified by the caller. Optionally, you may pass a
-// `Predicate` to `StrSplit()` indicating whether to include or exclude the
-// resulting element within the final result set. (See the overviews for
-// Delimiters and Predicates above.)
-//
-// Example:
-//
-//   std::vector<std::string> v = absl::StrSplit("a,b,c,d", ',');
-//   // v[0] == "a", v[1] == "b", v[2] == "c", v[3] == "d"
-//
-// You can also provide an explicit `Delimiter` object:
-//
-// Example:
-//
-//   using absl::ByAnyChar;
-//   std::vector<std::string> v = absl::StrSplit("a,b=c", ByAnyChar(",="));
-//   // v[0] == "a", v[1] == "b", v[2] == "c"
-//
-// See above for more information on delimiters.
-//
-// By default, empty strings are included in the result set. You can optionally
-// include a third `Predicate` argument to apply a test for whether the
-// resultant element should be included in the result set:
-//
-// Example:
-//
-//   std::vector<std::string> v = absl::StrSplit(" a , ,,b,",
-//                                               ',', SkipWhitespace());
-//   // v[0] == " a ", v[1] == "b"
-//
-// See above for more information on predicates.
-//
-//------------------------------------------------------------------------------
-// StrSplit() Return Types
-//------------------------------------------------------------------------------
-//
-// The `StrSplit()` function adapts the returned collection to the collection
-// specified by the caller (e.g. `std::vector` above). The returned collections
-// may contain `std::string`, `absl::string_view` (in which case the original
-// string being split must ensure that it outlives the collection), or any
-// object that can be explicitly created from an `absl::string_view`. This
-// behavior works for:
-//
-// 1) All standard STL containers including `std::vector`, `std::list`,
-//    `std::deque`, `std::set`,`std::multiset`, 'std::map`, and `std::multimap`
-// 2) `std::pair` (which is not actually a container). See below.
-//
-// Example:
-//
-//   // The results are returned as `absl::string_view` objects. Note that we
-//   // have to ensure that the input string outlives any results.
-//   std::vector<absl::string_view> v = absl::StrSplit("a,b,c", ',');
-//
-//   // Stores results in a std::set<std::string>, which also performs
-//   // de-duplication and orders the elements in ascending order.
-//   std::set<std::string> a = absl::StrSplit("b,a,c,a,b", ',');
-//   // v[0] == "a", v[1] == "b", v[2] = "c"
-//
-//   // `StrSplit()` can be used within a range-based for loop, in which case
-//   // each element will be of type `absl::string_view`.
-//   std::vector<std::string> v;
-//   for (const auto sv : absl::StrSplit("a,b,c", ',')) {
-//     if (sv != "b") v.emplace_back(sv);
-//   }
-//   // v[0] == "a", v[1] == "c"
-//
-//   // Stores results in a map. The map implementation assumes that the input
-//   // is provided as a series of key/value pairs. For example, the 0th element
-//   // resulting from the split will be stored as a key to the 1st element. If
-//   // an odd number of elements are resolved, the last element is paired with
-//   // a default-constructed value (e.g., empty string).
-//   std::map<std::string, std::string> m = absl::StrSplit("a,b,c", ',');
-//   // m["a"] == "b", m["c"] == ""     // last component value equals ""
-//
-// Splitting to `std::pair` is an interesting case because it can hold only two
-// elements and is not a collection type. When splitting to a `std::pair` the
-// first two split strings become the `std::pair` `.first` and `.second`
-// members, respectively. The remaining split substrings are discarded. If there
-// are less than two split substrings, the empty string is used for the
-// corresponding
-// `std::pair` member.
-//
-// Example:
-//
-//   // Stores first two split strings as the members in a std::pair.
-//   std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ',');
-//   // p.first == "a", p.second == "b"       // "c" is omitted.
-//
-// The `StrSplit()` function can be used multiple times to perform more
-// complicated splitting logic, such as intelligently parsing key-value pairs.
-//
-// Example:
-//
-//   // The input string "a=b=c,d=e,f=,g" becomes
-//   // { "a" => "b=c", "d" => "e", "f" => "", "g" => "" }
-//   std::map<std::string, std::string> m;
-//   for (absl::string_view sp : absl::StrSplit("a=b=c,d=e,f=,g", ',')) {
-//     m.insert(absl::StrSplit(sp, absl::MaxSplits('=', 1)));
-//   }
-//   EXPECT_EQ("b=c", m.find("a")->second);
-//   EXPECT_EQ("e", m.find("d")->second);
-//   EXPECT_EQ("", m.find("f")->second);
-//   EXPECT_EQ("", m.find("g")->second);
-//
-// WARNING: Due to a legacy bug that is maintained for backward compatibility,
-// splitting the following empty string_views produces different results:
-//
-//   absl::StrSplit(absl::string_view(""), '-');  // {""}
-//   absl::StrSplit(absl::string_view(), '-');    // {}, but should be {""}
-//
-// Try not to depend on this distinction because the bug may one day be fixed.
-template <typename Delimiter>
-strings_internal::Splitter<
-    typename strings_internal::SelectDelimiter<Delimiter>::type, AllowEmpty,
-    absl::string_view>
-StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d) {
-  using DelimiterType =
-      typename strings_internal::SelectDelimiter<Delimiter>::type;
-  return strings_internal::Splitter<DelimiterType, AllowEmpty,
-                                    absl::string_view>(
-      text.value(), DelimiterType(d), AllowEmpty());
-}
-
-template <typename Delimiter, typename StringType,
-          EnableSplitIfString<StringType> = 0>
-strings_internal::Splitter<
-    typename strings_internal::SelectDelimiter<Delimiter>::type, AllowEmpty,
-    std::string>
-StrSplit(StringType&& text, Delimiter d) {
-  using DelimiterType =
-      typename strings_internal::SelectDelimiter<Delimiter>::type;
-  return strings_internal::Splitter<DelimiterType, AllowEmpty, std::string>(
-      std::move(text), DelimiterType(d), AllowEmpty());
-}
-
-template <typename Delimiter, typename Predicate>
-strings_internal::Splitter<
-    typename strings_internal::SelectDelimiter<Delimiter>::type, Predicate,
-    absl::string_view>
-StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d,
-         Predicate p) {
-  using DelimiterType =
-      typename strings_internal::SelectDelimiter<Delimiter>::type;
-  return strings_internal::Splitter<DelimiterType, Predicate,
-                                    absl::string_view>(
-      text.value(), DelimiterType(d), std::move(p));
-}
-
-template <typename Delimiter, typename Predicate, typename StringType,
-          EnableSplitIfString<StringType> = 0>
-strings_internal::Splitter<
-    typename strings_internal::SelectDelimiter<Delimiter>::type, Predicate,
-    std::string>
-StrSplit(StringType&& text, Delimiter d, Predicate p) {
-  using DelimiterType =
-      typename strings_internal::SelectDelimiter<Delimiter>::type;
-  return strings_internal::Splitter<DelimiterType, Predicate, std::string>(
-      std::move(text), DelimiterType(d), std::move(p));
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_STR_SPLIT_H_
diff --git a/third_party/abseil_cpp/absl/strings/str_split_benchmark.cc b/third_party/abseil_cpp/absl/strings/str_split_benchmark.cc
deleted file mode 100644
index f38dfcfe5a..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_split_benchmark.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_split.h"
-
-#include <iterator>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/string_view.h"
-
-namespace {
-
-std::string MakeTestString(int desired_length) {
-  static const int kAverageValueLen = 25;
-  std::string test(desired_length * kAverageValueLen, 'x');
-  for (int i = 1; i < test.size(); i += kAverageValueLen) {
-    test[i] = ';';
-  }
-  return test;
-}
-
-void BM_Split2StringView(benchmark::State& state) {
-  std::string test = MakeTestString(state.range(0));
-  for (auto _ : state) {
-    std::vector<absl::string_view> result = absl::StrSplit(test, ';');
-    benchmark::DoNotOptimize(result);
-  }
-}
-BENCHMARK_RANGE(BM_Split2StringView, 0, 1 << 20);
-
-static const absl::string_view kDelimiters = ";:,.";
-
-std::string MakeMultiDelimiterTestString(int desired_length) {
-  static const int kAverageValueLen = 25;
-  std::string test(desired_length * kAverageValueLen, 'x');
-  for (int i = 0; i * kAverageValueLen < test.size(); ++i) {
-    // Cycle through a variety of delimiters.
-    test[i * kAverageValueLen] = kDelimiters[i % kDelimiters.size()];
-  }
-  return test;
-}
-
-// Measure StrSplit with ByAnyChar with four delimiters to choose from.
-void BM_Split2StringViewByAnyChar(benchmark::State& state) {
-  std::string test = MakeMultiDelimiterTestString(state.range(0));
-  for (auto _ : state) {
-    std::vector<absl::string_view> result =
-        absl::StrSplit(test, absl::ByAnyChar(kDelimiters));
-    benchmark::DoNotOptimize(result);
-  }
-}
-BENCHMARK_RANGE(BM_Split2StringViewByAnyChar, 0, 1 << 20);
-
-void BM_Split2StringViewLifted(benchmark::State& state) {
-  std::string test = MakeTestString(state.range(0));
-  std::vector<absl::string_view> result;
-  for (auto _ : state) {
-    result = absl::StrSplit(test, ';');
-  }
-  benchmark::DoNotOptimize(result);
-}
-BENCHMARK_RANGE(BM_Split2StringViewLifted, 0, 1 << 20);
-
-void BM_Split2String(benchmark::State& state) {
-  std::string test = MakeTestString(state.range(0));
-  for (auto _ : state) {
-    std::vector<std::string> result = absl::StrSplit(test, ';');
-    benchmark::DoNotOptimize(result);
-  }
-}
-BENCHMARK_RANGE(BM_Split2String, 0, 1 << 20);
-
-// This benchmark is for comparing Split2 to Split1 (SplitStringUsing). In
-// particular, this benchmark uses SkipEmpty() to match SplitStringUsing's
-// behavior.
-void BM_Split2SplitStringUsing(benchmark::State& state) {
-  std::string test = MakeTestString(state.range(0));
-  for (auto _ : state) {
-    std::vector<std::string> result =
-        absl::StrSplit(test, ';', absl::SkipEmpty());
-    benchmark::DoNotOptimize(result);
-  }
-}
-BENCHMARK_RANGE(BM_Split2SplitStringUsing, 0, 1 << 20);
-
-void BM_SplitStringToUnorderedSet(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string test(len, 'x');
-  for (int i = 1; i < len; i += 2) {
-    test[i] = ';';
-  }
-  for (auto _ : state) {
-    std::unordered_set<std::string> result =
-        absl::StrSplit(test, ':', absl::SkipEmpty());
-    benchmark::DoNotOptimize(result);
-  }
-}
-BENCHMARK_RANGE(BM_SplitStringToUnorderedSet, 0, 1 << 20);
-
-void BM_SplitStringToUnorderedMap(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string test(len, 'x');
-  for (int i = 1; i < len; i += 2) {
-    test[i] = ';';
-  }
-  for (auto _ : state) {
-    std::unordered_map<std::string, std::string> result =
-        absl::StrSplit(test, ':', absl::SkipEmpty());
-    benchmark::DoNotOptimize(result);
-  }
-}
-BENCHMARK_RANGE(BM_SplitStringToUnorderedMap, 0, 1 << 20);
-
-void BM_SplitStringAllowEmpty(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string test(len, 'x');
-  for (int i = 1; i < len; i += 2) {
-    test[i] = ';';
-  }
-  for (auto _ : state) {
-    std::vector<std::string> result = absl::StrSplit(test, ';');
-    benchmark::DoNotOptimize(result);
-  }
-}
-BENCHMARK_RANGE(BM_SplitStringAllowEmpty, 0, 1 << 20);
-
-struct OneCharLiteral {
-  char operator()() const { return 'X'; }
-};
-
-struct OneCharStringLiteral {
-  const char* operator()() const { return "X"; }
-};
-
-template <typename DelimiterFactory>
-void BM_SplitStringWithOneChar(benchmark::State& state) {
-  const auto delimiter = DelimiterFactory()();
-  std::vector<absl::string_view> pieces;
-  size_t v = 0;
-  for (auto _ : state) {
-    pieces = absl::StrSplit("The quick brown fox jumps over the lazy dog",
-                            delimiter);
-    v += pieces.size();
-  }
-  ABSL_RAW_CHECK(v == state.iterations(), "");
-}
-BENCHMARK_TEMPLATE(BM_SplitStringWithOneChar, OneCharLiteral);
-BENCHMARK_TEMPLATE(BM_SplitStringWithOneChar, OneCharStringLiteral);
-
-template <typename DelimiterFactory>
-void BM_SplitStringWithOneCharNoVector(benchmark::State& state) {
-  const auto delimiter = DelimiterFactory()();
-  size_t v = 0;
-  for (auto _ : state) {
-    auto splitter = absl::StrSplit(
-        "The quick brown fox jumps over the lazy dog", delimiter);
-    v += std::distance(splitter.begin(), splitter.end());
-  }
-  ABSL_RAW_CHECK(v == state.iterations(), "");
-}
-BENCHMARK_TEMPLATE(BM_SplitStringWithOneCharNoVector, OneCharLiteral);
-BENCHMARK_TEMPLATE(BM_SplitStringWithOneCharNoVector, OneCharStringLiteral);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/str_split_test.cc b/third_party/abseil_cpp/absl/strings/str_split_test.cc
deleted file mode 100644
index 7f7c097fae..0000000000
--- a/third_party/abseil_cpp/absl/strings/str_split_test.cc
+++ /dev/null
@@ -1,953 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/str_split.h"
-
-#include <deque>
-#include <initializer_list>
-#include <list>
-#include <map>
-#include <memory>
-#include <string>
-#include <type_traits>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/dynamic_annotations.h"
-#include "absl/base/macros.h"
-#include "absl/container/flat_hash_map.h"
-#include "absl/container/node_hash_map.h"
-#include "absl/strings/numbers.h"
-
-namespace {
-
-using ::testing::ElementsAre;
-using ::testing::Pair;
-using ::testing::UnorderedElementsAre;
-
-TEST(Split, TraitsTest) {
-  static_assert(!absl::strings_internal::SplitterIsConvertibleTo<int>::value,
-                "");
-  static_assert(
-      !absl::strings_internal::SplitterIsConvertibleTo<std::string>::value, "");
-  static_assert(absl::strings_internal::SplitterIsConvertibleTo<
-                    std::vector<std::string>>::value,
-                "");
-  static_assert(
-      !absl::strings_internal::SplitterIsConvertibleTo<std::vector<int>>::value,
-      "");
-  static_assert(absl::strings_internal::SplitterIsConvertibleTo<
-                    std::vector<absl::string_view>>::value,
-                "");
-  static_assert(absl::strings_internal::SplitterIsConvertibleTo<
-                    std::map<std::string, std::string>>::value,
-                "");
-  static_assert(absl::strings_internal::SplitterIsConvertibleTo<
-                    std::map<absl::string_view, absl::string_view>>::value,
-                "");
-  static_assert(!absl::strings_internal::SplitterIsConvertibleTo<
-                    std::map<int, std::string>>::value,
-                "");
-  static_assert(!absl::strings_internal::SplitterIsConvertibleTo<
-                    std::map<std::string, int>>::value,
-                "");
-}
-
-// This tests the overall split API, which is made up of the absl::StrSplit()
-// function and the Delimiter objects in the absl:: namespace.
-// This TEST macro is outside of any namespace to require full specification of
-// namespaces just like callers will need to use.
-TEST(Split, APIExamples) {
-  {
-    // Passes string delimiter. Assumes the default of ByString.
-    std::vector<std::string> v = absl::StrSplit("a,b,c", ",");  // NOLINT
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-
-    // Equivalent to...
-    using absl::ByString;
-    v = absl::StrSplit("a,b,c", ByString(","));
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-
-    // Equivalent to...
-    EXPECT_THAT(absl::StrSplit("a,b,c", ByString(",")),
-                ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Same as above, but using a single character as the delimiter.
-    std::vector<std::string> v = absl::StrSplit("a,b,c", ',');
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-
-    // Equivalent to...
-    using absl::ByChar;
-    v = absl::StrSplit("a,b,c", ByChar(','));
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Uses the Literal string "=>" as the delimiter.
-    const std::vector<std::string> v = absl::StrSplit("a=>b=>c", "=>");
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // The substrings are returned as string_views, eliminating copying.
-    std::vector<absl::string_view> v = absl::StrSplit("a,b,c", ',');
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Leading and trailing empty substrings.
-    std::vector<std::string> v = absl::StrSplit(",a,b,c,", ',');
-    EXPECT_THAT(v, ElementsAre("", "a", "b", "c", ""));
-  }
-
-  {
-    // Splits on a delimiter that is not found.
-    std::vector<std::string> v = absl::StrSplit("abc", ',');
-    EXPECT_THAT(v, ElementsAre("abc"));
-  }
-
-  {
-    // Splits the input string into individual characters by using an empty
-    // string as the delimiter.
-    std::vector<std::string> v = absl::StrSplit("abc", "");
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Splits string data with embedded NUL characters, using NUL as the
-    // delimiter. A simple delimiter of "\0" doesn't work because strlen() will
-    // say that's the empty string when constructing the absl::string_view
-    // delimiter. Instead, a non-empty string containing NUL can be used as the
-    // delimiter.
-    std::string embedded_nulls("a\0b\0c", 5);
-    std::string null_delim("\0", 1);
-    std::vector<std::string> v = absl::StrSplit(embedded_nulls, null_delim);
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Stores first two split strings as the members in a std::pair.
-    std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ',');
-    EXPECT_EQ("a", p.first);
-    EXPECT_EQ("b", p.second);
-    // "c" is omitted because std::pair can hold only two elements.
-  }
-
-  {
-    // Results stored in std::set<std::string>
-    std::set<std::string> v = absl::StrSplit("a,b,c,a,b,c,a,b,c", ',');
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Uses a non-const char* delimiter.
-    char a[] = ",";
-    char* d = a + 0;
-    std::vector<std::string> v = absl::StrSplit("a,b,c", d);
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Results split using either of , or ;
-    using absl::ByAnyChar;
-    std::vector<std::string> v = absl::StrSplit("a,b;c", ByAnyChar(",;"));
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Uses the SkipWhitespace predicate.
-    using absl::SkipWhitespace;
-    std::vector<std::string> v =
-        absl::StrSplit(" a , ,,b,", ',', SkipWhitespace());
-    EXPECT_THAT(v, ElementsAre(" a ", "b"));
-  }
-
-  {
-    // Uses the ByLength delimiter.
-    using absl::ByLength;
-    std::vector<std::string> v = absl::StrSplit("abcdefg", ByLength(3));
-    EXPECT_THAT(v, ElementsAre("abc", "def", "g"));
-  }
-
-  {
-    // Different forms of initialization / conversion.
-    std::vector<std::string> v1 = absl::StrSplit("a,b,c", ',');
-    EXPECT_THAT(v1, ElementsAre("a", "b", "c"));
-    std::vector<std::string> v2(absl::StrSplit("a,b,c", ','));
-    EXPECT_THAT(v2, ElementsAre("a", "b", "c"));
-    auto v3 = std::vector<std::string>(absl::StrSplit("a,b,c", ','));
-    EXPECT_THAT(v3, ElementsAre("a", "b", "c"));
-    v3 = absl::StrSplit("a,b,c", ',');
-    EXPECT_THAT(v3, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Results stored in a std::map.
-    std::map<std::string, std::string> m = absl::StrSplit("a,1,b,2,a,3", ',');
-    EXPECT_EQ(2, m.size());
-    EXPECT_EQ("3", m["a"]);
-    EXPECT_EQ("2", m["b"]);
-  }
-
-  {
-    // Results stored in a std::multimap.
-    std::multimap<std::string, std::string> m =
-        absl::StrSplit("a,1,b,2,a,3", ',');
-    EXPECT_EQ(3, m.size());
-    auto it = m.find("a");
-    EXPECT_EQ("1", it->second);
-    ++it;
-    EXPECT_EQ("3", it->second);
-    it = m.find("b");
-    EXPECT_EQ("2", it->second);
-  }
-
-  {
-    // Demonstrates use in a range-based for loop in C++11.
-    std::string s = "x,x,x,x,x,x,x";
-    for (absl::string_view sp : absl::StrSplit(s, ',')) {
-      EXPECT_EQ("x", sp);
-    }
-  }
-
-  {
-    // Demonstrates use with a Predicate in a range-based for loop.
-    using absl::SkipWhitespace;
-    std::string s = " ,x,,x,,x,x,x,,";
-    for (absl::string_view sp : absl::StrSplit(s, ',', SkipWhitespace())) {
-      EXPECT_EQ("x", sp);
-    }
-  }
-
-  {
-    // Demonstrates a "smart" split to std::map using two separate calls to
-    // absl::StrSplit. One call to split the records, and another call to split
-    // the keys and values. This also uses the Limit delimiter so that the
-    // std::string "a=b=c" will split to "a" -> "b=c".
-    std::map<std::string, std::string> m;
-    for (absl::string_view sp : absl::StrSplit("a=b=c,d=e,f=,g", ',')) {
-      m.insert(absl::StrSplit(sp, absl::MaxSplits('=', 1)));
-    }
-    EXPECT_EQ("b=c", m.find("a")->second);
-    EXPECT_EQ("e", m.find("d")->second);
-    EXPECT_EQ("", m.find("f")->second);
-    EXPECT_EQ("", m.find("g")->second);
-  }
-}
-
-//
-// Tests for SplitIterator
-//
-
-TEST(SplitIterator, Basics) {
-  auto splitter = absl::StrSplit("a,b", ',');
-  auto it = splitter.begin();
-  auto end = splitter.end();
-
-  EXPECT_NE(it, end);
-  EXPECT_EQ("a", *it);  // tests dereference
-  ++it;                 // tests preincrement
-  EXPECT_NE(it, end);
-  EXPECT_EQ("b",
-            std::string(it->data(), it->size()));  // tests dereference as ptr
-  it++;                                            // tests postincrement
-  EXPECT_EQ(it, end);
-}
-
-// Simple Predicate to skip a particular string.
-class Skip {
- public:
-  explicit Skip(const std::string& s) : s_(s) {}
-  bool operator()(absl::string_view sp) { return sp != s_; }
-
- private:
-  std::string s_;
-};
-
-TEST(SplitIterator, Predicate) {
-  auto splitter = absl::StrSplit("a,b,c", ',', Skip("b"));
-  auto it = splitter.begin();
-  auto end = splitter.end();
-
-  EXPECT_NE(it, end);
-  EXPECT_EQ("a", *it);  // tests dereference
-  ++it;                 // tests preincrement -- "b" should be skipped here.
-  EXPECT_NE(it, end);
-  EXPECT_EQ("c",
-            std::string(it->data(), it->size()));  // tests dereference as ptr
-  it++;                                            // tests postincrement
-  EXPECT_EQ(it, end);
-}
-
-TEST(SplitIterator, EdgeCases) {
-  // Expected input and output, assuming a delimiter of ','
-  struct {
-    std::string in;
-    std::vector<std::string> expect;
-  } specs[] = {
-      {"", {""}},
-      {"foo", {"foo"}},
-      {",", {"", ""}},
-      {",foo", {"", "foo"}},
-      {"foo,", {"foo", ""}},
-      {",foo,", {"", "foo", ""}},
-      {"foo,bar", {"foo", "bar"}},
-  };
-
-  for (const auto& spec : specs) {
-    SCOPED_TRACE(spec.in);
-    auto splitter = absl::StrSplit(spec.in, ',');
-    auto it = splitter.begin();
-    auto end = splitter.end();
-    for (const auto& expected : spec.expect) {
-      EXPECT_NE(it, end);
-      EXPECT_EQ(expected, *it++);
-    }
-    EXPECT_EQ(it, end);
-  }
-}
-
-TEST(Splitter, Const) {
-  const auto splitter = absl::StrSplit("a,b,c", ',');
-  EXPECT_THAT(splitter, ElementsAre("a", "b", "c"));
-}
-
-TEST(Split, EmptyAndNull) {
-  // Attention: Splitting a null absl::string_view is different than splitting
-  // an empty absl::string_view even though both string_views are considered
-  // equal. This behavior is likely surprising and undesirable. However, to
-  // maintain backward compatibility, there is a small "hack" in
-  // str_split_internal.h that preserves this behavior. If that behavior is ever
-  // changed/fixed, this test will need to be updated.
-  EXPECT_THAT(absl::StrSplit(absl::string_view(""), '-'), ElementsAre(""));
-  EXPECT_THAT(absl::StrSplit(absl::string_view(), '-'), ElementsAre());
-}
-
-TEST(SplitIterator, EqualityAsEndCondition) {
-  auto splitter = absl::StrSplit("a,b,c", ',');
-  auto it = splitter.begin();
-  auto it2 = it;
-
-  // Increments it2 twice to point to "c" in the input text.
-  ++it2;
-  ++it2;
-  EXPECT_EQ("c", *it2);
-
-  // This test uses a non-end SplitIterator as the terminating condition in a
-  // for loop. This relies on SplitIterator equality for non-end SplitIterators
-  // working correctly. At this point it2 points to "c", and we use that as the
-  // "end" condition in this test.
-  std::vector<absl::string_view> v;
-  for (; it != it2; ++it) {
-    v.push_back(*it);
-  }
-  EXPECT_THAT(v, ElementsAre("a", "b"));
-}
-
-//
-// Tests for Splitter
-//
-
-TEST(Splitter, RangeIterators) {
-  auto splitter = absl::StrSplit("a,b,c", ',');
-  std::vector<absl::string_view> output;
-  for (const absl::string_view& p : splitter) {
-    output.push_back(p);
-  }
-  EXPECT_THAT(output, ElementsAre("a", "b", "c"));
-}
-
-// Some template functions for use in testing conversion operators
-template <typename ContainerType, typename Splitter>
-void TestConversionOperator(const Splitter& splitter) {
-  ContainerType output = splitter;
-  EXPECT_THAT(output, UnorderedElementsAre("a", "b", "c", "d"));
-}
-
-template <typename MapType, typename Splitter>
-void TestMapConversionOperator(const Splitter& splitter) {
-  MapType m = splitter;
-  EXPECT_THAT(m, UnorderedElementsAre(Pair("a", "b"), Pair("c", "d")));
-}
-
-template <typename FirstType, typename SecondType, typename Splitter>
-void TestPairConversionOperator(const Splitter& splitter) {
-  std::pair<FirstType, SecondType> p = splitter;
-  EXPECT_EQ(p, (std::pair<FirstType, SecondType>("a", "b")));
-}
-
-TEST(Splitter, ConversionOperator) {
-  auto splitter = absl::StrSplit("a,b,c,d", ',');
-
-  TestConversionOperator<std::vector<absl::string_view>>(splitter);
-  TestConversionOperator<std::vector<std::string>>(splitter);
-  TestConversionOperator<std::list<absl::string_view>>(splitter);
-  TestConversionOperator<std::list<std::string>>(splitter);
-  TestConversionOperator<std::deque<absl::string_view>>(splitter);
-  TestConversionOperator<std::deque<std::string>>(splitter);
-  TestConversionOperator<std::set<absl::string_view>>(splitter);
-  TestConversionOperator<std::set<std::string>>(splitter);
-  TestConversionOperator<std::multiset<absl::string_view>>(splitter);
-  TestConversionOperator<std::multiset<std::string>>(splitter);
-  TestConversionOperator<std::unordered_set<std::string>>(splitter);
-
-  // Tests conversion to map-like objects.
-
-  TestMapConversionOperator<std::map<absl::string_view, absl::string_view>>(
-      splitter);
-  TestMapConversionOperator<std::map<absl::string_view, std::string>>(splitter);
-  TestMapConversionOperator<std::map<std::string, absl::string_view>>(splitter);
-  TestMapConversionOperator<std::map<std::string, std::string>>(splitter);
-  TestMapConversionOperator<
-      std::multimap<absl::string_view, absl::string_view>>(splitter);
-  TestMapConversionOperator<std::multimap<absl::string_view, std::string>>(
-      splitter);
-  TestMapConversionOperator<std::multimap<std::string, absl::string_view>>(
-      splitter);
-  TestMapConversionOperator<std::multimap<std::string, std::string>>(splitter);
-  TestMapConversionOperator<std::unordered_map<std::string, std::string>>(
-      splitter);
-  TestMapConversionOperator<
-      absl::node_hash_map<absl::string_view, absl::string_view>>(splitter);
-  TestMapConversionOperator<
-      absl::node_hash_map<absl::string_view, std::string>>(splitter);
-  TestMapConversionOperator<
-      absl::node_hash_map<std::string, absl::string_view>>(splitter);
-  TestMapConversionOperator<
-      absl::flat_hash_map<absl::string_view, absl::string_view>>(splitter);
-  TestMapConversionOperator<
-      absl::flat_hash_map<absl::string_view, std::string>>(splitter);
-  TestMapConversionOperator<
-      absl::flat_hash_map<std::string, absl::string_view>>(splitter);
-
-  // Tests conversion to std::pair
-
-  TestPairConversionOperator<absl::string_view, absl::string_view>(splitter);
-  TestPairConversionOperator<absl::string_view, std::string>(splitter);
-  TestPairConversionOperator<std::string, absl::string_view>(splitter);
-  TestPairConversionOperator<std::string, std::string>(splitter);
-}
-
-// A few additional tests for conversion to std::pair. This conversion is
-// different from others because a std::pair always has exactly two elements:
-// .first and .second. The split has to work even when the split has
-// less-than, equal-to, and more-than 2 strings.
-TEST(Splitter, ToPair) {
-  {
-    // Empty string
-    std::pair<std::string, std::string> p = absl::StrSplit("", ',');
-    EXPECT_EQ("", p.first);
-    EXPECT_EQ("", p.second);
-  }
-
-  {
-    // Only first
-    std::pair<std::string, std::string> p = absl::StrSplit("a", ',');
-    EXPECT_EQ("a", p.first);
-    EXPECT_EQ("", p.second);
-  }
-
-  {
-    // Only second
-    std::pair<std::string, std::string> p = absl::StrSplit(",b", ',');
-    EXPECT_EQ("", p.first);
-    EXPECT_EQ("b", p.second);
-  }
-
-  {
-    // First and second.
-    std::pair<std::string, std::string> p = absl::StrSplit("a,b", ',');
-    EXPECT_EQ("a", p.first);
-    EXPECT_EQ("b", p.second);
-  }
-
-  {
-    // First and second and then more stuff that will be ignored.
-    std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ',');
-    EXPECT_EQ("a", p.first);
-    EXPECT_EQ("b", p.second);
-    // "c" is omitted.
-  }
-}
-
-TEST(Splitter, Predicates) {
-  static const char kTestChars[] = ",a, ,b,";
-  using absl::AllowEmpty;
-  using absl::SkipEmpty;
-  using absl::SkipWhitespace;
-
-  {
-    // No predicate. Does not skip empties.
-    auto splitter = absl::StrSplit(kTestChars, ',');
-    std::vector<std::string> v = splitter;
-    EXPECT_THAT(v, ElementsAre("", "a", " ", "b", ""));
-  }
-
-  {
-    // Allows empty strings. Same behavior as no predicate at all.
-    auto splitter = absl::StrSplit(kTestChars, ',', AllowEmpty());
-    std::vector<std::string> v_allowempty = splitter;
-    EXPECT_THAT(v_allowempty, ElementsAre("", "a", " ", "b", ""));
-
-    // Ensures AllowEmpty equals the behavior with no predicate.
-    auto splitter_nopredicate = absl::StrSplit(kTestChars, ',');
-    std::vector<std::string> v_nopredicate = splitter_nopredicate;
-    EXPECT_EQ(v_allowempty, v_nopredicate);
-  }
-
-  {
-    // Skips empty strings.
-    auto splitter = absl::StrSplit(kTestChars, ',', SkipEmpty());
-    std::vector<std::string> v = splitter;
-    EXPECT_THAT(v, ElementsAre("a", " ", "b"));
-  }
-
-  {
-    // Skips empty and all-whitespace strings.
-    auto splitter = absl::StrSplit(kTestChars, ',', SkipWhitespace());
-    std::vector<std::string> v = splitter;
-    EXPECT_THAT(v, ElementsAre("a", "b"));
-  }
-}
-
-//
-// Tests for StrSplit()
-//
-
-TEST(Split, Basics) {
-  {
-    // Doesn't really do anything useful because the return value is ignored,
-    // but it should work.
-    absl::StrSplit("a,b,c", ',');
-  }
-
-  {
-    std::vector<absl::string_view> v = absl::StrSplit("a,b,c", ',');
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    std::vector<std::string> v = absl::StrSplit("a,b,c", ',');
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-  }
-
-  {
-    // Ensures that assignment works. This requires a little extra work with
-    // C++11 because of overloads with initializer_list.
-    std::vector<std::string> v;
-    v = absl::StrSplit("a,b,c", ',');
-
-    EXPECT_THAT(v, ElementsAre("a", "b", "c"));
-    std::map<std::string, std::string> m;
-    m = absl::StrSplit("a,b,c", ',');
-    EXPECT_EQ(2, m.size());
-    std::unordered_map<std::string, std::string> hm;
-    hm = absl::StrSplit("a,b,c", ',');
-    EXPECT_EQ(2, hm.size());
-  }
-}
-
-absl::string_view ReturnStringView() { return "Hello World"; }
-const char* ReturnConstCharP() { return "Hello World"; }
-char* ReturnCharP() { return const_cast<char*>("Hello World"); }
-
-TEST(Split, AcceptsCertainTemporaries) {
-  std::vector<std::string> v;
-  v = absl::StrSplit(ReturnStringView(), ' ');
-  EXPECT_THAT(v, ElementsAre("Hello", "World"));
-  v = absl::StrSplit(ReturnConstCharP(), ' ');
-  EXPECT_THAT(v, ElementsAre("Hello", "World"));
-  v = absl::StrSplit(ReturnCharP(), ' ');
-  EXPECT_THAT(v, ElementsAre("Hello", "World"));
-}
-
-TEST(Split, Temporary) {
-  // Use a std::string longer than the SSO length, so that when the temporary is
-  // destroyed, if the splitter keeps a reference to the string's contents,
-  // it'll reference freed memory instead of just dead on-stack memory.
-  const char input[] = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u";
-  EXPECT_LT(sizeof(std::string), ABSL_ARRAYSIZE(input))
-      << "Input should be larger than fits on the stack.";
-
-  // This happens more often in C++11 as part of a range-based for loop.
-  auto splitter = absl::StrSplit(std::string(input), ',');
-  std::string expected = "a";
-  for (absl::string_view letter : splitter) {
-    EXPECT_EQ(expected, letter);
-    ++expected[0];
-  }
-  EXPECT_EQ("v", expected);
-
-  // This happens more often in C++11 as part of a range-based for loop.
-  auto std_splitter = absl::StrSplit(std::string(input), ',');
-  expected = "a";
-  for (absl::string_view letter : std_splitter) {
-    EXPECT_EQ(expected, letter);
-    ++expected[0];
-  }
-  EXPECT_EQ("v", expected);
-}
-
-template <typename T>
-static std::unique_ptr<T> CopyToHeap(const T& value) {
-  return std::unique_ptr<T>(new T(value));
-}
-
-TEST(Split, LvalueCaptureIsCopyable) {
-  std::string input = "a,b";
-  auto heap_splitter = CopyToHeap(absl::StrSplit(input, ','));
-  auto stack_splitter = *heap_splitter;
-  heap_splitter.reset();
-  std::vector<std::string> result = stack_splitter;
-  EXPECT_THAT(result, testing::ElementsAre("a", "b"));
-}
-
-TEST(Split, TemporaryCaptureIsCopyable) {
-  auto heap_splitter = CopyToHeap(absl::StrSplit(std::string("a,b"), ','));
-  auto stack_splitter = *heap_splitter;
-  heap_splitter.reset();
-  std::vector<std::string> result = stack_splitter;
-  EXPECT_THAT(result, testing::ElementsAre("a", "b"));
-}
-
-TEST(Split, SplitterIsCopyableAndMoveable) {
-  auto a = absl::StrSplit("foo", '-');
-
-  // Ensures that the following expressions compile.
-  auto b = a;             // Copy construct
-  auto c = std::move(a);  // Move construct
-  b = c;                  // Copy assign
-  c = std::move(b);       // Move assign
-
-  EXPECT_THAT(c, ElementsAre("foo"));
-}
-
-TEST(Split, StringDelimiter) {
-  {
-    std::vector<absl::string_view> v = absl::StrSplit("a,b", ',');
-    EXPECT_THAT(v, ElementsAre("a", "b"));
-  }
-
-  {
-    std::vector<absl::string_view> v = absl::StrSplit("a,b", std::string(","));
-    EXPECT_THAT(v, ElementsAre("a", "b"));
-  }
-
-  {
-    std::vector<absl::string_view> v =
-        absl::StrSplit("a,b", absl::string_view(","));
-    EXPECT_THAT(v, ElementsAre("a", "b"));
-  }
-}
-
-#if !defined(__cpp_char8_t)
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wc++2a-compat"
-#endif
-TEST(Split, UTF8) {
-  // Tests splitting utf8 strings and utf8 delimiters.
-  std::string utf8_string = u8"\u03BA\u1F79\u03C3\u03BC\u03B5";
-  {
-    // A utf8 input string with an ascii delimiter.
-    std::string to_split = "a," + utf8_string;
-    std::vector<absl::string_view> v = absl::StrSplit(to_split, ',');
-    EXPECT_THAT(v, ElementsAre("a", utf8_string));
-  }
-
-  {
-    // A utf8 input string and a utf8 delimiter.
-    std::string to_split = "a," + utf8_string + ",b";
-    std::string unicode_delimiter = "," + utf8_string + ",";
-    std::vector<absl::string_view> v =
-        absl::StrSplit(to_split, unicode_delimiter);
-    EXPECT_THAT(v, ElementsAre("a", "b"));
-  }
-
-  {
-    // A utf8 input string and ByAnyChar with ascii chars.
-    std::vector<absl::string_view> v =
-        absl::StrSplit(u8"Foo h\u00E4llo th\u4E1Ere", absl::ByAnyChar(" \t"));
-    EXPECT_THAT(v, ElementsAre("Foo", u8"h\u00E4llo", u8"th\u4E1Ere"));
-  }
-}
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-#endif  // !defined(__cpp_char8_t)
-
-TEST(Split, EmptyStringDelimiter) {
-  {
-    std::vector<std::string> v = absl::StrSplit("", "");
-    EXPECT_THAT(v, ElementsAre(""));
-  }
-
-  {
-    std::vector<std::string> v = absl::StrSplit("a", "");
-    EXPECT_THAT(v, ElementsAre("a"));
-  }
-
-  {
-    std::vector<std::string> v = absl::StrSplit("ab", "");
-    EXPECT_THAT(v, ElementsAre("a", "b"));
-  }
-
-  {
-    std::vector<std::string> v = absl::StrSplit("a b", "");
-    EXPECT_THAT(v, ElementsAre("a", " ", "b"));
-  }
-}
-
-TEST(Split, SubstrDelimiter) {
-  std::vector<absl::string_view> results;
-  absl::string_view delim("//");
-
-  results = absl::StrSplit("", delim);
-  EXPECT_THAT(results, ElementsAre(""));
-
-  results = absl::StrSplit("//", delim);
-  EXPECT_THAT(results, ElementsAre("", ""));
-
-  results = absl::StrSplit("ab", delim);
-  EXPECT_THAT(results, ElementsAre("ab"));
-
-  results = absl::StrSplit("ab//", delim);
-  EXPECT_THAT(results, ElementsAre("ab", ""));
-
-  results = absl::StrSplit("ab/", delim);
-  EXPECT_THAT(results, ElementsAre("ab/"));
-
-  results = absl::StrSplit("a/b", delim);
-  EXPECT_THAT(results, ElementsAre("a/b"));
-
-  results = absl::StrSplit("a//b", delim);
-  EXPECT_THAT(results, ElementsAre("a", "b"));
-
-  results = absl::StrSplit("a///b", delim);
-  EXPECT_THAT(results, ElementsAre("a", "/b"));
-
-  results = absl::StrSplit("a////b", delim);
-  EXPECT_THAT(results, ElementsAre("a", "", "b"));
-}
-
-TEST(Split, EmptyResults) {
-  std::vector<absl::string_view> results;
-
-  results = absl::StrSplit("", '#');
-  EXPECT_THAT(results, ElementsAre(""));
-
-  results = absl::StrSplit("#", '#');
-  EXPECT_THAT(results, ElementsAre("", ""));
-
-  results = absl::StrSplit("#cd", '#');
-  EXPECT_THAT(results, ElementsAre("", "cd"));
-
-  results = absl::StrSplit("ab#cd#", '#');
-  EXPECT_THAT(results, ElementsAre("ab", "cd", ""));
-
-  results = absl::StrSplit("ab##cd", '#');
-  EXPECT_THAT(results, ElementsAre("ab", "", "cd"));
-
-  results = absl::StrSplit("ab##", '#');
-  EXPECT_THAT(results, ElementsAre("ab", "", ""));
-
-  results = absl::StrSplit("ab#ab#", '#');
-  EXPECT_THAT(results, ElementsAre("ab", "ab", ""));
-
-  results = absl::StrSplit("aaaa", 'a');
-  EXPECT_THAT(results, ElementsAre("", "", "", "", ""));
-
-  results = absl::StrSplit("", '#', absl::SkipEmpty());
-  EXPECT_THAT(results, ElementsAre());
-}
-
-template <typename Delimiter>
-static bool IsFoundAtStartingPos(absl::string_view text, Delimiter d,
-                                 size_t starting_pos, int expected_pos) {
-  absl::string_view found = d.Find(text, starting_pos);
-  return found.data() != text.data() + text.size() &&
-         expected_pos == found.data() - text.data();
-}
-
-// Helper function for testing Delimiter objects. Returns true if the given
-// Delimiter is found in the given string at the given position. This function
-// tests two cases:
-//   1. The actual text given, staring at position 0
-//   2. The text given with leading padding that should be ignored
-template <typename Delimiter>
-static bool IsFoundAt(absl::string_view text, Delimiter d, int expected_pos) {
-  const std::string leading_text = ",x,y,z,";
-  return IsFoundAtStartingPos(text, d, 0, expected_pos) &&
-         IsFoundAtStartingPos(leading_text + std::string(text), d,
-                              leading_text.length(),
-                              expected_pos + leading_text.length());
-}
-
-//
-// Tests for ByString
-//
-
-// Tests using any delimiter that represents a single comma.
-template <typename Delimiter>
-void TestComma(Delimiter d) {
-  EXPECT_TRUE(IsFoundAt(",", d, 0));
-  EXPECT_TRUE(IsFoundAt("a,", d, 1));
-  EXPECT_TRUE(IsFoundAt(",b", d, 0));
-  EXPECT_TRUE(IsFoundAt("a,b", d, 1));
-  EXPECT_TRUE(IsFoundAt("a,b,", d, 1));
-  EXPECT_TRUE(IsFoundAt("a,b,c", d, 1));
-  EXPECT_FALSE(IsFoundAt("", d, -1));
-  EXPECT_FALSE(IsFoundAt(" ", d, -1));
-  EXPECT_FALSE(IsFoundAt("a", d, -1));
-  EXPECT_FALSE(IsFoundAt("a b c", d, -1));
-  EXPECT_FALSE(IsFoundAt("a;b;c", d, -1));
-  EXPECT_FALSE(IsFoundAt(";", d, -1));
-}
-
-TEST(Delimiter, ByString) {
-  using absl::ByString;
-  TestComma(ByString(","));
-
-  // Works as named variable.
-  ByString comma_string(",");
-  TestComma(comma_string);
-
-  // The first occurrence of empty string ("") in a string is at position 0.
-  // There is a test below that demonstrates this for absl::string_view::find().
-  // If the ByString delimiter returned position 0 for this, there would
-  // be an infinite loop in the SplitIterator code. To avoid this, empty string
-  // is a special case in that it always returns the item at position 1.
-  absl::string_view abc("abc");
-  EXPECT_EQ(0, abc.find(""));  // "" is found at position 0
-  ByString empty("");
-  EXPECT_FALSE(IsFoundAt("", empty, 0));
-  EXPECT_FALSE(IsFoundAt("a", empty, 0));
-  EXPECT_TRUE(IsFoundAt("ab", empty, 1));
-  EXPECT_TRUE(IsFoundAt("abc", empty, 1));
-}
-
-TEST(Split, ByChar) {
-  using absl::ByChar;
-  TestComma(ByChar(','));
-
-  // Works as named variable.
-  ByChar comma_char(',');
-  TestComma(comma_char);
-}
-
-//
-// Tests for ByAnyChar
-//
-
-TEST(Delimiter, ByAnyChar) {
-  using absl::ByAnyChar;
-  ByAnyChar one_delim(",");
-  // Found
-  EXPECT_TRUE(IsFoundAt(",", one_delim, 0));
-  EXPECT_TRUE(IsFoundAt("a,", one_delim, 1));
-  EXPECT_TRUE(IsFoundAt("a,b", one_delim, 1));
-  EXPECT_TRUE(IsFoundAt(",b", one_delim, 0));
-  // Not found
-  EXPECT_FALSE(IsFoundAt("", one_delim, -1));
-  EXPECT_FALSE(IsFoundAt(" ", one_delim, -1));
-  EXPECT_FALSE(IsFoundAt("a", one_delim, -1));
-  EXPECT_FALSE(IsFoundAt("a;b;c", one_delim, -1));
-  EXPECT_FALSE(IsFoundAt(";", one_delim, -1));
-
-  ByAnyChar two_delims(",;");
-  // Found
-  EXPECT_TRUE(IsFoundAt(",", two_delims, 0));
-  EXPECT_TRUE(IsFoundAt(";", two_delims, 0));
-  EXPECT_TRUE(IsFoundAt(",;", two_delims, 0));
-  EXPECT_TRUE(IsFoundAt(";,", two_delims, 0));
-  EXPECT_TRUE(IsFoundAt(",;b", two_delims, 0));
-  EXPECT_TRUE(IsFoundAt(";,b", two_delims, 0));
-  EXPECT_TRUE(IsFoundAt("a;,", two_delims, 1));
-  EXPECT_TRUE(IsFoundAt("a,;", two_delims, 1));
-  EXPECT_TRUE(IsFoundAt("a;,b", two_delims, 1));
-  EXPECT_TRUE(IsFoundAt("a,;b", two_delims, 1));
-  // Not found
-  EXPECT_FALSE(IsFoundAt("", two_delims, -1));
-  EXPECT_FALSE(IsFoundAt(" ", two_delims, -1));
-  EXPECT_FALSE(IsFoundAt("a", two_delims, -1));
-  EXPECT_FALSE(IsFoundAt("a=b=c", two_delims, -1));
-  EXPECT_FALSE(IsFoundAt("=", two_delims, -1));
-
-  // ByAnyChar behaves just like ByString when given a delimiter of empty
-  // string. That is, it always returns a zero-length absl::string_view
-  // referring to the item at position 1, not position 0.
-  ByAnyChar empty("");
-  EXPECT_FALSE(IsFoundAt("", empty, 0));
-  EXPECT_FALSE(IsFoundAt("a", empty, 0));
-  EXPECT_TRUE(IsFoundAt("ab", empty, 1));
-  EXPECT_TRUE(IsFoundAt("abc", empty, 1));
-}
-
-//
-// Tests for ByLength
-//
-
-TEST(Delimiter, ByLength) {
-  using absl::ByLength;
-
-  ByLength four_char_delim(4);
-
-  // Found
-  EXPECT_TRUE(IsFoundAt("abcde", four_char_delim, 4));
-  EXPECT_TRUE(IsFoundAt("abcdefghijklmnopqrstuvwxyz", four_char_delim, 4));
-  EXPECT_TRUE(IsFoundAt("a b,c\nd", four_char_delim, 4));
-  // Not found
-  EXPECT_FALSE(IsFoundAt("", four_char_delim, 0));
-  EXPECT_FALSE(IsFoundAt("a", four_char_delim, 0));
-  EXPECT_FALSE(IsFoundAt("ab", four_char_delim, 0));
-  EXPECT_FALSE(IsFoundAt("abc", four_char_delim, 0));
-  EXPECT_FALSE(IsFoundAt("abcd", four_char_delim, 0));
-}
-
-TEST(Split, WorksWithLargeStrings) {
-  if (sizeof(size_t) > 4) {
-    std::string s((uint32_t{1} << 31) + 1, 'x');  // 2G + 1 byte
-    s.back() = '-';
-    std::vector<absl::string_view> v = absl::StrSplit(s, '-');
-    EXPECT_EQ(2, v.size());
-    // The first element will contain 2G of 'x's.
-    // testing::StartsWith is too slow with a 2G string.
-    EXPECT_EQ('x', v[0][0]);
-    EXPECT_EQ('x', v[0][1]);
-    EXPECT_EQ('x', v[0][3]);
-    EXPECT_EQ("", v[1]);
-  }
-}
-
-TEST(SplitInternalTest, TypeTraits) {
-  EXPECT_FALSE(absl::strings_internal::HasMappedType<int>::value);
-  EXPECT_TRUE(
-      (absl::strings_internal::HasMappedType<std::map<int, int>>::value));
-  EXPECT_FALSE(absl::strings_internal::HasValueType<int>::value);
-  EXPECT_TRUE(
-      (absl::strings_internal::HasValueType<std::map<int, int>>::value));
-  EXPECT_FALSE(absl::strings_internal::HasConstIterator<int>::value);
-  EXPECT_TRUE(
-      (absl::strings_internal::HasConstIterator<std::map<int, int>>::value));
-  EXPECT_FALSE(absl::strings_internal::IsInitializerList<int>::value);
-  EXPECT_TRUE((absl::strings_internal::IsInitializerList<
-               std::initializer_list<int>>::value));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/string_view.cc b/third_party/abseil_cpp/absl/strings/string_view.cc
deleted file mode 100644
index c5f5de936d..0000000000
--- a/third_party/abseil_cpp/absl/strings/string_view.cc
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/string_view.h"
-
-#ifndef ABSL_USES_STD_STRING_VIEW
-
-#include <algorithm>
-#include <climits>
-#include <cstring>
-#include <ostream>
-
-#include "absl/strings/internal/memutil.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace {
-void WritePadding(std::ostream& o, size_t pad) {
-  char fill_buf[32];
-  memset(fill_buf, o.fill(), sizeof(fill_buf));
-  while (pad) {
-    size_t n = std::min(pad, sizeof(fill_buf));
-    o.write(fill_buf, n);
-    pad -= n;
-  }
-}
-
-class LookupTable {
- public:
-  // For each character in wanted, sets the index corresponding
-  // to the ASCII code of that character. This is used by
-  // the find_.*_of methods below to tell whether or not a character is in
-  // the lookup table in constant time.
-  explicit LookupTable(string_view wanted) {
-    for (char c : wanted) {
-      table_[Index(c)] = true;
-    }
-  }
-  bool operator[](char c) const { return table_[Index(c)]; }
-
- private:
-  static unsigned char Index(char c) { return static_cast<unsigned char>(c); }
-  bool table_[UCHAR_MAX + 1] = {};
-};
-
-}  // namespace
-
-std::ostream& operator<<(std::ostream& o, string_view piece) {
-  std::ostream::sentry sentry(o);
-  if (sentry) {
-    size_t lpad = 0;
-    size_t rpad = 0;
-    if (static_cast<size_t>(o.width()) > piece.size()) {
-      size_t pad = o.width() - piece.size();
-      if ((o.flags() & o.adjustfield) == o.left) {
-        rpad = pad;
-      } else {
-        lpad = pad;
-      }
-    }
-    if (lpad) WritePadding(o, lpad);
-    o.write(piece.data(), piece.size());
-    if (rpad) WritePadding(o, rpad);
-    o.width(0);
-  }
-  return o;
-}
-
-string_view::size_type string_view::find(string_view s, size_type pos) const
-    noexcept {
-  if (empty() || pos > length_) {
-    if (empty() && pos == 0 && s.empty()) return 0;
-    return npos;
-  }
-  const char* result =
-      strings_internal::memmatch(ptr_ + pos, length_ - pos, s.ptr_, s.length_);
-  return result ? result - ptr_ : npos;
-}
-
-string_view::size_type string_view::find(char c, size_type pos) const noexcept {
-  if (empty() || pos >= length_) {
-    return npos;
-  }
-  const char* result =
-      static_cast<const char*>(memchr(ptr_ + pos, c, length_ - pos));
-  return result != nullptr ? result - ptr_ : npos;
-}
-
-string_view::size_type string_view::rfind(string_view s, size_type pos) const
-    noexcept {
-  if (length_ < s.length_) return npos;
-  if (s.empty()) return std::min(length_, pos);
-  const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_;
-  const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
-  return result != last ? result - ptr_ : npos;
-}
-
-// Search range is [0..pos] inclusive.  If pos == npos, search everything.
-string_view::size_type string_view::rfind(char c, size_type pos) const
-    noexcept {
-  // Note: memrchr() is not available on Windows.
-  if (empty()) return npos;
-  for (size_type i = std::min(pos, length_ - 1);; --i) {
-    if (ptr_[i] == c) {
-      return i;
-    }
-    if (i == 0) break;
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_first_of(string_view s,
-                                                  size_type pos) const
-    noexcept {
-  if (empty() || s.empty()) {
-    return npos;
-  }
-  // Avoid the cost of LookupTable() for a single-character search.
-  if (s.length_ == 1) return find_first_of(s.ptr_[0], pos);
-  LookupTable tbl(s);
-  for (size_type i = pos; i < length_; ++i) {
-    if (tbl[ptr_[i]]) {
-      return i;
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_first_not_of(string_view s,
-                                                      size_type pos) const
-    noexcept {
-  if (empty()) return npos;
-  // Avoid the cost of LookupTable() for a single-character search.
-  if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos);
-  LookupTable tbl(s);
-  for (size_type i = pos; i < length_; ++i) {
-    if (!tbl[ptr_[i]]) {
-      return i;
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_first_not_of(char c,
-                                                      size_type pos) const
-    noexcept {
-  if (empty()) return npos;
-  for (; pos < length_; ++pos) {
-    if (ptr_[pos] != c) {
-      return pos;
-    }
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_last_of(string_view s,
-                                                 size_type pos) const noexcept {
-  if (empty() || s.empty()) return npos;
-  // Avoid the cost of LookupTable() for a single-character search.
-  if (s.length_ == 1) return find_last_of(s.ptr_[0], pos);
-  LookupTable tbl(s);
-  for (size_type i = std::min(pos, length_ - 1);; --i) {
-    if (tbl[ptr_[i]]) {
-      return i;
-    }
-    if (i == 0) break;
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_last_not_of(string_view s,
-                                                     size_type pos) const
-    noexcept {
-  if (empty()) return npos;
-  size_type i = std::min(pos, length_ - 1);
-  if (s.empty()) return i;
-  // Avoid the cost of LookupTable() for a single-character search.
-  if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos);
-  LookupTable tbl(s);
-  for (;; --i) {
-    if (!tbl[ptr_[i]]) {
-      return i;
-    }
-    if (i == 0) break;
-  }
-  return npos;
-}
-
-string_view::size_type string_view::find_last_not_of(char c,
-                                                     size_type pos) const
-    noexcept {
-  if (empty()) return npos;
-  size_type i = std::min(pos, length_ - 1);
-  for (;; --i) {
-    if (ptr_[i] != c) {
-      return i;
-    }
-    if (i == 0) break;
-  }
-  return npos;
-}
-
-// MSVC has non-standard behavior that implicitly creates definitions for static
-// const members. These implicit definitions conflict with explicit out-of-class
-// member definitions that are required by the C++ standard, resulting in
-// LNK1169 "multiply defined" errors at link time. __declspec(selectany) asks
-// MSVC to choose only one definition for the symbol it decorates. See details
-// at https://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx
-#ifdef _MSC_VER
-#define ABSL_STRING_VIEW_SELECTANY __declspec(selectany)
-#else
-#define ABSL_STRING_VIEW_SELECTANY
-#endif
-
-ABSL_STRING_VIEW_SELECTANY
-constexpr string_view::size_type string_view::npos;
-ABSL_STRING_VIEW_SELECTANY
-constexpr string_view::size_type string_view::kMaxSize;
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_USES_STD_STRING_VIEW
diff --git a/third_party/abseil_cpp/absl/strings/string_view.h b/third_party/abseil_cpp/absl/strings/string_view.h
deleted file mode 100644
index 5260b5b73f..0000000000
--- a/third_party/abseil_cpp/absl/strings/string_view.h
+++ /dev/null
@@ -1,629 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: string_view.h
-// -----------------------------------------------------------------------------
-//
-// This file contains the definition of the `absl::string_view` class. A
-// `string_view` points to a contiguous span of characters, often part or all of
-// another `std::string`, double-quoted string literal, character array, or even
-// another `string_view`.
-//
-// This `absl::string_view` abstraction is designed to be a drop-in
-// replacement for the C++17 `std::string_view` abstraction.
-#ifndef ABSL_STRINGS_STRING_VIEW_H_
-#define ABSL_STRINGS_STRING_VIEW_H_
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstring>
-#include <iosfwd>
-#include <iterator>
-#include <limits>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/throw_delegate.h"
-#include "absl/base/macros.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"
-
-#ifdef ABSL_USES_STD_STRING_VIEW
-
-#include <string_view>  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-using string_view = std::string_view;
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // ABSL_USES_STD_STRING_VIEW
-
-#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_INTERNAL_STRING_VIEW_MEMCMP __builtin_memcmp
-#else  // ABSL_HAVE_BUILTIN(__builtin_memcmp)
-#define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp
-#endif  // ABSL_HAVE_BUILTIN(__builtin_memcmp)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// absl::string_view
-//
-// A `string_view` provides a lightweight view into the string data provided by
-// a `std::string`, double-quoted string literal, character array, or even
-// another `string_view`. A `string_view` does *not* own the string to which it
-// points, and that data cannot be modified through the view.
-//
-// You can use `string_view` as a function or method parameter anywhere a
-// parameter can receive a double-quoted string literal, `const char*`,
-// `std::string`, or another `absl::string_view` argument with no need to copy
-// the string data. Systematic use of `string_view` within function arguments
-// reduces data copies and `strlen()` calls.
-//
-// Because of its small size, prefer passing `string_view` by value:
-//
-//   void MyFunction(absl::string_view arg);
-//
-// If circumstances require, you may also pass one by const reference:
-//
-//   void MyFunction(const absl::string_view& arg);  // not preferred
-//
-// Passing by value generates slightly smaller code for many architectures.
-//
-// In either case, the source data of the `string_view` must outlive the
-// `string_view` itself.
-//
-// A `string_view` is also suitable for local variables if you know that the
-// lifetime of the underlying object is longer than the lifetime of your
-// `string_view` variable. However, beware of binding a `string_view` to a
-// temporary value:
-//
-//   // BAD use of string_view: lifetime problem
-//   absl::string_view sv = obj.ReturnAString();
-//
-//   // GOOD use of string_view: str outlives sv
-//   std::string str = obj.ReturnAString();
-//   absl::string_view sv = str;
-//
-// Due to lifetime issues, a `string_view` is sometimes a poor choice for a
-// return value and usually a poor choice for a data member. If you do use a
-// `string_view` this way, it is your responsibility to ensure that the object
-// pointed to by the `string_view` outlives the `string_view`.
-//
-// A `string_view` may represent a whole string or just part of a string. For
-// example, when splitting a string, `std::vector<absl::string_view>` is a
-// natural data type for the output.
-//
-// For another example, a Cord is a non-contiguous, potentially very
-// long string-like object.  The Cord class has an interface that iteratively
-// provides string_view objects that point to the successive pieces of a Cord
-// object.
-//
-// When constructed from a source which is NUL-terminated, the `string_view`
-// itself will not include the NUL-terminator unless a specific size (including
-// the NUL) is passed to the constructor. As a result, common idioms that work
-// on NUL-terminated strings do not work on `string_view` objects. If you write
-// code that scans a `string_view`, you must check its length rather than test
-// for nul, for example. Note, however, that nuls may still be embedded within
-// a `string_view` explicitly.
-//
-// You may create a null `string_view` in two ways:
-//
-//   absl::string_view sv;
-//   absl::string_view sv(nullptr, 0);
-//
-// For the above, `sv.data() == nullptr`, `sv.length() == 0`, and
-// `sv.empty() == true`. Also, if you create a `string_view` with a non-null
-// pointer then `sv.data() != nullptr`. Thus, you can use `string_view()` to
-// signal an undefined value that is different from other `string_view` values
-// in a similar fashion to how `const char* p1 = nullptr;` is different from
-// `const char* p2 = "";`. However, in practice, it is not recommended to rely
-// on this behavior.
-//
-// Be careful not to confuse a null `string_view` with an empty one. A null
-// `string_view` is an empty `string_view`, but some empty `string_view`s are
-// not null. Prefer checking for emptiness over checking for null.
-//
-// There are many ways to create an empty string_view:
-//
-//   const char* nullcp = nullptr;
-//   // string_view.size() will return 0 in all cases.
-//   absl::string_view();
-//   absl::string_view(nullcp, 0);
-//   absl::string_view("");
-//   absl::string_view("", 0);
-//   absl::string_view("abcdef", 0);
-//   absl::string_view("abcdef" + 6, 0);
-//
-// All empty `string_view` objects whether null or not, are equal:
-//
-//   absl::string_view() == absl::string_view("", 0)
-//   absl::string_view(nullptr, 0) == absl::string_view("abcdef"+6, 0)
-class string_view {
- public:
-  using traits_type = std::char_traits<char>;
-  using value_type = char;
-  using pointer = char*;
-  using const_pointer = const char*;
-  using reference = char&;
-  using const_reference = const char&;
-  using const_iterator = const char*;
-  using iterator = const_iterator;
-  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-  using reverse_iterator = const_reverse_iterator;
-  using size_type = size_t;
-  using difference_type = std::ptrdiff_t;
-
-  static constexpr size_type npos = static_cast<size_type>(-1);
-
-  // Null `string_view` constructor
-  constexpr string_view() noexcept : ptr_(nullptr), length_(0) {}
-
-  // Implicit constructors
-
-  template <typename Allocator>
-  string_view(  // NOLINT(runtime/explicit)
-      const std::basic_string<char, std::char_traits<char>, Allocator>&
-          str) noexcept
-      // This is implemented in terms of `string_view(p, n)` so `str.size()`
-      // doesn't need to be reevaluated after `ptr_` is set.
-      : string_view(str.data(), str.size()) {}
-
-  // Implicit constructor of a `string_view` from NUL-terminated `str`. When
-  // accepting possibly null strings, use `absl::NullSafeStringView(str)`
-  // instead (see below).
-  constexpr string_view(const char* str)  // NOLINT(runtime/explicit)
-      : ptr_(str),
-        length_(str ? CheckLengthInternal(StrlenInternal(str)) : 0) {}
-
-  // Implicit constructor of a `string_view` from a `const char*` and length.
-  constexpr string_view(const char* data, size_type len)
-      : ptr_(data), length_(CheckLengthInternal(len)) {}
-
-  // NOTE: Harmlessly omitted to work around gdb bug.
-  //   constexpr string_view(const string_view&) noexcept = default;
-  //   string_view& operator=(const string_view&) noexcept = default;
-
-  // Iterators
-
-  // string_view::begin()
-  //
-  // Returns an iterator pointing to the first character at the beginning of the
-  // `string_view`, or `end()` if the `string_view` is empty.
-  constexpr const_iterator begin() const noexcept { return ptr_; }
-
-  // string_view::end()
-  //
-  // Returns an iterator pointing just beyond the last character at the end of
-  // the `string_view`. This iterator acts as a placeholder; attempting to
-  // access it results in undefined behavior.
-  constexpr const_iterator end() const noexcept { return ptr_ + length_; }
-
-  // string_view::cbegin()
-  //
-  // Returns a const iterator pointing to the first character at the beginning
-  // of the `string_view`, or `end()` if the `string_view` is empty.
-  constexpr const_iterator cbegin() const noexcept { return begin(); }
-
-  // string_view::cend()
-  //
-  // Returns a const iterator pointing just beyond the last character at the end
-  // of the `string_view`. This pointer acts as a placeholder; attempting to
-  // access its element results in undefined behavior.
-  constexpr const_iterator cend() const noexcept { return end(); }
-
-  // string_view::rbegin()
-  //
-  // Returns a reverse iterator pointing to the last character at the end of the
-  // `string_view`, or `rend()` if the `string_view` is empty.
-  const_reverse_iterator rbegin() const noexcept {
-    return const_reverse_iterator(end());
-  }
-
-  // string_view::rend()
-  //
-  // Returns a reverse iterator pointing just before the first character at the
-  // beginning of the `string_view`. This pointer acts as a placeholder;
-  // attempting to access its element results in undefined behavior.
-  const_reverse_iterator rend() const noexcept {
-    return const_reverse_iterator(begin());
-  }
-
-  // string_view::crbegin()
-  //
-  // Returns a const reverse iterator pointing to the last character at the end
-  // of the `string_view`, or `crend()` if the `string_view` is empty.
-  const_reverse_iterator crbegin() const noexcept { return rbegin(); }
-
-  // string_view::crend()
-  //
-  // Returns a const reverse iterator pointing just before the first character
-  // at the beginning of the `string_view`. This pointer acts as a placeholder;
-  // attempting to access its element results in undefined behavior.
-  const_reverse_iterator crend() const noexcept { return rend(); }
-
-  // Capacity Utilities
-
-  // string_view::size()
-  //
-  // Returns the number of characters in the `string_view`.
-  constexpr size_type size() const noexcept {
-    return length_;
-  }
-
-  // string_view::length()
-  //
-  // Returns the number of characters in the `string_view`. Alias for `size()`.
-  constexpr size_type length() const noexcept { return size(); }
-
-  // string_view::max_size()
-  //
-  // Returns the maximum number of characters the `string_view` can hold.
-  constexpr size_type max_size() const noexcept { return kMaxSize; }
-
-  // string_view::empty()
-  //
-  // Checks if the `string_view` is empty (refers to no characters).
-  constexpr bool empty() const noexcept { return length_ == 0; }
-
-  // string_view::operator[]
-  //
-  // Returns the ith element of the `string_view` using the array operator.
-  // Note that this operator does not perform any bounds checking.
-  constexpr const_reference operator[](size_type i) const {
-    return ABSL_HARDENING_ASSERT(i < size()), ptr_[i];
-  }
-
-  // string_view::at()
-  //
-  // Returns the ith element of the `string_view`. Bounds checking is performed,
-  // and an exception of type `std::out_of_range` will be thrown on invalid
-  // access.
-  constexpr const_reference at(size_type i) const {
-    return ABSL_PREDICT_TRUE(i < size())
-               ? ptr_[i]
-               : ((void)base_internal::ThrowStdOutOfRange(
-                      "absl::string_view::at"),
-                  ptr_[i]);
-  }
-
-  // string_view::front()
-  //
-  // Returns the first element of a `string_view`.
-  constexpr const_reference front() const {
-    return ABSL_HARDENING_ASSERT(!empty()), ptr_[0];
-  }
-
-  // string_view::back()
-  //
-  // Returns the last element of a `string_view`.
-  constexpr const_reference back() const {
-    return ABSL_HARDENING_ASSERT(!empty()), ptr_[size() - 1];
-  }
-
-  // string_view::data()
-  //
-  // Returns a pointer to the underlying character array (which is of course
-  // stored elsewhere). Note that `string_view::data()` may contain embedded nul
-  // characters, but the returned buffer may or may not be NUL-terminated;
-  // therefore, do not pass `data()` to a routine that expects a NUL-terminated
-  // string.
-  constexpr const_pointer data() const noexcept { return ptr_; }
-
-  // Modifiers
-
-  // string_view::remove_prefix()
-  //
-  // Removes the first `n` characters from the `string_view`. Note that the
-  // underlying string is not changed, only the view.
-  void remove_prefix(size_type n) {
-    ABSL_HARDENING_ASSERT(n <= length_);
-    ptr_ += n;
-    length_ -= n;
-  }
-
-  // string_view::remove_suffix()
-  //
-  // Removes the last `n` characters from the `string_view`. Note that the
-  // underlying string is not changed, only the view.
-  void remove_suffix(size_type n) {
-    ABSL_HARDENING_ASSERT(n <= length_);
-    length_ -= n;
-  }
-
-  // string_view::swap()
-  //
-  // Swaps this `string_view` with another `string_view`.
-  void swap(string_view& s) noexcept {
-    auto t = *this;
-    *this = s;
-    s = t;
-  }
-
-  // Explicit conversion operators
-
-  // Converts to `std::basic_string`.
-  template <typename A>
-  explicit operator std::basic_string<char, traits_type, A>() const {
-    if (!data()) return {};
-    return std::basic_string<char, traits_type, A>(data(), size());
-  }
-
-  // string_view::copy()
-  //
-  // Copies the contents of the `string_view` at offset `pos` and length `n`
-  // into `buf`.
-  size_type copy(char* buf, size_type n, size_type pos = 0) const {
-    if (ABSL_PREDICT_FALSE(pos > length_)) {
-      base_internal::ThrowStdOutOfRange("absl::string_view::copy");
-    }
-    size_type rlen = (std::min)(length_ - pos, n);
-    if (rlen > 0) {
-      const char* start = ptr_ + pos;
-      traits_type::copy(buf, start, rlen);
-    }
-    return rlen;
-  }
-
-  // string_view::substr()
-  //
-  // Returns a "substring" of the `string_view` (at offset `pos` and length
-  // `n`) as another string_view. This function throws `std::out_of_bounds` if
-  // `pos > size`.
-  // Use absl::ClippedSubstr if you need a truncating substr operation.
-  constexpr string_view substr(size_type pos, size_type n = npos) const {
-    return ABSL_PREDICT_FALSE(pos > length_)
-               ? (base_internal::ThrowStdOutOfRange(
-                      "absl::string_view::substr"),
-                  string_view())
-               : string_view(ptr_ + pos, Min(n, length_ - pos));
-  }
-
-  // string_view::compare()
-  //
-  // Performs a lexicographical comparison between the `string_view` and
-  // another `absl::string_view`, returning -1 if `this` is less than, 0 if
-  // `this` is equal to, and 1 if `this` is greater than the passed string
-  // view. Note that in the case of data equality, a further comparison is made
-  // on the respective sizes of the two `string_view`s to determine which is
-  // smaller, equal, or greater.
-  constexpr int compare(string_view x) const noexcept {
-    return CompareImpl(length_, x.length_,
-                       Min(length_, x.length_) == 0
-                           ? 0
-                           : ABSL_INTERNAL_STRING_VIEW_MEMCMP(
-                                 ptr_, x.ptr_, Min(length_, x.length_)));
-  }
-
-  // Overload of `string_view::compare()` for comparing a substring of the
-  // 'string_view` and another `absl::string_view`.
-  int compare(size_type pos1, size_type count1, string_view v) const {
-    return substr(pos1, count1).compare(v);
-  }
-
-  // Overload of `string_view::compare()` for comparing a substring of the
-  // `string_view` and a substring of another `absl::string_view`.
-  int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
-              size_type count2) const {
-    return substr(pos1, count1).compare(v.substr(pos2, count2));
-  }
-
-  // Overload of `string_view::compare()` for comparing a `string_view` and a
-  // a different  C-style string `s`.
-  int compare(const char* s) const { return compare(string_view(s)); }
-
-  // Overload of `string_view::compare()` for comparing a substring of the
-  // `string_view` and a different string C-style string `s`.
-  int compare(size_type pos1, size_type count1, const char* s) const {
-    return substr(pos1, count1).compare(string_view(s));
-  }
-
-  // Overload of `string_view::compare()` for comparing a substring of the
-  // `string_view` and a substring of a different C-style string `s`.
-  int compare(size_type pos1, size_type count1, const char* s,
-              size_type count2) const {
-    return substr(pos1, count1).compare(string_view(s, count2));
-  }
-
-  // Find Utilities
-
-  // string_view::find()
-  //
-  // Finds the first occurrence of the substring `s` within the `string_view`,
-  // returning the position of the first character's match, or `npos` if no
-  // match was found.
-  size_type find(string_view s, size_type pos = 0) const noexcept;
-
-  // Overload of `string_view::find()` for finding the given character `c`
-  // within the `string_view`.
-  size_type find(char c, size_type pos = 0) const noexcept;
-
-  // string_view::rfind()
-  //
-  // Finds the last occurrence of a substring `s` within the `string_view`,
-  // returning the position of the first character's match, or `npos` if no
-  // match was found.
-  size_type rfind(string_view s, size_type pos = npos) const
-      noexcept;
-
-  // Overload of `string_view::rfind()` for finding the last given character `c`
-  // within the `string_view`.
-  size_type rfind(char c, size_type pos = npos) const noexcept;
-
-  // string_view::find_first_of()
-  //
-  // Finds the first occurrence of any of the characters in `s` within the
-  // `string_view`, returning the start position of the match, or `npos` if no
-  // match was found.
-  size_type find_first_of(string_view s, size_type pos = 0) const
-      noexcept;
-
-  // Overload of `string_view::find_first_of()` for finding a character `c`
-  // within the `string_view`.
-  size_type find_first_of(char c, size_type pos = 0) const
-      noexcept {
-    return find(c, pos);
-  }
-
-  // string_view::find_last_of()
-  //
-  // Finds the last occurrence of any of the characters in `s` within the
-  // `string_view`, returning the start position of the match, or `npos` if no
-  // match was found.
-  size_type find_last_of(string_view s, size_type pos = npos) const
-      noexcept;
-
-  // Overload of `string_view::find_last_of()` for finding a character `c`
-  // within the `string_view`.
-  size_type find_last_of(char c, size_type pos = npos) const
-      noexcept {
-    return rfind(c, pos);
-  }
-
-  // string_view::find_first_not_of()
-  //
-  // Finds the first occurrence of any of the characters not in `s` within the
-  // `string_view`, returning the start position of the first non-match, or
-  // `npos` if no non-match was found.
-  size_type find_first_not_of(string_view s, size_type pos = 0) const noexcept;
-
-  // Overload of `string_view::find_first_not_of()` for finding a character
-  // that is not `c` within the `string_view`.
-  size_type find_first_not_of(char c, size_type pos = 0) const noexcept;
-
-  // string_view::find_last_not_of()
-  //
-  // Finds the last occurrence of any of the characters not in `s` within the
-  // `string_view`, returning the start position of the last non-match, or
-  // `npos` if no non-match was found.
-  size_type find_last_not_of(string_view s,
-                                          size_type pos = npos) const noexcept;
-
-  // Overload of `string_view::find_last_not_of()` for finding a character
-  // that is not `c` within the `string_view`.
-  size_type find_last_not_of(char c, size_type pos = npos) const
-      noexcept;
-
- private:
-  static constexpr size_type kMaxSize =
-      (std::numeric_limits<difference_type>::max)();
-
-  static constexpr size_type CheckLengthInternal(size_type len) {
-    return ABSL_HARDENING_ASSERT(len <= kMaxSize), len;
-  }
-
-  static constexpr size_type StrlenInternal(const char* str) {
-#if defined(_MSC_VER) && _MSC_VER >= 1910 && !defined(__clang__)
-    // MSVC 2017+ can evaluate this at compile-time.
-    const char* begin = str;
-    while (*str != '\0') ++str;
-    return str - begin;
-#elif ABSL_HAVE_BUILTIN(__builtin_strlen) || \
-    (defined(__GNUC__) && !defined(__clang__))
-    // GCC has __builtin_strlen according to
-    // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html, but
-    // ABSL_HAVE_BUILTIN doesn't detect that, so we use the extra checks above.
-    // __builtin_strlen is constexpr.
-    return __builtin_strlen(str);
-#else
-    return str ? strlen(str) : 0;
-#endif
-  }
-
-  static constexpr size_t Min(size_type length_a, size_type length_b) {
-    return length_a < length_b ? length_a : length_b;
-  }
-
-  static constexpr int CompareImpl(size_type length_a, size_type length_b,
-                                   int compare_result) {
-    return compare_result == 0 ? static_cast<int>(length_a > length_b) -
-                                     static_cast<int>(length_a < length_b)
-                               : (compare_result < 0 ? -1 : 1);
-  }
-
-  const char* ptr_;
-  size_type length_;
-};
-
-// This large function is defined inline so that in a fairly common case where
-// one of the arguments is a literal, the compiler can elide a lot of the
-// following comparisons.
-constexpr bool operator==(string_view x, string_view y) noexcept {
-  return x.size() == y.size() &&
-         (x.empty() ||
-          ABSL_INTERNAL_STRING_VIEW_MEMCMP(x.data(), y.data(), x.size()) == 0);
-}
-
-constexpr bool operator!=(string_view x, string_view y) noexcept {
-  return !(x == y);
-}
-
-constexpr bool operator<(string_view x, string_view y) noexcept {
-  return x.compare(y) < 0;
-}
-
-constexpr bool operator>(string_view x, string_view y) noexcept {
-  return y < x;
-}
-
-constexpr bool operator<=(string_view x, string_view y) noexcept {
-  return !(y < x);
-}
-
-constexpr bool operator>=(string_view x, string_view y) noexcept {
-  return !(x < y);
-}
-
-// IO Insertion Operator
-std::ostream& operator<<(std::ostream& o, string_view piece);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#undef ABSL_INTERNAL_STRING_VIEW_MEMCMP
-
-#endif  // ABSL_USES_STD_STRING_VIEW
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// ClippedSubstr()
-//
-// Like `s.substr(pos, n)`, but clips `pos` to an upper bound of `s.size()`.
-// Provided because std::string_view::substr throws if `pos > size()`
-inline string_view ClippedSubstr(string_view s, size_t pos,
-                                 size_t n = string_view::npos) {
-  pos = (std::min)(pos, static_cast<size_t>(s.size()));
-  return s.substr(pos, n);
-}
-
-// NullSafeStringView()
-//
-// Creates an `absl::string_view` from a pointer `p` even if it's null-valued.
-// This function should be used where an `absl::string_view` can be created from
-// a possibly-null pointer.
-constexpr string_view NullSafeStringView(const char* p) {
-  return p ? string_view(p) : string_view();
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_STRING_VIEW_H_
diff --git a/third_party/abseil_cpp/absl/strings/string_view_benchmark.cc b/third_party/abseil_cpp/absl/strings/string_view_benchmark.cc
deleted file mode 100644
index 0d74e23e2f..0000000000
--- a/third_party/abseil_cpp/absl/strings/string_view_benchmark.cc
+++ /dev/null
@@ -1,381 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/string_view.h"
-
-#include <algorithm>
-#include <cstdint>
-#include <map>
-#include <random>
-#include <string>
-#include <unordered_set>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-#include "absl/base/attributes.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-void BM_StringViewFromString(benchmark::State& state) {
-  std::string s(state.range(0), 'x');
-  std::string* ps = &s;
-  struct SV {
-    SV() = default;
-    explicit SV(const std::string& s) : sv(s) {}
-    absl::string_view sv;
-  } sv;
-  SV* psv = &sv;
-  benchmark::DoNotOptimize(ps);
-  benchmark::DoNotOptimize(psv);
-  for (auto _ : state) {
-    new (psv) SV(*ps);
-    benchmark::DoNotOptimize(sv);
-  }
-}
-BENCHMARK(BM_StringViewFromString)->Arg(12)->Arg(128);
-
-// Provide a forcibly out-of-line wrapper for operator== that can be used in
-// benchmarks to measure the impact of inlining.
-ABSL_ATTRIBUTE_NOINLINE
-bool NonInlinedEq(absl::string_view a, absl::string_view b) { return a == b; }
-
-// We use functions that cannot be inlined to perform the comparison loops so
-// that inlining of the operator== can't optimize away *everything*.
-ABSL_ATTRIBUTE_NOINLINE
-void DoEqualityComparisons(benchmark::State& state, absl::string_view a,
-                           absl::string_view b) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a == b);
-  }
-}
-
-void BM_EqualIdentical(benchmark::State& state) {
-  std::string x(state.range(0), 'a');
-  DoEqualityComparisons(state, x, x);
-}
-BENCHMARK(BM_EqualIdentical)->DenseRange(0, 3)->Range(4, 1 << 10);
-
-void BM_EqualSame(benchmark::State& state) {
-  std::string x(state.range(0), 'a');
-  std::string y = x;
-  DoEqualityComparisons(state, x, y);
-}
-BENCHMARK(BM_EqualSame)
-    ->DenseRange(0, 10)
-    ->Arg(20)
-    ->Arg(40)
-    ->Arg(70)
-    ->Arg(110)
-    ->Range(160, 4096);
-
-void BM_EqualDifferent(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string x(len, 'a');
-  std::string y = x;
-  if (len > 0) {
-    y[len - 1] = 'b';
-  }
-  DoEqualityComparisons(state, x, y);
-}
-BENCHMARK(BM_EqualDifferent)->DenseRange(0, 3)->Range(4, 1 << 10);
-
-// This benchmark is intended to check that important simplifications can be
-// made with absl::string_view comparisons against constant strings. The idea is
-// that if constant strings cause redundant components of the comparison, the
-// compiler should detect and eliminate them. Here we use 8 different strings,
-// each with the same size. Provided our comparison makes the implementation
-// inline-able by the compiler, it should fold all of these away into a single
-// size check once per loop iteration.
-ABSL_ATTRIBUTE_NOINLINE
-void DoConstantSizeInlinedEqualityComparisons(benchmark::State& state,
-                                              absl::string_view a) {
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a == "aaa");
-    benchmark::DoNotOptimize(a == "bbb");
-    benchmark::DoNotOptimize(a == "ccc");
-    benchmark::DoNotOptimize(a == "ddd");
-    benchmark::DoNotOptimize(a == "eee");
-    benchmark::DoNotOptimize(a == "fff");
-    benchmark::DoNotOptimize(a == "ggg");
-    benchmark::DoNotOptimize(a == "hhh");
-  }
-}
-void BM_EqualConstantSizeInlined(benchmark::State& state) {
-  std::string x(state.range(0), 'a');
-  DoConstantSizeInlinedEqualityComparisons(state, x);
-}
-// We only need to check for size of 3, and <> 3 as this benchmark only has to
-// do with size differences.
-BENCHMARK(BM_EqualConstantSizeInlined)->DenseRange(2, 4);
-
-// This benchmark exists purely to give context to the above timings: this is
-// what they would look like if the compiler is completely unable to simplify
-// between two comparisons when they are comparing against constant strings.
-ABSL_ATTRIBUTE_NOINLINE
-void DoConstantSizeNonInlinedEqualityComparisons(benchmark::State& state,
-                                                 absl::string_view a) {
-  for (auto _ : state) {
-    // Force these out-of-line to compare with the above function.
-    benchmark::DoNotOptimize(NonInlinedEq(a, "aaa"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "bbb"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "ccc"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "ddd"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "eee"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "fff"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "ggg"));
-    benchmark::DoNotOptimize(NonInlinedEq(a, "hhh"));
-  }
-}
-
-void BM_EqualConstantSizeNonInlined(benchmark::State& state) {
-  std::string x(state.range(0), 'a');
-  DoConstantSizeNonInlinedEqualityComparisons(state, x);
-}
-// We only need to check for size of 3, and <> 3 as this benchmark only has to
-// do with size differences.
-BENCHMARK(BM_EqualConstantSizeNonInlined)->DenseRange(2, 4);
-
-void BM_CompareSame(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string x;
-  for (int i = 0; i < len; i++) {
-    x += 'a';
-  }
-  std::string y = x;
-  absl::string_view a = x;
-  absl::string_view b = y;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(b);
-    benchmark::DoNotOptimize(a.compare(b));
-  }
-}
-BENCHMARK(BM_CompareSame)->DenseRange(0, 3)->Range(4, 1 << 10);
-
-void BM_CompareFirstOneLess(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string x(len, 'a');
-  std::string y = x;
-  y.back() = 'b';
-  absl::string_view a = x;
-  absl::string_view b = y;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(b);
-    benchmark::DoNotOptimize(a.compare(b));
-  }
-}
-BENCHMARK(BM_CompareFirstOneLess)->DenseRange(1, 3)->Range(4, 1 << 10);
-
-void BM_CompareSecondOneLess(benchmark::State& state) {
-  const int len = state.range(0);
-  std::string x(len, 'a');
-  std::string y = x;
-  x.back() = 'b';
-  absl::string_view a = x;
-  absl::string_view b = y;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(b);
-    benchmark::DoNotOptimize(a.compare(b));
-  }
-}
-BENCHMARK(BM_CompareSecondOneLess)->DenseRange(1, 3)->Range(4, 1 << 10);
-
-void BM_find_string_view_len_one(benchmark::State& state) {
-  std::string haystack(state.range(0), '0');
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.find("x"));  // not present; length 1
-  }
-}
-BENCHMARK(BM_find_string_view_len_one)->Range(1, 1 << 20);
-
-void BM_find_string_view_len_two(benchmark::State& state) {
-  std::string haystack(state.range(0), '0');
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.find("xx"));  // not present; length 2
-  }
-}
-BENCHMARK(BM_find_string_view_len_two)->Range(1, 1 << 20);
-
-void BM_find_one_char(benchmark::State& state) {
-  std::string haystack(state.range(0), '0');
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.find('x'));  // not present
-  }
-}
-BENCHMARK(BM_find_one_char)->Range(1, 1 << 20);
-
-void BM_rfind_one_char(benchmark::State& state) {
-  std::string haystack(state.range(0), '0');
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.rfind('x'));  // not present
-  }
-}
-BENCHMARK(BM_rfind_one_char)->Range(1, 1 << 20);
-
-void BM_worst_case_find_first_of(benchmark::State& state, int haystack_len) {
-  const int needle_len = state.range(0);
-  std::string needle;
-  for (int i = 0; i < needle_len; ++i) {
-    needle += 'a' + i;
-  }
-  std::string haystack(haystack_len, '0');  // 1000 zeros.
-
-  absl::string_view s(haystack);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(s.find_first_of(needle));
-  }
-}
-
-void BM_find_first_of_short(benchmark::State& state) {
-  BM_worst_case_find_first_of(state, 10);
-}
-
-void BM_find_first_of_medium(benchmark::State& state) {
-  BM_worst_case_find_first_of(state, 100);
-}
-
-void BM_find_first_of_long(benchmark::State& state) {
-  BM_worst_case_find_first_of(state, 1000);
-}
-
-BENCHMARK(BM_find_first_of_short)->DenseRange(0, 4)->Arg(8)->Arg(16)->Arg(32);
-BENCHMARK(BM_find_first_of_medium)->DenseRange(0, 4)->Arg(8)->Arg(16)->Arg(32);
-BENCHMARK(BM_find_first_of_long)->DenseRange(0, 4)->Arg(8)->Arg(16)->Arg(32);
-
-struct EasyMap : public std::map<absl::string_view, uint64_t> {
-  explicit EasyMap(size_t) {}
-};
-
-// This templated benchmark helper function is intended to stress operator== or
-// operator< in a realistic test.  It surely isn't entirely realistic, but it's
-// a start.  The test creates a map of type Map, a template arg, and populates
-// it with table_size key/value pairs. Each key has WordsPerKey words.  After
-// creating the map, a number of lookups are done in random order.  Some keys
-// are used much more frequently than others in this phase of the test.
-template <typename Map, int WordsPerKey>
-void StringViewMapBenchmark(benchmark::State& state) {
-  const int table_size = state.range(0);
-  const double kFractionOfKeysThatAreHot = 0.2;
-  const int kNumLookupsOfHotKeys = 20;
-  const int kNumLookupsOfColdKeys = 1;
-  const char* words[] = {"the",   "quick",  "brown",    "fox",      "jumped",
-                         "over",  "the",    "lazy",     "dog",      "and",
-                         "found", "a",      "large",    "mushroom", "and",
-                         "a",     "couple", "crickets", "eating",   "pie"};
-  // Create some keys that consist of words in random order.
-  std::random_device r;
-  std::seed_seq seed({r(), r(), r(), r(), r(), r(), r(), r()});
-  std::mt19937 rng(seed);
-  std::vector<std::string> keys(table_size);
-  std::vector<int> all_indices;
-  const int kBlockSize = 1 << 12;
-  std::unordered_set<std::string> t(kBlockSize);
-  std::uniform_int_distribution<int> uniform(0, ABSL_ARRAYSIZE(words) - 1);
-  for (int i = 0; i < table_size; i++) {
-    all_indices.push_back(i);
-    do {
-      keys[i].clear();
-      for (int j = 0; j < WordsPerKey; j++) {
-        absl::StrAppend(&keys[i], j > 0 ? " " : "", words[uniform(rng)]);
-      }
-    } while (!t.insert(keys[i]).second);
-  }
-
-  // Create a list of strings to lookup: a permutation of the array of
-  // keys we just created, with repeats.  "Hot" keys get repeated more.
-  std::shuffle(all_indices.begin(), all_indices.end(), rng);
-  const int num_hot = table_size * kFractionOfKeysThatAreHot;
-  const int num_cold = table_size - num_hot;
-  std::vector<int> hot_indices(all_indices.begin(),
-                               all_indices.begin() + num_hot);
-  std::vector<int> indices;
-  for (int i = 0; i < kNumLookupsOfColdKeys; i++) {
-    indices.insert(indices.end(), all_indices.begin(), all_indices.end());
-  }
-  for (int i = 0; i < kNumLookupsOfHotKeys - kNumLookupsOfColdKeys; i++) {
-    indices.insert(indices.end(), hot_indices.begin(), hot_indices.end());
-  }
-  std::shuffle(indices.begin(), indices.end(), rng);
-  ABSL_RAW_CHECK(
-      num_cold * kNumLookupsOfColdKeys + num_hot * kNumLookupsOfHotKeys ==
-          indices.size(),
-      "");
-  // After constructing the array we probe it with absl::string_views built from
-  // test_strings.  This means operator== won't see equal pointers, so
-  // it'll have to check for equal lengths and equal characters.
-  std::vector<std::string> test_strings(indices.size());
-  for (int i = 0; i < indices.size(); i++) {
-    test_strings[i] = keys[indices[i]];
-  }
-
-  // Run the benchmark. It includes map construction but is mostly
-  // map lookups.
-  for (auto _ : state) {
-    Map h(table_size);
-    for (int i = 0; i < table_size; i++) {
-      h[keys[i]] = i * 2;
-    }
-    ABSL_RAW_CHECK(h.size() == table_size, "");
-    uint64_t sum = 0;
-    for (int i = 0; i < indices.size(); i++) {
-      sum += h[test_strings[i]];
-    }
-    benchmark::DoNotOptimize(sum);
-  }
-}
-
-void BM_StdMap_4(benchmark::State& state) {
-  StringViewMapBenchmark<EasyMap, 4>(state);
-}
-BENCHMARK(BM_StdMap_4)->Range(1 << 10, 1 << 16);
-
-void BM_StdMap_8(benchmark::State& state) {
-  StringViewMapBenchmark<EasyMap, 8>(state);
-}
-BENCHMARK(BM_StdMap_8)->Range(1 << 10, 1 << 16);
-
-void BM_CopyToStringNative(benchmark::State& state) {
-  std::string src(state.range(0), 'x');
-  absl::string_view sv(src);
-  std::string dst;
-  for (auto _ : state) {
-    dst.assign(sv.begin(), sv.end());
-  }
-}
-BENCHMARK(BM_CopyToStringNative)->Range(1 << 3, 1 << 12);
-
-void BM_AppendToStringNative(benchmark::State& state) {
-  std::string src(state.range(0), 'x');
-  absl::string_view sv(src);
-  std::string dst;
-  for (auto _ : state) {
-    dst.clear();
-    dst.insert(dst.end(), sv.begin(), sv.end());
-  }
-}
-BENCHMARK(BM_AppendToStringNative)->Range(1 << 3, 1 << 12);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/string_view_test.cc b/third_party/abseil_cpp/absl/strings/string_view_test.cc
deleted file mode 100644
index dcebb15001..0000000000
--- a/third_party/abseil_cpp/absl/strings/string_view_test.cc
+++ /dev/null
@@ -1,1264 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/string_view.h"
-
-#include <stdlib.h>
-#include <iomanip>
-#include <iterator>
-#include <limits>
-#include <map>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/base/dynamic_annotations.h"
-#include "absl/base/options.h"
-
-#if defined(ABSL_HAVE_STD_STRING_VIEW) || defined(__ANDROID__)
-// We don't control the death messaging when using std::string_view.
-// Android assert messages only go to system log, so death tests cannot inspect
-// the message for matching.
-#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
-  EXPECT_DEATH_IF_SUPPORTED(statement, ".*")
-#else
-#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
-  EXPECT_DEATH_IF_SUPPORTED(statement, regex)
-#endif
-
-namespace {
-
-// A minimal allocator that uses malloc().
-template <typename T>
-struct Mallocator {
-  typedef T value_type;
-  typedef size_t size_type;
-  typedef ptrdiff_t difference_type;
-  typedef T* pointer;
-  typedef const T* const_pointer;
-  typedef T& reference;
-  typedef const T& const_reference;
-
-  size_type max_size() const {
-    return size_t(std::numeric_limits<size_type>::max()) / sizeof(value_type);
-  }
-  template <typename U>
-  struct rebind {
-    typedef Mallocator<U> other;
-  };
-  Mallocator() = default;
-  template <class U>
-  Mallocator(const Mallocator<U>&) {}  // NOLINT(runtime/explicit)
-
-  T* allocate(size_t n) { return static_cast<T*>(std::malloc(n * sizeof(T))); }
-  void deallocate(T* p, size_t) { std::free(p); }
-};
-template <typename T, typename U>
-bool operator==(const Mallocator<T>&, const Mallocator<U>&) {
-  return true;
-}
-template <typename T, typename U>
-bool operator!=(const Mallocator<T>&, const Mallocator<U>&) {
-  return false;
-}
-
-TEST(StringViewTest, Ctor) {
-  {
-    // Null.
-    absl::string_view s10;
-    EXPECT_TRUE(s10.data() == nullptr);
-    EXPECT_EQ(0, s10.length());
-  }
-
-  {
-    // const char* without length.
-    const char* hello = "hello";
-    absl::string_view s20(hello);
-    EXPECT_TRUE(s20.data() == hello);
-    EXPECT_EQ(5, s20.length());
-
-    // const char* with length.
-    absl::string_view s21(hello, 4);
-    EXPECT_TRUE(s21.data() == hello);
-    EXPECT_EQ(4, s21.length());
-
-    // Not recommended, but valid C++
-    absl::string_view s22(hello, 6);
-    EXPECT_TRUE(s22.data() == hello);
-    EXPECT_EQ(6, s22.length());
-  }
-
-  {
-    // std::string.
-    std::string hola = "hola";
-    absl::string_view s30(hola);
-    EXPECT_TRUE(s30.data() == hola.data());
-    EXPECT_EQ(4, s30.length());
-
-    // std::string with embedded '\0'.
-    hola.push_back('\0');
-    hola.append("h2");
-    hola.push_back('\0');
-    absl::string_view s31(hola);
-    EXPECT_TRUE(s31.data() == hola.data());
-    EXPECT_EQ(8, s31.length());
-  }
-
-  {
-    using mstring =
-        std::basic_string<char, std::char_traits<char>, Mallocator<char>>;
-    mstring str1("BUNGIE-JUMPING!");
-    const mstring str2("SLEEPING!");
-
-    absl::string_view s1(str1);
-    s1.remove_prefix(strlen("BUNGIE-JUM"));
-
-    absl::string_view s2(str2);
-    s2.remove_prefix(strlen("SLEE"));
-
-    EXPECT_EQ(s1, s2);
-    EXPECT_EQ(s1, "PING!");
-  }
-
-  // TODO(mec): absl::string_view(const absl::string_view&);
-}
-
-TEST(StringViewTest, Swap) {
-  absl::string_view a("a");
-  absl::string_view b("bbb");
-  EXPECT_TRUE(noexcept(a.swap(b)));
-  a.swap(b);
-  EXPECT_EQ(a, "bbb");
-  EXPECT_EQ(b, "a");
-  a.swap(b);
-  EXPECT_EQ(a, "a");
-  EXPECT_EQ(b, "bbb");
-}
-
-TEST(StringViewTest, STLComparator) {
-  std::string s1("foo");
-  std::string s2("bar");
-  std::string s3("baz");
-
-  absl::string_view p1(s1);
-  absl::string_view p2(s2);
-  absl::string_view p3(s3);
-
-  typedef std::map<absl::string_view, int> TestMap;
-  TestMap map;
-
-  map.insert(std::make_pair(p1, 0));
-  map.insert(std::make_pair(p2, 1));
-  map.insert(std::make_pair(p3, 2));
-  EXPECT_EQ(map.size(), 3);
-
-  TestMap::const_iterator iter = map.begin();
-  EXPECT_EQ(iter->second, 1);
-  ++iter;
-  EXPECT_EQ(iter->second, 2);
-  ++iter;
-  EXPECT_EQ(iter->second, 0);
-  ++iter;
-  EXPECT_TRUE(iter == map.end());
-
-  TestMap::iterator new_iter = map.find("zot");
-  EXPECT_TRUE(new_iter == map.end());
-
-  new_iter = map.find("bar");
-  EXPECT_TRUE(new_iter != map.end());
-
-  map.erase(new_iter);
-  EXPECT_EQ(map.size(), 2);
-
-  iter = map.begin();
-  EXPECT_EQ(iter->second, 2);
-  ++iter;
-  EXPECT_EQ(iter->second, 0);
-  ++iter;
-  EXPECT_TRUE(iter == map.end());
-}
-
-#define COMPARE(result, op, x, y)                                      \
-  EXPECT_EQ(result, absl::string_view((x)) op absl::string_view((y))); \
-  EXPECT_EQ(result, absl::string_view((x)).compare(absl::string_view((y))) op 0)
-
-TEST(StringViewTest, ComparisonOperators) {
-  COMPARE(true, ==, "",   "");
-  COMPARE(true, ==, "", absl::string_view());
-  COMPARE(true, ==, absl::string_view(), "");
-  COMPARE(true, ==, "a",  "a");
-  COMPARE(true, ==, "aa", "aa");
-  COMPARE(false, ==, "a",  "");
-  COMPARE(false, ==, "",   "a");
-  COMPARE(false, ==, "a",  "b");
-  COMPARE(false, ==, "a",  "aa");
-  COMPARE(false, ==, "aa", "a");
-
-  COMPARE(false, !=, "",   "");
-  COMPARE(false, !=, "a",  "a");
-  COMPARE(false, !=, "aa", "aa");
-  COMPARE(true, !=, "a",  "");
-  COMPARE(true, !=, "",   "a");
-  COMPARE(true, !=, "a",  "b");
-  COMPARE(true, !=, "a",  "aa");
-  COMPARE(true, !=, "aa", "a");
-
-  COMPARE(true, <, "a",  "b");
-  COMPARE(true, <, "a",  "aa");
-  COMPARE(true, <, "aa", "b");
-  COMPARE(true, <, "aa", "bb");
-  COMPARE(false, <, "a",  "a");
-  COMPARE(false, <, "b",  "a");
-  COMPARE(false, <, "aa", "a");
-  COMPARE(false, <, "b",  "aa");
-  COMPARE(false, <, "bb", "aa");
-
-  COMPARE(true, <=, "a",  "a");
-  COMPARE(true, <=, "a",  "b");
-  COMPARE(true, <=, "a",  "aa");
-  COMPARE(true, <=, "aa", "b");
-  COMPARE(true, <=, "aa", "bb");
-  COMPARE(false, <=, "b",  "a");
-  COMPARE(false, <=, "aa", "a");
-  COMPARE(false, <=, "b",  "aa");
-  COMPARE(false, <=, "bb", "aa");
-
-  COMPARE(false, >=, "a",  "b");
-  COMPARE(false, >=, "a",  "aa");
-  COMPARE(false, >=, "aa", "b");
-  COMPARE(false, >=, "aa", "bb");
-  COMPARE(true, >=, "a",  "a");
-  COMPARE(true, >=, "b",  "a");
-  COMPARE(true, >=, "aa", "a");
-  COMPARE(true, >=, "b",  "aa");
-  COMPARE(true, >=, "bb", "aa");
-
-  COMPARE(false, >, "a",  "a");
-  COMPARE(false, >, "a",  "b");
-  COMPARE(false, >, "a",  "aa");
-  COMPARE(false, >, "aa", "b");
-  COMPARE(false, >, "aa", "bb");
-  COMPARE(true, >, "b",  "a");
-  COMPARE(true, >, "aa", "a");
-  COMPARE(true, >, "b",  "aa");
-  COMPARE(true, >, "bb", "aa");
-}
-
-TEST(StringViewTest, ComparisonOperatorsByCharacterPosition) {
-  std::string x;
-  for (int i = 0; i < 256; i++) {
-    x += 'a';
-    std::string y = x;
-    COMPARE(true, ==, x, y);
-    for (int j = 0; j < i; j++) {
-      std::string z = x;
-      z[j] = 'b';       // Differs in position 'j'
-      COMPARE(false, ==, x, z);
-      COMPARE(true, <, x, z);
-      COMPARE(true, >, z, x);
-      if (j + 1 < i) {
-        z[j + 1] = 'A';  // Differs in position 'j+1' as well
-        COMPARE(false, ==, x, z);
-        COMPARE(true, <, x, z);
-        COMPARE(true, >, z, x);
-        z[j + 1] = 'z';  // Differs in position 'j+1' as well
-        COMPARE(false, ==, x, z);
-        COMPARE(true, <, x, z);
-        COMPARE(true, >, z, x);
-      }
-    }
-  }
-}
-#undef COMPARE
-
-// Sadly, our users often confuse std::string::npos with
-// absl::string_view::npos; So much so that we test here that they are the same.
-// They need to both be unsigned, and both be the maximum-valued integer of
-// their type.
-
-template <typename T>
-struct is_type {
-  template <typename U>
-  static bool same(U) {
-    return false;
-  }
-  static bool same(T) { return true; }
-};
-
-TEST(StringViewTest, NposMatchesStdStringView) {
-  EXPECT_EQ(absl::string_view::npos, std::string::npos);
-
-  EXPECT_TRUE(is_type<size_t>::same(absl::string_view::npos));
-  EXPECT_FALSE(is_type<size_t>::same(""));
-
-  // Make sure absl::string_view::npos continues to be a header constant.
-  char test[absl::string_view::npos & 1] = {0};
-  EXPECT_EQ(0, test[0]);
-}
-
-TEST(StringViewTest, STL1) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  const absl::string_view d("foobar");
-  const absl::string_view e;
-  std::string temp("123");
-  temp += '\0';
-  temp += "456";
-  const absl::string_view f(temp);
-
-  EXPECT_EQ(a[6], 'g');
-  EXPECT_EQ(b[0], 'a');
-  EXPECT_EQ(c[2], 'z');
-  EXPECT_EQ(f[3], '\0');
-  EXPECT_EQ(f[5], '5');
-
-  EXPECT_EQ(*d.data(), 'f');
-  EXPECT_EQ(d.data()[5], 'r');
-  EXPECT_TRUE(e.data() == nullptr);
-
-  EXPECT_EQ(*a.begin(), 'a');
-  EXPECT_EQ(*(b.begin() + 2), 'c');
-  EXPECT_EQ(*(c.end() - 1), 'z');
-
-  EXPECT_EQ(*a.rbegin(), 'z');
-  EXPECT_EQ(*(b.rbegin() + 2), 'a');
-  EXPECT_EQ(*(c.rend() - 1), 'x');
-  EXPECT_TRUE(a.rbegin() + 26 == a.rend());
-
-  EXPECT_EQ(a.size(), 26);
-  EXPECT_EQ(b.size(), 3);
-  EXPECT_EQ(c.size(), 3);
-  EXPECT_EQ(d.size(), 6);
-  EXPECT_EQ(e.size(), 0);
-  EXPECT_EQ(f.size(), 7);
-
-  EXPECT_TRUE(!d.empty());
-  EXPECT_TRUE(d.begin() != d.end());
-  EXPECT_TRUE(d.begin() + 6 == d.end());
-
-  EXPECT_TRUE(e.empty());
-  EXPECT_TRUE(e.begin() == e.end());
-
-  char buf[4] = { '%', '%', '%', '%' };
-  EXPECT_EQ(a.copy(buf, 4), 4);
-  EXPECT_EQ(buf[0], a[0]);
-  EXPECT_EQ(buf[1], a[1]);
-  EXPECT_EQ(buf[2], a[2]);
-  EXPECT_EQ(buf[3], a[3]);
-  EXPECT_EQ(a.copy(buf, 3, 7), 3);
-  EXPECT_EQ(buf[0], a[7]);
-  EXPECT_EQ(buf[1], a[8]);
-  EXPECT_EQ(buf[2], a[9]);
-  EXPECT_EQ(buf[3], a[3]);
-  EXPECT_EQ(c.copy(buf, 99), 3);
-  EXPECT_EQ(buf[0], c[0]);
-  EXPECT_EQ(buf[1], c[1]);
-  EXPECT_EQ(buf[2], c[2]);
-  EXPECT_EQ(buf[3], a[3]);
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW(a.copy(buf, 1, 27), std::out_of_range);
-#else
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(a.copy(buf, 1, 27), "absl::string_view::copy");
-#endif
-}
-
-// Separated from STL1() because some compilers produce an overly
-// large stack frame for the combined function.
-TEST(StringViewTest, STL2) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  absl::string_view d("foobar");
-  const absl::string_view e;
-  const absl::string_view f(
-      "123"
-      "\0"
-      "456",
-      7);
-
-  d = absl::string_view();
-  EXPECT_EQ(d.size(), 0);
-  EXPECT_TRUE(d.empty());
-  EXPECT_TRUE(d.data() == nullptr);
-  EXPECT_TRUE(d.begin() == d.end());
-
-  EXPECT_EQ(a.find(b), 0);
-  EXPECT_EQ(a.find(b, 1), absl::string_view::npos);
-  EXPECT_EQ(a.find(c), 23);
-  EXPECT_EQ(a.find(c, 9), 23);
-  EXPECT_EQ(a.find(c, absl::string_view::npos), absl::string_view::npos);
-  EXPECT_EQ(b.find(c), absl::string_view::npos);
-  EXPECT_EQ(b.find(c, absl::string_view::npos), absl::string_view::npos);
-  EXPECT_EQ(a.find(d), 0);
-  EXPECT_EQ(a.find(e), 0);
-  EXPECT_EQ(a.find(d, 12), 12);
-  EXPECT_EQ(a.find(e, 17), 17);
-  absl::string_view g("xx not found bb");
-  EXPECT_EQ(a.find(g), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(d.find(b), absl::string_view::npos);
-  EXPECT_EQ(e.find(b), absl::string_view::npos);
-  EXPECT_EQ(d.find(b, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find(b, 7), absl::string_view::npos);
-
-  size_t empty_search_pos = std::string().find(std::string());
-  EXPECT_EQ(d.find(d), empty_search_pos);
-  EXPECT_EQ(d.find(e), empty_search_pos);
-  EXPECT_EQ(e.find(d), empty_search_pos);
-  EXPECT_EQ(e.find(e), empty_search_pos);
-  EXPECT_EQ(d.find(d, 4), std::string().find(std::string(), 4));
-  EXPECT_EQ(d.find(e, 4), std::string().find(std::string(), 4));
-  EXPECT_EQ(e.find(d, 4), std::string().find(std::string(), 4));
-  EXPECT_EQ(e.find(e, 4), std::string().find(std::string(), 4));
-
-  EXPECT_EQ(a.find('a'), 0);
-  EXPECT_EQ(a.find('c'), 2);
-  EXPECT_EQ(a.find('z'), 25);
-  EXPECT_EQ(a.find('$'), absl::string_view::npos);
-  EXPECT_EQ(a.find('\0'), absl::string_view::npos);
-  EXPECT_EQ(f.find('\0'), 3);
-  EXPECT_EQ(f.find('3'), 2);
-  EXPECT_EQ(f.find('5'), 5);
-  EXPECT_EQ(g.find('o'), 4);
-  EXPECT_EQ(g.find('o', 4), 4);
-  EXPECT_EQ(g.find('o', 5), 8);
-  EXPECT_EQ(a.find('b', 5), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(d.find('\0'), absl::string_view::npos);
-  EXPECT_EQ(e.find('\0'), absl::string_view::npos);
-  EXPECT_EQ(d.find('\0', 4), absl::string_view::npos);
-  EXPECT_EQ(e.find('\0', 7), absl::string_view::npos);
-  EXPECT_EQ(d.find('x'), absl::string_view::npos);
-  EXPECT_EQ(e.find('x'), absl::string_view::npos);
-  EXPECT_EQ(d.find('x', 4), absl::string_view::npos);
-  EXPECT_EQ(e.find('x', 7), absl::string_view::npos);
-
-  EXPECT_EQ(a.rfind(b), 0);
-  EXPECT_EQ(a.rfind(b, 1), 0);
-  EXPECT_EQ(a.rfind(c), 23);
-  EXPECT_EQ(a.rfind(c, 22), absl::string_view::npos);
-  EXPECT_EQ(a.rfind(c, 1), absl::string_view::npos);
-  EXPECT_EQ(a.rfind(c, 0), absl::string_view::npos);
-  EXPECT_EQ(b.rfind(c), absl::string_view::npos);
-  EXPECT_EQ(b.rfind(c, 0), absl::string_view::npos);
-  EXPECT_EQ(a.rfind(d), std::string(a).rfind(std::string()));
-  EXPECT_EQ(a.rfind(e), std::string(a).rfind(std::string()));
-  EXPECT_EQ(a.rfind(d, 12), 12);
-  EXPECT_EQ(a.rfind(e, 17), 17);
-  EXPECT_EQ(a.rfind(g), absl::string_view::npos);
-  EXPECT_EQ(d.rfind(b), absl::string_view::npos);
-  EXPECT_EQ(e.rfind(b), absl::string_view::npos);
-  EXPECT_EQ(d.rfind(b, 4), absl::string_view::npos);
-  EXPECT_EQ(e.rfind(b, 7), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(d.rfind(d, 4), std::string().rfind(std::string()));
-  EXPECT_EQ(e.rfind(d, 7), std::string().rfind(std::string()));
-  EXPECT_EQ(d.rfind(e, 4), std::string().rfind(std::string()));
-  EXPECT_EQ(e.rfind(e, 7), std::string().rfind(std::string()));
-  EXPECT_EQ(d.rfind(d), std::string().rfind(std::string()));
-  EXPECT_EQ(e.rfind(d), std::string().rfind(std::string()));
-  EXPECT_EQ(d.rfind(e), std::string().rfind(std::string()));
-  EXPECT_EQ(e.rfind(e), std::string().rfind(std::string()));
-
-  EXPECT_EQ(g.rfind('o'), 8);
-  EXPECT_EQ(g.rfind('q'), absl::string_view::npos);
-  EXPECT_EQ(g.rfind('o', 8), 8);
-  EXPECT_EQ(g.rfind('o', 7), 4);
-  EXPECT_EQ(g.rfind('o', 3), absl::string_view::npos);
-  EXPECT_EQ(f.rfind('\0'), 3);
-  EXPECT_EQ(f.rfind('\0', 12), 3);
-  EXPECT_EQ(f.rfind('3'), 2);
-  EXPECT_EQ(f.rfind('5'), 5);
-  // empty string nonsense
-  EXPECT_EQ(d.rfind('o'), absl::string_view::npos);
-  EXPECT_EQ(e.rfind('o'), absl::string_view::npos);
-  EXPECT_EQ(d.rfind('o', 4), absl::string_view::npos);
-  EXPECT_EQ(e.rfind('o', 7), absl::string_view::npos);
-}
-
-// Continued from STL2
-TEST(StringViewTest, STL2FindFirst) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  absl::string_view d("foobar");
-  const absl::string_view e;
-  const absl::string_view f(
-      "123"
-      "\0"
-      "456",
-      7);
-  absl::string_view g("xx not found bb");
-
-  d = absl::string_view();
-  EXPECT_EQ(a.find_first_of(b), 0);
-  EXPECT_EQ(a.find_first_of(b, 0), 0);
-  EXPECT_EQ(a.find_first_of(b, 1), 1);
-  EXPECT_EQ(a.find_first_of(b, 2), 2);
-  EXPECT_EQ(a.find_first_of(b, 3), absl::string_view::npos);
-  EXPECT_EQ(a.find_first_of(c), 23);
-  EXPECT_EQ(a.find_first_of(c, 23), 23);
-  EXPECT_EQ(a.find_first_of(c, 24), 24);
-  EXPECT_EQ(a.find_first_of(c, 25), 25);
-  EXPECT_EQ(a.find_first_of(c, 26), absl::string_view::npos);
-  EXPECT_EQ(g.find_first_of(b), 13);
-  EXPECT_EQ(g.find_first_of(c), 0);
-  EXPECT_EQ(a.find_first_of(f), absl::string_view::npos);
-  EXPECT_EQ(f.find_first_of(a), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(a.find_first_of(d), absl::string_view::npos);
-  EXPECT_EQ(a.find_first_of(e), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_of(b), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_of(b), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_of(d), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_of(d), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_of(e), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_of(e), absl::string_view::npos);
-
-  EXPECT_EQ(a.find_first_not_of(b), 3);
-  EXPECT_EQ(a.find_first_not_of(c), 0);
-  EXPECT_EQ(b.find_first_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(c.find_first_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(f.find_first_not_of(a), 0);
-  EXPECT_EQ(a.find_first_not_of(f), 0);
-  EXPECT_EQ(a.find_first_not_of(d), 0);
-  EXPECT_EQ(a.find_first_not_of(e), 0);
-  // empty string nonsense
-  EXPECT_EQ(a.find_first_not_of(d), 0);
-  EXPECT_EQ(a.find_first_not_of(e), 0);
-  EXPECT_EQ(a.find_first_not_of(d, 1), 1);
-  EXPECT_EQ(a.find_first_not_of(e, 1), 1);
-  EXPECT_EQ(a.find_first_not_of(d, a.size() - 1), a.size() - 1);
-  EXPECT_EQ(a.find_first_not_of(e, a.size() - 1), a.size() - 1);
-  EXPECT_EQ(a.find_first_not_of(d, a.size()), absl::string_view::npos);
-  EXPECT_EQ(a.find_first_not_of(e, a.size()), absl::string_view::npos);
-  EXPECT_EQ(a.find_first_not_of(d, absl::string_view::npos),
-            absl::string_view::npos);
-  EXPECT_EQ(a.find_first_not_of(e, absl::string_view::npos),
-            absl::string_view::npos);
-  EXPECT_EQ(d.find_first_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_not_of(d), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of(d), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_not_of(e), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of(e), absl::string_view::npos);
-
-  absl::string_view h("====");
-  EXPECT_EQ(h.find_first_not_of('='), absl::string_view::npos);
-  EXPECT_EQ(h.find_first_not_of('=', 3), absl::string_view::npos);
-  EXPECT_EQ(h.find_first_not_of('\0'), 0);
-  EXPECT_EQ(g.find_first_not_of('x'), 2);
-  EXPECT_EQ(f.find_first_not_of('\0'), 0);
-  EXPECT_EQ(f.find_first_not_of('\0', 3), 4);
-  EXPECT_EQ(f.find_first_not_of('\0', 2), 2);
-  // empty string nonsense
-  EXPECT_EQ(d.find_first_not_of('x'), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of('x'), absl::string_view::npos);
-  EXPECT_EQ(d.find_first_not_of('\0'), absl::string_view::npos);
-  EXPECT_EQ(e.find_first_not_of('\0'), absl::string_view::npos);
-}
-
-// Continued from STL2
-TEST(StringViewTest, STL2FindLast) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  absl::string_view d("foobar");
-  const absl::string_view e;
-  const absl::string_view f(
-      "123"
-      "\0"
-      "456",
-      7);
-  absl::string_view g("xx not found bb");
-  absl::string_view h("====");
-  absl::string_view i("56");
-
-  d = absl::string_view();
-  EXPECT_EQ(h.find_last_of(a), absl::string_view::npos);
-  EXPECT_EQ(g.find_last_of(a), g.size()-1);
-  EXPECT_EQ(a.find_last_of(b), 2);
-  EXPECT_EQ(a.find_last_of(c), a.size()-1);
-  EXPECT_EQ(f.find_last_of(i), 6);
-  EXPECT_EQ(a.find_last_of('a'), 0);
-  EXPECT_EQ(a.find_last_of('b'), 1);
-  EXPECT_EQ(a.find_last_of('z'), 25);
-  EXPECT_EQ(a.find_last_of('a', 5), 0);
-  EXPECT_EQ(a.find_last_of('b', 5), 1);
-  EXPECT_EQ(a.find_last_of('b', 0), absl::string_view::npos);
-  EXPECT_EQ(a.find_last_of('z', 25), 25);
-  EXPECT_EQ(a.find_last_of('z', 24), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_of(i, 5), 5);
-  EXPECT_EQ(f.find_last_of(i, 6), 6);
-  EXPECT_EQ(f.find_last_of(a, 4), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(f.find_last_of(d), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_of(e), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(d), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(e), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(d), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(e), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(f), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(f), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_of(f, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_of(f, 4), absl::string_view::npos);
-
-  EXPECT_EQ(a.find_last_not_of(b), a.size()-1);
-  EXPECT_EQ(a.find_last_not_of(c), 22);
-  EXPECT_EQ(b.find_last_not_of(a), absl::string_view::npos);
-  EXPECT_EQ(b.find_last_not_of(b), absl::string_view::npos);
-  EXPECT_EQ(f.find_last_not_of(i), 4);
-  EXPECT_EQ(a.find_last_not_of(c, 24), 22);
-  EXPECT_EQ(a.find_last_not_of(b, 3), 3);
-  EXPECT_EQ(a.find_last_not_of(b, 2), absl::string_view::npos);
-  // empty string nonsense
-  EXPECT_EQ(f.find_last_not_of(d), f.size()-1);
-  EXPECT_EQ(f.find_last_not_of(e), f.size()-1);
-  EXPECT_EQ(f.find_last_not_of(d, 4), 4);
-  EXPECT_EQ(f.find_last_not_of(e, 4), 4);
-  EXPECT_EQ(d.find_last_not_of(d), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(e), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(d), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(e), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(f), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(f), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(d, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(e, 4), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of(f, 4), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of(f, 4), absl::string_view::npos);
-
-  EXPECT_EQ(h.find_last_not_of('x'), h.size() - 1);
-  EXPECT_EQ(h.find_last_not_of('='), absl::string_view::npos);
-  EXPECT_EQ(b.find_last_not_of('c'), 1);
-  EXPECT_EQ(h.find_last_not_of('x', 2), 2);
-  EXPECT_EQ(h.find_last_not_of('=', 2), absl::string_view::npos);
-  EXPECT_EQ(b.find_last_not_of('b', 1), 0);
-  // empty string nonsense
-  EXPECT_EQ(d.find_last_not_of('x'), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of('x'), absl::string_view::npos);
-  EXPECT_EQ(d.find_last_not_of('\0'), absl::string_view::npos);
-  EXPECT_EQ(e.find_last_not_of('\0'), absl::string_view::npos);
-}
-
-// Continued from STL2
-TEST(StringViewTest, STL2Substr) {
-  const absl::string_view a("abcdefghijklmnopqrstuvwxyz");
-  const absl::string_view b("abc");
-  const absl::string_view c("xyz");
-  absl::string_view d("foobar");
-  const absl::string_view e;
-
-  d = absl::string_view();
-  EXPECT_EQ(a.substr(0, 3), b);
-  EXPECT_EQ(a.substr(23), c);
-  EXPECT_EQ(a.substr(23, 3), c);
-  EXPECT_EQ(a.substr(23, 99), c);
-  EXPECT_EQ(a.substr(0), a);
-  EXPECT_EQ(a.substr(3, 2), "de");
-  // empty string nonsense
-  EXPECT_EQ(d.substr(0, 99), e);
-  // use of npos
-  EXPECT_EQ(a.substr(0, absl::string_view::npos), a);
-  EXPECT_EQ(a.substr(23, absl::string_view::npos), c);
-  // throw exception
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW((void)a.substr(99, 2), std::out_of_range);
-#else
-  ABSL_EXPECT_DEATH_IF_SUPPORTED((void)a.substr(99, 2),
-                                 "absl::string_view::substr");
-#endif
-}
-
-TEST(StringViewTest, TruncSubstr) {
-  const absl::string_view hi("hi");
-  EXPECT_EQ("", absl::ClippedSubstr(hi, 0, 0));
-  EXPECT_EQ("h", absl::ClippedSubstr(hi, 0, 1));
-  EXPECT_EQ("hi", absl::ClippedSubstr(hi, 0));
-  EXPECT_EQ("i", absl::ClippedSubstr(hi, 1));
-  EXPECT_EQ("", absl::ClippedSubstr(hi, 2));
-  EXPECT_EQ("", absl::ClippedSubstr(hi, 3));  // truncation
-  EXPECT_EQ("", absl::ClippedSubstr(hi, 3, 2));  // truncation
-}
-
-TEST(StringViewTest, UTF8) {
-  std::string utf8 = "\u00E1";
-  std::string utf8_twice = utf8 + " " + utf8;
-  int utf8_len = strlen(utf8.data());
-  EXPECT_EQ(utf8_len, absl::string_view(utf8_twice).find_first_of(" "));
-  EXPECT_EQ(utf8_len, absl::string_view(utf8_twice).find_first_of(" \t"));
-}
-
-TEST(StringViewTest, FindConformance) {
-  struct {
-    std::string haystack;
-    std::string needle;
-  } specs[] = {
-    {"", ""},
-    {"", "a"},
-    {"a", ""},
-    {"a", "a"},
-    {"a", "b"},
-    {"aa", ""},
-    {"aa", "a"},
-    {"aa", "b"},
-    {"ab", "a"},
-    {"ab", "b"},
-    {"abcd", ""},
-    {"abcd", "a"},
-    {"abcd", "d"},
-    {"abcd", "ab"},
-    {"abcd", "bc"},
-    {"abcd", "cd"},
-    {"abcd", "abcd"},
-  };
-  for (const auto& s : specs) {
-    SCOPED_TRACE(s.haystack);
-    SCOPED_TRACE(s.needle);
-    std::string st = s.haystack;
-    absl::string_view sp = s.haystack;
-    for (size_t i = 0; i <= sp.size(); ++i) {
-      size_t pos = (i == sp.size()) ? absl::string_view::npos : i;
-      SCOPED_TRACE(pos);
-      EXPECT_EQ(sp.find(s.needle, pos),
-                st.find(s.needle, pos));
-      EXPECT_EQ(sp.rfind(s.needle, pos),
-                st.rfind(s.needle, pos));
-      EXPECT_EQ(sp.find_first_of(s.needle, pos),
-                st.find_first_of(s.needle, pos));
-      EXPECT_EQ(sp.find_first_not_of(s.needle, pos),
-                st.find_first_not_of(s.needle, pos));
-      EXPECT_EQ(sp.find_last_of(s.needle, pos),
-                st.find_last_of(s.needle, pos));
-      EXPECT_EQ(sp.find_last_not_of(s.needle, pos),
-                st.find_last_not_of(s.needle, pos));
-    }
-  }
-}
-
-TEST(StringViewTest, Remove) {
-  absl::string_view a("foobar");
-  std::string s1("123");
-  s1 += '\0';
-  s1 += "456";
-  absl::string_view e;
-  std::string s2;
-
-  // remove_prefix
-  absl::string_view c(a);
-  c.remove_prefix(3);
-  EXPECT_EQ(c, "bar");
-  c = a;
-  c.remove_prefix(0);
-  EXPECT_EQ(c, a);
-  c.remove_prefix(c.size());
-  EXPECT_EQ(c, e);
-
-  // remove_suffix
-  c = a;
-  c.remove_suffix(3);
-  EXPECT_EQ(c, "foo");
-  c = a;
-  c.remove_suffix(0);
-  EXPECT_EQ(c, a);
-  c.remove_suffix(c.size());
-  EXPECT_EQ(c, e);
-}
-
-TEST(StringViewTest, Set) {
-  absl::string_view a("foobar");
-  absl::string_view empty;
-  absl::string_view b;
-
-  // set
-  b = absl::string_view("foobar", 6);
-  EXPECT_EQ(b, a);
-  b = absl::string_view("foobar", 0);
-  EXPECT_EQ(b, empty);
-  b = absl::string_view("foobar", 7);
-  EXPECT_NE(b, a);
-
-  b = absl::string_view("foobar");
-  EXPECT_EQ(b, a);
-}
-
-TEST(StringViewTest, FrontBack) {
-  static const char arr[] = "abcd";
-  const absl::string_view csp(arr, 4);
-  EXPECT_EQ(&arr[0], &csp.front());
-  EXPECT_EQ(&arr[3], &csp.back());
-}
-
-TEST(StringViewTest, FrontBackSingleChar) {
-  static const char c = 'a';
-  const absl::string_view csp(&c, 1);
-  EXPECT_EQ(&c, &csp.front());
-  EXPECT_EQ(&c, &csp.back());
-}
-
-TEST(StringViewTest, FrontBackEmpty) {
-#ifndef ABSL_USES_STD_STRING_VIEW
-#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
-  // Abseil's string_view implementation has debug assertions that check that
-  // front() and back() are not called on an empty string_view.
-  absl::string_view sv;
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(sv.front(), "");
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(sv.back(), "");
-#endif
-#endif
-}
-
-// `std::string_view::string_view(const char*)` calls
-// `std::char_traits<char>::length(const char*)` to get the string length. In
-// libc++, it doesn't allow `nullptr` in the constexpr context, with the error
-// "read of dereferenced null pointer is not allowed in a constant expression".
-// At run time, the behavior of `std::char_traits::length()` on `nullptr` is
-// undefined by the standard and usually results in crash with libc++.
-// GCC also started rejected this in libstdc++ starting in GCC9.
-// In MSVC, creating a constexpr string_view from nullptr also triggers an
-// "unevaluable pointer value" error. This compiler implementation conforms
-// to the standard, but `absl::string_view` implements a different
-// behavior for historical reasons. We work around tests that construct
-// `string_view` from `nullptr` when using libc++.
-#if !defined(ABSL_USES_STD_STRING_VIEW) ||                    \
-    (!(defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 9) && \
-     !defined(_LIBCPP_VERSION) && !defined(_MSC_VER))
-#define ABSL_HAVE_STRING_VIEW_FROM_NULLPTR 1
-#endif
-
-TEST(StringViewTest, NULLInput) {
-  absl::string_view s;
-  EXPECT_EQ(s.data(), nullptr);
-  EXPECT_EQ(s.size(), 0);
-
-#ifdef ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
-  s = absl::string_view(nullptr);
-  EXPECT_EQ(s.data(), nullptr);
-  EXPECT_EQ(s.size(), 0);
-
-  // .ToString() on a absl::string_view with nullptr should produce the empty
-  // string.
-  EXPECT_EQ("", std::string(s));
-#endif  // ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
-}
-
-TEST(StringViewTest, Comparisons2) {
-  // The `compare` member has 6 overloads (v: string_view, s: const char*):
-  //  (1) compare(v)
-  //  (2) compare(pos1, count1, v)
-  //  (3) compare(pos1, count1, v, pos2, count2)
-  //  (4) compare(s)
-  //  (5) compare(pos1, count1, s)
-  //  (6) compare(pos1, count1, s, count2)
-
-  absl::string_view abc("abcdefghijklmnopqrstuvwxyz");
-
-  // check comparison operations on strings longer than 4 bytes.
-  EXPECT_EQ(abc, absl::string_view("abcdefghijklmnopqrstuvwxyz"));
-  EXPECT_EQ(abc.compare(absl::string_view("abcdefghijklmnopqrstuvwxyz")), 0);
-
-  EXPECT_LT(abc, absl::string_view("abcdefghijklmnopqrstuvwxzz"));
-  EXPECT_LT(abc.compare(absl::string_view("abcdefghijklmnopqrstuvwxzz")), 0);
-
-  EXPECT_GT(abc, absl::string_view("abcdefghijklmnopqrstuvwxyy"));
-  EXPECT_GT(abc.compare(absl::string_view("abcdefghijklmnopqrstuvwxyy")), 0);
-
-  // The "substr" variants of `compare`.
-  absl::string_view digits("0123456789");
-  auto npos = absl::string_view::npos;
-
-  // Taking string_view
-  EXPECT_EQ(digits.compare(3, npos, absl::string_view("3456789")), 0);  // 2
-  EXPECT_EQ(digits.compare(3, 4, absl::string_view("3456")), 0);        // 2
-  EXPECT_EQ(digits.compare(10, 0, absl::string_view()), 0);             // 2
-  EXPECT_EQ(digits.compare(3, 4, absl::string_view("0123456789"), 3, 4),
-            0);  // 3
-  EXPECT_LT(digits.compare(3, 4, absl::string_view("0123456789"), 3, 5),
-            0);  // 3
-  EXPECT_LT(digits.compare(0, npos, absl::string_view("0123456789"), 3, 5),
-            0);  // 3
-  // Taking const char*
-  EXPECT_EQ(digits.compare(3, 4, "3456"), 0);                 // 5
-  EXPECT_EQ(digits.compare(3, npos, "3456789"), 0);           // 5
-  EXPECT_EQ(digits.compare(10, 0, ""), 0);                    // 5
-  EXPECT_EQ(digits.compare(3, 4, "0123456789", 3, 4), 0);     // 6
-  EXPECT_LT(digits.compare(3, 4, "0123456789", 3, 5), 0);     // 6
-  EXPECT_LT(digits.compare(0, npos, "0123456789", 3, 5), 0);  // 6
-}
-
-TEST(StringViewTest, At) {
-  absl::string_view abc = "abc";
-  EXPECT_EQ(abc.at(0), 'a');
-  EXPECT_EQ(abc.at(1), 'b');
-  EXPECT_EQ(abc.at(2), 'c');
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW(abc.at(3), std::out_of_range);
-#else
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(abc.at(3), "absl::string_view::at");
-#endif
-}
-
-struct MyCharAlloc : std::allocator<char> {};
-
-TEST(StringViewTest, ExplicitConversionOperator) {
-  absl::string_view sp = "hi";
-  EXPECT_EQ(sp, std::string(sp));
-}
-
-TEST(StringViewTest, NullSafeStringView) {
-  {
-    absl::string_view s = absl::NullSafeStringView(nullptr);
-    EXPECT_EQ(nullptr, s.data());
-    EXPECT_EQ(0, s.size());
-    EXPECT_EQ(absl::string_view(), s);
-  }
-  {
-    static const char kHi[] = "hi";
-    absl::string_view s = absl::NullSafeStringView(kHi);
-    EXPECT_EQ(kHi, s.data());
-    EXPECT_EQ(strlen(kHi), s.size());
-    EXPECT_EQ(absl::string_view("hi"), s);
-  }
-}
-
-TEST(StringViewTest, ConstexprNullSafeStringView) {
-  {
-    constexpr absl::string_view s = absl::NullSafeStringView(nullptr);
-    EXPECT_EQ(nullptr, s.data());
-    EXPECT_EQ(0, s.size());
-    EXPECT_EQ(absl::string_view(), s);
-  }
-#if !defined(_MSC_VER) || _MSC_VER >= 1910
-  // MSVC 2017+ is required for good constexpr string_view support.
-  // See the implementation of `absl::string_view::StrlenInternal()`.
-  {
-    static constexpr char kHi[] = "hi";
-    absl::string_view s = absl::NullSafeStringView(kHi);
-    EXPECT_EQ(kHi, s.data());
-    EXPECT_EQ(strlen(kHi), s.size());
-    EXPECT_EQ(absl::string_view("hi"), s);
-  }
-  {
-    constexpr absl::string_view s = absl::NullSafeStringView("hello");
-    EXPECT_EQ(s.size(), 5);
-    EXPECT_EQ("hello", s);
-  }
-#endif
-}
-
-TEST(StringViewTest, ConstexprCompiles) {
-  constexpr absl::string_view sp;
-#ifdef ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
-  constexpr absl::string_view cstr(nullptr);
-#endif
-  constexpr absl::string_view cstr_len("cstr", 4);
-
-#if defined(ABSL_USES_STD_STRING_VIEW)
-  // In libstdc++ (as of 7.2), `std::string_view::string_view(const char*)`
-  // calls `std::char_traits<char>::length(const char*)` to get the string
-  // length, but it is not marked constexpr yet. See GCC bug:
-  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78156
-  // Also, there is a LWG issue that adds constexpr to length() which was just
-  // resolved 2017-06-02. See
-  // http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2232
-  // TODO(zhangxy): Update the condition when libstdc++ adopts the constexpr
-  // length().
-#if !defined(__GLIBCXX__)
-#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1
-#endif  // !__GLIBCXX__
-
-#else  // ABSL_USES_STD_STRING_VIEW
-
-// This duplicates the check for __builtin_strlen in the header.
-#if ABSL_HAVE_BUILTIN(__builtin_strlen) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1
-#elif defined(__GNUC__)  // GCC or clang
-#error GCC/clang should have constexpr string_view.
-#endif
-
-// MSVC 2017+ should be able to construct a constexpr string_view from a cstr.
-#if defined(_MSC_VER) && _MSC_VER >= 1910
-#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1
-#endif
-
-#endif  // ABSL_USES_STD_STRING_VIEW
-
-#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR
-  constexpr absl::string_view cstr_strlen("foo");
-  EXPECT_EQ(cstr_strlen.length(), 3);
-  constexpr absl::string_view cstr_strlen2 = "bar";
-  EXPECT_EQ(cstr_strlen2, "bar");
-
-#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON 1
-#endif
-#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON
-  constexpr absl::string_view foo = "foo";
-  constexpr absl::string_view bar = "bar";
-  constexpr bool foo_eq_bar = foo == bar;
-  constexpr bool foo_ne_bar = foo != bar;
-  constexpr bool foo_lt_bar = foo < bar;
-  constexpr bool foo_le_bar = foo <= bar;
-  constexpr bool foo_gt_bar = foo > bar;
-  constexpr bool foo_ge_bar = foo >= bar;
-  constexpr int foo_compare_bar = foo.compare(bar);
-  EXPECT_FALSE(foo_eq_bar);
-  EXPECT_TRUE(foo_ne_bar);
-  EXPECT_FALSE(foo_lt_bar);
-  EXPECT_FALSE(foo_le_bar);
-  EXPECT_TRUE(foo_gt_bar);
-  EXPECT_TRUE(foo_ge_bar);
-  EXPECT_GT(foo_compare_bar, 0);
-#endif
-#endif
-
-#if !defined(__clang__) || 3 < __clang_major__ || \
-  (3 == __clang_major__ && 4 < __clang_minor__)
-  // older clang versions (< 3.5) complain that:
-  //   "cannot perform pointer arithmetic on null pointer"
-  constexpr absl::string_view::iterator const_begin_empty = sp.begin();
-  constexpr absl::string_view::iterator const_end_empty = sp.end();
-  EXPECT_EQ(const_begin_empty, const_end_empty);
-
-#ifdef ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
-  constexpr absl::string_view::iterator const_begin_nullptr = cstr.begin();
-  constexpr absl::string_view::iterator const_end_nullptr = cstr.end();
-  EXPECT_EQ(const_begin_nullptr, const_end_nullptr);
-#endif  // ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
-#endif  // !defined(__clang__) || ...
-
-  constexpr absl::string_view::iterator const_begin = cstr_len.begin();
-  constexpr absl::string_view::iterator const_end = cstr_len.end();
-  constexpr absl::string_view::size_type const_size = cstr_len.size();
-  constexpr absl::string_view::size_type const_length = cstr_len.length();
-  static_assert(const_begin + const_size == const_end,
-                "pointer arithmetic check");
-  static_assert(const_begin + const_length == const_end,
-                "pointer arithmetic check");
-#ifndef _MSC_VER
-  // MSVC has bugs doing constexpr pointer arithmetic.
-  // https://developercommunity.visualstudio.com/content/problem/482192/bad-pointer-arithmetic-in-constepxr-2019-rc1-svc1.html
-  EXPECT_EQ(const_begin + const_size, const_end);
-  EXPECT_EQ(const_begin + const_length, const_end);
-#endif
-
-  constexpr bool isempty = sp.empty();
-  EXPECT_TRUE(isempty);
-
-  constexpr const char c = cstr_len[2];
-  EXPECT_EQ(c, 't');
-
-  constexpr const char cfront = cstr_len.front();
-  constexpr const char cback = cstr_len.back();
-  EXPECT_EQ(cfront, 'c');
-  EXPECT_EQ(cback, 'r');
-
-  constexpr const char* np = sp.data();
-  constexpr const char* cstr_ptr = cstr_len.data();
-  EXPECT_EQ(np, nullptr);
-  EXPECT_NE(cstr_ptr, nullptr);
-
-  constexpr size_t sp_npos = sp.npos;
-  EXPECT_EQ(sp_npos, -1);
-}
-
-TEST(StringViewTest, ConstexprSubstr) {
-  constexpr absl::string_view foobar("foobar", 6);
-  constexpr absl::string_view foo = foobar.substr(0, 3);
-  constexpr absl::string_view bar = foobar.substr(3);
-  EXPECT_EQ(foo, "foo");
-  EXPECT_EQ(bar, "bar");
-}
-
-TEST(StringViewTest, Noexcept) {
-  EXPECT_TRUE((std::is_nothrow_constructible<absl::string_view,
-                                             const std::string&>::value));
-  EXPECT_TRUE((std::is_nothrow_constructible<absl::string_view,
-                                             const std::string&>::value));
-  EXPECT_TRUE(std::is_nothrow_constructible<absl::string_view>::value);
-  constexpr absl::string_view sp;
-  EXPECT_TRUE(noexcept(sp.begin()));
-  EXPECT_TRUE(noexcept(sp.end()));
-  EXPECT_TRUE(noexcept(sp.cbegin()));
-  EXPECT_TRUE(noexcept(sp.cend()));
-  EXPECT_TRUE(noexcept(sp.rbegin()));
-  EXPECT_TRUE(noexcept(sp.rend()));
-  EXPECT_TRUE(noexcept(sp.crbegin()));
-  EXPECT_TRUE(noexcept(sp.crend()));
-  EXPECT_TRUE(noexcept(sp.size()));
-  EXPECT_TRUE(noexcept(sp.length()));
-  EXPECT_TRUE(noexcept(sp.empty()));
-  EXPECT_TRUE(noexcept(sp.data()));
-  EXPECT_TRUE(noexcept(sp.compare(sp)));
-  EXPECT_TRUE(noexcept(sp.find(sp)));
-  EXPECT_TRUE(noexcept(sp.find('f')));
-  EXPECT_TRUE(noexcept(sp.rfind(sp)));
-  EXPECT_TRUE(noexcept(sp.rfind('f')));
-  EXPECT_TRUE(noexcept(sp.find_first_of(sp)));
-  EXPECT_TRUE(noexcept(sp.find_first_of('f')));
-  EXPECT_TRUE(noexcept(sp.find_last_of(sp)));
-  EXPECT_TRUE(noexcept(sp.find_last_of('f')));
-  EXPECT_TRUE(noexcept(sp.find_first_not_of(sp)));
-  EXPECT_TRUE(noexcept(sp.find_first_not_of('f')));
-  EXPECT_TRUE(noexcept(sp.find_last_not_of(sp)));
-  EXPECT_TRUE(noexcept(sp.find_last_not_of('f')));
-}
-
-TEST(StringViewTest, BoundsCheck) {
-#ifndef ABSL_USES_STD_STRING_VIEW
-#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
-  // Abseil's string_view implementation has bounds-checking in debug mode.
-  absl::string_view h = "hello";
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(h[5], "");
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(h[-1], "");
-#endif
-#endif
-}
-
-TEST(ComparisonOpsTest, StringCompareNotAmbiguous) {
-  EXPECT_EQ("hello", std::string("hello"));
-  EXPECT_LT("hello", std::string("world"));
-}
-
-TEST(ComparisonOpsTest, HeterogenousStringViewEquals) {
-  EXPECT_EQ(absl::string_view("hello"), std::string("hello"));
-  EXPECT_EQ("hello", absl::string_view("hello"));
-}
-
-TEST(FindOneCharTest, EdgeCases) {
-  absl::string_view a("xxyyyxx");
-
-  // Set a = "xyyyx".
-  a.remove_prefix(1);
-  a.remove_suffix(1);
-
-  EXPECT_EQ(0, a.find('x'));
-  EXPECT_EQ(0, a.find('x', 0));
-  EXPECT_EQ(4, a.find('x', 1));
-  EXPECT_EQ(4, a.find('x', 4));
-  EXPECT_EQ(absl::string_view::npos, a.find('x', 5));
-
-  EXPECT_EQ(4, a.rfind('x'));
-  EXPECT_EQ(4, a.rfind('x', 5));
-  EXPECT_EQ(4, a.rfind('x', 4));
-  EXPECT_EQ(0, a.rfind('x', 3));
-  EXPECT_EQ(0, a.rfind('x', 0));
-
-  // Set a = "yyy".
-  a.remove_prefix(1);
-  a.remove_suffix(1);
-
-  EXPECT_EQ(absl::string_view::npos, a.find('x'));
-  EXPECT_EQ(absl::string_view::npos, a.rfind('x'));
-}
-
-#ifndef ABSL_HAVE_THREAD_SANITIZER  // Allocates too much memory for tsan.
-TEST(HugeStringView, TwoPointTwoGB) {
-  if (sizeof(size_t) <= 4)
-    return;
-  // Try a huge string piece.
-  const size_t size = size_t{2200} * 1000 * 1000;
-  std::string s(size, 'a');
-  absl::string_view sp(s);
-  EXPECT_EQ(size, sp.length());
-  sp.remove_prefix(1);
-  EXPECT_EQ(size - 1, sp.length());
-  sp.remove_suffix(2);
-  EXPECT_EQ(size - 1 - 2, sp.length());
-}
-#endif  // ABSL_HAVE_THREAD_SANITIZER
-
-#if !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW)
-TEST(NonNegativeLenTest, NonNegativeLen) {
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(absl::string_view("xyz", -1),
-                                 "len <= kMaxSize");
-}
-
-TEST(LenExceedsMaxSizeTest, LenExceedsMaxSize) {
-  auto max_size = absl::string_view().max_size();
-
-  // This should construct ok (although the view itself is obviously invalid).
-  absl::string_view ok_view("", max_size);
-
-  // Adding one to the max should trigger an assertion.
-  ABSL_EXPECT_DEATH_IF_SUPPORTED(absl::string_view("", max_size + 1),
-                                 "len <= kMaxSize");
-}
-#endif  // !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW)
-
-class StringViewStreamTest : public ::testing::Test {
- public:
-  // Set negative 'width' for right justification.
-  template <typename T>
-  std::string Pad(const T& s, int width, char fill = 0) {
-    std::ostringstream oss;
-    if (fill != 0) {
-      oss << std::setfill(fill);
-    }
-    if (width < 0) {
-      width = -width;
-      oss << std::right;
-    }
-    oss << std::setw(width) << s;
-    return oss.str();
-  }
-};
-
-TEST_F(StringViewStreamTest, Padding) {
-  std::string s("hello");
-  absl::string_view sp(s);
-  for (int w = -64; w < 64; ++w) {
-    SCOPED_TRACE(w);
-    EXPECT_EQ(Pad(s, w), Pad(sp, w));
-  }
-  for (int w = -64; w < 64; ++w) {
-    SCOPED_TRACE(w);
-    EXPECT_EQ(Pad(s, w, '#'), Pad(sp, w, '#'));
-  }
-}
-
-TEST_F(StringViewStreamTest, ResetsWidth) {
-  // Width should reset after one formatted write.
-  // If we weren't resetting width after formatting the string_view,
-  // we'd have width=5 carrying over to the printing of the "]",
-  // creating "[###hi####]".
-  std::string s = "hi";
-  absl::string_view sp = s;
-  {
-    std::ostringstream oss;
-    oss << "[" << std::setfill('#') << std::setw(5) << s << "]";
-    ASSERT_EQ("[###hi]", oss.str());
-  }
-  {
-    std::ostringstream oss;
-    oss << "[" << std::setfill('#') << std::setw(5) << sp << "]";
-    EXPECT_EQ("[###hi]", oss.str());
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/strip.h b/third_party/abseil_cpp/absl/strings/strip.h
deleted file mode 100644
index 111872ca54..0000000000
--- a/third_party/abseil_cpp/absl/strings/strip.h
+++ /dev/null
@@ -1,91 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: strip.h
-// -----------------------------------------------------------------------------
-//
-// This file contains various functions for stripping substrings from a string.
-#ifndef ABSL_STRINGS_STRIP_H_
-#define ABSL_STRINGS_STRIP_H_
-
-#include <cstddef>
-#include <string>
-
-#include "absl/base/macros.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/match.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// ConsumePrefix()
-//
-// Strips the `expected` prefix from the start of the given string, returning
-// `true` if the strip operation succeeded or false otherwise.
-//
-// Example:
-//
-//   absl::string_view input("abc");
-//   EXPECT_TRUE(absl::ConsumePrefix(&input, "a"));
-//   EXPECT_EQ(input, "bc");
-inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) {
-  if (!absl::StartsWith(*str, expected)) return false;
-  str->remove_prefix(expected.size());
-  return true;
-}
-// ConsumeSuffix()
-//
-// Strips the `expected` suffix from the end of the given string, returning
-// `true` if the strip operation succeeded or false otherwise.
-//
-// Example:
-//
-//   absl::string_view input("abcdef");
-//   EXPECT_TRUE(absl::ConsumeSuffix(&input, "def"));
-//   EXPECT_EQ(input, "abc");
-inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) {
-  if (!absl::EndsWith(*str, expected)) return false;
-  str->remove_suffix(expected.size());
-  return true;
-}
-
-// StripPrefix()
-//
-// Returns a view into the input string 'str' with the given 'prefix' removed,
-// but leaving the original string intact. If the prefix does not match at the
-// start of the string, returns the original string instead.
-ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix(
-    absl::string_view str, absl::string_view prefix) {
-  if (absl::StartsWith(str, prefix)) str.remove_prefix(prefix.size());
-  return str;
-}
-
-// StripSuffix()
-//
-// Returns a view into the input string 'str' with the given 'suffix' removed,
-// but leaving the original string intact. If the suffix does not match at the
-// end of the string, returns the original string instead.
-ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix(
-    absl::string_view str, absl::string_view suffix) {
-  if (absl::EndsWith(str, suffix)) str.remove_suffix(suffix.size());
-  return str;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_STRIP_H_
diff --git a/third_party/abseil_cpp/absl/strings/strip_test.cc b/third_party/abseil_cpp/absl/strings/strip_test.cc
deleted file mode 100644
index e4e00cb66e..0000000000
--- a/third_party/abseil_cpp/absl/strings/strip_test.cc
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file contains functions that remove a defined part from the string,
-// i.e., strip the string.
-
-#include "absl/strings/strip.h"
-
-#include <cassert>
-#include <cstdio>
-#include <cstring>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/string_view.h"
-
-namespace {
-
-TEST(Strip, ConsumePrefixOneChar) {
-  absl::string_view input("abc");
-  EXPECT_TRUE(absl::ConsumePrefix(&input, "a"));
-  EXPECT_EQ(input, "bc");
-
-  EXPECT_FALSE(absl::ConsumePrefix(&input, "x"));
-  EXPECT_EQ(input, "bc");
-
-  EXPECT_TRUE(absl::ConsumePrefix(&input, "b"));
-  EXPECT_EQ(input, "c");
-
-  EXPECT_TRUE(absl::ConsumePrefix(&input, "c"));
-  EXPECT_EQ(input, "");
-
-  EXPECT_FALSE(absl::ConsumePrefix(&input, "a"));
-  EXPECT_EQ(input, "");
-}
-
-TEST(Strip, ConsumePrefix) {
-  absl::string_view input("abcdef");
-  EXPECT_FALSE(absl::ConsumePrefix(&input, "abcdefg"));
-  EXPECT_EQ(input, "abcdef");
-
-  EXPECT_FALSE(absl::ConsumePrefix(&input, "abce"));
-  EXPECT_EQ(input, "abcdef");
-
-  EXPECT_TRUE(absl::ConsumePrefix(&input, ""));
-  EXPECT_EQ(input, "abcdef");
-
-  EXPECT_FALSE(absl::ConsumePrefix(&input, "abcdeg"));
-  EXPECT_EQ(input, "abcdef");
-
-  EXPECT_TRUE(absl::ConsumePrefix(&input, "abcdef"));
-  EXPECT_EQ(input, "");
-
-  input = "abcdef";
-  EXPECT_TRUE(absl::ConsumePrefix(&input, "abcde"));
-  EXPECT_EQ(input, "f");
-}
-
-TEST(Strip, ConsumeSuffix) {
-  absl::string_view input("abcdef");
-  EXPECT_FALSE(absl::ConsumeSuffix(&input, "abcdefg"));
-  EXPECT_EQ(input, "abcdef");
-
-  EXPECT_TRUE(absl::ConsumeSuffix(&input, ""));
-  EXPECT_EQ(input, "abcdef");
-
-  EXPECT_TRUE(absl::ConsumeSuffix(&input, "def"));
-  EXPECT_EQ(input, "abc");
-
-  input = "abcdef";
-  EXPECT_FALSE(absl::ConsumeSuffix(&input, "abcdeg"));
-  EXPECT_EQ(input, "abcdef");
-
-  EXPECT_TRUE(absl::ConsumeSuffix(&input, "f"));
-  EXPECT_EQ(input, "abcde");
-
-  EXPECT_TRUE(absl::ConsumeSuffix(&input, "abcde"));
-  EXPECT_EQ(input, "");
-}
-
-TEST(Strip, StripPrefix) {
-  const absl::string_view null_str;
-
-  EXPECT_EQ(absl::StripPrefix("foobar", "foo"), "bar");
-  EXPECT_EQ(absl::StripPrefix("foobar", ""), "foobar");
-  EXPECT_EQ(absl::StripPrefix("foobar", null_str), "foobar");
-  EXPECT_EQ(absl::StripPrefix("foobar", "foobar"), "");
-  EXPECT_EQ(absl::StripPrefix("foobar", "bar"), "foobar");
-  EXPECT_EQ(absl::StripPrefix("foobar", "foobarr"), "foobar");
-  EXPECT_EQ(absl::StripPrefix("", ""), "");
-}
-
-TEST(Strip, StripSuffix) {
-  const absl::string_view null_str;
-
-  EXPECT_EQ(absl::StripSuffix("foobar", "bar"), "foo");
-  EXPECT_EQ(absl::StripSuffix("foobar", ""), "foobar");
-  EXPECT_EQ(absl::StripSuffix("foobar", null_str), "foobar");
-  EXPECT_EQ(absl::StripSuffix("foobar", "foobar"), "");
-  EXPECT_EQ(absl::StripSuffix("foobar", "foo"), "foobar");
-  EXPECT_EQ(absl::StripSuffix("foobar", "ffoobar"), "foobar");
-  EXPECT_EQ(absl::StripSuffix("", ""), "");
-}
-
-TEST(Strip, RemoveExtraAsciiWhitespace) {
-  const char* inputs[] = {
-      "No extra space",
-      "  Leading whitespace",
-      "Trailing whitespace  ",
-      "  Leading and trailing  ",
-      " Whitespace \t  in\v   middle  ",
-      "'Eeeeep!  \n Newlines!\n",
-      "nospaces",
-  };
-  const char* outputs[] = {
-      "No extra space",
-      "Leading whitespace",
-      "Trailing whitespace",
-      "Leading and trailing",
-      "Whitespace in middle",
-      "'Eeeeep! Newlines!",
-      "nospaces",
-  };
-  int NUM_TESTS = 7;
-
-  for (int i = 0; i < NUM_TESTS; i++) {
-    std::string s(inputs[i]);
-    absl::RemoveExtraAsciiWhitespace(&s);
-    EXPECT_STREQ(outputs[i], s.c_str());
-  }
-
-  // Test that absl::RemoveExtraAsciiWhitespace returns immediately for empty
-  // strings (It was adding the \0 character to the C++ std::string, which broke
-  // tests involving empty())
-  std::string zero_string = "";
-  assert(zero_string.empty());
-  absl::RemoveExtraAsciiWhitespace(&zero_string);
-  EXPECT_EQ(zero_string.size(), 0);
-  EXPECT_TRUE(zero_string.empty());
-}
-
-TEST(Strip, StripTrailingAsciiWhitespace) {
-  std::string test = "foo  ";
-  absl::StripTrailingAsciiWhitespace(&test);
-  EXPECT_EQ(test, "foo");
-
-  test = "   ";
-  absl::StripTrailingAsciiWhitespace(&test);
-  EXPECT_EQ(test, "");
-
-  test = "";
-  absl::StripTrailingAsciiWhitespace(&test);
-  EXPECT_EQ(test, "");
-
-  test = " abc\t";
-  absl::StripTrailingAsciiWhitespace(&test);
-  EXPECT_EQ(test, " abc");
-}
-
-TEST(String, StripLeadingAsciiWhitespace) {
-  absl::string_view orig = "\t  \n\f\r\n\vfoo";
-  EXPECT_EQ("foo", absl::StripLeadingAsciiWhitespace(orig));
-  orig = "\t  \n\f\r\v\n\t  \n\f\r\v\n";
-  EXPECT_EQ(absl::string_view(), absl::StripLeadingAsciiWhitespace(orig));
-}
-
-TEST(Strip, StripAsciiWhitespace) {
-  std::string test2 = "\t  \f\r\n\vfoo \t\f\r\v\n";
-  absl::StripAsciiWhitespace(&test2);
-  EXPECT_EQ(test2, "foo");
-  std::string test3 = "bar";
-  absl::StripAsciiWhitespace(&test3);
-  EXPECT_EQ(test3, "bar");
-  std::string test4 = "\t  \f\r\n\vfoo";
-  absl::StripAsciiWhitespace(&test4);
-  EXPECT_EQ(test4, "foo");
-  std::string test5 = "foo \t\f\r\v\n";
-  absl::StripAsciiWhitespace(&test5);
-  EXPECT_EQ(test5, "foo");
-  absl::string_view test6("\t  \f\r\n\vfoo \t\f\r\v\n");
-  test6 = absl::StripAsciiWhitespace(test6);
-  EXPECT_EQ(test6, "foo");
-  test6 = absl::StripAsciiWhitespace(test6);
-  EXPECT_EQ(test6, "foo");  // already stripped
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/strings/substitute.cc b/third_party/abseil_cpp/absl/strings/substitute.cc
deleted file mode 100644
index 1f3c7409ab..0000000000
--- a/third_party/abseil_cpp/absl/strings/substitute.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/substitute.h"
-
-#include <algorithm>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/escaping.h"
-#include "absl/strings/internal/resize_uninitialized.h"
-#include "absl/strings/string_view.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace substitute_internal {
-
-void SubstituteAndAppendArray(std::string* output, absl::string_view format,
-                              const absl::string_view* args_array,
-                              size_t num_args) {
-  // Determine total size needed.
-  size_t size = 0;
-  for (size_t i = 0; i < format.size(); i++) {
-    if (format[i] == '$') {
-      if (i + 1 >= format.size()) {
-#ifndef NDEBUG
-        ABSL_RAW_LOG(FATAL,
-                     "Invalid absl::Substitute() format string: \"%s\".",
-                     absl::CEscape(format).c_str());
-#endif
-        return;
-      } else if (absl::ascii_isdigit(format[i + 1])) {
-        int index = format[i + 1] - '0';
-        if (static_cast<size_t>(index) >= num_args) {
-#ifndef NDEBUG
-          ABSL_RAW_LOG(
-              FATAL,
-              "Invalid absl::Substitute() format string: asked for \"$"
-              "%d\", but only %d args were given.  Full format string was: "
-              "\"%s\".",
-              index, static_cast<int>(num_args), absl::CEscape(format).c_str());
-#endif
-          return;
-        }
-        size += args_array[index].size();
-        ++i;  // Skip next char.
-      } else if (format[i + 1] == '$') {
-        ++size;
-        ++i;  // Skip next char.
-      } else {
-#ifndef NDEBUG
-        ABSL_RAW_LOG(FATAL,
-                     "Invalid absl::Substitute() format string: \"%s\".",
-                     absl::CEscape(format).c_str());
-#endif
-        return;
-      }
-    } else {
-      ++size;
-    }
-  }
-
-  if (size == 0) return;
-
-  // Build the string.
-  size_t original_size = output->size();
-  strings_internal::STLStringResizeUninitialized(output, original_size + size);
-  char* target = &(*output)[original_size];
-  for (size_t i = 0; i < format.size(); i++) {
-    if (format[i] == '$') {
-      if (absl::ascii_isdigit(format[i + 1])) {
-        const absl::string_view src = args_array[format[i + 1] - '0'];
-        target = std::copy(src.begin(), src.end(), target);
-        ++i;  // Skip next char.
-      } else if (format[i + 1] == '$') {
-        *target++ = '$';
-        ++i;  // Skip next char.
-      }
-    } else {
-      *target++ = format[i];
-    }
-  }
-
-  assert(target == output->data() + output->size());
-}
-
-Arg::Arg(const void* value) {
-  static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2,
-                "fix sizeof(scratch_)");
-  if (value == nullptr) {
-    piece_ = "NULL";
-  } else {
-    char* ptr = scratch_ + sizeof(scratch_);
-    uintptr_t num = reinterpret_cast<uintptr_t>(value);
-    do {
-      *--ptr = absl::numbers_internal::kHexChar[num & 0xf];
-      num >>= 4;
-    } while (num != 0);
-    *--ptr = 'x';
-    *--ptr = '0';
-    piece_ = absl::string_view(ptr, scratch_ + sizeof(scratch_) - ptr);
-  }
-}
-
-// TODO(jorg): Don't duplicate so much code between here and str_cat.cc
-Arg::Arg(Hex hex) {
-  char* const end = &scratch_[numbers_internal::kFastToBufferSize];
-  char* writer = end;
-  uint64_t value = hex.value;
-  do {
-    *--writer = absl::numbers_internal::kHexChar[value & 0xF];
-    value >>= 4;
-  } while (value != 0);
-
-  char* beg;
-  if (end - writer < hex.width) {
-    beg = end - hex.width;
-    std::fill_n(beg, writer - beg, hex.fill);
-  } else {
-    beg = writer;
-  }
-
-  piece_ = absl::string_view(beg, end - beg);
-}
-
-// TODO(jorg): Don't duplicate so much code between here and str_cat.cc
-Arg::Arg(Dec dec) {
-  assert(dec.width <= numbers_internal::kFastToBufferSize);
-  char* const end = &scratch_[numbers_internal::kFastToBufferSize];
-  char* const minfill = end - dec.width;
-  char* writer = end;
-  uint64_t value = dec.value;
-  bool neg = dec.neg;
-  while (value > 9) {
-    *--writer = '0' + (value % 10);
-    value /= 10;
-  }
-  *--writer = '0' + value;
-  if (neg) *--writer = '-';
-
-  ptrdiff_t fillers = writer - minfill;
-  if (fillers > 0) {
-    // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
-    // But...: if the fill character is '0', then it's <+/-><fill><digits>
-    bool add_sign_again = false;
-    if (neg && dec.fill == '0') {  // If filling with '0',
-      ++writer;                    // ignore the sign we just added
-      add_sign_again = true;       // and re-add the sign later.
-    }
-    writer -= fillers;
-    std::fill_n(writer, fillers, dec.fill);
-    if (add_sign_again) *--writer = '-';
-  }
-
-  piece_ = absl::string_view(writer, end - writer);
-}
-
-}  // namespace substitute_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/strings/substitute.h b/third_party/abseil_cpp/absl/strings/substitute.h
deleted file mode 100644
index c6da4dc6e7..0000000000
--- a/third_party/abseil_cpp/absl/strings/substitute.h
+++ /dev/null
@@ -1,696 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: substitute.h
-// -----------------------------------------------------------------------------
-//
-// This package contains functions for efficiently performing string
-// substitutions using a format string with positional notation:
-// `Substitute()` and `SubstituteAndAppend()`.
-//
-// Unlike printf-style format specifiers, `Substitute()` functions do not need
-// to specify the type of the substitution arguments. Supported arguments
-// following the format string, such as strings, string_views, ints,
-// floats, and bools, are automatically converted to strings during the
-// substitution process. (See below for a full list of supported types.)
-//
-// `Substitute()` does not allow you to specify *how* to format a value, beyond
-// the default conversion to string. For example, you cannot format an integer
-// in hex.
-//
-// The format string uses positional identifiers indicated by a dollar sign ($)
-// and single digit positional ids to indicate which substitution arguments to
-// use at that location within the format string.
-//
-// A '$$' sequence in the format string causes a literal '$' character to be
-// output.
-//
-// Example 1:
-//   std::string s = Substitute("$1 purchased $0 $2 for $$10. Thanks $1!",
-//                              5, "Bob", "Apples");
-//   EXPECT_EQ("Bob purchased 5 Apples for $10. Thanks Bob!", s);
-//
-// Example 2:
-//   std::string s = "Hi. ";
-//   SubstituteAndAppend(&s, "My name is $0 and I am $1 years old.", "Bob", 5);
-//   EXPECT_EQ("Hi. My name is Bob and I am 5 years old.", s);
-//
-// Supported types:
-//   * absl::string_view, std::string, const char* (null is equivalent to "")
-//   * int32_t, int64_t, uint32_t, uint64_t
-//   * float, double
-//   * bool (Printed as "true" or "false")
-//   * pointer types other than char* (Printed as "0x<lower case hex string>",
-//     except that null is printed as "NULL")
-//
-// If an invalid format string is provided, Substitute returns an empty string
-// and SubstituteAndAppend does not change the provided output string.
-// A format string is invalid if it:
-//   * ends in an unescaped $ character,
-//     e.g. "Hello $", or
-//   * calls for a position argument which is not provided,
-//     e.g. Substitute("Hello $2", "world"), or
-//   * specifies a non-digit, non-$ character after an unescaped $ character,
-//     e.g. "Hello $f".
-// In debug mode, i.e. #ifndef NDEBUG, such errors terminate the program.
-
-#ifndef ABSL_STRINGS_SUBSTITUTE_H_
-#define ABSL_STRINGS_SUBSTITUTE_H_
-
-#include <cstring>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/escaping.h"
-#include "absl/strings/numbers.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_split.h"
-#include "absl/strings/string_view.h"
-#include "absl/strings/strip.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace substitute_internal {
-
-// Arg
-//
-// This class provides an argument type for `absl::Substitute()` and
-// `absl::SubstituteAndAppend()`. `Arg` handles implicit conversion of various
-// types to a string. (`Arg` is very similar to the `AlphaNum` class in
-// `StrCat()`.)
-//
-// This class has implicit constructors.
-class Arg {
- public:
-  // Overloads for string-y things
-  //
-  // Explicitly overload `const char*` so the compiler doesn't cast to `bool`.
-  Arg(const char* value)  // NOLINT(runtime/explicit)
-      : piece_(absl::NullSafeStringView(value)) {}
-  template <typename Allocator>
-  Arg(  // NOLINT
-      const std::basic_string<char, std::char_traits<char>, Allocator>&
-          value) noexcept
-      : piece_(value) {}
-  Arg(absl::string_view value)  // NOLINT(runtime/explicit)
-      : piece_(value) {}
-
-  // Overloads for primitives
-  //
-  // No overloads are available for signed and unsigned char because if people
-  // are explicitly declaring their chars as signed or unsigned then they are
-  // probably using them as 8-bit integers and would probably prefer an integer
-  // representation. However, we can't really know, so we make the caller decide
-  // what to do.
-  Arg(char value)  // NOLINT(runtime/explicit)
-      : piece_(scratch_, 1) {
-    scratch_[0] = value;
-  }
-  Arg(short value)  // NOLINT(*)
-      : piece_(scratch_,
-               numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
-  Arg(unsigned short value)  // NOLINT(*)
-      : piece_(scratch_,
-               numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
-  Arg(int value)  // NOLINT(runtime/explicit)
-      : piece_(scratch_,
-               numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
-  Arg(unsigned int value)  // NOLINT(runtime/explicit)
-      : piece_(scratch_,
-               numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
-  Arg(long value)  // NOLINT(*)
-      : piece_(scratch_,
-               numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
-  Arg(unsigned long value)  // NOLINT(*)
-      : piece_(scratch_,
-               numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
-  Arg(long long value)  // NOLINT(*)
-      : piece_(scratch_,
-               numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
-  Arg(unsigned long long value)  // NOLINT(*)
-      : piece_(scratch_,
-               numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
-  Arg(float value)  // NOLINT(runtime/explicit)
-      : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
-  }
-  Arg(double value)  // NOLINT(runtime/explicit)
-      : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
-  }
-  Arg(bool value)  // NOLINT(runtime/explicit)
-      : piece_(value ? "true" : "false") {}
-
-  Arg(Hex hex);  // NOLINT(runtime/explicit)
-  Arg(Dec dec);  // NOLINT(runtime/explicit)
-
-  // vector<bool>::reference and const_reference require special help to
-  // convert to `AlphaNum` because it requires two user defined conversions.
-  template <typename T,
-            absl::enable_if_t<
-                std::is_class<T>::value &&
-                (std::is_same<T, std::vector<bool>::reference>::value ||
-                 std::is_same<T, std::vector<bool>::const_reference>::value)>* =
-                nullptr>
-  Arg(T value)  // NOLINT(google-explicit-constructor)
-      : Arg(static_cast<bool>(value)) {}
-
-  // `void*` values, with the exception of `char*`, are printed as
-  // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed.
-  Arg(const void* value);  // NOLINT(runtime/explicit)
-
-  Arg(const Arg&) = delete;
-  Arg& operator=(const Arg&) = delete;
-
-  absl::string_view piece() const { return piece_; }
-
- private:
-  absl::string_view piece_;
-  char scratch_[numbers_internal::kFastToBufferSize];
-};
-
-// Internal helper function. Don't call this from outside this implementation.
-// This interface may change without notice.
-void SubstituteAndAppendArray(std::string* output, absl::string_view format,
-                              const absl::string_view* args_array,
-                              size_t num_args);
-
-#if defined(ABSL_BAD_CALL_IF)
-constexpr int CalculateOneBit(const char* format) {
-  // Returns:
-  // * 2^N for '$N' when N is in [0-9]
-  // * 0 for correct '$' escaping: '$$'.
-  // * -1 otherwise.
-  return (*format < '0' || *format > '9') ? (*format == '$' ? 0 : -1)
-                                          : (1 << (*format - '0'));
-}
-
-constexpr const char* SkipNumber(const char* format) {
-  return !*format ? format : (format + 1);
-}
-
-constexpr int PlaceholderBitmask(const char* format) {
-  return !*format
-             ? 0
-             : *format != '$' ? PlaceholderBitmask(format + 1)
-                              : (CalculateOneBit(format + 1) |
-                                 PlaceholderBitmask(SkipNumber(format + 1)));
-}
-#endif  // ABSL_BAD_CALL_IF
-
-}  // namespace substitute_internal
-
-//
-// PUBLIC API
-//
-
-// SubstituteAndAppend()
-//
-// Substitutes variables into a given format string and appends to a given
-// output string. See file comments above for usage.
-//
-// The declarations of `SubstituteAndAppend()` below consist of overloads
-// for passing 0 to 10 arguments, respectively.
-//
-// NOTE: A zero-argument `SubstituteAndAppend()` may be used within variadic
-// templates to allow a variable number of arguments.
-//
-// Example:
-//  template <typename... Args>
-//  void VarMsg(std::string* boilerplate, absl::string_view format,
-//      const Args&... args) {
-//    absl::SubstituteAndAppend(boilerplate, format, args...);
-//  }
-//
-inline void SubstituteAndAppend(std::string* output, absl::string_view format) {
-  substitute_internal::SubstituteAndAppendArray(output, format, nullptr, 0);
-}
-
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
-                                const substitute_internal::Arg& a0) {
-  const absl::string_view args[] = {a0.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
-                                const substitute_internal::Arg& a0,
-                                const substitute_internal::Arg& a1) {
-  const absl::string_view args[] = {a0.piece(), a1.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
-                                const substitute_internal::Arg& a0,
-                                const substitute_internal::Arg& a1,
-                                const substitute_internal::Arg& a2) {
-  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
-                                const substitute_internal::Arg& a0,
-                                const substitute_internal::Arg& a1,
-                                const substitute_internal::Arg& a2,
-                                const substitute_internal::Arg& a3) {
-  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
-                                    a3.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
-                                const substitute_internal::Arg& a0,
-                                const substitute_internal::Arg& a1,
-                                const substitute_internal::Arg& a2,
-                                const substitute_internal::Arg& a3,
-                                const substitute_internal::Arg& a4) {
-  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
-                                    a3.piece(), a4.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
-                                const substitute_internal::Arg& a0,
-                                const substitute_internal::Arg& a1,
-                                const substitute_internal::Arg& a2,
-                                const substitute_internal::Arg& a3,
-                                const substitute_internal::Arg& a4,
-                                const substitute_internal::Arg& a5) {
-  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
-                                    a3.piece(), a4.piece(), a5.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
-                                const substitute_internal::Arg& a0,
-                                const substitute_internal::Arg& a1,
-                                const substitute_internal::Arg& a2,
-                                const substitute_internal::Arg& a3,
-                                const substitute_internal::Arg& a4,
-                                const substitute_internal::Arg& a5,
-                                const substitute_internal::Arg& a6) {
-  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
-                                    a3.piece(), a4.piece(), a5.piece(),
-                                    a6.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-inline void SubstituteAndAppend(
-    std::string* output, absl::string_view format,
-    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
-    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
-    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
-    const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) {
-  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
-                                    a3.piece(), a4.piece(), a5.piece(),
-                                    a6.piece(), a7.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-inline void SubstituteAndAppend(
-    std::string* output, absl::string_view format,
-    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
-    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
-    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
-    const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
-    const substitute_internal::Arg& a8) {
-  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
-                                    a3.piece(), a4.piece(), a5.piece(),
-                                    a6.piece(), a7.piece(), a8.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-inline void SubstituteAndAppend(
-    std::string* output, absl::string_view format,
-    const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
-    const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
-    const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
-    const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
-    const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) {
-  const absl::string_view args[] = {
-      a0.piece(), a1.piece(), a2.piece(), a3.piece(), a4.piece(),
-      a5.piece(), a6.piece(), a7.piece(), a8.piece(), a9.piece()};
-  substitute_internal::SubstituteAndAppendArray(output, format, args,
-                                                ABSL_ARRAYSIZE(args));
-}
-
-#if defined(ABSL_BAD_CALL_IF)
-// This body of functions catches cases where the number of placeholders
-// doesn't match the number of data arguments.
-void SubstituteAndAppend(std::string* output, const char* format)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
-                     "There were no substitution arguments "
-                     "but this format string has a $[0-9] in it");
-
-void SubstituteAndAppend(std::string* output, const char* format,
-                         const substitute_internal::Arg& a0)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
-                     "There was 1 substitution argument given, but "
-                     "this format string is either missing its $0, or "
-                     "contains one of $1-$9");
-
-void SubstituteAndAppend(std::string* output, const char* format,
-                         const substitute_internal::Arg& a0,
-                         const substitute_internal::Arg& a1)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3,
-                     "There were 2 substitution arguments given, but "
-                     "this format string is either missing its $0/$1, or "
-                     "contains one of $2-$9");
-
-void SubstituteAndAppend(std::string* output, const char* format,
-                         const substitute_internal::Arg& a0,
-                         const substitute_internal::Arg& a1,
-                         const substitute_internal::Arg& a2)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7,
-                     "There were 3 substitution arguments given, but "
-                     "this format string is either missing its $0/$1/$2, or "
-                     "contains one of $3-$9");
-
-void SubstituteAndAppend(std::string* output, const char* format,
-                         const substitute_internal::Arg& a0,
-                         const substitute_internal::Arg& a1,
-                         const substitute_internal::Arg& a2,
-                         const substitute_internal::Arg& a3)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15,
-                     "There were 4 substitution arguments given, but "
-                     "this format string is either missing its $0-$3, or "
-                     "contains one of $4-$9");
-
-void SubstituteAndAppend(std::string* output, const char* format,
-                         const substitute_internal::Arg& a0,
-                         const substitute_internal::Arg& a1,
-                         const substitute_internal::Arg& a2,
-                         const substitute_internal::Arg& a3,
-                         const substitute_internal::Arg& a4)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31,
-                     "There were 5 substitution arguments given, but "
-                     "this format string is either missing its $0-$4, or "
-                     "contains one of $5-$9");
-
-void SubstituteAndAppend(std::string* output, const char* format,
-                         const substitute_internal::Arg& a0,
-                         const substitute_internal::Arg& a1,
-                         const substitute_internal::Arg& a2,
-                         const substitute_internal::Arg& a3,
-                         const substitute_internal::Arg& a4,
-                         const substitute_internal::Arg& a5)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63,
-                     "There were 6 substitution arguments given, but "
-                     "this format string is either missing its $0-$5, or "
-                     "contains one of $6-$9");
-
-void SubstituteAndAppend(
-    std::string* output, const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127,
-                     "There were 7 substitution arguments given, but "
-                     "this format string is either missing its $0-$6, or "
-                     "contains one of $7-$9");
-
-void SubstituteAndAppend(
-    std::string* output, const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255,
-                     "There were 8 substitution arguments given, but "
-                     "this format string is either missing its $0-$7, or "
-                     "contains one of $8-$9");
-
-void SubstituteAndAppend(
-    std::string* output, const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
-    ABSL_BAD_CALL_IF(
-        substitute_internal::PlaceholderBitmask(format) != 511,
-        "There were 9 substitution arguments given, but "
-        "this format string is either missing its $0-$8, or contains a $9");
-
-void SubstituteAndAppend(
-    std::string* output, const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
-    const substitute_internal::Arg& a9)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023,
-                     "There were 10 substitution arguments given, but this "
-                     "format string doesn't contain all of $0 through $9");
-#endif  // ABSL_BAD_CALL_IF
-
-// Substitute()
-//
-// Substitutes variables into a given format string. See file comments above
-// for usage.
-//
-// The declarations of `Substitute()` below consist of overloads for passing 0
-// to 10 arguments, respectively.
-//
-// NOTE: A zero-argument `Substitute()` may be used within variadic templates to
-// allow a variable number of arguments.
-//
-// Example:
-//  template <typename... Args>
-//  void VarMsg(absl::string_view format, const Args&... args) {
-//    std::string s = absl::Substitute(format, args...);
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(absl::string_view format) {
-  std::string result;
-  SubstituteAndAppend(&result, format);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7, const substitute_internal::Arg& a8) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8);
-  return result;
-}
-
-ABSL_MUST_USE_RESULT inline std::string Substitute(
-    absl::string_view format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
-    const substitute_internal::Arg& a9) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
-  return result;
-}
-
-#if defined(ABSL_BAD_CALL_IF)
-// This body of functions catches cases where the number of placeholders
-// doesn't match the number of data arguments.
-std::string Substitute(const char* format)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
-                     "There were no substitution arguments "
-                     "but this format string has a $[0-9] in it");
-
-std::string Substitute(const char* format, const substitute_internal::Arg& a0)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
-                     "There was 1 substitution argument given, but "
-                     "this format string is either missing its $0, or "
-                     "contains one of $1-$9");
-
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
-                       const substitute_internal::Arg& a1)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3,
-                     "There were 2 substitution arguments given, but "
-                     "this format string is either missing its $0/$1, or "
-                     "contains one of $2-$9");
-
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
-                       const substitute_internal::Arg& a1,
-                       const substitute_internal::Arg& a2)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7,
-                     "There were 3 substitution arguments given, but "
-                     "this format string is either missing its $0/$1/$2, or "
-                     "contains one of $3-$9");
-
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
-                       const substitute_internal::Arg& a1,
-                       const substitute_internal::Arg& a2,
-                       const substitute_internal::Arg& a3)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15,
-                     "There were 4 substitution arguments given, but "
-                     "this format string is either missing its $0-$3, or "
-                     "contains one of $4-$9");
-
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
-                       const substitute_internal::Arg& a1,
-                       const substitute_internal::Arg& a2,
-                       const substitute_internal::Arg& a3,
-                       const substitute_internal::Arg& a4)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31,
-                     "There were 5 substitution arguments given, but "
-                     "this format string is either missing its $0-$4, or "
-                     "contains one of $5-$9");
-
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
-                       const substitute_internal::Arg& a1,
-                       const substitute_internal::Arg& a2,
-                       const substitute_internal::Arg& a3,
-                       const substitute_internal::Arg& a4,
-                       const substitute_internal::Arg& a5)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63,
-                     "There were 6 substitution arguments given, but "
-                     "this format string is either missing its $0-$5, or "
-                     "contains one of $6-$9");
-
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
-                       const substitute_internal::Arg& a1,
-                       const substitute_internal::Arg& a2,
-                       const substitute_internal::Arg& a3,
-                       const substitute_internal::Arg& a4,
-                       const substitute_internal::Arg& a5,
-                       const substitute_internal::Arg& a6)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127,
-                     "There were 7 substitution arguments given, but "
-                     "this format string is either missing its $0-$6, or "
-                     "contains one of $7-$9");
-
-std::string Substitute(const char* format, const substitute_internal::Arg& a0,
-                       const substitute_internal::Arg& a1,
-                       const substitute_internal::Arg& a2,
-                       const substitute_internal::Arg& a3,
-                       const substitute_internal::Arg& a4,
-                       const substitute_internal::Arg& a5,
-                       const substitute_internal::Arg& a6,
-                       const substitute_internal::Arg& a7)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255,
-                     "There were 8 substitution arguments given, but "
-                     "this format string is either missing its $0-$7, or "
-                     "contains one of $8-$9");
-
-std::string Substitute(
-    const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
-    ABSL_BAD_CALL_IF(
-        substitute_internal::PlaceholderBitmask(format) != 511,
-        "There were 9 substitution arguments given, but "
-        "this format string is either missing its $0-$8, or contains a $9");
-
-std::string Substitute(
-    const char* format, const substitute_internal::Arg& a0,
-    const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
-    const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
-    const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
-    const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
-    const substitute_internal::Arg& a9)
-    ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023,
-                     "There were 10 substitution arguments given, but this "
-                     "format string doesn't contain all of $0 through $9");
-#endif  // ABSL_BAD_CALL_IF
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_STRINGS_SUBSTITUTE_H_
diff --git a/third_party/abseil_cpp/absl/strings/substitute_test.cc b/third_party/abseil_cpp/absl/strings/substitute_test.cc
deleted file mode 100644
index 442c921528..0000000000
--- a/third_party/abseil_cpp/absl/strings/substitute_test.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/strings/substitute.h"
-
-#include <cstdint>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-TEST(SubstituteTest, Substitute) {
-  // Basic.
-  EXPECT_EQ("Hello, world!", absl::Substitute("$0, $1!", "Hello", "world"));
-
-  // Non-char* types.
-  EXPECT_EQ("123 0.2 0.1 foo true false x",
-            absl::Substitute("$0 $1 $2 $3 $4 $5 $6", 123, 0.2, 0.1f,
-                             std::string("foo"), true, false, 'x'));
-
-  // All int types.
-  EXPECT_EQ(
-      "-32767 65535 "
-      "-1234567890 3234567890 "
-      "-1234567890 3234567890 "
-      "-1234567890123456789 9234567890123456789",
-      absl::Substitute(
-          "$0 $1 $2 $3 $4 $5 $6 $7",
-          static_cast<short>(-32767),          // NOLINT(runtime/int)
-          static_cast<unsigned short>(65535),  // NOLINT(runtime/int)
-          -1234567890, 3234567890U, -1234567890L, 3234567890UL,
-          -int64_t{1234567890123456789}, uint64_t{9234567890123456789u}));
-
-  // Hex format
-  EXPECT_EQ("0 1 f ffff0ffff 0123456789abcdef",
-            absl::Substitute("$0$1$2$3$4 $5",  //
-                             absl::Hex(0), absl::Hex(1, absl::kSpacePad2),
-                             absl::Hex(0xf, absl::kSpacePad2),
-                             absl::Hex(int16_t{-1}, absl::kSpacePad5),
-                             absl::Hex(int16_t{-1}, absl::kZeroPad5),
-                             absl::Hex(0x123456789abcdef, absl::kZeroPad16)));
-
-  // Dec format
-  EXPECT_EQ("0 115   -1-0001 81985529216486895",
-            absl::Substitute("$0$1$2$3$4 $5",  //
-                             absl::Dec(0), absl::Dec(1, absl::kSpacePad2),
-                             absl::Dec(0xf, absl::kSpacePad2),
-                             absl::Dec(int16_t{-1}, absl::kSpacePad5),
-                             absl::Dec(int16_t{-1}, absl::kZeroPad5),
-                             absl::Dec(0x123456789abcdef, absl::kZeroPad16)));
-
-  // Pointer.
-  const int* int_p = reinterpret_cast<const int*>(0x12345);
-  std::string str = absl::Substitute("$0", int_p);
-  EXPECT_EQ(absl::StrCat("0x", absl::Hex(int_p)), str);
-
-  // Volatile Pointer.
-  // Like C++ streamed I/O, such pointers implicitly become bool
-  volatile int vol = 237;
-  volatile int *volatile volptr = &vol;
-  str = absl::Substitute("$0", volptr);
-  EXPECT_EQ("true", str);
-
-  // null is special. StrCat prints 0x0. Substitute prints NULL.
-  const uint64_t* null_p = nullptr;
-  str = absl::Substitute("$0", null_p);
-  EXPECT_EQ("NULL", str);
-
-  // char* is also special.
-  const char* char_p = "print me";
-  str = absl::Substitute("$0", char_p);
-  EXPECT_EQ("print me", str);
-
-  char char_buf[16];
-  strncpy(char_buf, "print me too", sizeof(char_buf));
-  str = absl::Substitute("$0", char_buf);
-  EXPECT_EQ("print me too", str);
-
-  // null char* is "doubly" special. Represented as the empty string.
-  char_p = nullptr;
-  str = absl::Substitute("$0", char_p);
-  EXPECT_EQ("", str);
-
-  // Out-of-order.
-  EXPECT_EQ("b, a, c, b", absl::Substitute("$1, $0, $2, $1", "a", "b", "c"));
-
-  // Literal $
-  EXPECT_EQ("$", absl::Substitute("$$"));
-
-  EXPECT_EQ("$1", absl::Substitute("$$1"));
-
-  // Test all overloads.
-  EXPECT_EQ("a", absl::Substitute("$0", "a"));
-  EXPECT_EQ("a b", absl::Substitute("$0 $1", "a", "b"));
-  EXPECT_EQ("a b c", absl::Substitute("$0 $1 $2", "a", "b", "c"));
-  EXPECT_EQ("a b c d", absl::Substitute("$0 $1 $2 $3", "a", "b", "c", "d"));
-  EXPECT_EQ("a b c d e",
-            absl::Substitute("$0 $1 $2 $3 $4", "a", "b", "c", "d", "e"));
-  EXPECT_EQ("a b c d e f", absl::Substitute("$0 $1 $2 $3 $4 $5", "a", "b", "c",
-                                            "d", "e", "f"));
-  EXPECT_EQ("a b c d e f g", absl::Substitute("$0 $1 $2 $3 $4 $5 $6", "a", "b",
-                                              "c", "d", "e", "f", "g"));
-  EXPECT_EQ("a b c d e f g h",
-            absl::Substitute("$0 $1 $2 $3 $4 $5 $6 $7", "a", "b", "c", "d", "e",
-                             "f", "g", "h"));
-  EXPECT_EQ("a b c d e f g h i",
-            absl::Substitute("$0 $1 $2 $3 $4 $5 $6 $7 $8", "a", "b", "c", "d",
-                             "e", "f", "g", "h", "i"));
-  EXPECT_EQ("a b c d e f g h i j",
-            absl::Substitute("$0 $1 $2 $3 $4 $5 $6 $7 $8 $9", "a", "b", "c",
-                             "d", "e", "f", "g", "h", "i", "j"));
-  EXPECT_EQ("a b c d e f g h i j b0",
-            absl::Substitute("$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 $10", "a", "b", "c",
-                             "d", "e", "f", "g", "h", "i", "j"));
-
-  const char* null_cstring = nullptr;
-  EXPECT_EQ("Text: ''", absl::Substitute("Text: '$0'", null_cstring));
-}
-
-TEST(SubstituteTest, SubstituteAndAppend) {
-  std::string str = "Hello";
-  absl::SubstituteAndAppend(&str, ", $0!", "world");
-  EXPECT_EQ("Hello, world!", str);
-
-  // Test all overloads.
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0", "a");
-  EXPECT_EQ("a", str);
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0 $1", "a", "b");
-  EXPECT_EQ("a b", str);
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0 $1 $2", "a", "b", "c");
-  EXPECT_EQ("a b c", str);
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0 $1 $2 $3", "a", "b", "c", "d");
-  EXPECT_EQ("a b c d", str);
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0 $1 $2 $3 $4", "a", "b", "c", "d", "e");
-  EXPECT_EQ("a b c d e", str);
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0 $1 $2 $3 $4 $5", "a", "b", "c", "d", "e",
-                            "f");
-  EXPECT_EQ("a b c d e f", str);
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0 $1 $2 $3 $4 $5 $6", "a", "b", "c", "d",
-                            "e", "f", "g");
-  EXPECT_EQ("a b c d e f g", str);
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0 $1 $2 $3 $4 $5 $6 $7", "a", "b", "c", "d",
-                            "e", "f", "g", "h");
-  EXPECT_EQ("a b c d e f g h", str);
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0 $1 $2 $3 $4 $5 $6 $7 $8", "a", "b", "c",
-                            "d", "e", "f", "g", "h", "i");
-  EXPECT_EQ("a b c d e f g h i", str);
-  str.clear();
-  absl::SubstituteAndAppend(&str, "$0 $1 $2 $3 $4 $5 $6 $7 $8 $9", "a", "b",
-                            "c", "d", "e", "f", "g", "h", "i", "j");
-  EXPECT_EQ("a b c d e f g h i j", str);
-}
-
-TEST(SubstituteTest, VectorBoolRef) {
-  std::vector<bool> v = {true, false};
-  const auto& cv = v;
-  EXPECT_EQ("true false true false",
-            absl::Substitute("$0 $1 $2 $3", v[0], v[1], cv[0], cv[1]));
-
-  std::string str = "Logic be like: ";
-  absl::SubstituteAndAppend(&str, "$0 $1 $2 $3", v[0], v[1], cv[0], cv[1]);
-  EXPECT_EQ("Logic be like: true false true false", str);
-}
-
-#ifdef GTEST_HAS_DEATH_TEST
-
-TEST(SubstituteDeathTest, SubstituteDeath) {
-  EXPECT_DEBUG_DEATH(
-      static_cast<void>(absl::Substitute(absl::string_view("-$2"), "a", "b")),
-      "Invalid absl::Substitute\\(\\) format string: asked for \"\\$2\", "
-      "but only 2 args were given.");
-  EXPECT_DEBUG_DEATH(
-      static_cast<void>(absl::Substitute(absl::string_view("-$z-"))),
-      "Invalid absl::Substitute\\(\\) format string: \"-\\$z-\"");
-  EXPECT_DEBUG_DEATH(
-      static_cast<void>(absl::Substitute(absl::string_view("-$"))),
-      "Invalid absl::Substitute\\(\\) format string: \"-\\$\"");
-}
-
-#endif  // GTEST_HAS_DEATH_TEST
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/synchronization/BUILD.bazel b/third_party/abseil_cpp/absl/synchronization/BUILD.bazel
deleted file mode 100644
index cd4009a157..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/BUILD.bazel
+++ /dev/null
@@ -1,288 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-# Internal data structure for efficiently detecting mutex dependency cycles
-cc_library(
-    name = "graphcycles_internal",
-    srcs = [
-        "internal/graphcycles.cc",
-    ],
-    hdrs = [
-        "internal/graphcycles.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        "//absl/base",
-        "//absl/base:base_internal",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:malloc_internal",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_library(
-    name = "kernel_timeout_internal",
-    hdrs = ["internal/kernel_timeout.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl/synchronization:__pkg__",
-    ],
-    deps = [
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/time",
-    ],
-)
-
-cc_library(
-    name = "synchronization",
-    srcs = [
-        "barrier.cc",
-        "blocking_counter.cc",
-        "internal/create_thread_identity.cc",
-        "internal/per_thread_sem.cc",
-        "internal/waiter.cc",
-        "mutex.cc",
-        "notification.cc",
-    ],
-    hdrs = [
-        "barrier.h",
-        "blocking_counter.h",
-        "internal/create_thread_identity.h",
-        "internal/futex.h",
-        "internal/per_thread_sem.h",
-        "internal/waiter.h",
-        "mutex.h",
-        "notification.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = select({
-        "//absl:windows": [],
-        "//absl:wasm": [],
-        "//conditions:default": ["-pthread"],
-    }) + ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":graphcycles_internal",
-        ":kernel_timeout_internal",
-        "//absl/base",
-        "//absl/base:atomic_hook",
-        "//absl/base:base_internal",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:dynamic_annotations",
-        "//absl/base:malloc_internal",
-        "//absl/base:raw_logging_internal",
-        "//absl/debugging:stacktrace",
-        "//absl/debugging:symbolize",
-        "//absl/time",
-    ],
-)
-
-cc_test(
-    name = "barrier_test",
-    size = "small",
-    srcs = ["barrier_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":synchronization",
-        "//absl/time",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "blocking_counter_test",
-    size = "small",
-    srcs = ["blocking_counter_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":synchronization",
-        "//absl/time",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "graphcycles_test",
-    size = "medium",
-    srcs = ["internal/graphcycles_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":graphcycles_internal",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "graphcycles_benchmark",
-    srcs = ["internal/graphcycles_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = [
-        "benchmark",
-    ],
-    deps = [
-        ":graphcycles_internal",
-        "//absl/base:raw_logging_internal",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_library(
-    name = "thread_pool",
-    testonly = 1,
-    hdrs = ["internal/thread_pool.h"],
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl:__subpackages__",
-    ],
-    deps = [
-        ":synchronization",
-        "//absl/base:core_headers",
-    ],
-)
-
-cc_test(
-    name = "mutex_test",
-    size = "large",
-    srcs = ["mutex_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    shard_count = 25,
-    deps = [
-        ":synchronization",
-        ":thread_pool",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/memory",
-        "//absl/time",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "mutex_benchmark_common",
-    testonly = 1,
-    srcs = ["mutex_benchmark.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl/synchronization:__pkg__",
-    ],
-    deps = [
-        ":synchronization",
-        ":thread_pool",
-        "//absl/base",
-        "//absl/base:config",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-    alwayslink = 1,
-)
-
-cc_binary(
-    name = "mutex_benchmark",
-    testonly = 1,
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        ":mutex_benchmark_common",
-    ],
-)
-
-cc_test(
-    name = "notification_test",
-    size = "small",
-    srcs = ["notification_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":synchronization",
-        "//absl/time",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "per_thread_sem_test_common",
-    testonly = 1,
-    srcs = ["internal/per_thread_sem_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":synchronization",
-        "//absl/base",
-        "//absl/base:config",
-        "//absl/strings",
-        "//absl/time",
-        "@com_google_googletest//:gtest",
-    ],
-    alwayslink = 1,
-)
-
-cc_test(
-    name = "per_thread_sem_test",
-    size = "medium",
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":per_thread_sem_test_common",
-        ":synchronization",
-        "//absl/strings",
-        "//absl/time",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "lifetime_test",
-    srcs = [
-        "lifetime_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["no_test_ios_x86_64"],
-    deps = [
-        ":synchronization",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/synchronization/CMakeLists.txt b/third_party/abseil_cpp/absl/synchronization/CMakeLists.txt
deleted file mode 100644
index e633d0bf53..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/CMakeLists.txt
+++ /dev/null
@@ -1,216 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    graphcycles_internal
-  HDRS
-    "internal/graphcycles.h"
-  SRCS
-    "internal/graphcycles.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base
-    absl::base_internal
-    absl::config
-    absl::core_headers
-    absl::malloc_internal
-    absl::raw_logging_internal
-)
-
-absl_cc_library(
-  NAME
-    kernel_timeout_internal
-  HDRS
-    "internal/kernel_timeout.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::core_headers
-    absl::raw_logging_internal
-    absl::time
-)
-
-absl_cc_library(
-  NAME
-    synchronization
-  HDRS
-    "barrier.h"
-    "blocking_counter.h"
-    "internal/create_thread_identity.h"
-    "internal/futex.h"
-    "internal/per_thread_sem.h"
-    "internal/waiter.h"
-    "mutex.h"
-    "notification.h"
-  SRCS
-    "barrier.cc"
-    "blocking_counter.cc"
-    "internal/create_thread_identity.cc"
-    "internal/per_thread_sem.cc"
-    "internal/waiter.cc"
-    "notification.cc"
-    "mutex.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::graphcycles_internal
-    absl::kernel_timeout_internal
-    absl::atomic_hook
-    absl::base
-    absl::base_internal
-    absl::config
-    absl::core_headers
-    absl::dynamic_annotations
-    absl::malloc_internal
-    absl::raw_logging_internal
-    absl::stacktrace
-    absl::symbolize
-    absl::time
-    Threads::Threads
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    barrier_test
-  SRCS
-    "barrier_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::synchronization
-    absl::time
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    blocking_counter_test
-  SRCS
-    "blocking_counter_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::synchronization
-    absl::time
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    graphcycles_test
-  SRCS
-    "internal/graphcycles_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::graphcycles_internal
-    absl::core_headers
-    absl::raw_logging_internal
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    thread_pool
-  HDRS
-    "internal/thread_pool.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::synchronization
-    absl::core_headers
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    mutex_test
-  SRCS
-    "mutex_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::synchronization
-    absl::thread_pool
-    absl::base
-    absl::config
-    absl::core_headers
-    absl::memory
-    absl::raw_logging_internal
-    absl::time
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    notification_test
-  SRCS
-    "notification_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::synchronization
-    absl::time
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    per_thread_sem_test_common
-  SRCS
-    "internal/per_thread_sem_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::synchronization
-    absl::base
-    absl::config
-    absl::strings
-    absl::time
-    gmock
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    per_thread_sem_test
-  SRCS
-    "internal/per_thread_sem_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::per_thread_sem_test_common
-    absl::synchronization
-    absl::strings
-    absl::time
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    lifetime_test
-  SRCS
-    "lifetime_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::synchronization
-    absl::core_headers
-    absl::raw_logging_internal
-)
diff --git a/third_party/abseil_cpp/absl/synchronization/barrier.cc b/third_party/abseil_cpp/absl/synchronization/barrier.cc
deleted file mode 100644
index 0dfd795e7b..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/barrier.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/barrier.h"
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Return whether int *arg is zero.
-static bool IsZero(void *arg) {
-  return 0 == *reinterpret_cast<int *>(arg);
-}
-
-bool Barrier::Block() {
-  MutexLock l(&this->lock_);
-
-  this->num_to_block_--;
-  if (this->num_to_block_ < 0) {
-    ABSL_RAW_LOG(
-        FATAL,
-        "Block() called too many times.  num_to_block_=%d out of total=%d",
-        this->num_to_block_, this->num_to_exit_);
-  }
-
-  this->lock_.Await(Condition(IsZero, &this->num_to_block_));
-
-  // Determine which thread can safely delete this Barrier object
-  this->num_to_exit_--;
-  ABSL_RAW_CHECK(this->num_to_exit_ >= 0, "barrier underflow");
-
-  // If num_to_exit_ == 0 then all other threads in the barrier have
-  // exited the Wait() and have released the Mutex so this thread is
-  // free to delete the barrier.
-  return this->num_to_exit_ == 0;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/synchronization/barrier.h b/third_party/abseil_cpp/absl/synchronization/barrier.h
deleted file mode 100644
index d8e754406f..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/barrier.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// barrier.h
-// -----------------------------------------------------------------------------
-
-#ifndef ABSL_SYNCHRONIZATION_BARRIER_H_
-#define ABSL_SYNCHRONIZATION_BARRIER_H_
-
-#include "absl/base/thread_annotations.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Barrier
-//
-// This class creates a barrier which blocks threads until a prespecified
-// threshold of threads (`num_threads`) utilizes the barrier. A thread utilizes
-// the `Barrier` by calling `Block()` on the barrier, which will block that
-// thread; no call to `Block()` will return until `num_threads` threads have
-// called it.
-//
-// Exactly one call to `Block()` will return `true`, which is then responsible
-// for destroying the barrier; because stack allocation will cause the barrier
-// to be deleted when it is out of scope, barriers should not be stack
-// allocated.
-//
-// Example:
-//
-//   // Main thread creates a `Barrier`:
-//   barrier = new Barrier(num_threads);
-//
-//   // Each participating thread could then call:
-//   if (barrier->Block()) delete barrier;  // Exactly one call to `Block()`
-//                                          // returns `true`; that call
-//                                          // deletes the barrier.
-class Barrier {
- public:
-  // `num_threads` is the number of threads that will participate in the barrier
-  explicit Barrier(int num_threads)
-      : num_to_block_(num_threads), num_to_exit_(num_threads) {}
-
-  Barrier(const Barrier&) = delete;
-  Barrier& operator=(const Barrier&) = delete;
-
-  // Barrier::Block()
-  //
-  // Blocks the current thread, and returns only when the `num_threads`
-  // threshold of threads utilizing this barrier has been reached. `Block()`
-  // returns `true` for precisely one caller, which may then destroy the
-  // barrier.
-  //
-  // Memory ordering: For any threads X and Y, any action taken by X
-  // before X calls `Block()` will be visible to Y after Y returns from
-  // `Block()`.
-  bool Block();
-
- private:
-  Mutex lock_;
-  int num_to_block_ ABSL_GUARDED_BY(lock_);
-  int num_to_exit_ ABSL_GUARDED_BY(lock_);
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-#endif  // ABSL_SYNCHRONIZATION_BARRIER_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/barrier_test.cc b/third_party/abseil_cpp/absl/synchronization/barrier_test.cc
deleted file mode 100644
index bfc6cb1883..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/barrier_test.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/barrier.h"
-
-#include <thread>  // NOLINT(build/c++11)
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/synchronization/mutex.h"
-#include "absl/time/clock.h"
-
-
-TEST(Barrier, SanityTest) {
-  constexpr int kNumThreads = 10;
-  absl::Barrier* barrier = new absl::Barrier(kNumThreads);
-
-  absl::Mutex mutex;
-  int counter = 0;  // Guarded by mutex.
-
-  auto thread_func = [&] {
-    if (barrier->Block()) {
-      // This thread is the last thread to reach the barrier so it is
-      // responsible for deleting it.
-      delete barrier;
-    }
-
-    // Increment the counter.
-    absl::MutexLock lock(&mutex);
-    ++counter;
-  };
-
-  // Start (kNumThreads - 1) threads running thread_func.
-  std::vector<std::thread> threads;
-  for (int i = 0; i < kNumThreads - 1; ++i) {
-    threads.push_back(std::thread(thread_func));
-  }
-
-  // Give (kNumThreads - 1) threads a chance to reach the barrier.
-  // This test assumes at least one thread will have run after the
-  // sleep has elapsed. Sleeping in a test is usually bad form, but we
-  // need to make sure that we are testing the barrier instead of some
-  // other synchronization method.
-  absl::SleepFor(absl::Seconds(1));
-
-  // The counter should still be zero since no thread should have
-  // been able to pass the barrier yet.
-  {
-    absl::MutexLock lock(&mutex);
-    EXPECT_EQ(counter, 0);
-  }
-
-  // Start 1 more thread. This should make all threads pass the barrier.
-  threads.push_back(std::thread(thread_func));
-
-  // All threads should now be able to proceed and finish.
-  for (auto& thread : threads) {
-    thread.join();
-  }
-
-  // All threads should now have incremented the counter.
-  absl::MutexLock lock(&mutex);
-  EXPECT_EQ(counter, kNumThreads);
-}
diff --git a/third_party/abseil_cpp/absl/synchronization/blocking_counter.cc b/third_party/abseil_cpp/absl/synchronization/blocking_counter.cc
deleted file mode 100644
index 3cea7aed24..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/blocking_counter.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/blocking_counter.h"
-
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Return whether int *arg is zero.
-static bool IsZero(void *arg) {
-  return 0 == *reinterpret_cast<int *>(arg);
-}
-
-bool BlockingCounter::DecrementCount() {
-  MutexLock l(&lock_);
-  count_--;
-  if (count_ < 0) {
-    ABSL_RAW_LOG(
-        FATAL,
-        "BlockingCounter::DecrementCount() called too many times.  count=%d",
-        count_);
-  }
-  return count_ == 0;
-}
-
-void BlockingCounter::Wait() {
-  MutexLock l(&this->lock_);
-  ABSL_RAW_CHECK(count_ >= 0, "BlockingCounter underflow");
-
-  // only one thread may call Wait(). To support more than one thread,
-  // implement a counter num_to_exit, like in the Barrier class.
-  ABSL_RAW_CHECK(num_waiting_ == 0, "multiple threads called Wait()");
-  num_waiting_++;
-
-  this->lock_.Await(Condition(IsZero, &this->count_));
-
-  // At this point, We know that all threads executing DecrementCount have
-  // released the lock, and so will not touch this object again.
-  // Therefore, the thread calling this method is free to delete the object
-  // after we return from this method.
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/synchronization/blocking_counter.h b/third_party/abseil_cpp/absl/synchronization/blocking_counter.h
deleted file mode 100644
index 1f53f9f240..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/blocking_counter.h
+++ /dev/null
@@ -1,99 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// blocking_counter.h
-// -----------------------------------------------------------------------------
-
-#ifndef ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_
-#define ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_
-
-#include "absl/base/thread_annotations.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// BlockingCounter
-//
-// This class allows a thread to block for a pre-specified number of actions.
-// `BlockingCounter` maintains a single non-negative abstract integer "count"
-// with an initial value `initial_count`. A thread can then call `Wait()` on
-// this blocking counter to block until the specified number of events occur;
-// worker threads then call 'DecrementCount()` on the counter upon completion of
-// their work. Once the counter's internal "count" reaches zero, the blocked
-// thread unblocks.
-//
-// A `BlockingCounter` requires the following:
-//     - its `initial_count` is non-negative.
-//     - the number of calls to `DecrementCount()` on it is at most
-//       `initial_count`.
-//     - `Wait()` is called at most once on it.
-//
-// Given the above requirements, a `BlockingCounter` provides the following
-// guarantees:
-//     - Once its internal "count" reaches zero, no legal action on the object
-//       can further change the value of "count".
-//     - When `Wait()` returns, it is legal to destroy the `BlockingCounter`.
-//     - When `Wait()` returns, the number of calls to `DecrementCount()` on
-//       this blocking counter exactly equals `initial_count`.
-//
-// Example:
-//     BlockingCounter bcount(N);         // there are N items of work
-//     ... Allow worker threads to start.
-//     ... On completing each work item, workers do:
-//     ... bcount.DecrementCount();      // an item of work has been completed
-//
-//     bcount.Wait();                    // wait for all work to be complete
-//
-class BlockingCounter {
- public:
-  explicit BlockingCounter(int initial_count)
-      : count_(initial_count), num_waiting_(0) {}
-
-  BlockingCounter(const BlockingCounter&) = delete;
-  BlockingCounter& operator=(const BlockingCounter&) = delete;
-
-  // BlockingCounter::DecrementCount()
-  //
-  // Decrements the counter's "count" by one, and return "count == 0". This
-  // function requires that "count != 0" when it is called.
-  //
-  // Memory ordering: For any threads X and Y, any action taken by X
-  // before it calls `DecrementCount()` is visible to thread Y after
-  // Y's call to `DecrementCount()`, provided Y's call returns `true`.
-  bool DecrementCount();
-
-  // BlockingCounter::Wait()
-  //
-  // Blocks until the counter reaches zero. This function may be called at most
-  // once. On return, `DecrementCount()` will have been called "initial_count"
-  // times and the blocking counter may be destroyed.
-  //
-  // Memory ordering: For any threads X and Y, any action taken by X
-  // before X calls `DecrementCount()` is visible to Y after Y returns
-  // from `Wait()`.
-  void Wait();
-
- private:
-  Mutex lock_;
-  int count_ ABSL_GUARDED_BY(lock_);
-  int num_waiting_ ABSL_GUARDED_BY(lock_);
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/blocking_counter_test.cc b/third_party/abseil_cpp/absl/synchronization/blocking_counter_test.cc
deleted file mode 100644
index 2926224af7..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/blocking_counter_test.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/blocking_counter.h"
-
-#include <thread>  // NOLINT(build/c++11)
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/time/clock.h"
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-void PauseAndDecreaseCounter(BlockingCounter* counter, int* done) {
-  absl::SleepFor(absl::Seconds(1));
-  *done = 1;
-  counter->DecrementCount();
-}
-
-TEST(BlockingCounterTest, BasicFunctionality) {
-  // This test verifies that BlockingCounter functions correctly. Starts a
-  // number of threads that just sleep for a second and decrement a counter.
-
-  // Initialize the counter.
-  const int num_workers = 10;
-  BlockingCounter counter(num_workers);
-
-  std::vector<std::thread> workers;
-  std::vector<int> done(num_workers, 0);
-
-  // Start a number of parallel tasks that will just wait for a seconds and
-  // then decrement the count.
-  workers.reserve(num_workers);
-  for (int k = 0; k < num_workers; k++) {
-    workers.emplace_back(
-        [&counter, &done, k] { PauseAndDecreaseCounter(&counter, &done[k]); });
-  }
-
-  // Wait for the threads to have all finished.
-  counter.Wait();
-
-  // Check that all the workers have completed.
-  for (int k = 0; k < num_workers; k++) {
-    EXPECT_EQ(1, done[k]);
-  }
-
-  for (std::thread& w : workers) {
-    w.join();
-  }
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/create_thread_identity.cc b/third_party/abseil_cpp/absl/synchronization/internal/create_thread_identity.cc
deleted file mode 100644
index 53a71b342b..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/create_thread_identity.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <stdint.h>
-#include <new>
-
-// This file is a no-op if the required LowLevelAlloc support is missing.
-#include "absl/base/internal/low_level_alloc.h"
-#ifndef ABSL_LOW_LEVEL_ALLOC_MISSING
-
-#include <string.h>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/base/internal/thread_identity.h"
-#include "absl/synchronization/internal/per_thread_sem.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-// ThreadIdentity storage is persistent, we maintain a free-list of previously
-// released ThreadIdentity objects.
-ABSL_CONST_INIT static base_internal::SpinLock freelist_lock(
-    absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
-ABSL_CONST_INIT static base_internal::ThreadIdentity* thread_identity_freelist;
-
-// A per-thread destructor for reclaiming associated ThreadIdentity objects.
-// Since we must preserve their storage we cache them for re-use.
-void ReclaimThreadIdentity(void* v) {
-  base_internal::ThreadIdentity* identity =
-      static_cast<base_internal::ThreadIdentity*>(v);
-
-  // all_locks might have been allocated by the Mutex implementation.
-  // We free it here when we are notified that our thread is dying.
-  if (identity->per_thread_synch.all_locks != nullptr) {
-    base_internal::LowLevelAlloc::Free(identity->per_thread_synch.all_locks);
-  }
-
-  PerThreadSem::Destroy(identity);
-
-  // We must explicitly clear the current thread's identity:
-  // (a) Subsequent (unrelated) per-thread destructors may require an identity.
-  //     We must guarantee a new identity is used in this case (this instructor
-  //     will be reinvoked up to PTHREAD_DESTRUCTOR_ITERATIONS in this case).
-  // (b) ThreadIdentity implementations may depend on memory that is not
-  //     reinitialized before reuse.  We must allow explicit clearing of the
-  //     association state in this case.
-  base_internal::ClearCurrentThreadIdentity();
-  {
-    base_internal::SpinLockHolder l(&freelist_lock);
-    identity->next = thread_identity_freelist;
-    thread_identity_freelist = identity;
-  }
-}
-
-// Return value rounded up to next multiple of align.
-// Align must be a power of two.
-static intptr_t RoundUp(intptr_t addr, intptr_t align) {
-  return (addr + align - 1) & ~(align - 1);
-}
-
-static void ResetThreadIdentity(base_internal::ThreadIdentity* identity) {
-  base_internal::PerThreadSynch* pts = &identity->per_thread_synch;
-  pts->next = nullptr;
-  pts->skip = nullptr;
-  pts->may_skip = false;
-  pts->waitp = nullptr;
-  pts->suppress_fatal_errors = false;
-  pts->readers = 0;
-  pts->priority = 0;
-  pts->next_priority_read_cycles = 0;
-  pts->state.store(base_internal::PerThreadSynch::State::kAvailable,
-                   std::memory_order_relaxed);
-  pts->maybe_unlocking = false;
-  pts->wake = false;
-  pts->cond_waiter = false;
-  pts->all_locks = nullptr;
-  identity->blocked_count_ptr = nullptr;
-  identity->ticker.store(0, std::memory_order_relaxed);
-  identity->wait_start.store(0, std::memory_order_relaxed);
-  identity->is_idle.store(false, std::memory_order_relaxed);
-  identity->next = nullptr;
-}
-
-static base_internal::ThreadIdentity* NewThreadIdentity() {
-  base_internal::ThreadIdentity* identity = nullptr;
-
-  {
-    // Re-use a previously released object if possible.
-    base_internal::SpinLockHolder l(&freelist_lock);
-    if (thread_identity_freelist) {
-      identity = thread_identity_freelist;  // Take list-head.
-      thread_identity_freelist = thread_identity_freelist->next;
-    }
-  }
-
-  if (identity == nullptr) {
-    // Allocate enough space to align ThreadIdentity to a multiple of
-    // PerThreadSynch::kAlignment. This space is never released (it is
-    // added to a freelist by ReclaimThreadIdentity instead).
-    void* allocation = base_internal::LowLevelAlloc::Alloc(
-        sizeof(*identity) + base_internal::PerThreadSynch::kAlignment - 1);
-    // Round up the address to the required alignment.
-    identity = reinterpret_cast<base_internal::ThreadIdentity*>(
-        RoundUp(reinterpret_cast<intptr_t>(allocation),
-                base_internal::PerThreadSynch::kAlignment));
-  }
-  ResetThreadIdentity(identity);
-
-  return identity;
-}
-
-// Allocates and attaches ThreadIdentity object for the calling thread.  Returns
-// the new identity.
-// REQUIRES: CurrentThreadIdentity(false) == nullptr
-base_internal::ThreadIdentity* CreateThreadIdentity() {
-  base_internal::ThreadIdentity* identity = NewThreadIdentity();
-  PerThreadSem::Init(identity);
-  // Associate the value with the current thread, and attach our destructor.
-  base_internal::SetCurrentThreadIdentity(identity, ReclaimThreadIdentity);
-  return identity;
-}
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/create_thread_identity.h b/third_party/abseil_cpp/absl/synchronization/internal/create_thread_identity.h
deleted file mode 100644
index e121f68377..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/create_thread_identity.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2017 The Abseil Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Interface for getting the current ThreadIdentity, creating one if necessary.
-// See thread_identity.h.
-//
-// This file is separate from thread_identity.h because creating a new
-// ThreadIdentity requires slightly higher level libraries (per_thread_sem
-// and low_level_alloc) than accessing an existing one.  This separation allows
-// us to have a smaller //absl/base:base.
-
-#ifndef ABSL_SYNCHRONIZATION_INTERNAL_CREATE_THREAD_IDENTITY_H_
-#define ABSL_SYNCHRONIZATION_INTERNAL_CREATE_THREAD_IDENTITY_H_
-
-#include "absl/base/internal/thread_identity.h"
-#include "absl/base/port.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-// Allocates and attaches a ThreadIdentity object for the calling thread.
-// For private use only.
-base_internal::ThreadIdentity* CreateThreadIdentity();
-
-// A per-thread destructor for reclaiming associated ThreadIdentity objects.
-// For private use only.
-void ReclaimThreadIdentity(void* v);
-
-// Returns the ThreadIdentity object representing the calling thread; guaranteed
-// to be unique for its lifetime.  The returned object will remain valid for the
-// program's lifetime; although it may be re-assigned to a subsequent thread.
-// If one does not exist for the calling thread, allocate it now.
-inline base_internal::ThreadIdentity* GetOrCreateCurrentThreadIdentity() {
-  base_internal::ThreadIdentity* identity =
-      base_internal::CurrentThreadIdentityIfPresent();
-  if (ABSL_PREDICT_FALSE(identity == nullptr)) {
-    return CreateThreadIdentity();
-  }
-  return identity;
-}
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_SYNCHRONIZATION_INTERNAL_CREATE_THREAD_IDENTITY_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/futex.h b/third_party/abseil_cpp/absl/synchronization/internal/futex.h
deleted file mode 100644
index 06fbd6d072..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/futex.h
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright 2020 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#ifndef ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
-#define ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
-
-#include "absl/base/config.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#else
-#include <sys/time.h>
-#include <unistd.h>
-#endif
-
-#ifdef __linux__
-#include <linux/futex.h>
-#include <sys/syscall.h>
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#include <time.h>
-
-#include <atomic>
-#include <cstdint>
-
-#include "absl/base/optimization.h"
-#include "absl/synchronization/internal/kernel_timeout.h"
-
-#ifdef ABSL_INTERNAL_HAVE_FUTEX
-#error ABSL_INTERNAL_HAVE_FUTEX may not be set on the command line
-#elif defined(__BIONIC__)
-// Bionic supports all the futex operations we need even when some of the futex
-// definitions are missing.
-#define ABSL_INTERNAL_HAVE_FUTEX
-#elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME)
-// FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28.
-#define ABSL_INTERNAL_HAVE_FUTEX
-#endif
-
-#ifdef ABSL_INTERNAL_HAVE_FUTEX
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-// Some Android headers are missing these definitions even though they
-// support these futex operations.
-#ifdef __BIONIC__
-#ifndef SYS_futex
-#define SYS_futex __NR_futex
-#endif
-#ifndef FUTEX_WAIT_BITSET
-#define FUTEX_WAIT_BITSET 9
-#endif
-#ifndef FUTEX_PRIVATE_FLAG
-#define FUTEX_PRIVATE_FLAG 128
-#endif
-#ifndef FUTEX_CLOCK_REALTIME
-#define FUTEX_CLOCK_REALTIME 256
-#endif
-#ifndef FUTEX_BITSET_MATCH_ANY
-#define FUTEX_BITSET_MATCH_ANY 0xFFFFFFFF
-#endif
-#endif
-
-#if defined(__NR_futex_time64) && !defined(SYS_futex_time64)
-#define SYS_futex_time64 __NR_futex_time64
-#endif
-
-#if defined(SYS_futex_time64) && !defined(SYS_futex)
-#define SYS_futex SYS_futex_time64
-#endif
-
-class FutexImpl {
- public:
-  static int WaitUntil(std::atomic<int32_t> *v, int32_t val,
-                       KernelTimeout t) {
-    int err = 0;
-    if (t.has_timeout()) {
-      // https://locklessinc.com/articles/futex_cheat_sheet/
-      // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
-      struct timespec abs_timeout = t.MakeAbsTimespec();
-      // Atomically check that the futex value is still 0, and if it
-      // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
-      err = syscall(
-          SYS_futex, reinterpret_cast<int32_t *>(v),
-          FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val,
-          &abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY);
-    } else {
-      // Atomically check that the futex value is still 0, and if it
-      // is, sleep until woken by FUTEX_WAKE.
-      err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v),
-                    FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, nullptr);
-    }
-    if (ABSL_PREDICT_FALSE(err != 0)) {
-      err = -errno;
-    }
-    return err;
-  }
-
-  static int WaitBitsetAbsoluteTimeout(std::atomic<int32_t> *v, int32_t val,
-                                       int32_t bits,
-                                       const struct timespec *abstime) {
-    int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v),
-                      FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, val, abstime,
-                      nullptr, bits);
-    if (ABSL_PREDICT_FALSE(err != 0)) {
-      err = -errno;
-    }
-    return err;
-  }
-
-  static int Wake(std::atomic<int32_t> *v, int32_t count) {
-    int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v),
-                      FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count);
-    if (ABSL_PREDICT_FALSE(err < 0)) {
-      err = -errno;
-    }
-    return err;
-  }
-
-  // FUTEX_WAKE_BITSET
-  static int WakeBitset(std::atomic<int32_t> *v, int32_t count, int32_t bits) {
-    int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v),
-                      FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, count, nullptr,
-                      nullptr, bits);
-    if (ABSL_PREDICT_FALSE(err < 0)) {
-      err = -errno;
-    }
-    return err;
-  }
-};
-
-class Futex : public FutexImpl {};
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_INTERNAL_HAVE_FUTEX
-
-#endif  // ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/graphcycles.cc b/third_party/abseil_cpp/absl/synchronization/internal/graphcycles.cc
deleted file mode 100644
index 27fec21681..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/graphcycles.cc
+++ /dev/null
@@ -1,698 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// GraphCycles provides incremental cycle detection on a dynamic
-// graph using the following algorithm:
-//
-// A dynamic topological sort algorithm for directed acyclic graphs
-// David J. Pearce, Paul H. J. Kelly
-// Journal of Experimental Algorithmics (JEA) JEA Homepage archive
-// Volume 11, 2006, Article No. 1.7
-//
-// Brief summary of the algorithm:
-//
-// (1) Maintain a rank for each node that is consistent
-//     with the topological sort of the graph. I.e., path from x to y
-//     implies rank[x] < rank[y].
-// (2) When a new edge (x->y) is inserted, do nothing if rank[x] < rank[y].
-// (3) Otherwise: adjust ranks in the neighborhood of x and y.
-
-#include "absl/base/attributes.h"
-// This file is a no-op if the required LowLevelAlloc support is missing.
-#include "absl/base/internal/low_level_alloc.h"
-#ifndef ABSL_LOW_LEVEL_ALLOC_MISSING
-
-#include "absl/synchronization/internal/graphcycles.h"
-
-#include <algorithm>
-#include <array>
-#include <limits>
-#include "absl/base/internal/hide_ptr.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/spinlock.h"
-
-// Do not use STL.   This module does not use standard memory allocation.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-namespace {
-
-// Avoid LowLevelAlloc's default arena since it calls malloc hooks in
-// which people are doing things like acquiring Mutexes.
-ABSL_CONST_INIT static absl::base_internal::SpinLock arena_mu(
-    absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
-ABSL_CONST_INIT static base_internal::LowLevelAlloc::Arena* arena;
-
-static void InitArenaIfNecessary() {
-  arena_mu.Lock();
-  if (arena == nullptr) {
-    arena = base_internal::LowLevelAlloc::NewArena(0);
-  }
-  arena_mu.Unlock();
-}
-
-// Number of inlined elements in Vec.  Hash table implementation
-// relies on this being a power of two.
-static const uint32_t kInline = 8;
-
-// A simple LowLevelAlloc based resizable vector with inlined storage
-// for a few elements.  T must be a plain type since constructor
-// and destructor are not run on elements of type T managed by Vec.
-template <typename T>
-class Vec {
- public:
-  Vec() { Init(); }
-  ~Vec() { Discard(); }
-
-  void clear() {
-    Discard();
-    Init();
-  }
-
-  bool empty() const { return size_ == 0; }
-  uint32_t size() const { return size_; }
-  T* begin() { return ptr_; }
-  T* end() { return ptr_ + size_; }
-  const T& operator[](uint32_t i) const { return ptr_[i]; }
-  T& operator[](uint32_t i) { return ptr_[i]; }
-  const T& back() const { return ptr_[size_-1]; }
-  void pop_back() { size_--; }
-
-  void push_back(const T& v) {
-    if (size_ == capacity_) Grow(size_ + 1);
-    ptr_[size_] = v;
-    size_++;
-  }
-
-  void resize(uint32_t n) {
-    if (n > capacity_) Grow(n);
-    size_ = n;
-  }
-
-  void fill(const T& val) {
-    for (uint32_t i = 0; i < size(); i++) {
-      ptr_[i] = val;
-    }
-  }
-
-  // Guarantees src is empty at end.
-  // Provided for the hash table resizing code below.
-  void MoveFrom(Vec<T>* src) {
-    if (src->ptr_ == src->space_) {
-      // Need to actually copy
-      resize(src->size_);
-      std::copy(src->ptr_, src->ptr_ + src->size_, ptr_);
-      src->size_ = 0;
-    } else {
-      Discard();
-      ptr_ = src->ptr_;
-      size_ = src->size_;
-      capacity_ = src->capacity_;
-      src->Init();
-    }
-  }
-
- private:
-  T* ptr_;
-  T space_[kInline];
-  uint32_t size_;
-  uint32_t capacity_;
-
-  void Init() {
-    ptr_ = space_;
-    size_ = 0;
-    capacity_ = kInline;
-  }
-
-  void Discard() {
-    if (ptr_ != space_) base_internal::LowLevelAlloc::Free(ptr_);
-  }
-
-  void Grow(uint32_t n) {
-    while (capacity_ < n) {
-      capacity_ *= 2;
-    }
-    size_t request = static_cast<size_t>(capacity_) * sizeof(T);
-    T* copy = static_cast<T*>(
-        base_internal::LowLevelAlloc::AllocWithArena(request, arena));
-    std::copy(ptr_, ptr_ + size_, copy);
-    Discard();
-    ptr_ = copy;
-  }
-
-  Vec(const Vec&) = delete;
-  Vec& operator=(const Vec&) = delete;
-};
-
-// A hash set of non-negative int32_t that uses Vec for its underlying storage.
-class NodeSet {
- public:
-  NodeSet() { Init(); }
-
-  void clear() { Init(); }
-  bool contains(int32_t v) const { return table_[FindIndex(v)] == v; }
-
-  bool insert(int32_t v) {
-    uint32_t i = FindIndex(v);
-    if (table_[i] == v) {
-      return false;
-    }
-    if (table_[i] == kEmpty) {
-      // Only inserting over an empty cell increases the number of occupied
-      // slots.
-      occupied_++;
-    }
-    table_[i] = v;
-    // Double when 75% full.
-    if (occupied_ >= table_.size() - table_.size()/4) Grow();
-    return true;
-  }
-
-  void erase(uint32_t v) {
-    uint32_t i = FindIndex(v);
-    if (static_cast<uint32_t>(table_[i]) == v) {
-      table_[i] = kDel;
-    }
-  }
-
-  // Iteration: is done via HASH_FOR_EACH
-  // Example:
-  //    HASH_FOR_EACH(elem, node->out) { ... }
-#define HASH_FOR_EACH(elem, eset) \
-  for (int32_t elem, _cursor = 0; (eset).Next(&_cursor, &elem); )
-  bool Next(int32_t* cursor, int32_t* elem) {
-    while (static_cast<uint32_t>(*cursor) < table_.size()) {
-      int32_t v = table_[*cursor];
-      (*cursor)++;
-      if (v >= 0) {
-        *elem = v;
-        return true;
-      }
-    }
-    return false;
-  }
-
- private:
-  enum : int32_t { kEmpty = -1, kDel = -2 };
-  Vec<int32_t> table_;
-  uint32_t occupied_;     // Count of non-empty slots (includes deleted slots)
-
-  static uint32_t Hash(uint32_t a) { return a * 41; }
-
-  // Return index for storing v.  May return an empty index or deleted index
-  int FindIndex(int32_t v) const {
-    // Search starting at hash index.
-    const uint32_t mask = table_.size() - 1;
-    uint32_t i = Hash(v) & mask;
-    int deleted_index = -1;  // If >= 0, index of first deleted element we see
-    while (true) {
-      int32_t e = table_[i];
-      if (v == e) {
-        return i;
-      } else if (e == kEmpty) {
-        // Return any previously encountered deleted slot.
-        return (deleted_index >= 0) ? deleted_index : i;
-      } else if (e == kDel && deleted_index < 0) {
-        // Keep searching since v might be present later.
-        deleted_index = i;
-      }
-      i = (i + 1) & mask;  // Linear probing; quadratic is slightly slower.
-    }
-  }
-
-  void Init() {
-    table_.clear();
-    table_.resize(kInline);
-    table_.fill(kEmpty);
-    occupied_ = 0;
-  }
-
-  void Grow() {
-    Vec<int32_t> copy;
-    copy.MoveFrom(&table_);
-    occupied_ = 0;
-    table_.resize(copy.size() * 2);
-    table_.fill(kEmpty);
-
-    for (const auto& e : copy) {
-      if (e >= 0) insert(e);
-    }
-  }
-
-  NodeSet(const NodeSet&) = delete;
-  NodeSet& operator=(const NodeSet&) = delete;
-};
-
-// We encode a node index and a node version in GraphId.  The version
-// number is incremented when the GraphId is freed which automatically
-// invalidates all copies of the GraphId.
-
-inline GraphId MakeId(int32_t index, uint32_t version) {
-  GraphId g;
-  g.handle =
-      (static_cast<uint64_t>(version) << 32) | static_cast<uint32_t>(index);
-  return g;
-}
-
-inline int32_t NodeIndex(GraphId id) {
-  return static_cast<uint32_t>(id.handle & 0xfffffffful);
-}
-
-inline uint32_t NodeVersion(GraphId id) {
-  return static_cast<uint32_t>(id.handle >> 32);
-}
-
-struct Node {
-  int32_t rank;               // rank number assigned by Pearce-Kelly algorithm
-  uint32_t version;           // Current version number
-  int32_t next_hash;          // Next entry in hash table
-  bool visited;               // Temporary marker used by depth-first-search
-  uintptr_t masked_ptr;       // User-supplied pointer
-  NodeSet in;                 // List of immediate predecessor nodes in graph
-  NodeSet out;                // List of immediate successor nodes in graph
-  int priority;               // Priority of recorded stack trace.
-  int nstack;                 // Depth of recorded stack trace.
-  void* stack[40];            // stack[0,nstack-1] holds stack trace for node.
-};
-
-// Hash table for pointer to node index lookups.
-class PointerMap {
- public:
-  explicit PointerMap(const Vec<Node*>* nodes) : nodes_(nodes) {
-    table_.fill(-1);
-  }
-
-  int32_t Find(void* ptr) {
-    auto masked = base_internal::HidePtr(ptr);
-    for (int32_t i = table_[Hash(ptr)]; i != -1;) {
-      Node* n = (*nodes_)[i];
-      if (n->masked_ptr == masked) return i;
-      i = n->next_hash;
-    }
-    return -1;
-  }
-
-  void Add(void* ptr, int32_t i) {
-    int32_t* head = &table_[Hash(ptr)];
-    (*nodes_)[i]->next_hash = *head;
-    *head = i;
-  }
-
-  int32_t Remove(void* ptr) {
-    // Advance through linked list while keeping track of the
-    // predecessor slot that points to the current entry.
-    auto masked = base_internal::HidePtr(ptr);
-    for (int32_t* slot = &table_[Hash(ptr)]; *slot != -1; ) {
-      int32_t index = *slot;
-      Node* n = (*nodes_)[index];
-      if (n->masked_ptr == masked) {
-        *slot = n->next_hash;  // Remove n from linked list
-        n->next_hash = -1;
-        return index;
-      }
-      slot = &n->next_hash;
-    }
-    return -1;
-  }
-
- private:
-  // Number of buckets in hash table for pointer lookups.
-  static constexpr uint32_t kHashTableSize = 8171;  // should be prime
-
-  const Vec<Node*>* nodes_;
-  std::array<int32_t, kHashTableSize> table_;
-
-  static uint32_t Hash(void* ptr) {
-    return reinterpret_cast<uintptr_t>(ptr) % kHashTableSize;
-  }
-};
-
-}  // namespace
-
-struct GraphCycles::Rep {
-  Vec<Node*> nodes_;
-  Vec<int32_t> free_nodes_;  // Indices for unused entries in nodes_
-  PointerMap ptrmap_;
-
-  // Temporary state.
-  Vec<int32_t> deltaf_;  // Results of forward DFS
-  Vec<int32_t> deltab_;  // Results of backward DFS
-  Vec<int32_t> list_;    // All nodes to reprocess
-  Vec<int32_t> merged_;  // Rank values to assign to list_ entries
-  Vec<int32_t> stack_;   // Emulates recursion stack for depth-first searches
-
-  Rep() : ptrmap_(&nodes_) {}
-};
-
-static Node* FindNode(GraphCycles::Rep* rep, GraphId id) {
-  Node* n = rep->nodes_[NodeIndex(id)];
-  return (n->version == NodeVersion(id)) ? n : nullptr;
-}
-
-GraphCycles::GraphCycles() {
-  InitArenaIfNecessary();
-  rep_ = new (base_internal::LowLevelAlloc::AllocWithArena(sizeof(Rep), arena))
-      Rep;
-}
-
-GraphCycles::~GraphCycles() {
-  for (auto* node : rep_->nodes_) {
-    node->Node::~Node();
-    base_internal::LowLevelAlloc::Free(node);
-  }
-  rep_->Rep::~Rep();
-  base_internal::LowLevelAlloc::Free(rep_);
-}
-
-bool GraphCycles::CheckInvariants() const {
-  Rep* r = rep_;
-  NodeSet ranks;  // Set of ranks seen so far.
-  for (uint32_t x = 0; x < r->nodes_.size(); x++) {
-    Node* nx = r->nodes_[x];
-    void* ptr = base_internal::UnhidePtr<void>(nx->masked_ptr);
-    if (ptr != nullptr && static_cast<uint32_t>(r->ptrmap_.Find(ptr)) != x) {
-      ABSL_RAW_LOG(FATAL, "Did not find live node in hash table %u %p", x, ptr);
-    }
-    if (nx->visited) {
-      ABSL_RAW_LOG(FATAL, "Did not clear visited marker on node %u", x);
-    }
-    if (!ranks.insert(nx->rank)) {
-      ABSL_RAW_LOG(FATAL, "Duplicate occurrence of rank %d", nx->rank);
-    }
-    HASH_FOR_EACH(y, nx->out) {
-      Node* ny = r->nodes_[y];
-      if (nx->rank >= ny->rank) {
-        ABSL_RAW_LOG(FATAL, "Edge %u->%d has bad rank assignment %d->%d", x, y,
-                     nx->rank, ny->rank);
-      }
-    }
-  }
-  return true;
-}
-
-GraphId GraphCycles::GetId(void* ptr) {
-  int32_t i = rep_->ptrmap_.Find(ptr);
-  if (i != -1) {
-    return MakeId(i, rep_->nodes_[i]->version);
-  } else if (rep_->free_nodes_.empty()) {
-    Node* n =
-        new (base_internal::LowLevelAlloc::AllocWithArena(sizeof(Node), arena))
-            Node;
-    n->version = 1;  // Avoid 0 since it is used by InvalidGraphId()
-    n->visited = false;
-    n->rank = rep_->nodes_.size();
-    n->masked_ptr = base_internal::HidePtr(ptr);
-    n->nstack = 0;
-    n->priority = 0;
-    rep_->nodes_.push_back(n);
-    rep_->ptrmap_.Add(ptr, n->rank);
-    return MakeId(n->rank, n->version);
-  } else {
-    // Preserve preceding rank since the set of ranks in use must be
-    // a permutation of [0,rep_->nodes_.size()-1].
-    int32_t r = rep_->free_nodes_.back();
-    rep_->free_nodes_.pop_back();
-    Node* n = rep_->nodes_[r];
-    n->masked_ptr = base_internal::HidePtr(ptr);
-    n->nstack = 0;
-    n->priority = 0;
-    rep_->ptrmap_.Add(ptr, r);
-    return MakeId(r, n->version);
-  }
-}
-
-void GraphCycles::RemoveNode(void* ptr) {
-  int32_t i = rep_->ptrmap_.Remove(ptr);
-  if (i == -1) {
-    return;
-  }
-  Node* x = rep_->nodes_[i];
-  HASH_FOR_EACH(y, x->out) {
-    rep_->nodes_[y]->in.erase(i);
-  }
-  HASH_FOR_EACH(y, x->in) {
-    rep_->nodes_[y]->out.erase(i);
-  }
-  x->in.clear();
-  x->out.clear();
-  x->masked_ptr = base_internal::HidePtr<void>(nullptr);
-  if (x->version == std::numeric_limits<uint32_t>::max()) {
-    // Cannot use x any more
-  } else {
-    x->version++;  // Invalidates all copies of node.
-    rep_->free_nodes_.push_back(i);
-  }
-}
-
-void* GraphCycles::Ptr(GraphId id) {
-  Node* n = FindNode(rep_, id);
-  return n == nullptr ? nullptr
-                      : base_internal::UnhidePtr<void>(n->masked_ptr);
-}
-
-bool GraphCycles::HasNode(GraphId node) {
-  return FindNode(rep_, node) != nullptr;
-}
-
-bool GraphCycles::HasEdge(GraphId x, GraphId y) const {
-  Node* xn = FindNode(rep_, x);
-  return xn && FindNode(rep_, y) && xn->out.contains(NodeIndex(y));
-}
-
-void GraphCycles::RemoveEdge(GraphId x, GraphId y) {
-  Node* xn = FindNode(rep_, x);
-  Node* yn = FindNode(rep_, y);
-  if (xn && yn) {
-    xn->out.erase(NodeIndex(y));
-    yn->in.erase(NodeIndex(x));
-    // No need to update the rank assignment since a previous valid
-    // rank assignment remains valid after an edge deletion.
-  }
-}
-
-static bool ForwardDFS(GraphCycles::Rep* r, int32_t n, int32_t upper_bound);
-static void BackwardDFS(GraphCycles::Rep* r, int32_t n, int32_t lower_bound);
-static void Reorder(GraphCycles::Rep* r);
-static void Sort(const Vec<Node*>&, Vec<int32_t>* delta);
-static void MoveToList(
-    GraphCycles::Rep* r, Vec<int32_t>* src, Vec<int32_t>* dst);
-
-bool GraphCycles::InsertEdge(GraphId idx, GraphId idy) {
-  Rep* r = rep_;
-  const int32_t x = NodeIndex(idx);
-  const int32_t y = NodeIndex(idy);
-  Node* nx = FindNode(r, idx);
-  Node* ny = FindNode(r, idy);
-  if (nx == nullptr || ny == nullptr) return true;  // Expired ids
-
-  if (nx == ny) return false;  // Self edge
-  if (!nx->out.insert(y)) {
-    // Edge already exists.
-    return true;
-  }
-
-  ny->in.insert(x);
-
-  if (nx->rank <= ny->rank) {
-    // New edge is consistent with existing rank assignment.
-    return true;
-  }
-
-  // Current rank assignments are incompatible with the new edge.  Recompute.
-  // We only need to consider nodes that fall in the range [ny->rank,nx->rank].
-  if (!ForwardDFS(r, y, nx->rank)) {
-    // Found a cycle.  Undo the insertion and tell caller.
-    nx->out.erase(y);
-    ny->in.erase(x);
-    // Since we do not call Reorder() on this path, clear any visited
-    // markers left by ForwardDFS.
-    for (const auto& d : r->deltaf_) {
-      r->nodes_[d]->visited = false;
-    }
-    return false;
-  }
-  BackwardDFS(r, x, ny->rank);
-  Reorder(r);
-  return true;
-}
-
-static bool ForwardDFS(GraphCycles::Rep* r, int32_t n, int32_t upper_bound) {
-  // Avoid recursion since stack space might be limited.
-  // We instead keep a stack of nodes to visit.
-  r->deltaf_.clear();
-  r->stack_.clear();
-  r->stack_.push_back(n);
-  while (!r->stack_.empty()) {
-    n = r->stack_.back();
-    r->stack_.pop_back();
-    Node* nn = r->nodes_[n];
-    if (nn->visited) continue;
-
-    nn->visited = true;
-    r->deltaf_.push_back(n);
-
-    HASH_FOR_EACH(w, nn->out) {
-      Node* nw = r->nodes_[w];
-      if (nw->rank == upper_bound) {
-        return false;  // Cycle
-      }
-      if (!nw->visited && nw->rank < upper_bound) {
-        r->stack_.push_back(w);
-      }
-    }
-  }
-  return true;
-}
-
-static void BackwardDFS(GraphCycles::Rep* r, int32_t n, int32_t lower_bound) {
-  r->deltab_.clear();
-  r->stack_.clear();
-  r->stack_.push_back(n);
-  while (!r->stack_.empty()) {
-    n = r->stack_.back();
-    r->stack_.pop_back();
-    Node* nn = r->nodes_[n];
-    if (nn->visited) continue;
-
-    nn->visited = true;
-    r->deltab_.push_back(n);
-
-    HASH_FOR_EACH(w, nn->in) {
-      Node* nw = r->nodes_[w];
-      if (!nw->visited && lower_bound < nw->rank) {
-        r->stack_.push_back(w);
-      }
-    }
-  }
-}
-
-static void Reorder(GraphCycles::Rep* r) {
-  Sort(r->nodes_, &r->deltab_);
-  Sort(r->nodes_, &r->deltaf_);
-
-  // Adds contents of delta lists to list_ (backwards deltas first).
-  r->list_.clear();
-  MoveToList(r, &r->deltab_, &r->list_);
-  MoveToList(r, &r->deltaf_, &r->list_);
-
-  // Produce sorted list of all ranks that will be reassigned.
-  r->merged_.resize(r->deltab_.size() + r->deltaf_.size());
-  std::merge(r->deltab_.begin(), r->deltab_.end(),
-             r->deltaf_.begin(), r->deltaf_.end(),
-             r->merged_.begin());
-
-  // Assign the ranks in order to the collected list.
-  for (uint32_t i = 0; i < r->list_.size(); i++) {
-    r->nodes_[r->list_[i]]->rank = r->merged_[i];
-  }
-}
-
-static void Sort(const Vec<Node*>& nodes, Vec<int32_t>* delta) {
-  struct ByRank {
-    const Vec<Node*>* nodes;
-    bool operator()(int32_t a, int32_t b) const {
-      return (*nodes)[a]->rank < (*nodes)[b]->rank;
-    }
-  };
-  ByRank cmp;
-  cmp.nodes = &nodes;
-  std::sort(delta->begin(), delta->end(), cmp);
-}
-
-static void MoveToList(
-    GraphCycles::Rep* r, Vec<int32_t>* src, Vec<int32_t>* dst) {
-  for (auto& v : *src) {
-    int32_t w = v;
-    v = r->nodes_[w]->rank;         // Replace v entry with its rank
-    r->nodes_[w]->visited = false;  // Prepare for future DFS calls
-    dst->push_back(w);
-  }
-}
-
-int GraphCycles::FindPath(GraphId idx, GraphId idy, int max_path_len,
-                          GraphId path[]) const {
-  Rep* r = rep_;
-  if (FindNode(r, idx) == nullptr || FindNode(r, idy) == nullptr) return 0;
-  const int32_t x = NodeIndex(idx);
-  const int32_t y = NodeIndex(idy);
-
-  // Forward depth first search starting at x until we hit y.
-  // As we descend into a node, we push it onto the path.
-  // As we leave a node, we remove it from the path.
-  int path_len = 0;
-
-  NodeSet seen;
-  r->stack_.clear();
-  r->stack_.push_back(x);
-  while (!r->stack_.empty()) {
-    int32_t n = r->stack_.back();
-    r->stack_.pop_back();
-    if (n < 0) {
-      // Marker to indicate that we are leaving a node
-      path_len--;
-      continue;
-    }
-
-    if (path_len < max_path_len) {
-      path[path_len] = MakeId(n, rep_->nodes_[n]->version);
-    }
-    path_len++;
-    r->stack_.push_back(-1);  // Will remove tentative path entry
-
-    if (n == y) {
-      return path_len;
-    }
-
-    HASH_FOR_EACH(w, r->nodes_[n]->out) {
-      if (seen.insert(w)) {
-        r->stack_.push_back(w);
-      }
-    }
-  }
-
-  return 0;
-}
-
-bool GraphCycles::IsReachable(GraphId x, GraphId y) const {
-  return FindPath(x, y, 0, nullptr) > 0;
-}
-
-void GraphCycles::UpdateStackTrace(GraphId id, int priority,
-                                   int (*get_stack_trace)(void** stack, int)) {
-  Node* n = FindNode(rep_, id);
-  if (n == nullptr || n->priority >= priority) {
-    return;
-  }
-  n->nstack = (*get_stack_trace)(n->stack, ABSL_ARRAYSIZE(n->stack));
-  n->priority = priority;
-}
-
-int GraphCycles::GetStackTrace(GraphId id, void*** ptr) {
-  Node* n = FindNode(rep_, id);
-  if (n == nullptr) {
-    *ptr = nullptr;
-    return 0;
-  } else {
-    *ptr = n->stack;
-    return n->nstack;
-  }
-}
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/graphcycles.h b/third_party/abseil_cpp/absl/synchronization/internal/graphcycles.h
deleted file mode 100644
index ceba33e4de..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/graphcycles.h
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_
-#define ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_
-
-// GraphCycles detects the introduction of a cycle into a directed
-// graph that is being built up incrementally.
-//
-// Nodes are identified by small integers.  It is not possible to
-// record multiple edges with the same (source, destination) pair;
-// requests to add an edge where one already exists are silently
-// ignored.
-//
-// It is also not possible to introduce a cycle; an attempt to insert
-// an edge that would introduce a cycle fails and returns false.
-//
-// GraphCycles uses no internal locking; calls into it should be
-// serialized externally.
-
-// Performance considerations:
-//   Works well on sparse graphs, poorly on dense graphs.
-//   Extra information is maintained incrementally to detect cycles quickly.
-//   InsertEdge() is very fast when the edge already exists, and reasonably fast
-//   otherwise.
-//   FindPath() is linear in the size of the graph.
-// The current implementation uses O(|V|+|E|) space.
-
-#include <cstdint>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-// Opaque identifier for a graph node.
-struct GraphId {
-  uint64_t handle;
-
-  bool operator==(const GraphId& x) const { return handle == x.handle; }
-  bool operator!=(const GraphId& x) const { return handle != x.handle; }
-};
-
-// Return an invalid graph id that will never be assigned by GraphCycles.
-inline GraphId InvalidGraphId() {
-  return GraphId{0};
-}
-
-class GraphCycles {
- public:
-  GraphCycles();
-  ~GraphCycles();
-
-  // Return the id to use for ptr, assigning one if necessary.
-  // Subsequent calls with the same ptr value will return the same id
-  // until Remove().
-  GraphId GetId(void* ptr);
-
-  // Remove "ptr" from the graph.  Its corresponding node and all
-  // edges to and from it are removed.
-  void RemoveNode(void* ptr);
-
-  // Return the pointer associated with id, or nullptr if id is not
-  // currently in the graph.
-  void* Ptr(GraphId id);
-
-  // Attempt to insert an edge from source_node to dest_node.  If the
-  // edge would introduce a cycle, return false without making any
-  // changes. Otherwise add the edge and return true.
-  bool InsertEdge(GraphId source_node, GraphId dest_node);
-
-  // Remove any edge that exists from source_node to dest_node.
-  void RemoveEdge(GraphId source_node, GraphId dest_node);
-
-  // Return whether node exists in the graph.
-  bool HasNode(GraphId node);
-
-  // Return whether there is an edge directly from source_node to dest_node.
-  bool HasEdge(GraphId source_node, GraphId dest_node) const;
-
-  // Return whether dest_node is reachable from source_node
-  // by following edges.
-  bool IsReachable(GraphId source_node, GraphId dest_node) const;
-
-  // Find a path from "source" to "dest".  If such a path exists,
-  // place the nodes on the path in the array path[], and return
-  // the number of nodes on the path.  If the path is longer than
-  // max_path_len nodes, only the first max_path_len nodes are placed
-  // in path[].  The client should compare the return value with
-  // max_path_len" to see when this occurs.  If no path exists, return
-  // 0.  Any valid path stored in path[] will start with "source" and
-  // end with "dest".  There is no guarantee that the path is the
-  // shortest, but no node will appear twice in the path, except the
-  // source and destination node if they are identical; therefore, the
-  // return value is at most one greater than the number of nodes in
-  // the graph.
-  int FindPath(GraphId source, GraphId dest, int max_path_len,
-               GraphId path[]) const;
-
-  // Update the stack trace recorded for id with the current stack
-  // trace if the last time it was updated had a smaller priority
-  // than the priority passed on this call.
-  //
-  // *get_stack_trace is called to get the stack trace.
-  void UpdateStackTrace(GraphId id, int priority,
-                        int (*get_stack_trace)(void**, int));
-
-  // Set *ptr to the beginning of the array that holds the recorded
-  // stack trace for id and return the depth of the stack trace.
-  int GetStackTrace(GraphId id, void*** ptr);
-
-  // Check internal invariants. Crashes on failure, returns true on success.
-  // Expensive: should only be called from graphcycles_test.cc.
-  bool CheckInvariants() const;
-
-  // ----------------------------------------------------
-  struct Rep;
- private:
-  Rep *rep_;      // opaque representation
-  GraphCycles(const GraphCycles&) = delete;
-  GraphCycles& operator=(const GraphCycles&) = delete;
-};
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/graphcycles_benchmark.cc b/third_party/abseil_cpp/absl/synchronization/internal/graphcycles_benchmark.cc
deleted file mode 100644
index 54823e0ba5..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/graphcycles_benchmark.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/internal/graphcycles.h"
-
-#include <algorithm>
-#include <cstdint>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace {
-
-void BM_StressTest(benchmark::State& state) {
-  const int num_nodes = state.range(0);
-  while (state.KeepRunningBatch(num_nodes)) {
-    absl::synchronization_internal::GraphCycles g;
-    std::vector<absl::synchronization_internal::GraphId> nodes(num_nodes);
-    for (int i = 0; i < num_nodes; i++) {
-      nodes[i] = g.GetId(reinterpret_cast<void*>(static_cast<uintptr_t>(i)));
-    }
-    for (int i = 0; i < num_nodes; i++) {
-      int end = std::min(num_nodes, i + 5);
-      for (int j = i + 1; j < end; j++) {
-        ABSL_RAW_CHECK(g.InsertEdge(nodes[i], nodes[j]), "");
-      }
-    }
-  }
-}
-BENCHMARK(BM_StressTest)->Range(2048, 1048576);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/graphcycles_test.cc b/third_party/abseil_cpp/absl/synchronization/internal/graphcycles_test.cc
deleted file mode 100644
index 74eaffe7a8..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/graphcycles_test.cc
+++ /dev/null
@@ -1,464 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/internal/graphcycles.h"
-
-#include <map>
-#include <random>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/macros.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-// We emulate a GraphCycles object with a node vector and an edge vector.
-// We then compare the two implementations.
-
-using Nodes = std::vector<int>;
-struct Edge {
-  int from;
-  int to;
-};
-using Edges = std::vector<Edge>;
-using RandomEngine = std::mt19937_64;
-
-// Mapping from integer index to GraphId.
-typedef std::map<int, GraphId> IdMap;
-static GraphId Get(const IdMap& id, int num) {
-  auto iter = id.find(num);
-  return (iter == id.end()) ? InvalidGraphId() : iter->second;
-}
-
-// Return whether "to" is reachable from "from".
-static bool IsReachable(Edges *edges, int from, int to,
-                        std::unordered_set<int> *seen) {
-  seen->insert(from);     // we are investigating "from"; don't do it again
-  if (from == to) return true;
-  for (const auto &edge : *edges) {
-    if (edge.from == from) {
-      if (edge.to == to) {  // success via edge directly
-        return true;
-      } else if (seen->find(edge.to) == seen->end() &&  // success via edge
-                 IsReachable(edges, edge.to, to, seen)) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-static void PrintEdges(Edges *edges) {
-  ABSL_RAW_LOG(INFO, "EDGES (%zu)", edges->size());
-  for (const auto &edge : *edges) {
-    int a = edge.from;
-    int b = edge.to;
-    ABSL_RAW_LOG(INFO, "%d %d", a, b);
-  }
-  ABSL_RAW_LOG(INFO, "---");
-}
-
-static void PrintGCEdges(Nodes *nodes, const IdMap &id, GraphCycles *gc) {
-  ABSL_RAW_LOG(INFO, "GC EDGES");
-  for (int a : *nodes) {
-    for (int b : *nodes) {
-      if (gc->HasEdge(Get(id, a), Get(id, b))) {
-        ABSL_RAW_LOG(INFO, "%d %d", a, b);
-      }
-    }
-  }
-  ABSL_RAW_LOG(INFO, "---");
-}
-
-static void PrintTransitiveClosure(Nodes *nodes, Edges *edges) {
-  ABSL_RAW_LOG(INFO, "Transitive closure");
-  for (int a : *nodes) {
-    for (int b : *nodes) {
-      std::unordered_set<int> seen;
-      if (IsReachable(edges, a, b, &seen)) {
-        ABSL_RAW_LOG(INFO, "%d %d", a, b);
-      }
-    }
-  }
-  ABSL_RAW_LOG(INFO, "---");
-}
-
-static void PrintGCTransitiveClosure(Nodes *nodes, const IdMap &id,
-                                     GraphCycles *gc) {
-  ABSL_RAW_LOG(INFO, "GC Transitive closure");
-  for (int a : *nodes) {
-    for (int b : *nodes) {
-      if (gc->IsReachable(Get(id, a), Get(id, b))) {
-        ABSL_RAW_LOG(INFO, "%d %d", a, b);
-      }
-    }
-  }
-  ABSL_RAW_LOG(INFO, "---");
-}
-
-static void CheckTransitiveClosure(Nodes *nodes, Edges *edges, const IdMap &id,
-                                   GraphCycles *gc) {
-  std::unordered_set<int> seen;
-  for (const auto &a : *nodes) {
-    for (const auto &b : *nodes) {
-      seen.clear();
-      bool gc_reachable = gc->IsReachable(Get(id, a), Get(id, b));
-      bool reachable = IsReachable(edges, a, b, &seen);
-      if (gc_reachable != reachable) {
-        PrintEdges(edges);
-        PrintGCEdges(nodes, id, gc);
-        PrintTransitiveClosure(nodes, edges);
-        PrintGCTransitiveClosure(nodes, id, gc);
-        ABSL_RAW_LOG(FATAL, "gc_reachable %s reachable %s a %d b %d",
-                     gc_reachable ? "true" : "false",
-                     reachable ? "true" : "false", a, b);
-      }
-    }
-  }
-}
-
-static void CheckEdges(Nodes *nodes, Edges *edges, const IdMap &id,
-                       GraphCycles *gc) {
-  int count = 0;
-  for (const auto &edge : *edges) {
-    int a = edge.from;
-    int b = edge.to;
-    if (!gc->HasEdge(Get(id, a), Get(id, b))) {
-      PrintEdges(edges);
-      PrintGCEdges(nodes, id, gc);
-      ABSL_RAW_LOG(FATAL, "!gc->HasEdge(%d, %d)", a, b);
-    }
-  }
-  for (const auto &a : *nodes) {
-    for (const auto &b : *nodes) {
-      if (gc->HasEdge(Get(id, a), Get(id, b))) {
-        count++;
-      }
-    }
-  }
-  if (count != edges->size()) {
-    PrintEdges(edges);
-    PrintGCEdges(nodes, id, gc);
-    ABSL_RAW_LOG(FATAL, "edges->size() %zu  count %d", edges->size(), count);
-  }
-}
-
-static void CheckInvariants(const GraphCycles &gc) {
-  if (ABSL_PREDICT_FALSE(!gc.CheckInvariants()))
-    ABSL_RAW_LOG(FATAL, "CheckInvariants");
-}
-
-// Returns the index of a randomly chosen node in *nodes.
-// Requires *nodes be non-empty.
-static int RandomNode(RandomEngine* rng, Nodes *nodes) {
-  std::uniform_int_distribution<int> uniform(0, nodes->size()-1);
-  return uniform(*rng);
-}
-
-// Returns the index of a randomly chosen edge in *edges.
-// Requires *edges be non-empty.
-static int RandomEdge(RandomEngine* rng, Edges *edges) {
-  std::uniform_int_distribution<int> uniform(0, edges->size()-1);
-  return uniform(*rng);
-}
-
-// Returns the index of edge (from, to) in *edges or -1 if it is not in *edges.
-static int EdgeIndex(Edges *edges, int from, int to) {
-  int i = 0;
-  while (i != edges->size() &&
-         ((*edges)[i].from != from || (*edges)[i].to != to)) {
-    i++;
-  }
-  return i == edges->size()? -1 : i;
-}
-
-TEST(GraphCycles, RandomizedTest) {
-  int next_node = 0;
-  Nodes nodes;
-  Edges edges;   // from, to
-  IdMap id;
-  GraphCycles graph_cycles;
-  static const int kMaxNodes = 7;  // use <= 7 nodes to keep test short
-  static const int kDataOffset = 17;  // an offset to the node-specific data
-  int n = 100000;
-  int op = 0;
-  RandomEngine rng(testing::UnitTest::GetInstance()->random_seed());
-  std::uniform_int_distribution<int> uniform(0, 5);
-
-  auto ptr = [](intptr_t i) {
-    return reinterpret_cast<void*>(i + kDataOffset);
-  };
-
-  for (int iter = 0; iter != n; iter++) {
-    for (const auto &node : nodes) {
-      ASSERT_EQ(graph_cycles.Ptr(Get(id, node)), ptr(node)) << " node " << node;
-    }
-    CheckEdges(&nodes, &edges, id, &graph_cycles);
-    CheckTransitiveClosure(&nodes, &edges, id, &graph_cycles);
-    op = uniform(rng);
-    switch (op) {
-    case 0:     // Add a node
-      if (nodes.size() < kMaxNodes) {
-        int new_node = next_node++;
-        GraphId new_gnode = graph_cycles.GetId(ptr(new_node));
-        ASSERT_NE(new_gnode, InvalidGraphId());
-        id[new_node] = new_gnode;
-        ASSERT_EQ(ptr(new_node), graph_cycles.Ptr(new_gnode));
-        nodes.push_back(new_node);
-      }
-      break;
-
-    case 1:    // Remove a node
-      if (nodes.size() > 0) {
-        int node_index = RandomNode(&rng, &nodes);
-        int node = nodes[node_index];
-        nodes[node_index] = nodes.back();
-        nodes.pop_back();
-        graph_cycles.RemoveNode(ptr(node));
-        ASSERT_EQ(graph_cycles.Ptr(Get(id, node)), nullptr);
-        id.erase(node);
-        int i = 0;
-        while (i != edges.size()) {
-          if (edges[i].from == node || edges[i].to == node) {
-            edges[i] = edges.back();
-            edges.pop_back();
-          } else {
-            i++;
-          }
-        }
-      }
-      break;
-
-    case 2:   // Add an edge
-      if (nodes.size() > 0) {
-        int from = RandomNode(&rng, &nodes);
-        int to = RandomNode(&rng, &nodes);
-        if (EdgeIndex(&edges, nodes[from], nodes[to]) == -1) {
-          if (graph_cycles.InsertEdge(id[nodes[from]], id[nodes[to]])) {
-            Edge new_edge;
-            new_edge.from = nodes[from];
-            new_edge.to = nodes[to];
-            edges.push_back(new_edge);
-          } else {
-            std::unordered_set<int> seen;
-            ASSERT_TRUE(IsReachable(&edges, nodes[to], nodes[from], &seen))
-                << "Edge " << nodes[to] << "->" << nodes[from];
-          }
-        }
-      }
-      break;
-
-    case 3:    // Remove an edge
-      if (edges.size() > 0) {
-        int i = RandomEdge(&rng, &edges);
-        int from = edges[i].from;
-        int to = edges[i].to;
-        ASSERT_EQ(i, EdgeIndex(&edges, from, to));
-        edges[i] = edges.back();
-        edges.pop_back();
-        ASSERT_EQ(-1, EdgeIndex(&edges, from, to));
-        graph_cycles.RemoveEdge(id[from], id[to]);
-      }
-      break;
-
-    case 4:   // Check a path
-      if (nodes.size() > 0) {
-        int from = RandomNode(&rng, &nodes);
-        int to = RandomNode(&rng, &nodes);
-        GraphId path[2*kMaxNodes];
-        int path_len = graph_cycles.FindPath(id[nodes[from]], id[nodes[to]],
-                                             ABSL_ARRAYSIZE(path), path);
-        std::unordered_set<int> seen;
-        bool reachable = IsReachable(&edges, nodes[from], nodes[to], &seen);
-        bool gc_reachable =
-            graph_cycles.IsReachable(Get(id, nodes[from]), Get(id, nodes[to]));
-        ASSERT_EQ(path_len != 0, reachable);
-        ASSERT_EQ(path_len != 0, gc_reachable);
-        // In the following line, we add one because a node can appear
-        // twice, if the path is from that node to itself, perhaps via
-        // every other node.
-        ASSERT_LE(path_len, kMaxNodes + 1);
-        if (path_len != 0) {
-          ASSERT_EQ(id[nodes[from]], path[0]);
-          ASSERT_EQ(id[nodes[to]], path[path_len-1]);
-          for (int i = 1; i < path_len; i++) {
-            ASSERT_TRUE(graph_cycles.HasEdge(path[i-1], path[i]));
-          }
-        }
-      }
-      break;
-
-    case 5:  // Check invariants
-      CheckInvariants(graph_cycles);
-      break;
-
-    default:
-      ABSL_RAW_LOG(FATAL, "op %d", op);
-    }
-
-    // Very rarely, test graph expansion by adding then removing many nodes.
-    std::bernoulli_distribution one_in_1024(1.0 / 1024);
-    if (one_in_1024(rng)) {
-      CheckEdges(&nodes, &edges, id, &graph_cycles);
-      CheckTransitiveClosure(&nodes, &edges, id, &graph_cycles);
-      for (int i = 0; i != 256; i++) {
-        int new_node = next_node++;
-        GraphId new_gnode = graph_cycles.GetId(ptr(new_node));
-        ASSERT_NE(InvalidGraphId(), new_gnode);
-        id[new_node] = new_gnode;
-        ASSERT_EQ(ptr(new_node), graph_cycles.Ptr(new_gnode));
-        for (const auto &node : nodes) {
-          ASSERT_NE(node, new_node);
-        }
-        nodes.push_back(new_node);
-      }
-      for (int i = 0; i != 256; i++) {
-        ASSERT_GT(nodes.size(), 0);
-        int node_index = RandomNode(&rng, &nodes);
-        int node = nodes[node_index];
-        nodes[node_index] = nodes.back();
-        nodes.pop_back();
-        graph_cycles.RemoveNode(ptr(node));
-        id.erase(node);
-        int j = 0;
-        while (j != edges.size()) {
-          if (edges[j].from == node || edges[j].to == node) {
-            edges[j] = edges.back();
-            edges.pop_back();
-          } else {
-            j++;
-          }
-        }
-      }
-      CheckInvariants(graph_cycles);
-    }
-  }
-}
-
-class GraphCyclesTest : public ::testing::Test {
- public:
-  IdMap id_;
-  GraphCycles g_;
-
-  static void* Ptr(int i) {
-    return reinterpret_cast<void*>(static_cast<uintptr_t>(i));
-  }
-
-  static int Num(void* ptr) {
-    return static_cast<int>(reinterpret_cast<uintptr_t>(ptr));
-  }
-
-  // Test relies on ith NewNode() call returning Node numbered i
-  GraphCyclesTest() {
-    for (int i = 0; i < 100; i++) {
-      id_[i] = g_.GetId(Ptr(i));
-    }
-    CheckInvariants(g_);
-  }
-
-  bool AddEdge(int x, int y) {
-    return g_.InsertEdge(Get(id_, x), Get(id_, y));
-  }
-
-  void AddMultiples() {
-    // For every node x > 0: add edge to 2*x, 3*x
-    for (int x = 1; x < 25; x++) {
-      EXPECT_TRUE(AddEdge(x, 2*x)) << x;
-      EXPECT_TRUE(AddEdge(x, 3*x)) << x;
-    }
-    CheckInvariants(g_);
-  }
-
-  std::string Path(int x, int y) {
-    GraphId path[5];
-    int np = g_.FindPath(Get(id_, x), Get(id_, y), ABSL_ARRAYSIZE(path), path);
-    std::string result;
-    for (int i = 0; i < np; i++) {
-      if (i >= ABSL_ARRAYSIZE(path)) {
-        result += " ...";
-        break;
-      }
-      if (!result.empty()) result.push_back(' ');
-      char buf[20];
-      snprintf(buf, sizeof(buf), "%d", Num(g_.Ptr(path[i])));
-      result += buf;
-    }
-    return result;
-  }
-};
-
-TEST_F(GraphCyclesTest, NoCycle) {
-  AddMultiples();
-  CheckInvariants(g_);
-}
-
-TEST_F(GraphCyclesTest, SimpleCycle) {
-  AddMultiples();
-  EXPECT_FALSE(AddEdge(8, 4));
-  EXPECT_EQ("4 8", Path(4, 8));
-  CheckInvariants(g_);
-}
-
-TEST_F(GraphCyclesTest, IndirectCycle) {
-  AddMultiples();
-  EXPECT_TRUE(AddEdge(16, 9));
-  CheckInvariants(g_);
-  EXPECT_FALSE(AddEdge(9, 2));
-  EXPECT_EQ("2 4 8 16 9", Path(2, 9));
-  CheckInvariants(g_);
-}
-
-TEST_F(GraphCyclesTest, LongPath) {
-  ASSERT_TRUE(AddEdge(2, 4));
-  ASSERT_TRUE(AddEdge(4, 6));
-  ASSERT_TRUE(AddEdge(6, 8));
-  ASSERT_TRUE(AddEdge(8, 10));
-  ASSERT_TRUE(AddEdge(10, 12));
-  ASSERT_FALSE(AddEdge(12, 2));
-  EXPECT_EQ("2 4 6 8 10 ...", Path(2, 12));
-  CheckInvariants(g_);
-}
-
-TEST_F(GraphCyclesTest, RemoveNode) {
-  ASSERT_TRUE(AddEdge(1, 2));
-  ASSERT_TRUE(AddEdge(2, 3));
-  ASSERT_TRUE(AddEdge(3, 4));
-  ASSERT_TRUE(AddEdge(4, 5));
-  g_.RemoveNode(g_.Ptr(id_[3]));
-  id_.erase(3);
-  ASSERT_TRUE(AddEdge(5, 1));
-}
-
-TEST_F(GraphCyclesTest, ManyEdges) {
-  const int N = 50;
-  for (int i = 0; i < N; i++) {
-    for (int j = 1; j < N; j++) {
-      ASSERT_TRUE(AddEdge(i, i+j));
-    }
-  }
-  CheckInvariants(g_);
-  ASSERT_TRUE(AddEdge(2*N-1, 0));
-  CheckInvariants(g_);
-  ASSERT_FALSE(AddEdge(10, 9));
-  CheckInvariants(g_);
-}
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/kernel_timeout.h b/third_party/abseil_cpp/absl/synchronization/internal/kernel_timeout.h
deleted file mode 100644
index bbd4d2d70f..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/kernel_timeout.h
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// An optional absolute timeout, with nanosecond granularity,
-// compatible with absl::Time. Suitable for in-register
-// parameter-passing (e.g. syscalls.)
-// Constructible from a absl::Time (for a timeout to be respected) or {}
-// (for "no timeout".)
-// This is a private low-level API for use by a handful of low-level
-// components that are friends of this class. Higher-level components
-// should build APIs based on absl::Time and absl::Duration.
-
-#ifndef ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
-#define ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
-
-#include <time.h>
-
-#include <algorithm>
-#include <limits>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/time/clock.h"
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-class Futex;
-class Waiter;
-
-class KernelTimeout {
- public:
-  // A timeout that should expire at <t>.  Any value, in the full
-  // InfinitePast() to InfiniteFuture() range, is valid here and will be
-  // respected.
-  explicit KernelTimeout(absl::Time t) : ns_(MakeNs(t)) {}
-  // No timeout.
-  KernelTimeout() : ns_(0) {}
-
-  // A more explicit factory for those who prefer it.  Equivalent to {}.
-  static KernelTimeout Never() { return {}; }
-
-  // We explicitly do not support other custom formats: timespec, int64_t nanos.
-  // Unify on this and absl::Time, please.
-
-  bool has_timeout() const { return ns_ != 0; }
-
-  // Convert to parameter for sem_timedwait/futex/similar.  Only for approved
-  // users.  Do not call if !has_timeout.
-  struct timespec MakeAbsTimespec();
-
- private:
-  // internal rep, not user visible: ns after unix epoch.
-  // zero = no timeout.
-  // Negative we treat as an unlikely (and certainly expired!) but valid
-  // timeout.
-  int64_t ns_;
-
-  static int64_t MakeNs(absl::Time t) {
-    // optimization--InfiniteFuture is common "no timeout" value
-    // and cheaper to compare than convert.
-    if (t == absl::InfiniteFuture()) return 0;
-    int64_t x = ToUnixNanos(t);
-
-    // A timeout that lands exactly on the epoch (x=0) needs to be respected,
-    // so we alter it unnoticably to 1.  Negative timeouts are in
-    // theory supported, but handled poorly by the kernel (long
-    // delays) so push them forward too; since all such times have
-    // already passed, it's indistinguishable.
-    if (x <= 0) x = 1;
-    // A time larger than what can be represented to the kernel is treated
-    // as no timeout.
-    if (x == (std::numeric_limits<int64_t>::max)()) x = 0;
-    return x;
-  }
-
-#ifdef _WIN32
-  // Converts to milliseconds from now, or INFINITE when
-  // !has_timeout(). For use by SleepConditionVariableSRW on
-  // Windows. Callers should recognize that the return value is a
-  // relative duration (it should be recomputed by calling this method
-  // in the case of a spurious wakeup).
-  // This header file may be included transitively by public header files,
-  // so we define our own DWORD and INFINITE instead of getting them from
-  // <intsafe.h> and <WinBase.h>.
-  typedef unsigned long DWord;  // NOLINT
-  DWord InMillisecondsFromNow() const {
-    constexpr DWord kInfinite = (std::numeric_limits<DWord>::max)();
-    if (!has_timeout()) {
-      return kInfinite;
-    }
-    // The use of absl::Now() to convert from absolute time to
-    // relative time means that absl::Now() cannot use anything that
-    // depends on KernelTimeout (for example, Mutex) on Windows.
-    int64_t now = ToUnixNanos(absl::Now());
-    if (ns_ >= now) {
-      // Round up so that Now() + ms_from_now >= ns_.
-      constexpr uint64_t max_nanos =
-          (std::numeric_limits<int64_t>::max)() - 999999u;
-      uint64_t ms_from_now =
-          (std::min<uint64_t>(max_nanos, ns_ - now) + 999999u) / 1000000u;
-      if (ms_from_now > kInfinite) {
-        return kInfinite;
-      }
-      return static_cast<DWord>(ms_from_now);
-    }
-    return 0;
-  }
-#endif
-
-  friend class Futex;
-  friend class Waiter;
-};
-
-inline struct timespec KernelTimeout::MakeAbsTimespec() {
-  int64_t n = ns_;
-  static const int64_t kNanosPerSecond = 1000 * 1000 * 1000;
-  if (n == 0) {
-    ABSL_RAW_LOG(
-        ERROR, "Tried to create a timespec from a non-timeout; never do this.");
-    // But we'll try to continue sanely.  no-timeout ~= saturated timeout.
-    n = (std::numeric_limits<int64_t>::max)();
-  }
-
-  // Kernel APIs validate timespecs as being at or after the epoch,
-  // despite the kernel time type being signed.  However, no one can
-  // tell the difference between a timeout at or before the epoch (since
-  // all such timeouts have expired!)
-  if (n < 0) n = 0;
-
-  struct timespec abstime;
-  int64_t seconds = (std::min)(n / kNanosPerSecond,
-                               int64_t{(std::numeric_limits<time_t>::max)()});
-  abstime.tv_sec = static_cast<time_t>(seconds);
-  abstime.tv_nsec = static_cast<decltype(abstime.tv_nsec)>(n % kNanosPerSecond);
-  return abstime;
-}
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem.cc b/third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem.cc
deleted file mode 100644
index 821ca9b4e9..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This file is a no-op if the required LowLevelAlloc support is missing.
-#include "absl/base/internal/low_level_alloc.h"
-#ifndef ABSL_LOW_LEVEL_ALLOC_MISSING
-
-#include "absl/synchronization/internal/per_thread_sem.h"
-
-#include <atomic>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/thread_identity.h"
-#include "absl/synchronization/internal/waiter.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-void PerThreadSem::SetThreadBlockedCounter(std::atomic<int> *counter) {
-  base_internal::ThreadIdentity *identity;
-  identity = GetOrCreateCurrentThreadIdentity();
-  identity->blocked_count_ptr = counter;
-}
-
-std::atomic<int> *PerThreadSem::GetThreadBlockedCounter() {
-  base_internal::ThreadIdentity *identity;
-  identity = GetOrCreateCurrentThreadIdentity();
-  return identity->blocked_count_ptr;
-}
-
-void PerThreadSem::Init(base_internal::ThreadIdentity *identity) {
-  new (Waiter::GetWaiter(identity)) Waiter();
-  identity->ticker.store(0, std::memory_order_relaxed);
-  identity->wait_start.store(0, std::memory_order_relaxed);
-  identity->is_idle.store(false, std::memory_order_relaxed);
-}
-
-void PerThreadSem::Destroy(base_internal::ThreadIdentity *identity) {
-  Waiter::GetWaiter(identity)->~Waiter();
-}
-
-void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) {
-  const int ticker =
-      identity->ticker.fetch_add(1, std::memory_order_relaxed) + 1;
-  const int wait_start = identity->wait_start.load(std::memory_order_relaxed);
-  const bool is_idle = identity->is_idle.load(std::memory_order_relaxed);
-  if (wait_start && (ticker - wait_start > Waiter::kIdlePeriods) && !is_idle) {
-    // Wakeup the waiting thread since it is time for it to become idle.
-    Waiter::GetWaiter(identity)->Poke();
-  }
-}
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-extern "C" {
-
-ABSL_ATTRIBUTE_WEAK void AbslInternalPerThreadSemPost(
-    absl::base_internal::ThreadIdentity *identity) {
-  absl::synchronization_internal::Waiter::GetWaiter(identity)->Post();
-}
-
-ABSL_ATTRIBUTE_WEAK bool AbslInternalPerThreadSemWait(
-    absl::synchronization_internal::KernelTimeout t) {
-  bool timeout = false;
-  absl::base_internal::ThreadIdentity *identity;
-  identity = absl::synchronization_internal::GetOrCreateCurrentThreadIdentity();
-
-  // Ensure wait_start != 0.
-  int ticker = identity->ticker.load(std::memory_order_relaxed);
-  identity->wait_start.store(ticker ? ticker : 1, std::memory_order_relaxed);
-  identity->is_idle.store(false, std::memory_order_relaxed);
-
-  if (identity->blocked_count_ptr != nullptr) {
-    // Increment count of threads blocked in a given thread pool.
-    identity->blocked_count_ptr->fetch_add(1, std::memory_order_relaxed);
-  }
-
-  timeout =
-      !absl::synchronization_internal::Waiter::GetWaiter(identity)->Wait(t);
-
-  if (identity->blocked_count_ptr != nullptr) {
-    identity->blocked_count_ptr->fetch_sub(1, std::memory_order_relaxed);
-  }
-
-  identity->is_idle.store(false, std::memory_order_relaxed);
-  identity->wait_start.store(0, std::memory_order_relaxed);
-  return !timeout;
-}
-
-}  // extern "C"
-
-#endif  // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem.h b/third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem.h
deleted file mode 100644
index 2228b6e8ea..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// PerThreadSem is a low-level synchronization primitive controlling the
-// runnability of a single thread, used internally by Mutex and CondVar.
-//
-// This is NOT a general-purpose synchronization mechanism, and should not be
-// used directly by applications.  Applications should use Mutex and CondVar.
-//
-// The semantics of PerThreadSem are the same as that of a counting semaphore.
-// Each thread maintains an abstract "count" value associated with its identity.
-
-#ifndef ABSL_SYNCHRONIZATION_INTERNAL_PER_THREAD_SEM_H_
-#define ABSL_SYNCHRONIZATION_INTERNAL_PER_THREAD_SEM_H_
-
-#include <atomic>
-
-#include "absl/base/internal/thread_identity.h"
-#include "absl/synchronization/internal/create_thread_identity.h"
-#include "absl/synchronization/internal/kernel_timeout.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class Mutex;
-
-namespace synchronization_internal {
-
-class PerThreadSem {
- public:
-  PerThreadSem() = delete;
-  PerThreadSem(const PerThreadSem&) = delete;
-  PerThreadSem& operator=(const PerThreadSem&) = delete;
-
-  // Routine invoked periodically (once a second) by a background thread.
-  // Has no effect on user-visible state.
-  static void Tick(base_internal::ThreadIdentity* identity);
-
-  // ---------------------------------------------------------------------------
-  // Routines used by autosizing threadpools to detect when threads are
-  // blocked.  Each thread has a counter pointer, initially zero.  If non-zero,
-  // the implementation atomically increments the counter when it blocks on a
-  // semaphore, a decrements it again when it wakes.  This allows a threadpool
-  // to keep track of how many of its threads are blocked.
-  // SetThreadBlockedCounter() should be used only by threadpool
-  // implementations.  GetThreadBlockedCounter() should be used by modules that
-  // block threads; if the pointer returned is non-zero, the location should be
-  // incremented before the thread blocks, and decremented after it wakes.
-  static void SetThreadBlockedCounter(std::atomic<int> *counter);
-  static std::atomic<int> *GetThreadBlockedCounter();
-
- private:
-  // Create the PerThreadSem associated with "identity".  Initializes count=0.
-  // REQUIRES: May only be called by ThreadIdentity.
-  static void Init(base_internal::ThreadIdentity* identity);
-
-  // Destroy the PerThreadSem associated with "identity".
-  // REQUIRES: May only be called by ThreadIdentity.
-  static void Destroy(base_internal::ThreadIdentity* identity);
-
-  // Increments "identity"'s count.
-  static inline void Post(base_internal::ThreadIdentity* identity);
-
-  // Waits until either our count > 0 or t has expired.
-  // If count > 0, decrements count and returns true.  Otherwise returns false.
-  // !t.has_timeout() => Wait(t) will return true.
-  static inline bool Wait(KernelTimeout t);
-
-  // Permitted callers.
-  friend class PerThreadSemTest;
-  friend class absl::Mutex;
-  friend absl::base_internal::ThreadIdentity* CreateThreadIdentity();
-  friend void ReclaimThreadIdentity(void* v);
-};
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// In some build configurations we pass --detect-odr-violations to the
-// gold linker.  This causes it to flag weak symbol overrides as ODR
-// violations.  Because ODR only applies to C++ and not C,
-// --detect-odr-violations ignores symbols not mangled with C++ names.
-// By changing our extension points to be extern "C", we dodge this
-// check.
-extern "C" {
-void AbslInternalPerThreadSemPost(
-    absl::base_internal::ThreadIdentity* identity);
-bool AbslInternalPerThreadSemWait(
-    absl::synchronization_internal::KernelTimeout t);
-}  // extern "C"
-
-void absl::synchronization_internal::PerThreadSem::Post(
-    absl::base_internal::ThreadIdentity* identity) {
-  AbslInternalPerThreadSemPost(identity);
-}
-
-bool absl::synchronization_internal::PerThreadSem::Wait(
-    absl::synchronization_internal::KernelTimeout t) {
-  return AbslInternalPerThreadSemWait(t);
-}
-
-#endif  // ABSL_SYNCHRONIZATION_INTERNAL_PER_THREAD_SEM_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem_test.cc b/third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem_test.cc
deleted file mode 100644
index 8cf59e64e9..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/per_thread_sem_test.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/internal/per_thread_sem.h"
-
-#include <atomic>
-#include <condition_variable>  // NOLINT(build/c++11)
-#include <functional>
-#include <limits>
-#include <mutex>               // NOLINT(build/c++11)
-#include <string>
-#include <thread>              // NOLINT(build/c++11)
-
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/cycleclock.h"
-#include "absl/base/internal/thread_identity.h"
-#include "absl/strings/str_cat.h"
-#include "absl/time/clock.h"
-#include "absl/time/time.h"
-
-// In this test we explicitly avoid the use of synchronization
-// primitives which might use PerThreadSem, most notably absl::Mutex.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-class SimpleSemaphore {
- public:
-  SimpleSemaphore() : count_(0) {}
-
-  // Decrements (locks) the semaphore. If the semaphore's value is
-  // greater than zero, then the decrement proceeds, and the function
-  // returns, immediately. If the semaphore currently has the value
-  // zero, then the call blocks until it becomes possible to perform
-  // the decrement.
-  void Wait() {
-    std::unique_lock<std::mutex> lock(mu_);
-    cv_.wait(lock, [this]() { return count_ > 0; });
-    --count_;
-    cv_.notify_one();
-  }
-
-  // Increments (unlocks) the semaphore. If the semaphore's value
-  // consequently becomes greater than zero, then another thread
-  // blocked Wait() call will be woken up and proceed to lock the
-  // semaphore.
-  void Post() {
-    std::lock_guard<std::mutex> lock(mu_);
-    ++count_;
-    cv_.notify_one();
-  }
-
- private:
-  std::mutex mu_;
-  std::condition_variable cv_;
-  int count_;
-};
-
-struct ThreadData {
-  int num_iterations;                 // Number of replies to send.
-  SimpleSemaphore identity2_written;  // Posted by thread writing identity2.
-  base_internal::ThreadIdentity *identity1;  // First Post()-er.
-  base_internal::ThreadIdentity *identity2;  // First Wait()-er.
-  KernelTimeout timeout;
-};
-
-// Need friendship with PerThreadSem.
-class PerThreadSemTest : public testing::Test {
- public:
-  static void TimingThread(ThreadData* t) {
-    t->identity2 = GetOrCreateCurrentThreadIdentity();
-    t->identity2_written.Post();
-    while (t->num_iterations--) {
-      Wait(t->timeout);
-      Post(t->identity1);
-    }
-  }
-
-  void TestTiming(const char *msg, bool timeout) {
-    static const int kNumIterations = 100;
-    ThreadData t;
-    t.num_iterations = kNumIterations;
-    t.timeout = timeout ?
-        KernelTimeout(absl::Now() + absl::Seconds(10000))  // far in the future
-        : KernelTimeout::Never();
-    t.identity1 = GetOrCreateCurrentThreadIdentity();
-
-    // We can't use the Thread class here because it uses the Mutex
-    // class which will invoke PerThreadSem, so we use std::thread instead.
-    std::thread partner_thread(std::bind(TimingThread, &t));
-
-    // Wait for our partner thread to register their identity.
-    t.identity2_written.Wait();
-
-    int64_t min_cycles = std::numeric_limits<int64_t>::max();
-    int64_t total_cycles = 0;
-    for (int i = 0; i < kNumIterations; ++i) {
-      absl::SleepFor(absl::Milliseconds(20));
-      int64_t cycles = base_internal::CycleClock::Now();
-      Post(t.identity2);
-      Wait(t.timeout);
-      cycles = base_internal::CycleClock::Now() - cycles;
-      min_cycles = std::min(min_cycles, cycles);
-      total_cycles += cycles;
-    }
-    std::string out = StrCat(
-        msg, "min cycle count=", min_cycles, " avg cycle count=",
-        absl::SixDigits(static_cast<double>(total_cycles) / kNumIterations));
-    printf("%s\n", out.c_str());
-
-    partner_thread.join();
-  }
-
- protected:
-  static void Post(base_internal::ThreadIdentity *id) {
-    PerThreadSem::Post(id);
-  }
-  static bool Wait(KernelTimeout t) {
-    return PerThreadSem::Wait(t);
-  }
-
-  // convenience overload
-  static bool Wait(absl::Time t) {
-    return Wait(KernelTimeout(t));
-  }
-
-  static void Tick(base_internal::ThreadIdentity *identity) {
-    PerThreadSem::Tick(identity);
-  }
-};
-
-namespace {
-
-TEST_F(PerThreadSemTest, WithoutTimeout) {
-  PerThreadSemTest::TestTiming("Without timeout: ", false);
-}
-
-TEST_F(PerThreadSemTest, WithTimeout) {
-  PerThreadSemTest::TestTiming("With timeout:    ", true);
-}
-
-TEST_F(PerThreadSemTest, Timeouts) {
-  const absl::Duration delay = absl::Milliseconds(50);
-  const absl::Time start = absl::Now();
-  EXPECT_FALSE(Wait(start + delay));
-  const absl::Duration elapsed = absl::Now() - start;
-  // Allow for a slight early return, to account for quality of implementation
-  // issues on various platforms.
-  const absl::Duration slop = absl::Microseconds(200);
-  EXPECT_LE(delay - slop, elapsed)
-      << "Wait returned " << delay - elapsed
-      << " early (with " << slop << " slop), start time was " << start;
-
-  absl::Time negative_timeout = absl::UnixEpoch() - absl::Milliseconds(100);
-  EXPECT_FALSE(Wait(negative_timeout));
-  EXPECT_LE(negative_timeout, absl::Now() + slop);  // trivially true :)
-
-  Post(GetOrCreateCurrentThreadIdentity());
-  // The wait here has an expired timeout, but we have a wake to consume,
-  // so this should succeed
-  EXPECT_TRUE(Wait(negative_timeout));
-}
-
-}  // namespace
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/thread_pool.h b/third_party/abseil_cpp/absl/synchronization/internal/thread_pool.h
deleted file mode 100644
index 0cb96dacde..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/thread_pool.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_SYNCHRONIZATION_INTERNAL_THREAD_POOL_H_
-#define ABSL_SYNCHRONIZATION_INTERNAL_THREAD_POOL_H_
-
-#include <cassert>
-#include <cstddef>
-#include <functional>
-#include <queue>
-#include <thread>  // NOLINT(build/c++11)
-#include <vector>
-
-#include "absl/base/thread_annotations.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-// A simple ThreadPool implementation for tests.
-class ThreadPool {
- public:
-  explicit ThreadPool(int num_threads) {
-    for (int i = 0; i < num_threads; ++i) {
-      threads_.push_back(std::thread(&ThreadPool::WorkLoop, this));
-    }
-  }
-
-  ThreadPool(const ThreadPool &) = delete;
-  ThreadPool &operator=(const ThreadPool &) = delete;
-
-  ~ThreadPool() {
-    {
-      absl::MutexLock l(&mu_);
-      for (size_t i = 0; i < threads_.size(); i++) {
-        queue_.push(nullptr);  // Shutdown signal.
-      }
-    }
-    for (auto &t : threads_) {
-      t.join();
-    }
-  }
-
-  // Schedule a function to be run on a ThreadPool thread immediately.
-  void Schedule(std::function<void()> func) {
-    assert(func != nullptr);
-    absl::MutexLock l(&mu_);
-    queue_.push(std::move(func));
-  }
-
- private:
-  bool WorkAvailable() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
-    return !queue_.empty();
-  }
-
-  void WorkLoop() {
-    while (true) {
-      std::function<void()> func;
-      {
-        absl::MutexLock l(&mu_);
-        mu_.Await(absl::Condition(this, &ThreadPool::WorkAvailable));
-        func = std::move(queue_.front());
-        queue_.pop();
-      }
-      if (func == nullptr) {  // Shutdown signal.
-        break;
-      }
-      func();
-    }
-  }
-
-  absl::Mutex mu_;
-  std::queue<std::function<void()>> queue_ ABSL_GUARDED_BY(mu_);
-  std::vector<std::thread> threads_;
-};
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_SYNCHRONIZATION_INTERNAL_THREAD_POOL_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/waiter.cc b/third_party/abseil_cpp/absl/synchronization/internal/waiter.cc
deleted file mode 100644
index 2123be60f5..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/waiter.cc
+++ /dev/null
@@ -1,428 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/internal/waiter.h"
-
-#include "absl/base/config.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#else
-#include <pthread.h>
-#include <sys/time.h>
-#include <unistd.h>
-#endif
-
-#ifdef __linux__
-#include <linux/futex.h>
-#include <sys/syscall.h>
-#endif
-
-#ifdef ABSL_HAVE_SEMAPHORE_H
-#include <semaphore.h>
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#include <time.h>
-
-#include <atomic>
-#include <cassert>
-#include <cstdint>
-#include <new>
-#include <type_traits>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/thread_identity.h"
-#include "absl/base/optimization.h"
-#include "absl/synchronization/internal/kernel_timeout.h"
-
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-static void MaybeBecomeIdle() {
-  base_internal::ThreadIdentity *identity =
-      base_internal::CurrentThreadIdentityIfPresent();
-  assert(identity != nullptr);
-  const bool is_idle = identity->is_idle.load(std::memory_order_relaxed);
-  const int ticker = identity->ticker.load(std::memory_order_relaxed);
-  const int wait_start = identity->wait_start.load(std::memory_order_relaxed);
-  if (!is_idle && ticker - wait_start > Waiter::kIdlePeriods) {
-    identity->is_idle.store(true, std::memory_order_relaxed);
-  }
-}
-
-#if ABSL_WAITER_MODE == ABSL_WAITER_MODE_FUTEX
-
-Waiter::Waiter() {
-  futex_.store(0, std::memory_order_relaxed);
-}
-
-Waiter::~Waiter() = default;
-
-bool Waiter::Wait(KernelTimeout t) {
-  // Loop until we can atomically decrement futex from a positive
-  // value, waiting on a futex while we believe it is zero.
-  // Note that, since the thread ticker is just reset, we don't need to check
-  // whether the thread is idle on the very first pass of the loop.
-  bool first_pass = true;
-  while (true) {
-    int32_t x = futex_.load(std::memory_order_relaxed);
-    while (x != 0) {
-      if (!futex_.compare_exchange_weak(x, x - 1,
-                                        std::memory_order_acquire,
-                                        std::memory_order_relaxed)) {
-        continue;  // Raced with someone, retry.
-      }
-      return true;  // Consumed a wakeup, we are done.
-    }
-
-
-    if (!first_pass) MaybeBecomeIdle();
-    const int err = Futex::WaitUntil(&futex_, 0, t);
-    if (err != 0) {
-      if (err == -EINTR || err == -EWOULDBLOCK) {
-        // Do nothing, the loop will retry.
-      } else if (err == -ETIMEDOUT) {
-        return false;
-      } else {
-        ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err);
-      }
-    }
-    first_pass = false;
-  }
-}
-
-void Waiter::Post() {
-  if (futex_.fetch_add(1, std::memory_order_release) == 0) {
-    // We incremented from 0, need to wake a potential waiter.
-    Poke();
-  }
-}
-
-void Waiter::Poke() {
-  // Wake one thread waiting on the futex.
-  const int err = Futex::Wake(&futex_, 1);
-  if (ABSL_PREDICT_FALSE(err < 0)) {
-    ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err);
-  }
-}
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_CONDVAR
-
-class PthreadMutexHolder {
- public:
-  explicit PthreadMutexHolder(pthread_mutex_t *mu) : mu_(mu) {
-    const int err = pthread_mutex_lock(mu_);
-    if (err != 0) {
-      ABSL_RAW_LOG(FATAL, "pthread_mutex_lock failed: %d", err);
-    }
-  }
-
-  PthreadMutexHolder(const PthreadMutexHolder &rhs) = delete;
-  PthreadMutexHolder &operator=(const PthreadMutexHolder &rhs) = delete;
-
-  ~PthreadMutexHolder() {
-    const int err = pthread_mutex_unlock(mu_);
-    if (err != 0) {
-      ABSL_RAW_LOG(FATAL, "pthread_mutex_unlock failed: %d", err);
-    }
-  }
-
- private:
-  pthread_mutex_t *mu_;
-};
-
-Waiter::Waiter() {
-  const int err = pthread_mutex_init(&mu_, 0);
-  if (err != 0) {
-    ABSL_RAW_LOG(FATAL, "pthread_mutex_init failed: %d", err);
-  }
-
-  const int err2 = pthread_cond_init(&cv_, 0);
-  if (err2 != 0) {
-    ABSL_RAW_LOG(FATAL, "pthread_cond_init failed: %d", err2);
-  }
-
-  waiter_count_ = 0;
-  wakeup_count_ = 0;
-}
-
-Waiter::~Waiter() {
-  const int err = pthread_mutex_destroy(&mu_);
-  if (err != 0) {
-    ABSL_RAW_LOG(FATAL, "pthread_mutex_destroy failed: %d", err);
-  }
-
-  const int err2 = pthread_cond_destroy(&cv_);
-  if (err2 != 0) {
-    ABSL_RAW_LOG(FATAL, "pthread_cond_destroy failed: %d", err2);
-  }
-}
-
-bool Waiter::Wait(KernelTimeout t) {
-  struct timespec abs_timeout;
-  if (t.has_timeout()) {
-    abs_timeout = t.MakeAbsTimespec();
-  }
-
-  PthreadMutexHolder h(&mu_);
-  ++waiter_count_;
-  // Loop until we find a wakeup to consume or timeout.
-  // Note that, since the thread ticker is just reset, we don't need to check
-  // whether the thread is idle on the very first pass of the loop.
-  bool first_pass = true;
-  while (wakeup_count_ == 0) {
-    if (!first_pass) MaybeBecomeIdle();
-    // No wakeups available, time to wait.
-    if (!t.has_timeout()) {
-      const int err = pthread_cond_wait(&cv_, &mu_);
-      if (err != 0) {
-        ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err);
-      }
-    } else {
-      const int err = pthread_cond_timedwait(&cv_, &mu_, &abs_timeout);
-      if (err == ETIMEDOUT) {
-        --waiter_count_;
-        return false;
-      }
-      if (err != 0) {
-        ABSL_RAW_LOG(FATAL, "pthread_cond_timedwait failed: %d", err);
-      }
-    }
-    first_pass = false;
-  }
-  // Consume a wakeup and we're done.
-  --wakeup_count_;
-  --waiter_count_;
-  return true;
-}
-
-void Waiter::Post() {
-  PthreadMutexHolder h(&mu_);
-  ++wakeup_count_;
-  InternalCondVarPoke();
-}
-
-void Waiter::Poke() {
-  PthreadMutexHolder h(&mu_);
-  InternalCondVarPoke();
-}
-
-void Waiter::InternalCondVarPoke() {
-  if (waiter_count_ != 0) {
-    const int err = pthread_cond_signal(&cv_);
-    if (ABSL_PREDICT_FALSE(err != 0)) {
-      ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err);
-    }
-  }
-}
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM
-
-Waiter::Waiter() {
-  if (sem_init(&sem_, 0, 0) != 0) {
-    ABSL_RAW_LOG(FATAL, "sem_init failed with errno %d\n", errno);
-  }
-  wakeups_.store(0, std::memory_order_relaxed);
-}
-
-Waiter::~Waiter() {
-  if (sem_destroy(&sem_) != 0) {
-    ABSL_RAW_LOG(FATAL, "sem_destroy failed with errno %d\n", errno);
-  }
-}
-
-bool Waiter::Wait(KernelTimeout t) {
-  struct timespec abs_timeout;
-  if (t.has_timeout()) {
-    abs_timeout = t.MakeAbsTimespec();
-  }
-
-  // Loop until we timeout or consume a wakeup.
-  // Note that, since the thread ticker is just reset, we don't need to check
-  // whether the thread is idle on the very first pass of the loop.
-  bool first_pass = true;
-  while (true) {
-    int x = wakeups_.load(std::memory_order_relaxed);
-    while (x != 0) {
-      if (!wakeups_.compare_exchange_weak(x, x - 1,
-                                          std::memory_order_acquire,
-                                          std::memory_order_relaxed)) {
-        continue;  // Raced with someone, retry.
-      }
-      // Successfully consumed a wakeup, we're done.
-      return true;
-    }
-
-    if (!first_pass) MaybeBecomeIdle();
-    // Nothing to consume, wait (looping on EINTR).
-    while (true) {
-      if (!t.has_timeout()) {
-        if (sem_wait(&sem_) == 0) break;
-        if (errno == EINTR) continue;
-        ABSL_RAW_LOG(FATAL, "sem_wait failed: %d", errno);
-      } else {
-        if (sem_timedwait(&sem_, &abs_timeout) == 0) break;
-        if (errno == EINTR) continue;
-        if (errno == ETIMEDOUT) return false;
-        ABSL_RAW_LOG(FATAL, "sem_timedwait failed: %d", errno);
-      }
-    }
-    first_pass = false;
-  }
-}
-
-void Waiter::Post() {
-  // Post a wakeup.
-  if (wakeups_.fetch_add(1, std::memory_order_release) == 0) {
-    // We incremented from 0, need to wake a potential waiter.
-    Poke();
-  }
-}
-
-void Waiter::Poke() {
-  if (sem_post(&sem_) != 0) {  // Wake any semaphore waiter.
-    ABSL_RAW_LOG(FATAL, "sem_post failed with errno %d\n", errno);
-  }
-}
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_WIN32
-
-class Waiter::WinHelper {
- public:
-  static SRWLOCK *GetLock(Waiter *w) {
-    return reinterpret_cast<SRWLOCK *>(&w->mu_storage_);
-  }
-
-  static CONDITION_VARIABLE *GetCond(Waiter *w) {
-    return reinterpret_cast<CONDITION_VARIABLE *>(&w->cv_storage_);
-  }
-
-  static_assert(sizeof(SRWLOCK) == sizeof(void *),
-                "`mu_storage_` does not have the same size as SRWLOCK");
-  static_assert(alignof(SRWLOCK) == alignof(void *),
-                "`mu_storage_` does not have the same alignment as SRWLOCK");
-
-  static_assert(sizeof(CONDITION_VARIABLE) == sizeof(void *),
-                "`ABSL_CONDITION_VARIABLE_STORAGE` does not have the same size "
-                "as `CONDITION_VARIABLE`");
-  static_assert(
-      alignof(CONDITION_VARIABLE) == alignof(void *),
-      "`cv_storage_` does not have the same alignment as `CONDITION_VARIABLE`");
-
-  // The SRWLOCK and CONDITION_VARIABLE types must be trivially constructible
-  // and destructible because we never call their constructors or destructors.
-  static_assert(std::is_trivially_constructible<SRWLOCK>::value,
-                "The `SRWLOCK` type must be trivially constructible");
-  static_assert(
-      std::is_trivially_constructible<CONDITION_VARIABLE>::value,
-      "The `CONDITION_VARIABLE` type must be trivially constructible");
-  static_assert(std::is_trivially_destructible<SRWLOCK>::value,
-                "The `SRWLOCK` type must be trivially destructible");
-  static_assert(std::is_trivially_destructible<CONDITION_VARIABLE>::value,
-                "The `CONDITION_VARIABLE` type must be trivially destructible");
-};
-
-class LockHolder {
- public:
-  explicit LockHolder(SRWLOCK* mu) : mu_(mu) {
-    AcquireSRWLockExclusive(mu_);
-  }
-
-  LockHolder(const LockHolder&) = delete;
-  LockHolder& operator=(const LockHolder&) = delete;
-
-  ~LockHolder() {
-    ReleaseSRWLockExclusive(mu_);
-  }
-
- private:
-  SRWLOCK* mu_;
-};
-
-Waiter::Waiter() {
-  auto *mu = ::new (static_cast<void *>(&mu_storage_)) SRWLOCK;
-  auto *cv = ::new (static_cast<void *>(&cv_storage_)) CONDITION_VARIABLE;
-  InitializeSRWLock(mu);
-  InitializeConditionVariable(cv);
-  waiter_count_ = 0;
-  wakeup_count_ = 0;
-}
-
-// SRW locks and condition variables do not need to be explicitly destroyed.
-// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock
-// https://stackoverflow.com/questions/28975958/why-does-windows-have-no-deleteconditionvariable-function-to-go-together-with
-Waiter::~Waiter() = default;
-
-bool Waiter::Wait(KernelTimeout t) {
-  SRWLOCK *mu = WinHelper::GetLock(this);
-  CONDITION_VARIABLE *cv = WinHelper::GetCond(this);
-
-  LockHolder h(mu);
-  ++waiter_count_;
-
-  // Loop until we find a wakeup to consume or timeout.
-  // Note that, since the thread ticker is just reset, we don't need to check
-  // whether the thread is idle on the very first pass of the loop.
-  bool first_pass = true;
-  while (wakeup_count_ == 0) {
-    if (!first_pass) MaybeBecomeIdle();
-    // No wakeups available, time to wait.
-    if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) {
-      // GetLastError() returns a Win32 DWORD, but we assign to
-      // unsigned long to simplify the ABSL_RAW_LOG case below.  The uniform
-      // initialization guarantees this is not a narrowing conversion.
-      const unsigned long err{GetLastError()};  // NOLINT(runtime/int)
-      if (err == ERROR_TIMEOUT) {
-        --waiter_count_;
-        return false;
-      } else {
-        ABSL_RAW_LOG(FATAL, "SleepConditionVariableSRW failed: %lu", err);
-      }
-    }
-    first_pass = false;
-  }
-  // Consume a wakeup and we're done.
-  --wakeup_count_;
-  --waiter_count_;
-  return true;
-}
-
-void Waiter::Post() {
-  LockHolder h(WinHelper::GetLock(this));
-  ++wakeup_count_;
-  InternalCondVarPoke();
-}
-
-void Waiter::Poke() {
-  LockHolder h(WinHelper::GetLock(this));
-  InternalCondVarPoke();
-}
-
-void Waiter::InternalCondVarPoke() {
-  if (waiter_count_ != 0) {
-    WakeConditionVariable(WinHelper::GetCond(this));
-  }
-}
-
-#else
-#error Unknown ABSL_WAITER_MODE
-#endif
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/synchronization/internal/waiter.h b/third_party/abseil_cpp/absl/synchronization/internal/waiter.h
deleted file mode 100644
index be3df180d4..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/internal/waiter.h
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_SYNCHRONIZATION_INTERNAL_WAITER_H_
-#define ABSL_SYNCHRONIZATION_INTERNAL_WAITER_H_
-
-#include "absl/base/config.h"
-
-#ifdef _WIN32
-#include <sdkddkver.h>
-#else
-#include <pthread.h>
-#endif
-
-#ifdef __linux__
-#include <linux/futex.h>
-#endif
-
-#ifdef ABSL_HAVE_SEMAPHORE_H
-#include <semaphore.h>
-#endif
-
-#include <atomic>
-#include <cstdint>
-
-#include "absl/base/internal/thread_identity.h"
-#include "absl/synchronization/internal/futex.h"
-#include "absl/synchronization/internal/kernel_timeout.h"
-
-// May be chosen at compile time via -DABSL_FORCE_WAITER_MODE=<index>
-#define ABSL_WAITER_MODE_FUTEX 0
-#define ABSL_WAITER_MODE_SEM 1
-#define ABSL_WAITER_MODE_CONDVAR 2
-#define ABSL_WAITER_MODE_WIN32 3
-
-#if defined(ABSL_FORCE_WAITER_MODE)
-#define ABSL_WAITER_MODE ABSL_FORCE_WAITER_MODE
-#elif defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
-#define ABSL_WAITER_MODE ABSL_WAITER_MODE_WIN32
-#elif defined(ABSL_INTERNAL_HAVE_FUTEX)
-#define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX
-#elif defined(ABSL_HAVE_SEMAPHORE_H)
-#define ABSL_WAITER_MODE ABSL_WAITER_MODE_SEM
-#else
-#define ABSL_WAITER_MODE ABSL_WAITER_MODE_CONDVAR
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace synchronization_internal {
-
-// Waiter is an OS-specific semaphore.
-class Waiter {
- public:
-  // Prepare any data to track waits.
-  Waiter();
-
-  // Not copyable or movable
-  Waiter(const Waiter&) = delete;
-  Waiter& operator=(const Waiter&) = delete;
-
-  // Destroy any data to track waits.
-  ~Waiter();
-
-  // Blocks the calling thread until a matching call to `Post()` or
-  // `t` has passed. Returns `true` if woken (`Post()` called),
-  // `false` on timeout.
-  bool Wait(KernelTimeout t);
-
-  // Restart the caller of `Wait()` as with a normal semaphore.
-  void Post();
-
-  // If anyone is waiting, wake them up temporarily and cause them to
-  // call `MaybeBecomeIdle()`. They will then return to waiting for a
-  // `Post()` or timeout.
-  void Poke();
-
-  // Returns the Waiter associated with the identity.
-  static Waiter* GetWaiter(base_internal::ThreadIdentity* identity) {
-    static_assert(
-        sizeof(Waiter) <= sizeof(base_internal::ThreadIdentity::WaiterState),
-        "Insufficient space for Waiter");
-    return reinterpret_cast<Waiter*>(identity->waiter_state.data);
-  }
-
-  // How many periods to remain idle before releasing resources
-#ifndef ABSL_HAVE_THREAD_SANITIZER
-  static constexpr int kIdlePeriods = 60;
-#else
-  // Memory consumption under ThreadSanitizer is a serious concern,
-  // so we release resources sooner. The value of 1 leads to 1 to 2 second
-  // delay before marking a thread as idle.
-  static const int kIdlePeriods = 1;
-#endif
-
- private:
-#if ABSL_WAITER_MODE == ABSL_WAITER_MODE_FUTEX
-  // Futexes are defined by specification to be 32-bits.
-  // Thus std::atomic<int32_t> must be just an int32_t with lockfree methods.
-  std::atomic<int32_t> futex_;
-  static_assert(sizeof(int32_t) == sizeof(futex_), "Wrong size for futex");
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_CONDVAR
-  // REQUIRES: mu_ must be held.
-  void InternalCondVarPoke();
-
-  pthread_mutex_t mu_;
-  pthread_cond_t cv_;
-  int waiter_count_;
-  int wakeup_count_;  // Unclaimed wakeups.
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM
-  sem_t sem_;
-  // This seems superfluous, but for Poke() we need to cause spurious
-  // wakeups on the semaphore. Hence we can't actually use the
-  // semaphore's count.
-  std::atomic<int> wakeups_;
-
-#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_WIN32
-  // WinHelper - Used to define utilities for accessing the lock and
-  // condition variable storage once the types are complete.
-  class WinHelper;
-
-  // REQUIRES: WinHelper::GetLock(this) must be held.
-  void InternalCondVarPoke();
-
-  // We can't include Windows.h in our headers, so we use aligned charachter
-  // buffers to define the storage of SRWLOCK and CONDITION_VARIABLE.
-  alignas(void*) unsigned char mu_storage_[sizeof(void*)];
-  alignas(void*) unsigned char cv_storage_[sizeof(void*)];
-  int waiter_count_;
-  int wakeup_count_;
-
-#else
-  #error Unknown ABSL_WAITER_MODE
-#endif
-};
-
-}  // namespace synchronization_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_SYNCHRONIZATION_INTERNAL_WAITER_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/lifetime_test.cc b/third_party/abseil_cpp/absl/synchronization/lifetime_test.cc
deleted file mode 100644
index cc973a3290..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/lifetime_test.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cstdlib>
-#include <thread>  // NOLINT(build/c++11), Abseil test
-#include <type_traits>
-
-#include "absl/base/attributes.h"
-#include "absl/base/const_init.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/synchronization/mutex.h"
-#include "absl/synchronization/notification.h"
-
-namespace {
-
-// A two-threaded test which checks that Mutex, CondVar, and Notification have
-// correct basic functionality.  The intent is to establish that they
-// function correctly in various phases of construction and destruction.
-//
-// Thread one acquires a lock on 'mutex', wakes thread two via 'notification',
-// then waits for 'state' to be set, as signalled by 'condvar'.
-//
-// Thread two waits on 'notification', then sets 'state' inside the 'mutex',
-// signalling the change via 'condvar'.
-//
-// These tests use ABSL_RAW_CHECK to validate invariants, rather than EXPECT or
-// ASSERT from gUnit, because we need to invoke them during global destructors,
-// when gUnit teardown would have already begun.
-void ThreadOne(absl::Mutex* mutex, absl::CondVar* condvar,
-               absl::Notification* notification, bool* state) {
-  // Test that the notification is in a valid initial state.
-  ABSL_RAW_CHECK(!notification->HasBeenNotified(), "invalid Notification");
-  ABSL_RAW_CHECK(*state == false, "*state not initialized");
-
-  {
-    absl::MutexLock lock(mutex);
-
-    notification->Notify();
-    ABSL_RAW_CHECK(notification->HasBeenNotified(), "invalid Notification");
-
-    while (*state == false) {
-      condvar->Wait(mutex);
-    }
-  }
-}
-
-void ThreadTwo(absl::Mutex* mutex, absl::CondVar* condvar,
-               absl::Notification* notification, bool* state) {
-  ABSL_RAW_CHECK(*state == false, "*state not initialized");
-
-  // Wake thread one
-  notification->WaitForNotification();
-  ABSL_RAW_CHECK(notification->HasBeenNotified(), "invalid Notification");
-  {
-    absl::MutexLock lock(mutex);
-    *state = true;
-    condvar->Signal();
-  }
-}
-
-// Launch thread 1 and thread 2, and block on their completion.
-// If any of 'mutex', 'condvar', or 'notification' is nullptr, use a locally
-// constructed instance instead.
-void RunTests(absl::Mutex* mutex, absl::CondVar* condvar) {
-  absl::Mutex default_mutex;
-  absl::CondVar default_condvar;
-  absl::Notification notification;
-  if (!mutex) {
-    mutex = &default_mutex;
-  }
-  if (!condvar) {
-    condvar = &default_condvar;
-  }
-  bool state = false;
-  std::thread thread_one(ThreadOne, mutex, condvar, &notification, &state);
-  std::thread thread_two(ThreadTwo, mutex, condvar, &notification, &state);
-  thread_one.join();
-  thread_two.join();
-}
-
-void TestLocals() {
-  absl::Mutex mutex;
-  absl::CondVar condvar;
-  RunTests(&mutex, &condvar);
-}
-
-// Normal kConstInit usage
-ABSL_CONST_INIT absl::Mutex const_init_mutex(absl::kConstInit);
-void TestConstInitGlobal() { RunTests(&const_init_mutex, nullptr); }
-
-// Global variables during start and termination
-//
-// In a translation unit, static storage duration variables are initialized in
-// the order of their definitions, and destroyed in the reverse order of their
-// definitions.  We can use this to arrange for tests to be run on these objects
-// before they are created, and after they are destroyed.
-
-using Function = void (*)();
-
-class OnConstruction {
- public:
-  explicit OnConstruction(Function fn) { fn(); }
-};
-
-class OnDestruction {
- public:
-  explicit OnDestruction(Function fn) : fn_(fn) {}
-  ~OnDestruction() { fn_(); }
- private:
-  Function fn_;
-};
-
-// These tests require that the compiler correctly supports C++11 constant
-// initialization... but MSVC has a known regression since v19.10:
-// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html
-// TODO(epastor): Limit the affected range once MSVC fixes this bug.
-#if defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900)
-// kConstInit
-// Test early usage.  (Declaration comes first; definitions must appear after
-// the test runner.)
-extern absl::Mutex early_const_init_mutex;
-// (Normally I'd write this +[], to make the cast-to-function-pointer explicit,
-// but in some MSVC setups we support, lambdas provide conversion operators to
-// different flavors of function pointers, making this trick ambiguous.)
-OnConstruction test_early_const_init([] {
-  RunTests(&early_const_init_mutex, nullptr);
-});
-// This definition appears before test_early_const_init, but it should be
-// initialized first (due to constant initialization).  Test that the object
-// actually works when constructed this way.
-ABSL_CONST_INIT absl::Mutex early_const_init_mutex(absl::kConstInit);
-
-// Furthermore, test that the const-init c'tor doesn't stomp over the state of
-// a Mutex.  Really, this is a test that the platform under test correctly
-// supports C++11 constant initialization.  (The constant-initialization
-// constructors of globals "happen at link time"; memory is pre-initialized,
-// before the constructors of either grab_lock or check_still_locked are run.)
-extern absl::Mutex const_init_sanity_mutex;
-OnConstruction grab_lock([]() ABSL_NO_THREAD_SAFETY_ANALYSIS {
-  const_init_sanity_mutex.Lock();
-});
-ABSL_CONST_INIT absl::Mutex const_init_sanity_mutex(absl::kConstInit);
-OnConstruction check_still_locked([]() ABSL_NO_THREAD_SAFETY_ANALYSIS {
-  const_init_sanity_mutex.AssertHeld();
-  const_init_sanity_mutex.Unlock();
-});
-#endif  // defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900)
-
-// Test shutdown usage.  (Declarations come first; definitions must appear after
-// the test runner.)
-extern absl::Mutex late_const_init_mutex;
-// OnDestruction is being used here as a global variable, even though it has a
-// non-trivial destructor.  This is against the style guide.  We're violating
-// that rule here to check that the exception we allow for kConstInit is safe.
-// NOLINTNEXTLINE
-OnDestruction test_late_const_init([] {
-  RunTests(&late_const_init_mutex, nullptr);
-});
-ABSL_CONST_INIT absl::Mutex late_const_init_mutex(absl::kConstInit);
-
-}  // namespace
-
-int main() {
-  TestLocals();
-  TestConstInitGlobal();
-  // Explicitly call exit(0) here, to make it clear that we intend for the
-  // above global object destructors to run.
-  std::exit(0);
-}
diff --git a/third_party/abseil_cpp/absl/synchronization/mutex.cc b/third_party/abseil_cpp/absl/synchronization/mutex.cc
deleted file mode 100644
index 9e01393ca4..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/mutex.cc
+++ /dev/null
@@ -1,2740 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/mutex.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#ifdef ERROR
-#undef ERROR
-#endif
-#else
-#include <fcntl.h>
-#include <pthread.h>
-#include <sched.h>
-#include <sys/time.h>
-#endif
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <algorithm>
-#include <atomic>
-#include <cinttypes>
-#include <thread>  // NOLINT(build/c++11)
-
-#include "absl/base/attributes.h"
-#include "absl/base/call_once.h"
-#include "absl/base/config.h"
-#include "absl/base/dynamic_annotations.h"
-#include "absl/base/internal/atomic_hook.h"
-#include "absl/base/internal/cycleclock.h"
-#include "absl/base/internal/hide_ptr.h"
-#include "absl/base/internal/low_level_alloc.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/base/internal/sysinfo.h"
-#include "absl/base/internal/thread_identity.h"
-#include "absl/base/internal/tsan_mutex_interface.h"
-#include "absl/base/port.h"
-#include "absl/debugging/stacktrace.h"
-#include "absl/debugging/symbolize.h"
-#include "absl/synchronization/internal/graphcycles.h"
-#include "absl/synchronization/internal/per_thread_sem.h"
-#include "absl/time/time.h"
-
-using absl::base_internal::CurrentThreadIdentityIfPresent;
-using absl::base_internal::PerThreadSynch;
-using absl::base_internal::SchedulingGuard;
-using absl::base_internal::ThreadIdentity;
-using absl::synchronization_internal::GetOrCreateCurrentThreadIdentity;
-using absl::synchronization_internal::GraphCycles;
-using absl::synchronization_internal::GraphId;
-using absl::synchronization_internal::InvalidGraphId;
-using absl::synchronization_internal::KernelTimeout;
-using absl::synchronization_internal::PerThreadSem;
-
-extern "C" {
-ABSL_ATTRIBUTE_WEAK void AbslInternalMutexYield() { std::this_thread::yield(); }
-}  // extern "C"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace {
-
-#if defined(ABSL_HAVE_THREAD_SANITIZER)
-constexpr OnDeadlockCycle kDeadlockDetectionDefault = OnDeadlockCycle::kIgnore;
-#else
-constexpr OnDeadlockCycle kDeadlockDetectionDefault = OnDeadlockCycle::kAbort;
-#endif
-
-ABSL_CONST_INIT std::atomic<OnDeadlockCycle> synch_deadlock_detection(
-    kDeadlockDetectionDefault);
-ABSL_CONST_INIT std::atomic<bool> synch_check_invariants(false);
-
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
-absl::base_internal::AtomicHook<void (*)(int64_t wait_cycles)>
-    submit_profile_data;
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<void (*)(
-    const char *msg, const void *obj, int64_t wait_cycles)>
-    mutex_tracer;
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
-    absl::base_internal::AtomicHook<void (*)(const char *msg, const void *cv)>
-        cond_var_tracer;
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<
-    bool (*)(const void *pc, char *out, int out_size)>
-    symbolizer(absl::Symbolize);
-
-}  // namespace
-
-static inline bool EvalConditionAnnotated(const Condition *cond, Mutex *mu,
-                                          bool locking, bool trylock,
-                                          bool read_lock);
-
-void RegisterMutexProfiler(void (*fn)(int64_t wait_timestamp)) {
-  submit_profile_data.Store(fn);
-}
-
-void RegisterMutexTracer(void (*fn)(const char *msg, const void *obj,
-                                    int64_t wait_cycles)) {
-  mutex_tracer.Store(fn);
-}
-
-void RegisterCondVarTracer(void (*fn)(const char *msg, const void *cv)) {
-  cond_var_tracer.Store(fn);
-}
-
-void RegisterSymbolizer(bool (*fn)(const void *pc, char *out, int out_size)) {
-  symbolizer.Store(fn);
-}
-
-struct ABSL_CACHELINE_ALIGNED MutexGlobals {
-  absl::once_flag once;
-  int num_cpus = 0;
-  int spinloop_iterations = 0;
-};
-
-static const MutexGlobals& GetMutexGlobals() {
-  ABSL_CONST_INIT static MutexGlobals data;
-  absl::base_internal::LowLevelCallOnce(&data.once, [&]() {
-    data.num_cpus = absl::base_internal::NumCPUs();
-    data.spinloop_iterations = data.num_cpus > 1 ? 1500 : 0;
-  });
-  return data;
-}
-
-// Spinlock delay on iteration c.  Returns new c.
-namespace {
-  enum DelayMode { AGGRESSIVE, GENTLE };
-};
-
-namespace synchronization_internal {
-int MutexDelay(int32_t c, int mode) {
-  // If this a uniprocessor, only yield/sleep.  Otherwise, if the mode is
-  // aggressive then spin many times before yielding.  If the mode is
-  // gentle then spin only a few times before yielding.  Aggressive spinning is
-  // used to ensure that an Unlock() call, which  must get the spin lock for
-  // any thread to make progress gets it without undue delay.
-  const int32_t limit =
-      GetMutexGlobals().num_cpus > 1 ? (mode == AGGRESSIVE ? 5000 : 250) : 0;
-  if (c < limit) {
-    // Spin.
-    c++;
-  } else {
-    SchedulingGuard::ScopedEnable enable_rescheduling;
-    ABSL_TSAN_MUTEX_PRE_DIVERT(nullptr, 0);
-    if (c == limit) {
-      // Yield once.
-      AbslInternalMutexYield();
-      c++;
-    } else {
-      // Then wait.
-      absl::SleepFor(absl::Microseconds(10));
-      c = 0;
-    }
-    ABSL_TSAN_MUTEX_POST_DIVERT(nullptr, 0);
-  }
-  return c;
-}
-}  // namespace synchronization_internal
-
-// --------------------------Generic atomic ops
-// Ensure that "(*pv & bits) == bits" by doing an atomic update of "*pv" to
-// "*pv | bits" if necessary.  Wait until (*pv & wait_until_clear)==0
-// before making any change.
-// This is used to set flags in mutex and condition variable words.
-static void AtomicSetBits(std::atomic<intptr_t>* pv, intptr_t bits,
-                          intptr_t wait_until_clear) {
-  intptr_t v;
-  do {
-    v = pv->load(std::memory_order_relaxed);
-  } while ((v & bits) != bits &&
-           ((v & wait_until_clear) != 0 ||
-            !pv->compare_exchange_weak(v, v | bits,
-                                       std::memory_order_release,
-                                       std::memory_order_relaxed)));
-}
-
-// Ensure that "(*pv & bits) == 0" by doing an atomic update of "*pv" to
-// "*pv & ~bits" if necessary.  Wait until (*pv & wait_until_clear)==0
-// before making any change.
-// This is used to unset flags in mutex and condition variable words.
-static void AtomicClearBits(std::atomic<intptr_t>* pv, intptr_t bits,
-                            intptr_t wait_until_clear) {
-  intptr_t v;
-  do {
-    v = pv->load(std::memory_order_relaxed);
-  } while ((v & bits) != 0 &&
-           ((v & wait_until_clear) != 0 ||
-            !pv->compare_exchange_weak(v, v & ~bits,
-                                       std::memory_order_release,
-                                       std::memory_order_relaxed)));
-}
-
-//------------------------------------------------------------------
-
-// Data for doing deadlock detection.
-ABSL_CONST_INIT static absl::base_internal::SpinLock deadlock_graph_mu(
-    absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
-
-// Graph used to detect deadlocks.
-ABSL_CONST_INIT static GraphCycles *deadlock_graph
-    ABSL_GUARDED_BY(deadlock_graph_mu) ABSL_PT_GUARDED_BY(deadlock_graph_mu);
-
-//------------------------------------------------------------------
-// An event mechanism for debugging mutex use.
-// It also allows mutexes to be given names for those who can't handle
-// addresses, and instead like to give their data structures names like
-// "Henry", "Fido", or "Rupert IV, King of Yondavia".
-
-namespace {  // to prevent name pollution
-enum {       // Mutex and CondVar events passed as "ev" to PostSynchEvent
-             // Mutex events
-  SYNCH_EV_TRYLOCK_SUCCESS,
-  SYNCH_EV_TRYLOCK_FAILED,
-  SYNCH_EV_READERTRYLOCK_SUCCESS,
-  SYNCH_EV_READERTRYLOCK_FAILED,
-  SYNCH_EV_LOCK,
-  SYNCH_EV_LOCK_RETURNING,
-  SYNCH_EV_READERLOCK,
-  SYNCH_EV_READERLOCK_RETURNING,
-  SYNCH_EV_UNLOCK,
-  SYNCH_EV_READERUNLOCK,
-
-  // CondVar events
-  SYNCH_EV_WAIT,
-  SYNCH_EV_WAIT_RETURNING,
-  SYNCH_EV_SIGNAL,
-  SYNCH_EV_SIGNALALL,
-};
-
-enum {                    // Event flags
-  SYNCH_F_R = 0x01,       // reader event
-  SYNCH_F_LCK = 0x02,     // PostSynchEvent called with mutex held
-  SYNCH_F_TRY = 0x04,     // TryLock or ReaderTryLock
-  SYNCH_F_UNLOCK = 0x08,  // Unlock or ReaderUnlock
-
-  SYNCH_F_LCK_W = SYNCH_F_LCK,
-  SYNCH_F_LCK_R = SYNCH_F_LCK | SYNCH_F_R,
-};
-}  // anonymous namespace
-
-// Properties of the events.
-static const struct {
-  int flags;
-  const char *msg;
-} event_properties[] = {
-    {SYNCH_F_LCK_W | SYNCH_F_TRY, "TryLock succeeded "},
-    {0, "TryLock failed "},
-    {SYNCH_F_LCK_R | SYNCH_F_TRY, "ReaderTryLock succeeded "},
-    {0, "ReaderTryLock failed "},
-    {0, "Lock blocking "},
-    {SYNCH_F_LCK_W, "Lock returning "},
-    {0, "ReaderLock blocking "},
-    {SYNCH_F_LCK_R, "ReaderLock returning "},
-    {SYNCH_F_LCK_W | SYNCH_F_UNLOCK, "Unlock "},
-    {SYNCH_F_LCK_R | SYNCH_F_UNLOCK, "ReaderUnlock "},
-    {0, "Wait on "},
-    {0, "Wait unblocked "},
-    {0, "Signal on "},
-    {0, "SignalAll on "},
-};
-
-ABSL_CONST_INIT static absl::base_internal::SpinLock synch_event_mu(
-    absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
-
-// Hash table size; should be prime > 2.
-// Can't be too small, as it's used for deadlock detection information.
-static constexpr uint32_t kNSynchEvent = 1031;
-
-static struct SynchEvent {     // this is a trivial hash table for the events
-  // struct is freed when refcount reaches 0
-  int refcount ABSL_GUARDED_BY(synch_event_mu);
-
-  // buckets have linear, 0-terminated  chains
-  SynchEvent *next ABSL_GUARDED_BY(synch_event_mu);
-
-  // Constant after initialization
-  uintptr_t masked_addr;  // object at this address is called "name"
-
-  // No explicit synchronization used.  Instead we assume that the
-  // client who enables/disables invariants/logging on a Mutex does so
-  // while the Mutex is not being concurrently accessed by others.
-  void (*invariant)(void *arg);  // called on each event
-  void *arg;            // first arg to (*invariant)()
-  bool log;             // logging turned on
-
-  // Constant after initialization
-  char name[1];         // actually longer---NUL-terminated string
-} * synch_event[kNSynchEvent] ABSL_GUARDED_BY(synch_event_mu);
-
-// Ensure that the object at "addr" has a SynchEvent struct associated with it,
-// set "bits" in the word there (waiting until lockbit is clear before doing
-// so), and return a refcounted reference that will remain valid until
-// UnrefSynchEvent() is called.  If a new SynchEvent is allocated,
-// the string name is copied into it.
-// When used with a mutex, the caller should also ensure that kMuEvent
-// is set in the mutex word, and similarly for condition variables and kCVEvent.
-static SynchEvent *EnsureSynchEvent(std::atomic<intptr_t> *addr,
-                                    const char *name, intptr_t bits,
-                                    intptr_t lockbit) {
-  uint32_t h = reinterpret_cast<intptr_t>(addr) % kNSynchEvent;
-  SynchEvent *e;
-  // first look for existing SynchEvent struct..
-  synch_event_mu.Lock();
-  for (e = synch_event[h];
-       e != nullptr && e->masked_addr != base_internal::HidePtr(addr);
-       e = e->next) {
-  }
-  if (e == nullptr) {  // no SynchEvent struct found; make one.
-    if (name == nullptr) {
-      name = "";
-    }
-    size_t l = strlen(name);
-    e = reinterpret_cast<SynchEvent *>(
-        base_internal::LowLevelAlloc::Alloc(sizeof(*e) + l));
-    e->refcount = 2;    // one for return value, one for linked list
-    e->masked_addr = base_internal::HidePtr(addr);
-    e->invariant = nullptr;
-    e->arg = nullptr;
-    e->log = false;
-    strcpy(e->name, name);  // NOLINT(runtime/printf)
-    e->next = synch_event[h];
-    AtomicSetBits(addr, bits, lockbit);
-    synch_event[h] = e;
-  } else {
-    e->refcount++;      // for return value
-  }
-  synch_event_mu.Unlock();
-  return e;
-}
-
-// Deallocate the SynchEvent *e, whose refcount has fallen to zero.
-static void DeleteSynchEvent(SynchEvent *e) {
-  base_internal::LowLevelAlloc::Free(e);
-}
-
-// Decrement the reference count of *e, or do nothing if e==null.
-static void UnrefSynchEvent(SynchEvent *e) {
-  if (e != nullptr) {
-    synch_event_mu.Lock();
-    bool del = (--(e->refcount) == 0);
-    synch_event_mu.Unlock();
-    if (del) {
-      DeleteSynchEvent(e);
-    }
-  }
-}
-
-// Forget the mapping from the object (Mutex or CondVar) at address addr
-// to SynchEvent object, and clear "bits" in its word (waiting until lockbit
-// is clear before doing so).
-static void ForgetSynchEvent(std::atomic<intptr_t> *addr, intptr_t bits,
-                             intptr_t lockbit) {
-  uint32_t h = reinterpret_cast<intptr_t>(addr) % kNSynchEvent;
-  SynchEvent **pe;
-  SynchEvent *e;
-  synch_event_mu.Lock();
-  for (pe = &synch_event[h];
-       (e = *pe) != nullptr && e->masked_addr != base_internal::HidePtr(addr);
-       pe = &e->next) {
-  }
-  bool del = false;
-  if (e != nullptr) {
-    *pe = e->next;
-    del = (--(e->refcount) == 0);
-  }
-  AtomicClearBits(addr, bits, lockbit);
-  synch_event_mu.Unlock();
-  if (del) {
-    DeleteSynchEvent(e);
-  }
-}
-
-// Return a refcounted reference to the SynchEvent of the object at address
-// "addr", if any.  The pointer returned is valid until the UnrefSynchEvent() is
-// called.
-static SynchEvent *GetSynchEvent(const void *addr) {
-  uint32_t h = reinterpret_cast<intptr_t>(addr) % kNSynchEvent;
-  SynchEvent *e;
-  synch_event_mu.Lock();
-  for (e = synch_event[h];
-       e != nullptr && e->masked_addr != base_internal::HidePtr(addr);
-       e = e->next) {
-  }
-  if (e != nullptr) {
-    e->refcount++;
-  }
-  synch_event_mu.Unlock();
-  return e;
-}
-
-// Called when an event "ev" occurs on a Mutex of CondVar "obj"
-// if event recording is on
-static void PostSynchEvent(void *obj, int ev) {
-  SynchEvent *e = GetSynchEvent(obj);
-  // logging is on if event recording is on and either there's no event struct,
-  // or it explicitly says to log
-  if (e == nullptr || e->log) {
-    void *pcs[40];
-    int n = absl::GetStackTrace(pcs, ABSL_ARRAYSIZE(pcs), 1);
-    // A buffer with enough space for the ASCII for all the PCs, even on a
-    // 64-bit machine.
-    char buffer[ABSL_ARRAYSIZE(pcs) * 24];
-    int pos = snprintf(buffer, sizeof (buffer), " @");
-    for (int i = 0; i != n; i++) {
-      pos += snprintf(&buffer[pos], sizeof (buffer) - pos, " %p", pcs[i]);
-    }
-    ABSL_RAW_LOG(INFO, "%s%p %s %s", event_properties[ev].msg, obj,
-                 (e == nullptr ? "" : e->name), buffer);
-  }
-  const int flags = event_properties[ev].flags;
-  if ((flags & SYNCH_F_LCK) != 0 && e != nullptr && e->invariant != nullptr) {
-    // Calling the invariant as is causes problems under ThreadSanitizer.
-    // We are currently inside of Mutex Lock/Unlock and are ignoring all
-    // memory accesses and synchronization. If the invariant transitively
-    // synchronizes something else and we ignore the synchronization, we will
-    // get false positive race reports later.
-    // Reuse EvalConditionAnnotated to properly call into user code.
-    struct local {
-      static bool pred(SynchEvent *ev) {
-        (*ev->invariant)(ev->arg);
-        return false;
-      }
-    };
-    Condition cond(&local::pred, e);
-    Mutex *mu = static_cast<Mutex *>(obj);
-    const bool locking = (flags & SYNCH_F_UNLOCK) == 0;
-    const bool trylock = (flags & SYNCH_F_TRY) != 0;
-    const bool read_lock = (flags & SYNCH_F_R) != 0;
-    EvalConditionAnnotated(&cond, mu, locking, trylock, read_lock);
-  }
-  UnrefSynchEvent(e);
-}
-
-//------------------------------------------------------------------
-
-// The SynchWaitParams struct encapsulates the way in which a thread is waiting:
-// whether it has a timeout, the condition, exclusive/shared, and whether a
-// condition variable wait has an associated Mutex (as opposed to another
-// type of lock).  It also points to the PerThreadSynch struct of its thread.
-// cv_word tells Enqueue() to enqueue on a CondVar using CondVarEnqueue().
-//
-// This structure is held on the stack rather than directly in
-// PerThreadSynch because a thread can be waiting on multiple Mutexes if,
-// while waiting on one Mutex, the implementation calls a client callback
-// (such as a Condition function) that acquires another Mutex. We don't
-// strictly need to allow this, but programmers become confused if we do not
-// allow them to use functions such a LOG() within Condition functions.  The
-// PerThreadSynch struct points at the most recent SynchWaitParams struct when
-// the thread is on a Mutex's waiter queue.
-struct SynchWaitParams {
-  SynchWaitParams(Mutex::MuHow how_arg, const Condition *cond_arg,
-                  KernelTimeout timeout_arg, Mutex *cvmu_arg,
-                  PerThreadSynch *thread_arg,
-                  std::atomic<intptr_t> *cv_word_arg)
-      : how(how_arg),
-        cond(cond_arg),
-        timeout(timeout_arg),
-        cvmu(cvmu_arg),
-        thread(thread_arg),
-        cv_word(cv_word_arg),
-        contention_start_cycles(base_internal::CycleClock::Now()) {}
-
-  const Mutex::MuHow how;  // How this thread needs to wait.
-  const Condition *cond;  // The condition that this thread is waiting for.
-                          // In Mutex, this field is set to zero if a timeout
-                          // expires.
-  KernelTimeout timeout;  // timeout expiry---absolute time
-                          // In Mutex, this field is set to zero if a timeout
-                          // expires.
-  Mutex *const cvmu;      // used for transfer from cond var to mutex
-  PerThreadSynch *const thread;  // thread that is waiting
-
-  // If not null, thread should be enqueued on the CondVar whose state
-  // word is cv_word instead of queueing normally on the Mutex.
-  std::atomic<intptr_t> *cv_word;
-
-  int64_t contention_start_cycles;  // Time (in cycles) when this thread started
-                                    // to contend for the mutex.
-};
-
-struct SynchLocksHeld {
-  int n;              // number of valid entries in locks[]
-  bool overflow;      // true iff we overflowed the array at some point
-  struct {
-    Mutex *mu;        // lock acquired
-    int32_t count;      // times acquired
-    GraphId id;       // deadlock_graph id of acquired lock
-  } locks[40];
-  // If a thread overfills the array during deadlock detection, we
-  // continue, discarding information as needed.  If no overflow has
-  // taken place, we can provide more error checking, such as
-  // detecting when a thread releases a lock it does not hold.
-};
-
-// A sentinel value in lists that is not 0.
-// A 0 value is used to mean "not on a list".
-static PerThreadSynch *const kPerThreadSynchNull =
-  reinterpret_cast<PerThreadSynch *>(1);
-
-static SynchLocksHeld *LocksHeldAlloc() {
-  SynchLocksHeld *ret = reinterpret_cast<SynchLocksHeld *>(
-      base_internal::LowLevelAlloc::Alloc(sizeof(SynchLocksHeld)));
-  ret->n = 0;
-  ret->overflow = false;
-  return ret;
-}
-
-// Return the PerThreadSynch-struct for this thread.
-static PerThreadSynch *Synch_GetPerThread() {
-  ThreadIdentity *identity = GetOrCreateCurrentThreadIdentity();
-  return &identity->per_thread_synch;
-}
-
-static PerThreadSynch *Synch_GetPerThreadAnnotated(Mutex *mu) {
-  if (mu) {
-    ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
-  }
-  PerThreadSynch *w = Synch_GetPerThread();
-  if (mu) {
-    ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
-  }
-  return w;
-}
-
-static SynchLocksHeld *Synch_GetAllLocks() {
-  PerThreadSynch *s = Synch_GetPerThread();
-  if (s->all_locks == nullptr) {
-    s->all_locks = LocksHeldAlloc();  // Freed by ReclaimThreadIdentity.
-  }
-  return s->all_locks;
-}
-
-// Post on "w"'s associated PerThreadSem.
-inline void Mutex::IncrementSynchSem(Mutex *mu, PerThreadSynch *w) {
-  if (mu) {
-    ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
-  }
-  PerThreadSem::Post(w->thread_identity());
-  if (mu) {
-    ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
-  }
-}
-
-// Wait on "w"'s associated PerThreadSem; returns false if timeout expired.
-bool Mutex::DecrementSynchSem(Mutex *mu, PerThreadSynch *w, KernelTimeout t) {
-  if (mu) {
-    ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
-  }
-  assert(w == Synch_GetPerThread());
-  static_cast<void>(w);
-  bool res = PerThreadSem::Wait(t);
-  if (mu) {
-    ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
-  }
-  return res;
-}
-
-// We're in a fatal signal handler that hopes to use Mutex and to get
-// lucky by not deadlocking.  We try to improve its chances of success
-// by effectively disabling some of the consistency checks.  This will
-// prevent certain ABSL_RAW_CHECK() statements from being triggered when
-// re-rentry is detected.  The ABSL_RAW_CHECK() statements are those in the
-// Mutex code checking that the "waitp" field has not been reused.
-void Mutex::InternalAttemptToUseMutexInFatalSignalHandler() {
-  // Fix the per-thread state only if it exists.
-  ThreadIdentity *identity = CurrentThreadIdentityIfPresent();
-  if (identity != nullptr) {
-    identity->per_thread_synch.suppress_fatal_errors = true;
-  }
-  // Don't do deadlock detection when we are already failing.
-  synch_deadlock_detection.store(OnDeadlockCycle::kIgnore,
-                                 std::memory_order_release);
-}
-
-// --------------------------time support
-
-// Return the current time plus the timeout.  Use the same clock as
-// PerThreadSem::Wait() for consistency.  Unfortunately, we don't have
-// such a choice when a deadline is given directly.
-static absl::Time DeadlineFromTimeout(absl::Duration timeout) {
-#ifndef _WIN32
-  struct timeval tv;
-  gettimeofday(&tv, nullptr);
-  return absl::TimeFromTimeval(tv) + timeout;
-#else
-  return absl::Now() + timeout;
-#endif
-}
-
-// --------------------------Mutexes
-
-// In the layout below, the msb of the bottom byte is currently unused.  Also,
-// the following constraints were considered in choosing the layout:
-//  o Both the debug allocator's "uninitialized" and "freed" patterns (0xab and
-//    0xcd) are illegal: reader and writer lock both held.
-//  o kMuWriter and kMuEvent should exceed kMuDesig and kMuWait, to enable the
-//    bit-twiddling trick in Mutex::Unlock().
-//  o kMuWriter / kMuReader == kMuWrWait / kMuWait,
-//    to enable the bit-twiddling trick in CheckForMutexCorruption().
-static const intptr_t kMuReader      = 0x0001L;  // a reader holds the lock
-static const intptr_t kMuDesig       = 0x0002L;  // there's a designated waker
-static const intptr_t kMuWait        = 0x0004L;  // threads are waiting
-static const intptr_t kMuWriter      = 0x0008L;  // a writer holds the lock
-static const intptr_t kMuEvent       = 0x0010L;  // record this mutex's events
-// INVARIANT1:  there's a thread that was blocked on the mutex, is
-// no longer, yet has not yet acquired the mutex.  If there's a
-// designated waker, all threads can avoid taking the slow path in
-// unlock because the designated waker will subsequently acquire
-// the lock and wake someone.  To maintain INVARIANT1 the bit is
-// set when a thread is unblocked(INV1a), and threads that were
-// unblocked reset the bit when they either acquire or re-block
-// (INV1b).
-static const intptr_t kMuWrWait      = 0x0020L;  // runnable writer is waiting
-                                                 // for a reader
-static const intptr_t kMuSpin        = 0x0040L;  // spinlock protects wait list
-static const intptr_t kMuLow         = 0x00ffL;  // mask all mutex bits
-static const intptr_t kMuHigh        = ~kMuLow;  // mask pointer/reader count
-
-// Hack to make constant values available to gdb pretty printer
-enum {
-  kGdbMuSpin = kMuSpin,
-  kGdbMuEvent = kMuEvent,
-  kGdbMuWait = kMuWait,
-  kGdbMuWriter = kMuWriter,
-  kGdbMuDesig = kMuDesig,
-  kGdbMuWrWait = kMuWrWait,
-  kGdbMuReader = kMuReader,
-  kGdbMuLow = kMuLow,
-};
-
-// kMuWrWait implies kMuWait.
-// kMuReader and kMuWriter are mutually exclusive.
-// If kMuReader is zero, there are no readers.
-// Otherwise, if kMuWait is zero, the high order bits contain a count of the
-// number of readers.  Otherwise, the reader count is held in
-// PerThreadSynch::readers of the most recently queued waiter, again in the
-// bits above kMuLow.
-static const intptr_t kMuOne = 0x0100;  // a count of one reader
-
-// flags passed to Enqueue and LockSlow{,WithTimeout,Loop}
-static const int kMuHasBlocked = 0x01;  // already blocked (MUST == 1)
-static const int kMuIsCond = 0x02;      // conditional waiter (CV or Condition)
-
-static_assert(PerThreadSynch::kAlignment > kMuLow,
-              "PerThreadSynch::kAlignment must be greater than kMuLow");
-
-// This struct contains various bitmasks to be used in
-// acquiring and releasing a mutex in a particular mode.
-struct MuHowS {
-  // if all the bits in fast_need_zero are zero, the lock can be acquired by
-  // adding fast_add and oring fast_or.  The bit kMuDesig should be reset iff
-  // this is the designated waker.
-  intptr_t fast_need_zero;
-  intptr_t fast_or;
-  intptr_t fast_add;
-
-  intptr_t slow_need_zero;  // fast_need_zero with events (e.g. logging)
-
-  intptr_t slow_inc_need_zero;  // if all the bits in slow_inc_need_zero are
-                                // zero a reader can acquire a read share by
-                                // setting the reader bit and incrementing
-                                // the reader count (in last waiter since
-                                // we're now slow-path).  kMuWrWait be may
-                                // be ignored if we already waited once.
-};
-
-static const MuHowS kSharedS = {
-    // shared or read lock
-    kMuWriter | kMuWait | kMuEvent,   // fast_need_zero
-    kMuReader,                        // fast_or
-    kMuOne,                           // fast_add
-    kMuWriter | kMuWait,              // slow_need_zero
-    kMuSpin | kMuWriter | kMuWrWait,  // slow_inc_need_zero
-};
-static const MuHowS kExclusiveS = {
-    // exclusive or write lock
-    kMuWriter | kMuReader | kMuEvent,  // fast_need_zero
-    kMuWriter,                         // fast_or
-    0,                                 // fast_add
-    kMuWriter | kMuReader,             // slow_need_zero
-    ~static_cast<intptr_t>(0),         // slow_inc_need_zero
-};
-static const Mutex::MuHow kShared = &kSharedS;        // shared lock
-static const Mutex::MuHow kExclusive = &kExclusiveS;  // exclusive lock
-
-#ifdef NDEBUG
-static constexpr bool kDebugMode = false;
-#else
-static constexpr bool kDebugMode = true;
-#endif
-
-#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE
-static unsigned TsanFlags(Mutex::MuHow how) {
-  return how == kShared ? __tsan_mutex_read_lock : 0;
-}
-#endif
-
-static bool DebugOnlyIsExiting() {
-  return false;
-}
-
-Mutex::~Mutex() {
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  if ((v & kMuEvent) != 0 && !DebugOnlyIsExiting()) {
-    ForgetSynchEvent(&this->mu_, kMuEvent, kMuSpin);
-  }
-  if (kDebugMode) {
-    this->ForgetDeadlockInfo();
-  }
-  ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static);
-}
-
-void Mutex::EnableDebugLog(const char *name) {
-  SynchEvent *e = EnsureSynchEvent(&this->mu_, name, kMuEvent, kMuSpin);
-  e->log = true;
-  UnrefSynchEvent(e);
-}
-
-void EnableMutexInvariantDebugging(bool enabled) {
-  synch_check_invariants.store(enabled, std::memory_order_release);
-}
-
-void Mutex::EnableInvariantDebugging(void (*invariant)(void *),
-                                     void *arg) {
-  if (synch_check_invariants.load(std::memory_order_acquire) &&
-      invariant != nullptr) {
-    SynchEvent *e = EnsureSynchEvent(&this->mu_, nullptr, kMuEvent, kMuSpin);
-    e->invariant = invariant;
-    e->arg = arg;
-    UnrefSynchEvent(e);
-  }
-}
-
-void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode) {
-  synch_deadlock_detection.store(mode, std::memory_order_release);
-}
-
-// Return true iff threads x and y are waiting on the same condition for the
-// same type of lock.  Requires that x and y be waiting on the same Mutex
-// queue.
-static bool MuSameCondition(PerThreadSynch *x, PerThreadSynch *y) {
-  return x->waitp->how == y->waitp->how &&
-         Condition::GuaranteedEqual(x->waitp->cond, y->waitp->cond);
-}
-
-// Given the contents of a mutex word containing a PerThreadSynch pointer,
-// return the pointer.
-static inline PerThreadSynch *GetPerThreadSynch(intptr_t v) {
-  return reinterpret_cast<PerThreadSynch *>(v & kMuHigh);
-}
-
-// The next several routines maintain the per-thread next and skip fields
-// used in the Mutex waiter queue.
-// The queue is a circular singly-linked list, of which the "head" is the
-// last element, and head->next if the first element.
-// The skip field has the invariant:
-//   For thread x, x->skip is one of:
-//     - invalid (iff x is not in a Mutex wait queue),
-//     - null, or
-//     - a pointer to a distinct thread waiting later in the same Mutex queue
-//       such that all threads in [x, x->skip] have the same condition and
-//       lock type (MuSameCondition() is true for all pairs in [x, x->skip]).
-// In addition, if x->skip is  valid, (x->may_skip || x->skip == null)
-//
-// By the spec of MuSameCondition(), it is not necessary when removing the
-// first runnable thread y from the front a Mutex queue to adjust the skip
-// field of another thread x because if x->skip==y, x->skip must (have) become
-// invalid before y is removed.  The function TryRemove can remove a specified
-// thread from an arbitrary position in the queue whether runnable or not, so
-// it fixes up skip fields that would otherwise be left dangling.
-// The statement
-//     if (x->may_skip && MuSameCondition(x, x->next)) { x->skip = x->next; }
-// maintains the invariant provided x is not the last waiter in a Mutex queue
-// The statement
-//          if (x->skip != null) { x->skip = x->skip->skip; }
-// maintains the invariant.
-
-// Returns the last thread y in a mutex waiter queue such that all threads in
-// [x, y] inclusive share the same condition.  Sets skip fields of some threads
-// in that range to optimize future evaluation of Skip() on x values in
-// the range.  Requires thread x is in a mutex waiter queue.
-// The locking is unusual.  Skip() is called under these conditions:
-//   - spinlock is held in call from Enqueue(), with maybe_unlocking == false
-//   - Mutex is held in call from UnlockSlow() by last unlocker, with
-//     maybe_unlocking == true
-//   - both Mutex and spinlock are held in call from DequeueAllWakeable() (from
-//     UnlockSlow()) and TryRemove()
-// These cases are mutually exclusive, so Skip() never runs concurrently
-// with itself on the same Mutex.   The skip chain is used in these other places
-// that cannot occur concurrently:
-//   - FixSkip() (from TryRemove()) - spinlock and Mutex are held)
-//   - Dequeue() (with spinlock and Mutex held)
-//   - UnlockSlow() (with spinlock and Mutex held)
-// A more complex case is Enqueue()
-//   - Enqueue() (with spinlock held and maybe_unlocking == false)
-//               This is the first case in which Skip is called, above.
-//   - Enqueue() (without spinlock held; but queue is empty and being freshly
-//                formed)
-//   - Enqueue() (with spinlock held and maybe_unlocking == true)
-// The first case has mutual exclusion, and the second isolation through
-// working on an otherwise unreachable data structure.
-// In the last case, Enqueue() is required to change no skip/next pointers
-// except those in the added node and the former "head" node.  This implies
-// that the new node is added after head, and so must be the new head or the
-// new front of the queue.
-static PerThreadSynch *Skip(PerThreadSynch *x) {
-  PerThreadSynch *x0 = nullptr;
-  PerThreadSynch *x1 = x;
-  PerThreadSynch *x2 = x->skip;
-  if (x2 != nullptr) {
-    // Each iteration attempts to advance sequence (x0,x1,x2) to next sequence
-    // such that   x1 == x0->skip && x2 == x1->skip
-    while ((x0 = x1, x1 = x2, x2 = x2->skip) != nullptr) {
-      x0->skip = x2;      // short-circuit skip from x0 to x2
-    }
-    x->skip = x1;         // short-circuit skip from x to result
-  }
-  return x1;
-}
-
-// "ancestor" appears before "to_be_removed" in the same Mutex waiter queue.
-// The latter is going to be removed out of order, because of a timeout.
-// Check whether "ancestor" has a skip field pointing to "to_be_removed",
-// and fix it if it does.
-static void FixSkip(PerThreadSynch *ancestor, PerThreadSynch *to_be_removed) {
-  if (ancestor->skip == to_be_removed) {  // ancestor->skip left dangling
-    if (to_be_removed->skip != nullptr) {
-      ancestor->skip = to_be_removed->skip;  // can skip past to_be_removed
-    } else if (ancestor->next != to_be_removed) {  // they are not adjacent
-      ancestor->skip = ancestor->next;             // can skip one past ancestor
-    } else {
-      ancestor->skip = nullptr;  // can't skip at all
-    }
-  }
-}
-
-static void CondVarEnqueue(SynchWaitParams *waitp);
-
-// Enqueue thread "waitp->thread" on a waiter queue.
-// Called with mutex spinlock held if head != nullptr
-// If head==nullptr and waitp->cv_word==nullptr, then Enqueue() is
-// idempotent; it alters no state associated with the existing (empty)
-// queue.
-//
-// If waitp->cv_word == nullptr, queue the thread at either the front or
-// the end (according to its priority) of the circular mutex waiter queue whose
-// head is "head", and return the new head.  mu is the previous mutex state,
-// which contains the reader count (perhaps adjusted for the operation in
-// progress) if the list was empty and a read lock held, and the holder hint if
-// the list was empty and a write lock held.  (flags & kMuIsCond) indicates
-// whether this thread was transferred from a CondVar or is waiting for a
-// non-trivial condition.  In this case, Enqueue() never returns nullptr
-//
-// If waitp->cv_word != nullptr, CondVarEnqueue() is called, and "head" is
-// returned. This mechanism is used by CondVar to queue a thread on the
-// condition variable queue instead of the mutex queue in implementing Wait().
-// In this case, Enqueue() can return nullptr (if head==nullptr).
-static PerThreadSynch *Enqueue(PerThreadSynch *head,
-                               SynchWaitParams *waitp, intptr_t mu, int flags) {
-  // If we have been given a cv_word, call CondVarEnqueue() and return
-  // the previous head of the Mutex waiter queue.
-  if (waitp->cv_word != nullptr) {
-    CondVarEnqueue(waitp);
-    return head;
-  }
-
-  PerThreadSynch *s = waitp->thread;
-  ABSL_RAW_CHECK(
-      s->waitp == nullptr ||    // normal case
-          s->waitp == waitp ||  // Fer()---transfer from condition variable
-          s->suppress_fatal_errors,
-      "detected illegal recursion into Mutex code");
-  s->waitp = waitp;
-  s->skip = nullptr;             // maintain skip invariant (see above)
-  s->may_skip = true;            // always true on entering queue
-  s->wake = false;               // not being woken
-  s->cond_waiter = ((flags & kMuIsCond) != 0);
-  if (head == nullptr) {         // s is the only waiter
-    s->next = s;                 // it's the only entry in the cycle
-    s->readers = mu;             // reader count is from mu word
-    s->maybe_unlocking = false;  // no one is searching an empty list
-    head = s;                    // s is new head
-  } else {
-    PerThreadSynch *enqueue_after = nullptr;  // we'll put s after this element
-#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
-    int64_t now_cycles = base_internal::CycleClock::Now();
-    if (s->next_priority_read_cycles < now_cycles) {
-      // Every so often, update our idea of the thread's priority.
-      // pthread_getschedparam() is 5% of the block/wakeup time;
-      // base_internal::CycleClock::Now() is 0.5%.
-      int policy;
-      struct sched_param param;
-      const int err = pthread_getschedparam(pthread_self(), &policy, &param);
-      if (err != 0) {
-        ABSL_RAW_LOG(ERROR, "pthread_getschedparam failed: %d", err);
-      } else {
-        s->priority = param.sched_priority;
-        s->next_priority_read_cycles =
-            now_cycles +
-            static_cast<int64_t>(base_internal::CycleClock::Frequency());
-      }
-    }
-    if (s->priority > head->priority) {  // s's priority is above head's
-      // try to put s in priority-fifo order, or failing that at the front.
-      if (!head->maybe_unlocking) {
-        // No unlocker can be scanning the queue, so we can insert between
-        // skip-chains, and within a skip-chain if it has the same condition as
-        // s.  We insert in priority-fifo order, examining the end of every
-        // skip-chain, plus every element with the same condition as s.
-        PerThreadSynch *advance_to = head;    // next value of enqueue_after
-        PerThreadSynch *cur;                  // successor of enqueue_after
-        do {
-          enqueue_after = advance_to;
-          cur = enqueue_after->next;  // this advance ensures progress
-          advance_to = Skip(cur);   // normally, advance to end of skip chain
-                                    // (side-effect: optimizes skip chain)
-          if (advance_to != cur && s->priority > advance_to->priority &&
-              MuSameCondition(s, cur)) {
-            // but this skip chain is not a singleton, s has higher priority
-            // than its tail and has the same condition as the chain,
-            // so we can insert within the skip-chain
-            advance_to = cur;         // advance by just one
-          }
-        } while (s->priority <= advance_to->priority);
-              // termination guaranteed because s->priority > head->priority
-              // and head is the end of a skip chain
-      } else if (waitp->how == kExclusive &&
-                 Condition::GuaranteedEqual(waitp->cond, nullptr)) {
-        // An unlocker could be scanning the queue, but we know it will recheck
-        // the queue front for writers that have no condition, which is what s
-        // is, so an insert at front is safe.
-        enqueue_after = head;       // add after head, at front
-      }
-    }
-#endif
-    if (enqueue_after != nullptr) {
-      s->next = enqueue_after->next;
-      enqueue_after->next = s;
-
-      // enqueue_after can be: head, Skip(...), or cur.
-      // The first two imply enqueue_after->skip == nullptr, and
-      // the last is used only if MuSameCondition(s, cur).
-      // We require this because clearing enqueue_after->skip
-      // is impossible; enqueue_after's predecessors might also
-      // incorrectly skip over s if we were to allow other
-      // insertion points.
-      ABSL_RAW_CHECK(
-          enqueue_after->skip == nullptr || MuSameCondition(enqueue_after, s),
-          "Mutex Enqueue failure");
-
-      if (enqueue_after != head && enqueue_after->may_skip &&
-          MuSameCondition(enqueue_after, enqueue_after->next)) {
-        // enqueue_after can skip to its new successor, s
-        enqueue_after->skip = enqueue_after->next;
-      }
-      if (MuSameCondition(s, s->next)) {  // s->may_skip is known to be true
-        s->skip = s->next;                // s may skip to its successor
-      }
-    } else {   // enqueue not done any other way, so
-               // we're inserting s at the back
-      // s will become new head; copy data from head into it
-      s->next = head->next;        // add s after head
-      head->next = s;
-      s->readers = head->readers;  // reader count is from previous head
-      s->maybe_unlocking = head->maybe_unlocking;  // same for unlock hint
-      if (head->may_skip && MuSameCondition(head, s)) {
-        // head now has successor; may skip
-        head->skip = s;
-      }
-      head = s;  // s is new head
-    }
-  }
-  s->state.store(PerThreadSynch::kQueued, std::memory_order_relaxed);
-  return head;
-}
-
-// Dequeue the successor pw->next of thread pw from the Mutex waiter queue
-// whose last element is head.  The new head element is returned, or null
-// if the list is made empty.
-// Dequeue is called with both spinlock and Mutex held.
-static PerThreadSynch *Dequeue(PerThreadSynch *head, PerThreadSynch *pw) {
-  PerThreadSynch *w = pw->next;
-  pw->next = w->next;         // snip w out of list
-  if (head == w) {            // we removed the head
-    head = (pw == w) ? nullptr : pw;  // either emptied list, or pw is new head
-  } else if (pw != head && MuSameCondition(pw, pw->next)) {
-    // pw can skip to its new successor
-    if (pw->next->skip !=
-        nullptr) {  // either skip to its successors skip target
-      pw->skip = pw->next->skip;
-    } else {                   // or to pw's successor
-      pw->skip = pw->next;
-    }
-  }
-  return head;
-}
-
-// Traverse the elements [ pw->next, h] of the circular list whose last element
-// is head.
-// Remove all elements with wake==true and place them in the
-// singly-linked list wake_list in the order found.   Assumes that
-// there is only one such element if the element has how == kExclusive.
-// Return the new head.
-static PerThreadSynch *DequeueAllWakeable(PerThreadSynch *head,
-                                          PerThreadSynch *pw,
-                                          PerThreadSynch **wake_tail) {
-  PerThreadSynch *orig_h = head;
-  PerThreadSynch *w = pw->next;
-  bool skipped = false;
-  do {
-    if (w->wake) {                    // remove this element
-      ABSL_RAW_CHECK(pw->skip == nullptr, "bad skip in DequeueAllWakeable");
-      // we're removing pw's successor so either pw->skip is zero or we should
-      // already have removed pw since if pw->skip!=null, pw has the same
-      // condition as w.
-      head = Dequeue(head, pw);
-      w->next = *wake_tail;           // keep list terminated
-      *wake_tail = w;                 // add w to wake_list;
-      wake_tail = &w->next;           // next addition to end
-      if (w->waitp->how == kExclusive) {  // wake at most 1 writer
-        break;
-      }
-    } else {                // not waking this one; skip
-      pw = Skip(w);       // skip as much as possible
-      skipped = true;
-    }
-    w = pw->next;
-    // We want to stop processing after we've considered the original head,
-    // orig_h.  We can't test for w==orig_h in the loop because w may skip over
-    // it; we are guaranteed only that w's predecessor will not skip over
-    // orig_h.  When we've considered orig_h, either we've processed it and
-    // removed it (so orig_h != head), or we considered it and skipped it (so
-    // skipped==true && pw == head because skipping from head always skips by
-    // just one, leaving pw pointing at head).  So we want to
-    // continue the loop with the negation of that expression.
-  } while (orig_h == head && (pw != head || !skipped));
-  return head;
-}
-
-// Try to remove thread s from the list of waiters on this mutex.
-// Does nothing if s is not on the waiter list.
-void Mutex::TryRemove(PerThreadSynch *s) {
-  SchedulingGuard::ScopedDisable disable_rescheduling;
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  // acquire spinlock & lock
-  if ((v & (kMuWait | kMuSpin | kMuWriter | kMuReader)) == kMuWait &&
-      mu_.compare_exchange_strong(v, v | kMuSpin | kMuWriter,
-                                  std::memory_order_acquire,
-                                  std::memory_order_relaxed)) {
-    PerThreadSynch *h = GetPerThreadSynch(v);
-    if (h != nullptr) {
-      PerThreadSynch *pw = h;   // pw is w's predecessor
-      PerThreadSynch *w;
-      if ((w = pw->next) != s) {  // search for thread,
-        do {                      // processing at least one element
-          if (!MuSameCondition(s, w)) {  // seeking different condition
-            pw = Skip(w);                // so skip all that won't match
-            // we don't have to worry about dangling skip fields
-            // in the threads we skipped; none can point to s
-            // because their condition differs from s
-          } else {          // seeking same condition
-            FixSkip(w, s);  // fix up any skip pointer from w to s
-            pw = w;
-          }
-          // don't search further if we found the thread, or we're about to
-          // process the first thread again.
-        } while ((w = pw->next) != s && pw != h);
-      }
-      if (w == s) {                 // found thread; remove it
-        // pw->skip may be non-zero here; the loop above ensured that
-        // no ancestor of s can skip to s, so removal is safe anyway.
-        h = Dequeue(h, pw);
-        s->next = nullptr;
-        s->state.store(PerThreadSynch::kAvailable, std::memory_order_release);
-      }
-    }
-    intptr_t nv;
-    do {                        // release spinlock and lock
-      v = mu_.load(std::memory_order_relaxed);
-      nv = v & (kMuDesig | kMuEvent);
-      if (h != nullptr) {
-        nv |= kMuWait | reinterpret_cast<intptr_t>(h);
-        h->readers = 0;            // we hold writer lock
-        h->maybe_unlocking = false;  // finished unlocking
-      }
-    } while (!mu_.compare_exchange_weak(v, nv,
-                                        std::memory_order_release,
-                                        std::memory_order_relaxed));
-  }
-}
-
-// Wait until thread "s", which must be the current thread, is removed from the
-// this mutex's waiter queue.  If "s->waitp->timeout" has a timeout, wake up
-// if the wait extends past the absolute time specified, even if "s" is still
-// on the mutex queue.  In this case, remove "s" from the queue and return
-// true, otherwise return false.
-ABSL_XRAY_LOG_ARGS(1) void Mutex::Block(PerThreadSynch *s) {
-  while (s->state.load(std::memory_order_acquire) == PerThreadSynch::kQueued) {
-    if (!DecrementSynchSem(this, s, s->waitp->timeout)) {
-      // After a timeout, we go into a spin loop until we remove ourselves
-      // from the queue, or someone else removes us.  We can't be sure to be
-      // able to remove ourselves in a single lock acquisition because this
-      // mutex may be held, and the holder has the right to read the centre
-      // of the waiter queue without holding the spinlock.
-      this->TryRemove(s);
-      int c = 0;
-      while (s->next != nullptr) {
-        c = synchronization_internal::MutexDelay(c, GENTLE);
-        this->TryRemove(s);
-      }
-      if (kDebugMode) {
-        // This ensures that we test the case that TryRemove() is called when s
-        // is not on the queue.
-        this->TryRemove(s);
-      }
-      s->waitp->timeout = KernelTimeout::Never();      // timeout is satisfied
-      s->waitp->cond = nullptr;  // condition no longer relevant for wakeups
-    }
-  }
-  ABSL_RAW_CHECK(s->waitp != nullptr || s->suppress_fatal_errors,
-                 "detected illegal recursion in Mutex code");
-  s->waitp = nullptr;
-}
-
-// Wake thread w, and return the next thread in the list.
-PerThreadSynch *Mutex::Wakeup(PerThreadSynch *w) {
-  PerThreadSynch *next = w->next;
-  w->next = nullptr;
-  w->state.store(PerThreadSynch::kAvailable, std::memory_order_release);
-  IncrementSynchSem(this, w);
-
-  return next;
-}
-
-static GraphId GetGraphIdLocked(Mutex *mu)
-    ABSL_EXCLUSIVE_LOCKS_REQUIRED(deadlock_graph_mu) {
-  if (!deadlock_graph) {  // (re)create the deadlock graph.
-    deadlock_graph =
-        new (base_internal::LowLevelAlloc::Alloc(sizeof(*deadlock_graph)))
-            GraphCycles;
-  }
-  return deadlock_graph->GetId(mu);
-}
-
-static GraphId GetGraphId(Mutex *mu) ABSL_LOCKS_EXCLUDED(deadlock_graph_mu) {
-  deadlock_graph_mu.Lock();
-  GraphId id = GetGraphIdLocked(mu);
-  deadlock_graph_mu.Unlock();
-  return id;
-}
-
-// Record a lock acquisition.  This is used in debug mode for deadlock
-// detection.  The held_locks pointer points to the relevant data
-// structure for each case.
-static void LockEnter(Mutex* mu, GraphId id, SynchLocksHeld *held_locks) {
-  int n = held_locks->n;
-  int i = 0;
-  while (i != n && held_locks->locks[i].id != id) {
-    i++;
-  }
-  if (i == n) {
-    if (n == ABSL_ARRAYSIZE(held_locks->locks)) {
-      held_locks->overflow = true;  // lost some data
-    } else {                        // we have room for lock
-      held_locks->locks[i].mu = mu;
-      held_locks->locks[i].count = 1;
-      held_locks->locks[i].id = id;
-      held_locks->n = n + 1;
-    }
-  } else {
-    held_locks->locks[i].count++;
-  }
-}
-
-// Record a lock release.  Each call to LockEnter(mu, id, x) should be
-// eventually followed by a call to LockLeave(mu, id, x) by the same thread.
-// It does not process the event if is not needed when deadlock detection is
-// disabled.
-static void LockLeave(Mutex* mu, GraphId id, SynchLocksHeld *held_locks) {
-  int n = held_locks->n;
-  int i = 0;
-  while (i != n && held_locks->locks[i].id != id) {
-    i++;
-  }
-  if (i == n) {
-    if (!held_locks->overflow) {
-      // The deadlock id may have been reassigned after ForgetDeadlockInfo,
-      // but in that case mu should still be present.
-      i = 0;
-      while (i != n && held_locks->locks[i].mu != mu) {
-        i++;
-      }
-      if (i == n) {  // mu missing means releasing unheld lock
-        SynchEvent *mu_events = GetSynchEvent(mu);
-        ABSL_RAW_LOG(FATAL,
-                     "thread releasing lock it does not hold: %p %s; "
-                     ,
-                     static_cast<void *>(mu),
-                     mu_events == nullptr ? "" : mu_events->name);
-      }
-    }
-  } else if (held_locks->locks[i].count == 1) {
-    held_locks->n = n - 1;
-    held_locks->locks[i] = held_locks->locks[n - 1];
-    held_locks->locks[n - 1].id = InvalidGraphId();
-    held_locks->locks[n - 1].mu =
-        nullptr;  // clear mu to please the leak detector.
-  } else {
-    assert(held_locks->locks[i].count > 0);
-    held_locks->locks[i].count--;
-  }
-}
-
-// Call LockEnter() if in debug mode and deadlock detection is enabled.
-static inline void DebugOnlyLockEnter(Mutex *mu) {
-  if (kDebugMode) {
-    if (synch_deadlock_detection.load(std::memory_order_acquire) !=
-        OnDeadlockCycle::kIgnore) {
-      LockEnter(mu, GetGraphId(mu), Synch_GetAllLocks());
-    }
-  }
-}
-
-// Call LockEnter() if in debug mode and deadlock detection is enabled.
-static inline void DebugOnlyLockEnter(Mutex *mu, GraphId id) {
-  if (kDebugMode) {
-    if (synch_deadlock_detection.load(std::memory_order_acquire) !=
-        OnDeadlockCycle::kIgnore) {
-      LockEnter(mu, id, Synch_GetAllLocks());
-    }
-  }
-}
-
-// Call LockLeave() if in debug mode and deadlock detection is enabled.
-static inline void DebugOnlyLockLeave(Mutex *mu) {
-  if (kDebugMode) {
-    if (synch_deadlock_detection.load(std::memory_order_acquire) !=
-        OnDeadlockCycle::kIgnore) {
-      LockLeave(mu, GetGraphId(mu), Synch_GetAllLocks());
-    }
-  }
-}
-
-static char *StackString(void **pcs, int n, char *buf, int maxlen,
-                         bool symbolize) {
-  static const int kSymLen = 200;
-  char sym[kSymLen];
-  int len = 0;
-  for (int i = 0; i != n; i++) {
-    if (symbolize) {
-      if (!symbolizer(pcs[i], sym, kSymLen)) {
-        sym[0] = '\0';
-      }
-      snprintf(buf + len, maxlen - len, "%s\t@ %p %s\n",
-               (i == 0 ? "\n" : ""),
-               pcs[i], sym);
-    } else {
-      snprintf(buf + len, maxlen - len, " %p", pcs[i]);
-    }
-    len += strlen(&buf[len]);
-  }
-  return buf;
-}
-
-static char *CurrentStackString(char *buf, int maxlen, bool symbolize) {
-  void *pcs[40];
-  return StackString(pcs, absl::GetStackTrace(pcs, ABSL_ARRAYSIZE(pcs), 2), buf,
-                     maxlen, symbolize);
-}
-
-namespace {
-enum { kMaxDeadlockPathLen = 10 };  // maximum length of a deadlock cycle;
-                                    // a path this long would be remarkable
-// Buffers required to report a deadlock.
-// We do not allocate them on stack to avoid large stack frame.
-struct DeadlockReportBuffers {
-  char buf[6100];
-  GraphId path[kMaxDeadlockPathLen];
-};
-
-struct ScopedDeadlockReportBuffers {
-  ScopedDeadlockReportBuffers() {
-    b = reinterpret_cast<DeadlockReportBuffers *>(
-        base_internal::LowLevelAlloc::Alloc(sizeof(*b)));
-  }
-  ~ScopedDeadlockReportBuffers() { base_internal::LowLevelAlloc::Free(b); }
-  DeadlockReportBuffers *b;
-};
-
-// Helper to pass to GraphCycles::UpdateStackTrace.
-int GetStack(void** stack, int max_depth) {
-  return absl::GetStackTrace(stack, max_depth, 3);
-}
-}  // anonymous namespace
-
-// Called in debug mode when a thread is about to acquire a lock in a way that
-// may block.
-static GraphId DeadlockCheck(Mutex *mu) {
-  if (synch_deadlock_detection.load(std::memory_order_acquire) ==
-      OnDeadlockCycle::kIgnore) {
-    return InvalidGraphId();
-  }
-
-  SynchLocksHeld *all_locks = Synch_GetAllLocks();
-
-  absl::base_internal::SpinLockHolder lock(&deadlock_graph_mu);
-  const GraphId mu_id = GetGraphIdLocked(mu);
-
-  if (all_locks->n == 0) {
-    // There are no other locks held. Return now so that we don't need to
-    // call GetSynchEvent(). This way we do not record the stack trace
-    // for this Mutex. It's ok, since if this Mutex is involved in a deadlock,
-    // it can't always be the first lock acquired by a thread.
-    return mu_id;
-  }
-
-  // We prefer to keep stack traces that show a thread holding and acquiring
-  // as many locks as possible.  This increases the chances that a given edge
-  // in the acquires-before graph will be represented in the stack traces
-  // recorded for the locks.
-  deadlock_graph->UpdateStackTrace(mu_id, all_locks->n + 1, GetStack);
-
-  // For each other mutex already held by this thread:
-  for (int i = 0; i != all_locks->n; i++) {
-    const GraphId other_node_id = all_locks->locks[i].id;
-    const Mutex *other =
-        static_cast<const Mutex *>(deadlock_graph->Ptr(other_node_id));
-    if (other == nullptr) {
-      // Ignore stale lock
-      continue;
-    }
-
-    // Add the acquired-before edge to the graph.
-    if (!deadlock_graph->InsertEdge(other_node_id, mu_id)) {
-      ScopedDeadlockReportBuffers scoped_buffers;
-      DeadlockReportBuffers *b = scoped_buffers.b;
-      static int number_of_reported_deadlocks = 0;
-      number_of_reported_deadlocks++;
-      // Symbolize only 2 first deadlock report to avoid huge slowdowns.
-      bool symbolize = number_of_reported_deadlocks <= 2;
-      ABSL_RAW_LOG(ERROR, "Potential Mutex deadlock: %s",
-                   CurrentStackString(b->buf, sizeof (b->buf), symbolize));
-      int len = 0;
-      for (int j = 0; j != all_locks->n; j++) {
-        void* pr = deadlock_graph->Ptr(all_locks->locks[j].id);
-        if (pr != nullptr) {
-          snprintf(b->buf + len, sizeof (b->buf) - len, " %p", pr);
-          len += static_cast<int>(strlen(&b->buf[len]));
-        }
-      }
-      ABSL_RAW_LOG(ERROR, "Acquiring %p    Mutexes held: %s",
-                   static_cast<void *>(mu), b->buf);
-      ABSL_RAW_LOG(ERROR, "Cycle: ");
-      int path_len = deadlock_graph->FindPath(
-          mu_id, other_node_id, ABSL_ARRAYSIZE(b->path), b->path);
-      for (int j = 0; j != path_len; j++) {
-        GraphId id = b->path[j];
-        Mutex *path_mu = static_cast<Mutex *>(deadlock_graph->Ptr(id));
-        if (path_mu == nullptr) continue;
-        void** stack;
-        int depth = deadlock_graph->GetStackTrace(id, &stack);
-        snprintf(b->buf, sizeof(b->buf),
-                 "mutex@%p stack: ", static_cast<void *>(path_mu));
-        StackString(stack, depth, b->buf + strlen(b->buf),
-                    static_cast<int>(sizeof(b->buf) - strlen(b->buf)),
-                    symbolize);
-        ABSL_RAW_LOG(ERROR, "%s", b->buf);
-      }
-      if (synch_deadlock_detection.load(std::memory_order_acquire) ==
-          OnDeadlockCycle::kAbort) {
-        deadlock_graph_mu.Unlock();  // avoid deadlock in fatal sighandler
-        ABSL_RAW_LOG(FATAL, "dying due to potential deadlock");
-        return mu_id;
-      }
-      break;   // report at most one potential deadlock per acquisition
-    }
-  }
-
-  return mu_id;
-}
-
-// Invoke DeadlockCheck() iff we're in debug mode and
-// deadlock checking has been enabled.
-static inline GraphId DebugOnlyDeadlockCheck(Mutex *mu) {
-  if (kDebugMode && synch_deadlock_detection.load(std::memory_order_acquire) !=
-                        OnDeadlockCycle::kIgnore) {
-    return DeadlockCheck(mu);
-  } else {
-    return InvalidGraphId();
-  }
-}
-
-void Mutex::ForgetDeadlockInfo() {
-  if (kDebugMode && synch_deadlock_detection.load(std::memory_order_acquire) !=
-                        OnDeadlockCycle::kIgnore) {
-    deadlock_graph_mu.Lock();
-    if (deadlock_graph != nullptr) {
-      deadlock_graph->RemoveNode(this);
-    }
-    deadlock_graph_mu.Unlock();
-  }
-}
-
-void Mutex::AssertNotHeld() const {
-  // We have the data to allow this check only if in debug mode and deadlock
-  // detection is enabled.
-  if (kDebugMode &&
-      (mu_.load(std::memory_order_relaxed) & (kMuWriter | kMuReader)) != 0 &&
-      synch_deadlock_detection.load(std::memory_order_acquire) !=
-          OnDeadlockCycle::kIgnore) {
-    GraphId id = GetGraphId(const_cast<Mutex *>(this));
-    SynchLocksHeld *locks = Synch_GetAllLocks();
-    for (int i = 0; i != locks->n; i++) {
-      if (locks->locks[i].id == id) {
-        SynchEvent *mu_events = GetSynchEvent(this);
-        ABSL_RAW_LOG(FATAL, "thread should not hold mutex %p %s",
-                     static_cast<const void *>(this),
-                     (mu_events == nullptr ? "" : mu_events->name));
-      }
-    }
-  }
-}
-
-// Attempt to acquire *mu, and return whether successful.  The implementation
-// may spin for a short while if the lock cannot be acquired immediately.
-static bool TryAcquireWithSpinning(std::atomic<intptr_t>* mu) {
-  int c = GetMutexGlobals().spinloop_iterations;
-  do {  // do/while somewhat faster on AMD
-    intptr_t v = mu->load(std::memory_order_relaxed);
-    if ((v & (kMuReader|kMuEvent)) != 0) {
-      return false;  // a reader or tracing -> give up
-    } else if (((v & kMuWriter) == 0) &&  // no holder -> try to acquire
-               mu->compare_exchange_strong(v, kMuWriter | v,
-                                           std::memory_order_acquire,
-                                           std::memory_order_relaxed)) {
-      return true;
-    }
-  } while (--c > 0);
-  return false;
-}
-
-ABSL_XRAY_LOG_ARGS(1) void Mutex::Lock() {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  // try fast acquire, then spin loop
-  if ((v & (kMuWriter | kMuReader | kMuEvent)) != 0 ||
-      !mu_.compare_exchange_strong(v, kMuWriter | v,
-                                   std::memory_order_acquire,
-                                   std::memory_order_relaxed)) {
-    // try spin acquire, then slow loop
-    if (!TryAcquireWithSpinning(&this->mu_)) {
-      this->LockSlow(kExclusive, nullptr, 0);
-    }
-  }
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
-}
-
-ABSL_XRAY_LOG_ARGS(1) void Mutex::ReaderLock() {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  // try fast acquire, then slow loop
-  if ((v & (kMuWriter | kMuWait | kMuEvent)) != 0 ||
-      !mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
-                                   std::memory_order_acquire,
-                                   std::memory_order_relaxed)) {
-    this->LockSlow(kShared, nullptr, 0);
-  }
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
-}
-
-void Mutex::LockWhen(const Condition &cond) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  this->LockSlow(kExclusive, &cond, 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
-}
-
-bool Mutex::LockWhenWithTimeout(const Condition &cond, absl::Duration timeout) {
-  return LockWhenWithDeadline(cond, DeadlineFromTimeout(timeout));
-}
-
-bool Mutex::LockWhenWithDeadline(const Condition &cond, absl::Time deadline) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  bool res = LockSlowWithDeadline(kExclusive, &cond,
-                                  KernelTimeout(deadline), 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
-  return res;
-}
-
-void Mutex::ReaderLockWhen(const Condition &cond) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  this->LockSlow(kShared, &cond, 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
-}
-
-bool Mutex::ReaderLockWhenWithTimeout(const Condition &cond,
-                                      absl::Duration timeout) {
-  return ReaderLockWhenWithDeadline(cond, DeadlineFromTimeout(timeout));
-}
-
-bool Mutex::ReaderLockWhenWithDeadline(const Condition &cond,
-                                       absl::Time deadline) {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
-  GraphId id = DebugOnlyDeadlockCheck(this);
-  bool res = LockSlowWithDeadline(kShared, &cond, KernelTimeout(deadline), 0);
-  DebugOnlyLockEnter(this, id);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
-  return res;
-}
-
-void Mutex::Await(const Condition &cond) {
-  if (cond.Eval()) {    // condition already true; nothing to do
-    if (kDebugMode) {
-      this->AssertReaderHeld();
-    }
-  } else {              // normal case
-    ABSL_RAW_CHECK(this->AwaitCommon(cond, KernelTimeout::Never()),
-                   "condition untrue on return from Await");
-  }
-}
-
-bool Mutex::AwaitWithTimeout(const Condition &cond, absl::Duration timeout) {
-  return AwaitWithDeadline(cond, DeadlineFromTimeout(timeout));
-}
-
-bool Mutex::AwaitWithDeadline(const Condition &cond, absl::Time deadline) {
-  if (cond.Eval()) {      // condition already true; nothing to do
-    if (kDebugMode) {
-      this->AssertReaderHeld();
-    }
-    return true;
-  }
-
-  KernelTimeout t{deadline};
-  bool res = this->AwaitCommon(cond, t);
-  ABSL_RAW_CHECK(res || t.has_timeout(),
-                 "condition untrue on return from Await");
-  return res;
-}
-
-bool Mutex::AwaitCommon(const Condition &cond, KernelTimeout t) {
-  this->AssertReaderHeld();
-  MuHow how =
-      (mu_.load(std::memory_order_relaxed) & kMuWriter) ? kExclusive : kShared;
-  ABSL_TSAN_MUTEX_PRE_UNLOCK(this, TsanFlags(how));
-  SynchWaitParams waitp(
-      how, &cond, t, nullptr /*no cvmu*/, Synch_GetPerThreadAnnotated(this),
-      nullptr /*no cv_word*/);
-  int flags = kMuHasBlocked;
-  if (!Condition::GuaranteedEqual(&cond, nullptr)) {
-    flags |= kMuIsCond;
-  }
-  this->UnlockSlow(&waitp);
-  this->Block(waitp.thread);
-  ABSL_TSAN_MUTEX_POST_UNLOCK(this, TsanFlags(how));
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, TsanFlags(how));
-  this->LockSlowLoop(&waitp, flags);
-  bool res = waitp.cond != nullptr ||  // => cond known true from LockSlowLoop
-             EvalConditionAnnotated(&cond, this, true, false, how == kShared);
-  ABSL_TSAN_MUTEX_POST_LOCK(this, TsanFlags(how), 0);
-  return res;
-}
-
-ABSL_XRAY_LOG_ARGS(1) bool Mutex::TryLock() {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock);
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  if ((v & (kMuWriter | kMuReader | kMuEvent)) == 0 &&  // try fast acquire
-      mu_.compare_exchange_strong(v, kMuWriter | v,
-                                  std::memory_order_acquire,
-                                  std::memory_order_relaxed)) {
-    DebugOnlyLockEnter(this);
-    ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
-    return true;
-  }
-  if ((v & kMuEvent) != 0) {              // we're recording events
-    if ((v & kExclusive->slow_need_zero) == 0 &&  // try fast acquire
-        mu_.compare_exchange_strong(
-            v, (kExclusive->fast_or | v) + kExclusive->fast_add,
-            std::memory_order_acquire, std::memory_order_relaxed)) {
-      DebugOnlyLockEnter(this);
-      PostSynchEvent(this, SYNCH_EV_TRYLOCK_SUCCESS);
-      ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
-      return true;
-    } else {
-      PostSynchEvent(this, SYNCH_EV_TRYLOCK_FAILED);
-    }
-  }
-  ABSL_TSAN_MUTEX_POST_LOCK(
-      this, __tsan_mutex_try_lock | __tsan_mutex_try_lock_failed, 0);
-  return false;
-}
-
-ABSL_XRAY_LOG_ARGS(1) bool Mutex::ReaderTryLock() {
-  ABSL_TSAN_MUTEX_PRE_LOCK(this,
-                           __tsan_mutex_read_lock | __tsan_mutex_try_lock);
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  // The while-loops (here and below) iterate only if the mutex word keeps
-  // changing (typically because the reader count changes) under the CAS.  We
-  // limit the number of attempts to avoid having to think about livelock.
-  int loop_limit = 5;
-  while ((v & (kMuWriter|kMuWait|kMuEvent)) == 0 && loop_limit != 0) {
-    if (mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
-                                    std::memory_order_acquire,
-                                    std::memory_order_relaxed)) {
-      DebugOnlyLockEnter(this);
-      ABSL_TSAN_MUTEX_POST_LOCK(
-          this, __tsan_mutex_read_lock | __tsan_mutex_try_lock, 0);
-      return true;
-    }
-    loop_limit--;
-    v = mu_.load(std::memory_order_relaxed);
-  }
-  if ((v & kMuEvent) != 0) {   // we're recording events
-    loop_limit = 5;
-    while ((v & kShared->slow_need_zero) == 0 && loop_limit != 0) {
-      if (mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
-                                      std::memory_order_acquire,
-                                      std::memory_order_relaxed)) {
-        DebugOnlyLockEnter(this);
-        PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_SUCCESS);
-        ABSL_TSAN_MUTEX_POST_LOCK(
-            this, __tsan_mutex_read_lock | __tsan_mutex_try_lock, 0);
-        return true;
-      }
-      loop_limit--;
-      v = mu_.load(std::memory_order_relaxed);
-    }
-    if ((v & kMuEvent) != 0) {
-      PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_FAILED);
-    }
-  }
-  ABSL_TSAN_MUTEX_POST_LOCK(this,
-                            __tsan_mutex_read_lock | __tsan_mutex_try_lock |
-                                __tsan_mutex_try_lock_failed,
-                            0);
-  return false;
-}
-
-ABSL_XRAY_LOG_ARGS(1) void Mutex::Unlock() {
-  ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0);
-  DebugOnlyLockLeave(this);
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-
-  if (kDebugMode && ((v & (kMuWriter | kMuReader)) != kMuWriter)) {
-    ABSL_RAW_LOG(FATAL, "Mutex unlocked when destroyed or not locked: v=0x%x",
-                 static_cast<unsigned>(v));
-  }
-
-  // should_try_cas is whether we'll try a compare-and-swap immediately.
-  // NOTE: optimized out when kDebugMode is false.
-  bool should_try_cas = ((v & (kMuEvent | kMuWriter)) == kMuWriter &&
-                          (v & (kMuWait | kMuDesig)) != kMuWait);
-  // But, we can use an alternate computation of it, that compilers
-  // currently don't find on their own.  When that changes, this function
-  // can be simplified.
-  intptr_t x = (v ^ (kMuWriter | kMuWait)) & (kMuWriter | kMuEvent);
-  intptr_t y = (v ^ (kMuWriter | kMuWait)) & (kMuWait | kMuDesig);
-  // Claim: "x == 0 && y > 0" is equal to should_try_cas.
-  // Also, because kMuWriter and kMuEvent exceed kMuDesig and kMuWait,
-  // all possible non-zero values for x exceed all possible values for y.
-  // Therefore, (x == 0 && y > 0) == (x < y).
-  if (kDebugMode && should_try_cas != (x < y)) {
-    // We would usually use PRIdPTR here, but is not correctly implemented
-    // within the android toolchain.
-    ABSL_RAW_LOG(FATAL, "internal logic error %llx %llx %llx\n",
-                 static_cast<long long>(v), static_cast<long long>(x),
-                 static_cast<long long>(y));
-  }
-  if (x < y &&
-      mu_.compare_exchange_strong(v, v & ~(kMuWrWait | kMuWriter),
-                                  std::memory_order_release,
-                                  std::memory_order_relaxed)) {
-    // fast writer release (writer with no waiters or with designated waker)
-  } else {
-    this->UnlockSlow(nullptr /*no waitp*/);  // take slow path
-  }
-  ABSL_TSAN_MUTEX_POST_UNLOCK(this, 0);
-}
-
-// Requires v to represent a reader-locked state.
-static bool ExactlyOneReader(intptr_t v) {
-  assert((v & (kMuWriter|kMuReader)) == kMuReader);
-  assert((v & kMuHigh) != 0);
-  // The more straightforward "(v & kMuHigh) == kMuOne" also works, but
-  // on some architectures the following generates slightly smaller code.
-  // It may be faster too.
-  constexpr intptr_t kMuMultipleWaitersMask = kMuHigh ^ kMuOne;
-  return (v & kMuMultipleWaitersMask) == 0;
-}
-
-ABSL_XRAY_LOG_ARGS(1) void Mutex::ReaderUnlock() {
-  ABSL_TSAN_MUTEX_PRE_UNLOCK(this, __tsan_mutex_read_lock);
-  DebugOnlyLockLeave(this);
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  assert((v & (kMuWriter|kMuReader)) == kMuReader);
-  if ((v & (kMuReader|kMuWait|kMuEvent)) == kMuReader) {
-    // fast reader release (reader with no waiters)
-    intptr_t clear = ExactlyOneReader(v) ? kMuReader|kMuOne : kMuOne;
-    if (mu_.compare_exchange_strong(v, v - clear,
-                                    std::memory_order_release,
-                                    std::memory_order_relaxed)) {
-      ABSL_TSAN_MUTEX_POST_UNLOCK(this, __tsan_mutex_read_lock);
-      return;
-    }
-  }
-  this->UnlockSlow(nullptr /*no waitp*/);  // take slow path
-  ABSL_TSAN_MUTEX_POST_UNLOCK(this, __tsan_mutex_read_lock);
-}
-
-// The zap_desig_waker bitmask is used to clear the designated waker flag in
-// the mutex if this thread has blocked, and therefore may be the designated
-// waker.
-static const intptr_t zap_desig_waker[] = {
-    ~static_cast<intptr_t>(0),  // not blocked
-    ~static_cast<intptr_t>(
-        kMuDesig)  // blocked; turn off the designated waker bit
-};
-
-// The ignore_waiting_writers bitmask is used to ignore the existence
-// of waiting writers if a reader that has already blocked once
-// wakes up.
-static const intptr_t ignore_waiting_writers[] = {
-    ~static_cast<intptr_t>(0),  // not blocked
-    ~static_cast<intptr_t>(
-        kMuWrWait)  // blocked; pretend there are no waiting writers
-};
-
-// Internal version of LockWhen().  See LockSlowWithDeadline()
-ABSL_ATTRIBUTE_NOINLINE void Mutex::LockSlow(MuHow how, const Condition *cond,
-                                             int flags) {
-  ABSL_RAW_CHECK(
-      this->LockSlowWithDeadline(how, cond, KernelTimeout::Never(), flags),
-      "condition untrue on return from LockSlow");
-}
-
-// Compute cond->Eval() and tell race detectors that we do it under mutex mu.
-static inline bool EvalConditionAnnotated(const Condition *cond, Mutex *mu,
-                                          bool locking, bool trylock,
-                                          bool read_lock) {
-  // Delicate annotation dance.
-  // We are currently inside of read/write lock/unlock operation.
-  // All memory accesses are ignored inside of mutex operations + for unlock
-  // operation tsan considers that we've already released the mutex.
-  bool res = false;
-#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE
-  const int flags = read_lock ? __tsan_mutex_read_lock : 0;
-  const int tryflags = flags | (trylock ? __tsan_mutex_try_lock : 0);
-#endif
-  if (locking) {
-    // For lock we pretend that we have finished the operation,
-    // evaluate the predicate, then unlock the mutex and start locking it again
-    // to match the annotation at the end of outer lock operation.
-    // Note: we can't simply do POST_LOCK, Eval, PRE_LOCK, because then tsan
-    // will think the lock acquisition is recursive which will trigger
-    // deadlock detector.
-    ABSL_TSAN_MUTEX_POST_LOCK(mu, tryflags, 0);
-    res = cond->Eval();
-    // There is no "try" version of Unlock, so use flags instead of tryflags.
-    ABSL_TSAN_MUTEX_PRE_UNLOCK(mu, flags);
-    ABSL_TSAN_MUTEX_POST_UNLOCK(mu, flags);
-    ABSL_TSAN_MUTEX_PRE_LOCK(mu, tryflags);
-  } else {
-    // Similarly, for unlock we pretend that we have unlocked the mutex,
-    // lock the mutex, evaluate the predicate, and start unlocking it again
-    // to match the annotation at the end of outer unlock operation.
-    ABSL_TSAN_MUTEX_POST_UNLOCK(mu, flags);
-    ABSL_TSAN_MUTEX_PRE_LOCK(mu, flags);
-    ABSL_TSAN_MUTEX_POST_LOCK(mu, flags, 0);
-    res = cond->Eval();
-    ABSL_TSAN_MUTEX_PRE_UNLOCK(mu, flags);
-  }
-  // Prevent unused param warnings in non-TSAN builds.
-  static_cast<void>(mu);
-  static_cast<void>(trylock);
-  static_cast<void>(read_lock);
-  return res;
-}
-
-// Compute cond->Eval() hiding it from race detectors.
-// We are hiding it because inside of UnlockSlow we can evaluate a predicate
-// that was just added by a concurrent Lock operation; Lock adds the predicate
-// to the internal Mutex list without actually acquiring the Mutex
-// (it only acquires the internal spinlock, which is rightfully invisible for
-// tsan). As the result there is no tsan-visible synchronization between the
-// addition and this thread. So if we would enable race detection here,
-// it would race with the predicate initialization.
-static inline bool EvalConditionIgnored(Mutex *mu, const Condition *cond) {
-  // Memory accesses are already ignored inside of lock/unlock operations,
-  // but synchronization operations are also ignored. When we evaluate the
-  // predicate we must ignore only memory accesses but not synchronization,
-  // because missed synchronization can lead to false reports later.
-  // So we "divert" (which un-ignores both memory accesses and synchronization)
-  // and then separately turn on ignores of memory accesses.
-  ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
-  ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN();
-  bool res = cond->Eval();
-  ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END();
-  ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
-  static_cast<void>(mu);  // Prevent unused param warning in non-TSAN builds.
-  return res;
-}
-
-// Internal equivalent of *LockWhenWithDeadline(), where
-//   "t" represents the absolute timeout; !t.has_timeout() means "forever".
-//   "how" is "kShared" (for ReaderLockWhen) or "kExclusive" (for LockWhen)
-// In flags, bits are ored together:
-// - kMuHasBlocked indicates that the client has already blocked on the call so
-//   the designated waker bit must be cleared and waiting writers should not
-//   obstruct this call
-// - kMuIsCond indicates that this is a conditional acquire (condition variable,
-//   Await,  LockWhen) so contention profiling should be suppressed.
-bool Mutex::LockSlowWithDeadline(MuHow how, const Condition *cond,
-                                 KernelTimeout t, int flags) {
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  bool unlock = false;
-  if ((v & how->fast_need_zero) == 0 &&  // try fast acquire
-      mu_.compare_exchange_strong(
-          v, (how->fast_or | (v & zap_desig_waker[flags & kMuHasBlocked])) +
-                 how->fast_add,
-          std::memory_order_acquire, std::memory_order_relaxed)) {
-    if (cond == nullptr ||
-        EvalConditionAnnotated(cond, this, true, false, how == kShared)) {
-      return true;
-    }
-    unlock = true;
-  }
-  SynchWaitParams waitp(
-      how, cond, t, nullptr /*no cvmu*/, Synch_GetPerThreadAnnotated(this),
-      nullptr /*no cv_word*/);
-  if (!Condition::GuaranteedEqual(cond, nullptr)) {
-    flags |= kMuIsCond;
-  }
-  if (unlock) {
-    this->UnlockSlow(&waitp);
-    this->Block(waitp.thread);
-    flags |= kMuHasBlocked;
-  }
-  this->LockSlowLoop(&waitp, flags);
-  return waitp.cond != nullptr ||  // => cond known true from LockSlowLoop
-         cond == nullptr ||
-         EvalConditionAnnotated(cond, this, true, false, how == kShared);
-}
-
-// RAW_CHECK_FMT() takes a condition, a printf-style format string, and
-// the printf-style argument list.   The format string must be a literal.
-// Arguments after the first are not evaluated unless the condition is true.
-#define RAW_CHECK_FMT(cond, ...)                                   \
-  do {                                                             \
-    if (ABSL_PREDICT_FALSE(!(cond))) {                             \
-      ABSL_RAW_LOG(FATAL, "Check " #cond " failed: " __VA_ARGS__); \
-    }                                                              \
-  } while (0)
-
-static void CheckForMutexCorruption(intptr_t v, const char* label) {
-  // Test for either of two situations that should not occur in v:
-  //   kMuWriter and kMuReader
-  //   kMuWrWait and !kMuWait
-  const uintptr_t w = v ^ kMuWait;
-  // By flipping that bit, we can now test for:
-  //   kMuWriter and kMuReader in w
-  //   kMuWrWait and kMuWait in w
-  // We've chosen these two pairs of values to be so that they will overlap,
-  // respectively, when the word is left shifted by three.  This allows us to
-  // save a branch in the common (correct) case of them not being coincident.
-  static_assert(kMuReader << 3 == kMuWriter, "must match");
-  static_assert(kMuWait << 3 == kMuWrWait, "must match");
-  if (ABSL_PREDICT_TRUE((w & (w << 3) & (kMuWriter | kMuWrWait)) == 0)) return;
-  RAW_CHECK_FMT((v & (kMuWriter | kMuReader)) != (kMuWriter | kMuReader),
-                "%s: Mutex corrupt: both reader and writer lock held: %p",
-                label, reinterpret_cast<void *>(v));
-  RAW_CHECK_FMT((v & (kMuWait | kMuWrWait)) != kMuWrWait,
-                "%s: Mutex corrupt: waiting writer with no waiters: %p",
-                label, reinterpret_cast<void *>(v));
-  assert(false);
-}
-
-void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) {
-  SchedulingGuard::ScopedDisable disable_rescheduling;
-  int c = 0;
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  if ((v & kMuEvent) != 0) {
-    PostSynchEvent(this,
-         waitp->how == kExclusive?  SYNCH_EV_LOCK: SYNCH_EV_READERLOCK);
-  }
-  ABSL_RAW_CHECK(
-      waitp->thread->waitp == nullptr || waitp->thread->suppress_fatal_errors,
-      "detected illegal recursion into Mutex code");
-  for (;;) {
-    v = mu_.load(std::memory_order_relaxed);
-    CheckForMutexCorruption(v, "Lock");
-    if ((v & waitp->how->slow_need_zero) == 0) {
-      if (mu_.compare_exchange_strong(
-              v, (waitp->how->fast_or |
-                  (v & zap_desig_waker[flags & kMuHasBlocked])) +
-                     waitp->how->fast_add,
-              std::memory_order_acquire, std::memory_order_relaxed)) {
-        if (waitp->cond == nullptr ||
-            EvalConditionAnnotated(waitp->cond, this, true, false,
-                                   waitp->how == kShared)) {
-          break;  // we timed out, or condition true, so return
-        }
-        this->UnlockSlow(waitp);  // got lock but condition false
-        this->Block(waitp->thread);
-        flags |= kMuHasBlocked;
-        c = 0;
-      }
-    } else {                      // need to access waiter list
-      bool dowait = false;
-      if ((v & (kMuSpin|kMuWait)) == 0) {   // no waiters
-        // This thread tries to become the one and only waiter.
-        PerThreadSynch *new_h = Enqueue(nullptr, waitp, v, flags);
-        intptr_t nv = (v & zap_desig_waker[flags & kMuHasBlocked] & kMuLow) |
-                      kMuWait;
-        ABSL_RAW_CHECK(new_h != nullptr, "Enqueue to empty list failed");
-        if (waitp->how == kExclusive && (v & kMuReader) != 0) {
-          nv |= kMuWrWait;
-        }
-        if (mu_.compare_exchange_strong(
-                v, reinterpret_cast<intptr_t>(new_h) | nv,
-                std::memory_order_release, std::memory_order_relaxed)) {
-          dowait = true;
-        } else {            // attempted Enqueue() failed
-          // zero out the waitp field set by Enqueue()
-          waitp->thread->waitp = nullptr;
-        }
-      } else if ((v & waitp->how->slow_inc_need_zero &
-                  ignore_waiting_writers[flags & kMuHasBlocked]) == 0) {
-        // This is a reader that needs to increment the reader count,
-        // but the count is currently held in the last waiter.
-        if (mu_.compare_exchange_strong(
-                v, (v & zap_desig_waker[flags & kMuHasBlocked]) | kMuSpin |
-                       kMuReader,
-                std::memory_order_acquire, std::memory_order_relaxed)) {
-          PerThreadSynch *h = GetPerThreadSynch(v);
-          h->readers += kMuOne;       // inc reader count in waiter
-          do {                        // release spinlock
-            v = mu_.load(std::memory_order_relaxed);
-          } while (!mu_.compare_exchange_weak(v, (v & ~kMuSpin) | kMuReader,
-                                              std::memory_order_release,
-                                              std::memory_order_relaxed));
-          if (waitp->cond == nullptr ||
-              EvalConditionAnnotated(waitp->cond, this, true, false,
-                                     waitp->how == kShared)) {
-            break;  // we timed out, or condition true, so return
-          }
-          this->UnlockSlow(waitp);           // got lock but condition false
-          this->Block(waitp->thread);
-          flags |= kMuHasBlocked;
-          c = 0;
-        }
-      } else if ((v & kMuSpin) == 0 &&  // attempt to queue ourselves
-                 mu_.compare_exchange_strong(
-                     v, (v & zap_desig_waker[flags & kMuHasBlocked]) | kMuSpin |
-                            kMuWait,
-                     std::memory_order_acquire, std::memory_order_relaxed)) {
-        PerThreadSynch *h = GetPerThreadSynch(v);
-        PerThreadSynch *new_h = Enqueue(h, waitp, v, flags);
-        intptr_t wr_wait = 0;
-        ABSL_RAW_CHECK(new_h != nullptr, "Enqueue to list failed");
-        if (waitp->how == kExclusive && (v & kMuReader) != 0) {
-          wr_wait = kMuWrWait;      // give priority to a waiting writer
-        }
-        do {                        // release spinlock
-          v = mu_.load(std::memory_order_relaxed);
-        } while (!mu_.compare_exchange_weak(
-            v, (v & (kMuLow & ~kMuSpin)) | kMuWait | wr_wait |
-            reinterpret_cast<intptr_t>(new_h),
-            std::memory_order_release, std::memory_order_relaxed));
-        dowait = true;
-      }
-      if (dowait) {
-        this->Block(waitp->thread);  // wait until removed from list or timeout
-        flags |= kMuHasBlocked;
-        c = 0;
-      }
-    }
-    ABSL_RAW_CHECK(
-        waitp->thread->waitp == nullptr || waitp->thread->suppress_fatal_errors,
-        "detected illegal recursion into Mutex code");
-    // delay, then try again
-    c = synchronization_internal::MutexDelay(c, GENTLE);
-  }
-  ABSL_RAW_CHECK(
-      waitp->thread->waitp == nullptr || waitp->thread->suppress_fatal_errors,
-      "detected illegal recursion into Mutex code");
-  if ((v & kMuEvent) != 0) {
-    PostSynchEvent(this,
-                   waitp->how == kExclusive? SYNCH_EV_LOCK_RETURNING :
-                                      SYNCH_EV_READERLOCK_RETURNING);
-  }
-}
-
-// Unlock this mutex, which is held by the current thread.
-// If waitp is non-zero, it must be the wait parameters for the current thread
-// which holds the lock but is not runnable because its condition is false
-// or it is in the process of blocking on a condition variable; it must requeue
-// itself on the mutex/condvar to wait for its condition to become true.
-ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams *waitp) {
-  SchedulingGuard::ScopedDisable disable_rescheduling;
-  intptr_t v = mu_.load(std::memory_order_relaxed);
-  this->AssertReaderHeld();
-  CheckForMutexCorruption(v, "Unlock");
-  if ((v & kMuEvent) != 0) {
-    PostSynchEvent(this,
-                (v & kMuWriter) != 0? SYNCH_EV_UNLOCK: SYNCH_EV_READERUNLOCK);
-  }
-  int c = 0;
-  // the waiter under consideration to wake, or zero
-  PerThreadSynch *w = nullptr;
-  // the predecessor to w or zero
-  PerThreadSynch *pw = nullptr;
-  // head of the list searched previously, or zero
-  PerThreadSynch *old_h = nullptr;
-  // a condition that's known to be false.
-  const Condition *known_false = nullptr;
-  PerThreadSynch *wake_list = kPerThreadSynchNull;   // list of threads to wake
-  intptr_t wr_wait = 0;        // set to kMuWrWait if we wake a reader and a
-                               // later writer could have acquired the lock
-                               // (starvation avoidance)
-  ABSL_RAW_CHECK(waitp == nullptr || waitp->thread->waitp == nullptr ||
-                     waitp->thread->suppress_fatal_errors,
-                 "detected illegal recursion into Mutex code");
-  // This loop finds threads wake_list to wakeup if any, and removes them from
-  // the list of waiters.  In addition, it places waitp.thread on the queue of
-  // waiters if waitp is non-zero.
-  for (;;) {
-    v = mu_.load(std::memory_order_relaxed);
-    if ((v & kMuWriter) != 0 && (v & (kMuWait | kMuDesig)) != kMuWait &&
-        waitp == nullptr) {
-      // fast writer release (writer with no waiters or with designated waker)
-      if (mu_.compare_exchange_strong(v, v & ~(kMuWrWait | kMuWriter),
-                                      std::memory_order_release,
-                                      std::memory_order_relaxed)) {
-        return;
-      }
-    } else if ((v & (kMuReader | kMuWait)) == kMuReader && waitp == nullptr) {
-      // fast reader release (reader with no waiters)
-      intptr_t clear = ExactlyOneReader(v) ? kMuReader | kMuOne : kMuOne;
-      if (mu_.compare_exchange_strong(v, v - clear,
-                                      std::memory_order_release,
-                                      std::memory_order_relaxed)) {
-        return;
-      }
-    } else if ((v & kMuSpin) == 0 &&  // attempt to get spinlock
-               mu_.compare_exchange_strong(v, v | kMuSpin,
-                                           std::memory_order_acquire,
-                                           std::memory_order_relaxed)) {
-      if ((v & kMuWait) == 0) {       // no one to wake
-        intptr_t nv;
-        bool do_enqueue = true;  // always Enqueue() the first time
-        ABSL_RAW_CHECK(waitp != nullptr,
-                       "UnlockSlow is confused");  // about to sleep
-        do {    // must loop to release spinlock as reader count may change
-          v = mu_.load(std::memory_order_relaxed);
-          // decrement reader count if there are readers
-          intptr_t new_readers = (v >= kMuOne)?  v - kMuOne : v;
-          PerThreadSynch *new_h = nullptr;
-          if (do_enqueue) {
-            // If we are enqueuing on a CondVar (waitp->cv_word != nullptr) then
-            // we must not retry here.  The initial attempt will always have
-            // succeeded, further attempts would enqueue us against *this due to
-            // Fer() handling.
-            do_enqueue = (waitp->cv_word == nullptr);
-            new_h = Enqueue(nullptr, waitp, new_readers, kMuIsCond);
-          }
-          intptr_t clear = kMuWrWait | kMuWriter;  // by default clear write bit
-          if ((v & kMuWriter) == 0 && ExactlyOneReader(v)) {  // last reader
-            clear = kMuWrWait | kMuReader;                    // clear read bit
-          }
-          nv = (v & kMuLow & ~clear & ~kMuSpin);
-          if (new_h != nullptr) {
-            nv |= kMuWait | reinterpret_cast<intptr_t>(new_h);
-          } else {  // new_h could be nullptr if we queued ourselves on a
-                    // CondVar
-            // In that case, we must place the reader count back in the mutex
-            // word, as Enqueue() did not store it in the new waiter.
-            nv |= new_readers & kMuHigh;
-          }
-          // release spinlock & our lock; retry if reader-count changed
-          // (writer count cannot change since we hold lock)
-        } while (!mu_.compare_exchange_weak(v, nv,
-                                            std::memory_order_release,
-                                            std::memory_order_relaxed));
-        break;
-      }
-
-      // There are waiters.
-      // Set h to the head of the circular waiter list.
-      PerThreadSynch *h = GetPerThreadSynch(v);
-      if ((v & kMuReader) != 0 && (h->readers & kMuHigh) > kMuOne) {
-        // a reader but not the last
-        h->readers -= kMuOne;  // release our lock
-        intptr_t nv = v;       // normally just release spinlock
-        if (waitp != nullptr) {  // but waitp!=nullptr => must queue ourselves
-          PerThreadSynch *new_h = Enqueue(h, waitp, v, kMuIsCond);
-          ABSL_RAW_CHECK(new_h != nullptr,
-                         "waiters disappeared during Enqueue()!");
-          nv &= kMuLow;
-          nv |= kMuWait | reinterpret_cast<intptr_t>(new_h);
-        }
-        mu_.store(nv, std::memory_order_release);  // release spinlock
-        // can release with a store because there were waiters
-        break;
-      }
-
-      // Either we didn't search before, or we marked the queue
-      // as "maybe_unlocking" and no one else should have changed it.
-      ABSL_RAW_CHECK(old_h == nullptr || h->maybe_unlocking,
-                     "Mutex queue changed beneath us");
-
-      // The lock is becoming free, and there's a waiter
-      if (old_h != nullptr &&
-          !old_h->may_skip) {                  // we used old_h as a terminator
-        old_h->may_skip = true;                // allow old_h to skip once more
-        ABSL_RAW_CHECK(old_h->skip == nullptr, "illegal skip from head");
-        if (h != old_h && MuSameCondition(old_h, old_h->next)) {
-          old_h->skip = old_h->next;  // old_h not head & can skip to successor
-        }
-      }
-      if (h->next->waitp->how == kExclusive &&
-          Condition::GuaranteedEqual(h->next->waitp->cond, nullptr)) {
-        // easy case: writer with no condition; no need to search
-        pw = h;                       // wake w, the successor of h (=pw)
-        w = h->next;
-        w->wake = true;
-        // We are waking up a writer.  This writer may be racing against
-        // an already awake reader for the lock.  We want the
-        // writer to usually win this race,
-        // because if it doesn't, we can potentially keep taking a reader
-        // perpetually and writers will starve.  Worse than
-        // that, this can also starve other readers if kMuWrWait gets set
-        // later.
-        wr_wait = kMuWrWait;
-      } else if (w != nullptr && (w->waitp->how == kExclusive || h == old_h)) {
-        // we found a waiter w to wake on a previous iteration and either it's
-        // a writer, or we've searched the entire list so we have all the
-        // readers.
-        if (pw == nullptr) {  // if w's predecessor is unknown, it must be h
-          pw = h;
-        }
-      } else {
-        // At this point we don't know all the waiters to wake, and the first
-        // waiter has a condition or is a reader.  We avoid searching over
-        // waiters we've searched on previous iterations by starting at
-        // old_h if it's set.  If old_h==h, there's no one to wakeup at all.
-        if (old_h == h) {      // we've searched before, and nothing's new
-                               // so there's no one to wake.
-          intptr_t nv = (v & ~(kMuReader|kMuWriter|kMuWrWait));
-          h->readers = 0;
-          h->maybe_unlocking = false;   // finished unlocking
-          if (waitp != nullptr) {       // we must queue ourselves and sleep
-            PerThreadSynch *new_h = Enqueue(h, waitp, v, kMuIsCond);
-            nv &= kMuLow;
-            if (new_h != nullptr) {
-              nv |= kMuWait | reinterpret_cast<intptr_t>(new_h);
-            }  // else new_h could be nullptr if we queued ourselves on a
-               // CondVar
-          }
-          // release spinlock & lock
-          // can release with a store because there were waiters
-          mu_.store(nv, std::memory_order_release);
-          break;
-        }
-
-        // set up to walk the list
-        PerThreadSynch *w_walk;   // current waiter during list walk
-        PerThreadSynch *pw_walk;  // previous waiter during list walk
-        if (old_h != nullptr) {  // we've searched up to old_h before
-          pw_walk = old_h;
-          w_walk = old_h->next;
-        } else {            // no prior search, start at beginning
-          pw_walk =
-              nullptr;  // h->next's predecessor may change; don't record it
-          w_walk = h->next;
-        }
-
-        h->may_skip = false;  // ensure we never skip past h in future searches
-                              // even if other waiters are queued after it.
-        ABSL_RAW_CHECK(h->skip == nullptr, "illegal skip from head");
-
-        h->maybe_unlocking = true;  // we're about to scan the waiter list
-                                    // without the spinlock held.
-                                    // Enqueue must be conservative about
-                                    // priority queuing.
-
-        // We must release the spinlock to evaluate the conditions.
-        mu_.store(v, std::memory_order_release);  // release just spinlock
-        // can release with a store because there were waiters
-
-        // h is the last waiter queued, and w_walk the first unsearched waiter.
-        // Without the spinlock, the locations mu_ and h->next may now change
-        // underneath us, but since we hold the lock itself, the only legal
-        // change is to add waiters between h and w_walk.  Therefore, it's safe
-        // to walk the path from w_walk to h inclusive. (TryRemove() can remove
-        // a waiter anywhere, but it acquires both the spinlock and the Mutex)
-
-        old_h = h;        // remember we searched to here
-
-        // Walk the path upto and including h looking for waiters we can wake.
-        while (pw_walk != h) {
-          w_walk->wake = false;
-          if (w_walk->waitp->cond ==
-                  nullptr ||  // no condition => vacuously true OR
-              (w_walk->waitp->cond != known_false &&
-               // this thread's condition is not known false, AND
-               //  is in fact true
-               EvalConditionIgnored(this, w_walk->waitp->cond))) {
-            if (w == nullptr) {
-              w_walk->wake = true;    // can wake this waiter
-              w = w_walk;
-              pw = pw_walk;
-              if (w_walk->waitp->how == kExclusive) {
-                wr_wait = kMuWrWait;
-                break;                // bail if waking this writer
-              }
-            } else if (w_walk->waitp->how == kShared) {  // wake if a reader
-              w_walk->wake = true;
-            } else {   // writer with true condition
-              wr_wait = kMuWrWait;
-            }
-          } else {                  // can't wake; condition false
-            known_false = w_walk->waitp->cond;  // remember last false condition
-          }
-          if (w_walk->wake) {   // we're waking reader w_walk
-            pw_walk = w_walk;   // don't skip similar waiters
-          } else {              // not waking; skip as much as possible
-            pw_walk = Skip(w_walk);
-          }
-          // If pw_walk == h, then load of pw_walk->next can race with
-          // concurrent write in Enqueue(). However, at the same time
-          // we do not need to do the load, because we will bail out
-          // from the loop anyway.
-          if (pw_walk != h) {
-            w_walk = pw_walk->next;
-          }
-        }
-
-        continue;  // restart for(;;)-loop to wakeup w or to find more waiters
-      }
-      ABSL_RAW_CHECK(pw->next == w, "pw not w's predecessor");
-      // The first (and perhaps only) waiter we've chosen to wake is w, whose
-      // predecessor is pw.  If w is a reader, we must wake all the other
-      // waiters with wake==true as well.  We may also need to queue
-      // ourselves if waitp != null.  The spinlock and the lock are still
-      // held.
-
-      // This traverses the list in [ pw->next, h ], where h is the head,
-      // removing all elements with wake==true and placing them in the
-      // singly-linked list wake_list.  Returns the new head.
-      h = DequeueAllWakeable(h, pw, &wake_list);
-
-      intptr_t nv = (v & kMuEvent) | kMuDesig;
-                                             // assume no waiters left,
-                                             // set kMuDesig for INV1a
-
-      if (waitp != nullptr) {  // we must queue ourselves and sleep
-        h = Enqueue(h, waitp, v, kMuIsCond);
-        // h is new last waiter; could be null if we queued ourselves on a
-        // CondVar
-      }
-
-      ABSL_RAW_CHECK(wake_list != kPerThreadSynchNull,
-                     "unexpected empty wake list");
-
-      if (h != nullptr) {  // there are waiters left
-        h->readers = 0;
-        h->maybe_unlocking = false;     // finished unlocking
-        nv |= wr_wait | kMuWait | reinterpret_cast<intptr_t>(h);
-      }
-
-      // release both spinlock & lock
-      // can release with a store because there were waiters
-      mu_.store(nv, std::memory_order_release);
-      break;  // out of for(;;)-loop
-    }
-    // aggressive here; no one can proceed till we do
-    c = synchronization_internal::MutexDelay(c, AGGRESSIVE);
-  }                            // end of for(;;)-loop
-
-  if (wake_list != kPerThreadSynchNull) {
-    int64_t enqueue_timestamp = wake_list->waitp->contention_start_cycles;
-    bool cond_waiter = wake_list->cond_waiter;
-    do {
-      wake_list = Wakeup(wake_list);              // wake waiters
-    } while (wake_list != kPerThreadSynchNull);
-    if (!cond_waiter) {
-      // Sample lock contention events only if the (first) waiter was trying to
-      // acquire the lock, not waiting on a condition variable or Condition.
-      int64_t wait_cycles =
-          base_internal::CycleClock::Now() - enqueue_timestamp;
-      mutex_tracer("slow release", this, wait_cycles);
-      ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0);
-      submit_profile_data(enqueue_timestamp);
-      ABSL_TSAN_MUTEX_POST_DIVERT(this, 0);
-    }
-  }
-}
-
-// Used by CondVar implementation to reacquire mutex after waking from
-// condition variable.  This routine is used instead of Lock() because the
-// waiting thread may have been moved from the condition variable queue to the
-// mutex queue without a wakeup, by Trans().  In that case, when the thread is
-// finally woken, the woken thread will believe it has been woken from the
-// condition variable (i.e. its PC will be in when in the CondVar code), when
-// in fact it has just been woken from the mutex.  Thus, it must enter the slow
-// path of the mutex in the same state as if it had just woken from the mutex.
-// That is, it must ensure to clear kMuDesig (INV1b).
-void Mutex::Trans(MuHow how) {
-  this->LockSlow(how, nullptr, kMuHasBlocked | kMuIsCond);
-}
-
-// Used by CondVar implementation to effectively wake thread w from the
-// condition variable.  If this mutex is free, we simply wake the thread.
-// It will later acquire the mutex with high probability.  Otherwise, we
-// enqueue thread w on this mutex.
-void Mutex::Fer(PerThreadSynch *w) {
-  SchedulingGuard::ScopedDisable disable_rescheduling;
-  int c = 0;
-  ABSL_RAW_CHECK(w->waitp->cond == nullptr,
-                 "Mutex::Fer while waiting on Condition");
-  ABSL_RAW_CHECK(!w->waitp->timeout.has_timeout(),
-                 "Mutex::Fer while in timed wait");
-  ABSL_RAW_CHECK(w->waitp->cv_word == nullptr,
-                 "Mutex::Fer with pending CondVar queueing");
-  for (;;) {
-    intptr_t v = mu_.load(std::memory_order_relaxed);
-    // Note: must not queue if the mutex is unlocked (nobody will wake it).
-    // For example, we can have only kMuWait (conditional) or maybe
-    // kMuWait|kMuWrWait.
-    // conflicting != 0 implies that the waking thread cannot currently take
-    // the mutex, which in turn implies that someone else has it and can wake
-    // us if we queue.
-    const intptr_t conflicting =
-        kMuWriter | (w->waitp->how == kShared ? 0 : kMuReader);
-    if ((v & conflicting) == 0) {
-      w->next = nullptr;
-      w->state.store(PerThreadSynch::kAvailable, std::memory_order_release);
-      IncrementSynchSem(this, w);
-      return;
-    } else {
-      if ((v & (kMuSpin|kMuWait)) == 0) {       // no waiters
-        // This thread tries to become the one and only waiter.
-        PerThreadSynch *new_h = Enqueue(nullptr, w->waitp, v, kMuIsCond);
-        ABSL_RAW_CHECK(new_h != nullptr,
-                       "Enqueue failed");  // we must queue ourselves
-        if (mu_.compare_exchange_strong(
-                v, reinterpret_cast<intptr_t>(new_h) | (v & kMuLow) | kMuWait,
-                std::memory_order_release, std::memory_order_relaxed)) {
-          return;
-        }
-      } else if ((v & kMuSpin) == 0 &&
-                 mu_.compare_exchange_strong(v, v | kMuSpin | kMuWait)) {
-        PerThreadSynch *h = GetPerThreadSynch(v);
-        PerThreadSynch *new_h = Enqueue(h, w->waitp, v, kMuIsCond);
-        ABSL_RAW_CHECK(new_h != nullptr,
-                       "Enqueue failed");  // we must queue ourselves
-        do {
-          v = mu_.load(std::memory_order_relaxed);
-        } while (!mu_.compare_exchange_weak(
-            v,
-            (v & kMuLow & ~kMuSpin) | kMuWait |
-                reinterpret_cast<intptr_t>(new_h),
-            std::memory_order_release, std::memory_order_relaxed));
-        return;
-      }
-    }
-    c = synchronization_internal::MutexDelay(c, GENTLE);
-  }
-}
-
-void Mutex::AssertHeld() const {
-  if ((mu_.load(std::memory_order_relaxed) & kMuWriter) == 0) {
-    SynchEvent *e = GetSynchEvent(this);
-    ABSL_RAW_LOG(FATAL, "thread should hold write lock on Mutex %p %s",
-                 static_cast<const void *>(this),
-                 (e == nullptr ? "" : e->name));
-  }
-}
-
-void Mutex::AssertReaderHeld() const {
-  if ((mu_.load(std::memory_order_relaxed) & (kMuReader | kMuWriter)) == 0) {
-    SynchEvent *e = GetSynchEvent(this);
-    ABSL_RAW_LOG(
-        FATAL, "thread should hold at least a read lock on Mutex %p %s",
-        static_cast<const void *>(this), (e == nullptr ? "" : e->name));
-  }
-}
-
-// -------------------------------- condition variables
-static const intptr_t kCvSpin = 0x0001L;   // spinlock protects waiter list
-static const intptr_t kCvEvent = 0x0002L;  // record events
-
-static const intptr_t kCvLow = 0x0003L;  // low order bits of CV
-
-// Hack to make constant values available to gdb pretty printer
-enum { kGdbCvSpin = kCvSpin, kGdbCvEvent = kCvEvent, kGdbCvLow = kCvLow, };
-
-static_assert(PerThreadSynch::kAlignment > kCvLow,
-              "PerThreadSynch::kAlignment must be greater than kCvLow");
-
-void CondVar::EnableDebugLog(const char *name) {
-  SynchEvent *e = EnsureSynchEvent(&this->cv_, name, kCvEvent, kCvSpin);
-  e->log = true;
-  UnrefSynchEvent(e);
-}
-
-CondVar::~CondVar() {
-  if ((cv_.load(std::memory_order_relaxed) & kCvEvent) != 0) {
-    ForgetSynchEvent(&this->cv_, kCvEvent, kCvSpin);
-  }
-}
-
-
-// Remove thread s from the list of waiters on this condition variable.
-void CondVar::Remove(PerThreadSynch *s) {
-  SchedulingGuard::ScopedDisable disable_rescheduling;
-  intptr_t v;
-  int c = 0;
-  for (v = cv_.load(std::memory_order_relaxed);;
-       v = cv_.load(std::memory_order_relaxed)) {
-    if ((v & kCvSpin) == 0 &&  // attempt to acquire spinlock
-        cv_.compare_exchange_strong(v, v | kCvSpin,
-                                    std::memory_order_acquire,
-                                    std::memory_order_relaxed)) {
-      PerThreadSynch *h = reinterpret_cast<PerThreadSynch *>(v & ~kCvLow);
-      if (h != nullptr) {
-        PerThreadSynch *w = h;
-        while (w->next != s && w->next != h) {  // search for thread
-          w = w->next;
-        }
-        if (w->next == s) {           // found thread; remove it
-          w->next = s->next;
-          if (h == s) {
-            h = (w == s) ? nullptr : w;
-          }
-          s->next = nullptr;
-          s->state.store(PerThreadSynch::kAvailable, std::memory_order_release);
-        }
-      }
-                                      // release spinlock
-      cv_.store((v & kCvEvent) | reinterpret_cast<intptr_t>(h),
-                std::memory_order_release);
-      return;
-    } else {
-      // try again after a delay
-      c = synchronization_internal::MutexDelay(c, GENTLE);
-    }
-  }
-}
-
-// Queue thread waitp->thread on condition variable word cv_word using
-// wait parameters waitp.
-// We split this into a separate routine, rather than simply doing it as part
-// of WaitCommon().  If we were to queue ourselves on the condition variable
-// before calling Mutex::UnlockSlow(), the Mutex code might be re-entered (via
-// the logging code, or via a Condition function) and might potentially attempt
-// to block this thread.  That would be a problem if the thread were already on
-// a the condition variable waiter queue.  Thus, we use the waitp->cv_word
-// to tell the unlock code to call CondVarEnqueue() to queue the thread on the
-// condition variable queue just before the mutex is to be unlocked, and (most
-// importantly) after any call to an external routine that might re-enter the
-// mutex code.
-static void CondVarEnqueue(SynchWaitParams *waitp) {
-  // This thread might be transferred to the Mutex queue by Fer() when
-  // we are woken.  To make sure that is what happens, Enqueue() doesn't
-  // call CondVarEnqueue() again but instead uses its normal code.  We
-  // must do this before we queue ourselves so that cv_word will be null
-  // when seen by the dequeuer, who may wish immediately to requeue
-  // this thread on another queue.
-  std::atomic<intptr_t> *cv_word = waitp->cv_word;
-  waitp->cv_word = nullptr;
-
-  intptr_t v = cv_word->load(std::memory_order_relaxed);
-  int c = 0;
-  while ((v & kCvSpin) != 0 ||  // acquire spinlock
-         !cv_word->compare_exchange_weak(v, v | kCvSpin,
-                                         std::memory_order_acquire,
-                                         std::memory_order_relaxed)) {
-    c = synchronization_internal::MutexDelay(c, GENTLE);
-    v = cv_word->load(std::memory_order_relaxed);
-  }
-  ABSL_RAW_CHECK(waitp->thread->waitp == nullptr, "waiting when shouldn't be");
-  waitp->thread->waitp = waitp;      // prepare ourselves for waiting
-  PerThreadSynch *h = reinterpret_cast<PerThreadSynch *>(v & ~kCvLow);
-  if (h == nullptr) {  // add this thread to waiter list
-    waitp->thread->next = waitp->thread;
-  } else {
-    waitp->thread->next = h->next;
-    h->next = waitp->thread;
-  }
-  waitp->thread->state.store(PerThreadSynch::kQueued,
-                             std::memory_order_relaxed);
-  cv_word->store((v & kCvEvent) | reinterpret_cast<intptr_t>(waitp->thread),
-                 std::memory_order_release);
-}
-
-bool CondVar::WaitCommon(Mutex *mutex, KernelTimeout t) {
-  bool rc = false;          // return value; true iff we timed-out
-
-  intptr_t mutex_v = mutex->mu_.load(std::memory_order_relaxed);
-  Mutex::MuHow mutex_how = ((mutex_v & kMuWriter) != 0) ? kExclusive : kShared;
-  ABSL_TSAN_MUTEX_PRE_UNLOCK(mutex, TsanFlags(mutex_how));
-
-  // maybe trace this call
-  intptr_t v = cv_.load(std::memory_order_relaxed);
-  cond_var_tracer("Wait", this);
-  if ((v & kCvEvent) != 0) {
-    PostSynchEvent(this, SYNCH_EV_WAIT);
-  }
-
-  // Release mu and wait on condition variable.
-  SynchWaitParams waitp(mutex_how, nullptr, t, mutex,
-                        Synch_GetPerThreadAnnotated(mutex), &cv_);
-  // UnlockSlow() will call CondVarEnqueue() just before releasing the
-  // Mutex, thus queuing this thread on the condition variable.  See
-  // CondVarEnqueue() for the reasons.
-  mutex->UnlockSlow(&waitp);
-
-  // wait for signal
-  while (waitp.thread->state.load(std::memory_order_acquire) ==
-         PerThreadSynch::kQueued) {
-    if (!Mutex::DecrementSynchSem(mutex, waitp.thread, t)) {
-      this->Remove(waitp.thread);
-      rc = true;
-    }
-  }
-
-  ABSL_RAW_CHECK(waitp.thread->waitp != nullptr, "not waiting when should be");
-  waitp.thread->waitp = nullptr;  // cleanup
-
-  // maybe trace this call
-  cond_var_tracer("Unwait", this);
-  if ((v & kCvEvent) != 0) {
-    PostSynchEvent(this, SYNCH_EV_WAIT_RETURNING);
-  }
-
-  // From synchronization point of view Wait is unlock of the mutex followed
-  // by lock of the mutex. We've annotated start of unlock in the beginning
-  // of the function. Now, finish unlock and annotate lock of the mutex.
-  // (Trans is effectively lock).
-  ABSL_TSAN_MUTEX_POST_UNLOCK(mutex, TsanFlags(mutex_how));
-  ABSL_TSAN_MUTEX_PRE_LOCK(mutex, TsanFlags(mutex_how));
-  mutex->Trans(mutex_how);  // Reacquire mutex
-  ABSL_TSAN_MUTEX_POST_LOCK(mutex, TsanFlags(mutex_how), 0);
-  return rc;
-}
-
-bool CondVar::WaitWithTimeout(Mutex *mu, absl::Duration timeout) {
-  return WaitWithDeadline(mu, DeadlineFromTimeout(timeout));
-}
-
-bool CondVar::WaitWithDeadline(Mutex *mu, absl::Time deadline) {
-  return WaitCommon(mu, KernelTimeout(deadline));
-}
-
-void CondVar::Wait(Mutex *mu) {
-  WaitCommon(mu, KernelTimeout::Never());
-}
-
-// Wake thread w
-// If it was a timed wait, w will be waiting on w->cv
-// Otherwise, if it was not a Mutex mutex, w will be waiting on w->sem
-// Otherwise, w is transferred to the Mutex mutex via Mutex::Fer().
-void CondVar::Wakeup(PerThreadSynch *w) {
-  if (w->waitp->timeout.has_timeout() || w->waitp->cvmu == nullptr) {
-    // The waiting thread only needs to observe "w->state == kAvailable" to be
-    // released, we must cache "cvmu" before clearing "next".
-    Mutex *mu = w->waitp->cvmu;
-    w->next = nullptr;
-    w->state.store(PerThreadSynch::kAvailable, std::memory_order_release);
-    Mutex::IncrementSynchSem(mu, w);
-  } else {
-    w->waitp->cvmu->Fer(w);
-  }
-}
-
-void CondVar::Signal() {
-  SchedulingGuard::ScopedDisable disable_rescheduling;
-  ABSL_TSAN_MUTEX_PRE_SIGNAL(nullptr, 0);
-  intptr_t v;
-  int c = 0;
-  for (v = cv_.load(std::memory_order_relaxed); v != 0;
-       v = cv_.load(std::memory_order_relaxed)) {
-    if ((v & kCvSpin) == 0 &&  // attempt to acquire spinlock
-        cv_.compare_exchange_strong(v, v | kCvSpin,
-                                    std::memory_order_acquire,
-                                    std::memory_order_relaxed)) {
-      PerThreadSynch *h = reinterpret_cast<PerThreadSynch *>(v & ~kCvLow);
-      PerThreadSynch *w = nullptr;
-      if (h != nullptr) {  // remove first waiter
-        w = h->next;
-        if (w == h) {
-          h = nullptr;
-        } else {
-          h->next = w->next;
-        }
-      }
-                                      // release spinlock
-      cv_.store((v & kCvEvent) | reinterpret_cast<intptr_t>(h),
-                std::memory_order_release);
-      if (w != nullptr) {
-        CondVar::Wakeup(w);                // wake waiter, if there was one
-        cond_var_tracer("Signal wakeup", this);
-      }
-      if ((v & kCvEvent) != 0) {
-        PostSynchEvent(this, SYNCH_EV_SIGNAL);
-      }
-      ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0);
-      return;
-    } else {
-      c = synchronization_internal::MutexDelay(c, GENTLE);
-    }
-  }
-  ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0);
-}
-
-void CondVar::SignalAll () {
-  ABSL_TSAN_MUTEX_PRE_SIGNAL(nullptr, 0);
-  intptr_t v;
-  int c = 0;
-  for (v = cv_.load(std::memory_order_relaxed); v != 0;
-       v = cv_.load(std::memory_order_relaxed)) {
-    // empty the list if spinlock free
-    // We do this by simply setting the list to empty using
-    // compare and swap.   We then have the entire list in our hands,
-    // which cannot be changing since we grabbed it while no one
-    // held the lock.
-    if ((v & kCvSpin) == 0 &&
-        cv_.compare_exchange_strong(v, v & kCvEvent, std::memory_order_acquire,
-                                    std::memory_order_relaxed)) {
-      PerThreadSynch *h = reinterpret_cast<PerThreadSynch *>(v & ~kCvLow);
-      if (h != nullptr) {
-        PerThreadSynch *w;
-        PerThreadSynch *n = h->next;
-        do {                          // for every thread, wake it up
-          w = n;
-          n = n->next;
-          CondVar::Wakeup(w);
-        } while (w != h);
-        cond_var_tracer("SignalAll wakeup", this);
-      }
-      if ((v & kCvEvent) != 0) {
-        PostSynchEvent(this, SYNCH_EV_SIGNALALL);
-      }
-      ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0);
-      return;
-    } else {
-      // try again after a delay
-      c = synchronization_internal::MutexDelay(c, GENTLE);
-    }
-  }
-  ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0);
-}
-
-void ReleasableMutexLock::Release() {
-  ABSL_RAW_CHECK(this->mu_ != nullptr,
-                 "ReleasableMutexLock::Release may only be called once");
-  this->mu_->Unlock();
-  this->mu_ = nullptr;
-}
-
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-extern "C" void __tsan_read1(void *addr);
-#else
-#define __tsan_read1(addr)  // do nothing if TSan not enabled
-#endif
-
-// A function that just returns its argument, dereferenced
-static bool Dereference(void *arg) {
-  // ThreadSanitizer does not instrument this file for memory accesses.
-  // This function dereferences a user variable that can participate
-  // in a data race, so we need to manually tell TSan about this memory access.
-  __tsan_read1(arg);
-  return *(static_cast<bool *>(arg));
-}
-
-Condition::Condition() {}   // null constructor, used for kTrue only
-const Condition Condition::kTrue;
-
-Condition::Condition(bool (*func)(void *), void *arg)
-    : eval_(&CallVoidPtrFunction),
-      function_(func),
-      method_(nullptr),
-      arg_(arg) {}
-
-bool Condition::CallVoidPtrFunction(const Condition *c) {
-  return (*c->function_)(c->arg_);
-}
-
-Condition::Condition(const bool *cond)
-    : eval_(CallVoidPtrFunction),
-      function_(Dereference),
-      method_(nullptr),
-      // const_cast is safe since Dereference does not modify arg
-      arg_(const_cast<bool *>(cond)) {}
-
-bool Condition::Eval() const {
-  // eval_ == null for kTrue
-  return (this->eval_ == nullptr) || (*this->eval_)(this);
-}
-
-bool Condition::GuaranteedEqual(const Condition *a, const Condition *b) {
-  if (a == nullptr) {
-    return b == nullptr || b->eval_ == nullptr;
-  }
-  if (b == nullptr || b->eval_ == nullptr) {
-    return a->eval_ == nullptr;
-  }
-  return a->eval_ == b->eval_ && a->function_ == b->function_ &&
-         a->arg_ == b->arg_ && a->method_ == b->method_;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/synchronization/mutex.h b/third_party/abseil_cpp/absl/synchronization/mutex.h
deleted file mode 100644
index 598d1e0617..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/mutex.h
+++ /dev/null
@@ -1,1084 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// mutex.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines a `Mutex` -- a mutually exclusive lock -- and the
-// most common type of synchronization primitive for facilitating locks on
-// shared resources. A mutex is used to prevent multiple threads from accessing
-// and/or writing to a shared resource concurrently.
-//
-// Unlike a `std::mutex`, the Abseil `Mutex` provides the following additional
-// features:
-//   * Conditional predicates intrinsic to the `Mutex` object
-//   * Shared/reader locks, in addition to standard exclusive/writer locks
-//   * Deadlock detection and debug support.
-//
-// The following helper classes are also defined within this file:
-//
-//  MutexLock - An RAII wrapper to acquire and release a `Mutex` for exclusive/
-//              write access within the current scope.
-//
-//  ReaderMutexLock
-//            - An RAII wrapper to acquire and release a `Mutex` for shared/read
-//              access within the current scope.
-//
-//  WriterMutexLock
-//            - Effectively an alias for `MutexLock` above, designed for use in
-//              distinguishing reader and writer locks within code.
-//
-// In addition to simple mutex locks, this file also defines ways to perform
-// locking under certain conditions.
-//
-//  Condition - (Preferred) Used to wait for a particular predicate that
-//              depends on state protected by the `Mutex` to become true.
-//  CondVar   - A lower-level variant of `Condition` that relies on
-//              application code to explicitly signal the `CondVar` when
-//              a condition has been met.
-//
-// See below for more information on using `Condition` or `CondVar`.
-//
-// Mutexes and mutex behavior can be quite complicated. The information within
-// this header file is limited, as a result. Please consult the Mutex guide for
-// more complete information and examples.
-
-#ifndef ABSL_SYNCHRONIZATION_MUTEX_H_
-#define ABSL_SYNCHRONIZATION_MUTEX_H_
-
-#include <atomic>
-#include <cstdint>
-#include <string>
-
-#include "absl/base/const_init.h"
-#include "absl/base/internal/identity.h"
-#include "absl/base/internal/low_level_alloc.h"
-#include "absl/base/internal/thread_identity.h"
-#include "absl/base/internal/tsan_mutex_interface.h"
-#include "absl/base/port.h"
-#include "absl/base/thread_annotations.h"
-#include "absl/synchronization/internal/kernel_timeout.h"
-#include "absl/synchronization/internal/per_thread_sem.h"
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class Condition;
-struct SynchWaitParams;
-
-// -----------------------------------------------------------------------------
-// Mutex
-// -----------------------------------------------------------------------------
-//
-// A `Mutex` is a non-reentrant (aka non-recursive) Mutually Exclusive lock
-// on some resource, typically a variable or data structure with associated
-// invariants. Proper usage of mutexes prevents concurrent access by different
-// threads to the same resource.
-//
-// A `Mutex` has two basic operations: `Mutex::Lock()` and `Mutex::Unlock()`.
-// The `Lock()` operation *acquires* a `Mutex` (in a state known as an
-// *exclusive* -- or write -- lock), while the `Unlock()` operation *releases* a
-// Mutex. During the span of time between the Lock() and Unlock() operations,
-// a mutex is said to be *held*. By design all mutexes support exclusive/write
-// locks, as this is the most common way to use a mutex.
-//
-// The `Mutex` state machine for basic lock/unlock operations is quite simple:
-//
-// |                | Lock()     | Unlock() |
-// |----------------+------------+----------|
-// | Free           | Exclusive  | invalid  |
-// | Exclusive      | blocks     | Free     |
-//
-// Attempts to `Unlock()` must originate from the thread that performed the
-// corresponding `Lock()` operation.
-//
-// An "invalid" operation is disallowed by the API. The `Mutex` implementation
-// is allowed to do anything on an invalid call, including but not limited to
-// crashing with a useful error message, silently succeeding, or corrupting
-// data structures. In debug mode, the implementation attempts to crash with a
-// useful error message.
-//
-// `Mutex` is not guaranteed to be "fair" in prioritizing waiting threads; it
-// is, however, approximately fair over long periods, and starvation-free for
-// threads at the same priority.
-//
-// The lock/unlock primitives are now annotated with lock annotations
-// defined in (base/thread_annotations.h). When writing multi-threaded code,
-// you should use lock annotations whenever possible to document your lock
-// synchronization policy. Besides acting as documentation, these annotations
-// also help compilers or static analysis tools to identify and warn about
-// issues that could potentially result in race conditions and deadlocks.
-//
-// For more information about the lock annotations, please see
-// [Thread Safety Analysis](http://clang.llvm.org/docs/ThreadSafetyAnalysis.html)
-// in the Clang documentation.
-//
-// See also `MutexLock`, below, for scoped `Mutex` acquisition.
-
-class ABSL_LOCKABLE Mutex {
- public:
-  // Creates a `Mutex` that is not held by anyone. This constructor is
-  // typically used for Mutexes allocated on the heap or the stack.
-  //
-  // To create `Mutex` instances with static storage duration
-  // (e.g. a namespace-scoped or global variable), see
-  // `Mutex::Mutex(absl::kConstInit)` below instead.
-  Mutex();
-
-  // Creates a mutex with static storage duration.  A global variable
-  // constructed this way avoids the lifetime issues that can occur on program
-  // startup and shutdown.  (See absl/base/const_init.h.)
-  //
-  // For Mutexes allocated on the heap and stack, instead use the default
-  // constructor, which can interact more fully with the thread sanitizer.
-  //
-  // Example usage:
-  //   namespace foo {
-  //   ABSL_CONST_INIT Mutex mu(absl::kConstInit);
-  //   }
-  explicit constexpr Mutex(absl::ConstInitType);
-
-  ~Mutex();
-
-  // Mutex::Lock()
-  //
-  // Blocks the calling thread, if necessary, until this `Mutex` is free, and
-  // then acquires it exclusively. (This lock is also known as a "write lock.")
-  void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION();
-
-  // Mutex::Unlock()
-  //
-  // Releases this `Mutex` and returns it from the exclusive/write state to the
-  // free state. Caller must hold the `Mutex` exclusively.
-  void Unlock() ABSL_UNLOCK_FUNCTION();
-
-  // Mutex::TryLock()
-  //
-  // If the mutex can be acquired without blocking, does so exclusively and
-  // returns `true`. Otherwise, returns `false`. Returns `true` with high
-  // probability if the `Mutex` was free.
-  bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true);
-
-  // Mutex::AssertHeld()
-  //
-  // Return immediately if this thread holds the `Mutex` exclusively (in write
-  // mode). Otherwise, may report an error (typically by crashing with a
-  // diagnostic), or may return immediately.
-  void AssertHeld() const ABSL_ASSERT_EXCLUSIVE_LOCK();
-
-  // ---------------------------------------------------------------------------
-  // Reader-Writer Locking
-  // ---------------------------------------------------------------------------
-
-  // A Mutex can also be used as a starvation-free reader-writer lock.
-  // Neither read-locks nor write-locks are reentrant/recursive to avoid
-  // potential client programming errors.
-  //
-  // The Mutex API provides `Writer*()` aliases for the existing `Lock()`,
-  // `Unlock()` and `TryLock()` methods for use within applications mixing
-  // reader/writer locks. Using `Reader*()` and `Writer*()` operations in this
-  // manner can make locking behavior clearer when mixing read and write modes.
-  //
-  // Introducing reader locks necessarily complicates the `Mutex` state
-  // machine somewhat. The table below illustrates the allowed state transitions
-  // of a mutex in such cases. Note that ReaderLock() may block even if the lock
-  // is held in shared mode; this occurs when another thread is blocked on a
-  // call to WriterLock().
-  //
-  // ---------------------------------------------------------------------------
-  //     Operation: WriterLock() Unlock()  ReaderLock()           ReaderUnlock()
-  // ---------------------------------------------------------------------------
-  // State
-  // ---------------------------------------------------------------------------
-  // Free           Exclusive    invalid   Shared(1)              invalid
-  // Shared(1)      blocks       invalid   Shared(2) or blocks    Free
-  // Shared(n) n>1  blocks       invalid   Shared(n+1) or blocks  Shared(n-1)
-  // Exclusive      blocks       Free      blocks                 invalid
-  // ---------------------------------------------------------------------------
-  //
-  // In comments below, "shared" refers to a state of Shared(n) for any n > 0.
-
-  // Mutex::ReaderLock()
-  //
-  // Blocks the calling thread, if necessary, until this `Mutex` is either free,
-  // or in shared mode, and then acquires a share of it. Note that
-  // `ReaderLock()` will block if some other thread has an exclusive/writer lock
-  // on the mutex.
-
-  void ReaderLock() ABSL_SHARED_LOCK_FUNCTION();
-
-  // Mutex::ReaderUnlock()
-  //
-  // Releases a read share of this `Mutex`. `ReaderUnlock` may return a mutex to
-  // the free state if this thread holds the last reader lock on the mutex. Note
-  // that you cannot call `ReaderUnlock()` on a mutex held in write mode.
-  void ReaderUnlock() ABSL_UNLOCK_FUNCTION();
-
-  // Mutex::ReaderTryLock()
-  //
-  // If the mutex can be acquired without blocking, acquires this mutex for
-  // shared access and returns `true`. Otherwise, returns `false`. Returns
-  // `true` with high probability if the `Mutex` was free or shared.
-  bool ReaderTryLock() ABSL_SHARED_TRYLOCK_FUNCTION(true);
-
-  // Mutex::AssertReaderHeld()
-  //
-  // Returns immediately if this thread holds the `Mutex` in at least shared
-  // mode (read mode). Otherwise, may report an error (typically by
-  // crashing with a diagnostic), or may return immediately.
-  void AssertReaderHeld() const ABSL_ASSERT_SHARED_LOCK();
-
-  // Mutex::WriterLock()
-  // Mutex::WriterUnlock()
-  // Mutex::WriterTryLock()
-  //
-  // Aliases for `Mutex::Lock()`, `Mutex::Unlock()`, and `Mutex::TryLock()`.
-  //
-  // These methods may be used (along with the complementary `Reader*()`
-  // methods) to distingish simple exclusive `Mutex` usage (`Lock()`,
-  // etc.) from reader/writer lock usage.
-  void WriterLock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { this->Lock(); }
-
-  void WriterUnlock() ABSL_UNLOCK_FUNCTION() { this->Unlock(); }
-
-  bool WriterTryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
-    return this->TryLock();
-  }
-
-  // ---------------------------------------------------------------------------
-  // Conditional Critical Regions
-  // ---------------------------------------------------------------------------
-
-  // Conditional usage of a `Mutex` can occur using two distinct paradigms:
-  //
-  //   * Use of `Mutex` member functions with `Condition` objects.
-  //   * Use of the separate `CondVar` abstraction.
-  //
-  // In general, prefer use of `Condition` and the `Mutex` member functions
-  // listed below over `CondVar`. When there are multiple threads waiting on
-  // distinctly different conditions, however, a battery of `CondVar`s may be
-  // more efficient. This section discusses use of `Condition` objects.
-  //
-  // `Mutex` contains member functions for performing lock operations only under
-  // certain conditions, of class `Condition`. For correctness, the `Condition`
-  // must return a boolean that is a pure function, only of state protected by
-  // the `Mutex`. The condition must be invariant w.r.t. environmental state
-  // such as thread, cpu id, or time, and must be `noexcept`. The condition will
-  // always be invoked with the mutex held in at least read mode, so you should
-  // not block it for long periods or sleep it on a timer.
-  //
-  // Since a condition must not depend directly on the current time, use
-  // `*WithTimeout()` member function variants to make your condition
-  // effectively true after a given duration, or `*WithDeadline()` variants to
-  // make your condition effectively true after a given time.
-  //
-  // The condition function should have no side-effects aside from debug
-  // logging; as a special exception, the function may acquire other mutexes
-  // provided it releases all those that it acquires.  (This exception was
-  // required to allow logging.)
-
-  // Mutex::Await()
-  //
-  // Unlocks this `Mutex` and blocks until simultaneously both `cond` is `true`
-  // and this `Mutex` can be reacquired, then reacquires this `Mutex` in the
-  // same mode in which it was previously held. If the condition is initially
-  // `true`, `Await()` *may* skip the release/re-acquire step.
-  //
-  // `Await()` requires that this thread holds this `Mutex` in some mode.
-  void Await(const Condition &cond);
-
-  // Mutex::LockWhen()
-  // Mutex::ReaderLockWhen()
-  // Mutex::WriterLockWhen()
-  //
-  // Blocks until simultaneously both `cond` is `true` and this `Mutex` can
-  // be acquired, then atomically acquires this `Mutex`. `LockWhen()` is
-  // logically equivalent to `*Lock(); Await();` though they may have different
-  // performance characteristics.
-  void LockWhen(const Condition &cond) ABSL_EXCLUSIVE_LOCK_FUNCTION();
-
-  void ReaderLockWhen(const Condition &cond) ABSL_SHARED_LOCK_FUNCTION();
-
-  void WriterLockWhen(const Condition &cond) ABSL_EXCLUSIVE_LOCK_FUNCTION() {
-    this->LockWhen(cond);
-  }
-
-  // ---------------------------------------------------------------------------
-  // Mutex Variants with Timeouts/Deadlines
-  // ---------------------------------------------------------------------------
-
-  // Mutex::AwaitWithTimeout()
-  // Mutex::AwaitWithDeadline()
-  //
-  // Unlocks this `Mutex` and blocks until simultaneously:
-  //   - either `cond` is true or the {timeout has expired, deadline has passed}
-  //     and
-  //   - this `Mutex` can be reacquired,
-  // then reacquire this `Mutex` in the same mode in which it was previously
-  // held, returning `true` iff `cond` is `true` on return.
-  //
-  // If the condition is initially `true`, the implementation *may* skip the
-  // release/re-acquire step and return immediately.
-  //
-  // Deadlines in the past are equivalent to an immediate deadline.
-  // Negative timeouts are equivalent to a zero timeout.
-  //
-  // This method requires that this thread holds this `Mutex` in some mode.
-  bool AwaitWithTimeout(const Condition &cond, absl::Duration timeout);
-
-  bool AwaitWithDeadline(const Condition &cond, absl::Time deadline);
-
-  // Mutex::LockWhenWithTimeout()
-  // Mutex::ReaderLockWhenWithTimeout()
-  // Mutex::WriterLockWhenWithTimeout()
-  //
-  // Blocks until simultaneously both:
-  //   - either `cond` is `true` or the timeout has expired, and
-  //   - this `Mutex` can be acquired,
-  // then atomically acquires this `Mutex`, returning `true` iff `cond` is
-  // `true` on return.
-  //
-  // Negative timeouts are equivalent to a zero timeout.
-  bool LockWhenWithTimeout(const Condition &cond, absl::Duration timeout)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION();
-  bool ReaderLockWhenWithTimeout(const Condition &cond, absl::Duration timeout)
-      ABSL_SHARED_LOCK_FUNCTION();
-  bool WriterLockWhenWithTimeout(const Condition &cond, absl::Duration timeout)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION() {
-    return this->LockWhenWithTimeout(cond, timeout);
-  }
-
-  // Mutex::LockWhenWithDeadline()
-  // Mutex::ReaderLockWhenWithDeadline()
-  // Mutex::WriterLockWhenWithDeadline()
-  //
-  // Blocks until simultaneously both:
-  //   - either `cond` is `true` or the deadline has been passed, and
-  //   - this `Mutex` can be acquired,
-  // then atomically acquires this Mutex, returning `true` iff `cond` is `true`
-  // on return.
-  //
-  // Deadlines in the past are equivalent to an immediate deadline.
-  bool LockWhenWithDeadline(const Condition &cond, absl::Time deadline)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION();
-  bool ReaderLockWhenWithDeadline(const Condition &cond, absl::Time deadline)
-      ABSL_SHARED_LOCK_FUNCTION();
-  bool WriterLockWhenWithDeadline(const Condition &cond, absl::Time deadline)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION() {
-    return this->LockWhenWithDeadline(cond, deadline);
-  }
-
-  // ---------------------------------------------------------------------------
-  // Debug Support: Invariant Checking, Deadlock Detection, Logging.
-  // ---------------------------------------------------------------------------
-
-  // Mutex::EnableInvariantDebugging()
-  //
-  // If `invariant`!=null and if invariant debugging has been enabled globally,
-  // cause `(*invariant)(arg)` to be called at moments when the invariant for
-  // this `Mutex` should hold (for example: just after acquire, just before
-  // release).
-  //
-  // The routine `invariant` should have no side-effects since it is not
-  // guaranteed how many times it will be called; it should check the invariant
-  // and crash if it does not hold. Enabling global invariant debugging may
-  // substantially reduce `Mutex` performance; it should be set only for
-  // non-production runs.  Optimization options may also disable invariant
-  // checks.
-  void EnableInvariantDebugging(void (*invariant)(void *), void *arg);
-
-  // Mutex::EnableDebugLog()
-  //
-  // Cause all subsequent uses of this `Mutex` to be logged via
-  // `ABSL_RAW_LOG(INFO)`. Log entries are tagged with `name` if no previous
-  // call to `EnableInvariantDebugging()` or `EnableDebugLog()` has been made.
-  //
-  // Note: This method substantially reduces `Mutex` performance.
-  void EnableDebugLog(const char *name);
-
-  // Deadlock detection
-
-  // Mutex::ForgetDeadlockInfo()
-  //
-  // Forget any deadlock-detection information previously gathered
-  // about this `Mutex`. Call this method in debug mode when the lock ordering
-  // of a `Mutex` changes.
-  void ForgetDeadlockInfo();
-
-  // Mutex::AssertNotHeld()
-  //
-  // Return immediately if this thread does not hold this `Mutex` in any
-  // mode; otherwise, may report an error (typically by crashing with a
-  // diagnostic), or may return immediately.
-  //
-  // Currently this check is performed only if all of:
-  //    - in debug mode
-  //    - SetMutexDeadlockDetectionMode() has been set to kReport or kAbort
-  //    - number of locks concurrently held by this thread is not large.
-  // are true.
-  void AssertNotHeld() const;
-
-  // Special cases.
-
-  // A `MuHow` is a constant that indicates how a lock should be acquired.
-  // Internal implementation detail.  Clients should ignore.
-  typedef const struct MuHowS *MuHow;
-
-  // Mutex::InternalAttemptToUseMutexInFatalSignalHandler()
-  //
-  // Causes the `Mutex` implementation to prepare itself for re-entry caused by
-  // future use of `Mutex` within a fatal signal handler. This method is
-  // intended for use only for last-ditch attempts to log crash information.
-  // It does not guarantee that attempts to use Mutexes within the handler will
-  // not deadlock; it merely makes other faults less likely.
-  //
-  // WARNING:  This routine must be invoked from a signal handler, and the
-  // signal handler must either loop forever or terminate the process.
-  // Attempts to return from (or `longjmp` out of) the signal handler once this
-  // call has been made may cause arbitrary program behaviour including
-  // crashes and deadlocks.
-  static void InternalAttemptToUseMutexInFatalSignalHandler();
-
- private:
-  std::atomic<intptr_t> mu_;  // The Mutex state.
-
-  // Post()/Wait() versus associated PerThreadSem; in class for required
-  // friendship with PerThreadSem.
-  static inline void IncrementSynchSem(Mutex *mu,
-                                       base_internal::PerThreadSynch *w);
-  static inline bool DecrementSynchSem(
-      Mutex *mu, base_internal::PerThreadSynch *w,
-      synchronization_internal::KernelTimeout t);
-
-  // slow path acquire
-  void LockSlowLoop(SynchWaitParams *waitp, int flags);
-  // wrappers around LockSlowLoop()
-  bool LockSlowWithDeadline(MuHow how, const Condition *cond,
-                            synchronization_internal::KernelTimeout t,
-                            int flags);
-  void LockSlow(MuHow how, const Condition *cond,
-                int flags) ABSL_ATTRIBUTE_COLD;
-  // slow path release
-  void UnlockSlow(SynchWaitParams *waitp) ABSL_ATTRIBUTE_COLD;
-  // Common code between Await() and AwaitWithTimeout/Deadline()
-  bool AwaitCommon(const Condition &cond,
-                   synchronization_internal::KernelTimeout t);
-  // Attempt to remove thread s from queue.
-  void TryRemove(base_internal::PerThreadSynch *s);
-  // Block a thread on mutex.
-  void Block(base_internal::PerThreadSynch *s);
-  // Wake a thread; return successor.
-  base_internal::PerThreadSynch *Wakeup(base_internal::PerThreadSynch *w);
-
-  friend class CondVar;   // for access to Trans()/Fer().
-  void Trans(MuHow how);  // used for CondVar->Mutex transfer
-  void Fer(
-      base_internal::PerThreadSynch *w);  // used for CondVar->Mutex transfer
-
-  // Catch the error of writing Mutex when intending MutexLock.
-  Mutex(const volatile Mutex * /*ignored*/) {}  // NOLINT(runtime/explicit)
-
-  Mutex(const Mutex&) = delete;
-  Mutex& operator=(const Mutex&) = delete;
-};
-
-// -----------------------------------------------------------------------------
-// Mutex RAII Wrappers
-// -----------------------------------------------------------------------------
-
-// MutexLock
-//
-// `MutexLock` is a helper class, which acquires and releases a `Mutex` via
-// RAII.
-//
-// Example:
-//
-// Class Foo {
-//  public:
-//   Foo::Bar* Baz() {
-//     MutexLock lock(&mu_);
-//     ...
-//     return bar;
-//   }
-//
-// private:
-//   Mutex mu_;
-// };
-class ABSL_SCOPED_LOCKABLE MutexLock {
- public:
-  // Constructors
-
-  // Calls `mu->Lock()` and returns when that call returns. That is, `*mu` is
-  // guaranteed to be locked when this object is constructed. Requires that
-  // `mu` be dereferenceable.
-  explicit MutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) {
-    this->mu_->Lock();
-  }
-
-  // Like above, but calls `mu->LockWhen(cond)` instead. That is, in addition to
-  // the above, the condition given by `cond` is also guaranteed to hold when
-  // this object is constructed.
-  explicit MutexLock(Mutex *mu, const Condition &cond)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
-      : mu_(mu) {
-    this->mu_->LockWhen(cond);
-  }
-
-  MutexLock(const MutexLock &) = delete;  // NOLINT(runtime/mutex)
-  MutexLock(MutexLock&&) = delete;  // NOLINT(runtime/mutex)
-  MutexLock& operator=(const MutexLock&) = delete;
-  MutexLock& operator=(MutexLock&&) = delete;
-
-  ~MutexLock() ABSL_UNLOCK_FUNCTION() { this->mu_->Unlock(); }
-
- private:
-  Mutex *const mu_;
-};
-
-// ReaderMutexLock
-//
-// The `ReaderMutexLock` is a helper class, like `MutexLock`, which acquires and
-// releases a shared lock on a `Mutex` via RAII.
-class ABSL_SCOPED_LOCKABLE ReaderMutexLock {
- public:
-  explicit ReaderMutexLock(Mutex *mu) ABSL_SHARED_LOCK_FUNCTION(mu) : mu_(mu) {
-    mu->ReaderLock();
-  }
-
-  explicit ReaderMutexLock(Mutex *mu, const Condition &cond)
-      ABSL_SHARED_LOCK_FUNCTION(mu)
-      : mu_(mu) {
-    mu->ReaderLockWhen(cond);
-  }
-
-  ReaderMutexLock(const ReaderMutexLock&) = delete;
-  ReaderMutexLock(ReaderMutexLock&&) = delete;
-  ReaderMutexLock& operator=(const ReaderMutexLock&) = delete;
-  ReaderMutexLock& operator=(ReaderMutexLock&&) = delete;
-
-  ~ReaderMutexLock() ABSL_UNLOCK_FUNCTION() { this->mu_->ReaderUnlock(); }
-
- private:
-  Mutex *const mu_;
-};
-
-// WriterMutexLock
-//
-// The `WriterMutexLock` is a helper class, like `MutexLock`, which acquires and
-// releases a write (exclusive) lock on a `Mutex` via RAII.
-class ABSL_SCOPED_LOCKABLE WriterMutexLock {
- public:
-  explicit WriterMutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
-      : mu_(mu) {
-    mu->WriterLock();
-  }
-
-  explicit WriterMutexLock(Mutex *mu, const Condition &cond)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
-      : mu_(mu) {
-    mu->WriterLockWhen(cond);
-  }
-
-  WriterMutexLock(const WriterMutexLock&) = delete;
-  WriterMutexLock(WriterMutexLock&&) = delete;
-  WriterMutexLock& operator=(const WriterMutexLock&) = delete;
-  WriterMutexLock& operator=(WriterMutexLock&&) = delete;
-
-  ~WriterMutexLock() ABSL_UNLOCK_FUNCTION() { this->mu_->WriterUnlock(); }
-
- private:
-  Mutex *const mu_;
-};
-
-// -----------------------------------------------------------------------------
-// Condition
-// -----------------------------------------------------------------------------
-//
-// As noted above, `Mutex` contains a number of member functions which take a
-// `Condition` as an argument; clients can wait for conditions to become `true`
-// before attempting to acquire the mutex. These sections are known as
-// "condition critical" sections. To use a `Condition`, you simply need to
-// construct it, and use within an appropriate `Mutex` member function;
-// everything else in the `Condition` class is an implementation detail.
-//
-// A `Condition` is specified as a function pointer which returns a boolean.
-// `Condition` functions should be pure functions -- their results should depend
-// only on passed arguments, should not consult any external state (such as
-// clocks), and should have no side-effects, aside from debug logging. Any
-// objects that the function may access should be limited to those which are
-// constant while the mutex is blocked on the condition (e.g. a stack variable),
-// or objects of state protected explicitly by the mutex.
-//
-// No matter which construction is used for `Condition`, the underlying
-// function pointer / functor / callable must not throw any
-// exceptions. Correctness of `Mutex` / `Condition` is not guaranteed in
-// the face of a throwing `Condition`. (When Abseil is allowed to depend
-// on C++17, these function pointers will be explicitly marked
-// `noexcept`; until then this requirement cannot be enforced in the
-// type system.)
-//
-// Note: to use a `Condition`, you need only construct it and pass it to a
-// suitable `Mutex' member function, such as `Mutex::Await()`, or to the
-// constructor of one of the scope guard classes.
-//
-// Example using LockWhen/Unlock:
-//
-//   // assume count_ is not internal reference count
-//   int count_ ABSL_GUARDED_BY(mu_);
-//   Condition count_is_zero(+[](int *count) { return *count == 0; }, &count_);
-//
-//   mu_.LockWhen(count_is_zero);
-//   // ...
-//   mu_.Unlock();
-//
-// Example using a scope guard:
-//
-//   {
-//     MutexLock lock(&mu_, count_is_zero);
-//     // ...
-//   }
-//
-// When multiple threads are waiting on exactly the same condition, make sure
-// that they are constructed with the same parameters (same pointer to function
-// + arg, or same pointer to object + method), so that the mutex implementation
-// can avoid redundantly evaluating the same condition for each thread.
-class Condition {
- public:
-  // A Condition that returns the result of "(*func)(arg)"
-  Condition(bool (*func)(void *), void *arg);
-
-  // Templated version for people who are averse to casts.
-  //
-  // To use a lambda, prepend it with unary plus, which converts the lambda
-  // into a function pointer:
-  //     Condition(+[](T* t) { return ...; }, arg).
-  //
-  // Note: lambdas in this case must contain no bound variables.
-  //
-  // See class comment for performance advice.
-  template<typename T>
-  Condition(bool (*func)(T *), T *arg);
-
-  // Templated version for invoking a method that returns a `bool`.
-  //
-  // `Condition(object, &Class::Method)` constructs a `Condition` that evaluates
-  // `object->Method()`.
-  //
-  // Implementation Note: `absl::internal::identity` is used to allow methods to
-  // come from base classes. A simpler signature like
-  // `Condition(T*, bool (T::*)())` does not suffice.
-  template<typename T>
-  Condition(T *object, bool (absl::internal::identity<T>::type::* method)());
-
-  // Same as above, for const members
-  template<typename T>
-  Condition(const T *object,
-            bool (absl::internal::identity<T>::type::* method)() const);
-
-  // A Condition that returns the value of `*cond`
-  explicit Condition(const bool *cond);
-
-  // Templated version for invoking a functor that returns a `bool`.
-  // This approach accepts pointers to non-mutable lambdas, `std::function`,
-  // the result of` std::bind` and user-defined functors that define
-  // `bool F::operator()() const`.
-  //
-  // Example:
-  //
-  //   auto reached = [this, current]() {
-  //     mu_.AssertReaderHeld();                // For annotalysis.
-  //     return processed_ >= current;
-  //   };
-  //   mu_.Await(Condition(&reached));
-  //
-  // NOTE: never use "mu_.AssertHeld()" instead of "mu_.AssertReaderHeld()" in
-  // the lambda as it may be called when the mutex is being unlocked from a
-  // scope holding only a reader lock, which will make the assertion not
-  // fulfilled and crash the binary.
-
-  // See class comment for performance advice. In particular, if there
-  // might be more than one waiter for the same condition, make sure
-  // that all waiters construct the condition with the same pointers.
-
-  // Implementation note: The second template parameter ensures that this
-  // constructor doesn't participate in overload resolution if T doesn't have
-  // `bool operator() const`.
-  template <typename T, typename E = decltype(
-      static_cast<bool (T::*)() const>(&T::operator()))>
-  explicit Condition(const T *obj)
-      : Condition(obj, static_cast<bool (T::*)() const>(&T::operator())) {}
-
-  // A Condition that always returns `true`.
-  static const Condition kTrue;
-
-  // Evaluates the condition.
-  bool Eval() const;
-
-  // Returns `true` if the two conditions are guaranteed to return the same
-  // value if evaluated at the same time, `false` if the evaluation *may* return
-  // different results.
-  //
-  // Two `Condition` values are guaranteed equal if both their `func` and `arg`
-  // components are the same. A null pointer is equivalent to a `true`
-  // condition.
-  static bool GuaranteedEqual(const Condition *a, const Condition *b);
-
- private:
-  typedef bool (*InternalFunctionType)(void * arg);
-  typedef bool (Condition::*InternalMethodType)();
-  typedef bool (*InternalMethodCallerType)(void * arg,
-                                           InternalMethodType internal_method);
-
-  bool (*eval_)(const Condition*);  // Actual evaluator
-  InternalFunctionType function_;   // function taking pointer returning bool
-  InternalMethodType method_;       // method returning bool
-  void *arg_;                       // arg of function_ or object of method_
-
-  Condition();        // null constructor used only to create kTrue
-
-  // Various functions eval_ can point to:
-  static bool CallVoidPtrFunction(const Condition*);
-  template <typename T> static bool CastAndCallFunction(const Condition* c);
-  template <typename T> static bool CastAndCallMethod(const Condition* c);
-};
-
-// -----------------------------------------------------------------------------
-// CondVar
-// -----------------------------------------------------------------------------
-//
-// A condition variable, reflecting state evaluated separately outside of the
-// `Mutex` object, which can be signaled to wake callers.
-// This class is not normally needed; use `Mutex` member functions such as
-// `Mutex::Await()` and intrinsic `Condition` abstractions. In rare cases
-// with many threads and many conditions, `CondVar` may be faster.
-//
-// The implementation may deliver signals to any condition variable at
-// any time, even when no call to `Signal()` or `SignalAll()` is made; as a
-// result, upon being awoken, you must check the logical condition you have
-// been waiting upon.
-//
-// Examples:
-//
-// Usage for a thread waiting for some condition C protected by mutex mu:
-//       mu.Lock();
-//       while (!C) { cv->Wait(&mu); }        // releases and reacquires mu
-//       //  C holds; process data
-//       mu.Unlock();
-//
-// Usage to wake T is:
-//       mu.Lock();
-//      // process data, possibly establishing C
-//      if (C) { cv->Signal(); }
-//      mu.Unlock();
-//
-// If C may be useful to more than one waiter, use `SignalAll()` instead of
-// `Signal()`.
-//
-// With this implementation it is efficient to use `Signal()/SignalAll()` inside
-// the locked region; this usage can make reasoning about your program easier.
-//
-class CondVar {
- public:
-  // A `CondVar` allocated on the heap or on the stack can use the this
-  // constructor.
-  CondVar();
-  ~CondVar();
-
-  // CondVar::Wait()
-  //
-  // Atomically releases a `Mutex` and blocks on this condition variable.
-  // Waits until awakened by a call to `Signal()` or `SignalAll()` (or a
-  // spurious wakeup), then reacquires the `Mutex` and returns.
-  //
-  // Requires and ensures that the current thread holds the `Mutex`.
-  void Wait(Mutex *mu);
-
-  // CondVar::WaitWithTimeout()
-  //
-  // Atomically releases a `Mutex` and blocks on this condition variable.
-  // Waits until awakened by a call to `Signal()` or `SignalAll()` (or a
-  // spurious wakeup), or until the timeout has expired, then reacquires
-  // the `Mutex` and returns.
-  //
-  // Returns true if the timeout has expired without this `CondVar`
-  // being signalled in any manner. If both the timeout has expired
-  // and this `CondVar` has been signalled, the implementation is free
-  // to return `true` or `false`.
-  //
-  // Requires and ensures that the current thread holds the `Mutex`.
-  bool WaitWithTimeout(Mutex *mu, absl::Duration timeout);
-
-  // CondVar::WaitWithDeadline()
-  //
-  // Atomically releases a `Mutex` and blocks on this condition variable.
-  // Waits until awakened by a call to `Signal()` or `SignalAll()` (or a
-  // spurious wakeup), or until the deadline has passed, then reacquires
-  // the `Mutex` and returns.
-  //
-  // Deadlines in the past are equivalent to an immediate deadline.
-  //
-  // Returns true if the deadline has passed without this `CondVar`
-  // being signalled in any manner. If both the deadline has passed
-  // and this `CondVar` has been signalled, the implementation is free
-  // to return `true` or `false`.
-  //
-  // Requires and ensures that the current thread holds the `Mutex`.
-  bool WaitWithDeadline(Mutex *mu, absl::Time deadline);
-
-  // CondVar::Signal()
-  //
-  // Signal this `CondVar`; wake at least one waiter if one exists.
-  void Signal();
-
-  // CondVar::SignalAll()
-  //
-  // Signal this `CondVar`; wake all waiters.
-  void SignalAll();
-
-  // CondVar::EnableDebugLog()
-  //
-  // Causes all subsequent uses of this `CondVar` to be logged via
-  // `ABSL_RAW_LOG(INFO)`. Log entries are tagged with `name` if `name != 0`.
-  // Note: this method substantially reduces `CondVar` performance.
-  void EnableDebugLog(const char *name);
-
- private:
-  bool WaitCommon(Mutex *mutex, synchronization_internal::KernelTimeout t);
-  void Remove(base_internal::PerThreadSynch *s);
-  void Wakeup(base_internal::PerThreadSynch *w);
-  std::atomic<intptr_t> cv_;  // Condition variable state.
-  CondVar(const CondVar&) = delete;
-  CondVar& operator=(const CondVar&) = delete;
-};
-
-
-// Variants of MutexLock.
-//
-// If you find yourself using one of these, consider instead using
-// Mutex::Unlock() and/or if-statements for clarity.
-
-// MutexLockMaybe
-//
-// MutexLockMaybe is like MutexLock, but is a no-op when mu is null.
-class ABSL_SCOPED_LOCKABLE MutexLockMaybe {
- public:
-  explicit MutexLockMaybe(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
-      : mu_(mu) {
-    if (this->mu_ != nullptr) {
-      this->mu_->Lock();
-    }
-  }
-
-  explicit MutexLockMaybe(Mutex *mu, const Condition &cond)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
-      : mu_(mu) {
-    if (this->mu_ != nullptr) {
-      this->mu_->LockWhen(cond);
-    }
-  }
-
-  ~MutexLockMaybe() ABSL_UNLOCK_FUNCTION() {
-    if (this->mu_ != nullptr) { this->mu_->Unlock(); }
-  }
-
- private:
-  Mutex *const mu_;
-  MutexLockMaybe(const MutexLockMaybe&) = delete;
-  MutexLockMaybe(MutexLockMaybe&&) = delete;
-  MutexLockMaybe& operator=(const MutexLockMaybe&) = delete;
-  MutexLockMaybe& operator=(MutexLockMaybe&&) = delete;
-};
-
-// ReleasableMutexLock
-//
-// ReleasableMutexLock is like MutexLock, but permits `Release()` of its
-// mutex before destruction. `Release()` may be called at most once.
-class ABSL_SCOPED_LOCKABLE ReleasableMutexLock {
- public:
-  explicit ReleasableMutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
-      : mu_(mu) {
-    this->mu_->Lock();
-  }
-
-  explicit ReleasableMutexLock(Mutex *mu, const Condition &cond)
-      ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
-      : mu_(mu) {
-    this->mu_->LockWhen(cond);
-  }
-
-  ~ReleasableMutexLock() ABSL_UNLOCK_FUNCTION() {
-    if (this->mu_ != nullptr) { this->mu_->Unlock(); }
-  }
-
-  void Release() ABSL_UNLOCK_FUNCTION();
-
- private:
-  Mutex *mu_;
-  ReleasableMutexLock(const ReleasableMutexLock&) = delete;
-  ReleasableMutexLock(ReleasableMutexLock&&) = delete;
-  ReleasableMutexLock& operator=(const ReleasableMutexLock&) = delete;
-  ReleasableMutexLock& operator=(ReleasableMutexLock&&) = delete;
-};
-
-inline Mutex::Mutex() : mu_(0) {
-  ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static);
-}
-
-inline constexpr Mutex::Mutex(absl::ConstInitType) : mu_(0) {}
-
-inline CondVar::CondVar() : cv_(0) {}
-
-// static
-template <typename T>
-bool Condition::CastAndCallMethod(const Condition *c) {
-  typedef bool (T::*MemberType)();
-  MemberType rm = reinterpret_cast<MemberType>(c->method_);
-  T *x = static_cast<T *>(c->arg_);
-  return (x->*rm)();
-}
-
-// static
-template <typename T>
-bool Condition::CastAndCallFunction(const Condition *c) {
-  typedef bool (*FuncType)(T *);
-  FuncType fn = reinterpret_cast<FuncType>(c->function_);
-  T *x = static_cast<T *>(c->arg_);
-  return (*fn)(x);
-}
-
-template <typename T>
-inline Condition::Condition(bool (*func)(T *), T *arg)
-    : eval_(&CastAndCallFunction<T>),
-      function_(reinterpret_cast<InternalFunctionType>(func)),
-      method_(nullptr),
-      arg_(const_cast<void *>(static_cast<const void *>(arg))) {}
-
-template <typename T>
-inline Condition::Condition(T *object,
-                            bool (absl::internal::identity<T>::type::*method)())
-    : eval_(&CastAndCallMethod<T>),
-      function_(nullptr),
-      method_(reinterpret_cast<InternalMethodType>(method)),
-      arg_(object) {}
-
-template <typename T>
-inline Condition::Condition(const T *object,
-                            bool (absl::internal::identity<T>::type::*method)()
-                                const)
-    : eval_(&CastAndCallMethod<T>),
-      function_(nullptr),
-      method_(reinterpret_cast<InternalMethodType>(method)),
-      arg_(reinterpret_cast<void *>(const_cast<T *>(object))) {}
-
-// Register a hook for profiling support.
-//
-// The function pointer registered here will be called whenever a mutex is
-// contended.  The callback is given the absl/base/cycleclock.h timestamp when
-// waiting began.
-//
-// Calls to this function do not race or block, but there is no ordering
-// guaranteed between calls to this function and call to the provided hook.
-// In particular, the previously registered hook may still be called for some
-// time after this function returns.
-void RegisterMutexProfiler(void (*fn)(int64_t wait_timestamp));
-
-// Register a hook for Mutex tracing.
-//
-// The function pointer registered here will be called whenever a mutex is
-// contended.  The callback is given an opaque handle to the contended mutex,
-// an event name, and the number of wait cycles (as measured by
-// //absl/base/internal/cycleclock.h, and which may not be real
-// "cycle" counts.)
-//
-// The only event name currently sent is "slow release".
-//
-// This has the same memory ordering concerns as RegisterMutexProfiler() above.
-void RegisterMutexTracer(void (*fn)(const char *msg, const void *obj,
-                                    int64_t wait_cycles));
-
-// TODO(gfalcon): Combine RegisterMutexProfiler() and RegisterMutexTracer()
-// into a single interface, since they are only ever called in pairs.
-
-// Register a hook for CondVar tracing.
-//
-// The function pointer registered here will be called here on various CondVar
-// events.  The callback is given an opaque handle to the CondVar object and
-// a string identifying the event.  This is thread-safe, but only a single
-// tracer can be registered.
-//
-// Events that can be sent are "Wait", "Unwait", "Signal wakeup", and
-// "SignalAll wakeup".
-//
-// This has the same memory ordering concerns as RegisterMutexProfiler() above.
-void RegisterCondVarTracer(void (*fn)(const char *msg, const void *cv));
-
-// Register a hook for symbolizing stack traces in deadlock detector reports.
-//
-// 'pc' is the program counter being symbolized, 'out' is the buffer to write
-// into, and 'out_size' is the size of the buffer.  This function can return
-// false if symbolizing failed, or true if a NUL-terminated symbol was written
-// to 'out.'
-//
-// This has the same memory ordering concerns as RegisterMutexProfiler() above.
-//
-// DEPRECATED: The default symbolizer function is absl::Symbolize() and the
-// ability to register a different hook for symbolizing stack traces will be
-// removed on or after 2023-05-01.
-ABSL_DEPRECATED("absl::RegisterSymbolizer() is deprecated and will be removed "
-                "on or after 2023-05-01")
-void RegisterSymbolizer(bool (*fn)(const void *pc, char *out, int out_size));
-
-// EnableMutexInvariantDebugging()
-//
-// Enable or disable global support for Mutex invariant debugging.  If enabled,
-// then invariant predicates can be registered per-Mutex for debug checking.
-// See Mutex::EnableInvariantDebugging().
-void EnableMutexInvariantDebugging(bool enabled);
-
-// When in debug mode, and when the feature has been enabled globally, the
-// implementation will keep track of lock ordering and complain (or optionally
-// crash) if a cycle is detected in the acquired-before graph.
-
-// Possible modes of operation for the deadlock detector in debug mode.
-enum class OnDeadlockCycle {
-  kIgnore,  // Neither report on nor attempt to track cycles in lock ordering
-  kReport,  // Report lock cycles to stderr when detected
-  kAbort,  // Report lock cycles to stderr when detected, then abort
-};
-
-// SetMutexDeadlockDetectionMode()
-//
-// Enable or disable global support for detection of potential deadlocks
-// due to Mutex lock ordering inversions.  When set to 'kIgnore', tracking of
-// lock ordering is disabled.  Otherwise, in debug builds, a lock ordering graph
-// will be maintained internally, and detected cycles will be reported in
-// the manner chosen here.
-void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// In some build configurations we pass --detect-odr-violations to the
-// gold linker.  This causes it to flag weak symbol overrides as ODR
-// violations.  Because ODR only applies to C++ and not C,
-// --detect-odr-violations ignores symbols not mangled with C++ names.
-// By changing our extension points to be extern "C", we dodge this
-// check.
-extern "C" {
-void AbslInternalMutexYield();
-}  // extern "C"
-
-#endif  // ABSL_SYNCHRONIZATION_MUTEX_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/mutex_benchmark.cc b/third_party/abseil_cpp/absl/synchronization/mutex_benchmark.cc
deleted file mode 100644
index 933ea14f8f..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/mutex_benchmark.cc
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cstdint>
-#include <mutex>  // NOLINT(build/c++11)
-#include <vector>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/cycleclock.h"
-#include "absl/base/internal/spinlock.h"
-#include "absl/synchronization/blocking_counter.h"
-#include "absl/synchronization/internal/thread_pool.h"
-#include "absl/synchronization/mutex.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-void BM_Mutex(benchmark::State& state) {
-  static absl::Mutex* mu = new absl::Mutex;
-  for (auto _ : state) {
-    absl::MutexLock lock(mu);
-  }
-}
-BENCHMARK(BM_Mutex)->UseRealTime()->Threads(1)->ThreadPerCpu();
-
-static void DelayNs(int64_t ns, int* data) {
-  int64_t end = absl::base_internal::CycleClock::Now() +
-                ns * absl::base_internal::CycleClock::Frequency() / 1e9;
-  while (absl::base_internal::CycleClock::Now() < end) {
-    ++(*data);
-    benchmark::DoNotOptimize(*data);
-  }
-}
-
-template <typename MutexType>
-class RaiiLocker {
- public:
-  explicit RaiiLocker(MutexType* mu) : mu_(mu) { mu_->Lock(); }
-  ~RaiiLocker() { mu_->Unlock(); }
- private:
-  MutexType* mu_;
-};
-
-template <>
-class RaiiLocker<std::mutex> {
- public:
-  explicit RaiiLocker(std::mutex* mu) : mu_(mu) { mu_->lock(); }
-  ~RaiiLocker() { mu_->unlock(); }
- private:
-  std::mutex* mu_;
-};
-
-template <typename MutexType>
-void BM_Contended(benchmark::State& state) {
-  struct Shared {
-    MutexType mu;
-    int data = 0;
-  };
-  static auto* shared = new Shared;
-  int local = 0;
-  for (auto _ : state) {
-    // Here we model both local work outside of the critical section as well as
-    // some work inside of the critical section. The idea is to capture some
-    // more or less realisitic contention levels.
-    // If contention is too low, the benchmark won't measure anything useful.
-    // If contention is unrealistically high, the benchmark will favor
-    // bad mutex implementations that block and otherwise distract threads
-    // from the mutex and shared state for as much as possible.
-    // To achieve this amount of local work is multiplied by number of threads
-    // to keep ratio between local work and critical section approximately
-    // equal regardless of number of threads.
-    DelayNs(100 * state.threads, &local);
-    RaiiLocker<MutexType> locker(&shared->mu);
-    DelayNs(state.range(0), &shared->data);
-  }
-}
-
-BENCHMARK_TEMPLATE(BM_Contended, absl::Mutex)
-    ->UseRealTime()
-    // ThreadPerCpu poorly handles non-power-of-two CPU counts.
-    ->Threads(1)
-    ->Threads(2)
-    ->Threads(4)
-    ->Threads(6)
-    ->Threads(8)
-    ->Threads(12)
-    ->Threads(16)
-    ->Threads(24)
-    ->Threads(32)
-    ->Threads(48)
-    ->Threads(64)
-    ->Threads(96)
-    ->Threads(128)
-    ->Threads(192)
-    ->Threads(256)
-    // Some empirically chosen amounts of work in critical section.
-    // 1 is low contention, 200 is high contention and few values in between.
-    ->Arg(1)
-    ->Arg(20)
-    ->Arg(50)
-    ->Arg(200);
-
-BENCHMARK_TEMPLATE(BM_Contended, absl::base_internal::SpinLock)
-    ->UseRealTime()
-    // ThreadPerCpu poorly handles non-power-of-two CPU counts.
-    ->Threads(1)
-    ->Threads(2)
-    ->Threads(4)
-    ->Threads(6)
-    ->Threads(8)
-    ->Threads(12)
-    ->Threads(16)
-    ->Threads(24)
-    ->Threads(32)
-    ->Threads(48)
-    ->Threads(64)
-    ->Threads(96)
-    ->Threads(128)
-    ->Threads(192)
-    ->Threads(256)
-    // Some empirically chosen amounts of work in critical section.
-    // 1 is low contention, 200 is high contention and few values in between.
-    ->Arg(1)
-    ->Arg(20)
-    ->Arg(50)
-    ->Arg(200);
-
-BENCHMARK_TEMPLATE(BM_Contended, std::mutex)
-    ->UseRealTime()
-    // ThreadPerCpu poorly handles non-power-of-two CPU counts.
-    ->Threads(1)
-    ->Threads(2)
-    ->Threads(4)
-    ->Threads(6)
-    ->Threads(8)
-    ->Threads(12)
-    ->Threads(16)
-    ->Threads(24)
-    ->Threads(32)
-    ->Threads(48)
-    ->Threads(64)
-    ->Threads(96)
-    ->Threads(128)
-    ->Threads(192)
-    ->Threads(256)
-    // Some empirically chosen amounts of work in critical section.
-    // 1 is low contention, 200 is high contention and few values in between.
-    ->Arg(1)
-    ->Arg(20)
-    ->Arg(50)
-    ->Arg(200);
-
-// Measure the overhead of conditions on mutex release (when they must be
-// evaluated).  Mutex has (some) support for equivalence classes allowing
-// Conditions with the same function/argument to potentially not be multiply
-// evaluated.
-//
-// num_classes==0 is used for the special case of every waiter being distinct.
-void BM_ConditionWaiters(benchmark::State& state) {
-  int num_classes = state.range(0);
-  int num_waiters = state.range(1);
-
-  struct Helper {
-    static void Waiter(absl::BlockingCounter* init, absl::Mutex* m, int* p) {
-      init->DecrementCount();
-      m->LockWhen(absl::Condition(
-          static_cast<bool (*)(int*)>([](int* v) { return *v == 0; }), p));
-      m->Unlock();
-    }
-  };
-
-  if (num_classes == 0) {
-    // No equivalence classes.
-    num_classes = num_waiters;
-  }
-
-  absl::BlockingCounter init(num_waiters);
-  absl::Mutex mu;
-  std::vector<int> equivalence_classes(num_classes, 1);
-
-  // Must be declared last to be destroyed first.
-  absl::synchronization_internal::ThreadPool pool(num_waiters);
-
-  for (int i = 0; i < num_waiters; i++) {
-    // Mutex considers Conditions with the same function and argument
-    // to be equivalent.
-    pool.Schedule([&, i] {
-      Helper::Waiter(&init, &mu, &equivalence_classes[i % num_classes]);
-    });
-  }
-  init.Wait();
-
-  for (auto _ : state) {
-    mu.Lock();
-    mu.Unlock();  // Each unlock requires Condition evaluation for our waiters.
-  }
-
-  mu.Lock();
-  for (int i = 0; i < num_classes; i++) {
-    equivalence_classes[i] = 0;
-  }
-  mu.Unlock();
-}
-
-// Some configurations have higher thread limits than others.
-#if defined(__linux__) && !defined(ABSL_HAVE_THREAD_SANITIZER)
-constexpr int kMaxConditionWaiters = 8192;
-#else
-constexpr int kMaxConditionWaiters = 1024;
-#endif
-BENCHMARK(BM_ConditionWaiters)->RangePair(0, 2, 1, kMaxConditionWaiters);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/synchronization/mutex_test.cc b/third_party/abseil_cpp/absl/synchronization/mutex_test.cc
deleted file mode 100644
index 058f757b48..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/mutex_test.cc
+++ /dev/null
@@ -1,1706 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/mutex.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-#include <algorithm>
-#include <atomic>
-#include <cstdlib>
-#include <functional>
-#include <memory>
-#include <random>
-#include <string>
-#include <thread>  // NOLINT(build/c++11)
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/internal/sysinfo.h"
-#include "absl/memory/memory.h"
-#include "absl/synchronization/internal/thread_pool.h"
-#include "absl/time/clock.h"
-#include "absl/time/time.h"
-
-namespace {
-
-// TODO(dmauro): Replace with a commandline flag.
-static constexpr bool kExtendedTest = false;
-
-std::unique_ptr<absl::synchronization_internal::ThreadPool> CreatePool(
-    int threads) {
-  return absl::make_unique<absl::synchronization_internal::ThreadPool>(threads);
-}
-
-std::unique_ptr<absl::synchronization_internal::ThreadPool>
-CreateDefaultPool() {
-  return CreatePool(kExtendedTest ? 32 : 10);
-}
-
-// Hack to schedule a function to run on a thread pool thread after a
-// duration has elapsed.
-static void ScheduleAfter(absl::synchronization_internal::ThreadPool *tp,
-                          absl::Duration after,
-                          const std::function<void()> &func) {
-  tp->Schedule([func, after] {
-    absl::SleepFor(after);
-    func();
-  });
-}
-
-struct TestContext {
-  int iterations;
-  int threads;
-  int g0;  // global 0
-  int g1;  // global 1
-  absl::Mutex mu;
-  absl::CondVar cv;
-};
-
-// To test whether the invariant check call occurs
-static std::atomic<bool> invariant_checked;
-
-static bool GetInvariantChecked() {
-  return invariant_checked.load(std::memory_order_relaxed);
-}
-
-static void SetInvariantChecked(bool new_value) {
-  invariant_checked.store(new_value, std::memory_order_relaxed);
-}
-
-static void CheckSumG0G1(void *v) {
-  TestContext *cxt = static_cast<TestContext *>(v);
-  ABSL_RAW_CHECK(cxt->g0 == -cxt->g1, "Error in CheckSumG0G1");
-  SetInvariantChecked(true);
-}
-
-static void TestMu(TestContext *cxt, int c) {
-  for (int i = 0; i != cxt->iterations; i++) {
-    absl::MutexLock l(&cxt->mu);
-    int a = cxt->g0 + 1;
-    cxt->g0 = a;
-    cxt->g1--;
-  }
-}
-
-static void TestTry(TestContext *cxt, int c) {
-  for (int i = 0; i != cxt->iterations; i++) {
-    do {
-      std::this_thread::yield();
-    } while (!cxt->mu.TryLock());
-    int a = cxt->g0 + 1;
-    cxt->g0 = a;
-    cxt->g1--;
-    cxt->mu.Unlock();
-  }
-}
-
-static void TestR20ms(TestContext *cxt, int c) {
-  for (int i = 0; i != cxt->iterations; i++) {
-    absl::ReaderMutexLock l(&cxt->mu);
-    absl::SleepFor(absl::Milliseconds(20));
-    cxt->mu.AssertReaderHeld();
-  }
-}
-
-static void TestRW(TestContext *cxt, int c) {
-  if ((c & 1) == 0) {
-    for (int i = 0; i != cxt->iterations; i++) {
-      absl::WriterMutexLock l(&cxt->mu);
-      cxt->g0++;
-      cxt->g1--;
-      cxt->mu.AssertHeld();
-      cxt->mu.AssertReaderHeld();
-    }
-  } else {
-    for (int i = 0; i != cxt->iterations; i++) {
-      absl::ReaderMutexLock l(&cxt->mu);
-      ABSL_RAW_CHECK(cxt->g0 == -cxt->g1, "Error in TestRW");
-      cxt->mu.AssertReaderHeld();
-    }
-  }
-}
-
-struct MyContext {
-  int target;
-  TestContext *cxt;
-  bool MyTurn();
-};
-
-bool MyContext::MyTurn() {
-  TestContext *cxt = this->cxt;
-  return cxt->g0 == this->target || cxt->g0 == cxt->iterations;
-}
-
-static void TestAwait(TestContext *cxt, int c) {
-  MyContext mc;
-  mc.target = c;
-  mc.cxt = cxt;
-  absl::MutexLock l(&cxt->mu);
-  cxt->mu.AssertHeld();
-  while (cxt->g0 < cxt->iterations) {
-    cxt->mu.Await(absl::Condition(&mc, &MyContext::MyTurn));
-    ABSL_RAW_CHECK(mc.MyTurn(), "Error in TestAwait");
-    cxt->mu.AssertHeld();
-    if (cxt->g0 < cxt->iterations) {
-      int a = cxt->g0 + 1;
-      cxt->g0 = a;
-      mc.target += cxt->threads;
-    }
-  }
-}
-
-static void TestSignalAll(TestContext *cxt, int c) {
-  int target = c;
-  absl::MutexLock l(&cxt->mu);
-  cxt->mu.AssertHeld();
-  while (cxt->g0 < cxt->iterations) {
-    while (cxt->g0 != target && cxt->g0 != cxt->iterations) {
-      cxt->cv.Wait(&cxt->mu);
-    }
-    if (cxt->g0 < cxt->iterations) {
-      int a = cxt->g0 + 1;
-      cxt->g0 = a;
-      cxt->cv.SignalAll();
-      target += cxt->threads;
-    }
-  }
-}
-
-static void TestSignal(TestContext *cxt, int c) {
-  ABSL_RAW_CHECK(cxt->threads == 2, "TestSignal should use 2 threads");
-  int target = c;
-  absl::MutexLock l(&cxt->mu);
-  cxt->mu.AssertHeld();
-  while (cxt->g0 < cxt->iterations) {
-    while (cxt->g0 != target && cxt->g0 != cxt->iterations) {
-      cxt->cv.Wait(&cxt->mu);
-    }
-    if (cxt->g0 < cxt->iterations) {
-      int a = cxt->g0 + 1;
-      cxt->g0 = a;
-      cxt->cv.Signal();
-      target += cxt->threads;
-    }
-  }
-}
-
-static void TestCVTimeout(TestContext *cxt, int c) {
-  int target = c;
-  absl::MutexLock l(&cxt->mu);
-  cxt->mu.AssertHeld();
-  while (cxt->g0 < cxt->iterations) {
-    while (cxt->g0 != target && cxt->g0 != cxt->iterations) {
-      cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(100));
-    }
-    if (cxt->g0 < cxt->iterations) {
-      int a = cxt->g0 + 1;
-      cxt->g0 = a;
-      cxt->cv.SignalAll();
-      target += cxt->threads;
-    }
-  }
-}
-
-static bool G0GE2(TestContext *cxt) { return cxt->g0 >= 2; }
-
-static void TestTime(TestContext *cxt, int c, bool use_cv) {
-  ABSL_RAW_CHECK(cxt->iterations == 1, "TestTime should only use 1 iteration");
-  ABSL_RAW_CHECK(cxt->threads > 2, "TestTime should use more than 2 threads");
-  const bool kFalse = false;
-  absl::Condition false_cond(&kFalse);
-  absl::Condition g0ge2(G0GE2, cxt);
-  if (c == 0) {
-    absl::MutexLock l(&cxt->mu);
-
-    absl::Time start = absl::Now();
-    if (use_cv) {
-      cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1));
-    } else {
-      ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)),
-                     "TestTime failed");
-    }
-    absl::Duration elapsed = absl::Now() - start;
-    ABSL_RAW_CHECK(
-        absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0),
-        "TestTime failed");
-    ABSL_RAW_CHECK(cxt->g0 == 1, "TestTime failed");
-
-    start = absl::Now();
-    if (use_cv) {
-      cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1));
-    } else {
-      ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)),
-                     "TestTime failed");
-    }
-    elapsed = absl::Now() - start;
-    ABSL_RAW_CHECK(
-        absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0),
-        "TestTime failed");
-    cxt->g0++;
-    if (use_cv) {
-      cxt->cv.Signal();
-    }
-
-    start = absl::Now();
-    if (use_cv) {
-      cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(4));
-    } else {
-      ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(4)),
-                     "TestTime failed");
-    }
-    elapsed = absl::Now() - start;
-    ABSL_RAW_CHECK(
-        absl::Seconds(3.9) <= elapsed && elapsed <= absl::Seconds(6.0),
-        "TestTime failed");
-    ABSL_RAW_CHECK(cxt->g0 >= 3, "TestTime failed");
-
-    start = absl::Now();
-    if (use_cv) {
-      cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1));
-    } else {
-      ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)),
-                     "TestTime failed");
-    }
-    elapsed = absl::Now() - start;
-    ABSL_RAW_CHECK(
-        absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0),
-        "TestTime failed");
-    if (use_cv) {
-      cxt->cv.SignalAll();
-    }
-
-    start = absl::Now();
-    if (use_cv) {
-      cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1));
-    } else {
-      ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)),
-                     "TestTime failed");
-    }
-    elapsed = absl::Now() - start;
-    ABSL_RAW_CHECK(absl::Seconds(0.9) <= elapsed &&
-                   elapsed <= absl::Seconds(2.0), "TestTime failed");
-    ABSL_RAW_CHECK(cxt->g0 == cxt->threads, "TestTime failed");
-
-  } else if (c == 1) {
-    absl::MutexLock l(&cxt->mu);
-    const absl::Time start = absl::Now();
-    if (use_cv) {
-      cxt->cv.WaitWithTimeout(&cxt->mu, absl::Milliseconds(500));
-    } else {
-      ABSL_RAW_CHECK(
-          !cxt->mu.AwaitWithTimeout(false_cond, absl::Milliseconds(500)),
-          "TestTime failed");
-    }
-    const absl::Duration elapsed = absl::Now() - start;
-    ABSL_RAW_CHECK(
-        absl::Seconds(0.4) <= elapsed && elapsed <= absl::Seconds(0.9),
-        "TestTime failed");
-    cxt->g0++;
-  } else if (c == 2) {
-    absl::MutexLock l(&cxt->mu);
-    if (use_cv) {
-      while (cxt->g0 < 2) {
-        cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(100));
-      }
-    } else {
-      ABSL_RAW_CHECK(cxt->mu.AwaitWithTimeout(g0ge2, absl::Seconds(100)),
-                     "TestTime failed");
-    }
-    cxt->g0++;
-  } else {
-    absl::MutexLock l(&cxt->mu);
-    if (use_cv) {
-      while (cxt->g0 < 2) {
-        cxt->cv.Wait(&cxt->mu);
-      }
-    } else {
-      cxt->mu.Await(g0ge2);
-    }
-    cxt->g0++;
-  }
-}
-
-static void TestMuTime(TestContext *cxt, int c) { TestTime(cxt, c, false); }
-
-static void TestCVTime(TestContext *cxt, int c) { TestTime(cxt, c, true); }
-
-static void EndTest(int *c0, int *c1, absl::Mutex *mu, absl::CondVar *cv,
-                    const std::function<void(int)>& cb) {
-  mu->Lock();
-  int c = (*c0)++;
-  mu->Unlock();
-  cb(c);
-  absl::MutexLock l(mu);
-  (*c1)++;
-  cv->Signal();
-}
-
-// Code common to RunTest() and RunTestWithInvariantDebugging().
-static int RunTestCommon(TestContext *cxt, void (*test)(TestContext *cxt, int),
-                         int threads, int iterations, int operations) {
-  absl::Mutex mu2;
-  absl::CondVar cv2;
-  int c0 = 0;
-  int c1 = 0;
-  cxt->g0 = 0;
-  cxt->g1 = 0;
-  cxt->iterations = iterations;
-  cxt->threads = threads;
-  absl::synchronization_internal::ThreadPool tp(threads);
-  for (int i = 0; i != threads; i++) {
-    tp.Schedule(std::bind(&EndTest, &c0, &c1, &mu2, &cv2,
-                          std::function<void(int)>(
-                              std::bind(test, cxt, std::placeholders::_1))));
-  }
-  mu2.Lock();
-  while (c1 != threads) {
-    cv2.Wait(&mu2);
-  }
-  mu2.Unlock();
-  return cxt->g0;
-}
-
-// Basis for the parameterized tests configured below.
-static int RunTest(void (*test)(TestContext *cxt, int), int threads,
-                   int iterations, int operations) {
-  TestContext cxt;
-  return RunTestCommon(&cxt, test, threads, iterations, operations);
-}
-
-// Like RunTest(), but sets an invariant on the tested Mutex and
-// verifies that the invariant check happened. The invariant function
-// will be passed the TestContext* as its arg and must call
-// SetInvariantChecked(true);
-#if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED)
-static int RunTestWithInvariantDebugging(void (*test)(TestContext *cxt, int),
-                                         int threads, int iterations,
-                                         int operations,
-                                         void (*invariant)(void *)) {
-  absl::EnableMutexInvariantDebugging(true);
-  SetInvariantChecked(false);
-  TestContext cxt;
-  cxt.mu.EnableInvariantDebugging(invariant, &cxt);
-  int ret = RunTestCommon(&cxt, test, threads, iterations, operations);
-  ABSL_RAW_CHECK(GetInvariantChecked(), "Invariant not checked");
-  absl::EnableMutexInvariantDebugging(false);  // Restore.
-  return ret;
-}
-#endif
-
-// --------------------------------------------------------
-// Test for fix of bug in TryRemove()
-struct TimeoutBugStruct {
-  absl::Mutex mu;
-  bool a;
-  int a_waiter_count;
-};
-
-static void WaitForA(TimeoutBugStruct *x) {
-  x->mu.LockWhen(absl::Condition(&x->a));
-  x->a_waiter_count--;
-  x->mu.Unlock();
-}
-
-static bool NoAWaiters(TimeoutBugStruct *x) { return x->a_waiter_count == 0; }
-
-// Test that a CondVar.Wait(&mutex) can un-block a call to mutex.Await() in
-// another thread.
-TEST(Mutex, CondVarWaitSignalsAwait) {
-  // Use a struct so the lock annotations apply.
-  struct {
-    absl::Mutex barrier_mu;
-    bool barrier ABSL_GUARDED_BY(barrier_mu) = false;
-
-    absl::Mutex release_mu;
-    bool release ABSL_GUARDED_BY(release_mu) = false;
-    absl::CondVar released_cv;
-  } state;
-
-  auto pool = CreateDefaultPool();
-
-  // Thread A.  Sets barrier, waits for release using Mutex::Await, then
-  // signals released_cv.
-  pool->Schedule([&state] {
-    state.release_mu.Lock();
-
-    state.barrier_mu.Lock();
-    state.barrier = true;
-    state.barrier_mu.Unlock();
-
-    state.release_mu.Await(absl::Condition(&state.release));
-    state.released_cv.Signal();
-    state.release_mu.Unlock();
-  });
-
-  state.barrier_mu.LockWhen(absl::Condition(&state.barrier));
-  state.barrier_mu.Unlock();
-  state.release_mu.Lock();
-  // Thread A is now blocked on release by way of Mutex::Await().
-
-  // Set release.  Calling released_cv.Wait() should un-block thread A,
-  // which will signal released_cv.  If not, the test will hang.
-  state.release = true;
-  state.released_cv.Wait(&state.release_mu);
-  state.release_mu.Unlock();
-}
-
-// Test that a CondVar.WaitWithTimeout(&mutex) can un-block a call to
-// mutex.Await() in another thread.
-TEST(Mutex, CondVarWaitWithTimeoutSignalsAwait) {
-  // Use a struct so the lock annotations apply.
-  struct {
-    absl::Mutex barrier_mu;
-    bool barrier ABSL_GUARDED_BY(barrier_mu) = false;
-
-    absl::Mutex release_mu;
-    bool release ABSL_GUARDED_BY(release_mu) = false;
-    absl::CondVar released_cv;
-  } state;
-
-  auto pool = CreateDefaultPool();
-
-  // Thread A.  Sets barrier, waits for release using Mutex::Await, then
-  // signals released_cv.
-  pool->Schedule([&state] {
-    state.release_mu.Lock();
-
-    state.barrier_mu.Lock();
-    state.barrier = true;
-    state.barrier_mu.Unlock();
-
-    state.release_mu.Await(absl::Condition(&state.release));
-    state.released_cv.Signal();
-    state.release_mu.Unlock();
-  });
-
-  state.barrier_mu.LockWhen(absl::Condition(&state.barrier));
-  state.barrier_mu.Unlock();
-  state.release_mu.Lock();
-  // Thread A is now blocked on release by way of Mutex::Await().
-
-  // Set release.  Calling released_cv.Wait() should un-block thread A,
-  // which will signal released_cv.  If not, the test will hang.
-  state.release = true;
-  EXPECT_TRUE(
-      !state.released_cv.WaitWithTimeout(&state.release_mu, absl::Seconds(10)))
-      << "; Unrecoverable test failure: CondVar::WaitWithTimeout did not "
-         "unblock the absl::Mutex::Await call in another thread.";
-
-  state.release_mu.Unlock();
-}
-
-// Test for regression of a bug in loop of TryRemove()
-TEST(Mutex, MutexTimeoutBug) {
-  auto tp = CreateDefaultPool();
-
-  TimeoutBugStruct x;
-  x.a = false;
-  x.a_waiter_count = 2;
-  tp->Schedule(std::bind(&WaitForA, &x));
-  tp->Schedule(std::bind(&WaitForA, &x));
-  absl::SleepFor(absl::Seconds(1));  // Allow first two threads to hang.
-  // The skip field of the second will point to the first because there are
-  // only two.
-
-  // Now cause a thread waiting on an always-false to time out
-  // This would deadlock when the bug was present.
-  bool always_false = false;
-  x.mu.LockWhenWithTimeout(absl::Condition(&always_false),
-                           absl::Milliseconds(500));
-
-  // if we get here, the bug is not present.   Cleanup the state.
-
-  x.a = true;                                    // wakeup the two waiters on A
-  x.mu.Await(absl::Condition(&NoAWaiters, &x));  // wait for them to exit
-  x.mu.Unlock();
-}
-
-struct CondVarWaitDeadlock : testing::TestWithParam<int> {
-  absl::Mutex mu;
-  absl::CondVar cv;
-  bool cond1 = false;
-  bool cond2 = false;
-  bool read_lock1;
-  bool read_lock2;
-  bool signal_unlocked;
-
-  CondVarWaitDeadlock() {
-    read_lock1 = GetParam() & (1 << 0);
-    read_lock2 = GetParam() & (1 << 1);
-    signal_unlocked = GetParam() & (1 << 2);
-  }
-
-  void Waiter1() {
-    if (read_lock1) {
-      mu.ReaderLock();
-      while (!cond1) {
-        cv.Wait(&mu);
-      }
-      mu.ReaderUnlock();
-    } else {
-      mu.Lock();
-      while (!cond1) {
-        cv.Wait(&mu);
-      }
-      mu.Unlock();
-    }
-  }
-
-  void Waiter2() {
-    if (read_lock2) {
-      mu.ReaderLockWhen(absl::Condition(&cond2));
-      mu.ReaderUnlock();
-    } else {
-      mu.LockWhen(absl::Condition(&cond2));
-      mu.Unlock();
-    }
-  }
-};
-
-// Test for a deadlock bug in Mutex::Fer().
-// The sequence of events that lead to the deadlock is:
-// 1. waiter1 blocks on cv in read mode (mu bits = 0).
-// 2. waiter2 blocks on mu in either mode (mu bits = kMuWait).
-// 3. main thread locks mu, sets cond1, unlocks mu (mu bits = kMuWait).
-// 4. main thread signals on cv and this eventually calls Mutex::Fer().
-// Currently Fer wakes waiter1 since mu bits = kMuWait (mutex is unlocked).
-// Before the bug fix Fer neither woke waiter1 nor queued it on mutex,
-// which resulted in deadlock.
-TEST_P(CondVarWaitDeadlock, Test) {
-  auto waiter1 = CreatePool(1);
-  auto waiter2 = CreatePool(1);
-  waiter1->Schedule([this] { this->Waiter1(); });
-  waiter2->Schedule([this] { this->Waiter2(); });
-
-  // Wait while threads block (best-effort is fine).
-  absl::SleepFor(absl::Milliseconds(100));
-
-  // Wake condwaiter.
-  mu.Lock();
-  cond1 = true;
-  if (signal_unlocked) {
-    mu.Unlock();
-    cv.Signal();
-  } else {
-    cv.Signal();
-    mu.Unlock();
-  }
-  waiter1.reset();  // "join" waiter1
-
-  // Wake waiter.
-  mu.Lock();
-  cond2 = true;
-  mu.Unlock();
-  waiter2.reset();  // "join" waiter2
-}
-
-INSTANTIATE_TEST_SUITE_P(CondVarWaitDeadlockTest, CondVarWaitDeadlock,
-                         ::testing::Range(0, 8),
-                         ::testing::PrintToStringParamName());
-
-// --------------------------------------------------------
-// Test for fix of bug in DequeueAllWakeable()
-// Bug was that if there was more than one waiting reader
-// and all should be woken, the most recently blocked one
-// would not be.
-
-struct DequeueAllWakeableBugStruct {
-  absl::Mutex mu;
-  absl::Mutex mu2;       // protects all fields below
-  int unfinished_count;  // count of unfinished readers; under mu2
-  bool done1;            // unfinished_count == 0; under mu2
-  int finished_count;    // count of finished readers, under mu2
-  bool done2;            // finished_count == 0; under mu2
-};
-
-// Test for regression of a bug in loop of DequeueAllWakeable()
-static void AcquireAsReader(DequeueAllWakeableBugStruct *x) {
-  x->mu.ReaderLock();
-  x->mu2.Lock();
-  x->unfinished_count--;
-  x->done1 = (x->unfinished_count == 0);
-  x->mu2.Unlock();
-  // make sure that both readers acquired mu before we release it.
-  absl::SleepFor(absl::Seconds(2));
-  x->mu.ReaderUnlock();
-
-  x->mu2.Lock();
-  x->finished_count--;
-  x->done2 = (x->finished_count == 0);
-  x->mu2.Unlock();
-}
-
-// Test for regression of a bug in loop of DequeueAllWakeable()
-TEST(Mutex, MutexReaderWakeupBug) {
-  auto tp = CreateDefaultPool();
-
-  DequeueAllWakeableBugStruct x;
-  x.unfinished_count = 2;
-  x.done1 = false;
-  x.finished_count = 2;
-  x.done2 = false;
-  x.mu.Lock();  // acquire mu exclusively
-  // queue two thread that will block on reader locks on x.mu
-  tp->Schedule(std::bind(&AcquireAsReader, &x));
-  tp->Schedule(std::bind(&AcquireAsReader, &x));
-  absl::SleepFor(absl::Seconds(1));  // give time for reader threads to block
-  x.mu.Unlock();                     // wake them up
-
-  // both readers should finish promptly
-  EXPECT_TRUE(
-      x.mu2.LockWhenWithTimeout(absl::Condition(&x.done1), absl::Seconds(10)));
-  x.mu2.Unlock();
-
-  EXPECT_TRUE(
-      x.mu2.LockWhenWithTimeout(absl::Condition(&x.done2), absl::Seconds(10)));
-  x.mu2.Unlock();
-}
-
-struct LockWhenTestStruct {
-  absl::Mutex mu1;
-  bool cond = false;
-
-  absl::Mutex mu2;
-  bool waiting = false;
-};
-
-static bool LockWhenTestIsCond(LockWhenTestStruct* s) {
-  s->mu2.Lock();
-  s->waiting = true;
-  s->mu2.Unlock();
-  return s->cond;
-}
-
-static void LockWhenTestWaitForIsCond(LockWhenTestStruct* s) {
-  s->mu1.LockWhen(absl::Condition(&LockWhenTestIsCond, s));
-  s->mu1.Unlock();
-}
-
-TEST(Mutex, LockWhen) {
-  LockWhenTestStruct s;
-
-  std::thread t(LockWhenTestWaitForIsCond, &s);
-  s.mu2.LockWhen(absl::Condition(&s.waiting));
-  s.mu2.Unlock();
-
-  s.mu1.Lock();
-  s.cond = true;
-  s.mu1.Unlock();
-
-  t.join();
-}
-
-TEST(Mutex, LockWhenGuard) {
-  absl::Mutex mu;
-  int n = 30;
-  bool done = false;
-
-  // We don't inline the lambda because the conversion is ambiguous in MSVC.
-  bool (*cond_eq_10)(int *) = [](int *p) { return *p == 10; };
-  bool (*cond_lt_10)(int *) = [](int *p) { return *p < 10; };
-
-  std::thread t1([&mu, &n, &done, cond_eq_10]() {
-    absl::ReaderMutexLock lock(&mu, absl::Condition(cond_eq_10, &n));
-    done = true;
-  });
-
-  std::thread t2[10];
-  for (std::thread &t : t2) {
-    t = std::thread([&mu, &n, cond_lt_10]() {
-      absl::WriterMutexLock lock(&mu, absl::Condition(cond_lt_10, &n));
-      ++n;
-    });
-  }
-
-  {
-    absl::MutexLock lock(&mu);
-    n = 0;
-  }
-
-  for (std::thread &t : t2) t.join();
-  t1.join();
-
-  EXPECT_TRUE(done);
-  EXPECT_EQ(n, 10);
-}
-
-// --------------------------------------------------------
-// The following test requires Mutex::ReaderLock to be a real shared
-// lock, which is not the case in all builds.
-#if !defined(ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE)
-
-// Test for fix of bug in UnlockSlow() that incorrectly decremented the reader
-// count when putting a thread to sleep waiting for a false condition when the
-// lock was not held.
-
-// For this bug to strike, we make a thread wait on a free mutex with no
-// waiters by causing its wakeup condition to be false.   Then the
-// next two acquirers must be readers.   The bug causes the lock
-// to be released when one reader unlocks, rather than both.
-
-struct ReaderDecrementBugStruct {
-  bool cond;  // to delay first thread (under mu)
-  int done;   // reference count (under mu)
-  absl::Mutex mu;
-
-  bool waiting_on_cond;   // under mu2
-  bool have_reader_lock;  // under mu2
-  bool complete;          // under mu2
-  absl::Mutex mu2;        // > mu
-};
-
-// L >= mu, L < mu_waiting_on_cond
-static bool IsCond(void *v) {
-  ReaderDecrementBugStruct *x = reinterpret_cast<ReaderDecrementBugStruct *>(v);
-  x->mu2.Lock();
-  x->waiting_on_cond = true;
-  x->mu2.Unlock();
-  return x->cond;
-}
-
-// L >= mu
-static bool AllDone(void *v) {
-  ReaderDecrementBugStruct *x = reinterpret_cast<ReaderDecrementBugStruct *>(v);
-  return x->done == 0;
-}
-
-// L={}
-static void WaitForCond(ReaderDecrementBugStruct *x) {
-  absl::Mutex dummy;
-  absl::MutexLock l(&dummy);
-  x->mu.LockWhen(absl::Condition(&IsCond, x));
-  x->done--;
-  x->mu.Unlock();
-}
-
-// L={}
-static void GetReadLock(ReaderDecrementBugStruct *x) {
-  x->mu.ReaderLock();
-  x->mu2.Lock();
-  x->have_reader_lock = true;
-  x->mu2.Await(absl::Condition(&x->complete));
-  x->mu2.Unlock();
-  x->mu.ReaderUnlock();
-  x->mu.Lock();
-  x->done--;
-  x->mu.Unlock();
-}
-
-// Test for reader counter being decremented incorrectly by waiter
-// with false condition.
-TEST(Mutex, MutexReaderDecrementBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-  ReaderDecrementBugStruct x;
-  x.cond = false;
-  x.waiting_on_cond = false;
-  x.have_reader_lock = false;
-  x.complete = false;
-  x.done = 2;  // initial ref count
-
-  // Run WaitForCond() and wait for it to sleep
-  std::thread thread1(WaitForCond, &x);
-  x.mu2.LockWhen(absl::Condition(&x.waiting_on_cond));
-  x.mu2.Unlock();
-
-  // Run GetReadLock(), and wait for it to get the read lock
-  std::thread thread2(GetReadLock, &x);
-  x.mu2.LockWhen(absl::Condition(&x.have_reader_lock));
-  x.mu2.Unlock();
-
-  // Get the reader lock ourselves, and release it.
-  x.mu.ReaderLock();
-  x.mu.ReaderUnlock();
-
-  // The lock should be held in read mode by GetReadLock().
-  // If we have the bug, the lock will be free.
-  x.mu.AssertReaderHeld();
-
-  // Wake up all the threads.
-  x.mu2.Lock();
-  x.complete = true;
-  x.mu2.Unlock();
-
-  // TODO(delesley): turn on analysis once lock upgrading is supported.
-  // (This call upgrades the lock from shared to exclusive.)
-  x.mu.Lock();
-  x.cond = true;
-  x.mu.Await(absl::Condition(&AllDone, &x));
-  x.mu.Unlock();
-
-  thread1.join();
-  thread2.join();
-}
-#endif  // !ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE
-
-// Test that we correctly handle the situation when a lock is
-// held and then destroyed (w/o unlocking).
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-// TSAN reports errors when locked Mutexes are destroyed.
-TEST(Mutex, DISABLED_LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS {
-#else
-TEST(Mutex, LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-#endif
-  for (int i = 0; i != 10; i++) {
-    // Create, lock and destroy 10 locks.
-    const int kNumLocks = 10;
-    auto mu = absl::make_unique<absl::Mutex[]>(kNumLocks);
-    for (int j = 0; j != kNumLocks; j++) {
-      if ((j % 2) == 0) {
-        mu[j].WriterLock();
-      } else {
-        mu[j].ReaderLock();
-      }
-    }
-  }
-}
-
-// --------------------------------------------------------
-// Test for bug with pattern of readers using a condvar.  The bug was that if a
-// reader went to sleep on a condition variable while one or more other readers
-// held the lock, but there were no waiters, the reader count (held in the
-// mutex word) would be lost.  (This is because Enqueue() had at one time
-// always placed the thread on the Mutex queue.  Later (CL 4075610), to
-// tolerate re-entry into Mutex from a Condition predicate, Enqueue() was
-// changed so that it could also place a thread on a condition-variable.  This
-// introduced the case where Enqueue() returned with an empty queue, and this
-// case was handled incorrectly in one place.)
-
-static void ReaderForReaderOnCondVar(absl::Mutex *mu, absl::CondVar *cv,
-                                     int *running) {
-  std::random_device dev;
-  std::mt19937 gen(dev());
-  std::uniform_int_distribution<int> random_millis(0, 15);
-  mu->ReaderLock();
-  while (*running == 3) {
-    absl::SleepFor(absl::Milliseconds(random_millis(gen)));
-    cv->WaitWithTimeout(mu, absl::Milliseconds(random_millis(gen)));
-  }
-  mu->ReaderUnlock();
-  mu->Lock();
-  (*running)--;
-  mu->Unlock();
-}
-
-struct True {
-  template <class... Args>
-  bool operator()(Args...) const {
-    return true;
-  }
-};
-
-struct DerivedTrue : True {};
-
-TEST(Mutex, FunctorCondition) {
-  {  // Variadic
-    True f;
-    EXPECT_TRUE(absl::Condition(&f).Eval());
-  }
-
-  {  // Inherited
-    DerivedTrue g;
-    EXPECT_TRUE(absl::Condition(&g).Eval());
-  }
-
-  {  // lambda
-    int value = 3;
-    auto is_zero = [&value] { return value == 0; };
-    absl::Condition c(&is_zero);
-    EXPECT_FALSE(c.Eval());
-    value = 0;
-    EXPECT_TRUE(c.Eval());
-  }
-
-  {  // bind
-    int value = 0;
-    auto is_positive = std::bind(std::less<int>(), 0, std::cref(value));
-    absl::Condition c(&is_positive);
-    EXPECT_FALSE(c.Eval());
-    value = 1;
-    EXPECT_TRUE(c.Eval());
-  }
-
-  {  // std::function
-    int value = 3;
-    std::function<bool()> is_zero = [&value] { return value == 0; };
-    absl::Condition c(&is_zero);
-    EXPECT_FALSE(c.Eval());
-    value = 0;
-    EXPECT_TRUE(c.Eval());
-  }
-}
-
-static bool IntIsZero(int *x) { return *x == 0; }
-
-// Test for reader waiting condition variable when there are other readers
-// but no waiters.
-TEST(Mutex, TestReaderOnCondVar) {
-  auto tp = CreateDefaultPool();
-  absl::Mutex mu;
-  absl::CondVar cv;
-  int running = 3;
-  tp->Schedule(std::bind(&ReaderForReaderOnCondVar, &mu, &cv, &running));
-  tp->Schedule(std::bind(&ReaderForReaderOnCondVar, &mu, &cv, &running));
-  absl::SleepFor(absl::Seconds(2));
-  mu.Lock();
-  running--;
-  mu.Await(absl::Condition(&IntIsZero, &running));
-  mu.Unlock();
-}
-
-// --------------------------------------------------------
-struct AcquireFromConditionStruct {
-  absl::Mutex mu0;   // protects value, done
-  int value;         // times condition function is called; under mu0,
-  bool done;         // done with test?  under mu0
-  absl::Mutex mu1;   // used to attempt to mess up state of mu0
-  absl::CondVar cv;  // so the condition function can be invoked from
-                     // CondVar::Wait().
-};
-
-static bool ConditionWithAcquire(AcquireFromConditionStruct *x) {
-  x->value++;  // count times this function is called
-
-  if (x->value == 2 || x->value == 3) {
-    // On the second and third invocation of this function, sleep for 100ms,
-    // but with the side-effect of altering the state of a Mutex other than
-    // than one for which this is a condition.  The spec now explicitly allows
-    // this side effect; previously it did not.  it was illegal.
-    bool always_false = false;
-    x->mu1.LockWhenWithTimeout(absl::Condition(&always_false),
-                               absl::Milliseconds(100));
-    x->mu1.Unlock();
-  }
-  ABSL_RAW_CHECK(x->value < 4, "should not be invoked a fourth time");
-
-  // We arrange for the condition to return true on only the 2nd and 3rd calls.
-  return x->value == 2 || x->value == 3;
-}
-
-static void WaitForCond2(AcquireFromConditionStruct *x) {
-  // wait for cond0 to become true
-  x->mu0.LockWhen(absl::Condition(&ConditionWithAcquire, x));
-  x->done = true;
-  x->mu0.Unlock();
-}
-
-// Test for Condition whose function acquires other Mutexes
-TEST(Mutex, AcquireFromCondition) {
-  auto tp = CreateDefaultPool();
-
-  AcquireFromConditionStruct x;
-  x.value = 0;
-  x.done = false;
-  tp->Schedule(
-      std::bind(&WaitForCond2, &x));  // run WaitForCond2() in a thread T
-  // T will hang because the first invocation of ConditionWithAcquire() will
-  // return false.
-  absl::SleepFor(absl::Milliseconds(500));  // allow T time to hang
-
-  x.mu0.Lock();
-  x.cv.WaitWithTimeout(&x.mu0, absl::Milliseconds(500));  // wake T
-  // T will be woken because the Wait() will call ConditionWithAcquire()
-  // for the second time, and it will return true.
-
-  x.mu0.Unlock();
-
-  // T will then acquire the lock and recheck its own condition.
-  // It will find the condition true, as this is the third invocation,
-  // but the use of another Mutex by the calling function will
-  // cause the old mutex implementation to think that the outer
-  // LockWhen() has timed out because the inner LockWhenWithTimeout() did.
-  // T will then check the condition a fourth time because it finds a
-  // timeout occurred.  This should not happen in the new
-  // implementation that allows the Condition function to use Mutexes.
-
-  // It should also succeed, even though the Condition function
-  // is being invoked from CondVar::Wait, and thus this thread
-  // is conceptually waiting both on the condition variable, and on mu2.
-
-  x.mu0.LockWhen(absl::Condition(&x.done));
-  x.mu0.Unlock();
-}
-
-TEST(Mutex, DeadlockDetector) {
-  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
-
-  // check that we can call ForgetDeadlockInfo() on a lock with the lock held
-  absl::Mutex m1;
-  absl::Mutex m2;
-  absl::Mutex m3;
-  absl::Mutex m4;
-
-  m1.Lock();  // m1 gets ID1
-  m2.Lock();  // m2 gets ID2
-  m3.Lock();  // m3 gets ID3
-  m3.Unlock();
-  m2.Unlock();
-  // m1 still held
-  m1.ForgetDeadlockInfo();  // m1 loses ID
-  m2.Lock();                // m2 gets ID2
-  m3.Lock();                // m3 gets ID3
-  m4.Lock();                // m4 gets ID4
-  m3.Unlock();
-  m2.Unlock();
-  m4.Unlock();
-  m1.Unlock();
-}
-
-// Bazel has a test "warning" file that programs can write to if the
-// test should pass with a warning.  This class disables the warning
-// file until it goes out of scope.
-class ScopedDisableBazelTestWarnings {
- public:
-  ScopedDisableBazelTestWarnings() {
-#ifdef _WIN32
-    char file[MAX_PATH];
-    if (GetEnvironmentVariableA(kVarName, file, sizeof(file)) < sizeof(file)) {
-      warnings_output_file_ = file;
-      SetEnvironmentVariableA(kVarName, nullptr);
-    }
-#else
-    const char *file = getenv(kVarName);
-    if (file != nullptr) {
-      warnings_output_file_ = file;
-      unsetenv(kVarName);
-    }
-#endif
-  }
-
-  ~ScopedDisableBazelTestWarnings() {
-    if (!warnings_output_file_.empty()) {
-#ifdef _WIN32
-      SetEnvironmentVariableA(kVarName, warnings_output_file_.c_str());
-#else
-      setenv(kVarName, warnings_output_file_.c_str(), 0);
-#endif
-    }
-  }
-
- private:
-  static const char kVarName[];
-  std::string warnings_output_file_;
-};
-const char ScopedDisableBazelTestWarnings::kVarName[] =
-    "TEST_WARNINGS_OUTPUT_FILE";
-
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-// This test intentionally creates deadlocks to test the deadlock detector.
-TEST(Mutex, DISABLED_DeadlockDetectorBazelWarning) {
-#else
-TEST(Mutex, DeadlockDetectorBazelWarning) {
-#endif
-  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kReport);
-
-  // Cause deadlock detection to detect something, if it's
-  // compiled in and enabled.  But turn off the bazel warning.
-  ScopedDisableBazelTestWarnings disable_bazel_test_warnings;
-
-  absl::Mutex mu0;
-  absl::Mutex mu1;
-  bool got_mu0 = mu0.TryLock();
-  mu1.Lock();  // acquire mu1 while holding mu0
-  if (got_mu0) {
-    mu0.Unlock();
-  }
-  if (mu0.TryLock()) {  // try lock shouldn't cause deadlock detector to fire
-    mu0.Unlock();
-  }
-  mu0.Lock();  // acquire mu0 while holding mu1; should get one deadlock
-               // report here
-  mu0.Unlock();
-  mu1.Unlock();
-
-  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
-}
-
-// This test is tagged with NO_THREAD_SAFETY_ANALYSIS because the
-// annotation-based static thread-safety analysis is not currently
-// predicate-aware and cannot tell if the two for-loops that acquire and
-// release the locks have the same predicates.
-TEST(Mutex, DeadlockDetectorStressTest) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-  // Stress test: Here we create a large number of locks and use all of them.
-  // If a deadlock detector keeps a full graph of lock acquisition order,
-  // it will likely be too slow for this test to pass.
-  const int n_locks = 1 << 17;
-  auto array_of_locks = absl::make_unique<absl::Mutex[]>(n_locks);
-  for (int i = 0; i < n_locks; i++) {
-    int end = std::min(n_locks, i + 5);
-    // acquire and then release locks i, i+1, ..., i+4
-    for (int j = i; j < end; j++) {
-      array_of_locks[j].Lock();
-    }
-    for (int j = i; j < end; j++) {
-      array_of_locks[j].Unlock();
-    }
-  }
-}
-
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-// TSAN reports errors when locked Mutexes are destroyed.
-TEST(Mutex, DISABLED_DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS {
-#else
-TEST(Mutex, DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-#endif
-  // Test a scenario where a cached deadlock graph node id in the
-  // list of held locks is not invalidated when the corresponding
-  // mutex is deleted.
-  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
-  // Mutex that will be destroyed while being held
-  absl::Mutex *a = new absl::Mutex;
-  // Other mutexes needed by test
-  absl::Mutex b, c;
-
-  // Hold mutex.
-  a->Lock();
-
-  // Force deadlock id assignment by acquiring another lock.
-  b.Lock();
-  b.Unlock();
-
-  // Delete the mutex. The Mutex destructor tries to remove held locks,
-  // but the attempt isn't foolproof.  It can fail if:
-  //   (a) Deadlock detection is currently disabled.
-  //   (b) The destruction is from another thread.
-  // We exploit (a) by temporarily disabling deadlock detection.
-  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kIgnore);
-  delete a;
-  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
-
-  // Now acquire another lock which will force a deadlock id assignment.
-  // We should end up getting assigned the same deadlock id that was
-  // freed up when "a" was deleted, which will cause a spurious deadlock
-  // report if the held lock entry for "a" was not invalidated.
-  c.Lock();
-  c.Unlock();
-}
-
-// --------------------------------------------------------
-// Test for timeouts/deadlines on condition waits that are specified using
-// absl::Duration and absl::Time.  For each waiting function we test with
-// a timeout/deadline that has already expired/passed, one that is infinite
-// and so never expires/passes, and one that will expire/pass in the near
-// future.
-
-static absl::Duration TimeoutTestAllowedSchedulingDelay() {
-  // Note: we use a function here because Microsoft Visual Studio fails to
-  // properly initialize constexpr static absl::Duration variables.
-  return absl::Milliseconds(150);
-}
-
-// Returns true if `actual_delay` is close enough to `expected_delay` to pass
-// the timeouts/deadlines test.  Otherwise, logs warnings and returns false.
-ABSL_MUST_USE_RESULT
-static bool DelayIsWithinBounds(absl::Duration expected_delay,
-                                absl::Duration actual_delay) {
-  bool pass = true;
-  // Do not allow the observed delay to be less than expected.  This may occur
-  // in practice due to clock skew or when the synchronization primitives use a
-  // different clock than absl::Now(), but these cases should be handled by the
-  // the retry mechanism in each TimeoutTest.
-  if (actual_delay < expected_delay) {
-    ABSL_RAW_LOG(WARNING,
-                 "Actual delay %s was too short, expected %s (difference %s)",
-                 absl::FormatDuration(actual_delay).c_str(),
-                 absl::FormatDuration(expected_delay).c_str(),
-                 absl::FormatDuration(actual_delay - expected_delay).c_str());
-    pass = false;
-  }
-  // If the expected delay is <= zero then allow a small error tolerance, since
-  // we do not expect context switches to occur during test execution.
-  // Otherwise, thread scheduling delays may be substantial in rare cases, so
-  // tolerate up to kTimeoutTestAllowedSchedulingDelay of error.
-  absl::Duration tolerance = expected_delay <= absl::ZeroDuration()
-                                 ? absl::Milliseconds(10)
-                                 : TimeoutTestAllowedSchedulingDelay();
-  if (actual_delay > expected_delay + tolerance) {
-    ABSL_RAW_LOG(WARNING,
-                 "Actual delay %s was too long, expected %s (difference %s)",
-                 absl::FormatDuration(actual_delay).c_str(),
-                 absl::FormatDuration(expected_delay).c_str(),
-                 absl::FormatDuration(actual_delay - expected_delay).c_str());
-    pass = false;
-  }
-  return pass;
-}
-
-// Parameters for TimeoutTest, below.
-struct TimeoutTestParam {
-  // The file and line number (used for logging purposes only).
-  const char *from_file;
-  int from_line;
-
-  // Should the absolute deadline API based on absl::Time be tested?  If false,
-  // the relative deadline API based on absl::Duration is tested.
-  bool use_absolute_deadline;
-
-  // The deadline/timeout used when calling the API being tested
-  // (e.g. Mutex::LockWhenWithDeadline).
-  absl::Duration wait_timeout;
-
-  // The delay before the condition will be set true by the test code.  If zero
-  // or negative, the condition is set true immediately (before calling the API
-  // being tested).  Otherwise, if infinite, the condition is never set true.
-  // Otherwise a closure is scheduled for the future that sets the condition
-  // true.
-  absl::Duration satisfy_condition_delay;
-
-  // The expected result of the condition after the call to the API being
-  // tested. Generally `true` means the condition was true when the API returns,
-  // `false` indicates an expected timeout.
-  bool expected_result;
-
-  // The expected delay before the API under test returns.  This is inherently
-  // flaky, so some slop is allowed (see `DelayIsWithinBounds` above), and the
-  // test keeps trying indefinitely until this constraint passes.
-  absl::Duration expected_delay;
-};
-
-// Print a `TimeoutTestParam` to a debug log.
-std::ostream &operator<<(std::ostream &os, const TimeoutTestParam &param) {
-  return os << "from: " << param.from_file << ":" << param.from_line
-            << " use_absolute_deadline: "
-            << (param.use_absolute_deadline ? "true" : "false")
-            << " wait_timeout: " << param.wait_timeout
-            << " satisfy_condition_delay: " << param.satisfy_condition_delay
-            << " expected_result: "
-            << (param.expected_result ? "true" : "false")
-            << " expected_delay: " << param.expected_delay;
-}
-
-std::string FormatString(const TimeoutTestParam &param) {
-  std::ostringstream os;
-  os << param;
-  return os.str();
-}
-
-// Like `thread::Executor::ScheduleAt` except:
-// a) Delays zero or negative are executed immediately in the current thread.
-// b) Infinite delays are never scheduled.
-// c) Calls this test's `ScheduleAt` helper instead of using `pool` directly.
-static void RunAfterDelay(absl::Duration delay,
-                          absl::synchronization_internal::ThreadPool *pool,
-                          const std::function<void()> &callback) {
-  if (delay <= absl::ZeroDuration()) {
-    callback();  // immediate
-  } else if (delay != absl::InfiniteDuration()) {
-    ScheduleAfter(pool, delay, callback);
-  }
-}
-
-class TimeoutTest : public ::testing::Test,
-                    public ::testing::WithParamInterface<TimeoutTestParam> {};
-
-std::vector<TimeoutTestParam> MakeTimeoutTestParamValues() {
-  // The `finite` delay is a finite, relatively short, delay.  We make it larger
-  // than our allowed scheduling delay (slop factor) to avoid confusion when
-  // diagnosing test failures.  The other constants here have clear meanings.
-  const absl::Duration finite = 3 * TimeoutTestAllowedSchedulingDelay();
-  const absl::Duration never = absl::InfiniteDuration();
-  const absl::Duration negative = -absl::InfiniteDuration();
-  const absl::Duration immediate = absl::ZeroDuration();
-
-  // Every test case is run twice; once using the absolute deadline API and once
-  // using the relative timeout API.
-  std::vector<TimeoutTestParam> values;
-  for (bool use_absolute_deadline : {false, true}) {
-    // Tests with a negative timeout (deadline in the past), which should
-    // immediately return current state of the condition.
-
-    // The condition is already true:
-    values.push_back(TimeoutTestParam{
-        __FILE__, __LINE__, use_absolute_deadline,
-        negative,   // wait_timeout
-        immediate,  // satisfy_condition_delay
-        true,       // expected_result
-        immediate,  // expected_delay
-    });
-
-    // The condition becomes true, but the timeout has already expired:
-    values.push_back(TimeoutTestParam{
-        __FILE__, __LINE__, use_absolute_deadline,
-        negative,  // wait_timeout
-        finite,    // satisfy_condition_delay
-        false,     // expected_result
-        immediate  // expected_delay
-    });
-
-    // The condition never becomes true:
-    values.push_back(TimeoutTestParam{
-        __FILE__, __LINE__, use_absolute_deadline,
-        negative,  // wait_timeout
-        never,     // satisfy_condition_delay
-        false,     // expected_result
-        immediate  // expected_delay
-    });
-
-    // Tests with an infinite timeout (deadline in the infinite future), which
-    // should only return when the condition becomes true.
-
-    // The condition is already true:
-    values.push_back(TimeoutTestParam{
-        __FILE__, __LINE__, use_absolute_deadline,
-        never,      // wait_timeout
-        immediate,  // satisfy_condition_delay
-        true,       // expected_result
-        immediate   // expected_delay
-    });
-
-    // The condition becomes true before the (infinite) expiry:
-    values.push_back(TimeoutTestParam{
-        __FILE__, __LINE__, use_absolute_deadline,
-        never,   // wait_timeout
-        finite,  // satisfy_condition_delay
-        true,    // expected_result
-        finite,  // expected_delay
-    });
-
-    // Tests with a (small) finite timeout (deadline soon), with the condition
-    // becoming true both before and after its expiry.
-
-    // The condition is already true:
-    values.push_back(TimeoutTestParam{
-        __FILE__, __LINE__, use_absolute_deadline,
-        never,      // wait_timeout
-        immediate,  // satisfy_condition_delay
-        true,       // expected_result
-        immediate   // expected_delay
-    });
-
-    // The condition becomes true before the expiry:
-    values.push_back(TimeoutTestParam{
-        __FILE__, __LINE__, use_absolute_deadline,
-        finite * 2,  // wait_timeout
-        finite,      // satisfy_condition_delay
-        true,        // expected_result
-        finite       // expected_delay
-    });
-
-    // The condition becomes true, but the timeout has already expired:
-    values.push_back(TimeoutTestParam{
-        __FILE__, __LINE__, use_absolute_deadline,
-        finite,      // wait_timeout
-        finite * 2,  // satisfy_condition_delay
-        false,       // expected_result
-        finite       // expected_delay
-    });
-
-    // The condition never becomes true:
-    values.push_back(TimeoutTestParam{
-        __FILE__, __LINE__, use_absolute_deadline,
-        finite,  // wait_timeout
-        never,   // satisfy_condition_delay
-        false,   // expected_result
-        finite   // expected_delay
-    });
-  }
-  return values;
-}
-
-// Instantiate `TimeoutTest` with `MakeTimeoutTestParamValues()`.
-INSTANTIATE_TEST_SUITE_P(All, TimeoutTest,
-                         testing::ValuesIn(MakeTimeoutTestParamValues()));
-
-TEST_P(TimeoutTest, Await) {
-  const TimeoutTestParam params = GetParam();
-  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
-
-  // Because this test asserts bounds on scheduling delays it is flaky.  To
-  // compensate it loops forever until it passes.  Failures express as test
-  // timeouts, in which case the test log can be used to diagnose the issue.
-  for (int attempt = 1;; ++attempt) {
-    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
-
-    absl::Mutex mu;
-    bool value = false;  // condition value (under mu)
-
-    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
-        CreateDefaultPool();
-    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
-      absl::MutexLock l(&mu);
-      value = true;
-    });
-
-    absl::MutexLock lock(&mu);
-    absl::Time start_time = absl::Now();
-    absl::Condition cond(&value);
-    bool result =
-        params.use_absolute_deadline
-            ? mu.AwaitWithDeadline(cond, start_time + params.wait_timeout)
-            : mu.AwaitWithTimeout(cond, params.wait_timeout);
-    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
-      EXPECT_EQ(params.expected_result, result);
-      break;
-    }
-  }
-}
-
-TEST_P(TimeoutTest, LockWhen) {
-  const TimeoutTestParam params = GetParam();
-  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
-
-  // Because this test asserts bounds on scheduling delays it is flaky.  To
-  // compensate it loops forever until it passes.  Failures express as test
-  // timeouts, in which case the test log can be used to diagnose the issue.
-  for (int attempt = 1;; ++attempt) {
-    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
-
-    absl::Mutex mu;
-    bool value = false;  // condition value (under mu)
-
-    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
-        CreateDefaultPool();
-    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
-      absl::MutexLock l(&mu);
-      value = true;
-    });
-
-    absl::Time start_time = absl::Now();
-    absl::Condition cond(&value);
-    bool result =
-        params.use_absolute_deadline
-            ? mu.LockWhenWithDeadline(cond, start_time + params.wait_timeout)
-            : mu.LockWhenWithTimeout(cond, params.wait_timeout);
-    mu.Unlock();
-
-    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
-      EXPECT_EQ(params.expected_result, result);
-      break;
-    }
-  }
-}
-
-TEST_P(TimeoutTest, ReaderLockWhen) {
-  const TimeoutTestParam params = GetParam();
-  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
-
-  // Because this test asserts bounds on scheduling delays it is flaky.  To
-  // compensate it loops forever until it passes.  Failures express as test
-  // timeouts, in which case the test log can be used to diagnose the issue.
-  for (int attempt = 0;; ++attempt) {
-    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
-
-    absl::Mutex mu;
-    bool value = false;  // condition value (under mu)
-
-    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
-        CreateDefaultPool();
-    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
-      absl::MutexLock l(&mu);
-      value = true;
-    });
-
-    absl::Time start_time = absl::Now();
-    bool result =
-        params.use_absolute_deadline
-            ? mu.ReaderLockWhenWithDeadline(absl::Condition(&value),
-                                            start_time + params.wait_timeout)
-            : mu.ReaderLockWhenWithTimeout(absl::Condition(&value),
-                                           params.wait_timeout);
-    mu.ReaderUnlock();
-
-    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
-      EXPECT_EQ(params.expected_result, result);
-      break;
-    }
-  }
-}
-
-TEST_P(TimeoutTest, Wait) {
-  const TimeoutTestParam params = GetParam();
-  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
-
-  // Because this test asserts bounds on scheduling delays it is flaky.  To
-  // compensate it loops forever until it passes.  Failures express as test
-  // timeouts, in which case the test log can be used to diagnose the issue.
-  for (int attempt = 0;; ++attempt) {
-    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
-
-    absl::Mutex mu;
-    bool value = false;  // condition value (under mu)
-    absl::CondVar cv;    // signals a change of `value`
-
-    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
-        CreateDefaultPool();
-    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
-      absl::MutexLock l(&mu);
-      value = true;
-      cv.Signal();
-    });
-
-    absl::MutexLock lock(&mu);
-    absl::Time start_time = absl::Now();
-    absl::Duration timeout = params.wait_timeout;
-    absl::Time deadline = start_time + timeout;
-    while (!value) {
-      if (params.use_absolute_deadline ? cv.WaitWithDeadline(&mu, deadline)
-                                       : cv.WaitWithTimeout(&mu, timeout)) {
-        break;  // deadline/timeout exceeded
-      }
-      timeout = deadline - absl::Now();  // recompute
-    }
-    bool result = value;  // note: `mu` is still held
-
-    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
-      EXPECT_EQ(params.expected_result, result);
-      break;
-    }
-  }
-}
-
-TEST(Mutex, Logging) {
-  // Allow user to look at logging output
-  absl::Mutex logged_mutex;
-  logged_mutex.EnableDebugLog("fido_mutex");
-  absl::CondVar logged_cv;
-  logged_cv.EnableDebugLog("rover_cv");
-  logged_mutex.Lock();
-  logged_cv.WaitWithTimeout(&logged_mutex, absl::Milliseconds(20));
-  logged_mutex.Unlock();
-  logged_mutex.ReaderLock();
-  logged_mutex.ReaderUnlock();
-  logged_mutex.Lock();
-  logged_mutex.Unlock();
-  logged_cv.Signal();
-  logged_cv.SignalAll();
-}
-
-// --------------------------------------------------------
-
-// Generate the vector of thread counts for tests parameterized on thread count.
-static std::vector<int> AllThreadCountValues() {
-  if (kExtendedTest) {
-    return {2, 4, 8, 10, 16, 20, 24, 30, 32};
-  }
-  return {2, 4, 10};
-}
-
-// A test fixture parameterized by thread count.
-class MutexVariableThreadCountTest : public ::testing::TestWithParam<int> {};
-
-// Instantiate the above with AllThreadCountOptions().
-INSTANTIATE_TEST_SUITE_P(ThreadCounts, MutexVariableThreadCountTest,
-                         ::testing::ValuesIn(AllThreadCountValues()),
-                         ::testing::PrintToStringParamName());
-
-// Reduces iterations by some factor for slow platforms
-// (determined empirically).
-static int ScaleIterations(int x) {
-  // ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE is set in the implementation
-  // of Mutex that uses either std::mutex or pthread_mutex_t. Use
-  // these as keys to determine the slow implementation.
-#if defined(ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE)
-  return x / 10;
-#else
-  return x;
-#endif
-}
-
-TEST_P(MutexVariableThreadCountTest, Mutex) {
-  int threads = GetParam();
-  int iterations = ScaleIterations(10000000) / threads;
-  int operations = threads * iterations;
-  EXPECT_EQ(RunTest(&TestMu, threads, iterations, operations), operations);
-#if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED)
-  iterations = std::min(iterations, 10);
-  operations = threads * iterations;
-  EXPECT_EQ(RunTestWithInvariantDebugging(&TestMu, threads, iterations,
-                                          operations, CheckSumG0G1),
-            operations);
-#endif
-}
-
-TEST_P(MutexVariableThreadCountTest, Try) {
-  int threads = GetParam();
-  int iterations = 1000000 / threads;
-  int operations = iterations * threads;
-  EXPECT_EQ(RunTest(&TestTry, threads, iterations, operations), operations);
-#if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED)
-  iterations = std::min(iterations, 10);
-  operations = threads * iterations;
-  EXPECT_EQ(RunTestWithInvariantDebugging(&TestTry, threads, iterations,
-                                          operations, CheckSumG0G1),
-            operations);
-#endif
-}
-
-TEST_P(MutexVariableThreadCountTest, R20ms) {
-  int threads = GetParam();
-  int iterations = 100;
-  int operations = iterations * threads;
-  EXPECT_EQ(RunTest(&TestR20ms, threads, iterations, operations), 0);
-}
-
-TEST_P(MutexVariableThreadCountTest, RW) {
-  int threads = GetParam();
-  int iterations = ScaleIterations(20000000) / threads;
-  int operations = iterations * threads;
-  EXPECT_EQ(RunTest(&TestRW, threads, iterations, operations), operations / 2);
-#if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED)
-  iterations = std::min(iterations, 10);
-  operations = threads * iterations;
-  EXPECT_EQ(RunTestWithInvariantDebugging(&TestRW, threads, iterations,
-                                          operations, CheckSumG0G1),
-            operations / 2);
-#endif
-}
-
-TEST_P(MutexVariableThreadCountTest, Await) {
-  int threads = GetParam();
-  int iterations = ScaleIterations(500000);
-  int operations = iterations;
-  EXPECT_EQ(RunTest(&TestAwait, threads, iterations, operations), operations);
-}
-
-TEST_P(MutexVariableThreadCountTest, SignalAll) {
-  int threads = GetParam();
-  int iterations = 200000 / threads;
-  int operations = iterations;
-  EXPECT_EQ(RunTest(&TestSignalAll, threads, iterations, operations),
-            operations);
-}
-
-TEST(Mutex, Signal) {
-  int threads = 2;  // TestSignal must use two threads
-  int iterations = 200000;
-  int operations = iterations;
-  EXPECT_EQ(RunTest(&TestSignal, threads, iterations, operations), operations);
-}
-
-TEST(Mutex, Timed) {
-  int threads = 10;  // Use a fixed thread count of 10
-  int iterations = 1000;
-  int operations = iterations;
-  EXPECT_EQ(RunTest(&TestCVTimeout, threads, iterations, operations),
-            operations);
-}
-
-TEST(Mutex, CVTime) {
-  int threads = 10;  // Use a fixed thread count of 10
-  int iterations = 1;
-  EXPECT_EQ(RunTest(&TestCVTime, threads, iterations, 1),
-            threads * iterations);
-}
-
-TEST(Mutex, MuTime) {
-  int threads = 10;  // Use a fixed thread count of 10
-  int iterations = 1;
-  EXPECT_EQ(RunTest(&TestMuTime, threads, iterations, 1), threads * iterations);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/synchronization/notification.cc b/third_party/abseil_cpp/absl/synchronization/notification.cc
deleted file mode 100644
index e91b903822..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/notification.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/notification.h"
-
-#include <atomic>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/synchronization/mutex.h"
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-void Notification::Notify() {
-  MutexLock l(&this->mutex_);
-
-#ifndef NDEBUG
-  if (ABSL_PREDICT_FALSE(notified_yet_.load(std::memory_order_relaxed))) {
-    ABSL_RAW_LOG(
-        FATAL,
-        "Notify() method called more than once for Notification object %p",
-        static_cast<void *>(this));
-  }
-#endif
-
-  notified_yet_.store(true, std::memory_order_release);
-}
-
-Notification::~Notification() {
-  // Make sure that the thread running Notify() exits before the object is
-  // destructed.
-  MutexLock l(&this->mutex_);
-}
-
-void Notification::WaitForNotification() const {
-  if (!HasBeenNotifiedInternal(&this->notified_yet_)) {
-    this->mutex_.LockWhen(Condition(&HasBeenNotifiedInternal,
-                                    &this->notified_yet_));
-    this->mutex_.Unlock();
-  }
-}
-
-bool Notification::WaitForNotificationWithTimeout(
-    absl::Duration timeout) const {
-  bool notified = HasBeenNotifiedInternal(&this->notified_yet_);
-  if (!notified) {
-    notified = this->mutex_.LockWhenWithTimeout(
-        Condition(&HasBeenNotifiedInternal, &this->notified_yet_), timeout);
-    this->mutex_.Unlock();
-  }
-  return notified;
-}
-
-bool Notification::WaitForNotificationWithDeadline(absl::Time deadline) const {
-  bool notified = HasBeenNotifiedInternal(&this->notified_yet_);
-  if (!notified) {
-    notified = this->mutex_.LockWhenWithDeadline(
-        Condition(&HasBeenNotifiedInternal, &this->notified_yet_), deadline);
-    this->mutex_.Unlock();
-  }
-  return notified;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/synchronization/notification.h b/third_party/abseil_cpp/absl/synchronization/notification.h
deleted file mode 100644
index 9a354ca2c0..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/notification.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// notification.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines a `Notification` abstraction, which allows threads
-// to receive notification of a single occurrence of a single event.
-//
-// The `Notification` object maintains a private boolean "notified" state that
-// transitions to `true` at most once. The `Notification` class provides the
-// following primary member functions:
-//   * `HasBeenNotified() `to query its state
-//   * `WaitForNotification*()` to have threads wait until the "notified" state
-//      is `true`.
-//   * `Notify()` to set the notification's "notified" state to `true` and
-//     notify all waiting threads that the event has occurred.
-//     This method may only be called once.
-//
-// Note that while `Notify()` may only be called once, it is perfectly valid to
-// call any of the `WaitForNotification*()` methods multiple times, from
-// multiple threads -- even after the notification's "notified" state has been
-// set -- in which case those methods will immediately return.
-//
-// Note that the lifetime of a `Notification` requires careful consideration;
-// it might not be safe to destroy a notification after calling `Notify()` since
-// it is still legal for other threads to call `WaitForNotification*()` methods
-// on the notification. However, observers responding to a "notified" state of
-// `true` can safely delete the notification without interfering with the call
-// to `Notify()` in the other thread.
-//
-// Memory ordering: For any threads X and Y, if X calls `Notify()`, then any
-// action taken by X before it calls `Notify()` is visible to thread Y after:
-//  * Y returns from `WaitForNotification()`, or
-//  * Y receives a `true` return value from either `HasBeenNotified()` or
-//    `WaitForNotificationWithTimeout()`.
-
-#ifndef ABSL_SYNCHRONIZATION_NOTIFICATION_H_
-#define ABSL_SYNCHRONIZATION_NOTIFICATION_H_
-
-#include <atomic>
-
-#include "absl/base/macros.h"
-#include "absl/synchronization/mutex.h"
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// Notification
-// -----------------------------------------------------------------------------
-class Notification {
- public:
-  // Initializes the "notified" state to unnotified.
-  Notification() : notified_yet_(false) {}
-  explicit Notification(bool prenotify) : notified_yet_(prenotify) {}
-  Notification(const Notification&) = delete;
-  Notification& operator=(const Notification&) = delete;
-  ~Notification();
-
-  // Notification::HasBeenNotified()
-  //
-  // Returns the value of the notification's internal "notified" state.
-  bool HasBeenNotified() const {
-    return HasBeenNotifiedInternal(&this->notified_yet_);
-  }
-
-  // Notification::WaitForNotification()
-  //
-  // Blocks the calling thread until the notification's "notified" state is
-  // `true`. Note that if `Notify()` has been previously called on this
-  // notification, this function will immediately return.
-  void WaitForNotification() const;
-
-  // Notification::WaitForNotificationWithTimeout()
-  //
-  // Blocks until either the notification's "notified" state is `true` (which
-  // may occur immediately) or the timeout has elapsed, returning the value of
-  // its "notified" state in either case.
-  bool WaitForNotificationWithTimeout(absl::Duration timeout) const;
-
-  // Notification::WaitForNotificationWithDeadline()
-  //
-  // Blocks until either the notification's "notified" state is `true` (which
-  // may occur immediately) or the deadline has expired, returning the value of
-  // its "notified" state in either case.
-  bool WaitForNotificationWithDeadline(absl::Time deadline) const;
-
-  // Notification::Notify()
-  //
-  // Sets the "notified" state of this notification to `true` and wakes waiting
-  // threads. Note: do not call `Notify()` multiple times on the same
-  // `Notification`; calling `Notify()` more than once on the same notification
-  // results in undefined behavior.
-  void Notify();
-
- private:
-  static inline bool HasBeenNotifiedInternal(
-      const std::atomic<bool>* notified_yet) {
-    return notified_yet->load(std::memory_order_acquire);
-  }
-
-  mutable Mutex mutex_;
-  std::atomic<bool> notified_yet_;  // written under mutex_
-};
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_SYNCHRONIZATION_NOTIFICATION_H_
diff --git a/third_party/abseil_cpp/absl/synchronization/notification_test.cc b/third_party/abseil_cpp/absl/synchronization/notification_test.cc
deleted file mode 100644
index 100ea76f33..0000000000
--- a/third_party/abseil_cpp/absl/synchronization/notification_test.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/synchronization/notification.h"
-
-#include <thread>  // NOLINT(build/c++11)
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// A thread-safe class that holds a counter.
-class ThreadSafeCounter {
- public:
-  ThreadSafeCounter() : count_(0) {}
-
-  void Increment() {
-    MutexLock lock(&mutex_);
-    ++count_;
-  }
-
-  int Get() const {
-    MutexLock lock(&mutex_);
-    return count_;
-  }
-
-  void WaitUntilGreaterOrEqual(int n) {
-    MutexLock lock(&mutex_);
-    auto cond = [this, n]() { return count_ >= n; };
-    mutex_.Await(Condition(&cond));
-  }
-
- private:
-  mutable Mutex mutex_;
-  int count_;
-};
-
-// Runs the |i|'th worker thread for the tests in BasicTests().  Increments the
-// |ready_counter|, waits on the |notification|, and then increments the
-// |done_counter|.
-static void RunWorker(int i, ThreadSafeCounter* ready_counter,
-                      Notification* notification,
-                      ThreadSafeCounter* done_counter) {
-  ready_counter->Increment();
-  notification->WaitForNotification();
-  done_counter->Increment();
-}
-
-// Tests that the |notification| properly blocks and awakens threads.  Assumes
-// that the |notification| is not yet triggered.  If |notify_before_waiting| is
-// true, the |notification| is triggered before any threads are created, so the
-// threads never block in WaitForNotification().  Otherwise, the |notification|
-// is triggered at a later point when most threads are likely to be blocking in
-// WaitForNotification().
-static void BasicTests(bool notify_before_waiting, Notification* notification) {
-  EXPECT_FALSE(notification->HasBeenNotified());
-  EXPECT_FALSE(
-      notification->WaitForNotificationWithTimeout(absl::Milliseconds(0)));
-  EXPECT_FALSE(notification->WaitForNotificationWithDeadline(absl::Now()));
-
-  const absl::Duration delay = absl::Milliseconds(50);
-  const absl::Time start = absl::Now();
-  EXPECT_FALSE(notification->WaitForNotificationWithTimeout(delay));
-  const absl::Duration elapsed = absl::Now() - start;
-
-  // Allow for a slight early return, to account for quality of implementation
-  // issues on various platforms.
-  const absl::Duration slop = absl::Microseconds(200);
-  EXPECT_LE(delay - slop, elapsed)
-      << "WaitForNotificationWithTimeout returned " << delay - elapsed
-      << " early (with " << slop << " slop), start time was " << start;
-
-  ThreadSafeCounter ready_counter;
-  ThreadSafeCounter done_counter;
-
-  if (notify_before_waiting) {
-    notification->Notify();
-  }
-
-  // Create a bunch of threads that increment the |done_counter| after being
-  // notified.
-  const int kNumThreads = 10;
-  std::vector<std::thread> workers;
-  for (int i = 0; i < kNumThreads; ++i) {
-    workers.push_back(std::thread(&RunWorker, i, &ready_counter, notification,
-                                  &done_counter));
-  }
-
-  if (!notify_before_waiting) {
-    ready_counter.WaitUntilGreaterOrEqual(kNumThreads);
-
-    // Workers have not been notified yet, so the |done_counter| should be
-    // unmodified.
-    EXPECT_EQ(0, done_counter.Get());
-
-    notification->Notify();
-  }
-
-  // After notifying and then joining the workers, both counters should be
-  // fully incremented.
-  notification->WaitForNotification();  // should exit immediately
-  EXPECT_TRUE(notification->HasBeenNotified());
-  EXPECT_TRUE(notification->WaitForNotificationWithTimeout(absl::Seconds(0)));
-  EXPECT_TRUE(notification->WaitForNotificationWithDeadline(absl::Now()));
-  for (std::thread& worker : workers) {
-    worker.join();
-  }
-  EXPECT_EQ(kNumThreads, ready_counter.Get());
-  EXPECT_EQ(kNumThreads, done_counter.Get());
-}
-
-TEST(NotificationTest, SanityTest) {
-  Notification local_notification1, local_notification2;
-  BasicTests(false, &local_notification1);
-  BasicTests(true, &local_notification2);
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/BUILD.bazel b/third_party/abseil_cpp/absl/time/BUILD.bazel
deleted file mode 100644
index 991241a0df..0000000000
--- a/third_party/abseil_cpp/absl/time/BUILD.bazel
+++ /dev/null
@@ -1,125 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "time",
-    srcs = [
-        "civil_time.cc",
-        "clock.cc",
-        "duration.cc",
-        "format.cc",
-        "internal/get_current_time_chrono.inc",
-        "internal/get_current_time_posix.inc",
-        "time.cc",
-    ],
-    hdrs = [
-        "civil_time.h",
-        "clock.h",
-        "time.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base",
-        "//absl/base:core_headers",
-        "//absl/base:raw_logging_internal",
-        "//absl/numeric:int128",
-        "//absl/strings",
-        "//absl/time/internal/cctz:civil_time",
-        "//absl/time/internal/cctz:time_zone",
-    ],
-)
-
-cc_library(
-    name = "test_util",
-    testonly = 1,
-    srcs = [
-        "internal/test_util.cc",
-        "internal/zoneinfo.inc",
-    ],
-    hdrs = ["internal/test_util.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = [
-        "//absl/time:__pkg__",
-    ],
-    deps = [
-        ":time",
-        "//absl/base:config",
-        "//absl/base:raw_logging_internal",
-        "//absl/time/internal/cctz:time_zone",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "time_test",
-    srcs = [
-        "civil_time_test.cc",
-        "clock_test.cc",
-        "duration_test.cc",
-        "format_test.cc",
-        "time_test.cc",
-        "time_zone_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":test_util",
-        ":time",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/numeric:int128",
-        "//absl/time/internal/cctz:time_zone",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "time_benchmark",
-    srcs = [
-        "civil_time_benchmark.cc",
-        "clock_benchmark.cc",
-        "duration_benchmark.cc",
-        "format_benchmark.cc",
-        "time_benchmark.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = [
-        "benchmark",
-    ],
-    deps = [
-        ":test_util",
-        ":time",
-        "//absl/base",
-        "//absl/base:core_headers",
-        "//absl/hash",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/time/CMakeLists.txt b/third_party/abseil_cpp/absl/time/CMakeLists.txt
deleted file mode 100644
index 00bdd499c1..0000000000
--- a/third_party/abseil_cpp/absl/time/CMakeLists.txt
+++ /dev/null
@@ -1,128 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    time
-  HDRS
-    "civil_time.h"
-    "clock.h"
-    "time.h"
-  SRCS
-    "civil_time.cc"
-    "clock.cc"
-    "duration.cc"
-    "format.cc"
-    "internal/get_current_time_chrono.inc"
-    "internal/get_current_time_posix.inc"
-    "time.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base
-    absl::civil_time
-    absl::core_headers
-    absl::int128
-    absl::raw_logging_internal
-    absl::strings
-    absl::time_zone
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    civil_time
-  HDRS
-    "internal/cctz/include/cctz/civil_time.h"
-    "internal/cctz/include/cctz/civil_time_detail.h"
-  SRCS
-  "internal/cctz/src/civil_time_detail.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-)
-
-if(APPLE)
-  find_library(CoreFoundation CoreFoundation)
-endif()
-
-absl_cc_library(
-  NAME
-    time_zone
-  HDRS
-    "internal/cctz/include/cctz/time_zone.h"
-    "internal/cctz/include/cctz/zone_info_source.h"
-  SRCS
-    "internal/cctz/src/time_zone_fixed.cc"
-    "internal/cctz/src/time_zone_fixed.h"
-    "internal/cctz/src/time_zone_format.cc"
-    "internal/cctz/src/time_zone_if.cc"
-    "internal/cctz/src/time_zone_if.h"
-    "internal/cctz/src/time_zone_impl.cc"
-    "internal/cctz/src/time_zone_impl.h"
-    "internal/cctz/src/time_zone_info.cc"
-    "internal/cctz/src/time_zone_info.h"
-    "internal/cctz/src/time_zone_libc.cc"
-    "internal/cctz/src/time_zone_libc.h"
-    "internal/cctz/src/time_zone_lookup.cc"
-    "internal/cctz/src/time_zone_posix.cc"
-    "internal/cctz/src/time_zone_posix.h"
-    "internal/cctz/src/tzfile.h"
-    "internal/cctz/src/zone_info_source.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    $<$<PLATFORM_ID:Darwin>:${CoreFoundation}>
-)
-
-absl_cc_library(
-  NAME
-    time_internal_test_util
-  HDRS
-    "internal/test_util.h"
-  SRCS
-    "internal/test_util.cc"
-    "internal/zoneinfo.inc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::time
-    absl::config
-    absl::raw_logging_internal
-    absl::time_zone
-    gmock
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    time_test
-  SRCS
-    "civil_time_test.cc"
-    "clock_test.cc"
-    "duration_test.cc"
-    "format_test.cc"
-    "time_test.cc"
-    "time_zone_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::time_internal_test_util
-    absl::time
-    absl::config
-    absl::core_headers
-    absl::time_zone
-    gmock_main
-)
diff --git a/third_party/abseil_cpp/absl/time/civil_time.cc b/third_party/abseil_cpp/absl/time/civil_time.cc
deleted file mode 100644
index bdfe9ce0ef..0000000000
--- a/third_party/abseil_cpp/absl/time/civil_time.cc
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/civil_time.h"
-
-#include <cstdlib>
-#include <string>
-
-#include "absl/strings/str_cat.h"
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace {
-
-// Since a civil time has a larger year range than absl::Time (64-bit years vs
-// 64-bit seconds, respectively) we normalize years to roughly +/- 400 years
-// around the year 2400, which will produce an equivalent year in a range that
-// absl::Time can handle.
-inline civil_year_t NormalizeYear(civil_year_t year) {
-  return 2400 + year % 400;
-}
-
-// Formats the given CivilSecond according to the given format.
-std::string FormatYearAnd(string_view fmt, CivilSecond cs) {
-  const CivilSecond ncs(NormalizeYear(cs.year()), cs.month(), cs.day(),
-                        cs.hour(), cs.minute(), cs.second());
-  const TimeZone utc = UTCTimeZone();
-  // TODO(absl-team): Avoid conversion of fmt string.
-  return StrCat(cs.year(),
-                FormatTime(std::string(fmt), FromCivil(ncs, utc), utc));
-}
-
-template <typename CivilT>
-bool ParseYearAnd(string_view fmt, string_view s, CivilT* c) {
-  // Civil times support a larger year range than absl::Time, so we need to
-  // parse the year separately, normalize it, then use absl::ParseTime on the
-  // normalized string.
-  const std::string ss = std::string(s);  // TODO(absl-team): Avoid conversion.
-  const char* const np = ss.c_str();
-  char* endp;
-  errno = 0;
-  const civil_year_t y =
-      std::strtoll(np, &endp, 10);  // NOLINT(runtime/deprecated_fn)
-  if (endp == np || errno == ERANGE) return false;
-  const std::string norm = StrCat(NormalizeYear(y), endp);
-
-  const TimeZone utc = UTCTimeZone();
-  Time t;
-  if (ParseTime(StrCat("%Y", fmt), norm, utc, &t, nullptr)) {
-    const auto cs = ToCivilSecond(t, utc);
-    *c = CivilT(y, cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second());
-    return true;
-  }
-
-  return false;
-}
-
-// Tries to parse the type as a CivilT1, but then assigns the result to the
-// argument of type CivilT2.
-template <typename CivilT1, typename CivilT2>
-bool ParseAs(string_view s, CivilT2* c) {
-  CivilT1 t1;
-  if (ParseCivilTime(s, &t1)) {
-    *c = CivilT2(t1);
-    return true;
-  }
-  return false;
-}
-
-template <typename CivilT>
-bool ParseLenient(string_view s, CivilT* c) {
-  // A fastpath for when the given string data parses exactly into the given
-  // type T (e.g., s="YYYY-MM-DD" and CivilT=CivilDay).
-  if (ParseCivilTime(s, c)) return true;
-  // Try parsing as each of the 6 types, trying the most common types first
-  // (based on csearch results).
-  if (ParseAs<CivilDay>(s, c)) return true;
-  if (ParseAs<CivilSecond>(s, c)) return true;
-  if (ParseAs<CivilHour>(s, c)) return true;
-  if (ParseAs<CivilMonth>(s, c)) return true;
-  if (ParseAs<CivilMinute>(s, c)) return true;
-  if (ParseAs<CivilYear>(s, c)) return true;
-  return false;
-}
-}  // namespace
-
-std::string FormatCivilTime(CivilSecond c) {
-  return FormatYearAnd("-%m-%d%ET%H:%M:%S", c);
-}
-std::string FormatCivilTime(CivilMinute c) {
-  return FormatYearAnd("-%m-%d%ET%H:%M", c);
-}
-std::string FormatCivilTime(CivilHour c) {
-  return FormatYearAnd("-%m-%d%ET%H", c);
-}
-std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); }
-std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); }
-std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); }
-
-bool ParseCivilTime(string_view s, CivilSecond* c) {
-  return ParseYearAnd("-%m-%d%ET%H:%M:%S", s, c);
-}
-bool ParseCivilTime(string_view s, CivilMinute* c) {
-  return ParseYearAnd("-%m-%d%ET%H:%M", s, c);
-}
-bool ParseCivilTime(string_view s, CivilHour* c) {
-  return ParseYearAnd("-%m-%d%ET%H", s, c);
-}
-bool ParseCivilTime(string_view s, CivilDay* c) {
-  return ParseYearAnd("-%m-%d", s, c);
-}
-bool ParseCivilTime(string_view s, CivilMonth* c) {
-  return ParseYearAnd("-%m", s, c);
-}
-bool ParseCivilTime(string_view s, CivilYear* c) {
-  return ParseYearAnd("", s, c);
-}
-
-bool ParseLenientCivilTime(string_view s, CivilSecond* c) {
-  return ParseLenient(s, c);
-}
-bool ParseLenientCivilTime(string_view s, CivilMinute* c) {
-  return ParseLenient(s, c);
-}
-bool ParseLenientCivilTime(string_view s, CivilHour* c) {
-  return ParseLenient(s, c);
-}
-bool ParseLenientCivilTime(string_view s, CivilDay* c) {
-  return ParseLenient(s, c);
-}
-bool ParseLenientCivilTime(string_view s, CivilMonth* c) {
-  return ParseLenient(s, c);
-}
-bool ParseLenientCivilTime(string_view s, CivilYear* c) {
-  return ParseLenient(s, c);
-}
-
-namespace time_internal {
-
-std::ostream& operator<<(std::ostream& os, CivilYear y) {
-  return os << FormatCivilTime(y);
-}
-std::ostream& operator<<(std::ostream& os, CivilMonth m) {
-  return os << FormatCivilTime(m);
-}
-std::ostream& operator<<(std::ostream& os, CivilDay d) {
-  return os << FormatCivilTime(d);
-}
-std::ostream& operator<<(std::ostream& os, CivilHour h) {
-  return os << FormatCivilTime(h);
-}
-std::ostream& operator<<(std::ostream& os, CivilMinute m) {
-  return os << FormatCivilTime(m);
-}
-std::ostream& operator<<(std::ostream& os, CivilSecond s) {
-  return os << FormatCivilTime(s);
-}
-
-}  // namespace time_internal
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/civil_time.h b/third_party/abseil_cpp/absl/time/civil_time.h
deleted file mode 100644
index bb46004434..0000000000
--- a/third_party/abseil_cpp/absl/time/civil_time.h
+++ /dev/null
@@ -1,538 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: civil_time.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines abstractions for computing with "civil time".
-// The term "civil time" refers to the legally recognized human-scale time
-// that is represented by the six fields `YYYY-MM-DD hh:mm:ss`. A "date"
-// is perhaps the most common example of a civil time (represented here as
-// an `absl::CivilDay`).
-//
-// Modern-day civil time follows the Gregorian Calendar and is a
-// time-zone-independent concept: a civil time of "2015-06-01 12:00:00", for
-// example, is not tied to a time zone. Put another way, a civil time does not
-// map to a unique point in time; a civil time must be mapped to an absolute
-// time *through* a time zone.
-//
-// Because a civil time is what most people think of as "time," it is common to
-// map absolute times to civil times to present to users.
-//
-// Time zones define the relationship between absolute and civil times. Given an
-// absolute or civil time and a time zone, you can compute the other time:
-//
-//   Civil Time = F(Absolute Time, Time Zone)
-//   Absolute Time = G(Civil Time, Time Zone)
-//
-// The Abseil time library allows you to construct such civil times from
-// absolute times; consult time.h for such functionality.
-//
-// This library provides six classes for constructing civil-time objects, and
-// provides several helper functions for rounding, iterating, and performing
-// arithmetic on civil-time objects, while avoiding complications like
-// daylight-saving time (DST):
-//
-//   * `absl::CivilSecond`
-//   * `absl::CivilMinute`
-//   * `absl::CivilHour`
-//   * `absl::CivilDay`
-//   * `absl::CivilMonth`
-//   * `absl::CivilYear`
-//
-// Example:
-//
-//   // Construct a civil-time object for a specific day
-//   const absl::CivilDay cd(1969, 07, 20);
-//
-//   // Construct a civil-time object for a specific second
-//   const absl::CivilSecond cd(2018, 8, 1, 12, 0, 1);
-//
-// Note: In C++14 and later, this library is usable in a constexpr context.
-//
-// Example:
-//
-//   // Valid in C++14
-//   constexpr absl::CivilDay cd(1969, 07, 20);
-
-#ifndef ABSL_TIME_CIVIL_TIME_H_
-#define ABSL_TIME_CIVIL_TIME_H_
-
-#include <string>
-
-#include "absl/strings/string_view.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace time_internal {
-struct second_tag : cctz::detail::second_tag {};
-struct minute_tag : second_tag, cctz::detail::minute_tag {};
-struct hour_tag : minute_tag, cctz::detail::hour_tag {};
-struct day_tag : hour_tag, cctz::detail::day_tag {};
-struct month_tag : day_tag, cctz::detail::month_tag {};
-struct year_tag : month_tag, cctz::detail::year_tag {};
-}  // namespace time_internal
-
-// -----------------------------------------------------------------------------
-// CivilSecond, CivilMinute, CivilHour, CivilDay, CivilMonth, CivilYear
-// -----------------------------------------------------------------------------
-//
-// Each of these civil-time types is a simple value type with the same
-// interface for construction and the same six accessors for each of the civil
-// time fields (year, month, day, hour, minute, and second, aka YMDHMS). These
-// classes differ only in their alignment, which is indicated by the type name
-// and specifies the field on which arithmetic operates.
-//
-// CONSTRUCTION
-//
-// Each of the civil-time types can be constructed in two ways: by directly
-// passing to the constructor up to six integers representing the YMDHMS fields,
-// or by copying the YMDHMS fields from a differently aligned civil-time type.
-// Omitted fields are assigned their minimum valid value. Hours, minutes, and
-// seconds will be set to 0, month and day will be set to 1. Since there is no
-// minimum year, the default is 1970.
-//
-// Examples:
-//
-//   absl::CivilDay default_value;               // 1970-01-01 00:00:00
-//
-//   absl::CivilDay a(2015, 2, 3);               // 2015-02-03 00:00:00
-//   absl::CivilDay b(2015, 2, 3, 4, 5, 6);      // 2015-02-03 00:00:00
-//   absl::CivilDay c(2015);                     // 2015-01-01 00:00:00
-//
-//   absl::CivilSecond ss(2015, 2, 3, 4, 5, 6);  // 2015-02-03 04:05:06
-//   absl::CivilMinute mm(ss);                   // 2015-02-03 04:05:00
-//   absl::CivilHour hh(mm);                     // 2015-02-03 04:00:00
-//   absl::CivilDay d(hh);                       // 2015-02-03 00:00:00
-//   absl::CivilMonth m(d);                      // 2015-02-01 00:00:00
-//   absl::CivilYear y(m);                       // 2015-01-01 00:00:00
-//
-//   m = absl::CivilMonth(y);                    // 2015-01-01 00:00:00
-//   d = absl::CivilDay(m);                      // 2015-01-01 00:00:00
-//   hh = absl::CivilHour(d);                    // 2015-01-01 00:00:00
-//   mm = absl::CivilMinute(hh);                 // 2015-01-01 00:00:00
-//   ss = absl::CivilSecond(mm);                 // 2015-01-01 00:00:00
-//
-// Each civil-time class is aligned to the civil-time field indicated in the
-// class's name after normalization. Alignment is performed by setting all the
-// inferior fields to their minimum valid value (as described above). The
-// following are examples of how each of the six types would align the fields
-// representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the
-// string format used here is not important; it's just a shorthand way of
-// showing the six YMDHMS fields.)
-//
-//   absl::CivilSecond   : 2015-11-22 12:34:56
-//   absl::CivilMinute   : 2015-11-22 12:34:00
-//   absl::CivilHour     : 2015-11-22 12:00:00
-//   absl::CivilDay      : 2015-11-22 00:00:00
-//   absl::CivilMonth    : 2015-11-01 00:00:00
-//   absl::CivilYear     : 2015-01-01 00:00:00
-//
-// Each civil-time type performs arithmetic on the field to which it is
-// aligned. This means that adding 1 to an absl::CivilDay increments the day
-// field (normalizing as necessary), and subtracting 7 from an absl::CivilMonth
-// operates on the month field (normalizing as necessary). All arithmetic
-// produces a valid civil time. Difference requires two similarly aligned
-// civil-time objects and returns the scalar answer in units of the objects'
-// alignment. For example, the difference between two absl::CivilHour objects
-// will give an answer in units of civil hours.
-//
-// ALIGNMENT CONVERSION
-//
-// The alignment of a civil-time object cannot change, but the object may be
-// used to construct a new object with a different alignment. This is referred
-// to as "realigning". When realigning to a type with the same or more
-// precision (e.g., absl::CivilDay -> absl::CivilSecond), the conversion may be
-// performed implicitly since no information is lost. However, if information
-// could be discarded (e.g., CivilSecond -> CivilDay), the conversion must
-// be explicit at the call site.
-//
-// Examples:
-//
-//   void UseDay(absl::CivilDay day);
-//
-//   absl::CivilSecond cs;
-//   UseDay(cs);                  // Won't compile because data may be discarded
-//   UseDay(absl::CivilDay(cs));  // OK: explicit conversion
-//
-//   absl::CivilDay cd;
-//   UseDay(cd);                  // OK: no conversion needed
-//
-//   absl::CivilMonth cm;
-//   UseDay(cm);                  // OK: implicit conversion to absl::CivilDay
-//
-// NORMALIZATION
-//
-// Normalization takes invalid values and adjusts them to produce valid values.
-// Within the civil-time library, integer arguments passed to the Civil*
-// constructors may be out-of-range, in which case they are normalized by
-// carrying overflow into a field of courser granularity to produce valid
-// civil-time objects. This normalization enables natural arithmetic on
-// constructor arguments without worrying about the field's range.
-//
-// Examples:
-//
-//   // Out-of-range; normalized to 2016-11-01
-//   absl::CivilDay d(2016, 10, 32);
-//   // Out-of-range, negative: normalized to 2016-10-30T23
-//   absl::CivilHour h1(2016, 10, 31, -1);
-//   // Normalization is cumulative: normalized to 2016-10-30T23
-//   absl::CivilHour h2(2016, 10, 32, -25);
-//
-// Note: If normalization is undesired, you can signal an error by comparing
-// the constructor arguments to the normalized values returned by the YMDHMS
-// properties.
-//
-// COMPARISON
-//
-// Comparison between civil-time objects considers all six YMDHMS fields,
-// regardless of the type's alignment. Comparison between differently aligned
-// civil-time types is allowed.
-//
-// Examples:
-//
-//   absl::CivilDay feb_3(2015, 2, 3);  // 2015-02-03 00:00:00
-//   absl::CivilDay mar_4(2015, 3, 4);  // 2015-03-04 00:00:00
-//   // feb_3 < mar_4
-//   // absl::CivilYear(feb_3) == absl::CivilYear(mar_4)
-//
-//   absl::CivilSecond feb_3_noon(2015, 2, 3, 12, 0, 0);  // 2015-02-03 12:00:00
-//   // feb_3 < feb_3_noon
-//   // feb_3 == absl::CivilDay(feb_3_noon)
-//
-//   // Iterates all the days of February 2015.
-//   for (absl::CivilDay d(2015, 2, 1); d < absl::CivilMonth(2015, 3); ++d) {
-//     // ...
-//   }
-//
-// ARITHMETIC
-//
-// Civil-time types support natural arithmetic operators such as addition,
-// subtraction, and difference. Arithmetic operates on the civil-time field
-// indicated in the type's name. Difference operators require arguments with
-// the same alignment and return the answer in units of the alignment.
-//
-// Example:
-//
-//   absl::CivilDay a(2015, 2, 3);
-//   ++a;                              // 2015-02-04 00:00:00
-//   --a;                              // 2015-02-03 00:00:00
-//   absl::CivilDay b = a + 1;         // 2015-02-04 00:00:00
-//   absl::CivilDay c = 1 + b;         // 2015-02-05 00:00:00
-//   int n = c - a;                    // n = 2 (civil days)
-//   int m = c - absl::CivilMonth(c);  // Won't compile: different types.
-//
-// ACCESSORS
-//
-// Each civil-time type has accessors for all six of the civil-time fields:
-// year, month, day, hour, minute, and second.
-//
-// civil_year_t year()
-// int          month()
-// int          day()
-// int          hour()
-// int          minute()
-// int          second()
-//
-// Recall that fields inferior to the type's alignment will be set to their
-// minimum valid value.
-//
-// Example:
-//
-//   absl::CivilDay d(2015, 6, 28);
-//   // d.year() == 2015
-//   // d.month() == 6
-//   // d.day() == 28
-//   // d.hour() == 0
-//   // d.minute() == 0
-//   // d.second() == 0
-//
-// CASE STUDY: Adding a month to January 31.
-//
-// One of the classic questions that arises when considering a civil time
-// library (or a date library or a date/time library) is this:
-//   "What is the result of adding a month to January 31?"
-// This is an interesting question because it is unclear what is meant by a
-// "month", and several different answers are possible, depending on context:
-//
-//   1. March 3 (or 2 if a leap year), if "add a month" means to add a month to
-//      the current month, and adjust the date to overflow the extra days into
-//      March. In this case the result of "February 31" would be normalized as
-//      within the civil-time library.
-//   2. February 28 (or 29 if a leap year), if "add a month" means to add a
-//      month, and adjust the date while holding the resulting month constant.
-//      In this case, the result of "February 31" would be truncated to the last
-//      day in February.
-//   3. An error. The caller may get some error, an exception, an invalid date
-//      object, or perhaps return `false`. This may make sense because there is
-//      no single unambiguously correct answer to the question.
-//
-// Practically speaking, any answer that is not what the programmer intended
-// is the wrong answer.
-//
-// The Abseil time library avoids this problem by making it impossible to
-// ask ambiguous questions. All civil-time objects are aligned to a particular
-// civil-field boundary (such as aligned to a year, month, day, hour, minute,
-// or second), and arithmetic operates on the field to which the object is
-// aligned. This means that in order to "add a month" the object must first be
-// aligned to a month boundary, which is equivalent to the first day of that
-// month.
-//
-// Of course, there are ways to compute an answer the question at hand using
-// this Abseil time library, but they require the programmer to be explicit
-// about the answer they expect. To illustrate, let's see how to compute all
-// three of the above possible answers to the question of "Jan 31 plus 1
-// month":
-//
-// Example:
-//
-//   const absl::CivilDay d(2015, 1, 31);
-//
-//   // Answer 1:
-//   // Add 1 to the month field in the constructor, and rely on normalization.
-//   const auto normalized = absl::CivilDay(d.year(), d.month() + 1, d.day());
-//   // normalized == 2015-03-03 (aka Feb 31)
-//
-//   // Answer 2:
-//   // Add 1 to month field, capping to the end of next month.
-//   const auto next_month = absl::CivilMonth(d) + 1;
-//   const auto last_day_of_next_month = absl::CivilDay(next_month + 1) - 1;
-//   const auto capped = std::min(normalized, last_day_of_next_month);
-//   // capped == 2015-02-28
-//
-//   // Answer 3:
-//   // Signal an error if the normalized answer is not in next month.
-//   if (absl::CivilMonth(normalized) != next_month) {
-//     // error, month overflow
-//   }
-//
-using CivilSecond =
-    time_internal::cctz::detail::civil_time<time_internal::second_tag>;
-using CivilMinute =
-    time_internal::cctz::detail::civil_time<time_internal::minute_tag>;
-using CivilHour =
-    time_internal::cctz::detail::civil_time<time_internal::hour_tag>;
-using CivilDay =
-    time_internal::cctz::detail::civil_time<time_internal::day_tag>;
-using CivilMonth =
-    time_internal::cctz::detail::civil_time<time_internal::month_tag>;
-using CivilYear =
-    time_internal::cctz::detail::civil_time<time_internal::year_tag>;
-
-// civil_year_t
-//
-// Type alias of a civil-time year value. This type is guaranteed to (at least)
-// support any year value supported by `time_t`.
-//
-// Example:
-//
-//   absl::CivilSecond cs = ...;
-//   absl::civil_year_t y = cs.year();
-//   cs = absl::CivilSecond(y, 1, 1, 0, 0, 0);  // CivilSecond(CivilYear(cs))
-//
-using civil_year_t = time_internal::cctz::year_t;
-
-// civil_diff_t
-//
-// Type alias of the difference between two civil-time values.
-// This type is used to indicate arguments that are not
-// normalized (such as parameters to the civil-time constructors), the results
-// of civil-time subtraction, or the operand to civil-time addition.
-//
-// Example:
-//
-//   absl::civil_diff_t n_sec = cs1 - cs2;             // cs1 == cs2 + n_sec;
-//
-using civil_diff_t = time_internal::cctz::diff_t;
-
-// Weekday::monday, Weekday::tuesday, Weekday::wednesday, Weekday::thursday,
-// Weekday::friday, Weekday::saturday, Weekday::sunday
-//
-// The Weekday enum class represents the civil-time concept of a "weekday" with
-// members for all days of the week.
-//
-//   absl::Weekday wd = absl::Weekday::thursday;
-//
-using Weekday = time_internal::cctz::weekday;
-
-// GetWeekday()
-//
-// Returns the absl::Weekday for the given (realigned) civil-time value.
-//
-// Example:
-//
-//   absl::CivilDay a(2015, 8, 13);
-//   absl::Weekday wd = absl::GetWeekday(a);  // wd == absl::Weekday::thursday
-//
-inline Weekday GetWeekday(CivilSecond cs) {
-  return time_internal::cctz::get_weekday(cs);
-}
-
-// NextWeekday()
-// PrevWeekday()
-//
-// Returns the absl::CivilDay that strictly follows or precedes a given
-// absl::CivilDay, and that falls on the given absl::Weekday.
-//
-// Example, given the following month:
-//
-//       August 2015
-//   Su Mo Tu We Th Fr Sa
-//                      1
-//    2  3  4  5  6  7  8
-//    9 10 11 12 13 14 15
-//   16 17 18 19 20 21 22
-//   23 24 25 26 27 28 29
-//   30 31
-//
-//   absl::CivilDay a(2015, 8, 13);
-//   // absl::GetWeekday(a) == absl::Weekday::thursday
-//   absl::CivilDay b = absl::NextWeekday(a, absl::Weekday::thursday);
-//   // b = 2015-08-20
-//   absl::CivilDay c = absl::PrevWeekday(a, absl::Weekday::thursday);
-//   // c = 2015-08-06
-//
-//   absl::CivilDay d = ...
-//   // Gets the following Thursday if d is not already Thursday
-//   absl::CivilDay thurs1 = absl::NextWeekday(d - 1, absl::Weekday::thursday);
-//   // Gets the previous Thursday if d is not already Thursday
-//   absl::CivilDay thurs2 = absl::PrevWeekday(d + 1, absl::Weekday::thursday);
-//
-inline CivilDay NextWeekday(CivilDay cd, Weekday wd) {
-  return CivilDay(time_internal::cctz::next_weekday(cd, wd));
-}
-inline CivilDay PrevWeekday(CivilDay cd, Weekday wd) {
-  return CivilDay(time_internal::cctz::prev_weekday(cd, wd));
-}
-
-// GetYearDay()
-//
-// Returns the day-of-year for the given (realigned) civil-time value.
-//
-// Example:
-//
-//   absl::CivilDay a(2015, 1, 1);
-//   int yd_jan_1 = absl::GetYearDay(a);   // yd_jan_1 = 1
-//   absl::CivilDay b(2015, 12, 31);
-//   int yd_dec_31 = absl::GetYearDay(b);  // yd_dec_31 = 365
-//
-inline int GetYearDay(CivilSecond cs) {
-  return time_internal::cctz::get_yearday(cs);
-}
-
-// FormatCivilTime()
-//
-// Formats the given civil-time value into a string value of the following
-// format:
-//
-//  Type        | Format
-//  ---------------------------------
-//  CivilSecond | YYYY-MM-DDTHH:MM:SS
-//  CivilMinute | YYYY-MM-DDTHH:MM
-//  CivilHour   | YYYY-MM-DDTHH
-//  CivilDay    | YYYY-MM-DD
-//  CivilMonth  | YYYY-MM
-//  CivilYear   | YYYY
-//
-// Example:
-//
-//   absl::CivilDay d = absl::CivilDay(1969, 7, 20);
-//   std::string day_string = absl::FormatCivilTime(d);  // "1969-07-20"
-//
-std::string FormatCivilTime(CivilSecond c);
-std::string FormatCivilTime(CivilMinute c);
-std::string FormatCivilTime(CivilHour c);
-std::string FormatCivilTime(CivilDay c);
-std::string FormatCivilTime(CivilMonth c);
-std::string FormatCivilTime(CivilYear c);
-
-// absl::ParseCivilTime()
-//
-// Parses a civil-time value from the specified `absl::string_view` into the
-// passed output parameter. Returns `true` upon successful parsing.
-//
-// The expected form of the input string is as follows:
-//
-//  Type        | Format
-//  ---------------------------------
-//  CivilSecond | YYYY-MM-DDTHH:MM:SS
-//  CivilMinute | YYYY-MM-DDTHH:MM
-//  CivilHour   | YYYY-MM-DDTHH
-//  CivilDay    | YYYY-MM-DD
-//  CivilMonth  | YYYY-MM
-//  CivilYear   | YYYY
-//
-// Example:
-//
-//   absl::CivilDay d;
-//   bool ok = absl::ParseCivilTime("2018-01-02", &d); // OK
-//
-// Note that parsing will fail if the string's format does not match the
-// expected type exactly. `ParseLenientCivilTime()` below is more lenient.
-//
-bool ParseCivilTime(absl::string_view s, CivilSecond* c);
-bool ParseCivilTime(absl::string_view s, CivilMinute* c);
-bool ParseCivilTime(absl::string_view s, CivilHour* c);
-bool ParseCivilTime(absl::string_view s, CivilDay* c);
-bool ParseCivilTime(absl::string_view s, CivilMonth* c);
-bool ParseCivilTime(absl::string_view s, CivilYear* c);
-
-// ParseLenientCivilTime()
-//
-// Parses any of the formats accepted by `absl::ParseCivilTime()`, but is more
-// lenient if the format of the string does not exactly match the associated
-// type.
-//
-// Example:
-//
-//   absl::CivilDay d;
-//   bool ok = absl::ParseLenientCivilTime("1969-07-20", &d); // OK
-//   ok = absl::ParseLenientCivilTime("1969-07-20T10", &d);   // OK: T10 floored
-//   ok = absl::ParseLenientCivilTime("1969-07", &d);   // OK: day defaults to 1
-//
-bool ParseLenientCivilTime(absl::string_view s, CivilSecond* c);
-bool ParseLenientCivilTime(absl::string_view s, CivilMinute* c);
-bool ParseLenientCivilTime(absl::string_view s, CivilHour* c);
-bool ParseLenientCivilTime(absl::string_view s, CivilDay* c);
-bool ParseLenientCivilTime(absl::string_view s, CivilMonth* c);
-bool ParseLenientCivilTime(absl::string_view s, CivilYear* c);
-
-namespace time_internal {  // For functions found via ADL on civil-time tags.
-
-// Streaming Operators
-//
-// Each civil-time type may be sent to an output stream using operator<<().
-// The result matches the string produced by `FormatCivilTime()`.
-//
-// Example:
-//
-//   absl::CivilDay d = absl::CivilDay(1969, 7, 20);
-//   std::cout << "Date is: " << d << "\n";
-//
-std::ostream& operator<<(std::ostream& os, CivilYear y);
-std::ostream& operator<<(std::ostream& os, CivilMonth m);
-std::ostream& operator<<(std::ostream& os, CivilDay d);
-std::ostream& operator<<(std::ostream& os, CivilHour h);
-std::ostream& operator<<(std::ostream& os, CivilMinute m);
-std::ostream& operator<<(std::ostream& os, CivilSecond s);
-
-}  // namespace time_internal
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_CIVIL_TIME_H_
diff --git a/third_party/abseil_cpp/absl/time/civil_time_benchmark.cc b/third_party/abseil_cpp/absl/time/civil_time_benchmark.cc
deleted file mode 100644
index f04dbe200e..0000000000
--- a/third_party/abseil_cpp/absl/time/civil_time_benchmark.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/civil_time.h"
-
-#include <numeric>
-#include <vector>
-
-#include "absl/hash/hash.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-// Run on (12 X 3492 MHz CPUs); 2018-11-05T13:44:29.814239103-08:00
-// CPU: Intel Haswell with HyperThreading (6 cores) dL1:32KB dL2:256KB dL3:15MB
-// Benchmark                 Time(ns)        CPU(ns)     Iterations
-// ----------------------------------------------------------------
-// BM_Difference_Days              14.5           14.5     48531105
-// BM_Step_Days                    12.6           12.6     54876006
-// BM_Format                      587            587        1000000
-// BM_Parse                       692            692        1000000
-// BM_RoundTripFormatParse       1309           1309         532075
-// BM_CivilYearAbslHash             0.710          0.710  976400000
-// BM_CivilMonthAbslHash            1.13           1.13   619500000
-// BM_CivilDayAbslHash              1.70           1.70   426000000
-// BM_CivilHourAbslHash             2.45           2.45   287600000
-// BM_CivilMinuteAbslHash           3.21           3.21   226200000
-// BM_CivilSecondAbslHash           4.10           4.10   171800000
-
-void BM_Difference_Days(benchmark::State& state) {
-  const absl::CivilDay c(2014, 8, 22);
-  const absl::CivilDay epoch(1970, 1, 1);
-  while (state.KeepRunning()) {
-    const absl::civil_diff_t n = c - epoch;
-    benchmark::DoNotOptimize(n);
-  }
-}
-BENCHMARK(BM_Difference_Days);
-
-void BM_Step_Days(benchmark::State& state) {
-  const absl::CivilDay kStart(2014, 8, 22);
-  absl::CivilDay c = kStart;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(++c);
-  }
-}
-BENCHMARK(BM_Step_Days);
-
-void BM_Format(benchmark::State& state) {
-  const absl::CivilSecond c(2014, 1, 2, 3, 4, 5);
-  while (state.KeepRunning()) {
-    const std::string s = absl::FormatCivilTime(c);
-    benchmark::DoNotOptimize(s);
-  }
-}
-BENCHMARK(BM_Format);
-
-void BM_Parse(benchmark::State& state) {
-  const std::string f = "2014-01-02T03:04:05";
-  absl::CivilSecond c;
-  while (state.KeepRunning()) {
-    const bool b = absl::ParseCivilTime(f, &c);
-    benchmark::DoNotOptimize(b);
-  }
-}
-BENCHMARK(BM_Parse);
-
-void BM_RoundTripFormatParse(benchmark::State& state) {
-  const absl::CivilSecond c(2014, 1, 2, 3, 4, 5);
-  absl::CivilSecond out;
-  while (state.KeepRunning()) {
-    const bool b = absl::ParseCivilTime(absl::FormatCivilTime(c), &out);
-    benchmark::DoNotOptimize(b);
-  }
-}
-BENCHMARK(BM_RoundTripFormatParse);
-
-template <typename T>
-void BM_CivilTimeAbslHash(benchmark::State& state) {
-  const int kSize = 100000;
-  std::vector<T> civil_times(kSize);
-  std::iota(civil_times.begin(), civil_times.end(), T(2018));
-
-  absl::Hash<T> absl_hasher;
-  while (state.KeepRunningBatch(kSize)) {
-    for (const T civil_time : civil_times) {
-      benchmark::DoNotOptimize(absl_hasher(civil_time));
-    }
-  }
-}
-void BM_CivilYearAbslHash(benchmark::State& state) {
-  BM_CivilTimeAbslHash<absl::CivilYear>(state);
-}
-void BM_CivilMonthAbslHash(benchmark::State& state) {
-  BM_CivilTimeAbslHash<absl::CivilMonth>(state);
-}
-void BM_CivilDayAbslHash(benchmark::State& state) {
-  BM_CivilTimeAbslHash<absl::CivilDay>(state);
-}
-void BM_CivilHourAbslHash(benchmark::State& state) {
-  BM_CivilTimeAbslHash<absl::CivilHour>(state);
-}
-void BM_CivilMinuteAbslHash(benchmark::State& state) {
-  BM_CivilTimeAbslHash<absl::CivilMinute>(state);
-}
-void BM_CivilSecondAbslHash(benchmark::State& state) {
-  BM_CivilTimeAbslHash<absl::CivilSecond>(state);
-}
-BENCHMARK(BM_CivilYearAbslHash);
-BENCHMARK(BM_CivilMonthAbslHash);
-BENCHMARK(BM_CivilDayAbslHash);
-BENCHMARK(BM_CivilHourAbslHash);
-BENCHMARK(BM_CivilMinuteAbslHash);
-BENCHMARK(BM_CivilSecondAbslHash);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/civil_time_test.cc b/third_party/abseil_cpp/absl/time/civil_time_test.cc
deleted file mode 100644
index 0ebd97adbc..0000000000
--- a/third_party/abseil_cpp/absl/time/civil_time_test.cc
+++ /dev/null
@@ -1,1243 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/civil_time.h"
-
-#include <limits>
-#include <sstream>
-#include <type_traits>
-
-#include "absl/base/macros.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-TEST(CivilTime, DefaultConstruction) {
-  absl::CivilSecond ss;
-  EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(ss));
-
-  absl::CivilMinute mm;
-  EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(mm));
-
-  absl::CivilHour hh;
-  EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hh));
-
-  absl::CivilDay d;
-  EXPECT_EQ("1970-01-01", absl::FormatCivilTime(d));
-
-  absl::CivilMonth m;
-  EXPECT_EQ("1970-01", absl::FormatCivilTime(m));
-
-  absl::CivilYear y;
-  EXPECT_EQ("1970", absl::FormatCivilTime(y));
-}
-
-TEST(CivilTime, StructMember) {
-  struct S {
-    absl::CivilDay day;
-  };
-  S s = {};
-  EXPECT_EQ(absl::CivilDay{}, s.day);
-}
-
-TEST(CivilTime, FieldsConstruction) {
-  EXPECT_EQ("2015-01-02T03:04:05",
-            absl::FormatCivilTime(absl::CivilSecond(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01-02T03:04:00",
-            absl::FormatCivilTime(absl::CivilSecond(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01-02T03:00:00",
-            absl::FormatCivilTime(absl::CivilSecond(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01-02T00:00:00",
-            absl::FormatCivilTime(absl::CivilSecond(2015, 1, 2)));
-  EXPECT_EQ("2015-01-01T00:00:00",
-            absl::FormatCivilTime(absl::CivilSecond(2015, 1)));
-  EXPECT_EQ("2015-01-01T00:00:00",
-            absl::FormatCivilTime(absl::CivilSecond(2015)));
-
-  EXPECT_EQ("2015-01-02T03:04",
-            absl::FormatCivilTime(absl::CivilMinute(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01-02T03:04",
-            absl::FormatCivilTime(absl::CivilMinute(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01-02T03:00",
-            absl::FormatCivilTime(absl::CivilMinute(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01-02T00:00",
-            absl::FormatCivilTime(absl::CivilMinute(2015, 1, 2)));
-  EXPECT_EQ("2015-01-01T00:00",
-            absl::FormatCivilTime(absl::CivilMinute(2015, 1)));
-  EXPECT_EQ("2015-01-01T00:00",
-            absl::FormatCivilTime(absl::CivilMinute(2015)));
-
-  EXPECT_EQ("2015-01-02T03",
-            absl::FormatCivilTime(absl::CivilHour(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01-02T03",
-            absl::FormatCivilTime(absl::CivilHour(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01-02T03",
-            absl::FormatCivilTime(absl::CivilHour(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01-02T00",
-            absl::FormatCivilTime(absl::CivilHour(2015, 1, 2)));
-  EXPECT_EQ("2015-01-01T00",
-            absl::FormatCivilTime(absl::CivilHour(2015, 1)));
-  EXPECT_EQ("2015-01-01T00",
-            absl::FormatCivilTime(absl::CivilHour(2015)));
-
-  EXPECT_EQ("2015-01-02",
-            absl::FormatCivilTime(absl::CivilDay(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01-02",
-            absl::FormatCivilTime(absl::CivilDay(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01-02",
-            absl::FormatCivilTime(absl::CivilDay(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01-02",
-            absl::FormatCivilTime(absl::CivilDay(2015, 1, 2)));
-  EXPECT_EQ("2015-01-01",
-            absl::FormatCivilTime(absl::CivilDay(2015, 1)));
-  EXPECT_EQ("2015-01-01",
-            absl::FormatCivilTime(absl::CivilDay(2015)));
-
-  EXPECT_EQ("2015-01",
-            absl::FormatCivilTime(absl::CivilMonth(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01",
-            absl::FormatCivilTime(absl::CivilMonth(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01",
-            absl::FormatCivilTime(absl::CivilMonth(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01",
-            absl::FormatCivilTime(absl::CivilMonth(2015, 1, 2)));
-  EXPECT_EQ("2015-01",
-            absl::FormatCivilTime(absl::CivilMonth(2015, 1)));
-  EXPECT_EQ("2015-01",
-            absl::FormatCivilTime(absl::CivilMonth(2015)));
-
-  EXPECT_EQ("2015",
-            absl::FormatCivilTime(absl::CivilYear(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015",
-            absl::FormatCivilTime(absl::CivilYear(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015",
-            absl::FormatCivilTime(absl::CivilYear(2015, 1, 2, 3)));
-  EXPECT_EQ("2015",
-            absl::FormatCivilTime(absl::CivilYear(2015, 1, 2)));
-  EXPECT_EQ("2015",
-            absl::FormatCivilTime(absl::CivilYear(2015, 1)));
-  EXPECT_EQ("2015",
-            absl::FormatCivilTime(absl::CivilYear(2015)));
-}
-
-TEST(CivilTime, FieldsConstructionLimits) {
-  const int kIntMax = std::numeric_limits<int>::max();
-  EXPECT_EQ("2038-01-19T03:14:07",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, 1, 1, 0, 0, kIntMax)));
-  EXPECT_EQ("6121-02-11T05:21:07",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, 1, 1, 0, kIntMax, kIntMax)));
-  EXPECT_EQ("251104-11-20T12:21:07",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, 1, 1, kIntMax, kIntMax, kIntMax)));
-  EXPECT_EQ("6130715-05-30T12:21:07",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, 1, kIntMax, kIntMax, kIntMax, kIntMax)));
-  EXPECT_EQ("185087685-11-26T12:21:07",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, kIntMax, kIntMax, kIntMax, kIntMax, kIntMax)));
-
-  const int kIntMin = std::numeric_limits<int>::min();
-  EXPECT_EQ("1901-12-13T20:45:52",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, 1, 1, 0, 0, kIntMin)));
-  EXPECT_EQ("-2182-11-20T18:37:52",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, 1, 1, 0, kIntMin, kIntMin)));
-  EXPECT_EQ("-247165-02-11T10:37:52",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, 1, 1, kIntMin, kIntMin, kIntMin)));
-  EXPECT_EQ("-6126776-08-01T10:37:52",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, 1, kIntMin, kIntMin, kIntMin, kIntMin)));
-  EXPECT_EQ("-185083747-10-31T10:37:52",
-            absl::FormatCivilTime(absl::CivilSecond(
-                1970, kIntMin, kIntMin, kIntMin, kIntMin, kIntMin)));
-}
-
-TEST(CivilTime, RangeLimits) {
-  const absl::civil_year_t kYearMax =
-      std::numeric_limits<absl::civil_year_t>::max();
-  EXPECT_EQ(absl::CivilYear(kYearMax),
-            absl::CivilYear::max());
-  EXPECT_EQ(absl::CivilMonth(kYearMax, 12),
-            absl::CivilMonth::max());
-  EXPECT_EQ(absl::CivilDay(kYearMax, 12, 31),
-            absl::CivilDay::max());
-  EXPECT_EQ(absl::CivilHour(kYearMax, 12, 31, 23),
-            absl::CivilHour::max());
-  EXPECT_EQ(absl::CivilMinute(kYearMax, 12, 31, 23, 59),
-            absl::CivilMinute::max());
-  EXPECT_EQ(absl::CivilSecond(kYearMax, 12, 31, 23, 59, 59),
-            absl::CivilSecond::max());
-
-  const absl::civil_year_t kYearMin =
-      std::numeric_limits<absl::civil_year_t>::min();
-  EXPECT_EQ(absl::CivilYear(kYearMin),
-            absl::CivilYear::min());
-  EXPECT_EQ(absl::CivilMonth(kYearMin, 1),
-            absl::CivilMonth::min());
-  EXPECT_EQ(absl::CivilDay(kYearMin, 1, 1),
-            absl::CivilDay::min());
-  EXPECT_EQ(absl::CivilHour(kYearMin, 1, 1, 0),
-            absl::CivilHour::min());
-  EXPECT_EQ(absl::CivilMinute(kYearMin, 1, 1, 0, 0),
-            absl::CivilMinute::min());
-  EXPECT_EQ(absl::CivilSecond(kYearMin, 1, 1, 0, 0, 0),
-            absl::CivilSecond::min());
-}
-
-TEST(CivilTime, ImplicitCrossAlignment) {
-  absl::CivilYear year(2015);
-  absl::CivilMonth month = year;
-  absl::CivilDay day = month;
-  absl::CivilHour hour = day;
-  absl::CivilMinute minute = hour;
-  absl::CivilSecond second = minute;
-
-  second = year;
-  EXPECT_EQ(second, year);
-  second = month;
-  EXPECT_EQ(second, month);
-  second = day;
-  EXPECT_EQ(second, day);
-  second = hour;
-  EXPECT_EQ(second, hour);
-  second = minute;
-  EXPECT_EQ(second, minute);
-
-  minute = year;
-  EXPECT_EQ(minute, year);
-  minute = month;
-  EXPECT_EQ(minute, month);
-  minute = day;
-  EXPECT_EQ(minute, day);
-  minute = hour;
-  EXPECT_EQ(minute, hour);
-
-  hour = year;
-  EXPECT_EQ(hour, year);
-  hour = month;
-  EXPECT_EQ(hour, month);
-  hour = day;
-  EXPECT_EQ(hour, day);
-
-  day = year;
-  EXPECT_EQ(day, year);
-  day = month;
-  EXPECT_EQ(day, month);
-
-  month = year;
-  EXPECT_EQ(month, year);
-
-  // Ensures unsafe conversions are not allowed.
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilSecond, absl::CivilMinute>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilSecond, absl::CivilHour>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilSecond, absl::CivilDay>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilSecond, absl::CivilMonth>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilSecond, absl::CivilYear>::value));
-
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilMinute, absl::CivilHour>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilMinute, absl::CivilDay>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilMinute, absl::CivilMonth>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilMinute, absl::CivilYear>::value));
-
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilHour, absl::CivilDay>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilHour, absl::CivilMonth>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilHour, absl::CivilYear>::value));
-
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilDay, absl::CivilMonth>::value));
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilDay, absl::CivilYear>::value));
-
-  EXPECT_FALSE(
-      (std::is_convertible<absl::CivilMonth, absl::CivilYear>::value));
-}
-
-TEST(CivilTime, ExplicitCrossAlignment) {
-  //
-  // Assign from smaller units -> larger units
-  //
-
-  absl::CivilSecond second(2015, 1, 2, 3, 4, 5);
-  EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(second));
-
-  absl::CivilMinute minute(second);
-  EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(minute));
-
-  absl::CivilHour hour(minute);
-  EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hour));
-
-  absl::CivilDay day(hour);
-  EXPECT_EQ("2015-01-02", absl::FormatCivilTime(day));
-
-  absl::CivilMonth month(day);
-  EXPECT_EQ("2015-01", absl::FormatCivilTime(month));
-
-  absl::CivilYear year(month);
-  EXPECT_EQ("2015", absl::FormatCivilTime(year));
-
-  //
-  // Now assign from larger units -> smaller units
-  //
-
-  month = absl::CivilMonth(year);
-  EXPECT_EQ("2015-01", absl::FormatCivilTime(month));
-
-  day = absl::CivilDay(month);
-  EXPECT_EQ("2015-01-01", absl::FormatCivilTime(day));
-
-  hour = absl::CivilHour(day);
-  EXPECT_EQ("2015-01-01T00", absl::FormatCivilTime(hour));
-
-  minute = absl::CivilMinute(hour);
-  EXPECT_EQ("2015-01-01T00:00", absl::FormatCivilTime(minute));
-
-  second = absl::CivilSecond(minute);
-  EXPECT_EQ("2015-01-01T00:00:00", absl::FormatCivilTime(second));
-}
-
-// Metafunction to test whether difference is allowed between two types.
-template <typename T1, typename T2>
-struct HasDiff {
-  template <typename U1, typename U2>
-  static std::false_type test(...);
-  template <typename U1, typename U2>
-  static std::true_type test(decltype(std::declval<U1>() - std::declval<U2>()));
-  static constexpr bool value = decltype(test<T1, T2>(0))::value;
-};
-
-TEST(CivilTime, DisallowCrossAlignedDifference) {
-  // Difference is allowed between types with the same alignment.
-  static_assert(HasDiff<absl::CivilSecond, absl::CivilSecond>::value, "");
-  static_assert(HasDiff<absl::CivilMinute, absl::CivilMinute>::value, "");
-  static_assert(HasDiff<absl::CivilHour, absl::CivilHour>::value, "");
-  static_assert(HasDiff<absl::CivilDay, absl::CivilDay>::value, "");
-  static_assert(HasDiff<absl::CivilMonth, absl::CivilMonth>::value, "");
-  static_assert(HasDiff<absl::CivilYear, absl::CivilYear>::value, "");
-
-  // Difference is disallowed between types with different alignments.
-  static_assert(!HasDiff<absl::CivilSecond, absl::CivilMinute>::value, "");
-  static_assert(!HasDiff<absl::CivilSecond, absl::CivilHour>::value, "");
-  static_assert(!HasDiff<absl::CivilSecond, absl::CivilDay>::value, "");
-  static_assert(!HasDiff<absl::CivilSecond, absl::CivilMonth>::value, "");
-  static_assert(!HasDiff<absl::CivilSecond, absl::CivilYear>::value, "");
-
-  static_assert(!HasDiff<absl::CivilMinute, absl::CivilHour>::value, "");
-  static_assert(!HasDiff<absl::CivilMinute, absl::CivilDay>::value, "");
-  static_assert(!HasDiff<absl::CivilMinute, absl::CivilMonth>::value, "");
-  static_assert(!HasDiff<absl::CivilMinute, absl::CivilYear>::value, "");
-
-  static_assert(!HasDiff<absl::CivilHour, absl::CivilDay>::value, "");
-  static_assert(!HasDiff<absl::CivilHour, absl::CivilMonth>::value, "");
-  static_assert(!HasDiff<absl::CivilHour, absl::CivilYear>::value, "");
-
-  static_assert(!HasDiff<absl::CivilDay, absl::CivilMonth>::value, "");
-  static_assert(!HasDiff<absl::CivilDay, absl::CivilYear>::value, "");
-
-  static_assert(!HasDiff<absl::CivilMonth, absl::CivilYear>::value, "");
-}
-
-TEST(CivilTime, ValueSemantics) {
-  const absl::CivilHour a(2015, 1, 2, 3);
-  const absl::CivilHour b = a;
-  const absl::CivilHour c(b);
-  absl::CivilHour d;
-  d = c;
-  EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(d));
-}
-
-TEST(CivilTime, Relational) {
-  // Tests that the alignment unit is ignored in comparison.
-  const absl::CivilYear year(2014);
-  const absl::CivilMonth month(year);
-  EXPECT_EQ(year, month);
-
-#define TEST_RELATIONAL(OLDER, YOUNGER) \
-  do {                                  \
-    EXPECT_FALSE(OLDER < OLDER);        \
-    EXPECT_FALSE(OLDER > OLDER);        \
-    EXPECT_TRUE(OLDER >= OLDER);        \
-    EXPECT_TRUE(OLDER <= OLDER);        \
-    EXPECT_FALSE(YOUNGER < YOUNGER);    \
-    EXPECT_FALSE(YOUNGER > YOUNGER);    \
-    EXPECT_TRUE(YOUNGER >= YOUNGER);    \
-    EXPECT_TRUE(YOUNGER <= YOUNGER);    \
-    EXPECT_EQ(OLDER, OLDER);            \
-    EXPECT_NE(OLDER, YOUNGER);          \
-    EXPECT_LT(OLDER, YOUNGER);          \
-    EXPECT_LE(OLDER, YOUNGER);          \
-    EXPECT_GT(YOUNGER, OLDER);          \
-    EXPECT_GE(YOUNGER, OLDER);          \
-  } while (0)
-
-  // Alignment is ignored in comparison (verified above), so CivilSecond is
-  // used to test comparison in all field positions.
-  TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 0, 0, 0),
-                  absl::CivilSecond(2015, 1, 1, 0, 0, 0));
-  TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 0, 0, 0),
-                  absl::CivilSecond(2014, 2, 1, 0, 0, 0));
-  TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 0, 0, 0),
-                  absl::CivilSecond(2014, 1, 2, 0, 0, 0));
-  TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 0, 0, 0),
-                  absl::CivilSecond(2014, 1, 1, 1, 0, 0));
-  TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 1, 0, 0),
-                  absl::CivilSecond(2014, 1, 1, 1, 1, 0));
-  TEST_RELATIONAL(absl::CivilSecond(2014, 1, 1, 1, 1, 0),
-                  absl::CivilSecond(2014, 1, 1, 1, 1, 1));
-
-  // Tests the relational operators of two different civil-time types.
-  TEST_RELATIONAL(absl::CivilDay(2014, 1, 1),
-                  absl::CivilMinute(2014, 1, 1, 1, 1));
-  TEST_RELATIONAL(absl::CivilDay(2014, 1, 1),
-                  absl::CivilMonth(2014, 2));
-
-#undef TEST_RELATIONAL
-}
-
-TEST(CivilTime, Arithmetic) {
-  absl::CivilSecond second(2015, 1, 2, 3, 4, 5);
-  EXPECT_EQ("2015-01-02T03:04:06", absl::FormatCivilTime(second += 1));
-  EXPECT_EQ("2015-01-02T03:04:07", absl::FormatCivilTime(second + 1));
-  EXPECT_EQ("2015-01-02T03:04:08", absl::FormatCivilTime(2 + second));
-  EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(second - 1));
-  EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(second -= 1));
-  EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(second++));
-  EXPECT_EQ("2015-01-02T03:04:07", absl::FormatCivilTime(++second));
-  EXPECT_EQ("2015-01-02T03:04:07", absl::FormatCivilTime(second--));
-  EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(--second));
-
-  absl::CivilMinute minute(2015, 1, 2, 3, 4);
-  EXPECT_EQ("2015-01-02T03:05", absl::FormatCivilTime(minute += 1));
-  EXPECT_EQ("2015-01-02T03:06", absl::FormatCivilTime(minute + 1));
-  EXPECT_EQ("2015-01-02T03:07", absl::FormatCivilTime(2 + minute));
-  EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(minute - 1));
-  EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(minute -= 1));
-  EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(minute++));
-  EXPECT_EQ("2015-01-02T03:06", absl::FormatCivilTime(++minute));
-  EXPECT_EQ("2015-01-02T03:06", absl::FormatCivilTime(minute--));
-  EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(--minute));
-
-  absl::CivilHour hour(2015, 1, 2, 3);
-  EXPECT_EQ("2015-01-02T04", absl::FormatCivilTime(hour += 1));
-  EXPECT_EQ("2015-01-02T05", absl::FormatCivilTime(hour + 1));
-  EXPECT_EQ("2015-01-02T06", absl::FormatCivilTime(2 + hour));
-  EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hour - 1));
-  EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hour -= 1));
-  EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hour++));
-  EXPECT_EQ("2015-01-02T05", absl::FormatCivilTime(++hour));
-  EXPECT_EQ("2015-01-02T05", absl::FormatCivilTime(hour--));
-  EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(--hour));
-
-  absl::CivilDay day(2015, 1, 2);
-  EXPECT_EQ("2015-01-03", absl::FormatCivilTime(day += 1));
-  EXPECT_EQ("2015-01-04", absl::FormatCivilTime(day + 1));
-  EXPECT_EQ("2015-01-05", absl::FormatCivilTime(2 + day));
-  EXPECT_EQ("2015-01-02", absl::FormatCivilTime(day - 1));
-  EXPECT_EQ("2015-01-02", absl::FormatCivilTime(day -= 1));
-  EXPECT_EQ("2015-01-02", absl::FormatCivilTime(day++));
-  EXPECT_EQ("2015-01-04", absl::FormatCivilTime(++day));
-  EXPECT_EQ("2015-01-04", absl::FormatCivilTime(day--));
-  EXPECT_EQ("2015-01-02", absl::FormatCivilTime(--day));
-
-  absl::CivilMonth month(2015, 1);
-  EXPECT_EQ("2015-02", absl::FormatCivilTime(month += 1));
-  EXPECT_EQ("2015-03", absl::FormatCivilTime(month + 1));
-  EXPECT_EQ("2015-04", absl::FormatCivilTime(2 + month));
-  EXPECT_EQ("2015-01", absl::FormatCivilTime(month - 1));
-  EXPECT_EQ("2015-01", absl::FormatCivilTime(month -= 1));
-  EXPECT_EQ("2015-01", absl::FormatCivilTime(month++));
-  EXPECT_EQ("2015-03", absl::FormatCivilTime(++month));
-  EXPECT_EQ("2015-03", absl::FormatCivilTime(month--));
-  EXPECT_EQ("2015-01", absl::FormatCivilTime(--month));
-
-  absl::CivilYear year(2015);
-  EXPECT_EQ("2016", absl::FormatCivilTime(year += 1));
-  EXPECT_EQ("2017", absl::FormatCivilTime(year + 1));
-  EXPECT_EQ("2018", absl::FormatCivilTime(2 + year));
-  EXPECT_EQ("2015", absl::FormatCivilTime(year - 1));
-  EXPECT_EQ("2015", absl::FormatCivilTime(year -= 1));
-  EXPECT_EQ("2015", absl::FormatCivilTime(year++));
-  EXPECT_EQ("2017", absl::FormatCivilTime(++year));
-  EXPECT_EQ("2017", absl::FormatCivilTime(year--));
-  EXPECT_EQ("2015", absl::FormatCivilTime(--year));
-}
-
-TEST(CivilTime, ArithmeticLimits) {
-  const int kIntMax = std::numeric_limits<int>::max();
-  const int kIntMin = std::numeric_limits<int>::min();
-
-  absl::CivilSecond second(1970, 1, 1, 0, 0, 0);
-  second += kIntMax;
-  EXPECT_EQ("2038-01-19T03:14:07", absl::FormatCivilTime(second));
-  second -= kIntMax;
-  EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(second));
-  second += kIntMin;
-  EXPECT_EQ("1901-12-13T20:45:52", absl::FormatCivilTime(second));
-  second -= kIntMin;
-  EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(second));
-
-  absl::CivilMinute minute(1970, 1, 1, 0, 0);
-  minute += kIntMax;
-  EXPECT_EQ("6053-01-23T02:07", absl::FormatCivilTime(minute));
-  minute -= kIntMax;
-  EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(minute));
-  minute += kIntMin;
-  EXPECT_EQ("-2114-12-08T21:52", absl::FormatCivilTime(minute));
-  minute -= kIntMin;
-  EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(minute));
-
-  absl::CivilHour hour(1970, 1, 1, 0);
-  hour += kIntMax;
-  EXPECT_EQ("246953-10-09T07", absl::FormatCivilTime(hour));
-  hour -= kIntMax;
-  EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hour));
-  hour += kIntMin;
-  EXPECT_EQ("-243014-03-24T16", absl::FormatCivilTime(hour));
-  hour -= kIntMin;
-  EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hour));
-
-  absl::CivilDay day(1970, 1, 1);
-  day += kIntMax;
-  EXPECT_EQ("5881580-07-11", absl::FormatCivilTime(day));
-  day -= kIntMax;
-  EXPECT_EQ("1970-01-01", absl::FormatCivilTime(day));
-  day += kIntMin;
-  EXPECT_EQ("-5877641-06-23", absl::FormatCivilTime(day));
-  day -= kIntMin;
-  EXPECT_EQ("1970-01-01", absl::FormatCivilTime(day));
-
-  absl::CivilMonth month(1970, 1);
-  month += kIntMax;
-  EXPECT_EQ("178958940-08", absl::FormatCivilTime(month));
-  month -= kIntMax;
-  EXPECT_EQ("1970-01", absl::FormatCivilTime(month));
-  month += kIntMin;
-  EXPECT_EQ("-178955001-05", absl::FormatCivilTime(month));
-  month -= kIntMin;
-  EXPECT_EQ("1970-01", absl::FormatCivilTime(month));
-
-  absl::CivilYear year(0);
-  year += kIntMax;
-  EXPECT_EQ("2147483647", absl::FormatCivilTime(year));
-  year -= kIntMax;
-  EXPECT_EQ("0", absl::FormatCivilTime(year));
-  year += kIntMin;
-  EXPECT_EQ("-2147483648", absl::FormatCivilTime(year));
-  year -= kIntMin;
-  EXPECT_EQ("0", absl::FormatCivilTime(year));
-}
-
-TEST(CivilTime, Difference) {
-  absl::CivilSecond second(2015, 1, 2, 3, 4, 5);
-  EXPECT_EQ(0, second - second);
-  EXPECT_EQ(10, (second + 10) - second);
-  EXPECT_EQ(-10, (second - 10) - second);
-
-  absl::CivilMinute minute(2015, 1, 2, 3, 4);
-  EXPECT_EQ(0, minute - minute);
-  EXPECT_EQ(10, (minute + 10) - minute);
-  EXPECT_EQ(-10, (minute - 10) - minute);
-
-  absl::CivilHour hour(2015, 1, 2, 3);
-  EXPECT_EQ(0, hour - hour);
-  EXPECT_EQ(10, (hour + 10) - hour);
-  EXPECT_EQ(-10, (hour - 10) - hour);
-
-  absl::CivilDay day(2015, 1, 2);
-  EXPECT_EQ(0, day - day);
-  EXPECT_EQ(10, (day + 10) - day);
-  EXPECT_EQ(-10, (day - 10) - day);
-
-  absl::CivilMonth month(2015, 1);
-  EXPECT_EQ(0, month - month);
-  EXPECT_EQ(10, (month + 10) - month);
-  EXPECT_EQ(-10, (month - 10) - month);
-
-  absl::CivilYear year(2015);
-  EXPECT_EQ(0, year - year);
-  EXPECT_EQ(10, (year + 10) - year);
-  EXPECT_EQ(-10, (year - 10) - year);
-}
-
-TEST(CivilTime, DifferenceLimits) {
-  const absl::civil_diff_t kDiffMax =
-      std::numeric_limits<absl::civil_diff_t>::max();
-  const absl::civil_diff_t kDiffMin =
-      std::numeric_limits<absl::civil_diff_t>::min();
-
-  // Check day arithmetic at the end of the year range.
-  const absl::CivilDay max_day(kDiffMax, 12, 31);
-  EXPECT_EQ(1, max_day - (max_day - 1));
-  EXPECT_EQ(-1, (max_day - 1) - max_day);
-
-  // Check day arithmetic at the start of the year range.
-  const absl::CivilDay min_day(kDiffMin, 1, 1);
-  EXPECT_EQ(1, (min_day + 1) - min_day);
-  EXPECT_EQ(-1, min_day - (min_day + 1));
-
-  // Check the limits of the return value.
-  const absl::CivilDay d1(1970, 1, 1);
-  const absl::CivilDay d2(25252734927768524, 7, 27);
-  EXPECT_EQ(kDiffMax, d2 - d1);
-  EXPECT_EQ(kDiffMin, d1 - (d2 + 1));
-}
-
-TEST(CivilTime, Properties) {
-  absl::CivilSecond ss(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, ss.year());
-  EXPECT_EQ(2, ss.month());
-  EXPECT_EQ(3, ss.day());
-  EXPECT_EQ(4, ss.hour());
-  EXPECT_EQ(5, ss.minute());
-  EXPECT_EQ(6, ss.second());
-  EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(ss));
-  EXPECT_EQ(34, absl::GetYearDay(ss));
-
-  absl::CivilMinute mm(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, mm.year());
-  EXPECT_EQ(2, mm.month());
-  EXPECT_EQ(3, mm.day());
-  EXPECT_EQ(4, mm.hour());
-  EXPECT_EQ(5, mm.minute());
-  EXPECT_EQ(0, mm.second());
-  EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(mm));
-  EXPECT_EQ(34, absl::GetYearDay(mm));
-
-  absl::CivilHour hh(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, hh.year());
-  EXPECT_EQ(2, hh.month());
-  EXPECT_EQ(3, hh.day());
-  EXPECT_EQ(4, hh.hour());
-  EXPECT_EQ(0, hh.minute());
-  EXPECT_EQ(0, hh.second());
-  EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(hh));
-  EXPECT_EQ(34, absl::GetYearDay(hh));
-
-  absl::CivilDay d(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, d.year());
-  EXPECT_EQ(2, d.month());
-  EXPECT_EQ(3, d.day());
-  EXPECT_EQ(0, d.hour());
-  EXPECT_EQ(0, d.minute());
-  EXPECT_EQ(0, d.second());
-  EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(d));
-  EXPECT_EQ(34, absl::GetYearDay(d));
-
-  absl::CivilMonth m(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, m.year());
-  EXPECT_EQ(2, m.month());
-  EXPECT_EQ(1, m.day());
-  EXPECT_EQ(0, m.hour());
-  EXPECT_EQ(0, m.minute());
-  EXPECT_EQ(0, m.second());
-  EXPECT_EQ(absl::Weekday::sunday, absl::GetWeekday(m));
-  EXPECT_EQ(32, absl::GetYearDay(m));
-
-  absl::CivilYear y(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, y.year());
-  EXPECT_EQ(1, y.month());
-  EXPECT_EQ(1, y.day());
-  EXPECT_EQ(0, y.hour());
-  EXPECT_EQ(0, y.minute());
-  EXPECT_EQ(0, y.second());
-  EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(y));
-  EXPECT_EQ(1, absl::GetYearDay(y));
-}
-
-TEST(CivilTime, Format) {
-  absl::CivilSecond ss;
-  EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(ss));
-
-  absl::CivilMinute mm;
-  EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(mm));
-
-  absl::CivilHour hh;
-  EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hh));
-
-  absl::CivilDay d;
-  EXPECT_EQ("1970-01-01", absl::FormatCivilTime(d));
-
-  absl::CivilMonth m;
-  EXPECT_EQ("1970-01", absl::FormatCivilTime(m));
-
-  absl::CivilYear y;
-  EXPECT_EQ("1970", absl::FormatCivilTime(y));
-}
-
-TEST(CivilTime, Parse) {
-  absl::CivilSecond ss;
-  absl::CivilMinute mm;
-  absl::CivilHour hh;
-  absl::CivilDay d;
-  absl::CivilMonth m;
-  absl::CivilYear y;
-
-  // CivilSecond OK; others fail
-  EXPECT_TRUE(absl::ParseCivilTime("2015-01-02T03:04:05", &ss));
-  EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(ss));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &mm));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &hh));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &d));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &m));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &y));
-
-  // CivilMinute OK; others fail
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &ss));
-  EXPECT_TRUE(absl::ParseCivilTime("2015-01-02T03:04", &mm));
-  EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(mm));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &hh));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &d));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &m));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &y));
-
-  // CivilHour OK; others fail
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &ss));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &mm));
-  EXPECT_TRUE(absl::ParseCivilTime("2015-01-02T03", &hh));
-  EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hh));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &d));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &m));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &y));
-
-  // CivilDay OK; others fail
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &ss));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &mm));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &hh));
-  EXPECT_TRUE(absl::ParseCivilTime("2015-01-02", &d));
-  EXPECT_EQ("2015-01-02", absl::FormatCivilTime(d));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &m));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &y));
-
-  // CivilMonth OK; others fail
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01", &ss));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01", &mm));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01", &hh));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01", &d));
-  EXPECT_TRUE(absl::ParseCivilTime("2015-01", &m));
-  EXPECT_EQ("2015-01", absl::FormatCivilTime(m));
-  EXPECT_FALSE(absl::ParseCivilTime("2015-01", &y));
-
-  // CivilYear OK; others fail
-  EXPECT_FALSE(absl::ParseCivilTime("2015", &ss));
-  EXPECT_FALSE(absl::ParseCivilTime("2015", &mm));
-  EXPECT_FALSE(absl::ParseCivilTime("2015", &hh));
-  EXPECT_FALSE(absl::ParseCivilTime("2015", &d));
-  EXPECT_FALSE(absl::ParseCivilTime("2015", &m));
-  EXPECT_TRUE(absl::ParseCivilTime("2015", &y));
-  EXPECT_EQ("2015", absl::FormatCivilTime(y));
-}
-
-TEST(CivilTime, FormatAndParseLenient) {
-  absl::CivilSecond ss;
-  EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(ss));
-
-  absl::CivilMinute mm;
-  EXPECT_EQ("1970-01-01T00:00", absl::FormatCivilTime(mm));
-
-  absl::CivilHour hh;
-  EXPECT_EQ("1970-01-01T00", absl::FormatCivilTime(hh));
-
-  absl::CivilDay d;
-  EXPECT_EQ("1970-01-01", absl::FormatCivilTime(d));
-
-  absl::CivilMonth m;
-  EXPECT_EQ("1970-01", absl::FormatCivilTime(m));
-
-  absl::CivilYear y;
-  EXPECT_EQ("1970", absl::FormatCivilTime(y));
-
-  EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &ss));
-  EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(ss));
-
-  EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &mm));
-  EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(mm));
-
-  EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &hh));
-  EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hh));
-
-  EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &d));
-  EXPECT_EQ("2015-01-02", absl::FormatCivilTime(d));
-
-  EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &m));
-  EXPECT_EQ("2015-01", absl::FormatCivilTime(m));
-
-  EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &y));
-  EXPECT_EQ("2015", absl::FormatCivilTime(y));
-}
-
-TEST(CivilTime, ParseEdgeCases) {
-  absl::CivilSecond ss;
-  EXPECT_TRUE(
-      absl::ParseLenientCivilTime("9223372036854775807-12-31T23:59:59", &ss));
-  EXPECT_EQ("9223372036854775807-12-31T23:59:59", absl::FormatCivilTime(ss));
-  EXPECT_TRUE(
-      absl::ParseLenientCivilTime("-9223372036854775808-01-01T00:00:00", &ss));
-  EXPECT_EQ("-9223372036854775808-01-01T00:00:00", absl::FormatCivilTime(ss));
-
-  absl::CivilMinute mm;
-  EXPECT_TRUE(
-      absl::ParseLenientCivilTime("9223372036854775807-12-31T23:59", &mm));
-  EXPECT_EQ("9223372036854775807-12-31T23:59", absl::FormatCivilTime(mm));
-  EXPECT_TRUE(
-      absl::ParseLenientCivilTime("-9223372036854775808-01-01T00:00", &mm));
-  EXPECT_EQ("-9223372036854775808-01-01T00:00", absl::FormatCivilTime(mm));
-
-  absl::CivilHour hh;
-  EXPECT_TRUE(
-      absl::ParseLenientCivilTime("9223372036854775807-12-31T23", &hh));
-  EXPECT_EQ("9223372036854775807-12-31T23", absl::FormatCivilTime(hh));
-  EXPECT_TRUE(
-      absl::ParseLenientCivilTime("-9223372036854775808-01-01T00", &hh));
-  EXPECT_EQ("-9223372036854775808-01-01T00", absl::FormatCivilTime(hh));
-
-  absl::CivilDay d;
-  EXPECT_TRUE(absl::ParseLenientCivilTime("9223372036854775807-12-31", &d));
-  EXPECT_EQ("9223372036854775807-12-31", absl::FormatCivilTime(d));
-  EXPECT_TRUE(absl::ParseLenientCivilTime("-9223372036854775808-01-01", &d));
-  EXPECT_EQ("-9223372036854775808-01-01", absl::FormatCivilTime(d));
-
-  absl::CivilMonth m;
-  EXPECT_TRUE(absl::ParseLenientCivilTime("9223372036854775807-12", &m));
-  EXPECT_EQ("9223372036854775807-12", absl::FormatCivilTime(m));
-  EXPECT_TRUE(absl::ParseLenientCivilTime("-9223372036854775808-01", &m));
-  EXPECT_EQ("-9223372036854775808-01", absl::FormatCivilTime(m));
-
-  absl::CivilYear y;
-  EXPECT_TRUE(absl::ParseLenientCivilTime("9223372036854775807", &y));
-  EXPECT_EQ("9223372036854775807", absl::FormatCivilTime(y));
-  EXPECT_TRUE(absl::ParseLenientCivilTime("-9223372036854775808", &y));
-  EXPECT_EQ("-9223372036854775808", absl::FormatCivilTime(y));
-
-  // Tests some valid, but interesting, cases
-  EXPECT_TRUE(absl::ParseLenientCivilTime("0", &ss)) << ss;
-  EXPECT_EQ(absl::CivilYear(0), ss);
-  EXPECT_TRUE(absl::ParseLenientCivilTime("0-1", &ss)) << ss;
-  EXPECT_EQ(absl::CivilMonth(0, 1), ss);
-  EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015 ", &ss)) << ss;
-  EXPECT_EQ(absl::CivilYear(2015), ss);
-  EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015-6 ", &ss)) << ss;
-  EXPECT_EQ(absl::CivilMonth(2015, 6), ss);
-  EXPECT_TRUE(absl::ParseLenientCivilTime("2015-6-7", &ss)) << ss;
-  EXPECT_EQ(absl::CivilDay(2015, 6, 7), ss);
-  EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015-6-7 ", &ss)) << ss;
-  EXPECT_EQ(absl::CivilDay(2015, 6, 7), ss);
-  EXPECT_TRUE(absl::ParseLenientCivilTime("2015-06-07T10:11:12 ", &ss)) << ss;
-  EXPECT_EQ(absl::CivilSecond(2015, 6, 7, 10, 11, 12), ss);
-  EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015-06-07T10:11:12 ", &ss)) << ss;
-  EXPECT_EQ(absl::CivilSecond(2015, 6, 7, 10, 11, 12), ss);
-  EXPECT_TRUE(absl::ParseLenientCivilTime("-01-01", &ss)) << ss;
-  EXPECT_EQ(absl::CivilMonth(-1, 1), ss);
-
-  // Tests some invalid cases
-  EXPECT_FALSE(absl::ParseLenientCivilTime("01-01-2015", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("2015-", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("0xff-01", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-30T04:05:06", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-03T04:05:96", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("X2015-02-03T04:05:06", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-03T04:05:003", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("2015 -02-03T04:05:06", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-03-04:05:06", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("2015:02:03T04-05-06", &ss)) << ss;
-  EXPECT_FALSE(absl::ParseLenientCivilTime("9223372036854775808", &y)) << y;
-}
-
-TEST(CivilTime, OutputStream) {
-  absl::CivilSecond cs(2016, 2, 3, 4, 5, 6);
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << absl::CivilYear(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016.................X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << absl::CivilMonth(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02..............X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << absl::CivilDay(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02-03...........X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << absl::CivilHour(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02-03T04........X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << absl::CivilMinute(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02-03T04:05.....X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << absl::CivilSecond(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02-03T04:05:06..X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << absl::Weekday::wednesday;
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..Wednesday............X..", ss.str());
-  }
-}
-
-TEST(CivilTime, Weekday) {
-  absl::CivilDay d(1970, 1, 1);
-  EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(d)) << d;
-
-  // We used to get this wrong for years < -30.
-  d = absl::CivilDay(-31, 12, 24);
-  EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(d)) << d;
-}
-
-TEST(CivilTime, NextPrevWeekday) {
-  // Jan 1, 1970 was a Thursday.
-  const absl::CivilDay thursday(1970, 1, 1);
-
-  // Thursday -> Thursday
-  absl::CivilDay d = absl::NextWeekday(thursday, absl::Weekday::thursday);
-  EXPECT_EQ(7, d - thursday) << d;
-  EXPECT_EQ(d - 14, absl::PrevWeekday(thursday, absl::Weekday::thursday));
-
-  // Thursday -> Friday
-  d = absl::NextWeekday(thursday, absl::Weekday::friday);
-  EXPECT_EQ(1, d - thursday) << d;
-  EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::friday));
-
-  // Thursday -> Saturday
-  d = absl::NextWeekday(thursday, absl::Weekday::saturday);
-  EXPECT_EQ(2, d - thursday) << d;
-  EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::saturday));
-
-  // Thursday -> Sunday
-  d = absl::NextWeekday(thursday, absl::Weekday::sunday);
-  EXPECT_EQ(3, d - thursday) << d;
-  EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::sunday));
-
-  // Thursday -> Monday
-  d = absl::NextWeekday(thursday, absl::Weekday::monday);
-  EXPECT_EQ(4, d - thursday) << d;
-  EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::monday));
-
-  // Thursday -> Tuesday
-  d = absl::NextWeekday(thursday, absl::Weekday::tuesday);
-  EXPECT_EQ(5, d - thursday) << d;
-  EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::tuesday));
-
-  // Thursday -> Wednesday
-  d = absl::NextWeekday(thursday, absl::Weekday::wednesday);
-  EXPECT_EQ(6, d - thursday) << d;
-  EXPECT_EQ(d - 7, absl::PrevWeekday(thursday, absl::Weekday::wednesday));
-}
-
-// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
-TEST(CivilTime, DifferenceWithHugeYear) {
-  absl::CivilDay d1(9223372036854775807, 1, 1);
-  absl::CivilDay d2(9223372036854775807, 12, 31);
-  EXPECT_EQ(364, d2 - d1);
-
-  d1 = absl::CivilDay(-9223372036854775807 - 1, 1, 1);
-  d2 = absl::CivilDay(-9223372036854775807 - 1, 12, 31);
-  EXPECT_EQ(365, d2 - d1);
-
-  // Check the limits of the return value at the end of the year range.
-  d1 = absl::CivilDay(9223372036854775807, 1, 1);
-  d2 = absl::CivilDay(9198119301927009252, 6, 6);
-  EXPECT_EQ(9223372036854775807, d1 - d2);
-  d2 = d2 - 1;
-  EXPECT_EQ(-9223372036854775807 - 1, d2 - d1);
-
-  // Check the limits of the return value at the start of the year range.
-  d1 = absl::CivilDay(-9223372036854775807 - 1, 1, 1);
-  d2 = absl::CivilDay(-9198119301927009254, 7, 28);
-  EXPECT_EQ(9223372036854775807, d2 - d1);
-  d2 = d2 + 1;
-  EXPECT_EQ(-9223372036854775807 - 1, d1 - d2);
-
-  // Check the limits of the return value from either side of year 0.
-  d1 = absl::CivilDay(-12626367463883278, 9, 3);
-  d2 = absl::CivilDay(12626367463883277, 3, 28);
-  EXPECT_EQ(9223372036854775807, d2 - d1);
-  d2 = d2 + 1;
-  EXPECT_EQ(-9223372036854775807 - 1, d1 - d2);
-}
-
-// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
-TEST(CivilTime, DifferenceNoIntermediateOverflow) {
-  // The difference up to the minute field would be below the minimum
-  // int64_t, but the 52 extra seconds brings us back to the minimum.
-  absl::CivilSecond s1(-292277022657, 1, 27, 8, 29 - 1, 52);
-  absl::CivilSecond s2(1970, 1, 1, 0, 0 - 1, 0);
-  EXPECT_EQ(-9223372036854775807 - 1, s1 - s2);
-
-  // The difference up to the minute field would be above the maximum
-  // int64_t, but the -53 extra seconds brings us back to the maximum.
-  s1 = absl::CivilSecond(292277026596, 12, 4, 15, 30, 7 - 7);
-  s2 = absl::CivilSecond(1970, 1, 1, 0, 0, 0 - 7);
-  EXPECT_EQ(9223372036854775807, s1 - s2);
-}
-
-TEST(CivilTime, NormalizeSimpleOverflow) {
-  absl::CivilSecond cs;
-  cs = absl::CivilSecond(2013, 11, 15, 16, 32, 59 + 1);
-  EXPECT_EQ("2013-11-15T16:33:00", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15, 16, 59 + 1, 14);
-  EXPECT_EQ("2013-11-15T17:00:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15, 23 + 1, 32, 14);
-  EXPECT_EQ("2013-11-16T00:32:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 30 + 1, 16, 32, 14);
-  EXPECT_EQ("2013-12-01T16:32:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 12 + 1, 15, 16, 32, 14);
-  EXPECT_EQ("2014-01-15T16:32:14", absl::FormatCivilTime(cs));
-}
-
-TEST(CivilTime, NormalizeSimpleUnderflow) {
-  absl::CivilSecond cs;
-  cs = absl::CivilSecond(2013, 11, 15, 16, 32, 0 - 1);
-  EXPECT_EQ("2013-11-15T16:31:59", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15, 16, 0 - 1, 14);
-  EXPECT_EQ("2013-11-15T15:59:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15, 0 - 1, 32, 14);
-  EXPECT_EQ("2013-11-14T23:32:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 1 - 1, 16, 32, 14);
-  EXPECT_EQ("2013-10-31T16:32:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 1 - 1, 15, 16, 32, 14);
-  EXPECT_EQ("2012-12-15T16:32:14", absl::FormatCivilTime(cs));
-}
-
-TEST(CivilTime, NormalizeMultipleOverflow) {
-  absl::CivilSecond cs(2013, 12, 31, 23, 59, 59 + 1);
-  EXPECT_EQ("2014-01-01T00:00:00", absl::FormatCivilTime(cs));
-}
-
-TEST(CivilTime, NormalizeMultipleUnderflow) {
-  absl::CivilSecond cs(2014, 1, 1, 0, 0, 0 - 1);
-  EXPECT_EQ("2013-12-31T23:59:59", absl::FormatCivilTime(cs));
-}
-
-TEST(CivilTime, NormalizeOverflowLimits) {
-  absl::CivilSecond cs;
-
-  const int kintmax = std::numeric_limits<int>::max();
-  cs = absl::CivilSecond(0, kintmax, kintmax, kintmax, kintmax, kintmax);
-  EXPECT_EQ("185085715-11-27T12:21:07", absl::FormatCivilTime(cs));
-
-  const int kintmin = std::numeric_limits<int>::min();
-  cs = absl::CivilSecond(0, kintmin, kintmin, kintmin, kintmin, kintmin);
-  EXPECT_EQ("-185085717-10-31T10:37:52", absl::FormatCivilTime(cs));
-}
-
-TEST(CivilTime, NormalizeComplexOverflow) {
-  absl::CivilSecond cs;
-  cs = absl::CivilSecond(2013, 11, 15, 16, 32, 14 + 123456789);
-  EXPECT_EQ("2017-10-14T14:05:23", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15, 16, 32 + 1234567, 14);
-  EXPECT_EQ("2016-03-22T00:39:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15, 16 + 123456, 32, 14);
-  EXPECT_EQ("2027-12-16T16:32:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15 + 1234, 16, 32, 14);
-  EXPECT_EQ("2017-04-02T16:32:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11 + 123, 15, 16, 32, 14);
-  EXPECT_EQ("2024-02-15T16:32:14", absl::FormatCivilTime(cs));
-}
-
-TEST(CivilTime, NormalizeComplexUnderflow) {
-  absl::CivilSecond cs;
-  cs = absl::CivilSecond(1999, 3, 0, 0, 0, 0);  // year 400
-  EXPECT_EQ("1999-02-28T00:00:00", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15, 16, 32, 14 - 123456789);
-  EXPECT_EQ("2009-12-17T18:59:05", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15, 16, 32 - 1234567, 14);
-  EXPECT_EQ("2011-07-12T08:25:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15, 16 - 123456, 32, 14);
-  EXPECT_EQ("1999-10-16T16:32:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11, 15 - 1234, 16, 32, 14);
-  EXPECT_EQ("2010-06-30T16:32:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11 - 123, 15, 16, 32, 14);
-  EXPECT_EQ("2003-08-15T16:32:14", absl::FormatCivilTime(cs));
-}
-
-TEST(CivilTime, NormalizeMishmash) {
-  absl::CivilSecond cs;
-  cs = absl::CivilSecond(2013, 11 - 123, 15 + 1234, 16 - 123456, 32 + 1234567,
-                         14 - 123456789);
-  EXPECT_EQ("1991-05-09T03:06:05", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11 + 123, 15 - 1234, 16 + 123456, 32 - 1234567,
-                         14 + 123456789);
-  EXPECT_EQ("2036-05-24T05:58:23", absl::FormatCivilTime(cs));
-
-  cs = absl::CivilSecond(2013, 11, -146097 + 1, 16, 32, 14);
-  EXPECT_EQ("1613-11-01T16:32:14", absl::FormatCivilTime(cs));
-  cs = absl::CivilSecond(2013, 11 + 400 * 12, -146097 + 1, 16, 32, 14);
-  EXPECT_EQ("2013-11-01T16:32:14", absl::FormatCivilTime(cs));
-}
-
-// Convert all the days from 1970-1-1 to 1970-1-146097 (aka 2369-12-31)
-// and check that they normalize to the expected time.  146097 days span
-// the 400-year Gregorian cycle used during normalization.
-TEST(CivilTime, NormalizeAllTheDays) {
-  absl::CivilDay expected(1970, 1, 1);
-  for (int day = 1; day <= 146097; ++day) {
-    absl::CivilSecond cs(1970, 1, day, 0, 0, 0);
-    EXPECT_EQ(expected, cs);
-    ++expected;
-  }
-}
-
-TEST(CivilTime, NormalizeWithHugeYear) {
-  absl::CivilMonth c(9223372036854775807, 1);
-  EXPECT_EQ("9223372036854775807-01", absl::FormatCivilTime(c));
-  c = c - 1;  // Causes normalization
-  EXPECT_EQ("9223372036854775806-12", absl::FormatCivilTime(c));
-
-  c = absl::CivilMonth(-9223372036854775807 - 1, 1);
-  EXPECT_EQ("-9223372036854775808-01", absl::FormatCivilTime(c));
-  c = c + 12;  // Causes normalization
-  EXPECT_EQ("-9223372036854775807-01", absl::FormatCivilTime(c));
-}
-
-TEST(CivilTime, LeapYears) {
-  const absl::CivilSecond s1(2013, 2, 28 + 1, 0, 0, 0);
-  EXPECT_EQ("2013-03-01T00:00:00", absl::FormatCivilTime(s1));
-
-  const absl::CivilSecond s2(2012, 2, 28 + 1, 0, 0, 0);
-  EXPECT_EQ("2012-02-29T00:00:00", absl::FormatCivilTime(s2));
-
-  const absl::CivilSecond s3(1900, 2, 28 + 1, 0, 0, 0);
-  EXPECT_EQ("1900-03-01T00:00:00", absl::FormatCivilTime(s3));
-
-  const struct {
-    int year;
-    int days;
-    struct {
-      int month;
-      int day;
-    } leap_day;  // The date of the day after Feb 28.
-  } kLeapYearTable[]{
-      {1900, 365, {3, 1}},
-      {1999, 365, {3, 1}},
-      {2000, 366, {2, 29}},  // leap year
-      {2001, 365, {3, 1}},
-      {2002, 365, {3, 1}},
-      {2003, 365, {3, 1}},
-      {2004, 366, {2, 29}},  // leap year
-      {2005, 365, {3, 1}},
-      {2006, 365, {3, 1}},
-      {2007, 365, {3, 1}},
-      {2008, 366, {2, 29}},  // leap year
-      {2009, 365, {3, 1}},
-      {2100, 365, {3, 1}},
-  };
-
-  for (int i = 0; i < ABSL_ARRAYSIZE(kLeapYearTable); ++i) {
-    const int y = kLeapYearTable[i].year;
-    const int m = kLeapYearTable[i].leap_day.month;
-    const int d = kLeapYearTable[i].leap_day.day;
-    const int n = kLeapYearTable[i].days;
-
-    // Tests incrementing through the leap day.
-    const absl::CivilDay feb28(y, 2, 28);
-    const absl::CivilDay next_day = feb28 + 1;
-    EXPECT_EQ(m, next_day.month());
-    EXPECT_EQ(d, next_day.day());
-
-    // Tests difference in days of leap years.
-    const absl::CivilYear year(feb28);
-    const absl::CivilYear next_year = year + 1;
-    EXPECT_EQ(n, absl::CivilDay(next_year) - absl::CivilDay(year));
-  }
-}
-
-TEST(CivilTime, FirstThursdayInMonth) {
-  const absl::CivilDay nov1(2014, 11, 1);
-  const absl::CivilDay thursday =
-      absl::NextWeekday(nov1 - 1, absl::Weekday::thursday);
-  EXPECT_EQ("2014-11-06", absl::FormatCivilTime(thursday));
-
-  // Bonus: Date of Thanksgiving in the United States
-  // Rule: Fourth Thursday of November
-  const absl::CivilDay thanksgiving = thursday +  7 * 3;
-  EXPECT_EQ("2014-11-27", absl::FormatCivilTime(thanksgiving));
-}
-
-TEST(CivilTime, DocumentationExample) {
-  absl::CivilSecond second(2015, 6, 28, 1, 2, 3);  // 2015-06-28 01:02:03
-  absl::CivilMinute minute(second);                // 2015-06-28 01:02:00
-  absl::CivilDay day(minute);                      // 2015-06-28 00:00:00
-
-  second -= 1;                    // 2015-06-28 01:02:02
-  --second;                       // 2015-06-28 01:02:01
-  EXPECT_EQ(minute, second - 1);  // Comparison between types
-  EXPECT_LT(minute, second);
-
-  // int diff = second - minute;  // ERROR: Mixed types, won't compile
-
-  absl::CivilDay june_1(2015, 6, 1);  // Pass fields to c'tor.
-  int diff = day - june_1;            // Num days between 'day' and June 1
-  EXPECT_EQ(27, diff);
-
-  // Fields smaller than alignment are floored to their minimum value.
-  absl::CivilDay day_floor(2015, 1, 2, 9, 9, 9);
-  EXPECT_EQ(0, day_floor.hour());  // 09:09:09 is floored
-  EXPECT_EQ(absl::CivilDay(2015, 1, 2), day_floor);
-
-  // Unspecified fields default to their minium value
-  absl::CivilDay day_default(2015);  // Defaults to Jan 1
-  EXPECT_EQ(absl::CivilDay(2015, 1, 1), day_default);
-
-  // Iterates all the days of June.
-  absl::CivilMonth june(day);  // CivilDay -> CivilMonth
-  absl::CivilMonth july = june + 1;
-  for (absl::CivilDay day = june_1; day < july; ++day) {
-    // ...
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/clock.cc b/third_party/abseil_cpp/absl/time/clock.cc
deleted file mode 100644
index 6862e011be..0000000000
--- a/third_party/abseil_cpp/absl/time/clock.cc
+++ /dev/null
@@ -1,567 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/clock.h"
-
-#include "absl/base/attributes.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-#include <algorithm>
-#include <atomic>
-#include <cerrno>
-#include <cstdint>
-#include <ctime>
-#include <limits>
-
-#include "absl/base/internal/spinlock.h"
-#include "absl/base/internal/unscaledcycleclock.h"
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/base/thread_annotations.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-Time Now() {
-  // TODO(bww): Get a timespec instead so we don't have to divide.
-  int64_t n = absl::GetCurrentTimeNanos();
-  if (n >= 0) {
-    return time_internal::FromUnixDuration(
-        time_internal::MakeDuration(n / 1000000000, n % 1000000000 * 4));
-  }
-  return time_internal::FromUnixDuration(absl::Nanoseconds(n));
-}
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// Decide if we should use the fast GetCurrentTimeNanos() algorithm
-// based on the cyclecounter, otherwise just get the time directly
-// from the OS on every call. This can be chosen at compile-time via
-// -DABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS=[0|1]
-#ifndef ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
-#if ABSL_USE_UNSCALED_CYCLECLOCK
-#define ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS 1
-#else
-#define ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS 0
-#endif
-#endif
-
-#if defined(__APPLE__) || defined(_WIN32)
-#include "absl/time/internal/get_current_time_chrono.inc"
-#else
-#include "absl/time/internal/get_current_time_posix.inc"
-#endif
-
-// Allows override by test.
-#ifndef GET_CURRENT_TIME_NANOS_FROM_SYSTEM
-#define GET_CURRENT_TIME_NANOS_FROM_SYSTEM() \
-  ::absl::time_internal::GetCurrentTimeNanosFromSystem()
-#endif
-
-#if !ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-int64_t GetCurrentTimeNanos() { return GET_CURRENT_TIME_NANOS_FROM_SYSTEM(); }
-ABSL_NAMESPACE_END
-}  // namespace absl
-#else  // Use the cyclecounter-based implementation below.
-
-// Allows override by test.
-#ifndef GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW
-#define GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW() \
-  ::absl::time_internal::UnscaledCycleClockWrapperForGetCurrentTime::Now()
-#endif
-
-// The following counters are used only by the test code.
-static int64_t stats_initializations;
-static int64_t stats_reinitializations;
-static int64_t stats_calibrations;
-static int64_t stats_slow_paths;
-static int64_t stats_fast_slow_paths;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-// This is a friend wrapper around UnscaledCycleClock::Now()
-// (needed to access UnscaledCycleClock).
-class UnscaledCycleClockWrapperForGetCurrentTime {
- public:
-  static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
-};
-}  // namespace time_internal
-
-// uint64_t is used in this module to provide an extra bit in multiplications
-
-// Return the time in ns as told by the kernel interface.  Place in *cycleclock
-// the value of the cycleclock at about the time of the syscall.
-// This call represents the time base that this module synchronizes to.
-// Ensures that *cycleclock does not step back by up to (1 << 16) from
-// last_cycleclock, to discard small backward counter steps.  (Larger steps are
-// assumed to be complete resyncs, which shouldn't happen.  If they do, a full
-// reinitialization of the outer algorithm should occur.)
-static int64_t GetCurrentTimeNanosFromKernel(uint64_t last_cycleclock,
-                                             uint64_t *cycleclock) {
-  // We try to read clock values at about the same time as the kernel clock.
-  // This value gets adjusted up or down as estimate of how long that should
-  // take, so we can reject attempts that take unusually long.
-  static std::atomic<uint64_t> approx_syscall_time_in_cycles{10 * 1000};
-
-  uint64_t local_approx_syscall_time_in_cycles =  // local copy
-      approx_syscall_time_in_cycles.load(std::memory_order_relaxed);
-
-  int64_t current_time_nanos_from_system;
-  uint64_t before_cycles;
-  uint64_t after_cycles;
-  uint64_t elapsed_cycles;
-  int loops = 0;
-  do {
-    before_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
-    current_time_nanos_from_system = GET_CURRENT_TIME_NANOS_FROM_SYSTEM();
-    after_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
-    // elapsed_cycles is unsigned, so is large on overflow
-    elapsed_cycles = after_cycles - before_cycles;
-    if (elapsed_cycles >= local_approx_syscall_time_in_cycles &&
-        ++loops == 20) {  // clock changed frequencies?  Back off.
-      loops = 0;
-      if (local_approx_syscall_time_in_cycles < 1000 * 1000) {
-        local_approx_syscall_time_in_cycles =
-            (local_approx_syscall_time_in_cycles + 1) << 1;
-      }
-      approx_syscall_time_in_cycles.store(
-          local_approx_syscall_time_in_cycles,
-          std::memory_order_relaxed);
-    }
-  } while (elapsed_cycles >= local_approx_syscall_time_in_cycles ||
-           last_cycleclock - after_cycles < (static_cast<uint64_t>(1) << 16));
-
-  // Number of times in a row we've seen a kernel time call take substantially
-  // less than approx_syscall_time_in_cycles.
-  static std::atomic<uint32_t> seen_smaller{ 0 };
-
-  // Adjust approx_syscall_time_in_cycles to be within a factor of 2
-  // of the typical time to execute one iteration of the loop above.
-  if ((local_approx_syscall_time_in_cycles >> 1) < elapsed_cycles) {
-    // measured time is no smaller than half current approximation
-    seen_smaller.store(0, std::memory_order_relaxed);
-  } else if (seen_smaller.fetch_add(1, std::memory_order_relaxed) >= 3) {
-    // smaller delays several times in a row; reduce approximation by 12.5%
-    const uint64_t new_approximation =
-        local_approx_syscall_time_in_cycles -
-        (local_approx_syscall_time_in_cycles >> 3);
-    approx_syscall_time_in_cycles.store(new_approximation,
-                                        std::memory_order_relaxed);
-    seen_smaller.store(0, std::memory_order_relaxed);
-  }
-
-  *cycleclock = after_cycles;
-  return current_time_nanos_from_system;
-}
-
-
-// ---------------------------------------------------------------------
-// An implementation of reader-write locks that use no atomic ops in the read
-// case.  This is a generalization of Lamport's method for reading a multiword
-// clock.  Increment a word on each write acquisition, using the low-order bit
-// as a spinlock; the word is the high word of the "clock".  Readers read the
-// high word, then all other data, then the high word again, and repeat the
-// read if the reads of the high words yields different answers, or an odd
-// value (either case suggests possible interference from a writer).
-// Here we use a spinlock to ensure only one writer at a time, rather than
-// spinning on the bottom bit of the word to benefit from SpinLock
-// spin-delay tuning.
-
-// Acquire seqlock (*seq) and return the value to be written to unlock.
-static inline uint64_t SeqAcquire(std::atomic<uint64_t> *seq) {
-  uint64_t x = seq->fetch_add(1, std::memory_order_relaxed);
-
-  // We put a release fence between update to *seq and writes to shared data.
-  // Thus all stores to shared data are effectively release operations and
-  // update to *seq above cannot be re-ordered past any of them.  Note that
-  // this barrier is not for the fetch_add above.  A release barrier for the
-  // fetch_add would be before it, not after.
-  std::atomic_thread_fence(std::memory_order_release);
-
-  return x + 2;   // original word plus 2
-}
-
-// Release seqlock (*seq) by writing x to it---a value previously returned by
-// SeqAcquire.
-static inline void SeqRelease(std::atomic<uint64_t> *seq, uint64_t x) {
-  // The unlock store to *seq must have release ordering so that all
-  // updates to shared data must finish before this store.
-  seq->store(x, std::memory_order_release);  // release lock for readers
-}
-
-// ---------------------------------------------------------------------
-
-// "nsscaled" is unit of time equal to a (2**kScale)th of a nanosecond.
-enum { kScale = 30 };
-
-// The minimum interval between samples of the time base.
-// We pick enough time to amortize the cost of the sample,
-// to get a reasonably accurate cycle counter rate reading,
-// and not so much that calculations will overflow 64-bits.
-static const uint64_t kMinNSBetweenSamples = 2000 << 20;
-
-// We require that kMinNSBetweenSamples shifted by kScale
-// have at least a bit left over for 64-bit calculations.
-static_assert(((kMinNSBetweenSamples << (kScale + 1)) >> (kScale + 1)) ==
-               kMinNSBetweenSamples,
-               "cannot represent kMaxBetweenSamplesNSScaled");
-
-// A reader-writer lock protecting the static locations below.
-// See SeqAcquire() and SeqRelease() above.
-ABSL_CONST_INIT static absl::base_internal::SpinLock lock(
-    absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
-ABSL_CONST_INIT static std::atomic<uint64_t> seq(0);
-
-// data from a sample of the kernel's time value
-struct TimeSampleAtomic {
-  std::atomic<uint64_t> raw_ns;              // raw kernel time
-  std::atomic<uint64_t> base_ns;             // our estimate of time
-  std::atomic<uint64_t> base_cycles;         // cycle counter reading
-  std::atomic<uint64_t> nsscaled_per_cycle;  // cycle period
-  // cycles before we'll sample again (a scaled reciprocal of the period,
-  // to avoid a division on the fast path).
-  std::atomic<uint64_t> min_cycles_per_sample;
-};
-// Same again, but with non-atomic types
-struct TimeSample {
-  uint64_t raw_ns;                 // raw kernel time
-  uint64_t base_ns;                // our estimate of time
-  uint64_t base_cycles;            // cycle counter reading
-  uint64_t nsscaled_per_cycle;     // cycle period
-  uint64_t min_cycles_per_sample;  // approx cycles before next sample
-};
-
-static struct TimeSampleAtomic last_sample;   // the last sample; under seq
-
-static int64_t GetCurrentTimeNanosSlowPath() ABSL_ATTRIBUTE_COLD;
-
-// Read the contents of *atomic into *sample.
-// Each field is read atomically, but to maintain atomicity between fields,
-// the access must be done under a lock.
-static void ReadTimeSampleAtomic(const struct TimeSampleAtomic *atomic,
-                                 struct TimeSample *sample) {
-  sample->base_ns = atomic->base_ns.load(std::memory_order_relaxed);
-  sample->base_cycles = atomic->base_cycles.load(std::memory_order_relaxed);
-  sample->nsscaled_per_cycle =
-      atomic->nsscaled_per_cycle.load(std::memory_order_relaxed);
-  sample->min_cycles_per_sample =
-      atomic->min_cycles_per_sample.load(std::memory_order_relaxed);
-  sample->raw_ns = atomic->raw_ns.load(std::memory_order_relaxed);
-}
-
-// Public routine.
-// Algorithm:  We wish to compute real time from a cycle counter.  In normal
-// operation, we construct a piecewise linear approximation to the kernel time
-// source, using the cycle counter value.  The start of each line segment is at
-// the same point as the end of the last, but may have a different slope (that
-// is, a different idea of the cycle counter frequency).  Every couple of
-// seconds, the kernel time source is sampled and compared with the current
-// approximation.  A new slope is chosen that, if followed for another couple
-// of seconds, will correct the error at the current position.  The information
-// for a sample is in the "last_sample" struct.  The linear approximation is
-//   estimated_time = last_sample.base_ns +
-//     last_sample.ns_per_cycle * (counter_reading - last_sample.base_cycles)
-// (ns_per_cycle is actually stored in different units and scaled, to avoid
-// overflow).  The base_ns of the next linear approximation is the
-// estimated_time using the last approximation; the base_cycles is the cycle
-// counter value at that time; the ns_per_cycle is the number of ns per cycle
-// measured since the last sample, but adjusted so that most of the difference
-// between the estimated_time and the kernel time will be corrected by the
-// estimated time to the next sample.  In normal operation, this algorithm
-// relies on:
-// - the cycle counter and kernel time rates not changing a lot in a few
-//   seconds.
-// - the client calling into the code often compared to a couple of seconds, so
-//   the time to the next correction can be estimated.
-// Any time ns_per_cycle is not known, a major error is detected, or the
-// assumption about frequent calls is violated, the implementation returns the
-// kernel time.  It records sufficient data that a linear approximation can
-// resume a little later.
-
-int64_t GetCurrentTimeNanos() {
-  // read the data from the "last_sample" struct (but don't need raw_ns yet)
-  // The reads of "seq" and test of the values emulate a reader lock.
-  uint64_t base_ns;
-  uint64_t base_cycles;
-  uint64_t nsscaled_per_cycle;
-  uint64_t min_cycles_per_sample;
-  uint64_t seq_read0;
-  uint64_t seq_read1;
-
-  // If we have enough information to interpolate, the value returned will be
-  // derived from this cycleclock-derived time estimate.  On some platforms
-  // (POWER) the function to retrieve this value has enough complexity to
-  // contribute to register pressure - reading it early before initializing
-  // the other pieces of the calculation minimizes spill/restore instructions,
-  // minimizing icache cost.
-  uint64_t now_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
-
-  // Acquire pairs with the barrier in SeqRelease - if this load sees that
-  // store, the shared-data reads necessarily see that SeqRelease's updates
-  // to the same shared data.
-  seq_read0 = seq.load(std::memory_order_acquire);
-
-  base_ns = last_sample.base_ns.load(std::memory_order_relaxed);
-  base_cycles = last_sample.base_cycles.load(std::memory_order_relaxed);
-  nsscaled_per_cycle =
-      last_sample.nsscaled_per_cycle.load(std::memory_order_relaxed);
-  min_cycles_per_sample =
-      last_sample.min_cycles_per_sample.load(std::memory_order_relaxed);
-
-  // This acquire fence pairs with the release fence in SeqAcquire.  Since it
-  // is sequenced between reads of shared data and seq_read1, the reads of
-  // shared data are effectively acquiring.
-  std::atomic_thread_fence(std::memory_order_acquire);
-
-  // The shared-data reads are effectively acquire ordered, and the
-  // shared-data writes are effectively release ordered. Therefore if our
-  // shared-data reads see any of a particular update's shared-data writes,
-  // seq_read1 is guaranteed to see that update's SeqAcquire.
-  seq_read1 = seq.load(std::memory_order_relaxed);
-
-  // Fast path.  Return if min_cycles_per_sample has not yet elapsed since the
-  // last sample, and we read a consistent sample.  The fast path activates
-  // only when min_cycles_per_sample is non-zero, which happens when we get an
-  // estimate for the cycle time.  The predicate will fail if now_cycles <
-  // base_cycles, or if some other thread is in the slow path.
-  //
-  // Since we now read now_cycles before base_ns, it is possible for now_cycles
-  // to be less than base_cycles (if we were interrupted between those loads and
-  // last_sample was updated). This is harmless, because delta_cycles will wrap
-  // and report a time much much bigger than min_cycles_per_sample. In that case
-  // we will take the slow path.
-  uint64_t delta_cycles = now_cycles - base_cycles;
-  if (seq_read0 == seq_read1 && (seq_read0 & 1) == 0 &&
-      delta_cycles < min_cycles_per_sample) {
-    return base_ns + ((delta_cycles * nsscaled_per_cycle) >> kScale);
-  }
-  return GetCurrentTimeNanosSlowPath();
-}
-
-// Return (a << kScale)/b.
-// Zero is returned if b==0.   Scaling is performed internally to
-// preserve precision without overflow.
-static uint64_t SafeDivideAndScale(uint64_t a, uint64_t b) {
-  // Find maximum safe_shift so that
-  //  0 <= safe_shift <= kScale  and  (a << safe_shift) does not overflow.
-  int safe_shift = kScale;
-  while (((a << safe_shift) >> safe_shift) != a) {
-    safe_shift--;
-  }
-  uint64_t scaled_b = b >> (kScale - safe_shift);
-  uint64_t quotient = 0;
-  if (scaled_b != 0) {
-    quotient = (a << safe_shift) / scaled_b;
-  }
-  return quotient;
-}
-
-static uint64_t UpdateLastSample(
-    uint64_t now_cycles, uint64_t now_ns, uint64_t delta_cycles,
-    const struct TimeSample *sample) ABSL_ATTRIBUTE_COLD;
-
-// The slow path of GetCurrentTimeNanos().  This is taken while gathering
-// initial samples, when enough time has elapsed since the last sample, and if
-// any other thread is writing to last_sample.
-//
-// Manually mark this 'noinline' to minimize stack frame size of the fast
-// path.  Without this, sometimes a compiler may inline this big block of code
-// into the fast path.  That causes lots of register spills and reloads that
-// are unnecessary unless the slow path is taken.
-//
-// TODO(absl-team): Remove this attribute when our compiler is smart enough
-// to do the right thing.
-ABSL_ATTRIBUTE_NOINLINE
-static int64_t GetCurrentTimeNanosSlowPath() ABSL_LOCKS_EXCLUDED(lock) {
-  // Serialize access to slow-path.  Fast-path readers are not blocked yet, and
-  // code below must not modify last_sample until the seqlock is acquired.
-  lock.Lock();
-
-  // Sample the kernel time base.  This is the definition of
-  // "now" if we take the slow path.
-  static uint64_t last_now_cycles;  // protected by lock
-  uint64_t now_cycles;
-  uint64_t now_ns = GetCurrentTimeNanosFromKernel(last_now_cycles, &now_cycles);
-  last_now_cycles = now_cycles;
-
-  uint64_t estimated_base_ns;
-
-  // ----------
-  // Read the "last_sample" values again; this time holding the write lock.
-  struct TimeSample sample;
-  ReadTimeSampleAtomic(&last_sample, &sample);
-
-  // ----------
-  // Try running the fast path again; another thread may have updated the
-  // sample between our run of the fast path and the sample we just read.
-  uint64_t delta_cycles = now_cycles - sample.base_cycles;
-  if (delta_cycles < sample.min_cycles_per_sample) {
-    // Another thread updated the sample.  This path does not take the seqlock
-    // so that blocked readers can make progress without blocking new readers.
-    estimated_base_ns = sample.base_ns +
-        ((delta_cycles * sample.nsscaled_per_cycle) >> kScale);
-    stats_fast_slow_paths++;
-  } else {
-    estimated_base_ns =
-        UpdateLastSample(now_cycles, now_ns, delta_cycles, &sample);
-  }
-
-  lock.Unlock();
-
-  return estimated_base_ns;
-}
-
-// Main part of the algorithm.  Locks out readers, updates the approximation
-// using the new sample from the kernel, and stores the result in last_sample
-// for readers.  Returns the new estimated time.
-static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns,
-                                 uint64_t delta_cycles,
-                                 const struct TimeSample *sample)
-    ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock) {
-  uint64_t estimated_base_ns = now_ns;
-  uint64_t lock_value = SeqAcquire(&seq);  // acquire seqlock to block readers
-
-  // The 5s in the next if-statement limits the time for which we will trust
-  // the cycle counter and our last sample to give a reasonable result.
-  // Errors in the rate of the source clock can be multiplied by the ratio
-  // between this limit and kMinNSBetweenSamples.
-  if (sample->raw_ns == 0 ||  // no recent sample, or clock went backwards
-      sample->raw_ns + static_cast<uint64_t>(5) * 1000 * 1000 * 1000 < now_ns ||
-      now_ns < sample->raw_ns || now_cycles < sample->base_cycles) {
-    // record this sample, and forget any previously known slope.
-    last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
-    last_sample.base_ns.store(estimated_base_ns, std::memory_order_relaxed);
-    last_sample.base_cycles.store(now_cycles, std::memory_order_relaxed);
-    last_sample.nsscaled_per_cycle.store(0, std::memory_order_relaxed);
-    last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed);
-    stats_initializations++;
-  } else if (sample->raw_ns + 500 * 1000 * 1000 < now_ns &&
-             sample->base_cycles + 50 < now_cycles) {
-    // Enough time has passed to compute the cycle time.
-    if (sample->nsscaled_per_cycle != 0) {  // Have a cycle time estimate.
-      // Compute time from counter reading, but avoiding overflow
-      // delta_cycles may be larger than on the fast path.
-      uint64_t estimated_scaled_ns;
-      int s = -1;
-      do {
-        s++;
-        estimated_scaled_ns = (delta_cycles >> s) * sample->nsscaled_per_cycle;
-      } while (estimated_scaled_ns / sample->nsscaled_per_cycle !=
-               (delta_cycles >> s));
-      estimated_base_ns = sample->base_ns +
-                          (estimated_scaled_ns >> (kScale - s));
-    }
-
-    // Compute the assumed cycle time kMinNSBetweenSamples ns into the future
-    // assuming the cycle counter rate stays the same as the last interval.
-    uint64_t ns = now_ns - sample->raw_ns;
-    uint64_t measured_nsscaled_per_cycle = SafeDivideAndScale(ns, delta_cycles);
-
-    uint64_t assumed_next_sample_delta_cycles =
-        SafeDivideAndScale(kMinNSBetweenSamples, measured_nsscaled_per_cycle);
-
-    int64_t diff_ns = now_ns - estimated_base_ns;  // estimate low by this much
-
-    // We want to set nsscaled_per_cycle so that our estimate of the ns time
-    // at the assumed cycle time is the assumed ns time.
-    // That is, we want to set nsscaled_per_cycle so:
-    //  kMinNSBetweenSamples + diff_ns  ==
-    //  (assumed_next_sample_delta_cycles * nsscaled_per_cycle) >> kScale
-    // But we wish to damp oscillations, so instead correct only most
-    // of our current error, by solving:
-    //  kMinNSBetweenSamples + diff_ns - (diff_ns / 16) ==
-    //  (assumed_next_sample_delta_cycles * nsscaled_per_cycle) >> kScale
-    ns = kMinNSBetweenSamples + diff_ns - (diff_ns / 16);
-    uint64_t new_nsscaled_per_cycle =
-        SafeDivideAndScale(ns, assumed_next_sample_delta_cycles);
-    if (new_nsscaled_per_cycle != 0 &&
-        diff_ns < 100 * 1000 * 1000 && -diff_ns < 100 * 1000 * 1000) {
-      // record the cycle time measurement
-      last_sample.nsscaled_per_cycle.store(
-          new_nsscaled_per_cycle, std::memory_order_relaxed);
-      uint64_t new_min_cycles_per_sample =
-          SafeDivideAndScale(kMinNSBetweenSamples, new_nsscaled_per_cycle);
-      last_sample.min_cycles_per_sample.store(
-          new_min_cycles_per_sample, std::memory_order_relaxed);
-      stats_calibrations++;
-    } else {  // something went wrong; forget the slope
-      last_sample.nsscaled_per_cycle.store(0, std::memory_order_relaxed);
-      last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed);
-      estimated_base_ns = now_ns;
-      stats_reinitializations++;
-    }
-    last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
-    last_sample.base_ns.store(estimated_base_ns, std::memory_order_relaxed);
-    last_sample.base_cycles.store(now_cycles, std::memory_order_relaxed);
-  } else {
-    // have a sample, but no slope; waiting for enough time for a calibration
-    stats_slow_paths++;
-  }
-
-  SeqRelease(&seq, lock_value);  // release the readers
-
-  return estimated_base_ns;
-}
-ABSL_NAMESPACE_END
-}  // namespace absl
-#endif  // ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-// Returns the maximum duration that SleepOnce() can sleep for.
-constexpr absl::Duration MaxSleep() {
-#ifdef _WIN32
-  // Windows Sleep() takes unsigned long argument in milliseconds.
-  return absl::Milliseconds(
-      std::numeric_limits<unsigned long>::max());  // NOLINT(runtime/int)
-#else
-  return absl::Seconds(std::numeric_limits<time_t>::max());
-#endif
-}
-
-// Sleeps for the given duration.
-// REQUIRES: to_sleep <= MaxSleep().
-void SleepOnce(absl::Duration to_sleep) {
-#ifdef _WIN32
-  Sleep(to_sleep / absl::Milliseconds(1));
-#else
-  struct timespec sleep_time = absl::ToTimespec(to_sleep);
-  while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {
-    // Ignore signals and wait for the full interval to elapse.
-  }
-#endif
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-extern "C" {
-
-ABSL_ATTRIBUTE_WEAK void AbslInternalSleepFor(absl::Duration duration) {
-  while (duration > absl::ZeroDuration()) {
-    absl::Duration to_sleep = std::min(duration, absl::MaxSleep());
-    absl::SleepOnce(to_sleep);
-    duration -= to_sleep;
-  }
-}
-
-}  // extern "C"
diff --git a/third_party/abseil_cpp/absl/time/clock.h b/third_party/abseil_cpp/absl/time/clock.h
deleted file mode 100644
index 27764a922d..0000000000
--- a/third_party/abseil_cpp/absl/time/clock.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: clock.h
-// -----------------------------------------------------------------------------
-//
-// This header file contains utility functions for working with the system-wide
-// realtime clock. For descriptions of the main time abstractions used within
-// this header file, consult the time.h header file.
-#ifndef ABSL_TIME_CLOCK_H_
-#define ABSL_TIME_CLOCK_H_
-
-#include "absl/base/macros.h"
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Now()
-//
-// Returns the current time, expressed as an `absl::Time` absolute time value.
-absl::Time Now();
-
-// GetCurrentTimeNanos()
-//
-// Returns the current time, expressed as a count of nanoseconds since the Unix
-// Epoch (https://en.wikipedia.org/wiki/Unix_time). Prefer `absl::Now()` instead
-// for all but the most performance-sensitive cases (i.e. when you are calling
-// this function hundreds of thousands of times per second).
-int64_t GetCurrentTimeNanos();
-
-// SleepFor()
-//
-// Sleeps for the specified duration, expressed as an `absl::Duration`.
-//
-// Notes:
-// * Signal interruptions will not reduce the sleep duration.
-// * Returns immediately when passed a nonpositive duration.
-void SleepFor(absl::Duration duration);
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-// -----------------------------------------------------------------------------
-// Implementation Details
-// -----------------------------------------------------------------------------
-
-// In some build configurations we pass --detect-odr-violations to the
-// gold linker.  This causes it to flag weak symbol overrides as ODR
-// violations.  Because ODR only applies to C++ and not C,
-// --detect-odr-violations ignores symbols not mangled with C++ names.
-// By changing our extension points to be extern "C", we dodge this
-// check.
-extern "C" {
-void AbslInternalSleepFor(absl::Duration duration);
-}  // extern "C"
-
-inline void absl::SleepFor(absl::Duration duration) {
-  AbslInternalSleepFor(duration);
-}
-
-#endif  // ABSL_TIME_CLOCK_H_
diff --git a/third_party/abseil_cpp/absl/time/clock_benchmark.cc b/third_party/abseil_cpp/absl/time/clock_benchmark.cc
deleted file mode 100644
index c5c795ecbd..0000000000
--- a/third_party/abseil_cpp/absl/time/clock_benchmark.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/clock.h"
-
-#if !defined(_WIN32)
-#include <sys/time.h>
-#else
-#include <winsock2.h>
-#endif  // _WIN32
-#include <cstdio>
-
-#include "absl/base/internal/cycleclock.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-void BM_Clock_Now_AbslTime(benchmark::State& state) {
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Now());
-  }
-}
-BENCHMARK(BM_Clock_Now_AbslTime);
-
-void BM_Clock_Now_GetCurrentTimeNanos(benchmark::State& state) {
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::GetCurrentTimeNanos());
-  }
-}
-BENCHMARK(BM_Clock_Now_GetCurrentTimeNanos);
-
-void BM_Clock_Now_AbslTime_ToUnixNanos(benchmark::State& state) {
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToUnixNanos(absl::Now()));
-  }
-}
-BENCHMARK(BM_Clock_Now_AbslTime_ToUnixNanos);
-
-void BM_Clock_Now_CycleClock(benchmark::State& state) {
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::base_internal::CycleClock::Now());
-  }
-}
-BENCHMARK(BM_Clock_Now_CycleClock);
-
-#if !defined(_WIN32)
-static void BM_Clock_Now_gettimeofday(benchmark::State& state) {
-  struct timeval tv;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(gettimeofday(&tv, nullptr));
-  }
-}
-BENCHMARK(BM_Clock_Now_gettimeofday);
-
-static void BM_Clock_Now_clock_gettime(benchmark::State& state) {
-  struct timespec ts;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(clock_gettime(CLOCK_REALTIME, &ts));
-  }
-}
-BENCHMARK(BM_Clock_Now_clock_gettime);
-#endif  // _WIN32
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/clock_test.cc b/third_party/abseil_cpp/absl/time/clock_test.cc
deleted file mode 100644
index 4bcfc6bc72..0000000000
--- a/third_party/abseil_cpp/absl/time/clock_test.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/clock.h"
-
-#include "absl/base/config.h"
-#if defined(ABSL_HAVE_ALARM)
-#include <signal.h>
-#include <unistd.h>
-#elif defined(__linux__) || defined(__APPLE__)
-#error all known Linux and Apple targets have alarm
-#endif
-
-#include "gtest/gtest.h"
-#include "absl/time/time.h"
-
-namespace {
-
-TEST(Time, Now) {
-  const absl::Time before = absl::FromUnixNanos(absl::GetCurrentTimeNanos());
-  const absl::Time now = absl::Now();
-  const absl::Time after = absl::FromUnixNanos(absl::GetCurrentTimeNanos());
-  EXPECT_GE(now, before);
-  EXPECT_GE(after, now);
-}
-
-enum class AlarmPolicy { kWithoutAlarm, kWithAlarm };
-
-#if defined(ABSL_HAVE_ALARM)
-bool alarm_handler_invoked = false;
-
-void AlarmHandler(int signo) {
-  ASSERT_EQ(signo, SIGALRM);
-  alarm_handler_invoked = true;
-}
-#endif
-
-// Does SleepFor(d) take between lower_bound and upper_bound at least
-// once between now and (now + timeout)?  If requested (and supported),
-// add an alarm for the middle of the sleep period and expect it to fire.
-bool SleepForBounded(absl::Duration d, absl::Duration lower_bound,
-                     absl::Duration upper_bound, absl::Duration timeout,
-                     AlarmPolicy alarm_policy, int* attempts) {
-  const absl::Time deadline = absl::Now() + timeout;
-  while (absl::Now() < deadline) {
-#if defined(ABSL_HAVE_ALARM)
-    sig_t old_alarm = SIG_DFL;
-    if (alarm_policy == AlarmPolicy::kWithAlarm) {
-      alarm_handler_invoked = false;
-      old_alarm = signal(SIGALRM, AlarmHandler);
-      alarm(absl::ToInt64Seconds(d / 2));
-    }
-#else
-    EXPECT_EQ(alarm_policy, AlarmPolicy::kWithoutAlarm);
-#endif
-    ++*attempts;
-    absl::Time start = absl::Now();
-    absl::SleepFor(d);
-    absl::Duration actual = absl::Now() - start;
-#if defined(ABSL_HAVE_ALARM)
-    if (alarm_policy == AlarmPolicy::kWithAlarm) {
-      signal(SIGALRM, old_alarm);
-      if (!alarm_handler_invoked) continue;
-    }
-#endif
-    if (lower_bound <= actual && actual <= upper_bound) {
-      return true;  // yes, the SleepFor() was correctly bounded
-    }
-  }
-  return false;
-}
-
-testing::AssertionResult AssertSleepForBounded(absl::Duration d,
-                                               absl::Duration early,
-                                               absl::Duration late,
-                                               absl::Duration timeout,
-                                               AlarmPolicy alarm_policy) {
-  const absl::Duration lower_bound = d - early;
-  const absl::Duration upper_bound = d + late;
-  int attempts = 0;
-  if (SleepForBounded(d, lower_bound, upper_bound, timeout, alarm_policy,
-                      &attempts)) {
-    return testing::AssertionSuccess();
-  }
-  return testing::AssertionFailure()
-         << "SleepFor(" << d << ") did not return within [" << lower_bound
-         << ":" << upper_bound << "] in " << attempts << " attempt"
-         << (attempts == 1 ? "" : "s") << " over " << timeout
-         << (alarm_policy == AlarmPolicy::kWithAlarm ? " with" : " without")
-         << " an alarm";
-}
-
-// Tests that SleepFor() returns neither too early nor too late.
-TEST(SleepFor, Bounded) {
-  const absl::Duration d = absl::Milliseconds(2500);
-  const absl::Duration early = absl::Milliseconds(100);
-  const absl::Duration late = absl::Milliseconds(300);
-  const absl::Duration timeout = 48 * d;
-  EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
-                                    AlarmPolicy::kWithoutAlarm));
-#if defined(ABSL_HAVE_ALARM)
-  EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
-                                    AlarmPolicy::kWithAlarm));
-#endif
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/duration.cc b/third_party/abseil_cpp/absl/time/duration.cc
deleted file mode 100644
index 4443109a51..0000000000
--- a/third_party/abseil_cpp/absl/time/duration.cc
+++ /dev/null
@@ -1,954 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// The implementation of the absl::Duration class, which is declared in
-// //absl/time.h.  This class behaves like a numeric type; it has no public
-// methods and is used only through the operators defined here.
-//
-// Implementation notes:
-//
-// An absl::Duration is represented as
-//
-//   rep_hi_ : (int64_t)  Whole seconds
-//   rep_lo_ : (uint32_t) Fractions of a second
-//
-// The seconds value (rep_hi_) may be positive or negative as appropriate.
-// The fractional seconds (rep_lo_) is always a positive offset from rep_hi_.
-// The API for Duration guarantees at least nanosecond resolution, which
-// means rep_lo_ could have a max value of 1B - 1 if it stored nanoseconds.
-// However, to utilize more of the available 32 bits of space in rep_lo_,
-// we instead store quarters of a nanosecond in rep_lo_ resulting in a max
-// value of 4B - 1.  This allows us to correctly handle calculations like
-// 0.5 nanos + 0.5 nanos = 1 nano.  The following example shows the actual
-// Duration rep using quarters of a nanosecond.
-//
-//    2.5 sec = {rep_hi_=2,  rep_lo_=2000000000}  // lo = 4 * 500000000
-//   -2.5 sec = {rep_hi_=-3, rep_lo_=2000000000}
-//
-// Infinite durations are represented as Durations with the rep_lo_ field set
-// to all 1s.
-//
-//   +InfiniteDuration:
-//     rep_hi_ : kint64max
-//     rep_lo_ : ~0U
-//
-//   -InfiniteDuration:
-//     rep_hi_ : kint64min
-//     rep_lo_ : ~0U
-//
-// Arithmetic overflows/underflows to +/- infinity and saturates.
-
-#if defined(_MSC_VER)
-#include <winsock2.h>  // for timeval
-#endif
-
-#include <algorithm>
-#include <cassert>
-#include <cctype>
-#include <cerrno>
-#include <cmath>
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-#include <ctime>
-#include <functional>
-#include <limits>
-#include <string>
-
-#include "absl/base/casts.h"
-#include "absl/base/macros.h"
-#include "absl/numeric/int128.h"
-#include "absl/strings/string_view.h"
-#include "absl/strings/strip.h"
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace {
-
-using time_internal::kTicksPerNanosecond;
-using time_internal::kTicksPerSecond;
-
-constexpr int64_t kint64max = std::numeric_limits<int64_t>::max();
-constexpr int64_t kint64min = std::numeric_limits<int64_t>::min();
-
-// Can't use std::isinfinite() because it doesn't exist on windows.
-inline bool IsFinite(double d) {
-  if (std::isnan(d)) return false;
-  return d != std::numeric_limits<double>::infinity() &&
-         d != -std::numeric_limits<double>::infinity();
-}
-
-inline bool IsValidDivisor(double d) {
-  if (std::isnan(d)) return false;
-  return d != 0.0;
-}
-
-// Can't use std::round() because it is only available in C++11.
-// Note that we ignore the possibility of floating-point over/underflow.
-template <typename Double>
-inline double Round(Double d) {
-  return d < 0 ? std::ceil(d - 0.5) : std::floor(d + 0.5);
-}
-
-// *sec may be positive or negative.  *ticks must be in the range
-// -kTicksPerSecond < *ticks < kTicksPerSecond.  If *ticks is negative it
-// will be normalized to a positive value by adjusting *sec accordingly.
-inline void NormalizeTicks(int64_t* sec, int64_t* ticks) {
-  if (*ticks < 0) {
-    --*sec;
-    *ticks += kTicksPerSecond;
-  }
-}
-
-// Makes a uint128 from the absolute value of the given scalar.
-inline uint128 MakeU128(int64_t a) {
-  uint128 u128 = 0;
-  if (a < 0) {
-    ++u128;
-    ++a;  // Makes it safe to negate 'a'
-    a = -a;
-  }
-  u128 += static_cast<uint64_t>(a);
-  return u128;
-}
-
-// Makes a uint128 count of ticks out of the absolute value of the Duration.
-inline uint128 MakeU128Ticks(Duration d) {
-  int64_t rep_hi = time_internal::GetRepHi(d);
-  uint32_t rep_lo = time_internal::GetRepLo(d);
-  if (rep_hi < 0) {
-    ++rep_hi;
-    rep_hi = -rep_hi;
-    rep_lo = kTicksPerSecond - rep_lo;
-  }
-  uint128 u128 = static_cast<uint64_t>(rep_hi);
-  u128 *= static_cast<uint64_t>(kTicksPerSecond);
-  u128 += rep_lo;
-  return u128;
-}
-
-// Breaks a uint128 of ticks into a Duration.
-inline Duration MakeDurationFromU128(uint128 u128, bool is_neg) {
-  int64_t rep_hi;
-  uint32_t rep_lo;
-  const uint64_t h64 = Uint128High64(u128);
-  const uint64_t l64 = Uint128Low64(u128);
-  if (h64 == 0) {  // fastpath
-    const uint64_t hi = l64 / kTicksPerSecond;
-    rep_hi = static_cast<int64_t>(hi);
-    rep_lo = static_cast<uint32_t>(l64 - hi * kTicksPerSecond);
-  } else {
-    // kMaxRepHi64 is the high 64 bits of (2^63 * kTicksPerSecond).
-    // Any positive tick count whose high 64 bits are >= kMaxRepHi64
-    // is not representable as a Duration.  A negative tick count can
-    // have its high 64 bits == kMaxRepHi64 but only when the low 64
-    // bits are all zero, otherwise it is not representable either.
-    const uint64_t kMaxRepHi64 = 0x77359400UL;
-    if (h64 >= kMaxRepHi64) {
-      if (is_neg && h64 == kMaxRepHi64 && l64 == 0) {
-        // Avoid trying to represent -kint64min below.
-        return time_internal::MakeDuration(kint64min);
-      }
-      return is_neg ? -InfiniteDuration() : InfiniteDuration();
-    }
-    const uint128 kTicksPerSecond128 = static_cast<uint64_t>(kTicksPerSecond);
-    const uint128 hi = u128 / kTicksPerSecond128;
-    rep_hi = static_cast<int64_t>(Uint128Low64(hi));
-    rep_lo =
-        static_cast<uint32_t>(Uint128Low64(u128 - hi * kTicksPerSecond128));
-  }
-  if (is_neg) {
-    rep_hi = -rep_hi;
-    if (rep_lo != 0) {
-      --rep_hi;
-      rep_lo = kTicksPerSecond - rep_lo;
-    }
-  }
-  return time_internal::MakeDuration(rep_hi, rep_lo);
-}
-
-// Convert between int64_t and uint64_t, preserving representation. This
-// allows us to do arithmetic in the unsigned domain, where overflow has
-// well-defined behavior. See operator+=() and operator-=().
-//
-// C99 7.20.1.1.1, as referenced by C++11 18.4.1.2, says, "The typedef
-// name intN_t designates a signed integer type with width N, no padding
-// bits, and a two's complement representation." So, we can convert to
-// and from the corresponding uint64_t value using a bit cast.
-inline uint64_t EncodeTwosComp(int64_t v) {
-  return absl::bit_cast<uint64_t>(v);
-}
-inline int64_t DecodeTwosComp(uint64_t v) { return absl::bit_cast<int64_t>(v); }
-
-// Note: The overflow detection in this function is done using greater/less *or
-// equal* because kint64max/min is too large to be represented exactly in a
-// double (which only has 53 bits of precision). In order to avoid assigning to
-// rep->hi a double value that is too large for an int64_t (and therefore is
-// undefined), we must consider computations that equal kint64max/min as a
-// double as overflow cases.
-inline bool SafeAddRepHi(double a_hi, double b_hi, Duration* d) {
-  double c = a_hi + b_hi;
-  if (c >= static_cast<double>(kint64max)) {
-    *d = InfiniteDuration();
-    return false;
-  }
-  if (c <= static_cast<double>(kint64min)) {
-    *d = -InfiniteDuration();
-    return false;
-  }
-  *d = time_internal::MakeDuration(c, time_internal::GetRepLo(*d));
-  return true;
-}
-
-// A functor that's similar to std::multiplies<T>, except this returns the max
-// T value instead of overflowing. This is only defined for uint128.
-template <typename Ignored>
-struct SafeMultiply {
-  uint128 operator()(uint128 a, uint128 b) const {
-    // b hi is always zero because it originated as an int64_t.
-    assert(Uint128High64(b) == 0);
-    // Fastpath to avoid the expensive overflow check with division.
-    if (Uint128High64(a) == 0) {
-      return (((Uint128Low64(a) | Uint128Low64(b)) >> 32) == 0)
-                 ? static_cast<uint128>(Uint128Low64(a) * Uint128Low64(b))
-                 : a * b;
-    }
-    return b == 0 ? b : (a > kuint128max / b) ? kuint128max : a * b;
-  }
-};
-
-// Scales (i.e., multiplies or divides, depending on the Operation template)
-// the Duration d by the int64_t r.
-template <template <typename> class Operation>
-inline Duration ScaleFixed(Duration d, int64_t r) {
-  const uint128 a = MakeU128Ticks(d);
-  const uint128 b = MakeU128(r);
-  const uint128 q = Operation<uint128>()(a, b);
-  const bool is_neg = (time_internal::GetRepHi(d) < 0) != (r < 0);
-  return MakeDurationFromU128(q, is_neg);
-}
-
-// Scales (i.e., multiplies or divides, depending on the Operation template)
-// the Duration d by the double r.
-template <template <typename> class Operation>
-inline Duration ScaleDouble(Duration d, double r) {
-  Operation<double> op;
-  double hi_doub = op(time_internal::GetRepHi(d), r);
-  double lo_doub = op(time_internal::GetRepLo(d), r);
-
-  double hi_int = 0;
-  double hi_frac = std::modf(hi_doub, &hi_int);
-
-  // Moves hi's fractional bits to lo.
-  lo_doub /= kTicksPerSecond;
-  lo_doub += hi_frac;
-
-  double lo_int = 0;
-  double lo_frac = std::modf(lo_doub, &lo_int);
-
-  // Rolls lo into hi if necessary.
-  int64_t lo64 = Round(lo_frac * kTicksPerSecond);
-
-  Duration ans;
-  if (!SafeAddRepHi(hi_int, lo_int, &ans)) return ans;
-  int64_t hi64 = time_internal::GetRepHi(ans);
-  if (!SafeAddRepHi(hi64, lo64 / kTicksPerSecond, &ans)) return ans;
-  hi64 = time_internal::GetRepHi(ans);
-  lo64 %= kTicksPerSecond;
-  NormalizeTicks(&hi64, &lo64);
-  return time_internal::MakeDuration(hi64, lo64);
-}
-
-// Tries to divide num by den as fast as possible by looking for common, easy
-// cases. If the division was done, the quotient is in *q and the remainder is
-// in *rem and true will be returned.
-inline bool IDivFastPath(const Duration num, const Duration den, int64_t* q,
-                         Duration* rem) {
-  // Bail if num or den is an infinity.
-  if (time_internal::IsInfiniteDuration(num) ||
-      time_internal::IsInfiniteDuration(den))
-    return false;
-
-  int64_t num_hi = time_internal::GetRepHi(num);
-  uint32_t num_lo = time_internal::GetRepLo(num);
-  int64_t den_hi = time_internal::GetRepHi(den);
-  uint32_t den_lo = time_internal::GetRepLo(den);
-
-  if (den_hi == 0 && den_lo == kTicksPerNanosecond) {
-    // Dividing by 1ns
-    if (num_hi >= 0 && num_hi < (kint64max - kTicksPerSecond) / 1000000000) {
-      *q = num_hi * 1000000000 + num_lo / kTicksPerNanosecond;
-      *rem = time_internal::MakeDuration(0, num_lo % den_lo);
-      return true;
-    }
-  } else if (den_hi == 0 && den_lo == 100 * kTicksPerNanosecond) {
-    // Dividing by 100ns (common when converting to Universal time)
-    if (num_hi >= 0 && num_hi < (kint64max - kTicksPerSecond) / 10000000) {
-      *q = num_hi * 10000000 + num_lo / (100 * kTicksPerNanosecond);
-      *rem = time_internal::MakeDuration(0, num_lo % den_lo);
-      return true;
-    }
-  } else if (den_hi == 0 && den_lo == 1000 * kTicksPerNanosecond) {
-    // Dividing by 1us
-    if (num_hi >= 0 && num_hi < (kint64max - kTicksPerSecond) / 1000000) {
-      *q = num_hi * 1000000 + num_lo / (1000 * kTicksPerNanosecond);
-      *rem = time_internal::MakeDuration(0, num_lo % den_lo);
-      return true;
-    }
-  } else if (den_hi == 0 && den_lo == 1000000 * kTicksPerNanosecond) {
-    // Dividing by 1ms
-    if (num_hi >= 0 && num_hi < (kint64max - kTicksPerSecond) / 1000) {
-      *q = num_hi * 1000 + num_lo / (1000000 * kTicksPerNanosecond);
-      *rem = time_internal::MakeDuration(0, num_lo % den_lo);
-      return true;
-    }
-  } else if (den_hi > 0 && den_lo == 0) {
-    // Dividing by positive multiple of 1s
-    if (num_hi >= 0) {
-      if (den_hi == 1) {
-        *q = num_hi;
-        *rem = time_internal::MakeDuration(0, num_lo);
-        return true;
-      }
-      *q = num_hi / den_hi;
-      *rem = time_internal::MakeDuration(num_hi % den_hi, num_lo);
-      return true;
-    }
-    if (num_lo != 0) {
-      num_hi += 1;
-    }
-    int64_t quotient = num_hi / den_hi;
-    int64_t rem_sec = num_hi % den_hi;
-    if (rem_sec > 0) {
-      rem_sec -= den_hi;
-      quotient += 1;
-    }
-    if (num_lo != 0) {
-      rem_sec -= 1;
-    }
-    *q = quotient;
-    *rem = time_internal::MakeDuration(rem_sec, num_lo);
-    return true;
-  }
-
-  return false;
-}
-
-}  // namespace
-
-namespace time_internal {
-
-// The 'satq' argument indicates whether the quotient should saturate at the
-// bounds of int64_t.  If it does saturate, the difference will spill over to
-// the remainder.  If it does not saturate, the remainder remain accurate,
-// but the returned quotient will over/underflow int64_t and should not be used.
-int64_t IDivDuration(bool satq, const Duration num, const Duration den,
-                     Duration* rem) {
-  int64_t q = 0;
-  if (IDivFastPath(num, den, &q, rem)) {
-    return q;
-  }
-
-  const bool num_neg = num < ZeroDuration();
-  const bool den_neg = den < ZeroDuration();
-  const bool quotient_neg = num_neg != den_neg;
-
-  if (time_internal::IsInfiniteDuration(num) || den == ZeroDuration()) {
-    *rem = num_neg ? -InfiniteDuration() : InfiniteDuration();
-    return quotient_neg ? kint64min : kint64max;
-  }
-  if (time_internal::IsInfiniteDuration(den)) {
-    *rem = num;
-    return 0;
-  }
-
-  const uint128 a = MakeU128Ticks(num);
-  const uint128 b = MakeU128Ticks(den);
-  uint128 quotient128 = a / b;
-
-  if (satq) {
-    // Limits the quotient to the range of int64_t.
-    if (quotient128 > uint128(static_cast<uint64_t>(kint64max))) {
-      quotient128 = quotient_neg ? uint128(static_cast<uint64_t>(kint64min))
-                                 : uint128(static_cast<uint64_t>(kint64max));
-    }
-  }
-
-  const uint128 remainder128 = a - quotient128 * b;
-  *rem = MakeDurationFromU128(remainder128, num_neg);
-
-  if (!quotient_neg || quotient128 == 0) {
-    return Uint128Low64(quotient128) & kint64max;
-  }
-  // The quotient needs to be negated, but we need to carefully handle
-  // quotient128s with the top bit on.
-  return -static_cast<int64_t>(Uint128Low64(quotient128 - 1) & kint64max) - 1;
-}
-
-}  // namespace time_internal
-
-//
-// Additive operators.
-//
-
-Duration& Duration::operator+=(Duration rhs) {
-  if (time_internal::IsInfiniteDuration(*this)) return *this;
-  if (time_internal::IsInfiniteDuration(rhs)) return *this = rhs;
-  const int64_t orig_rep_hi = rep_hi_;
-  rep_hi_ =
-      DecodeTwosComp(EncodeTwosComp(rep_hi_) + EncodeTwosComp(rhs.rep_hi_));
-  if (rep_lo_ >= kTicksPerSecond - rhs.rep_lo_) {
-    rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_) + 1);
-    rep_lo_ -= kTicksPerSecond;
-  }
-  rep_lo_ += rhs.rep_lo_;
-  if (rhs.rep_hi_ < 0 ? rep_hi_ > orig_rep_hi : rep_hi_ < orig_rep_hi) {
-    return *this = rhs.rep_hi_ < 0 ? -InfiniteDuration() : InfiniteDuration();
-  }
-  return *this;
-}
-
-Duration& Duration::operator-=(Duration rhs) {
-  if (time_internal::IsInfiniteDuration(*this)) return *this;
-  if (time_internal::IsInfiniteDuration(rhs)) {
-    return *this = rhs.rep_hi_ >= 0 ? -InfiniteDuration() : InfiniteDuration();
-  }
-  const int64_t orig_rep_hi = rep_hi_;
-  rep_hi_ =
-      DecodeTwosComp(EncodeTwosComp(rep_hi_) - EncodeTwosComp(rhs.rep_hi_));
-  if (rep_lo_ < rhs.rep_lo_) {
-    rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_) - 1);
-    rep_lo_ += kTicksPerSecond;
-  }
-  rep_lo_ -= rhs.rep_lo_;
-  if (rhs.rep_hi_ < 0 ? rep_hi_ < orig_rep_hi : rep_hi_ > orig_rep_hi) {
-    return *this = rhs.rep_hi_ >= 0 ? -InfiniteDuration() : InfiniteDuration();
-  }
-  return *this;
-}
-
-//
-// Multiplicative operators.
-//
-
-Duration& Duration::operator*=(int64_t r) {
-  if (time_internal::IsInfiniteDuration(*this)) {
-    const bool is_neg = (r < 0) != (rep_hi_ < 0);
-    return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
-  }
-  return *this = ScaleFixed<SafeMultiply>(*this, r);
-}
-
-Duration& Duration::operator*=(double r) {
-  if (time_internal::IsInfiniteDuration(*this) || !IsFinite(r)) {
-    const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0);
-    return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
-  }
-  return *this = ScaleDouble<std::multiplies>(*this, r);
-}
-
-Duration& Duration::operator/=(int64_t r) {
-  if (time_internal::IsInfiniteDuration(*this) || r == 0) {
-    const bool is_neg = (r < 0) != (rep_hi_ < 0);
-    return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
-  }
-  return *this = ScaleFixed<std::divides>(*this, r);
-}
-
-Duration& Duration::operator/=(double r) {
-  if (time_internal::IsInfiniteDuration(*this) || !IsValidDivisor(r)) {
-    const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0);
-    return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
-  }
-  return *this = ScaleDouble<std::divides>(*this, r);
-}
-
-Duration& Duration::operator%=(Duration rhs) {
-  time_internal::IDivDuration(false, *this, rhs, this);
-  return *this;
-}
-
-double FDivDuration(Duration num, Duration den) {
-  // Arithmetic with infinity is sticky.
-  if (time_internal::IsInfiniteDuration(num) || den == ZeroDuration()) {
-    return (num < ZeroDuration()) == (den < ZeroDuration())
-               ? std::numeric_limits<double>::infinity()
-               : -std::numeric_limits<double>::infinity();
-  }
-  if (time_internal::IsInfiniteDuration(den)) return 0.0;
-
-  double a =
-      static_cast<double>(time_internal::GetRepHi(num)) * kTicksPerSecond +
-      time_internal::GetRepLo(num);
-  double b =
-      static_cast<double>(time_internal::GetRepHi(den)) * kTicksPerSecond +
-      time_internal::GetRepLo(den);
-  return a / b;
-}
-
-//
-// Trunc/Floor/Ceil.
-//
-
-Duration Trunc(Duration d, Duration unit) {
-  return d - (d % unit);
-}
-
-Duration Floor(const Duration d, const Duration unit) {
-  const absl::Duration td = Trunc(d, unit);
-  return td <= d ? td : td - AbsDuration(unit);
-}
-
-Duration Ceil(const Duration d, const Duration unit) {
-  const absl::Duration td = Trunc(d, unit);
-  return td >= d ? td : td + AbsDuration(unit);
-}
-
-//
-// Factory functions.
-//
-
-Duration DurationFromTimespec(timespec ts) {
-  if (static_cast<uint64_t>(ts.tv_nsec) < 1000 * 1000 * 1000) {
-    int64_t ticks = ts.tv_nsec * kTicksPerNanosecond;
-    return time_internal::MakeDuration(ts.tv_sec, ticks);
-  }
-  return Seconds(ts.tv_sec) + Nanoseconds(ts.tv_nsec);
-}
-
-Duration DurationFromTimeval(timeval tv) {
-  if (static_cast<uint64_t>(tv.tv_usec) < 1000 * 1000) {
-    int64_t ticks = tv.tv_usec * 1000 * kTicksPerNanosecond;
-    return time_internal::MakeDuration(tv.tv_sec, ticks);
-  }
-  return Seconds(tv.tv_sec) + Microseconds(tv.tv_usec);
-}
-
-//
-// Conversion to other duration types.
-//
-
-int64_t ToInt64Nanoseconds(Duration d) {
-  if (time_internal::GetRepHi(d) >= 0 &&
-      time_internal::GetRepHi(d) >> 33 == 0) {
-    return (time_internal::GetRepHi(d) * 1000 * 1000 * 1000) +
-           (time_internal::GetRepLo(d) / kTicksPerNanosecond);
-  }
-  return d / Nanoseconds(1);
-}
-int64_t ToInt64Microseconds(Duration d) {
-  if (time_internal::GetRepHi(d) >= 0 &&
-      time_internal::GetRepHi(d) >> 43 == 0) {
-    return (time_internal::GetRepHi(d) * 1000 * 1000) +
-           (time_internal::GetRepLo(d) / (kTicksPerNanosecond * 1000));
-  }
-  return d / Microseconds(1);
-}
-int64_t ToInt64Milliseconds(Duration d) {
-  if (time_internal::GetRepHi(d) >= 0 &&
-      time_internal::GetRepHi(d) >> 53 == 0) {
-    return (time_internal::GetRepHi(d) * 1000) +
-           (time_internal::GetRepLo(d) / (kTicksPerNanosecond * 1000 * 1000));
-  }
-  return d / Milliseconds(1);
-}
-int64_t ToInt64Seconds(Duration d) {
-  int64_t hi = time_internal::GetRepHi(d);
-  if (time_internal::IsInfiniteDuration(d)) return hi;
-  if (hi < 0 && time_internal::GetRepLo(d) != 0) ++hi;
-  return hi;
-}
-int64_t ToInt64Minutes(Duration d) {
-  int64_t hi = time_internal::GetRepHi(d);
-  if (time_internal::IsInfiniteDuration(d)) return hi;
-  if (hi < 0 && time_internal::GetRepLo(d) != 0) ++hi;
-  return hi / 60;
-}
-int64_t ToInt64Hours(Duration d) {
-  int64_t hi = time_internal::GetRepHi(d);
-  if (time_internal::IsInfiniteDuration(d)) return hi;
-  if (hi < 0 && time_internal::GetRepLo(d) != 0) ++hi;
-  return hi / (60 * 60);
-}
-
-double ToDoubleNanoseconds(Duration d) {
-  return FDivDuration(d, Nanoseconds(1));
-}
-double ToDoubleMicroseconds(Duration d) {
-  return FDivDuration(d, Microseconds(1));
-}
-double ToDoubleMilliseconds(Duration d) {
-  return FDivDuration(d, Milliseconds(1));
-}
-double ToDoubleSeconds(Duration d) {
-  return FDivDuration(d, Seconds(1));
-}
-double ToDoubleMinutes(Duration d) {
-  return FDivDuration(d, Minutes(1));
-}
-double ToDoubleHours(Duration d) {
-  return FDivDuration(d, Hours(1));
-}
-
-timespec ToTimespec(Duration d) {
-  timespec ts;
-  if (!time_internal::IsInfiniteDuration(d)) {
-    int64_t rep_hi = time_internal::GetRepHi(d);
-    uint32_t rep_lo = time_internal::GetRepLo(d);
-    if (rep_hi < 0) {
-      // Tweak the fields so that unsigned division of rep_lo
-      // maps to truncation (towards zero) for the timespec.
-      rep_lo += kTicksPerNanosecond - 1;
-      if (rep_lo >= kTicksPerSecond) {
-        rep_hi += 1;
-        rep_lo -= kTicksPerSecond;
-      }
-    }
-    ts.tv_sec = rep_hi;
-    if (ts.tv_sec == rep_hi) {  // no time_t narrowing
-      ts.tv_nsec = rep_lo / kTicksPerNanosecond;
-      return ts;
-    }
-  }
-  if (d >= ZeroDuration()) {
-    ts.tv_sec = std::numeric_limits<time_t>::max();
-    ts.tv_nsec = 1000 * 1000 * 1000 - 1;
-  } else {
-    ts.tv_sec = std::numeric_limits<time_t>::min();
-    ts.tv_nsec = 0;
-  }
-  return ts;
-}
-
-timeval ToTimeval(Duration d) {
-  timeval tv;
-  timespec ts = ToTimespec(d);
-  if (ts.tv_sec < 0) {
-    // Tweak the fields so that positive division of tv_nsec
-    // maps to truncation (towards zero) for the timeval.
-    ts.tv_nsec += 1000 - 1;
-    if (ts.tv_nsec >= 1000 * 1000 * 1000) {
-      ts.tv_sec += 1;
-      ts.tv_nsec -= 1000 * 1000 * 1000;
-    }
-  }
-  tv.tv_sec = ts.tv_sec;
-  if (tv.tv_sec != ts.tv_sec) {  // narrowing
-    if (ts.tv_sec < 0) {
-      tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::min();
-      tv.tv_usec = 0;
-    } else {
-      tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::max();
-      tv.tv_usec = 1000 * 1000 - 1;
-    }
-    return tv;
-  }
-  tv.tv_usec = static_cast<int>(ts.tv_nsec / 1000);  // suseconds_t
-  return tv;
-}
-
-std::chrono::nanoseconds ToChronoNanoseconds(Duration d) {
-  return time_internal::ToChronoDuration<std::chrono::nanoseconds>(d);
-}
-std::chrono::microseconds ToChronoMicroseconds(Duration d) {
-  return time_internal::ToChronoDuration<std::chrono::microseconds>(d);
-}
-std::chrono::milliseconds ToChronoMilliseconds(Duration d) {
-  return time_internal::ToChronoDuration<std::chrono::milliseconds>(d);
-}
-std::chrono::seconds ToChronoSeconds(Duration d) {
-  return time_internal::ToChronoDuration<std::chrono::seconds>(d);
-}
-std::chrono::minutes ToChronoMinutes(Duration d) {
-  return time_internal::ToChronoDuration<std::chrono::minutes>(d);
-}
-std::chrono::hours ToChronoHours(Duration d) {
-  return time_internal::ToChronoDuration<std::chrono::hours>(d);
-}
-
-//
-// To/From string formatting.
-//
-
-namespace {
-
-// Formats a positive 64-bit integer in the given field width.  Note that
-// it is up to the caller of Format64() to ensure that there is sufficient
-// space before ep to hold the conversion.
-char* Format64(char* ep, int width, int64_t v) {
-  do {
-    --width;
-    *--ep = '0' + (v % 10);  // contiguous digits
-  } while (v /= 10);
-  while (--width >= 0) *--ep = '0';  // zero pad
-  return ep;
-}
-
-// Helpers for FormatDuration() that format 'n' and append it to 'out'
-// followed by the given 'unit'.  If 'n' formats to "0", nothing is
-// appended (not even the unit).
-
-// A type that encapsulates how to display a value of a particular unit. For
-// values that are displayed with fractional parts, the precision indicates
-// where to round the value. The precision varies with the display unit because
-// a Duration can hold only quarters of a nanosecond, so displaying information
-// beyond that is just noise.
-//
-// For example, a microsecond value of 42.00025xxxxx should not display beyond 5
-// fractional digits, because it is in the noise of what a Duration can
-// represent.
-struct DisplayUnit {
-  absl::string_view abbr;
-  int prec;
-  double pow10;
-};
-ABSL_CONST_INIT const DisplayUnit kDisplayNano = {"ns", 2, 1e2};
-ABSL_CONST_INIT const DisplayUnit kDisplayMicro = {"us", 5, 1e5};
-ABSL_CONST_INIT const DisplayUnit kDisplayMilli = {"ms", 8, 1e8};
-ABSL_CONST_INIT const DisplayUnit kDisplaySec = {"s", 11, 1e11};
-ABSL_CONST_INIT const DisplayUnit kDisplayMin = {"m", -1, 0.0};  // prec ignored
-ABSL_CONST_INIT const DisplayUnit kDisplayHour = {"h", -1,
-                                                  0.0};  // prec ignored
-
-void AppendNumberUnit(std::string* out, int64_t n, DisplayUnit unit) {
-  char buf[sizeof("2562047788015216")];  // hours in max duration
-  char* const ep = buf + sizeof(buf);
-  char* bp = Format64(ep, 0, n);
-  if (*bp != '0' || bp + 1 != ep) {
-    out->append(bp, ep - bp);
-    out->append(unit.abbr.data(), unit.abbr.size());
-  }
-}
-
-// Note: unit.prec is limited to double's digits10 value (typically 15) so it
-// always fits in buf[].
-void AppendNumberUnit(std::string* out, double n, DisplayUnit unit) {
-  constexpr int kBufferSize = std::numeric_limits<double>::digits10;
-  const int prec = std::min(kBufferSize, unit.prec);
-  char buf[kBufferSize];  // also large enough to hold integer part
-  char* ep = buf + sizeof(buf);
-  double d = 0;
-  int64_t frac_part = Round(std::modf(n, &d) * unit.pow10);
-  int64_t int_part = d;
-  if (int_part != 0 || frac_part != 0) {
-    char* bp = Format64(ep, 0, int_part);  // always < 1000
-    out->append(bp, ep - bp);
-    if (frac_part != 0) {
-      out->push_back('.');
-      bp = Format64(ep, prec, frac_part);
-      while (ep[-1] == '0') --ep;
-      out->append(bp, ep - bp);
-    }
-    out->append(unit.abbr.data(), unit.abbr.size());
-  }
-}
-
-}  // namespace
-
-// From Go's doc at https://golang.org/pkg/time/#Duration.String
-//   [FormatDuration] returns a string representing the duration in the
-//   form "72h3m0.5s". Leading zero units are omitted.  As a special
-//   case, durations less than one second format use a smaller unit
-//   (milli-, micro-, or nanoseconds) to ensure that the leading digit
-//   is non-zero.
-// Unlike Go, we format the zero duration as 0, with no unit.
-std::string FormatDuration(Duration d) {
-  const Duration min_duration = Seconds(kint64min);
-  if (d == min_duration) {
-    // Avoid needing to negate kint64min by directly returning what the
-    // following code should produce in that case.
-    return "-2562047788015215h30m8s";
-  }
-  std::string s;
-  if (d < ZeroDuration()) {
-    s.append("-");
-    d = -d;
-  }
-  if (d == InfiniteDuration()) {
-    s.append("inf");
-  } else if (d < Seconds(1)) {
-    // Special case for durations with a magnitude < 1 second.  The duration
-    // is printed as a fraction of a single unit, e.g., "1.2ms".
-    if (d < Microseconds(1)) {
-      AppendNumberUnit(&s, FDivDuration(d, Nanoseconds(1)), kDisplayNano);
-    } else if (d < Milliseconds(1)) {
-      AppendNumberUnit(&s, FDivDuration(d, Microseconds(1)), kDisplayMicro);
-    } else {
-      AppendNumberUnit(&s, FDivDuration(d, Milliseconds(1)), kDisplayMilli);
-    }
-  } else {
-    AppendNumberUnit(&s, IDivDuration(d, Hours(1), &d), kDisplayHour);
-    AppendNumberUnit(&s, IDivDuration(d, Minutes(1), &d), kDisplayMin);
-    AppendNumberUnit(&s, FDivDuration(d, Seconds(1)), kDisplaySec);
-  }
-  if (s.empty() || s == "-") {
-    s = "0";
-  }
-  return s;
-}
-
-namespace {
-
-// A helper for ParseDuration() that parses a leading number from the given
-// string and stores the result in *int_part/*frac_part/*frac_scale.  The
-// given string pointer is modified to point to the first unconsumed char.
-bool ConsumeDurationNumber(const char** dpp, const char* ep, int64_t* int_part,
-                           int64_t* frac_part, int64_t* frac_scale) {
-  *int_part = 0;
-  *frac_part = 0;
-  *frac_scale = 1;  // invariant: *frac_part < *frac_scale
-  const char* start = *dpp;
-  for (; *dpp != ep; *dpp += 1) {
-    const int d = **dpp - '0';  // contiguous digits
-    if (d < 0 || 10 <= d) break;
-
-    if (*int_part > kint64max / 10) return false;
-    *int_part *= 10;
-    if (*int_part > kint64max - d) return false;
-    *int_part += d;
-  }
-  const bool int_part_empty = (*dpp == start);
-  if (*dpp == ep || **dpp != '.') return !int_part_empty;
-
-  for (*dpp += 1; *dpp != ep; *dpp += 1) {
-    const int d = **dpp - '0';  // contiguous digits
-    if (d < 0 || 10 <= d) break;
-    if (*frac_scale <= kint64max / 10) {
-      *frac_part *= 10;
-      *frac_part += d;
-      *frac_scale *= 10;
-    }
-  }
-  return !int_part_empty || *frac_scale != 1;
-}
-
-// A helper for ParseDuration() that parses a leading unit designator (e.g.,
-// ns, us, ms, s, m, h) from the given string and stores the resulting unit
-// in "*unit".  The given string pointer is modified to point to the first
-// unconsumed char.
-bool ConsumeDurationUnit(const char** start, const char* end, Duration* unit) {
-  size_t size = end - *start;
-  switch (size) {
-    case 0:
-      return false;
-    default:
-      switch (**start) {
-        case 'n':
-          if (*(*start + 1) == 's') {
-            *start += 2;
-            *unit = Nanoseconds(1);
-            return true;
-          }
-          break;
-        case 'u':
-          if (*(*start + 1) == 's') {
-            *start += 2;
-            *unit = Microseconds(1);
-            return true;
-          }
-          break;
-        case 'm':
-          if (*(*start + 1) == 's') {
-            *start += 2;
-            *unit = Milliseconds(1);
-            return true;
-          }
-          break;
-        default:
-          break;
-      }
-      ABSL_FALLTHROUGH_INTENDED;
-    case 1:
-      switch (**start) {
-        case 's':
-          *unit = Seconds(1);
-          *start += 1;
-          return true;
-        case 'm':
-          *unit = Minutes(1);
-          *start += 1;
-          return true;
-        case 'h':
-          *unit = Hours(1);
-          *start += 1;
-          return true;
-        default:
-          return false;
-      }
-  }
-}
-
-}  // namespace
-
-// From Go's doc at https://golang.org/pkg/time/#ParseDuration
-//   [ParseDuration] parses a duration string. A duration string is
-//   a possibly signed sequence of decimal numbers, each with optional
-//   fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m".
-//   Valid time units are "ns", "us" "ms", "s", "m", "h".
-bool ParseDuration(absl::string_view dur_sv, Duration* d) {
-  int sign = 1;
-  if (absl::ConsumePrefix(&dur_sv, "-")) {
-    sign = -1;
-  } else {
-    absl::ConsumePrefix(&dur_sv, "+");
-  }
-  if (dur_sv.empty()) return false;
-
-  // Special case for a string of "0".
-  if (dur_sv == "0") {
-    *d = ZeroDuration();
-    return true;
-  }
-
-  if (dur_sv == "inf") {
-    *d = sign * InfiniteDuration();
-    return true;
-  }
-
-  const char* start = dur_sv.data();
-  const char* end = start + dur_sv.size();
-
-  Duration dur;
-  while (start != end) {
-    int64_t int_part;
-    int64_t frac_part;
-    int64_t frac_scale;
-    Duration unit;
-    if (!ConsumeDurationNumber(&start, end, &int_part, &frac_part,
-                               &frac_scale) ||
-        !ConsumeDurationUnit(&start, end, &unit)) {
-      return false;
-    }
-    if (int_part != 0) dur += sign * int_part * unit;
-    if (frac_part != 0) dur += sign * frac_part * unit / frac_scale;
-  }
-  *d = dur;
-  return true;
-}
-
-bool AbslParseFlag(absl::string_view text, Duration* dst, std::string*) {
-  return ParseDuration(text, dst);
-}
-
-std::string AbslUnparseFlag(Duration d) { return FormatDuration(d); }
-bool ParseFlag(const std::string& text, Duration* dst, std::string* ) {
-  return ParseDuration(text, dst);
-}
-
-std::string UnparseFlag(Duration d) { return FormatDuration(d); }
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/duration_benchmark.cc b/third_party/abseil_cpp/absl/time/duration_benchmark.cc
deleted file mode 100644
index 83a836c8c8..0000000000
--- a/third_party/abseil_cpp/absl/time/duration_benchmark.cc
+++ /dev/null
@@ -1,428 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cmath>
-#include <cstddef>
-#include <cstdint>
-#include <ctime>
-#include <string>
-
-#include "absl/base/attributes.h"
-#include "absl/time/time.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-//
-// Factory functions
-//
-
-void BM_Duration_Factory_Nanoseconds(benchmark::State& state) {
-  int64_t i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Nanoseconds(i));
-    i += 314159;
-  }
-}
-BENCHMARK(BM_Duration_Factory_Nanoseconds);
-
-void BM_Duration_Factory_Microseconds(benchmark::State& state) {
-  int64_t i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Microseconds(i));
-    i += 314;
-  }
-}
-BENCHMARK(BM_Duration_Factory_Microseconds);
-
-void BM_Duration_Factory_Milliseconds(benchmark::State& state) {
-  int64_t i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Milliseconds(i));
-    i += 1;
-  }
-}
-BENCHMARK(BM_Duration_Factory_Milliseconds);
-
-void BM_Duration_Factory_Seconds(benchmark::State& state) {
-  int64_t i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Seconds(i));
-    i += 1;
-  }
-}
-BENCHMARK(BM_Duration_Factory_Seconds);
-
-void BM_Duration_Factory_Minutes(benchmark::State& state) {
-  int64_t i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Minutes(i));
-    i += 1;
-  }
-}
-BENCHMARK(BM_Duration_Factory_Minutes);
-
-void BM_Duration_Factory_Hours(benchmark::State& state) {
-  int64_t i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Hours(i));
-    i += 1;
-  }
-}
-BENCHMARK(BM_Duration_Factory_Hours);
-
-void BM_Duration_Factory_DoubleNanoseconds(benchmark::State& state) {
-  double d = 1;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Nanoseconds(d));
-    d = d * 1.00000001 + 1;
-  }
-}
-BENCHMARK(BM_Duration_Factory_DoubleNanoseconds);
-
-void BM_Duration_Factory_DoubleMicroseconds(benchmark::State& state) {
-  double d = 1e-3;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Microseconds(d));
-    d = d * 1.00000001 + 1e-3;
-  }
-}
-BENCHMARK(BM_Duration_Factory_DoubleMicroseconds);
-
-void BM_Duration_Factory_DoubleMilliseconds(benchmark::State& state) {
-  double d = 1e-6;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Milliseconds(d));
-    d = d * 1.00000001 + 1e-6;
-  }
-}
-BENCHMARK(BM_Duration_Factory_DoubleMilliseconds);
-
-void BM_Duration_Factory_DoubleSeconds(benchmark::State& state) {
-  double d = 1e-9;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Seconds(d));
-    d = d * 1.00000001 + 1e-9;
-  }
-}
-BENCHMARK(BM_Duration_Factory_DoubleSeconds);
-
-void BM_Duration_Factory_DoubleMinutes(benchmark::State& state) {
-  double d = 1e-9;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Minutes(d));
-    d = d * 1.00000001 + 1e-9;
-  }
-}
-BENCHMARK(BM_Duration_Factory_DoubleMinutes);
-
-void BM_Duration_Factory_DoubleHours(benchmark::State& state) {
-  double d = 1e-9;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::Hours(d));
-    d = d * 1.00000001 + 1e-9;
-  }
-}
-BENCHMARK(BM_Duration_Factory_DoubleHours);
-
-//
-// Arithmetic
-//
-
-void BM_Duration_Addition(benchmark::State& state) {
-  absl::Duration d = absl::Nanoseconds(1);
-  absl::Duration step = absl::Milliseconds(1);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(d += step);
-  }
-}
-BENCHMARK(BM_Duration_Addition);
-
-void BM_Duration_Subtraction(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(std::numeric_limits<int64_t>::max());
-  absl::Duration step = absl::Milliseconds(1);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(d -= step);
-  }
-}
-BENCHMARK(BM_Duration_Subtraction);
-
-void BM_Duration_Multiplication_Fixed(benchmark::State& state) {
-  absl::Duration d = absl::Milliseconds(1);
-  absl::Duration s;
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(s += d * (i + 1));
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_Multiplication_Fixed);
-
-void BM_Duration_Multiplication_Double(benchmark::State& state) {
-  absl::Duration d = absl::Milliseconds(1);
-  absl::Duration s;
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(s += d * (i + 1.0));
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_Multiplication_Double);
-
-void BM_Duration_Division_Fixed(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(1);
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(d /= i + 1);
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_Division_Fixed);
-
-void BM_Duration_Division_Double(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(1);
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(d /= i + 1.0);
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_Division_Double);
-
-void BM_Duration_FDivDuration_Nanoseconds(benchmark::State& state) {
-  double d = 1;
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(
-        d += absl::FDivDuration(absl::Milliseconds(i), absl::Nanoseconds(1)));
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_FDivDuration_Nanoseconds);
-
-void BM_Duration_IDivDuration_Nanoseconds(benchmark::State& state) {
-  int64_t a = 1;
-  absl::Duration ignore;
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(a +=
-                             absl::IDivDuration(absl::Nanoseconds(i),
-                                                absl::Nanoseconds(1), &ignore));
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_IDivDuration_Nanoseconds);
-
-void BM_Duration_IDivDuration_Microseconds(benchmark::State& state) {
-  int64_t a = 1;
-  absl::Duration ignore;
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(a += absl::IDivDuration(absl::Microseconds(i),
-                                                     absl::Microseconds(1),
-                                                     &ignore));
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_IDivDuration_Microseconds);
-
-void BM_Duration_IDivDuration_Milliseconds(benchmark::State& state) {
-  int64_t a = 1;
-  absl::Duration ignore;
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(a += absl::IDivDuration(absl::Milliseconds(i),
-                                                     absl::Milliseconds(1),
-                                                     &ignore));
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_IDivDuration_Milliseconds);
-
-void BM_Duration_IDivDuration_Seconds(benchmark::State& state) {
-  int64_t a = 1;
-  absl::Duration ignore;
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(
-        a += absl::IDivDuration(absl::Seconds(i), absl::Seconds(1), &ignore));
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_IDivDuration_Seconds);
-
-void BM_Duration_IDivDuration_Minutes(benchmark::State& state) {
-  int64_t a = 1;
-  absl::Duration ignore;
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(
-        a += absl::IDivDuration(absl::Minutes(i), absl::Minutes(1), &ignore));
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_IDivDuration_Minutes);
-
-void BM_Duration_IDivDuration_Hours(benchmark::State& state) {
-  int64_t a = 1;
-  absl::Duration ignore;
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(
-        a += absl::IDivDuration(absl::Hours(i), absl::Hours(1), &ignore));
-    ++i;
-  }
-}
-BENCHMARK(BM_Duration_IDivDuration_Hours);
-
-void BM_Duration_ToInt64Nanoseconds(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(100000);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToInt64Nanoseconds(d));
-  }
-}
-BENCHMARK(BM_Duration_ToInt64Nanoseconds);
-
-void BM_Duration_ToInt64Microseconds(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(100000);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToInt64Microseconds(d));
-  }
-}
-BENCHMARK(BM_Duration_ToInt64Microseconds);
-
-void BM_Duration_ToInt64Milliseconds(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(100000);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToInt64Milliseconds(d));
-  }
-}
-BENCHMARK(BM_Duration_ToInt64Milliseconds);
-
-void BM_Duration_ToInt64Seconds(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(100000);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToInt64Seconds(d));
-  }
-}
-BENCHMARK(BM_Duration_ToInt64Seconds);
-
-void BM_Duration_ToInt64Minutes(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(100000);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToInt64Minutes(d));
-  }
-}
-BENCHMARK(BM_Duration_ToInt64Minutes);
-
-void BM_Duration_ToInt64Hours(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(100000);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToInt64Hours(d));
-  }
-}
-BENCHMARK(BM_Duration_ToInt64Hours);
-
-//
-// To/FromTimespec
-//
-
-void BM_Duration_ToTimespec_AbslTime(benchmark::State& state) {
-  absl::Duration d = absl::Seconds(1);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToTimespec(d));
-  }
-}
-BENCHMARK(BM_Duration_ToTimespec_AbslTime);
-
-ABSL_ATTRIBUTE_NOINLINE timespec DoubleToTimespec(double seconds) {
-  timespec ts;
-  ts.tv_sec = seconds;
-  ts.tv_nsec = (seconds - ts.tv_sec) * (1000 * 1000 * 1000);
-  return ts;
-}
-
-void BM_Duration_ToTimespec_Double(benchmark::State& state) {
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(DoubleToTimespec(1.0));
-  }
-}
-BENCHMARK(BM_Duration_ToTimespec_Double);
-
-void BM_Duration_FromTimespec_AbslTime(benchmark::State& state) {
-  timespec ts;
-  ts.tv_sec = 0;
-  ts.tv_nsec = 0;
-  while (state.KeepRunning()) {
-    if (++ts.tv_nsec == 1000 * 1000 * 1000) {
-      ++ts.tv_sec;
-      ts.tv_nsec = 0;
-    }
-    benchmark::DoNotOptimize(absl::DurationFromTimespec(ts));
-  }
-}
-BENCHMARK(BM_Duration_FromTimespec_AbslTime);
-
-ABSL_ATTRIBUTE_NOINLINE double TimespecToDouble(timespec ts) {
-  return ts.tv_sec + (ts.tv_nsec / (1000 * 1000 * 1000));
-}
-
-void BM_Duration_FromTimespec_Double(benchmark::State& state) {
-  timespec ts;
-  ts.tv_sec = 0;
-  ts.tv_nsec = 0;
-  while (state.KeepRunning()) {
-    if (++ts.tv_nsec == 1000 * 1000 * 1000) {
-      ++ts.tv_sec;
-      ts.tv_nsec = 0;
-    }
-    benchmark::DoNotOptimize(TimespecToDouble(ts));
-  }
-}
-BENCHMARK(BM_Duration_FromTimespec_Double);
-
-//
-// String conversions
-//
-
-const char* const kDurations[] = {
-    "0",                                   // 0
-    "123ns",                               // 1
-    "1h2m3s",                              // 2
-    "-2h3m4.005006007s",                   // 3
-    "2562047788015215h30m7.99999999975s",  // 4
-};
-const int kNumDurations = sizeof(kDurations) / sizeof(kDurations[0]);
-
-void BM_Duration_FormatDuration(benchmark::State& state) {
-  const std::string s = kDurations[state.range(0)];
-  state.SetLabel(s);
-  absl::Duration d;
-  absl::ParseDuration(kDurations[state.range(0)], &d);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::FormatDuration(d));
-  }
-}
-BENCHMARK(BM_Duration_FormatDuration)->DenseRange(0, kNumDurations - 1);
-
-void BM_Duration_ParseDuration(benchmark::State& state) {
-  const std::string s = kDurations[state.range(0)];
-  state.SetLabel(s);
-  absl::Duration d;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ParseDuration(s, &d));
-  }
-}
-BENCHMARK(BM_Duration_ParseDuration)->DenseRange(0, kNumDurations - 1);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/duration_test.cc b/third_party/abseil_cpp/absl/time/duration_test.cc
deleted file mode 100644
index 4d85a2c4f4..0000000000
--- a/third_party/abseil_cpp/absl/time/duration_test.cc
+++ /dev/null
@@ -1,1808 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#if defined(_MSC_VER)
-#include <winsock2.h>  // for timeval
-#endif
-
-#include <chrono>  // NOLINT(build/c++11)
-#include <cmath>
-#include <cstdint>
-#include <ctime>
-#include <iomanip>
-#include <limits>
-#include <random>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/time/time.h"
-
-namespace {
-
-constexpr int64_t kint64max = std::numeric_limits<int64_t>::max();
-constexpr int64_t kint64min = std::numeric_limits<int64_t>::min();
-
-// Approximates the given number of years. This is only used to make some test
-// code more readable.
-absl::Duration ApproxYears(int64_t n) { return absl::Hours(n) * 365 * 24; }
-
-// A gMock matcher to match timespec values. Use this matcher like:
-// timespec ts1, ts2;
-// EXPECT_THAT(ts1, TimespecMatcher(ts2));
-MATCHER_P(TimespecMatcher, ts, "") {
-  if (ts.tv_sec == arg.tv_sec && ts.tv_nsec == arg.tv_nsec)
-    return true;
-  *result_listener << "expected: {" << ts.tv_sec << ", " << ts.tv_nsec << "} ";
-  *result_listener << "actual: {" << arg.tv_sec << ", " << arg.tv_nsec << "}";
-  return false;
-}
-
-// A gMock matcher to match timeval values. Use this matcher like:
-// timeval tv1, tv2;
-// EXPECT_THAT(tv1, TimevalMatcher(tv2));
-MATCHER_P(TimevalMatcher, tv, "") {
-  if (tv.tv_sec == arg.tv_sec && tv.tv_usec == arg.tv_usec)
-    return true;
-  *result_listener << "expected: {" << tv.tv_sec << ", " << tv.tv_usec << "} ";
-  *result_listener << "actual: {" << arg.tv_sec << ", " << arg.tv_usec << "}";
-  return false;
-}
-
-TEST(Duration, ConstExpr) {
-  constexpr absl::Duration d0 = absl::ZeroDuration();
-  static_assert(d0 == absl::ZeroDuration(), "ZeroDuration()");
-  constexpr absl::Duration d1 = absl::Seconds(1);
-  static_assert(d1 == absl::Seconds(1), "Seconds(1)");
-  static_assert(d1 != absl::ZeroDuration(), "Seconds(1)");
-  constexpr absl::Duration d2 = absl::InfiniteDuration();
-  static_assert(d2 == absl::InfiniteDuration(), "InfiniteDuration()");
-  static_assert(d2 != absl::ZeroDuration(), "InfiniteDuration()");
-}
-
-TEST(Duration, ValueSemantics) {
-  // If this compiles, the test passes.
-  constexpr absl::Duration a;      // Default construction
-  constexpr absl::Duration b = a;  // Copy construction
-  constexpr absl::Duration c(b);   // Copy construction (again)
-
-  absl::Duration d;
-  d = c;  // Assignment
-}
-
-TEST(Duration, Factories) {
-  constexpr absl::Duration zero = absl::ZeroDuration();
-  constexpr absl::Duration nano = absl::Nanoseconds(1);
-  constexpr absl::Duration micro = absl::Microseconds(1);
-  constexpr absl::Duration milli = absl::Milliseconds(1);
-  constexpr absl::Duration sec = absl::Seconds(1);
-  constexpr absl::Duration min = absl::Minutes(1);
-  constexpr absl::Duration hour = absl::Hours(1);
-
-  EXPECT_EQ(zero, absl::Duration());
-  EXPECT_EQ(zero, absl::Seconds(0));
-  EXPECT_EQ(nano, absl::Nanoseconds(1));
-  EXPECT_EQ(micro, absl::Nanoseconds(1000));
-  EXPECT_EQ(milli, absl::Microseconds(1000));
-  EXPECT_EQ(sec, absl::Milliseconds(1000));
-  EXPECT_EQ(min, absl::Seconds(60));
-  EXPECT_EQ(hour, absl::Minutes(60));
-
-  // Tests factory limits
-  const absl::Duration inf = absl::InfiniteDuration();
-
-  EXPECT_GT(inf, absl::Seconds(kint64max));
-  EXPECT_LT(-inf, absl::Seconds(kint64min));
-  EXPECT_LT(-inf, absl::Seconds(-kint64max));
-
-  EXPECT_EQ(inf, absl::Minutes(kint64max));
-  EXPECT_EQ(-inf, absl::Minutes(kint64min));
-  EXPECT_EQ(-inf, absl::Minutes(-kint64max));
-  EXPECT_GT(inf, absl::Minutes(kint64max / 60));
-  EXPECT_LT(-inf, absl::Minutes(kint64min / 60));
-  EXPECT_LT(-inf, absl::Minutes(-kint64max / 60));
-
-  EXPECT_EQ(inf, absl::Hours(kint64max));
-  EXPECT_EQ(-inf, absl::Hours(kint64min));
-  EXPECT_EQ(-inf, absl::Hours(-kint64max));
-  EXPECT_GT(inf, absl::Hours(kint64max / 3600));
-  EXPECT_LT(-inf, absl::Hours(kint64min / 3600));
-  EXPECT_LT(-inf, absl::Hours(-kint64max / 3600));
-}
-
-TEST(Duration, ToConversion) {
-#define TEST_DURATION_CONVERSION(UNIT)                                  \
-  do {                                                                  \
-    const absl::Duration d = absl::UNIT(1.5);                           \
-    constexpr absl::Duration z = absl::ZeroDuration();                  \
-    constexpr absl::Duration inf = absl::InfiniteDuration();            \
-    constexpr double dbl_inf = std::numeric_limits<double>::infinity(); \
-    EXPECT_EQ(kint64min, absl::ToInt64##UNIT(-inf));                    \
-    EXPECT_EQ(-1, absl::ToInt64##UNIT(-d));                             \
-    EXPECT_EQ(0, absl::ToInt64##UNIT(z));                               \
-    EXPECT_EQ(1, absl::ToInt64##UNIT(d));                               \
-    EXPECT_EQ(kint64max, absl::ToInt64##UNIT(inf));                     \
-    EXPECT_EQ(-dbl_inf, absl::ToDouble##UNIT(-inf));                    \
-    EXPECT_EQ(-1.5, absl::ToDouble##UNIT(-d));                          \
-    EXPECT_EQ(0, absl::ToDouble##UNIT(z));                              \
-    EXPECT_EQ(1.5, absl::ToDouble##UNIT(d));                            \
-    EXPECT_EQ(dbl_inf, absl::ToDouble##UNIT(inf));                      \
-  } while (0)
-
-  TEST_DURATION_CONVERSION(Nanoseconds);
-  TEST_DURATION_CONVERSION(Microseconds);
-  TEST_DURATION_CONVERSION(Milliseconds);
-  TEST_DURATION_CONVERSION(Seconds);
-  TEST_DURATION_CONVERSION(Minutes);
-  TEST_DURATION_CONVERSION(Hours);
-
-#undef TEST_DURATION_CONVERSION
-}
-
-template <int64_t N>
-void TestToConversion() {
-  constexpr absl::Duration nano = absl::Nanoseconds(N);
-  EXPECT_EQ(N, absl::ToInt64Nanoseconds(nano));
-  EXPECT_EQ(0, absl::ToInt64Microseconds(nano));
-  EXPECT_EQ(0, absl::ToInt64Milliseconds(nano));
-  EXPECT_EQ(0, absl::ToInt64Seconds(nano));
-  EXPECT_EQ(0, absl::ToInt64Minutes(nano));
-  EXPECT_EQ(0, absl::ToInt64Hours(nano));
-  const absl::Duration micro = absl::Microseconds(N);
-  EXPECT_EQ(N * 1000, absl::ToInt64Nanoseconds(micro));
-  EXPECT_EQ(N, absl::ToInt64Microseconds(micro));
-  EXPECT_EQ(0, absl::ToInt64Milliseconds(micro));
-  EXPECT_EQ(0, absl::ToInt64Seconds(micro));
-  EXPECT_EQ(0, absl::ToInt64Minutes(micro));
-  EXPECT_EQ(0, absl::ToInt64Hours(micro));
-  const absl::Duration milli = absl::Milliseconds(N);
-  EXPECT_EQ(N * 1000 * 1000, absl::ToInt64Nanoseconds(milli));
-  EXPECT_EQ(N * 1000, absl::ToInt64Microseconds(milli));
-  EXPECT_EQ(N, absl::ToInt64Milliseconds(milli));
-  EXPECT_EQ(0, absl::ToInt64Seconds(milli));
-  EXPECT_EQ(0, absl::ToInt64Minutes(milli));
-  EXPECT_EQ(0, absl::ToInt64Hours(milli));
-  const absl::Duration sec = absl::Seconds(N);
-  EXPECT_EQ(N * 1000 * 1000 * 1000, absl::ToInt64Nanoseconds(sec));
-  EXPECT_EQ(N * 1000 * 1000, absl::ToInt64Microseconds(sec));
-  EXPECT_EQ(N * 1000, absl::ToInt64Milliseconds(sec));
-  EXPECT_EQ(N, absl::ToInt64Seconds(sec));
-  EXPECT_EQ(0, absl::ToInt64Minutes(sec));
-  EXPECT_EQ(0, absl::ToInt64Hours(sec));
-  const absl::Duration min = absl::Minutes(N);
-  EXPECT_EQ(N * 60 * 1000 * 1000 * 1000, absl::ToInt64Nanoseconds(min));
-  EXPECT_EQ(N * 60 * 1000 * 1000, absl::ToInt64Microseconds(min));
-  EXPECT_EQ(N * 60 * 1000, absl::ToInt64Milliseconds(min));
-  EXPECT_EQ(N * 60, absl::ToInt64Seconds(min));
-  EXPECT_EQ(N, absl::ToInt64Minutes(min));
-  EXPECT_EQ(0, absl::ToInt64Hours(min));
-  const absl::Duration hour = absl::Hours(N);
-  EXPECT_EQ(N * 60 * 60 * 1000 * 1000 * 1000, absl::ToInt64Nanoseconds(hour));
-  EXPECT_EQ(N * 60 * 60 * 1000 * 1000, absl::ToInt64Microseconds(hour));
-  EXPECT_EQ(N * 60 * 60 * 1000, absl::ToInt64Milliseconds(hour));
-  EXPECT_EQ(N * 60 * 60, absl::ToInt64Seconds(hour));
-  EXPECT_EQ(N * 60, absl::ToInt64Minutes(hour));
-  EXPECT_EQ(N, absl::ToInt64Hours(hour));
-}
-
-TEST(Duration, ToConversionDeprecated) {
-  TestToConversion<43>();
-  TestToConversion<1>();
-  TestToConversion<0>();
-  TestToConversion<-1>();
-  TestToConversion<-43>();
-}
-
-template <int64_t N>
-void TestFromChronoBasicEquality() {
-  using std::chrono::nanoseconds;
-  using std::chrono::microseconds;
-  using std::chrono::milliseconds;
-  using std::chrono::seconds;
-  using std::chrono::minutes;
-  using std::chrono::hours;
-
-  static_assert(absl::Nanoseconds(N) == absl::FromChrono(nanoseconds(N)), "");
-  static_assert(absl::Microseconds(N) == absl::FromChrono(microseconds(N)), "");
-  static_assert(absl::Milliseconds(N) == absl::FromChrono(milliseconds(N)), "");
-  static_assert(absl::Seconds(N) == absl::FromChrono(seconds(N)), "");
-  static_assert(absl::Minutes(N) == absl::FromChrono(minutes(N)), "");
-  static_assert(absl::Hours(N) == absl::FromChrono(hours(N)), "");
-}
-
-TEST(Duration, FromChrono) {
-  TestFromChronoBasicEquality<-123>();
-  TestFromChronoBasicEquality<-1>();
-  TestFromChronoBasicEquality<0>();
-  TestFromChronoBasicEquality<1>();
-  TestFromChronoBasicEquality<123>();
-
-  // Minutes (might, depending on the platform) saturate at +inf.
-  const auto chrono_minutes_max = std::chrono::minutes::max();
-  const auto minutes_max = absl::FromChrono(chrono_minutes_max);
-  const int64_t minutes_max_count = chrono_minutes_max.count();
-  if (minutes_max_count > kint64max / 60) {
-    EXPECT_EQ(absl::InfiniteDuration(), minutes_max);
-  } else {
-    EXPECT_EQ(absl::Minutes(minutes_max_count), minutes_max);
-  }
-
-  // Minutes (might, depending on the platform) saturate at -inf.
-  const auto chrono_minutes_min = std::chrono::minutes::min();
-  const auto minutes_min = absl::FromChrono(chrono_minutes_min);
-  const int64_t minutes_min_count = chrono_minutes_min.count();
-  if (minutes_min_count < kint64min / 60) {
-    EXPECT_EQ(-absl::InfiniteDuration(), minutes_min);
-  } else {
-    EXPECT_EQ(absl::Minutes(minutes_min_count), minutes_min);
-  }
-
-  // Hours (might, depending on the platform) saturate at +inf.
-  const auto chrono_hours_max = std::chrono::hours::max();
-  const auto hours_max = absl::FromChrono(chrono_hours_max);
-  const int64_t hours_max_count = chrono_hours_max.count();
-  if (hours_max_count > kint64max / 3600) {
-    EXPECT_EQ(absl::InfiniteDuration(), hours_max);
-  } else {
-    EXPECT_EQ(absl::Hours(hours_max_count), hours_max);
-  }
-
-  // Hours (might, depending on the platform) saturate at -inf.
-  const auto chrono_hours_min = std::chrono::hours::min();
-  const auto hours_min = absl::FromChrono(chrono_hours_min);
-  const int64_t hours_min_count = chrono_hours_min.count();
-  if (hours_min_count < kint64min / 3600) {
-    EXPECT_EQ(-absl::InfiniteDuration(), hours_min);
-  } else {
-    EXPECT_EQ(absl::Hours(hours_min_count), hours_min);
-  }
-}
-
-template <int64_t N>
-void TestToChrono() {
-  using std::chrono::nanoseconds;
-  using std::chrono::microseconds;
-  using std::chrono::milliseconds;
-  using std::chrono::seconds;
-  using std::chrono::minutes;
-  using std::chrono::hours;
-
-  EXPECT_EQ(nanoseconds(N), absl::ToChronoNanoseconds(absl::Nanoseconds(N)));
-  EXPECT_EQ(microseconds(N), absl::ToChronoMicroseconds(absl::Microseconds(N)));
-  EXPECT_EQ(milliseconds(N), absl::ToChronoMilliseconds(absl::Milliseconds(N)));
-  EXPECT_EQ(seconds(N), absl::ToChronoSeconds(absl::Seconds(N)));
-
-  constexpr auto absl_minutes = absl::Minutes(N);
-  auto chrono_minutes = minutes(N);
-  if (absl_minutes == -absl::InfiniteDuration()) {
-    chrono_minutes = minutes::min();
-  } else if (absl_minutes == absl::InfiniteDuration()) {
-    chrono_minutes = minutes::max();
-  }
-  EXPECT_EQ(chrono_minutes, absl::ToChronoMinutes(absl_minutes));
-
-  constexpr auto absl_hours = absl::Hours(N);
-  auto chrono_hours = hours(N);
-  if (absl_hours == -absl::InfiniteDuration()) {
-    chrono_hours = hours::min();
-  } else if (absl_hours == absl::InfiniteDuration()) {
-    chrono_hours = hours::max();
-  }
-  EXPECT_EQ(chrono_hours, absl::ToChronoHours(absl_hours));
-}
-
-TEST(Duration, ToChrono) {
-  using std::chrono::nanoseconds;
-  using std::chrono::microseconds;
-  using std::chrono::milliseconds;
-  using std::chrono::seconds;
-  using std::chrono::minutes;
-  using std::chrono::hours;
-
-  TestToChrono<kint64min>();
-  TestToChrono<-1>();
-  TestToChrono<0>();
-  TestToChrono<1>();
-  TestToChrono<kint64max>();
-
-  // Verify truncation toward zero.
-  const auto tick = absl::Nanoseconds(1) / 4;
-  EXPECT_EQ(nanoseconds(0), absl::ToChronoNanoseconds(tick));
-  EXPECT_EQ(nanoseconds(0), absl::ToChronoNanoseconds(-tick));
-  EXPECT_EQ(microseconds(0), absl::ToChronoMicroseconds(tick));
-  EXPECT_EQ(microseconds(0), absl::ToChronoMicroseconds(-tick));
-  EXPECT_EQ(milliseconds(0), absl::ToChronoMilliseconds(tick));
-  EXPECT_EQ(milliseconds(0), absl::ToChronoMilliseconds(-tick));
-  EXPECT_EQ(seconds(0), absl::ToChronoSeconds(tick));
-  EXPECT_EQ(seconds(0), absl::ToChronoSeconds(-tick));
-  EXPECT_EQ(minutes(0), absl::ToChronoMinutes(tick));
-  EXPECT_EQ(minutes(0), absl::ToChronoMinutes(-tick));
-  EXPECT_EQ(hours(0), absl::ToChronoHours(tick));
-  EXPECT_EQ(hours(0), absl::ToChronoHours(-tick));
-
-  // Verifies +/- infinity saturation at max/min.
-  constexpr auto inf = absl::InfiniteDuration();
-  EXPECT_EQ(nanoseconds::min(), absl::ToChronoNanoseconds(-inf));
-  EXPECT_EQ(nanoseconds::max(), absl::ToChronoNanoseconds(inf));
-  EXPECT_EQ(microseconds::min(), absl::ToChronoMicroseconds(-inf));
-  EXPECT_EQ(microseconds::max(), absl::ToChronoMicroseconds(inf));
-  EXPECT_EQ(milliseconds::min(), absl::ToChronoMilliseconds(-inf));
-  EXPECT_EQ(milliseconds::max(), absl::ToChronoMilliseconds(inf));
-  EXPECT_EQ(seconds::min(), absl::ToChronoSeconds(-inf));
-  EXPECT_EQ(seconds::max(), absl::ToChronoSeconds(inf));
-  EXPECT_EQ(minutes::min(), absl::ToChronoMinutes(-inf));
-  EXPECT_EQ(minutes::max(), absl::ToChronoMinutes(inf));
-  EXPECT_EQ(hours::min(), absl::ToChronoHours(-inf));
-  EXPECT_EQ(hours::max(), absl::ToChronoHours(inf));
-}
-
-TEST(Duration, FactoryOverloads) {
-  enum E { kOne = 1 };
-#define TEST_FACTORY_OVERLOADS(NAME)                                          \
-  EXPECT_EQ(1, NAME(kOne) / NAME(kOne));                                      \
-  EXPECT_EQ(1, NAME(static_cast<int8_t>(1)) / NAME(1));                       \
-  EXPECT_EQ(1, NAME(static_cast<int16_t>(1)) / NAME(1));                      \
-  EXPECT_EQ(1, NAME(static_cast<int32_t>(1)) / NAME(1));                      \
-  EXPECT_EQ(1, NAME(static_cast<int64_t>(1)) / NAME(1));                      \
-  EXPECT_EQ(1, NAME(static_cast<uint8_t>(1)) / NAME(1));                      \
-  EXPECT_EQ(1, NAME(static_cast<uint16_t>(1)) / NAME(1));                     \
-  EXPECT_EQ(1, NAME(static_cast<uint32_t>(1)) / NAME(1));                     \
-  EXPECT_EQ(1, NAME(static_cast<uint64_t>(1)) / NAME(1));                     \
-  EXPECT_EQ(NAME(1) / 2, NAME(static_cast<float>(0.5)));                      \
-  EXPECT_EQ(NAME(1) / 2, NAME(static_cast<double>(0.5)));                     \
-  EXPECT_EQ(1.5, absl::FDivDuration(NAME(static_cast<float>(1.5)), NAME(1))); \
-  EXPECT_EQ(1.5, absl::FDivDuration(NAME(static_cast<double>(1.5)), NAME(1)));
-
-  TEST_FACTORY_OVERLOADS(absl::Nanoseconds);
-  TEST_FACTORY_OVERLOADS(absl::Microseconds);
-  TEST_FACTORY_OVERLOADS(absl::Milliseconds);
-  TEST_FACTORY_OVERLOADS(absl::Seconds);
-  TEST_FACTORY_OVERLOADS(absl::Minutes);
-  TEST_FACTORY_OVERLOADS(absl::Hours);
-
-#undef TEST_FACTORY_OVERLOADS
-
-  EXPECT_EQ(absl::Milliseconds(1500), absl::Seconds(1.5));
-  EXPECT_LT(absl::Nanoseconds(1), absl::Nanoseconds(1.5));
-  EXPECT_GT(absl::Nanoseconds(2), absl::Nanoseconds(1.5));
-
-  const double dbl_inf = std::numeric_limits<double>::infinity();
-  EXPECT_EQ(absl::InfiniteDuration(), absl::Nanoseconds(dbl_inf));
-  EXPECT_EQ(absl::InfiniteDuration(), absl::Microseconds(dbl_inf));
-  EXPECT_EQ(absl::InfiniteDuration(), absl::Milliseconds(dbl_inf));
-  EXPECT_EQ(absl::InfiniteDuration(), absl::Seconds(dbl_inf));
-  EXPECT_EQ(absl::InfiniteDuration(), absl::Minutes(dbl_inf));
-  EXPECT_EQ(absl::InfiniteDuration(), absl::Hours(dbl_inf));
-  EXPECT_EQ(-absl::InfiniteDuration(), absl::Nanoseconds(-dbl_inf));
-  EXPECT_EQ(-absl::InfiniteDuration(), absl::Microseconds(-dbl_inf));
-  EXPECT_EQ(-absl::InfiniteDuration(), absl::Milliseconds(-dbl_inf));
-  EXPECT_EQ(-absl::InfiniteDuration(), absl::Seconds(-dbl_inf));
-  EXPECT_EQ(-absl::InfiniteDuration(), absl::Minutes(-dbl_inf));
-  EXPECT_EQ(-absl::InfiniteDuration(), absl::Hours(-dbl_inf));
-}
-
-TEST(Duration, InfinityExamples) {
-  // These examples are used in the documentation in time.h. They are
-  // written so that they can be copy-n-pasted easily.
-
-  constexpr absl::Duration inf = absl::InfiniteDuration();
-  constexpr absl::Duration d = absl::Seconds(1);  // Any finite duration
-
-  EXPECT_TRUE(inf == inf + inf);
-  EXPECT_TRUE(inf == inf + d);
-  EXPECT_TRUE(inf == inf - inf);
-  EXPECT_TRUE(-inf == d - inf);
-
-  EXPECT_TRUE(inf == d * 1e100);
-  EXPECT_TRUE(0 == d / inf);  // NOLINT(readability/check)
-
-  // Division by zero returns infinity, or kint64min/MAX where necessary.
-  EXPECT_TRUE(inf == d / 0);
-  EXPECT_TRUE(kint64max == d / absl::ZeroDuration());
-}
-
-TEST(Duration, InfinityComparison) {
-  const absl::Duration inf = absl::InfiniteDuration();
-  const absl::Duration any_dur = absl::Seconds(1);
-
-  // Equality
-  EXPECT_EQ(inf, inf);
-  EXPECT_EQ(-inf, -inf);
-  EXPECT_NE(inf, -inf);
-  EXPECT_NE(any_dur, inf);
-  EXPECT_NE(any_dur, -inf);
-
-  // Relational
-  EXPECT_GT(inf, any_dur);
-  EXPECT_LT(-inf, any_dur);
-  EXPECT_LT(-inf, inf);
-  EXPECT_GT(inf, -inf);
-}
-
-TEST(Duration, InfinityAddition) {
-  const absl::Duration sec_max = absl::Seconds(kint64max);
-  const absl::Duration sec_min = absl::Seconds(kint64min);
-  const absl::Duration any_dur = absl::Seconds(1);
-  const absl::Duration inf = absl::InfiniteDuration();
-
-  // Addition
-  EXPECT_EQ(inf, inf + inf);
-  EXPECT_EQ(inf, inf + -inf);
-  EXPECT_EQ(-inf, -inf + inf);
-  EXPECT_EQ(-inf, -inf + -inf);
-
-  EXPECT_EQ(inf, inf + any_dur);
-  EXPECT_EQ(inf, any_dur + inf);
-  EXPECT_EQ(-inf, -inf + any_dur);
-  EXPECT_EQ(-inf, any_dur + -inf);
-
-  // Interesting case
-  absl::Duration almost_inf = sec_max + absl::Nanoseconds(999999999);
-  EXPECT_GT(inf, almost_inf);
-  almost_inf += -absl::Nanoseconds(999999999);
-  EXPECT_GT(inf, almost_inf);
-
-  // Addition overflow/underflow
-  EXPECT_EQ(inf, sec_max + absl::Seconds(1));
-  EXPECT_EQ(inf, sec_max + sec_max);
-  EXPECT_EQ(-inf, sec_min + -absl::Seconds(1));
-  EXPECT_EQ(-inf, sec_min + -sec_max);
-
-  // For reference: IEEE 754 behavior
-  const double dbl_inf = std::numeric_limits<double>::infinity();
-  EXPECT_TRUE(std::isinf(dbl_inf + dbl_inf));
-  EXPECT_TRUE(std::isnan(dbl_inf + -dbl_inf));  // We return inf
-  EXPECT_TRUE(std::isnan(-dbl_inf + dbl_inf));  // We return inf
-  EXPECT_TRUE(std::isinf(-dbl_inf + -dbl_inf));
-}
-
-TEST(Duration, InfinitySubtraction) {
-  const absl::Duration sec_max = absl::Seconds(kint64max);
-  const absl::Duration sec_min = absl::Seconds(kint64min);
-  const absl::Duration any_dur = absl::Seconds(1);
-  const absl::Duration inf = absl::InfiniteDuration();
-
-  // Subtraction
-  EXPECT_EQ(inf, inf - inf);
-  EXPECT_EQ(inf, inf - -inf);
-  EXPECT_EQ(-inf, -inf - inf);
-  EXPECT_EQ(-inf, -inf - -inf);
-
-  EXPECT_EQ(inf, inf - any_dur);
-  EXPECT_EQ(-inf, any_dur - inf);
-  EXPECT_EQ(-inf, -inf - any_dur);
-  EXPECT_EQ(inf, any_dur - -inf);
-
-  // Subtraction overflow/underflow
-  EXPECT_EQ(inf, sec_max - -absl::Seconds(1));
-  EXPECT_EQ(inf, sec_max - -sec_max);
-  EXPECT_EQ(-inf, sec_min - absl::Seconds(1));
-  EXPECT_EQ(-inf, sec_min - sec_max);
-
-  // Interesting case
-  absl::Duration almost_neg_inf = sec_min;
-  EXPECT_LT(-inf, almost_neg_inf);
-  almost_neg_inf -= -absl::Nanoseconds(1);
-  EXPECT_LT(-inf, almost_neg_inf);
-
-  // For reference: IEEE 754 behavior
-  const double dbl_inf = std::numeric_limits<double>::infinity();
-  EXPECT_TRUE(std::isnan(dbl_inf - dbl_inf));  // We return inf
-  EXPECT_TRUE(std::isinf(dbl_inf - -dbl_inf));
-  EXPECT_TRUE(std::isinf(-dbl_inf - dbl_inf));
-  EXPECT_TRUE(std::isnan(-dbl_inf - -dbl_inf));  // We return inf
-}
-
-TEST(Duration, InfinityMultiplication) {
-  const absl::Duration sec_max = absl::Seconds(kint64max);
-  const absl::Duration sec_min = absl::Seconds(kint64min);
-  const absl::Duration inf = absl::InfiniteDuration();
-
-#define TEST_INF_MUL_WITH_TYPE(T)                                     \
-  EXPECT_EQ(inf, inf * static_cast<T>(2));                            \
-  EXPECT_EQ(-inf, inf * static_cast<T>(-2));                          \
-  EXPECT_EQ(-inf, -inf * static_cast<T>(2));                          \
-  EXPECT_EQ(inf, -inf * static_cast<T>(-2));                          \
-  EXPECT_EQ(inf, inf * static_cast<T>(0));                            \
-  EXPECT_EQ(-inf, -inf * static_cast<T>(0));                          \
-  EXPECT_EQ(inf, sec_max * static_cast<T>(2));                        \
-  EXPECT_EQ(inf, sec_min * static_cast<T>(-2));                       \
-  EXPECT_EQ(inf, (sec_max / static_cast<T>(2)) * static_cast<T>(3));  \
-  EXPECT_EQ(-inf, sec_max * static_cast<T>(-2));                      \
-  EXPECT_EQ(-inf, sec_min * static_cast<T>(2));                       \
-  EXPECT_EQ(-inf, (sec_min / static_cast<T>(2)) * static_cast<T>(3));
-
-  TEST_INF_MUL_WITH_TYPE(int64_t);  // NOLINT(readability/function)
-  TEST_INF_MUL_WITH_TYPE(double);   // NOLINT(readability/function)
-
-#undef TEST_INF_MUL_WITH_TYPE
-
-  const double dbl_inf = std::numeric_limits<double>::infinity();
-  EXPECT_EQ(inf, inf * dbl_inf);
-  EXPECT_EQ(-inf, -inf * dbl_inf);
-  EXPECT_EQ(-inf, inf * -dbl_inf);
-  EXPECT_EQ(inf, -inf * -dbl_inf);
-
-  const absl::Duration any_dur = absl::Seconds(1);
-  EXPECT_EQ(inf, any_dur * dbl_inf);
-  EXPECT_EQ(-inf, -any_dur * dbl_inf);
-  EXPECT_EQ(-inf, any_dur * -dbl_inf);
-  EXPECT_EQ(inf, -any_dur * -dbl_inf);
-
-  // Fixed-point multiplication will produce a finite value, whereas floating
-  // point fuzziness will overflow to inf.
-  EXPECT_NE(absl::InfiniteDuration(), absl::Seconds(1) * kint64max);
-  EXPECT_EQ(inf, absl::Seconds(1) * static_cast<double>(kint64max));
-  EXPECT_NE(-absl::InfiniteDuration(), absl::Seconds(1) * kint64min);
-  EXPECT_EQ(-inf, absl::Seconds(1) * static_cast<double>(kint64min));
-
-  // Note that sec_max * or / by 1.0 overflows to inf due to the 53-bit
-  // limitations of double.
-  EXPECT_NE(inf, sec_max);
-  EXPECT_NE(inf, sec_max / 1);
-  EXPECT_EQ(inf, sec_max / 1.0);
-  EXPECT_NE(inf, sec_max * 1);
-  EXPECT_EQ(inf, sec_max * 1.0);
-}
-
-TEST(Duration, InfinityDivision) {
-  const absl::Duration sec_max = absl::Seconds(kint64max);
-  const absl::Duration sec_min = absl::Seconds(kint64min);
-  const absl::Duration inf = absl::InfiniteDuration();
-
-  // Division of Duration by a double
-#define TEST_INF_DIV_WITH_TYPE(T)            \
-  EXPECT_EQ(inf, inf / static_cast<T>(2));   \
-  EXPECT_EQ(-inf, inf / static_cast<T>(-2)); \
-  EXPECT_EQ(-inf, -inf / static_cast<T>(2)); \
-  EXPECT_EQ(inf, -inf / static_cast<T>(-2));
-
-  TEST_INF_DIV_WITH_TYPE(int64_t);  // NOLINT(readability/function)
-  TEST_INF_DIV_WITH_TYPE(double);   // NOLINT(readability/function)
-
-#undef TEST_INF_DIV_WITH_TYPE
-
-  // Division of Duration by a double overflow/underflow
-  EXPECT_EQ(inf, sec_max / 0.5);
-  EXPECT_EQ(inf, sec_min / -0.5);
-  EXPECT_EQ(inf, ((sec_max / 0.5) + absl::Seconds(1)) / 0.5);
-  EXPECT_EQ(-inf, sec_max / -0.5);
-  EXPECT_EQ(-inf, sec_min / 0.5);
-  EXPECT_EQ(-inf, ((sec_min / 0.5) - absl::Seconds(1)) / 0.5);
-
-  const double dbl_inf = std::numeric_limits<double>::infinity();
-  EXPECT_EQ(inf, inf / dbl_inf);
-  EXPECT_EQ(-inf, inf / -dbl_inf);
-  EXPECT_EQ(-inf, -inf / dbl_inf);
-  EXPECT_EQ(inf, -inf / -dbl_inf);
-
-  const absl::Duration any_dur = absl::Seconds(1);
-  EXPECT_EQ(absl::ZeroDuration(), any_dur / dbl_inf);
-  EXPECT_EQ(absl::ZeroDuration(), any_dur / -dbl_inf);
-  EXPECT_EQ(absl::ZeroDuration(), -any_dur / dbl_inf);
-  EXPECT_EQ(absl::ZeroDuration(), -any_dur / -dbl_inf);
-}
-
-TEST(Duration, InfinityModulus) {
-  const absl::Duration sec_max = absl::Seconds(kint64max);
-  const absl::Duration any_dur = absl::Seconds(1);
-  const absl::Duration inf = absl::InfiniteDuration();
-
-  EXPECT_EQ(inf, inf % inf);
-  EXPECT_EQ(inf, inf % -inf);
-  EXPECT_EQ(-inf, -inf % -inf);
-  EXPECT_EQ(-inf, -inf % inf);
-
-  EXPECT_EQ(any_dur, any_dur % inf);
-  EXPECT_EQ(any_dur, any_dur % -inf);
-  EXPECT_EQ(-any_dur, -any_dur % inf);
-  EXPECT_EQ(-any_dur, -any_dur % -inf);
-
-  EXPECT_EQ(inf, inf % -any_dur);
-  EXPECT_EQ(inf, inf % any_dur);
-  EXPECT_EQ(-inf, -inf % -any_dur);
-  EXPECT_EQ(-inf, -inf % any_dur);
-
-  // Remainder isn't affected by overflow.
-  EXPECT_EQ(absl::ZeroDuration(), sec_max % absl::Seconds(1));
-  EXPECT_EQ(absl::ZeroDuration(), sec_max % absl::Milliseconds(1));
-  EXPECT_EQ(absl::ZeroDuration(), sec_max % absl::Microseconds(1));
-  EXPECT_EQ(absl::ZeroDuration(), sec_max % absl::Nanoseconds(1));
-  EXPECT_EQ(absl::ZeroDuration(), sec_max % absl::Nanoseconds(1) / 4);
-}
-
-TEST(Duration, InfinityIDiv) {
-  const absl::Duration sec_max = absl::Seconds(kint64max);
-  const absl::Duration any_dur = absl::Seconds(1);
-  const absl::Duration inf = absl::InfiniteDuration();
-  const double dbl_inf = std::numeric_limits<double>::infinity();
-
-  // IDivDuration (int64_t return value + a remainer)
-  absl::Duration rem = absl::ZeroDuration();
-  EXPECT_EQ(kint64max, absl::IDivDuration(inf, inf, &rem));
-  EXPECT_EQ(inf, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(kint64max, absl::IDivDuration(-inf, -inf, &rem));
-  EXPECT_EQ(-inf, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(kint64max, absl::IDivDuration(inf, any_dur, &rem));
-  EXPECT_EQ(inf, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(0, absl::IDivDuration(any_dur, inf, &rem));
-  EXPECT_EQ(any_dur, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(kint64max, absl::IDivDuration(-inf, -any_dur, &rem));
-  EXPECT_EQ(-inf, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(0, absl::IDivDuration(-any_dur, -inf, &rem));
-  EXPECT_EQ(-any_dur, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(kint64min, absl::IDivDuration(-inf, inf, &rem));
-  EXPECT_EQ(-inf, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(kint64min, absl::IDivDuration(inf, -inf, &rem));
-  EXPECT_EQ(inf, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(kint64min, absl::IDivDuration(-inf, any_dur, &rem));
-  EXPECT_EQ(-inf, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(0, absl::IDivDuration(-any_dur, inf, &rem));
-  EXPECT_EQ(-any_dur, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(kint64min, absl::IDivDuration(inf, -any_dur, &rem));
-  EXPECT_EQ(inf, rem);
-
-  rem = absl::ZeroDuration();
-  EXPECT_EQ(0, absl::IDivDuration(any_dur, -inf, &rem));
-  EXPECT_EQ(any_dur, rem);
-
-  // IDivDuration overflow/underflow
-  rem = any_dur;
-  EXPECT_EQ(kint64max,
-            absl::IDivDuration(sec_max, absl::Nanoseconds(1) / 4, &rem));
-  EXPECT_EQ(sec_max - absl::Nanoseconds(kint64max) / 4, rem);
-
-  rem = any_dur;
-  EXPECT_EQ(kint64max,
-            absl::IDivDuration(sec_max, absl::Milliseconds(1), &rem));
-  EXPECT_EQ(sec_max - absl::Milliseconds(kint64max), rem);
-
-  rem = any_dur;
-  EXPECT_EQ(kint64max,
-            absl::IDivDuration(-sec_max, -absl::Milliseconds(1), &rem));
-  EXPECT_EQ(-sec_max + absl::Milliseconds(kint64max), rem);
-
-  rem = any_dur;
-  EXPECT_EQ(kint64min,
-            absl::IDivDuration(-sec_max, absl::Milliseconds(1), &rem));
-  EXPECT_EQ(-sec_max - absl::Milliseconds(kint64min), rem);
-
-  rem = any_dur;
-  EXPECT_EQ(kint64min,
-            absl::IDivDuration(sec_max, -absl::Milliseconds(1), &rem));
-  EXPECT_EQ(sec_max + absl::Milliseconds(kint64min), rem);
-
-  //
-  // operator/(Duration, Duration) is a wrapper for IDivDuration().
-  //
-
-  // IEEE 754 says inf / inf should be nan, but int64_t doesn't have
-  // nan so we'll return kint64max/kint64min instead.
-  EXPECT_TRUE(std::isnan(dbl_inf / dbl_inf));
-  EXPECT_EQ(kint64max, inf / inf);
-  EXPECT_EQ(kint64max, -inf / -inf);
-  EXPECT_EQ(kint64min, -inf / inf);
-  EXPECT_EQ(kint64min, inf / -inf);
-
-  EXPECT_TRUE(std::isinf(dbl_inf / 2.0));
-  EXPECT_EQ(kint64max, inf / any_dur);
-  EXPECT_EQ(kint64max, -inf / -any_dur);
-  EXPECT_EQ(kint64min, -inf / any_dur);
-  EXPECT_EQ(kint64min, inf / -any_dur);
-
-  EXPECT_EQ(0.0, 2.0 / dbl_inf);
-  EXPECT_EQ(0, any_dur / inf);
-  EXPECT_EQ(0, any_dur / -inf);
-  EXPECT_EQ(0, -any_dur / inf);
-  EXPECT_EQ(0, -any_dur / -inf);
-  EXPECT_EQ(0, absl::ZeroDuration() / inf);
-
-  // Division of Duration by a Duration overflow/underflow
-  EXPECT_EQ(kint64max, sec_max / absl::Milliseconds(1));
-  EXPECT_EQ(kint64max, -sec_max / -absl::Milliseconds(1));
-  EXPECT_EQ(kint64min, -sec_max / absl::Milliseconds(1));
-  EXPECT_EQ(kint64min, sec_max / -absl::Milliseconds(1));
-}
-
-TEST(Duration, InfinityFDiv) {
-  const absl::Duration any_dur = absl::Seconds(1);
-  const absl::Duration inf = absl::InfiniteDuration();
-  const double dbl_inf = std::numeric_limits<double>::infinity();
-
-  EXPECT_EQ(dbl_inf, absl::FDivDuration(inf, inf));
-  EXPECT_EQ(dbl_inf, absl::FDivDuration(-inf, -inf));
-  EXPECT_EQ(dbl_inf, absl::FDivDuration(inf, any_dur));
-  EXPECT_EQ(0.0, absl::FDivDuration(any_dur, inf));
-  EXPECT_EQ(dbl_inf, absl::FDivDuration(-inf, -any_dur));
-  EXPECT_EQ(0.0, absl::FDivDuration(-any_dur, -inf));
-
-  EXPECT_EQ(-dbl_inf, absl::FDivDuration(-inf, inf));
-  EXPECT_EQ(-dbl_inf, absl::FDivDuration(inf, -inf));
-  EXPECT_EQ(-dbl_inf, absl::FDivDuration(-inf, any_dur));
-  EXPECT_EQ(0.0, absl::FDivDuration(-any_dur, inf));
-  EXPECT_EQ(-dbl_inf, absl::FDivDuration(inf, -any_dur));
-  EXPECT_EQ(0.0, absl::FDivDuration(any_dur, -inf));
-}
-
-TEST(Duration, DivisionByZero) {
-  const absl::Duration zero = absl::ZeroDuration();
-  const absl::Duration inf = absl::InfiniteDuration();
-  const absl::Duration any_dur = absl::Seconds(1);
-  const double dbl_inf = std::numeric_limits<double>::infinity();
-  const double dbl_denorm = std::numeric_limits<double>::denorm_min();
-
-  // Operator/(Duration, double)
-  EXPECT_EQ(inf, zero / 0.0);
-  EXPECT_EQ(-inf, zero / -0.0);
-  EXPECT_EQ(inf, any_dur / 0.0);
-  EXPECT_EQ(-inf, any_dur / -0.0);
-  EXPECT_EQ(-inf, -any_dur / 0.0);
-  EXPECT_EQ(inf, -any_dur / -0.0);
-
-  // Tests dividing by a number very close to, but not quite zero.
-  EXPECT_EQ(zero, zero / dbl_denorm);
-  EXPECT_EQ(zero, zero / -dbl_denorm);
-  EXPECT_EQ(inf, any_dur / dbl_denorm);
-  EXPECT_EQ(-inf, any_dur / -dbl_denorm);
-  EXPECT_EQ(-inf, -any_dur / dbl_denorm);
-  EXPECT_EQ(inf, -any_dur / -dbl_denorm);
-
-  // IDiv
-  absl::Duration rem = zero;
-  EXPECT_EQ(kint64max, absl::IDivDuration(zero, zero, &rem));
-  EXPECT_EQ(inf, rem);
-
-  rem = zero;
-  EXPECT_EQ(kint64max, absl::IDivDuration(any_dur, zero, &rem));
-  EXPECT_EQ(inf, rem);
-
-  rem = zero;
-  EXPECT_EQ(kint64min, absl::IDivDuration(-any_dur, zero, &rem));
-  EXPECT_EQ(-inf, rem);
-
-  // Operator/(Duration, Duration)
-  EXPECT_EQ(kint64max, zero / zero);
-  EXPECT_EQ(kint64max, any_dur / zero);
-  EXPECT_EQ(kint64min, -any_dur / zero);
-
-  // FDiv
-  EXPECT_EQ(dbl_inf, absl::FDivDuration(zero, zero));
-  EXPECT_EQ(dbl_inf, absl::FDivDuration(any_dur, zero));
-  EXPECT_EQ(-dbl_inf, absl::FDivDuration(-any_dur, zero));
-}
-
-TEST(Duration, NaN) {
-  // Note that IEEE 754 does not define the behavior of a nan's sign when it is
-  // copied, so the code below allows for either + or - InfiniteDuration.
-#define TEST_NAN_HANDLING(NAME, NAN)           \
-  do {                                         \
-    const auto inf = absl::InfiniteDuration(); \
-    auto x = NAME(NAN);                        \
-    EXPECT_TRUE(x == inf || x == -inf);        \
-    auto y = NAME(42);                         \
-    y *= NAN;                                  \
-    EXPECT_TRUE(y == inf || y == -inf);        \
-    auto z = NAME(42);                         \
-    z /= NAN;                                  \
-    EXPECT_TRUE(z == inf || z == -inf);        \
-  } while (0)
-
-  const double nan = std::numeric_limits<double>::quiet_NaN();
-  TEST_NAN_HANDLING(absl::Nanoseconds, nan);
-  TEST_NAN_HANDLING(absl::Microseconds, nan);
-  TEST_NAN_HANDLING(absl::Milliseconds, nan);
-  TEST_NAN_HANDLING(absl::Seconds, nan);
-  TEST_NAN_HANDLING(absl::Minutes, nan);
-  TEST_NAN_HANDLING(absl::Hours, nan);
-
-  TEST_NAN_HANDLING(absl::Nanoseconds, -nan);
-  TEST_NAN_HANDLING(absl::Microseconds, -nan);
-  TEST_NAN_HANDLING(absl::Milliseconds, -nan);
-  TEST_NAN_HANDLING(absl::Seconds, -nan);
-  TEST_NAN_HANDLING(absl::Minutes, -nan);
-  TEST_NAN_HANDLING(absl::Hours, -nan);
-
-#undef TEST_NAN_HANDLING
-}
-
-TEST(Duration, Range) {
-  const absl::Duration range = ApproxYears(100 * 1e9);
-  const absl::Duration range_future = range;
-  const absl::Duration range_past = -range;
-
-  EXPECT_LT(range_future, absl::InfiniteDuration());
-  EXPECT_GT(range_past, -absl::InfiniteDuration());
-
-  const absl::Duration full_range = range_future - range_past;
-  EXPECT_GT(full_range, absl::ZeroDuration());
-  EXPECT_LT(full_range, absl::InfiniteDuration());
-
-  const absl::Duration neg_full_range = range_past - range_future;
-  EXPECT_LT(neg_full_range, absl::ZeroDuration());
-  EXPECT_GT(neg_full_range, -absl::InfiniteDuration());
-
-  EXPECT_LT(neg_full_range, full_range);
-  EXPECT_EQ(neg_full_range, -full_range);
-}
-
-TEST(Duration, RelationalOperators) {
-#define TEST_REL_OPS(UNIT)               \
-  static_assert(UNIT(2) == UNIT(2), ""); \
-  static_assert(UNIT(1) != UNIT(2), ""); \
-  static_assert(UNIT(1) < UNIT(2), "");  \
-  static_assert(UNIT(3) > UNIT(2), "");  \
-  static_assert(UNIT(1) <= UNIT(2), ""); \
-  static_assert(UNIT(2) <= UNIT(2), ""); \
-  static_assert(UNIT(3) >= UNIT(2), ""); \
-  static_assert(UNIT(2) >= UNIT(2), "");
-
-  TEST_REL_OPS(absl::Nanoseconds);
-  TEST_REL_OPS(absl::Microseconds);
-  TEST_REL_OPS(absl::Milliseconds);
-  TEST_REL_OPS(absl::Seconds);
-  TEST_REL_OPS(absl::Minutes);
-  TEST_REL_OPS(absl::Hours);
-
-#undef TEST_REL_OPS
-}
-
-TEST(Duration, Addition) {
-#define TEST_ADD_OPS(UNIT)                  \
-  do {                                      \
-    EXPECT_EQ(UNIT(2), UNIT(1) + UNIT(1));  \
-    EXPECT_EQ(UNIT(1), UNIT(2) - UNIT(1));  \
-    EXPECT_EQ(UNIT(0), UNIT(2) - UNIT(2));  \
-    EXPECT_EQ(UNIT(-1), UNIT(1) - UNIT(2)); \
-    EXPECT_EQ(UNIT(-2), UNIT(0) - UNIT(2)); \
-    EXPECT_EQ(UNIT(-2), UNIT(1) - UNIT(3)); \
-    absl::Duration a = UNIT(1);             \
-    a += UNIT(1);                           \
-    EXPECT_EQ(UNIT(2), a);                  \
-    a -= UNIT(1);                           \
-    EXPECT_EQ(UNIT(1), a);                  \
-  } while (0)
-
-  TEST_ADD_OPS(absl::Nanoseconds);
-  TEST_ADD_OPS(absl::Microseconds);
-  TEST_ADD_OPS(absl::Milliseconds);
-  TEST_ADD_OPS(absl::Seconds);
-  TEST_ADD_OPS(absl::Minutes);
-  TEST_ADD_OPS(absl::Hours);
-
-#undef TEST_ADD_OPS
-
-  EXPECT_EQ(absl::Seconds(2), absl::Seconds(3) - 2 * absl::Milliseconds(500));
-  EXPECT_EQ(absl::Seconds(2) + absl::Milliseconds(500),
-            absl::Seconds(3) - absl::Milliseconds(500));
-
-  EXPECT_EQ(absl::Seconds(1) + absl::Milliseconds(998),
-            absl::Milliseconds(999) + absl::Milliseconds(999));
-
-  EXPECT_EQ(absl::Milliseconds(-1),
-            absl::Milliseconds(998) - absl::Milliseconds(999));
-
-  // Tests fractions of a nanoseconds. These are implementation details only.
-  EXPECT_GT(absl::Nanoseconds(1), absl::Nanoseconds(1) / 2);
-  EXPECT_EQ(absl::Nanoseconds(1),
-            absl::Nanoseconds(1) / 2 + absl::Nanoseconds(1) / 2);
-  EXPECT_GT(absl::Nanoseconds(1) / 4, absl::Nanoseconds(0));
-  EXPECT_EQ(absl::Nanoseconds(1) / 8, absl::Nanoseconds(0));
-
-  // Tests subtraction that will cause wrap around of the rep_lo_ bits.
-  absl::Duration d_7_5 = absl::Seconds(7) + absl::Milliseconds(500);
-  absl::Duration d_3_7 = absl::Seconds(3) + absl::Milliseconds(700);
-  absl::Duration ans_3_8 = absl::Seconds(3) + absl::Milliseconds(800);
-  EXPECT_EQ(ans_3_8, d_7_5 - d_3_7);
-
-  // Subtracting min_duration
-  absl::Duration min_dur = absl::Seconds(kint64min);
-  EXPECT_EQ(absl::Seconds(0), min_dur - min_dur);
-  EXPECT_EQ(absl::Seconds(kint64max), absl::Seconds(-1) - min_dur);
-}
-
-TEST(Duration, Negation) {
-  // By storing negations of various values in constexpr variables we
-  // verify that the initializers are constant expressions.
-  constexpr absl::Duration negated_zero_duration = -absl::ZeroDuration();
-  EXPECT_EQ(negated_zero_duration, absl::ZeroDuration());
-
-  constexpr absl::Duration negated_infinite_duration =
-      -absl::InfiniteDuration();
-  EXPECT_NE(negated_infinite_duration, absl::InfiniteDuration());
-  EXPECT_EQ(-negated_infinite_duration, absl::InfiniteDuration());
-
-  // The public APIs to check if a duration is infinite depend on using
-  // -InfiniteDuration(), but we're trying to test operator- here, so we
-  // need to use the lower-level internal query IsInfiniteDuration.
-  EXPECT_TRUE(
-      absl::time_internal::IsInfiniteDuration(negated_infinite_duration));
-
-  // The largest Duration is kint64max seconds and kTicksPerSecond - 1 ticks.
-  // Using the absl::time_internal::MakeDuration API is the cleanest way to
-  // construct that Duration.
-  constexpr absl::Duration max_duration = absl::time_internal::MakeDuration(
-      kint64max, absl::time_internal::kTicksPerSecond - 1);
-  constexpr absl::Duration negated_max_duration = -max_duration;
-  // The largest negatable value is one tick above the minimum representable;
-  // it's the negation of max_duration.
-  constexpr absl::Duration nearly_min_duration =
-      absl::time_internal::MakeDuration(kint64min, int64_t{1});
-  constexpr absl::Duration negated_nearly_min_duration = -nearly_min_duration;
-
-  EXPECT_EQ(negated_max_duration, nearly_min_duration);
-  EXPECT_EQ(negated_nearly_min_duration, max_duration);
-  EXPECT_EQ(-(-max_duration), max_duration);
-
-  constexpr absl::Duration min_duration =
-      absl::time_internal::MakeDuration(kint64min);
-  constexpr absl::Duration negated_min_duration = -min_duration;
-  EXPECT_EQ(negated_min_duration, absl::InfiniteDuration());
-}
-
-TEST(Duration, AbsoluteValue) {
-  EXPECT_EQ(absl::ZeroDuration(), AbsDuration(absl::ZeroDuration()));
-  EXPECT_EQ(absl::Seconds(1), AbsDuration(absl::Seconds(1)));
-  EXPECT_EQ(absl::Seconds(1), AbsDuration(absl::Seconds(-1)));
-
-  EXPECT_EQ(absl::InfiniteDuration(), AbsDuration(absl::InfiniteDuration()));
-  EXPECT_EQ(absl::InfiniteDuration(), AbsDuration(-absl::InfiniteDuration()));
-
-  absl::Duration max_dur =
-      absl::Seconds(kint64max) + (absl::Seconds(1) - absl::Nanoseconds(1) / 4);
-  EXPECT_EQ(max_dur, AbsDuration(max_dur));
-
-  absl::Duration min_dur = absl::Seconds(kint64min);
-  EXPECT_EQ(absl::InfiniteDuration(), AbsDuration(min_dur));
-  EXPECT_EQ(max_dur, AbsDuration(min_dur + absl::Nanoseconds(1) / 4));
-}
-
-TEST(Duration, Multiplication) {
-#define TEST_MUL_OPS(UNIT)                                    \
-  do {                                                        \
-    EXPECT_EQ(UNIT(5), UNIT(2) * 2.5);                        \
-    EXPECT_EQ(UNIT(2), UNIT(5) / 2.5);                        \
-    EXPECT_EQ(UNIT(-5), UNIT(-2) * 2.5);                      \
-    EXPECT_EQ(UNIT(-5), -UNIT(2) * 2.5);                      \
-    EXPECT_EQ(UNIT(-5), UNIT(2) * -2.5);                      \
-    EXPECT_EQ(UNIT(-2), UNIT(-5) / 2.5);                      \
-    EXPECT_EQ(UNIT(-2), -UNIT(5) / 2.5);                      \
-    EXPECT_EQ(UNIT(-2), UNIT(5) / -2.5);                      \
-    EXPECT_EQ(UNIT(2), UNIT(11) % UNIT(3));                   \
-    absl::Duration a = UNIT(2);                               \
-    a *= 2.5;                                                 \
-    EXPECT_EQ(UNIT(5), a);                                    \
-    a /= 2.5;                                                 \
-    EXPECT_EQ(UNIT(2), a);                                    \
-    a %= UNIT(1);                                             \
-    EXPECT_EQ(UNIT(0), a);                                    \
-    absl::Duration big = UNIT(1000000000);                    \
-    big *= 3;                                                 \
-    big /= 3;                                                 \
-    EXPECT_EQ(UNIT(1000000000), big);                         \
-    EXPECT_EQ(-UNIT(2), -UNIT(2));                            \
-    EXPECT_EQ(-UNIT(2), UNIT(2) * -1);                        \
-    EXPECT_EQ(-UNIT(2), -1 * UNIT(2));                        \
-    EXPECT_EQ(-UNIT(-2), UNIT(2));                            \
-    EXPECT_EQ(2, UNIT(2) / UNIT(1));                          \
-    absl::Duration rem;                                       \
-    EXPECT_EQ(2, absl::IDivDuration(UNIT(2), UNIT(1), &rem)); \
-    EXPECT_EQ(2.0, absl::FDivDuration(UNIT(2), UNIT(1)));     \
-  } while (0)
-
-  TEST_MUL_OPS(absl::Nanoseconds);
-  TEST_MUL_OPS(absl::Microseconds);
-  TEST_MUL_OPS(absl::Milliseconds);
-  TEST_MUL_OPS(absl::Seconds);
-  TEST_MUL_OPS(absl::Minutes);
-  TEST_MUL_OPS(absl::Hours);
-
-#undef TEST_MUL_OPS
-
-  // Ensures that multiplication and division by 1 with a maxed-out durations
-  // doesn't lose precision.
-  absl::Duration max_dur =
-      absl::Seconds(kint64max) + (absl::Seconds(1) - absl::Nanoseconds(1) / 4);
-  absl::Duration min_dur = absl::Seconds(kint64min);
-  EXPECT_EQ(max_dur, max_dur * 1);
-  EXPECT_EQ(max_dur, max_dur / 1);
-  EXPECT_EQ(min_dur, min_dur * 1);
-  EXPECT_EQ(min_dur, min_dur / 1);
-
-  // Tests division on a Duration with a large number of significant digits.
-  // Tests when the digits span hi and lo as well as only in hi.
-  absl::Duration sigfigs = absl::Seconds(2000000000) + absl::Nanoseconds(3);
-  EXPECT_EQ(absl::Seconds(666666666) + absl::Nanoseconds(666666667) +
-                absl::Nanoseconds(1) / 2,
-            sigfigs / 3);
-  sigfigs = absl::Seconds(int64_t{7000000000});
-  EXPECT_EQ(absl::Seconds(2333333333) + absl::Nanoseconds(333333333) +
-                absl::Nanoseconds(1) / 4,
-            sigfigs / 3);
-
-  EXPECT_EQ(absl::Seconds(7) + absl::Milliseconds(500), absl::Seconds(3) * 2.5);
-  EXPECT_EQ(absl::Seconds(8) * -1 + absl::Milliseconds(300),
-            (absl::Seconds(2) + absl::Milliseconds(200)) * -3.5);
-  EXPECT_EQ(-absl::Seconds(8) + absl::Milliseconds(300),
-            (absl::Seconds(2) + absl::Milliseconds(200)) * -3.5);
-  EXPECT_EQ(absl::Seconds(1) + absl::Milliseconds(875),
-            (absl::Seconds(7) + absl::Milliseconds(500)) / 4);
-  EXPECT_EQ(absl::Seconds(30),
-            (absl::Seconds(7) + absl::Milliseconds(500)) / 0.25);
-  EXPECT_EQ(absl::Seconds(3),
-            (absl::Seconds(7) + absl::Milliseconds(500)) / 2.5);
-
-  // Tests division remainder.
-  EXPECT_EQ(absl::Nanoseconds(0), absl::Nanoseconds(7) % absl::Nanoseconds(1));
-  EXPECT_EQ(absl::Nanoseconds(0), absl::Nanoseconds(0) % absl::Nanoseconds(10));
-  EXPECT_EQ(absl::Nanoseconds(2), absl::Nanoseconds(7) % absl::Nanoseconds(5));
-  EXPECT_EQ(absl::Nanoseconds(2), absl::Nanoseconds(2) % absl::Nanoseconds(5));
-
-  EXPECT_EQ(absl::Nanoseconds(1), absl::Nanoseconds(10) % absl::Nanoseconds(3));
-  EXPECT_EQ(absl::Nanoseconds(1),
-            absl::Nanoseconds(10) % absl::Nanoseconds(-3));
-  EXPECT_EQ(absl::Nanoseconds(-1),
-            absl::Nanoseconds(-10) % absl::Nanoseconds(3));
-  EXPECT_EQ(absl::Nanoseconds(-1),
-            absl::Nanoseconds(-10) % absl::Nanoseconds(-3));
-
-  EXPECT_EQ(absl::Milliseconds(100),
-            absl::Seconds(1) % absl::Milliseconds(300));
-  EXPECT_EQ(
-      absl::Milliseconds(300),
-      (absl::Seconds(3) + absl::Milliseconds(800)) % absl::Milliseconds(500));
-
-  EXPECT_EQ(absl::Nanoseconds(1), absl::Nanoseconds(1) % absl::Seconds(1));
-  EXPECT_EQ(absl::Nanoseconds(-1), absl::Nanoseconds(-1) % absl::Seconds(1));
-  EXPECT_EQ(0, absl::Nanoseconds(-1) / absl::Seconds(1));  // Actual -1e-9
-
-  // Tests identity a = (a/b)*b + a%b
-#define TEST_MOD_IDENTITY(a, b) \
-  EXPECT_EQ((a), ((a) / (b))*(b) + ((a)%(b)))
-
-  TEST_MOD_IDENTITY(absl::Seconds(0), absl::Seconds(2));
-  TEST_MOD_IDENTITY(absl::Seconds(1), absl::Seconds(1));
-  TEST_MOD_IDENTITY(absl::Seconds(1), absl::Seconds(2));
-  TEST_MOD_IDENTITY(absl::Seconds(2), absl::Seconds(1));
-
-  TEST_MOD_IDENTITY(absl::Seconds(-2), absl::Seconds(1));
-  TEST_MOD_IDENTITY(absl::Seconds(2), absl::Seconds(-1));
-  TEST_MOD_IDENTITY(absl::Seconds(-2), absl::Seconds(-1));
-
-  TEST_MOD_IDENTITY(absl::Nanoseconds(0), absl::Nanoseconds(2));
-  TEST_MOD_IDENTITY(absl::Nanoseconds(1), absl::Nanoseconds(1));
-  TEST_MOD_IDENTITY(absl::Nanoseconds(1), absl::Nanoseconds(2));
-  TEST_MOD_IDENTITY(absl::Nanoseconds(2), absl::Nanoseconds(1));
-
-  TEST_MOD_IDENTITY(absl::Nanoseconds(-2), absl::Nanoseconds(1));
-  TEST_MOD_IDENTITY(absl::Nanoseconds(2), absl::Nanoseconds(-1));
-  TEST_MOD_IDENTITY(absl::Nanoseconds(-2), absl::Nanoseconds(-1));
-
-  // Mixed seconds + subseconds
-  absl::Duration mixed_a = absl::Seconds(1) + absl::Nanoseconds(2);
-  absl::Duration mixed_b = absl::Seconds(1) + absl::Nanoseconds(3);
-
-  TEST_MOD_IDENTITY(absl::Seconds(0), mixed_a);
-  TEST_MOD_IDENTITY(mixed_a, mixed_a);
-  TEST_MOD_IDENTITY(mixed_a, mixed_b);
-  TEST_MOD_IDENTITY(mixed_b, mixed_a);
-
-  TEST_MOD_IDENTITY(-mixed_a, mixed_b);
-  TEST_MOD_IDENTITY(mixed_a, -mixed_b);
-  TEST_MOD_IDENTITY(-mixed_a, -mixed_b);
-
-#undef TEST_MOD_IDENTITY
-}
-
-TEST(Duration, Truncation) {
-  const absl::Duration d = absl::Nanoseconds(1234567890);
-  const absl::Duration inf = absl::InfiniteDuration();
-  for (int unit_sign : {1, -1}) {  // sign shouldn't matter
-    EXPECT_EQ(absl::Nanoseconds(1234567890),
-              Trunc(d, unit_sign * absl::Nanoseconds(1)));
-    EXPECT_EQ(absl::Microseconds(1234567),
-              Trunc(d, unit_sign * absl::Microseconds(1)));
-    EXPECT_EQ(absl::Milliseconds(1234),
-              Trunc(d, unit_sign * absl::Milliseconds(1)));
-    EXPECT_EQ(absl::Seconds(1), Trunc(d, unit_sign * absl::Seconds(1)));
-    EXPECT_EQ(inf, Trunc(inf, unit_sign * absl::Seconds(1)));
-
-    EXPECT_EQ(absl::Nanoseconds(-1234567890),
-              Trunc(-d, unit_sign * absl::Nanoseconds(1)));
-    EXPECT_EQ(absl::Microseconds(-1234567),
-              Trunc(-d, unit_sign * absl::Microseconds(1)));
-    EXPECT_EQ(absl::Milliseconds(-1234),
-              Trunc(-d, unit_sign * absl::Milliseconds(1)));
-    EXPECT_EQ(absl::Seconds(-1), Trunc(-d, unit_sign * absl::Seconds(1)));
-    EXPECT_EQ(-inf, Trunc(-inf, unit_sign * absl::Seconds(1)));
-  }
-}
-
-TEST(Duration, Flooring) {
-  const absl::Duration d = absl::Nanoseconds(1234567890);
-  const absl::Duration inf = absl::InfiniteDuration();
-  for (int unit_sign : {1, -1}) {  // sign shouldn't matter
-    EXPECT_EQ(absl::Nanoseconds(1234567890),
-              absl::Floor(d, unit_sign * absl::Nanoseconds(1)));
-    EXPECT_EQ(absl::Microseconds(1234567),
-              absl::Floor(d, unit_sign * absl::Microseconds(1)));
-    EXPECT_EQ(absl::Milliseconds(1234),
-              absl::Floor(d, unit_sign * absl::Milliseconds(1)));
-    EXPECT_EQ(absl::Seconds(1), absl::Floor(d, unit_sign * absl::Seconds(1)));
-    EXPECT_EQ(inf, absl::Floor(inf, unit_sign * absl::Seconds(1)));
-
-    EXPECT_EQ(absl::Nanoseconds(-1234567890),
-              absl::Floor(-d, unit_sign * absl::Nanoseconds(1)));
-    EXPECT_EQ(absl::Microseconds(-1234568),
-              absl::Floor(-d, unit_sign * absl::Microseconds(1)));
-    EXPECT_EQ(absl::Milliseconds(-1235),
-              absl::Floor(-d, unit_sign * absl::Milliseconds(1)));
-    EXPECT_EQ(absl::Seconds(-2), absl::Floor(-d, unit_sign * absl::Seconds(1)));
-    EXPECT_EQ(-inf, absl::Floor(-inf, unit_sign * absl::Seconds(1)));
-  }
-}
-
-TEST(Duration, Ceiling) {
-  const absl::Duration d = absl::Nanoseconds(1234567890);
-  const absl::Duration inf = absl::InfiniteDuration();
-  for (int unit_sign : {1, -1}) {  // // sign shouldn't matter
-    EXPECT_EQ(absl::Nanoseconds(1234567890),
-              absl::Ceil(d, unit_sign * absl::Nanoseconds(1)));
-    EXPECT_EQ(absl::Microseconds(1234568),
-              absl::Ceil(d, unit_sign * absl::Microseconds(1)));
-    EXPECT_EQ(absl::Milliseconds(1235),
-              absl::Ceil(d, unit_sign * absl::Milliseconds(1)));
-    EXPECT_EQ(absl::Seconds(2), absl::Ceil(d, unit_sign * absl::Seconds(1)));
-    EXPECT_EQ(inf, absl::Ceil(inf, unit_sign * absl::Seconds(1)));
-
-    EXPECT_EQ(absl::Nanoseconds(-1234567890),
-              absl::Ceil(-d, unit_sign * absl::Nanoseconds(1)));
-    EXPECT_EQ(absl::Microseconds(-1234567),
-              absl::Ceil(-d, unit_sign * absl::Microseconds(1)));
-    EXPECT_EQ(absl::Milliseconds(-1234),
-              absl::Ceil(-d, unit_sign * absl::Milliseconds(1)));
-    EXPECT_EQ(absl::Seconds(-1), absl::Ceil(-d, unit_sign * absl::Seconds(1)));
-    EXPECT_EQ(-inf, absl::Ceil(-inf, unit_sign * absl::Seconds(1)));
-  }
-}
-
-TEST(Duration, RoundTripUnits) {
-  const int kRange = 100000;
-
-#define ROUND_TRIP_UNIT(U, LOW, HIGH)          \
-  do {                                         \
-    for (int64_t i = LOW; i < HIGH; ++i) {     \
-      absl::Duration d = absl::U(i);           \
-      if (d == absl::InfiniteDuration())       \
-        EXPECT_EQ(kint64max, d / absl::U(1));  \
-      else if (d == -absl::InfiniteDuration()) \
-        EXPECT_EQ(kint64min, d / absl::U(1));  \
-      else                                     \
-        EXPECT_EQ(i, absl::U(i) / absl::U(1)); \
-    }                                          \
-  } while (0)
-
-  ROUND_TRIP_UNIT(Nanoseconds, kint64min, kint64min + kRange);
-  ROUND_TRIP_UNIT(Nanoseconds, -kRange, kRange);
-  ROUND_TRIP_UNIT(Nanoseconds, kint64max - kRange, kint64max);
-
-  ROUND_TRIP_UNIT(Microseconds, kint64min, kint64min + kRange);
-  ROUND_TRIP_UNIT(Microseconds, -kRange, kRange);
-  ROUND_TRIP_UNIT(Microseconds, kint64max - kRange, kint64max);
-
-  ROUND_TRIP_UNIT(Milliseconds, kint64min, kint64min + kRange);
-  ROUND_TRIP_UNIT(Milliseconds, -kRange, kRange);
-  ROUND_TRIP_UNIT(Milliseconds, kint64max - kRange, kint64max);
-
-  ROUND_TRIP_UNIT(Seconds, kint64min, kint64min + kRange);
-  ROUND_TRIP_UNIT(Seconds, -kRange, kRange);
-  ROUND_TRIP_UNIT(Seconds, kint64max - kRange, kint64max);
-
-  ROUND_TRIP_UNIT(Minutes, kint64min / 60, kint64min / 60 + kRange);
-  ROUND_TRIP_UNIT(Minutes, -kRange, kRange);
-  ROUND_TRIP_UNIT(Minutes, kint64max / 60 - kRange, kint64max / 60);
-
-  ROUND_TRIP_UNIT(Hours, kint64min / 3600, kint64min / 3600 + kRange);
-  ROUND_TRIP_UNIT(Hours, -kRange, kRange);
-  ROUND_TRIP_UNIT(Hours, kint64max / 3600 - kRange, kint64max / 3600);
-
-#undef ROUND_TRIP_UNIT
-}
-
-TEST(Duration, TruncConversions) {
-  // Tests ToTimespec()/DurationFromTimespec()
-  const struct {
-    absl::Duration d;
-    timespec ts;
-  } to_ts[] = {
-      {absl::Seconds(1) + absl::Nanoseconds(1), {1, 1}},
-      {absl::Seconds(1) + absl::Nanoseconds(1) / 2, {1, 0}},
-      {absl::Seconds(1) + absl::Nanoseconds(0), {1, 0}},
-      {absl::Seconds(0) + absl::Nanoseconds(0), {0, 0}},
-      {absl::Seconds(0) - absl::Nanoseconds(1) / 2, {0, 0}},
-      {absl::Seconds(0) - absl::Nanoseconds(1), {-1, 999999999}},
-      {absl::Seconds(-1) + absl::Nanoseconds(1), {-1, 1}},
-      {absl::Seconds(-1) + absl::Nanoseconds(1) / 2, {-1, 1}},
-      {absl::Seconds(-1) + absl::Nanoseconds(0), {-1, 0}},
-      {absl::Seconds(-1) - absl::Nanoseconds(1) / 2, {-1, 0}},
-  };
-  for (const auto& test : to_ts) {
-    EXPECT_THAT(absl::ToTimespec(test.d), TimespecMatcher(test.ts));
-  }
-  const struct {
-    timespec ts;
-    absl::Duration d;
-  } from_ts[] = {
-      {{1, 1}, absl::Seconds(1) + absl::Nanoseconds(1)},
-      {{1, 0}, absl::Seconds(1) + absl::Nanoseconds(0)},
-      {{0, 0}, absl::Seconds(0) + absl::Nanoseconds(0)},
-      {{0, -1}, absl::Seconds(0) - absl::Nanoseconds(1)},
-      {{-1, 999999999}, absl::Seconds(0) - absl::Nanoseconds(1)},
-      {{-1, 1}, absl::Seconds(-1) + absl::Nanoseconds(1)},
-      {{-1, 0}, absl::Seconds(-1) + absl::Nanoseconds(0)},
-      {{-1, -1}, absl::Seconds(-1) - absl::Nanoseconds(1)},
-      {{-2, 999999999}, absl::Seconds(-1) - absl::Nanoseconds(1)},
-  };
-  for (const auto& test : from_ts) {
-    EXPECT_EQ(test.d, absl::DurationFromTimespec(test.ts));
-  }
-
-  // Tests ToTimeval()/DurationFromTimeval() (same as timespec above)
-  const struct {
-    absl::Duration d;
-    timeval tv;
-  } to_tv[] = {
-      {absl::Seconds(1) + absl::Microseconds(1), {1, 1}},
-      {absl::Seconds(1) + absl::Microseconds(1) / 2, {1, 0}},
-      {absl::Seconds(1) + absl::Microseconds(0), {1, 0}},
-      {absl::Seconds(0) + absl::Microseconds(0), {0, 0}},
-      {absl::Seconds(0) - absl::Microseconds(1) / 2, {0, 0}},
-      {absl::Seconds(0) - absl::Microseconds(1), {-1, 999999}},
-      {absl::Seconds(-1) + absl::Microseconds(1), {-1, 1}},
-      {absl::Seconds(-1) + absl::Microseconds(1) / 2, {-1, 1}},
-      {absl::Seconds(-1) + absl::Microseconds(0), {-1, 0}},
-      {absl::Seconds(-1) - absl::Microseconds(1) / 2, {-1, 0}},
-  };
-  for (const auto& test : to_tv) {
-    EXPECT_THAT(absl::ToTimeval(test.d), TimevalMatcher(test.tv));
-  }
-  const struct {
-    timeval tv;
-    absl::Duration d;
-  } from_tv[] = {
-      {{1, 1}, absl::Seconds(1) + absl::Microseconds(1)},
-      {{1, 0}, absl::Seconds(1) + absl::Microseconds(0)},
-      {{0, 0}, absl::Seconds(0) + absl::Microseconds(0)},
-      {{0, -1}, absl::Seconds(0) - absl::Microseconds(1)},
-      {{-1, 999999}, absl::Seconds(0) - absl::Microseconds(1)},
-      {{-1, 1}, absl::Seconds(-1) + absl::Microseconds(1)},
-      {{-1, 0}, absl::Seconds(-1) + absl::Microseconds(0)},
-      {{-1, -1}, absl::Seconds(-1) - absl::Microseconds(1)},
-      {{-2, 999999}, absl::Seconds(-1) - absl::Microseconds(1)},
-  };
-  for (const auto& test : from_tv) {
-    EXPECT_EQ(test.d, absl::DurationFromTimeval(test.tv));
-  }
-}
-
-TEST(Duration, SmallConversions) {
-  // Special tests for conversions of small durations.
-
-  EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0));
-  // TODO(bww): Is the next one OK?
-  EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0.124999999e-9));
-  EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.125e-9));
-  EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.250e-9));
-  EXPECT_EQ(absl::Nanoseconds(1) / 2, absl::Seconds(0.375e-9));
-  EXPECT_EQ(absl::Nanoseconds(1) / 2, absl::Seconds(0.500e-9));
-  EXPECT_EQ(absl::Nanoseconds(3) / 4, absl::Seconds(0.625e-9));
-  EXPECT_EQ(absl::Nanoseconds(3) / 4, absl::Seconds(0.750e-9));
-  EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(0.875e-9));
-  EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(1.000e-9));
-
-  EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(-0.124999999e-9));
-  EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.125e-9));
-  EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.250e-9));
-  EXPECT_EQ(-absl::Nanoseconds(1) / 2, absl::Seconds(-0.375e-9));
-  EXPECT_EQ(-absl::Nanoseconds(1) / 2, absl::Seconds(-0.500e-9));
-  EXPECT_EQ(-absl::Nanoseconds(3) / 4, absl::Seconds(-0.625e-9));
-  EXPECT_EQ(-absl::Nanoseconds(3) / 4, absl::Seconds(-0.750e-9));
-  EXPECT_EQ(-absl::Nanoseconds(1), absl::Seconds(-0.875e-9));
-  EXPECT_EQ(-absl::Nanoseconds(1), absl::Seconds(-1.000e-9));
-
-  timespec ts;
-  ts.tv_sec = 0;
-  ts.tv_nsec = 0;
-  EXPECT_THAT(ToTimespec(absl::Nanoseconds(0)), TimespecMatcher(ts));
-  // TODO(bww): Are the next three OK?
-  EXPECT_THAT(ToTimespec(absl::Nanoseconds(1) / 4), TimespecMatcher(ts));
-  EXPECT_THAT(ToTimespec(absl::Nanoseconds(2) / 4), TimespecMatcher(ts));
-  EXPECT_THAT(ToTimespec(absl::Nanoseconds(3) / 4), TimespecMatcher(ts));
-  ts.tv_nsec = 1;
-  EXPECT_THAT(ToTimespec(absl::Nanoseconds(4) / 4), TimespecMatcher(ts));
-  EXPECT_THAT(ToTimespec(absl::Nanoseconds(5) / 4), TimespecMatcher(ts));
-  EXPECT_THAT(ToTimespec(absl::Nanoseconds(6) / 4), TimespecMatcher(ts));
-  EXPECT_THAT(ToTimespec(absl::Nanoseconds(7) / 4), TimespecMatcher(ts));
-  ts.tv_nsec = 2;
-  EXPECT_THAT(ToTimespec(absl::Nanoseconds(8) / 4), TimespecMatcher(ts));
-
-  timeval tv;
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
-  EXPECT_THAT(ToTimeval(absl::Nanoseconds(0)), TimevalMatcher(tv));
-  // TODO(bww): Is the next one OK?
-  EXPECT_THAT(ToTimeval(absl::Nanoseconds(999)), TimevalMatcher(tv));
-  tv.tv_usec = 1;
-  EXPECT_THAT(ToTimeval(absl::Nanoseconds(1000)), TimevalMatcher(tv));
-  EXPECT_THAT(ToTimeval(absl::Nanoseconds(1999)), TimevalMatcher(tv));
-  tv.tv_usec = 2;
-  EXPECT_THAT(ToTimeval(absl::Nanoseconds(2000)), TimevalMatcher(tv));
-}
-
-void VerifySameAsMul(double time_as_seconds, int* const misses) {
-  auto direct_seconds = absl::Seconds(time_as_seconds);
-  auto mul_by_one_second = time_as_seconds * absl::Seconds(1);
-  if (direct_seconds != mul_by_one_second) {
-    if (*misses > 10) return;
-    ASSERT_LE(++(*misses), 10) << "Too many errors, not reporting more.";
-    EXPECT_EQ(direct_seconds, mul_by_one_second)
-        << "given double time_as_seconds = " << std::setprecision(17)
-        << time_as_seconds;
-  }
-}
-
-// For a variety of interesting durations, we find the exact point
-// where one double converts to that duration, and the very next double
-// converts to the next duration.  For both of those points, verify that
-// Seconds(point) returns the same duration as point * Seconds(1.0)
-TEST(Duration, ToDoubleSecondsCheckEdgeCases) {
-  constexpr uint32_t kTicksPerSecond = absl::time_internal::kTicksPerSecond;
-  constexpr auto duration_tick = absl::time_internal::MakeDuration(0, 1u);
-  int misses = 0;
-  for (int64_t seconds = 0; seconds < 99; ++seconds) {
-    uint32_t tick_vals[] = {0, +999, +999999, +999999999, kTicksPerSecond - 1,
-                            0, 1000, 1000000, 1000000000, kTicksPerSecond,
-                            1, 1001, 1000001, 1000000001, kTicksPerSecond + 1,
-                            2, 1002, 1000002, 1000000002, kTicksPerSecond + 2,
-                            3, 1003, 1000003, 1000000003, kTicksPerSecond + 3,
-                            4, 1004, 1000004, 1000000004, kTicksPerSecond + 4,
-                            5, 6,    7,       8,          9};
-    for (uint32_t ticks : tick_vals) {
-      absl::Duration s_plus_t = absl::Seconds(seconds) + ticks * duration_tick;
-      for (absl::Duration d : {s_plus_t, -s_plus_t}) {
-        absl::Duration after_d = d + duration_tick;
-        EXPECT_NE(d, after_d);
-        EXPECT_EQ(after_d - d, duration_tick);
-
-        double low_edge = ToDoubleSeconds(d);
-        EXPECT_EQ(d, absl::Seconds(low_edge));
-
-        double high_edge = ToDoubleSeconds(after_d);
-        EXPECT_EQ(after_d, absl::Seconds(high_edge));
-
-        for (;;) {
-          double midpoint = low_edge + (high_edge - low_edge) / 2;
-          if (midpoint == low_edge || midpoint == high_edge) break;
-          absl::Duration mid_duration = absl::Seconds(midpoint);
-          if (mid_duration == d) {
-            low_edge = midpoint;
-          } else {
-            EXPECT_EQ(mid_duration, after_d);
-            high_edge = midpoint;
-          }
-        }
-        // Now low_edge is the highest double that converts to Duration d,
-        // and high_edge is the lowest double that converts to Duration after_d.
-        VerifySameAsMul(low_edge, &misses);
-        VerifySameAsMul(high_edge, &misses);
-      }
-    }
-  }
-}
-
-TEST(Duration, ToDoubleSecondsCheckRandom) {
-  std::random_device rd;
-  std::seed_seq seed({rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()});
-  std::mt19937_64 gen(seed);
-  // We want doubles distributed from 1/8ns up to 2^63, where
-  // as many values are tested from 1ns to 2ns as from 1sec to 2sec,
-  // so even distribute along a log-scale of those values, and
-  // exponentiate before using them.  (9.223377e+18 is just slightly
-  // out of bounds for absl::Duration.)
-  std::uniform_real_distribution<double> uniform(std::log(0.125e-9),
-                                                 std::log(9.223377e+18));
-  int misses = 0;
-  for (int i = 0; i < 1000000; ++i) {
-    double d = std::exp(uniform(gen));
-    VerifySameAsMul(d, &misses);
-    VerifySameAsMul(-d, &misses);
-  }
-}
-
-TEST(Duration, ConversionSaturation) {
-  absl::Duration d;
-
-  const auto max_timeval_sec =
-      std::numeric_limits<decltype(timeval::tv_sec)>::max();
-  const auto min_timeval_sec =
-      std::numeric_limits<decltype(timeval::tv_sec)>::min();
-  timeval tv;
-  tv.tv_sec = max_timeval_sec;
-  tv.tv_usec = 999998;
-  d = absl::DurationFromTimeval(tv);
-  tv = ToTimeval(d);
-  EXPECT_EQ(max_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(999998, tv.tv_usec);
-  d += absl::Microseconds(1);
-  tv = ToTimeval(d);
-  EXPECT_EQ(max_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(999999, tv.tv_usec);
-  d += absl::Microseconds(1);  // no effect
-  tv = ToTimeval(d);
-  EXPECT_EQ(max_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(999999, tv.tv_usec);
-
-  tv.tv_sec = min_timeval_sec;
-  tv.tv_usec = 1;
-  d = absl::DurationFromTimeval(tv);
-  tv = ToTimeval(d);
-  EXPECT_EQ(min_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(1, tv.tv_usec);
-  d -= absl::Microseconds(1);
-  tv = ToTimeval(d);
-  EXPECT_EQ(min_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(0, tv.tv_usec);
-  d -= absl::Microseconds(1);  // no effect
-  tv = ToTimeval(d);
-  EXPECT_EQ(min_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(0, tv.tv_usec);
-
-  const auto max_timespec_sec =
-      std::numeric_limits<decltype(timespec::tv_sec)>::max();
-  const auto min_timespec_sec =
-      std::numeric_limits<decltype(timespec::tv_sec)>::min();
-  timespec ts;
-  ts.tv_sec = max_timespec_sec;
-  ts.tv_nsec = 999999998;
-  d = absl::DurationFromTimespec(ts);
-  ts = absl::ToTimespec(d);
-  EXPECT_EQ(max_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(999999998, ts.tv_nsec);
-  d += absl::Nanoseconds(1);
-  ts = absl::ToTimespec(d);
-  EXPECT_EQ(max_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(999999999, ts.tv_nsec);
-  d += absl::Nanoseconds(1);  // no effect
-  ts = absl::ToTimespec(d);
-  EXPECT_EQ(max_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(999999999, ts.tv_nsec);
-
-  ts.tv_sec = min_timespec_sec;
-  ts.tv_nsec = 1;
-  d = absl::DurationFromTimespec(ts);
-  ts = absl::ToTimespec(d);
-  EXPECT_EQ(min_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(1, ts.tv_nsec);
-  d -= absl::Nanoseconds(1);
-  ts = absl::ToTimespec(d);
-  EXPECT_EQ(min_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(0, ts.tv_nsec);
-  d -= absl::Nanoseconds(1);  // no effect
-  ts = absl::ToTimespec(d);
-  EXPECT_EQ(min_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(0, ts.tv_nsec);
-}
-
-TEST(Duration, FormatDuration) {
-  // Example from Go's docs.
-  EXPECT_EQ("72h3m0.5s",
-            absl::FormatDuration(absl::Hours(72) + absl::Minutes(3) +
-                                 absl::Milliseconds(500)));
-  // Go's largest time: 2540400h10m10.000000000s
-  EXPECT_EQ("2540400h10m10s",
-            absl::FormatDuration(absl::Hours(2540400) + absl::Minutes(10) +
-                                 absl::Seconds(10)));
-
-  EXPECT_EQ("0", absl::FormatDuration(absl::ZeroDuration()));
-  EXPECT_EQ("0", absl::FormatDuration(absl::Seconds(0)));
-  EXPECT_EQ("0", absl::FormatDuration(absl::Nanoseconds(0)));
-
-  EXPECT_EQ("1ns", absl::FormatDuration(absl::Nanoseconds(1)));
-  EXPECT_EQ("1us", absl::FormatDuration(absl::Microseconds(1)));
-  EXPECT_EQ("1ms", absl::FormatDuration(absl::Milliseconds(1)));
-  EXPECT_EQ("1s", absl::FormatDuration(absl::Seconds(1)));
-  EXPECT_EQ("1m", absl::FormatDuration(absl::Minutes(1)));
-  EXPECT_EQ("1h", absl::FormatDuration(absl::Hours(1)));
-
-  EXPECT_EQ("1h1m", absl::FormatDuration(absl::Hours(1) + absl::Minutes(1)));
-  EXPECT_EQ("1h1s", absl::FormatDuration(absl::Hours(1) + absl::Seconds(1)));
-  EXPECT_EQ("1m1s", absl::FormatDuration(absl::Minutes(1) + absl::Seconds(1)));
-
-  EXPECT_EQ("1h0.25s",
-            absl::FormatDuration(absl::Hours(1) + absl::Milliseconds(250)));
-  EXPECT_EQ("1m0.25s",
-            absl::FormatDuration(absl::Minutes(1) + absl::Milliseconds(250)));
-  EXPECT_EQ("1h1m0.25s",
-            absl::FormatDuration(absl::Hours(1) + absl::Minutes(1) +
-                                 absl::Milliseconds(250)));
-  EXPECT_EQ("1h0.0005s",
-            absl::FormatDuration(absl::Hours(1) + absl::Microseconds(500)));
-  EXPECT_EQ("1h0.0000005s",
-            absl::FormatDuration(absl::Hours(1) + absl::Nanoseconds(500)));
-
-  // Subsecond special case.
-  EXPECT_EQ("1.5ns", absl::FormatDuration(absl::Nanoseconds(1) +
-                                          absl::Nanoseconds(1) / 2));
-  EXPECT_EQ("1.25ns", absl::FormatDuration(absl::Nanoseconds(1) +
-                                           absl::Nanoseconds(1) / 4));
-  EXPECT_EQ("1ns", absl::FormatDuration(absl::Nanoseconds(1) +
-                                        absl::Nanoseconds(1) / 9));
-  EXPECT_EQ("1.2us", absl::FormatDuration(absl::Microseconds(1) +
-                                          absl::Nanoseconds(200)));
-  EXPECT_EQ("1.2ms", absl::FormatDuration(absl::Milliseconds(1) +
-                                          absl::Microseconds(200)));
-  EXPECT_EQ("1.0002ms", absl::FormatDuration(absl::Milliseconds(1) +
-                                             absl::Nanoseconds(200)));
-  EXPECT_EQ("1.00001ms", absl::FormatDuration(absl::Milliseconds(1) +
-                                              absl::Nanoseconds(10)));
-  EXPECT_EQ("1.000001ms",
-            absl::FormatDuration(absl::Milliseconds(1) + absl::Nanoseconds(1)));
-
-  // Negative durations.
-  EXPECT_EQ("-1ns", absl::FormatDuration(absl::Nanoseconds(-1)));
-  EXPECT_EQ("-1us", absl::FormatDuration(absl::Microseconds(-1)));
-  EXPECT_EQ("-1ms", absl::FormatDuration(absl::Milliseconds(-1)));
-  EXPECT_EQ("-1s", absl::FormatDuration(absl::Seconds(-1)));
-  EXPECT_EQ("-1m", absl::FormatDuration(absl::Minutes(-1)));
-  EXPECT_EQ("-1h", absl::FormatDuration(absl::Hours(-1)));
-
-  EXPECT_EQ("-1h1m",
-            absl::FormatDuration(-(absl::Hours(1) + absl::Minutes(1))));
-  EXPECT_EQ("-1h1s",
-            absl::FormatDuration(-(absl::Hours(1) + absl::Seconds(1))));
-  EXPECT_EQ("-1m1s",
-            absl::FormatDuration(-(absl::Minutes(1) + absl::Seconds(1))));
-
-  EXPECT_EQ("-1ns", absl::FormatDuration(absl::Nanoseconds(-1)));
-  EXPECT_EQ("-1.2us", absl::FormatDuration(
-                          -(absl::Microseconds(1) + absl::Nanoseconds(200))));
-  EXPECT_EQ("-1.2ms", absl::FormatDuration(
-                          -(absl::Milliseconds(1) + absl::Microseconds(200))));
-  EXPECT_EQ("-1.0002ms", absl::FormatDuration(-(absl::Milliseconds(1) +
-                                                absl::Nanoseconds(200))));
-  EXPECT_EQ("-1.00001ms", absl::FormatDuration(-(absl::Milliseconds(1) +
-                                                 absl::Nanoseconds(10))));
-  EXPECT_EQ("-1.000001ms", absl::FormatDuration(-(absl::Milliseconds(1) +
-                                                  absl::Nanoseconds(1))));
-
-  //
-  // Interesting corner cases.
-  //
-
-  const absl::Duration qns = absl::Nanoseconds(1) / 4;
-  const absl::Duration max_dur =
-      absl::Seconds(kint64max) + (absl::Seconds(1) - qns);
-  const absl::Duration min_dur = absl::Seconds(kint64min);
-
-  EXPECT_EQ("0.25ns", absl::FormatDuration(qns));
-  EXPECT_EQ("-0.25ns", absl::FormatDuration(-qns));
-  EXPECT_EQ("2562047788015215h30m7.99999999975s",
-            absl::FormatDuration(max_dur));
-  EXPECT_EQ("-2562047788015215h30m8s", absl::FormatDuration(min_dur));
-
-  // Tests printing full precision from units that print using FDivDuration
-  EXPECT_EQ("55.00000000025s", absl::FormatDuration(absl::Seconds(55) + qns));
-  EXPECT_EQ("55.00000025ms",
-            absl::FormatDuration(absl::Milliseconds(55) + qns));
-  EXPECT_EQ("55.00025us", absl::FormatDuration(absl::Microseconds(55) + qns));
-  EXPECT_EQ("55.25ns", absl::FormatDuration(absl::Nanoseconds(55) + qns));
-
-  // Formatting infinity
-  EXPECT_EQ("inf", absl::FormatDuration(absl::InfiniteDuration()));
-  EXPECT_EQ("-inf", absl::FormatDuration(-absl::InfiniteDuration()));
-
-  // Formatting approximately +/- 100 billion years
-  const absl::Duration huge_range = ApproxYears(100000000000);
-  EXPECT_EQ("876000000000000h", absl::FormatDuration(huge_range));
-  EXPECT_EQ("-876000000000000h", absl::FormatDuration(-huge_range));
-
-  EXPECT_EQ("876000000000000h0.999999999s",
-            absl::FormatDuration(huge_range +
-                                 (absl::Seconds(1) - absl::Nanoseconds(1))));
-  EXPECT_EQ("876000000000000h0.9999999995s",
-            absl::FormatDuration(
-                huge_range + (absl::Seconds(1) - absl::Nanoseconds(1) / 2)));
-  EXPECT_EQ("876000000000000h0.99999999975s",
-            absl::FormatDuration(
-                huge_range + (absl::Seconds(1) - absl::Nanoseconds(1) / 4)));
-
-  EXPECT_EQ("-876000000000000h0.999999999s",
-            absl::FormatDuration(-huge_range -
-                                 (absl::Seconds(1) - absl::Nanoseconds(1))));
-  EXPECT_EQ("-876000000000000h0.9999999995s",
-            absl::FormatDuration(
-                -huge_range - (absl::Seconds(1) - absl::Nanoseconds(1) / 2)));
-  EXPECT_EQ("-876000000000000h0.99999999975s",
-            absl::FormatDuration(
-                -huge_range - (absl::Seconds(1) - absl::Nanoseconds(1) / 4)));
-}
-
-TEST(Duration, ParseDuration) {
-  absl::Duration d;
-
-  // No specified unit. Should only work for zero and infinity.
-  EXPECT_TRUE(absl::ParseDuration("0", &d));
-  EXPECT_EQ(absl::ZeroDuration(), d);
-  EXPECT_TRUE(absl::ParseDuration("+0", &d));
-  EXPECT_EQ(absl::ZeroDuration(), d);
-  EXPECT_TRUE(absl::ParseDuration("-0", &d));
-  EXPECT_EQ(absl::ZeroDuration(), d);
-
-  EXPECT_TRUE(absl::ParseDuration("inf", &d));
-  EXPECT_EQ(absl::InfiniteDuration(), d);
-  EXPECT_TRUE(absl::ParseDuration("+inf", &d));
-  EXPECT_EQ(absl::InfiniteDuration(), d);
-  EXPECT_TRUE(absl::ParseDuration("-inf", &d));
-  EXPECT_EQ(-absl::InfiniteDuration(), d);
-  EXPECT_FALSE(absl::ParseDuration("infBlah", &d));
-
-  // Illegal input forms.
-  EXPECT_FALSE(absl::ParseDuration("", &d));
-  EXPECT_FALSE(absl::ParseDuration("0.0", &d));
-  EXPECT_FALSE(absl::ParseDuration(".0", &d));
-  EXPECT_FALSE(absl::ParseDuration(".", &d));
-  EXPECT_FALSE(absl::ParseDuration("01", &d));
-  EXPECT_FALSE(absl::ParseDuration("1", &d));
-  EXPECT_FALSE(absl::ParseDuration("-1", &d));
-  EXPECT_FALSE(absl::ParseDuration("2", &d));
-  EXPECT_FALSE(absl::ParseDuration("2 s", &d));
-  EXPECT_FALSE(absl::ParseDuration(".s", &d));
-  EXPECT_FALSE(absl::ParseDuration("-.s", &d));
-  EXPECT_FALSE(absl::ParseDuration("s", &d));
-  EXPECT_FALSE(absl::ParseDuration(" 2s", &d));
-  EXPECT_FALSE(absl::ParseDuration("2s ", &d));
-  EXPECT_FALSE(absl::ParseDuration(" 2s ", &d));
-  EXPECT_FALSE(absl::ParseDuration("2mt", &d));
-  EXPECT_FALSE(absl::ParseDuration("1e3s", &d));
-
-  // One unit type.
-  EXPECT_TRUE(absl::ParseDuration("1ns", &d));
-  EXPECT_EQ(absl::Nanoseconds(1), d);
-  EXPECT_TRUE(absl::ParseDuration("1us", &d));
-  EXPECT_EQ(absl::Microseconds(1), d);
-  EXPECT_TRUE(absl::ParseDuration("1ms", &d));
-  EXPECT_EQ(absl::Milliseconds(1), d);
-  EXPECT_TRUE(absl::ParseDuration("1s", &d));
-  EXPECT_EQ(absl::Seconds(1), d);
-  EXPECT_TRUE(absl::ParseDuration("2m", &d));
-  EXPECT_EQ(absl::Minutes(2), d);
-  EXPECT_TRUE(absl::ParseDuration("2h", &d));
-  EXPECT_EQ(absl::Hours(2), d);
-
-  // Huge counts of a unit.
-  EXPECT_TRUE(absl::ParseDuration("9223372036854775807us", &d));
-  EXPECT_EQ(absl::Microseconds(9223372036854775807), d);
-  EXPECT_TRUE(absl::ParseDuration("-9223372036854775807us", &d));
-  EXPECT_EQ(absl::Microseconds(-9223372036854775807), d);
-
-  // Multiple units.
-  EXPECT_TRUE(absl::ParseDuration("2h3m4s", &d));
-  EXPECT_EQ(absl::Hours(2) + absl::Minutes(3) + absl::Seconds(4), d);
-  EXPECT_TRUE(absl::ParseDuration("3m4s5us", &d));
-  EXPECT_EQ(absl::Minutes(3) + absl::Seconds(4) + absl::Microseconds(5), d);
-  EXPECT_TRUE(absl::ParseDuration("2h3m4s5ms6us7ns", &d));
-  EXPECT_EQ(absl::Hours(2) + absl::Minutes(3) + absl::Seconds(4) +
-                absl::Milliseconds(5) + absl::Microseconds(6) +
-                absl::Nanoseconds(7),
-            d);
-
-  // Multiple units out of order.
-  EXPECT_TRUE(absl::ParseDuration("2us3m4s5h", &d));
-  EXPECT_EQ(absl::Hours(5) + absl::Minutes(3) + absl::Seconds(4) +
-                absl::Microseconds(2),
-            d);
-
-  // Fractional values of units.
-  EXPECT_TRUE(absl::ParseDuration("1.5ns", &d));
-  EXPECT_EQ(1.5 * absl::Nanoseconds(1), d);
-  EXPECT_TRUE(absl::ParseDuration("1.5us", &d));
-  EXPECT_EQ(1.5 * absl::Microseconds(1), d);
-  EXPECT_TRUE(absl::ParseDuration("1.5ms", &d));
-  EXPECT_EQ(1.5 * absl::Milliseconds(1), d);
-  EXPECT_TRUE(absl::ParseDuration("1.5s", &d));
-  EXPECT_EQ(1.5 * absl::Seconds(1), d);
-  EXPECT_TRUE(absl::ParseDuration("1.5m", &d));
-  EXPECT_EQ(1.5 * absl::Minutes(1), d);
-  EXPECT_TRUE(absl::ParseDuration("1.5h", &d));
-  EXPECT_EQ(1.5 * absl::Hours(1), d);
-
-  // Huge fractional counts of a unit.
-  EXPECT_TRUE(absl::ParseDuration("0.4294967295s", &d));
-  EXPECT_EQ(absl::Nanoseconds(429496729) + absl::Nanoseconds(1) / 2, d);
-  EXPECT_TRUE(absl::ParseDuration("0.429496729501234567890123456789s", &d));
-  EXPECT_EQ(absl::Nanoseconds(429496729) + absl::Nanoseconds(1) / 2, d);
-
-  // Negative durations.
-  EXPECT_TRUE(absl::ParseDuration("-1s", &d));
-  EXPECT_EQ(absl::Seconds(-1), d);
-  EXPECT_TRUE(absl::ParseDuration("-1m", &d));
-  EXPECT_EQ(absl::Minutes(-1), d);
-  EXPECT_TRUE(absl::ParseDuration("-1h", &d));
-  EXPECT_EQ(absl::Hours(-1), d);
-
-  EXPECT_TRUE(absl::ParseDuration("-1h2s", &d));
-  EXPECT_EQ(-(absl::Hours(1) + absl::Seconds(2)), d);
-  EXPECT_FALSE(absl::ParseDuration("1h-2s", &d));
-  EXPECT_FALSE(absl::ParseDuration("-1h-2s", &d));
-  EXPECT_FALSE(absl::ParseDuration("-1h -2s", &d));
-}
-
-TEST(Duration, FormatParseRoundTrip) {
-#define TEST_PARSE_ROUNDTRIP(d)                \
-  do {                                         \
-    std::string s = absl::FormatDuration(d);   \
-    absl::Duration dur;                        \
-    EXPECT_TRUE(absl::ParseDuration(s, &dur)); \
-    EXPECT_EQ(d, dur);                         \
-  } while (0)
-
-  TEST_PARSE_ROUNDTRIP(absl::Nanoseconds(1));
-  TEST_PARSE_ROUNDTRIP(absl::Microseconds(1));
-  TEST_PARSE_ROUNDTRIP(absl::Milliseconds(1));
-  TEST_PARSE_ROUNDTRIP(absl::Seconds(1));
-  TEST_PARSE_ROUNDTRIP(absl::Minutes(1));
-  TEST_PARSE_ROUNDTRIP(absl::Hours(1));
-  TEST_PARSE_ROUNDTRIP(absl::Hours(1) + absl::Nanoseconds(2));
-
-  TEST_PARSE_ROUNDTRIP(absl::Nanoseconds(-1));
-  TEST_PARSE_ROUNDTRIP(absl::Microseconds(-1));
-  TEST_PARSE_ROUNDTRIP(absl::Milliseconds(-1));
-  TEST_PARSE_ROUNDTRIP(absl::Seconds(-1));
-  TEST_PARSE_ROUNDTRIP(absl::Minutes(-1));
-  TEST_PARSE_ROUNDTRIP(absl::Hours(-1));
-
-  TEST_PARSE_ROUNDTRIP(absl::Hours(-1) + absl::Nanoseconds(2));
-  TEST_PARSE_ROUNDTRIP(absl::Hours(1) + absl::Nanoseconds(-2));
-  TEST_PARSE_ROUNDTRIP(absl::Hours(-1) + absl::Nanoseconds(-2));
-
-  TEST_PARSE_ROUNDTRIP(absl::Nanoseconds(1) +
-                       absl::Nanoseconds(1) / 4);  // 1.25ns
-
-  const absl::Duration huge_range = ApproxYears(100000000000);
-  TEST_PARSE_ROUNDTRIP(huge_range);
-  TEST_PARSE_ROUNDTRIP(huge_range + (absl::Seconds(1) - absl::Nanoseconds(1)));
-
-#undef TEST_PARSE_ROUNDTRIP
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/format.cc b/third_party/abseil_cpp/absl/time/format.cc
deleted file mode 100644
index 4005fb704c..0000000000
--- a/third_party/abseil_cpp/absl/time/format.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <string.h>
-
-#include <cctype>
-#include <cstdint>
-
-#include "absl/strings/match.h"
-#include "absl/strings/string_view.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-#include "absl/time/time.h"
-
-namespace cctz = absl::time_internal::cctz;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-ABSL_DLL extern const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
-ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
-
-ABSL_DLL extern const char RFC1123_full[] = "%a, %d %b %E4Y %H:%M:%S %z";
-ABSL_DLL extern const char RFC1123_no_wday[] = "%d %b %E4Y %H:%M:%S %z";
-
-namespace {
-
-const char kInfiniteFutureStr[] = "infinite-future";
-const char kInfinitePastStr[] = "infinite-past";
-
-struct cctz_parts {
-  cctz::time_point<cctz::seconds> sec;
-  cctz::detail::femtoseconds fem;
-};
-
-inline cctz::time_point<cctz::seconds> unix_epoch() {
-  return std::chrono::time_point_cast<cctz::seconds>(
-      std::chrono::system_clock::from_time_t(0));
-}
-
-// Splits a Time into seconds and femtoseconds, which can be used with CCTZ.
-// Requires that 't' is finite. See duration.cc for details about rep_hi and
-// rep_lo.
-cctz_parts Split(absl::Time t) {
-  const auto d = time_internal::ToUnixDuration(t);
-  const int64_t rep_hi = time_internal::GetRepHi(d);
-  const int64_t rep_lo = time_internal::GetRepLo(d);
-  const auto sec = unix_epoch() + cctz::seconds(rep_hi);
-  const auto fem = cctz::detail::femtoseconds(rep_lo * (1000 * 1000 / 4));
-  return {sec, fem};
-}
-
-// Joins the given seconds and femtoseconds into a Time. See duration.cc for
-// details about rep_hi and rep_lo.
-absl::Time Join(const cctz_parts& parts) {
-  const int64_t rep_hi = (parts.sec - unix_epoch()).count();
-  const uint32_t rep_lo = parts.fem.count() / (1000 * 1000 / 4);
-  const auto d = time_internal::MakeDuration(rep_hi, rep_lo);
-  return time_internal::FromUnixDuration(d);
-}
-
-}  // namespace
-
-std::string FormatTime(absl::string_view format, absl::Time t,
-                       absl::TimeZone tz) {
-  if (t == absl::InfiniteFuture()) return std::string(kInfiniteFutureStr);
-  if (t == absl::InfinitePast()) return std::string(kInfinitePastStr);
-  const auto parts = Split(t);
-  return cctz::detail::format(std::string(format), parts.sec, parts.fem,
-                              cctz::time_zone(tz));
-}
-
-std::string FormatTime(absl::Time t, absl::TimeZone tz) {
-  return FormatTime(RFC3339_full, t, tz);
-}
-
-std::string FormatTime(absl::Time t) {
-  return absl::FormatTime(RFC3339_full, t, absl::LocalTimeZone());
-}
-
-bool ParseTime(absl::string_view format, absl::string_view input,
-               absl::Time* time, std::string* err) {
-  return absl::ParseTime(format, input, absl::UTCTimeZone(), time, err);
-}
-
-// If the input string does not contain an explicit UTC offset, interpret
-// the fields with respect to the given TimeZone.
-bool ParseTime(absl::string_view format, absl::string_view input,
-               absl::TimeZone tz, absl::Time* time, std::string* err) {
-  auto strip_leading_space = [](absl::string_view* sv) {
-    while (!sv->empty()) {
-      if (!std::isspace(sv->front())) return;
-      sv->remove_prefix(1);
-    }
-  };
-
-  // Portable toolchains means we don't get nice constexpr here.
-  struct Literal {
-    const char* name;
-    size_t size;
-    absl::Time value;
-  };
-  static Literal literals[] = {
-      {kInfiniteFutureStr, strlen(kInfiniteFutureStr), InfiniteFuture()},
-      {kInfinitePastStr, strlen(kInfinitePastStr), InfinitePast()},
-  };
-  strip_leading_space(&input);
-  for (const auto& lit : literals) {
-    if (absl::StartsWith(input, absl::string_view(lit.name, lit.size))) {
-      absl::string_view tail = input;
-      tail.remove_prefix(lit.size);
-      strip_leading_space(&tail);
-      if (tail.empty()) {
-        *time = lit.value;
-        return true;
-      }
-    }
-  }
-
-  std::string error;
-  cctz_parts parts;
-  const bool b =
-      cctz::detail::parse(std::string(format), std::string(input),
-                          cctz::time_zone(tz), &parts.sec, &parts.fem, &error);
-  if (b) {
-    *time = Join(parts);
-  } else if (err != nullptr) {
-    *err = error;
-  }
-  return b;
-}
-
-// Functions required to support absl::Time flags.
-bool AbslParseFlag(absl::string_view text, absl::Time* t, std::string* error) {
-  return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error);
-}
-
-std::string AbslUnparseFlag(absl::Time t) {
-  return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone());
-}
-bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) {
-  return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error);
-}
-
-std::string UnparseFlag(absl::Time t) {
-  return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone());
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/format_benchmark.cc b/third_party/abseil_cpp/absl/time/format_benchmark.cc
deleted file mode 100644
index 19e481dbd1..0000000000
--- a/third_party/abseil_cpp/absl/time/format_benchmark.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cstddef>
-#include <string>
-
-#include "absl/time/internal/test_util.h"
-#include "absl/time/time.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-namespace {
-const char* const kFormats[] = {
-    absl::RFC1123_full,     // 0
-    absl::RFC1123_no_wday,  // 1
-    absl::RFC3339_full,     // 2
-    absl::RFC3339_sec,      // 3
-    "%Y-%m-%d%ET%H:%M:%S",  // 4
-    "%Y-%m-%d",             // 5
-};
-const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
-}  // namespace
-
-void BM_Format_FormatTime(benchmark::State& state) {
-  const std::string fmt = kFormats[state.range(0)];
-  state.SetLabel(fmt);
-  const absl::TimeZone lax =
-      absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  const absl::Time t =
-      absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax) +
-      absl::Nanoseconds(1);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::FormatTime(fmt, t, lax).length());
-  }
-}
-BENCHMARK(BM_Format_FormatTime)->DenseRange(0, kNumFormats - 1);
-
-void BM_Format_ParseTime(benchmark::State& state) {
-  const std::string fmt = kFormats[state.range(0)];
-  state.SetLabel(fmt);
-  const absl::TimeZone lax =
-      absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  absl::Time t = absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax) +
-                 absl::Nanoseconds(1);
-  const std::string when = absl::FormatTime(fmt, t, lax);
-  std::string err;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ParseTime(fmt, when, lax, &t, &err));
-  }
-}
-BENCHMARK(BM_Format_ParseTime)->DenseRange(0, kNumFormats - 1);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/format_test.cc b/third_party/abseil_cpp/absl/time/format_test.cc
deleted file mode 100644
index a9a1eb8ee1..0000000000
--- a/third_party/abseil_cpp/absl/time/format_test.cc
+++ /dev/null
@@ -1,441 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <cstdint>
-#include <limits>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/time/internal/test_util.h"
-#include "absl/time/time.h"
-
-using testing::HasSubstr;
-
-namespace {
-
-// A helper that tests the given format specifier by itself, and with leading
-// and trailing characters.  For example: TestFormatSpecifier(t, "%a", "Thu").
-void TestFormatSpecifier(absl::Time t, absl::TimeZone tz,
-                         const std::string& fmt, const std::string& ans) {
-  EXPECT_EQ(ans, absl::FormatTime(fmt, t, tz));
-  EXPECT_EQ("xxx " + ans, absl::FormatTime("xxx " + fmt, t, tz));
-  EXPECT_EQ(ans + " yyy", absl::FormatTime(fmt + " yyy", t, tz));
-  EXPECT_EQ("xxx " + ans + " yyy",
-            absl::FormatTime("xxx " + fmt + " yyy", t, tz));
-}
-
-//
-// Testing FormatTime()
-//
-
-TEST(FormatTime, Basics) {
-  absl::TimeZone tz = absl::UTCTimeZone();
-  absl::Time t = absl::FromTimeT(0);
-
-  // Starts with a couple basic edge cases.
-  EXPECT_EQ("", absl::FormatTime("", t, tz));
-  EXPECT_EQ(" ", absl::FormatTime(" ", t, tz));
-  EXPECT_EQ("  ", absl::FormatTime("  ", t, tz));
-  EXPECT_EQ("xxx", absl::FormatTime("xxx", t, tz));
-  std::string big(128, 'x');
-  EXPECT_EQ(big, absl::FormatTime(big, t, tz));
-  // Cause the 1024-byte buffer to grow.
-  std::string bigger(100000, 'x');
-  EXPECT_EQ(bigger, absl::FormatTime(bigger, t, tz));
-
-  t += absl::Hours(13) + absl::Minutes(4) + absl::Seconds(5);
-  t += absl::Milliseconds(6) + absl::Microseconds(7) + absl::Nanoseconds(8);
-  EXPECT_EQ("1970-01-01", absl::FormatTime("%Y-%m-%d", t, tz));
-  EXPECT_EQ("13:04:05", absl::FormatTime("%H:%M:%S", t, tz));
-  EXPECT_EQ("13:04:05.006", absl::FormatTime("%H:%M:%E3S", t, tz));
-  EXPECT_EQ("13:04:05.006007", absl::FormatTime("%H:%M:%E6S", t, tz));
-  EXPECT_EQ("13:04:05.006007008", absl::FormatTime("%H:%M:%E9S", t, tz));
-}
-
-TEST(FormatTime, LocaleSpecific) {
-  const absl::TimeZone tz = absl::UTCTimeZone();
-  absl::Time t = absl::FromTimeT(0);
-
-  TestFormatSpecifier(t, tz, "%a", "Thu");
-  TestFormatSpecifier(t, tz, "%A", "Thursday");
-  TestFormatSpecifier(t, tz, "%b", "Jan");
-  TestFormatSpecifier(t, tz, "%B", "January");
-
-  // %c should at least produce the numeric year and time-of-day.
-  const std::string s =
-      absl::FormatTime("%c", absl::FromTimeT(0), absl::UTCTimeZone());
-  EXPECT_THAT(s, HasSubstr("1970"));
-  EXPECT_THAT(s, HasSubstr("00:00:00"));
-
-  TestFormatSpecifier(t, tz, "%p", "AM");
-  TestFormatSpecifier(t, tz, "%x", "01/01/70");
-  TestFormatSpecifier(t, tz, "%X", "00:00:00");
-}
-
-TEST(FormatTime, ExtendedSeconds) {
-  const absl::TimeZone tz = absl::UTCTimeZone();
-
-  // No subseconds.
-  absl::Time t = absl::FromTimeT(0) + absl::Seconds(5);
-  EXPECT_EQ("05", absl::FormatTime("%E*S", t, tz));
-  EXPECT_EQ("05.000000000000000", absl::FormatTime("%E15S", t, tz));
-
-  // With subseconds.
-  t += absl::Milliseconds(6) + absl::Microseconds(7) + absl::Nanoseconds(8);
-  EXPECT_EQ("05.006007008", absl::FormatTime("%E*S", t, tz));
-  EXPECT_EQ("05", absl::FormatTime("%E0S", t, tz));
-  EXPECT_EQ("05.006007008000000", absl::FormatTime("%E15S", t, tz));
-
-  // Times before the Unix epoch.
-  t = absl::FromUnixMicros(-1);
-  EXPECT_EQ("1969-12-31 23:59:59.999999",
-            absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
-
-  // Here is a "%E*S" case we got wrong for a while.  While the first
-  // instant below is correctly rendered as "...:07.333304", the second
-  // one used to appear as "...:07.33330499999999999".
-  t = absl::FromUnixMicros(1395024427333304);
-  EXPECT_EQ("2014-03-17 02:47:07.333304",
-            absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
-  t += absl::Microseconds(1);
-  EXPECT_EQ("2014-03-17 02:47:07.333305",
-            absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
-}
-
-TEST(FormatTime, RFC1123FormatPadsYear) {  // locale specific
-  absl::TimeZone tz = absl::UTCTimeZone();
-
-  // A year of 77 should be padded to 0077.
-  absl::Time t = absl::FromCivil(absl::CivilSecond(77, 6, 28, 9, 8, 7), tz);
-  EXPECT_EQ("Mon, 28 Jun 0077 09:08:07 +0000",
-            absl::FormatTime(absl::RFC1123_full, t, tz));
-  EXPECT_EQ("28 Jun 0077 09:08:07 +0000",
-            absl::FormatTime(absl::RFC1123_no_wday, t, tz));
-}
-
-TEST(FormatTime, InfiniteTime) {
-  absl::TimeZone tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
-
-  // The format and timezone are ignored.
-  EXPECT_EQ("infinite-future",
-            absl::FormatTime("%H:%M blah", absl::InfiniteFuture(), tz));
-  EXPECT_EQ("infinite-past",
-            absl::FormatTime("%H:%M blah", absl::InfinitePast(), tz));
-}
-
-//
-// Testing ParseTime()
-//
-
-TEST(ParseTime, Basics) {
-  absl::Time t = absl::FromTimeT(1234567890);
-  std::string err;
-
-  // Simple edge cases.
-  EXPECT_TRUE(absl::ParseTime("", "", &t, &err)) << err;
-  EXPECT_EQ(absl::UnixEpoch(), t);  // everything defaulted
-  EXPECT_TRUE(absl::ParseTime(" ", " ", &t, &err)) << err;
-  EXPECT_TRUE(absl::ParseTime("  ", "  ", &t, &err)) << err;
-  EXPECT_TRUE(absl::ParseTime("x", "x", &t, &err)) << err;
-  EXPECT_TRUE(absl::ParseTime("xxx", "xxx", &t, &err)) << err;
-
-  EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
-                              "2013-06-28 19:08:09 -0800", &t, &err))
-      << err;
-  const auto ci = absl::FixedTimeZone(-8 * 60 * 60).At(t);
-  EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
-  EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
-}
-
-TEST(ParseTime, NullErrorString) {
-  absl::Time t;
-  EXPECT_FALSE(absl::ParseTime("%Q", "invalid format", &t, nullptr));
-  EXPECT_FALSE(absl::ParseTime("%H", "12 trailing data", &t, nullptr));
-  EXPECT_FALSE(
-      absl::ParseTime("%H out of range", "42 out of range", &t, nullptr));
-}
-
-TEST(ParseTime, WithTimeZone) {
-  const absl::TimeZone tz =
-      absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  absl::Time t;
-  std::string e;
-
-  // We can parse a string without a UTC offset if we supply a timezone.
-  EXPECT_TRUE(
-      absl::ParseTime("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &t, &e))
-      << e;
-  auto ci = tz.At(t);
-  EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
-  EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
-
-  // But the timezone is ignored when a UTC offset is present.
-  EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
-                              "2013-06-28 19:08:09 +0800", tz, &t, &e))
-      << e;
-  ci = absl::FixedTimeZone(8 * 60 * 60).At(t);
-  EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
-  EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
-}
-
-TEST(ParseTime, ErrorCases) {
-  absl::Time t = absl::FromTimeT(0);
-  std::string err;
-
-  EXPECT_FALSE(absl::ParseTime("%S", "123", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
-
-  // Can't parse an illegal format specifier.
-  err.clear();
-  EXPECT_FALSE(absl::ParseTime("%Q", "x", &t, &err)) << err;
-  // Exact contents of "err" are platform-dependent because of
-  // differences in the strptime implementation between macOS and Linux.
-  EXPECT_FALSE(err.empty());
-
-  // Fails because of trailing, unparsed data "blah".
-  EXPECT_FALSE(absl::ParseTime("%m-%d", "2-3 blah", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
-
-  // Feb 31 requires normalization.
-  EXPECT_FALSE(absl::ParseTime("%m-%d", "2-31", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Out-of-range"));
-
-  // Check that we cannot have spaces in UTC offsets.
-  EXPECT_TRUE(absl::ParseTime("%z", "-0203", &t, &err)) << err;
-  EXPECT_FALSE(absl::ParseTime("%z", "- 2 3", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-  EXPECT_TRUE(absl::ParseTime("%Ez", "-02:03", &t, &err)) << err;
-  EXPECT_FALSE(absl::ParseTime("%Ez", "- 2: 3", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-
-  // Check that we reject other malformed UTC offsets.
-  EXPECT_FALSE(absl::ParseTime("%Ez", "+-08:00", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-  EXPECT_FALSE(absl::ParseTime("%Ez", "-+08:00", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-
-  // Check that we do not accept "-0" in fields that allow zero.
-  EXPECT_FALSE(absl::ParseTime("%Y", "-0", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-  EXPECT_FALSE(absl::ParseTime("%E4Y", "-0", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-  EXPECT_FALSE(absl::ParseTime("%H", "-0", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-  EXPECT_FALSE(absl::ParseTime("%M", "-0", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-  EXPECT_FALSE(absl::ParseTime("%S", "-0", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-  EXPECT_FALSE(absl::ParseTime("%z", "+-000", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-  EXPECT_FALSE(absl::ParseTime("%Ez", "+-0:00", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-  EXPECT_FALSE(absl::ParseTime("%z", "-00-0", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
-  EXPECT_FALSE(absl::ParseTime("%Ez", "-00:-0", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
-}
-
-TEST(ParseTime, ExtendedSeconds) {
-  std::string err;
-  absl::Time t;
-
-  // Here is a "%E*S" case we got wrong for a while.  The fractional
-  // part of the first instant is less than 2^31 and was correctly
-  // parsed, while the second (and any subsecond field >=2^31) failed.
-  t = absl::UnixEpoch();
-  EXPECT_TRUE(absl::ParseTime("%E*S", "0.2147483647", &t, &err)) << err;
-  EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
-                absl::Nanoseconds(1) / 2,
-            t);
-  t = absl::UnixEpoch();
-  EXPECT_TRUE(absl::ParseTime("%E*S", "0.2147483648", &t, &err)) << err;
-  EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
-                absl::Nanoseconds(3) / 4,
-            t);
-
-  // We should also be able to specify long strings of digits far
-  // beyond the current resolution and have them convert the same way.
-  t = absl::UnixEpoch();
-  EXPECT_TRUE(absl::ParseTime(
-      "%E*S", "0.214748364801234567890123456789012345678901234567890123456789",
-      &t, &err))
-      << err;
-  EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
-                absl::Nanoseconds(3) / 4,
-            t);
-}
-
-TEST(ParseTime, ExtendedOffsetErrors) {
-  std::string err;
-  absl::Time t;
-
-  // %z against +-HHMM.
-  EXPECT_FALSE(absl::ParseTime("%z", "-123", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
-
-  // %z against +-HH.
-  EXPECT_FALSE(absl::ParseTime("%z", "-1", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-
-  // %Ez against +-HH:MM.
-  EXPECT_FALSE(absl::ParseTime("%Ez", "-12:3", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
-
-  // %Ez against +-HHMM.
-  EXPECT_FALSE(absl::ParseTime("%Ez", "-123", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
-
-  // %Ez against +-HH.
-  EXPECT_FALSE(absl::ParseTime("%Ez", "-1", &t, &err)) << err;
-  EXPECT_THAT(err, HasSubstr("Failed to parse"));
-}
-
-TEST(ParseTime, InfiniteTime) {
-  absl::Time t;
-  std::string err;
-  EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-future", &t, &err));
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-
-  // Surrounding whitespace.
-  EXPECT_TRUE(absl::ParseTime("%H:%M blah", "  infinite-future", &t, &err));
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-  EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-future  ", &t, &err));
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-  EXPECT_TRUE(absl::ParseTime("%H:%M blah", "  infinite-future  ", &t, &err));
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-
-  EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-past", &t, &err));
-  EXPECT_EQ(absl::InfinitePast(), t);
-
-  // Surrounding whitespace.
-  EXPECT_TRUE(absl::ParseTime("%H:%M blah", "  infinite-past", &t, &err));
-  EXPECT_EQ(absl::InfinitePast(), t);
-  EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-past  ", &t, &err));
-  EXPECT_EQ(absl::InfinitePast(), t);
-  EXPECT_TRUE(absl::ParseTime("%H:%M blah", "  infinite-past  ", &t, &err));
-  EXPECT_EQ(absl::InfinitePast(), t);
-
-  // "infinite-future" as literal string
-  absl::TimeZone tz = absl::UTCTimeZone();
-  EXPECT_TRUE(absl::ParseTime("infinite-future %H:%M", "infinite-future 03:04",
-                              &t, &err));
-  EXPECT_NE(absl::InfiniteFuture(), t);
-  EXPECT_EQ(3, tz.At(t).cs.hour());
-  EXPECT_EQ(4, tz.At(t).cs.minute());
-
-  // "infinite-past" as literal string
-  EXPECT_TRUE(
-      absl::ParseTime("infinite-past %H:%M", "infinite-past 03:04", &t, &err));
-  EXPECT_NE(absl::InfinitePast(), t);
-  EXPECT_EQ(3, tz.At(t).cs.hour());
-  EXPECT_EQ(4, tz.At(t).cs.minute());
-
-  // The input doesn't match the format.
-  EXPECT_FALSE(absl::ParseTime("infinite-future %H:%M", "03:04", &t, &err));
-  EXPECT_FALSE(absl::ParseTime("infinite-past %H:%M", "03:04", &t, &err));
-}
-
-TEST(ParseTime, FailsOnUnrepresentableTime) {
-  const absl::TimeZone utc = absl::UTCTimeZone();
-  absl::Time t;
-  EXPECT_FALSE(
-      absl::ParseTime("%Y-%m-%d", "-292277022657-01-27", utc, &t, nullptr));
-  EXPECT_TRUE(
-      absl::ParseTime("%Y-%m-%d", "-292277022657-01-28", utc, &t, nullptr));
-  EXPECT_TRUE(
-      absl::ParseTime("%Y-%m-%d", "292277026596-12-04", utc, &t, nullptr));
-  EXPECT_FALSE(
-      absl::ParseTime("%Y-%m-%d", "292277026596-12-05", utc, &t, nullptr));
-}
-
-//
-// Roundtrip test for FormatTime()/ParseTime().
-//
-
-TEST(FormatParse, RoundTrip) {
-  const absl::TimeZone lax =
-      absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  const absl::Time in =
-      absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax);
-  const absl::Duration subseconds = absl::Nanoseconds(654321);
-  std::string err;
-
-  // RFC3339, which renders subseconds.
-  {
-    absl::Time out;
-    const std::string s =
-        absl::FormatTime(absl::RFC3339_full, in + subseconds, lax);
-    EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
-        << s << ": " << err;
-    EXPECT_EQ(in + subseconds, out);  // RFC3339_full includes %Ez
-  }
-
-  // RFC1123, which only does whole seconds.
-  {
-    absl::Time out;
-    const std::string s = absl::FormatTime(absl::RFC1123_full, in, lax);
-    EXPECT_TRUE(absl::ParseTime(absl::RFC1123_full, s, &out, &err))
-        << s << ": " << err;
-    EXPECT_EQ(in, out);  // RFC1123_full includes %z
-  }
-
-  // `absl::FormatTime()` falls back to strftime() for "%c", which appears to
-  // work. On Windows, `absl::ParseTime()` falls back to std::get_time() which
-  // appears to fail on "%c" (or at least on the "%c" text produced by
-  // `strftime()`). This makes it fail the round-trip test.
-  //
-  // Under the emscripten compiler `absl::ParseTime() falls back to
-  // `strptime()`, but that ends up using a different definition for "%c"
-  // compared to `strftime()`, also causing the round-trip test to fail
-  // (see https://github.com/kripken/emscripten/pull/7491).
-#if !defined(_MSC_VER) && !defined(__EMSCRIPTEN__)
-  // Even though we don't know what %c will produce, it should roundtrip,
-  // but only in the 0-offset timezone.
-  {
-    absl::Time out;
-    const std::string s = absl::FormatTime("%c", in, absl::UTCTimeZone());
-    EXPECT_TRUE(absl::ParseTime("%c", s, &out, &err)) << s << ": " << err;
-    EXPECT_EQ(in, out);
-  }
-#endif  // !_MSC_VER && !__EMSCRIPTEN__
-}
-
-TEST(FormatParse, RoundTripDistantFuture) {
-  const absl::TimeZone tz = absl::UTCTimeZone();
-  const absl::Time in =
-      absl::FromUnixSeconds(std::numeric_limits<int64_t>::max());
-  std::string err;
-
-  absl::Time out;
-  const std::string s = absl::FormatTime(absl::RFC3339_full, in, tz);
-  EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
-      << s << ": " << err;
-  EXPECT_EQ(in, out);
-}
-
-TEST(FormatParse, RoundTripDistantPast) {
-  const absl::TimeZone tz = absl::UTCTimeZone();
-  const absl::Time in =
-      absl::FromUnixSeconds(std::numeric_limits<int64_t>::min());
-  std::string err;
-
-  absl::Time out;
-  const std::string s = absl::FormatTime(absl::RFC3339_full, in, tz);
-  EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
-      << s << ": " << err;
-  EXPECT_EQ(in, out);
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/BUILD.bazel b/third_party/abseil_cpp/absl/time/internal/cctz/BUILD.bazel
deleted file mode 100644
index 45a952924d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/BUILD.bazel
+++ /dev/null
@@ -1,171 +0,0 @@
-# Copyright 2016 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#   https://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-
-package(features = ["-parse_headers"])
-
-licenses(["notice"])
-
-filegroup(
-    name = "zoneinfo",
-    srcs = glob(["testdata/zoneinfo/**"]),
-)
-
-config_setting(
-    name = "osx",
-    constraint_values = [
-        "@bazel_tools//platforms:osx",
-    ],
-)
-
-config_setting(
-    name = "ios",
-    constraint_values = [
-        "@bazel_tools//platforms:ios",
-    ],
-)
-
-### libraries
-
-cc_library(
-    name = "civil_time",
-    srcs = ["src/civil_time_detail.cc"],
-    hdrs = [
-        "include/cctz/civil_time.h",
-    ],
-    textual_hdrs = ["include/cctz/civil_time_detail.h"],
-    visibility = ["//visibility:public"],
-    deps = ["//absl/base:config"],
-)
-
-cc_library(
-    name = "time_zone",
-    srcs = [
-        "src/time_zone_fixed.cc",
-        "src/time_zone_fixed.h",
-        "src/time_zone_format.cc",
-        "src/time_zone_if.cc",
-        "src/time_zone_if.h",
-        "src/time_zone_impl.cc",
-        "src/time_zone_impl.h",
-        "src/time_zone_info.cc",
-        "src/time_zone_info.h",
-        "src/time_zone_libc.cc",
-        "src/time_zone_libc.h",
-        "src/time_zone_lookup.cc",
-        "src/time_zone_posix.cc",
-        "src/time_zone_posix.h",
-        "src/tzfile.h",
-        "src/zone_info_source.cc",
-    ],
-    hdrs = [
-        "include/cctz/time_zone.h",
-        "include/cctz/zone_info_source.h",
-    ],
-    linkopts = select({
-        ":osx": [
-            "-framework Foundation",
-        ],
-        ":ios": [
-            "-framework Foundation",
-        ],
-        "//conditions:default": [],
-    }),
-    visibility = ["//visibility:public"],
-    deps = [
-        ":civil_time",
-        "//absl/base:config",
-    ],
-)
-
-### tests
-
-test_suite(
-    name = "all_tests",
-    visibility = ["//visibility:public"],
-)
-
-cc_test(
-    name = "civil_time_test",
-    size = "small",
-    srcs = ["src/civil_time_test.cc"],
-    deps = [
-        ":civil_time",
-        "//absl/base:config",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "time_zone_format_test",
-    size = "small",
-    srcs = ["src/time_zone_format_test.cc"],
-    data = [":zoneinfo"],
-    tags = [
-        "no_test_android_arm",
-        "no_test_android_arm64",
-        "no_test_android_x86",
-    ],
-    deps = [
-        ":civil_time",
-        ":time_zone",
-        "//absl/base:config",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "time_zone_lookup_test",
-    size = "small",
-    timeout = "moderate",
-    srcs = ["src/time_zone_lookup_test.cc"],
-    data = [":zoneinfo"],
-    tags = [
-        "no_test_android_arm",
-        "no_test_android_arm64",
-        "no_test_android_x86",
-    ],
-    deps = [
-        ":civil_time",
-        ":time_zone",
-        "//absl/base:config",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-### benchmarks
-
-cc_test(
-    name = "cctz_benchmark",
-    srcs = [
-        "src/cctz_benchmark.cc",
-        "src/time_zone_if.h",
-        "src/time_zone_impl.h",
-        "src/time_zone_info.h",
-        "src/tzfile.h",
-    ],
-    linkstatic = 1,
-    tags = ["benchmark"],
-    deps = [
-        ":civil_time",
-        ":time_zone",
-        "//absl/base:config",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-### examples
-
-### binaries
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time.h b/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time.h
deleted file mode 100644
index d47ff86fe4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time.h
+++ /dev/null
@@ -1,332 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
-#define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// The term "civil time" refers to the legally recognized human-scale time
-// that is represented by the six fields YYYY-MM-DD hh:mm:ss. Modern-day civil
-// time follows the Gregorian Calendar and is a time-zone-independent concept.
-// A "date" is perhaps the most common example of a civil time (represented in
-// this library as cctz::civil_day). This library provides six classes and a
-// handful of functions that help with rounding, iterating, and arithmetic on
-// civil times while avoiding complications like daylight-saving time (DST).
-//
-// The following six classes form the core of this civil-time library:
-//
-//   * civil_second
-//   * civil_minute
-//   * civil_hour
-//   * civil_day
-//   * civil_month
-//   * civil_year
-//
-// Each class is a simple value type with the same interface for construction
-// and the same six accessors for each of the civil fields (year, month, day,
-// hour, minute, and second, aka YMDHMS). These classes differ only in their
-// alignment, which is indicated by the type name and specifies the field on
-// which arithmetic operates.
-//
-// Each class can be constructed by passing up to six optional integer
-// arguments representing the YMDHMS fields (in that order) to the
-// constructor. Omitted fields are assigned their minimum valid value. Hours,
-// minutes, and seconds will be set to 0, month and day will be set to 1, and
-// since there is no minimum valid year, it will be set to 1970. So, a
-// default-constructed civil-time object will have YMDHMS fields representing
-// "1970-01-01 00:00:00". Fields that are out-of-range are normalized (e.g.,
-// October 32 -> November 1) so that all civil-time objects represent valid
-// values.
-//
-// Each civil-time class is aligned to the civil-time field indicated in the
-// class's name after normalization. Alignment is performed by setting all the
-// inferior fields to their minimum valid value (as described above). The
-// following are examples of how each of the six types would align the fields
-// representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the
-// string format used here is not important; it's just a shorthand way of
-// showing the six YMDHMS fields.)
-//
-//   civil_second  2015-11-22 12:34:56
-//   civil_minute  2015-11-22 12:34:00
-//   civil_hour    2015-11-22 12:00:00
-//   civil_day     2015-11-22 00:00:00
-//   civil_month   2015-11-01 00:00:00
-//   civil_year    2015-01-01 00:00:00
-//
-// Each civil-time type performs arithmetic on the field to which it is
-// aligned. This means that adding 1 to a civil_day increments the day field
-// (normalizing as necessary), and subtracting 7 from a civil_month operates
-// on the month field (normalizing as necessary). All arithmetic produces a
-// valid civil time. Difference requires two similarly aligned civil-time
-// objects and returns the scalar answer in units of the objects' alignment.
-// For example, the difference between two civil_hour objects will give an
-// answer in units of civil hours.
-//
-// In addition to the six civil-time types just described, there are
-// a handful of helper functions and algorithms for performing common
-// calculations. These are described below.
-//
-// Note: In C++14 and later, this library is usable in a constexpr context.
-//
-// CONSTRUCTION:
-//
-// Each of the civil-time types can be constructed in two ways: by directly
-// passing to the constructor up to six (optional) integers representing the
-// YMDHMS fields, or by copying the YMDHMS fields from a differently aligned
-// civil-time type.
-//
-//   civil_day default_value;  // 1970-01-01 00:00:00
-//
-//   civil_day a(2015, 2, 3);           // 2015-02-03 00:00:00
-//   civil_day b(2015, 2, 3, 4, 5, 6);  // 2015-02-03 00:00:00
-//   civil_day c(2015);                 // 2015-01-01 00:00:00
-//
-//   civil_second ss(2015, 2, 3, 4, 5, 6);  // 2015-02-03 04:05:06
-//   civil_minute mm(ss);                   // 2015-02-03 04:05:00
-//   civil_hour hh(mm);                     // 2015-02-03 04:00:00
-//   civil_day d(hh);                       // 2015-02-03 00:00:00
-//   civil_month m(d);                      // 2015-02-01 00:00:00
-//   civil_year y(m);                       // 2015-01-01 00:00:00
-//
-//   m = civil_month(y);     // 2015-01-01 00:00:00
-//   d = civil_day(m);       // 2015-01-01 00:00:00
-//   hh = civil_hour(d);     // 2015-01-01 00:00:00
-//   mm = civil_minute(hh);  // 2015-01-01 00:00:00
-//   ss = civil_second(mm);  // 2015-01-01 00:00:00
-//
-// ALIGNMENT CONVERSION:
-//
-// The alignment of a civil-time object cannot change, but the object may be
-// used to construct a new object with a different alignment. This is referred
-// to as "realigning". When realigning to a type with the same or more
-// precision (e.g., civil_day -> civil_second), the conversion may be
-// performed implicitly since no information is lost. However, if information
-// could be discarded (e.g., civil_second -> civil_day), the conversion must
-// be explicit at the call site.
-//
-//   void fun(const civil_day& day);
-//
-//   civil_second cs;
-//   fun(cs);  // Won't compile because data may be discarded
-//   fun(civil_day(cs));  // OK: explicit conversion
-//
-//   civil_day cd;
-//   fun(cd);  // OK: no conversion needed
-//
-//   civil_month cm;
-//   fun(cm);  // OK: implicit conversion to civil_day
-//
-// NORMALIZATION:
-//
-// Integer arguments passed to the constructor may be out-of-range, in which
-// case they are normalized to produce a valid civil-time object. This enables
-// natural arithmetic on constructor arguments without worrying about the
-// field's range. Normalization guarantees that there are no invalid
-// civil-time objects.
-//
-//   civil_day d(2016, 10, 32);  // Out-of-range day; normalized to 2016-11-01
-//
-// Note: If normalization is undesired, you can signal an error by comparing
-// the constructor arguments to the normalized values returned by the YMDHMS
-// properties.
-//
-// PROPERTIES:
-//
-// All civil-time types have accessors for all six of the civil-time fields:
-// year, month, day, hour, minute, and second. Recall that fields inferior to
-// the type's alignment will be set to their minimum valid value.
-//
-//   civil_day d(2015, 6, 28);
-//   // d.year() == 2015
-//   // d.month() == 6
-//   // d.day() == 28
-//   // d.hour() == 0
-//   // d.minute() == 0
-//   // d.second() == 0
-//
-// COMPARISON:
-//
-// Comparison always considers all six YMDHMS fields, regardless of the type's
-// alignment. Comparison between differently aligned civil-time types is
-// allowed.
-//
-//   civil_day feb_3(2015, 2, 3);  // 2015-02-03 00:00:00
-//   civil_day mar_4(2015, 3, 4);  // 2015-03-04 00:00:00
-//   // feb_3 < mar_4
-//   // civil_year(feb_3) == civil_year(mar_4)
-//
-//   civil_second feb_3_noon(2015, 2, 3, 12, 0, 0);  // 2015-02-03 12:00:00
-//   // feb_3 < feb_3_noon
-//   // feb_3 == civil_day(feb_3_noon)
-//
-//   // Iterates all the days of February 2015.
-//   for (civil_day d(2015, 2, 1); d < civil_month(2015, 3); ++d) {
-//     // ...
-//   }
-//
-// STREAMING:
-//
-// Each civil-time type may be sent to an output stream using operator<<().
-// The output format follows the pattern "YYYY-MM-DDThh:mm:ss" where fields
-// inferior to the type's alignment are omitted.
-//
-//   civil_second cs(2015, 2, 3, 4, 5, 6);
-//   std::cout << cs << "\n";  // Outputs: 2015-02-03T04:05:06
-//
-//   civil_day cd(cs);
-//   std::cout << cd << "\n";  // Outputs: 2015-02-03
-//
-//   civil_year cy(cs);
-//   std::cout << cy << "\n";  // Outputs: 2015
-//
-// ARITHMETIC:
-//
-// Civil-time types support natural arithmetic operators such as addition,
-// subtraction, and difference. Arithmetic operates on the civil-time field
-// indicated in the type's name. Difference requires arguments with the same
-// alignment and returns the answer in units of the alignment.
-//
-//   civil_day a(2015, 2, 3);
-//   ++a;                         // 2015-02-04 00:00:00
-//   --a;                         // 2015-02-03 00:00:00
-//   civil_day b = a + 1;         // 2015-02-04 00:00:00
-//   civil_day c = 1 + b;         // 2015-02-05 00:00:00
-//   int n = c - a;               // n = 2 (civil days)
-//   int m = c - civil_month(c);  // Won't compile: different types.
-//
-// EXAMPLE: Adding a month to January 31.
-//
-// One of the classic questions that arises when considering a civil-time
-// library (or a date library or a date/time library) is this: "What happens
-// when you add a month to January 31?" This is an interesting question
-// because there could be a number of possible answers:
-//
-//   1. March 3 (or 2 if a leap year). This may make sense if the operation
-//      wants the equivalent of February 31.
-//   2. February 28 (or 29 if a leap year). This may make sense if the operation
-//      wants the last day of January to go to the last day of February.
-//   3. Error. The caller may get some error, an exception, an invalid date
-//      object, or maybe false is returned. This may make sense because there is
-//      no single unambiguously correct answer to the question.
-//
-// Practically speaking, any answer that is not what the programmer intended
-// is the wrong answer.
-//
-// This civil-time library avoids the problem by making it impossible to ask
-// ambiguous questions. All civil-time objects are aligned to a particular
-// civil-field boundary (such as aligned to a year, month, day, hour, minute,
-// or second), and arithmetic operates on the field to which the object is
-// aligned. This means that in order to "add a month" the object must first be
-// aligned to a month boundary, which is equivalent to the first day of that
-// month.
-//
-// Of course, there are ways to compute an answer the question at hand using
-// this civil-time library, but they require the programmer to be explicit
-// about the answer they expect. To illustrate, let's see how to compute all
-// three of the above possible answers to the question of "Jan 31 plus 1
-// month":
-//
-//   const civil_day d(2015, 1, 31);
-//
-//   // Answer 1:
-//   // Add 1 to the month field in the constructor, and rely on normalization.
-//   const auto ans_normalized = civil_day(d.year(), d.month() + 1, d.day());
-//   // ans_normalized == 2015-03-03 (aka Feb 31)
-//
-//   // Answer 2:
-//   // Add 1 to month field, capping to the end of next month.
-//   const auto next_month = civil_month(d) + 1;
-//   const auto last_day_of_next_month = civil_day(next_month + 1) - 1;
-//   const auto ans_capped = std::min(ans_normalized, last_day_of_next_month);
-//   // ans_capped == 2015-02-28
-//
-//   // Answer 3:
-//   // Signal an error if the normalized answer is not in next month.
-//   if (civil_month(ans_normalized) != next_month) {
-//     // error, month overflow
-//   }
-//
-using civil_year = detail::civil_year;
-using civil_month = detail::civil_month;
-using civil_day = detail::civil_day;
-using civil_hour = detail::civil_hour;
-using civil_minute = detail::civil_minute;
-using civil_second = detail::civil_second;
-
-// An enum class with members monday, tuesday, wednesday, thursday, friday,
-// saturday, and sunday. These enum values may be sent to an output stream
-// using operator<<(). The result is the full weekday name in English with a
-// leading capital letter.
-//
-//   weekday wd = weekday::thursday;
-//   std::cout << wd << "\n";  // Outputs: Thursday
-//
-using detail::weekday;
-
-// Returns the weekday for the given civil-time value.
-//
-//   civil_day a(2015, 8, 13);
-//   weekday wd = get_weekday(a);  // wd == weekday::thursday
-//
-using detail::get_weekday;
-
-// Returns the civil_day that strictly follows or precedes the given
-// civil_day, and that falls on the given weekday.
-//
-// For example, given:
-//
-//     August 2015
-// Su Mo Tu We Th Fr Sa
-//                    1
-//  2  3  4  5  6  7  8
-//  9 10 11 12 13 14 15
-// 16 17 18 19 20 21 22
-// 23 24 25 26 27 28 29
-// 30 31
-//
-//   civil_day a(2015, 8, 13);  // get_weekday(a) == weekday::thursday
-//   civil_day b = next_weekday(a, weekday::thursday);  // b = 2015-08-20
-//   civil_day c = prev_weekday(a, weekday::thursday);  // c = 2015-08-06
-//
-//   civil_day d = ...
-//   // Gets the following Thursday if d is not already Thursday
-//   civil_day thurs1 = next_weekday(d - 1, weekday::thursday);
-//   // Gets the previous Thursday if d is not already Thursday
-//   civil_day thurs2 = prev_weekday(d + 1, weekday::thursday);
-//
-using detail::next_weekday;
-using detail::prev_weekday;
-
-// Returns the day-of-year for the given civil-time value.
-//
-//   civil_day a(2015, 1, 1);
-//   int yd_jan_1 = get_yearday(a);   // yd_jan_1 = 1
-//   civil_day b(2015, 12, 31);
-//   int yd_dec_31 = get_yearday(b);  // yd_dec_31 = 365
-//
-using detail::get_yearday;
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h
deleted file mode 100644
index 8aadde57ca..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h
+++ /dev/null
@@ -1,628 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
-#define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
-
-#include <cstdint>
-#include <limits>
-#include <ostream>
-#include <type_traits>
-
-#include "absl/base/config.h"
-
-// Disable constexpr support unless we are in C++14 mode.
-#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910)
-#define CONSTEXPR_D constexpr  // data
-#define CONSTEXPR_F constexpr  // function
-#define CONSTEXPR_M constexpr  // member
-#else
-#define CONSTEXPR_D const
-#define CONSTEXPR_F inline
-#define CONSTEXPR_M
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// Support years that at least span the range of 64-bit time_t values.
-using year_t = std::int_fast64_t;
-
-// Type alias that indicates an argument is not normalized (e.g., the
-// constructor parameters and operands/results of addition/subtraction).
-using diff_t = std::int_fast64_t;
-
-namespace detail {
-
-// Type aliases that indicate normalized argument values.
-using month_t = std::int_fast8_t;   // [1:12]
-using day_t = std::int_fast8_t;     // [1:31]
-using hour_t = std::int_fast8_t;    // [0:23]
-using minute_t = std::int_fast8_t;  // [0:59]
-using second_t = std::int_fast8_t;  // [0:59]
-
-// Normalized civil-time fields: Y-M-D HH:MM:SS.
-struct fields {
-  CONSTEXPR_M fields(year_t year, month_t month, day_t day, hour_t hour,
-                     minute_t minute, second_t second)
-      : y(year), m(month), d(day), hh(hour), mm(minute), ss(second) {}
-  std::int_least64_t y;
-  std::int_least8_t m;
-  std::int_least8_t d;
-  std::int_least8_t hh;
-  std::int_least8_t mm;
-  std::int_least8_t ss;
-};
-
-struct second_tag {};
-struct minute_tag : second_tag {};
-struct hour_tag : minute_tag {};
-struct day_tag : hour_tag {};
-struct month_tag : day_tag {};
-struct year_tag : month_tag {};
-
-////////////////////////////////////////////////////////////////////////
-
-// Field normalization (without avoidable overflow).
-
-namespace impl {
-
-CONSTEXPR_F bool is_leap_year(year_t y) noexcept {
-  return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
-}
-CONSTEXPR_F int year_index(year_t y, month_t m) noexcept {
-  return (static_cast<int>((y + (m > 2)) % 400) + 400) % 400;
-}
-CONSTEXPR_F int days_per_century(year_t y, month_t m) noexcept {
-  const int yi = year_index(y, m);
-  return 36524 + (yi == 0 || yi > 300);
-}
-CONSTEXPR_F int days_per_4years(year_t y, month_t m) noexcept {
-  const int yi = year_index(y, m);
-  return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96);
-}
-CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept {
-  return is_leap_year(y + (m > 2)) ? 366 : 365;
-}
-CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept {
-  CONSTEXPR_D int k_days_per_month[1 + 12] = {
-      -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31  // non leap year
-  };
-  return k_days_per_month[m] + (m == 2 && is_leap_year(y));
-}
-
-CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh,
-                         minute_t mm, second_t ss) noexcept {
-  year_t ey = y % 400;
-  const year_t oey = ey;
-  ey += (cd / 146097) * 400;
-  cd %= 146097;
-  if (cd < 0) {
-    ey -= 400;
-    cd += 146097;
-  }
-  ey += (d / 146097) * 400;
-  d = d % 146097 + cd;
-  if (d > 0) {
-    if (d > 146097) {
-      ey += 400;
-      d -= 146097;
-    }
-  } else {
-    if (d > -365) {
-      // We often hit the previous year when stepping a civil time backwards,
-      // so special case it to avoid counting up by 100/4/1-year chunks.
-      ey -= 1;
-      d += days_per_year(ey, m);
-    } else {
-      ey -= 400;
-      d += 146097;
-    }
-  }
-  if (d > 365) {
-    for (;;) {
-      int n = days_per_century(ey, m);
-      if (d <= n) break;
-      d -= n;
-      ey += 100;
-    }
-    for (;;) {
-      int n = days_per_4years(ey, m);
-      if (d <= n) break;
-      d -= n;
-      ey += 4;
-    }
-    for (;;) {
-      int n = days_per_year(ey, m);
-      if (d <= n) break;
-      d -= n;
-      ++ey;
-    }
-  }
-  if (d > 28) {
-    for (;;) {
-      int n = days_per_month(ey, m);
-      if (d <= n) break;
-      d -= n;
-      if (++m > 12) {
-        ++ey;
-        m = 1;
-      }
-    }
-  }
-  return fields(y + (ey - oey), m, static_cast<day_t>(d), hh, mm, ss);
-}
-CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh,
-                         minute_t mm, second_t ss) noexcept {
-  if (m != 12) {
-    y += m / 12;
-    m %= 12;
-    if (m <= 0) {
-      y -= 1;
-      m += 12;
-    }
-  }
-  return n_day(y, static_cast<month_t>(m), d, cd, hh, mm, ss);
-}
-CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, diff_t hh,
-                          minute_t mm, second_t ss) noexcept {
-  cd += hh / 24;
-  hh %= 24;
-  if (hh < 0) {
-    cd -= 1;
-    hh += 24;
-  }
-  return n_mon(y, m, d, cd, static_cast<hour_t>(hh), mm, ss);
-}
-CONSTEXPR_F fields n_min(year_t y, diff_t m, diff_t d, diff_t hh, diff_t ch,
-                         diff_t mm, second_t ss) noexcept {
-  ch += mm / 60;
-  mm %= 60;
-  if (mm < 0) {
-    ch -= 1;
-    mm += 60;
-  }
-  return n_hour(y, m, d, hh / 24 + ch / 24, hh % 24 + ch % 24,
-                static_cast<minute_t>(mm), ss);
-}
-CONSTEXPR_F fields n_sec(year_t y, diff_t m, diff_t d, diff_t hh, diff_t mm,
-                         diff_t ss) noexcept {
-  // Optimization for when (non-constexpr) fields are already normalized.
-  if (0 <= ss && ss < 60) {
-    const second_t nss = static_cast<second_t>(ss);
-    if (0 <= mm && mm < 60) {
-      const minute_t nmm = static_cast<minute_t>(mm);
-      if (0 <= hh && hh < 24) {
-        const hour_t nhh = static_cast<hour_t>(hh);
-        if (1 <= d && d <= 28 && 1 <= m && m <= 12) {
-          const day_t nd = static_cast<day_t>(d);
-          const month_t nm = static_cast<month_t>(m);
-          return fields(y, nm, nd, nhh, nmm, nss);
-        }
-        return n_mon(y, m, d, 0, nhh, nmm, nss);
-      }
-      return n_hour(y, m, d, hh / 24, hh % 24, nmm, nss);
-    }
-    return n_min(y, m, d, hh, mm / 60, mm % 60, nss);
-  }
-  diff_t cm = ss / 60;
-  ss %= 60;
-  if (ss < 0) {
-    cm -= 1;
-    ss += 60;
-  }
-  return n_min(y, m, d, hh, mm / 60 + cm / 60, mm % 60 + cm % 60,
-               static_cast<second_t>(ss));
-}
-
-}  // namespace impl
-
-////////////////////////////////////////////////////////////////////////
-
-// Increments the indicated (normalized) field by "n".
-CONSTEXPR_F fields step(second_tag, fields f, diff_t n) noexcept {
-  return impl::n_sec(f.y, f.m, f.d, f.hh, f.mm + n / 60, f.ss + n % 60);
-}
-CONSTEXPR_F fields step(minute_tag, fields f, diff_t n) noexcept {
-  return impl::n_min(f.y, f.m, f.d, f.hh + n / 60, 0, f.mm + n % 60, f.ss);
-}
-CONSTEXPR_F fields step(hour_tag, fields f, diff_t n) noexcept {
-  return impl::n_hour(f.y, f.m, f.d + n / 24, 0, f.hh + n % 24, f.mm, f.ss);
-}
-CONSTEXPR_F fields step(day_tag, fields f, diff_t n) noexcept {
-  return impl::n_day(f.y, f.m, f.d, n, f.hh, f.mm, f.ss);
-}
-CONSTEXPR_F fields step(month_tag, fields f, diff_t n) noexcept {
-  return impl::n_mon(f.y + n / 12, f.m + n % 12, f.d, 0, f.hh, f.mm, f.ss);
-}
-CONSTEXPR_F fields step(year_tag, fields f, diff_t n) noexcept {
-  return fields(f.y + n, f.m, f.d, f.hh, f.mm, f.ss);
-}
-
-////////////////////////////////////////////////////////////////////////
-
-namespace impl {
-
-// Returns (v * f + a) but avoiding intermediate overflow when possible.
-CONSTEXPR_F diff_t scale_add(diff_t v, diff_t f, diff_t a) noexcept {
-  return (v < 0) ? ((v + 1) * f + a) - f : ((v - 1) * f + a) + f;
-}
-
-// Map a (normalized) Y/M/D to the number of days before/after 1970-01-01.
-// Probably overflows for years outside [-292277022656:292277026595].
-CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept {
-  const diff_t eyear = (m <= 2) ? y - 1 : y;
-  const diff_t era = (eyear >= 0 ? eyear : eyear - 399) / 400;
-  const diff_t yoe = eyear - era * 400;
-  const diff_t doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
-  const diff_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
-  return era * 146097 + doe - 719468;
-}
-
-// Returns the difference in days between two normalized Y-M-D tuples.
-// ymd_ord() will encounter integer overflow given extreme year values,
-// yet the difference between two such extreme values may actually be
-// small, so we take a little care to avoid overflow when possible by
-// exploiting the 146097-day cycle.
-CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, year_t y2,
-                                  month_t m2, day_t d2) noexcept {
-  const diff_t a_c4_off = y1 % 400;
-  const diff_t b_c4_off = y2 % 400;
-  diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off);
-  diff_t delta = ymd_ord(a_c4_off, m1, d1) - ymd_ord(b_c4_off, m2, d2);
-  if (c4_diff > 0 && delta < 0) {
-    delta += 2 * 146097;
-    c4_diff -= 2 * 400;
-  } else if (c4_diff < 0 && delta > 0) {
-    delta -= 2 * 146097;
-    c4_diff += 2 * 400;
-  }
-  return (c4_diff / 400 * 146097) + delta;
-}
-
-}  // namespace impl
-
-// Returns the difference between fields structs using the indicated unit.
-CONSTEXPR_F diff_t difference(year_tag, fields f1, fields f2) noexcept {
-  return f1.y - f2.y;
-}
-CONSTEXPR_F diff_t difference(month_tag, fields f1, fields f2) noexcept {
-  return impl::scale_add(difference(year_tag{}, f1, f2), 12, (f1.m - f2.m));
-}
-CONSTEXPR_F diff_t difference(day_tag, fields f1, fields f2) noexcept {
-  return impl::day_difference(f1.y, f1.m, f1.d, f2.y, f2.m, f2.d);
-}
-CONSTEXPR_F diff_t difference(hour_tag, fields f1, fields f2) noexcept {
-  return impl::scale_add(difference(day_tag{}, f1, f2), 24, (f1.hh - f2.hh));
-}
-CONSTEXPR_F diff_t difference(minute_tag, fields f1, fields f2) noexcept {
-  return impl::scale_add(difference(hour_tag{}, f1, f2), 60, (f1.mm - f2.mm));
-}
-CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept {
-  return impl::scale_add(difference(minute_tag{}, f1, f2), 60, f1.ss - f2.ss);
-}
-
-////////////////////////////////////////////////////////////////////////
-
-// Aligns the (normalized) fields struct to the indicated field.
-CONSTEXPR_F fields align(second_tag, fields f) noexcept { return f; }
-CONSTEXPR_F fields align(minute_tag, fields f) noexcept {
-  return fields{f.y, f.m, f.d, f.hh, f.mm, 0};
-}
-CONSTEXPR_F fields align(hour_tag, fields f) noexcept {
-  return fields{f.y, f.m, f.d, f.hh, 0, 0};
-}
-CONSTEXPR_F fields align(day_tag, fields f) noexcept {
-  return fields{f.y, f.m, f.d, 0, 0, 0};
-}
-CONSTEXPR_F fields align(month_tag, fields f) noexcept {
-  return fields{f.y, f.m, 1, 0, 0, 0};
-}
-CONSTEXPR_F fields align(year_tag, fields f) noexcept {
-  return fields{f.y, 1, 1, 0, 0, 0};
-}
-
-////////////////////////////////////////////////////////////////////////
-
-namespace impl {
-
-template <typename H>
-H AbslHashValueImpl(second_tag, H h, fields f) {
-  return H::combine(std::move(h), f.y, f.m, f.d, f.hh, f.mm, f.ss);
-}
-template <typename H>
-H AbslHashValueImpl(minute_tag, H h, fields f) {
-  return H::combine(std::move(h), f.y, f.m, f.d, f.hh, f.mm);
-}
-template <typename H>
-H AbslHashValueImpl(hour_tag, H h, fields f) {
-  return H::combine(std::move(h), f.y, f.m, f.d, f.hh);
-}
-template <typename H>
-H AbslHashValueImpl(day_tag, H h, fields f) {
-  return H::combine(std::move(h), f.y, f.m, f.d);
-}
-template <typename H>
-H AbslHashValueImpl(month_tag, H h, fields f) {
-  return H::combine(std::move(h), f.y, f.m);
-}
-template <typename H>
-H AbslHashValueImpl(year_tag, H h, fields f) {
-  return H::combine(std::move(h), f.y);
-}
-
-}  // namespace impl
-
-////////////////////////////////////////////////////////////////////////
-
-template <typename T>
-class civil_time {
- public:
-  explicit CONSTEXPR_M civil_time(year_t y, diff_t m = 1, diff_t d = 1,
-                                  diff_t hh = 0, diff_t mm = 0,
-                                  diff_t ss = 0) noexcept
-      : civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {}
-
-  CONSTEXPR_M civil_time() noexcept : f_{1970, 1, 1, 0, 0, 0} {}
-  civil_time(const civil_time&) = default;
-  civil_time& operator=(const civil_time&) = default;
-
-  // Conversion between civil times of different alignment. Conversion to
-  // a more precise alignment is allowed implicitly (e.g., day -> hour),
-  // but conversion where information is discarded must be explicit
-  // (e.g., second -> minute).
-  template <typename U, typename S>
-  using preserves_data =
-      typename std::enable_if<std::is_base_of<U, S>::value>::type;
-  template <typename U>
-  CONSTEXPR_M civil_time(const civil_time<U>& ct,
-                         preserves_data<T, U>* = nullptr) noexcept
-      : civil_time(ct.f_) {}
-  template <typename U>
-  explicit CONSTEXPR_M civil_time(const civil_time<U>& ct,
-                                  preserves_data<U, T>* = nullptr) noexcept
-      : civil_time(ct.f_) {}
-
-  // Factories for the maximum/minimum representable civil_time.
-  static CONSTEXPR_F civil_time(max)() {
-    const auto max_year = (std::numeric_limits<std::int_least64_t>::max)();
-    return civil_time(max_year, 12, 31, 23, 59, 59);
-  }
-  static CONSTEXPR_F civil_time(min)() {
-    const auto min_year = (std::numeric_limits<std::int_least64_t>::min)();
-    return civil_time(min_year, 1, 1, 0, 0, 0);
-  }
-
-  // Field accessors.  Note: All but year() return an int.
-  CONSTEXPR_M year_t year() const noexcept { return f_.y; }
-  CONSTEXPR_M int month() const noexcept { return f_.m; }
-  CONSTEXPR_M int day() const noexcept { return f_.d; }
-  CONSTEXPR_M int hour() const noexcept { return f_.hh; }
-  CONSTEXPR_M int minute() const noexcept { return f_.mm; }
-  CONSTEXPR_M int second() const noexcept { return f_.ss; }
-
-  // Assigning arithmetic.
-  CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept {
-    return *this = *this + n;
-  }
-  CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept {
-    return *this = *this - n;
-  }
-  CONSTEXPR_M civil_time& operator++() noexcept { return *this += 1; }
-  CONSTEXPR_M civil_time operator++(int) noexcept {
-    const civil_time a = *this;
-    ++*this;
-    return a;
-  }
-  CONSTEXPR_M civil_time& operator--() noexcept { return *this -= 1; }
-  CONSTEXPR_M civil_time operator--(int) noexcept {
-    const civil_time a = *this;
-    --*this;
-    return a;
-  }
-
-  // Binary arithmetic operators.
-  friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept {
-    return civil_time(step(T{}, a.f_, n));
-  }
-  friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept {
-    return a + n;
-  }
-  friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept {
-    return n != (std::numeric_limits<diff_t>::min)()
-               ? civil_time(step(T{}, a.f_, -n))
-               : civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1));
-  }
-  friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept {
-    return difference(T{}, lhs.f_, rhs.f_);
-  }
-
-  template <typename H>
-  friend H AbslHashValue(H h, civil_time a) {
-    return impl::AbslHashValueImpl(T{}, std::move(h), a.f_);
-  }
-
- private:
-  // All instantiations of this template are allowed to call the following
-  // private constructor and access the private fields member.
-  template <typename U>
-  friend class civil_time;
-
-  // The designated constructor that all others eventually call.
-  explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {}
-
-  fields f_;
-};
-
-// Disallows difference between differently aligned types.
-// auto n = civil_day(...) - civil_hour(...);  // would be confusing.
-template <typename T, typename U>
-CONSTEXPR_F diff_t operator-(civil_time<T>, civil_time<U>) = delete;
-
-using civil_year = civil_time<year_tag>;
-using civil_month = civil_time<month_tag>;
-using civil_day = civil_time<day_tag>;
-using civil_hour = civil_time<hour_tag>;
-using civil_minute = civil_time<minute_tag>;
-using civil_second = civil_time<second_tag>;
-
-////////////////////////////////////////////////////////////////////////
-
-// Relational operators that work with differently aligned objects.
-// Always compares all six fields.
-template <typename T1, typename T2>
-CONSTEXPR_F bool operator<(const civil_time<T1>& lhs,
-                           const civil_time<T2>& rhs) noexcept {
-  return (
-      lhs.year() < rhs.year() ||
-      (lhs.year() == rhs.year() &&
-       (lhs.month() < rhs.month() ||
-        (lhs.month() == rhs.month() &&
-         (lhs.day() < rhs.day() || (lhs.day() == rhs.day() &&
-                                    (lhs.hour() < rhs.hour() ||
-                                     (lhs.hour() == rhs.hour() &&
-                                      (lhs.minute() < rhs.minute() ||
-                                       (lhs.minute() == rhs.minute() &&
-                                        (lhs.second() < rhs.second())))))))))));
-}
-template <typename T1, typename T2>
-CONSTEXPR_F bool operator<=(const civil_time<T1>& lhs,
-                            const civil_time<T2>& rhs) noexcept {
-  return !(rhs < lhs);
-}
-template <typename T1, typename T2>
-CONSTEXPR_F bool operator>=(const civil_time<T1>& lhs,
-                            const civil_time<T2>& rhs) noexcept {
-  return !(lhs < rhs);
-}
-template <typename T1, typename T2>
-CONSTEXPR_F bool operator>(const civil_time<T1>& lhs,
-                           const civil_time<T2>& rhs) noexcept {
-  return rhs < lhs;
-}
-template <typename T1, typename T2>
-CONSTEXPR_F bool operator==(const civil_time<T1>& lhs,
-                            const civil_time<T2>& rhs) noexcept {
-  return lhs.year() == rhs.year() && lhs.month() == rhs.month() &&
-         lhs.day() == rhs.day() && lhs.hour() == rhs.hour() &&
-         lhs.minute() == rhs.minute() && lhs.second() == rhs.second();
-}
-template <typename T1, typename T2>
-CONSTEXPR_F bool operator!=(const civil_time<T1>& lhs,
-                            const civil_time<T2>& rhs) noexcept {
-  return !(lhs == rhs);
-}
-
-////////////////////////////////////////////////////////////////////////
-
-enum class weekday {
-  monday,
-  tuesday,
-  wednesday,
-  thursday,
-  friday,
-  saturday,
-  sunday,
-};
-
-CONSTEXPR_F weekday get_weekday(const civil_second& cs) noexcept {
-  CONSTEXPR_D weekday k_weekday_by_mon_off[13] = {
-      weekday::monday,    weekday::tuesday,  weekday::wednesday,
-      weekday::thursday,  weekday::friday,   weekday::saturday,
-      weekday::sunday,    weekday::monday,   weekday::tuesday,
-      weekday::wednesday, weekday::thursday, weekday::friday,
-      weekday::saturday,
-  };
-  CONSTEXPR_D int k_weekday_offsets[1 + 12] = {
-      -1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4,
-  };
-  year_t wd = 2400 + (cs.year() % 400) - (cs.month() < 3);
-  wd += wd / 4 - wd / 100 + wd / 400;
-  wd += k_weekday_offsets[cs.month()] + cs.day();
-  return k_weekday_by_mon_off[wd % 7 + 6];
-}
-
-////////////////////////////////////////////////////////////////////////
-
-CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept {
-  CONSTEXPR_D weekday k_weekdays_forw[14] = {
-      weekday::monday,    weekday::tuesday,  weekday::wednesday,
-      weekday::thursday,  weekday::friday,   weekday::saturday,
-      weekday::sunday,    weekday::monday,   weekday::tuesday,
-      weekday::wednesday, weekday::thursday, weekday::friday,
-      weekday::saturday,  weekday::sunday,
-  };
-  weekday base = get_weekday(cd);
-  for (int i = 0;; ++i) {
-    if (base == k_weekdays_forw[i]) {
-      for (int j = i + 1;; ++j) {
-        if (wd == k_weekdays_forw[j]) {
-          return cd + (j - i);
-        }
-      }
-    }
-  }
-}
-
-CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept {
-  CONSTEXPR_D weekday k_weekdays_back[14] = {
-      weekday::sunday,   weekday::saturday,  weekday::friday,
-      weekday::thursday, weekday::wednesday, weekday::tuesday,
-      weekday::monday,   weekday::sunday,    weekday::saturday,
-      weekday::friday,   weekday::thursday,  weekday::wednesday,
-      weekday::tuesday,  weekday::monday,
-  };
-  weekday base = get_weekday(cd);
-  for (int i = 0;; ++i) {
-    if (base == k_weekdays_back[i]) {
-      for (int j = i + 1;; ++j) {
-        if (wd == k_weekdays_back[j]) {
-          return cd - (j - i);
-        }
-      }
-    }
-  }
-}
-
-CONSTEXPR_F int get_yearday(const civil_second& cs) noexcept {
-  CONSTEXPR_D int k_month_offsets[1 + 12] = {
-      -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
-  };
-  const int feb29 = (cs.month() > 2 && impl::is_leap_year(cs.year()));
-  return k_month_offsets[cs.month()] + feb29 + cs.day();
-}
-
-////////////////////////////////////////////////////////////////////////
-
-std::ostream& operator<<(std::ostream& os, const civil_year& y);
-std::ostream& operator<<(std::ostream& os, const civil_month& m);
-std::ostream& operator<<(std::ostream& os, const civil_day& d);
-std::ostream& operator<<(std::ostream& os, const civil_hour& h);
-std::ostream& operator<<(std::ostream& os, const civil_minute& m);
-std::ostream& operator<<(std::ostream& os, const civil_second& s);
-std::ostream& operator<<(std::ostream& os, weekday wd);
-
-}  // namespace detail
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#undef CONSTEXPR_M
-#undef CONSTEXPR_F
-#undef CONSTEXPR_D
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/time_zone.h b/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/time_zone.h
deleted file mode 100644
index 5562a37bc8..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/time_zone.h
+++ /dev/null
@@ -1,386 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-// A library for translating between absolute times (represented by
-// std::chrono::time_points of the std::chrono::system_clock) and civil
-// times (represented by cctz::civil_second) using the rules defined by
-// a time zone (cctz::time_zone).
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
-#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
-
-#include <chrono>
-#include <cstdint>
-#include <string>
-#include <utility>
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// Convenience aliases. Not intended as public API points.
-template <typename D>
-using time_point = std::chrono::time_point<std::chrono::system_clock, D>;
-using seconds = std::chrono::duration<std::int_fast64_t>;
-using sys_seconds = seconds;  // Deprecated.  Use cctz::seconds instead.
-
-namespace detail {
-template <typename D>
-inline std::pair<time_point<seconds>, D> split_seconds(
-    const time_point<D>& tp) {
-  auto sec = std::chrono::time_point_cast<seconds>(tp);
-  auto sub = tp - sec;
-  if (sub.count() < 0) {
-    sec -= seconds(1);
-    sub += seconds(1);
-  }
-  return {sec, std::chrono::duration_cast<D>(sub)};
-}
-inline std::pair<time_point<seconds>, seconds> split_seconds(
-    const time_point<seconds>& tp) {
-  return {tp, seconds::zero()};
-}
-}  // namespace detail
-
-// cctz::time_zone is an opaque, small, value-type class representing a
-// geo-political region within which particular rules are used for mapping
-// between absolute and civil times. Time zones are named using the TZ
-// identifiers from the IANA Time Zone Database, such as "America/Los_Angeles"
-// or "Australia/Sydney". Time zones are created from factory functions such
-// as load_time_zone(). Note: strings like "PST" and "EDT" are not valid TZ
-// identifiers.
-//
-// Example:
-//   cctz::time_zone utc = cctz::utc_time_zone();
-//   cctz::time_zone pst = cctz::fixed_time_zone(std::chrono::hours(-8));
-//   cctz::time_zone loc = cctz::local_time_zone();
-//   cctz::time_zone lax;
-//   if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
-//
-// See also:
-// - http://www.iana.org/time-zones
-// - https://en.wikipedia.org/wiki/Zoneinfo
-class time_zone {
- public:
-  time_zone() : time_zone(nullptr) {}  // Equivalent to UTC
-  time_zone(const time_zone&) = default;
-  time_zone& operator=(const time_zone&) = default;
-
-  std::string name() const;
-
-  // An absolute_lookup represents the civil time (cctz::civil_second) within
-  // this time_zone at the given absolute time (time_point). There are
-  // additionally a few other fields that may be useful when working with
-  // older APIs, such as std::tm.
-  //
-  // Example:
-  //   const cctz::time_zone tz = ...
-  //   const auto tp = std::chrono::system_clock::now();
-  //   const cctz::time_zone::absolute_lookup al = tz.lookup(tp);
-  struct absolute_lookup {
-    civil_second cs;
-    // Note: The following fields exist for backward compatibility with older
-    // APIs. Accessing these fields directly is a sign of imprudent logic in
-    // the calling code. Modern time-related code should only access this data
-    // indirectly by way of cctz::format().
-    int offset;        // civil seconds east of UTC
-    bool is_dst;       // is offset non-standard?
-    const char* abbr;  // time-zone abbreviation (e.g., "PST")
-  };
-  absolute_lookup lookup(const time_point<seconds>& tp) const;
-  template <typename D>
-  absolute_lookup lookup(const time_point<D>& tp) const {
-    return lookup(detail::split_seconds(tp).first);
-  }
-
-  // A civil_lookup represents the absolute time(s) (time_point) that
-  // correspond to the given civil time (cctz::civil_second) within this
-  // time_zone. Usually the given civil time represents a unique instant
-  // in time, in which case the conversion is unambiguous. However,
-  // within this time zone, the given civil time may be skipped (e.g.,
-  // during a positive UTC offset shift), or repeated (e.g., during a
-  // negative UTC offset shift). To account for these possibilities,
-  // civil_lookup is richer than just a single time_point.
-  //
-  // In all cases the civil_lookup::kind enum will indicate the nature
-  // of the given civil-time argument, and the pre, trans, and post
-  // members will give the absolute time answers using the pre-transition
-  // offset, the transition point itself, and the post-transition offset,
-  // respectively (all three times are equal if kind == UNIQUE). If any
-  // of these three absolute times is outside the representable range of a
-  // time_point<seconds> the field is set to its maximum/minimum value.
-  //
-  // Example:
-  //   cctz::time_zone lax;
-  //   if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
-  //
-  //   // A unique civil time.
-  //   auto jan01 = lax.lookup(cctz::civil_second(2011, 1, 1, 0, 0, 0));
-  //   // jan01.kind == cctz::time_zone::civil_lookup::UNIQUE
-  //   // jan01.pre    is 2011/01/01 00:00:00 -0800
-  //   // jan01.trans  is 2011/01/01 00:00:00 -0800
-  //   // jan01.post   is 2011/01/01 00:00:00 -0800
-  //
-  //   // A Spring DST transition, when there is a gap in civil time.
-  //   auto mar13 = lax.lookup(cctz::civil_second(2011, 3, 13, 2, 15, 0));
-  //   // mar13.kind == cctz::time_zone::civil_lookup::SKIPPED
-  //   // mar13.pre   is 2011/03/13 03:15:00 -0700
-  //   // mar13.trans is 2011/03/13 03:00:00 -0700
-  //   // mar13.post  is 2011/03/13 01:15:00 -0800
-  //
-  //   // A Fall DST transition, when civil times are repeated.
-  //   auto nov06 = lax.lookup(cctz::civil_second(2011, 11, 6, 1, 15, 0));
-  //   // nov06.kind == cctz::time_zone::civil_lookup::REPEATED
-  //   // nov06.pre   is 2011/11/06 01:15:00 -0700
-  //   // nov06.trans is 2011/11/06 01:00:00 -0800
-  //   // nov06.post  is 2011/11/06 01:15:00 -0800
-  struct civil_lookup {
-    enum civil_kind {
-      UNIQUE,    // the civil time was singular (pre == trans == post)
-      SKIPPED,   // the civil time did not exist (pre >= trans > post)
-      REPEATED,  // the civil time was ambiguous (pre < trans <= post)
-    } kind;
-    time_point<seconds> pre;    // uses the pre-transition offset
-    time_point<seconds> trans;  // instant of civil-offset change
-    time_point<seconds> post;   // uses the post-transition offset
-  };
-  civil_lookup lookup(const civil_second& cs) const;
-
-  // Finds the time of the next/previous offset change in this time zone.
-  //
-  // By definition, next_transition(tp, &trans) returns false when tp has
-  // its maximum value, and prev_transition(tp, &trans) returns false
-  // when tp has its minimum value. If the zone has no transitions, the
-  // result will also be false no matter what the argument.
-  //
-  // Otherwise, when tp has its minimum value, next_transition(tp, &trans)
-  // returns true and sets trans to the first recorded transition. Chains
-  // of calls to next_transition()/prev_transition() will eventually return
-  // false, but it is unspecified exactly when next_transition(tp, &trans)
-  // jumps to false, or what time is set by prev_transition(tp, &trans) for
-  // a very distant tp.
-  //
-  // Note: Enumeration of time-zone transitions is for informational purposes
-  // only. Modern time-related code should not care about when offset changes
-  // occur.
-  //
-  // Example:
-  //   cctz::time_zone nyc;
-  //   if (!cctz::load_time_zone("America/New_York", &nyc)) { ... }
-  //   const auto now = std::chrono::system_clock::now();
-  //   auto tp = cctz::time_point<cctz::seconds>::min();
-  //   cctz::time_zone::civil_transition trans;
-  //   while (tp <= now && nyc.next_transition(tp, &trans)) {
-  //     // transition: trans.from -> trans.to
-  //     tp = nyc.lookup(trans.to).trans;
-  //   }
-  struct civil_transition {
-    civil_second from;  // the civil time we jump from
-    civil_second to;    // the civil time we jump to
-  };
-  bool next_transition(const time_point<seconds>& tp,
-                       civil_transition* trans) const;
-  template <typename D>
-  bool next_transition(const time_point<D>& tp, civil_transition* trans) const {
-    return next_transition(detail::split_seconds(tp).first, trans);
-  }
-  bool prev_transition(const time_point<seconds>& tp,
-                       civil_transition* trans) const;
-  template <typename D>
-  bool prev_transition(const time_point<D>& tp, civil_transition* trans) const {
-    return prev_transition(detail::split_seconds(tp).first, trans);
-  }
-
-  // version() and description() provide additional information about the
-  // time zone. The content of each of the returned strings is unspecified,
-  // however, when the IANA Time Zone Database is the underlying data source
-  // the version() string will be in the familar form (e.g, "2018e") or
-  // empty when unavailable.
-  //
-  // Note: These functions are for informational or testing purposes only.
-  std::string version() const;  // empty when unknown
-  std::string description() const;
-
-  // Relational operators.
-  friend bool operator==(time_zone lhs, time_zone rhs) {
-    return &lhs.effective_impl() == &rhs.effective_impl();
-  }
-  friend bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); }
-
-  template <typename H>
-  friend H AbslHashValue(H h, time_zone tz) {
-    return H::combine(std::move(h), &tz.effective_impl());
-  }
-
-  class Impl;
-
- private:
-  explicit time_zone(const Impl* impl) : impl_(impl) {}
-  const Impl& effective_impl() const;  // handles implicit UTC
-  const Impl* impl_;
-};
-
-// Loads the named time zone. May perform I/O on the initial load.
-// If the name is invalid, or some other kind of error occurs, returns
-// false and "*tz" is set to the UTC time zone.
-bool load_time_zone(const std::string& name, time_zone* tz);
-
-// Returns a time_zone representing UTC. Cannot fail.
-time_zone utc_time_zone();
-
-// Returns a time zone that is a fixed offset (seconds east) from UTC.
-// Note: If the absolute value of the offset is greater than 24 hours
-// you'll get UTC (i.e., zero offset) instead.
-time_zone fixed_time_zone(const seconds& offset);
-
-// Returns a time zone representing the local time zone. Falls back to UTC.
-// Note: local_time_zone.name() may only be something like "localtime".
-time_zone local_time_zone();
-
-// Returns the civil time (cctz::civil_second) within the given time zone at
-// the given absolute time (time_point). Since the additional fields provided
-// by the time_zone::absolute_lookup struct should rarely be needed in modern
-// code, this convert() function is simpler and should be preferred.
-template <typename D>
-inline civil_second convert(const time_point<D>& tp, const time_zone& tz) {
-  return tz.lookup(tp).cs;
-}
-
-// Returns the absolute time (time_point) that corresponds to the given civil
-// time within the given time zone. If the civil time is not unique (i.e., if
-// it was either repeated or non-existent), then the returned time_point is
-// the best estimate that preserves relative order. That is, this function
-// guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz).
-inline time_point<seconds> convert(const civil_second& cs,
-                                   const time_zone& tz) {
-  const time_zone::civil_lookup cl = tz.lookup(cs);
-  if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans;
-  return cl.pre;
-}
-
-namespace detail {
-using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>;
-std::string format(const std::string&, const time_point<seconds>&,
-                   const femtoseconds&, const time_zone&);
-bool parse(const std::string&, const std::string&, const time_zone&,
-           time_point<seconds>*, femtoseconds*, std::string* err = nullptr);
-}  // namespace detail
-
-// Formats the given time_point in the given cctz::time_zone according to
-// the provided format string. Uses strftime()-like formatting options,
-// with the following extensions:
-//
-//   - %Ez  - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
-//   - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
-//   - %E#S - Seconds with # digits of fractional precision
-//   - %E*S - Seconds with full fractional precision (a literal '*')
-//   - %E#f - Fractional seconds with # digits of precision
-//   - %E*f - Fractional seconds with full precision (a literal '*')
-//   - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
-//   - %ET  - The RFC3339 "date-time" separator "T"
-//
-// Note that %E0S behaves like %S, and %E0f produces no characters. In
-// contrast %E*f always produces at least one digit, which may be '0'.
-//
-// Note that %Y produces as many characters as it takes to fully render the
-// year. A year outside of [-999:9999] when formatted with %E4Y will produce
-// more than four characters, just like %Y.
-//
-// Tip: Format strings should include the UTC offset (e.g., %z, %Ez, or %E*z)
-// so that the resulting string uniquely identifies an absolute time.
-//
-// Example:
-//   cctz::time_zone lax;
-//   if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
-//   auto tp = cctz::convert(cctz::civil_second(2013, 1, 2, 3, 4, 5), lax);
-//   std::string f = cctz::format("%H:%M:%S", tp, lax);  // "03:04:05"
-//   f = cctz::format("%H:%M:%E3S", tp, lax);            // "03:04:05.000"
-template <typename D>
-inline std::string format(const std::string& fmt, const time_point<D>& tp,
-                          const time_zone& tz) {
-  const auto p = detail::split_seconds(tp);
-  const auto n = std::chrono::duration_cast<detail::femtoseconds>(p.second);
-  return detail::format(fmt, p.first, n, tz);
-}
-
-// Parses an input string according to the provided format string and
-// returns the corresponding time_point. Uses strftime()-like formatting
-// options, with the same extensions as cctz::format(), but with the
-// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
-// and %E*z also accept the same inputs, which (along with %z) includes
-// 'z' and 'Z' as synonyms for +00:00.  %ET accepts either 'T' or 't'.
-//
-// %Y consumes as many numeric characters as it can, so the matching data
-// should always be terminated with a non-numeric. %E4Y always consumes
-// exactly four characters, including any sign.
-//
-// Unspecified fields are taken from the default date and time of ...
-//
-//   "1970-01-01 00:00:00.0 +0000"
-//
-// For example, parsing a string of "15:45" (%H:%M) will return a time_point
-// that represents "1970-01-01 15:45:00.0 +0000".
-//
-// Note that parse() returns time instants, so it makes most sense to parse
-// fully-specified date/time strings that include a UTC offset (%z, %Ez, or
-// %E*z).
-//
-// Note also that parse() only heeds the fields year, month, day, hour,
-// minute, (fractional) second, and UTC offset. Other fields, like weekday (%a
-// or %A), while parsed for syntactic validity, are ignored in the conversion.
-//
-// Date and time fields that are out-of-range will be treated as errors rather
-// than normalizing them like cctz::civil_second() would do. For example, it
-// is an error to parse the date "Oct 32, 2013" because 32 is out of range.
-//
-// A second of ":60" is normalized to ":00" of the following minute with
-// fractional seconds discarded. The following table shows how the given
-// seconds and subseconds will be parsed:
-//
-//   "59.x" -> 59.x  // exact
-//   "60.x" -> 00.0  // normalized
-//   "00.x" -> 00.x  // exact
-//
-// Errors are indicated by returning false.
-//
-// Example:
-//   const cctz::time_zone tz = ...
-//   std::chrono::system_clock::time_point tp;
-//   if (cctz::parse("%Y-%m-%d", "2015-10-09", tz, &tp)) {
-//     ...
-//   }
-template <typename D>
-inline bool parse(const std::string& fmt, const std::string& input,
-                  const time_zone& tz, time_point<D>* tpp) {
-  time_point<seconds> sec;
-  detail::femtoseconds fs;
-  const bool b = detail::parse(fmt, input, tz, &sec, &fs);
-  if (b) {
-    // TODO: Return false if unrepresentable as a time_point<D>.
-    *tpp = std::chrono::time_point_cast<D>(sec);
-    *tpp += std::chrono::duration_cast<D>(fs);
-  }
-  return b;
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h b/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h
deleted file mode 100644
index 012eb4ec30..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
-#define ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
-
-#include <cstddef>
-#include <functional>
-#include <memory>
-#include <string>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// A stdio-like interface for providing zoneinfo data for a particular zone.
-class ZoneInfoSource {
- public:
-  virtual ~ZoneInfoSource();
-
-  virtual std::size_t Read(void* ptr, std::size_t size) = 0;  // like fread()
-  virtual int Skip(std::size_t offset) = 0;                   // like fseek()
-
-  // Until the zoneinfo data supports versioning information, we provide
-  // a way for a ZoneInfoSource to indicate it out-of-band.  The default
-  // implementation returns an empty string.
-  virtual std::string Version() const;
-};
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz_extension {
-
-// A function-pointer type for a factory that returns a ZoneInfoSource
-// given the name of a time zone and a fallback factory.  Returns null
-// when the data for the named zone cannot be found.
-using ZoneInfoSourceFactory =
-    std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> (*)(
-        const std::string&,
-        const std::function<std::unique_ptr<
-            absl::time_internal::cctz::ZoneInfoSource>(const std::string&)>&);
-
-// The user can control the mapping of zone names to zoneinfo data by
-// providing a definition for cctz_extension::zone_info_source_factory.
-// For example, given functions my_factory() and my_other_factory() that
-// can return a ZoneInfoSource for a named zone, we could inject them into
-// cctz::load_time_zone() with:
-//
-//   namespace cctz_extension {
-//   namespace {
-//   std::unique_ptr<cctz::ZoneInfoSource> CustomFactory(
-//       const std::string& name,
-//       const std::function<std::unique_ptr<cctz::ZoneInfoSource>(
-//           const std::string& name)>& fallback_factory) {
-//     if (auto zip = my_factory(name)) return zip;
-//     if (auto zip = fallback_factory(name)) return zip;
-//     if (auto zip = my_other_factory(name)) return zip;
-//     return nullptr;
-//   }
-//   }  // namespace
-//   ZoneInfoSourceFactory zone_info_source_factory = CustomFactory;
-//   }  // namespace cctz_extension
-//
-// This might be used, say, to use zoneinfo data embedded in the program,
-// or read from a (possibly compressed) file archive, or both.
-//
-// cctz_extension::zone_info_source_factory() will be called:
-//   (1) from the same thread as the cctz::load_time_zone() call,
-//   (2) only once for any zone name, and
-//   (3) serially (i.e., no concurrent execution).
-//
-// The fallback factory obtains zoneinfo data by reading files in ${TZDIR},
-// and it is used automatically when no zone_info_source_factory definition
-// is linked into the program.
-extern ZoneInfoSourceFactory zone_info_source_factory;
-
-}  // namespace cctz_extension
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/cctz_benchmark.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/cctz_benchmark.cc
deleted file mode 100644
index 4e39188ff3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ /dev/null
@@ -1,1030 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include <algorithm>
-#include <cassert>
-#include <chrono>
-#include <ctime>
-#include <random>
-#include <string>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-#include "time_zone_impl.h"
-
-namespace {
-
-namespace cctz = absl::time_internal::cctz;
-
-void BM_Difference_Days(benchmark::State& state) {
-  const cctz::civil_day c(2014, 8, 22);
-  const cctz::civil_day epoch(1970, 1, 1);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(c - epoch);
-  }
-}
-BENCHMARK(BM_Difference_Days);
-
-void BM_Step_Days(benchmark::State& state) {
-  const cctz::civil_day kStart(2014, 8, 22);
-  cctz::civil_day c = kStart;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(++c);
-  }
-}
-BENCHMARK(BM_Step_Days);
-
-void BM_GetWeekday(benchmark::State& state) {
-  const cctz::civil_day c(2014, 8, 22);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(cctz::get_weekday(c));
-  }
-}
-BENCHMARK(BM_GetWeekday);
-
-void BM_NextWeekday(benchmark::State& state) {
-  const cctz::civil_day kStart(2014, 8, 22);
-  const cctz::civil_day kDays[7] = {
-      kStart + 0, kStart + 1, kStart + 2, kStart + 3,
-      kStart + 4, kStart + 5, kStart + 6,
-  };
-  const cctz::weekday kWeekdays[7] = {
-      cctz::weekday::monday,   cctz::weekday::tuesday, cctz::weekday::wednesday,
-      cctz::weekday::thursday, cctz::weekday::friday,  cctz::weekday::saturday,
-      cctz::weekday::sunday,
-  };
-  while (state.KeepRunningBatch(7 * 7)) {
-    for (const auto from : kDays) {
-      for (const auto to : kWeekdays) {
-        benchmark::DoNotOptimize(cctz::next_weekday(from, to));
-      }
-    }
-  }
-}
-BENCHMARK(BM_NextWeekday);
-
-void BM_PrevWeekday(benchmark::State& state) {
-  const cctz::civil_day kStart(2014, 8, 22);
-  const cctz::civil_day kDays[7] = {
-      kStart + 0, kStart + 1, kStart + 2, kStart + 3,
-      kStart + 4, kStart + 5, kStart + 6,
-  };
-  const cctz::weekday kWeekdays[7] = {
-      cctz::weekday::monday,   cctz::weekday::tuesday, cctz::weekday::wednesday,
-      cctz::weekday::thursday, cctz::weekday::friday,  cctz::weekday::saturday,
-      cctz::weekday::sunday,
-  };
-  while (state.KeepRunningBatch(7 * 7)) {
-    for (const auto from : kDays) {
-      for (const auto to : kWeekdays) {
-        benchmark::DoNotOptimize(cctz::prev_weekday(from, to));
-      }
-    }
-  }
-}
-BENCHMARK(BM_PrevWeekday);
-
-const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
-const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
-
-const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
-const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
-
-// A list of known time-zone names.
-// TODO: Refactor with src/time_zone_lookup_test.cc.
-const char* const kTimeZoneNames[] = {"Africa/Abidjan",
-                                      "Africa/Accra",
-                                      "Africa/Addis_Ababa",
-                                      "Africa/Algiers",
-                                      "Africa/Asmara",
-                                      "Africa/Asmera",
-                                      "Africa/Bamako",
-                                      "Africa/Bangui",
-                                      "Africa/Banjul",
-                                      "Africa/Bissau",
-                                      "Africa/Blantyre",
-                                      "Africa/Brazzaville",
-                                      "Africa/Bujumbura",
-                                      "Africa/Cairo",
-                                      "Africa/Casablanca",
-                                      "Africa/Ceuta",
-                                      "Africa/Conakry",
-                                      "Africa/Dakar",
-                                      "Africa/Dar_es_Salaam",
-                                      "Africa/Djibouti",
-                                      "Africa/Douala",
-                                      "Africa/El_Aaiun",
-                                      "Africa/Freetown",
-                                      "Africa/Gaborone",
-                                      "Africa/Harare",
-                                      "Africa/Johannesburg",
-                                      "Africa/Juba",
-                                      "Africa/Kampala",
-                                      "Africa/Khartoum",
-                                      "Africa/Kigali",
-                                      "Africa/Kinshasa",
-                                      "Africa/Lagos",
-                                      "Africa/Libreville",
-                                      "Africa/Lome",
-                                      "Africa/Luanda",
-                                      "Africa/Lubumbashi",
-                                      "Africa/Lusaka",
-                                      "Africa/Malabo",
-                                      "Africa/Maputo",
-                                      "Africa/Maseru",
-                                      "Africa/Mbabane",
-                                      "Africa/Mogadishu",
-                                      "Africa/Monrovia",
-                                      "Africa/Nairobi",
-                                      "Africa/Ndjamena",
-                                      "Africa/Niamey",
-                                      "Africa/Nouakchott",
-                                      "Africa/Ouagadougou",
-                                      "Africa/Porto-Novo",
-                                      "Africa/Sao_Tome",
-                                      "Africa/Timbuktu",
-                                      "Africa/Tripoli",
-                                      "Africa/Tunis",
-                                      "Africa/Windhoek",
-                                      "America/Adak",
-                                      "America/Anchorage",
-                                      "America/Anguilla",
-                                      "America/Antigua",
-                                      "America/Araguaina",
-                                      "America/Argentina/Buenos_Aires",
-                                      "America/Argentina/Catamarca",
-                                      "America/Argentina/ComodRivadavia",
-                                      "America/Argentina/Cordoba",
-                                      "America/Argentina/Jujuy",
-                                      "America/Argentina/La_Rioja",
-                                      "America/Argentina/Mendoza",
-                                      "America/Argentina/Rio_Gallegos",
-                                      "America/Argentina/Salta",
-                                      "America/Argentina/San_Juan",
-                                      "America/Argentina/San_Luis",
-                                      "America/Argentina/Tucuman",
-                                      "America/Argentina/Ushuaia",
-                                      "America/Aruba",
-                                      "America/Asuncion",
-                                      "America/Atikokan",
-                                      "America/Atka",
-                                      "America/Bahia",
-                                      "America/Bahia_Banderas",
-                                      "America/Barbados",
-                                      "America/Belem",
-                                      "America/Belize",
-                                      "America/Blanc-Sablon",
-                                      "America/Boa_Vista",
-                                      "America/Bogota",
-                                      "America/Boise",
-                                      "America/Buenos_Aires",
-                                      "America/Cambridge_Bay",
-                                      "America/Campo_Grande",
-                                      "America/Cancun",
-                                      "America/Caracas",
-                                      "America/Catamarca",
-                                      "America/Cayenne",
-                                      "America/Cayman",
-                                      "America/Chicago",
-                                      "America/Chihuahua",
-                                      "America/Coral_Harbour",
-                                      "America/Cordoba",
-                                      "America/Costa_Rica",
-                                      "America/Creston",
-                                      "America/Cuiaba",
-                                      "America/Curacao",
-                                      "America/Danmarkshavn",
-                                      "America/Dawson",
-                                      "America/Dawson_Creek",
-                                      "America/Denver",
-                                      "America/Detroit",
-                                      "America/Dominica",
-                                      "America/Edmonton",
-                                      "America/Eirunepe",
-                                      "America/El_Salvador",
-                                      "America/Ensenada",
-                                      "America/Fort_Nelson",
-                                      "America/Fort_Wayne",
-                                      "America/Fortaleza",
-                                      "America/Glace_Bay",
-                                      "America/Godthab",
-                                      "America/Goose_Bay",
-                                      "America/Grand_Turk",
-                                      "America/Grenada",
-                                      "America/Guadeloupe",
-                                      "America/Guatemala",
-                                      "America/Guayaquil",
-                                      "America/Guyana",
-                                      "America/Halifax",
-                                      "America/Havana",
-                                      "America/Hermosillo",
-                                      "America/Indiana/Indianapolis",
-                                      "America/Indiana/Knox",
-                                      "America/Indiana/Marengo",
-                                      "America/Indiana/Petersburg",
-                                      "America/Indiana/Tell_City",
-                                      "America/Indiana/Vevay",
-                                      "America/Indiana/Vincennes",
-                                      "America/Indiana/Winamac",
-                                      "America/Indianapolis",
-                                      "America/Inuvik",
-                                      "America/Iqaluit",
-                                      "America/Jamaica",
-                                      "America/Jujuy",
-                                      "America/Juneau",
-                                      "America/Kentucky/Louisville",
-                                      "America/Kentucky/Monticello",
-                                      "America/Knox_IN",
-                                      "America/Kralendijk",
-                                      "America/La_Paz",
-                                      "America/Lima",
-                                      "America/Los_Angeles",
-                                      "America/Louisville",
-                                      "America/Lower_Princes",
-                                      "America/Maceio",
-                                      "America/Managua",
-                                      "America/Manaus",
-                                      "America/Marigot",
-                                      "America/Martinique",
-                                      "America/Matamoros",
-                                      "America/Mazatlan",
-                                      "America/Mendoza",
-                                      "America/Menominee",
-                                      "America/Merida",
-                                      "America/Metlakatla",
-                                      "America/Mexico_City",
-                                      "America/Miquelon",
-                                      "America/Moncton",
-                                      "America/Monterrey",
-                                      "America/Montevideo",
-                                      "America/Montreal",
-                                      "America/Montserrat",
-                                      "America/Nassau",
-                                      "America/New_York",
-                                      "America/Nipigon",
-                                      "America/Nome",
-                                      "America/Noronha",
-                                      "America/North_Dakota/Beulah",
-                                      "America/North_Dakota/Center",
-                                      "America/North_Dakota/New_Salem",
-                                      "America/Nuuk",
-                                      "America/Ojinaga",
-                                      "America/Panama",
-                                      "America/Pangnirtung",
-                                      "America/Paramaribo",
-                                      "America/Phoenix",
-                                      "America/Port-au-Prince",
-                                      "America/Port_of_Spain",
-                                      "America/Porto_Acre",
-                                      "America/Porto_Velho",
-                                      "America/Puerto_Rico",
-                                      "America/Punta_Arenas",
-                                      "America/Rainy_River",
-                                      "America/Rankin_Inlet",
-                                      "America/Recife",
-                                      "America/Regina",
-                                      "America/Resolute",
-                                      "America/Rio_Branco",
-                                      "America/Rosario",
-                                      "America/Santa_Isabel",
-                                      "America/Santarem",
-                                      "America/Santiago",
-                                      "America/Santo_Domingo",
-                                      "America/Sao_Paulo",
-                                      "America/Scoresbysund",
-                                      "America/Shiprock",
-                                      "America/Sitka",
-                                      "America/St_Barthelemy",
-                                      "America/St_Johns",
-                                      "America/St_Kitts",
-                                      "America/St_Lucia",
-                                      "America/St_Thomas",
-                                      "America/St_Vincent",
-                                      "America/Swift_Current",
-                                      "America/Tegucigalpa",
-                                      "America/Thule",
-                                      "America/Thunder_Bay",
-                                      "America/Tijuana",
-                                      "America/Toronto",
-                                      "America/Tortola",
-                                      "America/Vancouver",
-                                      "America/Virgin",
-                                      "America/Whitehorse",
-                                      "America/Winnipeg",
-                                      "America/Yakutat",
-                                      "America/Yellowknife",
-                                      "Antarctica/Casey",
-                                      "Antarctica/Davis",
-                                      "Antarctica/DumontDUrville",
-                                      "Antarctica/Macquarie",
-                                      "Antarctica/Mawson",
-                                      "Antarctica/McMurdo",
-                                      "Antarctica/Palmer",
-                                      "Antarctica/Rothera",
-                                      "Antarctica/South_Pole",
-                                      "Antarctica/Syowa",
-                                      "Antarctica/Troll",
-                                      "Antarctica/Vostok",
-                                      "Arctic/Longyearbyen",
-                                      "Asia/Aden",
-                                      "Asia/Almaty",
-                                      "Asia/Amman",
-                                      "Asia/Anadyr",
-                                      "Asia/Aqtau",
-                                      "Asia/Aqtobe",
-                                      "Asia/Ashgabat",
-                                      "Asia/Ashkhabad",
-                                      "Asia/Atyrau",
-                                      "Asia/Baghdad",
-                                      "Asia/Bahrain",
-                                      "Asia/Baku",
-                                      "Asia/Bangkok",
-                                      "Asia/Barnaul",
-                                      "Asia/Beirut",
-                                      "Asia/Bishkek",
-                                      "Asia/Brunei",
-                                      "Asia/Calcutta",
-                                      "Asia/Chita",
-                                      "Asia/Choibalsan",
-                                      "Asia/Chongqing",
-                                      "Asia/Chungking",
-                                      "Asia/Colombo",
-                                      "Asia/Dacca",
-                                      "Asia/Damascus",
-                                      "Asia/Dhaka",
-                                      "Asia/Dili",
-                                      "Asia/Dubai",
-                                      "Asia/Dushanbe",
-                                      "Asia/Famagusta",
-                                      "Asia/Gaza",
-                                      "Asia/Harbin",
-                                      "Asia/Hebron",
-                                      "Asia/Ho_Chi_Minh",
-                                      "Asia/Hong_Kong",
-                                      "Asia/Hovd",
-                                      "Asia/Irkutsk",
-                                      "Asia/Istanbul",
-                                      "Asia/Jakarta",
-                                      "Asia/Jayapura",
-                                      "Asia/Jerusalem",
-                                      "Asia/Kabul",
-                                      "Asia/Kamchatka",
-                                      "Asia/Karachi",
-                                      "Asia/Kashgar",
-                                      "Asia/Kathmandu",
-                                      "Asia/Katmandu",
-                                      "Asia/Khandyga",
-                                      "Asia/Kolkata",
-                                      "Asia/Krasnoyarsk",
-                                      "Asia/Kuala_Lumpur",
-                                      "Asia/Kuching",
-                                      "Asia/Kuwait",
-                                      "Asia/Macao",
-                                      "Asia/Macau",
-                                      "Asia/Magadan",
-                                      "Asia/Makassar",
-                                      "Asia/Manila",
-                                      "Asia/Muscat",
-                                      "Asia/Nicosia",
-                                      "Asia/Novokuznetsk",
-                                      "Asia/Novosibirsk",
-                                      "Asia/Omsk",
-                                      "Asia/Oral",
-                                      "Asia/Phnom_Penh",
-                                      "Asia/Pontianak",
-                                      "Asia/Pyongyang",
-                                      "Asia/Qatar",
-                                      "Asia/Qostanay",
-                                      "Asia/Qyzylorda",
-                                      "Asia/Rangoon",
-                                      "Asia/Riyadh",
-                                      "Asia/Saigon",
-                                      "Asia/Sakhalin",
-                                      "Asia/Samarkand",
-                                      "Asia/Seoul",
-                                      "Asia/Shanghai",
-                                      "Asia/Singapore",
-                                      "Asia/Srednekolymsk",
-                                      "Asia/Taipei",
-                                      "Asia/Tashkent",
-                                      "Asia/Tbilisi",
-                                      "Asia/Tehran",
-                                      "Asia/Tel_Aviv",
-                                      "Asia/Thimbu",
-                                      "Asia/Thimphu",
-                                      "Asia/Tokyo",
-                                      "Asia/Tomsk",
-                                      "Asia/Ujung_Pandang",
-                                      "Asia/Ulaanbaatar",
-                                      "Asia/Ulan_Bator",
-                                      "Asia/Urumqi",
-                                      "Asia/Ust-Nera",
-                                      "Asia/Vientiane",
-                                      "Asia/Vladivostok",
-                                      "Asia/Yakutsk",
-                                      "Asia/Yangon",
-                                      "Asia/Yekaterinburg",
-                                      "Asia/Yerevan",
-                                      "Atlantic/Azores",
-                                      "Atlantic/Bermuda",
-                                      "Atlantic/Canary",
-                                      "Atlantic/Cape_Verde",
-                                      "Atlantic/Faeroe",
-                                      "Atlantic/Faroe",
-                                      "Atlantic/Jan_Mayen",
-                                      "Atlantic/Madeira",
-                                      "Atlantic/Reykjavik",
-                                      "Atlantic/South_Georgia",
-                                      "Atlantic/St_Helena",
-                                      "Atlantic/Stanley",
-                                      "Australia/ACT",
-                                      "Australia/Adelaide",
-                                      "Australia/Brisbane",
-                                      "Australia/Broken_Hill",
-                                      "Australia/Canberra",
-                                      "Australia/Currie",
-                                      "Australia/Darwin",
-                                      "Australia/Eucla",
-                                      "Australia/Hobart",
-                                      "Australia/LHI",
-                                      "Australia/Lindeman",
-                                      "Australia/Lord_Howe",
-                                      "Australia/Melbourne",
-                                      "Australia/NSW",
-                                      "Australia/North",
-                                      "Australia/Perth",
-                                      "Australia/Queensland",
-                                      "Australia/South",
-                                      "Australia/Sydney",
-                                      "Australia/Tasmania",
-                                      "Australia/Victoria",
-                                      "Australia/West",
-                                      "Australia/Yancowinna",
-                                      "Brazil/Acre",
-                                      "Brazil/DeNoronha",
-                                      "Brazil/East",
-                                      "Brazil/West",
-                                      "CET",
-                                      "CST6CDT",
-                                      "Canada/Atlantic",
-                                      "Canada/Central",
-                                      "Canada/Eastern",
-                                      "Canada/Mountain",
-                                      "Canada/Newfoundland",
-                                      "Canada/Pacific",
-                                      "Canada/Saskatchewan",
-                                      "Canada/Yukon",
-                                      "Chile/Continental",
-                                      "Chile/EasterIsland",
-                                      "Cuba",
-                                      "EET",
-                                      "EST",
-                                      "EST5EDT",
-                                      "Egypt",
-                                      "Eire",
-                                      "Etc/GMT",
-                                      "Etc/GMT+0",
-                                      "Etc/GMT+1",
-                                      "Etc/GMT+10",
-                                      "Etc/GMT+11",
-                                      "Etc/GMT+12",
-                                      "Etc/GMT+2",
-                                      "Etc/GMT+3",
-                                      "Etc/GMT+4",
-                                      "Etc/GMT+5",
-                                      "Etc/GMT+6",
-                                      "Etc/GMT+7",
-                                      "Etc/GMT+8",
-                                      "Etc/GMT+9",
-                                      "Etc/GMT-0",
-                                      "Etc/GMT-1",
-                                      "Etc/GMT-10",
-                                      "Etc/GMT-11",
-                                      "Etc/GMT-12",
-                                      "Etc/GMT-13",
-                                      "Etc/GMT-14",
-                                      "Etc/GMT-2",
-                                      "Etc/GMT-3",
-                                      "Etc/GMT-4",
-                                      "Etc/GMT-5",
-                                      "Etc/GMT-6",
-                                      "Etc/GMT-7",
-                                      "Etc/GMT-8",
-                                      "Etc/GMT-9",
-                                      "Etc/GMT0",
-                                      "Etc/Greenwich",
-                                      "Etc/UCT",
-                                      "Etc/UTC",
-                                      "Etc/Universal",
-                                      "Etc/Zulu",
-                                      "Europe/Amsterdam",
-                                      "Europe/Andorra",
-                                      "Europe/Astrakhan",
-                                      "Europe/Athens",
-                                      "Europe/Belfast",
-                                      "Europe/Belgrade",
-                                      "Europe/Berlin",
-                                      "Europe/Bratislava",
-                                      "Europe/Brussels",
-                                      "Europe/Bucharest",
-                                      "Europe/Budapest",
-                                      "Europe/Busingen",
-                                      "Europe/Chisinau",
-                                      "Europe/Copenhagen",
-                                      "Europe/Dublin",
-                                      "Europe/Gibraltar",
-                                      "Europe/Guernsey",
-                                      "Europe/Helsinki",
-                                      "Europe/Isle_of_Man",
-                                      "Europe/Istanbul",
-                                      "Europe/Jersey",
-                                      "Europe/Kaliningrad",
-                                      "Europe/Kiev",
-                                      "Europe/Kirov",
-                                      "Europe/Lisbon",
-                                      "Europe/Ljubljana",
-                                      "Europe/London",
-                                      "Europe/Luxembourg",
-                                      "Europe/Madrid",
-                                      "Europe/Malta",
-                                      "Europe/Mariehamn",
-                                      "Europe/Minsk",
-                                      "Europe/Monaco",
-                                      "Europe/Moscow",
-                                      "Europe/Nicosia",
-                                      "Europe/Oslo",
-                                      "Europe/Paris",
-                                      "Europe/Podgorica",
-                                      "Europe/Prague",
-                                      "Europe/Riga",
-                                      "Europe/Rome",
-                                      "Europe/Samara",
-                                      "Europe/San_Marino",
-                                      "Europe/Sarajevo",
-                                      "Europe/Saratov",
-                                      "Europe/Simferopol",
-                                      "Europe/Skopje",
-                                      "Europe/Sofia",
-                                      "Europe/Stockholm",
-                                      "Europe/Tallinn",
-                                      "Europe/Tirane",
-                                      "Europe/Tiraspol",
-                                      "Europe/Ulyanovsk",
-                                      "Europe/Uzhgorod",
-                                      "Europe/Vaduz",
-                                      "Europe/Vatican",
-                                      "Europe/Vienna",
-                                      "Europe/Vilnius",
-                                      "Europe/Volgograd",
-                                      "Europe/Warsaw",
-                                      "Europe/Zagreb",
-                                      "Europe/Zaporozhye",
-                                      "Europe/Zurich",
-                                      "GB",
-                                      "GB-Eire",
-                                      "GMT",
-                                      "GMT+0",
-                                      "GMT-0",
-                                      "GMT0",
-                                      "Greenwich",
-                                      "HST",
-                                      "Hongkong",
-                                      "Iceland",
-                                      "Indian/Antananarivo",
-                                      "Indian/Chagos",
-                                      "Indian/Christmas",
-                                      "Indian/Cocos",
-                                      "Indian/Comoro",
-                                      "Indian/Kerguelen",
-                                      "Indian/Mahe",
-                                      "Indian/Maldives",
-                                      "Indian/Mauritius",
-                                      "Indian/Mayotte",
-                                      "Indian/Reunion",
-                                      "Iran",
-                                      "Israel",
-                                      "Jamaica",
-                                      "Japan",
-                                      "Kwajalein",
-                                      "Libya",
-                                      "MET",
-                                      "MST",
-                                      "MST7MDT",
-                                      "Mexico/BajaNorte",
-                                      "Mexico/BajaSur",
-                                      "Mexico/General",
-                                      "NZ",
-                                      "NZ-CHAT",
-                                      "Navajo",
-                                      "PRC",
-                                      "PST8PDT",
-                                      "Pacific/Apia",
-                                      "Pacific/Auckland",
-                                      "Pacific/Bougainville",
-                                      "Pacific/Chatham",
-                                      "Pacific/Chuuk",
-                                      "Pacific/Easter",
-                                      "Pacific/Efate",
-                                      "Pacific/Enderbury",
-                                      "Pacific/Fakaofo",
-                                      "Pacific/Fiji",
-                                      "Pacific/Funafuti",
-                                      "Pacific/Galapagos",
-                                      "Pacific/Gambier",
-                                      "Pacific/Guadalcanal",
-                                      "Pacific/Guam",
-                                      "Pacific/Honolulu",
-                                      "Pacific/Johnston",
-                                      "Pacific/Kiritimati",
-                                      "Pacific/Kosrae",
-                                      "Pacific/Kwajalein",
-                                      "Pacific/Majuro",
-                                      "Pacific/Marquesas",
-                                      "Pacific/Midway",
-                                      "Pacific/Nauru",
-                                      "Pacific/Niue",
-                                      "Pacific/Norfolk",
-                                      "Pacific/Noumea",
-                                      "Pacific/Pago_Pago",
-                                      "Pacific/Palau",
-                                      "Pacific/Pitcairn",
-                                      "Pacific/Pohnpei",
-                                      "Pacific/Ponape",
-                                      "Pacific/Port_Moresby",
-                                      "Pacific/Rarotonga",
-                                      "Pacific/Saipan",
-                                      "Pacific/Samoa",
-                                      "Pacific/Tahiti",
-                                      "Pacific/Tarawa",
-                                      "Pacific/Tongatapu",
-                                      "Pacific/Truk",
-                                      "Pacific/Wake",
-                                      "Pacific/Wallis",
-                                      "Pacific/Yap",
-                                      "Poland",
-                                      "Portugal",
-                                      "ROC",
-                                      "ROK",
-                                      "Singapore",
-                                      "Turkey",
-                                      "UCT",
-                                      "US/Alaska",
-                                      "US/Aleutian",
-                                      "US/Arizona",
-                                      "US/Central",
-                                      "US/East-Indiana",
-                                      "US/Eastern",
-                                      "US/Hawaii",
-                                      "US/Indiana-Starke",
-                                      "US/Michigan",
-                                      "US/Mountain",
-                                      "US/Pacific",
-                                      "US/Samoa",
-                                      "UTC",
-                                      "Universal",
-                                      "W-SU",
-                                      "WET",
-                                      "Zulu",
-                                      nullptr};
-
-std::vector<std::string> AllTimeZoneNames() {
-  std::vector<std::string> names;
-  for (const char* const* namep = kTimeZoneNames; *namep != nullptr; ++namep) {
-    names.push_back(std::string("file:") + *namep);
-  }
-  assert(!names.empty());
-
-  std::mt19937 urbg(42);  // a UniformRandomBitGenerator with fixed seed
-  std::shuffle(names.begin(), names.end(), urbg);
-  return names;
-}
-
-cctz::time_zone TestTimeZone() {
-  cctz::time_zone tz;
-  cctz::load_time_zone("America/Los_Angeles", &tz);
-  return tz;
-}
-
-void BM_Zone_LoadUTCTimeZoneFirst(benchmark::State& state) {
-  cctz::time_zone tz;
-  cctz::load_time_zone("UTC", &tz);  // in case we're first
-  cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(cctz::load_time_zone("UTC", &tz));
-  }
-}
-BENCHMARK(BM_Zone_LoadUTCTimeZoneFirst);
-
-void BM_Zone_LoadUTCTimeZoneLast(benchmark::State& state) {
-  cctz::time_zone tz;
-  for (const auto& name : AllTimeZoneNames()) {
-    cctz::load_time_zone(name, &tz);  // prime cache
-  }
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(cctz::load_time_zone("UTC", &tz));
-  }
-}
-BENCHMARK(BM_Zone_LoadUTCTimeZoneLast);
-
-void BM_Zone_LoadTimeZoneFirst(benchmark::State& state) {
-  cctz::time_zone tz = cctz::utc_time_zone();  // in case we're first
-  const std::string name = "file:America/Los_Angeles";
-  while (state.KeepRunning()) {
-    state.PauseTiming();
-    cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
-    state.ResumeTiming();
-    benchmark::DoNotOptimize(cctz::load_time_zone(name, &tz));
-  }
-}
-BENCHMARK(BM_Zone_LoadTimeZoneFirst);
-
-void BM_Zone_LoadTimeZoneCached(benchmark::State& state) {
-  cctz::time_zone tz = cctz::utc_time_zone();  // in case we're first
-  cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
-  const std::string name = "file:America/Los_Angeles";
-  cctz::load_time_zone(name, &tz);  // prime cache
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(cctz::load_time_zone(name, &tz));
-  }
-}
-BENCHMARK(BM_Zone_LoadTimeZoneCached);
-
-void BM_Zone_LoadLocalTimeZoneCached(benchmark::State& state) {
-  cctz::utc_time_zone();  // in case we're first
-  cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
-  cctz::local_time_zone();  // prime cache
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(cctz::local_time_zone());
-  }
-}
-BENCHMARK(BM_Zone_LoadLocalTimeZoneCached);
-
-void BM_Zone_LoadAllTimeZonesFirst(benchmark::State& state) {
-  cctz::time_zone tz;
-  const std::vector<std::string> names = AllTimeZoneNames();
-  for (auto index = names.size(); state.KeepRunning(); ++index) {
-    if (index == names.size()) {
-      index = 0;
-    }
-    if (index == 0) {
-      state.PauseTiming();
-      cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
-      state.ResumeTiming();
-    }
-    benchmark::DoNotOptimize(cctz::load_time_zone(names[index], &tz));
-  }
-}
-BENCHMARK(BM_Zone_LoadAllTimeZonesFirst);
-
-void BM_Zone_LoadAllTimeZonesCached(benchmark::State& state) {
-  cctz::time_zone tz;
-  const std::vector<std::string> names = AllTimeZoneNames();
-  for (const auto& name : names) {
-    cctz::load_time_zone(name, &tz);  // prime cache
-  }
-  for (auto index = names.size(); state.KeepRunning(); ++index) {
-    if (index == names.size()) {
-      index = 0;
-    }
-    benchmark::DoNotOptimize(cctz::load_time_zone(names[index], &tz));
-  }
-}
-BENCHMARK(BM_Zone_LoadAllTimeZonesCached);
-
-void BM_Zone_TimeZoneEqualityImplicit(benchmark::State& state) {
-  cctz::time_zone tz;  // implicit UTC
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(tz == tz);
-  }
-}
-BENCHMARK(BM_Zone_TimeZoneEqualityImplicit);
-
-void BM_Zone_TimeZoneEqualityExplicit(benchmark::State& state) {
-  cctz::time_zone tz = cctz::utc_time_zone();  // explicit UTC
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(tz == tz);
-  }
-}
-BENCHMARK(BM_Zone_TimeZoneEqualityExplicit);
-
-void BM_Zone_UTCTimeZone(benchmark::State& state) {
-  cctz::time_zone tz;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(cctz::utc_time_zone());
-  }
-}
-BENCHMARK(BM_Zone_UTCTimeZone);
-
-// In each "ToCivil" benchmark we switch between two instants separated
-// by at least one transition in order to defeat any internal caching of
-// previous results (e.g., see local_time_hint_).
-//
-// The "UTC" variants use UTC instead of the Google/local time zone.
-
-void BM_Time_ToCivil_CCTZ(benchmark::State& state) {
-  const cctz::time_zone tz = TestTimeZone();
-  std::chrono::system_clock::time_point tp =
-      std::chrono::system_clock::from_time_t(1384569027);
-  std::chrono::system_clock::time_point tp2 =
-      std::chrono::system_clock::from_time_t(1418962578);
-  while (state.KeepRunning()) {
-    std::swap(tp, tp2);
-    tp += std::chrono::seconds(1);
-    benchmark::DoNotOptimize(cctz::convert(tp, tz));
-  }
-}
-BENCHMARK(BM_Time_ToCivil_CCTZ);
-
-void BM_Time_ToCivil_Libc(benchmark::State& state) {
-  // No timezone support, so just use localtime.
-  time_t t = 1384569027;
-  time_t t2 = 1418962578;
-  struct tm tm;
-  while (state.KeepRunning()) {
-    std::swap(t, t2);
-    t += 1;
-#if defined(_WIN32) || defined(_WIN64)
-    benchmark::DoNotOptimize(localtime_s(&tm, &t));
-#else
-    benchmark::DoNotOptimize(localtime_r(&t, &tm));
-#endif
-  }
-}
-BENCHMARK(BM_Time_ToCivil_Libc);
-
-void BM_Time_ToCivilUTC_CCTZ(benchmark::State& state) {
-  const cctz::time_zone tz = cctz::utc_time_zone();
-  std::chrono::system_clock::time_point tp =
-      std::chrono::system_clock::from_time_t(1384569027);
-  while (state.KeepRunning()) {
-    tp += std::chrono::seconds(1);
-    benchmark::DoNotOptimize(cctz::convert(tp, tz));
-  }
-}
-BENCHMARK(BM_Time_ToCivilUTC_CCTZ);
-
-void BM_Time_ToCivilUTC_Libc(benchmark::State& state) {
-  time_t t = 1384569027;
-  struct tm tm;
-  while (state.KeepRunning()) {
-    t += 1;
-#if defined(_WIN32) || defined(_WIN64)
-    benchmark::DoNotOptimize(gmtime_s(&tm, &t));
-#else
-    benchmark::DoNotOptimize(gmtime_r(&t, &tm));
-#endif
-  }
-}
-BENCHMARK(BM_Time_ToCivilUTC_Libc);
-
-// In each "FromCivil" benchmark we switch between two YMDhms values
-// separated by at least one transition in order to defeat any internal
-// caching of previous results (e.g., see time_local_hint_).
-//
-// The "UTC" variants use UTC instead of the Google/local time zone.
-// The "Day0" variants require normalization of the day of month.
-
-void BM_Time_FromCivil_CCTZ(benchmark::State& state) {
-  const cctz::time_zone tz = TestTimeZone();
-  int i = 0;
-  while (state.KeepRunning()) {
-    if ((i++ & 1) == 0) {
-      benchmark::DoNotOptimize(
-          cctz::convert(cctz::civil_second(2014, 12, 18, 20, 16, 18), tz));
-    } else {
-      benchmark::DoNotOptimize(
-          cctz::convert(cctz::civil_second(2013, 11, 15, 18, 30, 27), tz));
-    }
-  }
-}
-BENCHMARK(BM_Time_FromCivil_CCTZ);
-
-void BM_Time_FromCivil_Libc(benchmark::State& state) {
-  // No timezone support, so just use localtime.
-  int i = 0;
-  while (state.KeepRunning()) {
-    struct tm tm;
-    if ((i++ & 1) == 0) {
-      tm.tm_year = 2014 - 1900;
-      tm.tm_mon = 12 - 1;
-      tm.tm_mday = 18;
-      tm.tm_hour = 20;
-      tm.tm_min = 16;
-      tm.tm_sec = 18;
-    } else {
-      tm.tm_year = 2013 - 1900;
-      tm.tm_mon = 11 - 1;
-      tm.tm_mday = 15;
-      tm.tm_hour = 18;
-      tm.tm_min = 30;
-      tm.tm_sec = 27;
-    }
-    tm.tm_isdst = -1;
-    benchmark::DoNotOptimize(mktime(&tm));
-  }
-}
-BENCHMARK(BM_Time_FromCivil_Libc);
-
-void BM_Time_FromCivilUTC_CCTZ(benchmark::State& state) {
-  const cctz::time_zone tz = cctz::utc_time_zone();
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(
-        cctz::convert(cctz::civil_second(2014, 12, 18, 20, 16, 18), tz));
-  }
-}
-BENCHMARK(BM_Time_FromCivilUTC_CCTZ);
-
-// There is no BM_Time_FromCivilUTC_Libc.
-
-void BM_Time_FromCivilDay0_CCTZ(benchmark::State& state) {
-  const cctz::time_zone tz = TestTimeZone();
-  int i = 0;
-  while (state.KeepRunning()) {
-    if ((i++ & 1) == 0) {
-      benchmark::DoNotOptimize(
-          cctz::convert(cctz::civil_second(2014, 12, 0, 20, 16, 18), tz));
-    } else {
-      benchmark::DoNotOptimize(
-          cctz::convert(cctz::civil_second(2013, 11, 0, 18, 30, 27), tz));
-    }
-  }
-}
-BENCHMARK(BM_Time_FromCivilDay0_CCTZ);
-
-void BM_Time_FromCivilDay0_Libc(benchmark::State& state) {
-  // No timezone support, so just use localtime.
-  int i = 0;
-  while (state.KeepRunning()) {
-    struct tm tm;
-    if ((i++ & 1) == 0) {
-      tm.tm_year = 2014 - 1900;
-      tm.tm_mon = 12 - 1;
-      tm.tm_mday = 0;
-      tm.tm_hour = 20;
-      tm.tm_min = 16;
-      tm.tm_sec = 18;
-    } else {
-      tm.tm_year = 2013 - 1900;
-      tm.tm_mon = 11 - 1;
-      tm.tm_mday = 0;
-      tm.tm_hour = 18;
-      tm.tm_min = 30;
-      tm.tm_sec = 27;
-    }
-    tm.tm_isdst = -1;
-    benchmark::DoNotOptimize(mktime(&tm));
-  }
-}
-BENCHMARK(BM_Time_FromCivilDay0_Libc);
-
-const char* const kFormats[] = {
-    RFC1123_full,           // 0
-    RFC1123_no_wday,        // 1
-    RFC3339_full,           // 2
-    RFC3339_sec,            // 3
-    "%Y-%m-%d%ET%H:%M:%S",  // 4
-    "%Y-%m-%d",             // 5
-};
-const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
-
-void BM_Format_FormatTime(benchmark::State& state) {
-  const std::string fmt = kFormats[state.range(0)];
-  state.SetLabel(fmt);
-  const cctz::time_zone tz = TestTimeZone();
-  const std::chrono::system_clock::time_point tp =
-      cctz::convert(cctz::civil_second(1977, 6, 28, 9, 8, 7), tz) +
-      std::chrono::microseconds(1);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(cctz::format(fmt, tp, tz));
-  }
-}
-BENCHMARK(BM_Format_FormatTime)->DenseRange(0, kNumFormats - 1);
-
-void BM_Format_ParseTime(benchmark::State& state) {
-  const std::string fmt = kFormats[state.range(0)];
-  state.SetLabel(fmt);
-  const cctz::time_zone tz = TestTimeZone();
-  std::chrono::system_clock::time_point tp =
-      cctz::convert(cctz::civil_second(1977, 6, 28, 9, 8, 7), tz) +
-      std::chrono::microseconds(1);
-  const std::string when = cctz::format(fmt, tp, tz);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(cctz::parse(fmt, when, tz, &tp));
-  }
-}
-BENCHMARK(BM_Format_ParseTime)->DenseRange(0, kNumFormats - 1);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_detail.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_detail.cc
deleted file mode 100644
index 0b07e397e5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_detail.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h"
-
-#include <iomanip>
-#include <ostream>
-#include <sstream>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-namespace detail {
-
-// Output stream operators output a format matching YYYY-MM-DDThh:mm:ss,
-// while omitting fields inferior to the type's alignment. For example,
-// civil_day is formatted only as YYYY-MM-DD.
-std::ostream& operator<<(std::ostream& os, const civil_year& y) {
-  std::stringstream ss;
-  ss << y.year();  // No padding.
-  return os << ss.str();
-}
-std::ostream& operator<<(std::ostream& os, const civil_month& m) {
-  std::stringstream ss;
-  ss << civil_year(m) << '-';
-  ss << std::setfill('0') << std::setw(2) << m.month();
-  return os << ss.str();
-}
-std::ostream& operator<<(std::ostream& os, const civil_day& d) {
-  std::stringstream ss;
-  ss << civil_month(d) << '-';
-  ss << std::setfill('0') << std::setw(2) << d.day();
-  return os << ss.str();
-}
-std::ostream& operator<<(std::ostream& os, const civil_hour& h) {
-  std::stringstream ss;
-  ss << civil_day(h) << 'T';
-  ss << std::setfill('0') << std::setw(2) << h.hour();
-  return os << ss.str();
-}
-std::ostream& operator<<(std::ostream& os, const civil_minute& m) {
-  std::stringstream ss;
-  ss << civil_hour(m) << ':';
-  ss << std::setfill('0') << std::setw(2) << m.minute();
-  return os << ss.str();
-}
-std::ostream& operator<<(std::ostream& os, const civil_second& s) {
-  std::stringstream ss;
-  ss << civil_minute(s) << ':';
-  ss << std::setfill('0') << std::setw(2) << s.second();
-  return os << ss.str();
-}
-
-////////////////////////////////////////////////////////////////////////
-
-std::ostream& operator<<(std::ostream& os, weekday wd) {
-  switch (wd) {
-    case weekday::monday:
-      return os << "Monday";
-    case weekday::tuesday:
-      return os << "Tuesday";
-    case weekday::wednesday:
-      return os << "Wednesday";
-    case weekday::thursday:
-      return os << "Thursday";
-    case weekday::friday:
-      return os << "Friday";
-    case weekday::saturday:
-      return os << "Saturday";
-    case weekday::sunday:
-      return os << "Sunday";
-  }
-  return os;  // Should never get here, but -Wreturn-type may warn without this.
-}
-
-}  // namespace detail
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_test.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_test.cc
deleted file mode 100644
index a5a7123041..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_test.cc
+++ /dev/null
@@ -1,1066 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-
-#include <iomanip>
-#include <limits>
-#include <sstream>
-#include <string>
-#include <type_traits>
-
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-namespace {
-
-template <typename T>
-std::string Format(const T& t) {
-  std::stringstream ss;
-  ss << t;
-  return ss.str();
-}
-
-}  // namespace
-
-#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910)
-// Construction constexpr tests
-
-TEST(CivilTime, Normal) {
-  constexpr civil_second css(2016, 1, 28, 17, 14, 12);
-  static_assert(css.second() == 12, "Normal.second");
-  constexpr civil_minute cmm(2016, 1, 28, 17, 14);
-  static_assert(cmm.minute() == 14, "Normal.minute");
-  constexpr civil_hour chh(2016, 1, 28, 17);
-  static_assert(chh.hour() == 17, "Normal.hour");
-  constexpr civil_day cd(2016, 1, 28);
-  static_assert(cd.day() == 28, "Normal.day");
-  constexpr civil_month cm(2016, 1);
-  static_assert(cm.month() == 1, "Normal.month");
-  constexpr civil_year cy(2016);
-  static_assert(cy.year() == 2016, "Normal.year");
-}
-
-TEST(CivilTime, Conversion) {
-  constexpr civil_year cy(2016);
-  static_assert(cy.year() == 2016, "Conversion.year");
-  constexpr civil_month cm(cy);
-  static_assert(cm.month() == 1, "Conversion.month");
-  constexpr civil_day cd(cm);
-  static_assert(cd.day() == 1, "Conversion.day");
-  constexpr civil_hour chh(cd);
-  static_assert(chh.hour() == 0, "Conversion.hour");
-  constexpr civil_minute cmm(chh);
-  static_assert(cmm.minute() == 0, "Conversion.minute");
-  constexpr civil_second css(cmm);
-  static_assert(css.second() == 0, "Conversion.second");
-}
-
-// Normalization constexpr tests
-
-TEST(CivilTime, Normalized) {
-  constexpr civil_second cs(2016, 1, 28, 17, 14, 12);
-  static_assert(cs.year() == 2016, "Normalized.year");
-  static_assert(cs.month() == 1, "Normalized.month");
-  static_assert(cs.day() == 28, "Normalized.day");
-  static_assert(cs.hour() == 17, "Normalized.hour");
-  static_assert(cs.minute() == 14, "Normalized.minute");
-  static_assert(cs.second() == 12, "Normalized.second");
-}
-
-TEST(CivilTime, SecondOverflow) {
-  constexpr civil_second cs(2016, 1, 28, 17, 14, 121);
-  static_assert(cs.year() == 2016, "SecondOverflow.year");
-  static_assert(cs.month() == 1, "SecondOverflow.month");
-  static_assert(cs.day() == 28, "SecondOverflow.day");
-  static_assert(cs.hour() == 17, "SecondOverflow.hour");
-  static_assert(cs.minute() == 16, "SecondOverflow.minute");
-  static_assert(cs.second() == 1, "SecondOverflow.second");
-}
-
-TEST(CivilTime, SecondUnderflow) {
-  constexpr civil_second cs(2016, 1, 28, 17, 14, -121);
-  static_assert(cs.year() == 2016, "SecondUnderflow.year");
-  static_assert(cs.month() == 1, "SecondUnderflow.month");
-  static_assert(cs.day() == 28, "SecondUnderflow.day");
-  static_assert(cs.hour() == 17, "SecondUnderflow.hour");
-  static_assert(cs.minute() == 11, "SecondUnderflow.minute");
-  static_assert(cs.second() == 59, "SecondUnderflow.second");
-}
-
-TEST(CivilTime, MinuteOverflow) {
-  constexpr civil_second cs(2016, 1, 28, 17, 121, 12);
-  static_assert(cs.year() == 2016, "MinuteOverflow.year");
-  static_assert(cs.month() == 1, "MinuteOverflow.month");
-  static_assert(cs.day() == 28, "MinuteOverflow.day");
-  static_assert(cs.hour() == 19, "MinuteOverflow.hour");
-  static_assert(cs.minute() == 1, "MinuteOverflow.minute");
-  static_assert(cs.second() == 12, "MinuteOverflow.second");
-}
-
-TEST(CivilTime, MinuteUnderflow) {
-  constexpr civil_second cs(2016, 1, 28, 17, -121, 12);
-  static_assert(cs.year() == 2016, "MinuteUnderflow.year");
-  static_assert(cs.month() == 1, "MinuteUnderflow.month");
-  static_assert(cs.day() == 28, "MinuteUnderflow.day");
-  static_assert(cs.hour() == 14, "MinuteUnderflow.hour");
-  static_assert(cs.minute() == 59, "MinuteUnderflow.minute");
-  static_assert(cs.second() == 12, "MinuteUnderflow.second");
-}
-
-TEST(CivilTime, HourOverflow) {
-  constexpr civil_second cs(2016, 1, 28, 49, 14, 12);
-  static_assert(cs.year() == 2016, "HourOverflow.year");
-  static_assert(cs.month() == 1, "HourOverflow.month");
-  static_assert(cs.day() == 30, "HourOverflow.day");
-  static_assert(cs.hour() == 1, "HourOverflow.hour");
-  static_assert(cs.minute() == 14, "HourOverflow.minute");
-  static_assert(cs.second() == 12, "HourOverflow.second");
-}
-
-TEST(CivilTime, HourUnderflow) {
-  constexpr civil_second cs(2016, 1, 28, -49, 14, 12);
-  static_assert(cs.year() == 2016, "HourUnderflow.year");
-  static_assert(cs.month() == 1, "HourUnderflow.month");
-  static_assert(cs.day() == 25, "HourUnderflow.day");
-  static_assert(cs.hour() == 23, "HourUnderflow.hour");
-  static_assert(cs.minute() == 14, "HourUnderflow.minute");
-  static_assert(cs.second() == 12, "HourUnderflow.second");
-}
-
-TEST(CivilTime, MonthOverflow) {
-  constexpr civil_second cs(2016, 25, 28, 17, 14, 12);
-  static_assert(cs.year() == 2018, "MonthOverflow.year");
-  static_assert(cs.month() == 1, "MonthOverflow.month");
-  static_assert(cs.day() == 28, "MonthOverflow.day");
-  static_assert(cs.hour() == 17, "MonthOverflow.hour");
-  static_assert(cs.minute() == 14, "MonthOverflow.minute");
-  static_assert(cs.second() == 12, "MonthOverflow.second");
-}
-
-TEST(CivilTime, MonthUnderflow) {
-  constexpr civil_second cs(2016, -25, 28, 17, 14, 12);
-  static_assert(cs.year() == 2013, "MonthUnderflow.year");
-  static_assert(cs.month() == 11, "MonthUnderflow.month");
-  static_assert(cs.day() == 28, "MonthUnderflow.day");
-  static_assert(cs.hour() == 17, "MonthUnderflow.hour");
-  static_assert(cs.minute() == 14, "MonthUnderflow.minute");
-  static_assert(cs.second() == 12, "MonthUnderflow.second");
-}
-
-TEST(CivilTime, C4Overflow) {
-  constexpr civil_second cs(2016, 1, 292195, 17, 14, 12);
-  static_assert(cs.year() == 2816, "C4Overflow.year");
-  static_assert(cs.month() == 1, "C4Overflow.month");
-  static_assert(cs.day() == 1, "C4Overflow.day");
-  static_assert(cs.hour() == 17, "C4Overflow.hour");
-  static_assert(cs.minute() == 14, "C4Overflow.minute");
-  static_assert(cs.second() == 12, "C4Overflow.second");
-}
-
-TEST(CivilTime, C4Underflow) {
-  constexpr civil_second cs(2016, 1, -292195, 17, 14, 12);
-  static_assert(cs.year() == 1215, "C4Underflow.year");
-  static_assert(cs.month() == 12, "C4Underflow.month");
-  static_assert(cs.day() == 30, "C4Underflow.day");
-  static_assert(cs.hour() == 17, "C4Underflow.hour");
-  static_assert(cs.minute() == 14, "C4Underflow.minute");
-  static_assert(cs.second() == 12, "C4Underflow.second");
-}
-
-TEST(CivilTime, MixedNormalization) {
-  constexpr civil_second cs(2016, -42, 122, 99, -147, 4949);
-  static_assert(cs.year() == 2012, "MixedNormalization.year");
-  static_assert(cs.month() == 10, "MixedNormalization.month");
-  static_assert(cs.day() == 4, "MixedNormalization.day");
-  static_assert(cs.hour() == 1, "MixedNormalization.hour");
-  static_assert(cs.minute() == 55, "MixedNormalization.minute");
-  static_assert(cs.second() == 29, "MixedNormalization.second");
-}
-
-// Relational constexpr tests
-
-TEST(CivilTime, Less) {
-  constexpr civil_second cs1(2016, 1, 28, 17, 14, 12);
-  constexpr civil_second cs2(2016, 1, 28, 17, 14, 13);
-  constexpr bool less = cs1 < cs2;
-  static_assert(less, "Less");
-}
-
-// Arithmetic constexpr tests
-
-TEST(CivilTime, Addition) {
-  constexpr civil_second cs1(2016, 1, 28, 17, 14, 12);
-  constexpr civil_second cs2 = cs1 + 50;
-  static_assert(cs2.year() == 2016, "Addition.year");
-  static_assert(cs2.month() == 1, "Addition.month");
-  static_assert(cs2.day() == 28, "Addition.day");
-  static_assert(cs2.hour() == 17, "Addition.hour");
-  static_assert(cs2.minute() == 15, "Addition.minute");
-  static_assert(cs2.second() == 2, "Addition.second");
-}
-
-TEST(CivilTime, Subtraction) {
-  constexpr civil_second cs1(2016, 1, 28, 17, 14, 12);
-  constexpr civil_second cs2 = cs1 - 50;
-  static_assert(cs2.year() == 2016, "Subtraction.year");
-  static_assert(cs2.month() == 1, "Subtraction.month");
-  static_assert(cs2.day() == 28, "Subtraction.day");
-  static_assert(cs2.hour() == 17, "Subtraction.hour");
-  static_assert(cs2.minute() == 13, "Subtraction.minute");
-  static_assert(cs2.second() == 22, "Subtraction.second");
-}
-
-TEST(CivilTime, Difference) {
-  constexpr civil_day cd1(2016, 1, 28);
-  constexpr civil_day cd2(2015, 1, 28);
-  constexpr int diff = cd1 - cd2;
-  static_assert(diff == 365, "Difference");
-}
-
-// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
-TEST(CivilTime, ConstructionWithHugeYear) {
-  constexpr civil_hour h(-9223372036854775807, 1, 1, -1);
-  static_assert(h.year() == -9223372036854775807 - 1,
-                "ConstructionWithHugeYear");
-  static_assert(h.month() == 12, "ConstructionWithHugeYear");
-  static_assert(h.day() == 31, "ConstructionWithHugeYear");
-  static_assert(h.hour() == 23, "ConstructionWithHugeYear");
-}
-
-// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
-TEST(CivilTime, DifferenceWithHugeYear) {
-  {
-    constexpr civil_day d1(9223372036854775807, 1, 1);
-    constexpr civil_day d2(9223372036854775807, 12, 31);
-    static_assert(d2 - d1 == 364, "DifferenceWithHugeYear");
-  }
-  {
-    constexpr civil_day d1(-9223372036854775807 - 1, 1, 1);
-    constexpr civil_day d2(-9223372036854775807 - 1, 12, 31);
-    static_assert(d2 - d1 == 365, "DifferenceWithHugeYear");
-  }
-  {
-    // Check the limits of the return value at the end of the year range.
-    constexpr civil_day d1(9223372036854775807, 1, 1);
-    constexpr civil_day d2(9198119301927009252, 6, 6);
-    static_assert(d1 - d2 == 9223372036854775807, "DifferenceWithHugeYear");
-    static_assert((d2 - 1) - d1 == -9223372036854775807 - 1,
-                  "DifferenceWithHugeYear");
-  }
-  {
-    // Check the limits of the return value at the start of the year range.
-    constexpr civil_day d1(-9223372036854775807 - 1, 1, 1);
-    constexpr civil_day d2(-9198119301927009254, 7, 28);
-    static_assert(d2 - d1 == 9223372036854775807, "DifferenceWithHugeYear");
-    static_assert(d1 - (d2 + 1) == -9223372036854775807 - 1,
-                  "DifferenceWithHugeYear");
-  }
-  {
-    // Check the limits of the return value from either side of year 0.
-    constexpr civil_day d1(-12626367463883278, 9, 3);
-    constexpr civil_day d2(12626367463883277, 3, 28);
-    static_assert(d2 - d1 == 9223372036854775807, "DifferenceWithHugeYear");
-    static_assert(d1 - (d2 + 1) == -9223372036854775807 - 1,
-                  "DifferenceWithHugeYear");
-  }
-}
-
-// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
-TEST(CivilTime, DifferenceNoIntermediateOverflow) {
-  {
-    // The difference up to the minute field would be below the minimum
-    // diff_t, but the 52 extra seconds brings us back to the minimum.
-    constexpr civil_second s1(-292277022657, 1, 27, 8, 29 - 1, 52);
-    constexpr civil_second s2(1970, 1, 1, 0, 0 - 1, 0);
-    static_assert(s1 - s2 == -9223372036854775807 - 1,
-                  "DifferenceNoIntermediateOverflow");
-  }
-  {
-    // The difference up to the minute field would be above the maximum
-    // diff_t, but the -53 extra seconds brings us back to the maximum.
-    constexpr civil_second s1(292277026596, 12, 4, 15, 30, 7 - 7);
-    constexpr civil_second s2(1970, 1, 1, 0, 0, 0 - 7);
-    static_assert(s1 - s2 == 9223372036854775807,
-                  "DifferenceNoIntermediateOverflow");
-  }
-}
-
-// Helper constexpr tests
-
-TEST(CivilTime, WeekDay) {
-  constexpr civil_day cd(2016, 1, 28);
-  constexpr weekday wd = get_weekday(cd);
-  static_assert(wd == weekday::thursday, "Weekday");
-}
-
-TEST(CivilTime, NextWeekDay) {
-  constexpr civil_day cd(2016, 1, 28);
-  constexpr civil_day next = next_weekday(cd, weekday::thursday);
-  static_assert(next.year() == 2016, "NextWeekDay.year");
-  static_assert(next.month() == 2, "NextWeekDay.month");
-  static_assert(next.day() == 4, "NextWeekDay.day");
-}
-
-TEST(CivilTime, PrevWeekDay) {
-  constexpr civil_day cd(2016, 1, 28);
-  constexpr civil_day prev = prev_weekday(cd, weekday::thursday);
-  static_assert(prev.year() == 2016, "PrevWeekDay.year");
-  static_assert(prev.month() == 1, "PrevWeekDay.month");
-  static_assert(prev.day() == 21, "PrevWeekDay.day");
-}
-
-TEST(CivilTime, YearDay) {
-  constexpr civil_day cd(2016, 1, 28);
-  constexpr int yd = get_yearday(cd);
-  static_assert(yd == 28, "YearDay");
-}
-#endif  // __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910)
-
-// The remaining tests do not use constexpr.
-
-TEST(CivilTime, DefaultConstruction) {
-  civil_second ss;
-  EXPECT_EQ("1970-01-01T00:00:00", Format(ss));
-
-  civil_minute mm;
-  EXPECT_EQ("1970-01-01T00:00", Format(mm));
-
-  civil_hour hh;
-  EXPECT_EQ("1970-01-01T00", Format(hh));
-
-  civil_day d;
-  EXPECT_EQ("1970-01-01", Format(d));
-
-  civil_month m;
-  EXPECT_EQ("1970-01", Format(m));
-
-  civil_year y;
-  EXPECT_EQ("1970", Format(y));
-}
-
-TEST(CivilTime, StructMember) {
-  struct S {
-    civil_day day;
-  };
-  S s = {};
-  EXPECT_EQ(civil_day{}, s.day);
-}
-
-TEST(CivilTime, FieldsConstruction) {
-  EXPECT_EQ("2015-01-02T03:04:05", Format(civil_second(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01-02T03:04:00", Format(civil_second(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01-02T03:00:00", Format(civil_second(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01-02T00:00:00", Format(civil_second(2015, 1, 2)));
-  EXPECT_EQ("2015-01-01T00:00:00", Format(civil_second(2015, 1)));
-  EXPECT_EQ("2015-01-01T00:00:00", Format(civil_second(2015)));
-
-  EXPECT_EQ("2015-01-02T03:04", Format(civil_minute(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01-02T03:04", Format(civil_minute(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01-02T03:00", Format(civil_minute(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01-02T00:00", Format(civil_minute(2015, 1, 2)));
-  EXPECT_EQ("2015-01-01T00:00", Format(civil_minute(2015, 1)));
-  EXPECT_EQ("2015-01-01T00:00", Format(civil_minute(2015)));
-
-  EXPECT_EQ("2015-01-02T03", Format(civil_hour(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01-02T03", Format(civil_hour(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01-02T03", Format(civil_hour(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01-02T00", Format(civil_hour(2015, 1, 2)));
-  EXPECT_EQ("2015-01-01T00", Format(civil_hour(2015, 1)));
-  EXPECT_EQ("2015-01-01T00", Format(civil_hour(2015)));
-
-  EXPECT_EQ("2015-01-02", Format(civil_day(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01-02", Format(civil_day(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01-02", Format(civil_day(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01-02", Format(civil_day(2015, 1, 2)));
-  EXPECT_EQ("2015-01-01", Format(civil_day(2015, 1)));
-  EXPECT_EQ("2015-01-01", Format(civil_day(2015)));
-
-  EXPECT_EQ("2015-01", Format(civil_month(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015-01", Format(civil_month(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015-01", Format(civil_month(2015, 1, 2, 3)));
-  EXPECT_EQ("2015-01", Format(civil_month(2015, 1, 2)));
-  EXPECT_EQ("2015-01", Format(civil_month(2015, 1)));
-  EXPECT_EQ("2015-01", Format(civil_month(2015)));
-
-  EXPECT_EQ("2015", Format(civil_year(2015, 1, 2, 3, 4, 5)));
-  EXPECT_EQ("2015", Format(civil_year(2015, 1, 2, 3, 4)));
-  EXPECT_EQ("2015", Format(civil_year(2015, 1, 2, 3)));
-  EXPECT_EQ("2015", Format(civil_year(2015, 1, 2)));
-  EXPECT_EQ("2015", Format(civil_year(2015, 1)));
-  EXPECT_EQ("2015", Format(civil_year(2015)));
-}
-
-TEST(CivilTime, FieldsConstructionLimits) {
-  const int kIntMax = std::numeric_limits<int>::max();
-  EXPECT_EQ("2038-01-19T03:14:07",
-            Format(civil_second(1970, 1, 1, 0, 0, kIntMax)));
-  EXPECT_EQ("6121-02-11T05:21:07",
-            Format(civil_second(1970, 1, 1, 0, kIntMax, kIntMax)));
-  EXPECT_EQ("251104-11-20T12:21:07",
-            Format(civil_second(1970, 1, 1, kIntMax, kIntMax, kIntMax)));
-  EXPECT_EQ("6130715-05-30T12:21:07",
-            Format(civil_second(1970, 1, kIntMax, kIntMax, kIntMax, kIntMax)));
-  EXPECT_EQ(
-      "185087685-11-26T12:21:07",
-      Format(civil_second(1970, kIntMax, kIntMax, kIntMax, kIntMax, kIntMax)));
-
-  const int kIntMin = std::numeric_limits<int>::min();
-  EXPECT_EQ("1901-12-13T20:45:52",
-            Format(civil_second(1970, 1, 1, 0, 0, kIntMin)));
-  EXPECT_EQ("-2182-11-20T18:37:52",
-            Format(civil_second(1970, 1, 1, 0, kIntMin, kIntMin)));
-  EXPECT_EQ("-247165-02-11T10:37:52",
-            Format(civil_second(1970, 1, 1, kIntMin, kIntMin, kIntMin)));
-  EXPECT_EQ("-6126776-08-01T10:37:52",
-            Format(civil_second(1970, 1, kIntMin, kIntMin, kIntMin, kIntMin)));
-  EXPECT_EQ(
-      "-185083747-10-31T10:37:52",
-      Format(civil_second(1970, kIntMin, kIntMin, kIntMin, kIntMin, kIntMin)));
-}
-
-TEST(CivilTime, ImplicitCrossAlignment) {
-  civil_year year(2015);
-  civil_month month = year;
-  civil_day day = month;
-  civil_hour hour = day;
-  civil_minute minute = hour;
-  civil_second second = minute;
-
-  second = year;
-  EXPECT_EQ(second, year);
-  second = month;
-  EXPECT_EQ(second, month);
-  second = day;
-  EXPECT_EQ(second, day);
-  second = hour;
-  EXPECT_EQ(second, hour);
-  second = minute;
-  EXPECT_EQ(second, minute);
-
-  minute = year;
-  EXPECT_EQ(minute, year);
-  minute = month;
-  EXPECT_EQ(minute, month);
-  minute = day;
-  EXPECT_EQ(minute, day);
-  minute = hour;
-  EXPECT_EQ(minute, hour);
-
-  hour = year;
-  EXPECT_EQ(hour, year);
-  hour = month;
-  EXPECT_EQ(hour, month);
-  hour = day;
-  EXPECT_EQ(hour, day);
-
-  day = year;
-  EXPECT_EQ(day, year);
-  day = month;
-  EXPECT_EQ(day, month);
-
-  month = year;
-  EXPECT_EQ(month, year);
-
-  // Ensures unsafe conversions are not allowed.
-  EXPECT_FALSE((std::is_convertible<civil_second, civil_minute>::value));
-  EXPECT_FALSE((std::is_convertible<civil_second, civil_hour>::value));
-  EXPECT_FALSE((std::is_convertible<civil_second, civil_day>::value));
-  EXPECT_FALSE((std::is_convertible<civil_second, civil_month>::value));
-  EXPECT_FALSE((std::is_convertible<civil_second, civil_year>::value));
-
-  EXPECT_FALSE((std::is_convertible<civil_minute, civil_hour>::value));
-  EXPECT_FALSE((std::is_convertible<civil_minute, civil_day>::value));
-  EXPECT_FALSE((std::is_convertible<civil_minute, civil_month>::value));
-  EXPECT_FALSE((std::is_convertible<civil_minute, civil_year>::value));
-
-  EXPECT_FALSE((std::is_convertible<civil_hour, civil_day>::value));
-  EXPECT_FALSE((std::is_convertible<civil_hour, civil_month>::value));
-  EXPECT_FALSE((std::is_convertible<civil_hour, civil_year>::value));
-
-  EXPECT_FALSE((std::is_convertible<civil_day, civil_month>::value));
-  EXPECT_FALSE((std::is_convertible<civil_day, civil_year>::value));
-
-  EXPECT_FALSE((std::is_convertible<civil_month, civil_year>::value));
-}
-
-TEST(CivilTime, ExplicitCrossAlignment) {
-  //
-  // Assign from smaller units -> larger units
-  //
-
-  civil_second second(2015, 1, 2, 3, 4, 5);
-  EXPECT_EQ("2015-01-02T03:04:05", Format(second));
-
-  civil_minute minute(second);
-  EXPECT_EQ("2015-01-02T03:04", Format(minute));
-
-  civil_hour hour(minute);
-  EXPECT_EQ("2015-01-02T03", Format(hour));
-
-  civil_day day(hour);
-  EXPECT_EQ("2015-01-02", Format(day));
-
-  civil_month month(day);
-  EXPECT_EQ("2015-01", Format(month));
-
-  civil_year year(month);
-  EXPECT_EQ("2015", Format(year));
-
-  //
-  // Now assign from larger units -> smaller units
-  //
-
-  month = civil_month(year);
-  EXPECT_EQ("2015-01", Format(month));
-
-  day = civil_day(month);
-  EXPECT_EQ("2015-01-01", Format(day));
-
-  hour = civil_hour(day);
-  EXPECT_EQ("2015-01-01T00", Format(hour));
-
-  minute = civil_minute(hour);
-  EXPECT_EQ("2015-01-01T00:00", Format(minute));
-
-  second = civil_second(minute);
-  EXPECT_EQ("2015-01-01T00:00:00", Format(second));
-}
-
-// Metafunction to test whether difference is allowed between two types.
-template <typename T1, typename T2>
-struct HasDifference {
-  template <typename U1, typename U2>
-  static std::false_type test(...);
-  template <typename U1, typename U2>
-  static std::true_type test(decltype(std::declval<U1>() - std::declval<U2>()));
-  static constexpr bool value = decltype(test<T1, T2>(0))::value;
-};
-
-TEST(CivilTime, DisallowCrossAlignedDifference) {
-  // Difference is allowed between types with the same alignment.
-  static_assert(HasDifference<civil_second, civil_second>::value, "");
-  static_assert(HasDifference<civil_minute, civil_minute>::value, "");
-  static_assert(HasDifference<civil_hour, civil_hour>::value, "");
-  static_assert(HasDifference<civil_day, civil_day>::value, "");
-  static_assert(HasDifference<civil_month, civil_month>::value, "");
-  static_assert(HasDifference<civil_year, civil_year>::value, "");
-
-  // Difference is disallowed between types with different alignments.
-  static_assert(!HasDifference<civil_second, civil_minute>::value, "");
-  static_assert(!HasDifference<civil_second, civil_hour>::value, "");
-  static_assert(!HasDifference<civil_second, civil_day>::value, "");
-  static_assert(!HasDifference<civil_second, civil_month>::value, "");
-  static_assert(!HasDifference<civil_second, civil_year>::value, "");
-
-  static_assert(!HasDifference<civil_minute, civil_hour>::value, "");
-  static_assert(!HasDifference<civil_minute, civil_day>::value, "");
-  static_assert(!HasDifference<civil_minute, civil_month>::value, "");
-  static_assert(!HasDifference<civil_minute, civil_year>::value, "");
-
-  static_assert(!HasDifference<civil_hour, civil_day>::value, "");
-  static_assert(!HasDifference<civil_hour, civil_month>::value, "");
-  static_assert(!HasDifference<civil_hour, civil_year>::value, "");
-
-  static_assert(!HasDifference<civil_day, civil_month>::value, "");
-  static_assert(!HasDifference<civil_day, civil_year>::value, "");
-
-  static_assert(!HasDifference<civil_month, civil_year>::value, "");
-}
-
-TEST(CivilTime, ValueSemantics) {
-  const civil_hour a(2015, 1, 2, 3);
-  const civil_hour b = a;
-  const civil_hour c(b);
-  civil_hour d;
-  d = c;
-  EXPECT_EQ("2015-01-02T03", Format(d));
-}
-
-TEST(CivilTime, Relational) {
-  // Tests that the alignment unit is ignored in comparison.
-  const civil_year year(2014);
-  const civil_month month(year);
-  EXPECT_EQ(year, month);
-
-#define TEST_RELATIONAL(OLDER, YOUNGER) \
-  do {                                  \
-    EXPECT_FALSE(OLDER < OLDER);        \
-    EXPECT_FALSE(OLDER > OLDER);        \
-    EXPECT_TRUE(OLDER >= OLDER);        \
-    EXPECT_TRUE(OLDER <= OLDER);        \
-    EXPECT_FALSE(YOUNGER < YOUNGER);    \
-    EXPECT_FALSE(YOUNGER > YOUNGER);    \
-    EXPECT_TRUE(YOUNGER >= YOUNGER);    \
-    EXPECT_TRUE(YOUNGER <= YOUNGER);    \
-    EXPECT_EQ(OLDER, OLDER);            \
-    EXPECT_NE(OLDER, YOUNGER);          \
-    EXPECT_LT(OLDER, YOUNGER);          \
-    EXPECT_LE(OLDER, YOUNGER);          \
-    EXPECT_GT(YOUNGER, OLDER);          \
-    EXPECT_GE(YOUNGER, OLDER);          \
-  } while (0)
-
-  // Alignment is ignored in comparison (verified above), so kSecond is used
-  // to test comparison in all field positions.
-  TEST_RELATIONAL(civil_second(2014, 1, 1, 0, 0, 0),
-                  civil_second(2015, 1, 1, 0, 0, 0));
-  TEST_RELATIONAL(civil_second(2014, 1, 1, 0, 0, 0),
-                  civil_second(2014, 2, 1, 0, 0, 0));
-  TEST_RELATIONAL(civil_second(2014, 1, 1, 0, 0, 0),
-                  civil_second(2014, 1, 2, 0, 0, 0));
-  TEST_RELATIONAL(civil_second(2014, 1, 1, 0, 0, 0),
-                  civil_second(2014, 1, 1, 1, 0, 0));
-  TEST_RELATIONAL(civil_second(2014, 1, 1, 1, 0, 0),
-                  civil_second(2014, 1, 1, 1, 1, 0));
-  TEST_RELATIONAL(civil_second(2014, 1, 1, 1, 1, 0),
-                  civil_second(2014, 1, 1, 1, 1, 1));
-
-  // Tests the relational operators of two different civil-time types.
-  TEST_RELATIONAL(civil_day(2014, 1, 1), civil_minute(2014, 1, 1, 1, 1));
-  TEST_RELATIONAL(civil_day(2014, 1, 1), civil_month(2014, 2));
-
-#undef TEST_RELATIONAL
-}
-
-TEST(CivilTime, Arithmetic) {
-  civil_second second(2015, 1, 2, 3, 4, 5);
-  EXPECT_EQ("2015-01-02T03:04:06", Format(second += 1));
-  EXPECT_EQ("2015-01-02T03:04:07", Format(second + 1));
-  EXPECT_EQ("2015-01-02T03:04:08", Format(2 + second));
-  EXPECT_EQ("2015-01-02T03:04:05", Format(second - 1));
-  EXPECT_EQ("2015-01-02T03:04:05", Format(second -= 1));
-  EXPECT_EQ("2015-01-02T03:04:05", Format(second++));
-  EXPECT_EQ("2015-01-02T03:04:07", Format(++second));
-  EXPECT_EQ("2015-01-02T03:04:07", Format(second--));
-  EXPECT_EQ("2015-01-02T03:04:05", Format(--second));
-
-  civil_minute minute(2015, 1, 2, 3, 4);
-  EXPECT_EQ("2015-01-02T03:05", Format(minute += 1));
-  EXPECT_EQ("2015-01-02T03:06", Format(minute + 1));
-  EXPECT_EQ("2015-01-02T03:07", Format(2 + minute));
-  EXPECT_EQ("2015-01-02T03:04", Format(minute - 1));
-  EXPECT_EQ("2015-01-02T03:04", Format(minute -= 1));
-  EXPECT_EQ("2015-01-02T03:04", Format(minute++));
-  EXPECT_EQ("2015-01-02T03:06", Format(++minute));
-  EXPECT_EQ("2015-01-02T03:06", Format(minute--));
-  EXPECT_EQ("2015-01-02T03:04", Format(--minute));
-
-  civil_hour hour(2015, 1, 2, 3);
-  EXPECT_EQ("2015-01-02T04", Format(hour += 1));
-  EXPECT_EQ("2015-01-02T05", Format(hour + 1));
-  EXPECT_EQ("2015-01-02T06", Format(2 + hour));
-  EXPECT_EQ("2015-01-02T03", Format(hour - 1));
-  EXPECT_EQ("2015-01-02T03", Format(hour -= 1));
-  EXPECT_EQ("2015-01-02T03", Format(hour++));
-  EXPECT_EQ("2015-01-02T05", Format(++hour));
-  EXPECT_EQ("2015-01-02T05", Format(hour--));
-  EXPECT_EQ("2015-01-02T03", Format(--hour));
-
-  civil_day day(2015, 1, 2);
-  EXPECT_EQ("2015-01-03", Format(day += 1));
-  EXPECT_EQ("2015-01-04", Format(day + 1));
-  EXPECT_EQ("2015-01-05", Format(2 + day));
-  EXPECT_EQ("2015-01-02", Format(day - 1));
-  EXPECT_EQ("2015-01-02", Format(day -= 1));
-  EXPECT_EQ("2015-01-02", Format(day++));
-  EXPECT_EQ("2015-01-04", Format(++day));
-  EXPECT_EQ("2015-01-04", Format(day--));
-  EXPECT_EQ("2015-01-02", Format(--day));
-
-  civil_month month(2015, 1);
-  EXPECT_EQ("2015-02", Format(month += 1));
-  EXPECT_EQ("2015-03", Format(month + 1));
-  EXPECT_EQ("2015-04", Format(2 + month));
-  EXPECT_EQ("2015-01", Format(month - 1));
-  EXPECT_EQ("2015-01", Format(month -= 1));
-  EXPECT_EQ("2015-01", Format(month++));
-  EXPECT_EQ("2015-03", Format(++month));
-  EXPECT_EQ("2015-03", Format(month--));
-  EXPECT_EQ("2015-01", Format(--month));
-
-  civil_year year(2015);
-  EXPECT_EQ("2016", Format(year += 1));
-  EXPECT_EQ("2017", Format(year + 1));
-  EXPECT_EQ("2018", Format(2 + year));
-  EXPECT_EQ("2015", Format(year - 1));
-  EXPECT_EQ("2015", Format(year -= 1));
-  EXPECT_EQ("2015", Format(year++));
-  EXPECT_EQ("2017", Format(++year));
-  EXPECT_EQ("2017", Format(year--));
-  EXPECT_EQ("2015", Format(--year));
-}
-
-TEST(CivilTime, ArithmeticLimits) {
-  const int kIntMax = std::numeric_limits<int>::max();
-  const int kIntMin = std::numeric_limits<int>::min();
-
-  civil_second second(1970, 1, 1, 0, 0, 0);
-  second += kIntMax;
-  EXPECT_EQ("2038-01-19T03:14:07", Format(second));
-  second -= kIntMax;
-  EXPECT_EQ("1970-01-01T00:00:00", Format(second));
-  second += kIntMin;
-  EXPECT_EQ("1901-12-13T20:45:52", Format(second));
-  second -= kIntMin;
-  EXPECT_EQ("1970-01-01T00:00:00", Format(second));
-
-  civil_minute minute(1970, 1, 1, 0, 0);
-  minute += kIntMax;
-  EXPECT_EQ("6053-01-23T02:07", Format(minute));
-  minute -= kIntMax;
-  EXPECT_EQ("1970-01-01T00:00", Format(minute));
-  minute += kIntMin;
-  EXPECT_EQ("-2114-12-08T21:52", Format(minute));
-  minute -= kIntMin;
-  EXPECT_EQ("1970-01-01T00:00", Format(minute));
-
-  civil_hour hour(1970, 1, 1, 0);
-  hour += kIntMax;
-  EXPECT_EQ("246953-10-09T07", Format(hour));
-  hour -= kIntMax;
-  EXPECT_EQ("1970-01-01T00", Format(hour));
-  hour += kIntMin;
-  EXPECT_EQ("-243014-03-24T16", Format(hour));
-  hour -= kIntMin;
-  EXPECT_EQ("1970-01-01T00", Format(hour));
-
-  civil_day day(1970, 1, 1);
-  day += kIntMax;
-  EXPECT_EQ("5881580-07-11", Format(day));
-  day -= kIntMax;
-  EXPECT_EQ("1970-01-01", Format(day));
-  day += kIntMin;
-  EXPECT_EQ("-5877641-06-23", Format(day));
-  day -= kIntMin;
-  EXPECT_EQ("1970-01-01", Format(day));
-
-  civil_month month(1970, 1);
-  month += kIntMax;
-  EXPECT_EQ("178958940-08", Format(month));
-  month -= kIntMax;
-  EXPECT_EQ("1970-01", Format(month));
-  month += kIntMin;
-  EXPECT_EQ("-178955001-05", Format(month));
-  month -= kIntMin;
-  EXPECT_EQ("1970-01", Format(month));
-
-  civil_year year(0);
-  year += kIntMax;
-  EXPECT_EQ("2147483647", Format(year));
-  year -= kIntMax;
-  EXPECT_EQ("0", Format(year));
-  year += kIntMin;
-  EXPECT_EQ("-2147483648", Format(year));
-  year -= kIntMin;
-  EXPECT_EQ("0", Format(year));
-}
-
-TEST(CivilTime, ArithmeticDifference) {
-  civil_second second(2015, 1, 2, 3, 4, 5);
-  EXPECT_EQ(0, second - second);
-  EXPECT_EQ(10, (second + 10) - second);
-  EXPECT_EQ(-10, (second - 10) - second);
-
-  civil_minute minute(2015, 1, 2, 3, 4);
-  EXPECT_EQ(0, minute - minute);
-  EXPECT_EQ(10, (minute + 10) - minute);
-  EXPECT_EQ(-10, (minute - 10) - minute);
-
-  civil_hour hour(2015, 1, 2, 3);
-  EXPECT_EQ(0, hour - hour);
-  EXPECT_EQ(10, (hour + 10) - hour);
-  EXPECT_EQ(-10, (hour - 10) - hour);
-
-  civil_day day(2015, 1, 2);
-  EXPECT_EQ(0, day - day);
-  EXPECT_EQ(10, (day + 10) - day);
-  EXPECT_EQ(-10, (day - 10) - day);
-
-  civil_month month(2015, 1);
-  EXPECT_EQ(0, month - month);
-  EXPECT_EQ(10, (month + 10) - month);
-  EXPECT_EQ(-10, (month - 10) - month);
-
-  civil_year year(2015);
-  EXPECT_EQ(0, year - year);
-  EXPECT_EQ(10, (year + 10) - year);
-  EXPECT_EQ(-10, (year - 10) - year);
-}
-
-TEST(CivilTime, DifferenceLimits) {
-  const int kIntMax = std::numeric_limits<int>::max();
-  const int kIntMin = std::numeric_limits<int>::min();
-
-  // Check day arithmetic at the end of the year range.
-  const civil_day max_day(kIntMax, 12, 31);
-  EXPECT_EQ(1, max_day - (max_day - 1));
-  EXPECT_EQ(-1, (max_day - 1) - max_day);
-
-  // Check day arithmetic at the end of the year range.
-  const civil_day min_day(kIntMin, 1, 1);
-  EXPECT_EQ(1, (min_day + 1) - min_day);
-  EXPECT_EQ(-1, min_day - (min_day + 1));
-
-  // Check the limits of the return value.
-  const civil_day d1(1970, 1, 1);
-  const civil_day d2(5881580, 7, 11);
-  EXPECT_EQ(kIntMax, d2 - d1);
-  EXPECT_EQ(kIntMin, d1 - (d2 + 1));
-}
-
-TEST(CivilTime, Properties) {
-  civil_second ss(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, ss.year());
-  EXPECT_EQ(2, ss.month());
-  EXPECT_EQ(3, ss.day());
-  EXPECT_EQ(4, ss.hour());
-  EXPECT_EQ(5, ss.minute());
-  EXPECT_EQ(6, ss.second());
-  EXPECT_EQ(weekday::tuesday, get_weekday(ss));
-  EXPECT_EQ(34, get_yearday(ss));
-
-  civil_minute mm(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, mm.year());
-  EXPECT_EQ(2, mm.month());
-  EXPECT_EQ(3, mm.day());
-  EXPECT_EQ(4, mm.hour());
-  EXPECT_EQ(5, mm.minute());
-  EXPECT_EQ(0, mm.second());
-  EXPECT_EQ(weekday::tuesday, get_weekday(mm));
-  EXPECT_EQ(34, get_yearday(mm));
-
-  civil_hour hh(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, hh.year());
-  EXPECT_EQ(2, hh.month());
-  EXPECT_EQ(3, hh.day());
-  EXPECT_EQ(4, hh.hour());
-  EXPECT_EQ(0, hh.minute());
-  EXPECT_EQ(0, hh.second());
-  EXPECT_EQ(weekday::tuesday, get_weekday(hh));
-  EXPECT_EQ(34, get_yearday(hh));
-
-  civil_day d(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, d.year());
-  EXPECT_EQ(2, d.month());
-  EXPECT_EQ(3, d.day());
-  EXPECT_EQ(0, d.hour());
-  EXPECT_EQ(0, d.minute());
-  EXPECT_EQ(0, d.second());
-  EXPECT_EQ(weekday::tuesday, get_weekday(d));
-  EXPECT_EQ(34, get_yearday(d));
-
-  civil_month m(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, m.year());
-  EXPECT_EQ(2, m.month());
-  EXPECT_EQ(1, m.day());
-  EXPECT_EQ(0, m.hour());
-  EXPECT_EQ(0, m.minute());
-  EXPECT_EQ(0, m.second());
-  EXPECT_EQ(weekday::sunday, get_weekday(m));
-  EXPECT_EQ(32, get_yearday(m));
-
-  civil_year y(2015, 2, 3, 4, 5, 6);
-  EXPECT_EQ(2015, y.year());
-  EXPECT_EQ(1, y.month());
-  EXPECT_EQ(1, y.day());
-  EXPECT_EQ(0, y.hour());
-  EXPECT_EQ(0, y.minute());
-  EXPECT_EQ(0, y.second());
-  EXPECT_EQ(weekday::thursday, get_weekday(y));
-  EXPECT_EQ(1, get_yearday(y));
-}
-
-TEST(CivilTime, OutputStream) {
-  // Tests formatting of civil_year, which does not pad.
-  EXPECT_EQ("2016", Format(civil_year(2016)));
-  EXPECT_EQ("123", Format(civil_year(123)));
-  EXPECT_EQ("0", Format(civil_year(0)));
-  EXPECT_EQ("-1", Format(civil_year(-1)));
-
-  // Tests formatting of sub-year types, which pad to 2 digits
-  EXPECT_EQ("2016-02", Format(civil_month(2016, 2)));
-  EXPECT_EQ("2016-02-03", Format(civil_day(2016, 2, 3)));
-  EXPECT_EQ("2016-02-03T04", Format(civil_hour(2016, 2, 3, 4)));
-  EXPECT_EQ("2016-02-03T04:05", Format(civil_minute(2016, 2, 3, 4, 5)));
-  EXPECT_EQ("2016-02-03T04:05:06", Format(civil_second(2016, 2, 3, 4, 5, 6)));
-
-  // Tests formatting of weekday.
-  EXPECT_EQ("Monday", Format(weekday::monday));
-  EXPECT_EQ("Tuesday", Format(weekday::tuesday));
-  EXPECT_EQ("Wednesday", Format(weekday::wednesday));
-  EXPECT_EQ("Thursday", Format(weekday::thursday));
-  EXPECT_EQ("Friday", Format(weekday::friday));
-  EXPECT_EQ("Saturday", Format(weekday::saturday));
-  EXPECT_EQ("Sunday", Format(weekday::sunday));
-}
-
-TEST(CivilTime, OutputStreamLeftFillWidth) {
-  civil_second cs(2016, 2, 3, 4, 5, 6);
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << civil_year(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016.................X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << civil_month(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02..............X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << civil_day(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02-03...........X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << civil_hour(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02-03T04........X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << civil_minute(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02-03T04:05.....X..", ss.str());
-  }
-  {
-    std::stringstream ss;
-    ss << std::left << std::setfill('.');
-    ss << std::setw(3) << 'X';
-    ss << std::setw(21) << civil_second(cs);
-    ss << std::setw(3) << 'X';
-    EXPECT_EQ("X..2016-02-03T04:05:06..X..", ss.str());
-  }
-}
-
-TEST(CivilTime, NextPrevWeekday) {
-  // Jan 1, 1970 was a Thursday.
-  const civil_day thursday(1970, 1, 1);
-  EXPECT_EQ(weekday::thursday, get_weekday(thursday));
-
-  // Thursday -> Thursday
-  civil_day d = next_weekday(thursday, weekday::thursday);
-  EXPECT_EQ(7, d - thursday) << Format(d);
-  EXPECT_EQ(d - 14, prev_weekday(thursday, weekday::thursday));
-
-  // Thursday -> Friday
-  d = next_weekday(thursday, weekday::friday);
-  EXPECT_EQ(1, d - thursday) << Format(d);
-  EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::friday));
-
-  // Thursday -> Saturday
-  d = next_weekday(thursday, weekday::saturday);
-  EXPECT_EQ(2, d - thursday) << Format(d);
-  EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::saturday));
-
-  // Thursday -> Sunday
-  d = next_weekday(thursday, weekday::sunday);
-  EXPECT_EQ(3, d - thursday) << Format(d);
-  EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::sunday));
-
-  // Thursday -> Monday
-  d = next_weekday(thursday, weekday::monday);
-  EXPECT_EQ(4, d - thursday) << Format(d);
-  EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::monday));
-
-  // Thursday -> Tuesday
-  d = next_weekday(thursday, weekday::tuesday);
-  EXPECT_EQ(5, d - thursday) << Format(d);
-  EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::tuesday));
-
-  // Thursday -> Wednesday
-  d = next_weekday(thursday, weekday::wednesday);
-  EXPECT_EQ(6, d - thursday) << Format(d);
-  EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::wednesday));
-}
-
-TEST(CivilTime, NormalizeWithHugeYear) {
-  civil_month c(9223372036854775807, 1);
-  EXPECT_EQ("9223372036854775807-01", Format(c));
-  c = c - 1;  // Causes normalization
-  EXPECT_EQ("9223372036854775806-12", Format(c));
-
-  c = civil_month(-9223372036854775807 - 1, 1);
-  EXPECT_EQ("-9223372036854775808-01", Format(c));
-  c = c + 12;  // Causes normalization
-  EXPECT_EQ("-9223372036854775807-01", Format(c));
-}
-
-TEST(CivilTime, LeapYears) {
-  // Test data for leap years.
-  const struct {
-    int year;
-    int days;
-    struct {
-      int month;
-      int day;
-    } leap_day;  // The date of the day after Feb 28.
-  } kLeapYearTable[]{
-      {1900, 365, {3, 1}},  {1999, 365, {3, 1}},
-      {2000, 366, {2, 29}},  // leap year
-      {2001, 365, {3, 1}},  {2002, 365, {3, 1}},
-      {2003, 365, {3, 1}},  {2004, 366, {2, 29}},  // leap year
-      {2005, 365, {3, 1}},  {2006, 365, {3, 1}},
-      {2007, 365, {3, 1}},  {2008, 366, {2, 29}},  // leap year
-      {2009, 365, {3, 1}},  {2100, 365, {3, 1}},
-  };
-
-  for (const auto& e : kLeapYearTable) {
-    // Tests incrementing through the leap day.
-    const civil_day feb28(e.year, 2, 28);
-    const civil_day next_day = feb28 + 1;
-    EXPECT_EQ(e.leap_day.month, next_day.month());
-    EXPECT_EQ(e.leap_day.day, next_day.day());
-
-    // Tests difference in days of leap years.
-    const civil_year year(feb28);
-    const civil_year next_year = year + 1;
-    EXPECT_EQ(e.days, civil_day(next_year) - civil_day(year));
-  }
-}
-
-TEST(CivilTime, FirstThursdayInMonth) {
-  const civil_day nov1(2014, 11, 1);
-  const civil_day thursday = next_weekday(nov1 - 1, weekday::thursday);
-  EXPECT_EQ("2014-11-06", Format(thursday));
-
-  // Bonus: Date of Thanksgiving in the United States
-  // Rule: Fourth Thursday of November
-  const civil_day thanksgiving = thursday + 7 * 3;
-  EXPECT_EQ("2014-11-27", Format(thanksgiving));
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.cc
deleted file mode 100644
index 303c0244a8..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include "time_zone_fixed.h"
-
-#include <algorithm>
-#include <cassert>
-#include <chrono>
-#include <cstring>
-#include <string>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-namespace {
-
-// The prefix used for the internal names of fixed-offset zones.
-const char kFixedZonePrefix[] = "Fixed/UTC";
-
-const char kDigits[] = "0123456789";
-
-char* Format02d(char* p, int v) {
-  *p++ = kDigits[(v / 10) % 10];
-  *p++ = kDigits[v % 10];
-  return p;
-}
-
-int Parse02d(const char* p) {
-  if (const char* ap = std::strchr(kDigits, *p)) {
-    int v = static_cast<int>(ap - kDigits);
-    if (const char* bp = std::strchr(kDigits, *++p)) {
-      return (v * 10) + static_cast<int>(bp - kDigits);
-    }
-  }
-  return -1;
-}
-
-}  // namespace
-
-bool FixedOffsetFromName(const std::string& name, seconds* offset) {
-  if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
-    *offset = seconds::zero();
-    return true;
-  }
-
-  const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
-  const char* const ep = kFixedZonePrefix + prefix_len;
-  if (name.size() != prefix_len + 9)  // <prefix>+99:99:99
-    return false;
-  if (!std::equal(kFixedZonePrefix, ep, name.begin())) return false;
-  const char* np = name.data() + prefix_len;
-  if (np[0] != '+' && np[0] != '-') return false;
-  if (np[3] != ':' || np[6] != ':')  // see note below about large offsets
-    return false;
-
-  int hours = Parse02d(np + 1);
-  if (hours == -1) return false;
-  int mins = Parse02d(np + 4);
-  if (mins == -1) return false;
-  int secs = Parse02d(np + 7);
-  if (secs == -1) return false;
-
-  secs += ((hours * 60) + mins) * 60;
-  if (secs > 24 * 60 * 60) return false;  // outside supported offset range
-  *offset = seconds(secs * (np[0] == '-' ? -1 : 1));  // "-" means west
-  return true;
-}
-
-std::string FixedOffsetToName(const seconds& offset) {
-  if (offset == seconds::zero()) return "UTC";
-  if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) {
-    // We don't support fixed-offset zones more than 24 hours
-    // away from UTC to avoid complications in rendering such
-    // offsets and to (somewhat) limit the total number of zones.
-    return "UTC";
-  }
-  int offset_seconds = static_cast<int>(offset.count());
-  const char sign = (offset_seconds < 0 ? '-' : '+');
-  int offset_minutes = offset_seconds / 60;
-  offset_seconds %= 60;
-  if (sign == '-') {
-    if (offset_seconds > 0) {
-      offset_seconds -= 60;
-      offset_minutes += 1;
-    }
-    offset_seconds = -offset_seconds;
-    offset_minutes = -offset_minutes;
-  }
-  int offset_hours = offset_minutes / 60;
-  offset_minutes %= 60;
-  const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
-  char buf[prefix_len + sizeof("-24:00:00")];
-  char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf);
-  *ep++ = sign;
-  ep = Format02d(ep, offset_hours);
-  *ep++ = ':';
-  ep = Format02d(ep, offset_minutes);
-  *ep++ = ':';
-  ep = Format02d(ep, offset_seconds);
-  *ep++ = '\0';
-  assert(ep == buf + sizeof(buf));
-  return buf;
-}
-
-std::string FixedOffsetToAbbr(const seconds& offset) {
-  std::string abbr = FixedOffsetToName(offset);
-  const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
-  if (abbr.size() == prefix_len + 9) {         // <prefix>+99:99:99
-    abbr.erase(0, prefix_len);                 // +99:99:99
-    abbr.erase(6, 1);                          // +99:9999
-    abbr.erase(3, 1);                          // +999999
-    if (abbr[5] == '0' && abbr[6] == '0') {    // +999900
-      abbr.erase(5, 2);                        // +9999
-      if (abbr[3] == '0' && abbr[4] == '0') {  // +9900
-        abbr.erase(3, 2);                      // +99
-      }
-    }
-  }
-  return abbr;
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.h b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.h
deleted file mode 100644
index e74a0bbd9d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
-#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
-
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// Helper functions for dealing with the names and abbreviations
-// of time zones that are a fixed offset (seconds east) from UTC.
-// FixedOffsetFromName() extracts the offset from a valid fixed-offset
-// name, while FixedOffsetToName() and FixedOffsetToAbbr() generate
-// the canonical zone name and abbreviation respectively for the given
-// offset.
-//
-// A fixed-offset name looks like "Fixed/UTC<+-><hours>:<mins>:<secs>".
-// Its abbreviation is of the form "UTC(<+->H?H(MM(SS)?)?)?" where the
-// optional pieces are omitted when their values are zero.  (Note that
-// the sign is the opposite of that used in a POSIX TZ specification.)
-//
-// Note: FixedOffsetFromName() fails on syntax errors or when the parsed
-// offset exceeds 24 hours.  FixedOffsetToName() and FixedOffsetToAbbr()
-// both produce "UTC" when the argument offset exceeds 24 hours.
-bool FixedOffsetFromName(const std::string& name, seconds* offset);
-std::string FixedOffsetToName(const seconds& offset);
-std::string FixedOffsetToAbbr(const seconds& offset);
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format.cc
deleted file mode 100644
index d8cb047425..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format.cc
+++ /dev/null
@@ -1,1029 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#if !defined(HAS_STRPTIME)
-#if !defined(_MSC_VER) && !defined(__MINGW32__)
-#define HAS_STRPTIME 1  // assume everyone has strptime() except windows
-#endif
-#endif
-
-#if defined(HAS_STRPTIME) && HAS_STRPTIME
-#if !defined(_XOPEN_SOURCE)
-#define _XOPEN_SOURCE  // Definedness suffices for strptime.
-#endif
-#endif
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-// Include time.h directly since, by C++ standards, ctime doesn't have to
-// declare strptime.
-#include <time.h>
-
-#include <cctype>
-#include <chrono>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <ctime>
-#include <limits>
-#include <string>
-#include <vector>
-#if !HAS_STRPTIME
-#include <iomanip>
-#include <sstream>
-#endif
-
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "time_zone_if.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-namespace detail {
-
-namespace {
-
-#if !HAS_STRPTIME
-// Build a strptime() using C++11's std::get_time().
-char* strptime(const char* s, const char* fmt, std::tm* tm) {
-  std::istringstream input(s);
-  input >> std::get_time(tm, fmt);
-  if (input.fail()) return nullptr;
-  return const_cast<char*>(s) +
-         (input.eof() ? strlen(s) : static_cast<std::size_t>(input.tellg()));
-}
-#endif
-
-// Convert a cctz::weekday to a tm_wday value (0-6, Sunday = 0).
-int ToTmWday(weekday wd) {
-  switch (wd) {
-    case weekday::sunday:
-      return 0;
-    case weekday::monday:
-      return 1;
-    case weekday::tuesday:
-      return 2;
-    case weekday::wednesday:
-      return 3;
-    case weekday::thursday:
-      return 4;
-    case weekday::friday:
-      return 5;
-    case weekday::saturday:
-      return 6;
-  }
-  return 0; /*NOTREACHED*/
-}
-
-// Convert a tm_wday value (0-6, Sunday = 0) to a cctz::weekday.
-weekday FromTmWday(int tm_wday) {
-  switch (tm_wday) {
-    case 0:
-      return weekday::sunday;
-    case 1:
-      return weekday::monday;
-    case 2:
-      return weekday::tuesday;
-    case 3:
-      return weekday::wednesday;
-    case 4:
-      return weekday::thursday;
-    case 5:
-      return weekday::friday;
-    case 6:
-      return weekday::saturday;
-  }
-  return weekday::sunday; /*NOTREACHED*/
-}
-
-std::tm ToTM(const time_zone::absolute_lookup& al) {
-  std::tm tm{};
-  tm.tm_sec = al.cs.second();
-  tm.tm_min = al.cs.minute();
-  tm.tm_hour = al.cs.hour();
-  tm.tm_mday = al.cs.day();
-  tm.tm_mon = al.cs.month() - 1;
-
-  // Saturate tm.tm_year is cases of over/underflow.
-  if (al.cs.year() < std::numeric_limits<int>::min() + 1900) {
-    tm.tm_year = std::numeric_limits<int>::min();
-  } else if (al.cs.year() - 1900 > std::numeric_limits<int>::max()) {
-    tm.tm_year = std::numeric_limits<int>::max();
-  } else {
-    tm.tm_year = static_cast<int>(al.cs.year() - 1900);
-  }
-
-  tm.tm_wday = ToTmWday(get_weekday(al.cs));
-  tm.tm_yday = get_yearday(al.cs) - 1;
-  tm.tm_isdst = al.is_dst ? 1 : 0;
-  return tm;
-}
-
-// Returns the week of the year [0:53] given a civil day and the day on
-// which weeks are defined to start.
-int ToWeek(const civil_day& cd, weekday week_start) {
-  const civil_day d(cd.year() % 400, cd.month(), cd.day());
-  return static_cast<int>((d - prev_weekday(civil_year(d), week_start)) / 7);
-}
-
-const char kDigits[] = "0123456789";
-
-// Formats a 64-bit integer in the given field width.  Note that it is up
-// to the caller of Format64() [and Format02d()/FormatOffset()] to ensure
-// that there is sufficient space before ep to hold the conversion.
-char* Format64(char* ep, int width, std::int_fast64_t v) {
-  bool neg = false;
-  if (v < 0) {
-    --width;
-    neg = true;
-    if (v == std::numeric_limits<std::int_fast64_t>::min()) {
-      // Avoid negating minimum value.
-      std::int_fast64_t last_digit = -(v % 10);
-      v /= 10;
-      if (last_digit < 0) {
-        ++v;
-        last_digit += 10;
-      }
-      --width;
-      *--ep = kDigits[last_digit];
-    }
-    v = -v;
-  }
-  do {
-    --width;
-    *--ep = kDigits[v % 10];
-  } while (v /= 10);
-  while (--width >= 0) *--ep = '0';  // zero pad
-  if (neg) *--ep = '-';
-  return ep;
-}
-
-// Formats [0 .. 99] as %02d.
-char* Format02d(char* ep, int v) {
-  *--ep = kDigits[v % 10];
-  *--ep = kDigits[(v / 10) % 10];
-  return ep;
-}
-
-// Formats a UTC offset, like +00:00.
-char* FormatOffset(char* ep, int offset, const char* mode) {
-  // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
-  // generate a "negative zero" when we're formatting a zero offset
-  // as the result of a failed load_time_zone().
-  char sign = '+';
-  if (offset < 0) {
-    offset = -offset;  // bounded by 24h so no overflow
-    sign = '-';
-  }
-  const int seconds = offset % 60;
-  const int minutes = (offset /= 60) % 60;
-  const int hours = offset /= 60;
-  const char sep = mode[0];
-  const bool ext = (sep != '\0' && mode[1] == '*');
-  const bool ccc = (ext && mode[2] == ':');
-  if (ext && (!ccc || seconds != 0)) {
-    ep = Format02d(ep, seconds);
-    *--ep = sep;
-  } else {
-    // If we're not rendering seconds, sub-minute negative offsets
-    // should get a positive sign (e.g., offset=-10s => "+00:00").
-    if (hours == 0 && minutes == 0) sign = '+';
-  }
-  if (!ccc || minutes != 0 || seconds != 0) {
-    ep = Format02d(ep, minutes);
-    if (sep != '\0') *--ep = sep;
-  }
-  ep = Format02d(ep, hours);
-  *--ep = sign;
-  return ep;
-}
-
-// Formats a std::tm using strftime(3).
-void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) {
-  // strftime(3) returns the number of characters placed in the output
-  // array (which may be 0 characters).  It also returns 0 to indicate
-  // an error, like the array wasn't large enough.  To accommodate this,
-  // the following code grows the buffer size from 2x the format string
-  // length up to 32x.
-  for (std::size_t i = 2; i != 32; i *= 2) {
-    std::size_t buf_size = fmt.size() * i;
-    std::vector<char> buf(buf_size);
-    if (std::size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) {
-      out->append(&buf[0], len);
-      return;
-    }
-  }
-}
-
-// Used for %E#S/%E#f specifiers and for data values in parse().
-template <typename T>
-const char* ParseInt(const char* dp, int width, T min, T max, T* vp) {
-  if (dp != nullptr) {
-    const T kmin = std::numeric_limits<T>::min();
-    bool erange = false;
-    bool neg = false;
-    T value = 0;
-    if (*dp == '-') {
-      neg = true;
-      if (width <= 0 || --width != 0) {
-        ++dp;
-      } else {
-        dp = nullptr;  // width was 1
-      }
-    }
-    if (const char* const bp = dp) {
-      while (const char* cp = strchr(kDigits, *dp)) {
-        int d = static_cast<int>(cp - kDigits);
-        if (d >= 10) break;
-        if (value < kmin / 10) {
-          erange = true;
-          break;
-        }
-        value *= 10;
-        if (value < kmin + d) {
-          erange = true;
-          break;
-        }
-        value -= d;
-        dp += 1;
-        if (width > 0 && --width == 0) break;
-      }
-      if (dp != bp && !erange && (neg || value != kmin)) {
-        if (!neg || value != 0) {
-          if (!neg) value = -value;  // make positive
-          if (min <= value && value <= max) {
-            *vp = value;
-          } else {
-            dp = nullptr;
-          }
-        } else {
-          dp = nullptr;
-        }
-      } else {
-        dp = nullptr;
-      }
-    }
-  }
-  return dp;
-}
-
-// The number of base-10 digits that can be represented by a signed 64-bit
-// integer.  That is, 10^kDigits10_64 <= 2^63 - 1 < 10^(kDigits10_64 + 1).
-const int kDigits10_64 = 18;
-
-// 10^n for everything that can be represented by a signed 64-bit integer.
-const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
-    1,
-    10,
-    100,
-    1000,
-    10000,
-    100000,
-    1000000,
-    10000000,
-    100000000,
-    1000000000,
-    10000000000,
-    100000000000,
-    1000000000000,
-    10000000000000,
-    100000000000000,
-    1000000000000000,
-    10000000000000000,
-    100000000000000000,
-    1000000000000000000,
-};
-
-}  // namespace
-
-// Uses strftime(3) to format the given Time.  The following extended format
-// specifiers are also supported:
-//
-//   - %Ez  - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
-//   - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
-//   - %E#S - Seconds with # digits of fractional precision
-//   - %E*S - Seconds with full fractional precision (a literal '*')
-//   - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
-//   - %ET  - The RFC3339 "date-time" separator "T"
-//
-// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
-// handled internally for performance reasons.  strftime(3) is slow due to
-// a POSIX requirement to respect changes to ${TZ}.
-//
-// The TZ/GNU %s extension is handled internally because strftime() has
-// to use mktime() to generate it, and that assumes the local time zone.
-//
-// We also handle the %z and %Z specifiers to accommodate platforms that do
-// not support the tm_gmtoff and tm_zone extensions to std::tm.
-//
-// Requires that zero() <= fs < seconds(1).
-std::string format(const std::string& format, const time_point<seconds>& tp,
-                   const detail::femtoseconds& fs, const time_zone& tz) {
-  std::string result;
-  result.reserve(format.size());  // A reasonable guess for the result size.
-  const time_zone::absolute_lookup al = tz.lookup(tp);
-  const std::tm tm = ToTM(al);
-
-  // Scratch buffer for internal conversions.
-  char buf[3 + kDigits10_64];  // enough for longest conversion
-  char* const ep = buf + sizeof(buf);
-  char* bp;  // works back from ep
-
-  // Maintain three, disjoint subsequences that span format.
-  //   [format.begin() ... pending) : already formatted into result
-  //   [pending ... cur) : formatting pending, but no special cases
-  //   [cur ... format.end()) : unexamined
-  // Initially, everything is in the unexamined part.
-  const char* pending = format.c_str();  // NUL terminated
-  const char* cur = pending;
-  const char* end = pending + format.length();
-
-  while (cur != end) {  // while something is unexamined
-    // Moves cur to the next percent sign.
-    const char* start = cur;
-    while (cur != end && *cur != '%') ++cur;
-
-    // If the new pending text is all ordinary, copy it out.
-    if (cur != start && pending == start) {
-      result.append(pending, static_cast<std::size_t>(cur - pending));
-      pending = start = cur;
-    }
-
-    // Span the sequential percent signs.
-    const char* percent = cur;
-    while (cur != end && *cur == '%') ++cur;
-
-    // If the new pending text is all percents, copy out one
-    // percent for every matched pair, then skip those pairs.
-    if (cur != start && pending == start) {
-      std::size_t escaped = static_cast<std::size_t>(cur - pending) / 2;
-      result.append(pending, escaped);
-      pending += escaped * 2;
-      // Also copy out a single trailing percent.
-      if (pending != cur && cur == end) {
-        result.push_back(*pending++);
-      }
-    }
-
-    // Loop unless we have an unescaped percent.
-    if (cur == end || (cur - percent) % 2 == 0) continue;
-
-    // Simple specifiers that we handle ourselves.
-    if (strchr("YmdeUuWwHMSzZs%", *cur)) {
-      if (cur - 1 != pending) {
-        FormatTM(&result, std::string(pending, cur - 1), tm);
-      }
-      switch (*cur) {
-        case 'Y':
-          // This avoids the tm.tm_year overflow problem for %Y, however
-          // tm.tm_year will still be used by other specifiers like %D.
-          bp = Format64(ep, 0, al.cs.year());
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'm':
-          bp = Format02d(ep, al.cs.month());
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'd':
-        case 'e':
-          bp = Format02d(ep, al.cs.day());
-          if (*cur == 'e' && *bp == '0') *bp = ' ';  // for Windows
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'U':
-          bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::sunday));
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'u':
-          bp = Format64(ep, 0, tm.tm_wday ? tm.tm_wday : 7);
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'W':
-          bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::monday));
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'w':
-          bp = Format64(ep, 0, tm.tm_wday);
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'H':
-          bp = Format02d(ep, al.cs.hour());
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'M':
-          bp = Format02d(ep, al.cs.minute());
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'S':
-          bp = Format02d(ep, al.cs.second());
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'z':
-          bp = FormatOffset(ep, al.offset, "");
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case 'Z':
-          result.append(al.abbr);
-          break;
-        case 's':
-          bp = Format64(ep, 0, ToUnixSeconds(tp));
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          break;
-        case '%':
-          result.push_back('%');
-          break;
-      }
-      pending = ++cur;
-      continue;
-    }
-
-    // More complex specifiers that we handle ourselves.
-    if (*cur == ':' && cur + 1 != end) {
-      if (*(cur + 1) == 'z') {
-        // Formats %:z.
-        if (cur - 1 != pending) {
-          FormatTM(&result, std::string(pending, cur - 1), tm);
-        }
-        bp = FormatOffset(ep, al.offset, ":");
-        result.append(bp, static_cast<std::size_t>(ep - bp));
-        pending = cur += 2;
-        continue;
-      }
-      if (*(cur + 1) == ':' && cur + 2 != end) {
-        if (*(cur + 2) == 'z') {
-          // Formats %::z.
-          if (cur - 1 != pending) {
-            FormatTM(&result, std::string(pending, cur - 1), tm);
-          }
-          bp = FormatOffset(ep, al.offset, ":*");
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          pending = cur += 3;
-          continue;
-        }
-        if (*(cur + 2) == ':' && cur + 3 != end) {
-          if (*(cur + 3) == 'z') {
-            // Formats %:::z.
-            if (cur - 1 != pending) {
-              FormatTM(&result, std::string(pending, cur - 1), tm);
-            }
-            bp = FormatOffset(ep, al.offset, ":*:");
-            result.append(bp, static_cast<std::size_t>(ep - bp));
-            pending = cur += 4;
-            continue;
-          }
-        }
-      }
-    }
-
-    // Loop if there is no E modifier.
-    if (*cur != 'E' || ++cur == end) continue;
-
-    // Format our extensions.
-    if (*cur == 'T') {
-      // Formats %ET.
-      if (cur - 2 != pending) {
-        FormatTM(&result, std::string(pending, cur - 2), tm);
-      }
-      result.append("T");
-      pending = ++cur;
-    } else if (*cur == 'z') {
-      // Formats %Ez.
-      if (cur - 2 != pending) {
-        FormatTM(&result, std::string(pending, cur - 2), tm);
-      }
-      bp = FormatOffset(ep, al.offset, ":");
-      result.append(bp, static_cast<std::size_t>(ep - bp));
-      pending = ++cur;
-    } else if (*cur == '*' && cur + 1 != end && *(cur + 1) == 'z') {
-      // Formats %E*z.
-      if (cur - 2 != pending) {
-        FormatTM(&result, std::string(pending, cur - 2), tm);
-      }
-      bp = FormatOffset(ep, al.offset, ":*");
-      result.append(bp, static_cast<std::size_t>(ep - bp));
-      pending = cur += 2;
-    } else if (*cur == '*' && cur + 1 != end &&
-               (*(cur + 1) == 'S' || *(cur + 1) == 'f')) {
-      // Formats %E*S or %E*F.
-      if (cur - 2 != pending) {
-        FormatTM(&result, std::string(pending, cur - 2), tm);
-      }
-      char* cp = ep;
-      bp = Format64(cp, 15, fs.count());
-      while (cp != bp && cp[-1] == '0') --cp;
-      switch (*(cur + 1)) {
-        case 'S':
-          if (cp != bp) *--bp = '.';
-          bp = Format02d(bp, al.cs.second());
-          break;
-        case 'f':
-          if (cp == bp) *--bp = '0';
-          break;
-      }
-      result.append(bp, static_cast<std::size_t>(cp - bp));
-      pending = cur += 2;
-    } else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') {
-      // Formats %E4Y.
-      if (cur - 2 != pending) {
-        FormatTM(&result, std::string(pending, cur - 2), tm);
-      }
-      bp = Format64(ep, 4, al.cs.year());
-      result.append(bp, static_cast<std::size_t>(ep - bp));
-      pending = cur += 2;
-    } else if (std::isdigit(*cur)) {
-      // Possibly found %E#S or %E#f.
-      int n = 0;
-      if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) {
-        if (*np == 'S' || *np == 'f') {
-          // Formats %E#S or %E#f.
-          if (cur - 2 != pending) {
-            FormatTM(&result, std::string(pending, cur - 2), tm);
-          }
-          bp = ep;
-          if (n > 0) {
-            if (n > kDigits10_64) n = kDigits10_64;
-            bp = Format64(bp, n,
-                          (n > 15) ? fs.count() * kExp10[n - 15]
-                                   : fs.count() / kExp10[15 - n]);
-            if (*np == 'S') *--bp = '.';
-          }
-          if (*np == 'S') bp = Format02d(bp, al.cs.second());
-          result.append(bp, static_cast<std::size_t>(ep - bp));
-          pending = cur = ++np;
-        }
-      }
-    }
-  }
-
-  // Formats any remaining data.
-  if (end != pending) {
-    FormatTM(&result, std::string(pending, end), tm);
-  }
-
-  return result;
-}
-
-namespace {
-
-const char* ParseOffset(const char* dp, const char* mode, int* offset) {
-  if (dp != nullptr) {
-    const char first = *dp++;
-    if (first == '+' || first == '-') {
-      char sep = mode[0];
-      int hours = 0;
-      int minutes = 0;
-      int seconds = 0;
-      const char* ap = ParseInt(dp, 2, 0, 23, &hours);
-      if (ap != nullptr && ap - dp == 2) {
-        dp = ap;
-        if (sep != '\0' && *ap == sep) ++ap;
-        const char* bp = ParseInt(ap, 2, 0, 59, &minutes);
-        if (bp != nullptr && bp - ap == 2) {
-          dp = bp;
-          if (sep != '\0' && *bp == sep) ++bp;
-          const char* cp = ParseInt(bp, 2, 0, 59, &seconds);
-          if (cp != nullptr && cp - bp == 2) dp = cp;
-        }
-        *offset = ((hours * 60 + minutes) * 60) + seconds;
-        if (first == '-') *offset = -*offset;
-      } else {
-        dp = nullptr;
-      }
-    } else if (first == 'Z' || first == 'z') {  // Zulu
-      *offset = 0;
-    } else {
-      dp = nullptr;
-    }
-  }
-  return dp;
-}
-
-const char* ParseZone(const char* dp, std::string* zone) {
-  zone->clear();
-  if (dp != nullptr) {
-    while (*dp != '\0' && !std::isspace(*dp)) zone->push_back(*dp++);
-    if (zone->empty()) dp = nullptr;
-  }
-  return dp;
-}
-
-const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) {
-  if (dp != nullptr) {
-    std::int_fast64_t v = 0;
-    std::int_fast64_t exp = 0;
-    const char* const bp = dp;
-    while (const char* cp = strchr(kDigits, *dp)) {
-      int d = static_cast<int>(cp - kDigits);
-      if (d >= 10) break;
-      if (exp < 15) {
-        exp += 1;
-        v *= 10;
-        v += d;
-      }
-      ++dp;
-    }
-    if (dp != bp) {
-      v *= kExp10[15 - exp];
-      *subseconds = detail::femtoseconds(v);
-    } else {
-      dp = nullptr;
-    }
-  }
-  return dp;
-}
-
-// Parses a string into a std::tm using strptime(3).
-const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
-  if (dp != nullptr) {
-    dp = strptime(dp, fmt, tm);
-  }
-  return dp;
-}
-
-// Sets year, tm_mon and tm_mday given the year, week_num, and tm_wday,
-// and the day on which weeks are defined to start.  Returns false if year
-// would need to move outside its bounds.
-bool FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) {
-  const civil_year y(*year % 400);
-  civil_day cd = prev_weekday(y, week_start);  // week 0
-  cd = next_weekday(cd - 1, FromTmWday(tm->tm_wday)) + (week_num * 7);
-  if (const year_t shift = cd.year() - y.year()) {
-    if (shift > 0) {
-      if (*year > std::numeric_limits<year_t>::max() - shift) return false;
-    } else {
-      if (*year < std::numeric_limits<year_t>::min() - shift) return false;
-    }
-    *year += shift;
-  }
-  tm->tm_mon = cd.month() - 1;
-  tm->tm_mday = cd.day();
-  return true;
-}
-
-}  // namespace
-
-// Uses strptime(3) to parse the given input.  Supports the same extended
-// format specifiers as format(), although %E#S and %E*S are treated
-// identically (and similarly for %E#f and %E*f).  %Ez and %E*z also accept
-// the same inputs. %ET accepts either 'T' or 't'.
-//
-// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
-// handled internally so that we can normally avoid strptime() altogether
-// (which is particularly helpful when the native implementation is broken).
-//
-// The TZ/GNU %s extension is handled internally because strptime() has to
-// use localtime_r() to generate it, and that assumes the local time zone.
-//
-// We also handle the %z specifier to accommodate platforms that do not
-// support the tm_gmtoff extension to std::tm.  %Z is parsed but ignored.
-bool parse(const std::string& format, const std::string& input,
-           const time_zone& tz, time_point<seconds>* sec,
-           detail::femtoseconds* fs, std::string* err) {
-  // The unparsed input.
-  const char* data = input.c_str();  // NUL terminated
-
-  // Skips leading whitespace.
-  while (std::isspace(*data)) ++data;
-
-  const year_t kyearmax = std::numeric_limits<year_t>::max();
-  const year_t kyearmin = std::numeric_limits<year_t>::min();
-
-  // Sets default values for unspecified fields.
-  bool saw_year = false;
-  year_t year = 1970;
-  std::tm tm{};
-  tm.tm_year = 1970 - 1900;
-  tm.tm_mon = 1 - 1;  // Jan
-  tm.tm_mday = 1;
-  tm.tm_hour = 0;
-  tm.tm_min = 0;
-  tm.tm_sec = 0;
-  tm.tm_wday = 4;  // Thu
-  tm.tm_yday = 0;
-  tm.tm_isdst = 0;
-  auto subseconds = detail::femtoseconds::zero();
-  bool saw_offset = false;
-  int offset = 0;  // No offset from passed tz.
-  std::string zone = "UTC";
-
-  const char* fmt = format.c_str();  // NUL terminated
-  bool twelve_hour = false;
-  bool afternoon = false;
-  int week_num = -1;
-  weekday week_start = weekday::sunday;
-
-  bool saw_percent_s = false;
-  std::int_fast64_t percent_s = 0;
-
-  // Steps through format, one specifier at a time.
-  while (data != nullptr && *fmt != '\0') {
-    if (std::isspace(*fmt)) {
-      while (std::isspace(*data)) ++data;
-      while (std::isspace(*++fmt)) continue;
-      continue;
-    }
-
-    if (*fmt != '%') {
-      if (*data == *fmt) {
-        ++data;
-        ++fmt;
-      } else {
-        data = nullptr;
-      }
-      continue;
-    }
-
-    const char* percent = fmt;
-    if (*++fmt == '\0') {
-      data = nullptr;
-      continue;
-    }
-    switch (*fmt++) {
-      case 'Y':
-        // Symmetrically with FormatTime(), directly handing %Y avoids the
-        // tm.tm_year overflow problem.  However, tm.tm_year will still be
-        // used by other specifiers like %D.
-        data = ParseInt(data, 0, kyearmin, kyearmax, &year);
-        if (data != nullptr) saw_year = true;
-        continue;
-      case 'm':
-        data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
-        if (data != nullptr) tm.tm_mon -= 1;
-        week_num = -1;
-        continue;
-      case 'd':
-      case 'e':
-        data = ParseInt(data, 2, 1, 31, &tm.tm_mday);
-        week_num = -1;
-        continue;
-      case 'U':
-        data = ParseInt(data, 0, 0, 53, &week_num);
-        week_start = weekday::sunday;
-        continue;
-      case 'W':
-        data = ParseInt(data, 0, 0, 53, &week_num);
-        week_start = weekday::monday;
-        continue;
-      case 'u':
-        data = ParseInt(data, 0, 1, 7, &tm.tm_wday);
-        if (data != nullptr) tm.tm_wday %= 7;
-        continue;
-      case 'w':
-        data = ParseInt(data, 0, 0, 6, &tm.tm_wday);
-        continue;
-      case 'H':
-        data = ParseInt(data, 2, 0, 23, &tm.tm_hour);
-        twelve_hour = false;
-        continue;
-      case 'M':
-        data = ParseInt(data, 2, 0, 59, &tm.tm_min);
-        continue;
-      case 'S':
-        data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
-        continue;
-      case 'I':
-      case 'l':
-      case 'r':  // probably uses %I
-        twelve_hour = true;
-        break;
-      case 'R':  // uses %H
-      case 'T':  // uses %H
-      case 'c':  // probably uses %H
-      case 'X':  // probably uses %H
-        twelve_hour = false;
-        break;
-      case 'z':
-        data = ParseOffset(data, "", &offset);
-        if (data != nullptr) saw_offset = true;
-        continue;
-      case 'Z':  // ignored; zone abbreviations are ambiguous
-        data = ParseZone(data, &zone);
-        continue;
-      case 's':
-        data =
-            ParseInt(data, 0, std::numeric_limits<std::int_fast64_t>::min(),
-                     std::numeric_limits<std::int_fast64_t>::max(), &percent_s);
-        if (data != nullptr) saw_percent_s = true;
-        continue;
-      case ':':
-        if (fmt[0] == 'z' ||
-            (fmt[0] == ':' &&
-             (fmt[1] == 'z' || (fmt[1] == ':' && fmt[2] == 'z')))) {
-          data = ParseOffset(data, ":", &offset);
-          if (data != nullptr) saw_offset = true;
-          fmt += (fmt[0] == 'z') ? 1 : (fmt[1] == 'z') ? 2 : 3;
-          continue;
-        }
-        break;
-      case '%':
-        data = (*data == '%' ? data + 1 : nullptr);
-        continue;
-      case 'E':
-        if (fmt[0] == 'T') {
-          if (*data == 'T' || *data == 't') {
-            ++data;
-            ++fmt;
-          } else {
-            data = nullptr;
-          }
-          continue;
-        }
-        if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) {
-          data = ParseOffset(data, ":", &offset);
-          if (data != nullptr) saw_offset = true;
-          fmt += (fmt[0] == 'z') ? 1 : 2;
-          continue;
-        }
-        if (fmt[0] == '*' && fmt[1] == 'S') {
-          data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
-          if (data != nullptr && *data == '.') {
-            data = ParseSubSeconds(data + 1, &subseconds);
-          }
-          fmt += 2;
-          continue;
-        }
-        if (fmt[0] == '*' && fmt[1] == 'f') {
-          if (data != nullptr && std::isdigit(*data)) {
-            data = ParseSubSeconds(data, &subseconds);
-          }
-          fmt += 2;
-          continue;
-        }
-        if (fmt[0] == '4' && fmt[1] == 'Y') {
-          const char* bp = data;
-          data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year);
-          if (data != nullptr) {
-            if (data - bp == 4) {
-              saw_year = true;
-            } else {
-              data = nullptr;  // stopped too soon
-            }
-          }
-          fmt += 2;
-          continue;
-        }
-        if (std::isdigit(*fmt)) {
-          int n = 0;  // value ignored
-          if (const char* np = ParseInt(fmt, 0, 0, 1024, &n)) {
-            if (*np == 'S') {
-              data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
-              if (data != nullptr && *data == '.') {
-                data = ParseSubSeconds(data + 1, &subseconds);
-              }
-              fmt = ++np;
-              continue;
-            }
-            if (*np == 'f') {
-              if (data != nullptr && std::isdigit(*data)) {
-                data = ParseSubSeconds(data, &subseconds);
-              }
-              fmt = ++np;
-              continue;
-            }
-          }
-        }
-        if (*fmt == 'c') twelve_hour = false;  // probably uses %H
-        if (*fmt == 'X') twelve_hour = false;  // probably uses %H
-        if (*fmt != '\0') ++fmt;
-        break;
-      case 'O':
-        if (*fmt == 'H') twelve_hour = false;
-        if (*fmt == 'I') twelve_hour = true;
-        if (*fmt != '\0') ++fmt;
-        break;
-    }
-
-    // Parses the current specifier.
-    const char* orig_data = data;
-    std::string spec(percent, static_cast<std::size_t>(fmt - percent));
-    data = ParseTM(data, spec.c_str(), &tm);
-
-    // If we successfully parsed %p we need to remember whether the result
-    // was AM or PM so that we can adjust tm_hour before time_zone::lookup().
-    // So reparse the input with a known AM hour, and check if it is shifted
-    // to a PM hour.
-    if (spec == "%p" && data != nullptr) {
-      std::string test_input = "1";
-      test_input.append(orig_data, static_cast<std::size_t>(data - orig_data));
-      const char* test_data = test_input.c_str();
-      std::tm tmp{};
-      ParseTM(test_data, "%I%p", &tmp);
-      afternoon = (tmp.tm_hour == 13);
-    }
-  }
-
-  // Adjust a 12-hour tm_hour value if it should be in the afternoon.
-  if (twelve_hour && afternoon && tm.tm_hour < 12) {
-    tm.tm_hour += 12;
-  }
-
-  if (data == nullptr) {
-    if (err != nullptr) *err = "Failed to parse input";
-    return false;
-  }
-
-  // Skip any remaining whitespace.
-  while (std::isspace(*data)) ++data;
-
-  // parse() must consume the entire input string.
-  if (*data != '\0') {
-    if (err != nullptr) *err = "Illegal trailing data in input string";
-    return false;
-  }
-
-  // If we saw %s then we ignore anything else and return that time.
-  if (saw_percent_s) {
-    *sec = FromUnixSeconds(percent_s);
-    *fs = detail::femtoseconds::zero();
-    return true;
-  }
-
-  // If we saw %z, %Ez, or %E*z then we want to interpret the parsed fields
-  // in UTC and then shift by that offset.  Otherwise we want to interpret
-  // the fields directly in the passed time_zone.
-  time_zone ptz = saw_offset ? utc_time_zone() : tz;
-
-  // Allows a leap second of 60 to normalize forward to the following ":00".
-  if (tm.tm_sec == 60) {
-    tm.tm_sec -= 1;
-    offset -= 1;
-    subseconds = detail::femtoseconds::zero();
-  }
-
-  if (!saw_year) {
-    year = year_t{tm.tm_year};
-    if (year > kyearmax - 1900) {
-      // Platform-dependent, maybe unreachable.
-      if (err != nullptr) *err = "Out-of-range year";
-      return false;
-    }
-    year += 1900;
-  }
-
-  // Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number.
-  if (week_num != -1) {
-    if (!FromWeek(week_num, week_start, &year, &tm)) {
-      if (err != nullptr) *err = "Out-of-range field";
-      return false;
-    }
-  }
-
-  const int month = tm.tm_mon + 1;
-  civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
-
-  // parse() should not allow normalization. Due to the restricted field
-  // ranges above (see ParseInt()), the only possibility is for days to roll
-  // into months. That is, parsing "Sep 31" should not produce "Oct 1".
-  if (cs.month() != month || cs.day() != tm.tm_mday) {
-    if (err != nullptr) *err = "Out-of-range field";
-    return false;
-  }
-
-  // Accounts for the offset adjustment before converting to absolute time.
-  if ((offset < 0 && cs > civil_second::max() + offset) ||
-      (offset > 0 && cs < civil_second::min() + offset)) {
-    if (err != nullptr) *err = "Out-of-range field";
-    return false;
-  }
-  cs -= offset;
-
-  const auto tp = ptz.lookup(cs).pre;
-  // Checks for overflow/underflow and returns an error as necessary.
-  if (tp == time_point<seconds>::max()) {
-    const auto al = ptz.lookup(time_point<seconds>::max());
-    if (cs > al.cs) {
-      if (err != nullptr) *err = "Out-of-range field";
-      return false;
-    }
-  }
-  if (tp == time_point<seconds>::min()) {
-    const auto al = ptz.lookup(time_point<seconds>::min());
-    if (cs < al.cs) {
-      if (err != nullptr) *err = "Out-of-range field";
-      return false;
-    }
-  }
-
-  *sec = tp;
-  *fs = subseconds;
-  return true;
-}
-
-}  // namespace detail
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format_test.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format_test.cc
deleted file mode 100644
index a11f93e2a5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format_test.cc
+++ /dev/null
@@ -1,1603 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include <chrono>
-#include <iomanip>
-#include <sstream>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-namespace chrono = std::chrono;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-namespace {
-
-// This helper is a macro so that failed expectations show up with the
-// correct line numbers.
-#define ExpectTime(tp, tz, y, m, d, hh, mm, ss, off, isdst, zone) \
-  do {                                                            \
-    time_zone::absolute_lookup al = tz.lookup(tp);                \
-    EXPECT_EQ(y, al.cs.year());                                   \
-    EXPECT_EQ(m, al.cs.month());                                  \
-    EXPECT_EQ(d, al.cs.day());                                    \
-    EXPECT_EQ(hh, al.cs.hour());                                  \
-    EXPECT_EQ(mm, al.cs.minute());                                \
-    EXPECT_EQ(ss, al.cs.second());                                \
-    EXPECT_EQ(off, al.offset);                                    \
-    EXPECT_TRUE(isdst == al.is_dst);                              \
-    EXPECT_STREQ(zone, al.abbr);                                  \
-  } while (0)
-
-const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
-const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
-
-const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
-const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
-
-// A helper that tests the given format specifier by itself, and with leading
-// and trailing characters.  For example: TestFormatSpecifier(tp, "%a", "Thu").
-template <typename D>
-void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt,
-                         const std::string& ans) {
-  EXPECT_EQ(ans, format(fmt, tp, tz)) << fmt;
-  EXPECT_EQ("xxx " + ans, format("xxx " + fmt, tp, tz));
-  EXPECT_EQ(ans + " yyy", format(fmt + " yyy", tp, tz));
-  EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz));
-}
-
-}  // namespace
-
-//
-// Testing format()
-//
-
-TEST(Format, TimePointResolution) {
-  const char kFmt[] = "%H:%M:%E*S";
-  const time_zone utc = utc_time_zone();
-  const time_point<chrono::nanoseconds> t0 =
-      chrono::system_clock::from_time_t(1420167845) +
-      chrono::milliseconds(123) + chrono::microseconds(456) +
-      chrono::nanoseconds(789);
-  EXPECT_EQ(
-      "03:04:05.123456789",
-      format(kFmt, chrono::time_point_cast<chrono::nanoseconds>(t0), utc));
-  EXPECT_EQ(
-      "03:04:05.123456",
-      format(kFmt, chrono::time_point_cast<chrono::microseconds>(t0), utc));
-  EXPECT_EQ(
-      "03:04:05.123",
-      format(kFmt, chrono::time_point_cast<chrono::milliseconds>(t0), utc));
-  EXPECT_EQ("03:04:05",
-            format(kFmt, chrono::time_point_cast<chrono::seconds>(t0), utc));
-  EXPECT_EQ(
-      "03:04:05",
-      format(kFmt,
-             chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0),
-             utc));
-  EXPECT_EQ("03:04:00",
-            format(kFmt, chrono::time_point_cast<chrono::minutes>(t0), utc));
-  EXPECT_EQ("03:00:00",
-            format(kFmt, chrono::time_point_cast<chrono::hours>(t0), utc));
-}
-
-TEST(Format, TimePointExtendedResolution) {
-  const char kFmt[] = "%H:%M:%E*S";
-  const time_zone utc = utc_time_zone();
-  const time_point<absl::time_internal::cctz::seconds> tp =
-      chrono::time_point_cast<absl::time_internal::cctz::seconds>(
-          chrono::system_clock::from_time_t(0)) +
-      chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56);
-
-  EXPECT_EQ(
-      "12:34:56.123456789012345",
-      detail::format(kFmt, tp, detail::femtoseconds(123456789012345), utc));
-  EXPECT_EQ(
-      "12:34:56.012345678901234",
-      detail::format(kFmt, tp, detail::femtoseconds(12345678901234), utc));
-  EXPECT_EQ("12:34:56.001234567890123",
-            detail::format(kFmt, tp, detail::femtoseconds(1234567890123), utc));
-  EXPECT_EQ("12:34:56.000123456789012",
-            detail::format(kFmt, tp, detail::femtoseconds(123456789012), utc));
-
-  EXPECT_EQ("12:34:56.000000000000123",
-            detail::format(kFmt, tp, detail::femtoseconds(123), utc));
-  EXPECT_EQ("12:34:56.000000000000012",
-            detail::format(kFmt, tp, detail::femtoseconds(12), utc));
-  EXPECT_EQ("12:34:56.000000000000001",
-            detail::format(kFmt, tp, detail::femtoseconds(1), utc));
-}
-
-TEST(Format, Basics) {
-  time_zone tz = utc_time_zone();
-  time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
-
-  // Starts with a couple basic edge cases.
-  EXPECT_EQ("", format("", tp, tz));
-  EXPECT_EQ(" ", format(" ", tp, tz));
-  EXPECT_EQ("  ", format("  ", tp, tz));
-  EXPECT_EQ("xxx", format("xxx", tp, tz));
-  std::string big(128, 'x');
-  EXPECT_EQ(big, format(big, tp, tz));
-  // Cause the 1024-byte buffer to grow.
-  std::string bigger(100000, 'x');
-  EXPECT_EQ(bigger, format(bigger, tp, tz));
-
-  tp += chrono::hours(13) + chrono::minutes(4) + chrono::seconds(5);
-  tp += chrono::milliseconds(6) + chrono::microseconds(7) +
-        chrono::nanoseconds(8);
-  EXPECT_EQ("1970-01-01", format("%Y-%m-%d", tp, tz));
-  EXPECT_EQ("13:04:05", format("%H:%M:%S", tp, tz));
-  EXPECT_EQ("13:04:05.006", format("%H:%M:%E3S", tp, tz));
-  EXPECT_EQ("13:04:05.006007", format("%H:%M:%E6S", tp, tz));
-  EXPECT_EQ("13:04:05.006007008", format("%H:%M:%E9S", tp, tz));
-}
-
-TEST(Format, PosixConversions) {
-  const time_zone tz = utc_time_zone();
-  auto tp = chrono::system_clock::from_time_t(0);
-
-  TestFormatSpecifier(tp, tz, "%d", "01");
-  TestFormatSpecifier(tp, tz, "%e", " 1");  // extension but internal support
-  TestFormatSpecifier(tp, tz, "%H", "00");
-  TestFormatSpecifier(tp, tz, "%I", "12");
-  TestFormatSpecifier(tp, tz, "%j", "001");
-  TestFormatSpecifier(tp, tz, "%m", "01");
-  TestFormatSpecifier(tp, tz, "%M", "00");
-  TestFormatSpecifier(tp, tz, "%S", "00");
-  TestFormatSpecifier(tp, tz, "%U", "00");
-#if !defined(__EMSCRIPTEN__)
-  TestFormatSpecifier(tp, tz, "%w", "4");  // 4=Thursday
-#endif
-  TestFormatSpecifier(tp, tz, "%W", "00");
-  TestFormatSpecifier(tp, tz, "%y", "70");
-  TestFormatSpecifier(tp, tz, "%Y", "1970");
-  TestFormatSpecifier(tp, tz, "%z", "+0000");
-  TestFormatSpecifier(tp, tz, "%Z", "UTC");
-  TestFormatSpecifier(tp, tz, "%%", "%");
-
-#if defined(__linux__)
-  // SU/C99/TZ extensions
-  TestFormatSpecifier(tp, tz, "%C", "19");
-  TestFormatSpecifier(tp, tz, "%D", "01/01/70");
-  TestFormatSpecifier(tp, tz, "%F", "1970-01-01");
-  TestFormatSpecifier(tp, tz, "%g", "70");
-  TestFormatSpecifier(tp, tz, "%G", "1970");
-  TestFormatSpecifier(tp, tz, "%k", " 0");
-  TestFormatSpecifier(tp, tz, "%l", "12");
-  TestFormatSpecifier(tp, tz, "%n", "\n");
-  TestFormatSpecifier(tp, tz, "%R", "00:00");
-  TestFormatSpecifier(tp, tz, "%t", "\t");
-  TestFormatSpecifier(tp, tz, "%T", "00:00:00");
-  TestFormatSpecifier(tp, tz, "%u", "4");  // 4=Thursday
-  TestFormatSpecifier(tp, tz, "%V", "01");
-  TestFormatSpecifier(tp, tz, "%s", "0");
-#endif
-}
-
-TEST(Format, LocaleSpecific) {
-  const time_zone tz = utc_time_zone();
-  auto tp = chrono::system_clock::from_time_t(0);
-
-  TestFormatSpecifier(tp, tz, "%a", "Thu");
-  TestFormatSpecifier(tp, tz, "%A", "Thursday");
-  TestFormatSpecifier(tp, tz, "%b", "Jan");
-  TestFormatSpecifier(tp, tz, "%B", "January");
-
-  // %c should at least produce the numeric year and time-of-day.
-  const std::string s = format("%c", tp, utc_time_zone());
-  EXPECT_THAT(s, testing::HasSubstr("1970"));
-  EXPECT_THAT(s, testing::HasSubstr("00:00:00"));
-
-  TestFormatSpecifier(tp, tz, "%p", "AM");
-  TestFormatSpecifier(tp, tz, "%x", "01/01/70");
-  TestFormatSpecifier(tp, tz, "%X", "00:00:00");
-
-#if defined(__linux__)
-  // SU/C99/TZ extensions
-  TestFormatSpecifier(tp, tz, "%h", "Jan");  // Same as %b
-  TestFormatSpecifier(tp, tz, "%P", "am");
-  TestFormatSpecifier(tp, tz, "%r", "12:00:00 AM");
-
-  // Modified conversion specifiers %E_
-  TestFormatSpecifier(tp, tz, "%Ec", "Thu Jan  1 00:00:00 1970");
-  TestFormatSpecifier(tp, tz, "%EC", "19");
-  TestFormatSpecifier(tp, tz, "%Ex", "01/01/70");
-  TestFormatSpecifier(tp, tz, "%EX", "00:00:00");
-  TestFormatSpecifier(tp, tz, "%Ey", "70");
-  TestFormatSpecifier(tp, tz, "%EY", "1970");
-
-  // Modified conversion specifiers %O_
-  TestFormatSpecifier(tp, tz, "%Od", "01");
-  TestFormatSpecifier(tp, tz, "%Oe", " 1");
-  TestFormatSpecifier(tp, tz, "%OH", "00");
-  TestFormatSpecifier(tp, tz, "%OI", "12");
-  TestFormatSpecifier(tp, tz, "%Om", "01");
-  TestFormatSpecifier(tp, tz, "%OM", "00");
-  TestFormatSpecifier(tp, tz, "%OS", "00");
-  TestFormatSpecifier(tp, tz, "%Ou", "4");  // 4=Thursday
-  TestFormatSpecifier(tp, tz, "%OU", "00");
-  TestFormatSpecifier(tp, tz, "%OV", "01");
-  TestFormatSpecifier(tp, tz, "%Ow", "4");  // 4=Thursday
-  TestFormatSpecifier(tp, tz, "%OW", "00");
-  TestFormatSpecifier(tp, tz, "%Oy", "70");
-#endif
-}
-
-TEST(Format, Escaping) {
-  const time_zone tz = utc_time_zone();
-  auto tp = chrono::system_clock::from_time_t(0);
-
-  TestFormatSpecifier(tp, tz, "%%", "%");
-  TestFormatSpecifier(tp, tz, "%%a", "%a");
-  TestFormatSpecifier(tp, tz, "%%b", "%b");
-  TestFormatSpecifier(tp, tz, "%%Ea", "%Ea");
-  TestFormatSpecifier(tp, tz, "%%Es", "%Es");
-  TestFormatSpecifier(tp, tz, "%%E3S", "%E3S");
-  TestFormatSpecifier(tp, tz, "%%OS", "%OS");
-  TestFormatSpecifier(tp, tz, "%%O3S", "%O3S");
-
-  // Multiple levels of escaping.
-  TestFormatSpecifier(tp, tz, "%%%Y", "%1970");
-  TestFormatSpecifier(tp, tz, "%%%E3S", "%00.000");
-  TestFormatSpecifier(tp, tz, "%%%%E3S", "%%E3S");
-}
-
-TEST(Format, ExtendedSeconds) {
-  const time_zone tz = utc_time_zone();
-
-  // No subseconds.
-  time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
-  tp += chrono::seconds(5);
-  EXPECT_EQ("05", format("%E*S", tp, tz));
-  EXPECT_EQ("05", format("%E0S", tp, tz));
-  EXPECT_EQ("05.0", format("%E1S", tp, tz));
-  EXPECT_EQ("05.00", format("%E2S", tp, tz));
-  EXPECT_EQ("05.000", format("%E3S", tp, tz));
-  EXPECT_EQ("05.0000", format("%E4S", tp, tz));
-  EXPECT_EQ("05.00000", format("%E5S", tp, tz));
-  EXPECT_EQ("05.000000", format("%E6S", tp, tz));
-  EXPECT_EQ("05.0000000", format("%E7S", tp, tz));
-  EXPECT_EQ("05.00000000", format("%E8S", tp, tz));
-  EXPECT_EQ("05.000000000", format("%E9S", tp, tz));
-  EXPECT_EQ("05.0000000000", format("%E10S", tp, tz));
-  EXPECT_EQ("05.00000000000", format("%E11S", tp, tz));
-  EXPECT_EQ("05.000000000000", format("%E12S", tp, tz));
-  EXPECT_EQ("05.0000000000000", format("%E13S", tp, tz));
-  EXPECT_EQ("05.00000000000000", format("%E14S", tp, tz));
-  EXPECT_EQ("05.000000000000000", format("%E15S", tp, tz));
-
-  // With subseconds.
-  tp += chrono::milliseconds(6) + chrono::microseconds(7) +
-        chrono::nanoseconds(8);
-  EXPECT_EQ("05.006007008", format("%E*S", tp, tz));
-  EXPECT_EQ("05", format("%E0S", tp, tz));
-  EXPECT_EQ("05.0", format("%E1S", tp, tz));
-  EXPECT_EQ("05.00", format("%E2S", tp, tz));
-  EXPECT_EQ("05.006", format("%E3S", tp, tz));
-  EXPECT_EQ("05.0060", format("%E4S", tp, tz));
-  EXPECT_EQ("05.00600", format("%E5S", tp, tz));
-  EXPECT_EQ("05.006007", format("%E6S", tp, tz));
-  EXPECT_EQ("05.0060070", format("%E7S", tp, tz));
-  EXPECT_EQ("05.00600700", format("%E8S", tp, tz));
-  EXPECT_EQ("05.006007008", format("%E9S", tp, tz));
-  EXPECT_EQ("05.0060070080", format("%E10S", tp, tz));
-  EXPECT_EQ("05.00600700800", format("%E11S", tp, tz));
-  EXPECT_EQ("05.006007008000", format("%E12S", tp, tz));
-  EXPECT_EQ("05.0060070080000", format("%E13S", tp, tz));
-  EXPECT_EQ("05.00600700800000", format("%E14S", tp, tz));
-  EXPECT_EQ("05.006007008000000", format("%E15S", tp, tz));
-
-  // Times before the Unix epoch.
-  tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1);
-  EXPECT_EQ("1969-12-31 23:59:59.999999",
-            format("%Y-%m-%d %H:%M:%E*S", tp, tz));
-
-  // Here is a "%E*S" case we got wrong for a while.  While the first
-  // instant below is correctly rendered as "...:07.333304", the second
-  // one used to appear as "...:07.33330499999999999".
-  tp = chrono::system_clock::from_time_t(0) +
-       chrono::microseconds(1395024427333304);
-  EXPECT_EQ("2014-03-17 02:47:07.333304",
-            format("%Y-%m-%d %H:%M:%E*S", tp, tz));
-  tp += chrono::microseconds(1);
-  EXPECT_EQ("2014-03-17 02:47:07.333305",
-            format("%Y-%m-%d %H:%M:%E*S", tp, tz));
-}
-
-TEST(Format, ExtendedSubeconds) {
-  const time_zone tz = utc_time_zone();
-
-  // No subseconds.
-  time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
-  tp += chrono::seconds(5);
-  EXPECT_EQ("0", format("%E*f", tp, tz));
-  EXPECT_EQ("", format("%E0f", tp, tz));
-  EXPECT_EQ("0", format("%E1f", tp, tz));
-  EXPECT_EQ("00", format("%E2f", tp, tz));
-  EXPECT_EQ("000", format("%E3f", tp, tz));
-  EXPECT_EQ("0000", format("%E4f", tp, tz));
-  EXPECT_EQ("00000", format("%E5f", tp, tz));
-  EXPECT_EQ("000000", format("%E6f", tp, tz));
-  EXPECT_EQ("0000000", format("%E7f", tp, tz));
-  EXPECT_EQ("00000000", format("%E8f", tp, tz));
-  EXPECT_EQ("000000000", format("%E9f", tp, tz));
-  EXPECT_EQ("0000000000", format("%E10f", tp, tz));
-  EXPECT_EQ("00000000000", format("%E11f", tp, tz));
-  EXPECT_EQ("000000000000", format("%E12f", tp, tz));
-  EXPECT_EQ("0000000000000", format("%E13f", tp, tz));
-  EXPECT_EQ("00000000000000", format("%E14f", tp, tz));
-  EXPECT_EQ("000000000000000", format("%E15f", tp, tz));
-
-  // With subseconds.
-  tp += chrono::milliseconds(6) + chrono::microseconds(7) +
-        chrono::nanoseconds(8);
-  EXPECT_EQ("006007008", format("%E*f", tp, tz));
-  EXPECT_EQ("", format("%E0f", tp, tz));
-  EXPECT_EQ("0", format("%E1f", tp, tz));
-  EXPECT_EQ("00", format("%E2f", tp, tz));
-  EXPECT_EQ("006", format("%E3f", tp, tz));
-  EXPECT_EQ("0060", format("%E4f", tp, tz));
-  EXPECT_EQ("00600", format("%E5f", tp, tz));
-  EXPECT_EQ("006007", format("%E6f", tp, tz));
-  EXPECT_EQ("0060070", format("%E7f", tp, tz));
-  EXPECT_EQ("00600700", format("%E8f", tp, tz));
-  EXPECT_EQ("006007008", format("%E9f", tp, tz));
-  EXPECT_EQ("0060070080", format("%E10f", tp, tz));
-  EXPECT_EQ("00600700800", format("%E11f", tp, tz));
-  EXPECT_EQ("006007008000", format("%E12f", tp, tz));
-  EXPECT_EQ("0060070080000", format("%E13f", tp, tz));
-  EXPECT_EQ("00600700800000", format("%E14f", tp, tz));
-  EXPECT_EQ("006007008000000", format("%E15f", tp, tz));
-
-  // Times before the Unix epoch.
-  tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1);
-  EXPECT_EQ("1969-12-31 23:59:59.999999",
-            format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
-
-  // Here is a "%E*S" case we got wrong for a while.  While the first
-  // instant below is correctly rendered as "...:07.333304", the second
-  // one used to appear as "...:07.33330499999999999".
-  tp = chrono::system_clock::from_time_t(0) +
-       chrono::microseconds(1395024427333304);
-  EXPECT_EQ("2014-03-17 02:47:07.333304",
-            format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
-  tp += chrono::microseconds(1);
-  EXPECT_EQ("2014-03-17 02:47:07.333305",
-            format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
-}
-
-TEST(Format, CompareExtendSecondsVsSubseconds) {
-  const time_zone tz = utc_time_zone();
-
-  // This test case illustrates the differences/similarities between:
-  //   fmt_A: %E<prec>S
-  //   fmt_B: %S.%E<prec>f
-  auto fmt_A = [](const std::string& prec) { return "%E" + prec + "S"; };
-  auto fmt_B = [](const std::string& prec) { return "%S.%E" + prec + "f"; };
-
-  // No subseconds:
-  time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0);
-  tp += chrono::seconds(5);
-  // ... %E*S and %S.%E*f are different.
-  EXPECT_EQ("05", format(fmt_A("*"), tp, tz));
-  EXPECT_EQ("05.0", format(fmt_B("*"), tp, tz));
-  // ... %E0S and %S.%E0f are different.
-  EXPECT_EQ("05", format(fmt_A("0"), tp, tz));
-  EXPECT_EQ("05.", format(fmt_B("0"), tp, tz));
-  // ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15].
-  for (int prec = 1; prec <= 15; ++prec) {
-    const std::string a = format(fmt_A(std::to_string(prec)), tp, tz);
-    const std::string b = format(fmt_B(std::to_string(prec)), tp, tz);
-    EXPECT_EQ(a, b) << "prec=" << prec;
-  }
-
-  // With subseconds:
-  // ... %E*S and %S.%E*f are the same.
-  tp += chrono::milliseconds(6) + chrono::microseconds(7) +
-        chrono::nanoseconds(8);
-  EXPECT_EQ("05.006007008", format(fmt_A("*"), tp, tz));
-  EXPECT_EQ("05.006007008", format(fmt_B("*"), tp, tz));
-  // ... %E0S and %S.%E0f are different.
-  EXPECT_EQ("05", format(fmt_A("0"), tp, tz));
-  EXPECT_EQ("05.", format(fmt_B("0"), tp, tz));
-  // ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15].
-  for (int prec = 1; prec <= 15; ++prec) {
-    const std::string a = format(fmt_A(std::to_string(prec)), tp, tz);
-    const std::string b = format(fmt_B(std::to_string(prec)), tp, tz);
-    EXPECT_EQ(a, b) << "prec=" << prec;
-  }
-}
-
-TEST(Format, ExtendedOffset) {
-  const auto tp = chrono::system_clock::from_time_t(0);
-
-  auto tz = fixed_time_zone(absl::time_internal::cctz::seconds::zero());
-  TestFormatSpecifier(tp, tz, "%z", "+0000");
-  TestFormatSpecifier(tp, tz, "%:z", "+00:00");
-  TestFormatSpecifier(tp, tz, "%Ez", "+00:00");
-
-  tz = fixed_time_zone(chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%z", "+0000");
-  TestFormatSpecifier(tp, tz, "%:z", "+00:00");
-  TestFormatSpecifier(tp, tz, "%Ez", "+00:00");
-
-  tz = fixed_time_zone(-chrono::seconds(56));  // NOTE: +00:00
-  TestFormatSpecifier(tp, tz, "%z", "+0000");
-  TestFormatSpecifier(tp, tz, "%:z", "+00:00");
-  TestFormatSpecifier(tp, tz, "%Ez", "+00:00");
-
-  tz = fixed_time_zone(chrono::minutes(34));
-  TestFormatSpecifier(tp, tz, "%z", "+0034");
-  TestFormatSpecifier(tp, tz, "%:z", "+00:34");
-  TestFormatSpecifier(tp, tz, "%Ez", "+00:34");
-
-  tz = fixed_time_zone(-chrono::minutes(34));
-  TestFormatSpecifier(tp, tz, "%z", "-0034");
-  TestFormatSpecifier(tp, tz, "%:z", "-00:34");
-  TestFormatSpecifier(tp, tz, "%Ez", "-00:34");
-
-  tz = fixed_time_zone(chrono::minutes(34) + chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%z", "+0034");
-  TestFormatSpecifier(tp, tz, "%:z", "+00:34");
-  TestFormatSpecifier(tp, tz, "%Ez", "+00:34");
-
-  tz = fixed_time_zone(-chrono::minutes(34) - chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%z", "-0034");
-  TestFormatSpecifier(tp, tz, "%:z", "-00:34");
-  TestFormatSpecifier(tp, tz, "%Ez", "-00:34");
-
-  tz = fixed_time_zone(chrono::hours(12));
-  TestFormatSpecifier(tp, tz, "%z", "+1200");
-  TestFormatSpecifier(tp, tz, "%:z", "+12:00");
-  TestFormatSpecifier(tp, tz, "%Ez", "+12:00");
-
-  tz = fixed_time_zone(-chrono::hours(12));
-  TestFormatSpecifier(tp, tz, "%z", "-1200");
-  TestFormatSpecifier(tp, tz, "%:z", "-12:00");
-  TestFormatSpecifier(tp, tz, "%Ez", "-12:00");
-
-  tz = fixed_time_zone(chrono::hours(12) + chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%z", "+1200");
-  TestFormatSpecifier(tp, tz, "%:z", "+12:00");
-  TestFormatSpecifier(tp, tz, "%Ez", "+12:00");
-
-  tz = fixed_time_zone(-chrono::hours(12) - chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%z", "-1200");
-  TestFormatSpecifier(tp, tz, "%:z", "-12:00");
-  TestFormatSpecifier(tp, tz, "%Ez", "-12:00");
-
-  tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34));
-  TestFormatSpecifier(tp, tz, "%z", "+1234");
-  TestFormatSpecifier(tp, tz, "%:z", "+12:34");
-  TestFormatSpecifier(tp, tz, "%Ez", "+12:34");
-
-  tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34));
-  TestFormatSpecifier(tp, tz, "%z", "-1234");
-  TestFormatSpecifier(tp, tz, "%:z", "-12:34");
-  TestFormatSpecifier(tp, tz, "%Ez", "-12:34");
-
-  tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34) +
-                       chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%z", "+1234");
-  TestFormatSpecifier(tp, tz, "%:z", "+12:34");
-  TestFormatSpecifier(tp, tz, "%Ez", "+12:34");
-
-  tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34) -
-                       chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%z", "-1234");
-  TestFormatSpecifier(tp, tz, "%:z", "-12:34");
-  TestFormatSpecifier(tp, tz, "%Ez", "-12:34");
-}
-
-TEST(Format, ExtendedSecondOffset) {
-  const auto tp = chrono::system_clock::from_time_t(0);
-
-  auto tz = fixed_time_zone(absl::time_internal::cctz::seconds::zero());
-  TestFormatSpecifier(tp, tz, "%E*z", "+00:00:00");
-  TestFormatSpecifier(tp, tz, "%::z", "+00:00:00");
-  TestFormatSpecifier(tp, tz, "%:::z", "+00");
-
-  tz = fixed_time_zone(chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%E*z", "+00:00:56");
-  TestFormatSpecifier(tp, tz, "%::z", "+00:00:56");
-  TestFormatSpecifier(tp, tz, "%:::z", "+00:00:56");
-
-  tz = fixed_time_zone(-chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%E*z", "-00:00:56");
-  TestFormatSpecifier(tp, tz, "%::z", "-00:00:56");
-  TestFormatSpecifier(tp, tz, "%:::z", "-00:00:56");
-
-  tz = fixed_time_zone(chrono::minutes(34));
-  TestFormatSpecifier(tp, tz, "%E*z", "+00:34:00");
-  TestFormatSpecifier(tp, tz, "%::z", "+00:34:00");
-  TestFormatSpecifier(tp, tz, "%:::z", "+00:34");
-
-  tz = fixed_time_zone(-chrono::minutes(34));
-  TestFormatSpecifier(tp, tz, "%E*z", "-00:34:00");
-  TestFormatSpecifier(tp, tz, "%::z", "-00:34:00");
-  TestFormatSpecifier(tp, tz, "%:::z", "-00:34");
-
-  tz = fixed_time_zone(chrono::minutes(34) + chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%E*z", "+00:34:56");
-  TestFormatSpecifier(tp, tz, "%::z", "+00:34:56");
-  TestFormatSpecifier(tp, tz, "%:::z", "+00:34:56");
-
-  tz = fixed_time_zone(-chrono::minutes(34) - chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%E*z", "-00:34:56");
-  TestFormatSpecifier(tp, tz, "%::z", "-00:34:56");
-  TestFormatSpecifier(tp, tz, "%:::z", "-00:34:56");
-
-  tz = fixed_time_zone(chrono::hours(12));
-  TestFormatSpecifier(tp, tz, "%E*z", "+12:00:00");
-  TestFormatSpecifier(tp, tz, "%::z", "+12:00:00");
-  TestFormatSpecifier(tp, tz, "%:::z", "+12");
-
-  tz = fixed_time_zone(-chrono::hours(12));
-  TestFormatSpecifier(tp, tz, "%E*z", "-12:00:00");
-  TestFormatSpecifier(tp, tz, "%::z", "-12:00:00");
-  TestFormatSpecifier(tp, tz, "%:::z", "-12");
-
-  tz = fixed_time_zone(chrono::hours(12) + chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%E*z", "+12:00:56");
-  TestFormatSpecifier(tp, tz, "%::z", "+12:00:56");
-  TestFormatSpecifier(tp, tz, "%:::z", "+12:00:56");
-
-  tz = fixed_time_zone(-chrono::hours(12) - chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%E*z", "-12:00:56");
-  TestFormatSpecifier(tp, tz, "%::z", "-12:00:56");
-  TestFormatSpecifier(tp, tz, "%:::z", "-12:00:56");
-
-  tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34));
-  TestFormatSpecifier(tp, tz, "%E*z", "+12:34:00");
-  TestFormatSpecifier(tp, tz, "%::z", "+12:34:00");
-  TestFormatSpecifier(tp, tz, "%:::z", "+12:34");
-
-  tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34));
-  TestFormatSpecifier(tp, tz, "%E*z", "-12:34:00");
-  TestFormatSpecifier(tp, tz, "%::z", "-12:34:00");
-  TestFormatSpecifier(tp, tz, "%:::z", "-12:34");
-
-  tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34) +
-                       chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%E*z", "+12:34:56");
-  TestFormatSpecifier(tp, tz, "%::z", "+12:34:56");
-  TestFormatSpecifier(tp, tz, "%:::z", "+12:34:56");
-
-  tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34) -
-                       chrono::seconds(56));
-  TestFormatSpecifier(tp, tz, "%E*z", "-12:34:56");
-  TestFormatSpecifier(tp, tz, "%::z", "-12:34:56");
-  TestFormatSpecifier(tp, tz, "%:::z", "-12:34:56");
-}
-
-TEST(Format, ExtendedYears) {
-  const time_zone utc = utc_time_zone();
-  const char e4y_fmt[] = "%E4Y%m%d";  // no separators
-
-  // %E4Y zero-pads the year to produce at least 4 chars, including the sign.
-  auto tp = convert(civil_second(-999, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("-9991127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(-99, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("-0991127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(-9, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("-0091127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(-1, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("-0011127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(0, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("00001127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(1, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("00011127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(9, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("00091127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(99, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("00991127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(999, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("09991127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(9999, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("99991127", format(e4y_fmt, tp, utc));
-
-  // When the year is outside [-999:9999], more than 4 chars are produced.
-  tp = convert(civil_second(-1000, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("-10001127", format(e4y_fmt, tp, utc));
-  tp = convert(civil_second(10000, 11, 27, 0, 0, 0), utc);
-  EXPECT_EQ("100001127", format(e4y_fmt, tp, utc));
-}
-
-TEST(Format, RFC3339Format) {
-  time_zone tz;
-  EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
-
-  time_point<chrono::nanoseconds> tp =
-      convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-
-  tp += chrono::milliseconds(100);
-  EXPECT_EQ("1977-06-28T09:08:07.1-07:00", format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-
-  tp += chrono::milliseconds(20);
-  EXPECT_EQ("1977-06-28T09:08:07.12-07:00", format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-
-  tp += chrono::milliseconds(3);
-  EXPECT_EQ("1977-06-28T09:08:07.123-07:00", format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-
-  tp += chrono::microseconds(400);
-  EXPECT_EQ("1977-06-28T09:08:07.1234-07:00", format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-
-  tp += chrono::microseconds(50);
-  EXPECT_EQ("1977-06-28T09:08:07.12345-07:00", format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-
-  tp += chrono::microseconds(6);
-  EXPECT_EQ("1977-06-28T09:08:07.123456-07:00", format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-
-  tp += chrono::nanoseconds(700);
-  EXPECT_EQ("1977-06-28T09:08:07.1234567-07:00", format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-
-  tp += chrono::nanoseconds(80);
-  EXPECT_EQ("1977-06-28T09:08:07.12345678-07:00", format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-
-  tp += chrono::nanoseconds(9);
-  EXPECT_EQ("1977-06-28T09:08:07.123456789-07:00",
-            format(RFC3339_full, tp, tz));
-  EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
-}
-
-TEST(Format, RFC1123Format) {  // locale specific
-  time_zone tz;
-  EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
-
-  auto tp = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
-  EXPECT_EQ("Tue, 28 Jun 1977 09:08:07 -0700", format(RFC1123_full, tp, tz));
-  EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz));
-}
-
-TEST(Format, Week) {
-  const time_zone utc = utc_time_zone();
-
-  auto tp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc);
-  EXPECT_EQ("2017-01-7", format("%Y-%U-%u", tp, utc));
-  EXPECT_EQ("2017-00-0", format("%Y-%W-%w", tp, utc));
-
-  tp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc);
-  EXPECT_EQ("2017-53-7", format("%Y-%U-%u", tp, utc));
-  EXPECT_EQ("2017-52-0", format("%Y-%W-%w", tp, utc));
-
-  tp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc);
-  EXPECT_EQ("2018-00-1", format("%Y-%U-%u", tp, utc));
-  EXPECT_EQ("2018-01-1", format("%Y-%W-%w", tp, utc));
-
-  tp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc);
-  EXPECT_EQ("2018-52-1", format("%Y-%U-%u", tp, utc));
-  EXPECT_EQ("2018-53-1", format("%Y-%W-%w", tp, utc));
-
-  tp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc);
-  EXPECT_EQ("2019-00-2", format("%Y-%U-%u", tp, utc));
-  EXPECT_EQ("2019-00-2", format("%Y-%W-%w", tp, utc));
-
-  tp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
-  EXPECT_EQ("2019-52-2", format("%Y-%U-%u", tp, utc));
-  EXPECT_EQ("2019-52-2", format("%Y-%W-%w", tp, utc));
-}
-
-//
-// Testing parse()
-//
-
-TEST(Parse, TimePointResolution) {
-  const char kFmt[] = "%H:%M:%E*S";
-  const time_zone utc = utc_time_zone();
-
-  time_point<chrono::nanoseconds> tp_ns;
-  EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_ns));
-  EXPECT_EQ("03:04:05.123456789", format(kFmt, tp_ns, utc));
-  EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ns));
-  EXPECT_EQ("03:04:05.123456", format(kFmt, tp_ns, utc));
-
-  time_point<chrono::microseconds> tp_us;
-  EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_us));
-  EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc));
-  EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_us));
-  EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc));
-  EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_us));
-  EXPECT_EQ("03:04:05.123", format(kFmt, tp_us, utc));
-
-  time_point<chrono::milliseconds> tp_ms;
-  EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ms));
-  EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc));
-  EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_ms));
-  EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc));
-  EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_ms));
-  EXPECT_EQ("03:04:05", format(kFmt, tp_ms, utc));
-
-  time_point<chrono::seconds> tp_s;
-  EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_s));
-  EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc));
-  EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_s));
-  EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc));
-
-  time_point<chrono::minutes> tp_m;
-  EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_m));
-  EXPECT_EQ("03:04:00", format(kFmt, tp_m, utc));
-
-  time_point<chrono::hours> tp_h;
-  EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_h));
-  EXPECT_EQ("03:00:00", format(kFmt, tp_h, utc));
-}
-
-TEST(Parse, TimePointExtendedResolution) {
-  const char kFmt[] = "%H:%M:%E*S";
-  const time_zone utc = utc_time_zone();
-
-  time_point<absl::time_internal::cctz::seconds> tp;
-  detail::femtoseconds fs;
-  EXPECT_TRUE(detail::parse(kFmt, "12:34:56.123456789012345", utc, &tp, &fs));
-  EXPECT_EQ("12:34:56.123456789012345", detail::format(kFmt, tp, fs, utc));
-  EXPECT_TRUE(detail::parse(kFmt, "12:34:56.012345678901234", utc, &tp, &fs));
-  EXPECT_EQ("12:34:56.012345678901234", detail::format(kFmt, tp, fs, utc));
-  EXPECT_TRUE(detail::parse(kFmt, "12:34:56.001234567890123", utc, &tp, &fs));
-  EXPECT_EQ("12:34:56.001234567890123", detail::format(kFmt, tp, fs, utc));
-  EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000123", utc, &tp, &fs));
-  EXPECT_EQ("12:34:56.000000000000123", detail::format(kFmt, tp, fs, utc));
-  EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000012", utc, &tp, &fs));
-  EXPECT_EQ("12:34:56.000000000000012", detail::format(kFmt, tp, fs, utc));
-  EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000001", utc, &tp, &fs));
-  EXPECT_EQ("12:34:56.000000000000001", detail::format(kFmt, tp, fs, utc));
-}
-
-TEST(Parse, Basics) {
-  time_zone tz = utc_time_zone();
-  time_point<chrono::nanoseconds> tp =
-      chrono::system_clock::from_time_t(1234567890);
-
-  // Simple edge cases.
-  EXPECT_TRUE(parse("", "", tz, &tp));
-  EXPECT_EQ(chrono::system_clock::from_time_t(0), tp);  // everything defaulted
-  EXPECT_TRUE(parse(" ", " ", tz, &tp));
-  EXPECT_TRUE(parse("  ", "  ", tz, &tp));
-  EXPECT_TRUE(parse("x", "x", tz, &tp));
-  EXPECT_TRUE(parse("xxx", "xxx", tz, &tp));
-
-  EXPECT_TRUE(
-      parse("%Y-%m-%d %H:%M:%S %z", "2013-06-28 19:08:09 -0800", tz, &tp));
-  ExpectTime(tp, tz, 2013, 6, 29, 3, 8, 9, 0, false, "UTC");
-}
-
-TEST(Parse, WithTimeZone) {
-  time_zone tz;
-  EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
-  time_point<chrono::nanoseconds> tp;
-
-  // We can parse a string without a UTC offset if we supply a timezone.
-  EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &tp));
-  ExpectTime(tp, tz, 2013, 6, 28, 19, 8, 9, -7 * 60 * 60, true, "PDT");
-
-  // But the timezone is ignored when a UTC offset is present.
-  EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S %z", "2013-06-28 19:08:09 +0800",
-                    utc_time_zone(), &tp));
-  ExpectTime(tp, tz, 2013, 6, 28, 19 - 8 - 7, 8, 9, -7 * 60 * 60, true, "PDT");
-
-  // Check a skipped time (a Spring DST transition). parse() uses the
-  // pre-transition offset.
-  EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-03-13 02:15:00", tz, &tp));
-  ExpectTime(tp, tz, 2011, 3, 13, 3, 15, 0, -7 * 60 * 60, true, "PDT");
-
-  // Check a repeated time (a Fall DST transition).  parse() uses the
-  // pre-transition offset.
-  EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-11-06 01:15:00", tz, &tp));
-  ExpectTime(tp, tz, 2011, 11, 6, 1, 15, 0, -7 * 60 * 60, true, "PDT");
-}
-
-TEST(Parse, LeapSecond) {
-  time_zone tz;
-  EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
-  time_point<chrono::nanoseconds> tp;
-
-  // ":59" -> ":59"
-  EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59-08:00", tz, &tp));
-  ExpectTime(tp, tz, 2013, 6, 28, 8, 8, 59, -7 * 60 * 60, true, "PDT");
-
-  // ":59.5" -> ":59.5"
-  EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59.5-08:00", tz, &tp));
-  ExpectTime(tp, tz, 2013, 6, 28, 8, 8, 59, -7 * 60 * 60, true, "PDT");
-
-  // ":60" -> ":00"
-  EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:60-08:00", tz, &tp));
-  ExpectTime(tp, tz, 2013, 6, 28, 8, 9, 0, -7 * 60 * 60, true, "PDT");
-
-  // ":60.5" -> ":00.0"
-  EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:60.5-08:00", tz, &tp));
-  ExpectTime(tp, tz, 2013, 6, 28, 8, 9, 0, -7 * 60 * 60, true, "PDT");
-
-  // ":61" -> error
-  EXPECT_FALSE(parse(RFC3339_full, "2013-06-28T07:08:61-08:00", tz, &tp));
-}
-
-TEST(Parse, ErrorCases) {
-  const time_zone tz = utc_time_zone();
-  auto tp = chrono::system_clock::from_time_t(0);
-
-  // Illegal trailing data.
-  EXPECT_FALSE(parse("%S", "123", tz, &tp));
-
-  // Can't parse an illegal format specifier.
-  EXPECT_FALSE(parse("%Q", "x", tz, &tp));
-
-  // Fails because of trailing, unparsed data "blah".
-  EXPECT_FALSE(parse("%m-%d", "2-3 blah", tz, &tp));
-
-  // Trailing whitespace is allowed.
-  EXPECT_TRUE(parse("%m-%d", "2-3  ", tz, &tp));
-  EXPECT_EQ(2, convert(tp, utc_time_zone()).month());
-  EXPECT_EQ(3, convert(tp, utc_time_zone()).day());
-
-  // Feb 31 requires normalization.
-  EXPECT_FALSE(parse("%m-%d", "2-31", tz, &tp));
-
-  // Check that we cannot have spaces in UTC offsets.
-  EXPECT_TRUE(parse("%z", "-0203", tz, &tp));
-  EXPECT_FALSE(parse("%z", "- 2 3", tz, &tp));
-  EXPECT_TRUE(parse("%Ez", "-02:03", tz, &tp));
-  EXPECT_FALSE(parse("%Ez", "- 2: 3", tz, &tp));
-
-  // Check that we reject other malformed UTC offsets.
-  EXPECT_FALSE(parse("%Ez", "+-08:00", tz, &tp));
-  EXPECT_FALSE(parse("%Ez", "-+08:00", tz, &tp));
-
-  // Check that we do not accept "-0" in fields that allow zero.
-  EXPECT_FALSE(parse("%Y", "-0", tz, &tp));
-  EXPECT_FALSE(parse("%E4Y", "-0", tz, &tp));
-  EXPECT_FALSE(parse("%H", "-0", tz, &tp));
-  EXPECT_FALSE(parse("%M", "-0", tz, &tp));
-  EXPECT_FALSE(parse("%S", "-0", tz, &tp));
-  EXPECT_FALSE(parse("%z", "+-000", tz, &tp));
-  EXPECT_FALSE(parse("%Ez", "+-0:00", tz, &tp));
-  EXPECT_FALSE(parse("%z", "-00-0", tz, &tp));
-  EXPECT_FALSE(parse("%Ez", "-00:-0", tz, &tp));
-}
-
-TEST(Parse, PosixConversions) {
-  time_zone tz = utc_time_zone();
-  auto tp = chrono::system_clock::from_time_t(0);
-  const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
-
-  tp = reset;
-  EXPECT_TRUE(parse("%d", "15", tz, &tp));
-  EXPECT_EQ(15, convert(tp, tz).day());
-
-  // %e is an extension, but is supported internally.
-  tp = reset;
-  EXPECT_TRUE(parse("%e", "15", tz, &tp));
-  EXPECT_EQ(15, convert(tp, tz).day());  // Equivalent to %d
-
-  tp = reset;
-  EXPECT_TRUE(parse("%H", "17", tz, &tp));
-  EXPECT_EQ(17, convert(tp, tz).hour());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%I", "5", tz, &tp));
-  EXPECT_EQ(5, convert(tp, tz).hour());
-
-  // %j is parsed but ignored.
-  EXPECT_TRUE(parse("%j", "32", tz, &tp));
-
-  tp = reset;
-  EXPECT_TRUE(parse("%m", "11", tz, &tp));
-  EXPECT_EQ(11, convert(tp, tz).month());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%M", "33", tz, &tp));
-  EXPECT_EQ(33, convert(tp, tz).minute());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%S", "55", tz, &tp));
-  EXPECT_EQ(55, convert(tp, tz).second());
-
-  // %U is parsed but ignored.
-  EXPECT_TRUE(parse("%U", "15", tz, &tp));
-
-  // %w is parsed but ignored.
-  EXPECT_TRUE(parse("%w", "2", tz, &tp));
-
-  // %W is parsed but ignored.
-  EXPECT_TRUE(parse("%W", "22", tz, &tp));
-
-  tp = reset;
-  EXPECT_TRUE(parse("%y", "04", tz, &tp));
-  EXPECT_EQ(2004, convert(tp, tz).year());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%Y", "2004", tz, &tp));
-  EXPECT_EQ(2004, convert(tp, tz).year());
-
-  EXPECT_TRUE(parse("%%", "%", tz, &tp));
-
-#if defined(__linux__)
-  // SU/C99/TZ extensions
-
-  // Because we handle each (non-internal) specifier in a separate call
-  // to strptime(), there is no way to group %C and %y together.  So we
-  // just skip the %C/%y case.
-#if 0
-  tp = reset;
-  EXPECT_TRUE(parse("%C %y", "20 04", tz, &tp));
-  EXPECT_EQ(2004, convert(tp, tz).year());
-#endif
-
-  tp = reset;
-  EXPECT_TRUE(parse("%D", "02/03/04", tz, &tp));
-  EXPECT_EQ(2, convert(tp, tz).month());
-  EXPECT_EQ(3, convert(tp, tz).day());
-  EXPECT_EQ(2004, convert(tp, tz).year());
-
-  EXPECT_TRUE(parse("%n", "\n", tz, &tp));
-
-  tp = reset;
-  EXPECT_TRUE(parse("%R", "03:44", tz, &tp));
-  EXPECT_EQ(3, convert(tp, tz).hour());
-  EXPECT_EQ(44, convert(tp, tz).minute());
-
-  EXPECT_TRUE(parse("%t", "\t\v\f\n\r ", tz, &tp));
-
-  tp = reset;
-  EXPECT_TRUE(parse("%T", "03:44:55", tz, &tp));
-  EXPECT_EQ(3, convert(tp, tz).hour());
-  EXPECT_EQ(44, convert(tp, tz).minute());
-  EXPECT_EQ(55, convert(tp, tz).second());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%s", "1234567890", tz, &tp));
-  EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp);
-
-  // %s conversion, like %z/%Ez, pays no heed to the optional zone.
-  time_zone lax;
-  EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax));
-  tp = reset;
-  EXPECT_TRUE(parse("%s", "1234567890", lax, &tp));
-  EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp);
-
-  // This is most important when the time has the same YMDhms
-  // breakdown in the zone as some other time.  For example, ...
-  //  1414917000 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PDT)
-  //  1414920600 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PST)
-  tp = reset;
-  EXPECT_TRUE(parse("%s", "1414917000", lax, &tp));
-  EXPECT_EQ(chrono::system_clock::from_time_t(1414917000), tp);
-  tp = reset;
-  EXPECT_TRUE(parse("%s", "1414920600", lax, &tp));
-  EXPECT_EQ(chrono::system_clock::from_time_t(1414920600), tp);
-#endif
-}
-
-TEST(Parse, LocaleSpecific) {
-  time_zone tz = utc_time_zone();
-  auto tp = chrono::system_clock::from_time_t(0);
-  const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
-
-  // %a is parsed but ignored.
-  EXPECT_TRUE(parse("%a", "Mon", tz, &tp));
-
-  // %A is parsed but ignored.
-  EXPECT_TRUE(parse("%A", "Monday", tz, &tp));
-
-  tp = reset;
-  EXPECT_TRUE(parse("%b", "Feb", tz, &tp));
-  EXPECT_EQ(2, convert(tp, tz).month());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%B", "February", tz, &tp));
-  EXPECT_EQ(2, convert(tp, tz).month());
-
-  // %p is parsed but ignored if it's alone.  But it's used with %I.
-  EXPECT_TRUE(parse("%p", "AM", tz, &tp));
-  tp = reset;
-  EXPECT_TRUE(parse("%I %p", "5 PM", tz, &tp));
-  EXPECT_EQ(17, convert(tp, tz).hour());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%x", "02/03/04", tz, &tp));
-  if (convert(tp, tz).month() == 2) {
-    EXPECT_EQ(3, convert(tp, tz).day());
-  } else {
-    EXPECT_EQ(2, convert(tp, tz).day());
-    EXPECT_EQ(3, convert(tp, tz).month());
-  }
-  EXPECT_EQ(2004, convert(tp, tz).year());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%X", "15:44:55", tz, &tp));
-  EXPECT_EQ(15, convert(tp, tz).hour());
-  EXPECT_EQ(44, convert(tp, tz).minute());
-  EXPECT_EQ(55, convert(tp, tz).second());
-
-#if defined(__linux__)
-  // SU/C99/TZ extensions
-
-  tp = reset;
-  EXPECT_TRUE(parse("%h", "Feb", tz, &tp));
-  EXPECT_EQ(2, convert(tp, tz).month());  // Equivalent to %b
-
-  tp = reset;
-  EXPECT_TRUE(parse("%l %p", "5 PM", tz, &tp));
-  EXPECT_EQ(17, convert(tp, tz).hour());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%r", "03:44:55 PM", tz, &tp));
-  EXPECT_EQ(15, convert(tp, tz).hour());
-  EXPECT_EQ(44, convert(tp, tz).minute());
-  EXPECT_EQ(55, convert(tp, tz).second());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%Ec", "Tue Nov 19 05:06:07 2013", tz, &tp));
-  EXPECT_EQ(convert(civil_second(2013, 11, 19, 5, 6, 7), tz), tp);
-
-  // Modified conversion specifiers %E_
-
-  tp = reset;
-  EXPECT_TRUE(parse("%Ex", "02/03/04", tz, &tp));
-  EXPECT_EQ(2, convert(tp, tz).month());
-  EXPECT_EQ(3, convert(tp, tz).day());
-  EXPECT_EQ(2004, convert(tp, tz).year());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%EX", "15:44:55", tz, &tp));
-  EXPECT_EQ(15, convert(tp, tz).hour());
-  EXPECT_EQ(44, convert(tp, tz).minute());
-  EXPECT_EQ(55, convert(tp, tz).second());
-
-  // %Ey, the year offset from %EC, doesn't really make sense alone as there
-  // is no way to represent it in tm_year (%EC is not simply the century).
-  // Yet, because we handle each (non-internal) specifier in a separate call
-  // to strptime(), there is no way to group %EC and %Ey either.  So we just
-  // skip the %EC and %Ey cases.
-
-  tp = reset;
-  EXPECT_TRUE(parse("%EY", "2004", tz, &tp));
-  EXPECT_EQ(2004, convert(tp, tz).year());
-
-  // Modified conversion specifiers %O_
-
-  tp = reset;
-  EXPECT_TRUE(parse("%Od", "15", tz, &tp));
-  EXPECT_EQ(15, convert(tp, tz).day());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%Oe", "15", tz, &tp));
-  EXPECT_EQ(15, convert(tp, tz).day());  // Equivalent to %d
-
-  tp = reset;
-  EXPECT_TRUE(parse("%OH", "17", tz, &tp));
-  EXPECT_EQ(17, convert(tp, tz).hour());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%OI", "5", tz, &tp));
-  EXPECT_EQ(5, convert(tp, tz).hour());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%Om", "11", tz, &tp));
-  EXPECT_EQ(11, convert(tp, tz).month());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%OM", "33", tz, &tp));
-  EXPECT_EQ(33, convert(tp, tz).minute());
-
-  tp = reset;
-  EXPECT_TRUE(parse("%OS", "55", tz, &tp));
-  EXPECT_EQ(55, convert(tp, tz).second());
-
-  // %OU is parsed but ignored.
-  EXPECT_TRUE(parse("%OU", "15", tz, &tp));
-
-  // %Ow is parsed but ignored.
-  EXPECT_TRUE(parse("%Ow", "2", tz, &tp));
-
-  // %OW is parsed but ignored.
-  EXPECT_TRUE(parse("%OW", "22", tz, &tp));
-
-  tp = reset;
-  EXPECT_TRUE(parse("%Oy", "04", tz, &tp));
-  EXPECT_EQ(2004, convert(tp, tz).year());
-#endif
-}
-
-TEST(Parse, ExtendedSeconds) {
-  const time_zone tz = utc_time_zone();
-  const time_point<chrono::nanoseconds> unix_epoch =
-      chrono::system_clock::from_time_t(0);
-
-  // All %E<prec>S cases are treated the same as %E*S on input.
-  auto precisions = {"*", "0", "1",  "2",  "3",  "4",  "5",  "6", "7",
-                     "8", "9", "10", "11", "12", "13", "14", "15"};
-  for (const std::string& prec : precisions) {
-    const std::string fmt = "%E" + prec + "S";
-    SCOPED_TRACE(fmt);
-    time_point<chrono::nanoseconds> tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "5", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "05", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "05.0", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "05.00", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "05.6", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "05.60", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "05.600", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "05.67", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "05.670", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "05.678", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(678), tp);
-  }
-
-  // Here is a "%E*S" case we got wrong for a while.  The fractional
-  // part of the first instant is less than 2^31 and was correctly
-  // parsed, while the second (and any subsecond field >=2^31) failed.
-  time_point<chrono::nanoseconds> tp = unix_epoch;
-  EXPECT_TRUE(parse("%E*S", "0.2147483647", tz, &tp));
-  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
-  tp = unix_epoch;
-  EXPECT_TRUE(parse("%E*S", "0.2147483648", tz, &tp));
-  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
-
-  // We should also be able to specify long strings of digits far
-  // beyond the current resolution and have them convert the same way.
-  tp = unix_epoch;
-  EXPECT_TRUE(parse(
-      "%E*S", "0.214748364801234567890123456789012345678901234567890123456789",
-      tz, &tp));
-  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
-}
-
-TEST(Parse, ExtendedSecondsScan) {
-  const time_zone tz = utc_time_zone();
-  time_point<chrono::nanoseconds> tp;
-  for (int ms = 0; ms < 1000; ms += 111) {
-    for (int us = 0; us < 1000; us += 27) {
-      const int micros = ms * 1000 + us;
-      for (int ns = 0; ns < 1000; ns += 9) {
-        const auto expected = chrono::system_clock::from_time_t(0) +
-                              chrono::nanoseconds(micros * 1000 + ns);
-        std::ostringstream oss;
-        oss << "0." << std::setfill('0') << std::setw(3);
-        oss << ms << std::setw(3) << us << std::setw(3) << ns;
-        const std::string input = oss.str();
-        EXPECT_TRUE(parse("%E*S", input, tz, &tp));
-        EXPECT_EQ(expected, tp) << input;
-      }
-    }
-  }
-}
-
-TEST(Parse, ExtendedSubeconds) {
-  const time_zone tz = utc_time_zone();
-  const time_point<chrono::nanoseconds> unix_epoch =
-      chrono::system_clock::from_time_t(0);
-
-  // All %E<prec>f cases are treated the same as %E*f on input.
-  auto precisions = {"*", "0", "1",  "2",  "3",  "4",  "5",  "6", "7",
-                     "8", "9", "10", "11", "12", "13", "14", "15"};
-  for (const std::string& prec : precisions) {
-    const std::string fmt = "%E" + prec + "f";
-    SCOPED_TRACE(fmt);
-    time_point<chrono::nanoseconds> tp = unix_epoch - chrono::seconds(1);
-    EXPECT_TRUE(parse(fmt, "", tz, &tp));
-    EXPECT_EQ(unix_epoch, tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "6", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "60", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "600", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "67", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "670", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "678", tz, &tp));
-    EXPECT_EQ(unix_epoch + chrono::milliseconds(678), tp);
-    tp = unix_epoch;
-    EXPECT_TRUE(parse(fmt, "6789", tz, &tp));
-    EXPECT_EQ(
-        unix_epoch + chrono::milliseconds(678) + chrono::microseconds(900), tp);
-  }
-
-  // Here is a "%E*f" case we got wrong for a while.  The fractional
-  // part of the first instant is less than 2^31 and was correctly
-  // parsed, while the second (and any subsecond field >=2^31) failed.
-  time_point<chrono::nanoseconds> tp = unix_epoch;
-  EXPECT_TRUE(parse("%E*f", "2147483647", tz, &tp));
-  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
-  tp = unix_epoch;
-  EXPECT_TRUE(parse("%E*f", "2147483648", tz, &tp));
-  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
-
-  // We should also be able to specify long strings of digits far
-  // beyond the current resolution and have them convert the same way.
-  tp = unix_epoch;
-  EXPECT_TRUE(parse(
-      "%E*f", "214748364801234567890123456789012345678901234567890123456789",
-      tz, &tp));
-  EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp);
-}
-
-TEST(Parse, ExtendedSubecondsScan) {
-  time_point<chrono::nanoseconds> tp;
-  const time_zone tz = utc_time_zone();
-  for (int ms = 0; ms < 1000; ms += 111) {
-    for (int us = 0; us < 1000; us += 27) {
-      const int micros = ms * 1000 + us;
-      for (int ns = 0; ns < 1000; ns += 9) {
-        std::ostringstream oss;
-        oss << std::setfill('0') << std::setw(3) << ms;
-        oss << std::setw(3) << us << std::setw(3) << ns;
-        const std::string nanos = oss.str();
-        const auto expected = chrono::system_clock::from_time_t(0) +
-                              chrono::nanoseconds(micros * 1000 + ns);
-        for (int ps = 0; ps < 1000; ps += 250) {
-          std::ostringstream ps_oss;
-          oss << std::setfill('0') << std::setw(3) << ps;
-          const std::string input = nanos + ps_oss.str() + "999";
-          EXPECT_TRUE(parse("%E*f", input, tz, &tp));
-          EXPECT_EQ(expected + chrono::nanoseconds(ps) / 1000, tp) << input;
-        }
-      }
-    }
-  }
-}
-
-TEST(Parse, ExtendedOffset) {
-  const time_zone utc = utc_time_zone();
-  time_point<absl::time_internal::cctz::seconds> tp;
-
-  EXPECT_TRUE(parse("%Ez", "+00:00", utc, &tp));
-  EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse("%Ez", "-12:34", utc, &tp));
-  EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp);
-  EXPECT_TRUE(parse("%Ez", "+12:34", utc, &tp));
-  EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp);
-  EXPECT_FALSE(parse("%Ez", "-12:3", utc, &tp));
-
-  for (auto fmt : {"%Ez", "%z"}) {
-    EXPECT_TRUE(parse(fmt, "+0000", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "-1234", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "+1234", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp);
-    EXPECT_FALSE(parse(fmt, "-123", utc, &tp));
-
-    EXPECT_TRUE(parse(fmt, "+00", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "-12", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "+12", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp);
-    EXPECT_FALSE(parse(fmt, "-1", utc, &tp));
-  }
-}
-
-TEST(Parse, ExtendedSecondOffset) {
-  const time_zone utc = utc_time_zone();
-  time_point<absl::time_internal::cctz::seconds> tp;
-
-  for (auto fmt : {"%Ez", "%E*z", "%:z", "%::z", "%:::z"}) {
-    EXPECT_TRUE(parse(fmt, "+00:00:00", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "-12:34:56", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp);
-    EXPECT_TRUE(parse(fmt, "+12:34:56", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp);
-    EXPECT_FALSE(parse(fmt, "-12:34:5", utc, &tp));
-
-    EXPECT_TRUE(parse(fmt, "+000000", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "-123456", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp);
-    EXPECT_TRUE(parse(fmt, "+123456", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp);
-    EXPECT_FALSE(parse(fmt, "-12345", utc, &tp));
-
-    EXPECT_TRUE(parse(fmt, "+00:00", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "-12:34", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "+12:34", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp);
-    EXPECT_FALSE(parse(fmt, "-12:3", utc, &tp));
-
-    EXPECT_TRUE(parse(fmt, "+0000", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "-1234", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "+1234", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp);
-    EXPECT_FALSE(parse(fmt, "-123", utc, &tp));
-
-    EXPECT_TRUE(parse(fmt, "+00", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "-12", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp);
-    EXPECT_TRUE(parse(fmt, "+12", utc, &tp));
-    EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp);
-    EXPECT_FALSE(parse(fmt, "-1", utc, &tp));
-  }
-}
-
-TEST(Parse, ExtendedYears) {
-  const time_zone utc = utc_time_zone();
-  const char e4y_fmt[] = "%E4Y%m%d";  // no separators
-  time_point<absl::time_internal::cctz::seconds> tp;
-
-  // %E4Y consumes exactly four chars, including any sign.
-  EXPECT_TRUE(parse(e4y_fmt, "-9991127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(-999, 11, 27, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse(e4y_fmt, "-0991127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(-99, 11, 27, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse(e4y_fmt, "-0091127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(-9, 11, 27, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse(e4y_fmt, "-0011127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(-1, 11, 27, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse(e4y_fmt, "00001127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(0, 11, 27, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse(e4y_fmt, "00011127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(1, 11, 27, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse(e4y_fmt, "00091127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(9, 11, 27, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse(e4y_fmt, "00991127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(99, 11, 27, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse(e4y_fmt, "09991127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(999, 11, 27, 0, 0, 0), utc), tp);
-  EXPECT_TRUE(parse(e4y_fmt, "99991127", utc, &tp));
-  EXPECT_EQ(convert(civil_second(9999, 11, 27, 0, 0, 0), utc), tp);
-
-  // When the year is outside [-999:9999], the parse fails.
-  EXPECT_FALSE(parse(e4y_fmt, "-10001127", utc, &tp));
-  EXPECT_FALSE(parse(e4y_fmt, "100001127", utc, &tp));
-}
-
-TEST(Parse, RFC3339Format) {
-  const time_zone tz = utc_time_zone();
-  time_point<chrono::nanoseconds> tp;
-  EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp));
-  ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC");
-
-  // Check that %ET also accepts "t".
-  time_point<chrono::nanoseconds> tp2;
-  EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12t20:21:00+00:00", tz, &tp2));
-  EXPECT_EQ(tp, tp2);
-
-  // Check that %Ez also accepts "Z" as a synonym for "+00:00".
-  time_point<chrono::nanoseconds> tp3;
-  EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp3));
-  EXPECT_EQ(tp, tp3);
-
-  // Check that %Ez also accepts "z" as a synonym for "+00:00".
-  time_point<chrono::nanoseconds> tp4;
-  EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00z", tz, &tp4));
-  EXPECT_EQ(tp, tp4);
-}
-
-TEST(Parse, Week) {
-  const time_zone utc = utc_time_zone();
-  time_point<absl::time_internal::cctz::seconds> tp;
-
-  auto exp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc);
-  EXPECT_TRUE(parse("%Y-%U-%u", "2017-01-7", utc, &tp));
-  EXPECT_EQ(exp, tp);
-  EXPECT_TRUE(parse("%Y-%W-%w", "2017-00-0", utc, &tp));
-  EXPECT_EQ(exp, tp);
-
-  exp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc);
-  EXPECT_TRUE(parse("%Y-%U-%u", "2017-53-7", utc, &tp));
-  EXPECT_EQ(exp, tp);
-  EXPECT_TRUE(parse("%Y-%W-%w", "2017-52-0", utc, &tp));
-  EXPECT_EQ(exp, tp);
-
-  exp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc);
-  EXPECT_TRUE(parse("%Y-%U-%u", "2018-00-1", utc, &tp));
-  EXPECT_EQ(exp, tp);
-  EXPECT_TRUE(parse("%Y-%W-%w", "2018-01-1", utc, &tp));
-  EXPECT_EQ(exp, tp);
-
-  exp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc);
-  EXPECT_TRUE(parse("%Y-%U-%u", "2018-52-1", utc, &tp));
-  EXPECT_EQ(exp, tp);
-  EXPECT_TRUE(parse("%Y-%W-%w", "2018-53-1", utc, &tp));
-  EXPECT_EQ(exp, tp);
-
-  exp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc);
-  EXPECT_TRUE(parse("%Y-%U-%u", "2019-00-2", utc, &tp));
-  EXPECT_EQ(exp, tp);
-  EXPECT_TRUE(parse("%Y-%W-%w", "2019-00-2", utc, &tp));
-  EXPECT_EQ(exp, tp);
-
-  exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
-  EXPECT_TRUE(parse("%Y-%U-%u", "2019-52-2", utc, &tp));
-  EXPECT_EQ(exp, tp);
-  EXPECT_TRUE(parse("%Y-%W-%w", "2019-52-2", utc, &tp));
-  EXPECT_EQ(exp, tp);
-}
-
-TEST(Parse, WeekYearShift) {
-  // %U/%W conversions with week values in {0, 52, 53} can slip
-  // into the previous/following calendar years.
-  const time_zone utc = utc_time_zone();
-  time_point<absl::time_internal::cctz::seconds> tp;
-
-  auto exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
-  EXPECT_TRUE(parse("%Y-%U-%u", "2020-00-2", utc, &tp));
-  EXPECT_EQ(exp, tp);
-  EXPECT_TRUE(parse("%Y-%W-%w", "2020-00-2", utc, &tp));
-  EXPECT_EQ(exp, tp);
-
-  exp = convert(civil_second(2021, 1, 1, 0, 0, 0), utc);
-  EXPECT_TRUE(parse("%Y-%U-%u", "2020-52-5", utc, &tp));
-  EXPECT_EQ(exp, tp);
-  EXPECT_TRUE(parse("%Y-%W-%w", "2020-52-5", utc, &tp));
-  EXPECT_EQ(exp, tp);
-
-  // Slipping into the previous/following calendar years should fail when
-  // we're already at the extremes.
-  EXPECT_FALSE(parse("%Y-%U-%u", "-9223372036854775808-0-7", utc, &tp));
-  EXPECT_FALSE(parse("%Y-%U-%u", "9223372036854775807-53-7", utc, &tp));
-}
-
-TEST(Parse, MaxRange) {
-  const time_zone utc = utc_time_zone();
-  time_point<absl::time_internal::cctz::seconds> tp;
-
-  // tests the upper limit using +00:00 offset
-  EXPECT_TRUE(
-      parse(RFC3339_sec, "292277026596-12-04T15:30:07+00:00", utc, &tp));
-  EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
-  EXPECT_FALSE(
-      parse(RFC3339_sec, "292277026596-12-04T15:30:08+00:00", utc, &tp));
-
-  // tests the upper limit using -01:00 offset
-  EXPECT_TRUE(
-      parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp));
-  EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
-  EXPECT_FALSE(
-      parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp));
-
-  // tests the lower limit using +00:00 offset
-  EXPECT_TRUE(
-      parse(RFC3339_sec, "-292277022657-01-27T08:29:52+00:00", utc, &tp));
-  EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::min());
-  EXPECT_FALSE(
-      parse(RFC3339_sec, "-292277022657-01-27T08:29:51+00:00", utc, &tp));
-
-  // tests the lower limit using +01:00 offset
-  EXPECT_TRUE(
-      parse(RFC3339_sec, "-292277022657-01-27T09:29:52+01:00", utc, &tp));
-  EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::min());
-  EXPECT_FALSE(
-      parse(RFC3339_sec, "-292277022657-01-27T08:29:51+01:00", utc, &tp));
-
-  // tests max/min civil-second overflow
-  EXPECT_FALSE(
-      parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", utc, &tp));
-  EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01",
-                     utc, &tp));
-
-  // TODO: Add tests that parsing times with fractional seconds overflow
-  // appropriately. This can't be done until cctz::parse() properly detects
-  // overflow when combining the chrono seconds and femto.
-}
-
-//
-// Roundtrip test for format()/parse().
-//
-
-TEST(FormatParse, RoundTrip) {
-  time_zone lax;
-  EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax));
-  const auto in = convert(civil_second(1977, 6, 28, 9, 8, 7), lax);
-  const auto subseconds = chrono::nanoseconds(654321);
-
-  // RFC3339, which renders subseconds.
-  {
-    time_point<chrono::nanoseconds> out;
-    const std::string s = format(RFC3339_full, in + subseconds, lax);
-    EXPECT_TRUE(parse(RFC3339_full, s, lax, &out)) << s;
-    EXPECT_EQ(in + subseconds, out);  // RFC3339_full includes %Ez
-  }
-
-  // RFC1123, which only does whole seconds.
-  {
-    time_point<chrono::nanoseconds> out;
-    const std::string s = format(RFC1123_full, in, lax);
-    EXPECT_TRUE(parse(RFC1123_full, s, lax, &out)) << s;
-    EXPECT_EQ(in, out);  // RFC1123_full includes %z
-  }
-
-#if defined(_WIN32) || defined(_WIN64)
-  // Initial investigations indicate the %c does not roundtrip on Windows.
-  // TODO: Figure out what is going on here (perhaps a locale problem).
-#elif defined(__EMSCRIPTEN__)
-  // strftime() and strptime() use different defintions for "%c" under
-  // emscripten (see https://github.com/kripken/emscripten/pull/7491),
-  // causing its round-trip test to fail.
-#else
-  // Even though we don't know what %c will produce, it should roundtrip,
-  // but only in the 0-offset timezone.
-  {
-    time_point<chrono::nanoseconds> out;
-    time_zone utc = utc_time_zone();
-    const std::string s = format("%c", in, utc);
-    EXPECT_TRUE(parse("%c", s, utc, &out)) << s;
-    EXPECT_EQ(in, out);
-  }
-#endif
-}
-
-TEST(FormatParse, RoundTripDistantFuture) {
-  const time_zone utc = utc_time_zone();
-  const time_point<absl::time_internal::cctz::seconds> in =
-      time_point<absl::time_internal::cctz::seconds>::max();
-  const std::string s = format(RFC3339_full, in, utc);
-  time_point<absl::time_internal::cctz::seconds> out;
-  EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
-  EXPECT_EQ(in, out);
-}
-
-TEST(FormatParse, RoundTripDistantPast) {
-  const time_zone utc = utc_time_zone();
-  const time_point<absl::time_internal::cctz::seconds> in =
-      time_point<absl::time_internal::cctz::seconds>::min();
-  const std::string s = format(RFC3339_full, in, utc);
-  time_point<absl::time_internal::cctz::seconds> out;
-  EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
-  EXPECT_EQ(in, out);
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.cc
deleted file mode 100644
index 0319b2f98e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include "time_zone_if.h"
-
-#include "absl/base/config.h"
-#include "time_zone_info.h"
-#include "time_zone_libc.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-std::unique_ptr<TimeZoneIf> TimeZoneIf::Load(const std::string& name) {
-  // Support "libc:localtime" and "libc:*" to access the legacy
-  // localtime and UTC support respectively from the C library.
-  if (name.compare(0, 5, "libc:") == 0) {
-    return std::unique_ptr<TimeZoneIf>(new TimeZoneLibC(name.substr(5)));
-  }
-
-  // Otherwise use the "zoneinfo" implementation by default.
-  std::unique_ptr<TimeZoneInfo> tz(new TimeZoneInfo);
-  if (!tz->Load(name)) tz.reset();
-  return std::unique_ptr<TimeZoneIf>(tz.release());
-}
-
-// Defined out-of-line to avoid emitting a weak vtable in all TUs.
-TimeZoneIf::~TimeZoneIf() {}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.h b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.h
deleted file mode 100644
index 32c0891c1e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
-#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
-
-#include <chrono>
-#include <cstdint>
-#include <memory>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// A simple interface used to hide time-zone complexities from time_zone::Impl.
-// Subclasses implement the functions for civil-time conversions in the zone.
-class TimeZoneIf {
- public:
-  // A factory function for TimeZoneIf implementations.
-  static std::unique_ptr<TimeZoneIf> Load(const std::string& name);
-
-  virtual ~TimeZoneIf();
-
-  virtual time_zone::absolute_lookup BreakTime(
-      const time_point<seconds>& tp) const = 0;
-  virtual time_zone::civil_lookup MakeTime(const civil_second& cs) const = 0;
-
-  virtual bool NextTransition(const time_point<seconds>& tp,
-                              time_zone::civil_transition* trans) const = 0;
-  virtual bool PrevTransition(const time_point<seconds>& tp,
-                              time_zone::civil_transition* trans) const = 0;
-
-  virtual std::string Version() const = 0;
-  virtual std::string Description() const = 0;
-
- protected:
-  TimeZoneIf() {}
-};
-
-// Convert between time_point<seconds> and a count of seconds since the
-// Unix epoch.  We assume that the std::chrono::system_clock and the
-// Unix clock are second aligned, but not that they share an epoch.
-inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
-  return (tp - std::chrono::time_point_cast<seconds>(
-                   std::chrono::system_clock::from_time_t(0)))
-      .count();
-}
-inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) {
-  return std::chrono::time_point_cast<seconds>(
-             std::chrono::system_clock::from_time_t(0)) +
-         seconds(t);
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.cc
deleted file mode 100644
index f34e3aec84..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include "time_zone_impl.h"
-
-#include <deque>
-#include <memory>
-#include <mutex>
-#include <string>
-#include <unordered_map>
-#include <utility>
-
-#include "absl/base/config.h"
-#include "time_zone_fixed.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-namespace {
-
-// time_zone::Impls are linked into a map to support fast lookup by name.
-using TimeZoneImplByName =
-    std::unordered_map<std::string, const time_zone::Impl*>;
-TimeZoneImplByName* time_zone_map = nullptr;
-
-// Mutual exclusion for time_zone_map.
-std::mutex& TimeZoneMutex() {
-  // This mutex is intentionally "leaked" to avoid the static deinitialization
-  // order fiasco (std::mutex's destructor is not trivial on many platforms).
-  static std::mutex* time_zone_mutex = new std::mutex;
-  return *time_zone_mutex;
-}
-
-}  // namespace
-
-time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); }
-
-bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
-  const Impl* const utc_impl = UTCImpl();
-
-  // Check for UTC (which is never a key in time_zone_map).
-  auto offset = seconds::zero();
-  if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
-    *tz = time_zone(utc_impl);
-    return true;
-  }
-
-  // Check whether the time zone has already been loaded.
-  {
-    std::lock_guard<std::mutex> lock(TimeZoneMutex());
-    if (time_zone_map != nullptr) {
-      TimeZoneImplByName::const_iterator itr = time_zone_map->find(name);
-      if (itr != time_zone_map->end()) {
-        *tz = time_zone(itr->second);
-        return itr->second != utc_impl;
-      }
-    }
-  }
-
-  // Load the new time zone (outside the lock).
-  std::unique_ptr<const Impl> new_impl(new Impl(name));
-
-  // Add the new time zone to the map.
-  std::lock_guard<std::mutex> lock(TimeZoneMutex());
-  if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName;
-  const Impl*& impl = (*time_zone_map)[name];
-  if (impl == nullptr) {  // this thread won any load race
-    impl = new_impl->zone_ ? new_impl.release() : utc_impl;
-  }
-  *tz = time_zone(impl);
-  return impl != utc_impl;
-}
-
-void time_zone::Impl::ClearTimeZoneMapTestOnly() {
-  std::lock_guard<std::mutex> lock(TimeZoneMutex());
-  if (time_zone_map != nullptr) {
-    // Existing time_zone::Impl* entries are in the wild, so we can't delete
-    // them. Instead, we move them to a private container, where they are
-    // logically unreachable but not "leaked".  Future requests will result
-    // in reloading the data.
-    static auto* cleared = new std::deque<const time_zone::Impl*>;
-    for (const auto& element : *time_zone_map) {
-      cleared->push_back(element.second);
-    }
-    time_zone_map->clear();
-  }
-}
-
-time_zone::Impl::Impl(const std::string& name)
-    : name_(name), zone_(TimeZoneIf::Load(name_)) {}
-
-const time_zone::Impl* time_zone::Impl::UTCImpl() {
-  static const Impl* utc_impl = new Impl("UTC");  // never fails
-  return utc_impl;
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.h b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.h
deleted file mode 100644
index 7d747ba966..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
-#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
-
-#include <memory>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-#include "time_zone_if.h"
-#include "time_zone_info.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// time_zone::Impl is the internal object referenced by a cctz::time_zone.
-class time_zone::Impl {
- public:
-  // The UTC time zone. Also used for other time zones that fail to load.
-  static time_zone UTC();
-
-  // Load a named time zone. Returns false if the name is invalid, or if
-  // some other kind of error occurs. Note that loading "UTC" never fails.
-  static bool LoadTimeZone(const std::string& name, time_zone* tz);
-
-  // Clears the map of cached time zones.  Primarily for use in benchmarks
-  // that gauge the performance of loading/parsing the time-zone data.
-  static void ClearTimeZoneMapTestOnly();
-
-  // The primary key is the time-zone ID (e.g., "America/New_York").
-  const std::string& Name() const {
-    // TODO: It would nice if the zoneinfo data included the zone name.
-    return name_;
-  }
-
-  // Breaks a time_point down to civil-time components in this time zone.
-  time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const {
-    return zone_->BreakTime(tp);
-  }
-
-  // Converts the civil-time components in this time zone into a time_point.
-  // That is, the opposite of BreakTime(). The requested civil time may be
-  // ambiguous or illegal due to a change of UTC offset.
-  time_zone::civil_lookup MakeTime(const civil_second& cs) const {
-    return zone_->MakeTime(cs);
-  }
-
-  // Finds the time of the next/previous offset change in this time zone.
-  bool NextTransition(const time_point<seconds>& tp,
-                      time_zone::civil_transition* trans) const {
-    return zone_->NextTransition(tp, trans);
-  }
-  bool PrevTransition(const time_point<seconds>& tp,
-                      time_zone::civil_transition* trans) const {
-    return zone_->PrevTransition(tp, trans);
-  }
-
-  // Returns an implementation-defined version string for this time zone.
-  std::string Version() const { return zone_->Version(); }
-
-  // Returns an implementation-defined description of this time zone.
-  std::string Description() const { return zone_->Description(); }
-
- private:
-  explicit Impl(const std::string& name);
-  static const Impl* UTCImpl();
-
-  const std::string name_;
-  std::unique_ptr<TimeZoneIf> zone_;
-};
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.cc
deleted file mode 100644
index 8039353e58..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.cc
+++ /dev/null
@@ -1,965 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-// This file implements the TimeZoneIf interface using the "zoneinfo"
-// data provided by the IANA Time Zone Database (i.e., the only real game
-// in town).
-//
-// TimeZoneInfo represents the history of UTC-offset changes within a time
-// zone. Most changes are due to daylight-saving rules, but occasionally
-// shifts are made to the time-zone's base offset. The database only attempts
-// to be definitive for times since 1970, so be wary of local-time conversions
-// before that. Also, rule and zone-boundary changes are made at the whim
-// of governments, so the conversion of future times needs to be taken with
-// a grain of salt.
-//
-// For more information see tzfile(5), http://www.iana.org/time-zones, or
-// https://en.wikipedia.org/wiki/Zoneinfo.
-//
-// Note that we assume the proleptic Gregorian calendar and 60-second
-// minutes throughout.
-
-#include "time_zone_info.h"
-
-#include <algorithm>
-#include <cassert>
-#include <chrono>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <functional>
-#include <memory>
-#include <sstream>
-#include <string>
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "time_zone_fixed.h"
-#include "time_zone_posix.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-namespace {
-
-inline bool IsLeap(year_t year) {
-  return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
-}
-
-// The number of days in non-leap and leap years respectively.
-const std::int_least32_t kDaysPerYear[2] = {365, 366};
-
-// The day offsets of the beginning of each (1-based) month in non-leap and
-// leap years respectively (e.g., 335 days before December in a leap year).
-const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = {
-    {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
-    {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
-};
-
-// We reject leap-second encoded zoneinfo and so assume 60-second minutes.
-const std::int_least32_t kSecsPerDay = 24 * 60 * 60;
-
-// 400-year chunks always have 146097 days (20871 weeks).
-const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay;
-
-// Like kDaysPerYear[] but scaled up by a factor of kSecsPerDay.
-const std::int_least32_t kSecsPerYear[2] = {
-    365 * kSecsPerDay,
-    366 * kSecsPerDay,
-};
-
-// Convert a cctz::weekday to a POSIX TZ weekday number (0==Sun, ..., 6=Sat).
-inline int ToPosixWeekday(weekday wd) {
-  switch (wd) {
-    case weekday::sunday:
-      return 0;
-    case weekday::monday:
-      return 1;
-    case weekday::tuesday:
-      return 2;
-    case weekday::wednesday:
-      return 3;
-    case weekday::thursday:
-      return 4;
-    case weekday::friday:
-      return 5;
-    case weekday::saturday:
-      return 6;
-  }
-  return 0; /*NOTREACHED*/
-}
-
-// Single-byte, unsigned numeric values are encoded directly.
-inline std::uint_fast8_t Decode8(const char* cp) {
-  return static_cast<std::uint_fast8_t>(*cp) & 0xff;
-}
-
-// Multi-byte, numeric values are encoded using a MSB first,
-// twos-complement representation. These helpers decode, from
-// the given address, 4-byte and 8-byte values respectively.
-// Note: If int_fastXX_t == intXX_t and this machine is not
-// twos complement, then there will be at least one input value
-// we cannot represent.
-std::int_fast32_t Decode32(const char* cp) {
-  std::uint_fast32_t v = 0;
-  for (int i = 0; i != (32 / 8); ++i) v = (v << 8) | Decode8(cp++);
-  const std::int_fast32_t s32max = 0x7fffffff;
-  const auto s32maxU = static_cast<std::uint_fast32_t>(s32max);
-  if (v <= s32maxU) return static_cast<std::int_fast32_t>(v);
-  return static_cast<std::int_fast32_t>(v - s32maxU - 1) - s32max - 1;
-}
-
-std::int_fast64_t Decode64(const char* cp) {
-  std::uint_fast64_t v = 0;
-  for (int i = 0; i != (64 / 8); ++i) v = (v << 8) | Decode8(cp++);
-  const std::int_fast64_t s64max = 0x7fffffffffffffff;
-  const auto s64maxU = static_cast<std::uint_fast64_t>(s64max);
-  if (v <= s64maxU) return static_cast<std::int_fast64_t>(v);
-  return static_cast<std::int_fast64_t>(v - s64maxU - 1) - s64max - 1;
-}
-
-// Generate a year-relative offset for a PosixTransition.
-std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday,
-                              const PosixTransition& pt) {
-  std::int_fast64_t days = 0;
-  switch (pt.date.fmt) {
-    case PosixTransition::J: {
-      days = pt.date.j.day;
-      if (!leap_year || days < kMonthOffsets[1][3]) days -= 1;
-      break;
-    }
-    case PosixTransition::N: {
-      days = pt.date.n.day;
-      break;
-    }
-    case PosixTransition::M: {
-      const bool last_week = (pt.date.m.week == 5);
-      days = kMonthOffsets[leap_year][pt.date.m.month + last_week];
-      const std::int_fast64_t weekday = (jan1_weekday + days) % 7;
-      if (last_week) {
-        days -= (weekday + 7 - 1 - pt.date.m.weekday) % 7 + 1;
-      } else {
-        days += (pt.date.m.weekday + 7 - weekday) % 7;
-        days += (pt.date.m.week - 1) * 7;
-      }
-      break;
-    }
-  }
-  return (days * kSecsPerDay) + pt.time.offset;
-}
-
-inline time_zone::civil_lookup MakeUnique(const time_point<seconds>& tp) {
-  time_zone::civil_lookup cl;
-  cl.kind = time_zone::civil_lookup::UNIQUE;
-  cl.pre = cl.trans = cl.post = tp;
-  return cl;
-}
-
-inline time_zone::civil_lookup MakeUnique(std::int_fast64_t unix_time) {
-  return MakeUnique(FromUnixSeconds(unix_time));
-}
-
-inline time_zone::civil_lookup MakeSkipped(const Transition& tr,
-                                           const civil_second& cs) {
-  time_zone::civil_lookup cl;
-  cl.kind = time_zone::civil_lookup::SKIPPED;
-  cl.pre = FromUnixSeconds(tr.unix_time - 1 + (cs - tr.prev_civil_sec));
-  cl.trans = FromUnixSeconds(tr.unix_time);
-  cl.post = FromUnixSeconds(tr.unix_time - (tr.civil_sec - cs));
-  return cl;
-}
-
-inline time_zone::civil_lookup MakeRepeated(const Transition& tr,
-                                            const civil_second& cs) {
-  time_zone::civil_lookup cl;
-  cl.kind = time_zone::civil_lookup::REPEATED;
-  cl.pre = FromUnixSeconds(tr.unix_time - 1 - (tr.prev_civil_sec - cs));
-  cl.trans = FromUnixSeconds(tr.unix_time);
-  cl.post = FromUnixSeconds(tr.unix_time + (cs - tr.civil_sec));
-  return cl;
-}
-
-inline civil_second YearShift(const civil_second& cs, year_t shift) {
-  return civil_second(cs.year() + shift, cs.month(), cs.day(), cs.hour(),
-                      cs.minute(), cs.second());
-}
-
-}  // namespace
-
-// What (no leap-seconds) UTC+seconds zoneinfo would look like.
-bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
-  transition_types_.resize(1);
-  TransitionType& tt(transition_types_.back());
-  tt.utc_offset = static_cast<std::int_least32_t>(offset.count());
-  tt.is_dst = false;
-  tt.abbr_index = 0;
-
-  // We temporarily add some redundant, contemporary (2015 through 2025)
-  // transitions for performance reasons.  See TimeZoneInfo::LocalTime().
-  // TODO: Fix the performance issue and remove the extra transitions.
-  transitions_.clear();
-  transitions_.reserve(12);
-  for (const std::int_fast64_t unix_time : {
-           -(1LL << 59),  // a "first half" transition
-           1420070400LL,  // 2015-01-01T00:00:00+00:00
-           1451606400LL,  // 2016-01-01T00:00:00+00:00
-           1483228800LL,  // 2017-01-01T00:00:00+00:00
-           1514764800LL,  // 2018-01-01T00:00:00+00:00
-           1546300800LL,  // 2019-01-01T00:00:00+00:00
-           1577836800LL,  // 2020-01-01T00:00:00+00:00
-           1609459200LL,  // 2021-01-01T00:00:00+00:00
-           1640995200LL,  // 2022-01-01T00:00:00+00:00
-           1672531200LL,  // 2023-01-01T00:00:00+00:00
-           1704067200LL,  // 2024-01-01T00:00:00+00:00
-           1735689600LL,  // 2025-01-01T00:00:00+00:00
-       }) {
-    Transition& tr(*transitions_.emplace(transitions_.end()));
-    tr.unix_time = unix_time;
-    tr.type_index = 0;
-    tr.civil_sec = LocalTime(tr.unix_time, tt).cs;
-    tr.prev_civil_sec = tr.civil_sec - 1;
-  }
-
-  default_transition_type_ = 0;
-  abbreviations_ = FixedOffsetToAbbr(offset);
-  abbreviations_.append(1, '\0');
-  future_spec_.clear();  // never needed for a fixed-offset zone
-  extended_ = false;
-
-  tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
-  tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
-
-  transitions_.shrink_to_fit();
-  return true;
-}
-
-// Builds the in-memory header using the raw bytes from the file.
-bool TimeZoneInfo::Header::Build(const tzhead& tzh) {
-  std::int_fast32_t v;
-  if ((v = Decode32(tzh.tzh_timecnt)) < 0) return false;
-  timecnt = static_cast<std::size_t>(v);
-  if ((v = Decode32(tzh.tzh_typecnt)) < 0) return false;
-  typecnt = static_cast<std::size_t>(v);
-  if ((v = Decode32(tzh.tzh_charcnt)) < 0) return false;
-  charcnt = static_cast<std::size_t>(v);
-  if ((v = Decode32(tzh.tzh_leapcnt)) < 0) return false;
-  leapcnt = static_cast<std::size_t>(v);
-  if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false;
-  ttisstdcnt = static_cast<std::size_t>(v);
-  if ((v = Decode32(tzh.tzh_ttisutcnt)) < 0) return false;
-  ttisutcnt = static_cast<std::size_t>(v);
-  return true;
-}
-
-// How many bytes of data are associated with this header. The result
-// depends upon whether this is a section with 4-byte or 8-byte times.
-std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const {
-  std::size_t len = 0;
-  len += (time_len + 1) * timecnt;  // unix_time + type_index
-  len += (4 + 1 + 1) * typecnt;     // utc_offset + is_dst + abbr_index
-  len += 1 * charcnt;               // abbreviations
-  len += (time_len + 4) * leapcnt;  // leap-time + TAI-UTC
-  len += 1 * ttisstdcnt;            // UTC/local indicators
-  len += 1 * ttisutcnt;             // standard/wall indicators
-  return len;
-}
-
-// zic(8) can generate no-op transitions when a zone changes rules at an
-// instant when there is actually no discontinuity.  So we check whether
-// two transitions have equivalent types (same offset/is_dst/abbr).
-bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index,
-                                    std::uint_fast8_t tt2_index) const {
-  if (tt1_index == tt2_index) return true;
-  const TransitionType& tt1(transition_types_[tt1_index]);
-  const TransitionType& tt2(transition_types_[tt2_index]);
-  if (tt1.utc_offset != tt2.utc_offset) return false;
-  if (tt1.is_dst != tt2.is_dst) return false;
-  if (tt1.abbr_index != tt2.abbr_index) return false;
-  return true;
-}
-
-// Find/make a transition type with these attributes.
-bool TimeZoneInfo::GetTransitionType(std::int_fast32_t utc_offset, bool is_dst,
-                                     const std::string& abbr,
-                                     std::uint_least8_t* index) {
-  std::size_t type_index = 0;
-  std::size_t abbr_index = abbreviations_.size();
-  for (; type_index != transition_types_.size(); ++type_index) {
-    const TransitionType& tt(transition_types_[type_index]);
-    const char* tt_abbr = &abbreviations_[tt.abbr_index];
-    if (tt_abbr == abbr) abbr_index = tt.abbr_index;
-    if (tt.utc_offset == utc_offset && tt.is_dst == is_dst) {
-      if (abbr_index == tt.abbr_index) break;  // reuse
-    }
-  }
-  if (type_index > 255 || abbr_index > 255) {
-    // No index space (8 bits) available for a new type or abbreviation.
-    return false;
-  }
-  if (type_index == transition_types_.size()) {
-    TransitionType& tt(*transition_types_.emplace(transition_types_.end()));
-    tt.utc_offset = static_cast<std::int_least32_t>(utc_offset);
-    tt.is_dst = is_dst;
-    if (abbr_index == abbreviations_.size()) {
-      abbreviations_.append(abbr);
-      abbreviations_.append(1, '\0');
-    }
-    tt.abbr_index = static_cast<std::uint_least8_t>(abbr_index);
-  }
-  *index = static_cast<std::uint_least8_t>(type_index);
-  return true;
-}
-
-// Use the POSIX-TZ-environment-variable-style string to handle times
-// in years after the last transition stored in the zoneinfo data.
-bool TimeZoneInfo::ExtendTransitions() {
-  extended_ = false;
-  if (future_spec_.empty()) return true;  // last transition prevails
-
-  PosixTimeZone posix;
-  if (!ParsePosixSpec(future_spec_, &posix)) return false;
-
-  // Find transition type for the future std specification.
-  std::uint_least8_t std_ti;
-  if (!GetTransitionType(posix.std_offset, false, posix.std_abbr, &std_ti))
-    return false;
-
-  if (posix.dst_abbr.empty()) {  // std only
-    // The future specification should match the last transition, and
-    // that means that handling the future will fall out naturally.
-    return EquivTransitions(transitions_.back().type_index, std_ti);
-  }
-
-  // Find transition type for the future dst specification.
-  std::uint_least8_t dst_ti;
-  if (!GetTransitionType(posix.dst_offset, true, posix.dst_abbr, &dst_ti))
-    return false;
-
-  // Extend the transitions for an additional 400 years using the
-  // future specification. Years beyond those can be handled by
-  // mapping back to a cycle-equivalent year within that range.
-  // We may need two additional transitions for the current year.
-  transitions_.reserve(transitions_.size() + 400 * 2 + 2);
-  extended_ = true;
-
-  const Transition& last(transitions_.back());
-  const std::int_fast64_t last_time = last.unix_time;
-  const TransitionType& last_tt(transition_types_[last.type_index]);
-  last_year_ = LocalTime(last_time, last_tt).cs.year();
-  bool leap_year = IsLeap(last_year_);
-  const civil_second jan1(last_year_);
-  std::int_fast64_t jan1_time = jan1 - civil_second();
-  int jan1_weekday = ToPosixWeekday(get_weekday(jan1));
-
-  Transition dst = {0, dst_ti, civil_second(), civil_second()};
-  Transition std = {0, std_ti, civil_second(), civil_second()};
-  for (const year_t limit = last_year_ + 400;; ++last_year_) {
-    auto dst_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_start);
-    auto std_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_end);
-    dst.unix_time = jan1_time + dst_trans_off - posix.std_offset;
-    std.unix_time = jan1_time + std_trans_off - posix.dst_offset;
-    const auto* ta = dst.unix_time < std.unix_time ? &dst : &std;
-    const auto* tb = dst.unix_time < std.unix_time ? &std : &dst;
-    if (last_time < tb->unix_time) {
-      if (last_time < ta->unix_time) transitions_.push_back(*ta);
-      transitions_.push_back(*tb);
-    }
-    if (last_year_ == limit) break;
-    jan1_time += kSecsPerYear[leap_year];
-    jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7;
-    leap_year = !leap_year && IsLeap(last_year_ + 1);
-  }
-
-  return true;
-}
-
-bool TimeZoneInfo::Load(ZoneInfoSource* zip) {
-  // Read and validate the header.
-  tzhead tzh;
-  if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
-  if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
-    return false;
-  Header hdr;
-  if (!hdr.Build(tzh)) return false;
-  std::size_t time_len = 4;
-  if (tzh.tzh_version[0] != '\0') {
-    // Skip the 4-byte data.
-    if (zip->Skip(hdr.DataLength(time_len)) != 0) return false;
-    // Read and validate the header for the 8-byte data.
-    if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
-    if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
-      return false;
-    if (tzh.tzh_version[0] == '\0') return false;
-    if (!hdr.Build(tzh)) return false;
-    time_len = 8;
-  }
-  if (hdr.typecnt == 0) return false;
-  if (hdr.leapcnt != 0) {
-    // This code assumes 60-second minutes so we do not want
-    // the leap-second encoded zoneinfo. We could reverse the
-    // compensation, but the "right" encoding is rarely used
-    // so currently we simply reject such data.
-    return false;
-  }
-  if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false;
-  if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false;
-
-  // Read the data into a local buffer.
-  std::size_t len = hdr.DataLength(time_len);
-  std::vector<char> tbuf(len);
-  if (zip->Read(tbuf.data(), len) != len) return false;
-  const char* bp = tbuf.data();
-
-  // Decode and validate the transitions.
-  transitions_.reserve(hdr.timecnt + 2);
-  transitions_.resize(hdr.timecnt);
-  for (std::size_t i = 0; i != hdr.timecnt; ++i) {
-    transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp);
-    bp += time_len;
-    if (i != 0) {
-      // Check that the transitions are ordered by time (as zic guarantees).
-      if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i]))
-        return false;  // out of order
-    }
-  }
-  bool seen_type_0 = false;
-  for (std::size_t i = 0; i != hdr.timecnt; ++i) {
-    transitions_[i].type_index = Decode8(bp++);
-    if (transitions_[i].type_index >= hdr.typecnt) return false;
-    if (transitions_[i].type_index == 0) seen_type_0 = true;
-  }
-
-  // Decode and validate the transition types.
-  transition_types_.reserve(hdr.typecnt + 2);
-  transition_types_.resize(hdr.typecnt);
-  for (std::size_t i = 0; i != hdr.typecnt; ++i) {
-    transition_types_[i].utc_offset =
-        static_cast<std::int_least32_t>(Decode32(bp));
-    if (transition_types_[i].utc_offset >= kSecsPerDay ||
-        transition_types_[i].utc_offset <= -kSecsPerDay)
-      return false;
-    bp += 4;
-    transition_types_[i].is_dst = (Decode8(bp++) != 0);
-    transition_types_[i].abbr_index = Decode8(bp++);
-    if (transition_types_[i].abbr_index >= hdr.charcnt) return false;
-  }
-
-  // Determine the before-first-transition type.
-  default_transition_type_ = 0;
-  if (seen_type_0 && hdr.timecnt != 0) {
-    std::uint_fast8_t index = 0;
-    if (transition_types_[0].is_dst) {
-      index = transitions_[0].type_index;
-      while (index != 0 && transition_types_[index].is_dst) --index;
-    }
-    while (index != hdr.typecnt && transition_types_[index].is_dst) ++index;
-    if (index != hdr.typecnt) default_transition_type_ = index;
-  }
-
-  // Copy all the abbreviations.
-  abbreviations_.reserve(hdr.charcnt + 10);
-  abbreviations_.assign(bp, hdr.charcnt);
-  bp += hdr.charcnt;
-
-  // Skip the unused portions. We've already dispensed with leap-second
-  // encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when
-  // interpreting a POSIX spec that does not include start/end rules, and
-  // that isn't the case here (see "zic -p").
-  bp += (8 + 4) * hdr.leapcnt;  // leap-time + TAI-UTC
-  bp += 1 * hdr.ttisstdcnt;     // UTC/local indicators
-  bp += 1 * hdr.ttisutcnt;      // standard/wall indicators
-  assert(bp == tbuf.data() + tbuf.size());
-
-  future_spec_.clear();
-  if (tzh.tzh_version[0] != '\0') {
-    // Snarf up the NL-enclosed future POSIX spec. Note
-    // that version '3' files utilize an extended format.
-    auto get_char = [](ZoneInfoSource* azip) -> int {
-      unsigned char ch;  // all non-EOF results are positive
-      return (azip->Read(&ch, 1) == 1) ? ch : EOF;
-    };
-    if (get_char(zip) != '\n') return false;
-    for (int c = get_char(zip); c != '\n'; c = get_char(zip)) {
-      if (c == EOF) return false;
-      future_spec_.push_back(static_cast<char>(c));
-    }
-  }
-
-  // We don't check for EOF so that we're forwards compatible.
-
-  // If we did not find version information during the standard loading
-  // process (as of tzh_version '3' that is unsupported), then ask the
-  // ZoneInfoSource for any out-of-bound version string it may be privy to.
-  if (version_.empty()) {
-    version_ = zip->Version();
-  }
-
-  // Trim redundant transitions. zic may have added these to work around
-  // differences between the glibc and reference implementations (see
-  // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071).
-  // For us, they just get in the way when we do future_spec_ extension.
-  while (hdr.timecnt > 1) {
-    if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index,
-                          transitions_[hdr.timecnt - 2].type_index)) {
-      break;
-    }
-    hdr.timecnt -= 1;
-  }
-  transitions_.resize(hdr.timecnt);
-
-  // Ensure that there is always a transition in the first half of the
-  // time line (the second half is handled below) so that the signed
-  // difference between a civil_second and the civil_second of its
-  // previous transition is always representable, without overflow.
-  if (transitions_.empty() || transitions_.front().unix_time >= 0) {
-    Transition& tr(*transitions_.emplace(transitions_.begin()));
-    tr.unix_time = -(1LL << 59);  // -18267312070-10-26T17:01:52+00:00
-    tr.type_index = default_transition_type_;
-  }
-
-  // Extend the transitions using the future specification.
-  if (!ExtendTransitions()) return false;
-
-  // Ensure that there is always a transition in the second half of the
-  // time line (the first half is handled above) so that the signed
-  // difference between a civil_second and the civil_second of its
-  // previous transition is always representable, without overflow.
-  const Transition& last(transitions_.back());
-  if (last.unix_time < 0) {
-    const std::uint_fast8_t type_index = last.type_index;
-    Transition& tr(*transitions_.emplace(transitions_.end()));
-    tr.unix_time = 2147483647;  // 2038-01-19T03:14:07+00:00
-    tr.type_index = type_index;
-  }
-
-  // Compute the local civil time for each transition and the preceding
-  // second. These will be used for reverse conversions in MakeTime().
-  const TransitionType* ttp = &transition_types_[default_transition_type_];
-  for (std::size_t i = 0; i != transitions_.size(); ++i) {
-    Transition& tr(transitions_[i]);
-    tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1;
-    ttp = &transition_types_[tr.type_index];
-    tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs;
-    if (i != 0) {
-      // Check that the transitions are ordered by civil time. Essentially
-      // this means that an offset change cannot cross another such change.
-      // No one does this in practice, and we depend on it in MakeTime().
-      if (!Transition::ByCivilTime()(transitions_[i - 1], tr))
-        return false;  // out of order
-    }
-  }
-
-  // Compute the maximum/minimum civil times that can be converted to a
-  // time_point<seconds> for each of the zone's transition types.
-  for (auto& tt : transition_types_) {
-    tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
-    tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
-  }
-
-  transitions_.shrink_to_fit();
-  return true;
-}
-
-namespace {
-
-// fopen(3) adaptor.
-inline FILE* FOpen(const char* path, const char* mode) {
-#if defined(_MSC_VER)
-  FILE* fp;
-  if (fopen_s(&fp, path, mode) != 0) fp = nullptr;
-  return fp;
-#else
-  return fopen(path, mode);  // TODO: Enable the close-on-exec flag.
-#endif
-}
-
-// A stdio(3)-backed implementation of ZoneInfoSource.
-class FileZoneInfoSource : public ZoneInfoSource {
- public:
-  static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
-
-  std::size_t Read(void* ptr, std::size_t size) override {
-    size = std::min(size, len_);
-    std::size_t nread = fread(ptr, 1, size, fp_.get());
-    len_ -= nread;
-    return nread;
-  }
-  int Skip(std::size_t offset) override {
-    offset = std::min(offset, len_);
-    int rc = fseek(fp_.get(), static_cast<long>(offset), SEEK_CUR);
-    if (rc == 0) len_ -= offset;
-    return rc;
-  }
-  std::string Version() const override {
-    // TODO: It would nice if the zoneinfo data included the tzdb version.
-    return std::string();
-  }
-
- protected:
-  explicit FileZoneInfoSource(
-      FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max())
-      : fp_(fp, fclose), len_(len) {}
-
- private:
-  std::unique_ptr<FILE, int (*)(FILE*)> fp_;
-  std::size_t len_;
-};
-
-std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
-    const std::string& name) {
-  // Use of the "file:" prefix is intended for testing purposes only.
-  const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
-
-  // Map the time-zone name to a path name.
-  std::string path;
-  if (pos == name.size() || name[pos] != '/') {
-    const char* tzdir = "/usr/share/zoneinfo";
-    char* tzdir_env = nullptr;
-#if defined(_MSC_VER)
-    _dupenv_s(&tzdir_env, nullptr, "TZDIR");
-#else
-    tzdir_env = std::getenv("TZDIR");
-#endif
-    if (tzdir_env && *tzdir_env) tzdir = tzdir_env;
-    path += tzdir;
-    path += '/';
-#if defined(_MSC_VER)
-    free(tzdir_env);
-#endif
-  }
-  path.append(name, pos, std::string::npos);
-
-  // Open the zoneinfo file.
-  FILE* fp = FOpen(path.c_str(), "rb");
-  if (fp == nullptr) return nullptr;
-  std::size_t length = 0;
-  if (fseek(fp, 0, SEEK_END) == 0) {
-    long offset = ftell(fp);
-    if (offset >= 0) {
-      length = static_cast<std::size_t>(offset);
-    }
-    rewind(fp);
-  }
-  return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length));
-}
-
-class AndroidZoneInfoSource : public FileZoneInfoSource {
- public:
-  static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
-  std::string Version() const override { return version_; }
-
- private:
-  explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers)
-      : FileZoneInfoSource(fp, len), version_(vers) {}
-  std::string version_;
-};
-
-std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
-    const std::string& name) {
-  // Use of the "file:" prefix is intended for testing purposes only.
-  const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
-
-  // See Android's libc/tzcode/bionic.cpp for additional information.
-  for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
-                             "/system/usr/share/zoneinfo/tzdata"}) {
-    std::unique_ptr<FILE, int (*)(FILE*)> fp(FOpen(tzdata, "rb"), fclose);
-    if (fp.get() == nullptr) continue;
-
-    char hbuf[24];  // covers header.zonetab_offset too
-    if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
-    if (strncmp(hbuf, "tzdata", 6) != 0) continue;
-    const char* vers = (hbuf[11] == '\0') ? hbuf + 6 : "";
-    const std::int_fast32_t index_offset = Decode32(hbuf + 12);
-    const std::int_fast32_t data_offset = Decode32(hbuf + 16);
-    if (index_offset < 0 || data_offset < index_offset) continue;
-    if (fseek(fp.get(), static_cast<long>(index_offset), SEEK_SET) != 0)
-      continue;
-
-    char ebuf[52];  // covers entry.unused too
-    const std::size_t index_size =
-        static_cast<std::size_t>(data_offset - index_offset);
-    const std::size_t zonecnt = index_size / sizeof(ebuf);
-    if (zonecnt * sizeof(ebuf) != index_size) continue;
-    for (std::size_t i = 0; i != zonecnt; ++i) {
-      if (fread(ebuf, 1, sizeof(ebuf), fp.get()) != sizeof(ebuf)) break;
-      const std::int_fast32_t start = data_offset + Decode32(ebuf + 40);
-      const std::int_fast32_t length = Decode32(ebuf + 44);
-      if (start < 0 || length < 0) break;
-      ebuf[40] = '\0';  // ensure zone name is NUL terminated
-      if (strcmp(name.c_str() + pos, ebuf) == 0) {
-        if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
-        return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
-            fp.release(), static_cast<std::size_t>(length), vers));
-      }
-    }
-  }
-
-  return nullptr;
-}
-
-}  // namespace
-
-bool TimeZoneInfo::Load(const std::string& name) {
-  // We can ensure that the loading of UTC or any other fixed-offset
-  // zone never fails because the simple, fixed-offset state can be
-  // internally generated. Note that this depends on our choice to not
-  // accept leap-second encoded ("right") zoneinfo.
-  auto offset = seconds::zero();
-  if (FixedOffsetFromName(name, &offset)) {
-    return ResetToBuiltinUTC(offset);
-  }
-
-  // Find and use a ZoneInfoSource to load the named zone.
-  auto zip = cctz_extension::zone_info_source_factory(
-      name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> {
-        if (auto z = FileZoneInfoSource::Open(n)) return z;
-        if (auto z = AndroidZoneInfoSource::Open(n)) return z;
-        return nullptr;
-      });
-  return zip != nullptr && Load(zip.get());
-}
-
-// BreakTime() translation for a particular transition type.
-time_zone::absolute_lookup TimeZoneInfo::LocalTime(
-    std::int_fast64_t unix_time, const TransitionType& tt) const {
-  // A civil time in "+offset" looks like (time+offset) in UTC.
-  // Note: We perform two additions in the civil_second domain to
-  // sidestep the chance of overflow in (unix_time + tt.utc_offset).
-  return {(civil_second() + unix_time) + tt.utc_offset, tt.utc_offset,
-          tt.is_dst, &abbreviations_[tt.abbr_index]};
-}
-
-// BreakTime() translation for a particular transition.
-time_zone::absolute_lookup TimeZoneInfo::LocalTime(std::int_fast64_t unix_time,
-                                                   const Transition& tr) const {
-  const TransitionType& tt = transition_types_[tr.type_index];
-  // Note: (unix_time - tr.unix_time) will never overflow as we
-  // have ensured that there is always a "nearby" transition.
-  return {tr.civil_sec + (unix_time - tr.unix_time),  // TODO: Optimize.
-          tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]};
-}
-
-// MakeTime() translation with a conversion-preserving +N * 400-year shift.
-time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
-                                                year_t c4_shift) const {
-  assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_);
-  time_zone::civil_lookup cl = MakeTime(cs);
-  if (c4_shift > seconds::max().count() / kSecsPer400Years) {
-    cl.pre = cl.trans = cl.post = time_point<seconds>::max();
-  } else {
-    const auto offset = seconds(c4_shift * kSecsPer400Years);
-    const auto limit = time_point<seconds>::max() - offset;
-    for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) {
-      if (*tp > limit) {
-        *tp = time_point<seconds>::max();
-      } else {
-        *tp += offset;
-      }
-    }
-  }
-  return cl;
-}
-
-time_zone::absolute_lookup TimeZoneInfo::BreakTime(
-    const time_point<seconds>& tp) const {
-  std::int_fast64_t unix_time = ToUnixSeconds(tp);
-  const std::size_t timecnt = transitions_.size();
-  assert(timecnt != 0);  // We always add a transition.
-
-  if (unix_time < transitions_[0].unix_time) {
-    return LocalTime(unix_time, transition_types_[default_transition_type_]);
-  }
-  if (unix_time >= transitions_[timecnt - 1].unix_time) {
-    // After the last transition. If we extended the transitions using
-    // future_spec_, shift back to a supported year using the 400-year
-    // cycle of calendaric equivalence and then compensate accordingly.
-    if (extended_) {
-      const std::int_fast64_t diff =
-          unix_time - transitions_[timecnt - 1].unix_time;
-      const year_t shift = diff / kSecsPer400Years + 1;
-      const auto d = seconds(shift * kSecsPer400Years);
-      time_zone::absolute_lookup al = BreakTime(tp - d);
-      al.cs = YearShift(al.cs, shift * 400);
-      return al;
-    }
-    return LocalTime(unix_time, transitions_[timecnt - 1]);
-  }
-
-  const std::size_t hint = local_time_hint_.load(std::memory_order_relaxed);
-  if (0 < hint && hint < timecnt) {
-    if (transitions_[hint - 1].unix_time <= unix_time) {
-      if (unix_time < transitions_[hint].unix_time) {
-        return LocalTime(unix_time, transitions_[hint - 1]);
-      }
-    }
-  }
-
-  const Transition target = {unix_time, 0, civil_second(), civil_second()};
-  const Transition* begin = &transitions_[0];
-  const Transition* tr = std::upper_bound(begin, begin + timecnt, target,
-                                          Transition::ByUnixTime());
-  local_time_hint_.store(static_cast<std::size_t>(tr - begin),
-                         std::memory_order_relaxed);
-  return LocalTime(unix_time, *--tr);
-}
-
-time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
-  const std::size_t timecnt = transitions_.size();
-  assert(timecnt != 0);  // We always add a transition.
-
-  // Find the first transition after our target civil time.
-  const Transition* tr = nullptr;
-  const Transition* begin = &transitions_[0];
-  const Transition* end = begin + timecnt;
-  if (cs < begin->civil_sec) {
-    tr = begin;
-  } else if (cs >= transitions_[timecnt - 1].civil_sec) {
-    tr = end;
-  } else {
-    const std::size_t hint = time_local_hint_.load(std::memory_order_relaxed);
-    if (0 < hint && hint < timecnt) {
-      if (transitions_[hint - 1].civil_sec <= cs) {
-        if (cs < transitions_[hint].civil_sec) {
-          tr = begin + hint;
-        }
-      }
-    }
-    if (tr == nullptr) {
-      const Transition target = {0, 0, cs, civil_second()};
-      tr = std::upper_bound(begin, end, target, Transition::ByCivilTime());
-      time_local_hint_.store(static_cast<std::size_t>(tr - begin),
-                             std::memory_order_relaxed);
-    }
-  }
-
-  if (tr == begin) {
-    if (tr->prev_civil_sec >= cs) {
-      // Before first transition, so use the default offset.
-      const TransitionType& tt(transition_types_[default_transition_type_]);
-      if (cs < tt.civil_min) return MakeUnique(time_point<seconds>::min());
-      return MakeUnique(cs - (civil_second() + tt.utc_offset));
-    }
-    // tr->prev_civil_sec < cs < tr->civil_sec
-    return MakeSkipped(*tr, cs);
-  }
-
-  if (tr == end) {
-    if (cs > (--tr)->prev_civil_sec) {
-      // After the last transition. If we extended the transitions using
-      // future_spec_, shift back to a supported year using the 400-year
-      // cycle of calendaric equivalence and then compensate accordingly.
-      if (extended_ && cs.year() > last_year_) {
-        const year_t shift = (cs.year() - last_year_ - 1) / 400 + 1;
-        return TimeLocal(YearShift(cs, shift * -400), shift);
-      }
-      const TransitionType& tt(transition_types_[tr->type_index]);
-      if (cs > tt.civil_max) return MakeUnique(time_point<seconds>::max());
-      return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
-    }
-    // tr->civil_sec <= cs <= tr->prev_civil_sec
-    return MakeRepeated(*tr, cs);
-  }
-
-  if (tr->prev_civil_sec < cs) {
-    // tr->prev_civil_sec < cs < tr->civil_sec
-    return MakeSkipped(*tr, cs);
-  }
-
-  if (cs <= (--tr)->prev_civil_sec) {
-    // tr->civil_sec <= cs <= tr->prev_civil_sec
-    return MakeRepeated(*tr, cs);
-  }
-
-  // In between transitions.
-  return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
-}
-
-std::string TimeZoneInfo::Version() const { return version_; }
-
-std::string TimeZoneInfo::Description() const {
-  std::ostringstream oss;
-  oss << "#trans=" << transitions_.size();
-  oss << " #types=" << transition_types_.size();
-  oss << " spec='" << future_spec_ << "'";
-  return oss.str();
-}
-
-bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
-                                  time_zone::civil_transition* trans) const {
-  if (transitions_.empty()) return false;
-  const Transition* begin = &transitions_[0];
-  const Transition* end = begin + transitions_.size();
-  if (begin->unix_time <= -(1LL << 59)) {
-    // Do not report the BIG_BANG found in some zoneinfo data as it is
-    // really a sentinel, not a transition.  See pre-2018f tz/zic.c.
-    ++begin;
-  }
-  std::int_fast64_t unix_time = ToUnixSeconds(tp);
-  const Transition target = {unix_time, 0, civil_second(), civil_second()};
-  const Transition* tr =
-      std::upper_bound(begin, end, target, Transition::ByUnixTime());
-  for (; tr != end; ++tr) {  // skip no-op transitions
-    std::uint_fast8_t prev_type_index =
-        (tr == begin) ? default_transition_type_ : tr[-1].type_index;
-    if (!EquivTransitions(prev_type_index, tr[0].type_index)) break;
-  }
-  // When tr == end we return false, ignoring future_spec_.
-  if (tr == end) return false;
-  trans->from = tr->prev_civil_sec + 1;
-  trans->to = tr->civil_sec;
-  return true;
-}
-
-bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
-                                  time_zone::civil_transition* trans) const {
-  if (transitions_.empty()) return false;
-  const Transition* begin = &transitions_[0];
-  const Transition* end = begin + transitions_.size();
-  if (begin->unix_time <= -(1LL << 59)) {
-    // Do not report the BIG_BANG found in some zoneinfo data as it is
-    // really a sentinel, not a transition.  See pre-2018f tz/zic.c.
-    ++begin;
-  }
-  std::int_fast64_t unix_time = ToUnixSeconds(tp);
-  if (FromUnixSeconds(unix_time) != tp) {
-    if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) {
-      if (end == begin) return false;  // Ignore future_spec_.
-      trans->from = (--end)->prev_civil_sec + 1;
-      trans->to = end->civil_sec;
-      return true;
-    }
-    unix_time += 1;  // ceils
-  }
-  const Transition target = {unix_time, 0, civil_second(), civil_second()};
-  const Transition* tr =
-      std::lower_bound(begin, end, target, Transition::ByUnixTime());
-  for (; tr != begin; --tr) {  // skip no-op transitions
-    std::uint_fast8_t prev_type_index =
-        (tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index;
-    if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break;
-  }
-  // When tr == end we return the "last" transition, ignoring future_spec_.
-  if (tr == begin) return false;
-  trans->from = (--tr)->prev_civil_sec + 1;
-  trans->to = tr->civil_sec;
-  return true;
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.h b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.h
deleted file mode 100644
index 2467ff559d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
-#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
-
-#include <atomic>
-#include <cstddef>
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
-#include "time_zone_if.h"
-#include "tzfile.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// A transition to a new UTC offset.
-struct Transition {
-  std::int_least64_t unix_time;   // the instant of this transition
-  std::uint_least8_t type_index;  // index of the transition type
-  civil_second civil_sec;         // local civil time of transition
-  civil_second prev_civil_sec;    // local civil time one second earlier
-
-  struct ByUnixTime {
-    inline bool operator()(const Transition& lhs, const Transition& rhs) const {
-      return lhs.unix_time < rhs.unix_time;
-    }
-  };
-  struct ByCivilTime {
-    inline bool operator()(const Transition& lhs, const Transition& rhs) const {
-      return lhs.civil_sec < rhs.civil_sec;
-    }
-  };
-};
-
-// The characteristics of a particular transition.
-struct TransitionType {
-  std::int_least32_t utc_offset;  // the new prevailing UTC offset
-  civil_second civil_max;         // max convertible civil time for offset
-  civil_second civil_min;         // min convertible civil time for offset
-  bool is_dst;                    // did we move into daylight-saving time
-  std::uint_least8_t abbr_index;  // index of the new abbreviation
-};
-
-// A time zone backed by the IANA Time Zone Database (zoneinfo).
-class TimeZoneInfo : public TimeZoneIf {
- public:
-  TimeZoneInfo() = default;
-  TimeZoneInfo(const TimeZoneInfo&) = delete;
-  TimeZoneInfo& operator=(const TimeZoneInfo&) = delete;
-
-  // Loads the zoneinfo for the given name, returning true if successful.
-  bool Load(const std::string& name);
-
-  // TimeZoneIf implementations.
-  time_zone::absolute_lookup BreakTime(
-      const time_point<seconds>& tp) const override;
-  time_zone::civil_lookup MakeTime(const civil_second& cs) const override;
-  bool NextTransition(const time_point<seconds>& tp,
-                      time_zone::civil_transition* trans) const override;
-  bool PrevTransition(const time_point<seconds>& tp,
-                      time_zone::civil_transition* trans) const override;
-  std::string Version() const override;
-  std::string Description() const override;
-
- private:
-  struct Header {            // counts of:
-    std::size_t timecnt;     // transition times
-    std::size_t typecnt;     // transition types
-    std::size_t charcnt;     // zone abbreviation characters
-    std::size_t leapcnt;     // leap seconds (we expect none)
-    std::size_t ttisstdcnt;  // UTC/local indicators (unused)
-    std::size_t ttisutcnt;   // standard/wall indicators (unused)
-
-    bool Build(const tzhead& tzh);
-    std::size_t DataLength(std::size_t time_len) const;
-  };
-
-  bool GetTransitionType(std::int_fast32_t utc_offset, bool is_dst,
-                         const std::string& abbr, std::uint_least8_t* index);
-  bool EquivTransitions(std::uint_fast8_t tt1_index,
-                        std::uint_fast8_t tt2_index) const;
-  bool ExtendTransitions();
-
-  bool ResetToBuiltinUTC(const seconds& offset);
-  bool Load(ZoneInfoSource* zip);
-
-  // Helpers for BreakTime() and MakeTime().
-  time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
-                                       const TransitionType& tt) const;
-  time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
-                                       const Transition& tr) const;
-  time_zone::civil_lookup TimeLocal(const civil_second& cs,
-                                    year_t c4_shift) const;
-
-  std::vector<Transition> transitions_;  // ordered by unix_time and civil_sec
-  std::vector<TransitionType> transition_types_;  // distinct transition types
-  std::uint_fast8_t default_transition_type_;     // for before first transition
-  std::string abbreviations_;  // all the NUL-terminated abbreviations
-
-  std::string version_;      // the tzdata version if available
-  std::string future_spec_;  // for after the last zic transition
-  bool extended_;            // future_spec_ was used to generate transitions
-  year_t last_year_;         // the final year of the generated transitions
-
-  // We remember the transitions found during the last BreakTime() and
-  // MakeTime() calls. If the next request is for the same transition we
-  // will avoid re-searching.
-  mutable std::atomic<std::size_t> local_time_hint_ = {};  // BreakTime() hint
-  mutable std::atomic<std::size_t> time_local_hint_ = {};  // MakeTime() hint
-};
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.cc
deleted file mode 100644
index 887dd097c6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.cc
+++ /dev/null
@@ -1,315 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#if defined(_WIN32) || defined(_WIN64)
-#define _CRT_SECURE_NO_WARNINGS 1
-#endif
-
-#include "time_zone_libc.h"
-
-#include <chrono>
-#include <ctime>
-#include <limits>
-#include <utility>
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-#if defined(_AIX)
-extern "C" {
-extern long altzone;
-}
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-namespace {
-
-#if defined(_WIN32) || defined(_WIN64)
-// Uses the globals: '_timezone', '_dstbias' and '_tzname'.
-auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) {
-  const bool is_dst = tm.tm_isdst > 0;
-  return _timezone + (is_dst ? _dstbias : 0);
-}
-auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) {
-  const bool is_dst = tm.tm_isdst > 0;
-  return _tzname[is_dst];
-}
-#elif defined(__sun) || defined(_AIX)
-// Uses the globals: 'timezone', 'altzone' and 'tzname'.
-auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
-  const bool is_dst = tm.tm_isdst > 0;
-  return is_dst ? altzone : timezone;
-}
-auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
-  const bool is_dst = tm.tm_isdst > 0;
-  return tzname[is_dst];
-}
-#elif defined(__native_client__) || defined(__myriad2__) || \
-    defined(__EMSCRIPTEN__)
-// Uses the globals: 'timezone' and 'tzname'.
-auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) {
-  const bool is_dst = tm.tm_isdst > 0;
-  return _timezone + (is_dst ? 60 * 60 : 0);
-}
-auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
-  const bool is_dst = tm.tm_isdst > 0;
-  return tzname[is_dst];
-}
-#else
-// Adapt to different spellings of the struct std::tm extension fields.
-#if defined(tm_gmtoff)
-auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) {
-  return tm.tm_gmtoff;
-}
-#elif defined(__tm_gmtoff)
-auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) {
-  return tm.__tm_gmtoff;
-}
-#else
-template <typename T>
-auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
-  return tm.tm_gmtoff;
-}
-template <typename T>
-auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
-  return tm.__tm_gmtoff;
-}
-#endif  // tm_gmtoff
-#if defined(tm_zone)
-auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; }
-#elif defined(__tm_zone)
-auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
-  return tm.__tm_zone;
-}
-#else
-template <typename T>
-auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
-  return tm.tm_zone;
-}
-template <typename T>
-auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
-  return tm.__tm_zone;
-}
-#endif  // tm_zone
-#endif
-
-inline std::tm* gm_time(const std::time_t* timep, std::tm* result) {
-#if defined(_WIN32) || defined(_WIN64)
-  return gmtime_s(result, timep) ? nullptr : result;
-#else
-  return gmtime_r(timep, result);
-#endif
-}
-
-inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
-#if defined(_WIN32) || defined(_WIN64)
-  return localtime_s(result, timep) ? nullptr : result;
-#else
-  return localtime_r(timep, result);
-#endif
-}
-
-// Converts a civil second and "dst" flag into a time_t and UTC offset.
-// Returns false if time_t cannot represent the requested civil second.
-// Caller must have already checked that cs.year() will fit into a tm_year.
-bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
-  std::tm tm;
-  tm.tm_year = static_cast<int>(cs.year() - year_t{1900});
-  tm.tm_mon = cs.month() - 1;
-  tm.tm_mday = cs.day();
-  tm.tm_hour = cs.hour();
-  tm.tm_min = cs.minute();
-  tm.tm_sec = cs.second();
-  tm.tm_isdst = is_dst;
-  *t = std::mktime(&tm);
-  if (*t == std::time_t{-1}) {
-    std::tm tm2;
-    const std::tm* tmp = local_time(t, &tm2);
-    if (tmp == nullptr || tmp->tm_year != tm.tm_year ||
-        tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday ||
-        tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min ||
-        tmp->tm_sec != tm.tm_sec) {
-      // A true error (not just one second before the epoch).
-      return false;
-    }
-  }
-  *off = static_cast<int>(tm_gmtoff(tm));
-  return true;
-}
-
-// Find the least time_t in [lo:hi] where local time matches offset, given:
-// (1) lo doesn't match, (2) hi does, and (3) there is only one transition.
-std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
-  std::tm tm;
-  while (lo + 1 != hi) {
-    const std::time_t mid = lo + (hi - lo) / 2;
-    std::tm* tmp = local_time(&mid, &tm);
-    if (tmp != nullptr) {
-      if (tm_gmtoff(*tmp) == offset) {
-        hi = mid;
-      } else {
-        lo = mid;
-      }
-    } else {
-      // If std::tm cannot hold some result we resort to a linear search,
-      // ignoring all failed conversions.  Slow, but never really happens.
-      while (++lo != hi) {
-        tmp = local_time(&lo, &tm);
-        if (tmp != nullptr) {
-          if (tm_gmtoff(*tmp) == offset) break;
-        }
-      }
-      return lo;
-    }
-  }
-  return hi;
-}
-
-}  // namespace
-
-TimeZoneLibC::TimeZoneLibC(const std::string& name)
-    : local_(name == "localtime") {}
-
-time_zone::absolute_lookup TimeZoneLibC::BreakTime(
-    const time_point<seconds>& tp) const {
-  time_zone::absolute_lookup al;
-  al.offset = 0;
-  al.is_dst = false;
-  al.abbr = "-00";
-
-  const std::int_fast64_t s = ToUnixSeconds(tp);
-
-  // If std::time_t cannot hold the input we saturate the output.
-  if (s < std::numeric_limits<std::time_t>::min()) {
-    al.cs = civil_second::min();
-    return al;
-  }
-  if (s > std::numeric_limits<std::time_t>::max()) {
-    al.cs = civil_second::max();
-    return al;
-  }
-
-  const std::time_t t = static_cast<std::time_t>(s);
-  std::tm tm;
-  std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
-
-  // If std::tm cannot hold the result we saturate the output.
-  if (tmp == nullptr) {
-    al.cs = (s < 0) ? civil_second::min() : civil_second::max();
-    return al;
-  }
-
-  const year_t year = tmp->tm_year + year_t{1900};
-  al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour,
-                       tmp->tm_min, tmp->tm_sec);
-  al.offset = static_cast<int>(tm_gmtoff(*tmp));
-  al.abbr = local_ ? tm_zone(*tmp) : "UTC";  // as expected by cctz
-  al.is_dst = tmp->tm_isdst > 0;
-  return al;
-}
-
-time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
-  if (!local_) {
-    // If time_point<seconds> cannot hold the result we saturate.
-    static const civil_second min_tp_cs =
-        civil_second() + ToUnixSeconds(time_point<seconds>::min());
-    static const civil_second max_tp_cs =
-        civil_second() + ToUnixSeconds(time_point<seconds>::max());
-    const time_point<seconds> tp = (cs < min_tp_cs) ? time_point<seconds>::min()
-                                   : (cs > max_tp_cs)
-                                       ? time_point<seconds>::max()
-                                       : FromUnixSeconds(cs - civil_second());
-    return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
-  }
-
-  // If tm_year cannot hold the requested year we saturate the result.
-  if (cs.year() < 0) {
-    if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
-      const time_point<seconds> tp = time_point<seconds>::min();
-      return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
-    }
-  } else {
-    if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
-      const time_point<seconds> tp = time_point<seconds>::max();
-      return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
-    }
-  }
-
-  // We probe with "is_dst" values of 0 and 1 to try to distinguish unique
-  // civil seconds from skipped or repeated ones.  This is not always possible
-  // however, as the "dst" flag does not change over some offset transitions.
-  // We are also subject to the vagaries of mktime() implementations.
-  std::time_t t0, t1;
-  int offset0, offset1;
-  if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) {
-    if (t0 == t1) {
-      // The civil time was singular (pre == trans == post).
-      const time_point<seconds> tp = FromUnixSeconds(t0);
-      return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
-    }
-
-    if (t0 > t1) {
-      std::swap(t0, t1);
-      std::swap(offset0, offset1);
-    }
-    const std::time_t tt = find_trans(t0, t1, offset1);
-    const time_point<seconds> trans = FromUnixSeconds(tt);
-
-    if (offset0 < offset1) {
-      // The civil time did not exist (pre >= trans > post).
-      const time_point<seconds> pre = FromUnixSeconds(t1);
-      const time_point<seconds> post = FromUnixSeconds(t0);
-      return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
-    }
-
-    // The civil time was ambiguous (pre < trans <= post).
-    const time_point<seconds> pre = FromUnixSeconds(t0);
-    const time_point<seconds> post = FromUnixSeconds(t1);
-    return {time_zone::civil_lookup::REPEATED, pre, trans, post};
-  }
-
-  // make_time() failed somehow so we saturate the result.
-  const time_point<seconds> tp = (cs < civil_second())
-                                     ? time_point<seconds>::min()
-                                     : time_point<seconds>::max();
-  return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
-}
-
-bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
-                                  time_zone::civil_transition*) const {
-  return false;
-}
-
-bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
-                                  time_zone::civil_transition*) const {
-  return false;
-}
-
-std::string TimeZoneLibC::Version() const {
-  return std::string();  // unknown
-}
-
-std::string TimeZoneLibC::Description() const {
-  return local_ ? "localtime" : "UTC";
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.h b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.h
deleted file mode 100644
index 1da9039a15..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
-#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
-
-#include <string>
-
-#include "absl/base/config.h"
-#include "time_zone_if.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// A time zone backed by gmtime_r(3), localtime_r(3), and mktime(3),
-// and which therefore only supports UTC and the local time zone.
-// TODO: Add support for fixed offsets from UTC.
-class TimeZoneLibC : public TimeZoneIf {
- public:
-  explicit TimeZoneLibC(const std::string& name);
-
-  // TimeZoneIf implementations.
-  time_zone::absolute_lookup BreakTime(
-      const time_point<seconds>& tp) const override;
-  time_zone::civil_lookup MakeTime(const civil_second& cs) const override;
-  bool NextTransition(const time_point<seconds>& tp,
-                      time_zone::civil_transition* trans) const override;
-  bool PrevTransition(const time_point<seconds>& tp,
-                      time_zone::civil_transition* trans) const override;
-  std::string Version() const override;
-  std::string Description() const override;
-
- private:
-  const bool local_;  // localtime or UTC
-};
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup.cc
deleted file mode 100644
index efdea64b4e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-#if defined(__ANDROID__)
-#include <sys/system_properties.h>
-#if defined(__ANDROID_API__) && __ANDROID_API__ >= 21
-#include <dlfcn.h>
-#endif
-#endif
-
-#if defined(__APPLE__)
-#include <CoreFoundation/CFTimeZone.h>
-
-#include <vector>
-#endif
-
-#include <cstdlib>
-#include <cstring>
-#include <string>
-
-#include "time_zone_fixed.h"
-#include "time_zone_impl.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-#if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21
-namespace {
-// Android 'L' removes __system_property_get() from the NDK, however
-// it is still a hidden symbol in libc so we use dlsym() to access it.
-// See Chromium's base/sys_info_android.cc for a similar example.
-
-using property_get_func = int (*)(const char*, char*);
-
-property_get_func LoadSystemPropertyGet() {
-  int flag = RTLD_LAZY | RTLD_GLOBAL;
-#if defined(RTLD_NOLOAD)
-  flag |= RTLD_NOLOAD;  // libc.so should already be resident
-#endif
-  if (void* handle = dlopen("libc.so", flag)) {
-    void* sym = dlsym(handle, "__system_property_get");
-    dlclose(handle);
-    return reinterpret_cast<property_get_func>(sym);
-  }
-  return nullptr;
-}
-
-int __system_property_get(const char* name, char* value) {
-  static property_get_func system_property_get = LoadSystemPropertyGet();
-  return system_property_get ? system_property_get(name, value) : -1;
-}
-
-}  // namespace
-#endif
-
-std::string time_zone::name() const { return effective_impl().Name(); }
-
-time_zone::absolute_lookup time_zone::lookup(
-    const time_point<seconds>& tp) const {
-  return effective_impl().BreakTime(tp);
-}
-
-time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
-  return effective_impl().MakeTime(cs);
-}
-
-bool time_zone::next_transition(const time_point<seconds>& tp,
-                                civil_transition* trans) const {
-  return effective_impl().NextTransition(tp, trans);
-}
-
-bool time_zone::prev_transition(const time_point<seconds>& tp,
-                                civil_transition* trans) const {
-  return effective_impl().PrevTransition(tp, trans);
-}
-
-std::string time_zone::version() const { return effective_impl().Version(); }
-
-std::string time_zone::description() const {
-  return effective_impl().Description();
-}
-
-const time_zone::Impl& time_zone::effective_impl() const {
-  if (impl_ == nullptr) {
-    // Dereferencing an implicit-UTC time_zone is expected to be
-    // rare, so we don't mind paying a small synchronization cost.
-    return *time_zone::Impl::UTC().impl_;
-  }
-  return *impl_;
-}
-
-bool load_time_zone(const std::string& name, time_zone* tz) {
-  return time_zone::Impl::LoadTimeZone(name, tz);
-}
-
-time_zone utc_time_zone() {
-  return time_zone::Impl::UTC();  // avoid name lookup
-}
-
-time_zone fixed_time_zone(const seconds& offset) {
-  time_zone tz;
-  load_time_zone(FixedOffsetToName(offset), &tz);
-  return tz;
-}
-
-time_zone local_time_zone() {
-  const char* zone = ":localtime";
-#if defined(__ANDROID__)
-  char sysprop[PROP_VALUE_MAX];
-  if (__system_property_get("persist.sys.timezone", sysprop) > 0) {
-    zone = sysprop;
-  }
-#endif
-#if defined(__APPLE__)
-  std::vector<char> buffer;
-  CFTimeZoneRef tz_default = CFTimeZoneCopyDefault();
-  if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) {
-    CFStringEncoding encoding = kCFStringEncodingUTF8;
-    CFIndex length = CFStringGetLength(tz_name);
-    buffer.resize(CFStringGetMaximumSizeForEncoding(length, encoding) + 1);
-    if (CFStringGetCString(tz_name, &buffer[0], buffer.size(), encoding)) {
-      zone = &buffer[0];
-    }
-  }
-  CFRelease(tz_default);
-#endif
-
-  // Allow ${TZ} to override to default zone.
-  char* tz_env = nullptr;
-#if defined(_MSC_VER)
-  _dupenv_s(&tz_env, nullptr, "TZ");
-#else
-  tz_env = std::getenv("TZ");
-#endif
-  if (tz_env) zone = tz_env;
-
-  // We only support the "[:]<zone-name>" form.
-  if (*zone == ':') ++zone;
-
-  // Map "localtime" to a system-specific name, but
-  // allow ${LOCALTIME} to override the default name.
-  char* localtime_env = nullptr;
-  if (strcmp(zone, "localtime") == 0) {
-#if defined(_MSC_VER)
-    // System-specific default is just "localtime".
-    _dupenv_s(&localtime_env, nullptr, "LOCALTIME");
-#else
-    zone = "/etc/localtime";  // System-specific default.
-    localtime_env = std::getenv("LOCALTIME");
-#endif
-    if (localtime_env) zone = localtime_env;
-  }
-
-  const std::string name = zone;
-#if defined(_MSC_VER)
-  free(localtime_env);
-  free(tz_env);
-#endif
-
-  time_zone tz;
-  load_time_zone(name, &tz);  // Falls back to UTC.
-  // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
-  // arrange for %z to generate "-0000" when we don't know the local
-  // offset because the load_time_zone() failed and we're using UTC.
-  return tz;
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
deleted file mode 100644
index 9a1a8d6e40..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ /dev/null
@@ -1,1442 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include <chrono>
-#include <cstddef>
-#include <cstdlib>
-#include <future>
-#include <limits>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-namespace chrono = std::chrono;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-namespace {
-
-// A list of known time-zone names.
-const char* const kTimeZoneNames[] = {"Africa/Abidjan",
-                                      "Africa/Accra",
-                                      "Africa/Addis_Ababa",
-                                      "Africa/Algiers",
-                                      "Africa/Asmara",
-                                      "Africa/Asmera",
-                                      "Africa/Bamako",
-                                      "Africa/Bangui",
-                                      "Africa/Banjul",
-                                      "Africa/Bissau",
-                                      "Africa/Blantyre",
-                                      "Africa/Brazzaville",
-                                      "Africa/Bujumbura",
-                                      "Africa/Cairo",
-                                      "Africa/Casablanca",
-                                      "Africa/Ceuta",
-                                      "Africa/Conakry",
-                                      "Africa/Dakar",
-                                      "Africa/Dar_es_Salaam",
-                                      "Africa/Djibouti",
-                                      "Africa/Douala",
-                                      "Africa/El_Aaiun",
-                                      "Africa/Freetown",
-                                      "Africa/Gaborone",
-                                      "Africa/Harare",
-                                      "Africa/Johannesburg",
-                                      "Africa/Juba",
-                                      "Africa/Kampala",
-                                      "Africa/Khartoum",
-                                      "Africa/Kigali",
-                                      "Africa/Kinshasa",
-                                      "Africa/Lagos",
-                                      "Africa/Libreville",
-                                      "Africa/Lome",
-                                      "Africa/Luanda",
-                                      "Africa/Lubumbashi",
-                                      "Africa/Lusaka",
-                                      "Africa/Malabo",
-                                      "Africa/Maputo",
-                                      "Africa/Maseru",
-                                      "Africa/Mbabane",
-                                      "Africa/Mogadishu",
-                                      "Africa/Monrovia",
-                                      "Africa/Nairobi",
-                                      "Africa/Ndjamena",
-                                      "Africa/Niamey",
-                                      "Africa/Nouakchott",
-                                      "Africa/Ouagadougou",
-                                      "Africa/Porto-Novo",
-                                      "Africa/Sao_Tome",
-                                      "Africa/Timbuktu",
-                                      "Africa/Tripoli",
-                                      "Africa/Tunis",
-                                      "Africa/Windhoek",
-                                      "America/Adak",
-                                      "America/Anchorage",
-                                      "America/Anguilla",
-                                      "America/Antigua",
-                                      "America/Araguaina",
-                                      "America/Argentina/Buenos_Aires",
-                                      "America/Argentina/Catamarca",
-                                      "America/Argentina/ComodRivadavia",
-                                      "America/Argentina/Cordoba",
-                                      "America/Argentina/Jujuy",
-                                      "America/Argentina/La_Rioja",
-                                      "America/Argentina/Mendoza",
-                                      "America/Argentina/Rio_Gallegos",
-                                      "America/Argentina/Salta",
-                                      "America/Argentina/San_Juan",
-                                      "America/Argentina/San_Luis",
-                                      "America/Argentina/Tucuman",
-                                      "America/Argentina/Ushuaia",
-                                      "America/Aruba",
-                                      "America/Asuncion",
-                                      "America/Atikokan",
-                                      "America/Atka",
-                                      "America/Bahia",
-                                      "America/Bahia_Banderas",
-                                      "America/Barbados",
-                                      "America/Belem",
-                                      "America/Belize",
-                                      "America/Blanc-Sablon",
-                                      "America/Boa_Vista",
-                                      "America/Bogota",
-                                      "America/Boise",
-                                      "America/Buenos_Aires",
-                                      "America/Cambridge_Bay",
-                                      "America/Campo_Grande",
-                                      "America/Cancun",
-                                      "America/Caracas",
-                                      "America/Catamarca",
-                                      "America/Cayenne",
-                                      "America/Cayman",
-                                      "America/Chicago",
-                                      "America/Chihuahua",
-                                      "America/Coral_Harbour",
-                                      "America/Cordoba",
-                                      "America/Costa_Rica",
-                                      "America/Creston",
-                                      "America/Cuiaba",
-                                      "America/Curacao",
-                                      "America/Danmarkshavn",
-                                      "America/Dawson",
-                                      "America/Dawson_Creek",
-                                      "America/Denver",
-                                      "America/Detroit",
-                                      "America/Dominica",
-                                      "America/Edmonton",
-                                      "America/Eirunepe",
-                                      "America/El_Salvador",
-                                      "America/Ensenada",
-                                      "America/Fort_Nelson",
-                                      "America/Fort_Wayne",
-                                      "America/Fortaleza",
-                                      "America/Glace_Bay",
-                                      "America/Godthab",
-                                      "America/Goose_Bay",
-                                      "America/Grand_Turk",
-                                      "America/Grenada",
-                                      "America/Guadeloupe",
-                                      "America/Guatemala",
-                                      "America/Guayaquil",
-                                      "America/Guyana",
-                                      "America/Halifax",
-                                      "America/Havana",
-                                      "America/Hermosillo",
-                                      "America/Indiana/Indianapolis",
-                                      "America/Indiana/Knox",
-                                      "America/Indiana/Marengo",
-                                      "America/Indiana/Petersburg",
-                                      "America/Indiana/Tell_City",
-                                      "America/Indiana/Vevay",
-                                      "America/Indiana/Vincennes",
-                                      "America/Indiana/Winamac",
-                                      "America/Indianapolis",
-                                      "America/Inuvik",
-                                      "America/Iqaluit",
-                                      "America/Jamaica",
-                                      "America/Jujuy",
-                                      "America/Juneau",
-                                      "America/Kentucky/Louisville",
-                                      "America/Kentucky/Monticello",
-                                      "America/Knox_IN",
-                                      "America/Kralendijk",
-                                      "America/La_Paz",
-                                      "America/Lima",
-                                      "America/Los_Angeles",
-                                      "America/Louisville",
-                                      "America/Lower_Princes",
-                                      "America/Maceio",
-                                      "America/Managua",
-                                      "America/Manaus",
-                                      "America/Marigot",
-                                      "America/Martinique",
-                                      "America/Matamoros",
-                                      "America/Mazatlan",
-                                      "America/Mendoza",
-                                      "America/Menominee",
-                                      "America/Merida",
-                                      "America/Metlakatla",
-                                      "America/Mexico_City",
-                                      "America/Miquelon",
-                                      "America/Moncton",
-                                      "America/Monterrey",
-                                      "America/Montevideo",
-                                      "America/Montreal",
-                                      "America/Montserrat",
-                                      "America/Nassau",
-                                      "America/New_York",
-                                      "America/Nipigon",
-                                      "America/Nome",
-                                      "America/Noronha",
-                                      "America/North_Dakota/Beulah",
-                                      "America/North_Dakota/Center",
-                                      "America/North_Dakota/New_Salem",
-                                      "America/Nuuk",
-                                      "America/Ojinaga",
-                                      "America/Panama",
-                                      "America/Pangnirtung",
-                                      "America/Paramaribo",
-                                      "America/Phoenix",
-                                      "America/Port-au-Prince",
-                                      "America/Port_of_Spain",
-                                      "America/Porto_Acre",
-                                      "America/Porto_Velho",
-                                      "America/Puerto_Rico",
-                                      "America/Punta_Arenas",
-                                      "America/Rainy_River",
-                                      "America/Rankin_Inlet",
-                                      "America/Recife",
-                                      "America/Regina",
-                                      "America/Resolute",
-                                      "America/Rio_Branco",
-                                      "America/Rosario",
-                                      "America/Santa_Isabel",
-                                      "America/Santarem",
-                                      "America/Santiago",
-                                      "America/Santo_Domingo",
-                                      "America/Sao_Paulo",
-                                      "America/Scoresbysund",
-                                      "America/Shiprock",
-                                      "America/Sitka",
-                                      "America/St_Barthelemy",
-                                      "America/St_Johns",
-                                      "America/St_Kitts",
-                                      "America/St_Lucia",
-                                      "America/St_Thomas",
-                                      "America/St_Vincent",
-                                      "America/Swift_Current",
-                                      "America/Tegucigalpa",
-                                      "America/Thule",
-                                      "America/Thunder_Bay",
-                                      "America/Tijuana",
-                                      "America/Toronto",
-                                      "America/Tortola",
-                                      "America/Vancouver",
-                                      "America/Virgin",
-                                      "America/Whitehorse",
-                                      "America/Winnipeg",
-                                      "America/Yakutat",
-                                      "America/Yellowknife",
-                                      "Antarctica/Casey",
-                                      "Antarctica/Davis",
-                                      "Antarctica/DumontDUrville",
-                                      "Antarctica/Macquarie",
-                                      "Antarctica/Mawson",
-                                      "Antarctica/McMurdo",
-                                      "Antarctica/Palmer",
-                                      "Antarctica/Rothera",
-                                      "Antarctica/South_Pole",
-                                      "Antarctica/Syowa",
-                                      "Antarctica/Troll",
-                                      "Antarctica/Vostok",
-                                      "Arctic/Longyearbyen",
-                                      "Asia/Aden",
-                                      "Asia/Almaty",
-                                      "Asia/Amman",
-                                      "Asia/Anadyr",
-                                      "Asia/Aqtau",
-                                      "Asia/Aqtobe",
-                                      "Asia/Ashgabat",
-                                      "Asia/Ashkhabad",
-                                      "Asia/Atyrau",
-                                      "Asia/Baghdad",
-                                      "Asia/Bahrain",
-                                      "Asia/Baku",
-                                      "Asia/Bangkok",
-                                      "Asia/Barnaul",
-                                      "Asia/Beirut",
-                                      "Asia/Bishkek",
-                                      "Asia/Brunei",
-                                      "Asia/Calcutta",
-                                      "Asia/Chita",
-                                      "Asia/Choibalsan",
-                                      "Asia/Chongqing",
-                                      "Asia/Chungking",
-                                      "Asia/Colombo",
-                                      "Asia/Dacca",
-                                      "Asia/Damascus",
-                                      "Asia/Dhaka",
-                                      "Asia/Dili",
-                                      "Asia/Dubai",
-                                      "Asia/Dushanbe",
-                                      "Asia/Famagusta",
-                                      "Asia/Gaza",
-                                      "Asia/Harbin",
-                                      "Asia/Hebron",
-                                      "Asia/Ho_Chi_Minh",
-                                      "Asia/Hong_Kong",
-                                      "Asia/Hovd",
-                                      "Asia/Irkutsk",
-                                      "Asia/Istanbul",
-                                      "Asia/Jakarta",
-                                      "Asia/Jayapura",
-                                      "Asia/Jerusalem",
-                                      "Asia/Kabul",
-                                      "Asia/Kamchatka",
-                                      "Asia/Karachi",
-                                      "Asia/Kashgar",
-                                      "Asia/Kathmandu",
-                                      "Asia/Katmandu",
-                                      "Asia/Khandyga",
-                                      "Asia/Kolkata",
-                                      "Asia/Krasnoyarsk",
-                                      "Asia/Kuala_Lumpur",
-                                      "Asia/Kuching",
-                                      "Asia/Kuwait",
-                                      "Asia/Macao",
-                                      "Asia/Macau",
-                                      "Asia/Magadan",
-                                      "Asia/Makassar",
-                                      "Asia/Manila",
-                                      "Asia/Muscat",
-                                      "Asia/Nicosia",
-                                      "Asia/Novokuznetsk",
-                                      "Asia/Novosibirsk",
-                                      "Asia/Omsk",
-                                      "Asia/Oral",
-                                      "Asia/Phnom_Penh",
-                                      "Asia/Pontianak",
-                                      "Asia/Pyongyang",
-                                      "Asia/Qatar",
-                                      "Asia/Qostanay",
-                                      "Asia/Qyzylorda",
-                                      "Asia/Rangoon",
-                                      "Asia/Riyadh",
-                                      "Asia/Saigon",
-                                      "Asia/Sakhalin",
-                                      "Asia/Samarkand",
-                                      "Asia/Seoul",
-                                      "Asia/Shanghai",
-                                      "Asia/Singapore",
-                                      "Asia/Srednekolymsk",
-                                      "Asia/Taipei",
-                                      "Asia/Tashkent",
-                                      "Asia/Tbilisi",
-                                      "Asia/Tehran",
-                                      "Asia/Tel_Aviv",
-                                      "Asia/Thimbu",
-                                      "Asia/Thimphu",
-                                      "Asia/Tokyo",
-                                      "Asia/Tomsk",
-                                      "Asia/Ujung_Pandang",
-                                      "Asia/Ulaanbaatar",
-                                      "Asia/Ulan_Bator",
-                                      "Asia/Urumqi",
-                                      "Asia/Ust-Nera",
-                                      "Asia/Vientiane",
-                                      "Asia/Vladivostok",
-                                      "Asia/Yakutsk",
-                                      "Asia/Yangon",
-                                      "Asia/Yekaterinburg",
-                                      "Asia/Yerevan",
-                                      "Atlantic/Azores",
-                                      "Atlantic/Bermuda",
-                                      "Atlantic/Canary",
-                                      "Atlantic/Cape_Verde",
-                                      "Atlantic/Faeroe",
-                                      "Atlantic/Faroe",
-                                      "Atlantic/Jan_Mayen",
-                                      "Atlantic/Madeira",
-                                      "Atlantic/Reykjavik",
-                                      "Atlantic/South_Georgia",
-                                      "Atlantic/St_Helena",
-                                      "Atlantic/Stanley",
-                                      "Australia/ACT",
-                                      "Australia/Adelaide",
-                                      "Australia/Brisbane",
-                                      "Australia/Broken_Hill",
-                                      "Australia/Canberra",
-                                      "Australia/Currie",
-                                      "Australia/Darwin",
-                                      "Australia/Eucla",
-                                      "Australia/Hobart",
-                                      "Australia/LHI",
-                                      "Australia/Lindeman",
-                                      "Australia/Lord_Howe",
-                                      "Australia/Melbourne",
-                                      "Australia/NSW",
-                                      "Australia/North",
-                                      "Australia/Perth",
-                                      "Australia/Queensland",
-                                      "Australia/South",
-                                      "Australia/Sydney",
-                                      "Australia/Tasmania",
-                                      "Australia/Victoria",
-                                      "Australia/West",
-                                      "Australia/Yancowinna",
-                                      "Brazil/Acre",
-                                      "Brazil/DeNoronha",
-                                      "Brazil/East",
-                                      "Brazil/West",
-                                      "CET",
-                                      "CST6CDT",
-                                      "Canada/Atlantic",
-                                      "Canada/Central",
-                                      "Canada/Eastern",
-                                      "Canada/Mountain",
-                                      "Canada/Newfoundland",
-                                      "Canada/Pacific",
-                                      "Canada/Saskatchewan",
-                                      "Canada/Yukon",
-                                      "Chile/Continental",
-                                      "Chile/EasterIsland",
-                                      "Cuba",
-                                      "EET",
-                                      "EST",
-                                      "EST5EDT",
-                                      "Egypt",
-                                      "Eire",
-                                      "Etc/GMT",
-                                      "Etc/GMT+0",
-                                      "Etc/GMT+1",
-                                      "Etc/GMT+10",
-                                      "Etc/GMT+11",
-                                      "Etc/GMT+12",
-                                      "Etc/GMT+2",
-                                      "Etc/GMT+3",
-                                      "Etc/GMT+4",
-                                      "Etc/GMT+5",
-                                      "Etc/GMT+6",
-                                      "Etc/GMT+7",
-                                      "Etc/GMT+8",
-                                      "Etc/GMT+9",
-                                      "Etc/GMT-0",
-                                      "Etc/GMT-1",
-                                      "Etc/GMT-10",
-                                      "Etc/GMT-11",
-                                      "Etc/GMT-12",
-                                      "Etc/GMT-13",
-                                      "Etc/GMT-14",
-                                      "Etc/GMT-2",
-                                      "Etc/GMT-3",
-                                      "Etc/GMT-4",
-                                      "Etc/GMT-5",
-                                      "Etc/GMT-6",
-                                      "Etc/GMT-7",
-                                      "Etc/GMT-8",
-                                      "Etc/GMT-9",
-                                      "Etc/GMT0",
-                                      "Etc/Greenwich",
-                                      "Etc/UCT",
-                                      "Etc/UTC",
-                                      "Etc/Universal",
-                                      "Etc/Zulu",
-                                      "Europe/Amsterdam",
-                                      "Europe/Andorra",
-                                      "Europe/Astrakhan",
-                                      "Europe/Athens",
-                                      "Europe/Belfast",
-                                      "Europe/Belgrade",
-                                      "Europe/Berlin",
-                                      "Europe/Bratislava",
-                                      "Europe/Brussels",
-                                      "Europe/Bucharest",
-                                      "Europe/Budapest",
-                                      "Europe/Busingen",
-                                      "Europe/Chisinau",
-                                      "Europe/Copenhagen",
-                                      "Europe/Dublin",
-                                      "Europe/Gibraltar",
-                                      "Europe/Guernsey",
-                                      "Europe/Helsinki",
-                                      "Europe/Isle_of_Man",
-                                      "Europe/Istanbul",
-                                      "Europe/Jersey",
-                                      "Europe/Kaliningrad",
-                                      "Europe/Kiev",
-                                      "Europe/Kirov",
-                                      "Europe/Lisbon",
-                                      "Europe/Ljubljana",
-                                      "Europe/London",
-                                      "Europe/Luxembourg",
-                                      "Europe/Madrid",
-                                      "Europe/Malta",
-                                      "Europe/Mariehamn",
-                                      "Europe/Minsk",
-                                      "Europe/Monaco",
-                                      "Europe/Moscow",
-                                      "Europe/Nicosia",
-                                      "Europe/Oslo",
-                                      "Europe/Paris",
-                                      "Europe/Podgorica",
-                                      "Europe/Prague",
-                                      "Europe/Riga",
-                                      "Europe/Rome",
-                                      "Europe/Samara",
-                                      "Europe/San_Marino",
-                                      "Europe/Sarajevo",
-                                      "Europe/Saratov",
-                                      "Europe/Simferopol",
-                                      "Europe/Skopje",
-                                      "Europe/Sofia",
-                                      "Europe/Stockholm",
-                                      "Europe/Tallinn",
-                                      "Europe/Tirane",
-                                      "Europe/Tiraspol",
-                                      "Europe/Ulyanovsk",
-                                      "Europe/Uzhgorod",
-                                      "Europe/Vaduz",
-                                      "Europe/Vatican",
-                                      "Europe/Vienna",
-                                      "Europe/Vilnius",
-                                      "Europe/Volgograd",
-                                      "Europe/Warsaw",
-                                      "Europe/Zagreb",
-                                      "Europe/Zaporozhye",
-                                      "Europe/Zurich",
-                                      "GB",
-                                      "GB-Eire",
-                                      "GMT",
-                                      "GMT+0",
-                                      "GMT-0",
-                                      "GMT0",
-                                      "Greenwich",
-                                      "HST",
-                                      "Hongkong",
-                                      "Iceland",
-                                      "Indian/Antananarivo",
-                                      "Indian/Chagos",
-                                      "Indian/Christmas",
-                                      "Indian/Cocos",
-                                      "Indian/Comoro",
-                                      "Indian/Kerguelen",
-                                      "Indian/Mahe",
-                                      "Indian/Maldives",
-                                      "Indian/Mauritius",
-                                      "Indian/Mayotte",
-                                      "Indian/Reunion",
-                                      "Iran",
-                                      "Israel",
-                                      "Jamaica",
-                                      "Japan",
-                                      "Kwajalein",
-                                      "Libya",
-                                      "MET",
-                                      "MST",
-                                      "MST7MDT",
-                                      "Mexico/BajaNorte",
-                                      "Mexico/BajaSur",
-                                      "Mexico/General",
-                                      "NZ",
-                                      "NZ-CHAT",
-                                      "Navajo",
-                                      "PRC",
-                                      "PST8PDT",
-                                      "Pacific/Apia",
-                                      "Pacific/Auckland",
-                                      "Pacific/Bougainville",
-                                      "Pacific/Chatham",
-                                      "Pacific/Chuuk",
-                                      "Pacific/Easter",
-                                      "Pacific/Efate",
-                                      "Pacific/Enderbury",
-                                      "Pacific/Fakaofo",
-                                      "Pacific/Fiji",
-                                      "Pacific/Funafuti",
-                                      "Pacific/Galapagos",
-                                      "Pacific/Gambier",
-                                      "Pacific/Guadalcanal",
-                                      "Pacific/Guam",
-                                      "Pacific/Honolulu",
-                                      "Pacific/Johnston",
-                                      "Pacific/Kiritimati",
-                                      "Pacific/Kosrae",
-                                      "Pacific/Kwajalein",
-                                      "Pacific/Majuro",
-                                      "Pacific/Marquesas",
-                                      "Pacific/Midway",
-                                      "Pacific/Nauru",
-                                      "Pacific/Niue",
-                                      "Pacific/Norfolk",
-                                      "Pacific/Noumea",
-                                      "Pacific/Pago_Pago",
-                                      "Pacific/Palau",
-                                      "Pacific/Pitcairn",
-                                      "Pacific/Pohnpei",
-                                      "Pacific/Ponape",
-                                      "Pacific/Port_Moresby",
-                                      "Pacific/Rarotonga",
-                                      "Pacific/Saipan",
-                                      "Pacific/Samoa",
-                                      "Pacific/Tahiti",
-                                      "Pacific/Tarawa",
-                                      "Pacific/Tongatapu",
-                                      "Pacific/Truk",
-                                      "Pacific/Wake",
-                                      "Pacific/Wallis",
-                                      "Pacific/Yap",
-                                      "Poland",
-                                      "Portugal",
-                                      "ROC",
-                                      "ROK",
-                                      "Singapore",
-                                      "Turkey",
-                                      "UCT",
-                                      "US/Alaska",
-                                      "US/Aleutian",
-                                      "US/Arizona",
-                                      "US/Central",
-                                      "US/East-Indiana",
-                                      "US/Eastern",
-                                      "US/Hawaii",
-                                      "US/Indiana-Starke",
-                                      "US/Michigan",
-                                      "US/Mountain",
-                                      "US/Pacific",
-                                      "US/Samoa",
-                                      "UTC",
-                                      "Universal",
-                                      "W-SU",
-                                      "WET",
-                                      "Zulu",
-                                      nullptr};
-
-// Helper to return a loaded time zone by value (UTC on error).
-time_zone LoadZone(const std::string& name) {
-  time_zone tz;
-  load_time_zone(name, &tz);
-  return tz;
-}
-
-// This helper is a macro so that failed expectations show up with the
-// correct line numbers.
-#define ExpectTime(tp, tz, y, m, d, hh, mm, ss, off, isdst, zone) \
-  do {                                                            \
-    time_zone::absolute_lookup al = tz.lookup(tp);                \
-    EXPECT_EQ(y, al.cs.year());                                   \
-    EXPECT_EQ(m, al.cs.month());                                  \
-    EXPECT_EQ(d, al.cs.day());                                    \
-    EXPECT_EQ(hh, al.cs.hour());                                  \
-    EXPECT_EQ(mm, al.cs.minute());                                \
-    EXPECT_EQ(ss, al.cs.second());                                \
-    EXPECT_EQ(off, al.offset);                                    \
-    EXPECT_TRUE(isdst == al.is_dst);                              \
-    /* EXPECT_STREQ(zone, al.abbr); */                            \
-  } while (0)
-
-// These tests sometimes run on platforms that have zoneinfo data so old
-// that the transition we are attempting to check does not exist, most
-// notably Android emulators.  Fortunately, AndroidZoneInfoSource supports
-// time_zone::version() so, in cases where we've learned that it matters,
-// we can make the check conditionally.
-int VersionCmp(time_zone tz, const std::string& target) {
-  std::string version = tz.version();
-  if (version.empty() && !target.empty()) return 1;  // unknown > known
-  return version.compare(target);
-}
-
-}  // namespace
-
-#if !defined(__EMSCRIPTEN__)
-TEST(TimeZones, LoadZonesConcurrently) {
-  std::promise<void> ready_promise;
-  std::shared_future<void> ready_future(ready_promise.get_future());
-  auto load_zones = [ready_future](std::promise<void>* started,
-                                   std::set<std::string>* failures) {
-    started->set_value();
-    ready_future.wait();
-    for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) {
-      std::string zone = *np;
-      time_zone tz;
-      if (load_time_zone(zone, &tz)) {
-        EXPECT_EQ(zone, tz.name());
-      } else {
-        failures->insert(zone);
-      }
-    }
-  };
-
-  const std::size_t n_threads = 128;
-  std::vector<std::thread> threads;
-  std::vector<std::set<std::string>> thread_failures(n_threads);
-  for (std::size_t i = 0; i != n_threads; ++i) {
-    std::promise<void> started;
-    threads.emplace_back(load_zones, &started, &thread_failures[i]);
-    started.get_future().wait();
-  }
-  ready_promise.set_value();
-  for (auto& thread : threads) {
-    thread.join();
-  }
-
-  // Allow a small number of failures to account for skew between
-  // the contents of kTimeZoneNames and the zoneinfo data source.
-#if defined(__ANDROID__)
-  // Cater to the possibility of using an even older zoneinfo data
-  // source when running on Android, where it is difficult to override
-  // the bionic tzdata provided by the test environment.
-  const std::size_t max_failures = 20;
-#else
-  const std::size_t max_failures = 3;
-#endif
-  std::set<std::string> failures;
-  for (const auto& thread_failure : thread_failures) {
-    failures.insert(thread_failure.begin(), thread_failure.end());
-  }
-  EXPECT_LE(failures.size(), max_failures) << testing::PrintToString(failures);
-}
-#endif
-
-TEST(TimeZone, NamedTimeZones) {
-  const time_zone utc = utc_time_zone();
-  EXPECT_EQ("UTC", utc.name());
-  const time_zone nyc = LoadZone("America/New_York");
-  EXPECT_EQ("America/New_York", nyc.name());
-  const time_zone syd = LoadZone("Australia/Sydney");
-  EXPECT_EQ("Australia/Sydney", syd.name());
-  const time_zone fixed0 =
-      fixed_time_zone(absl::time_internal::cctz::seconds::zero());
-  EXPECT_EQ("UTC", fixed0.name());
-  const time_zone fixed_pos = fixed_time_zone(
-      chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45));
-  EXPECT_EQ("Fixed/UTC+03:25:45", fixed_pos.name());
-  const time_zone fixed_neg = fixed_time_zone(
-      -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56)));
-  EXPECT_EQ("Fixed/UTC-12:34:56", fixed_neg.name());
-}
-
-TEST(TimeZone, Failures) {
-  time_zone tz;
-  EXPECT_FALSE(load_time_zone(":America/Los_Angeles", &tz));
-
-  tz = LoadZone("America/Los_Angeles");
-  EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz));
-  EXPECT_EQ(chrono::system_clock::from_time_t(0),
-            convert(civil_second(1970, 1, 1, 0, 0, 0), tz));  // UTC
-
-  // Ensures that the load still fails on a subsequent attempt.
-  tz = LoadZone("America/Los_Angeles");
-  EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz));
-  EXPECT_EQ(chrono::system_clock::from_time_t(0),
-            convert(civil_second(1970, 1, 1, 0, 0, 0), tz));  // UTC
-
-  // Loading an empty string timezone should fail.
-  tz = LoadZone("America/Los_Angeles");
-  EXPECT_FALSE(load_time_zone("", &tz));
-  EXPECT_EQ(chrono::system_clock::from_time_t(0),
-            convert(civil_second(1970, 1, 1, 0, 0, 0), tz));  // UTC
-}
-
-TEST(TimeZone, Equality) {
-  const time_zone a;
-  const time_zone b;
-  EXPECT_EQ(a, b);
-  EXPECT_EQ(a.name(), b.name());
-
-  const time_zone implicit_utc;
-  const time_zone explicit_utc = utc_time_zone();
-  EXPECT_EQ(implicit_utc, explicit_utc);
-  EXPECT_EQ(implicit_utc.name(), explicit_utc.name());
-
-  const time_zone fixed_zero =
-      fixed_time_zone(absl::time_internal::cctz::seconds::zero());
-  EXPECT_EQ(fixed_zero, LoadZone(fixed_zero.name()));
-  EXPECT_EQ(fixed_zero, explicit_utc);
-
-  const time_zone fixed_utc = LoadZone("Fixed/UTC+00:00:00");
-  EXPECT_EQ(fixed_utc, LoadZone(fixed_utc.name()));
-  EXPECT_EQ(fixed_utc, explicit_utc);
-
-  const time_zone fixed_pos = fixed_time_zone(
-      chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45));
-  EXPECT_EQ(fixed_pos, LoadZone(fixed_pos.name()));
-  EXPECT_NE(fixed_pos, explicit_utc);
-  const time_zone fixed_neg = fixed_time_zone(
-      -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56)));
-  EXPECT_EQ(fixed_neg, LoadZone(fixed_neg.name()));
-  EXPECT_NE(fixed_neg, explicit_utc);
-
-  const time_zone fixed_lim = fixed_time_zone(chrono::hours(24));
-  EXPECT_EQ(fixed_lim, LoadZone(fixed_lim.name()));
-  EXPECT_NE(fixed_lim, explicit_utc);
-  const time_zone fixed_ovfl =
-      fixed_time_zone(chrono::hours(24) + chrono::seconds(1));
-  EXPECT_EQ(fixed_ovfl, LoadZone(fixed_ovfl.name()));
-  EXPECT_EQ(fixed_ovfl, explicit_utc);
-
-  EXPECT_EQ(fixed_time_zone(chrono::seconds(1)),
-            fixed_time_zone(chrono::seconds(1)));
-
-  const time_zone local = local_time_zone();
-  EXPECT_EQ(local, LoadZone(local.name()));
-
-  time_zone la = LoadZone("America/Los_Angeles");
-  time_zone nyc = LoadZone("America/New_York");
-  EXPECT_NE(la, nyc);
-}
-
-TEST(StdChronoTimePoint, TimeTAlignment) {
-  // Ensures that the Unix epoch and the system clock epoch are an integral
-  // number of seconds apart. This simplifies conversions to/from time_t.
-  auto diff =
-      chrono::system_clock::time_point() - chrono::system_clock::from_time_t(0);
-  EXPECT_EQ(chrono::system_clock::time_point::duration::zero(),
-            diff % chrono::seconds(1));
-}
-
-TEST(BreakTime, TimePointResolution) {
-  const time_zone utc = utc_time_zone();
-  const auto t0 = chrono::system_clock::from_time_t(0);
-
-  ExpectTime(chrono::time_point_cast<chrono::nanoseconds>(t0), utc, 1970, 1, 1,
-             0, 0, 0, 0, false, "UTC");
-  ExpectTime(chrono::time_point_cast<chrono::microseconds>(t0), utc, 1970, 1, 1,
-             0, 0, 0, 0, false, "UTC");
-  ExpectTime(chrono::time_point_cast<chrono::milliseconds>(t0), utc, 1970, 1, 1,
-             0, 0, 0, 0, false, "UTC");
-  ExpectTime(chrono::time_point_cast<chrono::seconds>(t0), utc, 1970, 1, 1, 0,
-             0, 0, 0, false, "UTC");
-  ExpectTime(chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0),
-             utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
-  ExpectTime(chrono::time_point_cast<chrono::minutes>(t0), utc, 1970, 1, 1, 0,
-             0, 0, 0, false, "UTC");
-  ExpectTime(chrono::time_point_cast<chrono::hours>(t0), utc, 1970, 1, 1, 0, 0,
-             0, 0, false, "UTC");
-}
-
-TEST(BreakTime, LocalTimeInUTC) {
-  const time_zone tz = utc_time_zone();
-  const auto tp = chrono::system_clock::from_time_t(0);
-  ExpectTime(tp, tz, 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
-  EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz)));
-}
-
-TEST(BreakTime, LocalTimeInUTCUnaligned) {
-  const time_zone tz = utc_time_zone();
-  const auto tp =
-      chrono::system_clock::from_time_t(0) - chrono::milliseconds(500);
-  ExpectTime(tp, tz, 1969, 12, 31, 23, 59, 59, 0, false, "UTC");
-  EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz)));
-}
-
-TEST(BreakTime, LocalTimePosix) {
-  // See IEEE Std 1003.1-1988 B.2.3 General Terms, Epoch.
-  const time_zone tz = utc_time_zone();
-  const auto tp = chrono::system_clock::from_time_t(536457599);
-  ExpectTime(tp, tz, 1986, 12, 31, 23, 59, 59, 0, false, "UTC");
-  EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz)));
-}
-
-TEST(TimeZoneImpl, LocalTimeInFixed) {
-  const absl::time_internal::cctz::seconds offset =
-      -(chrono::hours(8) + chrono::minutes(33) + chrono::seconds(47));
-  const time_zone tz = fixed_time_zone(offset);
-  const auto tp = chrono::system_clock::from_time_t(0);
-  ExpectTime(tp, tz, 1969, 12, 31, 15, 26, 13, offset.count(), false,
-             "-083347");
-  EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz)));
-}
-
-TEST(BreakTime, LocalTimeInNewYork) {
-  const time_zone tz = LoadZone("America/New_York");
-  const auto tp = chrono::system_clock::from_time_t(45);
-  ExpectTime(tp, tz, 1969, 12, 31, 19, 0, 45, -5 * 60 * 60, false, "EST");
-  EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz)));
-}
-
-TEST(BreakTime, LocalTimeInMTV) {
-  const time_zone tz = LoadZone("America/Los_Angeles");
-  const auto tp = chrono::system_clock::from_time_t(1380855729);
-  ExpectTime(tp, tz, 2013, 10, 3, 20, 2, 9, -7 * 60 * 60, true, "PDT");
-  EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz)));
-}
-
-TEST(BreakTime, LocalTimeInSydney) {
-  const time_zone tz = LoadZone("Australia/Sydney");
-  const auto tp = chrono::system_clock::from_time_t(90);
-  ExpectTime(tp, tz, 1970, 1, 1, 10, 1, 30, 10 * 60 * 60, false, "AEST");
-  EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz)));
-}
-
-TEST(MakeTime, TimePointResolution) {
-  const time_zone utc = utc_time_zone();
-  const time_point<chrono::nanoseconds> tp_ns =
-      convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
-  EXPECT_EQ("04:05", format("%M:%E*S", tp_ns, utc));
-  const time_point<chrono::microseconds> tp_us =
-      convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
-  EXPECT_EQ("04:05", format("%M:%E*S", tp_us, utc));
-  const time_point<chrono::milliseconds> tp_ms =
-      convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
-  EXPECT_EQ("04:05", format("%M:%E*S", tp_ms, utc));
-  const time_point<chrono::seconds> tp_s =
-      convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
-  EXPECT_EQ("04:05", format("%M:%E*S", tp_s, utc));
-  const time_point<absl::time_internal::cctz::seconds> tp_s64 =
-      convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
-  EXPECT_EQ("04:05", format("%M:%E*S", tp_s64, utc));
-
-  // These next two require chrono::time_point_cast because the conversion
-  // from a resolution of seconds (the return value of convert()) to a
-  // coarser resolution requires an explicit cast.
-  const time_point<chrono::minutes> tp_m =
-      chrono::time_point_cast<chrono::minutes>(
-          convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
-  EXPECT_EQ("04:00", format("%M:%E*S", tp_m, utc));
-  const time_point<chrono::hours> tp_h = chrono::time_point_cast<chrono::hours>(
-      convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
-  EXPECT_EQ("00:00", format("%M:%E*S", tp_h, utc));
-}
-
-TEST(MakeTime, Normalization) {
-  const time_zone tz = LoadZone("America/New_York");
-  const auto tp = convert(civil_second(2009, 2, 13, 18, 31, 30), tz);
-  EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp);
-
-  // Now requests for the same time_point but with out-of-range fields.
-  EXPECT_EQ(tp, convert(civil_second(2008, 14, 13, 18, 31, 30), tz));  // month
-  EXPECT_EQ(tp, convert(civil_second(2009, 1, 44, 18, 31, 30), tz));   // day
-  EXPECT_EQ(tp, convert(civil_second(2009, 2, 12, 42, 31, 30), tz));   // hour
-  EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 17, 91, 30), tz));   // minute
-  EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 18, 30, 90), tz));   // second
-}
-
-// NOTE: Run this with -ftrapv to detect overflow problems.
-TEST(MakeTime, SysSecondsLimits) {
-  const char RFC3339[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
-  const time_zone utc = utc_time_zone();
-  const time_zone east = fixed_time_zone(chrono::hours(14));
-  const time_zone west = fixed_time_zone(-chrono::hours(14));
-  time_point<absl::time_internal::cctz::seconds> tp;
-
-  // Approach the maximal time_point<cctz::seconds> value from below.
-  tp = convert(civil_second(292277026596, 12, 4, 15, 30, 6), utc);
-  EXPECT_EQ("292277026596-12-04T15:30:06+00:00", format(RFC3339, tp, utc));
-  tp = convert(civil_second(292277026596, 12, 4, 15, 30, 7), utc);
-  EXPECT_EQ("292277026596-12-04T15:30:07+00:00", format(RFC3339, tp, utc));
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
-  tp = convert(civil_second(292277026596, 12, 4, 15, 30, 8), utc);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
-  tp = convert(civil_second::max(), utc);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
-
-  // Checks that we can also get the maximal value for a far-east zone.
-  tp = convert(civil_second(292277026596, 12, 5, 5, 30, 7), east);
-  EXPECT_EQ("292277026596-12-05T05:30:07+14:00", format(RFC3339, tp, east));
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
-  tp = convert(civil_second(292277026596, 12, 5, 5, 30, 8), east);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
-  tp = convert(civil_second::max(), east);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
-
-  // Checks that we can also get the maximal value for a far-west zone.
-  tp = convert(civil_second(292277026596, 12, 4, 1, 30, 7), west);
-  EXPECT_EQ("292277026596-12-04T01:30:07-14:00", format(RFC3339, tp, west));
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
-  tp = convert(civil_second(292277026596, 12, 4, 7, 30, 8), west);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
-  tp = convert(civil_second::max(), west);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp);
-
-  // Approach the minimal time_point<cctz::seconds> value from above.
-  tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 53), utc);
-  EXPECT_EQ("-292277022657-01-27T08:29:53+00:00", format(RFC3339, tp, utc));
-  tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 52), utc);
-  EXPECT_EQ("-292277022657-01-27T08:29:52+00:00", format(RFC3339, tp, utc));
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
-  tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 51), utc);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
-  tp = convert(civil_second::min(), utc);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
-
-  // Checks that we can also get the minimal value for a far-east zone.
-  tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 52), east);
-  EXPECT_EQ("-292277022657-01-27T22:29:52+14:00", format(RFC3339, tp, east));
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
-  tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 51), east);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
-  tp = convert(civil_second::min(), east);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
-
-  // Checks that we can also get the minimal value for a far-west zone.
-  tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 52), west);
-  EXPECT_EQ("-292277022657-01-26T18:29:52-14:00", format(RFC3339, tp, west));
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
-  tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 51), west);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
-  tp = convert(civil_second::min(), west);
-  EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
-
-  // Some similar checks for the "libc" time-zone implementation.
-  if (sizeof(std::time_t) >= 8) {
-    // Checks that "tm_year + 1900", as used by the "libc" implementation,
-    // can produce year values beyond the range on an int without overflow.
-#if defined(_WIN32) || defined(_WIN64)
-    // localtime_s() and gmtime_s() don't believe in years outside [1970:3000].
-#else
-    const time_zone cut = LoadZone("libc:UTC");
-    const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900;
-    tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut);
-#if defined(__FreeBSD__) || defined(__OpenBSD__)
-    // The BSD gmtime_r() fails on extreme positive tm_year values.
-#else
-    EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, cut));
-#endif
-    const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
-    tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut);
-    EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
-#endif
-  }
-}
-
-TEST(MakeTime, LocalTimeLibC) {
-  // Checks that cctz and libc agree on transition points in [1970:2037].
-  //
-  // We limit this test case to environments where:
-  //  1) we know how to change the time zone used by localtime()/mktime(),
-  //  2) cctz and localtime()/mktime() will use similar-enough tzdata, and
-  //  3) we have some idea about how mktime() behaves during transitions.
-#if defined(__linux__) && !defined(__ANDROID__)
-  const char* const ep = getenv("TZ");
-  std::string tz_name = (ep != nullptr) ? ep : "";
-  for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) {
-    ASSERT_EQ(0, setenv("TZ", *np, 1));  // change what "localtime" means
-    const auto zi = local_time_zone();
-    const auto lc = LoadZone("libc:localtime");
-    time_zone::civil_transition transition;
-    for (auto tp = zi.lookup(civil_second()).trans;
-         zi.next_transition(tp, &transition);
-         tp = zi.lookup(transition.to).trans) {
-      const auto fcl = zi.lookup(transition.from);
-      const auto tcl = zi.lookup(transition.to);
-      civil_second cs;  // compare cs in zi and lc
-      if (fcl.kind == time_zone::civil_lookup::UNIQUE) {
-        if (tcl.kind == time_zone::civil_lookup::UNIQUE) {
-          // Both unique; must be an is_dst or abbr change.
-          ASSERT_EQ(transition.from, transition.to);
-          const auto trans = fcl.trans;
-          const auto tal = zi.lookup(trans);
-          const auto tprev = trans - absl::time_internal::cctz::seconds(1);
-          const auto pal = zi.lookup(tprev);
-          if (pal.is_dst == tal.is_dst) {
-            ASSERT_STRNE(pal.abbr, tal.abbr);
-          }
-          continue;
-        }
-        ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind);
-        cs = transition.to;
-      } else {
-        ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind);
-        ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind);
-        cs = transition.from;
-      }
-      if (cs.year() > 2037) break;  // limit test time (and to 32-bit time_t)
-      const auto cl_zi = zi.lookup(cs);
-      if (zi.lookup(cl_zi.pre).is_dst == zi.lookup(cl_zi.post).is_dst) {
-        // The "libc" implementation cannot correctly classify transitions
-        // that don't change the "tm_isdst" flag.  In Europe/Volgograd, for
-        // example, there is a SKIPPED transition from +03 to +04 with dst=F
-        // on both sides ...
-        //   1540681199 = 2018-10-28 01:59:59 +03:00:00 [dst=F off=10800]
-        //   1540681200 = 2018-10-28 03:00:00 +04:00:00 [dst=F off=14400]
-        // but std::mktime(2018-10-28 02:00:00, tm_isdst=0) fails, unlike,
-        // say, the similar Europe/Chisinau transition from +02 to +03 ...
-        //   1521935999 = 2018-03-25 01:59:59 +02:00:00 [dst=F off=7200]
-        //   1521936000 = 2018-03-25 03:00:00 +03:00:00 [dst=T off=10800]
-        // where std::mktime(2018-03-25 02:00:00, tm_isdst=0) succeeds and
-        // returns 1521936000.
-        continue;
-      }
-      if (cs == civil_second(2037, 10, 4, 2, 0, 0)) {
-        const std::string tzname = *np;
-        if (tzname == "Africa/Casablanca" || tzname == "Africa/El_Aaiun") {
-          // The "libc" implementation gets this transition wrong (at least
-          // until 2018g when it was removed), returning an offset of 3600
-          // instead of 0.  TODO: Revert this when 2018g is ubiquitous.
-          continue;
-        }
-      }
-      const auto cl_lc = lc.lookup(cs);
-      SCOPED_TRACE(testing::Message() << "For " << cs << " in " << *np);
-      EXPECT_EQ(cl_zi.kind, cl_lc.kind);
-      EXPECT_EQ(cl_zi.pre, cl_lc.pre);
-      EXPECT_EQ(cl_zi.trans, cl_lc.trans);
-      EXPECT_EQ(cl_zi.post, cl_lc.post);
-    }
-  }
-  if (ep == nullptr) {
-    ASSERT_EQ(0, unsetenv("TZ"));
-  } else {
-    ASSERT_EQ(0, setenv("TZ", tz_name.c_str(), 1));
-  }
-#endif
-}
-
-TEST(NextTransition, UTC) {
-  const auto tz = utc_time_zone();
-  time_zone::civil_transition trans;
-
-  auto tp = time_point<absl::time_internal::cctz::seconds>::min();
-  EXPECT_FALSE(tz.next_transition(tp, &trans));
-
-  tp = time_point<absl::time_internal::cctz::seconds>::max();
-  EXPECT_FALSE(tz.next_transition(tp, &trans));
-}
-
-TEST(PrevTransition, UTC) {
-  const auto tz = utc_time_zone();
-  time_zone::civil_transition trans;
-
-  auto tp = time_point<absl::time_internal::cctz::seconds>::max();
-  EXPECT_FALSE(tz.prev_transition(tp, &trans));
-
-  tp = time_point<absl::time_internal::cctz::seconds>::min();
-  EXPECT_FALSE(tz.prev_transition(tp, &trans));
-}
-
-TEST(NextTransition, AmericaNewYork) {
-  const auto tz = LoadZone("America/New_York");
-  time_zone::civil_transition trans;
-
-  auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz);
-  EXPECT_TRUE(tz.next_transition(tp, &trans));
-  EXPECT_EQ(civil_second(2018, 11, 4, 2, 0, 0), trans.from);
-  EXPECT_EQ(civil_second(2018, 11, 4, 1, 0, 0), trans.to);
-
-  tp = time_point<absl::time_internal::cctz::seconds>::max();
-  EXPECT_FALSE(tz.next_transition(tp, &trans));
-
-  tp = time_point<absl::time_internal::cctz::seconds>::min();
-  EXPECT_TRUE(tz.next_transition(tp, &trans));
-  if (trans.from == civil_second(1918, 3, 31, 2, 0, 0)) {
-    // It looks like the tzdata is only 32 bit (probably macOS),
-    // which bottoms out at 1901-12-13T20:45:52+00:00.
-    EXPECT_EQ(civil_second(1918, 3, 31, 3, 0, 0), trans.to);
-  } else {
-    EXPECT_EQ(civil_second(1883, 11, 18, 12, 3, 58), trans.from);
-    EXPECT_EQ(civil_second(1883, 11, 18, 12, 0, 0), trans.to);
-  }
-}
-
-TEST(PrevTransition, AmericaNewYork) {
-  const auto tz = LoadZone("America/New_York");
-  time_zone::civil_transition trans;
-
-  auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz);
-  EXPECT_TRUE(tz.prev_transition(tp, &trans));
-  EXPECT_EQ(civil_second(2018, 3, 11, 2, 0, 0), trans.from);
-  EXPECT_EQ(civil_second(2018, 3, 11, 3, 0, 0), trans.to);
-
-  tp = time_point<absl::time_internal::cctz::seconds>::min();
-  EXPECT_FALSE(tz.prev_transition(tp, &trans));
-
-  tp = time_point<absl::time_internal::cctz::seconds>::max();
-  EXPECT_TRUE(tz.prev_transition(tp, &trans));
-  // We have a transition but we don't know which one.
-}
-
-TEST(TimeZoneEdgeCase, AmericaNewYork) {
-  const time_zone tz = LoadZone("America/New_York");
-
-  // Spring 1:59:59 -> 3:00:00
-  auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
-  ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -5 * 3600, false, "EST");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -4 * 3600, true, "EDT");
-
-  // Fall 1:59:59 -> 1:00:00
-  tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
-  ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -4 * 3600, true, "EDT");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -5 * 3600, false, "EST");
-}
-
-TEST(TimeZoneEdgeCase, AmericaLosAngeles) {
-  const time_zone tz = LoadZone("America/Los_Angeles");
-
-  // Spring 1:59:59 -> 3:00:00
-  auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
-  ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -8 * 3600, false, "PST");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -7 * 3600, true, "PDT");
-
-  // Fall 1:59:59 -> 1:00:00
-  tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
-  ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, true, "PDT");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -8 * 3600, false, "PST");
-}
-
-TEST(TimeZoneEdgeCase, ArizonaNoTransition) {
-  const time_zone tz = LoadZone("America/Phoenix");
-
-  // No transition in Spring.
-  auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
-  ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -7 * 3600, false, "MST");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 3, 10, 2, 0, 0, -7 * 3600, false, "MST");
-
-  // No transition in Fall.
-  tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
-  ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, false, "MST");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 11, 3, 2, 0, 0, -7 * 3600, false, "MST");
-}
-
-TEST(TimeZoneEdgeCase, AsiaKathmandu) {
-  const time_zone tz = LoadZone("Asia/Kathmandu");
-
-  // A non-DST offset change from +0530 to +0545
-  //
-  //   504901799 == Tue, 31 Dec 1985 23:59:59 +0530 (+0530)
-  //   504901800 == Wed,  1 Jan 1986 00:15:00 +0545 (+0545)
-  auto tp = convert(civil_second(1985, 12, 31, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 1985, 12, 31, 23, 59, 59, 5.5 * 3600, false, "+0530");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 1986, 1, 1, 0, 15, 0, 5.75 * 3600, false, "+0545");
-}
-
-TEST(TimeZoneEdgeCase, PacificChatham) {
-  const time_zone tz = LoadZone("Pacific/Chatham");
-
-  // One-hour DST offset changes, but at atypical values
-  //
-  //   1365256799 == Sun,  7 Apr 2013 03:44:59 +1345 (+1345)
-  //   1365256800 == Sun,  7 Apr 2013 02:45:00 +1245 (+1245)
-  auto tp = convert(civil_second(2013, 4, 7, 3, 44, 59), tz);
-  ExpectTime(tp, tz, 2013, 4, 7, 3, 44, 59, 13.75 * 3600, true, "+1345");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 4, 7, 2, 45, 0, 12.75 * 3600, false, "+1245");
-
-  //   1380376799 == Sun, 29 Sep 2013 02:44:59 +1245 (+1245)
-  //   1380376800 == Sun, 29 Sep 2013 03:45:00 +1345 (+1345)
-  tp = convert(civil_second(2013, 9, 29, 2, 44, 59), tz);
-  ExpectTime(tp, tz, 2013, 9, 29, 2, 44, 59, 12.75 * 3600, false, "+1245");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 9, 29, 3, 45, 0, 13.75 * 3600, true, "+1345");
-}
-
-TEST(TimeZoneEdgeCase, AustraliaLordHowe) {
-  const time_zone tz = LoadZone("Australia/Lord_Howe");
-
-  // Half-hour DST offset changes
-  //
-  //   1365260399 == Sun,  7 Apr 2013 01:59:59 +1100 (+11)
-  //   1365260400 == Sun,  7 Apr 2013 01:30:00 +1030 (+1030)
-  auto tp = convert(civil_second(2013, 4, 7, 1, 59, 59), tz);
-  ExpectTime(tp, tz, 2013, 4, 7, 1, 59, 59, 11 * 3600, true, "+11");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 4, 7, 1, 30, 0, 10.5 * 3600, false, "+1030");
-
-  //   1380986999 == Sun,  6 Oct 2013 01:59:59 +1030 (+1030)
-  //   1380987000 == Sun,  6 Oct 2013 02:30:00 +1100 (+11)
-  tp = convert(civil_second(2013, 10, 6, 1, 59, 59), tz);
-  ExpectTime(tp, tz, 2013, 10, 6, 1, 59, 59, 10.5 * 3600, false, "+1030");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2013, 10, 6, 2, 30, 0, 11 * 3600, true, "+11");
-}
-
-TEST(TimeZoneEdgeCase, PacificApia) {
-  const time_zone tz = LoadZone("Pacific/Apia");
-
-  // At the end of December 2011, Samoa jumped forward by one day,
-  // skipping 30 December from the local calendar, when the nation
-  // moved to the west of the International Date Line.
-  //
-  // A one-day, non-DST offset change
-  //
-  //   1325239199 == Thu, 29 Dec 2011 23:59:59 -1000 (-10)
-  //   1325239200 == Sat, 31 Dec 2011 00:00:00 +1400 (+14)
-  auto tp = convert(civil_second(2011, 12, 29, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 2011, 12, 29, 23, 59, 59, -10 * 3600, true, "-10");
-  EXPECT_EQ(363, get_yearday(convert(tp, tz)));
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2011, 12, 31, 0, 0, 0, 14 * 3600, true, "+14");
-  EXPECT_EQ(365, get_yearday(convert(tp, tz)));
-}
-
-TEST(TimeZoneEdgeCase, AfricaCairo) {
-  const time_zone tz = LoadZone("Africa/Cairo");
-
-  if (VersionCmp(tz, "2014c") >= 0) {
-    // An interesting case of midnight not existing.
-    //
-    //   1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
-    //   1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
-    auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz);
-    ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
-    tp += absl::time_internal::cctz::seconds(1);
-    ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
-  }
-}
-
-TEST(TimeZoneEdgeCase, AfricaMonrovia) {
-  const time_zone tz = LoadZone("Africa/Monrovia");
-
-  if (VersionCmp(tz, "2017b") >= 0) {
-    // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
-    //
-    //   63593069 == Thu,  6 Jan 1972 23:59:59 -0044 (MMT)
-    //   63593070 == Fri,  7 Jan 1972 00:44:30 +0000 (GMT)
-    auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
-    ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
-    tp += absl::time_internal::cctz::seconds(1);
-    ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
-  }
-}
-
-TEST(TimeZoneEdgeCase, AmericaJamaica) {
-  // Jamaica discontinued DST transitions in 1983, and is now at a
-  // constant -0500.  This makes it an interesting edge-case target.
-  // Note that the 32-bit times used in a (tzh_version == 0) zoneinfo
-  // file cannot represent the abbreviation-only transition of 1890,
-  // so we ignore the abbreviation by expecting what we received.
-  const time_zone tz = LoadZone("America/Jamaica");
-
-  // Before the first transition.
-  if (!tz.version().empty() && VersionCmp(tz, "2018d") >= 0) {
-    // We avoid the expectations on the -18430 offset below unless we are
-    // certain we have commit 907241e (Fix off-by-1 error for Jamaica and
-    // T&C before 1913) from 2018d.  TODO: Remove the "version() not empty"
-    // part when 2018d is generally available from /usr/share/zoneinfo.
-    auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz);
-    ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false,
-               tz.lookup(tp).abbr);
-
-    // Over the first (abbreviation-change only) transition.
-    //   -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
-    //   -2524503169 == Wed,  1 Jan 1890 00:00:00 -0507 (KMT)
-    tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz);
-    ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false,
-               tz.lookup(tp).abbr);
-    tp += absl::time_internal::cctz::seconds(1);
-    ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT");
-  }
-
-  // Over the last (DST) transition.
-  //     436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT)
-  //     436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST)
-  auto tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz);
-  ExpectTime(tp, tz, 1983, 10, 30, 1, 59, 59, -4 * 3600, true, "EDT");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST");
-
-  // After the last transition.
-  tp = convert(civil_second(1983, 12, 31, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 1983, 12, 31, 23, 59, 59, -5 * 3600, false, "EST");
-}
-
-TEST(TimeZoneEdgeCase, WET) {
-  // Cover some non-existent times within forward transitions.
-  const time_zone tz = LoadZone("WET");
-
-  // Before the first transition.
-  auto tp = convert(civil_second(1977, 1, 1, 0, 0, 0), tz);
-  ExpectTime(tp, tz, 1977, 1, 1, 0, 0, 0, 0, false, "WET");
-
-  // Over the first transition.
-  //     228877199 == Sun,  3 Apr 1977 00:59:59 +0000 (WET)
-  //     228877200 == Sun,  3 Apr 1977 02:00:00 +0100 (WEST)
-  tp = convert(civil_second(1977, 4, 3, 0, 59, 59), tz);
-  ExpectTime(tp, tz, 1977, 4, 3, 0, 59, 59, 0, false, "WET");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 1977, 4, 3, 2, 0, 0, 1 * 3600, true, "WEST");
-
-  // A non-existent time within the first transition.
-  time_zone::civil_lookup cl1 = tz.lookup(civil_second(1977, 4, 3, 1, 15, 0));
-  EXPECT_EQ(time_zone::civil_lookup::SKIPPED, cl1.kind);
-  ExpectTime(cl1.pre, tz, 1977, 4, 3, 2, 15, 0, 1 * 3600, true, "WEST");
-  ExpectTime(cl1.trans, tz, 1977, 4, 3, 2, 0, 0, 1 * 3600, true, "WEST");
-  ExpectTime(cl1.post, tz, 1977, 4, 3, 0, 15, 0, 0 * 3600, false, "WET");
-
-  // A non-existent time within the second forward transition.
-  time_zone::civil_lookup cl2 = tz.lookup(civil_second(1978, 4, 2, 1, 15, 0));
-  EXPECT_EQ(time_zone::civil_lookup::SKIPPED, cl2.kind);
-  ExpectTime(cl2.pre, tz, 1978, 4, 2, 2, 15, 0, 1 * 3600, true, "WEST");
-  ExpectTime(cl2.trans, tz, 1978, 4, 2, 2, 0, 0, 1 * 3600, true, "WEST");
-  ExpectTime(cl2.post, tz, 1978, 4, 2, 0, 15, 0, 0 * 3600, false, "WET");
-}
-
-TEST(TimeZoneEdgeCase, FixedOffsets) {
-  const time_zone gmtm5 = LoadZone("Etc/GMT+5");  // -0500
-  auto tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtm5);
-  ExpectTime(tp, gmtm5, 1970, 1, 1, 0, 0, 0, -5 * 3600, false, "-05");
-  EXPECT_EQ(chrono::system_clock::from_time_t(5 * 3600), tp);
-
-  const time_zone gmtp5 = LoadZone("Etc/GMT-5");  // +0500
-  tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtp5);
-  ExpectTime(tp, gmtp5, 1970, 1, 1, 0, 0, 0, 5 * 3600, false, "+05");
-  EXPECT_EQ(chrono::system_clock::from_time_t(-5 * 3600), tp);
-}
-
-TEST(TimeZoneEdgeCase, NegativeYear) {
-  // Tests transition from year 0 (aka 1BCE) to year -1.
-  const time_zone tz = utc_time_zone();
-  auto tp = convert(civil_second(0, 1, 1, 0, 0, 0), tz);
-  ExpectTime(tp, tz, 0, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC");
-  EXPECT_EQ(weekday::saturday, get_weekday(convert(tp, tz)));
-  tp -= absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, -1, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC");
-  EXPECT_EQ(weekday::friday, get_weekday(convert(tp, tz)));
-}
-
-TEST(TimeZoneEdgeCase, UTC32bitLimit) {
-  const time_zone tz = utc_time_zone();
-
-  // Limits of signed 32-bit time_t
-  //
-  //   2147483647 == Tue, 19 Jan 2038 03:14:07 +0000 (UTC)
-  //   2147483648 == Tue, 19 Jan 2038 03:14:08 +0000 (UTC)
-  auto tp = convert(civil_second(2038, 1, 19, 3, 14, 7), tz);
-  ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 7, 0 * 3600, false, "UTC");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 8, 0 * 3600, false, "UTC");
-}
-
-TEST(TimeZoneEdgeCase, UTC5DigitYear) {
-  const time_zone tz = utc_time_zone();
-
-  // Rollover to 5-digit year
-  //
-  //   253402300799 == Fri, 31 Dec 9999 23:59:59 +0000 (UTC)
-  //   253402300800 == Sat,  1 Jan 1000 00:00:00 +0000 (UTC)
-  auto tp = convert(civil_second(9999, 12, 31, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 9999, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 10000, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC");
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.cc
deleted file mode 100644
index 5cdd09e89d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include "time_zone_posix.h"
-
-#include <cstddef>
-#include <cstring>
-#include <limits>
-#include <string>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-namespace {
-
-const char kDigits[] = "0123456789";
-
-const char* ParseInt(const char* p, int min, int max, int* vp) {
-  int value = 0;
-  const char* op = p;
-  const int kMaxInt = std::numeric_limits<int>::max();
-  for (; const char* dp = strchr(kDigits, *p); ++p) {
-    int d = static_cast<int>(dp - kDigits);
-    if (d >= 10) break;  // '\0'
-    if (value > kMaxInt / 10) return nullptr;
-    value *= 10;
-    if (value > kMaxInt - d) return nullptr;
-    value += d;
-  }
-  if (p == op || value < min || value > max) return nullptr;
-  *vp = value;
-  return p;
-}
-
-// abbr = <.*?> | [^-+,\d]{3,}
-const char* ParseAbbr(const char* p, std::string* abbr) {
-  const char* op = p;
-  if (*p == '<') {  // special zoneinfo <...> form
-    while (*++p != '>') {
-      if (*p == '\0') return nullptr;
-    }
-    abbr->assign(op + 1, static_cast<std::size_t>(p - op) - 1);
-    return ++p;
-  }
-  while (*p != '\0') {
-    if (strchr("-+,", *p)) break;
-    if (strchr(kDigits, *p)) break;
-    ++p;
-  }
-  if (p - op < 3) return nullptr;
-  abbr->assign(op, static_cast<std::size_t>(p - op));
-  return p;
-}
-
-// offset = [+|-]hh[:mm[:ss]] (aggregated into single seconds value)
-const char* ParseOffset(const char* p, int min_hour, int max_hour, int sign,
-                        std::int_fast32_t* offset) {
-  if (p == nullptr) return nullptr;
-  if (*p == '+' || *p == '-') {
-    if (*p++ == '-') sign = -sign;
-  }
-  int hours = 0;
-  int minutes = 0;
-  int seconds = 0;
-
-  p = ParseInt(p, min_hour, max_hour, &hours);
-  if (p == nullptr) return nullptr;
-  if (*p == ':') {
-    p = ParseInt(p + 1, 0, 59, &minutes);
-    if (p == nullptr) return nullptr;
-    if (*p == ':') {
-      p = ParseInt(p + 1, 0, 59, &seconds);
-      if (p == nullptr) return nullptr;
-    }
-  }
-  *offset = sign * ((((hours * 60) + minutes) * 60) + seconds);
-  return p;
-}
-
-// datetime = ( Jn | n | Mm.w.d ) [ / offset ]
-const char* ParseDateTime(const char* p, PosixTransition* res) {
-  if (p != nullptr && *p == ',') {
-    if (*++p == 'M') {
-      int month = 0;
-      if ((p = ParseInt(p + 1, 1, 12, &month)) != nullptr && *p == '.') {
-        int week = 0;
-        if ((p = ParseInt(p + 1, 1, 5, &week)) != nullptr && *p == '.') {
-          int weekday = 0;
-          if ((p = ParseInt(p + 1, 0, 6, &weekday)) != nullptr) {
-            res->date.fmt = PosixTransition::M;
-            res->date.m.month = static_cast<std::int_fast8_t>(month);
-            res->date.m.week = static_cast<std::int_fast8_t>(week);
-            res->date.m.weekday = static_cast<std::int_fast8_t>(weekday);
-          }
-        }
-      }
-    } else if (*p == 'J') {
-      int day = 0;
-      if ((p = ParseInt(p + 1, 1, 365, &day)) != nullptr) {
-        res->date.fmt = PosixTransition::J;
-        res->date.j.day = static_cast<std::int_fast16_t>(day);
-      }
-    } else {
-      int day = 0;
-      if ((p = ParseInt(p, 0, 365, &day)) != nullptr) {
-        res->date.fmt = PosixTransition::N;
-        res->date.n.day = static_cast<std::int_fast16_t>(day);
-      }
-    }
-  }
-  if (p != nullptr) {
-    res->time.offset = 2 * 60 * 60;  // default offset is 02:00:00
-    if (*p == '/') p = ParseOffset(p + 1, -167, 167, 1, &res->time.offset);
-  }
-  return p;
-}
-
-}  // namespace
-
-// spec = std offset [ dst [ offset ] , datetime , datetime ]
-bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) {
-  const char* p = spec.c_str();
-  if (*p == ':') return false;
-
-  p = ParseAbbr(p, &res->std_abbr);
-  p = ParseOffset(p, 0, 24, -1, &res->std_offset);
-  if (p == nullptr) return false;
-  if (*p == '\0') return true;
-
-  p = ParseAbbr(p, &res->dst_abbr);
-  if (p == nullptr) return false;
-  res->dst_offset = res->std_offset + (60 * 60);  // default
-  if (*p != ',') p = ParseOffset(p, 0, 24, -1, &res->dst_offset);
-
-  p = ParseDateTime(p, &res->dst_start);
-  p = ParseDateTime(p, &res->dst_end);
-
-  return p != nullptr && *p == '\0';
-}
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.h b/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.h
deleted file mode 100644
index 0cf29055f5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-// Parsing of a POSIX zone spec as described in the TZ part of section 8.3 in
-// http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html.
-//
-// The current POSIX spec for America/Los_Angeles is "PST8PDT,M3.2.0,M11.1.0",
-// which would be broken down as ...
-//
-//   PosixTimeZone {
-//     std_abbr = "PST"
-//     std_offset = -28800
-//     dst_abbr = "PDT"
-//     dst_offset = -25200
-//     dst_start = PosixTransition {
-//       date {
-//         m {
-//           month = 3
-//           week = 2
-//           weekday = 0
-//         }
-//       }
-//       time {
-//         offset = 7200
-//       }
-//     }
-//     dst_end = PosixTransition {
-//       date {
-//         m {
-//           month = 11
-//           week = 1
-//           weekday = 0
-//         }
-//       }
-//       time {
-//         offset = 7200
-//       }
-//     }
-//   }
-
-#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
-#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
-
-#include <cstdint>
-#include <string>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// The date/time of the transition. The date is specified as either:
-// (J) the Nth day of the year (1 <= N <= 365), excluding leap days, or
-// (N) the Nth day of the year (0 <= N <= 365), including leap days, or
-// (M) the Nth weekday of a month (e.g., the 2nd Sunday in March).
-// The time, specified as a day offset, identifies the particular moment
-// of the transition, and may be negative or >= 24h, and in which case
-// it would take us to another day, and perhaps week, or even month.
-struct PosixTransition {
-  enum DateFormat { J, N, M };
-
-  struct Date {
-    struct NonLeapDay {
-      std::int_fast16_t day;  // day of non-leap year [1:365]
-    };
-    struct Day {
-      std::int_fast16_t day;  // day of year [0:365]
-    };
-    struct MonthWeekWeekday {
-      std::int_fast8_t month;    // month of year [1:12]
-      std::int_fast8_t week;     // week of month [1:5] (5==last)
-      std::int_fast8_t weekday;  // 0==Sun, ..., 6=Sat
-    };
-
-    DateFormat fmt;
-
-    union {
-      NonLeapDay j;
-      Day n;
-      MonthWeekWeekday m;
-    };
-  };
-
-  struct Time {
-    std::int_fast32_t offset;  // seconds before/after 00:00:00
-  };
-
-  Date date;
-  Time time;
-};
-
-// The entirety of a POSIX-string specified time-zone rule. The standard
-// abbreviation and offset are always given. If the time zone includes
-// daylight saving, then the daylight abbrevation is non-empty and the
-// remaining fields are also valid. Note that the start/end transitions
-// are not ordered---in the southern hemisphere the transition to end
-// daylight time occurs first in any particular year.
-struct PosixTimeZone {
-  std::string std_abbr;
-  std::int_fast32_t std_offset;
-
-  std::string dst_abbr;
-  std::int_fast32_t dst_offset;
-  PosixTransition dst_start;
-  PosixTransition dst_end;
-};
-
-// Breaks down a POSIX time-zone specification into its constituent pieces,
-// filling in any missing values (DST offset, or start/end transition times)
-// with the standard-defined defaults. Returns false if the specification
-// could not be parsed (although some fields of *res may have been altered).
-bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res);
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/tzfile.h b/third_party/abseil_cpp/absl/time/internal/cctz/src/tzfile.h
deleted file mode 100644
index 269fa36c53..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/tzfile.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/* Layout and location of TZif files.  */
-
-#ifndef TZFILE_H
-
-#define TZFILE_H
-
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson.
-*/
-
-/*
-** This header is for use ONLY with the time conversion code.
-** There is no guarantee that it will remain unchanged,
-** or that it will remain at all.
-** Do NOT copy it to any system include directory.
-** Thank you!
-*/
-
-/*
-** Information about time zone files.
-*/
-
-#ifndef TZDIR
-#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
-#endif                              /* !defined TZDIR */
-
-#ifndef TZDEFAULT
-#define TZDEFAULT "/etc/localtime"
-#endif /* !defined TZDEFAULT */
-
-#ifndef TZDEFRULES
-#define TZDEFRULES "posixrules"
-#endif /* !defined TZDEFRULES */
-
-/* See Internet RFC 8536 for more details about the following format.  */
-
-/*
-** Each file begins with. . .
-*/
-
-#define TZ_MAGIC "TZif"
-
-struct tzhead {
-  char tzh_magic[4];      /* TZ_MAGIC */
-  char tzh_version[1];    /* '\0' or '2' or '3' as of 2013 */
-  char tzh_reserved[15];  /* reserved; must be zero */
-  char tzh_ttisutcnt[4];  /* coded number of trans. time flags */
-  char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
-  char tzh_leapcnt[4];    /* coded number of leap seconds */
-  char tzh_timecnt[4];    /* coded number of transition times */
-  char tzh_typecnt[4];    /* coded number of local time types */
-  char tzh_charcnt[4];    /* coded number of abbr. chars */
-};
-
-/*
-** . . .followed by. . .
-**
-**	tzh_timecnt (char [4])s		coded transition times a la time(2)
-**	tzh_timecnt (unsigned char)s	types of local time starting at above
-**	tzh_typecnt repetitions of
-**		one (char [4])		coded UT offset in seconds
-**		one (unsigned char)	used to set tm_isdst
-**		one (unsigned char)	that's an abbreviation list index
-**	tzh_charcnt (char)s		'\0'-terminated zone abbreviations
-**	tzh_leapcnt repetitions of
-**		one (char [4])		coded leap second transition times
-**		one (char [4])		total correction after above
-**	tzh_ttisstdcnt (char)s		indexed by type; if 1, transition
-**					time is standard time, if 0,
-**					transition time is local (wall clock)
-**					time; if absent, transition times are
-**					assumed to be local time
-**	tzh_ttisutcnt (char)s		indexed by type; if 1, transition
-**					time is UT, if 0, transition time is
-**					local time; if absent, transition
-**					times are assumed to be local time.
-**					When this is 1, the corresponding
-**					std/wall indicator must also be 1.
-*/
-
-/*
-** If tzh_version is '2' or greater, the above is followed by a second instance
-** of tzhead and a second instance of the data in which each coded transition
-** time uses 8 rather than 4 chars,
-** then a POSIX-TZ-environment-variable-style string for use in handling
-** instants after the last transition time stored in the file
-** (with nothing between the newlines if there is no POSIX representation for
-** such instants).
-**
-** If tz_version is '3' or greater, the above is extended as follows.
-** First, the POSIX TZ string's hour offset may range from -167
-** through 167 as compared to the POSIX-required 0 through 24.
-** Second, its DST start time may be January 1 at 00:00 and its stop
-** time December 31 at 24:00 plus the difference between DST and
-** standard time, indicating DST all year.
-*/
-
-/*
-** In the current implementation, "tzset()" refuses to deal with files that
-** exceed any of the limits below.
-*/
-
-#ifndef TZ_MAX_TIMES
-#define TZ_MAX_TIMES 2000
-#endif /* !defined TZ_MAX_TIMES */
-
-#ifndef TZ_MAX_TYPES
-/* This must be at least 17 for Europe/Samara and Europe/Vilnius.  */
-#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
-#endif                   /* !defined TZ_MAX_TYPES */
-
-#ifndef TZ_MAX_CHARS
-#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
-                        /* (limited by what unsigned chars can hold) */
-#endif                  /* !defined TZ_MAX_CHARS */
-
-#ifndef TZ_MAX_LEAPS
-#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
-#endif                  /* !defined TZ_MAX_LEAPS */
-
-#endif /* !defined TZFILE_H */
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/src/zone_info_source.cc b/third_party/abseil_cpp/absl/time/internal/cctz/src/zone_info_source.cc
deleted file mode 100644
index 72095339c3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/src/zone_info_source.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   https://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz {
-
-// Defined out-of-line to avoid emitting a weak vtable in all TUs.
-ZoneInfoSource::~ZoneInfoSource() {}
-std::string ZoneInfoSource::Version() const { return std::string(); }
-
-}  // namespace cctz
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz_extension {
-
-namespace {
-
-// A default for cctz_extension::zone_info_source_factory, which simply
-// defers to the fallback factory.
-std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory(
-    const std::string& name,
-    const std::function<
-        std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>(
-            const std::string& name)>& fallback_factory) {
-  return fallback_factory(name);
-}
-
-}  // namespace
-
-// A "weak" definition for cctz_extension::zone_info_source_factory.
-// The user may override this with their own "strong" definition (see
-// zone_info_source.h).
-#if !defined(__has_attribute)
-#define __has_attribute(x) 0
-#endif
-// MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the
-// Windows linker cannot handle that. Nor does the MinGW compiler know how to
-// pass "#pragma comment(linker, ...)" to the Windows linker.
-#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__)
-ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) =
-    DefaultFactory;
-#elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION)
-extern ZoneInfoSourceFactory zone_info_source_factory;
-extern ZoneInfoSourceFactory default_factory;
-ZoneInfoSourceFactory default_factory = DefaultFactory;
-#if defined(_M_IX86)
-#pragma comment(                                                                                                         \
-    linker,                                                                                                              \
-    "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS                    \
-    "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                 \
-    "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                   \
-    "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE      \
-    "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                   \
-    "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                   \
-    "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
-    "@@ZA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS                                       \
-    "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                 \
-    "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                   \
-    "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE      \
-    "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                   \
-    "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                   \
-    "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
-    "@@ZA")
-#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM) || \
-    defined(_M_ARM64)
-#pragma comment(                                                                                                          \
-    linker,                                                                                                               \
-    "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS                     \
-    "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                  \
-    "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                    \
-    "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE      \
-    "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                   \
-    "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                    \
-    "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
-    "@@ZEA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS                                       \
-    "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                  \
-    "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                    \
-    "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE      \
-    "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                   \
-    "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS                                    \
-    "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
-    "@@ZEA")
-#else
-#error Unsupported MSVC platform
-#endif  // _M_<PLATFORM>
-#else
-// Make it a "strong" definition if we have no other choice.
-ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory;
-#endif
-
-}  // namespace cctz_extension
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/README.zoneinfo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/README.zoneinfo
deleted file mode 100644
index 95fb4a91d1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/README.zoneinfo
+++ /dev/null
@@ -1,37 +0,0 @@
-testdata/zoneinfo contains time-zone data files that may be used with CCTZ.
-Install them in a location referenced by the ${TZDIR} environment variable.
-Symbolic and hard links have been eliminated for portability.
-
-On Linux systems the distribution's versions of these files can probably
-already be found in the default ${TZDIR} location, /usr/share/zoneinfo.
-
-New versions can be generated using the following shell script.
-
-  #!/bin/sh -
-  set -e
-  DESTDIR=$(mktemp -d)
-  trap "rm -fr ${DESTDIR}" 0 2 15
-  (
-    cd ${DESTDIR}
-    git clone https://github.com/eggert/tz.git
-    make --directory=tz \
-        install DESTDIR=${DESTDIR} \
-                DATAFORM=vanguard \
-                TZDIR=/zoneinfo \
-                REDO=posix_only \
-                LOCALTIME=Factory \
-                TZDATA_TEXT= \
-                ZONETABLES=zone1970.tab
-    tar --create --dereference --hard-dereference --file tzfile.tar \
-        --directory=tz tzfile.h
-    tar --create --dereference --hard-dereference --file zoneinfo.tar \
-        --exclude=zoneinfo/posixrules zoneinfo \
-        --directory=tz version
-  )
-  tar --extract --directory src --file ${DESTDIR}/tzfile.tar
-  tar --extract --directory testdata --file ${DESTDIR}/zoneinfo.tar
-  exit 0
-
-To run the CCTZ tests using the testdata/zoneinfo files, execute:
-
-  bazel test --test_env=TZDIR=${PWD}/testdata/zoneinfo ...
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/version b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/version
deleted file mode 100644
index b4410dce16..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/version
+++ /dev/null
@@ -1 +0,0 @@
-2020d
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
deleted file mode 100644
index 9ca907bf9b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
deleted file mode 100644
index 56a4dd2a19..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
deleted file mode 100644
index 0da1d1e211..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
deleted file mode 100644
index 651e5cf67a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
deleted file mode 100644
index 651e5cf67a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
deleted file mode 100644
index ea38c97008..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
deleted file mode 100644
index 0263c90bd0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
deleted file mode 100644
index a461dceaa2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
deleted file mode 100644
index 772e23c4cd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
deleted file mode 100644
index 651e5cf67a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
deleted file mode 100644
index 651e5cf67a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
deleted file mode 100644
index bada0638f8..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
deleted file mode 100644
index 36b05220ac..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
deleted file mode 100644
index 3f8e44b8a6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
deleted file mode 100644
index 651e5cf67a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
deleted file mode 100644
index 651e5cf67a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
deleted file mode 100644
index 651e5cf67a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
deleted file mode 100644
index 651e5cf67a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
deleted file mode 100644
index bada0638f8..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
deleted file mode 100644
index bada0638f8..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
deleted file mode 100644
index 837780922f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
deleted file mode 100644
index ecbc0966dc..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
deleted file mode 100644
index 2f2ce2f772..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
deleted file mode 100644
index 425ad3fda7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
deleted file mode 100644
index e0c89971aa..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
deleted file mode 100644
index ca324cb4cd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
deleted file mode 100644
index 0edc52b9b7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adak b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adak
deleted file mode 100644
index b1497bda63..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adak
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
deleted file mode 100644
index cdf0572be3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina
deleted file mode 100644
index f66c9f79d6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires
deleted file mode 100644
index d6f999b860..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca
deleted file mode 100644
index 1dcc8d8543..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia
deleted file mode 100644
index 1dcc8d8543..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba
deleted file mode 100644
index 35a52e53d1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy
deleted file mode 100644
index b275f27c02..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja
deleted file mode 100644
index 23fca12205..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza
deleted file mode 100644
index 691c56978a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos
deleted file mode 100644
index 991d1fae69..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta
deleted file mode 100644
index 58863e0436..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan
deleted file mode 100644
index 7eba33c1c5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis
deleted file mode 100644
index 0a81cbddfa..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman
deleted file mode 100644
index 10556d5d85..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia
deleted file mode 100644
index e031750276..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba
deleted file mode 100644
index d6ddf7d8f6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion
deleted file mode 100644
index 6225036742..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan
deleted file mode 100644
index c828715283..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atka b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atka
deleted file mode 100644
index b1497bda63..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atka
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia
deleted file mode 100644
index 7969e30766..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas
deleted file mode 100644
index cbe22a7622..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados
deleted file mode 100644
index 9d3afa6a53..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belem b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belem
deleted file mode 100644
index e0d7653c64..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belem
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belize b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belize
deleted file mode 100644
index de99b84501..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belize
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon
deleted file mode 100644
index 7096b69a56..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista
deleted file mode 100644
index fca97207b2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota
deleted file mode 100644
index 6cb53d4e61..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boise b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boise
deleted file mode 100644
index 72fec9e8c5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boise
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires
deleted file mode 100644
index d6f999b860..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay
deleted file mode 100644
index 0a2225244a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande
deleted file mode 100644
index 6855e4e9fe..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun
deleted file mode 100644
index 640b259fd0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas
deleted file mode 100644
index 8dbe6ff741..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca
deleted file mode 100644
index 1dcc8d8543..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne
deleted file mode 100644
index cd49f05344..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman
deleted file mode 100644
index 9154643f4c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago
deleted file mode 100644
index b016880653..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua
deleted file mode 100644
index e1780a5750..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour
deleted file mode 100644
index c828715283..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba
deleted file mode 100644
index 35a52e53d1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica
deleted file mode 100644
index 08f0128ee6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Creston b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Creston
deleted file mode 100644
index 9d69a0ab81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Creston
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba
deleted file mode 100644
index c09a87558d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao
deleted file mode 100644
index d6ddf7d8f6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn
deleted file mode 100644
index 8718efcce2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson
deleted file mode 100644
index 07e4c5f4ac..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek
deleted file mode 100644
index 761d1d9af5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Denver b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Denver
deleted file mode 100644
index 09e54e5c7c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Denver
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit
deleted file mode 100644
index 6eb3ac46ec..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton
deleted file mode 100644
index 645ee94530..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe
deleted file mode 100644
index 7da4b98fe3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador
deleted file mode 100644
index 43484117e2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
deleted file mode 100644
index 19ccd3576d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson
deleted file mode 100644
index 2a49c6c50f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne
deleted file mode 100644
index 6b08d15bda..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza
deleted file mode 100644
index 092e40d701..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay
deleted file mode 100644
index f85eb34157..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
deleted file mode 100644
index 4ddc99d8b7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
deleted file mode 100644
index 820e0dd2cd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk
deleted file mode 100644
index 062fcaed8e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala
deleted file mode 100644
index 8aa8e588e3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil
deleted file mode 100644
index 381ae6c463..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana
deleted file mode 100644
index ebd85d0f3e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax
deleted file mode 100644
index 9fa850a7d4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Havana b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Havana
deleted file mode 100644
index e06629d368..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Havana
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo
deleted file mode 100644
index 8283239eca..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis
deleted file mode 100644
index 6b08d15bda..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox
deleted file mode 100644
index b187d5f8c7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo
deleted file mode 100644
index a730fe666b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg
deleted file mode 100644
index 341a0235ef..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City
deleted file mode 100644
index 76e1f6285b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay
deleted file mode 100644
index f2acf6cbbd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes
deleted file mode 100644
index c255f89b6d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
deleted file mode 100644
index 8700ed9f06..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis
deleted file mode 100644
index 6b08d15bda..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik
deleted file mode 100644
index af3107db51..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit
deleted file mode 100644
index eb2c99cca5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica
deleted file mode 100644
index be6b1b6f1e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy
deleted file mode 100644
index b275f27c02..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau
deleted file mode 100644
index e347b369f7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville
deleted file mode 100644
index f2136d6ed4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello
deleted file mode 100644
index d9f54a18bb..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN
deleted file mode 100644
index b187d5f8c7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk
deleted file mode 100644
index d6ddf7d8f6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz
deleted file mode 100644
index 68ddaae768..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lima b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lima
deleted file mode 100644
index b643c5517f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lima
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles
deleted file mode 100644
index aaf07787ad..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville
deleted file mode 100644
index f2136d6ed4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes
deleted file mode 100644
index d6ddf7d8f6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio
deleted file mode 100644
index dbb8d57d91..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Managua b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Managua
deleted file mode 100644
index 86ef76bf22..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Managua
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus
deleted file mode 100644
index 59c952ebc6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique
deleted file mode 100644
index 25c0232d95..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
deleted file mode 100644
index 722751b20e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan
deleted file mode 100644
index 4c819fab02..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza
deleted file mode 100644
index 691c56978a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee
deleted file mode 100644
index 28d2c56e1a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Merida b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Merida
deleted file mode 100644
index d3b0ca12c9..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Merida
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
deleted file mode 100644
index 9fefee388e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City
deleted file mode 100644
index ffcf8bee10..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon
deleted file mode 100644
index 3b62585d0a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
deleted file mode 100644
index ecb69ef2c9..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey
deleted file mode 100644
index dea9e3f586..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo
deleted file mode 100644
index 4b2fb3e560..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal
deleted file mode 100644
index fe6be8ea8c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
deleted file mode 100644
index cf1e92dc0f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/New_York b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/New_York
deleted file mode 100644
index 2b6c2eea14..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/New_York
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon
deleted file mode 100644
index b9f67a9f94..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nome b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nome
deleted file mode 100644
index 23ead1c004..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nome
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha
deleted file mode 100644
index 9e74745ca7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah
deleted file mode 100644
index becf438330..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center
deleted file mode 100644
index d03bda045d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem
deleted file mode 100644
index ecefc15d8c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
deleted file mode 100644
index 4ddc99d8b7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
deleted file mode 100644
index da0909cb21..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Panama b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Panama
deleted file mode 100644
index 9154643f4c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Panama
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung
deleted file mode 100644
index 5be6f9b016..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo
deleted file mode 100644
index 24f925a2dd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix
deleted file mode 100644
index c2bd2f949b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince
deleted file mode 100644
index 3e75731baa..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre
deleted file mode 100644
index fb5185ca60..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho
deleted file mode 100644
index 7f8047d939..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico
deleted file mode 100644
index 47b4dc3416..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas
deleted file mode 100644
index 5c9a20b947..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
deleted file mode 100644
index d6ddda4822..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet
deleted file mode 100644
index 92e2ed2dbe..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Recife b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Recife
deleted file mode 100644
index 305abcb8a2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Recife
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Regina b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Regina
deleted file mode 100644
index a3f8217a54..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Regina
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute
deleted file mode 100644
index a84d1dfdb3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco
deleted file mode 100644
index fb5185ca60..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario
deleted file mode 100644
index 35a52e53d1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
deleted file mode 100644
index 19ccd3576d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem
deleted file mode 100644
index f81d144206..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago
deleted file mode 100644
index 8d6032264b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo
deleted file mode 100644
index 3e07850866..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo
deleted file mode 100644
index a16da2c4d5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
deleted file mode 100644
index 6db49124e2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock
deleted file mode 100644
index 09e54e5c7c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka
deleted file mode 100644
index 36681ed78e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
deleted file mode 100644
index e5f2aec2bb..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current
deleted file mode 100644
index bdbb494487..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa
deleted file mode 100644
index 38036a3283..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thule b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thule
deleted file mode 100644
index f38dc56bf2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thule
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay
deleted file mode 100644
index fcb0328043..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
deleted file mode 100644
index 19ccd3576d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto
deleted file mode 100644
index fe6be8ea8c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver
deleted file mode 100644
index c998491112..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin
deleted file mode 100644
index f4fe590342..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse
deleted file mode 100644
index 878b6a92f7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg
deleted file mode 100644
index 7e646d18e1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat
deleted file mode 100644
index 773feba89d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife
deleted file mode 100644
index c779cef92c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
deleted file mode 100644
index 30315cc078..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis
deleted file mode 100644
index 3ec32224f2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville
deleted file mode 100644
index c0cfc85a29..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
deleted file mode 100644
index 232717b613..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson
deleted file mode 100644
index 05e4c6c586..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo
deleted file mode 100644
index afb3929318..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer
deleted file mode 100644
index 32c1941634..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera
deleted file mode 100644
index ea49c00b22..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole
deleted file mode 100644
index afb3929318..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa
deleted file mode 100644
index 97d80d752c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
deleted file mode 100644
index 4e31affb50..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
deleted file mode 100644
index 6e3290717e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen
deleted file mode 100644
index dfc509570a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden
deleted file mode 100644
index 01c47ccb86..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty
deleted file mode 100644
index 3ec4fc89fe..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman
deleted file mode 100644
index 1bd09fef27..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr
deleted file mode 100644
index 551884d322..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau
deleted file mode 100644
index 3a40d1175a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe
deleted file mode 100644
index 62c5840a83..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat
deleted file mode 100644
index 8482167269..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad
deleted file mode 100644
index 8482167269..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau
deleted file mode 100644
index cb2c82f657..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad
deleted file mode 100644
index a3ce975991..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain
deleted file mode 100644
index 7409d74983..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku
deleted file mode 100644
index 96203d7a42..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok
deleted file mode 100644
index ed687d2985..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul
deleted file mode 100644
index ff976dd3b2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut
deleted file mode 100644
index 55dce5722c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek
deleted file mode 100644
index fe7832cdf9..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei
deleted file mode 100644
index e67b411b9f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta
deleted file mode 100644
index 00bc80a65e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita
deleted file mode 100644
index 9d49cd35cd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan
deleted file mode 100644
index 0a948c2eac..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing
deleted file mode 100644
index d6b66984a2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking
deleted file mode 100644
index d6b66984a2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo
deleted file mode 100644
index 3eeb1b72b6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca
deleted file mode 100644
index 28136808b6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus
deleted file mode 100644
index 168ef9baa4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka
deleted file mode 100644
index 28136808b6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili
deleted file mode 100644
index bb7be9f3a4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai
deleted file mode 100644
index 58d75bc26e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe
deleted file mode 100644
index d83fb076a2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta
deleted file mode 100644
index cc44179564..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
deleted file mode 100644
index 4278ffa512..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin
deleted file mode 100644
index d6b66984a2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
deleted file mode 100644
index e55318aaca..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh
deleted file mode 100644
index 7ca9972502..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong
deleted file mode 100644
index c80e364801..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd
deleted file mode 100644
index 6e08a26127..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk
deleted file mode 100644
index 550e2a0877..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul
deleted file mode 100644
index c891866873..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta
deleted file mode 100644
index c9752d2f23..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura
deleted file mode 100644
index 7c22f539d9..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem
deleted file mode 100644
index e6e6cc6ca5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul
deleted file mode 100644
index 660ce4cf69..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka
deleted file mode 100644
index c65155402d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi
deleted file mode 100644
index e56d5afdaf..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar
deleted file mode 100644
index 69ff7f6fb4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu
deleted file mode 100644
index 3a0d330ffd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu
deleted file mode 100644
index 3a0d330ffd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga
deleted file mode 100644
index aeb733202a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata
deleted file mode 100644
index 00bc80a65e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk
deleted file mode 100644
index e0d4fcb5c3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur
deleted file mode 100644
index e93dd5141b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching
deleted file mode 100644
index 59bc6e40b7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait
deleted file mode 100644
index 01c47ccb86..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao
deleted file mode 100644
index c22f75e42d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau
deleted file mode 100644
index c22f75e42d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan
deleted file mode 100644
index 16bac84446..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar
deleted file mode 100644
index 5990010b64..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila
deleted file mode 100644
index 3c3584e09a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat
deleted file mode 100644
index 58d75bc26e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
deleted file mode 100644
index c210d0a598..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk
deleted file mode 100644
index 9378d50539..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk
deleted file mode 100644
index 65a9fa2cd2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk
deleted file mode 100644
index dc0ed422f6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral
deleted file mode 100644
index 25a63ec8b9..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh
deleted file mode 100644
index ed687d2985..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak
deleted file mode 100644
index 285bed2c63..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang
deleted file mode 100644
index 57240cf89f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar
deleted file mode 100644
index 7409d74983..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay
deleted file mode 100644
index ff6fe61d50..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda
deleted file mode 100644
index fe4d6c6d6d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon
deleted file mode 100644
index 14b2ad09ea..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh
deleted file mode 100644
index 01c47ccb86..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon
deleted file mode 100644
index 7ca9972502..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin
deleted file mode 100644
index 69f0faad1e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand
deleted file mode 100644
index c43e27c5d4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul
deleted file mode 100644
index 1755147fab..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai
deleted file mode 100644
index d6b66984a2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore
deleted file mode 100644
index 350d77e28e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk
deleted file mode 100644
index 7fdee5cbee..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei
deleted file mode 100644
index 35d89d036d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent
deleted file mode 100644
index 65ee428ce1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi
deleted file mode 100644
index 166e4341d6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran
deleted file mode 100644
index f1555f0032..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv
deleted file mode 100644
index e6e6cc6ca5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu
deleted file mode 100644
index 0edc72cfe4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu
deleted file mode 100644
index 0edc72cfe4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo
deleted file mode 100644
index 1aa066ce38..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk
deleted file mode 100644
index c3c307d7b9..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang
deleted file mode 100644
index 5990010b64..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar
deleted file mode 100644
index 6f5d3a15ab..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator
deleted file mode 100644
index 6f5d3a15ab..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi
deleted file mode 100644
index 69ff7f6fb4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera
deleted file mode 100644
index c39331e3aa..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane
deleted file mode 100644
index ed687d2985..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok
deleted file mode 100644
index 72a3d4e87a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk
deleted file mode 100644
index 336f932e8d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon
deleted file mode 100644
index 14b2ad09ea..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg
deleted file mode 100644
index a3bf7f29b6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan
deleted file mode 100644
index 6dd927cb94..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores
deleted file mode 100644
index b7f75a9cf6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda
deleted file mode 100644
index aa3301478f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary
deleted file mode 100644
index 5ab3243a5f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde
deleted file mode 100644
index 8f7de1c0a1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe
deleted file mode 100644
index 9558bf7180..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe
deleted file mode 100644
index 9558bf7180..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen
deleted file mode 100644
index dfc509570a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira
deleted file mode 100644
index 7c3a49c0e8..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik
deleted file mode 100644
index 2451aca76d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia
deleted file mode 100644
index 7fa5f46835..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena
deleted file mode 100644
index 8906e88c81..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley
deleted file mode 100644
index 1a4c8ea863..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT
deleted file mode 100644
index f235d0dc74..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide
deleted file mode 100644
index f397b3b9ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane
deleted file mode 100644
index c7915db30b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill
deleted file mode 100644
index ed0d17a610..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra
deleted file mode 100644
index f235d0dc74..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie
deleted file mode 100644
index 55ceaefc60..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin
deleted file mode 100644
index 7114153b65..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla
deleted file mode 100644
index 9fbc01fe67..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart
deleted file mode 100644
index 21ef2d396f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI
deleted file mode 100644
index 4d4ec8ceea..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman
deleted file mode 100644
index e271d5b3e5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe
deleted file mode 100644
index 4d4ec8ceea..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne
deleted file mode 100644
index c7160da33f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW
deleted file mode 100644
index f235d0dc74..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/North b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/North
deleted file mode 100644
index 7114153b65..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/North
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth
deleted file mode 100644
index e449b03fcf..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland
deleted file mode 100644
index c7915db30b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/South b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/South
deleted file mode 100644
index f397b3b9ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/South
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney
deleted file mode 100644
index f235d0dc74..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania
deleted file mode 100644
index 21ef2d396f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria
deleted file mode 100644
index c7160da33f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/West b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/West
deleted file mode 100644
index e449b03fcf..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/West
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna
deleted file mode 100644
index ed0d17a610..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre
deleted file mode 100644
index fb5185ca60..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha
deleted file mode 100644
index 9e74745ca7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East
deleted file mode 100644
index a16da2c4d5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West
deleted file mode 100644
index 59c952ebc6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/CET b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/CET
deleted file mode 100644
index 546748d6ea..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/CET
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT
deleted file mode 100644
index d931558058..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic
deleted file mode 100644
index 9fa850a7d4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central
deleted file mode 100644
index 7e646d18e1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern
deleted file mode 100644
index fe6be8ea8c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain
deleted file mode 100644
index 645ee94530..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
deleted file mode 100644
index e5f2aec2bb..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific
deleted file mode 100644
index c998491112..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan
deleted file mode 100644
index a3f8217a54..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon
deleted file mode 100644
index 878b6a92f7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental
deleted file mode 100644
index 8d6032264b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland
deleted file mode 100644
index d29bcd68b0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Cuba b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Cuba
deleted file mode 100644
index e06629d368..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Cuba
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EET b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EET
deleted file mode 100644
index 378919ea11..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EET
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EST b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EST
deleted file mode 100644
index 3ae9691145..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EST
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT
deleted file mode 100644
index 50c95e0cb0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Egypt b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Egypt
deleted file mode 100644
index ea38c97008..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Egypt
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Eire b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Eire
deleted file mode 100644
index 4a45ea8f73..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Eire
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1
deleted file mode 100644
index 98d5dcf917..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10
deleted file mode 100644
index ecb287e667..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11
deleted file mode 100644
index e941412971..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12
deleted file mode 100644
index 9c95bd0736..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2
deleted file mode 100644
index 6d5ce3db73..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3
deleted file mode 100644
index 5ef7be71fd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4
deleted file mode 100644
index 75f16216f0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5
deleted file mode 100644
index 589990ae89..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6
deleted file mode 100644
index fcb60ca246..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7
deleted file mode 100644
index c0427a40ee..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8
deleted file mode 100644
index 9bdc2283c0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9
deleted file mode 100644
index ca7a81f656..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1
deleted file mode 100644
index cb45601c95..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10
deleted file mode 100644
index 11d988e10a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11
deleted file mode 100644
index f4c5d5cc29..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12
deleted file mode 100644
index cd397b02cd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13
deleted file mode 100644
index 8fad7c6b0b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14
deleted file mode 100644
index a595e60eea..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2
deleted file mode 100644
index 97b44a9bae..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3
deleted file mode 100644
index 4eb17ff005..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4
deleted file mode 100644
index 13aef80cbb..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5
deleted file mode 100644
index 83a2816955..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6
deleted file mode 100644
index 79a983e545..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7
deleted file mode 100644
index e136690e16..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8
deleted file mode 100644
index bc70fe416f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9
deleted file mode 100644
index d18cedd524..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT
deleted file mode 100644
index 00841a6221..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC
deleted file mode 100644
index 00841a6221..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal
deleted file mode 100644
index 00841a6221..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu
deleted file mode 100644
index 00841a6221..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam
deleted file mode 100644
index 4a6fa1d494..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra
deleted file mode 100644
index 38685d4219..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan
deleted file mode 100644
index aff8d82d2a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens
deleted file mode 100644
index 231bf9c3b7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
deleted file mode 100644
index 323cd3818a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade
deleted file mode 100644
index a1bf9281ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin
deleted file mode 100644
index 465546bd39..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava
deleted file mode 100644
index fb7c145ac4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels
deleted file mode 100644
index 31973271d2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
deleted file mode 100644
index efa689ba9e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest
deleted file mode 100644
index 940be4670a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen
deleted file mode 100644
index 388df2969f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
deleted file mode 100644
index 6970b14c50..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen
deleted file mode 100644
index 45984a7599..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin
deleted file mode 100644
index 4a45ea8f73..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar
deleted file mode 100644
index 017bb2e347..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
deleted file mode 100644
index 323cd3818a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki
deleted file mode 100644
index ff5e565305..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
deleted file mode 100644
index 323cd3818a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul
deleted file mode 100644
index c891866873..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
deleted file mode 100644
index 323cd3818a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad
deleted file mode 100644
index 0ec4756470..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
deleted file mode 100644
index 8f83cefbcc..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov
deleted file mode 100644
index d1c93c540c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon
deleted file mode 100644
index 64841661a0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana
deleted file mode 100644
index a1bf9281ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/London b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
deleted file mode 100644
index 323cd3818a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg
deleted file mode 100644
index 682bcbf61a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid
deleted file mode 100644
index 60bdf4d07e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta
deleted file mode 100644
index 27539c2243..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn
deleted file mode 100644
index ff5e565305..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk
deleted file mode 100644
index 30d3a672bf..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco
deleted file mode 100644
index f30dfc7014..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow
deleted file mode 100644
index 5e6b6de645..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
deleted file mode 100644
index c210d0a598..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo
deleted file mode 100644
index dfc509570a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris
deleted file mode 100644
index 00a27264c2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica
deleted file mode 100644
index a1bf9281ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague
deleted file mode 100644
index fb7c145ac4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
deleted file mode 100644
index 26af4c90b3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome
deleted file mode 100644
index 639ca3be40..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara
deleted file mode 100644
index 8d0c26e5c8..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino
deleted file mode 100644
index 639ca3be40..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo
deleted file mode 100644
index a1bf9281ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov
deleted file mode 100644
index 2684d8f8b8..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol
deleted file mode 100644
index 88a6f3bdb4..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje
deleted file mode 100644
index a1bf9281ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
deleted file mode 100644
index eabc972a22..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm
deleted file mode 100644
index dd3eb32278..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
deleted file mode 100644
index 5321bbd46e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane
deleted file mode 100644
index 743a7337ff..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
deleted file mode 100644
index 6970b14c50..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk
deleted file mode 100644
index bb842cb1f5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
deleted file mode 100644
index a5755685e3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz
deleted file mode 100644
index 388df2969f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican
deleted file mode 100644
index 639ca3be40..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna
deleted file mode 100644
index 75339e98d0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
deleted file mode 100644
index 75b2eebb57..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
deleted file mode 100644
index a486ad42ea..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw
deleted file mode 100644
index efe1a40f2a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb
deleted file mode 100644
index a1bf9281ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
deleted file mode 100644
index 4ea8dae45a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich
deleted file mode 100644
index 388df2969f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Factory b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Factory
deleted file mode 100644
index b4dd7735ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Factory
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GB b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GB
deleted file mode 100644
index 323cd3818a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GB
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
deleted file mode 100644
index 323cd3818a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT+0
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT+0
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT-0
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT-0
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT0 b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT0
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT0
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Greenwich b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Greenwich
deleted file mode 100644
index 157573b1d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Greenwich
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/HST b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/HST
deleted file mode 100644
index 160a53e045..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/HST
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Hongkong b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Hongkong
deleted file mode 100644
index c80e364801..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Hongkong
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Iceland b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Iceland
deleted file mode 100644
index 2451aca76d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Iceland
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos
deleted file mode 100644
index 8b8ce226b6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas
deleted file mode 100644
index 766024b36c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos
deleted file mode 100644
index 117503410c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen
deleted file mode 100644
index 8ce93e0124..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe
deleted file mode 100644
index 75362bbf03..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives
deleted file mode 100644
index 58a82e4eb7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius
deleted file mode 100644
index 7c11134882..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte
deleted file mode 100644
index d3c0bb328c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion
deleted file mode 100644
index 248a7c93a5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Iran b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Iran
deleted file mode 100644
index f1555f0032..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Iran
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Israel b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Israel
deleted file mode 100644
index e6e6cc6ca5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Israel
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Jamaica b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Jamaica
deleted file mode 100644
index be6b1b6f1e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Jamaica
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Japan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Japan
deleted file mode 100644
index 1aa066ce38..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Japan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein
deleted file mode 100644
index 9416d522d0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Libya b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Libya
deleted file mode 100644
index e0c89971aa..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Libya
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MET b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MET
deleted file mode 100644
index 6f0558c3b6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MET
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MST b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MST
deleted file mode 100644
index a0953d1e79..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MST
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT
deleted file mode 100644
index 137867c8bf..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
deleted file mode 100644
index 19ccd3576d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur
deleted file mode 100644
index 4c819fab02..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General
deleted file mode 100644
index ffcf8bee10..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ
deleted file mode 100644
index afb3929318..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT
deleted file mode 100644
index f06065ebd1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Navajo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Navajo
deleted file mode 100644
index 09e54e5c7c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Navajo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/PRC b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/PRC
deleted file mode 100644
index d6b66984a2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/PRC
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT
deleted file mode 100644
index fde4833f6b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia
deleted file mode 100644
index 244af26f8a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland
deleted file mode 100644
index afb3929318..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville
deleted file mode 100644
index 7c667093c5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham
deleted file mode 100644
index f06065ebd1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk
deleted file mode 100644
index ea3fb5cd3c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter
deleted file mode 100644
index d29bcd68b0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate
deleted file mode 100644
index 906971e2ce..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury
deleted file mode 100644
index b22ab147c3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo
deleted file mode 100644
index b7b30213e1..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
deleted file mode 100644
index e3934e423c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti
deleted file mode 100644
index 78ab35b6b0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos
deleted file mode 100644
index a9403eca46..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier
deleted file mode 100644
index ddfc34ffc0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal
deleted file mode 100644
index 720c679017..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam
deleted file mode 100644
index bf9a2d955f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu
deleted file mode 100644
index 40e3d492e6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston
deleted file mode 100644
index 40e3d492e6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati
deleted file mode 100644
index 2f676d3bf5..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae
deleted file mode 100644
index f5d58242c8..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein
deleted file mode 100644
index 9416d522d0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro
deleted file mode 100644
index 9228ee02ed..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas
deleted file mode 100644
index 6ea24b72cd..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway
deleted file mode 100644
index 001289ceec..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru
deleted file mode 100644
index ae13aac779..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue
deleted file mode 100644
index 7b35793513..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
deleted file mode 100644
index 79e2a9419a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea
deleted file mode 100644
index 824f814160..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago
deleted file mode 100644
index 001289ceec..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau
deleted file mode 100644
index bc8eb7a55b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn
deleted file mode 100644
index 8a4ba4d30a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei
deleted file mode 100644
index b92b254a9a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape
deleted file mode 100644
index b92b254a9a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby
deleted file mode 100644
index 5d8fc3a1b2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga
deleted file mode 100644
index 143a1883b0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan
deleted file mode 100644
index bf9a2d955f..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa
deleted file mode 100644
index 001289ceec..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti
deleted file mode 100644
index 50a064fa01..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa
deleted file mode 100644
index 6bc216823e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu
deleted file mode 100644
index 54aeb0ffa2..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk
deleted file mode 100644
index ea3fb5cd3c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake
deleted file mode 100644
index 71cca8877d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis
deleted file mode 100644
index 4bce893003..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap
deleted file mode 100644
index ea3fb5cd3c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Poland b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Poland
deleted file mode 100644
index efe1a40f2a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Poland
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Portugal b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Portugal
deleted file mode 100644
index 64841661a0..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Portugal
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/ROC b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/ROC
deleted file mode 100644
index 35d89d036d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/ROC
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/ROK b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/ROK
deleted file mode 100644
index 1755147fab..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/ROK
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Singapore b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Singapore
deleted file mode 100644
index 350d77e28e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Singapore
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Turkey b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Turkey
deleted file mode 100644
index c891866873..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Turkey
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/UCT b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/UCT
deleted file mode 100644
index 00841a6221..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/UCT
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska
deleted file mode 100644
index cdf0572be3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian
deleted file mode 100644
index b1497bda63..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona
deleted file mode 100644
index c2bd2f949b..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Central b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Central
deleted file mode 100644
index b016880653..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Central
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana
deleted file mode 100644
index 6b08d15bda..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern
deleted file mode 100644
index 2b6c2eea14..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii
deleted file mode 100644
index 40e3d492e6..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke
deleted file mode 100644
index b187d5f8c7..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan
deleted file mode 100644
index 6eb3ac46ec..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain
deleted file mode 100644
index 09e54e5c7c..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific
deleted file mode 100644
index aaf07787ad..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa
deleted file mode 100644
index 001289ceec..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/UTC b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/UTC
deleted file mode 100644
index 00841a6221..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/UTC
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Universal b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Universal
deleted file mode 100644
index 00841a6221..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Universal
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/W-SU b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/W-SU
deleted file mode 100644
index 5e6b6de645..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/W-SU
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/WET b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/WET
deleted file mode 100644
index 423c6c203a..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/WET
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Zulu b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Zulu
deleted file mode 100644
index 00841a6221..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Zulu
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
deleted file mode 100644
index a4ff61a4d3..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
+++ /dev/null
@@ -1,274 +0,0 @@
-# ISO 3166 alpha-2 country codes
-#
-# This file is in the public domain, so clarified as of
-# 2009-05-17 by Arthur David Olson.
-#
-# From Paul Eggert (2015-05-02):
-# This file contains a table of two-letter country codes.  Columns are
-# separated by a single tab.  Lines beginning with '#' are comments.
-# All text uses UTF-8 encoding.  The columns of the table are as follows:
-#
-# 1.  ISO 3166-1 alpha-2 country code, current as of
-#     ISO 3166-1 N976 (2018-11-06).  See: Updates on ISO 3166-1
-#     https://isotc.iso.org/livelink/livelink/Open/16944257
-# 2.  The usual English name for the coded region,
-#     chosen so that alphabetic sorting of subsets produces helpful lists.
-#     This is not the same as the English name in the ISO 3166 tables.
-#
-# The table is sorted by country code.
-#
-# This table is intended as an aid for users, to help them select time
-# zone data appropriate for their practical needs.  It is not intended
-# to take or endorse any position on legal or territorial claims.
-#
-#country-
-#code	name of country, territory, area, or subdivision
-AD	Andorra
-AE	United Arab Emirates
-AF	Afghanistan
-AG	Antigua & Barbuda
-AI	Anguilla
-AL	Albania
-AM	Armenia
-AO	Angola
-AQ	Antarctica
-AR	Argentina
-AS	Samoa (American)
-AT	Austria
-AU	Australia
-AW	Aruba
-AX	Γ…land Islands
-AZ	Azerbaijan
-BA	Bosnia & Herzegovina
-BB	Barbados
-BD	Bangladesh
-BE	Belgium
-BF	Burkina Faso
-BG	Bulgaria
-BH	Bahrain
-BI	Burundi
-BJ	Benin
-BL	St Barthelemy
-BM	Bermuda
-BN	Brunei
-BO	Bolivia
-BQ	Caribbean NL
-BR	Brazil
-BS	Bahamas
-BT	Bhutan
-BV	Bouvet Island
-BW	Botswana
-BY	Belarus
-BZ	Belize
-CA	Canada
-CC	Cocos (Keeling) Islands
-CD	Congo (Dem. Rep.)
-CF	Central African Rep.
-CG	Congo (Rep.)
-CH	Switzerland
-CI	CΓ΄te d'Ivoire
-CK	Cook Islands
-CL	Chile
-CM	Cameroon
-CN	China
-CO	Colombia
-CR	Costa Rica
-CU	Cuba
-CV	Cape Verde
-CW	Curaçao
-CX	Christmas Island
-CY	Cyprus
-CZ	Czech Republic
-DE	Germany
-DJ	Djibouti
-DK	Denmark
-DM	Dominica
-DO	Dominican Republic
-DZ	Algeria
-EC	Ecuador
-EE	Estonia
-EG	Egypt
-EH	Western Sahara
-ER	Eritrea
-ES	Spain
-ET	Ethiopia
-FI	Finland
-FJ	Fiji
-FK	Falkland Islands
-FM	Micronesia
-FO	Faroe Islands
-FR	France
-GA	Gabon
-GB	Britain (UK)
-GD	Grenada
-GE	Georgia
-GF	French Guiana
-GG	Guernsey
-GH	Ghana
-GI	Gibraltar
-GL	Greenland
-GM	Gambia
-GN	Guinea
-GP	Guadeloupe
-GQ	Equatorial Guinea
-GR	Greece
-GS	South Georgia & the South Sandwich Islands
-GT	Guatemala
-GU	Guam
-GW	Guinea-Bissau
-GY	Guyana
-HK	Hong Kong
-HM	Heard Island & McDonald Islands
-HN	Honduras
-HR	Croatia
-HT	Haiti
-HU	Hungary
-ID	Indonesia
-IE	Ireland
-IL	Israel
-IM	Isle of Man
-IN	India
-IO	British Indian Ocean Territory
-IQ	Iraq
-IR	Iran
-IS	Iceland
-IT	Italy
-JE	Jersey
-JM	Jamaica
-JO	Jordan
-JP	Japan
-KE	Kenya
-KG	Kyrgyzstan
-KH	Cambodia
-KI	Kiribati
-KM	Comoros
-KN	St Kitts & Nevis
-KP	Korea (North)
-KR	Korea (South)
-KW	Kuwait
-KY	Cayman Islands
-KZ	Kazakhstan
-LA	Laos
-LB	Lebanon
-LC	St Lucia
-LI	Liechtenstein
-LK	Sri Lanka
-LR	Liberia
-LS	Lesotho
-LT	Lithuania
-LU	Luxembourg
-LV	Latvia
-LY	Libya
-MA	Morocco
-MC	Monaco
-MD	Moldova
-ME	Montenegro
-MF	St Martin (French)
-MG	Madagascar
-MH	Marshall Islands
-MK	North Macedonia
-ML	Mali
-MM	Myanmar (Burma)
-MN	Mongolia
-MO	Macau
-MP	Northern Mariana Islands
-MQ	Martinique
-MR	Mauritania
-MS	Montserrat
-MT	Malta
-MU	Mauritius
-MV	Maldives
-MW	Malawi
-MX	Mexico
-MY	Malaysia
-MZ	Mozambique
-NA	Namibia
-NC	New Caledonia
-NE	Niger
-NF	Norfolk Island
-NG	Nigeria
-NI	Nicaragua
-NL	Netherlands
-NO	Norway
-NP	Nepal
-NR	Nauru
-NU	Niue
-NZ	New Zealand
-OM	Oman
-PA	Panama
-PE	Peru
-PF	French Polynesia
-PG	Papua New Guinea
-PH	Philippines
-PK	Pakistan
-PL	Poland
-PM	St Pierre & Miquelon
-PN	Pitcairn
-PR	Puerto Rico
-PS	Palestine
-PT	Portugal
-PW	Palau
-PY	Paraguay
-QA	Qatar
-RE	RΓ©union
-RO	Romania
-RS	Serbia
-RU	Russia
-RW	Rwanda
-SA	Saudi Arabia
-SB	Solomon Islands
-SC	Seychelles
-SD	Sudan
-SE	Sweden
-SG	Singapore
-SH	St Helena
-SI	Slovenia
-SJ	Svalbard & Jan Mayen
-SK	Slovakia
-SL	Sierra Leone
-SM	San Marino
-SN	Senegal
-SO	Somalia
-SR	Suriname
-SS	South Sudan
-ST	Sao Tome & Principe
-SV	El Salvador
-SX	St Maarten (Dutch)
-SY	Syria
-SZ	Eswatini (Swaziland)
-TC	Turks & Caicos Is
-TD	Chad
-TF	French Southern & Antarctic Lands
-TG	Togo
-TH	Thailand
-TJ	Tajikistan
-TK	Tokelau
-TL	East Timor
-TM	Turkmenistan
-TN	Tunisia
-TO	Tonga
-TR	Turkey
-TT	Trinidad & Tobago
-TV	Tuvalu
-TW	Taiwan
-TZ	Tanzania
-UA	Ukraine
-UG	Uganda
-UM	US minor outlying islands
-US	United States
-UY	Uruguay
-UZ	Uzbekistan
-VA	Vatican City
-VC	St Vincent
-VE	Venezuela
-VG	Virgin Islands (UK)
-VI	Virgin Islands (US)
-VN	Vietnam
-VU	Vanuatu
-WF	Wallis & Futuna
-WS	Samoa (western)
-YE	Yemen
-YT	Mayotte
-ZA	South Africa
-ZM	Zambia
-ZW	Zimbabwe
diff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/localtime b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/localtime
deleted file mode 100644
index afeeb88d06..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/localtime
+++ /dev/null
Binary files differdiff --git a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
deleted file mode 100644
index 53ee77e88e..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
+++ /dev/null
@@ -1,384 +0,0 @@
-# tzdb timezone descriptions
-#
-# This file is in the public domain.
-#
-# From Paul Eggert (2018-06-27):
-# This file contains a table where each row stands for a timezone where
-# civil timestamps have agreed since 1970.  Columns are separated by
-# a single tab.  Lines beginning with '#' are comments.  All text uses
-# UTF-8 encoding.  The columns of the table are as follows:
-#
-# 1.  The countries that overlap the timezone, as a comma-separated list
-#     of ISO 3166 2-character country codes.  See the file 'iso3166.tab'.
-# 2.  Latitude and longitude of the timezone's principal location
-#     in ISO 6709 sign-degrees-minutes-seconds format,
-#     either Β±DDMMΒ±DDDMM or Β±DDMMSSΒ±DDDMMSS,
-#     first latitude (+ is north), then longitude (+ is east).
-# 3.  Timezone name used in value of TZ environment variable.
-#     Please see the theory.html file for how these names are chosen.
-#     If multiple timezones overlap a country, each has a row in the
-#     table, with each column 1 containing the country code.
-# 4.  Comments; present if and only if a country has multiple timezones.
-#
-# If a timezone covers multiple countries, the most-populous city is used,
-# and that country is listed first in column 1; any other countries
-# are listed alphabetically by country code.  The table is sorted
-# first by country code, then (if possible) by an order within the
-# country that (1) makes some geographical sense, and (2) puts the
-# most populous timezones first, where that does not contradict (1).
-#
-# This table is intended as an aid for users, to help them select timezones
-# appropriate for their practical needs.  It is not intended to take or
-# endorse any position on legal or territorial claims.
-#
-#country-
-#codes	coordinates	TZ	comments
-AD	+4230+00131	Europe/Andorra
-AE,OM	+2518+05518	Asia/Dubai
-AF	+3431+06912	Asia/Kabul
-AL	+4120+01950	Europe/Tirane
-AM	+4011+04430	Asia/Yerevan
-AQ	-6617+11031	Antarctica/Casey	Casey
-AQ	-6835+07758	Antarctica/Davis	Davis
-AQ	-6640+14001	Antarctica/DumontDUrville	Dumont-d'Urville
-AQ	-6736+06253	Antarctica/Mawson	Mawson
-AQ	-6448-06406	Antarctica/Palmer	Palmer
-AQ	-6734-06808	Antarctica/Rothera	Rothera
-AQ	-690022+0393524	Antarctica/Syowa	Syowa
-AQ	-720041+0023206	Antarctica/Troll	Troll
-AQ	-7824+10654	Antarctica/Vostok	Vostok
-AR	-3436-05827	America/Argentina/Buenos_Aires	Buenos Aires (BA, CF)
-AR	-3124-06411	America/Argentina/Cordoba	Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF)
-AR	-2447-06525	America/Argentina/Salta	Salta (SA, LP, NQ, RN)
-AR	-2411-06518	America/Argentina/Jujuy	Jujuy (JY)
-AR	-2649-06513	America/Argentina/Tucuman	TucumΓ‘n (TM)
-AR	-2828-06547	America/Argentina/Catamarca	Catamarca (CT); Chubut (CH)
-AR	-2926-06651	America/Argentina/La_Rioja	La Rioja (LR)
-AR	-3132-06831	America/Argentina/San_Juan	San Juan (SJ)
-AR	-3253-06849	America/Argentina/Mendoza	Mendoza (MZ)
-AR	-3319-06621	America/Argentina/San_Luis	San Luis (SL)
-AR	-5138-06913	America/Argentina/Rio_Gallegos	Santa Cruz (SC)
-AR	-5448-06818	America/Argentina/Ushuaia	Tierra del Fuego (TF)
-AS,UM	-1416-17042	Pacific/Pago_Pago	Samoa, Midway
-AT	+4813+01620	Europe/Vienna
-AU	-3133+15905	Australia/Lord_Howe	Lord Howe Island
-AU	-5430+15857	Antarctica/Macquarie	Macquarie Island
-AU	-4253+14719	Australia/Hobart	Tasmania (most areas)
-AU	-3956+14352	Australia/Currie	Tasmania (King Island)
-AU	-3749+14458	Australia/Melbourne	Victoria
-AU	-3352+15113	Australia/Sydney	New South Wales (most areas)
-AU	-3157+14127	Australia/Broken_Hill	New South Wales (Yancowinna)
-AU	-2728+15302	Australia/Brisbane	Queensland (most areas)
-AU	-2016+14900	Australia/Lindeman	Queensland (Whitsunday Islands)
-AU	-3455+13835	Australia/Adelaide	South Australia
-AU	-1228+13050	Australia/Darwin	Northern Territory
-AU	-3157+11551	Australia/Perth	Western Australia (most areas)
-AU	-3143+12852	Australia/Eucla	Western Australia (Eucla)
-AZ	+4023+04951	Asia/Baku
-BB	+1306-05937	America/Barbados
-BD	+2343+09025	Asia/Dhaka
-BE	+5050+00420	Europe/Brussels
-BG	+4241+02319	Europe/Sofia
-BM	+3217-06446	Atlantic/Bermuda
-BN	+0456+11455	Asia/Brunei
-BO	-1630-06809	America/La_Paz
-BR	-0351-03225	America/Noronha	Atlantic islands
-BR	-0127-04829	America/Belem	ParΓ‘ (east); AmapΓ‘
-BR	-0343-03830	America/Fortaleza	Brazil (northeast: MA, PI, CE, RN, PB)
-BR	-0803-03454	America/Recife	Pernambuco
-BR	-0712-04812	America/Araguaina	Tocantins
-BR	-0940-03543	America/Maceio	Alagoas, Sergipe
-BR	-1259-03831	America/Bahia	Bahia
-BR	-2332-04637	America/Sao_Paulo	Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS)
-BR	-2027-05437	America/Campo_Grande	Mato Grosso do Sul
-BR	-1535-05605	America/Cuiaba	Mato Grosso
-BR	-0226-05452	America/Santarem	ParΓ‘ (west)
-BR	-0846-06354	America/Porto_Velho	RondΓ΄nia
-BR	+0249-06040	America/Boa_Vista	Roraima
-BR	-0308-06001	America/Manaus	Amazonas (east)
-BR	-0640-06952	America/Eirunepe	Amazonas (west)
-BR	-0958-06748	America/Rio_Branco	Acre
-BS	+2505-07721	America/Nassau
-BT	+2728+08939	Asia/Thimphu
-BY	+5354+02734	Europe/Minsk
-BZ	+1730-08812	America/Belize
-CA	+4734-05243	America/St_Johns	Newfoundland; Labrador (southeast)
-CA	+4439-06336	America/Halifax	Atlantic - NS (most areas); PE
-CA	+4612-05957	America/Glace_Bay	Atlantic - NS (Cape Breton)
-CA	+4606-06447	America/Moncton	Atlantic - New Brunswick
-CA	+5320-06025	America/Goose_Bay	Atlantic - Labrador (most areas)
-CA	+5125-05707	America/Blanc-Sablon	AST - QC (Lower North Shore)
-CA	+4339-07923	America/Toronto	Eastern - ON, QC (most areas)
-CA	+4901-08816	America/Nipigon	Eastern - ON, QC (no DST 1967-73)
-CA	+4823-08915	America/Thunder_Bay	Eastern - ON (Thunder Bay)
-CA	+6344-06828	America/Iqaluit	Eastern - NU (most east areas)
-CA	+6608-06544	America/Pangnirtung	Eastern - NU (Pangnirtung)
-CA	+484531-0913718	America/Atikokan	EST - ON (Atikokan); NU (Coral H)
-CA	+4953-09709	America/Winnipeg	Central - ON (west); Manitoba
-CA	+4843-09434	America/Rainy_River	Central - ON (Rainy R, Ft Frances)
-CA	+744144-0944945	America/Resolute	Central - NU (Resolute)
-CA	+624900-0920459	America/Rankin_Inlet	Central - NU (central)
-CA	+5024-10439	America/Regina	CST - SK (most areas)
-CA	+5017-10750	America/Swift_Current	CST - SK (midwest)
-CA	+5333-11328	America/Edmonton	Mountain - AB; BC (E); SK (W)
-CA	+690650-1050310	America/Cambridge_Bay	Mountain - NU (west)
-CA	+6227-11421	America/Yellowknife	Mountain - NT (central)
-CA	+682059-1334300	America/Inuvik	Mountain - NT (west)
-CA	+4906-11631	America/Creston	MST - BC (Creston)
-CA	+5946-12014	America/Dawson_Creek	MST - BC (Dawson Cr, Ft St John)
-CA	+5848-12242	America/Fort_Nelson	MST - BC (Ft Nelson)
-CA	+4916-12307	America/Vancouver	Pacific - BC (most areas)
-CA	+6043-13503	America/Whitehorse	Pacific - Yukon (east)
-CA	+6404-13925	America/Dawson	Pacific - Yukon (west)
-CC	-1210+09655	Indian/Cocos
-CH,DE,LI	+4723+00832	Europe/Zurich	Swiss time
-CI,BF,GM,GN,ML,MR,SH,SL,SN,TG	+0519-00402	Africa/Abidjan
-CK	-2114-15946	Pacific/Rarotonga
-CL	-3327-07040	America/Santiago	Chile (most areas)
-CL	-5309-07055	America/Punta_Arenas	Region of Magallanes
-CL	-2709-10926	Pacific/Easter	Easter Island
-CN	+3114+12128	Asia/Shanghai	Beijing Time
-CN	+4348+08735	Asia/Urumqi	Xinjiang Time
-CO	+0436-07405	America/Bogota
-CR	+0956-08405	America/Costa_Rica
-CU	+2308-08222	America/Havana
-CV	+1455-02331	Atlantic/Cape_Verde
-CW,AW,BQ,SX	+1211-06900	America/Curacao
-CX	-1025+10543	Indian/Christmas
-CY	+3510+03322	Asia/Nicosia	Cyprus (most areas)
-CY	+3507+03357	Asia/Famagusta	Northern Cyprus
-CZ,SK	+5005+01426	Europe/Prague
-DE	+5230+01322	Europe/Berlin	Germany (most areas)
-DK	+5540+01235	Europe/Copenhagen
-DO	+1828-06954	America/Santo_Domingo
-DZ	+3647+00303	Africa/Algiers
-EC	-0210-07950	America/Guayaquil	Ecuador (mainland)
-EC	-0054-08936	Pacific/Galapagos	GalΓ‘pagos Islands
-EE	+5925+02445	Europe/Tallinn
-EG	+3003+03115	Africa/Cairo
-EH	+2709-01312	Africa/El_Aaiun
-ES	+4024-00341	Europe/Madrid	Spain (mainland)
-ES	+3553-00519	Africa/Ceuta	Ceuta, Melilla
-ES	+2806-01524	Atlantic/Canary	Canary Islands
-FI,AX	+6010+02458	Europe/Helsinki
-FJ	-1808+17825	Pacific/Fiji
-FK	-5142-05751	Atlantic/Stanley
-FM	+0725+15147	Pacific/Chuuk	Chuuk/Truk, Yap
-FM	+0658+15813	Pacific/Pohnpei	Pohnpei/Ponape
-FM	+0519+16259	Pacific/Kosrae	Kosrae
-FO	+6201-00646	Atlantic/Faroe
-FR	+4852+00220	Europe/Paris
-GB,GG,IM,JE	+513030-0000731	Europe/London
-GE	+4143+04449	Asia/Tbilisi
-GF	+0456-05220	America/Cayenne
-GH	+0533-00013	Africa/Accra
-GI	+3608-00521	Europe/Gibraltar
-GL	+6411-05144	America/Nuuk	Greenland (most areas)
-GL	+7646-01840	America/Danmarkshavn	National Park (east coast)
-GL	+7029-02158	America/Scoresbysund	Scoresbysund/Ittoqqortoormiit
-GL	+7634-06847	America/Thule	Thule/Pituffik
-GR	+3758+02343	Europe/Athens
-GS	-5416-03632	Atlantic/South_Georgia
-GT	+1438-09031	America/Guatemala
-GU,MP	+1328+14445	Pacific/Guam
-GW	+1151-01535	Africa/Bissau
-GY	+0648-05810	America/Guyana
-HK	+2217+11409	Asia/Hong_Kong
-HN	+1406-08713	America/Tegucigalpa
-HT	+1832-07220	America/Port-au-Prince
-HU	+4730+01905	Europe/Budapest
-ID	-0610+10648	Asia/Jakarta	Java, Sumatra
-ID	-0002+10920	Asia/Pontianak	Borneo (west, central)
-ID	-0507+11924	Asia/Makassar	Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west)
-ID	-0232+14042	Asia/Jayapura	New Guinea (West Papua / Irian Jaya); Malukus/Moluccas
-IE	+5320-00615	Europe/Dublin
-IL	+314650+0351326	Asia/Jerusalem
-IN	+2232+08822	Asia/Kolkata
-IO	-0720+07225	Indian/Chagos
-IQ	+3321+04425	Asia/Baghdad
-IR	+3540+05126	Asia/Tehran
-IS	+6409-02151	Atlantic/Reykjavik
-IT,SM,VA	+4154+01229	Europe/Rome
-JM	+175805-0764736	America/Jamaica
-JO	+3157+03556	Asia/Amman
-JP	+353916+1394441	Asia/Tokyo
-KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT	-0117+03649	Africa/Nairobi
-KG	+4254+07436	Asia/Bishkek
-KI	+0125+17300	Pacific/Tarawa	Gilbert Islands
-KI	-0308-17105	Pacific/Enderbury	Phoenix Islands
-KI	+0152-15720	Pacific/Kiritimati	Line Islands
-KP	+3901+12545	Asia/Pyongyang
-KR	+3733+12658	Asia/Seoul
-KZ	+4315+07657	Asia/Almaty	Kazakhstan (most areas)
-KZ	+4448+06528	Asia/Qyzylorda	Qyzylorda/Kyzylorda/Kzyl-Orda
-KZ	+5312+06337	Asia/Qostanay	Qostanay/Kostanay/Kustanay
-KZ	+5017+05710	Asia/Aqtobe	AqtΓΆbe/Aktobe
-KZ	+4431+05016	Asia/Aqtau	MangghystaΕ«/Mankistau
-KZ	+4707+05156	Asia/Atyrau	AtyraΕ«/Atirau/Gur'yev
-KZ	+5113+05121	Asia/Oral	West Kazakhstan
-LB	+3353+03530	Asia/Beirut
-LK	+0656+07951	Asia/Colombo
-LR	+0618-01047	Africa/Monrovia
-LT	+5441+02519	Europe/Vilnius
-LU	+4936+00609	Europe/Luxembourg
-LV	+5657+02406	Europe/Riga
-LY	+3254+01311	Africa/Tripoli
-MA	+3339-00735	Africa/Casablanca
-MC	+4342+00723	Europe/Monaco
-MD	+4700+02850	Europe/Chisinau
-MH	+0709+17112	Pacific/Majuro	Marshall Islands (most areas)
-MH	+0905+16720	Pacific/Kwajalein	Kwajalein
-MM	+1647+09610	Asia/Yangon
-MN	+4755+10653	Asia/Ulaanbaatar	Mongolia (most areas)
-MN	+4801+09139	Asia/Hovd	Bayan-Γ–lgii, Govi-Altai, Hovd, Uvs, Zavkhan
-MN	+4804+11430	Asia/Choibalsan	Dornod, SΓΌkhbaatar
-MO	+221150+1133230	Asia/Macau
-MQ	+1436-06105	America/Martinique
-MT	+3554+01431	Europe/Malta
-MU	-2010+05730	Indian/Mauritius
-MV	+0410+07330	Indian/Maldives
-MX	+1924-09909	America/Mexico_City	Central Time
-MX	+2105-08646	America/Cancun	Eastern Standard Time - Quintana Roo
-MX	+2058-08937	America/Merida	Central Time - Campeche, YucatΓ‘n
-MX	+2540-10019	America/Monterrey	Central Time - Durango; Coahuila, Nuevo LeΓ³n, Tamaulipas (most areas)
-MX	+2550-09730	America/Matamoros	Central Time US - Coahuila, Nuevo LeΓ³n, Tamaulipas (US border)
-MX	+2313-10625	America/Mazatlan	Mountain Time - Baja California Sur, Nayarit, Sinaloa
-MX	+2838-10605	America/Chihuahua	Mountain Time - Chihuahua (most areas)
-MX	+2934-10425	America/Ojinaga	Mountain Time US - Chihuahua (US border)
-MX	+2904-11058	America/Hermosillo	Mountain Standard Time - Sonora
-MX	+3232-11701	America/Tijuana	Pacific Time US - Baja California
-MX	+2048-10515	America/Bahia_Banderas	Central Time - BahΓ­a de Banderas
-MY	+0310+10142	Asia/Kuala_Lumpur	Malaysia (peninsula)
-MY	+0133+11020	Asia/Kuching	Sabah, Sarawak
-MZ,BI,BW,CD,MW,RW,ZM,ZW	-2558+03235	Africa/Maputo	Central Africa Time
-NA	-2234+01706	Africa/Windhoek
-NC	-2216+16627	Pacific/Noumea
-NF	-2903+16758	Pacific/Norfolk
-NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE	+0627+00324	Africa/Lagos	West Africa Time
-NI	+1209-08617	America/Managua
-NL	+5222+00454	Europe/Amsterdam
-NO,SJ	+5955+01045	Europe/Oslo
-NP	+2743+08519	Asia/Kathmandu
-NR	-0031+16655	Pacific/Nauru
-NU	-1901-16955	Pacific/Niue
-NZ,AQ	-3652+17446	Pacific/Auckland	New Zealand time
-NZ	-4357-17633	Pacific/Chatham	Chatham Islands
-PA,KY	+0858-07932	America/Panama
-PE	-1203-07703	America/Lima
-PF	-1732-14934	Pacific/Tahiti	Society Islands
-PF	-0900-13930	Pacific/Marquesas	Marquesas Islands
-PF	-2308-13457	Pacific/Gambier	Gambier Islands
-PG	-0930+14710	Pacific/Port_Moresby	Papua New Guinea (most areas)
-PG	-0613+15534	Pacific/Bougainville	Bougainville
-PH	+1435+12100	Asia/Manila
-PK	+2452+06703	Asia/Karachi
-PL	+5215+02100	Europe/Warsaw
-PM	+4703-05620	America/Miquelon
-PN	-2504-13005	Pacific/Pitcairn
-PR	+182806-0660622	America/Puerto_Rico
-PS	+3130+03428	Asia/Gaza	Gaza Strip
-PS	+313200+0350542	Asia/Hebron	West Bank
-PT	+3843-00908	Europe/Lisbon	Portugal (mainland)
-PT	+3238-01654	Atlantic/Madeira	Madeira Islands
-PT	+3744-02540	Atlantic/Azores	Azores
-PW	+0720+13429	Pacific/Palau
-PY	-2516-05740	America/Asuncion
-QA,BH	+2517+05132	Asia/Qatar
-RE,TF	-2052+05528	Indian/Reunion	RΓ©union, Crozet, Scattered Islands
-RO	+4426+02606	Europe/Bucharest
-RS,BA,HR,ME,MK,SI	+4450+02030	Europe/Belgrade
-RU	+5443+02030	Europe/Kaliningrad	MSK-01 - Kaliningrad
-RU	+554521+0373704	Europe/Moscow	MSK+00 - Moscow area
-# Mention RU and UA alphabetically.  See "territorial claims" above.
-RU,UA	+4457+03406	Europe/Simferopol	Crimea
-RU	+5836+04939	Europe/Kirov	MSK+00 - Kirov
-RU	+4621+04803	Europe/Astrakhan	MSK+01 - Astrakhan
-RU	+4844+04425	Europe/Volgograd	MSK+01 - Volgograd
-RU	+5134+04602	Europe/Saratov	MSK+01 - Saratov
-RU	+5420+04824	Europe/Ulyanovsk	MSK+01 - Ulyanovsk
-RU	+5312+05009	Europe/Samara	MSK+01 - Samara, Udmurtia
-RU	+5651+06036	Asia/Yekaterinburg	MSK+02 - Urals
-RU	+5500+07324	Asia/Omsk	MSK+03 - Omsk
-RU	+5502+08255	Asia/Novosibirsk	MSK+04 - Novosibirsk
-RU	+5322+08345	Asia/Barnaul	MSK+04 - Altai
-RU	+5630+08458	Asia/Tomsk	MSK+04 - Tomsk
-RU	+5345+08707	Asia/Novokuznetsk	MSK+04 - Kemerovo
-RU	+5601+09250	Asia/Krasnoyarsk	MSK+04 - Krasnoyarsk area
-RU	+5216+10420	Asia/Irkutsk	MSK+05 - Irkutsk, Buryatia
-RU	+5203+11328	Asia/Chita	MSK+06 - Zabaykalsky
-RU	+6200+12940	Asia/Yakutsk	MSK+06 - Lena River
-RU	+623923+1353314	Asia/Khandyga	MSK+06 - Tomponsky, Ust-Maysky
-RU	+4310+13156	Asia/Vladivostok	MSK+07 - Amur River
-RU	+643337+1431336	Asia/Ust-Nera	MSK+07 - Oymyakonsky
-RU	+5934+15048	Asia/Magadan	MSK+08 - Magadan
-RU	+4658+14242	Asia/Sakhalin	MSK+08 - Sakhalin Island
-RU	+6728+15343	Asia/Srednekolymsk	MSK+08 - Sakha (E); North Kuril Is
-RU	+5301+15839	Asia/Kamchatka	MSK+09 - Kamchatka
-RU	+6445+17729	Asia/Anadyr	MSK+09 - Bering Sea
-SA,KW,YE	+2438+04643	Asia/Riyadh
-SB	-0932+16012	Pacific/Guadalcanal
-SC	-0440+05528	Indian/Mahe
-SD	+1536+03232	Africa/Khartoum
-SE	+5920+01803	Europe/Stockholm
-SG	+0117+10351	Asia/Singapore
-SR	+0550-05510	America/Paramaribo
-SS	+0451+03137	Africa/Juba
-ST	+0020+00644	Africa/Sao_Tome
-SV	+1342-08912	America/El_Salvador
-SY	+3330+03618	Asia/Damascus
-TC	+2128-07108	America/Grand_Turk
-TD	+1207+01503	Africa/Ndjamena
-TF	-492110+0701303	Indian/Kerguelen	Kerguelen, St Paul Island, Amsterdam Island
-TH,KH,LA,VN	+1345+10031	Asia/Bangkok	Indochina (most areas)
-TJ	+3835+06848	Asia/Dushanbe
-TK	-0922-17114	Pacific/Fakaofo
-TL	-0833+12535	Asia/Dili
-TM	+3757+05823	Asia/Ashgabat
-TN	+3648+01011	Africa/Tunis
-TO	-2110-17510	Pacific/Tongatapu
-TR	+4101+02858	Europe/Istanbul
-TT,AG,AI,BL,DM,GD,GP,KN,LC,MF,MS,VC,VG,VI	+1039-06131	America/Port_of_Spain
-TV	-0831+17913	Pacific/Funafuti
-TW	+2503+12130	Asia/Taipei
-UA	+5026+03031	Europe/Kiev	Ukraine (most areas)
-UA	+4837+02218	Europe/Uzhgorod	Transcarpathia
-UA	+4750+03510	Europe/Zaporozhye	Zaporozhye and east Lugansk
-UM	+1917+16637	Pacific/Wake	Wake Island
-US	+404251-0740023	America/New_York	Eastern (most areas)
-US	+421953-0830245	America/Detroit	Eastern - MI (most areas)
-US	+381515-0854534	America/Kentucky/Louisville	Eastern - KY (Louisville area)
-US	+364947-0845057	America/Kentucky/Monticello	Eastern - KY (Wayne)
-US	+394606-0860929	America/Indiana/Indianapolis	Eastern - IN (most areas)
-US	+384038-0873143	America/Indiana/Vincennes	Eastern - IN (Da, Du, K, Mn)
-US	+410305-0863611	America/Indiana/Winamac	Eastern - IN (Pulaski)
-US	+382232-0862041	America/Indiana/Marengo	Eastern - IN (Crawford)
-US	+382931-0871643	America/Indiana/Petersburg	Eastern - IN (Pike)
-US	+384452-0850402	America/Indiana/Vevay	Eastern - IN (Switzerland)
-US	+415100-0873900	America/Chicago	Central (most areas)
-US	+375711-0864541	America/Indiana/Tell_City	Central - IN (Perry)
-US	+411745-0863730	America/Indiana/Knox	Central - IN (Starke)
-US	+450628-0873651	America/Menominee	Central - MI (Wisconsin border)
-US	+470659-1011757	America/North_Dakota/Center	Central - ND (Oliver)
-US	+465042-1012439	America/North_Dakota/New_Salem	Central - ND (Morton rural)
-US	+471551-1014640	America/North_Dakota/Beulah	Central - ND (Mercer)
-US	+394421-1045903	America/Denver	Mountain (most areas)
-US	+433649-1161209	America/Boise	Mountain - ID (south); OR (east)
-US	+332654-1120424	America/Phoenix	MST - Arizona (except Navajo)
-US	+340308-1181434	America/Los_Angeles	Pacific
-US	+611305-1495401	America/Anchorage	Alaska (most areas)
-US	+581807-1342511	America/Juneau	Alaska - Juneau area
-US	+571035-1351807	America/Sitka	Alaska - Sitka area
-US	+550737-1313435	America/Metlakatla	Alaska - Annette Island
-US	+593249-1394338	America/Yakutat	Alaska - Yakutat
-US	+643004-1652423	America/Nome	Alaska (west)
-US	+515248-1763929	America/Adak	Aleutian Islands
-US,UM	+211825-1575130	Pacific/Honolulu	Hawaii
-UY	-345433-0561245	America/Montevideo
-UZ	+3940+06648	Asia/Samarkand	Uzbekistan (west)
-UZ	+4120+06918	Asia/Tashkent	Uzbekistan (east)
-VE	+1030-06656	America/Caracas
-VN	+1045+10640	Asia/Ho_Chi_Minh	Vietnam (south)
-VU	-1740+16825	Pacific/Efate
-WF	-1318-17610	Pacific/Wallis
-WS	-1350-17144	Pacific/Apia
-ZA,LS,SZ	-2615+02800	Africa/Johannesburg
diff --git a/third_party/abseil_cpp/absl/time/internal/get_current_time_chrono.inc b/third_party/abseil_cpp/absl/time/internal/get_current_time_chrono.inc
deleted file mode 100644
index 5eeb6406aa..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/get_current_time_chrono.inc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <chrono>
-#include <cstdint>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-
-static int64_t GetCurrentTimeNanosFromSystem() {
-  return std::chrono::duration_cast<std::chrono::nanoseconds>(
-             std::chrono::system_clock::now() -
-             std::chrono::system_clock::from_time_t(0))
-      .count();
-}
-
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/get_current_time_posix.inc b/third_party/abseil_cpp/absl/time/internal/get_current_time_posix.inc
deleted file mode 100644
index 42072000ae..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/get_current_time_posix.inc
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "absl/time/clock.h"
-
-#include <sys/time.h>
-#include <ctime>
-#include <cstdint>
-
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-
-static int64_t GetCurrentTimeNanosFromSystem() {
-  const int64_t kNanosPerSecond = 1000 * 1000 * 1000;
-  struct timespec ts;
-  ABSL_RAW_CHECK(clock_gettime(CLOCK_REALTIME, &ts) == 0,
-                 "Failed to read real-time clock.");
-  return (int64_t{ts.tv_sec} * kNanosPerSecond +
-          int64_t{ts.tv_nsec});
-}
-
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/test_util.cc b/third_party/abseil_cpp/absl/time/internal/test_util.cc
deleted file mode 100644
index 9a485a0750..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/test_util.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/internal/test_util.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <cstring>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
-
-namespace cctz = absl::time_internal::cctz;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-
-TimeZone LoadTimeZone(const std::string& name) {
-  TimeZone tz;
-  ABSL_RAW_CHECK(LoadTimeZone(name, &tz), name.c_str());
-  return tz;
-}
-
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-namespace cctz_extension {
-namespace {
-
-// Embed the zoneinfo data for time zones used during tests and benchmarks.
-// The data was generated using "xxd -i zoneinfo-file".  There is no need
-// to update the data as long as the tests do not depend on recent changes
-// (and the past rules remain the same).
-#include "absl/time/internal/zoneinfo.inc"
-
-const struct ZoneInfo {
-  const char* name;
-  const char* data;
-  std::size_t length;
-} kZoneInfo[] = {
-    // The three real time zones used by :time_test and :time_benchmark.
-    {"America/Los_Angeles",  //
-     reinterpret_cast<char*>(America_Los_Angeles), America_Los_Angeles_len},
-    {"America/New_York",  //
-     reinterpret_cast<char*>(America_New_York), America_New_York_len},
-    {"Australia/Sydney",  //
-     reinterpret_cast<char*>(Australia_Sydney), Australia_Sydney_len},
-
-    // Other zones named in tests but which should fail to load.
-    {"Invalid/TimeZone", nullptr, 0},
-    {"", nullptr, 0},
-
-    // Also allow for loading the local time zone under TZ=US/Pacific.
-    {"US/Pacific",  //
-     reinterpret_cast<char*>(America_Los_Angeles), America_Los_Angeles_len},
-
-    // Allows use of the local time zone from a system-specific location.
-#ifdef _MSC_VER
-    {"localtime",  //
-     reinterpret_cast<char*>(America_Los_Angeles), America_Los_Angeles_len},
-#else
-    {"/etc/localtime",  //
-     reinterpret_cast<char*>(America_Los_Angeles), America_Los_Angeles_len},
-#endif
-};
-
-class TestZoneInfoSource : public cctz::ZoneInfoSource {
- public:
-  TestZoneInfoSource(const char* data, std::size_t size)
-      : data_(data), end_(data + size) {}
-
-  std::size_t Read(void* ptr, std::size_t size) override {
-    const std::size_t len = std::min<std::size_t>(size, end_ - data_);
-    memcpy(ptr, data_, len);
-    data_ += len;
-    return len;
-  }
-
-  int Skip(std::size_t offset) override {
-    data_ += std::min<std::size_t>(offset, end_ - data_);
-    return 0;
-  }
-
- private:
-  const char* data_;
-  const char* const end_;
-};
-
-std::unique_ptr<cctz::ZoneInfoSource> TestFactory(
-    const std::string& name,
-    const std::function<std::unique_ptr<cctz::ZoneInfoSource>(
-        const std::string& name)>& /*fallback_factory*/) {
-  for (const ZoneInfo& zoneinfo : kZoneInfo) {
-    if (name == zoneinfo.name) {
-      if (zoneinfo.data == nullptr) return nullptr;
-      return std::unique_ptr<cctz::ZoneInfoSource>(
-          new TestZoneInfoSource(zoneinfo.data, zoneinfo.length));
-    }
-  }
-  ABSL_RAW_LOG(FATAL, "Unexpected time zone \"%s\" in test", name.c_str());
-  return nullptr;
-}
-
-}  // namespace
-
-#if !defined(__MINGW32__)
-// MinGW does not support the weak symbol extension mechanism.
-ZoneInfoSourceFactory zone_info_source_factory = TestFactory;
-#endif
-
-}  // namespace cctz_extension
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/internal/test_util.h b/third_party/abseil_cpp/absl/time/internal/test_util.h
deleted file mode 100644
index 5c4bf1f680..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/test_util.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_TIME_INTERNAL_TEST_UTIL_H_
-#define ABSL_TIME_INTERNAL_TEST_UTIL_H_
-
-#include <string>
-
-#include "absl/time/time.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace time_internal {
-
-// Loads the named timezone, but dies on any failure.
-absl::TimeZone LoadTimeZone(const std::string& name);
-
-}  // namespace time_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_INTERNAL_TEST_UTIL_H_
diff --git a/third_party/abseil_cpp/absl/time/internal/zoneinfo.inc b/third_party/abseil_cpp/absl/time/internal/zoneinfo.inc
deleted file mode 100644
index bfed82990d..0000000000
--- a/third_party/abseil_cpp/absl/time/internal/zoneinfo.inc
+++ /dev/null
@@ -1,729 +0,0 @@
-unsigned char America_Los_Angeles[] = {
-  0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00,
-  0x9e, 0xa6, 0x48, 0xa0, 0x9f, 0xbb, 0x15, 0x90, 0xa0, 0x86, 0x2a, 0xa0,
-  0xa1, 0x9a, 0xf7, 0x90, 0xcb, 0x89, 0x1a, 0xa0, 0xd2, 0x23, 0xf4, 0x70,
-  0xd2, 0x61, 0x26, 0x10, 0xd6, 0xfe, 0x74, 0x5c, 0xd8, 0x80, 0xad, 0x90,
-  0xda, 0xfe, 0xc3, 0x90, 0xdb, 0xc0, 0x90, 0x10, 0xdc, 0xde, 0xa5, 0x90,
-  0xdd, 0xa9, 0xac, 0x90, 0xde, 0xbe, 0x87, 0x90, 0xdf, 0x89, 0x8e, 0x90,
-  0xe0, 0x9e, 0x69, 0x90, 0xe1, 0x69, 0x70, 0x90, 0xe2, 0x7e, 0x4b, 0x90,
-  0xe3, 0x49, 0x52, 0x90, 0xe4, 0x5e, 0x2d, 0x90, 0xe5, 0x29, 0x34, 0x90,
-  0xe6, 0x47, 0x4a, 0x10, 0xe7, 0x12, 0x51, 0x10, 0xe8, 0x27, 0x2c, 0x10,
-  0xe8, 0xf2, 0x33, 0x10, 0xea, 0x07, 0x0e, 0x10, 0xea, 0xd2, 0x15, 0x10,
-  0xeb, 0xe6, 0xf0, 0x10, 0xec, 0xb1, 0xf7, 0x10, 0xed, 0xc6, 0xd2, 0x10,
-  0xee, 0x91, 0xd9, 0x10, 0xef, 0xaf, 0xee, 0x90, 0xf0, 0x71, 0xbb, 0x10,
-  0xf1, 0x8f, 0xd0, 0x90, 0xf2, 0x7f, 0xc1, 0x90, 0xf3, 0x6f, 0xb2, 0x90,
-  0xf4, 0x5f, 0xa3, 0x90, 0xf5, 0x4f, 0x94, 0x90, 0xf6, 0x3f, 0x85, 0x90,
-  0xf7, 0x2f, 0x76, 0x90, 0xf8, 0x28, 0xa2, 0x10, 0xf9, 0x0f, 0x58, 0x90,
-  0xfa, 0x08, 0x84, 0x10, 0xfa, 0xf8, 0x83, 0x20, 0xfb, 0xe8, 0x66, 0x10,
-  0xfc, 0xd8, 0x65, 0x20, 0xfd, 0xc8, 0x48, 0x10, 0xfe, 0xb8, 0x47, 0x20,
-  0xff, 0xa8, 0x2a, 0x10, 0x00, 0x98, 0x29, 0x20, 0x01, 0x88, 0x0c, 0x10,
-  0x02, 0x78, 0x0b, 0x20, 0x03, 0x71, 0x28, 0x90, 0x04, 0x61, 0x27, 0xa0,
-  0x05, 0x51, 0x0a, 0x90, 0x06, 0x41, 0x09, 0xa0, 0x07, 0x30, 0xec, 0x90,
-  0x07, 0x8d, 0x43, 0xa0, 0x09, 0x10, 0xce, 0x90, 0x09, 0xad, 0xbf, 0x20,
-  0x0a, 0xf0, 0xb0, 0x90, 0x0b, 0xe0, 0xaf, 0xa0, 0x0c, 0xd9, 0xcd, 0x10,
-  0x0d, 0xc0, 0x91, 0xa0, 0x0e, 0xb9, 0xaf, 0x10, 0x0f, 0xa9, 0xae, 0x20,
-  0x10, 0x99, 0x91, 0x10, 0x11, 0x89, 0x90, 0x20, 0x12, 0x79, 0x73, 0x10,
-  0x13, 0x69, 0x72, 0x20, 0x14, 0x59, 0x55, 0x10, 0x15, 0x49, 0x54, 0x20,
-  0x16, 0x39, 0x37, 0x10, 0x17, 0x29, 0x36, 0x20, 0x18, 0x22, 0x53, 0x90,
-  0x19, 0x09, 0x18, 0x20, 0x1a, 0x02, 0x35, 0x90, 0x1a, 0xf2, 0x34, 0xa0,
-  0x1b, 0xe2, 0x17, 0x90, 0x1c, 0xd2, 0x16, 0xa0, 0x1d, 0xc1, 0xf9, 0x90,
-  0x1e, 0xb1, 0xf8, 0xa0, 0x1f, 0xa1, 0xdb, 0x90, 0x20, 0x76, 0x2b, 0x20,
-  0x21, 0x81, 0xbd, 0x90, 0x22, 0x56, 0x0d, 0x20, 0x23, 0x6a, 0xda, 0x10,
-  0x24, 0x35, 0xef, 0x20, 0x25, 0x4a, 0xbc, 0x10, 0x26, 0x15, 0xd1, 0x20,
-  0x27, 0x2a, 0x9e, 0x10, 0x27, 0xfe, 0xed, 0xa0, 0x29, 0x0a, 0x80, 0x10,
-  0x29, 0xde, 0xcf, 0xa0, 0x2a, 0xea, 0x62, 0x10, 0x2b, 0xbe, 0xb1, 0xa0,
-  0x2c, 0xd3, 0x7e, 0x90, 0x2d, 0x9e, 0x93, 0xa0, 0x2e, 0xb3, 0x60, 0x90,
-  0x2f, 0x7e, 0x75, 0xa0, 0x30, 0x93, 0x42, 0x90, 0x31, 0x67, 0x92, 0x20,
-  0x32, 0x73, 0x24, 0x90, 0x33, 0x47, 0x74, 0x20, 0x34, 0x53, 0x06, 0x90,
-  0x35, 0x27, 0x56, 0x20, 0x36, 0x32, 0xe8, 0x90, 0x37, 0x07, 0x38, 0x20,
-  0x38, 0x1c, 0x05, 0x10, 0x38, 0xe7, 0x1a, 0x20, 0x39, 0xfb, 0xe7, 0x10,
-  0x3a, 0xc6, 0xfc, 0x20, 0x3b, 0xdb, 0xc9, 0x10, 0x3c, 0xb0, 0x18, 0xa0,
-  0x3d, 0xbb, 0xab, 0x10, 0x3e, 0x8f, 0xfa, 0xa0, 0x3f, 0x9b, 0x8d, 0x10,
-  0x40, 0x6f, 0xdc, 0xa0, 0x41, 0x84, 0xa9, 0x90, 0x42, 0x4f, 0xbe, 0xa0,
-  0x43, 0x64, 0x8b, 0x90, 0x44, 0x2f, 0xa0, 0xa0, 0x45, 0x44, 0x6d, 0x90,
-  0x45, 0xf3, 0xd3, 0x20, 0x47, 0x2d, 0x8a, 0x10, 0x47, 0xd3, 0xb5, 0x20,
-  0x49, 0x0d, 0x6c, 0x10, 0x49, 0xb3, 0x97, 0x20, 0x4a, 0xed, 0x4e, 0x10,
-  0x4b, 0x9c, 0xb3, 0xa0, 0x4c, 0xd6, 0x6a, 0x90, 0x4d, 0x7c, 0x95, 0xa0,
-  0x4e, 0xb6, 0x4c, 0x90, 0x4f, 0x5c, 0x77, 0xa0, 0x50, 0x96, 0x2e, 0x90,
-  0x51, 0x3c, 0x59, 0xa0, 0x52, 0x76, 0x10, 0x90, 0x53, 0x1c, 0x3b, 0xa0,
-  0x54, 0x55, 0xf2, 0x90, 0x54, 0xfc, 0x1d, 0xa0, 0x56, 0x35, 0xd4, 0x90,
-  0x56, 0xe5, 0x3a, 0x20, 0x58, 0x1e, 0xf1, 0x10, 0x58, 0xc5, 0x1c, 0x20,
-  0x59, 0xfe, 0xd3, 0x10, 0x5a, 0xa4, 0xfe, 0x20, 0x5b, 0xde, 0xb5, 0x10,
-  0x5c, 0x84, 0xe0, 0x20, 0x5d, 0xbe, 0x97, 0x10, 0x5e, 0x64, 0xc2, 0x20,
-  0x5f, 0x9e, 0x79, 0x10, 0x60, 0x4d, 0xde, 0xa0, 0x61, 0x87, 0x95, 0x90,
-  0x62, 0x2d, 0xc0, 0xa0, 0x63, 0x67, 0x77, 0x90, 0x64, 0x0d, 0xa2, 0xa0,
-  0x65, 0x47, 0x59, 0x90, 0x65, 0xed, 0x84, 0xa0, 0x67, 0x27, 0x3b, 0x90,
-  0x67, 0xcd, 0x66, 0xa0, 0x69, 0x07, 0x1d, 0x90, 0x69, 0xad, 0x48, 0xa0,
-  0x6a, 0xe6, 0xff, 0x90, 0x6b, 0x96, 0x65, 0x20, 0x6c, 0xd0, 0x1c, 0x10,
-  0x6d, 0x76, 0x47, 0x20, 0x6e, 0xaf, 0xfe, 0x10, 0x6f, 0x56, 0x29, 0x20,
-  0x70, 0x8f, 0xe0, 0x10, 0x71, 0x36, 0x0b, 0x20, 0x72, 0x6f, 0xc2, 0x10,
-  0x73, 0x15, 0xed, 0x20, 0x74, 0x4f, 0xa4, 0x10, 0x74, 0xff, 0x09, 0xa0,
-  0x76, 0x38, 0xc0, 0x90, 0x76, 0xde, 0xeb, 0xa0, 0x78, 0x18, 0xa2, 0x90,
-  0x78, 0xbe, 0xcd, 0xa0, 0x79, 0xf8, 0x84, 0x90, 0x7a, 0x9e, 0xaf, 0xa0,
-  0x7b, 0xd8, 0x66, 0x90, 0x7c, 0x7e, 0x91, 0xa0, 0x7d, 0xb8, 0x48, 0x90,
-  0x7e, 0x5e, 0x73, 0xa0, 0x7f, 0x98, 0x2a, 0x90, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0xff, 0xff, 0x91, 0x26, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x90,
-  0x01, 0x04, 0xff, 0xff, 0x8f, 0x80, 0x00, 0x08, 0xff, 0xff, 0x9d, 0x90,
-  0x01, 0x0c, 0xff, 0xff, 0x9d, 0x90, 0x01, 0x10, 0x4c, 0x4d, 0x54, 0x00,
-  0x50, 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00,
-  0x50, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x00, 0x01, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0xbb, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xf8, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x04,
-  0x1a, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x9e, 0xa6, 0x48, 0xa0, 0xff, 0xff,
-  0xff, 0xff, 0x9f, 0xbb, 0x15, 0x90, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x86,
-  0x2a, 0xa0, 0xff, 0xff, 0xff, 0xff, 0xa1, 0x9a, 0xf7, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xcb, 0x89, 0x1a, 0xa0, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x23,
-  0xf4, 0x70, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x61, 0x26, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xd6, 0xfe, 0x74, 0x5c, 0xff, 0xff, 0xff, 0xff, 0xd8, 0x80,
-  0xad, 0x90, 0xff, 0xff, 0xff, 0xff, 0xda, 0xfe, 0xc3, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xdb, 0xc0, 0x90, 0x10, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xde,
-  0xa5, 0x90, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xa9, 0xac, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xde, 0xbe, 0x87, 0x90, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x89,
-  0x8e, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x9e, 0x69, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xe1, 0x69, 0x70, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe2, 0x7e,
-  0x4b, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x49, 0x52, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xe4, 0x5e, 0x2d, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe5, 0x29,
-  0x34, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x47, 0x4a, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xe7, 0x12, 0x51, 0x10, 0xff, 0xff, 0xff, 0xff, 0xe8, 0x27,
-  0x2c, 0x10, 0xff, 0xff, 0xff, 0xff, 0xe8, 0xf2, 0x33, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xea, 0x07, 0x0e, 0x10, 0xff, 0xff, 0xff, 0xff, 0xea, 0xd2,
-  0x15, 0x10, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe6, 0xf0, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xec, 0xb1, 0xf7, 0x10, 0xff, 0xff, 0xff, 0xff, 0xed, 0xc6,
-  0xd2, 0x10, 0xff, 0xff, 0xff, 0xff, 0xee, 0x91, 0xd9, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xef, 0xaf, 0xee, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x71,
-  0xbb, 0x10, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x8f, 0xd0, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xf2, 0x7f, 0xc1, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x6f,
-  0xb2, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x5f, 0xa3, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xf5, 0x4f, 0x94, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x3f,
-  0x85, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x2f, 0x76, 0x90, 0xff, 0xff,
-  0xff, 0xff, 0xf8, 0x28, 0xa2, 0x10, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x0f,
-  0x58, 0x90, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x08, 0x84, 0x10, 0xff, 0xff,
-  0xff, 0xff, 0xfa, 0xf8, 0x83, 0x20, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xe8,
-  0x66, 0x10, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xd8, 0x65, 0x20, 0xff, 0xff,
-  0xff, 0xff, 0xfd, 0xc8, 0x48, 0x10, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xb8,
-  0x47, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8, 0x2a, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x98, 0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88,
-  0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, 0x0b, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x03, 0x71, 0x28, 0x90, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61,
-  0x27, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x05, 0x51, 0x0a, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x06, 0x41, 0x09, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30,
-  0xec, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8d, 0x43, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x09, 0x10, 0xce, 0x90, 0x00, 0x00, 0x00, 0x00, 0x09, 0xad,
-  0xbf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0xb0, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x0b, 0xe0, 0xaf, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xd9,
-  0xcd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xc0, 0x91, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x0e, 0xb9, 0xaf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xa9,
-  0xae, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x91, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x11, 0x89, 0x90, 0x20, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79,
-  0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x72, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x14, 0x59, 0x55, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15, 0x49,
-  0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x37, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x17, 0x29, 0x36, 0x20, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22,
-  0x53, 0x90, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x18, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x1a, 0x02, 0x35, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1a, 0xf2,
-  0x34, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe2, 0x17, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x1c, 0xd2, 0x16, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xc1,
-  0xf9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xb1, 0xf8, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x1f, 0xa1, 0xdb, 0x90, 0x00, 0x00, 0x00, 0x00, 0x20, 0x76,
-  0x2b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0xbd, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x22, 0x56, 0x0d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6a,
-  0xda, 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xef, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x25, 0x4a, 0xbc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x15,
-  0xd1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2a, 0x9e, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x27, 0xfe, 0xed, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0a,
-  0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x29, 0xde, 0xcf, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x2a, 0xea, 0x62, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2b, 0xbe,
-  0xb1, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd3, 0x7e, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x2d, 0x9e, 0x93, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xb3,
-  0x60, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7e, 0x75, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x30, 0x93, 0x42, 0x90, 0x00, 0x00, 0x00, 0x00, 0x31, 0x67,
-  0x92, 0x20, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, 0x24, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x33, 0x47, 0x74, 0x20, 0x00, 0x00, 0x00, 0x00, 0x34, 0x53,
-  0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x56, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x36, 0x32, 0xe8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x37, 0x07,
-  0x38, 0x20, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x05, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x38, 0xe7, 0x1a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x39, 0xfb,
-  0xe7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xc6, 0xfc, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x3b, 0xdb, 0xc9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xb0,
-  0x18, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xbb, 0xab, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x3e, 0x8f, 0xfa, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x9b,
-  0x8d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6f, 0xdc, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x41, 0x84, 0xa9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4f,
-  0xbe, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x8b, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x44, 0x2f, 0xa0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44,
-  0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0xf3, 0xd3, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x47, 0x2d, 0x8a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0xd3,
-  0xb5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0d, 0x6c, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x49, 0xb3, 0x97, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xed,
-  0x4e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x9c, 0xb3, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x4c, 0xd6, 0x6a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x7c,
-  0x95, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xb6, 0x4c, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x4f, 0x5c, 0x77, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96,
-  0x2e, 0x90, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3c, 0x59, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, 0x00, 0x00, 0x00, 0x53, 0x1c,
-  0x3b, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xf2, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x54, 0xfc, 0x1d, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35,
-  0xd4, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0xe5, 0x3a, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x58, 0x1e, 0xf1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0xc5,
-  0x1c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x59, 0xfe, 0xd3, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x5a, 0xa4, 0xfe, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xde,
-  0xb5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x84, 0xe0, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x5d, 0xbe, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x64,
-  0xc2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9e, 0x79, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x60, 0x4d, 0xde, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87,
-  0x95, 0x90, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2d, 0xc0, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0d,
-  0xa2, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x59, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x65, 0xed, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27,
-  0x3b, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0xcd, 0x66, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x69, 0x07, 0x1d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0xad,
-  0x48, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xe6, 0xff, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x6b, 0x96, 0x65, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xd0,
-  0x1c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x76, 0x47, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x6e, 0xaf, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x56,
-  0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8f, 0xe0, 0x10, 0x00, 0x00,
-  0x00, 0x00, 0x71, 0x36, 0x0b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6f,
-  0xc2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, 0xed, 0x20, 0x00, 0x00,
-  0x00, 0x00, 0x74, 0x4f, 0xa4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x74, 0xff,
-  0x09, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, 0xc0, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x76, 0xde, 0xeb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18,
-  0xa2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x78, 0xbe, 0xcd, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x79, 0xf8, 0x84, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x9e,
-  0xaf, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xd8, 0x66, 0x90, 0x00, 0x00,
-  0x00, 0x00, 0x7c, 0x7e, 0x91, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x7d, 0xb8,
-  0x48, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x5e, 0x73, 0xa0, 0x00, 0x00,
-  0x00, 0x00, 0x7f, 0x98, 0x2a, 0x90, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0xff, 0xff, 0x91, 0x26, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x90, 0x01,
-  0x04, 0xff, 0xff, 0x8f, 0x80, 0x00, 0x08, 0xff, 0xff, 0x9d, 0x90, 0x01,
-  0x0c, 0xff, 0xff, 0x9d, 0x90, 0x01, 0x10, 0x4c, 0x4d, 0x54, 0x00, 0x50,
-  0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50,
-  0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-  0x01, 0x0a, 0x50, 0x53, 0x54, 0x38, 0x50, 0x44, 0x54, 0x2c, 0x4d, 0x33,
-  0x2e, 0x32, 0x2e, 0x30, 0x2c, 0x4d, 0x31, 0x31, 0x2e, 0x31, 0x2e, 0x30,
-  0x0a
-};
-unsigned int America_Los_Angeles_len = 2845;
-unsigned char America_New_York[] = {
-  0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00,
-  0x9e, 0xa6, 0x1e, 0x70, 0x9f, 0xba, 0xeb, 0x60, 0xa0, 0x86, 0x00, 0x70,
-  0xa1, 0x9a, 0xcd, 0x60, 0xa2, 0x65, 0xe2, 0x70, 0xa3, 0x83, 0xe9, 0xe0,
-  0xa4, 0x6a, 0xae, 0x70, 0xa5, 0x35, 0xa7, 0x60, 0xa6, 0x53, 0xca, 0xf0,
-  0xa7, 0x15, 0x89, 0x60, 0xa8, 0x33, 0xac, 0xf0, 0xa8, 0xfe, 0xa5, 0xe0,
-  0xaa, 0x13, 0x8e, 0xf0, 0xaa, 0xde, 0x87, 0xe0, 0xab, 0xf3, 0x70, 0xf0,
-  0xac, 0xbe, 0x69, 0xe0, 0xad, 0xd3, 0x52, 0xf0, 0xae, 0x9e, 0x4b, 0xe0,
-  0xaf, 0xb3, 0x34, 0xf0, 0xb0, 0x7e, 0x2d, 0xe0, 0xb1, 0x9c, 0x51, 0x70,
-  0xb2, 0x67, 0x4a, 0x60, 0xb3, 0x7c, 0x33, 0x70, 0xb4, 0x47, 0x2c, 0x60,
-  0xb5, 0x5c, 0x15, 0x70, 0xb6, 0x27, 0x0e, 0x60, 0xb7, 0x3b, 0xf7, 0x70,
-  0xb8, 0x06, 0xf0, 0x60, 0xb9, 0x1b, 0xd9, 0x70, 0xb9, 0xe6, 0xd2, 0x60,
-  0xbb, 0x04, 0xf5, 0xf0, 0xbb, 0xc6, 0xb4, 0x60, 0xbc, 0xe4, 0xd7, 0xf0,
-  0xbd, 0xaf, 0xd0, 0xe0, 0xbe, 0xc4, 0xb9, 0xf0, 0xbf, 0x8f, 0xb2, 0xe0,
-  0xc0, 0xa4, 0x9b, 0xf0, 0xc1, 0x6f, 0x94, 0xe0, 0xc2, 0x84, 0x7d, 0xf0,
-  0xc3, 0x4f, 0x76, 0xe0, 0xc4, 0x64, 0x5f, 0xf0, 0xc5, 0x2f, 0x58, 0xe0,
-  0xc6, 0x4d, 0x7c, 0x70, 0xc7, 0x0f, 0x3a, 0xe0, 0xc8, 0x2d, 0x5e, 0x70,
-  0xc8, 0xf8, 0x57, 0x60, 0xca, 0x0d, 0x40, 0x70, 0xca, 0xd8, 0x39, 0x60,
-  0xcb, 0x88, 0xf0, 0x70, 0xd2, 0x23, 0xf4, 0x70, 0xd2, 0x60, 0xfb, 0xe0,
-  0xd3, 0x75, 0xe4, 0xf0, 0xd4, 0x40, 0xdd, 0xe0, 0xd5, 0x55, 0xc6, 0xf0,
-  0xd6, 0x20, 0xbf, 0xe0, 0xd7, 0x35, 0xa8, 0xf0, 0xd8, 0x00, 0xa1, 0xe0,
-  0xd9, 0x15, 0x8a, 0xf0, 0xd9, 0xe0, 0x83, 0xe0, 0xda, 0xfe, 0xa7, 0x70,
-  0xdb, 0xc0, 0x65, 0xe0, 0xdc, 0xde, 0x89, 0x70, 0xdd, 0xa9, 0x82, 0x60,
-  0xde, 0xbe, 0x6b, 0x70, 0xdf, 0x89, 0x64, 0x60, 0xe0, 0x9e, 0x4d, 0x70,
-  0xe1, 0x69, 0x46, 0x60, 0xe2, 0x7e, 0x2f, 0x70, 0xe3, 0x49, 0x28, 0x60,
-  0xe4, 0x5e, 0x11, 0x70, 0xe5, 0x57, 0x2e, 0xe0, 0xe6, 0x47, 0x2d, 0xf0,
-  0xe7, 0x37, 0x10, 0xe0, 0xe8, 0x27, 0x0f, 0xf0, 0xe9, 0x16, 0xf2, 0xe0,
-  0xea, 0x06, 0xf1, 0xf0, 0xea, 0xf6, 0xd4, 0xe0, 0xeb, 0xe6, 0xd3, 0xf0,
-  0xec, 0xd6, 0xb6, 0xe0, 0xed, 0xc6, 0xb5, 0xf0, 0xee, 0xbf, 0xd3, 0x60,
-  0xef, 0xaf, 0xd2, 0x70, 0xf0, 0x9f, 0xb5, 0x60, 0xf1, 0x8f, 0xb4, 0x70,
-  0xf2, 0x7f, 0x97, 0x60, 0xf3, 0x6f, 0x96, 0x70, 0xf4, 0x5f, 0x79, 0x60,
-  0xf5, 0x4f, 0x78, 0x70, 0xf6, 0x3f, 0x5b, 0x60, 0xf7, 0x2f, 0x5a, 0x70,
-  0xf8, 0x28, 0x77, 0xe0, 0xf9, 0x0f, 0x3c, 0x70, 0xfa, 0x08, 0x59, 0xe0,
-  0xfa, 0xf8, 0x58, 0xf0, 0xfb, 0xe8, 0x3b, 0xe0, 0xfc, 0xd8, 0x3a, 0xf0,
-  0xfd, 0xc8, 0x1d, 0xe0, 0xfe, 0xb8, 0x1c, 0xf0, 0xff, 0xa7, 0xff, 0xe0,
-  0x00, 0x97, 0xfe, 0xf0, 0x01, 0x87, 0xe1, 0xe0, 0x02, 0x77, 0xe0, 0xf0,
-  0x03, 0x70, 0xfe, 0x60, 0x04, 0x60, 0xfd, 0x70, 0x05, 0x50, 0xe0, 0x60,
-  0x06, 0x40, 0xdf, 0x70, 0x07, 0x30, 0xc2, 0x60, 0x07, 0x8d, 0x19, 0x70,
-  0x09, 0x10, 0xa4, 0x60, 0x09, 0xad, 0x94, 0xf0, 0x0a, 0xf0, 0x86, 0x60,
-  0x0b, 0xe0, 0x85, 0x70, 0x0c, 0xd9, 0xa2, 0xe0, 0x0d, 0xc0, 0x67, 0x70,
-  0x0e, 0xb9, 0x84, 0xe0, 0x0f, 0xa9, 0x83, 0xf0, 0x10, 0x99, 0x66, 0xe0,
-  0x11, 0x89, 0x65, 0xf0, 0x12, 0x79, 0x48, 0xe0, 0x13, 0x69, 0x47, 0xf0,
-  0x14, 0x59, 0x2a, 0xe0, 0x15, 0x49, 0x29, 0xf0, 0x16, 0x39, 0x0c, 0xe0,
-  0x17, 0x29, 0x0b, 0xf0, 0x18, 0x22, 0x29, 0x60, 0x19, 0x08, 0xed, 0xf0,
-  0x1a, 0x02, 0x0b, 0x60, 0x1a, 0xf2, 0x0a, 0x70, 0x1b, 0xe1, 0xed, 0x60,
-  0x1c, 0xd1, 0xec, 0x70, 0x1d, 0xc1, 0xcf, 0x60, 0x1e, 0xb1, 0xce, 0x70,
-  0x1f, 0xa1, 0xb1, 0x60, 0x20, 0x76, 0x00, 0xf0, 0x21, 0x81, 0x93, 0x60,
-  0x22, 0x55, 0xe2, 0xf0, 0x23, 0x6a, 0xaf, 0xe0, 0x24, 0x35, 0xc4, 0xf0,
-  0x25, 0x4a, 0x91, 0xe0, 0x26, 0x15, 0xa6, 0xf0, 0x27, 0x2a, 0x73, 0xe0,
-  0x27, 0xfe, 0xc3, 0x70, 0x29, 0x0a, 0x55, 0xe0, 0x29, 0xde, 0xa5, 0x70,
-  0x2a, 0xea, 0x37, 0xe0, 0x2b, 0xbe, 0x87, 0x70, 0x2c, 0xd3, 0x54, 0x60,
-  0x2d, 0x9e, 0x69, 0x70, 0x2e, 0xb3, 0x36, 0x60, 0x2f, 0x7e, 0x4b, 0x70,
-  0x30, 0x93, 0x18, 0x60, 0x31, 0x67, 0x67, 0xf0, 0x32, 0x72, 0xfa, 0x60,
-  0x33, 0x47, 0x49, 0xf0, 0x34, 0x52, 0xdc, 0x60, 0x35, 0x27, 0x2b, 0xf0,
-  0x36, 0x32, 0xbe, 0x60, 0x37, 0x07, 0x0d, 0xf0, 0x38, 0x1b, 0xda, 0xe0,
-  0x38, 0xe6, 0xef, 0xf0, 0x39, 0xfb, 0xbc, 0xe0, 0x3a, 0xc6, 0xd1, 0xf0,
-  0x3b, 0xdb, 0x9e, 0xe0, 0x3c, 0xaf, 0xee, 0x70, 0x3d, 0xbb, 0x80, 0xe0,
-  0x3e, 0x8f, 0xd0, 0x70, 0x3f, 0x9b, 0x62, 0xe0, 0x40, 0x6f, 0xb2, 0x70,
-  0x41, 0x84, 0x7f, 0x60, 0x42, 0x4f, 0x94, 0x70, 0x43, 0x64, 0x61, 0x60,
-  0x44, 0x2f, 0x76, 0x70, 0x45, 0x44, 0x43, 0x60, 0x45, 0xf3, 0xa8, 0xf0,
-  0x47, 0x2d, 0x5f, 0xe0, 0x47, 0xd3, 0x8a, 0xf0, 0x49, 0x0d, 0x41, 0xe0,
-  0x49, 0xb3, 0x6c, 0xf0, 0x4a, 0xed, 0x23, 0xe0, 0x4b, 0x9c, 0x89, 0x70,
-  0x4c, 0xd6, 0x40, 0x60, 0x4d, 0x7c, 0x6b, 0x70, 0x4e, 0xb6, 0x22, 0x60,
-  0x4f, 0x5c, 0x4d, 0x70, 0x50, 0x96, 0x04, 0x60, 0x51, 0x3c, 0x2f, 0x70,
-  0x52, 0x75, 0xe6, 0x60, 0x53, 0x1c, 0x11, 0x70, 0x54, 0x55, 0xc8, 0x60,
-  0x54, 0xfb, 0xf3, 0x70, 0x56, 0x35, 0xaa, 0x60, 0x56, 0xe5, 0x0f, 0xf0,
-  0x58, 0x1e, 0xc6, 0xe0, 0x58, 0xc4, 0xf1, 0xf0, 0x59, 0xfe, 0xa8, 0xe0,
-  0x5a, 0xa4, 0xd3, 0xf0, 0x5b, 0xde, 0x8a, 0xe0, 0x5c, 0x84, 0xb5, 0xf0,
-  0x5d, 0xbe, 0x6c, 0xe0, 0x5e, 0x64, 0x97, 0xf0, 0x5f, 0x9e, 0x4e, 0xe0,
-  0x60, 0x4d, 0xb4, 0x70, 0x61, 0x87, 0x6b, 0x60, 0x62, 0x2d, 0x96, 0x70,
-  0x63, 0x67, 0x4d, 0x60, 0x64, 0x0d, 0x78, 0x70, 0x65, 0x47, 0x2f, 0x60,
-  0x65, 0xed, 0x5a, 0x70, 0x67, 0x27, 0x11, 0x60, 0x67, 0xcd, 0x3c, 0x70,
-  0x69, 0x06, 0xf3, 0x60, 0x69, 0xad, 0x1e, 0x70, 0x6a, 0xe6, 0xd5, 0x60,
-  0x6b, 0x96, 0x3a, 0xf0, 0x6c, 0xcf, 0xf1, 0xe0, 0x6d, 0x76, 0x1c, 0xf0,
-  0x6e, 0xaf, 0xd3, 0xe0, 0x6f, 0x55, 0xfe, 0xf0, 0x70, 0x8f, 0xb5, 0xe0,
-  0x71, 0x35, 0xe0, 0xf0, 0x72, 0x6f, 0x97, 0xe0, 0x73, 0x15, 0xc2, 0xf0,
-  0x74, 0x4f, 0x79, 0xe0, 0x74, 0xfe, 0xdf, 0x70, 0x76, 0x38, 0x96, 0x60,
-  0x76, 0xde, 0xc1, 0x70, 0x78, 0x18, 0x78, 0x60, 0x78, 0xbe, 0xa3, 0x70,
-  0x79, 0xf8, 0x5a, 0x60, 0x7a, 0x9e, 0x85, 0x70, 0x7b, 0xd8, 0x3c, 0x60,
-  0x7c, 0x7e, 0x67, 0x70, 0x7d, 0xb8, 0x1e, 0x60, 0x7e, 0x5e, 0x49, 0x70,
-  0x7f, 0x98, 0x00, 0x60, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0xff, 0xff, 0xba, 0x9e, 0x00, 0x00, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x04,
-  0xff, 0xff, 0xb9, 0xb0, 0x00, 0x08, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x0c,
-  0xff, 0xff, 0xc7, 0xc0, 0x01, 0x10, 0x4c, 0x4d, 0x54, 0x00, 0x45, 0x44,
-  0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x45, 0x57, 0x54, 0x00, 0x45, 0x50,
-  0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
-  0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xf8, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x03, 0xf0, 0x90,
-  0xff, 0xff, 0xff, 0xff, 0x9e, 0xa6, 0x1e, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0x9f, 0xba, 0xeb, 0x60, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x86, 0x00, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xa1, 0x9a, 0xcd, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xa2, 0x65, 0xe2, 0x70, 0xff, 0xff, 0xff, 0xff, 0xa3, 0x83, 0xe9, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xa4, 0x6a, 0xae, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xa5, 0x35, 0xa7, 0x60, 0xff, 0xff, 0xff, 0xff, 0xa6, 0x53, 0xca, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xa7, 0x15, 0x89, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xa8, 0x33, 0xac, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xa8, 0xfe, 0xa5, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xaa, 0x13, 0x8e, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xaa, 0xde, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xab, 0xf3, 0x70, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xac, 0xbe, 0x69, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xad, 0xd3, 0x52, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xae, 0x9e, 0x4b, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xaf, 0xb3, 0x34, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xb0, 0x7e, 0x2d, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x9c, 0x51, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xb2, 0x67, 0x4a, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xb3, 0x7c, 0x33, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb4, 0x47, 0x2c, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xb5, 0x5c, 0x15, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xb6, 0x27, 0x0e, 0x60, 0xff, 0xff, 0xff, 0xff, 0xb7, 0x3b, 0xf7, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xb8, 0x06, 0xf0, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xb9, 0x1b, 0xd9, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xe6, 0xd2, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xbb, 0x04, 0xf5, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xbb, 0xc6, 0xb4, 0x60, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe4, 0xd7, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xbd, 0xaf, 0xd0, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xbe, 0xc4, 0xb9, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x8f, 0xb2, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xc0, 0xa4, 0x9b, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xc1, 0x6f, 0x94, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x84, 0x7d, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xc3, 0x4f, 0x76, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xc4, 0x64, 0x5f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xc5, 0x2f, 0x58, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xc6, 0x4d, 0x7c, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xc7, 0x0f, 0x3a, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc8, 0x2d, 0x5e, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xc8, 0xf8, 0x57, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xca, 0x0d, 0x40, 0x70, 0xff, 0xff, 0xff, 0xff, 0xca, 0xd8, 0x39, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xcb, 0x88, 0xf0, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xd2, 0x23, 0xf4, 0x70, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x60, 0xfb, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xd3, 0x75, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xd4, 0x40, 0xdd, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x55, 0xc6, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xd6, 0x20, 0xbf, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xd7, 0x35, 0xa8, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xd8, 0x00, 0xa1, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xd9, 0x15, 0x8a, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xd9, 0xe0, 0x83, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xda, 0xfe, 0xa7, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xdb, 0xc0, 0x65, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xdc, 0xde, 0x89, 0x70, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xa9, 0x82, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xde, 0xbe, 0x6b, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xdf, 0x89, 0x64, 0x60, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x9e, 0x4d, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xe1, 0x69, 0x46, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xe2, 0x7e, 0x2f, 0x70, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x49, 0x28, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xe4, 0x5e, 0x11, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xe5, 0x57, 0x2e, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x47, 0x2d, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xe7, 0x37, 0x10, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xe8, 0x27, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x16, 0xf2, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xea, 0x06, 0xf1, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xea, 0xf6, 0xd4, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe6, 0xd3, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xec, 0xd6, 0xb6, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xed, 0xc6, 0xb5, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xee, 0xbf, 0xd3, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xef, 0xaf, 0xd2, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xf0, 0x9f, 0xb5, 0x60, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x8f, 0xb4, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xf2, 0x7f, 0x97, 0x60, 0xff, 0xff, 0xff, 0xff,
-  0xf3, 0x6f, 0x96, 0x70, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x5f, 0x79, 0x60,
-  0xff, 0xff, 0xff, 0xff, 0xf5, 0x4f, 0x78, 0x70, 0xff, 0xff, 0xff, 0xff,
-  0xf6, 0x3f, 0x5b, 0x60, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x2f, 0x5a, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xf8, 0x28, 0x77, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xf9, 0x0f, 0x3c, 0x70, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x08, 0x59, 0xe0,
-  0xff, 0xff, 0xff, 0xff, 0xfa, 0xf8, 0x58, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xfb, 0xe8, 0x3b, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xd8, 0x3a, 0xf0,
-  0xff, 0xff, 0xff, 0xff, 0xfd, 0xc8, 0x1d, 0xe0, 0xff, 0xff, 0xff, 0xff,
-  0xfe, 0xb8, 0x1c, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x01, 0x87, 0xe1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x77, 0xe0, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x03, 0x70, 0xfe, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x04, 0x60, 0xfd, 0x70, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0xe0, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xdf, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x07, 0x30, 0xc2, 0x60, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8d, 0x19, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xa4, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x09, 0xad, 0x94, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x86, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x0b, 0xe0, 0x85, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x0c, 0xd9, 0xa2, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xc0, 0x67, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x0e, 0xb9, 0x84, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x0f, 0xa9, 0x83, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x66, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x65, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x12, 0x79, 0x48, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x47, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x2a, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x15, 0x49, 0x29, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x0c, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x0b, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x18, 0x22, 0x29, 0x60, 0x00, 0x00, 0x00, 0x00, 0x19, 0x08, 0xed, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x1a, 0x02, 0x0b, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x1a, 0xf2, 0x0a, 0x70, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe1, 0xed, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x1c, 0xd1, 0xec, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x1d, 0xc1, 0xcf, 0x60, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xb1, 0xce, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x1f, 0xa1, 0xb1, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x20, 0x76, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0x93, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xe2, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x23, 0x6a, 0xaf, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xc4, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x25, 0x4a, 0x91, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x26, 0x15, 0xa6, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2a, 0x73, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x27, 0xfe, 0xc3, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x29, 0x0a, 0x55, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x29, 0xde, 0xa5, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x2a, 0xea, 0x37, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x2b, 0xbe, 0x87, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd3, 0x54, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x2d, 0x9e, 0x69, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x2e, 0xb3, 0x36, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7e, 0x4b, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x31, 0x67, 0x67, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0xfa, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x49, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x34, 0x52, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x2b, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xbe, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x37, 0x07, 0x0d, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1b, 0xda, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x38, 0xe6, 0xef, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x39, 0xfb, 0xbc, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xc6, 0xd1, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x3b, 0xdb, 0x9e, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x3c, 0xaf, 0xee, 0x70, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xbb, 0x80, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x3e, 0x8f, 0xd0, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x3f, 0x9b, 0x62, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6f, 0xb2, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x7f, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x42, 0x4f, 0x94, 0x70, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x61, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x44, 0x2f, 0x76, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x45, 0x44, 0x43, 0x60, 0x00, 0x00, 0x00, 0x00, 0x45, 0xf3, 0xa8, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x47, 0x2d, 0x5f, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x47, 0xd3, 0x8a, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0d, 0x41, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x49, 0xb3, 0x6c, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x4a, 0xed, 0x23, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x9c, 0x89, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x4c, 0xd6, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x4d, 0x7c, 0x6b, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xb6, 0x22, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x4f, 0x5c, 0x4d, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x50, 0x96, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3c, 0x2f, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x52, 0x75, 0xe6, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x53, 0x1c, 0x11, 0x70, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xc8, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x54, 0xfb, 0xf3, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x56, 0x35, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x00, 0x56, 0xe5, 0x0f, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x58, 0x1e, 0xc6, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x58, 0xc4, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x59, 0xfe, 0xa8, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x5a, 0xa4, 0xd3, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x5b, 0xde, 0x8a, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x84, 0xb5, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x5d, 0xbe, 0x6c, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x5e, 0x64, 0x97, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9e, 0x4e, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x60, 0x4d, 0xb4, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x61, 0x87, 0x6b, 0x60, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2d, 0x96, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x4d, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x64, 0x0d, 0x78, 0x70, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x2f, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x65, 0xed, 0x5a, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x67, 0x27, 0x11, 0x60, 0x00, 0x00, 0x00, 0x00, 0x67, 0xcd, 0x3c, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x69, 0x06, 0xf3, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x69, 0xad, 0x1e, 0x70, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xe6, 0xd5, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x6b, 0x96, 0x3a, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x6c, 0xcf, 0xf1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x76, 0x1c, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x6e, 0xaf, 0xd3, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x6f, 0x55, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8f, 0xb5, 0xe0,
-  0x00, 0x00, 0x00, 0x00, 0x71, 0x35, 0xe0, 0xf0, 0x00, 0x00, 0x00, 0x00,
-  0x72, 0x6f, 0x97, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, 0xc2, 0xf0,
-  0x00, 0x00, 0x00, 0x00, 0x74, 0x4f, 0x79, 0xe0, 0x00, 0x00, 0x00, 0x00,
-  0x74, 0xfe, 0xdf, 0x70, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, 0x96, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x76, 0xde, 0xc1, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x78, 0x18, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, 0x78, 0xbe, 0xa3, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x79, 0xf8, 0x5a, 0x60, 0x00, 0x00, 0x00, 0x00,
-  0x7a, 0x9e, 0x85, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xd8, 0x3c, 0x60,
-  0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0x67, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x7d, 0xb8, 0x1e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x5e, 0x49, 0x70,
-  0x00, 0x00, 0x00, 0x00, 0x7f, 0x98, 0x00, 0x60, 0x00, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0xff, 0xff, 0xba, 0x9e, 0x00, 0x00, 0xff,
-  0xff, 0xc7, 0xc0, 0x01, 0x04, 0xff, 0xff, 0xb9, 0xb0, 0x00, 0x08, 0xff,
-  0xff, 0xc7, 0xc0, 0x01, 0x0c, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x10, 0x4c,
-  0x4d, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x45,
-  0x57, 0x54, 0x00, 0x45, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-  0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x45, 0x53, 0x54, 0x35, 0x45, 0x44,
-  0x54, 0x2c, 0x4d, 0x33, 0x2e, 0x32, 0x2e, 0x30, 0x2c, 0x4d, 0x31, 0x31,
-  0x2e, 0x31, 0x2e, 0x30, 0x0a
-};
-unsigned int America_New_York_len = 3545;
-unsigned char Australia_Sydney[] = {
-  0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x00, 0x00, 0x00,
-  0x9c, 0x4e, 0xa6, 0x9c, 0x9c, 0xbc, 0x20, 0xf0, 0xcb, 0x54, 0xb3, 0x00,
-  0xcb, 0xc7, 0x57, 0x70, 0xcc, 0xb7, 0x56, 0x80, 0xcd, 0xa7, 0x39, 0x70,
-  0xce, 0xa0, 0x73, 0x00, 0xcf, 0x87, 0x1b, 0x70, 0x03, 0x70, 0x39, 0x80,
-  0x04, 0x0d, 0x1c, 0x00, 0x05, 0x50, 0x1b, 0x80, 0x05, 0xf6, 0x38, 0x80,
-  0x07, 0x2f, 0xfd, 0x80, 0x07, 0xd6, 0x1a, 0x80, 0x09, 0x0f, 0xdf, 0x80,
-  0x09, 0xb5, 0xfc, 0x80, 0x0a, 0xef, 0xc1, 0x80, 0x0b, 0x9f, 0x19, 0x00,
-  0x0c, 0xd8, 0xde, 0x00, 0x0d, 0x7e, 0xfb, 0x00, 0x0e, 0xb8, 0xc0, 0x00,
-  0x0f, 0x5e, 0xdd, 0x00, 0x10, 0x98, 0xa2, 0x00, 0x11, 0x3e, 0xbf, 0x00,
-  0x12, 0x78, 0x84, 0x00, 0x13, 0x1e, 0xa1, 0x00, 0x14, 0x58, 0x66, 0x00,
-  0x14, 0xfe, 0x83, 0x00, 0x16, 0x38, 0x48, 0x00, 0x17, 0x0c, 0x89, 0x80,
-  0x18, 0x21, 0x64, 0x80, 0x18, 0xc7, 0x81, 0x80, 0x1a, 0x01, 0x46, 0x80,
-  0x1a, 0xa7, 0x63, 0x80, 0x1b, 0xe1, 0x28, 0x80, 0x1c, 0x87, 0x45, 0x80,
-  0x1d, 0xc1, 0x0a, 0x80, 0x1e, 0x79, 0x9c, 0x80, 0x1f, 0x97, 0xb2, 0x00,
-  0x20, 0x59, 0x7e, 0x80, 0x21, 0x80, 0xce, 0x80, 0x22, 0x42, 0x9b, 0x00,
-  0x23, 0x69, 0xeb, 0x00, 0x24, 0x22, 0x7d, 0x00, 0x25, 0x49, 0xcd, 0x00,
-  0x25, 0xef, 0xea, 0x00, 0x27, 0x29, 0xaf, 0x00, 0x27, 0xcf, 0xcc, 0x00,
-  0x29, 0x09, 0x91, 0x00, 0x29, 0xaf, 0xae, 0x00, 0x2a, 0xe9, 0x73, 0x00,
-  0x2b, 0x98, 0xca, 0x80, 0x2c, 0xd2, 0x8f, 0x80, 0x2d, 0x78, 0xac, 0x80,
-  0x2e, 0xb2, 0x71, 0x80, 0x2f, 0x58, 0x8e, 0x80, 0x30, 0x92, 0x53, 0x80,
-  0x31, 0x5d, 0x5a, 0x80, 0x32, 0x72, 0x35, 0x80, 0x33, 0x3d, 0x3c, 0x80,
-  0x34, 0x52, 0x17, 0x80, 0x35, 0x1d, 0x1e, 0x80, 0x36, 0x31, 0xf9, 0x80,
-  0x36, 0xfd, 0x00, 0x80, 0x38, 0x1b, 0x16, 0x00, 0x38, 0xdc, 0xe2, 0x80,
-  0x39, 0xa7, 0xe9, 0x80, 0x3a, 0xbc, 0xc4, 0x80, 0x3b, 0xda, 0xda, 0x00,
-  0x3c, 0xa5, 0xe1, 0x00, 0x3d, 0xba, 0xbc, 0x00, 0x3e, 0x85, 0xc3, 0x00,
-  0x3f, 0x9a, 0x9e, 0x00, 0x40, 0x65, 0xa5, 0x00, 0x41, 0x83, 0xba, 0x80,
-  0x42, 0x45, 0x87, 0x00, 0x43, 0x63, 0x9c, 0x80, 0x44, 0x2e, 0xa3, 0x80,
-  0x45, 0x43, 0x7e, 0x80, 0x46, 0x05, 0x4b, 0x00, 0x47, 0x23, 0x60, 0x80,
-  0x47, 0xf7, 0xa2, 0x00, 0x48, 0xe7, 0x93, 0x00, 0x49, 0xd7, 0x84, 0x00,
-  0x4a, 0xc7, 0x75, 0x00, 0x4b, 0xb7, 0x66, 0x00, 0x4c, 0xa7, 0x57, 0x00,
-  0x4d, 0x97, 0x48, 0x00, 0x4e, 0x87, 0x39, 0x00, 0x4f, 0x77, 0x2a, 0x00,
-  0x50, 0x70, 0x55, 0x80, 0x51, 0x60, 0x46, 0x80, 0x52, 0x50, 0x37, 0x80,
-  0x53, 0x40, 0x28, 0x80, 0x54, 0x30, 0x19, 0x80, 0x55, 0x20, 0x0a, 0x80,
-  0x56, 0x0f, 0xfb, 0x80, 0x56, 0xff, 0xec, 0x80, 0x57, 0xef, 0xdd, 0x80,
-  0x58, 0xdf, 0xce, 0x80, 0x59, 0xcf, 0xbf, 0x80, 0x5a, 0xbf, 0xb0, 0x80,
-  0x5b, 0xb8, 0xdc, 0x00, 0x5c, 0xa8, 0xcd, 0x00, 0x5d, 0x98, 0xbe, 0x00,
-  0x5e, 0x88, 0xaf, 0x00, 0x5f, 0x78, 0xa0, 0x00, 0x60, 0x68, 0x91, 0x00,
-  0x61, 0x58, 0x82, 0x00, 0x62, 0x48, 0x73, 0x00, 0x63, 0x38, 0x64, 0x00,
-  0x64, 0x28, 0x55, 0x00, 0x65, 0x18, 0x46, 0x00, 0x66, 0x11, 0x71, 0x80,
-  0x67, 0x01, 0x62, 0x80, 0x67, 0xf1, 0x53, 0x80, 0x68, 0xe1, 0x44, 0x80,
-  0x69, 0xd1, 0x35, 0x80, 0x6a, 0xc1, 0x26, 0x80, 0x6b, 0xb1, 0x17, 0x80,
-  0x6c, 0xa1, 0x08, 0x80, 0x6d, 0x90, 0xf9, 0x80, 0x6e, 0x80, 0xea, 0x80,
-  0x6f, 0x70, 0xdb, 0x80, 0x70, 0x6a, 0x07, 0x00, 0x71, 0x59, 0xf8, 0x00,
-  0x72, 0x49, 0xe9, 0x00, 0x73, 0x39, 0xda, 0x00, 0x74, 0x29, 0xcb, 0x00,
-  0x75, 0x19, 0xbc, 0x00, 0x76, 0x09, 0xad, 0x00, 0x76, 0xf9, 0x9e, 0x00,
-  0x77, 0xe9, 0x8f, 0x00, 0x78, 0xd9, 0x80, 0x00, 0x79, 0xc9, 0x71, 0x00,
-  0x7a, 0xb9, 0x62, 0x00, 0x7b, 0xb2, 0x8d, 0x80, 0x7c, 0xa2, 0x7e, 0x80,
-  0x7d, 0x92, 0x6f, 0x80, 0x7e, 0x82, 0x60, 0x80, 0x7f, 0x72, 0x51, 0x80,
-  0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03,
-  0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x00, 0x00,
-  0x8d, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x9a, 0xb0, 0x01, 0x04, 0x00, 0x00,
-  0x8c, 0xa0, 0x00, 0x09, 0x00, 0x00, 0x9a, 0xb0, 0x01, 0x04, 0x00, 0x00,
-  0x8c, 0xa0, 0x00, 0x09, 0x4c, 0x4d, 0x54, 0x00, 0x41, 0x45, 0x44, 0x54,
-  0x00, 0x41, 0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0e,
-  0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
-  0x73, 0x16, 0x7f, 0x3c, 0xff, 0xff, 0xff, 0xff, 0x9c, 0x4e, 0xa6, 0x9c,
-  0xff, 0xff, 0xff, 0xff, 0x9c, 0xbc, 0x20, 0xf0, 0xff, 0xff, 0xff, 0xff,
-  0xcb, 0x54, 0xb3, 0x00, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xc7, 0x57, 0x70,
-  0xff, 0xff, 0xff, 0xff, 0xcc, 0xb7, 0x56, 0x80, 0xff, 0xff, 0xff, 0xff,
-  0xcd, 0xa7, 0x39, 0x70, 0xff, 0xff, 0xff, 0xff, 0xce, 0xa0, 0x73, 0x00,
-  0xff, 0xff, 0xff, 0xff, 0xcf, 0x87, 0x1b, 0x70, 0x00, 0x00, 0x00, 0x00,
-  0x03, 0x70, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0d, 0x1c, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0x1b, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x05, 0xf6, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x2f, 0xfd, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x07, 0xd6, 0x1a, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x09, 0x0f, 0xdf, 0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0xb5, 0xfc, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x0a, 0xef, 0xc1, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x0b, 0x9f, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xd8, 0xde, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x0d, 0x7e, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x0e, 0xb8, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x5e, 0xdd, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x10, 0x98, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x11, 0x3e, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x78, 0x84, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x13, 0x1e, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x14, 0x58, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xfe, 0x83, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x16, 0x38, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x17, 0x0c, 0x89, 0x80, 0x00, 0x00, 0x00, 0x00, 0x18, 0x21, 0x64, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x18, 0xc7, 0x81, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x1a, 0x01, 0x46, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1a, 0xa7, 0x63, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x1b, 0xe1, 0x28, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x1c, 0x87, 0x45, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xc1, 0x0a, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x1e, 0x79, 0x9c, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x1f, 0x97, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x59, 0x7e, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x21, 0x80, 0xce, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x22, 0x42, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x69, 0xeb, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x24, 0x22, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x25, 0x49, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xef, 0xea, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x27, 0x29, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x27, 0xcf, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x09, 0x91, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x29, 0xaf, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x2a, 0xe9, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x98, 0xca, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x2c, 0xd2, 0x8f, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x2d, 0x78, 0xac, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xb2, 0x71, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x2f, 0x58, 0x8e, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x30, 0x92, 0x53, 0x80, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5d, 0x5a, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x35, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x33, 0x3d, 0x3c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0x17, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x35, 0x1d, 0x1e, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x36, 0x31, 0xf9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x36, 0xfd, 0x00, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x38, 0x1b, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x38, 0xdc, 0xe2, 0x80, 0x00, 0x00, 0x00, 0x00, 0x39, 0xa7, 0xe9, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x3a, 0xbc, 0xc4, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x3b, 0xda, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xa5, 0xe1, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x3d, 0xba, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x3e, 0x85, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x9a, 0x9e, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x40, 0x65, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x41, 0x83, 0xba, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x45, 0x87, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x9c, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x44, 0x2e, 0xa3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x45, 0x43, 0x7e, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x47, 0x23, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x47, 0xf7, 0xa2, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x48, 0xe7, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x49, 0xd7, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xc7, 0x75, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x4b, 0xb7, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x4c, 0xa7, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x97, 0x48, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x4e, 0x87, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x4f, 0x77, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x70, 0x55, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x51, 0x60, 0x46, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x52, 0x50, 0x37, 0x80, 0x00, 0x00, 0x00, 0x00, 0x53, 0x40, 0x28, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x54, 0x30, 0x19, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x55, 0x20, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x56, 0x0f, 0xfb, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x56, 0xff, 0xec, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x57, 0xef, 0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x58, 0xdf, 0xce, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x59, 0xcf, 0xbf, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x5a, 0xbf, 0xb0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xb8, 0xdc, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x5c, 0xa8, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x5d, 0x98, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x88, 0xaf, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x5f, 0x78, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x60, 0x68, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x58, 0x82, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x62, 0x48, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x63, 0x38, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x28, 0x55, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x65, 0x18, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x66, 0x11, 0x71, 0x80, 0x00, 0x00, 0x00, 0x00, 0x67, 0x01, 0x62, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x67, 0xf1, 0x53, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x68, 0xe1, 0x44, 0x80, 0x00, 0x00, 0x00, 0x00, 0x69, 0xd1, 0x35, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x6a, 0xc1, 0x26, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x6b, 0xb1, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xa1, 0x08, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x6d, 0x90, 0xf9, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x6e, 0x80, 0xea, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x70, 0xdb, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x70, 0x6a, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x71, 0x59, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x49, 0xe9, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x73, 0x39, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x74, 0x29, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x19, 0xbc, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x76, 0x09, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x76, 0xf9, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xe9, 0x8f, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x78, 0xd9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x79, 0xc9, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0xb9, 0x62, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x7b, 0xb2, 0x8d, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x7c, 0xa2, 0x7e, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x92, 0x6f, 0x80,
-  0x00, 0x00, 0x00, 0x00, 0x7e, 0x82, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00,
-  0x7f, 0x72, 0x51, 0x80, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
-  0x01, 0x02, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
-  0x03, 0x04, 0x03, 0x00, 0x00, 0x8d, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x9a,
-  0xb0, 0x01, 0x04, 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09, 0x00, 0x00, 0x9a,
-  0xb0, 0x01, 0x04, 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09, 0x4c, 0x4d, 0x54,
-  0x00, 0x41, 0x45, 0x44, 0x54, 0x00, 0x41, 0x45, 0x53, 0x54, 0x00, 0x00,
-  0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x41, 0x45,
-  0x53, 0x54, 0x2d, 0x31, 0x30, 0x41, 0x45, 0x44, 0x54, 0x2c, 0x4d, 0x31,
-  0x30, 0x2e, 0x31, 0x2e, 0x30, 0x2c, 0x4d, 0x34, 0x2e, 0x31, 0x2e, 0x30,
-  0x2f, 0x33, 0x0a
-};
-unsigned int Australia_Sydney_len = 2223;
diff --git a/third_party/abseil_cpp/absl/time/time.cc b/third_party/abseil_cpp/absl/time/time.cc
deleted file mode 100644
index 1ec2026e25..0000000000
--- a/third_party/abseil_cpp/absl/time/time.cc
+++ /dev/null
@@ -1,500 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// The implementation of the absl::Time class, which is declared in
-// //absl/time.h.
-//
-// The representation for an absl::Time is an absl::Duration offset from the
-// epoch.  We use the traditional Unix epoch (1970-01-01 00:00:00 +0000)
-// for convenience, but this is not exposed in the API and could be changed.
-//
-// NOTE: To keep type verbosity to a minimum, the following variable naming
-// conventions are used throughout this file.
-//
-// tz: An absl::TimeZone
-// ci: An absl::TimeZone::CivilInfo
-// ti: An absl::TimeZone::TimeInfo
-// cd: An absl::CivilDay or a cctz::civil_day
-// cs: An absl::CivilSecond or a cctz::civil_second
-// bd: An absl::Time::Breakdown
-// cl: A cctz::time_zone::civil_lookup
-// al: A cctz::time_zone::absolute_lookup
-
-#include "absl/time/time.h"
-
-#if defined(_MSC_VER)
-#include <winsock2.h>  // for timeval
-#endif
-
-#include <cstring>
-#include <ctime>
-#include <limits>
-
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-namespace cctz = absl::time_internal::cctz;
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace {
-
-inline cctz::time_point<cctz::seconds> unix_epoch() {
-  return std::chrono::time_point_cast<cctz::seconds>(
-      std::chrono::system_clock::from_time_t(0));
-}
-
-// Floors d to the next unit boundary closer to negative infinity.
-inline int64_t FloorToUnit(absl::Duration d, absl::Duration unit) {
-  absl::Duration rem;
-  int64_t q = absl::IDivDuration(d, unit, &rem);
-  return (q > 0 || rem >= ZeroDuration() ||
-          q == std::numeric_limits<int64_t>::min())
-             ? q
-             : q - 1;
-}
-
-inline absl::Time::Breakdown InfiniteFutureBreakdown() {
-  absl::Time::Breakdown bd;
-  bd.year = std::numeric_limits<int64_t>::max();
-  bd.month = 12;
-  bd.day = 31;
-  bd.hour = 23;
-  bd.minute = 59;
-  bd.second = 59;
-  bd.subsecond = absl::InfiniteDuration();
-  bd.weekday = 4;
-  bd.yearday = 365;
-  bd.offset = 0;
-  bd.is_dst = false;
-  bd.zone_abbr = "-00";
-  return bd;
-}
-
-inline absl::Time::Breakdown InfinitePastBreakdown() {
-  Time::Breakdown bd;
-  bd.year = std::numeric_limits<int64_t>::min();
-  bd.month = 1;
-  bd.day = 1;
-  bd.hour = 0;
-  bd.minute = 0;
-  bd.second = 0;
-  bd.subsecond = -absl::InfiniteDuration();
-  bd.weekday = 7;
-  bd.yearday = 1;
-  bd.offset = 0;
-  bd.is_dst = false;
-  bd.zone_abbr = "-00";
-  return bd;
-}
-
-inline absl::TimeZone::CivilInfo InfiniteFutureCivilInfo() {
-  TimeZone::CivilInfo ci;
-  ci.cs = CivilSecond::max();
-  ci.subsecond = InfiniteDuration();
-  ci.offset = 0;
-  ci.is_dst = false;
-  ci.zone_abbr = "-00";
-  return ci;
-}
-
-inline absl::TimeZone::CivilInfo InfinitePastCivilInfo() {
-  TimeZone::CivilInfo ci;
-  ci.cs = CivilSecond::min();
-  ci.subsecond = -InfiniteDuration();
-  ci.offset = 0;
-  ci.is_dst = false;
-  ci.zone_abbr = "-00";
-  return ci;
-}
-
-inline absl::TimeConversion InfiniteFutureTimeConversion() {
-  absl::TimeConversion tc;
-  tc.pre = tc.trans = tc.post = absl::InfiniteFuture();
-  tc.kind = absl::TimeConversion::UNIQUE;
-  tc.normalized = true;
-  return tc;
-}
-
-inline TimeConversion InfinitePastTimeConversion() {
-  absl::TimeConversion tc;
-  tc.pre = tc.trans = tc.post = absl::InfinitePast();
-  tc.kind = absl::TimeConversion::UNIQUE;
-  tc.normalized = true;
-  return tc;
-}
-
-// Makes a Time from sec, overflowing to InfiniteFuture/InfinitePast as
-// necessary. If sec is min/max, then consult cs+tz to check for overlow.
-Time MakeTimeWithOverflow(const cctz::time_point<cctz::seconds>& sec,
-                          const cctz::civil_second& cs,
-                          const cctz::time_zone& tz,
-                          bool* normalized = nullptr) {
-  const auto max = cctz::time_point<cctz::seconds>::max();
-  const auto min = cctz::time_point<cctz::seconds>::min();
-  if (sec == max) {
-    const auto al = tz.lookup(max);
-    if (cs > al.cs) {
-      if (normalized) *normalized = true;
-      return absl::InfiniteFuture();
-    }
-  }
-  if (sec == min) {
-    const auto al = tz.lookup(min);
-    if (cs < al.cs) {
-      if (normalized) *normalized = true;
-      return absl::InfinitePast();
-    }
-  }
-  const auto hi = (sec - unix_epoch()).count();
-  return time_internal::FromUnixDuration(time_internal::MakeDuration(hi));
-}
-
-// Returns Mon=1..Sun=7.
-inline int MapWeekday(const cctz::weekday& wd) {
-  switch (wd) {
-    case cctz::weekday::monday:
-      return 1;
-    case cctz::weekday::tuesday:
-      return 2;
-    case cctz::weekday::wednesday:
-      return 3;
-    case cctz::weekday::thursday:
-      return 4;
-    case cctz::weekday::friday:
-      return 5;
-    case cctz::weekday::saturday:
-      return 6;
-    case cctz::weekday::sunday:
-      return 7;
-  }
-  return 1;
-}
-
-bool FindTransition(const cctz::time_zone& tz,
-                    bool (cctz::time_zone::*find_transition)(
-                        const cctz::time_point<cctz::seconds>& tp,
-                        cctz::time_zone::civil_transition* trans) const,
-                    Time t, TimeZone::CivilTransition* trans) {
-  // Transitions are second-aligned, so we can discard any fractional part.
-  const auto tp = unix_epoch() + cctz::seconds(ToUnixSeconds(t));
-  cctz::time_zone::civil_transition tr;
-  if (!(tz.*find_transition)(tp, &tr)) return false;
-  trans->from = CivilSecond(tr.from);
-  trans->to = CivilSecond(tr.to);
-  return true;
-}
-
-}  // namespace
-
-//
-// Time
-//
-
-absl::Time::Breakdown Time::In(absl::TimeZone tz) const {
-  if (*this == absl::InfiniteFuture()) return InfiniteFutureBreakdown();
-  if (*this == absl::InfinitePast()) return InfinitePastBreakdown();
-
-  const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(rep_));
-  const auto al = cctz::time_zone(tz).lookup(tp);
-  const auto cs = al.cs;
-  const auto cd = cctz::civil_day(cs);
-
-  absl::Time::Breakdown bd;
-  bd.year = cs.year();
-  bd.month = cs.month();
-  bd.day = cs.day();
-  bd.hour = cs.hour();
-  bd.minute = cs.minute();
-  bd.second = cs.second();
-  bd.subsecond = time_internal::MakeDuration(0, time_internal::GetRepLo(rep_));
-  bd.weekday = MapWeekday(cctz::get_weekday(cd));
-  bd.yearday = cctz::get_yearday(cd);
-  bd.offset = al.offset;
-  bd.is_dst = al.is_dst;
-  bd.zone_abbr = al.abbr;
-  return bd;
-}
-
-//
-// Conversions from/to other time types.
-//
-
-absl::Time FromUDate(double udate) {
-  return time_internal::FromUnixDuration(absl::Milliseconds(udate));
-}
-
-absl::Time FromUniversal(int64_t universal) {
-  return absl::UniversalEpoch() + 100 * absl::Nanoseconds(universal);
-}
-
-int64_t ToUnixNanos(Time t) {
-  if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
-      time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 33 == 0) {
-    return (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) *
-            1000 * 1000 * 1000) +
-           (time_internal::GetRepLo(time_internal::ToUnixDuration(t)) / 4);
-  }
-  return FloorToUnit(time_internal::ToUnixDuration(t), absl::Nanoseconds(1));
-}
-
-int64_t ToUnixMicros(Time t) {
-  if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
-      time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 43 == 0) {
-    return (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) *
-            1000 * 1000) +
-           (time_internal::GetRepLo(time_internal::ToUnixDuration(t)) / 4000);
-  }
-  return FloorToUnit(time_internal::ToUnixDuration(t), absl::Microseconds(1));
-}
-
-int64_t ToUnixMillis(Time t) {
-  if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
-      time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 53 == 0) {
-    return (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) * 1000) +
-           (time_internal::GetRepLo(time_internal::ToUnixDuration(t)) /
-            (4000 * 1000));
-  }
-  return FloorToUnit(time_internal::ToUnixDuration(t), absl::Milliseconds(1));
-}
-
-int64_t ToUnixSeconds(Time t) {
-  return time_internal::GetRepHi(time_internal::ToUnixDuration(t));
-}
-
-time_t ToTimeT(Time t) { return absl::ToTimespec(t).tv_sec; }
-
-double ToUDate(Time t) {
-  return absl::FDivDuration(time_internal::ToUnixDuration(t),
-                            absl::Milliseconds(1));
-}
-
-int64_t ToUniversal(absl::Time t) {
-  return absl::FloorToUnit(t - absl::UniversalEpoch(), absl::Nanoseconds(100));
-}
-
-absl::Time TimeFromTimespec(timespec ts) {
-  return time_internal::FromUnixDuration(absl::DurationFromTimespec(ts));
-}
-
-absl::Time TimeFromTimeval(timeval tv) {
-  return time_internal::FromUnixDuration(absl::DurationFromTimeval(tv));
-}
-
-timespec ToTimespec(Time t) {
-  timespec ts;
-  absl::Duration d = time_internal::ToUnixDuration(t);
-  if (!time_internal::IsInfiniteDuration(d)) {
-    ts.tv_sec = time_internal::GetRepHi(d);
-    if (ts.tv_sec == time_internal::GetRepHi(d)) {  // no time_t narrowing
-      ts.tv_nsec = time_internal::GetRepLo(d) / 4;  // floor
-      return ts;
-    }
-  }
-  if (d >= absl::ZeroDuration()) {
-    ts.tv_sec = std::numeric_limits<time_t>::max();
-    ts.tv_nsec = 1000 * 1000 * 1000 - 1;
-  } else {
-    ts.tv_sec = std::numeric_limits<time_t>::min();
-    ts.tv_nsec = 0;
-  }
-  return ts;
-}
-
-timeval ToTimeval(Time t) {
-  timeval tv;
-  timespec ts = absl::ToTimespec(t);
-  tv.tv_sec = ts.tv_sec;
-  if (tv.tv_sec != ts.tv_sec) {  // narrowing
-    if (ts.tv_sec < 0) {
-      tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::min();
-      tv.tv_usec = 0;
-    } else {
-      tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::max();
-      tv.tv_usec = 1000 * 1000 - 1;
-    }
-    return tv;
-  }
-  tv.tv_usec = static_cast<int>(ts.tv_nsec / 1000);  // suseconds_t
-  return tv;
-}
-
-Time FromChrono(const std::chrono::system_clock::time_point& tp) {
-  return time_internal::FromUnixDuration(time_internal::FromChrono(
-      tp - std::chrono::system_clock::from_time_t(0)));
-}
-
-std::chrono::system_clock::time_point ToChronoTime(absl::Time t) {
-  using D = std::chrono::system_clock::duration;
-  auto d = time_internal::ToUnixDuration(t);
-  if (d < ZeroDuration()) d = Floor(d, FromChrono(D{1}));
-  return std::chrono::system_clock::from_time_t(0) +
-         time_internal::ToChronoDuration<D>(d);
-}
-
-//
-// TimeZone
-//
-
-absl::TimeZone::CivilInfo TimeZone::At(Time t) const {
-  if (t == absl::InfiniteFuture()) return InfiniteFutureCivilInfo();
-  if (t == absl::InfinitePast()) return InfinitePastCivilInfo();
-
-  const auto ud = time_internal::ToUnixDuration(t);
-  const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(ud));
-  const auto al = cz_.lookup(tp);
-
-  TimeZone::CivilInfo ci;
-  ci.cs = CivilSecond(al.cs);
-  ci.subsecond = time_internal::MakeDuration(0, time_internal::GetRepLo(ud));
-  ci.offset = al.offset;
-  ci.is_dst = al.is_dst;
-  ci.zone_abbr = al.abbr;
-  return ci;
-}
-
-absl::TimeZone::TimeInfo TimeZone::At(CivilSecond ct) const {
-  const cctz::civil_second cs(ct);
-  const auto cl = cz_.lookup(cs);
-
-  TimeZone::TimeInfo ti;
-  switch (cl.kind) {
-    case cctz::time_zone::civil_lookup::UNIQUE:
-      ti.kind = TimeZone::TimeInfo::UNIQUE;
-      break;
-    case cctz::time_zone::civil_lookup::SKIPPED:
-      ti.kind = TimeZone::TimeInfo::SKIPPED;
-      break;
-    case cctz::time_zone::civil_lookup::REPEATED:
-      ti.kind = TimeZone::TimeInfo::REPEATED;
-      break;
-  }
-  ti.pre = MakeTimeWithOverflow(cl.pre, cs, cz_);
-  ti.trans = MakeTimeWithOverflow(cl.trans, cs, cz_);
-  ti.post = MakeTimeWithOverflow(cl.post, cs, cz_);
-  return ti;
-}
-
-bool TimeZone::NextTransition(Time t, CivilTransition* trans) const {
-  return FindTransition(cz_, &cctz::time_zone::next_transition, t, trans);
-}
-
-bool TimeZone::PrevTransition(Time t, CivilTransition* trans) const {
-  return FindTransition(cz_, &cctz::time_zone::prev_transition, t, trans);
-}
-
-//
-// Conversions involving time zones.
-//
-
-absl::TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour,
-                                     int min, int sec, TimeZone tz) {
-  // Avoids years that are too extreme for CivilSecond to normalize.
-  if (year > 300000000000) return InfiniteFutureTimeConversion();
-  if (year < -300000000000) return InfinitePastTimeConversion();
-
-  const CivilSecond cs(year, mon, day, hour, min, sec);
-  const auto ti = tz.At(cs);
-
-  TimeConversion tc;
-  tc.pre = ti.pre;
-  tc.trans = ti.trans;
-  tc.post = ti.post;
-  switch (ti.kind) {
-    case TimeZone::TimeInfo::UNIQUE:
-      tc.kind = TimeConversion::UNIQUE;
-      break;
-    case TimeZone::TimeInfo::SKIPPED:
-      tc.kind = TimeConversion::SKIPPED;
-      break;
-    case TimeZone::TimeInfo::REPEATED:
-      tc.kind = TimeConversion::REPEATED;
-      break;
-  }
-  tc.normalized = false;
-  if (year != cs.year() || mon != cs.month() || day != cs.day() ||
-      hour != cs.hour() || min != cs.minute() || sec != cs.second()) {
-    tc.normalized = true;
-  }
-  return tc;
-}
-
-absl::Time FromTM(const struct tm& tm, absl::TimeZone tz) {
-  civil_year_t tm_year = tm.tm_year;
-  // Avoids years that are too extreme for CivilSecond to normalize.
-  if (tm_year > 300000000000ll) return InfiniteFuture();
-  if (tm_year < -300000000000ll) return InfinitePast();
-  int tm_mon = tm.tm_mon;
-  if (tm_mon == std::numeric_limits<int>::max()) {
-    tm_mon -= 12;
-    tm_year += 1;
-  }
-  const auto ti = tz.At(CivilSecond(tm_year + 1900, tm_mon + 1, tm.tm_mday,
-                                    tm.tm_hour, tm.tm_min, tm.tm_sec));
-  return tm.tm_isdst == 0 ? ti.post : ti.pre;
-}
-
-struct tm ToTM(absl::Time t, absl::TimeZone tz) {
-  struct tm tm = {};
-
-  const auto ci = tz.At(t);
-  const auto& cs = ci.cs;
-  tm.tm_sec = cs.second();
-  tm.tm_min = cs.minute();
-  tm.tm_hour = cs.hour();
-  tm.tm_mday = cs.day();
-  tm.tm_mon = cs.month() - 1;
-
-  // Saturates tm.tm_year in cases of over/underflow, accounting for the fact
-  // that tm.tm_year is years since 1900.
-  if (cs.year() < std::numeric_limits<int>::min() + 1900) {
-    tm.tm_year = std::numeric_limits<int>::min();
-  } else if (cs.year() > std::numeric_limits<int>::max()) {
-    tm.tm_year = std::numeric_limits<int>::max() - 1900;
-  } else {
-    tm.tm_year = static_cast<int>(cs.year() - 1900);
-  }
-
-  switch (GetWeekday(cs)) {
-    case Weekday::sunday:
-      tm.tm_wday = 0;
-      break;
-    case Weekday::monday:
-      tm.tm_wday = 1;
-      break;
-    case Weekday::tuesday:
-      tm.tm_wday = 2;
-      break;
-    case Weekday::wednesday:
-      tm.tm_wday = 3;
-      break;
-    case Weekday::thursday:
-      tm.tm_wday = 4;
-      break;
-    case Weekday::friday:
-      tm.tm_wday = 5;
-      break;
-    case Weekday::saturday:
-      tm.tm_wday = 6;
-      break;
-  }
-  tm.tm_yday = GetYearDay(cs) - 1;
-  tm.tm_isdst = ci.is_dst ? 1 : 0;
-
-  return tm;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/time/time.h b/third_party/abseil_cpp/absl/time/time.h
deleted file mode 100644
index 72508031ac..0000000000
--- a/third_party/abseil_cpp/absl/time/time.h
+++ /dev/null
@@ -1,1581 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// File: time.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines abstractions for computing with absolute points
-// in time, durations of time, and formatting and parsing time within a given
-// time zone. The following abstractions are defined:
-//
-//  * `absl::Time` defines an absolute, specific instance in time
-//  * `absl::Duration` defines a signed, fixed-length span of time
-//  * `absl::TimeZone` defines geopolitical time zone regions (as collected
-//     within the IANA Time Zone database (https://www.iana.org/time-zones)).
-//
-// Note: Absolute times are distinct from civil times, which refer to the
-// human-scale time commonly represented by `YYYY-MM-DD hh:mm:ss`. The mapping
-// between absolute and civil times can be specified by use of time zones
-// (`absl::TimeZone` within this API). That is:
-//
-//   Civil Time = F(Absolute Time, Time Zone)
-//   Absolute Time = G(Civil Time, Time Zone)
-//
-// See civil_time.h for abstractions related to constructing and manipulating
-// civil time.
-//
-// Example:
-//
-//   absl::TimeZone nyc;
-//   // LoadTimeZone() may fail so it's always better to check for success.
-//   if (!absl::LoadTimeZone("America/New_York", &nyc)) {
-//      // handle error case
-//   }
-//
-//   // My flight leaves NYC on Jan 2, 2017 at 03:04:05
-//   absl::CivilSecond cs(2017, 1, 2, 3, 4, 5);
-//   absl::Time takeoff = absl::FromCivil(cs, nyc);
-//
-//   absl::Duration flight_duration = absl::Hours(21) + absl::Minutes(35);
-//   absl::Time landing = takeoff + flight_duration;
-//
-//   absl::TimeZone syd;
-//   if (!absl::LoadTimeZone("Australia/Sydney", &syd)) {
-//      // handle error case
-//   }
-//   std::string s = absl::FormatTime(
-//       "My flight will land in Sydney on %Y-%m-%d at %H:%M:%S",
-//       landing, syd);
-
-#ifndef ABSL_TIME_TIME_H_
-#define ABSL_TIME_TIME_H_
-
-#if !defined(_MSC_VER)
-#include <sys/time.h>
-#else
-// We don't include `winsock2.h` because it drags in `windows.h` and friends,
-// and they define conflicting macros like OPAQUE, ERROR, and more. This has the
-// potential to break Abseil users.
-//
-// Instead we only forward declare `timeval` and require Windows users include
-// `winsock2.h` themselves. This is both inconsistent and troublesome, but so is
-// including 'windows.h' so we are picking the lesser of two evils here.
-struct timeval;
-#endif
-#include <chrono>  // NOLINT(build/c++11)
-#include <cmath>
-#include <cstdint>
-#include <ctime>
-#include <ostream>
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/macros.h"
-#include "absl/strings/string_view.h"
-#include "absl/time/civil_time.h"
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class Duration;  // Defined below
-class Time;      // Defined below
-class TimeZone;  // Defined below
-
-namespace time_internal {
-int64_t IDivDuration(bool satq, Duration num, Duration den, Duration* rem);
-constexpr Time FromUnixDuration(Duration d);
-constexpr Duration ToUnixDuration(Time t);
-constexpr int64_t GetRepHi(Duration d);
-constexpr uint32_t GetRepLo(Duration d);
-constexpr Duration MakeDuration(int64_t hi, uint32_t lo);
-constexpr Duration MakeDuration(int64_t hi, int64_t lo);
-inline Duration MakePosDoubleDuration(double n);
-constexpr int64_t kTicksPerNanosecond = 4;
-constexpr int64_t kTicksPerSecond = 1000 * 1000 * 1000 * kTicksPerNanosecond;
-template <std::intmax_t N>
-constexpr Duration FromInt64(int64_t v, std::ratio<1, N>);
-constexpr Duration FromInt64(int64_t v, std::ratio<60>);
-constexpr Duration FromInt64(int64_t v, std::ratio<3600>);
-template <typename T>
-using EnableIfIntegral = typename std::enable_if<
-    std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
-template <typename T>
-using EnableIfFloat =
-    typename std::enable_if<std::is_floating_point<T>::value, int>::type;
-}  // namespace time_internal
-
-// Duration
-//
-// The `absl::Duration` class represents a signed, fixed-length span of time.
-// A `Duration` is generated using a unit-specific factory function, or is
-// the result of subtracting one `absl::Time` from another. Durations behave
-// like unit-safe integers and they support all the natural integer-like
-// arithmetic operations. Arithmetic overflows and saturates at +/- infinity.
-// `Duration` should be passed by value rather than const reference.
-//
-// Factory functions `Nanoseconds()`, `Microseconds()`, `Milliseconds()`,
-// `Seconds()`, `Minutes()`, `Hours()` and `InfiniteDuration()` allow for
-// creation of constexpr `Duration` values
-//
-// Examples:
-//
-//   constexpr absl::Duration ten_ns = absl::Nanoseconds(10);
-//   constexpr absl::Duration min = absl::Minutes(1);
-//   constexpr absl::Duration hour = absl::Hours(1);
-//   absl::Duration dur = 60 * min;  // dur == hour
-//   absl::Duration half_sec = absl::Milliseconds(500);
-//   absl::Duration quarter_sec = 0.25 * absl::Seconds(1);
-//
-// `Duration` values can be easily converted to an integral number of units
-// using the division operator.
-//
-// Example:
-//
-//   constexpr absl::Duration dur = absl::Milliseconds(1500);
-//   int64_t ns = dur / absl::Nanoseconds(1);   // ns == 1500000000
-//   int64_t ms = dur / absl::Milliseconds(1);  // ms == 1500
-//   int64_t sec = dur / absl::Seconds(1);    // sec == 1 (subseconds truncated)
-//   int64_t min = dur / absl::Minutes(1);    // min == 0
-//
-// See the `IDivDuration()` and `FDivDuration()` functions below for details on
-// how to access the fractional parts of the quotient.
-//
-// Alternatively, conversions can be performed using helpers such as
-// `ToInt64Microseconds()` and `ToDoubleSeconds()`.
-class Duration {
- public:
-  // Value semantics.
-  constexpr Duration() : rep_hi_(0), rep_lo_(0) {}  // zero-length duration
-
-  // Copyable.
-#if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1910
-  // Explicitly defining the constexpr copy constructor avoids an MSVC bug.
-  constexpr Duration(const Duration& d)
-      : rep_hi_(d.rep_hi_), rep_lo_(d.rep_lo_) {}
-#else
-  constexpr Duration(const Duration& d) = default;
-#endif
-  Duration& operator=(const Duration& d) = default;
-
-  // Compound assignment operators.
-  Duration& operator+=(Duration d);
-  Duration& operator-=(Duration d);
-  Duration& operator*=(int64_t r);
-  Duration& operator*=(double r);
-  Duration& operator/=(int64_t r);
-  Duration& operator/=(double r);
-  Duration& operator%=(Duration rhs);
-
-  // Overloads that forward to either the int64_t or double overloads above.
-  // Integer operands must be representable as int64_t.
-  template <typename T>
-  Duration& operator*=(T r) {
-    int64_t x = r;
-    return *this *= x;
-  }
-  template <typename T>
-  Duration& operator/=(T r) {
-    int64_t x = r;
-    return *this /= x;
-  }
-  Duration& operator*=(float r) { return *this *= static_cast<double>(r); }
-  Duration& operator/=(float r) { return *this /= static_cast<double>(r); }
-
-  template <typename H>
-  friend H AbslHashValue(H h, Duration d) {
-    return H::combine(std::move(h), d.rep_hi_, d.rep_lo_);
-  }
-
- private:
-  friend constexpr int64_t time_internal::GetRepHi(Duration d);
-  friend constexpr uint32_t time_internal::GetRepLo(Duration d);
-  friend constexpr Duration time_internal::MakeDuration(int64_t hi,
-                                                        uint32_t lo);
-  constexpr Duration(int64_t hi, uint32_t lo) : rep_hi_(hi), rep_lo_(lo) {}
-  int64_t rep_hi_;
-  uint32_t rep_lo_;
-};
-
-// Relational Operators
-constexpr bool operator<(Duration lhs, Duration rhs);
-constexpr bool operator>(Duration lhs, Duration rhs) { return rhs < lhs; }
-constexpr bool operator>=(Duration lhs, Duration rhs) { return !(lhs < rhs); }
-constexpr bool operator<=(Duration lhs, Duration rhs) { return !(rhs < lhs); }
-constexpr bool operator==(Duration lhs, Duration rhs);
-constexpr bool operator!=(Duration lhs, Duration rhs) { return !(lhs == rhs); }
-
-// Additive Operators
-constexpr Duration operator-(Duration d);
-inline Duration operator+(Duration lhs, Duration rhs) { return lhs += rhs; }
-inline Duration operator-(Duration lhs, Duration rhs) { return lhs -= rhs; }
-
-// Multiplicative Operators
-// Integer operands must be representable as int64_t.
-template <typename T>
-Duration operator*(Duration lhs, T rhs) {
-  return lhs *= rhs;
-}
-template <typename T>
-Duration operator*(T lhs, Duration rhs) {
-  return rhs *= lhs;
-}
-template <typename T>
-Duration operator/(Duration lhs, T rhs) {
-  return lhs /= rhs;
-}
-inline int64_t operator/(Duration lhs, Duration rhs) {
-  return time_internal::IDivDuration(true, lhs, rhs,
-                                     &lhs);  // trunc towards zero
-}
-inline Duration operator%(Duration lhs, Duration rhs) { return lhs %= rhs; }
-
-// IDivDuration()
-//
-// Divides a numerator `Duration` by a denominator `Duration`, returning the
-// quotient and remainder. The remainder always has the same sign as the
-// numerator. The returned quotient and remainder respect the identity:
-//
-//   numerator = denominator * quotient + remainder
-//
-// Returned quotients are capped to the range of `int64_t`, with the difference
-// spilling into the remainder to uphold the above identity. This means that the
-// remainder returned could differ from the remainder returned by
-// `Duration::operator%` for huge quotients.
-//
-// See also the notes on `InfiniteDuration()` below regarding the behavior of
-// division involving zero and infinite durations.
-//
-// Example:
-//
-//   constexpr absl::Duration a =
-//       absl::Seconds(std::numeric_limits<int64_t>::max());  // big
-//   constexpr absl::Duration b = absl::Nanoseconds(1);       // small
-//
-//   absl::Duration rem = a % b;
-//   // rem == absl::ZeroDuration()
-//
-//   // Here, q would overflow int64_t, so rem accounts for the difference.
-//   int64_t q = absl::IDivDuration(a, b, &rem);
-//   // q == std::numeric_limits<int64_t>::max(), rem == a - b * q
-inline int64_t IDivDuration(Duration num, Duration den, Duration* rem) {
-  return time_internal::IDivDuration(true, num, den,
-                                     rem);  // trunc towards zero
-}
-
-// FDivDuration()
-//
-// Divides a `Duration` numerator into a fractional number of units of a
-// `Duration` denominator.
-//
-// See also the notes on `InfiniteDuration()` below regarding the behavior of
-// division involving zero and infinite durations.
-//
-// Example:
-//
-//   double d = absl::FDivDuration(absl::Milliseconds(1500), absl::Seconds(1));
-//   // d == 1.5
-double FDivDuration(Duration num, Duration den);
-
-// ZeroDuration()
-//
-// Returns a zero-length duration. This function behaves just like the default
-// constructor, but the name helps make the semantics clear at call sites.
-constexpr Duration ZeroDuration() { return Duration(); }
-
-// AbsDuration()
-//
-// Returns the absolute value of a duration.
-inline Duration AbsDuration(Duration d) {
-  return (d < ZeroDuration()) ? -d : d;
-}
-
-// Trunc()
-//
-// Truncates a duration (toward zero) to a multiple of a non-zero unit.
-//
-// Example:
-//
-//   absl::Duration d = absl::Nanoseconds(123456789);
-//   absl::Duration a = absl::Trunc(d, absl::Microseconds(1));  // 123456us
-Duration Trunc(Duration d, Duration unit);
-
-// Floor()
-//
-// Floors a duration using the passed duration unit to its largest value not
-// greater than the duration.
-//
-// Example:
-//
-//   absl::Duration d = absl::Nanoseconds(123456789);
-//   absl::Duration b = absl::Floor(d, absl::Microseconds(1));  // 123456us
-Duration Floor(Duration d, Duration unit);
-
-// Ceil()
-//
-// Returns the ceiling of a duration using the passed duration unit to its
-// smallest value not less than the duration.
-//
-// Example:
-//
-//   absl::Duration d = absl::Nanoseconds(123456789);
-//   absl::Duration c = absl::Ceil(d, absl::Microseconds(1));   // 123457us
-Duration Ceil(Duration d, Duration unit);
-
-// InfiniteDuration()
-//
-// Returns an infinite `Duration`.  To get a `Duration` representing negative
-// infinity, use `-InfiniteDuration()`.
-//
-// Duration arithmetic overflows to +/- infinity and saturates. In general,
-// arithmetic with `Duration` infinities is similar to IEEE 754 infinities
-// except where IEEE 754 NaN would be involved, in which case +/-
-// `InfiniteDuration()` is used in place of a "nan" Duration.
-//
-// Examples:
-//
-//   constexpr absl::Duration inf = absl::InfiniteDuration();
-//   const absl::Duration d = ... any finite duration ...
-//
-//   inf == inf + inf
-//   inf == inf + d
-//   inf == inf - inf
-//   -inf == d - inf
-//
-//   inf == d * 1e100
-//   inf == inf / 2
-//   0 == d / inf
-//   INT64_MAX == inf / d
-//
-//   d < inf
-//   -inf < d
-//
-//   // Division by zero returns infinity, or INT64_MIN/MAX where appropriate.
-//   inf == d / 0
-//   INT64_MAX == d / absl::ZeroDuration()
-//
-// The examples involving the `/` operator above also apply to `IDivDuration()`
-// and `FDivDuration()`.
-constexpr Duration InfiniteDuration();
-
-// Nanoseconds()
-// Microseconds()
-// Milliseconds()
-// Seconds()
-// Minutes()
-// Hours()
-//
-// Factory functions for constructing `Duration` values from an integral number
-// of the unit indicated by the factory function's name. The number must be
-// representable as int64_t.
-//
-// NOTE: no "Days()" factory function exists because "a day" is ambiguous.
-// Civil days are not always 24 hours long, and a 24-hour duration often does
-// not correspond with a civil day. If a 24-hour duration is needed, use
-// `absl::Hours(24)`. If you actually want a civil day, use absl::CivilDay
-// from civil_time.h.
-//
-// Example:
-//
-//   absl::Duration a = absl::Seconds(60);
-//   absl::Duration b = absl::Minutes(1);  // b == a
-constexpr Duration Nanoseconds(int64_t n);
-constexpr Duration Microseconds(int64_t n);
-constexpr Duration Milliseconds(int64_t n);
-constexpr Duration Seconds(int64_t n);
-constexpr Duration Minutes(int64_t n);
-constexpr Duration Hours(int64_t n);
-
-// Factory overloads for constructing `Duration` values from a floating-point
-// number of the unit indicated by the factory function's name. These functions
-// exist for convenience, but they are not as efficient as the integral
-// factories, which should be preferred.
-//
-// Example:
-//
-//   auto a = absl::Seconds(1.5);        // OK
-//   auto b = absl::Milliseconds(1500);  // BETTER
-template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Nanoseconds(T n) {
-  return n * Nanoseconds(1);
-}
-template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Microseconds(T n) {
-  return n * Microseconds(1);
-}
-template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Milliseconds(T n) {
-  return n * Milliseconds(1);
-}
-template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Seconds(T n) {
-  if (n >= 0) {  // Note: `NaN >= 0` is false.
-    if (n >= static_cast<T>((std::numeric_limits<int64_t>::max)())) {
-      return InfiniteDuration();
-    }
-    return time_internal::MakePosDoubleDuration(n);
-  } else {
-    if (std::isnan(n))
-      return std::signbit(n) ? -InfiniteDuration() : InfiniteDuration();
-    if (n <= (std::numeric_limits<int64_t>::min)()) return -InfiniteDuration();
-    return -time_internal::MakePosDoubleDuration(-n);
-  }
-}
-template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Minutes(T n) {
-  return n * Minutes(1);
-}
-template <typename T, time_internal::EnableIfFloat<T> = 0>
-Duration Hours(T n) {
-  return n * Hours(1);
-}
-
-// ToInt64Nanoseconds()
-// ToInt64Microseconds()
-// ToInt64Milliseconds()
-// ToInt64Seconds()
-// ToInt64Minutes()
-// ToInt64Hours()
-//
-// Helper functions that convert a Duration to an integral count of the
-// indicated unit. These functions are shorthand for the `IDivDuration()`
-// function above; see its documentation for details about overflow, etc.
-//
-// Example:
-//
-//   absl::Duration d = absl::Milliseconds(1500);
-//   int64_t isec = absl::ToInt64Seconds(d);  // isec == 1
-int64_t ToInt64Nanoseconds(Duration d);
-int64_t ToInt64Microseconds(Duration d);
-int64_t ToInt64Milliseconds(Duration d);
-int64_t ToInt64Seconds(Duration d);
-int64_t ToInt64Minutes(Duration d);
-int64_t ToInt64Hours(Duration d);
-
-// ToDoubleNanoSeconds()
-// ToDoubleMicroseconds()
-// ToDoubleMilliseconds()
-// ToDoubleSeconds()
-// ToDoubleMinutes()
-// ToDoubleHours()
-//
-// Helper functions that convert a Duration to a floating point count of the
-// indicated unit. These functions are shorthand for the `FDivDuration()`
-// function above; see its documentation for details about overflow, etc.
-//
-// Example:
-//
-//   absl::Duration d = absl::Milliseconds(1500);
-//   double dsec = absl::ToDoubleSeconds(d);  // dsec == 1.5
-double ToDoubleNanoseconds(Duration d);
-double ToDoubleMicroseconds(Duration d);
-double ToDoubleMilliseconds(Duration d);
-double ToDoubleSeconds(Duration d);
-double ToDoubleMinutes(Duration d);
-double ToDoubleHours(Duration d);
-
-// FromChrono()
-//
-// Converts any of the pre-defined std::chrono durations to an absl::Duration.
-//
-// Example:
-//
-//   std::chrono::milliseconds ms(123);
-//   absl::Duration d = absl::FromChrono(ms);
-constexpr Duration FromChrono(const std::chrono::nanoseconds& d);
-constexpr Duration FromChrono(const std::chrono::microseconds& d);
-constexpr Duration FromChrono(const std::chrono::milliseconds& d);
-constexpr Duration FromChrono(const std::chrono::seconds& d);
-constexpr Duration FromChrono(const std::chrono::minutes& d);
-constexpr Duration FromChrono(const std::chrono::hours& d);
-
-// ToChronoNanoseconds()
-// ToChronoMicroseconds()
-// ToChronoMilliseconds()
-// ToChronoSeconds()
-// ToChronoMinutes()
-// ToChronoHours()
-//
-// Converts an absl::Duration to any of the pre-defined std::chrono durations.
-// If overflow would occur, the returned value will saturate at the min/max
-// chrono duration value instead.
-//
-// Example:
-//
-//   absl::Duration d = absl::Microseconds(123);
-//   auto x = absl::ToChronoMicroseconds(d);
-//   auto y = absl::ToChronoNanoseconds(d);  // x == y
-//   auto z = absl::ToChronoSeconds(absl::InfiniteDuration());
-//   // z == std::chrono::seconds::max()
-std::chrono::nanoseconds ToChronoNanoseconds(Duration d);
-std::chrono::microseconds ToChronoMicroseconds(Duration d);
-std::chrono::milliseconds ToChronoMilliseconds(Duration d);
-std::chrono::seconds ToChronoSeconds(Duration d);
-std::chrono::minutes ToChronoMinutes(Duration d);
-std::chrono::hours ToChronoHours(Duration d);
-
-// FormatDuration()
-//
-// Returns a string representing the duration in the form "72h3m0.5s".
-// Returns "inf" or "-inf" for +/- `InfiniteDuration()`.
-std::string FormatDuration(Duration d);
-
-// Output stream operator.
-inline std::ostream& operator<<(std::ostream& os, Duration d) {
-  return os << FormatDuration(d);
-}
-
-// ParseDuration()
-//
-// Parses a duration string consisting of a possibly signed sequence of
-// decimal numbers, each with an optional fractional part and a unit
-// suffix.  The valid suffixes are "ns", "us" "ms", "s", "m", and "h".
-// Simple examples include "300ms", "-1.5h", and "2h45m".  Parses "0" as
-// `ZeroDuration()`. Parses "inf" and "-inf" as +/- `InfiniteDuration()`.
-bool ParseDuration(absl::string_view dur_string, Duration* d);
-
-// Support for flag values of type Duration. Duration flags must be specified
-// in a format that is valid input for absl::ParseDuration().
-bool AbslParseFlag(absl::string_view text, Duration* dst, std::string* error);
-std::string AbslUnparseFlag(Duration d);
-ABSL_DEPRECATED("Use AbslParseFlag() instead.")
-bool ParseFlag(const std::string& text, Duration* dst, std::string* error);
-ABSL_DEPRECATED("Use AbslUnparseFlag() instead.")
-std::string UnparseFlag(Duration d);
-
-// Time
-//
-// An `absl::Time` represents a specific instant in time. Arithmetic operators
-// are provided for naturally expressing time calculations. Instances are
-// created using `absl::Now()` and the `absl::From*()` factory functions that
-// accept the gamut of other time representations. Formatting and parsing
-// functions are provided for conversion to and from strings.  `absl::Time`
-// should be passed by value rather than const reference.
-//
-// `absl::Time` assumes there are 60 seconds in a minute, which means the
-// underlying time scales must be "smeared" to eliminate leap seconds.
-// See https://developers.google.com/time/smear.
-//
-// Even though `absl::Time` supports a wide range of timestamps, exercise
-// caution when using values in the distant past. `absl::Time` uses the
-// Proleptic Gregorian calendar, which extends the Gregorian calendar backward
-// to dates before its introduction in 1582.
-// See https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
-// for more information. Use the ICU calendar classes to convert a date in
-// some other calendar (http://userguide.icu-project.org/datetime/calendar).
-//
-// Similarly, standardized time zones are a reasonably recent innovation, with
-// the Greenwich prime meridian being established in 1884. The TZ database
-// itself does not profess accurate offsets for timestamps prior to 1970. The
-// breakdown of future timestamps is subject to the whim of regional
-// governments.
-//
-// The `absl::Time` class represents an instant in time as a count of clock
-// ticks of some granularity (resolution) from some starting point (epoch).
-//
-// `absl::Time` uses a resolution that is high enough to avoid loss in
-// precision, and a range that is wide enough to avoid overflow, when
-// converting between tick counts in most Google time scales (i.e., resolution
-// of at least one nanosecond, and range +/-100 billion years).  Conversions
-// between the time scales are performed by truncating (towards negative
-// infinity) to the nearest representable point.
-//
-// Examples:
-//
-//   absl::Time t1 = ...;
-//   absl::Time t2 = t1 + absl::Minutes(2);
-//   absl::Duration d = t2 - t1;  // == absl::Minutes(2)
-//
-class Time {
- public:
-  // Value semantics.
-
-  // Returns the Unix epoch.  However, those reading your code may not know
-  // or expect the Unix epoch as the default value, so make your code more
-  // readable by explicitly initializing all instances before use.
-  //
-  // Example:
-  //   absl::Time t = absl::UnixEpoch();
-  //   absl::Time t = absl::Now();
-  //   absl::Time t = absl::TimeFromTimeval(tv);
-  //   absl::Time t = absl::InfinitePast();
-  constexpr Time() = default;
-
-  // Copyable.
-  constexpr Time(const Time& t) = default;
-  Time& operator=(const Time& t) = default;
-
-  // Assignment operators.
-  Time& operator+=(Duration d) {
-    rep_ += d;
-    return *this;
-  }
-  Time& operator-=(Duration d) {
-    rep_ -= d;
-    return *this;
-  }
-
-  // Time::Breakdown
-  //
-  // The calendar and wall-clock (aka "civil time") components of an
-  // `absl::Time` in a certain `absl::TimeZone`. This struct is not
-  // intended to represent an instant in time. So, rather than passing
-  // a `Time::Breakdown` to a function, pass an `absl::Time` and an
-  // `absl::TimeZone`.
-  //
-  // Deprecated. Use `absl::TimeZone::CivilInfo`.
-  struct
-      Breakdown {
-    int64_t year;        // year (e.g., 2013)
-    int month;           // month of year [1:12]
-    int day;             // day of month [1:31]
-    int hour;            // hour of day [0:23]
-    int minute;          // minute of hour [0:59]
-    int second;          // second of minute [0:59]
-    Duration subsecond;  // [Seconds(0):Seconds(1)) if finite
-    int weekday;         // 1==Mon, ..., 7=Sun
-    int yearday;         // day of year [1:366]
-
-    // Note: The following fields exist for backward compatibility
-    // with older APIs.  Accessing these fields directly is a sign of
-    // imprudent logic in the calling code.  Modern time-related code
-    // should only access this data indirectly by way of FormatTime().
-    // These fields are undefined for InfiniteFuture() and InfinitePast().
-    int offset;             // seconds east of UTC
-    bool is_dst;            // is offset non-standard?
-    const char* zone_abbr;  // time-zone abbreviation (e.g., "PST")
-  };
-
-  // Time::In()
-  //
-  // Returns the breakdown of this instant in the given TimeZone.
-  //
-  // Deprecated. Use `absl::TimeZone::At(Time)`.
-  Breakdown In(TimeZone tz) const;
-
-  template <typename H>
-  friend H AbslHashValue(H h, Time t) {
-    return H::combine(std::move(h), t.rep_);
-  }
-
- private:
-  friend constexpr Time time_internal::FromUnixDuration(Duration d);
-  friend constexpr Duration time_internal::ToUnixDuration(Time t);
-  friend constexpr bool operator<(Time lhs, Time rhs);
-  friend constexpr bool operator==(Time lhs, Time rhs);
-  friend Duration operator-(Time lhs, Time rhs);
-  friend constexpr Time UniversalEpoch();
-  friend constexpr Time InfiniteFuture();
-  friend constexpr Time InfinitePast();
-  constexpr explicit Time(Duration rep) : rep_(rep) {}
-  Duration rep_;
-};
-
-// Relational Operators
-constexpr bool operator<(Time lhs, Time rhs) { return lhs.rep_ < rhs.rep_; }
-constexpr bool operator>(Time lhs, Time rhs) { return rhs < lhs; }
-constexpr bool operator>=(Time lhs, Time rhs) { return !(lhs < rhs); }
-constexpr bool operator<=(Time lhs, Time rhs) { return !(rhs < lhs); }
-constexpr bool operator==(Time lhs, Time rhs) { return lhs.rep_ == rhs.rep_; }
-constexpr bool operator!=(Time lhs, Time rhs) { return !(lhs == rhs); }
-
-// Additive Operators
-inline Time operator+(Time lhs, Duration rhs) { return lhs += rhs; }
-inline Time operator+(Duration lhs, Time rhs) { return rhs += lhs; }
-inline Time operator-(Time lhs, Duration rhs) { return lhs -= rhs; }
-inline Duration operator-(Time lhs, Time rhs) { return lhs.rep_ - rhs.rep_; }
-
-// UnixEpoch()
-//
-// Returns the `absl::Time` representing "1970-01-01 00:00:00.0 +0000".
-constexpr Time UnixEpoch() { return Time(); }
-
-// UniversalEpoch()
-//
-// Returns the `absl::Time` representing "0001-01-01 00:00:00.0 +0000", the
-// epoch of the ICU Universal Time Scale.
-constexpr Time UniversalEpoch() {
-  // 719162 is the number of days from 0001-01-01 to 1970-01-01,
-  // assuming the Gregorian calendar.
-  return Time(time_internal::MakeDuration(-24 * 719162 * int64_t{3600}, 0U));
-}
-
-// InfiniteFuture()
-//
-// Returns an `absl::Time` that is infinitely far in the future.
-constexpr Time InfiniteFuture() {
-  return Time(
-      time_internal::MakeDuration((std::numeric_limits<int64_t>::max)(), ~0U));
-}
-
-// InfinitePast()
-//
-// Returns an `absl::Time` that is infinitely far in the past.
-constexpr Time InfinitePast() {
-  return Time(
-      time_internal::MakeDuration((std::numeric_limits<int64_t>::min)(), ~0U));
-}
-
-// FromUnixNanos()
-// FromUnixMicros()
-// FromUnixMillis()
-// FromUnixSeconds()
-// FromTimeT()
-// FromUDate()
-// FromUniversal()
-//
-// Creates an `absl::Time` from a variety of other representations.
-constexpr Time FromUnixNanos(int64_t ns);
-constexpr Time FromUnixMicros(int64_t us);
-constexpr Time FromUnixMillis(int64_t ms);
-constexpr Time FromUnixSeconds(int64_t s);
-constexpr Time FromTimeT(time_t t);
-Time FromUDate(double udate);
-Time FromUniversal(int64_t universal);
-
-// ToUnixNanos()
-// ToUnixMicros()
-// ToUnixMillis()
-// ToUnixSeconds()
-// ToTimeT()
-// ToUDate()
-// ToUniversal()
-//
-// Converts an `absl::Time` to a variety of other representations.  Note that
-// these operations round down toward negative infinity where necessary to
-// adjust to the resolution of the result type.  Beware of possible time_t
-// over/underflow in ToTime{T,val,spec}() on 32-bit platforms.
-int64_t ToUnixNanos(Time t);
-int64_t ToUnixMicros(Time t);
-int64_t ToUnixMillis(Time t);
-int64_t ToUnixSeconds(Time t);
-time_t ToTimeT(Time t);
-double ToUDate(Time t);
-int64_t ToUniversal(Time t);
-
-// DurationFromTimespec()
-// DurationFromTimeval()
-// ToTimespec()
-// ToTimeval()
-// TimeFromTimespec()
-// TimeFromTimeval()
-// ToTimespec()
-// ToTimeval()
-//
-// Some APIs use a timespec or a timeval as a Duration (e.g., nanosleep(2)
-// and select(2)), while others use them as a Time (e.g. clock_gettime(2)
-// and gettimeofday(2)), so conversion functions are provided for both cases.
-// The "to timespec/val" direction is easily handled via overloading, but
-// for "from timespec/val" the desired type is part of the function name.
-Duration DurationFromTimespec(timespec ts);
-Duration DurationFromTimeval(timeval tv);
-timespec ToTimespec(Duration d);
-timeval ToTimeval(Duration d);
-Time TimeFromTimespec(timespec ts);
-Time TimeFromTimeval(timeval tv);
-timespec ToTimespec(Time t);
-timeval ToTimeval(Time t);
-
-// FromChrono()
-//
-// Converts a std::chrono::system_clock::time_point to an absl::Time.
-//
-// Example:
-//
-//   auto tp = std::chrono::system_clock::from_time_t(123);
-//   absl::Time t = absl::FromChrono(tp);
-//   // t == absl::FromTimeT(123)
-Time FromChrono(const std::chrono::system_clock::time_point& tp);
-
-// ToChronoTime()
-//
-// Converts an absl::Time to a std::chrono::system_clock::time_point. If
-// overflow would occur, the returned value will saturate at the min/max time
-// point value instead.
-//
-// Example:
-//
-//   absl::Time t = absl::FromTimeT(123);
-//   auto tp = absl::ToChronoTime(t);
-//   // tp == std::chrono::system_clock::from_time_t(123);
-std::chrono::system_clock::time_point ToChronoTime(Time);
-
-// Support for flag values of type Time. Time flags must be specified in a
-// format that matches absl::RFC3339_full. For example:
-//
-//   --start_time=2016-01-02T03:04:05.678+08:00
-//
-// Note: A UTC offset (or 'Z' indicating a zero-offset from UTC) is required.
-//
-// Additionally, if you'd like to specify a time as a count of
-// seconds/milliseconds/etc from the Unix epoch, use an absl::Duration flag
-// and add that duration to absl::UnixEpoch() to get an absl::Time.
-bool AbslParseFlag(absl::string_view text, Time* t, std::string* error);
-std::string AbslUnparseFlag(Time t);
-ABSL_DEPRECATED("Use AbslParseFlag() instead.")
-bool ParseFlag(const std::string& text, Time* t, std::string* error);
-ABSL_DEPRECATED("Use AbslUnparseFlag() instead.")
-std::string UnparseFlag(Time t);
-
-// TimeZone
-//
-// The `absl::TimeZone` is an opaque, small, value-type class representing a
-// geo-political region within which particular rules are used for converting
-// between absolute and civil times (see https://git.io/v59Ly). `absl::TimeZone`
-// values are named using the TZ identifiers from the IANA Time Zone Database,
-// such as "America/Los_Angeles" or "Australia/Sydney". `absl::TimeZone` values
-// are created from factory functions such as `absl::LoadTimeZone()`. Note:
-// strings like "PST" and "EDT" are not valid TZ identifiers. Prefer to pass by
-// value rather than const reference.
-//
-// For more on the fundamental concepts of time zones, absolute times, and civil
-// times, see https://github.com/google/cctz#fundamental-concepts
-//
-// Examples:
-//
-//   absl::TimeZone utc = absl::UTCTimeZone();
-//   absl::TimeZone pst = absl::FixedTimeZone(-8 * 60 * 60);
-//   absl::TimeZone loc = absl::LocalTimeZone();
-//   absl::TimeZone lax;
-//   if (!absl::LoadTimeZone("America/Los_Angeles", &lax)) {
-//     // handle error case
-//   }
-//
-// See also:
-// - https://github.com/google/cctz
-// - https://www.iana.org/time-zones
-// - https://en.wikipedia.org/wiki/Zoneinfo
-class TimeZone {
- public:
-  explicit TimeZone(time_internal::cctz::time_zone tz) : cz_(tz) {}
-  TimeZone() = default;  // UTC, but prefer UTCTimeZone() to be explicit.
-
-  // Copyable.
-  TimeZone(const TimeZone&) = default;
-  TimeZone& operator=(const TimeZone&) = default;
-
-  explicit operator time_internal::cctz::time_zone() const { return cz_; }
-
-  std::string name() const { return cz_.name(); }
-
-  // TimeZone::CivilInfo
-  //
-  // Information about the civil time corresponding to an absolute time.
-  // This struct is not intended to represent an instant in time. So, rather
-  // than passing a `TimeZone::CivilInfo` to a function, pass an `absl::Time`
-  // and an `absl::TimeZone`.
-  struct CivilInfo {
-    CivilSecond cs;
-    Duration subsecond;
-
-    // Note: The following fields exist for backward compatibility
-    // with older APIs.  Accessing these fields directly is a sign of
-    // imprudent logic in the calling code.  Modern time-related code
-    // should only access this data indirectly by way of FormatTime().
-    // These fields are undefined for InfiniteFuture() and InfinitePast().
-    int offset;             // seconds east of UTC
-    bool is_dst;            // is offset non-standard?
-    const char* zone_abbr;  // time-zone abbreviation (e.g., "PST")
-  };
-
-  // TimeZone::At(Time)
-  //
-  // Returns the civil time for this TimeZone at a certain `absl::Time`.
-  // If the input time is infinite, the output civil second will be set to
-  // CivilSecond::max() or min(), and the subsecond will be infinite.
-  //
-  // Example:
-  //
-  //   const auto epoch = lax.At(absl::UnixEpoch());
-  //   // epoch.cs == 1969-12-31 16:00:00
-  //   // epoch.subsecond == absl::ZeroDuration()
-  //   // epoch.offset == -28800
-  //   // epoch.is_dst == false
-  //   // epoch.abbr == "PST"
-  CivilInfo At(Time t) const;
-
-  // TimeZone::TimeInfo
-  //
-  // Information about the absolute times corresponding to a civil time.
-  // (Subseconds must be handled separately.)
-  //
-  // It is possible for a caller to pass a civil-time value that does
-  // not represent an actual or unique instant in time (due to a shift
-  // in UTC offset in the TimeZone, which results in a discontinuity in
-  // the civil-time components). For example, a daylight-saving-time
-  // transition skips or repeats civil times---in the United States,
-  // March 13, 2011 02:15 never occurred, while November 6, 2011 01:15
-  // occurred twice---so requests for such times are not well-defined.
-  // To account for these possibilities, `absl::TimeZone::TimeInfo` is
-  // richer than just a single `absl::Time`.
-  struct TimeInfo {
-    enum CivilKind {
-      UNIQUE,    // the civil time was singular (pre == trans == post)
-      SKIPPED,   // the civil time did not exist (pre >= trans > post)
-      REPEATED,  // the civil time was ambiguous (pre < trans <= post)
-    } kind;
-    Time pre;    // time calculated using the pre-transition offset
-    Time trans;  // when the civil-time discontinuity occurred
-    Time post;   // time calculated using the post-transition offset
-  };
-
-  // TimeZone::At(CivilSecond)
-  //
-  // Returns an `absl::TimeInfo` containing the absolute time(s) for this
-  // TimeZone at an `absl::CivilSecond`. When the civil time is skipped or
-  // repeated, returns times calculated using the pre-transition and post-
-  // transition UTC offsets, plus the transition time itself.
-  //
-  // Examples:
-  //
-  //   // A unique civil time
-  //   const auto jan01 = lax.At(absl::CivilSecond(2011, 1, 1, 0, 0, 0));
-  //   // jan01.kind == TimeZone::TimeInfo::UNIQUE
-  //   // jan01.pre    is 2011-01-01 00:00:00 -0800
-  //   // jan01.trans  is 2011-01-01 00:00:00 -0800
-  //   // jan01.post   is 2011-01-01 00:00:00 -0800
-  //
-  //   // A Spring DST transition, when there is a gap in civil time
-  //   const auto mar13 = lax.At(absl::CivilSecond(2011, 3, 13, 2, 15, 0));
-  //   // mar13.kind == TimeZone::TimeInfo::SKIPPED
-  //   // mar13.pre   is 2011-03-13 03:15:00 -0700
-  //   // mar13.trans is 2011-03-13 03:00:00 -0700
-  //   // mar13.post  is 2011-03-13 01:15:00 -0800
-  //
-  //   // A Fall DST transition, when civil times are repeated
-  //   const auto nov06 = lax.At(absl::CivilSecond(2011, 11, 6, 1, 15, 0));
-  //   // nov06.kind == TimeZone::TimeInfo::REPEATED
-  //   // nov06.pre   is 2011-11-06 01:15:00 -0700
-  //   // nov06.trans is 2011-11-06 01:00:00 -0800
-  //   // nov06.post  is 2011-11-06 01:15:00 -0800
-  TimeInfo At(CivilSecond ct) const;
-
-  // TimeZone::NextTransition()
-  // TimeZone::PrevTransition()
-  //
-  // Finds the time of the next/previous offset change in this time zone.
-  //
-  // By definition, `NextTransition(t, &trans)` returns false when `t` is
-  // `InfiniteFuture()`, and `PrevTransition(t, &trans)` returns false
-  // when `t` is `InfinitePast()`. If the zone has no transitions, the
-  // result will also be false no matter what the argument.
-  //
-  // Otherwise, when `t` is `InfinitePast()`, `NextTransition(t, &trans)`
-  // returns true and sets `trans` to the first recorded transition. Chains
-  // of calls to `NextTransition()/PrevTransition()` will eventually return
-  // false, but it is unspecified exactly when `NextTransition(t, &trans)`
-  // jumps to false, or what time is set by `PrevTransition(t, &trans)` for
-  // a very distant `t`.
-  //
-  // Note: Enumeration of time-zone transitions is for informational purposes
-  // only. Modern time-related code should not care about when offset changes
-  // occur.
-  //
-  // Example:
-  //   absl::TimeZone nyc;
-  //   if (!absl::LoadTimeZone("America/New_York", &nyc)) { ... }
-  //   const auto now = absl::Now();
-  //   auto t = absl::InfinitePast();
-  //   absl::TimeZone::CivilTransition trans;
-  //   while (t <= now && nyc.NextTransition(t, &trans)) {
-  //     // transition: trans.from -> trans.to
-  //     t = nyc.At(trans.to).trans;
-  //   }
-  struct CivilTransition {
-    CivilSecond from;  // the civil time we jump from
-    CivilSecond to;    // the civil time we jump to
-  };
-  bool NextTransition(Time t, CivilTransition* trans) const;
-  bool PrevTransition(Time t, CivilTransition* trans) const;
-
-  template <typename H>
-  friend H AbslHashValue(H h, TimeZone tz) {
-    return H::combine(std::move(h), tz.cz_);
-  }
-
- private:
-  friend bool operator==(TimeZone a, TimeZone b) { return a.cz_ == b.cz_; }
-  friend bool operator!=(TimeZone a, TimeZone b) { return a.cz_ != b.cz_; }
-  friend std::ostream& operator<<(std::ostream& os, TimeZone tz) {
-    return os << tz.name();
-  }
-
-  time_internal::cctz::time_zone cz_;
-};
-
-// LoadTimeZone()
-//
-// Loads the named zone. May perform I/O on the initial load of the named
-// zone. If the name is invalid, or some other kind of error occurs, returns
-// `false` and `*tz` is set to the UTC time zone.
-inline bool LoadTimeZone(absl::string_view name, TimeZone* tz) {
-  if (name == "localtime") {
-    *tz = TimeZone(time_internal::cctz::local_time_zone());
-    return true;
-  }
-  time_internal::cctz::time_zone cz;
-  const bool b = time_internal::cctz::load_time_zone(std::string(name), &cz);
-  *tz = TimeZone(cz);
-  return b;
-}
-
-// FixedTimeZone()
-//
-// Returns a TimeZone that is a fixed offset (seconds east) from UTC.
-// Note: If the absolute value of the offset is greater than 24 hours
-// you'll get UTC (i.e., no offset) instead.
-inline TimeZone FixedTimeZone(int seconds) {
-  return TimeZone(
-      time_internal::cctz::fixed_time_zone(std::chrono::seconds(seconds)));
-}
-
-// UTCTimeZone()
-//
-// Convenience method returning the UTC time zone.
-inline TimeZone UTCTimeZone() {
-  return TimeZone(time_internal::cctz::utc_time_zone());
-}
-
-// LocalTimeZone()
-//
-// Convenience method returning the local time zone, or UTC if there is
-// no configured local zone.  Warning: Be wary of using LocalTimeZone(),
-// and particularly so in a server process, as the zone configured for the
-// local machine should be irrelevant.  Prefer an explicit zone name.
-inline TimeZone LocalTimeZone() {
-  return TimeZone(time_internal::cctz::local_time_zone());
-}
-
-// ToCivilSecond()
-// ToCivilMinute()
-// ToCivilHour()
-// ToCivilDay()
-// ToCivilMonth()
-// ToCivilYear()
-//
-// Helpers for TimeZone::At(Time) to return particularly aligned civil times.
-//
-// Example:
-//
-//   absl::Time t = ...;
-//   absl::TimeZone tz = ...;
-//   const auto cd = absl::ToCivilDay(t, tz);
-inline CivilSecond ToCivilSecond(Time t, TimeZone tz) {
-  return tz.At(t).cs;  // already a CivilSecond
-}
-inline CivilMinute ToCivilMinute(Time t, TimeZone tz) {
-  return CivilMinute(tz.At(t).cs);
-}
-inline CivilHour ToCivilHour(Time t, TimeZone tz) {
-  return CivilHour(tz.At(t).cs);
-}
-inline CivilDay ToCivilDay(Time t, TimeZone tz) {
-  return CivilDay(tz.At(t).cs);
-}
-inline CivilMonth ToCivilMonth(Time t, TimeZone tz) {
-  return CivilMonth(tz.At(t).cs);
-}
-inline CivilYear ToCivilYear(Time t, TimeZone tz) {
-  return CivilYear(tz.At(t).cs);
-}
-
-// FromCivil()
-//
-// Helper for TimeZone::At(CivilSecond) that provides "order-preserving
-// semantics." If the civil time maps to a unique time, that time is
-// returned. If the civil time is repeated in the given time zone, the
-// time using the pre-transition offset is returned. Otherwise, the
-// civil time is skipped in the given time zone, and the transition time
-// is returned. This means that for any two civil times, ct1 and ct2,
-// (ct1 < ct2) => (FromCivil(ct1) <= FromCivil(ct2)), the equal case
-// being when two non-existent civil times map to the same transition time.
-//
-// Note: Accepts civil times of any alignment.
-inline Time FromCivil(CivilSecond ct, TimeZone tz) {
-  const auto ti = tz.At(ct);
-  if (ti.kind == TimeZone::TimeInfo::SKIPPED) return ti.trans;
-  return ti.pre;
-}
-
-// TimeConversion
-//
-// An `absl::TimeConversion` represents the conversion of year, month, day,
-// hour, minute, and second values (i.e., a civil time), in a particular
-// `absl::TimeZone`, to a time instant (an absolute time), as returned by
-// `absl::ConvertDateTime()`. Legacy version of `absl::TimeZone::TimeInfo`.
-//
-// Deprecated. Use `absl::TimeZone::TimeInfo`.
-struct
-    TimeConversion {
-  Time pre;    // time calculated using the pre-transition offset
-  Time trans;  // when the civil-time discontinuity occurred
-  Time post;   // time calculated using the post-transition offset
-
-  enum Kind {
-    UNIQUE,    // the civil time was singular (pre == trans == post)
-    SKIPPED,   // the civil time did not exist
-    REPEATED,  // the civil time was ambiguous
-  };
-  Kind kind;
-
-  bool normalized;  // input values were outside their valid ranges
-};
-
-// ConvertDateTime()
-//
-// Legacy version of `absl::TimeZone::At(absl::CivilSecond)` that takes
-// the civil time as six, separate values (YMDHMS).
-//
-// The input month, day, hour, minute, and second values can be outside
-// of their valid ranges, in which case they will be "normalized" during
-// the conversion.
-//
-// Example:
-//
-//   // "October 32" normalizes to "November 1".
-//   absl::TimeConversion tc =
-//       absl::ConvertDateTime(2013, 10, 32, 8, 30, 0, lax);
-//   // tc.kind == TimeConversion::UNIQUE && tc.normalized == true
-//   // absl::ToCivilDay(tc.pre, tz).month() == 11
-//   // absl::ToCivilDay(tc.pre, tz).day() == 1
-//
-// Deprecated. Use `absl::TimeZone::At(CivilSecond)`.
-TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour,
-                               int min, int sec, TimeZone tz);
-
-// FromDateTime()
-//
-// A convenience wrapper for `absl::ConvertDateTime()` that simply returns
-// the "pre" `absl::Time`.  That is, the unique result, or the instant that
-// is correct using the pre-transition offset (as if the transition never
-// happened).
-//
-// Example:
-//
-//   absl::Time t = absl::FromDateTime(2017, 9, 26, 9, 30, 0, lax);
-//   // t = 2017-09-26 09:30:00 -0700
-//
-// Deprecated. Use `absl::FromCivil(CivilSecond, TimeZone)`. Note that the
-// behavior of `FromCivil()` differs from `FromDateTime()` for skipped civil
-// times. If you care about that see `absl::TimeZone::At(absl::CivilSecond)`.
-inline Time FromDateTime(int64_t year, int mon, int day, int hour,
-                         int min, int sec, TimeZone tz) {
-  return ConvertDateTime(year, mon, day, hour, min, sec, tz).pre;
-}
-
-// FromTM()
-//
-// Converts the `tm_year`, `tm_mon`, `tm_mday`, `tm_hour`, `tm_min`, and
-// `tm_sec` fields to an `absl::Time` using the given time zone. See ctime(3)
-// for a description of the expected values of the tm fields. If the indicated
-// time instant is not unique (see `absl::TimeZone::At(absl::CivilSecond)`
-// above), the `tm_isdst` field is consulted to select the desired instant
-// (`tm_isdst` > 0 means DST, `tm_isdst` == 0 means no DST, `tm_isdst` < 0
-// means use the post-transition offset).
-Time FromTM(const struct tm& tm, TimeZone tz);
-
-// ToTM()
-//
-// Converts the given `absl::Time` to a struct tm using the given time zone.
-// See ctime(3) for a description of the values of the tm fields.
-struct tm ToTM(Time t, TimeZone tz);
-
-// RFC3339_full
-// RFC3339_sec
-//
-// FormatTime()/ParseTime() format specifiers for RFC3339 date/time strings,
-// with trailing zeros trimmed or with fractional seconds omitted altogether.
-//
-// Note that RFC3339_sec[] matches an ISO 8601 extended format for date and
-// time with UTC offset.  Also note the use of "%Y": RFC3339 mandates that
-// years have exactly four digits, but we allow them to take their natural
-// width.
-ABSL_DLL extern const char RFC3339_full[];  // %Y-%m-%d%ET%H:%M:%E*S%Ez
-ABSL_DLL extern const char RFC3339_sec[];   // %Y-%m-%d%ET%H:%M:%S%Ez
-
-// RFC1123_full
-// RFC1123_no_wday
-//
-// FormatTime()/ParseTime() format specifiers for RFC1123 date/time strings.
-ABSL_DLL extern const char RFC1123_full[];     // %a, %d %b %E4Y %H:%M:%S %z
-ABSL_DLL extern const char RFC1123_no_wday[];  // %d %b %E4Y %H:%M:%S %z
-
-// FormatTime()
-//
-// Formats the given `absl::Time` in the `absl::TimeZone` according to the
-// provided format string. Uses strftime()-like formatting options, with
-// the following extensions:
-//
-//   - %Ez  - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
-//   - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
-//   - %E#S - Seconds with # digits of fractional precision
-//   - %E*S - Seconds with full fractional precision (a literal '*')
-//   - %E#f - Fractional seconds with # digits of precision
-//   - %E*f - Fractional seconds with full precision (a literal '*')
-//   - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
-//   - %ET  - The RFC3339 "date-time" separator "T"
-//
-// Note that %E0S behaves like %S, and %E0f produces no characters.  In
-// contrast %E*f always produces at least one digit, which may be '0'.
-//
-// Note that %Y produces as many characters as it takes to fully render the
-// year.  A year outside of [-999:9999] when formatted with %E4Y will produce
-// more than four characters, just like %Y.
-//
-// We recommend that format strings include the UTC offset (%z, %Ez, or %E*z)
-// so that the result uniquely identifies a time instant.
-//
-// Example:
-//
-//   absl::CivilSecond cs(2013, 1, 2, 3, 4, 5);
-//   absl::Time t = absl::FromCivil(cs, lax);
-//   std::string f = absl::FormatTime("%H:%M:%S", t, lax);  // "03:04:05"
-//   f = absl::FormatTime("%H:%M:%E3S", t, lax);  // "03:04:05.000"
-//
-// Note: If the given `absl::Time` is `absl::InfiniteFuture()`, the returned
-// string will be exactly "infinite-future". If the given `absl::Time` is
-// `absl::InfinitePast()`, the returned string will be exactly "infinite-past".
-// In both cases the given format string and `absl::TimeZone` are ignored.
-//
-std::string FormatTime(absl::string_view format, Time t, TimeZone tz);
-
-// Convenience functions that format the given time using the RFC3339_full
-// format.  The first overload uses the provided TimeZone, while the second
-// uses LocalTimeZone().
-std::string FormatTime(Time t, TimeZone tz);
-std::string FormatTime(Time t);
-
-// Output stream operator.
-inline std::ostream& operator<<(std::ostream& os, Time t) {
-  return os << FormatTime(t);
-}
-
-// ParseTime()
-//
-// Parses an input string according to the provided format string and
-// returns the corresponding `absl::Time`. Uses strftime()-like formatting
-// options, with the same extensions as FormatTime(), but with the
-// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f.  %Ez
-// and %E*z also accept the same inputs, which (along with %z) includes
-// 'z' and 'Z' as synonyms for +00:00.  %ET accepts either 'T' or 't'.
-//
-// %Y consumes as many numeric characters as it can, so the matching data
-// should always be terminated with a non-numeric.  %E4Y always consumes
-// exactly four characters, including any sign.
-//
-// Unspecified fields are taken from the default date and time of ...
-//
-//   "1970-01-01 00:00:00.0 +0000"
-//
-// For example, parsing a string of "15:45" (%H:%M) will return an absl::Time
-// that represents "1970-01-01 15:45:00.0 +0000".
-//
-// Note that since ParseTime() returns time instants, it makes the most sense
-// to parse fully-specified date/time strings that include a UTC offset (%z,
-// %Ez, or %E*z).
-//
-// Note also that `absl::ParseTime()` only heeds the fields year, month, day,
-// hour, minute, (fractional) second, and UTC offset.  Other fields, like
-// weekday (%a or %A), while parsed for syntactic validity, are ignored
-// in the conversion.
-//
-// Date and time fields that are out-of-range will be treated as errors
-// rather than normalizing them like `absl::CivilSecond` does.  For example,
-// it is an error to parse the date "Oct 32, 2013" because 32 is out of range.
-//
-// A leap second of ":60" is normalized to ":00" of the following minute
-// with fractional seconds discarded.  The following table shows how the
-// given seconds and subseconds will be parsed:
-//
-//   "59.x" -> 59.x  // exact
-//   "60.x" -> 00.0  // normalized
-//   "00.x" -> 00.x  // exact
-//
-// Errors are indicated by returning false and assigning an error message
-// to the "err" out param if it is non-null.
-//
-// Note: If the input string is exactly "infinite-future", the returned
-// `absl::Time` will be `absl::InfiniteFuture()` and `true` will be returned.
-// If the input string is "infinite-past", the returned `absl::Time` will be
-// `absl::InfinitePast()` and `true` will be returned.
-//
-bool ParseTime(absl::string_view format, absl::string_view input, Time* time,
-               std::string* err);
-
-// Like ParseTime() above, but if the format string does not contain a UTC
-// offset specification (%z/%Ez/%E*z) then the input is interpreted in the
-// given TimeZone.  This means that the input, by itself, does not identify a
-// unique instant.  Being time-zone dependent, it also admits the possibility
-// of ambiguity or non-existence, in which case the "pre" time (as defined
-// by TimeZone::TimeInfo) is returned.  For these reasons we recommend that
-// all date/time strings include a UTC offset so they're context independent.
-bool ParseTime(absl::string_view format, absl::string_view input, TimeZone tz,
-               Time* time, std::string* err);
-
-// ============================================================================
-// Implementation Details Follow
-// ============================================================================
-
-namespace time_internal {
-
-// Creates a Duration with a given representation.
-// REQUIRES: hi,lo is a valid representation of a Duration as specified
-// in time/duration.cc.
-constexpr Duration MakeDuration(int64_t hi, uint32_t lo = 0) {
-  return Duration(hi, lo);
-}
-
-constexpr Duration MakeDuration(int64_t hi, int64_t lo) {
-  return MakeDuration(hi, static_cast<uint32_t>(lo));
-}
-
-// Make a Duration value from a floating-point number, as long as that number
-// is in the range [ 0 .. numeric_limits<int64_t>::max ), that is, as long as
-// it's positive and can be converted to int64_t without risk of UB.
-inline Duration MakePosDoubleDuration(double n) {
-  const int64_t int_secs = static_cast<int64_t>(n);
-  const uint32_t ticks = static_cast<uint32_t>(
-      (n - static_cast<double>(int_secs)) * kTicksPerSecond + 0.5);
-  return ticks < kTicksPerSecond
-             ? MakeDuration(int_secs, ticks)
-             : MakeDuration(int_secs + 1, ticks - kTicksPerSecond);
-}
-
-// Creates a normalized Duration from an almost-normalized (sec,ticks)
-// pair. sec may be positive or negative.  ticks must be in the range
-// -kTicksPerSecond < *ticks < kTicksPerSecond.  If ticks is negative it
-// will be normalized to a positive value in the resulting Duration.
-constexpr Duration MakeNormalizedDuration(int64_t sec, int64_t ticks) {
-  return (ticks < 0) ? MakeDuration(sec - 1, ticks + kTicksPerSecond)
-                     : MakeDuration(sec, ticks);
-}
-
-// Provide access to the Duration representation.
-constexpr int64_t GetRepHi(Duration d) { return d.rep_hi_; }
-constexpr uint32_t GetRepLo(Duration d) { return d.rep_lo_; }
-
-// Returns true iff d is positive or negative infinity.
-constexpr bool IsInfiniteDuration(Duration d) { return GetRepLo(d) == ~0U; }
-
-// Returns an infinite Duration with the opposite sign.
-// REQUIRES: IsInfiniteDuration(d)
-constexpr Duration OppositeInfinity(Duration d) {
-  return GetRepHi(d) < 0
-             ? MakeDuration((std::numeric_limits<int64_t>::max)(), ~0U)
-             : MakeDuration((std::numeric_limits<int64_t>::min)(), ~0U);
-}
-
-// Returns (-n)-1 (equivalently -(n+1)) without avoidable overflow.
-constexpr int64_t NegateAndSubtractOne(int64_t n) {
-  // Note: Good compilers will optimize this expression to ~n when using
-  // a two's-complement representation (which is required for int64_t).
-  return (n < 0) ? -(n + 1) : (-n) - 1;
-}
-
-// Map between a Time and a Duration since the Unix epoch.  Note that these
-// functions depend on the above mentioned choice of the Unix epoch for the
-// Time representation (and both need to be Time friends).  Without this
-// knowledge, we would need to add-in/subtract-out UnixEpoch() respectively.
-constexpr Time FromUnixDuration(Duration d) { return Time(d); }
-constexpr Duration ToUnixDuration(Time t) { return t.rep_; }
-
-template <std::intmax_t N>
-constexpr Duration FromInt64(int64_t v, std::ratio<1, N>) {
-  static_assert(0 < N && N <= 1000 * 1000 * 1000, "Unsupported ratio");
-  // Subsecond ratios cannot overflow.
-  return MakeNormalizedDuration(
-      v / N, v % N * kTicksPerNanosecond * 1000 * 1000 * 1000 / N);
-}
-constexpr Duration FromInt64(int64_t v, std::ratio<60>) {
-  return (v <= (std::numeric_limits<int64_t>::max)() / 60 &&
-          v >= (std::numeric_limits<int64_t>::min)() / 60)
-             ? MakeDuration(v * 60)
-             : v > 0 ? InfiniteDuration() : -InfiniteDuration();
-}
-constexpr Duration FromInt64(int64_t v, std::ratio<3600>) {
-  return (v <= (std::numeric_limits<int64_t>::max)() / 3600 &&
-          v >= (std::numeric_limits<int64_t>::min)() / 3600)
-             ? MakeDuration(v * 3600)
-             : v > 0 ? InfiniteDuration() : -InfiniteDuration();
-}
-
-// IsValidRep64<T>(0) is true if the expression `int64_t{std::declval<T>()}` is
-// valid. That is, if a T can be assigned to an int64_t without narrowing.
-template <typename T>
-constexpr auto IsValidRep64(int) -> decltype(int64_t{std::declval<T>()} == 0) {
-  return true;
-}
-template <typename T>
-constexpr auto IsValidRep64(char) -> bool {
-  return false;
-}
-
-// Converts a std::chrono::duration to an absl::Duration.
-template <typename Rep, typename Period>
-constexpr Duration FromChrono(const std::chrono::duration<Rep, Period>& d) {
-  static_assert(IsValidRep64<Rep>(0), "duration::rep is invalid");
-  return FromInt64(int64_t{d.count()}, Period{});
-}
-
-template <typename Ratio>
-int64_t ToInt64(Duration d, Ratio) {
-  // Note: This may be used on MSVC, which may have a system_clock period of
-  // std::ratio<1, 10 * 1000 * 1000>
-  return ToInt64Seconds(d * Ratio::den / Ratio::num);
-}
-// Fastpath implementations for the 6 common duration units.
-inline int64_t ToInt64(Duration d, std::nano) {
-  return ToInt64Nanoseconds(d);
-}
-inline int64_t ToInt64(Duration d, std::micro) {
-  return ToInt64Microseconds(d);
-}
-inline int64_t ToInt64(Duration d, std::milli) {
-  return ToInt64Milliseconds(d);
-}
-inline int64_t ToInt64(Duration d, std::ratio<1>) {
-  return ToInt64Seconds(d);
-}
-inline int64_t ToInt64(Duration d, std::ratio<60>) {
-  return ToInt64Minutes(d);
-}
-inline int64_t ToInt64(Duration d, std::ratio<3600>) {
-  return ToInt64Hours(d);
-}
-
-// Converts an absl::Duration to a chrono duration of type T.
-template <typename T>
-T ToChronoDuration(Duration d) {
-  using Rep = typename T::rep;
-  using Period = typename T::period;
-  static_assert(IsValidRep64<Rep>(0), "duration::rep is invalid");
-  if (time_internal::IsInfiniteDuration(d))
-    return d < ZeroDuration() ? (T::min)() : (T::max)();
-  const auto v = ToInt64(d, Period{});
-  if (v > (std::numeric_limits<Rep>::max)()) return (T::max)();
-  if (v < (std::numeric_limits<Rep>::min)()) return (T::min)();
-  return T{v};
-}
-
-}  // namespace time_internal
-
-constexpr Duration Nanoseconds(int64_t n) {
-  return time_internal::FromInt64(n, std::nano{});
-}
-constexpr Duration Microseconds(int64_t n) {
-  return time_internal::FromInt64(n, std::micro{});
-}
-constexpr Duration Milliseconds(int64_t n) {
-  return time_internal::FromInt64(n, std::milli{});
-}
-constexpr Duration Seconds(int64_t n) {
-  return time_internal::FromInt64(n, std::ratio<1>{});
-}
-constexpr Duration Minutes(int64_t n) {
-  return time_internal::FromInt64(n, std::ratio<60>{});
-}
-constexpr Duration Hours(int64_t n) {
-  return time_internal::FromInt64(n, std::ratio<3600>{});
-}
-
-constexpr bool operator<(Duration lhs, Duration rhs) {
-  return time_internal::GetRepHi(lhs) != time_internal::GetRepHi(rhs)
-             ? time_internal::GetRepHi(lhs) < time_internal::GetRepHi(rhs)
-         : time_internal::GetRepHi(lhs) == (std::numeric_limits<int64_t>::min)()
-             ? time_internal::GetRepLo(lhs) + 1 <
-                   time_internal::GetRepLo(rhs) + 1
-             : time_internal::GetRepLo(lhs) < time_internal::GetRepLo(rhs);
-}
-
-constexpr bool operator==(Duration lhs, Duration rhs) {
-  return time_internal::GetRepHi(lhs) == time_internal::GetRepHi(rhs) &&
-         time_internal::GetRepLo(lhs) == time_internal::GetRepLo(rhs);
-}
-
-constexpr Duration operator-(Duration d) {
-  // This is a little interesting because of the special cases.
-  //
-  // If rep_lo_ is zero, we have it easy; it's safe to negate rep_hi_, we're
-  // dealing with an integral number of seconds, and the only special case is
-  // the maximum negative finite duration, which can't be negated.
-  //
-  // Infinities stay infinite, and just change direction.
-  //
-  // Finally we're in the case where rep_lo_ is non-zero, and we can borrow
-  // a second's worth of ticks and avoid overflow (as negating int64_t-min + 1
-  // is safe).
-  return time_internal::GetRepLo(d) == 0
-             ? time_internal::GetRepHi(d) ==
-                       (std::numeric_limits<int64_t>::min)()
-                   ? InfiniteDuration()
-                   : time_internal::MakeDuration(-time_internal::GetRepHi(d))
-             : time_internal::IsInfiniteDuration(d)
-                   ? time_internal::OppositeInfinity(d)
-                   : time_internal::MakeDuration(
-                         time_internal::NegateAndSubtractOne(
-                             time_internal::GetRepHi(d)),
-                         time_internal::kTicksPerSecond -
-                             time_internal::GetRepLo(d));
-}
-
-constexpr Duration InfiniteDuration() {
-  return time_internal::MakeDuration((std::numeric_limits<int64_t>::max)(),
-                                     ~0U);
-}
-
-constexpr Duration FromChrono(const std::chrono::nanoseconds& d) {
-  return time_internal::FromChrono(d);
-}
-constexpr Duration FromChrono(const std::chrono::microseconds& d) {
-  return time_internal::FromChrono(d);
-}
-constexpr Duration FromChrono(const std::chrono::milliseconds& d) {
-  return time_internal::FromChrono(d);
-}
-constexpr Duration FromChrono(const std::chrono::seconds& d) {
-  return time_internal::FromChrono(d);
-}
-constexpr Duration FromChrono(const std::chrono::minutes& d) {
-  return time_internal::FromChrono(d);
-}
-constexpr Duration FromChrono(const std::chrono::hours& d) {
-  return time_internal::FromChrono(d);
-}
-
-constexpr Time FromUnixNanos(int64_t ns) {
-  return time_internal::FromUnixDuration(Nanoseconds(ns));
-}
-
-constexpr Time FromUnixMicros(int64_t us) {
-  return time_internal::FromUnixDuration(Microseconds(us));
-}
-
-constexpr Time FromUnixMillis(int64_t ms) {
-  return time_internal::FromUnixDuration(Milliseconds(ms));
-}
-
-constexpr Time FromUnixSeconds(int64_t s) {
-  return time_internal::FromUnixDuration(Seconds(s));
-}
-
-constexpr Time FromTimeT(time_t t) {
-  return time_internal::FromUnixDuration(Seconds(t));
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TIME_TIME_H_
diff --git a/third_party/abseil_cpp/absl/time/time_benchmark.cc b/third_party/abseil_cpp/absl/time/time_benchmark.cc
deleted file mode 100644
index 99e6279984..0000000000
--- a/third_party/abseil_cpp/absl/time/time_benchmark.cc
+++ /dev/null
@@ -1,316 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/time.h"
-
-#if !defined(_WIN32)
-#include <sys/time.h>
-#endif  // _WIN32
-#include <algorithm>
-#include <cmath>
-#include <cstddef>
-#include <cstring>
-#include <ctime>
-#include <memory>
-#include <string>
-
-#include "absl/time/clock.h"
-#include "absl/time/internal/test_util.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-//
-// Addition/Subtraction of a duration
-//
-
-void BM_Time_Arithmetic(benchmark::State& state) {
-  const absl::Duration nano = absl::Nanoseconds(1);
-  const absl::Duration sec = absl::Seconds(1);
-  absl::Time t = absl::UnixEpoch();
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(t += nano);
-    benchmark::DoNotOptimize(t -= sec);
-  }
-}
-BENCHMARK(BM_Time_Arithmetic);
-
-//
-// Time difference
-//
-
-void BM_Time_Difference(benchmark::State& state) {
-  absl::Time start = absl::Now();
-  absl::Time end = start + absl::Nanoseconds(1);
-  absl::Duration diff;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(diff += end - start);
-  }
-}
-BENCHMARK(BM_Time_Difference);
-
-//
-// ToDateTime
-//
-// In each "ToDateTime" benchmark we switch between two instants
-// separated by at least one transition in order to defeat any
-// internal caching of previous results (e.g., see local_time_hint_).
-//
-// The "UTC" variants use UTC instead of the Google/local time zone.
-//
-
-void BM_Time_ToDateTime_Absl(benchmark::State& state) {
-  const absl::TimeZone tz =
-      absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  absl::Time t = absl::FromUnixSeconds(1384569027);
-  absl::Time t2 = absl::FromUnixSeconds(1418962578);
-  while (state.KeepRunning()) {
-    std::swap(t, t2);
-    t += absl::Seconds(1);
-    benchmark::DoNotOptimize(t.In(tz));
-  }
-}
-BENCHMARK(BM_Time_ToDateTime_Absl);
-
-void BM_Time_ToDateTime_Libc(benchmark::State& state) {
-  // No timezone support, so just use localtime.
-  time_t t = 1384569027;
-  time_t t2 = 1418962578;
-  while (state.KeepRunning()) {
-    std::swap(t, t2);
-    t += 1;
-    struct tm tm;
-#if !defined(_WIN32)
-    benchmark::DoNotOptimize(localtime_r(&t, &tm));
-#else   // _WIN32
-    benchmark::DoNotOptimize(localtime_s(&tm, &t));
-#endif  // _WIN32
-  }
-}
-BENCHMARK(BM_Time_ToDateTime_Libc);
-
-void BM_Time_ToDateTimeUTC_Absl(benchmark::State& state) {
-  const absl::TimeZone tz = absl::UTCTimeZone();
-  absl::Time t = absl::FromUnixSeconds(1384569027);
-  while (state.KeepRunning()) {
-    t += absl::Seconds(1);
-    benchmark::DoNotOptimize(t.In(tz));
-  }
-}
-BENCHMARK(BM_Time_ToDateTimeUTC_Absl);
-
-void BM_Time_ToDateTimeUTC_Libc(benchmark::State& state) {
-  time_t t = 1384569027;
-  while (state.KeepRunning()) {
-    t += 1;
-    struct tm tm;
-#if !defined(_WIN32)
-    benchmark::DoNotOptimize(gmtime_r(&t, &tm));
-#else   // _WIN32
-    benchmark::DoNotOptimize(gmtime_s(&tm, &t));
-#endif  // _WIN32
-  }
-}
-BENCHMARK(BM_Time_ToDateTimeUTC_Libc);
-
-//
-// FromUnixMicros
-//
-
-void BM_Time_FromUnixMicros(benchmark::State& state) {
-  int i = 0;
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::FromUnixMicros(i));
-    ++i;
-  }
-}
-BENCHMARK(BM_Time_FromUnixMicros);
-
-void BM_Time_ToUnixNanos(benchmark::State& state) {
-  const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(ToUnixNanos(t));
-  }
-}
-BENCHMARK(BM_Time_ToUnixNanos);
-
-void BM_Time_ToUnixMicros(benchmark::State& state) {
-  const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(ToUnixMicros(t));
-  }
-}
-BENCHMARK(BM_Time_ToUnixMicros);
-
-void BM_Time_ToUnixMillis(benchmark::State& state) {
-  const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(ToUnixMillis(t));
-  }
-}
-BENCHMARK(BM_Time_ToUnixMillis);
-
-void BM_Time_ToUnixSeconds(benchmark::State& state) {
-  const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToUnixSeconds(t));
-  }
-}
-BENCHMARK(BM_Time_ToUnixSeconds);
-
-//
-// FromCivil
-//
-// In each "FromCivil" benchmark we switch between two YMDhms values
-// separated by at least one transition in order to defeat any internal
-// caching of previous results (e.g., see time_local_hint_).
-//
-// The "UTC" variants use UTC instead of the Google/local time zone.
-// The "Day0" variants require normalization of the day of month.
-//
-
-void BM_Time_FromCivil_Absl(benchmark::State& state) {
-  const absl::TimeZone tz =
-      absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  int i = 0;
-  while (state.KeepRunning()) {
-    if ((i & 1) == 0) {
-      absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz);
-    } else {
-      absl::FromCivil(absl::CivilSecond(2013, 11, 15, 18, 30, 27), tz);
-    }
-    ++i;
-  }
-}
-BENCHMARK(BM_Time_FromCivil_Absl);
-
-void BM_Time_FromCivil_Libc(benchmark::State& state) {
-  // No timezone support, so just use localtime.
-  int i = 0;
-  while (state.KeepRunning()) {
-    struct tm tm;
-    if ((i & 1) == 0) {
-      tm.tm_year = 2014 - 1900;
-      tm.tm_mon = 12 - 1;
-      tm.tm_mday = 18;
-      tm.tm_hour = 20;
-      tm.tm_min = 16;
-      tm.tm_sec = 18;
-    } else {
-      tm.tm_year = 2013 - 1900;
-      tm.tm_mon = 11 - 1;
-      tm.tm_mday = 15;
-      tm.tm_hour = 18;
-      tm.tm_min = 30;
-      tm.tm_sec = 27;
-    }
-    tm.tm_isdst = -1;
-    mktime(&tm);
-    ++i;
-  }
-}
-BENCHMARK(BM_Time_FromCivil_Libc);
-
-void BM_Time_FromCivilUTC_Absl(benchmark::State& state) {
-  const absl::TimeZone tz = absl::UTCTimeZone();
-  while (state.KeepRunning()) {
-    absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz);
-  }
-}
-BENCHMARK(BM_Time_FromCivilUTC_Absl);
-
-void BM_Time_FromCivilDay0_Absl(benchmark::State& state) {
-  const absl::TimeZone tz =
-      absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  int i = 0;
-  while (state.KeepRunning()) {
-    if ((i & 1) == 0) {
-      absl::FromCivil(absl::CivilSecond(2014, 12, 0, 20, 16, 18), tz);
-    } else {
-      absl::FromCivil(absl::CivilSecond(2013, 11, 0, 18, 30, 27), tz);
-    }
-    ++i;
-  }
-}
-BENCHMARK(BM_Time_FromCivilDay0_Absl);
-
-void BM_Time_FromCivilDay0_Libc(benchmark::State& state) {
-  // No timezone support, so just use localtime.
-  int i = 0;
-  while (state.KeepRunning()) {
-    struct tm tm;
-    if ((i & 1) == 0) {
-      tm.tm_year = 2014 - 1900;
-      tm.tm_mon = 12 - 1;
-      tm.tm_mday = 0;
-      tm.tm_hour = 20;
-      tm.tm_min = 16;
-      tm.tm_sec = 18;
-    } else {
-      tm.tm_year = 2013 - 1900;
-      tm.tm_mon = 11 - 1;
-      tm.tm_mday = 0;
-      tm.tm_hour = 18;
-      tm.tm_min = 30;
-      tm.tm_sec = 27;
-    }
-    tm.tm_isdst = -1;
-    mktime(&tm);
-    ++i;
-  }
-}
-BENCHMARK(BM_Time_FromCivilDay0_Libc);
-
-//
-// To/FromTimespec
-//
-
-void BM_Time_ToTimespec(benchmark::State& state) {
-  absl::Time now = absl::Now();
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::ToTimespec(now));
-  }
-}
-BENCHMARK(BM_Time_ToTimespec);
-
-void BM_Time_FromTimespec(benchmark::State& state) {
-  timespec ts = absl::ToTimespec(absl::Now());
-  while (state.KeepRunning()) {
-    if (++ts.tv_nsec == 1000 * 1000 * 1000) {
-      ++ts.tv_sec;
-      ts.tv_nsec = 0;
-    }
-    benchmark::DoNotOptimize(absl::TimeFromTimespec(ts));
-  }
-}
-BENCHMARK(BM_Time_FromTimespec);
-
-//
-// Comparison with InfiniteFuture/Past
-//
-
-void BM_Time_InfiniteFuture(benchmark::State& state) {
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::InfiniteFuture());
-  }
-}
-BENCHMARK(BM_Time_InfiniteFuture);
-
-void BM_Time_InfinitePast(benchmark::State& state) {
-  while (state.KeepRunning()) {
-    benchmark::DoNotOptimize(absl::InfinitePast());
-  }
-}
-BENCHMARK(BM_Time_InfinitePast);
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/time_test.cc b/third_party/abseil_cpp/absl/time/time_test.cc
deleted file mode 100644
index cde9423feb..0000000000
--- a/third_party/abseil_cpp/absl/time/time_test.cc
+++ /dev/null
@@ -1,1280 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/time.h"
-
-#if defined(_MSC_VER)
-#include <winsock2.h>  // for timeval
-#endif
-
-#include <chrono>  // NOLINT(build/c++11)
-#include <cstring>
-#include <ctime>
-#include <iomanip>
-#include <limits>
-#include <string>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/numeric/int128.h"
-#include "absl/time/clock.h"
-#include "absl/time/internal/test_util.h"
-
-namespace {
-
-#if defined(GTEST_USES_SIMPLE_RE) && GTEST_USES_SIMPLE_RE
-const char kZoneAbbrRE[] = ".*";  // just punt
-#else
-const char kZoneAbbrRE[] = "[A-Za-z]{3,4}|[-+][0-9]{2}([0-9]{2})?";
-#endif
-
-// This helper is a macro so that failed expectations show up with the
-// correct line numbers.
-#define EXPECT_CIVIL_INFO(ci, y, m, d, h, min, s, off, isdst)      \
-  do {                                                             \
-    EXPECT_EQ(y, ci.cs.year());                                    \
-    EXPECT_EQ(m, ci.cs.month());                                   \
-    EXPECT_EQ(d, ci.cs.day());                                     \
-    EXPECT_EQ(h, ci.cs.hour());                                    \
-    EXPECT_EQ(min, ci.cs.minute());                                \
-    EXPECT_EQ(s, ci.cs.second());                                  \
-    EXPECT_EQ(off, ci.offset);                                     \
-    EXPECT_EQ(isdst, ci.is_dst);                                   \
-    EXPECT_THAT(ci.zone_abbr, testing::MatchesRegex(kZoneAbbrRE)); \
-  } while (0)
-
-// A gMock matcher to match timespec values. Use this matcher like:
-// timespec ts1, ts2;
-// EXPECT_THAT(ts1, TimespecMatcher(ts2));
-MATCHER_P(TimespecMatcher, ts, "") {
-  if (ts.tv_sec == arg.tv_sec && ts.tv_nsec == arg.tv_nsec) return true;
-  *result_listener << "expected: {" << ts.tv_sec << ", " << ts.tv_nsec << "} ";
-  *result_listener << "actual: {" << arg.tv_sec << ", " << arg.tv_nsec << "}";
-  return false;
-}
-
-// A gMock matcher to match timeval values. Use this matcher like:
-// timeval tv1, tv2;
-// EXPECT_THAT(tv1, TimevalMatcher(tv2));
-MATCHER_P(TimevalMatcher, tv, "") {
-  if (tv.tv_sec == arg.tv_sec && tv.tv_usec == arg.tv_usec) return true;
-  *result_listener << "expected: {" << tv.tv_sec << ", " << tv.tv_usec << "} ";
-  *result_listener << "actual: {" << arg.tv_sec << ", " << arg.tv_usec << "}";
-  return false;
-}
-
-TEST(Time, ConstExpr) {
-  constexpr absl::Time t0 = absl::UnixEpoch();
-  static_assert(t0 == absl::Time(), "UnixEpoch");
-  constexpr absl::Time t1 = absl::InfiniteFuture();
-  static_assert(t1 != absl::Time(), "InfiniteFuture");
-  constexpr absl::Time t2 = absl::InfinitePast();
-  static_assert(t2 != absl::Time(), "InfinitePast");
-  constexpr absl::Time t3 = absl::FromUnixNanos(0);
-  static_assert(t3 == absl::Time(), "FromUnixNanos");
-  constexpr absl::Time t4 = absl::FromUnixMicros(0);
-  static_assert(t4 == absl::Time(), "FromUnixMicros");
-  constexpr absl::Time t5 = absl::FromUnixMillis(0);
-  static_assert(t5 == absl::Time(), "FromUnixMillis");
-  constexpr absl::Time t6 = absl::FromUnixSeconds(0);
-  static_assert(t6 == absl::Time(), "FromUnixSeconds");
-  constexpr absl::Time t7 = absl::FromTimeT(0);
-  static_assert(t7 == absl::Time(), "FromTimeT");
-}
-
-TEST(Time, ValueSemantics) {
-  absl::Time a;      // Default construction
-  absl::Time b = a;  // Copy construction
-  EXPECT_EQ(a, b);
-  absl::Time c(a);  // Copy construction (again)
-  EXPECT_EQ(a, b);
-  EXPECT_EQ(a, c);
-  EXPECT_EQ(b, c);
-  b = c;  // Assignment
-  EXPECT_EQ(a, b);
-  EXPECT_EQ(a, c);
-  EXPECT_EQ(b, c);
-}
-
-TEST(Time, UnixEpoch) {
-  const auto ci = absl::UTCTimeZone().At(absl::UnixEpoch());
-  EXPECT_EQ(absl::CivilSecond(1970, 1, 1, 0, 0, 0), ci.cs);
-  EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
-  EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(ci.cs));
-}
-
-TEST(Time, Breakdown) {
-  absl::TimeZone tz = absl::time_internal::LoadTimeZone("America/New_York");
-  absl::Time t = absl::UnixEpoch();
-
-  // The Unix epoch as seen in NYC.
-  auto ci = tz.At(t);
-  EXPECT_CIVIL_INFO(ci, 1969, 12, 31, 19, 0, 0, -18000, false);
-  EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
-  EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(ci.cs));
-
-  // Just before the epoch.
-  t -= absl::Nanoseconds(1);
-  ci = tz.At(t);
-  EXPECT_CIVIL_INFO(ci, 1969, 12, 31, 18, 59, 59, -18000, false);
-  EXPECT_EQ(absl::Nanoseconds(999999999), ci.subsecond);
-  EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(ci.cs));
-
-  // Some time later.
-  t += absl::Hours(24) * 2735;
-  t += absl::Hours(18) + absl::Minutes(30) + absl::Seconds(15) +
-       absl::Nanoseconds(9);
-  ci = tz.At(t);
-  EXPECT_CIVIL_INFO(ci, 1977, 6, 28, 14, 30, 15, -14400, true);
-  EXPECT_EQ(8, ci.subsecond / absl::Nanoseconds(1));
-  EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(ci.cs));
-}
-
-TEST(Time, AdditiveOperators) {
-  const absl::Duration d = absl::Nanoseconds(1);
-  const absl::Time t0;
-  const absl::Time t1 = t0 + d;
-
-  EXPECT_EQ(d, t1 - t0);
-  EXPECT_EQ(-d, t0 - t1);
-  EXPECT_EQ(t0, t1 - d);
-
-  absl::Time t(t0);
-  EXPECT_EQ(t0, t);
-  t += d;
-  EXPECT_EQ(t0 + d, t);
-  EXPECT_EQ(d, t - t0);
-  t -= d;
-  EXPECT_EQ(t0, t);
-
-  // Tests overflow between subseconds and seconds.
-  t = absl::UnixEpoch();
-  t += absl::Milliseconds(500);
-  EXPECT_EQ(absl::UnixEpoch() + absl::Milliseconds(500), t);
-  t += absl::Milliseconds(600);
-  EXPECT_EQ(absl::UnixEpoch() + absl::Milliseconds(1100), t);
-  t -= absl::Milliseconds(600);
-  EXPECT_EQ(absl::UnixEpoch() + absl::Milliseconds(500), t);
-  t -= absl::Milliseconds(500);
-  EXPECT_EQ(absl::UnixEpoch(), t);
-}
-
-TEST(Time, RelationalOperators) {
-  constexpr absl::Time t1 = absl::FromUnixNanos(0);
-  constexpr absl::Time t2 = absl::FromUnixNanos(1);
-  constexpr absl::Time t3 = absl::FromUnixNanos(2);
-
-  static_assert(absl::Time() == t1, "");
-  static_assert(t1 == t1, "");
-  static_assert(t2 == t2, "");
-  static_assert(t3 == t3, "");
-
-  static_assert(t1 < t2, "");
-  static_assert(t2 < t3, "");
-  static_assert(t1 < t3, "");
-
-  static_assert(t1 <= t1, "");
-  static_assert(t1 <= t2, "");
-  static_assert(t2 <= t2, "");
-  static_assert(t2 <= t3, "");
-  static_assert(t3 <= t3, "");
-  static_assert(t1 <= t3, "");
-
-  static_assert(t2 > t1, "");
-  static_assert(t3 > t2, "");
-  static_assert(t3 > t1, "");
-
-  static_assert(t2 >= t2, "");
-  static_assert(t2 >= t1, "");
-  static_assert(t3 >= t3, "");
-  static_assert(t3 >= t2, "");
-  static_assert(t1 >= t1, "");
-  static_assert(t3 >= t1, "");
-}
-
-TEST(Time, Infinity) {
-  constexpr absl::Time ifuture = absl::InfiniteFuture();
-  constexpr absl::Time ipast = absl::InfinitePast();
-
-  static_assert(ifuture == ifuture, "");
-  static_assert(ipast == ipast, "");
-  static_assert(ipast < ifuture, "");
-  static_assert(ifuture > ipast, "");
-
-  // Arithmetic saturates
-  EXPECT_EQ(ifuture, ifuture + absl::Seconds(1));
-  EXPECT_EQ(ifuture, ifuture - absl::Seconds(1));
-  EXPECT_EQ(ipast, ipast + absl::Seconds(1));
-  EXPECT_EQ(ipast, ipast - absl::Seconds(1));
-
-  EXPECT_EQ(absl::InfiniteDuration(), ifuture - ifuture);
-  EXPECT_EQ(absl::InfiniteDuration(), ifuture - ipast);
-  EXPECT_EQ(-absl::InfiniteDuration(), ipast - ifuture);
-  EXPECT_EQ(-absl::InfiniteDuration(), ipast - ipast);
-
-  constexpr absl::Time t = absl::UnixEpoch();  // Any finite time.
-  static_assert(t < ifuture, "");
-  static_assert(t > ipast, "");
-
-  EXPECT_EQ(ifuture, t + absl::InfiniteDuration());
-  EXPECT_EQ(ipast, t - absl::InfiniteDuration());
-}
-
-TEST(Time, FloorConversion) {
-#define TEST_FLOOR_CONVERSION(TO, FROM) \
-  EXPECT_EQ(1, TO(FROM(1001)));         \
-  EXPECT_EQ(1, TO(FROM(1000)));         \
-  EXPECT_EQ(0, TO(FROM(999)));          \
-  EXPECT_EQ(0, TO(FROM(1)));            \
-  EXPECT_EQ(0, TO(FROM(0)));            \
-  EXPECT_EQ(-1, TO(FROM(-1)));          \
-  EXPECT_EQ(-1, TO(FROM(-999)));        \
-  EXPECT_EQ(-1, TO(FROM(-1000)));       \
-  EXPECT_EQ(-2, TO(FROM(-1001)));
-
-  TEST_FLOOR_CONVERSION(absl::ToUnixMicros, absl::FromUnixNanos);
-  TEST_FLOOR_CONVERSION(absl::ToUnixMillis, absl::FromUnixMicros);
-  TEST_FLOOR_CONVERSION(absl::ToUnixSeconds, absl::FromUnixMillis);
-  TEST_FLOOR_CONVERSION(absl::ToTimeT, absl::FromUnixMillis);
-
-#undef TEST_FLOOR_CONVERSION
-
-  // Tests ToUnixNanos.
-  EXPECT_EQ(1, absl::ToUnixNanos(absl::UnixEpoch() + absl::Nanoseconds(3) / 2));
-  EXPECT_EQ(1, absl::ToUnixNanos(absl::UnixEpoch() + absl::Nanoseconds(1)));
-  EXPECT_EQ(0, absl::ToUnixNanos(absl::UnixEpoch() + absl::Nanoseconds(1) / 2));
-  EXPECT_EQ(0, absl::ToUnixNanos(absl::UnixEpoch() + absl::Nanoseconds(0)));
-  EXPECT_EQ(-1,
-            absl::ToUnixNanos(absl::UnixEpoch() - absl::Nanoseconds(1) / 2));
-  EXPECT_EQ(-1, absl::ToUnixNanos(absl::UnixEpoch() - absl::Nanoseconds(1)));
-  EXPECT_EQ(-2,
-            absl::ToUnixNanos(absl::UnixEpoch() - absl::Nanoseconds(3) / 2));
-
-  // Tests ToUniversal, which uses a different epoch than the tests above.
-  EXPECT_EQ(1,
-            absl::ToUniversal(absl::UniversalEpoch() + absl::Nanoseconds(101)));
-  EXPECT_EQ(1,
-            absl::ToUniversal(absl::UniversalEpoch() + absl::Nanoseconds(100)));
-  EXPECT_EQ(0,
-            absl::ToUniversal(absl::UniversalEpoch() + absl::Nanoseconds(99)));
-  EXPECT_EQ(0,
-            absl::ToUniversal(absl::UniversalEpoch() + absl::Nanoseconds(1)));
-  EXPECT_EQ(0,
-            absl::ToUniversal(absl::UniversalEpoch() + absl::Nanoseconds(0)));
-  EXPECT_EQ(-1,
-            absl::ToUniversal(absl::UniversalEpoch() + absl::Nanoseconds(-1)));
-  EXPECT_EQ(-1,
-            absl::ToUniversal(absl::UniversalEpoch() + absl::Nanoseconds(-99)));
-  EXPECT_EQ(
-      -1, absl::ToUniversal(absl::UniversalEpoch() + absl::Nanoseconds(-100)));
-  EXPECT_EQ(
-      -2, absl::ToUniversal(absl::UniversalEpoch() + absl::Nanoseconds(-101)));
-
-  // Tests ToTimespec()/TimeFromTimespec()
-  const struct {
-    absl::Time t;
-    timespec ts;
-  } to_ts[] = {
-      {absl::FromUnixSeconds(1) + absl::Nanoseconds(1), {1, 1}},
-      {absl::FromUnixSeconds(1) + absl::Nanoseconds(1) / 2, {1, 0}},
-      {absl::FromUnixSeconds(1) + absl::Nanoseconds(0), {1, 0}},
-      {absl::FromUnixSeconds(0) + absl::Nanoseconds(0), {0, 0}},
-      {absl::FromUnixSeconds(0) - absl::Nanoseconds(1) / 2, {-1, 999999999}},
-      {absl::FromUnixSeconds(0) - absl::Nanoseconds(1), {-1, 999999999}},
-      {absl::FromUnixSeconds(-1) + absl::Nanoseconds(1), {-1, 1}},
-      {absl::FromUnixSeconds(-1) + absl::Nanoseconds(1) / 2, {-1, 0}},
-      {absl::FromUnixSeconds(-1) + absl::Nanoseconds(0), {-1, 0}},
-      {absl::FromUnixSeconds(-1) - absl::Nanoseconds(1) / 2, {-2, 999999999}},
-  };
-  for (const auto& test : to_ts) {
-    EXPECT_THAT(absl::ToTimespec(test.t), TimespecMatcher(test.ts));
-  }
-  const struct {
-    timespec ts;
-    absl::Time t;
-  } from_ts[] = {
-      {{1, 1}, absl::FromUnixSeconds(1) + absl::Nanoseconds(1)},
-      {{1, 0}, absl::FromUnixSeconds(1) + absl::Nanoseconds(0)},
-      {{0, 0}, absl::FromUnixSeconds(0) + absl::Nanoseconds(0)},
-      {{0, -1}, absl::FromUnixSeconds(0) - absl::Nanoseconds(1)},
-      {{-1, 999999999}, absl::FromUnixSeconds(0) - absl::Nanoseconds(1)},
-      {{-1, 1}, absl::FromUnixSeconds(-1) + absl::Nanoseconds(1)},
-      {{-1, 0}, absl::FromUnixSeconds(-1) + absl::Nanoseconds(0)},
-      {{-1, -1}, absl::FromUnixSeconds(-1) - absl::Nanoseconds(1)},
-      {{-2, 999999999}, absl::FromUnixSeconds(-1) - absl::Nanoseconds(1)},
-  };
-  for (const auto& test : from_ts) {
-    EXPECT_EQ(test.t, absl::TimeFromTimespec(test.ts));
-  }
-
-  // Tests ToTimeval()/TimeFromTimeval() (same as timespec above)
-  const struct {
-    absl::Time t;
-    timeval tv;
-  } to_tv[] = {
-      {absl::FromUnixSeconds(1) + absl::Microseconds(1), {1, 1}},
-      {absl::FromUnixSeconds(1) + absl::Microseconds(1) / 2, {1, 0}},
-      {absl::FromUnixSeconds(1) + absl::Microseconds(0), {1, 0}},
-      {absl::FromUnixSeconds(0) + absl::Microseconds(0), {0, 0}},
-      {absl::FromUnixSeconds(0) - absl::Microseconds(1) / 2, {-1, 999999}},
-      {absl::FromUnixSeconds(0) - absl::Microseconds(1), {-1, 999999}},
-      {absl::FromUnixSeconds(-1) + absl::Microseconds(1), {-1, 1}},
-      {absl::FromUnixSeconds(-1) + absl::Microseconds(1) / 2, {-1, 0}},
-      {absl::FromUnixSeconds(-1) + absl::Microseconds(0), {-1, 0}},
-      {absl::FromUnixSeconds(-1) - absl::Microseconds(1) / 2, {-2, 999999}},
-  };
-  for (const auto& test : to_tv) {
-    EXPECT_THAT(ToTimeval(test.t), TimevalMatcher(test.tv));
-  }
-  const struct {
-    timeval tv;
-    absl::Time t;
-  } from_tv[] = {
-      {{1, 1}, absl::FromUnixSeconds(1) + absl::Microseconds(1)},
-      {{1, 0}, absl::FromUnixSeconds(1) + absl::Microseconds(0)},
-      {{0, 0}, absl::FromUnixSeconds(0) + absl::Microseconds(0)},
-      {{0, -1}, absl::FromUnixSeconds(0) - absl::Microseconds(1)},
-      {{-1, 999999}, absl::FromUnixSeconds(0) - absl::Microseconds(1)},
-      {{-1, 1}, absl::FromUnixSeconds(-1) + absl::Microseconds(1)},
-      {{-1, 0}, absl::FromUnixSeconds(-1) + absl::Microseconds(0)},
-      {{-1, -1}, absl::FromUnixSeconds(-1) - absl::Microseconds(1)},
-      {{-2, 999999}, absl::FromUnixSeconds(-1) - absl::Microseconds(1)},
-  };
-  for (const auto& test : from_tv) {
-    EXPECT_EQ(test.t, absl::TimeFromTimeval(test.tv));
-  }
-
-  // Tests flooring near negative infinity.
-  const int64_t min_plus_1 = std::numeric_limits<int64_t>::min() + 1;
-  EXPECT_EQ(min_plus_1, absl::ToUnixSeconds(absl::FromUnixSeconds(min_plus_1)));
-  EXPECT_EQ(std::numeric_limits<int64_t>::min(),
-            absl::ToUnixSeconds(absl::FromUnixSeconds(min_plus_1) -
-                                absl::Nanoseconds(1) / 2));
-
-  // Tests flooring near positive infinity.
-  EXPECT_EQ(std::numeric_limits<int64_t>::max(),
-            absl::ToUnixSeconds(
-                absl::FromUnixSeconds(std::numeric_limits<int64_t>::max()) +
-                absl::Nanoseconds(1) / 2));
-  EXPECT_EQ(std::numeric_limits<int64_t>::max(),
-            absl::ToUnixSeconds(
-                absl::FromUnixSeconds(std::numeric_limits<int64_t>::max())));
-  EXPECT_EQ(std::numeric_limits<int64_t>::max() - 1,
-            absl::ToUnixSeconds(
-                absl::FromUnixSeconds(std::numeric_limits<int64_t>::max()) -
-                absl::Nanoseconds(1) / 2));
-}
-
-TEST(Time, RoundtripConversion) {
-#define TEST_CONVERSION_ROUND_TRIP(SOURCE, FROM, TO, MATCHER) \
-  EXPECT_THAT(TO(FROM(SOURCE)), MATCHER(SOURCE))
-
-  // FromUnixNanos() and ToUnixNanos()
-  int64_t now_ns = absl::GetCurrentTimeNanos();
-  TEST_CONVERSION_ROUND_TRIP(-1, absl::FromUnixNanos, absl::ToUnixNanos,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(0, absl::FromUnixNanos, absl::ToUnixNanos,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(1, absl::FromUnixNanos, absl::ToUnixNanos,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(now_ns, absl::FromUnixNanos, absl::ToUnixNanos,
-                             testing::Eq)
-      << now_ns;
-
-  // FromUnixMicros() and ToUnixMicros()
-  int64_t now_us = absl::GetCurrentTimeNanos() / 1000;
-  TEST_CONVERSION_ROUND_TRIP(-1, absl::FromUnixMicros, absl::ToUnixMicros,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(0, absl::FromUnixMicros, absl::ToUnixMicros,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(1, absl::FromUnixMicros, absl::ToUnixMicros,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(now_us, absl::FromUnixMicros, absl::ToUnixMicros,
-                             testing::Eq)
-      << now_us;
-
-  // FromUnixMillis() and ToUnixMillis()
-  int64_t now_ms = absl::GetCurrentTimeNanos() / 1000000;
-  TEST_CONVERSION_ROUND_TRIP(-1, absl::FromUnixMillis, absl::ToUnixMillis,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(0, absl::FromUnixMillis, absl::ToUnixMillis,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(1, absl::FromUnixMillis, absl::ToUnixMillis,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(now_ms, absl::FromUnixMillis, absl::ToUnixMillis,
-                             testing::Eq)
-      << now_ms;
-
-  // FromUnixSeconds() and ToUnixSeconds()
-  int64_t now_s = std::time(nullptr);
-  TEST_CONVERSION_ROUND_TRIP(-1, absl::FromUnixSeconds, absl::ToUnixSeconds,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(0, absl::FromUnixSeconds, absl::ToUnixSeconds,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(1, absl::FromUnixSeconds, absl::ToUnixSeconds,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(now_s, absl::FromUnixSeconds, absl::ToUnixSeconds,
-                             testing::Eq)
-      << now_s;
-
-  // FromTimeT() and ToTimeT()
-  time_t now_time_t = std::time(nullptr);
-  TEST_CONVERSION_ROUND_TRIP(-1, absl::FromTimeT, absl::ToTimeT, testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(0, absl::FromTimeT, absl::ToTimeT, testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(1, absl::FromTimeT, absl::ToTimeT, testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(now_time_t, absl::FromTimeT, absl::ToTimeT,
-                             testing::Eq)
-      << now_time_t;
-
-  // TimeFromTimeval() and ToTimeval()
-  timeval tv;
-  tv.tv_sec = -1;
-  tv.tv_usec = 0;
-  TEST_CONVERSION_ROUND_TRIP(tv, absl::TimeFromTimeval, absl::ToTimeval,
-                             TimevalMatcher);
-  tv.tv_sec = -1;
-  tv.tv_usec = 999999;
-  TEST_CONVERSION_ROUND_TRIP(tv, absl::TimeFromTimeval, absl::ToTimeval,
-                             TimevalMatcher);
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
-  TEST_CONVERSION_ROUND_TRIP(tv, absl::TimeFromTimeval, absl::ToTimeval,
-                             TimevalMatcher);
-  tv.tv_sec = 0;
-  tv.tv_usec = 1;
-  TEST_CONVERSION_ROUND_TRIP(tv, absl::TimeFromTimeval, absl::ToTimeval,
-                             TimevalMatcher);
-  tv.tv_sec = 1;
-  tv.tv_usec = 0;
-  TEST_CONVERSION_ROUND_TRIP(tv, absl::TimeFromTimeval, absl::ToTimeval,
-                             TimevalMatcher);
-
-  // TimeFromTimespec() and ToTimespec()
-  timespec ts;
-  ts.tv_sec = -1;
-  ts.tv_nsec = 0;
-  TEST_CONVERSION_ROUND_TRIP(ts, absl::TimeFromTimespec, absl::ToTimespec,
-                             TimespecMatcher);
-  ts.tv_sec = -1;
-  ts.tv_nsec = 999999999;
-  TEST_CONVERSION_ROUND_TRIP(ts, absl::TimeFromTimespec, absl::ToTimespec,
-                             TimespecMatcher);
-  ts.tv_sec = 0;
-  ts.tv_nsec = 0;
-  TEST_CONVERSION_ROUND_TRIP(ts, absl::TimeFromTimespec, absl::ToTimespec,
-                             TimespecMatcher);
-  ts.tv_sec = 0;
-  ts.tv_nsec = 1;
-  TEST_CONVERSION_ROUND_TRIP(ts, absl::TimeFromTimespec, absl::ToTimespec,
-                             TimespecMatcher);
-  ts.tv_sec = 1;
-  ts.tv_nsec = 0;
-  TEST_CONVERSION_ROUND_TRIP(ts, absl::TimeFromTimespec, absl::ToTimespec,
-                             TimespecMatcher);
-
-  // FromUDate() and ToUDate()
-  double now_ud = absl::GetCurrentTimeNanos() / 1000000;
-  TEST_CONVERSION_ROUND_TRIP(-1.5, absl::FromUDate, absl::ToUDate,
-                             testing::DoubleEq);
-  TEST_CONVERSION_ROUND_TRIP(-1, absl::FromUDate, absl::ToUDate,
-                             testing::DoubleEq);
-  TEST_CONVERSION_ROUND_TRIP(-0.5, absl::FromUDate, absl::ToUDate,
-                             testing::DoubleEq);
-  TEST_CONVERSION_ROUND_TRIP(0, absl::FromUDate, absl::ToUDate,
-                             testing::DoubleEq);
-  TEST_CONVERSION_ROUND_TRIP(0.5, absl::FromUDate, absl::ToUDate,
-                             testing::DoubleEq);
-  TEST_CONVERSION_ROUND_TRIP(1, absl::FromUDate, absl::ToUDate,
-                             testing::DoubleEq);
-  TEST_CONVERSION_ROUND_TRIP(1.5, absl::FromUDate, absl::ToUDate,
-                             testing::DoubleEq);
-  TEST_CONVERSION_ROUND_TRIP(now_ud, absl::FromUDate, absl::ToUDate,
-                             testing::DoubleEq)
-      << std::fixed << std::setprecision(17) << now_ud;
-
-  // FromUniversal() and ToUniversal()
-  int64_t now_uni = ((719162LL * (24 * 60 * 60)) * (1000 * 1000 * 10)) +
-                    (absl::GetCurrentTimeNanos() / 100);
-  TEST_CONVERSION_ROUND_TRIP(-1, absl::FromUniversal, absl::ToUniversal,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(0, absl::FromUniversal, absl::ToUniversal,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(1, absl::FromUniversal, absl::ToUniversal,
-                             testing::Eq);
-  TEST_CONVERSION_ROUND_TRIP(now_uni, absl::FromUniversal, absl::ToUniversal,
-                             testing::Eq)
-      << now_uni;
-
-#undef TEST_CONVERSION_ROUND_TRIP
-}
-
-template <typename Duration>
-std::chrono::system_clock::time_point MakeChronoUnixTime(const Duration& d) {
-  return std::chrono::system_clock::from_time_t(0) + d;
-}
-
-TEST(Time, FromChrono) {
-  EXPECT_EQ(absl::FromTimeT(-1),
-            absl::FromChrono(std::chrono::system_clock::from_time_t(-1)));
-  EXPECT_EQ(absl::FromTimeT(0),
-            absl::FromChrono(std::chrono::system_clock::from_time_t(0)));
-  EXPECT_EQ(absl::FromTimeT(1),
-            absl::FromChrono(std::chrono::system_clock::from_time_t(1)));
-
-  EXPECT_EQ(
-      absl::FromUnixMillis(-1),
-      absl::FromChrono(MakeChronoUnixTime(std::chrono::milliseconds(-1))));
-  EXPECT_EQ(absl::FromUnixMillis(0),
-            absl::FromChrono(MakeChronoUnixTime(std::chrono::milliseconds(0))));
-  EXPECT_EQ(absl::FromUnixMillis(1),
-            absl::FromChrono(MakeChronoUnixTime(std::chrono::milliseconds(1))));
-
-  // Chrono doesn't define exactly its range and precision (neither does
-  // absl::Time), so let's simply test +/- ~100 years to make sure things work.
-  const auto century_sec = 60 * 60 * 24 * 365 * int64_t{100};
-  const auto century = std::chrono::seconds(century_sec);
-  const auto chrono_future = MakeChronoUnixTime(century);
-  const auto chrono_past = MakeChronoUnixTime(-century);
-  EXPECT_EQ(absl::FromUnixSeconds(century_sec),
-            absl::FromChrono(chrono_future));
-  EXPECT_EQ(absl::FromUnixSeconds(-century_sec), absl::FromChrono(chrono_past));
-
-  // Roundtrip them both back to chrono.
-  EXPECT_EQ(chrono_future,
-            absl::ToChronoTime(absl::FromUnixSeconds(century_sec)));
-  EXPECT_EQ(chrono_past,
-            absl::ToChronoTime(absl::FromUnixSeconds(-century_sec)));
-}
-
-TEST(Time, ToChronoTime) {
-  EXPECT_EQ(std::chrono::system_clock::from_time_t(-1),
-            absl::ToChronoTime(absl::FromTimeT(-1)));
-  EXPECT_EQ(std::chrono::system_clock::from_time_t(0),
-            absl::ToChronoTime(absl::FromTimeT(0)));
-  EXPECT_EQ(std::chrono::system_clock::from_time_t(1),
-            absl::ToChronoTime(absl::FromTimeT(1)));
-
-  EXPECT_EQ(MakeChronoUnixTime(std::chrono::milliseconds(-1)),
-            absl::ToChronoTime(absl::FromUnixMillis(-1)));
-  EXPECT_EQ(MakeChronoUnixTime(std::chrono::milliseconds(0)),
-            absl::ToChronoTime(absl::FromUnixMillis(0)));
-  EXPECT_EQ(MakeChronoUnixTime(std::chrono::milliseconds(1)),
-            absl::ToChronoTime(absl::FromUnixMillis(1)));
-
-  // Time before the Unix epoch should floor, not trunc.
-  const auto tick = absl::Nanoseconds(1) / 4;
-  EXPECT_EQ(std::chrono::system_clock::from_time_t(0) -
-                std::chrono::system_clock::duration(1),
-            absl::ToChronoTime(absl::UnixEpoch() - tick));
-}
-
-// Check that absl::int128 works as a std::chrono::duration representation.
-TEST(Time, Chrono128) {
-  // Define a std::chrono::time_point type whose time[sic]_since_epoch() is
-  // a signed 128-bit count of attoseconds. This has a range and resolution
-  // (currently) beyond those of absl::Time, and undoubtedly also beyond those
-  // of std::chrono::system_clock::time_point.
-  //
-  // Note: The to/from-chrono support should probably be updated to handle
-  // such wide representations.
-  using Timestamp =
-      std::chrono::time_point<std::chrono::system_clock,
-                              std::chrono::duration<absl::int128, std::atto>>;
-
-  // Expect that we can round-trip the std::chrono::system_clock::time_point
-  // extremes through both absl::Time and Timestamp, and that Timestamp can
-  // handle the (current) absl::Time extremes.
-  //
-  // Note: We should use std::chrono::floor() instead of time_point_cast(),
-  // but floor() is only available since c++17.
-  for (const auto tp : {std::chrono::system_clock::time_point::min(),
-                        std::chrono::system_clock::time_point::max()}) {
-    EXPECT_EQ(tp, absl::ToChronoTime(absl::FromChrono(tp)));
-    EXPECT_EQ(tp, std::chrono::time_point_cast<
-                      std::chrono::system_clock::time_point::duration>(
-                      std::chrono::time_point_cast<Timestamp::duration>(tp)));
-  }
-  Timestamp::duration::rep v = std::numeric_limits<int64_t>::min();
-  v *= Timestamp::duration::period::den;
-  auto ts = Timestamp(Timestamp::duration(v));
-  ts += std::chrono::duration<int64_t, std::atto>(0);
-  EXPECT_EQ(std::numeric_limits<int64_t>::min(),
-            ts.time_since_epoch().count() / Timestamp::duration::period::den);
-  EXPECT_EQ(0,
-            ts.time_since_epoch().count() % Timestamp::duration::period::den);
-  v = std::numeric_limits<int64_t>::max();
-  v *= Timestamp::duration::period::den;
-  ts = Timestamp(Timestamp::duration(v));
-  ts += std::chrono::duration<int64_t, std::atto>(999999999750000000);
-  EXPECT_EQ(std::numeric_limits<int64_t>::max(),
-            ts.time_since_epoch().count() / Timestamp::duration::period::den);
-  EXPECT_EQ(999999999750000000,
-            ts.time_since_epoch().count() % Timestamp::duration::period::den);
-}
-
-TEST(Time, TimeZoneAt) {
-  const absl::TimeZone nyc =
-      absl::time_internal::LoadTimeZone("America/New_York");
-  const std::string fmt = "%a, %e %b %Y %H:%M:%S %z (%Z)";
-
-  // A non-transition where the civil time is unique.
-  absl::CivilSecond nov01(2013, 11, 1, 8, 30, 0);
-  const auto nov01_ci = nyc.At(nov01);
-  EXPECT_EQ(absl::TimeZone::TimeInfo::UNIQUE, nov01_ci.kind);
-  EXPECT_EQ("Fri,  1 Nov 2013 08:30:00 -0400 (EDT)",
-            absl::FormatTime(fmt, nov01_ci.pre, nyc));
-  EXPECT_EQ(nov01_ci.pre, nov01_ci.trans);
-  EXPECT_EQ(nov01_ci.pre, nov01_ci.post);
-  EXPECT_EQ(nov01_ci.pre, absl::FromCivil(nov01, nyc));
-
-  // A Spring DST transition, when there is a gap in civil time
-  // and we prefer the later of the possible interpretations of a
-  // non-existent time.
-  absl::CivilSecond mar13(2011, 3, 13, 2, 15, 0);
-  const auto mar_ci = nyc.At(mar13);
-  EXPECT_EQ(absl::TimeZone::TimeInfo::SKIPPED, mar_ci.kind);
-  EXPECT_EQ("Sun, 13 Mar 2011 03:15:00 -0400 (EDT)",
-            absl::FormatTime(fmt, mar_ci.pre, nyc));
-  EXPECT_EQ("Sun, 13 Mar 2011 03:00:00 -0400 (EDT)",
-            absl::FormatTime(fmt, mar_ci.trans, nyc));
-  EXPECT_EQ("Sun, 13 Mar 2011 01:15:00 -0500 (EST)",
-            absl::FormatTime(fmt, mar_ci.post, nyc));
-  EXPECT_EQ(mar_ci.trans, absl::FromCivil(mar13, nyc));
-
-  // A Fall DST transition, when civil times are repeated and
-  // we prefer the earlier of the possible interpretations of an
-  // ambiguous time.
-  absl::CivilSecond nov06(2011, 11, 6, 1, 15, 0);
-  const auto nov06_ci = nyc.At(nov06);
-  EXPECT_EQ(absl::TimeZone::TimeInfo::REPEATED, nov06_ci.kind);
-  EXPECT_EQ("Sun,  6 Nov 2011 01:15:00 -0400 (EDT)",
-            absl::FormatTime(fmt, nov06_ci.pre, nyc));
-  EXPECT_EQ("Sun,  6 Nov 2011 01:00:00 -0500 (EST)",
-            absl::FormatTime(fmt, nov06_ci.trans, nyc));
-  EXPECT_EQ("Sun,  6 Nov 2011 01:15:00 -0500 (EST)",
-            absl::FormatTime(fmt, nov06_ci.post, nyc));
-  EXPECT_EQ(nov06_ci.pre, absl::FromCivil(nov06, nyc));
-
-  // Check that (time_t) -1 is handled correctly.
-  absl::CivilSecond minus1(1969, 12, 31, 18, 59, 59);
-  const auto minus1_cl = nyc.At(minus1);
-  EXPECT_EQ(absl::TimeZone::TimeInfo::UNIQUE, minus1_cl.kind);
-  EXPECT_EQ(-1, absl::ToTimeT(minus1_cl.pre));
-  EXPECT_EQ("Wed, 31 Dec 1969 18:59:59 -0500 (EST)",
-            absl::FormatTime(fmt, minus1_cl.pre, nyc));
-  EXPECT_EQ("Wed, 31 Dec 1969 23:59:59 +0000 (UTC)",
-            absl::FormatTime(fmt, minus1_cl.pre, absl::UTCTimeZone()));
-}
-
-// FromCivil(CivilSecond(year, mon, day, hour, min, sec), UTCTimeZone())
-// has a specialized fastpath implementation, which we exercise here.
-TEST(Time, FromCivilUTC) {
-  const absl::TimeZone utc = absl::UTCTimeZone();
-  const std::string fmt = "%a, %e %b %Y %H:%M:%S %z (%Z)";
-  const int kMax = std::numeric_limits<int>::max();
-  const int kMin = std::numeric_limits<int>::min();
-  absl::Time t;
-
-  // 292091940881 is the last positive year to use the fastpath.
-  t = absl::FromCivil(
-      absl::CivilSecond(292091940881, kMax, kMax, kMax, kMax, kMax), utc);
-  EXPECT_EQ("Fri, 25 Nov 292277026596 12:21:07 +0000 (UTC)",
-            absl::FormatTime(fmt, t, utc));
-  t = absl::FromCivil(
-      absl::CivilSecond(292091940882, kMax, kMax, kMax, kMax, kMax), utc);
-  EXPECT_EQ("infinite-future", absl::FormatTime(fmt, t, utc));  // no overflow
-
-  // -292091936940 is the last negative year to use the fastpath.
-  t = absl::FromCivil(
-      absl::CivilSecond(-292091936940, kMin, kMin, kMin, kMin, kMin), utc);
-  EXPECT_EQ("Fri,  1 Nov -292277022657 10:37:52 +0000 (UTC)",
-            absl::FormatTime(fmt, t, utc));
-  t = absl::FromCivil(
-      absl::CivilSecond(-292091936941, kMin, kMin, kMin, kMin, kMin), utc);
-  EXPECT_EQ("infinite-past", absl::FormatTime(fmt, t, utc));  // no underflow
-
-  // Check that we're counting leap years correctly.
-  t = absl::FromCivil(absl::CivilSecond(1900, 2, 28, 23, 59, 59), utc);
-  EXPECT_EQ("Wed, 28 Feb 1900 23:59:59 +0000 (UTC)",
-            absl::FormatTime(fmt, t, utc));
-  t = absl::FromCivil(absl::CivilSecond(1900, 3, 1, 0, 0, 0), utc);
-  EXPECT_EQ("Thu,  1 Mar 1900 00:00:00 +0000 (UTC)",
-            absl::FormatTime(fmt, t, utc));
-  t = absl::FromCivil(absl::CivilSecond(2000, 2, 29, 23, 59, 59), utc);
-  EXPECT_EQ("Tue, 29 Feb 2000 23:59:59 +0000 (UTC)",
-            absl::FormatTime(fmt, t, utc));
-  t = absl::FromCivil(absl::CivilSecond(2000, 3, 1, 0, 0, 0), utc);
-  EXPECT_EQ("Wed,  1 Mar 2000 00:00:00 +0000 (UTC)",
-            absl::FormatTime(fmt, t, utc));
-}
-
-TEST(Time, ToTM) {
-  const absl::TimeZone utc = absl::UTCTimeZone();
-
-  // Compares the results of ToTM() to gmtime_r() for lots of times over the
-  // course of a few days.
-  const absl::Time start =
-      absl::FromCivil(absl::CivilSecond(2014, 1, 2, 3, 4, 5), utc);
-  const absl::Time end =
-      absl::FromCivil(absl::CivilSecond(2014, 1, 5, 3, 4, 5), utc);
-  for (absl::Time t = start; t < end; t += absl::Seconds(30)) {
-    const struct tm tm_bt = ToTM(t, utc);
-    const time_t tt = absl::ToTimeT(t);
-    struct tm tm_lc;
-#ifdef _WIN32
-    gmtime_s(&tm_lc, &tt);
-#else
-    gmtime_r(&tt, &tm_lc);
-#endif
-    EXPECT_EQ(tm_lc.tm_year, tm_bt.tm_year);
-    EXPECT_EQ(tm_lc.tm_mon, tm_bt.tm_mon);
-    EXPECT_EQ(tm_lc.tm_mday, tm_bt.tm_mday);
-    EXPECT_EQ(tm_lc.tm_hour, tm_bt.tm_hour);
-    EXPECT_EQ(tm_lc.tm_min, tm_bt.tm_min);
-    EXPECT_EQ(tm_lc.tm_sec, tm_bt.tm_sec);
-    EXPECT_EQ(tm_lc.tm_wday, tm_bt.tm_wday);
-    EXPECT_EQ(tm_lc.tm_yday, tm_bt.tm_yday);
-    EXPECT_EQ(tm_lc.tm_isdst, tm_bt.tm_isdst);
-
-    ASSERT_FALSE(HasFailure());
-  }
-
-  // Checks that the tm_isdst field is correct when in standard time.
-  const absl::TimeZone nyc =
-      absl::time_internal::LoadTimeZone("America/New_York");
-  absl::Time t = absl::FromCivil(absl::CivilSecond(2014, 3, 1, 0, 0, 0), nyc);
-  struct tm tm = ToTM(t, nyc);
-  EXPECT_FALSE(tm.tm_isdst);
-
-  // Checks that the tm_isdst field is correct when in daylight time.
-  t = absl::FromCivil(absl::CivilSecond(2014, 4, 1, 0, 0, 0), nyc);
-  tm = ToTM(t, nyc);
-  EXPECT_TRUE(tm.tm_isdst);
-
-  // Checks overflow.
-  tm = ToTM(absl::InfiniteFuture(), nyc);
-  EXPECT_EQ(std::numeric_limits<int>::max() - 1900, tm.tm_year);
-  EXPECT_EQ(11, tm.tm_mon);
-  EXPECT_EQ(31, tm.tm_mday);
-  EXPECT_EQ(23, tm.tm_hour);
-  EXPECT_EQ(59, tm.tm_min);
-  EXPECT_EQ(59, tm.tm_sec);
-  EXPECT_EQ(4, tm.tm_wday);
-  EXPECT_EQ(364, tm.tm_yday);
-  EXPECT_FALSE(tm.tm_isdst);
-
-  // Checks underflow.
-  tm = ToTM(absl::InfinitePast(), nyc);
-  EXPECT_EQ(std::numeric_limits<int>::min(), tm.tm_year);
-  EXPECT_EQ(0, tm.tm_mon);
-  EXPECT_EQ(1, tm.tm_mday);
-  EXPECT_EQ(0, tm.tm_hour);
-  EXPECT_EQ(0, tm.tm_min);
-  EXPECT_EQ(0, tm.tm_sec);
-  EXPECT_EQ(0, tm.tm_wday);
-  EXPECT_EQ(0, tm.tm_yday);
-  EXPECT_FALSE(tm.tm_isdst);
-}
-
-TEST(Time, FromTM) {
-  const absl::TimeZone nyc =
-      absl::time_internal::LoadTimeZone("America/New_York");
-
-  // Verifies that tm_isdst doesn't affect anything when the time is unique.
-  struct tm tm;
-  std::memset(&tm, 0, sizeof(tm));
-  tm.tm_year = 2014 - 1900;
-  tm.tm_mon = 6 - 1;
-  tm.tm_mday = 28;
-  tm.tm_hour = 1;
-  tm.tm_min = 2;
-  tm.tm_sec = 3;
-  tm.tm_isdst = -1;
-  absl::Time t = FromTM(tm, nyc);
-  EXPECT_EQ("2014-06-28T01:02:03-04:00", absl::FormatTime(t, nyc));  // DST
-  tm.tm_isdst = 0;
-  t = FromTM(tm, nyc);
-  EXPECT_EQ("2014-06-28T01:02:03-04:00", absl::FormatTime(t, nyc));  // DST
-  tm.tm_isdst = 1;
-  t = FromTM(tm, nyc);
-  EXPECT_EQ("2014-06-28T01:02:03-04:00", absl::FormatTime(t, nyc));  // DST
-
-  // Adjusts tm to refer to an ambiguous time.
-  tm.tm_year = 2014 - 1900;
-  tm.tm_mon = 11 - 1;
-  tm.tm_mday = 2;
-  tm.tm_hour = 1;
-  tm.tm_min = 30;
-  tm.tm_sec = 42;
-  tm.tm_isdst = -1;
-  t = FromTM(tm, nyc);
-  EXPECT_EQ("2014-11-02T01:30:42-04:00", absl::FormatTime(t, nyc));  // DST
-  tm.tm_isdst = 0;
-  t = FromTM(tm, nyc);
-  EXPECT_EQ("2014-11-02T01:30:42-05:00", absl::FormatTime(t, nyc));  // STD
-  tm.tm_isdst = 1;
-  t = FromTM(tm, nyc);
-  EXPECT_EQ("2014-11-02T01:30:42-04:00", absl::FormatTime(t, nyc));  // DST
-
-  // Adjusts tm to refer to a skipped time.
-  tm.tm_year = 2014 - 1900;
-  tm.tm_mon = 3 - 1;
-  tm.tm_mday = 9;
-  tm.tm_hour = 2;
-  tm.tm_min = 30;
-  tm.tm_sec = 42;
-  tm.tm_isdst = -1;
-  t = FromTM(tm, nyc);
-  EXPECT_EQ("2014-03-09T03:30:42-04:00", absl::FormatTime(t, nyc));  // DST
-  tm.tm_isdst = 0;
-  t = FromTM(tm, nyc);
-  EXPECT_EQ("2014-03-09T01:30:42-05:00", absl::FormatTime(t, nyc));  // STD
-  tm.tm_isdst = 1;
-  t = FromTM(tm, nyc);
-  EXPECT_EQ("2014-03-09T03:30:42-04:00", absl::FormatTime(t, nyc));  // DST
-
-  // Adjusts tm to refer to a time with a year larger than 2147483647.
-  tm.tm_year = 2147483647 - 1900 + 1;
-  tm.tm_mon = 6 - 1;
-  tm.tm_mday = 28;
-  tm.tm_hour = 1;
-  tm.tm_min = 2;
-  tm.tm_sec = 3;
-  tm.tm_isdst = -1;
-  t = FromTM(tm, absl::UTCTimeZone());
-  EXPECT_EQ("2147483648-06-28T01:02:03+00:00",
-            absl::FormatTime(t, absl::UTCTimeZone()));
-
-  // Adjusts tm to refer to a time with a very large month.
-  tm.tm_year = 2019 - 1900;
-  tm.tm_mon = 2147483647;
-  tm.tm_mday = 28;
-  tm.tm_hour = 1;
-  tm.tm_min = 2;
-  tm.tm_sec = 3;
-  tm.tm_isdst = -1;
-  t = FromTM(tm, absl::UTCTimeZone());
-  EXPECT_EQ("178958989-08-28T01:02:03+00:00",
-            absl::FormatTime(t, absl::UTCTimeZone()));
-}
-
-TEST(Time, TMRoundTrip) {
-  const absl::TimeZone nyc =
-      absl::time_internal::LoadTimeZone("America/New_York");
-
-  // Test round-tripping across a skipped transition
-  absl::Time start = absl::FromCivil(absl::CivilHour(2014, 3, 9, 0), nyc);
-  absl::Time end = absl::FromCivil(absl::CivilHour(2014, 3, 9, 4), nyc);
-  for (absl::Time t = start; t < end; t += absl::Minutes(1)) {
-    struct tm tm = ToTM(t, nyc);
-    absl::Time rt = FromTM(tm, nyc);
-    EXPECT_EQ(rt, t);
-  }
-
-  // Test round-tripping across an ambiguous transition
-  start = absl::FromCivil(absl::CivilHour(2014, 11, 2, 0), nyc);
-  end = absl::FromCivil(absl::CivilHour(2014, 11, 2, 4), nyc);
-  for (absl::Time t = start; t < end; t += absl::Minutes(1)) {
-    struct tm tm = ToTM(t, nyc);
-    absl::Time rt = FromTM(tm, nyc);
-    EXPECT_EQ(rt, t);
-  }
-
-  // Test round-tripping of unique instants crossing a day boundary
-  start = absl::FromCivil(absl::CivilHour(2014, 6, 27, 22), nyc);
-  end = absl::FromCivil(absl::CivilHour(2014, 6, 28, 4), nyc);
-  for (absl::Time t = start; t < end; t += absl::Minutes(1)) {
-    struct tm tm = ToTM(t, nyc);
-    absl::Time rt = FromTM(tm, nyc);
-    EXPECT_EQ(rt, t);
-  }
-}
-
-TEST(Time, Range) {
-  // The API's documented range is +/- 100 billion years.
-  const absl::Duration range = absl::Hours(24) * 365.2425 * 100000000000;
-
-  // Arithmetic and comparison still works at +/-range around base values.
-  absl::Time bases[2] = {absl::UnixEpoch(), absl::Now()};
-  for (const auto base : bases) {
-    absl::Time bottom = base - range;
-    EXPECT_GT(bottom, bottom - absl::Nanoseconds(1));
-    EXPECT_LT(bottom, bottom + absl::Nanoseconds(1));
-    absl::Time top = base + range;
-    EXPECT_GT(top, top - absl::Nanoseconds(1));
-    EXPECT_LT(top, top + absl::Nanoseconds(1));
-    absl::Duration full_range = 2 * range;
-    EXPECT_EQ(full_range, top - bottom);
-    EXPECT_EQ(-full_range, bottom - top);
-  }
-}
-
-TEST(Time, Limits) {
-  // It is an implementation detail that Time().rep_ == ZeroDuration(),
-  // and that the resolution of a Duration is 1/4 of a nanosecond.
-  const absl::Time zero;
-  const absl::Time max =
-      zero + absl::Seconds(std::numeric_limits<int64_t>::max()) +
-      absl::Nanoseconds(999999999) + absl::Nanoseconds(3) / 4;
-  const absl::Time min =
-      zero + absl::Seconds(std::numeric_limits<int64_t>::min());
-
-  // Some simple max/min bounds checks.
-  EXPECT_LT(max, absl::InfiniteFuture());
-  EXPECT_GT(min, absl::InfinitePast());
-  EXPECT_LT(zero, max);
-  EXPECT_GT(zero, min);
-  EXPECT_GE(absl::UnixEpoch(), min);
-  EXPECT_LT(absl::UnixEpoch(), max);
-
-  // Check sign of Time differences.
-  EXPECT_LT(absl::ZeroDuration(), max - zero);
-  EXPECT_LT(absl::ZeroDuration(),
-            zero - absl::Nanoseconds(1) / 4 - min);  // avoid zero - min
-
-  // Arithmetic works at max - 0.25ns and min + 0.25ns.
-  EXPECT_GT(max, max - absl::Nanoseconds(1) / 4);
-  EXPECT_LT(min, min + absl::Nanoseconds(1) / 4);
-}
-
-TEST(Time, ConversionSaturation) {
-  const absl::TimeZone utc = absl::UTCTimeZone();
-  absl::Time t;
-
-  const auto max_time_t = std::numeric_limits<time_t>::max();
-  const auto min_time_t = std::numeric_limits<time_t>::min();
-  time_t tt = max_time_t - 1;
-  t = absl::FromTimeT(tt);
-  tt = absl::ToTimeT(t);
-  EXPECT_EQ(max_time_t - 1, tt);
-  t += absl::Seconds(1);
-  tt = absl::ToTimeT(t);
-  EXPECT_EQ(max_time_t, tt);
-  t += absl::Seconds(1);  // no effect
-  tt = absl::ToTimeT(t);
-  EXPECT_EQ(max_time_t, tt);
-
-  tt = min_time_t + 1;
-  t = absl::FromTimeT(tt);
-  tt = absl::ToTimeT(t);
-  EXPECT_EQ(min_time_t + 1, tt);
-  t -= absl::Seconds(1);
-  tt = absl::ToTimeT(t);
-  EXPECT_EQ(min_time_t, tt);
-  t -= absl::Seconds(1);  // no effect
-  tt = absl::ToTimeT(t);
-  EXPECT_EQ(min_time_t, tt);
-
-  const auto max_timeval_sec =
-      std::numeric_limits<decltype(timeval::tv_sec)>::max();
-  const auto min_timeval_sec =
-      std::numeric_limits<decltype(timeval::tv_sec)>::min();
-  timeval tv;
-  tv.tv_sec = max_timeval_sec;
-  tv.tv_usec = 999998;
-  t = absl::TimeFromTimeval(tv);
-  tv = ToTimeval(t);
-  EXPECT_EQ(max_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(999998, tv.tv_usec);
-  t += absl::Microseconds(1);
-  tv = ToTimeval(t);
-  EXPECT_EQ(max_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(999999, tv.tv_usec);
-  t += absl::Microseconds(1);  // no effect
-  tv = ToTimeval(t);
-  EXPECT_EQ(max_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(999999, tv.tv_usec);
-
-  tv.tv_sec = min_timeval_sec;
-  tv.tv_usec = 1;
-  t = absl::TimeFromTimeval(tv);
-  tv = ToTimeval(t);
-  EXPECT_EQ(min_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(1, tv.tv_usec);
-  t -= absl::Microseconds(1);
-  tv = ToTimeval(t);
-  EXPECT_EQ(min_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(0, tv.tv_usec);
-  t -= absl::Microseconds(1);  // no effect
-  tv = ToTimeval(t);
-  EXPECT_EQ(min_timeval_sec, tv.tv_sec);
-  EXPECT_EQ(0, tv.tv_usec);
-
-  const auto max_timespec_sec =
-      std::numeric_limits<decltype(timespec::tv_sec)>::max();
-  const auto min_timespec_sec =
-      std::numeric_limits<decltype(timespec::tv_sec)>::min();
-  timespec ts;
-  ts.tv_sec = max_timespec_sec;
-  ts.tv_nsec = 999999998;
-  t = absl::TimeFromTimespec(ts);
-  ts = absl::ToTimespec(t);
-  EXPECT_EQ(max_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(999999998, ts.tv_nsec);
-  t += absl::Nanoseconds(1);
-  ts = absl::ToTimespec(t);
-  EXPECT_EQ(max_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(999999999, ts.tv_nsec);
-  t += absl::Nanoseconds(1);  // no effect
-  ts = absl::ToTimespec(t);
-  EXPECT_EQ(max_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(999999999, ts.tv_nsec);
-
-  ts.tv_sec = min_timespec_sec;
-  ts.tv_nsec = 1;
-  t = absl::TimeFromTimespec(ts);
-  ts = absl::ToTimespec(t);
-  EXPECT_EQ(min_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(1, ts.tv_nsec);
-  t -= absl::Nanoseconds(1);
-  ts = absl::ToTimespec(t);
-  EXPECT_EQ(min_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(0, ts.tv_nsec);
-  t -= absl::Nanoseconds(1);  // no effect
-  ts = absl::ToTimespec(t);
-  EXPECT_EQ(min_timespec_sec, ts.tv_sec);
-  EXPECT_EQ(0, ts.tv_nsec);
-
-  // Checks how TimeZone::At() saturates on infinities.
-  auto ci = utc.At(absl::InfiniteFuture());
-  EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::max(), 12, 31, 23, 59, 59,
-                    0, false);
-  EXPECT_EQ(absl::InfiniteDuration(), ci.subsecond);
-  EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(ci.cs));
-  EXPECT_EQ(365, absl::GetYearDay(ci.cs));
-  EXPECT_STREQ("-00", ci.zone_abbr);  // artifact of TimeZone::At()
-  ci = utc.At(absl::InfinitePast());
-  EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::min(), 1, 1, 0, 0, 0, 0,
-                    false);
-  EXPECT_EQ(-absl::InfiniteDuration(), ci.subsecond);
-  EXPECT_EQ(absl::Weekday::sunday, absl::GetWeekday(ci.cs));
-  EXPECT_EQ(1, absl::GetYearDay(ci.cs));
-  EXPECT_STREQ("-00", ci.zone_abbr);  // artifact of TimeZone::At()
-
-  // Approach the maximal Time value from below.
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 15, 30, 6), utc);
-  EXPECT_EQ("292277026596-12-04T15:30:06+00:00",
-            absl::FormatTime(absl::RFC3339_full, t, utc));
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 15, 30, 7), utc);
-  EXPECT_EQ("292277026596-12-04T15:30:07+00:00",
-            absl::FormatTime(absl::RFC3339_full, t, utc));
-  EXPECT_EQ(
-      absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::max()),
-      t);
-
-  // Checks that we can also get the maximal Time value for a far-east zone.
-  const absl::TimeZone plus14 = absl::FixedTimeZone(14 * 60 * 60);
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 5, 5, 30, 7), plus14);
-  EXPECT_EQ("292277026596-12-05T05:30:07+14:00",
-            absl::FormatTime(absl::RFC3339_full, t, plus14));
-  EXPECT_EQ(
-      absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::max()),
-      t);
-
-  // One second later should push us to infinity.
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 15, 30, 8), utc);
-  EXPECT_EQ("infinite-future", absl::FormatTime(absl::RFC3339_full, t, utc));
-
-  // Approach the minimal Time value from above.
-  t = absl::FromCivil(absl::CivilSecond(-292277022657, 1, 27, 8, 29, 53), utc);
-  EXPECT_EQ("-292277022657-01-27T08:29:53+00:00",
-            absl::FormatTime(absl::RFC3339_full, t, utc));
-  t = absl::FromCivil(absl::CivilSecond(-292277022657, 1, 27, 8, 29, 52), utc);
-  EXPECT_EQ("-292277022657-01-27T08:29:52+00:00",
-            absl::FormatTime(absl::RFC3339_full, t, utc));
-  EXPECT_EQ(
-      absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::min()),
-      t);
-
-  // Checks that we can also get the minimal Time value for a far-west zone.
-  const absl::TimeZone minus12 = absl::FixedTimeZone(-12 * 60 * 60);
-  t = absl::FromCivil(absl::CivilSecond(-292277022657, 1, 26, 20, 29, 52),
-                      minus12);
-  EXPECT_EQ("-292277022657-01-26T20:29:52-12:00",
-            absl::FormatTime(absl::RFC3339_full, t, minus12));
-  EXPECT_EQ(
-      absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::min()),
-      t);
-
-  // One second before should push us to -infinity.
-  t = absl::FromCivil(absl::CivilSecond(-292277022657, 1, 27, 8, 29, 51), utc);
-  EXPECT_EQ("infinite-past", absl::FormatTime(absl::RFC3339_full, t, utc));
-}
-
-// In zones with POSIX-style recurring rules we use special logic to
-// handle conversions in the distant future.  Here we check the limits
-// of those conversions, particularly with respect to integer overflow.
-TEST(Time, ExtendedConversionSaturation) {
-  const absl::TimeZone syd =
-      absl::time_internal::LoadTimeZone("Australia/Sydney");
-  const absl::TimeZone nyc =
-      absl::time_internal::LoadTimeZone("America/New_York");
-  const absl::Time max =
-      absl::FromUnixSeconds(std::numeric_limits<int64_t>::max());
-  absl::TimeZone::CivilInfo ci;
-  absl::Time t;
-
-  // The maximal time converted in each zone.
-  ci = syd.At(max);
-  EXPECT_CIVIL_INFO(ci, 292277026596, 12, 5, 2, 30, 7, 39600, true);
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 5, 2, 30, 7), syd);
-  EXPECT_EQ(max, t);
-  ci = nyc.At(max);
-  EXPECT_CIVIL_INFO(ci, 292277026596, 12, 4, 10, 30, 7, -18000, false);
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 10, 30, 7), nyc);
-  EXPECT_EQ(max, t);
-
-  // One second later should push us to infinity.
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 5, 2, 30, 8), syd);
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 10, 30, 8), nyc);
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-
-  // And we should stick there.
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 5, 2, 30, 9), syd);
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-  t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 10, 30, 9), nyc);
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-
-  // All the way up to a saturated date/time, without overflow.
-  t = absl::FromCivil(absl::CivilSecond::max(), syd);
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-  t = absl::FromCivil(absl::CivilSecond::max(), nyc);
-  EXPECT_EQ(absl::InfiniteFuture(), t);
-}
-
-TEST(Time, FromCivilAlignment) {
-  const absl::TimeZone utc = absl::UTCTimeZone();
-  const absl::CivilSecond cs(2015, 2, 3, 4, 5, 6);
-  absl::Time t = absl::FromCivil(cs, utc);
-  EXPECT_EQ("2015-02-03T04:05:06+00:00", absl::FormatTime(t, utc));
-  t = absl::FromCivil(absl::CivilMinute(cs), utc);
-  EXPECT_EQ("2015-02-03T04:05:00+00:00", absl::FormatTime(t, utc));
-  t = absl::FromCivil(absl::CivilHour(cs), utc);
-  EXPECT_EQ("2015-02-03T04:00:00+00:00", absl::FormatTime(t, utc));
-  t = absl::FromCivil(absl::CivilDay(cs), utc);
-  EXPECT_EQ("2015-02-03T00:00:00+00:00", absl::FormatTime(t, utc));
-  t = absl::FromCivil(absl::CivilMonth(cs), utc);
-  EXPECT_EQ("2015-02-01T00:00:00+00:00", absl::FormatTime(t, utc));
-  t = absl::FromCivil(absl::CivilYear(cs), utc);
-  EXPECT_EQ("2015-01-01T00:00:00+00:00", absl::FormatTime(t, utc));
-}
-
-TEST(Time, LegacyDateTime) {
-  const absl::TimeZone utc = absl::UTCTimeZone();
-  const std::string ymdhms = "%Y-%m-%d %H:%M:%S";
-  const int kMax = std::numeric_limits<int>::max();
-  const int kMin = std::numeric_limits<int>::min();
-  absl::Time t;
-
-  t = absl::FromDateTime(std::numeric_limits<absl::civil_year_t>::max(), kMax,
-                         kMax, kMax, kMax, kMax, utc);
-  EXPECT_EQ("infinite-future",
-            absl::FormatTime(ymdhms, t, utc));  // no overflow
-  t = absl::FromDateTime(std::numeric_limits<absl::civil_year_t>::min(), kMin,
-                         kMin, kMin, kMin, kMin, utc);
-  EXPECT_EQ("infinite-past", absl::FormatTime(ymdhms, t, utc));  // no overflow
-
-  // Check normalization.
-  EXPECT_TRUE(absl::ConvertDateTime(2013, 10, 32, 8, 30, 0, utc).normalized);
-  t = absl::FromDateTime(2015, 1, 1, 0, 0, 60, utc);
-  EXPECT_EQ("2015-01-01 00:01:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, 1, 1, 0, 60, 0, utc);
-  EXPECT_EQ("2015-01-01 01:00:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, 1, 1, 24, 0, 0, utc);
-  EXPECT_EQ("2015-01-02 00:00:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, 1, 32, 0, 0, 0, utc);
-  EXPECT_EQ("2015-02-01 00:00:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, 13, 1, 0, 0, 0, utc);
-  EXPECT_EQ("2016-01-01 00:00:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, 13, 32, 60, 60, 60, utc);
-  EXPECT_EQ("2016-02-03 13:01:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, 1, 1, 0, 0, -1, utc);
-  EXPECT_EQ("2014-12-31 23:59:59", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, 1, 1, 0, -1, 0, utc);
-  EXPECT_EQ("2014-12-31 23:59:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, 1, 1, -1, 0, 0, utc);
-  EXPECT_EQ("2014-12-31 23:00:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, 1, -1, 0, 0, 0, utc);
-  EXPECT_EQ("2014-12-30 00:00:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, -1, 1, 0, 0, 0, utc);
-  EXPECT_EQ("2014-11-01 00:00:00", absl::FormatTime(ymdhms, t, utc));
-  t = absl::FromDateTime(2015, -1, -1, -1, -1, -1, utc);
-  EXPECT_EQ("2014-10-29 22:58:59", absl::FormatTime(ymdhms, t, utc));
-}
-
-TEST(Time, NextTransitionUTC) {
-  const auto tz = absl::UTCTimeZone();
-  absl::TimeZone::CivilTransition trans;
-
-  auto t = absl::InfinitePast();
-  EXPECT_FALSE(tz.NextTransition(t, &trans));
-
-  t = absl::InfiniteFuture();
-  EXPECT_FALSE(tz.NextTransition(t, &trans));
-}
-
-TEST(Time, PrevTransitionUTC) {
-  const auto tz = absl::UTCTimeZone();
-  absl::TimeZone::CivilTransition trans;
-
-  auto t = absl::InfiniteFuture();
-  EXPECT_FALSE(tz.PrevTransition(t, &trans));
-
-  t = absl::InfinitePast();
-  EXPECT_FALSE(tz.PrevTransition(t, &trans));
-}
-
-TEST(Time, NextTransitionNYC) {
-  const auto tz = absl::time_internal::LoadTimeZone("America/New_York");
-  absl::TimeZone::CivilTransition trans;
-
-  auto t = absl::FromCivil(absl::CivilSecond(2018, 6, 30, 0, 0, 0), tz);
-  EXPECT_TRUE(tz.NextTransition(t, &trans));
-  EXPECT_EQ(absl::CivilSecond(2018, 11, 4, 2, 0, 0), trans.from);
-  EXPECT_EQ(absl::CivilSecond(2018, 11, 4, 1, 0, 0), trans.to);
-
-  t = absl::InfiniteFuture();
-  EXPECT_FALSE(tz.NextTransition(t, &trans));
-
-  t = absl::InfinitePast();
-  EXPECT_TRUE(tz.NextTransition(t, &trans));
-  if (trans.from == absl::CivilSecond(1918, 03, 31, 2, 0, 0)) {
-    // It looks like the tzdata is only 32 bit (probably macOS),
-    // which bottoms out at 1901-12-13T20:45:52+00:00.
-    EXPECT_EQ(absl::CivilSecond(1918, 3, 31, 3, 0, 0), trans.to);
-  } else {
-    EXPECT_EQ(absl::CivilSecond(1883, 11, 18, 12, 3, 58), trans.from);
-    EXPECT_EQ(absl::CivilSecond(1883, 11, 18, 12, 0, 0), trans.to);
-  }
-}
-
-TEST(Time, PrevTransitionNYC) {
-  const auto tz = absl::time_internal::LoadTimeZone("America/New_York");
-  absl::TimeZone::CivilTransition trans;
-
-  auto t = absl::FromCivil(absl::CivilSecond(2018, 6, 30, 0, 0, 0), tz);
-  EXPECT_TRUE(tz.PrevTransition(t, &trans));
-  EXPECT_EQ(absl::CivilSecond(2018, 3, 11, 2, 0, 0), trans.from);
-  EXPECT_EQ(absl::CivilSecond(2018, 3, 11, 3, 0, 0), trans.to);
-
-  t = absl::InfinitePast();
-  EXPECT_FALSE(tz.PrevTransition(t, &trans));
-
-  t = absl::InfiniteFuture();
-  EXPECT_TRUE(tz.PrevTransition(t, &trans));
-  // We have a transition but we don't know which one.
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/time/time_zone_test.cc b/third_party/abseil_cpp/absl/time/time_zone_test.cc
deleted file mode 100644
index 229fcfccb0..0000000000
--- a/third_party/abseil_cpp/absl/time/time_zone_test.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
-#include "gtest/gtest.h"
-#include "absl/time/internal/test_util.h"
-#include "absl/time/time.h"
-
-namespace cctz = absl::time_internal::cctz;
-
-namespace {
-
-TEST(TimeZone, ValueSemantics) {
-  absl::TimeZone tz;
-  absl::TimeZone tz2 = tz;  // Copy-construct
-  EXPECT_EQ(tz, tz2);
-  tz2 = tz;  // Copy-assign
-  EXPECT_EQ(tz, tz2);
-}
-
-TEST(TimeZone, Equality) {
-  absl::TimeZone a, b;
-  EXPECT_EQ(a, b);
-  EXPECT_EQ(a.name(), b.name());
-
-  absl::TimeZone implicit_utc;
-  absl::TimeZone explicit_utc = absl::UTCTimeZone();
-  EXPECT_EQ(implicit_utc, explicit_utc);
-  EXPECT_EQ(implicit_utc.name(), explicit_utc.name());
-
-  absl::TimeZone la = absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  absl::TimeZone nyc = absl::time_internal::LoadTimeZone("America/New_York");
-  EXPECT_NE(la, nyc);
-}
-
-TEST(TimeZone, CCTZConversion) {
-  const cctz::time_zone cz = cctz::utc_time_zone();
-  const absl::TimeZone tz(cz);
-  EXPECT_EQ(cz, cctz::time_zone(tz));
-}
-
-TEST(TimeZone, DefaultTimeZones) {
-  absl::TimeZone tz;
-  EXPECT_EQ("UTC", absl::TimeZone().name());
-  EXPECT_EQ("UTC", absl::UTCTimeZone().name());
-}
-
-TEST(TimeZone, FixedTimeZone) {
-  const absl::TimeZone tz = absl::FixedTimeZone(123);
-  const cctz::time_zone cz = cctz::fixed_time_zone(cctz::seconds(123));
-  EXPECT_EQ(tz, absl::TimeZone(cz));
-}
-
-TEST(TimeZone, LocalTimeZone) {
-  const absl::TimeZone local_tz = absl::LocalTimeZone();
-  absl::TimeZone tz = absl::time_internal::LoadTimeZone("localtime");
-  EXPECT_EQ(tz, local_tz);
-}
-
-TEST(TimeZone, NamedTimeZones) {
-  absl::TimeZone nyc = absl::time_internal::LoadTimeZone("America/New_York");
-  EXPECT_EQ("America/New_York", nyc.name());
-  absl::TimeZone syd = absl::time_internal::LoadTimeZone("Australia/Sydney");
-  EXPECT_EQ("Australia/Sydney", syd.name());
-  absl::TimeZone fixed = absl::FixedTimeZone((((3 * 60) + 25) * 60) + 45);
-  EXPECT_EQ("Fixed/UTC+03:25:45", fixed.name());
-}
-
-TEST(TimeZone, Failures) {
-  absl::TimeZone tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  EXPECT_FALSE(LoadTimeZone("Invalid/TimeZone", &tz));
-  EXPECT_EQ(absl::UTCTimeZone(), tz);  // guaranteed fallback to UTC
-
-  // Ensures that the load still fails on a subsequent attempt.
-  tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  EXPECT_FALSE(LoadTimeZone("Invalid/TimeZone", &tz));
-  EXPECT_EQ(absl::UTCTimeZone(), tz);  // guaranteed fallback to UTC
-
-  // Loading an empty string timezone should fail.
-  tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
-  EXPECT_FALSE(LoadTimeZone("", &tz));
-  EXPECT_EQ(absl::UTCTimeZone(), tz);  // guaranteed fallback to UTC
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/types/BUILD.bazel b/third_party/abseil_cpp/absl/types/BUILD.bazel
deleted file mode 100644
index 83be9360ff..0000000000
--- a/third_party/abseil_cpp/absl/types/BUILD.bazel
+++ /dev/null
@@ -1,336 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "any",
-    hdrs = ["any.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":bad_any_cast",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:fast_type_id",
-        "//absl/meta:type_traits",
-        "//absl/utility",
-    ],
-)
-
-cc_library(
-    name = "bad_any_cast",
-    hdrs = ["bad_any_cast.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":bad_any_cast_impl",
-        "//absl/base:config",
-    ],
-)
-
-cc_library(
-    name = "bad_any_cast_impl",
-    srcs = [
-        "bad_any_cast.cc",
-        "bad_any_cast.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
-    deps = [
-        "//absl/base:config",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_test(
-    name = "any_test",
-    size = "small",
-    srcs = [
-        "any_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":any",
-        "//absl/base:config",
-        "//absl/base:exception_testing",
-        "//absl/base:raw_logging_internal",
-        "//absl/container:test_instance_tracker",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "any_exception_safety_test",
-    srcs = ["any_exception_safety_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":any",
-        "//absl/base:config",
-        "//absl/base:exception_safety_testing",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "span",
-    srcs = [
-        "internal/span.h",
-    ],
-    hdrs = [
-        "span.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/algorithm",
-        "//absl/base:core_headers",
-        "//absl/base:throw_delegate",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_test(
-    name = "span_test",
-    size = "small",
-    srcs = ["span_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":span",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/base:exception_testing",
-        "//absl/container:fixed_array",
-        "//absl/container:inlined_vector",
-        "//absl/hash:hash_testing",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "optional",
-    srcs = ["internal/optional.h"],
-    hdrs = ["optional.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":bad_optional_access",
-        "//absl/base:base_internal",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/utility",
-    ],
-)
-
-cc_library(
-    name = "bad_optional_access",
-    srcs = ["bad_optional_access.cc"],
-    hdrs = ["bad_optional_access.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_library(
-    name = "bad_variant_access",
-    srcs = ["bad_variant_access.cc"],
-    hdrs = ["bad_variant_access.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:config",
-        "//absl/base:raw_logging_internal",
-    ],
-)
-
-cc_test(
-    name = "optional_test",
-    size = "small",
-    srcs = [
-        "optional_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":optional",
-        "//absl/base:config",
-        "//absl/base:raw_logging_internal",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "optional_exception_safety_test",
-    srcs = [
-        "optional_exception_safety_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":optional",
-        "//absl/base:config",
-        "//absl/base:exception_safety_testing",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "conformance_testing",
-    testonly = 1,
-    hdrs = [
-        "internal/conformance_aliases.h",
-        "internal/conformance_archetype.h",
-        "internal/conformance_profile.h",
-        "internal/conformance_testing.h",
-        "internal/conformance_testing_helpers.h",
-        "internal/parentheses.h",
-        "internal/transform_args.h",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/algorithm:container",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "//absl/utility",
-        "@com_google_googletest//:gtest",
-    ],
-)
-
-cc_test(
-    name = "conformance_testing_test",
-    size = "small",
-    srcs = [
-        "internal/conformance_testing_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":conformance_testing",
-        "//absl/meta:type_traits",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "variant",
-    srcs = ["internal/variant.h"],
-    hdrs = ["variant.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":bad_variant_access",
-        "//absl/base:base_internal",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/meta:type_traits",
-        "//absl/utility",
-    ],
-)
-
-cc_test(
-    name = "variant_test",
-    size = "small",
-    srcs = ["variant_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":variant",
-        "//absl/base:config",
-        "//absl/base:core_headers",
-        "//absl/memory",
-        "//absl/meta:type_traits",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_test(
-    name = "variant_benchmark",
-    srcs = [
-        "variant_benchmark.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    tags = ["benchmark"],
-    deps = [
-        ":variant",
-        "//absl/utility",
-        "@com_github_google_benchmark//:benchmark_main",
-    ],
-)
-
-cc_test(
-    name = "variant_exception_safety_test",
-    size = "small",
-    srcs = [
-        "variant_exception_safety_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":variant",
-        "//absl/base:config",
-        "//absl/base:exception_safety_testing",
-        "//absl/memory",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
-
-cc_library(
-    name = "compare",
-    hdrs = ["compare.h"],
-    copts = ABSL_DEFAULT_COPTS,
-    deps = [
-        "//absl/base:core_headers",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_test(
-    name = "compare_test",
-    size = "small",
-    srcs = [
-        "compare_test.cc",
-    ],
-    copts = ABSL_TEST_COPTS,
-    deps = [
-        ":compare",
-        "//absl/base",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/types/CMakeLists.txt b/third_party/abseil_cpp/absl/types/CMakeLists.txt
deleted file mode 100644
index 3f99ad8a4b..0000000000
--- a/third_party/abseil_cpp/absl/types/CMakeLists.txt
+++ /dev/null
@@ -1,373 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-absl_cc_library(
-  NAME
-    any
-  HDRS
-    "any.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::bad_any_cast
-    absl::config
-    absl::core_headers
-    absl::fast_type_id
-    absl::type_traits
-    absl::utility
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    bad_any_cast
-  HDRS
-   "bad_any_cast.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::bad_any_cast_impl
-    absl::config
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    bad_any_cast_impl
-  SRCS
-   "bad_any_cast.h"
-   "bad_any_cast.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::raw_logging_internal
-)
-
-absl_cc_test(
-  NAME
-    any_test
-  SRCS
-    "any_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::any
-    absl::config
-    absl::exception_testing
-    absl::raw_logging_internal
-    absl::test_instance_tracker
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    any_test_noexceptions
-  SRCS
-    "any_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::any
-    absl::config
-    absl::exception_testing
-    absl::raw_logging_internal
-    absl::test_instance_tracker
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    any_exception_safety_test
-  SRCS
-    "any_exception_safety_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::any
-    absl::config
-    absl::exception_safety_testing
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    span
-  HDRS
-    "span.h"
-  SRCS
-    "internal/span.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::algorithm
-    absl::core_headers
-    absl::throw_delegate
-    absl::type_traits
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    span_test
-  SRCS
-    "span_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::span
-    absl::base
-    absl::config
-    absl::core_headers
-    absl::exception_testing
-    absl::fixed_array
-    absl::inlined_vector
-    absl::hash_testing
-    absl::strings
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    span_test_noexceptions
-  SRCS
-    "span_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::span
-    absl::base
-    absl::config
-    absl::core_headers
-    absl::exception_testing
-    absl::fixed_array
-    absl::inlined_vector
-    absl::hash_testing
-    absl::strings
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    optional
-  HDRS
-    "optional.h"
-  SRCS
-    "internal/optional.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::bad_optional_access
-    absl::base_internal
-    absl::config
-    absl::core_headers
-    absl::memory
-    absl::type_traits
-    absl::utility
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    bad_optional_access
-  HDRS
-    "bad_optional_access.h"
-  SRCS
-    "bad_optional_access.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::raw_logging_internal
-  PUBLIC
-)
-
-absl_cc_library(
-  NAME
-    bad_variant_access
-  HDRS
-    "bad_variant_access.h"
-  SRCS
-    "bad_variant_access.cc"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::config
-    absl::raw_logging_internal
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    optional_test
-  SRCS
-    "optional_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::optional
-    absl::config
-    absl::raw_logging_internal
-    absl::strings
-    absl::type_traits
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    optional_exception_safety_test
-  SRCS
-    "optional_exception_safety_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::optional
-    absl::config
-    absl::exception_safety_testing
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    conformance_testing
-  HDRS
-    "internal/conformance_aliases.h"
-    "internal/conformance_archetype.h"
-    "internal/conformance_profile.h"
-    "internal/conformance_testing.h"
-    "internal/conformance_testing_helpers.h"
-    "internal/parentheses.h"
-    "internal/transform_args.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::algorithm
-    absl::debugging
-    absl::type_traits
-    absl::strings
-    absl::utility
-    gmock_main
-  TESTONLY
-)
-
-absl_cc_test(
-  NAME
-    conformance_testing_test
-  SRCS
-    "internal/conformance_testing_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-    ${ABSL_EXCEPTIONS_FLAG}
-  LINKOPTS
-    ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
-  DEPS
-    absl::conformance_testing
-    absl::type_traits
-    gmock_main
-)
-
-absl_cc_test(
-  NAME
-    conformance_testing_test_no_exceptions
-  SRCS
-    "internal/conformance_testing_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::conformance_testing
-    absl::type_traits
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    variant
-  HDRS
-    "variant.h"
-  SRCS
-    "internal/variant.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::bad_variant_access
-    absl::base_internal
-    absl::config
-    absl::core_headers
-    absl::type_traits
-    absl::utility
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    variant_test
-  SRCS
-    "variant_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::variant
-    absl::config
-    absl::core_headers
-    absl::memory
-    absl::type_traits
-    absl::strings
-    gmock_main
-)
-
-absl_cc_library(
-  NAME
-    compare
-  HDRS
-    "compare.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::core_headers
-    absl::type_traits
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    compare_test
-  SRCS
-    "compare_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::base
-    absl::compare
-    gmock_main
-)
-
-# TODO(cohenjon,zhangxy) Figure out why this test is failing on gcc 4.8
-if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
-else()
-absl_cc_test(
-  NAME
-    variant_exception_safety_test
-  SRCS
-    "variant_exception_safety_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::variant
-    absl::config
-    absl::exception_safety_testing
-    absl::memory
-    gmock_main
-)
-endif()
diff --git a/third_party/abseil_cpp/absl/types/any.h b/third_party/abseil_cpp/absl/types/any.h
deleted file mode 100644
index fc5a07469f..0000000000
--- a/third_party/abseil_cpp/absl/types/any.h
+++ /dev/null
@@ -1,528 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// any.h
-// -----------------------------------------------------------------------------
-//
-// This header file define the `absl::any` type for holding a type-safe value
-// of any type. The 'absl::any` type is useful for providing a way to hold
-// something that is, as yet, unspecified. Such unspecified types
-// traditionally are passed between API boundaries until they are later cast to
-// their "destination" types. To cast to such a destination type, use
-// `absl::any_cast()`. Note that when casting an `absl::any`, you must cast it
-// to an explicit type; implicit conversions will throw.
-//
-// Example:
-//
-//   auto a = absl::any(65);
-//   absl::any_cast<int>(a);         // 65
-//   absl::any_cast<char>(a);        // throws absl::bad_any_cast
-//   absl::any_cast<std::string>(a); // throws absl::bad_any_cast
-//
-// `absl::any` is a C++11 compatible version of the C++17 `std::any` abstraction
-// and is designed to be a drop-in replacement for code compliant with C++17.
-//
-// Traditionally, the behavior of casting to a temporary unspecified type has
-// been accomplished with the `void *` paradigm, where the pointer was to some
-// other unspecified type. `absl::any` provides an "owning" version of `void *`
-// that avoids issues of pointer management.
-//
-// Note: just as in the case of `void *`, use of `absl::any` (and its C++17
-// version `std::any`) is a code smell indicating that your API might not be
-// constructed correctly. We have seen that most uses of `any` are unwarranted,
-// and `absl::any`, like `std::any`, is difficult to use properly. Before using
-// this abstraction, make sure that you should not instead be rewriting your
-// code to be more specific.
-//
-// Abseil has also released an `absl::variant` type (a C++11 compatible version
-// of the C++17 `std::variant`), which is generally preferred for use over
-// `absl::any`.
-#ifndef ABSL_TYPES_ANY_H_
-#define ABSL_TYPES_ANY_H_
-
-#include "absl/base/config.h"
-#include "absl/utility/utility.h"
-
-#ifdef ABSL_USES_STD_ANY
-
-#include <any>  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-using std::any;
-using std::any_cast;
-using std::bad_any_cast;
-using std::make_any;
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // ABSL_USES_STD_ANY
-
-#include <algorithm>
-#include <cstddef>
-#include <initializer_list>
-#include <memory>
-#include <stdexcept>
-#include <type_traits>
-#include <typeinfo>
-#include <utility>
-
-#include "absl/base/internal/fast_type_id.h"
-#include "absl/base/macros.h"
-#include "absl/meta/type_traits.h"
-#include "absl/types/bad_any_cast.h"
-
-// NOTE: This macro is an implementation detail that is undefined at the bottom
-// of the file. It is not intended for expansion directly from user code.
-#ifdef ABSL_ANY_DETAIL_HAS_RTTI
-#error ABSL_ANY_DETAIL_HAS_RTTI cannot be directly set
-#elif !defined(__GNUC__) || defined(__GXX_RTTI)
-#define ABSL_ANY_DETAIL_HAS_RTTI 1
-#endif  // !defined(__GNUC__) || defined(__GXX_RTTI)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-class any;
-
-// swap()
-//
-// Swaps two `absl::any` values. Equivalent to `x.swap(y) where `x` and `y` are
-// `absl::any` types.
-void swap(any& x, any& y) noexcept;
-
-// make_any()
-//
-// Constructs an `absl::any` of type `T` with the given arguments.
-template <typename T, typename... Args>
-any make_any(Args&&... args);
-
-// Overload of `absl::make_any()` for constructing an `absl::any` type from an
-// initializer list.
-template <typename T, typename U, typename... Args>
-any make_any(std::initializer_list<U> il, Args&&... args);
-
-// any_cast()
-//
-// Statically casts the value of a `const absl::any` type to the given type.
-// This function will throw `absl::bad_any_cast` if the stored value type of the
-// `absl::any` does not match the cast.
-//
-// `any_cast()` can also be used to get a reference to the internal storage iff
-// a reference type is passed as its `ValueType`:
-//
-// Example:
-//
-//   absl::any my_any = std::vector<int>();
-//   absl::any_cast<std::vector<int>&>(my_any).push_back(42);
-template <typename ValueType>
-ValueType any_cast(const any& operand);
-
-// Overload of `any_cast()` to statically cast the value of a non-const
-// `absl::any` type to the given type. This function will throw
-// `absl::bad_any_cast` if the stored value type of the `absl::any` does not
-// match the cast.
-template <typename ValueType>
-ValueType any_cast(any& operand);  // NOLINT(runtime/references)
-
-// Overload of `any_cast()` to statically cast the rvalue of an `absl::any`
-// type. This function will throw `absl::bad_any_cast` if the stored value type
-// of the `absl::any` does not match the cast.
-template <typename ValueType>
-ValueType any_cast(any&& operand);
-
-// Overload of `any_cast()` to statically cast the value of a const pointer
-// `absl::any` type to the given pointer type, or `nullptr` if the stored value
-// type of the `absl::any` does not match the cast.
-template <typename ValueType>
-const ValueType* any_cast(const any* operand) noexcept;
-
-// Overload of `any_cast()` to statically cast the value of a pointer
-// `absl::any` type to the given pointer type, or `nullptr` if the stored value
-// type of the `absl::any` does not match the cast.
-template <typename ValueType>
-ValueType* any_cast(any* operand) noexcept;
-
-// -----------------------------------------------------------------------------
-// absl::any
-// -----------------------------------------------------------------------------
-//
-// An `absl::any` object provides the facility to either store an instance of a
-// type, known as the "contained object", or no value. An `absl::any` is used to
-// store values of types that are unknown at compile time. The `absl::any`
-// object, when containing a value, must contain a value type; storing a
-// reference type is neither desired nor supported.
-//
-// An `absl::any` can only store a type that is copy-constructible; move-only
-// types are not allowed within an `any` object.
-//
-// Example:
-//
-//   auto a = absl::any(65);                 // Literal, copyable
-//   auto b = absl::any(std::vector<int>()); // Default-initialized, copyable
-//   std::unique_ptr<Foo> my_foo;
-//   auto c = absl::any(std::move(my_foo));  // Error, not copy-constructible
-//
-// Note that `absl::any` makes use of decayed types (`absl::decay_t` in this
-// context) to remove const-volatile qualifiers (known as "cv qualifiers"),
-// decay functions to function pointers, etc. We essentially "decay" a given
-// type into its essential type.
-//
-// `absl::any` makes use of decayed types when determining the basic type `T` of
-// the value to store in the any's contained object. In the documentation below,
-// we explicitly denote this by using the phrase "a decayed type of `T`".
-//
-// Example:
-//
-//   const int a = 4;
-//   absl::any foo(a);  // Decay ensures we store an "int", not a "const int&".
-//
-//   void my_function() {}
-//   absl::any bar(my_function);  // Decay ensures we store a function pointer.
-//
-// `absl::any` is a C++11 compatible version of the C++17 `std::any` abstraction
-// and is designed to be a drop-in replacement for code compliant with C++17.
-class any {
- private:
-  template <typename T>
-  struct IsInPlaceType;
-
- public:
-  // Constructors
-
-  // Constructs an empty `absl::any` object (`any::has_value()` will return
-  // `false`).
-  constexpr any() noexcept;
-
-  // Copy constructs an `absl::any` object with a "contained object" of the
-  // passed type of `other` (or an empty `absl::any` if `other.has_value()` is
-  // `false`.
-  any(const any& other)
-      : obj_(other.has_value() ? other.obj_->Clone()
-                               : std::unique_ptr<ObjInterface>()) {}
-
-  // Move constructs an `absl::any` object with a "contained object" of the
-  // passed type of `other` (or an empty `absl::any` if `other.has_value()` is
-  // `false`).
-  any(any&& other) noexcept = default;
-
-  // Constructs an `absl::any` object with a "contained object" of the decayed
-  // type of `T`, which is initialized via `std::forward<T>(value)`.
-  //
-  // This constructor will not participate in overload resolution if the
-  // decayed type of `T` is not copy-constructible.
-  template <
-      typename T, typename VT = absl::decay_t<T>,
-      absl::enable_if_t<!absl::disjunction<
-          std::is_same<any, VT>, IsInPlaceType<VT>,
-          absl::negation<std::is_copy_constructible<VT> > >::value>* = nullptr>
-  any(T&& value) : obj_(new Obj<VT>(in_place, std::forward<T>(value))) {}
-
-  // Constructs an `absl::any` object with a "contained object" of the decayed
-  // type of `T`, which is initialized via `std::forward<T>(value)`.
-  template <typename T, typename... Args, typename VT = absl::decay_t<T>,
-            absl::enable_if_t<absl::conjunction<
-                std::is_copy_constructible<VT>,
-                std::is_constructible<VT, Args...>>::value>* = nullptr>
-  explicit any(in_place_type_t<T> /*tag*/, Args&&... args)
-      : obj_(new Obj<VT>(in_place, std::forward<Args>(args)...)) {}
-
-  // Constructs an `absl::any` object with a "contained object" of the passed
-  // type `VT` as a decayed type of `T`. `VT` is initialized as if
-  // direct-non-list-initializing an object of type `VT` with the arguments
-  // `initializer_list, std::forward<Args>(args)...`.
-  template <
-      typename T, typename U, typename... Args, typename VT = absl::decay_t<T>,
-      absl::enable_if_t<
-          absl::conjunction<std::is_copy_constructible<VT>,
-                            std::is_constructible<VT, std::initializer_list<U>&,
-                                                  Args...>>::value>* = nullptr>
-  explicit any(in_place_type_t<T> /*tag*/, std::initializer_list<U> ilist,
-               Args&&... args)
-      : obj_(new Obj<VT>(in_place, ilist, std::forward<Args>(args)...)) {}
-
-  // Assignment operators
-
-  // Copy assigns an `absl::any` object with a "contained object" of the
-  // passed type.
-  any& operator=(const any& rhs) {
-    any(rhs).swap(*this);
-    return *this;
-  }
-
-  // Move assigns an `absl::any` object with a "contained object" of the
-  // passed type. `rhs` is left in a valid but otherwise unspecified state.
-  any& operator=(any&& rhs) noexcept {
-    any(std::move(rhs)).swap(*this);
-    return *this;
-  }
-
-  // Assigns an `absl::any` object with a "contained object" of the passed type.
-  template <typename T, typename VT = absl::decay_t<T>,
-            absl::enable_if_t<absl::conjunction<
-                absl::negation<std::is_same<VT, any>>,
-                std::is_copy_constructible<VT>>::value>* = nullptr>
-  any& operator=(T&& rhs) {
-    any tmp(in_place_type_t<VT>(), std::forward<T>(rhs));
-    tmp.swap(*this);
-    return *this;
-  }
-
-  // Modifiers
-
-  // any::emplace()
-  //
-  // Emplaces a value within an `absl::any` object by calling `any::reset()`,
-  // initializing the contained value as if direct-non-list-initializing an
-  // object of type `VT` with the arguments `std::forward<Args>(args)...`, and
-  // returning a reference to the new contained value.
-  //
-  // Note: If an exception is thrown during the call to `VT`'s constructor,
-  // `*this` does not contain a value, and any previously contained value has
-  // been destroyed.
-  template <
-      typename T, typename... Args, typename VT = absl::decay_t<T>,
-      absl::enable_if_t<std::is_copy_constructible<VT>::value &&
-                        std::is_constructible<VT, Args...>::value>* = nullptr>
-  VT& emplace(Args&&... args) {
-    reset();  // NOTE: reset() is required here even in the world of exceptions.
-    Obj<VT>* const object_ptr =
-        new Obj<VT>(in_place, std::forward<Args>(args)...);
-    obj_ = std::unique_ptr<ObjInterface>(object_ptr);
-    return object_ptr->value;
-  }
-
-  // Overload of `any::emplace()` to emplace a value within an `absl::any`
-  // object by calling `any::reset()`, initializing the contained value as if
-  // direct-non-list-initializing an object of type `VT` with the arguments
-  // `initializer_list, std::forward<Args>(args)...`, and returning a reference
-  // to the new contained value.
-  //
-  // Note: If an exception is thrown during the call to `VT`'s constructor,
-  // `*this` does not contain a value, and any previously contained value has
-  // been destroyed. The function shall not participate in overload resolution
-  // unless `is_copy_constructible_v<VT>` is `true` and
-  // `is_constructible_v<VT, initializer_list<U>&, Args...>` is `true`.
-  template <
-      typename T, typename U, typename... Args, typename VT = absl::decay_t<T>,
-      absl::enable_if_t<std::is_copy_constructible<VT>::value &&
-                        std::is_constructible<VT, std::initializer_list<U>&,
-                                              Args...>::value>* = nullptr>
-  VT& emplace(std::initializer_list<U> ilist, Args&&... args) {
-    reset();  // NOTE: reset() is required here even in the world of exceptions.
-    Obj<VT>* const object_ptr =
-        new Obj<VT>(in_place, ilist, std::forward<Args>(args)...);
-    obj_ = std::unique_ptr<ObjInterface>(object_ptr);
-    return object_ptr->value;
-  }
-
-  // any::reset()
-  //
-  // Resets the state of the `absl::any` object, destroying the contained object
-  // if present.
-  void reset() noexcept { obj_ = nullptr; }
-
-  // any::swap()
-  //
-  // Swaps the passed value and the value of this `absl::any` object.
-  void swap(any& other) noexcept { obj_.swap(other.obj_); }
-
-  // Observers
-
-  // any::has_value()
-  //
-  // Returns `true` if the `any` object has a contained value, otherwise
-  // returns `false`.
-  bool has_value() const noexcept { return obj_ != nullptr; }
-
-#if ABSL_ANY_DETAIL_HAS_RTTI
-  // Returns: typeid(T) if *this has a contained object of type T, otherwise
-  // typeid(void).
-  const std::type_info& type() const noexcept {
-    if (has_value()) {
-      return obj_->Type();
-    }
-
-    return typeid(void);
-  }
-#endif  // ABSL_ANY_DETAIL_HAS_RTTI
-
- private:
-  // Tagged type-erased abstraction for holding a cloneable object.
-  class ObjInterface {
-   public:
-    virtual ~ObjInterface() = default;
-    virtual std::unique_ptr<ObjInterface> Clone() const = 0;
-    virtual const void* ObjTypeId() const noexcept = 0;
-#if ABSL_ANY_DETAIL_HAS_RTTI
-    virtual const std::type_info& Type() const noexcept = 0;
-#endif  // ABSL_ANY_DETAIL_HAS_RTTI
-  };
-
-  // Hold a value of some queryable type, with an ability to Clone it.
-  template <typename T>
-  class Obj : public ObjInterface {
-   public:
-    template <typename... Args>
-    explicit Obj(in_place_t /*tag*/, Args&&... args)
-        : value(std::forward<Args>(args)...) {}
-
-    std::unique_ptr<ObjInterface> Clone() const final {
-      return std::unique_ptr<ObjInterface>(new Obj(in_place, value));
-    }
-
-    const void* ObjTypeId() const noexcept final { return IdForType<T>(); }
-
-#if ABSL_ANY_DETAIL_HAS_RTTI
-    const std::type_info& Type() const noexcept final { return typeid(T); }
-#endif  // ABSL_ANY_DETAIL_HAS_RTTI
-
-    T value;
-  };
-
-  std::unique_ptr<ObjInterface> CloneObj() const {
-    if (!obj_) return nullptr;
-    return obj_->Clone();
-  }
-
-  template <typename T>
-  constexpr static const void* IdForType() {
-    // Note: This type dance is to make the behavior consistent with typeid.
-    using NormalizedType =
-        typename std::remove_cv<typename std::remove_reference<T>::type>::type;
-
-    return base_internal::FastTypeId<NormalizedType>();
-  }
-
-  const void* GetObjTypeId() const {
-    return obj_ ? obj_->ObjTypeId() : base_internal::FastTypeId<void>();
-  }
-
-  // `absl::any` nonmember functions //
-
-  // Description at the declaration site (top of file).
-  template <typename ValueType>
-  friend ValueType any_cast(const any& operand);
-
-  // Description at the declaration site (top of file).
-  template <typename ValueType>
-  friend ValueType any_cast(any& operand);  // NOLINT(runtime/references)
-
-  // Description at the declaration site (top of file).
-  template <typename T>
-  friend const T* any_cast(const any* operand) noexcept;
-
-  // Description at the declaration site (top of file).
-  template <typename T>
-  friend T* any_cast(any* operand) noexcept;
-
-  std::unique_ptr<ObjInterface> obj_;
-};
-
-// -----------------------------------------------------------------------------
-// Implementation Details
-// -----------------------------------------------------------------------------
-
-constexpr any::any() noexcept = default;
-
-template <typename T>
-struct any::IsInPlaceType : std::false_type {};
-
-template <typename T>
-struct any::IsInPlaceType<in_place_type_t<T>> : std::true_type {};
-
-inline void swap(any& x, any& y) noexcept { x.swap(y); }
-
-// Description at the declaration site (top of file).
-template <typename T, typename... Args>
-any make_any(Args&&... args) {
-  return any(in_place_type_t<T>(), std::forward<Args>(args)...);
-}
-
-// Description at the declaration site (top of file).
-template <typename T, typename U, typename... Args>
-any make_any(std::initializer_list<U> il, Args&&... args) {
-  return any(in_place_type_t<T>(), il, std::forward<Args>(args)...);
-}
-
-// Description at the declaration site (top of file).
-template <typename ValueType>
-ValueType any_cast(const any& operand) {
-  using U = typename std::remove_cv<
-      typename std::remove_reference<ValueType>::type>::type;
-  static_assert(std::is_constructible<ValueType, const U&>::value,
-                "Invalid ValueType");
-  auto* const result = (any_cast<U>)(&operand);
-  if (result == nullptr) {
-    any_internal::ThrowBadAnyCast();
-  }
-  return static_cast<ValueType>(*result);
-}
-
-// Description at the declaration site (top of file).
-template <typename ValueType>
-ValueType any_cast(any& operand) {  // NOLINT(runtime/references)
-  using U = typename std::remove_cv<
-      typename std::remove_reference<ValueType>::type>::type;
-  static_assert(std::is_constructible<ValueType, U&>::value,
-                "Invalid ValueType");
-  auto* result = (any_cast<U>)(&operand);
-  if (result == nullptr) {
-    any_internal::ThrowBadAnyCast();
-  }
-  return static_cast<ValueType>(*result);
-}
-
-// Description at the declaration site (top of file).
-template <typename ValueType>
-ValueType any_cast(any&& operand) {
-  using U = typename std::remove_cv<
-      typename std::remove_reference<ValueType>::type>::type;
-  static_assert(std::is_constructible<ValueType, U>::value,
-                "Invalid ValueType");
-  return static_cast<ValueType>(std::move((any_cast<U&>)(operand)));
-}
-
-// Description at the declaration site (top of file).
-template <typename T>
-const T* any_cast(const any* operand) noexcept {
-  using U =
-      typename std::remove_cv<typename std::remove_reference<T>::type>::type;
-  return operand && operand->GetObjTypeId() == any::IdForType<U>()
-             ? std::addressof(
-                   static_cast<const any::Obj<U>*>(operand->obj_.get())->value)
-             : nullptr;
-}
-
-// Description at the declaration site (top of file).
-template <typename T>
-T* any_cast(any* operand) noexcept {
-  using U =
-      typename std::remove_cv<typename std::remove_reference<T>::type>::type;
-  return operand && operand->GetObjTypeId() == any::IdForType<U>()
-             ? std::addressof(
-                   static_cast<any::Obj<U>*>(operand->obj_.get())->value)
-             : nullptr;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#undef ABSL_ANY_DETAIL_HAS_RTTI
-
-#endif  // ABSL_USES_STD_ANY
-
-#endif  // ABSL_TYPES_ANY_H_
diff --git a/third_party/abseil_cpp/absl/types/any_exception_safety_test.cc b/third_party/abseil_cpp/absl/types/any_exception_safety_test.cc
deleted file mode 100644
index 31c1140135..0000000000
--- a/third_party/abseil_cpp/absl/types/any_exception_safety_test.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/any.h"
-
-#include "absl/base/config.h"
-
-// This test is a no-op when absl::any is an alias for std::any and when
-// exceptions are not enabled.
-#if !defined(ABSL_USES_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS)
-
-#include <typeinfo>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/exception_safety_testing.h"
-
-using Thrower = testing::ThrowingValue<>;
-using NoThrowMoveThrower =
-    testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
-using ThrowerList = std::initializer_list<Thrower>;
-using ThrowerVec = std::vector<Thrower>;
-using ThrowingAlloc = testing::ThrowingAllocator<Thrower>;
-using ThrowingThrowerVec = std::vector<Thrower, ThrowingAlloc>;
-
-namespace {
-
-testing::AssertionResult AnyInvariants(absl::any* a) {
-  using testing::AssertionFailure;
-  using testing::AssertionSuccess;
-
-  if (a->has_value()) {
-    if (a->type() == typeid(void)) {
-      return AssertionFailure()
-             << "A non-empty any should not have type `void`";
-    }
-  } else {
-    if (a->type() != typeid(void)) {
-      return AssertionFailure()
-             << "An empty any should have type void, but has type "
-             << a->type().name();
-    }
-  }
-
-  //  Make sure that reset() changes any to a valid state.
-  a->reset();
-  if (a->has_value()) {
-    return AssertionFailure() << "A reset `any` should be valueless";
-  }
-  if (a->type() != typeid(void)) {
-    return AssertionFailure() << "A reset `any` should have type() of `void`, "
-                                 "but instead has type "
-                              << a->type().name();
-  }
-  try {
-    auto unused = absl::any_cast<Thrower>(*a);
-    static_cast<void>(unused);
-    return AssertionFailure()
-           << "A reset `any` should not be able to be any_cast";
-  } catch (const absl::bad_any_cast&) {
-  } catch (...) {
-    return AssertionFailure()
-           << "Unexpected exception thrown from absl::any_cast";
-  }
-  return AssertionSuccess();
-}
-
-testing::AssertionResult AnyIsEmpty(absl::any* a) {
-  if (!a->has_value()) {
-    return testing::AssertionSuccess();
-  }
-  return testing::AssertionFailure()
-         << "a should be empty, but instead has value "
-         << absl::any_cast<Thrower>(*a).Get();
-}
-
-TEST(AnyExceptionSafety, Ctors) {
-  Thrower val(1);
-  testing::TestThrowingCtor<absl::any>(val);
-
-  Thrower copy(val);
-  testing::TestThrowingCtor<absl::any>(copy);
-
-  testing::TestThrowingCtor<absl::any>(absl::in_place_type_t<Thrower>(), 1);
-
-  testing::TestThrowingCtor<absl::any>(absl::in_place_type_t<ThrowerVec>(),
-                                       ThrowerList{val});
-
-  testing::TestThrowingCtor<absl::any,
-                            absl::in_place_type_t<ThrowingThrowerVec>,
-                            ThrowerList, ThrowingAlloc>(
-      absl::in_place_type_t<ThrowingThrowerVec>(), {val}, ThrowingAlloc());
-}
-
-TEST(AnyExceptionSafety, Assignment) {
-  auto original =
-      absl::any(absl::in_place_type_t<Thrower>(), 1, testing::nothrow_ctor);
-  auto any_is_strong = [original](absl::any* ap) {
-    return testing::AssertionResult(ap->has_value() &&
-                                    absl::any_cast<Thrower>(original) ==
-                                        absl::any_cast<Thrower>(*ap));
-  };
-  auto any_strong_tester = testing::MakeExceptionSafetyTester()
-                               .WithInitialValue(original)
-                               .WithContracts(AnyInvariants, any_is_strong);
-
-  Thrower val(2);
-  absl::any any_val(val);
-  NoThrowMoveThrower mv_val(2);
-
-  auto assign_any = [&any_val](absl::any* ap) { *ap = any_val; };
-  auto assign_val = [&val](absl::any* ap) { *ap = val; };
-  auto move = [&val](absl::any* ap) { *ap = std::move(val); };
-  auto move_movable = [&mv_val](absl::any* ap) { *ap = std::move(mv_val); };
-
-  EXPECT_TRUE(any_strong_tester.Test(assign_any));
-  EXPECT_TRUE(any_strong_tester.Test(assign_val));
-  EXPECT_TRUE(any_strong_tester.Test(move));
-  EXPECT_TRUE(any_strong_tester.Test(move_movable));
-
-  auto empty_any_is_strong = [](absl::any* ap) {
-    return testing::AssertionResult{!ap->has_value()};
-  };
-  auto strong_empty_any_tester =
-      testing::MakeExceptionSafetyTester()
-          .WithInitialValue(absl::any{})
-          .WithContracts(AnyInvariants, empty_any_is_strong);
-
-  EXPECT_TRUE(strong_empty_any_tester.Test(assign_any));
-  EXPECT_TRUE(strong_empty_any_tester.Test(assign_val));
-  EXPECT_TRUE(strong_empty_any_tester.Test(move));
-}
-
-TEST(AnyExceptionSafety, Emplace) {
-  auto initial_val =
-      absl::any{absl::in_place_type_t<Thrower>(), 1, testing::nothrow_ctor};
-  auto one_tester = testing::MakeExceptionSafetyTester()
-                        .WithInitialValue(initial_val)
-                        .WithContracts(AnyInvariants, AnyIsEmpty);
-
-  auto emp_thrower = [](absl::any* ap) { ap->emplace<Thrower>(2); };
-  auto emp_throwervec = [](absl::any* ap) {
-    std::initializer_list<Thrower> il{Thrower(2, testing::nothrow_ctor)};
-    ap->emplace<ThrowerVec>(il);
-  };
-  auto emp_movethrower = [](absl::any* ap) {
-    ap->emplace<NoThrowMoveThrower>(2);
-  };
-
-  EXPECT_TRUE(one_tester.Test(emp_thrower));
-  EXPECT_TRUE(one_tester.Test(emp_throwervec));
-  EXPECT_TRUE(one_tester.Test(emp_movethrower));
-
-  auto empty_tester = one_tester.WithInitialValue(absl::any{});
-
-  EXPECT_TRUE(empty_tester.Test(emp_thrower));
-  EXPECT_TRUE(empty_tester.Test(emp_throwervec));
-}
-
-}  // namespace
-
-#endif  // #if !defined(ABSL_USES_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS)
diff --git a/third_party/abseil_cpp/absl/types/any_test.cc b/third_party/abseil_cpp/absl/types/any_test.cc
deleted file mode 100644
index 70e4ba22b1..0000000000
--- a/third_party/abseil_cpp/absl/types/any_test.cc
+++ /dev/null
@@ -1,781 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/any.h"
-
-// This test is a no-op when absl::any is an alias for std::any.
-#if !defined(ABSL_USES_STD_ANY)
-
-#include <initializer_list>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/exception_testing.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/container/internal/test_instance_tracker.h"
-
-namespace {
-using absl::test_internal::CopyableOnlyInstance;
-using absl::test_internal::InstanceTracker;
-
-template <typename T>
-const T& AsConst(const T& t) {
-  return t;
-}
-
-struct MoveOnly {
-  MoveOnly() = default;
-  explicit MoveOnly(int value) : value(value) {}
-  MoveOnly(MoveOnly&&) = default;
-  MoveOnly& operator=(MoveOnly&&) = default;
-
-  int value = 0;
-};
-
-struct CopyOnly {
-  CopyOnly() = default;
-  explicit CopyOnly(int value) : value(value) {}
-  CopyOnly(CopyOnly&&) = delete;
-  CopyOnly& operator=(CopyOnly&&) = delete;
-  CopyOnly(const CopyOnly&) = default;
-  CopyOnly& operator=(const CopyOnly&) = default;
-
-  int value = 0;
-};
-
-struct MoveOnlyWithListConstructor {
-  MoveOnlyWithListConstructor() = default;
-  explicit MoveOnlyWithListConstructor(std::initializer_list<int> /*ilist*/,
-                                       int value)
-      : value(value) {}
-  MoveOnlyWithListConstructor(MoveOnlyWithListConstructor&&) = default;
-  MoveOnlyWithListConstructor& operator=(MoveOnlyWithListConstructor&&) =
-      default;
-
-  int value = 0;
-};
-
-struct IntMoveOnlyCopyOnly {
-  IntMoveOnlyCopyOnly(int value, MoveOnly /*move_only*/, CopyOnly /*copy_only*/)
-      : value(value) {}
-
-  int value;
-};
-
-struct ListMoveOnlyCopyOnly {
-  ListMoveOnlyCopyOnly(std::initializer_list<int> ilist, MoveOnly /*move_only*/,
-                       CopyOnly /*copy_only*/)
-      : values(ilist) {}
-
-  std::vector<int> values;
-};
-
-using FunctionType = void();
-void FunctionToEmplace() {}
-
-using ArrayType = int[2];
-using DecayedArray = absl::decay_t<ArrayType>;
-
-TEST(AnyTest, Noexcept) {
-  static_assert(std::is_nothrow_default_constructible<absl::any>(), "");
-  static_assert(std::is_nothrow_move_constructible<absl::any>(), "");
-  static_assert(std::is_nothrow_move_assignable<absl::any>(), "");
-  static_assert(noexcept(std::declval<absl::any&>().has_value()), "");
-  static_assert(noexcept(std::declval<absl::any&>().type()), "");
-  static_assert(noexcept(absl::any_cast<int>(std::declval<absl::any*>())), "");
-  static_assert(
-      noexcept(std::declval<absl::any&>().swap(std::declval<absl::any&>())),
-      "");
-
-  using std::swap;
-  static_assert(
-      noexcept(swap(std::declval<absl::any&>(), std::declval<absl::any&>())),
-      "");
-}
-
-TEST(AnyTest, HasValue) {
-  absl::any o;
-  EXPECT_FALSE(o.has_value());
-  o.emplace<int>();
-  EXPECT_TRUE(o.has_value());
-  o.reset();
-  EXPECT_FALSE(o.has_value());
-}
-
-TEST(AnyTest, Type) {
-  absl::any o;
-  EXPECT_EQ(typeid(void), o.type());
-  o.emplace<int>(5);
-  EXPECT_EQ(typeid(int), o.type());
-  o.emplace<float>(5.f);
-  EXPECT_EQ(typeid(float), o.type());
-  o.reset();
-  EXPECT_EQ(typeid(void), o.type());
-}
-
-TEST(AnyTest, EmptyPointerCast) {
-  // pointer-to-unqualified overload
-  {
-    absl::any o;
-    EXPECT_EQ(nullptr, absl::any_cast<int>(&o));
-    o.emplace<int>();
-    EXPECT_NE(nullptr, absl::any_cast<int>(&o));
-    o.reset();
-    EXPECT_EQ(nullptr, absl::any_cast<int>(&o));
-  }
-
-  // pointer-to-const overload
-  {
-    absl::any o;
-    EXPECT_EQ(nullptr, absl::any_cast<int>(&AsConst(o)));
-    o.emplace<int>();
-    EXPECT_NE(nullptr, absl::any_cast<int>(&AsConst(o)));
-    o.reset();
-    EXPECT_EQ(nullptr, absl::any_cast<int>(&AsConst(o)));
-  }
-}
-
-TEST(AnyTest, InPlaceConstruction) {
-  const CopyOnly copy_only{};
-  absl::any o(absl::in_place_type_t<IntMoveOnlyCopyOnly>(), 5, MoveOnly(),
-              copy_only);
-  IntMoveOnlyCopyOnly& v = absl::any_cast<IntMoveOnlyCopyOnly&>(o);
-  EXPECT_EQ(5, v.value);
-}
-
-TEST(AnyTest, InPlaceConstructionVariableTemplate) {
-  const CopyOnly copy_only{};
-  absl::any o(absl::in_place_type<IntMoveOnlyCopyOnly>, 5, MoveOnly(),
-              copy_only);
-  auto& v = absl::any_cast<IntMoveOnlyCopyOnly&>(o);
-  EXPECT_EQ(5, v.value);
-}
-
-TEST(AnyTest, InPlaceConstructionWithCV) {
-  const CopyOnly copy_only{};
-  absl::any o(absl::in_place_type_t<const volatile IntMoveOnlyCopyOnly>(), 5,
-              MoveOnly(), copy_only);
-  IntMoveOnlyCopyOnly& v = absl::any_cast<IntMoveOnlyCopyOnly&>(o);
-  EXPECT_EQ(5, v.value);
-}
-
-TEST(AnyTest, InPlaceConstructionWithCVVariableTemplate) {
-  const CopyOnly copy_only{};
-  absl::any o(absl::in_place_type<const volatile IntMoveOnlyCopyOnly>, 5,
-              MoveOnly(), copy_only);
-  auto& v = absl::any_cast<IntMoveOnlyCopyOnly&>(o);
-  EXPECT_EQ(5, v.value);
-}
-
-TEST(AnyTest, InPlaceConstructionWithFunction) {
-  absl::any o(absl::in_place_type_t<FunctionType>(), FunctionToEmplace);
-  FunctionType*& construction_result = absl::any_cast<FunctionType*&>(o);
-  EXPECT_EQ(&FunctionToEmplace, construction_result);
-}
-
-TEST(AnyTest, InPlaceConstructionWithFunctionVariableTemplate) {
-  absl::any o(absl::in_place_type<FunctionType>, FunctionToEmplace);
-  auto& construction_result = absl::any_cast<FunctionType*&>(o);
-  EXPECT_EQ(&FunctionToEmplace, construction_result);
-}
-
-TEST(AnyTest, InPlaceConstructionWithArray) {
-  ArrayType ar = {5, 42};
-  absl::any o(absl::in_place_type_t<ArrayType>(), ar);
-  DecayedArray& construction_result = absl::any_cast<DecayedArray&>(o);
-  EXPECT_EQ(&ar[0], construction_result);
-}
-
-TEST(AnyTest, InPlaceConstructionWithArrayVariableTemplate) {
-  ArrayType ar = {5, 42};
-  absl::any o(absl::in_place_type<ArrayType>, ar);
-  auto& construction_result = absl::any_cast<DecayedArray&>(o);
-  EXPECT_EQ(&ar[0], construction_result);
-}
-
-TEST(AnyTest, InPlaceConstructionIlist) {
-  const CopyOnly copy_only{};
-  absl::any o(absl::in_place_type_t<ListMoveOnlyCopyOnly>(), {1, 2, 3, 4},
-              MoveOnly(), copy_only);
-  ListMoveOnlyCopyOnly& v = absl::any_cast<ListMoveOnlyCopyOnly&>(o);
-  std::vector<int> expected_values = {1, 2, 3, 4};
-  EXPECT_EQ(expected_values, v.values);
-}
-
-TEST(AnyTest, InPlaceConstructionIlistVariableTemplate) {
-  const CopyOnly copy_only{};
-  absl::any o(absl::in_place_type<ListMoveOnlyCopyOnly>, {1, 2, 3, 4},
-              MoveOnly(), copy_only);
-  auto& v = absl::any_cast<ListMoveOnlyCopyOnly&>(o);
-  std::vector<int> expected_values = {1, 2, 3, 4};
-  EXPECT_EQ(expected_values, v.values);
-}
-
-TEST(AnyTest, InPlaceConstructionIlistWithCV) {
-  const CopyOnly copy_only{};
-  absl::any o(absl::in_place_type_t<const volatile ListMoveOnlyCopyOnly>(),
-              {1, 2, 3, 4}, MoveOnly(), copy_only);
-  ListMoveOnlyCopyOnly& v = absl::any_cast<ListMoveOnlyCopyOnly&>(o);
-  std::vector<int> expected_values = {1, 2, 3, 4};
-  EXPECT_EQ(expected_values, v.values);
-}
-
-TEST(AnyTest, InPlaceConstructionIlistWithCVVariableTemplate) {
-  const CopyOnly copy_only{};
-  absl::any o(absl::in_place_type<const volatile ListMoveOnlyCopyOnly>,
-              {1, 2, 3, 4}, MoveOnly(), copy_only);
-  auto& v = absl::any_cast<ListMoveOnlyCopyOnly&>(o);
-  std::vector<int> expected_values = {1, 2, 3, 4};
-  EXPECT_EQ(expected_values, v.values);
-}
-
-TEST(AnyTest, InPlaceNoArgs) {
-  absl::any o(absl::in_place_type_t<int>{});
-  EXPECT_EQ(0, absl::any_cast<int&>(o));
-}
-
-TEST(AnyTest, InPlaceNoArgsVariableTemplate) {
-  absl::any o(absl::in_place_type<int>);
-  EXPECT_EQ(0, absl::any_cast<int&>(o));
-}
-
-template <typename Enabler, typename T, typename... Args>
-struct CanEmplaceAnyImpl : std::false_type {};
-
-template <typename T, typename... Args>
-struct CanEmplaceAnyImpl<
-    absl::void_t<decltype(
-        std::declval<absl::any&>().emplace<T>(std::declval<Args>()...))>,
-    T, Args...> : std::true_type {};
-
-template <typename T, typename... Args>
-using CanEmplaceAny = CanEmplaceAnyImpl<void, T, Args...>;
-
-TEST(AnyTest, Emplace) {
-  const CopyOnly copy_only{};
-  absl::any o;
-  EXPECT_TRUE((std::is_same<decltype(o.emplace<IntMoveOnlyCopyOnly>(
-                                5, MoveOnly(), copy_only)),
-                            IntMoveOnlyCopyOnly&>::value));
-  IntMoveOnlyCopyOnly& emplace_result =
-      o.emplace<IntMoveOnlyCopyOnly>(5, MoveOnly(), copy_only);
-  EXPECT_EQ(5, emplace_result.value);
-  IntMoveOnlyCopyOnly& v = absl::any_cast<IntMoveOnlyCopyOnly&>(o);
-  EXPECT_EQ(5, v.value);
-  EXPECT_EQ(&emplace_result, &v);
-
-  static_assert(!CanEmplaceAny<int, int, int>::value, "");
-  static_assert(!CanEmplaceAny<MoveOnly, MoveOnly>::value, "");
-}
-
-TEST(AnyTest, EmplaceWithCV) {
-  const CopyOnly copy_only{};
-  absl::any o;
-  EXPECT_TRUE(
-      (std::is_same<decltype(o.emplace<const volatile IntMoveOnlyCopyOnly>(
-                        5, MoveOnly(), copy_only)),
-                    IntMoveOnlyCopyOnly&>::value));
-  IntMoveOnlyCopyOnly& emplace_result =
-      o.emplace<const volatile IntMoveOnlyCopyOnly>(5, MoveOnly(), copy_only);
-  EXPECT_EQ(5, emplace_result.value);
-  IntMoveOnlyCopyOnly& v = absl::any_cast<IntMoveOnlyCopyOnly&>(o);
-  EXPECT_EQ(5, v.value);
-  EXPECT_EQ(&emplace_result, &v);
-}
-
-TEST(AnyTest, EmplaceWithFunction) {
-  absl::any o;
-  EXPECT_TRUE(
-      (std::is_same<decltype(o.emplace<FunctionType>(FunctionToEmplace)),
-                    FunctionType*&>::value));
-  FunctionType*& emplace_result = o.emplace<FunctionType>(FunctionToEmplace);
-  EXPECT_EQ(&FunctionToEmplace, emplace_result);
-}
-
-TEST(AnyTest, EmplaceWithArray) {
-  absl::any o;
-  ArrayType ar = {5, 42};
-  EXPECT_TRUE(
-      (std::is_same<decltype(o.emplace<ArrayType>(ar)), DecayedArray&>::value));
-  DecayedArray& emplace_result = o.emplace<ArrayType>(ar);
-  EXPECT_EQ(&ar[0], emplace_result);
-}
-
-TEST(AnyTest, EmplaceIlist) {
-  const CopyOnly copy_only{};
-  absl::any o;
-  EXPECT_TRUE((std::is_same<decltype(o.emplace<ListMoveOnlyCopyOnly>(
-                                {1, 2, 3, 4}, MoveOnly(), copy_only)),
-                            ListMoveOnlyCopyOnly&>::value));
-  ListMoveOnlyCopyOnly& emplace_result =
-      o.emplace<ListMoveOnlyCopyOnly>({1, 2, 3, 4}, MoveOnly(), copy_only);
-  ListMoveOnlyCopyOnly& v = absl::any_cast<ListMoveOnlyCopyOnly&>(o);
-  EXPECT_EQ(&v, &emplace_result);
-  std::vector<int> expected_values = {1, 2, 3, 4};
-  EXPECT_EQ(expected_values, v.values);
-
-  static_assert(!CanEmplaceAny<int, std::initializer_list<int>>::value, "");
-  static_assert(!CanEmplaceAny<MoveOnlyWithListConstructor,
-                               std::initializer_list<int>, int>::value,
-                "");
-}
-
-TEST(AnyTest, EmplaceIlistWithCV) {
-  const CopyOnly copy_only{};
-  absl::any o;
-  EXPECT_TRUE(
-      (std::is_same<decltype(o.emplace<const volatile ListMoveOnlyCopyOnly>(
-                        {1, 2, 3, 4}, MoveOnly(), copy_only)),
-                    ListMoveOnlyCopyOnly&>::value));
-  ListMoveOnlyCopyOnly& emplace_result =
-      o.emplace<const volatile ListMoveOnlyCopyOnly>({1, 2, 3, 4}, MoveOnly(),
-                                                     copy_only);
-  ListMoveOnlyCopyOnly& v = absl::any_cast<ListMoveOnlyCopyOnly&>(o);
-  EXPECT_EQ(&v, &emplace_result);
-  std::vector<int> expected_values = {1, 2, 3, 4};
-  EXPECT_EQ(expected_values, v.values);
-}
-
-TEST(AnyTest, EmplaceNoArgs) {
-  absl::any o;
-  o.emplace<int>();
-  EXPECT_EQ(0, absl::any_cast<int>(o));
-}
-
-TEST(AnyTest, ConversionConstruction) {
-  {
-    absl::any o = 5;
-    EXPECT_EQ(5, absl::any_cast<int>(o));
-  }
-
-  {
-    const CopyOnly copy_only(5);
-    absl::any o = copy_only;
-    EXPECT_EQ(5, absl::any_cast<CopyOnly&>(o).value);
-  }
-
-  static_assert(!std::is_convertible<MoveOnly, absl::any>::value, "");
-}
-
-TEST(AnyTest, ConversionAssignment) {
-  {
-    absl::any o;
-    o = 5;
-    EXPECT_EQ(5, absl::any_cast<int>(o));
-  }
-
-  {
-    const CopyOnly copy_only(5);
-    absl::any o;
-    o = copy_only;
-    EXPECT_EQ(5, absl::any_cast<CopyOnly&>(o).value);
-  }
-
-  static_assert(!std::is_assignable<MoveOnly, absl::any>::value, "");
-}
-
-// Suppress MSVC warnings.
-// 4521: multiple copy constructors specified
-// We wrote multiple of them to test that the correct overloads are selected.
-#ifdef _MSC_VER
-#pragma warning( push )
-#pragma warning( disable : 4521)
-#endif
-
-// Weird type for testing, only used to make sure we "properly" perfect-forward
-// when being placed into an absl::any (use the l-value constructor if given an
-// l-value rather than use the copy constructor).
-struct WeirdConstructor42 {
-  explicit WeirdConstructor42(int value) : value(value) {}
-
-  // Copy-constructor
-  WeirdConstructor42(const WeirdConstructor42& other) : value(other.value) {}
-
-  // L-value "weird" constructor (used when given an l-value)
-  WeirdConstructor42(
-      WeirdConstructor42& /*other*/)  // NOLINT(runtime/references)
-      : value(42) {}
-
-  int value;
-};
-#ifdef _MSC_VER
-#pragma warning( pop )
-#endif
-
-TEST(AnyTest, WeirdConversionConstruction) {
-  {
-    const WeirdConstructor42 source(5);
-    absl::any o = source;  // Actual copy
-    EXPECT_EQ(5, absl::any_cast<WeirdConstructor42&>(o).value);
-  }
-
-  {
-    WeirdConstructor42 source(5);
-    absl::any o = source;  // Weird "conversion"
-    EXPECT_EQ(42, absl::any_cast<WeirdConstructor42&>(o).value);
-  }
-}
-
-TEST(AnyTest, WeirdConversionAssignment) {
-  {
-    const WeirdConstructor42 source(5);
-    absl::any o;
-    o = source;  // Actual copy
-    EXPECT_EQ(5, absl::any_cast<WeirdConstructor42&>(o).value);
-  }
-
-  {
-    WeirdConstructor42 source(5);
-    absl::any o;
-    o = source;  // Weird "conversion"
-    EXPECT_EQ(42, absl::any_cast<WeirdConstructor42&>(o).value);
-  }
-}
-
-struct Value {};
-
-TEST(AnyTest, AnyCastValue) {
-  {
-    absl::any o;
-    o.emplace<int>(5);
-    EXPECT_EQ(5, absl::any_cast<int>(o));
-    EXPECT_EQ(5, absl::any_cast<int>(AsConst(o)));
-    static_assert(
-        std::is_same<decltype(absl::any_cast<Value>(o)), Value>::value, "");
-  }
-
-  {
-    absl::any o;
-    o.emplace<int>(5);
-    EXPECT_EQ(5, absl::any_cast<const int>(o));
-    EXPECT_EQ(5, absl::any_cast<const int>(AsConst(o)));
-    static_assert(std::is_same<decltype(absl::any_cast<const Value>(o)),
-                               const Value>::value,
-                  "");
-  }
-}
-
-TEST(AnyTest, AnyCastReference) {
-  {
-    absl::any o;
-    o.emplace<int>(5);
-    EXPECT_EQ(5, absl::any_cast<int&>(o));
-    EXPECT_EQ(5, absl::any_cast<const int&>(AsConst(o)));
-    static_assert(
-        std::is_same<decltype(absl::any_cast<Value&>(o)), Value&>::value, "");
-  }
-
-  {
-    absl::any o;
-    o.emplace<int>(5);
-    EXPECT_EQ(5, absl::any_cast<const int>(o));
-    EXPECT_EQ(5, absl::any_cast<const int>(AsConst(o)));
-    static_assert(std::is_same<decltype(absl::any_cast<const Value&>(o)),
-                               const Value&>::value,
-                  "");
-  }
-
-  {
-    absl::any o;
-    o.emplace<int>(5);
-    EXPECT_EQ(5, absl::any_cast<int&&>(std::move(o)));
-    static_assert(std::is_same<decltype(absl::any_cast<Value&&>(std::move(o))),
-                               Value&&>::value,
-                  "");
-  }
-
-  {
-    absl::any o;
-    o.emplace<int>(5);
-    EXPECT_EQ(5, absl::any_cast<const int>(std::move(o)));
-    static_assert(
-        std::is_same<decltype(absl::any_cast<const Value&&>(std::move(o))),
-                     const Value&&>::value,
-        "");
-  }
-}
-
-TEST(AnyTest, AnyCastPointer) {
-  {
-    absl::any o;
-    EXPECT_EQ(nullptr, absl::any_cast<char>(&o));
-    o.emplace<int>(5);
-    EXPECT_EQ(nullptr, absl::any_cast<char>(&o));
-    o.emplace<char>('a');
-    EXPECT_EQ('a', *absl::any_cast<char>(&o));
-    static_assert(
-        std::is_same<decltype(absl::any_cast<Value>(&o)), Value*>::value, "");
-  }
-
-  {
-    absl::any o;
-    EXPECT_EQ(nullptr, absl::any_cast<const char>(&o));
-    o.emplace<int>(5);
-    EXPECT_EQ(nullptr, absl::any_cast<const char>(&o));
-    o.emplace<char>('a');
-    EXPECT_EQ('a', *absl::any_cast<const char>(&o));
-    static_assert(std::is_same<decltype(absl::any_cast<const Value>(&o)),
-                               const Value*>::value,
-                  "");
-  }
-}
-
-TEST(AnyTest, MakeAny) {
-  const CopyOnly copy_only{};
-  auto o = absl::make_any<IntMoveOnlyCopyOnly>(5, MoveOnly(), copy_only);
-  static_assert(std::is_same<decltype(o), absl::any>::value, "");
-  EXPECT_EQ(5, absl::any_cast<IntMoveOnlyCopyOnly&>(o).value);
-}
-
-TEST(AnyTest, MakeAnyIList) {
-  const CopyOnly copy_only{};
-  auto o =
-      absl::make_any<ListMoveOnlyCopyOnly>({1, 2, 3}, MoveOnly(), copy_only);
-  static_assert(std::is_same<decltype(o), absl::any>::value, "");
-  ListMoveOnlyCopyOnly& v = absl::any_cast<ListMoveOnlyCopyOnly&>(o);
-  std::vector<int> expected_values = {1, 2, 3};
-  EXPECT_EQ(expected_values, v.values);
-}
-
-// Test the use of copy constructor and operator=
-TEST(AnyTest, Copy) {
-  InstanceTracker tracker_raii;
-
-  {
-    absl::any o(absl::in_place_type<CopyableOnlyInstance>, 123);
-    CopyableOnlyInstance* f1 = absl::any_cast<CopyableOnlyInstance>(&o);
-
-    absl::any o2(o);
-    const CopyableOnlyInstance* f2 = absl::any_cast<CopyableOnlyInstance>(&o2);
-    EXPECT_EQ(123, f2->value());
-    EXPECT_NE(f1, f2);
-
-    absl::any o3;
-    o3 = o2;
-    const CopyableOnlyInstance* f3 = absl::any_cast<CopyableOnlyInstance>(&o3);
-    EXPECT_EQ(123, f3->value());
-    EXPECT_NE(f2, f3);
-
-    const absl::any o4(4);
-    // copy construct from const lvalue ref.
-    absl::any o5 = o4;
-    EXPECT_EQ(4, absl::any_cast<int>(o4));
-    EXPECT_EQ(4, absl::any_cast<int>(o5));
-
-    // Copy construct from const rvalue ref.
-    absl::any o6 = std::move(o4);  // NOLINT
-    EXPECT_EQ(4, absl::any_cast<int>(o4));
-    EXPECT_EQ(4, absl::any_cast<int>(o6));
-  }
-}
-
-TEST(AnyTest, Move) {
-  InstanceTracker tracker_raii;
-
-  absl::any any1;
-  any1.emplace<CopyableOnlyInstance>(5);
-
-  // This is a copy, so copy count increases to 1.
-  absl::any any2 = any1;
-  EXPECT_EQ(5, absl::any_cast<CopyableOnlyInstance&>(any1).value());
-  EXPECT_EQ(5, absl::any_cast<CopyableOnlyInstance&>(any2).value());
-  EXPECT_EQ(1, tracker_raii.copies());
-
-  // This isn't a copy, so copy count doesn't increase.
-  absl::any any3 = std::move(any2);
-  EXPECT_EQ(5, absl::any_cast<CopyableOnlyInstance&>(any3).value());
-  EXPECT_EQ(1, tracker_raii.copies());
-
-  absl::any any4;
-  any4 = std::move(any3);
-  EXPECT_EQ(5, absl::any_cast<CopyableOnlyInstance&>(any4).value());
-  EXPECT_EQ(1, tracker_raii.copies());
-
-  absl::any tmp4(4);
-  absl::any o4(std::move(tmp4));  // move construct
-  EXPECT_EQ(4, absl::any_cast<int>(o4));
-  o4 = *&o4;  // self assign
-  EXPECT_EQ(4, absl::any_cast<int>(o4));
-  EXPECT_TRUE(o4.has_value());
-
-  absl::any o5;
-  absl::any tmp5(5);
-  o5 = std::move(tmp5);  // move assign
-  EXPECT_EQ(5, absl::any_cast<int>(o5));
-}
-
-// Reset the ObjectOwner with an object of a different type
-TEST(AnyTest, Reset) {
-  absl::any o;
-  o.emplace<int>();
-
-  o.reset();
-  EXPECT_FALSE(o.has_value());
-
-  o.emplace<char>();
-  EXPECT_TRUE(o.has_value());
-}
-
-TEST(AnyTest, ConversionConstructionCausesOneCopy) {
-  InstanceTracker tracker_raii;
-  CopyableOnlyInstance counter(5);
-  absl::any o(counter);
-  EXPECT_EQ(5, absl::any_cast<CopyableOnlyInstance&>(o).value());
-  EXPECT_EQ(1, tracker_raii.copies());
-}
-
-//////////////////////////////////
-// Tests for Exception Behavior //
-//////////////////////////////////
-
-#if defined(ABSL_USES_STD_ANY)
-
-// If using a std `any` implementation, we can't check for a specific message.
-#define ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(...)                      \
-  ABSL_BASE_INTERNAL_EXPECT_FAIL((__VA_ARGS__), absl::bad_any_cast, \
-                                 "")
-
-#else
-
-// If using the absl `any` implementation, we can rely on a specific message.
-#define ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(...)                      \
-  ABSL_BASE_INTERNAL_EXPECT_FAIL((__VA_ARGS__), absl::bad_any_cast, \
-                                 "Bad any cast")
-
-#endif  // defined(ABSL_USES_STD_ANY)
-
-TEST(AnyTest, ThrowBadAlloc) {
-  {
-    absl::any a;
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<int&>(a));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const int&>(a));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<int&&>(absl::any{}));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const int&&>(absl::any{}));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<int>(a));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const int>(a));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<int>(absl::any{}));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const int>(absl::any{}));
-
-    // const absl::any operand
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const int&>(AsConst(a)));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<int>(AsConst(a)));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const int>(AsConst(a)));
-  }
-
-  {
-    absl::any a(absl::in_place_type<int>);
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<float&>(a));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const float&>(a));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<float&&>(absl::any{}));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(
-        absl::any_cast<const float&&>(absl::any{}));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<float>(a));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const float>(a));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<float>(absl::any{}));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const float>(absl::any{}));
-
-    // const absl::any operand
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const float&>(AsConst(a)));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<float>(AsConst(a)));
-    ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const float>(AsConst(a)));
-  }
-}
-
-class BadCopy {};
-
-struct BadCopyable {
-  BadCopyable() = default;
-  BadCopyable(BadCopyable&&) = default;
-  BadCopyable(const BadCopyable&) {
-#ifdef ABSL_HAVE_EXCEPTIONS
-    throw BadCopy();
-#else
-    ABSL_RAW_LOG(FATAL, "Bad copy");
-#endif
-  }
-};
-
-#define ABSL_ANY_TEST_EXPECT_BAD_COPY(...) \
-  ABSL_BASE_INTERNAL_EXPECT_FAIL((__VA_ARGS__), BadCopy, "Bad copy")
-
-// Test the guarantees regarding exceptions in copy/assign.
-TEST(AnyTest, FailedCopy) {
-  {
-    const BadCopyable bad{};
-    ABSL_ANY_TEST_EXPECT_BAD_COPY(absl::any{bad});
-  }
-
-  {
-    absl::any src(absl::in_place_type<BadCopyable>);
-    ABSL_ANY_TEST_EXPECT_BAD_COPY(absl::any{src});
-  }
-
-  {
-    BadCopyable bad;
-    absl::any target;
-    ABSL_ANY_TEST_EXPECT_BAD_COPY(target = bad);
-  }
-
-  {
-    BadCopyable bad;
-    absl::any target(absl::in_place_type<BadCopyable>);
-    ABSL_ANY_TEST_EXPECT_BAD_COPY(target = bad);
-    EXPECT_TRUE(target.has_value());
-  }
-
-  {
-    absl::any src(absl::in_place_type<BadCopyable>);
-    absl::any target;
-    ABSL_ANY_TEST_EXPECT_BAD_COPY(target = src);
-    EXPECT_FALSE(target.has_value());
-  }
-
-  {
-    absl::any src(absl::in_place_type<BadCopyable>);
-    absl::any target(absl::in_place_type<BadCopyable>);
-    ABSL_ANY_TEST_EXPECT_BAD_COPY(target = src);
-    EXPECT_TRUE(target.has_value());
-  }
-}
-
-// Test the guarantees regarding exceptions in emplace.
-TEST(AnyTest, FailedEmplace) {
-  {
-    BadCopyable bad;
-    absl::any target;
-    ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace<BadCopyable>(bad));
-  }
-
-  {
-    BadCopyable bad;
-    absl::any target(absl::in_place_type<int>);
-    ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace<BadCopyable>(bad));
-#if defined(ABSL_USES_STD_ANY) && defined(__GLIBCXX__)
-    // libstdc++ std::any::emplace() implementation (as of 7.2) has a bug: if an
-    // exception is thrown, *this contains a value.
-#define ABSL_GLIBCXX_ANY_EMPLACE_EXCEPTION_BUG 1
-#endif
-#if defined(ABSL_HAVE_EXCEPTIONS) && \
-    !defined(ABSL_GLIBCXX_ANY_EMPLACE_EXCEPTION_BUG)
-    EXPECT_FALSE(target.has_value());
-#endif
-  }
-}
-
-}  // namespace
-
-#endif  // #if !defined(ABSL_USES_STD_ANY)
diff --git a/third_party/abseil_cpp/absl/types/bad_any_cast.cc b/third_party/abseil_cpp/absl/types/bad_any_cast.cc
deleted file mode 100644
index b0592cc9bc..0000000000
--- a/third_party/abseil_cpp/absl/types/bad_any_cast.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/bad_any_cast.h"
-
-#ifndef ABSL_USES_STD_ANY
-
-#include <cstdlib>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-bad_any_cast::~bad_any_cast() = default;
-
-const char* bad_any_cast::what() const noexcept { return "Bad any cast"; }
-
-namespace any_internal {
-
-void ThrowBadAnyCast() {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  throw bad_any_cast();
-#else
-  ABSL_RAW_LOG(FATAL, "Bad any cast");
-  std::abort();
-#endif
-}
-
-}  // namespace any_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_USES_STD_ANY
diff --git a/third_party/abseil_cpp/absl/types/bad_any_cast.h b/third_party/abseil_cpp/absl/types/bad_any_cast.h
deleted file mode 100644
index 114cef80cd..0000000000
--- a/third_party/abseil_cpp/absl/types/bad_any_cast.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// bad_any_cast.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the `absl::bad_any_cast` type.
-
-#ifndef ABSL_TYPES_BAD_ANY_CAST_H_
-#define ABSL_TYPES_BAD_ANY_CAST_H_
-
-#include <typeinfo>
-
-#include "absl/base/config.h"
-
-#ifdef ABSL_USES_STD_ANY
-
-#include <any>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-using std::bad_any_cast;
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // ABSL_USES_STD_ANY
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// bad_any_cast
-// -----------------------------------------------------------------------------
-//
-// An `absl::bad_any_cast` type is an exception type that is thrown when
-// failing to successfully cast the return value of an `absl::any` object.
-//
-// Example:
-//
-//   auto a = absl::any(65);
-//   absl::any_cast<int>(a);         // 65
-//   try {
-//     absl::any_cast<char>(a);
-//   } catch(const absl::bad_any_cast& e) {
-//     std::cout << "Bad any cast: " << e.what() << '\n';
-//   }
-class bad_any_cast : public std::bad_cast {
- public:
-  ~bad_any_cast() override;
-  const char* what() const noexcept override;
-};
-
-namespace any_internal {
-
-[[noreturn]] void ThrowBadAnyCast();
-
-}  // namespace any_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_USES_STD_ANY
-
-#endif  // ABSL_TYPES_BAD_ANY_CAST_H_
diff --git a/third_party/abseil_cpp/absl/types/bad_optional_access.cc b/third_party/abseil_cpp/absl/types/bad_optional_access.cc
deleted file mode 100644
index 26aca70d9c..0000000000
--- a/third_party/abseil_cpp/absl/types/bad_optional_access.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/bad_optional_access.h"
-
-#ifndef ABSL_USES_STD_OPTIONAL
-
-#include <cstdlib>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-bad_optional_access::~bad_optional_access() = default;
-
-const char* bad_optional_access::what() const noexcept {
-  return "optional has no value";
-}
-
-namespace optional_internal {
-
-void throw_bad_optional_access() {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  throw bad_optional_access();
-#else
-  ABSL_RAW_LOG(FATAL, "Bad optional access");
-  abort();
-#endif
-}
-
-}  // namespace optional_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_USES_STD_OPTIONAL
diff --git a/third_party/abseil_cpp/absl/types/bad_optional_access.h b/third_party/abseil_cpp/absl/types/bad_optional_access.h
deleted file mode 100644
index a500286adc..0000000000
--- a/third_party/abseil_cpp/absl/types/bad_optional_access.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// bad_optional_access.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the `absl::bad_optional_access` type.
-
-#ifndef ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_
-#define ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_
-
-#include <stdexcept>
-
-#include "absl/base/config.h"
-
-#ifdef ABSL_USES_STD_OPTIONAL
-
-#include <optional>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-using std::bad_optional_access;
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // ABSL_USES_STD_OPTIONAL
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// bad_optional_access
-// -----------------------------------------------------------------------------
-//
-// An `absl::bad_optional_access` type is an exception type that is thrown when
-// attempting to access an `absl::optional` object that does not contain a
-// value.
-//
-// Example:
-//
-//   absl::optional<int> o;
-//
-//   try {
-//     int n = o.value();
-//   } catch(const absl::bad_optional_access& e) {
-//     std::cout << "Bad optional access: " << e.what() << '\n';
-//   }
-class bad_optional_access : public std::exception {
- public:
-  bad_optional_access() = default;
-  ~bad_optional_access() override;
-  const char* what() const noexcept override;
-};
-
-namespace optional_internal {
-
-// throw delegator
-[[noreturn]] void throw_bad_optional_access();
-
-}  // namespace optional_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_USES_STD_OPTIONAL
-
-#endif  // ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_
diff --git a/third_party/abseil_cpp/absl/types/bad_variant_access.cc b/third_party/abseil_cpp/absl/types/bad_variant_access.cc
deleted file mode 100644
index 3dc88cc09f..0000000000
--- a/third_party/abseil_cpp/absl/types/bad_variant_access.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/bad_variant_access.h"
-
-#ifndef ABSL_USES_STD_VARIANT
-
-#include <cstdlib>
-#include <stdexcept>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-//////////////////////////
-// [variant.bad.access] //
-//////////////////////////
-
-bad_variant_access::~bad_variant_access() = default;
-
-const char* bad_variant_access::what() const noexcept {
-  return "Bad variant access";
-}
-
-namespace variant_internal {
-
-void ThrowBadVariantAccess() {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  throw bad_variant_access();
-#else
-  ABSL_RAW_LOG(FATAL, "Bad variant access");
-  abort();  // TODO(calabrese) Remove once RAW_LOG FATAL is noreturn.
-#endif
-}
-
-void Rethrow() {
-#ifdef ABSL_HAVE_EXCEPTIONS
-  throw;
-#else
-  ABSL_RAW_LOG(FATAL,
-               "Internal error in absl::variant implementation. Attempted to "
-               "rethrow an exception when building with exceptions disabled.");
-  abort();  // TODO(calabrese) Remove once RAW_LOG FATAL is noreturn.
-#endif
-}
-
-}  // namespace variant_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_USES_STD_VARIANT
diff --git a/third_party/abseil_cpp/absl/types/bad_variant_access.h b/third_party/abseil_cpp/absl/types/bad_variant_access.h
deleted file mode 100644
index 095969f91e..0000000000
--- a/third_party/abseil_cpp/absl/types/bad_variant_access.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// bad_variant_access.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the `absl::bad_variant_access` type.
-
-#ifndef ABSL_TYPES_BAD_VARIANT_ACCESS_H_
-#define ABSL_TYPES_BAD_VARIANT_ACCESS_H_
-
-#include <stdexcept>
-
-#include "absl/base/config.h"
-
-#ifdef ABSL_USES_STD_VARIANT
-
-#include <variant>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-using std::bad_variant_access;
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // ABSL_USES_STD_VARIANT
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// bad_variant_access
-// -----------------------------------------------------------------------------
-//
-// An `absl::bad_variant_access` type is an exception type that is thrown in
-// the following cases:
-//
-//   * Calling `absl::get(absl::variant) with an index or type that does not
-//     match the currently selected alternative type
-//   * Calling `absl::visit on an `absl::variant` that is in the
-//     `variant::valueless_by_exception` state.
-//
-// Example:
-//
-//   absl::variant<int, std::string> v;
-//   v = 1;
-//   try {
-//     absl::get<std::string>(v);
-//   } catch(const absl::bad_variant_access& e) {
-//     std::cout << "Bad variant access: " << e.what() << '\n';
-//   }
-class bad_variant_access : public std::exception {
- public:
-  bad_variant_access() noexcept = default;
-  ~bad_variant_access() override;
-  const char* what() const noexcept override;
-};
-
-namespace variant_internal {
-
-[[noreturn]] void ThrowBadVariantAccess();
-[[noreturn]] void Rethrow();
-
-}  // namespace variant_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_USES_STD_VARIANT
-
-#endif  // ABSL_TYPES_BAD_VARIANT_ACCESS_H_
diff --git a/third_party/abseil_cpp/absl/types/compare.h b/third_party/abseil_cpp/absl/types/compare.h
deleted file mode 100644
index 19b076e7f1..0000000000
--- a/third_party/abseil_cpp/absl/types/compare.h
+++ /dev/null
@@ -1,600 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// compare.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the `absl::weak_equality`, `absl::strong_equality`,
-// `absl::partial_ordering`, `absl::weak_ordering`, and `absl::strong_ordering`
-// types for storing the results of three way comparisons.
-//
-// Example:
-//   absl::weak_ordering compare(const std::string& a, const std::string& b);
-//
-// These are C++11 compatible versions of the C++20 corresponding types
-// (`std::weak_equality`, etc.) and are designed to be drop-in replacements
-// for code compliant with C++20.
-
-#ifndef ABSL_TYPES_COMPARE_H_
-#define ABSL_TYPES_COMPARE_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <cstdlib>
-#include <type_traits>
-
-#include "absl/base/attributes.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace compare_internal {
-
-using value_type = int8_t;
-
-template <typename T>
-struct Fail {
-  static_assert(sizeof(T) < 0, "Only literal `0` is allowed.");
-};
-
-// We need the NullPtrT template to avoid triggering the modernize-use-nullptr
-// ClangTidy warning in user code.
-template <typename NullPtrT = std::nullptr_t>
-struct OnlyLiteralZero {
-  constexpr OnlyLiteralZero(NullPtrT) noexcept {}  // NOLINT
-
-  // Fails compilation when `nullptr` or integral type arguments other than
-  // `int` are passed. This constructor doesn't accept `int` because literal `0`
-  // has type `int`. Literal `0` arguments will be implicitly converted to
-  // `std::nullptr_t` and accepted by the above constructor, while other `int`
-  // arguments will fail to be converted and cause compilation failure.
-  template <
-      typename T,
-      typename = typename std::enable_if<
-          std::is_same<T, std::nullptr_t>::value ||
-          (std::is_integral<T>::value && !std::is_same<T, int>::value)>::type,
-      typename = typename Fail<T>::type>
-  OnlyLiteralZero(T);  // NOLINT
-};
-
-enum class eq : value_type {
-  equal = 0,
-  equivalent = equal,
-  nonequal = 1,
-  nonequivalent = nonequal,
-};
-
-enum class ord : value_type { less = -1, greater = 1 };
-
-enum class ncmp : value_type { unordered = -127 };
-
-// Define macros to allow for creation or emulation of C++17 inline variables
-// based on whether the feature is supported. Note: we can't use
-// ABSL_INTERNAL_INLINE_CONSTEXPR here because the variables here are of
-// incomplete types so they need to be defined after the types are complete.
-#ifdef __cpp_inline_variables
-
-// A no-op expansion that can be followed by a semicolon at class level.
-#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) static_assert(true, "")
-
-#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) \
-  static const type name
-
-#define ABSL_COMPARE_INLINE_INIT(type, name, init) \
-  inline constexpr type type::name(init)
-
-#else  // __cpp_inline_variables
-
-#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) \
-  ABSL_CONST_INIT static const T name
-
-// A no-op expansion that can be followed by a semicolon at class level.
-#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) static_assert(true, "")
-
-#define ABSL_COMPARE_INLINE_INIT(type, name, init) \
-  template <typename T>                            \
-  const T compare_internal::type##_base<T>::name(init)
-
-#endif  // __cpp_inline_variables
-
-// These template base classes allow for defining the values of the constants
-// in the header file (for performance) without using inline variables (which
-// aren't available in C++11).
-template <typename T>
-struct weak_equality_base {
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent);
-};
-
-template <typename T>
-struct strong_equality_base {
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(equal);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequal);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent);
-};
-
-template <typename T>
-struct partial_ordering_base {
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(greater);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(unordered);
-};
-
-template <typename T>
-struct weak_ordering_base {
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(greater);
-};
-
-template <typename T>
-struct strong_ordering_base {
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(equal);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
-  ABSL_COMPARE_INLINE_BASECLASS_DECL(greater);
-};
-
-}  // namespace compare_internal
-
-class weak_equality
-    : public compare_internal::weak_equality_base<weak_equality> {
-  explicit constexpr weak_equality(compare_internal::eq v) noexcept
-      : value_(static_cast<compare_internal::value_type>(v)) {}
-  friend struct compare_internal::weak_equality_base<weak_equality>;
-
- public:
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, equivalent);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, nonequivalent);
-
-  // Comparisons
-  friend constexpr bool operator==(
-      weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ == 0;
-  }
-  friend constexpr bool operator!=(
-      weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ != 0;
-  }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
-                                   weak_equality v) noexcept {
-    return 0 == v.value_;
-  }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
-                                   weak_equality v) noexcept {
-    return 0 != v.value_;
-  }
-  friend constexpr bool operator==(weak_equality v1,
-                                   weak_equality v2) noexcept {
-    return v1.value_ == v2.value_;
-  }
-  friend constexpr bool operator!=(weak_equality v1,
-                                   weak_equality v2) noexcept {
-    return v1.value_ != v2.value_;
-  }
-
- private:
-  compare_internal::value_type value_;
-};
-ABSL_COMPARE_INLINE_INIT(weak_equality, equivalent,
-                         compare_internal::eq::equivalent);
-ABSL_COMPARE_INLINE_INIT(weak_equality, nonequivalent,
-                         compare_internal::eq::nonequivalent);
-
-class strong_equality
-    : public compare_internal::strong_equality_base<strong_equality> {
-  explicit constexpr strong_equality(compare_internal::eq v) noexcept
-      : value_(static_cast<compare_internal::value_type>(v)) {}
-  friend struct compare_internal::strong_equality_base<strong_equality>;
-
- public:
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equal);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequal);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equivalent);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequivalent);
-
-  // Conversion
-  constexpr operator weak_equality() const noexcept {  // NOLINT
-    return value_ == 0 ? weak_equality::equivalent
-                       : weak_equality::nonequivalent;
-  }
-  // Comparisons
-  friend constexpr bool operator==(
-      strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ == 0;
-  }
-  friend constexpr bool operator!=(
-      strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ != 0;
-  }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
-                                   strong_equality v) noexcept {
-    return 0 == v.value_;
-  }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
-                                   strong_equality v) noexcept {
-    return 0 != v.value_;
-  }
-  friend constexpr bool operator==(strong_equality v1,
-                                   strong_equality v2) noexcept {
-    return v1.value_ == v2.value_;
-  }
-  friend constexpr bool operator!=(strong_equality v1,
-                                   strong_equality v2) noexcept {
-    return v1.value_ != v2.value_;
-  }
-
- private:
-  compare_internal::value_type value_;
-};
-ABSL_COMPARE_INLINE_INIT(strong_equality, equal, compare_internal::eq::equal);
-ABSL_COMPARE_INLINE_INIT(strong_equality, nonequal,
-                         compare_internal::eq::nonequal);
-ABSL_COMPARE_INLINE_INIT(strong_equality, equivalent,
-                         compare_internal::eq::equivalent);
-ABSL_COMPARE_INLINE_INIT(strong_equality, nonequivalent,
-                         compare_internal::eq::nonequivalent);
-
-class partial_ordering
-    : public compare_internal::partial_ordering_base<partial_ordering> {
-  explicit constexpr partial_ordering(compare_internal::eq v) noexcept
-      : value_(static_cast<compare_internal::value_type>(v)) {}
-  explicit constexpr partial_ordering(compare_internal::ord v) noexcept
-      : value_(static_cast<compare_internal::value_type>(v)) {}
-  explicit constexpr partial_ordering(compare_internal::ncmp v) noexcept
-      : value_(static_cast<compare_internal::value_type>(v)) {}
-  friend struct compare_internal::partial_ordering_base<partial_ordering>;
-
-  constexpr bool is_ordered() const noexcept {
-    return value_ !=
-           compare_internal::value_type(compare_internal::ncmp::unordered);
-  }
-
- public:
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, less);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, equivalent);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, greater);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, unordered);
-
-  // Conversion
-  constexpr operator weak_equality() const noexcept {  // NOLINT
-    return value_ == 0 ? weak_equality::equivalent
-                       : weak_equality::nonequivalent;
-  }
-  // Comparisons
-  friend constexpr bool operator==(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.is_ordered() && v.value_ == 0;
-  }
-  friend constexpr bool operator!=(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return !v.is_ordered() || v.value_ != 0;
-  }
-  friend constexpr bool operator<(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.is_ordered() && v.value_ < 0;
-  }
-  friend constexpr bool operator<=(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.is_ordered() && v.value_ <= 0;
-  }
-  friend constexpr bool operator>(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.is_ordered() && v.value_ > 0;
-  }
-  friend constexpr bool operator>=(
-      partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.is_ordered() && v.value_ >= 0;
-  }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
-                                   partial_ordering v) noexcept {
-    return v.is_ordered() && 0 == v.value_;
-  }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
-                                   partial_ordering v) noexcept {
-    return !v.is_ordered() || 0 != v.value_;
-  }
-  friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>,
-                                  partial_ordering v) noexcept {
-    return v.is_ordered() && 0 < v.value_;
-  }
-  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>,
-                                   partial_ordering v) noexcept {
-    return v.is_ordered() && 0 <= v.value_;
-  }
-  friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>,
-                                  partial_ordering v) noexcept {
-    return v.is_ordered() && 0 > v.value_;
-  }
-  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>,
-                                   partial_ordering v) noexcept {
-    return v.is_ordered() && 0 >= v.value_;
-  }
-  friend constexpr bool operator==(partial_ordering v1,
-                                   partial_ordering v2) noexcept {
-    return v1.value_ == v2.value_;
-  }
-  friend constexpr bool operator!=(partial_ordering v1,
-                                   partial_ordering v2) noexcept {
-    return v1.value_ != v2.value_;
-  }
-
- private:
-  compare_internal::value_type value_;
-};
-ABSL_COMPARE_INLINE_INIT(partial_ordering, less, compare_internal::ord::less);
-ABSL_COMPARE_INLINE_INIT(partial_ordering, equivalent,
-                         compare_internal::eq::equivalent);
-ABSL_COMPARE_INLINE_INIT(partial_ordering, greater,
-                         compare_internal::ord::greater);
-ABSL_COMPARE_INLINE_INIT(partial_ordering, unordered,
-                         compare_internal::ncmp::unordered);
-
-class weak_ordering
-    : public compare_internal::weak_ordering_base<weak_ordering> {
-  explicit constexpr weak_ordering(compare_internal::eq v) noexcept
-      : value_(static_cast<compare_internal::value_type>(v)) {}
-  explicit constexpr weak_ordering(compare_internal::ord v) noexcept
-      : value_(static_cast<compare_internal::value_type>(v)) {}
-  friend struct compare_internal::weak_ordering_base<weak_ordering>;
-
- public:
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, less);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, equivalent);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, greater);
-
-  // Conversions
-  constexpr operator weak_equality() const noexcept {  // NOLINT
-    return value_ == 0 ? weak_equality::equivalent
-                       : weak_equality::nonequivalent;
-  }
-  constexpr operator partial_ordering() const noexcept {  // NOLINT
-    return value_ == 0 ? partial_ordering::equivalent
-                       : (value_ < 0 ? partial_ordering::less
-                                     : partial_ordering::greater);
-  }
-  // Comparisons
-  friend constexpr bool operator==(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ == 0;
-  }
-  friend constexpr bool operator!=(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ != 0;
-  }
-  friend constexpr bool operator<(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ < 0;
-  }
-  friend constexpr bool operator<=(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ <= 0;
-  }
-  friend constexpr bool operator>(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ > 0;
-  }
-  friend constexpr bool operator>=(
-      weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ >= 0;
-  }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
-                                   weak_ordering v) noexcept {
-    return 0 == v.value_;
-  }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
-                                   weak_ordering v) noexcept {
-    return 0 != v.value_;
-  }
-  friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>,
-                                  weak_ordering v) noexcept {
-    return 0 < v.value_;
-  }
-  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>,
-                                   weak_ordering v) noexcept {
-    return 0 <= v.value_;
-  }
-  friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>,
-                                  weak_ordering v) noexcept {
-    return 0 > v.value_;
-  }
-  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>,
-                                   weak_ordering v) noexcept {
-    return 0 >= v.value_;
-  }
-  friend constexpr bool operator==(weak_ordering v1,
-                                   weak_ordering v2) noexcept {
-    return v1.value_ == v2.value_;
-  }
-  friend constexpr bool operator!=(weak_ordering v1,
-                                   weak_ordering v2) noexcept {
-    return v1.value_ != v2.value_;
-  }
-
- private:
-  compare_internal::value_type value_;
-};
-ABSL_COMPARE_INLINE_INIT(weak_ordering, less, compare_internal::ord::less);
-ABSL_COMPARE_INLINE_INIT(weak_ordering, equivalent,
-                         compare_internal::eq::equivalent);
-ABSL_COMPARE_INLINE_INIT(weak_ordering, greater,
-                         compare_internal::ord::greater);
-
-class strong_ordering
-    : public compare_internal::strong_ordering_base<strong_ordering> {
-  explicit constexpr strong_ordering(compare_internal::eq v) noexcept
-      : value_(static_cast<compare_internal::value_type>(v)) {}
-  explicit constexpr strong_ordering(compare_internal::ord v) noexcept
-      : value_(static_cast<compare_internal::value_type>(v)) {}
-  friend struct compare_internal::strong_ordering_base<strong_ordering>;
-
- public:
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, less);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equal);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equivalent);
-  ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, greater);
-
-  // Conversions
-  constexpr operator weak_equality() const noexcept {  // NOLINT
-    return value_ == 0 ? weak_equality::equivalent
-                       : weak_equality::nonequivalent;
-  }
-  constexpr operator strong_equality() const noexcept {  // NOLINT
-    return value_ == 0 ? strong_equality::equal : strong_equality::nonequal;
-  }
-  constexpr operator partial_ordering() const noexcept {  // NOLINT
-    return value_ == 0 ? partial_ordering::equivalent
-                       : (value_ < 0 ? partial_ordering::less
-                                     : partial_ordering::greater);
-  }
-  constexpr operator weak_ordering() const noexcept {  // NOLINT
-    return value_ == 0
-               ? weak_ordering::equivalent
-               : (value_ < 0 ? weak_ordering::less : weak_ordering::greater);
-  }
-  // Comparisons
-  friend constexpr bool operator==(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ == 0;
-  }
-  friend constexpr bool operator!=(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ != 0;
-  }
-  friend constexpr bool operator<(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ < 0;
-  }
-  friend constexpr bool operator<=(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ <= 0;
-  }
-  friend constexpr bool operator>(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ > 0;
-  }
-  friend constexpr bool operator>=(
-      strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept {
-    return v.value_ >= 0;
-  }
-  friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>,
-                                   strong_ordering v) noexcept {
-    return 0 == v.value_;
-  }
-  friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>,
-                                   strong_ordering v) noexcept {
-    return 0 != v.value_;
-  }
-  friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>,
-                                  strong_ordering v) noexcept {
-    return 0 < v.value_;
-  }
-  friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>,
-                                   strong_ordering v) noexcept {
-    return 0 <= v.value_;
-  }
-  friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>,
-                                  strong_ordering v) noexcept {
-    return 0 > v.value_;
-  }
-  friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>,
-                                   strong_ordering v) noexcept {
-    return 0 >= v.value_;
-  }
-  friend constexpr bool operator==(strong_ordering v1,
-                                   strong_ordering v2) noexcept {
-    return v1.value_ == v2.value_;
-  }
-  friend constexpr bool operator!=(strong_ordering v1,
-                                   strong_ordering v2) noexcept {
-    return v1.value_ != v2.value_;
-  }
-
- private:
-  compare_internal::value_type value_;
-};
-ABSL_COMPARE_INLINE_INIT(strong_ordering, less, compare_internal::ord::less);
-ABSL_COMPARE_INLINE_INIT(strong_ordering, equal, compare_internal::eq::equal);
-ABSL_COMPARE_INLINE_INIT(strong_ordering, equivalent,
-                         compare_internal::eq::equivalent);
-ABSL_COMPARE_INLINE_INIT(strong_ordering, greater,
-                         compare_internal::ord::greater);
-
-#undef ABSL_COMPARE_INLINE_BASECLASS_DECL
-#undef ABSL_COMPARE_INLINE_SUBCLASS_DECL
-#undef ABSL_COMPARE_INLINE_INIT
-
-namespace compare_internal {
-// We also provide these comparator adapter functions for internal absl use.
-
-// Helper functions to do a boolean comparison of two keys given a boolean
-// or three-way comparator.
-// SFINAE prevents implicit conversions to bool (such as from int).
-template <typename Bool,
-          absl::enable_if_t<std::is_same<bool, Bool>::value, int> = 0>
-constexpr bool compare_result_as_less_than(const Bool r) { return r; }
-constexpr bool compare_result_as_less_than(const absl::weak_ordering r) {
-  return r < 0;
-}
-
-template <typename Compare, typename K, typename LK>
-constexpr bool do_less_than_comparison(const Compare &compare, const K &x,
-                                       const LK &y) {
-  return compare_result_as_less_than(compare(x, y));
-}
-
-// Helper functions to do a three-way comparison of two keys given a boolean or
-// three-way comparator.
-// SFINAE prevents implicit conversions to int (such as from bool).
-template <typename Int,
-          absl::enable_if_t<std::is_same<int, Int>::value, int> = 0>
-constexpr absl::weak_ordering compare_result_as_ordering(const Int c) {
-  return c < 0 ? absl::weak_ordering::less
-               : c == 0 ? absl::weak_ordering::equivalent
-                        : absl::weak_ordering::greater;
-}
-constexpr absl::weak_ordering compare_result_as_ordering(
-    const absl::weak_ordering c) {
-  return c;
-}
-
-template <
-    typename Compare, typename K, typename LK,
-    absl::enable_if_t<!std::is_same<bool, absl::result_of_t<Compare(
-                                              const K &, const LK &)>>::value,
-                      int> = 0>
-constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare,
-                                                      const K &x, const LK &y) {
-  return compare_result_as_ordering(compare(x, y));
-}
-template <
-    typename Compare, typename K, typename LK,
-    absl::enable_if_t<std::is_same<bool, absl::result_of_t<Compare(
-                                             const K &, const LK &)>>::value,
-                      int> = 0>
-constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare,
-                                                      const K &x, const LK &y) {
-  return compare(x, y) ? absl::weak_ordering::less
-                       : compare(y, x) ? absl::weak_ordering::greater
-                                       : absl::weak_ordering::equivalent;
-}
-
-}  // namespace compare_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TYPES_COMPARE_H_
diff --git a/third_party/abseil_cpp/absl/types/compare_test.cc b/third_party/abseil_cpp/absl/types/compare_test.cc
deleted file mode 100644
index 8095baf956..0000000000
--- a/third_party/abseil_cpp/absl/types/compare_test.cc
+++ /dev/null
@@ -1,389 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/compare.h"
-
-#include "gtest/gtest.h"
-#include "absl/base/casts.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-// This is necessary to avoid a bunch of lint warnings suggesting that we use
-// EXPECT_EQ/etc., which doesn't work in this case because they convert the `0`
-// to an int, which can't be converted to the unspecified zero type.
-bool Identity(bool b) { return b; }
-
-TEST(Compare, WeakEquality) {
-  EXPECT_TRUE(Identity(weak_equality::equivalent == 0));
-  EXPECT_TRUE(Identity(0 == weak_equality::equivalent));
-  EXPECT_TRUE(Identity(weak_equality::nonequivalent != 0));
-  EXPECT_TRUE(Identity(0 != weak_equality::nonequivalent));
-  const weak_equality values[] = {weak_equality::equivalent,
-                                  weak_equality::nonequivalent};
-  for (const auto& lhs : values) {
-    for (const auto& rhs : values) {
-      const bool are_equal = &lhs == &rhs;
-      EXPECT_EQ(lhs == rhs, are_equal);
-      EXPECT_EQ(lhs != rhs, !are_equal);
-    }
-  }
-}
-
-TEST(Compare, StrongEquality) {
-  EXPECT_TRUE(Identity(strong_equality::equal == 0));
-  EXPECT_TRUE(Identity(0 == strong_equality::equal));
-  EXPECT_TRUE(Identity(strong_equality::nonequal != 0));
-  EXPECT_TRUE(Identity(0 != strong_equality::nonequal));
-  EXPECT_TRUE(Identity(strong_equality::equivalent == 0));
-  EXPECT_TRUE(Identity(0 == strong_equality::equivalent));
-  EXPECT_TRUE(Identity(strong_equality::nonequivalent != 0));
-  EXPECT_TRUE(Identity(0 != strong_equality::nonequivalent));
-  const strong_equality values[] = {strong_equality::equal,
-                                    strong_equality::nonequal};
-  for (const auto& lhs : values) {
-    for (const auto& rhs : values) {
-      const bool are_equal = &lhs == &rhs;
-      EXPECT_EQ(lhs == rhs, are_equal);
-      EXPECT_EQ(lhs != rhs, !are_equal);
-    }
-  }
-  EXPECT_TRUE(Identity(strong_equality::equivalent == strong_equality::equal));
-  EXPECT_TRUE(
-      Identity(strong_equality::nonequivalent == strong_equality::nonequal));
-}
-
-TEST(Compare, PartialOrdering) {
-  EXPECT_TRUE(Identity(partial_ordering::less < 0));
-  EXPECT_TRUE(Identity(0 > partial_ordering::less));
-  EXPECT_TRUE(Identity(partial_ordering::less <= 0));
-  EXPECT_TRUE(Identity(0 >= partial_ordering::less));
-  EXPECT_TRUE(Identity(partial_ordering::equivalent == 0));
-  EXPECT_TRUE(Identity(0 == partial_ordering::equivalent));
-  EXPECT_TRUE(Identity(partial_ordering::greater > 0));
-  EXPECT_TRUE(Identity(0 < partial_ordering::greater));
-  EXPECT_TRUE(Identity(partial_ordering::greater >= 0));
-  EXPECT_TRUE(Identity(0 <= partial_ordering::greater));
-  EXPECT_TRUE(Identity(partial_ordering::unordered != 0));
-  EXPECT_TRUE(Identity(0 != partial_ordering::unordered));
-  EXPECT_FALSE(Identity(partial_ordering::unordered < 0));
-  EXPECT_FALSE(Identity(0 < partial_ordering::unordered));
-  EXPECT_FALSE(Identity(partial_ordering::unordered <= 0));
-  EXPECT_FALSE(Identity(0 <= partial_ordering::unordered));
-  EXPECT_FALSE(Identity(partial_ordering::unordered > 0));
-  EXPECT_FALSE(Identity(0 > partial_ordering::unordered));
-  EXPECT_FALSE(Identity(partial_ordering::unordered >= 0));
-  EXPECT_FALSE(Identity(0 >= partial_ordering::unordered));
-  const partial_ordering values[] = {
-      partial_ordering::less, partial_ordering::equivalent,
-      partial_ordering::greater, partial_ordering::unordered};
-  for (const auto& lhs : values) {
-    for (const auto& rhs : values) {
-      const bool are_equal = &lhs == &rhs;
-      EXPECT_EQ(lhs == rhs, are_equal);
-      EXPECT_EQ(lhs != rhs, !are_equal);
-    }
-  }
-}
-
-TEST(Compare, WeakOrdering) {
-  EXPECT_TRUE(Identity(weak_ordering::less < 0));
-  EXPECT_TRUE(Identity(0 > weak_ordering::less));
-  EXPECT_TRUE(Identity(weak_ordering::less <= 0));
-  EXPECT_TRUE(Identity(0 >= weak_ordering::less));
-  EXPECT_TRUE(Identity(weak_ordering::equivalent == 0));
-  EXPECT_TRUE(Identity(0 == weak_ordering::equivalent));
-  EXPECT_TRUE(Identity(weak_ordering::greater > 0));
-  EXPECT_TRUE(Identity(0 < weak_ordering::greater));
-  EXPECT_TRUE(Identity(weak_ordering::greater >= 0));
-  EXPECT_TRUE(Identity(0 <= weak_ordering::greater));
-  const weak_ordering values[] = {
-      weak_ordering::less, weak_ordering::equivalent, weak_ordering::greater};
-  for (const auto& lhs : values) {
-    for (const auto& rhs : values) {
-      const bool are_equal = &lhs == &rhs;
-      EXPECT_EQ(lhs == rhs, are_equal);
-      EXPECT_EQ(lhs != rhs, !are_equal);
-    }
-  }
-}
-
-TEST(Compare, StrongOrdering) {
-  EXPECT_TRUE(Identity(strong_ordering::less < 0));
-  EXPECT_TRUE(Identity(0 > strong_ordering::less));
-  EXPECT_TRUE(Identity(strong_ordering::less <= 0));
-  EXPECT_TRUE(Identity(0 >= strong_ordering::less));
-  EXPECT_TRUE(Identity(strong_ordering::equal == 0));
-  EXPECT_TRUE(Identity(0 == strong_ordering::equal));
-  EXPECT_TRUE(Identity(strong_ordering::equivalent == 0));
-  EXPECT_TRUE(Identity(0 == strong_ordering::equivalent));
-  EXPECT_TRUE(Identity(strong_ordering::greater > 0));
-  EXPECT_TRUE(Identity(0 < strong_ordering::greater));
-  EXPECT_TRUE(Identity(strong_ordering::greater >= 0));
-  EXPECT_TRUE(Identity(0 <= strong_ordering::greater));
-  const strong_ordering values[] = {
-      strong_ordering::less, strong_ordering::equal, strong_ordering::greater};
-  for (const auto& lhs : values) {
-    for (const auto& rhs : values) {
-      const bool are_equal = &lhs == &rhs;
-      EXPECT_EQ(lhs == rhs, are_equal);
-      EXPECT_EQ(lhs != rhs, !are_equal);
-    }
-  }
-  EXPECT_TRUE(Identity(strong_ordering::equivalent == strong_ordering::equal));
-}
-
-TEST(Compare, Conversions) {
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(strong_equality::equal) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(strong_equality::nonequal) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(strong_equality::equivalent) == 0));
-  EXPECT_TRUE(Identity(
-      implicit_cast<weak_equality>(strong_equality::nonequivalent) != 0));
-
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(partial_ordering::less) != 0));
-  EXPECT_TRUE(Identity(
-      implicit_cast<weak_equality>(partial_ordering::equivalent) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(partial_ordering::greater) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(partial_ordering::unordered) != 0));
-
-  EXPECT_TRUE(implicit_cast<weak_equality>(weak_ordering::less) != 0);
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(weak_ordering::equivalent) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(weak_ordering::greater) != 0));
-
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(weak_ordering::less) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(weak_ordering::less) < 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(weak_ordering::less) <= 0));
-  EXPECT_TRUE(Identity(
-      implicit_cast<partial_ordering>(weak_ordering::equivalent) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(weak_ordering::greater) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(weak_ordering::greater) > 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(weak_ordering::greater) >= 0));
-
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(strong_ordering::less) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(strong_ordering::equal) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(strong_ordering::equivalent) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_equality>(strong_ordering::greater) != 0));
-
-  EXPECT_TRUE(
-      Identity(implicit_cast<strong_equality>(strong_ordering::less) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<strong_equality>(strong_ordering::equal) == 0));
-  EXPECT_TRUE(Identity(
-      implicit_cast<strong_equality>(strong_ordering::equivalent) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<strong_equality>(strong_ordering::greater) != 0));
-
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(strong_ordering::less) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(strong_ordering::less) < 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(strong_ordering::less) <= 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(strong_ordering::equal) == 0));
-  EXPECT_TRUE(Identity(
-      implicit_cast<partial_ordering>(strong_ordering::equivalent) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(strong_ordering::greater) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(strong_ordering::greater) > 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<partial_ordering>(strong_ordering::greater) >= 0));
-
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_ordering>(strong_ordering::less) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_ordering>(strong_ordering::less) < 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_ordering>(strong_ordering::less) <= 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_ordering>(strong_ordering::equal) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_ordering>(strong_ordering::equivalent) == 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_ordering>(strong_ordering::greater) != 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_ordering>(strong_ordering::greater) > 0));
-  EXPECT_TRUE(
-      Identity(implicit_cast<weak_ordering>(strong_ordering::greater) >= 0));
-}
-
-struct WeakOrderingLess {
-  template <typename T>
-  absl::weak_ordering operator()(const T& a, const T& b) const {
-    return a < b ? absl::weak_ordering::less
-                 : a == b ? absl::weak_ordering::equivalent
-                          : absl::weak_ordering::greater;
-  }
-};
-
-TEST(CompareResultAsLessThan, SanityTest) {
-  EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than(false));
-  EXPECT_TRUE(absl::compare_internal::compare_result_as_less_than(true));
-
-  EXPECT_TRUE(
-      absl::compare_internal::compare_result_as_less_than(weak_ordering::less));
-  EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than(
-      weak_ordering::equivalent));
-  EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than(
-      weak_ordering::greater));
-}
-
-TEST(DoLessThanComparison, SanityTest) {
-  std::less<int> less;
-  WeakOrderingLess weak;
-
-  EXPECT_TRUE(absl::compare_internal::do_less_than_comparison(less, -1, 0));
-  EXPECT_TRUE(absl::compare_internal::do_less_than_comparison(weak, -1, 0));
-
-  EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(less, 10, 10));
-  EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(weak, 10, 10));
-
-  EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(less, 10, 5));
-  EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(weak, 10, 5));
-}
-
-TEST(CompareResultAsOrdering, SanityTest) {
-  EXPECT_TRUE(
-      Identity(absl::compare_internal::compare_result_as_ordering(-1) < 0));
-  EXPECT_FALSE(
-      Identity(absl::compare_internal::compare_result_as_ordering(-1) == 0));
-  EXPECT_FALSE(
-      Identity(absl::compare_internal::compare_result_as_ordering(-1) > 0));
-  EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering(
-                           weak_ordering::less) < 0));
-  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
-                            weak_ordering::less) == 0));
-  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
-                            weak_ordering::less) > 0));
-
-  EXPECT_FALSE(
-      Identity(absl::compare_internal::compare_result_as_ordering(0) < 0));
-  EXPECT_TRUE(
-      Identity(absl::compare_internal::compare_result_as_ordering(0) == 0));
-  EXPECT_FALSE(
-      Identity(absl::compare_internal::compare_result_as_ordering(0) > 0));
-  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
-                            weak_ordering::equivalent) < 0));
-  EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering(
-                           weak_ordering::equivalent) == 0));
-  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
-                            weak_ordering::equivalent) > 0));
-
-  EXPECT_FALSE(
-      Identity(absl::compare_internal::compare_result_as_ordering(1) < 0));
-  EXPECT_FALSE(
-      Identity(absl::compare_internal::compare_result_as_ordering(1) == 0));
-  EXPECT_TRUE(
-      Identity(absl::compare_internal::compare_result_as_ordering(1) > 0));
-  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
-                            weak_ordering::greater) < 0));
-  EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
-                            weak_ordering::greater) == 0));
-  EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering(
-                           weak_ordering::greater) > 0));
-}
-
-TEST(DoThreeWayComparison, SanityTest) {
-  std::less<int> less;
-  WeakOrderingLess weak;
-
-  EXPECT_TRUE(Identity(
-      absl::compare_internal::do_three_way_comparison(less, -1, 0) < 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(less, -1, 0) == 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(less, -1, 0) > 0));
-  EXPECT_TRUE(Identity(
-      absl::compare_internal::do_three_way_comparison(weak, -1, 0) < 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(weak, -1, 0) == 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(weak, -1, 0) > 0));
-
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(less, 10, 10) < 0));
-  EXPECT_TRUE(Identity(
-      absl::compare_internal::do_three_way_comparison(less, 10, 10) == 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(less, 10, 10) > 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(weak, 10, 10) < 0));
-  EXPECT_TRUE(Identity(
-      absl::compare_internal::do_three_way_comparison(weak, 10, 10) == 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(weak, 10, 10) > 0));
-
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(less, 10, 5) < 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(less, 10, 5) == 0));
-  EXPECT_TRUE(Identity(
-      absl::compare_internal::do_three_way_comparison(less, 10, 5) > 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(weak, 10, 5) < 0));
-  EXPECT_FALSE(Identity(
-      absl::compare_internal::do_three_way_comparison(weak, 10, 5) == 0));
-  EXPECT_TRUE(Identity(
-      absl::compare_internal::do_three_way_comparison(weak, 10, 5) > 0));
-}
-
-#ifdef __cpp_inline_variables
-TEST(Compare, StaticAsserts) {
-  static_assert(weak_equality::equivalent == 0, "");
-  static_assert(weak_equality::nonequivalent != 0, "");
-
-  static_assert(strong_equality::equal == 0, "");
-  static_assert(strong_equality::nonequal != 0, "");
-  static_assert(strong_equality::equivalent == 0, "");
-  static_assert(strong_equality::nonequivalent != 0, "");
-
-  static_assert(partial_ordering::less < 0, "");
-  static_assert(partial_ordering::equivalent == 0, "");
-  static_assert(partial_ordering::greater > 0, "");
-  static_assert(partial_ordering::unordered != 0, "");
-
-  static_assert(weak_ordering::less < 0, "");
-  static_assert(weak_ordering::equivalent == 0, "");
-  static_assert(weak_ordering::greater > 0, "");
-
-  static_assert(strong_ordering::less < 0, "");
-  static_assert(strong_ordering::equal == 0, "");
-  static_assert(strong_ordering::equivalent == 0, "");
-  static_assert(strong_ordering::greater > 0, "");
-}
-#endif  // __cpp_inline_variables
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/types/internal/conformance_aliases.h b/third_party/abseil_cpp/absl/types/internal/conformance_aliases.h
deleted file mode 100644
index 0cc6884e30..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/conformance_aliases.h
+++ /dev/null
@@ -1,447 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// regularity_aliases.h
-// -----------------------------------------------------------------------------
-//
-// This file contains type aliases of common ConformanceProfiles and Archetypes
-// so that they can be directly used by name without creating them from scratch.
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_
-
-#include "absl/types/internal/conformance_archetype.h"
-#include "absl/types/internal/conformance_profile.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// Creates both a Profile and a corresponding Archetype with root name "name".
-#define ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(name, ...)                \
-  struct name##Profile : __VA_ARGS__ {};                                    \
-                                                                            \
-  using name##Archetype = ::absl::types_internal::Archetype<name##Profile>; \
-                                                                            \
-  template <class AbslInternalProfileTag>                                   \
-  using name##Archetype##_ = ::absl::types_internal::Archetype<             \
-      ::absl::types_internal::StrongProfileTypedef<name##Profile,           \
-                                                   AbslInternalProfileTag>>
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialDefaultConstructor,
-    ConformanceProfile<default_constructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowDefaultConstructor,
-    ConformanceProfile<default_constructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasDefaultConstructor, ConformanceProfile<default_constructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialMoveConstructor, ConformanceProfile<default_constructible::maybe,
-                                                  move_constructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowMoveConstructor, ConformanceProfile<default_constructible::maybe,
-                                                  move_constructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasMoveConstructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialCopyConstructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowCopyConstructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasCopyConstructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialMoveAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowMoveAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasMoveAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialCopyAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowCopyAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasCopyAssign,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasTrivialDestructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowDestructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasDestructor,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowEquality,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasEquality,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowInequality,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe,
-                       inequality_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasInequality,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowLessThan,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::maybe,
-                       less_than_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasLessThan,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::maybe,
-                       less_than_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowLessEqual,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::maybe,
-                       less_than_comparable::maybe,
-                       less_equal_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasLessEqual,
-    ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
-                       copy_constructible::maybe, move_assignable::maybe,
-                       copy_assignable::maybe, destructible::maybe,
-                       equality_comparable::maybe, inequality_comparable::maybe,
-                       less_than_comparable::maybe,
-                       less_equal_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowGreaterEqual,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasGreaterEqual,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowGreaterThan,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasGreaterThan,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasNothrowSwap,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::maybe, swappable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasSwap,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::maybe, swappable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HasStdHashSpecialization,
-    ConformanceProfile<
-        default_constructible::maybe, move_constructible::maybe,
-        copy_constructible::maybe, move_assignable::maybe,
-        copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
-        inequality_comparable::maybe, less_than_comparable::maybe,
-        less_equal_comparable::maybe, greater_equal_comparable::maybe,
-        greater_than_comparable::maybe, swappable::maybe, hashable::yes>);
-
-////////////////////////////////////////////////////////////////////////////////
-////     The remaining aliases are combinations of the previous aliases.    ////
-////////////////////////////////////////////////////////////////////////////////
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    Equatable, CombineProfiles<HasEqualityProfile, HasInequalityProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    Comparable,
-    CombineProfiles<EquatableProfile, HasLessThanProfile, HasLessEqualProfile,
-                    HasGreaterEqualProfile, HasGreaterThanProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    NothrowEquatable,
-    CombineProfiles<HasNothrowEqualityProfile, HasNothrowInequalityProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    NothrowComparable,
-    CombineProfiles<NothrowEquatableProfile, HasNothrowLessThanProfile,
-                    HasNothrowLessEqualProfile, HasNothrowGreaterEqualProfile,
-                    HasNothrowGreaterThanProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    Value,
-    CombineProfiles<HasNothrowMoveConstructorProfile, HasCopyConstructorProfile,
-                    HasNothrowMoveAssignProfile, HasCopyAssignProfile,
-                    HasNothrowDestructorProfile, HasNothrowSwapProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    EquatableValue, CombineProfiles<EquatableProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableValue, CombineProfiles<ComparableProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    DefaultConstructibleValue,
-    CombineProfiles<HasDefaultConstructorProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    NothrowMoveConstructible, CombineProfiles<HasNothrowMoveConstructorProfile,
-                                              HasNothrowDestructorProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    EquatableNothrowMoveConstructible,
-    CombineProfiles<EquatableProfile, NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableNothrowMoveConstructible,
-    CombineProfiles<ComparableProfile, NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    DefaultConstructibleNothrowMoveConstructible,
-    CombineProfiles<HasDefaultConstructorProfile,
-                    NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    CopyConstructible,
-    CombineProfiles<HasNothrowMoveConstructorProfile, HasCopyConstructorProfile,
-                    HasNothrowDestructorProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    EquatableCopyConstructible,
-    CombineProfiles<EquatableProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableCopyConstructible,
-    CombineProfiles<ComparableProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    DefaultConstructibleCopyConstructible,
-    CombineProfiles<HasDefaultConstructorProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    NothrowMovable,
-    CombineProfiles<HasNothrowMoveConstructorProfile,
-                    HasNothrowMoveAssignProfile, HasNothrowDestructorProfile,
-                    HasNothrowSwapProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    EquatableNothrowMovable,
-    CombineProfiles<EquatableProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableNothrowMovable,
-    CombineProfiles<ComparableProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    DefaultConstructibleNothrowMovable,
-    CombineProfiles<HasDefaultConstructorProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    TrivialSpecialMemberFunctions,
-    CombineProfiles<HasTrivialDefaultConstructorProfile,
-                    HasTrivialMoveConstructorProfile,
-                    HasTrivialCopyConstructorProfile,
-                    HasTrivialMoveAssignProfile, HasTrivialCopyAssignProfile,
-                    HasTrivialDestructorProfile, HasNothrowSwapProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    TriviallyComplete,
-    CombineProfiles<TrivialSpecialMemberFunctionsProfile, ComparableProfile,
-                    HasStdHashSpecializationProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HashableNothrowMoveConstructible,
-    CombineProfiles<HasStdHashSpecializationProfile,
-                    NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HashableCopyConstructible,
-    CombineProfiles<HasStdHashSpecializationProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HashableNothrowMovable,
-    CombineProfiles<HasStdHashSpecializationProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    HashableValue,
-    CombineProfiles<HasStdHashSpecializationProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
-    ComparableHashableValue,
-    CombineProfiles<HashableValueProfile, ComparableProfile>);
-
-// The "preferred" profiles that we support in Abseil.
-template <template <class...> class Receiver>
-using ExpandBasicProfiles =
-    Receiver<NothrowMoveConstructibleProfile, CopyConstructibleProfile,
-             NothrowMovableProfile, ValueProfile>;
-
-// The basic profiles except that they are also all Equatable.
-template <template <class...> class Receiver>
-using ExpandBasicEquatableProfiles =
-    Receiver<EquatableNothrowMoveConstructibleProfile,
-             EquatableCopyConstructibleProfile, EquatableNothrowMovableProfile,
-             EquatableValueProfile>;
-
-// The basic profiles except that they are also all Comparable.
-template <template <class...> class Receiver>
-using ExpandBasicComparableProfiles =
-    Receiver<ComparableNothrowMoveConstructibleProfile,
-             ComparableCopyConstructibleProfile,
-             ComparableNothrowMovableProfile, ComparableValueProfile>;
-
-// The basic profiles except that they are also all Hashable.
-template <template <class...> class Receiver>
-using ExpandBasicHashableProfiles =
-    Receiver<HashableNothrowMoveConstructibleProfile,
-             HashableCopyConstructibleProfile, HashableNothrowMovableProfile,
-             HashableValueProfile>;
-
-// The basic profiles except that they are also all DefaultConstructible.
-template <template <class...> class Receiver>
-using ExpandBasicDefaultConstructibleProfiles =
-    Receiver<DefaultConstructibleNothrowMoveConstructibleProfile,
-             DefaultConstructibleCopyConstructibleProfile,
-             DefaultConstructibleNothrowMovableProfile,
-             DefaultConstructibleValueProfile>;
-
-// The type profiles that we support in Abseil (all of the previous lists).
-template <template <class...> class Receiver>
-using ExpandSupportedProfiles = Receiver<
-    NothrowMoveConstructibleProfile, CopyConstructibleProfile,
-    NothrowMovableProfile, ValueProfile,
-    EquatableNothrowMoveConstructibleProfile, EquatableCopyConstructibleProfile,
-    EquatableNothrowMovableProfile, EquatableValueProfile,
-    ComparableNothrowMoveConstructibleProfile,
-    ComparableCopyConstructibleProfile, ComparableNothrowMovableProfile,
-    ComparableValueProfile, DefaultConstructibleNothrowMoveConstructibleProfile,
-    DefaultConstructibleCopyConstructibleProfile,
-    DefaultConstructibleNothrowMovableProfile, DefaultConstructibleValueProfile,
-    HashableNothrowMoveConstructibleProfile, HashableCopyConstructibleProfile,
-    HashableNothrowMovableProfile, HashableValueProfile>;
-
-// TODO(calabrese) Include types that have throwing move constructors, since in
-// practice we still need to support them because of standard library types with
-// (potentially) non-noexcept moves.
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#undef ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_
diff --git a/third_party/abseil_cpp/absl/types/internal/conformance_archetype.h b/third_party/abseil_cpp/absl/types/internal/conformance_archetype.h
deleted file mode 100644
index 2349e0f726..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/conformance_archetype.h
+++ /dev/null
@@ -1,978 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// conformance_archetype.h
-// -----------------------------------------------------------------------------
-//
-// This file contains a facility for generating "archetypes" of out of
-// "Conformance Profiles" (see "conformance_profiles.h" for more information
-// about Conformance Profiles). An archetype is a type that aims to support the
-// bare minimum requirements of a given Conformance Profile. For instance, an
-// archetype that corresponds to an ImmutableProfile has exactly a nothrow
-// move-constructor, a potentially-throwing copy constructor, a nothrow
-// destructor, with all other special-member-functions deleted. These archetypes
-// are useful for testing to make sure that templates are able to work with the
-// kinds of types that they claim to support (i.e. that they do not accidentally
-// under-constrain),
-//
-// The main type template in this file is the Archetype template, which takes
-// a Conformance Profile as a template argument and its instantiations are a
-// minimum-conforming model of that profile.
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_
-
-#include <cstddef>
-#include <functional>
-#include <type_traits>
-#include <utility>
-
-#include "absl/meta/type_traits.h"
-#include "absl/types/internal/conformance_profile.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// A minimum-conforming implementation of a type with properties specified in
-// `Prof`, where `Prof` is a valid Conformance Profile.
-template <class Prof, class /*Enabler*/ = void>
-class Archetype;
-
-// Given an Archetype, obtain the properties of the profile associated with that
-// archetype.
-template <class Archetype>
-struct PropertiesOfArchetype;
-
-template <class Prof>
-struct PropertiesOfArchetype<Archetype<Prof>> {
-  using type = PropertiesOfT<Prof>;
-};
-
-template <class Archetype>
-using PropertiesOfArchetypeT = typename PropertiesOfArchetype<Archetype>::type;
-
-// A metafunction to determine if a type is an `Archetype`.
-template <class T>
-struct IsArchetype : std::false_type {};
-
-template <class Prof>
-struct IsArchetype<Archetype<Prof>> : std::true_type {};
-
-// A constructor tag type used when creating an Archetype with internal state.
-struct MakeArchetypeState {};
-
-// Data stored within an archetype that is copied/compared/hashed when the
-// corresponding operations are used.
-using ArchetypeState = std::size_t;
-
-////////////////////////////////////////////////////////////////////////////////
-//   This section of the file defines a chain of base classes for Archetype,  //
-//   where each base defines a specific special member function with the      //
-//   appropriate properties (deleted, noexcept(false), noexcept, or trivial). //
-////////////////////////////////////////////////////////////////////////////////
-
-// The bottom-most base, which contains the state and the default constructor.
-template <default_constructible DefaultConstructibleValue>
-struct ArchetypeStateBase {
-  static_assert(DefaultConstructibleValue == default_constructible::yes ||
-                    DefaultConstructibleValue == default_constructible::nothrow,
-                "");
-
-  ArchetypeStateBase() noexcept(
-      DefaultConstructibleValue ==
-      default_constructible::
-          nothrow) /*Vacuous archetype_state initialization*/ {}
-  explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept
-      : archetype_state(state) {}
-
-  ArchetypeState archetype_state;
-};
-
-template <>
-struct ArchetypeStateBase<default_constructible::maybe> {
-  explicit ArchetypeStateBase() = delete;
-  explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept
-      : archetype_state(state) {}
-
-  ArchetypeState archetype_state;
-};
-
-template <>
-struct ArchetypeStateBase<default_constructible::trivial> {
-  ArchetypeStateBase() = default;
-  explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept
-      : archetype_state(state) {}
-
-  ArchetypeState archetype_state;
-};
-
-// The move-constructor base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue>
-struct ArchetypeMoveConstructor
-    : ArchetypeStateBase<DefaultConstructibleValue> {
-  static_assert(MoveConstructibleValue == move_constructible::yes ||
-                    MoveConstructibleValue == move_constructible::nothrow,
-                "");
-
-  explicit ArchetypeMoveConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(),
-                                                      state) {}
-
-  ArchetypeMoveConstructor() = default;
-  ArchetypeMoveConstructor(ArchetypeMoveConstructor&& other) noexcept(
-      MoveConstructibleValue == move_constructible::nothrow)
-      : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(),
-                                                      other.archetype_state) {}
-  ArchetypeMoveConstructor(const ArchetypeMoveConstructor&) = default;
-  ArchetypeMoveConstructor& operator=(ArchetypeMoveConstructor&&) = default;
-  ArchetypeMoveConstructor& operator=(const ArchetypeMoveConstructor&) =
-      default;
-};
-
-template <default_constructible DefaultConstructibleValue>
-struct ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                move_constructible::trivial>
-    : ArchetypeStateBase<DefaultConstructibleValue> {
-  explicit ArchetypeMoveConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(),
-                                                      state) {}
-
-  ArchetypeMoveConstructor() = default;
-};
-
-// The copy-constructor base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue>
-struct ArchetypeCopyConstructor
-    : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue> {
-  static_assert(CopyConstructibleValue == copy_constructible::yes ||
-                    CopyConstructibleValue == copy_constructible::nothrow,
-                "");
-  explicit ArchetypeCopyConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeCopyConstructor() = default;
-  ArchetypeCopyConstructor(ArchetypeCopyConstructor&&) = default;
-  ArchetypeCopyConstructor(const ArchetypeCopyConstructor& other) noexcept(
-      CopyConstructibleValue == copy_constructible::nothrow)
-      : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue>(
-            MakeArchetypeState(), other.archetype_state) {}
-  ArchetypeCopyConstructor& operator=(ArchetypeCopyConstructor&&) = default;
-  ArchetypeCopyConstructor& operator=(const ArchetypeCopyConstructor&) =
-      default;
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue>
-struct ArchetypeCopyConstructor<DefaultConstructibleValue,
-                                MoveConstructibleValue,
-                                copy_constructible::maybe>
-    : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue> {
-  explicit ArchetypeCopyConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeCopyConstructor() = default;
-  ArchetypeCopyConstructor(ArchetypeCopyConstructor&&) = default;
-  ArchetypeCopyConstructor(const ArchetypeCopyConstructor&) = delete;
-  ArchetypeCopyConstructor& operator=(ArchetypeCopyConstructor&&) = default;
-  ArchetypeCopyConstructor& operator=(const ArchetypeCopyConstructor&) =
-      default;
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue>
-struct ArchetypeCopyConstructor<DefaultConstructibleValue,
-                                MoveConstructibleValue,
-                                copy_constructible::trivial>
-    : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue> {
-  explicit ArchetypeCopyConstructor(MakeArchetypeState,
-                                    ArchetypeState state) noexcept
-      : ArchetypeMoveConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeCopyConstructor() = default;
-};
-
-// The move-assign base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue>
-struct ArchetypeMoveAssign
-    : ArchetypeCopyConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue, CopyConstructibleValue> {
-  static_assert(MoveAssignableValue == move_assignable::yes ||
-                    MoveAssignableValue == move_assignable::nothrow,
-                "");
-  explicit ArchetypeMoveAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeCopyConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue,
-                                 CopyConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeMoveAssign() = default;
-  ArchetypeMoveAssign(ArchetypeMoveAssign&&) = default;
-  ArchetypeMoveAssign(const ArchetypeMoveAssign&) = default;
-  ArchetypeMoveAssign& operator=(ArchetypeMoveAssign&& other) noexcept(
-      MoveAssignableValue == move_assignable::nothrow) {
-    this->archetype_state = other.archetype_state;
-    return *this;
-  }
-
-  ArchetypeMoveAssign& operator=(const ArchetypeMoveAssign&) = default;
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue>
-struct ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                           CopyConstructibleValue, move_assignable::trivial>
-    : ArchetypeCopyConstructor<DefaultConstructibleValue,
-                               MoveConstructibleValue, CopyConstructibleValue> {
-  explicit ArchetypeMoveAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeCopyConstructor<DefaultConstructibleValue,
-                                 MoveConstructibleValue,
-                                 CopyConstructibleValue>(MakeArchetypeState(),
-                                                         state) {}
-
-  ArchetypeMoveAssign() = default;
-};
-
-// The copy-assign base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue,
-          copy_assignable CopyAssignableValue>
-struct ArchetypeCopyAssign
-    : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue> {
-  static_assert(CopyAssignableValue == copy_assignable::yes ||
-                    CopyAssignableValue == copy_assignable::nothrow,
-                "");
-  explicit ArchetypeCopyAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue>(
-            MakeArchetypeState(), state) {}
-
-  ArchetypeCopyAssign() = default;
-  ArchetypeCopyAssign(ArchetypeCopyAssign&&) = default;
-  ArchetypeCopyAssign(const ArchetypeCopyAssign&) = default;
-  ArchetypeCopyAssign& operator=(ArchetypeCopyAssign&&) = default;
-
-  ArchetypeCopyAssign& operator=(const ArchetypeCopyAssign& other) noexcept(
-      CopyAssignableValue == copy_assignable::nothrow) {
-    this->archetype_state = other.archetype_state;
-    return *this;
-  }
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue>
-struct ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                           CopyConstructibleValue, MoveAssignableValue,
-                           copy_assignable::maybe>
-    : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue> {
-  explicit ArchetypeCopyAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue>(
-            MakeArchetypeState(), state) {}
-
-  ArchetypeCopyAssign() = default;
-  ArchetypeCopyAssign(ArchetypeCopyAssign&&) = default;
-  ArchetypeCopyAssign(const ArchetypeCopyAssign&) = default;
-  ArchetypeCopyAssign& operator=(ArchetypeCopyAssign&&) = default;
-  ArchetypeCopyAssign& operator=(const ArchetypeCopyAssign&) = delete;
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue>
-struct ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                           CopyConstructibleValue, MoveAssignableValue,
-                           copy_assignable::trivial>
-    : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue> {
-  explicit ArchetypeCopyAssign(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue>(
-            MakeArchetypeState(), state) {}
-
-  ArchetypeCopyAssign() = default;
-};
-
-// The destructor base
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue,
-          copy_assignable CopyAssignableValue, destructible DestructibleValue>
-struct ArchetypeDestructor
-    : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue,
-                          CopyAssignableValue> {
-  static_assert(DestructibleValue == destructible::yes ||
-                    DestructibleValue == destructible::nothrow,
-                "");
-
-  explicit ArchetypeDestructor(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue,
-                            CopyAssignableValue>(MakeArchetypeState(), state) {}
-
-  ArchetypeDestructor() = default;
-  ArchetypeDestructor(ArchetypeDestructor&&) = default;
-  ArchetypeDestructor(const ArchetypeDestructor&) = default;
-  ArchetypeDestructor& operator=(ArchetypeDestructor&&) = default;
-  ArchetypeDestructor& operator=(const ArchetypeDestructor&) = default;
-  ~ArchetypeDestructor() noexcept(DestructibleValue == destructible::nothrow) {}
-};
-
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue,
-          copy_assignable CopyAssignableValue>
-struct ArchetypeDestructor<DefaultConstructibleValue, MoveConstructibleValue,
-                           CopyConstructibleValue, MoveAssignableValue,
-                           CopyAssignableValue, destructible::trivial>
-    : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                          CopyConstructibleValue, MoveAssignableValue,
-                          CopyAssignableValue> {
-  explicit ArchetypeDestructor(MakeArchetypeState,
-                               ArchetypeState state) noexcept
-      : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
-                            CopyConstructibleValue, MoveAssignableValue,
-                            CopyAssignableValue>(MakeArchetypeState(), state) {}
-
-  ArchetypeDestructor() = default;
-};
-
-// An alias to the top of the chain of bases for special-member functions.
-// NOTE: move_constructible::maybe, move_assignable::maybe, and
-// destructible::maybe are handled in the top-level type by way of SFINAE.
-// Because of this, we never instantiate the base classes with
-// move_constructible::maybe, move_assignable::maybe, or destructible::maybe so
-// that we minimize the number of different possible type-template
-// instantiations.
-template <default_constructible DefaultConstructibleValue,
-          move_constructible MoveConstructibleValue,
-          copy_constructible CopyConstructibleValue,
-          move_assignable MoveAssignableValue,
-          copy_assignable CopyAssignableValue, destructible DestructibleValue>
-using ArchetypeSpecialMembersBase = ArchetypeDestructor<
-    DefaultConstructibleValue,
-    MoveConstructibleValue != move_constructible::maybe
-        ? MoveConstructibleValue
-        : move_constructible::nothrow,
-    CopyConstructibleValue,
-    MoveAssignableValue != move_assignable::maybe ? MoveAssignableValue
-                                                  : move_assignable::nothrow,
-    CopyAssignableValue,
-    DestructibleValue != destructible::maybe ? DestructibleValue
-                                             : destructible::nothrow>;
-
-// A function that is used to create an archetype with some associated state.
-template <class Arch>
-Arch MakeArchetype(ArchetypeState state) noexcept {
-  static_assert(IsArchetype<Arch>::value,
-                "The explicit template argument to MakeArchetype is required "
-                "to be an Archetype.");
-  return Arch(MakeArchetypeState(), state);
-}
-
-// This is used to conditionally delete "copy" and "move" constructors in a way
-// that is consistent with what the ConformanceProfile requires and that also
-// strictly enforces the arguments to the copy/move to not come from implicit
-// conversions when dealing with the Archetype.
-template <class Prof, class T>
-constexpr bool ShouldDeleteConstructor() {
-  return !((PropertiesOfT<Prof>::move_constructible_support !=
-                move_constructible::maybe &&
-            std::is_same<T, Archetype<Prof>>::value) ||
-           (PropertiesOfT<Prof>::copy_constructible_support !=
-                copy_constructible::maybe &&
-            (std::is_same<T, const Archetype<Prof>&>::value ||
-             std::is_same<T, Archetype<Prof>&>::value ||
-             std::is_same<T, const Archetype<Prof>>::value)));
-}
-
-// This is used to conditionally delete "copy" and "move" assigns in a way
-// that is consistent with what the ConformanceProfile requires and that also
-// strictly enforces the arguments to the copy/move to not come from implicit
-// conversions when dealing with the Archetype.
-template <class Prof, class T>
-constexpr bool ShouldDeleteAssign() {
-  return !(
-      (PropertiesOfT<Prof>::move_assignable_support != move_assignable::maybe &&
-       std::is_same<T, Archetype<Prof>>::value) ||
-      (PropertiesOfT<Prof>::copy_assignable_support != copy_assignable::maybe &&
-       (std::is_same<T, const Archetype<Prof>&>::value ||
-        std::is_same<T, Archetype<Prof>&>::value ||
-        std::is_same<T, const Archetype<Prof>>::value)));
-}
-
-// TODO(calabrese) Inherit from a chain of secondary bases to pull in the
-// associated functions of other concepts.
-template <class Prof, class Enabler>
-class Archetype : ArchetypeSpecialMembersBase<
-                      PropertiesOfT<Prof>::default_constructible_support,
-                      PropertiesOfT<Prof>::move_constructible_support,
-                      PropertiesOfT<Prof>::copy_constructible_support,
-                      PropertiesOfT<Prof>::move_assignable_support,
-                      PropertiesOfT<Prof>::copy_assignable_support,
-                      PropertiesOfT<Prof>::destructible_support> {
-  static_assert(std::is_same<Enabler, void>::value,
-                "An explicit type must not be passed as the second template "
-                "argument to 'Archetype`.");
-
-  // The cases mentioned in these static_asserts are expected to be handled in
-  // the partial template specializations of Archetype that follow this
-  // definition.
-  static_assert(PropertiesOfT<Prof>::destructible_support !=
-                    destructible::maybe,
-                "");
-  static_assert(PropertiesOfT<Prof>::move_constructible_support !=
-                        move_constructible::maybe ||
-                    PropertiesOfT<Prof>::copy_constructible_support ==
-                        copy_constructible::maybe,
-                "");
-  static_assert(PropertiesOfT<Prof>::move_assignable_support !=
-                        move_assignable::maybe ||
-                    PropertiesOfT<Prof>::copy_assignable_support ==
-                        copy_assignable::maybe,
-                "");
-
- public:
-  Archetype() = default;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support !=
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support ==
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support !=
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = default;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = delete;
-  Archetype& operator=(const Archetype&) = default;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support ==
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support ==
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support !=
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = delete;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = delete;
-  Archetype& operator=(const Archetype&) = default;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support ==
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support !=
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support !=
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = delete;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = default;
-  Archetype& operator=(const Archetype&) = default;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support !=
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support ==
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support ==
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = default;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = delete;
-  Archetype& operator=(const Archetype&) = default;
-  ~Archetype() = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support ==
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support ==
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support ==
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = delete;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = delete;
-  Archetype& operator=(const Archetype&) = default;
-  ~Archetype() = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
-                          PropertiesOfT<Prof>::move_constructible_support ==
-                              move_constructible::maybe &&
-                          PropertiesOfT<Prof>::move_assignable_support !=
-                              move_assignable::maybe &&
-                          PropertiesOfT<Prof>::destructible_support ==
-                              destructible::maybe>::type>
-    : ArchetypeSpecialMembersBase<
-          PropertiesOfT<Prof>::default_constructible_support,
-          PropertiesOfT<Prof>::move_constructible_support,
-          PropertiesOfT<Prof>::copy_constructible_support,
-          PropertiesOfT<Prof>::move_assignable_support,
-          PropertiesOfT<Prof>::copy_assignable_support,
-          PropertiesOfT<Prof>::destructible_support> {
- public:
-  Archetype() = default;
-  Archetype(Archetype&&) = delete;
-  Archetype(const Archetype&) = default;
-  Archetype& operator=(Archetype&&) = default;
-  Archetype& operator=(const Archetype&) = default;
-  ~Archetype() = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
-  Archetype(T&&) = delete;
-
-  // Disallow moves when requested, and disallow implicit conversions.
-  template <class T, typename std::enable_if<
-                         ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
-  Archetype& operator=(T&&) = delete;
-
-  using ArchetypeSpecialMembersBase<
-      PropertiesOfT<Prof>::default_constructible_support,
-      PropertiesOfT<Prof>::move_constructible_support,
-      PropertiesOfT<Prof>::copy_constructible_support,
-      PropertiesOfT<Prof>::move_assignable_support,
-      PropertiesOfT<Prof>::copy_assignable_support,
-      PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
-  explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
-      : ArchetypeSpecialMembersBase<
-            PropertiesOfT<Prof>::default_constructible_support,
-            PropertiesOfT<Prof>::move_constructible_support,
-            PropertiesOfT<Prof>::copy_constructible_support,
-            PropertiesOfT<Prof>::move_assignable_support,
-            PropertiesOfT<Prof>::copy_assignable_support,
-            PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
-                                                       state) {}
-
-  friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-// Explicitly deleted swap for Archetype if the profile does not require swap.
-// It is important to delete it rather than simply leave it out so that the
-// "using std::swap;" idiom will result in this deleted overload being picked.
-template <class Prof,
-          absl::enable_if_t<!PropertiesOfT<Prof>::is_swappable, int> = 0>
-void swap(Archetype<Prof>&, Archetype<Prof>&) = delete;  // NOLINT
-
-// A conditionally-noexcept swap implementation for Archetype when the profile
-// supports swap.
-template <class Prof,
-          absl::enable_if_t<PropertiesOfT<Prof>::is_swappable, int> = 0>
-void swap(Archetype<Prof>& lhs, Archetype<Prof>& rhs)  // NOLINT
-    noexcept(PropertiesOfT<Prof>::swappable_support != swappable::yes) {
-  std::swap(lhs.archetype_state, rhs.archetype_state);
-}
-
-// A convertible-to-bool type that is used as the return type of comparison
-// operators since the standard doesn't always require exactly bool.
-struct NothrowBool {
-  explicit NothrowBool() = delete;
-  ~NothrowBool() = default;
-
-  // TODO(calabrese) Delete the copy constructor in C++17 mode since guaranteed
-  // elision makes it not required when returning from a function.
-  // NothrowBool(NothrowBool const&) = delete;
-
-  NothrowBool& operator=(NothrowBool const&) = delete;
-
-  explicit operator bool() const noexcept { return value; }
-
-  static NothrowBool make(bool const value) noexcept {
-    return NothrowBool(value);
-  }
-
- private:
-  explicit NothrowBool(bool const value) noexcept : value(value) {}
-
-  bool value;
-};
-
-// A convertible-to-bool type that is used as the return type of comparison
-// operators since the standard doesn't always require exactly bool.
-// Note: ExceptionalBool has a conversion operator that is not noexcept, so
-// that even when a comparison operator is noexcept, that operation may still
-// potentially throw when converted to bool.
-struct ExceptionalBool {
-  explicit ExceptionalBool() = delete;
-  ~ExceptionalBool() = default;
-
-  // TODO(calabrese) Delete the copy constructor in C++17 mode since guaranteed
-  // elision makes it not required when returning from a function.
-  // ExceptionalBool(ExceptionalBool const&) = delete;
-
-  ExceptionalBool& operator=(ExceptionalBool const&) = delete;
-
-  explicit operator bool() const { return value; }  // NOLINT
-
-  static ExceptionalBool make(bool const value) noexcept {
-    return ExceptionalBool(value);
-  }
-
- private:
-  explicit ExceptionalBool(bool const value) noexcept : value(value) {}
-
-  bool value;
-};
-
-// The following macro is only used as a helper in this file to stamp out
-// comparison operator definitions. It is undefined after usage.
-//
-// NOTE: Non-nothrow operators throw via their result's conversion to bool even
-// though the operation itself is noexcept.
-#define ABSL_TYPES_INTERNAL_OP(enum_name, op)                                \
-  template <class Prof>                                                      \
-  absl::enable_if_t<!PropertiesOfT<Prof>::is_##enum_name, bool> operator op( \
-      const Archetype<Prof>&, const Archetype<Prof>&) = delete;              \
-                                                                             \
-  template <class Prof>                                                      \
-  typename absl::enable_if_t<                                                \
-      PropertiesOfT<Prof>::is_##enum_name,                                   \
-      std::conditional<PropertiesOfT<Prof>::enum_name##_support ==           \
-                           enum_name::nothrow,                               \
-                       NothrowBool, ExceptionalBool>>::type                  \
-  operator op(const Archetype<Prof>& lhs,                                    \
-              const Archetype<Prof>& rhs) noexcept {                         \
-    return absl::conditional_t<                                              \
-        PropertiesOfT<Prof>::enum_name##_support == enum_name::nothrow,      \
-        NothrowBool, ExceptionalBool>::make(lhs.archetype_state op           \
-                                                rhs.archetype_state);        \
-  }
-
-ABSL_TYPES_INTERNAL_OP(equality_comparable, ==);
-ABSL_TYPES_INTERNAL_OP(inequality_comparable, !=);
-ABSL_TYPES_INTERNAL_OP(less_than_comparable, <);
-ABSL_TYPES_INTERNAL_OP(less_equal_comparable, <=);
-ABSL_TYPES_INTERNAL_OP(greater_equal_comparable, >=);
-ABSL_TYPES_INTERNAL_OP(greater_than_comparable, >);
-
-#undef ABSL_TYPES_INTERNAL_OP
-
-// Base class for std::hash specializations when an Archetype doesn't support
-// hashing.
-struct PoisonedHash {
-  PoisonedHash() = delete;
-  PoisonedHash(const PoisonedHash&) = delete;
-  PoisonedHash& operator=(const PoisonedHash&) = delete;
-};
-
-// Base class for std::hash specializations when an Archetype supports hashing.
-template <class Prof>
-struct EnabledHash {
-  using argument_type = Archetype<Prof>;
-  using result_type = std::size_t;
-  result_type operator()(const argument_type& arg) const {
-    return std::hash<ArchetypeState>()(arg.archetype_state);
-  }
-};
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-namespace std {
-
-template <class Prof>  // NOLINT
-struct hash<::absl::types_internal::Archetype<Prof>>
-    : conditional<::absl::types_internal::PropertiesOfT<Prof>::is_hashable,
-                  ::absl::types_internal::EnabledHash<Prof>,
-                  ::absl::types_internal::PoisonedHash>::type {};
-
-}  // namespace std
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_
diff --git a/third_party/abseil_cpp/absl/types/internal/conformance_profile.h b/third_party/abseil_cpp/absl/types/internal/conformance_profile.h
deleted file mode 100644
index cf64ff4fcd..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/conformance_profile.h
+++ /dev/null
@@ -1,931 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// conformance_profiles.h
-// -----------------------------------------------------------------------------
-//
-// This file contains templates for representing "Regularity Profiles" and
-// concisely-named versions of commonly used Regularity Profiles.
-//
-// A Regularity Profile is a compile-time description of the types of operations
-// that a given type supports, along with properties of those operations when
-// they do exist. For instance, a Regularity Profile may describe a type that
-// has a move-constructor that is noexcept and a copy constructor that is not
-// noexcept. This description can then be examined and passed around to other
-// templates for the purposes of asserting expectations on user-defined types
-// via a series trait checks, or for determining what kinds of run-time tests
-// are able to be performed.
-//
-// Regularity Profiles are also used when creating "archetypes," which are
-// minimum-conforming types that meet all of the requirements of a given
-// Regularity Profile. For more information regarding archetypes, see
-// "conformance_archetypes.h".
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_
-
-#include <set>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "absl/algorithm/container.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/internal/conformance_testing_helpers.h"
-#include "absl/utility/utility.h"
-
-// TODO(calabrese) Add support for extending profiles.
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// Converts an enum to its underlying integral value.
-template <typename Enum>
-constexpr absl::underlying_type_t<Enum> UnderlyingValue(Enum value) {
-  return static_cast<absl::underlying_type_t<Enum>>(value);
-}
-
-// A tag type used in place of a matcher when checking that an assertion result
-// does not actually contain any errors.
-struct NoError {};
-
-// -----------------------------------------------------------------------------
-// ConformanceErrors
-// -----------------------------------------------------------------------------
-class ConformanceErrors {
- public:
-  // Setup the error reporting mechanism by seeding it with the name of the type
-  // that is being tested.
-  explicit ConformanceErrors(std::string type_name)
-      : assertion_result_(false), type_name_(std::move(type_name)) {
-    assertion_result_ << "\n\n"
-                         "Assuming the following type alias:\n"
-                         "\n"
-                         "  using _T = "
-                      << type_name_ << ";\n\n";
-    outputDivider();
-  }
-
-  // Adds the test name to the list of successfully run tests iff it was not
-  // previously reported as failing. This behavior is useful for tests that
-  // have multiple parts, where failures and successes are reported individually
-  // with the same test name.
-  void addTestSuccess(absl::string_view test_name) {
-    auto normalized_test_name = absl::AsciiStrToLower(test_name);
-
-    // If the test is already reported as failing, do not add it to the list of
-    // successes.
-    if (test_failures_.find(normalized_test_name) == test_failures_.end()) {
-      test_successes_.insert(std::move(normalized_test_name));
-    }
-  }
-
-  // Streams a single error description into the internal buffer (a visual
-  // divider is automatically inserted after the error so that multiple errors
-  // are visibly distinct).
-  //
-  // This function increases the error count by 1.
-  //
-  // TODO(calabrese) Determine desired behavior when if this function throws.
-  template <class... P>
-  void addTestFailure(absl::string_view test_name, const P&... args) {
-    // Output a message related to the test failure.
-    assertion_result_ << "\n\n"
-                         "Failed test: "
-                      << test_name << "\n\n";
-    addTestFailureImpl(args...);
-    assertion_result_ << "\n\n";
-    outputDivider();
-
-    auto normalized_test_name = absl::AsciiStrToLower(test_name);
-
-    // If previous parts of this test succeeded, remove it from that set.
-    test_successes_.erase(normalized_test_name);
-
-    // Add the test name to the list of failed tests.
-    test_failures_.insert(std::move(normalized_test_name));
-
-    has_error_ = true;
-  }
-
-  // Convert this object into a testing::AssertionResult instance such that it
-  // can be used with gtest.
-  ::testing::AssertionResult assertionResult() const {
-    return has_error_ ? assertion_result_ : ::testing::AssertionSuccess();
-  }
-
-  // Convert this object into a testing::AssertionResult instance such that it
-  // can be used with gtest. This overload expects errors, using the specified
-  // matcher.
-  ::testing::AssertionResult expectFailedTests(
-      const std::set<std::string>& test_names) const {
-    // Since we are expecting nonconformance, output an error message when the
-    // type actually conformed to the specified profile.
-    if (!has_error_) {
-      return ::testing::AssertionFailure()
-             << "Unexpected conformance of type:\n"
-                "    "
-             << type_name_ << "\n\n";
-    }
-
-    // Get a list of all expected failures that did not actually fail
-    // (or that were not run).
-    std::vector<std::string> nonfailing_tests;
-    absl::c_set_difference(test_names, test_failures_,
-                           std::back_inserter(nonfailing_tests));
-
-    // Get a list of all "expected failures" that were never actually run.
-    std::vector<std::string> unrun_tests;
-    absl::c_set_difference(nonfailing_tests, test_successes_,
-                           std::back_inserter(unrun_tests));
-
-    // Report when the user specified tests that were not run.
-    if (!unrun_tests.empty()) {
-      const bool tests_were_run =
-          !(test_failures_.empty() && test_successes_.empty());
-
-      // Prepare an assertion result used in the case that tests pass that were
-      // expected to fail.
-      ::testing::AssertionResult result = ::testing::AssertionFailure();
-      result << "When testing type:\n    " << type_name_
-             << "\n\nThe following tests were expected to fail but were not "
-                "run";
-
-      if (tests_were_run) result << " (was the test name spelled correctly?)";
-
-      result << ":\n\n";
-
-      // List all of the tests that unexpectedly passed.
-      for (const auto& test_name : unrun_tests) {
-        result << "    " << test_name << "\n";
-      }
-
-      if (!tests_were_run) result << "\nNo tests were run.";
-
-      if (!test_failures_.empty()) {
-        // List test failures
-        result << "\nThe tests that were run and failed are:\n\n";
-        for (const auto& test_name : test_failures_) {
-          result << "    " << test_name << "\n";
-        }
-      }
-
-      if (!test_successes_.empty()) {
-        // List test successes
-        result << "\nThe tests that were run and succeeded are:\n\n";
-        for (const auto& test_name : test_successes_) {
-          result << "    " << test_name << "\n";
-        }
-      }
-
-      return result;
-    }
-
-    // If some tests passed when they were expected to fail, alert the caller.
-    if (nonfailing_tests.empty()) return ::testing::AssertionSuccess();
-
-    // Prepare an assertion result used in the case that tests pass that were
-    // expected to fail.
-    ::testing::AssertionResult unexpected_successes =
-        ::testing::AssertionFailure();
-    unexpected_successes << "When testing type:\n    " << type_name_
-                         << "\n\nThe following tests passed when they were "
-                            "expected to fail:\n\n";
-
-    // List all of the tests that unexpectedly passed.
-    for (const auto& test_name : nonfailing_tests) {
-      unexpected_successes << "    " << test_name << "\n";
-    }
-
-    return unexpected_successes;
-  }
-
- private:
-  void outputDivider() {
-    assertion_result_ << "========================================";
-  }
-
-  void addTestFailureImpl() {}
-
-  template <class H, class... T>
-  void addTestFailureImpl(const H& head, const T&... tail) {
-    assertion_result_ << head;
-    addTestFailureImpl(tail...);
-  }
-
-  ::testing::AssertionResult assertion_result_;
-  std::set<std::string> test_failures_;
-  std::set<std::string> test_successes_;
-  std::string type_name_;
-  bool has_error_ = false;
-};
-
-template <class T, class /*Enabler*/ = void>
-struct PropertiesOfImpl {};
-
-template <class T>
-struct PropertiesOfImpl<T, absl::void_t<typename T::properties>> {
-  using type = typename T::properties;
-};
-
-template <class T>
-struct PropertiesOfImpl<T, absl::void_t<typename T::profile_alias_of>> {
-  using type = typename PropertiesOfImpl<typename T::profile_alias_of>::type;
-};
-
-template <class T>
-struct PropertiesOf : PropertiesOfImpl<T> {};
-
-template <class T>
-using PropertiesOfT = typename PropertiesOf<T>::type;
-
-// NOTE: These enums use this naming convention to be consistent with the
-// standard trait names, which is useful since it allows us to match up each
-// enum name with a corresponding trait name in macro definitions.
-
-// An enum that describes the various expectations on an operations existence.
-enum class function_support { maybe, yes, nothrow, trivial };
-
-constexpr const char* PessimisticPropertyDescription(function_support v) {
-  return v == function_support::maybe
-             ? "no"
-             : v == function_support::yes
-                   ? "yes, potentially throwing"
-                   : v == function_support::nothrow ? "yes, nothrow"
-                                                    : "yes, trivial";
-}
-
-// Return a string that describes the kind of property support that was
-// expected.
-inline std::string ExpectedFunctionKindList(function_support min,
-                                            function_support max) {
-  if (min == max) {
-    std::string result =
-        absl::StrCat("Expected:\n  ",
-                     PessimisticPropertyDescription(
-                         static_cast<function_support>(UnderlyingValue(min))),
-                     "\n");
-    return result;
-  }
-
-  std::string result = "Expected one of:\n";
-  for (auto curr_support = UnderlyingValue(min);
-       curr_support <= UnderlyingValue(max); ++curr_support) {
-    absl::StrAppend(&result, "  ",
-                    PessimisticPropertyDescription(
-                        static_cast<function_support>(curr_support)),
-                    "\n");
-  }
-
-  return result;
-}
-
-template <class Enum>
-void ExpectModelOfImpl(ConformanceErrors* errors, Enum min_support,
-                       Enum max_support, Enum kind) {
-  const auto kind_value = UnderlyingValue(kind);
-  const auto min_support_value = UnderlyingValue(min_support);
-  const auto max_support_value = UnderlyingValue(max_support);
-
-  if (!(kind_value >= min_support_value && kind_value <= max_support_value)) {
-    errors->addTestFailure(
-        PropertyName(kind), "**Failed property expectation**\n\n",
-        ExpectedFunctionKindList(
-            static_cast<function_support>(min_support_value),
-            static_cast<function_support>(max_support_value)),
-        '\n', "Actual:\n  ",
-        PessimisticPropertyDescription(
-            static_cast<function_support>(kind_value)));
-  } else {
-    errors->addTestSuccess(PropertyName(kind));
-  }
-}
-
-#define ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(description, name) \
-  enum class name { maybe, yes, nothrow, trivial };                   \
-                                                                      \
-  constexpr const char* PropertyName(name v) { return description; }  \
-  static_assert(true, "")  // Force a semicolon when using this macro.
-
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for default construction",
-                                           default_constructible);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for move construction",
-                                           move_constructible);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for copy construction",
-                                           copy_constructible);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for move assignment",
-                                           move_assignable);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for copy assignment",
-                                           copy_assignable);
-ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for destruction",
-                                           destructible);
-
-#undef ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM
-
-#define ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(description, name)     \
-  enum class name { maybe, yes, nothrow };                           \
-                                                                     \
-  constexpr const char* PropertyName(name v) { return description; } \
-  static_assert(true, "")  // Force a semicolon when using this macro.
-
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for ==", equality_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for !=", inequality_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for <", less_than_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for <=", less_equal_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for >=",
-                                      greater_equal_comparable);
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for >", greater_than_comparable);
-
-ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for swap", swappable);
-
-#undef ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM
-
-enum class hashable { maybe, yes };
-
-constexpr const char* PropertyName(hashable v) {
-  return "support for std::hash";
-}
-
-template <class T>
-using AlwaysFalse = std::false_type;
-
-#define ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(name, property)   \
-  template <class T>                                                        \
-  constexpr property property##_support_of() {                              \
-    return std::is_##property<T>::value                                     \
-               ? std::is_nothrow_##property<T>::value                       \
-                     ? absl::is_trivially_##property<T>::value              \
-                           ? property::trivial                              \
-                           : property::nothrow                              \
-                     : property::yes                                        \
-               : property::maybe;                                           \
-  }                                                                         \
-                                                                            \
-  template <class T, class MinProf, class MaxProf>                          \
-  void ExpectModelOf##name(ConformanceErrors* errors) {                     \
-    (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::property##_support, \
-                        PropertiesOfT<MaxProf>::property##_support,         \
-                        property##_support_of<T>());                        \
-  }
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(DefaultConstructible,
-                                                  default_constructible);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(MoveConstructible,
-                                                  move_constructible);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(CopyConstructible,
-                                                  copy_constructible);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(MoveAssignable,
-                                                  move_assignable);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(CopyAssignable,
-                                                  copy_assignable);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(Destructible, destructible);
-
-#undef ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER
-
-void BoolFunction(bool) noexcept;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction for checking if an operation exists through SFINAE.
-//
-// `T` is the type to test and Op is an alias containing the expression to test.
-template <class T, template <class...> class Op, class = void>
-struct IsOpableImpl : std::false_type {};
-
-template <class T, template <class...> class Op>
-struct IsOpableImpl<T, Op, absl::void_t<Op<T>>> : std::true_type {};
-
-template <template <class...> class Op>
-struct IsOpable {
-  template <class T>
-  using apply = typename IsOpableImpl<T, Op>::type;
-};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction for checking if an operation exists and is also noexcept
-// through SFINAE and the noexcept operator.
-///
-// `T` is the type to test and Op is an alias containing the expression to test.
-template <class T, template <class...> class Op, class = void>
-struct IsNothrowOpableImpl : std::false_type {};
-
-template <class T, template <class...> class Op>
-struct IsNothrowOpableImpl<T, Op, absl::enable_if_t<Op<T>::value>>
-    : std::true_type {};
-
-template <template <class...> class Op>
-struct IsNothrowOpable {
-  template <class T>
-  using apply = typename IsNothrowOpableImpl<T, Op>::type;
-};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A macro that produces the necessary function for reporting what kind of
-// support a specific comparison operation has and a function for reporting an
-// error if a given type's support for that operation does not meet the expected
-// requirements.
-#define ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(name, property, op)      \
-  template <class T,                                                           \
-            class Result = std::integral_constant<                             \
-                bool, noexcept((BoolFunction)(std::declval<const T&>() op      \
-                                                  std::declval<const T&>()))>> \
-  using name = Result;                                                         \
-                                                                               \
-  template <class T>                                                           \
-  constexpr property property##_support_of() {                                 \
-    return IsOpable<name>::apply<T>::value                                     \
-               ? IsNothrowOpable<name>::apply<T>::value ? property::nothrow    \
-                                                        : property::yes        \
-               : property::maybe;                                              \
-  }                                                                            \
-                                                                               \
-  template <class T, class MinProf, class MaxProf>                             \
-  void ExpectModelOf##name(ConformanceErrors* errors) {                        \
-    (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::property##_support,    \
-                        PropertiesOfT<MaxProf>::property##_support,            \
-                        property##_support_of<T>());                           \
-  }
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Generate the necessary support-checking and error reporting functions for
-// each of the comparison operators.
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(EqualityComparable,
-                                              equality_comparable, ==);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(InequalityComparable,
-                                              inequality_comparable, !=);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(LessThanComparable,
-                                              less_than_comparable, <);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(LessEqualComparable,
-                                              less_equal_comparable, <=);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(GreaterEqualComparable,
-                                              greater_equal_comparable, >=);
-
-ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(GreaterThanComparable,
-                                              greater_than_comparable, >);
-
-#undef ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// The necessary support-checking and error-reporting functions for swap.
-template <class T>
-constexpr swappable swappable_support_of() {
-  return type_traits_internal::IsSwappable<T>::value
-             ? type_traits_internal::IsNothrowSwappable<T>::value
-                   ? swappable::nothrow
-                   : swappable::yes
-             : swappable::maybe;
-}
-
-template <class T, class MinProf, class MaxProf>
-void ExpectModelOfSwappable(ConformanceErrors* errors) {
-  (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::swappable_support,
-                      PropertiesOfT<MaxProf>::swappable_support,
-                      swappable_support_of<T>());
-}
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// The necessary support-checking and error-reporting functions for std::hash.
-template <class T>
-constexpr hashable hashable_support_of() {
-  return type_traits_internal::IsHashable<T>::value ? hashable::yes
-                                                    : hashable::maybe;
-}
-
-template <class T, class MinProf, class MaxProf>
-void ExpectModelOfHashable(ConformanceErrors* errors) {
-  (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::hashable_support,
-                      PropertiesOfT<MaxProf>::hashable_support,
-                      hashable_support_of<T>());
-}
-//
-////////////////////////////////////////////////////////////////////////////////
-
-template <
-    default_constructible DefaultConstructibleValue =
-        default_constructible::maybe,
-    move_constructible MoveConstructibleValue = move_constructible::maybe,
-    copy_constructible CopyConstructibleValue = copy_constructible::maybe,
-    move_assignable MoveAssignableValue = move_assignable::maybe,
-    copy_assignable CopyAssignableValue = copy_assignable::maybe,
-    destructible DestructibleValue = destructible::maybe,
-    equality_comparable EqualityComparableValue = equality_comparable::maybe,
-    inequality_comparable InequalityComparableValue =
-        inequality_comparable::maybe,
-    less_than_comparable LessThanComparableValue = less_than_comparable::maybe,
-    less_equal_comparable LessEqualComparableValue =
-        less_equal_comparable::maybe,
-    greater_equal_comparable GreaterEqualComparableValue =
-        greater_equal_comparable::maybe,
-    greater_than_comparable GreaterThanComparableValue =
-        greater_than_comparable::maybe,
-    swappable SwappableValue = swappable::maybe,
-    hashable HashableValue = hashable::maybe>
-struct ConformanceProfile {
-  using properties = ConformanceProfile;
-
-  static constexpr default_constructible
-      default_constructible_support =  // NOLINT
-      DefaultConstructibleValue;
-
-  static constexpr move_constructible move_constructible_support =  // NOLINT
-      MoveConstructibleValue;
-
-  static constexpr copy_constructible copy_constructible_support =  // NOLINT
-      CopyConstructibleValue;
-
-  static constexpr move_assignable move_assignable_support =  // NOLINT
-      MoveAssignableValue;
-
-  static constexpr copy_assignable copy_assignable_support =  // NOLINT
-      CopyAssignableValue;
-
-  static constexpr destructible destructible_support =  // NOLINT
-      DestructibleValue;
-
-  static constexpr equality_comparable equality_comparable_support =  // NOLINT
-      EqualityComparableValue;
-
-  static constexpr inequality_comparable
-      inequality_comparable_support =  // NOLINT
-      InequalityComparableValue;
-
-  static constexpr less_than_comparable
-      less_than_comparable_support =  // NOLINT
-      LessThanComparableValue;
-
-  static constexpr less_equal_comparable
-      less_equal_comparable_support =  // NOLINT
-      LessEqualComparableValue;
-
-  static constexpr greater_equal_comparable
-      greater_equal_comparable_support =  // NOLINT
-      GreaterEqualComparableValue;
-
-  static constexpr greater_than_comparable
-      greater_than_comparable_support =  // NOLINT
-      GreaterThanComparableValue;
-
-  static constexpr swappable swappable_support = SwappableValue;  // NOLINT
-
-  static constexpr hashable hashable_support = HashableValue;  // NOLINT
-
-  static constexpr bool is_default_constructible =  // NOLINT
-      DefaultConstructibleValue != default_constructible::maybe;
-
-  static constexpr bool is_move_constructible =  // NOLINT
-      MoveConstructibleValue != move_constructible::maybe;
-
-  static constexpr bool is_copy_constructible =  // NOLINT
-      CopyConstructibleValue != copy_constructible::maybe;
-
-  static constexpr bool is_move_assignable =  // NOLINT
-      MoveAssignableValue != move_assignable::maybe;
-
-  static constexpr bool is_copy_assignable =  // NOLINT
-      CopyAssignableValue != copy_assignable::maybe;
-
-  static constexpr bool is_destructible =  // NOLINT
-      DestructibleValue != destructible::maybe;
-
-  static constexpr bool is_equality_comparable =  // NOLINT
-      EqualityComparableValue != equality_comparable::maybe;
-
-  static constexpr bool is_inequality_comparable =  // NOLINT
-      InequalityComparableValue != inequality_comparable::maybe;
-
-  static constexpr bool is_less_than_comparable =  // NOLINT
-      LessThanComparableValue != less_than_comparable::maybe;
-
-  static constexpr bool is_less_equal_comparable =  // NOLINT
-      LessEqualComparableValue != less_equal_comparable::maybe;
-
-  static constexpr bool is_greater_equal_comparable =  // NOLINT
-      GreaterEqualComparableValue != greater_equal_comparable::maybe;
-
-  static constexpr bool is_greater_than_comparable =  // NOLINT
-      GreaterThanComparableValue != greater_than_comparable::maybe;
-
-  static constexpr bool is_swappable =  // NOLINT
-      SwappableValue != swappable::maybe;
-
-  static constexpr bool is_hashable =  // NOLINT
-      HashableValue != hashable::maybe;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Compliant SFINAE-friendliness is not always present on the standard library
-// implementations that we support. This helper-struct (and associated enum) is
-// used as a means to conditionally check the hashability support of a type.
-enum class CheckHashability { no, yes };
-
-template <class T, CheckHashability ShouldCheckHashability>
-struct conservative_hashable_support_of;
-
-template <class T>
-struct conservative_hashable_support_of<T, CheckHashability::no> {
-  static constexpr hashable Invoke() { return hashable::maybe; }
-};
-
-template <class T>
-struct conservative_hashable_support_of<T, CheckHashability::yes> {
-  static constexpr hashable Invoke() { return hashable_support_of<T>(); }
-};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// The ConformanceProfile that is expected based on introspection into the type
-// by way of trait checks.
-template <class T, CheckHashability ShouldCheckHashability>
-struct SyntacticConformanceProfileOf {
-  using properties = ConformanceProfile<
-      default_constructible_support_of<T>(), move_constructible_support_of<T>(),
-      copy_constructible_support_of<T>(), move_assignable_support_of<T>(),
-      copy_assignable_support_of<T>(), destructible_support_of<T>(),
-      equality_comparable_support_of<T>(),
-      inequality_comparable_support_of<T>(),
-      less_than_comparable_support_of<T>(),
-      less_equal_comparable_support_of<T>(),
-      greater_equal_comparable_support_of<T>(),
-      greater_than_comparable_support_of<T>(), swappable_support_of<T>(),
-      conservative_hashable_support_of<T, ShouldCheckHashability>::Invoke()>;
-};
-
-#define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type, name)     \
-  template <default_constructible DefaultConstructibleValue,                   \
-            move_constructible MoveConstructibleValue,                         \
-            copy_constructible CopyConstructibleValue,                         \
-            move_assignable MoveAssignableValue,                               \
-            copy_assignable CopyAssignableValue,                               \
-            destructible DestructibleValue,                                    \
-            equality_comparable EqualityComparableValue,                       \
-            inequality_comparable InequalityComparableValue,                   \
-            less_than_comparable LessThanComparableValue,                      \
-            less_equal_comparable LessEqualComparableValue,                    \
-            greater_equal_comparable GreaterEqualComparableValue,              \
-            greater_than_comparable GreaterThanComparableValue,                \
-            swappable SwappableValue, hashable HashableValue>                  \
-  constexpr type ConformanceProfile<                                           \
-      DefaultConstructibleValue, MoveConstructibleValue,                       \
-      CopyConstructibleValue, MoveAssignableValue, CopyAssignableValue,        \
-      DestructibleValue, EqualityComparableValue, InequalityComparableValue,   \
-      LessThanComparableValue, LessEqualComparableValue,                       \
-      GreaterEqualComparableValue, GreaterThanComparableValue, SwappableValue, \
-      HashableValue>::name
-
-#define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(type)           \
-  ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type,            \
-                                                         type##_support); \
-  ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(bool, is_##type)
-
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(default_constructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_constructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_constructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_assignable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_assignable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(destructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(equality_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(inequality_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_than_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_equal_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_equal_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_than_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(swappable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(hashable);
-
-#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF
-#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL
-
-// Retrieve the enum with the minimum underlying value.
-// Note: std::min is not constexpr in C++11, which is why this is necessary.
-template <class H>
-constexpr H MinEnum(H head) {
-  return head;
-}
-
-template <class H, class N, class... T>
-constexpr H MinEnum(H head, N next, T... tail) {
-  return (UnderlyingValue)(head) < (UnderlyingValue)(next)
-             ? (MinEnum)(head, tail...)
-             : (MinEnum)(next, tail...);
-}
-
-template <class... Profs>
-struct MinimalProfiles {
-  static constexpr default_constructible
-      default_constructible_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::default_constructible_support...);
-
-  static constexpr move_constructible move_constructible_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::move_constructible_support...);
-
-  static constexpr copy_constructible copy_constructible_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::copy_constructible_support...);
-
-  static constexpr move_assignable move_assignable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::move_assignable_support...);
-
-  static constexpr copy_assignable copy_assignable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::copy_assignable_support...);
-
-  static constexpr destructible destructible_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::destructible_support...);
-
-  static constexpr equality_comparable equality_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::equality_comparable_support...);
-
-  static constexpr inequality_comparable
-      inequality_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::inequality_comparable_support...);
-
-  static constexpr less_than_comparable
-      less_than_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::less_than_comparable_support...);
-
-  static constexpr less_equal_comparable
-      less_equal_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::less_equal_comparable_support...);
-
-  static constexpr greater_equal_comparable
-      greater_equal_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::greater_equal_comparable_support...);
-
-  static constexpr greater_than_comparable
-      greater_than_comparable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::greater_than_comparable_support...);
-
-  static constexpr swappable swappable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::swappable_support...);
-
-  static constexpr hashable hashable_support =  // NOLINT
-      (MinEnum)(PropertiesOfT<Profs>::hashable_support...);
-
-  using properties = ConformanceProfile<
-      default_constructible_support, move_constructible_support,
-      copy_constructible_support, move_assignable_support,
-      copy_assignable_support, destructible_support,
-      equality_comparable_support, inequality_comparable_support,
-      less_than_comparable_support, less_equal_comparable_support,
-      greater_equal_comparable_support, greater_than_comparable_support,
-      swappable_support, hashable_support>;
-};
-
-// Retrieve the enum with the greatest underlying value.
-// Note: std::max is not constexpr in C++11, which is why this is necessary.
-template <class H>
-constexpr H MaxEnum(H head) {
-  return head;
-}
-
-template <class H, class N, class... T>
-constexpr H MaxEnum(H head, N next, T... tail) {
-  return (UnderlyingValue)(next) < (UnderlyingValue)(head)
-             ? (MaxEnum)(head, tail...)
-             : (MaxEnum)(next, tail...);
-}
-
-template <class... Profs>
-struct CombineProfilesImpl {
-  static constexpr default_constructible
-      default_constructible_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::default_constructible_support...);
-
-  static constexpr move_constructible move_constructible_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::move_constructible_support...);
-
-  static constexpr copy_constructible copy_constructible_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::copy_constructible_support...);
-
-  static constexpr move_assignable move_assignable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::move_assignable_support...);
-
-  static constexpr copy_assignable copy_assignable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::copy_assignable_support...);
-
-  static constexpr destructible destructible_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::destructible_support...);
-
-  static constexpr equality_comparable equality_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::equality_comparable_support...);
-
-  static constexpr inequality_comparable
-      inequality_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::inequality_comparable_support...);
-
-  static constexpr less_than_comparable
-      less_than_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::less_than_comparable_support...);
-
-  static constexpr less_equal_comparable
-      less_equal_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::less_equal_comparable_support...);
-
-  static constexpr greater_equal_comparable
-      greater_equal_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::greater_equal_comparable_support...);
-
-  static constexpr greater_than_comparable
-      greater_than_comparable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::greater_than_comparable_support...);
-
-  static constexpr swappable swappable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::swappable_support...);
-
-  static constexpr hashable hashable_support =  // NOLINT
-      (MaxEnum)(PropertiesOfT<Profs>::hashable_support...);
-
-  using properties = ConformanceProfile<
-      default_constructible_support, move_constructible_support,
-      copy_constructible_support, move_assignable_support,
-      copy_assignable_support, destructible_support,
-      equality_comparable_support, inequality_comparable_support,
-      less_than_comparable_support, less_equal_comparable_support,
-      greater_equal_comparable_support, greater_than_comparable_support,
-      swappable_support, hashable_support>;
-};
-
-// NOTE: We use this as opposed to a direct alias of CombineProfilesImpl so that
-// when named aliases of CombineProfiles are created (such as in
-// conformance_aliases.h), we only pay for the combination algorithm on the
-// profiles that are actually used.
-template <class... Profs>
-struct CombineProfiles {
-  using profile_alias_of = CombineProfilesImpl<Profs...>;
-};
-
-template <>
-struct CombineProfiles<> {
-  using properties = ConformanceProfile<>;
-};
-
-template <class Profile, class Tag>
-struct StrongProfileTypedef {
-  using properties = PropertiesOfT<Profile>;
-};
-
-template <class T, class /*Enabler*/ = void>
-struct IsProfileImpl : std::false_type {};
-
-template <class T>
-struct IsProfileImpl<T, absl::void_t<PropertiesOfT<T>>> : std::true_type {};
-
-template <class T>
-struct IsProfile : IsProfileImpl<T>::type {};
-
-// A tag that describes which set of properties we will check when the user
-// requires a strict match in conformance (as opposed to a loose match which
-// allows more-refined support of any given operation).
-//
-// Currently only the RegularityDomain exists and it includes all operations
-// that the conformance testing suite knows about. The intent is that if the
-// suite is expanded to support extension, such as for checking conformance of
-// concepts like Iterators or Containers, additional corresponding domains can
-// be created.
-struct RegularityDomain {};
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_
diff --git a/third_party/abseil_cpp/absl/types/internal/conformance_testing.h b/third_party/abseil_cpp/absl/types/internal/conformance_testing.h
deleted file mode 100644
index 487b0f786b..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/conformance_testing.h
+++ /dev/null
@@ -1,1386 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// conformance_testing.h
-// -----------------------------------------------------------------------------
-//
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_
-
-////////////////////////////////////////////////////////////////////////////////
-//                                                                            //
-// Many templates in this file take a `T` and a `Prof` type as explicit       //
-// template arguments. These are a type to be checked and a                   //
-// "Regularity Profile" that describes what operations that type `T` is       //
-// expected to support. See "regularity_profiles.h" for more details          //
-// regarding Regularity Profiles.                                             //
-//                                                                            //
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cstddef>
-#include <set>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/internal/conformance_aliases.h"
-#include "absl/types/internal/conformance_archetype.h"
-#include "absl/types/internal/conformance_profile.h"
-#include "absl/types/internal/conformance_testing_helpers.h"
-#include "absl/types/internal/parentheses.h"
-#include "absl/types/internal/transform_args.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// Returns true if the compiler incorrectly greedily instantiates constexpr
-// templates in any unevaluated context.
-constexpr bool constexpr_instantiation_when_unevaluated() {
-#if defined(__apple_build_version__)  // TODO(calabrese) Make more specific
-  return true;
-#elif defined(__clang__)
-  return __clang_major__ < 4;
-#elif defined(__GNUC__)
-  // TODO(calabrese) Figure out why gcc 7 fails (seems like a different bug)
-  return __GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 2) || __GNUC__ >= 7;
-#else
-  return false;
-#endif
-}
-
-// Returns true if the standard library being used incorrectly produces an error
-// when instantiating the definition of a poisoned std::hash specialization.
-constexpr bool poisoned_hash_fails_instantiation() {
-#if defined(_MSC_VER) && !defined(_LIBCPP_VERSION)
-  return _MSC_VER < 1914;
-#else
-  return false;
-#endif
-}
-
-template <class Fun>
-struct GeneratorType {
-  decltype(std::declval<const Fun&>()()) operator()() const
-      noexcept(noexcept(std::declval<const Fun&>()())) {
-    return fun();
-  }
-
-  Fun fun;
-  const char* description;
-};
-
-// A "make" function for the GeneratorType template that deduces the function
-// object type.
-template <class Fun,
-          absl::enable_if_t<IsNullaryCallable<Fun>::value>** = nullptr>
-GeneratorType<Fun> Generator(Fun fun, const char* description) {
-  return GeneratorType<Fun>{absl::move(fun), description};
-}
-
-// A type that contains a set of nullary function objects that each return an
-// instance of the same type and value (though possibly different
-// representations, such as +0 and -0 or two vectors with the same elements but
-// with different capacities).
-template <class... Funs>
-struct EquivalenceClassType {
-  std::tuple<GeneratorType<Funs>...> generators;
-};
-
-// A "make" function for the EquivalenceClassType template that deduces the
-// function object types and is constrained such that a user can only pass in
-// function objects that all have the same return type.
-template <class... Funs, absl::enable_if_t<AreGeneratorsWithTheSameReturnType<
-                             Funs...>::value>** = nullptr>
-EquivalenceClassType<Funs...> EquivalenceClass(GeneratorType<Funs>... funs) {
-  return {std::make_tuple(absl::move(funs)...)};
-}
-
-// A type that contains an ordered series of EquivalenceClassTypes, from
-// smallest value to largest value.
-template <class... EqClasses>
-struct OrderedEquivalenceClasses {
-  std::tuple<EqClasses...> eq_classes;
-};
-
-// An object containing the parts of a given (name, initialization expression),
-// and is capable of generating a string that describes the given.
-struct GivenDeclaration {
-  std::string outputDeclaration(std::size_t width) const {
-    const std::size_t indent_size = 2;
-    std::string result = absl::StrCat("  ", name);
-
-    if (!expression.empty()) {
-      // Indent
-      result.resize(indent_size + width, ' ');
-      absl::StrAppend(&result, " = ", expression, ";\n");
-    } else {
-      absl::StrAppend(&result, ";\n");
-    }
-
-    return result;
-  }
-
-  std::string name;
-  std::string expression;
-};
-
-// Produce a string that contains all of the givens of an error report.
-template <class... Decls>
-std::string PrepareGivenContext(const Decls&... decls) {
-  const std::size_t width = (std::max)({decls.name.size()...});
-  return absl::StrCat("Given:\n", decls.outputDeclaration(width)..., "\n");
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Function objects that perform a check for each comparison operator         //
-////////////////////////////////////////////////////////////////////////////////
-
-#define ABSL_INTERNAL_EXPECT_OP(name, op)                                   \
-  struct Expect##name {                                                     \
-    template <class T>                                                      \
-    void operator()(absl::string_view test_name, absl::string_view context, \
-                    const T& lhs, const T& rhs, absl::string_view lhs_name, \
-                    absl::string_view rhs_name) const {                     \
-      if (!static_cast<bool>(lhs op rhs)) {                                 \
-        errors->addTestFailure(                                             \
-            test_name, absl::StrCat(context,                                \
-                                    "**Unexpected comparison result**\n"    \
-                                    "\n"                                    \
-                                    "Expression:\n"                         \
-                                    "  ",                                   \
-                                    lhs_name, " " #op " ", rhs_name,        \
-                                    "\n"                                    \
-                                    "\n"                                    \
-                                    "Expected: true\n"                      \
-                                    "  Actual: false"));                    \
-      } else {                                                              \
-        errors->addTestSuccess(test_name);                                  \
-      }                                                                     \
-    }                                                                       \
-                                                                            \
-    ConformanceErrors* errors;                                              \
-  };                                                                        \
-                                                                            \
-  struct ExpectNot##name {                                                  \
-    template <class T>                                                      \
-    void operator()(absl::string_view test_name, absl::string_view context, \
-                    const T& lhs, const T& rhs, absl::string_view lhs_name, \
-                    absl::string_view rhs_name) const {                     \
-      if (lhs op rhs) {                                                     \
-        errors->addTestFailure(                                             \
-            test_name, absl::StrCat(context,                                \
-                                    "**Unexpected comparison result**\n"    \
-                                    "\n"                                    \
-                                    "Expression:\n"                         \
-                                    "  ",                                   \
-                                    lhs_name, " " #op " ", rhs_name,        \
-                                    "\n"                                    \
-                                    "\n"                                    \
-                                    "Expected: false\n"                     \
-                                    "  Actual: true"));                     \
-      } else {                                                              \
-        errors->addTestSuccess(test_name);                                  \
-      }                                                                     \
-    }                                                                       \
-                                                                            \
-    ConformanceErrors* errors;                                              \
-  }
-
-ABSL_INTERNAL_EXPECT_OP(Eq, ==);
-ABSL_INTERNAL_EXPECT_OP(Ne, !=);
-ABSL_INTERNAL_EXPECT_OP(Lt, <);
-ABSL_INTERNAL_EXPECT_OP(Le, <=);
-ABSL_INTERNAL_EXPECT_OP(Ge, >=);
-ABSL_INTERNAL_EXPECT_OP(Gt, >);
-
-#undef ABSL_INTERNAL_EXPECT_OP
-
-// A function object that verifies that two objects hash to the same value by
-// way of the std::hash specialization.
-struct ExpectSameHash {
-  template <class T>
-  void operator()(absl::string_view test_name, absl::string_view context,
-                  const T& lhs, const T& rhs, absl::string_view lhs_name,
-                  absl::string_view rhs_name) const {
-    if (std::hash<T>()(lhs) != std::hash<T>()(rhs)) {
-      errors->addTestFailure(
-          test_name, absl::StrCat(context,
-                                  "**Unexpected hash result**\n"
-                                  "\n"
-                                  "Expression:\n"
-                                  "  std::hash<T>()(",
-                                  lhs_name, ") == std::hash<T>()(", rhs_name,
-                                  ")\n"
-                                  "\n"
-                                  "Expected: true\n"
-                                  "  Actual: false"));
-    } else {
-      errors->addTestSuccess(test_name);
-    }
-  }
-
-  ConformanceErrors* errors;
-};
-
-// A function template that takes two objects and verifies that each comparison
-// operator behaves in a way that is consistent with equality. It has "OneWay"
-// in the name because the first argument will always be the left-hand operand
-// of the corresponding comparison operator and the second argument will
-// always be the right-hand operand. It will never switch that order.
-// At a higher level in the test suite, the one-way form is called once for each
-// of the two possible orders whenever lhs and rhs are not the same initializer.
-template <class T, class Prof>
-void ExpectOneWayEquality(ConformanceErrors* errors,
-                          absl::string_view test_name,
-                          absl::string_view context, const T& lhs, const T& rhs,
-                          absl::string_view lhs_name,
-                          absl::string_view rhs_name) {
-  If<PropertiesOfT<Prof>::is_equality_comparable>::Invoke(
-      ExpectEq{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_inequality_comparable>::Invoke(
-      ExpectNotNe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_less_than_comparable>::Invoke(
-      ExpectNotLt{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_less_equal_comparable>::Invoke(
-      ExpectLe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_greater_equal_comparable>::Invoke(
-      ExpectGe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_greater_than_comparable>::Invoke(
-      ExpectNotGt{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-
-  If<PropertiesOfT<Prof>::is_hashable>::Invoke(
-      ExpectSameHash{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name);
-}
-
-// A function template that takes two objects and verifies that each comparison
-// operator behaves in a way that is consistent with equality. This function
-// differs from ExpectOneWayEquality in that this will do checks with argument
-// order reversed in addition to in-order.
-template <class T, class Prof>
-void ExpectEquality(ConformanceErrors* errors, absl::string_view test_name,
-                    absl::string_view context, const T& lhs, const T& rhs,
-                    absl::string_view lhs_name, absl::string_view rhs_name) {
-  (ExpectOneWayEquality<T, Prof>)(errors, test_name, context, lhs, rhs,
-                                  lhs_name, rhs_name);
-  (ExpectOneWayEquality<T, Prof>)(errors, test_name, context, rhs, lhs,
-                                  rhs_name, lhs_name);
-}
-
-// Given a generator, makes sure that a generated value and a moved-from
-// generated value are equal.
-template <class T, class Prof>
-struct ExpectMoveConstructOneGenerator {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T object = generator();
-    const T moved_object = absl::move(generator());  // Force no elision.
-
-    (ExpectEquality<T, Prof>)(errors, "Move construction",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T object",
-                                                   generator.description},
-                                  GivenDeclaration{"const _T moved_object",
-                                                   std::string("std::move(") +
-                                                       generator.description +
-                                                       ")"}),
-                              object, moved_object, "object", "moved_object");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Given a generator, makes sure that a generated value and a copied-from
-// generated value are equal.
-template <class T, class Prof>
-struct ExpectCopyConstructOneGenerator {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T object = generator();
-    const T copied_object = static_cast<const T&>(generator());
-
-    (ExpectEquality<T, Prof>)(errors, "Copy construction",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T object",
-                                                   generator.description},
-                                  GivenDeclaration{
-                                      "const _T copied_object",
-                                      std::string("static_cast<const _T&>(") +
-                                          generator.description + ")"}),
-                              object, copied_object, "object", "copied_object");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Default-construct and do nothing before destruction.
-//
-// This is useful in exercising the codepath of default construction followed by
-// destruction, but does not explicitly test anything. An example of where this
-// might fail is a default destructor that default-initializes a scalar and a
-// destructor reads the value of that member. Sanitizers can catch this as long
-// as our test attempts to execute such a case.
-template <class T>
-struct ExpectDefaultConstructWithDestruct {
-  void operator()() const {
-    // Scoped so that destructor gets called before reporting success.
-    {
-      T object;
-      static_cast<void>(object);
-    }
-
-    errors->addTestSuccess("Default construction");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check move-assign into a default-constructed object.
-template <class T, class Prof>
-struct ExpectDefaultConstructWithMoveAssign {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T source_of_truth = generator();
-    T object;
-    object = generator();
-
-    (ExpectEquality<T, Prof>)(errors, "Move assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T object",
-                                                   generator.description},
-                                  GivenDeclaration{"_T object", ""},
-                                  GivenDeclaration{"object",
-                                                   generator.description}),
-                              object, source_of_truth, "std::as_const(object)",
-                              "source_of_truth");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check copy-assign into a default-constructed object.
-template <class T, class Prof>
-struct ExpectDefaultConstructWithCopyAssign {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T source_of_truth = generator();
-    T object;
-    object = static_cast<const T&>(generator());
-
-    (ExpectEquality<T, Prof>)(errors, "Copy assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T source_of_truth",
-                                                   generator.description},
-                                  GivenDeclaration{"_T object", ""},
-                                  GivenDeclaration{
-                                      "object",
-                                      std::string("static_cast<const _T&>(") +
-                                          generator.description + ")"}),
-                              object, source_of_truth, "std::as_const(object)",
-                              "source_of_truth");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform a self move-assign.
-template <class T, class Prof>
-struct ExpectSelfMoveAssign {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    T object = generator();
-    object = absl::move(object);
-
-    // NOTE: Self move-assign results in a valid-but-unspecified state.
-
-    (ExpectEquality<T, Prof>)(errors, "Move assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"_T object",
-                                                   generator.description},
-                                  GivenDeclaration{"object",
-                                                   "std::move(object)"}),
-                              object, object, "object", "object");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform a self copy-assign.
-template <class T, class Prof>
-struct ExpectSelfCopyAssign {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T source_of_truth = generator();
-    T object = generator();
-    const T& const_object = object;
-    object = const_object;
-
-    (ExpectEquality<T, Prof>)(errors, "Copy assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T source_of_truth",
-                                                   generator.description},
-                                  GivenDeclaration{"_T object",
-                                                   generator.description},
-                                  GivenDeclaration{"object",
-                                                   "std::as_const(object)"}),
-                              const_object, source_of_truth,
-                              "std::as_const(object)", "source_of_truth");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform a self-swap.
-template <class T, class Prof>
-struct ExpectSelfSwap {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T source_of_truth = generator();
-    T object = generator();
-
-    type_traits_internal::Swap(object, object);
-
-    std::string preliminary_info = absl::StrCat(
-        PrepareGivenContext(
-            GivenDeclaration{"const _T source_of_truth", generator.description},
-            GivenDeclaration{"_T object", generator.description}),
-        "After performing a self-swap:\n"
-        "  using std::swap;\n"
-        "  swap(object, object);\n"
-        "\n");
-
-    (ExpectEquality<T, Prof>)(errors, "Swap", std::move(preliminary_info),
-                              object, source_of_truth, "std::as_const(object)",
-                              "source_of_truth");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform each of the single-generator checks when necessary operations are
-// supported.
-template <class T, class Prof>
-struct ExpectSelfComparison {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    const T object = generator();
-    (ExpectOneWayEquality<T, Prof>)(errors, "Comparison",
-                                    PrepareGivenContext(GivenDeclaration{
-                                        "const _T object",
-                                        generator.description}),
-                                    object, object, "object", "object");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Perform each of the single-generator checks when necessary operations are
-// supported.
-template <class T, class Prof>
-struct ExpectConsistency {
-  template <class Fun>
-  void operator()(const Fun& generator) const {
-    If<PropertiesOfT<Prof>::is_move_constructible>::Invoke(
-        ExpectMoveConstructOneGenerator<T, Prof>{errors}, generator);
-
-    If<PropertiesOfT<Prof>::is_copy_constructible>::Invoke(
-        ExpectCopyConstructOneGenerator<T, Prof>{errors}, generator);
-
-    If<PropertiesOfT<Prof>::is_default_constructible &&
-       PropertiesOfT<Prof>::is_move_assignable>::
-        Invoke(ExpectDefaultConstructWithMoveAssign<T, Prof>{errors},
-               generator);
-
-    If<PropertiesOfT<Prof>::is_default_constructible &&
-       PropertiesOfT<Prof>::is_copy_assignable>::
-        Invoke(ExpectDefaultConstructWithCopyAssign<T, Prof>{errors},
-               generator);
-
-    If<PropertiesOfT<Prof>::is_move_assignable>::Invoke(
-        ExpectSelfMoveAssign<T, Prof>{errors}, generator);
-
-    If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke(
-        ExpectSelfCopyAssign<T, Prof>{errors}, generator);
-
-    If<PropertiesOfT<Prof>::is_swappable>::Invoke(
-        ExpectSelfSwap<T, Prof>{errors}, generator);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check move-assign with two different values.
-template <class T, class Prof>
-struct ExpectMoveAssign {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    const T source_of_truth1 = generator1();
-    T object = generator0();
-    object = generator1();
-
-    (ExpectEquality<T, Prof>)(errors, "Move assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T source_of_truth1",
-                                                   generator1.description},
-                                  GivenDeclaration{"_T object",
-                                                   generator0.description},
-                                  GivenDeclaration{"object",
-                                                   generator1.description}),
-                              object, source_of_truth1, "std::as_const(object)",
-                              "source_of_truth1");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check copy-assign with two different values.
-template <class T, class Prof>
-struct ExpectCopyAssign {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    const T source_of_truth1 = generator1();
-    T object = generator0();
-    object = static_cast<const T&>(generator1());
-
-    (ExpectEquality<T, Prof>)(errors, "Copy assignment",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T source_of_truth1",
-                                                   generator1.description},
-                                  GivenDeclaration{"_T object",
-                                                   generator0.description},
-                                  GivenDeclaration{
-                                      "object",
-                                      std::string("static_cast<const _T&>(") +
-                                          generator1.description + ")"}),
-                              object, source_of_truth1, "std::as_const(object)",
-                              "source_of_truth1");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Check swap with two different values.
-template <class T, class Prof>
-struct ExpectSwap {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    const T source_of_truth0 = generator0();
-    const T source_of_truth1 = generator1();
-    T object0 = generator0();
-    T object1 = generator1();
-
-    type_traits_internal::Swap(object0, object1);
-
-    const std::string context =
-        PrepareGivenContext(
-            GivenDeclaration{"const _T source_of_truth0",
-                             generator0.description},
-            GivenDeclaration{"const _T source_of_truth1",
-                             generator1.description},
-            GivenDeclaration{"_T object0", generator0.description},
-            GivenDeclaration{"_T object1", generator1.description}) +
-        "After performing a swap:\n"
-        "  using std::swap;\n"
-        "  swap(object0, object1);\n"
-        "\n";
-
-    (ExpectEquality<T, Prof>)(errors, "Swap", context, object0,
-                              source_of_truth1, "std::as_const(object0)",
-                              "source_of_truth1");
-    (ExpectEquality<T, Prof>)(errors, "Swap", context, object1,
-                              source_of_truth0, "std::as_const(object1)",
-                              "source_of_truth0");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that `generator0` and `generator1` produce values that are equal.
-template <class T, class Prof>
-struct ExpectEquivalenceClassComparison {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    const T object0 = generator0();
-    const T object1 = generator1();
-
-    (ExpectEquality<T, Prof>)(errors, "Comparison",
-                              PrepareGivenContext(
-                                  GivenDeclaration{"const _T object0",
-                                                   generator0.description},
-                                  GivenDeclaration{"const _T object1",
-                                                   generator1.description}),
-                              object0, object1, "object0", "object1");
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that all objects in the same equivalence-class have the same value.
-template <class T, class Prof>
-struct ExpectEquivalenceClassConsistency {
-  template <class Fun0, class Fun1>
-  void operator()(const Fun0& generator0, const Fun1& generator1) const {
-    If<PropertiesOfT<Prof>::is_move_assignable>::Invoke(
-        ExpectMoveAssign<T, Prof>{errors}, generator0, generator1);
-
-    If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke(
-        ExpectCopyAssign<T, Prof>{errors}, generator0, generator1);
-
-    If<PropertiesOfT<Prof>::is_swappable>::Invoke(ExpectSwap<T, Prof>{errors},
-                                                  generator0, generator1);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Given a "lesser" object and a "greater" object, perform every combination of
-// comparison operators supported for the type, expecting consistent results.
-template <class T, class Prof>
-void ExpectOrdered(ConformanceErrors* errors, absl::string_view context,
-                   const T& small, const T& big, absl::string_view small_name,
-                   absl::string_view big_name) {
-  const absl::string_view test_name = "Comparison";
-
-  If<PropertiesOfT<Prof>::is_equality_comparable>::Invoke(
-      ExpectNotEq{errors}, test_name, context, small, big, small_name,
-      big_name);
-  If<PropertiesOfT<Prof>::is_equality_comparable>::Invoke(
-      ExpectNotEq{errors}, test_name, context, big, small, big_name,
-      small_name);
-
-  If<PropertiesOfT<Prof>::is_inequality_comparable>::Invoke(
-      ExpectNe{errors}, test_name, context, small, big, small_name, big_name);
-  If<PropertiesOfT<Prof>::is_inequality_comparable>::Invoke(
-      ExpectNe{errors}, test_name, context, big, small, big_name, small_name);
-
-  If<PropertiesOfT<Prof>::is_less_than_comparable>::Invoke(
-      ExpectLt{errors}, test_name, context, small, big, small_name, big_name);
-  If<PropertiesOfT<Prof>::is_less_than_comparable>::Invoke(
-      ExpectNotLt{errors}, test_name, context, big, small, big_name,
-      small_name);
-
-  If<PropertiesOfT<Prof>::is_less_equal_comparable>::Invoke(
-      ExpectLe{errors}, test_name, context, small, big, small_name, big_name);
-  If<PropertiesOfT<Prof>::is_less_equal_comparable>::Invoke(
-      ExpectNotLe{errors}, test_name, context, big, small, big_name,
-      small_name);
-
-  If<PropertiesOfT<Prof>::is_greater_equal_comparable>::Invoke(
-      ExpectNotGe{errors}, test_name, context, small, big, small_name,
-      big_name);
-  If<PropertiesOfT<Prof>::is_greater_equal_comparable>::Invoke(
-      ExpectGe{errors}, test_name, context, big, small, big_name, small_name);
-
-  If<PropertiesOfT<Prof>::is_greater_than_comparable>::Invoke(
-      ExpectNotGt{errors}, test_name, context, small, big, small_name,
-      big_name);
-  If<PropertiesOfT<Prof>::is_greater_than_comparable>::Invoke(
-      ExpectGt{errors}, test_name, context, big, small, big_name, small_name);
-}
-
-// For every two elements of an equivalence class, makes sure that those two
-// elements compare equal, including checks with the same argument passed as
-// both operands.
-template <class T, class Prof>
-struct ExpectEquivalenceClassComparisons {
-  template <class... Funs>
-  void operator()(EquivalenceClassType<Funs...> eq_class) const {
-    (ForEachTupleElement)(ExpectSelfComparison<T, Prof>{errors},
-                          eq_class.generators);
-
-    (ForEveryTwo)(ExpectEquivalenceClassComparison<T, Prof>{errors},
-                  eq_class.generators);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// For every element of an equivalence class, makes sure that the element is
-// self-consistent (in other words, if any of move/copy/swap are defined,
-// perform those operations and make such that results and operands still
-// compare equal to known values whenever it is required for that operation.
-template <class T, class Prof>
-struct ExpectEquivalenceClass {
-  template <class... Funs>
-  void operator()(EquivalenceClassType<Funs...> eq_class) const {
-    (ForEachTupleElement)(ExpectConsistency<T, Prof>{errors},
-                          eq_class.generators);
-
-    (ForEveryTwo)(ExpectEquivalenceClassConsistency<T, Prof>{errors},
-                  eq_class.generators);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that the passed-in argument is a generator of a greater value than
-// the one produced by the "small_gen" datamember with respect to all of the
-// comparison operators that Prof requires, with both argument orders to test.
-template <class T, class Prof, class SmallGenerator>
-struct ExpectBiggerGeneratorThanComparisons {
-  template <class BigGenerator>
-  void operator()(BigGenerator big_gen) const {
-    const T small = small_gen();
-    const T big = big_gen();
-
-    (ExpectOrdered<T, Prof>)(errors,
-                             PrepareGivenContext(
-                                 GivenDeclaration{"const _T small",
-                                                  small_gen.description},
-                                 GivenDeclaration{"const _T big",
-                                                  big_gen.description}),
-                             small, big, "small", "big");
-  }
-
-  SmallGenerator small_gen;
-  ConformanceErrors* errors;
-};
-
-// Perform all of the move, copy, and swap checks on the value generated by
-// `small_gen` and the value generated by `big_gen`.
-template <class T, class Prof, class SmallGenerator>
-struct ExpectBiggerGeneratorThan {
-  template <class BigGenerator>
-  void operator()(BigGenerator big_gen) const {
-    If<PropertiesOfT<Prof>::is_move_assignable>::Invoke(
-        ExpectMoveAssign<T, Prof>{errors}, small_gen, big_gen);
-    If<PropertiesOfT<Prof>::is_move_assignable>::Invoke(
-        ExpectMoveAssign<T, Prof>{errors}, big_gen, small_gen);
-
-    If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke(
-        ExpectCopyAssign<T, Prof>{errors}, small_gen, big_gen);
-    If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke(
-        ExpectCopyAssign<T, Prof>{errors}, big_gen, small_gen);
-
-    If<PropertiesOfT<Prof>::is_swappable>::Invoke(ExpectSwap<T, Prof>{errors},
-                                                  small_gen, big_gen);
-  }
-
-  SmallGenerator small_gen;
-  ConformanceErrors* errors;
-};
-
-// Validate that the result of a generator is greater than the results of all
-// generators in an equivalence class with respect to comparisons.
-template <class T, class Prof, class SmallGenerator>
-struct ExpectBiggerGeneratorThanEqClassesComparisons {
-  template <class BigEqClass>
-  void operator()(BigEqClass big_eq_class) const {
-    (ForEachTupleElement)(
-        ExpectBiggerGeneratorThanComparisons<T, Prof, SmallGenerator>{small_gen,
-                                                                      errors},
-        big_eq_class.generators);
-  }
-
-  SmallGenerator small_gen;
-  ConformanceErrors* errors;
-};
-
-// Validate that the non-comparison binary operations required by Prof are
-// correct for the result of each generator of big_eq_class and a generator of
-// the logically smaller value returned by small_gen.
-template <class T, class Prof, class SmallGenerator>
-struct ExpectBiggerGeneratorThanEqClasses {
-  template <class BigEqClass>
-  void operator()(BigEqClass big_eq_class) const {
-    (ForEachTupleElement)(
-        ExpectBiggerGeneratorThan<T, Prof, SmallGenerator>{small_gen, errors},
-        big_eq_class.generators);
-  }
-
-  SmallGenerator small_gen;
-  ConformanceErrors* errors;
-};
-
-// Validate that each equivalence class that is passed is logically less than
-// the equivalence classes that comes later on in the argument list.
-template <class T, class Prof>
-struct ExpectOrderedEquivalenceClassesComparisons {
-  template <class... BigEqClasses>
-  struct Impl {
-    // Validate that the value produced by `small_gen` is less than all of the
-    // values generated by those of the logically larger equivalence classes.
-    template <class SmallGenerator>
-    void operator()(SmallGenerator small_gen) const {
-      (ForEachTupleElement)(ExpectBiggerGeneratorThanEqClassesComparisons<
-                                T, Prof, SmallGenerator>{small_gen, errors},
-                            big_eq_classes);
-    }
-
-    std::tuple<BigEqClasses...> big_eq_classes;
-    ConformanceErrors* errors;
-  };
-
-  // When given no equivalence classes, no validation is necessary.
-  void operator()() const {}
-
-  template <class SmallEqClass, class... BigEqClasses>
-  void operator()(SmallEqClass small_eq_class,
-                  BigEqClasses... big_eq_classes) const {
-    // For each generator in the first equivalence class, make sure that it is
-    // less than each of those in the logically greater equivalence classes.
-    (ForEachTupleElement)(
-        Impl<BigEqClasses...>{std::make_tuple(absl::move(big_eq_classes)...),
-                              errors},
-        small_eq_class.generators);
-
-    // Recurse so that all equivalence class combinations are checked.
-    (*this)(absl::move(big_eq_classes)...);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that the non-comparison binary operations required by Prof are
-// correct for the result of each generator of big_eq_classes and a generator of
-// the logically smaller value returned by small_gen.
-template <class T, class Prof>
-struct ExpectOrderedEquivalenceClasses {
-  template <class... BigEqClasses>
-  struct Impl {
-    template <class SmallGenerator>
-    void operator()(SmallGenerator small_gen) const {
-      (ForEachTupleElement)(
-          ExpectBiggerGeneratorThanEqClasses<T, Prof, SmallGenerator>{small_gen,
-                                                                      errors},
-          big_eq_classes);
-    }
-
-    std::tuple<BigEqClasses...> big_eq_classes;
-    ConformanceErrors* errors;
-  };
-
-  // Check that small_eq_class is logically consistent and also is logically
-  // less than all values in big_eq_classes.
-  template <class SmallEqClass, class... BigEqClasses>
-  void operator()(SmallEqClass small_eq_class,
-                  BigEqClasses... big_eq_classes) const {
-    (ForEachTupleElement)(
-        Impl<BigEqClasses...>{std::make_tuple(absl::move(big_eq_classes)...),
-                              errors},
-        small_eq_class.generators);
-
-    (*this)(absl::move(big_eq_classes)...);
-  }
-
-  // Terminating case of operator().
-  void operator()() const {}
-
-  ConformanceErrors* errors;
-};
-
-// Validate that a type meets the syntactic requirements of std::hash if the
-// range of profiles requires it.
-template <class T, class MinProf, class MaxProf>
-struct ExpectHashable {
-  void operator()() const {
-    ExpectModelOfHashable<T, MinProf, MaxProf>(errors);
-  }
-
-  ConformanceErrors* errors;
-};
-
-// Validate that the type `T` meets all of the requirements associated with
-// `MinProf` and without going beyond the syntactic properties of `MaxProf`.
-template <class T, class MinProf, class MaxProf>
-struct ExpectModels {
-  void operator()(ConformanceErrors* errors) const {
-    ExpectModelOfDefaultConstructible<T, MinProf, MaxProf>(errors);
-    ExpectModelOfMoveConstructible<T, MinProf, MaxProf>(errors);
-    ExpectModelOfCopyConstructible<T, MinProf, MaxProf>(errors);
-    ExpectModelOfMoveAssignable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfCopyAssignable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfDestructible<T, MinProf, MaxProf>(errors);
-    ExpectModelOfEqualityComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfInequalityComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfLessThanComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfLessEqualComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfGreaterEqualComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfGreaterThanComparable<T, MinProf, MaxProf>(errors);
-    ExpectModelOfSwappable<T, MinProf, MaxProf>(errors);
-
-    // Only check hashability on compilers that have a compliant default-hash.
-    If<!poisoned_hash_fails_instantiation()>::Invoke(
-        ExpectHashable<T, MinProf, MaxProf>{errors});
-  }
-};
-
-// A metafunction that yields a Profile matching the set of properties that are
-// safe to be checked (lack-of-hashability is only checked on standard library
-// implementations that are standards compliant in that they provide a std::hash
-// primary template that is SFINAE-friendly)
-template <class LogicalProf, class T>
-struct MinimalCheckableProfile {
-  using type =
-      MinimalProfiles<PropertiesOfT<LogicalProf>,
-                      PropertiesOfT<SyntacticConformanceProfileOf<
-                          T, !PropertiesOfT<LogicalProf>::is_hashable &&
-                                     poisoned_hash_fails_instantiation()
-                                 ? CheckHashability::no
-                                 : CheckHashability::yes>>>;
-};
-
-// An identity metafunction
-template <class T>
-struct Always {
-  using type = T;
-};
-
-// Validate the T meets all of the necessary requirements of LogicalProf, with
-// syntactic requirements defined by the profile range [MinProf, MaxProf].
-template <class T, class LogicalProf, class MinProf, class MaxProf,
-          class... EqClasses>
-ConformanceErrors ExpectRegularityImpl(
-    OrderedEquivalenceClasses<EqClasses...> vals) {
-  ConformanceErrors errors((NameOf<T>()));
-
-  If<!constexpr_instantiation_when_unevaluated()>::Invoke(
-      ExpectModels<T, MinProf, MaxProf>(), &errors);
-
-  using minimal_profile = typename absl::conditional_t<
-      constexpr_instantiation_when_unevaluated(), Always<LogicalProf>,
-      MinimalCheckableProfile<LogicalProf, T>>::type;
-
-  If<PropertiesOfT<minimal_profile>::is_default_constructible>::Invoke(
-      ExpectDefaultConstructWithDestruct<T>{&errors});
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Perform all comparison checks first, since later checks depend on their
-  // correctness.
-  //
-  // Check all of the comparisons for all values in the same equivalence
-  // class (equal with respect to comparison operators and hash the same).
-  (ForEachTupleElement)(
-      ExpectEquivalenceClassComparisons<T, minimal_profile>{&errors},
-      vals.eq_classes);
-
-  // Check all of the comparisons for each combination of values that are in
-  // different equivalence classes (not equal with respect to comparison
-  // operators).
-  absl::apply(
-      ExpectOrderedEquivalenceClassesComparisons<T, minimal_profile>{&errors},
-      vals.eq_classes);
-  //
-  //////////////////////////////////////////////////////////////////////////////
-
-  // Perform remaining checks, relying on comparisons.
-  // TODO(calabrese) short circuit if any comparisons above failed.
-  (ForEachTupleElement)(ExpectEquivalenceClass<T, minimal_profile>{&errors},
-                        vals.eq_classes);
-
-  absl::apply(ExpectOrderedEquivalenceClasses<T, minimal_profile>{&errors},
-              vals.eq_classes);
-
-  return errors;
-}
-
-// A type that represents a range of profiles that are acceptable to be matched.
-//
-// `MinProf` is the minimum set of syntactic requirements that must be met.
-//
-// `MaxProf` is the maximum set of syntactic requirements that must be met.
-// This maximum is particularly useful for certain "strictness" checking. Some
-// examples for when this is useful:
-//
-// * Making sure that a type is move-only (rather than simply movable)
-//
-// * Making sure that a member function is *not* noexcept in cases where it
-//   cannot be noexcept, such as if a dependent datamember has certain
-//   operations that are not noexcept.
-//
-// * Making sure that a type tightly matches a spec, such as the standard.
-//
-// `LogicalProf` is the Profile for which run-time testing is to take place.
-//
-// Note: The reason for `LogicalProf` is because it is often the case, when
-// dealing with templates, that a declaration of a given operation is specified,
-// but whose body would fail to instantiate. Examples include the
-// copy-constructor of a standard container when the element-type is move-only,
-// or the comparison operators of a standard container when the element-type
-// does not have the necessary comparison operations defined. The `LogicalProf`
-// parameter allows us to capture the intent of what should be tested at
-// run-time, even in the cases where syntactically it might otherwise appear as
-// though the type undergoing testing supports more than it actually does.
-template <class LogicalProf, class MinProf = LogicalProf,
-          class MaxProf = MinProf>
-struct ProfileRange {
-  using logical_profile = LogicalProf;
-  using min_profile = MinProf;
-  using max_profile = MaxProf;
-};
-
-// Similar to ProfileRange except that it creates a profile range that is
-// coupled with a Domain and is used when testing that a type matches exactly
-// the "minimum" requirements of LogicalProf.
-template <class StrictnessDomain, class LogicalProf,
-          class MinProf = LogicalProf, class MaxProf = MinProf>
-struct StrictProfileRange {
-  // We do not yet support extension.
-  static_assert(
-      std::is_same<StrictnessDomain, RegularityDomain>::value,
-      "Currently, the only valid StrictnessDomain is RegularityDomain.");
-  using strictness_domain = StrictnessDomain;
-  using logical_profile = LogicalProf;
-  using min_profile = MinProf;
-  using max_profile = MaxProf;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction that creates a StrictProfileRange from a Domain and either a
-// Profile or ProfileRange.
-template <class StrictnessDomain, class ProfOrRange>
-struct MakeStrictProfileRange;
-
-template <class StrictnessDomain, class LogicalProf>
-struct MakeStrictProfileRange {
-  using type = StrictProfileRange<StrictnessDomain, LogicalProf>;
-};
-
-template <class StrictnessDomain, class LogicalProf, class MinProf,
-          class MaxProf>
-struct MakeStrictProfileRange<StrictnessDomain,
-                              ProfileRange<LogicalProf, MinProf, MaxProf>> {
-  using type =
-      StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>;
-};
-
-template <class StrictnessDomain, class ProfOrRange>
-using MakeStrictProfileRangeT =
-    typename MakeStrictProfileRange<StrictnessDomain, ProfOrRange>::type;
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// A profile in the RegularityDomain with the strongest possible requirements.
-using MostStrictProfile =
-    CombineProfiles<TriviallyCompleteProfile, NothrowComparableProfile>;
-
-// Forms a ProfileRange that treats the Profile as the bare minimum requirements
-// of a type.
-template <class LogicalProf, class MinProf = LogicalProf>
-using LooseProfileRange = StrictProfileRange<RegularityDomain, LogicalProf,
-                                             MinProf, MostStrictProfile>;
-
-template <class Prof>
-using MakeLooseProfileRangeT = Prof;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// The following classes implement the metafunction ProfileRangeOfT<T> that
-// takes either a Profile or ProfileRange and yields the ProfileRange to be
-// used during testing.
-//
-template <class T, class /*Enabler*/ = void>
-struct ProfileRangeOfImpl;
-
-template <class T>
-struct ProfileRangeOfImpl<T, absl::void_t<PropertiesOfT<T>>> {
-  using type = LooseProfileRange<T>;
-};
-
-template <class T>
-struct ProfileRangeOf : ProfileRangeOfImpl<T> {};
-
-template <class StrictnessDomain, class LogicalProf, class MinProf,
-          class MaxProf>
-struct ProfileRangeOf<
-    StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>> {
-  using type =
-      StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>;
-};
-
-template <class T>
-using ProfileRangeOfT = typename ProfileRangeOf<T>::type;
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// Extract the logical profile of a range (what will be runtime tested).
-template <class T>
-using LogicalProfileOfT = typename ProfileRangeOfT<T>::logical_profile;
-
-// Extract the minimal syntactic profile of a range (error if not at least).
-template <class T>
-using MinProfileOfT = typename ProfileRangeOfT<T>::min_profile;
-
-// Extract the maximum syntactic profile of a range (error if more than).
-template <class T>
-using MaxProfileOfT = typename ProfileRangeOfT<T>::max_profile;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-template <class T>
-struct IsProfileOrProfileRange : IsProfile<T>::type {};
-
-template <class StrictnessDomain, class LogicalProf, class MinProf,
-          class MaxProf>
-struct IsProfileOrProfileRange<
-    StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>>
-    : std::true_type {};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// TODO(calabrese): Consider naming the functions in this class the same as
-// the macros (defined later on) so that auto-complete leads to the correct name
-// and so that a user cannot accidentally call a function rather than the macro
-// form.
-template <bool ExpectSuccess, class T, class... EqClasses>
-struct ExpectConformanceOf {
-  // Add a value to be tested. Subsequent calls to this function on the same
-  // object must specify logically "larger" values with respect to the
-  // comparison operators of the type, if any.
-  //
-  // NOTE: This function should not be called directly. A stateless lambda is
-  // implicitly formed and passed when using the INITIALIZER macro at the bottom
-  // of this file.
-  template <class Fun,
-            absl::enable_if_t<std::is_same<
-                ResultOfGeneratorT<GeneratorType<Fun>>, T>::value>** = nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...,
-                                           EquivalenceClassType<Fun>>
-  initializer(GeneratorType<Fun> fun) && {
-    return {
-        {std::tuple_cat(absl::move(ordered_vals.eq_classes),
-                        std::make_tuple((EquivalenceClass)(absl::move(fun))))},
-        std::move(expected_failed_tests)};
-  }
-
-  template <class... TestNames,
-            absl::enable_if_t<!ExpectSuccess && sizeof...(EqClasses) == 0 &&
-                              absl::conjunction<std::is_convertible<
-                                  TestNames, absl::string_view>...>::value>** =
-                nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...>
-  due_to(TestNames&&... test_names) && {
-    (InsertEach)(&expected_failed_tests,
-                 absl::AsciiStrToLower(absl::string_view(test_names))...);
-
-    return {absl::move(ordered_vals), std::move(expected_failed_tests)};
-  }
-
-  template <class... TestNames, int = 0,  // MSVC disambiguator
-            absl::enable_if_t<ExpectSuccess && sizeof...(EqClasses) == 0 &&
-                              absl::conjunction<std::is_convertible<
-                                  TestNames, absl::string_view>...>::value>** =
-                nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...>
-  due_to(TestNames&&... test_names) && {
-    // TODO(calabrese) Instead have DUE_TO only exist via a CRTP base.
-    // This would produce better errors messages than the static_assert.
-    static_assert(!ExpectSuccess,
-                  "DUE_TO cannot be called when conformance is expected -- did "
-                  "you mean to use ASSERT_NONCONFORMANCE_OF?");
-  }
-
-  // Add a value to be tested. Subsequent calls to this function on the same
-  // object must specify logically "larger" values with respect to the
-  // comparison operators of the type, if any.
-  //
-  // NOTE: This function should not be called directly. A stateful lambda is
-  // implicitly formed and passed when using the INITIALIZER macro at the bottom
-  // of this file.
-  template <class Fun,
-            absl::enable_if_t<std::is_same<
-                ResultOfGeneratorT<GeneratorType<Fun>>, T>::value>** = nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...,
-                                           EquivalenceClassType<Fun>>
-  dont_class_directly_stateful_initializer(GeneratorType<Fun> fun) && {
-    return {
-        {std::tuple_cat(absl::move(ordered_vals.eq_classes),
-                        std::make_tuple((EquivalenceClass)(absl::move(fun))))},
-        std::move(expected_failed_tests)};
-  }
-
-  // Add a set of value to be tested, where each value is equal with respect to
-  // the comparison operators and std::hash specialization, if defined.
-  template <
-      class... Funs,
-      absl::void_t<absl::enable_if_t<std::is_same<
-          ResultOfGeneratorT<GeneratorType<Funs>>, T>::value>...>** = nullptr>
-  ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...,
-                                           EquivalenceClassType<Funs...>>
-  equivalence_class(GeneratorType<Funs>... funs) && {
-    return {{std::tuple_cat(
-                absl::move(ordered_vals.eq_classes),
-                std::make_tuple((EquivalenceClass)(absl::move(funs)...)))},
-            std::move(expected_failed_tests)};
-  }
-
-  // Execute the tests for the captured set of values, strictly matching a range
-  // of expected profiles in a given domain.
-  template <
-      class ProfRange,
-      absl::enable_if_t<IsProfileOrProfileRange<ProfRange>::value>** = nullptr>
-  ABSL_MUST_USE_RESULT ::testing::AssertionResult with_strict_profile(
-      ProfRange /*profile*/) {
-    ConformanceErrors test_result =
-        (ExpectRegularityImpl<
-            T, LogicalProfileOfT<ProfRange>, MinProfileOfT<ProfRange>,
-            MaxProfileOfT<ProfRange>>)(absl::move(ordered_vals));
-
-    return ExpectSuccess ? test_result.assertionResult()
-                         : test_result.expectFailedTests(expected_failed_tests);
-  }
-
-  // Execute the tests for the captured set of values, loosely matching a range
-  // of expected profiles (loose in that an interface is allowed to be more
-  // refined that a profile suggests, such as a type having a noexcept copy
-  // constructor when all that is required is that the copy constructor exists).
-  template <class Prof, absl::enable_if_t<IsProfile<Prof>::value>** = nullptr>
-  ABSL_MUST_USE_RESULT ::testing::AssertionResult with_loose_profile(
-      Prof /*profile*/) {
-    ConformanceErrors test_result =
-        (ExpectRegularityImpl<
-            T, Prof, Prof,
-            CombineProfiles<TriviallyCompleteProfile,
-                            NothrowComparableProfile>>)(absl::
-                                                            move(ordered_vals));
-
-    return ExpectSuccess ? test_result.assertionResult()
-                         : test_result.expectFailedTests(expected_failed_tests);
-  }
-
-  OrderedEquivalenceClasses<EqClasses...> ordered_vals;
-  std::set<std::string> expected_failed_tests;
-};
-
-template <class T>
-using ExpectConformanceOfType = ExpectConformanceOf</*ExpectSuccess=*/true, T>;
-
-template <class T>
-using ExpectNonconformanceOfType =
-    ExpectConformanceOf</*ExpectSuccess=*/false, T>;
-
-struct EquivalenceClassMaker {
-  // TODO(calabrese) Constrain to callable
-  template <class Fun>
-  static GeneratorType<Fun> initializer(GeneratorType<Fun> fun) {
-    return fun;
-  }
-};
-
-// A top-level macro that begins the builder pattern.
-//
-// The argument here takes the datatype to be tested.
-#define ABSL_INTERNAL_ASSERT_CONFORMANCE_OF(...)                            \
-  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                             \
-  if ABSL_INTERNAL_LPAREN                                                   \
-  const ::testing::AssertionResult gtest_ar =                               \
-      ABSL_INTERNAL_LPAREN ::absl::types_internal::ExpectConformanceOfType< \
-          __VA_ARGS__>()
-
-// Akin to ASSERT_CONFORMANCE_OF except that it expects failure and tries to
-// match text.
-#define ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(...)                            \
-  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                                \
-  if ABSL_INTERNAL_LPAREN                                                      \
-  const ::testing::AssertionResult gtest_ar =                                  \
-      ABSL_INTERNAL_LPAREN ::absl::types_internal::ExpectNonconformanceOfType< \
-          __VA_ARGS__>()
-
-////////////////////////////////////////////////////////////////////////////////
-// NOTE: The following macros look like they are recursive, but are not (macros
-// cannot recurse). These actually refer to member functions of the same name.
-// This is done intentionally so that a user cannot accidentally invoke a
-// member function of the conformance-testing suite without going through the
-// macro.
-////////////////////////////////////////////////////////////////////////////////
-
-// Specify expected test failures as comma-separated strings.
-#define DUE_TO(...) due_to(__VA_ARGS__)
-
-// Specify a value to be tested.
-//
-// Note: Internally, this takes an expression and turns it into the return value
-// of lambda that captures no data. The expression is stringized during
-// preprocessing so that it can be used in error reports.
-#define INITIALIZER(...)                         \
-  initializer(::absl::types_internal::Generator( \
-      [] { return __VA_ARGS__; }, ABSL_INTERNAL_STRINGIZE(__VA_ARGS__)))
-
-// Specify a value to be tested.
-//
-// Note: Internally, this takes an expression and turns it into the return value
-// of lambda that captures data by reference. The expression is stringized
-// during preprocessing so that it can be used in error reports.
-#define STATEFUL_INITIALIZER(...)                         \
-  stateful_initializer(::absl::types_internal::Generator( \
-      [&] { return __VA_ARGS__; }, ABSL_INTERNAL_STRINGIZE(__VA_ARGS__)))
-
-// Used in the builder-pattern.
-//
-// Takes a series of INITIALIZER and/or STATEFUL_INITIALIZER invocations and
-// forwards them along to be tested, grouping them such that the testing suite
-// knows that they are supposed to represent the same logical value (the values
-// compare the same, hash the same, etc.).
-#define EQUIVALENCE_CLASS(...)                    \
-  equivalence_class(ABSL_INTERNAL_TRANSFORM_ARGS( \
-      ABSL_INTERNAL_PREPEND_EQ_MAKER, __VA_ARGS__))
-
-// An invocation of this or WITH_STRICT_PROFILE must end the builder-pattern.
-// It takes a Profile as its argument.
-//
-// This executes the tests and allows types that are "more referined" than the
-// profile specifies, but not less. For instance, if the Profile specifies
-// noexcept copy-constructiblity, the test will fail if the copy-constructor is
-// not noexcept, however, it will succeed if the copy constructor is trivial.
-//
-// This is useful for testing that a type meets some minimum set of
-// requirements.
-#define WITH_LOOSE_PROFILE(...)                                      \
-  with_loose_profile(                                                \
-      ::absl::types_internal::MakeLooseProfileRangeT<__VA_ARGS__>()) \
-      ABSL_INTERNAL_RPAREN ABSL_INTERNAL_RPAREN;                     \
-  else GTEST_FATAL_FAILURE_(gtest_ar.failure_message())  // NOLINT
-
-// An invocation of this or WITH_STRICT_PROFILE must end the builder-pattern.
-// It takes a Domain and a Profile as its arguments.
-//
-// This executes the tests and disallows types that differ at all from the
-// properties of the Profile. For instance, if the Profile specifies noexcept
-// copy-constructiblity, the test will fail if the copy constructor is trivial.
-//
-// This is useful for testing that a type does not do anything more than a
-// specification requires, such as to minimize things like Hyrum's Law, or more
-// commonly, to prevent a type from being "accidentally" copy-constructible in
-// a way that may produce incorrect results, simply because the user forget to
-// delete that operation.
-#define WITH_STRICT_PROFILE(...)                                      \
-  with_strict_profile(                                                \
-      ::absl::types_internal::MakeStrictProfileRangeT<__VA_ARGS__>()) \
-      ABSL_INTERNAL_RPAREN ABSL_INTERNAL_RPAREN;                      \
-  else GTEST_FATAL_FAILURE_(gtest_ar.failure_message())  // NOLINT
-
-// Internal macro that is used in the internals of the EDSL when forming
-// equivalence classes.
-#define ABSL_INTERNAL_PREPEND_EQ_MAKER(arg) \
-  ::absl::types_internal::EquivalenceClassMaker().arg
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_
diff --git a/third_party/abseil_cpp/absl/types/internal/conformance_testing_helpers.h b/third_party/abseil_cpp/absl/types/internal/conformance_testing_helpers.h
deleted file mode 100644
index 00775f960c..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/conformance_testing_helpers.h
+++ /dev/null
@@ -1,391 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_
-
-// Checks to determine whether or not we can use abi::__cxa_demangle
-#if (defined(__ANDROID__) || defined(ANDROID)) && !defined(OS_ANDROID)
-#define ABSL_INTERNAL_OS_ANDROID
-#endif
-
-// We support certain compilers only.  See demangle.h for details.
-#if defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__))
-#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0
-#elif (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \
-    !defined(__mips__)
-#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1
-#elif defined(__clang__) && !defined(_MSC_VER)
-#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1
-#else
-#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0
-#endif
-
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-#include "absl/utility/utility.h"
-
-#if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE
-#include <cxxabi.h>
-
-#include <cstdlib>
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// Return a readable name for type T.
-template <class T>
-absl::string_view NameOfImpl() {
-// TODO(calabrese) Investigate using debugging:internal_demangle as a fallback.
-#if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE
-  int status = 0;
-  char* demangled_name = nullptr;
-
-  demangled_name =
-      abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
-
-  if (status == 0 && demangled_name != nullptr) {
-    return demangled_name;
-  } else {
-    return typeid(T).name();
-  }
-#else
-  return typeid(T).name();
-#endif
-  // NOTE: We intentionally leak demangled_name so that it remains valid
-  // throughout the remainder of the program.
-}
-
-// Given a type, returns as nice of a type name as we can produce (demangled).
-//
-// Note: This currently strips cv-qualifiers and references, but that is okay
-// because we only use this internally with unqualified object types.
-template <class T>
-std::string NameOf() {
-  static const absl::string_view result = NameOfImpl<T>();
-  return std::string(result);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Metafunction to check if a type is callable with no explicit arguments
-template <class Fun, class /*Enabler*/ = void>
-struct IsNullaryCallableImpl : std::false_type {};
-
-template <class Fun>
-struct IsNullaryCallableImpl<
-    Fun, absl::void_t<decltype(std::declval<const Fun&>()())>>
-    : std::true_type {
-  using result_type = decltype(std::declval<const Fun&>()());
-
-  template <class ValueType>
-  using for_type = std::is_same<ValueType, result_type>;
-
-  using void_if_true = void;
-};
-
-template <class Fun>
-struct IsNullaryCallable : IsNullaryCallableImpl<Fun> {};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// A type that contains a function object that returns an instance of a type
-// that is undergoing conformance testing. This function is required to always
-// return the same value upon invocation.
-template <class Fun>
-struct GeneratorType;
-
-// A type that contains a tuple of GeneratorType<Fun> where each Fun has the
-// same return type. The result of each of the different generators should all
-// be equal values, though the underlying object representation may differ (such
-// as if one returns 0.0 and another return -0.0, or if one returns an empty
-// vector and another returns an empty vector with a different capacity.
-template <class... Funs>
-struct EquivalenceClassType;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction to check if a type is a specialization of EquivalenceClassType
-template <class T>
-struct IsEquivalenceClass : std::false_type {};
-
-template <>
-struct IsEquivalenceClass<EquivalenceClassType<>> : std::true_type {
-  using self = IsEquivalenceClass;
-
-  // A metafunction to check if this EquivalenceClassType is a valid
-  // EquivalenceClassType for a type `ValueType` that is undergoing testing
-  template <class ValueType>
-  using for_type = std::true_type;
-};
-
-template <class Head, class... Tail>
-struct IsEquivalenceClass<EquivalenceClassType<Head, Tail...>>
-    : std::true_type {
-  using self = IsEquivalenceClass;
-
-  // The type undergoing conformance testing that this EquivalenceClass
-  // corresponds to
-  using result_type = typename IsNullaryCallable<Head>::result_type;
-
-  // A metafunction to check if this EquivalenceClassType is a valid
-  // EquivalenceClassType for a type `ValueType` that is undergoing testing
-  template <class ValueType>
-  using for_type = std::is_same<ValueType, result_type>;
-};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// A type that contains an ordered series of EquivalenceClassTypes, where the
-// the function object of each underlying GeneratorType has the same return type
-//
-// These equivalence classes are required to be in a logical ascending order
-// that is consistent with comparison operators that are defined for the return
-// type of each GeneratorType, if any.
-template <class... EqClasses>
-struct OrderedEquivalenceClasses;
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction to determine the return type of the function object contained
-// in a GeneratorType specialization.
-template <class T>
-struct ResultOfGenerator {};
-
-template <class Fun>
-struct ResultOfGenerator<GeneratorType<Fun>> {
-  using type = decltype(std::declval<const Fun&>()());
-};
-
-template <class Fun>
-using ResultOfGeneratorT = typename ResultOfGenerator<GeneratorType<Fun>>::type;
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction that yields true iff each of Funs is a GeneratorType
-// specialization and they all contain functions with the same return type
-template <class /*Enabler*/, class... Funs>
-struct AreGeneratorsWithTheSameReturnTypeImpl : std::false_type {};
-
-template <>
-struct AreGeneratorsWithTheSameReturnTypeImpl<void> : std::true_type {};
-
-template <class Head, class... Tail>
-struct AreGeneratorsWithTheSameReturnTypeImpl<
-    typename std::enable_if<absl::conjunction<std::is_same<
-        ResultOfGeneratorT<Head>, ResultOfGeneratorT<Tail>>...>::value>::type,
-    Head, Tail...> : std::true_type {};
-
-template <class... Funs>
-struct AreGeneratorsWithTheSameReturnType
-    : AreGeneratorsWithTheSameReturnTypeImpl<void, Funs...>::type {};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// A metafunction that yields true iff each of Funs is an EquivalenceClassType
-// specialization and they all contain GeneratorType specializations that have
-// the same return type
-template <class... EqClasses>
-struct AreEquivalenceClassesOfTheSameType {
-  static_assert(sizeof...(EqClasses) != sizeof...(EqClasses), "");
-};
-
-template <>
-struct AreEquivalenceClassesOfTheSameType<> : std::true_type {
-  using self = AreEquivalenceClassesOfTheSameType;
-
-  // Metafunction to check that a type is the same as all of the equivalence
-  // classes, if any.
-  // Note: In this specialization there are no equivalence classes, so the
-  // value type is always compatible.
-  template <class /*ValueType*/>
-  using for_type = std::true_type;
-};
-
-template <class... Funs>
-struct AreEquivalenceClassesOfTheSameType<EquivalenceClassType<Funs...>>
-    : std::true_type {
-  using self = AreEquivalenceClassesOfTheSameType;
-
-  // Metafunction to check that a type is the same as all of the equivalence
-  // classes, if any.
-  template <class ValueType>
-  using for_type = typename IsEquivalenceClass<
-      EquivalenceClassType<Funs...>>::template for_type<ValueType>;
-};
-
-template <class... TailEqClasses>
-struct AreEquivalenceClassesOfTheSameType<
-    EquivalenceClassType<>, EquivalenceClassType<>, TailEqClasses...>
-    : AreEquivalenceClassesOfTheSameType<TailEqClasses...>::self {};
-
-template <class HeadNextFun, class... TailNextFuns, class... TailEqClasses>
-struct AreEquivalenceClassesOfTheSameType<
-    EquivalenceClassType<>, EquivalenceClassType<HeadNextFun, TailNextFuns...>,
-    TailEqClasses...>
-    : AreEquivalenceClassesOfTheSameType<
-          EquivalenceClassType<HeadNextFun, TailNextFuns...>,
-          TailEqClasses...>::self {};
-
-template <class HeadHeadFun, class... TailHeadFuns, class... TailEqClasses>
-struct AreEquivalenceClassesOfTheSameType<
-    EquivalenceClassType<HeadHeadFun, TailHeadFuns...>, EquivalenceClassType<>,
-    TailEqClasses...>
-    : AreEquivalenceClassesOfTheSameType<
-          EquivalenceClassType<HeadHeadFun, TailHeadFuns...>,
-          TailEqClasses...>::self {};
-
-template <class HeadHeadFun, class... TailHeadFuns, class HeadNextFun,
-          class... TailNextFuns, class... TailEqClasses>
-struct AreEquivalenceClassesOfTheSameType<
-    EquivalenceClassType<HeadHeadFun, TailHeadFuns...>,
-    EquivalenceClassType<HeadNextFun, TailNextFuns...>, TailEqClasses...>
-    : absl::conditional_t<
-          IsNullaryCallable<HeadNextFun>::template for_type<
-              typename IsNullaryCallable<HeadHeadFun>::result_type>::value,
-          AreEquivalenceClassesOfTheSameType<
-              EquivalenceClassType<HeadHeadFun, TailHeadFuns...>,
-              TailEqClasses...>,
-          std::false_type> {};
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// Execute a function for each passed-in parameter.
-template <class Fun, class... Cases>
-void ForEachParameter(const Fun& fun, const Cases&... cases) {
-  const std::initializer_list<bool> results = {
-      (static_cast<void>(fun(cases)), true)...};
-
-  (void)results;
-}
-
-// Execute a function on each passed-in parameter (using a bound function).
-template <class Fun>
-struct ForEachParameterFun {
-  template <class... T>
-  void operator()(const T&... cases) const {
-    (ForEachParameter)(fun, cases...);
-  }
-
-  Fun fun;
-};
-
-// Execute a function on each element of a tuple.
-template <class Fun, class Tup>
-void ForEachTupleElement(const Fun& fun, const Tup& tup) {
-  absl::apply(ForEachParameterFun<Fun>{fun}, tup);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Execute a function for each combination of two elements of a tuple, including
-// combinations of an element with itself.
-template <class Fun, class... T>
-struct ForEveryTwoImpl {
-  template <class Lhs>
-  struct WithBoundLhs {
-    template <class Rhs>
-    void operator()(const Rhs& rhs) const {
-      fun(lhs, rhs);
-    }
-
-    Fun fun;
-    Lhs lhs;
-  };
-
-  template <class Lhs>
-  void operator()(const Lhs& lhs) const {
-    (ForEachTupleElement)(WithBoundLhs<Lhs>{fun, lhs}, args);
-  }
-
-  Fun fun;
-  std::tuple<T...> args;
-};
-
-template <class Fun, class... T>
-void ForEveryTwo(const Fun& fun, std::tuple<T...> args) {
-  (ForEachTupleElement)(ForEveryTwoImpl<Fun, T...>{fun, args}, args);
-}
-//
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Insert all values into an associative container
-template<class Container>
-void InsertEach(Container* cont) {
-}
-
-template<class Container, class H, class... T>
-void InsertEach(Container* cont, H&& head, T&&... tail) {
-  cont->insert(head);
-  (InsertEach)(cont, tail...);
-}
-//
-////////////////////////////////////////////////////////////////////////////////
-// A template with a nested "Invoke" static-member-function that executes a
-// passed-in Callable when `Condition` is true, otherwise it ignores the
-// Callable. This is useful for executing a function object with a condition
-// that corresponds to whether or not the Callable can be safely instantiated.
-// It has some overlapping uses with C++17 `if constexpr`.
-template <bool Condition>
-struct If;
-
-template <>
-struct If</*Condition =*/false> {
-  template <class Fun, class... P>
-  static void Invoke(const Fun& /*fun*/, P&&... /*args*/) {}
-};
-
-template <>
-struct If</*Condition =*/true> {
-  template <class Fun, class... P>
-  static void Invoke(const Fun& fun, P&&... args) {
-    // TODO(calabrese) Use std::invoke equivalent instead of function-call.
-    fun(absl::forward<P>(args)...);
-  }
-};
-
-//
-// ABSL_INTERNAL_STRINGIZE(...)
-//
-// This variadic macro transforms its arguments into a c-string literal after
-// expansion.
-//
-// Example:
-//
-//   ABSL_INTERNAL_STRINGIZE(std::array<int, 10>)
-//
-// Results in:
-//
-//   "std::array<int, 10>"
-#define ABSL_INTERNAL_STRINGIZE(...) ABSL_INTERNAL_STRINGIZE_IMPL((__VA_ARGS__))
-#define ABSL_INTERNAL_STRINGIZE_IMPL(arg) ABSL_INTERNAL_STRINGIZE_IMPL2 arg
-#define ABSL_INTERNAL_STRINGIZE_IMPL2(...) #__VA_ARGS__
-
-}  // namespace types_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_
diff --git a/third_party/abseil_cpp/absl/types/internal/conformance_testing_test.cc b/third_party/abseil_cpp/absl/types/internal/conformance_testing_test.cc
deleted file mode 100644
index cf262fa6c2..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/conformance_testing_test.cc
+++ /dev/null
@@ -1,1556 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/internal/conformance_testing.h"
-
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/meta/type_traits.h"
-#include "absl/types/internal/conformance_aliases.h"
-#include "absl/types/internal/conformance_profile.h"
-
-namespace {
-
-namespace ti = absl::types_internal;
-
-template <class T>
-using DefaultConstructibleWithNewImpl = decltype(::new (std::nothrow) T);
-
-template <class T>
-using DefaultConstructibleWithNew =
-    absl::type_traits_internal::is_detected<DefaultConstructibleWithNewImpl, T>;
-
-template <class T>
-using MoveConstructibleWithNewImpl =
-    decltype(::new (std::nothrow) T(std::declval<T>()));
-
-template <class T>
-using MoveConstructibleWithNew =
-    absl::type_traits_internal::is_detected<MoveConstructibleWithNewImpl, T>;
-
-template <class T>
-using CopyConstructibleWithNewImpl =
-    decltype(::new (std::nothrow) T(std::declval<const T&>()));
-
-template <class T>
-using CopyConstructibleWithNew =
-    absl::type_traits_internal::is_detected<CopyConstructibleWithNewImpl, T>;
-
-template <class T,
-          class Result =
-              std::integral_constant<bool, noexcept(::new (std::nothrow) T)>>
-using NothrowDefaultConstructibleWithNewImpl =
-    typename std::enable_if<Result::value>::type;
-
-template <class T>
-using NothrowDefaultConstructibleWithNew =
-    absl::type_traits_internal::is_detected<
-        NothrowDefaultConstructibleWithNewImpl, T>;
-
-template <class T,
-          class Result = std::integral_constant<
-              bool, noexcept(::new (std::nothrow) T(std::declval<T>()))>>
-using NothrowMoveConstructibleWithNewImpl =
-    typename std::enable_if<Result::value>::type;
-
-template <class T>
-using NothrowMoveConstructibleWithNew =
-    absl::type_traits_internal::is_detected<NothrowMoveConstructibleWithNewImpl,
-                                            T>;
-
-template <class T,
-          class Result = std::integral_constant<
-              bool, noexcept(::new (std::nothrow) T(std::declval<const T&>()))>>
-using NothrowCopyConstructibleWithNewImpl =
-    typename std::enable_if<Result::value>::type;
-
-template <class T>
-using NothrowCopyConstructibleWithNew =
-    absl::type_traits_internal::is_detected<NothrowCopyConstructibleWithNewImpl,
-                                            T>;
-
-// NOTE: ?: is used to verify contextually-convertible to bool and not simply
-//       implicit or explicit convertibility.
-#define ABSL_INTERNAL_COMPARISON_OP_EXPR(op) \
-  ((std::declval<const T&>() op std::declval<const T&>()) ? true : true)
-
-#define ABSL_INTERNAL_COMPARISON_OP_TRAIT(name, op)                         \
-  template <class T>                                                        \
-  using name##Impl = decltype(ABSL_INTERNAL_COMPARISON_OP_EXPR(op));        \
-                                                                            \
-  template <class T>                                                        \
-  using name = absl::type_traits_internal::is_detected<name##Impl, T>;      \
-                                                                            \
-  template <class T,                                                        \
-            class Result = std::integral_constant<                          \
-                bool, noexcept(ABSL_INTERNAL_COMPARISON_OP_EXPR(op))>>      \
-  using Nothrow##name##Impl = typename std::enable_if<Result::value>::type; \
-                                                                            \
-  template <class T>                                                        \
-  using Nothrow##name =                                                     \
-      absl::type_traits_internal::is_detected<Nothrow##name##Impl, T>
-
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(EqualityComparable, ==);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(InequalityComparable, !=);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(LessThanComparable, <);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(LessEqualComparable, <=);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(GreaterEqualComparable, >=);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(GreaterThanComparable, >);
-
-#undef ABSL_INTERNAL_COMPARISON_OP_TRAIT
-
-template <class T>
-class ProfileTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(ProfileTest);
-
-TYPED_TEST_P(ProfileTest, HasAppropriateConstructionProperties) {
-  using profile = typename TypeParam::profile;
-  using arch = typename TypeParam::arch;
-  using expected_profile = typename TypeParam::expected_profile;
-
-  using props = ti::PropertiesOfT<profile>;
-  using arch_props = ti::PropertiesOfArchetypeT<arch>;
-  using expected_props = ti::PropertiesOfT<expected_profile>;
-
-  // Make sure all of the properties are as expected.
-  // There are seemingly redundant tests here to make it easier to diagnose
-  // the specifics of the failure if something were to go wrong.
-  EXPECT_TRUE((std::is_same<props, arch_props>::value));
-  EXPECT_TRUE((std::is_same<props, expected_props>::value));
-  EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
-  EXPECT_EQ(props::default_constructible_support,
-            expected_props::default_constructible_support);
-
-  EXPECT_EQ(props::move_constructible_support,
-            expected_props::move_constructible_support);
-
-  EXPECT_EQ(props::copy_constructible_support,
-            expected_props::copy_constructible_support);
-
-  EXPECT_EQ(props::destructible_support, expected_props::destructible_support);
-
-  // Avoid additional error message noise when profile and archetype match with
-  // each other but were not what was expected.
-  if (!std::is_same<props, arch_props>::value) {
-    EXPECT_EQ(arch_props::default_constructible_support,
-              expected_props::default_constructible_support);
-
-    EXPECT_EQ(arch_props::move_constructible_support,
-              expected_props::move_constructible_support);
-
-    EXPECT_EQ(arch_props::copy_constructible_support,
-              expected_props::copy_constructible_support);
-
-    EXPECT_EQ(arch_props::destructible_support,
-              expected_props::destructible_support);
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                       Default constructor checks                         //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::default_constructible_support,
-            expected_props::default_constructible_support);
-
-  switch (expected_props::default_constructible_support) {
-    case ti::default_constructible::maybe:
-      EXPECT_FALSE(DefaultConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowDefaultConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_FALSE(std::is_default_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_default_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value);
-      }
-      break;
-    case ti::default_constructible::yes:
-      EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowDefaultConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_default_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_default_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value);
-      }
-      break;
-    case ti::default_constructible::nothrow:
-      EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowDefaultConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_default_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value);
-
-        // Constructor traits also check the destructor.
-        if (std::is_nothrow_destructible<arch>::value) {
-          EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value);
-        }
-      }
-      break;
-    case ti::default_constructible::trivial:
-      EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowDefaultConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_default_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value);
-
-        // Constructor triviality traits require trivially destructible types.
-        if (absl::is_trivially_destructible<arch>::value) {
-          EXPECT_TRUE(absl::is_trivially_default_constructible<arch>::value);
-        }
-      }
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                         Move constructor checks                          //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::move_constructible_support,
-            expected_props::move_constructible_support);
-
-  switch (expected_props::move_constructible_support) {
-    case ti::move_constructible::maybe:
-      EXPECT_FALSE(MoveConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowMoveConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_FALSE(std::is_move_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_move_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value);
-      }
-      break;
-    case ti::move_constructible::yes:
-      EXPECT_TRUE(MoveConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowMoveConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_move_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_move_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value);
-      }
-      break;
-    case ti::move_constructible::nothrow:
-      EXPECT_TRUE(MoveConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowMoveConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_move_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value);
-
-        // Constructor traits also check the destructor.
-        if (std::is_nothrow_destructible<arch>::value) {
-          EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value);
-        }
-      }
-      break;
-    case ti::move_constructible::trivial:
-      EXPECT_TRUE(MoveConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowMoveConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_move_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value);
-
-        // Constructor triviality traits require trivially destructible types.
-        if (absl::is_trivially_destructible<arch>::value) {
-          EXPECT_TRUE(absl::is_trivially_move_constructible<arch>::value);
-        }
-      }
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                         Copy constructor checks                          //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::copy_constructible_support,
-            expected_props::copy_constructible_support);
-
-  switch (expected_props::copy_constructible_support) {
-    case ti::copy_constructible::maybe:
-      EXPECT_FALSE(CopyConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowCopyConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_FALSE(std::is_copy_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_copy_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value);
-      }
-      break;
-    case ti::copy_constructible::yes:
-      EXPECT_TRUE(CopyConstructibleWithNew<arch>::value);
-      EXPECT_FALSE(NothrowCopyConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_copy_constructible<arch>::value);
-        EXPECT_FALSE(std::is_nothrow_copy_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value);
-      }
-      break;
-    case ti::copy_constructible::nothrow:
-      EXPECT_TRUE(CopyConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowCopyConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_copy_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value);
-        EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value);
-
-        // Constructor traits also check the destructor.
-        if (std::is_nothrow_destructible<arch>::value) {
-          EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value);
-        }
-      }
-      break;
-    case ti::copy_constructible::trivial:
-      EXPECT_TRUE(CopyConstructibleWithNew<arch>::value);
-      EXPECT_TRUE(NothrowCopyConstructibleWithNew<arch>::value);
-
-      // Standard constructible traits depend on the destructor.
-      if (std::is_destructible<arch>::value) {
-        EXPECT_TRUE(std::is_copy_constructible<arch>::value);
-        EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value);
-
-        // Constructor triviality traits require trivially destructible types.
-        if (absl::is_trivially_destructible<arch>::value) {
-          EXPECT_TRUE(absl::is_trivially_copy_constructible<arch>::value);
-        }
-      }
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                           Destructible checks                            //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::destructible_support, expected_props::destructible_support);
-
-  switch (expected_props::destructible_support) {
-    case ti::destructible::maybe:
-      EXPECT_FALSE(std::is_destructible<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_destructible<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_destructible<arch>::value);
-      break;
-    case ti::destructible::yes:
-      EXPECT_TRUE(std::is_destructible<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_destructible<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_destructible<arch>::value);
-      break;
-    case ti::destructible::nothrow:
-      EXPECT_TRUE(std::is_destructible<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_destructible<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_destructible<arch>::value);
-      break;
-    case ti::destructible::trivial:
-      EXPECT_TRUE(std::is_destructible<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_destructible<arch>::value);
-      EXPECT_TRUE(absl::is_trivially_destructible<arch>::value);
-      break;
-  }
-}
-
-TYPED_TEST_P(ProfileTest, HasAppropriateAssignmentProperties) {
-  using profile = typename TypeParam::profile;
-  using arch = typename TypeParam::arch;
-  using expected_profile = typename TypeParam::expected_profile;
-
-  using props = ti::PropertiesOfT<profile>;
-  using arch_props = ti::PropertiesOfArchetypeT<arch>;
-  using expected_props = ti::PropertiesOfT<expected_profile>;
-
-  // Make sure all of the properties are as expected.
-  // There are seemingly redundant tests here to make it easier to diagnose
-  // the specifics of the failure if something were to go wrong.
-  EXPECT_TRUE((std::is_same<props, arch_props>::value));
-  EXPECT_TRUE((std::is_same<props, expected_props>::value));
-  EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
-  EXPECT_EQ(props::move_assignable_support,
-            expected_props::move_assignable_support);
-
-  EXPECT_EQ(props::copy_assignable_support,
-            expected_props::copy_assignable_support);
-
-  // Avoid additional error message noise when profile and archetype match with
-  // each other but were not what was expected.
-  if (!std::is_same<props, arch_props>::value) {
-    EXPECT_EQ(arch_props::move_assignable_support,
-              expected_props::move_assignable_support);
-
-    EXPECT_EQ(arch_props::copy_assignable_support,
-              expected_props::copy_assignable_support);
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                          Move assignment checks                          //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::move_assignable_support,
-            expected_props::move_assignable_support);
-
-  switch (expected_props::move_assignable_support) {
-    case ti::move_assignable::maybe:
-      EXPECT_FALSE(std::is_move_assignable<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_move_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value);
-      break;
-    case ti::move_assignable::yes:
-      EXPECT_TRUE(std::is_move_assignable<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_move_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value);
-      break;
-    case ti::move_assignable::nothrow:
-      EXPECT_TRUE(std::is_move_assignable<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_move_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value);
-      break;
-    case ti::move_assignable::trivial:
-      EXPECT_TRUE(std::is_move_assignable<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_move_assignable<arch>::value);
-      EXPECT_TRUE(absl::is_trivially_move_assignable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                          Copy assignment checks                          //
-  //////////////////////////////////////////////////////////////////////////////
-  EXPECT_EQ(props::copy_assignable_support,
-            expected_props::copy_assignable_support);
-
-  switch (expected_props::copy_assignable_support) {
-    case ti::copy_assignable::maybe:
-      EXPECT_FALSE(std::is_copy_assignable<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_copy_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value);
-      break;
-    case ti::copy_assignable::yes:
-      EXPECT_TRUE(std::is_copy_assignable<arch>::value);
-      EXPECT_FALSE(std::is_nothrow_copy_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value);
-      break;
-    case ti::copy_assignable::nothrow:
-      EXPECT_TRUE(std::is_copy_assignable<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_copy_assignable<arch>::value);
-      EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value);
-      break;
-    case ti::copy_assignable::trivial:
-      EXPECT_TRUE(std::is_copy_assignable<arch>::value);
-      EXPECT_TRUE(std::is_nothrow_copy_assignable<arch>::value);
-      EXPECT_TRUE(absl::is_trivially_copy_assignable<arch>::value);
-      break;
-  }
-}
-
-TYPED_TEST_P(ProfileTest, HasAppropriateComparisonProperties) {
-  using profile = typename TypeParam::profile;
-  using arch = typename TypeParam::arch;
-  using expected_profile = typename TypeParam::expected_profile;
-
-  using props = ti::PropertiesOfT<profile>;
-  using arch_props = ti::PropertiesOfArchetypeT<arch>;
-  using expected_props = ti::PropertiesOfT<expected_profile>;
-
-  // Make sure all of the properties are as expected.
-  // There are seemingly redundant tests here to make it easier to diagnose
-  // the specifics of the failure if something were to go wrong.
-  EXPECT_TRUE((std::is_same<props, arch_props>::value));
-  EXPECT_TRUE((std::is_same<props, expected_props>::value));
-  EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
-  EXPECT_EQ(props::equality_comparable_support,
-            expected_props::equality_comparable_support);
-
-  EXPECT_EQ(props::inequality_comparable_support,
-            expected_props::inequality_comparable_support);
-
-  EXPECT_EQ(props::less_than_comparable_support,
-            expected_props::less_than_comparable_support);
-
-  EXPECT_EQ(props::less_equal_comparable_support,
-            expected_props::less_equal_comparable_support);
-
-  EXPECT_EQ(props::greater_equal_comparable_support,
-            expected_props::greater_equal_comparable_support);
-
-  EXPECT_EQ(props::greater_than_comparable_support,
-            expected_props::greater_than_comparable_support);
-
-  // Avoid additional error message noise when profile and archetype match with
-  // each other but were not what was expected.
-  if (!std::is_same<props, arch_props>::value) {
-    EXPECT_EQ(arch_props::equality_comparable_support,
-              expected_props::equality_comparable_support);
-
-    EXPECT_EQ(arch_props::inequality_comparable_support,
-              expected_props::inequality_comparable_support);
-
-    EXPECT_EQ(arch_props::less_than_comparable_support,
-              expected_props::less_than_comparable_support);
-
-    EXPECT_EQ(arch_props::less_equal_comparable_support,
-              expected_props::less_equal_comparable_support);
-
-    EXPECT_EQ(arch_props::greater_equal_comparable_support,
-              expected_props::greater_equal_comparable_support);
-
-    EXPECT_EQ(arch_props::greater_than_comparable_support,
-              expected_props::greater_than_comparable_support);
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                        Equality comparable checks                        //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::equality_comparable_support) {
-    case ti::equality_comparable::maybe:
-      EXPECT_FALSE(EqualityComparable<arch>::value);
-      EXPECT_FALSE(NothrowEqualityComparable<arch>::value);
-      break;
-    case ti::equality_comparable::yes:
-      EXPECT_TRUE(EqualityComparable<arch>::value);
-      EXPECT_FALSE(NothrowEqualityComparable<arch>::value);
-      break;
-    case ti::equality_comparable::nothrow:
-      EXPECT_TRUE(EqualityComparable<arch>::value);
-      EXPECT_TRUE(NothrowEqualityComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                       Inequality comparable checks                       //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::inequality_comparable_support) {
-    case ti::inequality_comparable::maybe:
-      EXPECT_FALSE(InequalityComparable<arch>::value);
-      EXPECT_FALSE(NothrowInequalityComparable<arch>::value);
-      break;
-    case ti::inequality_comparable::yes:
-      EXPECT_TRUE(InequalityComparable<arch>::value);
-      EXPECT_FALSE(NothrowInequalityComparable<arch>::value);
-      break;
-    case ti::inequality_comparable::nothrow:
-      EXPECT_TRUE(InequalityComparable<arch>::value);
-      EXPECT_TRUE(NothrowInequalityComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                       Less than comparable checks                        //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::less_than_comparable_support) {
-    case ti::less_than_comparable::maybe:
-      EXPECT_FALSE(LessThanComparable<arch>::value);
-      EXPECT_FALSE(NothrowLessThanComparable<arch>::value);
-      break;
-    case ti::less_than_comparable::yes:
-      EXPECT_TRUE(LessThanComparable<arch>::value);
-      EXPECT_FALSE(NothrowLessThanComparable<arch>::value);
-      break;
-    case ti::less_than_comparable::nothrow:
-      EXPECT_TRUE(LessThanComparable<arch>::value);
-      EXPECT_TRUE(NothrowLessThanComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                      Less equal comparable checks                        //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::less_equal_comparable_support) {
-    case ti::less_equal_comparable::maybe:
-      EXPECT_FALSE(LessEqualComparable<arch>::value);
-      EXPECT_FALSE(NothrowLessEqualComparable<arch>::value);
-      break;
-    case ti::less_equal_comparable::yes:
-      EXPECT_TRUE(LessEqualComparable<arch>::value);
-      EXPECT_FALSE(NothrowLessEqualComparable<arch>::value);
-      break;
-    case ti::less_equal_comparable::nothrow:
-      EXPECT_TRUE(LessEqualComparable<arch>::value);
-      EXPECT_TRUE(NothrowLessEqualComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                     Greater equal comparable checks                      //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::greater_equal_comparable_support) {
-    case ti::greater_equal_comparable::maybe:
-      EXPECT_FALSE(GreaterEqualComparable<arch>::value);
-      EXPECT_FALSE(NothrowGreaterEqualComparable<arch>::value);
-      break;
-    case ti::greater_equal_comparable::yes:
-      EXPECT_TRUE(GreaterEqualComparable<arch>::value);
-      EXPECT_FALSE(NothrowGreaterEqualComparable<arch>::value);
-      break;
-    case ti::greater_equal_comparable::nothrow:
-      EXPECT_TRUE(GreaterEqualComparable<arch>::value);
-      EXPECT_TRUE(NothrowGreaterEqualComparable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                     Greater than comparable checks                       //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::greater_than_comparable_support) {
-    case ti::greater_than_comparable::maybe:
-      EXPECT_FALSE(GreaterThanComparable<arch>::value);
-      EXPECT_FALSE(NothrowGreaterThanComparable<arch>::value);
-      break;
-    case ti::greater_than_comparable::yes:
-      EXPECT_TRUE(GreaterThanComparable<arch>::value);
-      EXPECT_FALSE(NothrowGreaterThanComparable<arch>::value);
-      break;
-    case ti::greater_than_comparable::nothrow:
-      EXPECT_TRUE(GreaterThanComparable<arch>::value);
-      EXPECT_TRUE(NothrowGreaterThanComparable<arch>::value);
-      break;
-  }
-}
-
-TYPED_TEST_P(ProfileTest, HasAppropriateAuxilliaryProperties) {
-  using profile = typename TypeParam::profile;
-  using arch = typename TypeParam::arch;
-  using expected_profile = typename TypeParam::expected_profile;
-
-  using props = ti::PropertiesOfT<profile>;
-  using arch_props = ti::PropertiesOfArchetypeT<arch>;
-  using expected_props = ti::PropertiesOfT<expected_profile>;
-
-  // Make sure all of the properties are as expected.
-  // There are seemingly redundant tests here to make it easier to diagnose
-  // the specifics of the failure if something were to go wrong.
-  EXPECT_TRUE((std::is_same<props, arch_props>::value));
-  EXPECT_TRUE((std::is_same<props, expected_props>::value));
-  EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
-  EXPECT_EQ(props::swappable_support, expected_props::swappable_support);
-
-  EXPECT_EQ(props::hashable_support, expected_props::hashable_support);
-
-  // Avoid additional error message noise when profile and archetype match with
-  // each other but were not what was expected.
-  if (!std::is_same<props, arch_props>::value) {
-    EXPECT_EQ(arch_props::swappable_support, expected_props::swappable_support);
-
-    EXPECT_EQ(arch_props::hashable_support, expected_props::hashable_support);
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                            Swappable checks                              //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::swappable_support) {
-    case ti::swappable::maybe:
-      EXPECT_FALSE(absl::type_traits_internal::IsSwappable<arch>::value);
-      EXPECT_FALSE(absl::type_traits_internal::IsNothrowSwappable<arch>::value);
-      break;
-    case ti::swappable::yes:
-      EXPECT_TRUE(absl::type_traits_internal::IsSwappable<arch>::value);
-      EXPECT_FALSE(absl::type_traits_internal::IsNothrowSwappable<arch>::value);
-      break;
-    case ti::swappable::nothrow:
-      EXPECT_TRUE(absl::type_traits_internal::IsSwappable<arch>::value);
-      EXPECT_TRUE(absl::type_traits_internal::IsNothrowSwappable<arch>::value);
-      break;
-  }
-
-  //////////////////////////////////////////////////////////////////////////////
-  //                             Hashable checks                              //
-  //////////////////////////////////////////////////////////////////////////////
-  switch (expected_props::hashable_support) {
-    case ti::hashable::maybe:
-#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-      EXPECT_FALSE(absl::type_traits_internal::IsHashable<arch>::value);
-#endif  // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-      break;
-    case ti::hashable::yes:
-      EXPECT_TRUE(absl::type_traits_internal::IsHashable<arch>::value);
-      break;
-  }
-}
-
-REGISTER_TYPED_TEST_SUITE_P(ProfileTest, HasAppropriateConstructionProperties,
-                            HasAppropriateAssignmentProperties,
-                            HasAppropriateComparisonProperties,
-                            HasAppropriateAuxilliaryProperties);
-
-template <class Profile, class Arch, class ExpectedProfile>
-struct ProfileAndExpectation {
-  using profile = Profile;
-  using arch = Arch;
-  using expected_profile = ExpectedProfile;
-};
-
-using CoreProfilesToTest = ::testing::Types<
-    // The terminating case of combine (all properties are "maybe").
-    ProfileAndExpectation<ti::CombineProfiles<>,
-                          ti::Archetype<ti::CombineProfiles<>>,
-                          ti::ConformanceProfile<>>,
-
-    // Core default constructor profiles
-    ProfileAndExpectation<
-        ti::HasDefaultConstructorProfile, ti::HasDefaultConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowDefaultConstructorProfile,
-        ti::HasNothrowDefaultConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialDefaultConstructorProfile,
-        ti::HasTrivialDefaultConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::trivial>>,
-
-    // Core move constructor profiles
-    ProfileAndExpectation<
-        ti::HasMoveConstructorProfile, ti::HasMoveConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowMoveConstructorProfile,
-        ti::HasNothrowMoveConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialMoveConstructorProfile,
-        ti::HasTrivialMoveConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::trivial>>,
-
-    // Core copy constructor profiles
-    ProfileAndExpectation<
-        ti::HasCopyConstructorProfile, ti::HasCopyConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::maybe,
-                               ti::copy_constructible::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowCopyConstructorProfile,
-        ti::HasNothrowCopyConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::maybe,
-                               ti::copy_constructible::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialCopyConstructorProfile,
-        ti::HasTrivialCopyConstructorArchetype,
-        ti::ConformanceProfile<ti::default_constructible::maybe,
-                               ti::move_constructible::maybe,
-                               ti::copy_constructible::trivial>>,
-
-    // Core move assignment profiles
-    ProfileAndExpectation<
-        ti::HasMoveAssignProfile, ti::HasMoveAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowMoveAssignProfile, ti::HasNothrowMoveAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialMoveAssignProfile, ti::HasTrivialMoveAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::trivial>>,
-
-    // Core copy assignment profiles
-    ProfileAndExpectation<
-        ti::HasCopyAssignProfile, ti::HasCopyAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowCopyAssignProfile, ti::HasNothrowCopyAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialCopyAssignProfile, ti::HasTrivialCopyAssignArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::trivial>>,
-
-    // Core destructor profiles
-    ProfileAndExpectation<
-        ti::HasDestructorProfile, ti::HasDestructorArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowDestructorProfile, ti::HasNothrowDestructorArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-    ProfileAndExpectation<
-        ti::HasTrivialDestructorProfile, ti::HasTrivialDestructorArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::trivial>>,
-
-    // Core equality comparable profiles
-    ProfileAndExpectation<
-        ti::HasEqualityProfile, ti::HasEqualityArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowEqualityProfile, ti::HasNothrowEqualityArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::nothrow>>,
-
-    // Core inequality comparable profiles
-    ProfileAndExpectation<
-        ti::HasInequalityProfile, ti::HasInequalityArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowInequalityProfile, ti::HasNothrowInequalityArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe,
-            ti::inequality_comparable::nothrow>>,
-
-    // Core less than comparable profiles
-    ProfileAndExpectation<
-        ti::HasLessThanProfile, ti::HasLessThanArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowLessThanProfile, ti::HasNothrowLessThanArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::nothrow>>,
-
-    // Core less equal comparable profiles
-    ProfileAndExpectation<
-        ti::HasLessEqualProfile, ti::HasLessEqualArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowLessEqualProfile, ti::HasNothrowLessEqualArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe,
-            ti::less_equal_comparable::nothrow>>,
-
-    // Core greater equal comparable profiles
-    ProfileAndExpectation<
-        ti::HasGreaterEqualProfile, ti::HasGreaterEqualArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowGreaterEqualProfile, ti::HasNothrowGreaterEqualArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::nothrow>>,
-
-    // Core greater than comparable profiles
-    ProfileAndExpectation<
-        ti::HasGreaterThanProfile, ti::HasGreaterThanArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowGreaterThanProfile, ti::HasNothrowGreaterThanArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::nothrow>>,
-
-    // Core swappable profiles
-    ProfileAndExpectation<
-        ti::HasSwapProfile, ti::HasSwapArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::yes>>,
-    ProfileAndExpectation<
-        ti::HasNothrowSwapProfile, ti::HasNothrowSwapArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    // Core hashable profiles
-    ProfileAndExpectation<
-        ti::HasStdHashSpecializationProfile,
-        ti::HasStdHashSpecializationArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::maybe,
-            ti::hashable::yes>>>;
-
-using CommonProfilesToTest = ::testing::Types<
-    // NothrowMoveConstructible
-    ProfileAndExpectation<
-        ti::NothrowMoveConstructibleProfile,
-        ti::NothrowMoveConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
-    // CopyConstructible
-    ProfileAndExpectation<
-        ti::CopyConstructibleProfile, ti::CopyConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
-    // NothrowMovable
-    ProfileAndExpectation<
-        ti::NothrowMovableProfile, ti::NothrowMovableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::nothrow,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    // Value
-    ProfileAndExpectation<
-        ti::ValueProfile, ti::ValueArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::nothrow,
-            ti::copy_assignable::yes, ti::destructible::nothrow,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    ////////////////////////////////////////////////////////////////////////////
-    //                  Common but also DefaultConstructible                  //
-    ////////////////////////////////////////////////////////////////////////////
-
-    // DefaultConstructibleNothrowMoveConstructible
-    ProfileAndExpectation<
-        ti::DefaultConstructibleNothrowMoveConstructibleProfile,
-        ti::DefaultConstructibleNothrowMoveConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::yes, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
-    // DefaultConstructibleCopyConstructible
-    ProfileAndExpectation<
-        ti::DefaultConstructibleCopyConstructibleProfile,
-        ti::DefaultConstructibleCopyConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::yes, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
-    // DefaultConstructibleNothrowMovable
-    ProfileAndExpectation<
-        ti::DefaultConstructibleNothrowMovableProfile,
-        ti::DefaultConstructibleNothrowMovableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::yes, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::nothrow,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    // DefaultConstructibleValue
-    ProfileAndExpectation<
-        ti::DefaultConstructibleValueProfile,
-        ti::DefaultConstructibleValueArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::yes, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::nothrow,
-            ti::copy_assignable::yes, ti::destructible::nothrow,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>>;
-
-using ComparableHelpersProfilesToTest = ::testing::Types<
-    // Equatable
-    ProfileAndExpectation<
-        ti::EquatableProfile, ti::EquatableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes>>,
-
-    // Comparable
-    ProfileAndExpectation<
-        ti::ComparableProfile, ti::ComparableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes,
-            ti::greater_than_comparable::yes>>,
-
-    // NothrowEquatable
-    ProfileAndExpectation<
-        ti::NothrowEquatableProfile, ti::NothrowEquatableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::nothrow,
-            ti::inequality_comparable::nothrow>>,
-
-    // NothrowComparable
-    ProfileAndExpectation<
-        ti::NothrowComparableProfile, ti::NothrowComparableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::maybe,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::maybe,
-            ti::equality_comparable::nothrow,
-            ti::inequality_comparable::nothrow,
-            ti::less_than_comparable::nothrow,
-            ti::less_equal_comparable::nothrow,
-            ti::greater_equal_comparable::nothrow,
-            ti::greater_than_comparable::nothrow>>>;
-
-using CommonComparableProfilesToTest = ::testing::Types<
-    // ComparableNothrowMoveConstructible
-    ProfileAndExpectation<
-        ti::ComparableNothrowMoveConstructibleProfile,
-        ti::ComparableNothrowMoveConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes,
-            ti::greater_than_comparable::yes>>,
-
-    // ComparableCopyConstructible
-    ProfileAndExpectation<
-        ti::ComparableCopyConstructibleProfile,
-        ti::ComparableCopyConstructibleArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::maybe,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes,
-            ti::greater_than_comparable::yes>>,
-
-    // ComparableNothrowMovable
-    ProfileAndExpectation<
-        ti::ComparableNothrowMovableProfile,
-        ti::ComparableNothrowMovableArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::maybe, ti::move_assignable::nothrow,
-            ti::copy_assignable::maybe, ti::destructible::nothrow,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes,
-            ti::swappable::nothrow>>,
-
-    // ComparableValue
-    ProfileAndExpectation<
-        ti::ComparableValueProfile, ti::ComparableValueArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::maybe, ti::move_constructible::nothrow,
-            ti::copy_constructible::yes, ti::move_assignable::nothrow,
-            ti::copy_assignable::yes, ti::destructible::nothrow,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes,
-            ti::swappable::nothrow>>>;
-
-using TrivialProfilesToTest = ::testing::Types<
-    ProfileAndExpectation<
-        ti::TrivialSpecialMemberFunctionsProfile,
-        ti::TrivialSpecialMemberFunctionsArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::trivial, ti::move_constructible::trivial,
-            ti::copy_constructible::trivial, ti::move_assignable::trivial,
-            ti::copy_assignable::trivial, ti::destructible::trivial,
-            ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
-            ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
-            ti::greater_equal_comparable::maybe,
-            ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
-    ProfileAndExpectation<
-        ti::TriviallyCompleteProfile, ti::TriviallyCompleteArchetype,
-        ti::ConformanceProfile<
-            ti::default_constructible::trivial, ti::move_constructible::trivial,
-            ti::copy_constructible::trivial, ti::move_assignable::trivial,
-            ti::copy_assignable::trivial, ti::destructible::trivial,
-            ti::equality_comparable::yes, ti::inequality_comparable::yes,
-            ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
-            ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes,
-            ti::swappable::nothrow, ti::hashable::yes>>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(Core, ProfileTest, CoreProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(Common, ProfileTest, CommonProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(ComparableHelpers, ProfileTest,
-                               ComparableHelpersProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(CommonComparable, ProfileTest,
-                               CommonComparableProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(Trivial, ProfileTest, TrivialProfilesToTest);
-
-TEST(ConformanceTestingTest, Basic) {
-  using profile = ti::CombineProfiles<ti::TriviallyCompleteProfile,
-                                      ti::NothrowComparableProfile>;
-
-  using lim = std::numeric_limits<float>;
-
-  ABSL_INTERNAL_ASSERT_CONFORMANCE_OF(float)
-      .INITIALIZER(-lim::infinity())
-      .INITIALIZER(lim::lowest())
-      .INITIALIZER(-1.f)
-      .INITIALIZER(-lim::min())
-      .EQUIVALENCE_CLASS(INITIALIZER(-0.f), INITIALIZER(0.f))
-      .INITIALIZER(lim::min())
-      .INITIALIZER(1.f)
-      .INITIALIZER(lim::max())
-      .INITIALIZER(lim::infinity())
-      .WITH_STRICT_PROFILE(absl::types_internal::RegularityDomain, profile);
-}
-
-struct BadMoveConstruct {
-  BadMoveConstruct() = default;
-  BadMoveConstruct(BadMoveConstruct&& other) noexcept
-      : value(other.value + 1) {}
-  BadMoveConstruct& operator=(BadMoveConstruct&& other) noexcept = default;
-  int value = 0;
-
-  friend bool operator==(BadMoveConstruct const& lhs,
-                         BadMoveConstruct const& rhs) {
-    return lhs.value == rhs.value;
-  }
-  friend bool operator!=(BadMoveConstruct const& lhs,
-                         BadMoveConstruct const& rhs) {
-    return lhs.value != rhs.value;
-  }
-};
-
-struct BadMoveAssign {
-  BadMoveAssign() = default;
-  BadMoveAssign(BadMoveAssign&& other) noexcept = default;
-  BadMoveAssign& operator=(BadMoveAssign&& other) noexcept {
-    int new_value = other.value + 1;
-    value = new_value;
-    return *this;
-  }
-  int value = 0;
-
-  friend bool operator==(BadMoveAssign const& lhs, BadMoveAssign const& rhs) {
-    return lhs.value == rhs.value;
-  }
-  friend bool operator!=(BadMoveAssign const& lhs, BadMoveAssign const& rhs) {
-    return lhs.value != rhs.value;
-  }
-};
-
-enum class WhichCompIsBad { eq, ne, lt, le, ge, gt };
-
-template <WhichCompIsBad Which>
-struct BadCompare {
-  int value;
-
-  friend bool operator==(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::eq ? lhs.value != rhs.value
-                                       : lhs.value == rhs.value;
-  }
-
-  friend bool operator!=(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::ne ? lhs.value == rhs.value
-                                       : lhs.value != rhs.value;
-  }
-
-  friend bool operator<(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::lt ? lhs.value >= rhs.value
-                                       : lhs.value < rhs.value;
-  }
-
-  friend bool operator<=(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::le ? lhs.value > rhs.value
-                                       : lhs.value <= rhs.value;
-  }
-
-  friend bool operator>=(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::ge ? lhs.value < rhs.value
-                                       : lhs.value >= rhs.value;
-  }
-
-  friend bool operator>(BadCompare const& lhs, BadCompare const& rhs) {
-    return Which == WhichCompIsBad::gt ? lhs.value <= rhs.value
-                                       : lhs.value > rhs.value;
-  }
-};
-
-TEST(ConformanceTestingDeathTest, Failures) {
-  {
-    using profile = ti::CombineProfiles<ti::TriviallyCompleteProfile,
-                                        ti::NothrowComparableProfile>;
-
-    // Note: The initializers are intentionally in the wrong order.
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(float)
-        .INITIALIZER(1.f)
-        .INITIALIZER(0.f)
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using profile =
-        ti::CombineProfiles<ti::NothrowMovableProfile, ti::EquatableProfile>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadMoveConstruct)
-        .DUE_TO("Move construction")
-        .INITIALIZER(BadMoveConstruct())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using profile =
-        ti::CombineProfiles<ti::NothrowMovableProfile, ti::EquatableProfile>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadMoveAssign)
-        .DUE_TO("Move assignment")
-        .INITIALIZER(BadMoveAssign())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-TEST(ConformanceTestingDeathTest, CompFailures) {
-  using profile = ti::ComparableProfile;
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::eq>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::ne>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::lt>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::le>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::ge>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-
-  {
-    using BadComp = BadCompare<WhichCompIsBad::gt>;
-
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp)
-        .DUE_TO("Comparison")
-        .INITIALIZER(BadComp{0})
-        .INITIALIZER(BadComp{1})
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadSelfMove {
-  BadSelfMove() = default;
-  BadSelfMove(BadSelfMove&&) = default;
-  BadSelfMove& operator=(BadSelfMove&& other) noexcept {
-    if (this == &other) {
-      broken_state = true;
-    }
-    return *this;
-  }
-
-  friend bool operator==(const BadSelfMove& lhs, const BadSelfMove& rhs) {
-    return !(lhs.broken_state || rhs.broken_state);
-  }
-
-  friend bool operator!=(const BadSelfMove& lhs, const BadSelfMove& rhs) {
-    return lhs.broken_state || rhs.broken_state;
-  }
-
-  bool broken_state = false;
-};
-
-TEST(ConformanceTestingDeathTest, SelfMoveFailure) {
-  using profile = ti::EquatableNothrowMovableProfile;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadSelfMove)
-        .DUE_TO("Move assignment")
-        .INITIALIZER(BadSelfMove())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadSelfCopy {
-  BadSelfCopy() = default;
-  BadSelfCopy(BadSelfCopy&&) = default;
-  BadSelfCopy(const BadSelfCopy&) = default;
-  BadSelfCopy& operator=(BadSelfCopy&&) = default;
-  BadSelfCopy& operator=(BadSelfCopy const& other) {
-    if (this == &other) {
-      broken_state = true;
-    }
-    return *this;
-  }
-
-  friend bool operator==(const BadSelfCopy& lhs, const BadSelfCopy& rhs) {
-    return !(lhs.broken_state || rhs.broken_state);
-  }
-
-  friend bool operator!=(const BadSelfCopy& lhs, const BadSelfCopy& rhs) {
-    return lhs.broken_state || rhs.broken_state;
-  }
-
-  bool broken_state = false;
-};
-
-TEST(ConformanceTestingDeathTest, SelfCopyFailure) {
-  using profile = ti::EquatableValueProfile;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadSelfCopy)
-        .DUE_TO("Copy assignment")
-        .INITIALIZER(BadSelfCopy())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadSelfSwap {
-  friend void swap(BadSelfSwap& lhs, BadSelfSwap& rhs) noexcept {
-    if (&lhs == &rhs) lhs.broken_state = true;
-  }
-
-  friend bool operator==(const BadSelfSwap& lhs, const BadSelfSwap& rhs) {
-    return !(lhs.broken_state || rhs.broken_state);
-  }
-
-  friend bool operator!=(const BadSelfSwap& lhs, const BadSelfSwap& rhs) {
-    return lhs.broken_state || rhs.broken_state;
-  }
-
-  bool broken_state = false;
-};
-
-TEST(ConformanceTestingDeathTest, SelfSwapFailure) {
-  using profile = ti::EquatableNothrowMovableProfile;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadSelfSwap)
-        .DUE_TO("Swap")
-        .INITIALIZER(BadSelfSwap())
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadDefaultInitializedMoveAssign {
-  BadDefaultInitializedMoveAssign() : default_initialized(true) {}
-  explicit BadDefaultInitializedMoveAssign(int v) : value(v) {}
-  BadDefaultInitializedMoveAssign(
-      BadDefaultInitializedMoveAssign&& other) noexcept
-      : value(other.value) {}
-  BadDefaultInitializedMoveAssign& operator=(
-      BadDefaultInitializedMoveAssign&& other) noexcept {
-    value = other.value;
-    if (default_initialized) ++value;  // Bad move if lhs is default initialized
-    return *this;
-  }
-
-  friend bool operator==(const BadDefaultInitializedMoveAssign& lhs,
-                         const BadDefaultInitializedMoveAssign& rhs) {
-    return lhs.value == rhs.value;
-  }
-
-  friend bool operator!=(const BadDefaultInitializedMoveAssign& lhs,
-                         const BadDefaultInitializedMoveAssign& rhs) {
-    return lhs.value != rhs.value;
-  }
-
-  bool default_initialized = false;
-  int value = 0;
-};
-
-TEST(ConformanceTestingDeathTest, DefaultInitializedMoveAssignFailure) {
-  using profile =
-      ti::CombineProfiles<ti::DefaultConstructibleNothrowMovableProfile,
-                          ti::EquatableProfile>;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadDefaultInitializedMoveAssign)
-        .DUE_TO("move assignment")
-        .INITIALIZER(BadDefaultInitializedMoveAssign(0))
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-struct BadDefaultInitializedCopyAssign {
-  BadDefaultInitializedCopyAssign() : default_initialized(true) {}
-  explicit BadDefaultInitializedCopyAssign(int v) : value(v) {}
-  BadDefaultInitializedCopyAssign(
-      BadDefaultInitializedCopyAssign&& other) noexcept
-      : value(other.value) {}
-  BadDefaultInitializedCopyAssign(const BadDefaultInitializedCopyAssign& other)
-      : value(other.value) {}
-
-  BadDefaultInitializedCopyAssign& operator=(
-      BadDefaultInitializedCopyAssign&& other) noexcept {
-    value = other.value;
-    return *this;
-  }
-
-  BadDefaultInitializedCopyAssign& operator=(
-      const BadDefaultInitializedCopyAssign& other) {
-    value = other.value;
-    if (default_initialized) ++value;  // Bad move if lhs is default initialized
-    return *this;
-  }
-
-  friend bool operator==(const BadDefaultInitializedCopyAssign& lhs,
-                         const BadDefaultInitializedCopyAssign& rhs) {
-    return lhs.value == rhs.value;
-  }
-
-  friend bool operator!=(const BadDefaultInitializedCopyAssign& lhs,
-                         const BadDefaultInitializedCopyAssign& rhs) {
-    return lhs.value != rhs.value;
-  }
-
-  bool default_initialized = false;
-  int value = 0;
-};
-
-TEST(ConformanceTestingDeathTest, DefaultInitializedAssignFailure) {
-  using profile = ti::CombineProfiles<ti::DefaultConstructibleValueProfile,
-                                      ti::EquatableProfile>;
-
-  {
-    ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadDefaultInitializedCopyAssign)
-        .DUE_TO("copy assignment")
-        .INITIALIZER(BadDefaultInitializedCopyAssign(0))
-        .WITH_LOOSE_PROFILE(profile);
-  }
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/types/internal/optional.h b/third_party/abseil_cpp/absl/types/internal/optional.h
deleted file mode 100644
index 92932b6001..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/optional.h
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-#ifndef ABSL_TYPES_INTERNAL_OPTIONAL_H_
-#define ABSL_TYPES_INTERNAL_OPTIONAL_H_
-
-#include <functional>
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/internal/inline_variable.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-#include "absl/utility/utility.h"
-
-// ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
-//
-// Inheriting constructors is supported in GCC 4.8+, Clang 3.3+ and MSVC 2015.
-// __cpp_inheriting_constructors is a predefined macro and a recommended way to
-// check for this language feature, but GCC doesn't support it until 5.0 and
-// Clang doesn't support it until 3.6.
-// Also, MSVC 2015 has a bug: it doesn't inherit the constexpr template
-// constructor. For example, the following code won't work on MSVC 2015 Update3:
-// struct Base {
-//   int t;
-//   template <typename T>
-//   constexpr Base(T t_) : t(t_) {}
-// };
-// struct Foo : Base {
-//   using Base::Base;
-// }
-// constexpr Foo foo(0);  // doesn't work on MSVC 2015
-#if defined(__clang__)
-#if __has_feature(cxx_inheriting_constructors)
-#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1
-#endif
-#elif (defined(__GNUC__) &&                                       \
-       (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 8)) || \
-    (__cpp_inheriting_constructors >= 200802) ||                  \
-    (defined(_MSC_VER) && _MSC_VER >= 1910)
-#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1
-#endif
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// Forward declaration
-template <typename T>
-class optional;
-
-namespace optional_internal {
-
-// This tag type is used as a constructor parameter type for `nullopt_t`.
-struct init_t {
-  explicit init_t() = default;
-};
-
-struct empty_struct {};
-
-// This class stores the data in optional<T>.
-// It is specialized based on whether T is trivially destructible.
-// This is the specialization for non trivially destructible type.
-template <typename T, bool unused = std::is_trivially_destructible<T>::value>
-class optional_data_dtor_base {
-  struct dummy_type {
-    static_assert(sizeof(T) % sizeof(empty_struct) == 0, "");
-    // Use an array to avoid GCC 6 placement-new warning.
-    empty_struct data[sizeof(T) / sizeof(empty_struct)];
-  };
-
- protected:
-  // Whether there is data or not.
-  bool engaged_;
-  // Data storage
-  union {
-    T data_;
-    dummy_type dummy_;
-  };
-
-  void destruct() noexcept {
-    if (engaged_) {
-      data_.~T();
-      engaged_ = false;
-    }
-  }
-
-  // dummy_ must be initialized for constexpr constructor.
-  constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {}
-
-  template <typename... Args>
-  constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args)
-      : engaged_(true), data_(absl::forward<Args>(args)...) {}
-
-  ~optional_data_dtor_base() { destruct(); }
-};
-
-// Specialization for trivially destructible type.
-template <typename T>
-class optional_data_dtor_base<T, true> {
-  struct dummy_type {
-    static_assert(sizeof(T) % sizeof(empty_struct) == 0, "");
-    // Use array to avoid GCC 6 placement-new warning.
-    empty_struct data[sizeof(T) / sizeof(empty_struct)];
-  };
-
- protected:
-  // Whether there is data or not.
-  bool engaged_;
-  // Data storage
-  union {
-    T data_;
-    dummy_type dummy_;
-  };
-  void destruct() noexcept { engaged_ = false; }
-
-  // dummy_ must be initialized for constexpr constructor.
-  constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {}
-
-  template <typename... Args>
-  constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args)
-      : engaged_(true), data_(absl::forward<Args>(args)...) {}
-};
-
-template <typename T>
-class optional_data_base : public optional_data_dtor_base<T> {
- protected:
-  using base = optional_data_dtor_base<T>;
-#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
-  using base::base;
-#else
-  optional_data_base() = default;
-
-  template <typename... Args>
-  constexpr explicit optional_data_base(in_place_t t, Args&&... args)
-      : base(t, absl::forward<Args>(args)...) {}
-#endif
-
-  template <typename... Args>
-  void construct(Args&&... args) {
-    // Use dummy_'s address to work around casting cv-qualified T* to void*.
-    ::new (static_cast<void*>(&this->dummy_)) T(std::forward<Args>(args)...);
-    this->engaged_ = true;
-  }
-
-  template <typename U>
-  void assign(U&& u) {
-    if (this->engaged_) {
-      this->data_ = std::forward<U>(u);
-    } else {
-      construct(std::forward<U>(u));
-    }
-  }
-};
-
-// TODO(absl-team): Add another class using
-// std::is_trivially_move_constructible trait when available to match
-// http://cplusplus.github.io/LWG/lwg-defects.html#2900, for types that
-// have trivial move but nontrivial copy.
-// Also, we should be checking is_trivially_copyable here, which is not
-// supported now, so we use is_trivially_* traits instead.
-template <typename T,
-          bool unused = absl::is_trivially_copy_constructible<T>::value&&
-              absl::is_trivially_copy_assignable<typename std::remove_cv<
-                  T>::type>::value&& std::is_trivially_destructible<T>::value>
-class optional_data;
-
-// Trivially copyable types
-template <typename T>
-class optional_data<T, true> : public optional_data_base<T> {
- protected:
-#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
-  using optional_data_base<T>::optional_data_base;
-#else
-  optional_data() = default;
-
-  template <typename... Args>
-  constexpr explicit optional_data(in_place_t t, Args&&... args)
-      : optional_data_base<T>(t, absl::forward<Args>(args)...) {}
-#endif
-};
-
-template <typename T>
-class optional_data<T, false> : public optional_data_base<T> {
- protected:
-#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
-  using optional_data_base<T>::optional_data_base;
-#else
-  template <typename... Args>
-  constexpr explicit optional_data(in_place_t t, Args&&... args)
-      : optional_data_base<T>(t, absl::forward<Args>(args)...) {}
-#endif
-
-  optional_data() = default;
-
-  optional_data(const optional_data& rhs) : optional_data_base<T>() {
-    if (rhs.engaged_) {
-      this->construct(rhs.data_);
-    }
-  }
-
-  optional_data(optional_data&& rhs) noexcept(
-      absl::default_allocator_is_nothrow::value ||
-      std::is_nothrow_move_constructible<T>::value)
-      : optional_data_base<T>() {
-    if (rhs.engaged_) {
-      this->construct(std::move(rhs.data_));
-    }
-  }
-
-  optional_data& operator=(const optional_data& rhs) {
-    if (rhs.engaged_) {
-      this->assign(rhs.data_);
-    } else {
-      this->destruct();
-    }
-    return *this;
-  }
-
-  optional_data& operator=(optional_data&& rhs) noexcept(
-      std::is_nothrow_move_assignable<T>::value&&
-          std::is_nothrow_move_constructible<T>::value) {
-    if (rhs.engaged_) {
-      this->assign(std::move(rhs.data_));
-    } else {
-      this->destruct();
-    }
-    return *this;
-  }
-};
-
-// Ordered by level of restriction, from low to high.
-// Copyable implies movable.
-enum class copy_traits { copyable = 0, movable = 1, non_movable = 2 };
-
-// Base class for enabling/disabling copy/move constructor.
-template <copy_traits>
-class optional_ctor_base;
-
-template <>
-class optional_ctor_base<copy_traits::copyable> {
- public:
-  constexpr optional_ctor_base() = default;
-  optional_ctor_base(const optional_ctor_base&) = default;
-  optional_ctor_base(optional_ctor_base&&) = default;
-  optional_ctor_base& operator=(const optional_ctor_base&) = default;
-  optional_ctor_base& operator=(optional_ctor_base&&) = default;
-};
-
-template <>
-class optional_ctor_base<copy_traits::movable> {
- public:
-  constexpr optional_ctor_base() = default;
-  optional_ctor_base(const optional_ctor_base&) = delete;
-  optional_ctor_base(optional_ctor_base&&) = default;
-  optional_ctor_base& operator=(const optional_ctor_base&) = default;
-  optional_ctor_base& operator=(optional_ctor_base&&) = default;
-};
-
-template <>
-class optional_ctor_base<copy_traits::non_movable> {
- public:
-  constexpr optional_ctor_base() = default;
-  optional_ctor_base(const optional_ctor_base&) = delete;
-  optional_ctor_base(optional_ctor_base&&) = delete;
-  optional_ctor_base& operator=(const optional_ctor_base&) = default;
-  optional_ctor_base& operator=(optional_ctor_base&&) = default;
-};
-
-// Base class for enabling/disabling copy/move assignment.
-template <copy_traits>
-class optional_assign_base;
-
-template <>
-class optional_assign_base<copy_traits::copyable> {
- public:
-  constexpr optional_assign_base() = default;
-  optional_assign_base(const optional_assign_base&) = default;
-  optional_assign_base(optional_assign_base&&) = default;
-  optional_assign_base& operator=(const optional_assign_base&) = default;
-  optional_assign_base& operator=(optional_assign_base&&) = default;
-};
-
-template <>
-class optional_assign_base<copy_traits::movable> {
- public:
-  constexpr optional_assign_base() = default;
-  optional_assign_base(const optional_assign_base&) = default;
-  optional_assign_base(optional_assign_base&&) = default;
-  optional_assign_base& operator=(const optional_assign_base&) = delete;
-  optional_assign_base& operator=(optional_assign_base&&) = default;
-};
-
-template <>
-class optional_assign_base<copy_traits::non_movable> {
- public:
-  constexpr optional_assign_base() = default;
-  optional_assign_base(const optional_assign_base&) = default;
-  optional_assign_base(optional_assign_base&&) = default;
-  optional_assign_base& operator=(const optional_assign_base&) = delete;
-  optional_assign_base& operator=(optional_assign_base&&) = delete;
-};
-
-template <typename T>
-struct ctor_copy_traits {
-  static constexpr copy_traits traits =
-      std::is_copy_constructible<T>::value
-          ? copy_traits::copyable
-          : std::is_move_constructible<T>::value ? copy_traits::movable
-                                                 : copy_traits::non_movable;
-};
-
-template <typename T>
-struct assign_copy_traits {
-  static constexpr copy_traits traits =
-      absl::is_copy_assignable<T>::value && std::is_copy_constructible<T>::value
-          ? copy_traits::copyable
-          : absl::is_move_assignable<T>::value &&
-                    std::is_move_constructible<T>::value
-                ? copy_traits::movable
-                : copy_traits::non_movable;
-};
-
-// Whether T is constructible or convertible from optional<U>.
-template <typename T, typename U>
-struct is_constructible_convertible_from_optional
-    : std::integral_constant<
-          bool, std::is_constructible<T, optional<U>&>::value ||
-                    std::is_constructible<T, optional<U>&&>::value ||
-                    std::is_constructible<T, const optional<U>&>::value ||
-                    std::is_constructible<T, const optional<U>&&>::value ||
-                    std::is_convertible<optional<U>&, T>::value ||
-                    std::is_convertible<optional<U>&&, T>::value ||
-                    std::is_convertible<const optional<U>&, T>::value ||
-                    std::is_convertible<const optional<U>&&, T>::value> {};
-
-// Whether T is constructible or convertible or assignable from optional<U>.
-template <typename T, typename U>
-struct is_constructible_convertible_assignable_from_optional
-    : std::integral_constant<
-          bool, is_constructible_convertible_from_optional<T, U>::value ||
-                    std::is_assignable<T&, optional<U>&>::value ||
-                    std::is_assignable<T&, optional<U>&&>::value ||
-                    std::is_assignable<T&, const optional<U>&>::value ||
-                    std::is_assignable<T&, const optional<U>&&>::value> {};
-
-// Helper function used by [optional.relops], [optional.comp_with_t],
-// for checking whether an expression is convertible to bool.
-bool convertible_to_bool(bool);
-
-// Base class for std::hash<absl::optional<T>>:
-// If std::hash<std::remove_const_t<T>> is enabled, it provides operator() to
-// compute the hash; Otherwise, it is disabled.
-// Reference N4659 23.14.15 [unord.hash].
-template <typename T, typename = size_t>
-struct optional_hash_base {
-  optional_hash_base() = delete;
-  optional_hash_base(const optional_hash_base&) = delete;
-  optional_hash_base(optional_hash_base&&) = delete;
-  optional_hash_base& operator=(const optional_hash_base&) = delete;
-  optional_hash_base& operator=(optional_hash_base&&) = delete;
-};
-
-template <typename T>
-struct optional_hash_base<T, decltype(std::hash<absl::remove_const_t<T> >()(
-                                 std::declval<absl::remove_const_t<T> >()))> {
-  using argument_type = absl::optional<T>;
-  using result_type = size_t;
-  size_t operator()(const absl::optional<T>& opt) const {
-    absl::type_traits_internal::AssertHashEnabled<absl::remove_const_t<T>>();
-    if (opt) {
-      return std::hash<absl::remove_const_t<T> >()(*opt);
-    } else {
-      return static_cast<size_t>(0x297814aaad196e6dULL);
-    }
-  }
-};
-
-}  // namespace optional_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
-
-#endif  // ABSL_TYPES_INTERNAL_OPTIONAL_H_
diff --git a/third_party/abseil_cpp/absl/types/internal/parentheses.h b/third_party/abseil_cpp/absl/types/internal/parentheses.h
deleted file mode 100644
index 5aebee8fde..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/parentheses.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// parentheses.h
-// -----------------------------------------------------------------------------
-//
-// This file contains macros that expand to a left parenthesis and a right
-// parenthesis. These are in their own file and are generated from macros
-// because otherwise clang-format gets confused and clang-format off directives
-// do not help.
-//
-// The parentheses macros are used when wanting to require a rescan before
-// expansion of parenthesized text appearing after a function-style macro name.
-
-#ifndef ABSL_TYPES_INTERNAL_PARENTHESES_H_
-#define ABSL_TYPES_INTERNAL_PARENTHESES_H_
-
-#define ABSL_INTERNAL_LPAREN (
-
-#define ABSL_INTERNAL_RPAREN )
-
-#endif  // ABSL_TYPES_INTERNAL_PARENTHESES_H_
diff --git a/third_party/abseil_cpp/absl/types/internal/span.h b/third_party/abseil_cpp/absl/types/internal/span.h
deleted file mode 100644
index 112612f4bd..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/span.h
+++ /dev/null
@@ -1,128 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-#ifndef ABSL_TYPES_INTERNAL_SPAN_H_
-#define ABSL_TYPES_INTERNAL_SPAN_H_
-
-#include <algorithm>
-#include <cstddef>
-#include <string>
-#include <type_traits>
-
-#include "absl/algorithm/algorithm.h"
-#include "absl/base/internal/throw_delegate.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace span_internal {
-// A constexpr min function
-constexpr size_t Min(size_t a, size_t b) noexcept { return a < b ? a : b; }
-
-// Wrappers for access to container data pointers.
-template <typename C>
-constexpr auto GetDataImpl(C& c, char) noexcept  // NOLINT(runtime/references)
-    -> decltype(c.data()) {
-  return c.data();
-}
-
-// Before C++17, std::string::data returns a const char* in all cases.
-inline char* GetDataImpl(std::string& s,  // NOLINT(runtime/references)
-                         int) noexcept {
-  return &s[0];
-}
-
-template <typename C>
-constexpr auto GetData(C& c) noexcept  // NOLINT(runtime/references)
-    -> decltype(GetDataImpl(c, 0)) {
-  return GetDataImpl(c, 0);
-}
-
-// Detection idioms for size() and data().
-template <typename C>
-using HasSize =
-    std::is_integral<absl::decay_t<decltype(std::declval<C&>().size())>>;
-
-// We want to enable conversion from vector<T*> to Span<const T* const> but
-// disable conversion from vector<Derived> to Span<Base>. Here we use
-// the fact that U** is convertible to Q* const* if and only if Q is the same
-// type or a more cv-qualified version of U.  We also decay the result type of
-// data() to avoid problems with classes which have a member function data()
-// which returns a reference.
-template <typename T, typename C>
-using HasData =
-    std::is_convertible<absl::decay_t<decltype(GetData(std::declval<C&>()))>*,
-                        T* const*>;
-
-// Extracts value type from a Container
-template <typename C>
-struct ElementType {
-  using type = typename absl::remove_reference_t<C>::value_type;
-};
-
-template <typename T, size_t N>
-struct ElementType<T (&)[N]> {
-  using type = T;
-};
-
-template <typename C>
-using ElementT = typename ElementType<C>::type;
-
-template <typename T>
-using EnableIfMutable =
-    typename std::enable_if<!std::is_const<T>::value, int>::type;
-
-template <template <typename> class SpanT, typename T>
-bool EqualImpl(SpanT<T> a, SpanT<T> b) {
-  static_assert(std::is_const<T>::value, "");
-  return absl::equal(a.begin(), a.end(), b.begin(), b.end());
-}
-
-template <template <typename> class SpanT, typename T>
-bool LessThanImpl(SpanT<T> a, SpanT<T> b) {
-  // We can't use value_type since that is remove_cv_t<T>, so we go the long way
-  // around.
-  static_assert(std::is_const<T>::value, "");
-  return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
-}
-
-// The `IsConvertible` classes here are needed because of the
-// `std::is_convertible` bug in libcxx when compiled with GCC. This build
-// configuration is used by Android NDK toolchain. Reference link:
-// https://bugs.llvm.org/show_bug.cgi?id=27538.
-template <typename From, typename To>
-struct IsConvertibleHelper {
- private:
-  static std::true_type testval(To);
-  static std::false_type testval(...);
-
- public:
-  using type = decltype(testval(std::declval<From>()));
-};
-
-template <typename From, typename To>
-struct IsConvertible : IsConvertibleHelper<From, To>::type {};
-
-// TODO(zhangxy): replace `IsConvertible` with `std::is_convertible` once the
-// older version of libcxx is not supported.
-template <typename From, typename To>
-using EnableIfConvertibleTo =
-    typename std::enable_if<IsConvertible<From, To>::value>::type;
-}  // namespace span_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TYPES_INTERNAL_SPAN_H_
diff --git a/third_party/abseil_cpp/absl/types/internal/transform_args.h b/third_party/abseil_cpp/absl/types/internal/transform_args.h
deleted file mode 100644
index 4a0ab42ac4..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/transform_args.h
+++ /dev/null
@@ -1,246 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// transform_args.h
-// -----------------------------------------------------------------------------
-//
-// This file contains a higher-order macro that "transforms" each element of a
-// a variadic argument by a provided secondary macro.
-
-#ifndef ABSL_TYPES_INTERNAL_TRANSFORM_ARGS_H_
-#define ABSL_TYPES_INTERNAL_TRANSFORM_ARGS_H_
-
-//
-// ABSL_INTERNAL_CAT(a, b)
-//
-// This macro takes two arguments and concatenates them together via ## after
-// expansion.
-//
-// Example:
-//
-//   ABSL_INTERNAL_CAT(foo_, bar)
-//
-// Results in:
-//
-//   foo_bar
-#define ABSL_INTERNAL_CAT(a, b) ABSL_INTERNAL_CAT_IMPL(a, b)
-#define ABSL_INTERNAL_CAT_IMPL(a, b) a##b
-
-//
-// ABSL_INTERNAL_TRANSFORM_ARGS(m, ...)
-//
-// This macro takes another macro as an argument followed by a trailing series
-// of additional parameters (up to 32 additional arguments). It invokes the
-// passed-in macro once for each of the additional arguments, with the
-// expansions separated by commas.
-//
-// Example:
-//
-//   ABSL_INTERNAL_TRANSFORM_ARGS(MY_MACRO, a, b, c)
-//
-// Results in:
-//
-//   MY_MACRO(a), MY_MACRO(b), MY_MACRO(c)
-//
-// TODO(calabrese) Handle no arguments as a special case.
-#define ABSL_INTERNAL_TRANSFORM_ARGS(m, ...)             \
-  ABSL_INTERNAL_CAT(ABSL_INTERNAL_TRANSFORM_ARGS,        \
-                    ABSL_INTERNAL_NUM_ARGS(__VA_ARGS__)) \
-  (m, __VA_ARGS__)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS1(m, a0) m(a0)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS2(m, a0, a1) m(a0), m(a1)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS3(m, a0, a1, a2) m(a0), m(a1), m(a2)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS4(m, a0, a1, a2, a3) \
-  m(a0), m(a1), m(a2), m(a3)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS5(m, a0, a1, a2, a3, a4) \
-  m(a0), m(a1), m(a2), m(a3), m(a4)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS6(m, a0, a1, a2, a3, a4, a5) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS7(m, a0, a1, a2, a3, a4, a5, a6) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS8(m, a0, a1, a2, a3, a4, a5, a6, a7) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS9(m, a0, a1, a2, a3, a4, a5, a6, a7, a8) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS10(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9)                                    \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS11(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10)                               \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), m(a10)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS12(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11)                          \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS13(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12)                     \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS14(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13)                \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS15(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14)           \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS16(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15)      \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS17(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS18(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17)                                   \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS19(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18)                              \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS20(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19)                         \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS21(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19, a20)                    \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS22(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19, a20, a21)               \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS23(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19, a20, a21, a22)          \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS24(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \
-                                       a9, a10, a11, a12, a13, a14, a15, a16, \
-                                       a17, a18, a19, a20, a21, a22, a23)     \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS25(m, a0, a1, a2, a3, a4, a5, a6, a7, a8,  \
-                                       a9, a10, a11, a12, a13, a14, a15, a16,  \
-                                       a17, a18, a19, a20, a21, a22, a23, a24) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),        \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18),  \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS26(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25)                         \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS27(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26)                    \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS28(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27)               \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS29(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28)          \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27), \
-      m(a28)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS30(                                       \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,  \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29)     \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),       \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27), \
-      m(a28), m(a29)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS31(                                        \
-    m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,   \
-    a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30) \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),        \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18),  \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27),  \
-      m(a28), m(a29), m(a30)
-
-#define ABSL_INTERNAL_TRANSFORM_ARGS32(m, a0, a1, a2, a3, a4, a5, a6, a7, a8,  \
-                                       a9, a10, a11, a12, a13, a14, a15, a16,  \
-                                       a17, a18, a19, a20, a21, a22, a23, a24, \
-                                       a25, a26, a27, a28, a29, a30, a31)      \
-  m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9),        \
-      m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18),  \
-      m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27),  \
-      m(a28), m(a29), m(a30), m(a31)
-
-#define ABSL_INTERNAL_NUM_ARGS_IMPL(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9,    \
-                                    a10, a11, a12, a13, a14, a15, a16, a17,    \
-                                    a18, a19, a20, a21, a22, a23, a24, a25,    \
-                                    a26, a27, a28, a29, a30, a31, result, ...) \
-  result
-
-#define ABSL_INTERNAL_FORCE_EXPANSION(...) __VA_ARGS__
-
-#define ABSL_INTERNAL_NUM_ARGS(...)                                            \
-  ABSL_INTERNAL_FORCE_EXPANSION(ABSL_INTERNAL_NUM_ARGS_IMPL(                   \
-      __VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, \
-      17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, ))
-
-#endif  // ABSL_TYPES_INTERNAL_TRANSFORM_ARGS_H_
diff --git a/third_party/abseil_cpp/absl/types/internal/variant.h b/third_party/abseil_cpp/absl/types/internal/variant.h
deleted file mode 100644
index 772008c74e..0000000000
--- a/third_party/abseil_cpp/absl/types/internal/variant.h
+++ /dev/null
@@ -1,1646 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Implementation details of absl/types/variant.h, pulled into a
-// separate file to avoid cluttering the top of the API header with
-// implementation details.
-
-#ifndef ABSL_TYPES_variant_internal_H_
-#define ABSL_TYPES_variant_internal_H_
-
-#include <cassert>
-#include <cstddef>
-#include <cstdlib>
-#include <memory>
-#include <stdexcept>
-#include <tuple>
-#include <type_traits>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/identity.h"
-#include "absl/base/internal/inline_variable.h"
-#include "absl/base/internal/invoke.h"
-#include "absl/base/macros.h"
-#include "absl/base/optimization.h"
-#include "absl/meta/type_traits.h"
-#include "absl/types/bad_variant_access.h"
-#include "absl/utility/utility.h"
-
-#if !defined(ABSL_USES_STD_VARIANT)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-template <class... Types>
-class variant;
-
-ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, static_cast<size_t>(-1));
-
-template <class T>
-struct variant_size;
-
-template <std::size_t I, class T>
-struct variant_alternative;
-
-namespace variant_internal {
-
-// NOTE: See specializations below for details.
-template <std::size_t I, class T>
-struct VariantAlternativeSfinae {};
-
-// Requires: I < variant_size_v<T>.
-//
-// Value: The Ith type of Types...
-template <std::size_t I, class T0, class... Tn>
-struct VariantAlternativeSfinae<I, variant<T0, Tn...>>
-    : VariantAlternativeSfinae<I - 1, variant<Tn...>> {};
-
-// Value: T0
-template <class T0, class... Ts>
-struct VariantAlternativeSfinae<0, variant<T0, Ts...>> {
-  using type = T0;
-};
-
-template <std::size_t I, class T>
-using VariantAlternativeSfinaeT = typename VariantAlternativeSfinae<I, T>::type;
-
-// NOTE: Requires T to be a reference type.
-template <class T, class U>
-struct GiveQualsTo;
-
-template <class T, class U>
-struct GiveQualsTo<T&, U> {
-  using type = U&;
-};
-
-template <class T, class U>
-struct GiveQualsTo<T&&, U> {
-  using type = U&&;
-};
-
-template <class T, class U>
-struct GiveQualsTo<const T&, U> {
-  using type = const U&;
-};
-
-template <class T, class U>
-struct GiveQualsTo<const T&&, U> {
-  using type = const U&&;
-};
-
-template <class T, class U>
-struct GiveQualsTo<volatile T&, U> {
-  using type = volatile U&;
-};
-
-template <class T, class U>
-struct GiveQualsTo<volatile T&&, U> {
-  using type = volatile U&&;
-};
-
-template <class T, class U>
-struct GiveQualsTo<volatile const T&, U> {
-  using type = volatile const U&;
-};
-
-template <class T, class U>
-struct GiveQualsTo<volatile const T&&, U> {
-  using type = volatile const U&&;
-};
-
-template <class T, class U>
-using GiveQualsToT = typename GiveQualsTo<T, U>::type;
-
-// Convenience alias, since size_t integral_constant is used a lot in this file.
-template <std::size_t I>
-using SizeT = std::integral_constant<std::size_t, I>;
-
-using NPos = SizeT<variant_npos>;
-
-template <class Variant, class T, class = void>
-struct IndexOfConstructedType {};
-
-template <std::size_t I, class Variant>
-struct VariantAccessResultImpl;
-
-template <std::size_t I, template <class...> class Variantemplate, class... T>
-struct VariantAccessResultImpl<I, Variantemplate<T...>&> {
-  using type = typename absl::variant_alternative<I, variant<T...>>::type&;
-};
-
-template <std::size_t I, template <class...> class Variantemplate, class... T>
-struct VariantAccessResultImpl<I, const Variantemplate<T...>&> {
-  using type =
-      const typename absl::variant_alternative<I, variant<T...>>::type&;
-};
-
-template <std::size_t I, template <class...> class Variantemplate, class... T>
-struct VariantAccessResultImpl<I, Variantemplate<T...>&&> {
-  using type = typename absl::variant_alternative<I, variant<T...>>::type&&;
-};
-
-template <std::size_t I, template <class...> class Variantemplate, class... T>
-struct VariantAccessResultImpl<I, const Variantemplate<T...>&&> {
-  using type =
-      const typename absl::variant_alternative<I, variant<T...>>::type&&;
-};
-
-template <std::size_t I, class Variant>
-using VariantAccessResult =
-    typename VariantAccessResultImpl<I, Variant&&>::type;
-
-// NOTE: This is used instead of std::array to reduce instantiation overhead.
-template <class T, std::size_t Size>
-struct SimpleArray {
-  static_assert(Size != 0, "");
-  T value[Size];
-};
-
-template <class T>
-struct AccessedType {
-  using type = T;
-};
-
-template <class T>
-using AccessedTypeT = typename AccessedType<T>::type;
-
-template <class T, std::size_t Size>
-struct AccessedType<SimpleArray<T, Size>> {
-  using type = AccessedTypeT<T>;
-};
-
-template <class T>
-constexpr T AccessSimpleArray(const T& value) {
-  return value;
-}
-
-template <class T, std::size_t Size, class... SizeT>
-constexpr AccessedTypeT<T> AccessSimpleArray(const SimpleArray<T, Size>& table,
-                                             std::size_t head_index,
-                                             SizeT... tail_indices) {
-  return AccessSimpleArray(table.value[head_index], tail_indices...);
-}
-
-// Note: Intentionally is an alias.
-template <class T>
-using AlwaysZero = SizeT<0>;
-
-template <class Op, class... Vs>
-struct VisitIndicesResultImpl {
-  using type = absl::result_of_t<Op(AlwaysZero<Vs>...)>;
-};
-
-template <class Op, class... Vs>
-using VisitIndicesResultT = typename VisitIndicesResultImpl<Op, Vs...>::type;
-
-template <class ReturnType, class FunctionObject, class EndIndices,
-          class BoundIndices>
-struct MakeVisitationMatrix;
-
-template <class ReturnType, class FunctionObject, std::size_t... Indices>
-constexpr ReturnType call_with_indices(FunctionObject&& function) {
-  static_assert(
-      std::is_same<ReturnType, decltype(std::declval<FunctionObject>()(
-                                   SizeT<Indices>()...))>::value,
-      "Not all visitation overloads have the same return type.");
-  return absl::forward<FunctionObject>(function)(SizeT<Indices>()...);
-}
-
-template <class ReturnType, class FunctionObject, std::size_t... BoundIndices>
-struct MakeVisitationMatrix<ReturnType, FunctionObject, index_sequence<>,
-                            index_sequence<BoundIndices...>> {
-  using ResultType = ReturnType (*)(FunctionObject&&);
-  static constexpr ResultType Run() {
-    return &call_with_indices<ReturnType, FunctionObject,
-                              (BoundIndices - 1)...>;
-  }
-};
-
-template <typename Is, std::size_t J>
-struct AppendToIndexSequence;
-
-template <typename Is, std::size_t J>
-using AppendToIndexSequenceT = typename AppendToIndexSequence<Is, J>::type;
-
-template <std::size_t... Is, std::size_t J>
-struct AppendToIndexSequence<index_sequence<Is...>, J> {
-  using type = index_sequence<Is..., J>;
-};
-
-template <class ReturnType, class FunctionObject, class EndIndices,
-          class CurrIndices, class BoundIndices>
-struct MakeVisitationMatrixImpl;
-
-template <class ReturnType, class FunctionObject, class EndIndices,
-          std::size_t... CurrIndices, class BoundIndices>
-struct MakeVisitationMatrixImpl<ReturnType, FunctionObject, EndIndices,
-                                index_sequence<CurrIndices...>, BoundIndices> {
-  using ResultType = SimpleArray<
-      typename MakeVisitationMatrix<ReturnType, FunctionObject, EndIndices,
-                                    index_sequence<>>::ResultType,
-      sizeof...(CurrIndices)>;
-
-  static constexpr ResultType Run() {
-    return {{MakeVisitationMatrix<
-        ReturnType, FunctionObject, EndIndices,
-        AppendToIndexSequenceT<BoundIndices, CurrIndices>>::Run()...}};
-  }
-};
-
-template <class ReturnType, class FunctionObject, std::size_t HeadEndIndex,
-          std::size_t... TailEndIndices, std::size_t... BoundIndices>
-struct MakeVisitationMatrix<ReturnType, FunctionObject,
-                            index_sequence<HeadEndIndex, TailEndIndices...>,
-                            index_sequence<BoundIndices...>>
-    : MakeVisitationMatrixImpl<ReturnType, FunctionObject,
-                               index_sequence<TailEndIndices...>,
-                               absl::make_index_sequence<HeadEndIndex>,
-                               index_sequence<BoundIndices...>> {};
-
-struct UnreachableSwitchCase {
-  template <class Op>
-  [[noreturn]] static VisitIndicesResultT<Op, std::size_t> Run(
-      Op&& /*ignored*/) {
-#if ABSL_HAVE_BUILTIN(__builtin_unreachable) || \
-    (defined(__GNUC__) && !defined(__clang__))
-    __builtin_unreachable();
-#elif defined(_MSC_VER)
-    __assume(false);
-#else
-    // Try to use assert of false being identified as an unreachable intrinsic.
-    // NOTE: We use assert directly to increase chances of exploiting an assume
-    //       intrinsic.
-    assert(false);  // NOLINT
-
-    // Hack to silence potential no return warning -- cause an infinite loop.
-    return Run(absl::forward<Op>(op));
-#endif  // Checks for __builtin_unreachable
-  }
-};
-
-template <class Op, std::size_t I>
-struct ReachableSwitchCase {
-  static VisitIndicesResultT<Op, std::size_t> Run(Op&& op) {
-    return absl::base_internal::invoke(absl::forward<Op>(op), SizeT<I>());
-  }
-};
-
-// The number 33 is just a guess at a reasonable maximum to our switch. It is
-// not based on any analysis. The reason it is a power of 2 plus 1 instead of a
-// power of 2 is because the number was picked to correspond to a power of 2
-// amount of "normal" alternatives, plus one for the possibility of the user
-// providing "monostate" in addition to the more natural alternatives.
-ABSL_INTERNAL_INLINE_CONSTEXPR(std::size_t, MaxUnrolledVisitCases, 33);
-
-// Note: The default-definition is for unreachable cases.
-template <bool IsReachable>
-struct PickCaseImpl {
-  template <class Op, std::size_t I>
-  using Apply = UnreachableSwitchCase;
-};
-
-template <>
-struct PickCaseImpl</*IsReachable =*/true> {
-  template <class Op, std::size_t I>
-  using Apply = ReachableSwitchCase<Op, I>;
-};
-
-// Note: This form of dance with template aliases is to make sure that we
-//       instantiate a number of templates proportional to the number of variant
-//       alternatives rather than a number of templates proportional to our
-//       maximum unrolled amount of visitation cases (aliases are effectively
-//       "free" whereas other template instantiations are costly).
-template <class Op, std::size_t I, std::size_t EndIndex>
-using PickCase = typename PickCaseImpl<(I < EndIndex)>::template Apply<Op, I>;
-
-template <class ReturnType>
-[[noreturn]] ReturnType TypedThrowBadVariantAccess() {
-  absl::variant_internal::ThrowBadVariantAccess();
-}
-
-// Given N variant sizes, determine the number of cases there would need to be
-// in a single switch-statement that would cover every possibility in the
-// corresponding N-ary visit operation.
-template <std::size_t... NumAlternatives>
-struct NumCasesOfSwitch;
-
-template <std::size_t HeadNumAlternatives, std::size_t... TailNumAlternatives>
-struct NumCasesOfSwitch<HeadNumAlternatives, TailNumAlternatives...> {
-  static constexpr std::size_t value =
-      (HeadNumAlternatives + 1) *
-      NumCasesOfSwitch<TailNumAlternatives...>::value;
-};
-
-template <>
-struct NumCasesOfSwitch<> {
-  static constexpr std::size_t value = 1;
-};
-
-// A switch statement optimizes better than the table of function pointers.
-template <std::size_t EndIndex>
-struct VisitIndicesSwitch {
-  static_assert(EndIndex <= MaxUnrolledVisitCases,
-                "Maximum unrolled switch size exceeded.");
-
-  template <class Op>
-  static VisitIndicesResultT<Op, std::size_t> Run(Op&& op, std::size_t i) {
-    switch (i) {
-      case 0:
-        return PickCase<Op, 0, EndIndex>::Run(absl::forward<Op>(op));
-      case 1:
-        return PickCase<Op, 1, EndIndex>::Run(absl::forward<Op>(op));
-      case 2:
-        return PickCase<Op, 2, EndIndex>::Run(absl::forward<Op>(op));
-      case 3:
-        return PickCase<Op, 3, EndIndex>::Run(absl::forward<Op>(op));
-      case 4:
-        return PickCase<Op, 4, EndIndex>::Run(absl::forward<Op>(op));
-      case 5:
-        return PickCase<Op, 5, EndIndex>::Run(absl::forward<Op>(op));
-      case 6:
-        return PickCase<Op, 6, EndIndex>::Run(absl::forward<Op>(op));
-      case 7:
-        return PickCase<Op, 7, EndIndex>::Run(absl::forward<Op>(op));
-      case 8:
-        return PickCase<Op, 8, EndIndex>::Run(absl::forward<Op>(op));
-      case 9:
-        return PickCase<Op, 9, EndIndex>::Run(absl::forward<Op>(op));
-      case 10:
-        return PickCase<Op, 10, EndIndex>::Run(absl::forward<Op>(op));
-      case 11:
-        return PickCase<Op, 11, EndIndex>::Run(absl::forward<Op>(op));
-      case 12:
-        return PickCase<Op, 12, EndIndex>::Run(absl::forward<Op>(op));
-      case 13:
-        return PickCase<Op, 13, EndIndex>::Run(absl::forward<Op>(op));
-      case 14:
-        return PickCase<Op, 14, EndIndex>::Run(absl::forward<Op>(op));
-      case 15:
-        return PickCase<Op, 15, EndIndex>::Run(absl::forward<Op>(op));
-      case 16:
-        return PickCase<Op, 16, EndIndex>::Run(absl::forward<Op>(op));
-      case 17:
-        return PickCase<Op, 17, EndIndex>::Run(absl::forward<Op>(op));
-      case 18:
-        return PickCase<Op, 18, EndIndex>::Run(absl::forward<Op>(op));
-      case 19:
-        return PickCase<Op, 19, EndIndex>::Run(absl::forward<Op>(op));
-      case 20:
-        return PickCase<Op, 20, EndIndex>::Run(absl::forward<Op>(op));
-      case 21:
-        return PickCase<Op, 21, EndIndex>::Run(absl::forward<Op>(op));
-      case 22:
-        return PickCase<Op, 22, EndIndex>::Run(absl::forward<Op>(op));
-      case 23:
-        return PickCase<Op, 23, EndIndex>::Run(absl::forward<Op>(op));
-      case 24:
-        return PickCase<Op, 24, EndIndex>::Run(absl::forward<Op>(op));
-      case 25:
-        return PickCase<Op, 25, EndIndex>::Run(absl::forward<Op>(op));
-      case 26:
-        return PickCase<Op, 26, EndIndex>::Run(absl::forward<Op>(op));
-      case 27:
-        return PickCase<Op, 27, EndIndex>::Run(absl::forward<Op>(op));
-      case 28:
-        return PickCase<Op, 28, EndIndex>::Run(absl::forward<Op>(op));
-      case 29:
-        return PickCase<Op, 29, EndIndex>::Run(absl::forward<Op>(op));
-      case 30:
-        return PickCase<Op, 30, EndIndex>::Run(absl::forward<Op>(op));
-      case 31:
-        return PickCase<Op, 31, EndIndex>::Run(absl::forward<Op>(op));
-      case 32:
-        return PickCase<Op, 32, EndIndex>::Run(absl::forward<Op>(op));
-      default:
-        ABSL_ASSERT(i == variant_npos);
-        return absl::base_internal::invoke(absl::forward<Op>(op), NPos());
-    }
-  }
-};
-
-template <std::size_t... EndIndices>
-struct VisitIndicesFallback {
-  template <class Op, class... SizeT>
-  static VisitIndicesResultT<Op, SizeT...> Run(Op&& op, SizeT... indices) {
-    return AccessSimpleArray(
-        MakeVisitationMatrix<VisitIndicesResultT<Op, SizeT...>, Op,
-                             index_sequence<(EndIndices + 1)...>,
-                             index_sequence<>>::Run(),
-        (indices + 1)...)(absl::forward<Op>(op));
-  }
-};
-
-// Take an N-dimensional series of indices and convert them into a single index
-// without loss of information. The purpose of this is to be able to convert an
-// N-ary visit operation into a single switch statement.
-template <std::size_t...>
-struct FlattenIndices;
-
-template <std::size_t HeadSize, std::size_t... TailSize>
-struct FlattenIndices<HeadSize, TailSize...> {
-  template<class... SizeType>
-  static constexpr std::size_t Run(std::size_t head, SizeType... tail) {
-    return head + HeadSize * FlattenIndices<TailSize...>::Run(tail...);
-  }
-};
-
-template <>
-struct FlattenIndices<> {
-  static constexpr std::size_t Run() { return 0; }
-};
-
-// Take a single "flattened" index (flattened by FlattenIndices) and determine
-// the value of the index of one of the logically represented dimensions.
-template <std::size_t I, std::size_t IndexToGet, std::size_t HeadSize,
-          std::size_t... TailSize>
-struct UnflattenIndex {
-  static constexpr std::size_t value =
-      UnflattenIndex<I / HeadSize, IndexToGet - 1, TailSize...>::value;
-};
-
-template <std::size_t I, std::size_t HeadSize, std::size_t... TailSize>
-struct UnflattenIndex<I, 0, HeadSize, TailSize...> {
-  static constexpr std::size_t value = (I % HeadSize);
-};
-
-// The backend for converting an N-ary visit operation into a unary visit.
-template <class IndexSequence, std::size_t... EndIndices>
-struct VisitIndicesVariadicImpl;
-
-template <std::size_t... N, std::size_t... EndIndices>
-struct VisitIndicesVariadicImpl<absl::index_sequence<N...>, EndIndices...> {
-  // A type that can take an N-ary function object and converts it to a unary
-  // function object that takes a single, flattened index, and "unflattens" it
-  // into its individual dimensions when forwarding to the wrapped object.
-  template <class Op>
-  struct FlattenedOp {
-    template <std::size_t I>
-    VisitIndicesResultT<Op, decltype(EndIndices)...> operator()(
-        SizeT<I> /*index*/) && {
-      return base_internal::invoke(
-          absl::forward<Op>(op),
-          SizeT<UnflattenIndex<I, N, (EndIndices + 1)...>::value -
-                std::size_t{1}>()...);
-    }
-
-    Op&& op;
-  };
-
-  template <class Op, class... SizeType>
-  static VisitIndicesResultT<Op, decltype(EndIndices)...> Run(
-      Op&& op, SizeType... i) {
-    return VisitIndicesSwitch<NumCasesOfSwitch<EndIndices...>::value>::Run(
-        FlattenedOp<Op>{absl::forward<Op>(op)},
-        FlattenIndices<(EndIndices + std::size_t{1})...>::Run(
-            (i + std::size_t{1})...));
-  }
-};
-
-template <std::size_t... EndIndices>
-struct VisitIndicesVariadic
-    : VisitIndicesVariadicImpl<absl::make_index_sequence<sizeof...(EndIndices)>,
-                               EndIndices...> {};
-
-// This implementation will flatten N-ary visit operations into a single switch
-// statement when the number of cases would be less than our maximum specified
-// switch-statement size.
-// TODO(calabrese)
-//   Based on benchmarks, determine whether the function table approach actually
-//   does optimize better than a chain of switch statements and possibly update
-//   the implementation accordingly. Also consider increasing the maximum switch
-//   size.
-template <std::size_t... EndIndices>
-struct VisitIndices
-    : absl::conditional_t<(NumCasesOfSwitch<EndIndices...>::value <=
-                           MaxUnrolledVisitCases),
-                          VisitIndicesVariadic<EndIndices...>,
-                          VisitIndicesFallback<EndIndices...>> {};
-
-template <std::size_t EndIndex>
-struct VisitIndices<EndIndex>
-    : absl::conditional_t<(EndIndex <= MaxUnrolledVisitCases),
-                          VisitIndicesSwitch<EndIndex>,
-                          VisitIndicesFallback<EndIndex>> {};
-
-// Suppress bogus warning on MSVC: MSVC complains that the `reinterpret_cast`
-// below is returning the address of a temporary or local object.
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4172)
-#endif  // _MSC_VER
-
-// TODO(calabrese) std::launder
-// TODO(calabrese) constexpr
-// NOTE: DO NOT REMOVE the `inline` keyword as it is necessary to work around a
-// MSVC bug. See https://github.com/abseil/abseil-cpp/issues/129 for details.
-template <class Self, std::size_t I>
-inline VariantAccessResult<I, Self> AccessUnion(Self&& self, SizeT<I> /*i*/) {
-  return reinterpret_cast<VariantAccessResult<I, Self>>(self);
-}
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif  // _MSC_VER
-
-template <class T>
-void DeducedDestroy(T& self) {  // NOLINT
-  self.~T();
-}
-
-// NOTE: This type exists as a single entity for variant and its bases to
-// befriend. It contains helper functionality that manipulates the state of the
-// variant, such as the implementation of things like assignment and emplace
-// operations.
-struct VariantCoreAccess {
-  template <class VariantType>
-  static typename VariantType::Variant& Derived(VariantType& self) {  // NOLINT
-    return static_cast<typename VariantType::Variant&>(self);
-  }
-
-  template <class VariantType>
-  static const typename VariantType::Variant& Derived(
-      const VariantType& self) {  // NOLINT
-    return static_cast<const typename VariantType::Variant&>(self);
-  }
-
-  template <class VariantType>
-  static void Destroy(VariantType& self) {  // NOLINT
-    Derived(self).destroy();
-    self.index_ = absl::variant_npos;
-  }
-
-  template <class Variant>
-  static void SetIndex(Variant& self, std::size_t i) {  // NOLINT
-    self.index_ = i;
-  }
-
-  template <class Variant>
-  static void InitFrom(Variant& self, Variant&& other) {  // NOLINT
-    VisitIndices<absl::variant_size<Variant>::value>::Run(
-        InitFromVisitor<Variant, Variant&&>{&self,
-                                            std::forward<Variant>(other)},
-        other.index());
-    self.index_ = other.index();
-  }
-
-  // Access a variant alternative, assuming the index is correct.
-  template <std::size_t I, class Variant>
-  static VariantAccessResult<I, Variant> Access(Variant&& self) {
-    // This cast instead of invocation of AccessUnion with an rvalue is a
-    // workaround for msvc. Without this there is a runtime failure when dealing
-    // with rvalues.
-    // TODO(calabrese) Reduce test case and find a simpler workaround.
-    return static_cast<VariantAccessResult<I, Variant>>(
-        variant_internal::AccessUnion(self.state_, SizeT<I>()));
-  }
-
-  // Access a variant alternative, throwing if the index is incorrect.
-  template <std::size_t I, class Variant>
-  static VariantAccessResult<I, Variant> CheckedAccess(Variant&& self) {
-    if (ABSL_PREDICT_FALSE(self.index_ != I)) {
-      TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>();
-    }
-
-    return Access<I>(absl::forward<Variant>(self));
-  }
-
-  // The implementation of the move-assignment operation for a variant.
-  template <class VType>
-  struct MoveAssignVisitor {
-    using DerivedType = typename VType::Variant;
-    template <std::size_t NewIndex>
-    void operator()(SizeT<NewIndex> /*new_i*/) const {
-      if (left->index_ == NewIndex) {
-        Access<NewIndex>(*left) = std::move(Access<NewIndex>(*right));
-      } else {
-        Derived(*left).template emplace<NewIndex>(
-            std::move(Access<NewIndex>(*right)));
-      }
-    }
-
-    void operator()(SizeT<absl::variant_npos> /*new_i*/) const {
-      Destroy(*left);
-    }
-
-    VType* left;
-    VType* right;
-  };
-
-  template <class VType>
-  static MoveAssignVisitor<VType> MakeMoveAssignVisitor(VType* left,
-                                                        VType* other) {
-    return {left, other};
-  }
-
-  // The implementation of the assignment operation for a variant.
-  template <class VType>
-  struct CopyAssignVisitor {
-    using DerivedType = typename VType::Variant;
-    template <std::size_t NewIndex>
-    void operator()(SizeT<NewIndex> /*new_i*/) const {
-      using New =
-          typename absl::variant_alternative<NewIndex, DerivedType>::type;
-
-      if (left->index_ == NewIndex) {
-        Access<NewIndex>(*left) = Access<NewIndex>(*right);
-      } else if (std::is_nothrow_copy_constructible<New>::value ||
-                 !std::is_nothrow_move_constructible<New>::value) {
-        Derived(*left).template emplace<NewIndex>(Access<NewIndex>(*right));
-      } else {
-        Derived(*left) = DerivedType(Derived(*right));
-      }
-    }
-
-    void operator()(SizeT<absl::variant_npos> /*new_i*/) const {
-      Destroy(*left);
-    }
-
-    VType* left;
-    const VType* right;
-  };
-
-  template <class VType>
-  static CopyAssignVisitor<VType> MakeCopyAssignVisitor(VType* left,
-                                                        const VType& other) {
-    return {left, &other};
-  }
-
-  // The implementation of conversion-assignment operations for variant.
-  template <class Left, class QualifiedNew>
-  struct ConversionAssignVisitor {
-    using NewIndex =
-        variant_internal::IndexOfConstructedType<Left, QualifiedNew>;
-
-    void operator()(SizeT<NewIndex::value> /*old_i*/
-                    ) const {
-      Access<NewIndex::value>(*left) = absl::forward<QualifiedNew>(other);
-    }
-
-    template <std::size_t OldIndex>
-    void operator()(SizeT<OldIndex> /*old_i*/
-                    ) const {
-      using New =
-          typename absl::variant_alternative<NewIndex::value, Left>::type;
-      if (std::is_nothrow_constructible<New, QualifiedNew>::value ||
-          !std::is_nothrow_move_constructible<New>::value) {
-        left->template emplace<NewIndex::value>(
-            absl::forward<QualifiedNew>(other));
-      } else {
-        // the standard says "equivalent to
-        // operator=(variant(std::forward<T>(t)))", but we use `emplace` here
-        // because the variant's move assignment operator could be deleted.
-        left->template emplace<NewIndex::value>(
-            New(absl::forward<QualifiedNew>(other)));
-      }
-    }
-
-    Left* left;
-    QualifiedNew&& other;
-  };
-
-  template <class Left, class QualifiedNew>
-  static ConversionAssignVisitor<Left, QualifiedNew>
-  MakeConversionAssignVisitor(Left* left, QualifiedNew&& qual) {
-    return {left, absl::forward<QualifiedNew>(qual)};
-  }
-
-  // Backend for operations for `emplace()` which destructs `*self` then
-  // construct a new alternative with `Args...`.
-  template <std::size_t NewIndex, class Self, class... Args>
-  static typename absl::variant_alternative<NewIndex, Self>::type& Replace(
-      Self* self, Args&&... args) {
-    Destroy(*self);
-    using New = typename absl::variant_alternative<NewIndex, Self>::type;
-    New* const result = ::new (static_cast<void*>(&self->state_))
-        New(absl::forward<Args>(args)...);
-    self->index_ = NewIndex;
-    return *result;
-  }
-
-  template <class LeftVariant, class QualifiedRightVariant>
-  struct InitFromVisitor {
-    template <std::size_t NewIndex>
-    void operator()(SizeT<NewIndex> /*new_i*/) const {
-      using Alternative =
-          typename variant_alternative<NewIndex, LeftVariant>::type;
-      ::new (static_cast<void*>(&left->state_)) Alternative(
-          Access<NewIndex>(std::forward<QualifiedRightVariant>(right)));
-    }
-
-    void operator()(SizeT<absl::variant_npos> /*new_i*/) const {
-      // This space intentionally left blank.
-    }
-    LeftVariant* left;
-    QualifiedRightVariant&& right;
-  };
-};
-
-template <class Expected, class... T>
-struct IndexOfImpl;
-
-template <class Expected>
-struct IndexOfImpl<Expected> {
-  using IndexFromEnd = SizeT<0>;
-  using MatchedIndexFromEnd = IndexFromEnd;
-  using MultipleMatches = std::false_type;
-};
-
-template <class Expected, class Head, class... Tail>
-struct IndexOfImpl<Expected, Head, Tail...> : IndexOfImpl<Expected, Tail...> {
-  using IndexFromEnd =
-      SizeT<IndexOfImpl<Expected, Tail...>::IndexFromEnd::value + 1>;
-};
-
-template <class Expected, class... Tail>
-struct IndexOfImpl<Expected, Expected, Tail...>
-    : IndexOfImpl<Expected, Tail...> {
-  using IndexFromEnd =
-      SizeT<IndexOfImpl<Expected, Tail...>::IndexFromEnd::value + 1>;
-  using MatchedIndexFromEnd = IndexFromEnd;
-  using MultipleMatches = std::integral_constant<
-      bool, IndexOfImpl<Expected, Tail...>::MatchedIndexFromEnd::value != 0>;
-};
-
-template <class Expected, class... Types>
-struct IndexOfMeta {
-  using Results = IndexOfImpl<Expected, Types...>;
-  static_assert(!Results::MultipleMatches::value,
-                "Attempted to access a variant by specifying a type that "
-                "matches more than one alternative.");
-  static_assert(Results::MatchedIndexFromEnd::value != 0,
-                "Attempted to access a variant by specifying a type that does "
-                "not match any alternative.");
-  using type = SizeT<sizeof...(Types) - Results::MatchedIndexFromEnd::value>;
-};
-
-template <class Expected, class... Types>
-using IndexOf = typename IndexOfMeta<Expected, Types...>::type;
-
-template <class Variant, class T, std::size_t CurrIndex>
-struct UnambiguousIndexOfImpl;
-
-// Terminating case encountered once we've checked all of the alternatives
-template <class T, std::size_t CurrIndex>
-struct UnambiguousIndexOfImpl<variant<>, T, CurrIndex> : SizeT<CurrIndex> {};
-
-// Case where T is not Head
-template <class Head, class... Tail, class T, std::size_t CurrIndex>
-struct UnambiguousIndexOfImpl<variant<Head, Tail...>, T, CurrIndex>
-    : UnambiguousIndexOfImpl<variant<Tail...>, T, CurrIndex + 1>::type {};
-
-// Case where T is Head
-template <class Head, class... Tail, std::size_t CurrIndex>
-struct UnambiguousIndexOfImpl<variant<Head, Tail...>, Head, CurrIndex>
-    : SizeT<UnambiguousIndexOfImpl<variant<Tail...>, Head, 0>::value ==
-                    sizeof...(Tail)
-                ? CurrIndex
-                : CurrIndex + sizeof...(Tail) + 1> {};
-
-template <class Variant, class T>
-struct UnambiguousIndexOf;
-
-struct NoMatch {
-  struct type {};
-};
-
-template <class... Alts, class T>
-struct UnambiguousIndexOf<variant<Alts...>, T>
-    : std::conditional<UnambiguousIndexOfImpl<variant<Alts...>, T, 0>::value !=
-                           sizeof...(Alts),
-                       UnambiguousIndexOfImpl<variant<Alts...>, T, 0>,
-                       NoMatch>::type::type {};
-
-template <class T, std::size_t /*Dummy*/>
-using UnambiguousTypeOfImpl = T;
-
-template <class Variant, class T>
-using UnambiguousTypeOfT =
-    UnambiguousTypeOfImpl<T, UnambiguousIndexOf<Variant, T>::value>;
-
-template <class H, class... T>
-class VariantStateBase;
-
-// This is an implementation of the "imaginary function" that is described in
-// [variant.ctor]
-// It is used in order to determine which alternative to construct during
-// initialization from some type T.
-template <class Variant, std::size_t I = 0>
-struct ImaginaryFun;
-
-template <std::size_t I>
-struct ImaginaryFun<variant<>, I> {
-  static void Run() = delete;
-};
-
-template <class H, class... T, std::size_t I>
-struct ImaginaryFun<variant<H, T...>, I> : ImaginaryFun<variant<T...>, I + 1> {
-  using ImaginaryFun<variant<T...>, I + 1>::Run;
-
-  // NOTE: const& and && are used instead of by-value due to lack of guaranteed
-  // move elision of C++17. This may have other minor differences, but tests
-  // pass.
-  static SizeT<I> Run(const H&, SizeT<I>);
-  static SizeT<I> Run(H&&, SizeT<I>);
-};
-
-// The following metafunctions are used in constructor and assignment
-// constraints.
-template <class Self, class T>
-struct IsNeitherSelfNorInPlace : std::true_type {};
-
-template <class Self>
-struct IsNeitherSelfNorInPlace<Self, Self> : std::false_type {};
-
-template <class Self, class T>
-struct IsNeitherSelfNorInPlace<Self, in_place_type_t<T>> : std::false_type {};
-
-template <class Self, std::size_t I>
-struct IsNeitherSelfNorInPlace<Self, in_place_index_t<I>> : std::false_type {};
-
-template <class Variant, class T, class = void>
-struct ConversionIsPossibleImpl : std::false_type {};
-
-template <class Variant, class T>
-struct ConversionIsPossibleImpl<
-    Variant, T,
-    void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {}))>>
-    : std::true_type {};
-
-template <class Variant, class T>
-struct ConversionIsPossible : ConversionIsPossibleImpl<Variant, T>::type {};
-
-template <class Variant, class T>
-struct IndexOfConstructedType<
-    Variant, T,
-    void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {}))>>
-    : decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {})) {};
-
-template <std::size_t... Is>
-struct ContainsVariantNPos
-    : absl::negation<std::is_same<  // NOLINT
-          absl::integer_sequence<bool, 0 <= Is...>,
-          absl::integer_sequence<bool, Is != absl::variant_npos...>>> {};
-
-template <class Op, class... QualifiedVariants>
-using RawVisitResult =
-    absl::result_of_t<Op(VariantAccessResult<0, QualifiedVariants>...)>;
-
-// NOTE: The spec requires that all return-paths yield the same type and is not
-// SFINAE-friendly, so we can deduce the return type by examining the first
-// result. If it's not callable, then we get an error, but are compliant and
-// fast to compile.
-// TODO(calabrese) Possibly rewrite in a way that yields better compile errors
-// at the cost of longer compile-times.
-template <class Op, class... QualifiedVariants>
-struct VisitResultImpl {
-  using type =
-      absl::result_of_t<Op(VariantAccessResult<0, QualifiedVariants>...)>;
-};
-
-// Done in two steps intentionally so that we don't cause substitution to fail.
-template <class Op, class... QualifiedVariants>
-using VisitResult = typename VisitResultImpl<Op, QualifiedVariants...>::type;
-
-template <class Op, class... QualifiedVariants>
-struct PerformVisitation {
-  using ReturnType = VisitResult<Op, QualifiedVariants...>;
-
-  template <std::size_t... Is>
-  constexpr ReturnType operator()(SizeT<Is>... indices) const {
-    return Run(typename ContainsVariantNPos<Is...>::type{},
-               absl::index_sequence_for<QualifiedVariants...>(), indices...);
-  }
-
-  template <std::size_t... TupIs, std::size_t... Is>
-  constexpr ReturnType Run(std::false_type /*has_valueless*/,
-                           index_sequence<TupIs...>, SizeT<Is>...) const {
-    static_assert(
-        std::is_same<ReturnType,
-                     absl::result_of_t<Op(VariantAccessResult<
-                                          Is, QualifiedVariants>...)>>::value,
-        "All visitation overloads must have the same return type.");
-    return absl::base_internal::invoke(
-        absl::forward<Op>(op),
-        VariantCoreAccess::Access<Is>(
-            absl::forward<QualifiedVariants>(std::get<TupIs>(variant_tup)))...);
-  }
-
-  template <std::size_t... TupIs, std::size_t... Is>
-  [[noreturn]] ReturnType Run(std::true_type /*has_valueless*/,
-                              index_sequence<TupIs...>, SizeT<Is>...) const {
-    absl::variant_internal::ThrowBadVariantAccess();
-  }
-
-  // TODO(calabrese) Avoid using a tuple, which causes lots of instantiations
-  // Attempts using lambda variadic captures fail on current GCC.
-  std::tuple<QualifiedVariants&&...> variant_tup;
-  Op&& op;
-};
-
-template <class... T>
-union Union;
-
-// We want to allow for variant<> to be trivial. For that, we need the default
-// constructor to be trivial, which means we can't define it ourselves.
-// Instead, we use a non-default constructor that takes NoopConstructorTag
-// that doesn't affect the triviality of the types.
-struct NoopConstructorTag {};
-
-template <std::size_t I>
-struct EmplaceTag {};
-
-template <>
-union Union<> {
-  constexpr explicit Union(NoopConstructorTag) noexcept {}
-};
-
-// Suppress bogus warning on MSVC: MSVC complains that Union<T...> has a defined
-// deleted destructor from the `std::is_destructible` check below.
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4624)
-#endif  // _MSC_VER
-
-template <class Head, class... Tail>
-union Union<Head, Tail...> {
-  using TailUnion = Union<Tail...>;
-
-  explicit constexpr Union(NoopConstructorTag /*tag*/) noexcept
-      : tail(NoopConstructorTag()) {}
-
-  template <class... P>
-  explicit constexpr Union(EmplaceTag<0>, P&&... args)
-      : head(absl::forward<P>(args)...) {}
-
-  template <std::size_t I, class... P>
-  explicit constexpr Union(EmplaceTag<I>, P&&... args)
-      : tail(EmplaceTag<I - 1>{}, absl::forward<P>(args)...) {}
-
-  Head head;
-  TailUnion tail;
-};
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif  // _MSC_VER
-
-// TODO(calabrese) Just contain a Union in this union (certain configs fail).
-template <class... T>
-union DestructibleUnionImpl;
-
-template <>
-union DestructibleUnionImpl<> {
-  constexpr explicit DestructibleUnionImpl(NoopConstructorTag) noexcept {}
-};
-
-template <class Head, class... Tail>
-union DestructibleUnionImpl<Head, Tail...> {
-  using TailUnion = DestructibleUnionImpl<Tail...>;
-
-  explicit constexpr DestructibleUnionImpl(NoopConstructorTag /*tag*/) noexcept
-      : tail(NoopConstructorTag()) {}
-
-  template <class... P>
-  explicit constexpr DestructibleUnionImpl(EmplaceTag<0>, P&&... args)
-      : head(absl::forward<P>(args)...) {}
-
-  template <std::size_t I, class... P>
-  explicit constexpr DestructibleUnionImpl(EmplaceTag<I>, P&&... args)
-      : tail(EmplaceTag<I - 1>{}, absl::forward<P>(args)...) {}
-
-  ~DestructibleUnionImpl() {}
-
-  Head head;
-  TailUnion tail;
-};
-
-// This union type is destructible even if one or more T are not trivially
-// destructible. In the case that all T are trivially destructible, then so is
-// this resultant type.
-template <class... T>
-using DestructibleUnion =
-    absl::conditional_t<std::is_destructible<Union<T...>>::value, Union<T...>,
-                        DestructibleUnionImpl<T...>>;
-
-// Deepest base, containing the actual union and the discriminator
-template <class H, class... T>
-class VariantStateBase {
- protected:
-  using Variant = variant<H, T...>;
-
-  template <class LazyH = H,
-            class ConstructibleH = absl::enable_if_t<
-                std::is_default_constructible<LazyH>::value, LazyH>>
-  constexpr VariantStateBase() noexcept(
-      std::is_nothrow_default_constructible<ConstructibleH>::value)
-      : state_(EmplaceTag<0>()), index_(0) {}
-
-  template <std::size_t I, class... P>
-  explicit constexpr VariantStateBase(EmplaceTag<I> tag, P&&... args)
-      : state_(tag, absl::forward<P>(args)...), index_(I) {}
-
-  explicit constexpr VariantStateBase(NoopConstructorTag)
-      : state_(NoopConstructorTag()), index_(variant_npos) {}
-
-  void destroy() {}  // Does nothing (shadowed in child if non-trivial)
-
-  DestructibleUnion<H, T...> state_;
-  std::size_t index_;
-};
-
-using absl::internal::identity;
-
-// OverloadSet::Overload() is a unary function which is overloaded to
-// take any of the element types of the variant, by reference-to-const.
-// The return type of the overload on T is identity<T>, so that you
-// can statically determine which overload was called.
-//
-// Overload() is not defined, so it can only be called in unevaluated
-// contexts.
-template <typename... Ts>
-struct OverloadSet;
-
-template <typename T, typename... Ts>
-struct OverloadSet<T, Ts...> : OverloadSet<Ts...> {
-  using Base = OverloadSet<Ts...>;
-  static identity<T> Overload(const T&);
-  using Base::Overload;
-};
-
-template <>
-struct OverloadSet<> {
-  // For any case not handled above.
-  static void Overload(...);
-};
-
-template <class T>
-using LessThanResult = decltype(std::declval<T>() < std::declval<T>());
-
-template <class T>
-using GreaterThanResult = decltype(std::declval<T>() > std::declval<T>());
-
-template <class T>
-using LessThanOrEqualResult = decltype(std::declval<T>() <= std::declval<T>());
-
-template <class T>
-using GreaterThanOrEqualResult =
-    decltype(std::declval<T>() >= std::declval<T>());
-
-template <class T>
-using EqualResult = decltype(std::declval<T>() == std::declval<T>());
-
-template <class T>
-using NotEqualResult = decltype(std::declval<T>() != std::declval<T>());
-
-using type_traits_internal::is_detected_convertible;
-
-template <class... T>
-using RequireAllHaveEqualT = absl::enable_if_t<
-    absl::conjunction<is_detected_convertible<bool, EqualResult, T>...>::value,
-    bool>;
-
-template <class... T>
-using RequireAllHaveNotEqualT =
-    absl::enable_if_t<absl::conjunction<is_detected_convertible<
-                          bool, NotEqualResult, T>...>::value,
-                      bool>;
-
-template <class... T>
-using RequireAllHaveLessThanT =
-    absl::enable_if_t<absl::conjunction<is_detected_convertible<
-                          bool, LessThanResult, T>...>::value,
-                      bool>;
-
-template <class... T>
-using RequireAllHaveLessThanOrEqualT =
-    absl::enable_if_t<absl::conjunction<is_detected_convertible<
-                          bool, LessThanOrEqualResult, T>...>::value,
-                      bool>;
-
-template <class... T>
-using RequireAllHaveGreaterThanOrEqualT =
-    absl::enable_if_t<absl::conjunction<is_detected_convertible<
-                          bool, GreaterThanOrEqualResult, T>...>::value,
-                      bool>;
-
-template <class... T>
-using RequireAllHaveGreaterThanT =
-    absl::enable_if_t<absl::conjunction<is_detected_convertible<
-                          bool, GreaterThanResult, T>...>::value,
-                      bool>;
-
-// Helper template containing implementations details of variant that can't go
-// in the private section. For convenience, this takes the variant type as a
-// single template parameter.
-template <typename T>
-struct VariantHelper;
-
-template <typename... Ts>
-struct VariantHelper<variant<Ts...>> {
-  // Type metafunction which returns the element type selected if
-  // OverloadSet::Overload() is well-formed when called with argument type U.
-  template <typename U>
-  using BestMatch = decltype(
-      variant_internal::OverloadSet<Ts...>::Overload(std::declval<U>()));
-
-  // Type metafunction which returns true if OverloadSet::Overload() is
-  // well-formed when called with argument type U.
-  // CanAccept can't be just an alias because there is a MSVC bug on parameter
-  // pack expansion involving decltype.
-  template <typename U>
-  struct CanAccept :
-      std::integral_constant<bool, !std::is_void<BestMatch<U>>::value> {};
-
-  // Type metafunction which returns true if Other is an instantiation of
-  // variant, and variants's converting constructor from Other will be
-  // well-formed. We will use this to remove constructors that would be
-  // ill-formed from the overload set.
-  template <typename Other>
-  struct CanConvertFrom;
-
-  template <typename... Us>
-  struct CanConvertFrom<variant<Us...>>
-      : public absl::conjunction<CanAccept<Us>...> {};
-};
-
-// A type with nontrivial copy ctor and trivial move ctor.
-struct TrivialMoveOnly {
-  TrivialMoveOnly(TrivialMoveOnly&&) = default;
-};
-
-// Trait class to detect whether a type is trivially move constructible.
-// A union's defaulted copy/move constructor is deleted if any variant member's
-// copy/move constructor is nontrivial.
-template <typename T>
-struct IsTriviallyMoveConstructible:
-  std::is_move_constructible<Union<T, TrivialMoveOnly>> {};
-
-// To guarantee triviality of all special-member functions that can be trivial,
-// we use a chain of conditional bases for each one.
-// The order of inheritance of bases from child to base are logically:
-//
-// variant
-// VariantCopyAssignBase
-// VariantMoveAssignBase
-// VariantCopyBase
-// VariantMoveBase
-// VariantStateBaseDestructor
-// VariantStateBase
-//
-// Note that there is a separate branch at each base that is dependent on
-// whether or not that corresponding special-member-function can be trivial in
-// the resultant variant type.
-
-template <class... T>
-class VariantStateBaseDestructorNontrivial;
-
-template <class... T>
-class VariantMoveBaseNontrivial;
-
-template <class... T>
-class VariantCopyBaseNontrivial;
-
-template <class... T>
-class VariantMoveAssignBaseNontrivial;
-
-template <class... T>
-class VariantCopyAssignBaseNontrivial;
-
-// Base that is dependent on whether or not the destructor can be trivial.
-template <class... T>
-using VariantStateBaseDestructor =
-    absl::conditional_t<std::is_destructible<Union<T...>>::value,
-                        VariantStateBase<T...>,
-                        VariantStateBaseDestructorNontrivial<T...>>;
-
-// Base that is dependent on whether or not the move-constructor can be
-// implicitly generated by the compiler (trivial or deleted).
-// Previously we were using `std::is_move_constructible<Union<T...>>` to check
-// whether all Ts have trivial move constructor, but it ran into a GCC bug:
-// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84866
-// So we have to use a different approach (i.e. `HasTrivialMoveConstructor`) to
-// work around the bug.
-template <class... T>
-using VariantMoveBase = absl::conditional_t<
-    absl::disjunction<
-        absl::negation<absl::conjunction<std::is_move_constructible<T>...>>,
-        absl::conjunction<IsTriviallyMoveConstructible<T>...>>::value,
-    VariantStateBaseDestructor<T...>, VariantMoveBaseNontrivial<T...>>;
-
-// Base that is dependent on whether or not the copy-constructor can be trivial.
-template <class... T>
-using VariantCopyBase = absl::conditional_t<
-    absl::disjunction<
-        absl::negation<absl::conjunction<std::is_copy_constructible<T>...>>,
-        std::is_copy_constructible<Union<T...>>>::value,
-    VariantMoveBase<T...>, VariantCopyBaseNontrivial<T...>>;
-
-// Base that is dependent on whether or not the move-assign can be trivial.
-template <class... T>
-using VariantMoveAssignBase = absl::conditional_t<
-    absl::disjunction<
-        absl::conjunction<absl::is_move_assignable<Union<T...>>,
-                          std::is_move_constructible<Union<T...>>,
-                          std::is_destructible<Union<T...>>>,
-        absl::negation<absl::conjunction<std::is_move_constructible<T>...,
-                                         // Note: We're not qualifying this with
-                                         // absl:: because it doesn't compile
-                                         // under MSVC.
-                                         is_move_assignable<T>...>>>::value,
-    VariantCopyBase<T...>, VariantMoveAssignBaseNontrivial<T...>>;
-
-// Base that is dependent on whether or not the copy-assign can be trivial.
-template <class... T>
-using VariantCopyAssignBase = absl::conditional_t<
-    absl::disjunction<
-        absl::conjunction<absl::is_copy_assignable<Union<T...>>,
-                          std::is_copy_constructible<Union<T...>>,
-                          std::is_destructible<Union<T...>>>,
-        absl::negation<absl::conjunction<std::is_copy_constructible<T>...,
-                                         // Note: We're not qualifying this with
-                                         // absl:: because it doesn't compile
-                                         // under MSVC.
-                                         is_copy_assignable<T>...>>>::value,
-    VariantMoveAssignBase<T...>, VariantCopyAssignBaseNontrivial<T...>>;
-
-template <class... T>
-using VariantBase = VariantCopyAssignBase<T...>;
-
-template <class... T>
-class VariantStateBaseDestructorNontrivial : protected VariantStateBase<T...> {
- private:
-  using Base = VariantStateBase<T...>;
-
- protected:
-  using Base::Base;
-
-  VariantStateBaseDestructorNontrivial() = default;
-  VariantStateBaseDestructorNontrivial(VariantStateBaseDestructorNontrivial&&) =
-      default;
-  VariantStateBaseDestructorNontrivial(
-      const VariantStateBaseDestructorNontrivial&) = default;
-  VariantStateBaseDestructorNontrivial& operator=(
-      VariantStateBaseDestructorNontrivial&&) = default;
-  VariantStateBaseDestructorNontrivial& operator=(
-      const VariantStateBaseDestructorNontrivial&) = default;
-
-  struct Destroyer {
-    template <std::size_t I>
-    void operator()(SizeT<I> i) const {
-      using Alternative =
-          typename absl::variant_alternative<I, variant<T...>>::type;
-      variant_internal::AccessUnion(self->state_, i).~Alternative();
-    }
-
-    void operator()(SizeT<absl::variant_npos> /*i*/) const {
-      // This space intentionally left blank
-    }
-
-    VariantStateBaseDestructorNontrivial* self;
-  };
-
-  void destroy() { VisitIndices<sizeof...(T)>::Run(Destroyer{this}, index_); }
-
-  ~VariantStateBaseDestructorNontrivial() { destroy(); }
-
- protected:
-  using Base::index_;
-  using Base::state_;
-};
-
-template <class... T>
-class VariantMoveBaseNontrivial : protected VariantStateBaseDestructor<T...> {
- private:
-  using Base = VariantStateBaseDestructor<T...>;
-
- protected:
-  using Base::Base;
-
-  struct Construct {
-    template <std::size_t I>
-    void operator()(SizeT<I> i) const {
-      using Alternative =
-          typename absl::variant_alternative<I, variant<T...>>::type;
-      ::new (static_cast<void*>(&self->state_)) Alternative(
-          variant_internal::AccessUnion(absl::move(other->state_), i));
-    }
-
-    void operator()(SizeT<absl::variant_npos> /*i*/) const {}
-
-    VariantMoveBaseNontrivial* self;
-    VariantMoveBaseNontrivial* other;
-  };
-
-  VariantMoveBaseNontrivial() = default;
-  VariantMoveBaseNontrivial(VariantMoveBaseNontrivial&& other) noexcept(
-      absl::conjunction<std::is_nothrow_move_constructible<T>...>::value)
-      : Base(NoopConstructorTag()) {
-    VisitIndices<sizeof...(T)>::Run(Construct{this, &other}, other.index_);
-    index_ = other.index_;
-  }
-
-  VariantMoveBaseNontrivial(VariantMoveBaseNontrivial const&) = default;
-
-  VariantMoveBaseNontrivial& operator=(VariantMoveBaseNontrivial&&) = default;
-  VariantMoveBaseNontrivial& operator=(VariantMoveBaseNontrivial const&) =
-      default;
-
- protected:
-  using Base::index_;
-  using Base::state_;
-};
-
-template <class... T>
-class VariantCopyBaseNontrivial : protected VariantMoveBase<T...> {
- private:
-  using Base = VariantMoveBase<T...>;
-
- protected:
-  using Base::Base;
-
-  VariantCopyBaseNontrivial() = default;
-  VariantCopyBaseNontrivial(VariantCopyBaseNontrivial&&) = default;
-
-  struct Construct {
-    template <std::size_t I>
-    void operator()(SizeT<I> i) const {
-      using Alternative =
-          typename absl::variant_alternative<I, variant<T...>>::type;
-      ::new (static_cast<void*>(&self->state_))
-          Alternative(variant_internal::AccessUnion(other->state_, i));
-    }
-
-    void operator()(SizeT<absl::variant_npos> /*i*/) const {}
-
-    VariantCopyBaseNontrivial* self;
-    const VariantCopyBaseNontrivial* other;
-  };
-
-  VariantCopyBaseNontrivial(VariantCopyBaseNontrivial const& other)
-      : Base(NoopConstructorTag()) {
-    VisitIndices<sizeof...(T)>::Run(Construct{this, &other}, other.index_);
-    index_ = other.index_;
-  }
-
-  VariantCopyBaseNontrivial& operator=(VariantCopyBaseNontrivial&&) = default;
-  VariantCopyBaseNontrivial& operator=(VariantCopyBaseNontrivial const&) =
-      default;
-
- protected:
-  using Base::index_;
-  using Base::state_;
-};
-
-template <class... T>
-class VariantMoveAssignBaseNontrivial : protected VariantCopyBase<T...> {
-  friend struct VariantCoreAccess;
-
- private:
-  using Base = VariantCopyBase<T...>;
-
- protected:
-  using Base::Base;
-
-  VariantMoveAssignBaseNontrivial() = default;
-  VariantMoveAssignBaseNontrivial(VariantMoveAssignBaseNontrivial&&) = default;
-  VariantMoveAssignBaseNontrivial(const VariantMoveAssignBaseNontrivial&) =
-      default;
-  VariantMoveAssignBaseNontrivial& operator=(
-      VariantMoveAssignBaseNontrivial const&) = default;
-
-    VariantMoveAssignBaseNontrivial&
-    operator=(VariantMoveAssignBaseNontrivial&& other) noexcept(
-        absl::conjunction<std::is_nothrow_move_constructible<T>...,
-                          std::is_nothrow_move_assignable<T>...>::value) {
-      VisitIndices<sizeof...(T)>::Run(
-          VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_);
-      return *this;
-    }
-
- protected:
-  using Base::index_;
-  using Base::state_;
-};
-
-template <class... T>
-class VariantCopyAssignBaseNontrivial : protected VariantMoveAssignBase<T...> {
-  friend struct VariantCoreAccess;
-
- private:
-  using Base = VariantMoveAssignBase<T...>;
-
- protected:
-  using Base::Base;
-
-  VariantCopyAssignBaseNontrivial() = default;
-  VariantCopyAssignBaseNontrivial(VariantCopyAssignBaseNontrivial&&) = default;
-  VariantCopyAssignBaseNontrivial(const VariantCopyAssignBaseNontrivial&) =
-      default;
-  VariantCopyAssignBaseNontrivial& operator=(
-      VariantCopyAssignBaseNontrivial&&) = default;
-
-    VariantCopyAssignBaseNontrivial& operator=(
-        const VariantCopyAssignBaseNontrivial& other) {
-      VisitIndices<sizeof...(T)>::Run(
-          VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_);
-      return *this;
-    }
-
- protected:
-  using Base::index_;
-  using Base::state_;
-};
-
-////////////////////////////////////////
-// Visitors for Comparison Operations //
-////////////////////////////////////////
-
-template <class... Types>
-struct EqualsOp {
-  const variant<Types...>* v;
-  const variant<Types...>* w;
-
-  constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
-    return true;
-  }
-
-  template <std::size_t I>
-  constexpr bool operator()(SizeT<I> /*v_i*/) const {
-    return VariantCoreAccess::Access<I>(*v) == VariantCoreAccess::Access<I>(*w);
-  }
-};
-
-template <class... Types>
-struct NotEqualsOp {
-  const variant<Types...>* v;
-  const variant<Types...>* w;
-
-  constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
-    return false;
-  }
-
-  template <std::size_t I>
-  constexpr bool operator()(SizeT<I> /*v_i*/) const {
-    return VariantCoreAccess::Access<I>(*v) != VariantCoreAccess::Access<I>(*w);
-  }
-};
-
-template <class... Types>
-struct LessThanOp {
-  const variant<Types...>* v;
-  const variant<Types...>* w;
-
-  constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
-    return false;
-  }
-
-  template <std::size_t I>
-  constexpr bool operator()(SizeT<I> /*v_i*/) const {
-    return VariantCoreAccess::Access<I>(*v) < VariantCoreAccess::Access<I>(*w);
-  }
-};
-
-template <class... Types>
-struct GreaterThanOp {
-  const variant<Types...>* v;
-  const variant<Types...>* w;
-
-  constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
-    return false;
-  }
-
-  template <std::size_t I>
-  constexpr bool operator()(SizeT<I> /*v_i*/) const {
-    return VariantCoreAccess::Access<I>(*v) > VariantCoreAccess::Access<I>(*w);
-  }
-};
-
-template <class... Types>
-struct LessThanOrEqualsOp {
-  const variant<Types...>* v;
-  const variant<Types...>* w;
-
-  constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
-    return true;
-  }
-
-  template <std::size_t I>
-  constexpr bool operator()(SizeT<I> /*v_i*/) const {
-    return VariantCoreAccess::Access<I>(*v) <= VariantCoreAccess::Access<I>(*w);
-  }
-};
-
-template <class... Types>
-struct GreaterThanOrEqualsOp {
-  const variant<Types...>* v;
-  const variant<Types...>* w;
-
-  constexpr bool operator()(SizeT<absl::variant_npos> /*v_i*/) const {
-    return true;
-  }
-
-  template <std::size_t I>
-  constexpr bool operator()(SizeT<I> /*v_i*/) const {
-    return VariantCoreAccess::Access<I>(*v) >= VariantCoreAccess::Access<I>(*w);
-  }
-};
-
-// Precondition: v.index() == w.index();
-template <class... Types>
-struct SwapSameIndex {
-  variant<Types...>* v;
-  variant<Types...>* w;
-  template <std::size_t I>
-  void operator()(SizeT<I>) const {
-    type_traits_internal::Swap(VariantCoreAccess::Access<I>(*v),
-                               VariantCoreAccess::Access<I>(*w));
-  }
-
-  void operator()(SizeT<variant_npos>) const {}
-};
-
-// TODO(calabrese) do this from a different namespace for proper adl usage
-template <class... Types>
-struct Swap {
-  variant<Types...>* v;
-  variant<Types...>* w;
-
-  void generic_swap() const {
-    variant<Types...> tmp(std::move(*w));
-    VariantCoreAccess::Destroy(*w);
-    VariantCoreAccess::InitFrom(*w, std::move(*v));
-    VariantCoreAccess::Destroy(*v);
-    VariantCoreAccess::InitFrom(*v, std::move(tmp));
-  }
-
-  void operator()(SizeT<absl::variant_npos> /*w_i*/) const {
-    if (!v->valueless_by_exception()) {
-      generic_swap();
-    }
-  }
-
-  template <std::size_t Wi>
-  void operator()(SizeT<Wi> /*w_i*/) {
-    if (v->index() == Wi) {
-      VisitIndices<sizeof...(Types)>::Run(SwapSameIndex<Types...>{v, w}, Wi);
-    } else {
-      generic_swap();
-    }
-  }
-};
-
-template <typename Variant, typename = void, typename... Ts>
-struct VariantHashBase {
-  VariantHashBase() = delete;
-  VariantHashBase(const VariantHashBase&) = delete;
-  VariantHashBase(VariantHashBase&&) = delete;
-  VariantHashBase& operator=(const VariantHashBase&) = delete;
-  VariantHashBase& operator=(VariantHashBase&&) = delete;
-};
-
-struct VariantHashVisitor {
-  template <typename T>
-  size_t operator()(const T& t) {
-    return std::hash<T>{}(t);
-  }
-};
-
-template <typename Variant, typename... Ts>
-struct VariantHashBase<Variant,
-                       absl::enable_if_t<absl::conjunction<
-                           type_traits_internal::IsHashable<Ts>...>::value>,
-                       Ts...> {
-  using argument_type = Variant;
-  using result_type = size_t;
-  size_t operator()(const Variant& var) const {
-    type_traits_internal::AssertHashEnabled<Ts...>();
-    if (var.valueless_by_exception()) {
-      return 239799884;
-    }
-    size_t result = VisitIndices<variant_size<Variant>::value>::Run(
-        PerformVisitation<VariantHashVisitor, const Variant&>{
-            std::forward_as_tuple(var), VariantHashVisitor{}},
-        var.index());
-    // Combine the index and the hash result in order to distinguish
-    // std::variant<int, int> holding the same value as different alternative.
-    return result ^ var.index();
-  }
-};
-
-}  // namespace variant_internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // !defined(ABSL_USES_STD_VARIANT)
-#endif  // ABSL_TYPES_variant_internal_H_
diff --git a/third_party/abseil_cpp/absl/types/optional.h b/third_party/abseil_cpp/absl/types/optional.h
deleted file mode 100644
index 61540cfdb2..0000000000
--- a/third_party/abseil_cpp/absl/types/optional.h
+++ /dev/null
@@ -1,776 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// optional.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines the `absl::optional` type for holding a value which
-// may or may not be present. This type is useful for providing value semantics
-// for operations that may either wish to return or hold "something-or-nothing".
-//
-// Example:
-//
-//   // A common way to signal operation failure is to provide an output
-//   // parameter and a bool return type:
-//   bool AcquireResource(const Input&, Resource * out);
-//
-//   // Providing an absl::optional return type provides a cleaner API:
-//   absl::optional<Resource> AcquireResource(const Input&);
-//
-// `absl::optional` is a C++11 compatible version of the C++17 `std::optional`
-// abstraction and is designed to be a drop-in replacement for code compliant
-// with C++17.
-#ifndef ABSL_TYPES_OPTIONAL_H_
-#define ABSL_TYPES_OPTIONAL_H_
-
-#include "absl/base/config.h"   // TODO(calabrese) IWYU removal?
-#include "absl/utility/utility.h"
-
-#ifdef ABSL_USES_STD_OPTIONAL
-
-#include <optional>  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-using std::bad_optional_access;
-using std::optional;
-using std::make_optional;
-using std::nullopt_t;
-using std::nullopt;
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // ABSL_USES_STD_OPTIONAL
-
-#include <cassert>
-#include <functional>
-#include <initializer_list>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/attributes.h"
-#include "absl/base/internal/inline_variable.h"
-#include "absl/meta/type_traits.h"
-#include "absl/types/bad_optional_access.h"
-#include "absl/types/internal/optional.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// nullopt_t
-//
-// Class type for `absl::nullopt` used to indicate an `absl::optional<T>` type
-// that does not contain a value.
-struct nullopt_t {
-  // It must not be default-constructible to avoid ambiguity for opt = {}.
-  explicit constexpr nullopt_t(optional_internal::init_t) noexcept {}
-};
-
-// nullopt
-//
-// A tag constant of type `absl::nullopt_t` used to indicate an empty
-// `absl::optional` in certain functions, such as construction or assignment.
-ABSL_INTERNAL_INLINE_CONSTEXPR(nullopt_t, nullopt,
-                               nullopt_t(optional_internal::init_t()));
-
-// -----------------------------------------------------------------------------
-// absl::optional
-// -----------------------------------------------------------------------------
-//
-// A value of type `absl::optional<T>` holds either a value of `T` or an
-// "empty" value.  When it holds a value of `T`, it stores it as a direct
-// sub-object, so `sizeof(optional<T>)` is approximately
-// `sizeof(T) + sizeof(bool)`.
-//
-// This implementation is based on the specification in the latest draft of the
-// C++17 `std::optional` specification as of May 2017, section 20.6.
-//
-// Differences between `absl::optional<T>` and `std::optional<T>` include:
-//
-//    * `constexpr` is not used for non-const member functions.
-//      (dependency on some differences between C++11 and C++14.)
-//    * `absl::nullopt` and `absl::in_place` are not declared `constexpr`. We
-//      need the inline variable support in C++17 for external linkage.
-//    * Throws `absl::bad_optional_access` instead of
-//      `std::bad_optional_access`.
-//    * `make_optional()` cannot be declared `constexpr` due to the absence of
-//      guaranteed copy elision.
-//    * The move constructor's `noexcept` specification is stronger, i.e. if the
-//      default allocator is non-throwing (via setting
-//      `ABSL_ALLOCATOR_NOTHROW`), it evaluates to `noexcept(true)`, because
-//      we assume
-//       a) move constructors should only throw due to allocation failure and
-//       b) if T's move constructor allocates, it uses the same allocation
-//          function as the default allocator.
-//
-template <typename T>
-class optional : private optional_internal::optional_data<T>,
-                 private optional_internal::optional_ctor_base<
-                     optional_internal::ctor_copy_traits<T>::traits>,
-                 private optional_internal::optional_assign_base<
-                     optional_internal::assign_copy_traits<T>::traits> {
-  using data_base = optional_internal::optional_data<T>;
-
- public:
-  typedef T value_type;
-
-  // Constructors
-
-  // Constructs an `optional` holding an empty value, NOT a default constructed
-  // `T`.
-  constexpr optional() noexcept {}
-
-  // Constructs an `optional` initialized with `nullopt` to hold an empty value.
-  constexpr optional(nullopt_t) noexcept {}  // NOLINT(runtime/explicit)
-
-  // Copy constructor, standard semantics
-  optional(const optional&) = default;
-
-  // Move constructor, standard semantics
-  optional(optional&&) = default;
-
-  // Constructs a non-empty `optional` direct-initialized value of type `T` from
-  // the arguments `std::forward<Args>(args)...`  within the `optional`.
-  // (The `in_place_t` is a tag used to indicate that the contained object
-  // should be constructed in-place.)
-  template <typename InPlaceT, typename... Args,
-            absl::enable_if_t<absl::conjunction<
-                std::is_same<InPlaceT, in_place_t>,
-                std::is_constructible<T, Args&&...> >::value>* = nullptr>
-  constexpr explicit optional(InPlaceT, Args&&... args)
-      : data_base(in_place_t(), absl::forward<Args>(args)...) {}
-
-  // Constructs a non-empty `optional` direct-initialized value of type `T` from
-  // the arguments of an initializer_list and `std::forward<Args>(args)...`.
-  // (The `in_place_t` is a tag used to indicate that the contained object
-  // should be constructed in-place.)
-  template <typename U, typename... Args,
-            typename = typename std::enable_if<std::is_constructible<
-                T, std::initializer_list<U>&, Args&&...>::value>::type>
-  constexpr explicit optional(in_place_t, std::initializer_list<U> il,
-                              Args&&... args)
-      : data_base(in_place_t(), il, absl::forward<Args>(args)...) {
-  }
-
-  // Value constructor (implicit)
-  template <
-      typename U = T,
-      typename std::enable_if<
-          absl::conjunction<absl::negation<std::is_same<
-                                in_place_t, typename std::decay<U>::type> >,
-                            absl::negation<std::is_same<
-                                optional<T>, typename std::decay<U>::type> >,
-                            std::is_convertible<U&&, T>,
-                            std::is_constructible<T, U&&> >::value,
-          bool>::type = false>
-  constexpr optional(U&& v) : data_base(in_place_t(), absl::forward<U>(v)) {}
-
-  // Value constructor (explicit)
-  template <
-      typename U = T,
-      typename std::enable_if<
-          absl::conjunction<absl::negation<std::is_same<
-                                in_place_t, typename std::decay<U>::type>>,
-                            absl::negation<std::is_same<
-                                optional<T>, typename std::decay<U>::type>>,
-                            absl::negation<std::is_convertible<U&&, T>>,
-                            std::is_constructible<T, U&&>>::value,
-          bool>::type = false>
-  explicit constexpr optional(U&& v)
-      : data_base(in_place_t(), absl::forward<U>(v)) {}
-
-  // Converting copy constructor (implicit)
-  template <typename U,
-            typename std::enable_if<
-                absl::conjunction<
-                    absl::negation<std::is_same<T, U> >,
-                    std::is_constructible<T, const U&>,
-                    absl::negation<
-                        optional_internal::
-                            is_constructible_convertible_from_optional<T, U> >,
-                    std::is_convertible<const U&, T> >::value,
-                bool>::type = false>
-  optional(const optional<U>& rhs) {
-    if (rhs) {
-      this->construct(*rhs);
-    }
-  }
-
-  // Converting copy constructor (explicit)
-  template <typename U,
-            typename std::enable_if<
-                absl::conjunction<
-                    absl::negation<std::is_same<T, U>>,
-                    std::is_constructible<T, const U&>,
-                    absl::negation<
-                        optional_internal::
-                            is_constructible_convertible_from_optional<T, U>>,
-                    absl::negation<std::is_convertible<const U&, T>>>::value,
-                bool>::type = false>
-  explicit optional(const optional<U>& rhs) {
-    if (rhs) {
-      this->construct(*rhs);
-    }
-  }
-
-  // Converting move constructor (implicit)
-  template <typename U,
-            typename std::enable_if<
-                absl::conjunction<
-                    absl::negation<std::is_same<T, U> >,
-                    std::is_constructible<T, U&&>,
-                    absl::negation<
-                        optional_internal::
-                            is_constructible_convertible_from_optional<T, U> >,
-                    std::is_convertible<U&&, T> >::value,
-                bool>::type = false>
-  optional(optional<U>&& rhs) {
-    if (rhs) {
-      this->construct(std::move(*rhs));
-    }
-  }
-
-  // Converting move constructor (explicit)
-  template <
-      typename U,
-      typename std::enable_if<
-          absl::conjunction<
-              absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
-              absl::negation<
-                  optional_internal::is_constructible_convertible_from_optional<
-                      T, U>>,
-              absl::negation<std::is_convertible<U&&, T>>>::value,
-          bool>::type = false>
-  explicit optional(optional<U>&& rhs) {
-    if (rhs) {
-      this->construct(std::move(*rhs));
-    }
-  }
-
-  // Destructor. Trivial if `T` is trivially destructible.
-  ~optional() = default;
-
-  // Assignment Operators
-
-  // Assignment from `nullopt`
-  //
-  // Example:
-  //
-  //   struct S { int value; };
-  //   optional<S> opt = absl::nullopt;  // Could also use opt = { };
-  optional& operator=(nullopt_t) noexcept {
-    this->destruct();
-    return *this;
-  }
-
-  // Copy assignment operator, standard semantics
-  optional& operator=(const optional& src) = default;
-
-  // Move assignment operator, standard semantics
-  optional& operator=(optional&& src) = default;
-
-  // Value assignment operators
-  template <
-      typename U = T,
-      typename = typename std::enable_if<absl::conjunction<
-          absl::negation<
-              std::is_same<optional<T>, typename std::decay<U>::type>>,
-          absl::negation<
-              absl::conjunction<std::is_scalar<T>,
-                                std::is_same<T, typename std::decay<U>::type>>>,
-          std::is_constructible<T, U>, std::is_assignable<T&, U>>::value>::type>
-  optional& operator=(U&& v) {
-    this->assign(std::forward<U>(v));
-    return *this;
-  }
-
-  template <
-      typename U,
-      typename = typename std::enable_if<absl::conjunction<
-          absl::negation<std::is_same<T, U>>,
-          std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>,
-          absl::negation<
-              optional_internal::
-                  is_constructible_convertible_assignable_from_optional<
-                      T, U>>>::value>::type>
-  optional& operator=(const optional<U>& rhs) {
-    if (rhs) {
-      this->assign(*rhs);
-    } else {
-      this->destruct();
-    }
-    return *this;
-  }
-
-  template <typename U,
-            typename = typename std::enable_if<absl::conjunction<
-                absl::negation<std::is_same<T, U>>, std::is_constructible<T, U>,
-                std::is_assignable<T&, U>,
-                absl::negation<
-                    optional_internal::
-                        is_constructible_convertible_assignable_from_optional<
-                            T, U>>>::value>::type>
-  optional& operator=(optional<U>&& rhs) {
-    if (rhs) {
-      this->assign(std::move(*rhs));
-    } else {
-      this->destruct();
-    }
-    return *this;
-  }
-
-  // Modifiers
-
-  // optional::reset()
-  //
-  // Destroys the inner `T` value of an `absl::optional` if one is present.
-  ABSL_ATTRIBUTE_REINITIALIZES void reset() noexcept { this->destruct(); }
-
-  // optional::emplace()
-  //
-  // (Re)constructs the underlying `T` in-place with the given forwarded
-  // arguments.
-  //
-  // Example:
-  //
-  //   optional<Foo> opt;
-  //   opt.emplace(arg1,arg2,arg3);  // Constructs Foo(arg1,arg2,arg3)
-  //
-  // If the optional is non-empty, and the `args` refer to subobjects of the
-  // current object, then behaviour is undefined, because the current object
-  // will be destructed before the new object is constructed with `args`.
-  template <typename... Args,
-            typename = typename std::enable_if<
-                std::is_constructible<T, Args&&...>::value>::type>
-  T& emplace(Args&&... args) {
-    this->destruct();
-    this->construct(std::forward<Args>(args)...);
-    return reference();
-  }
-
-  // Emplace reconstruction overload for an initializer list and the given
-  // forwarded arguments.
-  //
-  // Example:
-  //
-  //   struct Foo {
-  //     Foo(std::initializer_list<int>);
-  //   };
-  //
-  //   optional<Foo> opt;
-  //   opt.emplace({1,2,3});  // Constructs Foo({1,2,3})
-  template <typename U, typename... Args,
-            typename = typename std::enable_if<std::is_constructible<
-                T, std::initializer_list<U>&, Args&&...>::value>::type>
-  T& emplace(std::initializer_list<U> il, Args&&... args) {
-    this->destruct();
-    this->construct(il, std::forward<Args>(args)...);
-    return reference();
-  }
-
-  // Swaps
-
-  // Swap, standard semantics
-  void swap(optional& rhs) noexcept(
-      std::is_nothrow_move_constructible<T>::value&&
-          type_traits_internal::IsNothrowSwappable<T>::value) {
-    if (*this) {
-      if (rhs) {
-        type_traits_internal::Swap(**this, *rhs);
-      } else {
-        rhs.construct(std::move(**this));
-        this->destruct();
-      }
-    } else {
-      if (rhs) {
-        this->construct(std::move(*rhs));
-        rhs.destruct();
-      } else {
-        // No effect (swap(disengaged, disengaged)).
-      }
-    }
-  }
-
-  // Observers
-
-  // optional::operator->()
-  //
-  // Accesses the underlying `T` value's member `m` of an `optional`. If the
-  // `optional` is empty, behavior is undefined.
-  //
-  // If you need myOpt->foo in constexpr, use (*myOpt).foo instead.
-  const T* operator->() const {
-    ABSL_HARDENING_ASSERT(this->engaged_);
-    return std::addressof(this->data_);
-  }
-  T* operator->() {
-    ABSL_HARDENING_ASSERT(this->engaged_);
-    return std::addressof(this->data_);
-  }
-
-  // optional::operator*()
-  //
-  // Accesses the underlying `T` value of an `optional`. If the `optional` is
-  // empty, behavior is undefined.
-  constexpr const T& operator*() const& {
-    return ABSL_HARDENING_ASSERT(this->engaged_), reference();
-  }
-  T& operator*() & {
-    ABSL_HARDENING_ASSERT(this->engaged_);
-    return reference();
-  }
-  constexpr const T&& operator*() const && {
-    return ABSL_HARDENING_ASSERT(this->engaged_), absl::move(reference());
-  }
-  T&& operator*() && {
-    ABSL_HARDENING_ASSERT(this->engaged_);
-    return std::move(reference());
-  }
-
-  // optional::operator bool()
-  //
-  // Returns false if and only if the `optional` is empty.
-  //
-  //   if (opt) {
-  //     // do something with *opt or opt->;
-  //   } else {
-  //     // opt is empty.
-  //   }
-  //
-  constexpr explicit operator bool() const noexcept { return this->engaged_; }
-
-  // optional::has_value()
-  //
-  // Determines whether the `optional` contains a value. Returns `false` if and
-  // only if `*this` is empty.
-  constexpr bool has_value() const noexcept { return this->engaged_; }
-
-// Suppress bogus warning on MSVC: MSVC complains call to reference() after
-// throw_bad_optional_access() is unreachable.
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4702)
-#endif  // _MSC_VER
-  // optional::value()
-  //
-  // Returns a reference to an `optional`s underlying value. The constness
-  // and lvalue/rvalue-ness of the `optional` is preserved to the view of
-  // the `T` sub-object. Throws `absl::bad_optional_access` when the `optional`
-  // is empty.
-  constexpr const T& value() const & {
-    return static_cast<bool>(*this)
-               ? reference()
-               : (optional_internal::throw_bad_optional_access(), reference());
-  }
-  T& value() & {
-    return static_cast<bool>(*this)
-               ? reference()
-               : (optional_internal::throw_bad_optional_access(), reference());
-  }
-  T&& value() && {  // NOLINT(build/c++11)
-    return std::move(
-        static_cast<bool>(*this)
-            ? reference()
-            : (optional_internal::throw_bad_optional_access(), reference()));
-  }
-  constexpr const T&& value() const && {  // NOLINT(build/c++11)
-    return absl::move(
-        static_cast<bool>(*this)
-            ? reference()
-            : (optional_internal::throw_bad_optional_access(), reference()));
-  }
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif  // _MSC_VER
-
-  // optional::value_or()
-  //
-  // Returns either the value of `T` or a passed default `v` if the `optional`
-  // is empty.
-  template <typename U>
-  constexpr T value_or(U&& v) const& {
-    static_assert(std::is_copy_constructible<value_type>::value,
-                  "optional<T>::value_or: T must be copy constructible");
-    static_assert(std::is_convertible<U&&, value_type>::value,
-                  "optional<T>::value_or: U must be convertible to T");
-    return static_cast<bool>(*this)
-               ? **this
-               : static_cast<T>(absl::forward<U>(v));
-  }
-  template <typename U>
-  T value_or(U&& v) && {  // NOLINT(build/c++11)
-    static_assert(std::is_move_constructible<value_type>::value,
-                  "optional<T>::value_or: T must be move constructible");
-    static_assert(std::is_convertible<U&&, value_type>::value,
-                  "optional<T>::value_or: U must be convertible to T");
-    return static_cast<bool>(*this) ? std::move(**this)
-                                    : static_cast<T>(std::forward<U>(v));
-  }
-
- private:
-  // Private accessors for internal storage viewed as reference to T.
-  constexpr const T& reference() const { return this->data_; }
-  T& reference() { return this->data_; }
-
-  // T constraint checks.  You can't have an optional of nullopt_t, in_place_t
-  // or a reference.
-  static_assert(
-      !std::is_same<nullopt_t, typename std::remove_cv<T>::type>::value,
-      "optional<nullopt_t> is not allowed.");
-  static_assert(
-      !std::is_same<in_place_t, typename std::remove_cv<T>::type>::value,
-      "optional<in_place_t> is not allowed.");
-  static_assert(!std::is_reference<T>::value,
-                "optional<reference> is not allowed.");
-};
-
-// Non-member functions
-
-// swap()
-//
-// Performs a swap between two `absl::optional` objects, using standard
-// semantics.
-template <typename T, typename std::enable_if<
-                          std::is_move_constructible<T>::value &&
-                              type_traits_internal::IsSwappable<T>::value,
-                          bool>::type = false>
-void swap(optional<T>& a, optional<T>& b) noexcept(noexcept(a.swap(b))) {
-  a.swap(b);
-}
-
-// make_optional()
-//
-// Creates a non-empty `optional<T>` where the type of `T` is deduced. An
-// `absl::optional` can also be explicitly instantiated with
-// `make_optional<T>(v)`.
-//
-// Note: `make_optional()` constructions may be declared `constexpr` for
-// trivially copyable types `T`. Non-trivial types require copy elision
-// support in C++17 for `make_optional` to support `constexpr` on such
-// non-trivial types.
-//
-// Example:
-//
-//   constexpr absl::optional<int> opt = absl::make_optional(1);
-//   static_assert(opt.value() == 1, "");
-template <typename T>
-constexpr optional<typename std::decay<T>::type> make_optional(T&& v) {
-  return optional<typename std::decay<T>::type>(absl::forward<T>(v));
-}
-
-template <typename T, typename... Args>
-constexpr optional<T> make_optional(Args&&... args) {
-  return optional<T>(in_place_t(), absl::forward<Args>(args)...);
-}
-
-template <typename T, typename U, typename... Args>
-constexpr optional<T> make_optional(std::initializer_list<U> il,
-                                    Args&&... args) {
-  return optional<T>(in_place_t(), il,
-                     absl::forward<Args>(args)...);
-}
-
-// Relational operators [optional.relops]
-
-// Empty optionals are considered equal to each other and less than non-empty
-// optionals. Supports relations between optional<T> and optional<U>, between
-// optional<T> and U, and between optional<T> and nullopt.
-//
-// Note: We're careful to support T having non-bool relationals.
-
-// Requires: The expression, e.g. "*x == *y" shall be well-formed and its result
-// shall be convertible to bool.
-// The C++17 (N4606) "Returns:" statements are translated into
-// code in an obvious way here, and the original text retained as function docs.
-// Returns: If bool(x) != bool(y), false; otherwise if bool(x) == false, true;
-// otherwise *x == *y.
-template <typename T, typename U>
-constexpr auto operator==(const optional<T>& x, const optional<U>& y)
-    -> decltype(optional_internal::convertible_to_bool(*x == *y)) {
-  return static_cast<bool>(x) != static_cast<bool>(y)
-             ? false
-             : static_cast<bool>(x) == false ? true
-                                             : static_cast<bool>(*x == *y);
-}
-
-// Returns: If bool(x) != bool(y), true; otherwise, if bool(x) == false, false;
-// otherwise *x != *y.
-template <typename T, typename U>
-constexpr auto operator!=(const optional<T>& x, const optional<U>& y)
-    -> decltype(optional_internal::convertible_to_bool(*x != *y)) {
-  return static_cast<bool>(x) != static_cast<bool>(y)
-             ? true
-             : static_cast<bool>(x) == false ? false
-                                             : static_cast<bool>(*x != *y);
-}
-// Returns: If !y, false; otherwise, if !x, true; otherwise *x < *y.
-template <typename T, typename U>
-constexpr auto operator<(const optional<T>& x, const optional<U>& y)
-    -> decltype(optional_internal::convertible_to_bool(*x < *y)) {
-  return !y ? false : !x ? true : static_cast<bool>(*x < *y);
-}
-// Returns: If !x, false; otherwise, if !y, true; otherwise *x > *y.
-template <typename T, typename U>
-constexpr auto operator>(const optional<T>& x, const optional<U>& y)
-    -> decltype(optional_internal::convertible_to_bool(*x > *y)) {
-  return !x ? false : !y ? true : static_cast<bool>(*x > *y);
-}
-// Returns: If !x, true; otherwise, if !y, false; otherwise *x <= *y.
-template <typename T, typename U>
-constexpr auto operator<=(const optional<T>& x, const optional<U>& y)
-    -> decltype(optional_internal::convertible_to_bool(*x <= *y)) {
-  return !x ? true : !y ? false : static_cast<bool>(*x <= *y);
-}
-// Returns: If !y, true; otherwise, if !x, false; otherwise *x >= *y.
-template <typename T, typename U>
-constexpr auto operator>=(const optional<T>& x, const optional<U>& y)
-    -> decltype(optional_internal::convertible_to_bool(*x >= *y)) {
-  return !y ? true : !x ? false : static_cast<bool>(*x >= *y);
-}
-
-// Comparison with nullopt [optional.nullops]
-// The C++17 (N4606) "Returns:" statements are used directly here.
-template <typename T>
-constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept {
-  return !x;
-}
-template <typename T>
-constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept {
-  return !x;
-}
-template <typename T>
-constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept {
-  return static_cast<bool>(x);
-}
-template <typename T>
-constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept {
-  return static_cast<bool>(x);
-}
-template <typename T>
-constexpr bool operator<(const optional<T>&, nullopt_t) noexcept {
-  return false;
-}
-template <typename T>
-constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept {
-  return static_cast<bool>(x);
-}
-template <typename T>
-constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept {
-  return !x;
-}
-template <typename T>
-constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept {
-  return true;
-}
-template <typename T>
-constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept {
-  return static_cast<bool>(x);
-}
-template <typename T>
-constexpr bool operator>(nullopt_t, const optional<T>&) noexcept {
-  return false;
-}
-template <typename T>
-constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept {
-  return true;
-}
-template <typename T>
-constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept {
-  return !x;
-}
-
-// Comparison with T [optional.comp_with_t]
-
-// Requires: The expression, e.g. "*x == v" shall be well-formed and its result
-// shall be convertible to bool.
-// The C++17 (N4606) "Equivalent to:" statements are used directly here.
-template <typename T, typename U>
-constexpr auto operator==(const optional<T>& x, const U& v)
-    -> decltype(optional_internal::convertible_to_bool(*x == v)) {
-  return static_cast<bool>(x) ? static_cast<bool>(*x == v) : false;
-}
-template <typename T, typename U>
-constexpr auto operator==(const U& v, const optional<T>& x)
-    -> decltype(optional_internal::convertible_to_bool(v == *x)) {
-  return static_cast<bool>(x) ? static_cast<bool>(v == *x) : false;
-}
-template <typename T, typename U>
-constexpr auto operator!=(const optional<T>& x, const U& v)
-    -> decltype(optional_internal::convertible_to_bool(*x != v)) {
-  return static_cast<bool>(x) ? static_cast<bool>(*x != v) : true;
-}
-template <typename T, typename U>
-constexpr auto operator!=(const U& v, const optional<T>& x)
-    -> decltype(optional_internal::convertible_to_bool(v != *x)) {
-  return static_cast<bool>(x) ? static_cast<bool>(v != *x) : true;
-}
-template <typename T, typename U>
-constexpr auto operator<(const optional<T>& x, const U& v)
-    -> decltype(optional_internal::convertible_to_bool(*x < v)) {
-  return static_cast<bool>(x) ? static_cast<bool>(*x < v) : true;
-}
-template <typename T, typename U>
-constexpr auto operator<(const U& v, const optional<T>& x)
-    -> decltype(optional_internal::convertible_to_bool(v < *x)) {
-  return static_cast<bool>(x) ? static_cast<bool>(v < *x) : false;
-}
-template <typename T, typename U>
-constexpr auto operator<=(const optional<T>& x, const U& v)
-    -> decltype(optional_internal::convertible_to_bool(*x <= v)) {
-  return static_cast<bool>(x) ? static_cast<bool>(*x <= v) : true;
-}
-template <typename T, typename U>
-constexpr auto operator<=(const U& v, const optional<T>& x)
-    -> decltype(optional_internal::convertible_to_bool(v <= *x)) {
-  return static_cast<bool>(x) ? static_cast<bool>(v <= *x) : false;
-}
-template <typename T, typename U>
-constexpr auto operator>(const optional<T>& x, const U& v)
-    -> decltype(optional_internal::convertible_to_bool(*x > v)) {
-  return static_cast<bool>(x) ? static_cast<bool>(*x > v) : false;
-}
-template <typename T, typename U>
-constexpr auto operator>(const U& v, const optional<T>& x)
-    -> decltype(optional_internal::convertible_to_bool(v > *x)) {
-  return static_cast<bool>(x) ? static_cast<bool>(v > *x) : true;
-}
-template <typename T, typename U>
-constexpr auto operator>=(const optional<T>& x, const U& v)
-    -> decltype(optional_internal::convertible_to_bool(*x >= v)) {
-  return static_cast<bool>(x) ? static_cast<bool>(*x >= v) : false;
-}
-template <typename T, typename U>
-constexpr auto operator>=(const U& v, const optional<T>& x)
-    -> decltype(optional_internal::convertible_to_bool(v >= *x)) {
-  return static_cast<bool>(x) ? static_cast<bool>(v >= *x) : true;
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-namespace std {
-
-// std::hash specialization for absl::optional.
-template <typename T>
-struct hash<absl::optional<T> >
-    : absl::optional_internal::optional_hash_base<T> {};
-
-}  // namespace std
-
-#undef ABSL_MSVC_CONSTEXPR_BUG_IN_UNION_LIKE_CLASS
-
-#endif  // ABSL_USES_STD_OPTIONAL
-
-#endif  // ABSL_TYPES_OPTIONAL_H_
diff --git a/third_party/abseil_cpp/absl/types/optional_exception_safety_test.cc b/third_party/abseil_cpp/absl/types/optional_exception_safety_test.cc
deleted file mode 100644
index 8e5fe851db..0000000000
--- a/third_party/abseil_cpp/absl/types/optional_exception_safety_test.cc
+++ /dev/null
@@ -1,292 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/optional.h"
-
-#include "absl/base/config.h"
-
-// This test is a no-op when absl::optional is an alias for std::optional and
-// when exceptions are not enabled.
-#if !defined(ABSL_USES_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS)
-
-#include "gtest/gtest.h"
-#include "absl/base/internal/exception_safety_testing.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-namespace {
-
-using ::testing::AssertionFailure;
-using ::testing::AssertionResult;
-using ::testing::AssertionSuccess;
-using ::testing::MakeExceptionSafetyTester;
-
-using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
-using Optional = absl::optional<Thrower>;
-
-using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
-using MoveOptional = absl::optional<MoveThrower>;
-
-constexpr int kInitialInteger = 5;
-constexpr int kUpdatedInteger = 10;
-
-template <typename OptionalT>
-bool ValueThrowsBadOptionalAccess(const OptionalT& optional) try {
-  return (static_cast<void>(optional.value()), false);
-} catch (const absl::bad_optional_access&) {
-  return true;
-}
-
-template <typename OptionalT>
-AssertionResult OptionalInvariants(OptionalT* optional_ptr) {
-  // Check the current state post-throw for validity
-  auto& optional = *optional_ptr;
-
-  if (optional.has_value() && ValueThrowsBadOptionalAccess(optional)) {
-    return AssertionFailure()
-           << "Optional with value should not throw bad_optional_access when "
-              "accessing the value.";
-  }
-  if (!optional.has_value() && !ValueThrowsBadOptionalAccess(optional)) {
-    return AssertionFailure()
-           << "Optional without a value should throw bad_optional_access when "
-              "accessing the value.";
-  }
-
-  // Reset to a known state
-  optional.reset();
-
-  // Confirm that the known post-reset state is valid
-  if (optional.has_value()) {
-    return AssertionFailure()
-           << "Optional should not contain a value after being reset.";
-  }
-  if (!ValueThrowsBadOptionalAccess(optional)) {
-    return AssertionFailure() << "Optional should throw bad_optional_access "
-                                 "when accessing the value after being reset.";
-  }
-
-  return AssertionSuccess();
-}
-
-template <typename OptionalT>
-AssertionResult CheckDisengaged(OptionalT* optional_ptr) {
-  auto& optional = *optional_ptr;
-
-  if (optional.has_value()) {
-    return AssertionFailure()
-           << "Expected optional to not contain a value but a value was found.";
-  }
-
-  return AssertionSuccess();
-}
-
-template <typename OptionalT>
-AssertionResult CheckEngaged(OptionalT* optional_ptr) {
-  auto& optional = *optional_ptr;
-
-  if (!optional.has_value()) {
-    return AssertionFailure()
-           << "Expected optional to contain a value but no value was found.";
-  }
-
-  return AssertionSuccess();
-}
-
-TEST(OptionalExceptionSafety, ThrowingConstructors) {
-  auto thrower_nonempty = Optional(Thrower(kInitialInteger));
-  testing::TestThrowingCtor<Optional>(thrower_nonempty);
-
-  auto integer_nonempty = absl::optional<int>(kInitialInteger);
-  testing::TestThrowingCtor<Optional>(integer_nonempty);
-  testing::TestThrowingCtor<Optional>(std::move(integer_nonempty));  // NOLINT
-
-  testing::TestThrowingCtor<Optional>(kInitialInteger);
-  using ThrowerVec = std::vector<Thrower, testing::ThrowingAllocator<Thrower>>;
-  testing::TestThrowingCtor<absl::optional<ThrowerVec>>(
-      absl::in_place,
-      std::initializer_list<Thrower>{Thrower(), Thrower(), Thrower()},
-      testing::ThrowingAllocator<Thrower>());
-}
-
-TEST(OptionalExceptionSafety, NothrowConstructors) {
-  // This constructor is marked noexcept. If it throws, the program will
-  // terminate.
-  testing::TestThrowingCtor<MoveOptional>(MoveOptional(kUpdatedInteger));
-}
-
-TEST(OptionalExceptionSafety, Emplace) {
-  // Test the basic guarantee plus test the result of optional::has_value()
-  // is false in all cases
-  auto disengaged_test = MakeExceptionSafetyTester().WithContracts(
-      OptionalInvariants<Optional>, CheckDisengaged<Optional>);
-  auto disengaged_test_empty = disengaged_test.WithInitialValue(Optional());
-  auto disengaged_test_nonempty =
-      disengaged_test.WithInitialValue(Optional(kInitialInteger));
-
-  auto emplace_thrower_directly = [](Optional* optional_ptr) {
-    optional_ptr->emplace(kUpdatedInteger);
-  };
-  EXPECT_TRUE(disengaged_test_empty.Test(emplace_thrower_directly));
-  EXPECT_TRUE(disengaged_test_nonempty.Test(emplace_thrower_directly));
-
-  auto emplace_thrower_copy = [](Optional* optional_ptr) {
-    auto thrower = Thrower(kUpdatedInteger, testing::nothrow_ctor);
-    optional_ptr->emplace(thrower);
-  };
-  EXPECT_TRUE(disengaged_test_empty.Test(emplace_thrower_copy));
-  EXPECT_TRUE(disengaged_test_nonempty.Test(emplace_thrower_copy));
-}
-
-TEST(OptionalExceptionSafety, EverythingThrowsSwap) {
-  // Test the basic guarantee plus test the result of optional::has_value()
-  // remains the same
-  auto test =
-      MakeExceptionSafetyTester().WithContracts(OptionalInvariants<Optional>);
-  auto disengaged_test_empty = test.WithInitialValue(Optional())
-                                   .WithContracts(CheckDisengaged<Optional>);
-  auto engaged_test_nonempty = test.WithInitialValue(Optional(kInitialInteger))
-                                   .WithContracts(CheckEngaged<Optional>);
-
-  auto swap_empty = [](Optional* optional_ptr) {
-    auto empty = Optional();
-    optional_ptr->swap(empty);
-  };
-  EXPECT_TRUE(engaged_test_nonempty.Test(swap_empty));
-
-  auto swap_nonempty = [](Optional* optional_ptr) {
-    auto nonempty =
-        Optional(absl::in_place, kUpdatedInteger, testing::nothrow_ctor);
-    optional_ptr->swap(nonempty);
-  };
-  EXPECT_TRUE(disengaged_test_empty.Test(swap_nonempty));
-  EXPECT_TRUE(engaged_test_nonempty.Test(swap_nonempty));
-}
-
-TEST(OptionalExceptionSafety, NoThrowMoveSwap) {
-  // Tests the nothrow guarantee for optional of T with non-throwing move
-  {
-    auto empty = MoveOptional();
-    auto nonempty = MoveOptional(kInitialInteger);
-    EXPECT_TRUE(testing::TestNothrowOp([&]() { nonempty.swap(empty); }));
-  }
-  {
-    auto nonempty = MoveOptional(kUpdatedInteger);
-    auto empty = MoveOptional();
-    EXPECT_TRUE(testing::TestNothrowOp([&]() { empty.swap(nonempty); }));
-  }
-  {
-    auto nonempty_from = MoveOptional(kUpdatedInteger);
-    auto nonempty_to = MoveOptional(kInitialInteger);
-    EXPECT_TRUE(
-        testing::TestNothrowOp([&]() { nonempty_to.swap(nonempty_from); }));
-  }
-}
-
-TEST(OptionalExceptionSafety, CopyAssign) {
-  // Test the basic guarantee plus test the result of optional::has_value()
-  // remains the same
-  auto test =
-      MakeExceptionSafetyTester().WithContracts(OptionalInvariants<Optional>);
-  auto disengaged_test_empty = test.WithInitialValue(Optional())
-                                   .WithContracts(CheckDisengaged<Optional>);
-  auto engaged_test_nonempty = test.WithInitialValue(Optional(kInitialInteger))
-                                   .WithContracts(CheckEngaged<Optional>);
-
-  auto copyassign_nonempty = [](Optional* optional_ptr) {
-    auto nonempty =
-        Optional(absl::in_place, kUpdatedInteger, testing::nothrow_ctor);
-    *optional_ptr = nonempty;
-  };
-  EXPECT_TRUE(disengaged_test_empty.Test(copyassign_nonempty));
-  EXPECT_TRUE(engaged_test_nonempty.Test(copyassign_nonempty));
-
-  auto copyassign_thrower = [](Optional* optional_ptr) {
-    auto thrower = Thrower(kUpdatedInteger, testing::nothrow_ctor);
-    *optional_ptr = thrower;
-  };
-  EXPECT_TRUE(disengaged_test_empty.Test(copyassign_thrower));
-  EXPECT_TRUE(engaged_test_nonempty.Test(copyassign_thrower));
-}
-
-TEST(OptionalExceptionSafety, MoveAssign) {
-  // Test the basic guarantee plus test the result of optional::has_value()
-  // remains the same
-  auto test =
-      MakeExceptionSafetyTester().WithContracts(OptionalInvariants<Optional>);
-  auto disengaged_test_empty = test.WithInitialValue(Optional())
-                                   .WithContracts(CheckDisengaged<Optional>);
-  auto engaged_test_nonempty = test.WithInitialValue(Optional(kInitialInteger))
-                                   .WithContracts(CheckEngaged<Optional>);
-
-  auto moveassign_empty = [](Optional* optional_ptr) {
-    auto empty = Optional();
-    *optional_ptr = std::move(empty);
-  };
-  EXPECT_TRUE(engaged_test_nonempty.Test(moveassign_empty));
-
-  auto moveassign_nonempty = [](Optional* optional_ptr) {
-    auto nonempty =
-        Optional(absl::in_place, kUpdatedInteger, testing::nothrow_ctor);
-    *optional_ptr = std::move(nonempty);
-  };
-  EXPECT_TRUE(disengaged_test_empty.Test(moveassign_nonempty));
-  EXPECT_TRUE(engaged_test_nonempty.Test(moveassign_nonempty));
-
-  auto moveassign_thrower = [](Optional* optional_ptr) {
-    auto thrower = Thrower(kUpdatedInteger, testing::nothrow_ctor);
-    *optional_ptr = std::move(thrower);
-  };
-  EXPECT_TRUE(disengaged_test_empty.Test(moveassign_thrower));
-  EXPECT_TRUE(engaged_test_nonempty.Test(moveassign_thrower));
-}
-
-TEST(OptionalExceptionSafety, NothrowMoveAssign) {
-  // Tests the nothrow guarantee for optional of T with non-throwing move
-  {
-    auto empty = MoveOptional();
-    auto nonempty = MoveOptional(kInitialInteger);
-    EXPECT_TRUE(testing::TestNothrowOp([&]() { nonempty = std::move(empty); }));
-  }
-  {
-    auto nonempty = MoveOptional(kInitialInteger);
-    auto empty = MoveOptional();
-    EXPECT_TRUE(testing::TestNothrowOp([&]() { empty = std::move(nonempty); }));
-  }
-  {
-    auto nonempty_from = MoveOptional(kUpdatedInteger);
-    auto nonempty_to = MoveOptional(kInitialInteger);
-    EXPECT_TRUE(testing::TestNothrowOp(
-        [&]() { nonempty_to = std::move(nonempty_from); }));
-  }
-  {
-    auto thrower = MoveThrower(kUpdatedInteger);
-    auto empty = MoveOptional();
-    EXPECT_TRUE(testing::TestNothrowOp([&]() { empty = std::move(thrower); }));
-  }
-  {
-    auto thrower = MoveThrower(kUpdatedInteger);
-    auto nonempty = MoveOptional(kInitialInteger);
-    EXPECT_TRUE(
-        testing::TestNothrowOp([&]() { nonempty = std::move(thrower); }));
-  }
-}
-
-}  // namespace
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // #if !defined(ABSL_USES_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS)
diff --git a/third_party/abseil_cpp/absl/types/optional_test.cc b/third_party/abseil_cpp/absl/types/optional_test.cc
deleted file mode 100644
index 7ef142cb99..0000000000
--- a/third_party/abseil_cpp/absl/types/optional_test.cc
+++ /dev/null
@@ -1,1659 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/optional.h"
-
-// This test is a no-op when absl::optional is an alias for std::optional.
-#if !defined(ABSL_USES_STD_OPTIONAL)
-
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/raw_logging.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-
-struct Hashable {};
-
-namespace std {
-template <>
-struct hash<Hashable> {
-  size_t operator()(const Hashable&) { return 0; }
-};
-}  // namespace std
-
-struct NonHashable {};
-
-namespace {
-
-std::string TypeQuals(std::string&) { return "&"; }
-std::string TypeQuals(std::string&&) { return "&&"; }
-std::string TypeQuals(const std::string&) { return "c&"; }
-std::string TypeQuals(const std::string&&) { return "c&&"; }
-
-struct StructorListener {
-  int construct0 = 0;
-  int construct1 = 0;
-  int construct2 = 0;
-  int listinit = 0;
-  int copy = 0;
-  int move = 0;
-  int copy_assign = 0;
-  int move_assign = 0;
-  int destruct = 0;
-  int volatile_copy = 0;
-  int volatile_move = 0;
-  int volatile_copy_assign = 0;
-  int volatile_move_assign = 0;
-};
-
-// Suppress MSVC warnings.
-// 4521: multiple copy constructors specified
-// 4522: multiple assignment operators specified
-// We wrote multiple of them to test that the correct overloads are selected.
-#ifdef _MSC_VER
-#pragma warning( push )
-#pragma warning( disable : 4521)
-#pragma warning( disable : 4522)
-#endif
-struct Listenable {
-  static StructorListener* listener;
-
-  Listenable() { ++listener->construct0; }
-  explicit Listenable(int /*unused*/) { ++listener->construct1; }
-  Listenable(int /*unused*/, int /*unused*/) { ++listener->construct2; }
-  Listenable(std::initializer_list<int> /*unused*/) { ++listener->listinit; }
-  Listenable(const Listenable& /*unused*/) { ++listener->copy; }
-  Listenable(const volatile Listenable& /*unused*/) {
-    ++listener->volatile_copy;
-  }
-  Listenable(volatile Listenable&& /*unused*/) { ++listener->volatile_move; }
-  Listenable(Listenable&& /*unused*/) { ++listener->move; }
-  Listenable& operator=(const Listenable& /*unused*/) {
-    ++listener->copy_assign;
-    return *this;
-  }
-  Listenable& operator=(Listenable&& /*unused*/) {
-    ++listener->move_assign;
-    return *this;
-  }
-  // use void return type instead of volatile T& to work around GCC warning
-  // when the assignment's returned reference is ignored.
-  void operator=(const volatile Listenable& /*unused*/) volatile {
-    ++listener->volatile_copy_assign;
-  }
-  void operator=(volatile Listenable&& /*unused*/) volatile {
-    ++listener->volatile_move_assign;
-  }
-  ~Listenable() { ++listener->destruct; }
-};
-#ifdef _MSC_VER
-#pragma warning( pop )
-#endif
-
-StructorListener* Listenable::listener = nullptr;
-
-// ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST is defined to 1 when the standard
-// library implementation doesn't marked initializer_list's default constructor
-// constexpr. The C++11 standard doesn't specify constexpr on it, but C++14
-// added it. However, libstdc++ 4.7 marked it constexpr.
-#if defined(_LIBCPP_VERSION) && \
-    (_LIBCPP_STD_VER <= 11 || defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR))
-#define ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST 1
-#endif
-
-struct ConstexprType {
-  enum CtorTypes {
-    kCtorDefault,
-    kCtorInt,
-    kCtorInitializerList,
-    kCtorConstChar
-  };
-  constexpr ConstexprType() : x(kCtorDefault) {}
-  constexpr explicit ConstexprType(int i) : x(kCtorInt) {}
-#ifndef ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST
-  constexpr ConstexprType(std::initializer_list<int> il)
-      : x(kCtorInitializerList) {}
-#endif
-  constexpr ConstexprType(const char*)  // NOLINT(runtime/explicit)
-      : x(kCtorConstChar) {}
-  int x;
-};
-
-struct Copyable {
-  Copyable() {}
-  Copyable(const Copyable&) {}
-  Copyable& operator=(const Copyable&) { return *this; }
-};
-
-struct MoveableThrow {
-  MoveableThrow() {}
-  MoveableThrow(MoveableThrow&&) {}
-  MoveableThrow& operator=(MoveableThrow&&) { return *this; }
-};
-
-struct MoveableNoThrow {
-  MoveableNoThrow() {}
-  MoveableNoThrow(MoveableNoThrow&&) noexcept {}
-  MoveableNoThrow& operator=(MoveableNoThrow&&) noexcept { return *this; }
-};
-
-struct NonMovable {
-  NonMovable() {}
-  NonMovable(const NonMovable&) = delete;
-  NonMovable& operator=(const NonMovable&) = delete;
-  NonMovable(NonMovable&&) = delete;
-  NonMovable& operator=(NonMovable&&) = delete;
-};
-
-struct NoDefault {
-  NoDefault() = delete;
-  NoDefault(const NoDefault&) {}
-  NoDefault& operator=(const NoDefault&) { return *this; }
-};
-
-struct ConvertsFromInPlaceT {
-  ConvertsFromInPlaceT(absl::in_place_t) {}  // NOLINT
-};
-
-TEST(optionalTest, DefaultConstructor) {
-  absl::optional<int> empty;
-  EXPECT_FALSE(empty);
-  constexpr absl::optional<int> cempty;
-  static_assert(!cempty.has_value(), "");
-  EXPECT_TRUE(
-      std::is_nothrow_default_constructible<absl::optional<int>>::value);
-}
-
-TEST(optionalTest, nulloptConstructor) {
-  absl::optional<int> empty(absl::nullopt);
-  EXPECT_FALSE(empty);
-  constexpr absl::optional<int> cempty{absl::nullopt};
-  static_assert(!cempty.has_value(), "");
-  EXPECT_TRUE((std::is_nothrow_constructible<absl::optional<int>,
-                                             absl::nullopt_t>::value));
-}
-
-TEST(optionalTest, CopyConstructor) {
-  {
-    absl::optional<int> empty, opt42 = 42;
-    absl::optional<int> empty_copy(empty);
-    EXPECT_FALSE(empty_copy);
-    absl::optional<int> opt42_copy(opt42);
-    EXPECT_TRUE(opt42_copy);
-    EXPECT_EQ(42, *opt42_copy);
-  }
-  {
-    absl::optional<const int> empty, opt42 = 42;
-    absl::optional<const int> empty_copy(empty);
-    EXPECT_FALSE(empty_copy);
-    absl::optional<const int> opt42_copy(opt42);
-    EXPECT_TRUE(opt42_copy);
-    EXPECT_EQ(42, *opt42_copy);
-  }
-  {
-    absl::optional<volatile int> empty, opt42 = 42;
-    absl::optional<volatile int> empty_copy(empty);
-    EXPECT_FALSE(empty_copy);
-    absl::optional<volatile int> opt42_copy(opt42);
-    EXPECT_TRUE(opt42_copy);
-    EXPECT_EQ(42, *opt42_copy);
-  }
-  // test copyablility
-  EXPECT_TRUE(std::is_copy_constructible<absl::optional<int>>::value);
-  EXPECT_TRUE(std::is_copy_constructible<absl::optional<Copyable>>::value);
-  EXPECT_FALSE(
-      std::is_copy_constructible<absl::optional<MoveableThrow>>::value);
-  EXPECT_FALSE(
-      std::is_copy_constructible<absl::optional<MoveableNoThrow>>::value);
-  EXPECT_FALSE(std::is_copy_constructible<absl::optional<NonMovable>>::value);
-
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<absl::optional<Copyable>>::value);
-#if defined(ABSL_USES_STD_OPTIONAL) && defined(__GLIBCXX__)
-  // libstdc++ std::optional implementation (as of 7.2) has a bug: when T is
-  // trivially copyable, optional<T> is not trivially copyable (due to one of
-  // its base class is unconditionally nontrivial).
-#define ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG 1
-#endif
-#ifndef ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG
-  EXPECT_TRUE(
-      absl::is_trivially_copy_constructible<absl::optional<int>>::value);
-  EXPECT_TRUE(
-      absl::is_trivially_copy_constructible<absl::optional<const int>>::value);
-#ifndef _MSC_VER
-  // See defect report "Trivial copy/move constructor for class with volatile
-  // member" at
-  // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2094
-  // A class with non-static data member of volatile-qualified type should still
-  // have a trivial copy constructor if the data member is trivial.
-  // Also a cv-qualified scalar type should be trivially copyable.
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<
-              absl::optional<volatile int>>::value);
-#endif  // _MSC_VER
-#endif  // ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG
-
-  // constexpr copy constructor for trivially copyable types
-  {
-    constexpr absl::optional<int> o1;
-    constexpr absl::optional<int> o2 = o1;
-    static_assert(!o2, "");
-  }
-  {
-    constexpr absl::optional<int> o1 = 42;
-    constexpr absl::optional<int> o2 = o1;
-    static_assert(o2, "");
-    static_assert(*o2 == 42, "");
-  }
-  {
-    struct TrivialCopyable {
-      constexpr TrivialCopyable() : x(0) {}
-      constexpr explicit TrivialCopyable(int i) : x(i) {}
-      int x;
-    };
-    constexpr absl::optional<TrivialCopyable> o1(42);
-    constexpr absl::optional<TrivialCopyable> o2 = o1;
-    static_assert(o2, "");
-    static_assert((*o2).x == 42, "");
-#ifndef ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG
-    EXPECT_TRUE(absl::is_trivially_copy_constructible<
-                absl::optional<TrivialCopyable>>::value);
-    EXPECT_TRUE(absl::is_trivially_copy_constructible<
-                absl::optional<const TrivialCopyable>>::value);
-#endif
-    // When testing with VS 2017 15.3, there seems to be a bug in MSVC
-    // std::optional when T is volatile-qualified. So skipping this test.
-    // Bug report:
-    // https://connect.microsoft.com/VisualStudio/feedback/details/3142534
-#if defined(ABSL_USES_STD_OPTIONAL) && defined(_MSC_VER) && _MSC_VER >= 1911
-#define ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG 1
-#endif
-#ifndef ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG
-    EXPECT_FALSE(std::is_copy_constructible<
-                 absl::optional<volatile TrivialCopyable>>::value);
-#endif
-  }
-}
-
-TEST(optionalTest, MoveConstructor) {
-  absl::optional<int> empty, opt42 = 42;
-  absl::optional<int> empty_move(std::move(empty));
-  EXPECT_FALSE(empty_move);
-  absl::optional<int> opt42_move(std::move(opt42));
-  EXPECT_TRUE(opt42_move);
-  EXPECT_EQ(42, opt42_move);
-  // test movability
-  EXPECT_TRUE(std::is_move_constructible<absl::optional<int>>::value);
-  EXPECT_TRUE(std::is_move_constructible<absl::optional<Copyable>>::value);
-  EXPECT_TRUE(std::is_move_constructible<absl::optional<MoveableThrow>>::value);
-  EXPECT_TRUE(
-      std::is_move_constructible<absl::optional<MoveableNoThrow>>::value);
-  EXPECT_FALSE(std::is_move_constructible<absl::optional<NonMovable>>::value);
-  // test noexcept
-  EXPECT_TRUE(std::is_nothrow_move_constructible<absl::optional<int>>::value);
-#ifndef ABSL_USES_STD_OPTIONAL
-  EXPECT_EQ(
-      absl::default_allocator_is_nothrow::value,
-      std::is_nothrow_move_constructible<absl::optional<MoveableThrow>>::value);
-#endif
-  EXPECT_TRUE(std::is_nothrow_move_constructible<
-              absl::optional<MoveableNoThrow>>::value);
-}
-
-TEST(optionalTest, Destructor) {
-  struct Trivial {};
-
-  struct NonTrivial {
-    NonTrivial(const NonTrivial&) {}
-    NonTrivial& operator=(const NonTrivial&) { return *this; }
-    ~NonTrivial() {}
-  };
-
-  EXPECT_TRUE(std::is_trivially_destructible<absl::optional<int>>::value);
-  EXPECT_TRUE(std::is_trivially_destructible<absl::optional<Trivial>>::value);
-  EXPECT_FALSE(
-      std::is_trivially_destructible<absl::optional<NonTrivial>>::value);
-}
-
-TEST(optionalTest, InPlaceConstructor) {
-  constexpr absl::optional<ConstexprType> opt0{absl::in_place_t()};
-  static_assert(opt0, "");
-  static_assert((*opt0).x == ConstexprType::kCtorDefault, "");
-  constexpr absl::optional<ConstexprType> opt1{absl::in_place_t(), 1};
-  static_assert(opt1, "");
-  static_assert((*opt1).x == ConstexprType::kCtorInt, "");
-#ifndef ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST
-  constexpr absl::optional<ConstexprType> opt2{absl::in_place_t(), {1, 2}};
-  static_assert(opt2, "");
-  static_assert((*opt2).x == ConstexprType::kCtorInitializerList, "");
-#endif
-
-  EXPECT_FALSE((std::is_constructible<absl::optional<ConvertsFromInPlaceT>,
-                                      absl::in_place_t>::value));
-  EXPECT_FALSE((std::is_constructible<absl::optional<ConvertsFromInPlaceT>,
-                                      const absl::in_place_t&>::value));
-  EXPECT_TRUE(
-      (std::is_constructible<absl::optional<ConvertsFromInPlaceT>,
-                             absl::in_place_t, absl::in_place_t>::value));
-
-  EXPECT_FALSE((std::is_constructible<absl::optional<NoDefault>,
-                                      absl::in_place_t>::value));
-  EXPECT_FALSE((std::is_constructible<absl::optional<NoDefault>,
-                                      absl::in_place_t&&>::value));
-}
-
-// template<U=T> optional(U&&);
-TEST(optionalTest, ValueConstructor) {
-  constexpr absl::optional<int> opt0(0);
-  static_assert(opt0, "");
-  static_assert(*opt0 == 0, "");
-  EXPECT_TRUE((std::is_convertible<int, absl::optional<int>>::value));
-  // Copy initialization ( = "abc") won't work due to optional(optional&&)
-  // is not constexpr. Use list initialization instead. This invokes
-  // absl::optional<ConstexprType>::absl::optional<U>(U&&), with U = const char
-  // (&) [4], which direct-initializes the ConstexprType value held by the
-  // optional via ConstexprType::ConstexprType(const char*).
-  constexpr absl::optional<ConstexprType> opt1 = {"abc"};
-  static_assert(opt1, "");
-  static_assert(ConstexprType::kCtorConstChar == (*opt1).x, "");
-  EXPECT_TRUE(
-      (std::is_convertible<const char*, absl::optional<ConstexprType>>::value));
-  // direct initialization
-  constexpr absl::optional<ConstexprType> opt2{2};
-  static_assert(opt2, "");
-  static_assert(ConstexprType::kCtorInt == (*opt2).x, "");
-  EXPECT_FALSE(
-      (std::is_convertible<int, absl::optional<ConstexprType>>::value));
-
-  // this invokes absl::optional<int>::optional(int&&)
-  // NOTE: this has different behavior than assignment, e.g.
-  // "opt3 = {};" clears the optional rather than setting the value to 0
-  // According to C++17 standard N4659 [over.ics.list] 16.3.3.1.5, (9.2)- "if
-  // the initializer list has no elements, the implicit conversion is the
-  // identity conversion", so `optional(int&&)` should be a better match than
-  // `optional(optional&&)` which is a user-defined conversion.
-  // Note: GCC 7 has a bug with this overload selection when compiled with
-  // `-std=c++17`.
-#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 7 && \
-    __cplusplus == 201703L
-#define ABSL_GCC7_OVER_ICS_LIST_BUG 1
-#endif
-#ifndef ABSL_GCC7_OVER_ICS_LIST_BUG
-  constexpr absl::optional<int> opt3({});
-  static_assert(opt3, "");
-  static_assert(*opt3 == 0, "");
-#endif
-
-  // this invokes the move constructor with a default constructed optional
-  // because non-template function is a better match than template function.
-  absl::optional<ConstexprType> opt4({});
-  EXPECT_FALSE(opt4);
-}
-
-struct Implicit {};
-
-struct Explicit {};
-
-struct Convert {
-  Convert(const Implicit&)  // NOLINT(runtime/explicit)
-      : implicit(true), move(false) {}
-  Convert(Implicit&&)  // NOLINT(runtime/explicit)
-      : implicit(true), move(true) {}
-  explicit Convert(const Explicit&) : implicit(false), move(false) {}
-  explicit Convert(Explicit&&) : implicit(false), move(true) {}
-
-  bool implicit;
-  bool move;
-};
-
-struct ConvertFromOptional {
-  ConvertFromOptional(const Implicit&)  // NOLINT(runtime/explicit)
-      : implicit(true), move(false), from_optional(false) {}
-  ConvertFromOptional(Implicit&&)  // NOLINT(runtime/explicit)
-      : implicit(true), move(true), from_optional(false) {}
-  ConvertFromOptional(
-      const absl::optional<Implicit>&)  // NOLINT(runtime/explicit)
-      : implicit(true), move(false), from_optional(true) {}
-  ConvertFromOptional(absl::optional<Implicit>&&)  // NOLINT(runtime/explicit)
-      : implicit(true), move(true), from_optional(true) {}
-  explicit ConvertFromOptional(const Explicit&)
-      : implicit(false), move(false), from_optional(false) {}
-  explicit ConvertFromOptional(Explicit&&)
-      : implicit(false), move(true), from_optional(false) {}
-  explicit ConvertFromOptional(const absl::optional<Explicit>&)
-      : implicit(false), move(false), from_optional(true) {}
-  explicit ConvertFromOptional(absl::optional<Explicit>&&)
-      : implicit(false), move(true), from_optional(true) {}
-
-  bool implicit;
-  bool move;
-  bool from_optional;
-};
-
-TEST(optionalTest, ConvertingConstructor) {
-  absl::optional<Implicit> i_empty;
-  absl::optional<Implicit> i(absl::in_place);
-  absl::optional<Explicit> e_empty;
-  absl::optional<Explicit> e(absl::in_place);
-  {
-    // implicitly constructing absl::optional<Convert> from
-    // absl::optional<Implicit>
-    absl::optional<Convert> empty = i_empty;
-    EXPECT_FALSE(empty);
-    absl::optional<Convert> opt_copy = i;
-    EXPECT_TRUE(opt_copy);
-    EXPECT_TRUE(opt_copy->implicit);
-    EXPECT_FALSE(opt_copy->move);
-    absl::optional<Convert> opt_move = absl::optional<Implicit>(absl::in_place);
-    EXPECT_TRUE(opt_move);
-    EXPECT_TRUE(opt_move->implicit);
-    EXPECT_TRUE(opt_move->move);
-  }
-  {
-    // explicitly constructing absl::optional<Convert> from
-    // absl::optional<Explicit>
-    absl::optional<Convert> empty(e_empty);
-    EXPECT_FALSE(empty);
-    absl::optional<Convert> opt_copy(e);
-    EXPECT_TRUE(opt_copy);
-    EXPECT_FALSE(opt_copy->implicit);
-    EXPECT_FALSE(opt_copy->move);
-    EXPECT_FALSE((std::is_convertible<const absl::optional<Explicit>&,
-                                      absl::optional<Convert>>::value));
-    absl::optional<Convert> opt_move{absl::optional<Explicit>(absl::in_place)};
-    EXPECT_TRUE(opt_move);
-    EXPECT_FALSE(opt_move->implicit);
-    EXPECT_TRUE(opt_move->move);
-    EXPECT_FALSE((std::is_convertible<absl::optional<Explicit>&&,
-                                      absl::optional<Convert>>::value));
-  }
-  {
-    // implicitly constructing absl::optional<ConvertFromOptional> from
-    // absl::optional<Implicit> via
-    // ConvertFromOptional(absl::optional<Implicit>&&) check that
-    // ConvertFromOptional(Implicit&&) is NOT called
-    static_assert(
-        std::is_convertible<absl::optional<Implicit>,
-                            absl::optional<ConvertFromOptional>>::value,
-        "");
-    absl::optional<ConvertFromOptional> opt0 = i_empty;
-    EXPECT_TRUE(opt0);
-    EXPECT_TRUE(opt0->implicit);
-    EXPECT_FALSE(opt0->move);
-    EXPECT_TRUE(opt0->from_optional);
-    absl::optional<ConvertFromOptional> opt1 = absl::optional<Implicit>();
-    EXPECT_TRUE(opt1);
-    EXPECT_TRUE(opt1->implicit);
-    EXPECT_TRUE(opt1->move);
-    EXPECT_TRUE(opt1->from_optional);
-  }
-  {
-    // implicitly constructing absl::optional<ConvertFromOptional> from
-    // absl::optional<Explicit> via
-    // ConvertFromOptional(absl::optional<Explicit>&&) check that
-    // ConvertFromOptional(Explicit&&) is NOT called
-    absl::optional<ConvertFromOptional> opt0(e_empty);
-    EXPECT_TRUE(opt0);
-    EXPECT_FALSE(opt0->implicit);
-    EXPECT_FALSE(opt0->move);
-    EXPECT_TRUE(opt0->from_optional);
-    EXPECT_FALSE(
-        (std::is_convertible<const absl::optional<Explicit>&,
-                             absl::optional<ConvertFromOptional>>::value));
-    absl::optional<ConvertFromOptional> opt1{absl::optional<Explicit>()};
-    EXPECT_TRUE(opt1);
-    EXPECT_FALSE(opt1->implicit);
-    EXPECT_TRUE(opt1->move);
-    EXPECT_TRUE(opt1->from_optional);
-    EXPECT_FALSE(
-        (std::is_convertible<absl::optional<Explicit>&&,
-                             absl::optional<ConvertFromOptional>>::value));
-  }
-}
-
-TEST(optionalTest, StructorBasic) {
-  StructorListener listener;
-  Listenable::listener = &listener;
-  {
-    absl::optional<Listenable> empty;
-    EXPECT_FALSE(empty);
-    absl::optional<Listenable> opt0(absl::in_place);
-    EXPECT_TRUE(opt0);
-    absl::optional<Listenable> opt1(absl::in_place, 1);
-    EXPECT_TRUE(opt1);
-    absl::optional<Listenable> opt2(absl::in_place, 1, 2);
-    EXPECT_TRUE(opt2);
-  }
-  EXPECT_EQ(1, listener.construct0);
-  EXPECT_EQ(1, listener.construct1);
-  EXPECT_EQ(1, listener.construct2);
-  EXPECT_EQ(3, listener.destruct);
-}
-
-TEST(optionalTest, CopyMoveStructor) {
-  StructorListener listener;
-  Listenable::listener = &listener;
-  absl::optional<Listenable> original(absl::in_place);
-  EXPECT_EQ(1, listener.construct0);
-  EXPECT_EQ(0, listener.copy);
-  EXPECT_EQ(0, listener.move);
-  absl::optional<Listenable> copy(original);
-  EXPECT_EQ(1, listener.construct0);
-  EXPECT_EQ(1, listener.copy);
-  EXPECT_EQ(0, listener.move);
-  absl::optional<Listenable> move(std::move(original));
-  EXPECT_EQ(1, listener.construct0);
-  EXPECT_EQ(1, listener.copy);
-  EXPECT_EQ(1, listener.move);
-}
-
-TEST(optionalTest, ListInit) {
-  StructorListener listener;
-  Listenable::listener = &listener;
-  absl::optional<Listenable> listinit1(absl::in_place, {1});
-  absl::optional<Listenable> listinit2(absl::in_place, {1, 2});
-  EXPECT_EQ(2, listener.listinit);
-}
-
-TEST(optionalTest, AssignFromNullopt) {
-  absl::optional<int> opt(1);
-  opt = absl::nullopt;
-  EXPECT_FALSE(opt);
-
-  StructorListener listener;
-  Listenable::listener = &listener;
-  absl::optional<Listenable> opt1(absl::in_place);
-  opt1 = absl::nullopt;
-  EXPECT_FALSE(opt1);
-  EXPECT_EQ(1, listener.construct0);
-  EXPECT_EQ(1, listener.destruct);
-
-  EXPECT_TRUE((
-      std::is_nothrow_assignable<absl::optional<int>, absl::nullopt_t>::value));
-  EXPECT_TRUE((std::is_nothrow_assignable<absl::optional<Listenable>,
-                                          absl::nullopt_t>::value));
-}
-
-TEST(optionalTest, CopyAssignment) {
-  const absl::optional<int> empty, opt1 = 1, opt2 = 2;
-  absl::optional<int> empty_to_opt1, opt1_to_opt2, opt2_to_empty;
-
-  EXPECT_FALSE(empty_to_opt1);
-  empty_to_opt1 = empty;
-  EXPECT_FALSE(empty_to_opt1);
-  empty_to_opt1 = opt1;
-  EXPECT_TRUE(empty_to_opt1);
-  EXPECT_EQ(1, empty_to_opt1.value());
-
-  EXPECT_FALSE(opt1_to_opt2);
-  opt1_to_opt2 = opt1;
-  EXPECT_TRUE(opt1_to_opt2);
-  EXPECT_EQ(1, opt1_to_opt2.value());
-  opt1_to_opt2 = opt2;
-  EXPECT_TRUE(opt1_to_opt2);
-  EXPECT_EQ(2, opt1_to_opt2.value());
-
-  EXPECT_FALSE(opt2_to_empty);
-  opt2_to_empty = opt2;
-  EXPECT_TRUE(opt2_to_empty);
-  EXPECT_EQ(2, opt2_to_empty.value());
-  opt2_to_empty = empty;
-  EXPECT_FALSE(opt2_to_empty);
-
-  EXPECT_FALSE(absl::is_copy_assignable<absl::optional<const int>>::value);
-  EXPECT_TRUE(absl::is_copy_assignable<absl::optional<Copyable>>::value);
-  EXPECT_FALSE(absl::is_copy_assignable<absl::optional<MoveableThrow>>::value);
-  EXPECT_FALSE(
-      absl::is_copy_assignable<absl::optional<MoveableNoThrow>>::value);
-  EXPECT_FALSE(absl::is_copy_assignable<absl::optional<NonMovable>>::value);
-
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<int>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<volatile int>::value);
-
-  struct Trivial {
-    int i;
-  };
-  struct NonTrivial {
-    NonTrivial& operator=(const NonTrivial&) { return *this; }
-    int i;
-  };
-
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial>::value);
-  EXPECT_FALSE(absl::is_copy_assignable<const Trivial>::value);
-  EXPECT_FALSE(absl::is_copy_assignable<volatile Trivial>::value);
-  EXPECT_TRUE(absl::is_copy_assignable<NonTrivial>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<NonTrivial>::value);
-
-  // std::optional doesn't support volatile nontrivial types.
-#ifndef ABSL_USES_STD_OPTIONAL
-  {
-    StructorListener listener;
-    Listenable::listener = &listener;
-
-    absl::optional<volatile Listenable> empty, set(absl::in_place);
-    EXPECT_EQ(1, listener.construct0);
-    absl::optional<volatile Listenable> empty_to_empty, empty_to_set,
-        set_to_empty(absl::in_place), set_to_set(absl::in_place);
-    EXPECT_EQ(3, listener.construct0);
-    empty_to_empty = empty;  // no effect
-    empty_to_set = set;      // copy construct
-    set_to_empty = empty;    // destruct
-    set_to_set = set;        // copy assign
-    EXPECT_EQ(1, listener.volatile_copy);
-    EXPECT_EQ(0, listener.volatile_move);
-    EXPECT_EQ(1, listener.destruct);
-    EXPECT_EQ(1, listener.volatile_copy_assign);
-  }
-#endif  // ABSL_USES_STD_OPTIONAL
-}
-
-TEST(optionalTest, MoveAssignment) {
-  {
-    StructorListener listener;
-    Listenable::listener = &listener;
-
-    absl::optional<Listenable> empty1, empty2, set1(absl::in_place),
-        set2(absl::in_place);
-    EXPECT_EQ(2, listener.construct0);
-    absl::optional<Listenable> empty_to_empty, empty_to_set,
-        set_to_empty(absl::in_place), set_to_set(absl::in_place);
-    EXPECT_EQ(4, listener.construct0);
-    empty_to_empty = std::move(empty1);
-    empty_to_set = std::move(set1);
-    set_to_empty = std::move(empty2);
-    set_to_set = std::move(set2);
-    EXPECT_EQ(0, listener.copy);
-    EXPECT_EQ(1, listener.move);
-    EXPECT_EQ(1, listener.destruct);
-    EXPECT_EQ(1, listener.move_assign);
-  }
-  // std::optional doesn't support volatile nontrivial types.
-#ifndef ABSL_USES_STD_OPTIONAL
-  {
-    StructorListener listener;
-    Listenable::listener = &listener;
-
-    absl::optional<volatile Listenable> empty1, empty2, set1(absl::in_place),
-        set2(absl::in_place);
-    EXPECT_EQ(2, listener.construct0);
-    absl::optional<volatile Listenable> empty_to_empty, empty_to_set,
-        set_to_empty(absl::in_place), set_to_set(absl::in_place);
-    EXPECT_EQ(4, listener.construct0);
-    empty_to_empty = std::move(empty1);  // no effect
-    empty_to_set = std::move(set1);      // move construct
-    set_to_empty = std::move(empty2);    // destruct
-    set_to_set = std::move(set2);        // move assign
-    EXPECT_EQ(0, listener.volatile_copy);
-    EXPECT_EQ(1, listener.volatile_move);
-    EXPECT_EQ(1, listener.destruct);
-    EXPECT_EQ(1, listener.volatile_move_assign);
-  }
-#endif  // ABSL_USES_STD_OPTIONAL
-  EXPECT_FALSE(absl::is_move_assignable<absl::optional<const int>>::value);
-  EXPECT_TRUE(absl::is_move_assignable<absl::optional<Copyable>>::value);
-  EXPECT_TRUE(absl::is_move_assignable<absl::optional<MoveableThrow>>::value);
-  EXPECT_TRUE(absl::is_move_assignable<absl::optional<MoveableNoThrow>>::value);
-  EXPECT_FALSE(absl::is_move_assignable<absl::optional<NonMovable>>::value);
-
-  EXPECT_FALSE(
-      std::is_nothrow_move_assignable<absl::optional<MoveableThrow>>::value);
-  EXPECT_TRUE(
-      std::is_nothrow_move_assignable<absl::optional<MoveableNoThrow>>::value);
-}
-
-struct NoConvertToOptional {
-  // disable implicit conversion from const NoConvertToOptional&
-  // to absl::optional<NoConvertToOptional>.
-  NoConvertToOptional(const NoConvertToOptional&) = delete;
-};
-
-struct CopyConvert {
-  CopyConvert(const NoConvertToOptional&);
-  CopyConvert& operator=(const CopyConvert&) = delete;
-  CopyConvert& operator=(const NoConvertToOptional&);
-};
-
-struct CopyConvertFromOptional {
-  CopyConvertFromOptional(const NoConvertToOptional&);
-  CopyConvertFromOptional(const absl::optional<NoConvertToOptional>&);
-  CopyConvertFromOptional& operator=(const CopyConvertFromOptional&) = delete;
-  CopyConvertFromOptional& operator=(const NoConvertToOptional&);
-  CopyConvertFromOptional& operator=(
-      const absl::optional<NoConvertToOptional>&);
-};
-
-struct MoveConvert {
-  MoveConvert(NoConvertToOptional&&);
-  MoveConvert& operator=(const MoveConvert&) = delete;
-  MoveConvert& operator=(NoConvertToOptional&&);
-};
-
-struct MoveConvertFromOptional {
-  MoveConvertFromOptional(NoConvertToOptional&&);
-  MoveConvertFromOptional(absl::optional<NoConvertToOptional>&&);
-  MoveConvertFromOptional& operator=(const MoveConvertFromOptional&) = delete;
-  MoveConvertFromOptional& operator=(NoConvertToOptional&&);
-  MoveConvertFromOptional& operator=(absl::optional<NoConvertToOptional>&&);
-};
-
-// template <typename U = T> absl::optional<T>& operator=(U&& v);
-TEST(optionalTest, ValueAssignment) {
-  absl::optional<int> opt;
-  EXPECT_FALSE(opt);
-  opt = 42;
-  EXPECT_TRUE(opt);
-  EXPECT_EQ(42, opt.value());
-  opt = absl::nullopt;
-  EXPECT_FALSE(opt);
-  opt = 42;
-  EXPECT_TRUE(opt);
-  EXPECT_EQ(42, opt.value());
-  opt = 43;
-  EXPECT_TRUE(opt);
-  EXPECT_EQ(43, opt.value());
-  opt = {};  // this should clear optional
-  EXPECT_FALSE(opt);
-
-  opt = {44};
-  EXPECT_TRUE(opt);
-  EXPECT_EQ(44, opt.value());
-
-  // U = const NoConvertToOptional&
-  EXPECT_TRUE((std::is_assignable<absl::optional<CopyConvert>&,
-                                  const NoConvertToOptional&>::value));
-  // U = const absl::optional<NoConvertToOptional>&
-  EXPECT_TRUE((std::is_assignable<absl::optional<CopyConvertFromOptional>&,
-                                  const NoConvertToOptional&>::value));
-  // U = const NoConvertToOptional& triggers SFINAE because
-  // std::is_constructible_v<MoveConvert, const NoConvertToOptional&> is false
-  EXPECT_FALSE((std::is_assignable<absl::optional<MoveConvert>&,
-                                   const NoConvertToOptional&>::value));
-  // U = NoConvertToOptional
-  EXPECT_TRUE((std::is_assignable<absl::optional<MoveConvert>&,
-                                  NoConvertToOptional&&>::value));
-  // U = const NoConvertToOptional& triggers SFINAE because
-  // std::is_constructible_v<MoveConvertFromOptional, const
-  // NoConvertToOptional&> is false
-  EXPECT_FALSE((std::is_assignable<absl::optional<MoveConvertFromOptional>&,
-                                   const NoConvertToOptional&>::value));
-  // U = NoConvertToOptional
-  EXPECT_TRUE((std::is_assignable<absl::optional<MoveConvertFromOptional>&,
-                                  NoConvertToOptional&&>::value));
-  // U = const absl::optional<NoConvertToOptional>&
-  EXPECT_TRUE(
-      (std::is_assignable<absl::optional<CopyConvertFromOptional>&,
-                          const absl::optional<NoConvertToOptional>&>::value));
-  // U = absl::optional<NoConvertToOptional>
-  EXPECT_TRUE(
-      (std::is_assignable<absl::optional<MoveConvertFromOptional>&,
-                          absl::optional<NoConvertToOptional>&&>::value));
-}
-
-// template <typename U> absl::optional<T>& operator=(const absl::optional<U>&
-// rhs); template <typename U> absl::optional<T>& operator=(absl::optional<U>&&
-// rhs);
-TEST(optionalTest, ConvertingAssignment) {
-  absl::optional<int> opt_i;
-  absl::optional<char> opt_c('c');
-  opt_i = opt_c;
-  EXPECT_TRUE(opt_i);
-  EXPECT_EQ(*opt_c, *opt_i);
-  opt_i = absl::optional<char>();
-  EXPECT_FALSE(opt_i);
-  opt_i = absl::optional<char>('d');
-  EXPECT_TRUE(opt_i);
-  EXPECT_EQ('d', *opt_i);
-
-  absl::optional<std::string> opt_str;
-  absl::optional<const char*> opt_cstr("abc");
-  opt_str = opt_cstr;
-  EXPECT_TRUE(opt_str);
-  EXPECT_EQ(std::string("abc"), *opt_str);
-  opt_str = absl::optional<const char*>();
-  EXPECT_FALSE(opt_str);
-  opt_str = absl::optional<const char*>("def");
-  EXPECT_TRUE(opt_str);
-  EXPECT_EQ(std::string("def"), *opt_str);
-
-  // operator=(const absl::optional<U>&) with U = NoConvertToOptional
-  EXPECT_TRUE(
-      (std::is_assignable<absl::optional<CopyConvert>,
-                          const absl::optional<NoConvertToOptional>&>::value));
-  // operator=(const absl::optional<U>&) with U = NoConvertToOptional
-  // triggers SFINAE because
-  // std::is_constructible_v<MoveConvert, const NoConvertToOptional&> is false
-  EXPECT_FALSE(
-      (std::is_assignable<absl::optional<MoveConvert>&,
-                          const absl::optional<NoConvertToOptional>&>::value));
-  // operator=(absl::optional<U>&&) with U = NoConvertToOptional
-  EXPECT_TRUE(
-      (std::is_assignable<absl::optional<MoveConvert>&,
-                          absl::optional<NoConvertToOptional>&&>::value));
-  // operator=(const absl::optional<U>&) with U = NoConvertToOptional triggers
-  // SFINAE because std::is_constructible_v<MoveConvertFromOptional, const
-  // NoConvertToOptional&> is false. operator=(U&&) with U = const
-  // absl::optional<NoConverToOptional>& triggers SFINAE because
-  // std::is_constructible<MoveConvertFromOptional,
-  // absl::optional<NoConvertToOptional>&&> is true.
-  EXPECT_FALSE(
-      (std::is_assignable<absl::optional<MoveConvertFromOptional>&,
-                          const absl::optional<NoConvertToOptional>&>::value));
-}
-
-TEST(optionalTest, ResetAndHasValue) {
-  StructorListener listener;
-  Listenable::listener = &listener;
-  absl::optional<Listenable> opt;
-  EXPECT_FALSE(opt);
-  EXPECT_FALSE(opt.has_value());
-  opt.emplace();
-  EXPECT_TRUE(opt);
-  EXPECT_TRUE(opt.has_value());
-  opt.reset();
-  EXPECT_FALSE(opt);
-  EXPECT_FALSE(opt.has_value());
-  EXPECT_EQ(1, listener.destruct);
-  opt.reset();
-  EXPECT_FALSE(opt);
-  EXPECT_FALSE(opt.has_value());
-
-  constexpr absl::optional<int> empty;
-  static_assert(!empty.has_value(), "");
-  constexpr absl::optional<int> nonempty(1);
-  static_assert(nonempty.has_value(), "");
-}
-
-TEST(optionalTest, Emplace) {
-  StructorListener listener;
-  Listenable::listener = &listener;
-  absl::optional<Listenable> opt;
-  EXPECT_FALSE(opt);
-  opt.emplace(1);
-  EXPECT_TRUE(opt);
-  opt.emplace(1, 2);
-  EXPECT_EQ(1, listener.construct1);
-  EXPECT_EQ(1, listener.construct2);
-  EXPECT_EQ(1, listener.destruct);
-
-  absl::optional<std::string> o;
-  EXPECT_TRUE((std::is_same<std::string&, decltype(o.emplace("abc"))>::value));
-  std::string& ref = o.emplace("abc");
-  EXPECT_EQ(&ref, &o.value());
-}
-
-TEST(optionalTest, ListEmplace) {
-  StructorListener listener;
-  Listenable::listener = &listener;
-  absl::optional<Listenable> opt;
-  EXPECT_FALSE(opt);
-  opt.emplace({1});
-  EXPECT_TRUE(opt);
-  opt.emplace({1, 2});
-  EXPECT_EQ(2, listener.listinit);
-  EXPECT_EQ(1, listener.destruct);
-
-  absl::optional<Listenable> o;
-  EXPECT_TRUE((std::is_same<Listenable&, decltype(o.emplace({1}))>::value));
-  Listenable& ref = o.emplace({1});
-  EXPECT_EQ(&ref, &o.value());
-}
-
-TEST(optionalTest, Swap) {
-  absl::optional<int> opt_empty, opt1 = 1, opt2 = 2;
-  EXPECT_FALSE(opt_empty);
-  EXPECT_TRUE(opt1);
-  EXPECT_EQ(1, opt1.value());
-  EXPECT_TRUE(opt2);
-  EXPECT_EQ(2, opt2.value());
-  swap(opt_empty, opt1);
-  EXPECT_FALSE(opt1);
-  EXPECT_TRUE(opt_empty);
-  EXPECT_EQ(1, opt_empty.value());
-  EXPECT_TRUE(opt2);
-  EXPECT_EQ(2, opt2.value());
-  swap(opt_empty, opt1);
-  EXPECT_FALSE(opt_empty);
-  EXPECT_TRUE(opt1);
-  EXPECT_EQ(1, opt1.value());
-  EXPECT_TRUE(opt2);
-  EXPECT_EQ(2, opt2.value());
-  swap(opt1, opt2);
-  EXPECT_FALSE(opt_empty);
-  EXPECT_TRUE(opt1);
-  EXPECT_EQ(2, opt1.value());
-  EXPECT_TRUE(opt2);
-  EXPECT_EQ(1, opt2.value());
-
-  EXPECT_TRUE(noexcept(opt1.swap(opt2)));
-  EXPECT_TRUE(noexcept(swap(opt1, opt2)));
-}
-
-template <int v>
-struct DeletedOpAddr {
-  int value = v;
-  constexpr DeletedOpAddr() = default;
-  constexpr const DeletedOpAddr<v>* operator&() const = delete;  // NOLINT
-  DeletedOpAddr<v>* operator&() = delete;                        // NOLINT
-};
-
-// The static_assert featuring a constexpr call to operator->() is commented out
-// to document the fact that the current implementation of absl::optional<T>
-// expects such usecases to be malformed and not compile.
-TEST(optionalTest, OperatorAddr) {
-  constexpr int v = -1;
-  {  // constexpr
-    constexpr absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{});
-    static_assert(opt.has_value(), "");
-    // static_assert(opt->value == v, "");
-    static_assert((*opt).value == v, "");
-  }
-  {  // non-constexpr
-    const absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{});
-    EXPECT_TRUE(opt.has_value());
-    EXPECT_TRUE(opt->value == v);
-    EXPECT_TRUE((*opt).value == v);
-  }
-}
-
-TEST(optionalTest, PointerStuff) {
-  absl::optional<std::string> opt(absl::in_place, "foo");
-  EXPECT_EQ("foo", *opt);
-  const auto& opt_const = opt;
-  EXPECT_EQ("foo", *opt_const);
-  EXPECT_EQ(opt->size(), 3);
-  EXPECT_EQ(opt_const->size(), 3);
-
-  constexpr absl::optional<ConstexprType> opt1(1);
-  static_assert((*opt1).x == ConstexprType::kCtorInt, "");
-}
-
-// gcc has a bug pre 4.9.1 where it doesn't do correct overload resolution
-// when overloads are const-qualified and *this is an raluve.
-// Skip that test to make the build green again when using the old compiler.
-// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59296 is fixed in 4.9.1.
-#if defined(__GNUC__) && !defined(__clang__)
-#define GCC_VERSION (__GNUC__ * 10000 \
-                     + __GNUC_MINOR__ * 100 \
-                     + __GNUC_PATCHLEVEL__)
-#if GCC_VERSION < 40901
-#define ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG
-#endif
-#endif
-
-// MSVC has a bug with "cv-qualifiers in class construction", fixed in 2017. See
-// https://docs.microsoft.com/en-us/cpp/cpp-conformance-improvements-2017#bug-fixes
-// The compiler some incorrectly ingores the cv-qualifier when generating a
-// class object via a constructor call. For example:
-//
-// class optional {
-//   constexpr T&& value() &&;
-//   constexpr const T&& value() const &&;
-// }
-//
-// using COI = const absl::optional<int>;
-// static_assert(2 == COI(2).value(), "");  // const &&
-//
-// This should invoke the "const &&" overload but since it ignores the const
-// qualifier it finds the "&&" overload the best candidate.
-#if defined(_MSC_VER) && _MSC_VER < 1910
-#define ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG
-#endif
-
-TEST(optionalTest, Value) {
-  using O = absl::optional<std::string>;
-  using CO = const absl::optional<std::string>;
-  using OC = absl::optional<const std::string>;
-  O lvalue(absl::in_place, "lvalue");
-  CO clvalue(absl::in_place, "clvalue");
-  OC lvalue_c(absl::in_place, "lvalue_c");
-  EXPECT_EQ("lvalue", lvalue.value());
-  EXPECT_EQ("clvalue", clvalue.value());
-  EXPECT_EQ("lvalue_c", lvalue_c.value());
-  EXPECT_EQ("xvalue", O(absl::in_place, "xvalue").value());
-  EXPECT_EQ("xvalue_c", OC(absl::in_place, "xvalue_c").value());
-#ifndef ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG
-  EXPECT_EQ("cxvalue", CO(absl::in_place, "cxvalue").value());
-#endif
-  EXPECT_EQ("&", TypeQuals(lvalue.value()));
-  EXPECT_EQ("c&", TypeQuals(clvalue.value()));
-  EXPECT_EQ("c&", TypeQuals(lvalue_c.value()));
-  EXPECT_EQ("&&", TypeQuals(O(absl::in_place, "xvalue").value()));
-#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \
-    !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG)
-  EXPECT_EQ("c&&", TypeQuals(CO(absl::in_place, "cxvalue").value()));
-#endif
-  EXPECT_EQ("c&&", TypeQuals(OC(absl::in_place, "xvalue_c").value()));
-
-  // test on volatile type
-  using OV = absl::optional<volatile int>;
-  OV lvalue_v(absl::in_place, 42);
-  EXPECT_EQ(42, lvalue_v.value());
-  EXPECT_EQ(42, OV(42).value());
-  EXPECT_TRUE((std::is_same<volatile int&, decltype(lvalue_v.value())>::value));
-  EXPECT_TRUE((std::is_same<volatile int&&, decltype(OV(42).value())>::value));
-
-  // test exception throw on value()
-  absl::optional<int> empty;
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW((void)empty.value(), absl::bad_optional_access);
-#else
-  EXPECT_DEATH_IF_SUPPORTED((void)empty.value(), "Bad optional access");
-#endif
-
-  // test constexpr value()
-  constexpr absl::optional<int> o1(1);
-  static_assert(1 == o1.value(), "");  // const &
-#if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG)
-  using COI = const absl::optional<int>;
-  static_assert(2 == COI(2).value(), "");  // const &&
-#endif
-}
-
-TEST(optionalTest, DerefOperator) {
-  using O = absl::optional<std::string>;
-  using CO = const absl::optional<std::string>;
-  using OC = absl::optional<const std::string>;
-  O lvalue(absl::in_place, "lvalue");
-  CO clvalue(absl::in_place, "clvalue");
-  OC lvalue_c(absl::in_place, "lvalue_c");
-  EXPECT_EQ("lvalue", *lvalue);
-  EXPECT_EQ("clvalue", *clvalue);
-  EXPECT_EQ("lvalue_c", *lvalue_c);
-  EXPECT_EQ("xvalue", *O(absl::in_place, "xvalue"));
-  EXPECT_EQ("xvalue_c", *OC(absl::in_place, "xvalue_c"));
-#ifndef ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG
-  EXPECT_EQ("cxvalue", *CO(absl::in_place, "cxvalue"));
-#endif
-  EXPECT_EQ("&", TypeQuals(*lvalue));
-  EXPECT_EQ("c&", TypeQuals(*clvalue));
-  EXPECT_EQ("&&", TypeQuals(*O(absl::in_place, "xvalue")));
-#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \
-    !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG)
-  EXPECT_EQ("c&&", TypeQuals(*CO(absl::in_place, "cxvalue")));
-#endif
-  EXPECT_EQ("c&&", TypeQuals(*OC(absl::in_place, "xvalue_c")));
-
-  // test on volatile type
-  using OV = absl::optional<volatile int>;
-  OV lvalue_v(absl::in_place, 42);
-  EXPECT_EQ(42, *lvalue_v);
-  EXPECT_EQ(42, *OV(42));
-  EXPECT_TRUE((std::is_same<volatile int&, decltype(*lvalue_v)>::value));
-  EXPECT_TRUE((std::is_same<volatile int&&, decltype(*OV(42))>::value));
-
-  constexpr absl::optional<int> opt1(1);
-  static_assert(*opt1 == 1, "");
-#if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG)
-  using COI = const absl::optional<int>;
-  static_assert(*COI(2) == 2, "");
-#endif
-}
-
-TEST(optionalTest, ValueOr) {
-  absl::optional<double> opt_empty, opt_set = 1.2;
-  EXPECT_EQ(42.0, opt_empty.value_or(42));
-  EXPECT_EQ(1.2, opt_set.value_or(42));
-  EXPECT_EQ(42.0, absl::optional<double>().value_or(42));
-  EXPECT_EQ(1.2, absl::optional<double>(1.2).value_or(42));
-
-  constexpr absl::optional<double> copt_empty, copt_set = {1.2};
-  static_assert(42.0 == copt_empty.value_or(42), "");
-  static_assert(1.2 == copt_set.value_or(42), "");
-#ifndef ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG
-  using COD = const absl::optional<double>;
-  static_assert(42.0 == COD().value_or(42), "");
-  static_assert(1.2 == COD(1.2).value_or(42), "");
-#endif
-}
-
-// make_optional cannot be constexpr until C++17
-TEST(optionalTest, make_optional) {
-  auto opt_int = absl::make_optional(42);
-  EXPECT_TRUE((std::is_same<decltype(opt_int), absl::optional<int>>::value));
-  EXPECT_EQ(42, opt_int);
-
-  StructorListener listener;
-  Listenable::listener = &listener;
-
-  absl::optional<Listenable> opt0 = absl::make_optional<Listenable>();
-  EXPECT_EQ(1, listener.construct0);
-  absl::optional<Listenable> opt1 = absl::make_optional<Listenable>(1);
-  EXPECT_EQ(1, listener.construct1);
-  absl::optional<Listenable> opt2 = absl::make_optional<Listenable>(1, 2);
-  EXPECT_EQ(1, listener.construct2);
-  absl::optional<Listenable> opt3 = absl::make_optional<Listenable>({1});
-  absl::optional<Listenable> opt4 = absl::make_optional<Listenable>({1, 2});
-  EXPECT_EQ(2, listener.listinit);
-
-  // Constexpr tests on trivially copyable types
-  // optional<T> has trivial copy/move ctors when T is trivially copyable.
-  // For nontrivial types with constexpr constructors, we need copy elision in
-  // C++17 for make_optional to be constexpr.
-  {
-    constexpr absl::optional<int> c_opt = absl::make_optional(42);
-    static_assert(c_opt.value() == 42, "");
-  }
-  {
-    struct TrivialCopyable {
-      constexpr TrivialCopyable() : x(0) {}
-      constexpr explicit TrivialCopyable(int i) : x(i) {}
-      int x;
-    };
-
-    constexpr TrivialCopyable v;
-    constexpr absl::optional<TrivialCopyable> c_opt0 = absl::make_optional(v);
-    static_assert((*c_opt0).x == 0, "");
-    constexpr absl::optional<TrivialCopyable> c_opt1 =
-        absl::make_optional<TrivialCopyable>();
-    static_assert((*c_opt1).x == 0, "");
-    constexpr absl::optional<TrivialCopyable> c_opt2 =
-        absl::make_optional<TrivialCopyable>(42);
-    static_assert((*c_opt2).x == 42, "");
-  }
-}
-
-template <typename T, typename U>
-void optionalTest_Comparisons_EXPECT_LESS(T x, U y) {
-  EXPECT_FALSE(x == y);
-  EXPECT_TRUE(x != y);
-  EXPECT_TRUE(x < y);
-  EXPECT_FALSE(x > y);
-  EXPECT_TRUE(x <= y);
-  EXPECT_FALSE(x >= y);
-}
-
-template <typename T, typename U>
-void optionalTest_Comparisons_EXPECT_SAME(T x, U y) {
-  EXPECT_TRUE(x == y);
-  EXPECT_FALSE(x != y);
-  EXPECT_FALSE(x < y);
-  EXPECT_FALSE(x > y);
-  EXPECT_TRUE(x <= y);
-  EXPECT_TRUE(x >= y);
-}
-
-template <typename T, typename U>
-void optionalTest_Comparisons_EXPECT_GREATER(T x, U y) {
-  EXPECT_FALSE(x == y);
-  EXPECT_TRUE(x != y);
-  EXPECT_FALSE(x < y);
-  EXPECT_TRUE(x > y);
-  EXPECT_FALSE(x <= y);
-  EXPECT_TRUE(x >= y);
-}
-
-
-template <typename T, typename U, typename V>
-void TestComparisons() {
-  absl::optional<T> ae, a2{2}, a4{4};
-  absl::optional<U> be, b2{2}, b4{4};
-  V v3 = 3;
-
-  // LHS: absl::nullopt, ae, a2, v3, a4
-  // RHS: absl::nullopt, be, b2, v3, b4
-
-  // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(absl::nullopt,absl::nullopt);
-  optionalTest_Comparisons_EXPECT_SAME(absl::nullopt, be);
-  optionalTest_Comparisons_EXPECT_LESS(absl::nullopt, b2);
-  // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(absl::nullopt,v3);
-  optionalTest_Comparisons_EXPECT_LESS(absl::nullopt, b4);
-
-  optionalTest_Comparisons_EXPECT_SAME(ae, absl::nullopt);
-  optionalTest_Comparisons_EXPECT_SAME(ae, be);
-  optionalTest_Comparisons_EXPECT_LESS(ae, b2);
-  optionalTest_Comparisons_EXPECT_LESS(ae, v3);
-  optionalTest_Comparisons_EXPECT_LESS(ae, b4);
-
-  optionalTest_Comparisons_EXPECT_GREATER(a2, absl::nullopt);
-  optionalTest_Comparisons_EXPECT_GREATER(a2, be);
-  optionalTest_Comparisons_EXPECT_SAME(a2, b2);
-  optionalTest_Comparisons_EXPECT_LESS(a2, v3);
-  optionalTest_Comparisons_EXPECT_LESS(a2, b4);
-
-  // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(v3,absl::nullopt);
-  optionalTest_Comparisons_EXPECT_GREATER(v3, be);
-  optionalTest_Comparisons_EXPECT_GREATER(v3, b2);
-  optionalTest_Comparisons_EXPECT_SAME(v3, v3);
-  optionalTest_Comparisons_EXPECT_LESS(v3, b4);
-
-  optionalTest_Comparisons_EXPECT_GREATER(a4, absl::nullopt);
-  optionalTest_Comparisons_EXPECT_GREATER(a4, be);
-  optionalTest_Comparisons_EXPECT_GREATER(a4, b2);
-  optionalTest_Comparisons_EXPECT_GREATER(a4, v3);
-  optionalTest_Comparisons_EXPECT_SAME(a4, b4);
-}
-
-struct Int1 {
-  Int1() = default;
-  Int1(int i) : i(i) {}  // NOLINT(runtime/explicit)
-  int i;
-};
-
-struct Int2 {
-  Int2() = default;
-  Int2(int i) : i(i) {}  // NOLINT(runtime/explicit)
-  int i;
-};
-
-// comparison between Int1 and Int2
-constexpr bool operator==(const Int1& lhs, const Int2& rhs) {
-  return lhs.i == rhs.i;
-}
-constexpr bool operator!=(const Int1& lhs, const Int2& rhs) {
-  return !(lhs == rhs);
-}
-constexpr bool operator<(const Int1& lhs, const Int2& rhs) {
-  return lhs.i < rhs.i;
-}
-constexpr bool operator<=(const Int1& lhs, const Int2& rhs) {
-  return lhs < rhs || lhs == rhs;
-}
-constexpr bool operator>(const Int1& lhs, const Int2& rhs) {
-  return !(lhs <= rhs);
-}
-constexpr bool operator>=(const Int1& lhs, const Int2& rhs) {
-  return !(lhs < rhs);
-}
-
-TEST(optionalTest, Comparisons) {
-  TestComparisons<int, int, int>();
-  TestComparisons<const int, int, int>();
-  TestComparisons<Int1, int, int>();
-  TestComparisons<int, Int2, int>();
-  TestComparisons<Int1, Int2, int>();
-
-  // compare absl::optional<std::string> with const char*
-  absl::optional<std::string> opt_str = "abc";
-  const char* cstr = "abc";
-  EXPECT_TRUE(opt_str == cstr);
-  // compare absl::optional<std::string> with absl::optional<const char*>
-  absl::optional<const char*> opt_cstr = cstr;
-  EXPECT_TRUE(opt_str == opt_cstr);
-  // compare absl::optional<std::string> with absl::optional<absl::string_view>
-  absl::optional<absl::string_view> e1;
-  absl::optional<std::string> e2;
-  EXPECT_TRUE(e1 == e2);
-}
-
-
-TEST(optionalTest, SwapRegression) {
-  StructorListener listener;
-  Listenable::listener = &listener;
-
-  {
-    absl::optional<Listenable> a;
-    absl::optional<Listenable> b(absl::in_place);
-    a.swap(b);
-  }
-
-  EXPECT_EQ(1, listener.construct0);
-  EXPECT_EQ(1, listener.move);
-  EXPECT_EQ(2, listener.destruct);
-
-  {
-    absl::optional<Listenable> a(absl::in_place);
-    absl::optional<Listenable> b;
-    a.swap(b);
-  }
-
-  EXPECT_EQ(2, listener.construct0);
-  EXPECT_EQ(2, listener.move);
-  EXPECT_EQ(4, listener.destruct);
-}
-
-TEST(optionalTest, BigStringLeakCheck) {
-  constexpr size_t n = 1 << 16;
-
-  using OS = absl::optional<std::string>;
-
-  OS a;
-  OS b = absl::nullopt;
-  OS c = std::string(n, 'c');
-  std::string sd(n, 'd');
-  OS d = sd;
-  OS e(absl::in_place, n, 'e');
-  OS f;
-  f.emplace(n, 'f');
-
-  OS ca(a);
-  OS cb(b);
-  OS cc(c);
-  OS cd(d);
-  OS ce(e);
-
-  OS oa;
-  OS ob = absl::nullopt;
-  OS oc = std::string(n, 'c');
-  std::string sod(n, 'd');
-  OS od = sod;
-  OS oe(absl::in_place, n, 'e');
-  OS of;
-  of.emplace(n, 'f');
-
-  OS ma(std::move(oa));
-  OS mb(std::move(ob));
-  OS mc(std::move(oc));
-  OS md(std::move(od));
-  OS me(std::move(oe));
-  OS mf(std::move(of));
-
-  OS aa1;
-  OS ab1 = absl::nullopt;
-  OS ac1 = std::string(n, 'c');
-  std::string sad1(n, 'd');
-  OS ad1 = sad1;
-  OS ae1(absl::in_place, n, 'e');
-  OS af1;
-  af1.emplace(n, 'f');
-
-  OS aa2;
-  OS ab2 = absl::nullopt;
-  OS ac2 = std::string(n, 'c');
-  std::string sad2(n, 'd');
-  OS ad2 = sad2;
-  OS ae2(absl::in_place, n, 'e');
-  OS af2;
-  af2.emplace(n, 'f');
-
-  aa1 = af2;
-  ab1 = ae2;
-  ac1 = ad2;
-  ad1 = ac2;
-  ae1 = ab2;
-  af1 = aa2;
-
-  OS aa3;
-  OS ab3 = absl::nullopt;
-  OS ac3 = std::string(n, 'c');
-  std::string sad3(n, 'd');
-  OS ad3 = sad3;
-  OS ae3(absl::in_place, n, 'e');
-  OS af3;
-  af3.emplace(n, 'f');
-
-  aa3 = absl::nullopt;
-  ab3 = absl::nullopt;
-  ac3 = absl::nullopt;
-  ad3 = absl::nullopt;
-  ae3 = absl::nullopt;
-  af3 = absl::nullopt;
-
-  OS aa4;
-  OS ab4 = absl::nullopt;
-  OS ac4 = std::string(n, 'c');
-  std::string sad4(n, 'd');
-  OS ad4 = sad4;
-  OS ae4(absl::in_place, n, 'e');
-  OS af4;
-  af4.emplace(n, 'f');
-
-  aa4 = OS(absl::in_place, n, 'a');
-  ab4 = OS(absl::in_place, n, 'b');
-  ac4 = OS(absl::in_place, n, 'c');
-  ad4 = OS(absl::in_place, n, 'd');
-  ae4 = OS(absl::in_place, n, 'e');
-  af4 = OS(absl::in_place, n, 'f');
-
-  OS aa5;
-  OS ab5 = absl::nullopt;
-  OS ac5 = std::string(n, 'c');
-  std::string sad5(n, 'd');
-  OS ad5 = sad5;
-  OS ae5(absl::in_place, n, 'e');
-  OS af5;
-  af5.emplace(n, 'f');
-
-  std::string saa5(n, 'a');
-  std::string sab5(n, 'a');
-  std::string sac5(n, 'a');
-  std::string sad52(n, 'a');
-  std::string sae5(n, 'a');
-  std::string saf5(n, 'a');
-
-  aa5 = saa5;
-  ab5 = sab5;
-  ac5 = sac5;
-  ad5 = sad52;
-  ae5 = sae5;
-  af5 = saf5;
-
-  OS aa6;
-  OS ab6 = absl::nullopt;
-  OS ac6 = std::string(n, 'c');
-  std::string sad6(n, 'd');
-  OS ad6 = sad6;
-  OS ae6(absl::in_place, n, 'e');
-  OS af6;
-  af6.emplace(n, 'f');
-
-  aa6 = std::string(n, 'a');
-  ab6 = std::string(n, 'b');
-  ac6 = std::string(n, 'c');
-  ad6 = std::string(n, 'd');
-  ae6 = std::string(n, 'e');
-  af6 = std::string(n, 'f');
-
-  OS aa7;
-  OS ab7 = absl::nullopt;
-  OS ac7 = std::string(n, 'c');
-  std::string sad7(n, 'd');
-  OS ad7 = sad7;
-  OS ae7(absl::in_place, n, 'e');
-  OS af7;
-  af7.emplace(n, 'f');
-
-  aa7.emplace(n, 'A');
-  ab7.emplace(n, 'B');
-  ac7.emplace(n, 'C');
-  ad7.emplace(n, 'D');
-  ae7.emplace(n, 'E');
-  af7.emplace(n, 'F');
-}
-
-TEST(optionalTest, MoveAssignRegression) {
-  StructorListener listener;
-  Listenable::listener = &listener;
-
-  {
-    absl::optional<Listenable> a;
-    Listenable b;
-    a = std::move(b);
-  }
-
-  EXPECT_EQ(1, listener.construct0);
-  EXPECT_EQ(1, listener.move);
-  EXPECT_EQ(2, listener.destruct);
-}
-
-TEST(optionalTest, ValueType) {
-  EXPECT_TRUE((std::is_same<absl::optional<int>::value_type, int>::value));
-  EXPECT_TRUE((std::is_same<absl::optional<std::string>::value_type,
-                            std::string>::value));
-  EXPECT_FALSE(
-      (std::is_same<absl::optional<int>::value_type, absl::nullopt_t>::value));
-}
-
-template <typename T>
-struct is_hash_enabled_for {
-  template <typename U, typename = decltype(std::hash<U>()(std::declval<U>()))>
-  static std::true_type test(int);
-
-  template <typename U>
-  static std::false_type test(...);
-
-  static constexpr bool value = decltype(test<T>(0))::value;
-};
-
-TEST(optionalTest, Hash) {
-  std::hash<absl::optional<int>> hash;
-  std::set<size_t> hashcodes;
-  hashcodes.insert(hash(absl::nullopt));
-  for (int i = 0; i < 100; ++i) {
-    hashcodes.insert(hash(i));
-  }
-  EXPECT_GT(hashcodes.size(), 90);
-
-  static_assert(is_hash_enabled_for<absl::optional<int>>::value, "");
-  static_assert(is_hash_enabled_for<absl::optional<Hashable>>::value, "");
-  static_assert(
-      absl::type_traits_internal::IsHashable<absl::optional<int>>::value, "");
-  static_assert(
-      absl::type_traits_internal::IsHashable<absl::optional<Hashable>>::value,
-      "");
-  absl::type_traits_internal::AssertHashEnabled<absl::optional<int>>();
-  absl::type_traits_internal::AssertHashEnabled<absl::optional<Hashable>>();
-
-#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-  static_assert(!is_hash_enabled_for<absl::optional<NonHashable>>::value, "");
-  static_assert(!absl::type_traits_internal::IsHashable<
-                    absl::optional<NonHashable>>::value,
-                "");
-#endif
-
-  // libstdc++ std::optional is missing remove_const_t, i.e. it's using
-  // std::hash<T> rather than std::hash<std::remove_const_t<T>>.
-  // Reference: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82262
-#ifndef __GLIBCXX__
-  static_assert(is_hash_enabled_for<absl::optional<const int>>::value, "");
-  static_assert(is_hash_enabled_for<absl::optional<const Hashable>>::value, "");
-  std::hash<absl::optional<const int>> c_hash;
-  for (int i = 0; i < 100; ++i) {
-    EXPECT_EQ(hash(i), c_hash(i));
-  }
-#endif
-}
-
-struct MoveMeNoThrow {
-  MoveMeNoThrow() : x(0) {}
-  [[noreturn]] MoveMeNoThrow(const MoveMeNoThrow& other) : x(other.x) {
-    ABSL_RAW_LOG(FATAL, "Should not be called.");
-    abort();
-  }
-  MoveMeNoThrow(MoveMeNoThrow&& other) noexcept : x(other.x) {}
-  int x;
-};
-
-struct MoveMeThrow {
-  MoveMeThrow() : x(0) {}
-  MoveMeThrow(const MoveMeThrow& other) : x(other.x) {}
-  MoveMeThrow(MoveMeThrow&& other) : x(other.x) {}
-  int x;
-};
-
-TEST(optionalTest, NoExcept) {
-  static_assert(
-      std::is_nothrow_move_constructible<absl::optional<MoveMeNoThrow>>::value,
-      "");
-#ifndef ABSL_USES_STD_OPTIONAL
-  static_assert(absl::default_allocator_is_nothrow::value ==
-                    std::is_nothrow_move_constructible<
-                        absl::optional<MoveMeThrow>>::value,
-                "");
-#endif
-  std::vector<absl::optional<MoveMeNoThrow>> v;
-  for (int i = 0; i < 10; ++i) v.emplace_back();
-}
-
-struct AnyLike {
-  AnyLike(AnyLike&&) = default;
-  AnyLike(const AnyLike&) = default;
-
-  template <typename ValueType,
-            typename T = typename std::decay<ValueType>::type,
-            typename std::enable_if<
-                !absl::disjunction<
-                    std::is_same<AnyLike, T>,
-                    absl::negation<std::is_copy_constructible<T>>>::value,
-                int>::type = 0>
-  AnyLike(ValueType&&) {}  // NOLINT(runtime/explicit)
-
-  AnyLike& operator=(AnyLike&&) = default;
-  AnyLike& operator=(const AnyLike&) = default;
-
-  template <typename ValueType,
-            typename T = typename std::decay<ValueType>::type>
-  typename std::enable_if<
-      absl::conjunction<absl::negation<std::is_same<AnyLike, T>>,
-                        std::is_copy_constructible<T>>::value,
-      AnyLike&>::type
-  operator=(ValueType&& /* rhs */) {
-    return *this;
-  }
-};
-
-TEST(optionalTest, ConstructionConstraints) {
-  EXPECT_TRUE((std::is_constructible<AnyLike, absl::optional<AnyLike>>::value));
-
-  EXPECT_TRUE(
-      (std::is_constructible<AnyLike, const absl::optional<AnyLike>&>::value));
-
-  EXPECT_TRUE((std::is_constructible<absl::optional<AnyLike>, AnyLike>::value));
-  EXPECT_TRUE(
-      (std::is_constructible<absl::optional<AnyLike>, const AnyLike&>::value));
-
-  EXPECT_TRUE((std::is_convertible<absl::optional<AnyLike>, AnyLike>::value));
-
-  EXPECT_TRUE(
-      (std::is_convertible<const absl::optional<AnyLike>&, AnyLike>::value));
-
-  EXPECT_TRUE((std::is_convertible<AnyLike, absl::optional<AnyLike>>::value));
-  EXPECT_TRUE(
-      (std::is_convertible<const AnyLike&, absl::optional<AnyLike>>::value));
-
-  EXPECT_TRUE(std::is_move_constructible<absl::optional<AnyLike>>::value);
-  EXPECT_TRUE(std::is_copy_constructible<absl::optional<AnyLike>>::value);
-}
-
-TEST(optionalTest, AssignmentConstraints) {
-  EXPECT_TRUE((std::is_assignable<AnyLike&, absl::optional<AnyLike>>::value));
-  EXPECT_TRUE(
-      (std::is_assignable<AnyLike&, const absl::optional<AnyLike>&>::value));
-  EXPECT_TRUE((std::is_assignable<absl::optional<AnyLike>&, AnyLike>::value));
-  EXPECT_TRUE(
-      (std::is_assignable<absl::optional<AnyLike>&, const AnyLike&>::value));
-  EXPECT_TRUE(std::is_move_assignable<absl::optional<AnyLike>>::value);
-  EXPECT_TRUE(absl::is_copy_assignable<absl::optional<AnyLike>>::value);
-}
-
-#if !defined(__EMSCRIPTEN__)
-struct NestedClassBug {
-  struct Inner {
-    bool dummy = false;
-  };
-  absl::optional<Inner> value;
-};
-
-TEST(optionalTest, InPlaceTSFINAEBug) {
-  NestedClassBug b;
-  ((void)b);
-  using Inner = NestedClassBug::Inner;
-
-  EXPECT_TRUE((std::is_default_constructible<Inner>::value));
-  EXPECT_TRUE((std::is_constructible<Inner>::value));
-  EXPECT_TRUE(
-      (std::is_constructible<absl::optional<Inner>, absl::in_place_t>::value));
-
-  absl::optional<Inner> o(absl::in_place);
-  EXPECT_TRUE(o.has_value());
-  o.emplace();
-  EXPECT_TRUE(o.has_value());
-}
-#endif  // !defined(__EMSCRIPTEN__)
-
-}  // namespace
-
-#endif  // #if !defined(ABSL_USES_STD_OPTIONAL)
diff --git a/third_party/abseil_cpp/absl/types/span.h b/third_party/abseil_cpp/absl/types/span.h
deleted file mode 100644
index 95fe79262d..0000000000
--- a/third_party/abseil_cpp/absl/types/span.h
+++ /dev/null
@@ -1,726 +0,0 @@
-//
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// span.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines a `Span<T>` type for holding a reference to existing
-// array data. The `Span` object, much like the `absl::string_view` object,
-// does not own such data itself, and the data being referenced by the span must
-// outlive the span itself. Unlike `view` type references, a span can hold a
-// reference to mutable data (and can mutate it for underlying types of
-// non-const T.) A span provides a lightweight way to pass a reference to such
-// data.
-//
-// Additionally, this header file defines `MakeSpan()` and `MakeConstSpan()`
-// factory functions, for clearly creating spans of type `Span<T>` or read-only
-// `Span<const T>` when such types may be difficult to identify due to issues
-// with implicit conversion.
-//
-// The C++20 draft standard includes a `std::span` type. As of June 2020, the
-// differences between `absl::Span` and `std::span` are:
-//    * `absl::Span` has `operator==` (which is likely a design bug,
-//       per https://abseil.io/blog/20180531-regular-types)
-//    * `absl::Span` has the factory functions `MakeSpan()` and
-//      `MakeConstSpan()`
-//    * bounds-checked access to `absl::Span` is accomplished with `at()`
-//    * `absl::Span` has compiler-provided move and copy constructors and
-//      assignment. This is due to them being specified as `constexpr`, but that
-//      implies const in C++11.
-//    * `absl::Span` has no `element_type` typedef
-//    * A read-only `absl::Span<const T>` can be implicitly constructed from an
-//      initializer list.
-//    * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or
-//      `as_mutable_bytes()` methods
-//    * `absl::Span` has no static extent template parameter, nor constructors
-//      which exist only because of the static extent parameter.
-//    * `absl::Span` has an explicit mutable-reference constructor
-//
-// For more information, see the class comments below.
-#ifndef ABSL_TYPES_SPAN_H_
-#define ABSL_TYPES_SPAN_H_
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <initializer_list>
-#include <iterator>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/internal/throw_delegate.h"
-#include "absl/base/macros.h"
-#include "absl/base/optimization.h"
-#include "absl/base/port.h"    // TODO(strel): remove this include
-#include "absl/meta/type_traits.h"
-#include "absl/types/internal/span.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-//------------------------------------------------------------------------------
-// Span
-//------------------------------------------------------------------------------
-//
-// A `Span` is an "array reference" type for holding a reference of contiguous
-// array data; the `Span` object does not and cannot own such data itself. A
-// span provides an easy way to provide overloads for anything operating on
-// contiguous sequences without needing to manage pointers and array lengths
-// manually.
-
-// A span is conceptually a pointer (ptr) and a length (size) into an already
-// existing array of contiguous memory; the array it represents references the
-// elements "ptr[0] .. ptr[size-1]". Passing a properly-constructed `Span`
-// instead of raw pointers avoids many issues related to index out of bounds
-// errors.
-//
-// Spans may also be constructed from containers holding contiguous sequences.
-// Such containers must supply `data()` and `size() const` methods (e.g
-// `std::vector<T>`, `absl::InlinedVector<T, N>`). All implicit conversions to
-// `absl::Span` from such containers will create spans of type `const T`;
-// spans which can mutate their values (of type `T`) must use explicit
-// constructors.
-//
-// A `Span<T>` is somewhat analogous to an `absl::string_view`, but for an array
-// of elements of type `T`, and unlike an `absl::string_view`, a span can hold a
-// reference to mutable data. A user of `Span` must ensure that the data being
-// pointed to outlives the `Span` itself.
-//
-// You can construct a `Span<T>` in several ways:
-//
-//   * Explicitly from a reference to a container type
-//   * Explicitly from a pointer and size
-//   * Implicitly from a container type (but only for spans of type `const T`)
-//   * Using the `MakeSpan()` or `MakeConstSpan()` factory functions.
-//
-// Examples:
-//
-//   // Construct a Span explicitly from a container:
-//   std::vector<int> v = {1, 2, 3, 4, 5};
-//   auto span = absl::Span<const int>(v);
-//
-//   // Construct a Span explicitly from a C-style array:
-//   int a[5] =  {1, 2, 3, 4, 5};
-//   auto span = absl::Span<const int>(a);
-//
-//   // Construct a Span implicitly from a container
-//   void MyRoutine(absl::Span<const int> a) {
-//     ...
-//   }
-//   std::vector v = {1,2,3,4,5};
-//   MyRoutine(v)                     // convert to Span<const T>
-//
-// Note that `Span` objects, in addition to requiring that the memory they
-// point to remains alive, must also ensure that such memory does not get
-// reallocated. Therefore, to avoid undefined behavior, containers with
-// associated spans should not invoke operations that may reallocate memory
-// (such as resizing) or invalidate iterators into the container.
-//
-// One common use for a `Span` is when passing arguments to a routine that can
-// accept a variety of array types (e.g. a `std::vector`, `absl::InlinedVector`,
-// a C-style array, etc.). Instead of creating overloads for each case, you
-// can simply specify a `Span` as the argument to such a routine.
-//
-// Example:
-//
-//   void MyRoutine(absl::Span<const int> a) {
-//     ...
-//   }
-//
-//   std::vector v = {1,2,3,4,5};
-//   MyRoutine(v);
-//
-//   absl::InlinedVector<int, 4> my_inline_vector;
-//   MyRoutine(my_inline_vector);
-//
-//   // Explicit constructor from pointer,size
-//   int* my_array = new int[10];
-//   MyRoutine(absl::Span<const int>(my_array, 10));
-template <typename T>
-class Span {
- private:
-  // Used to determine whether a Span can be constructed from a container of
-  // type C.
-  template <typename C>
-  using EnableIfConvertibleFrom =
-      typename std::enable_if<span_internal::HasData<T, C>::value &&
-                              span_internal::HasSize<C>::value>::type;
-
-  // Used to SFINAE-enable a function when the slice elements are const.
-  template <typename U>
-  using EnableIfConstView =
-      typename std::enable_if<std::is_const<T>::value, U>::type;
-
-  // Used to SFINAE-enable a function when the slice elements are mutable.
-  template <typename U>
-  using EnableIfMutableView =
-      typename std::enable_if<!std::is_const<T>::value, U>::type;
-
- public:
-  using value_type = absl::remove_cv_t<T>;
-  using pointer = T*;
-  using const_pointer = const T*;
-  using reference = T&;
-  using const_reference = const T&;
-  using iterator = pointer;
-  using const_iterator = const_pointer;
-  using reverse_iterator = std::reverse_iterator<iterator>;
-  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-  using size_type = size_t;
-  using difference_type = ptrdiff_t;
-
-  static const size_type npos = ~(size_type(0));
-
-  constexpr Span() noexcept : Span(nullptr, 0) {}
-  constexpr Span(pointer array, size_type length) noexcept
-      : ptr_(array), len_(length) {}
-
-  // Implicit conversion constructors
-  template <size_t N>
-  constexpr Span(T (&a)[N]) noexcept  // NOLINT(runtime/explicit)
-      : Span(a, N) {}
-
-  // Explicit reference constructor for a mutable `Span<T>` type. Can be
-  // replaced with MakeSpan() to infer the type parameter.
-  template <typename V, typename = EnableIfConvertibleFrom<V>,
-            typename = EnableIfMutableView<V>>
-  explicit Span(V& v) noexcept  // NOLINT(runtime/references)
-      : Span(span_internal::GetData(v), v.size()) {}
-
-  // Implicit reference constructor for a read-only `Span<const T>` type
-  template <typename V, typename = EnableIfConvertibleFrom<V>,
-            typename = EnableIfConstView<V>>
-  constexpr Span(const V& v) noexcept  // NOLINT(runtime/explicit)
-      : Span(span_internal::GetData(v), v.size()) {}
-
-  // Implicit constructor from an initializer list, making it possible to pass a
-  // brace-enclosed initializer list to a function expecting a `Span`. Such
-  // spans constructed from an initializer list must be of type `Span<const T>`.
-  //
-  //   void Process(absl::Span<const int> x);
-  //   Process({1, 2, 3});
-  //
-  // Note that as always the array referenced by the span must outlive the span.
-  // Since an initializer list constructor acts as if it is fed a temporary
-  // array (cf. C++ standard [dcl.init.list]/5), it's safe to use this
-  // constructor only when the `std::initializer_list` itself outlives the span.
-  // In order to meet this requirement it's sufficient to ensure that neither
-  // the span nor a copy of it is used outside of the expression in which it's
-  // created:
-  //
-  //   // Assume that this function uses the array directly, not retaining any
-  //   // copy of the span or pointer to any of its elements.
-  //   void Process(absl::Span<const int> ints);
-  //
-  //   // Okay: the std::initializer_list<int> will reference a temporary array
-  //   // that isn't destroyed until after the call to Process returns.
-  //   Process({ 17, 19 });
-  //
-  //   // Not okay: the storage used by the std::initializer_list<int> is not
-  //   // allowed to be referenced after the first line.
-  //   absl::Span<const int> ints = { 17, 19 };
-  //   Process(ints);
-  //
-  //   // Not okay for the same reason as above: even when the elements of the
-  //   // initializer list expression are not temporaries the underlying array
-  //   // is, so the initializer list must still outlive the span.
-  //   const int foo = 17;
-  //   absl::Span<const int> ints = { foo };
-  //   Process(ints);
-  //
-  template <typename LazyT = T,
-            typename = EnableIfConstView<LazyT>>
-  Span(
-      std::initializer_list<value_type> v) noexcept  // NOLINT(runtime/explicit)
-      : Span(v.begin(), v.size()) {}
-
-  // Accessors
-
-  // Span::data()
-  //
-  // Returns a pointer to the span's underlying array of data (which is held
-  // outside the span).
-  constexpr pointer data() const noexcept { return ptr_; }
-
-  // Span::size()
-  //
-  // Returns the size of this span.
-  constexpr size_type size() const noexcept { return len_; }
-
-  // Span::length()
-  //
-  // Returns the length (size) of this span.
-  constexpr size_type length() const noexcept { return size(); }
-
-  // Span::empty()
-  //
-  // Returns a boolean indicating whether or not this span is considered empty.
-  constexpr bool empty() const noexcept { return size() == 0; }
-
-  // Span::operator[]
-  //
-  // Returns a reference to the i'th element of this span.
-  constexpr reference operator[](size_type i) const noexcept {
-    // MSVC 2015 accepts this as constexpr, but not ptr_[i]
-    return ABSL_HARDENING_ASSERT(i < size()), *(data() + i);
-  }
-
-  // Span::at()
-  //
-  // Returns a reference to the i'th element of this span.
-  constexpr reference at(size_type i) const {
-    return ABSL_PREDICT_TRUE(i < size())  //
-               ? *(data() + i)
-               : (base_internal::ThrowStdOutOfRange(
-                      "Span::at failed bounds check"),
-                  *(data() + i));
-  }
-
-  // Span::front()
-  //
-  // Returns a reference to the first element of this span. The span must not
-  // be empty.
-  constexpr reference front() const noexcept {
-    return ABSL_HARDENING_ASSERT(size() > 0), *data();
-  }
-
-  // Span::back()
-  //
-  // Returns a reference to the last element of this span. The span must not
-  // be empty.
-  constexpr reference back() const noexcept {
-    return ABSL_HARDENING_ASSERT(size() > 0), *(data() + size() - 1);
-  }
-
-  // Span::begin()
-  //
-  // Returns an iterator pointing to the first element of this span, or `end()`
-  // if the span is empty.
-  constexpr iterator begin() const noexcept { return data(); }
-
-  // Span::cbegin()
-  //
-  // Returns a const iterator pointing to the first element of this span, or
-  // `end()` if the span is empty.
-  constexpr const_iterator cbegin() const noexcept { return begin(); }
-
-  // Span::end()
-  //
-  // Returns an iterator pointing just beyond the last element at the
-  // end of this span. This iterator acts as a placeholder; attempting to
-  // access it results in undefined behavior.
-  constexpr iterator end() const noexcept { return data() + size(); }
-
-  // Span::cend()
-  //
-  // Returns a const iterator pointing just beyond the last element at the
-  // end of this span. This iterator acts as a placeholder; attempting to
-  // access it results in undefined behavior.
-  constexpr const_iterator cend() const noexcept { return end(); }
-
-  // Span::rbegin()
-  //
-  // Returns a reverse iterator pointing to the last element at the end of this
-  // span, or `rend()` if the span is empty.
-  constexpr reverse_iterator rbegin() const noexcept {
-    return reverse_iterator(end());
-  }
-
-  // Span::crbegin()
-  //
-  // Returns a const reverse iterator pointing to the last element at the end of
-  // this span, or `crend()` if the span is empty.
-  constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
-
-  // Span::rend()
-  //
-  // Returns a reverse iterator pointing just before the first element
-  // at the beginning of this span. This pointer acts as a placeholder;
-  // attempting to access its element results in undefined behavior.
-  constexpr reverse_iterator rend() const noexcept {
-    return reverse_iterator(begin());
-  }
-
-  // Span::crend()
-  //
-  // Returns a reverse const iterator pointing just before the first element
-  // at the beginning of this span. This pointer acts as a placeholder;
-  // attempting to access its element results in undefined behavior.
-  constexpr const_reverse_iterator crend() const noexcept { return rend(); }
-
-  // Span mutations
-
-  // Span::remove_prefix()
-  //
-  // Removes the first `n` elements from the span.
-  void remove_prefix(size_type n) noexcept {
-    ABSL_HARDENING_ASSERT(size() >= n);
-    ptr_ += n;
-    len_ -= n;
-  }
-
-  // Span::remove_suffix()
-  //
-  // Removes the last `n` elements from the span.
-  void remove_suffix(size_type n) noexcept {
-    ABSL_HARDENING_ASSERT(size() >= n);
-    len_ -= n;
-  }
-
-  // Span::subspan()
-  //
-  // Returns a `Span` starting at element `pos` and of length `len`. Both `pos`
-  // and `len` are of type `size_type` and thus non-negative. Parameter `pos`
-  // must be <= size(). Any `len` value that points past the end of the span
-  // will be trimmed to at most size() - `pos`. A default `len` value of `npos`
-  // ensures the returned subspan continues until the end of the span.
-  //
-  // Examples:
-  //
-  //   std::vector<int> vec = {10, 11, 12, 13};
-  //   absl::MakeSpan(vec).subspan(1, 2);  // {11, 12}
-  //   absl::MakeSpan(vec).subspan(2, 8);  // {12, 13}
-  //   absl::MakeSpan(vec).subspan(1);     // {11, 12, 13}
-  //   absl::MakeSpan(vec).subspan(4);     // {}
-  //   absl::MakeSpan(vec).subspan(5);     // throws std::out_of_range
-  constexpr Span subspan(size_type pos = 0, size_type len = npos) const {
-    return (pos <= size())
-               ? Span(data() + pos, span_internal::Min(size() - pos, len))
-               : (base_internal::ThrowStdOutOfRange("pos > size()"), Span());
-  }
-
-  // Span::first()
-  //
-  // Returns a `Span` containing first `len` elements. Parameter `len` is of
-  // type `size_type` and thus non-negative. `len` value must be <= size().
-  //
-  // Examples:
-  //
-  //   std::vector<int> vec = {10, 11, 12, 13};
-  //   absl::MakeSpan(vec).first(1);  // {10}
-  //   absl::MakeSpan(vec).first(3);  // {10, 11, 12}
-  //   absl::MakeSpan(vec).first(5);  // throws std::out_of_range
-  constexpr Span first(size_type len) const {
-    return (len <= size())
-               ? Span(data(), len)
-               : (base_internal::ThrowStdOutOfRange("len > size()"), Span());
-  }
-
-  // Span::last()
-  //
-  // Returns a `Span` containing last `len` elements. Parameter `len` is of
-  // type `size_type` and thus non-negative. `len` value must be <= size().
-  //
-  // Examples:
-  //
-  //   std::vector<int> vec = {10, 11, 12, 13};
-  //   absl::MakeSpan(vec).last(1);  // {13}
-  //   absl::MakeSpan(vec).last(3);  // {11, 12, 13}
-  //   absl::MakeSpan(vec).last(5);  // throws std::out_of_range
-  constexpr Span last(size_type len) const {
-    return (len <= size())
-               ? Span(size() - len + data(), len)
-               : (base_internal::ThrowStdOutOfRange("len > size()"), Span());
-  }
-
-  // Support for absl::Hash.
-  template <typename H>
-  friend H AbslHashValue(H h, Span v) {
-    return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()),
-                      v.size());
-  }
-
- private:
-  pointer ptr_;
-  size_type len_;
-};
-
-template <typename T>
-const typename Span<T>::size_type Span<T>::npos;
-
-// Span relationals
-
-// Equality is compared element-by-element, while ordering is lexicographical.
-// We provide three overloads for each operator to cover any combination on the
-// left or right hand side of mutable Span<T>, read-only Span<const T>, and
-// convertible-to-read-only Span<T>.
-// TODO(zhangxy): Due to MSVC overload resolution bug with partial ordering
-// template functions, 5 overloads per operator is needed as a workaround. We
-// should update them to 3 overloads per operator using non-deduced context like
-// string_view, i.e.
-// - (Span<T>, Span<T>)
-// - (Span<T>, non_deduced<Span<const T>>)
-// - (non_deduced<Span<const T>>, Span<T>)
-
-// operator==
-template <typename T>
-bool operator==(Span<T> a, Span<T> b) {
-  return span_internal::EqualImpl<Span, const T>(a, b);
-}
-template <typename T>
-bool operator==(Span<const T> a, Span<T> b) {
-  return span_internal::EqualImpl<Span, const T>(a, b);
-}
-template <typename T>
-bool operator==(Span<T> a, Span<const T> b) {
-  return span_internal::EqualImpl<Span, const T>(a, b);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator==(const U& a, Span<T> b) {
-  return span_internal::EqualImpl<Span, const T>(a, b);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator==(Span<T> a, const U& b) {
-  return span_internal::EqualImpl<Span, const T>(a, b);
-}
-
-// operator!=
-template <typename T>
-bool operator!=(Span<T> a, Span<T> b) {
-  return !(a == b);
-}
-template <typename T>
-bool operator!=(Span<const T> a, Span<T> b) {
-  return !(a == b);
-}
-template <typename T>
-bool operator!=(Span<T> a, Span<const T> b) {
-  return !(a == b);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator!=(const U& a, Span<T> b) {
-  return !(a == b);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator!=(Span<T> a, const U& b) {
-  return !(a == b);
-}
-
-// operator<
-template <typename T>
-bool operator<(Span<T> a, Span<T> b) {
-  return span_internal::LessThanImpl<Span, const T>(a, b);
-}
-template <typename T>
-bool operator<(Span<const T> a, Span<T> b) {
-  return span_internal::LessThanImpl<Span, const T>(a, b);
-}
-template <typename T>
-bool operator<(Span<T> a, Span<const T> b) {
-  return span_internal::LessThanImpl<Span, const T>(a, b);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator<(const U& a, Span<T> b) {
-  return span_internal::LessThanImpl<Span, const T>(a, b);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator<(Span<T> a, const U& b) {
-  return span_internal::LessThanImpl<Span, const T>(a, b);
-}
-
-// operator>
-template <typename T>
-bool operator>(Span<T> a, Span<T> b) {
-  return b < a;
-}
-template <typename T>
-bool operator>(Span<const T> a, Span<T> b) {
-  return b < a;
-}
-template <typename T>
-bool operator>(Span<T> a, Span<const T> b) {
-  return b < a;
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator>(const U& a, Span<T> b) {
-  return b < a;
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator>(Span<T> a, const U& b) {
-  return b < a;
-}
-
-// operator<=
-template <typename T>
-bool operator<=(Span<T> a, Span<T> b) {
-  return !(b < a);
-}
-template <typename T>
-bool operator<=(Span<const T> a, Span<T> b) {
-  return !(b < a);
-}
-template <typename T>
-bool operator<=(Span<T> a, Span<const T> b) {
-  return !(b < a);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator<=(const U& a, Span<T> b) {
-  return !(b < a);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator<=(Span<T> a, const U& b) {
-  return !(b < a);
-}
-
-// operator>=
-template <typename T>
-bool operator>=(Span<T> a, Span<T> b) {
-  return !(a < b);
-}
-template <typename T>
-bool operator>=(Span<const T> a, Span<T> b) {
-  return !(a < b);
-}
-template <typename T>
-bool operator>=(Span<T> a, Span<const T> b) {
-  return !(a < b);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator>=(const U& a, Span<T> b) {
-  return !(a < b);
-}
-template <
-    typename T, typename U,
-    typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>>
-bool operator>=(Span<T> a, const U& b) {
-  return !(a < b);
-}
-
-// MakeSpan()
-//
-// Constructs a mutable `Span<T>`, deducing `T` automatically from either a
-// container or pointer+size.
-//
-// Because a read-only `Span<const T>` is implicitly constructed from container
-// types regardless of whether the container itself is a const container,
-// constructing mutable spans of type `Span<T>` from containers requires
-// explicit constructors. The container-accepting version of `MakeSpan()`
-// deduces the type of `T` by the constness of the pointer received from the
-// container's `data()` member. Similarly, the pointer-accepting version returns
-// a `Span<const T>` if `T` is `const`, and a `Span<T>` otherwise.
-//
-// Examples:
-//
-//   void MyRoutine(absl::Span<MyComplicatedType> a) {
-//     ...
-//   };
-//   // my_vector is a container of non-const types
-//   std::vector<MyComplicatedType> my_vector;
-//
-//   // Constructing a Span implicitly attempts to create a Span of type
-//   // `Span<const T>`
-//   MyRoutine(my_vector);                // error, type mismatch
-//
-//   // Explicitly constructing the Span is verbose
-//   MyRoutine(absl::Span<MyComplicatedType>(my_vector));
-//
-//   // Use MakeSpan() to make an absl::Span<T>
-//   MyRoutine(absl::MakeSpan(my_vector));
-//
-//   // Construct a span from an array ptr+size
-//   absl::Span<T> my_span() {
-//     return absl::MakeSpan(&array[0], num_elements_);
-//   }
-//
-template <int&... ExplicitArgumentBarrier, typename T>
-constexpr Span<T> MakeSpan(T* ptr, size_t size) noexcept {
-  return Span<T>(ptr, size);
-}
-
-template <int&... ExplicitArgumentBarrier, typename T>
-Span<T> MakeSpan(T* begin, T* end) noexcept {
-  return ABSL_HARDENING_ASSERT(begin <= end), Span<T>(begin, end - begin);
-}
-
-template <int&... ExplicitArgumentBarrier, typename C>
-constexpr auto MakeSpan(C& c) noexcept  // NOLINT(runtime/references)
-    -> decltype(absl::MakeSpan(span_internal::GetData(c), c.size())) {
-  return MakeSpan(span_internal::GetData(c), c.size());
-}
-
-template <int&... ExplicitArgumentBarrier, typename T, size_t N>
-constexpr Span<T> MakeSpan(T (&array)[N]) noexcept {
-  return Span<T>(array, N);
-}
-
-// MakeConstSpan()
-//
-// Constructs a `Span<const T>` as with `MakeSpan`, deducing `T` automatically,
-// but always returning a `Span<const T>`.
-//
-// Examples:
-//
-//   void ProcessInts(absl::Span<const int> some_ints);
-//
-//   // Call with a pointer and size.
-//   int array[3] = { 0, 0, 0 };
-//   ProcessInts(absl::MakeConstSpan(&array[0], 3));
-//
-//   // Call with a [begin, end) pair.
-//   ProcessInts(absl::MakeConstSpan(&array[0], &array[3]));
-//
-//   // Call directly with an array.
-//   ProcessInts(absl::MakeConstSpan(array));
-//
-//   // Call with a contiguous container.
-//   std::vector<int> some_ints = ...;
-//   ProcessInts(absl::MakeConstSpan(some_ints));
-//   ProcessInts(absl::MakeConstSpan(std::vector<int>{ 0, 0, 0 }));
-//
-template <int&... ExplicitArgumentBarrier, typename T>
-constexpr Span<const T> MakeConstSpan(T* ptr, size_t size) noexcept {
-  return Span<const T>(ptr, size);
-}
-
-template <int&... ExplicitArgumentBarrier, typename T>
-Span<const T> MakeConstSpan(T* begin, T* end) noexcept {
-  return ABSL_HARDENING_ASSERT(begin <= end), Span<const T>(begin, end - begin);
-}
-
-template <int&... ExplicitArgumentBarrier, typename C>
-constexpr auto MakeConstSpan(const C& c) noexcept -> decltype(MakeSpan(c)) {
-  return MakeSpan(c);
-}
-
-template <int&... ExplicitArgumentBarrier, typename T, size_t N>
-constexpr Span<const T> MakeConstSpan(const T (&array)[N]) noexcept {
-  return Span<const T>(array, N);
-}
-ABSL_NAMESPACE_END
-}  // namespace absl
-#endif  // ABSL_TYPES_SPAN_H_
diff --git a/third_party/abseil_cpp/absl/types/span_test.cc b/third_party/abseil_cpp/absl/types/span_test.cc
deleted file mode 100644
index 2584339bd3..0000000000
--- a/third_party/abseil_cpp/absl/types/span_test.cc
+++ /dev/null
@@ -1,846 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/span.h"
-
-#include <array>
-#include <initializer_list>
-#include <numeric>
-#include <stdexcept>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/internal/exception_testing.h"
-#include "absl/base/options.h"
-#include "absl/container/fixed_array.h"
-#include "absl/container/inlined_vector.h"
-#include "absl/hash/hash_testing.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-MATCHER_P(DataIs, data,
-          absl::StrCat("data() ", negation ? "isn't " : "is ",
-                       testing::PrintToString(data))) {
-  return arg.data() == data;
-}
-
-template <typename T>
-auto SpanIs(T data, size_t size)
-    -> decltype(testing::AllOf(DataIs(data), testing::SizeIs(size))) {
-  return testing::AllOf(DataIs(data), testing::SizeIs(size));
-}
-
-template <typename Container>
-auto SpanIs(const Container& c) -> decltype(SpanIs(c.data(), c.size())) {
-  return SpanIs(c.data(), c.size());
-}
-
-std::vector<int> MakeRamp(int len, int offset = 0) {
-  std::vector<int> v(len);
-  std::iota(v.begin(), v.end(), offset);
-  return v;
-}
-
-TEST(IntSpan, EmptyCtors) {
-  absl::Span<int> s;
-  EXPECT_THAT(s, SpanIs(nullptr, 0));
-}
-
-TEST(IntSpan, PtrLenCtor) {
-  int a[] = {1, 2, 3};
-  absl::Span<int> s(&a[0], 2);
-  EXPECT_THAT(s, SpanIs(a, 2));
-}
-
-TEST(IntSpan, ArrayCtor) {
-  int a[] = {1, 2, 3};
-  absl::Span<int> s(a);
-  EXPECT_THAT(s, SpanIs(a, 3));
-
-  EXPECT_TRUE((std::is_constructible<absl::Span<const int>, int[3]>::value));
-  EXPECT_TRUE(
-      (std::is_constructible<absl::Span<const int>, const int[3]>::value));
-  EXPECT_FALSE((std::is_constructible<absl::Span<int>, const int[3]>::value));
-  EXPECT_TRUE((std::is_convertible<int[3], absl::Span<const int>>::value));
-  EXPECT_TRUE(
-      (std::is_convertible<const int[3], absl::Span<const int>>::value));
-}
-
-template <typename T>
-void TakesGenericSpan(absl::Span<T>) {}
-
-TEST(IntSpan, ContainerCtor) {
-  std::vector<int> empty;
-  absl::Span<int> s_empty(empty);
-  EXPECT_THAT(s_empty, SpanIs(empty));
-
-  std::vector<int> filled{1, 2, 3};
-  absl::Span<int> s_filled(filled);
-  EXPECT_THAT(s_filled, SpanIs(filled));
-
-  absl::Span<int> s_from_span(filled);
-  EXPECT_THAT(s_from_span, SpanIs(s_filled));
-
-  absl::Span<const int> const_filled = filled;
-  EXPECT_THAT(const_filled, SpanIs(filled));
-
-  absl::Span<const int> const_from_span = s_filled;
-  EXPECT_THAT(const_from_span, SpanIs(s_filled));
-
-  EXPECT_TRUE(
-      (std::is_convertible<std::vector<int>&, absl::Span<const int>>::value));
-  EXPECT_TRUE(
-      (std::is_convertible<absl::Span<int>&, absl::Span<const int>>::value));
-
-  TakesGenericSpan(absl::Span<int>(filled));
-}
-
-// A struct supplying shallow data() const.
-struct ContainerWithShallowConstData {
-  std::vector<int> storage;
-  int* data() const { return const_cast<int*>(storage.data()); }
-  int size() const { return storage.size(); }
-};
-
-TEST(IntSpan, ShallowConstness) {
-  const ContainerWithShallowConstData c{MakeRamp(20)};
-  absl::Span<int> s(
-      c);  // We should be able to do this even though data() is const.
-  s[0] = -1;
-  EXPECT_EQ(c.storage[0], -1);
-}
-
-TEST(CharSpan, StringCtor) {
-  std::string empty = "";
-  absl::Span<char> s_empty(empty);
-  EXPECT_THAT(s_empty, SpanIs(empty));
-
-  std::string abc = "abc";
-  absl::Span<char> s_abc(abc);
-  EXPECT_THAT(s_abc, SpanIs(abc));
-
-  absl::Span<const char> s_const_abc = abc;
-  EXPECT_THAT(s_const_abc, SpanIs(abc));
-
-  EXPECT_FALSE((std::is_constructible<absl::Span<int>, std::string>::value));
-  EXPECT_FALSE(
-      (std::is_constructible<absl::Span<const int>, std::string>::value));
-  EXPECT_TRUE(
-      (std::is_convertible<std::string, absl::Span<const char>>::value));
-}
-
-TEST(IntSpan, FromConstPointer) {
-  EXPECT_TRUE((std::is_constructible<absl::Span<const int* const>,
-                                     std::vector<int*>>::value));
-  EXPECT_TRUE((std::is_constructible<absl::Span<const int* const>,
-                                     std::vector<const int*>>::value));
-  EXPECT_FALSE((
-      std::is_constructible<absl::Span<const int*>, std::vector<int*>>::value));
-  EXPECT_FALSE((
-      std::is_constructible<absl::Span<int*>, std::vector<const int*>>::value));
-}
-
-struct TypeWithMisleadingData {
-  int& data() { return i; }
-  int size() { return 1; }
-  int i;
-};
-
-struct TypeWithMisleadingSize {
-  int* data() { return &i; }
-  const char* size() { return "1"; }
-  int i;
-};
-
-TEST(IntSpan, EvilTypes) {
-  EXPECT_FALSE(
-      (std::is_constructible<absl::Span<int>, TypeWithMisleadingData&>::value));
-  EXPECT_FALSE(
-      (std::is_constructible<absl::Span<int>, TypeWithMisleadingSize&>::value));
-}
-
-struct Base {
-  int* data() { return &i; }
-  int size() { return 1; }
-  int i;
-};
-struct Derived : Base {};
-
-TEST(IntSpan, SpanOfDerived) {
-  EXPECT_TRUE((std::is_constructible<absl::Span<int>, Base&>::value));
-  EXPECT_TRUE((std::is_constructible<absl::Span<int>, Derived&>::value));
-  EXPECT_FALSE(
-      (std::is_constructible<absl::Span<Base>, std::vector<Derived>>::value));
-}
-
-void TestInitializerList(absl::Span<const int> s, const std::vector<int>& v) {
-  EXPECT_TRUE(absl::equal(s.begin(), s.end(), v.begin(), v.end()));
-}
-
-TEST(ConstIntSpan, InitializerListConversion) {
-  TestInitializerList({}, {});
-  TestInitializerList({1}, {1});
-  TestInitializerList({1, 2, 3}, {1, 2, 3});
-
-  EXPECT_FALSE((std::is_constructible<absl::Span<int>,
-                                      std::initializer_list<int>>::value));
-  EXPECT_FALSE((
-      std::is_convertible<absl::Span<int>, std::initializer_list<int>>::value));
-}
-
-TEST(IntSpan, Data) {
-  int i;
-  absl::Span<int> s(&i, 1);
-  EXPECT_EQ(&i, s.data());
-}
-
-TEST(IntSpan, SizeLengthEmpty) {
-  absl::Span<int> empty;
-  EXPECT_EQ(empty.size(), 0);
-  EXPECT_TRUE(empty.empty());
-  EXPECT_EQ(empty.size(), empty.length());
-
-  auto v = MakeRamp(10);
-  absl::Span<int> s(v);
-  EXPECT_EQ(s.size(), 10);
-  EXPECT_FALSE(s.empty());
-  EXPECT_EQ(s.size(), s.length());
-}
-
-TEST(IntSpan, ElementAccess) {
-  auto v = MakeRamp(10);
-  absl::Span<int> s(v);
-  for (int i = 0; i < s.size(); ++i) {
-    EXPECT_EQ(s[i], s.at(i));
-  }
-
-  EXPECT_EQ(s.front(), s[0]);
-  EXPECT_EQ(s.back(), s[9]);
-
-#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
-  EXPECT_DEATH_IF_SUPPORTED(s[-1], "");
-  EXPECT_DEATH_IF_SUPPORTED(s[10], "");
-#endif
-}
-
-TEST(IntSpan, AtThrows) {
-  auto v = MakeRamp(10);
-  absl::Span<int> s(v);
-
-  EXPECT_EQ(s.at(9), 9);
-  ABSL_BASE_INTERNAL_EXPECT_FAIL(s.at(10), std::out_of_range,
-                                 "failed bounds check");
-}
-
-TEST(IntSpan, RemovePrefixAndSuffix) {
-  auto v = MakeRamp(20, 1);
-  absl::Span<int> s(v);
-  EXPECT_EQ(s.size(), 20);
-
-  s.remove_suffix(0);
-  s.remove_prefix(0);
-  EXPECT_EQ(s.size(), 20);
-
-  s.remove_prefix(1);
-  EXPECT_EQ(s.size(), 19);
-  EXPECT_EQ(s[0], 2);
-
-  s.remove_suffix(1);
-  EXPECT_EQ(s.size(), 18);
-  EXPECT_EQ(s.back(), 19);
-
-  s.remove_prefix(7);
-  EXPECT_EQ(s.size(), 11);
-  EXPECT_EQ(s[0], 9);
-
-  s.remove_suffix(11);
-  EXPECT_EQ(s.size(), 0);
-
-  EXPECT_EQ(v, MakeRamp(20, 1));
-
-#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
-  absl::Span<int> prefix_death(v);
-  EXPECT_DEATH_IF_SUPPORTED(prefix_death.remove_prefix(21), "");
-  absl::Span<int> suffix_death(v);
-  EXPECT_DEATH_IF_SUPPORTED(suffix_death.remove_suffix(21), "");
-#endif
-}
-
-TEST(IntSpan, Subspan) {
-  std::vector<int> empty;
-  EXPECT_EQ(absl::MakeSpan(empty).subspan(), empty);
-  EXPECT_THAT(absl::MakeSpan(empty).subspan(0, 0), SpanIs(empty));
-  EXPECT_THAT(absl::MakeSpan(empty).subspan(0, absl::Span<const int>::npos),
-              SpanIs(empty));
-
-  auto ramp = MakeRamp(10);
-  EXPECT_THAT(absl::MakeSpan(ramp).subspan(), SpanIs(ramp));
-  EXPECT_THAT(absl::MakeSpan(ramp).subspan(0, 10), SpanIs(ramp));
-  EXPECT_THAT(absl::MakeSpan(ramp).subspan(0, absl::Span<const int>::npos),
-              SpanIs(ramp));
-  EXPECT_THAT(absl::MakeSpan(ramp).subspan(0, 3), SpanIs(ramp.data(), 3));
-  EXPECT_THAT(absl::MakeSpan(ramp).subspan(5, absl::Span<const int>::npos),
-              SpanIs(ramp.data() + 5, 5));
-  EXPECT_THAT(absl::MakeSpan(ramp).subspan(3, 3), SpanIs(ramp.data() + 3, 3));
-  EXPECT_THAT(absl::MakeSpan(ramp).subspan(10, 5), SpanIs(ramp.data() + 10, 0));
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW(absl::MakeSpan(ramp).subspan(11, 5), std::out_of_range);
-#else
-  EXPECT_DEATH_IF_SUPPORTED(absl::MakeSpan(ramp).subspan(11, 5), "");
-#endif
-}
-
-TEST(IntSpan, First) {
-  std::vector<int> empty;
-  EXPECT_THAT(absl::MakeSpan(empty).first(0), SpanIs(empty));
-
-  auto ramp = MakeRamp(10);
-  EXPECT_THAT(absl::MakeSpan(ramp).first(0), SpanIs(ramp.data(), 0));
-  EXPECT_THAT(absl::MakeSpan(ramp).first(10), SpanIs(ramp));
-  EXPECT_THAT(absl::MakeSpan(ramp).first(3), SpanIs(ramp.data(), 3));
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW(absl::MakeSpan(ramp).first(11), std::out_of_range);
-#else
-  EXPECT_DEATH_IF_SUPPORTED(absl::MakeSpan(ramp).first(11), "");
-#endif
-}
-
-TEST(IntSpan, Last) {
-  std::vector<int> empty;
-  EXPECT_THAT(absl::MakeSpan(empty).last(0), SpanIs(empty));
-
-  auto ramp = MakeRamp(10);
-  EXPECT_THAT(absl::MakeSpan(ramp).last(0), SpanIs(ramp.data() + 10, 0));
-  EXPECT_THAT(absl::MakeSpan(ramp).last(10), SpanIs(ramp));
-  EXPECT_THAT(absl::MakeSpan(ramp).last(3), SpanIs(ramp.data() + 7, 3));
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-  EXPECT_THROW(absl::MakeSpan(ramp).last(11), std::out_of_range);
-#else
-  EXPECT_DEATH_IF_SUPPORTED(absl::MakeSpan(ramp).last(11), "");
-#endif
-}
-
-TEST(IntSpan, MakeSpanPtrLength) {
-  std::vector<int> empty;
-  auto s_empty = absl::MakeSpan(empty.data(), empty.size());
-  EXPECT_THAT(s_empty, SpanIs(empty));
-
-  std::array<int, 3> a{{1, 2, 3}};
-  auto s = absl::MakeSpan(a.data(), a.size());
-  EXPECT_THAT(s, SpanIs(a));
-
-  EXPECT_THAT(absl::MakeConstSpan(empty.data(), empty.size()), SpanIs(s_empty));
-  EXPECT_THAT(absl::MakeConstSpan(a.data(), a.size()), SpanIs(s));
-}
-
-TEST(IntSpan, MakeSpanTwoPtrs) {
-  std::vector<int> empty;
-  auto s_empty = absl::MakeSpan(empty.data(), empty.data());
-  EXPECT_THAT(s_empty, SpanIs(empty));
-
-  std::vector<int> v{1, 2, 3};
-  auto s = absl::MakeSpan(v.data(), v.data() + 1);
-  EXPECT_THAT(s, SpanIs(v.data(), 1));
-
-  EXPECT_THAT(absl::MakeConstSpan(empty.data(), empty.data()), SpanIs(s_empty));
-  EXPECT_THAT(absl::MakeConstSpan(v.data(), v.data() + 1), SpanIs(s));
-}
-
-TEST(IntSpan, MakeSpanContainer) {
-  std::vector<int> empty;
-  auto s_empty = absl::MakeSpan(empty);
-  EXPECT_THAT(s_empty, SpanIs(empty));
-
-  std::vector<int> v{1, 2, 3};
-  auto s = absl::MakeSpan(v);
-  EXPECT_THAT(s, SpanIs(v));
-
-  EXPECT_THAT(absl::MakeConstSpan(empty), SpanIs(s_empty));
-  EXPECT_THAT(absl::MakeConstSpan(v), SpanIs(s));
-
-  EXPECT_THAT(absl::MakeSpan(s), SpanIs(s));
-  EXPECT_THAT(absl::MakeConstSpan(s), SpanIs(s));
-}
-
-TEST(CharSpan, MakeSpanString) {
-  std::string empty = "";
-  auto s_empty = absl::MakeSpan(empty);
-  EXPECT_THAT(s_empty, SpanIs(empty));
-
-  std::string str = "abc";
-  auto s_str = absl::MakeSpan(str);
-  EXPECT_THAT(s_str, SpanIs(str));
-
-  EXPECT_THAT(absl::MakeConstSpan(empty), SpanIs(s_empty));
-  EXPECT_THAT(absl::MakeConstSpan(str), SpanIs(s_str));
-}
-
-TEST(IntSpan, MakeSpanArray) {
-  int a[] = {1, 2, 3};
-  auto s = absl::MakeSpan(a);
-  EXPECT_THAT(s, SpanIs(a, 3));
-
-  const int ca[] = {1, 2, 3};
-  auto s_ca = absl::MakeSpan(ca);
-  EXPECT_THAT(s_ca, SpanIs(ca, 3));
-
-  EXPECT_THAT(absl::MakeConstSpan(a), SpanIs(s));
-  EXPECT_THAT(absl::MakeConstSpan(ca), SpanIs(s_ca));
-}
-
-// Compile-asserts that the argument has the expected decayed type.
-template <typename Expected, typename T>
-void CheckType(const T& /* value */) {
-  testing::StaticAssertTypeEq<Expected, T>();
-}
-
-TEST(IntSpan, MakeSpanTypes) {
-  std::vector<int> vec;
-  const std::vector<int> cvec;
-  int a[1];
-  const int ca[] = {1};
-  int* ip = a;
-  const int* cip = ca;
-  std::string s = "";
-  const std::string cs = "";
-  CheckType<absl::Span<int>>(absl::MakeSpan(vec));
-  CheckType<absl::Span<const int>>(absl::MakeSpan(cvec));
-  CheckType<absl::Span<int>>(absl::MakeSpan(ip, ip + 1));
-  CheckType<absl::Span<int>>(absl::MakeSpan(ip, 1));
-  CheckType<absl::Span<const int>>(absl::MakeSpan(cip, cip + 1));
-  CheckType<absl::Span<const int>>(absl::MakeSpan(cip, 1));
-  CheckType<absl::Span<int>>(absl::MakeSpan(a));
-  CheckType<absl::Span<int>>(absl::MakeSpan(a, a + 1));
-  CheckType<absl::Span<int>>(absl::MakeSpan(a, 1));
-  CheckType<absl::Span<const int>>(absl::MakeSpan(ca));
-  CheckType<absl::Span<const int>>(absl::MakeSpan(ca, ca + 1));
-  CheckType<absl::Span<const int>>(absl::MakeSpan(ca, 1));
-  CheckType<absl::Span<char>>(absl::MakeSpan(s));
-  CheckType<absl::Span<const char>>(absl::MakeSpan(cs));
-}
-
-TEST(ConstIntSpan, MakeConstSpanTypes) {
-  std::vector<int> vec;
-  const std::vector<int> cvec;
-  int array[1];
-  const int carray[] = {0};
-  int* ptr = array;
-  const int* cptr = carray;
-  std::string s = "";
-  std::string cs = "";
-  CheckType<absl::Span<const int>>(absl::MakeConstSpan(vec));
-  CheckType<absl::Span<const int>>(absl::MakeConstSpan(cvec));
-  CheckType<absl::Span<const int>>(absl::MakeConstSpan(ptr, ptr + 1));
-  CheckType<absl::Span<const int>>(absl::MakeConstSpan(ptr, 1));
-  CheckType<absl::Span<const int>>(absl::MakeConstSpan(cptr, cptr + 1));
-  CheckType<absl::Span<const int>>(absl::MakeConstSpan(cptr, 1));
-  CheckType<absl::Span<const int>>(absl::MakeConstSpan(array));
-  CheckType<absl::Span<const int>>(absl::MakeConstSpan(carray));
-  CheckType<absl::Span<const char>>(absl::MakeConstSpan(s));
-  CheckType<absl::Span<const char>>(absl::MakeConstSpan(cs));
-}
-
-TEST(IntSpan, Equality) {
-  const int arr1[] = {1, 2, 3, 4, 5};
-  int arr2[] = {1, 2, 3, 4, 5};
-  std::vector<int> vec1(std::begin(arr1), std::end(arr1));
-  std::vector<int> vec2 = vec1;
-  std::vector<int> other_vec = {2, 4, 6, 8, 10};
-  // These two slices are from different vectors, but have the same size and
-  // have the same elements (right now).  They should compare equal. Test both
-  // == and !=.
-  const absl::Span<const int> from1 = vec1;
-  const absl::Span<const int> from2 = vec2;
-  EXPECT_EQ(from1, from1);
-  EXPECT_FALSE(from1 != from1);
-  EXPECT_EQ(from1, from2);
-  EXPECT_FALSE(from1 != from2);
-
-  // These two slices have different underlying vector values. They should be
-  // considered not equal. Test both == and !=.
-  const absl::Span<const int> from_other = other_vec;
-  EXPECT_NE(from1, from_other);
-  EXPECT_FALSE(from1 == from_other);
-
-  // Comparison between a vector and its slice should be equal. And vice-versa.
-  // This ensures implicit conversion to Span works on both sides of ==.
-  EXPECT_EQ(vec1, from1);
-  EXPECT_FALSE(vec1 != from1);
-  EXPECT_EQ(from1, vec1);
-  EXPECT_FALSE(from1 != vec1);
-
-  // This verifies that absl::Span<T> can be compared freely with
-  // absl::Span<const T>.
-  const absl::Span<int> mutable_from1(vec1);
-  const absl::Span<int> mutable_from2(vec2);
-  EXPECT_EQ(from1, mutable_from1);
-  EXPECT_EQ(mutable_from1, from1);
-  EXPECT_EQ(mutable_from1, mutable_from2);
-  EXPECT_EQ(mutable_from2, mutable_from1);
-
-  // Comparison between a vector and its slice should be equal for mutable
-  // Spans as well.
-  EXPECT_EQ(vec1, mutable_from1);
-  EXPECT_FALSE(vec1 != mutable_from1);
-  EXPECT_EQ(mutable_from1, vec1);
-  EXPECT_FALSE(mutable_from1 != vec1);
-
-  // Comparison between convertible-to-Span-of-const and Span-of-mutable. Arrays
-  // are used because they're the only value type which converts to a
-  // Span-of-mutable. EXPECT_TRUE is used instead of EXPECT_EQ to avoid
-  // array-to-pointer decay.
-  EXPECT_TRUE(arr1 == mutable_from1);
-  EXPECT_FALSE(arr1 != mutable_from1);
-  EXPECT_TRUE(mutable_from1 == arr1);
-  EXPECT_FALSE(mutable_from1 != arr1);
-
-  // Comparison between convertible-to-Span-of-mutable and Span-of-const
-  EXPECT_TRUE(arr2 == from1);
-  EXPECT_FALSE(arr2 != from1);
-  EXPECT_TRUE(from1 == arr2);
-  EXPECT_FALSE(from1 != arr2);
-
-  // With a different size, the array slices should not be equal.
-  EXPECT_NE(from1, absl::Span<const int>(from1).subspan(0, from1.size() - 1));
-
-  // With different contents, the array slices should not be equal.
-  ++vec2.back();
-  EXPECT_NE(from1, from2);
-}
-
-class IntSpanOrderComparisonTest : public testing::Test {
- public:
-  IntSpanOrderComparisonTest()
-      : arr_before_{1, 2, 3},
-        arr_after_{1, 2, 4},
-        carr_after_{1, 2, 4},
-        vec_before_(std::begin(arr_before_), std::end(arr_before_)),
-        vec_after_(std::begin(arr_after_), std::end(arr_after_)),
-        before_(vec_before_),
-        after_(vec_after_),
-        cbefore_(vec_before_),
-        cafter_(vec_after_) {}
-
- protected:
-  int arr_before_[3], arr_after_[3];
-  const int carr_after_[3];
-  std::vector<int> vec_before_, vec_after_;
-  absl::Span<int> before_, after_;
-  absl::Span<const int> cbefore_, cafter_;
-};
-
-TEST_F(IntSpanOrderComparisonTest, CompareSpans) {
-  EXPECT_TRUE(cbefore_ < cafter_);
-  EXPECT_TRUE(cbefore_ <= cafter_);
-  EXPECT_TRUE(cafter_ > cbefore_);
-  EXPECT_TRUE(cafter_ >= cbefore_);
-
-  EXPECT_FALSE(cbefore_ > cafter_);
-  EXPECT_FALSE(cafter_ < cbefore_);
-
-  EXPECT_TRUE(before_ < after_);
-  EXPECT_TRUE(before_ <= after_);
-  EXPECT_TRUE(after_ > before_);
-  EXPECT_TRUE(after_ >= before_);
-
-  EXPECT_FALSE(before_ > after_);
-  EXPECT_FALSE(after_ < before_);
-
-  EXPECT_TRUE(cbefore_ < after_);
-  EXPECT_TRUE(cbefore_ <= after_);
-  EXPECT_TRUE(after_ > cbefore_);
-  EXPECT_TRUE(after_ >= cbefore_);
-
-  EXPECT_FALSE(cbefore_ > after_);
-  EXPECT_FALSE(after_ < cbefore_);
-}
-
-TEST_F(IntSpanOrderComparisonTest, SpanOfConstAndContainer) {
-  EXPECT_TRUE(cbefore_ < vec_after_);
-  EXPECT_TRUE(cbefore_ <= vec_after_);
-  EXPECT_TRUE(vec_after_ > cbefore_);
-  EXPECT_TRUE(vec_after_ >= cbefore_);
-
-  EXPECT_FALSE(cbefore_ > vec_after_);
-  EXPECT_FALSE(vec_after_ < cbefore_);
-
-  EXPECT_TRUE(arr_before_ < cafter_);
-  EXPECT_TRUE(arr_before_ <= cafter_);
-  EXPECT_TRUE(cafter_ > arr_before_);
-  EXPECT_TRUE(cafter_ >= arr_before_);
-
-  EXPECT_FALSE(arr_before_ > cafter_);
-  EXPECT_FALSE(cafter_ < arr_before_);
-}
-
-TEST_F(IntSpanOrderComparisonTest, SpanOfMutableAndContainer) {
-  EXPECT_TRUE(vec_before_ < after_);
-  EXPECT_TRUE(vec_before_ <= after_);
-  EXPECT_TRUE(after_ > vec_before_);
-  EXPECT_TRUE(after_ >= vec_before_);
-
-  EXPECT_FALSE(vec_before_ > after_);
-  EXPECT_FALSE(after_ < vec_before_);
-
-  EXPECT_TRUE(before_ < carr_after_);
-  EXPECT_TRUE(before_ <= carr_after_);
-  EXPECT_TRUE(carr_after_ > before_);
-  EXPECT_TRUE(carr_after_ >= before_);
-
-  EXPECT_FALSE(before_ > carr_after_);
-  EXPECT_FALSE(carr_after_ < before_);
-}
-
-TEST_F(IntSpanOrderComparisonTest, EqualSpans) {
-  EXPECT_FALSE(before_ < before_);
-  EXPECT_TRUE(before_ <= before_);
-  EXPECT_FALSE(before_ > before_);
-  EXPECT_TRUE(before_ >= before_);
-}
-
-TEST_F(IntSpanOrderComparisonTest, Subspans) {
-  auto subspan = before_.subspan(0, 1);
-  EXPECT_TRUE(subspan < before_);
-  EXPECT_TRUE(subspan <= before_);
-  EXPECT_TRUE(before_ > subspan);
-  EXPECT_TRUE(before_ >= subspan);
-
-  EXPECT_FALSE(subspan > before_);
-  EXPECT_FALSE(before_ < subspan);
-}
-
-TEST_F(IntSpanOrderComparisonTest, EmptySpans) {
-  absl::Span<int> empty;
-  EXPECT_FALSE(empty < empty);
-  EXPECT_TRUE(empty <= empty);
-  EXPECT_FALSE(empty > empty);
-  EXPECT_TRUE(empty >= empty);
-
-  EXPECT_TRUE(empty < before_);
-  EXPECT_TRUE(empty <= before_);
-  EXPECT_TRUE(before_ > empty);
-  EXPECT_TRUE(before_ >= empty);
-
-  EXPECT_FALSE(empty > before_);
-  EXPECT_FALSE(before_ < empty);
-}
-
-TEST(IntSpan, ExposesContainerTypesAndConsts) {
-  absl::Span<int> slice;
-  CheckType<absl::Span<int>::iterator>(slice.begin());
-  EXPECT_TRUE((std::is_convertible<decltype(slice.begin()),
-                                   absl::Span<int>::const_iterator>::value));
-  CheckType<absl::Span<int>::const_iterator>(slice.cbegin());
-  EXPECT_TRUE((std::is_convertible<decltype(slice.end()),
-                                   absl::Span<int>::const_iterator>::value));
-  CheckType<absl::Span<int>::const_iterator>(slice.cend());
-  CheckType<absl::Span<int>::reverse_iterator>(slice.rend());
-  EXPECT_TRUE(
-      (std::is_convertible<decltype(slice.rend()),
-                           absl::Span<int>::const_reverse_iterator>::value));
-  CheckType<absl::Span<int>::const_reverse_iterator>(slice.crend());
-  testing::StaticAssertTypeEq<int, absl::Span<int>::value_type>();
-  testing::StaticAssertTypeEq<int, absl::Span<const int>::value_type>();
-  testing::StaticAssertTypeEq<int*, absl::Span<int>::pointer>();
-  testing::StaticAssertTypeEq<const int*, absl::Span<const int>::pointer>();
-  testing::StaticAssertTypeEq<int&, absl::Span<int>::reference>();
-  testing::StaticAssertTypeEq<const int&, absl::Span<const int>::reference>();
-  testing::StaticAssertTypeEq<const int&, absl::Span<int>::const_reference>();
-  testing::StaticAssertTypeEq<const int&,
-                              absl::Span<const int>::const_reference>();
-  EXPECT_EQ(static_cast<absl::Span<int>::size_type>(-1), absl::Span<int>::npos);
-}
-
-TEST(IntSpan, IteratorsAndReferences) {
-  auto accept_pointer = [](int*) {};
-  auto accept_reference = [](int&) {};
-  auto accept_iterator = [](absl::Span<int>::iterator) {};
-  auto accept_const_iterator = [](absl::Span<int>::const_iterator) {};
-  auto accept_reverse_iterator = [](absl::Span<int>::reverse_iterator) {};
-  auto accept_const_reverse_iterator =
-      [](absl::Span<int>::const_reverse_iterator) {};
-
-  int a[1];
-  absl::Span<int> s = a;
-
-  accept_pointer(s.data());
-  accept_iterator(s.begin());
-  accept_const_iterator(s.begin());
-  accept_const_iterator(s.cbegin());
-  accept_iterator(s.end());
-  accept_const_iterator(s.end());
-  accept_const_iterator(s.cend());
-  accept_reverse_iterator(s.rbegin());
-  accept_const_reverse_iterator(s.rbegin());
-  accept_const_reverse_iterator(s.crbegin());
-  accept_reverse_iterator(s.rend());
-  accept_const_reverse_iterator(s.rend());
-  accept_const_reverse_iterator(s.crend());
-
-  accept_reference(s[0]);
-  accept_reference(s.at(0));
-  accept_reference(s.front());
-  accept_reference(s.back());
-}
-
-TEST(IntSpan, IteratorsAndReferences_Const) {
-  auto accept_pointer = [](int*) {};
-  auto accept_reference = [](int&) {};
-  auto accept_iterator = [](absl::Span<int>::iterator) {};
-  auto accept_const_iterator = [](absl::Span<int>::const_iterator) {};
-  auto accept_reverse_iterator = [](absl::Span<int>::reverse_iterator) {};
-  auto accept_const_reverse_iterator =
-      [](absl::Span<int>::const_reverse_iterator) {};
-
-  int a[1];
-  const absl::Span<int> s = a;
-
-  accept_pointer(s.data());
-  accept_iterator(s.begin());
-  accept_const_iterator(s.begin());
-  accept_const_iterator(s.cbegin());
-  accept_iterator(s.end());
-  accept_const_iterator(s.end());
-  accept_const_iterator(s.cend());
-  accept_reverse_iterator(s.rbegin());
-  accept_const_reverse_iterator(s.rbegin());
-  accept_const_reverse_iterator(s.crbegin());
-  accept_reverse_iterator(s.rend());
-  accept_const_reverse_iterator(s.rend());
-  accept_const_reverse_iterator(s.crend());
-
-  accept_reference(s[0]);
-  accept_reference(s.at(0));
-  accept_reference(s.front());
-  accept_reference(s.back());
-}
-
-TEST(IntSpan, NoexceptTest) {
-  int a[] = {1, 2, 3};
-  std::vector<int> v;
-  EXPECT_TRUE(noexcept(absl::Span<const int>()));
-  EXPECT_TRUE(noexcept(absl::Span<const int>(a, 2)));
-  EXPECT_TRUE(noexcept(absl::Span<const int>(a)));
-  EXPECT_TRUE(noexcept(absl::Span<const int>(v)));
-  EXPECT_TRUE(noexcept(absl::Span<int>(v)));
-  EXPECT_TRUE(noexcept(absl::Span<const int>({1, 2, 3})));
-  EXPECT_TRUE(noexcept(absl::MakeSpan(v)));
-  EXPECT_TRUE(noexcept(absl::MakeSpan(a)));
-  EXPECT_TRUE(noexcept(absl::MakeSpan(a, 2)));
-  EXPECT_TRUE(noexcept(absl::MakeSpan(a, a + 1)));
-  EXPECT_TRUE(noexcept(absl::MakeConstSpan(v)));
-  EXPECT_TRUE(noexcept(absl::MakeConstSpan(a)));
-  EXPECT_TRUE(noexcept(absl::MakeConstSpan(a, 2)));
-  EXPECT_TRUE(noexcept(absl::MakeConstSpan(a, a + 1)));
-
-  absl::Span<int> s(v);
-  EXPECT_TRUE(noexcept(s.data()));
-  EXPECT_TRUE(noexcept(s.size()));
-  EXPECT_TRUE(noexcept(s.length()));
-  EXPECT_TRUE(noexcept(s.empty()));
-  EXPECT_TRUE(noexcept(s[0]));
-  EXPECT_TRUE(noexcept(s.front()));
-  EXPECT_TRUE(noexcept(s.back()));
-  EXPECT_TRUE(noexcept(s.begin()));
-  EXPECT_TRUE(noexcept(s.cbegin()));
-  EXPECT_TRUE(noexcept(s.end()));
-  EXPECT_TRUE(noexcept(s.cend()));
-  EXPECT_TRUE(noexcept(s.rbegin()));
-  EXPECT_TRUE(noexcept(s.crbegin()));
-  EXPECT_TRUE(noexcept(s.rend()));
-  EXPECT_TRUE(noexcept(s.crend()));
-  EXPECT_TRUE(noexcept(s.remove_prefix(0)));
-  EXPECT_TRUE(noexcept(s.remove_suffix(0)));
-}
-
-// ConstexprTester exercises expressions in a constexpr context. Simply placing
-// the expression in a constexpr function is not enough, as some compilers will
-// simply compile the constexpr function as runtime code. Using template
-// parameters forces compile-time execution.
-template <int i>
-struct ConstexprTester {};
-
-#define ABSL_TEST_CONSTEXPR(expr)                       \
-  do {                                                  \
-    ABSL_ATTRIBUTE_UNUSED ConstexprTester<(expr, 1)> t; \
-  } while (0)
-
-struct ContainerWithConstexprMethods {
-  constexpr int size() const { return 1; }
-  constexpr const int* data() const { return &i; }
-  const int i;
-};
-
-TEST(ConstIntSpan, ConstexprTest) {
-  static constexpr int a[] = {1, 2, 3};
-  static constexpr int sized_arr[2] = {1, 2};
-  static constexpr ContainerWithConstexprMethods c{1};
-  ABSL_TEST_CONSTEXPR(absl::Span<const int>());
-  ABSL_TEST_CONSTEXPR(absl::Span<const int>(a, 2));
-  ABSL_TEST_CONSTEXPR(absl::Span<const int>(sized_arr));
-  ABSL_TEST_CONSTEXPR(absl::Span<const int>(c));
-  ABSL_TEST_CONSTEXPR(absl::MakeSpan(&a[0], 1));
-  ABSL_TEST_CONSTEXPR(absl::MakeSpan(c));
-  ABSL_TEST_CONSTEXPR(absl::MakeSpan(a));
-  ABSL_TEST_CONSTEXPR(absl::MakeConstSpan(&a[0], 1));
-  ABSL_TEST_CONSTEXPR(absl::MakeConstSpan(c));
-  ABSL_TEST_CONSTEXPR(absl::MakeConstSpan(a));
-
-  constexpr absl::Span<const int> span = c;
-  ABSL_TEST_CONSTEXPR(span.data());
-  ABSL_TEST_CONSTEXPR(span.size());
-  ABSL_TEST_CONSTEXPR(span.length());
-  ABSL_TEST_CONSTEXPR(span.empty());
-  ABSL_TEST_CONSTEXPR(span.begin());
-  ABSL_TEST_CONSTEXPR(span.cbegin());
-  ABSL_TEST_CONSTEXPR(span.subspan(0, 0));
-  ABSL_TEST_CONSTEXPR(span.first(1));
-  ABSL_TEST_CONSTEXPR(span.last(1));
-  ABSL_TEST_CONSTEXPR(span[0]);
-}
-
-struct BigStruct {
-  char bytes[10000];
-};
-
-TEST(Span, SpanSize) {
-  EXPECT_LE(sizeof(absl::Span<int>), 2 * sizeof(void*));
-  EXPECT_LE(sizeof(absl::Span<BigStruct>), 2 * sizeof(void*));
-}
-
-TEST(Span, Hash) {
-  int array[] = {1, 2, 3, 4};
-  int array2[] = {1, 2, 3};
-  using T = absl::Span<const int>;
-  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
-      {// Empties
-       T(), T(nullptr, 0), T(array, 0), T(array2, 0),
-       // Different array with same value
-       T(array, 3), T(array2), T({1, 2, 3}),
-       // Same array, but different length
-       T(array, 1), T(array, 2),
-       // Same length, but different array
-       T(array + 1, 2), T(array + 2, 2)}));
-}
-
-}  // namespace
diff --git a/third_party/abseil_cpp/absl/types/variant.h b/third_party/abseil_cpp/absl/types/variant.h
deleted file mode 100644
index ac93464bf8..0000000000
--- a/third_party/abseil_cpp/absl/types/variant.h
+++ /dev/null
@@ -1,866 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// -----------------------------------------------------------------------------
-// variant.h
-// -----------------------------------------------------------------------------
-//
-// This header file defines an `absl::variant` type for holding a type-safe
-// value of some prescribed set of types (noted as alternative types), and
-// associated functions for managing variants.
-//
-// The `absl::variant` type is a form of type-safe union. An `absl::variant`
-// should always hold a value of one of its alternative types (except in the
-// "valueless by exception state" -- see below). A default-constructed
-// `absl::variant` will hold the value of its first alternative type, provided
-// it is default-constructible.
-//
-// In exceptional cases due to error, an `absl::variant` can hold no
-// value (known as a "valueless by exception" state), though this is not the
-// norm.
-//
-// As with `absl::optional`, an `absl::variant` -- when it holds a value --
-// allocates a value of that type directly within the `variant` itself; it
-// cannot hold a reference, array, or the type `void`; it can, however, hold a
-// pointer to externally managed memory.
-//
-// `absl::variant` is a C++11 compatible version of the C++17 `std::variant`
-// abstraction and is designed to be a drop-in replacement for code compliant
-// with C++17.
-
-#ifndef ABSL_TYPES_VARIANT_H_
-#define ABSL_TYPES_VARIANT_H_
-
-#include "absl/base/config.h"
-#include "absl/utility/utility.h"
-
-#ifdef ABSL_USES_STD_VARIANT
-
-#include <variant>  // IWYU pragma: export
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-using std::bad_variant_access;
-using std::get;
-using std::get_if;
-using std::holds_alternative;
-using std::monostate;
-using std::variant;
-using std::variant_alternative;
-using std::variant_alternative_t;
-using std::variant_npos;
-using std::variant_size;
-using std::variant_size_v;
-using std::visit;
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#else  // ABSL_USES_STD_VARIANT
-
-#include <functional>
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/meta/type_traits.h"
-#include "absl/types/internal/variant.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// -----------------------------------------------------------------------------
-// absl::variant
-// -----------------------------------------------------------------------------
-//
-// An `absl::variant` type is a form of type-safe union. An `absl::variant` --
-// except in exceptional cases -- always holds a value of one of its alternative
-// types.
-//
-// Example:
-//
-//   // Construct a variant that holds either an integer or a std::string and
-//   // assign it to a std::string.
-//   absl::variant<int, std::string> v = std::string("abc");
-//
-//   // A default-constructed variant will hold a value-initialized value of
-//   // the first alternative type.
-//   auto a = absl::variant<int, std::string>();   // Holds an int of value '0'.
-//
-//   // variants are assignable.
-//
-//   // copy assignment
-//   auto v1 = absl::variant<int, std::string>("abc");
-//   auto v2 = absl::variant<int, std::string>(10);
-//   v2 = v1;  // copy assign
-//
-//   // move assignment
-//   auto v1 = absl::variant<int, std::string>("abc");
-//   v1 = absl::variant<int, std::string>(10);
-//
-//   // assignment through type conversion
-//   a = 128;         // variant contains int
-//   a = "128";       // variant contains std::string
-//
-// An `absl::variant` holding a value of one of its alternative types `T` holds
-// an allocation of `T` directly within the variant itself. An `absl::variant`
-// is not allowed to allocate additional storage, such as dynamic memory, to
-// allocate the contained value. The contained value shall be allocated in a
-// region of the variant storage suitably aligned for all alternative types.
-template <typename... Ts>
-class variant;
-
-// swap()
-//
-// Swaps two `absl::variant` values. This function is equivalent to `v.swap(w)`
-// where `v` and `w` are `absl::variant` types.
-//
-// Note that this function requires all alternative types to be both swappable
-// and move-constructible, because any two variants may refer to either the same
-// type (in which case, they will be swapped) or to two different types (in
-// which case the values will need to be moved).
-//
-template <
-    typename... Ts,
-    absl::enable_if_t<
-        absl::conjunction<std::is_move_constructible<Ts>...,
-                          type_traits_internal::IsSwappable<Ts>...>::value,
-        int> = 0>
-void swap(variant<Ts...>& v, variant<Ts...>& w) noexcept(noexcept(v.swap(w))) {
-  v.swap(w);
-}
-
-// variant_size
-//
-// Returns the number of alternative types available for a given `absl::variant`
-// type as a compile-time constant expression. As this is a class template, it
-// is not generally useful for accessing the number of alternative types of
-// any given `absl::variant` instance.
-//
-// Example:
-//
-//   auto a = absl::variant<int, std::string>;
-//   constexpr int num_types =
-//       absl::variant_size<absl::variant<int, std::string>>();
-//
-//   // You can also use the member constant `value`.
-//   constexpr int num_types =
-//       absl::variant_size<absl::variant<int, std::string>>::value;
-//
-//   // `absl::variant_size` is more valuable for use in generic code:
-//   template <typename Variant>
-//   constexpr bool IsVariantMultivalue() {
-//       return absl::variant_size<Variant>() > 1;
-//   }
-//
-// Note that the set of cv-qualified specializations of `variant_size` are
-// provided to ensure that those specializations compile (especially when passed
-// within template logic).
-template <class T>
-struct variant_size;
-
-template <class... Ts>
-struct variant_size<variant<Ts...>>
-    : std::integral_constant<std::size_t, sizeof...(Ts)> {};
-
-// Specialization of `variant_size` for const qualified variants.
-template <class T>
-struct variant_size<const T> : variant_size<T>::type {};
-
-// Specialization of `variant_size` for volatile qualified variants.
-template <class T>
-struct variant_size<volatile T> : variant_size<T>::type {};
-
-// Specialization of `variant_size` for const volatile qualified variants.
-template <class T>
-struct variant_size<const volatile T> : variant_size<T>::type {};
-
-// variant_alternative
-//
-// Returns the alternative type for a given `absl::variant` at the passed
-// index value as a compile-time constant expression. As this is a class
-// template resulting in a type, it is not useful for access of the run-time
-// value of any given `absl::variant` variable.
-//
-// Example:
-//
-//   // The type of the 0th alternative is "int".
-//   using alternative_type_0
-//     = absl::variant_alternative<0, absl::variant<int, std::string>>::type;
-//
-//   static_assert(std::is_same<alternative_type_0, int>::value, "");
-//
-//   // `absl::variant_alternative` is more valuable for use in generic code:
-//   template <typename Variant>
-//   constexpr bool IsFirstElementTrivial() {
-//       return std::is_trivial_v<variant_alternative<0, Variant>::type>;
-//   }
-//
-// Note that the set of cv-qualified specializations of `variant_alternative`
-// are provided to ensure that those specializations compile (especially when
-// passed within template logic).
-template <std::size_t I, class T>
-struct variant_alternative;
-
-template <std::size_t I, class... Types>
-struct variant_alternative<I, variant<Types...>> {
-  using type =
-      variant_internal::VariantAlternativeSfinaeT<I, variant<Types...>>;
-};
-
-// Specialization of `variant_alternative` for const qualified variants.
-template <std::size_t I, class T>
-struct variant_alternative<I, const T> {
-  using type = const typename variant_alternative<I, T>::type;
-};
-
-// Specialization of `variant_alternative` for volatile qualified variants.
-template <std::size_t I, class T>
-struct variant_alternative<I, volatile T> {
-  using type = volatile typename variant_alternative<I, T>::type;
-};
-
-// Specialization of `variant_alternative` for const volatile qualified
-// variants.
-template <std::size_t I, class T>
-struct variant_alternative<I, const volatile T> {
-  using type = const volatile typename variant_alternative<I, T>::type;
-};
-
-// Template type alias for variant_alternative<I, T>::type.
-//
-// Example:
-//
-//   using alternative_type_0
-//     = absl::variant_alternative_t<0, absl::variant<int, std::string>>;
-//   static_assert(std::is_same<alternative_type_0, int>::value, "");
-template <std::size_t I, class T>
-using variant_alternative_t = typename variant_alternative<I, T>::type;
-
-// holds_alternative()
-//
-// Checks whether the given variant currently holds a given alternative type,
-// returning `true` if so.
-//
-// Example:
-//
-//   absl::variant<int, std::string> foo = 42;
-//   if (absl::holds_alternative<int>(foo)) {
-//       std::cout << "The variant holds an integer";
-//   }
-template <class T, class... Types>
-constexpr bool holds_alternative(const variant<Types...>& v) noexcept {
-  static_assert(
-      variant_internal::UnambiguousIndexOfImpl<variant<Types...>, T,
-                                               0>::value != sizeof...(Types),
-      "The type T must occur exactly once in Types...");
-  return v.index() ==
-         variant_internal::UnambiguousIndexOf<variant<Types...>, T>::value;
-}
-
-// get()
-//
-// Returns a reference to the value currently within a given variant, using
-// either a unique alternative type amongst the variant's set of alternative
-// types, or the variant's index value. Attempting to get a variant's value
-// using a type that is not unique within the variant's set of alternative types
-// is a compile-time error. If the index of the alternative being specified is
-// different from the index of the alternative that is currently stored, throws
-// `absl::bad_variant_access`.
-//
-// Example:
-//
-//   auto a = absl::variant<int, std::string>;
-//
-//   // Get the value by type (if unique).
-//   int i = absl::get<int>(a);
-//
-//   auto b = absl::variant<int, int>;
-//
-//   // Getting the value by a type that is not unique is ill-formed.
-//   int j = absl::get<int>(b);     // Compile Error!
-//
-//   // Getting value by index not ambiguous and allowed.
-//   int k = absl::get<1>(b);
-
-// Overload for getting a variant's lvalue by type.
-template <class T, class... Types>
-constexpr T& get(variant<Types...>& v) {  // NOLINT
-  return variant_internal::VariantCoreAccess::CheckedAccess<
-      variant_internal::IndexOf<T, Types...>::value>(v);
-}
-
-// Overload for getting a variant's rvalue by type.
-// Note: `absl::move()` is required to allow use of constexpr in C++11.
-template <class T, class... Types>
-constexpr T&& get(variant<Types...>&& v) {
-  return variant_internal::VariantCoreAccess::CheckedAccess<
-      variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
-}
-
-// Overload for getting a variant's const lvalue by type.
-template <class T, class... Types>
-constexpr const T& get(const variant<Types...>& v) {
-  return variant_internal::VariantCoreAccess::CheckedAccess<
-      variant_internal::IndexOf<T, Types...>::value>(v);
-}
-
-// Overload for getting a variant's const rvalue by type.
-// Note: `absl::move()` is required to allow use of constexpr in C++11.
-template <class T, class... Types>
-constexpr const T&& get(const variant<Types...>&& v) {
-  return variant_internal::VariantCoreAccess::CheckedAccess<
-      variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
-}
-
-// Overload for getting a variant's lvalue by index.
-template <std::size_t I, class... Types>
-constexpr variant_alternative_t<I, variant<Types...>>& get(
-    variant<Types...>& v) {  // NOLINT
-  return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
-}
-
-// Overload for getting a variant's rvalue by index.
-// Note: `absl::move()` is required to allow use of constexpr in C++11.
-template <std::size_t I, class... Types>
-constexpr variant_alternative_t<I, variant<Types...>>&& get(
-    variant<Types...>&& v) {
-  return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
-}
-
-// Overload for getting a variant's const lvalue by index.
-template <std::size_t I, class... Types>
-constexpr const variant_alternative_t<I, variant<Types...>>& get(
-    const variant<Types...>& v) {
-  return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
-}
-
-// Overload for getting a variant's const rvalue by index.
-// Note: `absl::move()` is required to allow use of constexpr in C++11.
-template <std::size_t I, class... Types>
-constexpr const variant_alternative_t<I, variant<Types...>>&& get(
-    const variant<Types...>&& v) {
-  return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
-}
-
-// get_if()
-//
-// Returns a pointer to the value currently stored within a given variant, if
-// present, using either a unique alternative type amongst the variant's set of
-// alternative types, or the variant's index value. If such a value does not
-// exist, returns `nullptr`.
-//
-// As with `get`, attempting to get a variant's value using a type that is not
-// unique within the variant's set of alternative types is a compile-time error.
-
-// Overload for getting a pointer to the value stored in the given variant by
-// index.
-template <std::size_t I, class... Types>
-constexpr absl::add_pointer_t<variant_alternative_t<I, variant<Types...>>>
-get_if(variant<Types...>* v) noexcept {
-  return (v != nullptr && v->index() == I)
-             ? std::addressof(
-                   variant_internal::VariantCoreAccess::Access<I>(*v))
-             : nullptr;
-}
-
-// Overload for getting a pointer to the const value stored in the given
-// variant by index.
-template <std::size_t I, class... Types>
-constexpr absl::add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
-get_if(const variant<Types...>* v) noexcept {
-  return (v != nullptr && v->index() == I)
-             ? std::addressof(
-                   variant_internal::VariantCoreAccess::Access<I>(*v))
-             : nullptr;
-}
-
-// Overload for getting a pointer to the value stored in the given variant by
-// type.
-template <class T, class... Types>
-constexpr absl::add_pointer_t<T> get_if(variant<Types...>* v) noexcept {
-  return absl::get_if<variant_internal::IndexOf<T, Types...>::value>(v);
-}
-
-// Overload for getting a pointer to the const value stored in the given variant
-// by type.
-template <class T, class... Types>
-constexpr absl::add_pointer_t<const T> get_if(
-    const variant<Types...>* v) noexcept {
-  return absl::get_if<variant_internal::IndexOf<T, Types...>::value>(v);
-}
-
-// visit()
-//
-// Calls a provided functor on a given set of variants. `absl::visit()` is
-// commonly used to conditionally inspect the state of a given variant (or set
-// of variants).
-//
-// The functor must return the same type when called with any of the variants'
-// alternatives.
-//
-// Example:
-//
-//   // Define a visitor functor
-//   struct GetVariant {
-//       template<typename T>
-//       void operator()(const T& i) const {
-//         std::cout << "The variant's value is: " << i;
-//       }
-//   };
-//
-//   // Declare our variant, and call `absl::visit()` on it.
-//   // Note that `GetVariant()` returns void in either case.
-//   absl::variant<int, std::string> foo = std::string("foo");
-//   GetVariant visitor;
-//   absl::visit(visitor, foo);  // Prints `The variant's value is: foo'
-template <typename Visitor, typename... Variants>
-variant_internal::VisitResult<Visitor, Variants...> visit(Visitor&& vis,
-                                                          Variants&&... vars) {
-  return variant_internal::
-      VisitIndices<variant_size<absl::decay_t<Variants> >::value...>::Run(
-          variant_internal::PerformVisitation<Visitor, Variants...>{
-              std::forward_as_tuple(absl::forward<Variants>(vars)...),
-              absl::forward<Visitor>(vis)},
-          vars.index()...);
-}
-
-// monostate
-//
-// The monostate class serves as a first alternative type for a variant for
-// which the first variant type is otherwise not default-constructible.
-struct monostate {};
-
-// `absl::monostate` Relational Operators
-
-constexpr bool operator<(monostate, monostate) noexcept { return false; }
-constexpr bool operator>(monostate, monostate) noexcept { return false; }
-constexpr bool operator<=(monostate, monostate) noexcept { return true; }
-constexpr bool operator>=(monostate, monostate) noexcept { return true; }
-constexpr bool operator==(monostate, monostate) noexcept { return true; }
-constexpr bool operator!=(monostate, monostate) noexcept { return false; }
-
-
-//------------------------------------------------------------------------------
-// `absl::variant` Template Definition
-//------------------------------------------------------------------------------
-template <typename T0, typename... Tn>
-class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
-  static_assert(absl::conjunction<std::is_object<T0>,
-                                  std::is_object<Tn>...>::value,
-                "Attempted to instantiate a variant containing a non-object "
-                "type.");
-  // Intentionally not qualifying `negation` with `absl::` to work around a bug
-  // in MSVC 2015 with inline namespace and variadic template.
-  static_assert(absl::conjunction<negation<std::is_array<T0> >,
-                                  negation<std::is_array<Tn> >...>::value,
-                "Attempted to instantiate a variant containing an array type.");
-  static_assert(absl::conjunction<std::is_nothrow_destructible<T0>,
-                                  std::is_nothrow_destructible<Tn>...>::value,
-                "Attempted to instantiate a variant containing a non-nothrow "
-                "destructible type.");
-
-  friend struct variant_internal::VariantCoreAccess;
-
- private:
-  using Base = variant_internal::VariantBase<T0, Tn...>;
-
- public:
-  // Constructors
-
-  // Constructs a variant holding a default-initialized value of the first
-  // alternative type.
-  constexpr variant() /*noexcept(see 111above)*/ = default;
-
-  // Copy constructor, standard semantics
-  variant(const variant& other) = default;
-
-  // Move constructor, standard semantics
-  variant(variant&& other) /*noexcept(see above)*/ = default;
-
-  // Constructs a variant of an alternative type specified by overload
-  // resolution of the provided forwarding arguments through
-  // direct-initialization.
-  //
-  // Note: If the selected constructor is a constexpr constructor, this
-  // constructor shall be a constexpr constructor.
-  //
-  // NOTE: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0608r1.html
-  // has been voted passed the design phase in the C++ standard meeting in Mar
-  // 2018. It will be implemented and integrated into `absl::variant`.
-  template <
-      class T,
-      std::size_t I = std::enable_if<
-          variant_internal::IsNeitherSelfNorInPlace<variant,
-                                                    absl::decay_t<T>>::value,
-          variant_internal::IndexOfConstructedType<variant, T>>::type::value,
-      class Tj = absl::variant_alternative_t<I, variant>,
-      absl::enable_if_t<std::is_constructible<Tj, T>::value>* =
-          nullptr>
-  constexpr variant(T&& t) noexcept(std::is_nothrow_constructible<Tj, T>::value)
-      : Base(variant_internal::EmplaceTag<I>(), absl::forward<T>(t)) {}
-
-  // Constructs a variant of an alternative type from the arguments through
-  // direct-initialization.
-  //
-  // Note: If the selected constructor is a constexpr constructor, this
-  // constructor shall be a constexpr constructor.
-  template <class T, class... Args,
-            typename std::enable_if<std::is_constructible<
-                variant_internal::UnambiguousTypeOfT<variant, T>,
-                Args...>::value>::type* = nullptr>
-  constexpr explicit variant(in_place_type_t<T>, Args&&... args)
-      : Base(variant_internal::EmplaceTag<
-                 variant_internal::UnambiguousIndexOf<variant, T>::value>(),
-             absl::forward<Args>(args)...) {}
-
-  // Constructs a variant of an alternative type from an initializer list
-  // and other arguments through direct-initialization.
-  //
-  // Note: If the selected constructor is a constexpr constructor, this
-  // constructor shall be a constexpr constructor.
-  template <class T, class U, class... Args,
-            typename std::enable_if<std::is_constructible<
-                variant_internal::UnambiguousTypeOfT<variant, T>,
-                std::initializer_list<U>&, Args...>::value>::type* = nullptr>
-  constexpr explicit variant(in_place_type_t<T>, std::initializer_list<U> il,
-                             Args&&... args)
-      : Base(variant_internal::EmplaceTag<
-                 variant_internal::UnambiguousIndexOf<variant, T>::value>(),
-             il, absl::forward<Args>(args)...) {}
-
-  // Constructs a variant of an alternative type from a provided index,
-  // through value-initialization using the provided forwarded arguments.
-  template <std::size_t I, class... Args,
-            typename std::enable_if<std::is_constructible<
-                variant_internal::VariantAlternativeSfinaeT<I, variant>,
-                Args...>::value>::type* = nullptr>
-  constexpr explicit variant(in_place_index_t<I>, Args&&... args)
-      : Base(variant_internal::EmplaceTag<I>(), absl::forward<Args>(args)...) {}
-
-  // Constructs a variant of an alternative type from a provided index,
-  // through value-initialization of an initializer list and the provided
-  // forwarded arguments.
-  template <std::size_t I, class U, class... Args,
-            typename std::enable_if<std::is_constructible<
-                variant_internal::VariantAlternativeSfinaeT<I, variant>,
-                std::initializer_list<U>&, Args...>::value>::type* = nullptr>
-  constexpr explicit variant(in_place_index_t<I>, std::initializer_list<U> il,
-                             Args&&... args)
-      : Base(variant_internal::EmplaceTag<I>(), il,
-             absl::forward<Args>(args)...) {}
-
-  // Destructors
-
-  // Destroys the variant's currently contained value, provided that
-  // `absl::valueless_by_exception()` is false.
-  ~variant() = default;
-
-  // Assignment Operators
-
-  // Copy assignment operator
-  variant& operator=(const variant& other) = default;
-
-  // Move assignment operator
-  variant& operator=(variant&& other) /*noexcept(see above)*/ = default;
-
-  // Converting assignment operator
-  //
-  // NOTE: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0608r1.html
-  // has been voted passed the design phase in the C++ standard meeting in Mar
-  // 2018. It will be implemented and integrated into `absl::variant`.
-  template <
-      class T,
-      std::size_t I = std::enable_if<
-          !std::is_same<absl::decay_t<T>, variant>::value,
-          variant_internal::IndexOfConstructedType<variant, T>>::type::value,
-      class Tj = absl::variant_alternative_t<I, variant>,
-      typename std::enable_if<std::is_assignable<Tj&, T>::value &&
-                              std::is_constructible<Tj, T>::value>::type* =
-          nullptr>
-  variant& operator=(T&& t) noexcept(
-      std::is_nothrow_assignable<Tj&, T>::value&&
-          std::is_nothrow_constructible<Tj, T>::value) {
-    variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run(
-        variant_internal::VariantCoreAccess::MakeConversionAssignVisitor(
-            this, absl::forward<T>(t)),
-        index());
-
-    return *this;
-  }
-
-
-  // emplace() Functions
-
-  // Constructs a value of the given alternative type T within the variant. The
-  // existing value of the variant is destroyed first (provided that
-  // `absl::valueless_by_exception()` is false). Requires that T is unambiguous
-  // in the variant.
-  //
-  // Example:
-  //
-  //   absl::variant<std::vector<int>, int, std::string> v;
-  //   v.emplace<int>(99);
-  //   v.emplace<std::string>("abc");
-  template <
-      class T, class... Args,
-      typename std::enable_if<std::is_constructible<
-          absl::variant_alternative_t<
-              variant_internal::UnambiguousIndexOf<variant, T>::value, variant>,
-          Args...>::value>::type* = nullptr>
-  T& emplace(Args&&... args) {
-    return variant_internal::VariantCoreAccess::Replace<
-        variant_internal::UnambiguousIndexOf<variant, T>::value>(
-        this, absl::forward<Args>(args)...);
-  }
-
-  // Constructs a value of the given alternative type T within the variant using
-  // an initializer list. The existing value of the variant is destroyed first
-  // (provided that `absl::valueless_by_exception()` is false). Requires that T
-  // is unambiguous in the variant.
-  //
-  // Example:
-  //
-  //   absl::variant<std::vector<int>, int, std::string> v;
-  //   v.emplace<std::vector<int>>({0, 1, 2});
-  template <
-      class T, class U, class... Args,
-      typename std::enable_if<std::is_constructible<
-          absl::variant_alternative_t<
-              variant_internal::UnambiguousIndexOf<variant, T>::value, variant>,
-          std::initializer_list<U>&, Args...>::value>::type* = nullptr>
-  T& emplace(std::initializer_list<U> il, Args&&... args) {
-    return variant_internal::VariantCoreAccess::Replace<
-        variant_internal::UnambiguousIndexOf<variant, T>::value>(
-        this, il, absl::forward<Args>(args)...);
-  }
-
-  // Destroys the current value of the variant (provided that
-  // `absl::valueless_by_exception()` is false) and constructs a new value at
-  // the given index.
-  //
-  // Example:
-  //
-  //   absl::variant<std::vector<int>, int, int> v;
-  //   v.emplace<1>(99);
-  //   v.emplace<2>(98);
-  //   v.emplace<int>(99);  // Won't compile. 'int' isn't a unique type.
-  template <std::size_t I, class... Args,
-            typename std::enable_if<
-                std::is_constructible<absl::variant_alternative_t<I, variant>,
-                                      Args...>::value>::type* = nullptr>
-  absl::variant_alternative_t<I, variant>& emplace(Args&&... args) {
-    return variant_internal::VariantCoreAccess::Replace<I>(
-        this, absl::forward<Args>(args)...);
-  }
-
-  // Destroys the current value of the variant (provided that
-  // `absl::valueless_by_exception()` is false) and constructs a new value at
-  // the given index using an initializer list and the provided arguments.
-  //
-  // Example:
-  //
-  //   absl::variant<std::vector<int>, int, int> v;
-  //   v.emplace<0>({0, 1, 2});
-  template <std::size_t I, class U, class... Args,
-            typename std::enable_if<std::is_constructible<
-                absl::variant_alternative_t<I, variant>,
-                std::initializer_list<U>&, Args...>::value>::type* = nullptr>
-  absl::variant_alternative_t<I, variant>& emplace(std::initializer_list<U> il,
-                                                   Args&&... args) {
-    return variant_internal::VariantCoreAccess::Replace<I>(
-        this, il, absl::forward<Args>(args)...);
-  }
-
-  // variant::valueless_by_exception()
-  //
-  // Returns false if and only if the variant currently holds a valid value.
-  constexpr bool valueless_by_exception() const noexcept {
-    return this->index_ == absl::variant_npos;
-  }
-
-  // variant::index()
-  //
-  // Returns the index value of the variant's currently selected alternative
-  // type.
-  constexpr std::size_t index() const noexcept { return this->index_; }
-
-  // variant::swap()
-  //
-  // Swaps the values of two variant objects.
-  //
-  void swap(variant& rhs) noexcept(
-      absl::conjunction<
-          std::is_nothrow_move_constructible<T0>,
-          std::is_nothrow_move_constructible<Tn>...,
-          type_traits_internal::IsNothrowSwappable<T0>,
-          type_traits_internal::IsNothrowSwappable<Tn>...>::value) {
-    return variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run(
-        variant_internal::Swap<T0, Tn...>{this, &rhs}, rhs.index());
-  }
-};
-
-// We need a valid declaration of variant<> for SFINAE and overload resolution
-// to work properly above, but we don't need a full declaration since this type
-// will never be constructed. This declaration, though incomplete, suffices.
-template <>
-class variant<>;
-
-//------------------------------------------------------------------------------
-// Relational Operators
-//------------------------------------------------------------------------------
-//
-// If neither operand is in the `variant::valueless_by_exception` state:
-//
-//   * If the index of both variants is the same, the relational operator
-//     returns the result of the corresponding relational operator for the
-//     corresponding alternative type.
-//   * If the index of both variants is not the same, the relational operator
-//     returns the result of that operation applied to the value of the left
-//     operand's index and the value of the right operand's index.
-//   * If at least one operand is in the valueless_by_exception state:
-//     - A variant in the valueless_by_exception state is only considered equal
-//       to another variant in the valueless_by_exception state.
-//     - If exactly one operand is in the valueless_by_exception state, the
-//       variant in the valueless_by_exception state is less than the variant
-//       that is not in the valueless_by_exception state.
-//
-// Note: The value 1 is added to each index in the relational comparisons such
-// that the index corresponding to the valueless_by_exception state wraps around
-// to 0 (the lowest value for the index type), and the remaining indices stay in
-// the same relative order.
-
-// Equal-to operator
-template <typename... Types>
-constexpr variant_internal::RequireAllHaveEqualT<Types...> operator==(
-    const variant<Types...>& a, const variant<Types...>& b) {
-  return (a.index() == b.index()) &&
-         variant_internal::VisitIndices<sizeof...(Types)>::Run(
-             variant_internal::EqualsOp<Types...>{&a, &b}, a.index());
-}
-
-// Not equal operator
-template <typename... Types>
-constexpr variant_internal::RequireAllHaveNotEqualT<Types...> operator!=(
-    const variant<Types...>& a, const variant<Types...>& b) {
-  return (a.index() != b.index()) ||
-         variant_internal::VisitIndices<sizeof...(Types)>::Run(
-             variant_internal::NotEqualsOp<Types...>{&a, &b}, a.index());
-}
-
-// Less-than operator
-template <typename... Types>
-constexpr variant_internal::RequireAllHaveLessThanT<Types...> operator<(
-    const variant<Types...>& a, const variant<Types...>& b) {
-  return (a.index() != b.index())
-             ? (a.index() + 1) < (b.index() + 1)
-             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
-                   variant_internal::LessThanOp<Types...>{&a, &b}, a.index());
-}
-
-// Greater-than operator
-template <typename... Types>
-constexpr variant_internal::RequireAllHaveGreaterThanT<Types...> operator>(
-    const variant<Types...>& a, const variant<Types...>& b) {
-  return (a.index() != b.index())
-             ? (a.index() + 1) > (b.index() + 1)
-             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
-                   variant_internal::GreaterThanOp<Types...>{&a, &b},
-                   a.index());
-}
-
-// Less-than or equal-to operator
-template <typename... Types>
-constexpr variant_internal::RequireAllHaveLessThanOrEqualT<Types...> operator<=(
-    const variant<Types...>& a, const variant<Types...>& b) {
-  return (a.index() != b.index())
-             ? (a.index() + 1) < (b.index() + 1)
-             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
-                   variant_internal::LessThanOrEqualsOp<Types...>{&a, &b},
-                   a.index());
-}
-
-// Greater-than or equal-to operator
-template <typename... Types>
-constexpr variant_internal::RequireAllHaveGreaterThanOrEqualT<Types...>
-operator>=(const variant<Types...>& a, const variant<Types...>& b) {
-  return (a.index() != b.index())
-             ? (a.index() + 1) > (b.index() + 1)
-             : variant_internal::VisitIndices<sizeof...(Types)>::Run(
-                   variant_internal::GreaterThanOrEqualsOp<Types...>{&a, &b},
-                   a.index());
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-namespace std {
-
-// hash()
-template <>  // NOLINT
-struct hash<absl::monostate> {
-  std::size_t operator()(absl::monostate) const { return 0; }
-};
-
-template <class... T>  // NOLINT
-struct hash<absl::variant<T...>>
-    : absl::variant_internal::VariantHashBase<absl::variant<T...>, void,
-                                              absl::remove_const_t<T>...> {};
-
-}  // namespace std
-
-#endif  // ABSL_USES_STD_VARIANT
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace variant_internal {
-
-// Helper visitor for converting a variant<Ts...>` into another type (mostly
-// variant) that can be constructed from any type.
-template <typename To>
-struct ConversionVisitor {
-  template <typename T>
-  To operator()(T&& v) const {
-    return To(std::forward<T>(v));
-  }
-};
-
-}  // namespace variant_internal
-
-// ConvertVariantTo()
-//
-// Helper functions to convert an `absl::variant` to a variant of another set of
-// types, provided that the alternative type of the new variant type can be
-// converted from any type in the source variant.
-//
-// Example:
-//
-//   absl::variant<name1, name2, float> InternalReq(const Req&);
-//
-//   // name1 and name2 are convertible to name
-//   absl::variant<name, float> ExternalReq(const Req& req) {
-//     return absl::ConvertVariantTo<absl::variant<name, float>>(
-//              InternalReq(req));
-//   }
-template <typename To, typename Variant>
-To ConvertVariantTo(Variant&& variant) {
-  return absl::visit(variant_internal::ConversionVisitor<To>{},
-                     std::forward<Variant>(variant));
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_TYPES_VARIANT_H_
diff --git a/third_party/abseil_cpp/absl/types/variant_benchmark.cc b/third_party/abseil_cpp/absl/types/variant_benchmark.cc
deleted file mode 100644
index 350b175364..0000000000
--- a/third_party/abseil_cpp/absl/types/variant_benchmark.cc
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Unit tests for the variant template. The 'is' and 'IsEmpty' methods
-// of variant are not explicitly tested because they are used repeatedly
-// in building other tests. All other public variant methods should have
-// explicit tests.
-
-#include "absl/types/variant.h"
-
-#include <cstddef>
-#include <cstdlib>
-#include <string>
-#include <tuple>
-
-#include "benchmark/benchmark.h"
-#include "absl/utility/utility.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-template <std::size_t I>
-struct VariantAlternative {
-  char member;
-};
-
-template <class Indices>
-struct VariantOfAlternativesImpl;
-
-template <std::size_t... Indices>
-struct VariantOfAlternativesImpl<absl::index_sequence<Indices...>> {
-  using type = absl::variant<VariantAlternative<Indices>...>;
-};
-
-template <std::size_t NumAlternatives>
-using VariantOfAlternatives = typename VariantOfAlternativesImpl<
-    absl::make_index_sequence<NumAlternatives>>::type;
-
-struct Empty {};
-
-template <class... T>
-void Ignore(T...) noexcept {}
-
-template <class T>
-Empty DoNotOptimizeAndReturnEmpty(T&& arg) noexcept {
-  benchmark::DoNotOptimize(arg);
-  return {};
-}
-
-struct VisitorApplier {
-  struct Visitor {
-    template <class... T>
-    void operator()(T&&... args) const noexcept {
-      Ignore(DoNotOptimizeAndReturnEmpty(args)...);
-    }
-  };
-
-  template <class... Vars>
-  void operator()(const Vars&... vars) const noexcept {
-    absl::visit(Visitor(), vars...);
-  }
-};
-
-template <std::size_t NumIndices, std::size_t CurrIndex = NumIndices - 1>
-struct MakeWithIndex {
-  using Variant = VariantOfAlternatives<NumIndices>;
-
-  static Variant Run(std::size_t index) {
-    return index == CurrIndex
-               ? Variant(absl::in_place_index_t<CurrIndex>())
-               : MakeWithIndex<NumIndices, CurrIndex - 1>::Run(index);
-  }
-};
-
-template <std::size_t NumIndices>
-struct MakeWithIndex<NumIndices, 0> {
-  using Variant = VariantOfAlternatives<NumIndices>;
-
-  static Variant Run(std::size_t /*index*/) { return Variant(); }
-};
-
-template <std::size_t NumIndices, class Dimensions>
-struct MakeVariantTuple;
-
-template <class T, std::size_t /*I*/>
-using always_t = T;
-
-template <std::size_t NumIndices>
-VariantOfAlternatives<NumIndices> MakeVariant(std::size_t dimension,
-                                              std::size_t index) {
-  return dimension == 0
-             ? MakeWithIndex<NumIndices>::Run(index % NumIndices)
-             : MakeVariant<NumIndices>(dimension - 1, index / NumIndices);
-}
-
-template <std::size_t NumIndices, std::size_t... Dimensions>
-struct MakeVariantTuple<NumIndices, absl::index_sequence<Dimensions...>> {
-  using VariantTuple =
-      std::tuple<always_t<VariantOfAlternatives<NumIndices>, Dimensions>...>;
-
-  static VariantTuple Run(int index) {
-    return std::make_tuple(MakeVariant<NumIndices>(Dimensions, index)...);
-  }
-};
-
-constexpr std::size_t integral_pow(std::size_t base, std::size_t power) {
-  return power == 0 ? 1 : base * integral_pow(base, power - 1);
-}
-
-template <std::size_t End, std::size_t I = 0>
-struct VisitTestBody {
-  template <class Vars, class State>
-  static bool Run(Vars& vars, State& state) {
-    if (state.KeepRunning()) {
-      absl::apply(VisitorApplier(), vars[I]);
-      return VisitTestBody<End, I + 1>::Run(vars, state);
-    }
-    return false;
-  }
-};
-
-template <std::size_t End>
-struct VisitTestBody<End, End> {
-  template <class Vars, class State>
-  static bool Run(Vars& /*vars*/, State& /*state*/) {
-    return true;
-  }
-};
-
-// Visit operations where branch prediction is likely to give a boost.
-template <std::size_t NumIndices, std::size_t NumDimensions = 1>
-void BM_RedundantVisit(benchmark::State& state) {
-  auto vars =
-      MakeVariantTuple<NumIndices, absl::make_index_sequence<NumDimensions>>::
-          Run(static_cast<std::size_t>(state.range(0)));
-
-  for (auto _ : state) {  // NOLINT
-    benchmark::DoNotOptimize(vars);
-    absl::apply(VisitorApplier(), vars);
-  }
-}
-
-// Visit operations where branch prediction is unlikely to give a boost.
-template <std::size_t NumIndices, std::size_t NumDimensions = 1>
-void BM_Visit(benchmark::State& state) {
-  constexpr std::size_t num_possibilities =
-      integral_pow(NumIndices, NumDimensions);
-
-  using VariantTupleMaker =
-      MakeVariantTuple<NumIndices, absl::make_index_sequence<NumDimensions>>;
-  using Tuple = typename VariantTupleMaker::VariantTuple;
-
-  Tuple vars[num_possibilities];
-  for (std::size_t i = 0; i < num_possibilities; ++i)
-    vars[i] = VariantTupleMaker::Run(i);
-
-  while (VisitTestBody<num_possibilities>::Run(vars, state)) {
-  }
-}
-
-// Visitation
-//   Each visit is on a different variant with a different active alternative)
-
-// Unary visit
-BENCHMARK_TEMPLATE(BM_Visit, 1);
-BENCHMARK_TEMPLATE(BM_Visit, 2);
-BENCHMARK_TEMPLATE(BM_Visit, 3);
-BENCHMARK_TEMPLATE(BM_Visit, 4);
-BENCHMARK_TEMPLATE(BM_Visit, 5);
-BENCHMARK_TEMPLATE(BM_Visit, 6);
-BENCHMARK_TEMPLATE(BM_Visit, 7);
-BENCHMARK_TEMPLATE(BM_Visit, 8);
-BENCHMARK_TEMPLATE(BM_Visit, 16);
-BENCHMARK_TEMPLATE(BM_Visit, 32);
-BENCHMARK_TEMPLATE(BM_Visit, 64);
-
-// Binary visit
-BENCHMARK_TEMPLATE(BM_Visit, 1, 2);
-BENCHMARK_TEMPLATE(BM_Visit, 2, 2);
-BENCHMARK_TEMPLATE(BM_Visit, 3, 2);
-BENCHMARK_TEMPLATE(BM_Visit, 4, 2);
-BENCHMARK_TEMPLATE(BM_Visit, 5, 2);
-
-// Ternary visit
-BENCHMARK_TEMPLATE(BM_Visit, 1, 3);
-BENCHMARK_TEMPLATE(BM_Visit, 2, 3);
-BENCHMARK_TEMPLATE(BM_Visit, 3, 3);
-
-// Quaternary visit
-BENCHMARK_TEMPLATE(BM_Visit, 1, 4);
-BENCHMARK_TEMPLATE(BM_Visit, 2, 4);
-
-// Redundant Visitation
-//   Each visit consistently has the same alternative active
-
-// Unary visit
-BENCHMARK_TEMPLATE(BM_RedundantVisit, 1)->Arg(0);
-BENCHMARK_TEMPLATE(BM_RedundantVisit, 2)->DenseRange(0, 1);
-BENCHMARK_TEMPLATE(BM_RedundantVisit, 8)->DenseRange(0, 7);
-
-// Binary visit
-BENCHMARK_TEMPLATE(BM_RedundantVisit, 1, 2)->Arg(0);
-BENCHMARK_TEMPLATE(BM_RedundantVisit, 2, 2)
-    ->DenseRange(0, integral_pow(2, 2) - 1);
-BENCHMARK_TEMPLATE(BM_RedundantVisit, 4, 2)
-    ->DenseRange(0, integral_pow(4, 2) - 1);
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
diff --git a/third_party/abseil_cpp/absl/types/variant_exception_safety_test.cc b/third_party/abseil_cpp/absl/types/variant_exception_safety_test.cc
deleted file mode 100644
index 439c6e1df3..0000000000
--- a/third_party/abseil_cpp/absl/types/variant_exception_safety_test.cc
+++ /dev/null
@@ -1,532 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/types/variant.h"
-
-#include "absl/base/config.h"
-
-// This test is a no-op when absl::variant is an alias for std::variant and when
-// exceptions are not enabled.
-#if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS)
-
-#include <iostream>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/internal/exception_safety_testing.h"
-#include "absl/memory/memory.h"
-
-// See comment in absl/base/config.h
-#if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-using ::testing::MakeExceptionSafetyTester;
-using ::testing::strong_guarantee;
-using ::testing::TestNothrowOp;
-using ::testing::TestThrowingCtor;
-
-using Thrower = testing::ThrowingValue<>;
-using CopyNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowCopy>;
-using MoveNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
-using ThrowingAlloc = testing::ThrowingAllocator<Thrower>;
-using ThrowerVec = std::vector<Thrower, ThrowingAlloc>;
-using ThrowingVariant =
-    absl::variant<Thrower, CopyNothrow, MoveNothrow, ThrowerVec>;
-
-struct ConversionException {};
-
-template <class T>
-struct ExceptionOnConversion {
-  operator T() const {  // NOLINT
-    throw ConversionException();
-  }
-};
-
-// Forces a variant into the valueless by exception state.
-void ToValuelessByException(ThrowingVariant& v) {  // NOLINT
-  try {
-    v.emplace<Thrower>();
-    v.emplace<Thrower>(ExceptionOnConversion<Thrower>());
-  } catch (const ConversionException&) {
-    // This space intentionally left blank.
-  }
-}
-
-// Check that variant is still in a usable state after an exception is thrown.
-testing::AssertionResult VariantInvariants(ThrowingVariant* v) {
-  using testing::AssertionFailure;
-  using testing::AssertionSuccess;
-
-  // Try using the active alternative
-  if (absl::holds_alternative<Thrower>(*v)) {
-    auto& t = absl::get<Thrower>(*v);
-    t = Thrower{-100};
-    if (t.Get() != -100) {
-      return AssertionFailure() << "Thrower should be assigned -100";
-    }
-  } else if (absl::holds_alternative<ThrowerVec>(*v)) {
-    auto& tv = absl::get<ThrowerVec>(*v);
-    tv.clear();
-    tv.emplace_back(-100);
-    if (tv.size() != 1 || tv[0].Get() != -100) {
-      return AssertionFailure() << "ThrowerVec should be {Thrower{-100}}";
-    }
-  } else if (absl::holds_alternative<CopyNothrow>(*v)) {
-    auto& t = absl::get<CopyNothrow>(*v);
-    t = CopyNothrow{-100};
-    if (t.Get() != -100) {
-      return AssertionFailure() << "CopyNothrow should be assigned -100";
-    }
-  } else if (absl::holds_alternative<MoveNothrow>(*v)) {
-    auto& t = absl::get<MoveNothrow>(*v);
-    t = MoveNothrow{-100};
-    if (t.Get() != -100) {
-      return AssertionFailure() << "MoveNothrow should be assigned -100";
-    }
-  }
-
-  // Try making variant valueless_by_exception
-  if (!v->valueless_by_exception()) ToValuelessByException(*v);
-  if (!v->valueless_by_exception()) {
-    return AssertionFailure() << "Variant should be valueless_by_exception";
-  }
-  try {
-    auto unused = absl::get<Thrower>(*v);
-    static_cast<void>(unused);
-    return AssertionFailure() << "Variant should not contain Thrower";
-  } catch (const absl::bad_variant_access&) {
-  } catch (...) {
-    return AssertionFailure() << "Unexpected exception throw from absl::get";
-  }
-
-  // Try using the variant
-  v->emplace<Thrower>(100);
-  if (!absl::holds_alternative<Thrower>(*v) ||
-      absl::get<Thrower>(*v) != Thrower(100)) {
-    return AssertionFailure() << "Variant should contain Thrower(100)";
-  }
-  v->emplace<ThrowerVec>({Thrower(100)});
-  if (!absl::holds_alternative<ThrowerVec>(*v) ||
-      absl::get<ThrowerVec>(*v)[0] != Thrower(100)) {
-    return AssertionFailure()
-           << "Variant should contain ThrowerVec{Thrower(100)}";
-  }
-  return AssertionSuccess();
-}
-
-template <typename... Args>
-Thrower ExpectedThrower(Args&&... args) {
-  return Thrower(42, args...);
-}
-
-ThrowerVec ExpectedThrowerVec() { return {Thrower(100), Thrower(200)}; }
-ThrowingVariant ValuelessByException() {
-  ThrowingVariant v;
-  ToValuelessByException(v);
-  return v;
-}
-ThrowingVariant WithThrower() { return Thrower(39); }
-ThrowingVariant WithThrowerVec() {
-  return ThrowerVec{Thrower(1), Thrower(2), Thrower(3)};
-}
-ThrowingVariant WithCopyNoThrow() { return CopyNothrow(39); }
-ThrowingVariant WithMoveNoThrow() { return MoveNothrow(39); }
-
-TEST(VariantExceptionSafetyTest, DefaultConstructor) {
-  TestThrowingCtor<ThrowingVariant>();
-}
-
-TEST(VariantExceptionSafetyTest, CopyConstructor) {
-  {
-    ThrowingVariant v(ExpectedThrower());
-    TestThrowingCtor<ThrowingVariant>(v);
-  }
-  {
-    ThrowingVariant v(ExpectedThrowerVec());
-    TestThrowingCtor<ThrowingVariant>(v);
-  }
-  {
-    ThrowingVariant v(ValuelessByException());
-    TestThrowingCtor<ThrowingVariant>(v);
-  }
-}
-
-TEST(VariantExceptionSafetyTest, MoveConstructor) {
-  {
-    ThrowingVariant v(ExpectedThrower());
-    TestThrowingCtor<ThrowingVariant>(std::move(v));
-  }
-  {
-    ThrowingVariant v(ExpectedThrowerVec());
-    TestThrowingCtor<ThrowingVariant>(std::move(v));
-  }
-  {
-    ThrowingVariant v(ValuelessByException());
-    TestThrowingCtor<ThrowingVariant>(std::move(v));
-  }
-}
-
-TEST(VariantExceptionSafetyTest, ValueConstructor) {
-  TestThrowingCtor<ThrowingVariant>(ExpectedThrower());
-  TestThrowingCtor<ThrowingVariant>(ExpectedThrowerVec());
-}
-
-TEST(VariantExceptionSafetyTest, InPlaceTypeConstructor) {
-  TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<Thrower>{},
-                                    ExpectedThrower());
-  TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<ThrowerVec>{},
-                                    ExpectedThrowerVec());
-}
-
-TEST(VariantExceptionSafetyTest, InPlaceIndexConstructor) {
-  TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<0>{},
-                                    ExpectedThrower());
-  TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<3>{},
-                                    ExpectedThrowerVec());
-}
-
-TEST(VariantExceptionSafetyTest, CopyAssign) {
-  // variant& operator=(const variant& rhs);
-  // Let j be rhs.index()
-  {
-    // - neither *this nor rhs holds a value
-    const ThrowingVariant rhs = ValuelessByException();
-    ThrowingVariant lhs = ValuelessByException();
-    EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; }));
-  }
-  {
-    // - *this holds a value but rhs does not
-    const ThrowingVariant rhs = ValuelessByException();
-    ThrowingVariant lhs = WithThrower();
-    EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; }));
-  }
-  // - index() == j
-  {
-    const ThrowingVariant rhs(ExpectedThrower());
-    auto tester =
-        MakeExceptionSafetyTester()
-            .WithInitialValue(WithThrower())
-            .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
-    EXPECT_TRUE(tester.WithContracts(VariantInvariants).Test());
-    EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
-  }
-  {
-    const ThrowingVariant rhs(ExpectedThrowerVec());
-    auto tester =
-        MakeExceptionSafetyTester()
-            .WithInitialValue(WithThrowerVec())
-            .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
-    EXPECT_TRUE(tester.WithContracts(VariantInvariants).Test());
-    EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
-  }
-  // libstdc++ std::variant has bugs on copy assignment regarding exception
-  // safety.
-#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
-  // index() != j
-  // if is_nothrow_copy_constructible_v<Tj> or
-  // !is_nothrow_move_constructible<Tj> is true, equivalent to
-  // emplace<j>(get<j>(rhs))
-  {
-    // is_nothrow_copy_constructible_v<Tj> == true
-    // should not throw because emplace() invokes Tj's copy ctor
-    // which should not throw.
-    const ThrowingVariant rhs(CopyNothrow{});
-    ThrowingVariant lhs = WithThrower();
-    EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; }));
-  }
-  {
-    // is_nothrow_copy_constructible<Tj> == false &&
-    // is_nothrow_move_constructible<Tj> == false
-    // should provide basic guarantee because emplace() invokes Tj's copy ctor
-    // which may throw.
-    const ThrowingVariant rhs(ExpectedThrower());
-    auto tester =
-        MakeExceptionSafetyTester()
-            .WithInitialValue(WithCopyNoThrow())
-            .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
-    EXPECT_TRUE(tester
-                    .WithContracts(VariantInvariants,
-                                   [](ThrowingVariant* lhs) {
-                                     return lhs->valueless_by_exception();
-                                   })
-                    .Test());
-    EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
-  }
-#endif  // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
-  {
-    // is_nothrow_copy_constructible_v<Tj> == false &&
-    // is_nothrow_move_constructible_v<Tj> == true
-    // should provide strong guarantee because it is equivalent to
-    // operator=(variant(rhs)) which creates a temporary then invoke the move
-    // ctor which shouldn't throw.
-    const ThrowingVariant rhs(MoveNothrow{});
-    EXPECT_TRUE(MakeExceptionSafetyTester()
-                    .WithInitialValue(WithThrower())
-                    .WithContracts(VariantInvariants, strong_guarantee)
-                    .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
-  }
-}
-
-TEST(VariantExceptionSafetyTest, MoveAssign) {
-  // variant& operator=(variant&& rhs);
-  // Let j be rhs.index()
-  {
-    // - neither *this nor rhs holds a value
-    ThrowingVariant rhs = ValuelessByException();
-    ThrowingVariant lhs = ValuelessByException();
-    EXPECT_TRUE(TestNothrowOp([&]() { lhs = std::move(rhs); }));
-  }
-  {
-    // - *this holds a value but rhs does not
-    ThrowingVariant rhs = ValuelessByException();
-    ThrowingVariant lhs = WithThrower();
-    EXPECT_TRUE(TestNothrowOp([&]() { lhs = std::move(rhs); }));
-  }
-  {
-    // - index() == j
-    // assign get<j>(std::move(rhs)) to the value contained in *this.
-    // If an exception is thrown during call to Tj's move assignment, the state
-    // of the contained value is as defined by the exception safety guarantee of
-    // Tj's move assignment; index() will be j.
-    ThrowingVariant rhs(ExpectedThrower());
-    size_t j = rhs.index();
-    // Since Thrower's move assignment has basic guarantee, so should variant's.
-    auto tester = MakeExceptionSafetyTester()
-                      .WithInitialValue(WithThrower())
-                      .WithOperation([&](ThrowingVariant* lhs) {
-                        auto copy = rhs;
-                        *lhs = std::move(copy);
-                      });
-    EXPECT_TRUE(tester
-                    .WithContracts(
-                        VariantInvariants,
-                        [&](ThrowingVariant* lhs) { return lhs->index() == j; })
-                    .Test());
-    EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
-  }
-  {
-    // libstdc++ introduced a regression between 2018-09-25 and 2019-01-06.
-    // The fix is targeted for gcc-9.
-    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87431#c7
-    // https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=267614
-#if !(defined(ABSL_USES_STD_VARIANT) && \
-      defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8)
-    // - otherwise (index() != j), equivalent to
-    // emplace<j>(get<j>(std::move(rhs)))
-    // - If an exception is thrown during the call to Tj's move construction
-    // (with j being rhs.index()), the variant will hold no value.
-    ThrowingVariant rhs(CopyNothrow{});
-    EXPECT_TRUE(MakeExceptionSafetyTester()
-                    .WithInitialValue(WithThrower())
-                    .WithContracts(VariantInvariants,
-                                   [](ThrowingVariant* lhs) {
-                                     return lhs->valueless_by_exception();
-                                   })
-                    .Test([&](ThrowingVariant* lhs) {
-                      auto copy = rhs;
-                      *lhs = std::move(copy);
-                    }));
-#endif  // !(defined(ABSL_USES_STD_VARIANT) &&
-        //   defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8)
-  }
-}
-
-TEST(VariantExceptionSafetyTest, ValueAssign) {
-  // template<class T> variant& operator=(T&& t);
-  // Let Tj be the type that is selected by overload resolution to be assigned.
-  {
-    // If *this holds a Tj, assigns std::forward<T>(t) to the value contained in
-    // *this. If  an exception is thrown during the assignment of
-    // std::forward<T>(t) to the value contained in *this, the state of the
-    // contained value and t are as defined by the exception safety guarantee of
-    // the assignment expression; valueless_by_exception() will be false.
-    // Since Thrower's copy/move assignment has basic guarantee, so should
-    // variant's.
-    Thrower rhs = ExpectedThrower();
-    // copy assign
-    auto copy_tester =
-        MakeExceptionSafetyTester()
-            .WithInitialValue(WithThrower())
-            .WithOperation([rhs](ThrowingVariant* lhs) { *lhs = rhs; });
-    EXPECT_TRUE(copy_tester
-                    .WithContracts(VariantInvariants,
-                                   [](ThrowingVariant* lhs) {
-                                     return !lhs->valueless_by_exception();
-                                   })
-                    .Test());
-    EXPECT_FALSE(copy_tester.WithContracts(strong_guarantee).Test());
-    // move assign
-    auto move_tester = MakeExceptionSafetyTester()
-                           .WithInitialValue(WithThrower())
-                           .WithOperation([&](ThrowingVariant* lhs) {
-                             auto copy = rhs;
-                             *lhs = std::move(copy);
-                           });
-    EXPECT_TRUE(move_tester
-                    .WithContracts(VariantInvariants,
-                                   [](ThrowingVariant* lhs) {
-                                     return !lhs->valueless_by_exception();
-                                   })
-                    .Test());
-
-    EXPECT_FALSE(move_tester.WithContracts(strong_guarantee).Test());
-  }
-  // Otherwise (*this holds something else), if is_nothrow_constructible_v<Tj,
-  // T> || !is_nothrow_move_constructible_v<Tj> is true, equivalent to
-  // emplace<j>(std::forward<T>(t)).
-  // We simplify the test by letting T = `const Tj&`  or `Tj&&`, so we can reuse
-  // the CopyNothrow and MoveNothrow types.
-
-  // if is_nothrow_constructible_v<Tj, T>
-  // (i.e. is_nothrow_copy/move_constructible_v<Tj>) is true, emplace() just
-  // invokes the copy/move constructor and it should not throw.
-  {
-    const CopyNothrow rhs;
-    ThrowingVariant lhs = WithThrower();
-    EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; }));
-  }
-  {
-    MoveNothrow rhs;
-    ThrowingVariant lhs = WithThrower();
-    EXPECT_TRUE(TestNothrowOp([&]() { lhs = std::move(rhs); }));
-  }
-  // if is_nothrow_constructible_v<Tj, T> == false &&
-  // is_nothrow_move_constructible<Tj> == false
-  // emplace() invokes the copy/move constructor which may throw so it should
-  // provide basic guarantee and variant object might not hold a value.
-  {
-    Thrower rhs = ExpectedThrower();
-    // copy
-    auto copy_tester =
-        MakeExceptionSafetyTester()
-            .WithInitialValue(WithCopyNoThrow())
-            .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
-    EXPECT_TRUE(copy_tester
-                    .WithContracts(VariantInvariants,
-                                   [](ThrowingVariant* lhs) {
-                                     return lhs->valueless_by_exception();
-                                   })
-                    .Test());
-    EXPECT_FALSE(copy_tester.WithContracts(strong_guarantee).Test());
-    // move
-    auto move_tester = MakeExceptionSafetyTester()
-                           .WithInitialValue(WithCopyNoThrow())
-                           .WithOperation([](ThrowingVariant* lhs) {
-                             *lhs = ExpectedThrower(testing::nothrow_ctor);
-                           });
-    EXPECT_TRUE(move_tester
-                    .WithContracts(VariantInvariants,
-                                   [](ThrowingVariant* lhs) {
-                                     return lhs->valueless_by_exception();
-                                   })
-                    .Test());
-    EXPECT_FALSE(move_tester.WithContracts(strong_guarantee).Test());
-  }
-  // Otherwise (if is_nothrow_constructible_v<Tj, T> == false &&
-  // is_nothrow_move_constructible<Tj> == true),
-  // equivalent to operator=(variant(std::forward<T>(t)))
-  // This should have strong guarantee because it creates a temporary variant
-  // and operator=(variant&&) invokes Tj's move ctor which doesn't throw.
-  // libstdc++ std::variant has bugs on conversion assignment regarding
-  // exception safety.
-#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
-  {
-    MoveNothrow rhs;
-    EXPECT_TRUE(MakeExceptionSafetyTester()
-                    .WithInitialValue(WithThrower())
-                    .WithContracts(VariantInvariants, strong_guarantee)
-                    .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
-  }
-#endif  // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
-}
-
-TEST(VariantExceptionSafetyTest, Emplace) {
-  // If an exception during the initialization of the contained value, the
-  // variant might not hold a value. The standard requires emplace() to provide
-  // only basic guarantee.
-  {
-    Thrower args = ExpectedThrower();
-    auto tester = MakeExceptionSafetyTester()
-                      .WithInitialValue(WithThrower())
-                      .WithOperation([&args](ThrowingVariant* v) {
-                        v->emplace<Thrower>(args);
-                      });
-    EXPECT_TRUE(tester
-                    .WithContracts(VariantInvariants,
-                                   [](ThrowingVariant* v) {
-                                     return v->valueless_by_exception();
-                                   })
-                    .Test());
-    EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
-  }
-}
-
-TEST(VariantExceptionSafetyTest, Swap) {
-  // if both are valueless_by_exception(), no effect
-  {
-    ThrowingVariant rhs = ValuelessByException();
-    ThrowingVariant lhs = ValuelessByException();
-    EXPECT_TRUE(TestNothrowOp([&]() { lhs.swap(rhs); }));
-  }
-  // if index() == rhs.index(), calls swap(get<i>(*this), get<i>(rhs))
-  // where i is index().
-  {
-    ThrowingVariant rhs = ExpectedThrower();
-    EXPECT_TRUE(MakeExceptionSafetyTester()
-                    .WithInitialValue(WithThrower())
-                    .WithContracts(VariantInvariants)
-                    .Test([&](ThrowingVariant* lhs) {
-                      auto copy = rhs;
-                      lhs->swap(copy);
-                    }));
-  }
-  // Otherwise, exchanges the value of rhs and *this. The exception safety
-  // involves variant in moved-from state which is not specified in the
-  // standard, and since swap is 3-step it's impossible for it to provide a
-  // overall strong guarantee. So, we are only checking basic guarantee here.
-  {
-    ThrowingVariant rhs = ExpectedThrower();
-    EXPECT_TRUE(MakeExceptionSafetyTester()
-                    .WithInitialValue(WithCopyNoThrow())
-                    .WithContracts(VariantInvariants)
-                    .Test([&](ThrowingVariant* lhs) {
-                      auto copy = rhs;
-                      lhs->swap(copy);
-                    }));
-  }
-  {
-    ThrowingVariant rhs = ExpectedThrower();
-    EXPECT_TRUE(MakeExceptionSafetyTester()
-                    .WithInitialValue(WithCopyNoThrow())
-                    .WithContracts(VariantInvariants)
-                    .Test([&](ThrowingVariant* lhs) {
-                      auto copy = rhs;
-                      copy.swap(*lhs);
-                    }));
-  }
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
-
-#endif  // #if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS)
diff --git a/third_party/abseil_cpp/absl/types/variant_test.cc b/third_party/abseil_cpp/absl/types/variant_test.cc
deleted file mode 100644
index cf237334da..0000000000
--- a/third_party/abseil_cpp/absl/types/variant_test.cc
+++ /dev/null
@@ -1,2718 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Unit tests for the variant template. The 'is' and 'IsEmpty' methods
-// of variant are not explicitly tested because they are used repeatedly
-// in building other tests. All other public variant methods should have
-// explicit tests.
-
-#include "absl/types/variant.h"
-
-// This test is a no-op when absl::variant is an alias for std::variant.
-#if !defined(ABSL_USES_STD_VARIANT)
-
-#include <algorithm>
-#include <cstddef>
-#include <functional>
-#include <initializer_list>
-#include <memory>
-#include <ostream>
-#include <queue>
-#include <type_traits>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/config.h"
-#include "absl/base/port.h"
-#include "absl/memory/memory.h"
-#include "absl/meta/type_traits.h"
-#include "absl/strings/string_view.h"
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-#define ABSL_VARIANT_TEST_EXPECT_FAIL(expr, exception_t, text) \
-  EXPECT_THROW(expr, exception_t)
-
-#else
-
-#define ABSL_VARIANT_TEST_EXPECT_FAIL(expr, exception_t, text) \
-  EXPECT_DEATH_IF_SUPPORTED(expr, text)
-
-#endif  // ABSL_HAVE_EXCEPTIONS
-
-#define ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(...)                 \
-  ABSL_VARIANT_TEST_EXPECT_FAIL((void)(__VA_ARGS__), absl::bad_variant_access, \
-                                "Bad variant access")
-
-struct Hashable {};
-
-namespace std {
-template <>
-struct hash<Hashable> {
-  size_t operator()(const Hashable&);
-};
-}  // namespace std
-
-struct NonHashable {};
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace {
-
-using ::testing::DoubleEq;
-using ::testing::Pointee;
-using ::testing::VariantWith;
-
-struct MoveCanThrow {
-  MoveCanThrow() : v(0) {}
-  MoveCanThrow(int v) : v(v) {}  // NOLINT(runtime/explicit)
-  MoveCanThrow(const MoveCanThrow& other) : v(other.v) {}
-  MoveCanThrow& operator=(const MoveCanThrow& /*other*/) { return *this; }
-  int v;
-};
-
-bool operator==(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v == rhs.v; }
-bool operator!=(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v != rhs.v; }
-bool operator<(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v < rhs.v; }
-bool operator<=(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v <= rhs.v; }
-bool operator>=(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v >= rhs.v; }
-bool operator>(MoveCanThrow lhs, MoveCanThrow rhs) { return lhs.v > rhs.v; }
-
-// This helper class allows us to determine if it was swapped with std::swap()
-// or with its friend swap() function.
-struct SpecialSwap {
-  explicit SpecialSwap(int i) : i(i) {}
-  friend void swap(SpecialSwap& a, SpecialSwap& b) {
-    a.special_swap = b.special_swap = true;
-    std::swap(a.i, b.i);
-  }
-  bool operator==(SpecialSwap other) const { return i == other.i; }
-  int i;
-  bool special_swap = false;
-};
-
-struct MoveOnlyWithListConstructor {
-  MoveOnlyWithListConstructor() = default;
-  explicit MoveOnlyWithListConstructor(std::initializer_list<int> /*ilist*/,
-                                       int value)
-      : value(value) {}
-  MoveOnlyWithListConstructor(MoveOnlyWithListConstructor&&) = default;
-  MoveOnlyWithListConstructor& operator=(MoveOnlyWithListConstructor&&) =
-      default;
-
-  int value = 0;
-};
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-struct ConversionException {};
-
-template <class T>
-struct ExceptionOnConversion {
-  operator T() const {  // NOLINT(runtime/explicit)
-    throw ConversionException();
-  }
-};
-
-// Forces a variant into the valueless by exception state.
-template <class H, class... T>
-void ToValuelessByException(absl::variant<H, T...>& v) {  // NOLINT
-  try {
-    v.template emplace<0>(ExceptionOnConversion<H>());
-  } catch (ConversionException& /*e*/) {
-    // This space intentionally left blank.
-  }
-}
-
-#endif  // ABSL_HAVE_EXCEPTIONS
-
-// An indexed sequence of distinct structures holding a single
-// value of type T
-template<typename T, size_t N>
-struct ValueHolder {
-  explicit ValueHolder(const T& x) : value(x) {}
-  typedef T value_type;
-  value_type value;
-  static const size_t kIndex = N;
-};
-template<typename T, size_t N>
-const size_t ValueHolder<T, N>::kIndex;
-
-// The following three functions make ValueHolder compatible with
-// EXPECT_EQ and EXPECT_NE
-template<typename T, size_t N>
-inline bool operator==(const ValueHolder<T, N>& left,
-                       const ValueHolder<T, N>& right) {
-  return left.value == right.value;
-}
-
-template<typename T, size_t N>
-inline bool operator!=(const ValueHolder<T, N>& left,
-                       const ValueHolder<T, N>& right) {
-  return left.value != right.value;
-}
-
-template<typename T, size_t N>
-inline std::ostream& operator<<(
-    std::ostream& stream, const ValueHolder<T, N>& object) {
-  return stream << object.value;
-}
-
-// Makes a variant holding twelve uniquely typed T wrappers.
-template<typename T>
-struct VariantFactory {
-  typedef variant<ValueHolder<T, 1>, ValueHolder<T, 2>, ValueHolder<T, 3>,
-                  ValueHolder<T, 4>>
-      Type;
-};
-
-// A typelist in 1:1 with VariantFactory, to use type driven unit tests.
-typedef ::testing::Types<ValueHolder<size_t, 1>, ValueHolder<size_t, 2>,
-                         ValueHolder<size_t, 3>,
-                         ValueHolder<size_t, 4>> VariantTypes;
-
-// Increments the provided counter pointer in the destructor
-struct IncrementInDtor {
-  explicit IncrementInDtor(int* counter) : counter(counter) {}
-  ~IncrementInDtor() { *counter += 1; }
-  int* counter;
-};
-
-struct IncrementInDtorCopyCanThrow {
-  explicit IncrementInDtorCopyCanThrow(int* counter) : counter(counter) {}
-  IncrementInDtorCopyCanThrow(IncrementInDtorCopyCanThrow&& other) noexcept =
-      default;
-  IncrementInDtorCopyCanThrow(const IncrementInDtorCopyCanThrow& other)
-      : counter(other.counter) {}
-  IncrementInDtorCopyCanThrow& operator=(
-      IncrementInDtorCopyCanThrow&&) noexcept = default;
-  IncrementInDtorCopyCanThrow& operator=(
-      IncrementInDtorCopyCanThrow const& other) {
-    counter = other.counter;
-    return *this;
-  }
-  ~IncrementInDtorCopyCanThrow() { *counter += 1; }
-  int* counter;
-};
-
-// This is defined so operator== for ValueHolder<IncrementInDtor> will
-// return true if two IncrementInDtor objects increment the same
-// counter
-inline bool operator==(const IncrementInDtor& left,
-                       const IncrementInDtor& right) {
-  return left.counter == right.counter;
-}
-
-// This is defined so EXPECT_EQ can work with IncrementInDtor
-inline std::ostream& operator<<(
-    std::ostream& stream, const IncrementInDtor& object) {
-  return stream << object.counter;
-}
-
-// A class that can be copied, but not assigned.
-class CopyNoAssign {
- public:
-  explicit CopyNoAssign(int value) : foo(value) {}
-  CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {}
-  int foo;
- private:
-  const CopyNoAssign& operator=(const CopyNoAssign&);
-};
-
-// A class that can neither be copied nor assigned. We provide
-// overloads for the constructor with up to four parameters so we can
-// test the overloads of variant::emplace.
-class NonCopyable {
- public:
-  NonCopyable()
-      : value(0) {}
-  explicit NonCopyable(int value1)
-      : value(value1) {}
-
-  NonCopyable(int value1, int value2)
-      : value(value1 + value2) {}
-
-  NonCopyable(int value1, int value2, int value3)
-      : value(value1 + value2 + value3) {}
-
-  NonCopyable(int value1, int value2, int value3, int value4)
-      : value(value1 + value2 + value3 + value4) {}
-  NonCopyable(const NonCopyable&) = delete;
-  NonCopyable& operator=(const NonCopyable&) = delete;
-  int value;
-};
-
-// A typed test and typed test case over the VariantTypes typelist,
-// from which we derive a number of tests that will execute for one of
-// each type.
-template <typename T>
-class VariantTypesTest : public ::testing::Test {};
-TYPED_TEST_SUITE(VariantTypesTest, VariantTypes);
-
-////////////////////
-// [variant.ctor] //
-////////////////////
-
-struct NonNoexceptDefaultConstructible {
-  NonNoexceptDefaultConstructible() {}
-  int value = 5;
-};
-
-struct NonDefaultConstructible {
-  NonDefaultConstructible() = delete;
-};
-
-TEST(VariantTest, TestDefaultConstructor) {
-  {
-    using X = variant<int>;
-    constexpr variant<int> x{};
-    ASSERT_FALSE(x.valueless_by_exception());
-    ASSERT_EQ(0, x.index());
-    EXPECT_EQ(0, absl::get<0>(x));
-    EXPECT_TRUE(std::is_nothrow_default_constructible<X>::value);
-  }
-
-  {
-    using X = variant<NonNoexceptDefaultConstructible>;
-    X x{};
-    ASSERT_FALSE(x.valueless_by_exception());
-    ASSERT_EQ(0, x.index());
-    EXPECT_EQ(5, absl::get<0>(x).value);
-    EXPECT_FALSE(std::is_nothrow_default_constructible<X>::value);
-  }
-
-  {
-    using X = variant<int, NonNoexceptDefaultConstructible>;
-    X x{};
-    ASSERT_FALSE(x.valueless_by_exception());
-    ASSERT_EQ(0, x.index());
-    EXPECT_EQ(0, absl::get<0>(x));
-    EXPECT_TRUE(std::is_nothrow_default_constructible<X>::value);
-  }
-
-  {
-    using X = variant<NonNoexceptDefaultConstructible, int>;
-    X x{};
-    ASSERT_FALSE(x.valueless_by_exception());
-    ASSERT_EQ(0, x.index());
-    EXPECT_EQ(5, absl::get<0>(x).value);
-    EXPECT_FALSE(std::is_nothrow_default_constructible<X>::value);
-  }
-  EXPECT_FALSE(
-      std::is_default_constructible<variant<NonDefaultConstructible>>::value);
-  EXPECT_FALSE((std::is_default_constructible<
-                variant<NonDefaultConstructible, int>>::value));
-  EXPECT_TRUE((std::is_default_constructible<
-               variant<int, NonDefaultConstructible>>::value));
-}
-
-// Test that for each slot, copy constructing a variant with that type
-// produces a sensible object that correctly reports its type, and
-// that copies the provided value.
-TYPED_TEST(VariantTypesTest, TestCopyCtor) {
-  typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant;
-  using value_type1 = absl::variant_alternative_t<0, Variant>;
-  using value_type2 = absl::variant_alternative_t<1, Variant>;
-  using value_type3 = absl::variant_alternative_t<2, Variant>;
-  using value_type4 = absl::variant_alternative_t<3, Variant>;
-  const TypeParam value(TypeParam::kIndex);
-  Variant original(value);
-  Variant copied(original);
-  EXPECT_TRUE(absl::holds_alternative<value_type1>(copied) ||
-              TypeParam::kIndex != 1);
-  EXPECT_TRUE(absl::holds_alternative<value_type2>(copied) ||
-              TypeParam::kIndex != 2);
-  EXPECT_TRUE(absl::holds_alternative<value_type3>(copied) ||
-              TypeParam::kIndex != 3);
-  EXPECT_TRUE(absl::holds_alternative<value_type4>(copied) ||
-              TypeParam::kIndex != 4);
-  EXPECT_TRUE((absl::get_if<value_type1>(&original) ==
-               absl::get_if<value_type1>(&copied)) ||
-              TypeParam::kIndex == 1);
-  EXPECT_TRUE((absl::get_if<value_type2>(&original) ==
-               absl::get_if<value_type2>(&copied)) ||
-              TypeParam::kIndex == 2);
-  EXPECT_TRUE((absl::get_if<value_type3>(&original) ==
-               absl::get_if<value_type3>(&copied)) ||
-              TypeParam::kIndex == 3);
-  EXPECT_TRUE((absl::get_if<value_type4>(&original) ==
-               absl::get_if<value_type4>(&copied)) ||
-              TypeParam::kIndex == 4);
-  EXPECT_TRUE((absl::get_if<value_type1>(&original) ==
-               absl::get_if<value_type1>(&copied)) ||
-              TypeParam::kIndex == 1);
-  EXPECT_TRUE((absl::get_if<value_type2>(&original) ==
-               absl::get_if<value_type2>(&copied)) ||
-              TypeParam::kIndex == 2);
-  EXPECT_TRUE((absl::get_if<value_type3>(&original) ==
-               absl::get_if<value_type3>(&copied)) ||
-              TypeParam::kIndex == 3);
-  EXPECT_TRUE((absl::get_if<value_type4>(&original) ==
-               absl::get_if<value_type4>(&copied)) ||
-              TypeParam::kIndex == 4);
-  const TypeParam* ovalptr = absl::get_if<TypeParam>(&original);
-  const TypeParam* cvalptr = absl::get_if<TypeParam>(&copied);
-  ASSERT_TRUE(ovalptr != nullptr);
-  ASSERT_TRUE(cvalptr != nullptr);
-  EXPECT_EQ(*ovalptr, *cvalptr);
-  TypeParam* mutable_ovalptr = absl::get_if<TypeParam>(&original);
-  TypeParam* mutable_cvalptr = absl::get_if<TypeParam>(&copied);
-  ASSERT_TRUE(mutable_ovalptr != nullptr);
-  ASSERT_TRUE(mutable_cvalptr != nullptr);
-  EXPECT_EQ(*mutable_ovalptr, *mutable_cvalptr);
-}
-
-template <class>
-struct MoveOnly {
-  MoveOnly() = default;
-  explicit MoveOnly(int value) : value(value) {}
-  MoveOnly(MoveOnly&&) = default;
-  MoveOnly& operator=(MoveOnly&&) = default;
-  int value = 5;
-};
-
-TEST(VariantTest, TestMoveConstruct) {
-  using V = variant<MoveOnly<class A>, MoveOnly<class B>, MoveOnly<class C>>;
-
-  V v(in_place_index<1>, 10);
-  V v2 = absl::move(v);
-  EXPECT_EQ(10, absl::get<1>(v2).value);
-}
-
-// Used internally to emulate missing triviality traits for tests.
-template <class T>
-union SingleUnion {
-  T member;
-};
-
-// NOTE: These don't work with types that can't be union members.
-//       They are just for testing.
-template <class T>
-struct is_trivially_move_constructible
-    : std::is_move_constructible<SingleUnion<T>>::type {};
-
-template <class T>
-struct is_trivially_move_assignable
-    : absl::is_move_assignable<SingleUnion<T>>::type {};
-
-TEST(VariantTest, NothrowMoveConstructible) {
-  // Verify that variant is nothrow move constructible iff its template
-  // arguments are.
-  using U = std::unique_ptr<int>;
-  struct E {
-    E(E&&) {}
-  };
-  static_assert(std::is_nothrow_move_constructible<variant<U>>::value, "");
-  static_assert(std::is_nothrow_move_constructible<variant<U, int>>::value, "");
-  static_assert(!std::is_nothrow_move_constructible<variant<U, E>>::value, "");
-}
-
-// Test that for each slot, constructing a variant with that type
-// produces a sensible object that correctly reports its type, and
-// that copies the provided value.
-TYPED_TEST(VariantTypesTest, TestValueCtor) {
-  typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant;
-  using value_type1 = absl::variant_alternative_t<0, Variant>;
-  using value_type2 = absl::variant_alternative_t<1, Variant>;
-  using value_type3 = absl::variant_alternative_t<2, Variant>;
-  using value_type4 = absl::variant_alternative_t<3, Variant>;
-  const TypeParam value(TypeParam::kIndex);
-  Variant v(value);
-  EXPECT_TRUE(absl::holds_alternative<value_type1>(v) ||
-              TypeParam::kIndex != 1);
-  EXPECT_TRUE(absl::holds_alternative<value_type2>(v) ||
-              TypeParam::kIndex != 2);
-  EXPECT_TRUE(absl::holds_alternative<value_type3>(v) ||
-              TypeParam::kIndex != 3);
-  EXPECT_TRUE(absl::holds_alternative<value_type4>(v) ||
-              TypeParam::kIndex != 4);
-  EXPECT_TRUE(nullptr != absl::get_if<value_type1>(&v) ||
-              TypeParam::kIndex != 1);
-  EXPECT_TRUE(nullptr != absl::get_if<value_type2>(&v) ||
-              TypeParam::kIndex != 2);
-  EXPECT_TRUE(nullptr != absl::get_if<value_type3>(&v) ||
-              TypeParam::kIndex != 3);
-  EXPECT_TRUE(nullptr != absl::get_if<value_type4>(&v) ||
-              TypeParam::kIndex != 4);
-  EXPECT_TRUE(nullptr != absl::get_if<value_type1>(&v) ||
-              TypeParam::kIndex != 1);
-  EXPECT_TRUE(nullptr != absl::get_if<value_type2>(&v) ||
-              TypeParam::kIndex != 2);
-  EXPECT_TRUE(nullptr != absl::get_if<value_type3>(&v) ||
-              TypeParam::kIndex != 3);
-  EXPECT_TRUE(nullptr != absl::get_if<value_type4>(&v) ||
-              TypeParam::kIndex != 4);
-  const TypeParam* valptr = absl::get_if<TypeParam>(&v);
-  ASSERT_TRUE(nullptr != valptr);
-  EXPECT_EQ(value.value, valptr->value);
-  const TypeParam* mutable_valptr = absl::get_if<TypeParam>(&v);
-  ASSERT_TRUE(nullptr != mutable_valptr);
-  EXPECT_EQ(value.value, mutable_valptr->value);
-}
-
-TEST(VariantTest, AmbiguousValueConstructor) {
-  EXPECT_FALSE((std::is_convertible<int, absl::variant<int, int>>::value));
-  EXPECT_FALSE((std::is_constructible<absl::variant<int, int>, int>::value));
-}
-
-TEST(VariantTest, InPlaceType) {
-  using Var = variant<int, std::string, NonCopyable, std::vector<int>>;
-
-  Var v1(in_place_type_t<int>(), 7);
-  ASSERT_TRUE(absl::holds_alternative<int>(v1));
-  EXPECT_EQ(7, absl::get<int>(v1));
-
-  Var v2(in_place_type_t<std::string>(), "ABC");
-  ASSERT_TRUE(absl::holds_alternative<std::string>(v2));
-  EXPECT_EQ("ABC", absl::get<std::string>(v2));
-
-  Var v3(in_place_type_t<std::string>(), "ABC", 2);
-  ASSERT_TRUE(absl::holds_alternative<std::string>(v3));
-  EXPECT_EQ("AB", absl::get<std::string>(v3));
-
-  Var v4(in_place_type_t<NonCopyable>{});
-  ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v4));
-
-  Var v5(in_place_type_t<std::vector<int>>(), {1, 2, 3});
-  ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5));
-  EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3));
-}
-
-TEST(VariantTest, InPlaceTypeVariableTemplate) {
-  using Var = variant<int, std::string, NonCopyable, std::vector<int>>;
-
-  Var v1(in_place_type<int>, 7);
-  ASSERT_TRUE(absl::holds_alternative<int>(v1));
-  EXPECT_EQ(7, absl::get<int>(v1));
-
-  Var v2(in_place_type<std::string>, "ABC");
-  ASSERT_TRUE(absl::holds_alternative<std::string>(v2));
-  EXPECT_EQ("ABC", absl::get<std::string>(v2));
-
-  Var v3(in_place_type<std::string>, "ABC", 2);
-  ASSERT_TRUE(absl::holds_alternative<std::string>(v3));
-  EXPECT_EQ("AB", absl::get<std::string>(v3));
-
-  Var v4(in_place_type<NonCopyable>);
-  ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v4));
-
-  Var v5(in_place_type<std::vector<int>>, {1, 2, 3});
-  ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5));
-  EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3));
-}
-
-TEST(VariantTest, InPlaceTypeInitializerList) {
-  using Var =
-      variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
-
-  Var v1(in_place_type_t<MoveOnlyWithListConstructor>(), {1, 2, 3, 4, 5}, 6);
-  ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
-  EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
-}
-
-TEST(VariantTest, InPlaceTypeInitializerListVariabletemplate) {
-  using Var =
-      variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
-
-  Var v1(in_place_type<MoveOnlyWithListConstructor>, {1, 2, 3, 4, 5}, 6);
-  ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
-  EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
-}
-
-TEST(VariantTest, InPlaceIndex) {
-  using Var = variant<int, std::string, NonCopyable, std::vector<int>>;
-
-  Var v1(in_place_index_t<0>(), 7);
-  ASSERT_TRUE(absl::holds_alternative<int>(v1));
-  EXPECT_EQ(7, absl::get<int>(v1));
-
-  Var v2(in_place_index_t<1>(), "ABC");
-  ASSERT_TRUE(absl::holds_alternative<std::string>(v2));
-  EXPECT_EQ("ABC", absl::get<std::string>(v2));
-
-  Var v3(in_place_index_t<1>(), "ABC", 2);
-  ASSERT_TRUE(absl::holds_alternative<std::string>(v3));
-  EXPECT_EQ("AB", absl::get<std::string>(v3));
-
-  Var v4(in_place_index_t<2>{});
-  EXPECT_TRUE(absl::holds_alternative<NonCopyable>(v4));
-
-  // Verify that a variant with only non-copyables can still be constructed.
-  EXPECT_TRUE(absl::holds_alternative<NonCopyable>(
-      variant<NonCopyable>(in_place_index_t<0>{})));
-
-  Var v5(in_place_index_t<3>(), {1, 2, 3});
-  ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5));
-  EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3));
-}
-
-TEST(VariantTest, InPlaceIndexVariableTemplate) {
-  using Var = variant<int, std::string, NonCopyable, std::vector<int>>;
-
-  Var v1(in_place_index<0>, 7);
-  ASSERT_TRUE(absl::holds_alternative<int>(v1));
-  EXPECT_EQ(7, absl::get<int>(v1));
-
-  Var v2(in_place_index<1>, "ABC");
-  ASSERT_TRUE(absl::holds_alternative<std::string>(v2));
-  EXPECT_EQ("ABC", absl::get<std::string>(v2));
-
-  Var v3(in_place_index<1>, "ABC", 2);
-  ASSERT_TRUE(absl::holds_alternative<std::string>(v3));
-  EXPECT_EQ("AB", absl::get<std::string>(v3));
-
-  Var v4(in_place_index<2>);
-  EXPECT_TRUE(absl::holds_alternative<NonCopyable>(v4));
-
-  // Verify that a variant with only non-copyables can still be constructed.
-  EXPECT_TRUE(absl::holds_alternative<NonCopyable>(
-      variant<NonCopyable>(in_place_index<0>)));
-
-  Var v5(in_place_index<3>, {1, 2, 3});
-  ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5));
-  EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3));
-}
-
-TEST(VariantTest, InPlaceIndexInitializerList) {
-  using Var =
-      variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
-
-  Var v1(in_place_index_t<3>(), {1, 2, 3, 4, 5}, 6);
-  ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
-  EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
-}
-
-TEST(VariantTest, InPlaceIndexInitializerListVariableTemplate) {
-  using Var =
-      variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
-
-  Var v1(in_place_index<3>, {1, 2, 3, 4, 5}, 6);
-  ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
-  EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
-}
-
-////////////////////
-// [variant.dtor] //
-////////////////////
-
-// Make sure that the destructor destroys the contained value
-TEST(VariantTest, TestDtor) {
-  typedef VariantFactory<IncrementInDtor>::Type Variant;
-  using value_type1 = absl::variant_alternative_t<0, Variant>;
-  using value_type2 = absl::variant_alternative_t<1, Variant>;
-  using value_type3 = absl::variant_alternative_t<2, Variant>;
-  using value_type4 = absl::variant_alternative_t<3, Variant>;
-  int counter = 0;
-  IncrementInDtor counter_adjuster(&counter);
-  EXPECT_EQ(0, counter);
-
-  value_type1 value1(counter_adjuster);
-  { Variant object(value1); }
-  EXPECT_EQ(1, counter);
-
-  value_type2 value2(counter_adjuster);
-  { Variant object(value2); }
-  EXPECT_EQ(2, counter);
-
-  value_type3 value3(counter_adjuster);
-  { Variant object(value3); }
-  EXPECT_EQ(3, counter);
-
-  value_type4 value4(counter_adjuster);
-  { Variant object(value4); }
-  EXPECT_EQ(4, counter);
-}
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-// See comment in absl/base/config.h
-#if defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
-TEST(VariantTest, DISABLED_TestDtorValuelessByException)
-#else
-// Test destruction when in the valueless_by_exception state.
-TEST(VariantTest, TestDtorValuelessByException)
-#endif
-{
-  int counter = 0;
-  IncrementInDtor counter_adjuster(&counter);
-
-  {
-    using Variant = VariantFactory<IncrementInDtor>::Type;
-
-    Variant v(in_place_index<0>, counter_adjuster);
-    EXPECT_EQ(0, counter);
-
-    ToValuelessByException(v);
-    ASSERT_TRUE(v.valueless_by_exception());
-    EXPECT_EQ(1, counter);
-  }
-  EXPECT_EQ(1, counter);
-}
-
-#endif  // ABSL_HAVE_EXCEPTIONS
-
-//////////////////////
-// [variant.assign] //
-//////////////////////
-
-// Test that self-assignment doesn't destroy the current value
-TEST(VariantTest, TestSelfAssignment) {
-  typedef VariantFactory<IncrementInDtor>::Type Variant;
-  int counter = 0;
-  IncrementInDtor counter_adjuster(&counter);
-  absl::variant_alternative_t<0, Variant> value(counter_adjuster);
-  Variant object(value);
-  object.operator=(object);
-  EXPECT_EQ(0, counter);
-
-  // A string long enough that it's likely to defeat any inline representation
-  // optimization.
-  const std::string long_str(128, 'a');
-
-  std::string foo = long_str;
-  foo = *&foo;
-  EXPECT_EQ(long_str, foo);
-
-  variant<int, std::string> so = long_str;
-  ASSERT_EQ(1, so.index());
-  EXPECT_EQ(long_str, absl::get<1>(so));
-  so = *&so;
-
-  ASSERT_EQ(1, so.index());
-  EXPECT_EQ(long_str, absl::get<1>(so));
-}
-
-// Test that assigning a variant<..., T, ...> to a variant<..., T, ...> produces
-// a variant<..., T, ...> with the correct value.
-TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValueSameTypes) {
-  typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant;
-  const TypeParam value(TypeParam::kIndex);
-  const Variant source(value);
-  Variant target(TypeParam(value.value + 1));
-  ASSERT_TRUE(absl::holds_alternative<TypeParam>(source));
-  ASSERT_TRUE(absl::holds_alternative<TypeParam>(target));
-  ASSERT_NE(absl::get<TypeParam>(source), absl::get<TypeParam>(target));
-  target = source;
-  ASSERT_TRUE(absl::holds_alternative<TypeParam>(source));
-  ASSERT_TRUE(absl::holds_alternative<TypeParam>(target));
-  EXPECT_EQ(absl::get<TypeParam>(source), absl::get<TypeParam>(target));
-}
-
-// Test that assisnging a variant<..., T, ...> to a variant<1, ...>
-// produces a variant<..., T, ...> with the correct value.
-TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValuesVaryingSourceType) {
-  typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant;
-  using value_type1 = absl::variant_alternative_t<0, Variant>;
-  const TypeParam value(TypeParam::kIndex);
-  const Variant source(value);
-  ASSERT_TRUE(absl::holds_alternative<TypeParam>(source));
-  Variant target(value_type1(1));
-  ASSERT_TRUE(absl::holds_alternative<value_type1>(target));
-  target = source;
-  EXPECT_TRUE(absl::holds_alternative<TypeParam>(source));
-  EXPECT_TRUE(absl::holds_alternative<TypeParam>(target));
-  EXPECT_EQ(absl::get<TypeParam>(source), absl::get<TypeParam>(target));
-}
-
-// Test that assigning a variant<1, ...> to a variant<..., T, ...>
-// produces a variant<1, ...> with the correct value.
-TYPED_TEST(VariantTypesTest, TestAssignmentCopiesValuesVaryingTargetType) {
-  typedef typename VariantFactory<typename TypeParam::value_type>::Type Variant;
-  using value_type1 = absl::variant_alternative_t<0, Variant>;
-  const Variant source(value_type1(1));
-  ASSERT_TRUE(absl::holds_alternative<value_type1>(source));
-  const TypeParam value(TypeParam::kIndex);
-  Variant target(value);
-  ASSERT_TRUE(absl::holds_alternative<TypeParam>(target));
-  target = source;
-  EXPECT_TRUE(absl::holds_alternative<value_type1>(target));
-  EXPECT_TRUE(absl::holds_alternative<value_type1>(source));
-  EXPECT_EQ(absl::get<value_type1>(source), absl::get<value_type1>(target));
-}
-
-// Test that operator=<T> works, that assigning a new value destroys
-// the old and that assigning the new value again does not redestroy
-// the old
-TEST(VariantTest, TestAssign) {
-  typedef VariantFactory<IncrementInDtor>::Type Variant;
-  using value_type1 = absl::variant_alternative_t<0, Variant>;
-  using value_type2 = absl::variant_alternative_t<1, Variant>;
-  using value_type3 = absl::variant_alternative_t<2, Variant>;
-  using value_type4 = absl::variant_alternative_t<3, Variant>;
-
-  const int kSize = 4;
-  int counter[kSize];
-  std::unique_ptr<IncrementInDtor> counter_adjustor[kSize];
-  for (int i = 0; i != kSize; i++) {
-    counter[i] = 0;
-    counter_adjustor[i] = absl::make_unique<IncrementInDtor>(&counter[i]);
-  }
-
-  value_type1 v1(*counter_adjustor[0]);
-  value_type2 v2(*counter_adjustor[1]);
-  value_type3 v3(*counter_adjustor[2]);
-  value_type4 v4(*counter_adjustor[3]);
-
-  // Test that reassignment causes destruction of old value
-  {
-    Variant object(v1);
-    object = v2;
-    object = v3;
-    object = v4;
-    object = v1;
-  }
-
-  EXPECT_EQ(2, counter[0]);
-  EXPECT_EQ(1, counter[1]);
-  EXPECT_EQ(1, counter[2]);
-  EXPECT_EQ(1, counter[3]);
-
-  std::fill(std::begin(counter), std::end(counter), 0);
-
-  // Test that self-assignment does not cause destruction of old value
-  {
-    Variant object(v1);
-    object.operator=(object);
-    EXPECT_EQ(0, counter[0]);
-  }
-  {
-    Variant object(v2);
-    object.operator=(object);
-    EXPECT_EQ(0, counter[1]);
-  }
-  {
-    Variant object(v3);
-    object.operator=(object);
-    EXPECT_EQ(0, counter[2]);
-  }
-  {
-    Variant object(v4);
-    object.operator=(object);
-    EXPECT_EQ(0, counter[3]);
-  }
-
-  EXPECT_EQ(1, counter[0]);
-  EXPECT_EQ(1, counter[1]);
-  EXPECT_EQ(1, counter[2]);
-  EXPECT_EQ(1, counter[3]);
-}
-
-// This tests that we perform a backup if the copy-assign can throw but the move
-// cannot throw.
-TEST(VariantTest, TestBackupAssign) {
-  typedef VariantFactory<IncrementInDtorCopyCanThrow>::Type Variant;
-  using value_type1 = absl::variant_alternative_t<0, Variant>;
-  using value_type2 = absl::variant_alternative_t<1, Variant>;
-  using value_type3 = absl::variant_alternative_t<2, Variant>;
-  using value_type4 = absl::variant_alternative_t<3, Variant>;
-
-  const int kSize = 4;
-  int counter[kSize];
-  std::unique_ptr<IncrementInDtorCopyCanThrow> counter_adjustor[kSize];
-  for (int i = 0; i != kSize; i++) {
-    counter[i] = 0;
-    counter_adjustor[i].reset(new IncrementInDtorCopyCanThrow(&counter[i]));
-  }
-
-  value_type1 v1(*counter_adjustor[0]);
-  value_type2 v2(*counter_adjustor[1]);
-  value_type3 v3(*counter_adjustor[2]);
-  value_type4 v4(*counter_adjustor[3]);
-
-  // Test that reassignment causes destruction of old value
-  {
-    Variant object(v1);
-    object = v2;
-    object = v3;
-    object = v4;
-    object = v1;
-  }
-
-  // libstdc++ doesn't pass this test
-#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
-  EXPECT_EQ(3, counter[0]);
-  EXPECT_EQ(2, counter[1]);
-  EXPECT_EQ(2, counter[2]);
-  EXPECT_EQ(2, counter[3]);
-#endif
-
-  std::fill(std::begin(counter), std::end(counter), 0);
-
-  // Test that self-assignment does not cause destruction of old value
-  {
-    Variant object(v1);
-    object.operator=(object);
-    EXPECT_EQ(0, counter[0]);
-  }
-  {
-    Variant object(v2);
-    object.operator=(object);
-    EXPECT_EQ(0, counter[1]);
-  }
-  {
-    Variant object(v3);
-    object.operator=(object);
-    EXPECT_EQ(0, counter[2]);
-  }
-  {
-    Variant object(v4);
-    object.operator=(object);
-    EXPECT_EQ(0, counter[3]);
-  }
-
-  EXPECT_EQ(1, counter[0]);
-  EXPECT_EQ(1, counter[1]);
-  EXPECT_EQ(1, counter[2]);
-  EXPECT_EQ(1, counter[3]);
-}
-
-///////////////////
-// [variant.mod] //
-///////////////////
-
-TEST(VariantTest, TestEmplaceBasic) {
-  using Variant = variant<int, char>;
-
-  Variant v(absl::in_place_index<0>, 0);
-
-  {
-    char& emplace_result = v.emplace<char>();
-    ASSERT_TRUE(absl::holds_alternative<char>(v));
-    EXPECT_EQ(absl::get<char>(v), 0);
-    EXPECT_EQ(&emplace_result, &absl::get<char>(v));
-  }
-
-  // Make sure that another emplace does zero-initialization
-  absl::get<char>(v) = 'a';
-  v.emplace<char>('b');
-  ASSERT_TRUE(absl::holds_alternative<char>(v));
-  EXPECT_EQ(absl::get<char>(v), 'b');
-
-  {
-    int& emplace_result = v.emplace<int>();
-    EXPECT_TRUE(absl::holds_alternative<int>(v));
-    EXPECT_EQ(absl::get<int>(v), 0);
-    EXPECT_EQ(&emplace_result, &absl::get<int>(v));
-  }
-}
-
-TEST(VariantTest, TestEmplaceInitializerList) {
-  using Var =
-      variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
-
-  Var v1(absl::in_place_index<0>, 555);
-  MoveOnlyWithListConstructor& emplace_result =
-      v1.emplace<MoveOnlyWithListConstructor>({1, 2, 3, 4, 5}, 6);
-  ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
-  EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
-  EXPECT_EQ(&emplace_result, &absl::get<MoveOnlyWithListConstructor>(v1));
-}
-
-TEST(VariantTest, TestEmplaceIndex) {
-  using Variant = variant<int, char>;
-
-  Variant v(absl::in_place_index<0>, 555);
-
-  {
-    char& emplace_result = v.emplace<1>();
-    ASSERT_TRUE(absl::holds_alternative<char>(v));
-    EXPECT_EQ(absl::get<char>(v), 0);
-    EXPECT_EQ(&emplace_result, &absl::get<char>(v));
-  }
-
-  // Make sure that another emplace does zero-initialization
-  absl::get<char>(v) = 'a';
-  v.emplace<1>('b');
-  ASSERT_TRUE(absl::holds_alternative<char>(v));
-  EXPECT_EQ(absl::get<char>(v), 'b');
-
-  {
-    int& emplace_result = v.emplace<0>();
-    EXPECT_TRUE(absl::holds_alternative<int>(v));
-    EXPECT_EQ(absl::get<int>(v), 0);
-    EXPECT_EQ(&emplace_result, &absl::get<int>(v));
-  }
-}
-
-TEST(VariantTest, TestEmplaceIndexInitializerList) {
-  using Var =
-      variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>;
-
-  Var v1(absl::in_place_index<0>, 555);
-  MoveOnlyWithListConstructor& emplace_result =
-      v1.emplace<3>({1, 2, 3, 4, 5}, 6);
-  ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1));
-  EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value);
-  EXPECT_EQ(&emplace_result, &absl::get<MoveOnlyWithListConstructor>(v1));
-}
-
-//////////////////////
-// [variant.status] //
-//////////////////////
-
-TEST(VariantTest, Index) {
-  using Var = variant<int, std::string, double>;
-
-  Var v = 1;
-  EXPECT_EQ(0, v.index());
-  v = "str";
-  EXPECT_EQ(1, v.index());
-  v = 0.;
-  EXPECT_EQ(2, v.index());
-
-  Var v2 = v;
-  EXPECT_EQ(2, v2.index());
-  v2.emplace<int>(3);
-  EXPECT_EQ(0, v2.index());
-}
-
-TEST(VariantTest, NotValuelessByException) {
-  using Var = variant<int, std::string, double>;
-
-  Var v = 1;
-  EXPECT_FALSE(v.valueless_by_exception());
-  v = "str";
-  EXPECT_FALSE(v.valueless_by_exception());
-  v = 0.;
-  EXPECT_FALSE(v.valueless_by_exception());
-
-  Var v2 = v;
-  EXPECT_FALSE(v.valueless_by_exception());
-  v2.emplace<int>(3);
-  EXPECT_FALSE(v.valueless_by_exception());
-}
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-TEST(VariantTest, IndexValuelessByException) {
-  using Var = variant<MoveCanThrow, std::string, double>;
-
-  Var v(absl::in_place_index<0>);
-  EXPECT_EQ(0, v.index());
-  ToValuelessByException(v);
-  EXPECT_EQ(absl::variant_npos, v.index());
-  v = "str";
-  EXPECT_EQ(1, v.index());
-}
-
-TEST(VariantTest, ValuelessByException) {
-  using Var = variant<MoveCanThrow, std::string, double>;
-
-  Var v(absl::in_place_index<0>);
-  EXPECT_FALSE(v.valueless_by_exception());
-  ToValuelessByException(v);
-  EXPECT_TRUE(v.valueless_by_exception());
-  v = "str";
-  EXPECT_FALSE(v.valueless_by_exception());
-}
-
-#endif  // ABSL_HAVE_EXCEPTIONS
-
-////////////////////
-// [variant.swap] //
-////////////////////
-
-TEST(VariantTest, MemberSwap) {
-  SpecialSwap v1(3);
-  SpecialSwap v2(7);
-
-  variant<SpecialSwap> a = v1, b = v2;
-
-  EXPECT_THAT(a, VariantWith<SpecialSwap>(v1));
-  EXPECT_THAT(b, VariantWith<SpecialSwap>(v2));
-
-  a.swap(b);
-  EXPECT_THAT(a, VariantWith<SpecialSwap>(v2));
-  EXPECT_THAT(b, VariantWith<SpecialSwap>(v1));
-  EXPECT_TRUE(absl::get<SpecialSwap>(a).special_swap);
-
-  using V = variant<MoveCanThrow, std::string, int>;
-  int i = 33;
-  std::string s = "abc";
-  {
-    // lhs and rhs holds different alternative
-    V lhs(i), rhs(s);
-    lhs.swap(rhs);
-    EXPECT_THAT(lhs, VariantWith<std::string>(s));
-    EXPECT_THAT(rhs, VariantWith<int>(i));
-  }
-#ifdef ABSL_HAVE_EXCEPTIONS
-  V valueless(in_place_index<0>);
-  ToValuelessByException(valueless);
-  {
-    // lhs is valueless
-    V lhs(valueless), rhs(i);
-    lhs.swap(rhs);
-    EXPECT_THAT(lhs, VariantWith<int>(i));
-    EXPECT_TRUE(rhs.valueless_by_exception());
-  }
-  {
-    // rhs is valueless
-    V lhs(s), rhs(valueless);
-    lhs.swap(rhs);
-    EXPECT_THAT(rhs, VariantWith<std::string>(s));
-    EXPECT_TRUE(lhs.valueless_by_exception());
-  }
-  {
-    // both are valueless
-    V lhs(valueless), rhs(valueless);
-    lhs.swap(rhs);
-    EXPECT_TRUE(lhs.valueless_by_exception());
-    EXPECT_TRUE(rhs.valueless_by_exception());
-  }
-#endif  // ABSL_HAVE_EXCEPTIONS
-}
-
-//////////////////////
-// [variant.helper] //
-//////////////////////
-
-TEST(VariantTest, VariantSize) {
-  {
-    using Size1Variant = absl::variant<int>;
-    EXPECT_EQ(1, absl::variant_size<Size1Variant>::value);
-    EXPECT_EQ(1, absl::variant_size<const Size1Variant>::value);
-    EXPECT_EQ(1, absl::variant_size<volatile Size1Variant>::value);
-    EXPECT_EQ(1, absl::variant_size<const volatile Size1Variant>::value);
-  }
-
-  {
-    using Size3Variant = absl::variant<int, float, int>;
-    EXPECT_EQ(3, absl::variant_size<Size3Variant>::value);
-    EXPECT_EQ(3, absl::variant_size<const Size3Variant>::value);
-    EXPECT_EQ(3, absl::variant_size<volatile Size3Variant>::value);
-    EXPECT_EQ(3, absl::variant_size<const volatile Size3Variant>::value);
-  }
-}
-
-TEST(VariantTest, VariantAlternative) {
-  {
-    using V = absl::variant<float, int, const char*>;
-    EXPECT_TRUE(
-        (std::is_same<float, absl::variant_alternative_t<0, V>>::value));
-    EXPECT_TRUE((std::is_same<const float,
-                              absl::variant_alternative_t<0, const V>>::value));
-    EXPECT_TRUE(
-        (std::is_same<volatile float,
-                      absl::variant_alternative_t<0, volatile V>>::value));
-    EXPECT_TRUE((
-        std::is_same<const volatile float,
-                     absl::variant_alternative_t<0, const volatile V>>::value));
-
-    EXPECT_TRUE((std::is_same<int, absl::variant_alternative_t<1, V>>::value));
-    EXPECT_TRUE((std::is_same<const int,
-                              absl::variant_alternative_t<1, const V>>::value));
-    EXPECT_TRUE(
-        (std::is_same<volatile int,
-                      absl::variant_alternative_t<1, volatile V>>::value));
-    EXPECT_TRUE((
-        std::is_same<const volatile int,
-                     absl::variant_alternative_t<1, const volatile V>>::value));
-
-    EXPECT_TRUE(
-        (std::is_same<const char*, absl::variant_alternative_t<2, V>>::value));
-    EXPECT_TRUE((std::is_same<const char* const,
-                              absl::variant_alternative_t<2, const V>>::value));
-    EXPECT_TRUE(
-        (std::is_same<const char* volatile,
-                      absl::variant_alternative_t<2, volatile V>>::value));
-    EXPECT_TRUE((
-        std::is_same<const char* const volatile,
-                     absl::variant_alternative_t<2, const volatile V>>::value));
-  }
-
-  {
-    using V = absl::variant<float, volatile int, const char*>;
-    EXPECT_TRUE(
-        (std::is_same<float, absl::variant_alternative_t<0, V>>::value));
-    EXPECT_TRUE((std::is_same<const float,
-                              absl::variant_alternative_t<0, const V>>::value));
-    EXPECT_TRUE(
-        (std::is_same<volatile float,
-                      absl::variant_alternative_t<0, volatile V>>::value));
-    EXPECT_TRUE((
-        std::is_same<const volatile float,
-                     absl::variant_alternative_t<0, const volatile V>>::value));
-
-    EXPECT_TRUE(
-        (std::is_same<volatile int, absl::variant_alternative_t<1, V>>::value));
-    EXPECT_TRUE((std::is_same<const volatile int,
-                              absl::variant_alternative_t<1, const V>>::value));
-    EXPECT_TRUE(
-        (std::is_same<volatile int,
-                      absl::variant_alternative_t<1, volatile V>>::value));
-    EXPECT_TRUE((
-        std::is_same<const volatile int,
-                     absl::variant_alternative_t<1, const volatile V>>::value));
-
-    EXPECT_TRUE(
-        (std::is_same<const char*, absl::variant_alternative_t<2, V>>::value));
-    EXPECT_TRUE((std::is_same<const char* const,
-                              absl::variant_alternative_t<2, const V>>::value));
-    EXPECT_TRUE(
-        (std::is_same<const char* volatile,
-                      absl::variant_alternative_t<2, volatile V>>::value));
-    EXPECT_TRUE((
-        std::is_same<const char* const volatile,
-                     absl::variant_alternative_t<2, const volatile V>>::value));
-  }
-}
-
-///////////////////
-// [variant.get] //
-///////////////////
-
-TEST(VariantTest, HoldsAlternative) {
-  using Var = variant<int, std::string, double>;
-
-  Var v = 1;
-  EXPECT_TRUE(absl::holds_alternative<int>(v));
-  EXPECT_FALSE(absl::holds_alternative<std::string>(v));
-  EXPECT_FALSE(absl::holds_alternative<double>(v));
-  v = "str";
-  EXPECT_FALSE(absl::holds_alternative<int>(v));
-  EXPECT_TRUE(absl::holds_alternative<std::string>(v));
-  EXPECT_FALSE(absl::holds_alternative<double>(v));
-  v = 0.;
-  EXPECT_FALSE(absl::holds_alternative<int>(v));
-  EXPECT_FALSE(absl::holds_alternative<std::string>(v));
-  EXPECT_TRUE(absl::holds_alternative<double>(v));
-
-  Var v2 = v;
-  EXPECT_FALSE(absl::holds_alternative<int>(v2));
-  EXPECT_FALSE(absl::holds_alternative<std::string>(v2));
-  EXPECT_TRUE(absl::holds_alternative<double>(v2));
-  v2.emplace<int>(3);
-  EXPECT_TRUE(absl::holds_alternative<int>(v2));
-  EXPECT_FALSE(absl::holds_alternative<std::string>(v2));
-  EXPECT_FALSE(absl::holds_alternative<double>(v2));
-}
-
-TEST(VariantTest, GetIndex) {
-  using Var = variant<int, std::string, double, int>;
-
-  {
-    Var v(absl::in_place_index<0>, 0);
-
-    using LValueGetType = decltype(absl::get<0>(v));
-    using RValueGetType = decltype(absl::get<0>(absl::move(v)));
-
-    EXPECT_TRUE((std::is_same<LValueGetType, int&>::value));
-    EXPECT_TRUE((std::is_same<RValueGetType, int&&>::value));
-    EXPECT_EQ(absl::get<0>(v), 0);
-    EXPECT_EQ(absl::get<0>(absl::move(v)), 0);
-
-    const Var& const_v = v;
-    using ConstLValueGetType = decltype(absl::get<0>(const_v));
-    using ConstRValueGetType = decltype(absl::get<0>(absl::move(const_v)));
-    EXPECT_TRUE((std::is_same<ConstLValueGetType, const int&>::value));
-    EXPECT_TRUE((std::is_same<ConstRValueGetType, const int&&>::value));
-    EXPECT_EQ(absl::get<0>(const_v), 0);
-    EXPECT_EQ(absl::get<0>(absl::move(const_v)), 0);
-  }
-
-  {
-    Var v = std::string("Hello");
-
-    using LValueGetType = decltype(absl::get<1>(v));
-    using RValueGetType = decltype(absl::get<1>(absl::move(v)));
-
-    EXPECT_TRUE((std::is_same<LValueGetType, std::string&>::value));
-    EXPECT_TRUE((std::is_same<RValueGetType, std::string&&>::value));
-    EXPECT_EQ(absl::get<1>(v), "Hello");
-    EXPECT_EQ(absl::get<1>(absl::move(v)), "Hello");
-
-    const Var& const_v = v;
-    using ConstLValueGetType = decltype(absl::get<1>(const_v));
-    using ConstRValueGetType = decltype(absl::get<1>(absl::move(const_v)));
-    EXPECT_TRUE((std::is_same<ConstLValueGetType, const std::string&>::value));
-    EXPECT_TRUE((std::is_same<ConstRValueGetType, const std::string&&>::value));
-    EXPECT_EQ(absl::get<1>(const_v), "Hello");
-    EXPECT_EQ(absl::get<1>(absl::move(const_v)), "Hello");
-  }
-
-  {
-    Var v = 2.0;
-
-    using LValueGetType = decltype(absl::get<2>(v));
-    using RValueGetType = decltype(absl::get<2>(absl::move(v)));
-
-    EXPECT_TRUE((std::is_same<LValueGetType, double&>::value));
-    EXPECT_TRUE((std::is_same<RValueGetType, double&&>::value));
-    EXPECT_EQ(absl::get<2>(v), 2.);
-    EXPECT_EQ(absl::get<2>(absl::move(v)), 2.);
-
-    const Var& const_v = v;
-    using ConstLValueGetType = decltype(absl::get<2>(const_v));
-    using ConstRValueGetType = decltype(absl::get<2>(absl::move(const_v)));
-    EXPECT_TRUE((std::is_same<ConstLValueGetType, const double&>::value));
-    EXPECT_TRUE((std::is_same<ConstRValueGetType, const double&&>::value));
-    EXPECT_EQ(absl::get<2>(const_v), 2.);
-    EXPECT_EQ(absl::get<2>(absl::move(const_v)), 2.);
-  }
-
-  {
-    Var v(absl::in_place_index<0>, 0);
-    v.emplace<3>(1);
-
-    using LValueGetType = decltype(absl::get<3>(v));
-    using RValueGetType = decltype(absl::get<3>(absl::move(v)));
-
-    EXPECT_TRUE((std::is_same<LValueGetType, int&>::value));
-    EXPECT_TRUE((std::is_same<RValueGetType, int&&>::value));
-    EXPECT_EQ(absl::get<3>(v), 1);
-    EXPECT_EQ(absl::get<3>(absl::move(v)), 1);
-
-    const Var& const_v = v;
-    using ConstLValueGetType = decltype(absl::get<3>(const_v));
-    using ConstRValueGetType = decltype(absl::get<3>(absl::move(const_v)));
-    EXPECT_TRUE((std::is_same<ConstLValueGetType, const int&>::value));
-    EXPECT_TRUE((std::is_same<ConstRValueGetType, const int&&>::value));
-    EXPECT_EQ(absl::get<3>(const_v), 1);
-    EXPECT_EQ(absl::get<3>(absl::move(const_v)), 1);  // NOLINT
-  }
-}
-
-TEST(VariantTest, BadGetIndex) {
-  using Var = variant<int, std::string, double>;
-
-  {
-    Var v = 1;
-
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(v));
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(std::move(v)));
-
-    const Var& const_v = v;
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<1>(const_v));
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
-        absl::get<1>(std::move(const_v)));  // NOLINT
-  }
-
-  {
-    Var v = std::string("Hello");
-
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(v));
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(std::move(v)));
-
-    const Var& const_v = v;
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<0>(const_v));
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
-        absl::get<0>(std::move(const_v)));  // NOLINT
-  }
-}
-
-TEST(VariantTest, GetType) {
-  using Var = variant<int, std::string, double>;
-
-  {
-    Var v = 1;
-
-    using LValueGetType = decltype(absl::get<int>(v));
-    using RValueGetType = decltype(absl::get<int>(absl::move(v)));
-
-    EXPECT_TRUE((std::is_same<LValueGetType, int&>::value));
-    EXPECT_TRUE((std::is_same<RValueGetType, int&&>::value));
-    EXPECT_EQ(absl::get<int>(v), 1);
-    EXPECT_EQ(absl::get<int>(absl::move(v)), 1);
-
-    const Var& const_v = v;
-    using ConstLValueGetType = decltype(absl::get<int>(const_v));
-    using ConstRValueGetType = decltype(absl::get<int>(absl::move(const_v)));
-    EXPECT_TRUE((std::is_same<ConstLValueGetType, const int&>::value));
-    EXPECT_TRUE((std::is_same<ConstRValueGetType, const int&&>::value));
-    EXPECT_EQ(absl::get<int>(const_v), 1);
-    EXPECT_EQ(absl::get<int>(absl::move(const_v)), 1);
-  }
-
-  {
-    Var v = std::string("Hello");
-
-    using LValueGetType = decltype(absl::get<1>(v));
-    using RValueGetType = decltype(absl::get<1>(absl::move(v)));
-
-    EXPECT_TRUE((std::is_same<LValueGetType, std::string&>::value));
-    EXPECT_TRUE((std::is_same<RValueGetType, std::string&&>::value));
-    EXPECT_EQ(absl::get<std::string>(v), "Hello");
-    EXPECT_EQ(absl::get<std::string>(absl::move(v)), "Hello");
-
-    const Var& const_v = v;
-    using ConstLValueGetType = decltype(absl::get<1>(const_v));
-    using ConstRValueGetType = decltype(absl::get<1>(absl::move(const_v)));
-    EXPECT_TRUE((std::is_same<ConstLValueGetType, const std::string&>::value));
-    EXPECT_TRUE((std::is_same<ConstRValueGetType, const std::string&&>::value));
-    EXPECT_EQ(absl::get<std::string>(const_v), "Hello");
-    EXPECT_EQ(absl::get<std::string>(absl::move(const_v)), "Hello");
-  }
-
-  {
-    Var v = 2.0;
-
-    using LValueGetType = decltype(absl::get<2>(v));
-    using RValueGetType = decltype(absl::get<2>(absl::move(v)));
-
-    EXPECT_TRUE((std::is_same<LValueGetType, double&>::value));
-    EXPECT_TRUE((std::is_same<RValueGetType, double&&>::value));
-    EXPECT_EQ(absl::get<double>(v), 2.);
-    EXPECT_EQ(absl::get<double>(absl::move(v)), 2.);
-
-    const Var& const_v = v;
-    using ConstLValueGetType = decltype(absl::get<2>(const_v));
-    using ConstRValueGetType = decltype(absl::get<2>(absl::move(const_v)));
-    EXPECT_TRUE((std::is_same<ConstLValueGetType, const double&>::value));
-    EXPECT_TRUE((std::is_same<ConstRValueGetType, const double&&>::value));
-    EXPECT_EQ(absl::get<double>(const_v), 2.);
-    EXPECT_EQ(absl::get<double>(absl::move(const_v)), 2.);
-  }
-}
-
-TEST(VariantTest, BadGetType) {
-  using Var = variant<int, std::string, double>;
-
-  {
-    Var v = 1;
-
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<std::string>(v));
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
-        absl::get<std::string>(std::move(v)));
-
-    const Var& const_v = v;
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
-        absl::get<std::string>(const_v));
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
-        absl::get<std::string>(std::move(const_v)));  // NOLINT
-  }
-
-  {
-    Var v = std::string("Hello");
-
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<int>(v));
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<int>(std::move(v)));
-
-    const Var& const_v = v;
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<int>(const_v));
-    ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(
-        absl::get<int>(std::move(const_v)));  // NOLINT
-  }
-}
-
-TEST(VariantTest, GetIfIndex) {
-  using Var = variant<int, std::string, double, int>;
-
-  {
-    Var v(absl::in_place_index<0>, 0);
-    EXPECT_TRUE(noexcept(absl::get_if<0>(&v)));
-
-    {
-      auto* elem = absl::get_if<0>(&v);
-      EXPECT_TRUE((std::is_same<decltype(elem), int*>::value));
-      ASSERT_NE(elem, nullptr);
-      EXPECT_EQ(*elem, 0);
-      {
-        auto* bad_elem = absl::get_if<1>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), std::string*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<2>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), double*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<3>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-    }
-
-    const Var& const_v = v;
-    EXPECT_TRUE(noexcept(absl::get_if<0>(&const_v)));
-
-    {
-      auto* elem = absl::get_if<0>(&const_v);
-      EXPECT_TRUE((std::is_same<decltype(elem), const int*>::value));
-      ASSERT_NE(elem, nullptr);
-      EXPECT_EQ(*elem, 0);
-      {
-        auto* bad_elem = absl::get_if<1>(&const_v);
-        EXPECT_TRUE(
-            (std::is_same<decltype(bad_elem), const std::string*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<2>(&const_v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), const double*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<3>(&const_v);
-        EXPECT_EQ(bad_elem, nullptr);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
-      }
-    }
-  }
-
-  {
-    Var v = std::string("Hello");
-    EXPECT_TRUE(noexcept(absl::get_if<1>(&v)));
-
-    {
-      auto* elem = absl::get_if<1>(&v);
-      EXPECT_TRUE((std::is_same<decltype(elem), std::string*>::value));
-      ASSERT_NE(elem, nullptr);
-      EXPECT_EQ(*elem, "Hello");
-      {
-        auto* bad_elem = absl::get_if<0>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<2>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), double*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<3>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-    }
-
-    const Var& const_v = v;
-    EXPECT_TRUE(noexcept(absl::get_if<1>(&const_v)));
-
-    {
-      auto* elem = absl::get_if<1>(&const_v);
-      EXPECT_TRUE((std::is_same<decltype(elem), const std::string*>::value));
-      ASSERT_NE(elem, nullptr);
-      EXPECT_EQ(*elem, "Hello");
-      {
-        auto* bad_elem = absl::get_if<0>(&const_v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<2>(&const_v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), const double*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<3>(&const_v);
-        EXPECT_EQ(bad_elem, nullptr);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
-      }
-    }
-  }
-
-  {
-    Var v = 2.0;
-    EXPECT_TRUE(noexcept(absl::get_if<2>(&v)));
-
-    {
-      auto* elem = absl::get_if<2>(&v);
-      EXPECT_TRUE((std::is_same<decltype(elem), double*>::value));
-      ASSERT_NE(elem, nullptr);
-      EXPECT_EQ(*elem, 2.0);
-      {
-        auto* bad_elem = absl::get_if<0>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<1>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), std::string*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<3>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-    }
-
-    const Var& const_v = v;
-    EXPECT_TRUE(noexcept(absl::get_if<2>(&const_v)));
-
-    {
-      auto* elem = absl::get_if<2>(&const_v);
-      EXPECT_TRUE((std::is_same<decltype(elem), const double*>::value));
-      ASSERT_NE(elem, nullptr);
-      EXPECT_EQ(*elem, 2.0);
-      {
-        auto* bad_elem = absl::get_if<0>(&const_v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<1>(&const_v);
-        EXPECT_TRUE(
-            (std::is_same<decltype(bad_elem), const std::string*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<3>(&const_v);
-        EXPECT_EQ(bad_elem, nullptr);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
-      }
-    }
-  }
-
-  {
-    Var v(absl::in_place_index<0>, 0);
-    v.emplace<3>(1);
-    EXPECT_TRUE(noexcept(absl::get_if<3>(&v)));
-
-    {
-      auto* elem = absl::get_if<3>(&v);
-      EXPECT_TRUE((std::is_same<decltype(elem), int*>::value));
-      ASSERT_NE(elem, nullptr);
-      EXPECT_EQ(*elem, 1);
-      {
-        auto* bad_elem = absl::get_if<0>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), int*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<1>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), std::string*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<2>(&v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), double*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-    }
-
-    const Var& const_v = v;
-    EXPECT_TRUE(noexcept(absl::get_if<3>(&const_v)));
-
-    {
-      auto* elem = absl::get_if<3>(&const_v);
-      EXPECT_TRUE((std::is_same<decltype(elem), const int*>::value));
-      ASSERT_NE(elem, nullptr);
-      EXPECT_EQ(*elem, 1);
-      {
-        auto* bad_elem = absl::get_if<0>(&const_v);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), const int*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<1>(&const_v);
-        EXPECT_TRUE(
-            (std::is_same<decltype(bad_elem), const std::string*>::value));
-        EXPECT_EQ(bad_elem, nullptr);
-      }
-      {
-        auto* bad_elem = absl::get_if<2>(&const_v);
-        EXPECT_EQ(bad_elem, nullptr);
-        EXPECT_TRUE((std::is_same<decltype(bad_elem), const double*>::value));
-      }
-    }
-  }
-}
-
-//////////////////////
-// [variant.relops] //
-//////////////////////
-
-TEST(VariantTest, OperatorEquals) {
-  variant<int, std::string> a(1), b(1);
-  EXPECT_TRUE(a == b);
-  EXPECT_TRUE(b == a);
-  EXPECT_FALSE(a != b);
-  EXPECT_FALSE(b != a);
-
-  b = "str";
-  EXPECT_FALSE(a == b);
-  EXPECT_FALSE(b == a);
-  EXPECT_TRUE(a != b);
-  EXPECT_TRUE(b != a);
-
-  b = 0;
-  EXPECT_FALSE(a == b);
-  EXPECT_FALSE(b == a);
-  EXPECT_TRUE(a != b);
-  EXPECT_TRUE(b != a);
-
-  a = b = "foo";
-  EXPECT_TRUE(a == b);
-  EXPECT_TRUE(b == a);
-  EXPECT_FALSE(a != b);
-  EXPECT_FALSE(b != a);
-
-  a = "bar";
-  EXPECT_FALSE(a == b);
-  EXPECT_FALSE(b == a);
-  EXPECT_TRUE(a != b);
-  EXPECT_TRUE(b != a);
-}
-
-TEST(VariantTest, OperatorRelational) {
-  variant<int, std::string> a(1), b(1);
-  EXPECT_FALSE(a < b);
-  EXPECT_FALSE(b < a);
-  EXPECT_FALSE(a > b);
-  EXPECT_FALSE(b > a);
-  EXPECT_TRUE(a <= b);
-  EXPECT_TRUE(b <= a);
-  EXPECT_TRUE(a >= b);
-  EXPECT_TRUE(b >= a);
-
-  b = "str";
-  EXPECT_TRUE(a < b);
-  EXPECT_FALSE(b < a);
-  EXPECT_FALSE(a > b);
-  EXPECT_TRUE(b > a);
-  EXPECT_TRUE(a <= b);
-  EXPECT_FALSE(b <= a);
-  EXPECT_FALSE(a >= b);
-  EXPECT_TRUE(b >= a);
-
-  b = 0;
-  EXPECT_FALSE(a < b);
-  EXPECT_TRUE(b < a);
-  EXPECT_TRUE(a > b);
-  EXPECT_FALSE(b > a);
-  EXPECT_FALSE(a <= b);
-  EXPECT_TRUE(b <= a);
-  EXPECT_TRUE(a >= b);
-  EXPECT_FALSE(b >= a);
-
-  a = b = "foo";
-  EXPECT_FALSE(a < b);
-  EXPECT_FALSE(b < a);
-  EXPECT_FALSE(a > b);
-  EXPECT_FALSE(b > a);
-  EXPECT_TRUE(a <= b);
-  EXPECT_TRUE(b <= a);
-  EXPECT_TRUE(a >= b);
-  EXPECT_TRUE(b >= a);
-
-  a = "bar";
-  EXPECT_TRUE(a < b);
-  EXPECT_FALSE(b < a);
-  EXPECT_FALSE(a > b);
-  EXPECT_TRUE(b > a);
-  EXPECT_TRUE(a <= b);
-  EXPECT_FALSE(b <= a);
-  EXPECT_FALSE(a >= b);
-  EXPECT_TRUE(b >= a);
-}
-
-#ifdef ABSL_HAVE_EXCEPTIONS
-
-TEST(VariantTest, ValuelessOperatorEquals) {
-  variant<MoveCanThrow, std::string> int_v(1), string_v("Hello"),
-      valueless(absl::in_place_index<0>),
-      other_valueless(absl::in_place_index<0>);
-  ToValuelessByException(valueless);
-  ToValuelessByException(other_valueless);
-
-  EXPECT_TRUE(valueless == other_valueless);
-  EXPECT_TRUE(other_valueless == valueless);
-  EXPECT_FALSE(valueless == int_v);
-  EXPECT_FALSE(valueless == string_v);
-  EXPECT_FALSE(int_v == valueless);
-  EXPECT_FALSE(string_v == valueless);
-
-  EXPECT_FALSE(valueless != other_valueless);
-  EXPECT_FALSE(other_valueless != valueless);
-  EXPECT_TRUE(valueless != int_v);
-  EXPECT_TRUE(valueless != string_v);
-  EXPECT_TRUE(int_v != valueless);
-  EXPECT_TRUE(string_v != valueless);
-}
-
-TEST(VariantTest, ValuelessOperatorRelational) {
-  variant<MoveCanThrow, std::string> int_v(1), string_v("Hello"),
-      valueless(absl::in_place_index<0>),
-      other_valueless(absl::in_place_index<0>);
-  ToValuelessByException(valueless);
-  ToValuelessByException(other_valueless);
-
-  EXPECT_FALSE(valueless < other_valueless);
-  EXPECT_FALSE(other_valueless < valueless);
-  EXPECT_TRUE(valueless < int_v);
-  EXPECT_TRUE(valueless < string_v);
-  EXPECT_FALSE(int_v < valueless);
-  EXPECT_FALSE(string_v < valueless);
-
-  EXPECT_TRUE(valueless <= other_valueless);
-  EXPECT_TRUE(other_valueless <= valueless);
-  EXPECT_TRUE(valueless <= int_v);
-  EXPECT_TRUE(valueless <= string_v);
-  EXPECT_FALSE(int_v <= valueless);
-  EXPECT_FALSE(string_v <= valueless);
-
-  EXPECT_TRUE(valueless >= other_valueless);
-  EXPECT_TRUE(other_valueless >= valueless);
-  EXPECT_FALSE(valueless >= int_v);
-  EXPECT_FALSE(valueless >= string_v);
-  EXPECT_TRUE(int_v >= valueless);
-  EXPECT_TRUE(string_v >= valueless);
-
-  EXPECT_FALSE(valueless > other_valueless);
-  EXPECT_FALSE(other_valueless > valueless);
-  EXPECT_FALSE(valueless > int_v);
-  EXPECT_FALSE(valueless > string_v);
-  EXPECT_TRUE(int_v > valueless);
-  EXPECT_TRUE(string_v > valueless);
-}
-
-#endif
-
-/////////////////////
-// [variant.visit] //
-/////////////////////
-
-template <typename T>
-struct ConvertTo {
-  template <typename U>
-  T operator()(const U& u) const {
-    return u;
-  }
-};
-
-TEST(VariantTest, VisitSimple) {
-  variant<std::string, const char*> v = "A";
-
-  std::string str = absl::visit(ConvertTo<std::string>{}, v);
-  EXPECT_EQ("A", str);
-
-  v = std::string("B");
-
-  absl::string_view piece = absl::visit(ConvertTo<absl::string_view>{}, v);
-  EXPECT_EQ("B", piece);
-
-  struct StrLen {
-    int operator()(const char* s) const { return strlen(s); }
-    int operator()(const std::string& s) const { return s.size(); }
-  };
-
-  v = "SomeStr";
-  EXPECT_EQ(7, absl::visit(StrLen{}, v));
-  v = std::string("VeryLargeThisTime");
-  EXPECT_EQ(17, absl::visit(StrLen{}, v));
-}
-
-TEST(VariantTest, VisitRValue) {
-  variant<std::string> v = std::string("X");
-  struct Visitor {
-    bool operator()(const std::string&) const { return false; }
-    bool operator()(std::string&&) const { return true; }  // NOLINT
-
-    int operator()(const std::string&, const std::string&) const { return 0; }
-    int operator()(const std::string&, std::string&&) const {
-      return 1;
-    }  // NOLINT
-    int operator()(std::string&&, const std::string&) const {
-      return 2;
-    }                                                                 // NOLINT
-    int operator()(std::string&&, std::string&&) const { return 3; }  // NOLINT
-  };
-  EXPECT_FALSE(absl::visit(Visitor{}, v));
-  EXPECT_TRUE(absl::visit(Visitor{}, absl::move(v)));
-
-  // Also test the variadic overload.
-  EXPECT_EQ(0, absl::visit(Visitor{}, v, v));
-  EXPECT_EQ(1, absl::visit(Visitor{}, v, absl::move(v)));
-  EXPECT_EQ(2, absl::visit(Visitor{}, absl::move(v), v));
-  EXPECT_EQ(3, absl::visit(Visitor{}, absl::move(v), absl::move(v)));
-}
-
-TEST(VariantTest, VisitRValueVisitor) {
-  variant<std::string> v = std::string("X");
-  struct Visitor {
-    bool operator()(const std::string&) const& { return false; }
-    bool operator()(const std::string&) && { return true; }
-  };
-  Visitor visitor;
-  EXPECT_FALSE(absl::visit(visitor, v));
-  EXPECT_TRUE(absl::visit(Visitor{}, v));
-}
-
-TEST(VariantTest, VisitResultTypeDifferent) {
-  variant<std::string> v = std::string("X");
-  struct LValue_LValue {};
-  struct RValue_LValue {};
-  struct LValue_RValue {};
-  struct RValue_RValue {};
-  struct Visitor {
-    LValue_LValue operator()(const std::string&) const& { return {}; }
-    RValue_LValue operator()(std::string&&) const& { return {}; }  // NOLINT
-    LValue_RValue operator()(const std::string&) && { return {}; }
-    RValue_RValue operator()(std::string&&) && { return {}; }  // NOLINT
-  } visitor;
-
-  EXPECT_TRUE(
-      (std::is_same<LValue_LValue, decltype(absl::visit(visitor, v))>::value));
-  EXPECT_TRUE(
-      (std::is_same<RValue_LValue,
-                    decltype(absl::visit(visitor, absl::move(v)))>::value));
-  EXPECT_TRUE((
-      std::is_same<LValue_RValue, decltype(absl::visit(Visitor{}, v))>::value));
-  EXPECT_TRUE(
-      (std::is_same<RValue_RValue,
-                    decltype(absl::visit(Visitor{}, absl::move(v)))>::value));
-}
-
-TEST(VariantTest, VisitVariadic) {
-  using A = variant<int, std::string>;
-  using B = variant<std::unique_ptr<int>, absl::string_view>;
-
-  struct Visitor {
-    std::pair<int, int> operator()(int a, std::unique_ptr<int> b) const {
-      return {a, *b};
-    }
-    std::pair<int, int> operator()(absl::string_view a,
-                                   std::unique_ptr<int> b) const {
-      return {static_cast<int>(a.size()), static_cast<int>(*b)};
-    }
-    std::pair<int, int> operator()(int a, absl::string_view b) const {
-      return {a, static_cast<int>(b.size())};
-    }
-    std::pair<int, int> operator()(absl::string_view a,
-                                   absl::string_view b) const {
-      return {static_cast<int>(a.size()), static_cast<int>(b.size())};
-    }
-  };
-
-  EXPECT_THAT(absl::visit(Visitor(), A(1), B(std::unique_ptr<int>(new int(7)))),
-              ::testing::Pair(1, 7));
-  EXPECT_THAT(absl::visit(Visitor(), A(1), B(absl::string_view("ABC"))),
-              ::testing::Pair(1, 3));
-  EXPECT_THAT(absl::visit(Visitor(), A(std::string("BBBBB")),
-                          B(std::unique_ptr<int>(new int(7)))),
-              ::testing::Pair(5, 7));
-  EXPECT_THAT(absl::visit(Visitor(), A(std::string("BBBBB")),
-                          B(absl::string_view("ABC"))),
-              ::testing::Pair(5, 3));
-}
-
-TEST(VariantTest, VisitNoArgs) {
-  EXPECT_EQ(5, absl::visit([] { return 5; }));
-}
-
-struct ConstFunctor {
-  int operator()(int a, int b) const { return a - b; }
-};
-
-struct MutableFunctor {
-  int operator()(int a, int b) { return a - b; }
-};
-
-struct Class {
-  int Method(int a, int b) { return a - b; }
-  int ConstMethod(int a, int b) const { return a - b; }
-
-  int member;
-};
-
-TEST(VariantTest, VisitReferenceWrapper) {
-  ConstFunctor cf;
-  MutableFunctor mf;
-  absl::variant<int> three = 3;
-  absl::variant<int> two = 2;
-
-  EXPECT_EQ(1, absl::visit(std::cref(cf), three, two));
-  EXPECT_EQ(1, absl::visit(std::ref(cf), three, two));
-  EXPECT_EQ(1, absl::visit(std::ref(mf), three, two));
-}
-
-// libstdc++ std::variant doesn't support the INVOKE semantics.
-#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
-TEST(VariantTest, VisitMemberFunction) {
-  absl::variant<std::unique_ptr<Class>> p(absl::make_unique<Class>());
-  absl::variant<std::unique_ptr<const Class>> cp(
-      absl::make_unique<const Class>());
-  absl::variant<int> three = 3;
-  absl::variant<int> two = 2;
-
-  EXPECT_EQ(1, absl::visit(&Class::Method, p, three, two));
-  EXPECT_EQ(1, absl::visit(&Class::ConstMethod, p, three, two));
-  EXPECT_EQ(1, absl::visit(&Class::ConstMethod, cp, three, two));
-}
-
-TEST(VariantTest, VisitDataMember) {
-  absl::variant<std::unique_ptr<Class>> p(absl::make_unique<Class>(Class{42}));
-  absl::variant<std::unique_ptr<const Class>> cp(
-      absl::make_unique<const Class>(Class{42}));
-  EXPECT_EQ(42, absl::visit(&Class::member, p));
-
-  absl::visit(&Class::member, p) = 5;
-  EXPECT_EQ(5, absl::visit(&Class::member, p));
-
-  EXPECT_EQ(42, absl::visit(&Class::member, cp));
-}
-#endif  // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
-
-/////////////////////////
-// [variant.monostate] //
-/////////////////////////
-
-TEST(VariantTest, MonostateBasic) {
-  absl::monostate mono;
-  (void)mono;
-
-  // TODO(mattcalabrese) Expose move triviality metafunctions in absl.
-  EXPECT_TRUE(absl::is_trivially_default_constructible<absl::monostate>::value);
-  EXPECT_TRUE(is_trivially_move_constructible<absl::monostate>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<absl::monostate>::value);
-  EXPECT_TRUE(is_trivially_move_assignable<absl::monostate>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<absl::monostate>::value);
-  EXPECT_TRUE(absl::is_trivially_destructible<absl::monostate>::value);
-}
-
-TEST(VariantTest, VariantMonostateDefaultConstruction) {
-  absl::variant<absl::monostate, NonDefaultConstructible> var;
-  EXPECT_EQ(var.index(), 0);
-}
-
-////////////////////////////////
-// [variant.monostate.relops] //
-////////////////////////////////
-
-TEST(VariantTest, MonostateComparisons) {
-  absl::monostate lhs, rhs;
-
-  EXPECT_EQ(lhs, lhs);
-  EXPECT_EQ(lhs, rhs);
-
-  EXPECT_FALSE(lhs != lhs);
-  EXPECT_FALSE(lhs != rhs);
-  EXPECT_FALSE(lhs < lhs);
-  EXPECT_FALSE(lhs < rhs);
-  EXPECT_FALSE(lhs > lhs);
-  EXPECT_FALSE(lhs > rhs);
-
-  EXPECT_LE(lhs, lhs);
-  EXPECT_LE(lhs, rhs);
-  EXPECT_GE(lhs, lhs);
-  EXPECT_GE(lhs, rhs);
-
-  EXPECT_TRUE(noexcept(std::declval<absl::monostate>() ==
-                       std::declval<absl::monostate>()));
-  EXPECT_TRUE(noexcept(std::declval<absl::monostate>() !=
-                       std::declval<absl::monostate>()));
-  EXPECT_TRUE(noexcept(std::declval<absl::monostate>() <
-                       std::declval<absl::monostate>()));
-  EXPECT_TRUE(noexcept(std::declval<absl::monostate>() >
-                       std::declval<absl::monostate>()));
-  EXPECT_TRUE(noexcept(std::declval<absl::monostate>() <=
-                       std::declval<absl::monostate>()));
-  EXPECT_TRUE(noexcept(std::declval<absl::monostate>() >=
-                       std::declval<absl::monostate>()));
-}
-
-///////////////////////
-// [variant.specalg] //
-///////////////////////
-
-TEST(VariantTest, NonmemberSwap) {
-  using std::swap;
-
-  SpecialSwap v1(3);
-  SpecialSwap v2(7);
-
-  variant<SpecialSwap> a = v1, b = v2;
-
-  EXPECT_THAT(a, VariantWith<SpecialSwap>(v1));
-  EXPECT_THAT(b, VariantWith<SpecialSwap>(v2));
-
-  std::swap(a, b);
-  EXPECT_THAT(a, VariantWith<SpecialSwap>(v2));
-  EXPECT_THAT(b, VariantWith<SpecialSwap>(v1));
-#ifndef ABSL_USES_STD_VARIANT
-  EXPECT_FALSE(absl::get<SpecialSwap>(a).special_swap);
-#endif
-
-  swap(a, b);
-  EXPECT_THAT(a, VariantWith<SpecialSwap>(v1));
-  EXPECT_THAT(b, VariantWith<SpecialSwap>(v2));
-  EXPECT_TRUE(absl::get<SpecialSwap>(b).special_swap);
-}
-
-//////////////////////////
-// [variant.bad.access] //
-//////////////////////////
-
-TEST(VariantTest, BadAccess) {
-  EXPECT_TRUE(noexcept(absl::bad_variant_access()));
-  absl::bad_variant_access exception_obj;
-  std::exception* base = &exception_obj;
-  (void)base;
-}
-
-////////////////////
-// [variant.hash] //
-////////////////////
-
-TEST(VariantTest, MonostateHash) {
-  absl::monostate mono, other_mono;
-  std::hash<absl::monostate> const hasher{};
-  static_assert(std::is_same<decltype(hasher(mono)), std::size_t>::value, "");
-  EXPECT_EQ(hasher(mono), hasher(other_mono));
-}
-
-TEST(VariantTest, Hash) {
-  static_assert(type_traits_internal::IsHashable<variant<int>>::value, "");
-  static_assert(type_traits_internal::IsHashable<variant<Hashable>>::value, "");
-  static_assert(type_traits_internal::IsHashable<variant<int, Hashable>>::value,
-                "");
-
-#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
-  static_assert(!type_traits_internal::IsHashable<variant<NonHashable>>::value,
-                "");
-  static_assert(
-      !type_traits_internal::IsHashable<variant<Hashable, NonHashable>>::value,
-      "");
-#endif
-
-// MSVC std::hash<std::variant> does not use the index, thus produce the same
-// result on the same value as different alternative.
-#if !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT))
-  {
-    // same value as different alternative
-    variant<int, int> v0(in_place_index<0>, 42);
-    variant<int, int> v1(in_place_index<1>, 42);
-    std::hash<variant<int, int>> hash;
-    EXPECT_NE(hash(v0), hash(v1));
-  }
-#endif  // !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT))
-
-  {
-    std::hash<variant<int>> hash;
-    std::set<size_t> hashcodes;
-    for (int i = 0; i < 100; ++i) {
-      hashcodes.insert(hash(i));
-    }
-    EXPECT_GT(hashcodes.size(), 90);
-
-    // test const-qualified
-    static_assert(type_traits_internal::IsHashable<variant<const int>>::value,
-                  "");
-    static_assert(
-        type_traits_internal::IsHashable<variant<const Hashable>>::value, "");
-    std::hash<absl::variant<const int>> c_hash;
-    for (int i = 0; i < 100; ++i) {
-      EXPECT_EQ(hash(i), c_hash(i));
-    }
-  }
-}
-
-////////////////////////////////////////
-// Miscellaneous and deprecated tests //
-////////////////////////////////////////
-
-// Test that a set requiring a basic type conversion works correctly
-#if !defined(ABSL_USES_STD_VARIANT)
-TEST(VariantTest, TestConvertingSet) {
-  typedef variant<double> Variant;
-  Variant v(1.0);
-  const int two = 2;
-  v = two;
-  EXPECT_TRUE(absl::holds_alternative<double>(v));
-  ASSERT_TRUE(nullptr != absl::get_if<double>(&v));
-  EXPECT_DOUBLE_EQ(2, absl::get<double>(v));
-}
-#endif  // ABSL_USES_STD_VARIANT
-
-// Test that a vector of variants behaves reasonably.
-TEST(VariantTest, Container) {
-  typedef variant<int, float> Variant;
-
-  // Creation of vector should work
-  std::vector<Variant> vec;
-  vec.push_back(Variant(10));
-  vec.push_back(Variant(20.0f));
-
-  // Vector resizing should work if we supply a value for new slots
-  vec.resize(10, Variant(0));
-}
-
-// Test that a variant with a non-copyable type can be constructed and
-// manipulated to some degree.
-TEST(VariantTest, TestVariantWithNonCopyableType) {
-  typedef variant<int, NonCopyable> Variant;
-  const int kValue = 1;
-  Variant v(kValue);
-  ASSERT_TRUE(absl::holds_alternative<int>(v));
-  EXPECT_EQ(kValue, absl::get<int>(v));
-}
-
-// Test that a variant with a non-copyable type can be transformed to
-// the non-copyable type with a call to `emplace` for different numbers
-// of arguments. We do not need to test this for each of T1 ... T8
-// because `emplace` does not overload on T1 ... to T8, so if this
-// works for any one of T1 ... T8, then it works for all of them. We
-// do need to test that it works with varying numbers of parameters
-// though.
-TEST(VariantTest, TestEmplace) {
-  typedef variant<int, NonCopyable> Variant;
-  const int kValue = 1;
-  Variant v(kValue);
-  ASSERT_TRUE(absl::holds_alternative<int>(v));
-  EXPECT_EQ(kValue, absl::get<int>(v));
-
-  // emplace with zero arguments, then back to 'int'
-  v.emplace<NonCopyable>();
-  ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
-  EXPECT_EQ(0, absl::get<NonCopyable>(v).value);
-  v = kValue;
-  ASSERT_TRUE(absl::holds_alternative<int>(v));
-
-  // emplace with one argument:
-  v.emplace<NonCopyable>(1);
-  ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
-  EXPECT_EQ(1, absl::get<NonCopyable>(v).value);
-  v = kValue;
-  ASSERT_TRUE(absl::holds_alternative<int>(v));
-
-  // emplace with two arguments:
-  v.emplace<NonCopyable>(1, 2);
-  ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
-  EXPECT_EQ(3, absl::get<NonCopyable>(v).value);
-  v = kValue;
-  ASSERT_TRUE(absl::holds_alternative<int>(v));
-
-  // emplace with three arguments
-  v.emplace<NonCopyable>(1, 2, 3);
-  ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
-  EXPECT_EQ(6, absl::get<NonCopyable>(v).value);
-  v = kValue;
-  ASSERT_TRUE(absl::holds_alternative<int>(v));
-
-  // emplace with four arguments
-  v.emplace<NonCopyable>(1, 2, 3, 4);
-  ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
-  EXPECT_EQ(10, absl::get<NonCopyable>(v).value);
-  v = kValue;
-  ASSERT_TRUE(absl::holds_alternative<int>(v));
-}
-
-TEST(VariantTest, TestEmplaceDestroysCurrentValue) {
-  typedef variant<int, IncrementInDtor, NonCopyable> Variant;
-  int counter = 0;
-  Variant v(0);
-  ASSERT_TRUE(absl::holds_alternative<int>(v));
-  v.emplace<IncrementInDtor>(&counter);
-  ASSERT_TRUE(absl::holds_alternative<IncrementInDtor>(v));
-  ASSERT_EQ(0, counter);
-  v.emplace<NonCopyable>();
-  ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v));
-  EXPECT_EQ(1, counter);
-}
-
-TEST(VariantTest, TestMoveSemantics) {
-  typedef variant<std::unique_ptr<int>, std::unique_ptr<std::string>> Variant;
-
-  // Construct a variant by moving from an element value.
-  Variant v(absl::WrapUnique(new int(10)));
-  EXPECT_TRUE(absl::holds_alternative<std::unique_ptr<int>>(v));
-
-  // Construct a variant by moving from another variant.
-  Variant v2(absl::move(v));
-  ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<int>>(v2));
-  ASSERT_NE(nullptr, absl::get<std::unique_ptr<int>>(v2));
-  EXPECT_EQ(10, *absl::get<std::unique_ptr<int>>(v2));
-
-  // Moving from a variant object leaves it holding moved-from value of the
-  // same element type.
-  EXPECT_TRUE(absl::holds_alternative<std::unique_ptr<int>>(v));
-  ASSERT_NE(nullptr, absl::get_if<std::unique_ptr<int>>(&v));
-  EXPECT_EQ(nullptr, absl::get<std::unique_ptr<int>>(v));
-
-  // Assign a variant from an element value by move.
-  v = absl::make_unique<std::string>("foo");
-  ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<std::string>>(v));
-  EXPECT_EQ("foo", *absl::get<std::unique_ptr<std::string>>(v));
-
-  // Move-assign a variant.
-  v2 = absl::move(v);
-  ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<std::string>>(v2));
-  EXPECT_EQ("foo", *absl::get<std::unique_ptr<std::string>>(v2));
-  EXPECT_TRUE(absl::holds_alternative<std::unique_ptr<std::string>>(v));
-}
-
-variant<int, std::string> PassThrough(const variant<int, std::string>& arg) {
-  return arg;
-}
-
-TEST(VariantTest, TestImplicitConversion) {
-  EXPECT_TRUE(absl::holds_alternative<int>(PassThrough(0)));
-
-  // We still need the explicit cast for std::string, because C++ won't apply
-  // two user-defined implicit conversions in a row.
-  EXPECT_TRUE(
-      absl::holds_alternative<std::string>(PassThrough(std::string("foo"))));
-}
-
-struct Convertible2;
-struct Convertible1 {
-  Convertible1() {}
-  Convertible1(const Convertible1&) {}
-  Convertible1& operator=(const Convertible1&) { return *this; }
-
-  // implicit conversion from Convertible2
-  Convertible1(const Convertible2&) {}  // NOLINT(runtime/explicit)
-};
-
-struct Convertible2 {
-  Convertible2() {}
-  Convertible2(const Convertible2&) {}
-  Convertible2& operator=(const Convertible2&) { return *this; }
-
-  // implicit conversion from Convertible1
-  Convertible2(const Convertible1&) {}  // NOLINT(runtime/explicit)
-};
-
-TEST(VariantTest, TestRvalueConversion) {
-#if !defined(ABSL_USES_STD_VARIANT)
-  variant<double, std::string> var(
-      ConvertVariantTo<variant<double, std::string>>(
-          variant<std::string, int>(0)));
-  ASSERT_TRUE(absl::holds_alternative<double>(var));
-  EXPECT_EQ(0.0, absl::get<double>(var));
-
-  var = ConvertVariantTo<variant<double, std::string>>(
-      variant<const char*, float>("foo"));
-  ASSERT_TRUE(absl::holds_alternative<std::string>(var));
-  EXPECT_EQ("foo", absl::get<std::string>(var));
-
-  variant<double> singleton(
-      ConvertVariantTo<variant<double>>(variant<int, float>(42)));
-  ASSERT_TRUE(absl::holds_alternative<double>(singleton));
-  EXPECT_EQ(42.0, absl::get<double>(singleton));
-
-  singleton = ConvertVariantTo<variant<double>>(variant<int, float>(3.14f));
-  ASSERT_TRUE(absl::holds_alternative<double>(singleton));
-  EXPECT_FLOAT_EQ(3.14f, static_cast<float>(absl::get<double>(singleton)));
-
-  singleton = ConvertVariantTo<variant<double>>(variant<int>(0));
-  ASSERT_TRUE(absl::holds_alternative<double>(singleton));
-  EXPECT_EQ(0.0, absl::get<double>(singleton));
-
-  variant<int32_t, uint32_t> variant2(
-      ConvertVariantTo<variant<int32_t, uint32_t>>(variant<int32_t>(42)));
-  ASSERT_TRUE(absl::holds_alternative<int32_t>(variant2));
-  EXPECT_EQ(42, absl::get<int32_t>(variant2));
-
-  variant2 =
-      ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42));
-  ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2));
-  EXPECT_EQ(42, absl::get<uint32_t>(variant2));
-#endif  // !ABSL_USES_STD_VARIANT
-
-  variant<Convertible1, Convertible2> variant3(
-      ConvertVariantTo<variant<Convertible1, Convertible2>>(
-          (variant<Convertible2, Convertible1>(Convertible1()))));
-  ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3));
-
-  variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(
-      variant<Convertible2, Convertible1>(Convertible2()));
-  ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3));
-}
-
-TEST(VariantTest, TestLvalueConversion) {
-#if !defined(ABSL_USES_STD_VARIANT)
-  variant<std::string, int> source1 = 0;
-  variant<double, std::string> destination(
-      ConvertVariantTo<variant<double, std::string>>(source1));
-  ASSERT_TRUE(absl::holds_alternative<double>(destination));
-  EXPECT_EQ(0.0, absl::get<double>(destination));
-
-  variant<const char*, float> source2 = "foo";
-  destination = ConvertVariantTo<variant<double, std::string>>(source2);
-  ASSERT_TRUE(absl::holds_alternative<std::string>(destination));
-  EXPECT_EQ("foo", absl::get<std::string>(destination));
-
-  variant<int, float> source3(42);
-  variant<double> singleton(ConvertVariantTo<variant<double>>(source3));
-  ASSERT_TRUE(absl::holds_alternative<double>(singleton));
-  EXPECT_EQ(42.0, absl::get<double>(singleton));
-
-  source3 = 3.14f;
-  singleton = ConvertVariantTo<variant<double>>(source3);
-  ASSERT_TRUE(absl::holds_alternative<double>(singleton));
-  EXPECT_FLOAT_EQ(3.14f, static_cast<float>(absl::get<double>(singleton)));
-
-  variant<int> source4(0);
-  singleton = ConvertVariantTo<variant<double>>(source4);
-  ASSERT_TRUE(absl::holds_alternative<double>(singleton));
-  EXPECT_EQ(0.0, absl::get<double>(singleton));
-
-  variant<int32_t> source5(42);
-  variant<int32_t, uint32_t> variant2(
-      ConvertVariantTo<variant<int32_t, uint32_t>>(source5));
-  ASSERT_TRUE(absl::holds_alternative<int32_t>(variant2));
-  EXPECT_EQ(42, absl::get<int32_t>(variant2));
-
-  variant<uint32_t> source6(42);
-  variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6);
-  ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2));
-  EXPECT_EQ(42, absl::get<uint32_t>(variant2));
-#endif
-
-  variant<Convertible2, Convertible1> source7((Convertible1()));
-  variant<Convertible1, Convertible2> variant3(
-      ConvertVariantTo<variant<Convertible1, Convertible2>>(source7));
-  ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3));
-
-  source7 = Convertible2();
-  variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(source7);
-  ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3));
-}
-
-TEST(VariantTest, TestMoveConversion) {
-  using Variant =
-      variant<std::unique_ptr<const int>, std::unique_ptr<const std::string>>;
-  using OtherVariant =
-      variant<std::unique_ptr<int>, std::unique_ptr<std::string>>;
-
-  Variant var(
-      ConvertVariantTo<Variant>(OtherVariant{absl::make_unique<int>(0)}));
-  ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<const int>>(var));
-  ASSERT_NE(absl::get<std::unique_ptr<const int>>(var), nullptr);
-  EXPECT_EQ(0, *absl::get<std::unique_ptr<const int>>(var));
-
-  var = ConvertVariantTo<Variant>(
-      OtherVariant(absl::make_unique<std::string>("foo")));
-  ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<const std::string>>(var));
-  EXPECT_EQ("foo", *absl::get<std::unique_ptr<const std::string>>(var));
-}
-
-TEST(VariantTest, DoesNotMoveFromLvalues) {
-  // We use shared_ptr here because it's both copyable and movable, and
-  // a moved-from shared_ptr is guaranteed to be null, so we can detect
-  // whether moving or copying has occurred.
-  using Variant =
-      variant<std::shared_ptr<const int>, std::shared_ptr<const std::string>>;
-  using OtherVariant =
-      variant<std::shared_ptr<int>, std::shared_ptr<std::string>>;
-
-  Variant v1(std::make_shared<const int>(0));
-
-  // Test copy constructor
-  Variant v2(v1);
-  EXPECT_EQ(absl::get<std::shared_ptr<const int>>(v1),
-            absl::get<std::shared_ptr<const int>>(v2));
-
-  // Test copy-assignment operator
-  v1 = std::make_shared<const std::string>("foo");
-  v2 = v1;
-  EXPECT_EQ(absl::get<std::shared_ptr<const std::string>>(v1),
-            absl::get<std::shared_ptr<const std::string>>(v2));
-
-  // Test converting copy constructor
-  OtherVariant other(std::make_shared<int>(0));
-  Variant v3(ConvertVariantTo<Variant>(other));
-  EXPECT_EQ(absl::get<std::shared_ptr<int>>(other),
-            absl::get<std::shared_ptr<const int>>(v3));
-
-  other = std::make_shared<std::string>("foo");
-  v3 = ConvertVariantTo<Variant>(other);
-  EXPECT_EQ(absl::get<std::shared_ptr<std::string>>(other),
-            absl::get<std::shared_ptr<const std::string>>(v3));
-}
-
-TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) {
-#if !defined(ABSL_USES_STD_VARIANT)
-  variant<double, std::string> var(
-      ConvertVariantTo<variant<double, std::string>>(
-          variant<std::string, int>(3)));
-  EXPECT_THAT(absl::get_if<double>(&var), Pointee(3.0));
-
-  var = ConvertVariantTo<variant<double, std::string>>(
-      variant<const char*, float>("foo"));
-  EXPECT_THAT(absl::get_if<std::string>(&var), Pointee(std::string("foo")));
-
-  variant<double> singleton(
-      ConvertVariantTo<variant<double>>(variant<int, float>(42)));
-  EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(42.0));
-
-  singleton = ConvertVariantTo<variant<double>>(variant<int, float>(3.14f));
-  EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(DoubleEq(3.14f)));
-
-  singleton = ConvertVariantTo<variant<double>>(variant<int>(3));
-  EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(3.0));
-
-  variant<int32_t, uint32_t> variant2(
-      ConvertVariantTo<variant<int32_t, uint32_t>>(variant<int32_t>(42)));
-  EXPECT_THAT(absl::get_if<int32_t>(&variant2), Pointee(42));
-
-  variant2 =
-      ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42));
-  EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42));
-#endif
-
-  variant<Convertible1, Convertible2> variant3(
-      ConvertVariantTo<variant<Convertible1, Convertible2>>(
-          (variant<Convertible2, Convertible1>(Convertible1()))));
-  ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3));
-
-  variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(
-      variant<Convertible2, Convertible1>(Convertible2()));
-  ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3));
-}
-
-TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) {
-#if !defined(ABSL_USES_STD_VARIANT)
-  variant<std::string, int> source1 = 3;
-  variant<double, std::string> destination(
-      ConvertVariantTo<variant<double, std::string>>(source1));
-  EXPECT_THAT(absl::get_if<double>(&destination), Pointee(3.0));
-
-  variant<const char*, float> source2 = "foo";
-  destination = ConvertVariantTo<variant<double, std::string>>(source2);
-  EXPECT_THAT(absl::get_if<std::string>(&destination),
-              Pointee(std::string("foo")));
-
-  variant<int, float> source3(42);
-  variant<double> singleton(ConvertVariantTo<variant<double>>(source3));
-  EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(42.0));
-
-  source3 = 3.14f;
-  singleton = ConvertVariantTo<variant<double>>(source3);
-  EXPECT_FLOAT_EQ(3.14f, static_cast<float>(absl::get<double>(singleton)));
-  EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(DoubleEq(3.14f)));
-
-  variant<int> source4(3);
-  singleton = ConvertVariantTo<variant<double>>(source4);
-  EXPECT_THAT(absl::get_if<double>(&singleton), Pointee(3.0));
-
-  variant<int32_t> source5(42);
-  variant<int32_t, uint32_t> variant2(
-      ConvertVariantTo<variant<int32_t, uint32_t>>(source5));
-  EXPECT_THAT(absl::get_if<int32_t>(&variant2), Pointee(42));
-
-  variant<uint32_t> source6(42);
-  variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6);
-  EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42));
-#endif  // !ABSL_USES_STD_VARIANT
-
-  variant<Convertible2, Convertible1> source7((Convertible1()));
-  variant<Convertible1, Convertible2> variant3(
-      ConvertVariantTo<variant<Convertible1, Convertible2>>(source7));
-  ASSERT_TRUE(absl::holds_alternative<Convertible1>(variant3));
-
-  source7 = Convertible2();
-  variant3 = ConvertVariantTo<variant<Convertible1, Convertible2>>(source7);
-  ASSERT_TRUE(absl::holds_alternative<Convertible2>(variant3));
-}
-
-TEST(VariantTest, TestMoveConversionViaConvertVariantTo) {
-  using Variant =
-      variant<std::unique_ptr<const int>, std::unique_ptr<const std::string>>;
-  using OtherVariant =
-      variant<std::unique_ptr<int>, std::unique_ptr<std::string>>;
-
-  Variant var(
-      ConvertVariantTo<Variant>(OtherVariant{absl::make_unique<int>(3)}));
-  EXPECT_THAT(absl::get_if<std::unique_ptr<const int>>(&var),
-              Pointee(Pointee(3)));
-
-  var = ConvertVariantTo<Variant>(
-      OtherVariant(absl::make_unique<std::string>("foo")));
-  EXPECT_THAT(absl::get_if<std::unique_ptr<const std::string>>(&var),
-              Pointee(Pointee(std::string("foo"))));
-}
-
-// If all alternatives are trivially copy/move constructible, variant should
-// also be trivially copy/move constructible. This is not required by the
-// standard and we know that libstdc++ variant doesn't have this feature.
-// For more details see the paper:
-// http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0602r0.html
-#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
-#define ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY 1
-#endif
-
-TEST(VariantTest, TestCopyAndMoveTypeTraits) {
-  EXPECT_TRUE(std::is_copy_constructible<variant<std::string>>::value);
-  EXPECT_TRUE(absl::is_copy_assignable<variant<std::string>>::value);
-  EXPECT_TRUE(std::is_move_constructible<variant<std::string>>::value);
-  EXPECT_TRUE(absl::is_move_assignable<variant<std::string>>::value);
-  EXPECT_TRUE(std::is_move_constructible<variant<std::unique_ptr<int>>>::value);
-  EXPECT_TRUE(absl::is_move_assignable<variant<std::unique_ptr<int>>>::value);
-  EXPECT_FALSE(
-      std::is_copy_constructible<variant<std::unique_ptr<int>>>::value);
-  EXPECT_FALSE(absl::is_copy_assignable<variant<std::unique_ptr<int>>>::value);
-
-  EXPECT_FALSE(
-      absl::is_trivially_copy_constructible<variant<std::string>>::value);
-  EXPECT_FALSE(absl::is_trivially_copy_assignable<variant<std::string>>::value);
-#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
-  EXPECT_TRUE(absl::is_trivially_copy_constructible<variant<int>>::value);
-  EXPECT_TRUE(absl::is_trivially_copy_assignable<variant<int>>::value);
-  EXPECT_TRUE(is_trivially_move_constructible<variant<int>>::value);
-  EXPECT_TRUE(is_trivially_move_assignable<variant<int>>::value);
-#endif  // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
-}
-
-TEST(VariantTest, TestVectorOfMoveonlyVariant) {
-  // Verify that variant<MoveonlyType> works correctly as a std::vector element.
-  std::vector<variant<std::unique_ptr<int>, std::string>> vec;
-  vec.push_back(absl::make_unique<int>(42));
-  vec.emplace_back("Hello");
-  vec.reserve(3);
-  auto another_vec = absl::move(vec);
-  // As a sanity check, verify vector contents.
-  ASSERT_EQ(2, another_vec.size());
-  EXPECT_EQ(42, *absl::get<std::unique_ptr<int>>(another_vec[0]));
-  EXPECT_EQ("Hello", absl::get<std::string>(another_vec[1]));
-}
-
-TEST(VariantTest, NestedVariant) {
-#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
-  static_assert(absl::is_trivially_copy_constructible<variant<int>>(), "");
-  static_assert(absl::is_trivially_copy_assignable<variant<int>>(), "");
-  static_assert(is_trivially_move_constructible<variant<int>>(), "");
-  static_assert(is_trivially_move_assignable<variant<int>>(), "");
-
-  static_assert(absl::is_trivially_copy_constructible<variant<variant<int>>>(),
-                "");
-  static_assert(absl::is_trivially_copy_assignable<variant<variant<int>>>(),
-                "");
-  static_assert(is_trivially_move_constructible<variant<variant<int>>>(), "");
-  static_assert(is_trivially_move_assignable<variant<variant<int>>>(), "");
-#endif  // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
-
-  variant<int> x(42);
-  variant<variant<int>> y(x);
-  variant<variant<int>> z(y);
-  EXPECT_TRUE(absl::holds_alternative<variant<int>>(z));
-  EXPECT_EQ(x, absl::get<variant<int>>(z));
-}
-
-struct TriviallyDestructible {
-  TriviallyDestructible(TriviallyDestructible&&) {}
-  TriviallyDestructible(const TriviallyDestructible&) {}
-  TriviallyDestructible& operator=(TriviallyDestructible&&) { return *this; }
-  TriviallyDestructible& operator=(const TriviallyDestructible&) {
-    return *this;
-  }
-};
-
-struct TriviallyMovable {
-  TriviallyMovable(TriviallyMovable&&) = default;
-  TriviallyMovable(TriviallyMovable const&) {}
-  TriviallyMovable& operator=(const TriviallyMovable&) { return *this; }
-};
-
-struct TriviallyCopyable {
-  TriviallyCopyable(const TriviallyCopyable&) = default;
-  TriviallyCopyable& operator=(const TriviallyCopyable&) { return *this; }
-};
-
-struct TriviallyMoveAssignable {
-  TriviallyMoveAssignable(TriviallyMoveAssignable&&) = default;
-  TriviallyMoveAssignable(const TriviallyMoveAssignable&) {}
-  TriviallyMoveAssignable& operator=(TriviallyMoveAssignable&&) = default;
-  TriviallyMoveAssignable& operator=(const TriviallyMoveAssignable&) {
-    return *this;
-  }
-};
-
-struct TriviallyCopyAssignable {};
-
-#if ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
-TEST(VariantTest, TestTriviality) {
-  {
-    using TrivDestVar = absl::variant<TriviallyDestructible>;
-
-    EXPECT_FALSE(is_trivially_move_constructible<TrivDestVar>::value);
-    EXPECT_FALSE(absl::is_trivially_copy_constructible<TrivDestVar>::value);
-    EXPECT_FALSE(is_trivially_move_assignable<TrivDestVar>::value);
-    EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivDestVar>::value);
-    EXPECT_TRUE(absl::is_trivially_destructible<TrivDestVar>::value);
-  }
-
-  {
-    using TrivMoveVar = absl::variant<TriviallyMovable>;
-
-    EXPECT_TRUE(is_trivially_move_constructible<TrivMoveVar>::value);
-    EXPECT_FALSE(absl::is_trivially_copy_constructible<TrivMoveVar>::value);
-    EXPECT_FALSE(is_trivially_move_assignable<TrivMoveVar>::value);
-    EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivMoveVar>::value);
-    EXPECT_TRUE(absl::is_trivially_destructible<TrivMoveVar>::value);
-  }
-
-  {
-    using TrivCopyVar = absl::variant<TriviallyCopyable>;
-
-    EXPECT_TRUE(is_trivially_move_constructible<TrivCopyVar>::value);
-    EXPECT_TRUE(absl::is_trivially_copy_constructible<TrivCopyVar>::value);
-    EXPECT_FALSE(is_trivially_move_assignable<TrivCopyVar>::value);
-    EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivCopyVar>::value);
-    EXPECT_TRUE(absl::is_trivially_destructible<TrivCopyVar>::value);
-  }
-
-  {
-    using TrivMoveAssignVar = absl::variant<TriviallyMoveAssignable>;
-
-    EXPECT_TRUE(is_trivially_move_constructible<TrivMoveAssignVar>::value);
-    EXPECT_FALSE(
-        absl::is_trivially_copy_constructible<TrivMoveAssignVar>::value);
-    EXPECT_TRUE(is_trivially_move_assignable<TrivMoveAssignVar>::value);
-    EXPECT_FALSE(absl::is_trivially_copy_assignable<TrivMoveAssignVar>::value);
-    EXPECT_TRUE(absl::is_trivially_destructible<TrivMoveAssignVar>::value);
-  }
-
-  {
-    using TrivCopyAssignVar = absl::variant<TriviallyCopyAssignable>;
-
-    EXPECT_TRUE(is_trivially_move_constructible<TrivCopyAssignVar>::value);
-    EXPECT_TRUE(
-        absl::is_trivially_copy_constructible<TrivCopyAssignVar>::value);
-    EXPECT_TRUE(is_trivially_move_assignable<TrivCopyAssignVar>::value);
-    EXPECT_TRUE(absl::is_trivially_copy_assignable<TrivCopyAssignVar>::value);
-    EXPECT_TRUE(absl::is_trivially_destructible<TrivCopyAssignVar>::value);
-  }
-}
-#endif  // ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY
-
-// To verify that absl::variant correctly use the nontrivial move ctor of its
-// member rather than use the trivial copy constructor.
-TEST(VariantTest, MoveCtorBug) {
-  // To simulate std::tuple in libstdc++.
-  struct TrivialCopyNontrivialMove {
-    TrivialCopyNontrivialMove() = default;
-    TrivialCopyNontrivialMove(const TrivialCopyNontrivialMove&) = default;
-    TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) { called = true; }
-    bool called = false;
-  };
-  {
-    using V = absl::variant<TrivialCopyNontrivialMove, int>;
-    V v1(absl::in_place_index<0>);
-    // this should invoke the move ctor, rather than the trivial copy ctor.
-    V v2(std::move(v1));
-    EXPECT_TRUE(absl::get<0>(v2).called);
-  }
-  {
-    // this case failed to compile before our fix due to a GCC bug.
-    using V = absl::variant<int, TrivialCopyNontrivialMove>;
-    V v1(absl::in_place_index<1>);
-    // this should invoke the move ctor, rather than the trivial copy ctor.
-    V v2(std::move(v1));
-    EXPECT_TRUE(absl::get<1>(v2).called);
-  }
-}
-
-}  // namespace
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // #if !defined(ABSL_USES_STD_VARIANT)
diff --git a/third_party/abseil_cpp/absl/utility/BUILD.bazel b/third_party/abseil_cpp/absl/utility/BUILD.bazel
deleted file mode 100644
index 02b2c407f8..0000000000
--- a/third_party/abseil_cpp/absl/utility/BUILD.bazel
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load(
-    "//absl:copts/configure_copts.bzl",
-    "ABSL_DEFAULT_COPTS",
-    "ABSL_DEFAULT_LINKOPTS",
-    "ABSL_TEST_COPTS",
-)
-
-package(default_visibility = ["//visibility:public"])
-
-licenses(["notice"])
-
-cc_library(
-    name = "utility",
-    hdrs = [
-        "utility.h",
-    ],
-    copts = ABSL_DEFAULT_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        "//absl/base:base_internal",
-        "//absl/base:config",
-        "//absl/meta:type_traits",
-    ],
-)
-
-cc_test(
-    name = "utility_test",
-    srcs = ["utility_test.cc"],
-    copts = ABSL_TEST_COPTS,
-    linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":utility",
-        "//absl/base:core_headers",
-        "//absl/memory",
-        "//absl/strings",
-        "@com_google_googletest//:gtest_main",
-    ],
-)
diff --git a/third_party/abseil_cpp/absl/utility/CMakeLists.txt b/third_party/abseil_cpp/absl/utility/CMakeLists.txt
deleted file mode 100644
index e1edd19aa0..0000000000
--- a/third_party/abseil_cpp/absl/utility/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Copyright 2017 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-absl_cc_library(
-  NAME
-    utility
-  HDRS
-    "utility.h"
-  COPTS
-    ${ABSL_DEFAULT_COPTS}
-  DEPS
-    absl::base_internal
-    absl::config
-    absl::type_traits
-  PUBLIC
-)
-
-absl_cc_test(
-  NAME
-    utility_test
-  SRCS
-    "utility_test.cc"
-  COPTS
-    ${ABSL_TEST_COPTS}
-  DEPS
-    absl::utility
-    absl::core_headers
-    absl::memory
-    absl::strings
-    gmock_main
-)
diff --git a/third_party/abseil_cpp/absl/utility/utility.h b/third_party/abseil_cpp/absl/utility/utility.h
deleted file mode 100644
index bf9232209a..0000000000
--- a/third_party/abseil_cpp/absl/utility/utility.h
+++ /dev/null
@@ -1,350 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// This header file contains C++11 versions of standard <utility> header
-// abstractions available within C++14 and C++17, and are designed to be drop-in
-// replacement for code compliant with C++14 and C++17.
-//
-// The following abstractions are defined:
-//
-//   * integer_sequence<T, Ints...>  == std::integer_sequence<T, Ints...>
-//   * index_sequence<Ints...>       == std::index_sequence<Ints...>
-//   * make_integer_sequence<T, N>   == std::make_integer_sequence<T, N>
-//   * make_index_sequence<N>        == std::make_index_sequence<N>
-//   * index_sequence_for<Ts...>     == std::index_sequence_for<Ts...>
-//   * apply<Functor, Tuple>         == std::apply<Functor, Tuple>
-//   * exchange<T>                   == std::exchange<T>
-//   * make_from_tuple<T>            == std::make_from_tuple<T>
-//
-// This header file also provides the tag types `in_place_t`, `in_place_type_t`,
-// and `in_place_index_t`, as well as the constant `in_place`, and
-// `constexpr` `std::move()` and `std::forward()` implementations in C++11.
-//
-// References:
-//
-//  https://en.cppreference.com/w/cpp/utility/integer_sequence
-//  https://en.cppreference.com/w/cpp/utility/apply
-//  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
-
-#ifndef ABSL_UTILITY_UTILITY_H_
-#define ABSL_UTILITY_UTILITY_H_
-
-#include <cstddef>
-#include <cstdlib>
-#include <tuple>
-#include <utility>
-
-#include "absl/base/config.h"
-#include "absl/base/internal/inline_variable.h"
-#include "absl/base/internal/invoke.h"
-#include "absl/meta/type_traits.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-
-// integer_sequence
-//
-// Class template representing a compile-time integer sequence. An instantiation
-// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
-// type through its template arguments (which is a common need when
-// working with C++11 variadic templates). `absl::integer_sequence` is designed
-// to be a drop-in replacement for C++14's `std::integer_sequence`.
-//
-// Example:
-//
-//   template< class T, T... Ints >
-//   void user_function(integer_sequence<T, Ints...>);
-//
-//   int main()
-//   {
-//     // user_function's `T` will be deduced to `int` and `Ints...`
-//     // will be deduced to `0, 1, 2, 3, 4`.
-//     user_function(make_integer_sequence<int, 5>());
-//   }
-template <typename T, T... Ints>
-struct integer_sequence {
-  using value_type = T;
-  static constexpr size_t size() noexcept { return sizeof...(Ints); }
-};
-
-// index_sequence
-//
-// A helper template for an `integer_sequence` of `size_t`,
-// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
-// `std::index_sequence`.
-template <size_t... Ints>
-using index_sequence = integer_sequence<size_t, Ints...>;
-
-namespace utility_internal {
-
-template <typename Seq, size_t SeqSize, size_t Rem>
-struct Extend;
-
-// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
-template <typename T, T... Ints, size_t SeqSize>
-struct Extend<integer_sequence<T, Ints...>, SeqSize, 0> {
-  using type = integer_sequence<T, Ints..., (Ints + SeqSize)...>;
-};
-
-template <typename T, T... Ints, size_t SeqSize>
-struct Extend<integer_sequence<T, Ints...>, SeqSize, 1> {
-  using type = integer_sequence<T, Ints..., (Ints + SeqSize)..., 2 * SeqSize>;
-};
-
-// Recursion helper for 'make_integer_sequence<T, N>'.
-// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
-template <typename T, size_t N>
-struct Gen {
-  using type =
-      typename Extend<typename Gen<T, N / 2>::type, N / 2, N % 2>::type;
-};
-
-template <typename T>
-struct Gen<T, 0> {
-  using type = integer_sequence<T>;
-};
-
-template <typename T>
-struct InPlaceTypeTag {
-  explicit InPlaceTypeTag() = delete;
-  InPlaceTypeTag(const InPlaceTypeTag&) = delete;
-  InPlaceTypeTag& operator=(const InPlaceTypeTag&) = delete;
-};
-
-template <size_t I>
-struct InPlaceIndexTag {
-  explicit InPlaceIndexTag() = delete;
-  InPlaceIndexTag(const InPlaceIndexTag&) = delete;
-  InPlaceIndexTag& operator=(const InPlaceIndexTag&) = delete;
-};
-
-}  // namespace utility_internal
-
-// Compile-time sequences of integers
-
-// make_integer_sequence
-//
-// This template alias is equivalent to
-// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
-// replacement for C++14's `std::make_integer_sequence`.
-template <typename T, T N>
-using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
-
-// make_index_sequence
-//
-// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
-// and is designed to be a drop-in replacement for C++14's
-// `std::make_index_sequence`.
-template <size_t N>
-using make_index_sequence = make_integer_sequence<size_t, N>;
-
-// index_sequence_for
-//
-// Converts a typename pack into an index sequence of the same length, and
-// is designed to be a drop-in replacement for C++14's
-// `std::index_sequence_for()`
-template <typename... Ts>
-using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
-
-// Tag types
-
-#ifdef ABSL_USES_STD_OPTIONAL
-
-using std::in_place_t;
-using std::in_place;
-
-#else  // ABSL_USES_STD_OPTIONAL
-
-// in_place_t
-//
-// Tag type used to specify in-place construction, such as with
-// `absl::optional`, designed to be a drop-in replacement for C++17's
-// `std::in_place_t`.
-struct in_place_t {};
-
-ABSL_INTERNAL_INLINE_CONSTEXPR(in_place_t, in_place, {});
-
-#endif  // ABSL_USES_STD_OPTIONAL
-
-#if defined(ABSL_USES_STD_ANY) || defined(ABSL_USES_STD_VARIANT)
-using std::in_place_type;
-using std::in_place_type_t;
-#else
-
-// in_place_type_t
-//
-// Tag type used for in-place construction when the type to construct needs to
-// be specified, such as with `absl::any`, designed to be a drop-in replacement
-// for C++17's `std::in_place_type_t`.
-template <typename T>
-using in_place_type_t = void (*)(utility_internal::InPlaceTypeTag<T>);
-
-template <typename T>
-void in_place_type(utility_internal::InPlaceTypeTag<T>) {}
-#endif  // ABSL_USES_STD_ANY || ABSL_USES_STD_VARIANT
-
-#ifdef ABSL_USES_STD_VARIANT
-using std::in_place_index;
-using std::in_place_index_t;
-#else
-
-// in_place_index_t
-//
-// Tag type used for in-place construction when the type to construct needs to
-// be specified, such as with `absl::any`, designed to be a drop-in replacement
-// for C++17's `std::in_place_index_t`.
-template <size_t I>
-using in_place_index_t = void (*)(utility_internal::InPlaceIndexTag<I>);
-
-template <size_t I>
-void in_place_index(utility_internal::InPlaceIndexTag<I>) {}
-#endif  // ABSL_USES_STD_VARIANT
-
-// Constexpr move and forward
-
-// move()
-//
-// A constexpr version of `std::move()`, designed to be a drop-in replacement
-// for C++14's `std::move()`.
-template <typename T>
-constexpr absl::remove_reference_t<T>&& move(T&& t) noexcept {
-  return static_cast<absl::remove_reference_t<T>&&>(t);
-}
-
-// forward()
-//
-// A constexpr version of `std::forward()`, designed to be a drop-in replacement
-// for C++14's `std::forward()`.
-template <typename T>
-constexpr T&& forward(
-    absl::remove_reference_t<T>& t) noexcept {  // NOLINT(runtime/references)
-  return static_cast<T&&>(t);
-}
-
-namespace utility_internal {
-// Helper method for expanding tuple into a called method.
-template <typename Functor, typename Tuple, std::size_t... Indexes>
-auto apply_helper(Functor&& functor, Tuple&& t, index_sequence<Indexes...>)
-    -> decltype(absl::base_internal::invoke(
-        absl::forward<Functor>(functor),
-        std::get<Indexes>(absl::forward<Tuple>(t))...)) {
-  return absl::base_internal::invoke(
-      absl::forward<Functor>(functor),
-      std::get<Indexes>(absl::forward<Tuple>(t))...);
-}
-
-}  // namespace utility_internal
-
-// apply
-//
-// Invokes a Callable using elements of a tuple as its arguments.
-// Each element of the tuple corresponds to an argument of the call (in order).
-// Both the Callable argument and the tuple argument are perfect-forwarded.
-// For member-function Callables, the first tuple element acts as the `this`
-// pointer. `absl::apply` is designed to be a drop-in replacement for C++17's
-// `std::apply`. Unlike C++17's `std::apply`, this is not currently `constexpr`.
-//
-// Example:
-//
-//   class Foo {
-//    public:
-//     void Bar(int);
-//   };
-//   void user_function1(int, std::string);
-//   void user_function2(std::unique_ptr<Foo>);
-//   auto user_lambda = [](int, int) {};
-//
-//   int main()
-//   {
-//       std::tuple<int, std::string> tuple1(42, "bar");
-//       // Invokes the first user function on int, std::string.
-//       absl::apply(&user_function1, tuple1);
-//
-//       std::tuple<std::unique_ptr<Foo>> tuple2(absl::make_unique<Foo>());
-//       // Invokes the user function that takes ownership of the unique
-//       // pointer.
-//       absl::apply(&user_function2, std::move(tuple2));
-//
-//       auto foo = absl::make_unique<Foo>();
-//       std::tuple<Foo*, int> tuple3(foo.get(), 42);
-//       // Invokes the method Bar on foo with one argument, 42.
-//       absl::apply(&Foo::Bar, tuple3);
-//
-//       std::tuple<int, int> tuple4(8, 9);
-//       // Invokes a lambda.
-//       absl::apply(user_lambda, tuple4);
-//   }
-template <typename Functor, typename Tuple>
-auto apply(Functor&& functor, Tuple&& t)
-    -> decltype(utility_internal::apply_helper(
-        absl::forward<Functor>(functor), absl::forward<Tuple>(t),
-        absl::make_index_sequence<std::tuple_size<
-            typename std::remove_reference<Tuple>::type>::value>{})) {
-  return utility_internal::apply_helper(
-      absl::forward<Functor>(functor), absl::forward<Tuple>(t),
-      absl::make_index_sequence<std::tuple_size<
-          typename std::remove_reference<Tuple>::type>::value>{});
-}
-
-// exchange
-//
-// Replaces the value of `obj` with `new_value` and returns the old value of
-// `obj`.  `absl::exchange` is designed to be a drop-in replacement for C++14's
-// `std::exchange`.
-//
-// Example:
-//
-//   Foo& operator=(Foo&& other) {
-//     ptr1_ = absl::exchange(other.ptr1_, nullptr);
-//     int1_ = absl::exchange(other.int1_, -1);
-//     return *this;
-//   }
-template <typename T, typename U = T>
-T exchange(T& obj, U&& new_value) {
-  T old_value = absl::move(obj);
-  obj = absl::forward<U>(new_value);
-  return old_value;
-}
-
-namespace utility_internal {
-template <typename T, typename Tuple, size_t... I>
-T make_from_tuple_impl(Tuple&& tup, absl::index_sequence<I...>) {
-  return T(std::get<I>(std::forward<Tuple>(tup))...);
-}
-}  // namespace utility_internal
-
-// make_from_tuple
-//
-// Given the template parameter type `T` and a tuple of arguments
-// `std::tuple(arg0, arg1, ..., argN)` constructs an object of type `T` as if by
-// calling `T(arg0, arg1, ..., argN)`.
-//
-// Example:
-//
-//   std::tuple<const char*, size_t> args("hello world", 5);
-//   auto s = absl::make_from_tuple<std::string>(args);
-//   assert(s == "hello");
-//
-template <typename T, typename Tuple>
-constexpr T make_from_tuple(Tuple&& tup) {
-  return utility_internal::make_from_tuple_impl<T>(
-      std::forward<Tuple>(tup),
-      absl::make_index_sequence<
-          std::tuple_size<absl::decay_t<Tuple>>::value>{});
-}
-
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_UTILITY_UTILITY_H_
diff --git a/third_party/abseil_cpp/absl/utility/utility_test.cc b/third_party/abseil_cpp/absl/utility/utility_test.cc
deleted file mode 100644
index f044ad644a..0000000000
--- a/third_party/abseil_cpp/absl/utility/utility_test.cc
+++ /dev/null
@@ -1,376 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/utility/utility.h"
-
-#include <sstream>
-#include <string>
-#include <tuple>
-#include <type_traits>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/base/attributes.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/str_cat.h"
-
-namespace {
-
-#ifdef _MSC_VER
-// Warnings for unused variables in this test are false positives.  On other
-// platforms, they are suppressed by ABSL_ATTRIBUTE_UNUSED, but that doesn't
-// work on MSVC.
-// Both the unused variables and the name length warnings are due to calls
-// to absl::make_index_sequence with very large values, creating very long type
-// names. The resulting warnings are so long they make build output unreadable.
-#pragma warning( push )
-#pragma warning( disable : 4503 )  // decorated name length exceeded
-#pragma warning( disable : 4101 )  // unreferenced local variable
-#endif  // _MSC_VER
-
-using ::testing::ElementsAre;
-using ::testing::Pointee;
-using ::testing::StaticAssertTypeEq;
-
-TEST(IntegerSequenceTest, ValueType) {
-  StaticAssertTypeEq<int, absl::integer_sequence<int>::value_type>();
-  StaticAssertTypeEq<char, absl::integer_sequence<char>::value_type>();
-}
-
-TEST(IntegerSequenceTest, Size) {
-  EXPECT_EQ(0, (absl::integer_sequence<int>::size()));
-  EXPECT_EQ(1, (absl::integer_sequence<int, 0>::size()));
-  EXPECT_EQ(1, (absl::integer_sequence<int, 1>::size()));
-  EXPECT_EQ(2, (absl::integer_sequence<int, 1, 2>::size()));
-  EXPECT_EQ(3, (absl::integer_sequence<int, 0, 1, 2>::size()));
-  EXPECT_EQ(3, (absl::integer_sequence<int, -123, 123, 456>::size()));
-  constexpr size_t sz = absl::integer_sequence<int, 0, 1>::size();
-  EXPECT_EQ(2, sz);
-}
-
-TEST(IntegerSequenceTest, MakeIndexSequence) {
-  StaticAssertTypeEq<absl::index_sequence<>, absl::make_index_sequence<0>>();
-  StaticAssertTypeEq<absl::index_sequence<0>, absl::make_index_sequence<1>>();
-  StaticAssertTypeEq<absl::index_sequence<0, 1>,
-                     absl::make_index_sequence<2>>();
-  StaticAssertTypeEq<absl::index_sequence<0, 1, 2>,
-                     absl::make_index_sequence<3>>();
-}
-
-TEST(IntegerSequenceTest, MakeIntegerSequence) {
-  StaticAssertTypeEq<absl::integer_sequence<int>,
-                     absl::make_integer_sequence<int, 0>>();
-  StaticAssertTypeEq<absl::integer_sequence<int, 0>,
-                     absl::make_integer_sequence<int, 1>>();
-  StaticAssertTypeEq<absl::integer_sequence<int, 0, 1>,
-                     absl::make_integer_sequence<int, 2>>();
-  StaticAssertTypeEq<absl::integer_sequence<int, 0, 1, 2>,
-                     absl::make_integer_sequence<int, 3>>();
-}
-
-template <typename... Ts>
-class Counter {};
-
-template <size_t... Is>
-void CountAll(absl::index_sequence<Is...>) {
-  // We only need an alias here, but instantiate a variable to silence warnings
-  // for unused typedefs in some compilers.
-  ABSL_ATTRIBUTE_UNUSED Counter<absl::make_index_sequence<Is>...> seq;
-}
-
-// This test verifies that absl::make_index_sequence can handle large arguments
-// without blowing up template instantiation stack, going OOM or taking forever
-// to compile (there is hard 15 minutes limit imposed by forge).
-TEST(IntegerSequenceTest, MakeIndexSequencePerformance) {
-  // O(log N) template instantiations.
-  // We only need an alias here, but instantiate a variable to silence warnings
-  // for unused typedefs in some compilers.
-  ABSL_ATTRIBUTE_UNUSED absl::make_index_sequence<(1 << 16) - 1> seq;
-  // O(N) template instantiations.
-  CountAll(absl::make_index_sequence<(1 << 8) - 1>());
-}
-
-template <typename F, typename Tup, size_t... Is>
-auto ApplyFromTupleImpl(F f, const Tup& tup, absl::index_sequence<Is...>)
-    -> decltype(f(std::get<Is>(tup)...)) {
-  return f(std::get<Is>(tup)...);
-}
-
-template <typename Tup>
-using TupIdxSeq = absl::make_index_sequence<std::tuple_size<Tup>::value>;
-
-template <typename F, typename Tup>
-auto ApplyFromTuple(F f, const Tup& tup)
-    -> decltype(ApplyFromTupleImpl(f, tup, TupIdxSeq<Tup>{})) {
-  return ApplyFromTupleImpl(f, tup, TupIdxSeq<Tup>{});
-}
-
-template <typename T>
-std::string Fmt(const T& x) {
-  std::ostringstream os;
-  os << x;
-  return os.str();
-}
-
-struct PoorStrCat {
-  template <typename... Args>
-  std::string operator()(const Args&... args) const {
-    std::string r;
-    for (const auto& e : {Fmt(args)...}) r += e;
-    return r;
-  }
-};
-
-template <typename Tup, size_t... Is>
-std::vector<std::string> TupStringVecImpl(const Tup& tup,
-                                          absl::index_sequence<Is...>) {
-  return {Fmt(std::get<Is>(tup))...};
-}
-
-template <typename... Ts>
-std::vector<std::string> TupStringVec(const std::tuple<Ts...>& tup) {
-  return TupStringVecImpl(tup, absl::index_sequence_for<Ts...>());
-}
-
-TEST(MakeIndexSequenceTest, ApplyFromTupleExample) {
-  PoorStrCat f{};
-  EXPECT_EQ("12abc3.14", f(12, "abc", 3.14));
-  EXPECT_EQ("12abc3.14", ApplyFromTuple(f, std::make_tuple(12, "abc", 3.14)));
-}
-
-TEST(IndexSequenceForTest, Basic) {
-  StaticAssertTypeEq<absl::index_sequence<>, absl::index_sequence_for<>>();
-  StaticAssertTypeEq<absl::index_sequence<0>, absl::index_sequence_for<int>>();
-  StaticAssertTypeEq<absl::index_sequence<0, 1, 2, 3>,
-                     absl::index_sequence_for<int, void, char, int>>();
-}
-
-TEST(IndexSequenceForTest, Example) {
-  EXPECT_THAT(TupStringVec(std::make_tuple(12, "abc", 3.14)),
-              ElementsAre("12", "abc", "3.14"));
-}
-
-int Function(int a, int b) { return a - b; }
-
-int Sink(std::unique_ptr<int> p) { return *p; }
-
-std::unique_ptr<int> Factory(int n) { return absl::make_unique<int>(n); }
-
-void NoOp() {}
-
-struct ConstFunctor {
-  int operator()(int a, int b) const { return a - b; }
-};
-
-struct MutableFunctor {
-  int operator()(int a, int b) { return a - b; }
-};
-
-struct EphemeralFunctor {
-  EphemeralFunctor() {}
-  EphemeralFunctor(const EphemeralFunctor&) {}
-  EphemeralFunctor(EphemeralFunctor&&) {}
-  int operator()(int a, int b) && { return a - b; }
-};
-
-struct OverloadedFunctor {
-  OverloadedFunctor() {}
-  OverloadedFunctor(const OverloadedFunctor&) {}
-  OverloadedFunctor(OverloadedFunctor&&) {}
-  template <typename... Args>
-  std::string operator()(const Args&... args) & {
-    return absl::StrCat("&", args...);
-  }
-  template <typename... Args>
-  std::string operator()(const Args&... args) const& {
-    return absl::StrCat("const&", args...);
-  }
-  template <typename... Args>
-  std::string operator()(const Args&... args) && {
-    return absl::StrCat("&&", args...);
-  }
-};
-
-struct Class {
-  int Method(int a, int b) { return a - b; }
-  int ConstMethod(int a, int b) const { return a - b; }
-
-  int member;
-};
-
-struct FlipFlop {
-  int ConstMethod() const { return member; }
-  FlipFlop operator*() const { return {-member}; }
-
-  int member;
-};
-
-TEST(ApplyTest, Function) {
-  EXPECT_EQ(1, absl::apply(Function, std::make_tuple(3, 2)));
-  EXPECT_EQ(1, absl::apply(&Function, std::make_tuple(3, 2)));
-}
-
-TEST(ApplyTest, NonCopyableArgument) {
-  EXPECT_EQ(42, absl::apply(Sink, std::make_tuple(absl::make_unique<int>(42))));
-}
-
-TEST(ApplyTest, NonCopyableResult) {
-  EXPECT_THAT(absl::apply(Factory, std::make_tuple(42)),
-              ::testing::Pointee(42));
-}
-
-TEST(ApplyTest, VoidResult) { absl::apply(NoOp, std::tuple<>()); }
-
-TEST(ApplyTest, ConstFunctor) {
-  EXPECT_EQ(1, absl::apply(ConstFunctor(), std::make_tuple(3, 2)));
-}
-
-TEST(ApplyTest, MutableFunctor) {
-  MutableFunctor f;
-  EXPECT_EQ(1, absl::apply(f, std::make_tuple(3, 2)));
-  EXPECT_EQ(1, absl::apply(MutableFunctor(), std::make_tuple(3, 2)));
-}
-TEST(ApplyTest, EphemeralFunctor) {
-  EphemeralFunctor f;
-  EXPECT_EQ(1, absl::apply(std::move(f), std::make_tuple(3, 2)));
-  EXPECT_EQ(1, absl::apply(EphemeralFunctor(), std::make_tuple(3, 2)));
-}
-TEST(ApplyTest, OverloadedFunctor) {
-  OverloadedFunctor f;
-  const OverloadedFunctor& cf = f;
-
-  EXPECT_EQ("&", absl::apply(f, std::tuple<>{}));
-  EXPECT_EQ("& 42", absl::apply(f, std::make_tuple(" 42")));
-
-  EXPECT_EQ("const&", absl::apply(cf, std::tuple<>{}));
-  EXPECT_EQ("const& 42", absl::apply(cf, std::make_tuple(" 42")));
-
-  EXPECT_EQ("&&", absl::apply(std::move(f), std::tuple<>{}));
-  OverloadedFunctor f2;
-  EXPECT_EQ("&& 42", absl::apply(std::move(f2), std::make_tuple(" 42")));
-}
-
-TEST(ApplyTest, ReferenceWrapper) {
-  ConstFunctor cf;
-  MutableFunctor mf;
-  EXPECT_EQ(1, absl::apply(std::cref(cf), std::make_tuple(3, 2)));
-  EXPECT_EQ(1, absl::apply(std::ref(cf), std::make_tuple(3, 2)));
-  EXPECT_EQ(1, absl::apply(std::ref(mf), std::make_tuple(3, 2)));
-}
-
-TEST(ApplyTest, MemberFunction) {
-  std::unique_ptr<Class> p(new Class);
-  std::unique_ptr<const Class> cp(new Class);
-  EXPECT_EQ(
-      1, absl::apply(&Class::Method,
-                     std::tuple<std::unique_ptr<Class>&, int, int>(p, 3, 2)));
-  EXPECT_EQ(1, absl::apply(&Class::Method,
-                           std::tuple<Class*, int, int>(p.get(), 3, 2)));
-  EXPECT_EQ(
-      1, absl::apply(&Class::Method, std::tuple<Class&, int, int>(*p, 3, 2)));
-
-  EXPECT_EQ(
-      1, absl::apply(&Class::ConstMethod,
-                     std::tuple<std::unique_ptr<Class>&, int, int>(p, 3, 2)));
-  EXPECT_EQ(1, absl::apply(&Class::ConstMethod,
-                           std::tuple<Class*, int, int>(p.get(), 3, 2)));
-  EXPECT_EQ(1, absl::apply(&Class::ConstMethod,
-                           std::tuple<Class&, int, int>(*p, 3, 2)));
-
-  EXPECT_EQ(1, absl::apply(&Class::ConstMethod,
-                           std::tuple<std::unique_ptr<const Class>&, int, int>(
-                               cp, 3, 2)));
-  EXPECT_EQ(1, absl::apply(&Class::ConstMethod,
-                           std::tuple<const Class*, int, int>(cp.get(), 3, 2)));
-  EXPECT_EQ(1, absl::apply(&Class::ConstMethod,
-                           std::tuple<const Class&, int, int>(*cp, 3, 2)));
-
-  EXPECT_EQ(1, absl::apply(&Class::Method,
-                           std::make_tuple(absl::make_unique<Class>(), 3, 2)));
-  EXPECT_EQ(1, absl::apply(&Class::ConstMethod,
-                           std::make_tuple(absl::make_unique<Class>(), 3, 2)));
-  EXPECT_EQ(
-      1, absl::apply(&Class::ConstMethod,
-                     std::make_tuple(absl::make_unique<const Class>(), 3, 2)));
-}
-
-TEST(ApplyTest, DataMember) {
-  std::unique_ptr<Class> p(new Class{42});
-  std::unique_ptr<const Class> cp(new Class{42});
-  EXPECT_EQ(
-      42, absl::apply(&Class::member, std::tuple<std::unique_ptr<Class>&>(p)));
-  EXPECT_EQ(42, absl::apply(&Class::member, std::tuple<Class&>(*p)));
-  EXPECT_EQ(42, absl::apply(&Class::member, std::tuple<Class*>(p.get())));
-
-  absl::apply(&Class::member, std::tuple<std::unique_ptr<Class>&>(p)) = 42;
-  absl::apply(&Class::member, std::tuple<Class*>(p.get())) = 42;
-  absl::apply(&Class::member, std::tuple<Class&>(*p)) = 42;
-
-  EXPECT_EQ(42, absl::apply(&Class::member,
-                            std::tuple<std::unique_ptr<const Class>&>(cp)));
-  EXPECT_EQ(42, absl::apply(&Class::member, std::tuple<const Class&>(*cp)));
-  EXPECT_EQ(42,
-            absl::apply(&Class::member, std::tuple<const Class*>(cp.get())));
-}
-
-TEST(ApplyTest, FlipFlop) {
-  FlipFlop obj = {42};
-  // This call could resolve to (obj.*&FlipFlop::ConstMethod)() or
-  // ((*obj).*&FlipFlop::ConstMethod)(). We verify that it's the former.
-  EXPECT_EQ(42, absl::apply(&FlipFlop::ConstMethod, std::make_tuple(obj)));
-  EXPECT_EQ(42, absl::apply(&FlipFlop::member, std::make_tuple(obj)));
-}
-
-TEST(ExchangeTest, MoveOnly) {
-  auto a = Factory(1);
-  EXPECT_EQ(1, *a);
-  auto b = absl::exchange(a, Factory(2));
-  EXPECT_EQ(2, *a);
-  EXPECT_EQ(1, *b);
-}
-
-TEST(MakeFromTupleTest, String) {
-  EXPECT_EQ(
-      absl::make_from_tuple<std::string>(std::make_tuple("hello world", 5)),
-      "hello");
-}
-
-TEST(MakeFromTupleTest, MoveOnlyParameter) {
-  struct S {
-    S(std::unique_ptr<int> n, std::unique_ptr<int> m) : value(*n + *m) {}
-    int value = 0;
-  };
-  auto tup =
-      std::make_tuple(absl::make_unique<int>(3), absl::make_unique<int>(4));
-  auto s = absl::make_from_tuple<S>(std::move(tup));
-  EXPECT_EQ(s.value, 7);
-}
-
-TEST(MakeFromTupleTest, NoParameters) {
-  struct S {
-    S() : value(1) {}
-    int value = 2;
-  };
-  EXPECT_EQ(absl::make_from_tuple<S>(std::make_tuple()).value, 1);
-}
-
-TEST(MakeFromTupleTest, Pair) {
-  EXPECT_EQ(
-      (absl::make_from_tuple<std::pair<bool, int>>(std::make_tuple(true, 17))),
-      std::make_pair(true, 17));
-}
-
-}  // namespace
-
diff --git a/third_party/abseil_cpp/ci/absl_alternate_options.h b/third_party/abseil_cpp/ci/absl_alternate_options.h
deleted file mode 100644
index 29b020d9fa..0000000000
--- a/third_party/abseil_cpp/ci/absl_alternate_options.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Alternate options.h file, used in continuous integration testing to exercise
-// option settings not used by default.
-
-#ifndef ABSL_BASE_OPTIONS_H_
-#define ABSL_BASE_OPTIONS_H_
-
-#define ABSL_OPTION_USE_STD_ANY 0
-#define ABSL_OPTION_USE_STD_OPTIONAL 0
-#define ABSL_OPTION_USE_STD_STRING_VIEW 0
-#define ABSL_OPTION_USE_STD_VARIANT 0
-#define ABSL_OPTION_USE_INLINE_NAMESPACE 1
-#define ABSL_OPTION_INLINE_NAMESPACE_NAME ns
-#define ABSL_OPTION_HARDENED 1
-
-#endif  // ABSL_BASE_OPTIONS_H_
diff --git a/third_party/abseil_cpp/ci/cmake_common.sh b/third_party/abseil_cpp/ci/cmake_common.sh
deleted file mode 100644
index aec8a11717..0000000000
--- a/third_party/abseil_cpp/ci/cmake_common.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2020 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# The commit of GoogleTest to be used in the CMake tests in this directory.
-# Keep this in sync with the commit in the WORKSPACE file.
-readonly ABSL_GOOGLETEST_COMMIT="8567b09290fe402cf01923e2131c5635b8ed851b"
-
-# Avoid depending on GitHub by looking for a cached copy of the commit first.
-if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  ABSL_GOOGLETEST_DOWNLOAD_URL="file:///distdir/${ABSL_GOOGLETEST_COMMIT}.zip"
-else
-  ABSL_GOOGLETEST_DOWNLOAD_URL="https://github.com/google/googletest/archive/${ABSL_GOOGLETEST_COMMIT}.zip"
-fi
diff --git a/third_party/abseil_cpp/ci/cmake_install_test.sh b/third_party/abseil_cpp/ci/cmake_install_test.sh
deleted file mode 100755
index 5bf540c538..0000000000
--- a/third_party/abseil_cpp/ci/cmake_install_test.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-source "${ABSEIL_ROOT}/ci/cmake_common.sh"
-
-source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
-readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER}
-
-time docker run \
-    --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \
-    --workdir=/abseil-cpp \
-    --tmpfs=/buildfs:exec \
-    --cap-add=SYS_PTRACE \
-    --rm \
-    -e CFLAGS="-Werror" \
-    -e CXXFLAGS="-Werror" \
-    ${DOCKER_CONTAINER} \
-    /bin/bash CMake/install_test_project/test.sh $@
diff --git a/third_party/abseil_cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh b/third_party/abseil_cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh
deleted file mode 100755
index ffbb8327e7..0000000000
--- a/third_party/abseil_cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script that can be invoked to test abseil-cpp in a hermetic environment
-# using a Docker image on Linux. You must have Docker installed to use this
-# script.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-if [[ -z ${STD:-} ]]; then
-  STD="c++11 c++14 c++17 c++20"
-fi
-
-if [[ -z ${COMPILATION_MODE:-} ]]; then
-  COMPILATION_MODE="fastbuild opt"
-fi
-
-if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
-fi
-
-source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
-readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER}
-
-# USE_BAZEL_CACHE=1 only works on Kokoro.
-# Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
-  # Bazel doesn't track changes to tools outside of the workspace
-  # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
-  # the cache key. Hashing the key is to make it shorter and url-safe.
-  container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-for std in ${STD}; do
-  for compilation_mode in ${COMPILATION_MODE}; do
-    for exceptions_mode in ${EXCEPTIONS_MODE}; do
-      echo "--------------------------------------------------------------------"
-      time docker run \
-        --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \
-        --workdir=/abseil-cpp \
-        --cap-add=SYS_PTRACE \
-        --rm \
-        -e CC="/opt/llvm/clang/bin/clang" \
-        -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
-        -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib" \
-        -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/c++/v1" \
-        ${DOCKER_EXTRA_ARGS:-} \
-        ${DOCKER_CONTAINER} \
-        /usr/local/bin/bazel test ... \
-          --compilation_mode="${compilation_mode}" \
-          --copt="${exceptions_mode}" \
-          --copt="-fsanitize=address" \
-          --copt="-fsanitize=float-divide-by-zero" \
-          --copt="-fsanitize=nullability" \
-          --copt="-fsanitize=undefined" \
-          --copt="-fno-sanitize-blacklist" \
-          --copt=-Werror \
-          --keep_going \
-          --linkopt="-fsanitize=address" \
-          --linkopt="-fsanitize-link-c++-runtime" \
-          --show_timestamps \
-          --test_env="ASAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
-          --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
-          --test_env="UBSAN_OPTIONS=print_stacktrace=1" \
-          --test_env="UBSAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
-          --test_output=errors \
-          --test_tag_filters="-benchmark,-noasan" \
-          ${BAZEL_EXTRA_ARGS:-}
-    done
-  done
-done
diff --git a/third_party/abseil_cpp/ci/linux_clang-latest_libcxx_bazel.sh b/third_party/abseil_cpp/ci/linux_clang-latest_libcxx_bazel.sh
deleted file mode 100755
index f6a2221e8a..0000000000
--- a/third_party/abseil_cpp/ci/linux_clang-latest_libcxx_bazel.sh
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script that can be invoked to test abseil-cpp in a hermetic environment
-# using a Docker image on Linux. You must have Docker installed to use this
-# script.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-if [[ -z ${STD:-} ]]; then
-  STD="c++11 c++14 c++17 c++20"
-fi
-
-if [[ -z ${COMPILATION_MODE:-} ]]; then
-  COMPILATION_MODE="fastbuild opt"
-fi
-
-if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
-fi
-
-source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
-readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER}
-
-# USE_BAZEL_CACHE=1 only works on Kokoro.
-# Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
-  # Bazel doesn't track changes to tools outside of the workspace
-  # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
-  # the cache key. Hashing the key is to make it shorter and url-safe.
-  container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-for std in ${STD}; do
-  for compilation_mode in ${COMPILATION_MODE}; do
-    for exceptions_mode in ${EXCEPTIONS_MODE}; do
-      echo "--------------------------------------------------------------------"
-      time docker run \
-        --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp-ro,readonly \
-        --tmpfs=/abseil-cpp \
-        --workdir=/abseil-cpp \
-        --cap-add=SYS_PTRACE \
-        --rm \
-        -e CC="/opt/llvm/clang/bin/clang" \
-        -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
-        -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib" \
-        -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/c++/v1" \
-        ${DOCKER_EXTRA_ARGS:-} \
-        ${DOCKER_CONTAINER} \
-        /bin/sh -c "
-          cp -r /abseil-cpp-ro/* /abseil-cpp/
-          if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
-            cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
-          fi
-          /usr/local/bin/bazel test ... \
-            --compilation_mode=\"${compilation_mode}\" \
-            --copt=\"${exceptions_mode}\" \
-            --copt=-Werror \
-            --define=\"absl=1\" \
-            --keep_going \
-            --show_timestamps \
-            --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \
-            --test_env=\"TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo\" \
-            --test_output=errors \
-            --test_tag_filters=-benchmark \
-            ${BAZEL_EXTRA_ARGS:-}"
-    done
-  done
-done
diff --git a/third_party/abseil_cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/third_party/abseil_cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh
deleted file mode 100755
index e70e8214cd..0000000000
--- a/third_party/abseil_cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script that can be invoked to test abseil-cpp in a hermetic environment
-# using a Docker image on Linux. You must have Docker installed to use this
-# script.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-if [[ -z ${STD:-} ]]; then
-  STD="c++11 c++14 c++17 c++20"
-fi
-
-if [[ -z ${COMPILATION_MODE:-} ]]; then
-  COMPILATION_MODE="fastbuild opt"
-fi
-
-if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
-fi
-
-source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
-readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER}
-
-# USE_BAZEL_CACHE=1 only works on Kokoro.
-# Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
-  # Bazel doesn't track changes to tools outside of the workspace
-  # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
-  # the cache key. Hashing the key is to make it shorter and url-safe.
-  container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-for std in ${STD}; do
-  for compilation_mode in ${COMPILATION_MODE}; do
-    for exceptions_mode in ${EXCEPTIONS_MODE}; do
-      echo "--------------------------------------------------------------------"
-      time docker run \
-        --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \
-        --workdir=/abseil-cpp \
-        --cap-add=SYS_PTRACE \
-        --rm \
-        -e CC="/opt/llvm/clang/bin/clang" \
-        -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
-        -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx-tsan/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx-tsan/lib" \
-        -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx-tsan/include/c++/v1" \
-        ${DOCKER_EXTRA_ARGS:-} \
-        ${DOCKER_CONTAINER} \
-        /usr/local/bin/bazel test ... \
-          --build_tag_filters="-notsan" \
-          --compilation_mode="${compilation_mode}" \
-          --copt="${exceptions_mode}" \
-          --copt="-fsanitize=thread" \
-          --copt="-fno-sanitize-blacklist" \
-          --copt=-Werror \
-          --keep_going \
-          --linkopt="-fsanitize=thread" \
-          --show_timestamps \
-          --test_env="TSAN_OPTIONS=report_atomic_races=0" \
-          --test_env="TSAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
-          --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
-          --test_output=errors \
-          --test_tag_filters="-benchmark,-notsan" \
-          ${BAZEL_EXTRA_ARGS:-}
-    done
-  done
-done
diff --git a/third_party/abseil_cpp/ci/linux_clang-latest_libstdcxx_bazel.sh b/third_party/abseil_cpp/ci/linux_clang-latest_libstdcxx_bazel.sh
deleted file mode 100755
index 0986ff40cc..0000000000
--- a/third_party/abseil_cpp/ci/linux_clang-latest_libstdcxx_bazel.sh
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script that can be invoked to test abseil-cpp in a hermetic environment
-# using a Docker image on Linux. You must have Docker installed to use this
-# script.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-if [[ -z ${STD:-} ]]; then
-  STD="c++11 c++14 c++17 c++20"
-fi
-
-if [[ -z ${COMPILATION_MODE:-} ]]; then
-  COMPILATION_MODE="fastbuild opt"
-fi
-
-if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
-fi
-
-source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
-readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER}
-
-# USE_BAZEL_CACHE=1 only works on Kokoro.
-# Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
-  # Bazel doesn't track changes to tools outside of the workspace
-  # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
-  # the cache key. Hashing the key is to make it shorter and url-safe.
-  container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-for std in ${STD}; do
-  for compilation_mode in ${COMPILATION_MODE}; do
-    for exceptions_mode in ${EXCEPTIONS_MODE}; do
-      echo "--------------------------------------------------------------------"
-      time docker run \
-        --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \
-        --workdir=/abseil-cpp \
-        --cap-add=SYS_PTRACE \
-        --rm \
-        -e CC="/opt/llvm/clang/bin/clang" \
-        -e BAZEL_CXXOPTS="-std=${std}" \
-        ${DOCKER_EXTRA_ARGS:-} \
-        ${DOCKER_CONTAINER} \
-        /usr/local/bin/bazel test ... \
-          --compilation_mode="${compilation_mode}" \
-          --copt="--gcc-toolchain=/usr/local" \
-          --copt="${exceptions_mode}" \
-          --copt=-Werror \
-          --define="absl=1" \
-          --keep_going \
-          --linkopt="--gcc-toolchain=/usr/local" \
-          --show_timestamps \
-          --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" \
-          --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
-          --test_output=errors \
-          --test_tag_filters=-benchmark \
-          ${BAZEL_EXTRA_ARGS:-}
-    done
-  done
-done
diff --git a/third_party/abseil_cpp/ci/linux_docker_containers.sh b/third_party/abseil_cpp/ci/linux_docker_containers.sh
deleted file mode 100644
index 1c29d9a1f1..0000000000
--- a/third_party/abseil_cpp/ci/linux_docker_containers.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# The file contains Docker container identifiers currently used by test scripts.
-# Test scripts should source this file to get the identifiers.
-
-readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20201026"
-readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20201008"
-readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20201008"
-readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20201015"
diff --git a/third_party/abseil_cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh b/third_party/abseil_cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh
deleted file mode 100755
index 224aef81cb..0000000000
--- a/third_party/abseil_cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script that can be invoked to test abseil-cpp in a hermetic environment
-# using a Docker image on Linux. You must have Docker installed to use this
-# script.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-if [[ -z ${STD:-} ]]; then
-  STD="c++11 c++14"
-fi
-
-if [[ -z ${COMPILATION_MODE:-} ]]; then
-  COMPILATION_MODE="fastbuild opt"
-fi
-
-if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
-fi
-
-source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
-readonly DOCKER_CONTAINER=${LINUX_GCC_FLOOR_CONTAINER}
-
-# USE_BAZEL_CACHE=1 only works on Kokoro.
-# Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
-  DOCKER_EXTRA_ARGS="--volume=${KOKORO_KEYSTORE_DIR}:/keystore:ro ${DOCKER_EXTRA_ARGS:-}"
-  # Bazel doesn't track changes to tools outside of the workspace
-  # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
-  # the cache key. Hashing the key is to make it shorter and url-safe.
-  container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--volume=${KOKORO_GFILE_DIR}/distdir:/distdir:ro ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-for std in ${STD}; do
-  for compilation_mode in ${COMPILATION_MODE}; do
-    for exceptions_mode in ${EXCEPTIONS_MODE}; do
-      echo "--------------------------------------------------------------------"
-      time docker run \
-        --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \
-        --workdir=/abseil-cpp \
-        --cap-add=SYS_PTRACE \
-        --rm \
-        -e CC="/usr/local/bin/gcc" \
-        -e BAZEL_CXXOPTS="-std=${std}" \
-        ${DOCKER_EXTRA_ARGS:-} \
-        ${DOCKER_CONTAINER} \
-        /usr/local/bin/bazel test ... \
-          --compilation_mode="${compilation_mode}" \
-          --copt="${exceptions_mode}" \
-          --copt=-Werror \
-          --define="absl=1" \
-          --keep_going \
-          --show_timestamps \
-          --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" \
-          --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
-          --test_output=errors \
-          --test_tag_filters=-benchmark \
-          ${BAZEL_EXTRA_ARGS:-}
-    done
-  done
-done
diff --git a/third_party/abseil_cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh b/third_party/abseil_cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh
deleted file mode 100755
index 37d89d9f82..0000000000
--- a/third_party/abseil_cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script that can be invoked to test abseil-cpp in a hermetic environment
-# using a Docker image on Linux. You must have Docker installed to use this
-# script.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-if [[ -z ${STD:-} ]]; then
-  STD="c++11 c++14 c++17 c++20"
-fi
-
-if [[ -z ${COMPILATION_MODE:-} ]]; then
-  COMPILATION_MODE="fastbuild opt"
-fi
-
-if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
-fi
-
-source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
-readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER}
-
-# USE_BAZEL_CACHE=1 only works on Kokoro.
-# Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
-  # Bazel doesn't track changes to tools outside of the workspace
-  # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
-  # the cache key. Hashing the key is to make it shorter and url-safe.
-  container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
-fi
-
-for std in ${STD}; do
-  for compilation_mode in ${COMPILATION_MODE}; do
-    for exceptions_mode in ${EXCEPTIONS_MODE}; do
-      echo "--------------------------------------------------------------------"
-      time docker run \
-        --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp-ro,readonly \
-        --tmpfs=/abseil-cpp \
-        --workdir=/abseil-cpp \
-        --cap-add=SYS_PTRACE \
-        --rm \
-        -e CC="/usr/local/bin/gcc" \
-        -e BAZEL_CXXOPTS="-std=${std}" \
-        ${DOCKER_EXTRA_ARGS:-} \
-        ${DOCKER_CONTAINER} \
-        /bin/sh -c "
-          cp -r /abseil-cpp-ro/* /abseil-cpp/
-          if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
-            cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
-          fi
-          /usr/local/bin/bazel test ... \
-            --compilation_mode=\"${compilation_mode}\" \
-            --copt=\"${exceptions_mode}\" \
-            --copt=-Werror \
-            --define=\"absl=1\" \
-            --keep_going \
-            --show_timestamps \
-            --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \
-            --test_env=\"TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo\" \
-            --test_output=errors \
-            --test_tag_filters=-benchmark \
-            ${BAZEL_EXTRA_ARGS:-}"
-    done
-  done
-done
diff --git a/third_party/abseil_cpp/ci/linux_gcc-latest_libstdcxx_cmake.sh b/third_party/abseil_cpp/ci/linux_gcc-latest_libstdcxx_cmake.sh
deleted file mode 100755
index ed9cfa3852..0000000000
--- a/third_party/abseil_cpp/ci/linux_gcc-latest_libstdcxx_cmake.sh
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-source "${ABSEIL_ROOT}/ci/cmake_common.sh"
-
-if [[ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]]; then
-  ABSL_CMAKE_CXX_STANDARDS="11 14 17 20"
-fi
-
-if [[ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]]; then
-  ABSL_CMAKE_BUILD_TYPES="Debug Release"
-fi
-
-if [[ -z ${ABSL_CMAKE_BUILD_SHARED:-} ]]; then
-  ABSL_CMAKE_BUILD_SHARED="OFF ON"
-fi
-
-source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
-readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER}
-
-for std in ${ABSL_CMAKE_CXX_STANDARDS}; do
-  for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do
-    for build_shared in ${ABSL_CMAKE_BUILD_SHARED}; do
-      time docker run \
-        --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \
-        --tmpfs=/buildfs:exec \
-        --workdir=/buildfs \
-        --cap-add=SYS_PTRACE \
-        --rm \
-        -e CFLAGS="-Werror" \
-        -e CXXFLAGS="-Werror" \
-        ${DOCKER_EXTRA_ARGS:-} \
-        "${DOCKER_CONTAINER}" \
-        /bin/bash -c "
-          cmake /abseil-cpp \
-            -DABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL} \
-            -DABSL_RUN_TESTS=ON \
-            -DBUILD_SHARED_LIBS=${build_shared} \
-            -DCMAKE_BUILD_TYPE=${compilation_mode} \
-            -DCMAKE_CXX_STANDARD=${std} \
-            -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \
-          make -j$(nproc) && \
-          ctest -j$(nproc) --output-on-failure"
-    done
-  done
-done
diff --git a/third_party/abseil_cpp/ci/linux_gcc_alpine_cmake.sh b/third_party/abseil_cpp/ci/linux_gcc_alpine_cmake.sh
deleted file mode 100755
index 31310ac791..0000000000
--- a/third_party/abseil_cpp/ci/linux_gcc_alpine_cmake.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-source "${ABSEIL_ROOT}/ci/cmake_common.sh"
-
-if [[ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]]; then
-  ABSL_CMAKE_CXX_STANDARDS="11 14 17"
-fi
-
-if [[ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]]; then
-  ABSL_CMAKE_BUILD_TYPES="Debug Release"
-fi
-
-if [[ -z ${ABSL_CMAKE_BUILD_SHARED:-} ]]; then
-  ABSL_CMAKE_BUILD_SHARED="OFF ON"
-fi
-
-source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
-readonly DOCKER_CONTAINER=${LINUX_ALPINE_CONTAINER}
-
-for std in ${ABSL_CMAKE_CXX_STANDARDS}; do
-  for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do
-    for build_shared in ${ABSL_CMAKE_BUILD_SHARED}; do
-      time docker run \
-        --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \
-        --tmpfs=/buildfs:exec \
-        --workdir=/buildfs \
-        --cap-add=SYS_PTRACE \
-        --rm \
-        -e CFLAGS="-Werror" \
-        -e CXXFLAGS="-Werror" \
-        ${DOCKER_EXTRA_ARGS:-} \
-        "${DOCKER_CONTAINER}" \
-        /bin/sh -c "
-          cmake /abseil-cpp \
-            -DABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL} \
-            -DABSL_RUN_TESTS=ON \
-            -DCMAKE_BUILD_TYPE=${compilation_mode} \
-            -DCMAKE_CXX_STANDARD=${std} \
-            -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \
-          make -j$(nproc) && \
-          ctest -j$(nproc) --output-on-failure"
-    done
-  done
-done
diff --git a/third_party/abseil_cpp/ci/macos_xcode_bazel.sh b/third_party/abseil_cpp/ci/macos_xcode_bazel.sh
deleted file mode 100755
index 738adf9476..0000000000
--- a/third_party/abseil_cpp/ci/macos_xcode_bazel.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script is invoked on Kokoro to test Abseil on macOS.
-# It is not hermetic and may break when Kokoro is updated.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
-fi
-
-# If we are running on Kokoro, check for a versioned Bazel binary.
-KOKORO_GFILE_BAZEL_BIN="bazel-2.0.0-darwin-x86_64"
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then
-  BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}"
-  chmod +x ${BAZEL_BIN}
-else
-  BAZEL_BIN="bazel"
-fi
-
-# Print the compiler and Bazel versions.
-echo "---------------"
-gcc -v
-echo "---------------"
-${BAZEL_BIN} version
-echo "---------------"
-
-cd ${ABSEIL_ROOT}
-
-if [[ -n "${ALTERNATE_OPTIONS:-}" ]]; then
-  cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
-fi
-
-${BAZEL_BIN} test ... \
-  --copt=-Werror \
-  --keep_going \
-  --show_timestamps \
-  --test_env="TZDIR=${ABSEIL_ROOT}/absl/time/internal/cctz/testdata/zoneinfo" \
-  --test_output=errors \
-  --test_tag_filters=-benchmark
diff --git a/third_party/abseil_cpp/ci/macos_xcode_cmake.sh b/third_party/abseil_cpp/ci/macos_xcode_cmake.sh
deleted file mode 100755
index 0847b3ea78..0000000000
--- a/third_party/abseil_cpp/ci/macos_xcode_cmake.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Abseil Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -euox pipefail
-
-if [[ -z ${ABSEIL_ROOT:-} ]]; then
-  ABSEIL_ROOT="$(dirname ${0})/.."
-fi
-ABSEIL_ROOT=$(realpath ${ABSEIL_ROOT})
-
-source "${ABSEIL_ROOT}/ci/cmake_common.sh"
-
-# The MacOS build doesn't run in a docker container, so we have to override ABSL_GOOGLETEST_DOWNLOAD_URL.
-if [[ -r "${KOKORO_GFILE_DIR}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then
-  ABSL_GOOGLETEST_DOWNLOAD_URL="file://${KOKORO_GFILE_DIR}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip"
-fi
-
-if [[ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]]; then
-  ABSL_CMAKE_BUILD_TYPES="Debug"
-fi
-
-if [[ -z ${ABSL_CMAKE_BUILD_SHARED:-} ]]; then
-  ABSL_CMAKE_BUILD_SHARED="OFF ON"
-fi
-
-for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do
-  for build_shared in ${ABSL_CMAKE_BUILD_SHARED}; do
-    BUILD_DIR=$(mktemp -d ${compilation_mode}.XXXXXXXX)
-    cd ${BUILD_DIR}
-
-    # TODO(absl-team): Enable -Werror once all warnings are fixed.
-    time cmake ${ABSEIL_ROOT} \
-      -GXcode \
-      -DBUILD_SHARED_LIBS=${build_shared} \
-      -DCMAKE_BUILD_TYPE=${compilation_mode} \
-      -DCMAKE_CXX_STANDARD=11 \
-      -DCMAKE_MODULE_LINKER_FLAGS="-Wl,--no-undefined" \
-      -DABSL_GOOGLETEST_DOWNLOAD_URL="${ABSL_GOOGLETEST_DOWNLOAD_URL}" \
-      -DABSL_RUN_TESTS=ON
-    time cmake --build .
-    time ctest -C ${compilation_mode} --output-on-failure
-  done
-done
diff --git a/third_party/abseil_cpp/conanfile.py b/third_party/abseil_cpp/conanfile.py
deleted file mode 100755
index 926ec5ccd6..0000000000
--- a/third_party/abseil_cpp/conanfile.py
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Note: Conan is supported on a best-effort basis. Abseil doesn't use Conan
-# internally, so we won't know if it stops working. We may ask community
-# members to help us debug any problems that arise.
-
-from conans import ConanFile, CMake, tools
-from conans.errors import ConanInvalidConfiguration
-from conans.model.version import Version
-
-
-class AbseilConan(ConanFile):
-    name = "abseil"
-    url = "https://github.com/abseil/abseil-cpp"
-    homepage = url
-    author = "Abseil <abseil-io@googlegroups.com>"
-    description = "Abseil Common Libraries (C++) from Google"
-    license = "Apache-2.0"
-    topics = ("conan", "abseil", "abseil-cpp", "google", "common-libraries")
-    exports = ["LICENSE"]
-    exports_sources = ["CMakeLists.txt", "CMake/*", "absl/*"]
-    generators = "cmake"
-    settings = "os", "arch", "compiler", "build_type"
-
-    def configure(self):
-        if self.settings.os == "Windows" and \
-           self.settings.compiler == "Visual Studio" and \
-           Version(self.settings.compiler.version.value) < "14":
-            raise ConanInvalidConfiguration("Abseil does not support MSVC < 14")
-
-    def build(self):
-        tools.replace_in_file("CMakeLists.txt", "project(absl CXX)", "project(absl CXX)\ninclude(conanbuildinfo.cmake)\nconan_basic_setup()")
-        cmake = CMake(self)
-        cmake.definitions["BUILD_TESTING"] = False
-        cmake.configure()
-        cmake.build()
-
-    def package(self):
-        self.copy("LICENSE", dst="licenses")
-        self.copy("*.h", dst="include", src=".")
-        self.copy("*.inc", dst="include", src=".")
-        self.copy("*.a", dst="lib", src=".", keep_path=False)
-        self.copy("*.lib", dst="lib", src=".", keep_path=False)
-
-    def package_info(self):
-        if self.settings.os == "Linux":
-            self.cpp_info.libs = ["-Wl,--start-group"]
-        self.cpp_info.libs.extend(tools.collect_libs(self))
-        if self.settings.os == "Linux":
-            self.cpp_info.libs.extend(["-Wl,--end-group", "pthread"])
diff --git a/third_party/abseil_cpp/default.nix b/third_party/abseil_cpp/default.nix
deleted file mode 100644
index 66ab4f6ba1..0000000000
--- a/third_party/abseil_cpp/default.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-{ pkgs, lib, ... }:
-
-let inherit (pkgs) cmake fullLlvm11Stdenv;
-in pkgs.abseil-cpp.override {
-  stdenv = fullLlvm11Stdenv;
-  cxxStandard = "17";
-}
-
-/* TODO(tazjin): update abseil subtree
-
-fullLlvm11Stdenv.mkDerivation rec {
-  pname = "abseil-cpp";
-  version = "20200519-768eb2ca+tvl-1";
-  src = ./.;
-  nativeBuildInputs = [ cmake ];
-  # TODO: run tests
-  # doCheck = true;
-
-  cmakeFlags = [
-    "-DCMAKE_CXX_STANDARD=17"
-    #"-DABSL_RUN_TESTS=1"
-  ];
-
-  meta = with lib; {
-    description = "An open-source collection of C++ code designed to augment the C++ standard library";
-    homepage = https://abseil.io/;
-    license = licenses.asl20;
-    maintainers = [ maintainers.andersk ];
-  };
-}
-*/
diff --git a/third_party/agenix/default.nix b/third_party/agenix/default.nix
index 91fd9576b0..1462050ef1 100644
--- a/third_party/agenix/default.nix
+++ b/third_party/agenix/default.nix
@@ -1,15 +1,15 @@
-{ pkgs, ... }:
+{ pkgs, depot, ... }:
 
 let
-  commit = "52ea2f8c3231cc2b5302fa28c63588aacb77ea29";
-  src = builtins.fetchTarball {
-    url = "https://github.com/ryantm/agenix/archive/${commit}.tar.gz";
-    sha256 = "1sqgbriwmvxcmqp0zbk7873psk9g60a53fgrr9p0jafki5zzgvdx";
-  };
+  src = depot.third_party.sources.agenix;
+
   agenix = import src {
     inherit pkgs;
   };
-in {
+in
+{
   inherit src;
   cli = agenix.agenix;
+
+  meta.ci.targets = [ "cli" ];
 }
diff --git a/third_party/alsi/OWNERS b/third_party/alsi/OWNERS
deleted file mode 100644
index 79a422fcde..0000000000
--- a/third_party/alsi/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherit: true
-owners:
- - grfn
diff --git a/third_party/alsi/default.nix b/third_party/alsi/default.nix
deleted file mode 100644
index 8969374176..0000000000
--- a/third_party/alsi/default.nix
+++ /dev/null
@@ -1,25 +0,0 @@
-{ pkgs, ... }:
-
-with pkgs;
-
-stdenv.mkDerivation {
-  name = "alsi";
-  pname = "alsi";
-  version = "0.4.8";
-
-  src = fetchFromGitHub {
-    owner = "trizen";
-    repo = "alsi";
-    rev = "fe2a925caad38d4cc7afe10d74ba60c5db09ee66";
-    sha256 = "060xlalfclrda5f1h3svj4v2gr19mdrsc62vrg7hgii0f3lib7j5";
-  };
-
-  buildInputs = [
-    (perl.withPackages (ps: with ps; [ DataDump ]))
-  ];
-
-  installPhase = ''
-    mkdir -p $out/bin
-    cp alsi $out/bin/alsi
-  '';
-}
diff --git a/third_party/arion/OWNERS b/third_party/arion/OWNERS
deleted file mode 100644
index 79a422fcde..0000000000
--- a/third_party/arion/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherit: true
-owners:
- - grfn
diff --git a/third_party/arion/default.nix b/third_party/arion/default.nix
deleted file mode 100644
index 16cd92ea3f..0000000000
--- a/third_party/arion/default.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-{ pkgs, ... }:
-
-(import (pkgs.fetchFromGitHub {
-  owner = "hercules-ci";
-  repo = "arion";
-  rev = "db6d4d7490dff363de60cebbece3ae9361e3ce43";
-  sha256 = "0d8nqmc7fjshigax2g47ips262v8ml27x0ksq59kmprgb7ckzi5l";
-}) { inherit pkgs; }).arion
diff --git a/third_party/bat_syntaxes/default.nix b/third_party/bat_syntaxes/default.nix
index 2b7d025fae..15af130916 100644
--- a/third_party/bat_syntaxes/default.nix
+++ b/third_party/bat_syntaxes/default.nix
@@ -7,8 +7,9 @@
 { pkgs, ... }:
 
 let
-  inherit (pkgs) bat runCommandNoCC;
-in runCommandNoCC "bat-syntaxes.bin" {} ''
+  inherit (pkgs) bat runCommand;
+in
+runCommand "bat-syntaxes.bin" { } ''
   export HOME=$PWD
   mkdir -p .config/bat/syntaxes
   cp ${./Prolog.sublime-syntax} .config/bat/syntaxes
diff --git a/third_party/bufbuild/default.nix b/third_party/bufbuild/default.nix
deleted file mode 100644
index 12683b1062..0000000000
--- a/third_party/bufbuild/default.nix
+++ /dev/null
@@ -1,29 +0,0 @@
-# buf.build is a Protobuf linter and breaking change detector.
-# Several binaries are produced.
-{ pkgs, lib, ... }:
-
-pkgs.buildGoModule {
-  pname = "buf";
-  version = "v0.20.1";
-  vendorSha256 = "1gg5c7aiqb4w1zxwsraxxpln33xkmkzlp1h69xgi9i08zvrfipqs";
-
-  src = pkgs.fetchFromGitHub {
-    owner = "bufbuild";
-    repo = "buf";
-    rev = "5e8bf4c800de911764ffdf8d2188b7f6f54476e4";
-    sha256 = "1rni5swfnb4sbrd9rls4mc3902xhqrlsja96lfcdfjzx08g6kg20";
-  };
-
-  doCheck = false;
-
-  # TODO(riking): postinstall produce shell completions for bash, fish, zsh
-  # bin/buf bash-completion
-  # bin/buf zsh-completion
-  # # bin/buf manpages # not yet functional
-
-  meta = with lib; {
-    description = "Protobuf linter and breaking change detector";
-    homepage = "https://buf.build/docs/introduction";
-    license = licenses.asl20;
-  };
-}
diff --git a/third_party/buzz/default.nix b/third_party/buzz/default.nix
deleted file mode 100644
index 2af9df912e..0000000000
--- a/third_party/buzz/default.nix
+++ /dev/null
@@ -1,30 +0,0 @@
-{ depot, pkgs, ... }:
-
-depot.third_party.naersk.buildPackage {
-  src = pkgs.fetchFromGitHub {
-    owner = "jonhoo";
-    repo = "buzz";
-    rev = "02479643ed1b0325050245dbb3b70411b8cffb7a";
-    sha256 = "1spklfv02qlinlail5rmhh1c4926gyrkr2ydd9g6z919rxkl0ywk";
-  };
-
-  buildInputs = with pkgs; [
-    pkgconfig
-    dbus_libs
-    glib
-    openssl
-    cairo
-    pango
-    atk
-    gdk-pixbuf
-    gtk3
-    dbus-glib
-    libappindicator-gtk3
-    llvmPackages.llvm
-    llvmPackages.bintools
-    llvmPackages.clang
-    llvmPackages.libclang
-  ];
-
-  LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib/libclang.so";
-}
diff --git a/third_party/cgit/AUTHORS b/third_party/cgit/AUTHORS
index 031de338f9..256ea6b3bc 100644
--- a/third_party/cgit/AUTHORS
+++ b/third_party/cgit/AUTHORS
@@ -1,5 +1,5 @@
 Maintainer:
-	Jason A. Donenfeld <Jason@zx2c4.com>
+	June McEnroe <june@causal.agency>
 
 Contributors:
 	Jason A. Donenfeld <Jason@zx2c4.com>
diff --git a/third_party/cgit/Makefile b/third_party/cgit/Makefile
index 9153a393f7..1a7f1f6381 100644
--- a/third_party/cgit/Makefile
+++ b/third_party/cgit/Makefile
@@ -1,6 +1,6 @@
 all::
 
-CGIT_VERSION = v1.2.3
+CGIT_VERSION = 1.4.1
 CGIT_SCRIPT_NAME = cgit.cgi
 CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
 CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
@@ -14,7 +14,7 @@ htmldir = $(docdir)
 pdfdir = $(docdir)
 mandir = $(prefix)/share/man
 SHA1_HEADER = <openssl/sha.h>
-GIT_VER = 2.34.0
+GIT_VER = 2.41.0
 GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.xz
 INSTALL = install
 COPYTREE = cp -r
@@ -88,7 +88,6 @@ install: all
 	$(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH)
 	$(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
 	$(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
-	$(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico
 	$(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt
 	$(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir)
 	$(COPYTREE) filters/* $(DESTDIR)$(filterdir)
@@ -111,7 +110,6 @@ uninstall:
 	rm -f $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
 	rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
 	rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
-	rm -f $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico
 
 uninstall-doc: uninstall-man uninstall-html uninstall-pdf
 
diff --git a/third_party/cgit/README b/third_party/cgit/README
index 7a6b4a40ca..2094b87df0 100644
--- a/third_party/cgit/README
+++ b/third_party/cgit/README
@@ -1,8 +1,9 @@
-cgit - CGI for Git
-==================
+cgit-pink - CGI for Git
+=======================
 
-This is an attempt to create a fast web interface for the Git SCM, using a
-built-in cache to decrease server I/O pressure.
+This is a fork of cgit, an attempt to create a fast web interface
+for the Git SCM, using a built-in cache to decrease server I/O
+pressure.
 
 Installation
 ------------
@@ -32,18 +33,6 @@ This will install `cgit.cgi` and `cgit.css` into `/var/www/htdocs/cgit`. You
 can configure this location (and a few other things) by providing a `cgit.conf`
 file (see the Makefile for details).
 
-If you'd like to compile without Lua support, you may use:
-
-    $ make NO_LUA=1
-
-And if you'd like to specify a Lua implementation, you may use:
-
-    $ make LUA_PKGCONFIG=lua5.1
-
-If this is not specified, the Lua implementation will be auto-detected,
-preferring LuaJIT if many are present. Acceptable values are generally "lua",
-"luajit", "lua5.1", and "lua5.2".
-
 
 Dependencies
 ------------
@@ -51,7 +40,6 @@ Dependencies
 * libzip
 * libcrypto (OpenSSL)
 * libssl (OpenSSL)
-* optional: luajit or lua, most reliably used when pkg-config is available
 
 Apache configuration
 --------------------
@@ -92,8 +80,9 @@ the HTTP headers `Modified` and `Expires`.
 Online presence
 ---------------
 
-* The cgit homepage is hosted by cgit at <https://git.zx2c4.com/cgit/about/>
+* The cgit-pink homepage is hosted by cgit at
+  <https://git.causal.agency/cgit-pink/about>
 
-* Patches, bug reports, discussions and support should go to the cgit
-  mailing list: <cgit@lists.zx2c4.com>. To sign up, visit
-  <https://lists.zx2c4.com/mailman/listinfo/cgit>
+* Patches, bug reports, discussions and support should go to the cgit-pink
+  mailing list: <list+cgit@causal.agency>. Archives are available at:
+  <https://causal.agency/list/cgit.html>
diff --git a/third_party/cgit/cache.c b/third_party/cgit/cache.c
index 55199e8fe5..59372541cb 100644
--- a/third_party/cgit/cache.c
+++ b/third_party/cgit/cache.c
@@ -85,40 +85,45 @@ static int close_slot(struct cache_slot *slot)
 /* Print the content of the active cache slot (but skip the key). */
 static int print_slot(struct cache_slot *slot)
 {
+	off_t off;
 #ifdef HAVE_LINUX_SENDFILE
-	off_t start_off;
-	int ret;
+	off_t size;
+#endif
+
+	off = slot->keylen + 1;
 
-	start_off = slot->keylen + 1;
+#ifdef HAVE_LINUX_SENDFILE
+	size = slot->cache_st.st_size;
 
 	do {
-		ret = sendfile(STDOUT_FILENO, slot->cache_fd, &start_off,
-				slot->cache_st.st_size - start_off);
+		ssize_t ret;
+		ret = sendfile(STDOUT_FILENO, slot->cache_fd, &off, size - off);
 		if (ret < 0) {
 			if (errno == EAGAIN || errno == EINTR)
 				continue;
+			/* Fall back to read/write on EINVAL or ENOSYS */
+			if (errno == EINVAL || errno == ENOSYS)
+				break;
 			return errno;
 		}
-		return 0;
+		if (off == size)
+			return 0;
 	} while (1);
-#else
-	ssize_t i, j;
+#endif
 
-	i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET);
-	if (i != slot->keylen + 1)
+	if (lseek(slot->cache_fd, off, SEEK_SET) != off)
 		return errno;
 
 	do {
-		i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
-		if (i > 0)
-			j = xwrite(STDOUT_FILENO, slot->buf, i);
-	} while (i > 0 && j == i);
-
-	if (i < 0 || j != i)
-		return errno;
-	else
-		return 0;
-#endif
+		ssize_t ret;
+		ret = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
+		if (ret < 0)
+			return errno;
+		if (ret == 0)
+			return 0;
+		if (write_in_full(STDOUT_FILENO, slot->buf, ret) < 0)
+			return errno;
+	} while (1);
 }
 
 /* Check if the slot has expired */
@@ -265,6 +270,13 @@ static int process_slot(struct cache_slot *slot)
 {
 	int err;
 
+	/*
+	 * Make sure any buffered data is flushed before we redirect,
+	 * do sendfile(2) or write(2)
+	 */
+	if (fflush(stdout))
+		return errno;
+
 	err = open_slot(slot);
 	if (!err && slot->match) {
 		if (is_expired(slot)) {
diff --git a/third_party/cgit/cgit.c b/third_party/cgit/cgit.c
index 4b2d86c881..40202ead67 100644
--- a/third_party/cgit/cgit.c
+++ b/third_party/cgit/cgit.c
@@ -378,7 +378,7 @@ static void prepare_context(void)
 	ctx.cfg.commit_sort = 0;
 	ctx.cfg.css = "/cgit.css";
 	ctx.cfg.logo = "/cgit.png";
-	ctx.cfg.favicon = "/favicon.ico";
+	ctx.cfg.favicon = NULL;
 	ctx.cfg.local_time = 0;
 	ctx.cfg.enable_http_clone = 1;
 	ctx.cfg.enable_index_owner = 1;
@@ -507,9 +507,11 @@ static inline void parse_readme(const char *readme, char **filename, char **ref,
 	/* Check if the readme is tracked in the git repo. */
 	colon = strchr(readme, ':');
 	if (colon && strlen(colon) > 1) {
-		/* If it starts with a colon, we want to use
-		 * the default branch */
-		if (colon == readme && repo->defbranch)
+		/* If it starts with a colon, we want to use head given
+		 * from query or the default branch */
+		if (colon == readme && ctx.qry.head)
+			*ref = xstrdup(ctx.qry.head);
+		else if (colon == readme && repo->defbranch)
 			*ref = xstrdup(repo->defbranch);
 		else
 			*ref = xstrndup(readme, colon - readme);
@@ -626,7 +628,7 @@ static int prepare_repo_cmd(int nongit)
 		return 1;
 	}
 
-	if (get_oid(ctx.qry.head, &oid)) {
+	if (repo_get_oid(the_repository, ctx.qry.head, &oid)) {
 		char *old_head = ctx.qry.head;
 		ctx.qry.head = xstrdup(ctx.repo->defbranch);
 		cgit_print_error_page(404, "Not found",
@@ -674,7 +676,7 @@ static inline void authenticate_post(void)
 		len = MAX_AUTHENTICATION_POST_BYTES;
 	if ((len = read(STDIN_FILENO, buffer, len)) < 0)
 		die_errno("Could not read POST from stdin");
-	if (write(STDOUT_FILENO, buffer, len) < 0)
+	if (fwrite(buffer, 1, len, stdout) < len)
 		die_errno("Could not write POST to stdout");
 	cgit_close_filter(ctx.cfg.auth_filter);
 	exit(0);
@@ -963,13 +965,7 @@ static void cgit_parse_args(int argc, const char **argv)
 
 	for (i = 1; i < argc; i++) {
 		if (!strcmp(argv[i], "--version")) {
-			printf("CGit %s | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION);
-#ifdef NO_LUA
-			printf("[-] ");
-#else
-			printf("[+] ");
-#endif
-			printf("Lua scripting\n");
+			printf("CGit-pink %s | https://git.causal.agency/cgit-pink/\n\nCompiled in features:\n", CGIT_VERSION);
 #ifndef HAVE_LINUX_SENDFILE
 			printf("[-] ");
 #else
@@ -1051,7 +1047,6 @@ int cmd_main(int argc, const char **argv)
 	const char *path;
 	int err, ttl;
 
-	cgit_init_filters();
 	atexit(cgit_cleanup_filters);
 
 	prepare_context();
diff --git a/third_party/cgit/cgit.css b/third_party/cgit/cgit.css
index dfa144d05d..7133a7ba37 100644
--- a/third_party/cgit/cgit.css
+++ b/third_party/cgit/cgit.css
@@ -75,7 +75,7 @@ div#cgit table.tabs td {
 }
 
 div#cgit table.tabs td a {
-	padding: 2px 0.75em;
+	padding: 2px 0.25em;
 	color: #777;
 	font-size: 110%;
 }
@@ -363,6 +363,10 @@ div#cgit table.blame td.lines > div > pre {
 	top: 0;
 }
 
+div#cgit table.blame .oid {
+	font-size: 100%;
+}
+
 div#cgit table.bin-blob {
 	margin-top: 0.5em;
 	border: solid 1px black;
@@ -437,11 +441,6 @@ div#cgit div.commit-subject {
 	padding: 0em;
 }
 
-div#cgit div.commit-msg {
-	white-space: pre;
-	font-family: monospace;
-}
-
 div#cgit div.notes-header {
 	font-weight: bold;
 	padding-top: 1.5em;
@@ -538,26 +537,20 @@ div#cgit table.diff {
 	width: 100%;
 }
 
-div#cgit table.diff td {
-	font-family: monospace;
-	white-space: pre;
-}
-
-div#cgit table.diff td div.head {
+div#cgit table.diff td span.head {
 	font-weight: bold;
-	margin-top: 1em;
 	color: black;
 }
 
-div#cgit table.diff td div.hunk {
+div#cgit table.diff td span.hunk {
 	color: #009;
 }
 
-div#cgit table.diff td div.add {
+div#cgit table.diff td span.add {
 	color: green;
 }
 
-div#cgit table.diff td div.del {
+div#cgit table.diff td span.del {
 	color: red;
 }
 
@@ -581,7 +574,6 @@ div#cgit table.list td.reposection {
 
 div#cgit a.button {
 	font-size: 80%;
-	padding: 0em 0.5em;
 }
 
 div#cgit a.primary {
@@ -671,15 +663,20 @@ div#cgit div.footer a:hover {
 
 div#cgit a.branch-deco {
 	color: #000;
-	margin: 0px 0.5em;
 	padding: 0px 0.25em;
 	background-color: #88ff88;
 	border: solid 1px #007700;
 }
 
+div#cgit a.rev-deco {
+	color: #000;
+	padding: 0px 0.25em;
+	background-color: #eee;
+	border: solid 1px #aaa;
+}
+
 div#cgit a.tag-deco {
 	color: #000;
-	margin: 0px 0.5em;
 	padding: 0px 0.25em;
 	background-color: #ffff88;
 	border: solid 1px #777700;
@@ -687,7 +684,6 @@ div#cgit a.tag-deco {
 
 div#cgit a.tag-annotated-deco {
 	color: #000;
-	margin: 0px 0.5em;
 	padding: 0px 0.25em;
 	background-color: #ffcc88;
 	border: solid 1px #777700;
@@ -695,7 +691,6 @@ div#cgit a.tag-annotated-deco {
 
 div#cgit a.remote-deco {
 	color: #000;
-	margin: 0px 0.5em;
 	padding: 0px 0.25em;
 	background-color: #ccccff;
 	border: solid 1px #000077;
@@ -703,7 +698,6 @@ div#cgit a.remote-deco {
 
 div#cgit a.deco {
 	color: #000;
-	margin: 0px 0.5em;
 	padding: 0px 0.25em;
 	background-color: #ff8888;
 	border: solid 1px #770000;
@@ -713,8 +707,8 @@ div#cgit div.commit-subject a.branch-deco,
 div#cgit div.commit-subject a.tag-deco,
 div#cgit div.commit-subject a.tag-annotated-deco,
 div#cgit div.commit-subject a.remote-deco,
+div#cgit div.commit-subject a.rev-deco,
 div#cgit div.commit-subject a.deco {
-	margin-left: 1em;
 	font-size: 75%;
 }
 
diff --git a/third_party/cgit/cgit.h b/third_party/cgit/cgit.h
index 69b5c13232..f201f82b85 100644
--- a/third_party/cgit/cgit.h
+++ b/third_party/cgit/cgit.h
@@ -1,25 +1,33 @@
 #ifndef CGIT_H
 #define CGIT_H
 
+#include <stdbool.h>
 
 #include <git-compat-util.h>
-#include <stdbool.h>
 
-#include <cache.h>
+#include <archive.h>
+#include <commit.h>
+#include <diffcore.h>
+#include <diff.h>
+#include <environment.h>
+#include <graph.h>
 #include <grep.h>
+#include <hex.h>
+#include <log-tree.h>
+#include <notes.h>
 #include <object.h>
+#include <object-name.h>
 #include <object-store.h>
-#include <tree.h>
-#include <commit.h>
-#include <tag.h>
-#include <diff.h>
-#include <diffcore.h>
-#include <strvec.h>
+#include <path.h>
 #include <refs.h>
 #include <revision.h>
-#include <log-tree.h>
-#include <archive.h>
+#include <setup.h>
 #include <string-list.h>
+#include <strvec.h>
+#include <tag.h>
+#include <tree.h>
+#include <utf8.h>
+#include <wrapper.h>
 #include <xdiff-interface.h>
 #include <xdiff/xdiff.h>
 #include <utf8.h>
@@ -385,7 +393,6 @@ extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char
 extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv);
 extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype);
 extern void cgit_cleanup_filters(void);
-extern void cgit_init_filters(void);
 
 extern void cgit_prepare_repo_env(struct cgit_repo * repo);
 
diff --git a/third_party/cgit/cgit.mk b/third_party/cgit/cgit.mk
index 3fcc1ca314..5b9ed5be8e 100644
--- a/third_party/cgit/cgit.mk
+++ b/third_party/cgit/cgit.mk
@@ -27,32 +27,6 @@ ifdef NO_C99_FORMAT
 	CFLAGS += -DNO_C99_FORMAT
 endif
 
-ifdef NO_LUA
-	LUA_MESSAGE := linking without specified Lua support
-	CGIT_CFLAGS += -DNO_LUA
-else
-ifeq ($(LUA_PKGCONFIG),)
-	LUA_PKGCONFIG := $(shell for pc in luajit lua lua5.2 lua5.1; do \
-			$(PKG_CONFIG) --exists $$pc 2>/dev/null && echo $$pc && break; \
-			done)
-	LUA_MODE := autodetected
-else
-	LUA_MODE := specified
-endif
-ifneq ($(LUA_PKGCONFIG),)
-	LUA_MESSAGE := linking with $(LUA_MODE) $(LUA_PKGCONFIG)
-	LUA_LIBS := $(shell $(PKG_CONFIG) --libs $(LUA_PKGCONFIG) 2>/dev/null)
-	LUA_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(LUA_PKGCONFIG) 2>/dev/null)
-	CGIT_LIBS += $(LUA_LIBS)
-	CGIT_CFLAGS += $(LUA_CFLAGS)
-else
-	LUA_MESSAGE := linking without autodetected Lua support
-	NO_LUA := YesPlease
-	CGIT_CFLAGS += -DNO_LUA
-endif
-
-endif
-
 # Add -ldl to linker flags on systems that commonly use GNU libc.
 ifneq (,$(filter $(uname_S),Linux GNU GNU/kFreeBSD))
 	CGIT_LIBS += -ldl
@@ -130,7 +104,6 @@ $(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs)
 	$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $<
 
 $(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS)
-	@echo 1>&1 "    * $(LUA_MESSAGE)"
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS)
 
 CGIT_SP_OBJS := $(patsubst %.o,%.sp,$(CGIT_OBJS))
diff --git a/third_party/cgit/cgitrc.5.txt b/third_party/cgit/cgitrc.5.txt
index 33a6a8c0c7..cafb5355ea 100644
--- a/third_party/cgit/cgitrc.5.txt
+++ b/third_party/cgit/cgitrc.5.txt
@@ -219,7 +219,7 @@ enable-tree-linenumbers::
 favicon::
 	Url used as link to a shortcut icon for cgit. It is suggested to use
 	the value "/favicon.ico" since certain browsers will ignore other
-	values. Default value: "/favicon.ico".
+	values. Default value: none.
 
 footer::
 	The content of the file specified with this option will be included
@@ -579,11 +579,11 @@ repo.readme::
 	verbatim as the "About" page for this repo. You may also specify a
 	git refspec by head or by hash by prepending the refspec followed by
 	a colon. For example, "master:docs/readme.mkd". If the value begins
-	with a colon, i.e. ":docs/readme.rst", the default branch of the
-	repository will be used. Sharing any file will expose that entire
-	directory tree to the "/about/PATH" endpoints, so be sure that there
-	are no non-public files located in the same directory as the readme
-	file. Default value: <readme>.
+	with a colon, i.e. ":docs/readme.rst", the head giving in query or
+	the default branch of the repository will be used. Sharing any file
+	will expose that entire directory tree to the "/about/PATH" endpoints,
+	so be sure that there are no non-public files located in the same
+	directory as the readme file. Default value: <readme>.
 
 repo.section::
 	Override the current section name for this repository. Default value:
@@ -632,37 +632,6 @@ specification with the relevant string; available values are:
 'exec:'::
 	The default "one process per filter" mode.
 
-'lua:'::
-	Executes the script using a built-in Lua interpreter. The script is
-	loaded once per execution of cgit, and may be called multiple times
-	during cgit's lifetime, making it a good choice for repeated filters
-	such as the 'email filter'. It responds to three functions:
-
-	'filter_open(argument1, argument2, argument3, ...)'::
-		This is called upon activation of the filter for a particular
-		set of data.
-	'filter_write(buffer)'::
-		This is called whenever cgit writes data to the webpage.
-	'filter_close()'::
-		This is called when the current filtering operation is
-		completed. It must return an integer value. Usually 0
-		indicates success.
-
-	Additionally, cgit exposes to the Lua the following built-in functions:
-
-	'html(str)'::
-		Writes 'str' to the webpage.
-	'html_txt(str)'::
-		HTML escapes and writes 'str' to the webpage.
-	'html_attr(str)'::
-		HTML escapes for an attribute and writes "str' to the webpage.
-	'html_url_path(str)'::
-		URL escapes for a path and writes 'str' to the webpage.
-	'html_url_arg(str)'::
-		URL escapes for an argument and writes 'str' to the webpage.
-	'html_include(file)'::
-		Includes 'file' in webpage.
-
 
 Parameters are provided to filters as follows.
 
@@ -696,9 +665,6 @@ auth filter::
 	with a 302 redirect, and write to output one or more "Set-Cookie"
 	HTTP headers, each followed by a newline.
 
-	Please see `filters/simple-authentication.lua` for a clear example
-	script that may be modified.
-
 commit filter::
 	This filter is given no arguments. The commit message text that is to
 	be filtered is available on standard input and the filtered text is
diff --git a/third_party/cgit/default.nix b/third_party/cgit/default.nix
index 025877ee4b..c783bda16e 100644
--- a/third_party/cgit/default.nix
+++ b/third_party/cgit/default.nix
@@ -1,21 +1,23 @@
 { depot, lib, pkgs, ... }:
 
 let
-  inherit (pkgs) stdenv gzip bzip2 xz luajit zlib autoconf openssl pkgconfig;
-in stdenv.mkDerivation rec {
-  pname = "cgit";
+  inherit (pkgs) stdenv gzip bzip2 xz lzip zstd zlib openssl;
+in
+stdenv.mkDerivation rec {
+  pname = "cgit-pink";
   version = "master";
   src = ./.;
 
-  nativeBuildInputs = [ autoconf pkgconfig ];
-  buildInputs = [ openssl zlib luajit ];
+  buildInputs = [ openssl zlib ];
 
   enableParallelBuilding = true;
 
   postPatch = ''
     sed -e 's|"gzip"|"${gzip}/bin/gzip"|' \
         -e 's|"bzip2"|"${bzip2.bin}/bin/bzip2"|' \
+        -e 's|"lzip"|"${lzip}/bin/lzip"|' \
         -e 's|"xz"|"${xz.bin}/bin/xz"|' \
+        -e 's|"zstd"|"${zstd}/bin/zstd"|' \
         -i ui-snapshot.c
   '';
 
@@ -32,12 +34,18 @@ in stdenv.mkDerivation rec {
     cat tvl-extra.css >> cgit.css
   '';
 
+  stripDebugList = [ "cgit" ];
+
+  # We don't use the filters and they require wrapping to find their deps
+  postInstall = ''
+    rm -rf "$out/lib/cgit/filters"
+    find "$out" -type d -empty -delete
+  '';
+
   meta = {
-    homepage = https://git.zx2c4.com/cgit/about/;
-    repositories.git = git://git.zx2c4.com/cgit;
-    description = "Web frontend for git repositories";
+    hompepage = "https://git.causal.agency/cgit-pink/";
+    description = "cgit fork aiming for better maintenance";
     license = lib.licenses.gpl2;
     platforms = lib.platforms.linux;
-    maintainers = with lib.maintainers; [ bjornfor ];
   };
 }
diff --git a/third_party/cgit/favicon.ico b/third_party/cgit/favicon.ico
deleted file mode 100644
index 56ff59384f..0000000000
--- a/third_party/cgit/favicon.ico
+++ /dev/null
Binary files differdiff --git a/third_party/cgit/filter.c b/third_party/cgit/filter.c
index 181d9a892f..190fb5501b 100644
--- a/third_party/cgit/filter.c
+++ b/third_party/cgit/filter.c
@@ -8,12 +8,6 @@
 
 #include "cgit.h"
 #include "html.h"
-#ifndef NO_LUA
-#include <dlfcn.h>
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
-#endif
 
 static inline void reap_filter(struct cgit_filter *filter)
 {
@@ -48,6 +42,7 @@ static int open_exec_filter(struct cgit_filter *base, va_list ap)
 	for (i = 0; i < filter->base.argument_count; i++)
 		filter->argv[i + 1] = va_arg(ap, char *);
 
+	chk_zero(fflush(stdout), "unable to flush STDOUT");
 	filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
 		"Unable to duplicate STDOUT");
 	chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess");
@@ -71,6 +66,7 @@ static int close_exec_filter(struct cgit_filter *base)
 	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
 	int i, exit_status = 0;
 
+	chk_zero(fflush(stdout), "unable to flush STDOUT");
 	chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
 		"Unable to restore STDOUT");
 	close(filter->old_stdout);
@@ -136,234 +132,6 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar
 	filter->base.argument_count = 0;
 }
 
-#ifdef NO_LUA
-void cgit_init_filters(void)
-{
-}
-#endif
-
-#ifndef NO_LUA
-static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
-static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL;
-static struct cgit_filter *current_write_filter = NULL;
-
-void cgit_init_filters(void)
-{
-	libc_write = dlsym(RTLD_NEXT, "write");
-	if (!libc_write)
-		die("Could not locate libc's write function");
-}
-
-ssize_t write(int fd, const void *buf, size_t count)
-{
-	if (fd != STDOUT_FILENO || !filter_write)
-		return libc_write(fd, buf, count);
-	return filter_write(current_write_filter, buf, count);
-}
-
-static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count))
-{
-	/* We want to avoid buggy nested patterns. */
-	assert(filter_write == NULL);
-	assert(current_write_filter == NULL);
-	current_write_filter = filter;
-	filter_write = new_write;
-}
-
-static inline void unhook_write(void)
-{
-	assert(filter_write != NULL);
-	assert(current_write_filter != NULL);
-	filter_write = NULL;
-	current_write_filter = NULL;
-}
-
-struct lua_filter {
-	struct cgit_filter base;
-	char *script_file;
-	lua_State *lua_state;
-};
-
-static void error_lua_filter(struct lua_filter *filter)
-{
-	die("Lua error in %s: %s", filter->script_file, lua_tostring(filter->lua_state, -1));
-	lua_pop(filter->lua_state, 1);
-}
-
-static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_t count)
-{
-	struct lua_filter *filter = (struct lua_filter *)base;
-
-	lua_getglobal(filter->lua_state, "filter_write");
-	lua_pushlstring(filter->lua_state, buf, count);
-	if (lua_pcall(filter->lua_state, 1, 0, 0)) {
-		error_lua_filter(filter);
-		errno = EIO;
-		return -1;
-	}
-	return count;
-}
-
-static inline int hook_lua_filter(lua_State *lua_state, void (*fn)(const char *txt))
-{
-	const char *str;
-	ssize_t (*save_filter_write)(struct cgit_filter *base, const void *buf, size_t count);
-	struct cgit_filter *save_filter;
-
-	str = lua_tostring(lua_state, 1);
-	if (!str)
-		return 0;
-
-	save_filter_write = filter_write;
-	save_filter = current_write_filter;
-	unhook_write();
-	fn(str);
-	hook_write(save_filter, save_filter_write);
-
-	return 0;
-}
-
-static int html_lua_filter(lua_State *lua_state)
-{
-	return hook_lua_filter(lua_state, html);
-}
-
-static int html_txt_lua_filter(lua_State *lua_state)
-{
-	return hook_lua_filter(lua_state, html_txt);
-}
-
-static int html_attr_lua_filter(lua_State *lua_state)
-{
-	return hook_lua_filter(lua_state, html_attr);
-}
-
-static int html_url_path_lua_filter(lua_State *lua_state)
-{
-	return hook_lua_filter(lua_state, html_url_path);
-}
-
-static int html_url_arg_lua_filter(lua_State *lua_state)
-{
-	return hook_lua_filter(lua_state, html_url_arg);
-}
-
-static int html_include_lua_filter(lua_State *lua_state)
-{
-	return hook_lua_filter(lua_state, (void (*)(const char *))html_include);
-}
-
-static void cleanup_lua_filter(struct cgit_filter *base)
-{
-	struct lua_filter *filter = (struct lua_filter *)base;
-
-	if (!filter->lua_state)
-		return;
-
-	lua_close(filter->lua_state);
-	filter->lua_state = NULL;
-	if (filter->script_file) {
-		free(filter->script_file);
-		filter->script_file = NULL;
-	}
-}
-
-static int init_lua_filter(struct lua_filter *filter)
-{
-	if (filter->lua_state)
-		return 0;
-
-	if (!(filter->lua_state = luaL_newstate()))
-		return 1;
-
-	luaL_openlibs(filter->lua_state);
-
-	lua_pushcfunction(filter->lua_state, html_lua_filter);
-	lua_setglobal(filter->lua_state, "html");
-	lua_pushcfunction(filter->lua_state, html_txt_lua_filter);
-	lua_setglobal(filter->lua_state, "html_txt");
-	lua_pushcfunction(filter->lua_state, html_attr_lua_filter);
-	lua_setglobal(filter->lua_state, "html_attr");
-	lua_pushcfunction(filter->lua_state, html_url_path_lua_filter);
-	lua_setglobal(filter->lua_state, "html_url_path");
-	lua_pushcfunction(filter->lua_state, html_url_arg_lua_filter);
-	lua_setglobal(filter->lua_state, "html_url_arg");
-	lua_pushcfunction(filter->lua_state, html_include_lua_filter);
-	lua_setglobal(filter->lua_state, "html_include");
-
-	if (luaL_dofile(filter->lua_state, filter->script_file)) {
-		error_lua_filter(filter);
-		lua_close(filter->lua_state);
-		filter->lua_state = NULL;
-		return 1;
-	}
-	return 0;
-}
-
-static int open_lua_filter(struct cgit_filter *base, va_list ap)
-{
-	struct lua_filter *filter = (struct lua_filter *)base;
-	int i;
-
-	if (init_lua_filter(filter))
-		return 1;
-
-	hook_write(base, write_lua_filter);
-
-	lua_getglobal(filter->lua_state, "filter_open");
-	for (i = 0; i < filter->base.argument_count; ++i)
-		lua_pushstring(filter->lua_state, va_arg(ap, char *));
-	if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0)) {
-		error_lua_filter(filter);
-		return 1;
-	}
-	return 0;
-}
-
-static int close_lua_filter(struct cgit_filter *base)
-{
-	struct lua_filter *filter = (struct lua_filter *)base;
-	int ret = 0;
-
-	lua_getglobal(filter->lua_state, "filter_close");
-	if (lua_pcall(filter->lua_state, 0, 1, 0)) {
-		error_lua_filter(filter);
-		ret = -1;
-	} else {
-		ret = lua_tonumber(filter->lua_state, -1);
-		lua_pop(filter->lua_state, 1);
-	}
-
-	unhook_write();
-	return ret;
-}
-
-static void fprintf_lua_filter(struct cgit_filter *base, FILE *f, const char *prefix)
-{
-	struct lua_filter *filter = (struct lua_filter *)base;
-	fprintf(f, "%slua:%s\n", prefix, filter->script_file);
-}
-
-
-static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count)
-{
-	struct lua_filter *filter;
-
-	filter = xmalloc(sizeof(*filter));
-	memset(filter, 0, sizeof(*filter));
-	filter->base.open = open_lua_filter;
-	filter->base.close = close_lua_filter;
-	filter->base.fprintf = fprintf_lua_filter;
-	filter->base.cleanup = cleanup_lua_filter;
-	filter->base.argument_count = argument_count;
-	filter->script_file = xstrdup(cmd);
-
-	return &filter->base;
-}
-
-#endif
-
-
 int cgit_open_filter(struct cgit_filter *filter, ...)
 {
 	int result;
@@ -395,9 +163,6 @@ static const struct {
 	struct cgit_filter *(*ctor)(const char *cmd, int argument_count);
 } filter_specs[] = {
 	{ "exec", new_exec_filter },
-#ifndef NO_LUA
-	{ "lua", new_lua_filter },
-#endif
 };
 
 struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
diff --git a/third_party/cgit/filters/email-gravatar.lua b/third_party/cgit/filters/email-gravatar.lua
deleted file mode 100644
index c39b490d6b..0000000000
--- a/third_party/cgit/filters/email-gravatar.lua
+++ /dev/null
@@ -1,35 +0,0 @@
--- This script may be used with the email-filter or repo.email-filter settings in cgitrc.
--- It adds gravatar icons to author names. It is designed to be used with the lua:
--- prefix in filters. It is much faster than the corresponding python script.
---
--- Requirements:
--- 	luaossl
--- 	<http://25thandclement.com/~william/projects/luaossl.html>
---
-
-local digest = require("openssl.digest")
-
-function md5_hex(input)
-	local b = digest.new("md5"):final(input)
-	local x = ""
-	for i = 1, #b do
-		x = x .. string.format("%.2x", string.byte(b, i))
-	end
-	return x
-end
-
-function filter_open(email, page)
-	buffer = ""
-	md5 = md5_hex(email:sub(2, -2):lower())
-end
-
-function filter_close()
-	html("<img src='//www.gravatar.com/avatar/" .. md5 .. "?s=13&amp;d=retro' width='13' height='13' alt='Gravatar' /> " .. buffer)
-	return 0
-end
-
-function filter_write(str)
-	buffer = buffer .. str
-end
-
-
diff --git a/third_party/cgit/filters/email-gravatar.py b/third_party/cgit/filters/email-gravatar.py
index d70440ea54..012113c591 100755
--- a/third_party/cgit/filters/email-gravatar.py
+++ b/third_party/cgit/filters/email-gravatar.py
@@ -1,8 +1,5 @@
 #!/usr/bin/env python3
 
-# Please prefer the email-gravatar.lua using lua: as a prefix over this script. This
-# script is very slow, in comparison.
-#
 # This script may be used with the email-filter or repo.email-filter settings in cgitrc.
 #
 # The following environment variables can be used to retrieve the configuration
diff --git a/third_party/cgit/filters/email-libravatar.lua b/third_party/cgit/filters/email-libravatar.lua
deleted file mode 100644
index 7336baf830..0000000000
--- a/third_party/cgit/filters/email-libravatar.lua
+++ /dev/null
@@ -1,36 +0,0 @@
--- This script may be used with the email-filter or repo.email-filter settings in cgitrc.
--- It adds libravatar icons to author names. It is designed to be used with the lua:
--- prefix in filters.
---
--- Requirements:
--- 	luaossl
--- 	<http://25thandclement.com/~william/projects/luaossl.html>
---
-
-local digest = require("openssl.digest")
-
-function md5_hex(input)
-	local b = digest.new("md5"):final(input)
-	local x = ""
-	for i = 1, #b do
-		x = x .. string.format("%.2x", string.byte(b, i))
-	end
-	return x
-end
-
-function filter_open(email, page)
-	buffer = ""
-	md5 = md5_hex(email:sub(2, -2):lower())
-end
-
-function filter_close()
-	baseurl = os.getenv("HTTPS") and "https://seccdn.libravatar.org/" or "http://cdn.libravatar.org/"
-	html("<img src='" .. baseurl .. "avatar/" .. md5 .. "?s=13&amp;d=retro' width='13' height='13' alt='Libravatar' /> " .. buffer)
-	return 0
-end
-
-function filter_write(str)
-	buffer = buffer .. str
-end
-
-
diff --git a/third_party/cgit/filters/file-authentication.lua b/third_party/cgit/filters/file-authentication.lua
deleted file mode 100644
index 024880463c..0000000000
--- a/third_party/cgit/filters/file-authentication.lua
+++ /dev/null
@@ -1,359 +0,0 @@
--- This script may be used with the auth-filter.
---
--- Requirements:
--- 	luaossl
--- 	<http://25thandclement.com/~william/projects/luaossl.html>
--- 	luaposix
--- 	<https://github.com/luaposix/luaposix>
---
-local sysstat = require("posix.sys.stat")
-local unistd = require("posix.unistd")
-local rand = require("openssl.rand")
-local hmac = require("openssl.hmac")
-
--- This file should contain a series of lines in the form of:
---	username1:hash1
---	username2:hash2
---	username3:hash3
---	...
--- Hashes can be generated using something like `mkpasswd -m sha-512 -R 300000`.
--- This file should not be world-readable.
-local users_filename = "/etc/cgit-auth/users"
-
--- This file should contain a series of lines in the form of:
--- 	groupname1:username1,username2,username3,...
---	...
-local groups_filename = "/etc/cgit-auth/groups"
-
--- This file should contain a series of lines in the form of:
--- 	reponame1:groupname1,groupname2,groupname3,...
---	...
-local repos_filename = "/etc/cgit-auth/repos"
-
--- Set this to a path this script can write to for storing a persistent
--- cookie secret, which should not be world-readable.
-local secret_filename = "/var/cache/cgit/auth-secret"
-
---
---
--- Authentication functions follow below. Swap these out if you want different authentication semantics.
---
---
-
--- Looks up a hash for a given user.
-function lookup_hash(user)
-	local line
-	for line in io.lines(users_filename) do
-		local u, h = string.match(line, "(.-):(.+)")
-		if u:lower() == user:lower() then
-			return h
-		end
-	end
-	return nil
-end
-
--- Looks up users for a given repo.
-function lookup_users(repo)
-	local users = nil
-	local groups = nil
-	local line, group, user
-	for line in io.lines(repos_filename) do
-		local r, g = string.match(line, "(.-):(.+)")
-		if r == repo then
-			groups = { }
-			for group in string.gmatch(g, "([^,]+)") do
-				groups[group:lower()] = true
-			end
-			break
-		end
-	end
-	if groups == nil then
-		return nil
-	end
-	for line in io.lines(groups_filename) do
-		local g, u = string.match(line, "(.-):(.+)")
-		if groups[g:lower()] then
-			if users == nil then
-				users = { }
-			end
-			for user in string.gmatch(u, "([^,]+)") do
-				users[user:lower()] = true
-			end
-		end
-	end
-	return users
-end
-
-
--- Sets HTTP cookie headers based on post and sets up redirection.
-function authenticate_post()
-	local hash = lookup_hash(post["username"])
-	local redirect = validate_value("redirect", post["redirect"])
-
-	if redirect == nil then
-		not_found()
-		return 0
-	end
-
-	redirect_to(redirect)
-
-	if hash == nil or hash ~= unistd.crypt(post["password"], hash) then
-		set_cookie("cgitauth", "")
-	else
-		-- One week expiration time
-		local username = secure_value("username", post["username"], os.time() + 604800)
-		set_cookie("cgitauth", username)
-	end
-
-	html("\n")
-	return 0
-end
-
-
--- Returns 1 if the cookie is valid and 0 if it is not.
-function authenticate_cookie()
-	accepted_users = lookup_users(cgit["repo"])
-	if accepted_users == nil then
-		-- We return as valid if the repo is not protected.
-		return 1
-	end
-
-	local username = validate_value("username", get_cookie(http["cookie"], "cgitauth"))
-	if username == nil or not accepted_users[username:lower()] then
-		return 0
-	else
-		return 1
-	end
-end
-
--- Prints the html for the login form.
-function body()
-	html("<h2>Authentication Required</h2>")
-	html("<form method='post' action='")
-	html_attr(cgit["login"])
-	html("'>")
-	html("<input type='hidden' name='redirect' value='")
-	html_attr(secure_value("redirect", cgit["url"], 0))
-	html("' />")
-	html("<table>")
-	html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>")
-	html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>")
-	html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>")
-	html("</table></form>")
-
-	return 0
-end
-
-
-
---
---
--- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions.
---
---
-
-local actions = {}
-actions["authenticate-post"] = authenticate_post
-actions["authenticate-cookie"] = authenticate_cookie
-actions["body"] = body
-
-function filter_open(...)
-	action = actions[select(1, ...)]
-
-	http = {}
-	http["cookie"] = select(2, ...)
-	http["method"] = select(3, ...)
-	http["query"] = select(4, ...)
-	http["referer"] = select(5, ...)
-	http["path"] = select(6, ...)
-	http["host"] = select(7, ...)
-	http["https"] = select(8, ...)
-
-	cgit = {}
-	cgit["repo"] = select(9, ...)
-	cgit["page"] = select(10, ...)
-	cgit["url"] = select(11, ...)
-	cgit["login"] = select(12, ...)
-
-end
-
-function filter_close()
-	return action()
-end
-
-function filter_write(str)
-	post = parse_qs(str)
-end
-
-
---
---
--- Utility functions based on keplerproject/wsapi.
---
---
-
-function url_decode(str)
-	if not str then
-		return ""
-	end
-	str = string.gsub(str, "+", " ")
-	str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end)
-	str = string.gsub(str, "\r\n", "\n")
-	return str
-end
-
-function url_encode(str)
-	if not str then
-		return ""
-	end
-	str = string.gsub(str, "\n", "\r\n")
-	str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end)
-	str = string.gsub(str, " ", "+")
-	return str
-end
-
-function parse_qs(qs)
-	local tab = {}
-	for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do
-		tab[url_decode(key)] = url_decode(val)
-	end
-	return tab
-end
-
-function get_cookie(cookies, name)
-	cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";")
-	return url_decode(string.match(cookies, ";" .. name .. "=(.-);"))
-end
-
-function tohex(b)
-	local x = ""
-	for i = 1, #b do
-		x = x .. string.format("%.2x", string.byte(b, i))
-	end
-	return x
-end
-
---
---
--- Cookie construction and validation helpers.
---
---
-
-local secret = nil
-
--- Loads a secret from a file, creates a secret, or returns one from memory.
-function get_secret()
-	if secret ~= nil then
-		return secret
-	end
-	local secret_file = io.open(secret_filename, "r")
-	if secret_file == nil then
-		local old_umask = sysstat.umask(63)
-		local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16))
-		local temporary_file = io.open(temporary_filename, "w")
-		if temporary_file == nil then
-			os.exit(177)
-		end
-		temporary_file:write(tohex(rand.bytes(32)))
-		temporary_file:close()
-		unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same.
-		unistd.unlink(temporary_filename)
-		sysstat.umask(old_umask)
-		secret_file = io.open(secret_filename, "r")
-	end
-	if secret_file == nil then
-		os.exit(177)
-	end
-	secret = secret_file:read()
-	secret_file:close()
-	if secret:len() ~= 64 then
-		os.exit(177)
-	end
-	return secret
-end
-
--- Returns value of cookie if cookie is valid. Otherwise returns nil.
-function validate_value(expected_field, cookie)
-	local i = 0
-	local value = ""
-	local field = ""
-	local expiration = 0
-	local salt = ""
-	local chmac = ""
-
-	if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then
-		return nil
-	end
-
-	for component in string.gmatch(cookie, "[^|]+") do
-		if i == 0 then
-			field = component
-		elseif i == 1 then
-			value = component
-		elseif i == 2 then
-			expiration = tonumber(component)
-			if expiration == nil then
-				expiration = -1
-			end
-		elseif i == 3 then
-			salt = component
-		elseif i == 4 then
-			chmac = component
-		else
-			break
-		end
-		i = i + 1
-	end
-
-	if chmac == nil or chmac:len() == 0 then
-		return nil
-	end
-
-	-- Lua hashes strings, so these comparisons are time invariant.
-	if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
-		return nil
-	end
-
-	if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then
-		return nil
-	end
-
-	if url_decode(field) ~= expected_field then
-		return nil
-	end
-
-	return url_decode(value)
-end
-
-function secure_value(field, value, expiration)
-	if value == nil or value:len() <= 0 then
-		return ""
-	end
-
-	local authstr = ""
-	local salt = tohex(rand.bytes(16))
-	value = url_encode(value)
-	field = url_encode(field)
-	authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
-	authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr))
-	return authstr
-end
-
-function set_cookie(cookie, value)
-	html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly")
-	if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then
-		html("; secure")
-	end
-	html("\n")
-end
-
-function redirect_to(url)
-	html("Status: 302 Redirect\n")
-	html("Cache-Control: no-cache, no-store\n")
-	html("Location: " .. url .. "\n")
-end
-
-function not_found()
-	html("Status: 404 Not Found\n")
-	html("Cache-Control: no-cache, no-store\n\n")
-end
diff --git a/third_party/cgit/filters/gentoo-ldap-authentication.lua b/third_party/cgit/filters/gentoo-ldap-authentication.lua
deleted file mode 100644
index 673c88d102..0000000000
--- a/third_party/cgit/filters/gentoo-ldap-authentication.lua
+++ /dev/null
@@ -1,360 +0,0 @@
--- This script may be used with the auth-filter. Be sure to configure it as you wish.
---
--- Requirements:
--- 	luaossl
--- 	<http://25thandclement.com/~william/projects/luaossl.html>
--- 	lualdap >= 1.2
--- 	<https://git.zx2c4.com/lualdap/about/>
--- 	luaposix
--- 	<https://github.com/luaposix/luaposix>
---
-local sysstat = require("posix.sys.stat")
-local unistd = require("posix.unistd")
-local lualdap = require("lualdap")
-local rand = require("openssl.rand")
-local hmac = require("openssl.hmac")
-
---
---
--- Configure these variables for your settings.
---
---
-
--- A list of password protected repositories, with which gentooAccess
--- group is allowed to access each one.
-local protected_repos = {
-	glouglou = "infra",
-	portage = "dev"
-}
-
--- Set this to a path this script can write to for storing a persistent
--- cookie secret, which should be guarded.
-local secret_filename = "/var/cache/cgit/auth-secret"
-
-
---
---
--- Authentication functions follow below. Swap these out if you want different authentication semantics.
---
---
-
--- Sets HTTP cookie headers based on post and sets up redirection.
-function authenticate_post()
-	local redirect = validate_value("redirect", post["redirect"])
-
-	if redirect == nil then
-		not_found()
-		return 0
-	end
-
-	redirect_to(redirect)
-	
-	local groups = gentoo_ldap_user_groups(post["username"], post["password"])
-	if groups == nil then
-		set_cookie("cgitauth", "")
-	else
-		-- One week expiration time
-		set_cookie("cgitauth", secure_value("gentoogroups", table.concat(groups, ","), os.time() + 604800))
-	end
-
-	html("\n")
-	return 0
-end
-
-
--- Returns 1 if the cookie is valid and 0 if it is not.
-function authenticate_cookie()
-	local required_group = protected_repos[cgit["repo"]]
-	if required_group == nil then
-		-- We return as valid if the repo is not protected.
-		return 1
-	end
-	
-	local user_groups = validate_value("gentoogroups", get_cookie(http["cookie"], "cgitauth"))
-	if user_groups == nil or user_groups == "" then
-		return 0
-	end
-	for group in string.gmatch(user_groups, "[^,]+") do
-		if group == required_group then
-			return 1
-		end
-	end
-	return 0
-end
-
--- Prints the html for the login form.
-function body()
-	html("<h2>Gentoo LDAP Authentication Required</h2>")
-	html("<form method='post' action='")
-	html_attr(cgit["login"])
-	html("'>")
-	html("<input type='hidden' name='redirect' value='")
-	html_attr(secure_value("redirect", cgit["url"], 0))
-	html("' />")
-	html("<table>")
-	html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>")
-	html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>")
-	html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>")
-	html("</table></form>")
-
-	return 0
-end
-
---
---
--- Gentoo LDAP support.
---
---
-
-function gentoo_ldap_user_groups(username, password)
-	-- Ensure the user is alphanumeric
-	if username == nil or username:match("%W") then
-		return nil
-	end
-
-	local who = "uid=" .. username .. ",ou=devs,dc=gentoo,dc=org"
-
-	local ldap, err = lualdap.open_simple {
-		uri = "ldap://ldap1.gentoo.org",
-		who = who,
-		password = password,
-		starttls = true,
-		certfile = "/var/www/uwsgi/cgit/gentoo-ldap/star.gentoo.org.crt",
-		keyfile = "/var/www/uwsgi/cgit/gentoo-ldap/star.gentoo.org.key",
-		cacertfile = "/var/www/uwsgi/cgit/gentoo-ldap/ca.pem"
-	}
-	if ldap == nil then
-		return nil
-	end
-
-	local group_suffix = ".group"
-	local group_suffix_len = group_suffix:len()
-	local groups = {}
-	for dn, attribs in ldap:search { base = who, scope = "subtree" } do
-		local access = attribs["gentooAccess"]
-		if dn == who and access ~= nil then
-			for i, v in ipairs(access) do
-				local vlen = v:len()
-				if vlen > group_suffix_len and v:sub(-group_suffix_len) == group_suffix then
-					table.insert(groups, v:sub(1, vlen - group_suffix_len))
-				end
-			end
-		end
-	end
-
-	ldap:close()
-
-	return groups
-end
-
---
---
--- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions.
---
---
-
-local actions = {}
-actions["authenticate-post"] = authenticate_post
-actions["authenticate-cookie"] = authenticate_cookie
-actions["body"] = body
-
-function filter_open(...)
-	action = actions[select(1, ...)]
-
-	http = {}
-	http["cookie"] = select(2, ...)
-	http["method"] = select(3, ...)
-	http["query"] = select(4, ...)
-	http["referer"] = select(5, ...)
-	http["path"] = select(6, ...)
-	http["host"] = select(7, ...)
-	http["https"] = select(8, ...)
-
-	cgit = {}
-	cgit["repo"] = select(9, ...)
-	cgit["page"] = select(10, ...)
-	cgit["url"] = select(11, ...)
-	cgit["login"] = select(12, ...)
-
-end
-
-function filter_close()
-	return action()
-end
-
-function filter_write(str)
-	post = parse_qs(str)
-end
-
-
---
---
--- Utility functions based on keplerproject/wsapi.
---
---
-
-function url_decode(str)
-	if not str then
-		return ""
-	end
-	str = string.gsub(str, "+", " ")
-	str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end)
-	str = string.gsub(str, "\r\n", "\n")
-	return str
-end
-
-function url_encode(str)
-	if not str then
-		return ""
-	end
-	str = string.gsub(str, "\n", "\r\n")
-	str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end)
-	str = string.gsub(str, " ", "+")
-	return str
-end
-
-function parse_qs(qs)
-	local tab = {}
-	for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do
-		tab[url_decode(key)] = url_decode(val)
-	end
-	return tab
-end
-
-function get_cookie(cookies, name)
-	cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";")
-	return string.match(cookies, ";" .. name .. "=(.-);")
-end
-
-function tohex(b)
-	local x = ""
-	for i = 1, #b do
-		x = x .. string.format("%.2x", string.byte(b, i))
-	end
-	return x
-end
-
---
---
--- Cookie construction and validation helpers.
---
---
-
-local secret = nil
-
--- Loads a secret from a file, creates a secret, or returns one from memory.
-function get_secret()
-	if secret ~= nil then
-		return secret
-	end
-	local secret_file = io.open(secret_filename, "r")
-	if secret_file == nil then
-		local old_umask = sysstat.umask(63)
-		local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16))
-		local temporary_file = io.open(temporary_filename, "w")
-		if temporary_file == nil then
-			os.exit(177)
-		end
-		temporary_file:write(tohex(rand.bytes(32)))
-		temporary_file:close()
-		unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same.
-		unistd.unlink(temporary_filename)
-		sysstat.umask(old_umask)
-		secret_file = io.open(secret_filename, "r")
-	end
-	if secret_file == nil then
-		os.exit(177)
-	end
-	secret = secret_file:read()
-	secret_file:close()
-	if secret:len() ~= 64 then
-		os.exit(177)
-	end
-	return secret
-end
-
--- Returns value of cookie if cookie is valid. Otherwise returns nil.
-function validate_value(expected_field, cookie)
-	local i = 0
-	local value = ""
-	local field = ""
-	local expiration = 0
-	local salt = ""
-	local chmac = ""
-
-	if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then
-		return nil
-	end
-
-	for component in string.gmatch(cookie, "[^|]+") do
-		if i == 0 then
-			field = component
-		elseif i == 1 then
-			value = component
-		elseif i == 2 then
-			expiration = tonumber(component)
-			if expiration == nil then
-				expiration = -1
-			end
-		elseif i == 3 then
-			salt = component
-		elseif i == 4 then
-			chmac = component
-		else
-			break
-		end
-		i = i + 1
-	end
-
-	if chmac == nil or chmac:len() == 0 then
-		return nil
-	end
-
-	-- Lua hashes strings, so these comparisons are time invariant.
-	if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
-		return nil
-	end
-
-	if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then
-		return nil
-	end
-
-	if url_decode(field) ~= expected_field then
-		return nil
-	end
-
-	return url_decode(value)
-end
-
-function secure_value(field, value, expiration)
-	if value == nil or value:len() <= 0 then
-		return ""
-	end
-
-	local authstr = ""
-	local salt = tohex(rand.bytes(16))
-	value = url_encode(value)
-	field = url_encode(field)
-	authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
-	authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr))
-	return authstr
-end
-
-function set_cookie(cookie, value)
-	html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly")
-	if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then
-		html("; secure")
-	end
-	html("\n")
-end
-
-function redirect_to(url)
-	html("Status: 302 Redirect\n")
-	html("Cache-Control: no-cache, no-store\n")
-	html("Location: " .. url .. "\n")
-end
-
-function not_found()
-	html("Status: 404 Not Found\n")
-	html("Cache-Control: no-cache, no-store\n\n")
-end
diff --git a/third_party/cgit/filters/owner-example.lua b/third_party/cgit/filters/owner-example.lua
deleted file mode 100644
index 50fc25a8e5..0000000000
--- a/third_party/cgit/filters/owner-example.lua
+++ /dev/null
@@ -1,17 +0,0 @@
--- This script is an example of an owner-filter.  It replaces the
--- usual query link with one to a fictional homepage.  This script may
--- be used with the owner-filter or repo.owner-filter settings in
--- cgitrc with the `lua:` prefix.
-
-function filter_open()
-	buffer = ""
-end
-
-function filter_close()
-	html(string.format("<a href=\"%s\">%s</a>", "http://wiki.example.com/about/" .. buffer, buffer))
-	return 0
-end
-
-function filter_write(str)
-	buffer = buffer .. str
-end
diff --git a/third_party/cgit/filters/simple-authentication.lua b/third_party/cgit/filters/simple-authentication.lua
deleted file mode 100644
index 23d345763b..0000000000
--- a/third_party/cgit/filters/simple-authentication.lua
+++ /dev/null
@@ -1,314 +0,0 @@
--- This script may be used with the auth-filter. Be sure to configure it as you wish.
---
--- Requirements:
--- 	luaossl
--- 	<http://25thandclement.com/~william/projects/luaossl.html>
--- 	luaposix
--- 	<https://github.com/luaposix/luaposix>
---
-local sysstat = require("posix.sys.stat")
-local unistd = require("posix.unistd")
-local rand = require("openssl.rand")
-local hmac = require("openssl.hmac")
-
---
---
--- Configure these variables for your settings.
---
---
-
--- A list of password protected repositories along with the users who can access them.
-local protected_repos = {
-	glouglou	= { laurent = true, jason = true },
-	qt		= { jason = true, bob = true }
-}
-
--- A list of users and hashes, generated with `mkpasswd -m sha-512 -R 300000`.
-local users = {
-	jason		= "$6$rounds=300000$YYJct3n/o.ruYK$HhpSeuCuW1fJkpvMZOZzVizeLsBKcGA/aF2UPuV5v60JyH2MVSG6P511UMTj2F3H75.IT2HIlnvXzNb60FcZH1",
-	laurent		= "$6$rounds=300000$dP0KNHwYb3JKigT$pN/LG7rWxQ4HniFtx5wKyJXBJUKP7R01zTNZ0qSK/aivw8ywGAOdfYiIQFqFhZFtVGvr11/7an.nesvm8iJUi.",
-	bob		= "$6$rounds=300000$jCLCCt6LUpTz$PI1vvd1yaVYcCzqH8QAJFcJ60b6W/6sjcOsU7mAkNo7IE8FRGW1vkjF8I/T5jt/auv5ODLb1L4S2s.CAyZyUC"
-}
-
--- Set this to a path this script can write to for storing a persistent
--- cookie secret, which should be guarded.
-local secret_filename = "/var/cache/cgit/auth-secret"
-
---
---
--- Authentication functions follow below. Swap these out if you want different authentication semantics.
---
---
-
--- Sets HTTP cookie headers based on post and sets up redirection.
-function authenticate_post()
-	local hash = users[post["username"]]
-	local redirect = validate_value("redirect", post["redirect"])
-
-	if redirect == nil then
-		not_found()
-		return 0
-	end
-
-	redirect_to(redirect)
-
-	if hash == nil or hash ~= unistd.crypt(post["password"], hash) then
-		set_cookie("cgitauth", "")
-	else
-		-- One week expiration time
-		local username = secure_value("username", post["username"], os.time() + 604800)
-		set_cookie("cgitauth", username)
-	end
-
-	html("\n")
-	return 0
-end
-
-
--- Returns 1 if the cookie is valid and 0 if it is not.
-function authenticate_cookie()
-	accepted_users = protected_repos[cgit["repo"]]
-	if accepted_users == nil then
-		-- We return as valid if the repo is not protected.
-		return 1
-	end
-
-	local username = validate_value("username", get_cookie(http["cookie"], "cgitauth"))
-	if username == nil or not accepted_users[username:lower()] then
-		return 0
-	else
-		return 1
-	end
-end
-
--- Prints the html for the login form.
-function body()
-	html("<h2>Authentication Required</h2>")
-	html("<form method='post' action='")
-	html_attr(cgit["login"])
-	html("'>")
-	html("<input type='hidden' name='redirect' value='")
-	html_attr(secure_value("redirect", cgit["url"], 0))
-	html("' />")
-	html("<table>")
-	html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>")
-	html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>")
-	html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>")
-	html("</table></form>")
-
-	return 0
-end
-
-
-
---
---
--- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions.
---
---
-
-local actions = {}
-actions["authenticate-post"] = authenticate_post
-actions["authenticate-cookie"] = authenticate_cookie
-actions["body"] = body
-
-function filter_open(...)
-	action = actions[select(1, ...)]
-
-	http = {}
-	http["cookie"] = select(2, ...)
-	http["method"] = select(3, ...)
-	http["query"] = select(4, ...)
-	http["referer"] = select(5, ...)
-	http["path"] = select(6, ...)
-	http["host"] = select(7, ...)
-	http["https"] = select(8, ...)
-
-	cgit = {}
-	cgit["repo"] = select(9, ...)
-	cgit["page"] = select(10, ...)
-	cgit["url"] = select(11, ...)
-	cgit["login"] = select(12, ...)
-
-end
-
-function filter_close()
-	return action()
-end
-
-function filter_write(str)
-	post = parse_qs(str)
-end
-
-
---
---
--- Utility functions based on keplerproject/wsapi.
---
---
-
-function url_decode(str)
-	if not str then
-		return ""
-	end
-	str = string.gsub(str, "+", " ")
-	str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end)
-	str = string.gsub(str, "\r\n", "\n")
-	return str
-end
-
-function url_encode(str)
-	if not str then
-		return ""
-	end
-	str = string.gsub(str, "\n", "\r\n")
-	str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end)
-	str = string.gsub(str, " ", "+")
-	return str
-end
-
-function parse_qs(qs)
-	local tab = {}
-	for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do
-		tab[url_decode(key)] = url_decode(val)
-	end
-	return tab
-end
-
-function get_cookie(cookies, name)
-	cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";")
-	return url_decode(string.match(cookies, ";" .. name .. "=(.-);"))
-end
-
-function tohex(b)
-	local x = ""
-	for i = 1, #b do
-		x = x .. string.format("%.2x", string.byte(b, i))
-	end
-	return x
-end
-
---
---
--- Cookie construction and validation helpers.
---
---
-
-local secret = nil
-
--- Loads a secret from a file, creates a secret, or returns one from memory.
-function get_secret()
-	if secret ~= nil then
-		return secret
-	end
-	local secret_file = io.open(secret_filename, "r")
-	if secret_file == nil then
-		local old_umask = sysstat.umask(63)
-		local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16))
-		local temporary_file = io.open(temporary_filename, "w")
-		if temporary_file == nil then
-			os.exit(177)
-		end
-		temporary_file:write(tohex(rand.bytes(32)))
-		temporary_file:close()
-		unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same.
-		unistd.unlink(temporary_filename)
-		sysstat.umask(old_umask)
-		secret_file = io.open(secret_filename, "r")
-	end
-	if secret_file == nil then
-		os.exit(177)
-	end
-	secret = secret_file:read()
-	secret_file:close()
-	if secret:len() ~= 64 then
-		os.exit(177)
-	end
-	return secret
-end
-
--- Returns value of cookie if cookie is valid. Otherwise returns nil.
-function validate_value(expected_field, cookie)
-	local i = 0
-	local value = ""
-	local field = ""
-	local expiration = 0
-	local salt = ""
-	local chmac = ""
-
-	if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then
-		return nil
-	end
-
-	for component in string.gmatch(cookie, "[^|]+") do
-		if i == 0 then
-			field = component
-		elseif i == 1 then
-			value = component
-		elseif i == 2 then
-			expiration = tonumber(component)
-			if expiration == nil then
-				expiration = -1
-			end
-		elseif i == 3 then
-			salt = component
-		elseif i == 4 then
-			chmac = component
-		else
-			break
-		end
-		i = i + 1
-	end
-
-	if chmac == nil or chmac:len() == 0 then
-		return nil
-	end
-
-	-- Lua hashes strings, so these comparisons are time invariant.
-	if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
-		return nil
-	end
-
-	if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then
-		return nil
-	end
-
-	if url_decode(field) ~= expected_field then
-		return nil
-	end
-
-	return url_decode(value)
-end
-
-function secure_value(field, value, expiration)
-	if value == nil or value:len() <= 0 then
-		return ""
-	end
-
-	local authstr = ""
-	local salt = tohex(rand.bytes(16))
-	value = url_encode(value)
-	field = url_encode(field)
-	authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
-	authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr))
-	return authstr
-end
-
-function set_cookie(cookie, value)
-	html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly")
-	if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then
-		html("; secure")
-	end
-	html("\n")
-end
-
-function redirect_to(url)
-	html("Status: 302 Redirect\n")
-	html("Cache-Control: no-cache, no-store\n")
-	html("Location: " .. url .. "\n")
-end
-
-function not_found()
-	html("Status: 404 Not Found\n")
-	html("Cache-Control: no-cache, no-store\n\n")
-end
diff --git a/third_party/cgit/html.c b/third_party/cgit/html.c
index 7f81965fdd..ced781adc6 100644
--- a/third_party/cgit/html.c
+++ b/third_party/cgit/html.c
@@ -59,7 +59,7 @@ char *fmt(const char *format, ...)
 	va_start(args, format);
 	len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args);
 	va_end(args);
-	if (len > sizeof(buf[bufidx])) {
+	if (len >= sizeof(buf[bufidx])) {
 		fprintf(stderr, "[html.c] string truncated: %s\n", format);
 		exit(1);
 	}
@@ -80,7 +80,7 @@ char *fmtalloc(const char *format, ...)
 
 void html_raw(const char *data, size_t size)
 {
-	if (write(STDOUT_FILENO, data, size) != size)
+	if (fwrite(data, 1, size, stdout) != size)
 		die_errno("write error on html output");
 }
 
diff --git a/third_party/cgit/parsing.c b/third_party/cgit/parsing.c
index e093aaf701..83d3521e89 100644
--- a/third_party/cgit/parsing.c
+++ b/third_party/cgit/parsing.c
@@ -198,7 +198,7 @@ struct taginfo *cgit_parse_tag(struct tag *tag)
 	const char *p;
 	struct taginfo *ret = NULL;
 
-	data = read_object_file(&tag->object.oid, &type, &size);
+	data = repo_read_object_file(the_repository, &tag->object.oid, &type, &size);
 	if (!data || type != OBJ_TAG)
 		goto cleanup;
 
diff --git a/third_party/cgit/robots.txt b/third_party/cgit/robots.txt
index 4ce948fec2..1b33266d53 100644
--- a/third_party/cgit/robots.txt
+++ b/third_party/cgit/robots.txt
@@ -1,3 +1,4 @@
 User-agent: *
 Disallow: /*/snapshot/*
+Disallow: /*/blame/*
 Allow: /
diff --git a/third_party/cgit/scan-tree.c b/third_party/cgit/scan-tree.c
index 6a2f65a86b..aa93665426 100644
--- a/third_party/cgit/scan-tree.c
+++ b/third_party/cgit/scan-tree.c
@@ -54,7 +54,7 @@ static void scan_tree_repo_config(const char *name, const char *value)
 	config_fn(repo, name, value);
 }
 
-static int gitconfig_config(const char *key, const char *value, void *cb)
+static int gitconfig_config(const char *key, const char *value, const struct config_context *, void *cb)
 {
 	const char *name;
 
@@ -137,8 +137,6 @@ static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn)
 	repo->path = xstrdup(path->buf);
 	while (!repo->owner) {
 		if ((pwd = getpwuid(st.st_uid)) == NULL) {
-			fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
-				path->buf, strerror(errno), errno);
 			break;
 		}
 		if (pwd->pw_gecos)
diff --git a/third_party/cgit/shared.c b/third_party/cgit/shared.c
index 8115469a7c..26b6ddb329 100644
--- a/third_party/cgit/shared.c
+++ b/third_party/cgit/shared.c
@@ -241,7 +241,7 @@ static int load_mmfile(mmfile_t *file, const struct object_id *oid)
 		file->ptr = (char *)"";
 		file->size = 0;
 	} else {
-		file->ptr = read_object_file(oid, &type,
+		file->ptr = repo_read_object_file(the_repository, oid, &type,
 		                           (unsigned long *)&file->size);
 	}
 	return 1;
@@ -341,10 +341,9 @@ void cgit_diff_tree(const struct object_id *old_oid,
 		    filepair_fn fn, const char *prefix, int ignorews)
 {
 	struct diff_options opt;
-	struct pathspec_item item;
+	struct pathspec_item *item;
 
-	memset(&item, 0, sizeof(item));
-	diff_setup(&opt);
+	repo_diff_setup(the_repository, &opt);
 	opt.output_format = DIFF_FORMAT_CALLBACK;
 	opt.detect_rename = 1;
 	opt.rename_limit = ctx.cfg.renamelimit;
@@ -354,10 +353,11 @@ void cgit_diff_tree(const struct object_id *old_oid,
 	opt.format_callback = cgit_diff_tree_cb;
 	opt.format_callback_data = fn;
 	if (prefix) {
-		item.match = xstrdup(prefix);
-		item.len = strlen(prefix);
+		item = xcalloc(1, sizeof(*item));
+		item->match = xstrdup(prefix);
+		item->len = strlen(prefix);
 		opt.pathspec.nr = 1;
-		opt.pathspec.items = &item;
+		opt.pathspec.items = item;
 	}
 	diff_setup_done(&opt);
 
@@ -367,8 +367,6 @@ void cgit_diff_tree(const struct object_id *old_oid,
 		diff_root_tree_oid(new_oid, "", &opt);
 	diffcore_std(&opt);
 	diff_flush(&opt);
-
-	free(item.match);
 }
 
 void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix)
@@ -541,7 +539,9 @@ char *expand_macros(const char *txt)
 
 char *get_mimetype_for_filename(const char *filename)
 {
-	char *ext, *mimetype, *token, line[1024], *saveptr;
+	char *ext, *mimetype, line[1024];
+	struct string_list list = STRING_LIST_INIT_NODUP;
+	int i;
 	FILE *file;
 	struct string_list_item *mime;
 
@@ -566,13 +566,16 @@ char *get_mimetype_for_filename(const char *filename)
 	while (fgets(line, sizeof(line), file)) {
 		if (!line[0] || line[0] == '#')
 			continue;
-		mimetype = strtok_r(line, " \t\r\n", &saveptr);
-		while ((token = strtok_r(NULL, " \t\r\n", &saveptr))) {
-			if (!strcasecmp(ext, token)) {
+		string_list_split_in_place(&list, line, " \t\r\n", -1);
+		string_list_remove_empty_items(&list, 0);
+		mimetype = list.items[0].string;
+		for (i = 1; i < list.nr; i++) {
+			if (!strcasecmp(ext, list.items[i].string)) {
 				fclose(file);
 				return xstrdup(mimetype);
 			}
 		}
+		string_list_clear(&list, 0);
 	}
 	fclose(file);
 	return NULL;
diff --git a/third_party/cgit/tests/filters/dump.lua b/third_party/cgit/tests/filters/dump.lua
deleted file mode 100644
index 1f15c93105..0000000000
--- a/third_party/cgit/tests/filters/dump.lua
+++ /dev/null
@@ -1,17 +0,0 @@
-function filter_open(...)
-	buffer = ""
-	for i = 1, select("#", ...) do
-		buffer = buffer .. select(i, ...) .. " "
-	end
-end
-
-function filter_close()
-	html(buffer)
-	return 0
-end
-
-function filter_write(str)
-	buffer = buffer .. string.upper(str)
-end
-
-
diff --git a/third_party/cgit/tests/setup.sh b/third_party/cgit/tests/setup.sh
index 8db810ff11..31e7d5bb27 100755
--- a/third_party/cgit/tests/setup.sh
+++ b/third_party/cgit/tests/setup.sh
@@ -60,12 +60,6 @@ fi
 
 FILTER_DIRECTORY=$(cd ../filters && pwd)
 
-if cgit --version | grep -F -q "[+] Lua scripting"; then
-	export CGIT_HAS_LUA=1
-else
-	export CGIT_HAS_LUA=0
-fi
-
 mkrepo() {
 	name=$1
 	count=$2
@@ -144,19 +138,6 @@ repo.email-filter=exec:$FILTER_DIRECTORY/dump.sh
 repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh
 repo.readme=master:a+b
 EOF
-
-	if [ $CGIT_HAS_LUA -eq 1 ]; then
-		cat >>cgitrc <<EOF
-repo.url=filter-lua
-repo.path=$PWD/repos/filter/.git
-repo.desc=filtered repo
-repo.about-filter=lua:$FILTER_DIRECTORY/dump.lua
-repo.commit-filter=lua:$FILTER_DIRECTORY/dump.lua
-repo.email-filter=lua:$FILTER_DIRECTORY/dump.lua
-repo.source-filter=lua:$FILTER_DIRECTORY/dump.lua
-repo.readme=master:a+b
-EOF
-	fi
 }
 
 cgit_query()
diff --git a/third_party/cgit/tests/t0105-commit.sh b/third_party/cgit/tests/t0105-commit.sh
index 1a12ee39a9..cfed1e7d69 100755
--- a/third_party/cgit/tests/t0105-commit.sh
+++ b/third_party/cgit/tests/t0105-commit.sh
@@ -11,7 +11,7 @@ test_expect_success 'find commit subject' '
 	grep "<div class=.commit-subject.>commit 5<" tmp
 '
 
-test_expect_success 'find commit msg' 'grep "<div class=.commit-msg.></div>" tmp'
+test_expect_success 'find commit msg' 'grep "<pre class=.commit-msg.></pre>" tmp'
 test_expect_success 'find diffstat' 'grep "<table summary=.diffstat. class=.diffstat.>" tmp'
 
 test_expect_success 'find diff summary' '
@@ -29,8 +29,8 @@ test_expect_success 'root commit contains diffstat' '
 '
 
 test_expect_success 'root commit contains diff' '
-	grep ">diff --git a/file-1 b/file-1<" tmp &&
-	grep "<div class=.add.>+1</div>" tmp
+	grep ">diff --git a/file-1 b/file-1" tmp &&
+	grep "<span class=.add.>+1</span>" tmp
 '
 
 test_done
diff --git a/third_party/cgit/tests/t0106-diff.sh b/third_party/cgit/tests/t0106-diff.sh
index 82b645ec72..62a0a74a64 100755
--- a/third_party/cgit/tests/t0106-diff.sh
+++ b/third_party/cgit/tests/t0106-diff.sh
@@ -9,11 +9,11 @@ test_expect_success 'find blob link' 'grep "<a href=./foo/tree/file-5?id=" tmp'
 test_expect_success 'find added file' 'grep "new file mode 100644" tmp'
 
 test_expect_success 'find hunk header' '
-	grep "<div class=.hunk.>@@ -0,0 +1 @@</div>" tmp
+	grep "<span class=.hunk.>@@ -0,0 +1 @@</span>" tmp
 '
 
 test_expect_success 'find added line' '
-	grep "<div class=.add.>+5</div>" tmp
+	grep "<span class=.add.>+5</span>" tmp
 '
 
 test_done
diff --git a/third_party/cgit/tests/t0111-filter.sh b/third_party/cgit/tests/t0111-filter.sh
index 2fdc3669f4..e5d357507c 100755
--- a/third_party/cgit/tests/t0111-filter.sh
+++ b/third_party/cgit/tests/t0111-filter.sh
@@ -4,9 +4,6 @@ test_description='Check filtered content'
 . ./setup.sh
 
 prefixes="exec"
-if [ $CGIT_HAS_LUA -eq 1 ]; then
-	prefixes="$prefixes lua"
-fi
 
 for prefix in $prefixes
 do
diff --git a/third_party/cgit/ui-atom.c b/third_party/cgit/ui-atom.c
index 1056f36397..fefbc79809 100644
--- a/third_party/cgit/ui-atom.c
+++ b/third_party/cgit/ui-atom.c
@@ -67,17 +67,12 @@ static void add_entry(struct commit *commit, const char *host)
 		html("'/>\n");
 		free(pageurl);
 	}
-	htmlf("<id>%s</id>\n", hex);
+	html("<id>");
+	html_txtf("urn:%s:%s", the_hash_algo->name, hex);
+	html("</id>\n");
 	html("<content type='text'>\n");
 	html_txt(info->msg);
 	html("</content>\n");
-	html("<content type='xhtml'>\n");
-	html("<div xmlns='http://www.w3.org/1999/xhtml'>\n");
-	html("<pre>\n");
-	html_txt(info->msg);
-	html("</pre>\n");
-	html("</div>\n");
-	html("</content>\n");
 	html("</entry>\n");
 	cgit_free_commitinfo(info);
 }
@@ -90,6 +85,7 @@ void cgit_print_atom(char *tip, const char *path, int max_count)
 	struct commit *commit;
 	struct rev_info rev;
 	int argc = 2;
+	int first = 1;
 
 	if (ctx.qry.show_all)
 		argv[1] = "--all";
@@ -101,7 +97,7 @@ void cgit_print_atom(char *tip, const char *path, int max_count)
 		argv[argc++] = path;
 	}
 
-	init_revisions(&rev, NULL);
+	repo_init_revisions(the_repository, &rev, NULL);
 	rev.abbrev = DEFAULT_ABBREV;
 	rev.commit_format = CMIT_FMT_DEFAULT;
 	rev.verbose_header = 1;
@@ -130,18 +126,30 @@ void cgit_print_atom(char *tip, const char *path, int max_count)
 	html_txt(ctx.repo->desc);
 	html("</subtitle>\n");
 	if (host) {
+		char *fullurl = cgit_currentfullurl();
 		char *repourl = cgit_repourl(ctx.repo->url);
+		html("<id>");
+		html_txtf("%s%s%s", cgit_httpscheme(), host, fullurl);
+		html("</id>\n");
+		html("<link rel='self' href='");
+		html_attrf("%s%s%s", cgit_httpscheme(), host, fullurl);
+		html("'/>\n");
 		html("<link rel='alternate' type='text/html' href='");
-		html(cgit_httpscheme());
-		html_attr(host);
-		html_attr(repourl);
+		html_attrf("%s%s%s", cgit_httpscheme(), host, repourl);
 		html("'/>\n");
+		free(fullurl);
 		free(repourl);
 	}
 	while ((commit = get_revision(&rev)) != NULL) {
+		if (first) {
+			html("<updated>");
+			html_txt(show_date(commit->date, 0,
+				date_mode_from_type(DATE_ISO8601_STRICT)));
+			html("</updated>\n");
+			first = 0;
+		}
 		add_entry(commit, host);
-		free_commit_buffer(the_repository->parsed_objects, commit);
-		free_commit_list(commit->parents);
+		release_commit_memory(the_repository->parsed_objects, commit);
 		commit->parents = NULL;
 	}
 	html("</feed>\n");
diff --git a/third_party/cgit/ui-blame.c b/third_party/cgit/ui-blame.c
index 786a7105d5..6418b24221 100644
--- a/third_party/cgit/ui-blame.c
+++ b/third_party/cgit/ui-blame.c
@@ -49,11 +49,20 @@ static void emit_blame_entry_hash(struct blame_entry *ent)
 
 	char *detail = emit_suspect_detail(suspect);
 	html("<span class='oid'>");
-	cgit_commit_link(find_unique_abbrev(oid, DEFAULT_ABBREV), detail,
+	cgit_commit_link(repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV), detail,
 			 NULL, ctx.qry.head, oid_to_hex(oid), suspect->path);
 	html("</span>");
 	free(detail);
 
+	if (!repo_parse_commit(the_repository, suspect->commit) && suspect->commit->parents) {
+		struct commit *parent = suspect->commit->parents->item;
+
+		html(" ");
+		cgit_blame_link("^", "Blame the previous revision", NULL,
+				ctx.qry.head, oid_to_hex(&parent->object.oid),
+				suspect->path);
+	}
+
 	while (line++ < ent->num_lines)
 		html("\n");
 }
@@ -117,7 +126,7 @@ static void print_object(const struct object_id *oid, const char *path,
 		return;
 	}
 
-	buf = read_object_file(oid, &type, &size);
+	buf = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!buf) {
 		cgit_print_error_page(500, "Internal server error",
 			"Error reading object %s", oid_to_hex(oid));
@@ -126,7 +135,7 @@ static void print_object(const struct object_id *oid, const char *path,
 
 	strvec_push(&rev_argv, "blame");
 	strvec_push(&rev_argv, rev);
-	init_revisions(&revs, NULL);
+	repo_init_revisions(the_repository, &revs, NULL);
 	revs.diffopt.flags.allow_textconv = 1;
 	setup_revisions(rev_argv.nr, rev_argv.v, &revs, NULL);
 	init_scoreboard(&sb);
@@ -278,13 +287,13 @@ void cgit_print_blame(void)
 	if (!rev)
 		rev = ctx.qry.head;
 
-	if (get_oid(rev, &oid)) {
+	if (repo_get_oid(the_repository, rev, &oid)) {
 		cgit_print_error_page(404, "Not found",
 			"Invalid revision name: %s", rev);
 		return;
 	}
 	commit = lookup_commit_reference(the_repository, &oid);
-	if (!commit || parse_commit(commit)) {
+	if (!commit || repo_parse_commit(the_repository, commit)) {
 		cgit_print_error_page(404, "Not found",
 			"Invalid commit reference: %s", rev);
 		return;
diff --git a/third_party/cgit/ui-blob.c b/third_party/cgit/ui-blob.c
index c10ae42ebd..08f94ee97e 100644
--- a/third_party/cgit/ui-blob.c
+++ b/third_party/cgit/ui-blob.c
@@ -52,7 +52,7 @@ int cgit_ref_path_exists(const char *path, const char *ref, int file_only)
 		.file_only = file_only
 	};
 
-	if (get_oid(ref, &oid))
+	if (repo_get_oid(the_repository, ref, &oid))
 		goto done;
 	if (oid_object_info(the_repository, &oid, &size) != OBJ_COMMIT)
 		goto done;
@@ -87,7 +87,7 @@ int cgit_print_file(char *path, const char *head, int file_only)
 		.file_only = file_only
 	};
 
-	if (get_oid(head, &oid))
+	if (repo_get_oid(the_repository, head, &oid))
 		return -1;
 	type = oid_object_info(the_repository, &oid, &size);
 	if (type == OBJ_COMMIT) {
@@ -100,7 +100,7 @@ int cgit_print_file(char *path, const char *head, int file_only)
 	}
 	if (type == OBJ_BAD)
 		return -1;
-	buf = read_object_file(&oid, &type, &size);
+	buf = repo_read_object_file(the_repository, &oid, &type, &size);
 	if (!buf)
 		return -1;
 	buf[size] = '\0';
@@ -138,7 +138,7 @@ void cgit_print_blob(const char *hex, char *path, const char *head, int file_onl
 			return;
 		}
 	} else {
-		if (get_oid(head, &oid)) {
+		if (repo_get_oid(the_repository, head, &oid)) {
 			cgit_print_error_page(404, "Not found",
 					"Bad ref: %s", head);
 			return;
@@ -160,7 +160,7 @@ void cgit_print_blob(const char *hex, char *path, const char *head, int file_onl
 		return;
 	}
 
-	buf = read_object_file(&oid, &type, &size);
+	buf = repo_read_object_file(the_repository, &oid, &type, &size);
 	if (!buf) {
 		cgit_print_error_page(500, "Internal server error",
 				"Error reading object %s", hex);
diff --git a/third_party/cgit/ui-commit.c b/third_party/cgit/ui-commit.c
index 96eecebe19..6517e50cc6 100644
--- a/third_party/cgit/ui-commit.c
+++ b/third_party/cgit/ui-commit.c
@@ -26,7 +26,7 @@ void cgit_print_commit(char *hex, const char *prefix)
 	if (!hex)
 		hex = ctx.qry.head;
 
-	if (get_oid(hex, &oid)) {
+	if (repo_get_oid(the_repository, hex, &oid)) {
 		cgit_print_error_page(400, "Bad request",
 				"Bad object id: %s", hex);
 		return;
@@ -39,7 +39,7 @@ void cgit_print_commit(char *hex, const char *prefix)
 	}
 	info = cgit_parse_commit(commit);
 
-	format_display_notes(&oid, &notes, PAGE_ENCODING, 0);
+	format_display_notes(&oid, &notes, PAGE_ENCODING, 1);
 
 	load_ref_decorations(NULL, DECORATE_FULL_REFS);
 
@@ -121,11 +121,11 @@ void cgit_print_commit(char *hex, const char *prefix)
 	cgit_close_filter(ctx.repo->commit_filter);
 	show_commit_decorations(commit);
 	html("</div>");
-	html("<div class='commit-msg'>");
+	html("<pre class='commit-msg'>");
 	cgit_open_filter(ctx.repo->commit_filter);
 	html_txt(info->msg);
 	cgit_close_filter(ctx.repo->commit_filter);
-	html("</div>");
+	html("</pre>");
 	if (notes.len != 0) {
 		html("<div class='notes-header'>Notes</div>");
 		html("<div class='notes'>");
diff --git a/third_party/cgit/ui-diff.c b/third_party/cgit/ui-diff.c
index 5ed5990c29..a82313fc64 100644
--- a/third_party/cgit/ui-diff.c
+++ b/third_party/cgit/ui-diff.c
@@ -231,11 +231,11 @@ static void print_line(char *line, int len)
 	else if (line[0] == '@')
 		class = "hunk";
 
-	htmlf("<div class='%s'>", class);
+	htmlf("<span class='%s'>", class);
 	line[len-1] = '\0';
 	html_txt(line);
-	html("</div>");
 	line[len-1] = c;
+	html("</span>\n");
 }
 
 static void header(const struct object_id *oid1, char *path1, int mode1,
@@ -245,22 +245,23 @@ static void header(const struct object_id *oid1, char *path1, int mode1,
 	int subproject;
 
 	subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
-	html("<div class='head'>");
+	html("<span class='head'>");
 	html("diff --git a/");
 	html_txt(path1);
 	html(" b/");
 	html_txt(path2);
+	html("\n");
 
 	if (mode1 == 0)
-		htmlf("<br/>new file mode %.6o", mode2);
+		htmlf("new file mode %.6o\n", mode2);
 
 	if (mode2 == 0)
-		htmlf("<br/>deleted file mode %.6o", mode1);
+		htmlf("deleted file mode %.6o\n", mode1);
 
 	if (!subproject) {
-		abbrev1 = xstrdup(find_unique_abbrev(oid1, DEFAULT_ABBREV));
-		abbrev2 = xstrdup(find_unique_abbrev(oid2, DEFAULT_ABBREV));
-		htmlf("<br/>index %s..%s", abbrev1, abbrev2);
+		abbrev1 = xstrdup(repo_find_unique_abbrev(the_repository, oid1, DEFAULT_ABBREV));
+		abbrev2 = xstrdup(repo_find_unique_abbrev(the_repository, oid2, DEFAULT_ABBREV));
+		htmlf("index %s..%s", abbrev1, abbrev2);
 		free(abbrev1);
 		free(abbrev2);
 		if (mode1 != 0 && mode2 != 0) {
@@ -268,28 +269,31 @@ static void header(const struct object_id *oid1, char *path1, int mode1,
 			if (mode2 != mode1)
 				htmlf("..%.6o", mode2);
 		}
+		html("\n");
 		if (is_null_oid(oid1)) {
 			path1 = "dev/null";
-			html("<br/>--- /");
+			html("--- /");
 		} else
-			html("<br/>--- a/");
+			html("--- a/");
 		if (mode1 != 0)
 			cgit_tree_link(path1, NULL, NULL, ctx.qry.head,
 				       oid_to_hex(old_rev_oid), path1);
 		else
 			html_txt(path1);
+		html("\n");
 		if (is_null_oid(oid2)) {
 			path2 = "dev/null";
-			html("<br/>+++ /");
+			html("+++ /");
 		} else
-			html("<br/>+++ b/");
+			html("+++ b/");
 		if (mode2 != 0)
 			cgit_tree_link(path2, NULL, NULL, ctx.qry.head,
 				       oid_to_hex(new_rev_oid), path2);
 		else
 			html_txt(path2);
+		html("\n");
 	}
-	html("</div>");
+	html("</span>");
 }
 
 static void filepair_cb(struct diff_filepair *pair)
@@ -402,13 +406,13 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
 
 	if (!new_rev)
 		new_rev = ctx.qry.head;
-	if (get_oid(new_rev, new_rev_oid)) {
+	if (repo_get_oid(the_repository, new_rev, new_rev_oid)) {
 		cgit_print_error_page(404, "Not found",
 			"Bad object name: %s", new_rev);
 		return;
 	}
 	commit = lookup_commit_reference(the_repository, new_rev_oid);
-	if (!commit || parse_commit(commit)) {
+	if (!commit || repo_parse_commit(the_repository, commit)) {
 		cgit_print_error_page(404, "Not found",
 			"Bad commit: %s", oid_to_hex(new_rev_oid));
 		return;
@@ -416,7 +420,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
 	new_tree_oid = get_commit_tree_oid(commit);
 
 	if (old_rev) {
-		if (get_oid(old_rev, old_rev_oid)) {
+		if (repo_get_oid(the_repository, old_rev, old_rev_oid)) {
 			cgit_print_error_page(404, "Not found",
 				"Bad object name: %s", old_rev);
 			return;
@@ -429,7 +433,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
 
 	if (!is_null_oid(old_rev_oid)) {
 		commit2 = lookup_commit_reference(the_repository, old_rev_oid);
-		if (!commit2 || parse_commit(commit2)) {
+		if (!commit2 || repo_parse_commit(the_repository, commit2)) {
 			cgit_print_error_page(404, "Not found",
 				"Bad commit: %s", oid_to_hex(old_rev_oid));
 			return;
@@ -442,7 +446,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
 	if (raw) {
 		struct diff_options diffopt;
 
-		diff_setup(&diffopt);
+		repo_diff_setup(the_repository, &diffopt);
 		diffopt.output_format = DIFF_FORMAT_PATCH;
 		diffopt.flags.recursive = 1;
 		diff_setup_done(&diffopt);
@@ -488,12 +492,12 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
 		html("<table summary='ssdiff' class='ssdiff'>");
 	} else {
 		html("<table summary='diff' class='diff'>");
-		html("<tr><td>");
+		html("<tr><td><pre>");
 	}
 	cgit_diff_tree(old_rev_oid, new_rev_oid, filepair_cb, prefix,
 		       ctx.qry.ignorews);
 	if (!use_ssdiff)
-		html("</td></tr>");
+		html("</pre></td></tr>");
 	html("</table>");
 
 	if (show_ctrls)
diff --git a/third_party/cgit/ui-log.c b/third_party/cgit/ui-log.c
index 20774bf82f..358cdec4e7 100644
--- a/third_party/cgit/ui-log.c
+++ b/third_party/cgit/ui-log.c
@@ -71,15 +71,27 @@ void show_commit_decorations(struct commit *commit)
 		strlcpy(buf, prettify_refname(deco->name), sizeof(buf));
 		switch(deco->type) {
 		case DECORATION_NONE:
+			/* If it is a depot revision, display it, otherwise
+			 * ... */
+			if (strncmp("refs/r/", buf, 7) == 0) {
+				html(" ");
+				cgit_log_link(/* trim 'refs/' */ buf + 5,
+					NULL, "rev-deco", buf, NULL,
+					ctx.qry.vpath, 0, NULL, NULL,
+					ctx.qry.showmsg, 0);
+			}
+
 			/* If the git-core doesn't recognize it,
 			 * don't display anything. */
 			break;
 		case DECORATION_REF_LOCAL:
+			html(" ");
 			cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
 				ctx.qry.vpath, 0, NULL, NULL,
 				ctx.qry.showmsg, 0);
 			break;
 		case DECORATION_REF_TAG:
+			html(" ");
 			if (!read_ref(deco->name, &oid_tag) && !peel_iterated_oid(&oid_tag, &peeled))
 				is_annotated = !oideq(&oid_tag, &peeled);
 			cgit_tag_link(buf, NULL, is_annotated ? "tag-annotated-deco" : "tag-deco", buf);
@@ -87,12 +99,14 @@ void show_commit_decorations(struct commit *commit)
 		case DECORATION_REF_REMOTE:
 			if (!ctx.repo->enable_remote_branches)
 				break;
+			html(" ");
 			cgit_log_link(buf, NULL, "remote-deco", NULL,
 				oid_to_hex(&commit->object.oid),
 				ctx.qry.vpath, 0, NULL, NULL,
 				ctx.qry.showmsg, 0);
 			break;
 		default:
+			html(" ");
 			cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
 					oid_to_hex(&commit->object.oid),
 					ctx.qry.vpath);
@@ -146,7 +160,7 @@ static int show_commit(struct commit *commit, struct rev_info *revs)
 	/* When we get here we have precisely one parent. */
 	parent = parents->item;
 	/* If we can't parse the commit, let print_commit() report an error. */
-	if (parse_commit(parent))
+	if (repo_parse_commit(the_repository, parent))
 		return 1;
 
 	files = 0;
@@ -159,7 +173,7 @@ static int show_commit(struct commit *commit, struct rev_info *revs)
 		      "", &revs->diffopt);
 	diffcore_std(&revs->diffopt);
 
-	found = !diff_queue_is_empty();
+	found = !diff_queue_is_empty(&revs->diffopt);
 	saved_fmt = revs->diffopt.output_format;
 	revs->diffopt.output_format = DIFF_FORMAT_CALLBACK;
 	revs->diffopt.format_callback = cgit_diff_tree_cb;
@@ -238,9 +252,10 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
 			strlcpy(info->subject + i, wrap_symbol, subject_len - i + 1);
 		}
 	}
+	show_commit_decorations(commit);
+        html("&nbsp;");
 	cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
 			 oid_to_hex(&commit->object.oid), ctx.qry.vpath);
-	show_commit_decorations(commit);
 	html("</td><td>");
 	cgit_open_filter(ctx.repo->email_filter, info->author_email, "log");
 	html_txt(info->author);
@@ -330,7 +345,7 @@ static const char *disambiguate_ref(const char *ref, int *must_free_result)
 	struct strbuf longref = STRBUF_INIT;
 
 	strbuf_addf(&longref, "refs/heads/%s", ref);
-	if (get_oid(longref.buf, &oid) == 0) {
+	if (repo_get_oid(the_repository, longref.buf, &oid) == 0) {
 		*must_free_result = 1;
 		return strbuf_detach(&longref, NULL);
 	}
@@ -430,7 +445,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
 	if (path)
 		strvec_push(&rev_argv, path);
 
-	init_revisions(&rev, NULL);
+	repo_init_revisions(the_repository, &rev, NULL);
 	rev.abbrev = DEFAULT_ABBREV;
 	rev.commit_format = CMIT_FMT_DEFAULT;
 	rev.verbose_header = 1;
@@ -489,8 +504,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
 	for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; /* nop */) {
 		if (show_commit(commit, &rev))
 			i++;
-		free_commit_buffer(the_repository->parsed_objects, commit);
-		free_commit_list(commit->parents);
+		release_commit_memory(the_repository->parsed_objects, commit);
 		commit->parents = NULL;
 	}
 
@@ -511,8 +525,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
 			i++;
 			print_commit(commit, &rev);
 		}
-		free_commit_buffer(the_repository->parsed_objects, commit);
-		free_commit_list(commit->parents);
+		release_commit_memory(the_repository->parsed_objects, commit);
 		commit->parents = NULL;
 	}
 	if (pager) {
diff --git a/third_party/cgit/ui-patch.c b/third_party/cgit/ui-patch.c
index 4ac03cbef1..3819a8152a 100644
--- a/third_party/cgit/ui-patch.c
+++ b/third_party/cgit/ui-patch.c
@@ -31,7 +31,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev,
 	if (!new_rev)
 		new_rev = ctx.qry.head;
 
-	if (get_oid(new_rev, &new_rev_oid)) {
+	if (repo_get_oid(the_repository, new_rev, &new_rev_oid)) {
 		cgit_print_error_page(404, "Not found",
 				"Bad object id: %s", new_rev);
 		return;
@@ -44,7 +44,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev,
 	}
 
 	if (old_rev) {
-		if (get_oid(old_rev, &old_rev_oid)) {
+		if (repo_get_oid(the_repository, old_rev, &old_rev_oid)) {
 			cgit_print_error_page(404, "Not found",
 					"Bad object id: %s", old_rev);
 			return;
@@ -78,7 +78,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev,
 			      "%s%n%n%w(0)%b";
 	}
 
-	init_revisions(&rev, NULL);
+	repo_init_revisions(the_repository, &rev, NULL);
 	rev.abbrev = DEFAULT_ABBREV;
 	rev.verbose_header = 1;
 	rev.diff = 1;
diff --git a/third_party/cgit/ui-plain.c b/third_party/cgit/ui-plain.c
index 65a205fad7..a66c5a1de0 100644
--- a/third_party/cgit/ui-plain.c
+++ b/third_party/cgit/ui-plain.c
@@ -28,7 +28,7 @@ static int print_object(const struct object_id *oid, const char *path)
 		return 0;
 	}
 
-	buf = read_object_file(oid, &type, &size);
+	buf = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!buf) {
 		cgit_print_error_page(404, "Not found", "Not found");
 		return 0;
@@ -181,12 +181,12 @@ void cgit_print_plain(void)
 	if (!rev)
 		rev = ctx.qry.head;
 
-	if (get_oid(rev, &oid)) {
+	if (repo_get_oid(the_repository, rev, &oid)) {
 		cgit_print_error_page(404, "Not found", "Not found");
 		return;
 	}
 	commit = lookup_commit_reference(the_repository, &oid);
-	if (!commit || parse_commit(commit)) {
+	if (!commit || repo_parse_commit(the_repository, commit)) {
 		cgit_print_error_page(404, "Not found", "Not found");
 		return;
 	}
diff --git a/third_party/cgit/ui-repolist.c b/third_party/cgit/ui-repolist.c
index 529a2038ba..97b11c5f16 100644
--- a/third_party/cgit/ui-repolist.c
+++ b/third_party/cgit/ui-repolist.c
@@ -321,7 +321,7 @@ void cgit_print_repolist(void)
 		}
 		htmlf("<tr><td class='%s'>",
 		      !sorted && section ? "sublevel-repo" : "toplevel-repo");
-		cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
+		cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
 		html("</td><td>");
 		repourl = cgit_repourl(ctx.repo->url);
 		html_link_open(repourl, NULL, NULL);
@@ -353,8 +353,10 @@ void cgit_print_repolist(void)
 		if (ctx.cfg.enable_index_links) {
 			html("<td>");
 			cgit_summary_link("summary", NULL, "button", NULL);
+			html(" ");
 			cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
 				      0, NULL, NULL, ctx.qry.showmsg, 0);
+			html(" ");
 			cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
 			html("</td>");
 		}
diff --git a/third_party/cgit/ui-shared.c b/third_party/cgit/ui-shared.c
index cf7907ffe3..d047f9a131 100644
--- a/third_party/cgit/ui-shared.c
+++ b/third_party/cgit/ui-shared.c
@@ -836,7 +836,7 @@ void cgit_print_docend(void)
 	if (ctx.cfg.footer)
 		html_include(ctx.cfg.footer);
 	else {
-		htmlf("<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit %s</a> "
+		htmlf("<div class='footer'>generated by <a href='https://git.causal.agency/cgit-pink/about/'>cgit-pink %s</a> "
 			"(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string);
 		html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_DOTTIME)));
 		html("</div>\n");
@@ -898,7 +898,7 @@ void cgit_add_clone_urls(void (*fn)(const char *))
 static int print_this_commit_option(void)
 {
 	struct object_id oid;
-	if (get_oid(ctx.qry.head, &oid))
+	if (!ctx.qry.head || repo_get_oid(the_repository, ctx.qry.head, &oid))
 		return 1;
 	html_option(oid_to_hex(&oid), "this commit", ctx.qry.head);
 	return 0;
@@ -1003,7 +1003,7 @@ static void print_header(void)
 
 	html("<td class='main'>");
 	if (ctx.repo) {
-		cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
+		cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
 		if (ctx.env.authenticated) {
 			html("</td><td class='form'>");
 			html("<form method='get'>\n");
@@ -1027,7 +1027,13 @@ static void print_header(void)
 	if (ctx.repo) {
 		html_txt(ctx.repo->desc);
 		html("</td><td class='sub right'>");
-		html_txt(ctx.repo->owner);
+		if (ctx.repo->owner_filter) {
+			cgit_open_filter(ctx.repo->owner_filter);
+			html_txt(ctx.repo->owner);
+			cgit_close_filter(ctx.repo->owner_filter);
+		} else {
+			html_txt(ctx.repo->owner);
+		}
 	} else {
 		if (ctx.cfg.root_desc)
 			html_txt(ctx.cfg.root_desc);
@@ -1046,29 +1052,37 @@ void cgit_print_pageheader(void)
 		if (ctx.repo->readme.nr) {
 			cgit_about_link("about", NULL, hc("about"), ctx.qry.head,
 					 ctx.qry.oid, ctx.qry.vpath);
+			html(" ");
 		}
 		cgit_summary_link("summary", NULL, hc("summary"),
 				  ctx.qry.head);
+		html(" ");
 		cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head,
 			       ctx.qry.oid, NULL);
+		html(" ");
 		cgit_log_link("log", NULL, hc("log"), ctx.qry.head,
 			      NULL, ctx.qry.vpath, 0, NULL, NULL,
 			      ctx.qry.showmsg, ctx.qry.follow);
+		html(" ");
 		if (ctx.qry.page && !strcmp(ctx.qry.page, "blame"))
 			cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head,
 				        ctx.qry.oid, ctx.qry.vpath);
 		else
 			cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
-				       ctx.qry.oid, ctx.qry.vpath);
+				        ctx.qry.oid, ctx.qry.vpath);
+		html(" ");
 		cgit_commit_link("commit", NULL, hc("commit"),
 				 ctx.qry.head, ctx.qry.oid, ctx.qry.vpath);
+		html(" ");
 		cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head,
 			       ctx.qry.oid, ctx.qry.oid2, ctx.qry.vpath);
-		if (ctx.repo->max_stats)
+		if (ctx.repo->max_stats) {
+			html(" ");
 			cgit_stats_link("stats", NULL, hc("stats"),
 					ctx.qry.head, ctx.qry.vpath);
+		}
 		if (ctx.repo->homepage) {
-			html("<a href='");
+			html(" <a href='");
 			html_attr(ctx.repo->homepage);
 			html("'>homepage</a>");
 		}
@@ -1153,11 +1167,11 @@ void cgit_compose_snapshot_prefix(struct strbuf *filename, const char *base,
 	 * name starts with {v,V}[0-9] and the prettify mapping is injective,
 	 * i.e. each stripped tag can be inverted without ambiguities.
 	 */
-	if (get_oid(fmt("refs/tags/%s", ref), &oid) == 0 &&
+	if (repo_get_oid(the_repository, fmt("refs/tags/%s", ref), &oid) == 0 &&
 	    (ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1]) &&
-	    ((get_oid(fmt("refs/tags/%s", ref + 1), &oid) == 0) +
-	     (get_oid(fmt("refs/tags/v%s", ref + 1), &oid) == 0) +
-	     (get_oid(fmt("refs/tags/V%s", ref + 1), &oid) == 0) == 1))
+	    ((repo_get_oid(the_repository, fmt("refs/tags/%s", ref + 1), &oid) == 0) +
+	     (repo_get_oid(the_repository, fmt("refs/tags/v%s", ref + 1), &oid) == 0) +
+	     (repo_get_oid(the_repository, fmt("refs/tags/V%s", ref + 1), &oid) == 0) == 1))
 		ref++;
 
 	strbuf_addf(filename, "%s-%s", base, ref);
@@ -1212,9 +1226,12 @@ void cgit_set_title_from_path(const char *path)
 	if (!path)
 		return;
 
-	for (last_slash = path + strlen(path); (slash = memrchr(path, '/', last_slash - path)) != NULL; last_slash = slash) {
+	last_slash = path + strlen(path);
+	for (slash = last_slash; slash > path; --slash) {
+		if (*slash != '/') continue;
 		strbuf_add(&sb, slash + 1, last_slash - slash - 1);
 		strbuf_addstr(&sb, " \xc2\xab ");
+		last_slash = slash;
 	}
 	strbuf_add(&sb, path, last_slash - path);
 	strbuf_addf(&sb, " - %s", ctx.page.title);
diff --git a/third_party/cgit/ui-snapshot.c b/third_party/cgit/ui-snapshot.c
index 18361a6553..992853bcd7 100644
--- a/third_party/cgit/ui-snapshot.c
+++ b/third_party/cgit/ui-snapshot.c
@@ -37,6 +37,9 @@ static int write_archive_type(const char *format, const char *hex, const char *p
 	/* strvec guarantees a trailing NULL entry. */
 	memcpy(nargv, argv.v, sizeof(char *) * (argv.nr + 1));
 
+	if (fflush(stdout))
+		return errno;
+
 	result = write_archive(argv.nr, nargv, NULL, the_repository, NULL, 0);
 	strvec_clear(&argv);
 	free(nargv);
@@ -117,7 +120,7 @@ const struct object_id *cgit_snapshot_get_sig(const char *ref,
 	struct notes_tree *tree;
 	struct object_id oid;
 
-	if (get_oid(ref, &oid))
+	if (repo_get_oid(the_repository, ref, &oid))
 		return NULL;
 
 	tree = &snapshot_sig_notes[f - &cgit_snapshot_formats[0]];
@@ -156,7 +159,7 @@ static int make_snapshot(const struct cgit_snapshot_format *format,
 {
 	struct object_id oid;
 
-	if (get_oid(hex, &oid)) {
+	if (repo_get_oid(the_repository, hex, &oid)) {
 		cgit_print_error_page(404, "Not found",
 				"Bad object id: %s", hex);
 		return 1;
@@ -190,7 +193,7 @@ static int write_sig(const struct cgit_snapshot_format *format,
 		return 0;
 	}
 
-	buf = read_object_file(note, &type, &size);
+	buf = repo_read_object_file(the_repository, note, &type, &size);
 	if (!buf) {
 		cgit_print_error_page(404, "Not found", "Not found");
 		return 0;
@@ -230,7 +233,7 @@ static const char *get_ref_from_filename(const struct cgit_repo *repo,
 	strbuf_addstr(&snapshot, filename);
 	strbuf_setlen(&snapshot, snapshot.len - strlen(format->suffix));
 
-	if (get_oid(snapshot.buf, &oid) == 0)
+	if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0)
 		goto out;
 
 	reponame = cgit_snapshot_prefix(repo);
@@ -242,15 +245,15 @@ static const char *get_ref_from_filename(const struct cgit_repo *repo,
 		strbuf_splice(&snapshot, 0, new_start - snapshot.buf, "", 0);
 	}
 
-	if (get_oid(snapshot.buf, &oid) == 0)
+	if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0)
 		goto out;
 
 	strbuf_insert(&snapshot, 0, "v", 1);
-	if (get_oid(snapshot.buf, &oid) == 0)
+	if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0)
 		goto out;
 
 	strbuf_splice(&snapshot, 0, 1, "V", 1);
-	if (get_oid(snapshot.buf, &oid) == 0)
+	if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0)
 		goto out;
 
 	result = 0;
diff --git a/third_party/cgit/ui-stats.c b/third_party/cgit/ui-stats.c
index 09b3625e7e..9aed4ac3bf 100644
--- a/third_party/cgit/ui-stats.c
+++ b/third_party/cgit/ui-stats.c
@@ -230,7 +230,7 @@ static struct string_list collect_stats(const struct cgit_period *period)
 		argv[4] = ctx.qry.path;
 		argc += 2;
 	}
-	init_revisions(&rev, NULL);
+	repo_init_revisions(the_repository, &rev, NULL);
 	rev.abbrev = DEFAULT_ABBREV;
 	rev.commit_format = CMIT_FMT_DEFAULT;
 	rev.max_parents = 1;
@@ -241,8 +241,7 @@ static struct string_list collect_stats(const struct cgit_period *period)
 	memset(&authors, 0, sizeof(authors));
 	while ((commit = get_revision(&rev)) != NULL) {
 		add_commit(&authors, commit, period);
-		free_commit_buffer(the_repository->parsed_objects, commit);
-		free_commit_list(commit->parents);
+		release_commit_memory(the_repository->parsed_objects, commit);
 		commit->parents = NULL;
 	}
 	return authors;
diff --git a/third_party/cgit/ui-summary.c b/third_party/cgit/ui-summary.c
index d1bb6a59fa..ec7b76fabf 100644
--- a/third_party/cgit/ui-summary.c
+++ b/third_party/cgit/ui-summary.c
@@ -114,6 +114,9 @@ void cgit_print_repo_readme(const char *path)
 	}
 	free(mimetype);
 
+	if (path)
+		ctx.page.title = fmtalloc("%s - %s", path, ctx.page.title);
+
 	cgit_print_layout_start();
 	if (ctx.repo->readme.nr == 0)
 		goto done;
diff --git a/third_party/cgit/ui-tag.c b/third_party/cgit/ui-tag.c
index 08789102f6..be1122b905 100644
--- a/third_party/cgit/ui-tag.c
+++ b/third_party/cgit/ui-tag.c
@@ -25,9 +25,9 @@ static void print_tag_content(char *buf)
 	html_txt(buf);
 	html("</div>");
 	if (p) {
-		html("<div class='commit-msg'>");
+		html("<pre class='commit-msg'>");
 		html_txt(++p);
-		html("</div>");
+		html("</pre>");
 	}
 }
 
@@ -48,7 +48,7 @@ void cgit_print_tag(char *revname)
 		revname = ctx.qry.head;
 
 	strbuf_addf(&fullref, "refs/tags/%s", revname);
-	if (get_oid(fullref.buf, &oid)) {
+	if (repo_get_oid(the_repository, fullref.buf, &oid)) {
 		cgit_print_error_page(404, "Not found",
 			"Bad tag reference: %s", revname);
 		goto cleanup;
diff --git a/third_party/cgit/ui-tree.c b/third_party/cgit/ui-tree.c
index 1907450763..436b333809 100644
--- a/third_party/cgit/ui-tree.c
+++ b/third_party/cgit/ui-tree.c
@@ -98,7 +98,7 @@ static void print_object(const struct object_id *oid, const char *path, const ch
 		return;
 	}
 
-	buf = read_object_file(oid, &type, &size);
+	buf = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!buf) {
 		cgit_print_error_page(500, "Internal server error",
 			"Error reading object %s", oid_to_hex(oid));
@@ -242,7 +242,7 @@ static int ls_item(const struct object_id *oid, struct strbuf *base,
 	}
 	if (S_ISLNK(mode)) {
 		html(" -> ");
-		buf = read_object_file(oid, &type, &size);
+		buf = repo_read_object_file(the_repository, oid, &type, &size);
 		if (!buf) {
 			htmlf("Error reading object: %s", oid_to_hex(oid));
 			goto cleanup;
@@ -261,15 +261,21 @@ static int ls_item(const struct object_id *oid, struct strbuf *base,
 	cgit_log_link("log", NULL, "button", ctx.qry.head,
 		      walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL,
 		      ctx.qry.showmsg, 0);
-	if (ctx.repo->max_stats)
+	if (ctx.repo->max_stats) {
+		html(" ");
 		cgit_stats_link("stats", NULL, "button", ctx.qry.head,
 				fullpath.buf);
-	if (!S_ISGITLINK(mode))
+	}
+	if (!S_ISGITLINK(mode)) {
+		html(" ");
 		cgit_plain_link("plain", NULL, "button", ctx.qry.head,
 				walk_tree_ctx->curr_rev, fullpath.buf);
-	if (!S_ISDIR(mode) && ctx.repo->enable_blame)
+	}
+	if (!S_ISDIR(mode) && ctx.repo->enable_blame) {
+		html(" ");
 		cgit_blame_link("blame", NULL, "button", ctx.qry.head,
 				walk_tree_ctx->curr_rev, fullpath.buf);
+	}
 	html("</td></tr>\n");
 
 cleanup:
@@ -372,13 +378,13 @@ void cgit_print_tree(const char *rev, char *path)
 	if (!rev)
 		rev = ctx.qry.head;
 
-	if (get_oid(rev, &oid)) {
+	if (repo_get_oid(the_repository, rev, &oid)) {
 		cgit_print_error_page(404, "Not found",
 			"Invalid revision name: %s", rev);
 		return;
 	}
 	commit = lookup_commit_reference(the_repository, &oid);
-	if (!commit || parse_commit(commit)) {
+	if (!commit || repo_parse_commit(the_repository, commit)) {
 		cgit_print_error_page(404, "Not found",
 			"Invalid commit reference: %s", rev);
 		return;
diff --git a/third_party/clj2nix/OWNERS b/third_party/clj2nix/OWNERS
index 79a422fcde..b381c4e660 100644
--- a/third_party/clj2nix/OWNERS
+++ b/third_party/clj2nix/OWNERS
@@ -1,3 +1 @@
-inherit: true
-owners:
- - grfn
+aspen
diff --git a/third_party/clj2nix/default.nix b/third_party/clj2nix/default.nix
index 3bd21df756..f582debf29 100644
--- a/third_party/clj2nix/default.nix
+++ b/third_party/clj2nix/default.nix
@@ -5,4 +5,5 @@ pkgs.callPackage "${(pkgs.fetchFromGitHub {
   repo = "clj2nix";
   rev = "3d0a38c954c8e0926f57de1d80d357df05fc2f94";
   sha256 = "0y77b988qdgsrp4w72v1f5rrh33awbps2qdgp2wr2nmmi44541w5";
-})}/clj2nix.nix" {}
+})}/clj2nix.nix"
+{ }
diff --git a/third_party/ddclient/default.nix b/third_party/ddclient/default.nix
new file mode 100644
index 0000000000..28b036ea66
--- /dev/null
+++ b/third_party/ddclient/default.nix
@@ -0,0 +1,12 @@
+# Users of this package & module should replace it with something like
+# inadyn, after https://github.com/NixOS/nixpkgs/issues/242330 is
+# landed.
+#
+# TODO(aspen): replace ddclient with inadyn or something else.
+{ pkgs, ... }:
+
+(pkgs.callPackage ./pkg.nix { }).overrideAttrs (old: {
+  passthru = old.passthru // {
+    module = ./module.nix;
+  };
+})
diff --git a/third_party/ddclient/module.nix b/third_party/ddclient/module.nix
new file mode 100644
index 0000000000..c8d68f9be9
--- /dev/null
+++ b/third_party/ddclient/module.nix
@@ -0,0 +1,230 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: Copyright (c) 2003-2023 The Nixpkgs/NixOS contributors
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.deprecated-ddclient;
+  boolToStr = bool: if bool then "yes" else "no";
+  dataDir = "/var/lib/ddclient";
+  StateDirectory = builtins.baseNameOf dataDir;
+  RuntimeDirectory = StateDirectory;
+
+  configFile' = pkgs.writeText "ddclient.conf" ''
+    # This file can be used as a template for configFile or is automatically generated by Nix options.
+    cache=${dataDir}/ddclient.cache
+    foreground=YES
+    use=${cfg.use}
+    login=${cfg.username}
+    password=${if cfg.protocol == "nsupdate" then "/run/${RuntimeDirectory}/ddclient.key" else "@password_placeholder@"}
+    protocol=${cfg.protocol}
+    ${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
+    ${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
+    ${lib.optionalString (cfg.zone != "")   "zone=${cfg.zone}"}
+    ssl=${boolToStr cfg.ssl}
+    wildcard=YES
+    quiet=${boolToStr cfg.quiet}
+    verbose=${boolToStr cfg.verbose}
+    ${cfg.extraConfig}
+    ${lib.concatStringsSep "," cfg.domains}
+  '';
+  configFile = if (cfg.configFile != null) then cfg.configFile else configFile';
+
+  preStart = ''
+    install --mode=600 --owner=$USER ${configFile} /run/${RuntimeDirectory}/ddclient.conf
+    ${lib.optionalString (cfg.configFile == null) (if (cfg.protocol == "nsupdate") then ''
+      install --mode=600 --owner=$USER ${cfg.passwordFile} /run/${RuntimeDirectory}/ddclient.key
+    '' else if (cfg.passwordFile != null) then ''
+      "${pkgs.replace-secret}/bin/replace-secret" "@password_placeholder@" "${cfg.passwordFile}" "/run/${RuntimeDirectory}/ddclient.conf"
+    '' else ''
+      sed -i '/^password=@password_placeholder@$/d' /run/${RuntimeDirectory}/ddclient.conf
+    '')}
+  '';
+
+in
+
+with lib;
+
+{
+  ###### interface
+
+  options = {
+
+    services.deprecated-ddclient = with lib.types; {
+
+      enable = mkOption {
+        default = false;
+        type = bool;
+        description = lib.mdDoc ''
+          Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org).
+        '';
+      };
+
+      package = mkOption {
+        type = package;
+        default = pkgs.ddclient;
+        defaultText = lib.literalExpression "pkgs.ddclient";
+        description = lib.mdDoc ''
+          The ddclient executable package run by the service.
+        '';
+      };
+
+      domains = mkOption {
+        default = [ "" ];
+        type = listOf str;
+        description = lib.mdDoc ''
+          Domain name(s) to synchronize.
+        '';
+      };
+
+      username = mkOption {
+        # For `nsupdate` username contains the path to the nsupdate executable
+        default = lib.optionalString (cfg.protocol == "nsupdate") "${pkgs.bind.dnsutils}/bin/nsupdate";
+        defaultText = "";
+        type = str;
+        description = lib.mdDoc ''
+          User name.
+        '';
+      };
+
+      passwordFile = mkOption {
+        default = null;
+        type = nullOr str;
+        description = lib.mdDoc ''
+          A file containing the password or a TSIG key in named format when using the nsupdate protocol.
+        '';
+      };
+
+      interval = mkOption {
+        default = "10min";
+        type = str;
+        description = lib.mdDoc ''
+          The interval at which to run the check and update.
+          See {command}`man 7 systemd.time` for the format.
+        '';
+      };
+
+      configFile = mkOption {
+        default = null;
+        type = nullOr path;
+        description = lib.mdDoc ''
+          Path to configuration file.
+          When set this overrides the generated configuration from module options.
+        '';
+        example = "/root/nixos/secrets/ddclient.conf";
+      };
+
+      protocol = mkOption {
+        default = "dyndns2";
+        type = str;
+        description = lib.mdDoc ''
+          Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
+        '';
+      };
+
+      server = mkOption {
+        default = "";
+        type = str;
+        description = lib.mdDoc ''
+          Server address.
+        '';
+      };
+
+      ssl = mkOption {
+        default = true;
+        type = bool;
+        description = lib.mdDoc ''
+          Whether to use SSL/TLS to connect to dynamic DNS provider.
+        '';
+      };
+
+      quiet = mkOption {
+        default = false;
+        type = bool;
+        description = lib.mdDoc ''
+          Print no messages for unnecessary updates.
+        '';
+      };
+
+      script = mkOption {
+        default = "";
+        type = str;
+        description = lib.mdDoc ''
+          script as required by some providers.
+        '';
+      };
+
+      use = mkOption {
+        default = "web, web=checkip.dyndns.com/, web-skip='Current IP Address: '";
+        type = str;
+        description = lib.mdDoc ''
+          Method to determine the IP address to send to the dynamic DNS provider.
+        '';
+      };
+
+      verbose = mkOption {
+        default = false;
+        type = bool;
+        description = lib.mdDoc ''
+          Print verbose information.
+        '';
+      };
+
+      zone = mkOption {
+        default = "";
+        type = str;
+        description = lib.mdDoc ''
+          zone as required by some providers.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = lines;
+        description = lib.mdDoc ''
+          Extra configuration. Contents will be added verbatim to the configuration file.
+          ::: {.note}
+          `daemon` should not be added here because it does not work great with the systemd-timer approach the service uses.
+          :::
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkMerge [
+    (mkIf cfg.enable {
+      systemd.services.ddclient = {
+        description = "Dynamic DNS Client";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        restartTriggers = optional (cfg.configFile != null) cfg.configFile;
+        path = lib.optional (lib.hasPrefix "if," cfg.use) pkgs.iproute2;
+
+        serviceConfig = {
+          DynamicUser = true;
+          RuntimeDirectoryMode = "0700";
+          inherit RuntimeDirectory;
+          inherit StateDirectory;
+          Type = "oneshot";
+          ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}";
+          ExecStart = "${lib.getBin cfg.package}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
+        };
+      };
+
+      systemd.timers.ddclient = {
+        description = "Run ddclient";
+        wantedBy = [ "timers.target" ];
+        timerConfig = {
+          OnBootSec = cfg.interval;
+          OnUnitInactiveSec = cfg.interval;
+        };
+      };
+    })
+    {
+      ids.uids.ddclient = 30;
+      ids.gids.ddclient = 30;
+    }
+  ];
+}
diff --git a/third_party/ddclient/pkg.nix b/third_party/ddclient/pkg.nix
new file mode 100644
index 0000000000..586f3891ac
--- /dev/null
+++ b/third_party/ddclient/pkg.nix
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: Copyright (c) 2003-2023 The Nixpkgs/NixOS contributors
+{ lib, fetchFromGitHub, perlPackages, autoreconfHook, iproute2, perl }:
+
+perlPackages.buildPerlPackage rec {
+  pname = "ddclient";
+  version = "3.10.0";
+
+  outputs = [ "out" ];
+
+  src = fetchFromGitHub {
+    owner = "ddclient";
+    repo = "ddclient";
+    rev = "v${version}";
+    sha256 = "sha256-wWUkjXwVNZRJR1rXPn3IkDRi9is9vsRuNC/zq8RpB1E=";
+  };
+
+  postPatch = ''
+    touch Makefile.PL
+  '';
+
+  nativeBuildInputs = [ autoreconfHook ];
+
+  buildInputs = with perlPackages; [ IOSocketINET6 IOSocketSSL JSONPP ];
+
+  installPhase = ''
+    runHook preInstall
+    # patch sheebang ddclient script which only exists after buildPhase
+    preConfigure
+    install -Dm755 ddclient $out/bin/ddclient
+    install -Dm644 -t $out/share/doc/ddclient COP* README.* ChangeLog.md
+    runHook postInstall
+  '';
+
+  # TODO: run upstream tests
+  doCheck = false;
+
+  meta = with lib; {
+    description = "Client for updating dynamic DNS service entries";
+    homepage = "https://ddclient.net/";
+    license = licenses.gpl2Plus;
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ SuperSandro2000 ];
+  };
+}
diff --git a/third_party/default.nix b/third_party/default.nix
index e9e3c117ce..874aecd3e7 100644
--- a/third_party/default.nix
+++ b/third_party/default.nix
@@ -12,7 +12,7 @@
 #    other folders below //third_party, other than the ones mentioned
 #    above.
 
-{ pkgs, depot, ... }:
+{ pkgs, depot, localSystem, ... }:
 
 {
   # Expose a partially applied NixOS, expecting an attribute set with
@@ -24,32 +24,33 @@
   # be able to pass `specialArgs`. We depend on this because `depot`
   # needs to be partially evaluated in NixOS configuration before
   # module imports are resolved.
-  nixos = {
-    configuration,
-    specialArgs ? {},
-    system ? builtins.currentSystem,
-    ...
-  }:
-  let
-    eval = import "${pkgs.path}/nixos/lib/eval-config.nix" {
-      inherit specialArgs system;
-      modules = [
-        configuration
-        (import "${depot.path + "/ops/modules/default-imports.nix"}")
-      ];
-    };
+  nixos =
+    { configuration
+    , specialArgs ? { }
+    , system ? localSystem
+    , ...
+    }:
+    let
+      eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
+        inherit specialArgs system;
+        modules = [
+          configuration
+          (import (depot.path.origSrc + "/ops/modules/default-imports.nix"))
+        ];
+      };
 
-    # This is for `nixos-rebuild build-vm'.
-    vmConfig = (import "${pkgs.path}/nixos/lib/eval-config.nix" {
-      inherit specialArgs system;
-      modules = [
-        configuration
-        "${pkgs.path}/nixos/modules/virtualisation/qemu-vm.nix"
-      ];
-    }).config;
-  in {
-    inherit (eval) pkgs config options;
-    system = eval.config.system.build.toplevel;
-    vm = vmConfig.system.build.vm;
-  };
+      # This is for `nixos-rebuild build-vm'.
+      vmConfig = (import (pkgs.path + "/nixos/lib/eval-config.nix") {
+        inherit specialArgs system;
+        modules = [
+          configuration
+          (pkgs.path + "/nixos/modules/virtualisation/qemu-vm.nix")
+        ];
+      }).config;
+    in
+    {
+      inherit (eval) pkgs config options;
+      system = eval.config.system.build.toplevel;
+      vm = vmConfig.system.build.vm;
+    };
 }
diff --git a/third_party/dfmt/default.nix b/third_party/dfmt/default.nix
deleted file mode 100644
index 5ce0d7541c..0000000000
--- a/third_party/dfmt/default.nix
+++ /dev/null
@@ -1,36 +0,0 @@
-# dfmt is a code formatter for D
-{ pkgs, lib, ... }:
-
-pkgs.stdenv.mkDerivation rec {
-  pname = "dfmt";
-  version = "0.13.4";
-
-  src = pkgs.fetchFromGitHub {
-    owner = "dlang-community";
-    repo = "dfmt";
-    rev = "v${version}";
-    sha256 = "02a8qvrmnl1c15y6irxlhqpr0hjj5s8qk0jc20ivj0fl6p4v9shj";
-    fetchSubmodules = true;
-  };
-
-  nativeBuildInputs = with pkgs; [
-    dmd
-
-    # fake git that will be used to fetch the version string
-    (pkgs.writeShellScriptBin "git" "echo 'v${version}'")
-  ];
-
-  DFLAGS = "-release";
-
-  installPhase = ''
-    mkdir -p $out/bin
-    cp bin/dfmt $out/bin/
-    strip -s $out/bin/dfmt
-  '';
-
-  meta = {
-    description = "D code formatter";
-    homepage = "https://github.com/dlang-community/dfmt";
-    license = lib.licenses.boost;
-  };
-}
diff --git a/third_party/dhall/OWNERS b/third_party/dhall/OWNERS
deleted file mode 100644
index a742d0d22b..0000000000
--- a/third_party/dhall/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherited: true
-owners:
-  - Profpatsch
diff --git a/third_party/dhall/default.nix b/third_party/dhall/default.nix
deleted file mode 100644
index 6f3782233f..0000000000
--- a/third_party/dhall/default.nix
+++ /dev/null
@@ -1,22 +0,0 @@
-{ pkgs, ... }:
-
-let
-  # binary releases of dhall tools, since the build in nixpkgs is
-  # broken most of the time. The binaries are also fully static
-  # builds, instead of the half-static crap that nixpkgs produces.
-  easy-dhall-nix =
-    import (builtins.fetchTarball {
-      url = "https://github.com/justinwoo/easy-dhall-nix/archive/eae7f64c4d6c70681e5a56c84198236930ba425e.tar.gz";
-      sha256 = "1y2x15v8a679vlpxazjpibfwajp6zph60f8wjcm4xflbvazk0dx7";
-    }) { inherit pkgs; };
-in {
-  dhall = easy-dhall-nix.dhall-simple;
-  dhall-bash = easy-dhall-nix.dhall-bash-simple;
-  dhall-docs = easy-dhall-nix.dhall-docs-simple;
-  dhall-json = easy-dhall-nix.dhall-json-simple;
-  dhall-lsp-server = easy-dhall-nix.dhall-lsp-simple;
-  dhall-nix = easy-dhall-nix.dhall-nix-simple;
-  # not yet in dhall-simple
-  # dhall-nixpkgs = easy-dhall-nix.dhall-nixpkgs-simple;
-  dhall-yaml = easy-dhall-nix.dhall-yaml-simple;
-}
diff --git a/third_party/elmPackages_0_18/default.nix b/third_party/elmPackages_0_18/default.nix
index 55d66f0629..0481d0940a 100644
--- a/third_party/elmPackages_0_18/default.nix
+++ b/third_party/elmPackages_0_18/default.nix
@@ -4,12 +4,18 @@
 # Elm 0.19 changed the language & package ecosystem completely,
 # essentially requiring a partial rewrite of all Elm apps. However,
 # //fun/gemma uses Elm 0.18 and I don't have time to rewrite it.
+#
+# Since the ABIs of current glibc and the pinned version have diverged
+# too much, we need to build //fun/gemma completely based on a historical
+# nixpkgs version.
 
 { pkgs, ... }:
 
-(import (pkgs.fetchFromGitHub {
-  owner = "NixOS";
-  repo = "nixpkgs";
-  rev = "14f9ee66e63077539252f8b4550049381a082518";
-  sha256 = "1wn7nmb1cqfk2j91l3rwc6yhimfkzxprb8wknw5wi57yhq9m6lv1";
-}) {}).elmPackages
+(import
+  (pkgs.fetchFromGitHub {
+    owner = "NixOS";
+    repo = "nixpkgs";
+    rev = "14f9ee66e63077539252f8b4550049381a082518";
+    sha256 = "1wn7nmb1cqfk2j91l3rwc6yhimfkzxprb8wknw5wi57yhq9m6lv1";
+  })
+{ })
diff --git a/third_party/exwm/.elpaignore b/third_party/exwm/.elpaignore
index b43bf86b50..f0f644ee08 100644
--- a/third_party/exwm/.elpaignore
+++ b/third_party/exwm/.elpaignore
@@ -1 +1,2 @@
+LICENSE
 README.md
diff --git a/third_party/exwm/LICENSE b/third_party/exwm/LICENSE
new file mode 100644
index 0000000000..9cecc1d466
--- /dev/null
+++ b/third_party/exwm/LICENSE
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {one line to give the program's name and a brief idea of what it does.}
+    Copyright (C) {year}  {name of author}
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    {project}  Copyright (C) {year}  {fullname}
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/third_party/exwm/default.nix b/third_party/exwm/default.nix
new file mode 100644
index 0000000000..6daee882fe
--- /dev/null
+++ b/third_party/exwm/default.nix
@@ -0,0 +1,14 @@
+{ depot, pkgs, lib, ... }:
+
+# special dance for overriding this into the fixed-point of emacs
+# packages, but having it be separately buildable.
+
+pkgs.emacsPackages.callPackage
+  ({ trivialBuild, xelb }: trivialBuild {
+    pname = "depot-exwm";
+    version = "canon";
+    src = ./.;
+
+    packageRequires = [ xelb ];
+  })
+{ }
diff --git a/third_party/exwm/exwm-background.el b/third_party/exwm/exwm-background.el
new file mode 100644
index 0000000000..fa663d8fe6
--- /dev/null
+++ b/third_party/exwm/exwm-background.el
@@ -0,0 +1,199 @@
+;;; exwm-background.el --- X Background Module for EXWM  -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022-2024 Free Software Foundation, Inc.
+
+;; Author: Steven Allen <steven@stebalien.com>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This module adds X background color setting support to EXWM.
+
+;; To use this module, load and enable it as follows:
+;;   (require 'exwm-background)
+;;   (exwm-background-enable)
+;;
+;; By default, this will apply the theme's background color.  However, that
+;; color can be customized via the `exwm-background-color' setting.
+
+;;; Code:
+
+(require 'exwm-core)
+
+(defcustom exwm-background-color nil
+  "Background color for Xorg."
+  :type '(choice
+          (color :tag "Background Color")
+          (const :tag "Default" nil))
+  :group 'exwm
+  :initialize #'custom-initialize-default
+  :set (lambda (symbol value)
+         (set-default-toplevel-value symbol value)
+         (exwm-background--update)))
+
+(defconst exwm-background--properties '("_XROOTPMAP_ID" "_XSETROOT_ID" "ESETROOT_PMAP_ID")
+  "The background properties to set.
+We can't need to set these so that compositing window managers
+can correctly display the background color.")
+
+(defvar exwm-background--connection nil
+  "The X connection used for setting the background.
+We use a separate connection as other background-setting tools
+may kill this connection when they replace it.")
+
+(defvar exwm-background--pixmap nil
+  "Cached background pixmap.")
+
+(defvar exwm-background--atoms nil
+  "Cached background atoms.")
+
+(defun exwm-background--update (&rest _)
+  "Update the EXWM background."
+
+  ;; Always reconnect as any tool that sets the background may have disconnected us (to force X to
+  ;; free resources).
+  (exwm-background--connect)
+
+  (let ((gc (xcb:generate-id exwm-background--connection))
+        (color (exwm--color->pixel (or exwm-background-color
+                                       (face-background 'default)))))
+    ;; Fill the pixmap.
+    (xcb:+request exwm-background--connection
+        (make-instance 'xcb:CreateGC
+                       :cid gc :drawable exwm-background--pixmap
+                       :value-mask (logior xcb:GC:Foreground
+                                           xcb:GC:GraphicsExposures)
+                       :foreground color
+                       :graphics-exposures 0))
+
+    (xcb:+request exwm-background--connection
+        (make-instance 'xcb:PolyFillRectangle
+                       :gc gc :drawable exwm-background--pixmap
+                       :rectangles
+                       (list
+                        (make-instance
+                         'xcb:RECTANGLE
+                         :x 0 :y 0 :width 1 :height 1))))
+    (xcb:+request exwm-background--connection (make-instance 'xcb:FreeGC :gc gc)))
+
+  ;; Reapply it to force an update (also clobber anyone else who may have set it).
+  (xcb:+request exwm-background--connection
+      (make-instance 'xcb:ChangeWindowAttributes
+                     :window exwm--root
+                     :value-mask xcb:CW:BackPixmap
+                     :background-pixmap exwm-background--pixmap))
+
+  (let (old)
+    ;; Collect old pixmaps so we can kill other background clients (all the background setting tools
+    ;; seem to do this).
+    (dolist (atom exwm-background--atoms)
+      (when-let* ((reply (xcb:+request-unchecked+reply exwm-background--connection
+                             (make-instance 'xcb:GetProperty
+                                            :delete 0
+                                            :window exwm--root
+                                            :property atom
+                                            :type xcb:Atom:PIXMAP
+                                            :long-offset 0
+                                            :long-length 1)))
+                  (value (vconcat (slot-value reply 'value)))
+                  ((length= value 4))
+                  (pixmap (funcall (if xcb:lsb #'xcb:-unpack-u4-lsb #'xcb:-unpack-u4)
+                                   value 0))
+                  ((not (or (= pixmap exwm-background--pixmap)
+                            (member pixmap old)))))
+        (push pixmap old)))
+
+    ;; Change the background.
+    (dolist (atom exwm-background--atoms)
+      (xcb:+request exwm-background--connection
+          (make-instance 'xcb:ChangeProperty
+                         :window exwm--root
+                         :property atom
+                         :type xcb:Atom:PIXMAP
+                         :format 32
+                         :mode xcb:PropMode:Replace
+                         :data-len 1
+                         :data
+                         (funcall (if xcb:lsb
+                                      #'xcb:-pack-u4-lsb
+                                    #'xcb:-pack-u4)
+                                  exwm-background--pixmap))))
+
+    ;; Kill the old background clients.
+    (dolist (pixmap old)
+      (xcb:+request exwm-background--connection
+          (make-instance 'xcb:KillClient :resource pixmap))))
+
+  (xcb:flush exwm-background--connection))
+
+(defun exwm-background--connected-p ()
+  (and exwm-background--connection
+       (process-live-p (slot-value exwm-background--connection 'process))))
+
+(defun exwm-background--connect ()
+  (unless (exwm-background--connected-p)
+    (setq exwm-background--connection (xcb:connect))
+    ;;prevent query message on exit
+    (set-process-query-on-exit-flag (slot-value exwm-background--connection 'process) nil)
+
+    ;; Intern the background property atoms.
+    (setq exwm-background--atoms
+          (mapcar
+           (lambda (prop) (exwm--intern-atom prop exwm-background--connection))
+           exwm-background--properties))
+
+    ;; Create the pixmap.
+    (setq exwm-background--pixmap (xcb:generate-id exwm-background--connection))
+    (xcb:+request exwm-background--connection
+        (make-instance 'xcb:CreatePixmap
+                       :depth
+                       (slot-value
+                        (xcb:+request-unchecked+reply exwm-background--connection
+                            (make-instance 'xcb:GetGeometry :drawable exwm--root))
+                        'depth)
+                       :pid exwm-background--pixmap
+                       :drawable exwm--root
+                       :width 1 :height 1))))
+
+(defun exwm-background--init ()
+  "Initialize background module."
+  (exwm--log)
+  (add-hook 'enable-theme-functions 'exwm-background--update)
+  (add-hook 'disable-theme-functions 'exwm-background--update)
+  (exwm-background--update))
+
+(defun exwm-background--exit ()
+  "Uninitialize the background module."
+  (exwm--log)
+  (remove-hook 'enable-theme-functions 'exwm-background--update)
+  (remove-hook 'disable-theme-functions 'exwm-background--update)
+  (when (and exwm-background--connection
+             (slot-value exwm-background--connection 'connected))
+    (xcb:disconnect exwm-background--connection))
+  (setq exwm-background--pixmap nil
+        exwm-background--connection nil
+        exwm-background--atoms nil))
+
+(defun exwm-background-enable ()
+  "Enable background support for EXWM."
+  (exwm--log)
+  (add-hook 'exwm-init-hook #'exwm-background--init)
+  (add-hook 'exwm-exit-hook #'exwm-background--exit))
+
+(provide 'exwm-background)
+
+;;; exwm-background.el ends here
diff --git a/third_party/exwm/exwm-cm.el b/third_party/exwm/exwm-cm.el
deleted file mode 100644
index 8a45010030..0000000000
--- a/third_party/exwm/exwm-cm.el
+++ /dev/null
@@ -1,50 +0,0 @@
-;;; exwm-cm.el --- Compositing Manager for EXWM  -*- lexical-binding: t -*-
-
-;; Copyright (C) 2016-2021 Free Software Foundation, Inc.
-
-;; Author: Chris Feng <chris.w.feng@gmail.com>
-
-;; This file is part of GNU Emacs.
-
-;; GNU Emacs is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; GNU Emacs is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module is obsolete since EXWM now supports third-party compositors.
-
-;;; Code:
-
-(make-obsolete-variable 'exwm-cm-opacity
-                        "This variable should no longer be used." "26")
-
-(defun exwm-cm-set-opacity (&rest _args)
-  (declare (obsolete nil "26")))
-
-(defun exwm-cm-enable ()
-  (declare (obsolete nil "26")))
-
-(defun exwm-cm-start ()
-  (declare (obsolete nil "26")))
-
-(defun exwm-cm-stop ()
-  (declare (obsolete nil "26")))
-
-(defun exwm-cm-toggle ()
-  (declare (obsolete nil "26")))
-
-
-
-(provide 'exwm-cm)
-
-;;; exwm-cm.el ends here
diff --git a/third_party/exwm/exwm-config.el b/third_party/exwm/exwm-config.el
index 9609f4cf3c..a9f21e9c8c 100644
--- a/third_party/exwm/exwm-config.el
+++ b/third_party/exwm/exwm-config.el
@@ -1,6 +1,6 @@
 ;;; exwm-config.el --- Predefined configurations  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
diff --git a/third_party/exwm/exwm-core.el b/third_party/exwm/exwm-core.el
index 5356ef9b97..e0d644d941 100644
--- a/third_party/exwm/exwm-core.el
+++ b/third_party/exwm/exwm-core.el
@@ -1,6 +1,6 @@
 ;;; exwm-core.el --- Core definitions  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -33,6 +33,10 @@
 (require 'xcb-ewmh)
 (require 'xcb-debug)
 
+(defgroup exwm-debug nil
+  "Debugging."
+  :group 'exwm)
+
 (defcustom exwm-debug-log-time-function #'exwm-debug-log-uptime
   "Function used for generating timestamps in `exwm-debug' logs.
 
@@ -40,7 +44,6 @@ Here are some predefined candidates:
 `exwm-debug-log-uptime': Display the uptime of this Emacs instance.
 `exwm-debug-log-time': Display time of day.
 `nil': Disable timestamp."
-  :group 'exwm-debug
   :type `(choice (const :tag "Emacs uptime" ,#'exwm-debug-log-uptime)
                  (const :tag "Time of day" ,#'exwm-debug-log-time)
                  (const :tag "Off" nil)
@@ -59,6 +62,9 @@ Here are some predefined candidates:
 
 (defvar exwm--connection nil "X connection.")
 
+(defvar exwm--terminal nil
+  "Terminal corresponding to `exwm--connection'.")
+
 (defvar exwm--wmsn-window nil
   "An X window owning the WM_S0 selection.")
 
@@ -90,10 +96,12 @@ Here are some predefined candidates:
                   (frame-or-index &optional id))
 
 (define-minor-mode exwm-debug
-  "Debug-logging enabled if non-nil"
-  :global t)
+  "Debug-logging enabled if non-nil."
+  :global t
+  :group 'exwm-debug)
 
 (defmacro exwm--debug (&rest forms)
+  "Evaluate FORMS if mode `exwm-debug' is active."
   (when exwm-debug `(progn ,@forms)))
 
 (defmacro exwm--log (&optional format-string &rest objects)
@@ -113,10 +121,12 @@ FORMAT-STRING is a string specifying the message to output, as in
 
 (defsubst exwm--id->buffer (id)
   "X window ID => Emacs buffer."
+  (declare (indent defun))
   (cdr (assoc id exwm--id-buffer-alist)))
 
 (defsubst exwm--buffer->id (buffer)
   "Emacs buffer BUFFER => X window ID."
+  (declare (indent defun))
   (car (rassoc buffer exwm--id-buffer-alist)))
 
 (defun exwm--lock (&rest _args)
@@ -155,9 +165,11 @@ Nil can be passed as placeholder."
                                          (if height xcb:ConfigWindow:Height 0))
                      :x x :y y :width width :height height)))
 
-(defun exwm--intern-atom (atom)
-  "Intern X11 ATOM."
-  (slot-value (xcb:+request-unchecked+reply exwm--connection
+(defun exwm--intern-atom (atom &optional conn)
+  "Intern X11 ATOM.
+If CONN is non-nil, use it instead of the value of the variable
+`exwm--connection'."
+  (slot-value (xcb:+request-unchecked+reply (or conn exwm--connection)
                   (make-instance 'xcb:InternAtom
                                  :only-if-exists 0
                                  :name-len (length atom)
@@ -177,6 +189,12 @@ least SECS seconds later."
                         ,function
                         ,@args))
 
+(defsubst exwm--terminal-p (&optional frame)
+  "Return t when FRAME's terminal is EXWM's terminal.
+If FRAME is null, use selected frame."
+  (declare (indent defun))
+  (eq exwm--terminal (frame-terminal frame)))
+
 (defun exwm--get-client-event-mask ()
   "Return event mask set on all managed windows."
   (logior xcb:EventMask:StructureNotify
@@ -188,10 +206,28 @@ least SECS seconds later."
   "Convert COLOR to PIXEL (index in TrueColor colormap)."
   (when (and color
              (eq (x-display-visual-class) 'true-color))
-    (let ((rgb (x-color-values color)))
-      (logior (lsh (lsh (pop rgb) -8) 16)
-              (lsh (lsh (pop rgb) -8) 8)
-              (lsh (pop rgb) -8)))))
+    (let ((rgb (color-values color)))
+      (logior (ash (ash (pop rgb) -8) 16)
+              (ash (ash (pop rgb) -8) 8)
+              (ash (pop rgb) -8)))))
+
+(defun exwm--get-visual-depth-colormap (conn id)
+  "Get visual, depth and colormap from X window ID.
+Return a three element list with the respective results.
+
+If CONN is non-nil, use it instead of the value of the variable
+`exwm--connection'."
+  (let (ret-visual ret-depth ret-colormap)
+    (with-slots (visual colormap)
+        (xcb:+request-unchecked+reply conn
+            (make-instance 'xcb:GetWindowAttributes :window id))
+      (setq ret-visual visual)
+      (setq ret-colormap colormap))
+    (with-slots (depth)
+        (xcb:+request-unchecked+reply conn
+            (make-instance 'xcb:GetGeometry :drawable id))
+      (setq ret-depth depth))
+    (list ret-visual ret-depth ret-colormap)))
 
 ;; Internal variables
 (defvar-local exwm--id nil)               ;window ID
@@ -212,7 +248,7 @@ One of `line-mode' or `char-mode'.")
 (defvar-local exwm--geometry nil)
 (defvar-local exwm-class-name nil "Class name in WM_CLASS.")
 (defvar-local exwm-instance-name nil "Instance name in WM_CLASS.")
-(defvar-local exwm-title nil "Window title (either _NET_WM_NAME or WM_NAME)")
+(defvar-local exwm-title nil "Window title (either _NET_WM_NAME or WM_NAME).")
 (defvar-local exwm--title-is-utf8 nil)
 (defvar-local exwm-transient-for nil "WM_TRANSIENT_FOR.")
 (defvar-local exwm--protocols nil)
diff --git a/third_party/exwm/exwm-floating.el b/third_party/exwm/exwm-floating.el
index a9f9315b71..34d06a30db 100644
--- a/third_party/exwm/exwm-floating.el
+++ b/third_party/exwm/exwm-floating.el
@@ -1,6 +1,6 @@
 ;;; exwm-floating.el --- Floating Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -31,17 +31,16 @@
 
 (defgroup exwm-floating nil
   "Floating."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-floating-setup-hook nil
-  "Normal hook run when an X window has been made floating, in the
-context of the corresponding buffer."
+  "Normal hook run when an X window has been made floating.
+This hook runs in the context of the corresponding buffer."
   :type 'hook)
 
 (defcustom exwm-floating-exit-hook nil
-  "Normal hook run when an X window has exited floating state, in the
-context of the corresponding buffer."
+  "Normal hook run when an X window has exited floating state.
+This hook runs in the context of the corresponding buffer."
   :type 'hook)
 
 (defcustom exwm-floating-border-color "navy"
@@ -118,13 +117,13 @@ context of the corresponding buffer."
 (defvar exwm-workspace--current)
 (defvar exwm-workspace--frame-y-offset)
 (defvar exwm-workspace--window-y-offset)
-(defvar exwm-workspace--workareas)
 (declare-function exwm-layout--hide "exwm-layout.el" (id))
 (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
 (declare-function exwm-layout--refresh "exwm-layout.el" ())
 (declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
 (declare-function exwm-workspace--position "exwm-workspace.el" (frame))
 (declare-function exwm-workspace--update-offsets "exwm-workspace.el" ())
+(declare-function exwm-workspace--workarea "exwm-workspace.el" (frame))
 
 (defun exwm-floating--set-allowed-actions (id tilling)
   "Set _NET_WM_ALLOWED_ACTIONS."
@@ -161,6 +160,8 @@ context of the corresponding buffer."
                           (get-buffer "*scratch*")))
                   (make-frame
                    `((minibuffer . ,(minibuffer-window exwm--frame))
+                     (tab-bar-lines . 0)
+                     (tab-bar-lines-keep-state . t)
                      (left . ,(* window-min-width -10000))
                      (top . ,(* window-min-height -10000))
                      (width . ,window-min-width)
@@ -184,12 +185,8 @@ context of the corresponding buffer."
     (set-frame-parameter frame 'exwm-container frame-container)
     ;; Fix illegal parameters
     ;; FIXME: check normal hints restrictions
-    (let* ((workarea (elt exwm-workspace--workareas
-                          (exwm-workspace--position original-frame)))
-           (x* (aref workarea 0))
-           (y* (aref workarea 1))
-           (width* (aref workarea 2))
-           (height* (aref workarea 3)))
+    (with-slots ((x* x) (y* y) (width* width) (height* height))
+        (exwm-workspace--workarea original-frame)
       ;; Center floating windows
       (when (and (or (= x 0) (= x x*))
                  (or (= y 0) (= y y*)))
diff --git a/third_party/exwm/exwm-input.el b/third_party/exwm/exwm-input.el
index 50676217f1..f1f035c91a 100644
--- a/third_party/exwm/exwm-input.el
+++ b/third_party/exwm/exwm-input.el
@@ -1,6 +1,6 @@
 ;;; exwm-input.el --- Input Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -40,14 +40,13 @@
 
 (defgroup exwm-input nil
   "Input."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-input-prefix-keys
   '(?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:)
-  "List of prefix keys EXWM should forward to Emacs when in line-mode.
+  "List of prefix keys EXWM should forward to Emacs when in `line-mode'.
 
-The point is to make keys like 'C-x C-f' forwarded to Emacs in line-mode.
+The point is to make keys like 'C-x C-f' forwarded to Emacs in `line-mode'.
 There is no need to add prefix keys for global/simulation keys or those
 defined in `exwm-mode-map' here."
   :type '(repeat key-sequence)
@@ -87,7 +86,7 @@ defined in `exwm-mode-map' here."
                        value))))
 
 (defcustom exwm-input-line-mode-passthrough nil
-  "Non-nil makes 'line-mode' forward all events to Emacs."
+  "Non-nil makes `line-mode' forward all events to Emacs."
   :type 'boolean)
 
 ;; Input focus update requests should be accumulated for a short time
@@ -102,6 +101,13 @@ defined in `exwm-mode-map' here."
 (defconst exwm-input--update-focus-interval 0.01
   "Time interval (in seconds) for accumulating input focus update requests.")
 
+(defconst exwm-input--passthrough-functions '(read-char
+                                              read-char-exclusive
+                                              read-key-sequence-vector
+                                              read-key-sequence
+                                              read-event)
+  "Low-level read functions that must be exempted from EXWM input handling.")
+
 (defvar exwm-input--during-command nil
   "Indicate whether between `pre-command-hook' and `post-command-hook'.")
 
@@ -115,10 +121,13 @@ defined in `exwm-mode-map' here."
 (defvar exwm-input--local-simulation-keys nil
   "Whether simulation keys are local.")
 
-(defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.")
+(defvar exwm-input--simulation-keys nil "Simulation keys in `line-mode'.")
+
+(defvar exwm-input--skip-buffer-list-update nil
+  "Skip the upcoming `buffer-list-update'.")
 
 (defvar exwm-input--temp-line-mode nil
-  "Non-nil indicates it's in temporary line-mode for char-mode.")
+  "Non-nil indicates it's in temporary line-mode for `char-mode'.")
 
 (defvar exwm-input--timestamp-atom nil)
 
@@ -126,25 +135,15 @@ defined in `exwm-mode-map' here."
 
 (defvar exwm-input--timestamp-window nil)
 
-(defvar exwm-input--update-focus-defer-timer nil "Timer for polling the lock.")
+(defvar exwm-input--update-focus-timer nil
+  "Timer for deferring the update of input focus.")
 
 (defvar exwm-input--update-focus-lock nil
   "Lock for solving input focus update contention.")
 
-(defvar exwm-input--update-focus-timer nil
-  "Timer for deferring the update of input focus.")
-
 (defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused.
-It also helps us discern whether a `buffer-list-update-hook' was caused by a
-different window having been selected.
-
 This value should always be overwritten.")
 
-(defvar exwm-input--update-focus-window-buffer nil
-  "Buffer displayed in `exwm-input--update-focus-window'.
-Helps us discern whether a `buffer-list-update-hook' was caused by the selected
-window switching to a different buffer.")
-
 (defvar exwm-input--echo-area-timer nil "Timer for detecting echo area dirty.")
 
 (defvar exwm-input--event-hook nil
@@ -164,8 +163,6 @@ Current buffer will be the `exwm-mode' buffer when this hook runs.")
 (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
 (declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
 (declare-function exwm-reset "exwm.el" ())
-(declare-function exwm-workspace--client-p "exwm-workspace.el"
-                  (&optional frame))
 (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
 (declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace))
 (declare-function exwm-workspace-switch "exwm-workspace.el"
@@ -301,51 +298,39 @@ ARGS are additional arguments to CALLBACK."
 
 (defun exwm-input--on-buffer-list-update ()
   "Run in `buffer-list-update-hook' to track input focus."
-  ;; `buffer-list-update-hook' is invoked by several functions
-  ;; (`get-buffer-create', `select-window', `with-temp-buffer', etc.), but we
-  ;; just want to notice when a different window has been selected, or when the
-  ;; selected window displays a different buffer, so that we can set the focus
-  ;; to the associated X window (in case of an `exwm-mode' buffer).  In order to
-  ;; differentiate, we keep track of the last selected window and buffer in the
-  ;; `exwm-input--update-focus-window' and
-  ;; `exwm-input--update-focus-window-buffer' variables.
-  (let* ((win (selected-window))
-         (buf (window-buffer win)))
-    (when (and (not (exwm-workspace--client-p))
-               (not (and (eq exwm-input--update-focus-window win)
-                         (eq exwm-input--update-focus-window-buffer buf))))
-      (exwm--log "selected-window=%S current-buffer=%S" win buf)
-      (setq exwm-input--update-focus-window win)
-      (setq exwm-input--update-focus-window-buffer buf)
-      (redirect-frame-focus (selected-frame) nil)
-      (exwm-input--update-focus-defer))))
+  (when (and          ; this hook is called incesantly; place cheap tests on top
+         (not exwm-input--skip-buffer-list-update)
+         (exwm--terminal-p) ; skip other terminals, e.g. TTY client frames
+         (not (frame-parameter nil 'no-accept-focus)))
+    (exwm--log "current-buffer=%S selected-window=%S"
+               (current-buffer) (selected-window))
+    (redirect-frame-focus (selected-frame) nil)
+    (setq exwm-input--update-focus-window (selected-window))
+    (exwm-input--update-focus-defer)))
 
 (defun exwm-input--update-focus-defer ()
-  "Defer updating input focus."
-  (when exwm-input--update-focus-defer-timer
-    (cancel-timer exwm-input--update-focus-defer-timer))
+  "Schedule a deferred update to input focus.
+Instead of immediately focusing the current window, it defers the focus change
+until the selected window stops changing (debouncing input focus updates)."
+  (when exwm-input--update-focus-timer
+    (cancel-timer exwm-input--update-focus-timer))
+  (setq exwm-input--update-focus-timer
+        ;; Attempt to accumulate successive events close enough.
+        (run-with-timer exwm-input--update-focus-interval
+                        nil
+                        #'exwm-input--update-focus-commit)))
+
+(defun exwm-input--update-focus-commit ()
+  "Attempt to update the window focus.
+If we're currently updating the window focus, re-schedule a focus update
+attempt later."
   (if exwm-input--update-focus-lock
-      (setq exwm-input--update-focus-defer-timer
-            (exwm--defer 0 #'exwm-input--update-focus-defer))
-    (setq exwm-input--update-focus-defer-timer nil)
-    (when exwm-input--update-focus-timer
-      (cancel-timer exwm-input--update-focus-timer))
-    (setq exwm-input--update-focus-timer
-          ;; Attempt to accumulate successive events close enough.
-          (run-with-timer exwm-input--update-focus-interval
-                          nil
-                          #'exwm-input--update-focus-commit
-                          exwm-input--update-focus-window))))
-
-(defun exwm-input--update-focus-commit (window)
-  "Commit updating input focus."
-  (setq exwm-input--update-focus-lock t)
-  (unwind-protect
-      (exwm-input--update-focus window)
-    (setq exwm-input--update-focus-lock nil)))
+      (exwm-input--update-focus-defer)
+    (let ((exwm-input--update-focus-lock t))
+      (exwm-input--update-focus exwm-input--update-focus-window))))
 
 (defun exwm-input--update-focus (window)
-  "Update input focus."
+  "Update input focus to WINDOW."
   (when (window-live-p window)
     (exwm--log "focus-window=%s focus-buffer=%s" window (window-buffer window))
     (with-current-buffer (window-buffer window)
@@ -469,9 +454,12 @@ ARGS are additional arguments to CALLBACK."
             (t
              ;; Replay this event by default.
              (setq fake-last-command t)
-             (setq mode xcb:Allow:ReplayPointer))))
-    (when fake-last-command
-      (exwm-input--fake-last-command))
+             (setq mode xcb:Allow:ReplayPointer)))
+      (when fake-last-command
+        (if buffer
+            (with-current-buffer buffer
+              (exwm-input--fake-last-command))
+          (exwm-input--fake-last-command))))
     (xcb:+request exwm--connection
         (make-instance 'xcb:AllowEvents :mode mode :time xcb:Time:CurrentTime))
     (xcb:flush exwm--connection))
@@ -594,18 +582,10 @@ instead."
   (when (called-interactively-p 'any)
     (exwm-input--update-global-prefix-keys)))
 
-;; Putting (t . EVENT) into `unread-command-events' does not really work
-;; as documented for Emacs < 26.2.
-(eval-and-compile
-  (if (or (< emacs-major-version 26)
-          (and (= emacs-major-version 26)
-               (< emacs-minor-version 2)))
-      (defsubst exwm-input--unread-event (event)
-        (setq unread-command-events
-              (append unread-command-events (list event))))
-    (defsubst exwm-input--unread-event (event)
-      (setq unread-command-events
-            (append unread-command-events `((t . ,event)))))))
+(defsubst exwm-input--unread-event (event)
+  (declare (indent defun))
+  (setq unread-command-events
+        (append unread-command-events `((t . ,event)))))
 
 (defun exwm-input--mimic-read-event (event)
   "Process EVENT as if it were returned by `read-event'."
@@ -680,8 +660,26 @@ Current buffer must be an `exwm-mode' buffer."
 (defun exwm-input--fake-last-command ()
   "Fool some packages into thinking there is a change in the buffer."
   (setq last-command #'exwm-input--noop)
-  (run-hooks 'pre-command-hook)
-  (run-hooks 'post-command-hook))
+  ;; The Emacs manual says:
+  ;; > Quitting is suppressed while running pre-command-hook and
+  ;; > post-command-hook. If an error happens while executing one of these
+  ;; > hooks, it does not terminate execution of the hook; instead the error is
+  ;; > silenced and the function in which the error occurred is removed from the
+  ;; > hook.
+  ;; We supress errors but neither continue execution nor we remove from the
+  ;; hook.
+  (condition-case err
+      (run-hooks 'pre-command-hook)
+    ((error)
+     (exwm--log "Error occurred while running pre-command-hook: %s"
+                (error-message-string err))
+     (xcb-debug:backtrace)))
+  (condition-case err
+      (run-hooks 'post-command-hook)
+    ((error)
+     (exwm--log "Error occurred while running post-command-hook: %s"
+                (error-message-string err))
+     (xcb-debug:backtrace))))
 
 (defun exwm-input--on-KeyPress-line-mode (key-press raw-data)
   "Parse X KeyPress event to Emacs key event and then feed the command loop."
@@ -725,7 +723,7 @@ Current buffer must be an `exwm-mode' buffer."
       (xcb:flush exwm--connection))))
 
 (defun exwm-input--on-KeyPress-char-mode (key-press &optional _raw-data)
-  "Handle KeyPress event in char-mode."
+  "Handle KeyPress event in `char-mode'."
   (with-slots (detail state) key-press
     (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state))
           event raw-event)
@@ -748,7 +746,7 @@ Current buffer must be an `exwm-mode' buffer."
 (defun exwm-input--on-ButtonPress-line-mode (buffer button-event)
   "Handle button events in line mode.
 BUFFER is the `exwm-mode' buffer the event was generated
-on. BUTTON-EVENT is the X event converted into an Emacs event.
+on.  BUTTON-EVENT is the X event converted into an Emacs event.
 
 The return value is used as event_mode to release the original
 button event."
@@ -766,7 +764,7 @@ button event."
         xcb:Allow:ReplayPointer))))
 
 (defun exwm-input--on-ButtonPress-char-mode ()
-  "Handle button events in char-mode.
+  "Handle button events in `char-mode'.
 The return value is used as event_mode to release the original
 button event."
   (exwm--log)
@@ -842,7 +840,7 @@ button event."
 
 ;;;###autoload
 (defun exwm-input-grab-keyboard (&optional id)
-  "Switch to line-mode."
+  "Switch to `line-mode'."
   (interactive (list (when (derived-mode-p 'exwm-mode)
                        (exwm--buffer->id (window-buffer)))))
   (when id
@@ -853,7 +851,7 @@ button event."
 
 ;;;###autoload
 (defun exwm-input-release-keyboard (&optional id)
-  "Switch to char-mode."
+  "Switch to `char-mode`."
   (interactive (list (when (derived-mode-p 'exwm-mode)
                        (exwm--buffer->id (window-buffer)))))
   (when id
@@ -864,7 +862,7 @@ button event."
 
 ;;;###autoload
 (defun exwm-input-toggle-keyboard (&optional id)
-  "Toggle between 'line-mode' and 'char-mode'."
+  "Toggle between `line-mode' and `char-mode'."
   (interactive (list (when (derived-mode-p 'exwm-mode)
                        (exwm--buffer->id (window-buffer)))))
   (when id
@@ -971,17 +969,12 @@ multiple keys.  If END-KEY is non-nil, stop sending keys if it's pressed."
                    #'exwm-input-send-simulation-key))))
            exwm-input--simulation-keys))
 
-(defun exwm-input-set-simulation-keys (simulation-keys)
-  "Please customize or set `exwm-input-simulation-keys' instead."
-  (declare (obsolete nil "26"))
-  (exwm-input--set-simulation-keys simulation-keys))
-
 (defcustom exwm-input-simulation-keys nil
   "Simulation keys.
 
 It is an alist of the form (original-key . simulated-key), where both
 original-key and simulated-key are key sequences.  Original-key is what you
-type to an X window in line-mode which then gets translated to simulated-key
+type to an X window in `line-mode' which then gets translated to simulated-key
 by EXWM and forwarded to the X window.
 
 Notes:
@@ -1092,7 +1085,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences."
 (defmacro exwm-input-invoke-factory (keys)
   "Make a command that invokes KEYS when called.
 
-One use is to access the keymap bound to KEYS (as prefix keys) in char-mode."
+One use is to access the keymap bound to KEYS (as prefix keys) in `char-mode'."
   (let* ((keys (kbd keys))
          (description (key-description keys)))
     `(defun ,(intern (concat "exwm-input--invoke--" description)) ()
@@ -1116,39 +1109,47 @@ One use is to access the keymap bound to KEYS (as prefix keys) in char-mode."
 
 (defun exwm-input--on-minibuffer-setup ()
   "Run in `minibuffer-setup-hook' to grab keyboard if necessary."
-  (exwm--log)
-  (with-current-buffer
-      (window-buffer (frame-selected-window exwm-workspace--current))
-    (when (and (derived-mode-p 'exwm-mode)
-               (not (exwm-workspace--client-p))
-               (eq exwm--selected-input-mode 'char-mode))
-      (exwm-input--grab-keyboard exwm--id))))
+  (let* ((window (or (minibuffer-selected-window) ; minibuffer-setup-hook
+                     (selected-window)))          ; echo-area-clear-hook
+         (frame (window-frame window)))
+    (when (exwm--terminal-p frame)
+      (with-current-buffer (window-buffer window)
+        (when (and (derived-mode-p 'exwm-mode)
+                   (eq exwm--selected-input-mode 'char-mode))
+          (exwm--log "Grab #x%x window=%s frame=%s" exwm--id window frame)
+          (exwm-input--grab-keyboard exwm--id))))))
 
 (defun exwm-input--on-minibuffer-exit ()
   "Run in `minibuffer-exit-hook' to release keyboard if necessary."
-  (exwm--log)
-  (with-current-buffer
-      (window-buffer (frame-selected-window exwm-workspace--current))
-    (when (and (derived-mode-p 'exwm-mode)
-               (not (exwm-workspace--client-p))
-               (eq exwm--selected-input-mode 'char-mode)
-               (eq exwm--input-mode 'line-mode))
-      (exwm-input--release-keyboard exwm--id))))
+  (let* ((window (or (minibuffer-selected-window) ; minibuffer-setup-hook
+                     (selected-window)))          ; echo-area-clear-hook
+         (frame (window-frame window)))
+    (when (exwm--terminal-p frame)
+      (with-current-buffer (window-buffer window)
+        (when (and (derived-mode-p 'exwm-mode)
+                   (eq exwm--selected-input-mode 'char-mode)
+                   (eq exwm--input-mode 'line-mode))
+          (exwm--log "Release #x%x window=%s frame=%s" exwm--id window frame)
+          (exwm-input--release-keyboard exwm--id))))))
 
 (defun exwm-input--on-echo-area-dirty ()
   "Run when new message arrives to grab keyboard if necessary."
-  (exwm--log)
-  (when (and (not (active-minibuffer-window))
-             (not (exwm-workspace--client-p))
-             cursor-in-echo-area)
+  (when (and cursor-in-echo-area
+             (not (active-minibuffer-window)))
+    (exwm--log)
     (exwm-input--on-minibuffer-setup)))
 
 (defun exwm-input--on-echo-area-clear ()
   "Run in `echo-area-clear-hook' to release keyboard if necessary."
-  (exwm--log)
   (unless (current-message)
+    (exwm--log)
     (exwm-input--on-minibuffer-exit)))
 
+(defun exwm-input--call-with-passthrough (function &rest args)
+  "Bind `exwm-input-line-mode-passthrough' and call FUNCTION with ARGS."
+  (let ((exwm-input-line-mode-passthrough t))
+    (apply function args)))
+
 (defun exwm-input--init ()
   "Initialize the keyboard module."
   (exwm--log)
@@ -1204,7 +1205,10 @@ One use is to access the keymap bound to KEYS (as prefix keys) in char-mode."
         (run-with-idle-timer 0 t #'exwm-input--on-echo-area-dirty))
   (add-hook 'echo-area-clear-hook #'exwm-input--on-echo-area-clear)
   ;; Update focus when buffer list updates
-  (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update))
+  (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)
+
+  (dolist (fun exwm-input--passthrough-functions)
+    (advice-add fun :around #'exwm-input--call-with-passthrough)))
 
 (defun exwm-input--post-init ()
   "The second stage in the initialization of the input module."
@@ -1214,6 +1218,8 @@ One use is to access the keymap bound to KEYS (as prefix keys) in char-mode."
 (defun exwm-input--exit ()
   "Exit the input module."
   (exwm--log)
+  (dolist (fun exwm-input--passthrough-functions)
+    (advice-remove fun #'exwm-input--call-with-passthrough))
   (exwm-input--unset-simulation-keys)
   (remove-hook 'pre-command-hook #'exwm-input--on-pre-command)
   (remove-hook 'post-command-hook #'exwm-input--on-post-command)
@@ -1224,17 +1230,16 @@ One use is to access the keymap bound to KEYS (as prefix keys) in char-mode."
     (setq exwm-input--echo-area-timer nil))
   (remove-hook 'echo-area-clear-hook #'exwm-input--on-echo-area-clear)
   (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)
-  (when exwm-input--update-focus-defer-timer
-    (cancel-timer exwm-input--update-focus-defer-timer))
   (when exwm-input--update-focus-timer
     (cancel-timer exwm-input--update-focus-timer))
   ;; Make input focus working even without a WM.
-  (xcb:+request exwm--connection
-      (make-instance 'xcb:SetInputFocus
-                     :revert-to xcb:InputFocus:PointerRoot
-                     :focus exwm--root
-                     :time xcb:Time:CurrentTime))
-  (xcb:flush exwm--connection))
+  (when (slot-value exwm--connection 'connected)
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:SetInputFocus
+                       :revert-to xcb:InputFocus:PointerRoot
+                       :focus exwm--root
+                       :time xcb:Time:CurrentTime))
+    (xcb:flush exwm--connection)))
 
 
 
diff --git a/third_party/exwm/exwm-layout.el b/third_party/exwm/exwm-layout.el
index 9173a1c049..8649c11ffd 100644
--- a/third_party/exwm/exwm-layout.el
+++ b/third_party/exwm/exwm-layout.el
@@ -1,6 +1,6 @@
 ;;; exwm-layout.el --- Layout Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -29,7 +29,6 @@
 
 (defgroup exwm-layout nil
   "Layout."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-layout-auto-iconify t
@@ -57,8 +56,7 @@
 (declare-function exwm-input--grab-keyboard "exwm-input.el")
 (declare-function exwm-input-grab-keyboard "exwm-input.el")
 (declare-function exwm-workspace--active-p "exwm-workspace.el" (frame))
-(declare-function exwm-workspace--client-p "exwm-workspace.el"
-                  (&optional frame))
+(declare-function exwm-workspace--get-geometry "exwm-workspace.el" (frame))
 (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
 (declare-function exwm-workspace--workspace-p "exwm-workspace.el"
                   (workspace))
@@ -66,7 +64,7 @@
                   (frame-or-index &optional id))
 
 (defun exwm-layout--set-state (id state)
-  "Set WM_STATE."
+  "Set WM_STATE of X window ID to STATE."
   (exwm--log "id=#x%x" id)
   (xcb:+request exwm--connection
       (make-instance 'xcb:icccm:set-WM_STATE
@@ -75,24 +73,28 @@
     (setq exwm-state state)))
 
 (defun exwm-layout--iconic-state-p (&optional id)
+  "Check whether X window ID is in iconic state."
   (= xcb:icccm:WM_STATE:IconicState
      (if id
          (buffer-local-value 'exwm-state (exwm--id->buffer id))
        exwm-state)))
 
-(defun exwm-layout--set-ewmh-state (xwin)
-  "Set _NET_WM_STATE."
-  (with-current-buffer (exwm--id->buffer xwin)
+(defun exwm-layout--set-ewmh-state (id)
+  "Set _NET_WM_STATE of X window ID to the value of variable `exwm--ewmh-state'."
+  (with-current-buffer (exwm--id->buffer id)
     (xcb:+request exwm--connection
         (make-instance 'xcb:ewmh:set-_NET_WM_STATE
                        :window exwm--id
                        :data exwm--ewmh-state))))
 
 (defun exwm-layout--fullscreen-p ()
+  "Check whether current `exwm-mode' buffer is in fullscreen state."
   (when (derived-mode-p 'exwm-mode)
     (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)))
 
 (defun exwm-layout--auto-iconify ()
+  "Helper function to iconify unused X windows.
+See variable `exwm-layout-auto-iconify'."
   (when (and exwm-layout-auto-iconify
              (not exwm-transient-for))
     (let ((xwin exwm--id)
@@ -154,7 +156,8 @@
   (with-current-buffer (exwm--id->buffer id)
     (unless (or (exwm-layout--iconic-state-p)
                 (and exwm--floating-frame
-                     (eq 4294967295. exwm--desktop)))
+                     exwm--desktop
+                     (= 4294967295. exwm--desktop)))
       (exwm--log "Hide #x%x" id)
       (when exwm--floating-frame
         (let* ((container (frame-parameter exwm--floating-frame
@@ -212,7 +215,7 @@
 
 ;;;###autoload
 (cl-defun exwm-layout-unset-fullscreen (&optional id)
-  "Restore window from fullscreen state."
+  "Restore X window ID from fullscreen state."
   (interactive)
   (exwm--log "id=#x%x" (or id 0))
   (unless (and (or id (derived-mode-p 'exwm-mode))
@@ -243,7 +246,7 @@
 
 ;;;###autoload
 (cl-defun exwm-layout-toggle-fullscreen (&optional id)
-  "Toggle fullscreen mode."
+  "Toggle fullscreen mode of X window ID."
   (interactive (list (exwm--buffer->id (window-buffer))))
   (exwm--log "id=#x%x" (or id 0))
   (unless (or id (derived-mode-p 'exwm-mode))
@@ -300,7 +303,8 @@ selected by `other-buffer'."
                                               clients clients-floating))))))
 
 (defun exwm-layout--refresh (&optional frame)
-  "Refresh layout."
+  "Refresh layout of FRAME.
+If FRAME is nil, refresh layout of selected frame."
   ;; `window-size-change-functions' sets this argument while
   ;; `window-configuration-change-hook' makes the frame selected.
   (unless frame
@@ -405,22 +409,29 @@ selected by `other-buffer'."
 (defun exwm-layout--on-minibuffer-setup ()
   "Refresh layout when minibuffer grows."
   (exwm--log)
-  (unless (exwm-workspace--client-p)
-    (exwm--defer 0 (lambda ()
-                     (when (< 1 (window-height (minibuffer-window)))
-                       (exwm-layout--refresh))))))
+  ;; Only when active minibuffer's frame is an EXWM frame.
+  (let* ((mini-window (active-minibuffer-window))
+         (frame (window-frame mini-window)))
+    (when (exwm-workspace--workspace-p frame)
+      (exwm--defer 0 (lambda ()
+                       (when (< 1 (window-height mini-window))
+                         (exwm-layout--refresh frame)))))))
 
 (defun exwm-layout--on-echo-area-change (&optional dirty)
-  "Run when message arrives or in `echo-area-clear-hook' to refresh layout."
-  (when (and (current-message)
-             (not (exwm-workspace--client-p))
-             (or (cl-position ?\n (current-message))
-                 (> (length (current-message))
-                    (frame-width exwm-workspace--current))))
-    (exwm--log)
-    (if dirty
-        (exwm-layout--refresh)
-      (exwm--defer 0 #'exwm-layout--refresh))))
+  "Run when message arrives or in `echo-area-clear-hook' to refresh layout.
+If DIRTY is non-nil, refresh layout immediately."
+  (let ((frame (window-frame (active-minibuffer-window)))
+        (msg (current-message)))
+    ;; Check whether the frame where current window's minibuffer resides (not
+    ;; current window's frame for floating windows!) must be adjusted.
+    (when (and msg
+               (exwm-workspace--workspace-p frame)
+               (or (cl-position ?\n msg)
+                   (> (length msg) (frame-width frame))))
+      (exwm--log)
+      (if dirty
+          (exwm-layout--refresh exwm-workspace--current)
+        (exwm--defer 0 #'exwm-layout--refresh exwm-workspace--current)))))
 
 ;;;###autoload
 (defun exwm-layout-enlarge-window (delta &optional horizontal)
diff --git a/third_party/exwm/exwm-manage.el b/third_party/exwm/exwm-manage.el
index e940257fc9..ab66e298ac 100644
--- a/third_party/exwm/exwm-manage.el
+++ b/third_party/exwm/exwm-manage.el
@@ -1,7 +1,7 @@
 ;;; exwm-manage.el --- Window Management Module for  -*- lexical-binding: t -*-
 ;;;                    EXWM
 
-;; Copyright (C) 2015-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -30,12 +30,11 @@
 
 (defgroup exwm-manage nil
   "Manage."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-manage-finish-hook nil
-  "Normal hook run after a window is just managed, in the context of the
-corresponding buffer."
+  "Normal hook run after a window is just managed.
+This hook runs in the context of the corresponding `exwm-mode' buffer."
   :type 'hook)
 
 (defcustom exwm-manage-force-tiling nil
@@ -151,12 +150,12 @@ want to match against EXWM internal variables such as `exwm-title',
 (defvar exwm-manage--ping-lock nil
   "Non-nil indicates EXWM is pinging a window.")
 
+(defvar exwm-input--skip-buffer-list-update)
 (defvar exwm-input-prefix-keys)
 (defvar exwm-workspace--current)
 (defvar exwm-workspace--id-struts-alist)
 (defvar exwm-workspace--list)
 (defvar exwm-workspace--switch-history-outdated)
-(defvar exwm-workspace--workareas)
 (defvar exwm-workspace-current-index)
 (declare-function exwm--update-class "exwm.el" (id &optional force))
 (declare-function exwm--update-hints "exwm.el" (id &optional force))
@@ -169,17 +168,22 @@ want to match against EXWM internal variables such as `exwm-title',
 (declare-function exwm--update-window-type "exwm.el" (id &optional force))
 (declare-function exwm-floating--set-floating "exwm-floating.el" (id))
 (declare-function exwm-floating--unset-floating "exwm-floating.el" (id))
-(declare-function exwm-input-grab-keyboard "exwm-input.el")
+(declare-function exwm-input-grab-keyboard "exwm-input.el" (&optional id))
+(declare-function exwm-input-release-keyboard "exwm-input.el" (&optional id))
 (declare-function exwm-input-set-local-simulation-keys "exwm-input.el")
 (declare-function exwm-layout--fullscreen-p "exwm-layout.el" ())
 (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
+(declare-function exwm-layout-set-fullscreen "exwm-layout.el" (&optional id))
+(declare-function exwm-workspace--get-geometry "exwm-workspace.el" (frame))
 (declare-function exwm-workspace--position "exwm-workspace.el" (frame))
 (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
 (declare-function exwm-workspace--update-struts "exwm-workspace.el" ())
 (declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
+(declare-function exwm-workspace--workarea "exwm-workspace.el" (frame))
 
 (defun exwm-manage--update-geometry (id &optional force)
-  "Update window geometry."
+  "Update geometry of X window ID.
+Override current geometry if FORCE is non-nil."
   (exwm--log "id=#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and exwm--geometry (not force))
@@ -195,7 +199,7 @@ want to match against EXWM internal variables such as `exwm-title',
                                  :height (/ (x-display-pixel-height) 2))))))))
 
 (defun exwm-manage--update-ewmh-state (id)
-  "Update _NET_WM_STATE."
+  "Update _NET_WM_STATE of X window ID."
   (exwm--log "id=#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless exwm--ewmh-state
@@ -206,7 +210,8 @@ want to match against EXWM internal variables such as `exwm-title',
           (setq exwm--ewmh-state (append (slot-value reply 'value) nil)))))))
 
 (defun exwm-manage--update-mwm-hints (id &optional force)
-  "Update _MOTIF_WM_HINTS."
+  "Update _MOTIF_WM_HINTS of X window ID.
+Override current hinds if FORCE is non-nil."
   (exwm--log "id=#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and (not exwm--mwm-hints-decorations) (not force))
@@ -229,6 +234,23 @@ want to match against EXWM internal variables such as `exwm-title',
                           (elt value 2))) ;MotifWmHints.decorations
               (setq exwm--mwm-hints-decorations nil))))))))
 
+(defun exwm-manage--update-default-directory (id)
+  "Update the `default-directory' of X window ID.
+Sets the `default-directory' of the EXWM buffer associated with X window to
+match its current working directory.
+
+This only works when procfs is mounted, which may not be the case on some BSDs."
+  (with-current-buffer (exwm--id->buffer id)
+    (if-let* ((response (xcb:+request-unchecked+reply exwm--connection
+                            (make-instance 'xcb:ewmh:get-_NET_WM_PID
+                                           :window id)))
+              (pid (slot-value response 'value))
+              (cwd (file-symlink-p (format "/proc/%d/cwd" pid)))
+              ((file-accessible-directory-p cwd)))
+        (setq default-directory (file-name-as-directory cwd))
+      (setq default-directory (expand-file-name "~/")))))
+
+
 (defun exwm-manage--set-client-list ()
   "Set _NET_CLIENT_LIST."
   (exwm--log)
@@ -262,7 +284,8 @@ want to match against EXWM internal variables such as `exwm-title',
         (make-instance 'xcb:ChangeSaveSet
                        :mode xcb:SetMode:Insert
                        :window id))
-    (with-current-buffer (generate-new-buffer "*EXWM*")
+    (with-current-buffer (let ((exwm-input--skip-buffer-list-update t))
+                           (generate-new-buffer "*EXWM*"))
       ;; Keep the oldest X window first.
       (setq exwm--id-buffer-alist
             (nconc exwm--id-buffer-alist `((,id . ,(current-buffer)))))
@@ -324,12 +347,8 @@ want to match against EXWM internal variables such as `exwm-title',
         (with-slots (x y width height) exwm--geometry
           ;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH
           (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type)
-            (let* ((workarea (elt exwm-workspace--workareas
-                                  (exwm-workspace--position exwm--frame)))
-                   (x* (aref workarea 0))
-                   (y* (aref workarea 1))
-                   (width* (aref workarea 2))
-                   (height* (aref workarea 3)))
+            (with-slots ((x* x) (y* y) (width* width) (height* height))
+                (exwm-workspace--workarea exwm--frame)
               (exwm--set-geometry id
                                   (+ x* (/ (- width* width) 2))
                                   (+ y* (/ (- height* height) 2))
@@ -347,7 +366,8 @@ want to match against EXWM internal variables such as `exwm-title',
                              :stack-mode xcb:StackMode:Below)))
         (xcb:flush exwm--connection)
         (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist))
-        (let ((kill-buffer-query-functions nil))
+        (let ((kill-buffer-query-functions nil)
+              (exwm-input--skip-buffer-list-update t))
           (kill-buffer (current-buffer)))
         (throw 'return 'ignored))
       (let ((index (plist-get exwm--configurations 'workspace)))
@@ -390,29 +410,26 @@ want to match against EXWM internal variables such as `exwm-title',
       (if (plist-get exwm--configurations 'char-mode)
           (exwm-input-release-keyboard id)
         (exwm-input-grab-keyboard id))
-      (let ((simulation-keys (plist-get exwm--configurations 'simulation-keys))
-            (prefix-keys (plist-get exwm--configurations 'prefix-keys)))
-        (with-current-buffer (exwm--id->buffer id)
-          (when simulation-keys
-            (exwm-input-set-local-simulation-keys simulation-keys))
-          (when prefix-keys
-            (setq-local exwm-input-prefix-keys prefix-keys))))
+      (when-let ((simulation-keys (plist-get exwm--configurations 'simulation-keys)))
+        (exwm-input-set-local-simulation-keys simulation-keys))
+      (when-let ((prefix-keys (plist-get exwm--configurations 'prefix-keys)))
+        (setq-local exwm-input-prefix-keys prefix-keys))
       (setq exwm-workspace--switch-history-outdated t)
       (exwm--update-desktop id)
       (exwm-manage--update-ewmh-state id)
-      (with-current-buffer (exwm--id->buffer id)
-        (when (or (plist-get exwm--configurations 'fullscreen)
-                  (exwm-layout--fullscreen-p))
-          (setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN
-                                       exwm--ewmh-state))
-          (exwm-layout-set-fullscreen id))
-        (run-hooks 'exwm-manage-finish-hook)))))
+      (exwm-manage--update-default-directory id)
+      (when (or (plist-get exwm--configurations 'fullscreen)
+                (exwm-layout--fullscreen-p))
+        (setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN
+                                     exwm--ewmh-state))
+        (exwm-layout-set-fullscreen id))
+      (run-hooks 'exwm-manage-finish-hook))))
 
 (defun exwm-manage--unmanage-window (id &optional withdraw-only)
   "Unmanage window ID.
 
 If WITHDRAW-ONLY is non-nil, the X window will be properly placed back to the
-root window.  Set WITHDRAW-ONLY to 'quit if this functions is used when window
+root window.  Set WITHDRAW-ONLY to `quit' if this functions is used when window
 manager is shutting down."
   (let ((buffer (exwm--id->buffer id)))
     (exwm--log "Unmanage #x%x (buffer: %s, widthdraw: %s)"
@@ -427,7 +444,9 @@ manager is shutting down."
       (exwm-workspace--update-workareas)
       (dolist (f exwm-workspace--list)
         (exwm-workspace--set-fullscreen f)))
-    (when (buffer-live-p buffer)
+    (when (and (buffer-live-p buffer)
+               ;; Invoked from `exwm-manage--exit' upon disconnection.
+               (slot-value exwm--connection 'connected))
       (with-current-buffer buffer
         ;; Unmap the X window.
         (xcb:+request exwm--connection
@@ -509,8 +528,11 @@ manager is shutting down."
 
 (defun exwm-manage--kill-buffer-query-function ()
   "Run in `kill-buffer-query-functions'."
-  (exwm--log "id=#x%x; buffer=%s" exwm--id (current-buffer))
+  (exwm--log "id=#x%x; buffer=%s" (or exwm--id 0) (current-buffer))
   (catch 'return
+    (when (or (not exwm--connection)
+              (not (slot-value exwm--connection 'connected)))
+      (throw 'return t))
     (when (or (not exwm--id)
               (xcb:+request-checked+request-check exwm--connection
                   (make-instance 'xcb:ChangeWindowAttributes
@@ -587,7 +609,8 @@ Would you like to kill it? "
         (throw 'return nil)))))
 
 (defun exwm-manage--kill-client (&optional id)
-  "Kill an X client."
+  "Kill X client ID.
+If ID is nil, kill X window corresponding to current buffer."
   (unless id (setq id (exwm--buffer->id (current-buffer))))
   (exwm--log "id=#x%x" id)
   (let* ((response (xcb:+request-unchecked+reply exwm--connection
@@ -605,14 +628,16 @@ Would you like to kill it? "
     (xcb:flush exwm--connection)))
 
 (defun exwm-manage--add-frame (frame)
-  "Run in `after-make-frame-functions'."
+  "Run in `after-make-frame-functions'.
+FRAME is the newly created frame."
   (exwm--log "frame=%s" frame)
   (when (display-graphic-p frame)
     (push (string-to-number (frame-parameter frame 'outer-window-id))
           exwm-manage--frame-outer-id-list)))
 
 (defun exwm-manage--remove-frame (frame)
-  "Run in `delete-frame-functions'."
+  "Run in `delete-frame-functions'.
+FRAME is the frame to be deleted."
   (exwm--log "frame=%s" frame)
   (when (display-graphic-p frame)
     (setq exwm-manage--frame-outer-id-list
@@ -620,7 +645,8 @@ Would you like to kill it? "
                 exwm-manage--frame-outer-id-list))))
 
 (defun exwm-manage--on-ConfigureRequest (data _synthetic)
-  "Handle ConfigureRequest event."
+  "Handle ConfigureRequest event.
+DATA contains unmarshalled ConfigureRequest event data."
   (exwm--log)
   (let ((obj (make-instance 'xcb:ConfigureRequest))
         buffer edges width-delta height-delta)
@@ -710,7 +736,8 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
   (xcb:flush exwm--connection))
 
 (defun exwm-manage--on-MapRequest (data _synthetic)
-  "Handle MapRequest event."
+  "Handle MapRequest event.
+DATA contains unmarshalled MapRequest event data."
   (let ((obj (make-instance 'xcb:MapRequest)))
     (xcb:unmarshal obj data)
     (with-slots (parent window) obj
@@ -730,7 +757,8 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
           (exwm-manage--manage-window window))))))
 
 (defun exwm-manage--on-UnmapNotify (data _synthetic)
-  "Handle UnmapNotify event."
+  "Handle UnmapNotify event.
+DATA contains unmarshalled UnmapNotify event data."
   (let ((obj (make-instance 'xcb:UnmapNotify)))
     (xcb:unmarshal obj data)
     (with-slots (window) obj
@@ -738,7 +766,8 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
       (exwm-manage--unmanage-window window t))))
 
 (defun exwm-manage--on-MapNotify (data _synthetic)
-  "Handle MapNotify event."
+  "Handle MapNotify event.
+DATA contains unmarshalled MapNotify event data."
   (let ((obj (make-instance 'xcb:MapNotify)))
     (xcb:unmarshal obj data)
     (with-slots (window) obj
@@ -763,7 +792,9 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
         (xcb:flush exwm--connection)))))
 
 (defun exwm-manage--on-DestroyNotify (data synthetic)
-  "Handle DestroyNotify event."
+  "Handle DestroyNotify event.
+DATA contains unmarshalled DestroyNotify event data.
+SYNTHETIC indicates whether the event is a synthetic event."
   (unless synthetic
     (exwm--log)
     (let ((obj (make-instance 'xcb:DestroyNotify)))
diff --git a/third_party/exwm/exwm-randr.el b/third_party/exwm/exwm-randr.el
index 68bfdd70e9..7f0e50559b 100644
--- a/third_party/exwm/exwm-randr.el
+++ b/third_party/exwm/exwm-randr.el
@@ -1,6 +1,6 @@
 ;;; exwm-randr.el --- RandR Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -52,9 +52,10 @@
 (require 'exwm-core)
 (require 'exwm-workspace)
 
+(declare-function x-get-atom-name "C source code" (VALUE &optional FRAME))
+
 (defgroup exwm-randr nil
   "RandR."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-randr-refresh-hook nil
@@ -89,10 +90,6 @@ corresponding monitors whenever the monitors are active.
   \\='(1 \"HDMI-1\" 3 \"DP-1\")"
   :type '(plist :key-type integer :value-type string))
 
-(with-no-warnings
-  (define-obsolete-variable-alias 'exwm-randr-workspace-output-plist
-    'exwm-randr-workspace-monitor-plist "27.1"))
-
 (defvar exwm-randr--last-timestamp 0 "Used for debouncing events.")
 
 (defvar exwm-randr--prev-screen-change-seqnum nil
@@ -267,9 +264,6 @@ In a mirroring setup some monitors overlap and should be treated as one."
       (xcb:flush exwm--connection)
       (run-hooks 'exwm-randr-refresh-hook))))
 
-(define-obsolete-function-alias 'exwm-randr--refresh #'exwm-randr-refresh
-  "27.1")
-
 (defun exwm-randr--on-ScreenChangeNotify (data _synthetic)
   "Handle `ScreenChangeNotify' event.
 
diff --git a/third_party/exwm/exwm-systemtray.el b/third_party/exwm/exwm-systemtray.el
index 43b3e1eaef..9e57dae4eb 100644
--- a/third_party/exwm/exwm-systemtray.el
+++ b/third_party/exwm/exwm-systemtray.el
@@ -1,7 +1,7 @@
 ;;; exwm-systemtray.el --- System Tray Module for  -*- lexical-binding: t -*-
 ;;;                        EXWM
 
-;; Copyright (C) 2016-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2016-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -30,6 +30,7 @@
 
 ;;; Code:
 
+(require 'xcb-ewmh)
 (require 'xcb-icccm)
 (require 'xcb-xembed)
 (require 'xcb-systemtray)
@@ -37,6 +38,8 @@
 (require 'exwm-core)
 (require 'exwm-workspace)
 
+(declare-function exwm-workspace--workarea "exwm-workspace.el" (frame))
+
 (defclass exwm-systemtray--icon ()
   ((width :initarg :width)
    (height :initarg :height)
@@ -46,7 +49,7 @@
 (defclass xcb:systemtray:-ClientMessage
   (xcb:icccm:--ClientMessage xcb:ClientMessage)
   ((format :initform 32)
-   (type :initform xcb:Atom:MANAGER)
+   (type :initform 'xcb:Atom:MANAGER)
    (time :initarg :time :type xcb:TIMESTAMP)      ;new slot
    (selection :initarg :selection :type xcb:ATOM) ;new slot
    (owner :initarg :owner :type xcb:WINDOW))      ;new slot
@@ -54,7 +57,6 @@
 
 (defgroup exwm-systemtray nil
   "System tray."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-systemtray-height nil
@@ -67,44 +69,49 @@ You shall use the default value if using auto-hide minibuffer."
   "Gap between icons."
   :type 'integer)
 
+(defvar exwm-systemtray--connection nil "The X connection.")
+
 (defvar exwm-systemtray--embedder-window nil "The embedder window.")
+(defvar exwm-systemtray--embedder-window-depth nil
+  "The embedder window's depth.")
 
-(defcustom exwm-systemtray-background-color nil
+(defcustom exwm-systemtray-background-color 'workspace-background
   "Background color of systemtray.
-
-This should be a color, or nil for transparent background."
-  :type '(choice (const :tag "Transparent" nil)
-                 (color))
+This should be a color, the symbol `workspace-background' for the background
+color of current workspace frame, or the symbol `transparent' for transparent
+background.
+
+Transparent background is not yet supported when Emacs uses 32-bit depth
+visual, as reported by `x-display-planes'.  The X resource \"Emacs.visualClass:
+TrueColor-24\" can be used to force Emacs to use 24-bit depth."
+  :type '(choice (const :tag "Transparent" transparent)
+                 (const :tag "Frame background" workspace-background)
+                 (color :tag "Color"))
   :initialize #'custom-initialize-default
   :set (lambda (symbol value)
+         (when (and (eq value 'transparent)
+                    (not (exwm-systemtray--transparency-supported-p)))
+           (display-warning 'exwm-systemtray
+                            "Transparent background is not supported yet when \
+using 32-bit depth.  Using `workspace-background' instead.")
+           (setq value 'workspace-background))
          (set-default symbol value)
-         ;; Change the background color for embedder.
-         (when (and exwm--connection
+         (when (and exwm-systemtray--connection
                     exwm-systemtray--embedder-window)
-           (let ((background-pixel (exwm--color->pixel value)))
-             (xcb:+request exwm--connection
-                 (make-instance 'xcb:ChangeWindowAttributes
-                                :window exwm-systemtray--embedder-window
-                                :value-mask (logior xcb:CW:BackPixmap
-                                                    (if background-pixel
-                                                        xcb:CW:BackPixel 0))
-                                :background-pixmap
-                                xcb:BackPixmap:ParentRelative
-                                :background-pixel background-pixel))
-             ;; Unmap & map to take effect immediately.
-             (xcb:+request exwm--connection
-                 (make-instance 'xcb:UnmapWindow
-                                :window exwm-systemtray--embedder-window))
-             (xcb:+request exwm--connection
-                 (make-instance 'xcb:MapWindow
-                                :window exwm-systemtray--embedder-window))
-             (xcb:flush exwm--connection)))))
+           ;; Change the background color for embedder.
+           (exwm-systemtray--set-background-color)
+           ;; Unmap & map to take effect immediately.
+           (xcb:+request exwm-systemtray--connection
+                         (make-instance 'xcb:UnmapWindow
+                                        :window exwm-systemtray--embedder-window))
+           (xcb:+request exwm-systemtray--connection
+                         (make-instance 'xcb:MapWindow
+                                        :window exwm-systemtray--embedder-window))
+           (xcb:flush exwm-systemtray--connection))))
 
 ;; GTK icons require at least 16 pixels to show normally.
 (defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.")
 
-(defvar exwm-systemtray--connection nil "The X connection.")
-
 (defvar exwm-systemtray--list nil "The icon list.")
 
 (defvar exwm-systemtray--selection-owner-window nil
@@ -113,7 +120,7 @@ This should be a color, or nil for transparent background."
 (defvar xcb:Atom:_NET_SYSTEM_TRAY_S0)
 
 (defun exwm-systemtray--embed (icon)
-  "Embed an icon."
+  "Embed an ICON."
   (exwm--log "Try to embed #x%x" icon)
   (let ((info (xcb:+request-unchecked+reply exwm-systemtray--connection
                   (make-instance 'xcb:xembed:get-_XEMBED_INFO
@@ -202,7 +209,7 @@ This should be a color, or nil for transparent background."
       (exwm-systemtray--refresh))))
 
 (defun exwm-systemtray--unembed (icon)
-  "Unembed an icon."
+  "Unembed an ICON."
   (exwm--log "Unembed #x%x" icon)
   (xcb:+request exwm-systemtray--connection
       (make-instance 'xcb:UnmapWindow :window icon))
@@ -234,14 +241,13 @@ This should be a color, or nil for transparent background."
         (setq x (+ x (slot-value (cdr pair) 'width)
                    exwm-systemtray-icon-gap))
         (setq map t)))
-    (let ((workarea (elt exwm-workspace--workareas
-                         exwm-workspace-current-index)))
+    (let ((workarea (exwm-workspace--workarea exwm-workspace-current-index)))
       (xcb:+request exwm-systemtray--connection
           (make-instance 'xcb:ConfigureWindow
                          :window exwm-systemtray--embedder-window
                          :value-mask (logior xcb:ConfigWindow:X
                                              xcb:ConfigWindow:Width)
-                         :x (- (aref workarea 2) x)
+                         :x (- (slot-value workarea 'width) x)
                          :width x)))
     (when map
       (xcb:+request exwm-systemtray--connection
@@ -249,8 +255,83 @@ This should be a color, or nil for transparent background."
                          :window exwm-systemtray--embedder-window))))
   (xcb:flush exwm-systemtray--connection))
 
+(defun exwm-systemtray--refresh-background-color (&optional remap)
+  "Refresh background color after theme change or workspace switch.
+If REMAP is not nil, map and unmap the embedder window so that the background is
+redrawn."
+  ;; Only `workspace-background' is dependent on current theme and workspace.
+  (when (eq 'workspace-background exwm-systemtray-background-color)
+    (exwm-systemtray--set-background-color)
+    (when remap
+      (xcb:+request exwm-systemtray--connection
+                    (make-instance 'xcb:UnmapWindow
+                                   :window exwm-systemtray--embedder-window))
+      (xcb:+request exwm-systemtray--connection
+                    (make-instance 'xcb:MapWindow
+                                   :window exwm-systemtray--embedder-window))
+      (xcb:flush exwm-systemtray--connection))))
+
+(defun exwm-systemtray--set-background-color ()
+  "Change the background color of the embedder.
+The color is set according to `exwm-systemtray-background-color'.
+
+Note that this function does not change the current contents of the embedder
+window; unmap & map are necessary for the background color to take effect."
+  (when (and exwm-systemtray--connection
+             exwm-systemtray--embedder-window)
+    (let* ((color (cl-case exwm-systemtray-background-color
+                    ((transparent nil) ; nil means transparent as well
+                     (if (exwm-systemtray--transparency-supported-p)
+                         nil
+                       (message "%s" "[EXWM] system tray does not support \
+`transparent' background; using `workspace-background' instead")
+                       (face-background 'default exwm-workspace--current)))
+                    (workspace-background
+                     (face-background 'default exwm-workspace--current))
+                    (t exwm-systemtray-background-color)))
+           (background-pixel (exwm--color->pixel color)))
+      (xcb:+request exwm-systemtray--connection
+                    (make-instance 'xcb:ChangeWindowAttributes
+                                   :window exwm-systemtray--embedder-window
+                                   ;; Either-or.  A `background-pixel' of nil
+                                   ;; means simulate transparency.  We use
+                                   ;; `xcb:CW:BackPixmap' together with
+                                   ;; `xcb:BackPixmap:ParentRelative' do that,
+                                   ;; but this only works when the parent
+                                   ;; window's visual (Emacs') has the same
+                                   ;; visual depth.
+                                   :value-mask (if background-pixel
+                                                   xcb:CW:BackPixel
+                                                 xcb:CW:BackPixmap)
+                                   ;; Due to the :value-mask above,
+                                   ;; :background-pixmap only takes effect when
+                                   ;; `transparent' is requested and supported
+                                   ;; (visual depth of Emacs and of system tray
+                                   ;; are equal).  Setting
+                                   ;; `xcb:BackPixmap:ParentRelative' when
+                                   ;; that's not the case would produce an
+                                   ;; `xcb:Match' error.
+                                   :background-pixmap xcb:BackPixmap:ParentRelative
+                                   :background-pixel background-pixel)))))
+
+(defun exwm-systemtray--transparency-supported-p ()
+  "Check whether transparent background is supported.
+EXWM system tray supports transparency when the visual depth of the system tray
+window matches that of Emacs.  The visual depth of the system tray window is the
+default visual depth of the display.
+
+Sections \"Visual and background pixmap handling\" and
+\"_NET_SYSTEM_TRAY_VISUAL\" of the System Tray Protocol Specification
+\(https://specifications.freedesktop.org/systemtray-spec/systemtray-spec-latest.html#visuals)
+indicate how to support actual transparency."
+  (let ((planes (x-display-planes)))
+    (if exwm-systemtray--embedder-window-depth
+        (= planes exwm-systemtray--embedder-window-depth)
+      (<= planes 24))))
+
 (defun exwm-systemtray--on-DestroyNotify (data _synthetic)
-  "Unembed icons on DestroyNotify."
+  "Unembed icons on DestroyNotify.
+Argument DATA contains the raw event data."
   (exwm--log)
   (let ((obj (make-instance 'xcb:DestroyNotify)))
     (xcb:unmarshal obj data)
@@ -259,7 +340,8 @@ This should be a color, or nil for transparent background."
         (exwm-systemtray--unembed window)))))
 
 (defun exwm-systemtray--on-ReparentNotify (data _synthetic)
-  "Unembed icons on ReparentNotify."
+  "Unembed icons on ReparentNotify.
+Argument DATA contains the raw event data."
   (exwm--log)
   (let ((obj (make-instance 'xcb:ReparentNotify)))
     (xcb:unmarshal obj data)
@@ -269,7 +351,8 @@ This should be a color, or nil for transparent background."
         (exwm-systemtray--unembed window)))))
 
 (defun exwm-systemtray--on-ResizeRequest (data _synthetic)
-  "Resize the tray icon on ResizeRequest."
+  "Resize the tray icon on ResizeRequest.
+Argument DATA contains the raw event data."
   (exwm--log)
   (let ((obj (make-instance 'xcb:ResizeRequest))
         attr)
@@ -297,7 +380,8 @@ This should be a color, or nil for transparent background."
         (exwm-systemtray--refresh)))))
 
 (defun exwm-systemtray--on-PropertyNotify (data _synthetic)
-  "Map/Unmap the tray icon on PropertyNotify."
+  "Map/Unmap the tray icon on PropertyNotify.
+Argument DATA contains the raw event data."
   (exwm--log)
   (let ((obj (make-instance 'xcb:PropertyNotify))
         attr info visible)
@@ -322,7 +406,8 @@ This should be a color, or nil for transparent background."
           (exwm-systemtray--refresh))))))
 
 (defun exwm-systemtray--on-ClientMessage (data _synthetic)
-  "Handle client messages."
+  "Handle client messages.
+Argument DATA contains the raw event data."
   (let ((obj (make-instance 'xcb:ClientMessage))
         opcode data32)
     (xcb:unmarshal obj data)
@@ -341,7 +426,8 @@ This should be a color, or nil for transparent background."
                (exwm--log "Unknown opcode message: %s" obj)))))))
 
 (defun exwm-systemtray--on-KeyPress (data _synthetic)
-  "Forward all KeyPress events to Emacs frame."
+  "Forward all KeyPress events to Emacs frame.
+Argument DATA contains the raw event data."
   (exwm--log)
   ;; This function is only executed when there's no autohide minibuffer,
   ;; a workspace frame has the input focus and the pointer is over a
@@ -370,13 +456,18 @@ This should be a color, or nil for transparent background."
                                 (frame-parameter exwm-workspace--current
                                                  'window-id))
                        :x 0
-                       :y (- (elt (elt exwm-workspace--workareas
-                                       exwm-workspace-current-index)
-                                  3)
+                       :y (- (slot-value (exwm-workspace--workarea
+                                           exwm-workspace-current-index)
+                                         'height)
                              exwm-workspace--frame-y-offset
                              exwm-systemtray-height))))
+  (exwm-systemtray--refresh-background-color)
   (exwm-systemtray--refresh))
 
+(defun exwm-systemtray--on-theme-change (_theme)
+  "Refresh system tray upon theme change."
+  (exwm-systemtray--refresh-background-color 'remap))
+
 (defun exwm-systemtray--refresh-all ()
   "Reposition/Refresh the system tray."
   (exwm--log)
@@ -386,9 +477,9 @@ This should be a color, or nil for transparent background."
         (make-instance 'xcb:ConfigureWindow
                        :window exwm-systemtray--embedder-window
                        :value-mask xcb:ConfigWindow:Y
-                       :y (- (elt (elt exwm-workspace--workareas
-                                       exwm-workspace-current-index)
-                                  3)
+                       :y (- (slot-value (exwm-workspace--workarea
+                                           exwm-workspace-current-index)
+                                         'height)
                              exwm-workspace--frame-y-offset
                              exwm-systemtray-height))))
   (exwm-systemtray--refresh))
@@ -402,7 +493,8 @@ This should be a color, or nil for transparent background."
   (cl-assert (not exwm-systemtray--embedder-window))
   (unless exwm-systemtray-height
     (setq exwm-systemtray-height (max exwm-systemtray--icon-min-size
-                                      (line-pixel-height))))
+                                      (with-selected-window (minibuffer-window)
+                                        (line-pixel-height)))))
   ;; Create a new connection.
   (setq exwm-systemtray--connection (xcb:connect))
   (set-process-query-on-exit-flag (slot-value exwm-systemtray--connection
@@ -469,8 +561,7 @@ This should be a color, or nil for transparent background."
                        :data xcb:systemtray:ORIENTATION:HORZ)))
   ;; Create the embedder.
   (let ((id (xcb:generate-id exwm-systemtray--connection))
-        (background-pixel (exwm--color->pixel exwm-systemtray-background-color))
-        frame parent depth y)
+        frame parent embedder-depth embedder-visual embedder-colormap y)
     (setq exwm-systemtray--embedder-window id)
     (if (exwm-workspace--minibuffer-own-frame-p)
         (setq frame exwm-workspace--minibuffer
@@ -482,20 +573,26 @@ This should be a color, or nil for transparent background."
       (exwm-workspace--update-offsets)
       (setq frame exwm-workspace--current
             ;; Bottom aligned.
-            y (- (elt (elt exwm-workspace--workareas
-                           exwm-workspace-current-index)
-                      3)
+            y (- (slot-value (exwm-workspace--workarea
+                               exwm-workspace-current-index)
+                             'height)
                  exwm-workspace--frame-y-offset
                  exwm-systemtray-height)))
-    (setq parent (string-to-number (frame-parameter frame 'window-id))
-          depth (slot-value (xcb:+request-unchecked+reply
-                                exwm-systemtray--connection
-                                (make-instance 'xcb:GetGeometry
-                                               :drawable parent))
-                            'depth))
+    (setq parent (string-to-number (frame-parameter frame 'window-id)))
+    ;; Use default depth, visual and colormap (from root window), instead of
+    ;; Emacs frame's.  See Section "Visual and background pixmap handling" in
+    ;; "System Tray Protocol Specification 0.3".
+    (let* ((vdc (exwm--get-visual-depth-colormap exwm-systemtray--connection
+                                                 exwm--root)))
+      (setq embedder-visual (car vdc))
+      (setq embedder-depth (cadr vdc))
+      (setq embedder-colormap (caddr vdc)))
+    ;; Note down the embedder window's depth.  It will be used to check whether
+    ;; we can use xcb:BackPixmap:ParentRelative to emulate transparency.
+    (setq exwm-systemtray--embedder-window-depth embedder-depth)
     (xcb:+request exwm-systemtray--connection
         (make-instance 'xcb:CreateWindow
-                       :depth depth
+                       :depth embedder-depth
                        :wid id
                        :parent parent
                        :x 0
@@ -504,19 +601,29 @@ This should be a color, or nil for transparent background."
                        :height exwm-systemtray-height
                        :border-width 0
                        :class xcb:WindowClass:InputOutput
-                       :visual 0
-                       :value-mask (logior xcb:CW:BackPixmap
-                                           (if background-pixel
-                                               xcb:CW:BackPixel 0)
+                       :visual embedder-visual
+                       :colormap embedder-colormap
+                       :value-mask (logior xcb:CW:BorderPixel
+                                           xcb:CW:Colormap
                                            xcb:CW:EventMask)
-                       :background-pixmap xcb:BackPixmap:ParentRelative
-                       :background-pixel background-pixel
+                       :border-pixel 0
                        :event-mask xcb:EventMask:SubstructureNotify))
+    (exwm-systemtray--set-background-color)
     ;; Set _NET_WM_NAME.
     (xcb:+request exwm-systemtray--connection
         (make-instance 'xcb:ewmh:set-_NET_WM_NAME
                        :window id
-                       :data "EXWM: exwm-systemtray--embedder-window")))
+                       :data "EXWM: exwm-systemtray--embedder-window"))
+    ;; Set _NET_WM_WINDOW_TYPE.
+    (xcb:+request exwm-systemtray--connection
+        (make-instance 'xcb:ewmh:set-_NET_WM_WINDOW_TYPE
+                       :window id
+                       :data (vector xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK)))
+    ;; Set _NET_SYSTEM_TRAY_VISUAL.
+    (xcb:+request exwm-systemtray--connection
+        (make-instance 'xcb:xembed:set-_NET_SYSTEM_TRAY_VISUAL
+                       :window exwm-systemtray--selection-owner-window
+                       :data embedder-visual)))
   (xcb:flush exwm-systemtray--connection)
   ;; Attach event listeners.
   (xcb:+event exwm-systemtray--connection 'xcb:DestroyNotify
@@ -536,6 +643,9 @@ This should be a color, or nil for transparent background."
   (add-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch)
   (add-hook 'exwm-workspace--update-workareas-hook
             #'exwm-systemtray--refresh-all)
+  ;; Add hook to update background colors.
+  (add-hook 'enable-theme-functions #'exwm-systemtray--on-theme-change)
+  (add-hook 'disable-theme-functions #'exwm-systemtray--on-theme-change)
   (add-hook 'menu-bar-mode-hook #'exwm-systemtray--refresh-all)
   (add-hook 'tool-bar-mode-hook #'exwm-systemtray--refresh-all)
   (when (boundp 'exwm-randr-refresh-hook)
@@ -548,27 +658,31 @@ This should be a color, or nil for transparent background."
   "Exit the systemtray module."
   (exwm--log)
   (when exwm-systemtray--connection
-    ;; Hide & reparent out the embedder before disconnection to prevent
-    ;; embedded icons from being reparented to an Emacs frame (which is the
-    ;; parent of the embedder).
-    (xcb:+request exwm-systemtray--connection
-        (make-instance 'xcb:UnmapWindow
-                       :window exwm-systemtray--embedder-window))
-    (xcb:+request exwm-systemtray--connection
-        (make-instance 'xcb:ReparentWindow
-                       :window exwm-systemtray--embedder-window
-                       :parent exwm--root
-                       :x 0
-                       :y 0))
-    (xcb:disconnect exwm-systemtray--connection)
+    (when (slot-value exwm-systemtray--connection 'connected)
+      ;; Hide & reparent out the embedder before disconnection to prevent
+      ;; embedded icons from being reparented to an Emacs frame (which is the
+      ;; parent of the embedder).
+      (xcb:+request exwm-systemtray--connection
+          (make-instance 'xcb:UnmapWindow
+                         :window exwm-systemtray--embedder-window))
+      (xcb:+request exwm-systemtray--connection
+          (make-instance 'xcb:ReparentWindow
+                         :window exwm-systemtray--embedder-window
+                         :parent exwm--root
+                         :x 0
+                         :y 0))
+      (xcb:disconnect exwm-systemtray--connection))
     (setq exwm-systemtray--connection nil
           exwm-systemtray--list nil
           exwm-systemtray--selection-owner-window nil
-          exwm-systemtray--embedder-window nil)
+          exwm-systemtray--embedder-window nil
+          exwm-systemtray--embedder-window-depth nil)
     (remove-hook 'exwm-workspace-switch-hook
                  #'exwm-systemtray--on-workspace-switch)
     (remove-hook 'exwm-workspace--update-workareas-hook
                  #'exwm-systemtray--refresh-all)
+    (remove-hook 'enable-theme-functions #'exwm-systemtray--on-theme-change)
+    (remove-hook 'disable-theme-functions #'exwm-systemtray--on-theme-change)
     (remove-hook 'menu-bar-mode-hook #'exwm-systemtray--refresh-all)
     (remove-hook 'tool-bar-mode-hook #'exwm-systemtray--refresh-all)
     (when (boundp 'exwm-randr-refresh-hook)
diff --git a/third_party/exwm/exwm-workspace.el b/third_party/exwm/exwm-workspace.el
index c513347119..89be697159 100644
--- a/third_party/exwm/exwm-workspace.el
+++ b/third_party/exwm/exwm-workspace.el
@@ -1,6 +1,6 @@
 ;;; exwm-workspace.el --- Workspace Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 1015-2021 Free Software Foundation, Inc.
+;; Copyright (C) 1015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -31,7 +31,6 @@
 
 (defgroup exwm-workspace nil
   "Workspace."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-workspace-switch-hook nil
@@ -39,8 +38,8 @@
   :type 'hook)
 
 (defcustom exwm-workspace-list-change-hook nil
-  "Normal hook run when the workspace list is changed (workspace added,
-deleted, moved, etc)."
+  "Normal hook run when the workspace list is changed.
+This happens when a workspace is added, deleted, moved, etc."
   :type 'hook)
 
 (defcustom exwm-workspace-show-all-buffers nil
@@ -74,8 +73,7 @@ A restart is required for this change to take effect."
   :type 'integer)
 
 (defcustom exwm-workspace-switch-create-limit 10
-  "Number of workspaces `exwm-workspace-switch-create' allowed to create
-each time."
+  "Number of workspaces `exwm-workspace-switch-create' is allowed to create."
   :type 'integer)
 
 (defvar exwm-workspace-current-index 0 "Index of current active workspace.")
@@ -85,9 +83,6 @@ each time."
 
 If the minibuffer is detached, this value is 0.")
 
-(defvar exwm-workspace--client nil
-  "The 'client' frame parameter of emacsclient frames.")
-
 (defvar exwm-workspace--create-silently nil
   "When non-nil workspaces are created in the background (not switched to).
 
@@ -153,8 +148,8 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.")
 
 (defsubst exwm-workspace--position (frame)
   "Retrieve index of given FRAME in workspace list.
-
-NIL if FRAME is not a workspace"
+NIL if FRAME is not a workspace."
+  (declare (indent defun))
   (cl-position frame exwm-workspace--list))
 
 (defsubst exwm-workspace--count ()
@@ -163,28 +158,23 @@ NIL if FRAME is not a workspace"
 
 (defsubst exwm-workspace--workspace-p (frame)
   "Return t if FRAME is a workspace."
+  (declare (indent defun))
   (memq frame exwm-workspace--list))
 
-(defvar exwm-workspace--client-p-hash-table
-  (make-hash-table :test 'eq :weakness 'key)
-  "Used to cache the results of calling β€˜exwm-workspace--client-p’.")
-
-(defsubst exwm-workspace--client-p (&optional frame)
-  "Return non-nil if FRAME is an emacsclient frame."
-  (let* ((frame (or frame (selected-frame)))
-         (cached-value
-          (gethash frame exwm-workspace--client-p-hash-table 'absent)))
-    (if (eq cached-value 'absent)
-        (puthash frame
-                 (or (frame-parameter frame 'client)
-                     (not (display-graphic-p frame)))
-                 exwm-workspace--client-p-hash-table)
-        cached-value)))
+(defsubst exwm-workspace--workarea (frame)
+  "Return workarea corresponding to FRAME.
+FRAME may be either a workspace frame or a workspace position."
+  (declare (indent defun))
+  (elt exwm-workspace--workareas
+       (if (integerp frame)
+           frame
+         (exwm-workspace--position frame))))
 
 (defvar exwm-workspace--switch-map nil
   "Keymap used for interactively selecting workspace.")
 
 (defun exwm-workspace--init-switch-map ()
+  "Initialize variable `exwm-workspace--switch-map'."
   (let ((map (make-sparse-keymap)))
     (define-key map [t] (lambda () (interactive)))
     (define-key map "+" #'exwm-workspace--prompt-add)
@@ -235,7 +225,8 @@ NIL if FRAME is not a workspace"
    (t (user-error "[EXWM] Invalid workspace: %s" frame-or-index))))
 
 (defun exwm-workspace--prompt-for-workspace (&optional prompt)
-  "Prompt for a workspace, returning the workspace frame."
+  "Prompt for a workspace, returning the workspace frame.
+Show PROMPT to the user if non-nil."
   (exwm-workspace--update-switch-history)
   (let* ((current-idx (exwm-workspace--position exwm-workspace--current))
          (history-add-new-input nil)  ;prevent modifying history
@@ -264,7 +255,6 @@ NIL if FRAME is not a workspace"
   (when (and exwm-workspace--prompt-delete-allowed
              (< 1 (exwm-workspace--count)))
     (let ((frame (elt exwm-workspace--list (1- minibuffer-history-position))))
-      (exwm-workspace--get-remove-frame-next-workspace frame)
       (if (eq frame exwm-workspace--current)
           ;; Abort the recursive minibuffer if deleting the current workspace.
           (progn
@@ -351,63 +341,69 @@ NIL if FRAME is not a workspace"
 
 (defun exwm-workspace--update-workareas ()
   "Update `exwm-workspace--workareas'."
-  (let ((root-width (x-display-pixel-width))
-        (root-height (x-display-pixel-height))
-        workareas
-        edge width position
-        delta)
-    ;; Calculate workareas with no struts.
-    (if (frame-parameter (car exwm-workspace--list) 'exwm-geometry)
-        ;; Use the 'exwm-geometry' frame parameter if possible.
-        (dolist (f exwm-workspace--list)
-          (with-slots (x y width height) (frame-parameter f 'exwm-geometry)
-            (setq workareas (append workareas
-                                    (list (vector x y width height))))))
-      ;; Fall back to use the screen size.
-      (let ((workarea (vector 0 0 root-width root-height)))
-        (setq workareas (make-list (exwm-workspace--count) workarea))))
+  (let* ((root-width (x-display-pixel-width))
+         (root-height (x-display-pixel-height))
+         ;; Get workareas prior to struts.
+         (workareas (mapcar
+                     (lambda (frame)
+                       (if-let (rect (frame-parameter frame 'exwm-geometry))
+                           ;; Use the 'exwm-geometry' frame parameter if it
+                           ;; exists.  Make sure to clone it, will be modified
+                           ;; below!
+                           (clone rect)
+                         ;; Fall back to use the screen size.
+                         (make-instance 'xcb:RECTANGLE
+                                        :x 0
+                                        :y 0
+                                        :width root-width
+                                        :height root-height)))
+                     exwm-workspace--list)))
     ;; Exclude areas occupied by struts.
     (dolist (struts exwm-workspace--struts)
-      (setq edge (aref struts 0)
-            width (aref struts 1)
-            position (aref struts 2))
-      (dolist (w workareas)
-        (pcase edge
-          ;; Left and top are always processed first.
-          (`left
-           (setq delta (- (aref w 0) width))
-           (when (and (< delta 0)
-                      (or (not position)
-                          (< (max (aref position 0) (aref w 1))
-                             (min (aref position 1)
-                                  (+ (aref w 1) (aref w 3))))))
-             (cl-incf (aref w 2) delta)
-             (setf (aref w 0) width)))
-          (`right
-           (setq delta (- root-width (aref w 0) (aref w 2) width))
-           (when (and (< delta 0)
-                      (or (not position)
-                          (< (max (aref position 0) (aref w 1))
-                             (min (aref position 1)
-                                  (+ (aref w 1) (aref w 3))))))
-             (cl-incf (aref w 2) delta)))
-          (`top
-           (setq delta (- (aref w 1) width))
-           (when (and (< delta 0)
-                      (or (not position)
-                          (< (max (aref position 0) (aref w 0))
-                             (min (aref position 1)
-                                  (+ (aref w 0) (aref w 2))))))
-             (cl-incf (aref w 3) delta)
-             (setf (aref w 1) width)))
-          (`bottom
-           (setq delta (- root-height (aref w 1) (aref w 3) width))
-           (when (and (< delta 0)
-                      (or (not position)
-                          (< (max (aref position 0) (aref w 0))
-                             (min (aref position 1)
-                                  (+ (aref w 0) (aref w 2))))))
-             (cl-incf (aref w 3) delta))))))
+      (let* ((edge (aref struts 0))
+             (size (aref struts 1))
+             (position (aref struts 2))
+             (beg (and position (aref position 0)))
+             (end (and position (aref position 1)))
+             delta)
+        (dolist (w workareas)
+          (with-slots (x y width height) w
+            (pcase edge
+              ;; Left and top are always processed first.
+              ('left
+               (setq delta (- size x))
+               (when (and (< 0 delta)
+                          (< delta width)
+                          (or (not position)
+                              (< (max beg y)
+                                 (min end (+ y height)))))
+                 (cl-decf width delta)
+                 (setf x size)))
+              ('right
+               (setq delta (- size (- root-width x width)))
+               (when (and (< 0 delta)
+                          (< delta width)
+                          (or (not position)
+                              (< (max beg y)
+                                 (min end (+ y height)))))
+                 (cl-decf width delta)))
+              ('top
+               (setq delta (- size y))
+               (when (and (< 0 delta)
+                          (< delta height)
+                          (or (not position)
+                              (< (max beg x)
+                                 (min end (+ x width)))))
+                 (cl-decf height delta)
+                 (setf y size)))
+              ('bottom
+               (setq delta (- size (- root-height y height)))
+               (when (and (< 0 delta)
+                          (< delta height)
+                          (or (not position)
+                              (< (max beg x)
+                                 (min end (+ x width)))))
+                 (cl-decf height delta))))))))
     ;; Save the result.
     (setq exwm-workspace--workareas workareas)
     (xcb:flush exwm--connection))
@@ -443,8 +439,9 @@ NIL if FRAME is not a workspace"
                   exwm-workspace--window-y-offset (- (elt edges 1) y))))))))
 
 (defun exwm-workspace--set-active (frame active)
-  "Make frame FRAME active on its monitor."
-  (exwm--log "active=%s; frame=%s" frame active)
+  "Make frame FRAME active on its monitor.
+ACTIVE indicates whether to set the frame active or inactive."
+  (exwm--log "active=%s; frame=%s" active frame)
   (set-frame-parameter frame 'exwm-active active)
   (if active
       (exwm-workspace--set-fullscreen frame)
@@ -453,30 +450,25 @@ NIL if FRAME is not a workspace"
   (xcb:flush exwm--connection))
 
 (defun exwm-workspace--active-p (frame)
-  "Return non-nil if FRAME is active"
+  "Return non-nil if FRAME is active."
   (frame-parameter frame 'exwm-active))
 
 (defun exwm-workspace--set-fullscreen (frame)
   "Make frame FRAME fullscreen according to `exwm-workspace--workareas'."
   (exwm--log "frame=%s" frame)
-  (let ((workarea (elt exwm-workspace--workareas
-                       (exwm-workspace--position frame)))
-        (id (frame-parameter frame 'exwm-outer-id))
-        (container (frame-parameter frame 'exwm-container))
-        x y width height)
-    (setq x (aref workarea 0)
-          y (aref workarea 1)
-          width (aref workarea 2)
-          height (aref workarea 3))
-    (exwm--log "x=%s; y=%s; w=%s; h=%s" x y width height)
-    (when (and (eq frame exwm-workspace--current)
-               (exwm-workspace--minibuffer-own-frame-p))
-      (exwm-workspace--resize-minibuffer-frame))
-    (if (exwm-workspace--active-p frame)
-        (exwm--set-geometry container x y width height)
-      (exwm--set-geometry container x y 1 1))
-    (exwm--set-geometry id nil nil width height)
-    (xcb:flush exwm--connection))
+  (let ((id (frame-parameter frame 'exwm-outer-id))
+        (container (frame-parameter frame 'exwm-container)))
+    (with-slots (x y width height)
+        (exwm-workspace--workarea frame)
+      (exwm--log "x=%s; y=%s; w=%s; h=%s" x y width height)
+      (when (and (eq frame exwm-workspace--current)
+                 (exwm-workspace--minibuffer-own-frame-p))
+        (exwm-workspace--resize-minibuffer-frame))
+      (if (exwm-workspace--active-p frame)
+          (exwm--set-geometry container x y width height)
+        (exwm--set-geometry container x y 1 1))
+      (exwm--set-geometry id nil nil width height)
+      (xcb:flush exwm--connection)))
   ;; This is only used for workspace initialization.
   (when exwm-workspace--fullscreen-frame-count
     (cl-incf exwm-workspace--fullscreen-frame-count)))
@@ -484,20 +476,20 @@ NIL if FRAME is not a workspace"
 (defun exwm-workspace--resize-minibuffer-frame ()
   "Resize minibuffer (and its container) to fit the size of workspace."
   (cl-assert (exwm-workspace--minibuffer-own-frame-p))
-  (let ((workarea (elt exwm-workspace--workareas exwm-workspace-current-index))
+  (let ((workarea (exwm-workspace--workarea exwm-workspace-current-index))
         (container (frame-parameter exwm-workspace--minibuffer
                                     'exwm-container))
         y width)
     (setq y (if (eq exwm-workspace-minibuffer-position 'top)
-                (- (aref workarea 1)
+                (- (slot-value workarea 'y)
                    exwm-workspace--attached-minibuffer-height)
               ;; Reset the frame size.
               (set-frame-height exwm-workspace--minibuffer 1)
               (redisplay)               ;FIXME.
-              (+ (aref workarea 1) (aref workarea 3)
+              (+ (slot-value workarea 'y) (slot-value workarea 'height)
                  (- (frame-pixel-height exwm-workspace--minibuffer))
                  exwm-workspace--attached-minibuffer-height))
-          width (aref workarea 2))
+          width (slot-value workarea 'width))
     (xcb:+request exwm--connection
         (make-instance 'xcb:ConfigureWindow
                        :window container
@@ -508,7 +500,7 @@ NIL if FRAME is not a workspace"
                                                xcb:ConfigWindow:Sibling
                                              0)
                                            xcb:ConfigWindow:StackMode)
-                       :x (aref workarea 0)
+                       :x (slot-value workarea 'x)
                        :y y
                        :width width
                        :sibling exwm-manage--desktop
@@ -571,11 +563,13 @@ PREFIX-DIGITS is a list of the digits introduced so far."
 
 ;;;###autoload
 (defun exwm-workspace-switch (frame-or-index &optional force)
-  "Switch to workspace INDEX (0-based).
+  "Switch to workspace FRAME-OR-INDEX (0-based).
 
 Query for the index if not specified when called interactively.  Passing a
 workspace frame as the first option or making use of the rest options are
-for internal use only."
+for internal use only.
+
+When FORCE is true, allow switching to current workspace."
   (interactive
    (list
     (cond
@@ -701,7 +695,7 @@ for internal use only."
 
 ;;;###autoload
 (defun exwm-workspace-switch-create (frame-or-index)
-  "Switch to workspace INDEX or creating it first if it does not exist yet.
+  "Switch to workspace FRAME-OR-INDEX creating it first non-existent.
 
 Passing a workspace frame as the first option is for internal use only."
   (interactive
@@ -830,7 +824,6 @@ INDEX must not exceed the current number of workspaces."
                      (exwm-workspace--workspace-from-frame-or-index
                       frame-or-index)
                    exwm-workspace--current)))
-      (exwm-workspace--get-remove-frame-next-workspace frame)
       (delete-frame frame))))
 
 (defun exwm-workspace--set-desktop (id)
@@ -988,7 +981,7 @@ INDEX must not exceed the current number of workspaces."
 
 ;;;###autoload
 (defun exwm-workspace-switch-to-buffer (buffer-or-name)
-  "Make the current Emacs window display another buffer."
+  "Make selected window display BUFFER-OR-NAME."
   (interactive
    (let ((inhibit-quit t))
      ;; Show all buffers
@@ -1040,7 +1033,7 @@ INDEX must not exceed the current number of workspaces."
         (switch-to-buffer buffer-or-name)))))
 
 (defun exwm-workspace-rename-buffer (newname)
-  "Rename a buffer."
+  "Rename current buffer to NEWNAME."
   (let ((hidden (= ?\s (aref newname 0)))
         (basename (replace-regexp-in-string "<[0-9]+>$" "" newname))
         (counter 1)
@@ -1056,10 +1049,12 @@ INDEX must not exceed the current number of workspaces."
                  buffer-list-update-hook)))
       (rename-buffer (concat (and hidden " ") newname)))))
 
-(defun exwm-workspace--x-create-frame (orig-fun params)
-  "Set override-redirect on the frame created by `x-create-frame'."
+(defun exwm-workspace--x-create-frame (orig-x-create-frame params)
+  "Set override-redirect on the frame created by `x-create-frame'.
+ORIG-X-CREATE-FRAME is the advised function `x-create-frame'.
+PARAMS are the original arguments."
   (exwm--log)
-  (let ((frame (funcall orig-fun params)))
+  (let ((frame (funcall orig-x-create-frame params)))
     (xcb:+request exwm--connection
         (make-instance 'xcb:ChangeWindowAttributes
                        :window (string-to-number
@@ -1078,7 +1073,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 
 ;;;###autoload
 (defun exwm-workspace-attach-minibuffer ()
-  "Attach the minibuffer so that it always shows."
+  "Attach the minibuffer making it always visible."
   (interactive)
   (exwm--log)
   (when (and (exwm-workspace--minibuffer-own-frame-p)
@@ -1130,8 +1125,10 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
       (exwm-workspace-attach-minibuffer))))
 
 (defun exwm-workspace--update-minibuffer-height (&optional echo-area)
-  "Update the minibuffer frame height."
-  (unless (exwm-workspace--client-p)
+  "Update the minibuffer frame height.
+When ECHO-AREA is non-nil, take the size of the echo area into
+account when calculating the height."
+  (when (exwm--terminal-p)
     (let ((height
            (with-current-buffer
                (window-buffer (minibuffer-window exwm-workspace--minibuffer))
@@ -1153,9 +1150,9 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
       (set-frame-height exwm-workspace--minibuffer height))))
 
 (defun exwm-workspace--on-ConfigureNotify (data _synthetic)
-  "Adjust the container to fit the minibuffer frame."
-  (let ((obj (make-instance 'xcb:ConfigureNotify))
-        workarea y)
+  "Adjust the container to fit the minibuffer frame.
+DATA contains unmarshalled ConfigureNotify event data."
+  (let ((obj (make-instance 'xcb:ConfigureNotify)) y)
     (xcb:unmarshal obj data)
     (with-slots (window height) obj
       (when (eq (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id)
@@ -1175,13 +1172,13 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
         (when (/= (exwm-workspace--count) (length exwm-workspace--workareas))
           ;; There is a chance the workareas are not updated timely.
           (exwm-workspace--update-workareas))
-        (setq workarea (elt exwm-workspace--workareas
-                            exwm-workspace-current-index)
-              y (if (eq exwm-workspace-minibuffer-position 'top)
-                    (- (aref workarea 1)
-                       exwm-workspace--attached-minibuffer-height)
-                  (+ (aref workarea 1) (aref workarea 3) (- height)
-                     exwm-workspace--attached-minibuffer-height)))
+        (with-slots ((y* y) (height* height))
+            (exwm-workspace--workarea exwm-workspace-current-index)
+          (setq y (if (eq exwm-workspace-minibuffer-position 'top)
+                      (- y*
+                         exwm-workspace--attached-minibuffer-height)
+                    (+ y* height* (- height)
+                       exwm-workspace--attached-minibuffer-height))))
         (xcb:+request exwm--connection
             (make-instance 'xcb:ConfigureWindow
                            :window (frame-parameter exwm-workspace--minibuffer
@@ -1193,7 +1190,8 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
         (xcb:flush exwm--connection)))))
 
 (defun exwm-workspace--display-buffer (buffer alist)
-  "Display BUFFER as if the current workspace is selected."
+  "Display BUFFER as if the current workspace were selected.
+ALIST is an action alist, as accepted by function `display-buffer'."
   ;; Only when the floating minibuffer frame is selected.
   ;; This also protect this functions from being recursively called.
   (when (eq (selected-frame) exwm-workspace--minibuffer)
@@ -1245,10 +1243,10 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
   (xcb:flush exwm--connection))
 
 (defun exwm-workspace--on-minibuffer-setup ()
-  "Run in minibuffer-setup-hook to show the minibuffer and its container."
+  "Run in `minibuffer-setup-hook' to show the minibuffer and its container."
   (exwm--log)
   (when (and (= 1 (minibuffer-depth))
-             (not (exwm-workspace--client-p)))
+             (exwm--terminal-p))
     (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height)
     (exwm-workspace--show-minibuffer))
   ;; FIXME: This is a temporary fix for the *Completions* buffer not
@@ -1267,19 +1265,19 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
         (window-preserve-size window)))))
 
 (defun exwm-workspace--on-minibuffer-exit ()
-  "Run in minibuffer-exit-hook to hide the minibuffer container."
+  "Run in `minibuffer-exit-hook' to hide the minibuffer container."
   (exwm--log)
   (when (and (= 1 (minibuffer-depth))
-             (not (exwm-workspace--client-p)))
+             (exwm--terminal-p))
     (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height)
     (exwm-workspace--hide-minibuffer)))
 
 (defun exwm-workspace--on-echo-area-dirty ()
   "Run when new message arrives to show the echo area and its container."
   (when (and (not (active-minibuffer-window))
-             (not (exwm-workspace--client-p))
              (or (current-message)
-                 cursor-in-echo-area))
+                 cursor-in-echo-area)
+             (exwm--terminal-p))
     (exwm-workspace--update-minibuffer-height t)
     (exwm-workspace--show-minibuffer)
     (unless (or (not exwm-workspace-display-echo-area-timeout)
@@ -1301,8 +1299,8 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
                           #'exwm-workspace--echo-area-maybe-clear))))
 
 (defun exwm-workspace--on-echo-area-clear ()
-  "Run in echo-area-clear-hook to hide echo area container."
-  (unless (exwm-workspace--client-p)
+  "Run in `echo-area-clear-hook' to hide echo area container."
+  (when (exwm--terminal-p)
     (unless (active-minibuffer-window)
       (exwm-workspace--hide-minibuffer))
     (when exwm-workspace--display-echo-area-timer
@@ -1326,13 +1324,12 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
   (let ((outer-id (string-to-number (frame-parameter frame
                                                      'outer-window-id)))
         (window-id (string-to-number (frame-parameter frame 'window-id)))
-        (container (xcb:generate-id exwm--connection)))
+        (container (xcb:generate-id exwm--connection))
+        frame-colormap frame-visual frame-depth)
     ;; Save window IDs
     (set-frame-parameter frame 'exwm-outer-id outer-id)
     (set-frame-parameter frame 'exwm-id window-id)
     (set-frame-parameter frame 'exwm-container container)
-    ;; In case it's created by emacsclient.
-    (set-frame-parameter frame 'client nil)
     ;; Copy RandR frame parameters from the first workspace to
     ;; prevent potential problems.  The values do not matter here as
     ;; they'll be updated by the RandR module later.
@@ -1340,9 +1337,17 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
       (dolist (param '(exwm-randr-monitor
                        exwm-geometry))
         (set-frame-parameter frame param (frame-parameter w param))))
+    ;; Support transparency on the container X window when the Emacs frame
+    ;; does.  Note that in addition to setting the visual, colormap and depth
+    ;; we must also reset the `:border-pixmap', as its default value is
+    ;; relative to the parent window, which might have a different depth.
+    (let* ((vdc (exwm--get-visual-depth-colormap exwm--connection outer-id)))
+      (setq frame-visual (car vdc))
+      (setq frame-depth (cadr vdc))
+      (setq frame-colormap (caddr vdc)))
     (xcb:+request exwm--connection
         (make-instance 'xcb:CreateWindow
-                       :depth 0
+                       :depth frame-depth
                        :wid container
                        :parent exwm--root
                        :x -1
@@ -1351,10 +1356,14 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
                        :height 1
                        :border-width 0
                        :class xcb:WindowClass:InputOutput
-                       :visual 0
+                       :visual frame-visual
                        :value-mask (logior xcb:CW:BackPixmap
+                                           xcb:CW:BorderPixel
+                                           xcb:CW:Colormap
                                            xcb:CW:OverrideRedirect)
-                       :background-pixmap xcb:BackPixmap:ParentRelative
+                       :background-pixmap xcb:BackPixmap:None
+                       :border-pixel 0
+                       :colormap frame-colormap
                        :override-redirect 1))
     (xcb:+request exwm--connection
         (make-instance 'xcb:ConfigureWindow
@@ -1371,10 +1380,15 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
         (make-instance 'xcb:ReparentWindow
                        :window outer-id :parent container :x 0 :y 0))
     (xcb:+request exwm--connection
+        (make-instance 'xcb:icccm:set-WM_STATE
+                       :window outer-id
+                       :state xcb:icccm:WM_STATE:NormalState
+                       :icon xcb:Window:None))
+    (xcb:+request exwm--connection
         (make-instance 'xcb:MapWindow :window container)))
   (xcb:flush exwm--connection)
   ;; Delay making the workspace fullscreen until Emacs becomes idle
-  (exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth)
+  (exwm--defer 0 #'exwm-workspace--fullscreen-workspace frame)
   ;; Update EWMH properties.
   (exwm-workspace--update-ewmh-props)
   (if exwm-workspace--create-silently
@@ -1385,41 +1399,44 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
                frame exwm-workspace-current-index original-index))
     (run-hooks 'exwm-workspace-list-change-hook)))
 
-(defun exwm-workspace--get-remove-frame-next-workspace (frame)
-  "Return the next workspace if workspace FRAME is removed.
-
-All X windows currently on workspace FRAME will be automatically moved to
-the next workspace."
+(defun exwm-workspace--get-next-workspace (frame)
+  "Return the next workspace if workspace FRAME were removed.
+Return nil if FRAME is the only workspace."
   (let* ((index (exwm-workspace--position frame))
          (lastp (= index (1- (exwm-workspace--count))))
          (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1)))))
-    ;; Clients need to be moved to some other workspace before this being
-    ;; removed.
-    (dolist (pair exwm--id-buffer-alist)
-      (with-current-buffer (cdr pair)
-        (when (eq exwm--frame frame)
-          (exwm-workspace-move-window nextw exwm--id))))
-    nextw))
-
-(defun exwm-workspace--remove-frame-as-workspace (frame)
-  "Stop treating frame FRAME as a workspace."
+    (unless (eq frame nextw)
+      nextw)))
+
+(defun exwm-workspace--remove-frame-as-workspace (frame &optional quit)
+  "Stop treating FRAME as a workspace.
+When QUIT is non-nil cleanup avoid communicating with the X server."
   ;; TODO: restore all frame parameters (e.g. exwm-workspace, buffer-predicate,
   ;; etc)
   (exwm--log "Removing frame `%s' as workspace" frame)
-  (let* ((index (exwm-workspace--position frame))
-         (nextw (exwm-workspace--get-remove-frame-next-workspace frame)))
-    ;; Need to remove the workspace from the list in order for
-    ;; the correct calculation of indexes.
-    (setq exwm-workspace--list (delete frame exwm-workspace--list))
-    ;; Update the _NET_WM_DESKTOP property of each X window affected.
-    (dolist (pair exwm--id-buffer-alist)
-      (when (<= (1- index)
-                (exwm-workspace--position (buffer-local-value 'exwm--frame
-                                                              (cdr pair))))
-        (exwm-workspace--set-desktop (car pair))))
-    ;; If the current workspace is deleted, switch to next one.
-    (when (eq frame exwm-workspace--current)
-      (exwm-workspace-switch nextw)))
+  (unless quit
+    (let* ((next-frame (exwm-workspace--get-next-workspace frame))
+           (following-frames (cdr (memq frame exwm-workspace--list))))
+      ;; Need to remove the workspace from the list for the correct calculation of
+      ;; indexes below.
+      (setq exwm-workspace--list (delete frame exwm-workspace--list))
+      ;; Move the windows to the next workspace and switch to it.
+      (unless next-frame
+        ;; The user managed to delete the last workspace, so create a new one.
+        (exwm--log "Last workspace deleted; create a new one")
+        (let ((exwm-workspace--create-silently t))
+          (setq next-frame (make-frame))))
+      (dolist (pair exwm--id-buffer-alist)
+        (let ((other-frame (buffer-local-value 'exwm--frame (cdr pair))))
+          ;; Move X windows to next-frame.
+          (when (eq other-frame frame)
+            (exwm-workspace-move-window next-frame (car pair)))
+          ;; Update the _NET_WM_DESKTOP property of each following X window.
+          (when (memq other-frame following-frames)
+            (exwm-workspace--set-desktop (car pair)))))
+      ;; If the current workspace is deleted, switch to next one.
+      (when (eq frame exwm-workspace--current)
+        (exwm-workspace-switch next-frame))))
   ;; Reparent out the frame.
   (let ((outer-id (frame-parameter frame 'exwm-outer-id)))
     (xcb:+request exwm--connection
@@ -1453,24 +1470,23 @@ the next workspace."
   ;; Update EWMH properties.
   (exwm-workspace--update-ewmh-props)
   ;; Update switch history.
-  (setq exwm-workspace--switch-history-outdated t)
-  (run-hooks 'exwm-workspace-list-change-hook))
+  (unless quit
+    (setq exwm-workspace--switch-history-outdated t)
+    (run-hooks 'exwm-workspace-list-change-hook)))
 
 (defun exwm-workspace--on-delete-frame (frame)
-  "Hook run upon `delete-frame' that tears down FRAME's configuration as a workspace."
+  "Hook run upon `delete-frame' removing FRAME as a workspace."
   (cond
    ((not (exwm-workspace--workspace-p frame))
     (exwm--log "Frame `%s' is not a workspace" frame))
    (t
-    (when (= 1 (exwm-workspace--count))
-      ;; The user managed to delete the last workspace, so create a new one.
-      (exwm--log "Last workspace deleted; create a new one")
-      ;; TODO: this makes sense in the hook.  But we need a function that takes
-      ;; care of converting a workspace into a regular unmanaged frame.
-      (let ((exwm-workspace--create-silently t))
-        (make-frame)))
-    (exwm-workspace--remove-frame-as-workspace frame)
-    (remhash frame exwm-workspace--client-p-hash-table))))
+    (exwm-workspace--remove-frame-as-workspace frame))))
+
+(defun exwm-workspace--fullscreen-workspace (frame)
+  "Make workspace FRAME fullscreen.
+Called from a timer."
+  (when (frame-live-p frame)
+    (set-frame-parameter frame 'fullscreen 'fullboth)))
 
 (defun exwm-workspace--on-after-make-frame (frame)
   "Hook run upon `make-frame' that configures FRAME as a workspace."
@@ -1479,6 +1495,11 @@ the next workspace."
     (exwm--log "Frame `%s' is already a workspace" frame))
    ((not (display-graphic-p frame))
     (exwm--log "Frame `%s' is not graphical" frame))
+   ((not (eq (frame-terminal) exwm--terminal))
+    (exwm--log "Frame `%s' is on a different terminal (%S instead of %S)"
+               frame
+               (frame-terminal frame)
+               exwm--terminal))
    ((not (string-equal
           (replace-regexp-in-string "\\.0$" ""
                                     (slot-value exwm--connection 'display))
@@ -1539,13 +1560,13 @@ applied to all subsequently created X frames."
   (interactive "e"))
 
 (defun exwm-workspace--init-minibuffer-frame ()
+  "Initialize minibuffer-only frame."
   (exwm--log)
   ;; Initialize workspaces without minibuffers.
   (setq exwm-workspace--minibuffer
         (make-frame '((window-system . x) (minibuffer . only)
                       (left . 10000) (right . 10000)
-                      (width . 1) (height . 1)
-                      (client . nil))))
+                      (width . 1) (height . 1))))
   ;; This is the only usable minibuffer frame.
   (setq default-minibuffer-frame exwm-workspace--minibuffer)
   (exwm-workspace--modify-all-x-frames-parameters
@@ -1610,11 +1631,14 @@ applied to all subsequently created X frames."
               :test #'equal))
 
 (defun exwm-workspace--exit-minibuffer-frame ()
+  "Cleanup minibuffer-only frame."
   (exwm--log)
   ;; Only on minibuffer-frame.
   (remove-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup)
   (remove-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit)
   (remove-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear)
+  (when exwm-workspace--display-echo-area-timer
+    (cancel-timer exwm-workspace--display-echo-area-timer))
   (when exwm-workspace--timer
     (cancel-timer exwm-workspace--timer)
     (setq exwm-workspace--timer nil))
@@ -1622,15 +1646,18 @@ applied to all subsequently created X frames."
         (cl-delete '(exwm-workspace--display-buffer) display-buffer-alist
                    :test #'equal))
   (setq default-minibuffer-frame nil)
-  (let ((id (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id)))
-    (when (and exwm-workspace--minibuffer id)
-      (xcb:+request exwm--connection
-          (make-instance 'xcb:ReparentWindow
-                         :window id
-                         :parent exwm--root
-                         :x 0
-                         :y 0)))
-    (setq exwm-workspace--minibuffer nil)))
+  (when (frame-live-p exwm-workspace--minibuffer) ; might be already dead
+    (let ((id (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id)))
+      (when (and exwm-workspace--minibuffer id
+                 ;; Invoked from `exwm-manage--exit' upon disconnection.
+                 (slot-value exwm--connection 'connected))
+        (xcb:+request exwm--connection
+            (make-instance 'xcb:ReparentWindow
+                           :window id
+                           :parent exwm--root
+                           :x 0
+                           :y 0)))
+      (setq exwm-workspace--minibuffer nil))))
 
 (defun exwm-workspace--init ()
   "Initialize workspace module."
@@ -1648,33 +1675,22 @@ applied to all subsequently created X frames."
           (dolist (i initial-workspaces)
             (unless (frame-parameter i 'window-id)
               (setq initial-workspaces (delq i initial-workspaces))))
-          (setq exwm-workspace--client
-                (frame-parameter (car initial-workspaces) 'client))
           (let ((f (car initial-workspaces)))
             ;; Remove the possible internal border.
-            (set-frame-parameter f 'internal-border-width 0)
-            ;; Prevent user from deleting the first frame by accident.
-            (set-frame-parameter f 'client nil)))
+            (set-frame-parameter f 'internal-border-width 0)))
       (exwm-workspace--init-minibuffer-frame)
       ;; Remove/hide existing frames.
       (dolist (f initial-workspaces)
-        (if (frame-parameter f 'client)
-            (progn
-              (unless exwm-workspace--client
-                (setq exwm-workspace--client (frame-parameter f 'client)))
-              (make-frame-invisible f))
-          (when (eq 'x (framep f))   ;do not delete the initial frame.
-            (delete-frame f))))
+        (when (eq 'x (framep f))        ;do not delete the initial frame.
+          (delete-frame f)))
       ;; Recreate one frame with the external minibuffer set.
-      (setq initial-workspaces (list (make-frame '((window-system . x)
-                                                   (client . nil))))))
+      (setq initial-workspaces (list (make-frame '((window-system . x))))))
     ;; Prevent `other-buffer' from selecting already displayed EXWM buffers.
     (modify-all-frames-parameters
      '((buffer-predicate . exwm-layout--other-buffer-predicate)))
     ;; Create remaining workspaces.
     (dotimes (_ (- exwm-workspace-number (length initial-workspaces)))
-      (nconc initial-workspaces (list (make-frame '((window-system . x)
-                                                    (client . nil))))))
+      (nconc initial-workspaces (list (make-frame '((window-system . x))))))
     ;; Configure workspaces
     (let ((exwm-workspace--create-silently t))
       (dolist (i initial-workspaces)
@@ -1719,36 +1735,26 @@ applied to all subsequently created X frames."
                  #'exwm-workspace--on-echo-area-clear))
   ;; Hide & reparent out all frames (save-set can't be used here since
   ;; X windows will be re-mapped).
+  (when (slot-value exwm--connection 'connected)
+    (dolist (i exwm-workspace--list)
+      (when (frame-live-p i)                    ; might be already dead
+        (exwm-workspace--remove-frame-as-workspace i 'quit)
+        (modify-frame-parameters i '((exwm-selected-window . nil)
+                                     (exwm-urgency . nil)
+                                     (exwm-outer-id . nil)
+                                     (exwm-id . nil)
+                                     (exwm-container . nil)
+                                     ;; (internal-border-width . nil) ; integerp
+                                     (fullscreen . nil)
+                                     (buffer-predicate . nil))))))
+  ;; Don't let dead frames linger.
   (setq exwm-workspace--current nil)
-  (dolist (i exwm-workspace--list)
-    (exwm-workspace--remove-frame-as-workspace i)
-    (modify-frame-parameters i '((exwm-selected-window . nil)
-                                 (exwm-urgency . nil)
-                                 (exwm-outer-id . nil)
-                                 (exwm-id . nil)
-                                 (exwm-container . nil)
-                                 ;; (internal-border-width . nil) ; integerp
-                                 ;; (client . nil)
-                                 (fullscreen . nil)
-                                 (buffer-predicate . nil))))
-  ;; Restore the 'client' frame parameter (before `exwm-exit').
-  (when exwm-workspace--client
-    (dolist (f exwm-workspace--list)
-      (set-frame-parameter f 'client exwm-workspace--client))
-    (when (exwm-workspace--minibuffer-own-frame-p)
-      (set-frame-parameter exwm-workspace--minibuffer 'client
-                           exwm-workspace--client))
-    (setq exwm-workspace--client nil)))
+  (setq exwm-workspace-current-index 0)
+  (setq exwm-workspace--list nil))
 
 (defun exwm-workspace--post-init ()
   "The second stage in the initialization of the workspace module."
   (exwm--log)
-  (when exwm-workspace--client
-    ;; Reset the 'fullscreen' frame parameter to make emacsclinet frames
-    ;; fullscreen (even without the RandR module enabled).
-    (dolist (i exwm-workspace--list)
-      (set-frame-parameter i 'fullscreen nil)
-      (set-frame-parameter i 'fullscreen 'fullboth)))
   ;; Wait until all workspace frames are resized.
   (with-timeout (1)
     (while (< exwm-workspace--fullscreen-frame-count (exwm-workspace--count))
diff --git a/third_party/exwm/exwm-xim.el b/third_party/exwm/exwm-xim.el
index 9589648d22..1f0c9c460b 100644
--- a/third_party/exwm/exwm-xim.el
+++ b/third_party/exwm/exwm-xim.el
@@ -1,6 +1,6 @@
 ;;; exwm-xim.el --- XIM Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2019-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2019-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -68,7 +68,7 @@
 
 ;;; Code:
 
-(eval-when-compile (require 'cl-lib))
+(require 'cl-lib)
 
 (require 'xcb-keysyms)
 (require 'xcb-xim)
@@ -167,6 +167,7 @@ C,no"
 
 (defun exwm-xim--on-SelectionRequest (data _synthetic)
   "Handle SelectionRequest events on IMS window.
+DATA contains unmarshalled SelectionRequest event data.
 
 Such events would be received when clients query for LOCALES or TRANSPORT."
   (exwm--log)
@@ -754,10 +755,12 @@ Such event would be received when the client window is destroyed."
   ;; Close IMS communication connections.
   (mapc (lambda (i)
           (when (vectorp i)
-            (xcb:disconnect (elt i 0))))
+            (when (slot-value (elt i 0) 'connected)
+              (xcb:disconnect (elt i 0)))))
         exwm-xim--server-client-plist)
   ;; Close the IMS connection.
-  (unless exwm-xim--conn
+  (unless (and exwm-xim--conn
+               (slot-value exwm-xim--conn 'connected))
     (cl-return-from exwm-xim--exit))
   ;; Remove exwm-xim from XIM_SERVERS.
   (let ((reply (xcb:+request-unchecked+reply exwm-xim--conn
diff --git a/third_party/exwm/exwm-xsettings.el b/third_party/exwm/exwm-xsettings.el
new file mode 100644
index 0000000000..99d6b9c4ac
--- /dev/null
+++ b/third_party/exwm/exwm-xsettings.el
@@ -0,0 +1,336 @@
+;;; exwm-xsettings.el --- XSETTINGS Module for EXWM -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022-2024 Free Software Foundation, Inc.
+
+;; Author: Steven Allen <steven@stebalien.com>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Implements the XSETTINGS protocol, allowing Emacs to manage the system theme,
+;; fonts, icons, etc.
+;;
+;; This package can be configured as follows:
+;;
+;;   (require 'exwm-xsettings)
+;;   (setq exwm-xsettings-theme '("Adwaita" . "Adwaita-dark") ;; light/dark
+;;         exwm-xsettings `(("Xft/HintStyle" . "hintslight")
+;;                          ("Xft/RGBA" . "rgb")
+;;                          ("Xft/lcdfilter" . "lcddefault")
+;;                          ("Xft/Antialias" . 1)
+;;                          ;; DPI is in 1024ths of an inch, so this is a DPI of
+;;                          ;; 144, equivalent to ;; a scaling factor of 1.5
+;;                          ;; (144 = 1.5 * 96).
+;;                          ("Xft/DPI" . ,(* 144 1024))
+;;                          ("Xft/Hinting" . 1)))
+;;   (exwm-xsettings-enable)
+;;
+;; To modify these settings at runtime, customize them with
+;; `custom-set-variables' or `setopt' (Emacs 29+).  E.g., the following will
+;; immediately change the icon theme to "Papirus" at runtime, even in running
+;; applications:
+;;
+;;   (setopt exwm-xsettings-icon-theme "Papirus")
+
+;;; Code:
+
+(require 'xcb-ewmh)
+(require 'xcb-xsettings)
+(require 'exwm-core)
+
+(defvar exwm-xsettings--connection nil)
+(defvar exwm-xsettings--XSETTINGS_SETTINGS-atom nil)
+(defvar exwm-xsettings--XSETTINGS_S0-atom nil)
+(defvar exwm-xsettings--selection-owner-window nil)
+(defvar exwm-xsettings--serial 0)
+
+(defun exwm-xsettings--rgba-match (_widget value)
+  "Return t if VALUE is a valid RGBA color."
+  (and (numberp value) (<= 0 value 1)))
+
+(defun exwm-xsettings--custom-set (symbol value)
+  "Setter used by `exwm-xsettings' customization options.
+
+SYMBOL is the setting being updated and VALUE is the new value."
+  (set-default-toplevel-value symbol value)
+  (exwm-xsettings--update-settings))
+
+(defgroup exwm-xsettings nil
+  "XSETTINGS."
+  :group 'exwm)
+
+(defcustom exwm-xsettings nil
+  "Alist of custom XSETTINGS.
+These settings take precedence over `exwm-xsettings-theme' and
+`exwm-xsettings-icon-theme'."
+  :type '(alist :key-type (string :tag "Name")
+                :value-type (choice :tag "Value"
+                              (string :tag "String")
+                              (integer :tag "Integer")
+                              (list :tag "Color"
+                                (number :tag "Red"
+                                        :type-error
+                                        "This field should contain a number between 0 and 1."
+                                       :match exwm-xsettings--rgba-match)
+                                (number :tag "Green"
+                                        :type-error
+                                        "This field should contain a number between 0 and 1."
+                                       :match exwm-xsettings--rgba-match)
+                                (number :tag "Blue"
+                                        :type-error
+                                        "This field should contain a number between 0 and 1."
+                                       :match exwm-xsettings--rgba-match)
+                                (number :tag "Alpha"
+                                        :type-error
+                                        "This field should contain a number between 0 and 1."
+                                       :match exwm-xsettings--rgba-match
+                                       :value 1.0))))
+  :initialize #'custom-initialize-default
+  :set #'exwm-xsettings--custom-set)
+
+(defcustom exwm-xsettings-theme nil
+  "The system-wide theme."
+  :type '(choice (string :tag "Theme")
+                 (cons (string :tag "Light Theme")
+                       (string :tag "Dark Theme")))
+  :initialize #'custom-initialize-default
+  :set #'exwm-xsettings--custom-set)
+
+(defcustom exwm-xsettings-icon-theme nil
+  "The system-wide icon theme."
+  :type '(choice (string :tag "Icon Theme")
+                 (cons (string :tag "Light Icon Theme")
+                       (string :tag "Dark Icon Theme")))
+  :initialize #'custom-initialize-default
+  :set #'exwm-xsettings--custom-set)
+
+(defalias 'exwm-xsettings--color-dark-p
+  (if (eval-when-compile (< emacs-major-version 29))
+      ;; Borrowed from Emacs 29.
+      (lambda (rgb)
+        "Whether RGB is more readable against white than black."
+        (unless (<= 0 (apply #'min rgb) (apply #'max rgb) 1)
+          (error "RGB components %S not in [0,1]" rgb))
+        (let* ((r (expt (nth 0 rgb) 2.2))
+               (g (expt (nth 1 rgb) 2.2))
+               (b (expt (nth 2 rgb) 2.2))
+               (y (+ (* r 0.2126) (* g 0.7152) (* b 0.0722))))
+          (< y 0.325)))
+    'color-dark-p))
+
+(defun exwm-xsettings--pick-theme (theme)
+  "Pick a light or dark theme from the given THEME.
+If THEME is a string, it's returned directly.
+If THEME is a cons of (LIGHT . DARK), the appropriate theme is picked based on
+the default face's background color."
+  (pcase theme
+    ((cl-type string) theme)
+    (`(,(cl-type string) . ,(cl-type string))
+     (if (exwm-xsettings--color-dark-p (color-name-to-rgb (face-background 'default)))
+         (cdr theme) (car theme)))
+    (_ (error "Expected theme to be a string or a pair of strings"))))
+
+(defun exwm-xsettings--get-settings ()
+  "Get the current settings.
+Combines `exwm-xsettings', `exwm-xsettings-theme' (if set), and
+`exwm-xsettings-icon-theme' (if set)."
+  (cl-remove-duplicates
+   (append
+    exwm-xsettings
+    (when exwm-xsettings-theme
+      (list (cons "Net/ThemeName" (exwm-xsettings--pick-theme exwm-xsettings-theme))))
+    (when exwm-xsettings-icon-theme
+      (list (cons "Net/IconThemeName" (exwm-xsettings--pick-theme exwm-xsettings-icon-theme)))))
+   :key 'car
+   :test 'string=))
+
+(defun exwm-xsettings--make-settings (settings serial)
+  "Construct a new settings object.
+SETTINGS is an alist of key/value pairs.
+SERIAL is a sequence number."
+  (make-instance 'xcb:xsettings:-Settings
+                 :byte-order (if xcb:lsb 0 1)
+                 :serial serial
+                 :settings-len (length settings)
+                 :settings
+                 (mapcar
+                  (lambda (prop)
+                    (let* ((name (car prop))
+                           (value (cdr prop))
+                           (common (list :name name
+                                         :name-len (length name)
+                                         :last-change-serial serial)))
+                      (pcase value
+                        ((cl-type string)
+                         (apply #'make-instance 'xcb:xsettings:-SETTING_STRING
+                                :value-len (length value)
+                                :value value
+                                common))
+                        ((cl-type integer)
+                         (apply #'make-instance 'xcb:xsettings:-SETTING_INTEGER
+                                :value value common))
+                        ((and (cl-type list) (app length (or 3 4)))
+                         ;; Convert from RGB(A) to 16bit integers.
+                         (setq value (mapcar (lambda (x) (round (* x #xffff))) value))
+                         (apply #'make-instance 'xcb:xsettings:-SETTING_COLOR
+                                :red (pop value)
+                                :green (pop value)
+                                :blue (pop value)
+                                :alpha (or (pop value) #xffff)))
+                        (_ (error "Setting value must be a string, integer, or length 3-4 list")))))
+                  settings)))
+
+(defun exwm-xsettings--update-settings ()
+  "Update the xsettings."
+  (when exwm-xsettings--connection
+    (setq exwm-xsettings--serial (1+ exwm-xsettings--serial))
+    (let* ((settings (exwm-xsettings--get-settings))
+           (bytes (xcb:marshal (exwm-xsettings--make-settings settings exwm-xsettings--serial))))
+      (xcb:+request exwm-xsettings--connection
+          (make-instance 'xcb:ChangeProperty
+                         :mode xcb:PropMode:Replace
+                         :window exwm-xsettings--selection-owner-window
+                         :property exwm-xsettings--XSETTINGS_SETTINGS-atom
+                         :type exwm-xsettings--XSETTINGS_SETTINGS-atom
+                         :format 8
+                         :data-len (length bytes)
+                         :data bytes)))
+    (xcb:flush exwm-xsettings--connection)))
+
+(defun exwm-xsettings--on-theme-change (&rest _)
+  "Called when the Emacs theme is changed."
+  ;; We only bother updating the xsettings if changing the theme could effect
+  ;; the settings.
+  (when (or (consp exwm-xsettings-theme) (consp exwm-xsettings-icon-theme))
+    (exwm-xsettings--update-settings)))
+
+(defun exwm-xsettings--on-SelectionClear (_data _synthetic)
+  "Called when another xsettings daemon takes over."
+  (exwm--log "XSETTINGS manager has been replaced.")
+  (exwm-xsettings--exit))
+
+(cl-defun exwm-xsettings--init ()
+  "Initialize the XSETTINGS module."
+  (exwm--log)
+
+  (cl-assert (not exwm-xsettings--connection))
+
+  ;; Connect
+  (setq exwm-xsettings--connection (xcb:connect))
+  (set-process-query-on-exit-flag (slot-value exwm-xsettings--connection
+                                              'process)
+                                  nil)
+
+  ;; Intern the atoms.
+  (setq exwm-xsettings--XSETTINGS_SETTINGS-atom
+        (exwm--intern-atom "_XSETTINGS_SETTINGS" exwm-xsettings--connection)
+
+        exwm-xsettings--XSETTINGS_S0-atom
+        (exwm--intern-atom "_XSETTINGS_S0" exwm-xsettings--connection))
+
+  ;; Detect running XSETTINGS managers.
+  (with-slots (owner)
+      (xcb:+request-unchecked+reply exwm-xsettings--connection
+          (make-instance 'xcb:GetSelectionOwner
+                         :selection exwm-xsettings--XSETTINGS_S0-atom))
+    (when (/= owner xcb:Window:None)
+      (xcb:disconnect exwm-xsettings--connection)
+      (setq exwm-xsettings--connection nil)
+      (warn "[EXWM] Other XSETTINGS manager detected")
+      (cl-return-from exwm-xsettings--init)))
+
+  (let ((id(xcb:generate-id exwm-xsettings--connection)))
+    (setq exwm-xsettings--selection-owner-window id)
+
+    ;; Create a settings window.
+    (xcb:+request exwm-xsettings--connection
+        (make-instance 'xcb:CreateWindow
+                       :wid id
+                       :parent exwm--root
+                       :class xcb:WindowClass:InputOnly
+                       :x 0
+                       :y 0
+                       :width 1
+                       :height 1
+                       :border-width 0
+                       :depth 0
+                       :visual 0
+                       :value-mask xcb:CW:OverrideRedirect
+                       :override-redirect 1))
+
+    ;; Set _NET_WM_NAME.
+    (xcb:+request exwm-xsettings--connection
+        (make-instance 'xcb:ewmh:set-_NET_WM_NAME
+                       :window id
+                       :data "EXWM: exwm-xsettings--selection-owner-window"))
+
+    ;; Apply the XSETTINGS properties.
+    (exwm-xsettings--update-settings)
+
+    ;; Take ownership and notify.
+    (xcb:+request exwm-xsettings--connection
+        (make-instance 'xcb:SetSelectionOwner
+                       :owner id
+                       :selection exwm-xsettings--XSETTINGS_S0-atom
+                       :time xcb:Time:CurrentTime))
+    (xcb:+request exwm-xsettings--connection
+        (make-instance 'xcb:SendEvent
+                       :propagate 0
+                       :destination exwm--root
+                       :event-mask xcb:EventMask:StructureNotify
+                       :event (xcb:marshal
+                               (make-instance 'xcb:xsettings:-ClientMessage
+                                              :window exwm--root
+                                              :time xcb:Time:CurrentTime
+                                              :selection exwm-xsettings--XSETTINGS_S0-atom
+                                              :owner id)
+                               exwm-xsettings--connection)))
+
+    ;; Detect loss of XSETTINGS ownership.
+    (xcb:+event exwm-xsettings--connection 'xcb:SelectionClear
+                #'exwm-xsettings--on-SelectionClear)
+
+    (xcb:flush exwm-xsettings--connection))
+
+  ;; Update the xsettings if/when the theme changes.
+  (add-hook 'enable-theme-functions #'exwm-xsettings--on-theme-change)
+  (add-hook 'disable-theme-functions #'exwm-xsettings--on-theme-change))
+
+(defun exwm-xsettings--exit ()
+  "Exit the XSETTINGS module."
+  (exwm--log)
+
+  (when exwm-xsettings--connection
+    (remove-hook 'enable-theme-functions #'exwm-xsettings--on-theme-change)
+    (remove-hook 'disable-theme-functions #'exwm-xsettings--on-theme-change)
+
+    (xcb:disconnect exwm-xsettings--connection)
+
+    (setq exwm-xsettings--connection nil
+          exwm-xsettings--XSETTINGS_SETTINGS-atom nil
+          exwm-xsettings--XSETTINGS_S0-atom nil
+          exwm-xsettings--selection-owner-window nil)))
+
+(defun exwm-xsettings-enable ()
+  "Enable xsettings support for EXWM."
+  (exwm--log)
+  (add-hook 'exwm-init-hook #'exwm-xsettings--init)
+  (add-hook 'exwm-exit-hook #'exwm-xsettings--exit))
+
+(provide 'exwm-xsettings)
+
+;;; exwm-xsettings.el ends here
diff --git a/third_party/exwm/exwm.el b/third_party/exwm/exwm.el
index b025f6b49a..c4900eab48 100644
--- a/third_party/exwm/exwm.el
+++ b/third_party/exwm/exwm.el
@@ -1,13 +1,13 @@
 ;;; exwm.el --- Emacs X Window Manager  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2021 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
-;; Maintainer: AdriΓ‘n MedraΓ±o Calvo <adrian@medranocalvo.com>
-;; Version: 0.26
-;; Package-Requires: ((xelb "0.18"))
+;; Maintainer: AdriΓ‘n MedraΓ±o Calvo <adrian@medranocalvo.com>, Steven Allen <steven@stebalien.com>, Daniel Mendler <mail@daniel-mendler.de>
+;; Version: 0.28
+;; Package-Requires: ((emacs "27.1") (xelb "0.18"))
 ;; Keywords: unix
-;; URL: https://github.com/ch11ng/exwm
+;; URL: https://github.com/emacs-exwm/exwm
 
 ;; This file is part of GNU Emacs.
 
@@ -29,14 +29,18 @@
 ;; Overview
 ;; --------
 ;; EXWM (Emacs X Window Manager) is a full-featured tiling X window manager
-;; for Emacs built on top of [XELB](https://github.com/ch11ng/xelb).
+;; for Emacs built on top of [XELB](https://github.com/emacs-exwm/xelb).
 ;; It features:
 ;; + Fully keyboard-driven operations
 ;; + Hybrid layout modes (tiling & stacking)
 ;; + Dynamic workspace support
 ;; + ICCCM/EWMH compliance
-;; + (Optional) RandR (multi-monitor) support
-;; + (Optional) Built-in system tray
+;; Optional features:
+;; + RandR (multi-monitor) support
+;; + System tray
+;; + Input method
+;; + Background setting support
+;; + XSETTINGS server
 
 ;; Installation & configuration
 ;; ----------------------------
@@ -54,7 +58,7 @@
 ;;    xinit -- vt01
 ;;
 ;; You should additionally hide the menu-bar, tool-bar, etc to increase the
-;; usable space.  Please check the wiki (https://github.com/ch11ng/exwm/wiki)
+;; usable space.  Please check the wiki (https://github.com/emacs-exwm/exwm/wiki)
 ;; for more detailed instructions on installation, configuration, usage, etc.
 
 ;; References:
@@ -72,10 +76,11 @@
 (require 'exwm-manage)
 (require 'exwm-input)
 
+(declare-function x-get-atom-name "C source code" (VALUE &optional FRAME))
+
 (defgroup exwm nil
   "Emacs X Window Manager."
   :tag "EXWM"
-  :version "25.3"
   :group 'applications
   :prefix "exwm-")
 
@@ -95,7 +100,10 @@
   "Normal hook run when window title is updated."
   :type 'hook)
 
-(defcustom exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font)
+(defcustom exwm-blocking-subrs
+  ;; `x-file-dialog' and `x-select-font' are missing on some Emacs builds, for
+  ;; example on the X11 Lucid build.
+  '(x-file-dialog x-popup-dialog x-select-font message-box message-or-box)
   "Subrs (primitives) that would normally block EXWM."
   :type '(repeat function))
 
@@ -108,6 +116,10 @@
 (defconst exwm--server-name "server-exwm"
   "Name of the subordinate Emacs server.")
 
+(defvar exwm--server-timeout 1
+  "Number of seconds to wait for the subordinate Emacs server to exit.
+After this time, the server will be killed.")
+
 (defvar exwm--server-process nil "Process of the subordinate Emacs server.")
 
 (defun exwm-reset ()
@@ -127,7 +139,7 @@
   "Restart EXWM."
   (interactive)
   (exwm--log)
-  (when (exwm--confirm-kill-emacs "[EXWM] Restart? " 'no-check)
+  (when (exwm--confirm-kill-emacs "Restart?" 'no-check)
     (let* ((attr (process-attributes (emacs-pid)))
            (args (cdr (assq 'args attr)))
            (ppid (cdr (assq 'ppid attr)))
@@ -153,7 +165,8 @@
         (kill-emacs))))))
 
 (defun exwm--update-desktop (xwin)
-  "Update _NET_WM_DESKTOP."
+  "Update _NET_WM_DESKTOP.
+Argument XWIN contains the X window of the `exwm-mode' buffer."
   (exwm--log "#x%x" xwin)
   (with-current-buffer (exwm--id->buffer xwin)
     (let ((reply (xcb:+request-unchecked+reply exwm--connection
@@ -163,7 +176,7 @@
       (when reply
         (setq desktop (slot-value reply 'value))
         (cond
-         ((eq desktop 4294967295.)
+         ((and desktop (= desktop 4294967295.))
           (unless (or (not exwm--floating-frame)
                       (eq exwm--frame exwm-workspace--current)
                       (and exwm--desktop
@@ -180,7 +193,11 @@
           (exwm-workspace--set-desktop xwin)))))))
 
 (defun exwm--update-window-type (id &optional force)
-  "Update _NET_WM_WINDOW_TYPE."
+  "Update `exwm-window-type' from _NET_WM_WINDOW_TYPE.
+Argument ID contains the X window of the `exwm-mode' buffer.
+
+When FORCE is nil the update only takes place if
+`exwm-window-type' is unset."
   (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and exwm-window-type (not force))
@@ -191,7 +208,11 @@
           (setq exwm-window-type (append (slot-value reply 'value) nil)))))))
 
 (defun exwm--update-class (id &optional force)
-  "Update WM_CLASS."
+  "Update `exwm-instance-name' and `exwm-class' from WM_CLASS.
+Argument ID contains the X window of the `exwm-mode' buffer.
+
+When FORCE is nil the update only takes place if any of
+`exwm-instance-name' or `exwm-class' is unset."
   (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and exwm-instance-name exwm-class-name (not force))
@@ -204,7 +225,11 @@
             (run-hooks 'exwm-update-class-hook)))))))
 
 (defun exwm--update-utf8-title (id &optional force)
-  "Update _NET_WM_NAME."
+  "Update `exwm-title' from _NET_WM_NAME.
+Argument ID contains the X window of the `exwm-mode' buffer.
+
+When FORCE is nil the update only takes place if `exwm-title' is
+unset."
   (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (when (or force (not exwm-title))
@@ -217,7 +242,11 @@
             (run-hooks 'exwm-update-title-hook)))))))
 
 (defun exwm--update-ctext-title (id &optional force)
-  "Update WM_NAME."
+  "Update `exwm-title' from WM_NAME.
+Argument ID contains the X window of the `exwm-mode' buffer.
+
+When FORCE is nil the update only takes place if `exwm-title' is
+unset."
   (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (or exwm--title-is-utf8
@@ -230,13 +259,18 @@
             (run-hooks 'exwm-update-title-hook)))))))
 
 (defun exwm--update-title (id)
-  "Update _NET_WM_NAME or WM_NAME."
+  "Update _NET_WM_NAME or WM_NAME.
+Argument ID contains the X window of the `exwm-mode' buffer."
   (exwm--log "#x%x" id)
   (exwm--update-utf8-title id)
   (exwm--update-ctext-title id))
 
 (defun exwm--update-transient-for (id &optional force)
-  "Update WM_TRANSIENT_FOR."
+  "Update `exwm-transient-for' from WM_TRANSIENT_FOR.
+Argument ID contains the X window of the `exwm-mode' buffer.
+
+When FORCE is nil the update only takes place if `exwm-title' is
+unset."
   (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and exwm-transient-for (not force))
@@ -247,7 +281,15 @@
           (setq exwm-transient-for (slot-value reply 'value)))))))
 
 (defun exwm--update-normal-hints (id &optional force)
-  "Update WM_NORMAL_HINTS."
+  "Update normal hints from WM_NORMAL_HINTS.
+Argument ID contains the X window of the `exwm-mode' buffer.
+
+When FORCE is nil the update only takes place all of
+`exwm--normal-hints-x exwm--normal-hints-y',
+`exwm--normal-hints-width exwm--normal-hints-height',
+`exwm--normal-hints-min-width exwm--normal-hints-min-height' and
+`exwm--normal-hints-max-width exwm--normal-hints-max-height' are
+unset."
   (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and (not force)
@@ -295,7 +337,11 @@
                           exwm--normal-hints-max-height)))))))))
 
 (defun exwm--update-hints (id &optional force)
-  "Update WM_HINTS."
+  "Update hints from WM_HINTS.
+Argument ID contains the X window of the `exwm-mode' buffer.
+
+When FORCE is nil the update only takes place if both of
+`exwm--hints-input' and `exwm--hints-urgency' are unset."
   (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and (not force) exwm--hints-input exwm--hints-urgency)
@@ -317,7 +363,11 @@
               (setq exwm-workspace--switch-history-outdated t))))))))
 
 (defun exwm--update-protocols (id &optional force)
-  "Update WM_PROTOCOLS."
+  "Update `exwm--protocols' from WM_PROTOCOLS.
+Argument ID contains the X window of the `exwm-mode' buffer.
+
+When FORCE is nil the update only takes place if `exwm--protocols'
+is unset."
   (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and exwm--protocols (not force))
@@ -328,7 +378,7 @@
           (setq exwm--protocols (append (slot-value reply 'value) nil)))))))
 
 (defun exwm--update-struts-legacy (id)
-  "Update _NET_WM_STRUT."
+  "Update struts of X window ID from _NET_WM_STRUT."
   (exwm--log "#x%x" id)
   (let ((pair (assq id exwm-workspace--id-struts-alist))
         reply struts)
@@ -349,7 +399,7 @@
         (exwm-workspace--set-fullscreen f)))))
 
 (defun exwm--update-struts-partial (id)
-  "Update _NET_WM_STRUT_PARTIAL."
+  "Update struts of X window ID from _NET_WM_STRUT_PARTIAL."
   (exwm--log "#x%x" id)
   (let ((reply (xcb:+request-unchecked+reply exwm--connection
                    (make-instance 'xcb:ewmh:get-_NET_WM_STRUT_PARTIAL
@@ -369,13 +419,14 @@
       (exwm-workspace--set-fullscreen f))))
 
 (defun exwm--update-struts (id)
-  "Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT."
+  "Update struts of X window ID from _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT."
   (exwm--log "#x%x" id)
   (exwm--update-struts-partial id)
   (exwm--update-struts-legacy id))
 
 (defun exwm--on-PropertyNotify (data _synthetic)
-  "Handle PropertyNotify event."
+  "Handle PropertyNotify event.
+DATA contains unmarshalled PropertyNotify event data."
   (let ((obj (make-instance 'xcb:PropertyNotify))
         atom id buffer)
     (xcb:unmarshal obj data)
@@ -413,15 +464,16 @@
                           atom)))))))
 
 (defun exwm--on-ClientMessage (raw-data _synthetic)
-  "Handle ClientMessage event."
+  "Handle ClientMessage event.
+RAW-DATA contains unmarshalled ClientMessage event data."
   (let ((obj (make-instance 'xcb:ClientMessage))
         type id data)
     (xcb:unmarshal obj raw-data)
     (setq type (slot-value obj 'type)
           id (slot-value obj 'window)
           data (slot-value (slot-value obj 'data) 'data32))
-    (exwm--log "atom=%s(%s)" (x-get-atom-name type exwm-workspace--current)
-               type)
+    (exwm--log "atom=%s(%s) id=#x%x data=%s" (x-get-atom-name type exwm-workspace--current)
+               type (or id 0) data)
     (cond
      ;; _NET_NUMBER_OF_DESKTOPS.
      ((= type xcb:Atom:_NET_NUMBER_OF_DESKTOPS)
@@ -434,7 +486,6 @@
          ((and (> current requested)
                (> current 1))
           (let ((frame (car (last exwm-workspace--list))))
-            (exwm-workspace--get-remove-frame-next-workspace frame)
             (delete-frame frame))))))
      ;; _NET_CURRENT_DESKTOP.
      ((= type xcb:Atom:_NET_CURRENT_DESKTOP)
@@ -443,7 +494,8 @@
      ((= type xcb:Atom:_NET_ACTIVE_WINDOW)
       (let ((buffer (exwm--id->buffer id))
             iconic window)
-        (when (buffer-live-p buffer)
+        (if (buffer-live-p buffer)
+          ;; Either an `exwm-mode' buffer (an X window) or a floating frame.
           (with-current-buffer buffer
             (when (eq exwm--frame exwm-workspace--current)
               (if exwm--floating-frame
@@ -457,7 +509,11 @@
                 (setq window (get-buffer-window nil t))
                 (when (or iconic
                           (not (eq window (selected-window))))
-                  (select-window window))))))))
+                  (select-window window)))))
+          ;; A workspace.
+          (dolist (f exwm-workspace--list)
+            (when (eq id (frame-parameter f 'exwm-outer-id))
+              (x-focus-frame f t))))))
      ;; _NET_CLOSE_WINDOW.
      ((= type xcb:Atom:_NET_CLOSE_WINDOW)
       (let ((buffer (exwm--id->buffer id)))
@@ -594,7 +650,8 @@
                  (x-get-atom-name type exwm-workspace--current) type)))))
 
 (defun exwm--on-SelectionClear (data _synthetic)
-  "Handle SelectionClear events."
+  "Handle SelectionClear events.
+DATA contains unmarshalled SelectionClear event data."
   (exwm--log)
   (let ((obj (make-instance 'xcb:SelectionClear))
         owner selection)
@@ -605,6 +662,17 @@
                (eq selection xcb:Atom:WM_S0))
       (exwm-exit))))
 
+(defun exwm--on-delete-terminal (terminal)
+  "Handle terminal being deleted without Emacs being killed.
+This function is Hooked to `delete-terminal-functions'.
+
+TERMINAL is the terminal being (or that has been) deleted.
+
+This may happen when invoking `save-buffers-kill-terminal' within an emacsclient
+session."
+  (when (eq terminal exwm--terminal)
+    (exwm-exit)))
+
 (defun exwm--init-icccm-ewmh ()
   "Initialize ICCCM/EWMH support."
   (exwm--log)
@@ -825,7 +893,8 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
 
 ;;;###autoload
 (cl-defun exwm-init (&optional frame)
-  "Initialize EXWM."
+  "Initialize EXWM.
+FRAME, if given, indicates the X display EXWM should manage."
   (interactive)
   (exwm--log "%s" frame)
   (if frame
@@ -841,6 +910,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
   (condition-case err
       (progn
         (exwm-enable 'undo)               ;never initialize again
+        (setq exwm--terminal (frame-terminal frame))
         (setq exwm--connection (xcb:connect))
         (set-process-query-on-exit-flag (slot-value exwm--connection 'process)
                                         nil) ;prevent query message on exit
@@ -863,6 +933,10 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
         ;; Disable some features not working well with EXWM
         (setq use-dialog-box nil
               confirm-kill-emacs #'exwm--confirm-kill-emacs)
+        (advice-add 'save-buffers-kill-terminal
+                    :before-while #'exwm--confirm-kill-terminal)
+        ;; Clean up if the terminal is deleted.
+        (add-hook 'delete-terminal-functions 'exwm--on-delete-terminal)
         (exwm--lock)
         (exwm--init-icccm-ewmh)
         (exwm-layout--init)
@@ -891,15 +965,17 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
   (run-hooks 'exwm-exit-hook)
   (setq confirm-kill-emacs nil)
   ;; Exit modules.
-  (exwm-input--exit)
-  (exwm-manage--exit)
-  (exwm-workspace--exit)
-  (exwm-floating--exit)
-  (exwm-layout--exit)
   (when exwm--connection
+    (exwm-input--exit)
+    (exwm-manage--exit)
+    (exwm-workspace--exit)
+    (exwm-floating--exit)
+    (exwm-layout--exit)
     (xcb:flush exwm--connection)
     (xcb:disconnect exwm--connection))
-  (setq exwm--connection nil))
+  (setq exwm--connection nil)
+  (setq exwm--terminal nil)
+  (exwm--log "Exited"))
 
 ;;;###autoload
 (defun exwm-enable (&optional undo)
@@ -933,15 +1009,21 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
 (defun exwm--server-stop ()
   "Stop the subordinate Emacs server."
   (exwm--log)
-  (server-force-delete exwm--server-name)
   (when exwm--server-process
+    (when (process-live-p exwm--server-process)
+      (cl-loop
+       initially (signal-process exwm--server-process 'TERM)
+       while     (process-live-p exwm--server-process)
+       repeat    (* 10 exwm--server-timeout)
+       do        (sit-for 0.1)))
     (delete-process exwm--server-process)
     (setq exwm--server-process nil)))
 
-(defun exwm--server-eval-at (&rest args)
-  "Wrapper of `server-eval-at' used to advice subrs."
+(defun exwm--server-eval-at (function &rest args)
+  "Wrapper of `server-eval-at' used to advice subrs.
+FUNCTION is the function to be evaluated, ARGS are the arguments."
   ;; Start the subordinate Emacs server if it's not alive
-  (exwm--log "%s" args)
+  (exwm--log "%s %s" function args)
   (unless (server-running-p exwm--server-name)
     (when exwm--server-process (delete-process exwm--server-process))
     (setq exwm--server-process
@@ -950,7 +1032,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
                          (car command-line-args) ;The executable file
                          "-d" (frame-parameter nil 'display)
                          "-Q"
-                         (concat "--daemon=" exwm--server-name)
+                         (concat "--fg-daemon=" exwm--server-name)
                          "--eval"
                          ;; Create an invisible frame
                          "(make-frame '((window-system . x) (visibility)))"))
@@ -959,8 +1041,8 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
   (server-eval-at
    exwm--server-name
    `(progn (select-frame (car (frame-list)))
-           (let ((result ,(nconc (list (make-symbol (subr-name (car args))))
-                                 (cdr args))))
+           (let ((result ,(nconc (list (make-symbol (subr-name function)))
+                                 args)))
              (pcase (type-of result)
                ;; Return the name of a buffer
                (`buffer (buffer-name result))
@@ -978,8 +1060,20 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
                ;; For other types, return the value as-is.
                (t result))))))
 
+(defun exwm--confirm-kill-terminal (&optional _)
+  "Confirm before killing terminal."
+  ;; This is invoked instead of `save-buffers-kill-emacs' (C-x C-c) on client
+  ;; frames.
+  (if (exwm--terminal-p)
+      (exwm--confirm-kill-emacs "Kill terminal?")
+    t))
+
 (defun exwm--confirm-kill-emacs (prompt &optional force)
-  "Confirm before exiting Emacs."
+  "Confirm before exiting Emacs.
+PROMPT a reason to present to the user.
+If FORCE is nil, ask the user for confirmation.
+If FORCE is the symbol `no-check', ask if there are unsaved buffers.
+If FORCE is any other non-nil value, force killing of Emacs."
   (exwm--log)
   (when (cond
          ((and force (not (eq force 'no-check)))
@@ -996,7 +1090,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
             (`break (y-or-n-p prompt))
             (x x)))
          (t
-          (yes-or-no-p (format "[EXWM] %d window(s) will be destroyed.  %s"
+          (yes-or-no-p (format "[EXWM] %d X window(s) will be destroyed.  %s"
                                (length exwm--id-buffer-alist) prompt))))
     ;; Run `kill-emacs-hook' (`server-force-stop' excluded) before Emacs
     ;; frames are unmapped so that errors (if any) can be visible.
diff --git a/third_party/farmhash/default.nix b/third_party/farmhash/default.nix
deleted file mode 100644
index d7431dbd01..0000000000
--- a/third_party/farmhash/default.nix
+++ /dev/null
@@ -1,13 +0,0 @@
-# Google's farmhash family of hash functions
-{ pkgs, ... }:
-
-pkgs.stdenv.mkDerivation {
-  name = "farmhash";
-
-  src = pkgs.fetchFromGitHub {
-    owner = "google";
-    repo = "farmhash";
-    rev = "0d859a811870d10f53a594927d0d0b97573ad06d";
-    sha256 = "1w2583m5289hby0r91gds5yia6l8qpmzkl5b9bv58g5gacfj2h17";
-  };
-}
diff --git a/third_party/geesefs/default.nix b/third_party/geesefs/default.nix
new file mode 100644
index 0000000000..98448bb737
--- /dev/null
+++ b/third_party/geesefs/default.nix
@@ -0,0 +1,25 @@
+# Finally, a good FUSE FS implementation over S3.
+# https://github.com/yandex-cloud/geesefs
+
+{ pkgs, ... }:
+
+pkgs.buildGoModule rec {
+  pname = "geesefs";
+  version = "0.40.1";
+
+  src = pkgs.fetchFromGitHub {
+    owner = "yandex-cloud";
+    repo = "geesefs";
+    rev = "v${version}";
+    hash = "sha256:0ig8h17z8n5j8qb7k2jyh40vv77zazhnz8bxdam9xihxksj8mizp";
+  };
+
+  subPackages = [ "." ];
+  buildInputs = [ pkgs.fuse ];
+  vendorHash = "sha256:11i7cmnlxi00d0csgpv8drfcw0aqshwc4hfs0jw7zwafdhnlyy0j";
+
+  meta = with pkgs.lib; {
+    license = licenses.asl20;
+    maintainers = [ maintainers.tazjin ];
+  };
+}
diff --git a/third_party/gerrit-queue/.buildkite/build.sh b/third_party/gerrit-queue/.buildkite/build.sh
deleted file mode 100755
index 0a218c817e..0000000000
--- a/third_party/gerrit-queue/.buildkite/build.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env bash
-export GOPATH=~/go
-go generate
-CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -a -ldflags '-extldflags \"-static\"' -o gerrit-queue
diff --git a/third_party/gerrit-queue/.buildkite/pipeline.yml b/third_party/gerrit-queue/.buildkite/pipeline.yml
deleted file mode 100644
index 0885cb9694..0000000000
--- a/third_party/gerrit-queue/.buildkite/pipeline.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-steps:
-  - command: |
-      . /var/lib/buildkite-agent/.nix-profile/etc/profile.d/nix.sh
-      # produces a ./gerrit-queue
-      nix-shell --run ./.buildkite/build.sh
-
-      mkdir -p out
-      mv ./gerrit-queue out/gerrit-queue-$(git describe --tags)
-
-    label: "Build (linux/amd64)"
-    timeout: 30
-    artifact_paths:
-      - "out/*"
diff --git a/third_party/gerrit-queue/.gitignore b/third_party/gerrit-queue/.gitignore
deleted file mode 100644
index f2ec770e3c..0000000000
--- a/third_party/gerrit-queue/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/.vscode
-/statik
-/.envrc.private
-/gerrit-queue
diff --git a/third_party/gerrit-queue/LICENSE b/third_party/gerrit-queue/LICENSE
deleted file mode 100644
index 261eeb9e9f..0000000000
--- a/third_party/gerrit-queue/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/third_party/gerrit-queue/README.md b/third_party/gerrit-queue/README.md
deleted file mode 100644
index 9ffb81b8d2..0000000000
--- a/third_party/gerrit-queue/README.md
+++ /dev/null
@@ -1,80 +0,0 @@
-# gerrit-queue
-
-This daemon automatically rebases and submits changesets from a Gerrit
-instance, ensuring they still pass CI.
-
-In a usual gerrit setup with a linear master history, different developers
-await CI feedback on a rebased changeset, then one clicks submit, and
-effectively makes everybody else rebase again. `gerrit-queue` is meant to
-remove these races to master.
-
-Developers can set the `Autosubmit` label to `+1` on all changesets in a series,
-and if all preconditions on are met ("submittable" in gerrit speech, this
-usually means passing CI and passing Code Review), `gerrit-queue` takes care of
-rebasing and submitting it to master
-
-## How it works
-Gerrit only knows about Changesets (and some relations to other changesets),
-but usually developers think in terms of multiple changesets.
-
-### Fetching changesets
-`gerrit-queue` fetches all changesets from gerrit, and tries to identify these
-chains of changesets. We call them `Series`. All changesets need to have strict
-parent/child relationships to be detected (so if only half of the stack gets
-rebased by the Gerrit Web interface, these are considered individual series.
-
-Series are sorted by the number of changesets in them. This ensures longer
-series are merged faster, and less rebases are triggered. In the future, this
-might be extended to other metrics.
-
-### Submitting changesets
-The submitqueue has a Trigger() function, which gets periodically executed.
-
-It can keep a reference to one single serie across multiple runs. This is
-necessary if it previously rebased one serie to current HEAD and needs to wait
-some time until CI feedback is there. If it wouldn't keep that state, it would
-pick another series (with +1 from CI) and trigger a rebase on that one, so
-depending on CI run times and trigger intervals, if not keepig this information
-it'd end up rebasing all unrebased changesets on the same HEAD, and then just
-pick one, instead of waiting for the one to finish.
-
-The Trigger() function first instructs the gerrit client to fetch changesets
-and assemble series.
-If there is a `wipSerie` from a previous run, we check if it can still be found
-in the newly assembled list of series (it still needs to contain the same
-number of series. Commit IDs may differ, because the code doesn't reassemble a
-`wipSerie` after scheduling a rebase.
-If the `wipSerie` could be refreshed, we update the pointer with the newly
-assembled series. If we couldn't find it, we drop it.
-
-Now, we enter the main for loop. The first half of the loop checks various
-conditions of the current `wipSerie`, and if successful, does the submit
-("Submit phase"), the second half will pick a suitable new `wipSerie`, and
-potentially do a rebase ("Pick phase").
-
-#### Submit phase
-We check if there is an existing `wipSerie`. If there isn't, we immediately go to
-the "pick" phase.
-
-The `wipSerie` still needs to be rebased on `HEAD` (otherwise, the submit queue
-advanced outside of gerrit), and should not fail CI (logical merge conflict) -
-otherwise we discard it, and continue with the picking phase.
-
-If the `wipSerie` still contains a changeset awaiting CI feedback, we `return`
-from the `Trigger()` function (and go back to sleep).
-
-If the changeset is "submittable" in gerrit speech, and has the necessary
-submit queue tag set, we submit it.
-
-#### Pick phase
-The pick phase finds a new `wipSerie`. It'll first try to find one that already
-is rebased on the current `HEAD` (so the loop can just continue, and the next
-submit phase simply submit), and otherwise fall back to a not-yet-rebased
-serie. Because the rebase mandates waiting for CI, the code `return`s the
-`Trigger()` function, so it'll be called again after waiting some time.
-
-## Compile and Run
-```sh
-go generate
-GERRIT_PASSWORD=mypassword go run main.go --url https://gerrit.mydomain.com --username myuser --project myproject
-```
diff --git a/third_party/gerrit-queue/default.nix b/third_party/gerrit-queue/default.nix
deleted file mode 100644
index 427e312183..0000000000
--- a/third_party/gerrit-queue/default.nix
+++ /dev/null
@@ -1,14 +0,0 @@
-{ pkgs, lib, ... }:
-
-pkgs.buildGoModule {
-  pname = "gerrit-queue";
-  version = "master";
-  vendorSha256 = "0n5h7j416yb2mwic9c3rhqza64jlvl7iw507r9mkw3jadn4whm7a";
-  src = ./.;
-
-  meta = with lib; {
-    description = "Gerrit submit bot";
-    homepage = "https://github.com/tweag/gerrit-queue";
-    license = licenses.asl20;
-  };
-}
diff --git a/third_party/gerrit-queue/frontend/frontend.go b/third_party/gerrit-queue/frontend/frontend.go
deleted file mode 100644
index 2cc65423f0..0000000000
--- a/third_party/gerrit-queue/frontend/frontend.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package frontend
-
-import (
-	"embed"
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"net/http"
-
-	"html/template"
-
-	"github.com/apex/log"
-
-	"github.com/tweag/gerrit-queue/gerrit"
-	"github.com/tweag/gerrit-queue/misc"
-	"github.com/tweag/gerrit-queue/submitqueue"
-)
-
-//go:embed templates
-var templates embed.FS
-
-//loadTemplate loads a list of templates, relative to the templates root, and a
-//FuncMap, and returns a template object
-func loadTemplate(templateNames []string, funcMap template.FuncMap) (*template.Template, error) {
-	if len(templateNames) == 0 {
-		return nil, fmt.Errorf("templateNames can't be empty")
-	}
-	tmpl := template.New(templateNames[0]).Funcs(funcMap)
-
-	for _, templateName := range templateNames {
-		r, err := templates.Open("/" + templateName)
-		if err != nil {
-			return nil, err
-		}
-		defer r.Close()
-		contents, err := ioutil.ReadAll(r)
-		if err != nil {
-			return nil, err
-		}
-		tmpl, err = tmpl.Parse(string(contents))
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return tmpl, nil
-}
-
-// MakeFrontend returns a http.Handler
-func MakeFrontend(rotatingLogHandler *misc.RotatingLogHandler, gerritClient *gerrit.Client, runner *submitqueue.Runner) http.Handler {
-	projectName := gerritClient.GetProjectName()
-	branchName := gerritClient.GetBranchName()
-
-	mux := http.NewServeMux()
-	mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
-		var wipSerie *gerrit.Serie = nil
-		HEAD := ""
-		currentlyRunning := runner.IsCurrentlyRunning()
-
-		// don't trigger operations requiring a lock
-		if !currentlyRunning {
-			wipSerie = runner.GetWIPSerie()
-			HEAD = gerritClient.GetHEAD()
-		}
-
-		funcMap := template.FuncMap{
-			"changesetURL": func(changeset *gerrit.Changeset) string {
-				return gerritClient.GetChangesetURL(changeset)
-			},
-			"levelToClasses": func(level log.Level) string {
-				switch level {
-				case log.DebugLevel:
-					return "text-muted"
-				case log.InfoLevel:
-					return "text-info"
-				case log.WarnLevel:
-					return "text-warning"
-				case log.ErrorLevel:
-					return "text-danger"
-				case log.FatalLevel:
-					return "text-danger"
-				default:
-					return "text-white"
-				}
-			},
-			"fieldsToJSON": func(fields log.Fields) string {
-				jsonData, _ := json.Marshal(fields)
-				return string(jsonData)
-			},
-		}
-
-		tmpl := template.Must(loadTemplate([]string{
-			"index.tmpl.html",
-			"serie.tmpl.html",
-			"changeset.tmpl.html",
-		}, funcMap))
-
-		tmpl.ExecuteTemplate(w, "index.tmpl.html", map[string]interface{}{
-			// Config
-			"projectName": projectName,
-			"branchName":  branchName,
-
-			// State
-			"currentlyRunning": currentlyRunning,
-			"wipSerie":         wipSerie,
-			"HEAD":             HEAD,
-
-			// History
-			"memory": rotatingLogHandler,
-		})
-	})
-	return mux
-}
diff --git a/third_party/gerrit-queue/frontend/templates/changeset.tmpl.html b/third_party/gerrit-queue/frontend/templates/changeset.tmpl.html
deleted file mode 100644
index 5d3997885c..0000000000
--- a/third_party/gerrit-queue/frontend/templates/changeset.tmpl.html
+++ /dev/null
@@ -1,15 +0,0 @@
-{{ define "changeset" }}
-<tr>
-    <td>{{ .OwnerName }}</td>
-    <td>
-    <strong>{{ .Subject }}</strong> (<a href="{{ changesetURL . }}" target="_blank">#{{ .Number }}</a>)<br />
-    <small><code>{{ .CommitID }}</code></small>
-    </td>
-    <td>
-    <span>
-        {{ if .IsVerified }}<span class="badge badge-success badge-pill">+1 (CI)</span>{{ end }}
-        {{ if .IsCodeReviewed }}<span class="badge badge-info badge-pill">+2 (CR)</span>{{ end }}
-    </span>
-    </td>
-</tr>
-{{ end }}
\ No newline at end of file
diff --git a/third_party/gerrit-queue/frontend/templates/index.tmpl.html b/third_party/gerrit-queue/frontend/templates/index.tmpl.html
deleted file mode 100644
index e04c0a349d..0000000000
--- a/third_party/gerrit-queue/frontend/templates/index.tmpl.html
+++ /dev/null
@@ -1,76 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>Gerrit Submit Queue</title>
-  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
-  <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha256-CjSoeELFOcH0/uxWu6mC/Vlrc1AARqbm/jiiImDGV3s=" crossorigin="anonymous"></script>
-  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha256-YLGeXaapI0/5IgZopewRJcFXomhRMlYYjugPLSyNjTY=" crossorigin="anonymous" />
-</head>
-<body>
-  <nav class="navbar sticky-top navbar-expand-sm navbar-dark bg-dark">
-    <div class="container">
-      <a class="navbar-brand" href="#">Gerrit Submit Queue</a>
-      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
-        <span class="navbar-toggler-icon"></span>
-      </button>
-      <div class="collapse navbar-collapse" id="navbarSupportedContent">
-        <ul class="navbar-nav mr-auto">
-          <li class="nav-item">
-            <a class="nav-link" href="#region-info">Info</a>
-          </li>
-          <li class="nav-item">
-            <a class="nav-link" href="#region-wipserie">WIP Serie</a>
-          </li>
-          <li class="nav-item">
-            <a class="nav-link" href="#region-log">Log</a>
-          </li>
-        </ul>
-      </div>
-    </div>
-  </nav>
-  <div class="container">
-    <h2 id="region-info">Info</h2>
-    <table class="table">
-      <tbody>
-        <tr>
-          <th scope="row">Project Name:</th>
-          <td>{{ .projectName }}</td>
-        </tr>
-        <tr>
-          <th scope="row">Branch Name:</th>
-          <td>{{ .branchName }}</td>
-        </tr>
-        <tr>
-          <th scope="row">Currently running:</th>
-          <td>
-            {{ if .currentlyRunning }}yes{{ else }}no{{ end }}
-          </td>
-        </tr>
-        <tr>
-          <th scope="row">HEAD:</th>
-          <td>
-            {{ if .HEAD }}{{ .HEAD }}{{ else }}-{{ end }}
-          </td>
-        </tr>
-      </tbody>
-    </table>
-
-    <h2 id="region-wipserie">WIP Serie</h2>
-    {{ if .wipSerie }}
-    {{ block "serie" .wipSerie }}{{ end }}
-    {{ else }}
-    - 
-    {{ end }}
-
-    <h2 id="region-log">Log</h2>
-    {{ range $entry := .memory.Entries }}
-    <div class="d-flex flex-row bg-dark {{ levelToClasses $entry.Level }} text-monospace"> 
-      <div class="p-2"><small>{{ $entry.Timestamp.Format "2006-01-02 15:04:05 UTC"}}</small></div>
-      <div class="p-2 flex-grow-1"><small><strong>{{ $entry.Message }}</strong></small></div>
-    </div>
-    <div class="bg-dark {{ levelToClasses $entry.Level }} text-monospace text-break" style="padding-left: 4rem"> 
-    <small>{{ fieldsToJSON $entry.Fields }}</small>
-    </div>
-    {{ end }}
-</body>
-</html>
diff --git a/third_party/gerrit-queue/frontend/templates/serie.tmpl.html b/third_party/gerrit-queue/frontend/templates/serie.tmpl.html
deleted file mode 100644
index 60f0c18113..0000000000
--- a/third_party/gerrit-queue/frontend/templates/serie.tmpl.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{{ define "serie" }}
-<table class="table table-sm table-hover">
-<thead class="thead-light">
-    <tr>
-    <th scope="col">Owner</th>
-    <th scope="col">Changeset</th>
-    <th scope="col">Flags</th>
-    </tr>
-</thead>
-<tbody>
-    <tr>
-        <td colspan="3" class="table-success">Serie with {{ len .ChangeSets }} changes</td>
-    </tr>
-    {{ range $changeset := .ChangeSets }}
-    {{ block "changeset" $changeset }}{{ end }}
-    {{ end }}
-</tbody>
-</table>
-{{ end }}
\ No newline at end of file
diff --git a/third_party/gerrit-queue/gerrit/changeset.go b/third_party/gerrit-queue/gerrit/changeset.go
deleted file mode 100644
index f71032a567..0000000000
--- a/third_party/gerrit-queue/gerrit/changeset.go
+++ /dev/null
@@ -1,117 +0,0 @@
-package gerrit
-
-import (
-	"bytes"
-	"fmt"
-
-	goGerrit "github.com/andygrunwald/go-gerrit"
-	"github.com/apex/log"
-)
-
-// Changeset represents a single changeset
-// Relationships between different changesets are described in Series
-type Changeset struct {
-	changeInfo      *goGerrit.ChangeInfo
-	ChangeID        string
-	Number          int
-	Verified        int
-	CodeReviewed    int
-	Autosubmit      int
-	Submittable     bool
-	CommitID        string
-	ParentCommitIDs []string
-	OwnerName       string
-	Subject         string
-}
-
-// MakeChangeset creates a new Changeset object out of a goGerrit.ChangeInfo object
-func MakeChangeset(changeInfo *goGerrit.ChangeInfo) *Changeset {
-	return &Changeset{
-		changeInfo:      changeInfo,
-		ChangeID:        changeInfo.ChangeID,
-		Number:          changeInfo.Number,
-		Verified:        labelInfoToInt(changeInfo.Labels["Verified"]),
-		CodeReviewed:    labelInfoToInt(changeInfo.Labels["Code-Review"]),
-		Autosubmit:      labelInfoToInt(changeInfo.Labels["Autosubmit"]),
-		Submittable:     changeInfo.Submittable,
-		CommitID:        changeInfo.CurrentRevision, // yes, this IS the commit ID.
-		ParentCommitIDs: getParentCommitIDs(changeInfo),
-		OwnerName:       changeInfo.Owner.Name,
-		Subject:         changeInfo.Subject,
-	}
-}
-
-// IsAutosubmit returns true if the changeset is intended to be
-// automatically submitted by gerrit-queue.
-//
-// This is determined by the Change Owner setting +1 on the
-// "Autosubmit" label.
-func (c *Changeset) IsAutosubmit() bool {
-	return c.Autosubmit == 1
-}
-
-// IsVerified returns true if the changeset passed CI,
-// that's when somebody left the Approved (+1) on the "Verified" label
-func (c *Changeset) IsVerified() bool {
-	return c.Verified == 1
-}
-
-// IsCodeReviewed returns true if the changeset passed code review,
-// that's when somebody left the Recommended (+2) on the "Code-Review" label
-func (c *Changeset) IsCodeReviewed() bool {
-	return c.CodeReviewed == 2
-}
-
-func (c *Changeset) String() string {
-	var b bytes.Buffer
-	b.WriteString("Changeset")
-	b.WriteString(fmt.Sprintf("(commitID: %.7s, author: %s, subject: %s, submittable: %v)",
-		c.CommitID, c.OwnerName, c.Subject, c.Submittable))
-	return b.String()
-}
-
-// FilterChangesets filters a list of Changeset by a given filter function
-func FilterChangesets(changesets []*Changeset, f func(*Changeset) bool) []*Changeset {
-	newChangesets := make([]*Changeset, 0)
-	for _, changeset := range changesets {
-		if f(changeset) {
-			newChangesets = append(newChangesets, changeset)
-		} else {
-			log.WithField("changeset", changeset.String()).Debug("dropped by filter")
-		}
-	}
-	return newChangesets
-}
-
-// labelInfoToInt converts a goGerrit.LabelInfo to -2…+2 int
-func labelInfoToInt(labelInfo goGerrit.LabelInfo) int {
-	if labelInfo.Recommended.AccountID != 0 {
-		return 2
-	}
-	if labelInfo.Approved.AccountID != 0 {
-		return 1
-	}
-	if labelInfo.Disliked.AccountID != 0 {
-		return -1
-	}
-	if labelInfo.Rejected.AccountID != 0 {
-		return -2
-	}
-	return 0
-}
-
-// getParentCommitIDs returns the parent commit IDs of the goGerrit.ChangeInfo
-// There is usually only one parent commit ID, except for merge commits.
-func getParentCommitIDs(changeInfo *goGerrit.ChangeInfo) []string {
-	// obtain the RevisionInfo object
-	revisionInfo := changeInfo.Revisions[changeInfo.CurrentRevision]
-
-	// obtain the Commit object
-	commit := revisionInfo.Commit
-
-	commitIDs := make([]string, len(commit.Parents))
-	for i, commit := range commit.Parents {
-		commitIDs[i] = commit.Commit
-	}
-	return commitIDs
-}
diff --git a/third_party/gerrit-queue/gerrit/client.go b/third_party/gerrit-queue/gerrit/client.go
deleted file mode 100644
index 314f97281c..0000000000
--- a/third_party/gerrit-queue/gerrit/client.go
+++ /dev/null
@@ -1,220 +0,0 @@
-package gerrit
-
-import (
-	"fmt"
-
-	goGerrit "github.com/andygrunwald/go-gerrit"
-	"github.com/apex/log"
-
-	"net/url"
-)
-
-// passed to gerrit when retrieving changesets
-var additionalFields = []string{
-	"LABELS",
-	"CURRENT_REVISION",
-	"CURRENT_COMMIT",
-	"DETAILED_ACCOUNTS",
-	"SUBMITTABLE",
-}
-
-// IClient defines the gerrit.Client interface
-type IClient interface {
-	Refresh() error
-	GetHEAD() string
-	GetBaseURL() string
-	GetChangesetURL(changeset *Changeset) string
-	SubmitChangeset(changeset *Changeset) (*Changeset, error)
-	RebaseChangeset(changeset *Changeset, ref string) (*Changeset, error)
-	ChangesetIsRebasedOnHEAD(changeset *Changeset) bool
-	SerieIsRebasedOnHEAD(serie *Serie) bool
-	FilterSeries(filter func(s *Serie) bool) []*Serie
-	FindSerie(filter func(s *Serie) bool) *Serie
-}
-
-var _ IClient = &Client{}
-
-// Client provides some ways to interact with a gerrit instance
-type Client struct {
-	client      *goGerrit.Client
-	logger      *log.Logger
-	baseURL     string
-	projectName string
-	branchName  string
-	series      []*Serie
-	head        string
-}
-
-// NewClient initializes a new gerrit client
-func NewClient(logger *log.Logger, URL, username, password, projectName, branchName string) (*Client, error) {
-	urlParsed, err := url.Parse(URL)
-	if err != nil {
-		return nil, err
-	}
-	urlParsed.User = url.UserPassword(username, password)
-
-	goGerritClient, err := goGerrit.NewClient(urlParsed.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-	return &Client{
-		client:      goGerritClient,
-		baseURL:     URL,
-		logger:      logger,
-		projectName: projectName,
-		branchName:  branchName,
-	}, nil
-}
-
-// refreshHEAD queries the commit ID of the selected project and branch
-func (c *Client) refreshHEAD() (string, error) {
-	branchInfo, _, err := c.client.Projects.GetBranch(c.projectName, c.branchName)
-	if err != nil {
-		return "", err
-	}
-	return branchInfo.Revision, nil
-}
-
-// GetHEAD returns the internally stored HEAD
-func (c *Client) GetHEAD() string {
-	return c.head
-}
-
-// Refresh causes the client to refresh internal view of gerrit
-func (c *Client) Refresh() error {
-	c.logger.Debug("refreshing from gerrit")
-	HEAD, err := c.refreshHEAD()
-	if err != nil {
-		return err
-	}
-	c.head = HEAD
-
-	var queryString = fmt.Sprintf("status:open project:%s branch:%s", c.projectName, c.branchName)
-	c.logger.Debugf("fetching changesets: %s", queryString)
-	changesets, err := c.fetchChangesets(queryString)
-	if err != nil {
-		return err
-	}
-
-	c.logger.Infof("assembling series…")
-	series, err := AssembleSeries(changesets, c.logger)
-	if err != nil {
-		return err
-	}
-	series = SortSeries(series)
-	c.series = series
-	return nil
-}
-
-// fetchChangesets fetches a list of changesets matching a passed query string
-func (c *Client) fetchChangesets(queryString string) (changesets []*Changeset, Error error) {
-	opt := &goGerrit.QueryChangeOptions{}
-	opt.Query = []string{
-		queryString,
-	}
-	opt.AdditionalFields = additionalFields
-	changes, _, err := c.client.Changes.QueryChanges(opt)
-	if err != nil {
-		return nil, err
-	}
-
-	changesets = make([]*Changeset, 0)
-	for _, change := range *changes {
-		changesets = append(changesets, MakeChangeset(&change))
-	}
-
-	return changesets, nil
-}
-
-// fetchChangeset downloads an existing Changeset from gerrit, by its ID
-// Gerrit's API is a bit sparse, and only returns what you explicitly ask it
-// This is used to refresh an existing changeset with more data.
-func (c *Client) fetchChangeset(changeID string) (*Changeset, error) {
-	opt := goGerrit.ChangeOptions{}
-	opt.AdditionalFields = []string{"LABELS", "DETAILED_ACCOUNTS"}
-	changeInfo, _, err := c.client.Changes.GetChange(changeID, &opt)
-	if err != nil {
-		return nil, err
-	}
-	return MakeChangeset(changeInfo), nil
-}
-
-// SubmitChangeset submits a given changeset, and returns a changeset afterwards.
-func (c *Client) SubmitChangeset(changeset *Changeset) (*Changeset, error) {
-	changeInfo, _, err := c.client.Changes.SubmitChange(changeset.ChangeID, &goGerrit.SubmitInput{})
-	if err != nil {
-		return nil, err
-	}
-	c.head = changeInfo.CurrentRevision
-	return c.fetchChangeset(changeInfo.ChangeID)
-}
-
-// RebaseChangeset rebases a given changeset on top of a given ref
-func (c *Client) RebaseChangeset(changeset *Changeset, ref string) (*Changeset, error) {
-	changeInfo, _, err := c.client.Changes.RebaseChange(changeset.ChangeID, &goGerrit.RebaseInput{
-		Base: ref,
-	})
-	if err != nil {
-		return changeset, err
-	}
-	return c.fetchChangeset(changeInfo.ChangeID)
-}
-
-// GetBaseURL returns the gerrit base URL
-func (c *Client) GetBaseURL() string {
-	return c.baseURL
-}
-
-// GetProjectName returns the configured gerrit project name
-func (c *Client) GetProjectName() string {
-	return c.projectName
-}
-
-// GetBranchName returns the configured gerrit branch name
-func (c *Client) GetBranchName() string {
-	return c.branchName
-}
-
-// GetChangesetURL returns the URL to view a given changeset
-func (c *Client) GetChangesetURL(changeset *Changeset) string {
-	return fmt.Sprintf("%s/c/%s/+/%d", c.GetBaseURL(), c.projectName, changeset.Number)
-}
-
-// ChangesetIsRebasedOnHEAD returns true if the changeset is rebased on the current HEAD
-func (c *Client) ChangesetIsRebasedOnHEAD(changeset *Changeset) bool {
-	if len(changeset.ParentCommitIDs) != 1 {
-		return false
-	}
-	return changeset.ParentCommitIDs[0] == c.head
-}
-
-// SerieIsRebasedOnHEAD returns true if the whole series is rebased on the current HEAD
-// this is already the case if the first changeset in the series is rebased on the current HEAD
-func (c *Client) SerieIsRebasedOnHEAD(serie *Serie) bool {
-	// an empty serie should not exist
-	if len(serie.ChangeSets) == 0 {
-		return false
-	}
-	return c.ChangesetIsRebasedOnHEAD(serie.ChangeSets[0])
-}
-
-// FilterSeries returns a subset of all Series, passing the given filter function
-func (c *Client) FilterSeries(filter func(s *Serie) bool) []*Serie {
-	matchedSeries := []*Serie{}
-	for _, serie := range c.series {
-		if filter(serie) {
-			matchedSeries = append(matchedSeries, serie)
-		}
-	}
-	return matchedSeries
-}
-
-// FindSerie returns the first serie that matches the filter, or nil if none was found
-func (c *Client) FindSerie(filter func(s *Serie) bool) *Serie {
-	for _, serie := range c.series {
-		if filter(serie) {
-			return serie
-		}
-	}
-	return nil
-}
diff --git a/third_party/gerrit-queue/gerrit/serie.go b/third_party/gerrit-queue/gerrit/serie.go
deleted file mode 100644
index 788cf46f4e..0000000000
--- a/third_party/gerrit-queue/gerrit/serie.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package gerrit
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/apex/log"
-)
-
-// Serie represents a list of successive changesets with an unbroken parent -> child relation,
-// starting from the parent.
-type Serie struct {
-	ChangeSets []*Changeset
-}
-
-// GetParentCommitIDs returns the parent commit IDs
-func (s *Serie) GetParentCommitIDs() ([]string, error) {
-	if len(s.ChangeSets) == 0 {
-		return nil, fmt.Errorf("Can't return parent on a serie with zero ChangeSets")
-	}
-	return s.ChangeSets[0].ParentCommitIDs, nil
-}
-
-// GetLeafCommitID returns the commit id of the last commit in ChangeSets
-func (s *Serie) GetLeafCommitID() (string, error) {
-	if len(s.ChangeSets) == 0 {
-		return "", fmt.Errorf("Can't return leaf on a serie with zero ChangeSets")
-	}
-	return s.ChangeSets[len(s.ChangeSets)-1].CommitID, nil
-}
-
-// CheckIntegrity checks that the series contains a properly ordered and connected chain of commits
-func (s *Serie) CheckIntegrity() error {
-	logger := log.WithField("serie", s)
-	// an empty serie is invalid
-	if len(s.ChangeSets) == 0 {
-		return fmt.Errorf("An empty serie is invalid")
-	}
-
-	previousCommitID := ""
-	for i, changeset := range s.ChangeSets {
-		// we can't really check the parent of the first commit
-		// so skip verifying that one
-		logger.WithFields(log.Fields{
-			"changeset":        changeset.String(),
-			"previousCommitID": fmt.Sprintf("%.7s", previousCommitID),
-		}).Debug(" - verifying changeset")
-
-		parentCommitIDs := changeset.ParentCommitIDs
-		if len(parentCommitIDs) == 0 {
-			return fmt.Errorf("Changesets without any parent are not supported")
-		}
-		// we don't check parents of the first changeset in a series
-		if i != 0 {
-			if len(parentCommitIDs) != 1 {
-				return fmt.Errorf("Merge commits in the middle of a series are not supported (only at the beginning)")
-			}
-			if parentCommitIDs[0] != previousCommitID {
-				return fmt.Errorf("changesets parent commit id doesn't match previous commit id")
-			}
-		}
-		// update previous commit id for the next loop iteration
-		previousCommitID = changeset.CommitID
-	}
-	return nil
-}
-
-// FilterAllChangesets applies a filter function on all of the changesets in the series.
-// returns true if it returns true for all changesets, false otherwise
-func (s *Serie) FilterAllChangesets(f func(c *Changeset) bool) bool {
-	for _, changeset := range s.ChangeSets {
-		if f(changeset) == false {
-			return false
-		}
-	}
-	return true
-}
-
-func (s *Serie) String() string {
-	var sb strings.Builder
-	sb.WriteString(fmt.Sprintf("Serie[%d]", len(s.ChangeSets)))
-	if len(s.ChangeSets) == 0 {
-		sb.WriteString("()\n")
-		return sb.String()
-	}
-	parentCommitIDs, err := s.GetParentCommitIDs()
-	if err == nil {
-		if len(parentCommitIDs) == 1 {
-			sb.WriteString(fmt.Sprintf("(parent: %.7s)", parentCommitIDs[0]))
-		} else {
-			sb.WriteString("(merge: ")
-
-			for i, parentCommitID := range parentCommitIDs {
-				sb.WriteString(fmt.Sprintf("%.7s", parentCommitID))
-				if i < len(parentCommitIDs) {
-					sb.WriteString(", ")
-				}
-			}
-
-			sb.WriteString(")")
-
-		}
-	}
-	sb.WriteString(fmt.Sprintf("(%.7s..%.7s)",
-		s.ChangeSets[0].CommitID,
-		s.ChangeSets[len(s.ChangeSets)-1].CommitID))
-	return sb.String()
-}
-
-func shortCommitID(commitID string) string {
-	return commitID[:6]
-}
diff --git a/third_party/gerrit-queue/gerrit/series.go b/third_party/gerrit-queue/gerrit/series.go
deleted file mode 100644
index 295193ee95..0000000000
--- a/third_party/gerrit-queue/gerrit/series.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package gerrit
-
-import (
-	"sort"
-
-	"github.com/apex/log"
-)
-
-// AssembleSeries consumes a list of `Changeset`, and groups them together to series
-//
-// We initially put every Changeset in its own Serie
-//
-// As we have no control over the order of the passed changesets,
-// we maintain a lookup table, mapLeafToSerie,
-// which allows to lookup a serie by its leaf commit id
-// We concat series in a fixpoint approach
-// because both appending and prepending is much more complex.
-// Concatenation moves changesets of the later changeset in the previous one
-// in a cleanup phase, we remove orphaned series (those without any changesets inside)
-// afterwards, we do an integrity check, just to be on the safe side.
-func AssembleSeries(changesets []*Changeset, logger *log.Logger) ([]*Serie, error) {
-	series := make([]*Serie, 0)
-	mapLeafToSerie := make(map[string]*Serie, 0)
-
-	for _, changeset := range changesets {
-		l := logger.WithField("changeset", changeset.String())
-
-		l.Debug("creating initial serie")
-		serie := &Serie{
-			ChangeSets: []*Changeset{changeset},
-		}
-		series = append(series, serie)
-		mapLeafToSerie[changeset.CommitID] = serie
-	}
-
-	// Combine series using a fixpoint approach, with a max iteration count.
-	logger.Debug("glueing together phase")
-	for i := 1; i < 100; i++ {
-		didUpdate := false
-		logger.Debugf("at iteration %d", i)
-		for j, serie := range series {
-			l := logger.WithFields(log.Fields{
-				"i":     i,
-				"j":     j,
-				"serie": serie.String(),
-			})
-			parentCommitIDs, err := serie.GetParentCommitIDs()
-			if err != nil {
-				return series, err
-			}
-			if len(parentCommitIDs) != 1 {
-				// We can't append merge commits to other series
-				l.Infof("No single parent, skipping.")
-				continue
-			}
-			parentCommitID := parentCommitIDs[0]
-			l.Debug("Looking for a predecessor.")
-			// if there's another serie that has this parent as a leaf, glue together
-			if otherSerie, ok := mapLeafToSerie[parentCommitID]; ok {
-				if otherSerie == serie {
-					continue
-				}
-				l = l.WithField("otherSerie", otherSerie)
-
-				myLeafCommitID, err := serie.GetLeafCommitID()
-				if err != nil {
-					return series, err
-				}
-
-				// append our changesets to the other serie
-				l.Debug("Splicing together.")
-				otherSerie.ChangeSets = append(otherSerie.ChangeSets, serie.ChangeSets...)
-
-				delete(mapLeafToSerie, parentCommitID)
-				mapLeafToSerie[myLeafCommitID] = otherSerie
-
-				// orphan our serie
-				serie.ChangeSets = []*Changeset{}
-				// remove the orphaned serie from the lookup table
-				delete(mapLeafToSerie, myLeafCommitID)
-
-				didUpdate = true
-			} else {
-				l.Debug("Not found.")
-			}
-		}
-		series = removeOrphanedSeries(series)
-		if !didUpdate {
-			logger.Infof("converged after %d iterations", i)
-			break
-		}
-	}
-
-	// Check integrity, just to be on the safe side.
-	for _, serie := range series {
-		l := logger.WithField("serie", serie.String())
-		l.Debugf("checking integrity")
-		err := serie.CheckIntegrity()
-		if err != nil {
-			l.Errorf("checking integrity failed: %s", err)
-		}
-	}
-	return series, nil
-}
-
-// removeOrphanedSeries removes all empty series (that contain zero changesets)
-func removeOrphanedSeries(series []*Serie) []*Serie {
-	newSeries := []*Serie{}
-	for _, serie := range series {
-		if len(serie.ChangeSets) != 0 {
-			newSeries = append(newSeries, serie)
-		}
-	}
-	return newSeries
-}
-
-// SortSeries sorts a list of series by the number of changesets in each serie, descending
-func SortSeries(series []*Serie) []*Serie {
-	newSeries := make([]*Serie, len(series))
-	copy(newSeries, series)
-	sort.Slice(newSeries, func(i, j int) bool {
-		// the weight depends on the amount of changesets series changeset size
-		return len(series[i].ChangeSets) > len(series[j].ChangeSets)
-	})
-	return newSeries
-}
diff --git a/third_party/gerrit-queue/go.mod b/third_party/gerrit-queue/go.mod
deleted file mode 100644
index 3929f8cf65..0000000000
--- a/third_party/gerrit-queue/go.mod
+++ /dev/null
@@ -1,10 +0,0 @@
-module github.com/tweag/gerrit-queue
-
-go 1.16
-
-require (
-	github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8
-	github.com/apex/log v1.1.1
-	github.com/google/go-querystring v1.0.0 // indirect
-	github.com/urfave/cli v1.22.1
-)
diff --git a/third_party/gerrit-queue/go.sum b/third_party/gerrit-queue/go.sum
deleted file mode 100644
index d11545a6c9..0000000000
--- a/third_party/gerrit-queue/go.sum
+++ /dev/null
@@ -1,69 +0,0 @@
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8 h1:9PvNa6zH6gOW4VVfbAx5rjDLpxunG+RSaXQB+8TEv4w=
-github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8/go.mod h1:0iuRQp6WJ44ts+iihy5E/WlPqfg5RNeQxOmzRkxCdtk=
-github.com/apex/log v1.1.1 h1:BwhRZ0qbjYtTob0I+2M+smavV0kOC8XgcnGZcyL9liA=
-github.com/apex/log v1.1.1/go.mod h1:Ls949n1HFtXfbDcjiTTFQqkVUrte0puoIBfO3SVgwOA=
-github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
-github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
-github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
-github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
-github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
-github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
-github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
-github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
-github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
-github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
-github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/third_party/gerrit-queue/main.go b/third_party/gerrit-queue/main.go
deleted file mode 100644
index eaa792c958..0000000000
--- a/third_party/gerrit-queue/main.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package main
-
-import (
-	"os"
-	"time"
-
-	"net/http"
-
-	"github.com/tweag/gerrit-queue/frontend"
-	"github.com/tweag/gerrit-queue/gerrit"
-	"github.com/tweag/gerrit-queue/misc"
-	"github.com/tweag/gerrit-queue/submitqueue"
-
-	"github.com/urfave/cli"
-
-	"github.com/apex/log"
-	"github.com/apex/log/handlers/multi"
-	"github.com/apex/log/handlers/text"
-)
-
-func main() {
-	var URL, username, password, projectName, branchName string
-	var fetchOnly bool
-	var triggerInterval int
-
-	app := cli.NewApp()
-	app.Name = "gerrit-queue"
-
-	app.Flags = []cli.Flag{
-		cli.StringFlag{
-			Name:        "url",
-			Usage:       "URL to the gerrit instance",
-			EnvVar:      "GERRIT_URL",
-			Destination: &URL,
-			Required:    true,
-		},
-		cli.StringFlag{
-			Name:        "username",
-			Usage:       "Username to use to login to gerrit",
-			EnvVar:      "GERRIT_USERNAME",
-			Destination: &username,
-			Required:    true,
-		},
-		cli.StringFlag{
-			Name:        "password",
-			Usage:       "Password to use to login to gerrit",
-			EnvVar:      "GERRIT_PASSWORD",
-			Destination: &password,
-			Required:    true,
-		},
-		cli.StringFlag{
-			Name:        "project",
-			Usage:       "Gerrit project name to run the submit queue for",
-			EnvVar:      "GERRIT_PROJECT",
-			Destination: &projectName,
-			Required:    true,
-		},
-		cli.StringFlag{
-			Name:        "branch",
-			Usage:       "Destination branch",
-			EnvVar:      "GERRIT_BRANCH",
-			Destination: &branchName,
-			Value:       "master",
-		},
-		cli.IntFlag{
-			Name:        "trigger-interval",
-			Usage:       "How often we should trigger ourselves (interval in seconds)",
-			EnvVar:      "SUBMIT_QUEUE_TRIGGER_INTERVAL",
-			Destination: &triggerInterval,
-			Value:       600,
-		},
-		cli.BoolFlag{
-			Name:        "fetch-only",
-			Usage:       "Only fetch changes and assemble queue, but don't actually write",
-			EnvVar:      "SUBMIT_QUEUE_FETCH_ONLY",
-			Destination: &fetchOnly,
-		},
-	}
-
-	rotatingLogHandler := misc.NewRotatingLogHandler(10000)
-	l := &log.Logger{
-		Handler: multi.New(
-			text.New(os.Stderr),
-			rotatingLogHandler,
-		),
-		Level: log.DebugLevel,
-	}
-
-	app.Action = func(c *cli.Context) error {
-		gerrit, err := gerrit.NewClient(l, URL, username, password, projectName, branchName)
-		if err != nil {
-			return err
-		}
-		log.Infof("Successfully connected to gerrit at %s", URL)
-
-		runner := submitqueue.NewRunner(l, gerrit)
-
-		handler := frontend.MakeFrontend(rotatingLogHandler, gerrit, runner)
-
-		// fetch only on first run
-		err = runner.Trigger(fetchOnly)
-		if err != nil {
-			log.Error(err.Error())
-		}
-
-		// ticker
-		go func() {
-			for {
-				time.Sleep(time.Duration(triggerInterval) * time.Second)
-				err = runner.Trigger(fetchOnly)
-				if err != nil {
-					log.Error(err.Error())
-				}
-			}
-		}()
-
-		server := http.Server{
-			Addr:    ":8080",
-			Handler: handler,
-		}
-
-		server.ListenAndServe()
-		if err != nil {
-			log.Fatalf(err.Error())
-		}
-
-		return nil
-	}
-
-	err := app.Run(os.Args)
-	if err != nil {
-		log.Fatal(err.Error())
-	}
-
-	// TODOS:
-	// - handle event log, either by accepting webhooks, or by streaming events?
-}
diff --git a/third_party/gerrit-queue/misc/rotatingloghandler.go b/third_party/gerrit-queue/misc/rotatingloghandler.go
deleted file mode 100644
index 3d4c5f3a83..0000000000
--- a/third_party/gerrit-queue/misc/rotatingloghandler.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package misc
-
-import (
-	"sync"
-
-	"github.com/apex/log"
-)
-
-// RotatingLogHandler implementation.
-type RotatingLogHandler struct {
-	mu         sync.Mutex
-	Entries    []*log.Entry
-	maxEntries int
-}
-
-// NewRotatingLogHandler creates a new rotating log handler
-func NewRotatingLogHandler(maxEntries int) *RotatingLogHandler {
-	return &RotatingLogHandler{
-		maxEntries: maxEntries,
-	}
-}
-
-// HandleLog implements log.Handler.
-func (h *RotatingLogHandler) HandleLog(e *log.Entry) error {
-	h.mu.Lock()
-	defer h.mu.Unlock()
-	// drop tail if we have more entries than maxEntries
-	if len(h.Entries) > h.maxEntries {
-		h.Entries = append([]*log.Entry{e}, h.Entries[:(h.maxEntries-2)]...)
-	} else {
-		h.Entries = append([]*log.Entry{e}, h.Entries...)
-	}
-	return nil
-}
diff --git a/third_party/gerrit-queue/submitqueue/runner.go b/third_party/gerrit-queue/submitqueue/runner.go
deleted file mode 100644
index 0b4cbcd8dd..0000000000
--- a/third_party/gerrit-queue/submitqueue/runner.go
+++ /dev/null
@@ -1,220 +0,0 @@
-package submitqueue
-
-import (
-	"fmt"
-	"sync"
-
-	"github.com/apex/log"
-
-	"github.com/tweag/gerrit-queue/gerrit"
-)
-
-// Runner is a struct existing across the lifetime of a single run of the submit queue
-// it contains a mutex to avoid being run multiple times.
-// In fact, it even cancels runs while another one is still in progress.
-// It contains a Gerrit object facilitating access, a log object, the configured submit queue tag
-// and a `wipSerie` (only populated if waiting for a rebase)
-type Runner struct {
-	mut              sync.Mutex
-	currentlyRunning bool
-	wipSerie         *gerrit.Serie
-	logger           *log.Logger
-	gerrit           *gerrit.Client
-}
-
-// NewRunner creates a new Runner struct
-func NewRunner(logger *log.Logger, gerrit *gerrit.Client) *Runner {
-	return &Runner{
-		logger: logger,
-		gerrit: gerrit,
-	}
-}
-
-// isAutoSubmittable determines if something could be autosubmitted, potentially requiring a rebase
-// for this, it needs to:
-//  * have the "Autosubmit" label set to +1
-//  * have gerrit's 'submittable' field set to true
-// it doesn't check if the series is rebased on HEAD
-func (r *Runner) isAutoSubmittable(s *gerrit.Serie) bool {
-	for _, c := range s.ChangeSets {
-		if c.Submittable != true || !c.IsAutosubmit() {
-			return false
-		}
-	}
-	return true
-}
-
-// IsCurrentlyRunning returns true if the runner is currently running
-func (r *Runner) IsCurrentlyRunning() bool {
-	return r.currentlyRunning
-}
-
-// GetWIPSerie returns the current wipSerie, if any, nil otherwiese
-// Acquires a lock, so check with IsCurrentlyRunning first
-func (r *Runner) GetWIPSerie() *gerrit.Serie {
-	r.mut.Lock()
-	defer func() {
-		r.mut.Unlock()
-	}()
-	return r.wipSerie
-}
-
-// Trigger gets triggered periodically
-func (r *Runner) Trigger(fetchOnly bool) error {
-	// TODO: If CI fails, remove the auto-submit labels => rules.pl
-	// Only one trigger can run at the same time
-	r.mut.Lock()
-	if r.currentlyRunning {
-		return fmt.Errorf("Already running, skipping")
-	}
-	r.currentlyRunning = true
-	r.mut.Unlock()
-	defer func() {
-		r.mut.Lock()
-		r.currentlyRunning = false
-		r.mut.Unlock()
-	}()
-
-	// Prepare the work by creating a local cache of gerrit state
-	err := r.gerrit.Refresh()
-	if err != nil {
-		return err
-	}
-
-	// early return if we only want to fetch
-	if fetchOnly {
-		return nil
-	}
-
-	if r.wipSerie != nil {
-		// refresh wipSerie with how it looks like in gerrit now
-		wipSerie := r.gerrit.FindSerie(func(s *gerrit.Serie) bool {
-			// the new wipSerie needs to have the same number of changesets
-			if len(r.wipSerie.ChangeSets) != len(s.ChangeSets) {
-				return false
-			}
-			// … and the same ChangeIDs.
-			for idx, c := range s.ChangeSets {
-				if r.wipSerie.ChangeSets[idx].ChangeID != c.ChangeID {
-					return false
-				}
-			}
-			return true
-		})
-		if wipSerie == nil {
-			r.logger.WithField("wipSerie", r.wipSerie).Warn("wipSerie has disappeared")
-			r.wipSerie = nil
-		} else {
-			r.wipSerie = wipSerie
-		}
-	}
-
-	for {
-		// initialize logger
-		r.logger.Info("Running")
-		if r.wipSerie != nil {
-			// if we have a wipSerie
-			l := r.logger.WithField("wipSerie", r.wipSerie)
-			l.Info("Checking wipSerie")
-
-			// discard wipSerie not rebased on HEAD
-			// we rebase them at the end of the loop, so this means master advanced without going through the submit queue
-			if !r.gerrit.SerieIsRebasedOnHEAD(r.wipSerie) {
-				l.Warnf("HEAD has moved to %v while still waiting for wipSerie, discarding it", r.gerrit.GetHEAD())
-				r.wipSerie = nil
-				continue
-			}
-
-			// we now need to check CI feedback:
-			// wipSerie might have failed CI in the meantime
-			for _, c := range r.wipSerie.ChangeSets {
-				if c == nil {
-					l.Error("BUG: changeset is nil")
-					continue
-				}
-				if c.Verified < 0 {
-					l.WithField("failingChangeset", c).Warnf("wipSerie failed CI in the meantime, discarding.")
-					r.wipSerie = nil
-					continue
-				}
-			}
-
-			// it might still be waiting for CI
-			for _, c := range r.wipSerie.ChangeSets {
-				if c == nil {
-					l.Error("BUG: changeset is nil")
-					continue
-				}
-				if c.Verified == 0 {
-					l.WithField("pendingChangeset", c).Warnf("still waiting for CI feedback in wipSerie, going back to sleep.")
-					// break the loop, take a look at it at the next trigger.
-					return nil
-				}
-			}
-
-			// it might be autosubmittable
-			if r.isAutoSubmittable(r.wipSerie) {
-				l.Infof("submitting wipSerie")
-				// if the WIP changeset is ready (auto submittable and rebased on HEAD), submit
-				for _, changeset := range r.wipSerie.ChangeSets {
-					_, err := r.gerrit.SubmitChangeset(changeset)
-					if err != nil {
-						l.WithField("changeset", changeset).Error("error submitting changeset")
-						r.wipSerie = nil
-						return err
-					}
-				}
-				r.wipSerie = nil
-			} else {
-				l.Error("BUG: wipSerie is not autosubmittable")
-				r.wipSerie = nil
-			}
-		}
-
-		r.logger.Info("Looking for series ready to submit")
-		// Find serie, that:
-		//  * has the auto-submit label
-		//  * has +2 review
-		//  * has +1 CI
-		//  * is rebased on master
-		serie := r.gerrit.FindSerie(func(s *gerrit.Serie) bool {
-			return r.isAutoSubmittable(s) && s.ChangeSets[0].ParentCommitIDs[0] == r.gerrit.GetHEAD()
-		})
-		if serie != nil {
-			r.logger.WithField("serie", serie).Info("Found serie to submit without necessary rebase")
-			r.wipSerie = serie
-			continue
-		}
-
-		// Find serie, that:
-		//  * has the auto-submit label
-		//  * has +2 review
-		//  * has +1 CI
-		//  * is NOT rebased on master
-		serie = r.gerrit.FindSerie(r.isAutoSubmittable)
-		if serie == nil {
-			r.logger.Info("no more submittable series found, going back to sleep.")
-			break
-		}
-
-		l := r.logger.WithField("serie", serie)
-		l.Info("found serie, which needs a rebase")
-		// TODO: move into Client.RebaseSeries function
-		head := r.gerrit.GetHEAD()
-		for _, changeset := range serie.ChangeSets {
-			changeset, err := r.gerrit.RebaseChangeset(changeset, head)
-			if err != nil {
-				l.Error(err.Error())
-				return err
-			}
-			head = changeset.CommitID
-		}
-		// we don't need to care about updating the rebased changesets or getting the updated HEAD,
-		// as we'll refetch it on the beginning of the next trigger anyways
-		r.wipSerie = serie
-		break
-	}
-
-	r.logger.Info("Run complete")
-	return nil
-}
diff --git a/third_party/gerrit/0001-Syntax-highlight-nix.patch b/third_party/gerrit/0001-Syntax-highlight-nix.patch
new file mode 100644
index 0000000000..bdc3fd3b55
--- /dev/null
+++ b/third_party/gerrit/0001-Syntax-highlight-nix.patch
@@ -0,0 +1,37 @@
+From 084e4f92fb58f7cd85303ba602fb3c40133c8fcc Mon Sep 17 00:00:00 2001
+From: Luke Granger-Brown <git@lukegb.com>
+Date: Thu, 2 Jul 2020 23:02:32 +0100
+Subject: [PATCH 1/3] Syntax highlight nix
+
+---
+ .../app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts     | 1 +
+ resources/com/google/gerrit/server/mime/mime-types.properties    | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+index a9f88bdd81..385249f280 100644
+--- a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
++++ b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+@@ -93,6 +93,7 @@ const LANGUAGE_MAP = new Map<string, string>([
+   ['text/x-vhdl', 'vhdl'],
+   ['text/x-yaml', 'yaml'],
+   ['text/vbscript', 'vbscript'],
++  ['text/x-nix', 'nix'],
+ ]);
+ 
+ const CLASS_PREFIX = 'gr-diff gr-syntax gr-syntax-';
+diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
+index 2f9561ba2e..739818ec05 100644
+--- a/resources/com/google/gerrit/server/mime/mime-types.properties
++++ b/resources/com/google/gerrit/server/mime/mime-types.properties
+@@ -149,6 +149,7 @@ mscin = text/x-mscgen
+ msgenny = text/x-msgenny
+ nb = text/x-mathematica
+ nginx.conf = text/x-nginx-conf
++nix = text/x-nix
+ nsh = text/x-nsis
+ nsi = text/x-nsis
+ nt = text/n-triples
+-- 
+2.37.3
+
diff --git a/third_party/gerrit/0001-Use-detzip-in-download_bower.py.patch b/third_party/gerrit/0001-Use-detzip-in-download_bower.py.patch
deleted file mode 100644
index 7d197795b7..0000000000
--- a/third_party/gerrit/0001-Use-detzip-in-download_bower.py.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 621cadcc1dd71e9397c21cf8cf0f1aae4f6f7057 Mon Sep 17 00:00:00 2001
-From: Luke Granger-Brown <git@lukegb.com>
-Date: Thu, 2 Jul 2020 23:02:09 +0100
-Subject: [PATCH 1/7] Use detzip in download_bower.py
-
----
- tools/js/download_bower.py | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/tools/js/download_bower.py b/tools/js/download_bower.py
-index d541b565a9..ffdae60f95 100755
---- a/tools/js/download_bower.py
-+++ b/tools/js/download_bower.py
-@@ -110,7 +110,7 @@ def main():
-                 args.b, '--quiet', 'install', '%s#%s' % (args.p, args.v)))
-         bc = os.path.join(cwd, 'bower_components')
-         subprocess.check_call(
--            ['zip', '-q', '--exclude', '.bower.json', '-r', cached, args.n],
-+            ['detzip', '--exclude', '.bower.json', cached, args.n],
-             cwd=bc)
- 
-         if args.s:
--- 
-2.32.0
-
diff --git a/third_party/gerrit/0002-Syntax-highlight-nix.patch b/third_party/gerrit/0002-Syntax-highlight-nix.patch
deleted file mode 100644
index 256da0a3c9..0000000000
--- a/third_party/gerrit/0002-Syntax-highlight-nix.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From 924647c354576ade0dc46fdf30596967f58bb4c6 Mon Sep 17 00:00:00 2001
-From: Luke Granger-Brown <git@lukegb.com>
-Date: Thu, 2 Jul 2020 23:02:32 +0100
-Subject: [PATCH 2/7] Syntax highlight nix
-
----
- .../app/elements/diff/gr-syntax-layer/gr-syntax-layer.ts         | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.ts b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.ts
-index 081d28d749..2762ccc625 100644
---- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.ts
-+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.ts
-@@ -99,6 +99,7 @@ const LANGUAGE_MAP = new Map<string, string>([
-   ['text/x-vhdl', 'vhdl'],
-   ['text/x-yaml', 'yaml'],
-   ['text/vbscript', 'vbscript'],
-+  ['application/x-mix-transfer', 'nix'],
- ]);
- const ASYNC_DELAY = 10;
- 
--- 
-2.32.0
-
diff --git a/third_party/gerrit/0002-Syntax-highlight-rules.pl.patch b/third_party/gerrit/0002-Syntax-highlight-rules.pl.patch
new file mode 100644
index 0000000000..4b91e2c354
--- /dev/null
+++ b/third_party/gerrit/0002-Syntax-highlight-rules.pl.patch
@@ -0,0 +1,37 @@
+From aedf8ac8fa5113843bcd83ff85e2d9f3bffdb16c Mon Sep 17 00:00:00 2001
+From: Luke Granger-Brown <git@lukegb.com>
+Date: Thu, 2 Jul 2020 23:02:43 +0100
+Subject: [PATCH 2/3] Syntax highlight rules.pl
+
+---
+ .../app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts     | 1 +
+ resources/com/google/gerrit/server/mime/mime-types.properties    | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+index 385249f280..7cb3068494 100644
+--- a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
++++ b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+@@ -68,6 +68,7 @@ const LANGUAGE_MAP = new Map<string, string>([
+   ['text/x-perl', 'perl'],
+   ['text/x-pgsql', 'pgsql'], // postgresql
+   ['text/x-php', 'php'],
++  ['text/x-prolog', 'prolog'],
+   ['text/x-properties', 'properties'],
+   ['text/x-protobuf', 'protobuf'],
+   ['text/x-puppet', 'puppet'],
+diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
+index 739818ec05..58eb727bf9 100644
+--- a/resources/com/google/gerrit/server/mime/mime-types.properties
++++ b/resources/com/google/gerrit/server/mime/mime-types.properties
+@@ -200,6 +200,7 @@ rq = application/sparql-query
+ rs = text/x-rustsrc
+ rss = application/xml
+ rst = text/x-rst
++rules.pl = text/x-prolog
+ README.md = text/x-gfm
+ s = text/x-gas
+ sas = text/x-sas
+-- 
+2.37.3
+
diff --git a/third_party/gerrit/0003-Add-titles-to-CLs-over-HTTP.patch b/third_party/gerrit/0003-Add-titles-to-CLs-over-HTTP.patch
new file mode 100644
index 0000000000..c4edee3a40
--- /dev/null
+++ b/third_party/gerrit/0003-Add-titles-to-CLs-over-HTTP.patch
@@ -0,0 +1,215 @@
+From f49c50ca9a84ca374b7bd91c171bbea0457f2c7a Mon Sep 17 00:00:00 2001
+From: Luke Granger-Brown <git@lukegb.com>
+Date: Thu, 2 Jul 2020 23:03:02 +0100
+Subject: [PATCH 3/3] Add titles to CLs over HTTP
+
+---
+ .../gerrit/httpd/raw/IndexHtmlUtil.java       | 13 +++-
+ .../google/gerrit/httpd/raw/IndexServlet.java |  8 ++-
+ .../google/gerrit/httpd/raw/StaticModule.java |  5 +-
+ .../gerrit/httpd/raw/TitleComputer.java       | 67 +++++++++++++++++++
+ .../gerrit/httpd/raw/PolyGerritIndexHtml.soy  |  4 +-
+ 5 files changed, 89 insertions(+), 8 deletions(-)
+ create mode 100644 java/com/google/gerrit/httpd/raw/TitleComputer.java
+
+diff --git a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
+index 72bfe40c3b..439bd73b44 100644
+--- a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
++++ b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
+@@ -41,6 +41,7 @@ import java.util.Collections;
+ import java.util.HashMap;
+ import java.util.HashSet;
+ import java.util.Map;
++import java.util.Optional;
+ import java.util.Set;
+ import java.util.function.Function;
+ 
+@@ -62,13 +63,14 @@ public class IndexHtmlUtil {
+       String faviconPath,
+       Map<String, String[]> urlParameterMap,
+       Function<String, SanitizedContent> urlInScriptTagOrdainer,
+-      String requestedURL)
++      String requestedURL,
++      TitleComputer titleComputer)
+       throws URISyntaxException, RestApiException {
+     ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
+     data.putAll(
+             staticTemplateData(
+                 canonicalURL, cdnPath, faviconPath, urlParameterMap, urlInScriptTagOrdainer))
+-        .putAll(dynamicTemplateData(gerritApi, requestedURL));
++        .putAll(dynamicTemplateData(gerritApi, requestedURL, titleComputer));
+     Set<String> enabledExperiments = new HashSet<>();
+     enabledExperiments.addAll(experimentFeatures.getEnabledExperimentFeatures());
+     // Add all experiments enabled through url
+@@ -81,7 +83,8 @@ public class IndexHtmlUtil {
+ 
+   /** Returns dynamic parameters of {@code index.html}. */
+   public static ImmutableMap<String, Object> dynamicTemplateData(
+-      GerritApi gerritApi, String requestedURL) throws RestApiException, URISyntaxException {
++      GerritApi gerritApi, String requestedURL, TitleComputer titleComputer)
++                throws RestApiException, URISyntaxException {
+     ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
+     Map<String, SanitizedContent> initialData = new HashMap<>();
+     Server serverApi = gerritApi.config().server();
+@@ -129,6 +132,10 @@ public class IndexHtmlUtil {
+     }
+ 
+     data.put("gerritInitialData", initialData);
++
++    Optional<String> title = titleComputer.computeTitle(requestedURL);
++    title.ifPresent(s -> data.put("title", s));
++
+     return data.build();
+   }
+ 
+diff --git a/java/com/google/gerrit/httpd/raw/IndexServlet.java b/java/com/google/gerrit/httpd/raw/IndexServlet.java
+index fcb821e5ae..e1464b992b 100644
+--- a/java/com/google/gerrit/httpd/raw/IndexServlet.java
++++ b/java/com/google/gerrit/httpd/raw/IndexServlet.java
+@@ -48,13 +48,15 @@ public class IndexServlet extends HttpServlet {
+   private final ExperimentFeatures experimentFeatures;
+   private final SoySauce soySauce;
+   private final Function<String, SanitizedContent> urlOrdainer;
++  private TitleComputer titleComputer;
+ 
+   IndexServlet(
+       @Nullable String canonicalUrl,
+       @Nullable String cdnPath,
+       @Nullable String faviconPath,
+       GerritApi gerritApi,
+-      ExperimentFeatures experimentFeatures) {
++      ExperimentFeatures experimentFeatures,
++      TitleComputer titleComputer) {
+     this.canonicalUrl = canonicalUrl;
+     this.cdnPath = cdnPath;
+     this.faviconPath = faviconPath;
+@@ -69,6 +71,7 @@ public class IndexServlet extends HttpServlet {
+         (s) ->
+             UnsafeSanitizedContentOrdainer.ordainAsSafe(
+                 s, SanitizedContent.ContentKind.TRUSTED_RESOURCE_URI);
++    this.titleComputer = titleComputer;
+   }
+ 
+   @Override
+@@ -86,7 +89,8 @@ public class IndexServlet extends HttpServlet {
+               faviconPath,
+               parameterMap,
+               urlOrdainer,
+-              getRequestUrl(req));
++              getRequestUrl(req),
++              titleComputer);
+       renderer = soySauce.renderTemplate("com.google.gerrit.httpd.raw.Index").setData(templateData);
+     } catch (URISyntaxException | RestApiException e) {
+       throw new IOException(e);
+diff --git a/java/com/google/gerrit/httpd/raw/StaticModule.java b/java/com/google/gerrit/httpd/raw/StaticModule.java
+index 15dcf42e0e..9f56bf33ce 100644
+--- a/java/com/google/gerrit/httpd/raw/StaticModule.java
++++ b/java/com/google/gerrit/httpd/raw/StaticModule.java
+@@ -241,10 +241,11 @@ public class StaticModule extends ServletModule {
+         @CanonicalWebUrl @Nullable String canonicalUrl,
+         @GerritServerConfig Config cfg,
+         GerritApi gerritApi,
+-        ExperimentFeatures experimentFeatures) {
++        ExperimentFeatures experimentFeatures,
++        TitleComputer titleComputer) {
+       String cdnPath = options.devCdn().orElse(cfg.getString("gerrit", null, "cdnPath"));
+       String faviconPath = cfg.getString("gerrit", null, "faviconPath");
+-      return new IndexServlet(canonicalUrl, cdnPath, faviconPath, gerritApi, experimentFeatures);
++      return new IndexServlet(canonicalUrl, cdnPath, faviconPath, gerritApi, experimentFeatures, titleComputer);
+     }
+ 
+     @Provides
+diff --git a/java/com/google/gerrit/httpd/raw/TitleComputer.java b/java/com/google/gerrit/httpd/raw/TitleComputer.java
+new file mode 100644
+index 0000000000..8fd2053ad0
+--- /dev/null
++++ b/java/com/google/gerrit/httpd/raw/TitleComputer.java
+@@ -0,0 +1,67 @@
++package com.google.gerrit.httpd.raw;
++
++import com.google.common.flogger.FluentLogger;
++import com.google.gerrit.entities.Change;
++import com.google.gerrit.extensions.restapi.ResourceConflictException;
++import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
++import com.google.gerrit.server.change.ChangeResource;
++import com.google.gerrit.server.permissions.PermissionBackendException;
++import com.google.gerrit.server.restapi.change.ChangesCollection;
++import com.google.inject.Inject;
++import com.google.inject.Provider;
++import com.google.inject.Singleton;
++
++import java.net.MalformedURLException;
++import java.net.URL;
++import java.util.Optional;
++import java.util.regex.Matcher;
++import java.util.regex.Pattern;
++
++@Singleton
++public class TitleComputer {
++  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
++
++  @Inject
++  public TitleComputer(Provider<ChangesCollection> changes) {
++    this.changes = changes;
++  }
++
++  public Optional<String> computeTitle(String requestedURI) {
++    URL url = null;
++    try {
++      url = new URL(requestedURI);
++    } catch (MalformedURLException e) {
++      logger.atWarning().log("Failed to turn %s into a URL.", requestedURI);
++      return Optional.empty();
++    }
++
++    // Try to turn this into a change.
++    Optional<Change.Id> changeId = tryExtractChange(url.getPath());
++    if (changeId.isPresent()) {
++      return titleFromChangeId(changeId.get());
++    }
++
++    return Optional.empty();
++  }
++
++  private static final Pattern extractChangeIdRegex = Pattern.compile("^/(?:c/.*/\\+/)?(?<changeId>[0-9]+)(?:/[0-9]+)?(?:/.*)?$");
++  private final Provider<ChangesCollection> changes;
++
++  private Optional<Change.Id> tryExtractChange(String path) {
++    Matcher m = extractChangeIdRegex.matcher(path);
++    if (!m.matches()) {
++      return Optional.empty();
++    }
++    return Change.Id.tryParse(m.group("changeId"));
++  }
++
++  private Optional<String> titleFromChangeId(Change.Id changeId) {
++    ChangesCollection changesCollection = changes.get();
++    try {
++      ChangeResource changeResource = changesCollection.parse(changeId);
++      return Optional.of(changeResource.getChange().getSubject());
++    } catch (ResourceConflictException | ResourceNotFoundException | PermissionBackendException e) {
++      return Optional.empty();
++    }
++  }
++}
+diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+index dbfef44dfe..347ee75aab 100644
+--- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
++++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+@@ -33,10 +33,12 @@
+   {@param? defaultDashboardHex: ?}
+   {@param? dashboardQuery: ?}
+   {@param? userIsAuthenticated: ?}
++  {@param? title: ?}
+   <!DOCTYPE html>{\n}
+   <html lang="en">{\n}
+   <meta charset="utf-8">{\n}
+-  <meta name="description" content="Gerrit Code Review">{\n}
++  {if $title}<title>{$title} Β· Gerrit Code Review</title>{\n}{/if}
++  <meta name="description" content="{if $title}{$title} Β· {/if}Gerrit Code Review">{\n}
+   <meta name="referrer" content="never">{\n}
+   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">{\n}
+ 
+-- 
+2.37.3
+
diff --git a/third_party/gerrit/0003-Syntax-highlight-rules.pl.patch b/third_party/gerrit/0003-Syntax-highlight-rules.pl.patch
deleted file mode 100644
index 02bb3397ea..0000000000
--- a/third_party/gerrit/0003-Syntax-highlight-rules.pl.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From be348f64eda257ae0af1f89552548d3e8eca3688 Mon Sep 17 00:00:00 2001
-From: Luke Granger-Brown <git@lukegb.com>
-Date: Thu, 2 Jul 2020 23:02:43 +0100
-Subject: [PATCH 3/7] Syntax highlight rules.pl
-
----
- .../diff/gr-syntax-layer/gr-syntax-layer.ts         | 13 ++++++++++++-
- 1 file changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.ts b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.ts
-index 2762ccc625..598e14589f 100644
---- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.ts
-+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.ts
-@@ -103,6 +103,10 @@ const LANGUAGE_MAP = new Map<string, string>([
- ]);
- const ASYNC_DELAY = 10;
- 
-+const FILENAME_OVERRIDES = new Map<string, string>([
-+  ['rules.pl', 'prolog'],
-+]);
-+
- const CLASS_SAFELIST = new Set<string>([
-   'gr-diff gr-syntax gr-syntax-attr',
-   'gr-diff gr-syntax gr-syntax-attribute',
-@@ -241,10 +245,17 @@ export class GrSyntaxLayer implements DiffLayer {
-     }
-   }
- 
-+  _basename(filename: string): string {
-+    const pieces = filename.split(/\//);
-+    return pieces[pieces.length-1];
-+  }
-+
-   _getLanguage(metaInfo: DiffFileMetaInfo) {
-     // The Gerrit API provides only content-type, but for other users of
-     // gr-diff it may be more convenient to specify the language directly.
--    return metaInfo.language ?? LANGUAGE_MAP.get(metaInfo.content_type);
-+    return metaInfo.language ??
-+        FILENAME_OVERRIDES.get(this._basename(metaInfo.name)) ??
-+        LANGUAGE_MAP.get(metaInfo.content_type);
-   }
- 
-   /**
--- 
-2.32.0
-
diff --git a/third_party/gerrit/0004-Add-titles-to-CLs-over-HTTP.patch b/third_party/gerrit/0004-Add-titles-to-CLs-over-HTTP.patch
deleted file mode 100644
index 8e78e5f535..0000000000
--- a/third_party/gerrit/0004-Add-titles-to-CLs-over-HTTP.patch
+++ /dev/null
@@ -1,217 +0,0 @@
-From 32bf13d8316f93828d2ff47ccfca38d4e7a634b1 Mon Sep 17 00:00:00 2001
-From: Luke Granger-Brown <git@lukegb.com>
-Date: Thu, 2 Jul 2020 23:03:02 +0100
-Subject: [PATCH 4/7] Add titles to CLs over HTTP
-
----
- .../gerrit/httpd/raw/IndexHtmlUtil.java       | 13 +++-
- .../google/gerrit/httpd/raw/IndexServlet.java |  8 ++-
- .../google/gerrit/httpd/raw/StaticModule.java |  6 +-
- .../gerrit/httpd/raw/TitleComputer.java       | 67 +++++++++++++++++++
- .../gerrit/httpd/raw/PolyGerritIndexHtml.soy  |  4 +-
- 5 files changed, 90 insertions(+), 8 deletions(-)
- create mode 100644 java/com/google/gerrit/httpd/raw/TitleComputer.java
-
-diff --git a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
-index 8d52f5ad50..a9cfceb3b6 100644
---- a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
-+++ b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
-@@ -39,6 +39,7 @@ import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Map;
-+import java.util.Optional;
- import java.util.Set;
- import java.util.function.Function;
- 
-@@ -60,13 +61,14 @@ public class IndexHtmlUtil {
-       String faviconPath,
-       Map<String, String[]> urlParameterMap,
-       Function<String, SanitizedContent> urlInScriptTagOrdainer,
--      String requestedURL)
-+      String requestedURL,
-+      TitleComputer titleComputer)
-       throws URISyntaxException, RestApiException {
-     ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
-     data.putAll(
-             staticTemplateData(
-                 canonicalURL, cdnPath, faviconPath, urlParameterMap, urlInScriptTagOrdainer))
--        .putAll(dynamicTemplateData(gerritApi, requestedURL));
-+        .putAll(dynamicTemplateData(gerritApi, requestedURL, titleComputer));
-     Set<String> enabledExperiments = experimentFeatures.getEnabledExperimentFeatures();
- 
-     if (!enabledExperiments.isEmpty()) {
-@@ -77,7 +79,9 @@ public class IndexHtmlUtil {
- 
-   /** Returns dynamic parameters of {@code index.html}. */
-   public static ImmutableMap<String, Object> dynamicTemplateData(
--      GerritApi gerritApi, String requestedURL) throws RestApiException, URISyntaxException {
-+      GerritApi gerritApi,
-+      String requestedURL,
-+      TitleComputer titleComputer) throws RestApiException, URISyntaxException {
-     ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
-     Map<String, SanitizedContent> initialData = new HashMap<>();
-     Server serverApi = gerritApi.config().server();
-@@ -128,6 +132,9 @@ public class IndexHtmlUtil {
-       // Don't render data
-     }
- 
-+    Optional<String> title = titleComputer.computeTitle(requestedURL);
-+    title.ifPresent(s -> data.put("title", s));
-+
-     data.put("gerritInitialData", initialData);
-     return data.build();
-   }
-diff --git a/java/com/google/gerrit/httpd/raw/IndexServlet.java b/java/com/google/gerrit/httpd/raw/IndexServlet.java
-index 3f2c2028ae..7861c007df 100644
---- a/java/com/google/gerrit/httpd/raw/IndexServlet.java
-+++ b/java/com/google/gerrit/httpd/raw/IndexServlet.java
-@@ -46,13 +46,15 @@ public class IndexServlet extends HttpServlet {
-   private final ExperimentFeatures experimentFeatures;
-   private final SoySauce soySauce;
-   private final Function<String, SanitizedContent> urlOrdainer;
-+  private TitleComputer titleComputer;
- 
-   IndexServlet(
-       @Nullable String canonicalUrl,
-       @Nullable String cdnPath,
-       @Nullable String faviconPath,
-       GerritApi gerritApi,
--      ExperimentFeatures experimentFeatures) {
-+      ExperimentFeatures experimentFeatures,
-+      TitleComputer titleComputer) {
-     this.canonicalUrl = canonicalUrl;
-     this.cdnPath = cdnPath;
-     this.faviconPath = faviconPath;
-@@ -67,6 +69,7 @@ public class IndexServlet extends HttpServlet {
-         (s) ->
-             UnsafeSanitizedContentOrdainer.ordainAsSafe(
-                 s, SanitizedContent.ContentKind.TRUSTED_RESOURCE_URI);
-+    this.titleComputer = titleComputer;
-   }
- 
-   @Override
-@@ -85,7 +88,8 @@ public class IndexServlet extends HttpServlet {
-               faviconPath,
-               parameterMap,
-               urlOrdainer,
--              requestUrl);
-+              requestUrl,
-+              titleComputer);
-       renderer = soySauce.renderTemplate("com.google.gerrit.httpd.raw.Index").setData(templateData);
-     } catch (URISyntaxException | RestApiException e) {
-       throw new IOException(e);
-diff --git a/java/com/google/gerrit/httpd/raw/StaticModule.java b/java/com/google/gerrit/httpd/raw/StaticModule.java
-index bb1eb92525..6b20c504d2 100644
---- a/java/com/google/gerrit/httpd/raw/StaticModule.java
-+++ b/java/com/google/gerrit/httpd/raw/StaticModule.java
-@@ -224,11 +224,13 @@ public class StaticModule extends ServletModule {
-         @CanonicalWebUrl @Nullable String canonicalUrl,
-         @GerritServerConfig Config cfg,
-         GerritApi gerritApi,
--        ExperimentFeatures experimentFeatures) {
-+        ExperimentFeatures experimentFeatures,
-+        TitleComputer titleComputer) {
-       String cdnPath =
-           options.useDevCdn() ? options.devCdn() : cfg.getString("gerrit", null, "cdnPath");
-       String faviconPath = cfg.getString("gerrit", null, "faviconPath");
--      return new IndexServlet(canonicalUrl, cdnPath, faviconPath, gerritApi, experimentFeatures);
-+      return new IndexServlet(canonicalUrl, cdnPath, faviconPath, gerritApi,
-+          experimentFeatures, titleComputer);
-     }
- 
-     @Provides
-diff --git a/java/com/google/gerrit/httpd/raw/TitleComputer.java b/java/com/google/gerrit/httpd/raw/TitleComputer.java
-new file mode 100644
-index 0000000000..8fd2053ad0
---- /dev/null
-+++ b/java/com/google/gerrit/httpd/raw/TitleComputer.java
-@@ -0,0 +1,67 @@
-+package com.google.gerrit.httpd.raw;
-+
-+import com.google.common.flogger.FluentLogger;
-+import com.google.gerrit.entities.Change;
-+import com.google.gerrit.extensions.restapi.ResourceConflictException;
-+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-+import com.google.gerrit.server.change.ChangeResource;
-+import com.google.gerrit.server.permissions.PermissionBackendException;
-+import com.google.gerrit.server.restapi.change.ChangesCollection;
-+import com.google.inject.Inject;
-+import com.google.inject.Provider;
-+import com.google.inject.Singleton;
-+
-+import java.net.MalformedURLException;
-+import java.net.URL;
-+import java.util.Optional;
-+import java.util.regex.Matcher;
-+import java.util.regex.Pattern;
-+
-+@Singleton
-+public class TitleComputer {
-+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-+
-+  @Inject
-+  public TitleComputer(Provider<ChangesCollection> changes) {
-+    this.changes = changes;
-+  }
-+
-+  public Optional<String> computeTitle(String requestedURI) {
-+    URL url = null;
-+    try {
-+      url = new URL(requestedURI);
-+    } catch (MalformedURLException e) {
-+      logger.atWarning().log("Failed to turn %s into a URL.", requestedURI);
-+      return Optional.empty();
-+    }
-+
-+    // Try to turn this into a change.
-+    Optional<Change.Id> changeId = tryExtractChange(url.getPath());
-+    if (changeId.isPresent()) {
-+      return titleFromChangeId(changeId.get());
-+    }
-+
-+    return Optional.empty();
-+  }
-+
-+  private static final Pattern extractChangeIdRegex = Pattern.compile("^/(?:c/.*/\\+/)?(?<changeId>[0-9]+)(?:/[0-9]+)?(?:/.*)?$");
-+  private final Provider<ChangesCollection> changes;
-+
-+  private Optional<Change.Id> tryExtractChange(String path) {
-+    Matcher m = extractChangeIdRegex.matcher(path);
-+    if (!m.matches()) {
-+      return Optional.empty();
-+    }
-+    return Change.Id.tryParse(m.group("changeId"));
-+  }
-+
-+  private Optional<String> titleFromChangeId(Change.Id changeId) {
-+    ChangesCollection changesCollection = changes.get();
-+    try {
-+      ChangeResource changeResource = changesCollection.parse(changeId);
-+      return Optional.of(changeResource.getChange().getSubject());
-+    } catch (ResourceConflictException | ResourceNotFoundException | PermissionBackendException e) {
-+      return Optional.empty();
-+    }
-+  }
-+}
-diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
-index 11717fb8a4..1ae9046360 100644
---- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
-+++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
-@@ -33,10 +33,12 @@
-   {@param? defaultDashboardHex: ?}
-   {@param? dashboardQuery: ?}
-   {@param? userIsAuthenticated: ?}
-+  {@param? title: ?}
-   <!DOCTYPE html>{\n}
-   <html lang="en">{\n}
-   <meta charset="utf-8">{\n}
--  <meta name="description" content="Gerrit Code Review">{\n}
-+  {if $title}<title>{$title} Β· Gerrit Code Review</title>{\n}{/if}
-+  <meta name="description" content="{if $title}{$title} Β· {/if}Gerrit Code Review">{\n}
-   <meta name="referrer" content="never">{\n}
-   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">{\n}
- 
--- 
-2.32.0
-
diff --git a/third_party/gerrit/0005-When-using-local-fonts-always-assume-Gerrit-is-mount.patch b/third_party/gerrit/0005-When-using-local-fonts-always-assume-Gerrit-is-mount.patch
deleted file mode 100644
index b664ea0ea6..0000000000
--- a/third_party/gerrit/0005-When-using-local-fonts-always-assume-Gerrit-is-mount.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From bd7db44cabb6de64f03adbaf5e24c73e022a8932 Mon Sep 17 00:00:00 2001
-From: Luke Granger-Brown <git@lukegb.com>
-Date: Sat, 11 Jul 2020 00:45:57 +0000
-Subject: [PATCH 5/7] When using local fonts, always assume Gerrit is mounted
- at the root.
-
----
- polygerrit-ui/app/rollup.config.js | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/polygerrit-ui/app/rollup.config.js b/polygerrit-ui/app/rollup.config.js
-index d93b5eab39..c862c9bbae 100644
---- a/polygerrit-ui/app/rollup.config.js
-+++ b/polygerrit-ui/app/rollup.config.js
-@@ -50,7 +50,7 @@ const importLocalFontMetaUrlResolver = function() {
-     name: 'import-meta-url-resolver',
-     resolveImportMeta: function (property, data) {
-       if(property === 'url' && data.moduleId.endsWith('/@polymer/font-roboto-local/roboto.js')) {
--        return 'new URL("..", document.baseURI).href';
-+        return 'new URL("/", document.baseURI).href';
-       }
-       return null;
-     }
--- 
-2.32.0
-
diff --git a/third_party/gerrit/0006-Always-use-Google-Fonts.patch b/third_party/gerrit/0006-Always-use-Google-Fonts.patch
deleted file mode 100644
index 5b817d0b55..0000000000
--- a/third_party/gerrit/0006-Always-use-Google-Fonts.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From d71f51afe12a280b92831070a583b15c8b6bc2f4 Mon Sep 17 00:00:00 2001
-From: Luke Granger-Brown <git@lukegb.com>
-Date: Sat, 11 Jul 2020 00:46:13 +0000
-Subject: [PATCH 6/7] Always use Google Fonts.
-
-We're not a corporate, and we're not behind the GFW. Always use Google Fonts,
-because even though we no longer get the caching benefits (boo, browsers),
-it is still a better geographically-distributed CDN.
----
- java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
-index a9cfceb3b6..9c287c6e45 100644
---- a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
-+++ b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
-@@ -184,7 +184,7 @@ public class IndexHtmlUtil {
-     if (urlParameterMap.containsKey("ce")) {
-       data.put("polyfillCE", "true");
-     }
--    if (urlParameterMap.containsKey("gf")) {
-+    if (/* urlParameterMap.containsKey("gf") || */ true) {
-       data.put("useGoogleFonts", "true");
-     }
- 
--- 
-2.32.0
-
diff --git a/third_party/gerrit/default.nix b/third_party/gerrit/default.nix
index 4873cf09b9..a137946264 100644
--- a/third_party/gerrit/default.nix
+++ b/third_party/gerrit/default.nix
@@ -1,10 +1,6 @@
 { depot, pkgs, ... }:
 
 let
-  detzip = depot.nix.buildGo.program {
-    name = "detzip";
-    srcs = [ ./detzip.go ];
-  };
   bazelRunScript = pkgs.writeShellScriptBin "bazel-run" ''
     yarn config set cache-folder "$bazelOut/external/yarn_cache"
     export HOME="$bazelOut/external/home"
@@ -14,11 +10,10 @@ let
   bazelTop = pkgs.buildFHSUserEnv {
     name = "bazel";
     targetPkgs = pkgs: [
-      (pkgs.bazel.override { enableNixHacks = true; })
-      detzip
-      pkgs.jdk11_headless
+      (pkgs.bazel_5.override { enableNixHacks = true; })
+      pkgs.jdk17_headless
       pkgs.zlib
-      pkgs.python
+      pkgs.python3
       pkgs.curl
       pkgs.nodejs
       pkgs.yarn
@@ -28,7 +23,7 @@ let
     runScript = "/bin/bazel-run";
   };
   bazel = bazelTop // { override = x: bazelTop; };
-  version = "3.4.0";
+  version = "3.9.1";
 in
 pkgs.lib.makeOverridable pkgs.buildBazelPackage {
   pname = "gerrit";
@@ -36,33 +31,31 @@ pkgs.lib.makeOverridable pkgs.buildBazelPackage {
 
   src = pkgs.fetchgit {
     url = "https://gerrit.googlesource.com/gerrit";
-    rev = "471c1c15a7bc294d10e246df43812942b5ac8a13";
+    rev = "620a819cbf3c64fff7a66798822775ad42c91d8e";
     branchName = "v${version}";
-    sha256 = "sha256:0ayj0bcsxjln8qydkj9j7yiqibmjgd3bcpqvgsdzdx072wzx01c0";
+    sha256 = "sha256:1mdxbgnx3mpxand4wq96ic38bb4yh45q271h40jrk7dk23sgmz02";
     fetchSubmodules = true;
   };
 
   patches = [
-    ./0001-Use-detzip-in-download_bower.py.patch
-    ./0002-Syntax-highlight-nix.patch
-    ./0003-Syntax-highlight-rules.pl.patch
-    ./0004-Add-titles-to-CLs-over-HTTP.patch
-    ./0005-When-using-local-fonts-always-assume-Gerrit-is-mount.patch
-    ./0006-Always-use-Google-Fonts.patch
+    ./0001-Syntax-highlight-nix.patch
+    ./0002-Syntax-highlight-rules.pl.patch
+    ./0003-Add-titles-to-CLs-over-HTTP.patch
   ];
 
-  bazelTarget = "release api-skip-javadoc";
+  bazelTargets = [ "release" "api-skip-javadoc" ];
   inherit bazel;
 
   bazelFlags = [
     "--repository_cache="
     "--disk_cache="
   ];
+
   removeRulesCC = false;
   fetchConfigured = true;
 
   fetchAttrs = {
-    sha256 = "sha256:1q4sclf18zzh8hsnccg1y7vqnhgavq62mqp4xx50zxfcnixfkpbx";
+    sha256 = "sha256:119mqli75c9fy05ddrlh2brjxb354yvv1ijjkk1y1yqcaqppwwb8";
     preBuild = ''
       rm .bazelversion
     '';
@@ -87,7 +80,7 @@ pkgs.lib.makeOverridable pkgs.buildBazelPackage {
       # Removing top-level symlinks along with their markers.
       # This is needed because they sometimes point to temporary paths (?).
       # For example, in Tensorflow-gpu build:
-      # platforms -> NIX_BUILD_TOP/tmp/install/35282f5123611afa742331368e9ae529/_embedded_binaries/platforms
+      #sha256:06bmzbcb9717s4b016kcbn8nr9pgaz04i8bnzg7ybkbdwpl8vxvv"; platforms -> NIX_BUILD_TOP/tmp/install/35282f5123611afa742331368e9ae529/_embedded_binaries/platforms
       find $bazelOut/external -maxdepth 1 -type l | while read symlink; do
         name="$(basename "$symlink")"
         rm -rf "$symlink" "$bazelOut/external/@$name.marker"
@@ -103,13 +96,17 @@ pkgs.lib.makeOverridable pkgs.buildBazelPackage {
       echo '${bazel.name}' > $bazelOut/external/.nix-bazel-version
 
       # Gerrit fixups:
-      # Remove polymer-bridges and ba-linkify, they're in-repo
-      rm -rf $bazelOut/external/yarn_cache/v6/npm-polymer-bridges-*
-      rm -rf $bazelOut/external/yarn_cache/v6/npm-ba-linkify-*
       # Normalize permissions on .yarn-{tarball,metadata} files
-      find $bazelOut/external/yarn_cache \( -name .yarn-tarball.tgz -or -name .yarn-metadata.json \) -exec chmod 644 {} +
+      test -d $bazelOut/external/yarn_cache && find $bazelOut/external/yarn_cache \( -name .yarn-tarball.tgz -or -name .yarn-metadata.json \) -exec chmod 644 {} +
+
+      mkdir $bazelOut/_bits/
+      find . -name node_modules -prune -print | while read d; do
+        echo "$d" "$(dirname $d)"
+        mkdir -p $bazelOut/_bits/$(dirname $d)
+        cp -R "$d" "$bazelOut/_bits/$(dirname $d)/node_modules"
+      done
 
-      (cd $bazelOut/ && tar czf $out --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner external/)
+      (cd $bazelOut/ && tar czf $out --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner external/ _bits/)
 
       runHook postInstall
     '';
@@ -118,6 +115,15 @@ pkgs.lib.makeOverridable pkgs.buildBazelPackage {
   buildAttrs = {
     preConfigure = ''
       rm .bazelversion
+
+      [ "$(ls -A $bazelOut/_bits)" ] && cp -R $bazelOut/_bits/* ./ || true
+    '';
+    postPatch = ''
+      # Disable all errorprone checks, since we might be using a different version.
+      sed -i \
+        -e '/-Xep:/d' \
+        -e '/-XepExcludedPaths:/a "-XepDisableAllChecks",' \
+        tools/BUILD
     '';
     installPhase = ''
       mkdir -p "$out"/webapps/ "$out"/share/api/
@@ -147,4 +153,6 @@ pkgs.lib.makeOverridable pkgs.buildBazelPackage {
       "webhooks"
     ];
   };
+
+  meta.ci.targets = [ "deps" ];
 }
diff --git a/third_party/gerrit_plugins/builder.nix b/third_party/gerrit_plugins/builder.nix
index ff1754e088..50a4e78ae7 100644
--- a/third_party/gerrit_plugins/builder.nix
+++ b/third_party/gerrit_plugins/builder.nix
@@ -1,33 +1,39 @@
-{ depot, pkgs, ... }:
+{ depot, lib, pkgs, ... }:
 {
-  buildGerritBazelPlugin = {
-    name,
-    src,
-    depsOutputHash,
-    overlayPluginCmd ? ''
-      cp -R "${src}" "$out/plugins/${name}"
-    '',
-    postPatch ? "",
-  }: ((depot.third_party.gerrit.override {
-    name = "${name}.jar";
+  buildGerritBazelPlugin =
+    { name
+    , src
+    , depsOutputHash
+    , overlayPluginCmd ? ''
+        cp -R "${src}" "$out/plugins/${name}"
+      ''
+    , postPatch ? ""
+    , patches ? [ ]
+    }: ((depot.third_party.gerrit.override {
+      name = "${name}.jar";
 
-    src = pkgs.runCommandLocal "${name}-src" {} ''
-      cp -R "${depot.third_party.gerrit.src}" "$out"
-      chmod +w "$out/plugins"
-      ${overlayPluginCmd}
-    '';
+      src = pkgs.runCommandLocal "${name}-src" { } ''
+        cp -R "${depot.third_party.gerrit.src}" "$out"
+        chmod +w "$out/plugins"
+        ${overlayPluginCmd}
+      '';
 
-    bazelTarget = "//plugins/${name}";
-  }).overrideAttrs (super: {
-    deps = super.deps.overrideAttrs (superDeps: {
-      outputHash = depsOutputHash;
-    });
-    installPhase = ''
-      cp "bazel-bin/plugins/${name}/${name}.jar" "$out"
-    '';
-    postPatch = if super ? postPatch then ''
-      ${super.postPatch}
-      ${postPatch}
-    '' else postPatch;
-  }));
+      bazelTargets = [ "//plugins/${name}" ];
+    }).overrideAttrs (super: {
+      deps = super.deps.overrideAttrs (superDeps: {
+        outputHash = depsOutputHash;
+      });
+      installPhase = ''
+        cp "bazel-bin/plugins/${name}/${name}.jar" "$out"
+      '';
+      postPatch = ''
+        ${super.postPatch or ""}
+        pushd "plugins/${name}"
+        ${lib.concatMapStringsSep "\n" (patch: ''
+          patch -p1 < ${patch}
+        '') patches}
+        popd
+        ${postPatch}
+      '';
+    }));
 }
diff --git a/third_party/gerrit_plugins/code-owners/default.nix b/third_party/gerrit_plugins/code-owners/default.nix
new file mode 100644
index 0000000000..0dc3ef83ae
--- /dev/null
+++ b/third_party/gerrit_plugins/code-owners/default.nix
@@ -0,0 +1,17 @@
+{ depot, pkgs, ... }@args:
+
+let
+  inherit (import ../builder.nix args) buildGerritBazelPlugin;
+in
+buildGerritBazelPlugin rec {
+  name = "code-owners";
+  depsOutputHash = "sha256:0jv62cc1kpgsmwk119i9njmqn6w6k8frlbgcw87y8nfbpprmcf01";
+  src = pkgs.fetchgit {
+    url = "https://gerrit.googlesource.com/plugins/code-owners";
+    rev = "e654ae5bda2085bce9a99942bec440e004a114f3";
+    sha256 = "sha256:14d3x3iqskgw16pvyaa0swh252agj84p9pzlf24l8lgx9d7y4biz";
+  };
+  patches = [
+    ./using-usernames.patch
+  ];
+}
diff --git a/third_party/gerrit_plugins/code-owners/using-usernames.patch b/third_party/gerrit_plugins/code-owners/using-usernames.patch
new file mode 100644
index 0000000000..25079ae136
--- /dev/null
+++ b/third_party/gerrit_plugins/code-owners/using-usernames.patch
@@ -0,0 +1,472 @@
+commit 29ace6c38ac513f7ec56ca425230d5712c081043
+Author: Luke Granger-Brown <git@lukegb.com>
+Date:   Wed Sep 21 03:15:38 2022 +0100
+
+    Add support for usernames and groups
+    
+    Change-Id: I3ba8527f66216d08e555a6ac4451fe0d1e090de5
+
+diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java
+index 70009591..6dc596c9 100644
+--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java
++++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java
+@@ -17,6 +17,8 @@ package com.google.gerrit.plugins.codeowners.backend;
+ import static com.google.common.base.Preconditions.checkState;
+ import static com.google.common.collect.ImmutableMap.toImmutableMap;
+ import static com.google.common.collect.ImmutableSet.toImmutableSet;
++import static com.google.common.collect.ImmutableSetMultimap.flatteningToImmutableSetMultimap;
++import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
+ import static com.google.gerrit.plugins.codeowners.backend.CodeOwnersInternalServerErrorException.newInternalServerError;
+ import static java.util.Objects.requireNonNull;
+ 
+@@ -25,6 +27,7 @@ import com.google.common.collect.ImmutableList;
+ import com.google.common.collect.ImmutableMap;
+ import com.google.common.collect.ImmutableMultimap;
+ import com.google.common.collect.ImmutableSet;
++import com.google.common.collect.ImmutableSetMultimap;
+ import com.google.common.collect.Iterables;
+ import com.google.common.collect.Streams;
+ import com.google.common.flogger.FluentLogger;
+@@ -33,17 +36,24 @@ import com.google.gerrit.entities.Project;
+ import com.google.gerrit.metrics.Timer0;
+ import com.google.gerrit.plugins.codeowners.backend.config.CodeOwnersPluginConfiguration;
+ import com.google.gerrit.plugins.codeowners.metrics.CodeOwnerMetrics;
++import com.google.gerrit.server.AnonymousUser;
+ import com.google.gerrit.server.CurrentUser;
+ import com.google.gerrit.server.IdentifiedUser;
+ import com.google.gerrit.server.account.AccountCache;
+ import com.google.gerrit.server.account.AccountControl;
+ import com.google.gerrit.server.account.AccountState;
++import com.google.gerrit.server.account.GroupBackend;
++import com.google.gerrit.server.account.GroupBackends;
++import com.google.gerrit.server.account.InternalGroupBackend;
+ import com.google.gerrit.server.account.externalids.ExternalId;
+ import com.google.gerrit.server.account.externalids.ExternalIdCache;
+ import com.google.gerrit.server.permissions.GlobalPermission;
+ import com.google.gerrit.server.permissions.PermissionBackend;
+ import com.google.gerrit.server.permissions.PermissionBackendException;
++import com.google.gerrit.server.util.RequestContext;
++import com.google.gerrit.server.util.ThreadLocalRequestContext;
+ import com.google.inject.Inject;
++import com.google.inject.OutOfScopeException;
+ import com.google.inject.Provider;
+ import java.io.IOException;
+ import java.nio.file.Path;
+@@ -102,6 +112,8 @@ public class CodeOwnerResolver {
+ 
+   @VisibleForTesting public static final String ALL_USERS_WILDCARD = "*";
+ 
++  public static final String GROUP_PREFIX = "group:";
++
+   private final CodeOwnersPluginConfiguration codeOwnersPluginConfiguration;
+   private final PermissionBackend permissionBackend;
+   private final Provider<CurrentUser> currentUser;
+@@ -112,6 +124,8 @@ public class CodeOwnerResolver {
+   private final CodeOwnerMetrics codeOwnerMetrics;
+   private final UnresolvedImportFormatter unresolvedImportFormatter;
+   private final TransientCodeOwnerCache transientCodeOwnerCache;
++  private final InternalGroupBackend groupBackend;
++  private final ThreadLocalRequestContext context;
+ 
+   // Enforce visibility by default.
+   private boolean enforceVisibility = true;
+@@ -132,7 +146,9 @@ public class CodeOwnerResolver {
+       PathCodeOwners.Factory pathCodeOwnersFactory,
+       CodeOwnerMetrics codeOwnerMetrics,
+       UnresolvedImportFormatter unresolvedImportFormatter,
+-      TransientCodeOwnerCache transientCodeOwnerCache) {
++      TransientCodeOwnerCache transientCodeOwnerCache,
++      InternalGroupBackend groupBackend,
++      ThreadLocalRequestContext context) {
+     this.codeOwnersPluginConfiguration = codeOwnersPluginConfiguration;
+     this.permissionBackend = permissionBackend;
+     this.currentUser = currentUser;
+@@ -143,6 +159,8 @@ public class CodeOwnerResolver {
+     this.codeOwnerMetrics = codeOwnerMetrics;
+     this.unresolvedImportFormatter = unresolvedImportFormatter;
+     this.transientCodeOwnerCache = transientCodeOwnerCache;
++    this.groupBackend = groupBackend;
++    this.context = context;
+   }
+ 
+   /**
+@@ -361,6 +379,12 @@ public class CodeOwnerResolver {
+               "cannot resolve code owner email %s: no account with this email exists",
+               CodeOwnerResolver.ALL_USERS_WILDCARD));
+     }
++    if (codeOwnerReference.email().startsWith(GROUP_PREFIX)) {
++      return OptionalResultWithMessages.createEmpty(
++          String.format(
++              "cannot resolve code owner email %s: this is a group",
++              codeOwnerReference.email()));
++    }
+ 
+     ImmutableList.Builder<String> messageBuilder = ImmutableList.builder();
+     AtomicBoolean ownedByAllUsers = new AtomicBoolean(false);
+@@ -405,9 +429,53 @@ public class CodeOwnerResolver {
+       ImmutableMultimap<CodeOwnerReference, CodeOwnerAnnotation> annotations) {
+     requireNonNull(codeOwnerReferences, "codeOwnerReferences");
+ 
++    ImmutableSet<String> groupsToResolve =
++        codeOwnerReferences.stream()
++            .map(CodeOwnerReference::email)
++            .filter(ref -> ref.startsWith(GROUP_PREFIX))
++            .map(ref -> ref.substring(GROUP_PREFIX.length()))
++            .collect(toImmutableSet());
++
++    // When we call GroupBackends.findExactSuggestion we need to ensure that we
++    // have a user in context.  This is because the suggestion backend is
++    // likely to want to try to check that we can actually see the group it's
++    // returning (which we also check for explicitly, because I have trust
++    // issues).
++    RequestContext oldCtx = context.getContext();
++    // Check if we have a user in the context at all...
++    try {
++      oldCtx.getUser();
++    } catch (OutOfScopeException | NullPointerException e) {
++      // Nope.
++      RequestContext newCtx = () -> {
++        return new AnonymousUser();
++      };
++      context.setContext(newCtx);
++    }
++    ImmutableSetMultimap<String, CodeOwner> resolvedGroups = null;
++    try {
++      resolvedGroups =
++          groupsToResolve.stream()
++              .map(groupName -> GroupBackends.findExactSuggestion(groupBackend, groupName))
++              .filter(groupRef -> groupRef != null)
++              .filter(groupRef -> groupBackend.isVisibleToAll(groupRef.getUUID()))
++              .map(groupRef -> groupBackend.get(groupRef.getUUID()))
++              .collect(flatteningToImmutableSetMultimap(
++                    groupRef -> GROUP_PREFIX + groupRef.getName(),
++                    groupRef -> accountCache
++                        .get(ImmutableSet.copyOf(groupRef.getMembers()))
++                        .values().stream()
++                        .map(accountState -> CodeOwner.create(accountState.account().id()))));
++    } finally {
++      context.setContext(oldCtx);
++    }
++    ImmutableSetMultimap<CodeOwner, String> usersToGroups =
++        resolvedGroups.inverse();
++
+     ImmutableSet<String> emailsToResolve =
+         codeOwnerReferences.stream()
+             .map(CodeOwnerReference::email)
++            .filter(ref -> !ref.startsWith(GROUP_PREFIX))
+             .filter(filterOutAllUsersWildCard(ownedByAllUsers))
+             .collect(toImmutableSet());
+ 
+@@ -442,7 +510,8 @@ public class CodeOwnerResolver {
+     ImmutableMap<String, CodeOwner> codeOwnersByEmail =
+         accountsByEmail.map(mapToCodeOwner()).collect(toImmutableMap(Pair::key, Pair::value));
+ 
+-    if (codeOwnersByEmail.keySet().size() < emailsToResolve.size()) {
++    if (codeOwnersByEmail.keySet().size() < emailsToResolve.size() ||
++        resolvedGroups.keySet().size() < groupsToResolve.size()) {
+       hasUnresolvedCodeOwners.set(true);
+     }
+ 
+@@ -456,7 +525,9 @@ public class CodeOwnerResolver {
+         cachedCodeOwnersByEmail.entrySet().stream()
+             .filter(e -> e.getValue().isPresent())
+             .map(e -> Pair.of(e.getKey(), e.getValue().get()));
+-    Streams.concat(newlyResolvedCodeOwnersStream, cachedCodeOwnersStream)
++    Stream<Pair<String, CodeOwner>> resolvedGroupsCodeOwnersStream =
++        resolvedGroups.entries().stream().map(e -> Pair.of(e.getKey(), e.getValue()));
++    Streams.concat(Streams.concat(newlyResolvedCodeOwnersStream, cachedCodeOwnersStream), resolvedGroupsCodeOwnersStream)
+         .forEach(
+             p -> {
+               ImmutableSet.Builder<CodeOwnerAnnotation> annotationBuilder = ImmutableSet.builder();
+@@ -467,6 +538,12 @@ public class CodeOwnerResolver {
+               annotationBuilder.addAll(
+                   annotations.get(CodeOwnerReference.create(ALL_USERS_WILDCARD)));
+ 
++              // annotations for the groups this user is in apply as well
++              for (String group : usersToGroups.get(p.value())) {
++                annotationBuilder.addAll(
++                    annotations.get(CodeOwnerReference.create(group)));
++              }
++
+               if (!codeOwnersWithAnnotations.containsKey(p.value())) {
+                 codeOwnersWithAnnotations.put(p.value(), new HashSet<>());
+               }
+@@ -570,7 +647,7 @@ public class CodeOwnerResolver {
+     }
+ 
+     messages.add(String.format("email %s has no domain", email));
+-    return false;
++    return true;  // TVL: we allow domain-less strings which are treated as usernames.
+   }
+ 
+   /**
+@@ -585,11 +662,29 @@ public class CodeOwnerResolver {
+    */
+   private ImmutableMap<String, Collection<ExternalId>> lookupExternalIds(
+       ImmutableList.Builder<String> messages, ImmutableSet<String> emails) {
++    String[] actualEmails = emails.stream()
++      .filter(email -> email.contains("@"))
++      .toArray(String[]::new);
++    ImmutableSet<String> usernames = emails.stream()
++      .filter(email -> !email.contains("@"))
++      .collect(ImmutableSet.toImmutableSet());
+     try {
+-      ImmutableMap<String, Collection<ExternalId>> extIdsByEmail =
+-          externalIdCache.byEmails(emails.toArray(new String[0])).asMap();
++      ImmutableMap<String, Collection<ExternalId>> extIds =
++          new ImmutableMap.Builder<String, Collection<ExternalId>>()
++              .putAll(externalIdCache.byEmails(actualEmails).asMap())
++              .putAll(externalIdCache.allByAccount().entries().stream()
++                  .map(entry -> entry.getValue())
++                  .filter(externalId ->
++                      externalId.key().scheme() != null &&
++                      externalId.key().isScheme(ExternalId.SCHEME_USERNAME) &&
++                      usernames.contains(externalId.key().id()))
++                  .collect(toImmutableSetMultimap(
++                      externalId -> externalId.key().id(),
++                      externalId -> externalId))
++                  .asMap())
++              .build();
+       emails.stream()
+-          .filter(email -> !extIdsByEmail.containsKey(email))
++          .filter(email -> !extIds.containsKey(email))
+           .forEach(
+               email -> {
+                 transientCodeOwnerCache.cacheNonResolvable(email);
+@@ -598,7 +693,7 @@ public class CodeOwnerResolver {
+                         "cannot resolve code owner email %s: no account with this email exists",
+                         email));
+               });
+-      return extIdsByEmail;
++      return extIds;
+     } catch (IOException e) {
+       throw newInternalServerError(
+           String.format("cannot resolve code owner emails: %s", emails), e);
+@@ -815,6 +910,15 @@ public class CodeOwnerResolver {
+                 user != null ? user.getLoggableName() : currentUser.get().getLoggableName()));
+         return true;
+       }
++      if (!email.contains("@")) {
++        // the email is the username of the account, or a group, or something else.
++        messages.add(
++            String.format(
++                "account %s is visible to user %s",
++                accountState.account().id(),
++                user != null ? user.getLoggableName() : currentUser.get().getLoggableName()));
++        return true;
++      }
+ 
+       if (user != null) {
+         if (user.hasEmailAddress(email)) {
+diff --git a/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParser.java b/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParser.java
+index 5f350998..7977ba55 100644
+--- a/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParser.java
++++ b/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParser.java
+@@ -149,7 +149,8 @@ public class FindOwnersCodeOwnerConfigParser implements CodeOwnerConfigParser {
+     private static final String EOL = "[\\s]*(#.*)?$"; // end-of-line
+     private static final String GLOB = "[^\\s,=]+"; // a file glob
+ 
+-    private static final String EMAIL_OR_STAR = "([^\\s<>@,]+@[^\\s<>@#,]+|\\*)";
++    // Also allows usernames, and group:$GROUP_NAME.
++    private static final String EMAIL_OR_STAR = "([^\\s<>@,]+@[^\\s<>@#,]+?|\\*|[a-zA-Z0-9_\\-]+|group:[a-zA-Z0-9_\\-]+)";
+     private static final String EMAIL_LIST =
+         "(" + EMAIL_OR_STAR + "(" + COMMA + EMAIL_OR_STAR + ")*)";
+ 
+diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackendTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackendTest.java
+index 7ec92959..59cf7e05 100644
+--- a/javatests/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackendTest.java
++++ b/javatests/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackendTest.java
+@@ -424,7 +424,7 @@ public abstract class AbstractFileBasedCodeOwnerBackendTest extends AbstractCode
+               .commit()
+               .parent(head)
+               .message("Add invalid test code owner config")
+-              .add(JgitPath.of(codeOwnerConfigKey.filePath(getFileName())).get(), "INVALID"));
++              .add(JgitPath.of(codeOwnerConfigKey.filePath(getFileName())).get(), "INVALID!"));
+     }
+ 
+     // Try to update the code owner config.
+diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverTest.java
+index 6171aca9..37699012 100644
+--- a/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverTest.java
++++ b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverTest.java
+@@ -24,8 +24,10 @@ import com.google.gerrit.acceptance.TestAccount;
+ import com.google.gerrit.acceptance.TestMetricMaker;
+ import com.google.gerrit.acceptance.config.GerritConfig;
+ import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
++import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
+ import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+ import com.google.gerrit.entities.Account;
++import com.google.gerrit.entities.AccountGroup;
+ import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersTest;
+ import com.google.gerrit.server.ServerInitiated;
+ import com.google.gerrit.server.account.AccountsUpdate;
+@@ -51,6 +53,7 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
+   @Inject private RequestScopeOperations requestScopeOperations;
+   @Inject @ServerInitiated private Provider<AccountsUpdate> accountsUpdate;
+   @Inject private AccountOperations accountOperations;
++  @Inject private GroupOperations groupOperations;
+   @Inject private ExternalIdNotes.Factory externalIdNotesFactory;
+   @Inject private TestMetricMaker testMetricMaker;
+   @Inject private ExternalIdFactory externalIdFactory;
+@@ -112,6 +115,18 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
+         .contains(String.format("account %s is visible to user %s", admin.id(), admin.username()));
+   }
+ 
++  @Test
++  public void resolveCodeOwnerReferenceForUsername() throws Exception {
++    OptionalResultWithMessages<CodeOwner> result =
++        codeOwnerResolverProvider
++            .get()
++            .resolveWithMessages(CodeOwnerReference.create(admin.username()));
++    assertThat(result.get()).hasAccountIdThat().isEqualTo(admin.id());
++    assertThat(result)
++        .hasMessagesThat()
++        .contains(String.format("account %s is visible to user %s", admin.id(), admin.username()));
++  }
++
+   @Test
+   public void cannotResolveCodeOwnerReferenceForStarAsEmail() throws Exception {
+     OptionalResultWithMessages<CodeOwner> result =
+@@ -127,6 +142,18 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
+                 CodeOwnerResolver.ALL_USERS_WILDCARD));
+   }
+ 
++  @Test
++  public void cannotResolveCodeOwnerReferenceForGroup() throws Exception {
++    OptionalResultWithMessages<CodeOwner> result =
++        codeOwnerResolverProvider
++            .get()
++            .resolveWithMessages(CodeOwnerReference.create("group:Administrators"));
++    assertThat(result).isEmpty();
++    assertThat(result)
++        .hasMessagesThat()
++        .contains("cannot resolve code owner email group:Administrators: this is a group");
++  }
++
+   @Test
+   public void resolveCodeOwnerReferenceForAmbiguousEmailIfOtherAccountIsInactive()
+       throws Exception {
+@@ -397,6 +424,64 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
+     assertThat(result.hasUnresolvedCodeOwners()).isFalse();
+   }
+ 
++  @Test
++  public void resolvePathCodeOwnersWhenNonVisibleGroupIsUsed() throws Exception {
++    CodeOwnerConfig codeOwnerConfig =
++        CodeOwnerConfig.builder(CodeOwnerConfig.Key.create(project, "master", "/"), TEST_REVISION)
++            .addCodeOwnerSet(
++                CodeOwnerSet.createWithoutPathExpressions("group:Administrators"))
++            .build();
++
++    CodeOwnerResolverResult result =
++        codeOwnerResolverProvider
++            .get()
++            .resolvePathCodeOwners(codeOwnerConfig, Paths.get("/README.md"));
++    assertThat(result.codeOwnersAccountIds()).isEmpty();
++    assertThat(result.ownedByAllUsers()).isFalse();
++    assertThat(result.hasUnresolvedCodeOwners()).isTrue();
++  }
++
++  @Test
++  public void resolvePathCodeOwnersWhenVisibleGroupIsUsed() throws Exception {
++    AccountGroup.UUID createdGroupUUID = groupOperations
++        .newGroup()
++        .name("VisibleGroup")
++        .visibleToAll(true)
++        .addMember(admin.id())
++        .create();
++
++    CodeOwnerConfig codeOwnerConfig =
++        CodeOwnerConfig.builder(CodeOwnerConfig.Key.create(project, "master", "/"), TEST_REVISION)
++            .addCodeOwnerSet(
++                CodeOwnerSet.createWithoutPathExpressions("group:VisibleGroup"))
++            .build();
++
++    CodeOwnerResolverResult result =
++        codeOwnerResolverProvider
++            .get()
++            .resolvePathCodeOwners(codeOwnerConfig, Paths.get("/README.md"));
++    assertThat(result.codeOwnersAccountIds()).containsExactly(admin.id());
++    assertThat(result.ownedByAllUsers()).isFalse();
++    assertThat(result.hasUnresolvedCodeOwners()).isFalse();
++  }
++
++  @Test
++  public void resolvePathCodeOwnersWhenUsernameIsUsed() throws Exception {
++    CodeOwnerConfig codeOwnerConfig =
++        CodeOwnerConfig.builder(CodeOwnerConfig.Key.create(project, "master", "/"), TEST_REVISION)
++            .addCodeOwnerSet(
++                CodeOwnerSet.createWithoutPathExpressions(admin.username()))
++            .build();
++
++    CodeOwnerResolverResult result =
++        codeOwnerResolverProvider
++            .get()
++            .resolvePathCodeOwners(codeOwnerConfig, Paths.get("/README.md"));
++    assertThat(result.codeOwnersAccountIds()).containsExactly(admin.id());
++    assertThat(result.ownedByAllUsers()).isFalse();
++    assertThat(result.hasUnresolvedCodeOwners()).isFalse();
++  }
++
+   @Test
+   public void resolvePathCodeOwnersNonResolvableCodeOwnersAreFilteredOut() throws Exception {
+     CodeOwnerConfig codeOwnerConfig =
+@@ -655,7 +740,7 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
+         "domain example.com of email foo@example.org@example.com is allowed");
+     assertIsEmailDomainAllowed(
+         "foo@example.org", false, "domain example.org of email foo@example.org is not allowed");
+-    assertIsEmailDomainAllowed("foo", false, "email foo has no domain");
++    assertIsEmailDomainAllowed("foo", true, "email foo has no domain");
+     assertIsEmailDomainAllowed(
+         "foo@example.com@example.org",
+         false,
+diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParserTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParserTest.java
+index 260e635e..7aab99d0 100644
+--- a/javatests/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParserTest.java
++++ b/javatests/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParserTest.java
+@@ -158,16 +158,42 @@ public class FindOwnersCodeOwnerConfigParserTest extends AbstractCodeOwnerConfig
+                 codeOwnerConfigParser.parse(
+                     TEST_REVISION,
+                     CodeOwnerConfig.Key.create(project, "master", "/"),
+-                    getCodeOwnerConfig(EMAIL_1, "INVALID", "NOT_AN_EMAIL", EMAIL_2)));
++                    getCodeOwnerConfig(EMAIL_1, "INVALID!", "NOT!AN_EMAIL", EMAIL_2)));
+     assertThat(exception.getFullMessage(FindOwnersBackend.CODE_OWNER_CONFIG_FILE_NAME))
+         .isEqualTo(
+             String.format(
+                 "invalid code owner config file '/OWNERS' (project = %s, branch = master):\n"
+-                    + "  invalid line: INVALID\n"
+-                    + "  invalid line: NOT_AN_EMAIL",
++                    + "  invalid line: INVALID!\n"
++                    + "  invalid line: NOT!AN_EMAIL",
+                 project));
+   }
+ 
++  @Test
++  public void codeOwnerConfigWithUsernames() throws Exception {
++    assertParseAndFormat(
++        getCodeOwnerConfig(EMAIL_1, "USERNAME", EMAIL_2),
++        codeOwnerConfig ->
++            assertThat(codeOwnerConfig)
++                .hasCodeOwnerSetsThat()
++                .onlyElement()
++                .hasCodeOwnersEmailsThat()
++                .containsExactly(EMAIL_1, "USERNAME", EMAIL_2),
++        getCodeOwnerConfig(EMAIL_1, "USERNAME", EMAIL_2));
++  }
++
++  @Test
++  public void codeOwnerConfigWithGroups() throws Exception {
++    assertParseAndFormat(
++        getCodeOwnerConfig(EMAIL_1, "group:tvl-employees", EMAIL_2),
++        codeOwnerConfig ->
++            assertThat(codeOwnerConfig)
++                .hasCodeOwnerSetsThat()
++                .onlyElement()
++                .hasCodeOwnersEmailsThat()
++                .containsExactly(EMAIL_1, "group:tvl-employees", EMAIL_2),
++        getCodeOwnerConfig(EMAIL_1, "group:tvl-employees", EMAIL_2));
++  }
++
+   @Test
+   public void codeOwnerConfigWithComment() throws Exception {
+     assertParseAndFormat(
diff --git a/third_party/gerrit_plugins/default.nix b/third_party/gerrit_plugins/default.nix
deleted file mode 100644
index 8131ca2eb0..0000000000
--- a/third_party/gerrit_plugins/default.nix
+++ /dev/null
@@ -1,22 +0,0 @@
-{ depot, pkgs, ... }@args:
-
-let
-  inherit (import ./builder.nix args) buildGerritBazelPlugin;
-in depot.nix.readTree.drvTargets {
-  # https://gerrit.googlesource.com/plugins/owners
-  owners = buildGerritBazelPlugin rec {
-    name = "owners";
-    depsOutputHash = "sha256:0qx3675lkj241c1sqs6xia5jpcwha2ib3mv32cilmh0k3cwdyyh2";
-    src = pkgs.fetchgit {
-      url = "https://gerrit.googlesource.com/plugins/owners";
-      rev = "99a9ab585532d172d141b4641dfc70081513dfc2";
-      sha256 = "sha256:1xn9qb7q94jxfx7yq0zjqjm16gfyzzif13sak9x6j4f9r68frcd4";
-    };
-    overlayPluginCmd = ''
-      chmod +w "$out" "$out/plugins/external_plugin_deps.bzl"
-      cp -R "${src}/owners" "$out/plugins/owners"
-      cp "${src}/external_plugin_deps.bzl" "$out/plugins/external_plugin_deps.bzl"
-      cp -R "${src}/owners-common" "$out/owners-common"
-    '';
-  };
-}
diff --git a/third_party/gerrit_plugins/oauth/cas-6x.patch b/third_party/gerrit_plugins/oauth/cas-6x.patch
deleted file mode 100644
index 7494298b3f..0000000000
--- a/third_party/gerrit_plugins/oauth/cas-6x.patch
+++ /dev/null
@@ -1,69 +0,0 @@
-diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/CasApi.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/CasApi.java
-index 450549f..27310cd 100644
---- a/src/main/java/com/googlesource/gerrit/plugins/oauth/CasApi.java
-+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/CasApi.java
-@@ -15,7 +15,7 @@
- package com.googlesource.gerrit.plugins.oauth;
- 
- import com.github.scribejava.core.builder.api.DefaultApi20;
--import com.github.scribejava.core.extractors.OAuth2AccessTokenExtractor;
-+import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor;
- import com.github.scribejava.core.extractors.TokenExtractor;
- import com.github.scribejava.core.model.OAuth2AccessToken;
- import com.github.scribejava.core.oauth2.bearersignature.BearerSignature;
-@@ -47,6 +47,6 @@ public class CasApi extends DefaultApi20 {
- 
-   @Override
-   public TokenExtractor<OAuth2AccessToken> getAccessTokenExtractor() {
--    return OAuth2AccessTokenExtractor.instance();
-+    return OAuth2AccessTokenJsonExtractor.instance();
-   }
- }
-diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/CasOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/CasOAuthService.java
-index 5f3e4a1..fc5bc50 100644
---- a/src/main/java/com/googlesource/gerrit/plugins/oauth/CasOAuthService.java
-+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/CasOAuthService.java
-@@ -106,36 +106,14 @@ class CasOAuthService implements OAuthServiceProvider {
-         throw new IOException(String.format("CAS response missing id: %s", response.getBody()));
-       }
- 
--      JsonElement attrListJson = jsonObject.get("attributes");
--      if (attrListJson == null) {
--        throw new IOException(
--            String.format("CAS response missing attributes: %s", response.getBody()));
--      }
--
-       String email = null, name = null, login = null;
--      if (attrListJson.isJsonArray()) {
--        // It is possible for CAS to be configured to not return any attributes (email, name,
--        // login),
--        // in which case,
--        // CAS returns an empty JSON object "attributes":{}, rather than "null" or an empty JSON
--        // array
--        // "attributes": []
--
--        JsonArray attrJson = attrListJson.getAsJsonArray();
--        for (JsonElement elem : attrJson) {
--          if (elem == null || !elem.isJsonObject()) {
--            throw new IOException(String.format("Invalid JSON '%s': not a JSON Object", elem));
--          }
--          JsonObject obj = elem.getAsJsonObject();
--
--          String property = getStringElement(obj, "email");
--          if (property != null) email = property;
--          property = getStringElement(obj, "name");
--          if (property != null) name = property;
--          property = getStringElement(obj, "login");
--          if (property != null) login = property;
--        }
--      }
-+
-+      String property = getStringElement(jsonObject, "mail");
-+      if (property != null) email = property;
-+      property = getStringElement(jsonObject, "displayName");
-+      if (property != null) name = property;
-+      property = getStringElement(jsonObject, "uid");
-+      if (property != null) login = property;
- 
-       return new OAuthUserInfo(
-           CAS_PROVIDER_PREFIX + id.getAsString(),
diff --git a/third_party/gerrit_plugins/oauth/default.nix b/third_party/gerrit_plugins/oauth/default.nix
index 76c053ae42..e7626fa88c 100644
--- a/third_party/gerrit_plugins/oauth/default.nix
+++ b/third_party/gerrit_plugins/oauth/default.nix
@@ -2,25 +2,18 @@
 
 let
   inherit (import ../builder.nix args) buildGerritBazelPlugin;
-in buildGerritBazelPlugin rec {
+in
+buildGerritBazelPlugin rec {
   name = "oauth";
-  depsOutputHash = "sha256:0j86amkw54y177s522hc988hqg034fsrkywbsb9a7h14zwcqbran";
+  depsOutputHash = "sha256:01z7rn8hnms3cp7mq27yk063lpy4pmqwpfrcc3cfl0r43k889zz3";
   src = pkgs.fetchgit {
     url = "https://gerrit.googlesource.com/plugins/oauth";
-    rev = "4aa7322db5ec221b2419e12a9ec7af5b8c66659c";
-    sha256 = "1szra3pjl0axf4a7k96flpk7rhfvp37rdxay4gbglh939gzbba88";
+    rev = "b27cf3ea820eec2ddd22d217fc839261692ccdb0";
+    sha256 = "1m654ibgzprrhcl0wpzqrmq8drpgx6rzlw0ha16l1fi2zv5idkk2";
   };
   overlayPluginCmd = ''
     chmod +w "$out" "$out/plugins/external_plugin_deps.bzl"
     cp -R "${src}" "$out/plugins/${name}"
     cp "${src}/external_plugin_deps.bzl" "$out/plugins/external_plugin_deps.bzl"
   '';
-
-  # The code in the OAuth repo expects CAS to return oauth2 access tokens as urlencoded.
-  # Our version of CAS returns them as JSON instead.
-  postPatch = ''
-    pushd plugins/oauth
-    patch -p1 <${./cas-6x.patch}
-    popd
-  '';
 }
diff --git a/third_party/git/0001-feat-third_party-git-date-add-dottime-format.patch b/third_party/git/0001-feat-third_party-git-date-add-dottime-format.patch
index 0e11b2ca63..a249b1ce1b 100644
--- a/third_party/git/0001-feat-third_party-git-date-add-dottime-format.patch
+++ b/third_party/git/0001-feat-third_party-git-date-add-dottime-format.patch
@@ -1,4 +1,4 @@
-From 46c52e6b462f9b1c9aa08a1b1bba79dd16302a9c Mon Sep 17 00:00:00 2001
+From 2fd675c5379dcfa7a2c3465e325cdea8faa2b95c Mon Sep 17 00:00:00 2001
 From: Vincent Ambo <tazjin@google.com>
 Date: Mon, 6 Jan 2020 16:00:52 +0000
 Subject: [PATCH] feat(third_party/git/date): add "dottime" format
@@ -16,16 +16,16 @@ a strftime formatting string is not sufficient.
 ---
  Documentation/rev-list-options.txt |  3 +++
  builtin/blame.c                    |  3 +++
- cache.h                            |  3 ++-
  date.c                             | 17 +++++++++++++++++
+ date.h                             |  3 ++-
  t/t0006-date.sh                    |  2 ++
  5 files changed, 27 insertions(+), 1 deletion(-)
 
 diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
-index b7bd27e171..273971bdd0 100644
+index fd4f4e26c9..1f7ab97865 100644
 --- a/Documentation/rev-list-options.txt
 +++ b/Documentation/rev-list-options.txt
-@@ -1052,6 +1052,9 @@ omitted.
+@@ -1054,6 +1054,9 @@ omitted.
  1970).  As with `--raw`, this is always in UTC and therefore `-local`
  has no effect.
  
@@ -33,13 +33,13 @@ index b7bd27e171..273971bdd0 100644
 +but suffixed with the local timezone offset if given)
 +
  `--date=format:...` feeds the format `...` to your system `strftime`,
- except for %z and %Z, which are handled internally.
+ except for %s, %z, and %Z, which are handled internally.
  Use `--date=format:%c` to show the date in your system locale's
 diff --git a/builtin/blame.c b/builtin/blame.c
-index 641523ff9a..fa0240237c 100644
+index 8d15b68afc..e0cdf418f5 100644
 --- a/builtin/blame.c
 +++ b/builtin/blame.c
-@@ -1005,6 +1005,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
+@@ -1009,6 +1009,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
  	case DATE_STRFTIME:
  		blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */
  		break;
@@ -49,22 +49,8 @@ index 641523ff9a..fa0240237c 100644
  	}
  	blame_date_width -= 1; /* strip the null */
  
-diff --git a/cache.h b/cache.h
-index 0c245d4f10..fa79e37b49 100644
---- a/cache.h
-+++ b/cache.h
-@@ -1570,7 +1570,8 @@ enum date_mode_type {
- 	DATE_RFC2822,
- 	DATE_STRFTIME,
- 	DATE_RAW,
--	DATE_UNIX
-+	DATE_UNIX,
-+	DATE_DOTTIME
- };
- 
- struct date_mode {
 diff --git a/date.c b/date.c
-index c55ea47e96..7fe4fb982b 100644
+index 68a260c214..1485e3808f 100644
 --- a/date.c
 +++ b/date.c
 @@ -347,6 +347,21 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
@@ -98,11 +84,25 @@ index c55ea47e96..7fe4fb982b 100644
  	/*
  	 * Please update $__git_log_date_formats in
  	 * git-completion.bash when you add new formats.
+diff --git a/date.h b/date.h
+index 5d4eaba0a9..ff8fdffdbf 100644
+--- a/date.h
++++ b/date.h
+@@ -17,7 +17,8 @@ enum date_mode_type {
+ 	DATE_RFC2822,
+ 	DATE_STRFTIME,
+ 	DATE_RAW,
+-	DATE_UNIX
++	DATE_UNIX,
++	DATE_DOTTIME
+ };
+ 
+ struct date_mode {
 diff --git a/t/t0006-date.sh b/t/t0006-date.sh
-index 6b757d7169..5deb558497 100755
+index 2490162071..7ce4fe1927 100755
 --- a/t/t0006-date.sh
 +++ b/t/t0006-date.sh
-@@ -49,9 +49,11 @@ check_show short "$TIME" '2016-06-15'
+@@ -51,9 +51,11 @@ check_show short "$TIME" '2016-06-15'
  check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
  check_show raw "$TIME" '1466000000 +0200'
  check_show unix "$TIME" '1466000000'
@@ -115,5 +115,5 @@ index 6b757d7169..5deb558497 100755
  check_show 'format:%z' "$TIME" '+0200'
  check_show 'format-local:%z' "$TIME" '+0000'
 -- 
-2.32.0
+2.35.3
 
diff --git a/third_party/git/default.nix b/third_party/git/default.nix
index 75131c03c2..eed07b5616 100644
--- a/third_party/git/default.nix
+++ b/third_party/git/default.nix
@@ -2,8 +2,8 @@
 # `pkgs.srcOnly`.
 { pkgs, ... }:
 
-pkgs.git.overrideAttrs(old: {
-  patches = (old.patches or []) ++ [
+pkgs.git.overrideAttrs (old: {
+  patches = (old.patches or [ ]) ++ [
     ./0001-feat-third_party-git-date-add-dottime-format.patch
   ];
 })
diff --git a/third_party/gitignoreSource/default.nix b/third_party/gitignoreSource/default.nix
index 8bdd974e8d..150de7c990 100644
--- a/third_party/gitignoreSource/default.nix
+++ b/third_party/gitignoreSource/default.nix
@@ -1,14 +1,17 @@
 { pkgs, ... }:
 
 let
-  gitignoreNix = import (pkgs.fetchFromGitHub {
-    owner = "hercules-ci";
-    repo = "gitignore";
-    rev = "f9e996052b5af4032fe6150bba4a6fe4f7b9d698";
-    sha256 = "0jrh5ghisaqdd0vldbywags20m2cxpkbbk5jjjmwaw0gr8nhsafv";
-  }) { inherit (pkgs) lib; };
+  gitignoreNix = import
+    (pkgs.fetchFromGitHub {
+      owner = "hercules-ci";
+      repo = "gitignore";
+      rev = "f9e996052b5af4032fe6150bba4a6fe4f7b9d698";
+      sha256 = "0jrh5ghisaqdd0vldbywags20m2cxpkbbk5jjjmwaw0gr8nhsafv";
+    })
+    { inherit (pkgs) lib; };
 
-in {
+in
+{
   __functor = _: gitignoreNix.gitignoreSource;
 
   # expose extra functions here
diff --git a/third_party/glog/.bazelci/presubmit.yml b/third_party/glog/.bazelci/presubmit.yml
deleted file mode 100644
index 38e8515e9a..0000000000
--- a/third_party/glog/.bazelci/presubmit.yml
+++ /dev/null
@@ -1,25 +0,0 @@
----
-platforms:
-  ubuntu1604:
-    build_targets:
-    - "..."
-    test_targets:
-    - "..."
-  ubuntu1804:
-    build_targets:
-    - "..."
-    test_targets:
-    - "..."
-  macos:
-    build_targets:
-    - "..."
-    test_targets:
-    - "..."
-  windows:
-    # Optional: use VS 2017 instead of 2015.
-    environment:
-      BAZEL_VC: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\VC"
-    build_targets:
-    - "..."
-    test_targets:
-    - "..."
diff --git a/third_party/glog/.gitignore b/third_party/glog/.gitignore
deleted file mode 100644
index b3158cf319..0000000000
--- a/third_party/glog/.gitignore
+++ /dev/null
@@ -1,12 +0,0 @@
-/*.cmake
-/*.filters
-/*.sln
-/*.vcxproj
-autom4te.cache
-bazel-*
-CMakeCache.txt
-CMakeFiles/
-config.h
-glog-*.tar.gz
-packages/debian-*
-packages/rpm-unknown
diff --git a/third_party/glog/.travis.ubuntu.sh b/third_party/glog/.travis.ubuntu.sh
deleted file mode 100755
index 1043f31ef9..0000000000
--- a/third_party/glog/.travis.ubuntu.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-set -e
-set -x
-
-uname -a
-
-cmake -H. -B_build_${TOOLCHAIN} -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/${TOOLCHAIN}.cmake"
-cmake --build _build_${TOOLCHAIN} -- -j4
-
-if [ "$RUN_TESTS" = true ]; then
-	case "$TOOLCHAIN" in linux-mingw*)
-		echo "copy runtime libraries needed for tests into build directory"
-		cp /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll /usr/lib/gcc/x86_64-w64-mingw32/7.3-win32/{libstdc++-6.dll,libgcc_s_seh-1.dll} _build_${TOOLCHAIN}
-	esac
-	CTEST_OUTPUT_ON_FAILURE=1 cmake --build _build_${TOOLCHAIN} --target test
-fi
-
-
diff --git a/third_party/glog/.travis.yml b/third_party/glog/.travis.yml
deleted file mode 100644
index 911fa5e0e1..0000000000
--- a/third_party/glog/.travis.yml
+++ /dev/null
@@ -1,119 +0,0 @@
-sudo: required
-language: bash
-services:
-  - docker
-
-# define the build matrix
-env:
-  global:
-    - PROJECT_DIR: .
-    - TOOLCHAIN: gcc-cxx11
-    - BUILD_PACKAGES: cmake
-    - RUN_TESTS: true
-
-matrix:
-  include:
-    # Linux {
-
-    #   Ubuntu 14.04
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=trusty
-        BUILD_ARCH=amd64
-        BUILD_PACKAGES=cmake3
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=trusty
-        BUILD_ARCH=i386
-        BUILD_PACKAGES=cmake3
-    #   Ubuntu 16.04
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=xenial
-        BUILD_ARCH=amd64
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=xenial
-        BUILD_ARCH=i386
-    #   Ubuntu 18.04
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=bionic
-        BUILD_ARCH=amd64
-        TOOLCHAIN=gcc-cxx98
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=bionic
-        BUILD_ARCH=amd64
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=bionic
-        BUILD_ARCH=amd64
-        TOOLCHAIN=gcc-gnuxx11
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=bionic
-        BUILD_ARCH=amd64
-        TOOLCHAIN=gcc-cxx17
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=bionic
-        BUILD_ARCH=amd64
-        TOOLCHAIN=clang-cxx17
-        BUILD_PACKAGES="cmake clang"
-    # } // end Linux
-
-    # Windows build with mingw-w64 on Ubuntu 18.04
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=bionic
-        BUILD_ARCH=amd64
-        TOOLCHAIN=linux-mingw-w64-cxx11
-        BUILD_PACKAGES="cmake mingw-w64 wine-stable"
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=bionic
-        BUILD_ARCH=amd64
-        TOOLCHAIN=linux-mingw-w64-gnuxx11
-        BUILD_PACKAGES="cmake mingw-w64 wine-stable"
-    - os: linux
-      env: >
-        BUILD_FLAVOR=ubuntu
-        BUILD_RELEASE=bionic
-        BUILD_ARCH=amd64
-        TOOLCHAIN=linux-mingw-w64-cxx17
-        BUILD_PACKAGES="cmake mingw-w64 wine-stable"
-
-before_install:
-  # use the Dockerfile.<distro>.template file for building the image with sed magic
-  - |
-    sed \
-    -e "s/@BUILD_FLAVOR@/${BUILD_FLAVOR}/g" \
-    -e "s/@BUILD_RELEASE@/${BUILD_RELEASE}/g" \
-    -e "s/@BUILD_ARCH@/${BUILD_ARCH}/g" \
-    -e "s/@BUILD_PACKAGES@/${BUILD_PACKAGES}/g" \
-    Dockerfile.$BUILD_FLAVOR.template | tee Dockerfile.$BUILD_FLAVOR.$BUILD_RELEASE.$BUILD_ARCH
-  - docker build -f Dockerfile.$BUILD_FLAVOR.$BUILD_RELEASE.$BUILD_ARCH -t glog-devel .
-
-script: |
-  # run the respective .travis.<distro>.sh script
-  docker run \
-  -e BUILD_FLAVOR="$BUILD_FLAVOR" \
-  -e BUILD_RELEASE="$BUILD_RELEASE" \
-  -e BUILD_ARCH="$BUILD_ARCH" \
-  -e PROJECT_DIR="$PROJECT_DIR" \
-  -e TOOLCHAIN="$TOOLCHAIN" \
-  -e RUN_TESTS="$RUN_TESTS" \
-  -it glog-devel ./.travis.$BUILD_FLAVOR.sh
-
diff --git a/third_party/glog/AUTHORS b/third_party/glog/AUTHORS
deleted file mode 100644
index 1780c12bac..0000000000
--- a/third_party/glog/AUTHORS
+++ /dev/null
@@ -1,26 +0,0 @@
-# This is the official list of glog authors for copyright purposes.
-# This file is distinct from the CONTRIBUTORS files.
-# See the latter for an explanation.
-#
-# Names should be added to this file as:
-#	Name or Organization <email address>
-# The email address is not required for organizations.
-#
-# Please keep the list sorted.
-
-Abhishek Dasgupta <abhi2743@gmail.com>
-Abhishek Parmar <abhishek@orng.net>
-Andrew Schwartzmeyer <andrew@schwartzmeyer.com>
-Andy Ying <andy@trailofbits.com>
-Brian Silverman <bsilver16384@gmail.com>
-Google Inc.
-Guillaume Dumont <dumont.guillaume@gmail.com>
-Marco Wang <m.aesophor@gmail.com>
-Michael Tanner <michael@tannertaxpro.com>
-MiniLight <MiniLightAR@Gmail.com>
-romange <romange@users.noreply.github.com>
-Roman Perepelitsa <roman.perepelitsa@gmail.com>
-Sergiu Deitsch <sergiu.deitsch@gmail.com>
-tbennun <tbennun@gmail.com>
-Teddy Reed <teddy@prosauce.org>
-Zhongming Qu <qzmfranklin@gmail.com>
diff --git a/third_party/glog/BUILD b/third_party/glog/BUILD
deleted file mode 100644
index 37fb27bb43..0000000000
--- a/third_party/glog/BUILD
+++ /dev/null
@@ -1,5 +0,0 @@
-licenses(['notice'])
-
-load(':bazel/glog.bzl', 'glog_library')
-
-glog_library()
diff --git a/third_party/glog/CMakeLists.txt b/third_party/glog/CMakeLists.txt
deleted file mode 100644
index 79b4d9ed61..0000000000
--- a/third_party/glog/CMakeLists.txt
+++ /dev/null
@@ -1,848 +0,0 @@
-cmake_minimum_required (VERSION 3.0)
-
-if (POLICY CMP0042)
-  cmake_policy (SET CMP0042 NEW)
-endif (POLICY CMP0042)
-
-if (POLICY CMP0063)
-  cmake_policy (SET CMP0063 NEW)
-endif (POLICY CMP0063)
-
-project(glog VERSION 0.4.0 LANGUAGES C CXX)
-
-set (CPACK_PACKAGE_NAME glog)
-set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "")
-set (CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
-set (CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
-set (CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
-set (CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
-
-option (WITH_GFLAGS "Use gflags" ON)
-option (WITH_THREADS "Enable multithreading support" ON)
-option (WITH_TLS "Enable Thread Local Storage (TLS) support" ON)
-option (BUILD_SHARED_LIBS "Build shared libraries" OFF)
-option (PRINT_UNSYMBOLIZED_STACK_TRACES
-  "Print file offsets in traces instead of symbolizing" OFF)
-option (WITH_PKGCONFIG "Enable pkg-config support" ON)
-option (WITH_UNWIND "Enable libunwind support" ON)
-option (WITH_SYMBOLIZE "Enable symbolize module" ON)
-
-if (NOT WITH_UNWIND)
-  set (CMAKE_DISABLE_FIND_PACKAGE_Unwind ON)
-endif (NOT WITH_UNWIND)
-
-if (NOT WITH_THREADS)
-  set (CMAKE_DISABLE_FIND_PACKAGE_Threads ON)
-endif (NOT WITH_THREADS)
-
-list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
-
-include (CheckCSourceCompiles)
-include (CheckCXXCompilerFlag)
-include (CheckCXXSourceCompiles)
-include (CheckCXXSourceRuns)
-include (CheckFunctionExists)
-include (CheckIncludeFile)
-include (CheckIncludeFileCXX)
-include (CheckLibraryExists)
-include (CheckStructHasMember)
-include (CheckSymbolExists)
-include (CheckTypeSize)
-include (CMakePackageConfigHelpers)
-include (CMakePushCheckState)
-include (CPack)
-include (CTest)
-include (DetermineGflagsNamespace)
-include (GetCacheVariables)
-include (GNUInstallDirs)
-
-set (CMAKE_DEBUG_POSTFIX d)
-set (CMAKE_THREAD_PREFER_PTHREAD 1)
-
-if (WITH_GFLAGS)
-  find_package (gflags 2.2.0)
-
-  if (gflags_FOUND)
-    set (HAVE_LIB_GFLAGS 1)
-    determine_gflags_namespace (gflags_NAMESPACE)
-  endif (gflags_FOUND)
-endif (WITH_GFLAGS)
-
-find_package (Threads)
-find_package (Unwind)
-
-if (Unwind_FOUND)
-  set (HAVE_LIB_UNWIND 1)
-  set (HAVE_UNWIND_H 1)
-endif (Unwind_FOUND)
-
-check_include_file (dlfcn.h HAVE_DLFCN_H)
-check_include_file (execinfo.h HAVE_EXECINFO_H)
-check_include_file (glob.h HAVE_GLOB_H)
-check_include_file (inttypes.h HAVE_INTTYPES_H)
-check_include_file (memory.h HAVE_MEMORY_H)
-check_include_file (pwd.h HAVE_PWD_H)
-check_include_file (stdint.h HAVE_STDINT_H)
-check_include_file (stdlib.h HAVE_STDLIB_H)
-check_include_file (string.h HAVE_STRING_H)
-check_include_file (strings.h HAVE_STRINGS_H)
-check_include_file (sys/stat.h HAVE_SYS_STAT_H)
-check_include_file (sys/syscall.h HAVE_SYS_SYSCALL_H)
-check_include_file (sys/time.h HAVE_SYS_TIME_H)
-check_include_file (sys/types.h HAVE_SYS_TYPES_H)
-check_include_file (sys/utsname.h HAVE_SYS_UTSNAME_H)
-check_include_file (sys/wait.h HAVE_SYS_WAIT_H)
-check_include_file (syscall.h HAVE_SYSCALL_H)
-check_include_file (syslog.h HAVE_SYSLOG_H)
-check_include_file (ucontext.h HAVE_UCONTEXT_H)
-check_include_file (unistd.h HAVE_UNISTD_H)
-check_include_file (pwd.h HAVE_PWD_H)
-
-check_include_file_cxx ("ext/hash_map" HAVE_EXT_HASH_MAP)
-check_include_file_cxx ("ext/hash_set" HAVE_EXT_HASH_SET)
-check_include_file_cxx ("ext/slist" HAVE_EXT_SLIST)
-check_include_file_cxx ("tr1/unordered_map" HAVE_TR1_UNORDERED_MAP)
-check_include_file_cxx ("tr1/unordered_set" HAVE_TR1_UNORDERED_SET)
-check_include_file_cxx ("unordered_map" HAVE_UNORDERED_MAP)
-check_include_file_cxx ("unordered_set" HAVE_UNORDERED_SET)
-
-check_type_size ("unsigned __int16" HAVE___UINT16)
-check_type_size (u_int16_t HAVE_U_INT16_T)
-check_type_size (uint16_t HAVE_UINT16_T)
-
-check_function_exists (dladdr HAVE_DLADDR)
-check_function_exists (fcntl HAVE_FCNTL)
-check_function_exists (pread HAVE_PREAD)
-check_function_exists (pwrite HAVE_PWRITE)
-check_function_exists (sigaction HAVE_SIGACTION)
-check_function_exists (sigaltstack HAVE_SIGALSTACK)
-
-# NOTE gcc does not fail if you pass a non-existent -Wno-* option as an
-# argument. However, it will happily fail if you pass the corresponding -W*
-# option. So, we check whether options that disable warnings exist by testing
-# the availability of the corresponding option that enables the warning. This
-# eliminates the need to check for compiler for several (mainly Clang) options.
-
-check_cxx_compiler_flag (-Wdeprecated HAVE_NO_DEPRECATED)
-check_cxx_compiler_flag (-Wunnamed-type-template-args
-    HAVE_NO_UNNAMED_TYPE_TEMPLATE_ARGS)
-
-# NOTE: Cannot use check_function_exists here since >=vc-14.0 can define
-# snprintf as an inline function
-check_symbol_exists (snprintf stdio.h HAVE_SNPRINTF)
-
-check_library_exists (dbghelp UnDecorateSymbolName "" HAVE_DBGHELP)
-
-check_c_source_compiles ("
-#include <stdlib.h>
-static void foo(void) __attribute__ ((unused));
-int main(void) { return 0; }
-" HAVE___ATTRIBUTE__)
-
-check_c_source_compiles ("
-#include <stdlib.h>
-static void foo(void) __attribute__ ((visibility(\"default\")));
-int main(void) { return 0; }
-" HAVE___ATTRIBUTE__VISIBILITY_DEFAULT)
-
-check_c_source_compiles ("
-#include <stdlib.h>
-static void foo(void) __attribute__ ((visibility(\"hidden\")));
-int main(void) { return 0; }
-" HAVE___ATTRIBUTE__VISIBILITY_HIDDEN)
-
-check_c_source_compiles ("
-int main(void) { if (__builtin_expect(0, 0)) return 1; return 0; }
-" HAVE___BUILTIN_EXPECT)
-
-check_c_source_compiles ("
-int main(void)
-{
-  int a; if (__sync_val_compare_and_swap(&a, 0, 1)) return 1; return 0;
-}
-" HAVE___SYNC_VAL_COMPARE_AND_SWAP)
-
-check_c_source_compiles ("
-#define _XOPEN_SOURCE 500
-#include <pthread.h>
-int main(void)
-{
-  pthread_rwlock_t l;
-  pthread_rwlock_init(&l, NULL);
-  pthread_rwlock_rdlock(&l);
-  return 0;
-}
-" HAVE_RWLOCK)
-
-check_c_source_compiles ("
-__declspec(selectany) int a;
-int main(void) { return 0; }
-" HAVE___DECLSPEC)
-
-check_cxx_source_compiles ("
-#include <vector>
-vector<int> t; int main() { }
-" STL_NO_NAMESPACE)
-
-check_cxx_source_compiles ("
-#include <vector>
-std::vector<int> t; int main() { }
-" STL_STD_NAMESPACE)
-
-check_cxx_source_compiles ("
-#include <iostream>
-std::ostream& operator<<(std::ostream&, struct s);
-using ::operator<<;
-int main() { }
-" HAVE_USING_OPERATOR)
-
-check_cxx_source_compiles ("
-namespace Outer { namespace Inner { int i = 0; }}
-using namespace Outer::Inner;;
-int main() { return i; }
-" HAVE_NAMESPACES)
-
-check_cxx_source_compiles ("
-__thread int tls;
-int main() { }
-" HAVE_GCC_TLS)
-
-check_cxx_source_compiles ("
-__declspec(thread) int tls;
-int main() { }
-" HAVE_MSVC_TLS)
-
-check_cxx_source_compiles ("
-thread_local int tls;
-int main() { }
-" HAVE_CXX11_TLS)
-
-check_cxx_source_compiles ("
-#include <type_traits>
-std::aligned_storage<sizeof(char), alignof(char)>::type data;
-int main() { }
-" HAVE_ALIGNED_STORAGE)
-
-if (WITH_TLS)
-  # Cygwin does not support the thread attribute. Don't bother.
-  if (HAVE_GCC_TLS)
-    set (GLOG_THREAD_LOCAL_STORAGE "__thread")
-  elseif (HAVE_MSVC_TLS)
-    set (GLOG_THREAD_LOCAL_STORAGE "__declspec(thread)")
-  elseif (HAVE_CXX11_TLS)
-    set (GLOG_THREAD_LOCAL_STORAGE thread_local)
-  endif (HAVE_GCC_TLS)
-endif (WITH_TLS)
-
-set (_PC_FIELDS
-  "gregs[REG_PC]"
-  "gregs[REG_EIP]"
-  "gregs[REG_RIP]"
-  "sc_ip"
-  "uc_regs->gregs[PT_NIP]"
-  "gregs[R15]"
-  "arm_pc"
-  "mc_eip"
-  "mc_rip"
-  "__gregs[REG_EIP]"
-  "__gregs[REG_RIP]"
-  "ss.eip"
-  "__ss.__eip"
-  "ss.rip"
-  "__ss.__rip"
-  "ss.srr0"
-  "__ss.__srr0"
-)
-
-set (_PC_HEADERS ucontext.h signal.h)
-
-if (HAVE_UCONTEXT_H AND NOT PC_FROM_UCONTEXT)
-  foreach (_PC_FIELD ${_PC_FIELDS})
-    foreach (_PC_HEADER ${_PC_HEADERS})
-      set (_TMP
-      ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/uctfield.c)
-      file (WRITE ${_TMP} "
-#define _GNU_SOURCE 1
-#include <${_PC_HEADER}>
-int main(void)
-{
-  ucontext_t u;
-  return u.${_PC_FIELD} == 0;
-}
-")
-      try_compile (HAVE_PC_FROM_UCONTEXT ${CMAKE_CURRENT_BINARY_DIR} ${_TMP}
-        COMPILE_DEFINITIONS _GNU_SOURCE=1)
-
-      if (HAVE_PC_FROM_UCONTEXT)
-        set (PC_FROM_UCONTEXT ${_PC_FIELD} CACHE)
-      endif (HAVE_PC_FROM_UCONTEXT)
-    endforeach (_PC_HEADER)
-  endforeach (_PC_FIELD)
-endif  (HAVE_UCONTEXT_H AND NOT PC_FROM_UCONTEXT)
-
-if (STL_STD_NAMESPACE)
-  set (STL_NAMESPACE std)
-else (STL_STD_NAMESPACE)
-  set (STL_NAMESPACE "")
-endif (STL_STD_NAMESPACE)
-
-set (GOOGLE_NAMESPACE google)
-set (_START_GOOGLE_NAMESPACE_ "namespace ${GOOGLE_NAMESPACE} {")
-set (_END_GOOGLE_NAMESPACE_ "}")
-
-if (HAVE___UINT16)
-  set (ac_cv_have___uint16 1)
-else (HAVE___UINT16)
-  set (ac_cv_have___uint16 0)
-endif (HAVE___UINT16)
-
-if (HAVE_INTTYPES_H)
-  set (ac_cv_have_inttypes_h 1)
-else (HAVE_INTTYPES_H)
-  set (ac_cv_have_inttypes_h 0)
-endif (HAVE_INTTYPES_H)
-
-if (HAVE_LIB_GFLAGS)
-  set (ac_cv_have_libgflags 1)
-else (HAVE_LIB_GFLAGS)
-  set (ac_cv_have_libgflags 0)
-endif (HAVE_LIB_GFLAGS)
-
-if (HAVE_STDINT_H)
-  set (ac_cv_have_stdint_h 1)
-else (HAVE_STDINT_H)
-  set (ac_cv_have_stdint_h 0)
-endif (HAVE_STDINT_H)
-
-if (HAVE_SYS_TYPES_H)
-  set (ac_cv_have_systypes_h 1)
-else (HAVE_SYS_TYPES_H)
-  set (ac_cv_have_systypes_h 0)
-endif (HAVE_SYS_TYPES_H)
-
-if (HAVE_U_INT16_T)
-  set (ac_cv_have_u_int16_t 1)
-else (HAVE_U_INT16_T)
-  set (ac_cv_have_u_int16_t 0)
-endif (HAVE_U_INT16_T)
-
-if (HAVE_UINT16_T)
-  set (ac_cv_have_uint16_t 1)
-else (HAVE_UINT16_T)
-  set (ac_cv_have_uint16_t 0)
-endif (HAVE_UINT16_T)
-
-if (HAVE_UNISTD_H)
-  set (ac_cv_have_unistd_h 1)
-else (HAVE_UNISTD_H)
-  set (ac_cv_have_unistd_h 0)
-endif (HAVE_UNISTD_H)
-
-set (ac_google_namespace ${GOOGLE_NAMESPACE})
-set (ac_google_end_namespace ${_END_GOOGLE_NAMESPACE_})
-set (ac_google_start_namespace ${_START_GOOGLE_NAMESPACE_})
-
-if (HAVE___ATTRIBUTE__)
-  set (ac_cv___attribute___noreturn "__attribute__((noreturn))")
-  set (ac_cv___attribute___noinline "__attribute__((noinline))")
-elseif (HAVE___DECLSPEC)
-  set (ac_cv___attribute___noreturn "__declspec(noreturn)")
-  #set (ac_cv___attribute___noinline "__declspec(noinline)")
-endif (HAVE___ATTRIBUTE__)
-
-if (HAVE___BUILTIN_EXPECT)
-  set (ac_cv_have___builtin_expect 1)
-else (HAVE___BUILTIN_EXPECT)
-  set (ac_cv_have___builtin_expect 0)
-endif (HAVE___BUILTIN_EXPECT)
-
-if (HAVE_USING_OPERATOR)
-  set (ac_cv_cxx_using_operator 1)
-else (HAVE_USING_OPERATOR)
-  set (ac_cv_cxx_using_operator 0)
-endif (HAVE_USING_OPERATOR)
-
-if (HAVE_EXECINFO_H)
-  set (HAVE_STACKTRACE 1)
-endif (HAVE_EXECINFO_H)
-
-if (WITH_SYMBOLIZE)
-  if (WIN32 OR CYGWIN)
-    cmake_push_check_state (RESET)
-    set (CMAKE_REQUIRED_LIBRARIES DbgHelp)
-
-    check_cxx_source_runs ([=[
-    #include <windows.h>
-    #include <dbghelp.h>
-    #include <cstdlib>
-
-    void foobar() { }
-
-    int main()
-    {
-        HANDLE process = GetCurrentProcess();
-
-        if (!SymInitialize(process, NULL, TRUE))
-            return EXIT_FAILURE;
-
-        char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
-        SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(buf);
-        symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
-        symbol->MaxNameLen = MAX_SYM_NAME;
-
-        void* const pc = reinterpret_cast<void*>(&foobar);
-        BOOL ret = SymFromAddr(process, reinterpret_cast<DWORD64>(pc), 0, symbol);
-
-        return ret ? EXIT_SUCCESS : EXIT_FAILURE;
-    }
-    ]=] HAVE_SYMBOLIZE)
-
-  cmake_pop_check_state ()
-
-    if (HAVE_SYMBOLIZE)
-      set (HAVE_STACKTRACE 1)
-    endif (HAVE_SYMBOLIZE)
-  elseif (UNIX OR (APPLE AND HAVE_DLADDR))
-    set (HAVE_SYMBOLIZE 1)
-  endif (WIN32 OR CYGWIN)
-endif (WITH_SYMBOLIZE)
-
-check_cxx_source_compiles ("
-#include <cstdlib>
-#include <time.h>
-int main()
-{
-    time_t timep;
-    struct tm result;
-    localtime_r(&timep, &result);
-    return EXIT_SUCCESS;
-}
-" HAVE_LOCALTIME_R)
-
-set (SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
-
-if (WITH_THREADS AND Threads_FOUND)
-  if (CMAKE_USE_PTHREADS_INIT)
-    set (HAVE_PTHREAD 1)
-  endif (CMAKE_USE_PTHREADS_INIT)
-else (WITH_THREADS AND Threads_FOUND)
-  set (NO_THREADS 1)
-endif (WITH_THREADS AND Threads_FOUND)
-
-set (TEST_SRC_DIR \"${CMAKE_CURRENT_SOURCE_DIR}\")
-
-configure_file (src/config.h.cmake.in config.h)
-configure_file (src/glog/logging.h.in glog/logging.h @ONLY)
-configure_file (src/glog/raw_logging.h.in glog/raw_logging.h @ONLY)
-configure_file (src/glog/stl_logging.h.in glog/stl_logging.h @ONLY)
-configure_file (src/glog/vlog_is_on.h.in glog/vlog_is_on.h @ONLY)
-if (WITH_PKGCONFIG)
-  set (VERSION ${PROJECT_VERSION})
-  set (prefix ${CMAKE_INSTALL_PREFIX})
-  set (exec_prefix ${CMAKE_INSTALL_FULL_BINDIR})
-  set (libdir ${CMAKE_INSTALL_FULL_LIBDIR})
-  set (includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR})
-
-  configure_file (
-    "${PROJECT_SOURCE_DIR}/libglog.pc.in"
-    "${PROJECT_BINARY_DIR}/libglog.pc"
-    @ONLY
-  )
-
-  unset (VERSION)
-  unset (prefix)
-  unset (exec_prefix)
-  unset (libdir)
-  unset (includedir)
-endif (WITH_PKGCONFIG)
-
-set (CMAKE_CXX_VISIBILITY_PRESET default)
-set (CMAKE_VISIBILITY_INLINES_HIDDEN 1)
-
-set (GLOG_PUBLIC_H
-  ${CMAKE_CURRENT_BINARY_DIR}/glog/logging.h
-  ${CMAKE_CURRENT_BINARY_DIR}/glog/raw_logging.h
-  ${CMAKE_CURRENT_BINARY_DIR}/glog/stl_logging.h
-  ${CMAKE_CURRENT_BINARY_DIR}/glog/vlog_is_on.h
-  src/glog/log_severity.h
-)
-
-set (GLOG_SRCS
-  ${GLOG_PUBLIC_H}
-  src/base/commandlineflags.h
-  src/base/googleinit.h
-  src/base/mutex.h
-  src/demangle.cc
-  src/demangle.h
-  src/logging.cc
-  src/raw_logging.cc
-  src/symbolize.cc
-  src/symbolize.h
-  src/utilities.cc
-  src/utilities.h
-  src/vlog_is_on.cc
-)
-
-if (HAVE_PTHREAD OR WIN32)
-  list (APPEND GLOG_SRCS src/signalhandler.cc)
-endif (HAVE_PTHREAD OR WIN32)
-
-if (WIN32)
-  list (APPEND GLOG_SRCS
-    src/windows/port.cc
-    src/windows/port.h
-  )
-endif (WIN32)
-
-add_compile_options ($<$<AND:$<BOOL:${HAVE_NO_UNNAMED_TYPE_TEMPLATE_ARGS}>,$<NOT:$<CXX_COMPILER_ID:GNU>>>:-Wno-unnamed-type-template-args>)
-
-set (_glog_CMake_BINDIR ${CMAKE_INSTALL_BINDIR})
-set (_glog_CMake_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR})
-set (_glog_CMake_LIBDIR ${CMAKE_INSTALL_LIBDIR})
-set (_glog_CMake_INSTALLDIR ${_glog_CMake_LIBDIR}/cmake/glog)
-
-set (_glog_CMake_DIR glog/cmake)
-set (_glog_CMake_DATADIR ${CMAKE_INSTALL_DATAROOTDIR}/${_glog_CMake_DIR})
-set (_glog_BINARY_CMake_DATADIR
-  ${CMAKE_CURRENT_BINARY_DIR}/${_glog_CMake_DATADIR})
-
-# Add additional CMake find modules here.
-set (_glog_CMake_MODULES)
-
-if (Unwind_FOUND)
-  # Copy the module only if libunwind is actually used.
-  list (APPEND _glog_CMake_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindUnwind.cmake)
-endif (Unwind_FOUND)
-
-# Generate file name for each module in the binary directory
-foreach (_file ${_glog_CMake_MODULES})
-  get_filename_component (_module "${_file}" NAME)
-
-  list (APPEND _glog_BINARY_CMake_MODULES
-    ${_glog_BINARY_CMake_DATADIR}/${_module})
-endforeach (_file)
-
-if (_glog_CMake_MODULES)
-  # Copy modules to binary directory during the build
-  add_custom_command (OUTPUT ${_glog_BINARY_CMake_MODULES}
-    COMMAND ${CMAKE_COMMAND} -E make_directory
-    ${_glog_BINARY_CMake_DATADIR}
-    COMMAND ${CMAKE_COMMAND} -E copy ${_glog_CMake_MODULES}
-    ${_glog_BINARY_CMake_DATADIR}
-    DEPENDS ${_glog_CMake_MODULES}
-    COMMENT "Copying find modules..."
-  )
-endif (_glog_CMake_MODULES)
-
-add_library (glog
-  ${GLOG_SRCS}
-  ${_glog_BINARY_CMake_MODULES}
-)
-
-add_library(glog::glog ALIAS glog)
-
-set_target_properties (glog PROPERTIES POSITION_INDEPENDENT_CODE ON)
-
-if (Unwind_FOUND)
-  target_link_libraries (glog PUBLIC unwind::unwind)
-  set (Unwind_DEPENDENCY "find_dependency (Unwind ${Unwind_VERSION})")
-endif (Unwind_FOUND)
-
-if (HAVE_DBGHELP)
-   target_link_libraries (glog PUBLIC dbghelp)
-endif (HAVE_DBGHELP)
-
-if (HAVE_PTHREAD)
-  target_link_libraries (glog PUBLIC ${CMAKE_THREAD_LIBS_INIT})
-endif (HAVE_PTHREAD)
-
-if (WIN32 AND HAVE_SNPRINTF)
-  set_property (SOURCE src/windows/port.cc APPEND PROPERTY COMPILE_DEFINITIONS
-    HAVE_SNPRINTF)
-endif (WIN32 AND HAVE_SNPRINTF)
-
-if (gflags_FOUND)
-  target_link_libraries (glog PUBLIC gflags)
-
-  if (NOT BUILD_SHARED_LIBS)
-    # Don't use __declspec(dllexport|dllimport) if this is a static build
-    targeT_compile_definitions (glog PUBLIC GFLAGS_DLL_DECLARE_FLAG= GFLAGS_DLL_DEFINE_FLAG=)
-  endif (NOT BUILD_SHARED_LIBS)
-endif (gflags_FOUND)
-
-set_target_properties (glog PROPERTIES VERSION ${PROJECT_VERSION})
-set_target_properties (glog PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR})
-
-if (WIN32)
-  target_compile_definitions (glog PUBLIC GLOG_NO_ABBREVIATED_SEVERITIES)
-endif (WIN32)
-
-set_target_properties (glog PROPERTIES PUBLIC_HEADER "${GLOG_PUBLIC_H}")
-
-target_include_directories (glog BEFORE PUBLIC
-  "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
-  "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>"
-  "$<INSTALL_INTERFACE:${_glog_CMake_INCLUDE_DIR}>"
-  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
-
-if (WIN32)
-  target_include_directories (glog PUBLIC
-    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/windows>"
-    PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/windows)
-endif (WIN32)
-
-set_target_properties (glog PROPERTIES DEFINE_SYMBOL LIBGLOG_EXPORTS)
-
-if (NOT BUILD_SHARED_LIBS)
-  target_compile_definitions (glog PUBLIC GOOGLE_GLOG_DLL_DECL=)
-else (NOT BUILD_SHARED_LIBS)
-  target_compile_definitions (glog PRIVATE GOOGLE_GLOG_IS_A_DLL=1)
-
-  if (HAVE___DECLSPEC)
-    set (_EXPORT "__declspec(dllexport)")
-    set (_IMPORT "__declspec(dllimport)")
-  elseif (HAVE___ATTRIBUTE__VISIBILITY_DEFAULT)
-    set (_EXPORT "__attribute__((visibility(\"default\")))")
-    set (_IMPORT "")
-  endif (HAVE___DECLSPEC)
-
-  target_compile_definitions (glog PRIVATE
-    "GOOGLE_GLOG_DLL_DECL=${_EXPORT}")
-  target_compile_definitions (glog INTERFACE
-    "GOOGLE_GLOG_DLL_DECL=${_IMPORT}")
-  target_compile_definitions (glog INTERFACE
-    "GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS=${_IMPORT}")
-endif (NOT BUILD_SHARED_LIBS)
-
-# Unit testing
-
-if (BUILD_TESTING)
-  add_executable (logging_unittest
-    src/logging_unittest.cc
-  )
-
-  target_link_libraries (logging_unittest PRIVATE glog)
-
-  add_executable (stl_logging_unittest
-    src/stl_logging_unittest.cc
-  )
-
-  target_link_libraries (stl_logging_unittest PRIVATE glog)
-
-  if (HAVE_NO_DEPRECATED)
-    set_property (TARGET stl_logging_unittest APPEND PROPERTY COMPILE_OPTIONS
-      -Wno-deprecated)
-  endif (HAVE_NO_DEPRECATED)
-
-  if (HAVE_UNORDERED_MAP AND HAVE_UNORDERED_SET)
-    target_compile_definitions (stl_logging_unittest PRIVATE
-      GLOG_STL_LOGGING_FOR_UNORDERED)
-  endif (HAVE_UNORDERED_MAP AND HAVE_UNORDERED_SET)
-
-  if (HAVE_TR1_UNORDERED_MAP AND HAVE_TR1_UNORDERED_SET)
-    target_compile_definitions (stl_logging_unittest PRIVATE
-      GLOG_STL_LOGGING_FOR_TR1_UNORDERED)
-  endif (HAVE_TR1_UNORDERED_MAP AND HAVE_TR1_UNORDERED_SET)
-
-  if (HAVE_EXT_HASH_MAP AND HAVE_EXT_HASH_SET)
-    target_compile_definitions (stl_logging_unittest PRIVATE
-      GLOG_STL_LOGGING_FOR_EXT_HASH)
-  endif (HAVE_EXT_HASH_MAP AND HAVE_EXT_HASH_SET)
-
-  if (HAVE_EXT_SLIST)
-    target_compile_definitions (stl_logging_unittest PRIVATE
-      GLOG_STL_LOGGING_FOR_EXT_SLIST)
-  endif (HAVE_EXT_SLIST)
-
-  if (HAVE_SYMBOLIZE)
-    add_executable (symbolize_unittest
-      src/symbolize_unittest.cc
-    )
-
-    target_link_libraries (symbolize_unittest PRIVATE glog)
-  endif (HAVE_SYMBOLIZE)
-
-  add_executable (demangle_unittest
-    src/demangle_unittest.cc
-  )
-
-  target_link_libraries (demangle_unittest PRIVATE glog)
-
-  if (HAVE_STACKTRACE)
-    add_executable (stacktrace_unittest
-      src/stacktrace_unittest.cc
-    )
-
-    target_link_libraries (stacktrace_unittest PRIVATE glog)
-  endif (HAVE_STACKTRACE)
-
-  add_executable (utilities_unittest
-    src/utilities_unittest.cc
-  )
-
-  target_link_libraries (utilities_unittest PRIVATE glog)
-
-  if (HAVE_STACKTRACE AND HAVE_SYMBOLIZE)
-    add_executable (signalhandler_unittest
-      src/signalhandler_unittest.cc
-    )
-
-    target_link_libraries (signalhandler_unittest PRIVATE glog)
-  endif (HAVE_STACKTRACE AND HAVE_SYMBOLIZE)
-
-  add_test (NAME demangle COMMAND demangle_unittest)
-  add_test (NAME logging COMMAND logging_unittest)
-
-  if (TARGET signalhandler_unittest)
-    add_test (NAME signalhandler COMMAND signalhandler_unittest)
-  endif (TARGET signalhandler_unittest)
-
-  if (TARGET stacktrace_unittest)
-    add_test (NAME stacktrace COMMAND stacktrace_unittest)
-    set_tests_properties(stacktrace PROPERTIES TIMEOUT 30)
-  endif (TARGET stacktrace_unittest)
-
-  add_test (NAME stl_logging COMMAND stl_logging_unittest)
-
-  if (TARGET symbolize_unittest)
-    add_test (NAME symbolize COMMAND symbolize_unittest)
-  endif (TARGET symbolize_unittest)
-
-  # Generate an initial cache
-
-  get_cache_variables (_CACHEVARS EXCLUDE CMAKE_MAKE_PROGRAM)
-
-  set (_INITIAL_CACHE
-    ${CMAKE_CURRENT_BINARY_DIR}/test_package_config/glog_package_config_initial_cache.cmake)
-
-  # Package config test
-
-  add_test (NAME cmake_package_config_init COMMAND ${CMAKE_COMMAND}
-    -DTEST_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/test_package_config
-    -DINITIAL_CACHE=${_INITIAL_CACHE}
-    -DCACHEVARS=${_CACHEVARS}
-    -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestInitPackageConfig.cmake
-  )
-
-  add_test (NAME cmake_package_config_generate COMMAND ${CMAKE_COMMAND}
-    -DPATH=$ENV{PATH}
-    -DINITIAL_CACHE=${_INITIAL_CACHE}
-    -DTEST_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/test_package_config/working_config
-    -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/src/package_config_unittest/working_config
-    -DPACKAGE_DIR=${CMAKE_CURRENT_BINARY_DIR}
-    -DGENERATOR=${CMAKE_GENERATOR}
-    -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestPackageConfig.cmake
-  )
-
-  add_test (NAME cmake_package_config_build COMMAND
-    ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/test_package_config/working_config
-  )
-
-  add_test (NAME cmake_package_config_cleanup COMMAND ${CMAKE_COMMAND} -E
-    remove_directory
-    ${CMAKE_CURRENT_BINARY_DIR}/test_package_config
-  )
-
-  # Fixtures setup
-  set_tests_properties (cmake_package_config_init PROPERTIES FIXTURES_SETUP
-    cmake_package_config)
-  set_tests_properties (cmake_package_config_generate PROPERTIES FIXTURES_SETUP
-    cmake_package_config_working)
-
-  # Fixtures cleanup
-  set_tests_properties (cmake_package_config_cleanup PROPERTIES FIXTURES_CLEANUP
-    cmake_package_config)
-
-  # Fixture requirements
-  set_tests_properties (cmake_package_config_generate PROPERTIES
-    FIXTURES_REQUIRED cmake_package_config)
-  set_tests_properties (cmake_package_config_build PROPERTIES
-    FIXTURES_REQUIRED "cmake_package_config;cmake_package_config_working")
-endif (BUILD_TESTING)
-
-install (TARGETS glog
-  EXPORT glog-targets
-  RUNTIME DESTINATION ${_glog_CMake_BINDIR}
-  PUBLIC_HEADER DESTINATION ${_glog_CMake_INCLUDE_DIR}/glog
-  LIBRARY DESTINATION ${_glog_CMake_LIBDIR}
-  ARCHIVE DESTINATION ${_glog_CMake_LIBDIR})
-
-if (WITH_PKGCONFIG)
-  install (
-    FILES "${PROJECT_BINARY_DIR}/libglog.pc"
-    DESTINATION "${_glog_CMake_LIBDIR}/pkgconfig"
-  )
-endif (WITH_PKGCONFIG)
-
-set (glog_CMake_VERSION 3.0)
-
-if (gflags_FOUND)
-  # Ensure clients locate only the package config and not third party find
-  # modules having the same name. This avoid cmake_policy PUSH/POP errors.
-  if (CMAKE_VERSION VERSION_LESS 3.9)
-    set (gflags_DEPENDENCY "find_dependency (gflags ${gflags_VERSION})")
-  else (CMAKE_VERSION VERSION_LESS 3.9)
-    # Passing additional find_package arguments to find_dependency is possible
-    # starting with CMake 3.9.
-    set (glog_CMake_VERSION 3.9)
-    set (gflags_DEPENDENCY "find_dependency (gflags ${gflags_VERSION} NO_MODULE)")
-  endif (CMAKE_VERSION VERSION_LESS 3.9)
-endif (gflags_FOUND)
-
-configure_package_config_file (glog-config.cmake.in
-  ${CMAKE_CURRENT_BINARY_DIR}/glog-config.cmake
-  INSTALL_DESTINATION ${_glog_CMake_INSTALLDIR}
-  NO_CHECK_REQUIRED_COMPONENTS_MACRO)
-
-write_basic_package_version_file (glog-config-version.cmake VERSION
-  ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
-
-export (TARGETS glog NAMESPACE glog:: FILE glog-targets.cmake)
-export (PACKAGE glog)
-
-get_filename_component (_PREFIX "${CMAKE_INSTALL_PREFIX}" ABSOLUTE)
-
-# Directory containing the find modules relative to the config install
-# directory.
-file (RELATIVE_PATH glog_REL_CMake_MODULES
-  ${_PREFIX}/${_glog_CMake_INSTALLDIR}
-  ${_PREFIX}/${_glog_CMake_DATADIR}/glog-modules.cmake)
-
-get_filename_component (glog_REL_CMake_DATADIR ${glog_REL_CMake_MODULES}
-  DIRECTORY)
-
-set (glog_FULL_CMake_DATADIR
-  ${CMAKE_CURRENT_BINARY_DIR}/${_glog_CMake_DATADIR})
-
-configure_file (glog-modules.cmake.in
-  ${CMAKE_CURRENT_BINARY_DIR}/glog-modules.cmake @ONLY)
-
-install (CODE
-"
-set (glog_FULL_CMake_DATADIR \"\\\${CMAKE_CURRENT_LIST_DIR}/${glog_REL_CMake_DATADIR}\")
-configure_file (${CMAKE_CURRENT_SOURCE_DIR}/glog-modules.cmake.in
-  \"${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/glog-modules.cmake\" @ONLY)
-file (INSTALL
-  \"${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/glog-modules.cmake\"
-  DESTINATION
-  \"\${CMAKE_INSTALL_PREFIX}/${_glog_CMake_INSTALLDIR}\")
-"
-  COMPONENT Development
-)
-
-install (FILES
-  ${CMAKE_CURRENT_BINARY_DIR}/glog-config.cmake
-  ${CMAKE_CURRENT_BINARY_DIR}/glog-config-version.cmake
-  ${CMAKE_CURRENT_BINARY_DIR}/glog-modules.cmake
-  DESTINATION ${_glog_CMake_INSTALLDIR})
-
-# Find modules in share/glog/cmake
-install (DIRECTORY ${_glog_BINARY_CMake_DATADIR}
-  DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/glog
-  COMPONENT Development
-  FILES_MATCHING PATTERN "*.cmake"
-)
-
-install (EXPORT glog-targets NAMESPACE glog:: DESTINATION
-  ${_glog_CMake_INSTALLDIR})
diff --git a/third_party/glog/CONTRIBUTING.md b/third_party/glog/CONTRIBUTING.md
deleted file mode 100644
index 43de4c9d47..0000000000
--- a/third_party/glog/CONTRIBUTING.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# How to contribute #
-
-We'd love to accept your patches and contributions to this project.  There are
-a just a few small guidelines you need to follow.
-
-
-## Contributor License Agreement ##
-
-Contributions to any Google project must be accompanied by a Contributor
-License Agreement.  This is not a copyright **assignment**, it simply gives
-Google permission to use and redistribute your contributions as part of the
-project.
-
-  * If you are an individual writing original source code and you're sure you
-    own the intellectual property, then you'll need to sign an [individual
-    CLA][].
-
-  * If you work for a company that wants to allow you to contribute your work,
-    then you'll need to sign a [corporate CLA][].
-
-You generally only need to submit a CLA once, so if you've already submitted
-one (even if it was for a different project), you probably don't need to do it
-again.
-
-[individual CLA]: https://developers.google.com/open-source/cla/individual
-[corporate CLA]: https://developers.google.com/open-source/cla/corporate
-
-Once your CLA is submitted (or if you already submitted one for
-another Google project), make a commit adding yourself to the
-[AUTHORS][] and [CONTRIBUTORS][] files. This commit can be part
-of your first [pull request][].
-
-[AUTHORS]: AUTHORS
-[CONTRIBUTORS]: CONTRIBUTORS
-
-
-## Submitting a patch ##
-
-  1. It's generally best to start by opening a new issue describing the bug or
-     feature you're intending to fix.  Even if you think it's relatively minor,
-     it's helpful to know what people are working on.  Mention in the initial
-     issue that you are planning to work on that bug or feature so that it can
-     be assigned to you.
-
-  1. Follow the normal process of [forking][] the project, and setup a new
-     branch to work in.  It's important that each group of changes be done in
-     separate branches in order to ensure that a pull request only includes the
-     commits related to that bug or feature.
-
-  1. Do your best to have [well-formed commit messages][] for each change.
-     This provides consistency throughout the project, and ensures that commit
-     messages are able to be formatted properly by various git tools.
-
-  1. Finally, push the commits to your fork and submit a [pull request][].
-
-[forking]: https://help.github.com/articles/fork-a-repo
-[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
-[pull request]: https://help.github.com/articles/creating-a-pull-request
diff --git a/third_party/glog/CONTRIBUTORS b/third_party/glog/CONTRIBUTORS
deleted file mode 100644
index d90f859885..0000000000
--- a/third_party/glog/CONTRIBUTORS
+++ /dev/null
@@ -1,48 +0,0 @@
-# People who have agreed to one of the CLAs and can contribute patches.
-# The AUTHORS file lists the copyright holders; this file
-# lists people.  For example, Google employees are listed here
-# but not in AUTHORS, because Google holds the copyright.
-#
-# Names should be added to this file only after verifying that
-# the individual or the individual's organization has agreed to
-# the appropriate Contributor License Agreement, found here:
-#
-# https://developers.google.com/open-source/cla/individual
-# https://developers.google.com/open-source/cla/corporate
-#
-# The agreement for individuals can be filled out on the web.
-#
-# When adding J Random Contributor's name to this file,
-# either J's name or J's organization's name should be
-# added to the AUTHORS file, depending on whether the
-# individual or corporate CLA was used.
-#
-# Names should be added to this file as:
-#     Name <email address>
-#
-# Please keep the list sorted.
-
-Abhishek Dasgupta <abhi2743@gmail.com>
-Abhishek Parmar <abhishek@orng.net>
-Andrew Schwartzmeyer <andrew@schwartzmeyer.com>
-Andy Ying <andy@trailofbits.com>
-Brian Silverman <bsilver16384@gmail.com>
-Fumitoshi Ukai <ukai@google.com>
-Guillaume Dumont <dumont.guillaume@gmail.com>
-HΓ₯kan L. S. Younes <hyounes@google.com>
-Ivan Penkov <ivanpe@google.com>
-Jacob Trimble <modmaker@google.com>
-Jim Ray <jimray@google.com>
-Marco Wang <m.aesophor@gmail.com>
-Michael Darr <mdarr@matician.com>
-Michael Tanner <michael@tannertaxpro.com>
-MiniLight <MiniLightAR@Gmail.com>
-Peter Collingbourne <pcc@google.com>
-Rodrigo Queiro <rodrigoq@google.com>
-romange <romange@users.noreply.github.com>
-Roman Perepelitsa <roman.perepelitsa@gmail.com>
-Sergiu Deitsch <sergiu.deitsch@gmail.com>
-Shinichiro Hamaji <hamaji@google.com>
-tbennun <tbennun@gmail.com>
-Teddy Reed <teddy@prosauce.org>
-Zhongming Qu <qzmfranklin@gmail.com>
diff --git a/third_party/glog/COPYING b/third_party/glog/COPYING
deleted file mode 100644
index 38396b580b..0000000000
--- a/third_party/glog/COPYING
+++ /dev/null
@@ -1,65 +0,0 @@
-Copyright (c) 2008, Google 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:
-
-    * 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.
-
-
-A function gettimeofday in utilities.cc is based on
-
-http://www.google.com/codesearch/p?hl=en#dR3YEbitojA/COPYING&q=GetSystemTimeAsFileTime%20license:bsd
-
-The license of this code is:
-
-Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> and contributors
-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(s) of the above-listed copyright holder(s) 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/third_party/glog/ChangeLog b/third_party/glog/ChangeLog
deleted file mode 100644
index f8e43f8b39..0000000000
--- a/third_party/glog/ChangeLog
+++ /dev/null
@@ -1,99 +0,0 @@
-2019-01-22  Google Inc. <opensource@google.com>
-
-	* google-glog: version 0.4.0.
-	* See git log for the details.
-
-2017-05-09  Google Inc. <opensource@google.com>
-
-	* google-glog: version 0.3.5
-	* See git log for the details.
-
-2015-03-09  Google Inc. <opensource@google.com>
-
-	* google-glog: version 0.3.4
-	* See git log for the details.
-
-2013-02-01  Google Inc. <opensource@google.com>
-
-	* google-glog: version 0.3.3
-	* Add --disable-rtti option for configure.
-	* Visual Studio build and test fix.
-	* QNX build fix (thanks vanuan).
-	* Reduce warnings.
-	* Fixed LOG_SYSRESULT (thanks ukai).
-	* FreeBSD build fix (thanks yyanagisawa).
-	* Clang build fix.
-	* Now users can re-initialize glog after ShutdownGoogleLogging.
-	* Color output support by GLOG_colorlogtostderr (thanks alexs).
-	* Now glog's ABI around flags are compatible with gflags.
-	* Document mentions how to modify flags from user programs.
-
-2012-01-12  Google Inc. <opensource@google.com>
-
-	* google-glog: version 0.3.2
-	* Clang support.
-	* Demangler and stacktrace improvement for newer GCCs.
-	* Now fork(2) doesn't mess up log files.
-	* Make valgrind happier.
-	* Reduce warnings for more -W options.
-	* Provide a workaround for ERROR defined by windows.h.
-
-2010-06-15  Google Inc. <opensource@google.com>
-
-	* google-glog: version 0.3.1
-	* GLOG_* environment variables now work even when gflags is installed.
-	* Snow leopard support.
-	* Now we can build and test from out side tree.
-	* Add DCHECK_NOTNULL.
-	* Add ShutdownGoogleLogging to close syslog (thanks DGunchev)
-	* Fix --enable-frame-pointers option (thanks kazuki.ohta)
-	* Fix libunwind detection (thanks giantchen)
-
-2009-07-30  Google Inc. <opensource@google.com>
-
-	* google-glog: version 0.3.0
-	* Fix a deadlock happened when user uses glog with recent gflags.
-	* Suppress several unnecessary warnings (thanks keir).
-	* NetBSD and OpenBSD support.
-	* Use Win32API GetComputeNameA properly (thanks magila).
-	* Fix user name detection for Windows (thanks ademin).
-	* Fix several minor bugs.
-
-2009-04-10  Google Inc. <opensource@google.com>
-	* google-glog: version 0.2.1
-	* Fix timestamps of VC++ version.
-	* Add pkg-config support (thanks Tomasz)
-	* Fix build problem when building with gtest (thanks Michael)
-	* Add --with-gflags option for configure (thanks Michael)
-	* Fixes for GCC 4.4 (thanks John)
-
-2009-01-23  Google Inc. <opensource@google.com>
-	* google-glog: version 0.2
-	* Add initial Windows VC++ support.
-	* Google testing/mocking frameworks integration.
-	* Link pthread library automatically.
-	* Flush logs in signal handlers.
-	* Add macros LOG_TO_STRING, LOG_AT_LEVEL, DVLOG, and LOG_TO_SINK_ONLY.
-	* Log microseconds.
-	* Add --log_backtrace_at option.
-	* Fix some minor bugs.
-
-2008-11-18  Google Inc. <opensource@google.com>
-	* google-glog: version 0.1.2
-	* Add InstallFailureSignalHandler(). (satorux)
-	* Re-organize the way to produce stacktraces.
-	* Don't define unnecessary macro DISALLOW_EVIL_CONSTRUCTORS.
-
-2008-10-15  Google Inc. <opensource@google.com>
-	* google-glog: version 0.1.1
-	* Support symbolize for MacOSX 10.5.
-	* BUG FIX: --vmodule didn't work with gflags.
-	* BUG FIX: symbolize_unittest failed with GCC 4.3.
-	* Several fixes on the document.
-
-2008-10-07  Google Inc. <opensource@google.com>
-
-	* google-glog: initial release:
-	The glog package contains a library that implements application-level
-	logging.  This library provides logging APIs based on C++-style
-	streams and various helper macros.
diff --git a/third_party/glog/Dockerfile.ubuntu.template b/third_party/glog/Dockerfile.ubuntu.template
deleted file mode 100644
index 14f9b95891..0000000000
--- a/third_party/glog/Dockerfile.ubuntu.template
+++ /dev/null
@@ -1,14 +0,0 @@
-# Build Ubuntu image
-FROM @BUILD_ARCH@/@BUILD_FLAVOR@:@BUILD_RELEASE@
-
-# see https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#/run
-RUN apt-get update && \
-  apt-get install -y --no-install-recommends \
-  @BUILD_PACKAGES@ \
-  build-essential \
-  g++
-
-RUN mkdir -p /usr/src/app
-WORKDIR /usr/src/app
-
-COPY . /usr/src/app
diff --git a/third_party/glog/INSTALL b/third_party/glog/INSTALL
deleted file mode 100644
index e9530ac08c..0000000000
--- a/third_party/glog/INSTALL
+++ /dev/null
@@ -1,313 +0,0 @@
-Installation Instructions
-*************************
-
-Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
-2006, 2007 Free Software Foundation, Inc.
-
-This file is free documentation; the Free Software Foundation gives
-unlimited permission to copy, distribute and modify it.
-
-Glog-Specific Install Notes
-================================
-
-*** NOTE FOR 64-BIT LINUX SYSTEMS
-
-The glibc built-in stack-unwinder on 64-bit systems has some problems
-with the glog libraries.  (In particular, if you are using
-InstallFailureSignalHandler(), the signal may be raised in the middle
-of malloc, holding some malloc-related locks when they invoke the
-stack unwinder.  The built-in stack unwinder may call malloc
-recursively, which may require the thread to acquire a lock it already
-holds: deadlock.)
-
-For that reason, if you use a 64-bit system and you need
-InstallFailureSignalHandler(), we strongly recommend you install
-libunwind before trying to configure or install google glog.
-libunwind can be found at
-
-   http://download.savannah.nongnu.org/releases/libunwind/libunwind-snap-070410.tar.gz
-
-Even if you already have libunwind installed, you will probably still
-need to install from the snapshot to get the latest version.
-
-CAUTION: if you install libunwind from the URL above, be aware that
-you may have trouble if you try to statically link your binary with
-glog: that is, if you link with 'gcc -static -lgcc_eh ...'.  This
-is because both libunwind and libgcc implement the same C++ exception
-handling APIs, but they implement them differently on some platforms.
-This is not likely to be a problem on ia64, but may be on x86-64.
-
-Also, if you link binaries statically, make sure that you add
--Wl,--eh-frame-hdr to your linker options. This is required so that
-libunwind can find the information generated by the compiler required
-for stack unwinding.
-
-Using -static is rare, though, so unless you know this will affect you
-it probably won't.
-
-If you cannot or do not wish to install libunwind, you can still try
-to use two kinds of stack-unwinder: 1. glibc built-in stack-unwinder
-and 2. frame pointer based stack-unwinder.
-
-1. As we already mentioned, glibc's unwinder has a deadlock issue.
-However, if you don't use InstallFailureSignalHandler() or you don't
-worry about the rare possibilities of deadlocks, you can use this
-stack-unwinder.  If you specify no options and libunwind isn't
-detected on your system, the configure script chooses this unwinder by
-default.
-
-2. The frame pointer based stack unwinder requires that your
-application, the glog library, and system libraries like libc, all be
-compiled with a frame pointer.  This is *not* the default for x86-64.
-
-If you are on x86-64 system, know that you have a set of system
-libraries with frame-pointers enabled, and compile all your
-applications with -fno-omit-frame-pointer, then you can enable the
-frame pointer based stack unwinder by passing the
---enable-frame-pointers flag to configure.
-
-
-Basic Installation
-==================
-
-Briefly, the shell commands `./configure; make; make install' should
-configure, build, and install this package.  The following
-more-detailed instructions are generic; see the `README.md' file for
-instructions specific to this package.
-
-   The `configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation.  It uses
-those values to create a `Makefile' in each directory of the package.
-It may also create one or more `.h' files containing system-dependent
-definitions.  Finally, it creates a shell script `config.status' that
-you can run in the future to recreate the current configuration, and a
-file `config.log' containing compiler output (useful mainly for
-debugging `configure').
-
-   It can also use an optional file (typically called `config.cache'
-and enabled with `--cache-file=config.cache' or simply `-C') that saves
-the results of its tests to speed up reconfiguring.  Caching is
-disabled by default to prevent problems with accidental use of stale
-cache files.
-
-   If you need to do unusual things to compile the package, please try
-to figure out how `configure' could check whether to do them, and mail
-diffs or instructions to the address given in the `README.md' so they can
-be considered for the next release.  If you are using the cache, and at
-some point `config.cache' contains results you don't want to keep, you
-may remove or edit it.
-
-   The file `configure.ac' (or `configure.in') is used to create
-`configure' by a program called `autoconf'.  You need `configure.ac' if
-you want to change it or regenerate `configure' using a newer version
-of `autoconf'.
-
-The simplest way to compile this package is:
-
-  1. `cd' to the directory containing the package's source code and type
-     `./configure' to configure the package for your system.
-
-     Running `configure' might take a while.  While running, it prints
-     some messages telling which features it is checking for.
-
-  2. Type `make' to compile the package.
-
-  3. Optionally, type `make check' to run any self-tests that come with
-     the package.
-
-  4. Type `make install' to install the programs and any data files and
-     documentation.
-
-  5. You can remove the program binaries and object files from the
-     source code directory by typing `make clean'.  To also remove the
-     files that `configure' created (so you can compile the package for
-     a different kind of computer), type `make distclean'.  There is
-     also a `make maintainer-clean' target, but that is intended mainly
-     for the package's developers.  If you use it, you may have to get
-     all sorts of other programs in order to regenerate files that came
-     with the distribution.
-
-  6. Often, you can also type `make uninstall' to remove the installed
-     files again.
-
-
-Building glog - Using vcpkg
-===========================
-
-The url of vcpkg is: https://github.com/Microsoft/vcpkg
-You can download and install glog using the vcpkg dependency manager:
-
-    git clone https://github.com/Microsoft/vcpkg.git
-    cd vcpkg
-    ./bootstrap-vcpkg.sh
-    ./vcpkg integrate install
-    ./vcpkg install glog
-
-The glog port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please create an issue or pull request on the vcpkg repository.
-
-
-Compilers and Options
-=====================
-
-Some systems require unusual options for compilation or linking that the
-`configure' script does not know about.  Run `./configure --help' for
-details on some of the pertinent environment variables.
-
-   You can give `configure' initial values for configuration parameters
-by setting variables in the command line or in the environment.  Here
-is an example:
-
-     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
-
-   *Note Defining Variables::, for more details.
-
-Compiling For Multiple Architectures
-====================================
-
-You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory.  To do this, you can use GNU `make'.  `cd' to the
-directory where you want the object files and executables to go and run
-the `configure' script.  `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'.
-
-   With a non-GNU `make', it is safer to compile the package for one
-architecture at a time in the source code directory.  After you have
-installed the package for one architecture, use `make distclean' before
-reconfiguring for another architecture.
-
-Installation Names
-==================
-
-By default, `make install' installs the package's commands under
-`/usr/local/bin', include files under `/usr/local/include', etc.  You
-can specify an installation prefix other than `/usr/local' by giving
-`configure' the option `--prefix=PREFIX'.
-
-   You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files.  If you
-pass the option `--exec-prefix=PREFIX' to `configure', the package uses
-PREFIX as the prefix for installing programs and libraries.
-Documentation and other data files still use the regular prefix.
-
-   In addition, if you use an unusual directory layout you can give
-options like `--bindir=DIR' to specify different values for particular
-kinds of files.  Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them.
-
-   If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving `configure' the
-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
-
-Optional Features
-=================
-
-Some packages pay attention to `--enable-FEATURE' options to
-`configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to `--with-PACKAGE' options, where PACKAGE
-is something like `gnu-as' or `x' (for the X Window System).  The
-`README.md' should mention any `--enable-' and `--with-' options that the
-package recognizes.
-
-   For packages that use the X Window System, `configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the `configure' options `--x-includes=DIR' and
-`--x-libraries=DIR' to specify their locations.
-
-Specifying the System Type
-==========================
-
-There may be some features `configure' cannot figure out automatically,
-but needs to determine by the type of machine the package will run on.
-Usually, assuming the package is built to be run on the _same_
-architectures, `configure' can figure that out, but if it prints a
-message saying it cannot guess the machine type, give it the
-`--build=TYPE' option.  TYPE can either be a short name for the system
-type, such as `sun4', or a canonical name which has the form:
-
-     CPU-COMPANY-SYSTEM
-
-where SYSTEM can have one of these forms:
-
-     OS KERNEL-OS
-
-   See the file `config.sub' for the possible values of each field.  If
-`config.sub' isn't included in this package, then this package doesn't
-need to know the machine type.
-
-   If you are _building_ compiler tools for cross-compiling, you should
-use the option `--target=TYPE' to select the type of system they will
-produce code for.
-
-   If you want to _use_ a cross compiler, that generates code for a
-platform different from the build platform, you should specify the
-"host" platform (i.e., that on which the generated programs will
-eventually be run) with `--host=TYPE'.
-
-Sharing Defaults
-================
-
-If you want to set default values for `configure' scripts to share, you
-can create a site shell script called `config.site' that gives default
-values for variables like `CC', `cache_file', and `prefix'.
-`configure' looks for `PREFIX/share/config.site' if it exists, then
-`PREFIX/etc/config.site' if it exists.  Or, you can set the
-`CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all `configure' scripts look for a site script.
-
-Defining Variables
-==================
-
-Variables not defined in a site shell script can be set in the
-environment passed to `configure'.  However, some packages may run
-configure again during the build, and the customized values of these
-variables may be lost.  In order to avoid this problem, you should set
-them in the `configure' command line, using `VAR=value'.  For example:
-
-     ./configure CC=/usr/local2/bin/gcc
-
-causes the specified `gcc' to be used as the C compiler (unless it is
-overridden in the site shell script).
-
-Unfortunately, this technique does not work for `CONFIG_SHELL' due to
-an Autoconf bug.  Until the bug is fixed you can use this workaround:
-
-     CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
-
-`configure' Invocation
-======================
-
-`configure' recognizes the following options to control how it operates.
-
-`--help'
-`-h'
-     Print a summary of the options to `configure', and exit.
-
-`--version'
-`-V'
-     Print the version of Autoconf used to generate the `configure'
-     script, and exit.
-
-`--cache-file=FILE'
-     Enable the cache: use and save the results of the tests in FILE,
-     traditionally `config.cache'.  FILE defaults to `/dev/null' to
-     disable caching.
-
-`--config-cache'
-`-C'
-     Alias for `--cache-file=config.cache'.
-
-`--quiet'
-`--silent'
-`-q'
-     Do not print messages saying which checks are being made.  To
-     suppress all normal output, redirect it to `/dev/null' (any error
-     messages will still be shown).
-
-`--srcdir=DIR'
-     Look for the package's source code in directory DIR.  Usually
-     `configure' can determine that directory automatically.
-
-`configure' also accepts some other, not widely useful, options.  Run
-`configure --help' for more details.
-
diff --git a/third_party/glog/Makefile.am b/third_party/glog/Makefile.am
deleted file mode 100644
index e792332a02..0000000000
--- a/third_party/glog/Makefile.am
+++ /dev/null
@@ -1,259 +0,0 @@
-## Process this file with automake to produce Makefile.in
-
-AUTOMAKE_OPTIONS = subdir-objects foreign
-
-# Make sure that when we re-make ./configure, we get the macros we need
-ACLOCAL_AMFLAGS = -I m4
-
-# This is so we can #include <glog/foo>
-AM_CPPFLAGS = -I$(top_srcdir)/src
-
-# This is mostly based on configure options
-AM_CXXFLAGS =
-
-# These are good warnings to turn on by default
-if GCC
-  AM_CXXFLAGS += -Wall -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare
-endif
-
-# These are x86-specific, having to do with frame-pointers
-if X86_64
-if ENABLE_FRAME_POINTERS
-  AM_CXXFLAGS += -fno-omit-frame-pointer
-else
-  # TODO(csilvers): check if -fomit-frame-pointer might be in $(CXXFLAGS),
-  #                 before setting this.
-  AM_CXXFLAGS += -DNO_FRAME_POINTER
-endif
-endif
-
-if DISABLE_RTTI
-  AM_CXXFLAGS += -fno-rtti
-endif
-
-glogincludedir = $(includedir)/glog
-## The .h files you want to install (that is, .h files that people
-## who install this package can include in their own applications.)
-## We have to include both the .h and .h.in forms.  The latter we
-## put in noinst_HEADERS.
-gloginclude_HEADERS = src/glog/log_severity.h
-nodist_gloginclude_HEADERS = src/glog/logging.h src/glog/raw_logging.h src/glog/vlog_is_on.h src/glog/stl_logging.h
-noinst_HEADERS = src/glog/logging.h.in src/glog/raw_logging.h.in src/glog/vlog_is_on.h.in src/glog/stl_logging.h.in
-
-## This is for HTML and other documentation you want to install.
-## Add your documentation files (in doc/) in addition to these
-## top-level boilerplate files.  Also add a TODO file if you have one.
-dist_doc_DATA = AUTHORS COPYING ChangeLog INSTALL README.md README.windows \
-                doc/designstyle.css doc/glog.html
-
-## The libraries (.so's) you want to install
-lib_LTLIBRARIES =
-
-# The libraries libglog depends on.
-COMMON_LIBS = $(PTHREAD_LIBS) $(GFLAGS_LIBS) $(UNWIND_LIBS)
-# Compile switches for our unittest.
-TEST_CFLAGS = $(GTEST_CFLAGS) $(GMOCK_CFLAGS) $(GFLAGS_CFLAGS) \
-              $(MINGW_CFLAGS) $(AM_CXXFLAGS)
-# Libraries for our unittest.
-TEST_LIBS = $(GTEST_LIBS) $(GMOCK_LIBS) $(GFLAGS_LIBS)
-
-## unittests you want to run when people type 'make check'.
-## TESTS is for binary unittests, check_SCRIPTS for script-based unittests.
-## TESTS_ENVIRONMENT sets environment variables for when you run unittest,
-## but it only seems to take effect for *binary* unittests (argh!)
-TESTS =
-# Set a small stack size so that (at least on Linux) PIEs are mapped at a lower
-# address than DSOs. This is used by symbolize_pie_unittest to check that we can
-# successfully symbolize PIEs loaded at low addresses.
-TESTS_ENVIRONMENT = ulimit -s 8192;
-check_SCRIPTS =
-# Every time you add a unittest to check_SCRIPTS, add it here too
-noinst_SCRIPTS =
-# Binaries used for script-based unittests.
-TEST_BINARIES =
-
-TESTS += logging_unittest
-logging_unittest_SOURCES = $(gloginclude_HEADERS) \
-                           src/logging_unittest.cc \
-                           src/config_for_unittests.h \
-                           src/mock-log.h
-nodist_logging_unittest_SOURCES = $(nodist_gloginclude_HEADERS)
-logging_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-logging_unittest_LDFLAGS = $(PTHREAD_CFLAGS)
-logging_unittest_LDADD = libglog.la $(COMMON_LIBS) $(TEST_LIBS)
-
-check_SCRIPTS += logging_striplog_test_sh
-noinst_SCRIPTS += src/logging_striplog_test.sh
-logging_striplog_test_sh: logging_striptest0 logging_striptest2 logging_striptest10
-	$(top_srcdir)/src/logging_striplog_test.sh
-
-check_SCRIPTS += demangle_unittest_sh
-noinst_SCRIPTS += src/demangle_unittest.sh
-demangle_unittest_sh: demangle_unittest
-	$(builddir)/demangle_unittest  # force to create lt-demangle_unittest
-	$(top_srcdir)/src/demangle_unittest.sh
-
-check_SCRIPTS += signalhandler_unittest_sh
-noinst_SCRIPTS += src/signalhandler_unittest.sh
-signalhandler_unittest_sh: signalhandler_unittest
-	$(builddir)/signalhandler_unittest  # force to create lt-signalhandler_unittest
-	$(top_srcdir)/src/signalhandler_unittest.sh
-
-TEST_BINARIES += logging_striptest0
-logging_striptest0_SOURCES = $(gloginclude_HEADERS) \
-                             src/logging_striptest_main.cc
-nodist_logging_striptest0_SOURCES = $(nodist_gloginclude_HEADERS)
-logging_striptest0_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-logging_striptest0_LDFLAGS = $(PTHREAD_CFLAGS)
-logging_striptest0_LDADD = libglog.la $(COMMON_LIBS)
-
-TEST_BINARIES += logging_striptest2
-logging_striptest2_SOURCES = $(gloginclude_HEADERS) \
-                             src/logging_striptest2.cc
-nodist_logging_striptest2_SOURCES = $(nodist_gloginclude_HEADERS)
-logging_striptest2_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-logging_striptest2_LDFLAGS = $(PTHREAD_CFLAGS)
-logging_striptest2_LDADD = libglog.la $(COMMON_LIBS)
-
-TEST_BINARIES += logging_striptest10
-logging_striptest10_SOURCES = $(gloginclude_HEADERS) \
-                              src/logging_striptest10.cc
-nodist_logging_striptest10_SOURCES = $(nodist_gloginclude_HEADERS)
-logging_striptest10_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-logging_striptest10_LDFLAGS = $(PTHREAD_CFLAGS)
-logging_striptest10_LDADD = libglog.la $(COMMON_LIBS)
-
-TESTS += demangle_unittest
-demangle_unittest_SOURCES = $(gloginclude_HEADERS) \
-                            src/demangle_unittest.cc
-nodist_demangle_unittest_SOURCES = $(nodist_gloginclude_HEADERS)
-demangle_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-demangle_unittest_LDFLAGS = $(PTHREAD_CFLAGS)
-demangle_unittest_LDADD = libglog.la $(COMMON_LIBS) $(TEST_LIBS)
-
-TESTS += stacktrace_unittest
-stacktrace_unittest_SOURCES = $(gloginclude_HEADERS) \
-                              src/stacktrace_unittest.cc
-nodist_stacktrace_unittest_SOURCES = $(nodist_gloginclude_HEADERS)
-stacktrace_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-stacktrace_unittest_LDFLAGS = $(PTHREAD_CFLAGS)
-stacktrace_unittest_LDADD = libglog.la $(COMMON_LIBS)
-
-TESTS += symbolize_unittest
-symbolize_unittest_SOURCES = $(gloginclude_HEADERS) \
-                              src/symbolize_unittest.cc
-nodist_symbolize_unittest_SOURCES = $(nodist_gloginclude_HEADERS)
-symbolize_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-symbolize_unittest_LDFLAGS = $(PTHREAD_CFLAGS)
-symbolize_unittest_LDADD = libglog.la $(COMMON_LIBS) $(TEST_LIBS)
-
-TESTS += symbolize_pie_unittest
-symbolize_pie_unittest_SOURCES = $(gloginclude_HEADERS) \
-                                 src/symbolize_unittest.cc
-nodist_symbolize_pie_unittest_SOURCES = $(nodist_gloginclude_HEADERS)
-symbolize_pie_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS) -fPIE
-symbolize_pie_unittest_LDFLAGS = $(PTHREAD_CFLAGS) -pie
-symbolize_pie_unittest_LDADD = libglog.la $(COMMON_LIBS) $(TEST_LIBS)
-
-TESTS += stl_logging_unittest
-stl_logging_unittest_SOURCES = $(gloginclude_HEADERS) \
-                               src/stl_logging_unittest.cc
-nodist_stl_logging_unittest_SOURCES = $(nodist_gloginclude_HEADERS)
-stl_logging_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-stl_logging_unittest_LDFLAGS = $(PTHREAD_CFLAGS)
-stl_logging_unittest_LDADD = libglog.la $(COMMON_LIBS) $(TEST_LIBS)
-
-TEST_BINARIES += signalhandler_unittest
-signalhandler_unittest_SOURCES = $(gloginclude_HEADERS) \
-                               src/signalhandler_unittest.cc
-nodist_signalhandler_unittest_SOURCES = $(nodist_gloginclude_HEADERS)
-signalhandler_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-signalhandler_unittest_LDFLAGS = $(PTHREAD_CFLAGS)
-signalhandler_unittest_LDADD = libglog.la $(COMMON_LIBS) $(TEST_LIBS)
-
-TESTS += utilities_unittest
-utilities_unittest_SOURCES = $(gloginclude_HEADERS) \
-                             src/utilities_unittest.cc
-nodist_utilities_unittest_SOURCES = $(nodist_gloginclude_HEADERS)
-utilities_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-utilities_unittest_LDFLAGS = $(PTHREAD_CFLAGS)
-utilities_unittest_LDADD = libglog.la $(COMMON_LIBS) $(TEST_LIBS)
-
-if HAVE_GMOCK
-TESTS += mock_log_test
-mock_log_test_SOURCES = $(gloginclude_HEADERS) \
-                        src/mock-log_test.cc
-nodist_mock_log_test_SOURCES = $(nodist_gloginclude_HEADERS)
-mock_log_test_CXXFLAGS = $(PTHREAD_CFLAGS) $(TEST_CFLAGS)
-mock_log_test_LDFLAGS = $(PTHREAD_CFLAGS)
-mock_log_test_LDADD = libglog.la $(COMMON_LIBS) $(TEST_LIBS)
-endif
-
-## vvvv RULES TO MAKE THE LIBRARIES, BINARIES, AND UNITTESTS
-
-lib_LTLIBRARIES += libglog.la
-libglog_la_SOURCES = $(gloginclude_HEADERS) \
-                       src/logging.cc src/raw_logging.cc src/vlog_is_on.cc \
-                       src/utilities.cc src/utilities.h \
-                       src/demangle.cc src/demangle.h \
-                       src/stacktrace.h \
-                       src/stacktrace_generic-inl.h \
-                       src/stacktrace_libunwind-inl.h \
-                       src/stacktrace_powerpc-inl.h \
-                       src/stacktrace_x86-inl.h \
-                       src/stacktrace_x86_64-inl.h \
-                       src/symbolize.cc src/symbolize.h \
-                       src/signalhandler.cc \
-                       src/base/mutex.h src/base/googleinit.h \
-                       src/base/commandlineflags.h src/googletest.h
-nodist_libglog_la_SOURCES = $(nodist_gloginclude_HEADERS)
-
-libglog_la_CXXFLAGS = $(PTHREAD_CFLAGS) $(GFLAGS_CFLAGS) $(MINGW_CFLAGS) \
-                      $(AM_CXXFLAGS) -DNDEBUG
-libglog_la_LDFLAGS = $(PTHREAD_CFLAGS) $(GFLAGS_LDFLAGS)
-libglog_la_LIBADD = $(COMMON_LIBS)
-
-## The location of the windows project file for each binary we make
-WINDOWS_PROJECTS = google-glog.sln
-WINDOWS_PROJECTS += vsprojects/libglog/libglog.vcproj
-WINDOWS_PROJECTS += vsprojects/logging_unittest/logging_unittest.vcproj
-WINDOWS_PROJECTS += vsprojects/libglog_static/libglog_static.vcproj
-WINDOWS_PROJECTS += vsprojects/logging_unittest_static/logging_unittest_static.vcproj
-
-## ^^^^ END OF RULES TO MAKE THE LIBRARIES, BINARIES, AND UNITTESTS
-
-
-## This should always include $(TESTS), but may also include other
-## binaries that you compile but don't want automatically installed.
-noinst_PROGRAMS = $(TESTS) $(TEST_BINARIES)
-
-rpm: dist-gzip packages/rpm.sh packages/rpm/rpm.spec
-	@cd packages && ./rpm.sh ${PACKAGE} ${VERSION}
-
-deb: dist-gzip packages/deb.sh packages/deb/*
-	@cd packages && ./deb.sh ${PACKAGE} ${VERSION}
-
-# Windows wants write permission to .vcproj files and maybe even sln files.
-dist-hook:
-	test -e "$(distdir)/vsprojects" \
-	   && chmod -R u+w $(distdir)/*.sln $(distdir)/vsprojects/
-
-libtool: $(LIBTOOL_DEPS)
-	$(SHELL) ./config.status --recheck
-
-EXTRA_DIST = packages/rpm.sh packages/rpm/rpm.spec \
-	packages/deb.sh packages/deb/* \
-	$(SCRIPTS) src/logging_unittest.err src/demangle_unittest.txt \
-	src/windows/config.h src/windows/port.h src/windows/port.cc \
-	src/windows/preprocess.sh \
-	src/windows/glog/log_severity.h src/windows/glog/logging.h \
-	src/windows/glog/raw_logging.h src/windows/glog/stl_logging.h \
-	src/windows/glog/vlog_is_on.h \
-	$(WINDOWS_PROJECTS)
-
-CLEANFILES = core demangle.dm demangle.nm signalhandler.out* \
-	signalhandler_unittest.*.log.INFO.*
-
-# Add pkgconfig file
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libglog.pc
diff --git a/third_party/glog/README.md b/third_party/glog/README.md
deleted file mode 100644
index 965766c7ca..0000000000
--- a/third_party/glog/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-[![Build Status](https://img.shields.io/travis/google/glog/master.svg?label=Travis)](https://travis-ci.org/google/glog/builds)
-[![Grunt status](https://img.shields.io/appveyor/ci/google-admin/glog/master.svg?label=Appveyor)](https://ci.appveyor.com/project/google-admin/glog/history)
-
-This repository contains a C++ implementation of the Google logging
-module.  Documentation for the implementation is in doc/.
-
-See INSTALL for (generic) installation instructions for C++: basically
-```sh
-./autogen.sh && ./configure && make && make install
-```
diff --git a/third_party/glog/README.windows b/third_party/glog/README.windows
deleted file mode 100644
index ea6ccc20bd..0000000000
--- a/third_party/glog/README.windows
+++ /dev/null
@@ -1,17 +0,0 @@
-This project has been ported to Windows, including stack tracing, signal
-handling, and unit tests.
-
-A Visual Studio solution file is explicitly not provided because it is not
-maintainable. Instead, a CMake build system exists to generate the correct
-solution for your version of Visual Studio.
-
-In short,
-  (1) Install CMake from: https://cmake.org/download/
-  (2) With CMake on your PATH, run `cmake .` to generate the build files
-  (3) Either use `cmake --build`, or open the generated solution
-
-CMake provides different generators, and by default will pick the most relevant
-one to your environment. If you need a specific version of Visual Studio, use
-`cmake . -G <generator-name>`, and see `cmake --help` for the available
-generators. Also see `-T <toolset-name>`, which can used to request the native
-x64 toolchain with `-T host=x64`.
\ No newline at end of file
diff --git a/third_party/glog/WORKSPACE b/third_party/glog/WORKSPACE
deleted file mode 100644
index b5760d04ca..0000000000
--- a/third_party/glog/WORKSPACE
+++ /dev/null
@@ -1,10 +0,0 @@
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-
-http_archive(
-    name = "com_github_gflags_gflags",
-    strip_prefix = "gflags-2.2.2",
-    urls = [
-        "https://mirror.bazel.build/github.com/gflags/gflags/archive/v2.2.2.tar.gz",
-        "https://github.com/gflags/gflags/archive/v2.2.2.tar.gz",
-    ],
-)
diff --git a/third_party/glog/appveyor.yml b/third_party/glog/appveyor.yml
deleted file mode 100644
index ecc42c4f29..0000000000
--- a/third_party/glog/appveyor.yml
+++ /dev/null
@@ -1,71 +0,0 @@
-# global environment variables
-environment:
-  global:
-    # path to source directory of project to be built
-    PROJECT_DIR: .
-    # output test results for failing tests
-    CTEST_OUTPUT_ON_FAILURE: 1
-
-  # test matrix
-  matrix:
-
-    - TOOLCHAIN: "vs-14-2015-sdk-8-1"
-      GENERATOR: "Visual Studio 14 2015 Win64"
-      TEST_TARGET: RUN_TESTS
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-
-    - TOOLCHAIN: "vs-14-2015-win64"
-      GENERATOR: "Visual Studio 14 2015 Win64"
-      TEST_TARGET: RUN_TESTS
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-
-    - TOOLCHAIN: "vs-15-2017-win64"
-      GENERATOR: "Visual Studio 15 2017 Win64"
-      TEST_TARGET: RUN_TESTS
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-
-    - TOOLCHAIN: "vs-15-2017-win64-cxx17"
-      GENERATOR: "Visual Studio 15 2017 Win64"
-      TEST_TARGET: RUN_TESTS
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-
-    - TOOLCHAIN: "mingw-cxx11"
-      GENERATOR: "MinGW Makefiles"
-      MINGW_PATH: "C:\\mingw-w64\\x86_64-7.2.0-posix-seh-rt_v5-rev1\\mingw64\\bin"
-      TEST_TARGET: test
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-
-    - TOOLCHAIN: "mingw-gnuxx11"
-      GENERATOR: "MinGW Makefiles"
-      MINGW_PATH: "C:\\mingw-w64\\x86_64-7.2.0-posix-seh-rt_v5-rev1\\mingw64\\bin"
-      TEST_TARGET: test
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-
-    - TOOLCHAIN: "mingw-cxx17"
-      GENERATOR: "MinGW Makefiles"
-      MINGW_PATH: "C:\\mingw-w64\\x86_64-7.2.0-posix-seh-rt_v5-rev1\\mingw64\\bin"
-      TEST_TARGET: test
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-
-install:
-  # Remove entry with sh.exe from PATH to fix error with MinGW toolchain
-  # (For MinGW make to work correctly sh.exe must NOT be in your path)
-  # * http://stackoverflow.com/a/3870338/2288008
-  - cmd: set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
-
-  # set MINGW path
-  - cmd: IF DEFINED MINGW_PATH set PATH=%MINGW_PATH%;%PATH%
-
-  # Visual Studio 15 2017: Mimic behavior of older versions
-  - cmd: set VS150COMNTOOLS=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools
-
-build_script:
-  - cmd: cmake -H. -B_build_%TOOLCHAIN%_Debug -G "%GENERATOR%" -DCMAKE_TOOLCHAIN_FILE="%cd%\toolchains\%TOOLCHAIN%.cmake"
-  - cmd: cmake --build _build_%TOOLCHAIN%_Debug --config Debug
-  #- cmd: cmake -H. -B_build_%TOOLCHAIN%_Release -G "%GENERATOR%" -DCMAKE_TOOLCHAIN_FILE="%cd%\toolchains\%TOOLCHAIN%.cmake"
-  #- cmd: cmake --build _build_%TOOLCHAIN%_Release --config Release
-  # add git back to PATH for `diff` command in case of error
-  - cmd: set PATH=C:\Program Files\Git\usr\bin;%PATH%
-  - cmd: IF DEFINED TEST_TARGET cmake --build _build_%TOOLCHAIN%_Debug --target %TEST_TARGET%
-  #- cmd: IF DEFINED TEST_TARGET cmake --build _build_%TOOLCHAIN%_Release --target %TEST_TARGET%
-
diff --git a/third_party/glog/autogen.sh b/third_party/glog/autogen.sh
deleted file mode 100755
index 08b6590409..0000000000
--- a/third_party/glog/autogen.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-set -eu
-
-autoreconf -i
diff --git a/third_party/glog/bazel/example/BUILD b/third_party/glog/bazel/example/BUILD
deleted file mode 100644
index 6b10e2b2fb..0000000000
--- a/third_party/glog/bazel/example/BUILD
+++ /dev/null
@@ -1,8 +0,0 @@
-cc_test(
-    name = "main",
-    size = "small",
-    srcs = ["main.cc"],
-    deps = [
-        "//:glog",
-    ],
-)
diff --git a/third_party/glog/bazel/example/main.cc b/third_party/glog/bazel/example/main.cc
deleted file mode 100644
index fef01dc157..0000000000
--- a/third_party/glog/bazel/example/main.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <gflags/gflags.h>
-#include <glog/logging.h>
-#include <glog/stl_logging.h>
-
-int main(int argc, char* argv[]) {
-  // Initialize Google's logging library.
-  google::InitGoogleLogging(argv[0]);
-
-  // Optional: parse command line flags
-  gflags::ParseCommandLineFlags(&argc, &argv, true);
-
-  LOG(INFO) << "Hello, world!";
-
-  // glog/stl_logging.h allows logging STL containers.
-  std::vector<int> x;
-  x.push_back(1);
-  x.push_back(2);
-  x.push_back(3);
-  LOG(INFO) << "ABC, it's easy as " << x;
-
-  return 0;
-}
diff --git a/third_party/glog/bazel/glog.bzl b/third_party/glog/bazel/glog.bzl
deleted file mode 100644
index a46e2d148b..0000000000
--- a/third_party/glog/bazel/glog.bzl
+++ /dev/null
@@ -1,219 +0,0 @@
-# Implement a macro glog_library() that the BUILD file can load.
-
-# By default, glog is built with gflags support.  You can change this behavior
-# by using glog_library(with_gflags=0)
-#
-# This file is inspired by the following sample BUILD files:
-#       https://github.com/google/glog/issues/61
-#       https://github.com/google/glog/files/393474/BUILD.txt
-#
-# Known issue: the namespace parameter is not supported on Win32.
-
-def glog_library(namespace = "google", with_gflags = 1, **kwargs):
-    if native.repository_name() != "@":
-        repo_name = native.repository_name().lstrip("@")
-        gendir = "$(GENDIR)/external/" + repo_name
-        src_windows = "external/%s/src/windows" % repo_name
-    else:
-        gendir = "$(GENDIR)"
-        src_windows = "src/windows"
-
-    # Config setting for WebAssembly target.
-    native.config_setting(
-        name = "wasm",
-        values = {"cpu": "wasm"},
-    )
-
-    common_copts = [
-        "-DGLOG_BAZEL_BUILD",
-        "-DHAVE_STDINT_H",
-        "-DHAVE_STRING_H",
-        "-DHAVE_UNWIND_H",
-    ] + (["-DHAVE_LIB_GFLAGS"] if with_gflags else [])
-
-    wasm_copts = [
-        # Disable warnings that exists in glog.
-        "-Wno-sign-compare",
-        "-Wno-unused-function",
-        "-Wno-unused-local-typedefs",
-        "-Wno-unused-variable",
-        # Inject a C++ namespace.
-        "-DGOOGLE_NAMESPACE='%s'" % namespace,
-        # Allows src/base/mutex.h to include pthread.h.
-        "-DHAVE_PTHREAD",
-        # Allows src/logging.cc to determine the host name.
-        "-DHAVE_SYS_UTSNAME_H",
-        # For src/utilities.cc.
-        "-DHAVE_SYS_TIME_H",
-        # Enable dumping stacktrace upon sigaction.
-        "-DHAVE_SIGACTION",
-        # For logging.cc.
-        "-DHAVE_PREAD",
-        "-DHAVE___ATTRIBUTE__",
-        "-I%s/glog_internal" % gendir,
-    ]
-
-    linux_or_darwin_copts = wasm_copts + [
-        # For src/utilities.cc.
-        "-DHAVE_SYS_SYSCALL_H",
-    ]
-
-    freebsd_only_copts = [
-        # Enable declaration of _Unwind_Backtrace
-        "-D_GNU_SOURCE",
-    ]
-
-    darwin_only_copts = [
-        # For stacktrace.
-        "-DHAVE_DLADDR",
-    ]
-
-    windows_only_copts = [
-        "-DHAVE_SNPRINTF",
-        "-I" + src_windows,
-    ]
-
-    windows_only_srcs = [
-        "src/glog/log_severity.h",
-        "src/windows/config.h",
-        "src/windows/port.cc",
-        "src/windows/port.h",
-    ]
-
-    gflags_deps = ["@com_github_gflags_gflags//:gflags"] if with_gflags else []
-
-    native.cc_library(
-        name = "glog",
-        visibility = ["//visibility:public"],
-        srcs = [
-            "src/base/commandlineflags.h",
-            "src/base/googleinit.h",
-            "src/base/mutex.h",
-            "src/demangle.cc",
-            "src/demangle.h",
-            "src/logging.cc",
-            "src/raw_logging.cc",
-            "src/signalhandler.cc",
-            "src/stacktrace.h",
-            "src/stacktrace_generic-inl.h",
-            "src/stacktrace_libunwind-inl.h",
-            "src/stacktrace_powerpc-inl.h",
-            "src/stacktrace_windows-inl.h",
-            "src/stacktrace_x86-inl.h",
-            "src/stacktrace_x86_64-inl.h",
-            "src/symbolize.cc",
-            "src/symbolize.h",
-            "src/utilities.cc",
-            "src/utilities.h",
-            "src/vlog_is_on.cc",
-        ] + select({
-            "@bazel_tools//src/conditions:windows": windows_only_srcs,
-            "//conditions:default": [":config_h"],
-        }),
-        copts =
-            select({
-                "@bazel_tools//src/conditions:windows": common_copts + windows_only_copts,
-                "@bazel_tools//src/conditions:darwin": common_copts + linux_or_darwin_copts + darwin_only_copts,
-                "@bazel_tools//src/conditions:freebsd": common_copts + linux_or_darwin_copts + freebsd_only_copts,
-                ":wasm": common_copts + wasm_copts,
-                "//conditions:default": common_copts + linux_or_darwin_copts,
-            }),
-        deps = [
-            ":glog_headers",
-        ] + gflags_deps,
-        **kwargs
-    )
-
-    # glog headers vary depending on the os.
-    native.cc_library(
-        name = "glog_headers",
-        deps = select({
-            "@bazel_tools//src/conditions:windows": [":windows_glog_headers"],
-            "//conditions:default": [":default_glog_headers"],
-        }),
-    )
-
-    native.cc_library(
-        name = "windows_glog_headers",
-        hdrs = native.glob(["src/windows/glog/*.h"]),
-        strip_include_prefix = "src/windows",
-        # We need to override the default GOOGLE_GLOG_DLL_DECL from
-        # src/windows/glog/*.h to match src/windows/config.h.
-        defines = ["GOOGLE_GLOG_DLL_DECL=__declspec(dllexport)"],
-        deps = [":strip_include_prefix_hack"],
-    )
-
-    # Workaround https://github.com/bazelbuild/bazel/issues/6337 by declaring
-    # the dependencies without strip_include_prefix.
-    native.cc_library(
-        name = "strip_include_prefix_hack",
-        hdrs = native.glob(["src/windows/*.h"]),
-    )
-
-    native.cc_library(
-        name = "default_glog_headers",
-        strip_include_prefix = "src",
-        hdrs = [
-            "src/glog/log_severity.h",
-            ":logging_h",
-            ":raw_logging_h",
-            ":stl_logging_h",
-            ":vlog_is_on_h",
-        ],
-    )
-    native.genrule(
-        name = "config_h",
-        srcs = [
-            "src/config.h.cmake.in",
-        ],
-        outs = [
-            "glog_internal/config.h",
-        ],
-        cmd = "awk '{ gsub(/^#cmakedefine/, \"//cmakedefine\"); print; }' $< > $@",
-    )
-
-    native.genrule(
-        name = "gen_sh",
-        outs = [
-            "gen.sh",
-        ],
-        cmd = r'''\
-#!/bin/sh
-cat > $@ <<"EOF"
-sed -e 's/@ac_cv_cxx_using_operator@/1/g' \
-    -e 's/@ac_cv_have_unistd_h@/1/g' \
-    -e 's/@ac_cv_have_stdint_h@/1/g' \
-    -e 's/@ac_cv_have_systypes_h@/1/g' \
-    -e 's/@ac_cv_have_libgflags@/{}/g' \
-    -e 's/@ac_cv_have_uint16_t@/1/g' \
-    -e 's/@ac_cv_have___builtin_expect@/1/g' \
-    -e 's/@ac_cv_have_.*@/0/g' \
-    -e 's/@ac_google_start_namespace@/namespace google {{/g' \
-    -e 's/@ac_google_end_namespace@/}}/g' \
-    -e 's/@ac_google_namespace@/google/g' \
-    -e 's/@ac_cv___attribute___noinline@/__attribute__((noinline))/g' \
-    -e 's/@ac_cv___attribute___noreturn@/__attribute__((noreturn))/g' \
-    -e 's/@ac_cv___attribute___printf_4_5@/__attribute__((__format__ (__printf__, 4, 5)))/g'
-EOF
-'''.format(int(with_gflags)),
-    )
-
-    [
-        native.genrule(
-            name = "%s_h" % f,
-            srcs = [
-                "src/glog/%s.h.in" % f,
-            ],
-            outs = [
-                "src/glog/%s.h" % f,
-            ],
-            cmd = "$(location :gen_sh) < $< > $@",
-            tools = [":gen_sh"],
-        )
-        for f in [
-            "vlog_is_on",
-            "stl_logging",
-            "raw_logging",
-            "logging",
-        ]
-    ]
diff --git a/third_party/glog/cmake/DetermineGflagsNamespace.cmake b/third_party/glog/cmake/DetermineGflagsNamespace.cmake
deleted file mode 100644
index 3dde42b4b3..0000000000
--- a/third_party/glog/cmake/DetermineGflagsNamespace.cmake
+++ /dev/null
@@ -1,69 +0,0 @@
-macro(determine_gflags_namespace VARIABLE)
-  if (NOT DEFINED "${VARIABLE}")
-    if (CMAKE_REQUIRED_INCLUDES)
-      set (CHECK_INCLUDE_FILE_CXX_INCLUDE_DIRS "-DINCLUDE_DIRECTORIES=${CMAKE_REQUIRED_INCLUDES}")
-    else ()
-      set (CHECK_INCLUDE_FILE_CXX_INCLUDE_DIRS)
-    endif ()
-
-    set(MACRO_CHECK_INCLUDE_FILE_FLAGS ${CMAKE_REQUIRED_FLAGS})
-
-    set(_NAMESPACES gflags google)
-    set(_check_code
-"
-#include <gflags/gflags.h>
-
-int main(int argc, char**argv)
-{
-  GLOG_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
-}
-")
-    if (NOT CMAKE_REQUIRED_QUIET)
-      message (STATUS "Looking for gflags namespace")
-    endif ()
-    if (${ARGC} EQUAL 3)
-      set (CMAKE_CXX_FLAGS_SAVE ${CMAKE_CXX_FLAGS})
-      set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARGV2}")
-    endif ()
-
-    set (_check_file
-        ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/DetermineGflagsNamespace.cxx)
-
-    foreach (_namespace ${_NAMESPACES})
-      file (WRITE "${_check_file}" "${_check_code}")
-      try_compile (${VARIABLE}
-        "${CMAKE_BINARY_DIR}" "${_check_file}"
-        COMPILE_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}" -DGLOG_GFLAGS_NAMESPACE=${_namespace}
-        LINK_LIBRARIES gflags
-        CMAKE_FLAGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-        OUTPUT_VARIABLE OUTPUT)
-
-      if (${VARIABLE})
-        set (${VARIABLE} ${_namespace} CACHE INTERNAL "gflags namespace" FORCE)
-        break ()
-      else ()
-        file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
-          "Determining the gflags namespace ${_namespace} failed with the following output:\n"
-          "${OUTPUT}\n\n")
-      endif ()
-    endforeach (_namespace)
-
-    if (${ARGC} EQUAL 3)
-      set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS_SAVE})
-    endif ()
-
-    if (${VARIABLE})
-      if (NOT CMAKE_REQUIRED_QUIET)
-        message (STATUS "Looking for gflags namespace - ${${VARIABLE}}")
-      endif ()
-      file (APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
-        "Determining the gflags namespace passed with the following output:\n"
-        "${OUTPUT}\n\n")
-    else ()
-      if (NOT CMAKE_REQUIRED_QUIET)
-        message (STATUS "Looking for gflags namespace - failed")
-      endif ()
-      set (${VARIABLE} ${_namespace} CACHE INTERNAL "gflags namespace")
-    endif ()
-  endif ()
-endmacro ()
diff --git a/third_party/glog/cmake/FindUnwind.cmake b/third_party/glog/cmake/FindUnwind.cmake
deleted file mode 100644
index 8941bb0dac..0000000000
--- a/third_party/glog/cmake/FindUnwind.cmake
+++ /dev/null
@@ -1,78 +0,0 @@
-# - Try to find libunwind
-# Once done this will define
-#
-#  Unwind_FOUND - system has libunwind
-#  unwind::unwind - cmake target for libunwind
-
-include (FindPackageHandleStandardArgs)
-
-find_path (Unwind_INCLUDE_DIR NAMES unwind.h libunwind.h DOC "unwind include directory")
-find_library (Unwind_LIBRARY NAMES unwind DOC "unwind library")
-
-if (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
-    set (Unwind_ARCH "arm")
-elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
-    set (Unwind_ARCH "aarch64")
-elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR
-        CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64" OR
-        CMAKE_SYSTEM_PROCESSOR STREQUAL "corei7-64")
-    set (Unwind_ARCH "x86_64")
-elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
-    set (Unwind_ARCH "x86")
-elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^ppc64")
-    set (Unwind_ARCH "ppc64")
-elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^ppc")
-    set (Unwind_ARCH "ppc32")
-elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
-    set (Unwind_ARCH "mips")
-elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^hppa")
-    set (Unwind_ARCH "hppa")
-elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^ia64")
-    set (Unwind_ARCH "ia64")
-endif (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
-
-find_library (Unwind_PLATFORM_LIBRARY NAMES "unwind-${Unwind_ARCH}"
-  DOC "unwind library platform")
-
-mark_as_advanced (Unwind_INCLUDE_DIR Unwind_LIBRARY Unwind_PLATFORM_LIBRARY)
-
-# Extract version information
-if (Unwind_LIBRARY)
-  set (_Unwind_VERSION_HEADER ${Unwind_INCLUDE_DIR}/libunwind-common.h)
-
-  if (EXISTS ${_Unwind_VERSION_HEADER})
-    FILE (READ ${_Unwind_VERSION_HEADER} _Unwind_VERSION_CONTENTS)
-
-    string (REGEX REPLACE ".*#define UNW_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1"
-      Unwind_VERSION_MAJOR "${_Unwind_VERSION_CONTENTS}")
-    string (REGEX REPLACE ".*#define UNW_VERSION_MINOR[ \t]+([0-9]+).*" "\\1"
-      Unwind_VERSION_MINOR "${_Unwind_VERSION_CONTENTS}")
-    string (REGEX REPLACE ".*#define UNW_VERSION_EXTRA[ \t]+([0-9]+).*" "\\1"
-      Unwind_VERSION_PATCH "${_Unwind_VERSION_CONTENTS}")
-
-    set (Unwind_VERSION
-      ${Unwind_VERSION_MAJOR}.${Unwind_VERSION_MINOR}.${Unwind_VERSION_PATCH})
-    set (Unwind_VERSION_COMPONENTS 3)
-  endif (EXISTS ${_Unwind_VERSION_HEADER})
-endif (Unwind_LIBRARY)
-
-# handle the QUIETLY and REQUIRED arguments and set Unwind_FOUND to TRUE
-# if all listed variables are TRUE
-find_package_handle_standard_args (Unwind REQUIRED_VARS Unwind_INCLUDE_DIR
-  Unwind_LIBRARY Unwind_PLATFORM_LIBRARY VERSION_VAR Unwind_VERSION)
-
-if (Unwind_FOUND)
-  if (NOT TARGET unwind::unwind)
-    add_library (unwind::unwind INTERFACE IMPORTED)
-
-    set_property (TARGET unwind::unwind PROPERTY
-      INTERFACE_INCLUDE_DIRECTORIES ${Unwind_INCLUDE_DIR}
-    )
-    set_property (TARGET unwind::unwind PROPERTY
-      INTERFACE_LINK_LIBRARIES ${Unwind_LIBRARY} ${Unwind_PLATFORM_LIBRARY}
-    )
-    set_property (TARGET unwind::unwind PROPERTY
-      IMPORTED_CONFIGURATIONS RELEASE
-    )
-  endif (NOT TARGET unwind::unwind)
-endif (Unwind_FOUND)
diff --git a/third_party/glog/cmake/GetCacheVariables.cmake b/third_party/glog/cmake/GetCacheVariables.cmake
deleted file mode 100644
index 20485a8c8f..0000000000
--- a/third_party/glog/cmake/GetCacheVariables.cmake
+++ /dev/null
@@ -1,63 +0,0 @@
-cmake_policy (PUSH)
-cmake_policy (VERSION 3.3)
-
-include (CMakeParseArguments)
-
-function (get_cache_variables _CACHEVARS)
-  set (_SINGLE)
-  set (_MULTI EXCLUDE)
-  set (_OPTIONS)
-
-  cmake_parse_arguments (_ARGS "${_OPTIONS}" "${_SINGLE}" "${_MULTI}" ${ARGS} ${ARGN})
-
-  get_cmake_property (_VARIABLES VARIABLES)
-
-  set (CACHEVARS)
-
-  foreach (_VAR ${_VARIABLES})
-    if (DEFINED _ARGS_EXCLUDE)
-      if ("${_VAR}" IN_LIST _ARGS_EXCLUDE)
-        continue ()
-      endif ("${_VAR}" IN_LIST _ARGS_EXCLUDE)
-    endif (DEFINED _ARGS_EXCLUDE)
-
-    get_property (_CACHEVARTYPE CACHE ${_VAR} PROPERTY TYPE)
-
-    if ("${_CACHEVARTYPE}" STREQUAL INTERNAL OR
-        "${_CACHEVARTYPE}" STREQUAL STATIC OR
-        "${_CACHEVARTYPE}" STREQUAL UNINITIALIZED)
-        continue ()
-    endif ("${_CACHEVARTYPE}" STREQUAL INTERNAL OR
-        "${_CACHEVARTYPE}" STREQUAL STATIC OR
-        "${_CACHEVARTYPE}" STREQUAL UNINITIALIZED)
-
-    get_property (_CACHEVARVAL CACHE ${_VAR} PROPERTY VALUE)
-
-    if ("${_CACHEVARVAL}" STREQUAL "")
-      continue ()
-    endif ("${_CACHEVARVAL}" STREQUAL "")
-
-    get_property (_CACHEVARDOC CACHE ${_VAR} PROPERTY HELPSTRING)
-
-    # Escape " in values
-    string (REPLACE "\"" "\\\"" _CACHEVARVAL "${_CACHEVARVAL}")
-    # Escape " in help strings
-    string (REPLACE "\"" "\\\"" _CACHEVARDOC "${_CACHEVARDOC}")
-    # Escape ; in values
-    string (REPLACE ";" "\\\;" _CACHEVARVAL "${_CACHEVARVAL}")
-    # Escape backslash in values
-    string (REGEX REPLACE "\\\\([^\"])" "\\\\\\1" _CACHEVARVAL "${_CACHEVARVAL}")
-
-    if (NOT "${_CACHEVARTYPE}" STREQUAL BOOL)
-      set (_CACHEVARVAL "\"${_CACHEVARVAL}\"")
-    endif (NOT "${_CACHEVARTYPE}" STREQUAL BOOL)
-
-    if (NOT "${_CACHEVARTYPE}" STREQUAL "" AND NOT "${_CACHEVARVAL}" STREQUAL "")
-      set (CACHEVARS "${CACHEVARS}set (${_VAR} ${_CACHEVARVAL} CACHE ${_CACHEVARTYPE} \"${_CACHEVARDOC}\")\n")
-    endif (NOT "${_CACHEVARTYPE}" STREQUAL "" AND NOT "${_CACHEVARVAL}" STREQUAL "")
-  endforeach (_VAR)
-
-  set (${_CACHEVARS} ${CACHEVARS} PARENT_SCOPE)
-endfunction (get_cache_variables)
-
-cmake_policy (POP)
diff --git a/third_party/glog/cmake/INSTALL.md b/third_party/glog/cmake/INSTALL.md
deleted file mode 100644
index 1377d652ed..0000000000
--- a/third_party/glog/cmake/INSTALL.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# Glog - CMake Support
-
-Glog comes with a CMake build script ([CMakeLists.txt](../CMakeLists.txt)) that can be used on a wide range of platforms.  
-If you don't have CMake installed already, you can download it for free from <http://www.cmake.org/>.
-
-CMake works by generating native makefiles or build projects that can be used in the compiler environment of your choice.  
-You can either build Glog with CMake as a standalone project or it can be incorporated into an existing CMake build for another project.
-
-## Table of Contents
-
-- [Building Glog with CMake](#building-glog-with-cmake)
-- [Consuming Glog in a CMake Project](#consuming-glog-in-a-cmake-project)
-- [Incorporating Glog into a CMake Project](#incorporating-glog-into-a-cmake-project)
-
-## Building Glog with CMake
-
-When building Glog as a standalone project, on Unix-like systems with GNU Make as build tool, the typical workflow is:  
-
-1. Get the source code and change to it.
-e.g. cloning with git:
-```bash
-git clone git@github.com:google/glog.git
-cd glog
-```
-
-2. Run CMake to configure the build tree.
-```bash
-cmake -H. -Bbuild -G "Unix Makefiles"
-```
-note: To get the list of available generators (e.g. Visual Studio), use `-G ""`
-
-3. Afterwards, generated files can be used to compile the project.
-```bash
-cmake --build build
-```
-
-4. Test the build software (optional).
-```bash
-cmake --build build --target test
-```
-
-5. Install the built files (optional).
-```bash
-cmake --build build --target install
-```
-
-## Consuming Glog in a CMake Project
-
-If you have Glog installed in your system, you can use the CMake command
-`find_package()` to include it in your CMake Project.
-
-```cmake
-cmake_minimum_required(VERSION 3.0.2)
-project(myproj VERSION 1.0)
-
-find_package(glog 0.4.0 REQUIRED)
-
-add_executable(myapp main.cpp)
-target_link_libraries(myapp glog::glog)
-```
-
-Compile definitions and options will be added automatically to your target as
-needed.
-
-## Incorporating Glog into a CMake Project
-
-You can also use the CMake command `add_subdirectory()` to include Glog directly from a subdirectory of your project.  
-The **glog::glog** target is in this case an ALIAS library target for the **glog** library target. 
-
-```cmake
-cmake_minimum_required(VERSION 3.0.2)
-project(myproj VERSION 1.0)
-
-add_subdirectory(glog)
-
-add_executable(myapp main.cpp)
-target_link_libraries(myapp glog::glog)
-```
-
-Again, compile definitions and options will be added automatically to your target as
-needed.
diff --git a/third_party/glog/cmake/TestInitPackageConfig.cmake b/third_party/glog/cmake/TestInitPackageConfig.cmake
deleted file mode 100644
index 6cb11ccf29..0000000000
--- a/third_party/glog/cmake/TestInitPackageConfig.cmake
+++ /dev/null
@@ -1,12 +0,0 @@
-# Create the build directory
-execute_process (
-  COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_BINARY_DIR}
-  RESULT_VARIABLE _DIRECTORY_CREATED_SUCCEEDED
-)
-
-if (NOT _DIRECTORY_CREATED_SUCCEEDED EQUAL 0)
-  message (FATAL_ERROR "Failed to create build directory")
-endif (NOT _DIRECTORY_CREATED_SUCCEEDED EQUAL 0)
-
-file (WRITE ${INITIAL_CACHE} "${CACHEVARS}")
-
diff --git a/third_party/glog/cmake/TestPackageConfig.cmake b/third_party/glog/cmake/TestPackageConfig.cmake
deleted file mode 100644
index 191c3cac23..0000000000
--- a/third_party/glog/cmake/TestPackageConfig.cmake
+++ /dev/null
@@ -1,33 +0,0 @@
-# Create the build directory
-execute_process (
-  COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_BINARY_DIR}
-  RESULT_VARIABLE _DIRECTORY_CREATED_SUCCEEDED
-)
-
-if (NOT _DIRECTORY_CREATED_SUCCEEDED EQUAL 0)
-  message (FATAL_ERROR "Failed to create build directory")
-endif (NOT _DIRECTORY_CREATED_SUCCEEDED EQUAL 0)
-
-# Capture the PATH environment variable content set during project
-# generation stage. This is required because later during the build stage
-# the PATH is modified again (e.g., for MinGW AppVeyor CI builds) by adding
-# back the directory containing git.exe. Incidently, the Git installation
-# directory also contains sh.exe which causes MinGW Makefile generation to
-# fail.
-set (ENV{PATH} ${PATH})
-
-# Run CMake
-execute_process (
-  COMMAND ${CMAKE_COMMAND} -C ${INITIAL_CACHE}
-    -G ${GENERATOR}
-    -DCMAKE_PREFIX_PATH=${PACKAGE_DIR}
-    -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON
-    -DCMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY=ON
-    ${SOURCE_DIR}
-  WORKING_DIRECTORY ${TEST_BINARY_DIR}
-  RESULT_VARIABLE _GENERATE_SUCCEEDED
-)
-
-if (NOT _GENERATE_SUCCEEDED EQUAL 0)
-  message (FATAL_ERROR "Failed to generate project files using CMake")
-endif (NOT _GENERATE_SUCCEEDED EQUAL 0)
diff --git a/third_party/glog/configure.ac b/third_party/glog/configure.ac
deleted file mode 100644
index 80005c267e..0000000000
--- a/third_party/glog/configure.ac
+++ /dev/null
@@ -1,246 +0,0 @@
-## Process this file with autoconf to produce configure.
-## In general, the safest way to proceed is to run the following:
-##    % aclocal -I . -I `pwd`/../autoconf && autoheader && autoconf && automake
-
-# make sure we're interpreted by some minimal autoconf
-AC_PREREQ(2.57)
-
-AC_INIT(glog, 0.4.0, opensource@google.com)
-# The argument here is just something that should be in the current directory
-# (for sanity checking)
-AC_CONFIG_SRCDIR(README.md)
-AC_CONFIG_MACRO_DIR([m4])
-AM_INIT_AUTOMAKE
-AM_CONFIG_HEADER(src/config.h)
-
-AC_LANG(C++)
-
-# Checks for programs.
-AC_PROG_CC
-AC_PROG_CPP
-AC_PROG_CXX
-AM_CONDITIONAL(GCC, test "$GCC" = yes)   # let the Makefile know if we're gcc
-
-AC_PROG_LIBTOOL
-AC_SUBST(LIBTOOL_DEPS)
-
-# Check whether some low-level functions/files are available
-AC_HEADER_STDC
-
-# These are tested for by AC_HEADER_STDC, but I check again to set the var
-AC_CHECK_HEADER(stdint.h, ac_cv_have_stdint_h=1, ac_cv_have_stdint_h=0)
-AC_CHECK_HEADER(sys/types.h, ac_cv_have_systypes_h=1, ac_cv_have_systypes_h=0)
-AC_CHECK_HEADER(inttypes.h, ac_cv_have_inttypes_h=1, ac_cv_have_inttypes_h=0)
-AC_CHECK_HEADER(pwd.h, ac_cv_have_pwd_h=1, ac_cv_have_pwd_h=0)
-AC_CHECK_HEADERS(unistd.h, ac_cv_have_unistd_h=1, ac_cv_have_unistd_h=0)
-AC_CHECK_HEADERS(syscall.h)
-AC_CHECK_HEADERS(sys/syscall.h)
-# For backtrace with glibc.
-AC_CHECK_HEADERS(execinfo.h)
-# For backtrace with libunwind.
-AC_CHECK_HEADERS(libunwind.h, ac_cv_have_libunwind_h=1, ac_cv_have_libunwind_h=0)
-AC_CHECK_HEADERS(ucontext.h)
-AC_CHECK_HEADERS(sys/utsname.h)
-AC_CHECK_HEADERS(pwd.h)
-AC_CHECK_HEADERS(syslog.h)
-AC_CHECK_HEADERS(sys/time.h)
-AC_CHECK_HEADERS(glob.h)
-# For backtrace with gcc.
-AC_CHECK_HEADERS(unwind.h)
-
-AC_CHECK_HEADER(windows.h, ac_cv_have_windows_h=1, ac_cv_have_windows_h=0)
-if test x"$ac_cv_have_windows_h" = x"1"; then
-  MINGW_CFLAGS=-Isrc/windows
-fi
-
-AC_CHECK_SIZEOF(void *)
-
-# These are the types I need.  We look for them in either stdint.h,
-# sys/types.h, or inttypes.h, all of which are part of the default-includes.
-AC_CHECK_TYPE(uint16_t, ac_cv_have_uint16_t=1, ac_cv_have_uint16_t=0)
-AC_CHECK_TYPE(u_int16_t, ac_cv_have_u_int16_t=1, ac_cv_have_u_int16_t=0)
-AC_CHECK_TYPE(__uint16, ac_cv_have___uint16=1, ac_cv_have___uint16=0)
-
-AC_CHECK_FUNC(sigaltstack,
-              AC_DEFINE(HAVE_SIGALTSTACK, 1,
-                        [Define if you have the `sigaltstack' function]))
-AC_CHECK_FUNC(sigaction,
-              AC_DEFINE(HAVE_SIGACTION, 1,
-                        [Define if you have the 'sigaction' function]))
-AC_CHECK_FUNC(dladdr,
-              AC_DEFINE(HAVE_DLADDR, 1,
-                        [Define if you have the `dladdr' function]))
-AC_CHECK_FUNC(fcntl,
-              AC_DEFINE(HAVE_FCNTL, 1,
-                        [Define if you have the `fcntl' function]))
-AC_CHECK_FUNC(pread,
-              AC_DEFINE(HAVE_PREAD, 1,
-                        [Define if you have the 'pread' function]))
-AC_CHECK_FUNC(pwrite,
-              AC_DEFINE(HAVE_PWRITE, 1,
-                        [Define if you have the 'pwrite' function]))
-
-AX_C___ATTRIBUTE__
-# We only care about these two attributes.
-if test x"$ac_cv___attribute__" = x"yes"; then
-  ac_cv___attribute___noreturn="__attribute__ ((noreturn))"
-  ac_cv___attribute___noinline="__attribute__ ((noinline))"
-  ac_cv___attribute___printf_4_5="__attribute__((__format__ (__printf__, 4, 5)))"
-else
-  ac_cv___attribute___noreturn=
-  ac_cv___attribute___noinline=
-  ac_cv___attribute___printf_4_5=
-fi
-
-AX_C___BUILTIN_EXPECT
-if test x"$ac_cv___builtin_expect" = x"yes"; then
-  ac_cv_have___builtin_expect=1
-else
-  ac_cv_have___builtin_expect=0
-fi
-
-AX_C___SYNC_VAL_COMPARE_AND_SWAP
-
-# On x86_64, instead of libunwind, we can choose to compile with frame-pointers
-# (This isn't needed on i386, where -fno-omit-frame-pointer is the default).
-AC_ARG_ENABLE(frame_pointers,
-              AS_HELP_STRING([--enable-frame-pointers],
-                             [On x86_64 systems, compile with -fno-omit-frame-pointer (see INSTALL)]),,
-              enable_frame_pointers=no)
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [return __x86_64__ == 1 ? 0 : 1])],
-                  [is_x86_64=yes], [is_x86_64=no])
-AM_CONDITIONAL(ENABLE_FRAME_POINTERS, test "$enable_frame_pointers" = yes)
-AM_CONDITIONAL(X86_64, test "$is_x86_64" = yes)
-
-AC_ARG_ENABLE(rtti,
-              AS_HELP_STRING([--disable-rtti],
-                             [Disable RTTI in glog]))
-AM_CONDITIONAL(DISABLE_RTTI, test x"$enable_rtti" = x"no")
-if test x"$enable_rtti" = x"no"; then
-  AC_DEFINE(DISABLE_RTTI, 1, [define if glog doesn't use RTTI])
-fi
-
-# Some of the code in this directory depends on pthreads
-ACX_PTHREAD
-if test x"$acx_pthread_ok" = x"yes"; then
-  # To make libglog depend on libpthread on Linux, we need to add
-  # -lpthread in addition to -pthread.
-  AC_CHECK_LIB(pthread, pthread_self)
-fi
-
-# Check if there is google-gflags library installed.
-SAVE_CFLAGS="$CFLAGS"
-SAVE_LIBS="$LIBS"
-AC_ARG_WITH(gflags, AS_HELP_STRING[--with-gflags=GFLAGS_DIR],
-  GFLAGS_CFLAGS="-I${with_gflags}/include"
-  GFLAGS_LIBS="-L${with_gflags}/lib -lgflags"
-  CFLAGS="$CFLAGS $GFLAGS_CFLAGS"
-  LIBS="$LIBS $GFLAGS_LIBS"
-)
-AC_CHECK_LIB(gflags, main, ac_cv_have_libgflags=1, ac_cv_have_libgflags=0)
-if test x"$ac_cv_have_libgflags" = x"1"; then
-  AC_DEFINE(HAVE_LIB_GFLAGS, 1, [define if you have google gflags library])
-  if test x"$GFLAGS_LIBS" = x""; then
-    GFLAGS_LIBS="-lgflags"
-  fi
-else
-  GFLAGS_CFLAGS=
-  GFLAGS_LIBS=
-fi
-CFLAGS="$SAVE_CFLAGS"
-LIBS="$SAVE_LIBS"
-
-# TODO(hamaji): Use official m4 macros provided by testing libraries
-#               once the m4 macro of Google Mocking becomes ready.
-# Check if there is Google Test library installed.
-AC_CHECK_PROG(GTEST_CONFIG, gtest-config, "yes")
-AC_CHECK_LIB(gtest, main, have_gtest_lib="yes")
-if test x"$GTEST_CONFIG" = "xyes" -a x"$have_gtest_lib" = "xyes"; then
-  GTEST_CFLAGS=`gtest-config --cppflags --cxxflags`
-  GTEST_LIBS=`gtest-config --ldflags --libs`
-  AC_DEFINE(HAVE_LIB_GTEST, 1, [define if you have google gtest library])
-
-  # Check if there is Google Mocking library installed.
-  AC_CHECK_PROG(GMOCK_CONFIG, gmock-config, "yes")
-  if test x"$GMOCK_CONFIG" = "xyes"; then
-    GMOCK_CFLAGS=`gmock-config --cppflags --cxxflags`
-    GMOCK_LIBS=`gmock-config --ldflags --libs`
-    AC_DEFINE(HAVE_LIB_GMOCK, 1, [define if you have google gmock library])
-  else
-    # We don't run test cases which use Google Mocking framework.
-    GMOCK_CFLAGS=
-    GMOCK_LIBS=
-  fi
-else
-  # We'll use src/googletest.h for our unittests.
-  GTEST_CFLAGS=
-  GTEST_LIBS=
-fi
-AM_CONDITIONAL(HAVE_GMOCK, test x"$GMOCK_CONFIG" = "xyes")
-
-# We want to link in libunwind if it exists
-UNWIND_LIBS=
-# Unfortunately, we need to check the header file in addition to the
-# lib file to check if libunwind is available since libunwind-0.98
-# doesn't install all necessary header files.
-if test x"$ac_cv_have_libunwind_h" = x"1"; then
- AC_CHECK_LIB(unwind, backtrace, UNWIND_LIBS=-lunwind)
-fi
-AC_SUBST(UNWIND_LIBS)
-if test x"$UNWIND_LIBS" != x""; then
-  AC_DEFINE(HAVE_LIB_UNWIND, 1, [define if you have libunwind])
-fi
-
-# We'd like to use read/write locks in several places in the code.
-# See if our pthreads support extends to that.  Note: for linux, it
-# does as long as you define _XOPEN_SOURCE appropriately.
-AC_RWLOCK
-
-# Find out what namespace 'normal' STL code lives in, and also what namespace
-# the user wants our classes to be defined in
-AC_CXX_STL_NAMESPACE
-AC_DEFINE_GOOGLE_NAMESPACE(google)
-
-AC_CXX_USING_OPERATOR
-
-AC_PC_FROM_UCONTEXT(AC_MSG_WARN(Could not find the PC.  Will not output failed addresses...))
-
-AC_DEFINE_UNQUOTED(TEST_SRC_DIR, "$srcdir", [location of source code])
-
-AC_ARG_ENABLE(unsymbolized-traces,
-              AS_HELP_STRING([--enable-unsymbolized-traces],
-                             [Print file offsets in traces instead of symbolizing.]),
-              enable_unsymbolized_traces=yes)
-if test x"$enable_unsymbolized_traces" = x"yes"; then
-  AC_DEFINE(PRINT_UNSYMBOLIZED_STACK_TRACES, 1,
-            [define if we should print file offsets in traces instead of symbolizing.])
-fi
-
-# These are what's needed by logging.h.in and raw_logging.h.in
-AC_SUBST(ac_google_start_namespace)
-AC_SUBST(ac_google_end_namespace)
-AC_SUBST(ac_google_namespace)
-AC_SUBST(ac_cv_cxx_using_operator)
-AC_SUBST(ac_cv___attribute___noreturn)
-AC_SUBST(ac_cv___attribute___noinline)
-AC_SUBST(ac_cv___attribute___printf_4_5)
-AC_SUBST(ac_cv_have___builtin_expect)
-AC_SUBST(ac_cv_have_stdint_h)
-AC_SUBST(ac_cv_have_systypes_h)
-AC_SUBST(ac_cv_have_inttypes_h)
-AC_SUBST(ac_cv_have_unistd_h)
-AC_SUBST(ac_cv_have_uint16_t)
-AC_SUBST(ac_cv_have_u_int16_t)
-AC_SUBST(ac_cv_have___uint16)
-AC_SUBST(ac_cv_have_libgflags)
-AC_SUBST(GFLAGS_CFLAGS)
-AC_SUBST(GTEST_CFLAGS)
-AC_SUBST(GMOCK_CFLAGS)
-AC_SUBST(MINGW_CFLAGS)
-AC_SUBST(GFLAGS_LIBS)
-AC_SUBST(GTEST_LIBS)
-AC_SUBST(GMOCK_LIBS)
-
-# Write generated configuration file
-AC_CONFIG_FILES([Makefile src/glog/logging.h src/glog/raw_logging.h src/glog/vlog_is_on.h src/glog/stl_logging.h])
-AC_OUTPUT(libglog.pc)
diff --git a/third_party/glog/default.nix b/third_party/glog/default.nix
deleted file mode 100644
index a28bcbaea9..0000000000
--- a/third_party/glog/default.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-{ pkgs, lib, ... }:
-
-let inherit (pkgs) fullLlvm11Stdenv cmake;
-in fullLlvm11Stdenv.mkDerivation {
-  name = "glog";
-  version = "20200527-unstable";
-  src = ./.;
-
-  nativeBuildInputs = [ cmake ];
-  cmakeFlags = [
-    "-DCMAKE_CXX_STANDARD=17"
-    "-WITH_GFLAGS=OFF"
-  ];
-
-  meta = with lib; {
-    homepage = "https://github.com/google/glog";
-    license = licenses.bsd3;
-    description = "Library for application-level logging";
-    platforms = platforms.unix;
-  };
-}
diff --git a/third_party/glog/doc/designstyle.css b/third_party/glog/doc/designstyle.css
deleted file mode 100644
index f5d1ec2f76..0000000000
--- a/third_party/glog/doc/designstyle.css
+++ /dev/null
@@ -1,115 +0,0 @@
-body {
-  background-color: #ffffff;
-  color: black;
-  margin-right: 1in;
-  margin-left: 1in;
-}
-
-
-h1, h2, h3, h4, h5, h6 {
-  color: #3366ff;
-  font-family: sans-serif;
-}
-@media print {
-  /* Darker version for printing */
-  h1, h2, h3, h4, h5, h6 {
-    color: #000080;
-    font-family: helvetica, sans-serif;
-  }
-}
-
-h1 { 
-  text-align: center;
-  font-size: 18pt;
-}
-h2 {
-  margin-left: -0.5in;
-}
-h3 {
-  margin-left: -0.25in;
-}
-h4 {
-  margin-left: -0.125in;
-}
-hr {
-  margin-left: -1in;
-}
-
-/* Definition lists: definition term bold */
-dt {
-  font-weight: bold;
-}
-
-address {
-  text-align: right;
-}
-/* Use the <code> tag for bits of code and <var> for variables and objects. */
-code,pre,samp,var {
-  color: #006000;
-}
-/* Use the <file> tag for file and directory paths and names. */
-file {
-  color: #905050;
-  font-family: monospace;
-}
-/* Use the <kbd> tag for stuff the user should type. */
-kbd {
-  color: #600000;
-}
-div.note p {
-  float: right;
-  width: 3in;
-  margin-right: 0%;
-  padding: 1px;
-  border: 2px solid #6060a0;
-  background-color: #fffff0;
-}
-
-UL.nobullets {
-  list-style-type: none;
-  list-style-image: none;
-  margin-left: -1em;
-}
-
-/*
-body:after {
-  content: "Google Confidential";
-}
-*/
-
-/* pretty printing styles.  See prettify.js */
-.str { color: #080; }
-.kwd { color: #008; }
-.com { color: #800; }
-.typ { color: #606; }
-.lit { color: #066; }
-.pun { color: #660; }
-.pln { color: #000; }
-.tag { color: #008; }
-.atn { color: #606; }
-.atv { color: #080; }
-pre.prettyprint { padding: 2px; border: 1px solid #888; }
-
-.embsrc { background: #eee; }
-
-@media print {
-  .str { color: #060; }
-  .kwd { color: #006; font-weight: bold; }
-  .com { color: #600; font-style: italic; }
-  .typ { color: #404; font-weight: bold; }
-  .lit { color: #044; }
-  .pun { color: #440; }
-  .pln { color: #000; }
-  .tag { color: #006; font-weight: bold; }
-  .atn { color: #404; }
-  .atv { color: #060; }
-}
-
-/* Table Column Headers */
-.hdr { 
-  color: #006; 
-  font-weight: bold; 
-  background-color: #dddddd; }
-.hdr2 { 
-  color: #006; 
-  background-color: #eeeeee; }
\ No newline at end of file
diff --git a/third_party/glog/doc/glog.html b/third_party/glog/doc/glog.html
deleted file mode 100644
index 699d2ceb80..0000000000
--- a/third_party/glog/doc/glog.html
+++ /dev/null
@@ -1,631 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
-<html>
-<head>
-<title>How To Use Google Logging Library (glog)</title>
-
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-<link href="http://www.google.com/favicon.ico" type="image/x-icon"
-      rel="shortcut icon">
-<link href="designstyle.css" type="text/css" rel="stylesheet">
-<style type="text/css">
-<!--
-  ol.bluelist li {
-    color: #3366ff;
-    font-family: sans-serif;
-  }
-  ol.bluelist li p {
-    color: #000;
-    font-family: "Times Roman", times, serif;
-  }
-  ul.blacklist li {
-    color: #000;
-    font-family: "Times Roman", times, serif;
-  }
-//-->
-</style>
-</head>
-
-<body>
-
-<h1>How To Use Google Logging Library (glog)</h1>
-<small>(as of
-<script type=text/javascript>
-  var lm = new Date(document.lastModified);
-  document.write(lm.toDateString());
-</script>)
-</small>
-<br>
-
-<h2> <A NAME=intro>Introduction</A> </h2>
-
-<p><b>Google glog</b> is a library that implements application-level
-logging.  This library provides logging APIs based on C++-style
-streams and various helper macros.
-You can log a message by simply streaming things to LOG(&lt;a
-particular <a href="#severity">severity level</a>&gt;), e.g.
-
-<pre>
-   #include &lt;glog/logging.h&gt;
-
-   int main(int argc, char* argv[]) {
-     // Initialize Google's logging library.
-     google::InitGoogleLogging(argv[0]);
-
-     // ...
-     LOG(INFO) &lt;&lt; "Found " &lt;&lt; num_cookies &lt;&lt; " cookies";
-   }
-</pre>
-
-<p>Google glog defines a series of macros that simplify many common logging
-tasks.  You can log messages by severity level, control logging
-behavior from the command line, log based on conditionals, abort the
-program when expected conditions are not met, introduce your own
-verbose logging levels, and more.  This document describes the
-functionality supported by glog.  Please note that this document
-doesn't describe all features in this library, but the most useful
-ones.  If you want to find less common features, please check
-header files under <code>src/glog</code> directory.
-
-<h2> <A NAME=severity>Severity Level</A> </h2>
-
-<p>
-You can specify one of the following severity levels (in
-increasing order of severity): <code>INFO</code>, <code>WARNING</code>,
-<code>ERROR</code>, and <code>FATAL</code>.
-Logging a <code>FATAL</code> message terminates the program (after the
-message is logged).
-Note that messages of a given severity are logged not only in the
-logfile for that severity, but also in all logfiles of lower severity.
-E.g., a message of severity <code>FATAL</code> will be logged to the
-logfiles of severity <code>FATAL</code>, <code>ERROR</code>,
-<code>WARNING</code>, and <code>INFO</code>.
-
-<p>
-The <code>DFATAL</code> severity logs a <code>FATAL</code> error in
-debug mode (i.e., there is no <code>NDEBUG</code> macro defined), but
-avoids halting the program in production by automatically reducing the
-severity to <code>ERROR</code>.
-
-<p>Unless otherwise specified, glog writes to the filename
-"/tmp/&lt;program name&gt;.&lt;hostname&gt;.&lt;user name&gt;.log.&lt;severity level&gt;.&lt;date&gt;.&lt;time&gt;.&lt;pid&gt;"
-(e.g., "/tmp/hello_world.example.com.hamaji.log.INFO.20080709-222411.10474").
-By default, glog copies the log messages of severity level
-<code>ERROR</code> or <code>FATAL</code> to standard error (stderr)
-in addition to log files.
-
-<h2><A NAME=flags>Setting Flags</A></h2>
-
-<p>Several flags influence glog's output behavior.
-If the <a href="https://github.com/gflags/gflags">Google
-gflags library</a> is installed on your machine, the
-<code>configure</code> script (see the INSTALL file in the package for
-detail of this script) will automatically detect and use it,
-allowing you to pass flags on the command line.  For example, if you
-want to turn the flag <code>--logtostderr</code> on, you can start
-your application with the following command line:
-
-<pre>
-   ./your_application --logtostderr=1
-</pre>
-
-If the Google gflags library isn't installed, you set flags via
-environment variables, prefixing the flag name with "GLOG_", e.g.
-
-<pre>
-   GLOG_logtostderr=1 ./your_application
-</pre>
-
-<!-- TODO(hamaji): Fill the version number
-<p>By glog version 0.x.x, you can use GLOG_* environment variables
-even if you have gflags. If both an environment variable and a flag
-are specified, the value specified by a flag wins. E.g., if GLOG_v=0
-and --v=1, the verbosity will be 1, not 0.
--->
-
-<p>The following flags are most commonly used:
-
-<dl>
-<dt><code>logtostderr</code> (<code>bool</code>, default=<code>false</code>)
-<dd>Log messages to stderr instead of logfiles.<br>
-Note: you can set binary flags to <code>true</code> by specifying
-<code>1</code>, <code>true</code>, or <code>yes</code> (case
-insensitive).
-Also, you can set binary flags to <code>false</code> by specifying
-<code>0</code>, <code>false</code>, or <code>no</code> (again, case
-insensitive).
-<dt><code>stderrthreshold</code> (<code>int</code>, default=2, which
-is <code>ERROR</code>)
-<dd>Copy log messages at or above this level to stderr in
-addition to logfiles.  The numbers of severity levels
-<code>INFO</code>, <code>WARNING</code>, <code>ERROR</code>, and
-<code>FATAL</code> are 0, 1, 2, and 3, respectively.
-<dt><code>minloglevel</code> (<code>int</code>, default=0, which
-is <code>INFO</code>)
-<dd>Log messages at or above this level.  Again, the numbers of
-severity levels <code>INFO</code>, <code>WARNING</code>,
-<code>ERROR</code>, and <code>FATAL</code> are 0, 1, 2, and 3,
-respectively.
-<dt><code>log_dir</code> (<code>string</code>, default="")
-<dd>If specified, logfiles are written into this directory instead
-of the default logging directory.
-<dt><code>v</code> (<code>int</code>, default=0)
-<dd>Show all <code>VLOG(m)</code> messages for <code>m</code> less or
-equal the value of this flag.  Overridable by --vmodule.
-See <a href="#verbose">the section about verbose logging</a> for more
-detail.
-<dt><code>vmodule</code> (<code>string</code>, default="")
-<dd>Per-module verbose level.  The argument has to contain a
-comma-separated list of &lt;module name&gt;=&lt;log level&gt;.
-&lt;module name&gt;
-is a glob pattern (e.g., <code>gfs*</code> for all modules whose name
-starts with "gfs"), matched against the filename base
-(that is, name ignoring .cc/.h./-inl.h).
-&lt;log level&gt; overrides any value given by --v.
-See also <a href="#verbose">the section about verbose logging</a>.
-</dl>
-
-<p>There are some other flags defined in logging.cc.  Please grep the
-source code for "DEFINE_" to see a complete list of all flags.
-
-<p>You can also modify flag values in your program by modifying global
-variables <code>FLAGS_*</code> . Most settings start working
-immediately after you update <code>FLAGS_*</code> . The exceptions are
-the flags related to destination files. For example, you might want to
-set <code>FLAGS_log_dir</code> before
-calling <code>google::InitGoogleLogging</code> . Here is an example:
-
-<pre>
-   LOG(INFO) << "file";
-   // Most flags work immediately after updating values.
-   FLAGS_logtostderr = 1;
-   LOG(INFO) << "stderr";
-   FLAGS_logtostderr = 0;
-   // This won't change the log destination. If you want to set this
-   // value, you should do this before google::InitGoogleLogging .
-   FLAGS_log_dir = "/some/log/directory";
-   LOG(INFO) << "the same file";
-</pre>
-
-<h2><A NAME=conditional>Conditional / Occasional Logging</A></h2>
-
-<p>Sometimes, you may only want to log a message under certain
-conditions. You can use the following macros to perform conditional
-logging:
-
-<pre>
-   LOG_IF(INFO, num_cookies &gt; 10) &lt;&lt; "Got lots of cookies";
-</pre>
-
-The "Got lots of cookies" message is logged only when the variable
-<code>num_cookies</code> exceeds 10.
-
-If a line of code is executed many times, it may be useful to only log
-a message at certain intervals.  This kind of logging is most useful
-for informational messages.
-
-<pre>
-   LOG_EVERY_N(INFO, 10) &lt;&lt; "Got the " &lt;&lt; google::COUNTER &lt;&lt; "th cookie";
-</pre>
-
-<p>The above line outputs a log messages on the 1st, 11th,
-21st, ... times it is executed.  Note that the special
-<code>google::COUNTER</code> value is used to identify which repetition is
-happening.
-
-<p>You can combine conditional and occasional logging with the
-following macro.
-
-<pre>
-   LOG_IF_EVERY_N(INFO, (size &gt; 1024), 10) &lt;&lt; "Got the " &lt;&lt; google::COUNTER
-                                           &lt;&lt; "th big cookie";
-</pre>
-
-<p>Instead of outputting a message every nth time, you can also limit
-the output to the first n occurrences:
-
-<pre>
-   LOG_FIRST_N(INFO, 20) &lt;&lt; "Got the " &lt;&lt; google::COUNTER &lt;&lt; "th cookie";
-</pre>
-
-<p>Outputs log messages for the first 20 times it is executed.  Again,
-the <code>google::COUNTER</code> identifier indicates which repetition is
-happening.
-
-<h2><A NAME=debug>Debug Mode Support</A></h2>
-
-<p>Special "debug mode" logging macros only have an effect in debug
-mode and are compiled away to nothing for non-debug mode
-compiles.  Use these macros to avoid slowing down your production
-application due to excessive logging.
-
-<pre>
-   DLOG(INFO) &lt;&lt; "Found cookies";
-
-   DLOG_IF(INFO, num_cookies &gt; 10) &lt;&lt; "Got lots of cookies";
-
-   DLOG_EVERY_N(INFO, 10) &lt;&lt; "Got the " &lt;&lt; google::COUNTER &lt;&lt; "th cookie";
-</pre>
-
-<h2><A NAME=check>CHECK Macros</A></h2>
-
-<p>It is a good practice to check expected conditions in your program
-frequently to detect errors as early as possible. The
-<code>CHECK</code> macro provides the ability to abort the application
-when a condition is not met, similar to the <code>assert</code> macro
-defined in the standard C library.
-
-<p><code>CHECK</code> aborts the application if a condition is not
-true.  Unlike <code>assert</code>, it is *not* controlled by
-<code>NDEBUG</code>, so the check will be executed regardless of
-compilation mode.  Therefore, <code>fp-&gt;Write(x)</code> in the
-following example is always executed:
-
-<pre>
-   CHECK(fp-&gt;Write(x) == 4) &lt;&lt; "Write failed!";
-</pre>
-
-<p>There are various helper macros for
-equality/inequality checks - <code>CHECK_EQ</code>,
-<code>CHECK_NE</code>, <code>CHECK_LE</code>, <code>CHECK_LT</code>,
-<code>CHECK_GE</code>, and <code>CHECK_GT</code>.
-They compare two values, and log a
-<code>FATAL</code> message including the two values when the result is
-not as expected.  The values must have <code>operator&lt;&lt;(ostream,
-...)</code> defined.
-
-<p>You may append to the error message like so:
-
-<pre>
-   CHECK_NE(1, 2) &lt;&lt; ": The world must be ending!";
-</pre>
-
-<p>We are very careful to ensure that each argument is evaluated exactly
-once, and that anything which is legal to pass as a function argument is
-legal here.  In particular, the arguments may be temporary expressions
-which will end up being destroyed at the end of the apparent statement,
-for example:
-
-<pre>
-   CHECK_EQ(string("abc")[1], 'b');
-</pre>
-
-<p>The compiler reports an error if one of the arguments is a
-pointer and the other is NULL. To work around this, simply static_cast
-NULL to the type of the desired pointer.
-
-<pre>
-   CHECK_EQ(some_ptr, static_cast&lt;SomeType*&gt;(NULL));
-</pre>
-
-<p>Better yet, use the CHECK_NOTNULL macro:
-
-<pre>
-   CHECK_NOTNULL(some_ptr);
-   some_ptr-&gt;DoSomething();
-</pre>
-
-<p>Since this macro returns the given pointer, this is very useful in
-constructor initializer lists.
-
-<pre>
-   struct S {
-     S(Something* ptr) : ptr_(CHECK_NOTNULL(ptr)) {}
-     Something* ptr_;
-   };
-</pre>
-
-<p>Note that you cannot use this macro as a C++ stream due to this
-feature.  Please use <code>CHECK_EQ</code> described above to log a
-custom message before aborting the application.
-
-<p>If you are comparing C strings (char *), a handy set of macros
-performs case sensitive as well as case insensitive comparisons -
-<code>CHECK_STREQ</code>, <code>CHECK_STRNE</code>,
-<code>CHECK_STRCASEEQ</code>, and <code>CHECK_STRCASENE</code>.  The
-CASE versions are case-insensitive.  You can safely pass <code>NULL</code>
-pointers for this macro.  They treat <code>NULL</code> and any
-non-<code>NULL</code> string as not equal.  Two <code>NULL</code>s are
-equal.
-
-<p>Note that both arguments may be temporary strings which are
-destructed at the end of the current "full expression"
-(e.g., <code>CHECK_STREQ(Foo().c_str(), Bar().c_str())</code> where
-<code>Foo</code> and <code>Bar</code> return C++'s
-<code>std::string</code>).
-
-<p>The <code>CHECK_DOUBLE_EQ</code> macro checks the equality of two
-floating point values, accepting a small error margin.
-<code>CHECK_NEAR</code> accepts a third floating point argument, which
-specifies the acceptable error margin.
-
-<h2><A NAME=verbose>Verbose Logging</A></h2>
-
-<p>When you are chasing difficult bugs, thorough log messages are very
-useful.  However, you may want to ignore too verbose messages in usual
-development.  For such verbose logging, glog provides the
-<code>VLOG</code> macro, which allows you to define your own numeric
-logging levels.  The <code>--v</code> command line option controls
-which verbose messages are logged:
-
-<pre>
-   VLOG(1) &lt;&lt; "I'm printed when you run the program with --v=1 or higher";
-   VLOG(2) &lt;&lt; "I'm printed when you run the program with --v=2 or higher";
-</pre>
-
-<p>With <code>VLOG</code>, the lower the verbose level, the more
-likely messages are to be logged.  For example, if
-<code>--v==1</code>, <code>VLOG(1)</code> will log, but
-<code>VLOG(2)</code> will not log.  This is opposite of the severity
-level, where <code>INFO</code> is 0, and <code>ERROR</code> is 2.
-<code>--minloglevel</code> of 1 will log <code>WARNING</code> and
-above.  Though you can specify any integers for both <code>VLOG</code>
-macro and <code>--v</code> flag, the common values for them are small
-positive integers.  For example, if you write <code>VLOG(0)</code>,
-you should specify <code>--v=-1</code> or lower to silence it.  This
-is less useful since we may not want verbose logs by default in most
-cases.  The <code>VLOG</code> macros always log at the
-<code>INFO</code> log level (when they log at all).
-
-<p>Verbose logging can be controlled from the command line on a
-per-module basis:
-
-<pre>
-   --vmodule=mapreduce=2,file=1,gfs*=3 --v=0
-</pre>
-
-<p>will:
-
-<ul>
-  <li>a. Print VLOG(2) and lower messages from mapreduce.{h,cc}
-  <li>b. Print VLOG(1) and lower messages from file.{h,cc}
-  <li>c. Print VLOG(3) and lower messages from files prefixed with "gfs"
-  <li>d. Print VLOG(0) and lower messages from elsewhere
-</ul>
-
-<p>The wildcarding functionality shown by (c) supports both '*'
-(matches 0 or more characters) and '?' (matches any single character)
-wildcards.  Please also check the section about <a
-href="#flags">command line flags</a>.
-
-<p>There's also <code>VLOG_IS_ON(n)</code> "verbose level" condition
-macro.  This macro returns true when the <code>--v</code> is equal or
-greater than <code>n</code>.  To be used as
-
-<pre>
-   if (VLOG_IS_ON(2)) {
-     // do some logging preparation and logging
-     // that can't be accomplished with just VLOG(2) &lt;&lt; ...;
-   }
-</pre>
-
-<p>Verbose level condition macros <code>VLOG_IF</code>,
-<code>VLOG_EVERY_N</code> and <code>VLOG_IF_EVERY_N</code> behave
-analogous to <code>LOG_IF</code>, <code>LOG_EVERY_N</code>,
-<code>LOF_IF_EVERY</code>, but accept a numeric verbosity level as
-opposed to a severity level.
-
-<pre>
-   VLOG_IF(1, (size &gt; 1024))
-      &lt;&lt; "I'm printed when size is more than 1024 and when you run the "
-         "program with --v=1 or more";
-   VLOG_EVERY_N(1, 10)
-      &lt;&lt; "I'm printed every 10th occurrence, and when you run the program "
-         "with --v=1 or more. Present occurence is " &lt;&lt; google::COUNTER;
-   VLOG_IF_EVERY_N(1, (size &gt; 1024), 10)
-      &lt;&lt; "I'm printed on every 10th occurence of case when size is more "
-         " than 1024, when you run the program with --v=1 or more. ";
-         "Present occurence is " &lt;&lt; google::COUNTER;
-</pre>
-
-<h2> <A name="signal">Failure Signal Handler</A> </h2>
-
-<p>
-The library provides a convenient signal handler that will dump useful
-information when the program crashes on certain signals such as SIGSEGV.
-The signal handler can be installed by
-google::InstallFailureSignalHandler().  The following is an example of output
-from the signal handler.
-
-<pre>
-*** Aborted at 1225095260 (unix time) try "date -d @1225095260" if you are using GNU date ***
-*** SIGSEGV (@0x0) received by PID 17711 (TID 0x7f893090a6f0) from PID 0; stack trace: ***
-PC: @           0x412eb1 TestWaitingLogSink::send()
-    @     0x7f892fb417d0 (unknown)
-    @           0x412eb1 TestWaitingLogSink::send()
-    @     0x7f89304f7f06 google::LogMessage::SendToLog()
-    @     0x7f89304f35af google::LogMessage::Flush()
-    @     0x7f89304f3739 google::LogMessage::~LogMessage()
-    @           0x408cf4 TestLogSinkWaitTillSent()
-    @           0x4115de main
-    @     0x7f892f7ef1c4 (unknown)
-    @           0x4046f9 (unknown)
-</pre>
-
-<p>
-By default, the signal handler writes the failure dump to the standard
-error.  You can customize the destination by InstallFailureWriter().
-
-<h2> <A name="misc">Miscellaneous Notes</A> </h2>
-
-<h3><A NAME=message>Performance of Messages</A></h3>
-
-<p>The conditional logging macros provided by glog (e.g.,
-<code>CHECK</code>, <code>LOG_IF</code>, <code>VLOG</code>, ...) are
-carefully implemented and don't execute the right hand side
-expressions when the conditions are false.  So, the following check
-may not sacrifice the performance of your application.
-
-<pre>
-   CHECK(obj.ok) &lt;&lt; obj.CreatePrettyFormattedStringButVerySlow();
-</pre>
-
-<h3><A NAME=failure>User-defined Failure Function</A></h3>
-
-<p><code>FATAL</code> severity level messages or unsatisfied
-<code>CHECK</code> condition terminate your program.  You can change
-the behavior of the termination by
-<code>InstallFailureFunction</code>.
-
-<pre>
-   void YourFailureFunction() {
-     // Reports something...
-     exit(1);
-   }
-
-   int main(int argc, char* argv[]) {
-     google::InstallFailureFunction(&amp;YourFailureFunction);
-   }
-</pre>
-
-<p>By default, glog tries to dump stacktrace and makes the program
-exit with status 1.  The stacktrace is produced only when you run the
-program on an architecture for which glog supports stack tracing (as
-of September 2008, glog supports stack tracing for x86 and x86_64).
-
-<h3><A NAME=raw>Raw Logging</A></h3>
-
-<p>The header file <code>&lt;glog/raw_logging.h&gt;</code> can be
-used for thread-safe logging, which does not allocate any memory or
-acquire any locks.  Therefore, the macros defined in this
-header file can be used by low-level memory allocation and
-synchronization code.
-Please check <code>src/glog/raw_logging.h.in</code> for detail.
-</p>
-
-<h3><A NAME=plog>Google Style perror()</A></h3>
-
-<p><code>PLOG()</code> and <code>PLOG_IF()</code> and
-<code>PCHECK()</code> behave exactly like their <code>LOG*</code> and
-<code>CHECK</code> equivalents with the addition that they append a
-description of the current state of errno to their output lines.
-E.g.
-
-<pre>
-   PCHECK(write(1, NULL, 2) &gt;= 0) &lt;&lt; "Write NULL failed";
-</pre>
-
-<p>This check fails with the following error message.
-
-<pre>
-   F0825 185142 test.cc:22] Check failed: write(1, NULL, 2) &gt;= 0 Write NULL failed: Bad address [14]
-</pre>
-
-<h3><A NAME=syslog>Syslog</A></h3>
-
-<p><code>SYSLOG</code>, <code>SYSLOG_IF</code>, and
-<code>SYSLOG_EVERY_N</code> macros are available.
-These log to syslog in addition to the normal logs.  Be aware that
-logging to syslog can drastically impact performance, especially if
-syslog is configured for remote logging!  Make sure you understand the
-implications of outputting to syslog before you use these macros. In
-general, it's wise to use these macros sparingly.
-
-<h3><A NAME=strip>Strip Logging Messages</A></h3>
-
-<p>Strings used in log messages can increase the size of your binary
-and present a privacy concern.  You can therefore instruct glog to
-remove all strings which fall below a certain severity level by using
-the GOOGLE_STRIP_LOG macro:
-
-<p>If your application has code like this:
-
-<pre>
-   #define GOOGLE_STRIP_LOG 1    // this must go before the #include!
-   #include &lt;glog/logging.h&gt;
-</pre>
-
-<p>The compiler will remove the log messages whose severities are less
-than the specified integer value.  Since
-<code>VLOG</code> logs at the severity level <code>INFO</code>
-(numeric value <code>0</code>),
-setting <code>GOOGLE_STRIP_LOG</code> to 1 or greater removes
-all log messages associated with <code>VLOG</code>s as well as
-<code>INFO</code> log statements.
-
-<h3><A NAME=strip>Automatically Remove Old Logs</A></h3>
-
-<p>To enable the log cleaner:
-
-<pre>
-google::EnableLogCleaner(3); // keep your logs for 3 days
-</pre>
-
-And then Google glog will check if there are overdue logs whenever
-a flush is performed. In this example, any log file from your project whose
-last modified time is greater than 3 days will be unlink()ed.
-
-<p>This feature can be disabled at any time (if it has been enabled)
-
-<pre>
-google::DisableLogCleaner();
-</pre>
-
-<h3><A NAME=windows>Notes for Windows users</A></h3>
-
-<p>Google glog defines a severity level <code>ERROR</code>, which is
-also defined in <code>windows.h</code> . You can make glog not define
-<code>INFO</code>, <code>WARNING</code>, <code>ERROR</code>,
-and <code>FATAL</code> by defining
-<code>GLOG_NO_ABBREVIATED_SEVERITIES</code> before
-including <code>glog/logging.h</code> . Even with this macro, you can
-still use the iostream like logging facilities:
-
-<pre>
-  #define GLOG_NO_ABBREVIATED_SEVERITIES
-  #include &lt;windows.h&gt;
-  #include &lt;glog/logging.h&gt;
-
-  // ...
-
-  LOG(ERROR) &lt;&lt; "This should work";
-  LOG_IF(ERROR, x &gt; y) &lt;&lt; "This should be also OK";
-</pre>
-
-<p>
-However, you cannot
-use <code>INFO</code>, <code>WARNING</code>, <code>ERROR</code>,
-and <code>FATAL</code> anymore for functions defined
-in <code>glog/logging.h</code> .
-
-<pre>
-  #define GLOG_NO_ABBREVIATED_SEVERITIES
-  #include &lt;windows.h&gt;
-  #include &lt;glog/logging.h&gt;
-
-  // ...
-
-  // This won't work.
-  // google::FlushLogFiles(google::ERROR);
-
-  // Use this instead.
-  google::FlushLogFiles(google::GLOG_ERROR);
-</pre>
-
-<p>
-If you don't need <code>ERROR</code> defined
-by <code>windows.h</code>, there are a couple of more workarounds
-which sometimes don't work:
-
-<ul>
-  <li>#define <code>WIN32_LEAN_AND_MEAN</code> or <code>NOGDI</code>
-      <strong>before</strong> you #include <code>windows.h</code> .
-  <li>#undef <code>ERROR</code> <strong>after</strong> you #include
-      <code>windows.h</code> .
-</ul>
-
-<p>See <a href="http://code.google.com/p/google-glog/issues/detail?id=33">
-this issue</a> for more detail.
-
-<hr>
-<address>
-Shinichiro Hamaji<br>
-Gregor Hohpe<br>
-<script type=text/javascript>
-  var lm = new Date(document.lastModified);
-  document.write(lm.toDateString());
-</script>
-</address>
-
-</body>
-</html>
diff --git a/third_party/glog/glog-config.cmake.in b/third_party/glog/glog-config.cmake.in
deleted file mode 100644
index 5c5c9c0da2..0000000000
--- a/third_party/glog/glog-config.cmake.in
+++ /dev/null
@@ -1,13 +0,0 @@
-if (CMAKE_VERSION VERSION_LESS @glog_CMake_VERSION@)
-  message (FATAL_ERROR "CMake >= @glog_CMake_VERSION@ required")
-endif (CMAKE_VERSION VERSION_LESS @glog_CMake_VERSION@)
-
-@PACKAGE_INIT@
-
-include (CMakeFindDependencyMacro)
-include (${CMAKE_CURRENT_LIST_DIR}/glog-modules.cmake)
-
-@gflags_DEPENDENCY@
-@Unwind_DEPENDENCY@
-
-include (${CMAKE_CURRENT_LIST_DIR}/glog-targets.cmake)
diff --git a/third_party/glog/glog-modules.cmake.in b/third_party/glog/glog-modules.cmake.in
deleted file mode 100644
index 71c516078c..0000000000
--- a/third_party/glog/glog-modules.cmake.in
+++ /dev/null
@@ -1,18 +0,0 @@
-cmake_policy (PUSH)
-cmake_policy (SET CMP0057 NEW)
-
-if (CMAKE_VERSION VERSION_LESS 3.3)
-  message (FATAL_ERROR "glog-modules.cmake requires the consumer "
-    "to use CMake 3.3 (or newer)")
-endif (CMAKE_VERSION VERSION_LESS 3.3)
-
-set (glog_MODULE_PATH "@glog_FULL_CMake_DATADIR@")
-list (APPEND CMAKE_MODULE_PATH ${glog_MODULE_PATH})
-
-if (NOT glog_MODULE_PATH IN_LIST CMAKE_MODULE_PATH)
-  message (FATAL_ERROR "Cannot add '${glog_MODULE_PATH}' to "
-    "CMAKE_MODULE_PATH. This will cause glog-config.cmake to fail at "
-    "locating required find modules. Make sure CMAKE_MODULE_PATH is not a cache variable.")
-endif (NOT glog_MODULE_PATH IN_LIST CMAKE_MODULE_PATH)
-
-cmake_policy (POP)
diff --git a/third_party/glog/libglog.pc.in b/third_party/glog/libglog.pc.in
deleted file mode 100644
index ad2b0774d8..0000000000
--- a/third_party/glog/libglog.pc.in
+++ /dev/null
@@ -1,10 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: libglog
-Description: Google Log (glog) C++ logging framework
-Version: @VERSION@
-Libs: -L${libdir} -lglog
-Cflags: -I${includedir}
diff --git a/third_party/glog/m4/ac_have_attribute.m4 b/third_party/glog/m4/ac_have_attribute.m4
deleted file mode 100644
index 19f4021e99..0000000000
--- a/third_party/glog/m4/ac_have_attribute.m4
+++ /dev/null
@@ -1,16 +0,0 @@
-AC_DEFUN([AX_C___ATTRIBUTE__], [
-  AC_MSG_CHECKING(for __attribute__)
-  AC_CACHE_VAL(ac_cv___attribute__, [
-    AC_TRY_COMPILE(
-      [#include <stdlib.h>
-       static void foo(void) __attribute__ ((unused));
-       void foo(void) { exit(1); }],
-      [],
-      ac_cv___attribute__=yes,
-      ac_cv___attribute__=no
-    )])
-  if test "$ac_cv___attribute__" = "yes"; then
-    AC_DEFINE(HAVE___ATTRIBUTE__, 1, [define if your compiler has __attribute__])
-  fi
-  AC_MSG_RESULT($ac_cv___attribute__)
-])
diff --git a/third_party/glog/m4/ac_have_builtin_expect.m4 b/third_party/glog/m4/ac_have_builtin_expect.m4
deleted file mode 100644
index e91b6fd987..0000000000
--- a/third_party/glog/m4/ac_have_builtin_expect.m4
+++ /dev/null
@@ -1,14 +0,0 @@
-AC_DEFUN([AX_C___BUILTIN_EXPECT], [
-  AC_MSG_CHECKING(for __builtin_expect)
-  AC_CACHE_VAL(ac_cv___builtin_expect, [
-    AC_TRY_COMPILE(
-      [int foo(void) { if (__builtin_expect(0, 0)) return 1; return 0; }],
-      [],
-      ac_cv___builtin_expect=yes,
-      ac_cv___builtin_expect=no
-    )])
-  if test "$ac_cv___builtin_expect" = "yes"; then
-    AC_DEFINE(HAVE___BUILTIN_EXPECT, 1, [define if your compiler has __builtin_expect])
-  fi
-  AC_MSG_RESULT($ac_cv___builtin_expect)
-])
diff --git a/third_party/glog/m4/ac_have_sync_val_compare_and_swap.m4 b/third_party/glog/m4/ac_have_sync_val_compare_and_swap.m4
deleted file mode 100644
index 88b027e23a..0000000000
--- a/third_party/glog/m4/ac_have_sync_val_compare_and_swap.m4
+++ /dev/null
@@ -1,14 +0,0 @@
-AC_DEFUN([AX_C___SYNC_VAL_COMPARE_AND_SWAP], [
-  AC_MSG_CHECKING(for __sync_val_compare_and_swap)
-  AC_CACHE_VAL(ac_cv___sync_val_compare_and_swap, [
-    AC_TRY_LINK(
-      [],
-      [int a; if (__sync_val_compare_and_swap(&a, 0, 1)) return 1; return 0;],
-      ac_cv___sync_val_compare_and_swap=yes,
-      ac_cv___sync_val_compare_and_swap=no
-    )])
-  if test "$ac_cv___sync_val_compare_and_swap" = "yes"; then
-    AC_DEFINE(HAVE___SYNC_VAL_COMPARE_AND_SWAP, 1, [define if your compiler has __sync_val_compare_and_swap])
-  fi
-  AC_MSG_RESULT($ac_cv___sync_val_compare_and_swap)
-])
diff --git a/third_party/glog/m4/ac_rwlock.m4 b/third_party/glog/m4/ac_rwlock.m4
deleted file mode 100644
index 5065bcc1c5..0000000000
--- a/third_party/glog/m4/ac_rwlock.m4
+++ /dev/null
@@ -1,31 +0,0 @@
-# TODO(csilvers): it would be better to actually try to link against
-# -pthreads, to make sure it defines these methods, but that may be
-# too hard, since pthread support is really tricky.
-
-# Check for support for pthread_rwlock_init() etc.
-# These aren't posix, but are widely supported.  To get them on linux,
-# you need to define _XOPEN_SOURCE first, so this check assumes your
-# application does that.
-#
-# Note: OS X (as of 6/1/06) seems to support pthread_rwlock, but
-# doesn't define PTHREAD_RWLOCK_INITIALIZER.  Therefore, we don't test
-# that particularly macro.  It's probably best if you don't use that
-# macro in your code either.
-
-AC_DEFUN([AC_RWLOCK],
-[AC_CACHE_CHECK(support for pthread_rwlock_* functions,
-ac_cv_rwlock,
-[AC_LANG_SAVE
- AC_LANG_C
- AC_TRY_COMPILE([#define _XOPEN_SOURCE 500
-                 #include <pthread.h>],
-		[pthread_rwlock_t l; pthread_rwlock_init(&l, NULL);
-                 pthread_rwlock_rdlock(&l); 
-                 return 0;],
-                ac_cv_rwlock=yes, ac_cv_rwlock=no)
- AC_LANG_RESTORE
-])
-if test "$ac_cv_rwlock" = yes; then
-  AC_DEFINE(HAVE_RWLOCK,1,[define if the compiler implements pthread_rwlock_*])
-fi
-])
diff --git a/third_party/glog/m4/acx_pthread.m4 b/third_party/glog/m4/acx_pthread.m4
deleted file mode 100644
index 2cf20de144..0000000000
--- a/third_party/glog/m4/acx_pthread.m4
+++ /dev/null
@@ -1,363 +0,0 @@
-# This was retrieved from
-#    http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?revision=1277&root=avahi
-# See also (perhaps for new versions?)
-#    http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?root=avahi
-#
-# We've rewritten the inconsistency check code (from avahi), to work
-# more broadly.  In particular, it no longer assumes ld accepts -zdefs.
-# This caused a restructing of the code, but the functionality has only
-# changed a little.
-
-dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
-dnl
-dnl @summary figure out how to build C programs using POSIX threads
-dnl
-dnl This macro figures out how to build C programs using POSIX threads.
-dnl It sets the PTHREAD_LIBS output variable to the threads library and
-dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
-dnl C compiler flags that are needed. (The user can also force certain
-dnl compiler flags/libs to be tested by setting these environment
-dnl variables.)
-dnl
-dnl Also sets PTHREAD_CC to any special C compiler that is needed for
-dnl multi-threaded programs (defaults to the value of CC otherwise).
-dnl (This is necessary on AIX to use the special cc_r compiler alias.)
-dnl
-dnl NOTE: You are assumed to not only compile your program with these
-dnl flags, but also link it with them as well. e.g. you should link
-dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
-dnl $LIBS
-dnl
-dnl If you are only building threads programs, you may wish to use
-dnl these variables in your default LIBS, CFLAGS, and CC:
-dnl
-dnl        LIBS="$PTHREAD_LIBS $LIBS"
-dnl        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-dnl        CC="$PTHREAD_CC"
-dnl
-dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
-dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
-dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
-dnl
-dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
-dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
-dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
-dnl default action will define HAVE_PTHREAD.
-dnl
-dnl Please let the authors know if this macro fails on any platform, or
-dnl if you have any other suggestions or comments. This macro was based
-dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
-dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
-dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
-dnl We are also grateful for the helpful feedback of numerous users.
-dnl
-dnl @category InstalledPackages
-dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
-dnl @version 2006-05-29
-dnl @license GPLWithACException
-dnl 
-dnl Checks for GCC shared/pthread inconsistency based on work by
-dnl Marcin Owsiany <marcin@owsiany.pl>
-
-
-AC_DEFUN([ACX_PTHREAD], [
-AC_REQUIRE([AC_CANONICAL_HOST])
-AC_LANG_SAVE
-AC_LANG_C
-acx_pthread_ok=no
-
-# We used to check for pthread.h first, but this fails if pthread.h
-# requires special compiler flags (e.g. on True64 or Sequent).
-# It gets checked for in the link test anyway.
-
-# First of all, check if the user has set any of the PTHREAD_LIBS,
-# etcetera environment variables, and if threads linking works using
-# them:
-if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
-        save_CFLAGS="$CFLAGS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-        save_LIBS="$LIBS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
-        AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
-        AC_MSG_RESULT($acx_pthread_ok)
-        if test x"$acx_pthread_ok" = xno; then
-                PTHREAD_LIBS=""
-                PTHREAD_CFLAGS=""
-        fi
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
-fi
-
-# We must check for the threads library under a number of different
-# names; the ordering is very important because some systems
-# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
-# libraries is broken (non-POSIX).
-
-# Create a list of thread flags to try.  Items starting with a "-" are
-# C compiler flags, and other items are library names, except for "none"
-# which indicates that we try without any flags at all, and "pthread-config"
-# which is a program returning the flags for the Pth emulation library.
-
-acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
-
-# The ordering *is* (sometimes) important.  Some notes on the
-# individual items follow:
-
-# pthreads: AIX (must check this before -lpthread)
-# none: in case threads are in libc; should be tried before -Kthread and
-#       other compiler flags to prevent continual compiler warnings
-# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
-# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
-# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
-# -pthreads: Solaris/gcc
-# -mthreads: Mingw32/gcc, Lynx/gcc
-# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
-#      doesn't hurt to check since this sometimes defines pthreads too;
-#      also defines -D_REENTRANT)
-#      ... -mt is also the pthreads flag for HP/aCC
-# pthread: Linux, etcetera
-# --thread-safe: KAI C++
-# pthread-config: use pthread-config program (for GNU Pth library)
-
-case "${host_cpu}-${host_os}" in
-        *solaris*)
-
-        # On Solaris (at least, for some versions), libc contains stubbed
-        # (non-functional) versions of the pthreads routines, so link-based
-        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
-        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
-        # a function called by this macro, so we could check for that, but
-        # who knows whether they'll stub that too in a future libc.)  So,
-        # we'll just look for -pthreads and -lpthread first:
-
-        acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
-        ;;
-esac
-
-if test x"$acx_pthread_ok" = xno; then
-for flag in $acx_pthread_flags; do
-
-        case $flag in
-                none)
-                AC_MSG_CHECKING([whether pthreads work without any flags])
-                ;;
-
-                -*)
-                AC_MSG_CHECKING([whether pthreads work with $flag])
-                PTHREAD_CFLAGS="$flag"
-                ;;
-
-		pthread-config)
-		AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
-		if test x"$acx_pthread_config" = xno; then continue; fi
-		PTHREAD_CFLAGS="`pthread-config --cflags`"
-		PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
-		;;
-
-                *)
-                AC_MSG_CHECKING([for the pthreads library -l$flag])
-                PTHREAD_LIBS="-l$flag"
-                ;;
-        esac
-
-        save_LIBS="$LIBS"
-        save_CFLAGS="$CFLAGS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-
-        # Check for various functions.  We must include pthread.h,
-        # since some functions may be macros.  (On the Sequent, we
-        # need a special flag -Kthread to make this header compile.)
-        # We check for pthread_join because it is in -lpthread on IRIX
-        # while pthread_create is in libc.  We check for pthread_attr_init
-        # due to DEC craziness with -lpthreads.  We check for
-        # pthread_cleanup_push because it is one of the few pthread
-        # functions on Solaris that doesn't have a non-functional libc stub.
-        # We try pthread_create on general principles.
-        AC_TRY_LINK([#include <pthread.h>],
-                    [pthread_t th; pthread_join(th, 0);
-                     pthread_attr_init(0); pthread_cleanup_push(0, 0);
-                     pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
-                    [acx_pthread_ok=yes])
-
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
-
-        AC_MSG_RESULT($acx_pthread_ok)
-        if test "x$acx_pthread_ok" = xyes; then
-                break;
-        fi
-
-        PTHREAD_LIBS=""
-        PTHREAD_CFLAGS=""
-done
-fi
-
-# Various other checks:
-if test "x$acx_pthread_ok" = xyes; then
-        save_LIBS="$LIBS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-        save_CFLAGS="$CFLAGS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-
-        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
-	AC_MSG_CHECKING([for joinable pthread attribute])
-	attr_name=unknown
-	for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
-	    AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
-                        [attr_name=$attr; break])
-	done
-        AC_MSG_RESULT($attr_name)
-        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
-            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
-                               [Define to necessary symbol if this constant
-                                uses a non-standard name on your system.])
-        fi
-
-        AC_MSG_CHECKING([if more special flags are required for pthreads])
-        flag=no
-        case "${host_cpu}-${host_os}" in
-            *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
-            *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
-        esac
-        AC_MSG_RESULT(${flag})
-        if test "x$flag" != xno; then
-            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
-        fi
-
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
-        # More AIX lossage: must compile with xlc_r or cc_r
-	if test x"$GCC" != xyes; then
-          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
-        else
-          PTHREAD_CC=$CC
-	fi
-
-	# The next part tries to detect GCC inconsistency with -shared on some
-	# architectures and systems. The problem is that in certain
-	# configurations, when -shared is specified, GCC "forgets" to
-	# internally use various flags which are still necessary.
-	
-	#
-	# Prepare the flags
-	#
-	save_CFLAGS="$CFLAGS"
-	save_LIBS="$LIBS"
-	save_CC="$CC"
-	
-	# Try with the flags determined by the earlier checks.
-	#
-	# -Wl,-z,defs forces link-time symbol resolution, so that the
-	# linking checks with -shared actually have any value
-	#
-	# FIXME: -fPIC is required for -shared on many architectures,
-	# so we specify it here, but the right way would probably be to
-	# properly detect whether it is actually required.
-	CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS"
-	LIBS="$PTHREAD_LIBS $LIBS"
-	CC="$PTHREAD_CC"
-	
-	# In order not to create several levels of indentation, we test
-	# the value of "$done" until we find the cure or run out of ideas.
-	done="no"
-	
-	# First, make sure the CFLAGS we added are actually accepted by our
-	# compiler.  If not (and OS X's ld, for instance, does not accept -z),
-	# then we can't do this test.
-	if test x"$done" = xno; then
-	   AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies])
-	   AC_TRY_LINK(,, , [done=yes])
-	
-	   if test "x$done" = xyes ; then
-	      AC_MSG_RESULT([no])
-	   else
-	      AC_MSG_RESULT([yes])
-	   fi
-	fi
-	
-	if test x"$done" = xno; then
-	   AC_MSG_CHECKING([whether -pthread is sufficient with -shared])
-	   AC_TRY_LINK([#include <pthread.h>],
-	      [pthread_t th; pthread_join(th, 0);
-	      pthread_attr_init(0); pthread_cleanup_push(0, 0);
-	      pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
-	      [done=yes])
-	   
-	   if test "x$done" = xyes; then
-	      AC_MSG_RESULT([yes])
-	   else
-	      AC_MSG_RESULT([no])
-	   fi
-	fi
-	
-	#
-	# Linux gcc on some architectures such as mips/mipsel forgets
-	# about -lpthread
-	#
-	if test x"$done" = xno; then
-	   AC_MSG_CHECKING([whether -lpthread fixes that])
-	   LIBS="-lpthread $PTHREAD_LIBS $save_LIBS"
-	   AC_TRY_LINK([#include <pthread.h>],
-	      [pthread_t th; pthread_join(th, 0);
-	      pthread_attr_init(0); pthread_cleanup_push(0, 0);
-	      pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
-	      [done=yes])
-	
-	   if test "x$done" = xyes; then
-	      AC_MSG_RESULT([yes])
-	      PTHREAD_LIBS="-lpthread $PTHREAD_LIBS"
-	   else
-	      AC_MSG_RESULT([no])
-	   fi
-	fi
-	#
-	# FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc
-	#
-	if test x"$done" = xno; then
-	   AC_MSG_CHECKING([whether -lc_r fixes that])
-	   LIBS="-lc_r $PTHREAD_LIBS $save_LIBS"
-	   AC_TRY_LINK([#include <pthread.h>],
-	       [pthread_t th; pthread_join(th, 0);
-	        pthread_attr_init(0); pthread_cleanup_push(0, 0);
-	        pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
-	       [done=yes])
-	
-	   if test "x$done" = xyes; then
-	      AC_MSG_RESULT([yes])
-	      PTHREAD_LIBS="-lc_r $PTHREAD_LIBS"
-	   else
-	      AC_MSG_RESULT([no])
-	   fi
-	fi
-	if test x"$done" = xno; then
-	   # OK, we have run out of ideas
-	   AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries])
-	
-	   # so it's not safe to assume that we may use pthreads
-	   acx_pthread_ok=no
-	fi
-	
-	CFLAGS="$save_CFLAGS"
-	LIBS="$save_LIBS"
-	CC="$save_CC"
-else
-        PTHREAD_CC="$CC"
-fi
-
-AC_SUBST(PTHREAD_LIBS)
-AC_SUBST(PTHREAD_CFLAGS)
-AC_SUBST(PTHREAD_CC)
-
-# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
-if test x"$acx_pthread_ok" = xyes; then
-        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
-        :
-else
-        acx_pthread_ok=no
-        $2
-fi
-AC_LANG_RESTORE
-])dnl ACX_PTHREAD
diff --git a/third_party/glog/m4/google_namespace.m4 b/third_party/glog/m4/google_namespace.m4
deleted file mode 100644
index 79e0a6d42a..0000000000
--- a/third_party/glog/m4/google_namespace.m4
+++ /dev/null
@@ -1,36 +0,0 @@
-# Allow users to override the namespace we define our application's classes in
-# Arg $1 is the default namespace to use if --enable-namespace isn't present.
-
-# In general, $1 should be 'google', so we put all our exported symbols in a
-# unique namespace that is not likely to conflict with anyone else.  However,
-# when it makes sense -- for instance, when publishing stl-like code -- you
-# may want to go with a different default, like 'std'.
-
-AC_DEFUN([AC_DEFINE_GOOGLE_NAMESPACE],
-  [google_namespace_default=[$1]
-   AC_ARG_ENABLE(namespace, [  --enable-namespace=FOO to define these Google
-                             classes in the FOO namespace. --disable-namespace
-                             to define them in the global namespace. Default
-                             is to define them in namespace $1.],
-                 [case "$enableval" in
-                    yes) google_namespace="$google_namespace_default" ;;
-                     no) google_namespace="" ;;
-                      *) google_namespace="$enableval" ;;
-                  esac],
-                 [google_namespace="$google_namespace_default"])
-   if test -n "$google_namespace"; then
-     ac_google_namespace="$google_namespace"
-     ac_google_start_namespace="namespace $google_namespace {"
-     ac_google_end_namespace="}"
-   else
-     ac_google_namespace=""
-     ac_google_start_namespace=""
-     ac_google_end_namespace=""
-   fi
-   AC_DEFINE_UNQUOTED(GOOGLE_NAMESPACE, $ac_google_namespace,
-                      Namespace for Google classes)
-   AC_DEFINE_UNQUOTED(_START_GOOGLE_NAMESPACE_, $ac_google_start_namespace,
-                      Puts following code inside the Google namespace)
-   AC_DEFINE_UNQUOTED(_END_GOOGLE_NAMESPACE_,  $ac_google_end_namespace,
-                      Stops putting the code inside the Google namespace)
-])
diff --git a/third_party/glog/m4/ltsugar.m4 b/third_party/glog/m4/ltsugar.m4
deleted file mode 100644
index 9000a057d3..0000000000
--- a/third_party/glog/m4/ltsugar.m4
+++ /dev/null
@@ -1,123 +0,0 @@
-# ltsugar.m4 -- libtool m4 base layer.                         -*-Autoconf-*-
-#
-# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
-# Written by Gary V. Vaughan, 2004
-#
-# This file is free software; the Free Software Foundation gives
-# unlimited permission to copy and/or distribute it, with or without
-# modifications, as long as this notice is preserved.
-
-# serial 6 ltsugar.m4
-
-# This is to help aclocal find these macros, as it can't see m4_define.
-AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
-
-
-# lt_join(SEP, ARG1, [ARG2...])
-# -----------------------------
-# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
-# associated separator.
-# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
-# versions in m4sugar had bugs.
-m4_define([lt_join],
-[m4_if([$#], [1], [],
-       [$#], [2], [[$2]],
-       [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
-m4_define([_lt_join],
-[m4_if([$#$2], [2], [],
-       [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
-
-
-# lt_car(LIST)
-# lt_cdr(LIST)
-# ------------
-# Manipulate m4 lists.
-# These macros are necessary as long as will still need to support
-# Autoconf-2.59 which quotes differently.
-m4_define([lt_car], [[$1]])
-m4_define([lt_cdr],
-[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
-       [$#], 1, [],
-       [m4_dquote(m4_shift($@))])])
-m4_define([lt_unquote], $1)
-
-
-# lt_append(MACRO-NAME, STRING, [SEPARATOR])
-# ------------------------------------------
-# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
-# Note that neither SEPARATOR nor STRING are expanded; they are appended
-# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
-# No SEPARATOR is output if MACRO-NAME was previously undefined (different
-# than defined and empty).
-#
-# This macro is needed until we can rely on Autoconf 2.62, since earlier
-# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
-m4_define([lt_append],
-[m4_define([$1],
-	   m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
-
-
-
-# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
-# ----------------------------------------------------------
-# Produce a SEP delimited list of all paired combinations of elements of
-# PREFIX-LIST with SUFFIX1 through SUFFIXn.  Each element of the list
-# has the form PREFIXmINFIXSUFFIXn.
-# Needed until we can rely on m4_combine added in Autoconf 2.62.
-m4_define([lt_combine],
-[m4_if(m4_eval([$# > 3]), [1],
-       [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
-[[m4_foreach([_Lt_prefix], [$2],
-	     [m4_foreach([_Lt_suffix],
-		]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
-	[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
-
-
-# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
-# -----------------------------------------------------------------------
-# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
-# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
-m4_define([lt_if_append_uniq],
-[m4_ifdef([$1],
-	  [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
-		 [lt_append([$1], [$2], [$3])$4],
-		 [$5])],
-	  [lt_append([$1], [$2], [$3])$4])])
-
-
-# lt_dict_add(DICT, KEY, VALUE)
-# -----------------------------
-m4_define([lt_dict_add],
-[m4_define([$1($2)], [$3])])
-
-
-# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
-# --------------------------------------------
-m4_define([lt_dict_add_subkey],
-[m4_define([$1($2:$3)], [$4])])
-
-
-# lt_dict_fetch(DICT, KEY, [SUBKEY])
-# ----------------------------------
-m4_define([lt_dict_fetch],
-[m4_ifval([$3],
-	m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
-    m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
-
-
-# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
-# -----------------------------------------------------------------
-m4_define([lt_if_dict_fetch],
-[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
-	[$5],
-    [$6])])
-
-
-# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
-# --------------------------------------------------------------
-m4_define([lt_dict_filter],
-[m4_if([$5], [], [],
-  [lt_join(m4_quote(m4_default([$4], [[, ]])),
-           lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
-		      [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
-])
diff --git a/third_party/glog/m4/lt~obsolete.m4 b/third_party/glog/m4/lt~obsolete.m4
deleted file mode 100644
index c573da90c5..0000000000
--- a/third_party/glog/m4/lt~obsolete.m4
+++ /dev/null
@@ -1,98 +0,0 @@
-# lt~obsolete.m4 -- aclocal satisfying obsolete definitions.    -*-Autoconf-*-
-#
-#   Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
-#   Written by Scott James Remnant, 2004.
-#
-# This file is free software; the Free Software Foundation gives
-# unlimited permission to copy and/or distribute it, with or without
-# modifications, as long as this notice is preserved.
-
-# serial 5 lt~obsolete.m4
-
-# These exist entirely to fool aclocal when bootstrapping libtool.
-#
-# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
-# which have later been changed to m4_define as they aren't part of the
-# exported API, or moved to Autoconf or Automake where they belong.
-#
-# The trouble is, aclocal is a bit thick.  It'll see the old AC_DEFUN
-# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
-# using a macro with the same name in our local m4/libtool.m4 it'll
-# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
-# and doesn't know about Autoconf macros at all.)
-#
-# So we provide this file, which has a silly filename so it's always
-# included after everything else.  This provides aclocal with the
-# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
-# because those macros already exist, or will be overwritten later.
-# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. 
-#
-# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
-# Yes, that means every name once taken will need to remain here until
-# we give up compatibility with versions before 1.7, at which point
-# we need to keep only those names which we still refer to.
-
-# This is to help aclocal find these macros, as it can't see m4_define.
-AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
-
-m4_ifndef([AC_LIBTOOL_LINKER_OPTION],	[AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
-m4_ifndef([AC_PROG_EGREP],		[AC_DEFUN([AC_PROG_EGREP])])
-m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH],	[AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
-m4_ifndef([_LT_AC_SHELL_INIT],		[AC_DEFUN([_LT_AC_SHELL_INIT])])
-m4_ifndef([_LT_AC_SYS_LIBPATH_AIX],	[AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
-m4_ifndef([_LT_PROG_LTMAIN],		[AC_DEFUN([_LT_PROG_LTMAIN])])
-m4_ifndef([_LT_AC_TAGVAR],		[AC_DEFUN([_LT_AC_TAGVAR])])
-m4_ifndef([AC_LTDL_ENABLE_INSTALL],	[AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
-m4_ifndef([AC_LTDL_PREOPEN],		[AC_DEFUN([AC_LTDL_PREOPEN])])
-m4_ifndef([_LT_AC_SYS_COMPILER],	[AC_DEFUN([_LT_AC_SYS_COMPILER])])
-m4_ifndef([_LT_AC_LOCK],		[AC_DEFUN([_LT_AC_LOCK])])
-m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE],	[AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
-m4_ifndef([_LT_AC_TRY_DLOPEN_SELF],	[AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
-m4_ifndef([AC_LIBTOOL_PROG_CC_C_O],	[AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
-m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
-m4_ifndef([AC_LIBTOOL_OBJDIR],		[AC_DEFUN([AC_LIBTOOL_OBJDIR])])
-m4_ifndef([AC_LTDL_OBJDIR],		[AC_DEFUN([AC_LTDL_OBJDIR])])
-m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
-m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP],	[AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
-m4_ifndef([AC_PATH_MAGIC],		[AC_DEFUN([AC_PATH_MAGIC])])
-m4_ifndef([AC_PROG_LD_GNU],		[AC_DEFUN([AC_PROG_LD_GNU])])
-m4_ifndef([AC_PROG_LD_RELOAD_FLAG],	[AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
-m4_ifndef([AC_DEPLIBS_CHECK_METHOD],	[AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
-m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
-m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
-m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
-m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS],	[AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
-m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP],	[AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
-m4_ifndef([LT_AC_PROG_EGREP],		[AC_DEFUN([LT_AC_PROG_EGREP])])
-m4_ifndef([LT_AC_PROG_SED],		[AC_DEFUN([LT_AC_PROG_SED])])
-m4_ifndef([_LT_CC_BASENAME],		[AC_DEFUN([_LT_CC_BASENAME])])
-m4_ifndef([_LT_COMPILER_BOILERPLATE],	[AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
-m4_ifndef([_LT_LINKER_BOILERPLATE],	[AC_DEFUN([_LT_LINKER_BOILERPLATE])])
-m4_ifndef([_AC_PROG_LIBTOOL],		[AC_DEFUN([_AC_PROG_LIBTOOL])])
-m4_ifndef([AC_LIBTOOL_SETUP],		[AC_DEFUN([AC_LIBTOOL_SETUP])])
-m4_ifndef([_LT_AC_CHECK_DLFCN],		[AC_DEFUN([_LT_AC_CHECK_DLFCN])])
-m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER],	[AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
-m4_ifndef([_LT_AC_TAGCONFIG],		[AC_DEFUN([_LT_AC_TAGCONFIG])])
-m4_ifndef([AC_DISABLE_FAST_INSTALL],	[AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
-m4_ifndef([_LT_AC_LANG_CXX],		[AC_DEFUN([_LT_AC_LANG_CXX])])
-m4_ifndef([_LT_AC_LANG_F77],		[AC_DEFUN([_LT_AC_LANG_F77])])
-m4_ifndef([_LT_AC_LANG_GCJ],		[AC_DEFUN([_LT_AC_LANG_GCJ])])
-m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
-m4_ifndef([_LT_AC_LANG_C_CONFIG],	[AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
-m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
-m4_ifndef([_LT_AC_LANG_CXX_CONFIG],	[AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
-m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
-m4_ifndef([_LT_AC_LANG_F77_CONFIG],	[AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
-m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
-m4_ifndef([_LT_AC_LANG_GCJ_CONFIG],	[AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
-m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
-m4_ifndef([_LT_AC_LANG_RC_CONFIG],	[AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
-m4_ifndef([AC_LIBTOOL_CONFIG],		[AC_DEFUN([AC_LIBTOOL_CONFIG])])
-m4_ifndef([_LT_AC_FILE_LTDLL_C],	[AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
-m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS],	[AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
-m4_ifndef([_LT_AC_PROG_CXXCPP],		[AC_DEFUN([_LT_AC_PROG_CXXCPP])])
-m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS],	[AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
-m4_ifndef([_LT_PROG_ECHO_BACKSLASH],	[AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
-m4_ifndef([_LT_PROG_F77],		[AC_DEFUN([_LT_PROG_F77])])
-m4_ifndef([_LT_PROG_FC],		[AC_DEFUN([_LT_PROG_FC])])
-m4_ifndef([_LT_PROG_CXX],		[AC_DEFUN([_LT_PROG_CXX])])
diff --git a/third_party/glog/m4/namespaces.m4 b/third_party/glog/m4/namespaces.m4
deleted file mode 100644
index d78dbe4cc2..0000000000
--- a/third_party/glog/m4/namespaces.m4
+++ /dev/null
@@ -1,15 +0,0 @@
-# Checks whether the compiler implements namespaces
-AC_DEFUN([AC_CXX_NAMESPACES],
- [AC_CACHE_CHECK(whether the compiler implements namespaces,
-                 ac_cv_cxx_namespaces,
-                 [AC_LANG_SAVE
-                  AC_LANG_CPLUSPLUS
-                  AC_TRY_COMPILE([namespace Outer {
-                                    namespace Inner { int i = 0; }}],
-                                 [using namespace Outer::Inner; return i;],
-                                 ac_cv_cxx_namespaces=yes,
-                                 ac_cv_cxx_namespaces=no)
-                  AC_LANG_RESTORE])
-  if test "$ac_cv_cxx_namespaces" = yes; then
-    AC_DEFINE(HAVE_NAMESPACES, 1, [define if the compiler implements namespaces])
-  fi])
diff --git a/third_party/glog/m4/pc_from_ucontext.m4 b/third_party/glog/m4/pc_from_ucontext.m4
deleted file mode 100644
index daffddb536..0000000000
--- a/third_party/glog/m4/pc_from_ucontext.m4
+++ /dev/null
@@ -1,71 +0,0 @@
-# We want to access the "PC" (Program Counter) register from a struct
-# ucontext.  Every system has its own way of doing that.  We try all the
-# possibilities we know about.  Note REG_PC should come first (REG_RIP
-# is also defined on solaris, but does the wrong thing).
-
-# OpenBSD doesn't have ucontext.h, but we can get PC from ucontext_t
-# by using signal.h.
-
-# The first argument of AC_PC_FROM_UCONTEXT will be invoked when we
-# cannot find a way to obtain PC from ucontext.
-
-AC_DEFUN([AC_PC_FROM_UCONTEXT],
-  [AC_CHECK_HEADERS(ucontext.h)
-   AC_CHECK_HEADERS(sys/ucontext.h)       # ucontext on OS X 10.6 (at least)
-   AC_MSG_CHECKING([how to access the program counter from a struct ucontext])
-   pc_fields="           uc_mcontext.gregs[[REG_PC]]"  # Solaris x86 (32 + 64 bit)
-   pc_fields="$pc_fields uc_mcontext.gregs[[REG_EIP]]" # Linux (i386)
-   pc_fields="$pc_fields uc_mcontext.gregs[[REG_RIP]]" # Linux (x86_64)
-   pc_fields="$pc_fields uc_mcontext.sc_ip"            # Linux (ia64)
-   pc_fields="$pc_fields uc_mcontext.uc_regs->gregs[[PT_NIP]]" # Linux (ppc)
-   pc_fields="$pc_fields uc_mcontext.gregs[[R15]]"     # Linux (arm old [untested])
-   pc_fields="$pc_fields uc_mcontext.arm_pc"           # Linux (arm new [untested])
-   pc_fields="$pc_fields uc_mcontext.mc_eip"           # FreeBSD (i386)
-   pc_fields="$pc_fields uc_mcontext.mc_rip"           # FreeBSD (x86_64 [untested])
-   pc_fields="$pc_fields uc_mcontext.__gregs[[_REG_EIP]]"  # NetBSD (i386)
-   pc_fields="$pc_fields uc_mcontext.__gregs[[_REG_RIP]]"  # NetBSD (x86_64)
-   pc_fields="$pc_fields uc_mcontext->ss.eip"          # OS X (i386, <=10.4)
-   pc_fields="$pc_fields uc_mcontext->__ss.__eip"      # OS X (i386, >=10.5)
-   pc_fields="$pc_fields uc_mcontext->ss.rip"          # OS X (x86_64)
-   pc_fields="$pc_fields uc_mcontext->__ss.__rip"      # OS X (>=10.5 [untested])
-   pc_fields="$pc_fields uc_mcontext->ss.srr0"         # OS X (ppc, ppc64 [untested])
-   pc_fields="$pc_fields uc_mcontext->__ss.__srr0"     # OS X (>=10.5 [untested])
-   pc_field_found=false
-   for pc_field in $pc_fields; do
-     if ! $pc_field_found; then
-       if test "x$ac_cv_header_sys_ucontext_h" = xyes; then
-         AC_TRY_COMPILE([#define _GNU_SOURCE 1
-                         #include <sys/ucontext.h>],
-                        [ucontext_t u; return u.$pc_field == 0;],
-                        AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
-                                           How to access the PC from a struct ucontext)
-                        AC_MSG_RESULT([$pc_field])
-                        pc_field_found=true)
-       else
-         AC_TRY_COMPILE([#define _GNU_SOURCE 1
-                         #include <ucontext.h>],
-                        [ucontext_t u; return u.$pc_field == 0;],
-                        AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
-                                           How to access the PC from a struct ucontext)
-                        AC_MSG_RESULT([$pc_field])
-                        pc_field_found=true)
-       fi
-     fi
-   done
-   if ! $pc_field_found; then
-     pc_fields="           sc_eip"  # OpenBSD (i386)
-     pc_fields="$pc_fields sc_rip"  # OpenBSD (x86_64)
-     for pc_field in $pc_fields; do
-       if ! $pc_field_found; then
-         AC_TRY_COMPILE([#include <signal.h>],
-                        [ucontext_t u; return u.$pc_field == 0;],
-                        AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
-                                           How to access the PC from a struct ucontext)
-                        AC_MSG_RESULT([$pc_field])
-                        pc_field_found=true)
-       fi
-     done
-   fi
-   if ! $pc_field_found; then
-     [$1]
-   fi])
diff --git a/third_party/glog/m4/stl_namespace.m4 b/third_party/glog/m4/stl_namespace.m4
deleted file mode 100644
index 989ad80696..0000000000
--- a/third_party/glog/m4/stl_namespace.m4
+++ /dev/null
@@ -1,25 +0,0 @@
-# We check what namespace stl code like vector expects to be executed in
-
-AC_DEFUN([AC_CXX_STL_NAMESPACE],
-  [AC_CACHE_CHECK(
-      what namespace STL code is in,
-      ac_cv_cxx_stl_namespace,
-      [AC_REQUIRE([AC_CXX_NAMESPACES])
-      AC_LANG_SAVE
-      AC_LANG_CPLUSPLUS
-      AC_TRY_COMPILE([#include <vector>],
-                     [vector<int> t; return 0;],
-                     ac_cv_cxx_stl_namespace=none)
-      AC_TRY_COMPILE([#include <vector>],
-                     [std::vector<int> t; return 0;],
-                     ac_cv_cxx_stl_namespace=std)
-      AC_LANG_RESTORE])
-   if test "$ac_cv_cxx_stl_namespace" = none; then
-      AC_DEFINE(STL_NAMESPACE,,
-                [the namespace where STL code like vector<> is defined])
-   fi
-   if test "$ac_cv_cxx_stl_namespace" = std; then
-      AC_DEFINE(STL_NAMESPACE,std,
-                [the namespace where STL code like vector<> is defined])
-   fi
-])
diff --git a/third_party/glog/m4/using_operator.m4 b/third_party/glog/m4/using_operator.m4
deleted file mode 100644
index 95a9951eb3..0000000000
--- a/third_party/glog/m4/using_operator.m4
+++ /dev/null
@@ -1,15 +0,0 @@
-AC_DEFUN([AC_CXX_USING_OPERATOR],
-  [AC_CACHE_CHECK(
-      whether compiler supports using ::operator<<,
-      ac_cv_cxx_using_operator,
-      [AC_LANG_SAVE
-       AC_LANG_CPLUSPLUS
-       AC_TRY_COMPILE([#include <iostream>
-                       std::ostream& operator<<(std::ostream&, struct s);],
-                      [using ::operator<<; return 0;],
-                      ac_cv_cxx_using_operator=1,
-		      ac_cv_cxx_using_operator=0)
-      AC_LANG_RESTORE])
-  if test "$ac_cv_cxx_using_operator" = 1; then
-    AC_DEFINE(HAVE_USING_OPERATOR, 1, [define if the compiler supports using expression for operator])
-  fi])
diff --git a/third_party/glog/packages/deb.sh b/third_party/glog/packages/deb.sh
deleted file mode 100755
index a1cdf321ef..0000000000
--- a/third_party/glog/packages/deb.sh
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/bash -e
-
-# This takes one commandline argument, the name of the package.  If no
-# name is given, then we'll end up just using the name associated with
-# an arbitrary .tar.gz file in the rootdir.  That's fine: there's probably
-# only one.
-#
-# Run this from the 'packages' directory, just under rootdir
-
-## Set LIB to lib if exporting a library, empty-string else
-LIB=
-#LIB=lib
-
-PACKAGE="$1"
-VERSION="$2"
-
-# We can only build Debian packages, if the Debian build tools are installed
-if [ \! -x /usr/bin/debuild ]; then
-  echo "Cannot find /usr/bin/debuild. Not building Debian packages." 1>&2
-  exit 0
-fi
-
-# Double-check we're in the packages directory, just under rootdir
-if [ \! -r ../Makefile -a \! -r ../INSTALL ]; then
-  echo "Must run $0 in the 'packages' directory, under the root directory." 1>&2
-  echo "Also, you must run \"make dist\" before running this script." 1>&2
-  exit 0
-fi
-
-# Find the top directory for this package
-topdir="${PWD%/*}"
-
-# Find the tar archive built by "make dist"
-archive="$PACKAGE-$VERSION"
-if [ -z "${archive}" ]; then
-  echo "Cannot find ../$PACKAGE*.tar.gz. Run \"make dist\" first." 1>&2
-  exit 0
-fi
-
-# Create a pristine directory for building the Debian package files
-trap 'rm -rf '`pwd`/tmp'; exit $?' EXIT SIGHUP SIGINT SIGTERM
-
-rm -rf tmp
-mkdir -p tmp
-cd tmp
-
-package="google-glog_$VERSION"
-
-# Debian has very specific requirements about the naming of build
-# directories, and tar archives. It also wants to write all generated
-# packages to the parent of the source directory. We accommodate these
-# requirements by building directly from the tar file.
-ln -s "${topdir}/${archive}.tar.gz" "${LIB}${package}.orig.tar.gz"
-tar zfx "${LIB}${package}.orig.tar.gz"
-mv "${archive}" "${LIB}${package}"
-cd "${LIB}${package}"
-# This is one of those 'specific requirements': where the deb control files live
-cp -a "packages/deb" "debian"
-
-# Now, we can call Debian's standard build tool
-debuild -uc -us
-cd ../..                            # get back to the original top-level dir
-
-# We'll put the result in a subdirectory that's named after the OS version
-# we've made this .deb file for.
-destdir="debian-$(cat /etc/debian_version 2>/dev/null || echo UNKNOWN)"
-
-rm -rf "$destdir"
-mkdir -p "$destdir"
-mv $(find tmp -mindepth 1 -maxdepth 1 -type f) "$destdir"
-
-echo
-echo "The Debian package files are located in $PWD/$destdir"
diff --git a/third_party/glog/packages/deb/README b/third_party/glog/packages/deb/README
deleted file mode 100644
index 57becfda75..0000000000
--- a/third_party/glog/packages/deb/README
+++ /dev/null
@@ -1,7 +0,0 @@
-The list of files here isn't complete.  For a step-by-step guide on
-how to set this package up correctly, check out
-    http://www.debian.org/doc/maint-guide/
-
-Most of the files that are in this directory are boilerplate.
-However, you may need to change the list of binary-arch dependencies
-in 'rules'.
diff --git a/third_party/glog/packages/deb/changelog b/third_party/glog/packages/deb/changelog
deleted file mode 100644
index 4cbed1bd0c..0000000000
--- a/third_party/glog/packages/deb/changelog
+++ /dev/null
@@ -1,71 +0,0 @@
-google-glog (0.4.0-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Tue, 22 Jan 2019 21:00:26 +0900
-
-google-glog (0.3.5-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Tue, 09 May 2017 16:22:12 +0900
-
-google-glog (0.3.4-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Tue, 10 Mar 2015 12:02:20 +0900
-
-google-glog (0.3.3-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Fri, 01 Feb 2012 14:54:14 +0900
-
-google-glog (0.3.2-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Thu, 12 Jan 2012 17:36:14 +0900
-
-google-glog (0.3.1-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Tue, 15 Jun 2010 13:50:47 +0900
-
-google-glog (0.3-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Thu, 30 Jul 2009 21:31:35 +0900
-
-google-glog (0.2.1-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Fri, 10 Apr 2009 15:24:17 +0900
-
-google-glog (0.2-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Fri, 23 Jan 2009 03:14:29 +0900
-
-google-glog (0.1.2-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Tue, 18 Nov 2008 20:37:00 +0900
-
-google-glog (0.1.1-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Google Inc. <opensource@google.com>  Wed, 15 Oct 2008 20:38:19 +0900
-
-google-glog (0.1-1) unstable; urgency=low
-
-  * Initial release.
-
- -- Google Inc. <opensource@google.com>  Sat, 10 May 2008 12:31:10 +0900
diff --git a/third_party/glog/packages/deb/compat b/third_party/glog/packages/deb/compat
deleted file mode 100644
index b8626c4cff..0000000000
--- a/third_party/glog/packages/deb/compat
+++ /dev/null
@@ -1 +0,0 @@
-4
diff --git a/third_party/glog/packages/deb/control b/third_party/glog/packages/deb/control
deleted file mode 100644
index 110a72cb35..0000000000
--- a/third_party/glog/packages/deb/control
+++ /dev/null
@@ -1,23 +0,0 @@
-Source: google-glog
-Priority: optional
-Maintainer: Google Inc. <opensource@google.com>
-Build-Depends: debhelper (>= 4.0.0), binutils
-Standards-Version: 3.6.1
-
-Package: libgoogle-glog-dev
-Section: libdevel
-Architecture: any
-Depends: libgoogle-glog0 (= ${Source-Version})
-Description:  a library that implements application-level logging.
- This library provides logging APIs based on C++-style streams and
- various helper macros.  The devel package contains static and debug
- libraries and header files for developing applications that use the
- google-glog package.
-
-Package: libgoogle-glog0
-Section: libs
-Architecture: any
-Depends: ${shlibs:Depends}
-Description:  a library that implements application-level logging.
- This library provides logging APIs based on C++-style streams and
- various helper macros.
diff --git a/third_party/glog/packages/deb/copyright b/third_party/glog/packages/deb/copyright
deleted file mode 100644
index b2ce8e8f02..0000000000
--- a/third_party/glog/packages/deb/copyright
+++ /dev/null
@@ -1,35 +0,0 @@
-This package was debianized by Google Inc. <opensource@google.com> on
-13 June 2008.
-
-It was downloaded from https://github.com/google/glog
-
-Upstream Author: opensource@google.com
-
-Copyright (c) 2008, Google 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:
-
-    * 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/third_party/glog/packages/deb/docs b/third_party/glog/packages/deb/docs
deleted file mode 100644
index 6da25d4d31..0000000000
--- a/third_party/glog/packages/deb/docs
+++ /dev/null
@@ -1,7 +0,0 @@
-AUTHORS
-COPYING
-ChangeLog
-INSTALL
-README.md
-doc/designstyle.css
-doc/glog.html
diff --git a/third_party/glog/packages/deb/libgoogle-glog-dev.dirs b/third_party/glog/packages/deb/libgoogle-glog-dev.dirs
deleted file mode 100644
index bddfbf5c11..0000000000
--- a/third_party/glog/packages/deb/libgoogle-glog-dev.dirs
+++ /dev/null
@@ -1,4 +0,0 @@
-usr/lib
-usr/lib/pkgconfig
-usr/include
-usr/include/glog
diff --git a/third_party/glog/packages/deb/libgoogle-glog-dev.install b/third_party/glog/packages/deb/libgoogle-glog-dev.install
deleted file mode 100644
index 9c61e86eaf..0000000000
--- a/third_party/glog/packages/deb/libgoogle-glog-dev.install
+++ /dev/null
@@ -1,10 +0,0 @@
-usr/include/glog/*
-usr/lib/lib*.so
-usr/lib/lib*.a
-usr/lib/*.la
-usr/lib/pkgconfig/*
-debian/tmp/usr/include/glog/*
-debian/tmp/usr/lib/lib*.so
-debian/tmp/usr/lib/lib*.a
-debian/tmp/usr/lib/*.la
-debian/tmp/usr/lib/pkgconfig/*
diff --git a/third_party/glog/packages/deb/libgoogle-glog0.dirs b/third_party/glog/packages/deb/libgoogle-glog0.dirs
deleted file mode 100644
index 68457717bd..0000000000
--- a/third_party/glog/packages/deb/libgoogle-glog0.dirs
+++ /dev/null
@@ -1 +0,0 @@
-usr/lib
diff --git a/third_party/glog/packages/deb/libgoogle-glog0.install b/third_party/glog/packages/deb/libgoogle-glog0.install
deleted file mode 100644
index 704ea87ab7..0000000000
--- a/third_party/glog/packages/deb/libgoogle-glog0.install
+++ /dev/null
@@ -1,2 +0,0 @@
-usr/lib/lib*.so.*
-debian/tmp/usr/lib/lib*.so.*
diff --git a/third_party/glog/packages/deb/rules b/third_party/glog/packages/deb/rules
deleted file mode 100755
index f520befd25..0000000000
--- a/third_party/glog/packages/deb/rules
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-
-# These are used for cross-compiling and for saving the configure script
-# from having to guess our platform (since we know it already)
-DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
-DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-
-
-CFLAGS = -Wall -g
-
-ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
-	CFLAGS += -O0
-else
-	CFLAGS += -O2
-endif
-ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
-	INSTALL_PROGRAM += -s
-endif
-
-# shared library versions, option 1
-#version=2.0.5
-#major=2
-# option 2, assuming the library is created as src/.libs/libfoo.so.2.0.5 or so
-version=`ls src/.libs/lib*.so.* | \
- awk '{if (match($$0,/[0-9]+\.[0-9]+\.[0-9]+$$/)) print substr($$0,RSTART)}'`
-major=`ls src/.libs/lib*.so.* | \
- awk '{if (match($$0,/\.so\.[0-9]+$$/)) print substr($$0,RSTART+4)}'`
-
-config.status: configure
-	dh_testdir
-	# Add here commands to configure the package.
-	CFLAGS="$(CFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info
-
-
-build: build-stamp
-build-stamp:  config.status
-	dh_testdir
-
-	# Add here commands to compile the package.
-	$(MAKE)
-
-	touch build-stamp
-
-clean:
-	dh_testdir
-	dh_testroot
-	rm -f build-stamp 
-
-	# Add here commands to clean up after the build process.
-	-$(MAKE) distclean
-ifneq "$(wildcard /usr/share/misc/config.sub)" ""
-	cp -f /usr/share/misc/config.sub config.sub
-endif
-ifneq "$(wildcard /usr/share/misc/config.guess)" ""
-	cp -f /usr/share/misc/config.guess config.guess
-endif
-
-
-	dh_clean 
-
-install: build
-	dh_testdir
-	dh_testroot
-	dh_clean -k 
-	dh_installdirs
-
-	# Add here commands to install the package into debian/tmp
-	$(MAKE) install DESTDIR=$(CURDIR)/debian/tmp
-
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-# Build architecture-dependent files here.
-binary-arch: build install
-	dh_testdir
-	dh_testroot
-	dh_installchangelogs ChangeLog
-	dh_installdocs
-	dh_installexamples
-	dh_install --sourcedir=debian/tmp
-#	dh_installmenu
-#	dh_installdebconf	
-#	dh_installlogrotate
-#	dh_installemacsen
-#	dh_installpam
-#	dh_installmime
-#	dh_installinit
-#	dh_installcron
-#	dh_installinfo
-	dh_installman
-	dh_link
-	dh_strip
-	dh_compress
-	dh_fixperms
-#	dh_perl
-#	dh_python
-	dh_makeshlibs
-	dh_installdeb
-	dh_shlibdeps
-	dh_gencontrol
-	dh_md5sums
-	dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install 
diff --git a/third_party/glog/packages/rpm.sh b/third_party/glog/packages/rpm.sh
deleted file mode 100755
index e5649a2258..0000000000
--- a/third_party/glog/packages/rpm.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/bin/sh -e
-
-# Run this from the 'packages' directory, just under rootdir
-
-# We can only build rpm packages, if the rpm build tools are installed
-if [ \! -x /usr/bin/rpmbuild ]
-then
-  echo "Cannot find /usr/bin/rpmbuild. Not building an rpm." 1>&2
-  exit 0
-fi
-
-# Check the commandline flags
-PACKAGE="$1"
-VERSION="$2"
-fullname="${PACKAGE}-${VERSION}"
-archive=../$fullname.tar.gz
-
-if [ -z "$1" -o -z "$2" ]
-then
-  echo "Usage: $0 <package name> <package version>" 1>&2
-  exit 0
-fi
-
-# Double-check we're in the packages directory, just under rootdir
-if [ \! -r ../Makefile -a \! -r ../INSTALL ]
-then
-  echo "Must run $0 in the 'packages' directory, under the root directory." 1>&2
-  echo "Also, you must run \"make dist\" before running this script." 1>&2
-  exit 0
-fi
-
-if [ \! -r "$archive" ]
-then
-  echo "Cannot find $archive. Run \"make dist\" first." 1>&2
-  exit 0
-fi
-
-# Create the directory where the input lives, and where the output should live
-RPM_SOURCE_DIR="/tmp/rpmsource-$fullname"
-RPM_BUILD_DIR="/tmp/rpmbuild-$fullname"
-
-trap 'rm -rf $RPM_SOURCE_DIR $RPM_BUILD_DIR; exit $?' EXIT SIGHUP SIGINT SIGTERM
-
-rm -rf "$RPM_SOURCE_DIR" "$RPM_BUILD_DIR"
-mkdir "$RPM_SOURCE_DIR"
-mkdir "$RPM_BUILD_DIR"
-
-cp "$archive" "$RPM_SOURCE_DIR"/v"$VERSION".tar.gz
-
-rpmbuild -bb rpm/rpm.spec \
-  --define "NAME $PACKAGE" \
-  --define "VERSION $VERSION" \
-  --define "_sourcedir $RPM_SOURCE_DIR" \
-  --define "_builddir $RPM_BUILD_DIR" \
-  --define "_rpmdir $RPM_SOURCE_DIR"
-
-# We put the output in a directory based on what system we've built for
-destdir=rpm-unknown
-if [ -r /etc/issue ]
-then
-   grep "Red Hat.*release 7" /etc/issue >/dev/null 2>&1 && destdir=rh7
-   grep "Red Hat.*release 8" /etc/issue >/dev/null 2>&1 && destdir=rh8
-   grep "Red Hat.*release 9" /etc/issue >/dev/null 2>&1 && destdir=rh9
-   if grep Fedora /etc/issue >/dev/null; then 
-	destdir=fc`grep Fedora /etc/issue | cut -d' ' -f 4`;
-   fi
-fi
-
-rm -rf "$destdir"
-mkdir -p "$destdir"
-# We want to get not only the main package but devel etc, hence the middle *
-mv "$RPM_SOURCE_DIR"/*/"${PACKAGE}"-*"${VERSION}"*.rpm "$destdir"
-
-echo
-echo "The rpm package file(s) are located in $PWD/$destdir"
diff --git a/third_party/glog/packages/rpm/rpm.spec b/third_party/glog/packages/rpm/rpm.spec
deleted file mode 100644
index 24ef134df5..0000000000
--- a/third_party/glog/packages/rpm/rpm.spec
+++ /dev/null
@@ -1,72 +0,0 @@
-%define	RELEASE	1
-%define rel     %{?CUSTOM_RELEASE} %{!?CUSTOM_RELEASE:%RELEASE}
-%define	prefix	/usr
-
-Name: %NAME
-Summary: A C++ application logging library
-Version: %VERSION
-Release: %rel
-Group: Development/Libraries
-URL: http://github.com/google/glog
-License: BSD
-Vendor: Google
-Packager: Google Inc. <opensource@google.com>
-Source: https://github.com/google/glog/archive/v%{VERSION}.tar.gz
-Distribution: Redhat 7 and above.
-Buildroot: %{_tmppath}/%{name}-root
-Prefix: %prefix
-
-%description
-The %name package contains a library that implements application-level
-logging.  This library provides logging APIs based on C++-style
-streams and various helper macros.
-
-%package devel
-Summary: A C++ application logging library
-Group: Development/Libraries
-Requires: %{NAME} = %{VERSION}
-
-%description devel
-The %name-devel package contains static and debug libraries and header
-files for developing applications that use the %name package.
-
-%changelog
-    * Wed Mar 26 2008 <opensource@google.com>
-    - First draft
-
-%prep
-%setup
-
-%build
-./configure
-make prefix=%prefix
-
-%install
-rm -rf $RPM_BUILD_ROOT
-make prefix=$RPM_BUILD_ROOT%{prefix} install
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%files
-%defattr(-,root,root)
-
-## Mark all installed files within /usr/share/doc/{package name} as
-## documentation.  This depends on the following two lines appearing in
-## Makefile.am:
-##     docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
-##     dist_doc_DATA = AUTHORS COPYING ChangeLog INSTALL README.md
-%docdir %{prefix}/share/doc/%{NAME}-%{VERSION}
-%{prefix}/share/doc/%{NAME}-%{VERSION}/*
-
-%{prefix}/lib/libglog.so.0
-%{prefix}/lib/libglog.so.0.0.0
-
-%files devel
-%defattr(-,root,root)
-
-%{prefix}/include/glog
-%{prefix}/lib/libglog.a
-%{prefix}/lib/libglog.la
-%{prefix}/lib/libglog.so
-%{prefix}/lib/pkgconfig/libglog.pc
diff --git a/third_party/glog/src/base/commandlineflags.h b/third_party/glog/src/base/commandlineflags.h
deleted file mode 100644
index c8d5089026..0000000000
--- a/third_party/glog/src/base/commandlineflags.h
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2008, Google 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:
-// 
-//     * 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.
-
-// ---
-// This file is a compatibility layer that defines Google's version of
-// command line flags that are used for configuration.
-//
-// We put flags into their own namespace.  It is purposefully
-// named in an opaque way that people should have trouble typing
-// directly.  The idea is that DEFINE puts the flag in the weird
-// namespace, and DECLARE imports the flag from there into the
-// current namespace.  The net result is to force people to use
-// DECLARE to get access to a flag, rather than saying
-//   extern bool FLAGS_logtostderr;
-// or some such instead.  We want this so we can put extra
-// functionality (like sanity-checking) in DECLARE if we want,
-// and make sure it is picked up everywhere.
-//
-// We also put the type of the variable in the namespace, so that
-// people can't DECLARE_int32 something that they DEFINE_bool'd
-// elsewhere.
-#ifndef BASE_COMMANDLINEFLAGS_H__
-#define BASE_COMMANDLINEFLAGS_H__
-
-#include "config.h"
-#include <string>
-#include <string.h>               // for memchr
-#include <stdlib.h>               // for getenv
-
-#ifdef HAVE_LIB_GFLAGS
-
-#include <gflags/gflags.h>
-
-#else
-
-#include "glog/logging.h"
-
-#define DECLARE_VARIABLE(type, shorttype, name, tn)                     \
-  namespace fL##shorttype {                                             \
-    extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name;                      \
-  }                                                                     \
-  using fL##shorttype::FLAGS_##name
-#define DEFINE_VARIABLE(type, shorttype, name, value, meaning, tn)      \
-  namespace fL##shorttype {                                             \
-    GOOGLE_GLOG_DLL_DECL type FLAGS_##name(value);                      \
-    char FLAGS_no##name;                                                \
-  }                                                                     \
-  using fL##shorttype::FLAGS_##name
-
-// bool specialization
-#define DECLARE_bool(name) \
-  DECLARE_VARIABLE(bool, B, name, bool)
-#define DEFINE_bool(name, value, meaning) \
-  DEFINE_VARIABLE(bool, B, name, value, meaning, bool)
-
-// int32 specialization
-#define DECLARE_int32(name) \
-  DECLARE_VARIABLE(GOOGLE_NAMESPACE::int32, I, name, int32)
-#define DEFINE_int32(name, value, meaning) \
-  DEFINE_VARIABLE(GOOGLE_NAMESPACE::int32, I, name, value, meaning, int32)
-
-// Special case for string, because we have to specify the namespace
-// std::string, which doesn't play nicely with our FLAG__namespace hackery.
-#define DECLARE_string(name)                                            \
-  namespace fLS {                                                       \
-    extern GOOGLE_GLOG_DLL_DECL std::string& FLAGS_##name;              \
-  }                                                                     \
-  using fLS::FLAGS_##name
-#define DEFINE_string(name, value, meaning)                             \
-  namespace fLS {                                                       \
-    std::string FLAGS_##name##_buf(value);                              \
-    GOOGLE_GLOG_DLL_DECL std::string& FLAGS_##name = FLAGS_##name##_buf; \
-    char FLAGS_no##name;                                                \
-  }                                                                     \
-  using fLS::FLAGS_##name
-
-#endif  // HAVE_LIB_GFLAGS
-
-// Define GLOG_DEFINE_* using DEFINE_* . By using these macros, we
-// have GLOG_* environ variables even if we have gflags installed.
-//
-// If both an environment variable and a flag are specified, the value
-// specified by a flag wins. E.g., if GLOG_v=0 and --v=1, the
-// verbosity will be 1, not 0.
-
-#define GLOG_DEFINE_bool(name, value, meaning) \
-  DEFINE_bool(name, EnvToBool("GLOG_" #name, value), meaning)
-
-#define GLOG_DEFINE_int32(name, value, meaning) \
-  DEFINE_int32(name, EnvToInt("GLOG_" #name, value), meaning)
-
-#define GLOG_DEFINE_string(name, value, meaning) \
-  DEFINE_string(name, EnvToString("GLOG_" #name, value), meaning)
-
-// These macros (could be functions, but I don't want to bother with a .cc
-// file), make it easier to initialize flags from the environment.
-
-#define EnvToString(envname, dflt)   \
-  (!getenv(envname) ? (dflt) : getenv(envname))
-
-#define EnvToBool(envname, dflt)   \
-  (!getenv(envname) ? (dflt) : memchr("tTyY1\0", getenv(envname)[0], 6) != NULL)
-
-#define EnvToInt(envname, dflt)  \
-  (!getenv(envname) ? (dflt) : strtol(getenv(envname), NULL, 10))
-
-#endif  // BASE_COMMANDLINEFLAGS_H__
diff --git a/third_party/glog/src/base/googleinit.h b/third_party/glog/src/base/googleinit.h
deleted file mode 100644
index 5a8b515cd6..0000000000
--- a/third_party/glog/src/base/googleinit.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2008, Google 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:
-// 
-//     * 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.
-
-// ---
-// Author: Jacob Hoffman-Andrews
-
-#ifndef _GOOGLEINIT_H
-#define _GOOGLEINIT_H
-
-class GoogleInitializer {
- public:
-  typedef void (*void_function)(void);
-  GoogleInitializer(const char*, void_function f) {
-    f();
-  }
-};
-
-#define REGISTER_MODULE_INITIALIZER(name, body)                 \
-  namespace {                                                   \
-    static void google_init_module_##name () { body; }          \
-    GoogleInitializer google_initializer_module_##name(#name,   \
-            google_init_module_##name);                         \
-  }
-
-#endif /* _GOOGLEINIT_H */
diff --git a/third_party/glog/src/base/mutex.h b/third_party/glog/src/base/mutex.h
deleted file mode 100644
index ced2b9950e..0000000000
--- a/third_party/glog/src/base/mutex.h
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright (c) 2007, Google 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:
-// 
-//     * 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.
-// 
-// ---
-// Author: Craig Silverstein.
-//
-// A simple mutex wrapper, supporting locks and read-write locks.
-// You should assume the locks are *not* re-entrant.
-//
-// To use: you should define the following macros in your configure.ac:
-//   ACX_PTHREAD
-//   AC_RWLOCK
-// The latter is defined in ../autoconf.
-//
-// This class is meant to be internal-only and should be wrapped by an
-// internal namespace.  Before you use this module, please give the
-// name of your internal namespace for this module.  Or, if you want
-// to expose it, you'll want to move it to the Google namespace.  We
-// cannot put this class in global namespace because there can be some
-// problems when we have multiple versions of Mutex in each shared object.
-//
-// NOTE: by default, we have #ifdef'ed out the TryLock() method.
-//       This is for two reasons:
-// 1) TryLock() under Windows is a bit annoying (it requires a
-//    #define to be defined very early).
-// 2) TryLock() is broken for NO_THREADS mode, at least in NDEBUG
-//    mode.
-// If you need TryLock(), and either these two caveats are not a
-// problem for you, or you're willing to work around them, then
-// feel free to #define GMUTEX_TRYLOCK, or to remove the #ifdefs
-// in the code below.
-//
-// CYGWIN NOTE: Cygwin support for rwlock seems to be buggy:
-//    http://www.cygwin.com/ml/cygwin/2008-12/msg00017.html
-// Because of that, we might as well use windows locks for
-// cygwin.  They seem to be more reliable than the cygwin pthreads layer.
-//
-// TRICKY IMPLEMENTATION NOTE:
-// This class is designed to be safe to use during
-// dynamic-initialization -- that is, by global constructors that are
-// run before main() starts.  The issue in this case is that
-// dynamic-initialization happens in an unpredictable order, and it
-// could be that someone else's dynamic initializer could call a
-// function that tries to acquire this mutex -- but that all happens
-// before this mutex's constructor has run.  (This can happen even if
-// the mutex and the function that uses the mutex are in the same .cc
-// file.)  Basically, because Mutex does non-trivial work in its
-// constructor, it's not, in the naive implementation, safe to use
-// before dynamic initialization has run on it.
-//
-// The solution used here is to pair the actual mutex primitive with a
-// bool that is set to true when the mutex is dynamically initialized.
-// (Before that it's false.)  Then we modify all mutex routines to
-// look at the bool, and not try to lock/unlock until the bool makes
-// it to true (which happens after the Mutex constructor has run.)
-//
-// This works because before main() starts -- particularly, during
-// dynamic initialization -- there are no threads, so a) it's ok that
-// the mutex operations are a no-op, since we don't need locking then
-// anyway; and b) we can be quite confident our bool won't change
-// state between a call to Lock() and a call to Unlock() (that would
-// require a global constructor in one translation unit to call Lock()
-// and another global constructor in another translation unit to call
-// Unlock() later, which is pretty perverse).
-//
-// That said, it's tricky, and can conceivably fail; it's safest to
-// avoid trying to acquire a mutex in a global constructor, if you
-// can.  One way it can fail is that a really smart compiler might
-// initialize the bool to true at static-initialization time (too
-// early) rather than at dynamic-initialization time.  To discourage
-// that, we set is_safe_ to true in code (not the constructor
-// colon-initializer) and set it to true via a function that always
-// evaluates to true, but that the compiler can't know always
-// evaluates to true.  This should be good enough.
-
-#ifndef GOOGLE_MUTEX_H_
-#define GOOGLE_MUTEX_H_
-
-#include "config.h"           // to figure out pthreads support
-
-#if defined(NO_THREADS)
-  typedef int MutexType;      // to keep a lock-count
-#elif defined(_WIN32) || defined(__CYGWIN32__) || defined(__CYGWIN64__)
-# ifndef WIN32_LEAN_AND_MEAN
-#  define WIN32_LEAN_AND_MEAN  // We only need minimal includes
-# endif
-# ifdef GMUTEX_TRYLOCK
-  // We need Windows NT or later for TryEnterCriticalSection().  If you
-  // don't need that functionality, you can remove these _WIN32_WINNT
-  // lines, and change TryLock() to assert(0) or something.
-#   ifndef _WIN32_WINNT
-#     define _WIN32_WINNT 0x0400
-#   endif
-# endif
-// To avoid macro definition of ERROR.
-# ifndef NOGDI
-#  define NOGDI
-# endif
-// To avoid macro definition of min/max.
-# ifndef NOMINMAX
-#  define NOMINMAX
-# endif
-# include <windows.h>
-  typedef CRITICAL_SECTION MutexType;
-#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
-  // Needed for pthread_rwlock_*.  If it causes problems, you could take it
-  // out, but then you'd have to unset HAVE_RWLOCK (at least on linux -- it
-  // *does* cause problems for FreeBSD, or MacOSX, but isn't needed
-  // for locking there.)
-# ifdef __linux__
-#   ifndef _XOPEN_SOURCE  // Some other header might have already set it for us.
-#     define _XOPEN_SOURCE 500  // may be needed to get the rwlock calls
-#   endif
-# endif
-# include <pthread.h>
-  typedef pthread_rwlock_t MutexType;
-#elif defined(HAVE_PTHREAD)
-# include <pthread.h>
-  typedef pthread_mutex_t MutexType;
-#else
-# error Need to implement mutex.h for your architecture, or #define NO_THREADS
-#endif
-
-// We need to include these header files after defining _XOPEN_SOURCE
-// as they may define the _XOPEN_SOURCE macro.
-#include <assert.h>
-#include <stdlib.h>      // for abort()
-
-#define MUTEX_NAMESPACE glog_internal_namespace_
-
-namespace MUTEX_NAMESPACE {
-
-class Mutex {
- public:
-  // Create a Mutex that is not held by anybody.  This constructor is
-  // typically used for Mutexes allocated on the heap or the stack.
-  // See below for a recommendation for constructing global Mutex
-  // objects.
-  inline Mutex();
-
-  // Destructor
-  inline ~Mutex();
-
-  inline void Lock();    // Block if needed until free then acquire exclusively
-  inline void Unlock();  // Release a lock acquired via Lock()
-#ifdef GMUTEX_TRYLOCK
-  inline bool TryLock(); // If free, Lock() and return true, else return false
-#endif
-  // Note that on systems that don't support read-write locks, these may
-  // be implemented as synonyms to Lock() and Unlock().  So you can use
-  // these for efficiency, but don't use them anyplace where being able
-  // to do shared reads is necessary to avoid deadlock.
-  inline void ReaderLock();   // Block until free or shared then acquire a share
-  inline void ReaderUnlock(); // Release a read share of this Mutex
-  inline void WriterLock() { Lock(); }     // Acquire an exclusive lock
-  inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock()
-
-  // TODO(hamaji): Do nothing, implement correctly.
-  inline void AssertHeld() {}
-
- private:
-  MutexType mutex_;
-  // We want to make sure that the compiler sets is_safe_ to true only
-  // when we tell it to, and never makes assumptions is_safe_ is
-  // always true.  volatile is the most reliable way to do that.
-  volatile bool is_safe_;
-
-  inline void SetIsSafe() { is_safe_ = true; }
-
-  // Catch the error of writing Mutex when intending MutexLock.
-  Mutex(Mutex* /*ignored*/) {}
-  // Disallow "evil" constructors
-  Mutex(const Mutex&);
-  void operator=(const Mutex&);
-};
-
-// Now the implementation of Mutex for various systems
-#if defined(NO_THREADS)
-
-// When we don't have threads, we can be either reading or writing,
-// but not both.  We can have lots of readers at once (in no-threads
-// mode, that's most likely to happen in recursive function calls),
-// but only one writer.  We represent this by having mutex_ be -1 when
-// writing and a number > 0 when reading (and 0 when no lock is held).
-//
-// In debug mode, we assert these invariants, while in non-debug mode
-// we do nothing, for efficiency.  That's why everything is in an
-// assert.
-
-Mutex::Mutex() : mutex_(0) { }
-Mutex::~Mutex()            { assert(mutex_ == 0); }
-void Mutex::Lock()         { assert(--mutex_ == -1); }
-void Mutex::Unlock()       { assert(mutex_++ == -1); }
-#ifdef GMUTEX_TRYLOCK
-bool Mutex::TryLock()      { if (mutex_) return false; Lock(); return true; }
-#endif
-void Mutex::ReaderLock()   { assert(++mutex_ > 0); }
-void Mutex::ReaderUnlock() { assert(mutex_-- > 0); }
-
-#elif defined(_WIN32) || defined(__CYGWIN32__) || defined(__CYGWIN64__)
-
-Mutex::Mutex()             { InitializeCriticalSection(&mutex_); SetIsSafe(); }
-Mutex::~Mutex()            { DeleteCriticalSection(&mutex_); }
-void Mutex::Lock()         { if (is_safe_) EnterCriticalSection(&mutex_); }
-void Mutex::Unlock()       { if (is_safe_) LeaveCriticalSection(&mutex_); }
-#ifdef GMUTEX_TRYLOCK
-bool Mutex::TryLock()      { return is_safe_ ?
-                                 TryEnterCriticalSection(&mutex_) != 0 : true; }
-#endif
-void Mutex::ReaderLock()   { Lock(); }      // we don't have read-write locks
-void Mutex::ReaderUnlock() { Unlock(); }
-
-#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
-
-#define SAFE_PTHREAD(fncall)  do {   /* run fncall if is_safe_ is true */  \
-  if (is_safe_ && fncall(&mutex_) != 0) abort();                           \
-} while (0)
-
-Mutex::Mutex() {
-  SetIsSafe();
-  if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort();
-}
-Mutex::~Mutex()            { SAFE_PTHREAD(pthread_rwlock_destroy); }
-void Mutex::Lock()         { SAFE_PTHREAD(pthread_rwlock_wrlock); }
-void Mutex::Unlock()       { SAFE_PTHREAD(pthread_rwlock_unlock); }
-#ifdef GMUTEX_TRYLOCK
-bool Mutex::TryLock()      { return is_safe_ ?
-                                    pthread_rwlock_trywrlock(&mutex_) == 0 :
-                                    true; }
-#endif
-void Mutex::ReaderLock()   { SAFE_PTHREAD(pthread_rwlock_rdlock); }
-void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock); }
-#undef SAFE_PTHREAD
-
-#elif defined(HAVE_PTHREAD)
-
-#define SAFE_PTHREAD(fncall)  do {   /* run fncall if is_safe_ is true */  \
-  if (is_safe_ && fncall(&mutex_) != 0) abort();                           \
-} while (0)
-
-Mutex::Mutex()             {
-  SetIsSafe();
-  if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
-}
-Mutex::~Mutex()            { SAFE_PTHREAD(pthread_mutex_destroy); }
-void Mutex::Lock()         { SAFE_PTHREAD(pthread_mutex_lock); }
-void Mutex::Unlock()       { SAFE_PTHREAD(pthread_mutex_unlock); }
-#ifdef GMUTEX_TRYLOCK
-bool Mutex::TryLock()      { return is_safe_ ?
-                                 pthread_mutex_trylock(&mutex_) == 0 : true; }
-#endif
-void Mutex::ReaderLock()   { Lock(); }
-void Mutex::ReaderUnlock() { Unlock(); }
-#undef SAFE_PTHREAD
-
-#endif
-
-// --------------------------------------------------------------------------
-// Some helper classes
-
-// MutexLock(mu) acquires mu when constructed and releases it when destroyed.
-class MutexLock {
- public:
-  explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); }
-  ~MutexLock() { mu_->Unlock(); }
- private:
-  Mutex * const mu_;
-  // Disallow "evil" constructors
-  MutexLock(const MutexLock&);
-  void operator=(const MutexLock&);
-};
-
-// ReaderMutexLock and WriterMutexLock do the same, for rwlocks
-class ReaderMutexLock {
- public:
-  explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); }
-  ~ReaderMutexLock() { mu_->ReaderUnlock(); }
- private:
-  Mutex * const mu_;
-  // Disallow "evil" constructors
-  ReaderMutexLock(const ReaderMutexLock&);
-  void operator=(const ReaderMutexLock&);
-};
-
-class WriterMutexLock {
- public:
-  explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); }
-  ~WriterMutexLock() { mu_->WriterUnlock(); }
- private:
-  Mutex * const mu_;
-  // Disallow "evil" constructors
-  WriterMutexLock(const WriterMutexLock&);
-  void operator=(const WriterMutexLock&);
-};
-
-// Catch bug where variable name is omitted, e.g. MutexLock (&mu);
-#define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_decl_missing_var_name)
-#define ReaderMutexLock(x) COMPILE_ASSERT(0, rmutex_lock_decl_missing_var_name)
-#define WriterMutexLock(x) COMPILE_ASSERT(0, wmutex_lock_decl_missing_var_name)
-
-}  // namespace MUTEX_NAMESPACE
-
-using namespace MUTEX_NAMESPACE;
-
-#undef MUTEX_NAMESPACE
-
-#endif  /* #define GOOGLE_MUTEX_H__ */
diff --git a/third_party/glog/src/config.h.cmake.in b/third_party/glog/src/config.h.cmake.in
deleted file mode 100644
index 20f099050e..0000000000
--- a/third_party/glog/src/config.h.cmake.in
+++ /dev/null
@@ -1,219 +0,0 @@
-#ifndef GLOG_CONFIG_H
-#define GLOG_CONFIG_H
-
-/* define if glog doesn't use RTTI */
-#cmakedefine DISABLE_RTTI
-
-/* Namespace for Google classes */
-#cmakedefine GOOGLE_NAMESPACE ${GOOGLE_NAMESPACE}
-
-/* Define if you have the `dladdr' function */
-#cmakedefine HAVE_DLADDR
-
-/* Define if you have the `snprintf' function */
-#cmakedefine HAVE_SNPRINTF
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#cmakedefine HAVE_DLFCN_H
-
-/* Define to 1 if you have the <execinfo.h> header file. */
-#cmakedefine HAVE_EXECINFO_H
-
-/* Define if you have the `fcntl' function */
-#cmakedefine HAVE_FCNTL
-
-/* Define to 1 if you have the <glob.h> header file. */
-#cmakedefine HAVE_GLOB_H
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#cmakedefine HAVE_INTTYPES_H ${HAVE_INTTYPES_H}
-
-/* Define to 1 if you have the `pthread' library (-lpthread). */
-#cmakedefine HAVE_LIBPTHREAD
-
-/* Define to 1 if you have the <libunwind.h> header file. */
-#cmakedefine HAVE_LIBUNWIND_H
-
-/* define if you have google gflags library */
-#cmakedefine HAVE_LIB_GFLAGS
-
-/* define if you have google gmock library */
-#cmakedefine HAVE_LIB_GMOCK
-
-/* define if you have google gtest library */
-#cmakedefine HAVE_LIB_GTEST
-
-/* define if you have libunwind */
-#cmakedefine HAVE_LIB_UNWIND
-
-/* Define to 1 if you have the <memory.h> header file. */
-#cmakedefine HAVE_MEMORY_H
-
-/* define to disable multithreading support. */
-#cmakedefine NO_THREADS
-
-/* define if the compiler implements namespaces */
-#cmakedefine HAVE_NAMESPACES
-
-/* Define if you have the 'pread' function */
-#cmakedefine HAVE_PREAD
-
-/* Define if you have POSIX threads libraries and header files. */
-#cmakedefine HAVE_PTHREAD
-
-/* Define to 1 if you have the <pwd.h> header file. */
-#cmakedefine HAVE_PWD_H
-
-/* Define if you have the 'pwrite' function */
-#cmakedefine HAVE_PWRITE
-
-/* define if the compiler implements pthread_rwlock_* */
-#cmakedefine HAVE_RWLOCK
-
-/* Define if you have the 'sigaction' function */
-#cmakedefine HAVE_SIGACTION
-
-/* Define if you have the `sigaltstack' function */
-#cmakedefine HAVE_SIGALTSTACK
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#cmakedefine HAVE_STDINT_H ${HAVE_STDINT_H}
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#cmakedefine HAVE_STDLIB_H
-
-/* Define to 1 if you have the <strings.h> header file. */
-#cmakedefine HAVE_STRINGS_H
-
-/* Define to 1 if you have the <string.h> header file. */
-#cmakedefine HAVE_STRING_H
-
-/* Define to 1 if you have the <syscall.h> header file. */
-#cmakedefine HAVE_SYSCALL_H
-
-/* Define to 1 if you have the <syslog.h> header file. */
-#cmakedefine HAVE_SYSLOG_H
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#cmakedefine HAVE_SYS_STAT_H
-
-/* Define to 1 if you have the <sys/syscall.h> header file. */
-#cmakedefine HAVE_SYS_SYSCALL_H
-
-/* Define to 1 if you have the <sys/time.h> header file. */
-#cmakedefine HAVE_SYS_TIME_H
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#cmakedefine HAVE_SYS_TYPES_H ${HAVE_SYS_TYPES_H}
-
-/* Define to 1 if you have the <sys/ucontext.h> header file. */
-#cmakedefine HAVE_SYS_UCONTEXT_H
-
-/* Define to 1 if you have the <sys/utsname.h> header file. */
-#cmakedefine HAVE_SYS_UTSNAME_H
-
-/* Define to 1 if you have the <sys/wait.h> header file. */
-#cmakedefine HAVE_SYS_WAIT_H
-
-/* Define to 1 if you have the <ucontext.h> header file. */
-#cmakedefine HAVE_UCONTEXT_H
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H}
-
-/* Define to 1 if you have the <unwind.h> header file. */
-#cmakedefine HAVE_UNWIND_H ${HAVE_UNWIND_H}
-
-/* define if the compiler supports using expression for operator */
-#cmakedefine HAVE_USING_OPERATOR
-
-/* define if your compiler has __attribute__ */
-#cmakedefine HAVE___ATTRIBUTE__
-
-/* define if your compiler has __builtin_expect */
-#cmakedefine HAVE___BUILTIN_EXPECT ${HAVE___BUILTIN_EXPECT}
-
-/* define if your compiler has __sync_val_compare_and_swap */
-#cmakedefine HAVE___SYNC_VAL_COMPARE_AND_SWAP
-
-/* define if symbolize support is available */
-#cmakedefine HAVE_SYMBOLIZE
-
-/* define if localtime_r is available in time.h */
-#cmakedefine HAVE_LOCALTIME_R
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
-   */
-#cmakedefine LT_OBJDIR
-
-/* Name of package */
-#cmakedefine PACKAGE
-
-/* Define to the address where bug reports for this package should be sent. */
-#cmakedefine PACKAGE_BUGREPORT
-
-/* Define to the full name of this package. */
-#cmakedefine PACKAGE_NAME
-
-/* Define to the full name and version of this package. */
-#cmakedefine PACKAGE_STRING
-
-/* Define to the one symbol short name of this package. */
-#cmakedefine PACKAGE_TARNAME
-
-/* Define to the home page for this package. */
-#cmakedefine PACKAGE_URL
-
-/* Define to the version of this package. */
-#cmakedefine PACKAGE_VERSION
-
-/* How to access the PC from a struct ucontext */
-#cmakedefine PC_FROM_UCONTEXT
-
-/* define if we should print file offsets in traces instead of symbolizing. */
-#cmakedefine PRINT_UNSYMBOLIZED_STACK_TRACES
-
-/* Define to necessary symbol if this constant uses a non-standard name on
-   your system. */
-#cmakedefine PTHREAD_CREATE_JOINABLE
-
-/* The size of `void *', as computed by sizeof. */
-#cmakedefine SIZEOF_VOID_P ${SIZEOF_VOID_P}
-
-/* Define to 1 if you have the ANSI C header files. */
-#cmakedefine STDC_HEADERS
-
-/* the namespace where STL code like vector<> is defined */
-#cmakedefine STL_NAMESPACE ${STL_NAMESPACE}
-
-/* location of source code */
-#cmakedefine TEST_SRC_DIR ${TEST_SRC_DIR}
-
-/* Define to necessary thread-local storage attribute. */
-#cmakedefine GLOG_THREAD_LOCAL_STORAGE ${GLOG_THREAD_LOCAL_STORAGE}
-
-/* Check whether aligned_storage and alignof present */
-#cmakedefine HAVE_ALIGNED_STORAGE ${HAVE_ALIGNED_STORAGE}
-
-/* Version number of package */
-#cmakedefine VERSION
-
-#ifdef GLOG_BAZEL_BUILD
-
-/* TODO(rodrigoq): remove this workaround once bazel#3979 is resolved:
- * https://github.com/bazelbuild/bazel/issues/3979 */
-#define _START_GOOGLE_NAMESPACE_ namespace GOOGLE_NAMESPACE {
-
-#define _END_GOOGLE_NAMESPACE_ }
-
-#else
-
-/* Stops putting the code inside the Google namespace */
-#cmakedefine _END_GOOGLE_NAMESPACE_ ${_END_GOOGLE_NAMESPACE_}
-
-/* Puts following code inside the Google namespace */
-#cmakedefine _START_GOOGLE_NAMESPACE_ ${_START_GOOGLE_NAMESPACE_}
-
-#endif
-
-#endif  // GLOG_CONFIG_H
diff --git a/third_party/glog/src/config_for_unittests.h b/third_party/glog/src/config_for_unittests.h
deleted file mode 100644
index 13ea8eab7a..0000000000
--- a/third_party/glog/src/config_for_unittests.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2008, Google 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:
-//
-//     * 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.
-
-// ---
-// All Rights Reserved.
-//
-// Author: Craig Silverstein
-// Copied from google-perftools and modified by Shinichiro Hamaji
-//
-// This file is needed for windows -- unittests are not part of the
-// glog dll, but still want to include config.h just like the
-// dll does, so they can use internal tools and APIs for testing.
-//
-// The problem is that config.h declares GOOGLE_GLOG_DLL_DECL to be
-// for exporting symbols, but the unittest needs to *import* symbols
-// (since it's not the dll).
-//
-// The solution is to have this file, which is just like config.h but
-// sets GOOGLE_GLOG_DLL_DECL to do a dllimport instead of a dllexport.
-//
-// The reason we need this extra GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS
-// variable is in case people want to set GOOGLE_GLOG_DLL_DECL explicitly
-// to something other than __declspec(dllexport).  In that case, they
-// may want to use something other than __declspec(dllimport) for the
-// unittest case.  For that, we allow folks to define both
-// GOOGLE_GLOG_DLL_DECL and GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS explicitly.
-//
-// NOTE: This file is equivalent to config.h on non-windows systems,
-// which never defined GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS and always
-// define GOOGLE_GLOG_DLL_DECL to the empty string.
-
-#include "config.h"
-
-#undef GOOGLE_GLOG_DLL_DECL
-#ifdef GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS
-# define GOOGLE_GLOG_DLL_DECL  GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS
-#else
-// if DLL_DECL_FOR_UNITTESTS isn't defined, use ""
-# define GOOGLE_GLOG_DLL_DECL
-#endif
diff --git a/third_party/glog/src/demangle.cc b/third_party/glog/src/demangle.cc
deleted file mode 100644
index 9369f9a65c..0000000000
--- a/third_party/glog/src/demangle.cc
+++ /dev/null
@@ -1,1356 +0,0 @@
-// Copyright (c) 2006, Google 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:
-//
-//     * 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.
-//
-// Author: Satoru Takabayashi
-//
-// For reference check out:
-// http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
-//
-// Note that we only have partial C++0x support yet.
-
-#include <stdio.h>  // for NULL
-#include "utilities.h"
-#include "demangle.h"
-
-#if defined(OS_WINDOWS)
-#include <dbghelp.h>
-#pragma comment(lib, "dbghelp")
-#endif
-
-_START_GOOGLE_NAMESPACE_
-
-#if !defined(OS_WINDOWS)
-typedef struct {
-  const char *abbrev;
-  const char *real_name;
-} AbbrevPair;
-
-// List of operators from Itanium C++ ABI.
-static const AbbrevPair kOperatorList[] = {
-  { "nw", "new" },
-  { "na", "new[]" },
-  { "dl", "delete" },
-  { "da", "delete[]" },
-  { "ps", "+" },
-  { "ng", "-" },
-  { "ad", "&" },
-  { "de", "*" },
-  { "co", "~" },
-  { "pl", "+" },
-  { "mi", "-" },
-  { "ml", "*" },
-  { "dv", "/" },
-  { "rm", "%" },
-  { "an", "&" },
-  { "or", "|" },
-  { "eo", "^" },
-  { "aS", "=" },
-  { "pL", "+=" },
-  { "mI", "-=" },
-  { "mL", "*=" },
-  { "dV", "/=" },
-  { "rM", "%=" },
-  { "aN", "&=" },
-  { "oR", "|=" },
-  { "eO", "^=" },
-  { "ls", "<<" },
-  { "rs", ">>" },
-  { "lS", "<<=" },
-  { "rS", ">>=" },
-  { "eq", "==" },
-  { "ne", "!=" },
-  { "lt", "<" },
-  { "gt", ">" },
-  { "le", "<=" },
-  { "ge", ">=" },
-  { "nt", "!" },
-  { "aa", "&&" },
-  { "oo", "||" },
-  { "pp", "++" },
-  { "mm", "--" },
-  { "cm", "," },
-  { "pm", "->*" },
-  { "pt", "->" },
-  { "cl", "()" },
-  { "ix", "[]" },
-  { "qu", "?" },
-  { "st", "sizeof" },
-  { "sz", "sizeof" },
-  { NULL, NULL },
-};
-
-// List of builtin types from Itanium C++ ABI.
-static const AbbrevPair kBuiltinTypeList[] = {
-  { "v", "void" },
-  { "w", "wchar_t" },
-  { "b", "bool" },
-  { "c", "char" },
-  { "a", "signed char" },
-  { "h", "unsigned char" },
-  { "s", "short" },
-  { "t", "unsigned short" },
-  { "i", "int" },
-  { "j", "unsigned int" },
-  { "l", "long" },
-  { "m", "unsigned long" },
-  { "x", "long long" },
-  { "y", "unsigned long long" },
-  { "n", "__int128" },
-  { "o", "unsigned __int128" },
-  { "f", "float" },
-  { "d", "double" },
-  { "e", "long double" },
-  { "g", "__float128" },
-  { "z", "ellipsis" },
-  { NULL, NULL }
-};
-
-// List of substitutions Itanium C++ ABI.
-static const AbbrevPair kSubstitutionList[] = {
-  { "St", "" },
-  { "Sa", "allocator" },
-  { "Sb", "basic_string" },
-  // std::basic_string<char, std::char_traits<char>,std::allocator<char> >
-  { "Ss", "string"},
-  // std::basic_istream<char, std::char_traits<char> >
-  { "Si", "istream" },
-  // std::basic_ostream<char, std::char_traits<char> >
-  { "So", "ostream" },
-  // std::basic_iostream<char, std::char_traits<char> >
-  { "Sd", "iostream" },
-  { NULL, NULL }
-};
-
-// State needed for demangling.
-typedef struct {
-  const char *mangled_cur;  // Cursor of mangled name.
-  char *out_cur;            // Cursor of output string.
-  const char *out_begin;    // Beginning of output string.
-  const char *out_end;      // End of output string.
-  const char *prev_name;    // For constructors/destructors.
-  int prev_name_length;     // For constructors/destructors.
-  short nest_level;         // For nested names.
-  bool append;              // Append flag.
-  bool overflowed;          // True if output gets overflowed.
-} State;
-
-// We don't use strlen() in libc since it's not guaranteed to be async
-// signal safe.
-static size_t StrLen(const char *str) {
-  size_t len = 0;
-  while (*str != '\0') {
-    ++str;
-    ++len;
-  }
-  return len;
-}
-
-// Returns true if "str" has at least "n" characters remaining.
-static bool AtLeastNumCharsRemaining(const char *str, int n) {
-  for (int i = 0; i < n; ++i) {
-    if (str[i] == '\0') {
-      return false;
-    }
-  }
-  return true;
-}
-
-// Returns true if "str" has "prefix" as a prefix.
-static bool StrPrefix(const char *str, const char *prefix) {
-  size_t i = 0;
-  while (str[i] != '\0' && prefix[i] != '\0' &&
-         str[i] == prefix[i]) {
-    ++i;
-  }
-  return prefix[i] == '\0';  // Consumed everything in "prefix".
-}
-
-static void InitState(State *state, const char *mangled,
-                      char *out, int out_size) {
-  state->mangled_cur = mangled;
-  state->out_cur = out;
-  state->out_begin = out;
-  state->out_end = out + out_size;
-  state->prev_name  = NULL;
-  state->prev_name_length = -1;
-  state->nest_level = -1;
-  state->append = true;
-  state->overflowed = false;
-}
-
-// Returns true and advances "mangled_cur" if we find "one_char_token"
-// at "mangled_cur" position.  It is assumed that "one_char_token" does
-// not contain '\0'.
-static bool ParseOneCharToken(State *state, const char one_char_token) {
-  if (state->mangled_cur[0] == one_char_token) {
-    ++state->mangled_cur;
-    return true;
-  }
-  return false;
-}
-
-// Returns true and advances "mangled_cur" if we find "two_char_token"
-// at "mangled_cur" position.  It is assumed that "two_char_token" does
-// not contain '\0'.
-static bool ParseTwoCharToken(State *state, const char *two_char_token) {
-  if (state->mangled_cur[0] == two_char_token[0] &&
-      state->mangled_cur[1] == two_char_token[1]) {
-    state->mangled_cur += 2;
-    return true;
-  }
-  return false;
-}
-
-// Returns true and advances "mangled_cur" if we find any character in
-// "char_class" at "mangled_cur" position.
-static bool ParseCharClass(State *state, const char *char_class) {
-  const char *p = char_class;
-  for (; *p != '\0'; ++p) {
-    if (state->mangled_cur[0] == *p) {
-      ++state->mangled_cur;
-      return true;
-    }
-  }
-  return false;
-}
-
-// This function is used for handling an optional non-terminal.
-static bool Optional(bool) {
-  return true;
-}
-
-// This function is used for handling <non-terminal>+ syntax.
-typedef bool (*ParseFunc)(State *);
-static bool OneOrMore(ParseFunc parse_func, State *state) {
-  if (parse_func(state)) {
-    while (parse_func(state)) {
-    }
-    return true;
-  }
-  return false;
-}
-
-// This function is used for handling <non-terminal>* syntax. The function
-// always returns true and must be followed by a termination token or a
-// terminating sequence not handled by parse_func (e.g.
-// ParseOneCharToken(state, 'E')).
-static bool ZeroOrMore(ParseFunc parse_func, State *state) {
-  while (parse_func(state)) {
-  }
-  return true;
-}
-
-// Append "str" at "out_cur".  If there is an overflow, "overflowed"
-// is set to true for later use.  The output string is ensured to
-// always terminate with '\0' as long as there is no overflow.
-static void Append(State *state, const char * const str, const int length) {
-  int i;
-  for (i = 0; i < length; ++i) {
-    if (state->out_cur + 1 < state->out_end) {  // +1 for '\0'
-      *state->out_cur = str[i];
-      ++state->out_cur;
-    } else {
-      state->overflowed = true;
-      break;
-    }
-  }
-  if (!state->overflowed) {
-    *state->out_cur = '\0';  // Terminate it with '\0'
-  }
-}
-
-// We don't use equivalents in libc to avoid locale issues.
-static bool IsLower(char c) {
-  return c >= 'a' && c <= 'z';
-}
-
-static bool IsAlpha(char c) {
-  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
-}
-
-static bool IsDigit(char c) {
-  return c >= '0' && c <= '9';
-}
-
-// Returns true if "str" is a function clone suffix.  These suffixes are used
-// by GCC 4.5.x and later versions to indicate functions which have been
-// cloned during optimization.  We treat any sequence (.<alpha>+.<digit>+)+ as
-// a function clone suffix.
-static bool IsFunctionCloneSuffix(const char *str) {
-  size_t i = 0;
-  while (str[i] != '\0') {
-    // Consume a single .<alpha>+.<digit>+ sequence.
-    if (str[i] != '.' || !IsAlpha(str[i + 1])) {
-      return false;
-    }
-    i += 2;
-    while (IsAlpha(str[i])) {
-      ++i;
-    }
-    if (str[i] != '.' || !IsDigit(str[i + 1])) {
-      return false;
-    }
-    i += 2;
-    while (IsDigit(str[i])) {
-      ++i;
-    }
-  }
-  return true;  // Consumed everything in "str".
-}
-
-// Append "str" with some tweaks, iff "append" state is true.
-// Returns true so that it can be placed in "if" conditions.
-static void MaybeAppendWithLength(State *state, const char * const str,
-                                  const int length) {
-  if (state->append && length > 0) {
-    // Append a space if the output buffer ends with '<' and "str"
-    // starts with '<' to avoid <<<.
-    if (str[0] == '<' && state->out_begin < state->out_cur  &&
-        state->out_cur[-1] == '<') {
-      Append(state, " ", 1);
-    }
-    // Remember the last identifier name for ctors/dtors.
-    if (IsAlpha(str[0]) || str[0] == '_') {
-      state->prev_name = state->out_cur;
-      state->prev_name_length = length;
-    }
-    Append(state, str, length);
-  }
-}
-
-// A convenient wrapper arount MaybeAppendWithLength().
-static bool MaybeAppend(State *state, const char * const str) {
-  if (state->append) {
-    int length = StrLen(str);
-    MaybeAppendWithLength(state, str, length);
-  }
-  return true;
-}
-
-// This function is used for handling nested names.
-static bool EnterNestedName(State *state) {
-  state->nest_level = 0;
-  return true;
-}
-
-// This function is used for handling nested names.
-static bool LeaveNestedName(State *state, short prev_value) {
-  state->nest_level = prev_value;
-  return true;
-}
-
-// Disable the append mode not to print function parameters, etc.
-static bool DisableAppend(State *state) {
-  state->append = false;
-  return true;
-}
-
-// Restore the append mode to the previous state.
-static bool RestoreAppend(State *state, bool prev_value) {
-  state->append = prev_value;
-  return true;
-}
-
-// Increase the nest level for nested names.
-static void MaybeIncreaseNestLevel(State *state) {
-  if (state->nest_level > -1) {
-    ++state->nest_level;
-  }
-}
-
-// Appends :: for nested names if necessary.
-static void MaybeAppendSeparator(State *state) {
-  if (state->nest_level >= 1) {
-    MaybeAppend(state, "::");
-  }
-}
-
-// Cancel the last separator if necessary.
-static void MaybeCancelLastSeparator(State *state) {
-  if (state->nest_level >= 1 && state->append &&
-      state->out_begin <= state->out_cur - 2) {
-    state->out_cur -= 2;
-    *state->out_cur = '\0';
-  }
-}
-
-// Returns true if the identifier of the given length pointed to by
-// "mangled_cur" is anonymous namespace.
-static bool IdentifierIsAnonymousNamespace(State *state, int length) {
-  static const char anon_prefix[] = "_GLOBAL__N_";
-  return (length > (int)sizeof(anon_prefix) - 1 &&  // Should be longer.
-          StrPrefix(state->mangled_cur, anon_prefix));
-}
-
-// Forward declarations of our parsing functions.
-static bool ParseMangledName(State *state);
-static bool ParseEncoding(State *state);
-static bool ParseName(State *state);
-static bool ParseUnscopedName(State *state);
-static bool ParseUnscopedTemplateName(State *state);
-static bool ParseNestedName(State *state);
-static bool ParsePrefix(State *state);
-static bool ParseUnqualifiedName(State *state);
-static bool ParseSourceName(State *state);
-static bool ParseLocalSourceName(State *state);
-static bool ParseNumber(State *state, int *number_out);
-static bool ParseFloatNumber(State *state);
-static bool ParseSeqId(State *state);
-static bool ParseIdentifier(State *state, int length);
-static bool ParseAbiTags(State *state);
-static bool ParseAbiTag(State *state);
-static bool ParseOperatorName(State *state);
-static bool ParseSpecialName(State *state);
-static bool ParseCallOffset(State *state);
-static bool ParseNVOffset(State *state);
-static bool ParseVOffset(State *state);
-static bool ParseCtorDtorName(State *state);
-static bool ParseType(State *state);
-static bool ParseCVQualifiers(State *state);
-static bool ParseBuiltinType(State *state);
-static bool ParseFunctionType(State *state);
-static bool ParseBareFunctionType(State *state);
-static bool ParseClassEnumType(State *state);
-static bool ParseArrayType(State *state);
-static bool ParsePointerToMemberType(State *state);
-static bool ParseTemplateParam(State *state);
-static bool ParseTemplateTemplateParam(State *state);
-static bool ParseTemplateArgs(State *state);
-static bool ParseTemplateArg(State *state);
-static bool ParseExpression(State *state);
-static bool ParseExprPrimary(State *state);
-static bool ParseLocalName(State *state);
-static bool ParseDiscriminator(State *state);
-static bool ParseSubstitution(State *state);
-
-// Implementation note: the following code is a straightforward
-// translation of the Itanium C++ ABI defined in BNF with a couple of
-// exceptions.
-//
-// - Support GNU extensions not defined in the Itanium C++ ABI
-// - <prefix> and <template-prefix> are combined to avoid infinite loop
-// - Reorder patterns to shorten the code
-// - Reorder patterns to give greedier functions precedence
-//   We'll mark "Less greedy than" for these cases in the code
-//
-// Each parsing function changes the state and returns true on
-// success.  Otherwise, don't change the state and returns false.  To
-// ensure that the state isn't changed in the latter case, we save the
-// original state before we call more than one parsing functions
-// consecutively with &&, and restore the state if unsuccessful.  See
-// ParseEncoding() as an example of this convention.  We follow the
-// convention throughout the code.
-//
-// Originally we tried to do demangling without following the full ABI
-// syntax but it turned out we needed to follow the full syntax to
-// parse complicated cases like nested template arguments.  Note that
-// implementing a full-fledged demangler isn't trivial (libiberty's
-// cp-demangle.c has +4300 lines).
-//
-// Note that (foo) in <(foo) ...> is a modifier to be ignored.
-//
-// Reference:
-// - Itanium C++ ABI
-//   <http://www.codesourcery.com/cxx-abi/abi.html#mangling>
-
-// <mangled-name> ::= _Z <encoding>
-static bool ParseMangledName(State *state) {
-  return ParseTwoCharToken(state, "_Z") && ParseEncoding(state);
-}
-
-// <encoding> ::= <(function) name> <bare-function-type>
-//            ::= <(data) name>
-//            ::= <special-name>
-static bool ParseEncoding(State *state) {
-  State copy = *state;
-  if (ParseName(state) && ParseBareFunctionType(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseName(state) || ParseSpecialName(state)) {
-    return true;
-  }
-  return false;
-}
-
-// <name> ::= <nested-name>
-//        ::= <unscoped-template-name> <template-args>
-//        ::= <unscoped-name>
-//        ::= <local-name>
-static bool ParseName(State *state) {
-  if (ParseNestedName(state) || ParseLocalName(state)) {
-    return true;
-  }
-
-  State copy = *state;
-  if (ParseUnscopedTemplateName(state) &&
-      ParseTemplateArgs(state)) {
-    return true;
-  }
-  *state = copy;
-
-  // Less greedy than <unscoped-template-name> <template-args>.
-  if (ParseUnscopedName(state)) {
-    return true;
-  }
-  return false;
-}
-
-// <unscoped-name> ::= <unqualified-name>
-//                 ::= St <unqualified-name>
-static bool ParseUnscopedName(State *state) {
-  if (ParseUnqualifiedName(state)) {
-    return true;
-  }
-
-  State copy = *state;
-  if (ParseTwoCharToken(state, "St") &&
-      MaybeAppend(state, "std::") &&
-      ParseUnqualifiedName(state)) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <unscoped-template-name> ::= <unscoped-name>
-//                          ::= <substitution>
-static bool ParseUnscopedTemplateName(State *state) {
-  return ParseUnscopedName(state) || ParseSubstitution(state);
-}
-
-// <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
-//               ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
-static bool ParseNestedName(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'N') &&
-      EnterNestedName(state) &&
-      Optional(ParseCVQualifiers(state)) &&
-      ParsePrefix(state) &&
-      LeaveNestedName(state, copy.nest_level) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// This part is tricky.  If we literally translate them to code, we'll
-// end up infinite loop.  Hence we merge them to avoid the case.
-//
-// <prefix> ::= <prefix> <unqualified-name>
-//          ::= <template-prefix> <template-args>
-//          ::= <template-param>
-//          ::= <substitution>
-//          ::= # empty
-// <template-prefix> ::= <prefix> <(template) unqualified-name>
-//                   ::= <template-param>
-//                   ::= <substitution>
-static bool ParsePrefix(State *state) {
-  bool has_something = false;
-  while (true) {
-    MaybeAppendSeparator(state);
-    if (ParseTemplateParam(state) ||
-        ParseSubstitution(state) ||
-        ParseUnscopedName(state)) {
-      has_something = true;
-      MaybeIncreaseNestLevel(state);
-      continue;
-    }
-    MaybeCancelLastSeparator(state);
-    if (has_something && ParseTemplateArgs(state)) {
-      return ParsePrefix(state);
-    } else {
-      break;
-    }
-  }
-  return true;
-}
-
-// <unqualified-name> ::= <operator-name>
-//                    ::= <ctor-dtor-name>
-//                    ::= <source-name> [<abi-tags>]
-//                    ::= <local-source-name> [<abi-tags>]
-static bool ParseUnqualifiedName(State *state) {
-  return (ParseOperatorName(state) ||
-          ParseCtorDtorName(state) ||
-          (ParseSourceName(state) && Optional(ParseAbiTags(state))) ||
-          (ParseLocalSourceName(state) && Optional(ParseAbiTags(state))));
-}
-
-// <source-name> ::= <positive length number> <identifier>
-static bool ParseSourceName(State *state) {
-  State copy = *state;
-  int length = -1;
-  if (ParseNumber(state, &length) && ParseIdentifier(state, length)) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <local-source-name> ::= L <source-name> [<discriminator>]
-//
-// References:
-//   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775
-//   http://gcc.gnu.org/viewcvs?view=rev&revision=124467
-static bool ParseLocalSourceName(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'L') && ParseSourceName(state) &&
-      Optional(ParseDiscriminator(state))) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <number> ::= [n] <non-negative decimal integer>
-// If "number_out" is non-null, then *number_out is set to the value of the
-// parsed number on success.
-static bool ParseNumber(State *state, int *number_out) {
-  int sign = 1;
-  if (ParseOneCharToken(state, 'n')) {
-    sign = -1;
-  }
-  const char *p = state->mangled_cur;
-  int number = 0;
-  for (;*p != '\0'; ++p) {
-    if (IsDigit(*p)) {
-      number = number * 10 + (*p - '0');
-    } else {
-      break;
-    }
-  }
-  if (p != state->mangled_cur) {  // Conversion succeeded.
-    state->mangled_cur = p;
-    if (number_out != NULL) {
-      *number_out = number * sign;
-    }
-    return true;
-  }
-  return false;
-}
-
-// Floating-point literals are encoded using a fixed-length lowercase
-// hexadecimal string.
-static bool ParseFloatNumber(State *state) {
-  const char *p = state->mangled_cur;
-  for (;*p != '\0'; ++p) {
-    if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) {
-      break;
-    }
-  }
-  if (p != state->mangled_cur) {  // Conversion succeeded.
-    state->mangled_cur = p;
-    return true;
-  }
-  return false;
-}
-
-// The <seq-id> is a sequence number in base 36,
-// using digits and upper case letters
-static bool ParseSeqId(State *state) {
-  const char *p = state->mangled_cur;
-  for (;*p != '\0'; ++p) {
-    if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) {
-      break;
-    }
-  }
-  if (p != state->mangled_cur) {  // Conversion succeeded.
-    state->mangled_cur = p;
-    return true;
-  }
-  return false;
-}
-
-// <identifier> ::= <unqualified source code identifier> (of given length)
-static bool ParseIdentifier(State *state, int length) {
-  if (length == -1 ||
-      !AtLeastNumCharsRemaining(state->mangled_cur, length)) {
-    return false;
-  }
-  if (IdentifierIsAnonymousNamespace(state, length)) {
-    MaybeAppend(state, "(anonymous namespace)");
-  } else {
-    MaybeAppendWithLength(state, state->mangled_cur, length);
-  }
-  state->mangled_cur += length;
-  return true;
-}
-
-// <abi-tags> ::= <abi-tag> [<abi-tags>]
-static bool ParseAbiTags(State *state) {
-  State copy = *state;
-  DisableAppend(state);
-  if (OneOrMore(ParseAbiTag, state)) {
-    RestoreAppend(state, copy.append);
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <abi-tag> ::= B <source-name>
-static bool ParseAbiTag(State *state) {
-  return ParseOneCharToken(state, 'B') && ParseSourceName(state);
-}
-
-// <operator-name> ::= nw, and other two letters cases
-//                 ::= cv <type>  # (cast)
-//                 ::= v  <digit> <source-name> # vendor extended operator
-static bool ParseOperatorName(State *state) {
-  if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) {
-    return false;
-  }
-  // First check with "cv" (cast) case.
-  State copy = *state;
-  if (ParseTwoCharToken(state, "cv") &&
-      MaybeAppend(state, "operator ") &&
-      EnterNestedName(state) &&
-      ParseType(state) &&
-      LeaveNestedName(state, copy.nest_level)) {
-    return true;
-  }
-  *state = copy;
-
-  // Then vendor extended operators.
-  if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") &&
-      ParseSourceName(state)) {
-    return true;
-  }
-  *state = copy;
-
-  // Other operator names should start with a lower alphabet followed
-  // by a lower/upper alphabet.
-  if (!(IsLower(state->mangled_cur[0]) &&
-        IsAlpha(state->mangled_cur[1]))) {
-    return false;
-  }
-  // We may want to perform a binary search if we really need speed.
-  const AbbrevPair *p;
-  for (p = kOperatorList; p->abbrev != NULL; ++p) {
-    if (state->mangled_cur[0] == p->abbrev[0] &&
-        state->mangled_cur[1] == p->abbrev[1]) {
-      MaybeAppend(state, "operator");
-      if (IsLower(*p->real_name)) {  // new, delete, etc.
-        MaybeAppend(state, " ");
-      }
-      MaybeAppend(state, p->real_name);
-      state->mangled_cur += 2;
-      return true;
-    }
-  }
-  return false;
-}
-
-// <special-name> ::= TV <type>
-//                ::= TT <type>
-//                ::= TI <type>
-//                ::= TS <type>
-//                ::= Tc <call-offset> <call-offset> <(base) encoding>
-//                ::= GV <(object) name>
-//                ::= T <call-offset> <(base) encoding>
-// G++ extensions:
-//                ::= TC <type> <(offset) number> _ <(base) type>
-//                ::= TF <type>
-//                ::= TJ <type>
-//                ::= GR <name>
-//                ::= GA <encoding>
-//                ::= Th <call-offset> <(base) encoding>
-//                ::= Tv <call-offset> <(base) encoding>
-//
-// Note: we don't care much about them since they don't appear in
-// stack traces.  The are special data.
-static bool ParseSpecialName(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'T') &&
-      ParseCharClass(state, "VTIS") &&
-      ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) &&
-      ParseCallOffset(state) && ParseEncoding(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseTwoCharToken(state, "GV") &&
-      ParseName(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) &&
-      ParseEncoding(state)) {
-    return true;
-  }
-  *state = copy;
-
-  // G++ extensions
-  if (ParseTwoCharToken(state, "TC") && ParseType(state) &&
-      ParseNumber(state, NULL) && ParseOneCharToken(state, '_') &&
-      DisableAppend(state) &&
-      ParseType(state)) {
-    RestoreAppend(state, copy.append);
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") &&
-      ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseTwoCharToken(state, "GR") && ParseName(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") &&
-      ParseCallOffset(state) && ParseEncoding(state)) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <call-offset> ::= h <nv-offset> _
-//               ::= v <v-offset> _
-static bool ParseCallOffset(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'h') &&
-      ParseNVOffset(state) && ParseOneCharToken(state, '_')) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'v') &&
-      ParseVOffset(state) && ParseOneCharToken(state, '_')) {
-    return true;
-  }
-  *state = copy;
-
-  return false;
-}
-
-// <nv-offset> ::= <(offset) number>
-static bool ParseNVOffset(State *state) {
-  return ParseNumber(state, NULL);
-}
-
-// <v-offset>  ::= <(offset) number> _ <(virtual offset) number>
-static bool ParseVOffset(State *state) {
-  State copy = *state;
-  if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') &&
-      ParseNumber(state, NULL)) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <ctor-dtor-name> ::= C1 | C2 | C3
-//                  ::= D0 | D1 | D2
-static bool ParseCtorDtorName(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'C') &&
-      ParseCharClass(state, "123")) {
-    const char * const prev_name = state->prev_name;
-    const int prev_name_length = state->prev_name_length;
-    MaybeAppendWithLength(state, prev_name, prev_name_length);
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'D') &&
-      ParseCharClass(state, "012")) {
-    const char * const prev_name = state->prev_name;
-    const int prev_name_length = state->prev_name_length;
-    MaybeAppend(state, "~");
-    MaybeAppendWithLength(state, prev_name, prev_name_length);
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <type> ::= <CV-qualifiers> <type>
-//        ::= P <type>   # pointer-to
-//        ::= R <type>   # reference-to
-//        ::= O <type>   # rvalue reference-to (C++0x)
-//        ::= C <type>   # complex pair (C 2000)
-//        ::= G <type>   # imaginary (C 2000)
-//        ::= U <source-name> <type>  # vendor extended type qualifier
-//        ::= <builtin-type>
-//        ::= <function-type>
-//        ::= <class-enum-type>
-//        ::= <array-type>
-//        ::= <pointer-to-member-type>
-//        ::= <template-template-param> <template-args>
-//        ::= <template-param>
-//        ::= <substitution>
-//        ::= Dp <type>          # pack expansion of (C++0x)
-//        ::= Dt <expression> E  # decltype of an id-expression or class
-//                               # member access (C++0x)
-//        ::= DT <expression> E  # decltype of an expression (C++0x)
-//
-static bool ParseType(State *state) {
-  // We should check CV-qualifers, and PRGC things first.
-  State copy = *state;
-  if (ParseCVQualifiers(state) && ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseCharClass(state, "OPRCG") && ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseTwoCharToken(state, "Dp") && ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") &&
-      ParseExpression(state) && ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'U') && ParseSourceName(state) &&
-      ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseBuiltinType(state) ||
-      ParseFunctionType(state) ||
-      ParseClassEnumType(state) ||
-      ParseArrayType(state) ||
-      ParsePointerToMemberType(state) ||
-      ParseSubstitution(state)) {
-    return true;
-  }
-
-  if (ParseTemplateTemplateParam(state) &&
-      ParseTemplateArgs(state)) {
-    return true;
-  }
-  *state = copy;
-
-  // Less greedy than <template-template-param> <template-args>.
-  if (ParseTemplateParam(state)) {
-    return true;
-  }
-
-  return false;
-}
-
-// <CV-qualifiers> ::= [r] [V] [K]
-// We don't allow empty <CV-qualifiers> to avoid infinite loop in
-// ParseType().
-static bool ParseCVQualifiers(State *state) {
-  int num_cv_qualifiers = 0;
-  num_cv_qualifiers += ParseOneCharToken(state, 'r');
-  num_cv_qualifiers += ParseOneCharToken(state, 'V');
-  num_cv_qualifiers += ParseOneCharToken(state, 'K');
-  return num_cv_qualifiers > 0;
-}
-
-// <builtin-type> ::= v, etc.
-//                ::= u <source-name>
-static bool ParseBuiltinType(State *state) {
-  const AbbrevPair *p;
-  for (p = kBuiltinTypeList; p->abbrev != NULL; ++p) {
-    if (state->mangled_cur[0] == p->abbrev[0]) {
-      MaybeAppend(state, p->real_name);
-      ++state->mangled_cur;
-      return true;
-    }
-  }
-
-  State copy = *state;
-  if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <function-type> ::= F [Y] <bare-function-type> E
-static bool ParseFunctionType(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'F') &&
-      Optional(ParseOneCharToken(state, 'Y')) &&
-      ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <bare-function-type> ::= <(signature) type>+
-static bool ParseBareFunctionType(State *state) {
-  State copy = *state;
-  DisableAppend(state);
-  if (OneOrMore(ParseType, state)) {
-    RestoreAppend(state, copy.append);
-    MaybeAppend(state, "()");
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <class-enum-type> ::= <name>
-static bool ParseClassEnumType(State *state) {
-  return ParseName(state);
-}
-
-// <array-type> ::= A <(positive dimension) number> _ <(element) type>
-//              ::= A [<(dimension) expression>] _ <(element) type>
-static bool ParseArrayType(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) &&
-      ParseOneCharToken(state, '_') && ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) &&
-      ParseOneCharToken(state, '_') && ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <pointer-to-member-type> ::= M <(class) type> <(member) type>
-static bool ParsePointerToMemberType(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'M') && ParseType(state) &&
-      ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <template-param> ::= T_
-//                  ::= T <parameter-2 non-negative number> _
-static bool ParseTemplateParam(State *state) {
-  if (ParseTwoCharToken(state, "T_")) {
-    MaybeAppend(state, "?");  // We don't support template substitutions.
-    return true;
-  }
-
-  State copy = *state;
-  if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) &&
-      ParseOneCharToken(state, '_')) {
-    MaybeAppend(state, "?");  // We don't support template substitutions.
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-
-// <template-template-param> ::= <template-param>
-//                           ::= <substitution>
-static bool ParseTemplateTemplateParam(State *state) {
-  return (ParseTemplateParam(state) ||
-          ParseSubstitution(state));
-}
-
-// <template-args> ::= I <template-arg>+ E
-static bool ParseTemplateArgs(State *state) {
-  State copy = *state;
-  DisableAppend(state);
-  if (ParseOneCharToken(state, 'I') &&
-      OneOrMore(ParseTemplateArg, state) &&
-      ParseOneCharToken(state, 'E')) {
-    RestoreAppend(state, copy.append);
-    MaybeAppend(state, "<>");
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <template-arg>  ::= <type>
-//                 ::= <expr-primary>
-//                 ::= I <template-arg>* E        # argument pack
-//                 ::= J <template-arg>* E        # argument pack
-//                 ::= X <expression> E
-static bool ParseTemplateArg(State *state) {
-  State copy = *state;
-  if ((ParseOneCharToken(state, 'I') || ParseOneCharToken(state, 'J')) &&
-      ZeroOrMore(ParseTemplateArg, state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseType(state) ||
-      ParseExprPrimary(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'X') && ParseExpression(state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <expression> ::= <template-param>
-//              ::= <expr-primary>
-//              ::= <unary operator-name> <expression>
-//              ::= <binary operator-name> <expression> <expression>
-//              ::= <trinary operator-name> <expression> <expression>
-//                  <expression>
-//              ::= st <type>
-//              ::= sr <type> <unqualified-name> <template-args>
-//              ::= sr <type> <unqualified-name>
-static bool ParseExpression(State *state) {
-  if (ParseTemplateParam(state) || ParseExprPrimary(state)) {
-    return true;
-  }
-
-  State copy = *state;
-  if (ParseOperatorName(state) &&
-      ParseExpression(state) &&
-      ParseExpression(state) &&
-      ParseExpression(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOperatorName(state) &&
-      ParseExpression(state) &&
-      ParseExpression(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOperatorName(state) &&
-      ParseExpression(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseTwoCharToken(state, "st") && ParseType(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseTwoCharToken(state, "sr") && ParseType(state) &&
-      ParseUnqualifiedName(state) &&
-      ParseTemplateArgs(state)) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseTwoCharToken(state, "sr") && ParseType(state) &&
-      ParseUnqualifiedName(state)) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <expr-primary> ::= L <type> <(value) number> E
-//                ::= L <type> <(value) float> E
-//                ::= L <mangled-name> E
-//                // A bug in g++'s C++ ABI version 2 (-fabi-version=2).
-//                ::= LZ <encoding> E
-static bool ParseExprPrimary(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'L') && ParseType(state) &&
-      ParseNumber(state, NULL) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'L') && ParseType(state) &&
-      ParseFloatNumber(state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'L') && ParseMangledName(state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) &&
-      ParseOneCharToken(state, 'E')) {
-    return true;
-  }
-  *state = copy;
-
-  return false;
-}
-
-// <local-name> := Z <(function) encoding> E <(entity) name>
-//                 [<discriminator>]
-//              := Z <(function) encoding> E s [<discriminator>]
-static bool ParseLocalName(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) &&
-      ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") &&
-      ParseName(state) && Optional(ParseDiscriminator(state))) {
-    return true;
-  }
-  *state = copy;
-
-  if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) &&
-      ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <discriminator> := _ <(non-negative) number>
-static bool ParseDiscriminator(State *state) {
-  State copy = *state;
-  if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) {
-    return true;
-  }
-  *state = copy;
-  return false;
-}
-
-// <substitution> ::= S_
-//                ::= S <seq-id> _
-//                ::= St, etc.
-static bool ParseSubstitution(State *state) {
-  if (ParseTwoCharToken(state, "S_")) {
-    MaybeAppend(state, "?");  // We don't support substitutions.
-    return true;
-  }
-
-  State copy = *state;
-  if (ParseOneCharToken(state, 'S') && ParseSeqId(state) &&
-      ParseOneCharToken(state, '_')) {
-    MaybeAppend(state, "?");  // We don't support substitutions.
-    return true;
-  }
-  *state = copy;
-
-  // Expand abbreviations like "St" => "std".
-  if (ParseOneCharToken(state, 'S')) {
-    const AbbrevPair *p;
-    for (p = kSubstitutionList; p->abbrev != NULL; ++p) {
-      if (state->mangled_cur[0] == p->abbrev[1]) {
-        MaybeAppend(state, "std");
-        if (p->real_name[0] != '\0') {
-          MaybeAppend(state, "::");
-          MaybeAppend(state, p->real_name);
-        }
-        ++state->mangled_cur;
-        return true;
-      }
-    }
-  }
-  *state = copy;
-  return false;
-}
-
-// Parse <mangled-name>, optionally followed by either a function-clone suffix
-// or version suffix.  Returns true only if all of "mangled_cur" was consumed.
-static bool ParseTopLevelMangledName(State *state) {
-  if (ParseMangledName(state)) {
-    if (state->mangled_cur[0] != '\0') {
-      // Drop trailing function clone suffix, if any.
-      if (IsFunctionCloneSuffix(state->mangled_cur)) {
-        return true;
-      }
-      // Append trailing version suffix if any.
-      // ex. _Z3foo@@GLIBCXX_3.4
-      if (state->mangled_cur[0] == '@') {
-        MaybeAppend(state, state->mangled_cur);
-        return true;
-      }
-      return false;  // Unconsumed suffix.
-    }
-    return true;
-  }
-  return false;
-}
-#endif
-
-// The demangler entry point.
-bool Demangle(const char *mangled, char *out, int out_size) {
-#if defined(OS_WINDOWS)
-  // When built with incremental linking, the Windows debugger
-  // library provides a more complicated `Symbol->Name` with the
-  // Incremental Linking Table offset, which looks like
-  // `@ILT+1105(?func@Foo@@SAXH@Z)`. However, the demangler expects
-  // only the mangled symbol, `?func@Foo@@SAXH@Z`. Fortunately, the
-  // mangled symbol is guaranteed not to have parentheses,
-  // so we search for `(` and extract up to `)`.
-  //
-  // Since we may be in a signal handler here, we cannot use `std::string`.
-  char buffer[1024];  // Big enough for a sane symbol.
-  const char *lparen = strchr(mangled, '(');
-  if (lparen) {
-    // Extract the string `(?...)`
-    const char *rparen = strchr(lparen, ')');
-    size_t length = rparen - lparen - 1;
-    strncpy(buffer, lparen + 1, length);
-    buffer[length] = '\0';
-    mangled = buffer;
-  } // Else the symbol wasn't inside a set of parentheses
-  // We use the ANSI version to ensure the string type is always `char *`.
-  return UnDecorateSymbolName(mangled, out, out_size, UNDNAME_COMPLETE);
-#else
-  State state;
-  InitState(&state, mangled, out, out_size);
-  return ParseTopLevelMangledName(&state) && !state.overflowed;
-#endif
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/demangle.h b/third_party/glog/src/demangle.h
deleted file mode 100644
index 991b6ffcf2..0000000000
--- a/third_party/glog/src/demangle.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2006, Google 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:
-//
-//     * 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.
-//
-// Author: Satoru Takabayashi
-//
-// An async-signal-safe and thread-safe demangler for Itanium C++ ABI
-// (aka G++ V3 ABI).
-
-// The demangler is implemented to be used in async signal handlers to
-// symbolize stack traces.  We cannot use libstdc++'s
-// abi::__cxa_demangle() in such signal handlers since it's not async
-// signal safe (it uses malloc() internally).
-//
-// Note that this demangler doesn't support full demangling.  More
-// specifically, it doesn't print types of function parameters and
-// types of template arguments.  It just skips them.  However, it's
-// still very useful to extract basic information such as class,
-// function, constructor, destructor, and operator names.
-//
-// See the implementation note in demangle.cc if you are interested.
-//
-// Example:
-//
-// | Mangled Name  | The Demangler | abi::__cxa_demangle()
-// |---------------|---------------|-----------------------
-// | _Z1fv         | f()           | f()
-// | _Z1fi         | f()           | f(int)
-// | _Z3foo3bar    | foo()         | foo(bar)
-// | _Z1fIiEvi     | f<>()         | void f<int>(int)
-// | _ZN1N1fE      | N::f          | N::f
-// | _ZN3Foo3BarEv | Foo::Bar()    | Foo::Bar()
-// | _Zrm1XS_"     | operator%()   | operator%(X, X)
-// | _ZN3FooC1Ev   | Foo::Foo()    | Foo::Foo()
-// | _Z1fSs        | f()           | f(std::basic_string<char,
-// |               |               |   std::char_traits<char>,
-// |               |               |   std::allocator<char> >)
-//
-// See the unit test for more examples.
-//
-// Note: we might want to write demanglers for ABIs other than Itanium
-// C++ ABI in the future.
-//
-
-#ifndef BASE_DEMANGLE_H_
-#define BASE_DEMANGLE_H_
-
-#include "config.h"
-#include "glog/logging.h"
-
-_START_GOOGLE_NAMESPACE_
-
-// Demangle "mangled".  On success, return true and write the
-// demangled symbol name to "out".  Otherwise, return false.
-// "out" is modified even if demangling is unsuccessful.
-bool GOOGLE_GLOG_DLL_DECL Demangle(const char *mangled, char *out, int out_size);
-
-_END_GOOGLE_NAMESPACE_
-
-#endif  // BASE_DEMANGLE_H_
diff --git a/third_party/glog/src/demangle_unittest.cc b/third_party/glog/src/demangle_unittest.cc
deleted file mode 100644
index be483411f1..0000000000
--- a/third_party/glog/src/demangle_unittest.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright (c) 2006, Google 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:
-//
-//     * 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.
-//
-// Author: Satoru Takabayashi
-//
-// Unit tests for functions in demangle.c.
-
-#include "utilities.h"
-
-#include <iostream>
-#include <fstream>
-#include <string>
-#include "glog/logging.h"
-#include "demangle.h"
-#include "googletest.h"
-#include "config.h"
-
-#ifdef HAVE_LIB_GFLAGS
-#include <gflags/gflags.h>
-using namespace GFLAGS_NAMESPACE;
-#endif
-
-GLOG_DEFINE_bool(demangle_filter, false,
-                 "Run demangle_unittest in filter mode");
-
-using namespace std;
-using namespace GOOGLE_NAMESPACE;
-
-// A wrapper function for Demangle() to make the unit test simple.
-static const char *DemangleIt(const char * const mangled) {
-  static char demangled[4096];
-  if (Demangle(mangled, demangled, sizeof(demangled))) {
-    return demangled;
-  } else {
-    return mangled;
-  }
-}
-
-#if defined(OS_WINDOWS)
-
-TEST(Demangle, Windows) {
-  EXPECT_STREQ(
-    "public: static void __cdecl Foo::func(int)",
-    DemangleIt("?func@Foo@@SAXH@Z"));
-  EXPECT_STREQ(
-    "public: static void __cdecl Foo::func(int)",
-    DemangleIt("@ILT+1105(?func@Foo@@SAXH@Z)"));
-  EXPECT_STREQ(
-    "int __cdecl foobarArray(int * const)",
-    DemangleIt("?foobarArray@@YAHQAH@Z"));
-}
-
-#else
-
-// Test corner cases of bounary conditions.
-TEST(Demangle, CornerCases) {
-  const size_t size = 10;
-  char tmp[size] = { 0 };
-  const char *demangled = "foobar()";
-  const char *mangled = "_Z6foobarv";
-  EXPECT_TRUE(Demangle(mangled, tmp, sizeof(tmp)));
-  // sizeof("foobar()") == size - 1
-  EXPECT_STREQ(demangled, tmp);
-  EXPECT_TRUE(Demangle(mangled, tmp, size - 1));
-  EXPECT_STREQ(demangled, tmp);
-  EXPECT_FALSE(Demangle(mangled, tmp, size - 2));  // Not enough.
-  EXPECT_FALSE(Demangle(mangled, tmp, 1));
-  EXPECT_FALSE(Demangle(mangled, tmp, 0));
-  EXPECT_FALSE(Demangle(mangled, NULL, 0));  // Should not cause SEGV.
-}
-
-// Test handling of functions suffixed with .clone.N, which is used by GCC
-// 4.5.x, and .constprop.N and .isra.N, which are used by GCC 4.6.x.  These
-// suffixes are used to indicate functions which have been cloned during
-// optimization.  We ignore these suffixes.
-TEST(Demangle, Clones) {
-  char tmp[20];
-  EXPECT_TRUE(Demangle("_ZL3Foov", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  EXPECT_TRUE(Demangle("_ZL3Foov.clone.3", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  EXPECT_TRUE(Demangle("_ZL3Foov.constprop.80", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  EXPECT_TRUE(Demangle("_ZL3Foov.isra.18", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  EXPECT_TRUE(Demangle("_ZL3Foov.isra.2.constprop.18", tmp, sizeof(tmp)));
-  EXPECT_STREQ("Foo()", tmp);
-  // Invalid (truncated), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.clo", tmp, sizeof(tmp)));
-  // Invalid (.clone. not followed by number), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.clone.", tmp, sizeof(tmp)));
-  // Invalid (.clone. followed by non-number), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.clone.foo", tmp, sizeof(tmp)));
-  // Invalid (.constprop. not followed by number), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.isra.2.constprop.", tmp, sizeof(tmp)));
-}
-
-TEST(Demangle, FromFile) {
-  string test_file = FLAGS_test_srcdir + "/src/demangle_unittest.txt";
-  ifstream f(test_file.c_str());  // The file should exist.
-  EXPECT_FALSE(f.fail());
-
-  string line;
-  while (getline(f, line)) {
-    // Lines start with '#' are considered as comments.
-    if (line.empty() || line[0] == '#') {
-      continue;
-    }
-    // Each line should contain a mangled name and a demangled name
-    // separated by '\t'.  Example: "_Z3foo\tfoo"
-    string::size_type tab_pos = line.find('\t');
-    EXPECT_NE(string::npos, tab_pos);
-    string mangled = line.substr(0, tab_pos);
-    string demangled = line.substr(tab_pos + 1);
-    EXPECT_EQ(demangled, DemangleIt(mangled.c_str()));
-  }
-}
-
-#endif
-
-int main(int argc, char **argv) {
-#ifdef HAVE_LIB_GFLAGS
-  ParseCommandLineFlags(&argc, &argv, true);
-#endif
-  InitGoogleTest(&argc, argv);
-
-  FLAGS_logtostderr = true;
-  InitGoogleLogging(argv[0]);
-  if (FLAGS_demangle_filter) {
-    // Read from cin and write to cout.
-    string line;
-    while (getline(cin, line, '\n')) {
-      cout << DemangleIt(line.c_str()) << endl;
-    }
-    return 0;
-  } else if (argc > 1) {
-    cout << DemangleIt(argv[1]) << endl;
-    return 0;
-  } else {
-    return RUN_ALL_TESTS();
-  }
-}
diff --git a/third_party/glog/src/demangle_unittest.sh b/third_party/glog/src/demangle_unittest.sh
deleted file mode 100755
index 91deee2198..0000000000
--- a/third_party/glog/src/demangle_unittest.sh
+++ /dev/null
@@ -1,95 +0,0 @@
-#! /bin/sh
-#
-# Copyright (c) 2006, Google 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:
-#
-#     * 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.
-#
-# Author: Satoru Takabayashi
-#
-# Unit tests for demangle.c with a real binary.
-
-set -e
-
-die () {
-    echo $1
-    exit 1
-}
-
-BINDIR=".libs"
-LIBGLOG="$BINDIR/libglog.so"
-
-DEMANGLER="$BINDIR/demangle_unittest"
-
-if test -e "$DEMANGLER"; then
-  # We need shared object.
-  export LD_LIBRARY_PATH=$BINDIR
-  export DYLD_LIBRARY_PATH=$BINDIR
-else
-  # For windows
-  DEMANGLER="./demangle_unittest.exe"
-  if ! test -e "$DEMANGLER"; then
-    echo "We coundn't find demangle_unittest binary."
-    exit 1
-  fi
-fi
-
-# Extract C++ mangled symbols from libbase.so.
-NM_OUTPUT="demangle.nm"
-nm "$LIBGLOG" | perl -nle 'print $1 if /\s(_Z\S+$)/' > "$NM_OUTPUT"
-
-# Check if mangled symbols exist. If there are none, we quit.
-# The binary is more likely compiled with GCC 2.95 or something old.
-if ! grep --quiet '^_Z' "$NM_OUTPUT"; then
-    echo "PASS"
-    exit 0
-fi
-
-# Demangle the symbols using our demangler.
-DM_OUTPUT="demangle.dm"
-GLOG_demangle_filter=1 "$DEMANGLER" --demangle_filter < "$NM_OUTPUT" > "$DM_OUTPUT"
-
-# Calculate the numbers of lines.
-NM_LINES=`wc -l "$NM_OUTPUT" | awk '{ print $1 }'`
-DM_LINES=`wc -l "$DM_OUTPUT" | awk '{ print $1 }'`
-
-# Compare the numbers of lines.  They must be the same.
-if test "$NM_LINES" != "$DM_LINES"; then
-    die "$NM_OUTPUT and $DM_OUTPUT don't have the same numbers of lines"
-fi
-
-# Check if mangled symbols exist.  They must not exist.
-if grep --quiet '^_Z' "$DM_OUTPUT"; then
-    MANGLED=`grep '^_Z' "$DM_OUTPUT" | wc -l | awk '{ print \$1 }'`
-    echo "Mangled symbols ($MANGLED out of $NM_LINES) found in $DM_OUTPUT:"
-    grep '^_Z' "$DM_OUTPUT"
-    die "Mangled symbols ($MANGLED out of $NM_LINES) found in $DM_OUTPUT"
-fi
-
-# All C++ symbols are demangled successfully.
-echo "PASS"
-exit 0
diff --git a/third_party/glog/src/demangle_unittest.txt b/third_party/glog/src/demangle_unittest.txt
deleted file mode 100644
index 07e28bce2f..0000000000
--- a/third_party/glog/src/demangle_unittest.txt
+++ /dev/null
@@ -1,145 +0,0 @@
-# Test caces for demangle_unittest.  Each line consists of a
-# tab-separated pair of mangled and demangled symbol names.
-
-# Constructors and destructors.
-_ZN3FooC1Ev	Foo::Foo()
-_ZN3FooD1Ev	Foo::~Foo()
-_ZNSoD0Ev	std::ostream::~ostream()
-
-# G++ extensions.
-_ZTCN10LogMessage9LogStreamE0_So	LogMessage::LogStream
-_ZTv0_n12_N10LogMessage9LogStreamD0Ev	LogMessage::LogStream::~LogStream()
-_ZThn4_N7icu_3_410UnicodeSetD0Ev	icu_3_4::UnicodeSet::~UnicodeSet()
-
-# A bug in g++'s C++ ABI version 2 (-fabi-version=2).
-_ZN7NSSInfoI5groupjjXadL_Z10getgrgid_rEELZ19nss_getgrgid_r_nameEEC1Ei	NSSInfo<>::NSSInfo()
-
-# C linkage symbol names.  Should keep them untouched.
-main	main
-Demangle	Demangle
-_ZERO	_ZERO
-
-# Cast operator.
-_Zcviv	operator int()
-_ZN3foocviEv	foo::operator int()
-
-# Versioned symbols.
-_Z3Foo@GLIBCXX_3.4	Foo@GLIBCXX_3.4
-_Z3Foo@@GLIBCXX_3.4	Foo@@GLIBCXX_3.4
-
-# Abbreviations.
-_ZNSaE	std::allocator
-_ZNSbE	std::basic_string
-_ZNSdE	std::iostream
-_ZNSiE	std::istream
-_ZNSoE	std::ostream
-_ZNSsE	std::string
-
-# Substitutions.  We just replace them with ?.
-_ZN3fooS_E	foo::?
-_ZN3foo3barS0_E	foo::bar::?
-_ZNcvT_IiEEv	operator ?<>()
-
-# "<< <" case.
-_ZlsI3fooE	operator<< <>
-
-# ABI tags.
-_Z1AB3barB3foo	A
-_ZN3fooL3barB5cxx11E	foo::bar
-
-# Random things we found interesting.
-_ZN3FooISt6vectorISsSaISsEEEclEv	Foo<>::operator()()
-_ZTI9Callback1IiE	Callback1<>
-_ZN7icu_3_47UMemorynwEj	icu_3_4::UMemory::operator new()
-_ZNSt6vectorIbE9push_backE	std::vector<>::push_back
-_ZNSt6vectorIbSaIbEE9push_backEb	std::vector<>::push_back()
-_ZlsRSoRK15PRIVATE_Counter	operator<<()
-_ZSt6fill_nIPPN9__gnu_cxx15_Hashtable_nodeISt4pairIKPKcjEEEjS8_ET_SA_T0_RKT1_	std::fill_n<>()
-_ZZ3FoovE3Bar	Foo()::Bar
-_ZGVZ7UpTimervE8up_timer	UpTimer()::up_timer
-
-# Test cases from gcc-4.1.0/libstdc++-v3/testsuite/demangle.
-# Collected by:
-# % grep verify_demangle **/*.cc | perl -nle 'print $1 if /"(_Z.*?)"/' |
-#   sort | uniq
-#
-# Note that the following symbols are invalid.
-# That's why they are not demangled.
-# - _ZNZN1N1fEiE1X1gE
-# - _ZNZN1N1fEiE1X1gEv
-# - _Z1xINiEE
-_Z1fA37_iPS_	f()
-_Z1fAszL_ZZNK1N1A1fEvE3foo_0E_i	f()
-_Z1fI1APS0_PKS0_EvT_T0_T1_PA4_S3_M1CS8_	f<>()
-_Z1fI1XENT_1tES2_	f<>()
-_Z1fI1XEvPVN1AIT_E1TE	f<>()
-_Z1fILi1ELc120EEv1AIXplT_cviLd4028ae147ae147aeEEE	f<>()
-_Z1fILi1ELc120EEv1AIXplT_cviLf3f800000EEE	f<>()
-_Z1fILi5E1AEvN1CIXqugtT_Li0ELi1ELi2EEE1qE	f<>()
-_Z1fILi5E1AEvN1CIXstN1T1tEEXszsrS2_1tEE1qE	f<>()
-_Z1fILi5EEvN1AIXcvimlT_Li22EEE1qE	f<>()
-_Z1fIiEvi	f<>()
-_Z1fKPFiiE	f()
-_Z1fM1AFivEPS0_	f()
-_Z1fM1AKFivE	f()
-_Z1fM1AKFvvE	f()
-_Z1fPFPA1_ivE	f()
-_Z1fPFYPFiiEiE	f()
-_Z1fPFvvEM1SFvvE	f()
-_Z1fPKM1AFivE	f()
-_Z1fi	f()
-_Z1fv	f()
-_Z1jM1AFivEPS1_	j()
-_Z1rM1GFivEMS_KFivES_M1HFivES1_4whatIKS_E5what2IS8_ES3_	r()
-_Z1sPA37_iPS0_	s()
-_Z1xINiEE	_Z1xINiEE
-_Z3absILi11EEvv	abs<>()
-_Z3foo3bar	foo()
-_Z3foo5Hello5WorldS0_S_	foo()
-_Z3fooA30_A_i	foo()
-_Z3fooIA6_KiEvA9_KT_rVPrS4_	foo<>()
-_Z3fooILi2EEvRAplT_Li1E_i	foo<>()
-_Z3fooIiFvdEiEvv	foo<>()
-_Z3fooPM2ABi	foo()
-_Z3fooc	foo()
-_Z3fooiPiPS_PS0_PS1_PS2_PS3_PS4_PS5_PS6_PS7_PS8_PS9_PSA_PSB_PSC_	foo()
-_Z3kooPA28_A30_i	koo()
-_Z4makeI7FactoryiET_IT0_Ev	make<>()
-_Z5firstI3DuoEvS0_	first<>()
-_Z5firstI3DuoEvT_	first<>()
-_Z9hairyfuncM1YKFPVPFrPA2_PM1XKFKPA3_ilEPcEiE	hairyfunc()
-_ZGVN5libcw24_GLOBAL__N_cbll.cc0ZhUKa23compiler_bug_workaroundISt6vectorINS_13omanip_id_tctINS_5debug32memblk_types_manipulator_data_ctEEESaIS6_EEE3idsE	libcw::(anonymous namespace)::compiler_bug_workaround<>::ids
-_ZN12libcw_app_ct10add_optionIS_EEvMT_FvPKcES3_cS3_S3_	libcw_app_ct::add_option<>()
-_ZN1AIfEcvT_IiEEv	A<>::operator ?<>()
-_ZN1N1TIiiE2mfES0_IddE	N::T<>::mf()
-_ZN1N1fE	N::f
-_ZN1f1fE	f::f
-_ZN3FooIA4_iE3barE	Foo<>::bar
-_ZN5Arena5levelE	Arena::level
-_ZN5StackIiiE5levelE	Stack<>::level
-_ZN5libcw5debug13cwprint_usingINS_9_private_12GlobalObjectEEENS0_17cwprint_using_tctIT_EERKS5_MS5_KFvRSt7ostreamE	libcw::debug::cwprint_using<>()
-_ZN6System5Sound4beepEv	System::Sound::beep()
-_ZNKSt14priority_queueIP27timer_event_request_base_ctSt5dequeIS1_SaIS1_EE13timer_greaterE3topEv	std::priority_queue<>::top()
-_ZNKSt15_Deque_iteratorIP15memory_block_stRKS1_PS2_EeqERKS5_	std::_Deque_iterator<>::operator==()
-_ZNKSt17__normal_iteratorIPK6optionSt6vectorIS0_SaIS0_EEEmiERKS6_	std::__normal_iterator<>::operator-()
-_ZNSbIcSt11char_traitsIcEN5libcw5debug27no_alloc_checking_allocatorEE12_S_constructIPcEES6_T_S7_RKS3_	std::basic_string<>::_S_construct<>()
-_ZNSt13_Alloc_traitsISbIcSt18string_char_traitsIcEN5libcw5debug9_private_17allocator_adaptorIcSt24__default_alloc_templateILb0ELi327664EELb1EEEENS5_IS9_S7_Lb1EEEE15_S_instancelessE	std::_Alloc_traits<>::_S_instanceless
-_ZNSt3_In4wardE	std::_In::ward
-_ZNZN1N1fEiE1X1gE	_ZNZN1N1fEiE1X1gE
-_ZNZN1N1fEiE1X1gEv	_ZNZN1N1fEiE1X1gEv
-_ZSt1BISt1DIP1ARKS2_PS3_ES0_IS2_RS2_PS2_ES2_ET0_T_SB_SA_PT1_	std::B<>()
-_ZSt5state	std::state
-_ZTI7a_class	a_class
-_ZZN1N1fEiE1p	N::f()::p
-_ZZN1N1fEiEs	N::f()
-_ZlsRK1XS1_	operator<<()
-_ZlsRKU3fooU4bart1XS0_	operator<<()
-_ZlsRKU3fooU4bart1XS2_	operator<<()
-_ZlsRSoRKSs	operator<<()
-_ZngILi42EEvN1AIXplT_Li2EEE1TE	operator-<>()
-_ZplR1XS0_	operator+()
-_Zrm1XS_	operator%()
-
-# Template argument packs can start with I or J.
-_Z3addIIiEEvDpT_	add<>()
-_Z3addIJiEEvDpT_	add<>()
diff --git a/third_party/glog/src/glog/log_severity.h b/third_party/glog/src/glog/log_severity.h
deleted file mode 100644
index 987cc57dae..0000000000
--- a/third_party/glog/src/glog/log_severity.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (c) 2007, Google 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:
-//
-//     * 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.
-
-#ifndef BASE_LOG_SEVERITY_H__
-#define BASE_LOG_SEVERITY_H__
-
-// The recommended semantics of the log levels are as follows:
-//
-// INFO:
-//   Use for state changes or other major events, or to aid debugging.
-// WARNING:
-//   Use for undesired but relatively expected events, which may indicate a
-//   problem
-// ERROR:
-//   Use for undesired and unexpected events that the program can recover from.
-//   All ERRORs should be actionable - it should be appropriate to file a bug
-//   whenever an ERROR occurs in production.
-// FATAL:
-//   Use for undesired and unexpected events that the program cannot recover
-//   from. 
-
-// Annoying stuff for windows -- makes sure clients can import these functions
-#ifndef GOOGLE_GLOG_DLL_DECL
-# if defined(_WIN32) && !defined(__CYGWIN__)
-#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
-# else
-#   define GOOGLE_GLOG_DLL_DECL
-# endif
-#endif
-
-// Variables of type LogSeverity are widely taken to lie in the range
-// [0, NUM_SEVERITIES-1].  Be careful to preserve this assumption if
-// you ever need to change their values or add a new severity.
-typedef int LogSeverity;
-
-const int GLOG_INFO = 0, GLOG_WARNING = 1, GLOG_ERROR = 2, GLOG_FATAL = 3,
-  NUM_SEVERITIES = 4;
-#ifndef GLOG_NO_ABBREVIATED_SEVERITIES
-# ifdef ERROR
-#  error ERROR macro is defined. Define GLOG_NO_ABBREVIATED_SEVERITIES before including logging.h. See the document for detail.
-# endif
-const int INFO = GLOG_INFO, WARNING = GLOG_WARNING,
-  ERROR = GLOG_ERROR, FATAL = GLOG_FATAL;
-#endif
-
-// DFATAL is FATAL in debug mode, ERROR in normal mode
-#ifdef NDEBUG
-#define DFATAL_LEVEL ERROR
-#else
-#define DFATAL_LEVEL FATAL
-#endif
-
-extern GOOGLE_GLOG_DLL_DECL const char* const LogSeverityNames[NUM_SEVERITIES];
-
-// NDEBUG usage helpers related to (RAW_)DCHECK:
-//
-// DEBUG_MODE is for small !NDEBUG uses like
-//   if (DEBUG_MODE) foo.CheckThatFoo();
-// instead of substantially more verbose
-//   #ifndef NDEBUG
-//     foo.CheckThatFoo();
-//   #endif
-//
-// IF_DEBUG_MODE is for small !NDEBUG uses like
-//   IF_DEBUG_MODE( string error; )
-//   DCHECK(Foo(&error)) << error;
-// instead of substantially more verbose
-//   #ifndef NDEBUG
-//     string error;
-//     DCHECK(Foo(&error)) << error;
-//   #endif
-//
-#ifdef NDEBUG
-enum { DEBUG_MODE = 0 };
-#define IF_DEBUG_MODE(x)
-#else
-enum { DEBUG_MODE = 1 };
-#define IF_DEBUG_MODE(x) x
-#endif
-
-#endif  // BASE_LOG_SEVERITY_H__
diff --git a/third_party/glog/src/glog/logging.h.in b/third_party/glog/src/glog/logging.h.in
deleted file mode 100644
index 1e552fc1a1..0000000000
--- a/third_party/glog/src/glog/logging.h.in
+++ /dev/null
@@ -1,1689 +0,0 @@
-// Copyright (c) 1999, Google 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:
-//
-//     * 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.
-//
-// Author: Ray Sidney
-//
-// This file contains #include information about logging-related stuff.
-// Pretty much everybody needs to #include this file so that they can
-// log various happenings.
-//
-#ifndef _LOGGING_H_
-#define _LOGGING_H_
-
-#include <errno.h>
-#include <string.h>
-#include <time.h>
-#include <iosfwd>
-#include <ostream>
-#include <sstream>
-#include <string>
-#if @ac_cv_have_unistd_h@
-# include <unistd.h>
-#endif
-#include <vector>
-
-#if defined(_MSC_VER)
-#define GLOG_MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
-                                     __pragma(warning(disable:n))
-#define GLOG_MSVC_POP_WARNING() __pragma(warning(pop))
-#else
-#define GLOG_MSVC_PUSH_DISABLE_WARNING(n)
-#define GLOG_MSVC_POP_WARNING()
-#endif
-
-// Annoying stuff for windows -- makes sure clients can import these functions
-#ifndef GOOGLE_GLOG_DLL_DECL
-# if defined(_WIN32) && !defined(__CYGWIN__)
-#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
-# else
-#   define GOOGLE_GLOG_DLL_DECL
-# endif
-#endif
-
-// We care a lot about number of bits things take up.  Unfortunately,
-// systems define their bit-specific ints in a lot of different ways.
-// We use our own way, and have a typedef to get there.
-// Note: these commands below may look like "#if 1" or "#if 0", but
-// that's because they were constructed that way at ./configure time.
-// Look at logging.h.in to see how they're calculated (based on your config).
-#if @ac_cv_have_stdint_h@
-#include <stdint.h>             // the normal place uint16_t is defined
-#endif
-#if @ac_cv_have_systypes_h@
-#include <sys/types.h>          // the normal place u_int16_t is defined
-#endif
-#if @ac_cv_have_inttypes_h@
-#include <inttypes.h>           // a third place for uint16_t or u_int16_t
-#endif
-
-#if @ac_cv_have_libgflags@
-#include <gflags/gflags.h>
-#endif
-
-@ac_google_start_namespace@
-
-#if @ac_cv_have_uint16_t@      // the C99 format
-typedef int32_t int32;
-typedef uint32_t uint32;
-typedef int64_t int64;
-typedef uint64_t uint64;
-#elif @ac_cv_have_u_int16_t@   // the BSD format
-typedef int32_t int32;
-typedef u_int32_t uint32;
-typedef int64_t int64;
-typedef u_int64_t uint64;
-#elif @ac_cv_have___uint16@    // the windows (vc7) format
-typedef __int32 int32;
-typedef unsigned __int32 uint32;
-typedef __int64 int64;
-typedef unsigned __int64 uint64;
-#else
-#error Do not know how to define a 32-bit integer quantity on your system
-#endif
-
-@ac_google_end_namespace@
-
-// The global value of GOOGLE_STRIP_LOG. All the messages logged to
-// LOG(XXX) with severity less than GOOGLE_STRIP_LOG will not be displayed.
-// If it can be determined at compile time that the message will not be
-// printed, the statement will be compiled out.
-//
-// Example: to strip out all INFO and WARNING messages, use the value
-// of 2 below. To make an exception for WARNING messages from a single
-// file, add "#define GOOGLE_STRIP_LOG 1" to that file _before_ including
-// base/logging.h
-#ifndef GOOGLE_STRIP_LOG
-#define GOOGLE_STRIP_LOG 0
-#endif
-
-// GCC can be told that a certain branch is not likely to be taken (for
-// instance, a CHECK failure), and use that information in static analysis.
-// Giving it this information can help it optimize for the common case in
-// the absence of better information (ie. -fprofile-arcs).
-//
-#ifndef GOOGLE_PREDICT_BRANCH_NOT_TAKEN
-#if @ac_cv_have___builtin_expect@
-#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0))
-#else
-#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) x
-#endif
-#endif
-
-#ifndef GOOGLE_PREDICT_FALSE
-#if @ac_cv_have___builtin_expect@
-#define GOOGLE_PREDICT_FALSE(x) (__builtin_expect(x, 0))
-#else
-#define GOOGLE_PREDICT_FALSE(x) x
-#endif
-#endif
-
-#ifndef GOOGLE_PREDICT_TRUE
-#if @ac_cv_have___builtin_expect@
-#define GOOGLE_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
-#else
-#define GOOGLE_PREDICT_TRUE(x) x
-#endif
-#endif
-
-
-// Make a bunch of macros for logging.  The way to log things is to stream
-// things to LOG(<a particular severity level>).  E.g.,
-//
-//   LOG(INFO) << "Found " << num_cookies << " cookies";
-//
-// You can capture log messages in a string, rather than reporting them
-// immediately:
-//
-//   vector<string> errors;
-//   LOG_STRING(ERROR, &errors) << "Couldn't parse cookie #" << cookie_num;
-//
-// This pushes back the new error onto 'errors'; if given a NULL pointer,
-// it reports the error via LOG(ERROR).
-//
-// You can also do conditional logging:
-//
-//   LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
-//
-// You can also do occasional logging (log every n'th occurrence of an
-// event):
-//
-//   LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
-//
-// The above will cause log messages to be output on the 1st, 11th, 21st, ...
-// times it is executed.  Note that the special google::COUNTER value is used
-// to identify which repetition is happening.
-//
-// You can also do occasional conditional logging (log every n'th
-// occurrence of an event, when condition is satisfied):
-//
-//   LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER
-//                                           << "th big cookie";
-//
-// You can log messages the first N times your code executes a line. E.g.
-//
-//   LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";
-//
-// Outputs log messages for the first 20 times it is executed.
-//
-// Analogous SYSLOG, SYSLOG_IF, and SYSLOG_EVERY_N macros are available.
-// These log to syslog as well as to the normal logs.  If you use these at
-// all, you need to be aware that syslog can drastically reduce performance,
-// especially if it is configured for remote logging!  Don't use these
-// unless you fully understand this and have a concrete need to use them.
-// Even then, try to minimize your use of them.
-//
-// There are also "debug mode" logging macros like the ones above:
-//
-//   DLOG(INFO) << "Found cookies";
-//
-//   DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
-//
-//   DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
-//
-// All "debug mode" logging is compiled away to nothing for non-debug mode
-// compiles.
-//
-// We also have
-//
-//   LOG_ASSERT(assertion);
-//   DLOG_ASSERT(assertion);
-//
-// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
-//
-// There are "verbose level" logging macros.  They look like
-//
-//   VLOG(1) << "I'm printed when you run the program with --v=1 or more";
-//   VLOG(2) << "I'm printed when you run the program with --v=2 or more";
-//
-// These always log at the INFO log level (when they log at all).
-// The verbose logging can also be turned on module-by-module.  For instance,
-//    --vmodule=mapreduce=2,file=1,gfs*=3 --v=0
-// will cause:
-//   a. VLOG(2) and lower messages to be printed from mapreduce.{h,cc}
-//   b. VLOG(1) and lower messages to be printed from file.{h,cc}
-//   c. VLOG(3) and lower messages to be printed from files prefixed with "gfs"
-//   d. VLOG(0) and lower messages to be printed from elsewhere
-//
-// The wildcarding functionality shown by (c) supports both '*' (match
-// 0 or more characters) and '?' (match any single character) wildcards.
-//
-// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as
-//
-//   if (VLOG_IS_ON(2)) {
-//     // do some logging preparation and logging
-//     // that can't be accomplished with just VLOG(2) << ...;
-//   }
-//
-// There are also VLOG_IF, VLOG_EVERY_N and VLOG_IF_EVERY_N "verbose level"
-// condition macros for sample cases, when some extra computation and
-// preparation for logs is not needed.
-//   VLOG_IF(1, (size > 1024))
-//      << "I'm printed when size is more than 1024 and when you run the "
-//         "program with --v=1 or more";
-//   VLOG_EVERY_N(1, 10)
-//      << "I'm printed every 10th occurrence, and when you run the program "
-//         "with --v=1 or more. Present occurence is " << google::COUNTER;
-//   VLOG_IF_EVERY_N(1, (size > 1024), 10)
-//      << "I'm printed on every 10th occurence of case when size is more "
-//         " than 1024, when you run the program with --v=1 or more. ";
-//         "Present occurence is " << google::COUNTER;
-//
-// The supported severity levels for macros that allow you to specify one
-// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
-// Note that messages of a given severity are logged not only in the
-// logfile for that severity, but also in all logfiles of lower severity.
-// E.g., a message of severity FATAL will be logged to the logfiles of
-// severity FATAL, ERROR, WARNING, and INFO.
-//
-// There is also the special severity of DFATAL, which logs FATAL in
-// debug mode, ERROR in normal mode.
-//
-// Very important: logging a message at the FATAL severity level causes
-// the program to terminate (after the message is logged).
-//
-// Unless otherwise specified, logs will be written to the filename
-// "<program name>.<hostname>.<user name>.log.<severity level>.", followed
-// by the date, time, and pid (you can't prevent the date, time, and pid
-// from being in the filename).
-//
-// The logging code takes two flags:
-//     --v=#           set the verbose level
-//     --logtostderr   log all the messages to stderr instead of to logfiles
-
-// LOG LINE PREFIX FORMAT
-//
-// Log lines have this form:
-//
-//     Lyyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg...
-//
-// where the fields are defined as follows:
-//
-//   L                A single character, representing the log level
-//                    (eg 'I' for INFO)
-//   yyyy             The year
-//   mm               The month (zero padded; ie May is '05')
-//   dd               The day (zero padded)
-//   hh:mm:ss.uuuuuu  Time in hours, minutes and fractional seconds
-//   threadid         The space-padded thread ID as returned by GetTID()
-//                    (this matches the PID on Linux)
-//   file             The file name
-//   line             The line number
-//   msg              The user-supplied message
-//
-// Example:
-//
-//   I1103 11:57:31.739339 24395 google.cc:2341] Command line: ./some_prog
-//   I1103 11:57:31.739403 24395 google.cc:2342] Process id 24395
-//
-// NOTE: although the microseconds are useful for comparing events on
-// a single machine, clocks on different machines may not be well
-// synchronized.  Hence, use caution when comparing the low bits of
-// timestamps from different machines.
-
-#ifndef DECLARE_VARIABLE
-#define MUST_UNDEF_GFLAGS_DECLARE_MACROS
-#define DECLARE_VARIABLE(type, shorttype, name, tn)                     \
-  namespace fL##shorttype {                                             \
-    extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name;                      \
-  }                                                                     \
-  using fL##shorttype::FLAGS_##name
-
-// bool specialization
-#define DECLARE_bool(name) \
-  DECLARE_VARIABLE(bool, B, name, bool)
-
-// int32 specialization
-#define DECLARE_int32(name) \
-  DECLARE_VARIABLE(@ac_google_namespace@::int32, I, name, int32)
-
-// Special case for string, because we have to specify the namespace
-// std::string, which doesn't play nicely with our FLAG__namespace hackery.
-#define DECLARE_string(name)                                            \
-  namespace fLS {                                                       \
-    extern GOOGLE_GLOG_DLL_DECL std::string& FLAGS_##name;              \
-  }                                                                     \
-  using fLS::FLAGS_##name
-#endif
-
-// Set whether appending a timestamp to the log file name
-DECLARE_bool(timestamp_in_logfile_name);
-
-// Set whether log messages go to stderr instead of logfiles
-DECLARE_bool(logtostderr);
-
-// Set whether log messages go to stderr in addition to logfiles.
-DECLARE_bool(alsologtostderr);
-
-// Set color messages logged to stderr (if supported by terminal).
-DECLARE_bool(colorlogtostderr);
-
-// Log messages at a level >= this flag are automatically sent to
-// stderr in addition to log files.
-DECLARE_int32(stderrthreshold);
-
-// Set whether the log prefix should be prepended to each line of output.
-DECLARE_bool(log_prefix);
-
-// Log messages at a level <= this flag are buffered.
-// Log messages at a higher level are flushed immediately.
-DECLARE_int32(logbuflevel);
-
-// Sets the maximum number of seconds which logs may be buffered for.
-DECLARE_int32(logbufsecs);
-
-// Log suppression level: messages logged at a lower level than this
-// are suppressed.
-DECLARE_int32(minloglevel);
-
-// If specified, logfiles are written into this directory instead of the
-// default logging directory.
-DECLARE_string(log_dir);
-
-// Set the log file mode.
-DECLARE_int32(logfile_mode);
-
-// Sets the path of the directory into which to put additional links
-// to the log files.
-DECLARE_string(log_link);
-
-DECLARE_int32(v);  // in vlog_is_on.cc
-
-// Sets the maximum log file size (in MB).
-DECLARE_int32(max_log_size);
-
-// Sets whether to avoid logging to the disk if the disk is full.
-DECLARE_bool(stop_logging_if_full_disk);
-
-#ifdef MUST_UNDEF_GFLAGS_DECLARE_MACROS
-#undef MUST_UNDEF_GFLAGS_DECLARE_MACROS
-#undef DECLARE_VARIABLE
-#undef DECLARE_bool
-#undef DECLARE_int32
-#undef DECLARE_string
-#endif
-
-// Log messages below the GOOGLE_STRIP_LOG level will be compiled away for
-// security reasons. See LOG(severtiy) below.
-
-// A few definitions of macros that don't generate much code.  Since
-// LOG(INFO) and its ilk are used all over our code, it's
-// better to have compact code for these operations.
-
-#if GOOGLE_STRIP_LOG == 0
-#define COMPACT_GOOGLE_LOG_INFO @ac_google_namespace@::LogMessage( \
-      __FILE__, __LINE__)
-#define LOG_TO_STRING_INFO(message) @ac_google_namespace@::LogMessage( \
-      __FILE__, __LINE__, @ac_google_namespace@::GLOG_INFO, message)
-#else
-#define COMPACT_GOOGLE_LOG_INFO @ac_google_namespace@::NullStream()
-#define LOG_TO_STRING_INFO(message) @ac_google_namespace@::NullStream()
-#endif
-
-#if GOOGLE_STRIP_LOG <= 1
-#define COMPACT_GOOGLE_LOG_WARNING @ac_google_namespace@::LogMessage( \
-      __FILE__, __LINE__, @ac_google_namespace@::GLOG_WARNING)
-#define LOG_TO_STRING_WARNING(message) @ac_google_namespace@::LogMessage( \
-      __FILE__, __LINE__, @ac_google_namespace@::GLOG_WARNING, message)
-#else
-#define COMPACT_GOOGLE_LOG_WARNING @ac_google_namespace@::NullStream()
-#define LOG_TO_STRING_WARNING(message) @ac_google_namespace@::NullStream()
-#endif
-
-#if GOOGLE_STRIP_LOG <= 2
-#define COMPACT_GOOGLE_LOG_ERROR @ac_google_namespace@::LogMessage( \
-      __FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR)
-#define LOG_TO_STRING_ERROR(message) @ac_google_namespace@::LogMessage( \
-      __FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR, message)
-#else
-#define COMPACT_GOOGLE_LOG_ERROR @ac_google_namespace@::NullStream()
-#define LOG_TO_STRING_ERROR(message) @ac_google_namespace@::NullStream()
-#endif
-
-#if GOOGLE_STRIP_LOG <= 3
-#define COMPACT_GOOGLE_LOG_FATAL @ac_google_namespace@::LogMessageFatal( \
-      __FILE__, __LINE__)
-#define LOG_TO_STRING_FATAL(message) @ac_google_namespace@::LogMessage( \
-      __FILE__, __LINE__, @ac_google_namespace@::GLOG_FATAL, message)
-#else
-#define COMPACT_GOOGLE_LOG_FATAL @ac_google_namespace@::NullStreamFatal()
-#define LOG_TO_STRING_FATAL(message) @ac_google_namespace@::NullStreamFatal()
-#endif
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-#define DCHECK_IS_ON() 0
-#else
-#define DCHECK_IS_ON() 1
-#endif
-
-// For DFATAL, we want to use LogMessage (as opposed to
-// LogMessageFatal), to be consistent with the original behavior.
-#if !DCHECK_IS_ON()
-#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR
-#elif GOOGLE_STRIP_LOG <= 3
-#define COMPACT_GOOGLE_LOG_DFATAL @ac_google_namespace@::LogMessage( \
-      __FILE__, __LINE__, @ac_google_namespace@::GLOG_FATAL)
-#else
-#define COMPACT_GOOGLE_LOG_DFATAL @ac_google_namespace@::NullStreamFatal()
-#endif
-
-#define GOOGLE_LOG_INFO(counter) @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_INFO, counter, &@ac_google_namespace@::LogMessage::SendToLog)
-#define SYSLOG_INFO(counter) \
-  @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_INFO, counter, \
-  &@ac_google_namespace@::LogMessage::SendToSyslogAndLog)
-#define GOOGLE_LOG_WARNING(counter)  \
-  @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_WARNING, counter, \
-  &@ac_google_namespace@::LogMessage::SendToLog)
-#define SYSLOG_WARNING(counter)  \
-  @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_WARNING, counter, \
-  &@ac_google_namespace@::LogMessage::SendToSyslogAndLog)
-#define GOOGLE_LOG_ERROR(counter)  \
-  @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR, counter, \
-  &@ac_google_namespace@::LogMessage::SendToLog)
-#define SYSLOG_ERROR(counter)  \
-  @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR, counter, \
-  &@ac_google_namespace@::LogMessage::SendToSyslogAndLog)
-#define GOOGLE_LOG_FATAL(counter) \
-  @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_FATAL, counter, \
-  &@ac_google_namespace@::LogMessage::SendToLog)
-#define SYSLOG_FATAL(counter) \
-  @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_FATAL, counter, \
-  &@ac_google_namespace@::LogMessage::SendToSyslogAndLog)
-#define GOOGLE_LOG_DFATAL(counter) \
-  @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::DFATAL_LEVEL, counter, \
-  &@ac_google_namespace@::LogMessage::SendToLog)
-#define SYSLOG_DFATAL(counter) \
-  @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::DFATAL_LEVEL, counter, \
-  &@ac_google_namespace@::LogMessage::SendToSyslogAndLog)
-
-#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
-// A very useful logging macro to log windows errors:
-#define LOG_SYSRESULT(result) \
-  if (FAILED(HRESULT_FROM_WIN32(result))) { \
-    LPSTR message = NULL; \
-    LPSTR msg = reinterpret_cast<LPSTR>(&message); \
-    DWORD message_length = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | \
-                         FORMAT_MESSAGE_FROM_SYSTEM, \
-                         0, result, 0, msg, 100, NULL); \
-    if (message_length > 0) { \
-      @ac_google_namespace@::LogMessage(__FILE__, __LINE__, @ac_google_namespace@::GLOG_ERROR, 0, \
-          &@ac_google_namespace@::LogMessage::SendToLog).stream() \
-          << reinterpret_cast<const char*>(message); \
-      LocalFree(message); \
-    } \
-  }
-#endif
-
-// We use the preprocessor's merging operator, "##", so that, e.g.,
-// LOG(INFO) becomes the token GOOGLE_LOG_INFO.  There's some funny
-// subtle difference between ostream member streaming functions (e.g.,
-// ostream::operator<<(int) and ostream non-member streaming functions
-// (e.g., ::operator<<(ostream&, string&): it turns out that it's
-// impossible to stream something like a string directly to an unnamed
-// ostream. We employ a neat hack by calling the stream() member
-// function of LogMessage which seems to avoid the problem.
-#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
-#define SYSLOG(severity) SYSLOG_ ## severity(0).stream()
-
-@ac_google_start_namespace@
-
-// They need the definitions of integer types.
-#include "glog/log_severity.h"
-#include "glog/vlog_is_on.h"
-
-// Initialize google's logging library. You will see the program name
-// specified by argv0 in log outputs.
-GOOGLE_GLOG_DLL_DECL void InitGoogleLogging(const char* argv0);
-
-// Shutdown google's logging library.
-GOOGLE_GLOG_DLL_DECL void ShutdownGoogleLogging();
-
-// Install a function which will be called after LOG(FATAL).
-GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)());
-
-// Enable/Disable old log cleaner.
-GOOGLE_GLOG_DLL_DECL void EnableLogCleaner(int overdue_days);
-GOOGLE_GLOG_DLL_DECL void DisableLogCleaner();
-
-
-class LogSink;  // defined below
-
-// If a non-NULL sink pointer is given, we push this message to that sink.
-// For LOG_TO_SINK we then do normal LOG(severity) logging as well.
-// This is useful for capturing messages and passing/storing them
-// somewhere more specific than the global log of the process.
-// Argument types:
-//   LogSink* sink;
-//   LogSeverity severity;
-// The cast is to disambiguate NULL arguments.
-#define LOG_TO_SINK(sink, severity) \
-  @ac_google_namespace@::LogMessage(                                    \
-      __FILE__, __LINE__,                                               \
-      @ac_google_namespace@::GLOG_ ## severity,                         \
-      static_cast<@ac_google_namespace@::LogSink*>(sink), true).stream()
-#define LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity)                  \
-  @ac_google_namespace@::LogMessage(                                    \
-      __FILE__, __LINE__,                                               \
-      @ac_google_namespace@::GLOG_ ## severity,                         \
-      static_cast<@ac_google_namespace@::LogSink*>(sink), false).stream()
-
-// If a non-NULL string pointer is given, we write this message to that string.
-// We then do normal LOG(severity) logging as well.
-// This is useful for capturing messages and storing them somewhere more
-// specific than the global log of the process.
-// Argument types:
-//   string* message;
-//   LogSeverity severity;
-// The cast is to disambiguate NULL arguments.
-// NOTE: LOG(severity) expands to LogMessage().stream() for the specified
-// severity.
-#define LOG_TO_STRING(severity, message) \
-  LOG_TO_STRING_##severity(static_cast<string*>(message)).stream()
-
-// If a non-NULL pointer is given, we push the message onto the end
-// of a vector of strings; otherwise, we report it with LOG(severity).
-// This is handy for capturing messages and perhaps passing them back
-// to the caller, rather than reporting them immediately.
-// Argument types:
-//   LogSeverity severity;
-//   vector<string> *outvec;
-// The cast is to disambiguate NULL arguments.
-#define LOG_STRING(severity, outvec) \
-  LOG_TO_STRING_##severity(static_cast<std::vector<std::string>*>(outvec)).stream()
-
-#define LOG_IF(severity, condition) \
-  static_cast<void>(0),             \
-  !(condition) ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity)
-#define SYSLOG_IF(severity, condition) \
-  static_cast<void>(0),                \
-  !(condition) ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & SYSLOG(severity)
-
-#define LOG_ASSERT(condition)  \
-  LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
-#define SYSLOG_ASSERT(condition) \
-  SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
-
-// CHECK dies with a fatal error if condition is not true.  It is *not*
-// controlled by DCHECK_IS_ON(), so the check will be executed regardless of
-// compilation mode.  Therefore, it is safe to do things like:
-//    CHECK(fp->Write(x) == 4)
-#define CHECK(condition)  \
-      LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \
-             << "Check failed: " #condition " "
-
-// A container for a string pointer which can be evaluated to a bool -
-// true iff the pointer is NULL.
-struct CheckOpString {
-  CheckOpString(std::string* str) : str_(str) { }
-  // No destructor: if str_ is non-NULL, we're about to LOG(FATAL),
-  // so there's no point in cleaning up str_.
-  operator bool() const {
-    return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != NULL);
-  }
-  std::string* str_;
-};
-
-// Function is overloaded for integral types to allow static const
-// integrals declared in classes and not defined to be used as arguments to
-// CHECK* macros. It's not encouraged though.
-template <class T>
-inline const T&       GetReferenceableValue(const T&           t) { return t; }
-inline char           GetReferenceableValue(char               t) { return t; }
-inline unsigned char  GetReferenceableValue(unsigned char      t) { return t; }
-inline signed char    GetReferenceableValue(signed char        t) { return t; }
-inline short          GetReferenceableValue(short              t) { return t; }
-inline unsigned short GetReferenceableValue(unsigned short     t) { return t; }
-inline int            GetReferenceableValue(int                t) { return t; }
-inline unsigned int   GetReferenceableValue(unsigned int       t) { return t; }
-inline long           GetReferenceableValue(long               t) { return t; }
-inline unsigned long  GetReferenceableValue(unsigned long      t) { return t; }
-inline long long      GetReferenceableValue(long long          t) { return t; }
-inline unsigned long long GetReferenceableValue(unsigned long long t) {
-  return t;
-}
-
-// This is a dummy class to define the following operator.
-struct DummyClassToDefineOperator {};
-
-@ac_google_end_namespace@
-
-// Define global operator<< to declare using ::operator<<.
-// This declaration will allow use to use CHECK macros for user
-// defined classes which have operator<< (e.g., stl_logging.h).
-inline std::ostream& operator<<(
-    std::ostream& out, const google::DummyClassToDefineOperator&) {
-  return out;
-}
-
-@ac_google_start_namespace@
-
-// This formats a value for a failing CHECK_XX statement.  Ordinarily,
-// it uses the definition for operator<<, with a few special cases below.
-template <typename T>
-inline void MakeCheckOpValueString(std::ostream* os, const T& v) {
-  (*os) << v;
-}
-
-// Overrides for char types provide readable values for unprintable
-// characters.
-template <> GOOGLE_GLOG_DLL_DECL
-void MakeCheckOpValueString(std::ostream* os, const char& v);
-template <> GOOGLE_GLOG_DLL_DECL
-void MakeCheckOpValueString(std::ostream* os, const signed char& v);
-template <> GOOGLE_GLOG_DLL_DECL
-void MakeCheckOpValueString(std::ostream* os, const unsigned char& v);
-
-// Build the error message string. Specify no inlining for code size.
-template <typename T1, typename T2>
-std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext)
-    @ac_cv___attribute___noinline@;
-
-namespace base {
-namespace internal {
-
-// If "s" is less than base_logging::INFO, returns base_logging::INFO.
-// If "s" is greater than base_logging::FATAL, returns
-// base_logging::ERROR.  Otherwise, returns "s".
-LogSeverity NormalizeSeverity(LogSeverity s);
-
-}  // namespace internal
-
-// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX
-// statement.  See MakeCheckOpString for sample usage.  Other
-// approaches were considered: use of a template method (e.g.,
-// base::BuildCheckOpString(exprtext, base::Print<T1>, &v1,
-// base::Print<T2>, &v2), however this approach has complications
-// related to volatile arguments and function-pointer arguments).
-class GOOGLE_GLOG_DLL_DECL CheckOpMessageBuilder {
- public:
-  // Inserts "exprtext" and " (" to the stream.
-  explicit CheckOpMessageBuilder(const char *exprtext);
-  // Deletes "stream_".
-  ~CheckOpMessageBuilder();
-  // For inserting the first variable.
-  std::ostream* ForVar1() { return stream_; }
-  // For inserting the second variable (adds an intermediate " vs. ").
-  std::ostream* ForVar2();
-  // Get the result (inserts the closing ")").
-  std::string* NewString();
-
- private:
-  std::ostringstream *stream_;
-};
-
-}  // namespace base
-
-template <typename T1, typename T2>
-std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) {
-  base::CheckOpMessageBuilder comb(exprtext);
-  MakeCheckOpValueString(comb.ForVar1(), v1);
-  MakeCheckOpValueString(comb.ForVar2(), v2);
-  return comb.NewString();
-}
-
-// Helper functions for CHECK_OP macro.
-// The (int, int) specialization works around the issue that the compiler
-// will not instantiate the template version of the function on values of
-// unnamed enum type - see comment below.
-#define DEFINE_CHECK_OP_IMPL(name, op) \
-  template <typename T1, typename T2> \
-  inline std::string* name##Impl(const T1& v1, const T2& v2,    \
-                            const char* exprtext) { \
-    if (GOOGLE_PREDICT_TRUE(v1 op v2)) return NULL; \
-    else return MakeCheckOpString(v1, v2, exprtext); \
-  } \
-  inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \
-    return name##Impl<int, int>(v1, v2, exprtext); \
-  }
-
-// We use the full name Check_EQ, Check_NE, etc. in case the file including
-// base/logging.h provides its own #defines for the simpler names EQ, NE, etc.
-// This happens if, for example, those are used as token names in a
-// yacc grammar.
-DEFINE_CHECK_OP_IMPL(Check_EQ, ==)  // Compilation error with CHECK_EQ(NULL, x)?
-DEFINE_CHECK_OP_IMPL(Check_NE, !=)  // Use CHECK(x == NULL) instead.
-DEFINE_CHECK_OP_IMPL(Check_LE, <=)
-DEFINE_CHECK_OP_IMPL(Check_LT, < )
-DEFINE_CHECK_OP_IMPL(Check_GE, >=)
-DEFINE_CHECK_OP_IMPL(Check_GT, > )
-#undef DEFINE_CHECK_OP_IMPL
-
-// Helper macro for binary operators.
-// Don't use this macro directly in your code, use CHECK_EQ et al below.
-
-#if defined(STATIC_ANALYSIS)
-// Only for static analysis tool to know that it is equivalent to assert
-#define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2))
-#elif DCHECK_IS_ON()
-// In debug mode, avoid constructing CheckOpStrings if possible,
-// to reduce the overhead of CHECK statments by 2x.
-// Real DCHECK-heavy tests have seen 1.5x speedups.
-
-// The meaning of "string" might be different between now and
-// when this macro gets invoked (e.g., if someone is experimenting
-// with other string implementations that get defined after this
-// file is included).  Save the current meaning now and use it
-// in the macro.
-typedef std::string _Check_string;
-#define CHECK_OP_LOG(name, op, val1, val2, log)                         \
-  while (@ac_google_namespace@::_Check_string* _result =                \
-         @ac_google_namespace@::Check##name##Impl(                      \
-             @ac_google_namespace@::GetReferenceableValue(val1),        \
-             @ac_google_namespace@::GetReferenceableValue(val2),        \
-             #val1 " " #op " " #val2))                                  \
-    log(__FILE__, __LINE__,                                             \
-        @ac_google_namespace@::CheckOpString(_result)).stream()
-#else
-// In optimized mode, use CheckOpString to hint to compiler that
-// the while condition is unlikely.
-#define CHECK_OP_LOG(name, op, val1, val2, log)                         \
-  while (@ac_google_namespace@::CheckOpString _result =                 \
-         @ac_google_namespace@::Check##name##Impl(                      \
-             @ac_google_namespace@::GetReferenceableValue(val1),        \
-             @ac_google_namespace@::GetReferenceableValue(val2),        \
-             #val1 " " #op " " #val2))                                  \
-    log(__FILE__, __LINE__, _result).stream()
-#endif  // STATIC_ANALYSIS, DCHECK_IS_ON()
-
-#if GOOGLE_STRIP_LOG <= 3
-#define CHECK_OP(name, op, val1, val2) \
-  CHECK_OP_LOG(name, op, val1, val2, @ac_google_namespace@::LogMessageFatal)
-#else
-#define CHECK_OP(name, op, val1, val2) \
-  CHECK_OP_LOG(name, op, val1, val2, @ac_google_namespace@::NullStreamFatal)
-#endif // STRIP_LOG <= 3
-
-// Equality/Inequality checks - compare two values, and log a FATAL message
-// including the two values when the result is not as expected.  The values
-// must have operator<<(ostream, ...) defined.
-//
-// You may append to the error message like so:
-//   CHECK_NE(1, 2) << ": The world must be ending!";
-//
-// We are very careful to ensure that each argument is evaluated exactly
-// once, and that anything which is legal to pass as a function argument is
-// legal here.  In particular, the arguments may be temporary expressions
-// which will end up being destroyed at the end of the apparent statement,
-// for example:
-//   CHECK_EQ(string("abc")[1], 'b');
-//
-// WARNING: These don't compile correctly if one of the arguments is a pointer
-// and the other is NULL. To work around this, simply static_cast NULL to the
-// type of the desired pointer.
-
-#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2)
-#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2)
-#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2)
-#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2)
-#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2)
-#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2)
-
-// Check that the input is non NULL.  This very useful in constructor
-// initializer lists.
-
-#define CHECK_NOTNULL(val) \
-  @ac_google_namespace@::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val))
-
-// Helper functions for string comparisons.
-// To avoid bloat, the definitions are in logging.cc.
-#define DECLARE_CHECK_STROP_IMPL(func, expected) \
-  GOOGLE_GLOG_DLL_DECL std::string* Check##func##expected##Impl( \
-      const char* s1, const char* s2, const char* names);
-DECLARE_CHECK_STROP_IMPL(strcmp, true)
-DECLARE_CHECK_STROP_IMPL(strcmp, false)
-DECLARE_CHECK_STROP_IMPL(strcasecmp, true)
-DECLARE_CHECK_STROP_IMPL(strcasecmp, false)
-#undef DECLARE_CHECK_STROP_IMPL
-
-// Helper macro for string comparisons.
-// Don't use this macro directly in your code, use CHECK_STREQ et al below.
-#define CHECK_STROP(func, op, expected, s1, s2) \
-  while (@ac_google_namespace@::CheckOpString _result = \
-         @ac_google_namespace@::Check##func##expected##Impl((s1), (s2), \
-                                     #s1 " " #op " " #s2)) \
-    LOG(FATAL) << *_result.str_
-
-
-// String (char*) equality/inequality checks.
-// CASE versions are case-insensitive.
-//
-// Note that "s1" and "s2" may be temporary strings which are destroyed
-// by the compiler at the end of the current "full expression"
-// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())).
-
-#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2)
-#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2)
-#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(strcasecmp, ==, true, s1, s2)
-#define CHECK_STRCASENE(s1, s2) CHECK_STROP(strcasecmp, !=, false, s1, s2)
-
-#define CHECK_INDEX(I,A) CHECK(I < (sizeof(A)/sizeof(A[0])))
-#define CHECK_BOUND(B,A) CHECK(B <= (sizeof(A)/sizeof(A[0])))
-
-#define CHECK_DOUBLE_EQ(val1, val2)              \
-  do {                                           \
-    CHECK_LE((val1), (val2)+0.000000000000001L); \
-    CHECK_GE((val1), (val2)-0.000000000000001L); \
-  } while (0)
-
-#define CHECK_NEAR(val1, val2, margin)           \
-  do {                                           \
-    CHECK_LE((val1), (val2)+(margin));           \
-    CHECK_GE((val1), (val2)-(margin));           \
-  } while (0)
-
-// perror()..googly style!
-//
-// PLOG() and PLOG_IF() and PCHECK() behave exactly like their LOG* and
-// CHECK equivalents with the addition that they postpend a description
-// of the current state of errno to their output lines.
-
-#define PLOG(severity) GOOGLE_PLOG(severity, 0).stream()
-
-#define GOOGLE_PLOG(severity, counter)  \
-  @ac_google_namespace@::ErrnoLogMessage( \
-      __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, counter, \
-      &@ac_google_namespace@::LogMessage::SendToLog)
-
-#define PLOG_IF(severity, condition) \
-  static_cast<void>(0),              \
-  !(condition) ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & PLOG(severity)
-
-// A CHECK() macro that postpends errno if the condition is false. E.g.
-//
-// if (poll(fds, nfds, timeout) == -1) { PCHECK(errno == EINTR); ... }
-#define PCHECK(condition)  \
-      PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \
-              << "Check failed: " #condition " "
-
-// A CHECK() macro that lets you assert the success of a function that
-// returns -1 and sets errno in case of an error. E.g.
-//
-// CHECK_ERR(mkdir(path, 0700));
-//
-// or
-//
-// int fd = open(filename, flags); CHECK_ERR(fd) << ": open " << filename;
-#define CHECK_ERR(invocation)                                          \
-PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN((invocation) == -1))    \
-        << #invocation
-
-// Use macro expansion to create, for each use of LOG_EVERY_N(), static
-// variables with the __LINE__ expansion as part of the variable name.
-#define LOG_EVERY_N_VARNAME(base, line) LOG_EVERY_N_VARNAME_CONCAT(base, line)
-#define LOG_EVERY_N_VARNAME_CONCAT(base, line) base ## line
-
-#define LOG_OCCURRENCES LOG_EVERY_N_VARNAME(occurrences_, __LINE__)
-#define LOG_OCCURRENCES_MOD_N LOG_EVERY_N_VARNAME(occurrences_mod_n_, __LINE__)
-
-#define SOME_KIND_OF_LOG_EVERY_N(severity, n, what_to_do) \
-  static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \
-  ++LOG_OCCURRENCES; \
-  if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \
-  if (LOG_OCCURRENCES_MOD_N == 1) \
-    @ac_google_namespace@::LogMessage( \
-        __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, LOG_OCCURRENCES, \
-        &what_to_do).stream()
-
-#define SOME_KIND_OF_LOG_IF_EVERY_N(severity, condition, n, what_to_do) \
-  static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \
-  ++LOG_OCCURRENCES; \
-  if (condition && \
-      ((LOG_OCCURRENCES_MOD_N=(LOG_OCCURRENCES_MOD_N + 1) % n) == (1 % n))) \
-    @ac_google_namespace@::LogMessage( \
-        __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, LOG_OCCURRENCES, \
-                 &what_to_do).stream()
-
-#define SOME_KIND_OF_PLOG_EVERY_N(severity, n, what_to_do) \
-  static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \
-  ++LOG_OCCURRENCES; \
-  if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \
-  if (LOG_OCCURRENCES_MOD_N == 1) \
-    @ac_google_namespace@::ErrnoLogMessage( \
-        __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, LOG_OCCURRENCES, \
-        &what_to_do).stream()
-
-#define SOME_KIND_OF_LOG_FIRST_N(severity, n, what_to_do) \
-  static int LOG_OCCURRENCES = 0; \
-  if (LOG_OCCURRENCES <= n) \
-    ++LOG_OCCURRENCES; \
-  if (LOG_OCCURRENCES <= n) \
-    @ac_google_namespace@::LogMessage( \
-        __FILE__, __LINE__, @ac_google_namespace@::GLOG_ ## severity, LOG_OCCURRENCES, \
-        &what_to_do).stream()
-
-namespace glog_internal_namespace_ {
-template <bool>
-struct CompileAssert {
-};
-struct CrashReason;
-
-// Returns true if FailureSignalHandler is installed.
-// Needs to be exported since it's used by the signalhandler_unittest.
-GOOGLE_GLOG_DLL_DECL bool IsFailureSignalHandlerInstalled();
-}  // namespace glog_internal_namespace_
-
-#define LOG_EVERY_N(severity, n)                                        \
-  SOME_KIND_OF_LOG_EVERY_N(severity, (n), @ac_google_namespace@::LogMessage::SendToLog)
-
-#define SYSLOG_EVERY_N(severity, n) \
-  SOME_KIND_OF_LOG_EVERY_N(severity, (n), @ac_google_namespace@::LogMessage::SendToSyslogAndLog)
-
-#define PLOG_EVERY_N(severity, n) \
-  SOME_KIND_OF_PLOG_EVERY_N(severity, (n), @ac_google_namespace@::LogMessage::SendToLog)
-
-#define LOG_FIRST_N(severity, n) \
-  SOME_KIND_OF_LOG_FIRST_N(severity, (n), @ac_google_namespace@::LogMessage::SendToLog)
-
-#define LOG_IF_EVERY_N(severity, condition, n) \
-  SOME_KIND_OF_LOG_IF_EVERY_N(severity, (condition), (n), @ac_google_namespace@::LogMessage::SendToLog)
-
-// We want the special COUNTER value available for LOG_EVERY_X()'ed messages
-enum PRIVATE_Counter {COUNTER};
-
-#ifdef GLOG_NO_ABBREVIATED_SEVERITIES
-// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
-// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
-// to keep using this syntax, we define this macro to do the same thing
-// as COMPACT_GOOGLE_LOG_ERROR.
-#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR
-#define SYSLOG_0 SYSLOG_ERROR
-#define LOG_TO_STRING_0 LOG_TO_STRING_ERROR
-// Needed for LOG_IS_ON(ERROR).
-const LogSeverity GLOG_0 = GLOG_ERROR;
-#else
-// Users may include windows.h after logging.h without
-// GLOG_NO_ABBREVIATED_SEVERITIES nor WIN32_LEAN_AND_MEAN.
-// For this case, we cannot detect if ERROR is defined before users
-// actually use ERROR. Let's make an undefined symbol to warn users.
-# define GLOG_ERROR_MSG ERROR_macro_is_defined_Define_GLOG_NO_ABBREVIATED_SEVERITIES_before_including_logging_h_See_the_document_for_detail
-# define COMPACT_GOOGLE_LOG_0 GLOG_ERROR_MSG
-# define SYSLOG_0 GLOG_ERROR_MSG
-# define LOG_TO_STRING_0 GLOG_ERROR_MSG
-# define GLOG_0 GLOG_ERROR_MSG
-#endif
-
-// Plus some debug-logging macros that get compiled to nothing for production
-
-#if DCHECK_IS_ON()
-
-#define DLOG(severity) LOG(severity)
-#define DVLOG(verboselevel) VLOG(verboselevel)
-#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
-#define DLOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n)
-#define DLOG_IF_EVERY_N(severity, condition, n) \
-  LOG_IF_EVERY_N(severity, condition, n)
-#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
-
-// debug-only checking.  executed if DCHECK_IS_ON().
-#define DCHECK(condition) CHECK(condition)
-#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2)
-#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2)
-#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2)
-#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2)
-#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2)
-#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2)
-#define DCHECK_NOTNULL(val) CHECK_NOTNULL(val)
-#define DCHECK_STREQ(str1, str2) CHECK_STREQ(str1, str2)
-#define DCHECK_STRCASEEQ(str1, str2) CHECK_STRCASEEQ(str1, str2)
-#define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2)
-#define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2)
-
-#else  // !DCHECK_IS_ON()
-
-#define DLOG(severity)  \
-  static_cast<void>(0), \
-  true ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity)
-
-#define DVLOG(verboselevel)             \
-  static_cast<void>(0),                 \
-  (true || !VLOG_IS_ON(verboselevel)) ? \
-      (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(INFO)
-
-#define DLOG_IF(severity, condition) \
-  static_cast<void>(0),              \
-  (true || !(condition)) ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity)
-
-#define DLOG_EVERY_N(severity, n) \
-  static_cast<void>(0),           \
-  true ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity)
-
-#define DLOG_IF_EVERY_N(severity, condition, n) \
-  static_cast<void>(0),                         \
-  (true || !(condition))? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity)
-
-#define DLOG_ASSERT(condition) \
-  static_cast<void>(0),        \
-  true ? (void) 0 : LOG_ASSERT(condition)
-
-// MSVC warning C4127: conditional expression is constant
-#define DCHECK(condition) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK(condition)
-
-#define DCHECK_EQ(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_EQ(val1, val2)
-
-#define DCHECK_NE(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_NE(val1, val2)
-
-#define DCHECK_LE(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_LE(val1, val2)
-
-#define DCHECK_LT(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_LT(val1, val2)
-
-#define DCHECK_GE(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_GE(val1, val2)
-
-#define DCHECK_GT(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_GT(val1, val2)
-
-// You may see warnings in release mode if you don't use the return
-// value of DCHECK_NOTNULL. Please just use DCHECK for such cases.
-#define DCHECK_NOTNULL(val) (val)
-
-#define DCHECK_STREQ(str1, str2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_STREQ(str1, str2)
-
-#define DCHECK_STRCASEEQ(str1, str2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_STRCASEEQ(str1, str2)
-
-#define DCHECK_STRNE(str1, str2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_STRNE(str1, str2)
-
-#define DCHECK_STRCASENE(str1, str2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_STRCASENE(str1, str2)
-
-#endif  // DCHECK_IS_ON()
-
-// Log only in verbose mode.
-
-#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel))
-
-#define VLOG_IF(verboselevel, condition) \
-  LOG_IF(INFO, (condition) && VLOG_IS_ON(verboselevel))
-
-#define VLOG_EVERY_N(verboselevel, n) \
-  LOG_IF_EVERY_N(INFO, VLOG_IS_ON(verboselevel), n)
-
-#define VLOG_IF_EVERY_N(verboselevel, condition, n) \
-  LOG_IF_EVERY_N(INFO, (condition) && VLOG_IS_ON(verboselevel), n)
-
-namespace base_logging {
-
-// LogMessage::LogStream is a std::ostream backed by this streambuf.
-// This class ignores overflow and leaves two bytes at the end of the
-// buffer to allow for a '\n' and '\0'.
-class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf {
- public:
-  // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\0'.
-  LogStreamBuf(char *buf, int len) {
-    setp(buf, buf + len - 2);
-  }
-
-  // This effectively ignores overflow.
-  virtual int_type overflow(int_type ch) {
-    return ch;
-  }
-
-  // Legacy public ostrstream method.
-  size_t pcount() const { return pptr() - pbase(); }
-  char* pbase() const { return std::streambuf::pbase(); }
-};
-
-}  // namespace base_logging
-
-//
-// This class more or less represents a particular log message.  You
-// create an instance of LogMessage and then stream stuff to it.
-// When you finish streaming to it, ~LogMessage is called and the
-// full message gets streamed to the appropriate destination.
-//
-// You shouldn't actually use LogMessage's constructor to log things,
-// though.  You should use the LOG() macro (and variants thereof)
-// above.
-class GOOGLE_GLOG_DLL_DECL LogMessage {
-public:
-  enum {
-    // Passing kNoLogPrefix for the line number disables the
-    // log-message prefix. Useful for using the LogMessage
-    // infrastructure as a printing utility. See also the --log_prefix
-    // flag for controlling the log-message prefix on an
-    // application-wide basis.
-    kNoLogPrefix = -1
-  };
-
-  // LogStream inherit from non-DLL-exported class (std::ostrstream)
-  // and VC++ produces a warning for this situation.
-  // However, MSDN says "C4275 can be ignored in Microsoft Visual C++
-  // 2005 if you are deriving from a type in the Standard C++ Library"
-  // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
-  // Let's just ignore the warning.
-GLOG_MSVC_PUSH_DISABLE_WARNING(4275)
-  class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream {
-GLOG_MSVC_POP_WARNING()
-  public:
-    LogStream(char *buf, int len, int ctr)
-        : std::ostream(NULL),
-          streambuf_(buf, len),
-          ctr_(ctr),
-          self_(this) {
-      rdbuf(&streambuf_);
-    }
-
-    int ctr() const { return ctr_; }
-    void set_ctr(int ctr) { ctr_ = ctr; }
-    LogStream* self() const { return self_; }
-
-    // Legacy std::streambuf methods.
-    size_t pcount() const { return streambuf_.pcount(); }
-    char* pbase() const { return streambuf_.pbase(); }
-    char* str() const { return pbase(); }
-
-  private:
-    LogStream(const LogStream&);
-    LogStream& operator=(const LogStream&);
-    base_logging::LogStreamBuf streambuf_;
-    int ctr_;  // Counter hack (for the LOG_EVERY_X() macro)
-    LogStream *self_;  // Consistency check hack
-  };
-
-public:
-  // icc 8 requires this typedef to avoid an internal compiler error.
-  typedef void (LogMessage::*SendMethod)();
-
-  LogMessage(const char* file, int line, LogSeverity severity, int ctr,
-             SendMethod send_method);
-
-  // Two special constructors that generate reduced amounts of code at
-  // LOG call sites for common cases.
-
-  // Used for LOG(INFO): Implied are:
-  // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog.
-  //
-  // Using this constructor instead of the more complex constructor above
-  // saves 19 bytes per call site.
-  LogMessage(const char* file, int line);
-
-  // Used for LOG(severity) where severity != INFO.  Implied
-  // are: ctr = 0, send_method = &LogMessage::SendToLog
-  //
-  // Using this constructor instead of the more complex constructor above
-  // saves 17 bytes per call site.
-  LogMessage(const char* file, int line, LogSeverity severity);
-
-  // Constructor to log this message to a specified sink (if not NULL).
-  // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if
-  // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise.
-  LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink,
-             bool also_send_to_log);
-
-  // Constructor where we also give a vector<string> pointer
-  // for storing the messages (if the pointer is not NULL).
-  // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog.
-  LogMessage(const char* file, int line, LogSeverity severity,
-             std::vector<std::string>* outvec);
-
-  // Constructor where we also give a string pointer for storing the
-  // message (if the pointer is not NULL).  Implied are: ctr = 0,
-  // send_method = &LogMessage::WriteToStringAndLog.
-  LogMessage(const char* file, int line, LogSeverity severity,
-             std::string* message);
-
-  // A special constructor used for check failures
-  LogMessage(const char* file, int line, const CheckOpString& result);
-
-  ~LogMessage();
-
-  // Flush a buffered message to the sink set in the constructor.  Always
-  // called by the destructor, it may also be called from elsewhere if
-  // needed.  Only the first call is actioned; any later ones are ignored.
-  void Flush();
-
-  // An arbitrary limit on the length of a single log message.  This
-  // is so that streaming can be done more efficiently.
-  static const size_t kMaxLogMessageLen;
-
-  // Theses should not be called directly outside of logging.*,
-  // only passed as SendMethod arguments to other LogMessage methods:
-  void SendToLog();  // Actually dispatch to the logs
-  void SendToSyslogAndLog();  // Actually dispatch to syslog and the logs
-
-  // Call abort() or similar to perform LOG(FATAL) crash.
-  static void @ac_cv___attribute___noreturn@ Fail();
-
-  std::ostream& stream();
-
-  int preserved_errno() const;
-
-  // Must be called without the log_mutex held.  (L < log_mutex)
-  static int64 num_messages(int severity);
-
-  struct LogMessageData;
-
-private:
-  // Fully internal SendMethod cases:
-  void SendToSinkAndLog();  // Send to sink if provided and dispatch to the logs
-  void SendToSink();  // Send to sink if provided, do nothing otherwise.
-
-  // Write to string if provided and dispatch to the logs.
-  void WriteToStringAndLog();
-
-  void SaveOrSendToLog();  // Save to stringvec if provided, else to logs
-
-  void Init(const char* file, int line, LogSeverity severity,
-            void (LogMessage::*send_method)());
-
-  // Used to fill in crash information during LOG(FATAL) failures.
-  void RecordCrashReason(glog_internal_namespace_::CrashReason* reason);
-
-  // Counts of messages sent at each priority:
-  static int64 num_messages_[NUM_SEVERITIES];  // under log_mutex
-
-  // We keep the data in a separate struct so that each instance of
-  // LogMessage uses less stack space.
-  LogMessageData* allocated_;
-  LogMessageData* data_;
-
-  friend class LogDestination;
-
-  LogMessage(const LogMessage&);
-  void operator=(const LogMessage&);
-};
-
-// This class happens to be thread-hostile because all instances share
-// a single data buffer, but since it can only be created just before
-// the process dies, we don't worry so much.
-class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage {
- public:
-  LogMessageFatal(const char* file, int line);
-  LogMessageFatal(const char* file, int line, const CheckOpString& result);
-  @ac_cv___attribute___noreturn@ ~LogMessageFatal();
-};
-
-// A non-macro interface to the log facility; (useful
-// when the logging level is not a compile-time constant).
-inline void LogAtLevel(int const severity, std::string const &msg) {
-  LogMessage(__FILE__, __LINE__, severity).stream() << msg;
-}
-
-// A macro alternative of LogAtLevel. New code may want to use this
-// version since there are two advantages: 1. this version outputs the
-// file name and the line number where this macro is put like other
-// LOG macros, 2. this macro can be used as C++ stream.
-#define LOG_AT_LEVEL(severity) @ac_google_namespace@::LogMessage(__FILE__, __LINE__, severity).stream()
-
-// Check if it's compiled in C++11 mode.
-//
-// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least
-// gcc-4.7 and clang-3.1 (2011-12-13).  __cplusplus was defined to 1
-// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is
-// defined according to the language version in effect thereafter.
-// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite
-// reasonably good C++11 support, so we set LANG_CXX for it and
-// newer versions (_MSC_VER >= 1900).
-#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \
-     (defined(_MSC_VER) && _MSC_VER >= 1900)) && !defined(__UCLIBCXX_MAJOR__)
-// Helper for CHECK_NOTNULL().
-//
-// In C++11, all cases can be handled by a single function. Since the value
-// category of the argument is preserved (also for rvalue references),
-// member initializer lists like the one below will compile correctly:
-//
-//   Foo()
-//     : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {}
-template <typename T>
-T CheckNotNull(const char* file, int line, const char* names, T&& t) {
- if (t == nullptr) {
-   LogMessageFatal(file, line, new std::string(names));
- }
- return std::forward<T>(t);
-}
-
-#else
-
-// A small helper for CHECK_NOTNULL().
-template <typename T>
-T* CheckNotNull(const char *file, int line, const char *names, T* t) {
-  if (t == NULL) {
-    LogMessageFatal(file, line, new std::string(names));
-  }
-  return t;
-}
-#endif
-
-// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This
-// only works if ostream is a LogStream. If the ostream is not a
-// LogStream you'll get an assert saying as much at runtime.
-GOOGLE_GLOG_DLL_DECL std::ostream& operator<<(std::ostream &os,
-                                              const PRIVATE_Counter&);
-
-
-// Derived class for PLOG*() above.
-class GOOGLE_GLOG_DLL_DECL ErrnoLogMessage : public LogMessage {
- public:
-
-  ErrnoLogMessage(const char* file, int line, LogSeverity severity, int ctr,
-                  void (LogMessage::*send_method)());
-
-  // Postpends ": strerror(errno) [errno]".
-  ~ErrnoLogMessage();
-
- private:
-  ErrnoLogMessage(const ErrnoLogMessage&);
-  void operator=(const ErrnoLogMessage&);
-};
-
-
-// This class is used to explicitly ignore values in the conditional
-// logging macros.  This avoids compiler warnings like "value computed
-// is not used" and "statement has no effect".
-
-class GOOGLE_GLOG_DLL_DECL LogMessageVoidify {
- public:
-  LogMessageVoidify() { }
-  // This has to be an operator with a precedence lower than << but
-  // higher than ?:
-  void operator&(std::ostream&) { }
-};
-
-
-// Flushes all log files that contains messages that are at least of
-// the specified severity level.  Thread-safe.
-GOOGLE_GLOG_DLL_DECL void FlushLogFiles(LogSeverity min_severity);
-
-// Flushes all log files that contains messages that are at least of
-// the specified severity level. Thread-hostile because it ignores
-// locking -- used for catastrophic failures.
-GOOGLE_GLOG_DLL_DECL void FlushLogFilesUnsafe(LogSeverity min_severity);
-
-//
-// Set the destination to which a particular severity level of log
-// messages is sent.  If base_filename is "", it means "don't log this
-// severity".  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetLogDestination(LogSeverity severity,
-                                            const char* base_filename);
-
-//
-// Set the basename of the symlink to the latest log file at a given
-// severity.  If symlink_basename is empty, do not make a symlink.  If
-// you don't call this function, the symlink basename is the
-// invocation name of the program.  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetLogSymlink(LogSeverity severity,
-                                        const char* symlink_basename);
-
-//
-// Used to send logs to some other kind of destination
-// Users should subclass LogSink and override send to do whatever they want.
-// Implementations must be thread-safe because a shared instance will
-// be called from whichever thread ran the LOG(XXX) line.
-class GOOGLE_GLOG_DLL_DECL LogSink {
- public:
-  virtual ~LogSink();
-
-  // Sink's logging logic (message_len is such as to exclude '\n' at the end).
-  // This method can't use LOG() or CHECK() as logging system mutex(s) are held
-  // during this call.
-  virtual void send(LogSeverity severity, const char* full_filename,
-                    const char* base_filename, int line,
-                    const struct ::tm* tm_time,
-                    const char* message, size_t message_len, int32 usecs) {
-    send(severity, full_filename, base_filename, line,
-         tm_time, message, message_len);
-  }
-  // This send() signature is obsolete.
-  // New implementations should define this in terms of
-  // the above send() method.
-  virtual void send(LogSeverity severity, const char* full_filename,
-                    const char* base_filename, int line,
-                    const struct ::tm* tm_time,
-                    const char* message, size_t message_len) = 0;
-
-  // Redefine this to implement waiting for
-  // the sink's logging logic to complete.
-  // It will be called after each send() returns,
-  // but before that LogMessage exits or crashes.
-  // By default this function does nothing.
-  // Using this function one can implement complex logic for send()
-  // that itself involves logging; and do all this w/o causing deadlocks and
-  // inconsistent rearrangement of log messages.
-  // E.g. if a LogSink has thread-specific actions, the send() method
-  // can simply add the message to a queue and wake up another thread that
-  // handles real logging while itself making some LOG() calls;
-  // WaitTillSent() can be implemented to wait for that logic to complete.
-  // See our unittest for an example.
-  virtual void WaitTillSent();
-
-  // Returns the normal text output of the log message.
-  // Can be useful to implement send().
-  static std::string ToString(LogSeverity severity, const char* file, int line,
-                              const struct ::tm* tm_time,
-                              const char* message, size_t message_len,
-                              int32 usecs);
-
-  // Obsolete
-  static std::string ToString(LogSeverity severity, const char* file, int line,
-                              const struct ::tm* tm_time,
-                              const char* message, size_t message_len) {
-    return ToString(severity, file, line, tm_time, message, message_len, 0);
-  }
-};
-
-// Add or remove a LogSink as a consumer of logging data.  Thread-safe.
-GOOGLE_GLOG_DLL_DECL void AddLogSink(LogSink *destination);
-GOOGLE_GLOG_DLL_DECL void RemoveLogSink(LogSink *destination);
-
-//
-// Specify an "extension" added to the filename specified via
-// SetLogDestination.  This applies to all severity levels.  It's
-// often used to append the port we're listening on to the logfile
-// name.  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetLogFilenameExtension(
-    const char* filename_extension);
-
-//
-// Make it so that all log messages of at least a particular severity
-// are logged to stderr (in addition to logging to the usual log
-// file(s)).  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetStderrLogging(LogSeverity min_severity);
-
-//
-// Make it so that all log messages go only to stderr.  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void LogToStderr();
-
-//
-// Make it so that all log messages of at least a particular severity are
-// logged via email to a list of addresses (in addition to logging to the
-// usual log file(s)).  The list of addresses is just a string containing
-// the email addresses to send to (separated by spaces, say).  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetEmailLogging(LogSeverity min_severity,
-                                          const char* addresses);
-
-// A simple function that sends email. dest is a commma-separated
-// list of addressess.  Thread-safe.
-GOOGLE_GLOG_DLL_DECL bool SendEmail(const char *dest,
-                                    const char *subject, const char *body);
-
-GOOGLE_GLOG_DLL_DECL const std::vector<std::string>& GetLoggingDirectories();
-
-// For tests only:  Clear the internal [cached] list of logging directories to
-// force a refresh the next time GetLoggingDirectories is called.
-// Thread-hostile.
-void TestOnly_ClearLoggingDirectoriesList();
-
-// Returns a set of existing temporary directories, which will be a
-// subset of the directories returned by GetLogginDirectories().
-// Thread-safe.
-GOOGLE_GLOG_DLL_DECL void GetExistingTempDirectories(
-    std::vector<std::string>* list);
-
-// Print any fatal message again -- useful to call from signal handler
-// so that the last thing in the output is the fatal message.
-// Thread-hostile, but a race is unlikely.
-GOOGLE_GLOG_DLL_DECL void ReprintFatalMessage();
-
-// Truncate a log file that may be the append-only output of multiple
-// processes and hence can't simply be renamed/reopened (typically a
-// stdout/stderr).  If the file "path" is > "limit" bytes, copy the
-// last "keep" bytes to offset 0 and truncate the rest. Since we could
-// be racing with other writers, this approach has the potential to
-// lose very small amounts of data. For security, only follow symlinks
-// if the path is /proc/self/fd/*
-GOOGLE_GLOG_DLL_DECL void TruncateLogFile(const char *path,
-                                          int64 limit, int64 keep);
-
-// Truncate stdout and stderr if they are over the value specified by
-// --max_log_size; keep the final 1MB.  This function has the same
-// race condition as TruncateLogFile.
-GOOGLE_GLOG_DLL_DECL void TruncateStdoutStderr();
-
-// Return the string representation of the provided LogSeverity level.
-// Thread-safe.
-GOOGLE_GLOG_DLL_DECL const char* GetLogSeverityName(LogSeverity severity);
-
-// ---------------------------------------------------------------------
-// Implementation details that are not useful to most clients
-// ---------------------------------------------------------------------
-
-// A Logger is the interface used by logging modules to emit entries
-// to a log.  A typical implementation will dump formatted data to a
-// sequence of files.  We also provide interfaces that will forward
-// the data to another thread so that the invoker never blocks.
-// Implementations should be thread-safe since the logging system
-// will write to them from multiple threads.
-
-namespace base {
-
-class GOOGLE_GLOG_DLL_DECL Logger {
- public:
-  virtual ~Logger();
-
-  // Writes "message[0,message_len-1]" corresponding to an event that
-  // occurred at "timestamp".  If "force_flush" is true, the log file
-  // is flushed immediately.
-  //
-  // The input message has already been formatted as deemed
-  // appropriate by the higher level logging facility.  For example,
-  // textual log messages already contain timestamps, and the
-  // file:linenumber header.
-  virtual void Write(bool force_flush,
-                     time_t timestamp,
-                     const char* message,
-                     int message_len) = 0;
-
-  // Flush any buffered messages
-  virtual void Flush() = 0;
-
-  // Get the current LOG file size.
-  // The returned value is approximate since some
-  // logged data may not have been flushed to disk yet.
-  virtual uint32 LogSize() = 0;
-};
-
-// Get the logger for the specified severity level.  The logger
-// remains the property of the logging module and should not be
-// deleted by the caller.  Thread-safe.
-extern GOOGLE_GLOG_DLL_DECL Logger* GetLogger(LogSeverity level);
-
-// Set the logger for the specified severity level.  The logger
-// becomes the property of the logging module and should not
-// be deleted by the caller.  Thread-safe.
-extern GOOGLE_GLOG_DLL_DECL void SetLogger(LogSeverity level, Logger* logger);
-
-}
-
-// glibc has traditionally implemented two incompatible versions of
-// strerror_r(). There is a poorly defined convention for picking the
-// version that we want, but it is not clear whether it even works with
-// all versions of glibc.
-// So, instead, we provide this wrapper that automatically detects the
-// version that is in use, and then implements POSIX semantics.
-// N.B. In addition to what POSIX says, we also guarantee that "buf" will
-// be set to an empty string, if this function failed. This means, in most
-// cases, you do not need to check the error code and you can directly
-// use the value of "buf". It will never have an undefined value.
-// DEPRECATED: Use StrError(int) instead.
-GOOGLE_GLOG_DLL_DECL int posix_strerror_r(int err, char *buf, size_t len);
-
-// A thread-safe replacement for strerror(). Returns a string describing the
-// given POSIX error code.
-GOOGLE_GLOG_DLL_DECL std::string StrError(int err);
-
-// A class for which we define operator<<, which does nothing.
-class GOOGLE_GLOG_DLL_DECL NullStream : public LogMessage::LogStream {
- public:
-  // Initialize the LogStream so the messages can be written somewhere
-  // (they'll never be actually displayed). This will be needed if a
-  // NullStream& is implicitly converted to LogStream&, in which case
-  // the overloaded NullStream::operator<< will not be invoked.
-  NullStream() : LogMessage::LogStream(message_buffer_, 1, 0) { }
-  NullStream(const char* /*file*/, int /*line*/,
-             const CheckOpString& /*result*/) :
-      LogMessage::LogStream(message_buffer_, 1, 0) { }
-  NullStream &stream() { return *this; }
- private:
-  // A very short buffer for messages (which we discard anyway). This
-  // will be needed if NullStream& converted to LogStream& (e.g. as a
-  // result of a conditional expression).
-  char message_buffer_[2];
-};
-
-// Do nothing. This operator is inline, allowing the message to be
-// compiled away. The message will not be compiled away if we do
-// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when
-// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly
-// converted to LogStream and the message will be computed and then
-// quietly discarded.
-template<class T>
-inline NullStream& operator<<(NullStream &str, const T &) { return str; }
-
-// Similar to NullStream, but aborts the program (without stack
-// trace), like LogMessageFatal.
-class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream {
- public:
-  NullStreamFatal() { }
-  NullStreamFatal(const char* file, int line, const CheckOpString& result) :
-      NullStream(file, line, result) { }
-  @ac_cv___attribute___noreturn@ ~NullStreamFatal() throw () { _exit(1); }
-};
-
-// Install a signal handler that will dump signal information and a stack
-// trace when the program crashes on certain signals.  We'll install the
-// signal handler for the following signals.
-//
-// SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, and SIGTERM.
-//
-// By default, the signal handler will write the failure dump to the
-// standard error.  You can customize the destination by installing your
-// own writer function by InstallFailureWriter() below.
-//
-// Note on threading:
-//
-// The function should be called before threads are created, if you want
-// to use the failure signal handler for all threads.  The stack trace
-// will be shown only for the thread that receives the signal.  In other
-// words, stack traces of other threads won't be shown.
-GOOGLE_GLOG_DLL_DECL void InstallFailureSignalHandler();
-
-// Installs a function that is used for writing the failure dump.  "data"
-// is the pointer to the beginning of a message to be written, and "size"
-// is the size of the message.  You should not expect the data is
-// terminated with '\0'.
-GOOGLE_GLOG_DLL_DECL void InstallFailureWriter(
-    void (*writer)(const char* data, int size));
-
-@ac_google_end_namespace@
-
-#endif // _LOGGING_H_
diff --git a/third_party/glog/src/glog/raw_logging.h.in b/third_party/glog/src/glog/raw_logging.h.in
deleted file mode 100644
index 31b7ed32d9..0000000000
--- a/third_party/glog/src/glog/raw_logging.h.in
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (c) 2006, Google 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:
-//
-//     * 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.
-//
-// Author: Maxim Lifantsev
-//
-// Thread-safe logging routines that do not allocate any memory or
-// acquire any locks, and can therefore be used by low-level memory
-// allocation and synchronization code.
-
-#ifndef BASE_RAW_LOGGING_H_
-#define BASE_RAW_LOGGING_H_
-
-#include <time.h>
-
-@ac_google_start_namespace@
-
-#include "glog/log_severity.h"
-#include "glog/vlog_is_on.h"
-
-// Annoying stuff for windows -- makes sure clients can import these functions
-#ifndef GOOGLE_GLOG_DLL_DECL
-# if defined(_WIN32) && !defined(__CYGWIN__)
-#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
-# else
-#   define GOOGLE_GLOG_DLL_DECL
-# endif
-#endif
-
-// This is similar to LOG(severity) << format... and VLOG(level) << format..,
-// but
-// * it is to be used ONLY by low-level modules that can't use normal LOG()
-// * it is desiged to be a low-level logger that does not allocate any
-//   memory and does not need any locks, hence:
-// * it logs straight and ONLY to STDERR w/o buffering
-// * it uses an explicit format and arguments list
-// * it will silently chop off really long message strings
-// Usage example:
-//   RAW_LOG(ERROR, "Failed foo with %i: %s", status, error);
-//   RAW_VLOG(3, "status is %i", status);
-// These will print an almost standard log lines like this to stderr only:
-//   E20200821 211317 file.cc:123] RAW: Failed foo with 22: bad_file
-//   I20200821 211317 file.cc:142] RAW: status is 20
-#define RAW_LOG(severity, ...) \
-  do { \
-    switch (@ac_google_namespace@::GLOG_ ## severity) {  \
-      case 0: \
-        RAW_LOG_INFO(__VA_ARGS__); \
-        break; \
-      case 1: \
-        RAW_LOG_WARNING(__VA_ARGS__); \
-        break; \
-      case 2: \
-        RAW_LOG_ERROR(__VA_ARGS__); \
-        break; \
-      case 3: \
-        RAW_LOG_FATAL(__VA_ARGS__); \
-        break; \
-      default: \
-        break; \
-    } \
-  } while (0)
-
-// The following STRIP_LOG testing is performed in the header file so that it's
-// possible to completely compile out the logging code and the log messages.
-#if STRIP_LOG == 0
-#define RAW_VLOG(verboselevel, ...) \
-  do { \
-    if (VLOG_IS_ON(verboselevel)) { \
-      RAW_LOG_INFO(__VA_ARGS__); \
-    } \
-  } while (0)
-#else
-#define RAW_VLOG(verboselevel, ...) RawLogStub__(0, __VA_ARGS__)
-#endif // STRIP_LOG == 0
-
-#if STRIP_LOG == 0
-#define RAW_LOG_INFO(...) @ac_google_namespace@::RawLog__(@ac_google_namespace@::GLOG_INFO, \
-                                   __FILE__, __LINE__, __VA_ARGS__)
-#else
-#define RAW_LOG_INFO(...) @ac_google_namespace@::RawLogStub__(0, __VA_ARGS__)
-#endif // STRIP_LOG == 0
-
-#if STRIP_LOG <= 1
-#define RAW_LOG_WARNING(...) @ac_google_namespace@::RawLog__(@ac_google_namespace@::GLOG_WARNING,   \
-                                      __FILE__, __LINE__, __VA_ARGS__)
-#else
-#define RAW_LOG_WARNING(...) @ac_google_namespace@::RawLogStub__(0, __VA_ARGS__)
-#endif // STRIP_LOG <= 1
-
-#if STRIP_LOG <= 2
-#define RAW_LOG_ERROR(...) @ac_google_namespace@::RawLog__(@ac_google_namespace@::GLOG_ERROR,       \
-                                    __FILE__, __LINE__, __VA_ARGS__)
-#else
-#define RAW_LOG_ERROR(...) @ac_google_namespace@::RawLogStub__(0, __VA_ARGS__)
-#endif // STRIP_LOG <= 2
-
-#if STRIP_LOG <= 3
-#define RAW_LOG_FATAL(...) @ac_google_namespace@::RawLog__(@ac_google_namespace@::GLOG_FATAL,       \
-                                    __FILE__, __LINE__, __VA_ARGS__)
-#else
-#define RAW_LOG_FATAL(...) \
-  do { \
-    @ac_google_namespace@::RawLogStub__(0, __VA_ARGS__);        \
-    exit(1); \
-  } while (0)
-#endif // STRIP_LOG <= 3
-
-// Similar to CHECK(condition) << message,
-// but for low-level modules: we use only RAW_LOG that does not allocate memory.
-// We do not want to provide args list here to encourage this usage:
-//   if (!cond)  RAW_LOG(FATAL, "foo ...", hard_to_compute_args);
-// so that the args are not computed when not needed.
-#define RAW_CHECK(condition, message)                                   \
-  do {                                                                  \
-    if (!(condition)) {                                                 \
-      RAW_LOG(FATAL, "Check %s failed: %s", #condition, message);       \
-    }                                                                   \
-  } while (0)
-
-// Debug versions of RAW_LOG and RAW_CHECK
-#ifndef NDEBUG
-
-#define RAW_DLOG(severity, ...) RAW_LOG(severity, __VA_ARGS__)
-#define RAW_DCHECK(condition, message) RAW_CHECK(condition, message)
-
-#else  // NDEBUG
-
-#define RAW_DLOG(severity, ...)                                 \
-  while (false)                                                 \
-    RAW_LOG(severity, __VA_ARGS__)
-#define RAW_DCHECK(condition, message) \
-  while (false) \
-    RAW_CHECK(condition, message)
-
-#endif  // NDEBUG
-
-// Stub log function used to work around for unused variable warnings when
-// building with STRIP_LOG > 0.
-static inline void RawLogStub__(int /* ignored */, ...) {
-}
-
-// Helper function to implement RAW_LOG and RAW_VLOG
-// Logs format... at "severity" level, reporting it
-// as called from file:line.
-// This does not allocate memory or acquire locks.
-GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity,
-                                   const char* file,
-                                   int line,
-                                   const char* format, ...)
-   @ac_cv___attribute___printf_4_5@;
-
-@ac_google_end_namespace@
-
-#endif  // BASE_RAW_LOGGING_H_
diff --git a/third_party/glog/src/glog/stl_logging.h.in b/third_party/glog/src/glog/stl_logging.h.in
deleted file mode 100644
index 600945d2c2..0000000000
--- a/third_party/glog/src/glog/stl_logging.h.in
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright (c) 2003, Google 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:
-//
-//     * 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.
-//
-// Stream output operators for STL containers; to be used for logging *only*.
-// Inclusion of this file lets you do:
-//
-// list<string> x;
-// LOG(INFO) << "data: " << x;
-// vector<int> v1, v2;
-// CHECK_EQ(v1, v2);
-//
-// If you want to use this header file with hash maps or slist, you
-// need to define macros before including this file:
-//
-// - GLOG_STL_LOGGING_FOR_UNORDERED     - <unordered_map> and <unordered_set>
-// - GLOG_STL_LOGGING_FOR_TR1_UNORDERED - <tr1/unordered_(map|set)>
-// - GLOG_STL_LOGGING_FOR_EXT_HASH      - <ext/hash_(map|set)>
-// - GLOG_STL_LOGGING_FOR_EXT_SLIST     - <ext/slist>
-//
-
-#ifndef UTIL_GTL_STL_LOGGING_INL_H_
-#define UTIL_GTL_STL_LOGGING_INL_H_
-
-#if !@ac_cv_cxx_using_operator@
-# error We do not support stl_logging for this compiler
-#endif
-
-#include <deque>
-#include <list>
-#include <map>
-#include <ostream>
-#include <set>
-#include <utility>
-#include <vector>
-
-#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
-# include <unordered_map>
-# include <unordered_set>
-#endif
-
-#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
-# include <tr1/unordered_map>
-# include <tr1/unordered_set>
-#endif
-
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-# include <ext/hash_set>
-# include <ext/hash_map>
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST
-# include <ext/slist>
-#endif
-
-// Forward declare these two, and define them after all the container streams
-// operators so that we can recurse from pair -> container -> container -> pair
-// properly.
-template<class First, class Second>
-std::ostream& operator<<(std::ostream& out, const std::pair<First, Second>& p);
-
-@ac_google_start_namespace@
-
-template<class Iter>
-void PrintSequence(std::ostream& out, Iter begin, Iter end);
-
-@ac_google_end_namespace@
-
-#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \
-template<class T1, class T2> \
-inline std::ostream& operator<<(std::ostream& out, \
-                                const Sequence<T1, T2>& seq) { \
-  @ac_google_namespace@::PrintSequence(out, seq.begin(), seq.end()); \
-  return out; \
-}
-
-OUTPUT_TWO_ARG_CONTAINER(std::vector)
-OUTPUT_TWO_ARG_CONTAINER(std::deque)
-OUTPUT_TWO_ARG_CONTAINER(std::list)
-#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST
-OUTPUT_TWO_ARG_CONTAINER(__gnu_cxx::slist)
-#endif
-
-#undef OUTPUT_TWO_ARG_CONTAINER
-
-#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \
-template<class T1, class T2, class T3> \
-inline std::ostream& operator<<(std::ostream& out, \
-                                const Sequence<T1, T2, T3>& seq) { \
-  @ac_google_namespace@::PrintSequence(out, seq.begin(), seq.end()); \
-  return out; \
-}
-
-OUTPUT_THREE_ARG_CONTAINER(std::set)
-OUTPUT_THREE_ARG_CONTAINER(std::multiset)
-
-#undef OUTPUT_THREE_ARG_CONTAINER
-
-#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \
-template<class T1, class T2, class T3, class T4> \
-inline std::ostream& operator<<(std::ostream& out, \
-                                const Sequence<T1, T2, T3, T4>& seq) { \
-  @ac_google_namespace@::PrintSequence(out, seq.begin(), seq.end()); \
-  return out; \
-}
-
-OUTPUT_FOUR_ARG_CONTAINER(std::map)
-OUTPUT_FOUR_ARG_CONTAINER(std::multimap)
-#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
-OUTPUT_FOUR_ARG_CONTAINER(std::unordered_set)
-OUTPUT_FOUR_ARG_CONTAINER(std::unordered_multiset)
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
-OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_set)
-OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_multiset)
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_set)
-OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_multiset)
-#endif
-
-#undef OUTPUT_FOUR_ARG_CONTAINER
-
-#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \
-template<class T1, class T2, class T3, class T4, class T5> \
-inline std::ostream& operator<<(std::ostream& out, \
-                                const Sequence<T1, T2, T3, T4, T5>& seq) { \
-  @ac_google_namespace@::PrintSequence(out, seq.begin(), seq.end()); \
-  return out; \
-}
-
-#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
-OUTPUT_FIVE_ARG_CONTAINER(std::unordered_map)
-OUTPUT_FIVE_ARG_CONTAINER(std::unordered_multimap)
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
-OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_map)
-OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_multimap)
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_map)
-OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_multimap)
-#endif
-
-#undef OUTPUT_FIVE_ARG_CONTAINER
-
-template<class First, class Second>
-inline std::ostream& operator<<(std::ostream& out,
-                                const std::pair<First, Second>& p) {
-  out << '(' << p.first << ", " << p.second << ')';
-  return out;
-}
-
-@ac_google_start_namespace@
-
-template<class Iter>
-inline void PrintSequence(std::ostream& out, Iter begin, Iter end) {
-  // Output at most 100 elements -- appropriate if used for logging.
-  for (int i = 0; begin != end && i < 100; ++i, ++begin) {
-    if (i > 0) out << ' ';
-    out << *begin;
-  }
-  if (begin != end) {
-    out << " ...";
-  }
-}
-
-@ac_google_end_namespace@
-
-// Note that this is technically undefined behavior! We are adding things into
-// the std namespace for a reason though -- we are providing new operations on
-// types which are themselves defined with this namespace. Without this, these
-// operator overloads cannot be found via ADL. If these definitions are not
-// found via ADL, they must be #included before they're used, which requires
-// this header to be included before apparently independent other headers.
-//
-// For example, base/logging.h defines various template functions to implement
-// CHECK_EQ(x, y) and stream x and y into the log in the event the check fails.
-// It does so via the function template MakeCheckOpValueString:
-//   template<class T>
-//   void MakeCheckOpValueString(strstream* ss, const T& v) {
-//     (*ss) << v;
-//   }
-// Because 'glog/logging.h' is included before 'glog/stl_logging.h',
-// subsequent CHECK_EQ(v1, v2) for vector<...> typed variable v1 and v2 can only
-// find these operator definitions via ADL.
-//
-// Even this solution has problems -- it may pull unintended operators into the
-// namespace as well, allowing them to also be found via ADL, and creating code
-// that only works with a particular order of includes. Long term, we need to
-// move all of the *definitions* into namespace std, bet we need to ensure no
-// one references them first. This lets us take that step. We cannot define them
-// in both because that would create ambiguous overloads when both are found.
-namespace std { using ::operator<<; }
-
-#endif  // UTIL_GTL_STL_LOGGING_INL_H_
diff --git a/third_party/glog/src/glog/vlog_is_on.h.in b/third_party/glog/src/glog/vlog_is_on.h.in
deleted file mode 100644
index 3f4c4a32a8..0000000000
--- a/third_party/glog/src/glog/vlog_is_on.h.in
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (c) 1999, 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Ray Sidney and many others
-//
-// Defines the VLOG_IS_ON macro that controls the variable-verbosity
-// conditional logging.
-//
-// It's used by VLOG and VLOG_IF in logging.h
-// and by RAW_VLOG in raw_logging.h to trigger the logging.
-//
-// It can also be used directly e.g. like this:
-//   if (VLOG_IS_ON(2)) {
-//     // do some logging preparation and logging
-//     // that can't be accomplished e.g. via just VLOG(2) << ...;
-//   }
-//
-// The truth value that VLOG_IS_ON(level) returns is determined by 
-// the three verbosity level flags:
-//   --v=<n>  Gives the default maximal active V-logging level;
-//            0 is the default.
-//            Normally positive values are used for V-logging levels.
-//   --vmodule=<str>  Gives the per-module maximal V-logging levels to override
-//                    the value given by --v.
-//                    E.g. "my_module=2,foo*=3" would change the logging level
-//                    for all code in source files "my_module.*" and "foo*.*"
-//                    ("-inl" suffixes are also disregarded for this matching).
-//
-// SetVLOGLevel helper function is provided to do limited dynamic control over
-// V-logging by overriding the per-module settings given via --vmodule flag.
-//
-// CAVEAT: --vmodule functionality is not available in non gcc compilers.
-//
-
-#ifndef BASE_VLOG_IS_ON_H_
-#define BASE_VLOG_IS_ON_H_
-
-#include "glog/log_severity.h"
-
-// Annoying stuff for windows -- makes sure clients can import these functions
-#ifndef GOOGLE_GLOG_DLL_DECL
-# if defined(_WIN32) && !defined(__CYGWIN__)
-#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
-# else
-#   define GOOGLE_GLOG_DLL_DECL
-# endif
-#endif
-
-#if defined(__GNUC__)
-// We emit an anonymous static int* variable at every VLOG_IS_ON(n) site.
-// (Normally) the first time every VLOG_IS_ON(n) site is hit,
-// we determine what variable will dynamically control logging at this site:
-// it's either FLAGS_v or an appropriate internal variable
-// matching the current source file that represents results of
-// parsing of --vmodule flag and/or SetVLOGLevel calls.
-#define VLOG_IS_ON(verboselevel)                                \
-  __extension__  \
-  ({ static @ac_google_namespace@::int32* vlocal__ = &@ac_google_namespace@::kLogSiteUninitialized;           \
-     @ac_google_namespace@::int32 verbose_level__ = (verboselevel);                    \
-     (*vlocal__ >= verbose_level__) &&                          \
-     ((vlocal__ != &@ac_google_namespace@::kLogSiteUninitialized) ||                   \
-      (@ac_google_namespace@::InitVLOG3__(&vlocal__, &FLAGS_v,                         \
-                   __FILE__, verbose_level__))); })
-#else
-// GNU extensions not available, so we do not support --vmodule.
-// Dynamic value of FLAGS_v always controls the logging level.
-#define VLOG_IS_ON(verboselevel) (FLAGS_v >= (verboselevel))
-#endif
-
-// Set VLOG(_IS_ON) level for module_pattern to log_level.
-// This lets us dynamically control what is normally set by the --vmodule flag.
-// Returns the level that previously applied to module_pattern.
-// NOTE: To change the log level for VLOG(_IS_ON) sites
-//	 that have already executed after/during InitGoogleLogging,
-//	 one needs to supply the exact --vmodule pattern that applied to them.
-//       (If no --vmodule pattern applied to them
-//       the value of FLAGS_v will continue to control them.)
-extern GOOGLE_GLOG_DLL_DECL int SetVLOGLevel(const char* module_pattern,
-                                             int log_level);
-
-// Various declarations needed for VLOG_IS_ON above: =========================
-
-// Special value used to indicate that a VLOG_IS_ON site has not been
-// initialized.  We make this a large value, so the common-case check
-// of "*vlocal__ >= verbose_level__" in VLOG_IS_ON definition
-// passes in such cases and InitVLOG3__ is then triggered.
-extern @ac_google_namespace@::int32 kLogSiteUninitialized;
-
-// Helper routine which determines the logging info for a particalur VLOG site.
-//   site_flag     is the address of the site-local pointer to the controlling
-//                 verbosity level
-//   site_default  is the default to use for *site_flag
-//   fname         is the current source file name
-//   verbose_level is the argument to VLOG_IS_ON
-// We will return the return value for VLOG_IS_ON
-// and if possible set *site_flag appropriately.
-extern GOOGLE_GLOG_DLL_DECL bool InitVLOG3__(
-    @ac_google_namespace@::int32** site_flag,
-    @ac_google_namespace@::int32* site_default,
-    const char* fname,
-    @ac_google_namespace@::int32 verbose_level);
-
-#endif  // BASE_VLOG_IS_ON_H_
diff --git a/third_party/glog/src/googletest.h b/third_party/glog/src/googletest.h
deleted file mode 100644
index a9e7795dca..0000000000
--- a/third_party/glog/src/googletest.h
+++ /dev/null
@@ -1,611 +0,0 @@
-// Copyright (c) 2009, Google 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:
-//
-//     * 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.
-//
-// Author: Shinichiro Hamaji
-//   (based on googletest: http://code.google.com/p/googletest/)
-
-#ifdef GOOGLETEST_H__
-#error You must not include this file twice.
-#endif
-#define GOOGLETEST_H__
-
-#include "utilities.h"
-
-#include <ctype.h>
-#include <setjmp.h>
-#include <time.h>
-
-#include <map>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#include "base/commandlineflags.h"
-
-using std::map;
-using std::string;
-using std::vector;
-
-_START_GOOGLE_NAMESPACE_
-
-extern GOOGLE_GLOG_DLL_DECL void (*g_logging_fail_func)();
-
-_END_GOOGLE_NAMESPACE_
-
-#undef GOOGLE_GLOG_DLL_DECL
-#define GOOGLE_GLOG_DLL_DECL
-
-static inline string GetTempDir() {
-#ifndef OS_WINDOWS
-  return "/tmp";
-#else
-  char tmp[MAX_PATH];
-  GetTempPathA(MAX_PATH, tmp);
-  return tmp;
-#endif
-}
-
-#if defined(OS_WINDOWS) && defined(_MSC_VER) && !defined(TEST_SRC_DIR)
-// The test will run in glog/vsproject/<project name>
-// (e.g., glog/vsproject/logging_unittest).
-static const char TEST_SRC_DIR[] = "../..";
-#elif !defined(TEST_SRC_DIR)
-# warning TEST_SRC_DIR should be defined in config.h
-static const char TEST_SRC_DIR[] = ".";
-#endif
-
-static const uint32_t PTR_TEST_VALUE = 0x12345678;
-
-DEFINE_string(test_tmpdir, GetTempDir(), "Dir we use for temp files");
-DEFINE_string(test_srcdir, TEST_SRC_DIR,
-              "Source-dir root, needed to find glog_unittest_flagfile");
-DEFINE_bool(run_benchmark, false, "If true, run benchmarks");
-#ifdef NDEBUG
-DEFINE_int32(benchmark_iters, 100000000, "Number of iterations per benchmark");
-#else
-DEFINE_int32(benchmark_iters, 100000, "Number of iterations per benchmark");
-#endif
-
-#ifdef HAVE_LIB_GTEST
-# include <gtest/gtest.h>
-// Use our ASSERT_DEATH implementation.
-# undef ASSERT_DEATH
-# undef ASSERT_DEBUG_DEATH
-using testing::InitGoogleTest;
-#else
-
-_START_GOOGLE_NAMESPACE_
-
-void InitGoogleTest(int*, char**);
-
-void InitGoogleTest(int*, char**) {}
-
-// The following is some bare-bones testing infrastructure
-
-#define EXPECT_TRUE(cond)                               \
-  do {                                                  \
-    if (!(cond)) {                                      \
-      fprintf(stderr, "Check failed: %s\n", #cond);     \
-      exit(1);                                          \
-    }                                                   \
-  } while (0)
-
-#define EXPECT_FALSE(cond)  EXPECT_TRUE(!(cond))
-
-#define EXPECT_OP(op, val1, val2)                                       \
-  do {                                                                  \
-    if (!((val1) op (val2))) {                                          \
-      fprintf(stderr, "Check failed: %s %s %s\n", #val1, #op, #val2);   \
-      exit(1);                                                          \
-    }                                                                   \
-  } while (0)
-
-#define EXPECT_EQ(val1, val2)  EXPECT_OP(==, val1, val2)
-#define EXPECT_NE(val1, val2)  EXPECT_OP(!=, val1, val2)
-#define EXPECT_GT(val1, val2)  EXPECT_OP(>, val1, val2)
-#define EXPECT_LT(val1, val2)  EXPECT_OP(<, val1, val2)
-
-#define EXPECT_NAN(arg)                                         \
-  do {                                                          \
-    if (!isnan(arg)) {                                          \
-      fprintf(stderr, "Check failed: isnan(%s)\n", #arg);       \
-      exit(1);                                                  \
-    }                                                           \
-  } while (0)
-
-#define EXPECT_INF(arg)                                         \
-  do {                                                          \
-    if (!isinf(arg)) {                                          \
-      fprintf(stderr, "Check failed: isinf(%s)\n", #arg);       \
-      exit(1);                                                  \
-    }                                                           \
-  } while (0)
-
-#define EXPECT_DOUBLE_EQ(val1, val2)                                    \
-  do {                                                                  \
-    if (((val1) < (val2) - 0.001 || (val1) > (val2) + 0.001)) {         \
-      fprintf(stderr, "Check failed: %s == %s\n", #val1, #val2);        \
-      exit(1);                                                          \
-    }                                                                   \
-  } while (0)
-
-#define EXPECT_STREQ(val1, val2)                                        \
-  do {                                                                  \
-    if (strcmp((val1), (val2)) != 0) {                                  \
-      fprintf(stderr, "Check failed: streq(%s, %s)\n", #val1, #val2);   \
-      exit(1);                                                          \
-    }                                                                   \
-  } while (0)
-
-vector<void (*)()> g_testlist;  // the tests to run
-
-#define TEST(a, b)                                      \
-  struct Test_##a##_##b {                               \
-    Test_##a##_##b() { g_testlist.push_back(&Run); }    \
-    static void Run() { FlagSaver fs; RunTest(); }      \
-    static void RunTest();                              \
-  };                                                    \
-  static Test_##a##_##b g_test_##a##_##b;               \
-  void Test_##a##_##b::RunTest()
-
-
-static inline int RUN_ALL_TESTS() {
-  vector<void (*)()>::const_iterator it;
-  for (it = g_testlist.begin(); it != g_testlist.end(); ++it) {
-    (*it)();
-  }
-  fprintf(stderr, "Passed %d tests\n\nPASS\n", (int)g_testlist.size());
-  return 0;
-}
-
-_END_GOOGLE_NAMESPACE_
-
-#endif  // ! HAVE_LIB_GTEST
-
-_START_GOOGLE_NAMESPACE_
-
-static bool g_called_abort;
-static jmp_buf g_jmp_buf;
-static inline void CalledAbort() {
-  g_called_abort = true;
-  longjmp(g_jmp_buf, 1);
-}
-
-#ifdef OS_WINDOWS
-// TODO(hamaji): Death test somehow doesn't work in Windows.
-#define ASSERT_DEATH(fn, msg)
-#else
-#define ASSERT_DEATH(fn, msg)                                           \
-  do {                                                                  \
-    g_called_abort = false;                                             \
-    /* in logging.cc */                                                 \
-    void (*original_logging_fail_func)() = g_logging_fail_func;         \
-    g_logging_fail_func = &CalledAbort;                                 \
-    if (!setjmp(g_jmp_buf)) fn;                                         \
-    /* set back to their default */                                     \
-    g_logging_fail_func = original_logging_fail_func;                   \
-    if (!g_called_abort) {                                              \
-      fprintf(stderr, "Function didn't die (%s): %s\n", msg, #fn);      \
-      exit(1);                                                          \
-    }                                                                   \
-  } while (0)
-#endif
-
-#ifdef NDEBUG
-#define ASSERT_DEBUG_DEATH(fn, msg)
-#else
-#define ASSERT_DEBUG_DEATH(fn, msg) ASSERT_DEATH(fn, msg)
-#endif  // NDEBUG
-
-// Benchmark tools.
-
-#define BENCHMARK(n) static BenchmarkRegisterer __benchmark_ ## n (#n, &n);
-
-map<string, void (*)(int)> g_benchlist;  // the benchmarks to run
-
-class BenchmarkRegisterer {
- public:
-  BenchmarkRegisterer(const char* name, void (*function)(int iters)) {
-    EXPECT_TRUE(g_benchlist.insert(std::make_pair(name, function)).second);
-  }
-};
-
-static inline void RunSpecifiedBenchmarks() {
-  if (!FLAGS_run_benchmark) {
-    return;
-  }
-
-  int iter_cnt = FLAGS_benchmark_iters;
-  puts("Benchmark\tTime(ns)\tIterations");
-  for (map<string, void (*)(int)>::const_iterator iter = g_benchlist.begin();
-       iter != g_benchlist.end();
-       ++iter) {
-    clock_t start = clock();
-    iter->second(iter_cnt);
-    double elapsed_ns =
-        ((double)clock() - start) / CLOCKS_PER_SEC * 1000*1000*1000;
-    printf("%s\t%8.2lf\t%10d\n",
-           iter->first.c_str(), elapsed_ns / iter_cnt, iter_cnt);
-  }
-  puts("");
-}
-
-// ----------------------------------------------------------------------
-// Golden file functions
-// ----------------------------------------------------------------------
-
-class CapturedStream {
- public:
-  CapturedStream(int fd, const string & filename) :
-    fd_(fd),
-    uncaptured_fd_(-1),
-    filename_(filename) {
-    Capture();
-  }
-
-  ~CapturedStream() {
-    if (uncaptured_fd_ != -1) {
-      CHECK(close(uncaptured_fd_) != -1);
-    }
-  }
-
-  // Start redirecting output to a file
-  void Capture() {
-    // Keep original stream for later
-    CHECK(uncaptured_fd_ == -1) << ", Stream " << fd_ << " already captured!";
-    uncaptured_fd_ = dup(fd_);
-    CHECK(uncaptured_fd_ != -1);
-
-    // Open file to save stream to
-    int cap_fd = open(filename_.c_str(),
-                      O_CREAT | O_TRUNC | O_WRONLY,
-                      S_IRUSR | S_IWUSR);
-    CHECK(cap_fd != -1);
-
-    // Send stdout/stderr to this file
-    fflush(NULL);
-    CHECK(dup2(cap_fd, fd_) != -1);
-    CHECK(close(cap_fd) != -1);
-  }
-
-  // Remove output redirection
-  void StopCapture() {
-    // Restore original stream
-    if (uncaptured_fd_ != -1) {
-      fflush(NULL);
-      CHECK(dup2(uncaptured_fd_, fd_) != -1);
-    }
-  }
-
-  const string & filename() const { return filename_; }
-
- private:
-  int fd_;             // file descriptor being captured
-  int uncaptured_fd_;  // where the stream was originally being sent to
-  string filename_;    // file where stream is being saved
-};
-static CapturedStream * s_captured_streams[STDERR_FILENO+1];
-// Redirect a file descriptor to a file.
-//   fd       - Should be STDOUT_FILENO or STDERR_FILENO
-//   filename - File where output should be stored
-static inline void CaptureTestOutput(int fd, const string & filename) {
-  CHECK((fd == STDOUT_FILENO) || (fd == STDERR_FILENO));
-  CHECK(s_captured_streams[fd] == NULL);
-  s_captured_streams[fd] = new CapturedStream(fd, filename);
-}
-static inline void CaptureTestStderr() {
-  CaptureTestOutput(STDERR_FILENO, FLAGS_test_tmpdir + "/captured.err");
-}
-// Return the size (in bytes) of a file
-static inline size_t GetFileSize(FILE * file) {
-  fseek(file, 0, SEEK_END);
-  return static_cast<size_t>(ftell(file));
-}
-// Read the entire content of a file as a string
-static inline string ReadEntireFile(FILE * file) {
-  const size_t file_size = GetFileSize(file);
-  char * const buffer = new char[file_size];
-
-  size_t bytes_last_read = 0;  // # of bytes read in the last fread()
-  size_t bytes_read = 0;       // # of bytes read so far
-
-  fseek(file, 0, SEEK_SET);
-
-  // Keep reading the file until we cannot read further or the
-  // pre-determined file size is reached.
-  do {
-    bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file);
-    bytes_read += bytes_last_read;
-  } while (bytes_last_read > 0 && bytes_read < file_size);
-
-  const string content = string(buffer, buffer+bytes_read);
-  delete[] buffer;
-
-  return content;
-}
-// Get the captured stdout (when fd is STDOUT_FILENO) or stderr (when
-// fd is STDERR_FILENO) as a string
-static inline string GetCapturedTestOutput(int fd) {
-  CHECK(fd == STDOUT_FILENO || fd == STDERR_FILENO);
-  CapturedStream * const cap = s_captured_streams[fd];
-  CHECK(cap)
-    << ": did you forget CaptureTestStdout() or CaptureTestStderr()?";
-
-  // Make sure everything is flushed.
-  cap->StopCapture();
-
-  // Read the captured file.
-  FILE * const file = fopen(cap->filename().c_str(), "r");
-  const string content = ReadEntireFile(file);
-  fclose(file);
-
-  delete cap;
-  s_captured_streams[fd] = NULL;
-
-  return content;
-}
-// Get the captured stderr of a test as a string.
-static inline string GetCapturedTestStderr() {
-  return GetCapturedTestOutput(STDERR_FILENO);
-}
-
-// Check if the string is [IWEF](\d{8}|YEARDATE)
-static inline bool IsLoggingPrefix(const string& s) {
-  if (s.size() != 9) return false;
-  if (!strchr("IWEF", s[0])) return false;
-  for (int i = 1; i <= 8; ++i) {
-    if (!isdigit(s[i]) && s[i] != "YEARDATE"[i-1]) return false;
-  }
-  return true;
-}
-
-// Convert log output into normalized form.
-//
-// Example:
-//     I20200102 030405 logging_unittest.cc:345] RAW: vlog -1
-//  => IYEARDATE TIME__ logging_unittest.cc:LINE] RAW: vlog -1
-static inline string MungeLine(const string& line) {
-  std::istringstream iss(line);
-  string before, logcode_date, time, thread_lineinfo;
-  iss >> logcode_date;
-  while (!IsLoggingPrefix(logcode_date)) {
-    before += " " + logcode_date;
-    if (!(iss >> logcode_date)) {
-      // We cannot find the header of log output.
-      return before;
-    }
-  }
-  if (!before.empty()) before += " ";
-  iss >> time;
-  iss >> thread_lineinfo;
-  CHECK(!thread_lineinfo.empty());
-  if (thread_lineinfo[thread_lineinfo.size() - 1] != ']') {
-    // We found thread ID.
-    string tmp;
-    iss >> tmp;
-    CHECK(!tmp.empty());
-    CHECK_EQ(']', tmp[tmp.size() - 1]);
-    thread_lineinfo = "THREADID " + tmp;
-  }
-  size_t index = thread_lineinfo.find(':');
-  CHECK_NE(string::npos, index);
-  thread_lineinfo = thread_lineinfo.substr(0, index+1) + "LINE]";
-  string rest;
-  std::getline(iss, rest);
-  return (before + logcode_date[0] + "YEARDATE TIME__ " + thread_lineinfo +
-          MungeLine(rest));
-}
-
-static inline void StringReplace(string* str,
-                          const string& oldsub,
-                          const string& newsub) {
-  size_t pos = str->find(oldsub);
-  if (pos != string::npos) {
-    str->replace(pos, oldsub.size(), newsub.c_str());
-  }
-}
-
-static inline string Munge(const string& filename) {
-  FILE* fp = fopen(filename.c_str(), "rb");
-  CHECK(fp != NULL) << filename << ": couldn't open";
-  char buf[4096];
-  string result;
-  while (fgets(buf, 4095, fp)) {
-    string line = MungeLine(buf);
-    char null_str[256];
-    char ptr_str[256];
-    sprintf(null_str, "%p", static_cast<void*>(NULL));
-    sprintf(ptr_str, "%p", reinterpret_cast<void*>(PTR_TEST_VALUE));
-
-    StringReplace(&line, "__NULLP__", null_str);
-    StringReplace(&line, "__PTRTEST__", ptr_str);
-
-    StringReplace(&line, "__SUCCESS__", StrError(0));
-    StringReplace(&line, "__ENOENT__", StrError(ENOENT));
-    StringReplace(&line, "__EINTR__", StrError(EINTR));
-    StringReplace(&line, "__ENXIO__", StrError(ENXIO));
-    StringReplace(&line, "__ENOEXEC__", StrError(ENOEXEC));
-    result += line + "\n";
-  }
-  fclose(fp);
-  return result;
-}
-
-static inline void WriteToFile(const string& body, const string& file) {
-  FILE* fp = fopen(file.c_str(), "wb");
-  fwrite(body.data(), 1, body.size(), fp);
-  fclose(fp);
-}
-
-static inline bool MungeAndDiffTestStderr(const string& golden_filename) {
-  CapturedStream* cap = s_captured_streams[STDERR_FILENO];
-  CHECK(cap) << ": did you forget CaptureTestStderr()?";
-
-  cap->StopCapture();
-
-  // Run munge
-  const string captured = Munge(cap->filename());
-  const string golden = Munge(golden_filename);
-  if (captured != golden) {
-    fprintf(stderr,
-            "Test with golden file failed. We'll try to show the diff:\n");
-    string munged_golden = golden_filename + ".munged";
-    WriteToFile(golden, munged_golden);
-    string munged_captured = cap->filename() + ".munged";
-    WriteToFile(captured, munged_captured);
-#ifdef OS_WINDOWS
-    string diffcmd("fc " + munged_golden + " " + munged_captured);
-#else
-    string diffcmd("diff -u " + munged_golden + " " + munged_captured);
-#endif
-    if (system(diffcmd.c_str()) != 0) {
-      fprintf(stderr, "diff command was failed.\n");
-    }
-    unlink(munged_golden.c_str());
-    unlink(munged_captured.c_str());
-    return false;
-  }
-  LOG(INFO) << "Diff was successful";
-  return true;
-}
-
-// Save flags used from logging_unittest.cc.
-#ifndef HAVE_LIB_GFLAGS
-struct FlagSaver {
-  FlagSaver()
-      : v_(FLAGS_v),
-        stderrthreshold_(FLAGS_stderrthreshold),
-        logtostderr_(FLAGS_logtostderr),
-        alsologtostderr_(FLAGS_alsologtostderr) {}
-  ~FlagSaver() {
-    FLAGS_v = v_;
-    FLAGS_stderrthreshold = stderrthreshold_;
-    FLAGS_logtostderr = logtostderr_;
-    FLAGS_alsologtostderr = alsologtostderr_;
-  }
-  int v_;
-  int stderrthreshold_;
-  bool logtostderr_;
-  bool alsologtostderr_;
-};
-#endif
-
-class Thread {
- public:
-  virtual ~Thread() {}
-
-  void SetJoinable(bool) {}
-#if defined(OS_WINDOWS) && !defined(OS_CYGWIN)
-  void Start() {
-    handle_ = CreateThread(NULL,
-                           0,
-                           (LPTHREAD_START_ROUTINE)&Thread::InvokeThread,
-                           (LPVOID)this,
-                           0,
-                           &th_);
-    CHECK(handle_) << "CreateThread";
-  }
-  void Join() {
-    WaitForSingleObject(handle_, INFINITE);
-  }
-#elif defined(HAVE_PTHREAD)
-  void Start() {
-    pthread_create(&th_, NULL, &Thread::InvokeThread, this);
-  }
-  void Join() {
-    pthread_join(th_, NULL);
-  }
-#else
-# error No thread implementation.
-#endif
-
- protected:
-  virtual void Run() = 0;
-
- private:
-  static void* InvokeThread(void* self) {
-    ((Thread*)self)->Run();
-    return NULL;
-  }
-
-#if defined(OS_WINDOWS) && !defined(OS_CYGWIN)
-  HANDLE handle_;
-  DWORD th_;
-#else
-  pthread_t th_;
-#endif
-};
-
-static inline void SleepForMilliseconds(int t) {
-#ifndef OS_WINDOWS
-# if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L
-  const struct timespec req = {0, t * 1000 * 1000};
-  nanosleep(&req, NULL);
-# else
-  usleep(t * 1000);
-# endif
-#else
-  Sleep(t);
-#endif
-}
-
-// Add hook for operator new to ensure there are no memory allocation.
-
-void (*g_new_hook)() = NULL;
-
-_END_GOOGLE_NAMESPACE_
-
-void* operator new(size_t size) {
-  if (GOOGLE_NAMESPACE::g_new_hook) {
-    GOOGLE_NAMESPACE::g_new_hook();
-  }
-  return malloc(size);
-}
-
-void* operator new[](size_t size) {
-  return ::operator new(size);
-}
-
-void operator delete(void* p) {
-  free(p);
-}
-
-void operator delete[](void* p) {
-  ::operator delete(p);
-}
diff --git a/third_party/glog/src/logging.cc b/third_party/glog/src/logging.cc
deleted file mode 100644
index ddcf910073..0000000000
--- a/third_party/glog/src/logging.cc
+++ /dev/null
@@ -1,2344 +0,0 @@
-// Copyright (c) 1999, Google 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:
-//
-//     * 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.
-
-#define _GNU_SOURCE 1 // needed for O_NOFOLLOW and pread()/pwrite()
-
-#include "utilities.h"
-
-#include <algorithm>
-#include <assert.h>
-#include <iomanip>
-#include <string>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>  // For _exit.
-#endif
-#include <climits>
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_SYS_UTSNAME_H
-# include <sys/utsname.h>  // For uname.
-#endif
-#include <time.h>
-#include <fcntl.h>
-#include <cstdio>
-#include <iostream>
-#include <stdarg.h>
-#include <stdlib.h>
-#ifdef HAVE_PWD_H
-# include <pwd.h>
-#endif
-#ifdef HAVE_SYSLOG_H
-# include <syslog.h>
-#endif
-#include <vector>
-#include <errno.h>                   // for errno
-#include <sstream>
-#ifdef OS_WINDOWS
-#include "windows/dirent.h"
-#else
-#include <dirent.h> // for automatic removal of old logs
-#endif
-#include "base/commandlineflags.h"        // to get the program name
-#include "glog/logging.h"
-#include "glog/raw_logging.h"
-#include "base/googleinit.h"
-
-#ifdef HAVE_STACKTRACE
-# include "stacktrace.h"
-#endif
-
-using std::string;
-using std::vector;
-using std::setw;
-using std::setfill;
-using std::hex;
-using std::dec;
-using std::min;
-using std::ostream;
-using std::ostringstream;
-
-using std::FILE;
-using std::fwrite;
-using std::fclose;
-using std::fflush;
-using std::fprintf;
-using std::perror;
-
-#ifdef __QNX__
-using std::fdopen;
-#endif
-
-#ifdef _WIN32
-#define fdopen _fdopen
-#endif
-
-// There is no thread annotation support.
-#define EXCLUSIVE_LOCKS_REQUIRED(mu)
-
-static bool BoolFromEnv(const char *varname, bool defval) {
-  const char* const valstr = getenv(varname);
-  if (!valstr) {
-    return defval;
-  }
-  return memchr("tTyY1\0", valstr[0], 6) != NULL;
-}
-
-GLOG_DEFINE_bool(timestamp_in_logfile_name,
-                 BoolFromEnv("GOOGLE_TIMESTAMP_IN_LOGFILE_NAME", true),
-                 "put a timestamp at the end of the log file name");
-GLOG_DEFINE_bool(logtostderr, BoolFromEnv("GOOGLE_LOGTOSTDERR", false),
-                 "log messages go to stderr instead of logfiles");
-GLOG_DEFINE_bool(alsologtostderr, BoolFromEnv("GOOGLE_ALSOLOGTOSTDERR", false),
-                 "log messages go to stderr in addition to logfiles");
-GLOG_DEFINE_bool(colorlogtostderr, false,
-                 "color messages logged to stderr (if supported by terminal)");
-#ifdef OS_LINUX
-GLOG_DEFINE_bool(drop_log_memory, true, "Drop in-memory buffers of log contents. "
-                 "Logs can grow very quickly and they are rarely read before they "
-                 "need to be evicted from memory. Instead, drop them from memory "
-                 "as soon as they are flushed to disk.");
-#endif
-
-// By default, errors (including fatal errors) get logged to stderr as
-// well as the file.
-//
-// The default is ERROR instead of FATAL so that users can see problems
-// when they run a program without having to look in another file.
-DEFINE_int32(stderrthreshold,
-             GOOGLE_NAMESPACE::GLOG_ERROR,
-             "log messages at or above this level are copied to stderr in "
-             "addition to logfiles.  This flag obsoletes --alsologtostderr.");
-
-GLOG_DEFINE_string(alsologtoemail, "",
-                   "log messages go to these email addresses "
-                   "in addition to logfiles");
-GLOG_DEFINE_bool(log_prefix, true,
-                 "Prepend the log prefix to the start of each log line");
-GLOG_DEFINE_int32(minloglevel, 0, "Messages logged at a lower level than this don't "
-                  "actually get logged anywhere");
-GLOG_DEFINE_int32(logbuflevel, 0,
-                  "Buffer log messages logged at this level or lower"
-                  " (-1 means don't buffer; 0 means buffer INFO only;"
-                  " ...)");
-GLOG_DEFINE_int32(logbufsecs, 30,
-                  "Buffer log messages for at most this many seconds");
-GLOG_DEFINE_int32(logemaillevel, 999,
-                  "Email log messages logged at this level or higher"
-                  " (0 means email all; 3 means email FATAL only;"
-                  " ...)");
-GLOG_DEFINE_string(logmailer, "/bin/mail",
-                   "Mailer used to send logging email");
-
-// Compute the default value for --log_dir
-static const char* DefaultLogDir() {
-  const char* env;
-  env = getenv("GOOGLE_LOG_DIR");
-  if (env != NULL && env[0] != '\0') {
-    return env;
-  }
-  env = getenv("TEST_TMPDIR");
-  if (env != NULL && env[0] != '\0') {
-    return env;
-  }
-  return "";
-}
-
-GLOG_DEFINE_int32(logfile_mode, 0664, "Log file mode/permissions.");
-
-GLOG_DEFINE_string(log_dir, DefaultLogDir(),
-                   "If specified, logfiles are written into this directory instead "
-                   "of the default logging directory.");
-GLOG_DEFINE_string(log_link, "", "Put additional links to the log "
-                   "files in this directory");
-
-GLOG_DEFINE_int32(max_log_size, 1800,
-                  "approx. maximum log file size (in MB). A value of 0 will "
-                  "be silently overridden to 1.");
-
-GLOG_DEFINE_bool(stop_logging_if_full_disk, false,
-                 "Stop attempting to log to disk if the disk is full.");
-
-GLOG_DEFINE_string(log_backtrace_at, "",
-                   "Emit a backtrace when logging at file:linenum.");
-
-// TODO(hamaji): consider windows
-#define PATH_SEPARATOR '/'
-
-#ifndef HAVE_PREAD
-#if defined(OS_WINDOWS)
-#include <basetsd.h>
-#define ssize_t SSIZE_T
-#endif
-static ssize_t pread(int fd, void* buf, size_t count, off_t offset) {
-  off_t orig_offset = lseek(fd, 0, SEEK_CUR);
-  if (orig_offset == (off_t)-1)
-    return -1;
-  if (lseek(fd, offset, SEEK_CUR) == (off_t)-1)
-    return -1;
-  ssize_t len = read(fd, buf, count);
-  if (len < 0)
-    return len;
-  if (lseek(fd, orig_offset, SEEK_SET) == (off_t)-1)
-    return -1;
-  return len;
-}
-#endif  // !HAVE_PREAD
-
-#ifndef HAVE_PWRITE
-static ssize_t pwrite(int fd, void* buf, size_t count, off_t offset) {
-  off_t orig_offset = lseek(fd, 0, SEEK_CUR);
-  if (orig_offset == (off_t)-1)
-    return -1;
-  if (lseek(fd, offset, SEEK_CUR) == (off_t)-1)
-    return -1;
-  ssize_t len = write(fd, buf, count);
-  if (len < 0)
-    return len;
-  if (lseek(fd, orig_offset, SEEK_SET) == (off_t)-1)
-    return -1;
-  return len;
-}
-#endif  // !HAVE_PWRITE
-
-static void GetHostName(string* hostname) {
-#if defined(HAVE_SYS_UTSNAME_H)
-  struct utsname buf;
-  if (0 != uname(&buf)) {
-    // ensure null termination on failure
-    *buf.nodename = '\0';
-  }
-  *hostname = buf.nodename;
-#elif defined(OS_WINDOWS)
-  char buf[MAX_COMPUTERNAME_LENGTH + 1];
-  DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
-  if (GetComputerNameA(buf, &len)) {
-    *hostname = buf;
-  } else {
-    hostname->clear();
-  }
-#else
-# warning There is no way to retrieve the host name.
-  *hostname = "(unknown)";
-#endif
-}
-
-// Returns true iff terminal supports using colors in output.
-static bool TerminalSupportsColor() {
-  bool term_supports_color = false;
-#ifdef OS_WINDOWS
-  // on Windows TERM variable is usually not set, but the console does
-  // support colors.
-  term_supports_color = true;
-#else
-  // On non-Windows platforms, we rely on the TERM variable.
-  const char* const term = getenv("TERM");
-  if (term != NULL && term[0] != '\0') {
-    term_supports_color =
-      !strcmp(term, "xterm") ||
-      !strcmp(term, "xterm-color") ||
-      !strcmp(term, "xterm-256color") ||
-      !strcmp(term, "screen-256color") ||
-      !strcmp(term, "konsole") ||
-      !strcmp(term, "konsole-16color") ||
-      !strcmp(term, "konsole-256color") ||
-      !strcmp(term, "screen") ||
-      !strcmp(term, "linux") ||
-      !strcmp(term, "cygwin");
-  }
-#endif
-  return term_supports_color;
-}
-
-_START_GOOGLE_NAMESPACE_
-
-enum GLogColor {
-  COLOR_DEFAULT,
-  COLOR_RED,
-  COLOR_GREEN,
-  COLOR_YELLOW
-};
-
-static GLogColor SeverityToColor(LogSeverity severity) {
-  assert(severity >= 0 && severity < NUM_SEVERITIES);
-  GLogColor color = COLOR_DEFAULT;
-  switch (severity) {
-  case GLOG_INFO:
-    color = COLOR_DEFAULT;
-    break;
-  case GLOG_WARNING:
-    color = COLOR_YELLOW;
-    break;
-  case GLOG_ERROR:
-  case GLOG_FATAL:
-    color = COLOR_RED;
-    break;
-  default:
-    // should never get here.
-    assert(false);
-  }
-  return color;
-}
-
-#ifdef OS_WINDOWS
-
-// Returns the character attribute for the given color.
-static WORD GetColorAttribute(GLogColor color) {
-  switch (color) {
-    case COLOR_RED:    return FOREGROUND_RED;
-    case COLOR_GREEN:  return FOREGROUND_GREEN;
-    case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN;
-    default:           return 0;
-  }
-}
-
-#else
-
-// Returns the ANSI color code for the given color.
-static const char* GetAnsiColorCode(GLogColor color) {
-  switch (color) {
-  case COLOR_RED:     return "1";
-  case COLOR_GREEN:   return "2";
-  case COLOR_YELLOW:  return "3";
-  case COLOR_DEFAULT:  return "";
-  };
-  return NULL; // stop warning about return type.
-}
-
-#endif  // OS_WINDOWS
-
-// Safely get max_log_size, overriding to 1 if it somehow gets defined as 0
-static int32 MaxLogSize() {
-  return (FLAGS_max_log_size > 0 ? FLAGS_max_log_size : 1);
-}
-
-// An arbitrary limit on the length of a single log message.  This
-// is so that streaming can be done more efficiently.
-const size_t LogMessage::kMaxLogMessageLen = 30000;
-
-struct LogMessage::LogMessageData  {
-  LogMessageData();
-
-  int preserved_errno_;      // preserved errno
-  // Buffer space; contains complete message text.
-  char message_text_[LogMessage::kMaxLogMessageLen+1];
-  LogStream stream_;
-  char severity_;      // What level is this LogMessage logged at?
-  int line_;                 // line number where logging call is.
-  void (LogMessage::*send_method_)();  // Call this in destructor to send
-  union {  // At most one of these is used: union to keep the size low.
-    LogSink* sink_;             // NULL or sink to send message to
-    std::vector<std::string>* outvec_; // NULL or vector to push message onto
-    std::string* message_;             // NULL or string to write message into
-  };
-  time_t timestamp_;            // Time of creation of LogMessage
-  struct ::tm tm_time_;         // Time of creation of LogMessage
-  int32 usecs_;                   // Time of creation of LogMessage - microseconds part
-  size_t num_prefix_chars_;     // # of chars of prefix in this message
-  size_t num_chars_to_log_;     // # of chars of msg to send to log
-  size_t num_chars_to_syslog_;  // # of chars of msg to send to syslog
-  const char* basename_;        // basename of file that called LOG
-  const char* fullname_;        // fullname of file that called LOG
-  bool has_been_flushed_;       // false => data has not been flushed
-  bool first_fatal_;            // true => this was first fatal msg
-
- private:
-  LogMessageData(const LogMessageData&);
-  void operator=(const LogMessageData&);
-};
-
-// A mutex that allows only one thread to log at a time, to keep things from
-// getting jumbled.  Some other very uncommon logging operations (like
-// changing the destination file for log messages of a given severity) also
-// lock this mutex.  Please be sure that anybody who might possibly need to
-// lock it does so.
-static Mutex log_mutex;
-
-// Number of messages sent at each severity.  Under log_mutex.
-int64 LogMessage::num_messages_[NUM_SEVERITIES] = {0, 0, 0, 0};
-
-// Globally disable log writing (if disk is full)
-static bool stop_writing = false;
-
-const char*const LogSeverityNames[NUM_SEVERITIES] = {
-  "INFO", "WARNING", "ERROR", "FATAL"
-};
-
-// Has the user called SetExitOnDFatal(true)?
-static bool exit_on_dfatal = true;
-
-const char* GetLogSeverityName(LogSeverity severity) {
-  return LogSeverityNames[severity];
-}
-
-static bool SendEmailInternal(const char*dest, const char *subject,
-                              const char*body, bool use_logging);
-
-base::Logger::~Logger() {
-}
-
-namespace {
-
-// Encapsulates all file-system related state
-class LogFileObject : public base::Logger {
- public:
-  LogFileObject(LogSeverity severity, const char* base_filename);
-  ~LogFileObject();
-
-  virtual void Write(bool force_flush, // Should we force a flush here?
-                     time_t timestamp,  // Timestamp for this entry
-                     const char* message,
-                     int message_len);
-
-  // Configuration options
-  void SetBasename(const char* basename);
-  void SetExtension(const char* ext);
-  void SetSymlinkBasename(const char* symlink_basename);
-
-  // Normal flushing routine
-  virtual void Flush();
-
-  // It is the actual file length for the system loggers,
-  // i.e., INFO, ERROR, etc.
-  virtual uint32 LogSize() {
-    MutexLock l(&lock_);
-    return file_length_;
-  }
-
-  // Internal flush routine.  Exposed so that FlushLogFilesUnsafe()
-  // can avoid grabbing a lock.  Usually Flush() calls it after
-  // acquiring lock_.
-  void FlushUnlocked();
-
- private:
-  static const uint32 kRolloverAttemptFrequency = 0x20;
-
-  Mutex lock_;
-  bool base_filename_selected_;
-  string base_filename_;
-  string symlink_basename_;
-  string filename_extension_;     // option users can specify (eg to add port#)
-  FILE* file_;
-  LogSeverity severity_;
-  uint32 bytes_since_flush_;
-  uint32 dropped_mem_length_;
-  uint32 file_length_;
-  unsigned int rollover_attempt_;
-  int64 next_flush_time_;         // cycle count at which to flush log
-
-  // Actually create a logfile using the value of base_filename_ and the
-  // optional argument time_pid_string
-  // REQUIRES: lock_ is held
-  bool CreateLogfile(const string& time_pid_string);
-};
-
-}  // namespace
-
-class LogDestination {
- public:
-  friend class LogMessage;
-  friend void ReprintFatalMessage();
-  friend base::Logger* base::GetLogger(LogSeverity);
-  friend void base::SetLogger(LogSeverity, base::Logger*);
-
-  // These methods are just forwarded to by their global versions.
-  static void SetLogDestination(LogSeverity severity,
-				const char* base_filename);
-  static void SetLogSymlink(LogSeverity severity,
-                            const char* symlink_basename);
-  static void AddLogSink(LogSink *destination);
-  static void RemoveLogSink(LogSink *destination);
-  static void SetLogFilenameExtension(const char* filename_extension);
-  static void SetStderrLogging(LogSeverity min_severity);
-  static void SetEmailLogging(LogSeverity min_severity, const char* addresses);
-  static void LogToStderr();
-  // Flush all log files that are at least at the given severity level
-  static void FlushLogFiles(int min_severity);
-  static void FlushLogFilesUnsafe(int min_severity);
-
-  // we set the maximum size of our packet to be 1400, the logic being
-  // to prevent fragmentation.
-  // Really this number is arbitrary.
-  static const int kNetworkBytes = 1400;
-
-  static const string& hostname();
-  static const bool& terminal_supports_color() {
-    return terminal_supports_color_;
-  }
-
-  static void DeleteLogDestinations();
-
- private:
-  LogDestination(LogSeverity severity, const char* base_filename);
-  ~LogDestination();
-
-  // Take a log message of a particular severity and log it to stderr
-  // iff it's of a high enough severity to deserve it.
-  static void MaybeLogToStderr(LogSeverity severity, const char* message,
-			       size_t len);
-
-  // Take a log message of a particular severity and log it to email
-  // iff it's of a high enough severity to deserve it.
-  static void MaybeLogToEmail(LogSeverity severity, const char* message,
-			      size_t len);
-  // Take a log message of a particular severity and log it to a file
-  // iff the base filename is not "" (which means "don't log to me")
-  static void MaybeLogToLogfile(LogSeverity severity,
-                                time_t timestamp,
-				const char* message, size_t len);
-  // Take a log message of a particular severity and log it to the file
-  // for that severity and also for all files with severity less than
-  // this severity.
-  static void LogToAllLogfiles(LogSeverity severity,
-                               time_t timestamp,
-                               const char* message, size_t len);
-
-  // Send logging info to all registered sinks.
-  static void LogToSinks(LogSeverity severity,
-                         const char *full_filename,
-                         const char *base_filename,
-                         int line,
-                         const struct ::tm* tm_time,
-                         const char* message,
-                         size_t message_len,
-                         int32 usecs);
-
-  // Wait for all registered sinks via WaitTillSent
-  // including the optional one in "data".
-  static void WaitForSinks(LogMessage::LogMessageData* data);
-
-  static LogDestination* log_destination(LogSeverity severity);
-
-  LogFileObject fileobject_;
-  base::Logger* logger_;      // Either &fileobject_, or wrapper around it
-
-  static LogDestination* log_destinations_[NUM_SEVERITIES];
-  static LogSeverity email_logging_severity_;
-  static string addresses_;
-  static string hostname_;
-  static bool terminal_supports_color_;
-
-  // arbitrary global logging destinations.
-  static vector<LogSink*>* sinks_;
-
-  // Protects the vector sinks_,
-  // but not the LogSink objects its elements reference.
-  static Mutex sink_mutex_;
-
-  // Disallow
-  LogDestination(const LogDestination&);
-  LogDestination& operator=(const LogDestination&);
-};
-
-// Errors do not get logged to email by default.
-LogSeverity LogDestination::email_logging_severity_ = 99999;
-
-string LogDestination::addresses_;
-string LogDestination::hostname_;
-
-vector<LogSink*>* LogDestination::sinks_ = NULL;
-Mutex LogDestination::sink_mutex_;
-bool LogDestination::terminal_supports_color_ = TerminalSupportsColor();
-
-/* static */
-const string& LogDestination::hostname() {
-  if (hostname_.empty()) {
-    GetHostName(&hostname_);
-    if (hostname_.empty()) {
-      hostname_ = "(unknown)";
-    }
-  }
-  return hostname_;
-}
-
-LogDestination::LogDestination(LogSeverity severity,
-                               const char* base_filename)
-  : fileobject_(severity, base_filename),
-    logger_(&fileobject_) {
-}
-
-LogDestination::~LogDestination() {
-  if (logger_ && logger_ != &fileobject_) {
-    // Delete user-specified logger set via SetLogger().
-    delete logger_;
-  }
-}
-
-inline void LogDestination::FlushLogFilesUnsafe(int min_severity) {
-  // assume we have the log_mutex or we simply don't care
-  // about it
-  for (int i = min_severity; i < NUM_SEVERITIES; i++) {
-    LogDestination* log = log_destinations_[i];
-    if (log != NULL) {
-      // Flush the base fileobject_ logger directly instead of going
-      // through any wrappers to reduce chance of deadlock.
-      log->fileobject_.FlushUnlocked();
-    }
-  }
-}
-
-inline void LogDestination::FlushLogFiles(int min_severity) {
-  // Prevent any subtle race conditions by wrapping a mutex lock around
-  // all this stuff.
-  MutexLock l(&log_mutex);
-  for (int i = min_severity; i < NUM_SEVERITIES; i++) {
-    LogDestination* log = log_destination(i);
-    if (log != NULL) {
-      log->logger_->Flush();
-    }
-  }
-}
-
-inline void LogDestination::SetLogDestination(LogSeverity severity,
-					      const char* base_filename) {
-  assert(severity >= 0 && severity < NUM_SEVERITIES);
-  // Prevent any subtle race conditions by wrapping a mutex lock around
-  // all this stuff.
-  MutexLock l(&log_mutex);
-  log_destination(severity)->fileobject_.SetBasename(base_filename);
-}
-
-inline void LogDestination::SetLogSymlink(LogSeverity severity,
-                                          const char* symlink_basename) {
-  CHECK_GE(severity, 0);
-  CHECK_LT(severity, NUM_SEVERITIES);
-  MutexLock l(&log_mutex);
-  log_destination(severity)->fileobject_.SetSymlinkBasename(symlink_basename);
-}
-
-inline void LogDestination::AddLogSink(LogSink *destination) {
-  // Prevent any subtle race conditions by wrapping a mutex lock around
-  // all this stuff.
-  MutexLock l(&sink_mutex_);
-  if (!sinks_)  sinks_ = new vector<LogSink*>;
-  sinks_->push_back(destination);
-}
-
-inline void LogDestination::RemoveLogSink(LogSink *destination) {
-  // Prevent any subtle race conditions by wrapping a mutex lock around
-  // all this stuff.
-  MutexLock l(&sink_mutex_);
-  // This doesn't keep the sinks in order, but who cares?
-  if (sinks_) {
-    for (int i = sinks_->size() - 1; i >= 0; i--) {
-      if ((*sinks_)[i] == destination) {
-        (*sinks_)[i] = (*sinks_)[sinks_->size() - 1];
-        sinks_->pop_back();
-        break;
-      }
-    }
-  }
-}
-
-inline void LogDestination::SetLogFilenameExtension(const char* ext) {
-  // Prevent any subtle race conditions by wrapping a mutex lock around
-  // all this stuff.
-  MutexLock l(&log_mutex);
-  for ( int severity = 0; severity < NUM_SEVERITIES; ++severity ) {
-    log_destination(severity)->fileobject_.SetExtension(ext);
-  }
-}
-
-inline void LogDestination::SetStderrLogging(LogSeverity min_severity) {
-  assert(min_severity >= 0 && min_severity < NUM_SEVERITIES);
-  // Prevent any subtle race conditions by wrapping a mutex lock around
-  // all this stuff.
-  MutexLock l(&log_mutex);
-  FLAGS_stderrthreshold = min_severity;
-}
-
-inline void LogDestination::LogToStderr() {
-  // *Don't* put this stuff in a mutex lock, since SetStderrLogging &
-  // SetLogDestination already do the locking!
-  SetStderrLogging(0);            // thus everything is "also" logged to stderr
-  for ( int i = 0; i < NUM_SEVERITIES; ++i ) {
-    SetLogDestination(i, "");     // "" turns off logging to a logfile
-  }
-}
-
-inline void LogDestination::SetEmailLogging(LogSeverity min_severity,
-					    const char* addresses) {
-  assert(min_severity >= 0 && min_severity < NUM_SEVERITIES);
-  // Prevent any subtle race conditions by wrapping a mutex lock around
-  // all this stuff.
-  MutexLock l(&log_mutex);
-  LogDestination::email_logging_severity_ = min_severity;
-  LogDestination::addresses_ = addresses;
-}
-
-static void ColoredWriteToStderr(LogSeverity severity,
-                                 const char* message, size_t len) {
-  const GLogColor color =
-      (LogDestination::terminal_supports_color() && FLAGS_colorlogtostderr) ?
-      SeverityToColor(severity) : COLOR_DEFAULT;
-
-  // Avoid using cerr from this module since we may get called during
-  // exit code, and cerr may be partially or fully destroyed by then.
-  if (COLOR_DEFAULT == color) {
-    fwrite(message, len, 1, stderr);
-    return;
-  }
-#ifdef OS_WINDOWS
-  const HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
-
-  // Gets the current text color.
-  CONSOLE_SCREEN_BUFFER_INFO buffer_info;
-  GetConsoleScreenBufferInfo(stderr_handle, &buffer_info);
-  const WORD old_color_attrs = buffer_info.wAttributes;
-
-  // We need to flush the stream buffers into the console before each
-  // SetConsoleTextAttribute call lest it affect the text that is already
-  // printed but has not yet reached the console.
-  fflush(stderr);
-  SetConsoleTextAttribute(stderr_handle,
-                          GetColorAttribute(color) | FOREGROUND_INTENSITY);
-  fwrite(message, len, 1, stderr);
-  fflush(stderr);
-  // Restores the text color.
-  SetConsoleTextAttribute(stderr_handle, old_color_attrs);
-#else
-  fprintf(stderr, "\033[0;3%sm", GetAnsiColorCode(color));
-  fwrite(message, len, 1, stderr);
-  fprintf(stderr, "\033[m");  // Resets the terminal to default.
-#endif  // OS_WINDOWS
-}
-
-static void WriteToStderr(const char* message, size_t len) {
-  // Avoid using cerr from this module since we may get called during
-  // exit code, and cerr may be partially or fully destroyed by then.
-  fwrite(message, len, 1, stderr);
-}
-
-inline void LogDestination::MaybeLogToStderr(LogSeverity severity,
-					     const char* message, size_t len) {
-  if ((severity >= FLAGS_stderrthreshold) || FLAGS_alsologtostderr) {
-    ColoredWriteToStderr(severity, message, len);
-#ifdef OS_WINDOWS
-    // On Windows, also output to the debugger
-    ::OutputDebugStringA(string(message,len).c_str());
-#endif
-  }
-}
-
-
-inline void LogDestination::MaybeLogToEmail(LogSeverity severity,
-					    const char* message, size_t len) {
-  if (severity >= email_logging_severity_ ||
-      severity >= FLAGS_logemaillevel) {
-    string to(FLAGS_alsologtoemail);
-    if (!addresses_.empty()) {
-      if (!to.empty()) {
-        to += ",";
-      }
-      to += addresses_;
-    }
-    const string subject(string("[LOG] ") + LogSeverityNames[severity] + ": " +
-                         glog_internal_namespace_::ProgramInvocationShortName());
-    string body(hostname());
-    body += "\n\n";
-    body.append(message, len);
-
-    // should NOT use SendEmail().  The caller of this function holds the
-    // log_mutex and SendEmail() calls LOG/VLOG which will block trying to
-    // acquire the log_mutex object.  Use SendEmailInternal() and set
-    // use_logging to false.
-    SendEmailInternal(to.c_str(), subject.c_str(), body.c_str(), false);
-  }
-}
-
-
-inline void LogDestination::MaybeLogToLogfile(LogSeverity severity,
-                                              time_t timestamp,
-					      const char* message,
-					      size_t len) {
-  const bool should_flush = severity > FLAGS_logbuflevel;
-  LogDestination* destination = log_destination(severity);
-  destination->logger_->Write(should_flush, timestamp, message, len);
-}
-
-inline void LogDestination::LogToAllLogfiles(LogSeverity severity,
-                                             time_t timestamp,
-                                             const char* message,
-                                             size_t len) {
-
-  if ( FLAGS_logtostderr ) {           // global flag: never log to file
-    ColoredWriteToStderr(severity, message, len);
-  } else {
-    for (int i = severity; i >= 0; --i)
-      LogDestination::MaybeLogToLogfile(i, timestamp, message, len);
-  }
-}
-
-inline void LogDestination::LogToSinks(LogSeverity severity,
-                                       const char *full_filename,
-                                       const char *base_filename,
-                                       int line,
-                                       const struct ::tm* tm_time,
-                                       const char* message,
-                                       size_t message_len,
-                                       int32 usecs) {
-  ReaderMutexLock l(&sink_mutex_);
-  if (sinks_) {
-    for (int i = sinks_->size() - 1; i >= 0; i--) {
-      (*sinks_)[i]->send(severity, full_filename, base_filename,
-                         line, tm_time, message, message_len, usecs);
-    }
-  }
-}
-
-inline void LogDestination::WaitForSinks(LogMessage::LogMessageData* data) {
-  ReaderMutexLock l(&sink_mutex_);
-  if (sinks_) {
-    for (int i = sinks_->size() - 1; i >= 0; i--) {
-      (*sinks_)[i]->WaitTillSent();
-    }
-  }
-  const bool send_to_sink =
-      (data->send_method_ == &LogMessage::SendToSink) ||
-      (data->send_method_ == &LogMessage::SendToSinkAndLog);
-  if (send_to_sink && data->sink_ != NULL) {
-    data->sink_->WaitTillSent();
-  }
-}
-
-LogDestination* LogDestination::log_destinations_[NUM_SEVERITIES];
-
-inline LogDestination* LogDestination::log_destination(LogSeverity severity) {
-  assert(severity >=0 && severity < NUM_SEVERITIES);
-  if (!log_destinations_[severity]) {
-    log_destinations_[severity] = new LogDestination(severity, NULL);
-  }
-  return log_destinations_[severity];
-}
-
-void LogDestination::DeleteLogDestinations() {
-  for (int severity = 0; severity < NUM_SEVERITIES; ++severity) {
-    delete log_destinations_[severity];
-    log_destinations_[severity] = NULL;
-  }
-  MutexLock l(&sink_mutex_);
-  delete sinks_;
-  sinks_ = NULL;
-}
-
-namespace {
-
-bool IsGlogLog(const string& filename) {
-  // Check if filename matches the pattern of a glog file:
-  // "<program name>.<hostname>.<user name>.log...".
-  const int kKeywordCount = 4;
-  std::string keywords[kKeywordCount] = {
-    glog_internal_namespace_::ProgramInvocationShortName(),
-    LogDestination::hostname(),
-    MyUserName(),
-    "log"
-  };
-
-  int start_pos = 0;
-  for (int i = 0; i < kKeywordCount; i++) {
-    if (filename.find(keywords[i], start_pos) == filename.npos) {
-      return false;
-    }
-    start_pos += keywords[i].size() + 1;
-  }
-  return true;
-}
-
-bool LastModifiedOver(const string& filepath, int days) {
-  // Try to get the last modified time of this file.
-  struct stat file_stat;
-
-  if (stat(filepath.c_str(), &file_stat) == 0) {
-    // A day is 86400 seconds, so 7 days is 86400 * 7 = 604800 seconds.
-    time_t last_modified_time = file_stat.st_mtime;
-    time_t current_time = time(NULL);
-    return difftime(current_time, last_modified_time) > days * 86400;
-  }
-
-  // If failed to get file stat, don't return true!
-  return false;
-}
-
-vector<string> GetOverdueLogNames(string log_directory, int days) {
-  // The names of overdue logs.
-  vector<string> overdue_log_names;
-
-  // Try to get all files within log_directory.
-  DIR *dir;
-  struct dirent *ent;
-
-  char dir_delim = '/';
-#ifdef OS_WINDOWS
-  dir_delim = '\\';
-#endif
-
-  // If log_directory doesn't end with a slash, append a slash to it.
-  if (log_directory.at(log_directory.size() - 1) != dir_delim) {
-    log_directory += dir_delim;
-  }
-
-  if ((dir=opendir(log_directory.c_str()))) {
-    while ((ent=readdir(dir))) {
-      if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
-        continue;
-      }
-      string filepath = log_directory + ent->d_name;
-      if (IsGlogLog(ent->d_name) && LastModifiedOver(filepath, days)) {
-        overdue_log_names.push_back(filepath);
-      }
-    }
-    closedir(dir);
-  }
-
-  return overdue_log_names;
-}
-
-// Is log_cleaner enabled?
-// This option can be enabled by calling google::EnableLogCleaner(days)
-bool log_cleaner_enabled_;
-int log_cleaner_overdue_days_ = 7;
-
-} // namespace
-
-
-namespace {
-
-LogFileObject::LogFileObject(LogSeverity severity,
-                             const char* base_filename)
-  : base_filename_selected_(base_filename != NULL),
-    base_filename_((base_filename != NULL) ? base_filename : ""),
-    symlink_basename_(glog_internal_namespace_::ProgramInvocationShortName()),
-    filename_extension_(),
-    file_(NULL),
-    severity_(severity),
-    bytes_since_flush_(0),
-    dropped_mem_length_(0),
-    file_length_(0),
-    rollover_attempt_(kRolloverAttemptFrequency-1),
-    next_flush_time_(0) {
-  assert(severity >= 0);
-  assert(severity < NUM_SEVERITIES);
-}
-
-LogFileObject::~LogFileObject() {
-  MutexLock l(&lock_);
-  if (file_ != NULL) {
-    fclose(file_);
-    file_ = NULL;
-  }
-}
-
-void LogFileObject::SetBasename(const char* basename) {
-  MutexLock l(&lock_);
-  base_filename_selected_ = true;
-  if (base_filename_ != basename) {
-    // Get rid of old log file since we are changing names
-    if (file_ != NULL) {
-      fclose(file_);
-      file_ = NULL;
-      rollover_attempt_ = kRolloverAttemptFrequency-1;
-    }
-    base_filename_ = basename;
-  }
-}
-
-void LogFileObject::SetExtension(const char* ext) {
-  MutexLock l(&lock_);
-  if (filename_extension_ != ext) {
-    // Get rid of old log file since we are changing names
-    if (file_ != NULL) {
-      fclose(file_);
-      file_ = NULL;
-      rollover_attempt_ = kRolloverAttemptFrequency-1;
-    }
-    filename_extension_ = ext;
-  }
-}
-
-void LogFileObject::SetSymlinkBasename(const char* symlink_basename) {
-  MutexLock l(&lock_);
-  symlink_basename_ = symlink_basename;
-}
-
-void LogFileObject::Flush() {
-  MutexLock l(&lock_);
-  FlushUnlocked();
-}
-
-void LogFileObject::FlushUnlocked(){
-  if (file_ != NULL) {
-    fflush(file_);
-    bytes_since_flush_ = 0;
-  }
-  // Figure out when we are due for another flush.
-  const int64 next = (FLAGS_logbufsecs
-                      * static_cast<int64>(1000000));  // in usec
-  next_flush_time_ = CycleClock_Now() + UsecToCycles(next);
-}
-
-bool LogFileObject::CreateLogfile(const string& time_pid_string) {
-  string string_filename = base_filename_+filename_extension_;
-  if (FLAGS_timestamp_in_logfile_name) {
-    string_filename += time_pid_string;
-  }
-  const char* filename = string_filename.c_str();
-  //only write to files, create if non-existant.
-  int flags = O_WRONLY | O_CREAT;
-  if (FLAGS_timestamp_in_logfile_name) {
-    //demand that the file is unique for our timestamp (fail if it exists).
-    flags = flags | O_EXCL;
-  }
-  int fd = open(filename, flags, FLAGS_logfile_mode);
-  if (fd == -1) return false;
-#ifdef HAVE_FCNTL
-  // Mark the file close-on-exec. We don't really care if this fails
-  fcntl(fd, F_SETFD, FD_CLOEXEC);
-
-  // Mark the file as exclusive write access to avoid two clients logging to the
-  // same file. This applies particularly when !FLAGS_timestamp_in_logfile_name
-  // (otherwise open would fail because the O_EXCL flag on similar filename).
-  // locks are released on unlock or close() automatically, only after log is
-  // released.
-  // This will work after a fork as it is not inherited (not stored in the fd).
-  // Lock will not be lost because the file is opened with exclusive lock (write)
-  // and we will never read from it inside the process.
-  // TODO windows implementation of this (as flock is not available on mingw).
-  static struct flock w_lock;
-
-  w_lock.l_type = F_WRLCK;
-  w_lock.l_start = 0;
-  w_lock.l_whence = SEEK_SET;
-  w_lock.l_len = 0;
-
-  int wlock_ret = fcntl(fd, F_SETLK, &w_lock);
-  if (wlock_ret == -1) {
-      close(fd); //as we are failing already, do not check errors here
-      return false;
-  }
-#endif
-
-  //fdopen in append mode so if the file exists it will fseek to the end
-  file_ = fdopen(fd, "a");  // Make a FILE*.
-  if (file_ == NULL) {  // Man, we're screwed!
-    close(fd);
-    if (FLAGS_timestamp_in_logfile_name) {
-      unlink(filename);  // Erase the half-baked evidence: an unusable log file, only if we just created it.
-    }
-    return false;
-  }
-#ifdef OS_WINDOWS
-  // https://github.com/golang/go/issues/27638 - make sure we seek to the end to append
-  // empirically replicated with wine over mingw build
-  if (!FLAGS_timestamp_in_logfile_name) {
-    if (fseek(file_, 0, SEEK_END) != 0) {
-      return false;
-    }
-  }
-#endif
-  // We try to create a symlink called <program_name>.<severity>,
-  // which is easier to use.  (Every time we create a new logfile,
-  // we destroy the old symlink and create a new one, so it always
-  // points to the latest logfile.)  If it fails, we're sad but it's
-  // no error.
-  if (!symlink_basename_.empty()) {
-    // take directory from filename
-    const char* slash = strrchr(filename, PATH_SEPARATOR);
-    const string linkname =
-      symlink_basename_ + '.' + LogSeverityNames[severity_];
-    string linkpath;
-    if ( slash ) linkpath = string(filename, slash-filename+1);  // get dirname
-    linkpath += linkname;
-    unlink(linkpath.c_str());                    // delete old one if it exists
-
-#if defined(OS_WINDOWS)
-    // TODO(hamaji): Create lnk file on Windows?
-#elif defined(HAVE_UNISTD_H)
-    // We must have unistd.h.
-    // Make the symlink be relative (in the same dir) so that if the
-    // entire log directory gets relocated the link is still valid.
-    const char *linkdest = slash ? (slash + 1) : filename;
-    if (symlink(linkdest, linkpath.c_str()) != 0) {
-      // silently ignore failures
-    }
-
-    // Make an additional link to the log file in a place specified by
-    // FLAGS_log_link, if indicated
-    if (!FLAGS_log_link.empty()) {
-      linkpath = FLAGS_log_link + "/" + linkname;
-      unlink(linkpath.c_str());                  // delete old one if it exists
-      if (symlink(filename, linkpath.c_str()) != 0) {
-        // silently ignore failures
-      }
-    }
-#endif
-  }
-
-  return true;  // Everything worked
-}
-
-void LogFileObject::Write(bool force_flush,
-                          time_t timestamp,
-                          const char* message,
-                          int message_len) {
-  MutexLock l(&lock_);
-
-  // We don't log if the base_name_ is "" (which means "don't write")
-  if (base_filename_selected_ && base_filename_.empty()) {
-    return;
-  }
-
-  if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() ||
-      PidHasChanged()) {
-    if (file_ != NULL) fclose(file_);
-    file_ = NULL;
-    file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0;
-    rollover_attempt_ = kRolloverAttemptFrequency-1;
-  }
-
-  // If there's no destination file, make one before outputting
-  if (file_ == NULL) {
-    // Try to rollover the log file every 32 log messages.  The only time
-    // this could matter would be when we have trouble creating the log
-    // file.  If that happens, we'll lose lots of log messages, of course!
-    if (++rollover_attempt_ != kRolloverAttemptFrequency) return;
-    rollover_attempt_ = 0;
-
-    struct ::tm tm_time;
-    localtime_r(&timestamp, &tm_time);
-
-    // The logfile's filename will have the date/time & pid in it
-    ostringstream time_pid_stream;
-    time_pid_stream.fill('0');
-    time_pid_stream << 1900+tm_time.tm_year
-                    << setw(2) << 1+tm_time.tm_mon
-                    << setw(2) << tm_time.tm_mday
-                    << '-'
-                    << setw(2) << tm_time.tm_hour
-                    << setw(2) << tm_time.tm_min
-                    << setw(2) << tm_time.tm_sec
-                    << '.'
-                    << GetMainThreadPid();
-    const string& time_pid_string = time_pid_stream.str();
-
-    if (base_filename_selected_) {
-      if (!CreateLogfile(time_pid_string)) {
-        perror("Could not create log file");
-        fprintf(stderr, "COULD NOT CREATE LOGFILE '%s'!\n",
-                time_pid_string.c_str());
-        return;
-      }
-    } else {
-      // If no base filename for logs of this severity has been set, use a
-      // default base filename of
-      // "<program name>.<hostname>.<user name>.log.<severity level>.".  So
-      // logfiles will have names like
-      // webserver.examplehost.root.log.INFO.19990817-150000.4354, where
-      // 19990817 is a date (1999 August 17), 150000 is a time (15:00:00),
-      // and 4354 is the pid of the logging process.  The date & time reflect
-      // when the file was created for output.
-      //
-      // Where does the file get put?  Successively try the directories
-      // "/tmp", and "."
-      string stripped_filename(
-          glog_internal_namespace_::ProgramInvocationShortName());
-      string hostname;
-      GetHostName(&hostname);
-
-      string uidname = MyUserName();
-      // We should not call CHECK() here because this function can be
-      // called after holding on to log_mutex. We don't want to
-      // attempt to hold on to the same mutex, and get into a
-      // deadlock. Simply use a name like invalid-user.
-      if (uidname.empty()) uidname = "invalid-user";
-
-      stripped_filename = stripped_filename+'.'+hostname+'.'
-                          +uidname+".log."
-                          +LogSeverityNames[severity_]+'.';
-      // We're going to (potentially) try to put logs in several different dirs
-      const vector<string> & log_dirs = GetLoggingDirectories();
-
-      // Go through the list of dirs, and try to create the log file in each
-      // until we succeed or run out of options
-      bool success = false;
-      for (vector<string>::const_iterator dir = log_dirs.begin();
-           dir != log_dirs.end();
-           ++dir) {
-        base_filename_ = *dir + "/" + stripped_filename;
-        if ( CreateLogfile(time_pid_string) ) {
-          success = true;
-          break;
-        }
-      }
-      // If we never succeeded, we have to give up
-      if ( success == false ) {
-        perror("Could not create logging file");
-        fprintf(stderr, "COULD NOT CREATE A LOGGINGFILE %s!",
-                time_pid_string.c_str());
-        return;
-      }
-    }
-
-    // Write a header message into the log file
-    ostringstream file_header_stream;
-    file_header_stream.fill('0');
-    file_header_stream << "Log file created at: "
-                       << 1900+tm_time.tm_year << '/'
-                       << setw(2) << 1+tm_time.tm_mon << '/'
-                       << setw(2) << tm_time.tm_mday
-                       << ' '
-                       << setw(2) << tm_time.tm_hour << ':'
-                       << setw(2) << tm_time.tm_min << ':'
-                       << setw(2) << tm_time.tm_sec << '\n'
-                       << "Running on machine: "
-                       << LogDestination::hostname() << '\n'
-                       << "Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu "
-                       << "threadid file:line] msg" << '\n';
-    const string& file_header_string = file_header_stream.str();
-
-    const int header_len = file_header_string.size();
-    fwrite(file_header_string.data(), 1, header_len, file_);
-    file_length_ += header_len;
-    bytes_since_flush_ += header_len;
-  }
- 
-  // Write to LOG file
-  if ( !stop_writing ) {
-    // fwrite() doesn't return an error when the disk is full, for
-    // messages that are less than 4096 bytes. When the disk is full,
-    // it returns the message length for messages that are less than
-    // 4096 bytes. fwrite() returns 4096 for message lengths that are
-    // greater than 4096, thereby indicating an error.
-    errno = 0;
-    fwrite(message, 1, message_len, file_);
-    if ( FLAGS_stop_logging_if_full_disk &&
-         errno == ENOSPC ) {  // disk full, stop writing to disk
-      stop_writing = true;  // until the disk is
-      return;
-    } else {
-      file_length_ += message_len;
-      bytes_since_flush_ += message_len;
-    }
-  } else {
-    if ( CycleClock_Now() >= next_flush_time_ )
-      stop_writing = false;  // check to see if disk has free space.
-    return;  // no need to flush
-  }
-
-  // See important msgs *now*.  Also, flush logs at least every 10^6 chars,
-  // or every "FLAGS_logbufsecs" seconds.
-  if ( force_flush ||
-       (bytes_since_flush_ >= 1000000) ||
-       (CycleClock_Now() >= next_flush_time_) ) {
-    FlushUnlocked();
-#ifdef OS_LINUX
-    // Only consider files >= 3MiB
-    if (FLAGS_drop_log_memory && file_length_ >= (3 << 20)) {
-      // Don't evict the most recent 1-2MiB so as not to impact a tailer
-      // of the log file and to avoid page rounding issue on linux < 4.7
-      uint32 total_drop_length = (file_length_ & ~((1 << 20) - 1)) - (1 << 20);
-      uint32 this_drop_length = total_drop_length - dropped_mem_length_;
-      if (this_drop_length >= (2 << 20)) {
-        // Only advise when >= 2MiB to drop
-# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
-        // 'posix_fadvise' introduced in API 21:
-        // * https://android.googlesource.com/platform/bionic/+/6880f936173081297be0dc12f687d341b86a4cfa/libc/libc.map.txt#732
-# else
-        posix_fadvise(fileno(file_), dropped_mem_length_, this_drop_length,
-                      POSIX_FADV_DONTNEED);
-# endif
-        dropped_mem_length_ = total_drop_length;
-      }
-    }
-#endif
-    // Perform clean up for old logs
-    if (log_cleaner_enabled_) {
-      const vector<string>& dirs = GetLoggingDirectories();
-      for (size_t i = 0; i < dirs.size(); i++) {
-        vector<string> logs = GetOverdueLogNames(dirs[i], log_cleaner_overdue_days_);
-        for (size_t j = 0; j < logs.size(); j++) {
-          static_cast<void>(unlink(logs[j].c_str()));
-        }
-      }
-    }
-  }
-}
-
-}  // namespace
-
-// Static log data space to avoid alloc failures in a LOG(FATAL)
-//
-// Since multiple threads may call LOG(FATAL), and we want to preserve
-// the data from the first call, we allocate two sets of space.  One
-// for exclusive use by the first thread, and one for shared use by
-// all other threads.
-static Mutex fatal_msg_lock;
-static CrashReason crash_reason;
-static bool fatal_msg_exclusive = true;
-static LogMessage::LogMessageData fatal_msg_data_exclusive;
-static LogMessage::LogMessageData fatal_msg_data_shared;
-
-#ifdef GLOG_THREAD_LOCAL_STORAGE
-// Static thread-local log data space to use, because typically at most one
-// LogMessageData object exists (in this case glog makes zero heap memory
-// allocations).
-static GLOG_THREAD_LOCAL_STORAGE bool thread_data_available = true;
-
-#ifdef HAVE_ALIGNED_STORAGE
-static GLOG_THREAD_LOCAL_STORAGE
-    std::aligned_storage<sizeof(LogMessage::LogMessageData),
-                         alignof(LogMessage::LogMessageData)>::type thread_msg_data;
-#else
-static GLOG_THREAD_LOCAL_STORAGE
-    char thread_msg_data[sizeof(void*) + sizeof(LogMessage::LogMessageData)];
-#endif  // HAVE_ALIGNED_STORAGE
-#endif  // defined(GLOG_THREAD_LOCAL_STORAGE)
-
-LogMessage::LogMessageData::LogMessageData()
-  : stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) {
-}
-
-LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
-                       int ctr, void (LogMessage::*send_method)())
-    : allocated_(NULL) {
-  Init(file, line, severity, send_method);
-  data_->stream_.set_ctr(ctr);
-}
-
-LogMessage::LogMessage(const char* file, int line,
-                       const CheckOpString& result)
-    : allocated_(NULL) {
-  Init(file, line, GLOG_FATAL, &LogMessage::SendToLog);
-  stream() << "Check failed: " << (*result.str_) << " ";
-}
-
-LogMessage::LogMessage(const char* file, int line)
-    : allocated_(NULL) {
-  Init(file, line, GLOG_INFO, &LogMessage::SendToLog);
-}
-
-LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
-    : allocated_(NULL) {
-  Init(file, line, severity, &LogMessage::SendToLog);
-}
-
-LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
-                       LogSink* sink, bool also_send_to_log)
-    : allocated_(NULL) {
-  Init(file, line, severity, also_send_to_log ? &LogMessage::SendToSinkAndLog :
-                                                &LogMessage::SendToSink);
-  data_->sink_ = sink;  // override Init()'s setting to NULL
-}
-
-LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
-                       vector<string> *outvec)
-    : allocated_(NULL) {
-  Init(file, line, severity, &LogMessage::SaveOrSendToLog);
-  data_->outvec_ = outvec; // override Init()'s setting to NULL
-}
-
-LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
-                       string *message)
-    : allocated_(NULL) {
-  Init(file, line, severity, &LogMessage::WriteToStringAndLog);
-  data_->message_ = message;  // override Init()'s setting to NULL
-}
-
-void LogMessage::Init(const char* file,
-                      int line,
-                      LogSeverity severity,
-                      void (LogMessage::*send_method)()) {
-  allocated_ = NULL;
-  if (severity != GLOG_FATAL || !exit_on_dfatal) {
-#ifdef GLOG_THREAD_LOCAL_STORAGE
-    // No need for locking, because this is thread local.
-    if (thread_data_available) {
-      thread_data_available = false;
-#ifdef HAVE_ALIGNED_STORAGE
-      data_ = new (&thread_msg_data) LogMessageData;
-#else
-      const uintptr_t kAlign = sizeof(void*) - 1;
-
-      char* align_ptr =
-          reinterpret_cast<char*>(reinterpret_cast<uintptr_t>(thread_msg_data + kAlign) & ~kAlign);
-      data_ = new (align_ptr) LogMessageData;
-      assert(reinterpret_cast<uintptr_t>(align_ptr) % sizeof(void*) == 0);
-#endif
-    } else {
-      allocated_ = new LogMessageData();
-      data_ = allocated_;
-    }
-#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
-    allocated_ = new LogMessageData();
-    data_ = allocated_;
-#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
-    data_->first_fatal_ = false;
-  } else {
-    MutexLock l(&fatal_msg_lock);
-    if (fatal_msg_exclusive) {
-      fatal_msg_exclusive = false;
-      data_ = &fatal_msg_data_exclusive;
-      data_->first_fatal_ = true;
-    } else {
-      data_ = &fatal_msg_data_shared;
-      data_->first_fatal_ = false;
-    }
-  }
-
-  stream().fill('0');
-  data_->preserved_errno_ = errno;
-  data_->severity_ = severity;
-  data_->line_ = line;
-  data_->send_method_ = send_method;
-  data_->sink_ = NULL;
-  data_->outvec_ = NULL;
-  WallTime now = WallTime_Now();
-  data_->timestamp_ = static_cast<time_t>(now);
-  localtime_r(&data_->timestamp_, &data_->tm_time_);
-  data_->usecs_ = static_cast<int32>((now - data_->timestamp_) * 1000000);
-
-  data_->num_chars_to_log_ = 0;
-  data_->num_chars_to_syslog_ = 0;
-  data_->basename_ = const_basename(file);
-  data_->fullname_ = file;
-  data_->has_been_flushed_ = false;
-
-  // If specified, prepend a prefix to each line.  For example:
-  //    I20201018 160715 f5d4fbb0 logging.cc:1153]
-  //    (log level, GMT year, month, date, time, thread_id, file basename, line)
-  // We exclude the thread_id for the default thread.
-  if (FLAGS_log_prefix && (line != kNoLogPrefix)) {
-    stream() << LogSeverityNames[severity][0]
-             << setw(4) << 1900+data_->tm_time_.tm_year
-             << setw(2) << 1+data_->tm_time_.tm_mon
-             << setw(2) << data_->tm_time_.tm_mday
-             << ' '
-             << setw(2) << data_->tm_time_.tm_hour  << ':'
-             << setw(2) << data_->tm_time_.tm_min   << ':'
-             << setw(2) << data_->tm_time_.tm_sec   << "."
-             << setw(6) << data_->usecs_
-             << ' '
-             << setfill(' ') << setw(5)
-             << static_cast<unsigned int>(GetTID()) << setfill('0')
-             << ' '
-             << data_->basename_ << ':' << data_->line_ << "] ";
-  }
-  data_->num_prefix_chars_ = data_->stream_.pcount();
-
-  if (!FLAGS_log_backtrace_at.empty()) {
-    char fileline[128];
-    snprintf(fileline, sizeof(fileline), "%s:%d", data_->basename_, line);
-#ifdef HAVE_STACKTRACE
-    if (!strcmp(FLAGS_log_backtrace_at.c_str(), fileline)) {
-      string stacktrace;
-      DumpStackTraceToString(&stacktrace);
-      stream() << " (stacktrace:\n" << stacktrace << ") ";
-    }
-#endif
-  }
-}
-
-LogMessage::~LogMessage() {
-  Flush();
-#ifdef GLOG_THREAD_LOCAL_STORAGE
-  if (data_ == static_cast<void*>(&thread_msg_data)) {
-    data_->~LogMessageData();
-    thread_data_available = true;
-  }
-  else {
-    delete allocated_;
-  }
-#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
-  delete allocated_;
-#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
-}
-
-int LogMessage::preserved_errno() const {
-  return data_->preserved_errno_;
-}
-
-ostream& LogMessage::stream() {
-  return data_->stream_;
-}
-
-// Flush buffered message, called by the destructor, or any other function
-// that needs to synchronize the log.
-void LogMessage::Flush() {
-  if (data_->has_been_flushed_ || data_->severity_ < FLAGS_minloglevel)
-    return;
-
-  data_->num_chars_to_log_ = data_->stream_.pcount();
-  data_->num_chars_to_syslog_ =
-    data_->num_chars_to_log_ - data_->num_prefix_chars_;
-
-  // Do we need to add a \n to the end of this message?
-  bool append_newline =
-      (data_->message_text_[data_->num_chars_to_log_-1] != '\n');
-  char original_final_char = '\0';
-
-  // If we do need to add a \n, we'll do it by violating the memory of the
-  // ostrstream buffer.  This is quick, and we'll make sure to undo our
-  // modification before anything else is done with the ostrstream.  It
-  // would be preferable not to do things this way, but it seems to be
-  // the best way to deal with this.
-  if (append_newline) {
-    original_final_char = data_->message_text_[data_->num_chars_to_log_];
-    data_->message_text_[data_->num_chars_to_log_++] = '\n';
-  }
-
-  // Prevent any subtle race conditions by wrapping a mutex lock around
-  // the actual logging action per se.
-  {
-    MutexLock l(&log_mutex);
-    (this->*(data_->send_method_))();
-    ++num_messages_[static_cast<int>(data_->severity_)];
-  }
-  LogDestination::WaitForSinks(data_);
-
-  if (append_newline) {
-    // Fix the ostrstream back how it was before we screwed with it.
-    // It's 99.44% certain that we don't need to worry about doing this.
-    data_->message_text_[data_->num_chars_to_log_-1] = original_final_char;
-  }
-
-  // If errno was already set before we enter the logging call, we'll
-  // set it back to that value when we return from the logging call.
-  // It happens often that we log an error message after a syscall
-  // failure, which can potentially set the errno to some other
-  // values.  We would like to preserve the original errno.
-  if (data_->preserved_errno_ != 0) {
-    errno = data_->preserved_errno_;
-  }
-
-  // Note that this message is now safely logged.  If we're asked to flush
-  // again, as a result of destruction, say, we'll do nothing on future calls.
-  data_->has_been_flushed_ = true;
-}
-
-// Copy of first FATAL log message so that we can print it out again
-// after all the stack traces.  To preserve legacy behavior, we don't
-// use fatal_msg_data_exclusive.
-static time_t fatal_time;
-static char fatal_message[256];
-
-void ReprintFatalMessage() {
-  if (fatal_message[0]) {
-    const int n = strlen(fatal_message);
-    if (!FLAGS_logtostderr) {
-      // Also write to stderr (don't color to avoid terminal checks)
-      WriteToStderr(fatal_message, n);
-    }
-    LogDestination::LogToAllLogfiles(GLOG_ERROR, fatal_time, fatal_message, n);
-  }
-}
-
-// L >= log_mutex (callers must hold the log_mutex).
-void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
-  static bool already_warned_before_initgoogle = false;
-
-  log_mutex.AssertHeld();
-
-  RAW_DCHECK(data_->num_chars_to_log_ > 0 &&
-             data_->message_text_[data_->num_chars_to_log_-1] == '\n', "");
-
-  // Messages of a given severity get logged to lower severity logs, too
-
-  if (!already_warned_before_initgoogle && !IsGoogleLoggingInitialized()) {
-    const char w[] = "WARNING: Logging before InitGoogleLogging() is "
-                     "written to STDERR\n";
-    WriteToStderr(w, strlen(w));
-    already_warned_before_initgoogle = true;
-  }
-
-  // global flag: never log to file if set.  Also -- don't log to a
-  // file if we haven't parsed the command line flags to get the
-  // program name.
-  if (FLAGS_logtostderr || !IsGoogleLoggingInitialized()) {
-    ColoredWriteToStderr(data_->severity_,
-                         data_->message_text_, data_->num_chars_to_log_);
-
-    // this could be protected by a flag if necessary.
-    LogDestination::LogToSinks(data_->severity_,
-                               data_->fullname_, data_->basename_,
-                               data_->line_, &data_->tm_time_,
-                               data_->message_text_ + data_->num_prefix_chars_,
-                               (data_->num_chars_to_log_ -
-                                data_->num_prefix_chars_ - 1),
-                               data_->usecs_);
-  } else {
-
-    // log this message to all log files of severity <= severity_
-    LogDestination::LogToAllLogfiles(data_->severity_, data_->timestamp_,
-                                     data_->message_text_,
-                                     data_->num_chars_to_log_);
-
-    LogDestination::MaybeLogToStderr(data_->severity_, data_->message_text_,
-                                     data_->num_chars_to_log_);
-    LogDestination::MaybeLogToEmail(data_->severity_, data_->message_text_,
-                                    data_->num_chars_to_log_);
-    LogDestination::LogToSinks(data_->severity_,
-                               data_->fullname_, data_->basename_,
-                               data_->line_, &data_->tm_time_,
-                               data_->message_text_ + data_->num_prefix_chars_,
-                               (data_->num_chars_to_log_
-                                - data_->num_prefix_chars_ - 1),
-                               data_->usecs_);
-    // NOTE: -1 removes trailing \n
-  }
-
-  // If we log a FATAL message, flush all the log destinations, then toss
-  // a signal for others to catch. We leave the logs in a state that
-  // someone else can use them (as long as they flush afterwards)
-  if (data_->severity_ == GLOG_FATAL && exit_on_dfatal) {
-    if (data_->first_fatal_) {
-      // Store crash information so that it is accessible from within signal
-      // handlers that may be invoked later.
-      RecordCrashReason(&crash_reason);
-      SetCrashReason(&crash_reason);
-
-      // Store shortened fatal message for other logs and GWQ status
-      const int copy = min<int>(data_->num_chars_to_log_,
-                                sizeof(fatal_message)-1);
-      memcpy(fatal_message, data_->message_text_, copy);
-      fatal_message[copy] = '\0';
-      fatal_time = data_->timestamp_;
-    }
-
-    if (!FLAGS_logtostderr) {
-      for (int i = 0; i < NUM_SEVERITIES; ++i) {
-        if ( LogDestination::log_destinations_[i] )
-          LogDestination::log_destinations_[i]->logger_->Write(true, 0, "", 0);
-      }
-    }
-
-    // release the lock that our caller (directly or indirectly)
-    // LogMessage::~LogMessage() grabbed so that signal handlers
-    // can use the logging facility. Alternately, we could add
-    // an entire unsafe logging interface to bypass locking
-    // for signal handlers but this seems simpler.
-    log_mutex.Unlock();
-    LogDestination::WaitForSinks(data_);
-
-    const char* message = "*** Check failure stack trace: ***\n";
-    if (write(STDERR_FILENO, message, strlen(message)) < 0) {
-      // Ignore errors.
-    }
-    Fail();
-  }
-}
-
-void LogMessage::RecordCrashReason(
-    glog_internal_namespace_::CrashReason* reason) {
-  reason->filename = fatal_msg_data_exclusive.fullname_;
-  reason->line_number = fatal_msg_data_exclusive.line_;
-  reason->message = fatal_msg_data_exclusive.message_text_ +
-                    fatal_msg_data_exclusive.num_prefix_chars_;
-#ifdef HAVE_STACKTRACE
-  // Retrieve the stack trace, omitting the logging frames that got us here.
-  reason->depth = GetStackTrace(reason->stack, ARRAYSIZE(reason->stack), 4);
-#else
-  reason->depth = 0;
-#endif
-}
-
-#ifdef HAVE___ATTRIBUTE__
-# define ATTRIBUTE_NORETURN __attribute__((noreturn))
-#else
-# define ATTRIBUTE_NORETURN
-#endif
-
-#if defined(OS_WINDOWS)
-__declspec(noreturn)
-#endif
-static void logging_fail() ATTRIBUTE_NORETURN;
-
-static void logging_fail() {
-  abort();
-}
-
-typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN;
-
-GOOGLE_GLOG_DLL_DECL
-logging_fail_func_t g_logging_fail_func = &logging_fail;
-
-void InstallFailureFunction(void (*fail_func)()) {
-  g_logging_fail_func = (logging_fail_func_t)fail_func;
-}
-
-void LogMessage::Fail() {
-  g_logging_fail_func();
-}
-
-// L >= log_mutex (callers must hold the log_mutex).
-void LogMessage::SendToSink() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
-  if (data_->sink_ != NULL) {
-    RAW_DCHECK(data_->num_chars_to_log_ > 0 &&
-               data_->message_text_[data_->num_chars_to_log_-1] == '\n', "");
-    data_->sink_->send(data_->severity_, data_->fullname_, data_->basename_,
-                       data_->line_, &data_->tm_time_,
-                       data_->message_text_ + data_->num_prefix_chars_,
-                       (data_->num_chars_to_log_ -
-                        data_->num_prefix_chars_ - 1),
-                       data_->usecs_);
-  }
-}
-
-// L >= log_mutex (callers must hold the log_mutex).
-void LogMessage::SendToSinkAndLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
-  SendToSink();
-  SendToLog();
-}
-
-// L >= log_mutex (callers must hold the log_mutex).
-void LogMessage::SaveOrSendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
-  if (data_->outvec_ != NULL) {
-    RAW_DCHECK(data_->num_chars_to_log_ > 0 &&
-               data_->message_text_[data_->num_chars_to_log_-1] == '\n', "");
-    // Omit prefix of message and trailing newline when recording in outvec_.
-    const char *start = data_->message_text_ + data_->num_prefix_chars_;
-    int len = data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1;
-    data_->outvec_->push_back(string(start, len));
-  } else {
-    SendToLog();
-  }
-}
-
-void LogMessage::WriteToStringAndLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
-  if (data_->message_ != NULL) {
-    RAW_DCHECK(data_->num_chars_to_log_ > 0 &&
-               data_->message_text_[data_->num_chars_to_log_-1] == '\n', "");
-    // Omit prefix of message and trailing newline when writing to message_.
-    const char *start = data_->message_text_ + data_->num_prefix_chars_;
-    int len = data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1;
-    data_->message_->assign(start, len);
-  }
-  SendToLog();
-}
-
-// L >= log_mutex (callers must hold the log_mutex).
-void LogMessage::SendToSyslogAndLog() {
-#ifdef HAVE_SYSLOG_H
-  // Before any calls to syslog(), make a single call to openlog()
-  static bool openlog_already_called = false;
-  if (!openlog_already_called) {
-    openlog(glog_internal_namespace_::ProgramInvocationShortName(),
-            LOG_CONS | LOG_NDELAY | LOG_PID,
-            LOG_USER);
-    openlog_already_called = true;
-  }
-
-  // This array maps Google severity levels to syslog levels
-  const int SEVERITY_TO_LEVEL[] = { LOG_INFO, LOG_WARNING, LOG_ERR, LOG_EMERG };
-  syslog(LOG_USER | SEVERITY_TO_LEVEL[static_cast<int>(data_->severity_)], "%.*s",
-         int(data_->num_chars_to_syslog_),
-         data_->message_text_ + data_->num_prefix_chars_);
-  SendToLog();
-#else
-  LOG(ERROR) << "No syslog support: message=" << data_->message_text_;
-#endif
-}
-
-base::Logger* base::GetLogger(LogSeverity severity) {
-  MutexLock l(&log_mutex);
-  return LogDestination::log_destination(severity)->logger_;
-}
-
-void base::SetLogger(LogSeverity severity, base::Logger* logger) {
-  MutexLock l(&log_mutex);
-  LogDestination::log_destination(severity)->logger_ = logger;
-}
-
-// L < log_mutex.  Acquires and releases mutex_.
-int64 LogMessage::num_messages(int severity) {
-  MutexLock l(&log_mutex);
-  return num_messages_[severity];
-}
-
-// Output the COUNTER value. This is only valid if ostream is a
-// LogStream.
-ostream& operator<<(ostream &os, const PRIVATE_Counter&) {
-#ifdef DISABLE_RTTI
-  LogMessage::LogStream *log = static_cast<LogMessage::LogStream*>(&os);
-#else
-  LogMessage::LogStream *log = dynamic_cast<LogMessage::LogStream*>(&os);
-#endif
-  CHECK(log && log == log->self())
-      << "You must not use COUNTER with non-glog ostream";
-  os << log->ctr();
-  return os;
-}
-
-ErrnoLogMessage::ErrnoLogMessage(const char* file, int line,
-                                 LogSeverity severity, int ctr,
-                                 void (LogMessage::*send_method)())
-    : LogMessage(file, line, severity, ctr, send_method) {
-}
-
-ErrnoLogMessage::~ErrnoLogMessage() {
-  // Don't access errno directly because it may have been altered
-  // while streaming the message.
-  stream() << ": " << StrError(preserved_errno()) << " ["
-           << preserved_errno() << "]";
-}
-
-void FlushLogFiles(LogSeverity min_severity) {
-  LogDestination::FlushLogFiles(min_severity);
-}
-
-void FlushLogFilesUnsafe(LogSeverity min_severity) {
-  LogDestination::FlushLogFilesUnsafe(min_severity);
-}
-
-void SetLogDestination(LogSeverity severity, const char* base_filename) {
-  LogDestination::SetLogDestination(severity, base_filename);
-}
-
-void SetLogSymlink(LogSeverity severity, const char* symlink_basename) {
-  LogDestination::SetLogSymlink(severity, symlink_basename);
-}
-
-LogSink::~LogSink() {
-}
-
-void LogSink::WaitTillSent() {
-  // noop default
-}
-
-string LogSink::ToString(LogSeverity severity, const char* file, int line,
-                         const struct ::tm* tm_time,
-                         const char* message, size_t message_len, int32 usecs) {
-  ostringstream stream(string(message, message_len));
-  stream.fill('0');
-
-  stream << LogSeverityNames[severity][0]
-         << setw(4) << 1900+tm_time->tm_year
-         << setw(2) << 1+tm_time->tm_mon
-         << setw(2) << tm_time->tm_mday
-         << ' '
-         << setw(2) << tm_time->tm_hour << ':'
-         << setw(2) << tm_time->tm_min << ':'
-         << setw(2) << tm_time->tm_sec << '.'
-         << setw(6) << usecs
-         << ' '
-         << setfill(' ') << setw(5) << GetTID() << setfill('0')
-         << ' '
-         << file << ':' << line << "] ";
-
-  stream << string(message, message_len);
-  return stream.str();
-}
-
-void AddLogSink(LogSink *destination) {
-  LogDestination::AddLogSink(destination);
-}
-
-void RemoveLogSink(LogSink *destination) {
-  LogDestination::RemoveLogSink(destination);
-}
-
-void SetLogFilenameExtension(const char* ext) {
-  LogDestination::SetLogFilenameExtension(ext);
-}
-
-void SetStderrLogging(LogSeverity min_severity) {
-  LogDestination::SetStderrLogging(min_severity);
-}
-
-void SetEmailLogging(LogSeverity min_severity, const char* addresses) {
-  LogDestination::SetEmailLogging(min_severity, addresses);
-}
-
-void LogToStderr() {
-  LogDestination::LogToStderr();
-}
-
-namespace base {
-namespace internal {
-
-bool GetExitOnDFatal();
-bool GetExitOnDFatal() {
-  MutexLock l(&log_mutex);
-  return exit_on_dfatal;
-}
-
-// Determines whether we exit the program for a LOG(DFATAL) message in
-// debug mode.  It does this by skipping the call to Fail/FailQuietly.
-// This is intended for testing only.
-//
-// This can have some effects on LOG(FATAL) as well.  Failure messages
-// are always allocated (rather than sharing a buffer), the crash
-// reason is not recorded, the "gwq" status message is not updated,
-// and the stack trace is not recorded.  The LOG(FATAL) *will* still
-// exit the program.  Since this function is used only in testing,
-// these differences are acceptable.
-void SetExitOnDFatal(bool value);
-void SetExitOnDFatal(bool value) {
-  MutexLock l(&log_mutex);
-  exit_on_dfatal = value;
-}
-
-}  // namespace internal
-}  // namespace base
-
-// Shell-escaping as we need to shell out ot /bin/mail.
-static const char kDontNeedShellEscapeChars[] =
-            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-            "abcdefghijklmnopqrstuvwxyz"
-            "0123456789+-_.=/:,@";
-
-static string ShellEscape(const string& src) {
-  string result;
-  if (!src.empty() &&  // empty string needs quotes
-      src.find_first_not_of(kDontNeedShellEscapeChars) == string::npos) {
-    // only contains chars that don't need quotes; it's fine
-    result.assign(src);
-  } else if (src.find_first_of('\'') == string::npos) {
-    // no single quotes; just wrap it in single quotes
-    result.assign("'");
-    result.append(src);
-    result.append("'");
-  } else {
-    // needs double quote escaping
-    result.assign("\"");
-    for (size_t i = 0; i < src.size(); ++i) {
-      switch (src[i]) {
-        case '\\':
-        case '$':
-        case '"':
-        case '`':
-          result.append("\\");
-      }
-      result.append(src, i, 1);
-    }
-    result.append("\"");
-  }
-  return result;
-}
-
-
-// use_logging controls whether the logging functions LOG/VLOG are used
-// to log errors.  It should be set to false when the caller holds the
-// log_mutex.
-static bool SendEmailInternal(const char*dest, const char *subject,
-                              const char*body, bool use_logging) {
-  if (dest && *dest) {
-    if ( use_logging ) {
-      VLOG(1) << "Trying to send TITLE:" << subject
-              << " BODY:" << body << " to " << dest;
-    } else {
-      fprintf(stderr, "Trying to send TITLE: %s BODY: %s to %s\n",
-              subject, body, dest);
-    }
-
-    string cmd =
-        FLAGS_logmailer + " -s" +
-        ShellEscape(subject) + " " + ShellEscape(dest);
-    VLOG(4) << "Mailing command: " << cmd;
-
-    FILE* pipe = popen(cmd.c_str(), "w");
-    if (pipe != NULL) {
-      // Add the body if we have one
-      if (body)
-        fwrite(body, sizeof(char), strlen(body), pipe);
-      bool ok = pclose(pipe) != -1;
-      if ( !ok ) {
-        if ( use_logging ) {
-          LOG(ERROR) << "Problems sending mail to " << dest << ": "
-                     << StrError(errno);
-        } else {
-          fprintf(stderr, "Problems sending mail to %s: %s\n",
-                  dest, StrError(errno).c_str());
-        }
-      }
-      return ok;
-    } else {
-      if ( use_logging ) {
-        LOG(ERROR) << "Unable to send mail to " << dest;
-      } else {
-        fprintf(stderr, "Unable to send mail to %s\n", dest);
-      }
-    }
-  }
-  return false;
-}
-
-bool SendEmail(const char*dest, const char *subject, const char*body){
-  return SendEmailInternal(dest, subject, body, true);
-}
-
-static void GetTempDirectories(vector<string>* list) {
-  list->clear();
-#ifdef OS_WINDOWS
-  // On windows we'll try to find a directory in this order:
-  //   C:/Documents & Settings/whomever/TEMP (or whatever GetTempPath() is)
-  //   C:/TMP/
-  //   C:/TEMP/
-  //   C:/WINDOWS/ or C:/WINNT/
-  //   .
-  char tmp[MAX_PATH];
-  if (GetTempPathA(MAX_PATH, tmp))
-    list->push_back(tmp);
-  list->push_back("C:\\tmp\\");
-  list->push_back("C:\\temp\\");
-#else
-  // Directories, in order of preference. If we find a dir that
-  // exists, we stop adding other less-preferred dirs
-  const char * candidates[] = {
-    // Non-null only during unittest/regtest
-    getenv("TEST_TMPDIR"),
-
-    // Explicitly-supplied temp dirs
-    getenv("TMPDIR"), getenv("TMP"),
-
-    // If all else fails
-    "/tmp",
-  };
-
-  for (size_t i = 0; i < ARRAYSIZE(candidates); i++) {
-    const char *d = candidates[i];
-    if (!d) continue;  // Empty env var
-
-    // Make sure we don't surprise anyone who's expecting a '/'
-    string dstr = d;
-    if (dstr[dstr.size() - 1] != '/') {
-      dstr += "/";
-    }
-    list->push_back(dstr);
-
-    struct stat statbuf;
-    if (!stat(d, &statbuf) && S_ISDIR(statbuf.st_mode)) {
-      // We found a dir that exists - we're done.
-      return;
-    }
-  }
-
-#endif
-}
-
-static vector<string>* logging_directories_list;
-
-const vector<string>& GetLoggingDirectories() {
-  // Not strictly thread-safe but we're called early in InitGoogle().
-  if (logging_directories_list == NULL) {
-    logging_directories_list = new vector<string>;
-
-    if ( !FLAGS_log_dir.empty() ) {
-      // A dir was specified, we should use it
-      logging_directories_list->push_back(FLAGS_log_dir.c_str());
-    } else {
-      GetTempDirectories(logging_directories_list);
-#ifdef OS_WINDOWS
-      char tmp[MAX_PATH];
-      if (GetWindowsDirectoryA(tmp, MAX_PATH))
-        logging_directories_list->push_back(tmp);
-      logging_directories_list->push_back(".\\");
-#else
-      logging_directories_list->push_back("./");
-#endif
-    }
-  }
-  return *logging_directories_list;
-}
-
-void TestOnly_ClearLoggingDirectoriesList() {
-  fprintf(stderr, "TestOnly_ClearLoggingDirectoriesList should only be "
-          "called from test code.\n");
-  delete logging_directories_list;
-  logging_directories_list = NULL;
-}
-
-void GetExistingTempDirectories(vector<string>* list) {
-  GetTempDirectories(list);
-  vector<string>::iterator i_dir = list->begin();
-  while( i_dir != list->end() ) {
-    // zero arg to access means test for existence; no constant
-    // defined on windows
-    if ( access(i_dir->c_str(), 0) ) {
-      i_dir = list->erase(i_dir);
-    } else {
-      ++i_dir;
-    }
-  }
-}
-
-void TruncateLogFile(const char *path, int64 limit, int64 keep) {
-#ifdef HAVE_UNISTD_H
-  struct stat statbuf;
-  const int kCopyBlockSize = 8 << 10;
-  char copybuf[kCopyBlockSize];
-  int64 read_offset, write_offset;
-  // Don't follow symlinks unless they're our own fd symlinks in /proc
-  int flags = O_RDWR;
-  // TODO(hamaji): Support other environments.
-#ifdef OS_LINUX
-  const char *procfd_prefix = "/proc/self/fd/";
-  if (strncmp(procfd_prefix, path, strlen(procfd_prefix))) flags |= O_NOFOLLOW;
-#endif
-
-  int fd = open(path, flags);
-  if (fd == -1) {
-    if (errno == EFBIG) {
-      // The log file in question has got too big for us to open. The
-      // real fix for this would be to compile logging.cc (or probably
-      // all of base/...) with -D_FILE_OFFSET_BITS=64 but that's
-      // rather scary.
-      // Instead just truncate the file to something we can manage
-      if (truncate(path, 0) == -1) {
-        PLOG(ERROR) << "Unable to truncate " << path;
-      } else {
-        LOG(ERROR) << "Truncated " << path << " due to EFBIG error";
-      }
-    } else {
-      PLOG(ERROR) << "Unable to open " << path;
-    }
-    return;
-  }
-
-  if (fstat(fd, &statbuf) == -1) {
-    PLOG(ERROR) << "Unable to fstat()";
-    goto out_close_fd;
-  }
-
-  // See if the path refers to a regular file bigger than the
-  // specified limit
-  if (!S_ISREG(statbuf.st_mode)) goto out_close_fd;
-  if (statbuf.st_size <= limit)  goto out_close_fd;
-  if (statbuf.st_size <= keep) goto out_close_fd;
-
-  // This log file is too large - we need to truncate it
-  LOG(INFO) << "Truncating " << path << " to " << keep << " bytes";
-
-  // Copy the last "keep" bytes of the file to the beginning of the file
-  read_offset = statbuf.st_size - keep;
-  write_offset = 0;
-  int bytesin, bytesout;
-  while ((bytesin = pread(fd, copybuf, sizeof(copybuf), read_offset)) > 0) {
-    bytesout = pwrite(fd, copybuf, bytesin, write_offset);
-    if (bytesout == -1) {
-      PLOG(ERROR) << "Unable to write to " << path;
-      break;
-    } else if (bytesout != bytesin) {
-      LOG(ERROR) << "Expected to write " << bytesin << ", wrote " << bytesout;
-    }
-    read_offset += bytesin;
-    write_offset += bytesout;
-  }
-  if (bytesin == -1) PLOG(ERROR) << "Unable to read from " << path;
-
-  // Truncate the remainder of the file. If someone else writes to the
-  // end of the file after our last read() above, we lose their latest
-  // data. Too bad ...
-  if (ftruncate(fd, write_offset) == -1) {
-    PLOG(ERROR) << "Unable to truncate " << path;
-  }
-
- out_close_fd:
-  close(fd);
-#else
-  LOG(ERROR) << "No log truncation support.";
-#endif
-}
-
-void TruncateStdoutStderr() {
-#ifdef HAVE_UNISTD_H
-  int64 limit = MaxLogSize() << 20;
-  int64 keep = 1 << 20;
-  TruncateLogFile("/proc/self/fd/1", limit, keep);
-  TruncateLogFile("/proc/self/fd/2", limit, keep);
-#else
-  LOG(ERROR) << "No log truncation support.";
-#endif
-}
-
-
-// Helper functions for string comparisons.
-#define DEFINE_CHECK_STROP_IMPL(name, func, expected)                   \
-  string* Check##func##expected##Impl(const char* s1, const char* s2,   \
-                                      const char* names) {              \
-    bool equal = s1 == s2 || (s1 && s2 && !func(s1, s2));               \
-    if (equal == expected) return NULL;                                 \
-    else {                                                              \
-      ostringstream ss;                                                 \
-      if (!s1) s1 = "";                                                 \
-      if (!s2) s2 = "";                                                 \
-      ss << #name " failed: " << names << " (" << s1 << " vs. " << s2 << ")"; \
-      return new string(ss.str());                                      \
-    }                                                                   \
-  }
-DEFINE_CHECK_STROP_IMPL(CHECK_STREQ, strcmp, true)
-DEFINE_CHECK_STROP_IMPL(CHECK_STRNE, strcmp, false)
-DEFINE_CHECK_STROP_IMPL(CHECK_STRCASEEQ, strcasecmp, true)
-DEFINE_CHECK_STROP_IMPL(CHECK_STRCASENE, strcasecmp, false)
-#undef DEFINE_CHECK_STROP_IMPL
-
-int posix_strerror_r(int err, char *buf, size_t len) {
-  // Sanity check input parameters
-  if (buf == NULL || len <= 0) {
-    errno = EINVAL;
-    return -1;
-  }
-
-  // Reset buf and errno, and try calling whatever version of strerror_r()
-  // is implemented by glibc
-  buf[0] = '\000';
-  int old_errno = errno;
-  errno = 0;
-  char *rc = reinterpret_cast<char *>(strerror_r(err, buf, len));
-
-  // Both versions set errno on failure
-  if (errno) {
-    // Should already be there, but better safe than sorry
-    buf[0]     = '\000';
-    return -1;
-  }
-  errno = old_errno;
-
-  // POSIX is vague about whether the string will be terminated, although
-  // is indirectly implies that typically ERANGE will be returned, instead
-  // of truncating the string. This is different from the GNU implementation.
-  // We play it safe by always terminating the string explicitly.
-  buf[len-1] = '\000';
-
-  // If the function succeeded, we can use its exit code to determine the
-  // semantics implemented by glibc
-  if (!rc) {
-    return 0;
-  } else {
-    // GNU semantics detected
-    if (rc == buf) {
-      return 0;
-    } else {
-      buf[0] = '\000';
-#if defined(OS_MACOSX) || defined(OS_FREEBSD) || defined(OS_OPENBSD)
-      if (reinterpret_cast<intptr_t>(rc) < sys_nerr) {
-        // This means an error on MacOSX or FreeBSD.
-        return -1;
-      }
-#endif
-      strncat(buf, rc, len-1);
-      return 0;
-    }
-  }
-}
-
-string StrError(int err) {
-  char buf[100];
-  int rc = posix_strerror_r(err, buf, sizeof(buf));
-  if ((rc < 0) || (buf[0] == '\000')) {
-    snprintf(buf, sizeof(buf), "Error number %d", err);
-  }
-  return buf;
-}
-
-LogMessageFatal::LogMessageFatal(const char* file, int line) :
-    LogMessage(file, line, GLOG_FATAL) {}
-
-LogMessageFatal::LogMessageFatal(const char* file, int line,
-                                 const CheckOpString& result) :
-    LogMessage(file, line, result) {}
-
-LogMessageFatal::~LogMessageFatal() {
-    Flush();
-    LogMessage::Fail();
-}
-
-namespace base {
-
-CheckOpMessageBuilder::CheckOpMessageBuilder(const char *exprtext)
-    : stream_(new ostringstream) {
-  *stream_ << exprtext << " (";
-}
-
-CheckOpMessageBuilder::~CheckOpMessageBuilder() {
-  delete stream_;
-}
-
-ostream* CheckOpMessageBuilder::ForVar2() {
-  *stream_ << " vs. ";
-  return stream_;
-}
-
-string* CheckOpMessageBuilder::NewString() {
-  *stream_ << ")";
-  return new string(stream_->str());
-}
-
-}  // namespace base
-
-template <>
-void MakeCheckOpValueString(std::ostream* os, const char& v) {
-  if (v >= 32 && v <= 126) {
-    (*os) << "'" << v << "'";
-  } else {
-    (*os) << "char value " << (short)v;
-  }
-}
-
-template <>
-void MakeCheckOpValueString(std::ostream* os, const signed char& v) {
-  if (v >= 32 && v <= 126) {
-    (*os) << "'" << v << "'";
-  } else {
-    (*os) << "signed char value " << (short)v;
-  }
-}
-
-template <>
-void MakeCheckOpValueString(std::ostream* os, const unsigned char& v) {
-  if (v >= 32 && v <= 126) {
-    (*os) << "'" << v << "'";
-  } else {
-    (*os) << "unsigned char value " << (unsigned short)v;
-  }
-}
-
-void InitGoogleLogging(const char* argv0) {
-  glog_internal_namespace_::InitGoogleLoggingUtilities(argv0);
-}
-
-void ShutdownGoogleLogging() {
-  glog_internal_namespace_::ShutdownGoogleLoggingUtilities();
-  LogDestination::DeleteLogDestinations();
-  delete logging_directories_list;
-  logging_directories_list = NULL;
-}
-
-void EnableLogCleaner(int overdue_days) {
-  log_cleaner_enabled_ = true;
-
-  // Setting overdue_days to 0 day should not be allowed!
-  // Since all logs will be deleted immediately, which will cause troubles.
-  if (overdue_days > 0) {
-    log_cleaner_overdue_days_ = overdue_days;
-  }
-}
-
-void DisableLogCleaner() {
-  log_cleaner_enabled_ = false;
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/logging_striplog_test.sh b/third_party/glog/src/logging_striplog_test.sh
deleted file mode 100755
index 73492bda7a..0000000000
--- a/third_party/glog/src/logging_striplog_test.sh
+++ /dev/null
@@ -1,79 +0,0 @@
-#! /bin/sh
-#
-# Copyright (c) 2007, Google 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:
-#
-#     * 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.
-#
-# Author: Sergey Ioffe
-
-get_strings () {
-    if test -e ".libs/$1"; then
-        binary=".libs/$1"
-    elif test -e "$1.exe"; then
-        binary="$1.exe"
-    else
-        echo "We coundn't find $1 binary."
-        exit 1
-    fi
-    
-    strings -n 10 $binary | sort | awk '/TESTMESSAGE/ {printf "%s ", $2}'
-}
-
-# Die if "$1" != "$2", print $3 as death reason
-check_eq () {
-    if [ "$1" != "$2" ]; then
-        echo "Check failed: '$1' == '$2' ${3:+ ($3)}"
-        exit 1
-    fi
-}
-
-die () {
-    echo $1
-    exit 1
-}
-
-# Check that the string literals are appropriately stripped. This will
-# not be the case in debug mode.
-
-mode=`GLOG_check_mode=1 ./logging_striptest0 2> /dev/null`
-if [ "$mode" = "opt" ];
-then
-    echo "In OPT mode"
-    check_eq "`get_strings logging_striptest0`" "COND ERROR FATAL INFO USAGE WARNING "
-    check_eq "`get_strings logging_striptest2`" "COND ERROR FATAL USAGE "
-    check_eq "`get_strings logging_striptest10`" "" 
-else
-    echo "In DBG mode; not checking strings"
-fi
-
-# Check that LOG(FATAL) aborts even for large STRIP_LOG
-
-./logging_striptest2 2>/dev/null && die "Did not abort for STRIP_LOG=2"
-./logging_striptest10 2>/dev/null && die "Did not abort for STRIP_LOG=10"
-
-echo "PASS"
diff --git a/third_party/glog/src/logging_striptest10.cc b/third_party/glog/src/logging_striptest10.cc
deleted file mode 100644
index f6e1078f39..0000000000
--- a/third_party/glog/src/logging_striptest10.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Sergey Ioffe
-
-#define GOOGLE_STRIP_LOG 10
-
-// Include the actual test.
-#include "logging_striptest_main.cc"
diff --git a/third_party/glog/src/logging_striptest2.cc b/third_party/glog/src/logging_striptest2.cc
deleted file mode 100644
index a64685c9e5..0000000000
--- a/third_party/glog/src/logging_striptest2.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Sergey Ioffe
-
-#define GOOGLE_STRIP_LOG 2
-
-// Include the actual test.
-#include "logging_striptest_main.cc"
diff --git a/third_party/glog/src/logging_striptest_main.cc b/third_party/glog/src/logging_striptest_main.cc
deleted file mode 100644
index 2fb91272a7..0000000000
--- a/third_party/glog/src/logging_striptest_main.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Sergey Ioffe
-
-// The common part of the striplog tests.
-
-#include <stdio.h>
-#include <string>
-#include <iosfwd>
-#include "glog/logging.h"
-#include "base/commandlineflags.h"
-#include "config.h"
-
-DECLARE_bool(logtostderr);
-GLOG_DEFINE_bool(check_mode, false, "Prints 'opt' or 'dbg'");
-
-using std::string;
-using namespace GOOGLE_NAMESPACE;
-
-int CheckNoReturn(bool b) {
-  string s;
-  if (b) {
-    LOG(FATAL) << "Fatal";
-  } else {
-    return 0;
-  }
-}
-
-struct A { };
-std::ostream &operator<<(std::ostream &str, const A&) {return str;}
-
-int main(int, char* argv[]) {
-  FLAGS_logtostderr = true;
-  InitGoogleLogging(argv[0]);
-  if (FLAGS_check_mode) {
-    printf("%s\n", DEBUG_MODE ? "dbg" : "opt");
-    return 0;
-  }
-  LOG(INFO) << "TESTMESSAGE INFO";
-  LOG(WARNING) << 2 << "something" << "TESTMESSAGE WARNING"
-               << 1 << 'c' << A() << std::endl;
-  LOG(ERROR) << "TESTMESSAGE ERROR";
-  bool flag = true;
-  (flag ? LOG(INFO) : LOG(ERROR)) << "TESTMESSAGE COND";
-  LOG(FATAL) << "TESTMESSAGE FATAL";
-}
diff --git a/third_party/glog/src/logging_unittest.cc b/third_party/glog/src/logging_unittest.cc
deleted file mode 100644
index 73d426b878..0000000000
--- a/third_party/glog/src/logging_unittest.cc
+++ /dev/null
@@ -1,1348 +0,0 @@
-// Copyright (c) 2002, Google 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:
-//
-//     * 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.
-//
-// Author: Ray Sidney
-
-#include "config_for_unittests.h"
-#include "utilities.h"
-
-#include <fcntl.h>
-#ifdef HAVE_GLOB_H
-# include <glob.h>
-#endif
-#include <sys/stat.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#ifdef HAVE_SYS_WAIT_H
-# include <sys/wait.h>
-#endif
-
-#include <iomanip>
-#include <iostream>
-#include <fstream>
-#include <memory>
-#include <queue>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "base/commandlineflags.h"
-#include "glog/logging.h"
-#include "glog/raw_logging.h"
-#include "googletest.h"
-
-DECLARE_string(log_backtrace_at);  // logging.cc
-
-#ifdef HAVE_LIB_GFLAGS
-#include <gflags/gflags.h>
-using namespace GFLAGS_NAMESPACE;
-#endif
-
-#ifdef HAVE_LIB_GMOCK
-#include <gmock/gmock.h>
-#include "mock-log.h"
-// Introduce several symbols from gmock.
-using testing::_;
-using testing::AnyNumber;
-using testing::HasSubstr;
-using testing::AllOf;
-using testing::StrNe;
-using testing::StrictMock;
-using testing::InitGoogleMock;
-using GOOGLE_NAMESPACE::glog_testing::ScopedMockLog;
-#endif
-
-using namespace std;
-using namespace GOOGLE_NAMESPACE;
-
-// Some non-advertised functions that we want to test or use.
-_START_GOOGLE_NAMESPACE_
-namespace base {
-namespace internal {
-bool GetExitOnDFatal();
-void SetExitOnDFatal(bool value);
-}  // namespace internal
-}  // namespace base
-_END_GOOGLE_NAMESPACE_
-
-static void TestLogging(bool check_counts);
-static void TestRawLogging();
-static void LogWithLevels(int v, int severity, bool err, bool alsoerr);
-static void TestLoggingLevels();
-static void TestLogString();
-static void TestLogSink();
-static void TestLogToString();
-static void TestLogSinkWaitTillSent();
-static void TestCHECK();
-static void TestDCHECK();
-static void TestSTREQ();
-static void TestBasename();
-static void TestBasenameAppendWhenNoTimestamp();
-static void TestTwoProcessesWrite();
-static void TestSymlink();
-static void TestExtension();
-static void TestWrapper();
-static void TestErrno();
-static void TestTruncate();
-static void TestCustomLoggerDeletionOnShutdown();
-
-static int x = -1;
-static void BM_Check1(int n) {
-  while (n-- > 0) {
-    CHECK_GE(n, x);
-    CHECK_GE(n, x);
-    CHECK_GE(n, x);
-    CHECK_GE(n, x);
-    CHECK_GE(n, x);
-    CHECK_GE(n, x);
-    CHECK_GE(n, x);
-    CHECK_GE(n, x);
-  }
-}
-BENCHMARK(BM_Check1);
-
-static void CheckFailure(int a, int b, const char* file, int line, const char* msg);
-static void BM_Check3(int n) {
-  while (n-- > 0) {
-    if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
-    if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
-    if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
-    if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
-    if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
-    if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
-    if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
-    if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
-  }
-}
-BENCHMARK(BM_Check3);
-
-static void BM_Check2(int n) {
-  if (n == 17) {
-    x = 5;
-  }
-  while (n-- > 0) {
-    CHECK(n >= x);
-    CHECK(n >= x);
-    CHECK(n >= x);
-    CHECK(n >= x);
-    CHECK(n >= x);
-    CHECK(n >= x);
-    CHECK(n >= x);
-    CHECK(n >= x);
-  }
-}
-BENCHMARK(BM_Check2);
-
-static void CheckFailure(int, int, const char* /* file */, int /* line */,
-                         const char* /* msg */) {
-}
-
-static void BM_logspeed(int n) {
-  while (n-- > 0) {
-    LOG(INFO) << "test message";
-  }
-}
-BENCHMARK(BM_logspeed);
-
-static void BM_vlog(int n) {
-  while (n-- > 0) {
-    VLOG(1) << "test message";
-  }
-}
-BENCHMARK(BM_vlog);
-
-int main(int argc, char **argv) {
-  FLAGS_colorlogtostderr = false;
-  FLAGS_timestamp_in_logfile_name = true;
-#ifdef HAVE_LIB_GFLAGS
-  ParseCommandLineFlags(&argc, &argv, true);
-#endif
-  // Make sure stderr is not buffered as stderr seems to be buffered
-  // on recent windows.
-  setbuf(stderr, NULL);
-
-  // Test some basics before InitGoogleLogging:
-  CaptureTestStderr();
-  LogWithLevels(FLAGS_v, FLAGS_stderrthreshold,
-                FLAGS_logtostderr, FLAGS_alsologtostderr);
-  LogWithLevels(0, 0, 0, 0);  // simulate "before global c-tors"
-  const string early_stderr = GetCapturedTestStderr();
-
-  InitGoogleLogging(argv[0]);
-
-  RunSpecifiedBenchmarks();
-
-  FLAGS_logtostderr = true;
-
-  InitGoogleTest(&argc, argv);
-#ifdef HAVE_LIB_GMOCK
-  InitGoogleMock(&argc, argv);
-#endif
-
-  // so that death tests run before we use threads
-  CHECK_EQ(RUN_ALL_TESTS(), 0);
-
-  CaptureTestStderr();
-
-  // re-emit early_stderr
-  LogMessage("dummy", LogMessage::kNoLogPrefix, GLOG_INFO).stream() << early_stderr;
-
-  TestLogging(true);
-  TestRawLogging();
-  TestLoggingLevels();
-  TestLogString();
-  TestLogSink();
-  TestLogToString();
-  TestLogSinkWaitTillSent();
-  TestCHECK();
-  TestDCHECK();
-  TestSTREQ();
-
-  // TODO: The golden test portion of this test is very flakey.
-  EXPECT_TRUE(
-      MungeAndDiffTestStderr(FLAGS_test_srcdir + "/src/logging_unittest.err"));
-
-  FLAGS_logtostderr = false;
-
-  TestBasename();
-  TestBasenameAppendWhenNoTimestamp();
-  TestTwoProcessesWrite();
-  TestSymlink();
-  TestExtension();
-  TestWrapper();
-  TestErrno();
-  TestTruncate();
-  TestCustomLoggerDeletionOnShutdown();
-
-  fprintf(stdout, "PASS\n");
-  return 0;
-}
-
-void TestLogging(bool check_counts) {
-  int64 base_num_infos   = LogMessage::num_messages(GLOG_INFO);
-  int64 base_num_warning = LogMessage::num_messages(GLOG_WARNING);
-  int64 base_num_errors  = LogMessage::num_messages(GLOG_ERROR);
-
-  LOG(INFO) << string("foo ") << "bar " << 10 << ' ' << 3.4;
-  for ( int i = 0; i < 10; ++i ) {
-    int old_errno = errno;
-    errno = i;
-    PLOG_EVERY_N(ERROR, 2) << "Plog every 2, iteration " << COUNTER;
-    errno = old_errno;
-
-    LOG_EVERY_N(ERROR, 3) << "Log every 3, iteration " << COUNTER << endl;
-    LOG_EVERY_N(ERROR, 4) << "Log every 4, iteration " << COUNTER << endl;
-
-    LOG_IF_EVERY_N(WARNING, true, 5) << "Log if every 5, iteration " << COUNTER;
-    LOG_IF_EVERY_N(WARNING, false, 3)
-        << "Log if every 3, iteration " << COUNTER;
-    LOG_IF_EVERY_N(INFO, true, 1) << "Log if every 1, iteration " << COUNTER;
-    LOG_IF_EVERY_N(ERROR, (i < 3), 2)
-        << "Log if less than 3 every 2, iteration " << COUNTER;
-  }
-  LOG_IF(WARNING, true) << "log_if this";
-  LOG_IF(WARNING, false) << "don't log_if this";
-
-  char s[] = "array";
-  LOG(INFO) << s;
-  const char const_s[] = "const array";
-  LOG(INFO) << const_s;
-  int j = 1000;
-  LOG(ERROR) << string("foo") << ' '<< j << ' ' << setw(10) << j << " "
-             << setw(1) << hex << j;
-
-  {
-    google::LogMessage outer(__FILE__, __LINE__, GLOG_ERROR);
-    outer.stream() << "outer";
-
-    LOG(ERROR) << "inner";
-  }
-
-  LogMessage("foo", LogMessage::kNoLogPrefix, GLOG_INFO).stream() << "no prefix";
-
-  if (check_counts) {
-    CHECK_EQ(base_num_infos   + 14, LogMessage::num_messages(GLOG_INFO));
-    CHECK_EQ(base_num_warning + 3,  LogMessage::num_messages(GLOG_WARNING));
-    CHECK_EQ(base_num_errors  + 17, LogMessage::num_messages(GLOG_ERROR));
-  }
-}
-
-static void NoAllocNewHook() {
-  LOG(FATAL) << "unexpected new";
-}
-
-struct NewHook {
-  NewHook() {
-    g_new_hook = &NoAllocNewHook;
-  }
-  ~NewHook() {
-    g_new_hook = NULL;
-  }
-};
-
-TEST(DeathNoAllocNewHook, logging) {
-  // tests that NewHook used below works
-  NewHook new_hook;
-  ASSERT_DEATH({
-    new int;
-  }, "unexpected new");
-}
-
-void TestRawLogging() {
-  string* foo = new string("foo ");
-  string huge_str(50000, 'a');
-
-  FlagSaver saver;
-
-  // Check that RAW loggging does not use mallocs.
-  NewHook new_hook;
-
-  RAW_LOG(INFO, "%s%s%d%c%f", foo->c_str(), "bar ", 10, ' ', 3.4);
-  char s[] = "array";
-  RAW_LOG(WARNING, "%s", s);
-  const char const_s[] = "const array";
-  RAW_LOG(INFO, "%s", const_s);
-  void* p = reinterpret_cast<void*>(PTR_TEST_VALUE);
-  RAW_LOG(INFO, "ptr %p", p);
-  p = NULL;
-  RAW_LOG(INFO, "ptr %p", p);
-  int j = 1000;
-  RAW_LOG(ERROR, "%s%d%c%010d%s%1x", foo->c_str(), j, ' ', j, " ", j);
-  RAW_VLOG(0, "foo %d", j);
-
-#ifdef NDEBUG
-  RAW_LOG(INFO, "foo %d", j);  // so that have same stderr to compare
-#else
-  RAW_DLOG(INFO, "foo %d", j);  // test RAW_DLOG in debug mode
-#endif
-
-  // test how long messages are chopped:
-  RAW_LOG(WARNING, "Huge string: %s", huge_str.c_str());
-  RAW_VLOG(0, "Huge string: %s", huge_str.c_str());
-
-  FLAGS_v = 0;
-  RAW_LOG(INFO, "log");
-  RAW_VLOG(0, "vlog 0 on");
-  RAW_VLOG(1, "vlog 1 off");
-  RAW_VLOG(2, "vlog 2 off");
-  RAW_VLOG(3, "vlog 3 off");
-  FLAGS_v = 2;
-  RAW_LOG(INFO, "log");
-  RAW_VLOG(1, "vlog 1 on");
-  RAW_VLOG(2, "vlog 2 on");
-  RAW_VLOG(3, "vlog 3 off");
-
-#ifdef NDEBUG
-  RAW_DCHECK(1 == 2, " RAW_DCHECK's shouldn't be compiled in normal mode");
-#endif
-
-  RAW_CHECK(1 == 1, "should be ok");
-  RAW_DCHECK(true, "should be ok");
-
-  delete foo;
-}
-
-void LogWithLevels(int v, int severity, bool err, bool alsoerr) {
-  RAW_LOG(INFO,
-          "Test: v=%d stderrthreshold=%d logtostderr=%d alsologtostderr=%d",
-          v, severity, err, alsoerr);
-
-  FlagSaver saver;
-
-  FLAGS_v = v;
-  FLAGS_stderrthreshold = severity;
-  FLAGS_logtostderr = err;
-  FLAGS_alsologtostderr = alsoerr;
-
-  RAW_VLOG(-1, "vlog -1");
-  RAW_VLOG(0, "vlog 0");
-  RAW_VLOG(1, "vlog 1");
-  RAW_LOG(INFO, "log info");
-  RAW_LOG(WARNING, "log warning");
-  RAW_LOG(ERROR, "log error");
-
-  VLOG(-1) << "vlog -1";
-  VLOG(0) << "vlog 0";
-  VLOG(1) << "vlog 1";
-  LOG(INFO) << "log info";
-  LOG(WARNING) << "log warning";
-  LOG(ERROR) << "log error";
-
-  VLOG_IF(-1, true) << "vlog_if -1";
-  VLOG_IF(-1, false) << "don't vlog_if -1";
-  VLOG_IF(0, true) << "vlog_if 0";
-  VLOG_IF(0, false) << "don't vlog_if 0";
-  VLOG_IF(1, true) << "vlog_if 1";
-  VLOG_IF(1, false) << "don't vlog_if 1";
-  LOG_IF(INFO, true) << "log_if info";
-  LOG_IF(INFO, false) << "don't log_if info";
-  LOG_IF(WARNING, true) << "log_if warning";
-  LOG_IF(WARNING, false) << "don't log_if warning";
-  LOG_IF(ERROR, true) << "log_if error";
-  LOG_IF(ERROR, false) << "don't log_if error";
-
-  int c;
-  c = 1; VLOG_IF(100, c -= 2) << "vlog_if 100 expr"; EXPECT_EQ(c, -1);
-  c = 1; VLOG_IF(0, c -= 2) << "vlog_if 0 expr"; EXPECT_EQ(c, -1);
-  c = 1; LOG_IF(INFO, c -= 2) << "log_if info expr"; EXPECT_EQ(c, -1);
-  c = 1; LOG_IF(ERROR, c -= 2) << "log_if error expr"; EXPECT_EQ(c, -1);
-  c = 2; VLOG_IF(0, c -= 2) << "don't vlog_if 0 expr"; EXPECT_EQ(c, 0);
-  c = 2; LOG_IF(ERROR, c -= 2) << "don't log_if error expr"; EXPECT_EQ(c, 0);
-
-  c = 3; LOG_IF_EVERY_N(INFO, c -= 4, 1) << "log_if info every 1 expr";
-  EXPECT_EQ(c, -1);
-  c = 3; LOG_IF_EVERY_N(ERROR, c -= 4, 1) << "log_if error every 1 expr";
-  EXPECT_EQ(c, -1);
-  c = 4; LOG_IF_EVERY_N(ERROR, c -= 4, 3) << "don't log_if info every 3 expr";
-  EXPECT_EQ(c, 0);
-  c = 4; LOG_IF_EVERY_N(ERROR, c -= 4, 3) << "don't log_if error every 3 expr";
-  EXPECT_EQ(c, 0);
-  c = 5; VLOG_IF_EVERY_N(0, c -= 4, 1) << "vlog_if 0 every 1 expr";
-  EXPECT_EQ(c, 1);
-  c = 5; VLOG_IF_EVERY_N(100, c -= 4, 3) << "vlog_if 100 every 3 expr";
-  EXPECT_EQ(c, 1);
-  c = 6; VLOG_IF_EVERY_N(0, c -= 6, 1) << "don't vlog_if 0 every 1 expr";
-  EXPECT_EQ(c, 0);
-  c = 6; VLOG_IF_EVERY_N(100, c -= 6, 3) << "don't vlog_if 100 every 1 expr";
-  EXPECT_EQ(c, 0);
-}
-
-void TestLoggingLevels() {
-  LogWithLevels(0, GLOG_INFO, false, false);
-  LogWithLevels(1, GLOG_INFO, false, false);
-  LogWithLevels(-1, GLOG_INFO, false, false);
-  LogWithLevels(0, GLOG_WARNING, false, false);
-  LogWithLevels(0, GLOG_ERROR, false, false);
-  LogWithLevels(0, GLOG_FATAL, false, false);
-  LogWithLevels(0, GLOG_FATAL, true, false);
-  LogWithLevels(0, GLOG_FATAL, false, true);
-  LogWithLevels(1, GLOG_WARNING, false, false);
-  LogWithLevels(1, GLOG_FATAL, false, true);
-}
-
-TEST(DeathRawCHECK, logging) {
-  ASSERT_DEATH(RAW_CHECK(false, "failure 1"),
-               "RAW: Check false failed: failure 1");
-  ASSERT_DEBUG_DEATH(RAW_DCHECK(1 == 2, "failure 2"),
-               "RAW: Check 1 == 2 failed: failure 2");
-}
-
-void TestLogString() {
-  vector<string> errors;
-  vector<string> *no_errors = NULL;
-
-  LOG_STRING(INFO, &errors) << "LOG_STRING: " << "collected info";
-  LOG_STRING(WARNING, &errors) << "LOG_STRING: " << "collected warning";
-  LOG_STRING(ERROR, &errors) << "LOG_STRING: " << "collected error";
-
-  LOG_STRING(INFO, no_errors) << "LOG_STRING: " << "reported info";
-  LOG_STRING(WARNING, no_errors) << "LOG_STRING: " << "reported warning";
-  LOG_STRING(ERROR, NULL) << "LOG_STRING: " << "reported error";
-
-  for (size_t i = 0; i < errors.size(); ++i) {
-    LOG(INFO) << "Captured by LOG_STRING:  " << errors[i];
-  }
-}
-
-void TestLogToString() {
-  string error;
-  string* no_error = NULL;
-
-  LOG_TO_STRING(INFO, &error) << "LOG_TO_STRING: " << "collected info";
-  LOG(INFO) << "Captured by LOG_TO_STRING:  " << error;
-  LOG_TO_STRING(WARNING, &error) << "LOG_TO_STRING: " << "collected warning";
-  LOG(INFO) << "Captured by LOG_TO_STRING:  " << error;
-  LOG_TO_STRING(ERROR, &error) << "LOG_TO_STRING: " << "collected error";
-  LOG(INFO) << "Captured by LOG_TO_STRING:  " << error;
-
-  LOG_TO_STRING(INFO, no_error) << "LOG_TO_STRING: " << "reported info";
-  LOG_TO_STRING(WARNING, no_error) << "LOG_TO_STRING: " << "reported warning";
-  LOG_TO_STRING(ERROR, NULL) << "LOG_TO_STRING: " << "reported error";
-}
-
-class TestLogSinkImpl : public LogSink {
- public:
-  vector<string> errors;
-  virtual void send(LogSeverity severity, const char* /* full_filename */,
-                    const char* base_filename, int line,
-                    const struct tm* tm_time,
-                    const char* message, size_t message_len, int usecs) {
-    errors.push_back(
-      ToString(severity, base_filename, line, tm_time, message, message_len, usecs));
-  }
-  virtual void send(LogSeverity severity, const char* full_filename,
-                    const char* base_filename, int line,
-                    const struct tm* tm_time,
-                    const char* message, size_t message_len) {
-    send(severity, full_filename, base_filename, line,
-         tm_time, message, message_len, 0);
-  }
-};
-
-void TestLogSink() {
-  TestLogSinkImpl sink;
-  LogSink *no_sink = NULL;
-
-  LOG_TO_SINK(&sink, INFO) << "LOG_TO_SINK: " << "collected info";
-  LOG_TO_SINK(&sink, WARNING) << "LOG_TO_SINK: " << "collected warning";
-  LOG_TO_SINK(&sink, ERROR) << "LOG_TO_SINK: " << "collected error";
-
-  LOG_TO_SINK(no_sink, INFO) << "LOG_TO_SINK: " << "reported info";
-  LOG_TO_SINK(no_sink, WARNING) << "LOG_TO_SINK: " << "reported warning";
-  LOG_TO_SINK(NULL, ERROR) << "LOG_TO_SINK: " << "reported error";
-
-  LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, INFO)
-      << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "collected info";
-  LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, WARNING)
-      << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "collected warning";
-  LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, ERROR)
-      << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "collected error";
-
-  LOG_TO_SINK_BUT_NOT_TO_LOGFILE(no_sink, INFO)
-      << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "thrashed info";
-  LOG_TO_SINK_BUT_NOT_TO_LOGFILE(no_sink, WARNING)
-      << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "thrashed warning";
-  LOG_TO_SINK_BUT_NOT_TO_LOGFILE(NULL, ERROR)
-      << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "thrashed error";
-
-  LOG(INFO) << "Captured by LOG_TO_SINK:";
-  for (size_t i = 0; i < sink.errors.size(); ++i) {
-    LogMessage("foo", LogMessage::kNoLogPrefix, GLOG_INFO).stream()
-      << sink.errors[i];
-  }
-}
-
-// For testing using CHECK*() on anonymous enums.
-enum {
-  CASE_A,
-  CASE_B
-};
-
-void TestCHECK() {
-  // Tests using CHECK*() on int values.
-  CHECK(1 == 1);
-  CHECK_EQ(1, 1);
-  CHECK_NE(1, 2);
-  CHECK_GE(1, 1);
-  CHECK_GE(2, 1);
-  CHECK_LE(1, 1);
-  CHECK_LE(1, 2);
-  CHECK_GT(2, 1);
-  CHECK_LT(1, 2);
-
-  // Tests using CHECK*() on anonymous enums.
-  // Apple's GCC doesn't like this.
-#if !defined(OS_MACOSX)
-  CHECK_EQ(CASE_A, CASE_A);
-  CHECK_NE(CASE_A, CASE_B);
-  CHECK_GE(CASE_A, CASE_A);
-  CHECK_GE(CASE_B, CASE_A);
-  CHECK_LE(CASE_A, CASE_A);
-  CHECK_LE(CASE_A, CASE_B);
-  CHECK_GT(CASE_B, CASE_A);
-  CHECK_LT(CASE_A, CASE_B);
-#endif
-}
-
-void TestDCHECK() {
-#ifdef NDEBUG
-  DCHECK( 1 == 2 ) << " DCHECK's shouldn't be compiled in normal mode";
-#endif
-  DCHECK( 1 == 1 );
-  DCHECK_EQ(1, 1);
-  DCHECK_NE(1, 2);
-  DCHECK_GE(1, 1);
-  DCHECK_GE(2, 1);
-  DCHECK_LE(1, 1);
-  DCHECK_LE(1, 2);
-  DCHECK_GT(2, 1);
-  DCHECK_LT(1, 2);
-
-  int64* orig_ptr = new int64;
-  int64* ptr = DCHECK_NOTNULL(orig_ptr);
-  CHECK_EQ(ptr, orig_ptr);
-  delete orig_ptr;
-}
-
-void TestSTREQ() {
-  CHECK_STREQ("this", "this");
-  CHECK_STREQ(NULL, NULL);
-  CHECK_STRCASEEQ("this", "tHiS");
-  CHECK_STRCASEEQ(NULL, NULL);
-  CHECK_STRNE("this", "tHiS");
-  CHECK_STRNE("this", NULL);
-  CHECK_STRCASENE("this", "that");
-  CHECK_STRCASENE(NULL, "that");
-  CHECK_STREQ((string("a")+"b").c_str(), "ab");
-  CHECK_STREQ(string("test").c_str(),
-              (string("te") + string("st")).c_str());
-}
-
-TEST(DeathSTREQ, logging) {
-  ASSERT_DEATH(CHECK_STREQ(NULL, "this"), "");
-  ASSERT_DEATH(CHECK_STREQ("this", "siht"), "");
-  ASSERT_DEATH(CHECK_STRCASEEQ(NULL, "siht"), "");
-  ASSERT_DEATH(CHECK_STRCASEEQ("this", "siht"), "");
-  ASSERT_DEATH(CHECK_STRNE(NULL, NULL), "");
-  ASSERT_DEATH(CHECK_STRNE("this", "this"), "");
-  ASSERT_DEATH(CHECK_STREQ((string("a")+"b").c_str(), "abc"), "");
-}
-
-TEST(CheckNOTNULL, Simple) {
-  int64 t;
-  void *ptr = static_cast<void *>(&t);
-  void *ref = CHECK_NOTNULL(ptr);
-  EXPECT_EQ(ptr, ref);
-  CHECK_NOTNULL(reinterpret_cast<char *>(ptr));
-  CHECK_NOTNULL(reinterpret_cast<unsigned char *>(ptr));
-  CHECK_NOTNULL(reinterpret_cast<int *>(ptr));
-  CHECK_NOTNULL(reinterpret_cast<int64 *>(ptr));
-}
-
-TEST(DeathCheckNN, Simple) {
-  ASSERT_DEATH(CHECK_NOTNULL(static_cast<void *>(NULL)), "");
-}
-
-// Get list of file names that match pattern
-static void GetFiles(const string& pattern, vector<string>* files) {
-  files->clear();
-#if defined(HAVE_GLOB_H)
-  glob_t g;
-  const int r = glob(pattern.c_str(), 0, NULL, &g);
-  CHECK((r == 0) || (r == GLOB_NOMATCH)) << ": error matching " << pattern;
-  for (size_t i = 0; i < g.gl_pathc; i++) {
-    files->push_back(string(g.gl_pathv[i]));
-  }
-  globfree(&g);
-#elif defined(OS_WINDOWS)
-  WIN32_FIND_DATAA data;
-  HANDLE handle = FindFirstFileA(pattern.c_str(), &data);
-  size_t index = pattern.rfind('\\');
-  if (index == string::npos) {
-    LOG(FATAL) << "No directory separator.";
-  }
-  const string dirname = pattern.substr(0, index + 1);
-  if (handle == INVALID_HANDLE_VALUE) {
-    // Finding no files is OK.
-    return;
-  }
-  do {
-    files->push_back(dirname + data.cFileName);
-  } while (FindNextFileA(handle, &data));
-  BOOL result = FindClose(handle);
-  LOG_SYSRESULT(result);
-#else
-# error There is no way to do glob.
-#endif
-}
-
-// Delete files patching pattern
-static void DeleteFiles(const string& pattern) {
-  vector<string> files;
-  GetFiles(pattern, &files);
-  for (size_t i = 0; i < files.size(); i++) {
-    CHECK(unlink(files[i].c_str()) == 0) << ": " << strerror(errno);
-  }
-}
-
-//check string is in file (or is *NOT*, depending on optional checkInFileOrNot)
-static void CheckFile(const string& name, const string& expected_string, const bool checkInFileOrNot = true) {
-  vector<string> files;
-  GetFiles(name + "*", &files);
-  CHECK_EQ(files.size(), 1UL);
-
-  FILE* file = fopen(files[0].c_str(), "r");
-  CHECK(file != NULL) << ": could not open " << files[0];
-  char buf[1000];
-  while (fgets(buf, sizeof(buf), file) != NULL) {
-    char* first = strstr(buf, expected_string.c_str());
-    //if first == NULL, not found.
-    //Terser than if (checkInFileOrNot && first != NULL || !check...
-    if (checkInFileOrNot != (first == NULL)) {
-      fclose(file);
-      return;
-    }
-  }
-  fclose(file);
-  LOG(FATAL) << "Did " << (checkInFileOrNot? "not " : "") << "find " << expected_string << " in " << files[0];
-}
-
-static void TestBasename() {
-  fprintf(stderr, "==== Test setting log file basename\n");
-  const string dest = FLAGS_test_tmpdir + "/logging_test_basename";
-  DeleteFiles(dest + "*");
-
-  SetLogDestination(GLOG_INFO, dest.c_str());
-  LOG(INFO) << "message to new base";
-  FlushLogFiles(GLOG_INFO);
-
-  CheckFile(dest, "message to new base");
-
-  // Release file handle for the destination file to unlock the file in Windows.
-  LogToStderr();
-  DeleteFiles(dest + "*");
-}
-
-static void TestBasenameAppendWhenNoTimestamp() {
-  fprintf(stderr, "==== Test setting log file basename without timestamp and appending properly\n");
-  const string dest = FLAGS_test_tmpdir + "/logging_test_basename_append_when_no_timestamp";
-  DeleteFiles(dest + "*");
-
-  ofstream out(dest.c_str());
-  out << "test preexisting content" << endl;
-  out.close();
-
-  CheckFile(dest, "test preexisting content");
-
-  FLAGS_timestamp_in_logfile_name=false;
-  SetLogDestination(GLOG_INFO, dest.c_str());
-  LOG(INFO) << "message to new base, appending to preexisting file";
-  FlushLogFiles(GLOG_INFO);
-  FLAGS_timestamp_in_logfile_name=true;
-
-  //if the logging overwrites the file instead of appending it will fail.
-  CheckFile(dest, "test preexisting content");
-  CheckFile(dest, "message to new base, appending to preexisting file");
-
-  // Release file handle for the destination file to unlock the file in Windows.
-  LogToStderr();
-  DeleteFiles(dest + "*");
-}
-
-static void TestTwoProcessesWrite() {
-// test only implemented for platforms with fork & wait; the actual implementation relies on flock
-#if defined(HAVE_SYS_WAIT_H) && defined(HAVE_UNISTD_H) && defined(HAVE_FCNTL)
-  fprintf(stderr, "==== Test setting log file basename and two processes writing - second should fail\n");
-  const string dest = FLAGS_test_tmpdir + "/logging_test_basename_two_processes_writing";
-  DeleteFiles(dest + "*");
-
-  //make both processes write into the same file (easier test)
-  FLAGS_timestamp_in_logfile_name=false;
-  SetLogDestination(GLOG_INFO, dest.c_str());
-  LOG(INFO) << "message to new base, parent";
-  FlushLogFiles(GLOG_INFO);
-
-  pid_t pid = fork();
-  CHECK_ERR(pid);
-  if (pid == 0) {
-    LOG(INFO) << "message to new base, child - should only appear on STDERR not on the file";
-    ShutdownGoogleLogging(); //for children proc
-    exit(0);
-  } else if (pid > 0) {
-    wait(NULL);
-  }
-  FLAGS_timestamp_in_logfile_name=true;
-
-  CheckFile(dest, "message to new base, parent");
-  CheckFile(dest, "message to new base, child - should only appear on STDERR not on the file", false);
-
-  // Release
-  LogToStderr();
-  DeleteFiles(dest + "*");
-#endif
-}
-
-static void TestSymlink() {
-#ifndef OS_WINDOWS
-  fprintf(stderr, "==== Test setting log file symlink\n");
-  string dest = FLAGS_test_tmpdir + "/logging_test_symlink";
-  string sym = FLAGS_test_tmpdir + "/symlinkbase";
-  DeleteFiles(dest + "*");
-  DeleteFiles(sym + "*");
-
-  SetLogSymlink(GLOG_INFO, "symlinkbase");
-  SetLogDestination(GLOG_INFO, dest.c_str());
-  LOG(INFO) << "message to new symlink";
-  FlushLogFiles(GLOG_INFO);
-  CheckFile(sym, "message to new symlink");
-
-  DeleteFiles(dest + "*");
-  DeleteFiles(sym + "*");
-#endif
-}
-
-static void TestExtension() {
-  fprintf(stderr, "==== Test setting log file extension\n");
-  string dest = FLAGS_test_tmpdir + "/logging_test_extension";
-  DeleteFiles(dest + "*");
-
-  SetLogDestination(GLOG_INFO, dest.c_str());
-  SetLogFilenameExtension("specialextension");
-  LOG(INFO) << "message to new extension";
-  FlushLogFiles(GLOG_INFO);
-  CheckFile(dest, "message to new extension");
-
-  // Check that file name ends with extension
-  vector<string> filenames;
-  GetFiles(dest + "*", &filenames);
-  CHECK_EQ(filenames.size(), 1UL);
-  CHECK(strstr(filenames[0].c_str(), "specialextension") != NULL);
-
-  // Release file handle for the destination file to unlock the file in Windows.
-  LogToStderr();
-  DeleteFiles(dest + "*");
-}
-
-struct MyLogger : public base::Logger {
-  string data;
-
-  virtual void Write(bool /* should_flush */,
-                     time_t /* timestamp */,
-                     const char* message,
-                     int length) {
-    data.append(message, length);
-  }
-
-  virtual void Flush() { }
-
-  virtual uint32 LogSize() { return data.length(); }
-};
-
-static void TestWrapper() {
-  fprintf(stderr, "==== Test log wrapper\n");
-
-  MyLogger my_logger;
-  base::Logger* old_logger = base::GetLogger(GLOG_INFO);
-  base::SetLogger(GLOG_INFO, &my_logger);
-  LOG(INFO) << "Send to wrapped logger";
-  FlushLogFiles(GLOG_INFO);
-  base::SetLogger(GLOG_INFO, old_logger);
-
-  CHECK(strstr(my_logger.data.c_str(), "Send to wrapped logger") != NULL);
-}
-
-static void TestErrno() {
-  fprintf(stderr, "==== Test errno preservation\n");
-
-  errno = ENOENT;
-  TestLogging(false);
-  CHECK_EQ(errno, ENOENT);
-}
-
-static void TestOneTruncate(const char *path, int64 limit, int64 keep,
-                            int64 dsize, int64 ksize, int64 expect) {
-  int fd;
-  CHECK_ERR(fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600));
-
-  const char *discardstr = "DISCARDME!", *keepstr = "KEEPME!";
-  const size_t discard_size = strlen(discardstr), keep_size = strlen(keepstr);
-
-  // Fill the file with the requested data; first discard data, then kept data
-  int64 written = 0;
-  while (written < dsize) {
-    int bytes = min<int64>(dsize - written, discard_size);
-    CHECK_ERR(write(fd, discardstr, bytes));
-    written += bytes;
-  }
-  written = 0;
-  while (written < ksize) {
-    int bytes = min<int64>(ksize - written, keep_size);
-    CHECK_ERR(write(fd, keepstr, bytes));
-    written += bytes;
-  }
-
-  TruncateLogFile(path, limit, keep);
-
-  // File should now be shorter
-  struct stat statbuf;
-  CHECK_ERR(fstat(fd, &statbuf));
-  CHECK_EQ(statbuf.st_size, expect);
-  CHECK_ERR(lseek(fd, 0, SEEK_SET));
-
-  // File should contain the suffix of the original file
-  const size_t buf_size = statbuf.st_size + 1;
-  char* buf = new char[buf_size];
-  memset(buf, 0, buf_size);
-  CHECK_ERR(read(fd, buf, buf_size));
-
-  const char *p = buf;
-  int64 checked = 0;
-  while (checked < expect) {
-    int bytes = min<int64>(expect - checked, keep_size);
-    CHECK(!memcmp(p, keepstr, bytes));
-    checked += bytes;
-  }
-  close(fd);
-  delete[] buf;
-}
-
-static void TestTruncate() {
-#ifdef HAVE_UNISTD_H
-  fprintf(stderr, "==== Test log truncation\n");
-  string path = FLAGS_test_tmpdir + "/truncatefile";
-
-  // Test on a small file
-  TestOneTruncate(path.c_str(), 10, 10, 10, 10, 10);
-
-  // And a big file (multiple blocks to copy)
-  TestOneTruncate(path.c_str(), 2<<20, 4<<10, 3<<20, 4<<10, 4<<10);
-
-  // Check edge-case limits
-  TestOneTruncate(path.c_str(), 10, 20, 0, 20, 20);
-  TestOneTruncate(path.c_str(), 10, 0, 0, 0, 0);
-  TestOneTruncate(path.c_str(), 10, 50, 0, 10, 10);
-  TestOneTruncate(path.c_str(), 50, 100, 0, 30, 30);
-
-  // MacOSX 10.4 doesn't fail in this case.
-  // Windows doesn't have symlink.
-  // Let's just ignore this test for these cases.
-#if !defined(OS_MACOSX) && !defined(OS_WINDOWS)
-  // Through a symlink should fail to truncate
-  string linkname = path + ".link";
-  unlink(linkname.c_str());
-  CHECK_ERR(symlink(path.c_str(), linkname.c_str()));
-  TestOneTruncate(linkname.c_str(), 10, 10, 0, 30, 30);
-#endif
-
-  // The /proc/self path makes sense only for linux.
-#if defined(OS_LINUX)
-  // Through an open fd symlink should work
-  int fd;
-  CHECK_ERR(fd = open(path.c_str(), O_APPEND | O_WRONLY));
-  char fdpath[64];
-  snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", fd);
-  TestOneTruncate(fdpath, 10, 10, 10, 10, 10);
-#endif
-
-#endif
-}
-
-struct RecordDeletionLogger : public base::Logger {
-  RecordDeletionLogger(bool* set_on_destruction,
-                       base::Logger* wrapped_logger) :
-      set_on_destruction_(set_on_destruction),
-      wrapped_logger_(wrapped_logger)
-  {
-    *set_on_destruction_ = false;
-  }
-  virtual ~RecordDeletionLogger() {
-    *set_on_destruction_ = true;
-  }
-  virtual void Write(bool force_flush,
-                     time_t timestamp,
-                     const char* message,
-                     int length) {
-    wrapped_logger_->Write(force_flush, timestamp, message, length);
-  }
-  virtual void Flush() { wrapped_logger_->Flush(); }
-  virtual uint32 LogSize() { return wrapped_logger_->LogSize(); }
- private:
-  bool* set_on_destruction_;
-  base::Logger* wrapped_logger_;
-};
-
-static void TestCustomLoggerDeletionOnShutdown() {
-  bool custom_logger_deleted = false;
-  base::SetLogger(GLOG_INFO,
-                  new RecordDeletionLogger(&custom_logger_deleted,
-                                           base::GetLogger(GLOG_INFO)));
-  ShutdownGoogleLogging();
-  EXPECT_TRUE(custom_logger_deleted);
-}
-
-_START_GOOGLE_NAMESPACE_
-namespace glog_internal_namespace_ {
-extern  // in logging.cc
-bool SafeFNMatch_(const char* pattern, size_t patt_len,
-                  const char* str, size_t str_len);
-} // namespace glog_internal_namespace_
-using glog_internal_namespace_::SafeFNMatch_;
-_END_GOOGLE_NAMESPACE_
-
-static bool WrapSafeFNMatch(string pattern, string str) {
-  pattern += "abc";
-  str += "defgh";
-  return SafeFNMatch_(pattern.data(), pattern.size() - 3,
-                      str.data(), str.size() - 5);
-}
-
-TEST(SafeFNMatch, logging) {
-  CHECK(WrapSafeFNMatch("foo", "foo"));
-  CHECK(!WrapSafeFNMatch("foo", "bar"));
-  CHECK(!WrapSafeFNMatch("foo", "fo"));
-  CHECK(!WrapSafeFNMatch("foo", "foo2"));
-  CHECK(WrapSafeFNMatch("bar/foo.ext", "bar/foo.ext"));
-  CHECK(WrapSafeFNMatch("*ba*r/fo*o.ext*", "bar/foo.ext"));
-  CHECK(!WrapSafeFNMatch("bar/foo.ext", "bar/baz.ext"));
-  CHECK(!WrapSafeFNMatch("bar/foo.ext", "bar/foo"));
-  CHECK(!WrapSafeFNMatch("bar/foo.ext", "bar/foo.ext.zip"));
-  CHECK(WrapSafeFNMatch("ba?/*.ext", "bar/foo.ext"));
-  CHECK(WrapSafeFNMatch("ba?/*.ext", "baZ/FOO.ext"));
-  CHECK(!WrapSafeFNMatch("ba?/*.ext", "barr/foo.ext"));
-  CHECK(!WrapSafeFNMatch("ba?/*.ext", "bar/foo.ext2"));
-  CHECK(WrapSafeFNMatch("ba?/*", "bar/foo.ext2"));
-  CHECK(WrapSafeFNMatch("ba?/*", "bar/"));
-  CHECK(!WrapSafeFNMatch("ba?/?", "bar/"));
-  CHECK(!WrapSafeFNMatch("ba?/*", "bar"));
-}
-
-// TestWaitingLogSink will save messages here
-// No lock: Accessed only by TestLogSinkWriter thread
-// and after its demise by its creator.
-static vector<string> global_messages;
-
-// helper for TestWaitingLogSink below.
-// Thread that does the logic of TestWaitingLogSink
-// It's free to use LOG() itself.
-class TestLogSinkWriter : public Thread {
- public:
-
-  TestLogSinkWriter() : should_exit_(false) {
-    SetJoinable(true);
-    Start();
-  }
-
-  // Just buffer it (can't use LOG() here).
-  void Buffer(const string& message) {
-    mutex_.Lock();
-    RAW_LOG(INFO, "Buffering");
-    messages_.push(message);
-    mutex_.Unlock();
-    RAW_LOG(INFO, "Buffered");
-  }
-
-  // Wait for the buffer to clear (can't use LOG() here).
-  void Wait() {
-    RAW_LOG(INFO, "Waiting");
-    mutex_.Lock();
-    while (!NoWork()) {
-      mutex_.Unlock();
-      SleepForMilliseconds(1);
-      mutex_.Lock();
-    }
-    RAW_LOG(INFO, "Waited");
-    mutex_.Unlock();
-  }
-
-  // Trigger thread exit.
-  void Stop() {
-    MutexLock l(&mutex_);
-    should_exit_ = true;
-  }
-
- private:
-
-  // helpers ---------------
-
-  // For creating a "Condition".
-  bool NoWork() { return messages_.empty(); }
-  bool HaveWork() { return !messages_.empty() || should_exit_; }
-
-  // Thread body; CAN use LOG() here!
-  virtual void Run() {
-    while (1) {
-      mutex_.Lock();
-      while (!HaveWork()) {
-        mutex_.Unlock();
-        SleepForMilliseconds(1);
-        mutex_.Lock();
-      }
-      if (should_exit_ && messages_.empty()) {
-        mutex_.Unlock();
-        break;
-      }
-      // Give the main thread time to log its message,
-      // so that we get a reliable log capture to compare to golden file.
-      // Same for the other sleep below.
-      SleepForMilliseconds(20);
-      RAW_LOG(INFO, "Sink got a messages");  // only RAW_LOG under mutex_ here
-      string message = messages_.front();
-      messages_.pop();
-      // Normally this would be some more real/involved logging logic
-      // where LOG() usage can't be eliminated,
-      // e.g. pushing the message over with an RPC:
-      int messages_left = messages_.size();
-      mutex_.Unlock();
-      SleepForMilliseconds(20);
-      // May not use LOG while holding mutex_, because Buffer()
-      // acquires mutex_, and Buffer is called from LOG(),
-      // which has its own internal mutex:
-      // LOG()->LogToSinks()->TestWaitingLogSink::send()->Buffer()
-      LOG(INFO) << "Sink is sending out a message: " << message;
-      LOG(INFO) << "Have " << messages_left << " left";
-      global_messages.push_back(message);
-    }
-  }
-
-  // data ---------------
-
-  Mutex mutex_;
-  bool should_exit_;
-  queue<string> messages_;  // messages to be logged
-};
-
-// A log sink that exercises WaitTillSent:
-// it pushes data to a buffer and wakes up another thread to do the logging
-// (that other thread can than use LOG() itself),
-class TestWaitingLogSink : public LogSink {
- public:
-
-  TestWaitingLogSink() {
-    tid_ = pthread_self();  // for thread-specific behavior
-    AddLogSink(this);
-  }
-  ~TestWaitingLogSink() {
-    RemoveLogSink(this);
-    writer_.Stop();
-    writer_.Join();
-  }
-
-  // (re)define LogSink interface
-
-  virtual void send(LogSeverity severity, const char* /* full_filename */,
-                    const char* base_filename, int line,
-                    const struct tm* tm_time,
-                    const char* message, size_t message_len, int usecs) {
-    // Push it to Writer thread if we are the original logging thread.
-    // Note: Something like ThreadLocalLogSink is a better choice
-    //       to do thread-specific LogSink logic for real.
-    if (pthread_equal(tid_, pthread_self())) {
-      writer_.Buffer(ToString(severity, base_filename, line,
-                              tm_time, message, message_len, usecs));
-    }
-  }
-
-  virtual void send(LogSeverity severity, const char* full_filename,
-                    const char* base_filename, int line,
-                    const struct tm* tm_time,
-                    const char* message, size_t message_len) {
-    send(severity, full_filename, base_filename, line, tm_time, message, message_len);
-  }
-
-  virtual void WaitTillSent() {
-    // Wait for Writer thread if we are the original logging thread.
-    if (pthread_equal(tid_, pthread_self()))  writer_.Wait();
-  }
-
- private:
-
-  pthread_t tid_;
-  TestLogSinkWriter writer_;
-};
-
-// Check that LogSink::WaitTillSent can be used in the advertised way.
-// We also do golden-stderr comparison.
-static void TestLogSinkWaitTillSent() {
-  { TestWaitingLogSink sink;
-    // Sleeps give the sink threads time to do all their work,
-    // so that we get a reliable log capture to compare to the golden file.
-    LOG(INFO) << "Message 1";
-    SleepForMilliseconds(60);
-    LOG(ERROR) << "Message 2";
-    SleepForMilliseconds(60);
-    LOG(WARNING) << "Message 3";
-    SleepForMilliseconds(60);
-  }
-  for (size_t i = 0; i < global_messages.size(); ++i) {
-    LOG(INFO) << "Sink capture: " << global_messages[i];
-  }
-  CHECK_EQ(global_messages.size(), 3UL);
-}
-
-TEST(Strerror, logging) {
-  int errcode = EINTR;
-  char *msg = strdup(strerror(errcode));
-  const size_t buf_size = strlen(msg) + 1;
-  char *buf = new char[buf_size];
-  CHECK_EQ(posix_strerror_r(errcode, NULL, 0), -1);
-  buf[0] = 'A';
-  CHECK_EQ(posix_strerror_r(errcode, buf, 0), -1);
-  CHECK_EQ(buf[0], 'A');
-  CHECK_EQ(posix_strerror_r(errcode, NULL, buf_size), -1);
-#if defined(OS_MACOSX) || defined(OS_FREEBSD) || defined(OS_OPENBSD)
-  // MacOSX or FreeBSD considers this case is an error since there is
-  // no enough space.
-  CHECK_EQ(posix_strerror_r(errcode, buf, 1), -1);
-#else
-  CHECK_EQ(posix_strerror_r(errcode, buf, 1), 0);
-#endif
-  CHECK_STREQ(buf, "");
-  CHECK_EQ(posix_strerror_r(errcode, buf, buf_size), 0);
-  CHECK_STREQ(buf, msg);
-  delete[] buf;
-  CHECK_EQ(msg, StrError(errcode));
-  free(msg);
-}
-
-// Simple routines to look at the sizes of generated code for LOG(FATAL) and
-// CHECK(..) via objdump
-/*
-static void MyFatal() {
-  LOG(FATAL) << "Failed";
-}
-static void MyCheck(bool a, bool b) {
-  CHECK_EQ(a, b);
-}
-*/
-#ifdef HAVE_LIB_GMOCK
-
-TEST(DVLog, Basic) {
-  ScopedMockLog log;
-
-#if NDEBUG
-  // We are expecting that nothing is logged.
-  EXPECT_CALL(log, Log(_, _, _)).Times(0);
-#else
-  EXPECT_CALL(log, Log(INFO, __FILE__, "debug log"));
-#endif
-
-  FLAGS_v = 1;
-  DVLOG(1) << "debug log";
-}
-
-TEST(DVLog, V0) {
-  ScopedMockLog log;
-
-  // We are expecting that nothing is logged.
-  EXPECT_CALL(log, Log(_, _, _)).Times(0);
-
-  FLAGS_v = 0;
-  DVLOG(1) << "debug log";
-}
-
-TEST(LogAtLevel, Basic) {
-  ScopedMockLog log;
-
-  // The function version outputs "logging.h" as a file name.
-  EXPECT_CALL(log, Log(WARNING, StrNe(__FILE__), "function version"));
-  EXPECT_CALL(log, Log(INFO, __FILE__, "macro version"));
-
-  int severity = WARNING;
-  LogAtLevel(severity, "function version");
-
-  severity = INFO;
-  // We can use the macro version as a C++ stream.
-  LOG_AT_LEVEL(severity) << "macro" << ' ' << "version";
-}
-
-TEST(TestExitOnDFatal, ToBeOrNotToBe) {
-  // Check the default setting...
-  EXPECT_TRUE(base::internal::GetExitOnDFatal());
-
-  // Turn off...
-  base::internal::SetExitOnDFatal(false);
-  EXPECT_FALSE(base::internal::GetExitOnDFatal());
-
-  // We don't die.
-  {
-    ScopedMockLog log;
-    //EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-    // LOG(DFATAL) has severity FATAL if debugging, but is
-    // downgraded to ERROR if not debugging.
-    const LogSeverity severity =
-#ifdef NDEBUG
-        ERROR;
-#else
-        FATAL;
-#endif
-    EXPECT_CALL(log, Log(severity, __FILE__, "This should not be fatal"));
-    LOG(DFATAL) << "This should not be fatal";
-  }
-
-  // Turn back on...
-  base::internal::SetExitOnDFatal(true);
-  EXPECT_TRUE(base::internal::GetExitOnDFatal());
-
-#ifdef GTEST_HAS_DEATH_TEST
-  // Death comes on little cats' feet.
-  EXPECT_DEBUG_DEATH({
-      LOG(DFATAL) << "This should be fatal in debug mode";
-    }, "This should be fatal in debug mode");
-#endif
-}
-
-#ifdef HAVE_STACKTRACE
-
-static void BacktraceAtHelper() {
-  LOG(INFO) << "Not me";
-
-// The vertical spacing of the next 3 lines is significant.
-  LOG(INFO) << "Backtrace me";
-}
-static int kBacktraceAtLine = __LINE__ - 2;  // The line of the LOG(INFO) above
-
-TEST(LogBacktraceAt, DoesNotBacktraceWhenDisabled) {
-  StrictMock<ScopedMockLog> log;
-
-  FLAGS_log_backtrace_at = "";
-
-  EXPECT_CALL(log, Log(_, _, "Backtrace me"));
-  EXPECT_CALL(log, Log(_, _, "Not me"));
-
-  BacktraceAtHelper();
-}
-
-TEST(LogBacktraceAt, DoesBacktraceAtRightLineWhenEnabled) {
-  StrictMock<ScopedMockLog> log;
-
-  char where[100];
-  snprintf(where, 100, "%s:%d", const_basename(__FILE__), kBacktraceAtLine);
-  FLAGS_log_backtrace_at = where;
-
-  // The LOG at the specified line should include a stacktrace which includes
-  // the name of the containing function, followed by the log message.
-  // We use HasSubstr()s instead of ContainsRegex() for environments
-  // which don't have regexp.
-  EXPECT_CALL(log, Log(_, _, AllOf(HasSubstr("stacktrace:"),
-                                   HasSubstr("BacktraceAtHelper"),
-                                   HasSubstr("main"),
-                                   HasSubstr("Backtrace me"))));
-  // Other LOGs should not include a backtrace.
-  EXPECT_CALL(log, Log(_, _, "Not me"));
-
-  BacktraceAtHelper();
-}
-
-#endif // HAVE_STACKTRACE
-
-#endif // HAVE_LIB_GMOCK
-
-struct UserDefinedClass {
-  bool operator==(const UserDefinedClass&) const { return true; }
-};
-
-inline ostream& operator<<(ostream& out, const UserDefinedClass&) {
-  out << "OK";
-  return out;
-}
-
-TEST(UserDefinedClass, logging) {
-  UserDefinedClass u;
-  vector<string> buf;
-  LOG_STRING(INFO, &buf) << u;
-  CHECK_EQ(1UL, buf.size());
-  CHECK(buf[0].find("OK") != string::npos);
-
-  // We must be able to compile this.
-  CHECK_EQ(u, u);
-}
diff --git a/third_party/glog/src/logging_unittest.err b/third_party/glog/src/logging_unittest.err
deleted file mode 100644
index def0e93f1e..0000000000
--- a/third_party/glog/src/logging_unittest.err
+++ /dev/null
@@ -1,307 +0,0 @@
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=2 logtostderr=0 alsologtostderr=0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-WARNING: Logging before InitGoogleLogging() is written to STDERR
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=0 logtostderr=0 alsologtostderr=0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] foo bar 10 3.4
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 1: __SUCCESS__ [0]
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 3, iteration 1
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 4, iteration 1
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 5, iteration 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 1
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if less than 3 every 2, iteration 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 2
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 3: __ENOENT__ [2]
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 3
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if less than 3 every 2, iteration 3
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 3, iteration 4
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 4
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 5: __EINTR__ [4]
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 4, iteration 5
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 5
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 5, iteration 6
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 6
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 7: __ENXIO__ [6]
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 3, iteration 7
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 7
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 8
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 9: __ENOEXEC__ [8]
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 4, iteration 9
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 9
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 3, iteration 10
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 10
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if this
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] array
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] const array
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] foo 1000 0000001000 3e8
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] inner
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] outer
-no prefix
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo bar 10 3.400000
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: array
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: const array
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: ptr __PTRTEST__
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: ptr __NULLP__
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo 1000 0000001000 3e8
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo 1000
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo 1000
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: RAW_LOG ERROR: The Message was too long!
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: RAW_LOG ERROR: The Message was too long!
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0 on
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 1 on
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 2 on
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=0 logtostderr=0 alsologtostderr=0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=1 stderrthreshold=0 logtostderr=0 alsologtostderr=0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=-1 stderrthreshold=0 logtostderr=0 alsologtostderr=0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=1 logtostderr=0 alsologtostderr=0
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=2 logtostderr=0 alsologtostderr=0
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=3 logtostderr=0 alsologtostderr=0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=3 logtostderr=1 alsologtostderr=0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=3 logtostderr=0 alsologtostderr=1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=1 stderrthreshold=1 logtostderr=0 alsologtostderr=0
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=1 stderrthreshold=3 logtostderr=0 alsologtostderr=1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_STRING: reported info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_STRING: reported warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_STRING: reported error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_STRING:  LOG_STRING: collected info
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_STRING:  LOG_STRING: collected warning
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_STRING:  LOG_STRING: collected error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: reported info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: reported warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: reported error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_SINK:
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK_BUT_NOT_TO_LOGFILE: collected info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK_BUT_NOT_TO_LOGFILE: collected warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK_BUT_NOT_TO_LOGFILE: collected error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: collected info
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_STRING:  LOG_TO_STRING: collected info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: collected warning
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_STRING:  LOG_TO_STRING: collected warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: collected error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_STRING:  LOG_TO_STRING: collected error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: reported info
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: reported warning
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: reported error
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffering
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffered
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waiting
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Sink got a messages
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waited
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink is sending out a message: IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Have 0 left
-EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 2
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffering
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffered
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waiting
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Sink got a messages
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waited
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink is sending out a message: EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 2
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Have 0 left
-WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 3
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffering
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffered
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waiting
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Sink got a messages
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waited
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink is sending out a message: WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 3
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Have 0 left
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink capture: IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 1
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink capture: EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 2
-IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink capture: WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 3
diff --git a/third_party/glog/src/mock-log.h b/third_party/glog/src/mock-log.h
deleted file mode 100644
index 30a0f74efe..0000000000
--- a/third_party/glog/src/mock-log.h
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright (c) 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Zhanyong Wan
-//
-// Defines the ScopedMockLog class (using Google C++ Mocking
-// Framework), which is convenient for testing code that uses LOG().
-
-#ifndef GLOG_SRC_MOCK_LOG_H_
-#define GLOG_SRC_MOCK_LOG_H_
-
-// For GOOGLE_NAMESPACE. This must go first so we get _XOPEN_SOURCE.
-#include "utilities.h"
-
-#include <string>
-
-#include <gmock/gmock.h>
-
-#include "glog/logging.h"
-
-_START_GOOGLE_NAMESPACE_
-namespace glog_testing {
-
-// A ScopedMockLog object intercepts LOG() messages issued during its
-// lifespan.  Using this together with Google C++ Mocking Framework,
-// it's very easy to test how a piece of code calls LOG().  The
-// typical usage:
-//
-//   TEST(FooTest, LogsCorrectly) {
-//     ScopedMockLog log;
-//
-//     // We expect the WARNING "Something bad!" exactly twice.
-//     EXPECT_CALL(log, Log(WARNING, _, "Something bad!"))
-//         .Times(2);
-//
-//     // We allow foo.cc to call LOG(INFO) any number of times.
-//     EXPECT_CALL(log, Log(INFO, HasSubstr("/foo.cc"), _))
-//         .Times(AnyNumber());
-//
-//     Foo();  // Exercises the code under test.
-//   }
-class ScopedMockLog : public GOOGLE_NAMESPACE::LogSink {
- public:
-  // When a ScopedMockLog object is constructed, it starts to
-  // intercept logs.
-  ScopedMockLog() { AddLogSink(this); }
-
-  // When the object is destructed, it stops intercepting logs.
-  virtual ~ScopedMockLog() { RemoveLogSink(this); }
-
-  // Implements the mock method:
-  //
-  //   void Log(LogSeverity severity, const string& file_path,
-  //            const string& message);
-  //
-  // The second argument to Send() is the full path of the source file
-  // in which the LOG() was issued.
-  //
-  // Note, that in a multi-threaded environment, all LOG() messages from a
-  // single thread will be handled in sequence, but that cannot be guaranteed
-  // for messages from different threads. In fact, if the same or multiple
-  // expectations are matched on two threads concurrently, their actions will
-  // be executed concurrently as well and may interleave.
-  MOCK_METHOD3(Log, void(GOOGLE_NAMESPACE::LogSeverity severity,
-                         const std::string& file_path,
-                         const std::string& message));
-
- private:
-  // Implements the send() virtual function in class LogSink.
-  // Whenever a LOG() statement is executed, this function will be
-  // invoked with information presented in the LOG().
-  //
-  // The method argument list is long and carries much information a
-  // test usually doesn't care about, so we trim the list before
-  // forwarding the call to Log(), which is much easier to use in
-  // tests.
-  //
-  // We still cannot call Log() directly, as it may invoke other LOG()
-  // messages, either due to Invoke, or due to an error logged in
-  // Google C++ Mocking Framework code, which would trigger a deadlock
-  // since a lock is held during send().
-  //
-  // Hence, we save the message for WaitTillSent() which will be called after
-  // the lock on send() is released, and we'll call Log() inside
-  // WaitTillSent(). Since while a single send() call may be running at a
-  // time, multiple WaitTillSent() calls (along with the one send() call) may
-  // be running simultaneously, we ensure thread-safety of the exchange between
-  // send() and WaitTillSent(), and that for each message, LOG(), send(),
-  // WaitTillSent() and Log() are executed in the same thread.
-  virtual void send(GOOGLE_NAMESPACE::LogSeverity severity,
-                    const char* full_filename,
-                    const char* /*base_filename*/, int /*line*/,
-                    const tm* /*tm_time*/,
-                    const char* message, size_t message_len) {
-    // We are only interested in the log severity, full file name, and
-    // log message.
-    message_info_.severity = severity;
-    message_info_.file_path = full_filename;
-    message_info_.message = std::string(message, message_len);
-  }
-
-  // Implements the WaitTillSent() virtual function in class LogSink.
-  // It will be executed after send() and after the global logging lock is
-  // released, so calls within it (or rather within the Log() method called
-  // within) may also issue LOG() statements.
-  //
-  // LOG(), send(), WaitTillSent() and Log() will occur in the same thread for
-  // a given log message.
-  virtual void WaitTillSent() {
-    // First, and very importantly, we save a copy of the message being
-    // processed before calling Log(), since Log() may indirectly call send()
-    // and WaitTillSent() in the same thread again.
-    MessageInfo message_info = message_info_;
-    Log(message_info.severity, message_info.file_path, message_info.message);
-  }
-
-  // All relevant information about a logged message that needs to be passed
-  // from send() to WaitTillSent().
-  struct MessageInfo {
-    GOOGLE_NAMESPACE::LogSeverity severity;
-    std::string file_path;
-    std::string message;
-  };
-  MessageInfo message_info_;
-};
-
-}  // namespace glog_testing
-_END_GOOGLE_NAMESPACE_
-
-#endif  // GLOG_SRC_MOCK_LOG_H_
diff --git a/third_party/glog/src/mock-log_test.cc b/third_party/glog/src/mock-log_test.cc
deleted file mode 100644
index 7d58a307c2..0000000000
--- a/third_party/glog/src/mock-log_test.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Zhanyong Wan
-
-// Tests the ScopedMockLog class.
-
-#include "mock-log.h"
-
-#include <string>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-namespace {
-
-using GOOGLE_NAMESPACE::INFO;
-using GOOGLE_NAMESPACE::WARNING;
-using GOOGLE_NAMESPACE::ERROR;
-using GOOGLE_NAMESPACE::glog_testing::ScopedMockLog;
-using std::string;
-using testing::_;
-using testing::HasSubstr;
-using testing::InSequence;
-using testing::InvokeWithoutArgs;
-
-// Tests that ScopedMockLog intercepts LOG()s when it's alive.
-TEST(ScopedMockLogTest, InterceptsLog) {
-  ScopedMockLog log;
-
-  InSequence s;
-  EXPECT_CALL(log, Log(WARNING, HasSubstr("/mock-log_test.cc"), "Fishy."));
-  EXPECT_CALL(log, Log(INFO, _, "Working..."))
-      .Times(2);
-  EXPECT_CALL(log, Log(ERROR, _, "Bad!!"));
-
-  LOG(WARNING) << "Fishy.";
-  LOG(INFO) << "Working...";
-  LOG(INFO) << "Working...";
-  LOG(ERROR) << "Bad!!";
-}
-
-void LogBranch() {
-  LOG(INFO) << "Logging a branch...";
-}
-
-void LogTree() {
-  LOG(INFO) << "Logging the whole tree...";
-}
-
-void LogForest() {
-  LOG(INFO) << "Logging the entire forest.";
-  LOG(INFO) << "Logging the entire forest..";
-  LOG(INFO) << "Logging the entire forest...";
-}
-
-// The purpose of the following test is to verify that intercepting logging
-// continues to work properly if a LOG statement is executed within the scope
-// of a mocked call.
-TEST(ScopedMockLogTest, LogDuringIntercept) {
-  ScopedMockLog log;
-  InSequence s;
-  EXPECT_CALL(log, Log(INFO, __FILE__, "Logging a branch..."))
-      .WillOnce(InvokeWithoutArgs(LogTree));
-  EXPECT_CALL(log, Log(INFO, __FILE__, "Logging the whole tree..."))
-      .WillOnce(InvokeWithoutArgs(LogForest));
-  EXPECT_CALL(log, Log(INFO, __FILE__, "Logging the entire forest."));
-  EXPECT_CALL(log, Log(INFO, __FILE__, "Logging the entire forest.."));
-  EXPECT_CALL(log, Log(INFO, __FILE__, "Logging the entire forest..."));
-  LogBranch();
-}
-
-}  // namespace
-
-int main(int argc, char **argv) {
-  GOOGLE_NAMESPACE::InitGoogleLogging(argv[0]);
-  testing::InitGoogleMock(&argc, argv);
-
-  return RUN_ALL_TESTS();
-}
diff --git a/third_party/glog/src/package_config_unittest/working_config/CMakeLists.txt b/third_party/glog/src/package_config_unittest/working_config/CMakeLists.txt
deleted file mode 100644
index d701aa7b1b..0000000000
--- a/third_party/glog/src/package_config_unittest/working_config/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-cmake_minimum_required (VERSION 3.1)
-project (glog_package_config)
-
-find_package (glog REQUIRED NO_MODULE)
-
-add_executable (glog_package_config glog_package_config.cc)
-
-target_link_libraries (glog_package_config PRIVATE glog::glog)
diff --git a/third_party/glog/src/package_config_unittest/working_config/glog_package_config.cc b/third_party/glog/src/package_config_unittest/working_config/glog_package_config.cc
deleted file mode 100644
index b7b5cf6c66..0000000000
--- a/third_party/glog/src/package_config_unittest/working_config/glog_package_config.cc
+++ /dev/null
@@ -1,6 +0,0 @@
-#include <glog/logging.h>
-
-int main(int /*argc*/, char** argv)
-{
-    google::InitGoogleLogging(argv[0]);
-}
diff --git a/third_party/glog/src/raw_logging.cc b/third_party/glog/src/raw_logging.cc
deleted file mode 100644
index ba65961e90..0000000000
--- a/third_party/glog/src/raw_logging.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (c) 2006, Google 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:
-//
-//     * 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.
-//
-// Author: Maxim Lifantsev
-//
-// logging_unittest.cc covers the functionality herein
-
-#include "utilities.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <errno.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>               // for close() and write()
-#endif
-#include <fcntl.h>                 // for open()
-#include <time.h>
-#include "config.h"
-#include "glog/logging.h"          // To pick up flag settings etc.
-#include "glog/raw_logging.h"
-#include "base/commandlineflags.h"
-
-#ifdef HAVE_STACKTRACE
-# include "stacktrace.h"
-#endif
-
-#if defined(HAVE_SYSCALL_H)
-#include <syscall.h>                 // for syscall()
-#elif defined(HAVE_SYS_SYSCALL_H)
-#include <sys/syscall.h>                 // for syscall()
-#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#if defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)
-# define safe_write(fd, s, len)  syscall(SYS_write, fd, s, len)
-#else
-  // Not so safe, but what can you do?
-# define safe_write(fd, s, len)  write(fd, s, len)
-#endif
-
-_START_GOOGLE_NAMESPACE_
-
-// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths
-// that invoke malloc() and getenv() that might acquire some locks.
-// If this becomes a problem we should reimplement a subset of vsnprintf
-// that does not need locks and malloc.
-
-// Helper for RawLog__ below.
-// *DoRawLog writes to *buf of *size and move them past the written portion.
-// It returns true iff there was no overflow or error.
-static bool DoRawLog(char** buf, int* size, const char* format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  int n = vsnprintf(*buf, *size, format, ap);
-  va_end(ap);
-  if (n < 0 || n > *size) return false;
-  *size -= n;
-  *buf += n;
-  return true;
-}
-
-// Helper for RawLog__ below.
-inline static bool VADoRawLog(char** buf, int* size,
-                              const char* format, va_list ap) {
-  int n = vsnprintf(*buf, *size, format, ap);
-  if (n < 0 || n > *size) return false;
-  *size -= n;
-  *buf += n;
-  return true;
-}
-
-static const int kLogBufSize = 3000;
-static bool crashed = false;
-static CrashReason crash_reason;
-static char crash_buf[kLogBufSize + 1] = { 0 };  // Will end in '\0'
-
-void RawLog__(LogSeverity severity, const char* file, int line,
-              const char* format, ...) {
-  if (!(FLAGS_logtostderr || severity >= FLAGS_stderrthreshold ||
-        FLAGS_alsologtostderr || !IsGoogleLoggingInitialized())) {
-    return;  // this stderr log message is suppressed
-  }
-  // can't call localtime_r here: it can allocate
-  char buffer[kLogBufSize];
-  char* buf = buffer;
-  int size = sizeof(buffer);
-
-  // NOTE: this format should match the specification in base/logging.h
-  DoRawLog(&buf, &size, "%c00000000 00:00:00.000000 %5u %s:%d] RAW: ",
-           LogSeverityNames[severity][0],
-           static_cast<unsigned int>(GetTID()),
-           const_basename(const_cast<char *>(file)), line);
-
-  // Record the position and size of the buffer after the prefix
-  const char* msg_start = buf;
-  const int msg_size = size;
-
-  va_list ap;
-  va_start(ap, format);
-  bool no_chop = VADoRawLog(&buf, &size, format, ap);
-  va_end(ap);
-  if (no_chop) {
-    DoRawLog(&buf, &size, "\n");
-  } else {
-    DoRawLog(&buf, &size, "RAW_LOG ERROR: The Message was too long!\n");
-  }
-  // We make a raw syscall to write directly to the stderr file descriptor,
-  // avoiding FILE buffering (to avoid invoking malloc()), and bypassing
-  // libc (to side-step any libc interception).
-  // We write just once to avoid races with other invocations of RawLog__.
-  safe_write(STDERR_FILENO, buffer, strlen(buffer));
-  if (severity == GLOG_FATAL)  {
-    if (!sync_val_compare_and_swap(&crashed, false, true)) {
-      crash_reason.filename = file;
-      crash_reason.line_number = line;
-      memcpy(crash_buf, msg_start, msg_size);  // Don't include prefix
-      crash_reason.message = crash_buf;
-#ifdef HAVE_STACKTRACE
-      crash_reason.depth =
-          GetStackTrace(crash_reason.stack, ARRAYSIZE(crash_reason.stack), 1);
-#else
-      crash_reason.depth = 0;
-#endif
-      SetCrashReason(&crash_reason);
-    }
-    LogMessage::Fail();  // abort()
-  }
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/signalhandler.cc b/third_party/glog/src/signalhandler.cc
deleted file mode 100644
index 955471899a..0000000000
--- a/third_party/glog/src/signalhandler.cc
+++ /dev/null
@@ -1,403 +0,0 @@
-// Copyright (c) 2008, Google 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:
-//
-//     * 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.
-//
-// Author: Satoru Takabayashi
-//
-// Implementation of InstallFailureSignalHandler().
-
-#include "utilities.h"
-#include "stacktrace.h"
-#include "symbolize.h"
-#include "glog/logging.h"
-
-#include <signal.h>
-#include <time.h>
-#ifdef HAVE_UCONTEXT_H
-# include <ucontext.h>
-#endif
-#ifdef HAVE_SYS_UCONTEXT_H
-# include <sys/ucontext.h>
-#endif
-#include <algorithm>
-
-_START_GOOGLE_NAMESPACE_
-
-namespace {
-
-// We'll install the failure signal handler for these signals.  We could
-// use strsignal() to get signal names, but we don't use it to avoid
-// introducing yet another #ifdef complication.
-//
-// The list should be synced with the comment in signalhandler.h.
-const struct {
-  int number;
-  const char *name;
-} kFailureSignals[] = {
-  { SIGSEGV, "SIGSEGV" },
-  { SIGILL, "SIGILL" },
-  { SIGFPE, "SIGFPE" },
-  { SIGABRT, "SIGABRT" },
-#if !defined(OS_WINDOWS)
-  { SIGBUS, "SIGBUS" },
-#endif
-  { SIGTERM, "SIGTERM" },
-};
-
-static bool kFailureSignalHandlerInstalled = false;
-
-// Returns the program counter from signal context, NULL if unknown.
-void* GetPC(void* ucontext_in_void) {
-#if (defined(HAVE_UCONTEXT_H) || defined(HAVE_SYS_UCONTEXT_H)) && defined(PC_FROM_UCONTEXT)
-  if (ucontext_in_void != NULL) {
-    ucontext_t *context = reinterpret_cast<ucontext_t *>(ucontext_in_void);
-    return (void*)context->PC_FROM_UCONTEXT;
-  }
-#endif
-  return NULL;
-}
-
-// The class is used for formatting error messages.  We don't use printf()
-// as it's not async signal safe.
-class MinimalFormatter {
- public:
-  MinimalFormatter(char *buffer, int size)
-      : buffer_(buffer),
-        cursor_(buffer),
-        end_(buffer + size) {
-  }
-
-  // Returns the number of bytes written in the buffer.
-  int num_bytes_written() const { return (int) (cursor_ - buffer_); }
-
-  // Appends string from "str" and updates the internal cursor.
-  void AppendString(const char* str) {
-    int i = 0;
-    while (str[i] != '\0' && cursor_ + i < end_) {
-      cursor_[i] = str[i];
-      ++i;
-    }
-    cursor_ += i;
-  }
-
-  // Formats "number" in "radix" and updates the internal cursor.
-  // Lowercase letters are used for 'a' - 'z'.
-  void AppendUint64(uint64 number, int radix) {
-    int i = 0;
-    while (cursor_ + i < end_) {
-      const int tmp = number % radix;
-      number /= radix;
-      cursor_[i] = (tmp < 10 ? '0' + tmp : 'a' + tmp - 10);
-      ++i;
-      if (number == 0) {
-        break;
-      }
-    }
-    // Reverse the bytes written.
-    std::reverse(cursor_, cursor_ + i);
-    cursor_ += i;
-  }
-
-  // Formats "number" as hexadecimal number, and updates the internal
-  // cursor.  Padding will be added in front if needed.
-  void AppendHexWithPadding(uint64 number, int width) {
-    char* start = cursor_;
-    AppendString("0x");
-    AppendUint64(number, 16);
-    // Move to right and add padding in front if needed.
-    if (cursor_ < start + width) {
-      const int64 delta = start + width - cursor_;
-      std::copy(start, cursor_, start + delta);
-      std::fill(start, start + delta, ' ');
-      cursor_ = start + width;
-    }
-  }
-
- private:
-  char *buffer_;
-  char *cursor_;
-  const char * const end_;
-};
-
-// Writes the given data with the size to the standard error.
-void WriteToStderr(const char* data, int size) {
-  if (write(STDERR_FILENO, data, size) < 0) {
-    // Ignore errors.
-  }
-}
-
-// The writer function can be changed by InstallFailureWriter().
-void (*g_failure_writer)(const char* data, int size) = WriteToStderr;
-
-// Dumps time information.  We don't dump human-readable time information
-// as localtime() is not guaranteed to be async signal safe.
-void DumpTimeInfo() {
-  time_t time_in_sec = time(NULL);
-  char buf[256];  // Big enough for time info.
-  MinimalFormatter formatter(buf, sizeof(buf));
-  formatter.AppendString("*** Aborted at ");
-  formatter.AppendUint64(time_in_sec, 10);
-  formatter.AppendString(" (unix time)");
-  formatter.AppendString(" try \"date -d @");
-  formatter.AppendUint64(time_in_sec, 10);
-  formatter.AppendString("\" if you are using GNU date ***\n");
-  g_failure_writer(buf, formatter.num_bytes_written());
-}
-
-// TODO(hamaji): Use signal instead of sigaction?
-#ifdef HAVE_SIGACTION
-
-// Dumps information about the signal to STDERR.
-void DumpSignalInfo(int signal_number, siginfo_t *siginfo) {
-  // Get the signal name.
-  const char* signal_name = NULL;
-  for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
-    if (signal_number == kFailureSignals[i].number) {
-      signal_name = kFailureSignals[i].name;
-    }
-  }
-
-  char buf[256];  // Big enough for signal info.
-  MinimalFormatter formatter(buf, sizeof(buf));
-
-  formatter.AppendString("*** ");
-  if (signal_name) {
-    formatter.AppendString(signal_name);
-  } else {
-    // Use the signal number if the name is unknown.  The signal name
-    // should be known, but just in case.
-    formatter.AppendString("Signal ");
-    formatter.AppendUint64(signal_number, 10);
-  }
-  formatter.AppendString(" (@0x");
-  formatter.AppendUint64(reinterpret_cast<uintptr_t>(siginfo->si_addr), 16);
-  formatter.AppendString(")");
-  formatter.AppendString(" received by PID ");
-  formatter.AppendUint64(getpid(), 10);
-  formatter.AppendString(" (TID 0x");
-  // We assume pthread_t is an integral number or a pointer, rather
-  // than a complex struct.  In some environments, pthread_self()
-  // returns an uint64 but in some other environments pthread_self()
-  // returns a pointer.  Hence we use C-style cast here, rather than
-  // reinterpret/static_cast, to support both types of environments.
-  formatter.AppendUint64((uintptr_t)pthread_self(), 16);
-  formatter.AppendString(") ");
-  // Only linux has the PID of the signal sender in si_pid.
-#ifdef OS_LINUX
-  formatter.AppendString("from PID ");
-  formatter.AppendUint64(siginfo->si_pid, 10);
-  formatter.AppendString("; ");
-#endif
-  formatter.AppendString("stack trace: ***\n");
-  g_failure_writer(buf, formatter.num_bytes_written());
-}
-
-#endif  // HAVE_SIGACTION
-
-// Dumps information about the stack frame to STDERR.
-void DumpStackFrameInfo(const char* prefix, void* pc) {
-  // Get the symbol name.
-  const char *symbol = "(unknown)";
-  char symbolized[1024];  // Big enough for a sane symbol.
-  // Symbolizes the previous address of pc because pc may be in the
-  // next function.
-  if (Symbolize(reinterpret_cast<char *>(pc) - 1,
-                symbolized, sizeof(symbolized))) {
-    symbol = symbolized;
-  }
-
-  char buf[1024];  // Big enough for stack frame info.
-  MinimalFormatter formatter(buf, sizeof(buf));
-
-  formatter.AppendString(prefix);
-  formatter.AppendString("@ ");
-  const int width = 2 * sizeof(void*) + 2;  // + 2  for "0x".
-  formatter.AppendHexWithPadding(reinterpret_cast<uintptr_t>(pc), width);
-  formatter.AppendString(" ");
-  formatter.AppendString(symbol);
-  formatter.AppendString("\n");
-  g_failure_writer(buf, formatter.num_bytes_written());
-}
-
-// Invoke the default signal handler.
-void InvokeDefaultSignalHandler(int signal_number) {
-#ifdef HAVE_SIGACTION
-  struct sigaction sig_action;
-  memset(&sig_action, 0, sizeof(sig_action));
-  sigemptyset(&sig_action.sa_mask);
-  sig_action.sa_handler = SIG_DFL;
-  sigaction(signal_number, &sig_action, NULL);
-  kill(getpid(), signal_number);
-#elif defined(OS_WINDOWS)
-  signal(signal_number, SIG_DFL);
-  raise(signal_number);
-#endif
-}
-
-// This variable is used for protecting FailureSignalHandler() from
-// dumping stuff while another thread is doing it.  Our policy is to let
-// the first thread dump stuff and let other threads wait.
-// See also comments in FailureSignalHandler().
-static pthread_t* g_entered_thread_id_pointer = NULL;
-
-// Dumps signal and stack frame information, and invokes the default
-// signal handler once our job is done.
-#if defined(OS_WINDOWS)
-void FailureSignalHandler(int signal_number)
-#else
-void FailureSignalHandler(int signal_number,
-                          siginfo_t *signal_info,
-                          void *ucontext)
-#endif
-{
-  // First check if we've already entered the function.  We use an atomic
-  // compare and swap operation for platforms that support it.  For other
-  // platforms, we use a naive method that could lead to a subtle race.
-
-  // We assume pthread_self() is async signal safe, though it's not
-  // officially guaranteed.
-  pthread_t my_thread_id = pthread_self();
-  // NOTE: We could simply use pthread_t rather than pthread_t* for this,
-  // if pthread_self() is guaranteed to return non-zero value for thread
-  // ids, but there is no such guarantee.  We need to distinguish if the
-  // old value (value returned from __sync_val_compare_and_swap) is
-  // different from the original value (in this case NULL).
-  pthread_t* old_thread_id_pointer =
-      glog_internal_namespace_::sync_val_compare_and_swap(
-          &g_entered_thread_id_pointer,
-          static_cast<pthread_t*>(NULL),
-          &my_thread_id);
-  if (old_thread_id_pointer != NULL) {
-    // We've already entered the signal handler.  What should we do?
-    if (pthread_equal(my_thread_id, *g_entered_thread_id_pointer)) {
-      // It looks the current thread is reentering the signal handler.
-      // Something must be going wrong (maybe we are reentering by another
-      // type of signal?).  Kill ourself by the default signal handler.
-      InvokeDefaultSignalHandler(signal_number);
-    }
-    // Another thread is dumping stuff.  Let's wait until that thread
-    // finishes the job and kills the process.
-    while (true) {
-      sleep(1);
-    }
-  }
-  // This is the first time we enter the signal handler.  We are going to
-  // do some interesting stuff from here.
-  // TODO(satorux): We might want to set timeout here using alarm(), but
-  // mixing alarm() and sleep() can be a bad idea.
-
-  // First dump time info.
-  DumpTimeInfo();
-
-#if !defined(OS_WINDOWS)
-  // Get the program counter from ucontext.
-  void *pc = GetPC(ucontext);
-  DumpStackFrameInfo("PC: ", pc);
-#endif
-
-#ifdef HAVE_STACKTRACE
-  // Get the stack traces.
-  void *stack[32];
-  // +1 to exclude this function.
-  const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1);
-# ifdef HAVE_SIGACTION
-  DumpSignalInfo(signal_number, signal_info);
-# endif
-  // Dump the stack traces.
-  for (int i = 0; i < depth; ++i) {
-    DumpStackFrameInfo("    ", stack[i]);
-  }
-#endif
-
-  // *** TRANSITION ***
-  //
-  // BEFORE this point, all code must be async-termination-safe!
-  // (See WARNING above.)
-  //
-  // AFTER this point, we do unsafe things, like using LOG()!
-  // The process could be terminated or hung at any time.  We try to
-  // do more useful things first and riskier things later.
-
-  // Flush the logs before we do anything in case 'anything'
-  // causes problems.
-  FlushLogFilesUnsafe(0);
-
-  // Kill ourself by the default signal handler.
-  InvokeDefaultSignalHandler(signal_number);
-}
-
-}  // namespace
-
-namespace glog_internal_namespace_ {
-
-bool IsFailureSignalHandlerInstalled() {
-#ifdef HAVE_SIGACTION
-  // TODO(andschwa): Return kFailureSignalHandlerInstalled?
-  struct sigaction sig_action;
-  memset(&sig_action, 0, sizeof(sig_action));
-  sigemptyset(&sig_action.sa_mask);
-  sigaction(SIGABRT, NULL, &sig_action);
-  if (sig_action.sa_sigaction == &FailureSignalHandler)
-    return true;
-#elif defined(OS_WINDOWS)
-  return kFailureSignalHandlerInstalled;
-#endif  // HAVE_SIGACTION
-  return false;
-}
-
-}  // namespace glog_internal_namespace_
-
-void InstallFailureSignalHandler() {
-#ifdef HAVE_SIGACTION
-  // Build the sigaction struct.
-  struct sigaction sig_action;
-  memset(&sig_action, 0, sizeof(sig_action));
-  sigemptyset(&sig_action.sa_mask);
-  sig_action.sa_flags |= SA_SIGINFO;
-  sig_action.sa_sigaction = &FailureSignalHandler;
-
-  for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
-    CHECK_ERR(sigaction(kFailureSignals[i].number, &sig_action, NULL));
-  }
-  kFailureSignalHandlerInstalled = true;
-#elif defined(OS_WINDOWS)
-  for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
-    CHECK_NE(signal(kFailureSignals[i].number, &FailureSignalHandler),
-             SIG_ERR);
-  }
-  kFailureSignalHandlerInstalled = true;
-#endif  // HAVE_SIGACTION
-}
-
-void InstallFailureWriter(void (*writer)(const char* data, int size)) {
-#if defined(HAVE_SIGACTION) || defined(OS_WINDOWS)
-  g_failure_writer = writer;
-#endif  // HAVE_SIGACTION
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/signalhandler_unittest.cc b/third_party/glog/src/signalhandler_unittest.cc
deleted file mode 100644
index 36d957b9c3..0000000000
--- a/third_party/glog/src/signalhandler_unittest.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2008, Google 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:
-//
-//     * 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.
-//
-// Author: Satoru Takabayashi
-//
-// This is a helper binary for testing signalhandler.cc.  The actual test
-// is done in signalhandler_unittest.sh.
-
-#include "utilities.h"
-
-#if defined(HAVE_PTHREAD)
-# include <pthread.h>
-#endif
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string>
-#include "glog/logging.h"
-
-#ifdef HAVE_LIB_GFLAGS
-#include <gflags/gflags.h>
-using namespace GFLAGS_NAMESPACE;
-#endif
-
-using namespace GOOGLE_NAMESPACE;
-
-static void* DieInThread(void*) {
-  // We assume pthread_t is an integral number or a pointer, rather
-  // than a complex struct.  In some environments, pthread_self()
-  // returns an uint64 but in some other environments pthread_self()
-  // returns a pointer.  Hence we use C-style cast here, rather than
-  // reinterpret/static_cast, to support both types of environments.
-  fprintf(stderr, "0x%lx is dying\n", (long)pthread_self());
-  // Use volatile to prevent from these to be optimized away.
-  volatile int a = 0;
-  volatile int b = 1 / a;
-  fprintf(stderr, "We should have died: b=%d\n", b);
-  return NULL;
-}
-
-static void WriteToStdout(const char* data, int size) {
-  if (write(STDOUT_FILENO, data, size) < 0) {
-    // Ignore errors.
-  }
-}
-
-int main(int argc, char **argv) {
-#if defined(HAVE_STACKTRACE) && defined(HAVE_SYMBOLIZE)
-  InitGoogleLogging(argv[0]);
-#ifdef HAVE_LIB_GFLAGS
-  ParseCommandLineFlags(&argc, &argv, true);
-#endif
-  InstallFailureSignalHandler();
-  const std::string command = argc > 1 ? argv[1] : "none";
-  if (command == "segv") {
-    // We'll check if this is outputted.
-    LOG(INFO) << "create the log file";
-    LOG(INFO) << "a message before segv";
-    // We assume 0xDEAD is not writable.
-    int *a = (int*)0xDEAD;
-    *a = 0;
-  } else if (command == "loop") {
-    fprintf(stderr, "looping\n");
-    while (true);
-  } else if (command == "die_in_thread") {
-#if defined(HAVE_PTHREAD)
-    pthread_t thread;
-    pthread_create(&thread, NULL, &DieInThread, NULL);
-    pthread_join(thread, NULL);
-#else
-    fprintf(stderr, "no pthread\n");
-    return 1;
-#endif
-  } else if (command == "dump_to_stdout") {
-    InstallFailureWriter(WriteToStdout);
-    abort();
-  } else if (command == "installed") {
-    fprintf(stderr, "signal handler installed: %s\n",
-        IsFailureSignalHandlerInstalled() ? "true" : "false");
-  } else {
-    // Tell the shell script
-    puts("OK");
-  }
-#endif
-  return 0;
-}
diff --git a/third_party/glog/src/signalhandler_unittest.sh b/third_party/glog/src/signalhandler_unittest.sh
deleted file mode 100755
index 265cd458c0..0000000000
--- a/third_party/glog/src/signalhandler_unittest.sh
+++ /dev/null
@@ -1,131 +0,0 @@
-#! /bin/sh
-#
-# Copyright (c) 2008, Google 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:
-#
-#     * 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.
-#
-# Author: Satoru Takabayashi
-#
-# Unit tests for signalhandler.cc.
-
-die () {
-    echo $1
-    exit 1
-}
-
-BINDIR=".libs"
-LIBGLOG="$BINDIR/libglog.so"
-
-BINARY="$BINDIR/signalhandler_unittest"
-LOG_INFO="./signalhandler_unittest.INFO"
-
-# Remove temporary files.
-rm -f signalhandler.out*
-
-if test -e "$BINARY"; then
-  # We need shared object.
-  export LD_LIBRARY_PATH=$BINDIR
-  export DYLD_LIBRARY_PATH=$BINDIR
-else
-  # For windows
-  BINARY="./signalhandler_unittest.exe"
-  if ! test -e "$BINARY"; then
-    echo "We coundn't find demangle_unittest binary."
-    exit 1
-  fi
-fi
-
-if [ x`$BINARY` != 'xOK' ]; then
-  echo "PASS (No stacktrace support. We don't run this test.)"
-  exit 0
-fi
-
-# The PC cannot be obtained in signal handlers on PowerPC correctly.
-# We just skip the test for PowerPC.
-if [ x`uname -p` = x"powerpc" ]; then
-  echo "PASS (We don't test the signal handler on PowerPC.)"
-  exit 0
-fi
-
-# Test for a case the program kills itself by SIGSEGV.
-GOOGLE_LOG_DIR=. $BINARY segv 2> signalhandler.out1
-for pattern in SIGSEGV 0xdead main "Aborted at [0-9]"; do
-  if ! grep --quiet "$pattern" signalhandler.out1; then
-    die "'$pattern' should appear in the output"
-  fi
-done
-if ! grep --quiet "a message before segv" $LOG_INFO; then
-  die "'a message before segv' should appear in the INFO log"
-fi
-rm -f $LOG_INFO
-
-# Test for a case the program is killed by this shell script.
-# $! = the process id of the last command run in the background.
-# $$ = the process id of this shell.
-$BINARY loop 2> signalhandler.out2 &
-# Wait until "looping" is written in the file.  This indicates the program
-# is ready to accept signals.
-while true; do
-  if grep --quiet looping signalhandler.out2; then
-    break
-  fi
-done
-kill -TERM $!
-wait $!
-
-from_pid=''
-# Only linux has the process ID of the signal sender.
-if [ x`uname` = "xLinux" ]; then
-  from_pid="from PID $$"
-fi
-for pattern in SIGTERM "by PID $!" "$from_pid" main "Aborted at [0-9]"; do
-  if ! grep --quiet "$pattern" signalhandler.out2; then
-    die "'$pattern' should appear in the output"
-  fi
-done
-
-# Test for a case the program dies in a non-main thread.
-$BINARY die_in_thread 2> signalhandler.out3
-EXPECTED_TID="`sed 's/ .*//; q' signalhandler.out3`"
-
-for pattern in SIGFPE DieInThread "TID $EXPECTED_TID" "Aborted at [0-9]"; do
-  if ! grep --quiet "$pattern" signalhandler.out3; then
-    die "'$pattern' should appear in the output"
-  fi
-done
-
-# Test for a case the program installs a custom failure writer that writes
-# stuff to stdout instead of stderr.
-$BINARY dump_to_stdout 1> signalhandler.out4
-for pattern in SIGABRT main "Aborted at [0-9]"; do
-  if ! grep --quiet "$pattern" signalhandler.out4; then
-    die "'$pattern' should appear in the output"
-  fi
-done
-
-echo PASS
diff --git a/third_party/glog/src/stacktrace.h b/third_party/glog/src/stacktrace.h
deleted file mode 100644
index cb64b33a68..0000000000
--- a/third_party/glog/src/stacktrace.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2000 - 2007, Google 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:
-//
-//     * 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.
-//
-// Routines to extract the current stack trace.  These functions are
-// thread-safe.
-
-#ifndef BASE_STACKTRACE_H_
-#define BASE_STACKTRACE_H_
-
-#include "config.h"
-#include "glog/logging.h"
-
-_START_GOOGLE_NAMESPACE_
-
-// This is similar to the GetStackFrames routine, except that it returns
-// the stack trace only, and not the stack frame sizes as well.
-// Example:
-//      main() { foo(); }
-//      foo() { bar(); }
-//      bar() {
-//        void* result[10];
-//        int depth = GetStackFrames(result, 10, 1);
-//      }
-//
-// This produces:
-//      result[0]       foo
-//      result[1]       main
-//           ....       ...
-//
-// "result" must not be NULL.
-GOOGLE_GLOG_DLL_DECL int GetStackTrace(void** result, int max_depth, int skip_count);
-
-_END_GOOGLE_NAMESPACE_
-
-#endif  // BASE_STACKTRACE_H_
diff --git a/third_party/glog/src/stacktrace_generic-inl.h b/third_party/glog/src/stacktrace_generic-inl.h
deleted file mode 100644
index fad81d3e3f..0000000000
--- a/third_party/glog/src/stacktrace_generic-inl.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2000 - 2007, Google 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:
-//
-//     * 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.
-//
-// Portable implementation - just use glibc
-//
-// Note:  The glibc implementation may cause a call to malloc.
-// This can cause a deadlock in HeapProfiler.
-#include <execinfo.h>
-#include <string.h>
-#include "stacktrace.h"
-
-_START_GOOGLE_NAMESPACE_
-
-// If you change this function, also change GetStackFrames below.
-int GetStackTrace(void** result, int max_depth, int skip_count) {
-  static const int kStackLength = 64;
-  void * stack[kStackLength];
-  int size;
-
-  size = backtrace(stack, kStackLength);
-  skip_count++;  // we want to skip the current frame as well
-  int result_count = size - skip_count;
-  if (result_count < 0)
-    result_count = 0;
-  if (result_count > max_depth)
-    result_count = max_depth;
-  for (int i = 0; i < result_count; i++)
-    result[i] = stack[i + skip_count];
-
-  return result_count;
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/stacktrace_libunwind-inl.h b/third_party/glog/src/stacktrace_libunwind-inl.h
deleted file mode 100644
index e29a50c004..0000000000
--- a/third_party/glog/src/stacktrace_libunwind-inl.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2005 - 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Arun Sharma
-//
-// Produce stack trace using libunwind
-
-#include "utilities.h"
-
-extern "C" {
-#define UNW_LOCAL_ONLY
-#include <libunwind.h>
-}
-#include "glog/raw_logging.h"
-#include "stacktrace.h"
-
-_START_GOOGLE_NAMESPACE_
-
-// Sometimes, we can try to get a stack trace from within a stack
-// trace, because libunwind can call mmap (maybe indirectly via an
-// internal mmap based memory allocator), and that mmap gets trapped
-// and causes a stack-trace request.  If were to try to honor that
-// recursive request, we'd end up with infinite recursion or deadlock.
-// Luckily, it's safe to ignore those subsequent traces.  In such
-// cases, we return 0 to indicate the situation.
-// We can use the GCC __thread syntax here since libunwind is not supported on
-// Windows.
-static __thread bool g_tl_entered; // Initialized to false.
-
-// If you change this function, also change GetStackFrames below.
-int GetStackTrace(void** result, int max_depth, int skip_count) {
-  void *ip;
-  int n = 0;
-  unw_cursor_t cursor;
-  unw_context_t uc;
-
-  if (g_tl_entered) {
-    return 0;
-  }
-  g_tl_entered = true;
-
-  unw_getcontext(&uc);
-  RAW_CHECK(unw_init_local(&cursor, &uc) >= 0, "unw_init_local failed");
-  skip_count++;         // Do not include the "GetStackTrace" frame
-
-  while (n < max_depth) {
-    int ret = unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *) &ip);
-    if (ret < 0)
-      break;
-    if (skip_count > 0) {
-      skip_count--;
-    } else {
-      result[n++] = ip;
-    }
-    ret = unw_step(&cursor);
-    if (ret <= 0)
-      break;
-  }
-
-  g_tl_entered = false;
-  return n;
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/stacktrace_powerpc-inl.h b/third_party/glog/src/stacktrace_powerpc-inl.h
deleted file mode 100644
index 03b91089aa..0000000000
--- a/third_party/glog/src/stacktrace_powerpc-inl.h
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (c) 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Craig Silverstein
-//
-// Produce stack trace.  I'm guessing (hoping!) the code is much like
-// for x86.  For apple machines, at least, it seems to be; see
-//    http://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html
-//    http://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK
-// Linux has similar code: http://patchwork.ozlabs.org/linuxppc/patch?id=8882
-
-#include <stdio.h>
-#include <stdint.h>   // for uintptr_t
-#include "stacktrace.h"
-
-_START_GOOGLE_NAMESPACE_
-
-// Given a pointer to a stack frame, locate and return the calling
-// stackframe, or return NULL if no stackframe can be found. Perform sanity
-// checks (the strictness of which is controlled by the boolean parameter
-// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
-template<bool STRICT_UNWINDING>
-static void **NextStackFrame(void **old_sp) {
-  void **new_sp = (void **) *old_sp;
-
-  // Check that the transition from frame pointer old_sp to frame
-  // pointer new_sp isn't clearly bogus
-  if (STRICT_UNWINDING) {
-    // With the stack growing downwards, older stack frame must be
-    // at a greater address that the current one.
-    if (new_sp <= old_sp) return NULL;
-    // Assume stack frames larger than 100,000 bytes are bogus.
-    if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return NULL;
-  } else {
-    // In the non-strict mode, allow discontiguous stack frames.
-    // (alternate-signal-stacks for example).
-    if (new_sp == old_sp) return NULL;
-    // And allow frames upto about 1MB.
-    if ((new_sp > old_sp)
-        && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return NULL;
-  }
-  if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return NULL;
-  return new_sp;
-}
-
-// This ensures that GetStackTrace stes up the Link Register properly.
-void StacktracePowerPCDummyFunction() __attribute__((noinline));
-void StacktracePowerPCDummyFunction() { __asm__ volatile(""); }
-
-// If you change this function, also change GetStackFrames below.
-int GetStackTrace(void** result, int max_depth, int skip_count) {
-  void **sp;
-  // Apple OS X uses an old version of gnu as -- both Darwin 7.9.0 (Panther)
-  // and Darwin 8.8.1 (Tiger) use as 1.38.  This means we have to use a
-  // different asm syntax.  I don't know quite the best way to discriminate
-  // systems using the old as from the new one; I've gone with __APPLE__.
-#ifdef __APPLE__
-  __asm__ volatile ("mr %0,r1" : "=r" (sp));
-#else
-  __asm__ volatile ("mr %0,1" : "=r" (sp));
-#endif
-
-  // On PowerPC, the "Link Register" or "Link Record" (LR), is a stack
-  // entry that holds the return address of the subroutine call (what
-  // instruction we run after our function finishes).  This is the
-  // same as the stack-pointer of our parent routine, which is what we
-  // want here.  While the compiler will always(?) set up LR for
-  // subroutine calls, it may not for leaf functions (such as this one).
-  // This routine forces the compiler (at least gcc) to push it anyway.
-  StacktracePowerPCDummyFunction();
-
-  // The LR save area is used by the callee, so the top entry is bogus.
-  skip_count++;
-
-  int n = 0;
-  while (sp && n < max_depth) {
-    if (skip_count > 0) {
-      skip_count--;
-    } else {
-      // PowerPC has 3 main ABIs, which say where in the stack the
-      // Link Register is.  For DARWIN and AIX (used by apple and
-      // linux ppc64), it's in sp[2].  For SYSV (used by linux ppc),
-      // it's in sp[1].
-#if defined(_CALL_AIX) || defined(_CALL_DARWIN)
-      result[n++] = *(sp+2);
-#elif defined(_CALL_SYSV)
-      result[n++] = *(sp+1);
-#elif defined(__APPLE__) || ((defined(__linux) || defined(__linux__)) && defined(__PPC64__))
-      // This check is in case the compiler doesn't define _CALL_AIX/etc.
-      result[n++] = *(sp+2);
-#elif defined(__linux)
-      // This check is in case the compiler doesn't define _CALL_SYSV.
-      result[n++] = *(sp+1);
-#else
-#error Need to specify the PPC ABI for your archiecture.
-#endif
-    }
-    // Use strict unwinding rules.
-    sp = NextStackFrame<true>(sp);
-  }
-  return n;
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/stacktrace_unittest.cc b/third_party/glog/src/stacktrace_unittest.cc
deleted file mode 100644
index 77d34291b3..0000000000
--- a/third_party/glog/src/stacktrace_unittest.cc
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright (c) 2004, Google 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:
-//
-//     * 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 "utilities.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include "config.h"
-#include "base/commandlineflags.h"
-#include "glog/logging.h"
-#include "stacktrace.h"
-
-#ifdef HAVE_EXECINFO_H
-# include <execinfo.h>
-#endif
-
-using namespace GOOGLE_NAMESPACE;
-
-#ifdef HAVE_STACKTRACE
-
-// Obtain a backtrace, verify that the expected callers are present in the
-// backtrace, and maybe print the backtrace to stdout.
-
-// The sequence of functions whose return addresses we expect to see in the
-// backtrace.
-const int BACKTRACE_STEPS = 6;
-
-struct AddressRange {
-  const void *start, *end;
-};
-
-// Expected function [start,end] range.
-AddressRange expected_range[BACKTRACE_STEPS];
-
-#if __GNUC__
-// Using GCC extension: address of a label can be taken with '&&label'.
-// Start should be a label somewhere before recursive call, end somewhere
-// after it.
-#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange)           \
-  do {                                                                   \
-    (prange)->start = &&start_label;                                     \
-    (prange)->end = &&end_label;                                         \
-    CHECK_LT((prange)->start, (prange)->end);                            \
-  } while (0)
-// This macro expands into "unmovable" code (opaque to GCC), and that
-// prevents GCC from moving a_label up or down in the code.
-// Without it, there is no code following the 'end' label, and GCC
-// (4.3.1, 4.4.0) thinks it safe to assign &&end an address that is before
-// the recursive call.
-#define DECLARE_ADDRESS_LABEL(a_label)                                   \
-  a_label: do { __asm__ __volatile__(""); } while (0)
-// Gcc 4.4.0 may split function into multiple chunks, and the chunk
-// performing recursive call may end up later in the code then the return
-// instruction (this actually happens with FDO).
-// Adjust function range from __builtin_return_address.
-#define ADJUST_ADDRESS_RANGE_FROM_RA(prange)                             \
-  do {                                                                   \
-    void *ra = __builtin_return_address(0);                              \
-    CHECK_LT((prange)->start, ra);                                       \
-    if (ra > (prange)->end) {                                            \
-      printf("Adjusting range from %p..%p to %p..%p\n",                  \
-             (prange)->start, (prange)->end,                             \
-             (prange)->start, ra);                                       \
-      (prange)->end = ra;                                                \
-    }                                                                    \
-  } while (0)
-#else
-// Assume the Check* functions below are not longer than 256 bytes.
-#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange)           \
-  do {                                                                   \
-    (prange)->start = reinterpret_cast<const void *>(&fn);               \
-    (prange)->end = reinterpret_cast<const char *>(&fn) + 256;           \
-  } while (0)
-#define DECLARE_ADDRESS_LABEL(a_label) do { } while (0)
-#define ADJUST_ADDRESS_RANGE_FROM_RA(prange) do { } while (0)
-#endif  // __GNUC__
-
-//-----------------------------------------------------------------------//
-
-static void CheckRetAddrIsInFunction(void *ret_addr, const AddressRange &range)
-{
-  CHECK_GE(ret_addr, range.start);
-  CHECK_LE(ret_addr, range.end);
-}
-
-//-----------------------------------------------------------------------//
-
-void ATTRIBUTE_NOINLINE CheckStackTrace(int);
-static void ATTRIBUTE_NOINLINE CheckStackTraceLeaf(void) {
-  const int STACK_LEN = 10;
-  void *stack[STACK_LEN];
-  int size;
-
-  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[1]);
-  INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]);
-  DECLARE_ADDRESS_LABEL(start);
-  size = GetStackTrace(stack, STACK_LEN, 0);
-  printf("Obtained %d stack frames.\n", size);
-  CHECK_GE(size, 1);
-  CHECK_LE(size, STACK_LEN);
-
-  if (1) {
-#ifdef HAVE_EXECINFO_H
-    char **strings = backtrace_symbols(stack, size);
-    printf("Obtained %d stack frames.\n", size);
-    for (int i = 0; i < size; i++)
-      printf("%s %p\n", strings[i], stack[i]);
-    printf("CheckStackTrace() addr: %p\n", &CheckStackTrace);
-    free(strings);
-#endif
-  }
-  for (int i = 0; i < BACKTRACE_STEPS; i++) {
-    printf("Backtrace %d: expected: %p..%p  actual: %p ... ",
-           i, expected_range[i].start, expected_range[i].end, stack[i]);
-    fflush(stdout);
-    CheckRetAddrIsInFunction(stack[i], expected_range[i]);
-    printf("OK\n");
-  }
-  DECLARE_ADDRESS_LABEL(end);
-}
-
-//-----------------------------------------------------------------------//
-
-/* Dummy functions to make the backtrace more interesting. */
-static void ATTRIBUTE_NOINLINE CheckStackTrace4(int i) {
-  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[2]);
-  INIT_ADDRESS_RANGE(CheckStackTrace4, start, end, &expected_range[1]);
-  DECLARE_ADDRESS_LABEL(start);
-  for (int j = i; j >= 0; j--)
-    CheckStackTraceLeaf();
-  DECLARE_ADDRESS_LABEL(end);
-}
-static void ATTRIBUTE_NOINLINE CheckStackTrace3(int i) {
-  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[3]);
-  INIT_ADDRESS_RANGE(CheckStackTrace3, start, end, &expected_range[2]);
-  DECLARE_ADDRESS_LABEL(start);
-  for (int j = i; j >= 0; j--)
-    CheckStackTrace4(j);
-  DECLARE_ADDRESS_LABEL(end);
-}
-static void ATTRIBUTE_NOINLINE CheckStackTrace2(int i) {
-  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[4]);
-  INIT_ADDRESS_RANGE(CheckStackTrace2, start, end, &expected_range[3]);
-  DECLARE_ADDRESS_LABEL(start);
-  for (int j = i; j >= 0; j--)
-    CheckStackTrace3(j);
-  DECLARE_ADDRESS_LABEL(end);
-}
-static void ATTRIBUTE_NOINLINE CheckStackTrace1(int i) {
-  ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[5]);
-  INIT_ADDRESS_RANGE(CheckStackTrace1, start, end, &expected_range[4]);
-  DECLARE_ADDRESS_LABEL(start);
-  for (int j = i; j >= 0; j--)
-    CheckStackTrace2(j);
-  DECLARE_ADDRESS_LABEL(end);
-}
-#ifndef __GNUC__
-// On non-GNU environment, we use the address of `CheckStackTrace` to
-// guess the address range of this function. This guess is wrong for
-// non-static function on Windows. This is probably because
-// `&CheckStackTrace` returns the address of a trampoline like PLT,
-// not the actual address of `CheckStackTrace`.
-// See https://github.com/google/glog/issues/421 for the detail.
-static
-#endif
-void ATTRIBUTE_NOINLINE CheckStackTrace(int i) {
-  INIT_ADDRESS_RANGE(CheckStackTrace, start, end, &expected_range[5]);
-  DECLARE_ADDRESS_LABEL(start);
-  for (int j = i; j >= 0; j--)
-    CheckStackTrace1(j);
-  DECLARE_ADDRESS_LABEL(end);
-}
-
-//-----------------------------------------------------------------------//
-
-int main(int, char ** argv) {
-  FLAGS_logtostderr = true;
-  InitGoogleLogging(argv[0]);
-
-  CheckStackTrace(0);
-
-  printf("PASS\n");
-  return 0;
-}
-
-#else
-int main() {
-  printf("PASS (no stacktrace support)\n");
-  return 0;
-}
-#endif  // HAVE_STACKTRACE
diff --git a/third_party/glog/src/stacktrace_windows-inl.h b/third_party/glog/src/stacktrace_windows-inl.h
deleted file mode 100644
index f7553a6d97..0000000000
--- a/third_party/glog/src/stacktrace_windows-inl.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2000 - 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Andrew Schwartzmeyer
-//
-// Windows implementation - just use CaptureStackBackTrace
-
-#include "config.h"
-#include "port.h"
-#include "stacktrace.h"
-#include <dbghelp.h>
-
-_START_GOOGLE_NAMESPACE_
-
-int GetStackTrace(void** result, int max_depth, int skip_count) {
-  if (max_depth > 64) {
-    max_depth = 64;
-  }
-  skip_count++;  // we want to skip the current frame as well
-  // This API is thread-safe (moreover it walks only the current thread).
-  return CaptureStackBackTrace(skip_count, max_depth, result, NULL);
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/stacktrace_x86-inl.h b/third_party/glog/src/stacktrace_x86-inl.h
deleted file mode 100644
index 3b8d5a8282..0000000000
--- a/third_party/glog/src/stacktrace_x86-inl.h
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (c) 2000 - 2007, Google 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:
-//
-//     * 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.
-//
-// Produce stack trace
-
-#include <stdint.h>   // for uintptr_t
-
-#include "utilities.h"   // for OS_* macros
-
-#if !defined(OS_WINDOWS)
-#include <unistd.h>
-#include <sys/mman.h>
-#endif
-
-#include <stdio.h>  // for NULL
-#include "stacktrace.h"
-
-_START_GOOGLE_NAMESPACE_
-
-// Given a pointer to a stack frame, locate and return the calling
-// stackframe, or return NULL if no stackframe can be found. Perform sanity
-// checks (the strictness of which is controlled by the boolean parameter
-// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
-template<bool STRICT_UNWINDING>
-static void **NextStackFrame(void **old_sp) {
-  void **new_sp = (void **) *old_sp;
-
-  // Check that the transition from frame pointer old_sp to frame
-  // pointer new_sp isn't clearly bogus
-  if (STRICT_UNWINDING) {
-    // With the stack growing downwards, older stack frame must be
-    // at a greater address that the current one.
-    if (new_sp <= old_sp) return NULL;
-    // Assume stack frames larger than 100,000 bytes are bogus.
-    if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return NULL;
-  } else {
-    // In the non-strict mode, allow discontiguous stack frames.
-    // (alternate-signal-stacks for example).
-    if (new_sp == old_sp) return NULL;
-    // And allow frames upto about 1MB.
-    if ((new_sp > old_sp)
-        && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return NULL;
-  }
-  if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return NULL;
-#ifdef __i386__
-  // On 64-bit machines, the stack pointer can be very close to
-  // 0xffffffff, so we explicitly check for a pointer into the
-  // last two pages in the address space
-  if ((uintptr_t)new_sp >= 0xffffe000) return NULL;
-#endif
-#if !defined(OS_WINDOWS)
-  if (!STRICT_UNWINDING) {
-    // Lax sanity checks cause a crash in 32-bit tcmalloc/crash_reason_test
-    // on AMD-based machines with VDSO-enabled kernels.
-    // Make an extra sanity check to insure new_sp is readable.
-    // Note: NextStackFrame<false>() is only called while the program
-    //       is already on its last leg, so it's ok to be slow here.
-    static int page_size = getpagesize();
-    void *new_sp_aligned = (void *)((uintptr_t)new_sp & ~(page_size - 1));
-    if (msync(new_sp_aligned, page_size, MS_ASYNC) == -1)
-      return NULL;
-  }
-#endif
-  return new_sp;
-}
-
-// If you change this function, also change GetStackFrames below.
-int GetStackTrace(void** result, int max_depth, int skip_count) {
-  void **sp;
-
-#ifdef __GNUC__
-#if __GNUC__ * 100 + __GNUC_MINOR__ >= 402
-#define USE_BUILTIN_FRAME_ADDRESS
-#endif
-#endif
-
-#ifdef USE_BUILTIN_FRAME_ADDRESS
-  sp = reinterpret_cast<void**>(__builtin_frame_address(0));
-#elif defined(__i386__)
-  // Stack frame format:
-  //    sp[0]   pointer to previous frame
-  //    sp[1]   caller address
-  //    sp[2]   first argument
-  //    ...
-  sp = (void **)&result - 2;
-#elif defined(__x86_64__)
-  // __builtin_frame_address(0) can return the wrong address on gcc-4.1.0-k8
-  unsigned long rbp;
-  // Move the value of the register %rbp into the local variable rbp.
-  // We need 'volatile' to prevent this instruction from getting moved
-  // around during optimization to before function prologue is done.
-  // An alternative way to achieve this
-  // would be (before this __asm__ instruction) to call Noop() defined as
-  //   static void Noop() __attribute__ ((noinline));  // prevent inlining
-  //   static void Noop() { asm(""); }  // prevent optimizing-away
-  __asm__ volatile ("mov %%rbp, %0" : "=r" (rbp));
-  // Arguments are passed in registers on x86-64, so we can't just
-  // offset from &result
-  sp = (void **) rbp;
-#endif
-
-  int n = 0;
-  while (sp && n < max_depth) {
-    if (*(sp+1) == (void *)0) {
-      // In 64-bit code, we often see a frame that
-      // points to itself and has a return address of 0.
-      break;
-    }
-    if (skip_count > 0) {
-      skip_count--;
-    } else {
-      result[n++] = *(sp+1);
-    }
-    // Use strict unwinding rules.
-    sp = NextStackFrame<true>(sp);
-  }
-  return n;
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/stacktrace_x86_64-inl.h b/third_party/glog/src/stacktrace_x86_64-inl.h
deleted file mode 100644
index f7d1dca85b..0000000000
--- a/third_party/glog/src/stacktrace_x86_64-inl.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (c) 2005 - 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Arun Sharma
-//
-// Produce stack trace using libgcc
-
-extern "C" {
-#include <stdlib.h> // for NULL
-#include <unwind.h> // ABI defined unwinder
-}
-#include "stacktrace.h"
-
-_START_GOOGLE_NAMESPACE_
-
-typedef struct {
-  void **result;
-  int max_depth;
-  int skip_count;
-  int count;
-} trace_arg_t;
-
-
-// Workaround for the malloc() in _Unwind_Backtrace() issue.
-static _Unwind_Reason_Code nop_backtrace(struct _Unwind_Context *uc, void *opq) {
-  return _URC_NO_REASON;
-}
-
-
-// This code is not considered ready to run until
-// static initializers run so that we are guaranteed
-// that any malloc-related initialization is done.
-static bool ready_to_run = false;
-class StackTraceInit {
- public:
-   StackTraceInit() {
-     // Extra call to force initialization
-     _Unwind_Backtrace(nop_backtrace, NULL);
-     ready_to_run = true;
-   }
-};
-
-static StackTraceInit module_initializer;  // Force initialization
-
-static _Unwind_Reason_Code GetOneFrame(struct _Unwind_Context *uc, void *opq) {
-  trace_arg_t *targ = (trace_arg_t *) opq;
-
-  if (targ->skip_count > 0) {
-    targ->skip_count--;
-  } else {
-    targ->result[targ->count++] = (void *) _Unwind_GetIP(uc);
-  }
-
-  if (targ->count == targ->max_depth)
-    return _URC_END_OF_STACK;
-
-  return _URC_NO_REASON;
-}
-
-// If you change this function, also change GetStackFrames below.
-int GetStackTrace(void** result, int max_depth, int skip_count) {
-  if (!ready_to_run)
-    return 0;
-
-  trace_arg_t targ;
-
-  skip_count += 1;         // Do not include the "GetStackTrace" frame
-
-  targ.result = result;
-  targ.max_depth = max_depth;
-  targ.skip_count = skip_count;
-  targ.count = 0;
-
-  _Unwind_Backtrace(GetOneFrame, &targ);
-
-  return targ.count;
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/stl_logging_unittest.cc b/third_party/glog/src/stl_logging_unittest.cc
deleted file mode 100644
index 269094c07b..0000000000
--- a/third_party/glog/src/stl_logging_unittest.cc
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright (c) 2003, Google 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:
-//
-//     * 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 "config.h"
-
-#ifdef HAVE_USING_OPERATOR
-
-#include <functional>
-#include <iostream>
-#include <map>
-#include <ostream>
-#include <string>
-#include <vector>
-
-#ifdef __GNUC__
-// C++0x isn't enabled by default in GCC and libc++ does not have
-// non-standard ext/* and tr1/unordered_*.
-# if defined(_LIBCPP_VERSION)
-#  ifndef GLOG_STL_LOGGING_FOR_UNORDERED
-#  define GLOG_STL_LOGGING_FOR_UNORDERED
-#  endif
-# else
-#  ifndef GLOG_STL_LOGGING_FOR_EXT_HASH
-#  define GLOG_STL_LOGGING_FOR_EXT_HASH
-#  endif
-#  ifndef GLOG_STL_LOGGING_FOR_EXT_SLIST
-#  define GLOG_STL_LOGGING_FOR_EXT_SLIST
-#  endif
-#  ifndef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
-#  define GLOG_STL_LOGGING_FOR_TR1_UNORDERED
-#  endif
-# endif
-#endif
-
-#include "glog/logging.h"
-#include "glog/stl_logging.h"
-#include "googletest.h"
-
-using namespace std;
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-using namespace __gnu_cxx;
-#endif
-
-struct user_hash {
-  size_t operator()(int x) const { return x; }
-};
-
-static void TestSTLLogging() {
-  {
-    // Test a sequence.
-    vector<int> v;
-    v.push_back(10);
-    v.push_back(20);
-    v.push_back(30);
-    ostringstream ss;
-    ss << v;
-    EXPECT_EQ(ss.str(), "10 20 30");
-    vector<int> copied_v(v);
-    CHECK_EQ(v, copied_v);  // This must compile.
-  }
-
-  {
-    // Test a sorted pair associative container.
-    map< int, string > m;
-    m[20] = "twenty";
-    m[10] = "ten";
-    m[30] = "thirty";
-    ostringstream ss;
-    ss << m;
-    EXPECT_EQ(ss.str(), "(10, ten) (20, twenty) (30, thirty)");
-    map< int, string > copied_m(m);
-    CHECK_EQ(m, copied_m);  // This must compile.
-  }
-
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-  {
-    // Test a hashed simple associative container.
-    hash_set<int> hs;
-    hs.insert(10);
-    hs.insert(20);
-    hs.insert(30);
-    ostringstream ss;
-    ss << hs;
-    EXPECT_EQ(ss.str(), "10 20 30");
-    hash_set<int> copied_hs(hs);
-    CHECK_EQ(hs, copied_hs);  // This must compile.
-  }
-#endif
-
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-  {
-    // Test a hashed pair associative container.
-    hash_map<int, string> hm;
-    hm[10] = "ten";
-    hm[20] = "twenty";
-    hm[30] = "thirty";
-    ostringstream ss;
-    ss << hm;
-    EXPECT_EQ(ss.str(), "(10, ten) (20, twenty) (30, thirty)");
-    hash_map<int, string> copied_hm(hm);
-    CHECK_EQ(hm, copied_hm);  // this must compile
-  }
-#endif
-
-  {
-    // Test a long sequence.
-    vector<int> v;
-    string expected;
-    for (int i = 0; i < 100; i++) {
-      v.push_back(i);
-      if (i > 0) expected += ' ';
-      char buf[256];
-      sprintf(buf, "%d", i);
-      expected += buf;
-    }
-    v.push_back(100);
-    expected += " ...";
-    ostringstream ss;
-    ss << v;
-    CHECK_EQ(ss.str(), expected.c_str());
-  }
-
-  {
-    // Test a sorted pair associative container.
-    // Use a non-default comparison functor.
-    map< int, string, greater<int> > m;
-    m[20] = "twenty";
-    m[10] = "ten";
-    m[30] = "thirty";
-    ostringstream ss;
-    ss << m;
-    EXPECT_EQ(ss.str(), "(30, thirty) (20, twenty) (10, ten)");
-    map< int, string, greater<int> > copied_m(m);
-    CHECK_EQ(m, copied_m);  // This must compile.
-  }
-
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-  {
-    // Test a hashed simple associative container.
-    // Use a user defined hash function.
-    hash_set<int, user_hash> hs;
-    hs.insert(10);
-    hs.insert(20);
-    hs.insert(30);
-    ostringstream ss;
-    ss << hs;
-    EXPECT_EQ(ss.str(), "10 20 30");
-    hash_set<int, user_hash> copied_hs(hs);
-    CHECK_EQ(hs, copied_hs);  // This must compile.
-  }
-#endif
-}
-
-int main(int, char**) {
-  TestSTLLogging();
-  std::cout << "PASS\n";
-  return 0;
-}
-
-#else
-
-#include <iostream>
-
-int main(int, char**) {
-  std::cout << "We don't support stl_logging for this compiler.\n"
-            << "(we need compiler support of 'using ::operator<<' "
-            << "for this feature.)\n";
-  return 0;
-}
-
-#endif  // HAVE_USING_OPERATOR
diff --git a/third_party/glog/src/symbolize.cc b/third_party/glog/src/symbolize.cc
deleted file mode 100644
index 76a1b0febe..0000000000
--- a/third_party/glog/src/symbolize.cc
+++ /dev/null
@@ -1,966 +0,0 @@
-// Copyright (c) 2006, Google 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:
-//
-//     * 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.
-//
-// Author: Satoru Takabayashi
-// Stack-footprint reduction work done by Raksit Ashok
-//
-// Implementation note:
-//
-// We don't use heaps but only use stacks.  We want to reduce the
-// stack consumption so that the symbolizer can run on small stacks.
-//
-// Here are some numbers collected with GCC 4.1.0 on x86:
-// - sizeof(Elf32_Sym)  = 16
-// - sizeof(Elf32_Shdr) = 40
-// - sizeof(Elf64_Sym)  = 24
-// - sizeof(Elf64_Shdr) = 64
-//
-// This implementation is intended to be async-signal-safe but uses
-// some functions which are not guaranteed to be so, such as memchr()
-// and memmove().  We assume they are async-signal-safe.
-//
-// Additional header can be specified by the GLOG_BUILD_CONFIG_INCLUDE
-// macro to add platform specific defines (e.g. OS_OPENBSD).
-
-#ifdef GLOG_BUILD_CONFIG_INCLUDE
-#include GLOG_BUILD_CONFIG_INCLUDE
-#endif  // GLOG_BUILD_CONFIG_INCLUDE
-
-#include "utilities.h"
-
-#if defined(HAVE_SYMBOLIZE)
-
-#include <string.h>
-
-#include <algorithm>
-#include <limits>
-
-#include "symbolize.h"
-#include "demangle.h"
-
-_START_GOOGLE_NAMESPACE_
-
-// We don't use assert() since it's not guaranteed to be
-// async-signal-safe.  Instead we define a minimal assertion
-// macro. So far, we don't need pretty printing for __FILE__, etc.
-
-// A wrapper for abort() to make it callable in ? :.
-static int AssertFail() {
-  abort();
-  return 0;  // Should not reach.
-}
-
-#define SAFE_ASSERT(expr) ((expr) ? 0 : AssertFail())
-
-static SymbolizeCallback g_symbolize_callback = NULL;
-void InstallSymbolizeCallback(SymbolizeCallback callback) {
-  g_symbolize_callback = callback;
-}
-
-static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback =
-    NULL;
-void InstallSymbolizeOpenObjectFileCallback(
-    SymbolizeOpenObjectFileCallback callback) {
-  g_symbolize_open_object_file_callback = callback;
-}
-
-// This function wraps the Demangle function to provide an interface
-// where the input symbol is demangled in-place.
-// To keep stack consumption low, we would like this function to not
-// get inlined.
-static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size) {
-  char demangled[256];  // Big enough for sane demangled symbols.
-  if (Demangle(out, demangled, sizeof(demangled))) {
-    // Demangling succeeded. Copy to out if the space allows.
-    size_t len = strlen(demangled);
-    if (len + 1 <= (size_t)out_size) {  // +1 for '\0'.
-      SAFE_ASSERT(len < sizeof(demangled));
-      memmove(out, demangled, len + 1);
-    }
-  }
-}
-
-_END_GOOGLE_NAMESPACE_
-
-#if defined(__ELF__)
-
-#if defined(HAVE_DLFCN_H)
-#include <dlfcn.h>
-#endif
-#if defined(OS_OPENBSD)
-#include <sys/exec_elf.h>
-#else
-#include <elf.h>
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "symbolize.h"
-#include "config.h"
-#include "glog/raw_logging.h"
-
-// Re-runs fn until it doesn't cause EINTR.
-#define NO_INTR(fn)   do {} while ((fn) < 0 && errno == EINTR)
-
-_START_GOOGLE_NAMESPACE_
-
-// Read up to "count" bytes from "offset" in the file pointed by file
-// descriptor "fd" into the buffer starting at "buf" while handling short reads
-// and EINTR.  On success, return the number of bytes read.  Otherwise, return
-// -1.
-static ssize_t ReadFromOffset(const int fd, void *buf, const size_t count,
-                              const off_t offset) {
-  SAFE_ASSERT(fd >= 0);
-  SAFE_ASSERT(count <= std::numeric_limits<ssize_t>::max());
-  char *buf0 = reinterpret_cast<char *>(buf);
-  ssize_t num_bytes = 0;
-  while (num_bytes < count) {
-    ssize_t len;
-    NO_INTR(len = pread(fd, buf0 + num_bytes, count - num_bytes,
-                        offset + num_bytes));
-    if (len < 0) {  // There was an error other than EINTR.
-      return -1;
-    }
-    if (len == 0) {  // Reached EOF.
-      break;
-    }
-    num_bytes += len;
-  }
-  SAFE_ASSERT(num_bytes <= count);
-  return num_bytes;
-}
-
-// Try reading exactly "count" bytes from "offset" bytes in a file
-// pointed by "fd" into the buffer starting at "buf" while handling
-// short reads and EINTR.  On success, return true. Otherwise, return
-// false.
-static bool ReadFromOffsetExact(const int fd, void *buf,
-                                const size_t count, const off_t offset) {
-  ssize_t len = ReadFromOffset(fd, buf, count, offset);
-  return len == count;
-}
-
-// Returns elf_header.e_type if the file pointed by fd is an ELF binary.
-static int FileGetElfType(const int fd) {
-  ElfW(Ehdr) elf_header;
-  if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
-    return -1;
-  }
-  if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) {
-    return -1;
-  }
-  return elf_header.e_type;
-}
-
-// Read the section headers in the given ELF binary, and if a section
-// of the specified type is found, set the output to this section header
-// and return true.  Otherwise, return false.
-// To keep stack consumption low, we would like this function to not get
-// inlined.
-static ATTRIBUTE_NOINLINE bool
-GetSectionHeaderByType(const int fd, ElfW(Half) sh_num, const off_t sh_offset,
-                       ElfW(Word) type, ElfW(Shdr) *out) {
-  // Read at most 16 section headers at a time to save read calls.
-  ElfW(Shdr) buf[16];
-  for (int i = 0; i < sh_num;) {
-    const ssize_t num_bytes_left = (sh_num - i) * sizeof(buf[0]);
-    const ssize_t num_bytes_to_read =
-        (sizeof(buf) > num_bytes_left) ? num_bytes_left : sizeof(buf);
-    const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read,
-                                       sh_offset + i * sizeof(buf[0]));
-    if (len == -1) {
-      return false;
-    }
-    SAFE_ASSERT(len % sizeof(buf[0]) == 0);
-    const ssize_t num_headers_in_buf = len / sizeof(buf[0]);
-    SAFE_ASSERT(num_headers_in_buf <= sizeof(buf) / sizeof(buf[0]));
-    for (int j = 0; j < num_headers_in_buf; ++j) {
-      if (buf[j].sh_type == type) {
-        *out = buf[j];
-        return true;
-      }
-    }
-    i += num_headers_in_buf;
-  }
-  return false;
-}
-
-// There is no particular reason to limit section name to 63 characters,
-// but there has (as yet) been no need for anything longer either.
-const int kMaxSectionNameLen = 64;
-
-// name_len should include terminating '\0'.
-bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
-                            ElfW(Shdr) *out) {
-  ElfW(Ehdr) elf_header;
-  if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
-    return false;
-  }
-
-  ElfW(Shdr) shstrtab;
-  off_t shstrtab_offset = (elf_header.e_shoff +
-                           elf_header.e_shentsize * elf_header.e_shstrndx);
-  if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) {
-    return false;
-  }
-
-  for (int i = 0; i < elf_header.e_shnum; ++i) {
-    off_t section_header_offset = (elf_header.e_shoff +
-                                   elf_header.e_shentsize * i);
-    if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) {
-      return false;
-    }
-    char header_name[kMaxSectionNameLen];
-    if (sizeof(header_name) < name_len) {
-      RAW_LOG(WARNING, "Section name '%s' is too long (%" PRIuS "); "
-              "section will not be found (even if present).", name, name_len);
-      // No point in even trying.
-      return false;
-    }
-    off_t name_offset = shstrtab.sh_offset + out->sh_name;
-    ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset);
-    if (n_read == -1) {
-      return false;
-    } else if (n_read != name_len) {
-      // Short read -- name could be at end of file.
-      continue;
-    }
-    if (memcmp(header_name, name, name_len) == 0) {
-      return true;
-    }
-  }
-  return false;
-}
-
-// Read a symbol table and look for the symbol containing the
-// pc. Iterate over symbols in a symbol table and look for the symbol
-// containing "pc".  On success, return true and write the symbol name
-// to out.  Otherwise, return false.
-// To keep stack consumption low, we would like this function to not get
-// inlined.
-static ATTRIBUTE_NOINLINE bool
-FindSymbol(uint64_t pc, const int fd, char *out, int out_size,
-           uint64_t symbol_offset, const ElfW(Shdr) *strtab,
-           const ElfW(Shdr) *symtab) {
-  if (symtab == NULL) {
-    return false;
-  }
-  const int num_symbols = symtab->sh_size / symtab->sh_entsize;
-  for (int i = 0; i < num_symbols;) {
-    off_t offset = symtab->sh_offset + i * symtab->sh_entsize;
-
-    // If we are reading Elf64_Sym's, we want to limit this array to
-    // 32 elements (to keep stack consumption low), otherwise we can
-    // have a 64 element Elf32_Sym array.
-#if __WORDSIZE == 64
-#define NUM_SYMBOLS 32
-#else
-#define NUM_SYMBOLS 64
-#endif
-
-    // Read at most NUM_SYMBOLS symbols at once to save read() calls.
-    ElfW(Sym) buf[NUM_SYMBOLS];
-    int num_symbols_to_read = std::min(NUM_SYMBOLS, num_symbols - i);
-    const ssize_t len =
-        ReadFromOffset(fd, &buf, sizeof(buf[0]) * num_symbols_to_read, offset);
-    SAFE_ASSERT(len % sizeof(buf[0]) == 0);
-    const ssize_t num_symbols_in_buf = len / sizeof(buf[0]);
-    SAFE_ASSERT(num_symbols_in_buf <= num_symbols_to_read);
-    for (int j = 0; j < num_symbols_in_buf; ++j) {
-      const ElfW(Sym)& symbol = buf[j];
-      uint64_t start_address = symbol.st_value;
-      start_address += symbol_offset;
-      uint64_t end_address = start_address + symbol.st_size;
-      if (symbol.st_value != 0 &&  // Skip null value symbols.
-          symbol.st_shndx != 0 &&  // Skip undefined symbols.
-          start_address <= pc && pc < end_address) {
-        ssize_t len1 = ReadFromOffset(fd, out, out_size,
-                                      strtab->sh_offset + symbol.st_name);
-        if (len1 <= 0 || memchr(out, '\0', out_size) == NULL) {
-          memset(out, 0, out_size);
-          return false;
-        }
-        return true;  // Obtained the symbol name.
-      }
-    }
-    i += num_symbols_in_buf;
-  }
-  return false;
-}
-
-// Get the symbol name of "pc" from the file pointed by "fd".  Process
-// both regular and dynamic symbol tables if necessary.  On success,
-// write the symbol name to "out" and return true.  Otherwise, return
-// false.
-static bool GetSymbolFromObjectFile(const int fd,
-                                    uint64_t pc,
-                                    char* out,
-                                    int out_size,
-                                    uint64_t base_address) {
-  // Read the ELF header.
-  ElfW(Ehdr) elf_header;
-  if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
-    return false;
-  }
-
-  ElfW(Shdr) symtab, strtab;
-
-  // Consult a regular symbol table first.
-  if (GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff,
-                             SHT_SYMTAB, &symtab)) {
-    if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff +
-                             symtab.sh_link * sizeof(symtab))) {
-      return false;
-    }
-    if (FindSymbol(pc, fd, out, out_size, base_address, &strtab, &symtab)) {
-      return true;  // Found the symbol in a regular symbol table.
-    }
-  }
-
-  // If the symbol is not found, then consult a dynamic symbol table.
-  if (GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff,
-                             SHT_DYNSYM, &symtab)) {
-    if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff +
-                             symtab.sh_link * sizeof(symtab))) {
-      return false;
-    }
-    if (FindSymbol(pc, fd, out, out_size, base_address, &strtab, &symtab)) {
-      return true;  // Found the symbol in a dynamic symbol table.
-    }
-  }
-
-  return false;
-}
-
-namespace {
-// Thin wrapper around a file descriptor so that the file descriptor
-// gets closed for sure.
-struct FileDescriptor {
-  const int fd_;
-  explicit FileDescriptor(int fd) : fd_(fd) {}
-  ~FileDescriptor() {
-    if (fd_ >= 0) {
-      close(fd_);
-    }
-  }
-  int get() { return fd_; }
-
- private:
-  explicit FileDescriptor(const FileDescriptor&);
-  void operator=(const FileDescriptor&);
-};
-
-// Helper class for reading lines from file.
-//
-// Note: we don't use ProcMapsIterator since the object is big (it has
-// a 5k array member) and uses async-unsafe functions such as sscanf()
-// and snprintf().
-class LineReader {
- public:
-  explicit LineReader(int fd, char *buf, int buf_len, off_t offset)
-      : fd_(fd),
-        buf_(buf),
-        buf_len_(buf_len),
-        offset_(offset),
-        bol_(buf),
-        eol_(buf),
-        eod_(buf) {}
-
-  // Read '\n'-terminated line from file.  On success, modify "bol"
-  // and "eol", then return true.  Otherwise, return false.
-  //
-  // Note: if the last line doesn't end with '\n', the line will be
-  // dropped.  It's an intentional behavior to make the code simple.
-  bool ReadLine(const char **bol, const char **eol) {
-    if (BufferIsEmpty()) {  // First time.
-      const ssize_t num_bytes = ReadFromOffset(fd_, buf_, buf_len_, offset_);
-      if (num_bytes <= 0) {  // EOF or error.
-        return false;
-      }
-      offset_ += num_bytes;
-      eod_ = buf_ + num_bytes;
-      bol_ = buf_;
-    } else {
-      bol_ = eol_ + 1;  // Advance to the next line in the buffer.
-      SAFE_ASSERT(bol_ <= eod_);  // "bol_" can point to "eod_".
-      if (!HasCompleteLine()) {
-        const int incomplete_line_length = eod_ - bol_;
-        // Move the trailing incomplete line to the beginning.
-        memmove(buf_, bol_, incomplete_line_length);
-        // Read text from file and append it.
-        char * const append_pos = buf_ + incomplete_line_length;
-        const int capacity_left = buf_len_ - incomplete_line_length;
-        const ssize_t num_bytes =
-            ReadFromOffset(fd_, append_pos, capacity_left, offset_);
-        if (num_bytes <= 0) {  // EOF or error.
-          return false;
-        }
-        offset_ += num_bytes;
-        eod_ = append_pos + num_bytes;
-        bol_ = buf_;
-      }
-    }
-    eol_ = FindLineFeed();
-    if (eol_ == NULL) {  // '\n' not found.  Malformed line.
-      return false;
-    }
-    *eol_ = '\0';  // Replace '\n' with '\0'.
-
-    *bol = bol_;
-    *eol = eol_;
-    return true;
-  }
-
-  // Beginning of line.
-  const char *bol() {
-    return bol_;
-  }
-
-  // End of line.
-  const char *eol() {
-    return eol_;
-  }
-
- private:
-  explicit LineReader(const LineReader&);
-  void operator=(const LineReader&);
-
-  char *FindLineFeed() {
-    return reinterpret_cast<char *>(memchr(bol_, '\n', eod_ - bol_));
-  }
-
-  bool BufferIsEmpty() {
-    return buf_ == eod_;
-  }
-
-  bool HasCompleteLine() {
-    return !BufferIsEmpty() && FindLineFeed() != NULL;
-  }
-
-  const int fd_;
-  char * const buf_;
-  const int buf_len_;
-  off_t offset_;
-  char *bol_;
-  char *eol_;
-  const char *eod_;  // End of data in "buf_".
-};
-}  // namespace
-
-// Place the hex number read from "start" into "*hex".  The pointer to
-// the first non-hex character or "end" is returned.
-static char *GetHex(const char *start, const char *end, uint64_t *hex) {
-  *hex = 0;
-  const char *p;
-  for (p = start; p < end; ++p) {
-    int ch = *p;
-    if ((ch >= '0' && ch <= '9') ||
-        (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) {
-      *hex = (*hex << 4) | (ch < 'A' ? ch - '0' : (ch & 0xF) + 9);
-    } else {  // Encountered the first non-hex character.
-      break;
-    }
-  }
-  SAFE_ASSERT(p <= end);
-  return const_cast<char *>(p);
-}
-
-// Searches for the object file (from /proc/self/maps) that contains
-// the specified pc.  If found, sets |start_address| to the start address
-// of where this object file is mapped in memory, sets the module base
-// address into |base_address|, copies the object file name into
-// |out_file_name|, and attempts to open the object file.  If the object
-// file is opened successfully, returns the file descriptor.  Otherwise,
-// returns -1.  |out_file_name_size| is the size of the file name buffer
-// (including the null-terminator).
-static ATTRIBUTE_NOINLINE int
-OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
-                                             uint64_t &start_address,
-                                             uint64_t &base_address,
-                                             char *out_file_name,
-                                             int out_file_name_size) {
-  int object_fd;
-
-  int maps_fd;
-  NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY));
-  FileDescriptor wrapped_maps_fd(maps_fd);
-  if (wrapped_maps_fd.get() < 0) {
-    return -1;
-  }
-
-  int mem_fd;
-  NO_INTR(mem_fd = open("/proc/self/mem", O_RDONLY));
-  FileDescriptor wrapped_mem_fd(mem_fd);
-  if (wrapped_mem_fd.get() < 0) {
-    return -1;
-  }
-
-  // Iterate over maps and look for the map containing the pc.  Then
-  // look into the symbol tables inside.
-  char buf[1024];  // Big enough for line of sane /proc/self/maps
-  int num_maps = 0;
-  LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf), 0);
-  while (true) {
-    num_maps++;
-    const char *cursor;
-    const char *eol;
-    if (!reader.ReadLine(&cursor, &eol)) {  // EOF or malformed line.
-      return -1;
-    }
-
-    // Start parsing line in /proc/self/maps.  Here is an example:
-    //
-    // 08048000-0804c000 r-xp 00000000 08:01 2142121    /bin/cat
-    //
-    // We want start address (08048000), end address (0804c000), flags
-    // (r-xp) and file name (/bin/cat).
-
-    // Read start address.
-    cursor = GetHex(cursor, eol, &start_address);
-    if (cursor == eol || *cursor != '-') {
-      return -1;  // Malformed line.
-    }
-    ++cursor;  // Skip '-'.
-
-    // Read end address.
-    uint64_t end_address;
-    cursor = GetHex(cursor, eol, &end_address);
-    if (cursor == eol || *cursor != ' ') {
-      return -1;  // Malformed line.
-    }
-    ++cursor;  // Skip ' '.
-
-    // Read flags.  Skip flags until we encounter a space or eol.
-    const char * const flags_start = cursor;
-    while (cursor < eol && *cursor != ' ') {
-      ++cursor;
-    }
-    // We expect at least four letters for flags (ex. "r-xp").
-    if (cursor == eol || cursor < flags_start + 4) {
-      return -1;  // Malformed line.
-    }
-
-    // Determine the base address by reading ELF headers in process memory.
-    ElfW(Ehdr) ehdr;
-    // Skip non-readable maps.
-    if (flags_start[0] == 'r' &&
-        ReadFromOffsetExact(mem_fd, &ehdr, sizeof(ElfW(Ehdr)), start_address) &&
-        memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) {
-      switch (ehdr.e_type) {
-        case ET_EXEC:
-          base_address = 0;
-          break;
-        case ET_DYN:
-          // Find the segment containing file offset 0. This will correspond
-          // to the ELF header that we just read. Normally this will have
-          // virtual address 0, but this is not guaranteed. We must subtract
-          // the virtual address from the address where the ELF header was
-          // mapped to get the base address.
-          //
-          // If we fail to find a segment for file offset 0, use the address
-          // of the ELF header as the base address.
-          base_address = start_address;
-          for (unsigned i = 0; i != ehdr.e_phnum; ++i) {
-            ElfW(Phdr) phdr;
-            if (ReadFromOffsetExact(
-                    mem_fd, &phdr, sizeof(phdr),
-                    start_address + ehdr.e_phoff + i * sizeof(phdr)) &&
-                phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
-              base_address = start_address - phdr.p_vaddr;
-              break;
-            }
-          }
-          break;
-        default:
-          // ET_REL or ET_CORE. These aren't directly executable, so they don't
-          // affect the base address.
-          break;
-      }
-    }
-
-    // Check start and end addresses.
-    if (!(start_address <= pc && pc < end_address)) {
-      continue;  // We skip this map.  PC isn't in this map.
-    }
-
-   // Check flags.  We are only interested in "r*x" maps.
-    if (flags_start[0] != 'r' || flags_start[2] != 'x') {
-      continue;  // We skip this map.
-    }
-    ++cursor;  // Skip ' '.
-
-    // Read file offset.
-    uint64_t file_offset;
-    cursor = GetHex(cursor, eol, &file_offset);
-    if (cursor == eol || *cursor != ' ') {
-      return -1;  // Malformed line.
-    }
-    ++cursor;  // Skip ' '.
-
-    // Skip to file name.  "cursor" now points to dev.  We need to
-    // skip at least two spaces for dev and inode.
-    int num_spaces = 0;
-    while (cursor < eol) {
-      if (*cursor == ' ') {
-        ++num_spaces;
-      } else if (num_spaces >= 2) {
-        // The first non-space character after skipping two spaces
-        // is the beginning of the file name.
-        break;
-      }
-      ++cursor;
-    }
-    if (cursor == eol) {
-      return -1;  // Malformed line.
-    }
-
-    // Finally, "cursor" now points to file name of our interest.
-    NO_INTR(object_fd = open(cursor, O_RDONLY));
-    if (object_fd < 0) {
-      // Failed to open object file.  Copy the object file name to
-      // |out_file_name|.
-      strncpy(out_file_name, cursor, out_file_name_size);
-      // Making sure |out_file_name| is always null-terminated.
-      out_file_name[out_file_name_size - 1] = '\0';
-      return -1;
-    }
-    return object_fd;
-  }
-}
-
-// POSIX doesn't define any async-signal safe function for converting
-// an integer to ASCII. We'll have to define our own version.
-// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the
-// conversion was successful or NULL otherwise. It never writes more than "sz"
-// bytes. Output will be truncated as needed, and a NUL character is always
-// appended.
-// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
-static char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) {
-  // Make sure we can write at least one NUL byte.
-  size_t n = 1;
-  if (n > sz)
-    return NULL;
-
-  if (base < 2 || base > 16) {
-    buf[0] = '\000';
-    return NULL;
-  }
-
-  char *start = buf;
-
-  uintptr_t j = i;
-
-  // Handle negative numbers (only for base 10).
-  if (i < 0 && base == 10) {
-    // This does "j = -i" while avoiding integer overflow.
-    j = static_cast<uintptr_t>(-(i + 1)) + 1;
-
-    // Make sure we can write the '-' character.
-    if (++n > sz) {
-      buf[0] = '\000';
-      return NULL;
-    }
-    *start++ = '-';
-  }
-
-  // Loop until we have converted the entire number. Output at least one
-  // character (i.e. '0').
-  char *ptr = start;
-  do {
-    // Make sure there is still enough space left in our output buffer.
-    if (++n > sz) {
-      buf[0] = '\000';
-      return NULL;
-    }
-
-    // Output the next digit.
-    *ptr++ = "0123456789abcdef"[j % base];
-    j /= base;
-
-    if (padding > 0)
-      padding--;
-  } while (j > 0 || padding > 0);
-
-  // Terminate the output with a NUL character.
-  *ptr = '\000';
-
-  // Conversion to ASCII actually resulted in the digits being in reverse
-  // order. We can't easily generate them in forward order, as we can't tell
-  // the number of characters needed until we are done converting.
-  // So, now, we reverse the string (except for the possible "-" sign).
-  while (--ptr > start) {
-    char ch = *ptr;
-    *ptr = *start;
-    *start++ = ch;
-  }
-  return buf;
-}
-
-// Safely appends string |source| to string |dest|.  Never writes past the
-// buffer size |dest_size| and guarantees that |dest| is null-terminated.
-static void SafeAppendString(const char* source, char* dest, int dest_size) {
-  int dest_string_length = strlen(dest);
-  SAFE_ASSERT(dest_string_length < dest_size);
-  dest += dest_string_length;
-  dest_size -= dest_string_length;
-  strncpy(dest, source, dest_size);
-  // Making sure |dest| is always null-terminated.
-  dest[dest_size - 1] = '\0';
-}
-
-// Converts a 64-bit value into a hex string, and safely appends it to |dest|.
-// Never writes past the buffer size |dest_size| and guarantees that |dest| is
-// null-terminated.
-static void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) {
-  // 64-bit numbers in hex can have up to 16 digits.
-  char buf[17] = {'\0'};
-  SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size);
-}
-
-// The implementation of our symbolization routine.  If it
-// successfully finds the symbol containing "pc" and obtains the
-// symbol name, returns true and write the symbol name to "out".
-// Otherwise, returns false. If Callback function is installed via
-// InstallSymbolizeCallback(), the function is also called in this function,
-// and "out" is used as its output.
-// To keep stack consumption low, we would like this function to not
-// get inlined.
-static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
-                                                    int out_size) {
-  uint64_t pc0 = reinterpret_cast<uintptr_t>(pc);
-  uint64_t start_address = 0;
-  uint64_t base_address = 0;
-  int object_fd = -1;
-
-  if (out_size < 1) {
-    return false;
-  }
-  out[0] = '\0';
-  SafeAppendString("(", out, out_size);
-
-  if (g_symbolize_open_object_file_callback) {
-    object_fd = g_symbolize_open_object_file_callback(pc0, start_address,
-                                                      base_address, out + 1,
-                                                      out_size - 1);
-  } else {
-    object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, start_address,
-                                                             base_address,
-                                                             out + 1,
-                                                             out_size - 1);
-  }
-
-  FileDescriptor wrapped_object_fd(object_fd);
-
-#if defined(PRINT_UNSYMBOLIZED_STACK_TRACES)
-  {
-#else
-  // Check whether a file name was returned.
-  if (object_fd < 0) {
-#endif
-    if (out[1]) {
-      // The object file containing PC was determined successfully however the
-      // object file was not opened successfully.  This is still considered
-      // success because the object file name and offset are known and tools
-      // like asan_symbolize.py can be used for the symbolization.
-      out[out_size - 1] = '\0';  // Making sure |out| is always null-terminated.
-      SafeAppendString("+0x", out, out_size);
-      SafeAppendHexNumber(pc0 - base_address, out, out_size);
-      SafeAppendString(")", out, out_size);
-      return true;
-    }
-    // Failed to determine the object file containing PC.  Bail out.
-    return false;
-  }
-  int elf_type = FileGetElfType(wrapped_object_fd.get());
-  if (elf_type == -1) {
-    return false;
-  }
-  if (g_symbolize_callback) {
-    // Run the call back if it's installed.
-    // Note: relocation (and much of the rest of this code) will be
-    // wrong for prelinked shared libraries and PIE executables.
-    uint64_t relocation = (elf_type == ET_DYN) ? start_address : 0;
-    int num_bytes_written = g_symbolize_callback(wrapped_object_fd.get(),
-                                                 pc, out, out_size,
-                                                 relocation);
-    if (num_bytes_written > 0) {
-      out += num_bytes_written;
-      out_size -= num_bytes_written;
-    }
-  }
-  if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0,
-                               out, out_size, base_address)) {
-    if (out[1] && !g_symbolize_callback) {
-      // The object file containing PC was opened successfully however the
-      // symbol was not found. The object may have been stripped. This is still
-      // considered success because the object file name and offset are known
-      // and tools like asan_symbolize.py can be used for the symbolization.
-      out[out_size - 1] = '\0';  // Making sure |out| is always null-terminated.
-      SafeAppendString("+0x", out, out_size);
-      SafeAppendHexNumber(pc0 - base_address, out, out_size);
-      SafeAppendString(")", out, out_size);
-      return true;
-    }
-    return false;
-  }
-
-  // Symbolization succeeded.  Now we try to demangle the symbol.
-  DemangleInplace(out, out_size);
-  return true;
-}
-
-_END_GOOGLE_NAMESPACE_
-
-#elif defined(OS_MACOSX) && defined(HAVE_DLADDR)
-
-#include <dlfcn.h>
-#include <string.h>
-
-_START_GOOGLE_NAMESPACE_
-
-static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
-                                                    int out_size) {
-  Dl_info info;
-  if (dladdr(pc, &info)) {
-    if ((int)strlen(info.dli_sname) < out_size) {
-      strcpy(out, info.dli_sname);
-      // Symbolization succeeded.  Now we try to demangle the symbol.
-      DemangleInplace(out, out_size);
-      return true;
-    }
-  }
-  return false;
-}
-
-_END_GOOGLE_NAMESPACE_
-
-#elif defined(OS_WINDOWS) || defined(OS_CYGWIN)
-
-#include <windows.h>
-#include <dbghelp.h>
-
-#ifdef _MSC_VER
-#pragma comment(lib, "dbghelp")
-#endif
-
-_START_GOOGLE_NAMESPACE_
-
-class SymInitializer {
-public:
-  HANDLE process;
-  bool ready;
-  SymInitializer() : process(NULL), ready(false) {
-    // Initialize the symbol handler.
-    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx
-    process = GetCurrentProcess();
-    // Defer symbol loading.
-    // We do not request undecorated symbols with SYMOPT_UNDNAME
-    // because the mangling library calls UnDecorateSymbolName.
-    SymSetOptions(SYMOPT_DEFERRED_LOADS);
-    if (SymInitialize(process, NULL, true)) {
-      ready = true;
-    }
-  }
-  ~SymInitializer() {
-    SymCleanup(process);
-    // We do not need to close `HANDLE process` because it's a "pseudo handle."
-  }
-private:
-  SymInitializer(const SymInitializer&);
-  SymInitializer& operator=(const SymInitializer&);
-};
-
-static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
-                                                      int out_size) {
-  const static SymInitializer symInitializer;
-  if (!symInitializer.ready) {
-    return false;
-  }
-  // Resolve symbol information from address.
-  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx
-  char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
-  SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(buf);
-  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
-  symbol->MaxNameLen = MAX_SYM_NAME;
-  // We use the ANSI version to ensure the string type is always `char *`.
-  // This could break if a symbol has Unicode in it.
-  BOOL ret = SymFromAddr(symInitializer.process,
-                         reinterpret_cast<DWORD64>(pc), 0, symbol);
-  if (ret == 1 && static_cast<int>(symbol->NameLen) < out_size) {
-    // `NameLen` does not include the null terminating character.
-    strncpy(out, symbol->Name, static_cast<size_t>(symbol->NameLen) + 1);
-    out[static_cast<size_t>(symbol->NameLen)] = '\0';
-    // Symbolization succeeded.  Now we try to demangle the symbol.
-    DemangleInplace(out, out_size);
-    return true;
-  }
-  return false;
-}
-
-_END_GOOGLE_NAMESPACE_
-
-#else
-# error BUG: HAVE_SYMBOLIZE was wrongly set
-#endif
-
-_START_GOOGLE_NAMESPACE_
-
-bool Symbolize(void *pc, char *out, int out_size) {
-  SAFE_ASSERT(out_size >= 0);
-  return SymbolizeAndDemangle(pc, out, out_size);
-}
-
-_END_GOOGLE_NAMESPACE_
-
-#else  /* HAVE_SYMBOLIZE */
-
-#include <assert.h>
-
-#include "config.h"
-
-_START_GOOGLE_NAMESPACE_
-
-// TODO: Support other environments.
-bool Symbolize(void *pc, char *out, int out_size) {
-  assert(0);
-  return false;
-}
-
-_END_GOOGLE_NAMESPACE_
-
-#endif
diff --git a/third_party/glog/src/symbolize.h b/third_party/glog/src/symbolize.h
deleted file mode 100644
index c6f9ec4360..0000000000
--- a/third_party/glog/src/symbolize.h
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (c) 2006, Google 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:
-//
-//     * 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.
-//
-// Author: Satoru Takabayashi
-//
-// This library provides Symbolize() function that symbolizes program
-// counters to their corresponding symbol names on linux platforms.
-// This library has a minimal implementation of an ELF symbol table
-// reader (i.e. it doesn't depend on libelf, etc.).
-//
-// The algorithm used in Symbolize() is as follows.
-//
-//   1. Go through a list of maps in /proc/self/maps and find the map
-//   containing the program counter.
-//
-//   2. Open the mapped file and find a regular symbol table inside.
-//   Iterate over symbols in the symbol table and look for the symbol
-//   containing the program counter.  If such a symbol is found,
-//   obtain the symbol name, and demangle the symbol if possible.
-//   If the symbol isn't found in the regular symbol table (binary is
-//   stripped), try the same thing with a dynamic symbol table.
-//
-// Note that Symbolize() is originally implemented to be used in
-// FailureSignalHandler() in base/google.cc.  Hence it doesn't use
-// malloc() and other unsafe operations.  It should be both
-// thread-safe and async-signal-safe.
-
-#ifndef BASE_SYMBOLIZE_H_
-#define BASE_SYMBOLIZE_H_
-
-#include "utilities.h"
-#include "config.h"
-#include "glog/logging.h"
-
-#ifdef HAVE_SYMBOLIZE
-
-#if defined(__ELF__)  // defined by gcc
-#if defined(__OpenBSD__)
-#include <sys/exec_elf.h>
-#else
-#include <elf.h>
-#endif
-
-#if !defined(ANDROID)
-#include <link.h>  // For ElfW() macro.
-#endif
-
-// For systems where SIZEOF_VOID_P is not defined, determine it
-// based on __LP64__ (defined by gcc on 64-bit systems)
-#if !defined(SIZEOF_VOID_P)
-# if defined(__LP64__)
-#  define SIZEOF_VOID_P 8
-# else
-#  define SIZEOF_VOID_P 4
-# endif
-#endif
-
-// If there is no ElfW macro, let's define it by ourself.
-#ifndef ElfW
-# if SIZEOF_VOID_P == 4
-#  define ElfW(type) Elf32_##type
-# elif SIZEOF_VOID_P == 8
-#  define ElfW(type) Elf64_##type
-# else
-#  error "Unknown sizeof(void *)"
-# endif
-#endif
-
-_START_GOOGLE_NAMESPACE_
-
-// Gets the section header for the given name, if it exists. Returns true on
-// success. Otherwise, returns false.
-bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
-                            ElfW(Shdr) *out);
-
-_END_GOOGLE_NAMESPACE_
-
-#endif  /* __ELF__ */
-
-_START_GOOGLE_NAMESPACE_
-
-// Restrictions on the callbacks that follow:
-//  - The callbacks must not use heaps but only use stacks.
-//  - The callbacks must be async-signal-safe.
-
-// Installs a callback function, which will be called right before a symbol name
-// is printed. The callback is intended to be used for showing a file name and a
-// line number preceding a symbol name.
-// "fd" is a file descriptor of the object file containing the program
-// counter "pc". The callback function should write output to "out"
-// and return the size of the output written. On error, the callback
-// function should return -1.
-typedef int (*SymbolizeCallback)(int fd,
-                                 void* pc,
-                                 char* out,
-                                 size_t out_size,
-                                 uint64_t relocation);
-void InstallSymbolizeCallback(SymbolizeCallback callback);
-
-// Installs a callback function, which will be called instead of
-// OpenObjectFileContainingPcAndGetStartAddress.  The callback is expected
-// to searches for the object file (from /proc/self/maps) that contains
-// the specified pc.  If found, sets |start_address| to the start address
-// of where this object file is mapped in memory, sets the module base
-// address into |base_address|, copies the object file name into
-// |out_file_name|, and attempts to open the object file.  If the object
-// file is opened successfully, returns the file descriptor.  Otherwise,
-// returns -1.  |out_file_name_size| is the size of the file name buffer
-// (including the null-terminator).
-typedef int (*SymbolizeOpenObjectFileCallback)(uint64_t pc,
-                                               uint64_t& start_address,
-                                               uint64_t& base_address,
-                                               char* out_file_name,
-                                               int out_file_name_size);
-void InstallSymbolizeOpenObjectFileCallback(
-    SymbolizeOpenObjectFileCallback callback);
-
-_END_GOOGLE_NAMESPACE_
-
-#endif
-
-_START_GOOGLE_NAMESPACE_
-
-// Symbolizes a program counter.  On success, returns true and write the
-// symbol name to "out".  The symbol name is demangled if possible
-// (supports symbols generated by GCC 3.x or newer).  Otherwise,
-// returns false.
-GOOGLE_GLOG_DLL_DECL bool Symbolize(void *pc, char *out, int out_size);
-
-_END_GOOGLE_NAMESPACE_
-
-#endif  // BASE_SYMBOLIZE_H_
diff --git a/third_party/glog/src/symbolize_unittest.cc b/third_party/glog/src/symbolize_unittest.cc
deleted file mode 100644
index 35cc2d31ff..0000000000
--- a/third_party/glog/src/symbolize_unittest.cc
+++ /dev/null
@@ -1,425 +0,0 @@
-// Copyright (c) 2006, Google 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:
-//
-//     * 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.
-//
-// Author: Satoru Takabayashi
-//
-// Unit tests for functions in symbolize.cc.
-
-#include "utilities.h"
-
-#include <signal.h>
-#include <iostream>
-
-#include "glog/logging.h"
-#include "symbolize.h"
-#include "googletest.h"
-#include "config.h"
-
-#ifdef HAVE_LIB_GFLAGS
-#include <gflags/gflags.h>
-using namespace GFLAGS_NAMESPACE;
-#endif
-
-using namespace std;
-using namespace GOOGLE_NAMESPACE;
-
-#if defined(HAVE_STACKTRACE)
-
-#define always_inline
-
-// A wrapper function for Symbolize() to make the unit test simple.
-static const char *TrySymbolize(void *pc) {
-  static char symbol[4096];
-  if (Symbolize(pc, symbol, sizeof(symbol))) {
-    return symbol;
-  } else {
-    return NULL;
-  }
-}
-
-# if defined(__ELF__)
-
-// This unit tests make sense only with GCC.
-// Uses lots of GCC specific features.
-#if defined(__GNUC__) && !defined(__OPENCC__)
-#  if __GNUC__ >= 4
-#    define TEST_WITH_MODERN_GCC
-#    if __i386__  // always_inline isn't supported for x86_64 with GCC 4.1.0.
-#      undef always_inline
-#      define always_inline __attribute__((always_inline))
-#      define HAVE_ALWAYS_INLINE
-#    endif  // __i386__
-#  else
-#  endif  // __GNUC__ >= 4
-#  if defined(__i386__) || defined(__x86_64__)
-#    define TEST_X86_32_AND_64 1
-#  endif  // defined(__i386__) || defined(__x86_64__)
-#endif
-
-// Make them C linkage to avoid mangled names.
-extern "C" {
-void nonstatic_func();
-void nonstatic_func() {
-  volatile int a = 0;
-  ++a;
-}
-
-static void static_func() {
-  volatile int a = 0;
-  ++a;
-}
-}
-
-TEST(Symbolize, Symbolize) {
-  // We do C-style cast since GCC 2.95.3 doesn't allow
-  // reinterpret_cast<void *>(&func).
-
-  // Compilers should give us pointers to them.
-  EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
-
-  // The name of an internal linkage symbol is not specified; allow either a
-  // mangled or an unmangled name here.
-  const char *static_func_symbol = TrySymbolize((void *)(&static_func));
-  CHECK(NULL != static_func_symbol);
-  EXPECT_TRUE(strcmp("static_func", static_func_symbol) == 0 ||
-              strcmp("static_func()", static_func_symbol) == 0);
-
-  EXPECT_TRUE(NULL == TrySymbolize(NULL));
-}
-
-struct Foo {
-  static void func(int x);
-};
-
-void ATTRIBUTE_NOINLINE Foo::func(int x) {
-  volatile int a = x;
-  ++a;
-}
-
-// With a modern GCC, Symbolize() should return demangled symbol
-// names.  Function parameters should be omitted.
-#ifdef TEST_WITH_MODERN_GCC
-TEST(Symbolize, SymbolizeWithDemangling) {
-  Foo::func(100);
-  EXPECT_STREQ("Foo::func()", TrySymbolize((void *)(&Foo::func)));
-}
-#endif
-
-// Tests that verify that Symbolize footprint is within some limit.
-
-// To measure the stack footprint of the Symbolize function, we create
-// a signal handler (for SIGUSR1 say) that calls the Symbolize function
-// on an alternate stack. This alternate stack is initialized to some
-// known pattern (0x55, 0x55, 0x55, ...). We then self-send this signal,
-// and after the signal handler returns, look at the alternate stack
-// buffer to see what portion has been touched.
-//
-// This trick gives us the the stack footprint of the signal handler.
-// But the signal handler, even before the call to Symbolize, consumes
-// some stack already. We however only want the stack usage of the
-// Symbolize function. To measure this accurately, we install two signal
-// handlers: one that does nothing and just returns, and another that
-// calls Symbolize. The difference between the stack consumption of these
-// two signals handlers should give us the Symbolize stack foorprint.
-
-static void *g_pc_to_symbolize;
-static char g_symbolize_buffer[4096];
-static char *g_symbolize_result;
-
-static void EmptySignalHandler(int signo) {}
-
-static void SymbolizeSignalHandler(int signo) {
-  if (Symbolize(g_pc_to_symbolize, g_symbolize_buffer,
-                sizeof(g_symbolize_buffer))) {
-    g_symbolize_result = g_symbolize_buffer;
-  } else {
-    g_symbolize_result = NULL;
-  }
-}
-
-const int kAlternateStackSize = 8096;
-const char kAlternateStackFillValue = 0x55;
-
-// These helper functions look at the alternate stack buffer, and figure
-// out what portion of this buffer has been touched - this is the stack
-// consumption of the signal handler running on this alternate stack.
-static ATTRIBUTE_NOINLINE bool StackGrowsDown(int *x) {
-  int y;
-  return &y < x;
-}
-static int GetStackConsumption(const char* alt_stack) {
-  int x;
-  if (StackGrowsDown(&x)) {
-    for (int i = 0; i < kAlternateStackSize; i++) {
-      if (alt_stack[i] != kAlternateStackFillValue) {
-        return (kAlternateStackSize - i);
-      }
-    }
-  } else {
-    for (int i = (kAlternateStackSize - 1); i >= 0; i--) {
-      if (alt_stack[i] != kAlternateStackFillValue) {
-        return i;
-      }
-    }
-  }
-  return -1;
-}
-
-#ifdef HAVE_SIGALTSTACK
-
-// Call Symbolize and figure out the stack footprint of this call.
-static const char *SymbolizeStackConsumption(void *pc, int *stack_consumed) {
-
-  g_pc_to_symbolize = pc;
-
-  // The alt-signal-stack cannot be heap allocated because there is a
-  // bug in glibc-2.2 where some signal handler setup code looks at the
-  // current stack pointer to figure out what thread is currently running.
-  // Therefore, the alternate stack must be allocated from the main stack
-  // itself.
-  char altstack[kAlternateStackSize];
-  memset(altstack, kAlternateStackFillValue, kAlternateStackSize);
-
-  // Set up the alt-signal-stack (and save the older one).
-  stack_t sigstk;
-  memset(&sigstk, 0, sizeof(stack_t));
-  stack_t old_sigstk;
-  sigstk.ss_sp = altstack;
-  sigstk.ss_size = kAlternateStackSize;
-  sigstk.ss_flags = 0;
-  CHECK_ERR(sigaltstack(&sigstk, &old_sigstk));
-
-  // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones).
-  struct sigaction sa;
-  memset(&sa, 0, sizeof(struct sigaction));
-  struct sigaction old_sa1, old_sa2;
-  sigemptyset(&sa.sa_mask);
-  sa.sa_flags = SA_ONSTACK;
-
-  // SIGUSR1 maps to EmptySignalHandler.
-  sa.sa_handler = EmptySignalHandler;
-  CHECK_ERR(sigaction(SIGUSR1, &sa, &old_sa1));
-
-  // SIGUSR2 maps to SymbolizeSignalHanlder.
-  sa.sa_handler = SymbolizeSignalHandler;
-  CHECK_ERR(sigaction(SIGUSR2, &sa, &old_sa2));
-
-  // Send SIGUSR1 signal and measure the stack consumption of the empty
-  // signal handler.
-  CHECK_ERR(kill(getpid(), SIGUSR1));
-  int stack_consumption1 = GetStackConsumption(altstack);
-
-  // Send SIGUSR2 signal and measure the stack consumption of the symbolize
-  // signal handler.
-  CHECK_ERR(kill(getpid(), SIGUSR2));
-  int stack_consumption2 = GetStackConsumption(altstack);
-
-  // The difference between the two stack consumption values is the
-  // stack footprint of the Symbolize function.
-  if (stack_consumption1 != -1 && stack_consumption2 != -1) {
-    *stack_consumed = stack_consumption2 - stack_consumption1;
-  } else {
-    *stack_consumed = -1;
-  }
-
-  // Log the stack consumption values.
-  LOG(INFO) << "Stack consumption of empty signal handler: "
-            << stack_consumption1;
-  LOG(INFO) << "Stack consumption of symbolize signal handler: "
-            << stack_consumption2;
-  LOG(INFO) << "Stack consumption of Symbolize: " << *stack_consumed;
-
-  // Now restore the old alt-signal-stack and signal handlers.
-  CHECK_ERR(sigaltstack(&old_sigstk, NULL));
-  CHECK_ERR(sigaction(SIGUSR1, &old_sa1, NULL));
-  CHECK_ERR(sigaction(SIGUSR2, &old_sa2, NULL));
-
-  return g_symbolize_result;
-}
-
-#ifdef __ppc64__
-// Symbolize stack consumption should be within 4kB.
-const int kStackConsumptionUpperLimit = 4096;
-#else
-// Symbolize stack consumption should be within 2kB.
-const int kStackConsumptionUpperLimit = 2048;
-#endif
-
-TEST(Symbolize, SymbolizeStackConsumption) {
-  int stack_consumed;
-  const char* symbol;
-
-  symbol = SymbolizeStackConsumption((void *)(&nonstatic_func),
-                                     &stack_consumed);
-  EXPECT_STREQ("nonstatic_func", symbol);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
-
-  // The name of an internal linkage symbol is not specified; allow either a
-  // mangled or an unmangled name here.
-  symbol = SymbolizeStackConsumption((void *)(&static_func),
-                                     &stack_consumed);
-  CHECK(NULL != symbol);
-  EXPECT_TRUE(strcmp("static_func", symbol) == 0 ||
-              strcmp("static_func()", symbol) == 0);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
-}
-
-#ifdef TEST_WITH_MODERN_GCC
-TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) {
-  Foo::func(100);
-  int stack_consumed;
-  const char* symbol;
-
-  symbol = SymbolizeStackConsumption((void *)(&Foo::func), &stack_consumed);
-
-  EXPECT_STREQ("Foo::func()", symbol);
-  EXPECT_GT(stack_consumed, 0);
-  EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
-}
-#endif
-
-#endif  // HAVE_SIGALTSTACK
-
-// x86 specific tests.  Uses some inline assembler.
-extern "C" {
-inline void* always_inline inline_func() {
-  void *pc = NULL;
-#ifdef TEST_X86_32_AND_64
-  __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
-#endif
-  return pc;
-}
-
-void* ATTRIBUTE_NOINLINE non_inline_func();
-void* ATTRIBUTE_NOINLINE non_inline_func() {
-  void *pc = NULL;
-#ifdef TEST_X86_32_AND_64
-  __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc));
-#endif
-  return pc;
-}
-
-static void ATTRIBUTE_NOINLINE TestWithPCInsideNonInlineFunction() {
-#if defined(TEST_X86_32_AND_64) && defined(HAVE_ATTRIBUTE_NOINLINE)
-  void *pc = non_inline_func();
-  const char *symbol = TrySymbolize(pc);
-  CHECK(symbol != NULL);
-  CHECK_STREQ(symbol, "non_inline_func");
-  cout << "Test case TestWithPCInsideNonInlineFunction passed." << endl;
-#endif
-}
-
-static void ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
-#if defined(TEST_X86_32_AND_64) && defined(HAVE_ALWAYS_INLINE)
-  void *pc = inline_func();  // Must be inlined.
-  const char *symbol = TrySymbolize(pc);
-  CHECK(symbol != NULL);
-  CHECK_STREQ(symbol, __FUNCTION__);
-  cout << "Test case TestWithPCInsideInlineFunction passed." << endl;
-#endif
-}
-}
-
-// Test with a return address.
-static void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
-#if defined(HAVE_ATTRIBUTE_NOINLINE)
-  void *return_address = __builtin_return_address(0);
-  const char *symbol = TrySymbolize(return_address);
-  CHECK(symbol != NULL);
-  CHECK_STREQ(symbol, "main");
-  cout << "Test case TestWithReturnAddress passed." << endl;
-#endif
-}
-
-# elif defined(OS_WINDOWS) || defined(OS_CYGWIN)
-
-#ifdef _MSC_VER
-#include <intrin.h>
-#pragma intrinsic(_ReturnAddress)
-#endif
-
-struct Foo {
-  static void func(int x);
-};
-
-__declspec(noinline) void Foo::func(int x) {
-  volatile int a = x;
-  ++a;
-}
-
-TEST(Symbolize, SymbolizeWithDemangling) {
-  Foo::func(100);
-  const char* ret = TrySymbolize((void *)(&Foo::func));
-  EXPECT_STREQ("public: static void __cdecl Foo::func(int)", ret);
-}
-
-__declspec(noinline) void TestWithReturnAddress() {
-  void *return_address =
-#ifdef __GNUC__ // Cygwin and MinGW support
-	  __builtin_return_address(0)
-#else
-	  _ReturnAddress()
-#endif
-	  ;
-  const char *symbol = TrySymbolize(return_address);
-  CHECK(symbol != NULL);
-  CHECK_STREQ(symbol, "main");
-  cout << "Test case TestWithReturnAddress passed." << endl;
-}
-# endif  // __ELF__
-#endif  // HAVE_STACKTRACE
-
-int main(int argc, char **argv) {
-  FLAGS_logtostderr = true;
-  InitGoogleLogging(argv[0]);
-  InitGoogleTest(&argc, argv);
-#if defined(HAVE_SYMBOLIZE) && defined(HAVE_STACKTRACE)
-# if defined(__ELF__)
-  // We don't want to get affected by the callback interface, that may be
-  // used to install some callback function at InitGoogle() time.
-  InstallSymbolizeCallback(NULL);
-
-  TestWithPCInsideInlineFunction();
-  TestWithPCInsideNonInlineFunction();
-  TestWithReturnAddress();
-  return RUN_ALL_TESTS();
-# elif defined(OS_WINDOWS) || defined(OS_CYGWIN)
-  TestWithReturnAddress();
-  return RUN_ALL_TESTS();
-# else  // OS_WINDOWS
-  printf("PASS (no symbolize_unittest support)\n");
-  return 0;
-# endif  // __ELF__
-#else
-  printf("PASS (no symbolize support)\n");
-  return 0;
-#endif  // HAVE_SYMBOLIZE
-}
diff --git a/third_party/glog/src/utilities.cc b/third_party/glog/src/utilities.cc
deleted file mode 100644
index 9a1e35d022..0000000000
--- a/third_party/glog/src/utilities.cc
+++ /dev/null
@@ -1,374 +0,0 @@
-// Copyright (c) 2008, Google 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:
-//
-//     * 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.
-//
-// Author: Shinichiro Hamaji
-
-#include "utilities.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <signal.h>
-#ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-#include <time.h>
-#if defined(HAVE_SYSCALL_H)
-#include <syscall.h>                 // for syscall()
-#elif defined(HAVE_SYS_SYSCALL_H)
-#include <sys/syscall.h>                 // for syscall()
-#endif
-#ifdef HAVE_SYSLOG_H
-# include <syslog.h>
-#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>  // For geteuid.
-#endif
-#ifdef HAVE_PWD_H
-# include <pwd.h>
-#endif
-
-#include "base/googleinit.h"
-
-using std::string;
-
-_START_GOOGLE_NAMESPACE_
-
-static const char* g_program_invocation_short_name = NULL;
-
-_END_GOOGLE_NAMESPACE_
-
-// The following APIs are all internal.
-#ifdef HAVE_STACKTRACE
-
-#include "stacktrace.h"
-#include "symbolize.h"
-#include "base/commandlineflags.h"
-
-GLOG_DEFINE_bool(symbolize_stacktrace, true,
-                 "Symbolize the stack trace in the tombstone");
-
-_START_GOOGLE_NAMESPACE_
-
-typedef void DebugWriter(const char*, void*);
-
-// The %p field width for printf() functions is two characters per byte.
-// For some environments, add two extra bytes for the leading "0x".
-static const int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
-
-static void DebugWriteToStderr(const char* data, void *) {
-  // This one is signal-safe.
-  if (write(STDERR_FILENO, data, strlen(data)) < 0) {
-    // Ignore errors.
-  }
-}
-
-static void DebugWriteToString(const char* data, void *arg) {
-  reinterpret_cast<string*>(arg)->append(data);
-}
-
-#ifdef HAVE_SYMBOLIZE
-// Print a program counter and its symbol name.
-static void DumpPCAndSymbol(DebugWriter *writerfn, void *arg, void *pc,
-                            const char * const prefix) {
-  char tmp[1024];
-  const char *symbol = "(unknown)";
-  // Symbolizes the previous address of pc because pc may be in the
-  // next function.  The overrun happens when the function ends with
-  // a call to a function annotated noreturn (e.g. CHECK).
-  if (Symbolize(reinterpret_cast<char *>(pc) - 1, tmp, sizeof(tmp))) {
-      symbol = tmp;
-  }
-  char buf[1024];
-  snprintf(buf, sizeof(buf), "%s@ %*p  %s\n",
-           prefix, kPrintfPointerFieldWidth, pc, symbol);
-  writerfn(buf, arg);
-}
-#endif
-
-static void DumpPC(DebugWriter *writerfn, void *arg, void *pc,
-                   const char * const prefix) {
-  char buf[100];
-  snprintf(buf, sizeof(buf), "%s@ %*p\n",
-           prefix, kPrintfPointerFieldWidth, pc);
-  writerfn(buf, arg);
-}
-
-// Dump current stack trace as directed by writerfn
-static void DumpStackTrace(int skip_count, DebugWriter *writerfn, void *arg) {
-  // Print stack trace
-  void* stack[32];
-  int depth = GetStackTrace(stack, ARRAYSIZE(stack), skip_count+1);
-  for (int i = 0; i < depth; i++) {
-#if defined(HAVE_SYMBOLIZE)
-    if (FLAGS_symbolize_stacktrace) {
-      DumpPCAndSymbol(writerfn, arg, stack[i], "    ");
-    } else {
-      DumpPC(writerfn, arg, stack[i], "    ");
-    }
-#else
-    DumpPC(writerfn, arg, stack[i], "    ");
-#endif
-  }
-}
-
-static void DumpStackTraceAndExit() {
-  DumpStackTrace(1, DebugWriteToStderr, NULL);
-
-  // TODO(hamaji): Use signal instead of sigaction?
-  if (IsFailureSignalHandlerInstalled()) {
-    // Set the default signal handler for SIGABRT, to avoid invoking our
-    // own signal handler installed by InstallFailureSignalHandler().
-#ifdef HAVE_SIGACTION
-    struct sigaction sig_action;
-    memset(&sig_action, 0, sizeof(sig_action));
-    sigemptyset(&sig_action.sa_mask);
-    sig_action.sa_handler = SIG_DFL;
-    sigaction(SIGABRT, &sig_action, NULL);
-#elif defined(OS_WINDOWS)
-    signal(SIGABRT, SIG_DFL);
-#endif  // HAVE_SIGACTION
-  }
-
-  abort();
-}
-
-_END_GOOGLE_NAMESPACE_
-
-#endif  // HAVE_STACKTRACE
-
-_START_GOOGLE_NAMESPACE_
-
-namespace glog_internal_namespace_ {
-
-const char* ProgramInvocationShortName() {
-  if (g_program_invocation_short_name != NULL) {
-    return g_program_invocation_short_name;
-  } else {
-    // TODO(hamaji): Use /proc/self/cmdline and so?
-    return "UNKNOWN";
-  }
-}
-
-bool IsGoogleLoggingInitialized() {
-  return g_program_invocation_short_name != NULL;
-}
-
-#ifdef OS_WINDOWS
-struct timeval {
-  long tv_sec, tv_usec;
-};
-
-// Based on: http://www.google.com/codesearch/p?hl=en#dR3YEbitojA/os_win32.c&q=GetSystemTimeAsFileTime%20license:bsd
-// See COPYING for copyright information.
-static int gettimeofday(struct timeval *tv, void* tz) {
-#define EPOCHFILETIME (116444736000000000ULL)
-  FILETIME ft;
-  LARGE_INTEGER li;
-  uint64 tt;
-
-  GetSystemTimeAsFileTime(&ft);
-  li.LowPart = ft.dwLowDateTime;
-  li.HighPart = ft.dwHighDateTime;
-  tt = (li.QuadPart - EPOCHFILETIME) / 10;
-  tv->tv_sec = tt / 1000000;
-  tv->tv_usec = tt % 1000000;
-
-  return 0;
-}
-#endif
-
-int64 CycleClock_Now() {
-  // TODO(hamaji): temporary impementation - it might be too slow.
-  struct timeval tv;
-  gettimeofday(&tv, NULL);
-  return static_cast<int64>(tv.tv_sec) * 1000000 + tv.tv_usec;
-}
-
-int64 UsecToCycles(int64 usec) {
-  return usec;
-}
-
-WallTime WallTime_Now() {
-  // Now, cycle clock is retuning microseconds since the epoch.
-  return CycleClock_Now() * 0.000001;
-}
-
-static int32 g_main_thread_pid = getpid();
-int32 GetMainThreadPid() {
-  return g_main_thread_pid;
-}
-
-bool PidHasChanged() {
-  int32 pid = getpid();
-  if (g_main_thread_pid == pid) {
-    return false;
-  }
-  g_main_thread_pid = pid;
-  return true;
-}
-
-pid_t GetTID() {
-  // On Linux and MacOSX, we try to use gettid().
-#if defined OS_LINUX || defined OS_MACOSX
-#ifndef __NR_gettid
-#ifdef OS_MACOSX
-#define __NR_gettid SYS_gettid
-#elif ! defined __i386__
-#error "Must define __NR_gettid for non-x86 platforms"
-#else
-#define __NR_gettid 224
-#endif
-#endif
-  static bool lacks_gettid = false;
-  if (!lacks_gettid) {
-    pid_t tid = syscall(__NR_gettid);
-    if (tid != -1) {
-      return tid;
-    }
-    // Technically, this variable has to be volatile, but there is a small
-    // performance penalty in accessing volatile variables and there should
-    // not be any serious adverse effect if a thread does not immediately see
-    // the value change to "true".
-    lacks_gettid = true;
-  }
-#endif  // OS_LINUX || OS_MACOSX
-
-  // If gettid() could not be used, we use one of the following.
-#if defined OS_LINUX
-  return getpid();  // Linux:  getpid returns thread ID when gettid is absent
-#elif defined OS_WINDOWS && !defined OS_CYGWIN
-  return GetCurrentThreadId();
-#elif defined(HAVE_PTHREAD)
-  // If none of the techniques above worked, we use pthread_self().
-  return (pid_t)(uintptr_t)pthread_self();
-#else
-  return -1;
-#endif
-}
-
-const char* const_basename(const char* filepath) {
-  const char* base = strrchr(filepath, '/');
-#ifdef OS_WINDOWS  // Look for either path separator in Windows
-  if (!base)
-    base = strrchr(filepath, '\\');
-#endif
-  return base ? (base+1) : filepath;
-}
-
-static string g_my_user_name;
-const string& MyUserName() {
-  return g_my_user_name;
-}
-static void MyUserNameInitializer() {
-  // TODO(hamaji): Probably this is not portable.
-#if defined(OS_WINDOWS)
-  const char* user = getenv("USERNAME");
-#else
-  const char* user = getenv("USER");
-#endif
-  if (user != NULL) {
-    g_my_user_name = user;
-  } else {
-#if defined(HAVE_PWD_H) && defined(HAVE_UNISTD_H)
-    struct passwd pwd;
-    struct passwd* result = NULL;
-    char buffer[1024] = {'\0'};
-    uid_t uid = geteuid();
-    int pwuid_res = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &result);
-    if (pwuid_res == 0 && result) {
-      g_my_user_name = pwd.pw_name;
-    } else {
-      snprintf(buffer, sizeof(buffer), "uid%d", uid);
-      g_my_user_name = buffer;
-    }
-#endif
-    if (g_my_user_name.empty()) {
-      g_my_user_name = "invalid-user";
-    }
-  }
-
-}
-REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer());
-
-#ifdef HAVE_STACKTRACE
-void DumpStackTraceToString(string* stacktrace) {
-  DumpStackTrace(1, DebugWriteToString, stacktrace);
-}
-#endif
-
-// We use an atomic operation to prevent problems with calling CrashReason
-// from inside the Mutex implementation (potentially through RAW_CHECK).
-static const CrashReason* g_reason = 0;
-
-void SetCrashReason(const CrashReason* r) {
-  sync_val_compare_and_swap(&g_reason,
-                            reinterpret_cast<const CrashReason*>(0),
-                            r);
-}
-
-void InitGoogleLoggingUtilities(const char* argv0) {
-  CHECK(!IsGoogleLoggingInitialized())
-      << "You called InitGoogleLogging() twice!";
-  const char* slash = strrchr(argv0, '/');
-#ifdef OS_WINDOWS
-  if (!slash)  slash = strrchr(argv0, '\\');
-#endif
-  g_program_invocation_short_name = slash ? slash + 1 : argv0;
-
-#ifdef HAVE_STACKTRACE
-  InstallFailureFunction(&DumpStackTraceAndExit);
-#endif
-}
-
-void ShutdownGoogleLoggingUtilities() {
-  CHECK(IsGoogleLoggingInitialized())
-      << "You called ShutdownGoogleLogging() without calling InitGoogleLogging() first!";
-  g_program_invocation_short_name = NULL;
-#ifdef HAVE_SYSLOG_H
-  closelog();
-#endif
-}
-
-}  // namespace glog_internal_namespace_
-
-_END_GOOGLE_NAMESPACE_
-
-// Make an implementation of stacktrace compiled.
-#ifdef STACKTRACE_H
-# include STACKTRACE_H
-# if 0
-// For include scanners which can't handle macro expansions.
-#  include "stacktrace_libunwind-inl.h"
-#  include "stacktrace_x86-inl.h"
-#  include "stacktrace_x86_64-inl.h"
-#  include "stacktrace_powerpc-inl.h"
-#  include "stacktrace_generic-inl.h"
-# endif
-#endif
diff --git a/third_party/glog/src/utilities.h b/third_party/glog/src/utilities.h
deleted file mode 100644
index c66f91467d..0000000000
--- a/third_party/glog/src/utilities.h
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright (c) 2008, Google 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:
-//
-//     * 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.
-//
-// Author: Shinichiro Hamaji
-//
-// Define utilties for glog internal usage.
-
-#ifndef UTILITIES_H__
-#define UTILITIES_H__
-
-#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
-# define OS_WINDOWS
-#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
-# define OS_CYGWIN
-#elif defined(linux) || defined(__linux) || defined(__linux__)
-# ifndef OS_LINUX
-#  define OS_LINUX
-# endif
-#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
-# define OS_MACOSX
-#elif defined(__FreeBSD__)
-# define OS_FREEBSD
-#elif defined(__NetBSD__)
-# define OS_NETBSD
-#elif defined(__OpenBSD__)
-# define OS_OPENBSD
-#else
-// TODO(hamaji): Add other platforms.
-#endif
-
-// printf macros for size_t, in the style of inttypes.h
-#ifdef _LP64
-#define __PRIS_PREFIX "z"
-#else
-#define __PRIS_PREFIX
-#endif
-
-// Use these macros after a % in a printf format string
-// to get correct 32/64 bit behavior, like this:
-// size_t size = records.size();
-// printf("%"PRIuS"\n", size);
-
-#define PRIdS __PRIS_PREFIX "d"
-#define PRIxS __PRIS_PREFIX "x"
-#define PRIuS __PRIS_PREFIX "u"
-#define PRIXS __PRIS_PREFIX "X"
-#define PRIoS __PRIS_PREFIX "o"
-
-#include "base/mutex.h"  // This must go first so we get _XOPEN_SOURCE
-
-#include <string>
-
-#if defined(OS_WINDOWS)
-# include "port.h"
-#endif
-
-#include "config.h"
-#include "glog/logging.h"
-
-// There are three different ways we can try to get the stack trace:
-//
-// 1) The libunwind library.  This is still in development, and as a
-//    separate library adds a new dependency, but doesn't need a frame
-//    pointer.  It also doesn't call malloc.
-//
-// 2) Our hand-coded stack-unwinder.  This depends on a certain stack
-//    layout, which is used by gcc (and those systems using a
-//    gcc-compatible ABI) on x86 systems, at least since gcc 2.95.
-//    It uses the frame pointer to do its work.
-//
-// 3) The gdb unwinder -- also the one used by the c++ exception code.
-//    It's obviously well-tested, but has a fatal flaw: it can call
-//    malloc() from the unwinder.  This is a problem because we're
-//    trying to use the unwinder to instrument malloc().
-//
-// 4) The Windows API CaptureStackTrace.
-//
-// Note: if you add a new implementation here, make sure it works
-// correctly when GetStackTrace() is called with max_depth == 0.
-// Some code may do that.
-
-#if defined(HAVE_LIB_UNWIND)
-# define STACKTRACE_H "stacktrace_libunwind-inl.h"
-#elif !defined(NO_FRAME_POINTER)
-# if defined(__i386__) && __GNUC__ >= 2
-#  define STACKTRACE_H "stacktrace_x86-inl.h"
-# elif defined(__x86_64__) && __GNUC__ >= 2 && HAVE_UNWIND_H
-#  define STACKTRACE_H "stacktrace_x86_64-inl.h"
-# elif (defined(__ppc__) || defined(__PPC__)) && __GNUC__ >= 2
-#  define STACKTRACE_H "stacktrace_powerpc-inl.h"
-# elif defined(OS_WINDOWS)
-#  define STACKTRACE_H "stacktrace_windows-inl.h"
-# endif
-#endif
-
-#if !defined(STACKTRACE_H) && defined(HAVE_EXECINFO_H)
-# define STACKTRACE_H "stacktrace_generic-inl.h"
-#endif
-
-#if defined(STACKTRACE_H)
-# define HAVE_STACKTRACE
-#endif
-
-#ifndef HAVE_SYMBOLIZE
-// defined by gcc
-#if defined(__ELF__) && defined(OS_LINUX)
-# define HAVE_SYMBOLIZE
-#elif defined(OS_MACOSX) && defined(HAVE_DLADDR)
-// Use dladdr to symbolize.
-# define HAVE_SYMBOLIZE
-#elif defined(OS_WINDOWS)
-// Use DbgHelp to symbolize
-# define HAVE_SYMBOLIZE
-#endif
-#endif // !defined(HAVE_SYMBOLIZE)
-
-#ifndef ARRAYSIZE
-// There is a better way, but this is good enough for our purpose.
-# define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a)))
-#endif
-
-_START_GOOGLE_NAMESPACE_
-
-namespace glog_internal_namespace_ {
-
-#ifdef HAVE___ATTRIBUTE__
-# define ATTRIBUTE_NOINLINE __attribute__ ((noinline))
-# define HAVE_ATTRIBUTE_NOINLINE
-#elif defined(OS_WINDOWS)
-# define ATTRIBUTE_NOINLINE __declspec(noinline)
-# define HAVE_ATTRIBUTE_NOINLINE
-#else
-# define ATTRIBUTE_NOINLINE
-#endif
-
-const char* ProgramInvocationShortName();
-
-bool IsGoogleLoggingInitialized();
-
-int64 CycleClock_Now();
-
-int64 UsecToCycles(int64 usec);
-
-typedef double WallTime;
-WallTime WallTime_Now();
-
-int32 GetMainThreadPid();
-bool PidHasChanged();
-
-pid_t GetTID();
-
-const std::string& MyUserName();
-
-// Get the part of filepath after the last path separator.
-// (Doesn't modify filepath, contrary to basename() in libgen.h.)
-const char* const_basename(const char* filepath);
-
-// Wrapper of __sync_val_compare_and_swap. If the GCC extension isn't
-// defined, we try the CPU specific logics (we only support x86 and
-// x86_64 for now) first, then use a naive implementation, which has a
-// race condition.
-template<typename T>
-inline T sync_val_compare_and_swap(T* ptr, T oldval, T newval) {
-#if defined(HAVE___SYNC_VAL_COMPARE_AND_SWAP)
-  return __sync_val_compare_and_swap(ptr, oldval, newval);
-#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-  T ret;
-  __asm__ __volatile__("lock; cmpxchg %1, (%2);"
-                       :"=a"(ret)
-                        // GCC may produces %sil or %dil for
-                        // constraint "r", but some of apple's gas
-                        // dosn't know the 8 bit registers.
-                        // We use "q" to avoid these registers.
-                       :"q"(newval), "q"(ptr), "a"(oldval)
-                       :"memory", "cc");
-  return ret;
-#else
-  T ret = *ptr;
-  if (ret == oldval) {
-    *ptr = newval;
-  }
-  return ret;
-#endif
-}
-
-void DumpStackTraceToString(std::string* stacktrace);
-
-struct CrashReason {
-  CrashReason() : filename(0), line_number(0), message(0), depth(0) {}
-
-  const char* filename;
-  int line_number;
-  const char* message;
-
-  // We'll also store a bit of stack trace context at the time of crash as
-  // it may not be available later on.
-  void* stack[32];
-  int depth;
-};
-
-void SetCrashReason(const CrashReason* r);
-
-void InitGoogleLoggingUtilities(const char* argv0);
-void ShutdownGoogleLoggingUtilities();
-
-}  // namespace glog_internal_namespace_
-
-_END_GOOGLE_NAMESPACE_
-
-using namespace GOOGLE_NAMESPACE::glog_internal_namespace_;
-
-#endif  // UTILITIES_H__
diff --git a/third_party/glog/src/utilities_unittest.cc b/third_party/glog/src/utilities_unittest.cc
deleted file mode 100644
index 38e847dfbf..0000000000
--- a/third_party/glog/src/utilities_unittest.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2008, Google 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:
-//
-//     * 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.
-//
-// Author: Shinichiro Hamaji
-#include "utilities.h"
-#include "googletest.h"
-#include "glog/logging.h"
-
-#ifdef HAVE_LIB_GFLAGS
-#include <gflags/gflags.h>
-using namespace GFLAGS_NAMESPACE;
-#endif
-
-using namespace GOOGLE_NAMESPACE;
-
-TEST(utilities, sync_val_compare_and_swap) {
-  bool now_entering = false;
-  EXPECT_FALSE(sync_val_compare_and_swap(&now_entering, false, true));
-  EXPECT_TRUE(sync_val_compare_and_swap(&now_entering, false, true));
-  EXPECT_TRUE(sync_val_compare_and_swap(&now_entering, false, true));
-}
-
-TEST(utilities, InitGoogleLoggingDeathTest) {
-  ASSERT_DEATH(InitGoogleLogging("foobar"), "");
-}
-
-int main(int argc, char **argv) {
-  InitGoogleLogging(argv[0]);
-  InitGoogleTest(&argc, argv);
-
-  CHECK_EQ(RUN_ALL_TESTS(), 0);
-}
diff --git a/third_party/glog/src/vlog_is_on.cc b/third_party/glog/src/vlog_is_on.cc
deleted file mode 100644
index e8fdbae7dc..0000000000
--- a/third_party/glog/src/vlog_is_on.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright (c) 1999, 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Ray Sidney and many others
-//
-// Broken out from logging.cc by Soren Lassen
-// logging_unittest.cc covers the functionality herein
-
-#include "utilities.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <cstdio>
-#include <string>
-#include "base/commandlineflags.h"
-#include "glog/logging.h"
-#include "glog/raw_logging.h"
-#include "base/googleinit.h"
-
-// glog doesn't have annotation
-#define ANNOTATE_BENIGN_RACE(address, description)
-
-using std::string;
-
-GLOG_DEFINE_int32(v, 0, "Show all VLOG(m) messages for m <= this."
-" Overridable by --vmodule.");
-
-GLOG_DEFINE_string(vmodule, "", "per-module verbose level."
-" Argument is a comma-separated list of <module name>=<log level>."
-" <module name> is a glob pattern, matched against the filename base"
-" (that is, name ignoring .cc/.h./-inl.h)."
-" <log level> overrides any value given by --v.");
-
-_START_GOOGLE_NAMESPACE_
-
-namespace glog_internal_namespace_ {
-
-// Used by logging_unittests.cc so can't make it static here.
-GOOGLE_GLOG_DLL_DECL bool SafeFNMatch_(const char* pattern,
-                                       size_t patt_len,
-                                       const char* str,
-                                       size_t str_len);
-
-// Implementation of fnmatch that does not need 0-termination
-// of arguments and does not allocate any memory,
-// but we only support "*" and "?" wildcards, not the "[...]" patterns.
-// It's not a static function for the unittest.
-GOOGLE_GLOG_DLL_DECL bool SafeFNMatch_(const char* pattern,
-                                       size_t patt_len,
-                                       const char* str,
-                                       size_t str_len) {
-  size_t p = 0;
-  size_t s = 0;
-  while (1) {
-    if (p == patt_len  &&  s == str_len) return true;
-    if (p == patt_len) return false;
-    if (s == str_len) return p+1 == patt_len  &&  pattern[p] == '*';
-    if (pattern[p] == str[s]  ||  pattern[p] == '?') {
-      p += 1;
-      s += 1;
-      continue;
-    }
-    if (pattern[p] == '*') {
-      if (p+1 == patt_len) return true;
-      do {
-        if (SafeFNMatch_(pattern+(p+1), patt_len-(p+1), str+s, str_len-s)) {
-          return true;
-        }
-        s += 1;
-      } while (s != str_len);
-      return false;
-    }
-    return false;
-  }
-}
-
-}  // namespace glog_internal_namespace_
-
-using glog_internal_namespace_::SafeFNMatch_;
-
-int32 kLogSiteUninitialized = 1000;
-
-// List of per-module log levels from FLAGS_vmodule.
-// Once created each element is never deleted/modified
-// except for the vlog_level: other threads will read VModuleInfo blobs
-// w/o locks and we'll store pointers to vlog_level at VLOG locations
-// that will never go away.
-// We can't use an STL struct here as we wouldn't know
-// when it's safe to delete/update it: other threads need to use it w/o locks.
-struct VModuleInfo {
-  string module_pattern;
-  mutable int32 vlog_level;  // Conceptually this is an AtomicWord, but it's
-                             // too much work to use AtomicWord type here
-                             // w/o much actual benefit.
-  const VModuleInfo* next;
-};
-
-// This protects the following global variables.
-static Mutex vmodule_lock;
-// Pointer to head of the VModuleInfo list.
-// It's a map from module pattern to logging level for those module(s).
-static VModuleInfo* vmodule_list = 0;
-// Boolean initialization flag.
-static bool inited_vmodule = false;
-
-// L >= vmodule_lock.
-static void VLOG2Initializer() {
-  vmodule_lock.AssertHeld();
-  // Can now parse --vmodule flag and initialize mapping of module-specific
-  // logging levels.
-  inited_vmodule = false;
-  const char* vmodule = FLAGS_vmodule.c_str();
-  const char* sep;
-  VModuleInfo* head = NULL;
-  VModuleInfo* tail = NULL;
-  while ((sep = strchr(vmodule, '=')) != NULL) {
-    string pattern(vmodule, sep - vmodule);
-    int module_level;
-    if (sscanf(sep, "=%d", &module_level) == 1) {
-      VModuleInfo* info = new VModuleInfo;
-      info->module_pattern = pattern;
-      info->vlog_level = module_level;
-      if (head)  tail->next = info;
-      else  head = info;
-      tail = info;
-    }
-    // Skip past this entry
-    vmodule = strchr(sep, ',');
-    if (vmodule == NULL) break;
-    vmodule++;  // Skip past ","
-  }
-  if (head) {  // Put them into the list at the head:
-    tail->next = vmodule_list;
-    vmodule_list = head;
-  }
-  inited_vmodule = true;
-}
-
-// This can be called very early, so we use SpinLock and RAW_VLOG here.
-int SetVLOGLevel(const char* module_pattern, int log_level) {
-  int result = FLAGS_v;
-  int const pattern_len = strlen(module_pattern);
-  bool found = false;
-  {
-    MutexLock l(&vmodule_lock);  // protect whole read-modify-write
-    for (const VModuleInfo* info = vmodule_list;
-         info != NULL; info = info->next) {
-      if (info->module_pattern == module_pattern) {
-        if (!found) {
-          result = info->vlog_level;
-          found = true;
-        }
-        info->vlog_level = log_level;
-      } else if (!found  &&
-                 SafeFNMatch_(info->module_pattern.c_str(),
-                              info->module_pattern.size(),
-                              module_pattern, pattern_len)) {
-        result = info->vlog_level;
-        found = true;
-      }
-    }
-    if (!found) {
-      VModuleInfo* info = new VModuleInfo;
-      info->module_pattern = module_pattern;
-      info->vlog_level = log_level;
-      info->next = vmodule_list;
-      vmodule_list = info;
-    }
-  }
-  RAW_VLOG(1, "Set VLOG level for \"%s\" to %d", module_pattern, log_level);
-  return result;
-}
-
-// NOTE: Individual VLOG statements cache the integer log level pointers.
-// NOTE: This function must not allocate memory or require any locks.
-bool InitVLOG3__(int32** site_flag, int32* site_default,
-                 const char* fname, int32 verbose_level) {
-  MutexLock l(&vmodule_lock);
-  bool read_vmodule_flag = inited_vmodule;
-  if (!read_vmodule_flag) {
-    VLOG2Initializer();
-  }
-
-  // protect the errno global in case someone writes:
-  // VLOG(..) << "The last error was " << strerror(errno)
-  int old_errno = errno;
-
-  // site_default normally points to FLAGS_v
-  int32* site_flag_value = site_default;
-
-  // Get basename for file
-  const char* base = strrchr(fname, '/');
-  base = base ? (base+1) : fname;
-  const char* base_end = strchr(base, '.');
-  size_t base_length = base_end ? size_t(base_end - base) : strlen(base);
-
-  // Trim out trailing "-inl" if any
-  if (base_length >= 4 && (memcmp(base+base_length-4, "-inl", 4) == 0)) {
-    base_length -= 4;
-  }
-
-  // TODO: Trim out _unittest suffix?  Perhaps it is better to have
-  // the extra control and just leave it there.
-
-  // find target in vector of modules, replace site_flag_value with
-  // a module-specific verbose level, if any.
-  for (const VModuleInfo* info = vmodule_list;
-       info != NULL; info = info->next) {
-    if (SafeFNMatch_(info->module_pattern.c_str(), info->module_pattern.size(),
-                     base, base_length)) {
-      site_flag_value = &info->vlog_level;
-        // value at info->vlog_level is now what controls
-        // the VLOG at the caller site forever
-      break;
-    }
-  }
-
-  // Cache the vlog value pointer if --vmodule flag has been parsed.
-  ANNOTATE_BENIGN_RACE(site_flag,
-                       "*site_flag may be written by several threads,"
-                       " but the value will be the same");
-  if (read_vmodule_flag) *site_flag = site_flag_value;
-
-  // restore the errno in case something recoverable went wrong during
-  // the initialization of the VLOG mechanism (see above note "protect the..")
-  errno = old_errno;
-  return *site_flag_value >= verbose_level;
-}
-
-_END_GOOGLE_NAMESPACE_
diff --git a/third_party/glog/src/windows/config.h b/third_party/glog/src/windows/config.h
deleted file mode 100755
index 2d23fb0815..0000000000
--- a/third_party/glog/src/windows/config.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* src/config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Namespace for Google classes */
-#define GOOGLE_NAMESPACE google
-
-/* Stops putting the code inside the Google namespace */
-#define _END_GOOGLE_NAMESPACE_ }
-
-/* Puts following code inside the Google namespace */
-#define _START_GOOGLE_NAMESPACE_ namespace google {
-
-/* Always the empty-string on non-windows systems. On windows, should be
-   "__declspec(dllexport)". This way, when we compile the dll, we export our
-   functions/classes. It's safe to define this here because config.h is only
-   used internally, to compile the DLL, and every DLL source file #includes
-   "config.h" before anything else. */
-#ifndef GOOGLE_GLOG_DLL_DECL
-# define GOOGLE_GLOG_IS_A_DLL  1   /* not set if you're statically linking */
-# define GOOGLE_GLOG_DLL_DECL  __declspec(dllexport)
-# define GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS  __declspec(dllimport)
-#endif
diff --git a/third_party/glog/src/windows/dirent.h b/third_party/glog/src/windows/dirent.h
deleted file mode 100644
index f7a46dafcb..0000000000
--- a/third_party/glog/src/windows/dirent.h
+++ /dev/null
@@ -1,1160 +0,0 @@
-/*
- * Dirent interface for Microsoft Visual Studio
- *
- * Copyright (C) 1998-2019 Toni Ronkko
- * This file is part of dirent.  Dirent may be freely distributed
- * under the MIT license.  For all details and documentation, see
- * https://github.com/tronkko/dirent
- */
-#ifndef DIRENT_H
-#define DIRENT_H
-
-/* Hide warnings about unreferenced local functions */
-#if defined(__clang__)
-#   pragma clang diagnostic ignored "-Wunused-function"
-#elif defined(_MSC_VER)
-#   pragma warning(disable:4505)
-#elif defined(__GNUC__)
-#   pragma GCC diagnostic ignored "-Wunused-function"
-#endif
-
-/*
- * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
- * Windows Sockets 2.0.
- */
-#ifndef WIN32_LEAN_AND_MEAN
-#   define WIN32_LEAN_AND_MEAN
-#endif
-#include <windows.h>
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <wchar.h>
-#include <string.h>
-#include <stdlib.h>
-#include <malloc.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-
-/* Indicates that d_type field is available in dirent structure */
-#define _DIRENT_HAVE_D_TYPE
-
-/* Indicates that d_namlen field is available in dirent structure */
-#define _DIRENT_HAVE_D_NAMLEN
-
-/* Entries missing from MSVC 6.0 */
-#if !defined(FILE_ATTRIBUTE_DEVICE)
-#   define FILE_ATTRIBUTE_DEVICE 0x40
-#endif
-
-/* File type and permission flags for stat(), general mask */
-#if !defined(S_IFMT)
-#   define S_IFMT _S_IFMT
-#endif
-
-/* Directory bit */
-#if !defined(S_IFDIR)
-#   define S_IFDIR _S_IFDIR
-#endif
-
-/* Character device bit */
-#if !defined(S_IFCHR)
-#   define S_IFCHR _S_IFCHR
-#endif
-
-/* Pipe bit */
-#if !defined(S_IFFIFO)
-#   define S_IFFIFO _S_IFFIFO
-#endif
-
-/* Regular file bit */
-#if !defined(S_IFREG)
-#   define S_IFREG _S_IFREG
-#endif
-
-/* Read permission */
-#if !defined(S_IREAD)
-#   define S_IREAD _S_IREAD
-#endif
-
-/* Write permission */
-#if !defined(S_IWRITE)
-#   define S_IWRITE _S_IWRITE
-#endif
-
-/* Execute permission */
-#if !defined(S_IEXEC)
-#   define S_IEXEC _S_IEXEC
-#endif
-
-/* Pipe */
-#if !defined(S_IFIFO)
-#   define S_IFIFO _S_IFIFO
-#endif
-
-/* Block device */
-#if !defined(S_IFBLK)
-#   define S_IFBLK 0
-#endif
-
-/* Link */
-#if !defined(S_IFLNK)
-#   define S_IFLNK 0
-#endif
-
-/* Socket */
-#if !defined(S_IFSOCK)
-#   define S_IFSOCK 0
-#endif
-
-/* Read user permission */
-#if !defined(S_IRUSR)
-#   define S_IRUSR S_IREAD
-#endif
-
-/* Write user permission */
-#if !defined(S_IWUSR)
-#   define S_IWUSR S_IWRITE
-#endif
-
-/* Execute user permission */
-#if !defined(S_IXUSR)
-#   define S_IXUSR 0
-#endif
-
-/* Read group permission */
-#if !defined(S_IRGRP)
-#   define S_IRGRP 0
-#endif
-
-/* Write group permission */
-#if !defined(S_IWGRP)
-#   define S_IWGRP 0
-#endif
-
-/* Execute group permission */
-#if !defined(S_IXGRP)
-#   define S_IXGRP 0
-#endif
-
-/* Read others permission */
-#if !defined(S_IROTH)
-#   define S_IROTH 0
-#endif
-
-/* Write others permission */
-#if !defined(S_IWOTH)
-#   define S_IWOTH 0
-#endif
-
-/* Execute others permission */
-#if !defined(S_IXOTH)
-#   define S_IXOTH 0
-#endif
-
-/* Maximum length of file name */
-#if !defined(PATH_MAX)
-#   define PATH_MAX MAX_PATH
-#endif
-#if !defined(FILENAME_MAX)
-#   define FILENAME_MAX MAX_PATH
-#endif
-#if !defined(NAME_MAX)
-#   define NAME_MAX FILENAME_MAX
-#endif
-
-/* File type flags for d_type */
-#define DT_UNKNOWN 0
-#define DT_REG S_IFREG
-#define DT_DIR S_IFDIR
-#define DT_FIFO S_IFIFO
-#define DT_SOCK S_IFSOCK
-#define DT_CHR S_IFCHR
-#define DT_BLK S_IFBLK
-#define DT_LNK S_IFLNK
-
-/* Macros for converting between st_mode and d_type */
-#define IFTODT(mode) ((mode) & S_IFMT)
-#define DTTOIF(type) (type)
-
-/*
- * File type macros.  Note that block devices, sockets and links cannot be
- * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
- * only defined for compatibility.  These macros should always return false
- * on Windows.
- */
-#if !defined(S_ISFIFO)
-#   define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
-#endif
-#if !defined(S_ISDIR)
-#   define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
-#endif
-#if !defined(S_ISREG)
-#   define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
-#endif
-#if !defined(S_ISLNK)
-#   define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
-#endif
-#if !defined(S_ISSOCK)
-#   define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
-#endif
-#if !defined(S_ISCHR)
-#   define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
-#endif
-#if !defined(S_ISBLK)
-#   define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
-#endif
-
-/* Return the exact length of the file name without zero terminator */
-#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
-
-/* Return the maximum size of a file name */
-#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1)
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/* Wide-character version */
-struct _wdirent {
-    /* Always zero */
-    long d_ino;
-
-    /* File position within stream */
-    long d_off;
-
-    /* Structure size */
-    unsigned short d_reclen;
-
-    /* Length of name without \0 */
-    size_t d_namlen;
-
-    /* File type */
-    int d_type;
-
-    /* File name */
-    wchar_t d_name[PATH_MAX+1];
-};
-typedef struct _wdirent _wdirent;
-
-struct _WDIR {
-    /* Current directory entry */
-    struct _wdirent ent;
-
-    /* Private file data */
-    WIN32_FIND_DATAW data;
-
-    /* True if data is valid */
-    int cached;
-
-    /* Win32 search handle */
-    HANDLE handle;
-
-    /* Initial directory name */
-    wchar_t *patt;
-};
-typedef struct _WDIR _WDIR;
-
-/* Multi-byte character version */
-struct dirent {
-    /* Always zero */
-    long d_ino;
-
-    /* File position within stream */
-    long d_off;
-
-    /* Structure size */
-    unsigned short d_reclen;
-
-    /* Length of name without \0 */
-    size_t d_namlen;
-
-    /* File type */
-    int d_type;
-
-    /* File name */
-    char d_name[PATH_MAX+1];
-};
-typedef struct dirent dirent;
-
-struct DIR {
-    struct dirent ent;
-    struct _WDIR *wdirp;
-};
-typedef struct DIR DIR;
-
-
-/* Dirent functions */
-static DIR *opendir (const char *dirname);
-static _WDIR *_wopendir (const wchar_t *dirname);
-
-static struct dirent *readdir (DIR *dirp);
-static struct _wdirent *_wreaddir (_WDIR *dirp);
-
-static int readdir_r(
-    DIR *dirp, struct dirent *entry, struct dirent **result);
-static int _wreaddir_r(
-    _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result);
-
-static int closedir (DIR *dirp);
-static int _wclosedir (_WDIR *dirp);
-
-static void rewinddir (DIR* dirp);
-static void _wrewinddir (_WDIR* dirp);
-
-static int scandir (const char *dirname, struct dirent ***namelist,
-    int (*filter)(const struct dirent*),
-    int (*compare)(const struct dirent**, const struct dirent**));
-
-static int alphasort (const struct dirent **a, const struct dirent **b);
-
-static int versionsort (const struct dirent **a, const struct dirent **b);
-
-
-/* For compatibility with Symbian */
-#define wdirent _wdirent
-#define WDIR _WDIR
-#define wopendir _wopendir
-#define wreaddir _wreaddir
-#define wclosedir _wclosedir
-#define wrewinddir _wrewinddir
-
-
-/* Internal utility functions */
-static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
-static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
-
-static int dirent_mbstowcs_s(
-    size_t *pReturnValue,
-    wchar_t *wcstr,
-    size_t sizeInWords,
-    const char *mbstr,
-    size_t count);
-
-static int dirent_wcstombs_s(
-    size_t *pReturnValue,
-    char *mbstr,
-    size_t sizeInBytes,
-    const wchar_t *wcstr,
-    size_t count);
-
-static void dirent_set_errno (int error);
-
-
-/*
- * Open directory stream DIRNAME for read and return a pointer to the
- * internal working area that is used to retrieve individual directory
- * entries.
- */
-static _WDIR*
-_wopendir(
-    const wchar_t *dirname)
-{
-    _WDIR *dirp;
-    DWORD n;
-    wchar_t *p;
-
-    /* Must have directory name */
-    if (dirname == NULL  ||  dirname[0] == '\0') {
-        dirent_set_errno (ENOENT);
-        return NULL;
-    }
-
-    /* Allocate new _WDIR structure */
-    dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
-    if (!dirp) {
-        return NULL;
-    }
-
-    /* Reset _WDIR structure */
-    dirp->handle = INVALID_HANDLE_VALUE;
-    dirp->patt = NULL;
-    dirp->cached = 0;
-
-    /*
-     * Compute the length of full path plus zero terminator
-     *
-     * Note that on WinRT there's no way to convert relative paths
-     * into absolute paths, so just assume it is an absolute path.
-     */
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
-    /* Desktop */
-    n = GetFullPathNameW (dirname, 0, NULL, NULL);
-#else
-    /* WinRT */
-    n = wcslen (dirname);
-#endif
-
-    /* Allocate room for absolute directory name and search pattern */
-    dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
-    if (dirp->patt == NULL) {
-        goto exit_closedir;
-    }
-
-    /*
-     * Convert relative directory name to an absolute one.  This
-     * allows rewinddir() to function correctly even when current
-     * working directory is changed between opendir() and rewinddir().
-     *
-     * Note that on WinRT there's no way to convert relative paths
-     * into absolute paths, so just assume it is an absolute path.
-     */
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
-    /* Desktop */
-    n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
-    if (n <= 0) {
-        goto exit_closedir;
-    }
-#else
-    /* WinRT */
-    wcsncpy_s (dirp->patt, n+1, dirname, n);
-#endif
-
-    /* Append search pattern \* to the directory name */
-    p = dirp->patt + n;
-    switch (p[-1]) {
-    case '\\':
-    case '/':
-    case ':':
-        /* Directory ends in path separator, e.g. c:\temp\ */
-        /*NOP*/;
-        break;
-
-    default:
-        /* Directory name doesn't end in path separator */
-        *p++ = '\\';
-    }
-    *p++ = '*';
-    *p = '\0';
-
-    /* Open directory stream and retrieve the first entry */
-    if (!dirent_first (dirp)) {
-        goto exit_closedir;
-    }
-
-    /* Success */
-    return dirp;
-
-    /* Failure */
-exit_closedir:
-    _wclosedir (dirp);
-    return NULL;
-}
-
-/*
- * Read next directory entry.
- *
- * Returns pointer to static directory entry which may be overwritten by
- * subsequent calls to _wreaddir().
- */
-static struct _wdirent*
-_wreaddir(
-    _WDIR *dirp)
-{
-    struct _wdirent *entry;
-
-    /*
-     * Read directory entry to buffer.  We can safely ignore the return value
-     * as entry will be set to NULL in case of error.
-     */
-    (void) _wreaddir_r (dirp, &dirp->ent, &entry);
-
-    /* Return pointer to statically allocated directory entry */
-    return entry;
-}
-
-/*
- * Read next directory entry.
- *
- * Returns zero on success.  If end of directory stream is reached, then sets
- * result to NULL and returns zero.
- */
-static int
-_wreaddir_r(
-    _WDIR *dirp,
-    struct _wdirent *entry,
-    struct _wdirent **result)
-{
-    WIN32_FIND_DATAW *datap;
-
-    /* Read next directory entry */
-    datap = dirent_next (dirp);
-    if (datap) {
-        size_t n;
-        DWORD attr;
-
-        /*
-         * Copy file name as wide-character string.  If the file name is too
-         * long to fit in to the destination buffer, then truncate file name
-         * to PATH_MAX characters and zero-terminate the buffer.
-         */
-        n = 0;
-        while (n < PATH_MAX  &&  datap->cFileName[n] != 0) {
-            entry->d_name[n] = datap->cFileName[n];
-            n++;
-        }
-        entry->d_name[n] = 0;
-
-        /* Length of file name excluding zero terminator */
-        entry->d_namlen = n;
-
-        /* File type */
-        attr = datap->dwFileAttributes;
-        if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
-            entry->d_type = DT_CHR;
-        } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
-            entry->d_type = DT_DIR;
-        } else {
-            entry->d_type = DT_REG;
-        }
-
-        /* Reset dummy fields */
-        entry->d_ino = 0;
-        entry->d_off = 0;
-        entry->d_reclen = sizeof (struct _wdirent);
-
-        /* Set result address */
-        *result = entry;
-
-    } else {
-
-        /* Return NULL to indicate end of directory */
-        *result = NULL;
-
-    }
-
-    return /*OK*/0;
-}
-
-/*
- * Close directory stream opened by opendir() function.  This invalidates the
- * DIR structure as well as any directory entry read previously by
- * _wreaddir().
- */
-static int
-_wclosedir(
-    _WDIR *dirp)
-{
-    int ok;
-    if (dirp) {
-
-        /* Release search handle */
-        if (dirp->handle != INVALID_HANDLE_VALUE) {
-            FindClose (dirp->handle);
-        }
-
-        /* Release search pattern */
-        free (dirp->patt);
-
-        /* Release directory structure */
-        free (dirp);
-        ok = /*success*/0;
-
-    } else {
-
-        /* Invalid directory stream */
-        dirent_set_errno (EBADF);
-        ok = /*failure*/-1;
-
-    }
-    return ok;
-}
-
-/*
- * Rewind directory stream such that _wreaddir() returns the very first
- * file name again.
- */
-static void
-_wrewinddir(
-    _WDIR* dirp)
-{
-    if (dirp) {
-        /* Release existing search handle */
-        if (dirp->handle != INVALID_HANDLE_VALUE) {
-            FindClose (dirp->handle);
-        }
-
-        /* Open new search handle */
-        dirent_first (dirp);
-    }
-}
-
-/* Get first directory entry (internal) */
-static WIN32_FIND_DATAW*
-dirent_first(
-    _WDIR *dirp)
-{
-    WIN32_FIND_DATAW *datap;
-    DWORD error;
-
-    /* Open directory and retrieve the first entry */
-    dirp->handle = FindFirstFileExW(
-        dirp->patt, FindExInfoStandard, &dirp->data,
-        FindExSearchNameMatch, NULL, 0);
-    if (dirp->handle != INVALID_HANDLE_VALUE) {
-
-        /* a directory entry is now waiting in memory */
-        datap = &dirp->data;
-        dirp->cached = 1;
-
-    } else {
-
-        /* Failed to open directory: no directory entry in memory */
-        dirp->cached = 0;
-        datap = NULL;
-
-        /* Set error code */
-        error = GetLastError ();
-        switch (error) {
-        case ERROR_ACCESS_DENIED:
-            /* No read access to directory */
-            dirent_set_errno (EACCES);
-            break;
-
-        case ERROR_DIRECTORY:
-            /* Directory name is invalid */
-            dirent_set_errno (ENOTDIR);
-            break;
-
-        case ERROR_PATH_NOT_FOUND:
-        default:
-            /* Cannot find the file */
-            dirent_set_errno (ENOENT);
-        }
-
-    }
-    return datap;
-}
-
-/*
- * Get next directory entry (internal).
- *
- * Returns
- */
-static WIN32_FIND_DATAW*
-dirent_next(
-    _WDIR *dirp)
-{
-    WIN32_FIND_DATAW *p;
-
-    /* Get next directory entry */
-    if (dirp->cached != 0) {
-
-        /* A valid directory entry already in memory */
-        p = &dirp->data;
-        dirp->cached = 0;
-
-    } else if (dirp->handle != INVALID_HANDLE_VALUE) {
-
-        /* Get the next directory entry from stream */
-        if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
-            /* Got a file */
-            p = &dirp->data;
-        } else {
-            /* The very last entry has been processed or an error occurred */
-            FindClose (dirp->handle);
-            dirp->handle = INVALID_HANDLE_VALUE;
-            p = NULL;
-        }
-
-    } else {
-
-        /* End of directory stream reached */
-        p = NULL;
-
-    }
-
-    return p;
-}
-
-/*
- * Open directory stream using plain old C-string.
- */
-static DIR*
-opendir(
-    const char *dirname)
-{
-    struct DIR *dirp;
-
-    /* Must have directory name */
-    if (dirname == NULL  ||  dirname[0] == '\0') {
-        dirent_set_errno (ENOENT);
-        return NULL;
-    }
-
-    /* Allocate memory for DIR structure */
-    dirp = (DIR*) malloc (sizeof (struct DIR));
-    if (!dirp) {
-        return NULL;
-    }
-    {
-        int error;
-        wchar_t wname[PATH_MAX + 1];
-        size_t n;
-
-        /* Convert directory name to wide-character string */
-        error = dirent_mbstowcs_s(
-            &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1);
-        if (error) {
-            /*
-             * Cannot convert file name to wide-character string.  This
-             * occurs if the string contains invalid multi-byte sequences or
-             * the output buffer is too small to contain the resulting
-             * string.
-             */
-            goto exit_free;
-        }
-
-
-        /* Open directory stream using wide-character name */
-        dirp->wdirp = _wopendir (wname);
-        if (!dirp->wdirp) {
-            goto exit_free;
-        }
-
-    }
-
-    /* Success */
-    return dirp;
-
-    /* Failure */
-exit_free:
-    free (dirp);
-    return NULL;
-}
-
-/*
- * Read next directory entry.
- */
-static struct dirent*
-readdir(
-    DIR *dirp)
-{
-    struct dirent *entry;
-
-    /*
-     * Read directory entry to buffer.  We can safely ignore the return value
-     * as entry will be set to NULL in case of error.
-     */
-    (void) readdir_r (dirp, &dirp->ent, &entry);
-
-    /* Return pointer to statically allocated directory entry */
-    return entry;
-}
-
-/*
- * Read next directory entry into called-allocated buffer.
- *
- * Returns zero on success.  If the end of directory stream is reached, then
- * sets result to NULL and returns zero.
- */
-static int
-readdir_r(
-    DIR *dirp,
-    struct dirent *entry,
-    struct dirent **result)
-{
-    WIN32_FIND_DATAW *datap;
-
-    /* Read next directory entry */
-    datap = dirent_next (dirp->wdirp);
-    if (datap) {
-        size_t n;
-        int error;
-
-        /* Attempt to convert file name to multi-byte string */
-        error = dirent_wcstombs_s(
-            &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1);
-
-        /*
-         * If the file name cannot be represented by a multi-byte string,
-         * then attempt to use old 8+3 file name.  This allows traditional
-         * Unix-code to access some file names despite of unicode
-         * characters, although file names may seem unfamiliar to the user.
-         *
-         * Be ware that the code below cannot come up with a short file
-         * name unless the file system provides one.  At least
-         * VirtualBox shared folders fail to do this.
-         */
-        if (error  &&  datap->cAlternateFileName[0] != '\0') {
-            error = dirent_wcstombs_s(
-                &n, entry->d_name, PATH_MAX + 1,
-                datap->cAlternateFileName, PATH_MAX + 1);
-        }
-
-        if (!error) {
-            DWORD attr;
-
-            /* Length of file name excluding zero terminator */
-            entry->d_namlen = n - 1;
-
-            /* File attributes */
-            attr = datap->dwFileAttributes;
-            if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
-                entry->d_type = DT_CHR;
-            } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
-                entry->d_type = DT_DIR;
-            } else {
-                entry->d_type = DT_REG;
-            }
-
-            /* Reset dummy fields */
-            entry->d_ino = 0;
-            entry->d_off = 0;
-            entry->d_reclen = sizeof (struct dirent);
-
-        } else {
-
-            /*
-             * Cannot convert file name to multi-byte string so construct
-             * an erroneous directory entry and return that.  Note that
-             * we cannot return NULL as that would stop the processing
-             * of directory entries completely.
-             */
-            entry->d_name[0] = '?';
-            entry->d_name[1] = '\0';
-            entry->d_namlen = 1;
-            entry->d_type = DT_UNKNOWN;
-            entry->d_ino = 0;
-            entry->d_off = -1;
-            entry->d_reclen = 0;
-
-        }
-
-        /* Return pointer to directory entry */
-        *result = entry;
-
-    } else {
-
-        /* No more directory entries */
-        *result = NULL;
-
-    }
-
-    return /*OK*/0;
-}
-
-/*
- * Close directory stream.
- */
-static int
-closedir(
-    DIR *dirp)
-{
-    int ok;
-    if (dirp) {
-
-        /* Close wide-character directory stream */
-        ok = _wclosedir (dirp->wdirp);
-        dirp->wdirp = NULL;
-
-        /* Release multi-byte character version */
-        free (dirp);
-
-    } else {
-
-        /* Invalid directory stream */
-        dirent_set_errno (EBADF);
-        ok = /*failure*/-1;
-
-    }
-    return ok;
-}
-
-/*
- * Rewind directory stream to beginning.
- */
-static void
-rewinddir(
-    DIR* dirp)
-{
-    /* Rewind wide-character string directory stream */
-    _wrewinddir (dirp->wdirp);
-}
-
-/*
- * Scan directory for entries.
- */
-static int
-scandir(
-    const char *dirname,
-    struct dirent ***namelist,
-    int (*filter)(const struct dirent*),
-    int (*compare)(const struct dirent**, const struct dirent**))
-{
-    struct dirent **files = NULL;
-    size_t size = 0;
-    size_t allocated = 0;
-    const size_t init_size = 1;
-    DIR *dir = NULL;
-    struct dirent *entry;
-    struct dirent *tmp = NULL;
-    size_t i;
-    int result = 0;
-
-    /* Open directory stream */
-    dir = opendir (dirname);
-    if (dir) {
-
-        /* Read directory entries to memory */
-        while (1) {
-
-            /* Enlarge pointer table to make room for another pointer */
-            if (size >= allocated) {
-                void *p;
-                size_t num_entries;
-
-                /* Compute number of entries in the enlarged pointer table */
-                if (size < init_size) {
-                    /* Allocate initial pointer table */
-                    num_entries = init_size;
-                } else {
-                    /* Double the size */
-                    num_entries = size * 2;
-                }
-
-                /* Allocate first pointer table or enlarge existing table */
-                p = realloc (files, sizeof (void*) * num_entries);
-                if (p != NULL) {
-                    /* Got the memory */
-                    files = (dirent**) p;
-                    allocated = num_entries;
-                } else {
-                    /* Out of memory */
-                    result = -1;
-                    break;
-                }
-
-            }
-
-            /* Allocate room for temporary directory entry */
-            if (tmp == NULL) {
-                tmp = (struct dirent*) malloc (sizeof (struct dirent));
-                if (tmp == NULL) {
-                    /* Cannot allocate temporary directory entry */
-                    result = -1;
-                    break;
-                }
-            }
-
-            /* Read directory entry to temporary area */
-            if (readdir_r (dir, tmp, &entry) == /*OK*/0) {
-
-                /* Did we get an entry? */
-                if (entry != NULL) {
-                    int pass;
-
-                    /* Determine whether to include the entry in result */
-                    if (filter) {
-                        /* Let the filter function decide */
-                        pass = filter (tmp);
-                    } else {
-                        /* No filter function, include everything */
-                        pass = 1;
-                    }
-
-                    if (pass) {
-                        /* Store the temporary entry to pointer table */
-                        files[size++] = tmp;
-                        tmp = NULL;
-
-                        /* Keep up with the number of files */
-                        result++;
-                    }
-
-                } else {
-
-                    /*
-                     * End of directory stream reached => sort entries and
-                     * exit.
-                     */
-                    qsort (files, size, sizeof (void*),
-                        (int (*) (const void*, const void*)) compare);
-                    break;
-
-                }
-
-            } else {
-                /* Error reading directory entry */
-                result = /*Error*/ -1;
-                break;
-            }
-
-        }
-
-    } else {
-        /* Cannot open directory */
-        result = /*Error*/ -1;
-    }
-
-    /* Release temporary directory entry */
-    free (tmp);
-
-    /* Release allocated memory on error */
-    if (result < 0) {
-        for (i = 0; i < size; i++) {
-            free (files[i]);
-        }
-        free (files);
-        files = NULL;
-    }
-
-    /* Close directory stream */
-    if (dir) {
-        closedir (dir);
-    }
-
-    /* Pass pointer table to caller */
-    if (namelist) {
-        *namelist = files;
-    }
-    return result;
-}
-
-/* Alphabetical sorting */
-static int
-alphasort(
-    const struct dirent **a, const struct dirent **b)
-{
-    return strcoll ((*a)->d_name, (*b)->d_name);
-}
-
-/* Sort versions */
-static int
-versionsort(
-    const struct dirent **a, const struct dirent **b)
-{
-    /* FIXME: implement strverscmp and use that */
-    return alphasort (a, b);
-}
-
-/* Convert multi-byte string to wide character string */
-static int
-dirent_mbstowcs_s(
-    size_t *pReturnValue,
-    wchar_t *wcstr,
-    size_t sizeInWords,
-    const char *mbstr,
-    size_t count)
-{
-    int error;
-
-#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
-
-    /* Microsoft Visual Studio 2005 or later */
-    error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
-
-#else
-
-    /* Older Visual Studio or non-Microsoft compiler */
-    size_t n;
-
-    /* Convert to wide-character string (or count characters) */
-    n = mbstowcs (wcstr, mbstr, sizeInWords);
-    if (!wcstr  ||  n < count) {
-
-        /* Zero-terminate output buffer */
-        if (wcstr  &&  sizeInWords) {
-            if (n >= sizeInWords) {
-                n = sizeInWords - 1;
-            }
-            wcstr[n] = 0;
-        }
-
-        /* Length of resulting multi-byte string WITH zero terminator */
-        if (pReturnValue) {
-            *pReturnValue = n + 1;
-        }
-
-        /* Success */
-        error = 0;
-
-    } else {
-
-        /* Could not convert string */
-        error = 1;
-
-    }
-
-#endif
-    return error;
-}
-
-/* Convert wide-character string to multi-byte string */
-static int
-dirent_wcstombs_s(
-    size_t *pReturnValue,
-    char *mbstr,
-    size_t sizeInBytes, /* max size of mbstr */
-    const wchar_t *wcstr,
-    size_t count)
-{
-    int error;
-
-#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
-
-    /* Microsoft Visual Studio 2005 or later */
-    error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
-
-#else
-
-    /* Older Visual Studio or non-Microsoft compiler */
-    size_t n;
-
-    /* Convert to multi-byte string (or count the number of bytes needed) */
-    n = wcstombs (mbstr, wcstr, sizeInBytes);
-    if (!mbstr  ||  n < count) {
-
-        /* Zero-terminate output buffer */
-        if (mbstr  &&  sizeInBytes) {
-            if (n >= sizeInBytes) {
-                n = sizeInBytes - 1;
-            }
-            mbstr[n] = '\0';
-        }
-
-        /* Length of resulting multi-bytes string WITH zero-terminator */
-        if (pReturnValue) {
-            *pReturnValue = n + 1;
-        }
-
-        /* Success */
-        error = 0;
-
-    } else {
-
-        /* Cannot convert string */
-        error = 1;
-
-    }
-
-#endif
-    return error;
-}
-
-/* Set errno variable */
-static void
-dirent_set_errno(
-    int error)
-{
-#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
-
-    /* Microsoft Visual Studio 2005 and later */
-    _set_errno (error);
-
-#else
-
-    /* Non-Microsoft compiler or older Microsoft compiler */
-    errno = error;
-
-#endif
-}
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif /*DIRENT_H*/
diff --git a/third_party/glog/src/windows/glog/log_severity.h b/third_party/glog/src/windows/glog/log_severity.h
deleted file mode 100644
index 22a4191ab8..0000000000
--- a/third_party/glog/src/windows/glog/log_severity.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// This file is automatically generated from src/glog/log_severity.h
-// using src/windows/preprocess.sh.
-// DO NOT EDIT!
-
-// Copyright (c) 2007, Google 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:
-//
-//     * 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.
-
-#ifndef BASE_LOG_SEVERITY_H__
-#define BASE_LOG_SEVERITY_H__
-
-// Annoying stuff for windows -- makes sure clients can import these functions
-#ifndef GOOGLE_GLOG_DLL_DECL
-# if defined(_WIN32) && !defined(__CYGWIN__)
-#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
-# else
-#   define GOOGLE_GLOG_DLL_DECL
-# endif
-#endif
-
-// Variables of type LogSeverity are widely taken to lie in the range
-// [0, NUM_SEVERITIES-1].  Be careful to preserve this assumption if
-// you ever need to change their values or add a new severity.
-typedef int LogSeverity;
-
-const int GLOG_INFO = 0, GLOG_WARNING = 1, GLOG_ERROR = 2, GLOG_FATAL = 3,
-  NUM_SEVERITIES = 4;
-#ifndef GLOG_NO_ABBREVIATED_SEVERITIES
-# ifdef ERROR
-#  error ERROR macro is defined. Define GLOG_NO_ABBREVIATED_SEVERITIES before including logging.h. See the document for detail.
-# endif
-const int INFO = GLOG_INFO, WARNING = GLOG_WARNING,
-  ERROR = GLOG_ERROR, FATAL = GLOG_FATAL;
-#endif
-
-// DFATAL is FATAL in debug mode, ERROR in normal mode
-#ifdef NDEBUG
-#define DFATAL_LEVEL ERROR
-#else
-#define DFATAL_LEVEL FATAL
-#endif
-
-extern GOOGLE_GLOG_DLL_DECL const char* const LogSeverityNames[NUM_SEVERITIES];
-
-// NDEBUG usage helpers related to (RAW_)DCHECK:
-//
-// DEBUG_MODE is for small !NDEBUG uses like
-//   if (DEBUG_MODE) foo.CheckThatFoo();
-// instead of substantially more verbose
-//   #ifndef NDEBUG
-//     foo.CheckThatFoo();
-//   #endif
-//
-// IF_DEBUG_MODE is for small !NDEBUG uses like
-//   IF_DEBUG_MODE( string error; )
-//   DCHECK(Foo(&error)) << error;
-// instead of substantially more verbose
-//   #ifndef NDEBUG
-//     string error;
-//     DCHECK(Foo(&error)) << error;
-//   #endif
-//
-#ifdef NDEBUG
-enum { DEBUG_MODE = 0 };
-#define IF_DEBUG_MODE(x)
-#else
-enum { DEBUG_MODE = 1 };
-#define IF_DEBUG_MODE(x) x
-#endif
-
-#endif  // BASE_LOG_SEVERITY_H__
diff --git a/third_party/glog/src/windows/glog/logging.h b/third_party/glog/src/windows/glog/logging.h
deleted file mode 100755
index e111bf07c4..0000000000
--- a/third_party/glog/src/windows/glog/logging.h
+++ /dev/null
@@ -1,1690 +0,0 @@
-// This file is automatically generated from src/glog/logging.h.in
-// using src/windows/preprocess.sh.
-// DO NOT EDIT!
-
-// Copyright (c) 1999, Google 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:
-//
-//     * 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.
-//
-// Author: Ray Sidney
-//
-// This file contains #include information about logging-related stuff.
-// Pretty much everybody needs to #include this file so that they can
-// log various happenings.
-//
-#ifndef _LOGGING_H_
-#define _LOGGING_H_
-
-#include <errno.h>
-#include <string.h>
-#include <time.h>
-#include <iosfwd>
-#include <ostream>
-#include <sstream>
-#include <string>
-#if 0
-# include <unistd.h>
-#endif
-#include <vector>
-
-#if defined(_MSC_VER)
-#define GLOG_MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
-                                     __pragma(warning(disable:n))
-#define GLOG_MSVC_POP_WARNING() __pragma(warning(pop))
-#else
-#define GLOG_MSVC_PUSH_DISABLE_WARNING(n)
-#define GLOG_MSVC_POP_WARNING()
-#endif
-
-// Annoying stuff for windows -- makes sure clients can import these functions
-#ifndef GOOGLE_GLOG_DLL_DECL
-# if defined(_WIN32) && !defined(__CYGWIN__)
-#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
-# else
-#   define GOOGLE_GLOG_DLL_DECL
-# endif
-#endif
-
-// We care a lot about number of bits things take up.  Unfortunately,
-// systems define their bit-specific ints in a lot of different ways.
-// We use our own way, and have a typedef to get there.
-// Note: these commands below may look like "#if 1" or "#if 0", but
-// that's because they were constructed that way at ./configure time.
-// Look at logging.h.in to see how they're calculated (based on your config).
-#if 0
-#include <stdint.h>             // the normal place uint16_t is defined
-#endif
-#if 0
-#include <sys/types.h>          // the normal place u_int16_t is defined
-#endif
-#if 0
-#include <inttypes.h>           // a third place for uint16_t or u_int16_t
-#endif
-
-#if 0
-#include <gflags/gflags.h>
-#endif
-
-namespace google {
-
-#if 0      // the C99 format
-typedef int32_t int32;
-typedef uint32_t uint32;
-typedef int64_t int64;
-typedef uint64_t uint64;
-#elif 0   // the BSD format
-typedef int32_t int32;
-typedef u_int32_t uint32;
-typedef int64_t int64;
-typedef u_int64_t uint64;
-#elif 1    // the windows (vc7) format
-typedef __int32 int32;
-typedef unsigned __int32 uint32;
-typedef __int64 int64;
-typedef unsigned __int64 uint64;
-#else
-#error Do not know how to define a 32-bit integer quantity on your system
-#endif
-
-}
-
-// The global value of GOOGLE_STRIP_LOG. All the messages logged to
-// LOG(XXX) with severity less than GOOGLE_STRIP_LOG will not be displayed.
-// If it can be determined at compile time that the message will not be
-// printed, the statement will be compiled out.
-//
-// Example: to strip out all INFO and WARNING messages, use the value
-// of 2 below. To make an exception for WARNING messages from a single
-// file, add "#define GOOGLE_STRIP_LOG 1" to that file _before_ including
-// base/logging.h
-#ifndef GOOGLE_STRIP_LOG
-#define GOOGLE_STRIP_LOG 0
-#endif
-
-// GCC can be told that a certain branch is not likely to be taken (for
-// instance, a CHECK failure), and use that information in static analysis.
-// Giving it this information can help it optimize for the common case in
-// the absence of better information (ie. -fprofile-arcs).
-//
-#ifndef GOOGLE_PREDICT_BRANCH_NOT_TAKEN
-#if 0
-#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0))
-#else
-#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) x
-#endif
-#endif
-
-#ifndef GOOGLE_PREDICT_FALSE
-#if 0
-#define GOOGLE_PREDICT_FALSE(x) (__builtin_expect(x, 0))
-#else
-#define GOOGLE_PREDICT_FALSE(x) x
-#endif
-#endif
-
-#ifndef GOOGLE_PREDICT_TRUE
-#if 0
-#define GOOGLE_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
-#else
-#define GOOGLE_PREDICT_TRUE(x) x
-#endif
-#endif
-
-
-// Make a bunch of macros for logging.  The way to log things is to stream
-// things to LOG(<a particular severity level>).  E.g.,
-//
-//   LOG(INFO) << "Found " << num_cookies << " cookies";
-//
-// You can capture log messages in a string, rather than reporting them
-// immediately:
-//
-//   vector<string> errors;
-//   LOG_STRING(ERROR, &errors) << "Couldn't parse cookie #" << cookie_num;
-//
-// This pushes back the new error onto 'errors'; if given a NULL pointer,
-// it reports the error via LOG(ERROR).
-//
-// You can also do conditional logging:
-//
-//   LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
-//
-// You can also do occasional logging (log every n'th occurrence of an
-// event):
-//
-//   LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
-//
-// The above will cause log messages to be output on the 1st, 11th, 21st, ...
-// times it is executed.  Note that the special google::COUNTER value is used
-// to identify which repetition is happening.
-//
-// You can also do occasional conditional logging (log every n'th
-// occurrence of an event, when condition is satisfied):
-//
-//   LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER
-//                                           << "th big cookie";
-//
-// You can log messages the first N times your code executes a line. E.g.
-//
-//   LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";
-//
-// Outputs log messages for the first 20 times it is executed.
-//
-// Analogous SYSLOG, SYSLOG_IF, and SYSLOG_EVERY_N macros are available.
-// These log to syslog as well as to the normal logs.  If you use these at
-// all, you need to be aware that syslog can drastically reduce performance,
-// especially if it is configured for remote logging!  Don't use these
-// unless you fully understand this and have a concrete need to use them.
-// Even then, try to minimize your use of them.
-//
-// There are also "debug mode" logging macros like the ones above:
-//
-//   DLOG(INFO) << "Found cookies";
-//
-//   DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
-//
-//   DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
-//
-// All "debug mode" logging is compiled away to nothing for non-debug mode
-// compiles.
-//
-// We also have
-//
-//   LOG_ASSERT(assertion);
-//   DLOG_ASSERT(assertion);
-//
-// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
-//
-// There are "verbose level" logging macros.  They look like
-//
-//   VLOG(1) << "I'm printed when you run the program with --v=1 or more";
-//   VLOG(2) << "I'm printed when you run the program with --v=2 or more";
-//
-// These always log at the INFO log level (when they log at all).
-// The verbose logging can also be turned on module-by-module.  For instance,
-//    --vmodule=mapreduce=2,file=1,gfs*=3 --v=0
-// will cause:
-//   a. VLOG(2) and lower messages to be printed from mapreduce.{h,cc}
-//   b. VLOG(1) and lower messages to be printed from file.{h,cc}
-//   c. VLOG(3) and lower messages to be printed from files prefixed with "gfs"
-//   d. VLOG(0) and lower messages to be printed from elsewhere
-//
-// The wildcarding functionality shown by (c) supports both '*' (match
-// 0 or more characters) and '?' (match any single character) wildcards.
-//
-// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as
-//
-//   if (VLOG_IS_ON(2)) {
-//     // do some logging preparation and logging
-//     // that can't be accomplished with just VLOG(2) << ...;
-//   }
-//
-// There are also VLOG_IF, VLOG_EVERY_N and VLOG_IF_EVERY_N "verbose level"
-// condition macros for sample cases, when some extra computation and
-// preparation for logs is not needed.
-//   VLOG_IF(1, (size > 1024))
-//      << "I'm printed when size is more than 1024 and when you run the "
-//         "program with --v=1 or more";
-//   VLOG_EVERY_N(1, 10)
-//      << "I'm printed every 10th occurrence, and when you run the program "
-//         "with --v=1 or more. Present occurence is " << google::COUNTER;
-//   VLOG_IF_EVERY_N(1, (size > 1024), 10)
-//      << "I'm printed on every 10th occurence of case when size is more "
-//         " than 1024, when you run the program with --v=1 or more. ";
-//         "Present occurence is " << google::COUNTER;
-//
-// The supported severity levels for macros that allow you to specify one
-// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
-// Note that messages of a given severity are logged not only in the
-// logfile for that severity, but also in all logfiles of lower severity.
-// E.g., a message of severity FATAL will be logged to the logfiles of
-// severity FATAL, ERROR, WARNING, and INFO.
-//
-// There is also the special severity of DFATAL, which logs FATAL in
-// debug mode, ERROR in normal mode.
-//
-// Very important: logging a message at the FATAL severity level causes
-// the program to terminate (after the message is logged).
-//
-// Unless otherwise specified, logs will be written to the filename
-// "<program name>.<hostname>.<user name>.log.<severity level>.", followed
-// by the date, time, and pid (you can't prevent the date, time, and pid
-// from being in the filename).
-//
-// The logging code takes two flags:
-//     --v=#           set the verbose level
-//     --logtostderr   log all the messages to stderr instead of to logfiles
-
-// LOG LINE PREFIX FORMAT
-//
-// Log lines have this form:
-//
-//     Lyyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg...
-//
-// where the fields are defined as follows:
-//
-//   L                A single character, representing the log level
-//                    (eg 'I' for INFO)
-//   yyyy             The year
-//   mm               The month (zero padded; ie May is '05')
-//   dd               The day (zero padded)
-//   hh:mm:ss.uuuuuu  Time in hours, minutes and fractional seconds
-//   threadid         The space-padded thread ID as returned by GetTID()
-//                    (this matches the PID on Linux)
-//   file             The file name
-//   line             The line number
-//   msg              The user-supplied message
-//
-// Example:
-//
-//   I1103 11:57:31.739339 24395 google.cc:2341] Command line: ./some_prog
-//   I1103 11:57:31.739403 24395 google.cc:2342] Process id 24395
-//
-// NOTE: although the microseconds are useful for comparing events on
-// a single machine, clocks on different machines may not be well
-// synchronized.  Hence, use caution when comparing the low bits of
-// timestamps from different machines.
-
-#ifndef DECLARE_VARIABLE
-#define MUST_UNDEF_GFLAGS_DECLARE_MACROS
-#define DECLARE_VARIABLE(type, shorttype, name, tn)                     \
-  namespace fL##shorttype {                                             \
-    extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name;                      \
-  }                                                                     \
-  using fL##shorttype::FLAGS_##name
-
-// bool specialization
-#define DECLARE_bool(name) \
-  DECLARE_VARIABLE(bool, B, name, bool)
-
-// int32 specialization
-#define DECLARE_int32(name) \
-  DECLARE_VARIABLE(google::int32, I, name, int32)
-
-// Special case for string, because we have to specify the namespace
-// std::string, which doesn't play nicely with our FLAG__namespace hackery.
-#define DECLARE_string(name)                                            \
-  namespace fLS {                                                       \
-    extern GOOGLE_GLOG_DLL_DECL std::string& FLAGS_##name;              \
-  }                                                                     \
-  using fLS::FLAGS_##name
-#endif
-
-// Set whether log messages go to stderr instead of logfiles
-DECLARE_bool(logtostderr);
-
-// Set whether log messages go to stderr in addition to logfiles.
-DECLARE_bool(alsologtostderr);
-
-// Set color messages logged to stderr (if supported by terminal).
-DECLARE_bool(colorlogtostderr);
-
-// Log messages at a level >= this flag are automatically sent to
-// stderr in addition to log files.
-DECLARE_int32(stderrthreshold);
-
-// Set whether the log prefix should be prepended to each line of output.
-DECLARE_bool(log_prefix);
-
-// Log messages at a level <= this flag are buffered.
-// Log messages at a higher level are flushed immediately.
-DECLARE_int32(logbuflevel);
-
-// Sets the maximum number of seconds which logs may be buffered for.
-DECLARE_int32(logbufsecs);
-
-// Log suppression level: messages logged at a lower level than this
-// are suppressed.
-DECLARE_int32(minloglevel);
-
-// If specified, logfiles are written into this directory instead of the
-// default logging directory.
-DECLARE_string(log_dir);
-
-// Set the log file mode.
-DECLARE_int32(logfile_mode);
-
-// Sets the path of the directory into which to put additional links
-// to the log files.
-DECLARE_string(log_link);
-
-DECLARE_int32(v);  // in vlog_is_on.cc
-
-// Sets the maximum log file size (in MB).
-DECLARE_int32(max_log_size);
-
-// Sets whether to avoid logging to the disk if the disk is full.
-DECLARE_bool(stop_logging_if_full_disk);
-
-#ifdef MUST_UNDEF_GFLAGS_DECLARE_MACROS
-#undef MUST_UNDEF_GFLAGS_DECLARE_MACROS
-#undef DECLARE_VARIABLE
-#undef DECLARE_bool
-#undef DECLARE_int32
-#undef DECLARE_string
-#endif
-
-// Log messages below the GOOGLE_STRIP_LOG level will be compiled away for
-// security reasons. See LOG(severtiy) below.
-
-// A few definitions of macros that don't generate much code.  Since
-// LOG(INFO) and its ilk are used all over our code, it's
-// better to have compact code for these operations.
-
-#if GOOGLE_STRIP_LOG == 0
-#define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \
-      __FILE__, __LINE__)
-#define LOG_TO_STRING_INFO(message) google::LogMessage( \
-      __FILE__, __LINE__, google::GLOG_INFO, message)
-#else
-#define COMPACT_GOOGLE_LOG_INFO google::NullStream()
-#define LOG_TO_STRING_INFO(message) google::NullStream()
-#endif
-
-#if GOOGLE_STRIP_LOG <= 1
-#define COMPACT_GOOGLE_LOG_WARNING google::LogMessage( \
-      __FILE__, __LINE__, google::GLOG_WARNING)
-#define LOG_TO_STRING_WARNING(message) google::LogMessage( \
-      __FILE__, __LINE__, google::GLOG_WARNING, message)
-#else
-#define COMPACT_GOOGLE_LOG_WARNING google::NullStream()
-#define LOG_TO_STRING_WARNING(message) google::NullStream()
-#endif
-
-#if GOOGLE_STRIP_LOG <= 2
-#define COMPACT_GOOGLE_LOG_ERROR google::LogMessage( \
-      __FILE__, __LINE__, google::GLOG_ERROR)
-#define LOG_TO_STRING_ERROR(message) google::LogMessage( \
-      __FILE__, __LINE__, google::GLOG_ERROR, message)
-#else
-#define COMPACT_GOOGLE_LOG_ERROR google::NullStream()
-#define LOG_TO_STRING_ERROR(message) google::NullStream()
-#endif
-
-#if GOOGLE_STRIP_LOG <= 3
-#define COMPACT_GOOGLE_LOG_FATAL google::LogMessageFatal( \
-      __FILE__, __LINE__)
-#define LOG_TO_STRING_FATAL(message) google::LogMessage( \
-      __FILE__, __LINE__, google::GLOG_FATAL, message)
-#else
-#define COMPACT_GOOGLE_LOG_FATAL google::NullStreamFatal()
-#define LOG_TO_STRING_FATAL(message) google::NullStreamFatal()
-#endif
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-#define DCHECK_IS_ON() 0
-#else
-#define DCHECK_IS_ON() 1
-#endif
-
-// For DFATAL, we want to use LogMessage (as opposed to
-// LogMessageFatal), to be consistent with the original behavior.
-#if !DCHECK_IS_ON()
-#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR
-#elif GOOGLE_STRIP_LOG <= 3
-#define COMPACT_GOOGLE_LOG_DFATAL google::LogMessage( \
-      __FILE__, __LINE__, google::GLOG_FATAL)
-#else
-#define COMPACT_GOOGLE_LOG_DFATAL google::NullStreamFatal()
-#endif
-
-#define GOOGLE_LOG_INFO(counter) google::LogMessage(__FILE__, __LINE__, google::GLOG_INFO, counter, &google::LogMessage::SendToLog)
-#define SYSLOG_INFO(counter) \
-  google::LogMessage(__FILE__, __LINE__, google::GLOG_INFO, counter, \
-  &google::LogMessage::SendToSyslogAndLog)
-#define GOOGLE_LOG_WARNING(counter)  \
-  google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING, counter, \
-  &google::LogMessage::SendToLog)
-#define SYSLOG_WARNING(counter)  \
-  google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING, counter, \
-  &google::LogMessage::SendToSyslogAndLog)
-#define GOOGLE_LOG_ERROR(counter)  \
-  google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, counter, \
-  &google::LogMessage::SendToLog)
-#define SYSLOG_ERROR(counter)  \
-  google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, counter, \
-  &google::LogMessage::SendToSyslogAndLog)
-#define GOOGLE_LOG_FATAL(counter) \
-  google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL, counter, \
-  &google::LogMessage::SendToLog)
-#define SYSLOG_FATAL(counter) \
-  google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL, counter, \
-  &google::LogMessage::SendToSyslogAndLog)
-#define GOOGLE_LOG_DFATAL(counter) \
-  google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \
-  &google::LogMessage::SendToLog)
-#define SYSLOG_DFATAL(counter) \
-  google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \
-  &google::LogMessage::SendToSyslogAndLog)
-
-#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
-// A very useful logging macro to log windows errors:
-#define LOG_SYSRESULT(result) \
-  if (FAILED(HRESULT_FROM_WIN32(result))) { \
-    LPSTR message = NULL; \
-    LPSTR msg = reinterpret_cast<LPSTR>(&message); \
-    DWORD message_length = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | \
-                         FORMAT_MESSAGE_FROM_SYSTEM, \
-                         0, result, 0, msg, 100, NULL); \
-    if (message_length > 0) { \
-      google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, 0, \
-          &google::LogMessage::SendToLog).stream() \
-          << reinterpret_cast<const char*>(message); \
-      LocalFree(message); \
-    } \
-  }
-#endif
-
-// We use the preprocessor's merging operator, "##", so that, e.g.,
-// LOG(INFO) becomes the token GOOGLE_LOG_INFO.  There's some funny
-// subtle difference between ostream member streaming functions (e.g.,
-// ostream::operator<<(int) and ostream non-member streaming functions
-// (e.g., ::operator<<(ostream&, string&): it turns out that it's
-// impossible to stream something like a string directly to an unnamed
-// ostream. We employ a neat hack by calling the stream() member
-// function of LogMessage which seems to avoid the problem.
-#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
-#define SYSLOG(severity) SYSLOG_ ## severity(0).stream()
-
-namespace google {
-
-// They need the definitions of integer types.
-#include "glog/log_severity.h"
-#include "glog/vlog_is_on.h"
-
-// Initialize google's logging library. You will see the program name
-// specified by argv0 in log outputs.
-GOOGLE_GLOG_DLL_DECL void InitGoogleLogging(const char* argv0);
-
-// Shutdown google's logging library.
-GOOGLE_GLOG_DLL_DECL void ShutdownGoogleLogging();
-
-// Install a function which will be called after LOG(FATAL).
-GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)());
-
-// Enable/Disable old log cleaner.
-GOOGLE_GLOG_DLL_DECL void EnableLogCleaner(int overdue_days);
-GOOGLE_GLOG_DLL_DECL void DisableLogCleaner();
-
-
-class LogSink;  // defined below
-
-// If a non-NULL sink pointer is given, we push this message to that sink.
-// For LOG_TO_SINK we then do normal LOG(severity) logging as well.
-// This is useful for capturing messages and passing/storing them
-// somewhere more specific than the global log of the process.
-// Argument types:
-//   LogSink* sink;
-//   LogSeverity severity;
-// The cast is to disambiguate NULL arguments.
-#define LOG_TO_SINK(sink, severity) \
-  google::LogMessage(                                    \
-      __FILE__, __LINE__,                                               \
-      google::GLOG_ ## severity,                         \
-      static_cast<google::LogSink*>(sink), true).stream()
-#define LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity)                  \
-  google::LogMessage(                                    \
-      __FILE__, __LINE__,                                               \
-      google::GLOG_ ## severity,                         \
-      static_cast<google::LogSink*>(sink), false).stream()
-
-// If a non-NULL string pointer is given, we write this message to that string.
-// We then do normal LOG(severity) logging as well.
-// This is useful for capturing messages and storing them somewhere more
-// specific than the global log of the process.
-// Argument types:
-//   string* message;
-//   LogSeverity severity;
-// The cast is to disambiguate NULL arguments.
-// NOTE: LOG(severity) expands to LogMessage().stream() for the specified
-// severity.
-#define LOG_TO_STRING(severity, message) \
-  LOG_TO_STRING_##severity(static_cast<string*>(message)).stream()
-
-// If a non-NULL pointer is given, we push the message onto the end
-// of a vector of strings; otherwise, we report it with LOG(severity).
-// This is handy for capturing messages and perhaps passing them back
-// to the caller, rather than reporting them immediately.
-// Argument types:
-//   LogSeverity severity;
-//   vector<string> *outvec;
-// The cast is to disambiguate NULL arguments.
-#define LOG_STRING(severity, outvec) \
-  LOG_TO_STRING_##severity(static_cast<std::vector<std::string>*>(outvec)).stream()
-
-#define LOG_IF(severity, condition) \
-  static_cast<void>(0),             \
-  !(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
-#define SYSLOG_IF(severity, condition) \
-  static_cast<void>(0),                \
-  !(condition) ? (void) 0 : google::LogMessageVoidify() & SYSLOG(severity)
-
-#define LOG_ASSERT(condition)  \
-  LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
-#define SYSLOG_ASSERT(condition) \
-  SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
-
-// CHECK dies with a fatal error if condition is not true.  It is *not*
-// controlled by DCHECK_IS_ON(), so the check will be executed regardless of
-// compilation mode.  Therefore, it is safe to do things like:
-//    CHECK(fp->Write(x) == 4)
-#define CHECK(condition)  \
-      LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \
-             << "Check failed: " #condition " "
-
-// A container for a string pointer which can be evaluated to a bool -
-// true iff the pointer is NULL.
-struct CheckOpString {
-  CheckOpString(std::string* str) : str_(str) { }
-  // No destructor: if str_ is non-NULL, we're about to LOG(FATAL),
-  // so there's no point in cleaning up str_.
-  operator bool() const {
-    return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != NULL);
-  }
-  std::string* str_;
-};
-
-// Function is overloaded for integral types to allow static const
-// integrals declared in classes and not defined to be used as arguments to
-// CHECK* macros. It's not encouraged though.
-template <class T>
-inline const T&       GetReferenceableValue(const T&           t) { return t; }
-inline char           GetReferenceableValue(char               t) { return t; }
-inline unsigned char  GetReferenceableValue(unsigned char      t) { return t; }
-inline signed char    GetReferenceableValue(signed char        t) { return t; }
-inline short          GetReferenceableValue(short              t) { return t; }
-inline unsigned short GetReferenceableValue(unsigned short     t) { return t; }
-inline int            GetReferenceableValue(int                t) { return t; }
-inline unsigned int   GetReferenceableValue(unsigned int       t) { return t; }
-inline long           GetReferenceableValue(long               t) { return t; }
-inline unsigned long  GetReferenceableValue(unsigned long      t) { return t; }
-inline long long      GetReferenceableValue(long long          t) { return t; }
-inline unsigned long long GetReferenceableValue(unsigned long long t) {
-  return t;
-}
-
-// This is a dummy class to define the following operator.
-struct DummyClassToDefineOperator {};
-
-}
-
-// Define global operator<< to declare using ::operator<<.
-// This declaration will allow use to use CHECK macros for user
-// defined classes which have operator<< (e.g., stl_logging.h).
-inline std::ostream& operator<<(
-    std::ostream& out, const google::DummyClassToDefineOperator&) {
-  return out;
-}
-
-namespace google {
-
-// This formats a value for a failing CHECK_XX statement.  Ordinarily,
-// it uses the definition for operator<<, with a few special cases below.
-template <typename T>
-inline void MakeCheckOpValueString(std::ostream* os, const T& v) {
-  (*os) << v;
-}
-
-// Overrides for char types provide readable values for unprintable
-// characters.
-template <> GOOGLE_GLOG_DLL_DECL
-void MakeCheckOpValueString(std::ostream* os, const char& v);
-template <> GOOGLE_GLOG_DLL_DECL
-void MakeCheckOpValueString(std::ostream* os, const signed char& v);
-template <> GOOGLE_GLOG_DLL_DECL
-void MakeCheckOpValueString(std::ostream* os, const unsigned char& v);
-
-// Build the error message string. Specify no inlining for code size.
-template <typename T1, typename T2>
-std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext)
-    ;
-
-namespace base {
-namespace internal {
-
-// If "s" is less than base_logging::INFO, returns base_logging::INFO.
-// If "s" is greater than base_logging::FATAL, returns
-// base_logging::ERROR.  Otherwise, returns "s".
-LogSeverity NormalizeSeverity(LogSeverity s);
-
-}  // namespace internal
-
-// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX
-// statement.  See MakeCheckOpString for sample usage.  Other
-// approaches were considered: use of a template method (e.g.,
-// base::BuildCheckOpString(exprtext, base::Print<T1>, &v1,
-// base::Print<T2>, &v2), however this approach has complications
-// related to volatile arguments and function-pointer arguments).
-class GOOGLE_GLOG_DLL_DECL CheckOpMessageBuilder {
- public:
-  // Inserts "exprtext" and " (" to the stream.
-  explicit CheckOpMessageBuilder(const char *exprtext);
-  // Deletes "stream_".
-  ~CheckOpMessageBuilder();
-  // For inserting the first variable.
-  std::ostream* ForVar1() { return stream_; }
-  // For inserting the second variable (adds an intermediate " vs. ").
-  std::ostream* ForVar2();
-  // Get the result (inserts the closing ")").
-  std::string* NewString();
-
- private:
-  std::ostringstream *stream_;
-};
-
-}  // namespace base
-
-template <typename T1, typename T2>
-std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) {
-  base::CheckOpMessageBuilder comb(exprtext);
-  MakeCheckOpValueString(comb.ForVar1(), v1);
-  MakeCheckOpValueString(comb.ForVar2(), v2);
-  return comb.NewString();
-}
-
-// Helper functions for CHECK_OP macro.
-// The (int, int) specialization works around the issue that the compiler
-// will not instantiate the template version of the function on values of
-// unnamed enum type - see comment below.
-#define DEFINE_CHECK_OP_IMPL(name, op) \
-  template <typename T1, typename T2> \
-  inline std::string* name##Impl(const T1& v1, const T2& v2,    \
-                            const char* exprtext) { \
-    if (GOOGLE_PREDICT_TRUE(v1 op v2)) return NULL; \
-    else return MakeCheckOpString(v1, v2, exprtext); \
-  } \
-  inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \
-    return name##Impl<int, int>(v1, v2, exprtext); \
-  }
-
-// We use the full name Check_EQ, Check_NE, etc. in case the file including
-// base/logging.h provides its own #defines for the simpler names EQ, NE, etc.
-// This happens if, for example, those are used as token names in a
-// yacc grammar.
-DEFINE_CHECK_OP_IMPL(Check_EQ, ==)  // Compilation error with CHECK_EQ(NULL, x)?
-DEFINE_CHECK_OP_IMPL(Check_NE, !=)  // Use CHECK(x == NULL) instead.
-DEFINE_CHECK_OP_IMPL(Check_LE, <=)
-DEFINE_CHECK_OP_IMPL(Check_LT, < )
-DEFINE_CHECK_OP_IMPL(Check_GE, >=)
-DEFINE_CHECK_OP_IMPL(Check_GT, > )
-#undef DEFINE_CHECK_OP_IMPL
-
-// Helper macro for binary operators.
-// Don't use this macro directly in your code, use CHECK_EQ et al below.
-
-#if defined(STATIC_ANALYSIS)
-// Only for static analysis tool to know that it is equivalent to assert
-#define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2))
-#elif DCHECK_IS_ON()
-// In debug mode, avoid constructing CheckOpStrings if possible,
-// to reduce the overhead of CHECK statments by 2x.
-// Real DCHECK-heavy tests have seen 1.5x speedups.
-
-// The meaning of "string" might be different between now and
-// when this macro gets invoked (e.g., if someone is experimenting
-// with other string implementations that get defined after this
-// file is included).  Save the current meaning now and use it
-// in the macro.
-typedef std::string _Check_string;
-#define CHECK_OP_LOG(name, op, val1, val2, log)                         \
-  while (google::_Check_string* _result =                \
-         google::Check##name##Impl(                      \
-             google::GetReferenceableValue(val1),        \
-             google::GetReferenceableValue(val2),        \
-             #val1 " " #op " " #val2))                                  \
-    log(__FILE__, __LINE__,                                             \
-        google::CheckOpString(_result)).stream()
-#else
-// In optimized mode, use CheckOpString to hint to compiler that
-// the while condition is unlikely.
-#define CHECK_OP_LOG(name, op, val1, val2, log)                         \
-  while (google::CheckOpString _result =                 \
-         google::Check##name##Impl(                      \
-             google::GetReferenceableValue(val1),        \
-             google::GetReferenceableValue(val2),        \
-             #val1 " " #op " " #val2))                                  \
-    log(__FILE__, __LINE__, _result).stream()
-#endif  // STATIC_ANALYSIS, DCHECK_IS_ON()
-
-#if GOOGLE_STRIP_LOG <= 3
-#define CHECK_OP(name, op, val1, val2) \
-  CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal)
-#else
-#define CHECK_OP(name, op, val1, val2) \
-  CHECK_OP_LOG(name, op, val1, val2, google::NullStreamFatal)
-#endif // STRIP_LOG <= 3
-
-// Equality/Inequality checks - compare two values, and log a FATAL message
-// including the two values when the result is not as expected.  The values
-// must have operator<<(ostream, ...) defined.
-//
-// You may append to the error message like so:
-//   CHECK_NE(1, 2) << ": The world must be ending!";
-//
-// We are very careful to ensure that each argument is evaluated exactly
-// once, and that anything which is legal to pass as a function argument is
-// legal here.  In particular, the arguments may be temporary expressions
-// which will end up being destroyed at the end of the apparent statement,
-// for example:
-//   CHECK_EQ(string("abc")[1], 'b');
-//
-// WARNING: These don't compile correctly if one of the arguments is a pointer
-// and the other is NULL. To work around this, simply static_cast NULL to the
-// type of the desired pointer.
-
-#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2)
-#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2)
-#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2)
-#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2)
-#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2)
-#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2)
-
-// Check that the input is non NULL.  This very useful in constructor
-// initializer lists.
-
-#define CHECK_NOTNULL(val) \
-  google::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val))
-
-// Helper functions for string comparisons.
-// To avoid bloat, the definitions are in logging.cc.
-#define DECLARE_CHECK_STROP_IMPL(func, expected) \
-  GOOGLE_GLOG_DLL_DECL std::string* Check##func##expected##Impl( \
-      const char* s1, const char* s2, const char* names);
-DECLARE_CHECK_STROP_IMPL(strcmp, true)
-DECLARE_CHECK_STROP_IMPL(strcmp, false)
-DECLARE_CHECK_STROP_IMPL(strcasecmp, true)
-DECLARE_CHECK_STROP_IMPL(strcasecmp, false)
-#undef DECLARE_CHECK_STROP_IMPL
-
-// Helper macro for string comparisons.
-// Don't use this macro directly in your code, use CHECK_STREQ et al below.
-#define CHECK_STROP(func, op, expected, s1, s2) \
-  while (google::CheckOpString _result = \
-         google::Check##func##expected##Impl((s1), (s2), \
-                                     #s1 " " #op " " #s2)) \
-    LOG(FATAL) << *_result.str_
-
-
-// String (char*) equality/inequality checks.
-// CASE versions are case-insensitive.
-//
-// Note that "s1" and "s2" may be temporary strings which are destroyed
-// by the compiler at the end of the current "full expression"
-// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())).
-
-#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2)
-#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2)
-#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(strcasecmp, ==, true, s1, s2)
-#define CHECK_STRCASENE(s1, s2) CHECK_STROP(strcasecmp, !=, false, s1, s2)
-
-#define CHECK_INDEX(I,A) CHECK(I < (sizeof(A)/sizeof(A[0])))
-#define CHECK_BOUND(B,A) CHECK(B <= (sizeof(A)/sizeof(A[0])))
-
-#define CHECK_DOUBLE_EQ(val1, val2)              \
-  do {                                           \
-    CHECK_LE((val1), (val2)+0.000000000000001L); \
-    CHECK_GE((val1), (val2)-0.000000000000001L); \
-  } while (0)
-
-#define CHECK_NEAR(val1, val2, margin)           \
-  do {                                           \
-    CHECK_LE((val1), (val2)+(margin));           \
-    CHECK_GE((val1), (val2)-(margin));           \
-  } while (0)
-
-// perror()..googly style!
-//
-// PLOG() and PLOG_IF() and PCHECK() behave exactly like their LOG* and
-// CHECK equivalents with the addition that they postpend a description
-// of the current state of errno to their output lines.
-
-#define PLOG(severity) GOOGLE_PLOG(severity, 0).stream()
-
-#define GOOGLE_PLOG(severity, counter)  \
-  google::ErrnoLogMessage( \
-      __FILE__, __LINE__, google::GLOG_ ## severity, counter, \
-      &google::LogMessage::SendToLog)
-
-#define PLOG_IF(severity, condition) \
-  static_cast<void>(0),              \
-  !(condition) ? (void) 0 : google::LogMessageVoidify() & PLOG(severity)
-
-// A CHECK() macro that postpends errno if the condition is false. E.g.
-//
-// if (poll(fds, nfds, timeout) == -1) { PCHECK(errno == EINTR); ... }
-#define PCHECK(condition)  \
-      PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \
-              << "Check failed: " #condition " "
-
-// A CHECK() macro that lets you assert the success of a function that
-// returns -1 and sets errno in case of an error. E.g.
-//
-// CHECK_ERR(mkdir(path, 0700));
-//
-// or
-//
-// int fd = open(filename, flags); CHECK_ERR(fd) << ": open " << filename;
-#define CHECK_ERR(invocation)                                          \
-PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN((invocation) == -1))    \
-        << #invocation
-
-// Use macro expansion to create, for each use of LOG_EVERY_N(), static
-// variables with the __LINE__ expansion as part of the variable name.
-#define LOG_EVERY_N_VARNAME(base, line) LOG_EVERY_N_VARNAME_CONCAT(base, line)
-#define LOG_EVERY_N_VARNAME_CONCAT(base, line) base ## line
-
-#define LOG_OCCURRENCES LOG_EVERY_N_VARNAME(occurrences_, __LINE__)
-#define LOG_OCCURRENCES_MOD_N LOG_EVERY_N_VARNAME(occurrences_mod_n_, __LINE__)
-
-#define SOME_KIND_OF_LOG_EVERY_N(severity, n, what_to_do) \
-  static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \
-  ++LOG_OCCURRENCES; \
-  if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \
-  if (LOG_OCCURRENCES_MOD_N == 1) \
-    google::LogMessage( \
-        __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \
-        &what_to_do).stream()
-
-#define SOME_KIND_OF_LOG_IF_EVERY_N(severity, condition, n, what_to_do) \
-  static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \
-  ++LOG_OCCURRENCES; \
-  if (condition && \
-      ((LOG_OCCURRENCES_MOD_N=(LOG_OCCURRENCES_MOD_N + 1) % n) == (1 % n))) \
-    google::LogMessage( \
-        __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \
-                 &what_to_do).stream()
-
-#define SOME_KIND_OF_PLOG_EVERY_N(severity, n, what_to_do) \
-  static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \
-  ++LOG_OCCURRENCES; \
-  if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \
-  if (LOG_OCCURRENCES_MOD_N == 1) \
-    google::ErrnoLogMessage( \
-        __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \
-        &what_to_do).stream()
-
-#define SOME_KIND_OF_LOG_FIRST_N(severity, n, what_to_do) \
-  static int LOG_OCCURRENCES = 0; \
-  if (LOG_OCCURRENCES <= n) \
-    ++LOG_OCCURRENCES; \
-  if (LOG_OCCURRENCES <= n) \
-    google::LogMessage( \
-        __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \
-        &what_to_do).stream()
-
-namespace glog_internal_namespace_ {
-template <bool>
-struct CompileAssert {
-};
-struct CrashReason;
-
-// Returns true if FailureSignalHandler is installed.
-// Needs to be exported since it's used by the signalhandler_unittest.
-GOOGLE_GLOG_DLL_DECL bool IsFailureSignalHandlerInstalled();
-}  // namespace glog_internal_namespace_
-
-#define LOG_EVERY_N(severity, n)                                        \
-  SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog)
-
-#define SYSLOG_EVERY_N(severity, n) \
-  SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToSyslogAndLog)
-
-#define PLOG_EVERY_N(severity, n) \
-  SOME_KIND_OF_PLOG_EVERY_N(severity, (n), google::LogMessage::SendToLog)
-
-#define LOG_FIRST_N(severity, n) \
-  SOME_KIND_OF_LOG_FIRST_N(severity, (n), google::LogMessage::SendToLog)
-
-#define LOG_IF_EVERY_N(severity, condition, n) \
-  SOME_KIND_OF_LOG_IF_EVERY_N(severity, (condition), (n), google::LogMessage::SendToLog)
-
-// We want the special COUNTER value available for LOG_EVERY_X()'ed messages
-enum PRIVATE_Counter {COUNTER};
-
-#ifdef GLOG_NO_ABBREVIATED_SEVERITIES
-// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
-// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
-// to keep using this syntax, we define this macro to do the same thing
-// as COMPACT_GOOGLE_LOG_ERROR.
-#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR
-#define SYSLOG_0 SYSLOG_ERROR
-#define LOG_TO_STRING_0 LOG_TO_STRING_ERROR
-// Needed for LOG_IS_ON(ERROR).
-const LogSeverity GLOG_0 = GLOG_ERROR;
-#else
-// Users may include windows.h after logging.h without
-// GLOG_NO_ABBREVIATED_SEVERITIES nor WIN32_LEAN_AND_MEAN.
-// For this case, we cannot detect if ERROR is defined before users
-// actually use ERROR. Let's make an undefined symbol to warn users.
-# define GLOG_ERROR_MSG ERROR_macro_is_defined_Define_GLOG_NO_ABBREVIATED_SEVERITIES_before_including_logging_h_See_the_document_for_detail
-# define COMPACT_GOOGLE_LOG_0 GLOG_ERROR_MSG
-# define SYSLOG_0 GLOG_ERROR_MSG
-# define LOG_TO_STRING_0 GLOG_ERROR_MSG
-# define GLOG_0 GLOG_ERROR_MSG
-#endif
-
-// Plus some debug-logging macros that get compiled to nothing for production
-
-#if DCHECK_IS_ON()
-
-#define DLOG(severity) LOG(severity)
-#define DVLOG(verboselevel) VLOG(verboselevel)
-#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
-#define DLOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n)
-#define DLOG_IF_EVERY_N(severity, condition, n) \
-  LOG_IF_EVERY_N(severity, condition, n)
-#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
-
-// debug-only checking.  executed if DCHECK_IS_ON().
-#define DCHECK(condition) CHECK(condition)
-#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2)
-#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2)
-#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2)
-#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2)
-#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2)
-#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2)
-#define DCHECK_NOTNULL(val) CHECK_NOTNULL(val)
-#define DCHECK_STREQ(str1, str2) CHECK_STREQ(str1, str2)
-#define DCHECK_STRCASEEQ(str1, str2) CHECK_STRCASEEQ(str1, str2)
-#define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2)
-#define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2)
-
-#else  // !DCHECK_IS_ON()
-
-#define DLOG(severity)  \
-  static_cast<void>(0), \
-  true ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
-
-#define DVLOG(verboselevel)             \
-  static_cast<void>(0),                 \
-  (true || !VLOG_IS_ON(verboselevel)) ? \
-    (void) 0 : google::LogMessageVoidify() & LOG(INFO)
-
-#define DLOG_IF(severity, condition) \
-  static_cast<void>(0),              \
-  (true || !(condition)) ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
-
-#define DLOG_EVERY_N(severity, n) \
-  static_cast<void>(0),           \
-  true ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
-
-#define DLOG_IF_EVERY_N(severity, condition, n) \
-  static_cast<void>(0),                         \
-  (true || !(condition))? (void) 0 : google::LogMessageVoidify() & LOG(severity)
-
-#define DLOG_ASSERT(condition) \
-  static_cast<void>(0),        \
-  true ? (void) 0 : LOG_ASSERT(condition)
-
-// MSVC warning C4127: conditional expression is constant
-#define DCHECK(condition) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK(condition)
-
-#define DCHECK_EQ(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_EQ(val1, val2)
-
-#define DCHECK_NE(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_NE(val1, val2)
-
-#define DCHECK_LE(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_LE(val1, val2)
-
-#define DCHECK_LT(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_LT(val1, val2)
-
-#define DCHECK_GE(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_GE(val1, val2)
-
-#define DCHECK_GT(val1, val2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_GT(val1, val2)
-
-// You may see warnings in release mode if you don't use the return
-// value of DCHECK_NOTNULL. Please just use DCHECK for such cases.
-#define DCHECK_NOTNULL(val) (val)
-
-#define DCHECK_STREQ(str1, str2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_STREQ(str1, str2)
-
-#define DCHECK_STRCASEEQ(str1, str2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_STRCASEEQ(str1, str2)
-
-#define DCHECK_STRNE(str1, str2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_STRNE(str1, str2)
-
-#define DCHECK_STRCASENE(str1, str2) \
-  GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \
-  while (false) \
-    GLOG_MSVC_POP_WARNING() CHECK_STRCASENE(str1, str2)
-
-#endif  // DCHECK_IS_ON()
-
-// Log only in verbose mode.
-
-#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel))
-
-#define VLOG_IF(verboselevel, condition) \
-  LOG_IF(INFO, (condition) && VLOG_IS_ON(verboselevel))
-
-#define VLOG_EVERY_N(verboselevel, n) \
-  LOG_IF_EVERY_N(INFO, VLOG_IS_ON(verboselevel), n)
-
-#define VLOG_IF_EVERY_N(verboselevel, condition, n) \
-  LOG_IF_EVERY_N(INFO, (condition) && VLOG_IS_ON(verboselevel), n)
-
-namespace base_logging {
-
-// LogMessage::LogStream is a std::ostream backed by this streambuf.
-// This class ignores overflow and leaves two bytes at the end of the
-// buffer to allow for a '\n' and '\0'.
-class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf {
- public:
-  // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\0'.
-  LogStreamBuf(char *buf, int len) {
-    setp(buf, buf + len - 2);
-  }
-
-  // This effectively ignores overflow.
-  virtual int_type overflow(int_type ch) {
-    return ch;
-  }
-
-  // Legacy public ostrstream method.
-  size_t pcount() const { return pptr() - pbase(); }
-  char* pbase() const { return std::streambuf::pbase(); }
-};
-
-}  // namespace base_logging
-
-//
-// This class more or less represents a particular log message.  You
-// create an instance of LogMessage and then stream stuff to it.
-// When you finish streaming to it, ~LogMessage is called and the
-// full message gets streamed to the appropriate destination.
-//
-// You shouldn't actually use LogMessage's constructor to log things,
-// though.  You should use the LOG() macro (and variants thereof)
-// above.
-class GOOGLE_GLOG_DLL_DECL LogMessage {
-public:
-  enum {
-    // Passing kNoLogPrefix for the line number disables the
-    // log-message prefix. Useful for using the LogMessage
-    // infrastructure as a printing utility. See also the --log_prefix
-    // flag for controlling the log-message prefix on an
-    // application-wide basis.
-    kNoLogPrefix = -1
-  };
-
-  // LogStream inherit from non-DLL-exported class (std::ostrstream)
-  // and VC++ produces a warning for this situation.
-  // However, MSDN says "C4275 can be ignored in Microsoft Visual C++
-  // 2005 if you are deriving from a type in the Standard C++ Library"
-  // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
-  // Let's just ignore the warning.
-GLOG_MSVC_PUSH_DISABLE_WARNING(4275)
-  class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream {
-GLOG_MSVC_POP_WARNING()
-  public:
-    LogStream(char *buf, int len, int ctr)
-        : std::ostream(NULL),
-          streambuf_(buf, len),
-          ctr_(ctr),
-          self_(this) {
-      rdbuf(&streambuf_);
-    }
-
-    int ctr() const { return ctr_; }
-    void set_ctr(int ctr) { ctr_ = ctr; }
-    LogStream* self() const { return self_; }
-
-    // Legacy std::streambuf methods.
-    size_t pcount() const { return streambuf_.pcount(); }
-    char* pbase() const { return streambuf_.pbase(); }
-    char* str() const { return pbase(); }
-
-  private:
-    LogStream(const LogStream&);
-    LogStream& operator=(const LogStream&);
-    base_logging::LogStreamBuf streambuf_;
-    int ctr_;  // Counter hack (for the LOG_EVERY_X() macro)
-    LogStream *self_;  // Consistency check hack
-  };
-
-public:
-  // icc 8 requires this typedef to avoid an internal compiler error.
-  typedef void (LogMessage::*SendMethod)();
-
-  LogMessage(const char* file, int line, LogSeverity severity, int ctr,
-             SendMethod send_method);
-
-  // Two special constructors that generate reduced amounts of code at
-  // LOG call sites for common cases.
-
-  // Used for LOG(INFO): Implied are:
-  // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog.
-  //
-  // Using this constructor instead of the more complex constructor above
-  // saves 19 bytes per call site.
-  LogMessage(const char* file, int line);
-
-  // Used for LOG(severity) where severity != INFO.  Implied
-  // are: ctr = 0, send_method = &LogMessage::SendToLog
-  //
-  // Using this constructor instead of the more complex constructor above
-  // saves 17 bytes per call site.
-  LogMessage(const char* file, int line, LogSeverity severity);
-
-  // Constructor to log this message to a specified sink (if not NULL).
-  // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if
-  // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise.
-  LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink,
-             bool also_send_to_log);
-
-  // Constructor where we also give a vector<string> pointer
-  // for storing the messages (if the pointer is not NULL).
-  // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog.
-  LogMessage(const char* file, int line, LogSeverity severity,
-             std::vector<std::string>* outvec);
-
-  // Constructor where we also give a string pointer for storing the
-  // message (if the pointer is not NULL).  Implied are: ctr = 0,
-  // send_method = &LogMessage::WriteToStringAndLog.
-  LogMessage(const char* file, int line, LogSeverity severity,
-             std::string* message);
-
-  // A special constructor used for check failures
-  LogMessage(const char* file, int line, const CheckOpString& result);
-
-  ~LogMessage();
-
-  // Flush a buffered message to the sink set in the constructor.  Always
-  // called by the destructor, it may also be called from elsewhere if
-  // needed.  Only the first call is actioned; any later ones are ignored.
-  void Flush();
-
-  // An arbitrary limit on the length of a single log message.  This
-  // is so that streaming can be done more efficiently.
-  static const size_t kMaxLogMessageLen;
-
-  // Theses should not be called directly outside of logging.*,
-  // only passed as SendMethod arguments to other LogMessage methods:
-  void SendToLog();  // Actually dispatch to the logs
-  void SendToSyslogAndLog();  // Actually dispatch to syslog and the logs
-
-  // Call abort() or similar to perform LOG(FATAL) crash.
-  static void __declspec(noreturn) Fail();
-
-  std::ostream& stream();
-
-  int preserved_errno() const;
-
-  // Must be called without the log_mutex held.  (L < log_mutex)
-  static int64 num_messages(int severity);
-
-  struct LogMessageData;
-
-private:
-  // Fully internal SendMethod cases:
-  void SendToSinkAndLog();  // Send to sink if provided and dispatch to the logs
-  void SendToSink();  // Send to sink if provided, do nothing otherwise.
-
-  // Write to string if provided and dispatch to the logs.
-  void WriteToStringAndLog();
-
-  void SaveOrSendToLog();  // Save to stringvec if provided, else to logs
-
-  void Init(const char* file, int line, LogSeverity severity,
-            void (LogMessage::*send_method)());
-
-  // Used to fill in crash information during LOG(FATAL) failures.
-  void RecordCrashReason(glog_internal_namespace_::CrashReason* reason);
-
-  // Counts of messages sent at each priority:
-  static int64 num_messages_[NUM_SEVERITIES];  // under log_mutex
-
-  // We keep the data in a separate struct so that each instance of
-  // LogMessage uses less stack space.
-  LogMessageData* allocated_;
-  LogMessageData* data_;
-
-  friend class LogDestination;
-
-  LogMessage(const LogMessage&);
-  void operator=(const LogMessage&);
-};
-
-// This class happens to be thread-hostile because all instances share
-// a single data buffer, but since it can only be created just before
-// the process dies, we don't worry so much.
-class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage {
- public:
-  LogMessageFatal(const char* file, int line);
-  LogMessageFatal(const char* file, int line, const CheckOpString& result);
-  __declspec(noreturn) ~LogMessageFatal();
-};
-
-// A non-macro interface to the log facility; (useful
-// when the logging level is not a compile-time constant).
-inline void LogAtLevel(int const severity, std::string const &msg) {
-  LogMessage(__FILE__, __LINE__, severity).stream() << msg;
-}
-
-// A macro alternative of LogAtLevel. New code may want to use this
-// version since there are two advantages: 1. this version outputs the
-// file name and the line number where this macro is put like other
-// LOG macros, 2. this macro can be used as C++ stream.
-#define LOG_AT_LEVEL(severity) google::LogMessage(__FILE__, __LINE__, severity).stream()
-
-// Check if it's compiled in C++11 mode.
-//
-// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least
-// gcc-4.7 and clang-3.1 (2011-12-13).  __cplusplus was defined to 1
-// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is
-// defined according to the language version in effect thereafter.
-// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite
-// reasonably good C++11 support, so we set LANG_CXX for it and
-// newer versions (_MSC_VER >= 1900).
-#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \
-     (defined(_MSC_VER) && _MSC_VER >= 1900))
-// Helper for CHECK_NOTNULL().
-//
-// In C++11, all cases can be handled by a single function. Since the value
-// category of the argument is preserved (also for rvalue references),
-// member initializer lists like the one below will compile correctly:
-//
-//   Foo()
-//     : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {}
-template <typename T>
-T CheckNotNull(const char* file, int line, const char* names, T&& t) {
- if (t == nullptr) {
-   LogMessageFatal(file, line, new std::string(names));
- }
- return std::forward<T>(t);
-}
-
-#else
-
-// A small helper for CHECK_NOTNULL().
-template <typename T>
-T* CheckNotNull(const char *file, int line, const char *names, T* t) {
-  if (t == NULL) {
-    LogMessageFatal(file, line, new std::string(names));
-  }
-  return t;
-}
-#endif
-
-// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This
-// only works if ostream is a LogStream. If the ostream is not a
-// LogStream you'll get an assert saying as much at runtime.
-GOOGLE_GLOG_DLL_DECL std::ostream& operator<<(std::ostream &os,
-                                              const PRIVATE_Counter&);
-
-
-// Derived class for PLOG*() above.
-class GOOGLE_GLOG_DLL_DECL ErrnoLogMessage : public LogMessage {
- public:
-
-  ErrnoLogMessage(const char* file, int line, LogSeverity severity, int ctr,
-                  void (LogMessage::*send_method)());
-
-  // Postpends ": strerror(errno) [errno]".
-  ~ErrnoLogMessage();
-
- private:
-  ErrnoLogMessage(const ErrnoLogMessage&);
-  void operator=(const ErrnoLogMessage&);
-};
-
-
-// This class is used to explicitly ignore values in the conditional
-// logging macros.  This avoids compiler warnings like "value computed
-// is not used" and "statement has no effect".
-
-class GOOGLE_GLOG_DLL_DECL LogMessageVoidify {
- public:
-  LogMessageVoidify() { }
-  // This has to be an operator with a precedence lower than << but
-  // higher than ?:
-  void operator&(std::ostream&) { }
-};
-
-
-// Flushes all log files that contains messages that are at least of
-// the specified severity level.  Thread-safe.
-GOOGLE_GLOG_DLL_DECL void FlushLogFiles(LogSeverity min_severity);
-
-// Flushes all log files that contains messages that are at least of
-// the specified severity level. Thread-hostile because it ignores
-// locking -- used for catastrophic failures.
-GOOGLE_GLOG_DLL_DECL void FlushLogFilesUnsafe(LogSeverity min_severity);
-
-//
-// Set the destination to which a particular severity level of log
-// messages is sent.  If base_filename is "", it means "don't log this
-// severity".  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetLogDestination(LogSeverity severity,
-                                            const char* base_filename);
-
-//
-// Set the basename of the symlink to the latest log file at a given
-// severity.  If symlink_basename is empty, do not make a symlink.  If
-// you don't call this function, the symlink basename is the
-// invocation name of the program.  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetLogSymlink(LogSeverity severity,
-                                        const char* symlink_basename);
-
-//
-// Used to send logs to some other kind of destination
-// Users should subclass LogSink and override send to do whatever they want.
-// Implementations must be thread-safe because a shared instance will
-// be called from whichever thread ran the LOG(XXX) line.
-class GOOGLE_GLOG_DLL_DECL LogSink {
- public:
-  virtual ~LogSink();
-
-  // Sink's logging logic (message_len is such as to exclude '\n' at the end).
-  // This method can't use LOG() or CHECK() as logging system mutex(s) are held
-  // during this call.
-  virtual void send(LogSeverity severity, const char* full_filename,
-                    const char* base_filename, int line,
-                    const struct ::tm* tm_time,
-                    const char* message, size_t message_len, int32 usecs) {
-    send(severity, full_filename, base_filename, line,
-         tm_time, message, message_len);
-  }
-  // This send() signature is obsolete.
-  // New implementations should define this in terms of
-  // the above send() method.
-  virtual void send(LogSeverity severity, const char* full_filename,
-                    const char* base_filename, int line,
-                    const struct ::tm* tm_time,
-                    const char* message, size_t message_len) = 0;
-
-  // Redefine this to implement waiting for
-  // the sink's logging logic to complete.
-  // It will be called after each send() returns,
-  // but before that LogMessage exits or crashes.
-  // By default this function does nothing.
-  // Using this function one can implement complex logic for send()
-  // that itself involves logging; and do all this w/o causing deadlocks and
-  // inconsistent rearrangement of log messages.
-  // E.g. if a LogSink has thread-specific actions, the send() method
-  // can simply add the message to a queue and wake up another thread that
-  // handles real logging while itself making some LOG() calls;
-  // WaitTillSent() can be implemented to wait for that logic to complete.
-  // See our unittest for an example.
-  virtual void WaitTillSent();
-
-  // Returns the normal text output of the log message.
-  // Can be useful to implement send().
-  static std::string ToString(LogSeverity severity, const char* file, int line,
-                              const struct ::tm* tm_time,
-                              const char* message, size_t message_len,
-                              int32 usecs);
-
-  // Obsolete
-  static std::string ToString(LogSeverity severity, const char* file, int line,
-                              const struct ::tm* tm_time,
-                              const char* message, size_t message_len) {
-    return ToString(severity, file, line, tm_time, message, message_len, 0);
-  }
-};
-
-// Add or remove a LogSink as a consumer of logging data.  Thread-safe.
-GOOGLE_GLOG_DLL_DECL void AddLogSink(LogSink *destination);
-GOOGLE_GLOG_DLL_DECL void RemoveLogSink(LogSink *destination);
-
-//
-// Specify an "extension" added to the filename specified via
-// SetLogDestination.  This applies to all severity levels.  It's
-// often used to append the port we're listening on to the logfile
-// name.  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetLogFilenameExtension(
-    const char* filename_extension);
-
-//
-// Make it so that all log messages of at least a particular severity
-// are logged to stderr (in addition to logging to the usual log
-// file(s)).  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetStderrLogging(LogSeverity min_severity);
-
-//
-// Make it so that all log messages go only to stderr.  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void LogToStderr();
-
-//
-// Make it so that all log messages of at least a particular severity are
-// logged via email to a list of addresses (in addition to logging to the
-// usual log file(s)).  The list of addresses is just a string containing
-// the email addresses to send to (separated by spaces, say).  Thread-safe.
-//
-GOOGLE_GLOG_DLL_DECL void SetEmailLogging(LogSeverity min_severity,
-                                          const char* addresses);
-
-// A simple function that sends email. dest is a commma-separated
-// list of addressess.  Thread-safe.
-GOOGLE_GLOG_DLL_DECL bool SendEmail(const char *dest,
-                                    const char *subject, const char *body);
-
-GOOGLE_GLOG_DLL_DECL const std::vector<std::string>& GetLoggingDirectories();
-
-// For tests only:  Clear the internal [cached] list of logging directories to
-// force a refresh the next time GetLoggingDirectories is called.
-// Thread-hostile.
-void TestOnly_ClearLoggingDirectoriesList();
-
-// Returns a set of existing temporary directories, which will be a
-// subset of the directories returned by GetLogginDirectories().
-// Thread-safe.
-GOOGLE_GLOG_DLL_DECL void GetExistingTempDirectories(
-    std::vector<std::string>* list);
-
-// Print any fatal message again -- useful to call from signal handler
-// so that the last thing in the output is the fatal message.
-// Thread-hostile, but a race is unlikely.
-GOOGLE_GLOG_DLL_DECL void ReprintFatalMessage();
-
-// Truncate a log file that may be the append-only output of multiple
-// processes and hence can't simply be renamed/reopened (typically a
-// stdout/stderr).  If the file "path" is > "limit" bytes, copy the
-// last "keep" bytes to offset 0 and truncate the rest. Since we could
-// be racing with other writers, this approach has the potential to
-// lose very small amounts of data. For security, only follow symlinks
-// if the path is /proc/self/fd/*
-GOOGLE_GLOG_DLL_DECL void TruncateLogFile(const char *path,
-                                          int64 limit, int64 keep);
-
-// Truncate stdout and stderr if they are over the value specified by
-// --max_log_size; keep the final 1MB.  This function has the same
-// race condition as TruncateLogFile.
-GOOGLE_GLOG_DLL_DECL void TruncateStdoutStderr();
-
-// Return the string representation of the provided LogSeverity level.
-// Thread-safe.
-GOOGLE_GLOG_DLL_DECL const char* GetLogSeverityName(LogSeverity severity);
-
-// ---------------------------------------------------------------------
-// Implementation details that are not useful to most clients
-// ---------------------------------------------------------------------
-
-// A Logger is the interface used by logging modules to emit entries
-// to a log.  A typical implementation will dump formatted data to a
-// sequence of files.  We also provide interfaces that will forward
-// the data to another thread so that the invoker never blocks.
-// Implementations should be thread-safe since the logging system
-// will write to them from multiple threads.
-
-namespace base {
-
-class GOOGLE_GLOG_DLL_DECL Logger {
- public:
-  virtual ~Logger();
-
-  // Writes "message[0,message_len-1]" corresponding to an event that
-  // occurred at "timestamp".  If "force_flush" is true, the log file
-  // is flushed immediately.
-  //
-  // The input message has already been formatted as deemed
-  // appropriate by the higher level logging facility.  For example,
-  // textual log messages already contain timestamps, and the
-  // file:linenumber header.
-  virtual void Write(bool force_flush,
-                     time_t timestamp,
-                     const char* message,
-                     int message_len) = 0;
-
-  // Flush any buffered messages
-  virtual void Flush() = 0;
-
-  // Get the current LOG file size.
-  // The returned value is approximate since some
-  // logged data may not have been flushed to disk yet.
-  virtual uint32 LogSize() = 0;
-};
-
-// Get the logger for the specified severity level.  The logger
-// remains the property of the logging module and should not be
-// deleted by the caller.  Thread-safe.
-extern GOOGLE_GLOG_DLL_DECL Logger* GetLogger(LogSeverity level);
-
-// Set the logger for the specified severity level.  The logger
-// becomes the property of the logging module and should not
-// be deleted by the caller.  Thread-safe.
-extern GOOGLE_GLOG_DLL_DECL void SetLogger(LogSeverity level, Logger* logger);
-
-}
-
-// glibc has traditionally implemented two incompatible versions of
-// strerror_r(). There is a poorly defined convention for picking the
-// version that we want, but it is not clear whether it even works with
-// all versions of glibc.
-// So, instead, we provide this wrapper that automatically detects the
-// version that is in use, and then implements POSIX semantics.
-// N.B. In addition to what POSIX says, we also guarantee that "buf" will
-// be set to an empty string, if this function failed. This means, in most
-// cases, you do not need to check the error code and you can directly
-// use the value of "buf". It will never have an undefined value.
-// DEPRECATED: Use StrError(int) instead.
-GOOGLE_GLOG_DLL_DECL int posix_strerror_r(int err, char *buf, size_t len);
-
-// A thread-safe replacement for strerror(). Returns a string describing the
-// given POSIX error code.
-GOOGLE_GLOG_DLL_DECL std::string StrError(int err);
-
-// A class for which we define operator<<, which does nothing.
-class GOOGLE_GLOG_DLL_DECL NullStream : public LogMessage::LogStream {
- public:
-  // Initialize the LogStream so the messages can be written somewhere
-  // (they'll never be actually displayed). This will be needed if a
-  // NullStream& is implicitly converted to LogStream&, in which case
-  // the overloaded NullStream::operator<< will not be invoked.
-  NullStream() : LogMessage::LogStream(message_buffer_, 1, 0) { }
-  NullStream(const char* /*file*/, int /*line*/,
-             const CheckOpString& /*result*/) :
-      LogMessage::LogStream(message_buffer_, 1, 0) { }
-  NullStream &stream() { return *this; }
- private:
-  // A very short buffer for messages (which we discard anyway). This
-  // will be needed if NullStream& converted to LogStream& (e.g. as a
-  // result of a conditional expression).
-  char message_buffer_[2];
-};
-
-// Do nothing. This operator is inline, allowing the message to be
-// compiled away. The message will not be compiled away if we do
-// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when
-// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly
-// converted to LogStream and the message will be computed and then
-// quietly discarded.
-template<class T>
-inline NullStream& operator<<(NullStream &str, const T &) { return str; }
-
-// Similar to NullStream, but aborts the program (without stack
-// trace), like LogMessageFatal.
-class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream {
- public:
-  NullStreamFatal() { }
-  NullStreamFatal(const char* file, int line, const CheckOpString& result) :
-      NullStream(file, line, result) { }
-  __declspec(noreturn) ~NullStreamFatal() throw () { _exit(1); }
-};
-
-// Install a signal handler that will dump signal information and a stack
-// trace when the program crashes on certain signals.  We'll install the
-// signal handler for the following signals.
-//
-// SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, and SIGTERM.
-//
-// By default, the signal handler will write the failure dump to the
-// standard error.  You can customize the destination by installing your
-// own writer function by InstallFailureWriter() below.
-//
-// Note on threading:
-//
-// The function should be called before threads are created, if you want
-// to use the failure signal handler for all threads.  The stack trace
-// will be shown only for the thread that receives the signal.  In other
-// words, stack traces of other threads won't be shown.
-GOOGLE_GLOG_DLL_DECL void InstallFailureSignalHandler();
-
-// Installs a function that is used for writing the failure dump.  "data"
-// is the pointer to the beginning of a message to be written, and "size"
-// is the size of the message.  You should not expect the data is
-// terminated with '\0'.
-GOOGLE_GLOG_DLL_DECL void InstallFailureWriter(
-    void (*writer)(const char* data, int size));
-
-}
-
-#endif // _LOGGING_H_
diff --git a/third_party/glog/src/windows/glog/raw_logging.h b/third_party/glog/src/windows/glog/raw_logging.h
deleted file mode 100755
index e0e6d6f1a2..0000000000
--- a/third_party/glog/src/windows/glog/raw_logging.h
+++ /dev/null
@@ -1,184 +0,0 @@
-// This file is automatically generated from src/glog/raw_logging.h.in
-// using src/windows/preprocess.sh.
-// DO NOT EDIT!
-
-// Copyright (c) 2006, Google 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:
-//
-//     * 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.
-//
-// Author: Maxim Lifantsev
-//
-// Thread-safe logging routines that do not allocate any memory or
-// acquire any locks, and can therefore be used by low-level memory
-// allocation and synchronization code.
-
-#ifndef BASE_RAW_LOGGING_H_
-#define BASE_RAW_LOGGING_H_
-
-#include <time.h>
-
-namespace google {
-
-#include "glog/log_severity.h"
-#include "glog/vlog_is_on.h"
-
-// Annoying stuff for windows -- makes sure clients can import these functions
-#ifndef GOOGLE_GLOG_DLL_DECL
-# if defined(_WIN32) && !defined(__CYGWIN__)
-#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
-# else
-#   define GOOGLE_GLOG_DLL_DECL
-# endif
-#endif
-
-// This is similar to LOG(severity) << format... and VLOG(level) << format..,
-// but
-// * it is to be used ONLY by low-level modules that can't use normal LOG()
-// * it is desiged to be a low-level logger that does not allocate any
-//   memory and does not need any locks, hence:
-// * it logs straight and ONLY to STDERR w/o buffering
-// * it uses an explicit format and arguments list
-// * it will silently chop off really long message strings
-// Usage example:
-//   RAW_LOG(ERROR, "Failed foo with %i: %s", status, error);
-//   RAW_VLOG(3, "status is %i", status);
-// These will print an almost standard log lines like this to stderr only:
-//   E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file
-//   I0821 211317 file.cc:142] RAW: status is 20
-#define RAW_LOG(severity, ...) \
-  do { \
-    switch (google::GLOG_ ## severity) {  \
-      case 0: \
-        RAW_LOG_INFO(__VA_ARGS__); \
-        break; \
-      case 1: \
-        RAW_LOG_WARNING(__VA_ARGS__); \
-        break; \
-      case 2: \
-        RAW_LOG_ERROR(__VA_ARGS__); \
-        break; \
-      case 3: \
-        RAW_LOG_FATAL(__VA_ARGS__); \
-        break; \
-      default: \
-        break; \
-    } \
-  } while (0)
-
-// The following STRIP_LOG testing is performed in the header file so that it's
-// possible to completely compile out the logging code and the log messages.
-#if STRIP_LOG == 0
-#define RAW_VLOG(verboselevel, ...) \
-  do { \
-    if (VLOG_IS_ON(verboselevel)) { \
-      RAW_LOG_INFO(__VA_ARGS__); \
-    } \
-  } while (0)
-#else
-#define RAW_VLOG(verboselevel, ...) RawLogStub__(0, __VA_ARGS__)
-#endif // STRIP_LOG == 0
-
-#if STRIP_LOG == 0
-#define RAW_LOG_INFO(...) google::RawLog__(google::GLOG_INFO, \
-                                   __FILE__, __LINE__, __VA_ARGS__)
-#else
-#define RAW_LOG_INFO(...) google::RawLogStub__(0, __VA_ARGS__)
-#endif // STRIP_LOG == 0
-
-#if STRIP_LOG <= 1
-#define RAW_LOG_WARNING(...) google::RawLog__(google::GLOG_WARNING,   \
-                                      __FILE__, __LINE__, __VA_ARGS__)
-#else
-#define RAW_LOG_WARNING(...) google::RawLogStub__(0, __VA_ARGS__)
-#endif // STRIP_LOG <= 1
-
-#if STRIP_LOG <= 2
-#define RAW_LOG_ERROR(...) google::RawLog__(google::GLOG_ERROR,       \
-                                    __FILE__, __LINE__, __VA_ARGS__)
-#else
-#define RAW_LOG_ERROR(...) google::RawLogStub__(0, __VA_ARGS__)
-#endif // STRIP_LOG <= 2
-
-#if STRIP_LOG <= 3
-#define RAW_LOG_FATAL(...) google::RawLog__(google::GLOG_FATAL,       \
-                                    __FILE__, __LINE__, __VA_ARGS__)
-#else
-#define RAW_LOG_FATAL(...) \
-  do { \
-    google::RawLogStub__(0, __VA_ARGS__);        \
-    exit(1); \
-  } while (0)
-#endif // STRIP_LOG <= 3
-
-// Similar to CHECK(condition) << message,
-// but for low-level modules: we use only RAW_LOG that does not allocate memory.
-// We do not want to provide args list here to encourage this usage:
-//   if (!cond)  RAW_LOG(FATAL, "foo ...", hard_to_compute_args);
-// so that the args are not computed when not needed.
-#define RAW_CHECK(condition, message)                                   \
-  do {                                                                  \
-    if (!(condition)) {                                                 \
-      RAW_LOG(FATAL, "Check %s failed: %s", #condition, message);       \
-    }                                                                   \
-  } while (0)
-
-// Debug versions of RAW_LOG and RAW_CHECK
-#ifndef NDEBUG
-
-#define RAW_DLOG(severity, ...) RAW_LOG(severity, __VA_ARGS__)
-#define RAW_DCHECK(condition, message) RAW_CHECK(condition, message)
-
-#else  // NDEBUG
-
-#define RAW_DLOG(severity, ...)                                 \
-  while (false)                                                 \
-    RAW_LOG(severity, __VA_ARGS__)
-#define RAW_DCHECK(condition, message) \
-  while (false) \
-    RAW_CHECK(condition, message)
-
-#endif  // NDEBUG
-
-// Stub log function used to work around for unused variable warnings when
-// building with STRIP_LOG > 0.
-static inline void RawLogStub__(int /* ignored */, ...) {
-}
-
-// Helper function to implement RAW_LOG and RAW_VLOG
-// Logs format... at "severity" level, reporting it
-// as called from file:line.
-// This does not allocate memory or acquire locks.
-GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity,
-                                   const char* file,
-                                   int line,
-                                   const char* format, ...)
-   ;
-
-}
-
-#endif  // BASE_RAW_LOGGING_H_
diff --git a/third_party/glog/src/windows/glog/stl_logging.h b/third_party/glog/src/windows/glog/stl_logging.h
deleted file mode 100755
index a97a908959..0000000000
--- a/third_party/glog/src/windows/glog/stl_logging.h
+++ /dev/null
@@ -1,224 +0,0 @@
-// This file is automatically generated from src/glog/stl_logging.h.in
-// using src/windows/preprocess.sh.
-// DO NOT EDIT!
-
-// Copyright (c) 2003, Google 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:
-//
-//     * 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.
-//
-// Stream output operators for STL containers; to be used for logging *only*.
-// Inclusion of this file lets you do:
-//
-// list<string> x;
-// LOG(INFO) << "data: " << x;
-// vector<int> v1, v2;
-// CHECK_EQ(v1, v2);
-//
-// If you want to use this header file with hash_compare maps or slist, you
-// need to define macros before including this file:
-//
-// - GLOG_STL_LOGGING_FOR_UNORDERED     - <unordered_map> and <unordered_set>
-// - GLOG_STL_LOGGING_FOR_TR1_UNORDERED - <tr1/unordered_(map|set)>
-// - GLOG_STL_LOGGING_FOR_EXT_HASH      - <ext/hash_(map|set)>
-// - GLOG_STL_LOGGING_FOR_EXT_SLIST     - <ext/slist>
-//
-
-#ifndef UTIL_GTL_STL_LOGGING_INL_H_
-#define UTIL_GTL_STL_LOGGING_INL_H_
-
-#if !1
-# error We do not support stl_logging for this compiler
-#endif
-
-#include <deque>
-#include <list>
-#include <map>
-#include <ostream>
-#include <set>
-#include <utility>
-#include <vector>
-
-#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
-# include <unordered_map>
-# include <unordered_set>
-#endif
-
-#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
-# include <tr1/unordered_map>
-# include <tr1/unordered_set>
-#endif
-
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-# include <ext/hash_set>
-# include <ext/hash_map>
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST
-# include <ext/slist>
-#endif
-
-// Forward declare these two, and define them after all the container streams
-// operators so that we can recurse from pair -> container -> container -> pair
-// properly.
-template<class First, class Second>
-std::ostream& operator<<(std::ostream& out, const std::pair<First, Second>& p);
-
-namespace google {
-
-template<class Iter>
-void PrintSequence(std::ostream& out, Iter begin, Iter end);
-
-}
-
-#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \
-template<class T1, class T2> \
-inline std::ostream& operator<<(std::ostream& out, \
-                                const Sequence<T1, T2>& seq) { \
-  google::PrintSequence(out, seq.begin(), seq.end()); \
-  return out; \
-}
-
-OUTPUT_TWO_ARG_CONTAINER(std::vector)
-OUTPUT_TWO_ARG_CONTAINER(std::deque)
-OUTPUT_TWO_ARG_CONTAINER(std::list)
-#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST
-OUTPUT_TWO_ARG_CONTAINER(__gnu_cxx::slist)
-#endif
-
-#undef OUTPUT_TWO_ARG_CONTAINER
-
-#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \
-template<class T1, class T2, class T3> \
-inline std::ostream& operator<<(std::ostream& out, \
-                                const Sequence<T1, T2, T3>& seq) { \
-  google::PrintSequence(out, seq.begin(), seq.end()); \
-  return out; \
-}
-
-OUTPUT_THREE_ARG_CONTAINER(std::set)
-OUTPUT_THREE_ARG_CONTAINER(std::multiset)
-
-#undef OUTPUT_THREE_ARG_CONTAINER
-
-#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \
-template<class T1, class T2, class T3, class T4> \
-inline std::ostream& operator<<(std::ostream& out, \
-                                const Sequence<T1, T2, T3, T4>& seq) { \
-  google::PrintSequence(out, seq.begin(), seq.end()); \
-  return out; \
-}
-
-OUTPUT_FOUR_ARG_CONTAINER(std::map)
-OUTPUT_FOUR_ARG_CONTAINER(std::multimap)
-#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
-OUTPUT_FOUR_ARG_CONTAINER(std::unordered_set)
-OUTPUT_FOUR_ARG_CONTAINER(std::unordered_multiset)
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
-OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_set)
-OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_multiset)
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_set)
-OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_multiset)
-#endif
-
-#undef OUTPUT_FOUR_ARG_CONTAINER
-
-#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \
-template<class T1, class T2, class T3, class T4, class T5> \
-inline std::ostream& operator<<(std::ostream& out, \
-                                const Sequence<T1, T2, T3, T4, T5>& seq) { \
-  google::PrintSequence(out, seq.begin(), seq.end()); \
-  return out; \
-}
-
-#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
-OUTPUT_FIVE_ARG_CONTAINER(std::unordered_map)
-OUTPUT_FIVE_ARG_CONTAINER(std::unordered_multimap)
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
-OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_map)
-OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_multimap)
-#endif
-#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
-OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_map)
-OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_multimap)
-#endif
-
-#undef OUTPUT_FIVE_ARG_CONTAINER
-
-template<class First, class Second>
-inline std::ostream& operator<<(std::ostream& out,
-                                const std::pair<First, Second>& p) {
-  out << '(' << p.first << ", " << p.second << ')';
-  return out;
-}
-
-namespace google {
-
-template<class Iter>
-inline void PrintSequence(std::ostream& out, Iter begin, Iter end) {
-  // Output at most 100 elements -- appropriate if used for logging.
-  for (int i = 0; begin != end && i < 100; ++i, ++begin) {
-    if (i > 0) out << ' ';
-    out << *begin;
-  }
-  if (begin != end) {
-    out << " ...";
-  }
-}
-
-}
-
-// Note that this is technically undefined behavior! We are adding things into
-// the std namespace for a reason though -- we are providing new operations on
-// types which are themselves defined with this namespace. Without this, these
-// operator overloads cannot be found via ADL. If these definitions are not
-// found via ADL, they must be #included before they're used, which requires
-// this header to be included before apparently independent other headers.
-//
-// For example, base/logging.h defines various template functions to implement
-// CHECK_EQ(x, y) and stream x and y into the log in the event the check fails.
-// It does so via the function template MakeCheckOpValueString:
-//   template<class T>
-//   void MakeCheckOpValueString(strstream* ss, const T& v) {
-//     (*ss) << v;
-//   }
-// Because 'glog/logging.h' is included before 'glog/stl_logging.h',
-// subsequent CHECK_EQ(v1, v2) for vector<...> typed variable v1 and v2 can only
-// find these operator definitions via ADL.
-//
-// Even this solution has problems -- it may pull unintended operators into the
-// namespace as well, allowing them to also be found via ADL, and creating code
-// that only works with a particular order of includes. Long term, we need to
-// move all of the *definitions* into namespace std, bet we need to ensure no
-// one references them first. This lets us take that step. We cannot define them
-// in both because that would create ambiguous overloads when both are found.
-namespace std { using ::operator<<; }
-
-#endif  // UTIL_GTL_STL_LOGGING_INL_H_
diff --git a/third_party/glog/src/windows/glog/vlog_is_on.h b/third_party/glog/src/windows/glog/vlog_is_on.h
deleted file mode 100755
index 409a4011b3..0000000000
--- a/third_party/glog/src/windows/glog/vlog_is_on.h
+++ /dev/null
@@ -1,133 +0,0 @@
-// This file is automatically generated from src/glog/vlog_is_on.h.in
-// using src/windows/preprocess.sh.
-// DO NOT EDIT!
-
-// Copyright (c) 1999, 2007, Google 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:
-//
-//     * 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.
-//
-// Author: Ray Sidney and many others
-//
-// Defines the VLOG_IS_ON macro that controls the variable-verbosity
-// conditional logging.
-//
-// It's used by VLOG and VLOG_IF in logging.h
-// and by RAW_VLOG in raw_logging.h to trigger the logging.
-//
-// It can also be used directly e.g. like this:
-//   if (VLOG_IS_ON(2)) {
-//     // do some logging preparation and logging
-//     // that can't be accomplished e.g. via just VLOG(2) << ...;
-//   }
-//
-// The truth value that VLOG_IS_ON(level) returns is determined by 
-// the three verbosity level flags:
-//   --v=<n>  Gives the default maximal active V-logging level;
-//            0 is the default.
-//            Normally positive values are used for V-logging levels.
-//   --vmodule=<str>  Gives the per-module maximal V-logging levels to override
-//                    the value given by --v.
-//                    E.g. "my_module=2,foo*=3" would change the logging level
-//                    for all code in source files "my_module.*" and "foo*.*"
-//                    ("-inl" suffixes are also disregarded for this matching).
-//
-// SetVLOGLevel helper function is provided to do limited dynamic control over
-// V-logging by overriding the per-module settings given via --vmodule flag.
-//
-// CAVEAT: --vmodule functionality is not available in non gcc compilers.
-//
-
-#ifndef BASE_VLOG_IS_ON_H_
-#define BASE_VLOG_IS_ON_H_
-
-#include "glog/log_severity.h"
-
-// Annoying stuff for windows -- makes sure clients can import these functions
-#ifndef GOOGLE_GLOG_DLL_DECL
-# if defined(_WIN32) && !defined(__CYGWIN__)
-#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
-# else
-#   define GOOGLE_GLOG_DLL_DECL
-# endif
-#endif
-
-#if defined(__GNUC__)
-// We emit an anonymous static int* variable at every VLOG_IS_ON(n) site.
-// (Normally) the first time every VLOG_IS_ON(n) site is hit,
-// we determine what variable will dynamically control logging at this site:
-// it's either FLAGS_v or an appropriate internal variable
-// matching the current source file that represents results of
-// parsing of --vmodule flag and/or SetVLOGLevel calls.
-#define VLOG_IS_ON(verboselevel)                                \
-  __extension__  \
-  ({ static google::int32* vlocal__ = &google::kLogSiteUninitialized;           \
-     google::int32 verbose_level__ = (verboselevel);                    \
-     (*vlocal__ >= verbose_level__) &&                          \
-     ((vlocal__ != &google::kLogSiteUninitialized) ||                   \
-      (google::InitVLOG3__(&vlocal__, &FLAGS_v,                         \
-                   __FILE__, verbose_level__))); })
-#else
-// GNU extensions not available, so we do not support --vmodule.
-// Dynamic value of FLAGS_v always controls the logging level.
-#define VLOG_IS_ON(verboselevel) (FLAGS_v >= (verboselevel))
-#endif
-
-// Set VLOG(_IS_ON) level for module_pattern to log_level.
-// This lets us dynamically control what is normally set by the --vmodule flag.
-// Returns the level that previously applied to module_pattern.
-// NOTE: To change the log level for VLOG(_IS_ON) sites
-//	 that have already executed after/during InitGoogleLogging,
-//	 one needs to supply the exact --vmodule pattern that applied to them.
-//       (If no --vmodule pattern applied to them
-//       the value of FLAGS_v will continue to control them.)
-extern GOOGLE_GLOG_DLL_DECL int SetVLOGLevel(const char* module_pattern,
-                                             int log_level);
-
-// Various declarations needed for VLOG_IS_ON above: =========================
-
-// Special value used to indicate that a VLOG_IS_ON site has not been
-// initialized.  We make this a large value, so the common-case check
-// of "*vlocal__ >= verbose_level__" in VLOG_IS_ON definition
-// passes in such cases and InitVLOG3__ is then triggered.
-extern google::int32 kLogSiteUninitialized;
-
-// Helper routine which determines the logging info for a particalur VLOG site.
-//   site_flag     is the address of the site-local pointer to the controlling
-//                 verbosity level
-//   site_default  is the default to use for *site_flag
-//   fname         is the current source file name
-//   verbose_level is the argument to VLOG_IS_ON
-// We will return the return value for VLOG_IS_ON
-// and if possible set *site_flag appropriately.
-extern GOOGLE_GLOG_DLL_DECL bool InitVLOG3__(
-    google::int32** site_flag,
-    google::int32* site_default,
-    const char* fname,
-    google::int32 verbose_level);
-
-#endif  // BASE_VLOG_IS_ON_H_
diff --git a/third_party/glog/src/windows/port.cc b/third_party/glog/src/windows/port.cc
deleted file mode 100755
index 19bda367c6..0000000000
--- a/third_party/glog/src/windows/port.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-/* Copyright (c) 2008, Google 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:
- * 
- *     * 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.
- *
- * ---
- * Author: Craig Silverstein
- * Copied from google-perftools and modified by Shinichiro Hamaji
- */
-
-#ifndef _WIN32
-# error You should only be including windows/port.cc in a windows environment!
-#endif
-
-#include "config.h"
-#include <stdarg.h>    // for va_list, va_start, va_end
-#include "port.h"
-
-// These call the windows _vsnprintf, but always NUL-terminate.
-int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
-  if (size == 0)        // not even room for a \0?
-    return -1;          // not what C99 says to do, but what windows does
-  str[size-1] = '\0';
-  return _vsnprintf(str, size-1, format, ap);
-}
-
-#ifndef HAVE_LOCALTIME_R
-struct tm* localtime_r(const time_t* timep, struct tm* result) {
-  localtime_s(result, timep);
-  return result;
-}
-#endif // not HAVE_LOCALTIME_R
-#ifndef HAVE_SNPRINTF
-int snprintf(char *str, size_t size, const char *format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  const int r = vsnprintf(str, size, format, ap);
-  va_end(ap);
-  return r;
-}
-#endif
diff --git a/third_party/glog/src/windows/port.h b/third_party/glog/src/windows/port.h
deleted file mode 100755
index 7b4b9c8545..0000000000
--- a/third_party/glog/src/windows/port.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/* Copyright (c) 2008, Google 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:
- * 
- *     * 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.
- *
- * ---
- * Author: Craig Silverstein
- * Copied from google-perftools and modified by Shinichiro Hamaji
- *
- * These are some portability typedefs and defines to make it a bit
- * easier to compile this code under VC++.
- *
- * Several of these are taken from glib:
- *    http://developer.gnome.org/doc/API/glib/glib-windows-compatability-functions.html
- */
-
-#ifndef CTEMPLATE_WINDOWS_PORT_H_
-#define CTEMPLATE_WINDOWS_PORT_H_
-
-#include "config.h"
-
-#ifdef _WIN32
-
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN  /* We always want minimal includes */
-#endif
-
-#include <windows.h>
-#include <winsock.h>         /* for gethostname */
-#include <io.h>              /* because we so often use open/close/etc */
-#include <direct.h>          /* for _getcwd() */
-#include <process.h>         /* for _getpid() */
-#include <stdio.h>           /* read in vsnprintf decl. before redifining it */
-#include <stdarg.h>          /* template_dictionary.cc uses va_copy */
-#include <string.h>          /* for _strnicmp(), strerror_s() */
-#include <time.h>            /* for localtime_s() */
-/* Note: the C++ #includes are all together at the bottom.  This file is
- * used by both C and C++ code, so we put all the C++ together.
- */
-
-#ifdef _MSC_VER
-
-/* 4244: otherwise we get problems when substracting two size_t's to an int
- * 4251: it's complaining about a private struct I've chosen not to dllexport
- * 4355: we use this in a constructor, but we do it safely
- * 4715: for some reason VC++ stopped realizing you can't return after abort()
- * 4800: we know we're casting ints/char*'s to bools, and we're ok with that
- * 4996: Yes, we're ok using "unsafe" functions like fopen() and strerror()
- * 4312: Converting uint32_t to a pointer when testing %p
- * 4267: also subtracting two size_t to int
- * 4722: Destructor never returns due to abort()
- */
-#pragma warning(disable:4244 4251 4355 4715 4800 4996 4267 4312 4722)
-
-/* file I/O */
-#define PATH_MAX 1024
-#define access  _access
-#define getcwd  _getcwd
-#define open    _open
-#define read    _read
-#define write   _write
-#define lseek   _lseek
-#define close   _close
-#define popen   _popen
-#define pclose  _pclose
-#define R_OK    04           /* read-only (for access()) */
-#define S_ISDIR(m)  (((m) & _S_IFMT) == _S_IFDIR)
-
-#define O_WRONLY _O_WRONLY
-#define O_CREAT _O_CREAT
-#define O_EXCL _O_EXCL
-
-#ifndef __MINGW32__
-enum { STDIN_FILENO = 0, STDOUT_FILENO = 1, STDERR_FILENO = 2 };
-#endif
-#define S_IRUSR S_IREAD
-#define S_IWUSR S_IWRITE
-
-/* Not quite as lightweight as a hard-link, but more than good enough for us. */
-#define link(oldpath, newpath)  CopyFileA(oldpath, newpath, false)
-
-#define strcasecmp   _stricmp
-#define strncasecmp  _strnicmp
-
-/* In windows-land, hash<> is called hash_compare<> (from xhash.h) */
-/* VC11 provides std::hash */
-#if defined(_MSC_VER) && (_MSC_VER < 1700)
-#define hash  hash_compare
-#endif
-
-/* Sleep is in ms, on windows */
-#define sleep(secs)  Sleep((secs) * 1000)
-
-/* We can't just use _vsnprintf and _snprintf as drop-in-replacements,
- * because they don't always NUL-terminate. :-(  We also can't use the
- * name vsnprintf, since windows defines that (but not snprintf (!)).
- */
-#ifndef HAVE_SNPRINTF
-extern int GOOGLE_GLOG_DLL_DECL snprintf(char *str, size_t size,
-                                       const char *format, ...);
-#endif
-extern int GOOGLE_GLOG_DLL_DECL safe_vsnprintf(char *str, size_t size,
-                          const char *format, va_list ap);
-#define vsnprintf(str, size, format, ap)  safe_vsnprintf(str, size, format, ap)
-#ifndef va_copy
-#define va_copy(dst, src)  (dst) = (src)
-#endif
-
-/* Windows doesn't support specifying the number of buckets as a
- * hash_map constructor arg, so we leave this blank.
- */
-#define CTEMPLATE_SMALL_HASHTABLE
-
-#define DEFAULT_TEMPLATE_ROOTDIR  ".."
-
-// ----------------------------------- SYSTEM/PROCESS
-typedef int pid_t;
-#define getpid  _getpid
-
-#endif  // _MSC_VER
-
-// ----------------------------------- THREADS
-#if defined(HAVE_PTHREAD)
-# include <pthread.h>
-#else // no PTHREAD
-typedef DWORD pthread_t;
-typedef DWORD pthread_key_t;
-typedef LONG pthread_once_t;
-enum { PTHREAD_ONCE_INIT = 0 };   // important that this be 0! for SpinLock
-#define pthread_self  GetCurrentThreadId
-#define pthread_equal(pthread_t_1, pthread_t_2)  ((pthread_t_1)==(pthread_t_2))
-#endif // HAVE_PTHREAD
-
-#ifndef HAVE_LOCALTIME_R
-extern GOOGLE_GLOG_DLL_DECL struct tm* localtime_r(const time_t* timep, struct tm* result);
-#endif // not HAVE_LOCALTIME_R
-
-inline char* strerror_r(int errnum, char* buf, size_t buflen) {
-  strerror_s(buf, buflen, errnum);
-  return buf;
-}
-
-#ifndef __cplusplus
-/* I don't see how to get inlining for C code in MSVC.  Ah well. */
-#define inline
-#endif
-
-#endif  /* _WIN32 */
-
-#endif  /* CTEMPLATE_WINDOWS_PORT_H_ */
diff --git a/third_party/glog/src/windows/preprocess.sh b/third_party/glog/src/windows/preprocess.sh
deleted file mode 100755
index c35e92913b..0000000000
--- a/third_party/glog/src/windows/preprocess.sh
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/bin/sh
-
-# Copyright (c) 2008, Google 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:
-#
-#     * 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.
-
-# ---
-# Author: Craig Silverstein
-# Copied from google-perftools and modified by Shinichiro Hamaji
-#
-# This script is meant to be run at distribution-generation time, for
-# instance by autogen.sh.  It does some of the work configure would
-# normally do, for windows systems.  In particular, it expands all the
-# @...@ variables found in .in files, and puts them here, in the windows
-# directory.
-#
-# This script should be run before any new release.
-
-if [ -z "$1" ]; then
-   echo "USAGE: $0 <src/ directory>"
-   exit 1
-fi
-
-DLLDEF_MACRO_NAME="GLOG_DLL_DECL"
-
-# The text we put in every .h files we create.  As a courtesy, we'll
-# include a helpful comment for windows users as to how to use
-# GLOG_DLL_DECL.  Apparently sed expands \n into a newline.  Good!
-DLLDEF_DEFINES="\
-// NOTE: if you are statically linking the template library into your binary\n\
-// (rather than using the template .dll), set '/D $DLLDEF_MACRO_NAME='\n\
-// as a compiler flag in your project file to turn off the dllimports.\n\
-#ifndef $DLLDEF_MACRO_NAME\n\
-# define $DLLDEF_MACRO_NAME  __declspec(dllimport)\n\
-#endif"
-
-# Read all the windows config info into variables
-# In order for the 'set' to take, this requires putting all in a subshell.
-(
-  while read define varname value; do
-    [ "$define" != "#define" ] && continue
-    eval "$varname='$value'"
-  done
-
-  # Process all the .in files in the "glog" subdirectory
-  mkdir -p "$1/windows/glog"
-  for file in `echo "$1"/glog/*.in`; do
-     echo "Processing $file"
-     outfile="$1/windows/glog/`basename $file .in`"
-
-     echo "\
-// This file is automatically generated from $file
-// using src/windows/preprocess.sh.
-// DO NOT EDIT!
-" > "$outfile"
-     # Besides replacing @...@, we also need to turn on dllimport
-     # We also need to replace hash by hash_compare (annoying we hard-code :-( )
-     sed -e "s!@ac_windows_dllexport@!$DLLDEF_MACRO_NAME!g" \
-         -e "s!@ac_windows_dllexport_defines@!$DLLDEF_DEFINES!g" \
-         -e "s!@ac_cv_cxx_hash_map@!$HASH_MAP_H!g" \
-         -e "s!@ac_cv_cxx_hash_namespace@!$HASH_NAMESPACE!g" \
-         -e "s!@ac_cv_cxx_hash_set@!$HASH_SET_H!g" \
-         -e "s!@ac_cv_have_stdint_h@!0!g" \
-         -e "s!@ac_cv_have_systypes_h@!0!g" \
-         -e "s!@ac_cv_have_inttypes_h@!0!g" \
-         -e "s!@ac_cv_have_unistd_h@!0!g" \
-         -e "s!@ac_cv_have_uint16_t@!0!g" \
-         -e "s!@ac_cv_have_u_int16_t@!0!g" \
-         -e "s!@ac_cv_have___uint16@!1!g" \
-         -e "s!@ac_cv_have_libgflags@!0!g" \
-         -e "s!@ac_cv_have___builtin_expect@!0!g" \
-         -e "s!@ac_cv_cxx_using_operator@!1!g" \
-         -e "s!@ac_cv___attribute___noreturn@!__declspec(noreturn)!g" \
-         -e "s!@ac_cv___attribute___noinline@!!g" \
-         -e "s!@ac_cv___attribute___printf_4_5@!!g" \
-         -e "s!@ac_google_attribute@!${HAVE___ATTRIBUTE__:-0}!g" \
-         -e "s!@ac_google_end_namespace@!$_END_GOOGLE_NAMESPACE_!g" \
-         -e "s!@ac_google_namespace@!$GOOGLE_NAMESPACE!g" \
-         -e "s!@ac_google_start_namespace@!$_START_GOOGLE_NAMESPACE_!g" \
-         -e "s!@ac_htmlparser_namespace@!$HTMLPARSER_NAMESPACE!g" \
-         -e "s!\\bhash\\b!hash_compare!g" \
-         "$file" >> "$outfile"
-  done
-) < "$1/windows/config.h"
-
-# log_severity.h isn't a .in file.
-echo "\
-// This file is automatically generated from $1/glog/log_severity.h
-// using src/windows/preprocess.sh.
-// DO NOT EDIT!
-" > "$1/windows/glog/log_severity.h"
-cat "$1/glog/log_severity.h" >> "$1/windows/glog/log_severity.h"
-
-echo "DONE"
diff --git a/third_party/glog/toolchains/.gitignore b/third_party/glog/toolchains/.gitignore
deleted file mode 100644
index 13489ce3b5..0000000000
--- a/third_party/glog/toolchains/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# allow toolchain files to be tracked by git
-!*.cmake
diff --git a/third_party/glog/toolchains/clang-cxx17.cmake b/third_party/glog/toolchains/clang-cxx17.cmake
deleted file mode 100644
index 09d077a38d..0000000000
--- a/third_party/glog/toolchains/clang-cxx17.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# Sample toolchain file for building with gcc compiler
-#
-# Typical usage:
-#    *) cmake -H. -B_build -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/gcc.cmake"
-
-# set compiler
-set(CMAKE_C_COMPILER clang)
-set(CMAKE_CXX_COMPILER clang++)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/third_party/glog/toolchains/gcc-cxx11.cmake b/third_party/glog/toolchains/gcc-cxx11.cmake
deleted file mode 100644
index da1ab5778a..0000000000
--- a/third_party/glog/toolchains/gcc-cxx11.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# Sample toolchain file for building with gcc compiler
-#
-# Typical usage:
-#    *) cmake -H. -B_build -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/gcc.cmake"
-
-# set compiler
-set(CMAKE_C_COMPILER gcc)
-set(CMAKE_CXX_COMPILER g++)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/third_party/glog/toolchains/gcc-cxx17.cmake b/third_party/glog/toolchains/gcc-cxx17.cmake
deleted file mode 100644
index 1b834f1201..0000000000
--- a/third_party/glog/toolchains/gcc-cxx17.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# Sample toolchain file for building with gcc compiler
-#
-# Typical usage:
-#    *) cmake -H. -B_build -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/gcc.cmake"
-
-# set compiler
-set(CMAKE_C_COMPILER gcc)
-set(CMAKE_CXX_COMPILER g++)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/third_party/glog/toolchains/gcc-cxx98.cmake b/third_party/glog/toolchains/gcc-cxx98.cmake
deleted file mode 100644
index 5697ab252a..0000000000
--- a/third_party/glog/toolchains/gcc-cxx98.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# Sample toolchain file for building with gcc compiler
-#
-# Typical usage:
-#    *) cmake -H. -B_build -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/gcc.cmake"
-
-# set compiler
-set(CMAKE_C_COMPILER gcc)
-set(CMAKE_CXX_COMPILER g++)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 98)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/third_party/glog/toolchains/gcc-gnuxx11.cmake b/third_party/glog/toolchains/gcc-gnuxx11.cmake
deleted file mode 100644
index 91f0e4a3ce..0000000000
--- a/third_party/glog/toolchains/gcc-gnuxx11.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# Sample toolchain file for building with gcc compiler
-#
-# Typical usage:
-#    *) cmake -H. -B_build -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/gcc.cmake"
-
-# set compiler
-set(CMAKE_C_COMPILER gcc)
-set(CMAKE_CXX_COMPILER g++)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS ON)
diff --git a/third_party/glog/toolchains/linux-mingw-w64-cxx11.cmake b/third_party/glog/toolchains/linux-mingw-w64-cxx11.cmake
deleted file mode 100644
index 9c6e126a68..0000000000
--- a/third_party/glog/toolchains/linux-mingw-w64-cxx11.cmake
+++ /dev/null
@@ -1,33 +0,0 @@
-# Sample toolchain file for building for Windows from an Ubuntu Linux system.
-#
-# Typical usage:
-#    *) install cross compiler: `sudo apt-get install mingw-w64`
-#    *) cmake -H. -B_build_mingw -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/linux-mingw-w64.cmake"
-
-set(CMAKE_SYSTEM_NAME Windows)
-set(CMAKE_SYSTEM_PROCESSOR x86_64)
-set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
-
-# set compiler
-set(CMAKE_C_COMPILER   ${TOOLCHAIN_PREFIX}-gcc)
-set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
-set(CMAKE_RC_COMPILER  ${TOOLCHAIN_PREFIX}-windres)
-
-# target environment on the build host system
-#   set 1st to dir with the cross compiler's C/C++ headers/libs
-set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
-
-# modify default behavior of FIND_XXX() commands to
-# search for headers/libs in the target environment and
-# search for programs in the build host environment
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-
-# use emulator for `try_run` calls
-set(CMAKE_CROSSCOMPILING_EMULATOR wine64)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/third_party/glog/toolchains/linux-mingw-w64-cxx17.cmake b/third_party/glog/toolchains/linux-mingw-w64-cxx17.cmake
deleted file mode 100644
index 1c363f10eb..0000000000
--- a/third_party/glog/toolchains/linux-mingw-w64-cxx17.cmake
+++ /dev/null
@@ -1,33 +0,0 @@
-# Sample toolchain file for building for Windows from an Ubuntu Linux system.
-#
-# Typical usage:
-#    *) install cross compiler: `sudo apt-get install mingw-w64`
-#    *) cmake -H. -B_build_mingw -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/linux-mingw-w64.cmake"
-
-set(CMAKE_SYSTEM_NAME Windows)
-set(CMAKE_SYSTEM_PROCESSOR x86_64)
-set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
-
-# set compiler
-set(CMAKE_C_COMPILER   ${TOOLCHAIN_PREFIX}-gcc)
-set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
-set(CMAKE_RC_COMPILER  ${TOOLCHAIN_PREFIX}-windres)
-
-# target environment on the build host system
-#   set 1st to dir with the cross compiler's C/C++ headers/libs
-set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
-
-# modify default behavior of FIND_XXX() commands to
-# search for headers/libs in the target environment and
-# search for programs in the build host environment
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-
-# use emulator for `try_run` calls
-set(CMAKE_CROSSCOMPILING_EMULATOR wine64)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/third_party/glog/toolchains/linux-mingw-w64-gnuxx11.cmake b/third_party/glog/toolchains/linux-mingw-w64-gnuxx11.cmake
deleted file mode 100644
index 99029649df..0000000000
--- a/third_party/glog/toolchains/linux-mingw-w64-gnuxx11.cmake
+++ /dev/null
@@ -1,33 +0,0 @@
-# Sample toolchain file for building for Windows from an Ubuntu Linux system.
-#
-# Typical usage:
-#    *) install cross compiler: `sudo apt-get install mingw-w64`
-#    *) cmake -H. -B_build_mingw -DCMAKE_TOOLCHAIN_FILE="${PWD}/toolchains/linux-mingw-w64.cmake"
-
-set(CMAKE_SYSTEM_NAME Windows)
-set(CMAKE_SYSTEM_PROCESSOR x86_64)
-set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
-
-# set compiler
-set(CMAKE_C_COMPILER   ${TOOLCHAIN_PREFIX}-gcc)
-set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
-set(CMAKE_RC_COMPILER  ${TOOLCHAIN_PREFIX}-windres)
-
-# target environment on the build host system
-#   set 1st to dir with the cross compiler's C/C++ headers/libs
-set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
-
-# modify default behavior of FIND_XXX() commands to
-# search for headers/libs in the target environment and
-# search for programs in the build host environment
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-
-# use emulator for `try_run` calls
-set(CMAKE_CROSSCOMPILING_EMULATOR wine64)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS ON)
diff --git a/third_party/glog/toolchains/mingw-cxx11.cmake b/third_party/glog/toolchains/mingw-cxx11.cmake
deleted file mode 100644
index 873f623f60..0000000000
--- a/third_party/glog/toolchains/mingw-cxx11.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# Sample toolchain file for building with gcc compiler
-#
-# Typical usage:
-#    *) cmake -H. -B_build -DCMAKE_TOOLCHAIN_FILE="%cd%\toolchains\mingw.cmake"
-
-# set compiler
-set(CMAKE_C_COMPILER gcc)
-set(CMAKE_CXX_COMPILER g++)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/third_party/glog/toolchains/mingw-cxx17.cmake b/third_party/glog/toolchains/mingw-cxx17.cmake
deleted file mode 100644
index c0c89cc459..0000000000
--- a/third_party/glog/toolchains/mingw-cxx17.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# Sample toolchain file for building with gcc compiler
-#
-# Typical usage:
-#    *) cmake -H. -B_build -DCMAKE_TOOLCHAIN_FILE="%cd%\toolchains\mingw.cmake"
-
-# set compiler
-set(CMAKE_C_COMPILER gcc)
-set(CMAKE_CXX_COMPILER g++)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/third_party/glog/toolchains/mingw-gnuxx11.cmake b/third_party/glog/toolchains/mingw-gnuxx11.cmake
deleted file mode 100644
index 66337050c9..0000000000
--- a/third_party/glog/toolchains/mingw-gnuxx11.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# Sample toolchain file for building with gcc compiler
-#
-# Typical usage:
-#    *) cmake -H. -B_build -DCMAKE_TOOLCHAIN_FILE="%cd%\toolchains\mingw.cmake"
-
-# set compiler
-set(CMAKE_C_COMPILER gcc)
-set(CMAKE_CXX_COMPILER g++)
-
-# set c++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS YES)
diff --git a/third_party/glog/toolchains/vs-14-2015-sdk-8-1.cmake b/third_party/glog/toolchains/vs-14-2015-sdk-8-1.cmake
deleted file mode 100644
index 463bee0523..0000000000
--- a/third_party/glog/toolchains/vs-14-2015-sdk-8-1.cmake
+++ /dev/null
@@ -1,2 +0,0 @@
-# set c++ standard
-set(CMAKE_SYSTEM_VERSION 8.1)
diff --git a/third_party/glog/toolchains/vs-14-2015-win64.cmake b/third_party/glog/toolchains/vs-14-2015-win64.cmake
deleted file mode 100644
index 00beda25ef..0000000000
--- a/third_party/glog/toolchains/vs-14-2015-win64.cmake
+++ /dev/null
@@ -1 +0,0 @@
-# dummy, nothing extra to set
diff --git a/third_party/glog/toolchains/vs-15-2017-win64-cxx17.cmake b/third_party/glog/toolchains/vs-15-2017-win64-cxx17.cmake
deleted file mode 100644
index 5e4e936872..0000000000
--- a/third_party/glog/toolchains/vs-15-2017-win64-cxx17.cmake
+++ /dev/null
@@ -1,2 +0,0 @@
-# set c++ standard
-set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /std:c++17")
diff --git a/third_party/glog/toolchains/vs-15-2017-win64.cmake b/third_party/glog/toolchains/vs-15-2017-win64.cmake
deleted file mode 100644
index 00beda25ef..0000000000
--- a/third_party/glog/toolchains/vs-15-2017-win64.cmake
+++ /dev/null
@@ -1 +0,0 @@
-# dummy, nothing extra to set
diff --git a/third_party/gopkgs/github.com/charmbracelet/bubbletea/default.nix b/third_party/gopkgs/github.com/charmbracelet/bubbletea/default.nix
index 75eb5402c1..8dc25bd918 100644
--- a/third_party/gopkgs/github.com/charmbracelet/bubbletea/default.nix
+++ b/third_party/gopkgs/github.com/charmbracelet/bubbletea/default.nix
@@ -3,15 +3,17 @@
 depot.nix.buildGo.external {
   path = "github.com/charmbracelet/bubbletea";
   src =
-    let gitSrc = pkgs.fetchFromGitHub {
+    let
+      gitSrc = pkgs.fetchFromGitHub {
         owner = "charmbracelet";
         repo = "bubbletea";
         rev = "v0.13.1";
         sha256 = "0yf2fjkvx8ym9n6f3qp2z7sxs0qsfpj148sfvbrp38k67s3h20cs";
       };
-    # The examples/ directory is fairly extensive,
-    # but it also adds most of the dependencies.
-    in pkgs.runCommand gitSrc.name {} ''
+      # The examples/ directory is fairly extensive,
+      # but it also adds most of the dependencies.
+    in
+    pkgs.runCommand gitSrc.name { } ''
       mkdir -p $out
       ln -s "${gitSrc}"/* $out
       rm -r $out/examples
diff --git a/third_party/grpc/default.nix b/third_party/grpc/default.nix
deleted file mode 100644
index 2ef67d4028..0000000000
--- a/third_party/grpc/default.nix
+++ /dev/null
@@ -1,13 +0,0 @@
-{ depot, pkgs, ... }:
-
-(pkgs.grpc.override {
-  protobuf = depot.third_party.protobuf;
-  stdenv = pkgs.fullLlvm11Stdenv;
-  abseil-cpp = depot.third_party.abseil_cpp;
-  re2 = depot.third_party.re2;
-}).overrideAttrs(orig: rec {
-  cmakeFlags = orig.cmakeFlags ++ [
-    "-DCMAKE_CXX_STANDARD_REQUIRED=ON"
-    "-DCMAKE_CXX_STANDARD=17"
-  ];
-})
diff --git a/third_party/gtest/default.nix b/third_party/gtest/default.nix
deleted file mode 100644
index 70516853ed..0000000000
--- a/third_party/gtest/default.nix
+++ /dev/null
@@ -1,12 +0,0 @@
-{ pkgs, ... }:
-
-(pkgs.gtest.override {
-  stdenv = pkgs.fullLlvm11Stdenv;
-}).overrideAttrs(_: {
-  src = pkgs.fetchFromGitHub {
-    owner = "google";
-    repo = "googletest";
-    rev = "9dce5e5d878176dc0054ef381f5c6e705f43ef99";
-    sha256 = "05xi61j7j251dzkgk9965lqpbacsy44iblzql941kw9d4nk0q6jl";
-  };
-})
diff --git a/third_party/hii/OWNERS b/third_party/hii/OWNERS
index a742d0d22b..a640227914 100644
--- a/third_party/hii/OWNERS
+++ b/third_party/hii/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - Profpatsch
+Profpatsch
diff --git a/third_party/impermanence/default.nix b/third_party/impermanence/default.nix
deleted file mode 100644
index 6ef72e4943..0000000000
--- a/third_party/impermanence/default.nix
+++ /dev/null
@@ -1,12 +0,0 @@
-# NixOS modules for systems with ephemeral root disks.
-#
-# https://github.com/nix-community/impermanence
-
-{ pkgs, ... }:
-
-pkgs.fetchFromGitHub {
-  owner = "nix-community";
-  repo = "impermanence";
-  rev = "58558845bc68dcf2bb32caa80564f7fe3f6cbc61";
-  sha256 = "10z3g4knkvq838zbfq71pkfyl8cffrpavna448wf5mjscycp0gnv";
-}
diff --git a/third_party/irccat/default.nix b/third_party/irccat/default.nix
index 3181fd2067..c5d7a5f6df 100644
--- a/third_party/irccat/default.nix
+++ b/third_party/irccat/default.nix
@@ -5,7 +5,7 @@ pkgs.buildGoModule rec {
   pname = "irccat";
   version = "20201108";
   meta.license = lib.licenses.gpl3;
-  vendorSha256 = "06a985y4alw1rsghgmhfyczns6klz7bbkfn5mnqc9fdfclgg4s3r";
+  vendorHash = "sha256:06a985y4alw1rsghgmhfyczns6klz7bbkfn5mnqc9fdfclgg4s3r";
 
   src = pkgs.fetchFromGitHub {
     owner = "irccloud";
diff --git a/third_party/josh/default.nix b/third_party/josh/default.nix
deleted file mode 100644
index 3a4e494e5f..0000000000
--- a/third_party/josh/default.nix
+++ /dev/null
@@ -1,25 +0,0 @@
-# https://github.com/esrlabs/josh
-{ depot, pkgs, ... }:
-
-let
-  src = pkgs.fetchFromGitHub {
-    owner = "esrlabs";
-    repo = "josh";
-    rev = "69dc986e506ba5631c8bbf52835da076a18ec8dc";
-    hash = "sha256:0ybc6ivjkm7bchaszs9lhbl1gbjnyhwq7a3vw6jml3ama84l52lb";
-  };
-in depot.third_party.naersk.buildPackage {
-  inherit src;
-
-  buildInputs = with pkgs; [
-    libgit2
-    openssl
-    pkgconfig
-  ];
-
-  cargoBuildOptions = x: x ++ [
-    "-p" "josh"
-    "-p" "josh-proxy"
-    "-p" "josh-ui"
-  ];
-}
diff --git a/third_party/lisp/OWNERS b/third_party/lisp/OWNERS
index 2d7f7e237b..6536baf505 100644
--- a/third_party/lisp/OWNERS
+++ b/third_party/lisp/OWNERS
@@ -1,5 +1,2 @@
-# -*- mode: yaml; -*-
-inherited: true
-owners:
-  - eta
-  - grfn
+eta
+aspen
diff --git a/third_party/lisp/bordeaux-threads.nix b/third_party/lisp/bordeaux-threads.nix
index 17ee6e539e..8a2e099508 100644
--- a/third_party/lisp/bordeaux-threads.nix
+++ b/third_party/lisp/bordeaux-threads.nix
@@ -5,7 +5,8 @@
 let
   src = with pkgs; srcOnly lispPackages.bordeaux-threads;
   getSrc = f: "${src}/src/${f}";
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "bordeaux-threads";
   deps = [ depot.third_party.lisp.alexandria ];
 
diff --git a/third_party/lisp/cl-change-case.nix b/third_party/lisp/cl-change-case.nix
new file mode 100644
index 0000000000..b66368a9b6
--- /dev/null
+++ b/third_party/lisp/cl-change-case.nix
@@ -0,0 +1,22 @@
+{ depot, pkgs, ... }:
+
+let src = with pkgs; srcOnly lispPackages.cl-change-case;
+in depot.nix.buildLisp.library {
+  name = "cl-change-case";
+
+  deps = with depot.third_party.lisp; [ cl-ppcre cl-ppcre.unicode ];
+
+  srcs = [ (src + "/src/cl-change-case.lisp") ];
+
+  tests = {
+    name = "cl-change-case-tests";
+    srcs = [ (src + "/t/cl-change-case.lisp") ];
+    deps = [
+      depot.third_party.lisp.fiveam
+    ];
+
+    expression = ''
+      (5am:run! :cl-change-case)
+    '';
+  };
+}
diff --git a/third_party/lisp/cl-fad.nix b/third_party/lisp/cl-fad.nix
index ec1170bf16..9350abe2e3 100644
--- a/third_party/lisp/cl-fad.nix
+++ b/third_party/lisp/cl-fad.nix
@@ -1,5 +1,5 @@
 # Portable pathname library
-{ depot, pkgs, ...}:
+{ depot, pkgs, ... }:
 
 with depot.nix;
 
@@ -18,7 +18,7 @@ in buildLisp.library {
   srcs = map (f: src + ("/" + f)) [
     "packages.lisp"
   ] ++ [
-    { ccl =  "${src}/openmcl.lisp"; }
+    { ccl = "${src}/openmcl.lisp"; }
   ] ++ map (f: src + ("/" + f)) [
     "fad.lisp"
     "path.lisp"
diff --git a/third_party/lisp/cl-json.nix b/third_party/lisp/cl-json.nix
index 5d1450a3e9..6b82fac772 100644
--- a/third_party/lisp/cl-json.nix
+++ b/third_party/lisp/cl-json.nix
@@ -4,25 +4,50 @@
 let
   inherit (depot.nix) buildLisp;
 
+  # https://github.com/sharplispers/cl-json/pull/12/
   src = pkgs.fetchFromGitHub {
-    owner = "hankhero";
+    owner = "sternenseemann";
     repo = "cl-json";
-    rev = "6dfebb9540bfc3cc33582d0c03c9ec27cb913e79";
-    sha256 = "0fx3m3x3s5ji950yzpazz4s0img3l6b3d6l3jrfjv0lr702496lh";
+    rev = "c059bec94e28a11102a994d6949e2e52764f21fd";
+    sha256 = "0l07syw1b1x2zi8kj4iph3rf6vi6c16b7fk69iv7x27wrdsr1qwj";
   };
-in buildLisp.library {
+
+  getSrcs = subdir: map (f: src + ("/" + subdir + "/" + f));
+in
+buildLisp.library {
   name = "cl-json";
   deps = [ (buildLisp.bundled "asdf") ];
 
   srcs = [ "${src}/cl-json.asd" ] ++
-  (map (f: src + ("/src/" + f)) [
-    "package.lisp"
-    "common.lisp"
-    "objects.lisp"
-    "camel-case.lisp"
-    "decoder.lisp"
-    "encoder.lisp"
-    "utils.lisp"
-    "json-rpc.lisp"
-  ]);
+    (getSrcs "src" [
+      "package.lisp"
+      "common.lisp"
+      "objects.lisp"
+      "camel-case.lisp"
+      "decoder.lisp"
+      "encoder.lisp"
+      "utils.lisp"
+      "json-rpc.lisp"
+    ]);
+
+  tests = {
+    deps = [
+      depot.third_party.lisp.cl-unicode
+      depot.third_party.lisp.fiveam
+    ];
+    srcs = [
+      # CLOS tests are broken upstream as well
+      # https://github.com/sharplispers/cl-json/issues/11
+      (pkgs.writeText "no-clos-tests.lisp" ''
+        (replace *features* (delete :cl-json-clos *features*))
+      '')
+    ] ++ getSrcs "t" [
+      "package.lisp"
+      "testencoder.lisp"
+      "testdecoder.lisp"
+      "testmisc.lisp"
+    ];
+
+    expression = "(fiveam:run! 'json-test::json)";
+  };
 }
diff --git a/third_party/lisp/cl-plus-ssl.nix b/third_party/lisp/cl-plus-ssl.nix
index bec5d5b3a2..dc0a95944f 100644
--- a/third_party/lisp/cl-plus-ssl.nix
+++ b/third_party/lisp/cl-plus-ssl.nix
@@ -3,12 +3,14 @@
 
 with depot.nix;
 
-let src = pkgs.fetchgit {
-  url = "https://github.com/cl-plus-ssl/cl-plus-ssl.git";
-  rev = "29081992f6d7b4e3aa2c5eeece4cd92b745071f4";
-  hash = "sha256:16lyrixl98b7vy29dbbzkbq0xaz789350dajrr1gdny5i55rkjq0";
-};
-in buildLisp.library {
+let
+  src = pkgs.fetchgit {
+    url = "https://github.com/cl-plus-ssl/cl-plus-ssl.git";
+    rev = "29081992f6d7b4e3aa2c5eeece4cd92b745071f4";
+    hash = "sha256:16lyrixl98b7vy29dbbzkbq0xaz789350dajrr1gdny5i55rkjq0";
+  };
+in
+buildLisp.library {
   name = "cl-plus-ssl";
   deps = with depot.third_party.lisp; [
     alexandria
diff --git a/third_party/lisp/cl-ppcre.nix b/third_party/lisp/cl-ppcre.nix
index 561e306191..7cb99db639 100644
--- a/third_party/lisp/cl-ppcre.nix
+++ b/third_party/lisp/cl-ppcre.nix
@@ -24,4 +24,16 @@ in depot.nix.buildLisp.library {
     "scanner.lisp"
     "api.lisp"
   ];
+
+  passthru = {
+    unicode = depot.nix.buildLisp.library {
+      name = "cl-ppcre-unicode";
+      deps = with depot.third_party.lisp; [ cl-ppcre cl-unicode ];
+
+      srcs = map (f: src + ("/cl-ppcre-unicode/" + f)) [
+        "packages.lisp"
+        "resolver.lisp"
+      ];
+    };
+  };
 }
diff --git a/third_party/lisp/cl-unicode.nix b/third_party/lisp/cl-unicode.nix
index 5fff1fbe6b..815d99c2dc 100644
--- a/third_party/lisp/cl-unicode.nix
+++ b/third_party/lisp/cl-unicode.nix
@@ -40,7 +40,7 @@ let
       "char-info.lisp"
       "read.lisp"
     ]) ++ [
-      (runCommand "dump.lisp" {} ''
+      (runCommand "dump.lisp" { } ''
         substitute ${src}/build/dump.lisp $out \
           --replace ':defaults *this-file*' ":defaults (uiop:getcwd)"
       '')
@@ -55,7 +55,7 @@ let
   };
 
 
-  generated = runCommand "cl-unicode-generated" {} ''
+  generated = runCommand "cl-unicode-generated" { } ''
     mkdir -p $out/build
     mkdir -p $out/test
     cd $out/build
@@ -66,7 +66,7 @@ let
 in
 depot.nix.buildLisp.library {
   name = "cl-unicode";
-  deps = [cl-unicode-base];
+  deps = [ cl-unicode-base ];
   srcs = [
     "${src}/conditions.lisp"
     "${generated}/lists.lisp"
diff --git a/third_party/lisp/cl-yacc.nix b/third_party/lisp/cl-yacc.nix
index d2ceb81103..b40d5d0601 100644
--- a/third_party/lisp/cl-yacc.nix
+++ b/third_party/lisp/cl-yacc.nix
@@ -1,12 +1,14 @@
 { depot, pkgs, ... }:
 
-let src = pkgs.fetchFromGitHub {
+let
+  src = pkgs.fetchFromGitHub {
     owner = "jech";
     repo = "cl-yacc";
     rev = "1334f5469251ffb3f8738a682dc8ee646cb26635";
     sha256 = "16946pzf8vvadnyfayvj8rbh4zjzw90h0azz2qk1mxrvhh5wklib";
   };
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "cl-yacc";
 
   srcs = map (f: src + ("/" + f)) [
diff --git a/third_party/lisp/closure-common.nix b/third_party/lisp/closure-common.nix
index 0856fc9e52..7f7f79f855 100644
--- a/third_party/lisp/closure-common.nix
+++ b/third_party/lisp/closure-common.nix
@@ -3,7 +3,8 @@
 let
   src = with pkgs; srcOnly lispPackages.closure-common;
   getSrcs = builtins.map (p: "${src}/${p}");
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "closure-common";
 
   # closure-common.asd surpresses some warnings otherwise breaking
@@ -18,12 +19,12 @@ in depot.nix.buildLisp.library {
     "closure-common.asd"
     "package.lisp"
     "definline.lisp"
-    "characters.lisp"     #+rune-is-character
+    "characters.lisp" #+rune-is-character
     "syntax.lisp"
-    "encodings.lisp"      #-x&y-streams-are-stream
+    "encodings.lisp" #-x&y-streams-are-stream
     "encodings-data.lisp" #-x&y-streams-are-stream
-    "xstream.lisp"        #-x&y-streams-are-stream
-    "ystream.lisp"        #-x&y-streams-are-stream
+    "xstream.lisp" #-x&y-streams-are-stream
+    "ystream.lisp" #-x&y-streams-are-stream
     "hax.lisp"
   ];
 
diff --git a/third_party/lisp/easy-routes.nix b/third_party/lisp/easy-routes.nix
index 93aed8a667..5caf8261fa 100644
--- a/third_party/lisp/easy-routes.nix
+++ b/third_party/lisp/easy-routes.nix
@@ -9,7 +9,8 @@ let
     sha256 = "06lnipwc6mmg0v5gybcnr7wn5xmn5xfd1gs19vbima777245bfka";
   };
 
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "easy-routes";
   deps = with depot.third_party.lisp; [
     hunchentoot
diff --git a/third_party/lisp/flexi-streams.nix b/third_party/lisp/flexi-streams.nix
index 4b88809024..a6a06d4ad0 100644
--- a/third_party/lisp/flexi-streams.nix
+++ b/third_party/lisp/flexi-streams.nix
@@ -28,6 +28,6 @@ in depot.nix.buildLisp.library {
     "input.lisp"
     "io.lisp"
     "strings.lisp"
- ];
+  ];
 }
 
diff --git a/third_party/lisp/global-vars.nix b/third_party/lisp/global-vars.nix
index 0f6630f721..a3d27a09b6 100644
--- a/third_party/lisp/global-vars.nix
+++ b/third_party/lisp/global-vars.nix
@@ -3,5 +3,5 @@
 let src = with pkgs; srcOnly lispPackages.global-vars;
 in depot.nix.buildLisp.library {
   name = "global-vars";
-  srcs = [ "${src}/global-vars.lisp" ] ;
+  srcs = [ "${src}/global-vars.lisp" ];
 }
diff --git a/third_party/lisp/hunchentoot.nix b/third_party/lisp/hunchentoot.nix
index 5b953d94b2..e2480cd349 100644
--- a/third_party/lisp/hunchentoot.nix
+++ b/third_party/lisp/hunchentoot.nix
@@ -1,5 +1,5 @@
 # Hunchentoot is a web framework for Common Lisp.
-{ depot, pkgs, ...}:
+{ depot, pkgs, ... }:
 
 let
   src = with pkgs; srcOnly lispPackages.hunchentoot;
@@ -15,7 +15,8 @@ let
       "url-rewrite.lisp"
     ];
   };
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "hunchentoot";
 
   deps = with depot.third_party.lisp; [
diff --git a/third_party/lisp/ironclad.nix b/third_party/lisp/ironclad.nix
index 3436776b7d..324c5da265 100644
--- a/third_party/lisp/ironclad.nix
+++ b/third_party/lisp/ironclad.nix
@@ -1,4 +1,4 @@
-{ depot, pkgs, ...}:
+{ depot, pkgs, ... }:
 
 let
   inherit (pkgs) runCommand;
@@ -6,7 +6,8 @@ let
   src = with pkgs; srcOnly lispPackages.ironclad;
   getSrc = f: "${src}/src/${f}";
 
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "ironclad";
 
   deps = with depot.third_party.lisp; [
diff --git a/third_party/lisp/lass.nix b/third_party/lisp/lass.nix
index 457e25c7e5..00f66c1fe3 100644
--- a/third_party/lisp/lass.nix
+++ b/third_party/lisp/lass.nix
@@ -8,7 +8,8 @@ let
     sha256 = "11mxzyx34ynsfsrs8pgrarqi9s442vkpmh7kdpzvarhj7i97g8yx";
   };
 
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "lass";
 
   deps = with depot.third_party.lisp; [
diff --git a/third_party/lisp/lisp-binary.nix b/third_party/lisp/lisp-binary.nix
index 3e7a43b8ac..296112cc9e 100644
--- a/third_party/lisp/lisp-binary.nix
+++ b/third_party/lisp/lisp-binary.nix
@@ -1,21 +1,19 @@
 # A library to easily read and write complex binary formats.
 { depot, pkgs, ... }:
 
-let src = pkgs.fetchFromGitHub {
-  owner = "j3pic";
-  repo = "lisp-binary";
-  rev = "052df578900dea59bf951e0a6749281fa73432e4";
-  sha256 = "1i1s5g01aimfq6lndcl1pnw7ly5hdh0wmjp2dj9cjjwbkz9lnwcf";
-};
-in depot.nix.buildLisp.library {
+let
+  src = pkgs.srcOnly pkgs.lispPackages.lisp-binary;
+in
+depot.nix.buildLisp.library {
   name = "lisp-binary";
 
   deps = with depot.third_party.lisp; [
+    alexandria
     cffi
-    quasiquote_2
-    moptilities
-    flexi-streams
     closer-mop
+    flexi-streams
+    moptilities
+    quasiquote_2
   ];
 
   srcs = map (f: src + ("/" + f)) [
@@ -30,6 +28,6 @@ in depot.nix.buildLisp.library {
   ];
 
   brokenOn = [
-    "ecl" # dynamic cffi
+    "ecl" # TODO(sterni): disable conditionally cffi for ECL
   ];
 }
diff --git a/third_party/lisp/local-time.nix b/third_party/lisp/local-time.nix
index 8e96c5e517..1358408d38 100644
--- a/third_party/lisp/local-time.nix
+++ b/third_party/lisp/local-time.nix
@@ -4,7 +4,8 @@
 let
   inherit (depot.nix) buildLisp;
   src = with pkgs; srcOnly lispPackages.local-time;
-in buildLisp.library {
+in
+buildLisp.library {
   name = "local-time";
   deps = [
     depot.third_party.lisp.cl-fad
diff --git a/third_party/lisp/mime4cl/OWNERS b/third_party/lisp/mime4cl/OWNERS
index f16dd105d7..2e95807063 100644
--- a/third_party/lisp/mime4cl/OWNERS
+++ b/third_party/lisp/mime4cl/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - sterni
+sterni
diff --git a/third_party/lisp/mime4cl/README b/third_party/lisp/mime4cl/README
deleted file mode 100644
index 73f0efbda9..0000000000
--- a/third_party/lisp/mime4cl/README
+++ /dev/null
@@ -1,7 +0,0 @@
-MIME4CL is a Common Lisp library for dealing with MIME messages.
-It has originally been written by Walter C. Pelissero and vendored
-into depot as upstream has become inactive and provides no repo
-of any kind. Upstream and depot version may diverge.
-
-Upstream Website: http://wcp.sdf-eu.org/software/#mime4cl
-Vendored Tarball: http://wcp.sdf-eu.org/software/mime4cl-20150207T211851.tbz
diff --git a/third_party/lisp/mime4cl/README.md b/third_party/lisp/mime4cl/README.md
new file mode 100644
index 0000000000..2704d481ed
--- /dev/null
+++ b/third_party/lisp/mime4cl/README.md
@@ -0,0 +1,27 @@
+# mime4cl
+
+`MIME4CL` is a Common Lisp library for dealing with MIME messages. It was
+originally been written by Walter C. Pelissero and vendored into depot
+([mime4cl-20150207T211851.tbz](http://wcp.sdf-eu.org/software/mime4cl-20150207T211851.tbz)
+to be exact) as upstream has become inactive. Its [original
+website](http://wcp.sdf-eu.org/software/#mime4cl) can still be accessed.
+
+The depot version has since diverged from upstream. Main aims were to improve
+performance and reduce code size by relying on third party libraries like
+flexi-streams. It is planned to improve encoding handling in the long term.
+Currently, the library is being worked on intermittently and not very well
+testedβ€”**it may not work as expected**.
+
+## Differences from the original version
+
+* `//nix/buildLisp` is used as the build system. ASDF is currently untested and
+  may be broken.
+
+* The dependency on [sclf](http://wcp.sdf-eu.org/software/#sclf) has been
+  eliminated by inlining the relevant parts.
+
+* `MY-STRING-INPUT-STREAM`, `DELIMITED-INPUT-STREAM`,
+  `CHARACTER-INPUT-ADAPTER-STREAM`, `BINARY-INPUT-ADAPTER-STREAM` etc. have been
+  replaced by (thin wrappers around) flexi-streams. In addition to improved
+  handling of encodings, this allows using `READ-SEQUENCE` via the gray stream
+  interface.
diff --git a/third_party/lisp/mime4cl/address.lisp b/third_party/lisp/mime4cl/address.lisp
index 944156916c..42688a595b 100644
--- a/third_party/lisp/mime4cl/address.lisp
+++ b/third_party/lisp/mime4cl/address.lisp
@@ -1,7 +1,7 @@
 ;;;  address.lisp --- e-mail address parser
 
 ;;;  Copyright (C) 2007, 2008, 2009 by Walter C. Pelissero
-;;;  Copyright (C) 2022 The TVL Authors
+;;;  Copyright (C) 2022-2023 The TVL Authors
 
 ;;;  Author: Walter C. Pelissero <walter@pelissero.de>
 ;;;  Project: mime4cl
@@ -219,14 +219,14 @@
   (not (find c " ()\"[]@.<>:;,")))
 
 (defun read-atext (first-character cursor)
-  (be string (with-output-to-string (out)
-               (write-char first-character out)
-               (loop
-                  for c = (read-char (cursor-stream cursor) nil)
-                  while (and c (atom-component-p c))
-                  do (write-char c out)
-                  finally (when c
-                            (unread-char c (cursor-stream cursor)))))
+  (let ((string (with-output-to-string (out)
+                  (write-char first-character out)
+                  (loop
+                    for c = (read-char (cursor-stream cursor) nil)
+                    while (and c (atom-component-p c))
+                    do (write-char c out)
+                    finally (when c
+                              (unread-char c (cursor-stream cursor)))))))
     (make-token :type 'atext
                 :value string
                 :position (incf (cursor-position cursor)))))
@@ -236,7 +236,7 @@
            (make-token :type 'keyword
                        :value (string c)
                        :position (incf (cursor-position cursor)))))
-    (be in (cursor-stream cursor)
+    (let ((in (cursor-stream cursor)))
       (loop
          for c = (read-char in nil)
          while c
@@ -259,7 +259,7 @@
   "Return the list of tokens produced by a lexical analysis of
 STRING.  These are the tokens that would be seen by the parser."
   (with-input-from-string (stream string)
-    (be cursor (make-cursor :stream stream)
+    (let ((cursor (make-cursor :stream stream)))
       (loop
          for tokens = (read-next-tokens cursor)
          until (endp tokens)
@@ -282,19 +282,19 @@ addresses only."
 MAILBOX-GROUPs.  If STRING is unparsable return NIL.  If
 NO-GROUPS is true, return a flat list of mailboxes throwing away
 the group containers, if any."
-  (be grammar (force define-grammar)
+  (let ((grammar (force define-grammar)))
     (with-input-from-string (stream string)
-      (be* cursor (make-cursor :stream stream)
-           mailboxes (ignore-errors	; ignore parsing errors
-                       (parse grammar 'address-list cursor))
+      (let* ((cursor (make-cursor :stream stream))
+             (mailboxes (ignore-errors  ; ignore parsing errors
+                         (parse grammar 'address-list cursor))))
         (if no-groups
             (mailboxes-only mailboxes)
             mailboxes)))))
 
 (defun debug-addresses (string)
   "More or less like PARSE-ADDRESSES, but don't ignore parsing errors."
-  (be grammar (force define-grammar)
+  (let ((grammar (force define-grammar)))
     (with-input-from-string (stream string)
-      (be cursor (make-cursor :stream stream)
+      (let ((cursor (make-cursor :stream stream)))
         (parse grammar 'address-list cursor)))))
 
diff --git a/third_party/lisp/mime4cl/default.nix b/third_party/lisp/mime4cl/default.nix
index a14d695c29..af015a257b 100644
--- a/third_party/lisp/mime4cl/default.nix
+++ b/third_party/lisp/mime4cl/default.nix
@@ -6,12 +6,15 @@ depot.nix.buildLisp.library {
   name = "mime4cl";
 
   deps = [
-    depot.third_party.lisp.sclf
+    depot.third_party.lisp.flexi-streams
     depot.third_party.lisp.npg
     depot.third_party.lisp.trivial-gray-streams
+    depot.third_party.lisp.qbase64
+    { sbcl = depot.nix.buildLisp.bundled "sb-posix"; }
   ];
 
   srcs = [
+    ./ex-sclf.lisp
     ./package.lisp
     ./endec.lisp
     ./streams.lisp
@@ -28,11 +31,10 @@ depot.nix.buildLisp.library {
       (pkgs.writeText "nix-samples.lisp" ''
         (in-package :mime4cl-tests)
 
-        ;; missing from the tarball completely
-        (defvar *samples-directory* (pathname "/this/does/not/exist"))
-        ;; override auto discovery which doesn't work in store
-        (defvar *sample1-file* (pathname "${./test/sample1.msg}"))
+        ;; override auto discovery which doesn't work in the nix store
+        (defvar *samples-directory* (pathname "${./test/samples}/"))
       '')
+      ./test/temp-file.lisp
       ./test/endec.lisp
       ./test/address.lisp
       ./test/mime.lisp
diff --git a/third_party/lisp/mime4cl/endec.lisp b/third_party/lisp/mime4cl/endec.lisp
index 9f2f9c51c2..2e282c2378 100644
--- a/third_party/lisp/mime4cl/endec.lisp
+++ b/third_party/lisp/mime4cl/endec.lisp
@@ -1,6 +1,7 @@
 ;;;  endec.lisp --- encoder/decoder functions
 
 ;;;  Copyright (C) 2005-2008, 2010 by Walter C. Pelissero
+;;;  Copyright (C) 2023 by The TVL Authors
 
 ;;;  Author: Walter C. Pelissero <walter@pelissero.de>
 ;;;  Project: mime4cl
@@ -21,19 +22,21 @@
 
 (in-package :mime4cl)
 
+(defun redirect-stream (in out &key (buffer-size 4096))
+  "Consume input stream IN and write all its content to output stream OUT.
+The streams' element types need to match."
+  (let ((buf (make-array buffer-size :element-type (stream-element-type in))))
+    (loop for pos = (read-sequence buf in)
+          while (> pos 0)
+          do (write-sequence buf out :end pos))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; Thank you SBCL for rendering constants totally useless!
 (defparameter +base64-encode-table+
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=")
 
-(defparameter +base64-decode-table+
-  (let ((da (make-array 256 :element-type '(unsigned-byte 8) :initial-element 65)))
-    (dotimes (i 64)
-      (setf (aref da (char-code (char +base64-encode-table+ i))) i))
-    da))
-
-(declaim (type (simple-array (unsigned-byte 8)) +base64-decode-table+)
-         (type simple-string +base64-encode-table+))
+(declaim (type simple-string +base64-encode-table+))
 
 (defvar *base64-line-length* 76
   "Maximum length of the encoded base64 line.  NIL means it can
@@ -161,7 +164,7 @@ It should expect a character as its only argument."))
        for byte = (decoder-read-byte decoder)
        unless byte
        do (return-from decoder-read-line nil)
-       do (be c (code-char byte)
+       do (let ((c (code-char byte)))
             (cond ((char= c #\return)
                    ;; skip the newline
                    (decoder-read-byte decoder)
@@ -198,7 +201,7 @@ value."
              (save (c)
                (saveb (char-code c)))
              (push-next ()
-               (be c (funcall input-function)
+               (let ((c (funcall input-function)))
                  (declare (type (or null character) c))
                  (cond ((not c))
                        ((or (char= c #\space)
@@ -206,7 +209,7 @@ value."
                         (save c)
                         (push-next))
                        ((char= c #\=)
-                        (be c1 (funcall input-function)
+                        (let ((c1 (funcall input-function)))
                           (cond ((not c1)
                                  (save #\=))
                                 ((char= c1 #\return)
@@ -221,7 +224,7 @@ value."
                                  (push-next))
                                 (t
                                  ;; hexadecimal sequence: get the 2nd digit
-                                 (be c2 (funcall input-function)
+                                 (let ((c2 (funcall input-function)))
                                    (if c2
                                        (aif (parse-hex c1 c2)
                                             (saveb it)
@@ -271,10 +274,10 @@ binary output OUT the decoded stream of bytes."
 (defmacro make-stream-to-sequence-decoder (decoder-class input-form &key parser-errors)
   "Decode the character stream STREAM and return a sequence of bytes."
   (with-gensyms (output-sequence)
-    `(be ,output-sequence (make-array 0
-                                      :element-type '(unsigned-byte 8)
-                                      :fill-pointer 0
-                                      :adjustable t)
+    `(let ((,output-sequence (make-array 0
+                                         :element-type '(unsigned-byte 8)
+                                         :fill-pointer 0
+                                         :adjustable t)))
        (make-decoder-loop ,decoder-class ,input-form
                           (vector-push-extend byte ,output-sequence)
                           :parser-errors ,parser-errors)
@@ -377,7 +380,7 @@ characters quoted printables encoded."
 (defun encode-quoted-printable-sequence-to-stream (sequence stream &key (start 0) (end (length sequence)))
   "Encode the sequence of bytes SEQUENCE and write to STREAM a
 quoted printable sequence of characters."
-  (be i start
+  (let ((i start))
     (make-encoder-loop quoted-printable-encoder
      (when (< i end)
        (prog1 (elt sequence i)
@@ -470,7 +473,7 @@ character stream."
 (defun encode-base64-sequence-to-stream (sequence stream &key (start 0) (end (length sequence)))
   "Encode the sequence of bytes SEQUENCE and write to STREAM the
 Base64 character sequence."
-  (be i start
+  (let ((i start))
     (make-encoder-loop base64-encoder
                        (when (< i end)
                          (prog1 (elt sequence i)
@@ -483,60 +486,34 @@ return it."
   (with-output-to-string (out)
     (encode-base64-sequence-to-stream sequence out :start start :end end)))
 
-(defclass base64-decoder (parsing-decoder)
-  ((bitstore :initform 0
-             :type fixnum)
-   (bytecount :initform 0 :type fixnum))
-  (:documentation
-   "Class for Base64 decoder input streams."))
-
-(defmethod decoder-read-byte ((decoder base64-decoder))
-  (declare (optimize (speed 3) (safety 0) (debug 0)))
-  (with-slots (bitstore bytecount input-function) decoder
-    (declare (type fixnum bitstore bytecount)
-             (type function input-function))
-    (labels ((in6 ()
-               (loop
-                  for c = (funcall input-function)
-                  when (or (not c) (char= #\= c))
-                  do (return-from decoder-read-byte nil)
-                  do (be sextet (aref +base64-decode-table+ (char-code c))
-                       (unless (= sextet 65) ; ignore unrecognised characters
-                         (return sextet)))))
-             (push6 (sextet)
-               (declare (type fixnum sextet))
-               (setf bitstore
-                     (logior sextet (the fixnum (ash bitstore 6))))))
-      (case bytecount
-        (0
-         (setf bitstore (in6))
-         (push6 (in6))
-         (setf bytecount 1)
-         (ash bitstore -4))
-        (1
-         (push6 (in6))
-         (setf bytecount 2)
-         (logand #xFF (ash bitstore -2)))
-        (2
-         (push6 (in6))
-         (setf bytecount 0)
-         (logand #xFF bitstore))))))
-
 (defun decode-base64-stream (in out &key parser-errors)
   "Read from IN a stream of characters Base64 encoded and write
 to OUT a stream of decoded bytes."
-  (make-decoder-loop base64-decoder
-                     (read-byte in nil) (write-byte byte out)
-                     :parser-errors parser-errors))
+  ;; parser-errors are ignored for base64
+  (declare (ignore parser-errors))
+  (redirect-stream (make-instance 'qbase64:decode-stream
+                                  :underlying-stream in)
+                   out))
 
 (defun decode-base64-stream-to-sequence (stream &key parser-errors)
-  (make-stream-to-sequence-decoder base64-decoder
-                                   (read-char stream nil)
-                                   :parser-errors parser-errors))
-
-(defun decode-base64-string (string &key (start 0) (end (length string)) parser-errors)
-  (with-input-from-string (in string :start start :end end)
-    (decode-base64-stream-to-sequence in :parser-errors parser-errors)))
+  "Read Base64 characters from STREAM and return result of decoding them as a
+binary sequence."
+  ;; parser-errors are ignored for base64
+  (declare (ignore parser-errors))
+  (let* ((buffered-size 4096)
+         (dstream (make-instance 'qbase64:decode-stream
+                                 :underlying-stream stream))
+         (output-seq (make-array buffered-size
+                                 :element-type '(unsigned-byte 8)
+                                 :adjustable t)))
+    (loop for cap = (array-dimension output-seq 0)
+          for pos = (read-sequence output-seq dstream :start (or pos 0))
+          if (>= pos cap)
+            do (adjust-array output-seq (+ cap buffered-size))
+          else
+            do (progn
+                 (adjust-array output-seq pos)
+                 (return output-seq)))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -547,25 +524,14 @@ to OUT a stream of decoded bytes."
      while c
      do (write-byte (char-code c) out)))
 
-(defun decode-stream (in out encoding &key parser-errors-p)
-  (gcase (encoding string-equal)
-    (:quoted-printable
-     (decode-quoted-printable-stream in out
-                                     :parser-errors parser-errors-p))
-    (:base64
-     (decode-base64-stream in out
-                           :parser-errors parser-errors-p))
-    (otherwise
-     (dump-stream-binary in out))))
-
 (defun decode-string (string encoding &key parser-errors-p)
   (gcase (encoding string-equal)
     (:quoted-printable
      (decode-quoted-printable-string string
                                      :parser-errors parser-errors-p))
     (:base64
-     (decode-base64-string string
-                           :parser-errors parser-errors-p))
+     ;; parser-errors-p is unused in base64
+     (qbase64:decode-string string))
     (otherwise
      (map '(vector (unsigned-byte 8)) #'char-code string))))
 
@@ -644,12 +610,12 @@ method of RFC2047 and return a sequence of bytes."
            (vector-push-extend (char-code c) output-sequence)))
        finally (return output-sequence)))
 
-(defun decode-RFC2047-string (encoding string &key (start 0) (end (length string)))
+(defun decode-RFC2047-part (encoding string &key (start 0) (end (length string)))
   "Decode STRING according to RFC2047 and return a sequence of
 bytes."
   (gcase (encoding string-equal)
     ("Q" (decode-quoted-printable-RFC2047-string string :start start :end end))
-    ("B" (decode-base64-string string :start start :end end))
+    ("B" (qbase64:decode-string (subseq string start end)))
     (t string)))
 
 (defun parse-RFC2047-text (text)
@@ -674,10 +640,24 @@ sequence, a charset string indicating the original coding."
             (push (subseq text previous-end start)
                   result))
           (setf previous-end (+ end 2))
-          (push (cons (decode-RFC2047-string encoding text :start (1+ second-?) :end end)
+          (push (cons (decode-RFC2047-part encoding text :start (1+ second-?) :end end)
                       charset)
                 result))
      finally (unless (= previous-end (length text))
                (push (subseq text previous-end (length text))
                      result))
        (return (nreverse result))))
+
+(defun decode-RFC2047 (text)
+  "Decode TEXT into a fully decoded string. Whenever a non ASCII part is
+  encountered, try to decode it using flexi-streams, otherwise signal an error."
+  (flet ((decode-part (part)
+           (etypecase part
+             (cons (flexi-streams:octets-to-string
+                    (car part)
+                    :external-format (flexi-streams:make-external-format
+                                      (intern (string-upcase (cdr part)) 'keyword))))
+             (string part))))
+    (apply #'concatenate
+           (cons 'string
+                 (mapcar #'decode-part (mime:parse-RFC2047-text text))))))
diff --git a/third_party/lisp/mime4cl/ex-sclf.lisp b/third_party/lisp/mime4cl/ex-sclf.lisp
new file mode 100644
index 0000000000..1719732fb3
--- /dev/null
+++ b/third_party/lisp/mime4cl/ex-sclf.lisp
@@ -0,0 +1,329 @@
+;;; ex-sclf.lisp --- subset of sclf used by mime4cl
+
+;;;  Copyright (C) 2005-2010 by Walter C. Pelissero
+;;;  Copyright (C) 2022-2023 The TVL Authors
+
+;;;  Author: sternenseemann <sternenseemann@systemli.org>
+;;;  Project: mime4cl
+;;;
+;;;  mime4cl uses sclf for miscellaneous utility functions. sclf's portability
+;;;  is quite limited. Since mime4cl is the only thing in TVL's depot depending
+;;;  on sclf, it made more sense to strip down sclf to the extent mime4cl needed
+;;;  in order to lessen the burden of porting it to other CL implementations
+;;;  later.
+;;;
+;;;  Eventually it probably makes sense to drop the utilities we don't like and
+;;;  merge the ones we do like into depot's own utility package, klatre.
+
+#+cmu (ext:file-comment "$Module: ex-sclf.lisp $")
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public License
+;;; as published by the Free Software Foundation; either version 2.1
+;;; of the License, or (at your option) any later version.
+;;; This library is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;; Lesser General Public License for more details.
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free
+;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+;;; 02111-1307 USA
+
+(defpackage :mime4cl-ex-sclf
+  (:use :common-lisp)
+  (:import-from :sb-posix :stat :stat-size)
+
+  (:export
+   #:aif
+   #:awhen
+   #:aand
+   #:it
+
+   #:gcase
+
+   #:with-gensyms
+
+   #:split-at
+   #:split-string-at-char
+   #:+whitespace+
+   #:whitespace-p
+   #:string-concat
+   #:s+
+   #:string-starts-with
+   #:string-trim-whitespace
+   #:string-left-trim-whitespace
+   #:string-right-trim-whitespace
+
+   #:queue
+   #:make-queue
+   #:queue-append
+   #:queue-pop
+   #:queue-empty-p
+
+   #:save-file-excursion
+   #:read-file
+
+   #:file-size
+
+   #:promise
+   #:make-promise
+   #:lazy
+   #:force
+   #:forced-p
+   #:deflazy
+
+   #:f++
+
+   #:week-day->string
+   #:month->string))
+
+(in-package :mime4cl-ex-sclf)
+
+;; MACRO UTILS
+
+(defmacro with-gensyms ((&rest symbols) &body body)
+  "Gensym all SYMBOLS and make them available in BODY.
+See also LET-GENSYMS."
+  `(let ,(mapcar #'(lambda (s)
+                     (list s '(gensym))) symbols)
+     ,@body))
+
+;; CONTROL FLOW
+
+(defmacro aif (test then &optional else)
+  `(let ((it ,test))
+     (if it
+         ,then
+         ,else)))
+
+(defmacro awhen (test &body then)
+  `(let ((it ,test))
+     (when it
+       ,@then)))
+
+(defmacro aand (&rest args)
+  (cond ((null args) t)
+        ((null (cdr args)) (car args))
+        (t `(aif ,(car args) (aand ,@(cdr args))))))
+
+(defmacro gcase ((value &optional (test 'equalp)) &rest cases)
+  "Generic CASE macro.  Match VALUE to CASES as if by the normal CASE
+but use TEST as the comparison function, which defaults to EQUALP."
+  (with-gensyms (val)
+    `(let ((,val ,value))
+       ,(cons 'cond
+              (mapcar #'(lambda (case-desc)
+                          (destructuring-bind (vals &rest forms) case-desc
+                            `(,(cond ((consp vals)
+                                      (cons 'or (mapcar #'(lambda (v)
+                                                            (list test val v))
+                                                        vals)))
+                                     ((or (eq vals 'otherwise)
+                                          (eq vals t))
+                                      t)
+                                     (t (list test val vals)))
+                               ,@forms)))
+                      cases)))))
+
+;; SEQUENCES
+
+(defun position-any (bag sequence &rest position-args)
+  "Find any element of bag in sequence and return its position.
+Accept any argument accepted by the POSITION function."
+  (apply #'position-if #'(lambda (element)
+                           (find element bag)) sequence position-args))
+
+(defun split-at (bag sequence &key (start 0) key)
+  "Split SEQUENCE at occurence of any element from BAG.
+Contiguous occurences of elements from BAG are considered atomic;
+so no empty sequence is returned."
+  (let ((len (length sequence)))
+    (labels ((split-from (start)
+               (unless (>= start len)
+                 (let ((sep (position-any bag sequence :start start :key key)))
+                   (cond ((not sep)
+                          (list (subseq sequence start)))
+                         ((> sep start)
+                          (cons (subseq sequence start sep)
+                                (split-from (1+ sep))))
+                         (t
+                          (split-from (1+ start))))))))
+      (split-from start))))
+
+;; STRINGS
+
+(defvar +whitespace+ '(#\return #\newline #\tab #\space #\page))
+
+(defun whitespace-p (char)
+  (member char +whitespace+))
+
+(defun string-trim-whitespace (string)
+  (string-trim +whitespace+ string))
+
+(defun string-right-trim-whitespace (string)
+  (string-right-trim +whitespace+ string))
+
+(defun string-left-trim-whitespace (string)
+  (string-left-trim +whitespace+ string))
+
+(defun split-string-at-char (string separator &key escape skip-empty)
+  "Split STRING at SEPARATORs and return a list of the substrings.  If
+SKIP-EMPTY is true then filter out the empty substrings.  If ESCAPE is
+not nil then split at SEPARATOR only if it's not preceded by ESCAPE."
+  (declare (type string string) (type character separator))
+  (labels ((next-separator (beg)
+             (let ((pos (position separator string :start beg)))
+               (if (and escape
+                        pos
+                        (plusp pos)
+                        (char= escape (char string (1- pos))))
+                   (next-separator (1+ pos))
+                   pos)))
+           (parse (beg)
+             (cond ((< beg (length string))
+                    (let* ((end (next-separator beg))
+                           (substring (subseq string beg end)))
+                      (cond ((and skip-empty (string= "" substring))
+                             (parse (1+ end)))
+                            ((not end)
+                             (list substring))
+                            (t
+                             (cons substring (parse (1+ end)))))))
+                   (skip-empty
+                    '())
+                   (t
+                    (list "")))))
+    (parse 0)))
+
+(defun s+ (&rest strings)
+  "Return a string which is made of the concatenation of STRINGS."
+  (apply #'concatenate 'string strings))
+
+(defun string-concat (list &optional (separator ""))
+  "Concatenate the strings in LIST interposing SEPARATOR (default
+nothing) between them."
+  (reduce #'(lambda (&rest args)
+              (if args
+                  (s+ (car args) separator (cadr args))
+                  ""))
+          list))
+
+(defun string-starts-with (prefix string &optional (compare #'string=))
+  (let ((prefix-length (length prefix)))
+    (and (>= (length string) prefix-length)
+         (funcall compare prefix string :end2 prefix-length))))
+
+;; QUEUE
+
+(defstruct queue
+  first
+  last)
+
+(defgeneric queue-append (queue objects))
+(defgeneric queue-pop (queue))
+(defgeneric queue-empty-p (queue))
+
+(defmethod queue-append ((queue queue) (objects list))
+  (cond ((null (queue-first queue))
+         (setf (queue-first queue) objects
+               (queue-last queue) (last objects)))
+        (t
+         (setf (cdr (queue-last queue)) objects
+               (queue-last queue) (last objects))))
+  queue)
+
+(defmethod queue-append ((queue queue) object)
+  (queue-append queue (list object)))
+
+(defmethod queue-pop ((queue queue))
+  (prog1 (car (queue-first queue))
+    (setf (queue-first queue) (cdr (queue-first queue)))))
+
+(defmethod queue-empty-p ((queue queue))
+  (null (queue-first queue)))
+
+;; STREAMS
+
+(defmacro save-file-excursion ((stream &optional position) &body forms)
+  "Execute FORMS returning, on exit, STREAM to the position it was
+before FORMS.  Optionally POSITION can be set to the starting offset."
+  (unless position
+    (setf position (gensym)))
+  `(let ((,position (file-position ,stream)))
+     (unwind-protect (progn ,@forms)
+       (file-position ,stream ,position))))
+
+(defun read-file (pathname &key (element-type 'character) (if-does-not-exist :error) default)
+  "Read the whole content of file and return it as a sequence which
+can be a string, a vector of bytes, or whatever you specify as
+ELEMENT-TYPE."
+  (with-open-file (in pathname
+                      :element-type element-type
+                      :if-does-not-exist (unless (eq :value if-does-not-exist)
+                                           :error))
+    (if in
+        (let ((seq (make-array (file-length in) :element-type element-type)))
+          (read-sequence seq in)
+          seq)
+        default)))
+
+;; FILES
+
+(defun native-namestring (pathname)
+  #+sbcl (sb-ext:native-namestring pathname)
+  #-sbcl (let (#+cmu (lisp::*ignore-wildcards* t))
+           (namestring pathname)))
+
+;; FILE-LENGTH is a bit idiosyncratic in this respect.  Besides, Unix
+;; allows to get to know the file size without being able to open a
+;; file; just ask politely.
+(defun file-size (pathname)
+  #+sbcl (stat-size (unix-stat pathname))
+  #-sbcl (error "nyi"))
+
+;; LAZY
+
+(defstruct promise
+  procedure
+  value)
+
+(defmacro lazy (form)
+  `(make-promise :procedure #'(lambda () ,form)))
+
+(defun forced-p (promise)
+  (null (promise-procedure promise)))
+
+(defun force (promise)
+  (if (forced-p promise)
+      (promise-value promise)
+      (prog1 (setf (promise-value promise)
+                   (funcall (promise-procedure promise)))
+        (setf (promise-procedure promise) nil))))
+
+(defmacro deflazy (name value &optional documentation)
+  `(defparameter ,name (lazy ,value)
+     ,@(when documentation
+             (list documentation))))
+
+;; FIXNUMS
+
+(defmacro f++ (x &optional (delta 1))
+  "Same as INCF but hopefully optimised for fixnums."
+  `(setf ,x (+ (the fixnum ,x) (the fixnum ,delta))))
+
+;; TIME
+
+(defun week-day->string (day &optional sunday-first)
+  "Return the weekday string corresponding to DAY number."
+  (elt (if sunday-first
+           #("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday")
+           #("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"))
+       day))
+
+(defvar +month-names+  #("January" "February" "March" "April" "May" "June" "July"
+                           "August" "September" "October" "November" "December"))
+
+(defun month->string (month)
+  "Return the month string corresponding to MONTH number."
+  (elt +month-names+ (1- month)))
diff --git a/third_party/lisp/mime4cl/mime.lisp b/third_party/lisp/mime4cl/mime.lisp
index 1b1d98bfaf..3cdac4b26b 100644
--- a/third_party/lisp/mime4cl/mime.lisp
+++ b/third_party/lisp/mime4cl/mime.lisp
@@ -1,7 +1,7 @@
 ;;;  mime4cl.lisp --- MIME primitives for Common Lisp
 
 ;;;  Copyright (C) 2005-2008, 2010 by Walter C. Pelissero
-;;;  Copyright (C) 2021 by the TVL Authors
+;;;  Copyright (C) 2021-2023 by the TVL Authors
 
 ;;;  Author: Walter C. Pelissero <walter@pelissero.de>
 ;;;  Project: mime4cl
@@ -183,14 +183,11 @@
                :test #'string=)
        (mime= (mime-body part1) (mime-body part2))))
 
-(defun mime-body-stream (mime-part &key (binary t))
-  (make-instance (if binary
-                     'binary-input-adapter-stream
-                     'character-input-adapter-stream)
-                 :source (mime-body mime-part)))
+(defun mime-body-stream (mime-part)
+  (make-input-adapter (mime-body mime-part)))
 
 (defun mime-body-length (mime-part)
-  (be body (mime-body mime-part)
+  (let ((body (mime-body mime-part)))
     ;; here the stream type is missing on purpose, because we may not
     ;; be able to size the length of a stream
     (etypecase body
@@ -207,8 +204,8 @@
             while byte
             count byte))))))
 
-(defmacro with-input-from-mime-body-stream ((stream part &key (binary t)) &body forms)
-  `(with-open-stream (,stream (mime-body-stream ,part :binary ,binary))
+(defmacro with-input-from-mime-body-stream ((stream part) &body forms)
+  `(with-open-stream (,stream (mime-body-stream ,part))
      ,@forms))
 
 (defmethod mime= ((part1 mime-bodily-part) (part2 mime-bodily-part))
@@ -302,12 +299,13 @@ semi-colons not within strings or comments."
 (defun parse-parameter (string)
   "Given a string like \"foo=bar\" return a pair (\"foo\" .
 \"bar\").  Return NIL if string is not parsable."
-  (be equal-position (position #\= string)
+  ;; TODO(sterni): when-let
+  (let ((equal-position (position #\= string)))
     (when equal-position
-      (be key (subseq string  0 equal-position)
+      (let ((key (subseq string  0 equal-position)))
         (if (= equal-position (1- (length string)))
             (cons key "")
-            (be value (string-trim-whitespace (subseq string (1+ equal-position)))
+            (let ((value (string-trim-whitespace (subseq string (1+ equal-position)))))
               (cons key
                     (if (and (> (length value) 1)
                              (char= #\" (elt value 0)))
@@ -316,8 +314,8 @@ semi-colons not within strings or comments."
                         ;; reader
                         (or (ignore-errors (read-from-string value))
                             (subseq value 1))
-                        (be end (or (position-if #'whitespace-p value)
-                                    (length value))
+                        (let ((end (or (position-if #'whitespace-p value)
+                                       (length value))))
                           (subseq value 0 end))))))))))
 
 (defun parse-content-type (string)
@@ -340,7 +338,7 @@ Example: (\"text\" \"plain\" ((\"charset\" . \"us-ascii\")...))."
 list.  The first element is the layout, the other elements are
 the optional parameters alist.
 Example: (\"inline\" (\"filename\" . \"doggy.jpg\"))."
-  (be parts (split-header-parts string)
+  (let ((parts (split-header-parts string)))
     (cons (car parts) (mapcan #'(lambda (parameter-string)
                                   (awhen (parse-parameter parameter-string)
                                     (list it)))
@@ -350,7 +348,7 @@ Example: (\"inline\" (\"filename\" . \"doggy.jpg\"))."
   "Parse STRING which should be a valid RFC822 message header and
 return two values: a string of the header name and a string of
 the header value."
-  (be colon (position #\: string)
+  (let ((colon (position #\: string)))
     (when colon
       (values (string-trim-whitespace (subseq string 0 colon))
               (string-trim-whitespace (subseq string (1+ colon)))))))
@@ -419,34 +417,6 @@ each (non-boundary) line or END-PART-FUNCTION at each PART-BOUNDARY."
          do (last-part)
          do (process-line line)))))
 
-;; This awkward handling of newlines is due to RFC2046: "The CRLF
-;; preceding the boundary delimiter line is conceptually attached to
-;; the boundary so that it is possible to have a part that does not
-;; end with a CRLF (line break).  Body parts that must be considered
-;; to end with line breaks, therefore, must have two CRLFs preceding
-;; the boundary delimiter line, the first of which is part of the
-;; preceding body part, and the second of which is part of the
-;; encapsulation boundary".
-(defun split-multipart-parts (body-stream part-boundary)
-  "Read from BODY-STREAM and split MIME parts separated by
-PART-BOUNDARY.  Return a list of strings."
-  (let ((part (make-string-output-stream))
-        (parts '())
-        (beginning-of-part-p t))
-    (flet ((output-line (line)
-             (if beginning-of-part-p
-                 (setf beginning-of-part-p nil)
-                 (terpri part))
-             (write-string line part))
-           (end-part ()
-             (setf beginning-of-part-p t)
-             (push (get-output-stream-string part) parts)))
-      (do-multipart-parts body-stream part-boundary #'output-line #'end-part)
-      (close part)
-      ;; the first part is empty or contains all the junk
-      ;; to the first boundary
-      (cdr (nreverse parts)))))
-
 (defun index-multipart-parts (body-stream part-boundary)
   "Read from BODY-STREAM and return the file offset of the MIME parts
 separated by PART-BOUNDARY."
@@ -531,9 +501,9 @@ separated by PART-BOUNDARY."
   (encode-mime-body (mime-body part) stream))
 
 (defmethod encode-mime-body ((part mime-multipart) stream)
-  (be boundary (or (get-mime-type-parameter part :boundary)
-                   (setf (get-mime-type-parameter part :boundary)
-                         (choose-boundary (mime-parts part))))
+  (let ((boundary (or (get-mime-type-parameter part :boundary)
+                      (setf (get-mime-type-parameter part :boundary)
+                            (choose-boundary (mime-parts part))))))
     (dolist (p (mime-parts part))
       (format stream "~%--~A~%" boundary)
       (encode-mime-part p stream))
@@ -588,7 +558,7 @@ found in STREAM."
   ;; continuation line of a header we don't want to a header we want
   (loop
      with headers = '() and skip-header = nil
-     for line = (be line (read-line stream nil)
+     for line = (let ((line (read-line stream nil)))
                   ;; skip the Unix "From " header if present
                   (if (string-starts-with "From " line)
                       (read-line stream nil)
@@ -622,24 +592,38 @@ found in STREAM."
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+(defun mime-message-header-values (name message &key decode)
+  "Return all values of the header with NAME in MESSAGE, optionally decoding
+  it according to RFC2047 if :DECODE is T."
+  (loop ;; A header may occur multiple times
+        for header in (mime-message-headers message)
+        ;; MIME Headers should be case insensitive
+        ;; https://stackoverflow.com/a/6143644
+        when (string-equal (car header) name)
+        collect (if decode
+                    (decode-RFC2047 (cdr header))
+                    (cdr header))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
 (defvar *lazy-mime-decode* t
   "If true don't  decode mime bodies in memory.")
 
 (defgeneric decode-mime-body (part input-stream))
 
-(defmethod decode-mime-body ((part mime-part) (stream delimited-input-stream))
- (be base (base-stream stream)
-   (if *lazy-mime-decode*
-       (setf (mime-body part)
-             (make-file-portion :data (etypecase base
-                                        (my-string-input-stream
-                                         (stream-string base))
-                                        (file-stream
-                                         (pathname base)))
-                                :encoding (mime-encoding part)
-                                :start (file-position stream)
-                                :end (stream-end stream)))
-       (call-next-method))))
+(defmethod decode-mime-body ((part mime-part) (stream flexi-stream))
+  (let ((base (flexi-stream-root-stream stream)))
+    (if *lazy-mime-decode*
+        (setf (mime-body part)
+              (make-file-portion :data (etypecase base
+                                         (vector-stream
+                                          (flexi-streams::vector-stream-vector base))
+                                         (file-stream
+                                          (pathname base)))
+                                 :encoding (mime-encoding part)
+                                 :start (flexi-stream-position stream)
+                                 :end (flexi-stream-bound stream)))
+        (call-next-method))))
 
 (defmethod decode-mime-body ((part mime-part) (stream file-stream))
   (if *lazy-mime-decode*
@@ -649,12 +633,12 @@ found in STREAM."
                                :start (file-position stream)))
       (call-next-method)))
 
-(defmethod decode-mime-body ((part mime-part) (stream my-string-input-stream))
+(defmethod decode-mime-body ((part mime-part) (stream vector-stream))
   (if *lazy-mime-decode*
       (setf (mime-body part)
-            (make-file-portion :data (stream-string stream)
+            (make-file-portion :data (flexi-streams::vector-stream-vector stream)
                                :encoding (mime-encoding part)
-                               :start (file-position stream)))
+                               :start (flexi-streams::vector-stream-index stream)))
       (call-next-method)))
 
 (defmethod decode-mime-body ((part mime-part) stream)
@@ -665,19 +649,18 @@ found in STREAM."
   "Decode STREAM according to PART characteristics and return a
 list of MIME parts."
   (save-file-excursion (stream)
-    (be offsets (index-multipart-parts stream (get-mime-type-parameter part :boundary))
+    (let ((offsets (index-multipart-parts stream (get-mime-type-parameter part :boundary))))
       (setf (mime-parts part)
             (mapcar #'(lambda (p)
                         (destructuring-bind (start . end) p
-                          (be *default-type* (if (eq :digest (mime-subtype part))
-                                                 '("message" "rfc822" ())
-                                                 '("text" "plain" (("charset" . "us-ascii"))))
-                              in (make-instance 'delimited-input-stream
-                                                :stream stream
-                                                :dont-close t
-                                                :start start
-                                                :end end)
-                              (read-mime-part in))))
+                          (let ((*default-type* (if (eq :digest (mime-subtype part))
+                                                    '("message" "rfc822" ())
+                                                    '("text" "plain" (("charset" . "us-ascii")))))
+                                (in (make-positioned-flexi-input-stream stream
+                                                                        :position start
+                                                                        :bound end
+                                                                        :ignore-close t)))
+                            (read-mime-part in))))
                     offsets)))))
 
 (defmethod decode-mime-body ((part mime-message) stream)
@@ -688,7 +671,7 @@ body."
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(defconst +known-encodings+ '(:7BIT :8BIT :BINARY :QUOTED-PRINTABLE :BASE64)
+(defvar +known-encodings+ '(:7BIT :8BIT :BINARY :QUOTED-PRINTABLE :BASE64)
   "List of known content encodings.")
 
 (defun keywordify-encoding (string)
@@ -699,11 +682,11 @@ Return STRING itself if STRING is an unkown encoding."
        string))
 
 (defun header (name headers)
-  (be elt (assoc name headers :test #'string-equal)
+  (let ((elt (assoc name headers :test #'string-equal)))
     (values (cdr elt) (car elt))))
 
 (defun (setf header) (value name headers)
-  (be entry (assoc name headers :test #'string-equal)
+  (let ((entry (assoc name headers :test #'string-equal)))
     (unless entry
       (error "missing header ~A can't be set" name))
     (setf (cdr entry) value)))
@@ -715,7 +698,7 @@ guessed from the headers, use the *DEFAULT-TYPE*."
   (flet ((hdr (what)
            (header what headers)))
     (destructuring-bind (type subtype parms)
-        (or 
+        (or
          (aand (hdr :content-type)
                (parse-content-type it))
          *default-type*)
@@ -741,16 +724,16 @@ guessed from the headers, use the *DEFAULT-TYPE*."
 
 (defun read-mime-part (stream)
   "Read mime part from STREAM.  Return a MIME-PART object."
-  (be headers (read-rfc822-headers stream
-                                   '(:mime-version :content-transfer-encoding :content-type
-                                     :content-disposition :content-description :content-id))
+  (let ((headers (read-rfc822-headers stream
+                                      '(:mime-version :content-transfer-encoding :content-type
+                                        :content-disposition :content-description :content-id))))
     (make-mime-part headers stream)))
 
 (defun read-mime-message (stream)
   "Main function to read a MIME message from a stream.  It
 returns a MIME-MESSAGE object."
-  (be headers (read-rfc822-headers stream)
-      *default-type* '("text" "plain" (("charset" . "us-ascii")))
+  (let ((headers (read-rfc822-headers stream))
+        (*default-type* '("text" "plain" (("charset" . "us-ascii")))))
     (flet ((hdr (what)
              (header what headers)))
       (destructuring-bind (type subtype parms)
@@ -768,17 +751,21 @@ returns a MIME-MESSAGE object."
   msg)
 
 (defmethod mime-message ((msg string))
-  (with-open-stream (in (make-instance 'my-string-input-stream :string msg))
-    (read-mime-message in)))
+  (mime-message (flexi-streams:string-to-octets msg)))
 
-(defmethod mime-message ((msg stream))
-  (read-mime-message msg))
+(defmethod mime-message ((msg vector))
+  (with-input-from-sequence (in msg)
+    (mime-message in)))
 
 (defmethod mime-message ((msg pathname))
-  (let (#+sbcl(sb-impl::*default-external-format* :latin-1)
-        #+sbcl(sb-alien::*default-c-string-external-format* :latin-1))
-    (with-open-file (in msg)
-      (read-mime-message in))))
+  (with-open-file (in msg :element-type '(unsigned-byte 8))
+    (mime-message in)))
+
+(defmethod mime-message ((msg flexi-stream))
+  (read-mime-message msg))
+
+(defmethod mime-message ((msg stream))
+  (read-mime-message (make-flexi-stream msg)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -801,15 +788,16 @@ returns a MIME-MESSAGE object."
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (defmethod make-encoded-body-stream ((part mime-bodily-part))
-  (be body (mime-body part)
+  (let ((body (mime-body part)))
     (make-instance (case (mime-encoding part)
                      (:base64
                       'base64-encoder-input-stream)
                      (:quoted-printable
                       'quoted-printable-encoder-input-stream)
-                     (t
+                     (otherwise
                       '8bit-encoder-input-stream))
-                   :stream (make-instance 'binary-input-adapter-stream :source body))))
+                   :underlying-stream
+                   (make-input-adapter body))))
 
 (defun choose-boundary (parts &optional default)
   (labels ((match-in-parts (boundary parts)
@@ -841,7 +829,7 @@ returns a MIME-MESSAGE object."
 
 ;; fall back method
 (defmethod mime-part-size ((part mime-part))
-  (be body (mime-body part)
+  (let ((body (mime-body part)))
     (typecase body
       (pathname
        (file-size body))
@@ -868,7 +856,7 @@ returns a MIME-MESSAGE object."
   (case (mime-subtype part)
     (:alternative
      ;; try to choose something simple to print or the first thing
-     (be parts (mime-parts part)
+     (let ((parts (mime-parts part)))
        (print-mime-part (or (find-if #'(lambda (part)
                                          (and (eq (class-of part) (find-class 'mime-text))
                                               (eq (mime-subtype part) :plain)))
@@ -882,7 +870,7 @@ returns a MIME-MESSAGE object."
 ;; because we don't know which one we should use.  Messages written in
 ;; anything but ASCII will likely be unreadable -wcp11/10/07.
 (defmethod print-mime-part ((part mime-text) (out stream))
-  (be body (mime-body part)
+  (let ((body (mime-body part)))
     (etypecase body
       (string
        (write-string body out))
@@ -936,8 +924,8 @@ second in MIME."))
 (defmethod find-mime-part-by-path ((part mime-multipart) path)
   (if (null path)
       part
-      (be parts (mime-parts part)
-          part-number (car path)
+      (let ((parts (mime-parts part))
+            (part-number (car path)))
         (if (<= 1 part-number (length parts))
             (find-mime-part-by-path (nth (1- (car path)) (mime-parts part)) (cdr path))
             (error "~S has just ~D subparts, but part ~D was requested (parts are enumerated base 1)."
@@ -965,7 +953,7 @@ is a string."))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(defmethod find-mime-text-part (msg)
+(defgeneric find-mime-text-part (msg)
   (:documentation
    "Return message if it is a text message or first text part.
    If no suitable text part is found, return NIL."))
diff --git a/third_party/lisp/mime4cl/package.lisp b/third_party/lisp/mime4cl/package.lisp
index b1217a0b68..94b9e6b390 100644
--- a/third_party/lisp/mime4cl/package.lisp
+++ b/third_party/lisp/mime4cl/package.lisp
@@ -23,13 +23,7 @@
 
 (defpackage :mime4cl
   (:nicknames :mime)
-  (:use :common-lisp :npg :sclf :trivial-gray-streams)
-  ;; this is stuff that comes from SCLF and clashes with CMUCL's EXT
-  ;; package
-  (:shadowing-import-from :sclf
-                          #:process-wait
-                          #:process-alive-p
-                          #:run-program)
+  (:use :common-lisp :npg :mime4cl-ex-sclf :trivial-gray-streams :flexi-streams)
   (:export #:*lazy-mime-decode*
            #:print-mime-part
            #:read-mime-message
@@ -61,6 +55,7 @@
            #:mime-type-string
            #:mime-type-parameters
            #:mime-message-headers
+           #:mime-message-header-values
            #:mime=
            #:find-mime-part-by-path
            #:find-mime-part-by-id
@@ -71,11 +66,10 @@
            #:decode-quoted-printable-string
            #:encode-quoted-printable-stream
            #:encode-quoted-printable-sequence
-           #:decode-base64-stream
-           #:decode-base64-string
            #:encode-base64-stream
            #:encode-base64-sequence
            #:parse-RFC2047-text
+           #:decode-RFC2047
            #:parse-RFC822-header
            #:read-RFC822-headers
            #:time-RFC822-string
@@ -88,7 +82,6 @@
            #:with-input-from-mime-body-stream
            ;; endec.lisp
            #:base64-encoder
-           #:base64-decoder
            #:null-encoder
            #:null-decoder
            #:byte-encoder
@@ -104,4 +97,7 @@
            ;; address.lisp
            #:parse-addresses #:mailboxes-only
            #:mailbox #:mbx-description #:mbx-user #:mbx-host #:mbx-domain #:mbx-domain-name #:mbx-address
-           #:mailbox-group #:mbxg-name #:mbxg-mailboxes))
+           #:mailbox-group #:mbxg-name #:mbxg-mailboxes
+           ;; streams.lisp
+           #:redirect-stream
+           ))
diff --git a/third_party/lisp/mime4cl/streams.lisp b/third_party/lisp/mime4cl/streams.lisp
index d175a5d04f..71a32d84e4 100644
--- a/third_party/lisp/mime4cl/streams.lisp
+++ b/third_party/lisp/mime4cl/streams.lisp
@@ -1,7 +1,7 @@
 ;;; streams.lisp --- En/De-coding Streams
 
 ;;; Copyright (C) 2012 by Walter C. Pelissero
-;;; Copyright (C) 2021-2022 by the TVL Authors
+;;; Copyright (C) 2021-2023 by the TVL Authors
 
 ;;; Author: Walter C. Pelissero <walter@pelissero.de>
 ;;; Project: mime4cl
@@ -21,9 +21,17 @@
 
 (in-package :mime4cl)
 
+(defun flexi-stream-root-stream (stream)
+  "Return the non FLEXI-STREAM stream a given chain of FLEXI-STREAMs is based on."
+  (if (typep stream 'flexi-stream)
+      (flexi-stream-root-stream (flexi-stream-stream stream))
+      stream))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
 (defclass coder-stream-mixin ()
   ((real-stream :type stream
-                :initarg :stream
+                :initarg :underlying-stream
                 :reader real-stream)
    (dont-close :initform nil
                :initarg :dont-close)))
@@ -39,9 +47,12 @@
 (defclass coder-output-stream-mixin (fundamental-binary-output-stream coder-stream-mixin)
   ())
 
+;; TODO(sterni): temporary, ugly measure to make flexi-streams happy
+(defmethod stream-element-type ((stream coder-input-stream-mixin))
+  (declare (ignore stream))
+  '(unsigned-byte 8))
 
 (defclass quoted-printable-decoder-stream (coder-input-stream-mixin quoted-printable-decoder) ())
-(defclass base64-decoder-stream (coder-input-stream-mixin base64-decoder) ())
 (defclass 8bit-decoder-stream (coder-input-stream-mixin 8bit-decoder) ())
 
 (defclass quoted-printable-encoder-stream (coder-output-stream-mixin quoted-printable-encoder) ())
@@ -52,7 +63,7 @@
 
 (defmethod initialize-instance :after ((stream coder-stream-mixin) &key &allow-other-keys)
   (unless (slot-boundp stream 'real-stream)
-    (error "REAL-STREAM is unbound.  Must provide a :STREAM argument.")))
+    (error "REAL-STREAM is unbound.  Must provide a :UNDERLYING-STREAM argument.")))
 
 (defmethod initialize-instance ((stream coder-output-stream-mixin) &key &allow-other-keys)
   (call-next-method)
@@ -119,7 +130,7 @@ in a stream of character."))
   (with-slots (encoder buffer-queue real-stream) stream
     (loop
        while (queue-empty-p buffer-queue)
-       do (be byte (read-byte real-stream nil)
+       do (let ((byte (read-byte real-stream nil)))
             (if byte
                 (encoder-write-byte encoder byte)
                 (progn
@@ -136,200 +147,128 @@ in a stream of character."))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(defclass input-adapter-stream ()
-  ((source :initarg :source)
-   (real-stream)
-   (input-function)))
-
-(defclass binary-input-adapter-stream (fundamental-binary-input-stream input-adapter-stream) ())
-
-(defclass character-input-adapter-stream (fundamental-character-input-stream input-adapter-stream) ())
-
-(defmethod stream-element-type ((stream binary-input-adapter-stream))
-  '(unsigned-byte 8))
-
-(defmethod initialize-instance ((stream input-adapter-stream) &key &allow-other-keys)
-  (call-next-method)
-  (assert (slot-boundp stream 'source)))
-
-(defmethod initialize-instance ((stream binary-input-adapter-stream) &key &allow-other-keys)
-  (call-next-method)
-  ;; REAL-STREAM slot is set only if we are going to close it later on
-  (with-slots (source real-stream input-function) stream
-    (etypecase source
-      (string
-       (setf real-stream (make-string-input-stream source)
-             input-function #'(lambda ()
-                                (awhen (read-char real-stream nil)
-                                  (char-code it)))))
-      ((vector (unsigned-byte 8))
-       (be i 0
-         (setf input-function #'(lambda ()
-                                  (when (< i (length source))
-                                    (prog1 (aref source i)
-                                      (incf i)))))))
-      (stream
-       (assert (input-stream-p source))
-       (setf input-function (if (subtypep (stream-element-type source) 'character)
-                                #'(lambda ()
-                                    (awhen (read-char source nil)
-                                      (char-code it)))
-                                #'(lambda ()
-                                    (read-byte source nil)))))
-      (pathname
-       (setf real-stream (open source :element-type '(unsigned-byte 8))
-             input-function #'(lambda ()
-                                (read-byte real-stream nil))))
-      (file-portion
-       (setf real-stream (open-decoded-file-portion source)
-             input-function #'(lambda ()
-                                (read-byte real-stream nil)))))))
-
-(defmethod initialize-instance ((stream character-input-adapter-stream) &key &allow-other-keys)
-  (call-next-method)
-  ;; REAL-STREAM slot is set only if we are going to close later on
-  (with-slots (source real-stream input-function) stream
-    (etypecase source
-      (string
-       (setf real-stream (make-string-input-stream source)
-             input-function #'(lambda ()
-                                (read-char real-stream nil))))
-      ((vector (unsigned-byte 8))
-       (be i 0
-         (setf input-function #'(lambda ()
-                                  (when (< i (length source))
-                                    (prog1 (code-char (aref source i))
-                                      (incf i)))))))
-      (stream
-       (assert (input-stream-p source))
-       (setf input-function (if (subtypep (stream-element-type source) 'character)
-                                #'(lambda ()
-                                    (read-char source nil))
-                                #'(lambda ()
-                                    (awhen (read-byte source nil)
-                                      (code-char it))))))
-      (pathname
-       (setf real-stream (open source :element-type 'character)
-             input-function #'(lambda ()
-                                (read-char real-stream nil))))
-      (file-portion
-       (setf real-stream (open-decoded-file-portion source)
-             input-function #'(lambda ()
-                                (awhen (read-byte real-stream nil)
-                                  (code-char it))))))))
-
-(defmethod close ((stream input-adapter-stream) &key abort)
-  (when (slot-boundp stream 'real-stream)
-    (with-slots (real-stream) stream
-      (close real-stream :abort abort))))
-
-(defmethod stream-read-byte ((stream binary-input-adapter-stream))
-  (with-slots (input-function) stream
-    (or (funcall input-function)
-        :eof)))
-
-(defmethod stream-read-char ((stream character-input-adapter-stream))
-  (with-slots (input-function) stream
-    (or (funcall input-function)
-        :eof)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defclass delimited-input-stream (fundamental-character-input-stream coder-stream-mixin)
-  ((start-offset :initarg :start
-                 :initform 0
-                 :reader stream-start
-                 :type integer)
-   (end-offset :initarg :end
-               :initform nil
-               :reader stream-end
-               :type (or null integer))))
-
-(defmethod print-object ((object delimited-input-stream) stream)
-  (if *print-readably*
-      (call-next-method)
-      (with-slots (start-offset end-offset) object
-        (print-unreadable-object (object stream :type t :identity t)
-          (format stream "start=~A end=~A" start-offset end-offset)))))
-
-(defun base-stream (stream)
-  (if (typep stream 'delimited-input-stream)
-      (base-stream (real-stream stream))
-      stream))
-
-(defmethod initialize-instance ((stream delimited-input-stream) &key &allow-other-keys)
-  (call-next-method)
-  (unless (slot-boundp stream 'real-stream)
-    (error "REAL-STREAM is unbound.  Must provide a :STREAM argument."))
-  (with-slots (start-offset) stream
-    (when start-offset
-      (file-position stream start-offset))))
-
-(defmethod stream-read-char ((stream delimited-input-stream))
-  (with-slots (real-stream end-offset) stream
-    (if (or (not end-offset)
-            (< (file-position real-stream) end-offset))
-        (or (read-char real-stream nil)
-            :eof)
-        :eof)))
-
-#+(OR)(defmethod stream-read-byte ((stream delimited-input-stream))
-  (with-slots (real-stream end-offset) stream
-    (if (or (not end-offset)
-            (< (file-position real-stream) end-offset))
-        (or (read-byte real-stream nil)
-            :eof)
-        :eof)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defclass my-string-input-stream (fundamental-character-input-stream coder-stream-mixin)
-  ((string :initarg :string
-           :reader stream-string)))
-
-(defmethod initialize-instance ((stream my-string-input-stream) &key &allow-other-keys)
+(defun make-custom-flexi-stream (class stream other-args)
+  (apply #'make-instance
+         class
+         :stream stream
+         (mapcar (lambda (x)
+                   ;; make-flexi-stream has a discrepancy between :initarg of
+                   ;; make-instance and its &key which we mirror here.
+                   (if (eq x :external-format) :flexi-stream-external-format x))
+                 other-args)))
+
+(defclass adapter-flexi-input-stream (flexi-input-stream)
+  ((ignore-close
+    :initform nil
+    :initarg :ignore-close
+    :documentation
+    "If T, calling CLOSE on the stream does nothing.
+If NIL, the underlying stream is closed."))
+  (:documentation "FLEXI-STREAM that does not close the underlying stream on
+CLOSE if :IGNORE-CLOSE is T."))
+
+(defmethod close ((stream adapter-flexi-input-stream) &key abort)
+  (declare (ignore abort))
+  (with-slots (ignore-close) stream
+    (unless ignore-close
+      (call-next-method))))
+
+(defun make-input-adapter (source)
+  (etypecase source
+    ;; If it's already a stream, we need to make sure it's not closed by the adapter
+    (stream
+     (assert (input-stream-p source))
+     (if (and (typep source 'adapter-flexi-input-stream)
+              (slot-value source 'ignore-close))
+         source ; already ignores CLOSE
+         (make-adapter-flexi-input-stream source :ignore-close t)))
+    ;; TODO(sterni): is this necessary? (maybe with (not *lazy-mime-decode*)?)
+    (string
+     (make-input-adapter (string-to-octets source)))
+    ((vector (unsigned-byte 8))
+     (make-in-memory-input-stream source))
+    (pathname
+     (make-flexi-stream (open source :element-type '(unsigned-byte 8))))
+    (file-portion
+     (open-decoded-file-portion source))))
+
+(defun make-adapter-flexi-input-stream (stream &rest args)
+  "Create a ADAPTER-FLEXI-INPUT-STREAM. Accepts the same keyword arguments as
+MAKE-FLEXI-STREAM as well as :IGNORE-CLOSE. If T, the underlying stream is not
+closed."
+  (make-custom-flexi-stream 'adapter-flexi-input-stream stream args))
+
+(defclass positioned-flexi-input-stream (adapter-flexi-input-stream)
+  ()
+  (:documentation
+   "FLEXI-INPUT-STREAM that automatically advances the underlying :STREAM to
+the location given by :POSITION. This uses FILE-POSITION internally, so it'll
+only works if the underlying stream position is tracked in bytes. Note that
+the underlying stream is still advanced, so having multiple instances of
+POSITIONED-FLEXI-INPUT-STREAM based with the same underlying stream won't work
+reliably.
+Also supports :IGNORE-CLOSE of ADAPTER-FLEXI-INPUT-STREAM."))
+
+(defmethod initialize-instance ((stream positioned-flexi-input-stream)
+                                &key &allow-other-keys)
   (call-next-method)
-  (assert (slot-boundp stream 'string))
-  (with-slots (string real-stream) stream
-    (setf real-stream (make-string-input-stream string))))
-
-(defmethod stream-read-char ((stream my-string-input-stream))
-  (with-slots (real-stream) stream
-    (or (read-char real-stream nil)
-        :eof)))
+  ;; The :POSITION initarg is only informational for flexi-streams: It assumes
+  ;; it is were the stream it got is already at and continuously updates it
+  ;; for querying (via FLEXI-STREAM-POSITION) and bound checking.
+  ;; Since we have streams that are not positioned correctly, we need to do this
+  ;; here using FILE-POSITION. Note that assumes the underlying implementation
+  ;; uses bytes for FILE-POSITION which is not guaranteed (probably some streams
+  ;; even in SBCL don't).
+  (file-position (flexi-stream-stream stream) (flexi-stream-position stream)))
+
+(defun make-positioned-flexi-input-stream (stream &rest args)
+  "Create a POSITIONED-FLEXI-INPUT-STREAM. Accepts the same keyword arguments as
+MAKE-FLEXI-STREAM as well as :IGNORE-CLOSE. Causes the FILE-POSITION of STREAM to
+be modified to match the :POSITION argument."
+  (make-custom-flexi-stream 'positioned-flexi-input-stream stream args))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+;; TODO(sterni): test correct behavior with END NIL
 (defstruct file-portion
-  data					;  string or a pathname
+  data                                  ; string or a pathname
   encoding
   start
   end)
 
-(defun open-file-portion (file-portion)
-  (be data (file-portion-data file-portion)
-    (etypecase data
-      (pathname
-       (be stream (open data)
-         (make-instance 'delimited-input-stream
-                        :stream stream
-                        :start (file-portion-start file-portion)
-                        :end (file-portion-end file-portion))))
-      (string
-       (make-instance 'delimited-input-stream
-                      :stream (make-string-input-stream data)
-                      :start (file-portion-start file-portion)
-                      :end (file-portion-end file-portion)))
-      (stream
-       (make-instance 'delimited-input-stream
-                      :stream data
-                      :dont-close t
-                      :start (file-portion-start file-portion)
-                      :end (file-portion-end file-portion))))))
-
 (defun open-decoded-file-portion (file-portion)
-  (make-instance (case (file-portion-encoding file-portion)
-                   (:quoted-printable 'quoted-printable-decoder-stream)
-                   (:base64 'base64-decoder-stream)
-                   (t '8bit-decoder-stream))
-                 :stream (open-file-portion file-portion)))
+  (with-slots (data encoding start end)
+      file-portion
+    (let* ((binary-stream
+             (etypecase data
+               (pathname
+                (open data :element-type '(unsigned-byte 8)))
+               ((vector (unsigned-byte 8))
+                (flexi-streams:make-in-memory-input-stream data))
+               (stream
+                ;; TODO(sterni): assert that bytes/flexi-stream
+                data)))
+           (params (ccase encoding
+                     ((:quoted-printable :base64) '(:external-format :us-ascii))
+                     (:8bit '(:element-type (unsigned-byte 8)))
+                     (:7bit '(:external-format :us-ascii))))
+           (portion-stream (apply #'make-positioned-flexi-input-stream
+                                  binary-stream
+                                  :position start
+                                  :bound end
+                                  ;; if data is a stream we can't have a
+                                  ;; FILE-PORTION without modifying it when
+                                  ;; reading etc. The least we can do, though,
+                                  ;; is forgo destroying it.
+                                  :ignore-close (typep data 'stream)
+                                  params))
+           (needs-decoder-stream (member encoding '(:quoted-printable
+                                                    :base64))))
+
+      (if needs-decoder-stream
+          (make-instance
+           (ccase encoding
+             (:quoted-printable 'quoted-printable-decoder-stream)
+             (:base64 'qbase64:decode-stream))
+           :underlying-stream portion-stream)
+          portion-stream))))
diff --git a/third_party/lisp/mime4cl/test/endec.lisp b/third_party/lisp/mime4cl/test/endec.lisp
index 5e8d43a7d4..6b22b3f6a2 100644
--- a/third_party/lisp/mime4cl/test/endec.lisp
+++ b/third_party/lisp/mime4cl/test/endec.lisp
@@ -103,13 +103,12 @@ line")
 
 (deftest base64.3
     (map 'string #'code-char
-         (decode-base64-string "U29tZSByYW5kb20gc3RyaW5nLg=="))
+         (qbase64:decode-string "U29tZSByYW5kb20gc3RyaW5nLg=="))
   "Some random string.")
 
 (deftest base64.4
     (map 'string #'code-char
-         (decode-base64-string "some rubbish U29tZSByYW5kb20gc3RyaW5nLg== more rubbish"
-                               :start 13 :end 41))
+         (qbase64:decode-string "U29tZSByYW5kb20gc3RyaW5nLg=="))
   "Some random string.")
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -118,6 +117,26 @@ line")
     (parse-RFC2047-text "foo bar")
   ("foo bar"))
 
+;; from RFC2047 section 8
+(deftest RFC2047.2
+    (decode-RFC2047 "=?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>")
+  "Keith Moore <moore@cs.utk.edu>")
+
+;; from RFC2047 section 8
+(deftest RFC2047.3
+    (decode-RFC2047 "=?ISO-8859-1?Q?Olle_J=E4rnefors?=")
+  "Olle JΓ€rnefors")
+
+;; from RFC2047 section 8
+(deftest RFC2047.4
+    (decode-RFC2047 "Nathaniel Borenstein <nsb@thumper.bellcore.com> (=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?=)")
+  "Nathaniel Borenstein <nsb@thumper.bellcore.com> (ΧΧ•ΧœΧ© ΧŸΧ‘ Χ™ΧœΧ˜Χ€Χ )")
+
+;; from RFC2047 section 8
+(deftest RFC2047.5
+  (decode-RFC2047 "=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>")
+  "Keld JΓΈrn Simonsen <keld@dkuug.dk>")
+
 (defun perftest-encoder (encoder-class &optional (megs 100))
   (declare (optimize (speed 3) (debug 0) (safety 0))
            (type fixnum megs))
@@ -139,13 +158,12 @@ line")
   (declare (optimize (speed 3) (debug 0) (safety 0))
            (type fixnum megs))
   (with-open-file (in #P"/dev/random" :element-type '(unsigned-byte 8))
-    (let ((sclf:*tmp-file-defaults* (make-pathname :defaults #.(or *load-pathname* *compile-file-pathname*)
+    (let ((*tmp-file-defaults* (make-pathname :defaults #.(or *load-pathname* *compile-file-pathname*)
                                                    :type "encoded-data")))
-      (sclf:with-temp-file (tmp nil :direction :io)
+      (with-temp-file (tmp nil :direction :io)
         (let* ((meg (* 1024 1024))
                (buffer (make-sequence '(vector (unsigned-byte 8)) meg))
                (encoder-class (ecase decoder-class
-                                (mime4cl:base64-decoder 'mime4cl:base64-encoder)
                                 (mime4cl:quoted-printable-decoder 'mime4cl:quoted-printable-encoder)))
                (encoder (make-instance encoder-class
                                        :output-function #'(lambda (c)
diff --git a/third_party/lisp/mime4cl/test/mime.lisp b/third_party/lisp/mime4cl/test/mime.lisp
index 8d93978599..dbd1dd996d 100644
--- a/third_party/lisp/mime4cl/test/mime.lisp
+++ b/third_party/lisp/mime4cl/test/mime.lisp
@@ -1,7 +1,7 @@
 ;;; mime.lisp --- MIME regression tests
 
 ;;; Copyright (C) 2012 by Walter C. Pelissero
-;;; Copyright (C) 2021-2022 by the TVL Authors
+;;; Copyright (C) 2021-2023 by the TVL Authors
 
 ;;; Author: Walter C. Pelissero <walter@pelissero.de>
 ;;; Project: mime4cl
@@ -27,28 +27,15 @@
                          *load-pathname*
                          #P"")))
 
-(defvar *sample1-file* (make-pathname :defaults #.(or *compile-file-pathname*
-                                                      *load-pathname*)
-                                      :name "sample1"
-                                      :type "msg"))
-
-(deftest mime.1
-    (let* ((orig (mime-message *sample1-file*))
-           (dup (mime-message (with-output-to-string (out) (encode-mime-part orig out)))))
-      (mime= orig dup))
-  t)
-
-(deftest mime.2
-    (loop
-       for f in (directory (make-pathname :defaults *samples-directory*
-                                          :name :wild
-                                          :type "txt"))
-       do
-         (format t "~A:~%" f)
-         (finish-output)
-         (let* ((orig (mime-message f))
-                (dup (mime-message (with-output-to-string (out) (encode-mime-part orig out)))))
-           (unless (mime= orig dup)
-             (return nil)))
-       finally (return t))
-  t)
+(loop
+ for f in (directory (make-pathname :defaults *samples-directory*
+                                    :name :wild
+                                    :type "msg"))
+ for i from 1
+ do
+ (add-test (intern (format nil "MIME.~A" i))
+           `(let* ((orig (mime-message ,f))
+                   (dup (mime-message
+                         (with-output-to-string (out) (encode-mime-part orig out)))))
+              (mime= orig dup))
+           t))
diff --git a/third_party/lisp/mime4cl/test/package.lisp b/third_party/lisp/mime4cl/test/package.lisp
index 6da1fc8fa2..965680448f 100644
--- a/third_party/lisp/mime4cl/test/package.lisp
+++ b/third_party/lisp/mime4cl/test/package.lisp
@@ -23,5 +23,5 @@
 
 (defpackage :mime4cl-tests
   (:use :common-lisp
-        :rtest :mime4cl)
+        :rtest :mime4cl :mime4cl-ex-sclf)
   (:export))
diff --git a/third_party/lisp/mime4cl/test/rt.lisp b/third_party/lisp/mime4cl/test/rt.lisp
index 06160debbe..3f3aa5c56c 100644
--- a/third_party/lisp/mime4cl/test/rt.lisp
+++ b/third_party/lisp/mime4cl/test/rt.lisp
@@ -1,5 +1,6 @@
 #|----------------------------------------------------------------------------|
  | Copyright 1990 by the Massachusetts Institute of Technology, Cambridge MA. |
+ | Copyright 2023 by the TVL Authors                                          |
  |                                                                            |
  | Permission  to  use,  copy, modify, and distribute this software  and  its |
  | documentation for any purpose  and without fee is hereby granted, provided |
@@ -20,10 +21,10 @@
  |----------------------------------------------------------------------------|#
 
 (defpackage #:regression-test
-  (:nicknames #:rtest #-lispworks #:rt) 
+  (:nicknames #:rtest #-lispworks #:rt)
   (:use #:cl)
   (:export #:*do-tests-when-defined* #:*test* #:continue-testing
-           #:deftest #:do-test #:do-tests #:get-test #:pending-tests
+           #:deftest #:add-test #:do-test #:do-tests #:get-test #:pending-tests
            #:rem-all-tests #:rem-test)
   (:documentation "The MIT regression tester with pfdietz's modifications"))
 
@@ -86,25 +87,28 @@
 (defmacro deftest (name form &rest values)
   `(add-entry '(t ,name ,form .,values)))
 
+(defun add-test (name form &rest values)
+  (funcall #'add-entry (append (list 't name form) values)))
+
 (defun add-entry (entry)
   (setq entry (copy-list entry))
   (do ((l *entries* (cdr l))) (nil)
     (when (null (cdr l))
       (setf (cdr l) (list entry))
       (return nil))
-    (when (equal (name (cadr l)) 
+    (when (equal (name (cadr l))
                  (name entry))
       (setf (cadr l) entry)
       (report-error nil
-        "Redefining test ~:@(~S~)"
-        (name entry))
+                    "Redefining test ~:@(~S~)"
+                    (name entry))
       (return nil)))
   (when *do-tests-when-defined*
     (do-entry entry))
   (setq *test* (name entry)))
 
 (defun report-error (error? &rest args)
-  (cond (*debug* 
+  (cond (*debug*
          (apply #'format t args)
          (if error? (throw '*debug* nil)))
         (error? (apply #'error args))
@@ -184,7 +188,7 @@
       (setf (pend entry)
             (or aborted
                 (not (equalp-with-case r (vals entry)))))
-      
+
       (when (pend entry)
         (let ((*print-circle* *print-circle-on-failure*))
           (format s "~&Test ~:@(~S~) failed~
@@ -210,7 +214,7 @@
     (setf (pend entry) t))
   (if (streamp out)
       (do-entries out)
-      (with-open-file 
+      (with-open-file
           (stream out :direction :output)
         (do-entries stream))))
 
diff --git a/third_party/lisp/mime4cl/test/sample1.msg b/third_party/lisp/mime4cl/test/samples/sample1.msg
index 662a9fab34..662a9fab34 100644
--- a/third_party/lisp/mime4cl/test/sample1.msg
+++ b/third_party/lisp/mime4cl/test/samples/sample1.msg
diff --git a/third_party/lisp/mime4cl/test/temp-file.lisp b/third_party/lisp/mime4cl/test/temp-file.lisp
new file mode 100644
index 0000000000..554f35844b
--- /dev/null
+++ b/third_party/lisp/mime4cl/test/temp-file.lisp
@@ -0,0 +1,72 @@
+;;; temp-file.lisp --- temporary file creation
+
+;;;  Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 by Walter C. Pelissero
+;;;  Copyright (C) 2022 The TVL Authors
+
+;;;  Author: Walter C. Pelissero <walter@pelissero.de>
+;;;  Project: mime4cl
+;;;
+;;;  Code taken from SCLF
+
+#+cmu (ext:file-comment "$Module: temp-file.lisp $")
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public License
+;;; as published by the Free Software Foundation; either version 2.1
+;;; of the License, or (at your option) any later version.
+;;; This library is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;; Lesser General Public License for more details.
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free
+;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+;;; 02111-1307 USA
+
+(in-package :mime4cl-tests)
+
+(defvar *tmp-file-defaults* #P"/tmp/")
+
+(defun temp-file-name (&optional (default *tmp-file-defaults*))
+  "Create a random pathname based on DEFAULT.  No effort is made
+to make sure that the returned pathname doesn't identify an
+already existing file.  If missing DEFAULT defaults to
+*TMP-FILE-DEFAULTS*."
+  (make-pathname :defaults default
+                 :name (format nil "~36R" (random #.(expt 36 10)))))
+
+(defun open-temp-file (&optional default-pathname &rest open-args)
+  "Open a new temporary file and return a stream to it.  This function
+makes sure the pathname of the temporary file is unique.  OPEN-ARGS
+are arguments passed verbatim to OPEN.  If OPEN-ARGS specify
+the :DIRECTION it should be either :OUTPUT (default) or :IO;
+any other value causes an error.  If DEFAULT-PATHNAME is specified and
+not NIL it's used as defaults to produce the pathname of the temporary
+file, otherwise *TMP-FILE-DEFAULTS* is used."
+  (unless default-pathname
+    (setf default-pathname *tmp-file-defaults*))
+  ;; if :DIRECTION is specified check that it's compatible with the
+  ;; purpose of this function, otherwise make it default to :OUTPUT
+  (aif (getf open-args :direction)
+       (unless (member it '(:output :io))
+         (error "Can't create temporary file with open direction ~A." it))
+       (setf open-args (append '(:direction :output)
+                               open-args)))
+  (do* ((name #1=(temp-file-name default-pathname) #1#)
+        (stream #2=(apply #'open  name
+                          :if-exists nil
+                          :if-does-not-exist :create
+                          open-args) #2#))
+       (stream stream)))
+
+(defmacro with-temp-file ((stream &rest open-temp-args) &body body)
+  "Execute BODY within a dynamic extent where STREAM is bound to
+a STREAM open on a unique temporary file name.  OPEN-TEMP-ARGS are
+passed verbatim to OPEN-TEMP-FILE."
+  `(let ((,stream (open-temp-file ,@open-temp-args)))
+     (unwind-protect
+          (progn ,@body)
+       (close ,stream)
+       ;; body may decide to rename the file so we must ignore the errors
+       (ignore-errors
+         (delete-file (pathname ,stream))))))
diff --git a/third_party/lisp/nibbles.nix b/third_party/lisp/nibbles.nix
index 3c0a75e46d..b71f439c93 100644
--- a/third_party/lisp/nibbles.nix
+++ b/third_party/lisp/nibbles.nix
@@ -3,7 +3,8 @@
 let
   inherit (depot.nix.buildLisp) bundled;
   src = with pkgs; srcOnly lispPackages.nibbles;
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "nibbles";
 
   deps = with depot.third_party.lisp; [
diff --git a/third_party/lisp/npg/OWNERS b/third_party/lisp/npg/OWNERS
index f16dd105d7..2e95807063 100644
--- a/third_party/lisp/npg/OWNERS
+++ b/third_party/lisp/npg/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - sterni
+sterni
diff --git a/third_party/lisp/postmodern.nix b/third_party/lisp/postmodern.nix
index b2ea318f8a..25e0625c20 100644
--- a/third_party/lisp/postmodern.nix
+++ b/third_party/lisp/postmodern.nix
@@ -88,6 +88,7 @@ let
     ];
   };
 
-in postmodern // {
+in
+postmodern // {
   inherit s-sql cl-postgres;
 }
diff --git a/third_party/lisp/qbase64/coreutils-base64.patch b/third_party/lisp/qbase64/coreutils-base64.patch
new file mode 100644
index 0000000000..5a2f2a9f08
--- /dev/null
+++ b/third_party/lisp/qbase64/coreutils-base64.patch
@@ -0,0 +1,13 @@
+diff --git a/qbase64-test.lisp b/qbase64-test.lisp
+index 310fdf3..b92abb5 100644
+--- a/qbase64-test.lisp
++++ b/qbase64-test.lisp
+@@ -14,7 +14,7 @@
+       (with-open-temporary-file (tmp :direction :output :element-type '(unsigned-byte 8))
+         (write-sequence bytes tmp)
+         (force-output tmp)
+-        (let* ((encoded (uiop:run-program `("base64" "-b" ,(format nil "~A" linebreak) "-i" ,(namestring tmp)) :output (if (zerop linebreak) '(:string :stripped t) :string)))
++        (let* ((encoded (uiop:run-program `("base64" "-w" ,(format nil "~A" linebreak) ,(namestring tmp)) :output (if (zerop linebreak) '(:string :stripped t) :string) :error-output *error-output*))
+                (length (length encoded)))
+           (cond ((and (> length 1)
+                       (string= (subseq encoded (- length 2))
diff --git a/third_party/lisp/qbase64/default.nix b/third_party/lisp/qbase64/default.nix
new file mode 100644
index 0000000000..40a93e04f0
--- /dev/null
+++ b/third_party/lisp/qbase64/default.nix
@@ -0,0 +1,57 @@
+{ depot, pkgs, ... }:
+
+let
+  src = pkgs.applyPatches {
+    src = pkgs.fetchFromGitHub {
+      owner = "chaitanyagupta";
+      repo = "qbase64";
+      rev = "4ac193ed6b35a867ca453ed74acc128c9a077407";
+      sha256 = "06daqqfdd51wkx0pyxgz7zq4ibzsqsgn3qs04jabx67gyybgnmjm";
+    };
+
+    patches = [
+      # qbase64 expects macOS base64
+      ./coreutils-base64.patch
+    ];
+  };
+
+  getSrcs = builtins.map (p: "${src}/${p}");
+
+in
+
+depot.nix.buildLisp.library {
+  name = "qbase64";
+
+  srcs = getSrcs [
+    "package.lisp"
+    "utils.lisp"
+    "stream-utils.lisp"
+    "qbase64.lisp"
+  ];
+
+  deps = [
+    depot.third_party.lisp.trivial-gray-streams
+    depot.third_party.lisp.metabang-bind
+  ];
+
+  tests = {
+    name = "qbase64-tests";
+
+    srcs = getSrcs [
+      "qbase64-test.lisp"
+    ];
+
+    deps = [
+      {
+        sbcl = depot.nix.buildLisp.bundled "uiop";
+        default = depot.nix.buildLisp.bundled "asdf";
+      }
+      depot.third_party.lisp.fiveam
+      depot.third_party.lisp.cl-fad
+    ];
+
+    expression = ''
+      (fiveam:run! '(qbase64-test::encoder 'qbase64-test::decoder))
+    '';
+  };
+}
diff --git a/third_party/lisp/routes.nix b/third_party/lisp/routes.nix
index a76912c651..fc7d4e3067 100644
--- a/third_party/lisp/routes.nix
+++ b/third_party/lisp/routes.nix
@@ -20,7 +20,8 @@ let
     ];
   };
 
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "routes";
 
   deps = with depot.third_party.lisp; [
diff --git a/third_party/lisp/s-xml/default.nix b/third_party/lisp/s-xml/default.nix
index 3cd13ffb6b..486e1c1ac8 100644
--- a/third_party/lisp/s-xml/default.nix
+++ b/third_party/lisp/s-xml/default.nix
@@ -1,15 +1,17 @@
 # XML serialiser for Common Lisp.
 { depot, pkgs, ... }:
 
-let src = pkgs.applyPatches {
-  name = "s-xml-source";
-  src = pkgs.lispPackages.s-xml.src;
+let
+  src = pkgs.applyPatches {
+    name = "s-xml-source";
+    src = pkgs.lispPackages.s-xml.src;
 
-  patches = [
-    ./0001-fix-definition-order-in-xml.lisp.patch
-  ];
-};
-in depot.nix.buildLisp.library {
+    patches = [
+      ./0001-fix-definition-order-in-xml.lisp.patch
+    ];
+  };
+in
+depot.nix.buildLisp.library {
   name = "s-xml";
 
   srcs = map (f: src + ("/src/" + f)) [
diff --git a/third_party/lisp/sclf/.skip-subtree b/third_party/lisp/sclf/.skip-subtree
deleted file mode 100644
index 5051f60d6b..0000000000
--- a/third_party/lisp/sclf/.skip-subtree
+++ /dev/null
@@ -1 +0,0 @@
-prevent readTree from creating entries for subdirs that don't contain an .nix files
diff --git a/third_party/lisp/sclf/OWNERS b/third_party/lisp/sclf/OWNERS
deleted file mode 100644
index f16dd105d7..0000000000
--- a/third_party/lisp/sclf/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherited: true
-owners:
-  - sterni
diff --git a/third_party/lisp/sclf/README b/third_party/lisp/sclf/README
deleted file mode 100644
index 2a1c2c3c5c..0000000000
--- a/third_party/lisp/sclf/README
+++ /dev/null
@@ -1,6 +0,0 @@
-SCLF has originally been written by Walter C. Pelissero and vendored
-into depot since it is a dependency of mime4cl. Upstream and depot version
-may diverge.
-
-Upstream Website: http://wcp.sdf-eu.org/software/#sclf
-Vendored Tarball: http://wcp.sdf-eu.org/software/sclf-20150207T213551.tbz
diff --git a/third_party/lisp/sclf/default.nix b/third_party/lisp/sclf/default.nix
deleted file mode 100644
index fb07f8f764..0000000000
--- a/third_party/lisp/sclf/default.nix
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (C) 2021 by the TVL Authors
-# SPDX-License-Identifier: LGPL-2.1-or-later
-{ depot, pkgs, ... }:
-
-depot.nix.buildLisp.library {
-  name = "sclf";
-
-  deps = [
-    (depot.nix.buildLisp.bundled "sb-posix")
-  ];
-
-  srcs = [
-    ./package.lisp
-    ./sclf.lisp
-    ./sysproc.lisp
-    ./lazy.lisp
-    ./time.lisp
-    ./directory.lisp
-    ./serial.lisp
-    ./mp/sbcl.lisp
-  ];
-
-  # TODO(sterni): implement OS interaction for ECL and CCL
-  brokenOn = [
-    "ecl"
-    "ccl"
-  ];
-}
diff --git a/third_party/lisp/sclf/directory.lisp b/third_party/lisp/sclf/directory.lisp
deleted file mode 100644
index 3e479c4ac2..0000000000
--- a/third_party/lisp/sclf/directory.lisp
+++ /dev/null
@@ -1,404 +0,0 @@
-;;;  directory.lisp --- filesystem directory access
-
-;;;  Copyright (C) 2006, 2007, 2008, 2009, 2010 by Walter C. Pelissero
-;;;  Copyright (C) 2021 by the TVL Authors
-
-;;;  Author: Walter C. Pelissero <walter@pelissero.de>
-;;;  Project: sclf
-
-#+cmu (ext:file-comment "$Module: directory.lisp $")
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 2.1
-;;; of the License, or (at your option) any later version.
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Lesser General Public License for more details.
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free
-;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-;;; 02111-1307 USA
-
-
-(cl:in-package :sclf)
-
-(defun pathname-as-directory (pathname)
-  "Converts PATHNAME to directory form and return it."
-  (setf pathname (pathname pathname))
-  (if (pathname-name pathname)
-      (make-pathname :directory (append (or (pathname-directory pathname)
-                                            '(:relative))
-                                        (list (file-namestring pathname)))
-                     :name nil
-                     :type nil
-                     :defaults pathname)
-      pathname))
-
-(defun d+ (path &rest rest)
-  "Concatenate directory pathname parts and return a pathname."
-  (make-pathname :defaults path
-                 :directory (append (pathname-directory path) rest)))
-
-(defun delete-directory (pathname)
-  "Remove directory PATHNAME.  Return PATHNAME."
-  #+cmu (multiple-value-bind (done errno)
-             (unix:unix-rmdir (namestring pathname))
-           (unless done
-             (error "Unable to delete directory ~A (errno=~A)"
-                    pathname errno)))
-  #+sbcl (sb-posix:rmdir pathname)
-  #+lispworks (lw:delete-directory pathname)
-  #-(or cmu sbcl)
-  (error "DELETE-DIRECTORY not implemented for you lisp system.")
-  pathname)
-
-(defun list-directory (pathname &key truenamep)
-  "List content of directory PATHNAME.  If TRUENAMEP is true don't try
-to follow symbolic links."
-  #-(or sbcl cmu) (declare (ignore truenamep))
-  (let (#+cmu (lisp::*ignore-wildcards* t))
-    (directory (make-pathname :defaults (pathname-as-directory pathname)
-                              :name :wild
-                              :type :wild
-                              :version :wild)
-               #+cmu :truenamep #+cmu truenamep
-               #+sbcl :resolve-symlinks #+sbcl truenamep)))
-
-(defun traverse-directory-tree (root-pathname proc &key truenamep test depth-first)
-  "Call PROC on all pathnames under ROOT-PATHNAME, both files and
-directories.  Unless TRUENAMEP is true, this function doesn't try
-to lookup the truename of files, as finding the truename may be a
-superfluous and noxious activity expecially when you expect
-broken symbolic links in your filesystem."
-  (check-type root-pathname pathname)
-  (check-type proc (or function symbol))
-  (check-type test (or function symbol null))
-  (labels ((ls (dir)
-             (declare (type pathname dir))
-             (list-directory dir :truenamep truenamep))
-           (traverse? (file)
-             (declare (type pathname file))
-             (and (not (pathname-name file))
-                  (or truenamep
-                      (not (symbolic-link-p file)))
-                  (or (not test)
-                      (funcall test file))))
-           (traverse-pre-order (dir)
-             (declare (type pathname dir))
-             (loop
-                for file in (ls dir)
-                do (funcall proc file)
-                when (traverse? file)
-                do (traverse-pre-order file)))
-           (traverse-post-order (dir)
-             (declare (type pathname dir))
-             (loop
-                for file in (ls dir)
-                when (traverse? file)
-                do (traverse-post-order file)
-                do (funcall proc file))))
-    (if depth-first
-        (traverse-post-order root-pathname)
-        (traverse-pre-order root-pathname))
-    (values)))
-
-(defmacro do-directory-tree ((file root-pathname &key truenamep test depth-first) &body body)
-  "Call TRAVERSE-DIRECTORY-TREE with BODY es procedure."
-  `(traverse-directory-tree ,root-pathname
-                            #'(lambda (,file)
-                                ,@body)
-                            :truenamep ,truenamep
-                            :test ,test
-                            :depth-first ,depth-first))
-
-(defun empty-directory-p (pathname)
-  (and (directory-p pathname)
-       (endp (list-directory pathname))))
-
-(defun remove-empty-directories (root)
-  (do-directory-tree (pathname root :depth-first t)
-    (when (empty-directory-p pathname)
-      (delete-directory pathname))))
-
-(defun map-directory-tree (pathname function)
-  "Apply FUNCTION to every file in a directory tree starting from
-PATHNAME.  Return the list of results."
-  (be return-list '()
-    (do-directory-tree (directory-entry pathname)
-      (push (funcall function directory-entry) return-list))
-    (nreverse return-list)))
-
-(defun find-files (root-pathname matcher-function &key truenamep)
-  "In the directory tree rooted at ROOT-PATHNAME, find files that
-when the pathname is applied to MATCHER-FUNCTION will return
-true.  Return the list of files found.  Unless TRUENAMEP is true
-this function doesn't try to lookup the truename of
-files. Finding the truename may be a superfluous and noxious
-activity expecially when you expect broken symbolic links in your
-filesystem.  (This may not apply to your particular lisp
-system.)"
-  (be files '()
-    (do-directory-tree (file root-pathname :truenamep truenamep)
-      (when (funcall matcher-function file)
-        (push file files)))
-    (nreverse files)))
-
-(defun delete-directory-tree (pathname)
-  "Recursively delete PATHNAME and all the directory structure below
-it.
-
-WARNING: depending on the way the DIRECTORY function is implemented on
-your Lisp system this function may follow Unix symbolic links and thus
-delete files outside the PATHNAME hierarchy.  Check this before using
-this function in your programs."
-  (if (pathname-name pathname)
-      (delete-file pathname)
-      (progn
-        (dolist (file (list-directory pathname))
-          (delete-directory-tree file))
-        (delete-directory pathname))))
-
-(defun make-directory (pathname &optional (mode #o777))
-  "Create a new directory in the filesystem.  Permissions MODE
-will be assigned to it.  Return PATHNAME."
-  #+cmu (multiple-value-bind (done errno)
-            (unix:unix-mkdir (native-namestring pathname) mode)
-          (unless done
-            (error "Unable to create directory ~A (errno=~A)." pathname errno)))
-  #+sbcl (sb-posix:mkdir pathname mode)
-  #-(or cmu sbcl)
-  (error "MAKE-DIRECTORY is not implemented for this Lisp system.")
-  pathname)
-
-;; At least on SBCL/CMUCL + Unix + NFS this function is faster than
-;; ENSURE-DIRECTORIES-EXIST, because it doesn't check all the pathname
-;; components starting from the root; it proceeds from the leaf and
-;; crawls the directory tree upward only if necessary."
-(defun ensure-directory (pathname &key verbose (mode #o777))
-  "Just like ENSURE-DIRECTORIES-EXIST but, in some situations,
-it's faster."
-  (labels ((ensure (path)
-             (unless (probe-file path)
-               (be* tail (last (pathname-directory path) 2)
-                    last (cdr tail)
-                 (setf (cdr tail) nil)
-                 (unwind-protect
-                      (ensure path)
-                   (setf (cdr tail) last))
-                 (make-directory path mode)
-                 (when verbose
-                   (format t "Created ~S~%" path))))))
-    (ensure (make-pathname :defaults pathname
-                           :name nil :type nil
-                           :version nil))))
-
-(defun make-temp-directory (&optional (default-pathname *tmp-file-defaults*) (mode #o777))
-  "Create a new directory and return its pathname.
-If DEFAULT-PATHNAME is specified and not NIL it's used as
-defaults to produce the pathname of the directory.  Return the
-pathname of the temporary directory."
-  (loop
-     for name = (pathname-as-directory (temp-file-name default-pathname))
-     when (ignore-errors (make-directory name mode))
-     return name))
-
-(defmacro with-temp-directory ((path &rest make-temp-directory-args) &body body)
-  "Execute BODY with PATH bound to the pathname of a new unique
-temporary directory.  On exit of BODY the directory tree starting from
-PATH will be automatically removed from the filesystem.  Return what
-BODY returns.  BODY is _not_ executed within the PATH directory; the
-working directory is never changed."
-  `(be ,path (make-temp-directory ,@make-temp-directory-args)
-     (unwind-protect
-          (progn ,@body)
-       (delete-directory-tree ,path))))
-
-(defun current-directory ()
-  "Return the pathname of the current directory."
-  (truename (make-pathname :directory '(:relative))))
-
-(defun ensure-home-translations ()
-  "Ensure that the logical pathname translations for the host \"home\"
-are defined."
-  ;; CMUCL already defines a HOME translation of its own and gets
-  ;; angry if we try to redefine it
-  #-cmu
-  (be home (user-homedir-pathname)
-    ;; we should discard and replace whatever has been defined in any
-    ;; rc file during compilation
-    (setf (logical-pathname-translations "home")
-          (list
-           (list "**;*.*.*"
-                 (make-pathname :defaults home
-                                :directory (append (pathname-directory home)
-                                                   '(:wild-inferiors))
-                                :name :wild
-                                :type :wild))))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun parse-native-namestring (string &optional host (defaults *default-pathname-defaults*)
-                                &key (start 0) end junk-allowed)
-  #+sbcl (sb-ext:parse-native-namestring string host defaults
-                                         :start start
-                                         :end end
-                                         :junk-allowed junk-allowed)
-  #-sbcl (let (#+cmu(lisp::*ignore-wildcards* t))
-           (parse-namestring string host defaults
-                             :start start
-                             :end end
-                             :junk-allowed junk-allowed)))
-
-(defun native-namestring (pathname)
-  #+sbcl (sb-ext:native-namestring pathname)
-  #-sbcl (let (#+cmu (lisp::*ignore-wildcards* t))
-           (namestring pathname)))
-
-(defun native-file-namestring (pathname)
-  #+sbcl (sb-ext:native-namestring
-          (make-pathname :name (pathname-name pathname)
-                         :type (pathname-type pathname)))
-  #+cmu (be lisp::*ignore-wildcards* t
-          (file-namestring pathname)))
-
-(defun native-pathname (thing)
-  #+sbcl (sb-ext:native-pathname thing)
-  #+cmu (be lisp::*ignore-wildcards* t
-          (pathname thing)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun bits-set-p (x bits)
-  (= (logand x bits)
-     bits))
-
-(defun directory-p (pathname)
-  "Return true if PATHNAME names a directory on the filesystem."
-  #-clisp (awhen (unix-stat (native-namestring pathname))
-            (bits-set-p (stat-mode it)
-                        #+sbcl sb-posix:s-ifdir
-                        #+cmu unix:s-ifdir))
-  #+clisp (ext:probe-directory (pathname-as-directory pathname)))
-
-(defun regular-file-p (pathname)
-  "Return true if PATHNAME names a regular file on the filesystem."
-  #-(or sbcl cmu) (error "don't know how to check whether a file might be a regular file")
-  (awhen (unix-stat (native-namestring pathname))
-    (bits-set-p (stat-mode it)
-                #+sbcl sb-posix:s-ifreg
-                #+cmu unix:s-ifreg)))
-
-(defun file-readable-p (pathname)
-  #+sbcl (sb-unix:unix-access (native-namestring pathname) sb-unix:r_ok)
-  #+cmu (unix:unix-access (native-namestring pathname) unix:r_ok)
-  #-(or sbcl cmu) (error "don't know how to check whether a file might be readable"))
-
-(defun file-writable-p (pathname)
-  #+sbcl (sb-unix:unix-access (native-namestring pathname) sb-unix:w_ok)
-  #+cmu (unix:unix-access (native-namestring pathname) unix:w_ok)
-  #-(or sbcl cmu) (error "don't know how to check whether a file might be writable"))
-
-(defun file-executable-p (pathname)
-  #+sbcl (sb-unix:unix-access (native-namestring pathname) sb-unix:x_ok)
-  #+cmu (unix:unix-access (native-namestring pathname) unix:x_ok)
-  #-(or sbcl cmu) (error "don't know how to check whether a file might be executable"))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defstruct (unix-file-stat (:conc-name stat-))
-  device
-  inode
-  links
-  atime
-  mtime
-  ctime
-  size
-  blksize
-  blocks
-  uid
-  gid
-  mode)
-
-(defun unix-stat (pathname)
-  ;; this could be different depending on the unix systems
-  (multiple-value-bind (ok? device inode mode links uid gid rdev
-                            size atime mtime ctime
-                            blksize blocks)
-      (#+cmu unix:unix-lstat
-       #+sbcl sb-unix:unix-lstat
-       (if (stringp pathname)
-           pathname
-           (native-namestring pathname)))
-    (declare (ignore rdev))
-    (when ok?
-      (make-unix-file-stat :device device
-                           :inode inode
-                           :links links
-                           :atime atime
-                           :mtime mtime
-                           :ctime ctime
-                           :size size
-                           :blksize blksize
-                           :blocks blocks
-                           :uid uid
-                           :gid gid
-                           :mode mode))))
-
-(defun stat-modification-time (stat)
-  "Return the modification time of the STAT structure as Lisp
-Universal Time, which is not the same as the Unix time."
-  (unix->universal-time (stat-mtime stat)))
-
-(defun stat-creation-time (stat)
-  "Return the creation time of the STAT structure as Lisp
-Universal Time, which is not the same as the Unix time."
-  (unix->universal-time (stat-ctime stat)))
-
-(defun file-modification-time (file)
-  "Return the modification time of FILE as Lisp Universal Time, which
-is not the same as the Unix time."
-  (awhen (unix-stat file)
-    (stat-modification-time it)))
-
-(defun file-creation-time (file)
-  "Return the creation time of FILE as Lisp Universal Time, which
-is not the same as the Unix time."
-  (awhen (unix-stat file)
-    (stat-creation-time it)))
-
-(defun read-symbolic-link (symlink)
-  "Return the pathname the SYMLINK points to.  That is, it's
-contents."
-  #+sbcl (sb-posix:readlink (native-namestring symlink))
-  #+cmu (unix:unix-readlink (native-namestring symlink)))
-
-;; FILE-LENGTH is a bit idiosyncratic in this respect.  Besides, Unix
-;; allows to get to know the file size without being able to open a
-;; file; just ask politely.
-(defun file-size (pathname)
-  (stat-size (unix-stat pathname)))
-
-(defun symbolic-link-p (pathname)
-  #-(or sbcl cmu) (error "don't know hot to test for symbolic links.")
-  (aand (unix-stat pathname)
-        (bits-set-p (stat-mode it)
-                    #+sbcl sb-posix:s-iflnk
-                    #+cmu unix:s-iflnk)))
-
-(defun broken-link-p (pathname)
- (when (symbolic-link-p pathname)
-   #+cmu (not (ignore-errors (truename pathname)))
-   ;; On a broken symlink SBCL returns the link path without resolving
-   ;; the link itself.  De gustibus non est disputandum.
-   #+sbcl (equalp pathname (probe-file pathname))))
-
-(defun move-file (old new)
-  "Just like RENAME-FILE, but doesn't carry on to NEW file the type of
-OLD file, if NEW doesn't specify one.  It does what most people would
-expect from a rename function, which RENAME-FILE doesn't do.
-So (MOVE-FILE \"foo.bar\" \"foo\") does rename foo.bar to foo, losing
-the \"bar\" type; RENAME-FILE wouldn't allow you that."
-  #+sbcl (sb-posix:rename (native-namestring old) (native-namestring new))
-  #+cmu (unix:unix-rename (native-namestring old) (native-namestring new)))
diff --git a/third_party/lisp/sclf/lazy.lisp b/third_party/lisp/sclf/lazy.lisp
deleted file mode 100644
index 34bae82ebb..0000000000
--- a/third_party/lisp/sclf/lazy.lisp
+++ /dev/null
@@ -1,134 +0,0 @@
-;;;  lazy.lisp --- lazy primitives
-
-;;;  Copyright (C) 2008, 2009, 2010 by Walter C. Pelissero
-
-;;;  Author: Walter C. Pelissero <walter@pelissero.de>
-;;;  Project: sclf
-
-#+cmu (ext:file-comment "$Module: lazy.lisp $")
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 2.1
-;;; of the License, or (at your option) any later version.
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Lesser General Public License for more details.
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free
-;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-;;; 02111-1307 USA
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;
-;;; Lazy primitives
-;;;
-
-(in-package :sclf)
-
-(defstruct promise
-  procedure
-  value)
-
-(defmacro lazy (form)
-  `(make-promise :procedure #'(lambda () ,form)))
-
-(defun forced-p (promise)
-  (null (promise-procedure promise)))
-
-(defun force (promise)
-  (if (forced-p promise)
-      (promise-value promise)
-      (prog1 (setf (promise-value promise)
-                   (funcall (promise-procedure promise)))
-        (setf (promise-procedure promise) nil))))
-
-(defmacro deflazy (name value &optional documentation)
-  `(defparameter ,name (lazy ,value)
-     ,@(when documentation
-             (list documentation))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defclass lazy-metaclass (standard-class)
-  ()
-  (:documentation "Metaclass for object having lazy slots.  Lazy slots
-should be specified with the :LAZY keyword which must be a function of
-one argument.  If required this function will be called once to get
-the value to memoize in the slot.  Lazy slots can also be set/read as
-any other."))
-
-(defmethod validate-superclass ((class lazy-metaclass) (super standard-class))
-  "Lazy classes may inherit from ordinary classes."
-  (declare (ignore class super))
-  t)
-
-(defmethod validate-superclass ((class standard-class) (super lazy-metaclass))
-  "Ordinary classes may inherit from lazy classes."
-  (declare (ignore class super))
-  t)
-
-(defclass lazy-slot-mixin ()
-  ((lazy-function :initarg :lazy
-                   :reader lazy-slot-function
-                   :initform nil))
-  (:documentation
-   "Slot for LAZY-METACLASS classes.  Lazy slots must be declared with
-the argument :LAZY which must be a function accepting the object
-instance as argument."))
-
-(defclass lazy-direct-slot-definition (lazy-slot-mixin standard-direct-slot-definition)
-  ())
-
-(defclass lazy-effective-slot-definition (lazy-slot-mixin standard-effective-slot-definition)
-  ())
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defmethod direct-slot-definition-class ((class lazy-metaclass) &rest initargs)
-  (if (getf initargs :lazy nil)
-      (find-class 'lazy-direct-slot-definition)
-      (call-next-method)))
-
-(defmethod effective-slot-definition-class ((class lazy-metaclass) &rest initargs)
-  (if (getf initargs :lazy nil)
-      (find-class 'lazy-effective-slot-definition)
-      (call-next-method)))
-
-(defmethod compute-effective-slot-definition-initargs ((class lazy-metaclass) direct-slots)
-  (let ((ds (car direct-slots)))
-    (if (typep ds 'lazy-direct-slot-definition)
-      (let ((form (lazy-slot-function ds))
-            (args (call-next-method)))
-        (when (or (getf args :initarg)
-                  (getf args :initform))
-          (error "Lazy slot ~S cannot have :INITARG nor :INITFORM arguments." ds))
-        (list* :lazy
-               (cond ((and (listp form)
-                           (eq 'lambda (car form)))
-                      (compile nil form))
-                     ((symbolp form)
-                      form)
-                     (t (compile nil `(lambda (self)
-                                        (declare (ignorable self))
-                                        ,form))))
-               args))
-      (call-next-method))))
-
-(defmethod slot-value-using-class ((class lazy-metaclass) instance (slot lazy-slot-mixin))
-  (declare (ignore class))
-  ;; If the slot is unbound, call the lazy function passing the
-  ;; instance and memoize the value in the slot.
-  (unless (slot-boundp-using-class class instance slot)
-    (setf (slot-value-using-class class instance slot)
-          (funcall (lazy-slot-function slot) instance)))
-  (call-next-method))
-
-(defun reset-lazy-slots (object)
-  "Unbind all the lazy slots in OBJECT so that they will be
-re-evaluated next time their value is requested again."
-  (be* class (class-of object)
-    (dolist (slot (class-slots class))
-      (when (typep slot 'lazy-effective-slot-definition)
-        (slot-makunbound object (slot-definition-name slot))))))
\ No newline at end of file
diff --git a/third_party/lisp/sclf/mp/README b/third_party/lisp/sclf/mp/README
deleted file mode 100644
index a0732c0294..0000000000
--- a/third_party/lisp/sclf/mp/README
+++ /dev/null
@@ -1,6 +0,0 @@
-This directory contains an uniforming layer for multiprocessing in the
-style supported by Allegro Common Lisp and CMUCL.  Almost nothing of
-this has been written by me.  It's mostly the work of Gilbert Baumann
-(unk6@rz.uni-karlsruhe.de) and I've shamelessly lifted it from McCLIM.
-The copyright disclaimer in this code is compatible with the one of
-SCLF, so I believe there should be no legal issues.
diff --git a/third_party/lisp/sclf/mp/cmu.lisp b/third_party/lisp/sclf/mp/cmu.lisp
deleted file mode 100644
index 1bdbba7989..0000000000
--- a/third_party/lisp/sclf/mp/cmu.lisp
+++ /dev/null
@@ -1,115 +0,0 @@
-;;;
-;;; Code freely lifted from various places with compatible license
-;;; terms.  Most of this code is copyright Gilbert Baumann
-;;; <unk6@rz.uni-karlsruhe.de>.  The bugs are copyright Walter
-;;; C. Pelissero <walter@pelissero.de>.
-;;;
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Library General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 2 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Library General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Library General Public
-;;; License along with this library; if not, write to the 
-;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
-;;; Boston, MA  02111-1307  USA.
-
-(in-package :sclf)
-
-(defun make-lock (&optional name)
-  (mp:make-lock name))
-
-(defun make-recursive-lock (&optional name)
-  (mp:make-lock name :kind :recursive))
-
-(defmacro with-lock-held ((lock &key whostate (wait t) timeout) &body forms)
-  `(mp:with-lock-held (,lock ,(or whostate "Lock Wait")
-                             :wait wait
-                             ,@(when timeout (list :timeout timeout)))
-     ,@forms))
-
-(defmacro with-recursive-lock-held ((lock &key wait timeout) &body forms)
-  `(mp:with-lock-held (,lock
-                       ,@(when wait (list :wait wait))
-                       ,@(when timeout (list :timeout timeout)))
-     ,@forms))
-
-(defstruct condition-variable
-  (lock (make-lock "condition variable"))
-  (value nil)
-  (process-queue nil))
-
-(defun %release-lock (lock) ; copied from with-lock-held in multiproc.lisp
-  #+i486 (kernel:%instance-set-conditional
-          lock 2 mp:*current-process* nil)
-  #-i486 (when (eq (lock-process lock) mp:*current-process*)
-           (setf (lock-process lock) nil)))
-
-(defun condition-wait (cv lock &optional timeout)
-  (declare (ignore timeout))		;For now
-  (loop
-     (let ((cv-lock (condition-variable-lock cv)))
-       (with-lock-held (cv-lock)
-         (when (condition-variable-value cv)
-           (setf (condition-variable-value cv) nil)
-           (return-from condition-wait t))
-         (setf (condition-variable-process-queue cv)
-               (nconc (condition-variable-process-queue cv)
-                      (list mp:*current-process*)))
-         (%release-lock lock))
-       (mp:process-add-arrest-reason mp:*current-process* cv)
-       (let ((cv-val nil))
-         (with-lock-held (cv-lock)
-           (setq cv-val (condition-variable-value cv))
-           (when cv-val
-             (setf (condition-variable-value cv) nil)))
-         (when cv-val
-           (mp::lock-wait lock "waiting for condition variable lock")
-           (return-from condition-wait t))))))
-
-(defun condition-notify (cv)
-  (with-lock-held ((condition-variable-lock cv))
-    (let ((proc (pop (condition-variable-process-queue cv))))
-      ;; The waiting process may have released the CV lock but not
-      ;; suspended itself yet
-      (when proc
-        (loop
-         for activep = (mp:process-active-p proc)
-         while activep
-         do (mp:process-yield))
-        (setf (condition-variable-value cv) t)
-        (mp:process-revoke-arrest-reason proc cv))))
-  ;; Give the other process a chance
-  (mp:process-yield))
-
-(defun process-execute (process function)
-  (mp:process-preset process function)
-  ;; For some obscure reason process-preset doesn't make the process
-  ;; runnable.  I'm sure it's me who didn't understand how
-  ;; multiprocessing works under CMUCL, despite the vast documentation
-  ;; available.
-  (mp:enable-process process)
-  (mp:process-add-run-reason process :enable))
-
-(defun destroy-process (process)
-  ;; silnetly ignore a process that is trying to destroy itself
-  (unless (eq (mp:current-process)
-              process)
-    (mp:destroy-process process)))
-
-(defun restart-process (process)
-  (mp:restart-process process)
-  (mp:enable-process process)
-  (mp:process-add-run-reason process :enable))
-
-(defun process-alive-p (process)
-  (mp:process-alive-p process))
-
-(defun process-join (process)
-  (error "PROCESS-JOIN not support under CMUCL."))
diff --git a/third_party/lisp/sclf/mp/sbcl.lisp b/third_party/lisp/sclf/mp/sbcl.lisp
deleted file mode 100644
index a2cf497ff9..0000000000
--- a/third_party/lisp/sclf/mp/sbcl.lisp
+++ /dev/null
@@ -1,235 +0,0 @@
-;;;
-;;; Code freely lifted from various places with compatible license
-;;; terms.  Most of this code is copyright Daniel Barlow
-;;; <dan@metacircles.com> or Gilbert Baumann
-;;; <unk6@rz.uni-karlsruhe.de>.  The bugs are copyright Walter
-;;; C. Pelissero <walter@pelissero.de>.
-;;;
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Library General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 2 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Library General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Library General Public
-;;; License along with this library; if not, write to the 
-;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
-;;; Boston, MA  02111-1307  USA.
-
-(in-package :sclf)
-
-(defstruct (process
-             (:constructor %make-process)
-             (:predicate processp))
-  name
-  state
-  whostate
-  function
-  thread)
-
-(defvar *current-process*
-  (%make-process
-   :name "initial process" :function nil
-   :thread
-   #+#.(cl:if (cl:find-symbol "THREAD-NAME" "SB-THREAD") '(and) '(or))
-   sb-thread:*current-thread*
-   #-#.(cl:if (cl:find-symbol "THREAD-NAME" "SB-THREAD") '(and) '(or))
-   (sb-thread:current-thread-id)))
-
-(defvar *all-processes* (list *current-process*))
-
-(defvar *all-processes-lock*
-  (sb-thread:make-mutex :name "Lock around *ALL-PROCESSES*"))
-
-;; we implement disable-process by making the disablee attempt to lock
-;; *permanent-queue*, which is already locked because we locked it
-;; here.  enable-process just interrupts the lock attempt.
-
-(defmacro get-mutex (mutex &optional (wait t))
-  `(
-    #+#.(cl:if (cl:find-symbol "GRAB-MUTEX" "SB-THREAD") '(and) '(or))
-        sb-thread:grab-mutex
-        #-#.(cl:if (cl:find-symbol "GRAB-MUTEX" "SB-THREAD") '(and) '(or))
-        sb-thread:get-mutex
-        ,mutex :waitp ,wait))
-
-(defvar *permanent-queue*
-  (sb-thread:make-mutex :name "Lock for disabled threads"))
-(unless (sb-thread:mutex-owner *permanent-queue*)
-  (get-mutex *permanent-queue* nil))
-
-(defun make-process (function &key name)
-  (let ((p (%make-process :name name
-                          :function function)))
-    (sb-thread:with-mutex (*all-processes-lock*)
-      (pushnew p *all-processes*))
-    (restart-process p)))
-
-(defun process-kill-thread (process)
-  (let ((thread (process-thread process)))
-    (when (and thread
-               (sb-thread:thread-alive-p thread))
-      (assert (not (eq thread sb-thread:*current-thread*)))
-      (sb-thread:terminate-thread thread)
-      ;; Wait until all the clean-up forms are done.
-      (sb-thread:join-thread thread :default nil))
-    (setf (process-thread process) nil)))
-
-(defun process-join (process)
-  (sb-thread:join-thread (process-thread process)))
-
-(defun restart-process (p)
-  (labels ((boing ()
-             (let ((*current-process* p)
-                   (function (process-function p)))
-               (when function
-                 (funcall function)))))
-    (process-kill-thread p)
-    (when (setf (process-thread p)
-                (sb-thread:make-thread #'boing :name (process-name p)))
-      p)))
-
-(defun destroy-process (process)
-  (sb-thread:with-mutex (*all-processes-lock*)
-    (setf *all-processes* (delete process *all-processes*)))
-  (process-kill-thread process))
-
-(defun current-process ()
-  *current-process*)
-
-(defun all-processes ()
-  ;; we're calling DELETE on *ALL-PROCESSES*.  If we look up the value
-  ;; while that delete is executing, we could end up with nonsense.
-  ;; Better use a lock (or call REMOVE instead in DESTROY-PROCESS).
-  (sb-thread:with-mutex (*all-processes-lock*)
-    *all-processes*))
-
-(defun process-yield ()
-  (sb-thread:thread-yield))
-
-(defun process-wait (reason predicate)
-  (let ((old-state (process-whostate *current-process*)))
-    (unwind-protect
-         (progn
-           (setf old-state (process-whostate *current-process*)
-                 (process-whostate *current-process*) reason)
-           (until (funcall predicate)
-             (process-yield)))
-      (setf (process-whostate *current-process*) old-state))))
-
-(defun process-wait-with-timeout (reason timeout predicate)
-  (let ((old-state (process-whostate *current-process*))
-        (end-time (+ (get-universal-time) timeout)))
-    (unwind-protect
-         (progn
-           (setf old-state (process-whostate *current-process*)
-                 (process-whostate *current-process*) reason)
-           (loop 
-              for result = (funcall predicate)
-              until (or result
-                        (> (get-universal-time) end-time))
-              do (process-yield)
-              finally (return result)))
-      (setf (process-whostate *current-process*) old-state))))
-
-(defun process-interrupt (process function)
-  (sb-thread:interrupt-thread (process-thread process) function))
-
-(defun disable-process (process)
-  (sb-thread:interrupt-thread
-   (process-thread process)
-   (lambda ()
-     (catch 'interrupted-wait (get-mutex *permanent-queue*)))))
-
-(defun enable-process (process)
-  (sb-thread:interrupt-thread
-   (process-thread process) (lambda () (throw 'interrupted-wait nil))))
-
-(defmacro without-scheduling (&body body)
-  (declare (ignore body))
-  (error "WITHOUT-SCHEDULING is not supported on this platform."))
-
-(defparameter *atomic-lock*
-  (sb-thread:make-mutex :name "atomic incf/decf"))
-
-(defmacro atomic-incf (place)
-  `(sb-thread:with-mutex (*atomic-lock*)
-    (incf ,place)))
-
-(defmacro atomic-decf (place) 
-  `(sb-thread:with-mutex (*atomic-lock*)
-    (decf ,place)))
-
-;;; 32.3 Locks
-
-(defun make-lock (&optional name)
-  (sb-thread:make-mutex :name name))
-
-(defmacro with-lock-held ((place &key state (wait t) timeout) &body body)
-  (declare (ignore timeout))
-  (let ((old-state (gensym "OLD-STATE")))
-    `(sb-thread:with-mutex (,place :wait-p ,wait)
-       (let (,old-state)
-         (unwind-protect
-              (progn
-                (when ,state
-                  (setf ,old-state (process-state *current-process*))
-                  (setf (process-state *current-process*) ,state))
-                ,@body)
-           (setf (process-state *current-process*) ,old-state))))))
-
-
-(defun make-recursive-lock (&optional name)
-  (sb-thread:make-mutex :name name))
-
-(defmacro with-recursive-lock-held ((place &optional state (wait t) timeout) &body body)
-  (declare (ignore wait timeout))
-  (let ((old-state (gensym "OLD-STATE")))
-  `(sb-thread:with-recursive-lock (,place)
-    (let (,old-state)
-      (unwind-protect
-           (progn
-             (when ,state
-               (setf ,old-state (process-state *current-process*))
-               (setf (process-state *current-process*) ,state))
-             ,@body)
-        (setf (process-state *current-process*) ,old-state))))))
-
-(defun make-condition-variable () (sb-thread:make-waitqueue))
-
-(defun condition-wait (cv lock &optional timeout)
-  (if timeout
-      (handler-case 
-          (sb-ext:with-timeout timeout
-            (sb-thread:condition-wait cv lock)
-            t)
-        (sb-ext:timeout (c)
-          (declare (ignore c))
-          nil))
-      (progn (sb-thread:condition-wait cv lock) t)))
-
-(defun condition-notify (cv)
-  (sb-thread:condition-notify cv))
-
-
-(defvar *process-plists* (make-hash-table)
-  "Hash table mapping processes to a property list.  This is used by
-PROCESS-PLIST.")
-
-(defun process-property-list (process)
-  (gethash process *process-plists*))
-
-(defun (setf process-property-list) (value process)
-  (setf (gethash process *process-plists*) value))
-
-(defun process-execute (process function)
-  (setf (process-function process) function)
-  (restart-process process))
-
-(defun process-alive-p (process)
-  (sb-thread:thread-alive-p (process-thread process)))
diff --git a/third_party/lisp/sclf/package.lisp b/third_party/lisp/sclf/package.lisp
deleted file mode 100644
index 565ab301c7..0000000000
--- a/third_party/lisp/sclf/package.lisp
+++ /dev/null
@@ -1,258 +0,0 @@
-;;;  package.lisp --- packages description
-
-;;;  Copyright (C) 2006, 2007, 2008, 2009, 2010 by Walter C. Pelissero
-;;;  Copyright (C) 2021 by the TVL Authors
-
-;;;  Author: Walter C. Pelissero <walter@pelissero.de>
-;;;  Project: sclf
-
-#+cmu (ext:file-comment "$Module: package.lisp $")
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 2.1
-;;; of the License, or (at your option) any later version.
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Lesser General Public License for more details.
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free
-;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-;;; 02111-1307 USA
-
-(in-package :cl-user)
-
-(defpackage :sclf
-  (:use :common-lisp
-        ;; we need the MOP for lazy.lisp and serial.lisp
-        #+cmu :pcl
-        #+sbcl :sb-mop)
-  ;; Don't know why but compute-effective-slot-definition-initargs is
-  ;; internal in both CMUCL and SBCL
-  (:import-from #+cmu"PCL" #+sbcl"SB-PCL"
-                #-(or cmu sbcl) "CLOS"
-                "COMPUTE-EFFECTIVE-SLOT-DEFINITION-INITARGS")
-  #+cmu (:import-from :mp
-                      #:make-process
-                      #:current-process
-                      #:all-processes
-                      #:processp
-                      #:process-name
-                      #:process-state
-                      #:process-whostate
-                      #:process-wait
-                      #:process-wait-with-timeout
-                      #:process-yield
-                      #:process-interrupt
-                      #:disable-process
-                      #:enable-process
-                      #:without-scheduling
-                      #:atomic-incf
-                      #:atomic-decf
-                      #:process-property-list)
-  (:export #:be #:be*
-           #:defconst
-           #:with-gensyms
-           #:d+
-           #:s+
-           #:f++
-           #:list->string
-           #:string-starts-with #:string-ends-with
-           #:aif #:awhen #:acond #:aand #:acase #:it
-           #:+whitespace+
-           #:string-trim-whitespace
-           #:string-right-trim-whitespace
-           #:string-left-trim-whitespace
-           #:whitespace-p #:seq-whitespace-p
-           #:not-empty
-           #:position-any
-           #:+month-names+
-           #:find-any
-           #:split-at
-           #:split-string-at-char
-           #:week-day->string
-           #:month->string
-           #:month-string->number
-           #:add-months #:add-days
-           #:read-whole-stream
-           #:read-file #:write-file #:read-lines
-           #:read-from-file #:write-to-file
-           #:string-concat
-           #:gcase
-           #:string-truncate
-           #:promise #:force #:forced-p #:lazy #:deflazy #:lazy-metaclass #:self #:reset-lazy-slots
-           #:copy-stream #:copy-file
-           #:symlink-file
-           #:keywordify
-           #:until
-           #:year #:month #:day #:hour #:minute #:week-day #:week #:day-of-the-year
-           #:beginning-of-week #:end-of-week
-           #:next-week-day #:next-monday #:full-weeks-in-span
-           #:beginning-of-first-week #:end-of-last-week
-           #:beginning-of-month #:end-of-month
-           #:locate-system-program
-           #:*tmp-file-defaults*
-           #:temp-file-name
-           #:open-temp-file
-           #:with-temp-file
-           #:file-size
-           #:getenv
-           #:with-system-environment
-           #:time-string #:iso-time-string #:parse-iso-time-string
-           #:soundex
-           #:string-soundex=
-           #:lru-cache
-           #:getcache #:cached
-           #:print-time-span
-           #:double-linked-list #:limited-list #:sorted-list
-           #:insert #:size
-           #:heap #:heap-add #:heap-pop #:heap-empty-p
-           #:double-linked-element #:make-double-linked-element #:double-linked-element-p
-           #:dle-previous #:dle-next #:dle-value
-           #:cons-dle #:dle-remove #:dle-map #:do-dle :do-dle*
-           #:sl-map #:do-dll #:do-dll*
-           #:dll-find #:dll-find-cursor
-           #:push-first #:push-last #:dll-remove
-           #:pop-first #:pop-last
-           #:leap-year-p #:last-day-of-month
-           #:getuid #:setuid #:with-euid
-           #:get-logname #:get-user-name #:get-user-home #:find-uid
-           #:super-user-p
-           #:pathname-as-directory #:pathname-as-file
-           #:alist->plist #:plist->alist
-           #:byte-vector->string
-           #:string->byte-vector
-           #:outdated-p
-           #:with-hidden-temp-file
-           #:let-places #:let-slots
-           #:*decimal-point*
-           #:*thousands-comma*
-           #:format-amount #:parse-amount
-           #:with-package
-           #:make-directory #:ensure-directory
-           #:make-temp-directory
-           #:with-temp-directory
-           #:delete-directory
-           #:delete-directory-tree
-           #:do-directory-tree
-           #:traverse-directory-tree
-           #:empty-directory-p
-           #:remove-empty-directories
-           #:map-directory-tree
-           #:find-files
-           #:directory-p
-           #:regular-file-p
-           #:file-readable-p
-           #:file-writable-p
-           #:file-executable-p
-           #:current-directory
-           #:ensure-home-translations
-           #:list-directory
-           #:string-escape
-           #:string-substitute
-           #:bytes-simple-string
-           #:make-lock-files
-           #:with-lock-files
-           #:getpid
-           #:on-error
-           #:floor-to
-           #:round-to
-           #:ceiling-to
-           #:insert-in-order
-           #:forget-documentation
-           #:load-compiled
-           #:swap
-           #:queue #:make-queue #:queue-append #:queue-pop #:queue-empty-p
-           #:unix-stat #:unix-file-stat
-           #:stat-device
-           #:stat-inode
-           #:stat-links
-           #:stat-atime
-           #:stat-mtime
-           #:stat-ctime
-           #:stat-birthtime
-           #:stat-size
-           #:stat-blksize
-           #:stat-blocks
-           #:stat-uid
-           #:stat-gid
-           #:stat-mode
-           #:save-file-excursion
-           #:stat-modification-time
-           #:stat-creation-time
-           #:file-modification-time
-           #:file-creation-time
-           #:show
-           #:memoize-function
-           #:memoized
-           #:defun-memoized
-           #:parse-native-namestring
-           #:native-file-namestring
-           #:native-namestring
-           #:native-pathname
-           #:read-symbolic-link
-           #:symbolic-link-p
-           #:broken-link-p
-           #:circular-list
-           #:last-member
-           #:glob->regex
-           #:universal->unix-time #:unix->universal-time
-           #:get-unix-time
-           #:move-file
-
-           ;; sysproc.lisp
-           #:*run-verbose*
-           #:run-pipe
-           #:run-program
-           #:run-shell-command
-           #:run-async-shell-command
-           #:exit-code
-           #:with-open-pipe
-           #:*bourne-shell*
-           #:sysproc-kill
-           #:sysproc-input
-           #:sysproc-output
-           #:sysproc-alive-p
-           #:sysproc-pid
-           #:sysproc-p
-           #:sysproc-wait
-           #:sysproc-exit-code
-           #:sysproc-set-signal-callback
-
-           ;; MP
-           #:make-process
-           #:destroy-process
-           #:current-process
-           #:all-processes
-           #:processp
-           #:process-name
-           #:process-state
-           #:process-whostate
-           #:process-wait
-           #:process-wait-with-timeout
-           #:process-yield
-           #:process-interrupt
-           #:disable-process
-           #:enable-process
-           #:restart-process
-           #:without-scheduling
-           #:atomic-incf
-           #:atomic-decf
-           #:process-property-list
-           #:process-alive-p
-           #:process-join
-           ;;
-           #:make-lock
-           #:with-lock-held
-           #:make-recursive-lock
-           #:with-recursive-lock-held
-           ;;
-           #:make-condition-variable
-           #:condition-wait
-           #:condition-notify
-           #:process-property-list
-           #:process-execute
-           ;; mop.lisp
-           #:printable-object-mixin
-           ))
diff --git a/third_party/lisp/sclf/sclf.asd b/third_party/lisp/sclf/sclf.asd
deleted file mode 100644
index a9754b7569..0000000000
--- a/third_party/lisp/sclf/sclf.asd
+++ /dev/null
@@ -1,58 +0,0 @@
-;;;  sclf.asd --- system definition
-
-;;;  Copyright (C) 2005, 2006, 2008, 2009 by Walter C. Pelissero
-;;;  Copyright (C) 2021 by the TVL Authors
-
-;;;  Author: Walter C. Pelissero <walter@pelissero.de>
-;;;  Project: SCLF
-
-#+cmu (ext:file-comment "$Module: sclf.asd, Time-stamp: <2013-06-17 15:32:29 wcp> $")
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 2.1
-;;; of the License, or (at your option) any later version.
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Lesser General Public License for more details.
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free
-;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-;;; 02111-1307 USA
-
-(in-package :cl-user)
-
-(defpackage :sclf-system
-  (:use :common-lisp :asdf #+asdfa :asdfa))
-
-(in-package :sclf-system)
-
-(defsystem sclf
-    :name "SCLF"
-    :author "Walter C. Pelissero <walter@pelissero.de>"
-    :maintainer "Walter C. Pelissero <walter@pelissero.de>"
-    ;; :version "0.0"
-    :description "Stray Common Lisp Functions"
-    :long-description
-    "A collection of Common Lisp functions for the most disparate
-uses, too small to fit anywhere else."
-    :licence "LGPL"
-    :depends-on (#+sbcl :sb-posix)
-    :components
-    ((:doc-file "README")
-     (:file "package")
-     (:file "sclf" :depends-on ("package"))
-     (:file "sysproc" :depends-on ("package" "sclf"))
-     (:file "lazy" :depends-on ("package" "sclf"))
-     (:file "time" :depends-on ("package" "sclf"))
-     (:file "directory" :depends-on ("package" "sclf" "time"))
-     (:file "serial" :depends-on ("package" "sclf"))
-     (:module "mp"
-              :depends-on ("package" "sclf")
-              :components
-              ((:doc-file "README")
-               (:file #.(first
-                         (list #+cmu "cmu"
-                               #+sbcl "sbcl"
-                               "unknown")))))))
diff --git a/third_party/lisp/sclf/sclf.lisp b/third_party/lisp/sclf/sclf.lisp
deleted file mode 100644
index dfbc2078c8..0000000000
--- a/third_party/lisp/sclf/sclf.lisp
+++ /dev/null
@@ -1,1717 +0,0 @@
-;;;  sclf.lisp --- miscellanea
-
-;;;  Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 by Walter C. Pelissero
-
-;;;  Author: Walter C. Pelissero <walter@pelissero.de>
-;;;  Project: SCLF
-
-#+cmu (ext:file-comment "$Module: sclf.lisp $")
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 2.1
-;;; of the License, or (at your option) any later version.
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Lesser General Public License for more details.
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free
-;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-;;; 02111-1307 USA
-
-;;;  Commentary:
-
-;;; This is a collection of Common Lisp functions of the most disparate
-;;; uses and purposes.  These functions are too small or too unrelated
-;;; to each other to deserve an own module.
-;;;
-;;; If you want to indent properly the following macros you should add
-;;; the following lines to your .emacs file:
-;;;
-;;; (defun cl-indent-be (path state indent-point sexp-column normal-indent)
-;;;   (let ((sexp-start (cadr state))
-;;; 	(i 0))
-;;;     (save-excursion
-;;;       (goto-char sexp-start)
-;;;       (forward-char)
-;;;       (+ sexp-column
-;;; 	 (block indentation
-;;; 	   (condition-case nil
-;;; 	       (while (< (point) indent-point)
-;;; 		 (setq i (1+ i))
-;;; 		 (when (and (= 0 (logand i 1))
-;;; 			    (looking-at "[\t\n ]*\\s("))
-;;; 		   (return-from indentation 2))
-;;; 		 (forward-sexp))
-;;; 	     (error nil))
-;;; 	   (if (= 1 (logand i 1))
-;;; 	       6 4))))))
-;;;
-;;; (put 'be 'common-lisp-indent-function 'cl-indent-be)
-;;; (put 'be* 'common-lisp-indent-function 'cl-indent-be)
-;;; (put 'awhen 'lisp-indent-function 1)
-;;; (put 'gcase 'lisp-indent-function 1)
-;;; (put 'acase 'lisp-indent-function 1)
-;;; (put 'acond 'lisp-indent-function 1)
-;;; (put 'until 'lisp-indent-function 1)
-
-
-
-(cl:in-package :sclf)
-
-(defmacro be (&rest bindings-and-body)
-  "Less-parenthetic let."
-  (let ((bindings
-         (loop
-            while (and (symbolp (car bindings-and-body))
-                       (cdr bindings-and-body))
-            collect (list (pop bindings-and-body)
-                          (pop bindings-and-body)))))
-    `(let ,bindings
-       ,@bindings-and-body)))
-
-(defmacro be* (&rest bindings-and-body)
-  "Less-parenthetic let*."
-  (let ((bindings
-         (loop
-            while (and (symbolp (car bindings-and-body))
-                       (cdr bindings-and-body))
-            collect (list (pop bindings-and-body)
-                          (pop bindings-and-body)))))
-    `(let* ,bindings
-       ,@bindings-and-body)))
-
-(defmacro defconst (name value &rest etc)
-  "For some reason SBCL, between usefulness and adherence to the ANSI
-standard, has chosen the latter, thus rendering the DEFCONSTANT pretty
-useless.  This macro works around that problem."
-  #+sbcl (list* 'defvar name value etc)
-  #-sbcl (list* 'defconstant name value etc))
-
-(defmacro with-gensyms ((&rest symbols) &body body)
-  "Gensym all SYMBOLS and make them available in BODY.
-See also LET-GENSYMS."
-  `(let ,(mapcar #'(lambda (s)
-                     (list s '(gensym))) symbols)
-     ,@body))
-
-(defun s+ (&rest strings)
-  "Return a string which is made of the concatenation of STRINGS."
-  (apply #'concatenate 'string strings))
-
-(defun string-starts-with (prefix string &optional (compare #'string=))
-  (be prefix-length (length prefix)
-    (and (>= (length string) prefix-length)
-         (funcall compare prefix string :end2 prefix-length))))
-
-(defun string-ends-with (postfix string &optional (compare #'string=))
-  "Return true if STRING's last characters are the same as POSTFIX."
-  (be postfix-length (length postfix)
-      string-length (length string)
-    (and (>= string-length postfix-length)
-         (funcall compare postfix string :start2 (- string-length postfix-length)))))
-
-(defun string-substitute (from to sequence &key (start 0) end (test #'eql))
-  "Replace in SEQUENCE occurrences of FROM with TO.  FROM and TO don't
-need to be the same length."
-  (be from-length (length from)
-    (with-output-to-string (out)
-      (write-string sequence out :start 0 :end start)
-      (loop
-         for position = (search from sequence :start2 start :end2 end :test test)
-         while position
-         do
-           (write-string sequence out :start start :end position)
-           (write-string to out)
-           (setf start (+ position from-length))
-         finally (write-string (subseq sequence start) out)))))
-
-(defun string-escape (string character &key (escape-character #\\) (escape-escape t))
-  "Prepend all occurences of CHARACTER in STRING with a
-ESCAPE-CHARACTER."
-  (with-output-to-string (stream)
-    (loop
-       for c across string
-       when (or (char= c character)
-                (and escape-escape
-                     (char= c escape-character)))
-       do (write-char escape-character stream)
-       do (write-char c stream))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defmacro aif (test then &optional else)
-  `(be it ,test
-     (if it
-         ,then
-         ,else)))
-
-(defmacro awhen (test &body then)
-  `(be it ,test
-     (when it
-       ,@then)))
-
-(defmacro acond (&body forms)
-  (when forms
-    `(aif ,(caar forms)
-          (progn ,@(cdar forms))
-          (acond ,@(cdr forms)))))
-
-(defmacro aand (&rest args)
-  (cond ((null args) t)
-        ((null (cdr args)) (car args))
-        (t `(aif ,(car args) (aand ,@(cdr args))))))
-
-(defmacro acase (condition &body forms)
-  `(be it ,condition
-     (case it ,@forms)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defconst +whitespace+ '(#\return #\newline #\tab #\space #\page))
-
-(defun string-trim-whitespace (string)
-  (string-trim +whitespace+ string))
-
-(defun string-right-trim-whitespace (string)
-  (string-right-trim +whitespace+ string))
-
-(defun string-left-trim-whitespace (string)
-  (string-left-trim +whitespace+ string))
-
-(defun whitespace-p (char)
-  (member char +whitespace+))
-
-(defun seq-whitespace-p (sequence)
-  (every #'whitespace-p sequence))
-
-(defun not-empty (sequence)
-  "Return SEQUENCE if it's not empty, otherwise NIL.
-NIL is indeed empty."
-  (when (or (listp sequence)
-            (not (zerop (length sequence))))
-      sequence))
-
-(defun position-any (bag sequence &rest position-args)
-  "Find any element of bag in sequence and return its position.
-Accept any argument accepted by the POSITION function."
-  (apply #'position-if #'(lambda (element)
-                           (find element bag)) sequence position-args))
-
-(defun find-any (bag sequence &rest find-args)
-  "Find any element of bag in sequence.  Accept any argument
-accepted by the FIND function."
-  (apply #'find-if #'(lambda (element)
-                           (find element bag)) sequence find-args))
-
-(defun split-at (bag sequence &key (start 0) key)
-  "Split SEQUENCE at occurence of any element from BAG.
-Contiguous occurences of elements from BAG are considered atomic;
-so no empty sequence is returned."
-  (be len (length sequence)
-    (labels ((split-from (start)
-               (unless (>= start len)
-                 (be sep (position-any bag sequence :start start :key key)
-                   (cond ((not sep)
-                          (list (subseq sequence start)))
-                         ((> sep start)
-                          (cons (subseq sequence start sep)
-                                (split-from (1+ sep))))
-                         (t
-                          (split-from (1+ start))))))))
-      (split-from start))))
-
-(defun split-string-at-char (string separator &key escape skip-empty)
-  "Split STRING at SEPARATORs and return a list of the substrings.  If
-SKIP-EMPTY is true then filter out the empty substrings.  If ESCAPE is
-not nil then split at SEPARATOR only if it's not preceded by ESCAPE."
-  (declare (type string string) (type character separator))
-  (labels ((next-separator (beg)
-             (be pos (position separator string :start beg)
-               (if (and escape
-                        pos
-                        (plusp pos)
-                        (char= escape (char string (1- pos))))
-                   (next-separator (1+ pos))
-                   pos)))
-           (parse (beg)
-             (cond ((< beg (length string))
-                    (let* ((end (next-separator beg))
-                           (substring (subseq string beg end)))
-                      (cond ((and skip-empty (string= "" substring))
-                             (parse (1+ end)))
-                            ((not end)
-                             (list substring))
-                            (t
-                             (cons substring (parse (1+ end)))))))
-                   (skip-empty
-                    '())
-                   (t
-                    (list "")))))
-    (parse 0)))
-
-(defun copy-stream (in out)
-  (loop
-     for c = (read-char in nil)
-     while c
-     do (write-char c out)))
-
-(defun pathname-as-file (pathname)
-  "Converts PATHNAME to file form and return it."
-  (unless (pathnamep pathname)
-    (setf pathname (pathname pathname)))
-  (cond ((pathname-name pathname)
-         pathname)
-        ((stringp (car (last (pathname-directory pathname))))
-         (be name (parse-native-namestring (car (last (pathname-directory pathname))))
-           (make-pathname :directory (butlast (pathname-directory pathname))
-                          :name (pathname-name name)
-                          :type (pathname-type name)
-                          :defaults pathname)))
-        ;; it can't be done?
-        (t pathname)))
-
-(defun copy-file (file copy-file &key (if-exists :error))
-  (with-open-file (in file)
-    (with-open-file (out copy-file :direction :output :if-exists if-exists)
-      (copy-stream in out))))
-
-(defun symlink-file (src dst &key (if-exists :error))
-  (when (and (eq :supersede if-exists)
-             (probe-file dst))
-    (delete-file dst))
-  #+sbcl (sb-posix:symlink src dst)
-  #+cmu(unix:unix-symlink (native-namestring src) (native-namestring dst))
-  #-(or sbcl cmu) (error "don't know how to symlink files"))
-
-(defun read-whole-stream (stream)
-  "Read stream until the end and return it as a string."
-  (with-output-to-string (string)
-    (loop
-       for line = (read-line stream nil)
-       while line
-       do (write-line line string))))
-
-(defun read-lines (stream &optional n)
-  "Read N lines from stream and return them as a list of strings.  If
-N is NIL, read the whole stream til the end.  If the stream ends
-before N lines a read, this function will return those without
-signalling an error."
-  (loop
-     for line = (read-line stream nil)
-     for i from 0
-     while (and line
-                (or (not n)
-                    (< i n)))
-     collect line))
-
-(defun read-file (pathname &key (element-type 'character) (if-does-not-exist :error) default)
-  "Read the whole content of file and return it as a sequence which
-can be a string, a vector of bytes, or whatever you specify as
-ELEMENT-TYPE."
-  (with-open-file (in pathname
-                      :element-type element-type
-                      :if-does-not-exist (unless (eq :value if-does-not-exist)
-                                           :error))
-    (if in
-        (be seq (make-array (file-length in) :element-type element-type)
-          (read-sequence seq in)
-          seq)
-        default)))
-
-(defun write-file (pathname contents &key (if-exists :error))
-  "Read the whole content of file and return it as a sequence which
-can be a string, a vector of bytes, or whatever you specify as
-ELEMENT-TYPE."
-  (with-open-file (out pathname
-                       :element-type (if (stringp contents)
-                                         'character
-                                         (array-element-type contents))
-                       :if-exists if-exists)
-    (write-sequence contents out)))
-
-(defun read-from-file (pathname &key (on-error :error) default)
-  "Similar to READ-FROM-STRING but for files.  Read the first Lisp
-object in file and return it.  If file does not exist or does not
-contain a readable Lisp object, ON-ERROR tells what to do.  If
-ON-ERROR is :ERROR, an error is signalled.  If ON-ERROR is :VALUE,
-DEFAULT is returned."
-  (ecase on-error
-    (:error
-     (with-open-file (in pathname)
-       (read in)))
-    (:value
-     (handler-case (with-open-file (in pathname)
-                     (read in))
-       (t ()
-         default)))))
-
-(defun write-to-file (object pathname &key (if-exists :error) pretty)
-  "Similar to WRITE-TO-STRING but for files.  Write OBJECT to a file
-with pathname PATHNAME."
-  (with-open-file (out pathname :direction :output :if-exists if-exists)
-    (write object :stream out :escape t :readably t :pretty pretty)))
-
-(defun string-concat (list &optional (separator ""))
-  "Concatenate the strings in LIST interposing SEPARATOR (default
-nothing) between them."
-  (reduce #'(lambda (&rest args)
-              (if args
-                  (s+ (car args) separator (cadr args))
-                  ""))
-          list))
-
-;; to indent it properly: (put 'gcase 'lisp-indent-function 1)
-(defmacro gcase ((value &optional (test 'equalp)) &rest cases)
-  "Generic CASE macro.  Match VALUE to CASES as if by the normal CASE
-but use TEST as the comparison function, which defaults to EQUALP."
-  (with-gensyms (val)
-    `(be ,val ,value
-       ,(cons 'cond
-              (mapcar #'(lambda (case-desc)
-                          (destructuring-bind (vals &rest forms) case-desc
-                            `(,(cond ((consp vals)
-                                      (cons 'or (mapcar #'(lambda (v)
-                                                            (list test val v))
-                                                        vals)))
-                                     ((or (eq vals 'otherwise)
-                                          (eq vals t))
-                                      t)
-                                     (t (list test val vals)))
-                               ,@forms)))
-                      cases)))))
-
-(defun string-truncate (string max-length)
-  "If STRING is longer than MAX-LENGTH, return a shorter version.
-Otherwise return the same string unchanged."
-  (if (> (length string) max-length)
-      (subseq string 0 max-length)
-      string))
-
-;; to indent properly: (put 'until 'lisp-indent-function 1)
-(defmacro until (test &body body)
-  (with-gensyms (result)
-    `(loop
-        for ,result = ,test
-        until ,result
-        do (progn ,@body)
-        finally (return ,result))))
-
-(defun keywordify (string)
-  (intern (string-upcase string) :keyword))
-
-(defun locate-system-program (name)
-  "Given the NAME of a system program try to find it through the
-search of the environment variable PATH.  Return the full
-pathname."
-  (loop
-     for dir in (split-string-at-char (getenv "PATH") #\:)
-     for pathname = (merge-pathnames name (pathname-as-directory dir))
-     when (probe-file pathname)
-     return pathname))
-
-(defvar *tmp-file-defaults* #P"/tmp/")
-
-(defun temp-file-name (&optional (default *tmp-file-defaults*))
-  "Create a random pathname based on DEFAULT.  No effort is made
-to make sure that the returned pathname doesn't identify an
-already existing file.  If missing DEFAULT defaults to
-*TMP-FILE-DEFAULTS*."
-  (make-pathname :defaults default
-                 :name (format nil "~36R" (random #.(expt 36 10)))))
-
-(defun open-temp-file (&optional default-pathname &rest open-args)
-  "Open a new temporary file and return a stream to it.  This function
-makes sure the pathname of the temporary file is unique.  OPEN-ARGS
-are arguments passed verbatim to OPEN.  If OPEN-ARGS specify
-the :DIRECTION it should be either :OUTPUT (default) or :IO;
-any other value causes an error.  If DEFAULT-PATHNAME is specified and
-not NIL it's used as defaults to produce the pathname of the temporary
-file, otherwise *TMP-FILE-DEFAULTS* is used."
-  (unless default-pathname
-    (setf default-pathname *tmp-file-defaults*))
-  ;; if :DIRECTION is specified check that it's compatible with the
-  ;; purpose of this function, otherwise make it default to :OUTPUT
-  (aif (getf open-args :direction)
-       (unless (member it '(:output :io))
-         (error "Can't create temporary file with open direction ~A." it))
-       (setf open-args (append '(:direction :output)
-                               open-args)))
-  (do* ((name #1=(temp-file-name default-pathname) #1#)
-        (stream #2=(apply #'open  name
-                          :if-exists nil
-                          :if-does-not-exist :create
-                          open-args) #2#))
-       (stream stream)))
-
-(defmacro with-temp-file ((stream &rest open-temp-args) &body body)
-  "Execute BODY within a dynamic extent where STREAM is bound to
-a STREAM open on a unique temporary file name.  OPEN-TEMP-ARGS are
-passed verbatim to OPEN-TEMP-FILE."
-  `(be ,stream (open-temp-file ,@open-temp-args)
-     (unwind-protect
-          (progn ,@body)
-       (close ,stream)
-       ;; body may decide to rename the file so we must ignore the errors
-       (ignore-errors
-         (delete-file (pathname ,stream))))))
-
-(defmacro with-hidden-temp-file ((stream &rest open-args) &body body)
-  "Just like WITH-TEMP-FILE but unlink (delete) the temporary file
-before the execution of BODY.  As such BODY won't be able to
-manipulate the file but through STREAM, and no other program is able
-to see it.  Once STREAM is closed the temporary file blocks are
-automatically relinquished by the operating system.  This works at
-least on Unix filesystems.  I don't know about MS-OSs where the system
-may likely decide to crash, take all your data with it and, in the
-meanwhile, report you to the NSA as terrorist."
-  `(be ,stream (open-temp-file ,@open-args)
-     (unwind-protect
-          (progn (delete-file (pathname ,stream))
-                 ,@body)
-       (close ,stream))))
-
-(defun insert-in-order (item seq &key (test #'<) key)
-  "Destructively insert ITEM in LIST in order by TEST.  Return
-the new list.  This is a simple wrapper around MERGE."
-  (merge (if seq
-             (type-of seq)
-             'list)
-         (list item) seq test :key key))
-
-(defmacro f++ (x &optional (delta 1))
-  "Same as INCF but hopefully optimised for fixnums."
-  `(setf ,x (+ (the fixnum ,x) (the fixnum ,delta))))
-
-(defun soundex (word &optional (key-length 4))
-  "Knuth's Soundex algorithm.  Returns a string representing the
-sound of a certain word (English).  Different words will thus
-yield the same output string.  To compare two string by the
-sound, simply do:
-
-   (string= (soundex str1) (soundex str2))
-
-Examples:
-
-   (soundex \"Knuth\") => \"K530\"
-   (soundex \"Kant\") => \"K530\"
-   (soundex \"Lloyd\") => \"L300\"
-   (soundex \"Ladd\") => \"L300\""
-  (declare (type string word))
-  (flet ((translate-char (char)
-           (awhen (position char "BFPVCGJKQSXZDTLMNR")
-             (elt "111122222222334556" it))))
-    (let ((key (make-string key-length :initial-element #\0))
-          (word-length (length word)))
-      (setf (elt key 0) (elt word 0))
-      (loop
-         with previous-sound = (translate-char (char-upcase (elt word 0)))
-         with j = 1
-         for i from 1 by 1 below word-length
-         for c = (char-upcase (elt word i))
-         while (< j key-length)
-         do (be sound (translate-char c)
-              (cond ((not (eq sound previous-sound))
-                     (unless (member c '(#\H #\W))
-                       (setf previous-sound sound))
-                     (when sound
-                       (setf (elt key j) sound)
-                       (incf j))))))
-      key)))
-
-(defun string-soundex= (string1 string2)
-  (let ((l1 (split-at +whitespace+ string1))
-        (l2 (split-at +whitespace+ string2)))
-    (and (= (length l1) (length l2))
-         (every #'string= (mapcar #'soundex l1) (mapcar #'soundex l2)))))
-
-#+(OR)
-(defun soundex-test ()
-  (let* ((words1 '("Euler" "Gauss" "Hilbert" "Knuth" "Lloyd" "Lukasiewicz" "Wachs"))
-         (words2 '("Ellery" "Ghosh" "Heilbronn" "Kant" "Ladd" "Lissajous" "Waugh"))
-         (results '("E460" "G200" "H416" "K530" "L300" "L222" "W200")))
-    (mapc #'(lambda (w1 w2 r)
-              (let ((r1 (soundex w1))
-                    (r2 (soundex w2)))
-                (format t "~A = ~A, ~A = ~A => ~A~%" w1 r1 w2 r2
-                        (if (and (string= r1 r2)
-                                 (string= r r1))
-                            "OK"
-                            (format nil "ERROR (expected ~A)" r)))))
-          words1 words2 results)
-    (values)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; (defstruct cache-slot ()
-;;   ((previous :type (or cache-slot null)
-;; 	     :initarg :previous
-;; 	     :initform nil
-;; 	     :accessor cslot-previous)
-;;    (key :initarg :key
-;; 	:accessor cslot-key)
-;;    (value :initarg :value
-;; 	  :accessor cslot-value)
-;;    (next :type (or cache-slot null)
-;; 	 :initarg :next
-;; 	 :initform nil
-;; 	 :accessor cslot-next)))
-
-;; (defmethod print-object ((object cache-slot) stream)
-;;   (print-unreadable-object (object stream :type t)
-;;     (if (slot-boundp object 'key)
-;; 	(format stream "key=~S, value=~S" (cslot-key object) (cslot-value object))
-;; 	(format stream "NULL"))))
-
-
-(defstruct (double-linked-element (:conc-name dle-))
-  (previous nil :type (or double-linked-element null))
-  value
-  (next nil :type (or double-linked-element null)))
-
-(defmethod print-object ((object double-linked-element) stream)
-  (print-unreadable-object (object stream :type t)
-    (format stream "value=~S" (dle-value object))))
-
-(defun cons-dle (value previous next)
-  (declare (type (or double-linked-element null) previous next))
-  (be new-element (make-double-linked-element :previous previous :next next :value value)
-    (when previous
-      (setf (dle-next previous) new-element))
-    (when next
-      (setf (dle-previous next) new-element))
-    new-element))
-
-(defun dle-remove (dle-object)
-  "Remove the DLE-OBJECT from its current position in the list of
-elements agjusting the pointer of dle-objects before and after this
-one (if any)."
-  (declare (type double-linked-element dle-object))
-  (awhen (dle-next dle-object)
-    (setf (dle-previous it) (dle-previous dle-object)))
-  (awhen (dle-previous dle-object)
-    (setf (dle-next it) (dle-next dle-object))))
-
-(defun dle-map (function dle-object)
-  (when dle-object
-    (make-double-linked-element :value (funcall function (dle-value dle-object))
-                                :previous (dle-previous dle-object)
-                                :next (dle-map function (dle-next dle-object)))))
-
-(defmacro do-dle ((var dle &optional (result nil)) &body body)
-  "Iterate over a list of DOUBLE-LINKED-ELEMENTs and map body to
-each element's value.  Bind VAR to the value on each iteration."
-  (be cursor (gensym)
-    `(do ((,cursor ,dle (dle-next ,cursor)))
-         ((not ,cursor) ,result)
-       (be ,var (dle-value ,cursor)
-         ,@body))))
-
-(defmacro do-dle* ((var dle &optional (result nil)) &body body)
-  "Same as DO-DLE but VAR is a symbol macro, so that BODY can
-modify the element's value."
-  (be cursor (gensym)
-    `(symbol-macrolet ((,var (dle-value ,cursor)))
-       (do ((,cursor ,dle (dle-next ,cursor)))
-           ((not ,cursor) ,result)
-         ,@body))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defclass double-linked-list ()
-  ((elements :type double-linked-element
-             :documentation "The actual list of elements held by this object.")
-   (last-element :type double-linked-element))
-  (:documentation
-   "A double linked list where elements can be added or removed
-from either end."))
-
-(defmethod initialize-instance ((object double-linked-list) &rest rest)
-  (declare (ignorable rest))
-  (call-next-method)
-  (with-slots (last-element elements) object
-    (setf last-element (make-double-linked-element)
-          elements last-element)))
-
-(defmethod print-object ((object double-linked-list) stream)
-  (print-unreadable-object (object stream :type t)
-    (be elements '()
-      (do-dle (e (slot-value object 'elements))
-        (push e elements))
-      (format stream "elements=~S" (nreverse elements)))))
-
-(defgeneric pop-first (double-linked-list)
-  (:documentation
-   "Pop the first element of a double-linked-list."))
-(defgeneric pop-last (double-linked-list)
-  (:documentation
-   "Pop the last element of a double-linked-list."))
-(defgeneric push-first (item double-linked-list)
-  (:documentation
-   "Push an item in front of a double-linked-list."))
-(defgeneric push-last (item double-linked-list)
-  (:documentation
-   "Append an item to a double-linked-list."))
-(defgeneric list-map (function double-linked-list)
-  (:documentation
-   "Map a function to a double-linked-list."))
-(defgeneric dll-find-cursor (object dll &key test key))
-(defgeneric dll-find (object dll &key test key))
-(defgeneric dll-remove (cursor dll))
-
-(defmethod pop-last ((list double-linked-list))
-  "Drop the last element in the dl list."
-  (with-slots (last-element) list
-    (awhen (dle-previous last-element)
-      (dle-remove it)
-      (dle-value it))))
-
-(defmethod pop-first ((list double-linked-list))
-  "Drop the first element in the dl list."
-  (with-slots (elements) list
-    (when (dle-next elements)
-      (prog1 (dle-value elements)
-        (setf (dle-previous (dle-next elements)) nil
-              elements (dle-next elements))))))
-
-(defmethod push-first (value (list double-linked-list))
-  (with-slots (elements) list
-    (setf elements (cons-dle value nil elements)))
-  list)
-
-(defmethod push-last (value (list double-linked-list))
-  (with-slots (last-element) list
-    (cons-dle value (dle-previous last-element) last-element))
-  list)
-
-(defmethod list-map (function (list double-linked-list))
-  (labels ((map-dll (dle)
-             (when (dle-next dle)
-               (make-double-linked-element
-                :value (funcall function (dle-value dle))
-                :previous (dle-previous dle)
-                :next (map-dll (dle-next dle))))))
-    (map-dll (slot-value list 'elements))))
-
-(defmethod dll-find-cursor (object (list double-linked-list) &key (test #'eql) (key #'identity))
-  (do ((cursor (slot-value list 'elements) (dle-next cursor)))
-      ((not (dle-next cursor)))
-    (be value (dle-value cursor)
-      (when (funcall test (funcall key value) object)
-        (return cursor)))))
-
-(defmethod dll-find (object (list double-linked-list) &key (test #'eql) (key #'identity))
-  (awhen (dll-find-cursor object list :test test :key key)
-    (dle-value it)))
-
-(defmethod dll-remove ((cursor double-linked-element) (list double-linked-list))
-  (with-slots (elements) list
-    (if (dle-previous cursor)
-        (dle-remove cursor)
-        (setf (dle-previous (dle-next elements)) nil
-              elements (dle-next elements))))
-  list)
-
-(defmacro do-dll ((var list &optional (result nil)) &body body)
-  "Iterate over a dll and map body to each element's
-value.  Bind VAR to the value on each iteration."
-  (be cursor (gensym)
-    `(do ((,cursor (slot-value ,list 'elements) (dle-next ,cursor)))
-         ((not (dle-next ,cursor)) ,result)
-       (be ,var (dle-value ,cursor)
-         ,@body))))
-
-(defmacro do-dll* ((var list &optional (result nil)) &body body)
-  "Same as DO-DLL but VAR is a symbol macro, so that BODY can
-modify the element's value."
-  (be cursor (gensym)
-    `(symbol-macrolet ((,var (dle-value ,cursor)))
-       (do ((,cursor (slot-value ,list 'elements) (dle-next ,cursor)))
-           ((not (dle-next ,cursor)) ,result)
-         ,@body))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defclass limited-list (double-linked-list)
-  ((max-size :initform nil
-             :initarg :size
-             :reader max-size
-             :type (or integer null)
-             :documentation "Size limit to which the list is allowed to grow to.  NIL = no limit.")
-   (size :initform 0
-         :reader size
-         :type integer
-         :documentation "Current number of elements in the list."))
-  (:documentation
-   "A double linked list where the maximum number of elements can
-be limited."))
-
-(defun dll-member-p (dle list)
-  (with-slots (elements size) list
-    (do ((e elements (dle-next e)))
-        ((not e))
-      (when (eq e dle)
-        (return t)))))
-
-(defmethod dll-remove ((cursor double-linked-element) (list limited-list))
-  (with-slots (size) list
-    (unless (zerop size)
-      (decf size)
-      (call-next-method)))
-  list)
-
-(defmethod pop-first ((list limited-list))
-  (with-slots (size) list
-    (unless (zerop size)
-      (decf size)
-      (call-next-method))))
-
-(defmethod pop-last ((list limited-list))
-  (with-slots (size) list
-    (unless (zerop size)
-      (decf size)
-      (call-next-method))))
-
-(defmethod push-first (value (list limited-list))
-  "Add in front of the list and drop the last element if list is
-full."
-  (declare (ignore value))
-  (prog1 (call-next-method)
-    (with-slots (max-size size last-element) list
-      (if (or (not max-size)
-              (< size max-size))
-          (incf size)
-          (dle-remove (dle-previous last-element))))))
-
-(defmethod push-last (value (list limited-list))
-  "Add at the end of the list and drop the first element if list
-is full."
-  (declare (ignore value))
-  (prog1 (call-next-method)
-    (with-slots (max-size size elements) list
-      (if (or (not max-size)
-              (< size max-size))
-        (incf size)
-        (setf (dle-previous (dle-next elements)) nil
-              elements (dle-next elements))))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defclass sorted-list (limited-list)
-  ((test :type function
-         :initarg :test))
-  (:documentation
-   "A double linked list where elements are inserted in a
-sorted order."))
-
-(defgeneric insert (item sorted-list)
-  (:documentation
-   "Insert an item in a sorted-list."))
-
-(defmethod insert (item (sl sorted-list))
-  "Insert ITEM in SL, which is a sorted double linked list,
-before the item for which TEST is true or at the end of the list.
-Returns two values, the modified list and the cursor to the new
-element."
-  (with-slots (max-size size elements test last-element) sl
-    (do ((cursor elements (dle-next cursor)))
-        ((or (not (dle-next cursor))
-             (funcall test item (dle-value cursor)))
-         (if (dle-previous cursor)
-             (cons-dle item (dle-previous cursor) cursor)
-             (setf elements (cons-dle item nil cursor)))
-         (if (or (not max-size)
-                  (< size max-size))
-             (incf size)
-             (dle-remove (dle-previous last-element)))
-         (values sl (dle-previous cursor))))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defclass heap ()
-  ((less-than :type function
-              :initarg :test
-              :documentation "The heap invariant.")
-   (data :type array
-         :documentation "The heap tree representation.")))
-
-(defmethod initialize-instance ((heap heap) &rest args)
-  (declare (ignore args))
-  (call-next-method)
-  (with-slots (data) heap
-    (setf data (make-array 0 :fill-pointer 0 :adjustable t))))
-
-(defgeneric heap-add (heap item))
-
-(defun bubble-up (heap pos)
-  (with-slots (data less-than) heap
-    (loop
-       for current = pos then parent
-       for parent = (truncate (1- current) 2)
-       until (or (zerop current)
-                 (funcall less-than (aref data parent) (aref data current)))
-       do (rotatef (aref data current) (aref data parent)))))
-
-(defmethod heap-add ((heap heap) item)
-  (with-slots (data) heap
-    (vector-push-extend item data)
-    (bubble-up heap (1- (fill-pointer data)))))
-
-(defgeneric heap-size (heap))
-
-(defmethod heap-size ((heap heap))
-  (fill-pointer (slot-value heap 'data)))
-
-(defgeneric heap-empty-p (heap))
-
-(defmethod heap-empty-p ((heap heap))
-  (zerop (heap-size heap)))
-
-
-(defgeneric heap-pop (heap))
-
-(defun percolate-down (heap pos)
-  (with-slots (data less-than) heap
-    (loop
-       with end = (fill-pointer data)
-       for current = pos then child
-       for left-child = (+ 1 (* 2 current))
-       for right-child = (+ 2 (* 2 current))
-       for child = (cond ((>= left-child end)
-                          (return))
-                         ((>= right-child end)
-                          left-child)
-                         ((funcall less-than (aref data left-child) (aref data right-child))
-                          left-child)
-                         (t
-                          right-child))
-       while (funcall less-than (aref data child) (aref data current))
-       do (rotatef (aref data current) (aref data child)))))
-
-(defmethod heap-pop ((heap heap))
-  (assert (not (heap-empty-p heap)))
-  (with-slots (data) heap
-    (be root (aref data 0)
-      (setf (aref data 0) (vector-pop data))
-      (percolate-down heap 0)
-      root)))
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defstruct (lru-cache-slot (:include double-linked-element)
-                           (:conc-name lruc-slot-))
-  key)
-
-(defmethod print-object ((object lru-cache-slot) stream)
-  (print-unreadable-object (object stream :type t)
-    (format stream "key=~S value=~S" (lruc-slot-key object) (lruc-slot-value object))))
-
-(defvar *default-cache-size* 100
-  "Default size of a LRU cache if it's not specified at instantiation
-time.")
-
-(defclass lru-cache ()
-  ((max-size :initform *default-cache-size*
-             :initarg :size
-             :reader max-size
-             :type (or integer null)
-             :documentation
-             "Maximum number of elements that the cache can fit.")
-   (elements-list :type lru-cache-slot
-                  :documentation "The list of elements held by the cache.")
-   (elements-hash :type hash-table
-                  :documentation "The hash table of the elements held bye the cache.")
-   (last-element :type lru-cache-slot)
-   (size :initform 0
-         :reader size
-         :type integer
-         :documentation "Current number of elements in the cache.")
-   (finalizer :initform nil
-              :initarg :finalizer
-              :documentation
-              "Procedure to call when elements are dropped from cache."))
-  (:documentation
-   "An objects cache that keeps the elements used more often and
-drops those that are used less often.  The usage is similar to an
-hash table.  Elements are added to the list up to MAX-SIZE, then
-any new element will drop the less used one in the cache.  Every
-time an element is set or retrieved it goes in front of a list.
-Those which get at the end of the list are dropped when more room
-is required."))
-
-(defmethod initialize-instance ((object lru-cache) &key test &allow-other-keys)
-  (call-next-method)
-  (with-slots (last-element elements-list elements-hash) object
-    (setf last-element (make-lru-cache-slot)
-          elements-list last-element
-          elements-hash (if test
-                            (make-hash-table :test test)
-                            (make-hash-table)))))
-
-(defgeneric getcache (key cache)
-  (:documentation
-   "Get an item with KEY from a CACHE."))
-
-(defgeneric (setf getcache) (value key cache)
-  (:documentation
-   "Set or add an item with KEY in a CACHE."))
-
-(defgeneric remcache (key cache)
-  (:documentation
-   "Remove an item with KEY from a CACHE."))
-
-(defun move-in-front-of-cache-list (slot cache)
-  "Relocate slot to the front of the elements list in cache.
-This will stretch its lifespan in the cache."
-  (declare (type lru-cache-slot slot)
-           (type lru-cache cache))
-  (with-slots (elements-list) cache
-    ;; unless it's already the first
-    (unless (eq slot elements-list)
-      ;; remove the slot from its original place...
-      (dle-remove slot)
-      ;; ... and add it in front of the list
-      (setf (lruc-slot-next slot) elements-list
-            (lruc-slot-previous slot) nil
-            (lruc-slot-previous elements-list) slot
-            elements-list slot))))
-
-(defun drop-last-cache-element (cache)
-  "Drop the last element in the list of the cache object."
-  (declare (type lru-cache cache))
-  (with-slots (last-element elements-hash finalizer) cache
-    (let ((second-last (lruc-slot-previous last-element)))
-      (assert second-last)
-      (when finalizer
-        (funcall finalizer (lruc-slot-value second-last)))
-      (dle-remove second-last)
-      (remhash (lruc-slot-key second-last) elements-hash))))
-
-(defun add-to-cache (slot cache)
-  (declare (type lru-cache-slot slot)
-           (type lru-cache cache))
-  (move-in-front-of-cache-list slot cache)
-  (with-slots (max-size size elements-hash) cache
-    (setf (gethash (lruc-slot-key slot) elements-hash) slot)
-    (if (and max-size
-             (< size max-size))
-        (incf size)
-        (drop-last-cache-element cache))))
-
-(defmethod getcache (key (cache lru-cache))
-  (multiple-value-bind (slot found?) (gethash key (slot-value cache 'elements-hash))
-    (when found?
-      (move-in-front-of-cache-list slot cache)
-      (values (lruc-slot-value slot) t))))
-
-(defmethod (setf getcache) (value key (cache lru-cache))
-  (with-slots (elements-hash elements-list) cache
-    (multiple-value-bind (slot found?) (gethash key elements-hash)
-      (if found?
-          (progn
-            (move-in-front-of-cache-list slot cache)
-            (setf (lruc-slot-value slot) value))
-          (add-to-cache (make-lru-cache-slot :key key :value value) cache))
-      value)))
-
-(defmethod remcache (key (cache lru-cache))
-  (with-slots (elements-hash size elements-list finalizer) cache
-    (multiple-value-bind (slot found?) (gethash key elements-hash)
-      (when found?
-        (remhash key elements-hash)
-        (when finalizer
-          (funcall finalizer (lruc-slot-value slot)))
-        (when (eq slot elements-list)
-          (setf elements-list (dle-next slot)))
-        (dle-remove slot)
-        (decf size)
-        t))))
-
-(defmacro cached (cache key value)
-  "If KEY is found in CACHE return the associated object.  Otherwise
-store VALUE for later re-use."
-  (with-gensyms (object my-cache my-key my-value found?)
-    `(let* ((,my-cache ,cache)
-            (,my-key ,key))
-       (multiple-value-bind (,object ,found?) (getcache ,my-key ,my-cache)
-         (if ,found?
-             ,object
-             (let ((,my-value ,value))
-               (setf (getcache ,my-key ,my-cache) ,my-value)
-               ,my-value))))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
-(declaim (inline list->string))
-(defun list->string (list)
-  "Coerce a list of characters into a string."
-  (coerce list 'string))
-
-(defun setuid (id)
-  "Set the Unix real user id."
-  (when (stringp id)
-    (setf id (find-uid id)))
-  #+sbcl (sb-posix:setuid id)
-  #+cmu (unix:unix-setuid id)
-  #+clisp (posix::%setuid id)		; not verified -wcp26/8/09.
-  #-(or cmu sbcl clisp)
-  (error "setuid unsupported under this Lisp implementation"))
-
-(defun seteuid (id)
-  "Set the Unix effective user id."
-  (when (stringp id)
-    (setf id (find-uid id)))
-  #+sbcl (sb-posix:seteuid id)
-  #+cmu (unix:unix-setreuid -1 id)
-  #+clisp (posix::%seteuid id)		; not verified -wcp26/8/09.
-  #-(or cmu sbcl clisp)
-  (error "seteuid unsupported under this Lisp implementation"))
-
-(defun find-uid (name)
-  "Find the user id of NAME.  Return an integer."
-  #+sbcl (awhen (sb-posix:getpwnam name)
-           (sb-posix:passwd-uid it))
-  #+cmu (awhen (unix:unix-getpwnam name)
-          (unix:user-info-uid it))
-  #-(or cmu sbcl)
-  (error "Unable to find a UID on this Lisp system."))
-
-#+clisp (ffi:def-call-out %getuid
-            (:name "getuid")
-          (:arguments)
-          (:return-type ffi:int)
-          (:library "libc.so"))
-
-(defun getuid ()
-  "Return the Unix user id.  This is an integer."
-  #+sbcl (sb-unix:unix-getuid)
-  #+cmu (unix:unix-getuid)
-  #+clisp (%getuid)
-  #-(or cmu sbcl clisp)
-  (error "getuid unsupported under this Lisp implementation"))
-
-(defun super-user-p (&optional id)
-  "Return true if the user ID is zero.  ID defaults to the current
-user id."
-  (zerop (or id (getuid))))
-
-(defmacro with-euid (uid &body forms)
-  "Switch temporarely to Unix user id UID, while performing FORMS."
-  (with-gensyms (ruid)
-    `(be ,ruid (getuid)
-       (seteuid ,uid)
-       (unwind-protect (progn ,@forms)
-         (seteuid ,ruid)))))
-
-(defun get-logname (&optional uid)
-  "Return the login id of the user.  This is a string and it is not
-the Unix uid, which is a number."
-  (unless uid
-    (setf uid (getuid)))
-  (when (stringp uid)
-    (setf uid (find-uid uid)))
-  (when uid
-    #+sbcl (sb-unix:uid-username uid)
-    #+cmu (unix:user-info-name (unix:unix-getpwuid uid))
-    #+clisp (posix:user-info-login-id (posix:user-info uid))
-    #-(or cmu sbcl clisp)
-    (error "get-logname unsupported under this Lisp implementation")))
-
-(defun get-user-name (&optional uid)
-  "Return the user name, taken from the GCOS field of the /etc/passwd
-file."
-  (unless uid
-    (setf uid (getuid)))
-  (when (stringp uid)
-    (setf uid (find-uid uid)))
-  (when uid
-    (car (split-string-at-char #+cmu (unix:user-info-gecos (unix:unix-getpwuid uid))
-                               #+sbcl (sb-posix:passwd-gecos (sb-posix:getpwuid uid))
-                               #-(or cmu sbcl) (error "can't getpwuid() on this Lisp system.")
-                               #\,))))
-
-(defun get-user-home (&optional uid)
-  (unless uid
-    (setf uid (getuid)))
-  (when (stringp uid)
-    (setf uid (find-uid uid)))
-  (when uid
-    #+cmu (unix:user-info-dir (unix:unix-getpwuid uid))
-    #+sbcl (sb-posix:passwd-dir (sb-posix:getpwuid uid))))
-
-;; Rather stupid, but the mnemonic is worth it
-(declaim (inline alist->plist))
-(defun alist->plist (alist)
-  "Convert an association list into a property list.  The alist
-elements are assumed to be lists of just two elements: the key
-and the value.  If the element list is longer this function
-doesn't work."
-  (mapcan #'identity alist))
-
-(defun plist->alist (plist &optional pairs-p)
-  "Convert a property list into an association list.  The alist
-elements wiil be lists of just two elements: the key and the
-value.  If PAIRS-P is true the alist elements will be pairs."
-  (loop
-     for (key val) on plist by #'cddr
-     collect (if pairs-p
-                 (cons key val)
-                 (list key val))))
-
-(defun string->byte-vector (string &key start end)
-  "Convert a string of characters into a vector of (unsigned-byte
-8) elements."
-  (map '(vector (unsigned-byte 8)) #'char-code
-       (if (or start end)
-           (subseq string (or start 0) end)
-           string)))
-
-(defun byte-vector->string (vector &key start end)
-  "Convert a vector of (unsigned-byte 8) elements into a string
-of characters."
-  (map 'string #'code-char
-       (if (or start end)
-           (subseq vector (or start 0) end)
-           vector)))
-
-(defun outdated-p (file dependencies)
-  "Check if FILE has been modified before any of its
-DEPENDENCIES."
-  (be epoch (and (probe-file file)
-                 (file-write-date file))
-    ;; if file is missing altogether, we consider it outdated
-    (or (not epoch)
-        (loop
-           for dep in dependencies
-           thereis (aand (probe-file dep)
-                         (file-write-date dep)
-                         (> it epoch))))))
-
-(defmacro let-places (places-and-values &body body)
-  "Execute BODY binding temporarily some places to new values and
-restoring the original values of these places on exit of BODY.  The
-syntax of this macro is identical to LET.  The difference is that
-instead of new variable names this macro binds values to existing
-places (variables)."
-  (be tmp-variables (loop for x in places-and-values collect (gensym))
-    `(let ,(mapcar #'(lambda (tmp-var place-and-value)
-                       (list tmp-var (car place-and-value)))
-                   tmp-variables places-and-values)
-       (unwind-protect
-            (progn
-              ;; as some assignments could signal an error, we assign
-              ;; within the unwind-protect block so that we can always
-              ;; guarantee a consistent state on exit
-              ,@(mapcar #'(lambda (place-and-value)
-                            `(setf ,(car place-and-value) ,(cadr place-and-value)))
-                        places-and-values)
-              ,@body)
-         ,@(mapcar #'(lambda (tmp-var place-and-value)
-                       `(setf ,(car place-and-value) ,tmp-var))
-                   tmp-variables
-                   places-and-values)))))
-
-(defmacro let-slots (accessor/new-value-pairs object &body body)
-  "Execute BODY with some OBJECT's slots temporary sets to new
-values as described in ACCESSOR/NEW-VALUE-PAIRS.  The latter
-should be an alist of accessor names and the value to be assigned
-to that slot.  On exit from BODY, those slots are restored to
-their original value.  See LET-PLACES."
-  (with-gensyms (obj)
-    `(be ,obj ,object
-       (let-places ,(mapcar #'(lambda (av)
-                                `((,(car av) ,obj) ,(cadr av)))
-                            accessor/new-value-pairs)
-         ,@body))))
-
-(defvar *decimal-point* #\.)
-(defvar *thousands-comma* #\,)
-
-(defun format-amount (number &key (decimals 2) (rounder #'round)
-                      (comma *thousands-comma*) (comma-stance 3)
-                      (decimal-point *decimal-point*))
-  "Return a string formatted as fixed decimal point number of DECIMALS
-adding commas every COMMA-STANCE places before the decimal point."
-  (declare (type number number)
-           (type fixnum decimals comma-stance)
-           (type function rounder)
-           (type character comma decimal-point)
-           (optimize (speed 3) (safety 0) (debug 0)))
-  (let* ((int (funcall rounder (* number (expt 10 decimals))))
-         (negative (< int 0)))
-    (declare (integer int))
-    (when negative
-      (setf int (- int)))
-    (let* ((digits (max (1+ decimals)
-                        (1+ (if (zerop int)
-                                0
-                                (truncate (log int 10))))))
-           (string-length (+ digits
-                             ;; the minus sign
-                             (if negative 1 0)
-                             ;; the decimal point
-                             (if (zerop decimals) 0 1)
-                             ;; the thousands commas
-                             (1- (ceiling (- digits decimals) comma-stance))))
-           (string (make-string string-length))
-           (pos (1- string-length)))
-      (declare (type fixnum pos digits))
-      (labels ((add-char (char)
-                 (setf (schar string pos) char)
-                 (decf pos))
-               (add-digit ()
-                 (add-char (digit-char (mod int 10)))
-                 (setf int (truncate int 10))))
-        (unless (zerop decimals)
-          (loop
-             for i fixnum from 0 below decimals
-             do (add-digit))
-          (add-char decimal-point))
-        (loop
-           for i fixnum from 1
-           do (add-digit)
-           while (>= pos (if negative 1 0))
-           when (zerop (mod i comma-stance))
-           do (add-char comma))
-        (when negative
-          (add-char #\-)))
-      string)))
-
-(defun parse-amount (string &key (start 0) end)
-  "Parse STRING as if it was formatted with FORMAT-AMOUNT and return
-the parsed number.  Return NIL if STRING is malformed.  Leading or
-trailing spaces must be removed from the string in advance."
-  (loop
-     with amount = 0
-     with decimals = nil
-     with negative = (when (and (not (zerop (length string)))
-                                (char= #\- (char string 0)))
-                       (incf start)
-                       t)
-     for i from start below (or end (length string))
-     for c = (char string i)
-     do (cond ((char= c *decimal-point*)
-               (if decimals
-                   (return nil)
-                   (setf decimals 0)))
-              ((char= c *thousands-comma*))
-              (t
-               (be d (digit-char-p c)
-                 (cond ((not d)
-                        (return nil))
-                       (decimals
-                        (incf decimals)
-                        (incf amount (/ d (expt 10 decimals))))
-                       (t
-                        (setf amount (+ d (* amount 10))))))))
-     finally (return (if negative
-                         (- amount)
-                         amount))))
-
-(defmacro with-package (name &body body)
-  `(let ((*package* (find-package ,name)))
-     ,@body))
-
-(defun bytes-simple-string (n &optional imply-bytes)
-  "Return a string describing N using a unit of measure multiple
-of a byte that is most apporpriate for the magnitude of N.  A
-kilobyte is 1024 not 1000 bytes, everything follows."
-  (let* ((kilo 1024)
-         (mega (* kilo kilo))
-         (giga (* kilo mega))
-         (tera (* mega mega))
-         (peta (* kilo tera)))
-    (apply #'format nil "~,1F~A"
-           (cond ((> n (* 2 peta))
-                  (list (/ n peta) (if imply-bytes "P" "PB")))
-                 ((> n (* 2 tera))
-                  (list (/ n tera) (if imply-bytes "T" "TB")))
-                 ((> n (* 2 giga))
-                  (list (/ n giga) (if imply-bytes "G" "GB")))
-                 ((> n (* 2 mega))
-                  (list (/ n mega) (if imply-bytes "M" "MB")))
-                 ((> n (* 2 kilo))
-                  (list (/ n kilo) (if imply-bytes "K" "KB")))
-                 (t (list n (if imply-bytes "" " bytes")))))))
-
-;; WARNING: This function may or may not work on your Lisp system.  It
-;; all depends on how the OPEN function has been implemented regarding
-;; the :IF-EXISTS option.  This function requires that OPEN be
-;; implemented in a way so that the checking of the existence of file
-;; and its open attempt be atomic.  If the Lisp OPEN first checks that
-;; the file exists and then tries to open it, this function won't be
-;; reliable.  CMUCL seems to use the O_EXCL open() flag in the right
-;; way.  So at least on CMUCL this function will work.  Same goes for
-;; SBCL.
-(defun make-lock-files (pathnames &key (sleep-time 7) retries (suspend 13) expiration)
-  "Create semaphore files.  If it can't create all the specified
-files in the specified order, it waits SLEEP-TIME seconds and
-retries the last file that didn't succeed.  You can specify the
-number of RETRIES to do until failure is returned.  If the number
-of retries is NIL this function will retry forever.
-
-If it tries RETRIES times without success, this function signal
-an error and removes all the lock files it created until then.
-
-All files created by lock file will be read-only.
-
-If you specify a EXPIRATION then an existing lock file will be
-removed by force after EXPIRATION seconds have passed since the
-lock file was last modified/created (most likely by some other
-program that unexpectedly died without cleaning up its lock
-files).  After a lock file has been removed by force, a
-suspension of SUSPEND seconds is taken into account, in order to
-prevent the inadvertent immediate removal of any newly created
-lock file by another program."
-  (be locked '()
-    (flet ((lock (file)
-             (when (and expiration
-                        (> (get-universal-time)
-                           (+ (file-write-date file) expiration)))
-               (delete-file file)
-               (when suspend
-                 (sleep suspend)))
-             (do ((i 0 (1+ i))
-                  (done nil))
-                 (done)
-               (unless (or (not retries)
-                           (< i retries))
-                 (error "Can't create lock file ~S: tried ~A time~:P." file retries))
-               (with-open-file (out file :direction :output :if-exists nil)
-                 (cond (out
-                        (format out "Lock file created on ~A~%" (time-string (get-universal-time)))
-                        (setf done t))
-                       (sleep-time
-                        (sleep sleep-time)))))))
-      (unwind-protect
-           (progn
-             (dolist (file pathnames)
-               (lock file)
-               (push file locked))
-             (setf locked '()))
-        (mapc #'delete-file locked)))))
-
-(defmacro with-lock-files ((lock-files &rest lock-args) &body body)
-  "Execute BODY after creating LOCK-FILES.  Remove the lock files
-on exit.  LOCK-ARGS are passed to MAKE-LOCK-FILES."
-  (with-gensyms (files)
-    `(be ,files (list ,@lock-files)
-       (make-lock-files ,files ,@lock-args)
-       (unwind-protect (progn ,@body)
-         (mapc #'delete-file ,files)))))
-
-(defun getpid ()
-  #+cmu (unix:unix-getpid)
-  #+sbcl (sb-unix:unix-getpid)
-  #+clisp (ext:process-id)
-  #-(or cmu sbcl clisp)
-   (error "getpid unsupported under this Lisp implementation"))
-
-(defmacro on-error (form &body error-forms)
-  "Execute FORM and in case of error execute ERROR-FORMS too.
-This does _not_ stop the error from propagating."
-  (be done-p (gensym)
-    `(be ,done-p nil
-       (unwind-protect
-            (prog1
-                ,form
-              (setf ,done-p t))
-         (unless ,done-p
-           ,@error-forms)))))
-
-(defun floor-to (x aim)
-  "Round X down to the nearest multiple of AIM."
-  (* (floor x aim) aim))
-
-(defun round-to (x aim)
-  "Round X to the nearest multiple of AIM."
-  (* (round x aim) aim))
-
-(defun ceiling-to (x aim)
-  "Round X up to the nearest multiple of AIM."
-  (* (ceiling x aim) aim))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defstruct queue
-  first
-  last)
-
-(defgeneric queue-append (queue objects))
-(defgeneric queue-pop (queue))
-(defgeneric queue-empty-p (queue))
-
-(defmethod queue-append ((queue queue) (objects list))
-  (cond ((null (queue-first queue))
-         (setf (queue-first queue) objects
-               (queue-last queue) (last objects)))
-        (t
-         (setf (cdr (queue-last queue)) objects
-               (queue-last queue) (last objects))))
-  queue)
-
-(defmethod queue-append ((queue queue) object)
-  (queue-append queue (list object)))
-
-(defmethod queue-pop ((queue queue))
-  (prog1 (car (queue-first queue))
-    (setf (queue-first queue) (cdr (queue-first queue)))))
-
-(defmethod queue-empty-p ((queue queue))
-  (null (queue-first queue)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun package-locked-p (package)
-  #+sbcl (sb-ext:package-locked-p package)
-  #+cmu (ext:package-definition-lock package)
-  #+clisp (ext:package-lock package)
-  #-(or sbcl cmu clisp) (error "Don't know how to check whether a package might be locked."))
-
-(defun forget-documentation (packages)
-  "Remove documentation from all known symbols in PACKAGES.  If
-PACKAGES is NIL remove documentations from all packages.  This may not
-make sense if your Lisp image has been built so that existing objects
-don't get garbage collected.  It may work for your own code, though.
-Locked packages are left alone.  If you need to do those too, unlock
-them first."
-  (flet ((forget (symbol)
-           (dolist (type '(compiler-macro function method-combination setf structure type variable))
-             (when (ignore-errors (documentation symbol type))
-               (setf (documentation symbol type) nil)))))
-    (setf packages (mapcar #'(lambda (pkg)
-                               (if (packagep pkg)
-                                   (package-name pkg)
-                                   (package-name (find-package pkg))))
-                           packages))
-    (setf packages
-          ;; don't try to modify locked packages
-          (remove-if #'package-locked-p
-                     (mapcar #'find-package
-                             (or packages
-                                 (list-all-packages)))))
-    (dolist (package packages)
-      (with-package-iterator (next package :internal :external)
-        (loop
-           (multiple-value-bind (more? symbol) (next)
-             (unless more?
-               (return))
-             (forget symbol)))))
-    #+(OR) (do-all-symbols (symbol)
-             (when (member (symbol-package symbol) packages)
-               (forget symbol))))
-  (values))
-
-(defun load-compiled (pathname &optional compiled-pathname)
-  "Make sure to compile PATHNAME before loading it.  Don't compile if
-the compiled version is more recent than its source."
-  ;; be tolerant if we didn't get a type
-  (unless (probe-file pathname)
-    (setf pathname (merge-pathnames pathname (make-pathname :type "lisp"))))
-  (if (probe-file pathname)
-      (progn
-        (setf compiled-pathname (or compiled-pathname
-                                    (compile-file-pathname pathname)))
-        (when (or (not (probe-file compiled-pathname))
-                  (< (file-write-date compiled-pathname)
-                     (file-write-date pathname)))
-          (compile-file pathname))
-        (load compiled-pathname))
-      (error "Can't load ~A as it doesn't exist." pathname)))
-
-;; Just a silly mnemonic for those used to lesser languages
-(defmacro swap (x y)
-  "Swap values of places X and Y."
-  `(rotatef ,x ,y))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defmacro show (&rest things)
-  "Debugging macro to show the name and content of variables.  You can
-also specify forms, not just variables."
-  (let ((*print-pretty* nil))
-    `(let ((*print-circle* t))
-       (format t ,(format nil "~~&~{~A=~~:W~~%~}" things)
-               ,@things)
-       (finish-output)
-       (values))))
-
-(defmacro memoize-function (name &key test)
-  "Make function NAME memoized.  TEST is passed to MAKE-HASH-TABLE."
-  `(setf (get ',name 'results-hash-table)
-         (make-hash-table ,@(when test (list :test test)))))
-
-(defmacro defun-memoized (name args &body forms)
-  "Define function NAME and make it memoizable.  Then the MEMOIZED
-macro can be used to call this function and memoize its results.  The
-function NAME must accept only one argument and return just one
-argument; more complicated cases are not considered.  The hash table
-test function is the default 'EQL."
-  `(eval-when (:load-toplevel :compile-toplevel)
-     (defun ,name ,args ,@forms)
-     (memoize-function ,name)))
-
-(defmacro memoized (function arg)
-  "If necessary call FUNCTION passing ARG so that its return value is
-memoized.  The next time this form is executed with the same argument
-value, the memoized result is returned instead of executing FUNCTION."
-  (with-gensyms (table key result not-found)
-    `(be* ,key ,arg
-          ,table (get ',function 'results-hash-table)
-          ,not-found (list nil)
-          ,result (gethash ,key ,table ,not-found)
-       (if (eq ,not-found ,result)
-           (setf (gethash ,key ,table)
-                 (,function ,key))
-           ,result))))
-
-
-(defmacro save-file-excursion ((stream &optional position) &body forms)
-  "Execute FORMS returning, on exit, STREAM to the position it was
-before FORMS.  Optionally POSITION can be set to the starting offset."
-  (unless position
-    (setf position (gensym)))
-  `(be ,position (file-position ,stream)
-     (unwind-protect (progn ,@forms)
-       (file-position ,stream ,position))))
-
-(defun circular-list (&rest elements)
-  "Return a circular list of ELEMENTS."
-  (setf (cdr (last elements)) elements))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun getenv (var)
-  "Return the string associate to VAR in the system environment."
-  #+cmu (cdr (assoc (if (symbolp var)
-                        var
-                        (intern var :keyword))
-                    ext:*environment-list*))
-  #+sbcl (sb-ext:posix-getenv (string var))
-  #+lispworks (hcl:getenv var)
-  #+clisp (ext:getenv (string var))
-  #-(or cmu sbcl lispworks clisp)
-  (error "GETENV not implemented for your Lisp system."))
-
-#+clisp (ffi:def-call-out %setenv
-            (:name "setenv")
-          (:arguments (name ffi:c-string) (value ffi:c-string) (overwrite ffi:int))
-          (:return-type ffi:int)
-          (:library "libc.so"))
-
-#+clisp (ffi:def-call-out %unsetenv
-            (:name "unsetenv")
-          (:arguments (name ffi:c-string))
-          (:return-type ffi:int)
-          (:library "libc.so"))
-
-(defun setenv (name value &optional (overwrite t))
-  (typecase value
-    (string)
-    (pathname
-     (setf value (native-namestring value)))
-    (t
-     (setf value (format nil "~A" value))))
-  #+sbcl (unless (zerop (sb-posix:setenv name value (if overwrite 1 0)))
-           (error "unable to setenv ~A: errno=~A." name
-                  (sb-alien:get-errno)))
-  #+cmu (be key (keywordify name)
-          (aif (assoc key
-                      ext:*environment-list*)
-               (when overwrite
-                 (setf (cdr it) value))
-               (setf ext:*environment-list*
-                     (cons (cons key value)
-                           ext:*environment-list*))))
-  #-(or cmu sbcl) (unless (zerop (%setenv name value (if overwrite 1 0)))
-                    (error "unable to setenv ~A." name)))
-
-(defun unsetenv (name)
-  #+sbcl (unless (zerop (sb-posix:unsetenv name))
-           (error "unable to unsetenv ~A: errno=~A." name
-                  (sb-alien:get-errno)))
-  #+cmu (be key (keywordify name)
-          (setf ext:*environment-list*
-                (delete-if #'(lambda (e)
-                               (eq (car e) key))
-                           ext:*environment-list*)))
-  #-(or cmu sbcl) (unless (zerop (%unsetenv name))
-                    (error "unable to unsetenv ~A." name)))
-
-(defun (setf getenv) (value name)
-  (if value
-      (setenv name value t)
-      (unsetenv name)))
-
-;; in CMUCL it's much easier (see below)
-#-cmu
-(defmacro with-system-environment ((&rest var-and-values) &body body)
-  (be gensym-alist (mapcar #'(lambda (vv)
-                               (list (gensym) (string (car vv)) (cadr vv)))
-                           var-and-values)
-      `(let ,(mapcar #'(lambda (vv)
-                         (destructuring-bind (varsym var value) vv
-                           (declare (ignore value))
-                           `(,varsym (getenv ,var))))
-                     gensym-alist)
-         (unwind-protect
-              (progn
-                ,@(mapcar #'(lambda (vv)
-                              (destructuring-bind (varsym var value) vv
-                                (declare (ignore varsym))
-                                `(setenv ,var ,value)))
-                          gensym-alist)
-                ,@body)
-           ,@(mapcar #'(lambda (vv)
-                         (destructuring-bind (varsym var value) vv
-                           (declare (ignore value))
-                           `(if ,varsym
-                                (setenv ,var ,varsym)
-                                (unsetenv ,var))))
-                     gensym-alist)))))
-
-#+cmu
-(defmacro with-system-environment ((&rest var-and-values) &body body)
-  `(let ((ext:*environment-list*
-          (append (list ,@(mapcar #'(lambda (vv)
-                                      (destructuring-bind (variable value) vv
-                                        `(cons ,(keywordify variable)
-                                               ,value)))
-                                  var-and-values))
-                  ext:*environment-list*)))
-     ,@body))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun last-member (item list &key key (test #'eq))
-  "Return the last sublist in LIST that is prefixed by ITEM."
-  (loop
-     with l = list and result = nil
-     for l2 = (member item l :key key :test test)
-     while l2
-     do (setf result l2
-              l (cdr l2))
-     finally (return result)))
-
-
-(defun glob->regex (string)
-  "Convert a shell glob expression into a regular expression string."
-  (with-output-to-string (out)
-    ;; globs are always anchored to beginning and end
-    (write-char #\^ out)
-    (loop
-       for i from 0 below (length string)
-       do (be c (char string i)
-            (cond ((char= c #\\)
-                   (setf c (char string (incf i))))
-                  ((find c  ".+()|^$")
-                   (write-char #\\ out))
-                  ((char= c #\*)
-                   (write-char #\. out))
-                  ((char= c #\?)
-                   (setf c #\.)))
-            (write-char c out)))
-    (write-char #\$ out)))
diff --git a/third_party/lisp/sclf/serial.lisp b/third_party/lisp/sclf/serial.lisp
deleted file mode 100644
index 41d32e4c49..0000000000
--- a/third_party/lisp/sclf/serial.lisp
+++ /dev/null
@@ -1,62 +0,0 @@
- ;;; serial.lisp --- serialisation of CLOS objects
-
- ;;; Copyright (C) 2009 by Walter C. Pelissero
-
- ;;; Author: Walter C. Pelissero <walter@pelissero.de>
- ;;; Project: sclf
-
-#+cmu (ext:file-comment "$Module: serial.lisp $")
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 2.1
-;;; of the License, or (at your option) any later version.
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Lesser General Public License for more details.
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free
-;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-;;; 02111-1307 USA
-
-(in-package :sclf)
-
-(defclass printable-object-mixin () ())
-
-(defmacro reconstruct-object (class &rest args)
-  `(apply #'make-instance ',class ',args))
-
-(defun print-readable-instance (object &optional stream)
-  (unless stream
-    (setf stream *standard-output*))
-  (be class (class-of object)
-    (pprint-logical-block (stream (copy-list (class-slots class)) :prefix "#.(" :suffix ")")
-      (flet ((spc ()
-               (write-char #\space stream)))
-        (write 'reconstruct-object :stream stream)
-        (spc)
-        (write (class-name class) :stream stream :escape t :readably t :pretty t)
-        (pprint-exit-if-list-exhausted)
-        (spc)
-        (loop
-           (be* slot (pprint-pop)
-                slot-name (slot-definition-name slot)
-                initarg (car (slot-definition-initargs slot))
-             (when (and initarg
-                        (slot-boundp object slot-name))
-               (write initarg :stream stream)
-               (spc)
-               (when *print-pretty*
-                 (pprint-newline :miser stream))
-               (write (slot-value object slot-name)
-                      :stream stream)
-               (pprint-exit-if-list-exhausted)
-               (if *print-pretty*
-                   (pprint-newline :linear stream)
-                   (spc)))))))))
-
-(defmethod print-object ((object printable-object-mixin) stream)
-  (if *print-readably*
-      (print-readable-instance object stream)
-      (call-next-method)))
diff --git a/third_party/lisp/sclf/sysproc.lisp b/third_party/lisp/sclf/sysproc.lisp
deleted file mode 100644
index 1dd559ebe3..0000000000
--- a/third_party/lisp/sclf/sysproc.lisp
+++ /dev/null
@@ -1,295 +0,0 @@
-;;;  sysproc.lisp --- system processes
-
-;;;  Copyright (C) 2008, 2009, 2010 by Walter C. Pelissero
-
-;;;  Author: Walter C. Pelissero <walter@pelissero.de>
-;;;  Project: sclf
-
-#+cmu (ext:file-comment "$Module: sysproc.lisp $")
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 2.1
-;;; of the License, or (at your option) any later version.
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Lesser General Public License for more details.
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free
-;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-;;; 02111-1307 USA
-
-(in-package :sclf)
-
-(defvar *bourne-shell* "/bin/sh")
-
-(defvar *run-verbose* nil
-  "If true system commands are displayed before execution and standard
-error is not discarded.")
-
-;;
-;; SIGINFO is missing in both CMUCL and SBCL
-;;
-
-#+cmu
-(eval-when (:compile-toplevel :load-toplevel :execute)
-  (defconstant unix::siginfo 29)
-  (defvar siginfo (unix::make-unix-signal :siginfo unix::siginfo "Information"))
-  (export '(unix::siginfo) "UNIX")
-  (pushnew siginfo unix::*unix-signals*))
-
-#+sbcl (in-package :sb-posix)
-#+sbcl
-(eval-when (:execute :compile-toplevel :load-toplevel)
-  (unless (find-symbol "SIGINFO" :sb-posix)
-    (sb-ext:with-unlocked-packages (:sb-posix)
-      (defvar siginfo 29)
-      (export '(SIGINFO)))))
-#+sbcl (in-package :sclf)
-
-(defun signal-number (signal-name)
-  (ecase signal-name
-    ((:abrt :abort)
-     #+cmu unix:sigabrt
-     #+sbcl sb-posix:sigabrt)
-    ((:alrm :alarm)
-     #+cmu unix:sigalrm
-     #+sbcl sb-posix:sigalrm)
-    ((:bus :bus-error)
-     #+cmu unix:sigbus
-     #+sbcl sb-posix:sigbus)
-    ((:chld :child)
-     #+cmu unix:sigchld
-     #+sbcl sb-posix:sigchld)
-    ((:cont :continue)
-     #+cmu unix:sigcont
-     #+sbcl sb-posix:sigcont)
-    #+freebsd((:emt :emulate-instruction)
-              #+cmu unix:sigemt
-              #+sbcl sb-posix:sigemt)
-    ((:fpe :floating-point-exception)
-     #+cmu unix:sigfpe
-     #+sbcl sb-posix:sigfpe)
-    ((:hup :hangup)
-     #+cmu unix:sighup
-     #+sbcl sb-posix:sighup)
-    ((:ill :illegal :illegal-instruction)
-     #+cmu unix:sigill
-     #+sbcl sb-posix:sigill)
-    ((:int :interrupt)
-     #+cmu unix:sigint
-     #+sbcl sb-posix:sigint)
-    ((:io :input-output)
-     #+cmu unix:sigio
-     #+sbcl sb-posix:sigio)
-    (:kill
-     #+cmu unix:sigkill
-     #+sbcl sb-posix:sigkill)
-    ((:pipe :broke-pipe)
-     #+cmu unix:sigpipe
-     #+sbcl sb-posix:sigpipe)
-    ((:prof :profiler)
-     #+cmu unix:sigprof
-     #+sbcl sb-posix:sigprof)
-    (:quit
-     #+cmu unix:sigquit
-     #+sbcl sb-posix:sigquit)
-    ((:segv :segmentation-violation)
-     #+cmu unix:sigsegv
-     #+sbcl sb-posix:sigsegv)
-    (:stop
-     #+cmu unix:sigstop
-     #+sbcl sb-posix:sigstop)
-    ((:sys :system-call)
-     #+cmu unix:sigsys
-     #+sbcl sb-posix:sigsys)
-    ((:term :terminate)
-     #+cmu unix:sigterm
-     #+sbcl sb-posix:sigterm)
-    ((:trap)
-     #+cmu unix:sigtrap
-     #+sbcl sb-posix:sigtrap)
-    ((:tstp :terminal-stop)
-     #+cmu unix:sigtstp
-     #+sbcl sb-posix:sigtstp)
-    ((:ttin :tty-input)
-     #+cmu unix:sigttin
-     #+sbcl sb-posix:sigttin)
-    ((:ttou :tty-output)
-     #+cmu unix:sigttou
-     #+sbcl sb-posix:sigttou)
-    ((:urg :urgent)
-     #+cmu unix:sigurg
-     #+sbcl sb-posix:sigurg)
-    ((:usr1 :user1)
-     #+cmu unix:sigusr1
-     #+sbcl sb-posix:sigusr1)
-    ((:usr2 :user2)
-     #+cmu unix:sigusr2
-     #+sbcl sb-posix:sigusr2)
-    ((:vtalrm :virtual-timer-alarm)
-     #+cmu unix:sigvtalrm
-     #+sbcl sb-posix:sigvtalrm)
-    ((:winch :window-change :window-size-change)
-     #+cmu unix:sigwinch
-     #+sbcl sb-posix:sigwinch)
-    ((:xcpu :exceeded-cpu)
-     #+cmu unix:sigxcpu
-     #+sbcl sb-posix:sigxcpu)
-    ((:xfsz :exceeded-file-size)
-     #+cmu unix:sigxfsz
-     #+sbcl sb-posix:sigxfsz)
-    ;; oddly this is not defined by neither CMUCL nor SBCL
-    (:info 29)))
-
-(defun sysproc-kill (process signal)
-  (when (keywordp signal)
-    (setf signal (signal-number signal)))
-  #+cmu (ext:process-kill process signal)
-  #+sbcl (sb-ext:process-kill process signal)
-  #-(or sbcl cmu) (error "Don't know how to kill a process"))
-
-(defun sysproc-exit-code (process)
-  #+cmu (ext:process-exit-code process)
-  #+sbcl (sb-ext:process-exit-code process)
-  #-(or sbcl cmu) (error "Don't know how to get a process exit code"))
-
-(defun sysproc-wait (process)
-  #+cmu (ext:process-wait process)
-  #+sbcl (sb-ext:process-wait process)
-  #-(or sbcl cmu) (error "Don't know how to wait a process"))
-
-(defun sysproc-input (process)
-  #+cmu (ext:process-input process)
-  #+sbcl (sb-ext:process-input process)
-  #-(or sbcl cmu) (error "Don't know how to get the process input"))
-
-(defun sysproc-output (process)
-  #+cmu (ext:process-output process)
-  #+sbcl (sb-ext:process-output process)
-  #-(or sbcl cmu) (error "Don't know how to get the process output"))
-
-(defun sysproc-alive-p (process)
-  #+cmu (ext:process-alive-p process)
-  #+sbcl (sb-ext:process-alive-p process)
-  #-(or sbcl cmu) (error "Don't know how to test wether a process might be running"))
-
-(defun sysproc-pid (process)
-  #+cmu (ext:process-pid process)
-  #+sbcl (sb-ext:process-pid process)
-  #-(or sbcl cmu) (error "Don't know how to get the id of a process"))
-
-(defun sysproc-p (thing)
-  #+sbcl (sb-ext:process-p thing)
-  #+cmu (ext:process-p thing)
-  #-(or sbcl cmu) (error "Don't know how to figure out whether something is a system process"))
-
-(defun run-program (program arguments &key (wait t) pty input output error)
-  "Run PROGRAM with ARGUMENTS (a list) and return a process object."
-  ;; convert arguments to strings
-  (setf arguments
-        (mapcar #'(lambda (item)
-                    (typecase item
-                      (string item)
-                      (pathname (native-namestring item))
-                      (t (format nil "~A" item))))
-                arguments))
-  (when *run-verbose*
-    (unless error
-      (setf error t))
-    (format t "~&; run-pipe ~A~{ ~S~}~%" program arguments))
-  #+cmu (ext:run-program program arguments
-                         :wait wait
-                         :pty pty
-                         :input input
-                         :output output
-                         :error (or error *run-verbose*))
-  #+sbcl (sb-ext:run-program program arguments
-                             :search t
-                             :wait wait
-                             :pty pty
-                             :input input
-                             :output output
-                             :error (or error *run-verbose*))
-  #-(or sbcl cmu)
-  (error "Unsupported Lisp system."))
-
-(defun run-pipe (direction program arguments &key error)
-  "Run PROGRAM with a list of ARGUMENTS and according to DIRECTION
-return the input and output streams and process object of that
-process."
-  (be process (run-program program arguments
-                           :wait nil
-                           :pty nil
-                           :input (when (member direction '(:output :input-output :io))
-                                    :stream)
-                           :output (when (member direction '(:input :input-output :io))
-                                     :stream)
-                           :error error)
-    (values (sysproc-output process)
-            (sysproc-input process)
-            process))
-  #-(or sbcl cmu)
-  (error "Unsupported Lisp system."))
-
-(defun exit-code (process)
-  (sysproc-wait process)
-  (sysproc-exit-code process))
-
-(defun run-shell-command (fmt &rest args)
-  "Run a Bourne Shell command.  Return the exit status of the command."
-  (run-program *bourne-shell* (list "-c" (apply #'format nil fmt args))))
-
-(defun run-async-shell-command (fmt &rest args)
-  "Run a Bourne Shell command asynchronously. Return a process
-object if provided by your Lisp implementation."
-  (run-program *bourne-shell* (list "-c" (apply #'format nil fmt args))
-               :wait nil))
-
-(defmacro with-open-pipe ((in out program arguments &key (process (gensym)) error pty) &body forms)
-  "Run BODY with IN and OUT bound respectively to an input and an
-output stream connected to a system process created by running PROGRAM
-with ARGUMENTS.  If IN or OUT are NIL, then don't create that stream."
-  (with-gensyms (prg args)
-    `(be* ,prg ,program
-          ,args ,arguments
-          ,process (run-program ,prg ,args
-                                :output ,(case in
-                                               ((t nil) in)
-                                               (t :stream))
-                                :input ,(case out
-                                              ((t nil) out)
-                                              (t :stream))
-                                :wait nil
-                                :pty ,pty
-                                ,@(when error `(:error ,error)))
-       (if ,process
-           (let (,@(case in
-                         ((t nil))
-                         (t `((,in (sysproc-output ,process)))))
-                 ,@(case out
-                         ((t nil))
-                         (t `((,out (sysproc-input ,process))))))
-             (unwind-protect
-                  (progn
-                    ,@forms)
-               ,@(case in
-                       ((t nil))
-                       (t `((close ,in))))
-               ,@(case out
-                       ((t nil))
-                       (t `((close ,out))))
-               (when (sysproc-alive-p ,process)
-                 (sysproc-kill ,process :term))))
-           (error "unable to run ~A~{ ~A~}." ,prg ,args)))))
-
-
-(defun sysproc-set-signal-callback (signal handler)
-  "Arrange HANDLER function to be called when receiving the system
-signal SIGNAL."
-  (when (keywordp signal)
-    (setf signal (signal-number signal)))
-  #+cmu (system:enable-interrupt signal handler)
-  #+sbcl (sb-sys:enable-interrupt signal handler)
-  #-(or cmu sbcl) (error "Don't know how to set a system signal callback."))
diff --git a/third_party/lisp/sclf/time.lisp b/third_party/lisp/sclf/time.lisp
deleted file mode 100644
index 71b943aa43..0000000000
--- a/third_party/lisp/sclf/time.lisp
+++ /dev/null
@@ -1,311 +0,0 @@
-;;;  time.lisp --- time primitives
-
-;;;  Copyright (C) 2006, 2007, 2009 by Walter C. Pelissero
-
-;;;  Author: Walter C. Pelissero <walter@pelissero.de>
-;;;  Project: sclf
-
-#+cmu (ext:file-comment "$Module: time.lisp $")
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 2.1
-;;; of the License, or (at your option) any later version.
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;; Lesser General Public License for more details.
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free
-;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-;;; 02111-1307 USA
-
-(in-package :sclf)
-
-(defun year (epoch &optional time-zone)
-  "Return the year of EPOCH."
-  (sixth (multiple-value-list (decode-universal-time epoch time-zone))))
-
-(defun month (epoch &optional time-zone)
-  "Return the month of EPOCH."
-  (fifth (multiple-value-list (decode-universal-time epoch time-zone))))
-
-(defun day (epoch &optional time-zone)
-  "Return the day of EPOCH."
-  (fourth (multiple-value-list (decode-universal-time epoch time-zone))))
-
-(defun week-day (epoch &optional time-zone)
-  "Return the day of the week of EPOCH."
-  (seventh (multiple-value-list (decode-universal-time epoch time-zone))))
-
-(defun hour (epoch &optional time-zone)
-  "Return the hour of EPOCH."
-  (third (multiple-value-list (decode-universal-time epoch time-zone))))
-
-(defun minute (epoch &optional time-zone)
-  "Return the minute of EPOCH."
-  (second (multiple-value-list (decode-universal-time epoch time-zone))))
-
-(defun leap-year-p (year)
-  "Return true if YEAR is a leap year."
-  (and (zerop (mod year 4))
-       (or (not (zerop (mod year 100)))
-           (zerop (mod year 400)))))
-
-(defun last-day-of-month (month year)
-  "Return the last day of the month as integer."
-  (be last (elt #(31 28 31 30 31 30 31 31 30 31 30 31) (1- month))
-    (if (and (= last 28)
-             (leap-year-p year))
-        (1+ last)
-        last)))
-
-(defun add-months (months epoch &optional time-zone)
-  "Add MONTHS to EPOCH, which is a universal time.  MONTHS can be
-negative."
-  (multiple-value-bind (ss mm hh day month year) (decode-universal-time epoch time-zone)
-    (multiple-value-bind (y m) (floor (+ month months -1) 12)
-      (let ((new-month (1+ m))
-            (new-year (+ year y)))
-        (encode-universal-time ss mm hh
-                               (min day (last-day-of-month new-month (year epoch)))
-                               new-month
-                               new-year
-                               time-zone)))))
-
-(defun add-days (days epoch)
-  "Add DAYS to EPOCH, which is an universal time.  DAYS can be
-negative."
-  (+ (* 60 60 24 days) epoch))
-
-;; The following two functions are based on Thomas Russ <tar@isi.edu>
-;; code which didn't carry any copyright notice, so I assume it was in
-;; the public domain.
-
-(defun iso-time-string (time &key time-zone with-timezone-p basic)
-  "Return an ISO 8601 string representing TIME.  The time zone is
-included if WITH-TIMEZONE-P is true."
-  (flet ((format-timezone (zone)
-           (if (zerop zone)
-               "Z"
-               (multiple-value-bind (h m) (truncate (abs zone) 1.0)
-                 ;; Sign of time zone is reversed in ISO 8601 relative
-                 ;; to Common Lisp convention!
-                 (format nil "~:[+~;-~]~2,'0D:~2,'0D"
-                         (> zone 0) h (round m))))))
-    (multiple-value-bind (second minute hour day month year dow dst zone)
-        (decode-universal-time time time-zone)
-      (declare (ignore dow dst))
-      (if basic
-          (format nil "~4,'0D~2,'0D~2,'0DT~2,'0D~2,'0D~2,'0D~[~*~;~A~]"
-                  year month day hour minute second
-                  with-timezone-p (format-timezone zone))
-          (format nil "~4,'0D-~2,'0D-~2,'0DT~2,'0D:~2,'0D:~2,'0D~:[~*~;~A~]"
-                  year month day hour minute second
-                  with-timezone-p (format-timezone zone))))))
-
-(defun parse-iso-time-string (time-string)
-  "Parse an ISO 8601 formated string and return the universal time.
-It can parse the basic and the extended format, but may not be able to
-cover all the cases."
-  (labels ((parse-delimited-string (string delimiter n)
-             ;; Parses a delimited string and returns a list of
-             ;; n integers found in that string.
-             (let ((answer (make-list n :initial-element 0)))
-               (loop
-                  for i upfrom 0
-                  for start = 0 then (1+ end)
-                  for end = (position delimiter string :start (1+ start))
-                  do (setf (nth i answer)
-                           (parse-integer (subseq string start end)))
-                  when (null end) return t)
-               (values-list answer)))
-           (parse-fixed-field-string (string field-sizes)
-             ;; Parses a string with fixed length fields and returns
-             ;; a list of integers found in that string.
-             (let ((answer (make-list (length field-sizes) :initial-element 0)))
-               (loop
-                  with len = (length string)
-                  for start = 0 then (+ start field-size)
-                  for field-size in field-sizes
-                  for i upfrom 0
-                  while (< start len)
-                  do (setf (nth i answer)
-                           (parse-integer (subseq string start (+ start field-size)))))
-               (values-list answer)))
-           (parse-iso8601-date (date-string)
-             (let ((hyphen-pos (position #\- date-string)))
-               (if hyphen-pos
-                   (parse-delimited-string date-string #\- 3)
-                   (parse-fixed-field-string date-string '(4 2 2)))))
-           (parse-iso8601-timeonly (time-string)
-             (let* ((colon-pos (position #\: time-string))
-                    (zone-pos (or (position #\- time-string)
-                                  (position #\+ time-string)))
-                    (timeonly-string (subseq time-string 0 zone-pos))
-                    (zone-string (when zone-pos (subseq time-string (1+ zone-pos))))
-                    (time-zone nil))
-               (when zone-pos
-                 (multiple-value-bind (zone-h zone-m)
-                     (parse-delimited-string zone-string #\: 2)
-                   (setq time-zone (+ zone-h (/ zone-m 60)))
-                   (when (char= (char time-string zone-pos) #\-)
-                     (setq time-zone (- time-zone)))))
-               (multiple-value-bind (hh mm ss)
-                   (if colon-pos
-                       (parse-delimited-string timeonly-string #\: 3)
-                       (parse-fixed-field-string timeonly-string '(2 2 2)))
-                 (values hh mm ss time-zone)))))
-    (let ((time-separator (position #\T time-string)))
-      (multiple-value-bind (year month date)
-          (parse-iso8601-date
-           (subseq time-string 0 time-separator))
-        (if time-separator
-            (multiple-value-bind (hh mm ss zone)
-                (parse-iso8601-timeonly
-                 (subseq time-string (1+ time-separator)))
-              (if zone
-                  ;; Sign of time zone is reversed in ISO 8601
-                  ;; relative to Common Lisp convention!
-                  (encode-universal-time ss mm hh date month year (- zone))
-                  (encode-universal-time ss mm hh date month year)))
-            (encode-universal-time 0 0 0 date month year))))))
-
-(defun time-string (time &optional time-zone)
-  "Return a string representing TIME in the form:
-  Tue Jan 25 12:55:40 2005"
-  (multiple-value-bind (ss mm hh day month year week-day)
-      (decode-universal-time time time-zone)
-    (format nil "~A ~A ~A ~D:~2,'0D:~2,'0D ~A"
-            (subseq (week-day->string week-day) 0 3)
-            (subseq (month->string month) 0 3)
-            day
-            hh mm ss
-            year)))
-
-(defun beginning-of-month (month year &optional time-zone)
-  (encode-universal-time 0 0 0 1 month year time-zone))
-
-(defun end-of-month (month year &optional time-zone)
-  (1- (add-months 1 (encode-universal-time 0 0 0 1 month year time-zone))))
-
-(defun beginning-of-first-week (year &optional time-zone)
-  "Return the epoch of the first week of YEAR.  As the first week
-of the year needs to have Thursday in this YEAR, the returned
-time can actually fall in the previous year."
-  (let* ((Jan-1st (encode-universal-time 0 0 0 1 1 year time-zone))
-         (start (- 4 (week-day (add-days 4 Jan-1st)))))
-    (add-days start Jan-1st)))
-
-(defun beginning-of-week (week year &optional time-zone)
-  "Return the epoch of the beginning of WEEK of YEAR."
-  (add-days (* (1- week) 7) (beginning-of-first-week year time-zone)))
-
-(defun end-of-week (week year &optional time-zone)
-  "Return the epoch of the beginning of WEEK of YEAR."
-  (1- (beginning-of-week (1+ week) year time-zone)))
-
-(defun end-of-last-week (year &optional time-zone)
-  "Return the epoch of the last week of YEAR.  As the last week
-of the year needs to have Thursday in this YEAR, the returned
-time can fall in the next year."
-  (1- (beginning-of-first-week (1+ year) time-zone)))
-
-(defun seconds-from-beginning-of-the-year (time &optional time-zone)
-  (- time (encode-universal-time 0 0 0 1 1 (year time) time-zone)))
-
-(defun day-of-the-year (time &optional time-zone)
-  "Return the day within the year of TIME starting from 1 up to
-365 (or 366)."
-  (1+ (truncate (seconds-from-beginning-of-the-year time time-zone)
-                (* 60 60 24))))
-
-(defun week (time &optional time-zone)
-  "Return the number of the week and the year TIME referes to.
-Week is an integer from 1 to 52.  Due to the way the first week
-of the year is calculated a day in one year could actually be in
-the last week of the previous or next year."
-  (let* ((year (year time))
-         (start (beginning-of-first-week year time-zone))
-         (days-from-start (truncate (- time start) (* 60 60 24)))
-         (weeks (truncate days-from-start 7))
-         (week-number (mod weeks 52)))
-    (values (1+ week-number)
-            (cond ((< weeks 0)
-                   (1- year))
-                  ((> weeks 51)
-                   (1+ year))
-                  (t year)))))
-
-(defun week-day->string (day &optional sunday-first)
-  "Return the weekday string corresponding to DAY number."
-  (elt (if sunday-first
-           #("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday")
-           #("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"))
-       day))
-
-(defconst +month-names+  #("January" "February" "March" "April" "May" "June" "July"
-                           "August" "September" "October" "November" "December"))
-
-(defun month->string (month)
-  "Return the month string corresponding to MONTH number."
-  (elt +month-names+ (1- month)))
-
-(defun month-string->number (month)
-  (1+ (position month +month-names+ :test #'string-equal)))
-
-(defun print-time-span (span &optional stream)
-  "Print in English the time SPAN expressed in seconds."
-  (let* ((minute 60)
-         (hour (* minute 60))
-         (day (* hour 24))
-         (seconds span))
-    (macrolet ((split (divisor)
-                 `(when (>= seconds ,divisor)
-                    (prog1 (truncate seconds ,divisor)
-                      (setf seconds (mod seconds ,divisor))))))
-      (let* ((days (split day))
-             (hours (split hour))
-             (minutes (split minute)))
-        (format stream "~{~A~^ ~}" (remove nil
-                                           (list
-                                            (when days
-                                              (format nil "~D day~:P" days))
-                                            (when hours
-                                              (format nil "~D hour~:P" hours))
-                                            (when minutes
-                                              (format nil "~D minute~:P" minutes))
-                                            (when (or (> seconds 0)
-                                                      (= span 0))
-                                              (format nil "~D second~:P" seconds)))))))))
-
-(defun next-week-day (epoch week-day &optional time-zone)
-  "Return the universal time of the next WEEK-DAY starting from epoch."
-  (add-days (mod (- week-day (week-day epoch time-zone)) 7)
-            epoch))
-
-(defun next-monday (epoch &optional time-zone)
-  "Return the universal time of the next Monday starting from
-EPOCH."
-  (next-week-day epoch 0 time-zone))
-
-(defun full-weeks-in-span (start end &optional time-zone)
-  "Return the number of full weeks in time span START to END.  A
-full week starts on Monday and ends on Sunday."
-  (be first-monday (next-monday start time-zone)
-    (truncate (- end first-monday) (* 7 24 60 60))))
-
-(defconst +unix-lisp-time-difference+
-  (encode-universal-time 0 0 0 1 1 1970 0)
-  "Time difference between Unix epoch and Common Lisp epoch.  The
-former is 1st January 1970, while the latter is the beginning of the
-XX century.")
-
-(defun universal->unix-time (time)
-  (- time +unix-lisp-time-difference+))
-
-(defun unix->universal-time (time)
-  (+ time +unix-lisp-time-difference+))
-
-(defun get-unix-time ()
-  (universal->unix-time (get-universal-time)))
diff --git a/third_party/lisp/str.nix b/third_party/lisp/str.nix
new file mode 100644
index 0000000000..556f9cc307
--- /dev/null
+++ b/third_party/lisp/str.nix
@@ -0,0 +1,49 @@
+{ depot, pkgs, ... }:
+
+let
+  inherit (depot.nix) buildLisp;
+  src = with pkgs; srcOnly lispPackages.str;
+in
+buildLisp.library {
+  name = "str";
+
+  deps = with depot.third_party.lisp; [
+    {
+      sbcl = buildLisp.bundled "uiop";
+      default = buildLisp.bundled "asdf";
+    }
+    cl-ppcre
+    cl-ppcre.unicode
+    cl-change-case
+  ];
+
+  srcs = [
+    (pkgs.runCommand "str.lisp" { } ''
+      substitute ${src}/str.lisp $out \
+        --replace-fail \
+          '(asdf:component-version (asdf:find-system "str"))' \
+          '"${pkgs.lispPackages.str.meta.version}"'
+    '')
+  ];
+
+  brokenOn = [
+    "ccl" # In REPLACE-USING: Shouldn't assign to variable I
+  ];
+
+  tests = {
+    name = "str-test";
+    srcs = [ (src + "/test/test-str.lisp") ];
+    deps = [
+      {
+        sbcl = depot.nix.buildLisp.bundled "uiop";
+        default = depot.nix.buildLisp.bundled "asdf";
+      }
+      depot.third_party.lisp.prove
+      depot.third_party.lisp.fiveam
+    ];
+
+    expression = ''
+      (fiveam:run! 'str::test-str)
+    '';
+  };
+}
diff --git a/third_party/lisp/trivial-ldap.nix b/third_party/lisp/trivial-ldap.nix
index c8a27431c6..c85fe2accb 100644
--- a/third_party/lisp/trivial-ldap.nix
+++ b/third_party/lisp/trivial-ldap.nix
@@ -1,12 +1,14 @@
 { depot, pkgs, ... }:
 
-let src = pkgs.fetchFromGitHub {
+let
+  src = pkgs.fetchFromGitHub {
     owner = "rwiker";
     repo = "trivial-ldap";
     rev = "3b8f1ff85f29ea63e6ab2d0d27029d68b046faf8";
     sha256 = "1zaa4wnk5y5ff211pkg6dl27j4pjwh56hq0246slxsdxv6kvp1z9";
   };
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "trivial-ldap";
 
   deps = with depot.third_party.lisp; [
diff --git a/third_party/lisp/trivial-mimes.nix b/third_party/lisp/trivial-mimes.nix
index 04e8b5ef5d..b097a3d0ee 100644
--- a/third_party/lisp/trivial-mimes.nix
+++ b/third_party/lisp/trivial-mimes.nix
@@ -3,7 +3,7 @@
 let
   src = with pkgs; srcOnly lispPackages.trivial-mimes;
 
-  mime-types = pkgs.runCommand "mime-types.lisp" {} ''
+  mime-types = pkgs.runCommand "mime-types.lisp" { } ''
     substitute ${src}/mime-types.lisp $out \
       --replace /etc/mime.types ${src}/mime.types \
       --replace "(asdf:system-source-directory :trivial-mimes)" '"/bogus-dir"'
@@ -11,7 +11,8 @@ let
       # generally fail β€” we are not using ASDF after all.
   '';
 
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "trivial-mimes";
 
   deps = [
diff --git a/third_party/lisp/uax-15.nix b/third_party/lisp/uax-15.nix
index 1e44f88d5c..f98c029d36 100644
--- a/third_party/lisp/uax-15.nix
+++ b/third_party/lisp/uax-15.nix
@@ -4,7 +4,8 @@ let
   inherit (pkgs) runCommand;
   inherit (depot.nix.buildLisp) bundled;
   src = with pkgs; srcOnly lispPackages.uax-15;
-in depot.nix.buildLisp.library {
+in
+depot.nix.buildLisp.library {
   name = "uax-15";
 
   deps = with depot.third_party.lisp; [
@@ -23,7 +24,7 @@ in depot.nix.buildLisp.library {
     #
     # additionally there are some wonky variable usages of variables
     # that are never defined, for which we patch in defvar statements.
-    (runCommand "precomputed-tables.lisp" {} ''
+    (runCommand "precomputed-tables.lisp" { } ''
       substitute ${src}/src/precomputed-tables.lisp precomputed-tables.lisp \
         --replace "(asdf:system-source-directory (asdf:find-system 'uax-15 nil))" \
                   '"${src}/"'
diff --git a/third_party/lisp/unix-opts.nix b/third_party/lisp/unix-opts.nix
index e52eab959d..2482961132 100644
--- a/third_party/lisp/unix-opts.nix
+++ b/third_party/lisp/unix-opts.nix
@@ -1,5 +1,5 @@
 # unix-opts is a portable command line argument parser
-{ depot, pkgs, ...}:
+{ depot, pkgs, ... }:
 
 
 let src = with pkgs; srcOnly lispPackages.unix-opts;
diff --git a/third_party/lisp/usocket-server.nix b/third_party/lisp/usocket-server.nix
index f2f11d7a17..5d6d04535f 100644
--- a/third_party/lisp/usocket-server.nix
+++ b/third_party/lisp/usocket-server.nix
@@ -4,7 +4,8 @@
 let
   inherit (depot.nix) buildLisp;
   src = with pkgs; srcOnly lispPackages.usocket-server;
-in buildLisp.library {
+in
+buildLisp.library {
   name = "usocket-server";
 
   deps = with depot.third_party.lisp; [
diff --git a/third_party/lisp/usocket.nix b/third_party/lisp/usocket.nix
index 3359549784..589a3a0cfc 100644
--- a/third_party/lisp/usocket.nix
+++ b/third_party/lisp/usocket.nix
@@ -4,7 +4,8 @@
 let
   inherit (depot.nix) buildLisp;
   src = with pkgs; srcOnly lispPackages.usocket;
-in buildLisp.library {
+in
+buildLisp.library {
   name = "usocket";
   deps = with depot.third_party.lisp; [
     (buildLisp.bundled "asdf")
diff --git a/third_party/naersk/default.nix b/third_party/naersk/default.nix
index 865fcf04d2..bf4c55fe55 100644
--- a/third_party/naersk/default.nix
+++ b/third_party/naersk/default.nix
@@ -1,8 +1,3 @@
-{ pkgs, ... }:
+{ depot, pkgs, ... }:
 
-pkgs.callPackage (pkgs.fetchFromGitHub {
-  owner = "nmattia";
-  repo = "naersk";
-  rev = "a3f40fe42cc6d267ff7518fa3199e99ff1444ac4";
-  sha256 = "1nf7fn8anghwf6p5p58ywbcwdkjxq112qv663rn52jq9k95iakdi";
-}) {}
+pkgs.callPackage depot.third_party.sources.naersk { }
diff --git a/third_party/napalm/default.nix b/third_party/napalm/default.nix
new file mode 100644
index 0000000000..e85c360ba9
--- /dev/null
+++ b/third_party/napalm/default.nix
@@ -0,0 +1,7 @@
+{ depot, pkgs, ... }:
+
+pkgs.callPackage depot.third_party.sources.napalm { } // {
+  meta.ci.targets = [
+    "napalm-registry"
+  ];
+}
diff --git a/third_party/nix/.clang-format b/third_party/nix/.clang-format
deleted file mode 100644
index b8c36e122b..0000000000
--- a/third_party/nix/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-# Use the Google style in this project.
-BasedOnStyle: Google
-DerivePointerAlignment: false
-PointerAlignment: Left
-IncludeCategories:
-  - Regex: '^<.*\.h>'
-    Priority: 2
-  - Regex: '^<.*'
-    Priority: 1
-  - Regex: '.*'
-    Priority: 3
diff --git a/third_party/nix/.clang-tidy b/third_party/nix/.clang-tidy
deleted file mode 100644
index 5b22be767f..0000000000
--- a/third_party/nix/.clang-tidy
+++ /dev/null
@@ -1,4 +0,0 @@
----
-Checks: 'abseil-c*,clang-analyzer-security-*,bugprone-*,google-*,modernize-*,cppcoreguidelines-*,misc-*,-modernize-use-trailing-return-type'
-WarningsAsErrors: 'abseil-*,clang-analyzer-security*'
-...
diff --git a/third_party/nix/.dir-locals.el b/third_party/nix/.dir-locals.el
deleted file mode 100644
index 92aa816f10..0000000000
--- a/third_party/nix/.dir-locals.el
+++ /dev/null
@@ -1 +0,0 @@
-((c++-mode . ((c-file-style . "google"))))
diff --git a/third_party/nix/.github/ISSUE_TEMPLATE.md b/third_party/nix/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 3372b1f03f..0000000000
--- a/third_party/nix/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-
-# Filing a Nix issue
-
-*WAIT* Are you sure you're filing your issue in the right repository?
-
-We appreciate you taking the time to tell us about issues you encounter, but routing the issue to the right place will get you help sooner and save everyone time.
-
-This is the Nix repository, and issues here should be about Nix the build and package management *_tool_*.
-
-If you have a problem with a specific package on NixOS or when using Nix, you probably want to file an issue with _nixpkgs_, whose issue tracker is over at https://github.com/NixOS/nixpkgs/issues.
-
-Examples of _Nix_ issues:
-
-- Nix segfaults when I run `nix-build -A blahblah`
-- The Nix language needs a new builtin: `builtins.foobar`
-- Regression in the behavior of `nix-env` in Nix 2.0
-
-Examples of _nixpkgs_ issues:
-
-- glibc is b0rked on aarch64
-- chromium in NixOS doesn't support U2F but google-chrome does!
-- The OpenJDK package on macOS is missing a key component
-
-Chances are if you're a newcomer to the Nix world, you'll probably want the [nixpkgs tracker](https://github.com/NixOS/nixpkgs/issues). It also gets a lot more eyeball traffic so you'll probably get a response a lot more quickly.
-
--->
diff --git a/third_party/nix/.gitignore b/third_party/nix/.gitignore
deleted file mode 100644
index 7f2d477131..0000000000
--- a/third_party/nix/.gitignore
+++ /dev/null
@@ -1,119 +0,0 @@
-Makefile.config
-perl/Makefile.config
-
-# /
-/aclocal.m4
-/autom4te.cache
-/configure
-/nix.spec
-/stamp-h1
-/svn-revision
-/build-gcc
-/libtool
-
-/corepkgs/config.nix
-
-# /corepkgs/channels/
-/corepkgs/channels/unpack.sh
-
-# /corepkgs/nar/
-/corepkgs/nar/nar.sh
-/corepkgs/nar/unnar.sh
-
-# /doc/manual/
-/doc/manual/manual.html
-/doc/manual/manual.xmli
-/doc/manual/manual.pdf
-/doc/manual/manual.is-valid
-/doc/manual/*.1
-/doc/manual/*.5
-/doc/manual/*.8
-/doc/manual/version.txt
-
-# /scripts/
-/scripts/nix-profile.sh
-/scripts/nix-copy-closure
-/scripts/nix-reduce-build
-/scripts/nix-http-export.cgi
-/scripts/nix-profile-daemon.sh
-
-# /src/libexpr/
-/src/libexpr/lexer-tab.cc
-/src/libexpr/lexer-tab.hh
-/src/libexpr/parser-tab.cc
-/src/libexpr/parser-tab.hh
-/src/libexpr/parser-tab.output
-/src/libexpr/nix.tbl
-
-# /src/libstore/
-/src/libstore/*.gen.hh
-
-/src/nix/nix
-
-# /src/nix-env/
-/src/nix-env/nix-env
-
-# /src/nix-instantiate/
-/src/nix-instantiate/nix-instantiate
-
-# /src/nix-store/
-/src/nix-store/nix-store
-
-/src/nix-prefetch-url/nix-prefetch-url
-
-# /src/nix-daemon/
-/src/nix-daemon/nix-daemon
-
-/src/nix-collect-garbage/nix-collect-garbage
-
-# /src/nix-channel/
-/src/nix-channel/nix-channel
-
-# /src/nix-build/
-/src/nix-build/nix-build
-
-/src/nix-copy-closure/nix-copy-closure
-
-/src/build-remote/build-remote
-
-# /tests/
-/tests/test-tmp
-/tests/common.sh
-/tests/dummy
-/tests/result*
-/tests/restricted-innocent
-/tests/shell
-/tests/shell.drv
-
-# /tests/lang/
-/tests/lang/*.out
-/tests/lang/*.out.xml
-/tests/lang/*.ast
-
-/perl/lib/Nix/Config.pm
-/perl/lib/Nix/Store.cc
-
-/misc/systemd/nix-daemon.service
-/misc/systemd/nix-daemon.socket
-/misc/upstart/nix-daemon.conf
-
-/src/resolve-system-dependencies/resolve-system-dependencies
-
-inst/
-
-*.a
-*.o
-*.so
-*.dylib
-*.dll
-*.exe
-*.dep
-*~
-*.pc
-*.plist
-
-# GNU Global
-GPATH
-GRTAGS
-GSYMS
-GTAGS
diff --git a/third_party/nix/.skip-subtree b/third_party/nix/.skip-subtree
deleted file mode 100644
index d49b47f75a..0000000000
--- a/third_party/nix/.skip-subtree
+++ /dev/null
@@ -1 +0,0 @@
-Third-party code with non-depot layout.
diff --git a/third_party/nix/.travis.yml b/third_party/nix/.travis.yml
deleted file mode 100644
index 99218a963c..0000000000
--- a/third_party/nix/.travis.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-os: osx
-script: ./tests/install-darwin.sh
diff --git a/third_party/nix/.version b/third_party/nix/.version
deleted file mode 100644
index fd06a9268d..0000000000
--- a/third_party/nix/.version
+++ /dev/null
@@ -1 +0,0 @@
-2.3.4
\ No newline at end of file
diff --git a/third_party/nix/CMakeLists.txt b/third_party/nix/CMakeLists.txt
deleted file mode 100644
index 5d89572f16..0000000000
--- a/third_party/nix/CMakeLists.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-# -*- mode: cmake; -*-
-cmake_minimum_required(VERSION 3.16)
-project(nix CXX)
-set(CMAKE_CXX_STANDARD 17)
-
-# Export compile_commands.json which can be used by tools such as
-# clangd and clang-tidy.
-set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-
-# Enable warnings
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
-
-# Provide an output path for pkgconfig.
-include(GNUInstallDirs)
-set(PKGCONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
-
-# The following lines import CMake-native dependencies which may
-# contain useful definitions. Other dependencies are not treated
-# specially by CMake and are only linked into the resulting binary.
-find_package(BZip2)
-find_package(Boost COMPONENTS context)
-find_package(CURL)
-find_package(LibLZMA)
-find_package(Protobuf REQUIRED)
-find_package(SQLite3)
-find_package(Threads)
-find_package(absl REQUIRED)
-find_package(gRPC REQUIRED)
-find_package(glog REQUIRED)
-
-find_program(CLANG_TIDY_PATH clang-tidy)
-if (CLANG_TIDY_PATH)
-  # TODO(kanepyork): figure out how to reenable
-  #message("Found clang-tidy: ${CLANG_TIDY_PATH}")
-  #set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_PATH};--line-filter=[{\"name\":\"src/cpptoml/cpptoml.h\"},{\"name\":\"google/protobuf/metadata_lite.h\"}]")
-
-  # nix's toolchain has a problem with system header includes, so clang-tidy requires a manual -isystem
-  if (DEFINED ENV{LIBCXX_INCLUDE})
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem $ENV{LIBCXX_INCLUDE}")
-  endif()
-endif()
-
-if (DEFINED ENV{SANDBOX_SHELL})
-  message("Using SANDBOX_SHELL = $ENV{SANDBOX_SHELL}")
-  set(SANDBOX_SHELL "$ENV{SANDBOX_SHELL}")
-else()
-  find_program(BUSYBOX busybox)
-  if (BUSYBOX)
-    set(SANDBOX_SHELL "${BUSYBOX}")
-  else()
-    message(FATAL_ERROR "Could not find busybox and SANDBOX_SHELL is not set")
-  endif()
-endif()
-
-# generate a configuration file (autoheader-style) to configure
-# certain symbols that Nix depends on.
-configure_file(config.h.in nix_config.h @ONLY)
-INSTALL(FILES "${PROJECT_BINARY_DIR}/nix_config.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/nix")
-
-# install corepkgs
-configure_file(corepkgs/config.nix.in config.nix @ONLY)
-INSTALL(DIRECTORY corepkgs
-  DESTINATION ${CMAKE_INSTALL_DATADIR}/nix
-  FILES_MATCHING
-    PATTERN "*.nix")
-INSTALL(FILES "${PROJECT_BINARY_DIR}/config.nix" DESTINATION "${CMAKE_INSTALL_DATADIR}/nix/corepkgs")
-
-# Conditionally run tests
-option(PACKAGE_TESTS "Build the tests" ON)
-if (PACKAGE_TESTS)
-  enable_testing()
-  find_package(GTest)
-  find_package(rapidcheck)
-  include(GoogleTest)
-endif()
-
-add_subdirectory(src)
diff --git a/third_party/nix/COPYING b/third_party/nix/COPYING
deleted file mode 100644
index 5ab7695ab8..0000000000
--- a/third_party/nix/COPYING
+++ /dev/null
@@ -1,504 +0,0 @@
-		  GNU LESSER GENERAL PUBLIC LICENSE
-		       Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard.  To achieve this, non-free programs must be
-allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-
-		  GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-  
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded.  In such case, this License incorporates the limitation as if
-written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-			    NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-           How to Apply These Terms to Your New Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.  It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
-
-
diff --git a/third_party/nix/OWNERS b/third_party/nix/OWNERS
deleted file mode 100644
index 886f766d0c..0000000000
--- a/third_party/nix/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-inherited: true
-owners:
-  - grfn
-  - tazjin
-  - kanepyork
diff --git a/third_party/nix/README.md b/third_party/nix/README.md
deleted file mode 100644
index 4bb5896831..0000000000
--- a/third_party/nix/README.md
+++ /dev/null
@@ -1,179 +0,0 @@
-Tvix, also known as TVL's fork of Nix
--------------------------------------
-
-Nix is a new take on package management that is fairly unique. Because
-of its purity aspects, a lot of issues found in traditional package
-managers don't appear with Nix.
-
-To find out more about the tool, usage and installation instructions,
-please read the manual, which is available on the Nix website at
-<http://nixos.org/nix/manual>.
-
-This repository is [TVL's](https://tvl.fyi)'s fork of Nix, which we lovingly
-refer to as Tvix.
-
-## Fork background
-
-Nix is a fantastic project with over a decade of demonstrated
-real-world usage, but also with quite a few problems.
-
-First of all, the project consists of two main components: The Nix
-package collection ("[nixpkgs][]") and the package manager itself.
-
-The package collection is an enormous effort with hundreds of
-thousands of commits, encoding expert knowledge about lots of
-different software and ways of building and managing it. It is a very
-valuable piece of software.
-
-The package manager however is an old C++ project with severe code
-quality issues, little to no documentation, no consistent style and no
-unit test coverage.
-
-Its codebase is larger than it needs to be (often due to custom
-reimplementations of basic functionality) and is mostly ad-hoc
-structured, making it difficult to correctly implement large-scale
-improvements.
-
-In addition, the upstream Nix project is diverging from the opinions
-of some community members via the introduction of concepts such as Nix
-flakes.
-
-To counteract these things we have decided to fork Nix.
-
-## Fork goals
-
-The things listed here are explicitly in-scope for work on the fork.
-This list is not exhaustive, and it is very likely that many other
-smaller things will be discovered along the way.
-
-### nixpkgs compatibility
-
-This fork will maintain compatibility with nixpkgs as much as
-possible. If at any point we do need to diverge, we will do it in a
-way that is backwards compatible.
-
-### Code quality improvements
-
-Code quality encompasses several different issues.
-
-One goal is to slowly bring the codebase in line with the [Google C++
-style guide][google-style]. Apart from the trivial reformatting (which
-is already done), this means slowly chipping away at incorrectly
-structured type hierarchies, usage of exceptions, usage of raw
-pointers, global mutability and so on.
-
-Another goal is to reduce the amount of code in Nix by removing custom
-reimplementations of basic functionality (such as string splitting or
-reading files).
-
-For functionality that is not part of the C++17 standard library,
-[Abseil][] will be the primary external library used.
-
-### Explicit RPC mechanisms
-
-Nix currently uses homegrown mechanisms of interacting with other Nix
-binaries, for example for remote builds or interaction between the CLI
-and the Nix daemon.
-
-This will be replaced with [gRPC][].
-
-### New sandboxing mechanism
-
-Nix implements its own sandboxing mechanism. This was probably the
-correct decision at the time, but is not necessary anymore because
-Linux containers have become massively popular and lots of new tooling
-is now available.
-
-The goal is to replace the custom sandboxing implementation with
-pluggable [OCI runtimes][oci], which will make it possible to use
-arbitrary container runtimes such as [gVisor][] or [systemd-nspawn][]
-
-### Pluggable Nix store backends
-
-The current Nix store implementation will be removed from Nix' core
-and instead be refactored into a gRPC API that can be implemented by
-different backends.
-
-### Builds as graph reductions
-
-A Nix derivation that should be instantiated describes a build graph.
-This graph will become a first-class citizen, making it possible to
-distribute different parts of the computation to different nodes.
-
-Implementing this properly will also allow us to improve the
-implementation of import-from-derivation by explicitly moving through
-different graph reduction stages.
-
-## Fork non-goals
-
-To set expectations, there are some explicit non-goals, too.
-
-* Merging these changes back into upstream is not a goal, and maybe
-  not even feasible. The core work has not even started yet and just
-  basic cleanup has already created a diff of over 40 000 lines.
-
-  This would likely also turn into a political effort, which we have
-  no interest in.
-
-* Improved performance is not an (initial) goal. Nix performance is
-  very unevenly distributed across the codebase (some things have seen
-  a lot of ad-hoc optimisation, others are written like inefficient
-  toy implementations) and we simply don't know what effect the
-  cleanup will have.
-
-  Once the codebase is in a better state we will be able to start
-  optimising it again while retaining readability, but this is not a
-  goal until a later point in time.
-
-* Compatibility with new upstream features is not a goal. Specifically
-  we do not want Nix flakes, but other changes upstream makes will be
-  considered for inclusion.
-
-* Support for non-Linux systems. Currently Nix support Mac OS and
-  potentially other systems, but this support will be dropped.
-
-  Once we have OCI-compatible sandboxes and a store protocol it will
-  be possible to reintroduce these with less friction.
-
-## Building
-
-To build the project, set up an out-of-tree cmake directory and run cmake in
-nix-shell.
-
-```
-mkdir ~/build/tvix
-cd ~/build/tvix
-
-nix-shell $DEPOT_PATH -A third_party.nix.build-shell
-
-# Disable clang-tidy for quicker builds
-cmake $DEPOT_PATH/third_party/nix/ -DCLANG_TIDY_PATH=""
-make -j16 -l12
-
-# Run tests
-make test
-```
-
-## Contributing to the fork
-
-The TVL depot's default [contribution guidelines][contributing] apply.
-
-In addition, please make sure that submitted code builds and is
-formatted with `clang-format`, using the configuration found in this
-folder.
-
-## License
-
-Nix is released under the LGPL v2.1
-
-This product includes software developed by the OpenSSL Project for
-use in the [OpenSSL Toolkit](http://www.OpenSSL.org/).
-
-[nixpkgs]: https://github.com/NixOS/nixpkgs
-[google-style]: https://google.github.io/styleguide/cppguide.html
-[Abseil]: https://abseil.io/
-[gRPC]: https://grpc.io/
-[oci]: https://www.opencontainers.org/
-[gVisor]: https://gvisor.dev/
-[systemd-nspawn]: https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html
-[contributing]: https://cs.tvl.fyi/depot/-/blob/docs/CONTRIBUTING.md
diff --git a/third_party/nix/clangd.nix b/third_party/nix/clangd.nix
deleted file mode 100644
index 7a29819b10..0000000000
--- a/third_party/nix/clangd.nix
+++ /dev/null
@@ -1,30 +0,0 @@
-# Create a clangd wrapper script that can be used with this project.
-# The default Nix wrapper only works with C projects, not C++
-# projects.
-#
-# The CPATH construction logic is lifted from the original wrapper
-# script.
-
-pkgs:
-
-pkgs.writeShellScriptBin "nix-clangd" ''
-  buildcpath() {
-    local path
-    while (( $# )); do
-      case $1 in
-          -isystem)
-              shift
-              path=$path''${path:+':'}$1
-      esac
-      shift
-    done
-    echo $path
-  }
-
-  export CPATH=''${CPATH}''${CPATH:+':'}:$(buildcpath ''${NIX_CFLAGS_COMPILE})
-  export CPATH=${pkgs.glibc.dev}/include''${CPATH:+':'}''${CPATH}
-  export CPLUS_INCLUDE_PATH=${pkgs.llvmPackages_11.libcxx}/include/c++/v1:''${CPATH}
-
-  # TODO(tazjin): Configurable commands directory?
-  exec -a clangd ${pkgs.llvmPackages_11.clang-unwrapped}/bin/clangd -cross-file-rename $@
-''
diff --git a/third_party/nix/config.h.in b/third_party/nix/config.h.in
deleted file mode 100644
index 986969705b..0000000000
--- a/third_party/nix/config.h.in
+++ /dev/null
@@ -1,130 +0,0 @@
-// This file configures various build-time settings in Nix. In
-// previous iterations it was mostly responsible for configuring
-// OS-dependent settings, which are still preserved below but should
-// be removed.
-
-#ifndef NIX_CONFIG_H
-#define NIX_CONFIG_H
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "2.3.4"
-
-/* Platform identifier (`cpu-os`) */
-// TODO(tazjin): generate
-#define SYSTEM "x86_64-linux"
-
-// TODO(tazjin): some of these values are nonsensical for Nix
-#define NIX_PREFIX "@CMAKE_INSTALL_PREFIX@"
-#define NIX_STORE_DIR "/nix/store"
-#define NIX_DATA_DIR "@CMAKE_INSTALL_FULL_DATADIR@"
-#define NIX_LOG_DIR "/nix/var/log/nix"
-#define NIX_STATE_DIR "/nix/var/nix"
-#define NIX_CONF_DIR "/etc/nix"
-#define NIX_LIBEXEC_DIR "@CMAKE_INSTALL_FULL_LIBEXECDIR@"
-#define NIX_BIN_DIR "@CMAKE_INSTALL_FULL_BINDIR@"
-#define NIX_MAN_DIR "@CMAKE_INSTALL_FULL_MANDIR@"
-#define SANDBOX_SHELL "@SANDBOX_SHELL@"
-
-// Defines used only in tests (e.g. to access data)
-#define NIX_SRC_DIR "@CMAKE_SOURCE_DIR@"
-
-// These are hardcoded either because support for non-Linux is being
-// dropped, or because a decision was made to make inclusion of these
-// libraries mandatory.
-
-#define HAVE_STRUCT_DIRENT_D_TYPE 1
-
-#define HAVE_LUTIMES 1
-
-// Whether link() works on symlinks
-#define CAN_LINK_SYMLINK 1
-
-/* Whether to use the Boehm garbage collector. */
-#define HAVE_BOEHMGC 1
-
-/* Define if the Boost library is available. */
-#define HAVE_BOOST 1
-
-/* Define to 1 if you have the <bzlib.h> header file. */
-#define HAVE_BZLIB_H 1
-
-/* Define if the compiler supports basic C++17 syntax */
-#define HAVE_CXX17 1
-
-/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR` */
-#define HAVE_DIRENT_H 1
-
-/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR` */
-#define HAVE_DIR_H 1
-
-/* Define to 1 if you have the <editline.h> header file. */
-#define HAVE_EDITLINE_H 1
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the `lchown` function. */
-#define HAVE_LCHOWN 1
-
-/* Define to 1 if you have the <locale.h> header file. */
-#define HAVE_LOCALE 1
-
-/* Define to 1 if you have the `lutimes` function. */
-#define HAVE_LUTIMES 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `pipe2` function. */
-#define HAVE_PIPE2 1
-
-/* Define to 1 if you have the `posix_fallocate` function. */
-#define HAVE_POSIX_FALLOCATE 1
-
-/* Define to 1 if you have the `pubsetbuf` function. */
-#define HAVE_PUBSETBUF 1
-
-/* Whether seccomp is available and should be used for sandboxing. */
-#define HAVE_SECCOMP 1
-
-/* Define to 1 if you have the `setresuid` function. */
-#define HAVE_SETRESUID 1
-
-/* Define to 1 if you have the `setreuid` function. */
-#define HAVE_SETREUID 1
-
-/* Whether to use libsodium for cryptography. */
-#define HAVE_SODIUM 1
-
-/* Define to 1 if you have the `statvfs` function. */
-#define HAVE_STATVFS 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the `strsignal` function. */
-#define HAVE_STRSIGNAL 1
-
-/* Define to 1 if you have the `sysconf` function. */
-#define HAVE_SYSCONF 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-
-#endif
diff --git a/third_party/nix/config/config.sub b/third_party/nix/config/config.sub
deleted file mode 100755
index c19e671805..0000000000
--- a/third_party/nix/config/config.sub
+++ /dev/null
@@ -1,1818 +0,0 @@
-#! /bin/sh
-# Configuration validation subroutine script.
-#   Copyright 1992-2018 Free Software Foundation, Inc.
-
-timestamp='2018-08-13'
-
-# This file is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, see <https://www.gnu.org/licenses/>.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that
-# program.  This Exception is an additional permission under section 7
-# of the GNU General Public License, version 3 ("GPLv3").
-
-
-# Please send patches to <config-patches@gnu.org>.
-#
-# Configuration subroutine to validate and canonicalize a configuration type.
-# Supply the specified configuration type as an argument.
-# If it is invalid, we print an error message on stderr and exit with code 1.
-# Otherwise, we print the canonical config type on stdout and succeed.
-
-# You can get the latest version of this script from:
-# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
-
-# This file is supposed to be the same for all GNU packages
-# and recognize all the CPU types, system types and aliases
-# that are meaningful with *any* GNU software.
-# Each package is responsible for reporting which valid configurations
-# it does not support.  The user should be able to distinguish
-# a failure to support a valid configuration from a meaningless
-# configuration.
-
-# The goal of this file is to map all the various variations of a given
-# machine specification into a single specification in the form:
-#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
-# or in some cases, the newer four-part form:
-#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
-# It is wrong to echo any other type of specification.
-
-me=`echo "$0" | sed -e 's,.*/,,'`
-
-usage="\
-Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
-
-Canonicalize a configuration name.
-
-Options:
-  -h, --help         print this help, then exit
-  -t, --time-stamp   print date of last modification, then exit
-  -v, --version      print version number, then exit
-
-Report bugs and patches to <config-patches@gnu.org>."
-
-version="\
-GNU config.sub ($timestamp)
-
-Copyright 1992-2018 Free Software Foundation, Inc.
-
-This is free software; see the source for copying conditions.  There is NO
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-
-help="
-Try \`$me --help' for more information."
-
-# Parse command line
-while test $# -gt 0 ; do
-  case $1 in
-    --time-stamp | --time* | -t )
-       echo "$timestamp" ; exit ;;
-    --version | -v )
-       echo "$version" ; exit ;;
-    --help | --h* | -h )
-       echo "$usage"; exit ;;
-    -- )     # Stop option processing
-       shift; break ;;
-    - )	# Use stdin as input.
-       break ;;
-    -* )
-       echo "$me: invalid option $1$help"
-       exit 1 ;;
-
-    *local*)
-       # First pass through any local machine types.
-       echo "$1"
-       exit ;;
-
-    * )
-       break ;;
-  esac
-done
-
-case $# in
- 0) echo "$me: missing argument$help" >&2
-    exit 1;;
- 1) ;;
- *) echo "$me: too many arguments$help" >&2
-    exit 1;;
-esac
-
-# Split fields of configuration type
-IFS="-" read -r field1 field2 field3 field4 <<EOF
-$1
-EOF
-
-# Separate into logical components for further validation
-case $1 in
-	*-*-*-*-*)
-		echo Invalid configuration \`"$1"\': more than four components >&2
-		exit 1
-		;;
-	*-*-*-*)
-		basic_machine=$field1-$field2
-		os=$field3-$field4
-		;;
-	*-*-*)
-		# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
-		# parts
-		maybe_os=$field2-$field3
-		case $maybe_os in
-			nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \
-			| linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \
-			| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
-			| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
-			| storm-chaos* | os2-emx* | rtmk-nova*)
-				basic_machine=$field1
-				os=$maybe_os
-				;;
-			android-linux)
-				basic_machine=$field1-unknown
-				os=linux-android
-				;;
-			*)
-				basic_machine=$field1-$field2
-				os=$field3
-				;;
-		esac
-		;;
-	*-*)
-		# Second component is usually, but not always the OS
-		case $field2 in
-			# Prevent following clause from handling this valid os
-			sun*os*)
-				basic_machine=$field1
-				os=$field2
-				;;
-			# Manufacturers
-			dec* | mips* | sequent* | encore* | pc532* | sgi* | sony* \
-			| att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
-			| unicom* | ibm* | next | hp | isi* | apollo | altos* \
-			| convergent* | ncr* | news | 32* | 3600* | 3100* | hitachi* \
-			| c[123]* | convex* | sun | crds | omron* | dg | ultra | tti* \
-			| harris | dolphin | highlevel | gould | cbm | ns | masscomp \
-			| apple | axis | knuth | cray | microblaze* \
-			| sim | cisco | oki | wec | wrs | winbond)
-				basic_machine=$field1-$field2
-				os=
-				;;
-			*)
-				basic_machine=$field1
-				os=$field2
-				;;
-		esac
-		;;
-	*)
-		# Convert single-component short-hands not valid as part of
-		# multi-component configurations.
-		case $field1 in
-			386bsd)
-				basic_machine=i386-pc
-				os=bsd
-				;;
-			a29khif)
-				basic_machine=a29k-amd
-				os=udi
-				;;
-			adobe68k)
-				basic_machine=m68010-adobe
-				os=scout
-				;;
-			alliant)
-				basic_machine=fx80-alliant
-				os=
-				;;
-			altos | altos3068)
-				basic_machine=m68k-altos
-				os=
-				;;
-			am29k)
-				basic_machine=a29k-none
-				os=bsd
-				;;
-			amdahl)
-				basic_machine=580-amdahl
-				os=sysv
-				;;
-			amigaos | amigados)
-				basic_machine=m68k-unknown
-				os=amigaos
-				;;
-			amigaunix | amix)
-				basic_machine=m68k-unknown
-				os=sysv4
-				;;
-			apollo68)
-				basic_machine=m68k-apollo
-				os=sysv
-				;;
-			apollo68bsd)
-				basic_machine=m68k-apollo
-				os=bsd
-				;;
-			aros)
-				basic_machine=i386-pc
-				os=aros
-				;;
-			aux)
-				basic_machine=m68k-apple
-				os=aux
-				;;
-			balance)
-				basic_machine=ns32k-sequent
-				os=dynix
-				;;
-			blackfin)
-				basic_machine=bfin-unknown
-				os=linux
-				;;
-			cegcc)
-				basic_machine=arm-unknown
-				os=cegcc
-				;;
-			convex-c1)
-				basic_machine=c1-convex
-				os=bsd
-				;;
-			convex-c2)
-				basic_machine=c2-convex
-				os=bsd
-				;;
-			convex-c32)
-				basic_machine=c32-convex
-				os=bsd
-				;;
-			convex-c34)
-				basic_machine=c34-convex
-				os=bsd
-				;;
-			convex-c38)
-				basic_machine=c38-convex
-				os=bsd
-				;;
-			cray)
-				basic_machine=j90-cray
-				os=unicos
-				;;
-			crds | unos)
-				basic_machine=m68k-crds
-				os=
-				;;
-			delta88)
-				basic_machine=m88k-motorola
-				os=sysv3
-				;;
-			dicos)
-				basic_machine=i686-pc
-				os=dicos
-				;;
-			djgpp)
-				basic_machine=i586-pc
-				os=msdosdjgpp
-				;;
-			ebmon29k)
-				basic_machine=a29k-amd
-				os=ebmon
-				;;
-			es1800 | OSE68k | ose68k | ose | OSE)
-				basic_machine=m68k-ericsson
-				os=ose
-				;;
-			gmicro)
-				basic_machine=tron-gmicro
-				os=sysv
-				;;
-			go32)
-				basic_machine=i386-pc
-				os=go32
-				;;
-			h8300hms)
-				basic_machine=h8300-hitachi
-				os=hms
-				;;
-			h8300xray)
-				basic_machine=h8300-hitachi
-				os=xray
-				;;
-			h8500hms)
-				basic_machine=h8500-hitachi
-				os=hms
-				;;
-			harris)
-				basic_machine=m88k-harris
-				os=sysv3
-				;;
-			hp300bsd)
-				basic_machine=m68k-hp
-				os=bsd
-				;;
-			hp300hpux)
-				basic_machine=m68k-hp
-				os=hpux
-				;;
-			hppaosf)
-				basic_machine=hppa1.1-hp
-				os=osf
-				;;
-			hppro)
-				basic_machine=hppa1.1-hp
-				os=proelf
-				;;
-			i386mach)
-				basic_machine=i386-mach
-				os=mach
-				;;
-			vsta)
-				basic_machine=i386-pc
-				os=vsta
-				;;
-			isi68 | isi)
-				basic_machine=m68k-isi
-				os=sysv
-				;;
-			m68knommu)
-				basic_machine=m68k-unknown
-				os=linux
-				;;
-			magnum | m3230)
-				basic_machine=mips-mips
-				os=sysv
-				;;
-			merlin)
-				basic_machine=ns32k-utek
-				os=sysv
-				;;
-			mingw64)
-				basic_machine=x86_64-pc
-				os=mingw64
-				;;
-			mingw32)
-				basic_machine=i686-pc
-				os=mingw32
-				;;
-			mingw32ce)
-				basic_machine=arm-unknown
-				os=mingw32ce
-				;;
-			monitor)
-				basic_machine=m68k-rom68k
-				os=coff
-				;;
-			morphos)
-				basic_machine=powerpc-unknown
-				os=morphos
-				;;
-			moxiebox)
-				basic_machine=moxie-unknown
-				os=moxiebox
-				;;
-			msdos)
-				basic_machine=i386-pc
-				os=msdos
-				;;
-			msys)
-				basic_machine=i686-pc
-				os=msys
-				;;
-			mvs)
-				basic_machine=i370-ibm
-				os=mvs
-				;;
-			nacl)
-				basic_machine=le32-unknown
-				os=nacl
-				;;
-			ncr3000)
-				basic_machine=i486-ncr
-				os=sysv4
-				;;
-			netbsd386)
-				basic_machine=i386-pc
-				os=netbsd
-				;;
-			netwinder)
-				basic_machine=armv4l-rebel
-				os=linux
-				;;
-			news | news700 | news800 | news900)
-				basic_machine=m68k-sony
-				os=newsos
-				;;
-			news1000)
-				basic_machine=m68030-sony
-				os=newsos
-				;;
-			necv70)
-				basic_machine=v70-nec
-				os=sysv
-				;;
-			nh3000)
-				basic_machine=m68k-harris
-				os=cxux
-				;;
-			nh[45]000)
-				basic_machine=m88k-harris
-				os=cxux
-				;;
-			nindy960)
-				basic_machine=i960-intel
-				os=nindy
-				;;
-			mon960)
-				basic_machine=i960-intel
-				os=mon960
-				;;
-			nonstopux)
-				basic_machine=mips-compaq
-				os=nonstopux
-				;;
-			os400)
-				basic_machine=powerpc-ibm
-				os=os400
-				;;
-			OSE68000 | ose68000)
-				basic_machine=m68000-ericsson
-				os=ose
-				;;
-			os68k)
-				basic_machine=m68k-none
-				os=os68k
-				;;
-			paragon)
-				basic_machine=i860-intel
-				os=osf
-				;;
-			parisc)
-				basic_machine=hppa-unknown
-				os=linux
-				;;
-			pw32)
-				basic_machine=i586-unknown
-				os=pw32
-				;;
-			rdos | rdos64)
-				basic_machine=x86_64-pc
-				os=rdos
-				;;
-			rdos32)
-				basic_machine=i386-pc
-				os=rdos
-				;;
-			rom68k)
-				basic_machine=m68k-rom68k
-				os=coff
-				;;
-			sa29200)
-				basic_machine=a29k-amd
-				os=udi
-				;;
-			sei)
-				basic_machine=mips-sei
-				os=seiux
-				;;
-			sps7)
-				basic_machine=m68k-bull
-				os=sysv2
-				;;
-			st2000)
-				basic_machine=m68k-tandem
-				os=
-				;;
-			stratus)
-				basic_machine=i860-stratus
-				os=sysv4
-				;;
-			sun2)
-				basic_machine=m68000-sun
-				os=
-				;;
-			sun2os3)
-				basic_machine=m68000-sun
-				os=sunos3
-				;;
-			sun2os4)
-				basic_machine=m68000-sun
-				os=sunos4
-				;;
-			sun3)
-				basic_machine=m68k-sun
-				os=
-				;;
-			sun3os3)
-				basic_machine=m68k-sun
-				os=sunos3
-				;;
-			sun3os4)
-				basic_machine=m68k-sun
-				os=sunos4
-				;;
-			sun4)
-				basic_machine=sparc-sun
-				os=
-				;;
-			sun4os3)
-				basic_machine=sparc-sun
-				os=sunos3
-				;;
-			sun4os4)
-				basic_machine=sparc-sun
-				os=sunos4
-				;;
-			sun4sol2)
-				basic_machine=sparc-sun
-				os=solaris2
-				;;
-			sun386 | sun386i | roadrunner)
-				basic_machine=i386-sun
-				os=
-				;;
-			sv1)
-				basic_machine=sv1-cray
-				os=unicos
-				;;
-			symmetry)
-				basic_machine=i386-sequent
-				os=dynix
-				;;
-			t3e)
-				basic_machine=alphaev5-cray
-				os=unicos
-				;;
-			t90)
-				basic_machine=t90-cray
-				os=unicos
-				;;
-			toad1)
-				basic_machine=pdp10-xkl
-				os=tops20
-				;;
-			tpf)
-				basic_machine=s390x-ibm
-				os=tpf
-				;;
-			udi29k)
-				basic_machine=a29k-amd
-				os=udi
-				;;
-			ultra3)
-				basic_machine=a29k-nyu
-				os=sym1
-				;;
-			v810 | necv810)
-				basic_machine=v810-nec
-				os=none
-				;;
-			vaxv)
-				basic_machine=vax-dec
-				os=sysv
-				;;
-			vms)
-				basic_machine=vax-dec
-				os=vms
-				;;
-			vxworks960)
-				basic_machine=i960-wrs
-				os=vxworks
-				;;
-			vxworks68)
-				basic_machine=m68k-wrs
-				os=vxworks
-				;;
-			vxworks29k)
-				basic_machine=a29k-wrs
-				os=vxworks
-				;;
-			xbox)
-				basic_machine=i686-pc
-				os=mingw32
-				;;
-			ymp)
-				basic_machine=ymp-cray
-				os=unicos
-				;;
-			*)
-				basic_machine=$1
-				os=
-				;;
-		esac
-		;;
-esac
-
-# Decode aliases for certain CPU-COMPANY combinations.
-case $basic_machine in
-	# Here we handle the default manufacturer of certain CPU types.  It is in
-	# some cases the only manufacturer, in others, it is the most popular.
-	craynv)
-		basic_machine=craynv-cray
-		os=${os:-unicosmp}
-		;;
-	fx80)
-		basic_machine=fx80-alliant
-		;;
-	w89k)
-		basic_machine=hppa1.1-winbond
-		;;
-	op50n)
-		basic_machine=hppa1.1-oki
-		;;
-	op60c)
-		basic_machine=hppa1.1-oki
-		;;
-	romp)
-		basic_machine=romp-ibm
-		;;
-	mmix)
-		basic_machine=mmix-knuth
-		;;
-	rs6000)
-		basic_machine=rs6000-ibm
-		;;
-	vax)
-		basic_machine=vax-dec
-		;;
-	pdp11)
-		basic_machine=pdp11-dec
-		;;
-	we32k)
-		basic_machine=we32k-att
-		;;
-	cydra)
-		basic_machine=cydra-cydrome
-		;;
-	i370-ibm* | ibm*)
-		basic_machine=i370-ibm
-		;;
-	orion)
-		basic_machine=orion-highlevel
-		;;
-	orion105)
-		basic_machine=clipper-highlevel
-		;;
-	mac | mpw | mac-mpw)
-		basic_machine=m68k-apple
-		;;
-	pmac | pmac-mpw)
-		basic_machine=powerpc-apple
-		;;
-	xps | xps100)
-		basic_machine=xps100-honeywell
-		;;
-
-	# Recognize the basic CPU types without company name.
-	# Some are omitted here because they have special meanings below.
-	1750a | 580 \
-	| a29k \
-	| aarch64 | aarch64_be \
-	| abacus \
-	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
-	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
-	| am33_2.0 \
-	| arc | arceb \
-	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv6m | armv[78][arm] \
-	| avr | avr32 \
-	| asmjs \
-	| ba \
-	| be32 | be64 \
-	| bfin \
-	| c4x | c8051 | clipper | csky \
-	| d10v | d30v | dlx | dsp16xx \
-	| e2k | epiphany \
-	| fido | fr30 | frv | ft32 \
-	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
-	| hexagon \
-	| i370 | i860 | i960 | ia16 | ia64 \
-	| ip2k | iq2000 \
-	| k1om \
-	| le32 | le64 \
-	| lm32 \
-	| m32c | m32r | m32rle | m68000 | m68k | m88k \
-	| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip \
-	| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
-	| mips | mipsbe | mipseb | mipsel | mipsle \
-	| mips16 \
-	| mips64 | mips64el \
-	| mips64octeon | mips64octeonel \
-	| mips64orion | mips64orionel \
-	| mips64r5900 | mips64r5900el \
-	| mips64vr | mips64vrel \
-	| mips64vr4100 | mips64vr4100el \
-	| mips64vr4300 | mips64vr4300el \
-	| mips64vr5000 | mips64vr5000el \
-	| mips64vr5900 | mips64vr5900el \
-	| mipsisa32 | mipsisa32el \
-	| mipsisa32r2 | mipsisa32r2el \
-	| mipsisa32r6 | mipsisa32r6el \
-	| mipsisa64 | mipsisa64el \
-	| mipsisa64r2 | mipsisa64r2el \
-	| mipsisa64r6 | mipsisa64r6el \
-	| mipsisa64sb1 | mipsisa64sb1el \
-	| mipsisa64sr71k | mipsisa64sr71kel \
-	| mipsr5900 | mipsr5900el \
-	| mipstx39 | mipstx39el \
-	| mn10200 | mn10300 \
-	| moxie \
-	| mt \
-	| msp430 \
-	| nds32 | nds32le | nds32be \
-	| nfp \
-	| nios | nios2 | nios2eb | nios2el \
-	| ns16k | ns32k \
-	| open8 | or1k | or1knd | or32 \
-	| pdp10 | pj | pjl \
-	| powerpc | powerpc64 | powerpc64le | powerpcle \
-	| pru \
-	| pyramid \
-	| riscv | riscv32 | riscv64 \
-	| rl78 | rx \
-	| score \
-	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh[23]ele \
-	| sh64 | sh64le \
-	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
-	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
-	| spu \
-	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
-	| ubicom32 \
-	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
-	| visium \
-	| wasm32 \
-	| x86 | xc16x | xstormy16 | xgate | xtensa \
-	| z8k | z80)
-		basic_machine=$basic_machine-unknown
-		;;
-	c54x)
-		basic_machine=tic54x-unknown
-		;;
-	c55x)
-		basic_machine=tic55x-unknown
-		;;
-	c6x)
-		basic_machine=tic6x-unknown
-		;;
-	leon|leon[3-9])
-		basic_machine=sparc-$basic_machine
-		;;
-	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65)
-		;;
-	m9s12z | m68hcs12z | hcs12z | s12z)
-		basic_machine=s12z-unknown
-		;;
-	m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
-		basic_machine=s12z-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	ms1)
-		basic_machine=mt-unknown
-		;;
-	strongarm | thumb | xscale)
-		basic_machine=arm-unknown
-		;;
-	xscaleeb)
-		basic_machine=armeb-unknown
-		;;
-
-	xscaleel)
-		basic_machine=armel-unknown
-		;;
-
-	# We use `pc' rather than `unknown'
-	# because (1) that's what they normally are, and
-	# (2) the word "unknown" tends to confuse beginning users.
-	i*86 | x86_64)
-	  basic_machine=$basic_machine-pc
-	  ;;
-	# Recognize the basic CPU types with company name.
-	1750a-* | 580-* \
-	| a29k-* \
-	| aarch64-* | aarch64_be-* \
-	| abacus-* \
-	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
-	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
-	| alphapca5[67]-* | alpha64pca5[67]-* \
-	| am33_2.0-* \
-	| arc-* | arceb-* \
-	| arm-*  | arm[lb]e-* | arme[lb]-* | armv*-* \
-	| avr-* | avr32-* \
-	| asmjs-* \
-	| ba-* \
-	| be32-* | be64-* \
-	| bfin-* | bs2000-* \
-	| c[123]* | c30-* | [cjt]90-* | c4x-* \
-	| c8051-* | clipper-* | craynv-* | csky-* | cydra-* \
-	| d10v-* | d30v-* | dlx-* | dsp16xx-* \
-	| e2k-* | elxsi-* | epiphany-* \
-	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | ft32-* | fx80-* \
-	| h8300-* | h8500-* \
-	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
-	| hexagon-* \
-	| i370-* | i*86-* | i860-* | i960-* | ia16-* | ia64-* \
-	| ip2k-* | iq2000-* \
-	| k1om-* \
-	| le32-* | le64-* \
-	| lm32-* \
-	| m32c-* | m32r-* | m32rle-* \
-	| m5200-* | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* | v70-* | w65-* \
-	| m6811-* | m68hc11-* | m6812-* | m68hc12-* | m68hcs12x-* | nvptx-* | picochip-* \
-	| m88110-* | m88k-* | maxq-* | mb-* | mcore-* | mep-* | metag-* \
-	| microblaze-* | microblazeel-* \
-	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
-	| mips16-* \
-	| mips64-* | mips64el-* \
-	| mips64octeon-* | mips64octeonel-* \
-	| mips64orion-* | mips64orionel-* \
-	| mips64r5900-* | mips64r5900el-* \
-	| mips64vr-* | mips64vrel-* \
-	| mips64vr4100-* | mips64vr4100el-* \
-	| mips64vr4300-* | mips64vr4300el-* \
-	| mips64vr5000-* | mips64vr5000el-* \
-	| mips64vr5900-* | mips64vr5900el-* \
-	| mipsisa32-* | mipsisa32el-* \
-	| mipsisa32r2-* | mipsisa32r2el-* \
-	| mipsisa32r6-* | mipsisa32r6el-* \
-	| mipsisa64-* | mipsisa64el-* \
-	| mipsisa64r2-* | mipsisa64r2el-* \
-	| mipsisa64r6-* | mipsisa64r6el-* \
-	| mipsisa64sb1-* | mipsisa64sb1el-* \
-	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
-	| mipsr5900-* | mipsr5900el-* \
-	| mipstx39-* | mipstx39el-* \
-	| mmix-* \
-	| mn10200-* | mn10300-* \
-	| moxie-* \
-	| mt-* \
-	| msp430-* \
-	| nds32-* | nds32le-* | nds32be-* \
-	| nfp-* \
-	| nios-* | nios2-* | nios2eb-* | nios2el-* \
-	| none-* | np1-* | ns16k-* | ns32k-* \
-	| open8-* \
-	| or1k*-* \
-	| or32-* \
-	| orion-* \
-	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
-	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
-	| pru-* \
-	| pyramid-* \
-	| riscv-* | riscv32-* | riscv64-* \
-	| rl78-* | romp-* | rs6000-* | rx-* \
-	| score-* \
-	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]ae[lb]-* | sh[23]e-* | she[lb]-* | sh[lb]e-* \
-	| sh[1234]e[lb]-* |  sh[12345][lb]e-* | sh[23]ele-* | sh64-* | sh64le-* \
-	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
-	| sparclite-* \
-	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
-	| spu-* \
-	| tahoe-* \
-	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
-	| tron-* \
-	| ubicom32-* \
-	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
-	| vax-* \
-	| visium-* \
-	| wasm32-* \
-	| we32k-* \
-	| x86-* | x86_64-* | xc16x-* | xgate-* | xps100-* \
-	| xstormy16-* | xtensa*-* \
-	| ymp-* \
-	| z8k-* | z80-*)
-		;;
-	# Recognize the basic CPU types without company name, with glob match.
-	xtensa*)
-		basic_machine=$basic_machine-unknown
-		;;
-	# Recognize the various machine names and aliases which stand
-	# for a CPU type and a company and sometimes even an OS.
-	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
-		basic_machine=m68000-att
-		;;
-	3b*)
-		basic_machine=we32k-att
-		;;
-	amd64)
-		basic_machine=x86_64-pc
-		;;
-	amd64-*)
-		basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	amiga | amiga-*)
-		basic_machine=m68k-unknown
-		;;
-	blackfin-*)
-		basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		os=linux
-		;;
-	bluegene*)
-		basic_machine=powerpc-ibm
-		os=cnk
-		;;
-	c54x-*)
-		basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	c55x-*)
-		basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	c6x-*)
-		basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	c90)
-		basic_machine=c90-cray
-		os=${os:-unicos}
-		;;
-	cr16 | cr16-*)
-		basic_machine=cr16-unknown
-		os=${os:-elf}
-		;;
-	crisv32 | crisv32-* | etraxfs*)
-		basic_machine=crisv32-axis
-		;;
-	cris | cris-* | etrax*)
-		basic_machine=cris-axis
-		;;
-	crx)
-		basic_machine=crx-unknown
-		os=${os:-elf}
-		;;
-	da30 | da30-*)
-		basic_machine=m68k-da30
-		;;
-	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
-		basic_machine=mips-dec
-		;;
-	decsystem10* | dec10*)
-		basic_machine=pdp10-dec
-		os=tops10
-		;;
-	decsystem20* | dec20*)
-		basic_machine=pdp10-dec
-		os=tops20
-		;;
-	delta | 3300 | motorola-3300 | motorola-delta \
-	      | 3300-motorola | delta-motorola)
-		basic_machine=m68k-motorola
-		;;
-	dpx20 | dpx20-*)
-		basic_machine=rs6000-bull
-		os=${os:-bosx}
-		;;
-	dpx2*)
-		basic_machine=m68k-bull
-		os=sysv3
-		;;
-	e500v[12])
-		basic_machine=powerpc-unknown
-		os=$os"spe"
-		;;
-	e500v[12]-*)
-		basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		os=$os"spe"
-		;;
-	encore | umax | mmax)
-		basic_machine=ns32k-encore
-		;;
-	elxsi)
-		basic_machine=elxsi-elxsi
-		os=${os:-bsd}
-		;;
-	fx2800)
-		basic_machine=i860-alliant
-		;;
-	genix)
-		basic_machine=ns32k-ns
-		;;
-	h3050r* | hiux*)
-		basic_machine=hppa1.1-hitachi
-		os=hiuxwe2
-		;;
-	hp300-*)
-		basic_machine=m68k-hp
-		;;
-	hp3k9[0-9][0-9] | hp9[0-9][0-9])
-		basic_machine=hppa1.0-hp
-		;;
-	hp9k2[0-9][0-9] | hp9k31[0-9])
-		basic_machine=m68000-hp
-		;;
-	hp9k3[2-9][0-9])
-		basic_machine=m68k-hp
-		;;
-	hp9k6[0-9][0-9] | hp6[0-9][0-9])
-		basic_machine=hppa1.0-hp
-		;;
-	hp9k7[0-79][0-9] | hp7[0-79][0-9])
-		basic_machine=hppa1.1-hp
-		;;
-	hp9k78[0-9] | hp78[0-9])
-		# FIXME: really hppa2.0-hp
-		basic_machine=hppa1.1-hp
-		;;
-	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
-		# FIXME: really hppa2.0-hp
-		basic_machine=hppa1.1-hp
-		;;
-	hp9k8[0-9][13679] | hp8[0-9][13679])
-		basic_machine=hppa1.1-hp
-		;;
-	hp9k8[0-9][0-9] | hp8[0-9][0-9])
-		basic_machine=hppa1.0-hp
-		;;
-	i*86v32)
-		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
-		os=sysv32
-		;;
-	i*86v4*)
-		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
-		os=sysv4
-		;;
-	i*86v)
-		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
-		os=sysv
-		;;
-	i*86sol2)
-		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
-		os=solaris2
-		;;
-	j90 | j90-cray)
-		basic_machine=j90-cray
-		os=${os:-unicos}
-		;;
-	iris | iris4d)
-		basic_machine=mips-sgi
-		case $os in
-		    irix*)
-			;;
-		    *)
-			os=irix4
-			;;
-		esac
-		;;
-	leon-*|leon[3-9]-*)
-		basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'`
-		;;
-	m68knommu-*)
-		basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		os=linux
-		;;
-	microblaze*)
-		basic_machine=microblaze-xilinx
-		;;
-	miniframe)
-		basic_machine=m68000-convergent
-		;;
-	*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
-		basic_machine=m68k-atari
-		os=mint
-		;;
-	mips3*-*)
-		basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`
-		;;
-	mips3*)
-		basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown
-		;;
-	ms1-*)
-		basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'`
-		;;
-	news-3600 | risc-news)
-		basic_machine=mips-sony
-		os=newsos
-		;;
-	next | m*-next)
-		basic_machine=m68k-next
-		case $os in
-		    nextstep* )
-			;;
-		    ns2*)
-		      os=nextstep2
-			;;
-		    *)
-		      os=nextstep3
-			;;
-		esac
-		;;
-	np1)
-		basic_machine=np1-gould
-		;;
-	neo-tandem)
-		basic_machine=neo-tandem
-		;;
-	nse-tandem)
-		basic_machine=nse-tandem
-		;;
-	nsr-tandem)
-		basic_machine=nsr-tandem
-		;;
-	nsv-tandem)
-		basic_machine=nsv-tandem
-		;;
-	nsx-tandem)
-		basic_machine=nsx-tandem
-		;;
-	op50n-* | op60c-*)
-		basic_machine=hppa1.1-oki
-		os=proelf
-		;;
-	openrisc | openrisc-*)
-		basic_machine=or32-unknown
-		;;
-	pa-hitachi)
-		basic_machine=hppa1.1-hitachi
-		os=hiuxwe2
-		;;
-	parisc-*)
-		basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		os=linux
-		;;
-	pbd)
-		basic_machine=sparc-tti
-		;;
-	pbb)
-		basic_machine=m68k-tti
-		;;
-	pc532 | pc532-*)
-		basic_machine=ns32k-pc532
-		;;
-	pc98)
-		basic_machine=i386-pc
-		;;
-	pc98-*)
-		basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	pentium | p5 | k5 | k6 | nexgen | viac3)
-		basic_machine=i586-pc
-		;;
-	pentiumpro | p6 | 6x86 | athlon | athlon_*)
-		basic_machine=i686-pc
-		;;
-	pentiumii | pentium2 | pentiumiii | pentium3)
-		basic_machine=i686-pc
-		;;
-	pentium4)
-		basic_machine=i786-pc
-		;;
-	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
-		basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	pentiumpro-* | p6-* | 6x86-* | athlon-*)
-		basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
-		basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	pentium4-*)
-		basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	pn)
-		basic_machine=pn-gould
-		;;
-	power)	basic_machine=power-ibm
-		;;
-	ppc | ppcbe)	basic_machine=powerpc-unknown
-		;;
-	ppc-* | ppcbe-*)
-		basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	ppcle | powerpclittle)
-		basic_machine=powerpcle-unknown
-		;;
-	ppcle-* | powerpclittle-*)
-		basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	ppc64)	basic_machine=powerpc64-unknown
-		;;
-	ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	ppc64le | powerpc64little)
-		basic_machine=powerpc64le-unknown
-		;;
-	ppc64le-* | powerpc64little-*)
-		basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	ps2)
-		basic_machine=i386-ibm
-		;;
-	rm[46]00)
-		basic_machine=mips-siemens
-		;;
-	rtpc | rtpc-*)
-		basic_machine=romp-ibm
-		;;
-	s390 | s390-*)
-		basic_machine=s390-ibm
-		;;
-	s390x | s390x-*)
-		basic_machine=s390x-ibm
-		;;
-	sb1)
-		basic_machine=mipsisa64sb1-unknown
-		;;
-	sb1el)
-		basic_machine=mipsisa64sb1el-unknown
-		;;
-	sde)
-		basic_machine=mipsisa32-sde
-		os=${os:-elf}
-		;;
-	sequent)
-		basic_machine=i386-sequent
-		;;
-	sh5el)
-		basic_machine=sh5le-unknown
-		;;
-	sh5el-*)
-		basic_machine=sh5le-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	simso-wrs)
-		basic_machine=sparclite-wrs
-		os=vxworks
-		;;
-	spur)
-		basic_machine=spur-unknown
-		;;
-	strongarm-* | thumb-*)
-		basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'`
-		;;
-	tile*-*)
-		;;
-	tile*)
-		basic_machine=$basic_machine-unknown
-		os=${os:-linux-gnu}
-		;;
-	tx39)
-		basic_machine=mipstx39-unknown
-		;;
-	tx39el)
-		basic_machine=mipstx39el-unknown
-		;;
-	tower | tower-32)
-		basic_machine=m68k-ncr
-		;;
-	vpp*|vx|vx-*)
-		basic_machine=f301-fujitsu
-		;;
-	w65*)
-		basic_machine=w65-wdc
-		os=none
-		;;
-	w89k-*)
-		basic_machine=hppa1.1-winbond
-		os=proelf
-		;;
-	x64)
-		basic_machine=x86_64-pc
-		;;
-	xscale-* | xscalee[bl]-*)
-		basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'`
-		;;
-	none)
-		basic_machine=none-none
-		;;
-
-	*)
-		echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
-		exit 1
-		;;
-esac
-
-# Here we canonicalize certain aliases for manufacturers.
-case $basic_machine in
-	*-digital*)
-		basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'`
-		;;
-	*-commodore*)
-		basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'`
-		;;
-	*)
-		;;
-esac
-
-# Decode manufacturer-specific aliases for certain operating systems.
-
-if [ x$os != x ]
-then
-case $os in
-	# First match some system type aliases that might get confused
-	# with valid system types.
-	# solaris* is a basic system type, with this one exception.
-	auroraux)
-		os=auroraux
-		;;
-	bluegene*)
-		os=cnk
-		;;
-	solaris1 | solaris1.*)
-		os=`echo $os | sed -e 's|solaris1|sunos4|'`
-		;;
-	solaris)
-		os=solaris2
-		;;
-	unixware*)
-		os=sysv4.2uw
-		;;
-	gnu/linux*)
-		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
-		;;
-	# es1800 is here to avoid being matched by es* (a different OS)
-	es1800*)
-		os=ose
-		;;
-	# Some version numbers need modification
-	chorusos*)
-		os=chorusos
-		;;
-	isc)
-		os=isc2.2
-		;;
-	sco6)
-		os=sco5v6
-		;;
-	sco5)
-		os=sco3.2v5
-		;;
-	sco4)
-		os=sco3.2v4
-		;;
-	sco3.2.[4-9]*)
-		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
-		;;
-	sco3.2v[4-9]* | sco5v6*)
-		# Don't forget version if it is 3.2v4 or newer.
-		;;
-	scout)
-		# Don't match below
-		;;
-	sco*)
-		os=sco3.2v2
-		;;
-	psos*)
-		os=psos
-		;;
-	# Now accept the basic system types.
-	# The portable systems comes first.
-	# Each alternative MUST end in a * to match a version number.
-	# sysv* is not here because it comes later, after sysvr4.
-	gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
-	     | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\
-	     | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
-	     | sym* | kopensolaris* | plan9* \
-	     | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
-	     | aos* | aros* | cloudabi* | sortix* \
-	     | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
-	     | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
-	     | knetbsd* | mirbsd* | netbsd* \
-	     | bitrig* | openbsd* | solidbsd* | libertybsd* \
-	     | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \
-	     | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
-	     | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
-	     | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \
-	     | chorusrdb* | cegcc* | glidix* \
-	     | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
-	     | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \
-	     | linux-newlib* | linux-musl* | linux-uclibc* \
-	     | uxpv* | beos* | mpeix* | udk* | moxiebox* \
-	     | interix* | uwin* | mks* | rhapsody* | darwin* \
-	     | openstep* | oskit* | conix* | pw32* | nonstopux* \
-	     | storm-chaos* | tops10* | tenex* | tops20* | its* \
-	     | os2* | vos* | palmos* | uclinux* | nucleus* \
-	     | morphos* | superux* | rtmk* | windiss* \
-	     | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
-	     | skyos* | haiku* | rdos* | toppers* | drops* | es* \
-	     | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
-	     | midnightbsd*)
-	# Remember, each alternative MUST END IN *, to match a version number.
-		;;
-	qnx*)
-		case $basic_machine in
-		    x86-* | i*86-*)
-			;;
-		    *)
-			os=nto-$os
-			;;
-		esac
-		;;
-	hiux*)
-		os=hiuxwe2
-		;;
-	nto-qnx*)
-		;;
-	nto*)
-		os=`echo $os | sed -e 's|nto|nto-qnx|'`
-		;;
-	sim | xray | os68k* | v88r* \
-	    | windows* | osx | abug | netware* | os9* \
-	    | macos* | mpw* | magic* | mmixware* | mon960* | lnews*)
-		;;
-	linux-dietlibc)
-		os=linux-dietlibc
-		;;
-	linux*)
-		os=`echo $os | sed -e 's|linux|linux-gnu|'`
-		;;
-	lynx*178)
-		os=lynxos178
-		;;
-	lynx*5)
-		os=lynxos5
-		;;
-	lynx*)
-		os=lynxos
-		;;
-	mac*)
-		os=`echo "$os" | sed -e 's|mac|macos|'`
-		;;
-	opened*)
-		os=openedition
-		;;
-	os400*)
-		os=os400
-		;;
-	sunos5*)
-		os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
-		;;
-	sunos6*)
-		os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
-		;;
-	wince*)
-		os=wince
-		;;
-	utek*)
-		os=bsd
-		;;
-	dynix*)
-		os=bsd
-		;;
-	acis*)
-		os=aos
-		;;
-	atheos*)
-		os=atheos
-		;;
-	syllable*)
-		os=syllable
-		;;
-	386bsd)
-		os=bsd
-		;;
-	ctix* | uts*)
-		os=sysv
-		;;
-	nova*)
-		os=rtmk-nova
-		;;
-	ns2)
-		os=nextstep2
-		;;
-	nsk*)
-		os=nsk
-		;;
-	# Preserve the version number of sinix5.
-	sinix5.*)
-		os=`echo $os | sed -e 's|sinix|sysv|'`
-		;;
-	sinix*)
-		os=sysv4
-		;;
-	tpf*)
-		os=tpf
-		;;
-	triton*)
-		os=sysv3
-		;;
-	oss*)
-		os=sysv3
-		;;
-	svr4*)
-		os=sysv4
-		;;
-	svr3)
-		os=sysv3
-		;;
-	sysvr4)
-		os=sysv4
-		;;
-	# This must come after sysvr4.
-	sysv*)
-		;;
-	ose*)
-		os=ose
-		;;
-	*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
-		os=mint
-		;;
-	zvmoe)
-		os=zvmoe
-		;;
-	dicos*)
-		os=dicos
-		;;
-	pikeos*)
-		# Until real need of OS specific support for
-		# particular features comes up, bare metal
-		# configurations are quite functional.
-		case $basic_machine in
-		    arm*)
-			os=eabi
-			;;
-		    *)
-			os=elf
-			;;
-		esac
-		;;
-	nacl*)
-		;;
-	ios)
-		;;
-	none)
-		;;
-	*-eabi)
-		;;
-	*)
-		echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
-		exit 1
-		;;
-esac
-else
-
-# Here we handle the default operating systems that come with various machines.
-# The value should be what the vendor currently ships out the door with their
-# machine or put another way, the most popular os provided with the machine.
-
-# Note that if you're going to try to match "-MANUFACTURER" here (say,
-# "-sun"), then you have to tell the case statement up towards the top
-# that MANUFACTURER isn't an operating system.  Otherwise, code above
-# will signal an error saying that MANUFACTURER isn't an operating
-# system, and we'll never get to this point.
-
-case $basic_machine in
-	score-*)
-		os=elf
-		;;
-	spu-*)
-		os=elf
-		;;
-	*-acorn)
-		os=riscix1.2
-		;;
-	arm*-rebel)
-		os=linux
-		;;
-	arm*-semi)
-		os=aout
-		;;
-	c4x-* | tic4x-*)
-		os=coff
-		;;
-	c8051-*)
-		os=elf
-		;;
-	clipper-intergraph)
-		os=clix
-		;;
-	hexagon-*)
-		os=elf
-		;;
-	tic54x-*)
-		os=coff
-		;;
-	tic55x-*)
-		os=coff
-		;;
-	tic6x-*)
-		os=coff
-		;;
-	# This must come before the *-dec entry.
-	pdp10-*)
-		os=tops20
-		;;
-	pdp11-*)
-		os=none
-		;;
-	*-dec | vax-*)
-		os=ultrix4.2
-		;;
-	m68*-apollo)
-		os=domain
-		;;
-	i386-sun)
-		os=sunos4.0.2
-		;;
-	m68000-sun)
-		os=sunos3
-		;;
-	m68*-cisco)
-		os=aout
-		;;
-	mep-*)
-		os=elf
-		;;
-	mips*-cisco)
-		os=elf
-		;;
-	mips*-*)
-		os=elf
-		;;
-	or32-*)
-		os=coff
-		;;
-	*-tti)	# must be before sparc entry or we get the wrong os.
-		os=sysv3
-		;;
-	sparc-* | *-sun)
-		os=sunos4.1.1
-		;;
-	pru-*)
-		os=elf
-		;;
-	*-be)
-		os=beos
-		;;
-	*-ibm)
-		os=aix
-		;;
-	*-knuth)
-		os=mmixware
-		;;
-	*-wec)
-		os=proelf
-		;;
-	*-winbond)
-		os=proelf
-		;;
-	*-oki)
-		os=proelf
-		;;
-	*-hp)
-		os=hpux
-		;;
-	*-hitachi)
-		os=hiux
-		;;
-	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
-		os=sysv
-		;;
-	*-cbm)
-		os=amigaos
-		;;
-	*-dg)
-		os=dgux
-		;;
-	*-dolphin)
-		os=sysv3
-		;;
-	m68k-ccur)
-		os=rtu
-		;;
-	m88k-omron*)
-		os=luna
-		;;
-	*-next)
-		os=nextstep
-		;;
-	*-sequent)
-		os=ptx
-		;;
-	*-crds)
-		os=unos
-		;;
-	*-ns)
-		os=genix
-		;;
-	i370-*)
-		os=mvs
-		;;
-	*-gould)
-		os=sysv
-		;;
-	*-highlevel)
-		os=bsd
-		;;
-	*-encore)
-		os=bsd
-		;;
-	*-sgi)
-		os=irix
-		;;
-	*-siemens)
-		os=sysv4
-		;;
-	*-masscomp)
-		os=rtu
-		;;
-	f30[01]-fujitsu | f700-fujitsu)
-		os=uxpv
-		;;
-	*-rom68k)
-		os=coff
-		;;
-	*-*bug)
-		os=coff
-		;;
-	*-apple)
-		os=macos
-		;;
-	*-atari*)
-		os=mint
-		;;
-	*-wrs)
-		os=vxworks
-		;;
-	*)
-		os=none
-		;;
-esac
-fi
-
-# Here we handle the case where we know the os, and the CPU type, but not the
-# manufacturer.  We pick the logical manufacturer.
-vendor=unknown
-case $basic_machine in
-	*-unknown)
-		case $os in
-			riscix*)
-				vendor=acorn
-				;;
-			sunos*)
-				vendor=sun
-				;;
-			cnk*|-aix*)
-				vendor=ibm
-				;;
-			beos*)
-				vendor=be
-				;;
-			hpux*)
-				vendor=hp
-				;;
-			mpeix*)
-				vendor=hp
-				;;
-			hiux*)
-				vendor=hitachi
-				;;
-			unos*)
-				vendor=crds
-				;;
-			dgux*)
-				vendor=dg
-				;;
-			luna*)
-				vendor=omron
-				;;
-			genix*)
-				vendor=ns
-				;;
-			clix*)
-				vendor=intergraph
-				;;
-			mvs* | opened*)
-				vendor=ibm
-				;;
-			os400*)
-				vendor=ibm
-				;;
-			ptx*)
-				vendor=sequent
-				;;
-			tpf*)
-				vendor=ibm
-				;;
-			vxsim* | vxworks* | windiss*)
-				vendor=wrs
-				;;
-			aux*)
-				vendor=apple
-				;;
-			hms*)
-				vendor=hitachi
-				;;
-			mpw* | macos*)
-				vendor=apple
-				;;
-			*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
-				vendor=atari
-				;;
-			vos*)
-				vendor=stratus
-				;;
-		esac
-		basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"`
-		;;
-esac
-
-echo "$basic_machine-$os"
-exit
-
-# Local variables:
-# eval: (add-hook 'before-save-hook 'time-stamp)
-# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
-# time-stamp-end: "'"
-# End:
diff --git a/third_party/nix/config/install-sh b/third_party/nix/config/install-sh
deleted file mode 100755
index 377bb8687f..0000000000
--- a/third_party/nix/config/install-sh
+++ /dev/null
@@ -1,527 +0,0 @@
-#!/bin/sh
-# install - install a program, script, or datafile
-
-scriptversion=2011-11-20.07; # UTC
-
-# This originates from X11R5 (mit/util/scripts/install.sh), which was
-# later released in X11R6 (xc/config/util/install.sh) with the
-# following copyright and license.
-#
-# Copyright (C) 1994 X Consortium
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
-# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
-# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-# Except as contained in this notice, the name of the X Consortium shall not
-# be used in advertising or otherwise to promote the sale, use or other deal-
-# ings in this Software without prior written authorization from the X Consor-
-# tium.
-#
-#
-# FSF changes to this file are in the public domain.
-#
-# Calling this script install-sh is preferred over install.sh, to prevent
-# 'make' implicit rules from creating a file called install from it
-# when there is no Makefile.
-#
-# This script is compatible with the BSD install script, but was written
-# from scratch.
-
-nl='
-'
-IFS=" ""	$nl"
-
-# set DOITPROG to echo to test this script
-
-# Don't use :- since 4.3BSD and earlier shells don't like it.
-doit=${DOITPROG-}
-if test -z "$doit"; then
-  doit_exec=exec
-else
-  doit_exec=$doit
-fi
-
-# Put in absolute file names if you don't have them in your path;
-# or use environment vars.
-
-chgrpprog=${CHGRPPROG-chgrp}
-chmodprog=${CHMODPROG-chmod}
-chownprog=${CHOWNPROG-chown}
-cmpprog=${CMPPROG-cmp}
-cpprog=${CPPROG-cp}
-mkdirprog=${MKDIRPROG-mkdir}
-mvprog=${MVPROG-mv}
-rmprog=${RMPROG-rm}
-stripprog=${STRIPPROG-strip}
-
-posix_glob='?'
-initialize_posix_glob='
-  test "$posix_glob" != "?" || {
-    if (set -f) 2>/dev/null; then
-      posix_glob=
-    else
-      posix_glob=:
-    fi
-  }
-'
-
-posix_mkdir=
-
-# Desired mode of installed file.
-mode=0755
-
-chgrpcmd=
-chmodcmd=$chmodprog
-chowncmd=
-mvcmd=$mvprog
-rmcmd="$rmprog -f"
-stripcmd=
-
-src=
-dst=
-dir_arg=
-dst_arg=
-
-copy_on_change=false
-no_target_directory=
-
-usage="\
-Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
-   or: $0 [OPTION]... SRCFILES... DIRECTORY
-   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
-   or: $0 [OPTION]... -d DIRECTORIES...
-
-In the 1st form, copy SRCFILE to DSTFILE.
-In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
-In the 4th, create DIRECTORIES.
-
-Options:
-     --help     display this help and exit.
-     --version  display version info and exit.
-
-  -c            (ignored)
-  -C            install only if different (preserve the last data modification time)
-  -d            create directories instead of installing files.
-  -g GROUP      $chgrpprog installed files to GROUP.
-  -m MODE       $chmodprog installed files to MODE.
-  -o USER       $chownprog installed files to USER.
-  -s            $stripprog installed files.
-  -t DIRECTORY  install into DIRECTORY.
-  -T            report an error if DSTFILE is a directory.
-
-Environment variables override the default commands:
-  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
-  RMPROG STRIPPROG
-"
-
-while test $# -ne 0; do
-  case $1 in
-    -c) ;;
-
-    -C) copy_on_change=true;;
-
-    -d) dir_arg=true;;
-
-    -g) chgrpcmd="$chgrpprog $2"
-	shift;;
-
-    --help) echo "$usage"; exit $?;;
-
-    -m) mode=$2
-	case $mode in
-	  *' '* | *'	'* | *'
-'*	  | *'*'* | *'?'* | *'['*)
-	    echo "$0: invalid mode: $mode" >&2
-	    exit 1;;
-	esac
-	shift;;
-
-    -o) chowncmd="$chownprog $2"
-	shift;;
-
-    -s) stripcmd=$stripprog;;
-
-    -t) dst_arg=$2
-	# Protect names problematic for 'test' and other utilities.
-	case $dst_arg in
-	  -* | [=\(\)!]) dst_arg=./$dst_arg;;
-	esac
-	shift;;
-
-    -T) no_target_directory=true;;
-
-    --version) echo "$0 $scriptversion"; exit $?;;
-
-    --)	shift
-	break;;
-
-    -*)	echo "$0: invalid option: $1" >&2
-	exit 1;;
-
-    *)  break;;
-  esac
-  shift
-done
-
-if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
-  # When -d is used, all remaining arguments are directories to create.
-  # When -t is used, the destination is already specified.
-  # Otherwise, the last argument is the destination.  Remove it from $@.
-  for arg
-  do
-    if test -n "$dst_arg"; then
-      # $@ is not empty: it contains at least $arg.
-      set fnord "$@" "$dst_arg"
-      shift # fnord
-    fi
-    shift # arg
-    dst_arg=$arg
-    # Protect names problematic for 'test' and other utilities.
-    case $dst_arg in
-      -* | [=\(\)!]) dst_arg=./$dst_arg;;
-    esac
-  done
-fi
-
-if test $# -eq 0; then
-  if test -z "$dir_arg"; then
-    echo "$0: no input file specified." >&2
-    exit 1
-  fi
-  # It's OK to call 'install-sh -d' without argument.
-  # This can happen when creating conditional directories.
-  exit 0
-fi
-
-if test -z "$dir_arg"; then
-  do_exit='(exit $ret); exit $ret'
-  trap "ret=129; $do_exit" 1
-  trap "ret=130; $do_exit" 2
-  trap "ret=141; $do_exit" 13
-  trap "ret=143; $do_exit" 15
-
-  # Set umask so as not to create temps with too-generous modes.
-  # However, 'strip' requires both read and write access to temps.
-  case $mode in
-    # Optimize common cases.
-    *644) cp_umask=133;;
-    *755) cp_umask=22;;
-
-    *[0-7])
-      if test -z "$stripcmd"; then
-	u_plus_rw=
-      else
-	u_plus_rw='% 200'
-      fi
-      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
-    *)
-      if test -z "$stripcmd"; then
-	u_plus_rw=
-      else
-	u_plus_rw=,u+rw
-      fi
-      cp_umask=$mode$u_plus_rw;;
-  esac
-fi
-
-for src
-do
-  # Protect names problematic for 'test' and other utilities.
-  case $src in
-    -* | [=\(\)!]) src=./$src;;
-  esac
-
-  if test -n "$dir_arg"; then
-    dst=$src
-    dstdir=$dst
-    test -d "$dstdir"
-    dstdir_status=$?
-  else
-
-    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
-    # might cause directories to be created, which would be especially bad
-    # if $src (and thus $dsttmp) contains '*'.
-    if test ! -f "$src" && test ! -d "$src"; then
-      echo "$0: $src does not exist." >&2
-      exit 1
-    fi
-
-    if test -z "$dst_arg"; then
-      echo "$0: no destination specified." >&2
-      exit 1
-    fi
-    dst=$dst_arg
-
-    # If destination is a directory, append the input filename; won't work
-    # if double slashes aren't ignored.
-    if test -d "$dst"; then
-      if test -n "$no_target_directory"; then
-	echo "$0: $dst_arg: Is a directory" >&2
-	exit 1
-      fi
-      dstdir=$dst
-      dst=$dstdir/`basename "$src"`
-      dstdir_status=0
-    else
-      # Prefer dirname, but fall back on a substitute if dirname fails.
-      dstdir=`
-	(dirname "$dst") 2>/dev/null ||
-	expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-	     X"$dst" : 'X\(//\)[^/]' \| \
-	     X"$dst" : 'X\(//\)$' \| \
-	     X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
-	echo X"$dst" |
-	    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-		   s//\1/
-		   q
-		 }
-		 /^X\(\/\/\)[^/].*/{
-		   s//\1/
-		   q
-		 }
-		 /^X\(\/\/\)$/{
-		   s//\1/
-		   q
-		 }
-		 /^X\(\/\).*/{
-		   s//\1/
-		   q
-		 }
-		 s/.*/./; q'
-      `
-
-      test -d "$dstdir"
-      dstdir_status=$?
-    fi
-  fi
-
-  obsolete_mkdir_used=false
-
-  if test $dstdir_status != 0; then
-    case $posix_mkdir in
-      '')
-	# Create intermediate dirs using mode 755 as modified by the umask.
-	# This is like FreeBSD 'install' as of 1997-10-28.
-	umask=`umask`
-	case $stripcmd.$umask in
-	  # Optimize common cases.
-	  *[2367][2367]) mkdir_umask=$umask;;
-	  .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
-	  *[0-7])
-	    mkdir_umask=`expr $umask + 22 \
-	      - $umask % 100 % 40 + $umask % 20 \
-	      - $umask % 10 % 4 + $umask % 2
-	    `;;
-	  *) mkdir_umask=$umask,go-w;;
-	esac
-
-	# With -d, create the new directory with the user-specified mode.
-	# Otherwise, rely on $mkdir_umask.
-	if test -n "$dir_arg"; then
-	  mkdir_mode=-m$mode
-	else
-	  mkdir_mode=
-	fi
-
-	posix_mkdir=false
-	case $umask in
-	  *[123567][0-7][0-7])
-	    # POSIX mkdir -p sets u+wx bits regardless of umask, which
-	    # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
-	    ;;
-	  *)
-	    tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
-	    trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
-	    if (umask $mkdir_umask &&
-		exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
-	    then
-	      if test -z "$dir_arg" || {
-		   # Check for POSIX incompatibilities with -m.
-		   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
-		   # other-writable bit of parent directory when it shouldn't.
-		   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
-		   ls_ld_tmpdir=`ls -ld "$tmpdir"`
-		   case $ls_ld_tmpdir in
-		     d????-?r-*) different_mode=700;;
-		     d????-?--*) different_mode=755;;
-		     *) false;;
-		   esac &&
-		   $mkdirprog -m$different_mode -p -- "$tmpdir" && {
-		     ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
-		     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
-		   }
-		 }
-	      then posix_mkdir=:
-	      fi
-	      rmdir "$tmpdir/d" "$tmpdir"
-	    else
-	      # Remove any dirs left behind by ancient mkdir implementations.
-	      rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
-	    fi
-	    trap '' 0;;
-	esac;;
-    esac
-
-    if
-      $posix_mkdir && (
-	umask $mkdir_umask &&
-	$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
-      )
-    then :
-    else
-
-      # The umask is ridiculous, or mkdir does not conform to POSIX,
-      # or it failed possibly due to a race condition.  Create the
-      # directory the slow way, step by step, checking for races as we go.
-
-      case $dstdir in
-	/*) prefix='/';;
-	[-=\(\)!]*) prefix='./';;
-	*)  prefix='';;
-      esac
-
-      eval "$initialize_posix_glob"
-
-      oIFS=$IFS
-      IFS=/
-      $posix_glob set -f
-      set fnord $dstdir
-      shift
-      $posix_glob set +f
-      IFS=$oIFS
-
-      prefixes=
-
-      for d
-      do
-	test X"$d" = X && continue
-
-	prefix=$prefix$d
-	if test -d "$prefix"; then
-	  prefixes=
-	else
-	  if $posix_mkdir; then
-	    (umask=$mkdir_umask &&
-	     $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
-	    # Don't fail if two instances are running concurrently.
-	    test -d "$prefix" || exit 1
-	  else
-	    case $prefix in
-	      *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
-	      *) qprefix=$prefix;;
-	    esac
-	    prefixes="$prefixes '$qprefix'"
-	  fi
-	fi
-	prefix=$prefix/
-      done
-
-      if test -n "$prefixes"; then
-	# Don't fail if two instances are running concurrently.
-	(umask $mkdir_umask &&
-	 eval "\$doit_exec \$mkdirprog $prefixes") ||
-	  test -d "$dstdir" || exit 1
-	obsolete_mkdir_used=true
-      fi
-    fi
-  fi
-
-  if test -n "$dir_arg"; then
-    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
-    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
-    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
-      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
-  else
-
-    # Make a couple of temp file names in the proper directory.
-    dsttmp=$dstdir/_inst.$$_
-    rmtmp=$dstdir/_rm.$$_
-
-    # Trap to clean up those temp files at exit.
-    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
-
-    # Copy the file name to the temp name.
-    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
-
-    # and set any options; do chmod last to preserve setuid bits.
-    #
-    # If any of these fail, we abort the whole thing.  If we want to
-    # ignore errors from any of these, just make sure not to ignore
-    # errors from the above "$doit $cpprog $src $dsttmp" command.
-    #
-    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
-    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
-    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
-    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
-
-    # If -C, don't bother to copy if it wouldn't change the file.
-    if $copy_on_change &&
-       old=`LC_ALL=C ls -dlL "$dst"	2>/dev/null` &&
-       new=`LC_ALL=C ls -dlL "$dsttmp"	2>/dev/null` &&
-
-       eval "$initialize_posix_glob" &&
-       $posix_glob set -f &&
-       set X $old && old=:$2:$4:$5:$6 &&
-       set X $new && new=:$2:$4:$5:$6 &&
-       $posix_glob set +f &&
-
-       test "$old" = "$new" &&
-       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
-    then
-      rm -f "$dsttmp"
-    else
-      # Rename the file to the real destination.
-      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
-
-      # The rename failed, perhaps because mv can't rename something else
-      # to itself, or perhaps because mv is so ancient that it does not
-      # support -f.
-      {
-	# Now remove or move aside any old file at destination location.
-	# We try this two ways since rm can't unlink itself on some
-	# systems and the destination file might be busy for other
-	# reasons.  In this case, the final cleanup might fail but the new
-	# file should still install successfully.
-	{
-	  test ! -f "$dst" ||
-	  $doit $rmcmd -f "$dst" 2>/dev/null ||
-	  { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
-	    { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
-	  } ||
-	  { echo "$0: cannot unlink or rename $dst" >&2
-	    (exit 1); exit 1
-	  }
-	} &&
-
-	# Now rename the file to the real destination.
-	$doit $mvcmd "$dsttmp" "$dst"
-      }
-    fi || exit 1
-
-    trap '' 0
-  fi
-done
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
-# time-stamp-end: "; # UTC"
-# End:
diff --git a/third_party/nix/contrib/stack-collapse.py b/third_party/nix/contrib/stack-collapse.py
deleted file mode 100755
index f5602c95c4..0000000000
--- a/third_party/nix/contrib/stack-collapse.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env nix-shell
-#!nix-shell -i python3 -p python3 --pure
-
-# To be used with `--trace-function-calls` and `flamegraph.pl`.
-#
-# For example:
-#
-# nix-instantiate --trace-function-calls '<nixpkgs>' -A hello 2> nix-function-calls.trace
-# ./contrib/stack-collapse.py nix-function-calls.trace > nix-function-calls.folded
-# nix-shell -p flamegraph --run "flamegraph.pl nix-function-calls.folded > nix-function-calls.svg"
-
-import sys
-from pprint import pprint
-import fileinput
-
-stack = []
-timestack = []
-
-for line in fileinput.input():
-    components = line.strip().split(" ", 2)
-    if components[0] != "function-trace":
-        continue
-
-    direction = components[1]
-    components = components[2].rsplit(" ", 2)
-
-    loc = components[0]
-    _at = components[1]
-    time = int(components[2])
-
-    if direction == "entered":
-        stack.append(loc)
-        timestack.append(time)
-    elif direction == "exited":
-        dur = time - timestack.pop()
-        vst = ";".join(stack)
-        print(f"{vst} {dur}")
-        stack.pop()
diff --git a/third_party/nix/corepkgs/buildenv.nix b/third_party/nix/corepkgs/buildenv.nix
deleted file mode 100644
index 0bac4c44b4..0000000000
--- a/third_party/nix/corepkgs/buildenv.nix
+++ /dev/null
@@ -1,25 +0,0 @@
-{ derivations, manifest }:
-
-derivation {
-  name = "user-environment";
-  system = "builtin";
-  builder = "builtin:buildenv";
-
-  inherit manifest;
-
-  # !!! grmbl, need structured data for passing this in a clean way.
-  derivations =
-    map (d:
-      [ (d.meta.active or "true")
-        (d.meta.priority or 5)
-        (builtins.length d.outputs)
-      ] ++ map (output: builtins.getAttr output d) d.outputs)
-      derivations;
-
-  # Building user environments remotely just causes huge amounts of
-  # network traffic, so don't do that.
-  preferLocalBuild = true;
-
-  # Also don't bother substituting.
-  allowSubstitutes = false;
-}
diff --git a/third_party/nix/corepkgs/config.nix.in b/third_party/nix/corepkgs/config.nix.in
deleted file mode 100644
index 0e4a2f0c90..0000000000
--- a/third_party/nix/corepkgs/config.nix.in
+++ /dev/null
@@ -1,29 +0,0 @@
-let
-  fromEnv = var: def:
-    let val = builtins.getEnv var; in
-    if val != "" then val else def;
-in rec {
-  shell = "@bash@";
-  coreutils = "@coreutils@";
-  bzip2 = "@bzip2@";
-  gzip = "@gzip@";
-  xz = "@xz@";
-  tar = "@tar@";
-  tarFlags = "@tarFlags@";
-  tr = "@tr@";
-  nixBinDir = fromEnv "NIX_BIN_DIR" "@CMAKE_INSTALL_FULL_BINDIR@";
-  nixPrefix = "@CMAKE_INSTALL_PREFIX@";
-  nixLibexecDir = fromEnv "NIX_LIBEXEC_DIR" "@CMAKE_INSTALL_FULL_LIBEXECDIR@";
-  nixLocalstateDir = "/nix/var";
-  nixSysconfDir = "/etc";
-  nixStoreDir = fromEnv "NIX_STORE_DIR" "/nix/store";
-
-  # If Nix is installed in the Nix store, then automatically add it as
-  # a dependency to the core packages. This ensures that they work
-  # properly in a chroot.
-  chrootDeps =
-    if dirOf nixPrefix == builtins.storeDir then
-      [ (builtins.storePath nixPrefix) ]
-    else
-      [ ];
-}
diff --git a/third_party/nix/corepkgs/derivation.nix b/third_party/nix/corepkgs/derivation.nix
deleted file mode 100644
index c0fbe8082c..0000000000
--- a/third_party/nix/corepkgs/derivation.nix
+++ /dev/null
@@ -1,27 +0,0 @@
-/* This is the implementation of the β€˜derivation’ builtin function.
-   It's actually a wrapper around the β€˜derivationStrict’ primop. */
-
-drvAttrs @ { outputs ? [ "out" ], ... }:
-
-let
-
-  strict = derivationStrict drvAttrs;
-
-  commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) //
-    { all = map (x: x.value) outputsList;
-      inherit drvAttrs;
-    };
-
-  outputToAttrListElement = outputName:
-    { name = outputName;
-      value = commonAttrs // {
-        outPath = builtins.getAttr outputName strict;
-        drvPath = strict.drvPath;
-        type = "derivation";
-        inherit outputName;
-      };
-    };
-
-  outputsList = map outputToAttrListElement outputs;
-
-in (builtins.head outputsList).value
diff --git a/third_party/nix/corepkgs/fetchurl.nix b/third_party/nix/corepkgs/fetchurl.nix
deleted file mode 100644
index a84777f574..0000000000
--- a/third_party/nix/corepkgs/fetchurl.nix
+++ /dev/null
@@ -1,41 +0,0 @@
-{ system ? "" # obsolete
-, url
-, hash ? "" # an SRI ash
-
-# Legacy hash specification
-, md5 ? "", sha1 ? "", sha256 ? "", sha512 ? ""
-, outputHash ?
-    if hash != "" then hash else if sha512 != "" then sha512 else if sha1 != "" then sha1 else if md5 != "" then md5 else sha256
-, outputHashAlgo ?
-    if hash != "" then "" else if sha512 != "" then "sha512" else if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256"
-
-, executable ? false
-, unpack ? false
-, name ? baseNameOf (toString url)
-}:
-
-derivation {
-  builder = "builtin:fetchurl";
-
-  # New-style output content requirements.
-  inherit outputHashAlgo outputHash;
-  outputHashMode = if unpack || executable then "recursive" else "flat";
-
-  inherit name url executable unpack;
-
-  system = "builtin";
-
-  # No need to double the amount of network traffic
-  preferLocalBuild = true;
-
-  impureEnvVars = [
-    # We borrow these environment variables from the caller to allow
-    # easy proxy configuration.  This is impure, but a fixed-output
-    # derivation like fetchurl is allowed to do so since its result is
-    # by definition pure.
-    "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy"
-  ];
-
-  # To make "nix-prefetch-url" work.
-  urls = [ url ];
-}
diff --git a/third_party/nix/corepkgs/imported-drv-to-derivation.nix b/third_party/nix/corepkgs/imported-drv-to-derivation.nix
deleted file mode 100644
index eab8b050e8..0000000000
--- a/third_party/nix/corepkgs/imported-drv-to-derivation.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-attrs @ { drvPath, outputs, name, ... }:
-
-let
-
-  commonAttrs = (builtins.listToAttrs outputsList) //
-    { all = map (x: x.value) outputsList;
-      inherit drvPath name;
-      type = "derivation";
-    };
-
-  outputToAttrListElement = outputName:
-    { name = outputName;
-      value = commonAttrs // {
-        outPath = builtins.getAttr outputName attrs;
-        inherit outputName;
-      };
-    };
-    
-  outputsList = map outputToAttrListElement outputs;
-    
-in (builtins.head outputsList).value
diff --git a/third_party/nix/corepkgs/unpack-channel.nix b/third_party/nix/corepkgs/unpack-channel.nix
deleted file mode 100644
index d39a206378..0000000000
--- a/third_party/nix/corepkgs/unpack-channel.nix
+++ /dev/null
@@ -1,39 +0,0 @@
-with import <nix/config.nix>;
-
-let
-
-  builder = builtins.toFile "unpack-channel.sh"
-    ''
-      mkdir $out
-      cd $out
-      xzpat="\.xz\$"
-      gzpat="\.gz\$"
-      if [[ "$src" =~ $xzpat ]]; then
-        ${xz} -d < $src | ${tar} xf - ${tarFlags}
-      elif [[ "$src" =~ $gzpat ]]; then
-        ${gzip} -d < $src | ${tar} xf - ${tarFlags}
-      else
-        ${bzip2} -d < $src | ${tar} xf - ${tarFlags}
-      fi
-      if [ * != $channelName ]; then
-        mv * $out/$channelName
-      fi
-    '';
-
-in
-
-{ name, channelName, src }:
-
-derivation {
-  system = builtins.currentSystem;
-  builder = shell;
-  args = [ "-e" builder ];
-  inherit name channelName src;
-
-  PATH = "${nixBinDir}:${coreutils}";
-
-  # No point in doing this remotely.
-  preferLocalBuild = true;
-
-  inherit chrootDeps;
-}
diff --git a/third_party/nix/default.nix b/third_party/nix/default.nix
deleted file mode 100644
index e01fad12f0..0000000000
--- a/third_party/nix/default.nix
+++ /dev/null
@@ -1,237 +0,0 @@
-args@{
-  depot ? (import ../.. {})
-, pkgs ? depot.third_party.nixpkgs
-, lib
-, buildType ? "release"
-, ...
-}:
-
-let
-  aws-s3-cpp = pkgs.aws-sdk-cpp.override {
-    apis = ["s3" "transfer"];
-    customMemoryManagement = false;
-  };
-
-  src = let
-    srcDir = ./.;
-    # create relative paths for all the sources we are filtering
-    asRelative = path:
-      let
-        srcS = toString srcDir;
-        pathS = toString path;
-      in
-        if ! lib.hasPrefix srcS pathS then
-          throw "Path is outside of the working directory."
-        else
-        lib.removePrefix srcS pathS;
-
-  in builtins.filterSource (path: type:
-    # Strip out .nix files that are in the root of the repository.  Changing
-    # the expression of tvix shouldn't cause a rebuild of tvix unless really
-    # required.
-    !(dirOf (asRelative path) == "/" && lib.hasSuffix ".nix" path) &&
-
-    # remove the proto files from the repo as those are compiled separately
-    !(lib.hasPrefix "src/proto" (asRelative path)) &&
-
-    # ignore result symlinks
-    !(type == "symlink" && lib.hasPrefix "result" (baseNameOf path))
-  ) srcDir;
-
-  # Proto generation in CMake is theoretically possible, but that is
-  # very theoretical - this does it in Nix instead.
-  protoSrcs = pkgs.runCommand "nix-proto-srcs" {} ''
-    export PROTO_SRCS=${./src/proto}
-    mkdir -p $out/libproto
-    ${depot.third_party.protobuf}/bin/protoc -I=$PROTO_SRCS \
-      --cpp_out=$out/libproto \
-      --plugin=protoc-gen-grpc=${depot.third_party.grpc}/bin/grpc_cpp_plugin \
-        --grpc_out=$out/libproto \
-        $PROTO_SRCS/*.proto
-  '';
-
-  # Derivation for busybox that just has the `busybox` binary in bin/, not all
-  # the symlinks, so cmake can find it
-  busybox = pkgs.runCommand "busybox" {} ''
-    mkdir -p $out/bin
-    cp ${pkgs.busybox}/bin/busybox $out/bin
-  '';
-
-in lib.fix (self: pkgs.fullLlvm11Stdenv.mkDerivation {
-  pname = "tvix";
-  version = "2.3.4";
-  inherit src;
-
-  nativeBuildInputs = with pkgs; [
-    bison
-    clang-tools_11
-    cmake
-    libxml2
-    libxslt
-    pkgconfig
-    (import ./clangd.nix pkgs)
-  ];
-
-  # TODO(tazjin): Some of these might only be required for native inputs
-  buildInputs = (with pkgs; [
-    aws-s3-cpp
-    brotli
-    bzip2
-    c-ares
-    curl
-    editline
-    flex
-    libseccomp
-    libsodium
-    systemd.dev
-    openssl
-    sqlite
-    xz
-  ]) ++ (with depot.third_party; [
-    abseil_cpp
-    glog
-    grpc
-    protobuf
-  ]);
-
-  doCheck = false;
-  doInstallCheck = true;
-
-  # Preserve debug symbols, for core dumps + other live debugging
-  dontStrip = true;
-
-  installCheckInputs = with depot.third_party; [
-    gtest
-    pkgs.fd
-    rapidcheck
-  ];
-
-  propagatedBuildInputs = with pkgs; [
-    boost
-  ];
-
-  configurePhase = ''
-    mkdir build
-    cd build
-    cmake .. \
-      -DCMAKE_INSTALL_PREFIX=$out \
-      -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-      -DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF \
-      -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF \
-      -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON
-  '';
-
-  installCheckPhase = ''
-    export NIX_DATA_DIR=$out/share
-    export NIX_TEST_VAR=foo # this is required by a language test
-    make test
-  '';
-
-  preBuild = ''
-    if [ -n "$NIX_BUILD_CORES" ]; then
-      makeFlags+="-j$NIX_BUILD_CORES "
-      makeFlags+="-l$NIX_BUILD_CORES "
-    fi
-  '';
-
-  # Forward the location of the generated Protobuf / gRPC files so
-  # that they can be included by CMake.
-  NIX_PROTO_SRCS = protoSrcs;
-
-  # Work around broken system header include flags in the cxx toolchain.
-  LIBCXX_INCLUDE = "${pkgs.llvmPackages_11.libcxx}/include/c++/v1";
-
-  SANDBOX_SHELL="${pkgs.busybox}/bin/busybox";
-
-  # Install the various symlinks to the Nix binary which users expect
-  # to exist.
-  postInstall = ''
-    ln -s $out/bin/nix $out/bin/nix-build
-    ln -s $out/bin/nix $out/bin/nix-channel
-    ln -s $out/bin/nix $out/bin/nix-collect-garbage
-    ln -s $out/bin/nix $out/bin/nix-copy-closure
-    ln -s $out/bin/nix $out/bin/nix-env
-    ln -s $out/bin/nix $out/bin/nix-hash
-    ln -s $out/bin/nix $out/bin/nix-instantiate
-    ln -s $out/bin/nix $out/bin/nix-prefetch-url
-    ln -s $out/bin/nix $out/bin/nix-shell
-    ln -s $out/bin/nix $out/bin/nix-store
-
-    mkdir -p $out/libexec/nix
-    ln -s $out/bin/nix $out/libexec/nix/build-remote
-
-    # configuration variables for templated files
-    export storedir=/nix/store
-    export localstatedir=/nix/var
-    export bindir=$out/bin
-
-    mkdir -p $out/lib/systemd/system
-    substituteAll \
-      ${src}/misc/systemd/nix-daemon.service.in \
-      $out/lib/systemd/system/nix-daemon.service
-    substituteAll \
-      ${src}/misc/systemd/nix-daemon.socket.in \
-      $out/lib/systemd/system/nix-daemon.socket
-
-    mkdir -p $out/etc/profile.d
-    substituteAll \
-      ${src}/scripts/nix-profile.sh.in $out/etc/profile.d/nix.sh
-    substituteAll \
-      ${src}/scripts/nix-profile-daemon.sh.in $out/etc/profile.d/nix-daemon.sh
-  '';
-
-  # TODO(tazjin): integration test setup?
-  # TODO(tazjin): docs generation?
-
-  passthru = {
-    build-shell = self.overrideAttrs (up: rec {
-      run_clang_tidy = pkgs.writeShellScriptBin "run-clang-tidy" ''
-        test -f compile_commands.json || (echo "run from build output directory"; exit 1) || exit 1
-        ${pkgs.jq}/bin/jq < compile_commands.json -r 'map(.file)|.[]' | grep -v '/generated/' | ${pkgs.parallel}/bin/parallel ${pkgs.clang-tools}/bin/clang-tidy -p compile_commands.json $@
-      '';
-
-      installCheckInputs = up.installCheckInputs ++ [run_clang_tidy];
-
-      shellHook = ''
-        export NIX_DATA_DIR="${toString depot.path}/third_party"
-        export NIX_TEST_VAR=foo
-      '';
-    });
-
-    # Ensure formatting is coherent,
-    # but do this in parallel to the main build because:
-    #  - (in favor of building this after tvix)
-    #    tests run so that developers get all the useful feedback
-    #  - (in favor of building this before tvix)
-    #    if the formatting is broken, and this build was submitted to CI
-    #    it would be a good idea to get this feedback rather sooner than later
-    #  - we don't want builds to differ between local and CI runs
-    checkfmt = pkgs.fullLlvm11Stdenv.mkDerivation {
-      name = "tvix-checkfmt";
-      inherit src;
-      nativeBuildInputs = with pkgs; [ clang-tools_11 fd ];
-      SANDBOX_SHELL = "${pkgs.busybox}/bin/busybox";
-
-      buildPhase = ''
-        set -e
-        runHook preBuild
-        fd . $src -e hh -e cc | xargs clang-format --dry-run --Werror
-        runHook postBuild
-      '';
-
-      installPhase = ''
-        runHook preInstall
-        touch $out
-        runHook postInstall
-      '';
-    };
-
-    test-vm = import ./test-vm.nix args;
-  };
-
-  meta = {
-    targets = [
-      "checkfmt"
-    ];
-  };
-})
diff --git a/third_party/nix/doc/manual/advanced-topics/advanced-topics.xml b/third_party/nix/doc/manual/advanced-topics/advanced-topics.xml
deleted file mode 100644
index 871b7eb1d3..0000000000
--- a/third_party/nix/doc/manual/advanced-topics/advanced-topics.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<part xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      xml:id="part-advanced-topics"
-      version="5.0">
-
-<title>Advanced Topics</title>
-
-<xi:include href="distributed-builds.xml" />
-<xi:include href="cores-vs-jobs.xml" />
-<xi:include href="diff-hook.xml" />
-<xi:include href="post-build-hook.xml" />
-
-</part>
diff --git a/third_party/nix/doc/manual/advanced-topics/cores-vs-jobs.xml b/third_party/nix/doc/manual/advanced-topics/cores-vs-jobs.xml
deleted file mode 100644
index eba645faf8..0000000000
--- a/third_party/nix/doc/manual/advanced-topics/cores-vs-jobs.xml
+++ /dev/null
@@ -1,121 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="chap-tuning-cores-and-jobs">
-
-<title>Tuning Cores and Jobs</title>
-
-<para>Nix has two relevant settings with regards to how your CPU cores
-will be utilized: <xref linkend="conf-cores" /> and
-<xref linkend="conf-max-jobs" />. This chapter will talk about what
-they are, how they interact, and their configuration trade-offs.</para>
-
-<variablelist>
-  <varlistentry>
-    <term><xref linkend="conf-max-jobs" /></term>
-    <listitem><para>
-      Dictates how many separate derivations will be built at the same
-      time. If you set this to zero, the local machine will do no
-      builds. Nix will still substitute from binary caches, and build
-      remotely if remote builders are configured.
-    </para></listitem>
-  </varlistentry>
-  <varlistentry>
-    <term><xref linkend="conf-cores" /></term>
-    <listitem><para>
-      Suggests how many cores each derivation should use. Similar to
-      <command>make -j</command>.
-    </para></listitem>
-  </varlistentry>
-</variablelist>
-
-<para>The <xref linkend="conf-cores" /> setting determines the value of
-<envar>NIX_BUILD_CORES</envar>. <envar>NIX_BUILD_CORES</envar> is equal
-to <xref linkend="conf-cores" />, unless <xref linkend="conf-cores" />
-equals <literal>0</literal>, in which case <envar>NIX_BUILD_CORES</envar>
-will be the total number of cores in the system.</para>
-
-<para>The total number of consumed cores is a simple multiplication,
-<xref linkend="conf-cores" /> * <envar>NIX_BUILD_CORES</envar>.</para>
-
-<para>The balance on how to set these two independent variables depends
-upon each builder's workload and hardware. Here are a few example
-scenarios on a machine with 24 cores:</para>
-
-<table>
-  <caption>Balancing 24 Build Cores</caption>
-  <thead>
-    <tr>
-      <th><xref linkend="conf-max-jobs" /></th>
-      <th><xref linkend="conf-cores" /></th>
-      <th><envar>NIX_BUILD_CORES</envar></th>
-      <th>Maximum Processes</th>
-      <th>Result</th>
-    </tr>
-  </thead>
-  <tbody>
-    <tr>
-      <td>1</td>
-      <td>24</td>
-      <td>24</td>
-      <td>24</td>
-      <td>
-        One derivation will be built at a time, each one can use 24
-        cores. Undersold if a job can’t use 24 cores.
-      </td>
-    </tr>
-
-    <tr>
-      <td>4</td>
-      <td>6</td>
-      <td>6</td>
-      <td>24</td>
-      <td>
-        Four derivations will be built at once, each given access to
-        six cores.
-      </td>
-    </tr>
-    <tr>
-      <td>12</td>
-      <td>6</td>
-      <td>6</td>
-      <td>72</td>
-      <td>
-        12 derivations will be built at once, each given access to six
-        cores. This configuration is over-sold. If all 12 derivations
-        being built simultaneously try to use all six cores, the
-        machine's performance will be degraded due to extensive context
-        switching between the 12 builds.
-      </td>
-    </tr>
-    <tr>
-      <td>24</td>
-      <td>1</td>
-      <td>1</td>
-      <td>24</td>
-      <td>
-        24 derivations can build at the same time, each using a single
-        core. Never oversold, but derivations which require many cores
-        will be very slow to compile.
-      </td>
-    </tr>
-    <tr>
-      <td>24</td>
-      <td>0</td>
-      <td>24</td>
-      <td>576</td>
-      <td>
-        24 derivations can build at the same time, each using all the
-        available cores of the machine. Very likely to be oversold,
-        and very likely to suffer context switches.
-      </td>
-    </tr>
-  </tbody>
-</table>
-
-<para>It is up to the derivations' build script to respect
-host's requested cores-per-build by following the value of the
-<envar>NIX_BUILD_CORES</envar> environment variable.</para>
-
-</chapter>
diff --git a/third_party/nix/doc/manual/advanced-topics/diff-hook.xml b/third_party/nix/doc/manual/advanced-topics/diff-hook.xml
deleted file mode 100644
index fb4bf819f9..0000000000
--- a/third_party/nix/doc/manual/advanced-topics/diff-hook.xml
+++ /dev/null
@@ -1,205 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      xml:id="chap-diff-hook"
-      version="5.0"
-      >
-
-<title>Verifying Build Reproducibility with <option linkend="conf-diff-hook">diff-hook</option></title>
-
-<subtitle>Check build reproducibility by running builds multiple times
-and comparing their results.</subtitle>
-
-<para>Specify a program with Nix's <xref linkend="conf-diff-hook" /> to
-compare build results when two builds produce different results. Note:
-this hook is only executed if the results are not the same, this hook
-is not used for determining if the results are the same.</para>
-
-<para>For purposes of demonstration, we'll use the following Nix file,
-<filename>deterministic.nix</filename> for testing:</para>
-
-<programlisting>
-let
-  inherit (import &lt;nixpkgs&gt; {}) runCommand;
-in {
-  stable = runCommand "stable" {} ''
-    touch $out
-  '';
-
-  unstable = runCommand "unstable" {} ''
-    echo $RANDOM > $out
-  '';
-}
-</programlisting>
-
-<para>Additionally, <filename>nix.conf</filename> contains:
-
-<programlisting>
-diff-hook = /etc/nix/my-diff-hook
-run-diff-hook = true
-</programlisting>
-
-where <filename>/etc/nix/my-diff-hook</filename> is an executable
-file containing:
-
-<programlisting>
-#!/bin/sh
-exec &gt;&amp;2
-echo "For derivation $3:"
-/run/current-system/sw/bin/diff -r "$1" "$2"
-</programlisting>
-
-</para>
-
-<para>The diff hook is executed by the same user and group who ran the
-build. However, the diff hook does not have write access to the store
-path just built.</para>
-
-<section>
-  <title>
-    Spot-Checking Build Determinism
-  </title>
-
-  <para>
-    Verify a path which already exists in the Nix store by passing
-    <option>--check</option> to the build command.
-  </para>
-
-  <para>If the build passes and is deterministic, Nix will exit with a
-  status code of 0:</para>
-
-  <screen>
-$ nix-build ./deterministic.nix -A stable
-these derivations will be built:
-  /nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv
-building '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
-/nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable
-
-$ nix-build ./deterministic.nix -A stable --check
-checking outputs of '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
-/nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable
-</screen>
-
-  <para>If the build is not deterministic, Nix will exit with a status
-  code of 1:</para>
-
-  <screen>
-$ nix-build ./deterministic.nix -A unstable
-these derivations will be built:
-  /nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv
-building '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
-/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable
-
-$ nix-build ./deterministic.nix -A unstable --check
-checking outputs of '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
-error: derivation '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv' may not be deterministic: output '/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable' differs
-</screen>
-
-<para>In the Nix daemon's log, we will now see:
-<screen>
-For derivation /nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv:
-1c1
-&lt; 8108
----
-&gt; 30204
-</screen>
-</para>
-
-  <para>Using <option>--check</option> with <option>--keep-failed</option>
-  will cause Nix to keep the second build's output in a special,
-  <literal>.check</literal> path:</para>
-
-  <screen>
-$ nix-build ./deterministic.nix -A unstable --check --keep-failed
-checking outputs of '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
-note: keeping build directory '/tmp/nix-build-unstable.drv-0'
-error: derivation '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv' may not be deterministic: output '/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable' differs from '/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable.check'
-</screen>
-
-  <para>In particular, notice the
-  <literal>/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable.check</literal>
-  output. Nix has copied the build results to that directory where you
-  can examine it.</para>
-
-  <note xml:id="check-dirs-are-unregistered">
-    <title><literal>.check</literal> paths are not registered store paths</title>
-
-    <para>Check paths are not protected against garbage collection,
-    and this path will be deleted on the next garbage collection.</para>
-
-    <para>The path is guaranteed to be alive for the duration of
-    <xref linkend="conf-diff-hook" />'s execution, but may be deleted
-    any time after.</para>
-
-    <para>If the comparison is performed as part of automated tooling,
-    please use the diff-hook or author your tooling to handle the case
-    where the build was not deterministic and also a check path does
-    not exist.</para>
-  </note>
-
-  <para>
-    <option>--check</option> is only usable if the derivation has
-    been built on the system already. If the derivation has not been
-    built Nix will fail with the error:
-    <screen>
-error: some outputs of '/nix/store/hzi1h60z2qf0nb85iwnpvrai3j2w7rr6-unstable.drv' are not valid, so checking is not possible
-</screen>
-
-    Run the build without <option>--check</option>, and then try with
-    <option>--check</option> again.
-  </para>
-</section>
-
-<section>
-  <title>
-    Automatic and Optionally Enforced Determinism Verification
-  </title>
-
-  <para>
-    Automatically verify every build at build time by executing the
-    build multiple times.
-  </para>
-
-  <para>
-    Setting <xref linkend="conf-repeat" /> and
-    <xref linkend="conf-enforce-determinism" /> in your
-    <filename>nix.conf</filename> permits the automated verification
-    of every build Nix performs.
-  </para>
-
-  <para>
-    The following configuration will run each build three times, and
-    will require the build to be deterministic:
-
-    <programlisting>
-enforce-determinism = true
-repeat = 2
-</programlisting>
-  </para>
-
-  <para>
-    Setting <xref linkend="conf-enforce-determinism" /> to false as in
-    the following configuration will run the build multiple times,
-    execute the build hook, but will allow the build to succeed even
-    if it does not build reproducibly:
-
-    <programlisting>
-enforce-determinism = false
-repeat = 1
-</programlisting>
-  </para>
-
-  <para>
-    An example output of this configuration:
-    <screen>
-$ nix-build ./test.nix -A unstable
-these derivations will be built:
-  /nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv
-building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 1/2)...
-building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 2/2)...
-output '/nix/store/6xg356v9gl03hpbbg8gws77n19qanh02-unstable' of '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' differs from '/nix/store/6xg356v9gl03hpbbg8gws77n19qanh02-unstable.check' from previous round
-/nix/store/6xg356v9gl03hpbbg8gws77n19qanh02-unstable
-</screen>
-  </para>
-</section>
-</chapter>
diff --git a/third_party/nix/doc/manual/advanced-topics/distributed-builds.xml b/third_party/nix/doc/manual/advanced-topics/distributed-builds.xml
deleted file mode 100644
index 9ac4a92cd5..0000000000
--- a/third_party/nix/doc/manual/advanced-topics/distributed-builds.xml
+++ /dev/null
@@ -1,190 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='chap-distributed-builds'>
-
-<title>Remote Builds</title>
-
-<para>Nix supports remote builds, where a local Nix installation can
-forward Nix builds to other machines.  This allows multiple builds to
-be performed in parallel and allows Nix to perform multi-platform
-builds in a semi-transparent way.  For instance, if you perform a
-build for a <literal>x86_64-darwin</literal> on an
-<literal>i686-linux</literal> machine, Nix can automatically forward
-the build to a <literal>x86_64-darwin</literal> machine, if
-available.</para>
-
-<para>To forward a build to a remote machine, it’s required that the
-remote machine is accessible via SSH and that it has Nix
-installed. You can test whether connecting to the remote Nix instance
-works, e.g.
-
-<screen>
-$ nix ping-store --store ssh://mac
-</screen>
-
-will try to connect to the machine named <literal>mac</literal>. It is
-possible to specify an SSH identity file as part of the remote store
-URI, e.g.
-
-<screen>
-$ nix ping-store --store ssh://mac?ssh-key=/home/alice/my-key
-</screen>
-
-Since builds should be non-interactive, the key should not have a
-passphrase. Alternatively, you can load identities ahead of time into
-<command>ssh-agent</command> or <command>gpg-agent</command>.</para>
-
-<para>If you get the error
-
-<screen>
-bash: nix-store: command not found
-error: cannot connect to 'mac'
-</screen>
-
-then you need to ensure that the <envar>PATH</envar> of
-non-interactive login shells contains Nix.</para>
-
-<warning><para>If you are building via the Nix daemon, it is the Nix
-daemon user account (that is, <literal>root</literal>) that should
-have SSH access to the remote machine. If you can’t or don’t want to
-configure <literal>root</literal> to be able to access to remote
-machine, you can use a private Nix store instead by passing
-e.g. <literal>--store ~/my-nix</literal>.</para></warning>
-
-<para>The list of remote machines can be specified on the command line
-or in the Nix configuration file. The former is convenient for
-testing. For example, the following command allows you to build a
-derivation for <literal>x86_64-darwin</literal> on a Linux machine:
-
-<screen>
-$ uname
-Linux
-
-$ nix build \
-  '(with import &lt;nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out")' \
-  --builders 'ssh://mac x86_64-darwin'
-[1/0/1 built, 0.0 MiB DL] building foo on ssh://mac
-
-$ cat ./result
-Darwin
-</screen>
-
-It is possible to specify multiple builders separated by a semicolon
-or a newline, e.g.
-
-<screen>
-  --builders 'ssh://mac x86_64-darwin ; ssh://beastie x86_64-freebsd'
-</screen>
-</para>
-
-<para>Each machine specification consists of the following elements,
-separated by spaces. Only the first element is required.
-To leave a field at its default, set it to <literal>-</literal>.
-
-<orderedlist>
-
-  <listitem><para>The URI of the remote store in the format
-  <literal>ssh://[<replaceable>username</replaceable>@]<replaceable>hostname</replaceable></literal>,
-  e.g. <literal>ssh://nix@mac</literal> or
-  <literal>ssh://mac</literal>. For backward compatibility,
-  <literal>ssh://</literal> may be omitted. The hostname may be an
-  alias defined in your
-  <filename>~/.ssh/config</filename>.</para></listitem>
-
-  <listitem><para>A comma-separated list of Nix platform type
-  identifiers, such as <literal>x86_64-darwin</literal>.  It is
-  possible for a machine to support multiple platform types, e.g.,
-  <literal>i686-linux,x86_64-linux</literal>. If omitted, this
-  defaults to the local platform type.</para></listitem>
-
-  <listitem><para>The SSH identity file to be used to log in to the
-  remote machine. If omitted, SSH will use its regular
-  identities.</para></listitem>
-
-  <listitem><para>The maximum number of builds that Nix will execute
-  in parallel on the machine.  Typically this should be equal to the
-  number of CPU cores.  For instance, the machine
-  <literal>itchy</literal> in the example will execute up to 8 builds
-  in parallel.</para></listitem>
-
-  <listitem><para>The β€œspeed factor”, indicating the relative speed of
-  the machine.  If there are multiple machines of the right type, Nix
-  will prefer the fastest, taking load into account.</para></listitem>
-
-  <listitem><para>A comma-separated list of <emphasis>supported
-  features</emphasis>.  If a derivation has the
-  <varname>requiredSystemFeatures</varname> attribute, then Nix will
-  only perform the derivation on a machine that has the specified
-  features.  For instance, the attribute
-
-<programlisting>
-requiredSystemFeatures = [ "kvm" ];
-</programlisting>
-
-  will cause the build to be performed on a machine that has the
-  <literal>kvm</literal> feature.</para></listitem>
-
-  <listitem><para>A comma-separated list of <emphasis>mandatory
-  features</emphasis>.  A machine will only be used to build a
-  derivation if all of the machine’s mandatory features appear in the
-  derivation’s <varname>requiredSystemFeatures</varname>
-  attribute..</para></listitem>
-
-</orderedlist>
-
-For example, the machine specification
-
-<programlisting>
-nix@scratchy.labs.cs.uu.nl  i686-linux      /home/nix/.ssh/id_scratchy_auto        8 1 kvm
-nix@itchy.labs.cs.uu.nl     i686-linux      /home/nix/.ssh/id_scratchy_auto        8 2
-nix@poochie.labs.cs.uu.nl   i686-linux      /home/nix/.ssh/id_scratchy_auto        1 2 kvm benchmark
-</programlisting>
-
-specifies several machines that can perform
-<literal>i686-linux</literal> builds. However,
-<literal>poochie</literal> will only do builds that have the attribute
-
-<programlisting>
-requiredSystemFeatures = [ "benchmark" ];
-</programlisting>
-
-or
-
-<programlisting>
-requiredSystemFeatures = [ "benchmark" "kvm" ];
-</programlisting>
-
-<literal>itchy</literal> cannot do builds that require
-<literal>kvm</literal>, but <literal>scratchy</literal> does support
-such builds. For regular builds, <literal>itchy</literal> will be
-preferred over <literal>scratchy</literal> because it has a higher
-speed factor.</para>
-
-<para>Remote builders can also be configured in
-<filename>nix.conf</filename>, e.g.
-
-<programlisting>
-builders = ssh://mac x86_64-darwin ; ssh://beastie x86_64-freebsd
-</programlisting>
-
-Finally, remote builders can be configured in a separate configuration
-file included in <option>builders</option> via the syntax
-<literal>@<replaceable>file</replaceable></literal>. For example,
-
-<programlisting>
-builders = @/etc/nix/machines
-</programlisting>
-
-causes the list of machines in <filename>/etc/nix/machines</filename>
-to be included. (This is the default.)</para>
-
-<para>If you want the builders to use caches, you likely want to set
-the option <link linkend='conf-builders-use-substitutes'><literal>builders-use-substitutes</literal></link>
-in your local <filename>nix.conf</filename>.</para>
-
-<para>To build only on remote builders and disable building on the local machine,
-you can use the option <option>--max-jobs 0</option>.</para>
-
-</chapter>
diff --git a/third_party/nix/doc/manual/advanced-topics/post-build-hook.xml b/third_party/nix/doc/manual/advanced-topics/post-build-hook.xml
deleted file mode 100644
index 3dc43ee795..0000000000
--- a/third_party/nix/doc/manual/advanced-topics/post-build-hook.xml
+++ /dev/null
@@ -1,160 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      xml:id="chap-post-build-hook"
-      version="5.0"
-      >
-
-<title>Using the <xref linkend="conf-post-build-hook" /></title>
-<subtitle>Uploading to an S3-compatible binary cache after each build</subtitle>
-
-
-<section xml:id="chap-post-build-hook-caveats">
-  <title>Implementation Caveats</title>
-  <para>Here we use the post-build hook to upload to a binary cache.
-  This is a simple and working example, but it is not suitable for all
-  use cases.</para>
-
-  <para>The post build hook program runs after each executed build,
-  and blocks the build loop. The build loop exits if the hook program
-  fails.</para>
-
-  <para>Concretely, this implementation will make Nix slow or unusable
-  when the internet is slow or unreliable.</para>
-
-  <para>A more advanced implementation might pass the store paths to a
-  user-supplied daemon or queue for processing the store paths outside
-  of the build loop.</para>
-</section>
-
-<section>
-  <title>Prerequisites</title>
-
-  <para>
-    This tutorial assumes you have configured an S3-compatible binary cache
-    according to the instructions at
-    <xref linkend="ssec-s3-substituter-authenticated-writes" />, and
-    that the <literal>root</literal> user's default AWS profile can
-    upload to the bucket.
-  </para>
-</section>
-
-<section>
-  <title>Set up a Signing Key</title>
-  <para>Use <command>nix-store --generate-binary-cache-key</command> to
-  create our public and private signing keys. We will sign paths
-  with the private key, and distribute the public key for verifying
-  the authenticity of the paths.</para>
-
-  <screen>
-# nix-store --generate-binary-cache-key example-nix-cache-1 /etc/nix/key.private /etc/nix/key.public
-# cat /etc/nix/key.public
-example-nix-cache-1:1/cKDz3QCCOmwcztD2eV6Coggp6rqc9DGjWv7C0G+rM=
-</screen>
-
-<para>Then, add the public key and the cache URL to your
-<filename>nix.conf</filename>'s <xref linkend="conf-trusted-public-keys" />
-and <xref linkend="conf-substituters" /> like:</para>
-
-<programlisting>
-substituters = https://cache.nixos.org/ s3://example-nix-cache
-trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= example-nix-cache-1:1/cKDz3QCCOmwcztD2eV6Coggp6rqc9DGjWv7C0G+rM=
-</programlisting>
-
-<para>we will restart the Nix daemon a later step.</para>
-</section>
-
-<section>
-  <title>Implementing the build hook</title>
-  <para>Write the following script to
-  <filename>/etc/nix/upload-to-cache.sh</filename>:
-  </para>
-
-  <programlisting>
-#!/bin/sh
-
-set -eu
-set -f # disable globbing
-export IFS=' '
-
-echo "Signing paths" $OUT_PATHS
-nix sign-paths --key-file /etc/nix/key.private $OUT_PATHS
-echo "Uploading paths" $OUT_PATHS
-exec nix copy --to 's3://example-nix-cache' $OUT_PATHS
-</programlisting>
-
-  <note>
-    <title>Should <literal>$OUT_PATHS</literal> be quoted?</title>
-    <para>
-      The <literal>$OUT_PATHS</literal> variable is a space-separated
-      list of Nix store paths. In this case, we expect and want the
-      shell to perform word splitting to make each output path its
-      own argument to <command>nix sign-paths</command>. Nix guarantees
-      the paths will not contain any spaces, however a store path
-      might contain glob characters. The <command>set -f</command>
-      disables globbing in the shell.
-    </para>
-  </note>
-  <para>
-    Then make sure the hook program is executable by the <literal>root</literal> user:
-    <screen>
-# chmod +x /etc/nix/upload-to-cache.sh
-</screen></para>
-</section>
-
-<section>
-  <title>Updating Nix Configuration</title>
-
-  <para>Edit <filename>/etc/nix/nix.conf</filename> to run our hook,
-  by adding the following configuration snippet at the end:</para>
-
-  <programlisting>
-post-build-hook = /etc/nix/upload-to-cache.sh
-</programlisting>
-
-<para>Then, restart the <command>nix-daemon</command>.</para>
-</section>
-
-<section>
-  <title>Testing</title>
-
-  <para>Build any derivation, for example:</para>
-
-  <screen>
-$ nix-build -E '(import &lt;nixpkgs&gt; {}).writeText "example" (builtins.toString builtins.currentTime)'
-these derivations will be built:
-  /nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv
-building '/nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv'...
-running post-build-hook '/home/grahamc/projects/github.com/NixOS/nix/post-hook.sh'...
-post-build-hook: Signing paths /nix/store/ibcyipq5gf91838ldx40mjsp0b8w9n18-example
-post-build-hook: Uploading paths /nix/store/ibcyipq5gf91838ldx40mjsp0b8w9n18-example
-/nix/store/ibcyipq5gf91838ldx40mjsp0b8w9n18-example
-</screen>
-
-  <para>Then delete the path from the store, and try substituting it from the binary cache:</para>
-  <screen>
-$ rm ./result
-$ nix-store --delete /nix/store/ibcyipq5gf91838ldx40mjsp0b8w9n18-example
-</screen>
-
-<para>Now, copy the path back from the cache:</para>
-<screen>
-$ nix store --realize /nix/store/ibcyipq5gf91838ldx40mjsp0b8w9n18-example
-copying path '/nix/store/m8bmqwrch6l3h8s0k3d673xpmipcdpsa-example from 's3://example-nix-cache'...
-warning: you did not specify '--add-root'; the result might be removed by the garbage collector
-/nix/store/m8bmqwrch6l3h8s0k3d673xpmipcdpsa-example
-</screen>
-</section>
-<section>
-  <title>Conclusion</title>
-  <para>
-    We now have a Nix installation configured to automatically sign and
-    upload every local build to a remote binary cache.
-  </para>
-
-  <para>
-    Before deploying this to production, be sure to consider the
-    implementation caveats in <xref linkend="chap-post-build-hook-caveats" />.
-  </para>
-</section>
-</chapter>
diff --git a/third_party/nix/doc/manual/command-ref/command-ref.xml b/third_party/nix/doc/manual/command-ref/command-ref.xml
deleted file mode 100644
index cfad9b7d79..0000000000
--- a/third_party/nix/doc/manual/command-ref/command-ref.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<part xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='part-command-ref'>
-
-<title>Command Reference</title>
-
-<partintro>
-<para>This section lists commands and options that you can use when you
-work with Nix.</para>
-</partintro>
-
-<xi:include href="opt-common.xml" />
-<xi:include href="env-common.xml" />
-<xi:include href="main-commands.xml" />
-<xi:include href="utilities.xml" />
-<xi:include href="files.xml" />
-
-</part>
diff --git a/third_party/nix/doc/manual/command-ref/conf-file.xml b/third_party/nix/doc/manual/command-ref/conf-file.xml
deleted file mode 100644
index 4a5400b193..0000000000
--- a/third_party/nix/doc/manual/command-ref/conf-file.xml
+++ /dev/null
@@ -1,1202 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<refentry xmlns="http://docbook.org/ns/docbook"
-          xmlns:xlink="http://www.w3.org/1999/xlink"
-          xmlns:xi="http://www.w3.org/2001/XInclude"
-          xml:id="sec-conf-file"
-          version="5">
-
-<refmeta>
-  <refentrytitle>nix.conf</refentrytitle>
-  <manvolnum>5</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix.conf</refname>
-  <refpurpose>Nix configuration file</refpurpose>
-</refnamediv>
-
-<refsection><title>Description</title>
-
-<para>Nix reads settings from two configuration files:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>The system-wide configuration file
-    <filename><replaceable>sysconfdir</replaceable>/nix/nix.conf</filename>
-    (i.e. <filename>/etc/nix/nix.conf</filename> on most systems), or
-    <filename>$NIX_CONF_DIR/nix.conf</filename> if
-    <envar>NIX_CONF_DIR</envar> is set.</para>
-  </listitem>
-
-  <listitem>
-    <para>The user configuration file
-    <filename>$XDG_CONFIG_HOME/nix/nix.conf</filename>, or
-    <filename>~/.config/nix/nix.conf</filename> if
-    <envar>XDG_CONFIG_HOME</envar> is not set.</para>
-  </listitem>
-
-</itemizedlist>
-
-<para>The configuration files consist of
-<literal><replaceable>name</replaceable> =
-<replaceable>value</replaceable></literal> pairs, one per line. Other
-files can be included with a line like <literal>include
-<replaceable>path</replaceable></literal>, where
-<replaceable>path</replaceable> is interpreted relative to the current
-conf file and a missing file is an error unless
-<literal>!include</literal> is used instead.
-Comments start with a <literal>#</literal> character.  Here is an
-example configuration file:</para>
-
-<programlisting>
-keep-outputs = true       # Nice for developers
-keep-derivations = true   # Idem
-</programlisting>
-
-<para>You can override settings on the command line using the
-<option>--option</option> flag, e.g. <literal>--option keep-outputs
-false</literal>.</para>
-
-<para>The following settings are currently available:
-
-<variablelist>
-
-
-  <varlistentry xml:id="conf-allowed-uris"><term><literal>allowed-uris</literal></term>
-
-    <listitem>
-
-      <para>A list of URI prefixes to which access is allowed in
-      restricted evaluation mode. For example, when set to
-      <literal>https://github.com/NixOS</literal>, builtin functions
-      such as <function>fetchGit</function> are allowed to access
-      <literal>https://github.com/NixOS/patchelf.git</literal>.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-allow-import-from-derivation"><term><literal>allow-import-from-derivation</literal></term>
-
-    <listitem><para>By default, Nix allows you to <function>import</function> from a derivation,
-    allowing building at evaluation time. With this option set to false, Nix will throw an error
-    when evaluating an expression that uses this feature, allowing users to ensure their evaluation
-    will not require any builds to take place.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-allow-new-privileges"><term><literal>allow-new-privileges</literal></term>
-
-    <listitem><para>(Linux-specific.) By default, builders on Linux
-    cannot acquire new privileges by calling setuid/setgid programs or
-    programs that have file capabilities. For example, programs such
-    as <command>sudo</command> or <command>ping</command> will
-    fail. (Note that in sandbox builds, no such programs are available
-    unless you bind-mount them into the sandbox via the
-    <option>sandbox-paths</option> option.) You can allow the
-    use of such programs by enabling this option. This is impure and
-    usually undesirable, but may be useful in certain scenarios
-    (e.g. to spin up containers or set up userspace network interfaces
-    in tests).</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-allowed-users"><term><literal>allowed-users</literal></term>
-
-    <listitem>
-
-      <para>A list of names of users (separated by whitespace) that
-      are allowed to connect to the Nix daemon. As with the
-      <option>trusted-users</option> option, you can specify groups by
-      prefixing them with <literal>@</literal>. Also, you can allow
-      all users by specifying <literal>*</literal>. The default is
-      <literal>*</literal>.</para>
-
-      <para>Note that trusted users are always allowed to connect.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-auto-optimise-store"><term><literal>auto-optimise-store</literal></term>
-
-    <listitem><para>If set to <literal>true</literal>, Nix
-    automatically detects files in the store that have identical
-    contents, and replaces them with hard links to a single copy.
-    This saves disk space.  If set to <literal>false</literal> (the
-    default), you can still run <command>nix-store
-    --optimise</command> to get rid of duplicate
-    files.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-builders">
-    <term><literal>builders</literal></term>
-    <listitem>
-      <para>A list of machines on which to perform builds. <phrase
-      condition="manual">See <xref linkend="chap-distributed-builds"
-      /> for details.</phrase></para>
-    </listitem>
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-builders-use-substitutes"><term><literal>builders-use-substitutes</literal></term>
-
-    <listitem><para>If set to <literal>true</literal>, Nix will instruct
-    remote build machines to use their own binary substitutes if available. In
-    practical terms, this means that remote hosts will fetch as many build
-    dependencies as possible from their own substitutes (e.g, from
-    <literal>cache.nixos.org</literal>), instead of waiting for this host to
-    upload them all. This can drastically reduce build times if the network
-    connection between this computer and the remote build host is slow. Defaults
-    to <literal>false</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-users-group"><term><literal>build-users-group</literal></term>
-
-    <listitem><para>This options specifies the Unix group containing
-    the Nix build user accounts.  In multi-user Nix installations,
-    builds should not be performed by the Nix account since that would
-    allow users to arbitrarily modify the Nix store and database by
-    supplying specially crafted builders; and they cannot be performed
-    by the calling user since that would allow him/her to influence
-    the build result.</para>
-
-    <para>Therefore, if this option is non-empty and specifies a valid
-    group, builds will be performed under the user accounts that are a
-    member of the group specified here (as listed in
-    <filename>/etc/group</filename>).  Those user accounts should not
-    be used for any other purpose!</para>
-
-    <para>Nix will never run two builds under the same user account at
-    the same time.  This is to prevent an obvious security hole: a
-    malicious user writing a Nix expression that modifies the build
-    result of a legitimate Nix expression being built by another user.
-    Therefore it is good to have as many Nix build user accounts as
-    you can spare.  (Remember: uids are cheap.)</para>
-
-    <para>The build users should have permission to create files in
-    the Nix store, but not delete them.  Therefore,
-    <filename>/nix/store</filename> should be owned by the Nix
-    account, its group should be the group specified here, and its
-    mode should be <literal>1775</literal>.</para>
-
-    <para>If the build users group is empty, builds will be performed
-    under the uid of the Nix process (that is, the uid of the caller
-    if <envar>NIX_REMOTE</envar> is empty, the uid under which the Nix
-    daemon runs if <envar>NIX_REMOTE</envar> is
-    <literal>daemon</literal>).  Obviously, this should not be used in
-    multi-user settings with untrusted users.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-compress-build-log"><term><literal>compress-build-log</literal></term>
-
-    <listitem><para>If set to <literal>true</literal> (the default),
-    build logs written to <filename>/nix/var/log/nix/drvs</filename>
-    will be compressed on the fly using bzip2.  Otherwise, they will
-    not be compressed.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-connect-timeout"><term><literal>connect-timeout</literal></term>
-
-    <listitem>
-
-      <para>The timeout (in seconds) for establishing connections in
-      the binary cache substituter.  It corresponds to
-      <command>curl</command>’s <option>--connect-timeout</option>
-      option.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-cores"><term><literal>cores</literal></term>
-
-    <listitem><para>Sets the value of the
-    <envar>NIX_BUILD_CORES</envar> environment variable in the
-    invocation of builders.  Builders can use this variable at their
-    discretion to control the maximum amount of parallelism.  For
-    instance, in Nixpkgs, if the derivation attribute
-    <varname>enableParallelBuilding</varname> is set to
-    <literal>true</literal>, the builder passes the
-    <option>-j<replaceable>N</replaceable></option> flag to GNU Make.
-    It can be overridden using the <option
-    linkend='opt-cores'>--cores</option> command line switch and
-    defaults to <literal>1</literal>.  The value <literal>0</literal>
-    means that the builder should use all available CPU cores in the
-    system.</para>
-
-    <para>See also <xref linkend="chap-tuning-cores-and-jobs" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-diff-hook"><term><literal>diff-hook</literal></term>
-  <listitem>
-    <para>
-      Absolute path to an executable capable of diffing build results.
-      The hook executes if <xref linkend="conf-run-diff-hook" /> is
-      true, and the output of a build is known to not be the same.
-      This program is not executed to determine if two results are the
-      same.
-    </para>
-
-    <para>
-      The diff hook is executed by the same user and group who ran the
-      build. However, the diff hook does not have write access to the
-      store path just built.
-    </para>
-
-    <para>The diff hook program receives three parameters:</para>
-
-    <orderedlist>
-      <listitem>
-        <para>
-          A path to the previous build's results
-        </para>
-      </listitem>
-
-      <listitem>
-        <para>
-          A path to the current build's results
-        </para>
-      </listitem>
-
-      <listitem>
-        <para>
-          The path to the build's derivation
-        </para>
-      </listitem>
-
-      <listitem>
-        <para>
-          The path to the build's scratch directory. This directory
-          will exist only if the build was run with
-          <option>--keep-failed</option>.
-        </para>
-      </listitem>
-    </orderedlist>
-
-    <para>
-      The stderr and stdout output from the diff hook will not be
-      displayed to the user. Instead, it will print to the nix-daemon's
-      log.
-    </para>
-
-    <para>When using the Nix daemon, <literal>diff-hook</literal> must
-    be set in the <filename>nix.conf</filename> configuration file, and
-    cannot be passed at the command line.
-    </para>
-  </listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-enforce-determinism">
-    <term><literal>enforce-determinism</literal></term>
-
-    <listitem><para>See <xref linkend="conf-repeat" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-extra-sandbox-paths">
-    <term><literal>extra-sandbox-paths</literal></term>
-
-    <listitem><para>A list of additional paths appended to
-    <option>sandbox-paths</option>. Useful if you want to extend
-    its default value.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-extra-platforms"><term><literal>extra-platforms</literal></term>
-
-    <listitem><para>Platforms other than the native one which
-    this machine is capable of building for. This can be useful for
-    supporting additional architectures on compatible machines:
-    i686-linux can be built on x86_64-linux machines (and the default
-    for this setting reflects this); armv7 is backwards-compatible with
-    armv6 and armv5tel; some aarch64 machines can also natively run
-    32-bit ARM code; and qemu-user may be used to support non-native
-    platforms (though this may be slow and buggy). Most values for this
-    are not enabled by default because build systems will often
-    misdetect the target platform and generate incompatible code, so you
-    may wish to cross-check the results of using this option against
-    proper natively-built versions of your
-    derivations.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-extra-substituters"><term><literal>extra-substituters</literal></term>
-
-    <listitem><para>Additional binary caches appended to those
-    specified in <option>substituters</option>.  When used by
-    unprivileged users, untrusted substituters (i.e. those not listed
-    in <option>trusted-substituters</option>) are silently
-    ignored.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-fallback"><term><literal>fallback</literal></term>
-
-    <listitem><para>If set to <literal>true</literal>, Nix will fall
-    back to building from source if a binary substitute fails.  This
-    is equivalent to the <option>--fallback</option> flag.  The
-    default is <literal>false</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-fsync-metadata"><term><literal>fsync-metadata</literal></term>
-
-    <listitem><para>If set to <literal>true</literal>, changes to the
-    Nix store metadata (in <filename>/nix/var/nix/db</filename>) are
-    synchronously flushed to disk.  This improves robustness in case
-    of system crashes, but reduces performance.  The default is
-    <literal>true</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-hashed-mirrors"><term><literal>hashed-mirrors</literal></term>
-
-    <listitem><para>A list of web servers used by
-    <function>builtins.fetchurl</function> to obtain files by
-    hash. The default is
-    <literal>http://tarballs.nixos.org/</literal>. Given a hash type
-    <replaceable>ht</replaceable> and a base-16 hash
-    <replaceable>h</replaceable>, Nix will try to download the file
-    from
-    <literal>hashed-mirror/<replaceable>ht</replaceable>/<replaceable>h</replaceable></literal>.
-    This allows files to be downloaded even if they have disappeared
-    from their original URI. For example, given the default mirror
-    <literal>http://tarballs.nixos.org/</literal>, when building the derivation
-
-<programlisting>
-builtins.fetchurl {
-  url = https://example.org/foo-1.2.3.tar.xz;
-  sha256 = "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae";
-}
-</programlisting>
-
-    Nix will attempt to download this file from
-    <literal>http://tarballs.nixos.org/sha256/2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae</literal>
-    first. If it is not available there, if will try the original URI.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-http-connections"><term><literal>http-connections</literal></term>
-
-    <listitem><para>The maximum number of parallel TCP connections
-    used to fetch files from binary caches and by other downloads. It
-    defaults to 25. 0 means no limit.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-keep-build-log"><term><literal>keep-build-log</literal></term>
-
-    <listitem><para>If set to <literal>true</literal> (the default),
-    Nix will write the build log of a derivation (i.e. the standard
-    output and error of its builder) to the directory
-    <filename>/nix/var/log/nix/drvs</filename>.  The build log can be
-    retrieved using the command <command>nix-store -l
-    <replaceable>path</replaceable></command>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-keep-derivations"><term><literal>keep-derivations</literal></term>
-
-    <listitem><para>If <literal>true</literal> (default), the garbage
-    collector will keep the derivations from which non-garbage store
-    paths were built.  If <literal>false</literal>, they will be
-    deleted unless explicitly registered as a root (or reachable from
-    other roots).</para>
-
-    <para>Keeping derivation around is useful for querying and
-    traceability (e.g., it allows you to ask with what dependencies or
-    options a store path was built), so by default this option is on.
-    Turn it off to save a bit of disk space (or a lot if
-    <literal>keep-outputs</literal> is also turned on).</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-keep-env-derivations"><term><literal>keep-env-derivations</literal></term>
-
-    <listitem><para>If <literal>false</literal> (default), derivations
-    are not stored in Nix user environments.  That is, the derivations of
-    any build-time-only dependencies may be garbage-collected.</para>
-
-    <para>If <literal>true</literal>, when you add a Nix derivation to
-    a user environment, the path of the derivation is stored in the
-    user environment.  Thus, the derivation will not be
-    garbage-collected until the user environment generation is deleted
-    (<command>nix-env --delete-generations</command>).  To prevent
-    build-time-only dependencies from being collected, you should also
-    turn on <literal>keep-outputs</literal>.</para>
-
-    <para>The difference between this option and
-    <literal>keep-derivations</literal> is that this one is
-    β€œsticky”: it applies to any user environment created while this
-    option was enabled, while <literal>keep-derivations</literal>
-    only applies at the moment the garbage collector is
-    run.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-keep-outputs"><term><literal>keep-outputs</literal></term>
-
-    <listitem><para>If <literal>true</literal>, the garbage collector
-    will keep the outputs of non-garbage derivations.  If
-    <literal>false</literal> (default), outputs will be deleted unless
-    they are GC roots themselves (or reachable from other roots).</para>
-
-    <para>In general, outputs must be registered as roots separately.
-    However, even if the output of a derivation is registered as a
-    root, the collector will still delete store paths that are used
-    only at build time (e.g., the C compiler, or source tarballs
-    downloaded from the network).  To prevent it from doing so, set
-    this option to <literal>true</literal>.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-max-build-log-size"><term><literal>max-build-log-size</literal></term>
-
-    <listitem>
-
-      <para>This option defines the maximum number of bytes that a
-      builder can write to its stdout/stderr.  If the builder exceeds
-      this limit, it’s killed.  A value of <literal>0</literal> (the
-      default) means that there is no limit.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-max-free"><term><literal>max-free</literal></term>
-
-    <listitem><para>When a garbage collection is triggered by the
-    <literal>min-free</literal> option, it stops as soon as
-    <literal>max-free</literal> bytes are available. The default is
-    infinity (i.e. delete all garbage).</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-max-jobs"><term><literal>max-jobs</literal></term>
-
-    <listitem><para>This option defines the maximum number of jobs
-    that Nix will try to build in parallel.  The default is
-    <literal>1</literal>. The special value <literal>auto</literal>
-    causes Nix to use the number of CPUs in your system.  <literal>0</literal>
-    is useful when using remote builders to prevent any local builds (except for
-    <literal>preferLocalBuild</literal> derivation attribute which executes locally
-    regardless).  It can be
-    overridden using the <option
-    linkend='opt-max-jobs'>--max-jobs</option> (<option>-j</option>)
-    command line switch.</para>
-
-    <para>See also <xref linkend="chap-tuning-cores-and-jobs" />.</para>
-    </listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-max-silent-time"><term><literal>max-silent-time</literal></term>
-
-    <listitem>
-
-      <para>This option defines the maximum number of seconds that a
-      builder can go without producing any data on standard output or
-      standard error.  This is useful (for instance in an automated
-      build system) to catch builds that are stuck in an infinite
-      loop, or to catch remote builds that are hanging due to network
-      problems.  It can be overridden using the <option
-      linkend="opt-max-silent-time">--max-silent-time</option> command
-      line switch.</para>
-
-      <para>The value <literal>0</literal> means that there is no
-      timeout.  This is also the default.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-min-free"><term><literal>min-free</literal></term>
-
-    <listitem>
-      <para>When free disk space in <filename>/nix/store</filename>
-      drops below <literal>min-free</literal> during a build, Nix
-      performs a garbage-collection until <literal>max-free</literal>
-      bytes are available or there is no more garbage.  A value of
-      <literal>0</literal> (the default) disables this feature.</para>
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-narinfo-cache-negative-ttl"><term><literal>narinfo-cache-negative-ttl</literal></term>
-
-    <listitem>
-
-      <para>The TTL in seconds for negative lookups. If a store path is
-      queried from a substituter but was not found, there will be a
-      negative lookup cached in the local disk cache database for the
-      specified duration.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-narinfo-cache-positive-ttl"><term><literal>narinfo-cache-positive-ttl</literal></term>
-
-    <listitem>
-
-      <para>The TTL in seconds for positive lookups. If a store path is
-      queried from a substituter, the result of the query will be cached
-      in the local disk cache database including some of the NAR
-      metadata. The default TTL is a month, setting a shorter TTL for
-      positive lookups can be useful for binary caches that have
-      frequent garbage collection, in which case having a more frequent
-      cache invalidation would prevent trying to pull the path again and
-      failing with a hash mismatch if the build isn't reproducible.
-      </para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-netrc-file"><term><literal>netrc-file</literal></term>
-
-    <listitem><para>If set to an absolute path to a <filename>netrc</filename>
-    file, Nix will use the HTTP authentication credentials in this file when
-    trying to download from a remote host through HTTP or HTTPS. Defaults to
-    <filename>$NIX_CONF_DIR/netrc</filename>.</para>
-
-    <para>The <filename>netrc</filename> file consists of a list of
-    accounts in the following format:
-
-<screen>
-machine <replaceable>my-machine</replaceable>
-login <replaceable>my-username</replaceable>
-password <replaceable>my-password</replaceable>
-</screen>
-
-    For the exact syntax, see <link
-    xlink:href="https://ec.haxx.se/usingcurl-netrc.html">the
-    <literal>curl</literal> documentation.</link></para>
-
-    <note><para>This must be an absolute path, and <literal>~</literal>
-    is not resolved. For example, <filename>~/.netrc</filename> won't
-    resolve to your home directory's <filename>.netrc</filename>.</para></note>
-    </listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-pre-build-hook"><term><literal>pre-build-hook</literal></term>
-
-    <listitem>
-
-
-      <para>If set, the path to a program that can set extra
-      derivation-specific settings for this system. This is used for settings
-      that can't be captured by the derivation model itself and are too variable
-      between different versions of the same system to be hard-coded into nix.
-      </para>
-
-      <para>The hook is passed the derivation path and, if sandboxes are enabled,
-      the sandbox directory. It can then modify the sandbox and send a series of
-      commands to modify various settings to stdout. The currently recognized
-      commands are:</para>
-
-      <variablelist>
-        <varlistentry xml:id="extra-sandbox-paths">
-          <term><literal>extra-sandbox-paths</literal></term>
-
-          <listitem>
-
-            <para>Pass a list of files and directories to be included in the
-            sandbox for this build. One entry per line, terminated by an empty
-            line. Entries have the same format as
-            <literal>sandbox-paths</literal>.</para>
-
-          </listitem>
-
-        </varlistentry>
-      </variablelist>
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-post-build-hook">
-    <term><literal>post-build-hook</literal></term>
-    <listitem>
-      <para>Optional. The path to a program to execute after each build.</para>
-
-      <para>This option is only settable in the global
-      <filename>nix.conf</filename>, or on the command line by trusted
-      users.</para>
-
-      <para>When using the nix-daemon, the daemon executes the hook as
-      <literal>root</literal>. If the nix-daemon is not involved, the
-      hook runs as the user executing the nix-build.</para>
-
-      <itemizedlist>
-        <listitem><para>The hook executes after an evaluation-time build.</para></listitem>
-        <listitem><para>The hook does not execute on substituted paths.</para></listitem>
-        <listitem><para>The hook's output always goes to the user's terminal.</para></listitem>
-        <listitem><para>If the hook fails, the build succeeds but no further builds execute.</para></listitem>
-        <listitem><para>The hook executes synchronously, and blocks other builds from progressing while it runs.</para></listitem>
-      </itemizedlist>
-
-      <para>The program executes with no arguments. The program's environment
-      contains the following environment variables:</para>
-
-      <variablelist>
-        <varlistentry>
-          <term><envar>DRV_PATH</envar></term>
-          <listitem>
-            <para>The derivation for the built paths.</para>
-            <para>Example:
-            <literal>/nix/store/5nihn1a7pa8b25l9zafqaqibznlvvp3f-bash-4.4-p23.drv</literal>
-            </para>
-          </listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><envar>OUT_PATHS</envar></term>
-          <listitem>
-            <para>Output paths of the built derivation, separated by a space character.</para>
-            <para>Example:
-            <literal>/nix/store/zf5lbh336mnzf1nlswdn11g4n2m8zh3g-bash-4.4-p23-dev
-            /nix/store/rjxwxwv1fpn9wa2x5ssk5phzwlcv4mna-bash-4.4-p23-doc
-            /nix/store/6bqvbzjkcp9695dq0dpl5y43nvy37pq1-bash-4.4-p23-info
-            /nix/store/r7fng3kk3vlpdlh2idnrbn37vh4imlj2-bash-4.4-p23-man
-            /nix/store/xfghy8ixrhz3kyy6p724iv3cxji088dx-bash-4.4-p23</literal>.
-            </para>
-          </listitem>
-        </varlistentry>
-      </variablelist>
-
-      <para>See <xref linkend="chap-post-build-hook" /> for an example
-      implementation.</para>
-
-    </listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-repeat"><term><literal>repeat</literal></term>
-
-    <listitem><para>How many times to repeat builds to check whether
-    they are deterministic. The default value is 0. If the value is
-    non-zero, every build is repeated the specified number of
-    times. If the contents of any of the runs differs from the
-    previous ones and <xref linkend="conf-enforce-determinism" /> is
-    true, the build is rejected and the resulting store paths are not
-    registered as β€œvalid” in Nix’s database.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-require-sigs"><term><literal>require-sigs</literal></term>
-
-    <listitem><para>If set to <literal>true</literal> (the default),
-    any non-content-addressed path added or copied to the Nix store
-    (e.g. when substituting from a binary cache) must have a valid
-    signature, that is, be signed using one of the keys listed in
-    <option>trusted-public-keys</option> or
-    <option>secret-key-files</option>. Set to <literal>false</literal>
-    to disable signature checking.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-restrict-eval"><term><literal>restrict-eval</literal></term>
-
-    <listitem>
-
-      <para>If set to <literal>true</literal>, the Nix evaluator will
-      not allow access to any files outside of the Nix search path (as
-      set via the <envar>NIX_PATH</envar> environment variable or the
-      <option>-I</option> option), or to URIs outside of
-      <option>allowed-uri</option>. The default is
-      <literal>false</literal>.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-run-diff-hook"><term><literal>run-diff-hook</literal></term>
-  <listitem>
-    <para>
-      If true, enable the execution of <xref linkend="conf-diff-hook" />.
-    </para>
-
-    <para>
-      When using the Nix daemon, <literal>run-diff-hook</literal> must
-      be set in the <filename>nix.conf</filename> configuration file,
-      and cannot be passed at the command line.
-    </para>
-  </listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-sandbox"><term><literal>sandbox</literal></term>
-
-    <listitem><para>If set to <literal>true</literal>, builds will be
-    performed in a <emphasis>sandboxed environment</emphasis>, i.e.,
-    they’re isolated from the normal file system hierarchy and will
-    only see their dependencies in the Nix store, the temporary build
-    directory, private versions of <filename>/proc</filename>,
-    <filename>/dev</filename>, <filename>/dev/shm</filename> and
-    <filename>/dev/pts</filename> (on Linux), and the paths configured with the
-    <link linkend='conf-sandbox-paths'><literal>sandbox-paths</literal>
-    option</link>. This is useful to prevent undeclared dependencies
-    on files in directories such as <filename>/usr/bin</filename>. In
-    addition, on Linux, builds run in private PID, mount, network, IPC
-    and UTS namespaces to isolate them from other processes in the
-    system (except that fixed-output derivations do not run in private
-    network namespace to ensure they can access the network).</para>
-
-    <para>Currently, sandboxing only work on Linux and macOS. The use
-    of a sandbox requires that Nix is run as root (so you should use
-    the <link linkend='conf-build-users-group'>β€œbuild users”
-    feature</link> to perform the actual builds under different users
-    than root).</para>
-
-    <para>If this option is set to <literal>relaxed</literal>, then
-    fixed-output derivations and derivations that have the
-    <varname>__noChroot</varname> attribute set to
-    <literal>true</literal> do not run in sandboxes.</para>
-
-    <para>The default is <literal>true</literal> on Linux and
-    <literal>false</literal> on all other platforms.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-sandbox-dev-shm-size"><term><literal>sandbox-dev-shm-size</literal></term>
-
-    <listitem><para>This option determines the maximum size of the
-    <literal>tmpfs</literal> filesystem mounted on
-    <filename>/dev/shm</filename> in Linux sandboxes. For the format,
-    see the description of the <option>size</option> option of
-    <literal>tmpfs</literal> in
-    <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>. The
-    default is <literal>50%</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-sandbox-paths">
-    <term><literal>sandbox-paths</literal></term>
-
-    <listitem><para>A list of paths bind-mounted into Nix sandbox
-    environments. You can use the syntax
-    <literal><replaceable>target</replaceable>=<replaceable>source</replaceable></literal>
-    to mount a path in a different location in the sandbox; for
-    instance, <literal>/bin=/nix-bin</literal> will mount the path
-    <literal>/nix-bin</literal> as <literal>/bin</literal> inside the
-    sandbox. If <replaceable>source</replaceable> is followed by
-    <literal>?</literal>, then it is not an error if
-    <replaceable>source</replaceable> does not exist; for example,
-    <literal>/dev/nvidiactl?</literal> specifies that
-    <filename>/dev/nvidiactl</filename> will only be mounted in the
-    sandbox if it exists in the host filesystem.</para>
-
-    <para>Depending on how Nix was built, the default value for this option
-    may be empty or provide <filename>/bin/sh</filename> as a
-    bind-mount of <command>bash</command>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-secret-key-files"><term><literal>secret-key-files</literal></term>
-
-    <listitem><para>A whitespace-separated list of files containing
-    secret (private) keys. These are used to sign locally-built
-    paths. They can be generated using <command>nix-store
-    --generate-binary-cache-key</command>. The corresponding public
-    key can be distributed to other users, who can add it to
-    <option>trusted-public-keys</option> in their
-    <filename>nix.conf</filename>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-show-trace"><term><literal>show-trace</literal></term>
-
-    <listitem><para>Causes Nix to print out a stack trace in case of Nix
-    expression evaluation errors.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-substitute"><term><literal>substitute</literal></term>
-
-    <listitem><para>If set to <literal>true</literal> (default), Nix
-    will use binary substitutes if available.  This option can be
-    disabled to force building from source.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-stalled-download-timeout"><term><literal>stalled-download-timeout</literal></term>
-    <listitem>
-      <para>The timeout (in seconds) for receiving data from servers
-      during download. Nix cancels idle downloads after this timeout's
-      duration.</para>
-    </listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-substituters"><term><literal>substituters</literal></term>
-
-    <listitem><para>A list of URLs of substituters, separated by
-    whitespace.  The default is
-    <literal>https://cache.nixos.org</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-system"><term><literal>system</literal></term>
-
-    <listitem><para>This option specifies the canonical Nix system
-    name of the current installation, such as
-    <literal>i686-linux</literal> or
-    <literal>x86_64-darwin</literal>.  Nix can only build derivations
-    whose <literal>system</literal> attribute equals the value
-    specified here.  In general, it never makes sense to modify this
-    value from its default, since you can use it to β€˜lie’ about the
-    platform you are building on (e.g., perform a Mac OS build on a
-    Linux machine; the result would obviously be wrong).  It only
-    makes sense if the Nix binaries can run on multiple platforms,
-    e.g., β€˜universal binaries’ that run on <literal>x86_64-linux</literal> and
-    <literal>i686-linux</literal>.</para>
-
-    <para>It defaults to the canonical Nix system name detected by
-    <filename>configure</filename> at build time.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="conf-system-features"><term><literal>system-features</literal></term>
-
-    <listitem><para>A set of system β€œfeatures” supported by this
-    machine, e.g. <literal>kvm</literal>. Derivations can express a
-    dependency on such features through the derivation attribute
-    <varname>requiredSystemFeatures</varname>. For example, the
-    attribute
-
-<programlisting>
-requiredSystemFeatures = [ "kvm" ];
-</programlisting>
-
-    ensures that the derivation can only be built on a machine with
-    the <literal>kvm</literal> feature.</para>
-
-    <para>This setting by default includes <literal>kvm</literal> if
-    <filename>/dev/kvm</filename> is accessible, and the
-    pseudo-features <literal>nixos-test</literal>,
-    <literal>benchmark</literal> and <literal>big-parallel</literal>
-    that are used in Nixpkgs to route builds to specific
-    machines.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-tarball-ttl"><term><literal>tarball-ttl</literal></term>
-
-    <listitem>
-      <para>Default: <literal>3600</literal> seconds.</para>
-
-      <para>The number of seconds a downloaded tarball is considered
-      fresh. If the cached tarball is stale, Nix will check whether
-      it is still up to date using the ETag header. Nix will download
-      a new version if the ETag header is unsupported, or the
-      cached ETag doesn't match.
-      </para>
-
-      <para>Setting the TTL to <literal>0</literal> forces Nix to always
-      check if the tarball is up to date.</para>
-
-      <para>Nix caches tarballs in
-      <filename>$XDG_CACHE_HOME/nix/tarballs</filename>.</para>
-
-      <para>Files fetched via <envar>NIX_PATH</envar>,
-      <function>fetchGit</function>, <function>fetchMercurial</function>,
-      <function>fetchTarball</function>, and <function>fetchurl</function>
-      respect this TTL.
-      </para>
-    </listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-timeout"><term><literal>timeout</literal></term>
-
-    <listitem>
-
-      <para>This option defines the maximum number of seconds that a
-      builder can run.  This is useful (for instance in an automated
-      build system) to catch builds that are stuck in an infinite loop
-      but keep writing to their standard output or standard error.  It
-      can be overridden using the <option
-      linkend="opt-timeout">--timeout</option> command line
-      switch.</para>
-
-      <para>The value <literal>0</literal> means that there is no
-      timeout.  This is also the default.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-trace-function-calls"><term><literal>trace-function-calls</literal></term>
-
-    <listitem>
-
-      <para>Default: <literal>false</literal>.</para>
-
-      <para>If set to <literal>true</literal>, the Nix evaluator will
-      trace every function call. Nix will print a log message at the
-      "vomit" level for every function entrance and function exit.</para>
-
-      <informalexample><screen>
-function-trace entered undefined position at 1565795816999559622
-function-trace exited undefined position at 1565795816999581277
-function-trace entered /nix/store/.../example.nix:226:41 at 1565795253249935150
-function-trace exited /nix/store/.../example.nix:226:41 at 1565795253249941684
-</screen></informalexample>
-
-      <para>The <literal>undefined position</literal> means the function
-      call is a builtin.</para>
-
-      <para>Use the <literal>contrib/stack-collapse.py</literal> script
-      distributed with the Nix source code to convert the trace logs
-      in to a format suitable for <command>flamegraph.pl</command>.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-trusted-public-keys"><term><literal>trusted-public-keys</literal></term>
-
-    <listitem><para>A whitespace-separated list of public keys. When
-    paths are copied from another Nix store (such as a binary cache),
-    they must be signed with one of these keys. For example:
-    <literal>cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
-    hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-trusted-substituters"><term><literal>trusted-substituters</literal></term>
-
-    <listitem><para>A list of URLs of substituters, separated by
-    whitespace.  These are not used by default, but can be enabled by
-    users of the Nix daemon by specifying <literal>--option
-    substituters <replaceable>urls</replaceable></literal> on the
-    command line.  Unprivileged users are only allowed to pass a
-    subset of the URLs listed in <literal>substituters</literal> and
-    <literal>trusted-substituters</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id="conf-trusted-users"><term><literal>trusted-users</literal></term>
-
-    <listitem>
-
-      <para>A list of names of users (separated by whitespace) that
-      have additional rights when connecting to the Nix daemon, such
-      as the ability to specify additional binary caches, or to import
-      unsigned NARs. You can also specify groups by prefixing them
-      with <literal>@</literal>; for instance,
-      <literal>@wheel</literal> means all users in the
-      <literal>wheel</literal> group. The default is
-      <literal>root</literal>.</para>
-
-      <warning><para>Adding a user to <option>trusted-users</option>
-      is essentially equivalent to giving that user root access to the
-      system. For example, the user can set
-      <option>sandbox-paths</option> and thereby obtain read access to
-      directories that are otherwise inacessible to
-      them.</para></warning>
-
-    </listitem>
-
-  </varlistentry>
-
-</variablelist>
-</para>
-
-<refsection>
-  <title>Deprecated Settings</title>
-
-<para>
-
-<variablelist>
-
-  <varlistentry xml:id="conf-binary-caches">
-    <term><literal>binary-caches</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>binary-caches</literal> is now an alias to
-    <xref linkend="conf-substituters" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-binary-cache-public-keys">
-    <term><literal>binary-cache-public-keys</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>binary-cache-public-keys</literal> is now an alias to
-    <xref linkend="conf-trusted-public-keys" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-compress-log">
-    <term><literal>build-compress-log</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-compress-log</literal> is now an alias to
-    <xref linkend="conf-compress-build-log" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-cores">
-    <term><literal>build-cores</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-cores</literal> is now an alias to
-    <xref linkend="conf-cores" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-extra-chroot-dirs">
-    <term><literal>build-extra-chroot-dirs</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-extra-chroot-dirs</literal> is now an alias to
-    <xref linkend="conf-extra-sandbox-paths" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-extra-sandbox-paths">
-    <term><literal>build-extra-sandbox-paths</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-extra-sandbox-paths</literal> is now an alias to
-    <xref linkend="conf-extra-sandbox-paths" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-fallback">
-    <term><literal>build-fallback</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-fallback</literal> is now an alias to
-    <xref linkend="conf-fallback" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-max-jobs">
-    <term><literal>build-max-jobs</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-max-jobs</literal> is now an alias to
-    <xref linkend="conf-max-jobs" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-max-log-size">
-    <term><literal>build-max-log-size</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-max-log-size</literal> is now an alias to
-    <xref linkend="conf-max-build-log-size" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-max-silent-time">
-    <term><literal>build-max-silent-time</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-max-silent-time</literal> is now an alias to
-    <xref linkend="conf-max-silent-time" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-repeat">
-    <term><literal>build-repeat</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-repeat</literal> is now an alias to
-    <xref linkend="conf-repeat" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-timeout">
-    <term><literal>build-timeout</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-timeout</literal> is now an alias to
-    <xref linkend="conf-timeout" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-use-chroot">
-    <term><literal>build-use-chroot</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-use-chroot</literal> is now an alias to
-    <xref linkend="conf-sandbox" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-use-sandbox">
-    <term><literal>build-use-sandbox</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-use-sandbox</literal> is now an alias to
-    <xref linkend="conf-sandbox" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-build-use-substitutes">
-    <term><literal>build-use-substitutes</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>build-use-substitutes</literal> is now an alias to
-    <xref linkend="conf-substitute" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-gc-keep-derivations">
-    <term><literal>gc-keep-derivations</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>gc-keep-derivations</literal> is now an alias to
-    <xref linkend="conf-keep-derivations" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-gc-keep-outputs">
-    <term><literal>gc-keep-outputs</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>gc-keep-outputs</literal> is now an alias to
-    <xref linkend="conf-keep-outputs" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-env-keep-derivations">
-    <term><literal>env-keep-derivations</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>env-keep-derivations</literal> is now an alias to
-    <xref linkend="conf-keep-env-derivations" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-extra-binary-caches">
-    <term><literal>extra-binary-caches</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>extra-binary-caches</literal> is now an alias to
-    <xref linkend="conf-extra-substituters" />.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id="conf-trusted-binary-caches">
-    <term><literal>trusted-binary-caches</literal></term>
-
-    <listitem><para><emphasis>Deprecated:</emphasis>
-    <literal>trusted-binary-caches</literal> is now an alias to
-    <xref linkend="conf-trusted-substituters" />.</para></listitem>
-  </varlistentry>
-</variablelist>
-</para>
-</refsection>
-
-</refsection>
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/env-common.xml b/third_party/nix/doc/manual/command-ref/env-common.xml
deleted file mode 100644
index 696d68c345..0000000000
--- a/third_party/nix/doc/manual/command-ref/env-common.xml
+++ /dev/null
@@ -1,202 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-common-env">
-
-<title>Common Environment Variables</title>
-
-
-<para>Most Nix commands interpret the following environment variables:</para>
-
-<variablelist xml:id="env-common">
-
-<varlistentry><term><envar>IN_NIX_SHELL</envar></term>
-
-  <listitem><para>Indicator that tells if the current environment was set up by
-  <command>nix-shell</command>.  Since Nix 2.0 the values are
-  <literal>"pure"</literal> and <literal>"impure"</literal></para></listitem>
-
-</varlistentry>
-
-<varlistentry xml:id="env-NIX_PATH"><term><envar>NIX_PATH</envar></term>
-
-  <listitem>
-
-    <para>A colon-separated list of directories used to look up Nix
-    expressions enclosed in angle brackets (i.e.,
-    <literal>&lt;<replaceable>path</replaceable>></literal>).  For
-    instance, the value
-
-    <screen>
-/home/eelco/Dev:/etc/nixos</screen>
-
-    will cause Nix to look for paths relative to
-    <filename>/home/eelco/Dev</filename> and
-    <filename>/etc/nixos</filename>, in that order.  It is also
-    possible to match paths against a prefix.  For example, the value
-
-    <screen>
-nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos</screen>
-
-    will cause Nix to search for
-    <literal>&lt;nixpkgs/<replaceable>path</replaceable>></literal> in
-    <filename>/home/eelco/Dev/nixpkgs-branch/<replaceable>path</replaceable></filename>
-    and
-    <filename>/etc/nixos/nixpkgs/<replaceable>path</replaceable></filename>.</para>
-
-    <para>If a path in the Nix search path starts with
-    <literal>http://</literal> or <literal>https://</literal>, it is
-    interpreted as the URL of a tarball that will be downloaded and
-    unpacked to a temporary location. The tarball must consist of a
-    single top-level directory. For example, setting
-    <envar>NIX_PATH</envar> to
-
-    <screen>
-nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-15.09.tar.gz</screen>
-
-    tells Nix to download the latest revision in the Nixpkgs/NixOS
-    15.09 channel.</para>
-
-    <para>A following shorthand can be used to refer to the official channels:
-    
-    <screen>nixpkgs=channel:nixos-15.09</screen>
-    </para>
-
-    <para>The search path can be extended using the <option
-    linkend="opt-I">-I</option> option, which takes precedence over
-    <envar>NIX_PATH</envar>.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>NIX_IGNORE_SYMLINK_STORE</envar></term>
-
-  <listitem>
-
-  <para>Normally, the Nix store directory (typically
-  <filename>/nix/store</filename>) is not allowed to contain any
-  symlink components.  This is to prevent β€œimpure” builds.  Builders
-  sometimes β€œcanonicalise” paths by resolving all symlink components.
-  Thus, builds on different machines (with
-  <filename>/nix/store</filename> resolving to different locations)
-  could yield different results.  This is generally not a problem,
-  except when builds are deployed to machines where
-  <filename>/nix/store</filename> resolves differently.  If you are
-  sure that you’re not going to do that, you can set
-  <envar>NIX_IGNORE_SYMLINK_STORE</envar> to <envar>1</envar>.</para>
-
-  <para>Note that if you’re symlinking the Nix store so that you can
-  put it on another file system than the root file system, on Linux
-  you’re better off using <literal>bind</literal> mount points, e.g.,
-
-  <screen>
-$ mkdir /nix
-$ mount -o bind /mnt/otherdisk/nix /nix</screen>
-
-  Consult the <citerefentry><refentrytitle>mount</refentrytitle>
-  <manvolnum>8</manvolnum></citerefentry> manual page for details.</para>
-
-  </listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>NIX_STORE_DIR</envar></term>
-
-  <listitem><para>Overrides the location of the Nix store (default
-  <filename><replaceable>prefix</replaceable>/store</filename>).</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>NIX_DATA_DIR</envar></term>
-
-  <listitem><para>Overrides the location of the Nix static data
-  directory (default
-  <filename><replaceable>prefix</replaceable>/share</filename>).</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>NIX_LOG_DIR</envar></term>
-
-  <listitem><para>Overrides the location of the Nix log directory
-  (default <filename><replaceable>prefix</replaceable>/var/log/nix</filename>).</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>NIX_STATE_DIR</envar></term>
-
-  <listitem><para>Overrides the location of the Nix state directory
-  (default <filename><replaceable>prefix</replaceable>/var/nix</filename>).</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>NIX_CONF_DIR</envar></term>
-
-  <listitem><para>Overrides the location of the Nix configuration
-  directory (default
-  <filename><replaceable>prefix</replaceable>/etc/nix</filename>).</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>TMPDIR</envar></term>
-
-  <listitem><para>Use the specified directory to store temporary
-  files.  In particular, this includes temporary build directories;
-  these can take up substantial amounts of disk space.  The default is
-  <filename>/tmp</filename>.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry xml:id="envar-remote"><term><envar>NIX_REMOTE</envar></term>
-
-  <listitem><para>This variable should be set to
-  <literal>daemon</literal> if you want to use the Nix daemon to
-  execute Nix operations. This is necessary in <link
-  linkend="ssec-multi-user">multi-user Nix installations</link>.
-  If the Nix daemon's Unix socket is at some non-standard path,
-  this variable should be set to <literal>unix://path/to/socket</literal>.
-  Otherwise, it should be left unset.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>NIX_SHOW_STATS</envar></term>
-
-  <listitem><para>If set to <literal>1</literal>, Nix will print some
-  evaluation statistics, such as the number of values
-  allocated.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>NIX_COUNT_CALLS</envar></term>
-
-  <listitem><para>If set to <literal>1</literal>, Nix will print how
-  often functions were called during Nix expression evaluation.  This
-  is useful for profiling your Nix expressions.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><envar>GC_INITIAL_HEAP_SIZE</envar></term>
-
-  <listitem><para>If Nix has been configured to use the Boehm garbage
-  collector, this variable sets the initial size of the heap in bytes.
-  It defaults to 384 MiB.  Setting it to a low value reduces memory
-  consumption, but will increase runtime due to the overhead of
-  garbage collection.</para></listitem>
-
-</varlistentry>
-
-
-</variablelist>
-
-
-</chapter>
diff --git a/third_party/nix/doc/manual/command-ref/files.xml b/third_party/nix/doc/manual/command-ref/files.xml
deleted file mode 100644
index 7bbc96e899..0000000000
--- a/third_party/nix/doc/manual/command-ref/files.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='ch-files'>
-
-<title>Files</title>
-
-<para>This section lists configuration files that you can use when you
-work with Nix.</para>
-
-<xi:include href="conf-file.xml" />
-
-</chapter>
\ No newline at end of file
diff --git a/third_party/nix/doc/manual/command-ref/main-commands.xml b/third_party/nix/doc/manual/command-ref/main-commands.xml
deleted file mode 100644
index 0f4169243c..0000000000
--- a/third_party/nix/doc/manual/command-ref/main-commands.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='ch-main-commands'>
-
-<title>Main Commands</title>
-
-<para>This section lists commands and options that you can use when you
-work with Nix.</para>
-
-<xi:include href="nix-env.xml" />
-<xi:include href="nix-build.xml" />
-<xi:include href="nix-shell.xml" />
-<xi:include href="nix-store.xml" />
-
-</chapter>
\ No newline at end of file
diff --git a/third_party/nix/doc/manual/command-ref/nix-build.xml b/third_party/nix/doc/manual/command-ref/nix-build.xml
deleted file mode 100644
index c1b783c87d..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-build.xml
+++ /dev/null
@@ -1,190 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-build">
-
-<refmeta>
-  <refentrytitle>nix-build</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-build</refname>
-  <refpurpose>build a Nix expression</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-build</command>
-    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
-    <arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
-    <arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
-    <arg>
-      <group choice='req'>
-        <arg choice='plain'><option>--attr</option></arg>
-        <arg choice='plain'><option>-A</option></arg>
-      </group>
-      <replaceable>attrPath</replaceable>
-    </arg>
-    <arg><option>--no-out-link</option></arg>
-    <arg><option>--dry-run</option></arg>
-    <arg>
-      <group choice='req'>
-        <arg choice='plain'><option>--out-link</option></arg>
-        <arg choice='plain'><option>-o</option></arg>
-      </group>
-      <replaceable>outlink</replaceable>
-    </arg>
-    <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-<refsection><title>Description</title>
-
-<para>The <command>nix-build</command> command builds the derivations
-described by the Nix expressions in <replaceable>paths</replaceable>.
-If the build succeeds, it places a symlink to the result in the
-current directory.  The symlink is called <filename>result</filename>.
-If there are multiple Nix expressions, or the Nix expressions evaluate
-to multiple derivations, multiple sequentially numbered symlinks are
-created (<filename>result</filename>, <filename>result-2</filename>,
-and so on).</para>
-
-<para>If no <replaceable>paths</replaceable> are specified, then
-<command>nix-build</command> will use <filename>default.nix</filename>
-in the current directory, if it exists.</para>
-
-<para>If an element of <replaceable>paths</replaceable> starts with
-<literal>http://</literal> or <literal>https://</literal>, it is
-interpreted as the URL of a tarball that will be downloaded and
-unpacked to a temporary location. The tarball must include a single
-top-level directory containing at least a file named
-<filename>default.nix</filename>.</para>
-
-<para><command>nix-build</command> is essentially a wrapper around
-<link
-linkend="sec-nix-instantiate"><command>nix-instantiate</command></link>
-(to translate a high-level Nix expression to a low-level store
-derivation) and <link
-linkend="rsec-nix-store-realise"><command>nix-store
---realise</command></link> (to build the store derivation).</para>
-
-<warning><para>The result of the build is automatically registered as
-a root of the Nix garbage collector.  This root disappears
-automatically when the <filename>result</filename> symlink is deleted
-or renamed.  So don’t rename the symlink.</para></warning>
-
-</refsection>
-
-
-<refsection><title>Options</title>
-
-<para>All options not listed here are passed to <command>nix-store
---realise</command>, except for <option>--arg</option> and
-<option>--attr</option> / <option>-A</option> which are passed to
-<command>nix-instantiate</command>.  <phrase condition="manual">See
-also <xref linkend="sec-common-options" />.</phrase></para>
-
-<variablelist>
-
-  <varlistentry><term><option>--no-out-link</option></term>
-
-    <listitem><para>Do not create a symlink to the output path.  Note
-    that as a result the output does not become a root of the garbage
-    collector, and so might be deleted by <command>nix-store
-    --gc</command>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--dry-run</option></term>
-   <listitem><para>Show what store paths would be built or downloaded</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id='opt-out-link'><term><option>--out-link</option> /
-  <option>-o</option> <replaceable>outlink</replaceable></term>
-
-    <listitem><para>Change the name of the symlink to the output path
-    created from <filename>result</filename> to
-    <replaceable>outlink</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-<para>The following common options are supported:</para>
-
-<variablelist condition="manpage">
-  <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<screen>
-$ nix-build '&lt;nixpkgs>' -A firefox
-store derivation is /nix/store/qybprl8sz2lc...-firefox-1.5.0.7.drv
-/nix/store/d18hyl92g30l...-firefox-1.5.0.7
-
-$ ls -l result
-lrwxrwxrwx  <replaceable>...</replaceable>  result -> /nix/store/d18hyl92g30l...-firefox-1.5.0.7
-
-$ ls ./result/bin/
-firefox  firefox-config</screen>
-
-<para>If a derivation has multiple outputs,
-<command>nix-build</command> will build the default (first) output.
-You can also build all outputs:
-<screen>
-$ nix-build '&lt;nixpkgs>' -A openssl.all
-</screen>
-This will create a symlink for each output named
-<filename>result-<replaceable>outputname</replaceable></filename>.
-The suffix is omitted if the output name is <literal>out</literal>.
-So if <literal>openssl</literal> has outputs <literal>out</literal>,
-<literal>bin</literal> and <literal>man</literal>,
-<command>nix-build</command> will create symlinks
-<literal>result</literal>, <literal>result-bin</literal> and
-<literal>result-man</literal>.  It’s also possible to build a specific
-output:
-<screen>
-$ nix-build '&lt;nixpkgs>' -A openssl.man
-</screen>
-This will create a symlink <literal>result-man</literal>.</para>
-
-<para>Build a Nix expression given on the command line:
-
-<screen>
-$ nix-build -E 'with import &lt;nixpkgs> { }; runCommand "foo" { } "echo bar > $out"'
-$ cat ./result
-bar
-</screen>
-
-</para>
-
-<para>Build the GNU Hello package from the latest revision of the
-master branch of Nixpkgs:
-
-<screen>
-$ nix-build https://github.com/NixOS/nixpkgs/archive/master.tar.gz -A hello
-</screen>
-
-</para>
-
-</refsection>
-
-
-<refsection condition="manpage"><title>Environment variables</title>
-
-<variablelist>
-  <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-channel.xml b/third_party/nix/doc/manual/command-ref/nix-channel.xml
deleted file mode 100644
index 5a2866e6bc..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-channel.xml
+++ /dev/null
@@ -1,178 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-channel">
-
-<refmeta>
-  <refentrytitle>nix-channel</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-channel</refname>
-  <refpurpose>manage Nix channels</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-channel</command>
-    <group choice='req'>
-      <arg choice='plain'><option>--add</option> <replaceable>url</replaceable> <arg choice='opt'><replaceable>name</replaceable></arg></arg>
-      <arg choice='plain'><option>--remove</option> <replaceable>name</replaceable></arg>
-      <arg choice='plain'><option>--list</option></arg>
-      <arg choice='plain'><option>--update</option> <arg rep='repeat'><replaceable>names</replaceable></arg></arg>
-      <arg choice='plain'><option>--rollback</option> <arg choice='opt'><replaceable>generation</replaceable></arg></arg>
-    </group>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-<refsection><title>Description</title>
-
-<para>A Nix channel is a mechanism that allows you to automatically
-stay up-to-date with a set of pre-built Nix expressions.  A Nix
-channel is just a URL that points to a place containing a set of Nix
-expressions.  <phrase condition="manual">See also <xref
-linkend="sec-channels" />.</phrase></para>
-
-<para>This command has the following operations:
-
-<variablelist>
-
-  <varlistentry><term><option>--add</option> <replaceable>url</replaceable> [<replaceable>name</replaceable>]</term>
-
-    <listitem><para>Adds a channel named
-    <replaceable>name</replaceable> with URL
-    <replaceable>url</replaceable> to the list of subscribed channels.
-    If <replaceable>name</replaceable> is omitted, it defaults to the
-    last component of <replaceable>url</replaceable>, with the
-    suffixes <literal>-stable</literal> or
-    <literal>-unstable</literal> removed.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--remove</option> <replaceable>name</replaceable></term>
-
-    <listitem><para>Removes the channel named
-    <replaceable>name</replaceable> from the list of subscribed
-    channels.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--list</option></term>
-
-    <listitem><para>Prints the names and URLs of all subscribed
-    channels on standard output.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--update</option> [<replaceable>names</replaceable>…]</term>
-
-    <listitem><para>Downloads the Nix expressions of all subscribed
-    channels (or only those included in
-    <replaceable>names</replaceable> if specified) and makes them the
-    default for <command>nix-env</command> operations (by symlinking
-    them from the directory
-    <filename>~/.nix-defexpr</filename>).</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--rollback</option> [<replaceable>generation</replaceable>]</term>
-
-    <listitem><para>Reverts the previous call to <command>nix-channel
-    --update</command>. Optionally, you can specify a specific channel
-    generation number to restore.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</para>
-
-<para>Note that <option>--add</option> does not automatically perform
-an update.</para>
-
-<para>The list of subscribed channels is stored in
-<filename>~/.nix-channels</filename>.</para>
-
-</refsection>
-
-<refsection><title>Examples</title>
-
-<para>To subscribe to the Nixpkgs channel and install the GNU Hello package:</para>
-
-<screen>
-$ nix-channel --add https://nixos.org/channels/nixpkgs-unstable
-$ nix-channel --update
-$ nix-env -iA nixpkgs.hello</screen>
-
-<para>You can revert channel updates using <option>--rollback</option>:</para>
-
-<screen>
-$ nix-instantiate --eval -E '(import &lt;nixpkgs> {}).lib.nixpkgsVersion'
-"14.04.527.0e935f1"
-
-$ nix-channel --rollback
-switching from generation 483 to 482
-
-$ nix-instantiate --eval -E '(import &lt;nixpkgs> {}).lib.nixpkgsVersion'
-"14.04.526.dbadfad"
-</screen>
-
-</refsection>
-
-<refsection><title>Files</title>
-
-<variablelist>
-
-  <varlistentry><term><filename>/nix/var/nix/profiles/per-user/<replaceable>username</replaceable>/channels</filename></term>
-
-    <listitem><para><command>nix-channel</command> uses a
-    <command>nix-env</command> profile to keep track of previous
-    versions of the subscribed channels. Every time you run
-    <command>nix-channel --update</command>, a new channel generation
-    (that is, a symlink to the channel Nix expressions in the Nix store)
-    is created. This enables <command>nix-channel --rollback</command>
-    to revert to previous versions.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><filename>~/.nix-defexpr/channels</filename></term>
-
-    <listitem><para>This is a symlink to
-    <filename>/nix/var/nix/profiles/per-user/<replaceable>username</replaceable>/channels</filename>. It
-    ensures that <command>nix-env</command> can find your channels. In
-    a multi-user installation, you may also have
-    <filename>~/.nix-defexpr/channels_root</filename>, which links to
-    the channels of the root user.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-<refsection><title>Channel format</title>
-
-<para>A channel URL should point to a directory containing the
-following files:</para>
-
-<variablelist>
-
-  <varlistentry><term><filename>nixexprs.tar.xz</filename></term>
-
-    <listitem><para>A tarball containing Nix expressions and files
-    referenced by them (such as build scripts and patches). At the
-    top level, the tarball should contain a single directory. That
-    directory must contain a file <filename>default.nix</filename>
-    that serves as the channel’s β€œentry point”.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-collect-garbage.xml b/third_party/nix/doc/manual/command-ref/nix-collect-garbage.xml
deleted file mode 100644
index 43e0687969..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-collect-garbage.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-collect-garbage">
-  
-<refmeta>
-  <refentrytitle>nix-collect-garbage</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-collect-garbage</refname>
-  <refpurpose>delete unreachable store paths</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-collect-garbage</command>
-    <arg><option>--delete-old</option></arg>
-    <arg><option>-d</option></arg>
-    <arg><option>--delete-older-than</option> <replaceable>period</replaceable></arg>
-    <arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg>
-    <arg><option>--dry-run</option></arg>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-<refsection><title>Description</title>
-
-<para>The command <command>nix-collect-garbage</command> is mostly an
-alias of <link linkend="rsec-nix-store-gc"><command>nix-store
---gc</command></link>, that is, it deletes all unreachable paths in
-the Nix store to clean up your system.  However, it provides two
-additional options: <option>-d</option> (<option>--delete-old</option>),
-which deletes all old generations of all profiles in
-<filename>/nix/var/nix/profiles</filename> by invoking
-<literal>nix-env --delete-generations old</literal> on all profiles
-(of course, this makes rollbacks to previous configurations
-impossible); and
-<option>--delete-older-than</option> <replaceable>period</replaceable>,
-where period is a value such as <literal>30d</literal>, which deletes
-all generations older than the specified number of days in all profiles
-in <filename>/nix/var/nix/profiles</filename> (except for the generations
-that were active at that point in time).
-</para>
-
-</refsection>
-
-<refsection><title>Example</title>
-
-<para>To delete from the Nix store everything that is not used by the
-current generations of each profile, do
-
-<screen>
-$ nix-collect-garbage -d</screen>
-
-</para>
-
-</refsection>
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-copy-closure.xml b/third_party/nix/doc/manual/command-ref/nix-copy-closure.xml
deleted file mode 100644
index e6dcf180ad..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-copy-closure.xml
+++ /dev/null
@@ -1,169 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-          xmlns:xlink="http://www.w3.org/1999/xlink"
-          xmlns:xi="http://www.w3.org/2001/XInclude"
-          xml:id="sec-nix-copy-closure">
-
-<refmeta>
-  <refentrytitle>nix-copy-closure</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-copy-closure</refname>
-  <refpurpose>copy a closure to or from a remote machine via SSH</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-copy-closure</command>
-    <group>
-      <arg choice='plain'><option>--to</option></arg>
-      <arg choice='plain'><option>--from</option></arg>
-    </group>
-    <arg><option>--gzip</option></arg>
-    <!--
-    <arg><option>- -show-progress</option></arg>
-    -->
-    <arg><option>--include-outputs</option></arg>
-    <group>
-      <arg choice='plain'><option>--use-substitutes</option></arg>
-      <arg choice='plain'><option>-s</option></arg>
-    </group>
-    <arg><option>-v</option></arg>
-    <arg choice='plain'>
-      <replaceable>user@</replaceable><replaceable>machine</replaceable>
-    </arg>
-    <arg choice='plain'><replaceable>paths</replaceable></arg>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-
-<refsection><title>Description</title>
-
-<para><command>nix-copy-closure</command> gives you an easy and
-efficient way to exchange software between machines.  Given one or
-more Nix store <replaceable>paths</replaceable> on the local
-machine, <command>nix-copy-closure</command> computes the closure of
-those paths (i.e. all their dependencies in the Nix store), and copies
-all paths in the closure to the remote machine via the
-<command>ssh</command> (Secure Shell) command.  With the
-<option>--from</option>, the direction is reversed:
-the closure of <replaceable>paths</replaceable> on a remote machine is
-copied to the Nix store on the local machine.</para>
-
-<para>This command is efficient because it only sends the store paths
-that are missing on the target machine.</para>
-
-<para>Since <command>nix-copy-closure</command> calls
-<command>ssh</command>, you may be asked to type in the appropriate
-password or passphrase.  In fact, you may be asked
-<emphasis>twice</emphasis> because <command>nix-copy-closure</command>
-currently connects twice to the remote machine, first to get the set
-of paths missing on the target machine, and second to send the dump of
-those paths.  If this bothers you, use
-<command>ssh-agent</command>.</para>
-
-
-<refsection><title>Options</title>
-
-<variablelist>
-
-  <varlistentry><term><option>--to</option></term>
-
-    <listitem><para>Copy the closure of
-    <replaceable>paths</replaceable> from the local Nix store to the
-    Nix store on <replaceable>machine</replaceable>.  This is the
-    default.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--from</option></term>
-
-    <listitem><para>Copy the closure of
-    <replaceable>paths</replaceable> from the Nix store on
-    <replaceable>machine</replaceable> to the local Nix
-    store.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--gzip</option></term>
-
-    <listitem><para>Enable compression of the SSH
-    connection.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--include-outputs</option></term>
-
-    <listitem><para>Also copy the outputs of store derivations
-    included in the closure.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--use-substitutes</option> / <option>-s</option></term>
-
-    <listitem><para>Attempt to download missing paths on the target
-    machine using Nix’s substitute mechanism.  Any paths that cannot
-    be substituted on the target are still copied normally from the
-    source.  This is useful, for instance, if the connection between
-    the source and target machine is slow, but the connection between
-    the target machine and <literal>nixos.org</literal> (the default
-    binary cache server) is fast.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>-v</option></term>
-
-    <listitem><para>Show verbose output.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Environment variables</title>
-
-<variablelist>
-
-  <varlistentry><term><envar>NIX_SSHOPTS</envar></term>
-
-    <listitem><para>Additional options to be passed to
-    <command>ssh</command> on the command line.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<para>Copy Firefox with all its dependencies to a remote machine:
-
-<screen>
-$ nix-copy-closure --to alice@itchy.labs $(type -tP firefox)</screen>
-
-</para>
-
-<para>Copy Subversion from a remote machine and then install it into a
-user environment:
-
-<screen>
-$ nix-copy-closure --from alice@itchy.labs \
-    /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4
-$ nix-env -i /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4
-</screen>
-
-</para>
-
-</refsection>
-
-
-</refsection>
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-daemon.xml b/third_party/nix/doc/manual/command-ref/nix-daemon.xml
deleted file mode 100644
index 9159d15d1c..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-daemon.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-daemon">
-
-<refmeta>
-  <refentrytitle>nix-daemon</refentrytitle>
-  <manvolnum>8</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-daemon</refname>
-  <refpurpose>Nix multi-user support daemon</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-daemon</command>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-
-<refsection><title>Description</title>
-
-<para>The Nix daemon is necessary in multi-user Nix installations.  It
-performs build actions and other operations on the Nix store on behalf
-of unprivileged users.</para>
-
-
-</refsection>
-
-<refsection><title>Options</title>
-
-  <variablelist>
-
-  <varlistentry><term><option>--pipe</option></term>
-
-    <listitem><para>Causes the nix daemon to forward stdin and stdout to and
-    from the actual daemon socket. This is used when communicating with a remote
-    store over SSH</para></listitem>
-
-  </varlistentry>
-
-  </variablelist>
-
-</refsection>
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-env.xml b/third_party/nix/doc/manual/command-ref/nix-env.xml
deleted file mode 100644
index d257a5e49c..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-env.xml
+++ /dev/null
@@ -1,1505 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-env">
-
-<refmeta>
-  <refentrytitle>nix-env</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-env</refname>
-  <refpurpose>manipulate or query Nix user environments</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-env</command>
-    <xi:include href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
-    <arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
-    <arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
-    <arg>
-      <group choice='req'>
-        <arg choice='plain'><option>--file</option></arg>
-        <arg choice='plain'><option>-f</option></arg>
-      </group>
-      <replaceable>path</replaceable>
-    </arg>
-    <arg>
-      <group choice='req'>
-        <arg choice='plain'><option>--profile</option></arg>
-        <arg choice='plain'><option>-p</option></arg>
-      </group>
-      <replaceable>path</replaceable>
-    </arg>
-    <arg>
-      <arg choice='plain'><option>--system-filter</option></arg>
-      <replaceable>system</replaceable>
-    </arg>
-    <arg><option>--dry-run</option></arg>
-    <arg choice='plain'><replaceable>operation</replaceable></arg>
-    <arg rep='repeat'><replaceable>options</replaceable></arg>
-    <arg rep='repeat'><replaceable>arguments</replaceable></arg>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-
-<refsection><title>Description</title>
-
-<para>The command <command>nix-env</command> is used to manipulate Nix
-user environments.  User environments are sets of software packages
-available to a user at some point in time.  In other words, they are a
-synthesised view of the programs available in the Nix store.  There
-may be many user environments: different users can have different
-environments, and individual users can switch between different
-environments.</para>
-
-<para><command>nix-env</command> takes exactly one
-<emphasis>operation</emphasis> flag which indicates the subcommand to
-be performed.  These are documented below.</para>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Selectors</title>
-
-<para>Several commands, such as <command>nix-env -q</command> and
-<command>nix-env -i</command>, take a list of arguments that specify
-the packages on which to operate. These are extended regular
-expressions that must match the entire name of the package. (For
-details on regular expressions, see
-<citerefentry><refentrytitle>regex</refentrytitle><manvolnum>7</manvolnum></citerefentry>.)
-The match is case-sensitive. The regular expression can optionally be
-followed by a dash and a version number; if omitted, any version of
-the package will match.  Here are some examples:
-
-<variablelist>
-
-  <varlistentry>
-    <term><literal>firefox</literal></term>
-    <listitem><para>Matches the package name
-    <literal>firefox</literal> and any version.</para></listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><literal>firefox-32.0</literal></term>
-    <listitem><para>Matches the package name
-    <literal>firefox</literal> and version
-    <literal>32.0</literal>.</para></listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><literal>gtk\\+</literal></term>
-    <listitem><para>Matches the package name
-    <literal>gtk+</literal>. The <literal>+</literal> character must
-    be escaped using a backslash to prevent it from being interpreted
-    as a quantifier, and the backslash must be escaped in turn with
-    another backslash to ensure that the shell passes it
-    on.</para></listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><literal>.\*</literal></term>
-    <listitem><para>Matches any package name. This is the default for
-    most commands.</para></listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><literal>'.*zip.*'</literal></term>
-    <listitem><para>Matches any package name containing the string
-    <literal>zip</literal>. Note the dots: <literal>'*zip*'</literal>
-    does not work, because in a regular expression, the character
-    <literal>*</literal> is interpreted as a
-    quantifier.</para></listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><literal>'.*(firefox|chromium).*'</literal></term>
-    <listitem><para>Matches any package name containing the strings
-    <literal>firefox</literal> or
-    <literal>chromium</literal>.</para></listitem>
-  </varlistentry>
-
-</variablelist>
-
-</para>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Common options</title>
-
-<para>This section lists the options that are common to all
-operations.  These options are allowed for every subcommand, though
-they may not always have an effect.  <phrase condition="manual">See
-also <xref linkend="sec-common-options" />.</phrase></para>
-
-<variablelist>
-
-  <varlistentry><term><option>--file</option> / <option>-f</option> <replaceable>path</replaceable></term>
-
-    <listitem><para>Specifies the Nix expression (designated below as
-    the <emphasis>active Nix expression</emphasis>) used by the
-    <option>--install</option>, <option>--upgrade</option>, and
-    <option>--query --available</option> operations to obtain
-    derivations.  The default is
-    <filename>~/.nix-defexpr</filename>.</para>
-
-    <para>If the argument starts with <literal>http://</literal> or
-    <literal>https://</literal>, it is interpreted as the URL of a
-    tarball that will be downloaded and unpacked to a temporary
-    location. The tarball must include a single top-level directory
-    containing at least a file named <filename>default.nix</filename>.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--profile</option> / <option>-p</option> <replaceable>path</replaceable></term>
-
-    <listitem><para>Specifies the profile to be used by those
-    operations that operate on a profile (designated below as the
-    <emphasis>active profile</emphasis>).  A profile is a sequence of
-    user environments called <emphasis>generations</emphasis>, one of
-    which is the <emphasis>current
-    generation</emphasis>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--dry-run</option></term>
-
-    <listitem><para>For the <option>--install</option>,
-    <option>--upgrade</option>, <option>--uninstall</option>,
-    <option>--switch-generation</option>,
-    <option>--delete-generations</option> and
-    <option>--rollback</option> operations, this flag will cause
-    <command>nix-env</command> to print what
-    <emphasis>would</emphasis> be done if this flag had not been
-    specified, without actually doing it.</para>
-
-    <para><option>--dry-run</option> also prints out which paths will
-    be <link linkend="gloss-substitute">substituted</link> (i.e.,
-    downloaded) and which paths will be built from source (because no
-    substitute is available).</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--system-filter</option> <replaceable>system</replaceable></term>
-
-    <listitem><para>By default, operations such as <option>--query
-    --available</option> show derivations matching any platform.  This
-    option allows you to use derivations for the specified platform
-    <replaceable>system</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-<variablelist condition="manpage">
-  <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Files</title>
-
-<variablelist>
-
-  <varlistentry><term><filename>~/.nix-defexpr</filename></term>
-
-    <listitem><para>The source for the default Nix
-    expressions used by the <option>--install</option>,
-    <option>--upgrade</option>, and <option>--query
-    --available</option> operations to obtain derivations. The
-    <option>--file</option> option may be used to override this
-    default.</para>
-
-    <para>If <filename>~/.nix-defexpr</filename> is a file,
-    it is loaded as a Nix expression. If the expression
-    is a set, it is used as the default Nix expression.
-    If the expression is a function, an empty set is passed
-    as argument and the return value is used as
-    the default Nix expression.</para>
-
-    <para>If <filename>~/.nix-defexpr</filename> is a directory
-    containing a <filename>default.nix</filename> file, that file
-    is loaded as in the above paragraph.</para>
-
-    <para>If <filename>~/.nix-defexpr</filename> is a directory without
-    a <filename>default.nix</filename> file, then its contents
-    (both files and subdirectories) are loaded as Nix expressions.
-    The expressions are combined into a single set, each expression
-    under an attribute with the same name as the original file
-    or subdirectory.
-    </para>
-
-    <para>For example, if <filename>~/.nix-defexpr</filename> contains
-    two files, <filename>foo.nix</filename> and <filename>bar.nix</filename>,
-    then the default Nix expression will essentially be
-
-<programlisting>
-{
-  foo = import ~/.nix-defexpr/foo.nix;
-  bar = import ~/.nix-defexpr/bar.nix;
-}</programlisting>
-
-    </para>
-
-    <para>The file <filename>manifest.nix</filename> is always ignored.
-    Subdirectories without a <filename>default.nix</filename> file
-    are traversed recursively in search of more Nix expressions,
-    but the names of these intermediate directories are not
-    added to the attribute paths of the default Nix expression.</para>
-
-    <para>The command <command>nix-channel</command> places symlinks
-    to the downloaded Nix expressions from each subscribed channel in
-    this directory.</para>
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><filename>~/.nix-profile</filename></term>
-
-    <listitem><para>A symbolic link to the user's current profile.  By
-    default, this symlink points to
-    <filename><replaceable>prefix</replaceable>/var/nix/profiles/default</filename>.
-    The <envar>PATH</envar> environment variable should include
-    <filename>~/.nix-profile/bin</filename> for the user environment
-    to be visible to the user.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection xml:id="rsec-nix-env-install"><title>Operation <option>--install</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <group choice='req'>
-    <arg choice='plain'><option>--install</option></arg>
-    <arg choice='plain'><option>-i</option></arg>
-  </group>
-  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
-  <group choice='opt'>
-    <arg choice='plain'><option>--preserve-installed</option></arg>
-    <arg choice='plain'><option>-P</option></arg>
-  </group>
-  <group choice='opt'>
-    <arg choice='plain'><option>--remove-all</option></arg>
-    <arg choice='plain'><option>-r</option></arg>
-  </group>
-  <arg choice='plain' rep='repeat'><replaceable>args</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-
-<refsection><title>Description</title>
-
-<para>The install operation creates a new user environment, based on
-the current generation of the active profile, to which a set of store
-paths described by <replaceable>args</replaceable> is added.  The
-arguments <replaceable>args</replaceable> map to store paths in a
-number of possible ways:
-
-<itemizedlist>
-
-  <listitem><para>By default, <replaceable>args</replaceable> is a set
-  of derivation names denoting derivations in the active Nix
-  expression.  These are realised, and the resulting output paths are
-  installed.  Currently installed derivations with a name equal to the
-  name of a derivation being added are removed unless the option
-  <option>--preserve-installed</option> is
-  specified.</para>
-
-  <para>If there are multiple derivations matching a name in
-  <replaceable>args</replaceable> that have the same name (e.g.,
-  <literal>gcc-3.3.6</literal> and <literal>gcc-4.1.1</literal>), then
-  the derivation with the highest <emphasis>priority</emphasis> is
-  used.  A derivation can define a priority by declaring the
-  <varname>meta.priority</varname> attribute.  This attribute should
-  be a number, with a higher value denoting a lower priority.  The
-  default priority is <literal>0</literal>.</para>
-
-  <para>If there are multiple matching derivations with the same
-  priority, then the derivation with the highest version will be
-  installed.</para>
-
-  <para>You can force the installation of multiple derivations with
-  the same name by being specific about the versions.  For instance,
-  <literal>nix-env -i gcc-3.3.6 gcc-4.1.1</literal> will install both
-  version of GCC (and will probably cause a user environment
-  conflict!).</para></listitem>
-
-  <listitem><para>If <link
-  linkend='opt-attr'><option>--attr</option></link>
-  (<option>-A</option>) is specified, the arguments are
-  <emphasis>attribute paths</emphasis> that select attributes from the
-  top-level Nix expression.  This is faster than using derivation
-  names and unambiguous.  To find out the attribute paths of available
-  packages, use <literal>nix-env -qaP</literal>.</para></listitem>
-
-  <listitem><para>If <option>--from-profile</option>
-  <replaceable>path</replaceable> is given,
-  <replaceable>args</replaceable> is a set of names denoting installed
-  store paths in the profile <replaceable>path</replaceable>.  This is
-  an easy way to copy user environment elements from one profile to
-  another.</para></listitem>
-
-  <listitem><para>If <option>--from-expression</option> is given,
-  <replaceable>args</replaceable> are Nix <link
-  linkend="ss-functions">functions</link> that are called with the
-  active Nix expression as their single argument.  The derivations
-  returned by those function calls are installed.  This allows
-  derivations to be specified in an unambiguous way, which is necessary
-  if there are multiple derivations with the same
-  name.</para></listitem>
-
-  <listitem><para>If <replaceable>args</replaceable> are store
-  derivations, then these are <link
-  linkend="rsec-nix-store-realise">realised</link>, and the resulting
-  output paths are installed.</para></listitem>
-
-  <listitem><para>If <replaceable>args</replaceable> are store paths
-  that are not store derivations, then these are <link
-  linkend="rsec-nix-store-realise">realised</link> and
-  installed.</para></listitem>
-
-  <listitem><para>By default all outputs are installed for each derivation.
-  That can be reduced by setting <literal>meta.outputsToInstall</literal>.
-  </para></listitem> <!-- TODO: link nixpkgs docs on the ability to override those. -->
-
-</itemizedlist>
-
-</para>
-
-</refsection>
-
-
-<refsection><title>Flags</title>
-
-<variablelist>
-
-  <varlistentry><term><option>--prebuilt-only</option> / <option>-b</option></term>
-
-    <listitem><para>Use only derivations for which a substitute is
-    registered, i.e., there is a pre-built binary available that can
-    be downloaded in lieu of building the derivation.  Thus, no
-    packages will be built from source.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--preserve-installed</option></term>
-    <term><option>-P</option></term>
-
-    <listitem><para>Do not remove derivations with a name matching one
-    of the derivations being installed.  Usually, trying to have two
-    versions of the same package installed in the same generation of a
-    profile will lead to an error in building the generation, due to
-    file name clashes between the two versions.  However, this is not
-    the case for all packages.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--remove-all</option></term>
-    <term><option>-r</option></term>
-
-    <listitem><para>Remove all previously installed packages first.
-    This is equivalent to running <literal>nix-env -e '.*'</literal>
-    first, except that everything happens in a single
-    transaction.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection xml:id='refsec-nix-env-install-examples'><title>Examples</title>
-
-<para>To install a specific version of <command>gcc</command> from the
-active Nix expression:
-
-<screen>
-$ nix-env --install gcc-3.3.2
-installing `gcc-3.3.2'
-uninstalling `gcc-3.1'</screen>
-
-Note the previously installed version is removed, since
-<option>--preserve-installed</option> was not specified.</para>
-
-<para>To install an arbitrary version:
-
-<screen>
-$ nix-env --install gcc
-installing `gcc-3.3.2'</screen>
-
-</para>
-
-<para>To install using a specific attribute:
-
-<screen>
-$ nix-env -i -A gcc40mips
-$ nix-env -i -A xorg.xorgserver</screen>
-
-</para>
-
-<para>To install all derivations in the Nix expression <filename>foo.nix</filename>:
-
-<screen>
-$ nix-env -f ~/foo.nix -i '.*'</screen>
-
-</para>
-
-<para>To copy the store path with symbolic name <literal>gcc</literal>
-from another profile:
-
-<screen>
-$ nix-env -i --from-profile /nix/var/nix/profiles/foo gcc</screen>
-
-</para>
-
-<para>To install a specific store derivation (typically created by
-<command>nix-instantiate</command>):
-
-<screen>
-$ nix-env -i /nix/store/fibjb1bfbpm5mrsxc4mh2d8n37sxh91i-gcc-3.4.3.drv</screen>
-
-</para>
-
-<para>To install a specific output path:
-
-<screen>
-$ nix-env -i /nix/store/y3cgx0xj1p4iv9x0pnnmdhr8iyg741vk-gcc-3.4.3</screen>
-
-</para>
-
-<para>To install from a Nix expression specified on the command-line:
-
-<screen>
-$ nix-env -f ./foo.nix -i -E \
-    'f: (f {system = "i686-linux";}).subversionWithJava'</screen>
-
-I.e., this evaluates to <literal>(f: (f {system =
-"i686-linux";}).subversionWithJava) (import ./foo.nix)</literal>, thus
-selecting the <literal>subversionWithJava</literal> attribute from the
-set returned by calling the function defined in
-<filename>./foo.nix</filename>.</para>
-
-<para>A dry-run tells you which paths will be downloaded or built from
-source:
-
-<screen>
-$ nix-env -f '&lt;nixpkgs>' -iA hello --dry-run
-(dry run; not doing anything)
-installing β€˜hello-2.10’
-these paths will be fetched (0.04 MiB download, 0.19 MiB unpacked):
-  /nix/store/wkhdf9jinag5750mqlax6z2zbwhqb76n-hello-2.10
-  <replaceable>...</replaceable></screen>
-
-</para>
-
-<para>To install Firefox from the latest revision in the Nixpkgs/NixOS
-14.12 channel:
-
-<screen>
-$ nix-env -f https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz -iA firefox
-</screen>
-
-(The GitHub repository <literal>nixpkgs-channels</literal> is updated
-automatically from the main <literal>nixpkgs</literal> repository
-after certain tests have succeeded and binaries have been built and
-uploaded to the binary cache at <uri>cache.nixos.org</uri>.)</para>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection xml:id="rsec-nix-env-upgrade"><title>Operation <option>--upgrade</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <group choice='req'>
-    <arg choice='plain'><option>--upgrade</option></arg>
-    <arg choice='plain'><option>-u</option></arg>
-  </group>
-  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
-  <group choice='opt'>
-    <arg choice='plain'><option>--lt</option></arg>
-    <arg choice='plain'><option>--leq</option></arg>
-    <arg choice='plain'><option>--eq</option></arg>
-    <arg choice='plain'><option>--always</option></arg>
-  </group>
-  <arg choice='plain' rep='repeat'><replaceable>args</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The upgrade operation creates a new user environment, based on
-the current generation of the active profile, in which all store paths
-are replaced for which there are newer versions in the set of paths
-described by <replaceable>args</replaceable>.  Paths for which there
-are no newer versions are left untouched; this is not an error.  It is
-also not an error if an element of <replaceable>args</replaceable>
-matches no installed derivations.</para>
-
-<para>For a description of how <replaceable>args</replaceable> is
-mapped to a set of store paths, see <link
-linkend="rsec-nix-env-install"><option>--install</option></link>.  If
-<replaceable>args</replaceable> describes multiple store paths with
-the same symbolic name, only the one with the highest version is
-installed.</para>
-
-</refsection>
-
-<refsection><title>Flags</title>
-
-<variablelist>
-
-  <varlistentry><term><option>--lt</option></term>
-
-    <listitem><para>Only upgrade a derivation to newer versions.  This
-    is the default.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--leq</option></term>
-
-    <listitem><para>In addition to upgrading to newer versions, also
-    β€œupgrade” to derivations that have the same version.  Version are
-    not a unique identification of a derivation, so there may be many
-    derivations that have the same version.  This flag may be useful
-    to force β€œsynchronisation” between the installed and available
-    derivations.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--eq</option></term>
-
-    <listitem><para><emphasis>Only</emphasis> β€œupgrade” to derivations
-    that have the same version.  This may not seem very useful, but it
-    actually is, e.g., when there is a new release of Nixpkgs and you
-    want to replace installed applications with the same versions
-    built against newer dependencies (to reduce the number of
-    dependencies floating around on your system).</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--always</option></term>
-
-    <listitem><para>In addition to upgrading to newer versions, also
-    β€œupgrade” to derivations that have the same or a lower version.
-    I.e., derivations may actually be downgraded depending on what is
-    available in the active Nix expression.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-<para>For the other flags, see <option
-linkend="rsec-nix-env-install">--install</option>.</para>
-
-</refsection>
-
-<refsection><title>Examples</title>
-
-<screen>
-$ nix-env --upgrade gcc
-upgrading `gcc-3.3.1' to `gcc-3.4'
-
-$ nix-env -u gcc-3.3.2 --always <lineannotation>(switch to a specific version)</lineannotation>
-upgrading `gcc-3.4' to `gcc-3.3.2'
-
-$ nix-env --upgrade pan
-<lineannotation>(no upgrades available, so nothing happens)</lineannotation>
-
-$ nix-env -u <lineannotation>(try to upgrade everything)</lineannotation>
-upgrading `hello-2.1.2' to `hello-2.1.3'
-upgrading `mozilla-1.2' to `mozilla-1.4'</screen>
-
-</refsection>
-
-<refsection xml:id="ssec-version-comparisons"><title>Versions</title>
-
-<para>The upgrade operation determines whether a derivation
-<varname>y</varname> is an upgrade of a derivation
-<varname>x</varname> by looking at their respective
-<literal>name</literal> attributes.  The names (e.g.,
-<literal>gcc-3.3.1</literal> are split into two parts: the package
-name (<literal>gcc</literal>), and the version
-(<literal>3.3.1</literal>).  The version part starts after the first
-dash not followed by a letter.  <varname>x</varname> is considered an
-upgrade of <varname>y</varname> if their package names match, and the
-version of <varname>y</varname> is higher that that of
-<varname>x</varname>.</para>
-
-<para>The versions are compared by splitting them into contiguous
-components of numbers and letters.  E.g., <literal>3.3.1pre5</literal>
-is split into <literal>[3, 3, 1, "pre", 5]</literal>.  These lists are
-then compared lexicographically (from left to right).  Corresponding
-components <varname>a</varname> and <varname>b</varname> are compared
-as follows.  If they are both numbers, integer comparison is used.  If
-<varname>a</varname> is an empty string and <varname>b</varname> is a
-number, <varname>a</varname> is considered less than
-<varname>b</varname>.  The special string component
-<literal>pre</literal> (for <emphasis>pre-release</emphasis>) is
-considered to be less than other components.  String components are
-considered less than number components.  Otherwise, they are compared
-lexicographically (i.e., using case-sensitive string comparison).</para>
-
-<para>This is illustrated by the following examples:
-
-<screen>
-1.0 &lt; 2.3
-2.1 &lt; 2.3
-2.3 = 2.3
-2.5 > 2.3
-3.1 > 2.3
-2.3.1 > 2.3
-2.3.1 > 2.3a
-2.3pre1 &lt; 2.3
-2.3pre3 &lt; 2.3pre12
-2.3a &lt; 2.3c
-2.3pre1 &lt; 2.3c
-2.3pre1 &lt; 2.3q</screen>
-
-</para>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--uninstall</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <group choice='req'>
-    <arg choice='plain'><option>--uninstall</option></arg>
-    <arg choice='plain'><option>-e</option></arg>
-  </group>
-  <arg choice='plain' rep='repeat'><replaceable>drvnames</replaceable></arg>
-</cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The uninstall operation creates a new user environment, based on
-the current generation of the active profile, from which the store
-paths designated by the symbolic names
-<replaceable>names</replaceable> are removed.</para>
-
-</refsection>
-
-<refsection><title>Examples</title>
-
-<screen>
-$ nix-env --uninstall gcc
-$ nix-env -e '.*' <lineannotation>(remove everything)</lineannotation></screen>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection xml:id="rsec-nix-env-set"><title>Operation <option>--set</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <arg choice='plain'><option>--set</option></arg>
-  <arg choice='plain'><replaceable>drvname</replaceable></arg>
-</cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The <option>--set</option> operation modifies the current generation of a
-profile so that it contains exactly the specified derivation, and nothing else.
-</para>
-
-</refsection>
-
-<refsection><title>Examples</title>
-
-<para>
-The following updates a profile such that its current generation will contain
-just Firefox:
-
-<screen>
-$ nix-env -p /nix/var/nix/profiles/browser --set firefox</screen>
-
-</para>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection xml:id="rsec-nix-env-set-flag"><title>Operation <option>--set-flag</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <arg choice='plain'><option>--set-flag</option></arg>
-  <arg choice='plain'><replaceable>name</replaceable></arg>
-  <arg choice='plain'><replaceable>value</replaceable></arg>
-  <arg choice='plain' rep='repeat'><replaceable>drvnames</replaceable></arg>
-</cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The <option>--set-flag</option> operation allows meta attributes
-of installed packages to be modified.  There are several attributes
-that can be usefully modified, because they affect the behaviour of
-<command>nix-env</command> or the user environment build
-script:
-
-<itemizedlist>
-
-  <listitem><para><varname>priority</varname> can be changed to
-  resolve filename clashes.  The user environment build script uses
-  the <varname>meta.priority</varname> attribute of derivations to
-  resolve filename collisions between packages.  Lower priority values
-  denote a higher priority.  For instance, the GCC wrapper package and
-  the Binutils package in Nixpkgs both have a file
-  <filename>bin/ld</filename>, so previously if you tried to install
-  both you would get a collision.  Now, on the other hand, the GCC
-  wrapper declares a higher priority than Binutils, so the former’s
-  <filename>bin/ld</filename> is symlinked in the user
-  environment.</para></listitem>
-
-  <listitem><para><varname>keep</varname> can be set to
-  <literal>true</literal> to prevent the package from being upgraded
-  or replaced.  This is useful if you want to hang on to an older
-  version of a package.</para></listitem>
-
-  <listitem><para><varname>active</varname> can be set to
-  <literal>false</literal> to β€œdisable” the package.  That is, no
-  symlinks will be generated to the files of the package, but it
-  remains part of the profile (so it won’t be garbage-collected).  It
-  can be set back to <literal>true</literal> to re-enable the
-  package.</para></listitem>
-
-</itemizedlist>
-
-</para>
-
-</refsection>
-
-<refsection><title>Examples</title>
-
-<para>To prevent the currently installed Firefox from being upgraded:
-
-<screen>
-$ nix-env --set-flag keep true firefox</screen>
-
-After this, <command>nix-env -u</command> will ignore Firefox.</para>
-
-<para>To disable the currently installed Firefox, then install a new
-Firefox while the old remains part of the profile:
-
-<screen>
-$ nix-env -q
-firefox-2.0.0.9 <lineannotation>(the current one)</lineannotation>
-
-$ nix-env --preserve-installed -i firefox-2.0.0.11
-installing `firefox-2.0.0.11'
-building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment'
-collision between `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.11/bin/firefox'
-  and `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.9/bin/firefox'.
-<lineannotation>(i.e., can’t have two active at the same time)</lineannotation>
-
-$ nix-env --set-flag active false firefox
-setting flag on `firefox-2.0.0.9'
-
-$ nix-env --preserve-installed -i firefox-2.0.0.11
-installing `firefox-2.0.0.11'
-
-$ nix-env -q
-firefox-2.0.0.11 <lineannotation>(the enabled one)</lineannotation>
-firefox-2.0.0.9 <lineannotation>(the disabled one)</lineannotation></screen>
-
-</para>
-
-<para>To make files from <literal>binutils</literal> take precedence
-over files from <literal>gcc</literal>:
-
-<screen>
-$ nix-env --set-flag priority 5 binutils
-$ nix-env --set-flag priority 10 gcc</screen>
-
-</para>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--query</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <group choice='req'>
-    <arg choice='plain'><option>--query</option></arg>
-    <arg choice='plain'><option>-q</option></arg>
-  </group>
-  <group choice='opt'>
-    <arg choice='plain'><option>--installed</option></arg>
-    <arg choice='plain'><option>--available</option></arg>
-    <arg choice='plain'><option>-a</option></arg>
-  </group>
-
-  <sbr />
-
-  <arg>
-    <group choice='req'>
-      <arg choice='plain'><option>--status</option></arg>
-      <arg choice='plain'><option>-s</option></arg>
-    </group>
-  </arg>
-  <arg>
-    <group choice='req'>
-      <arg choice='plain'><option>--attr-path</option></arg>
-      <arg choice='plain'><option>-P</option></arg>
-    </group>
-  </arg>
-  <arg><option>--no-name</option></arg>
-  <arg>
-    <group choice='req'>
-      <arg choice='plain'><option>--compare-versions</option></arg>
-      <arg choice='plain'><option>-c</option></arg>
-    </group>
-  </arg>
-  <arg><option>--system</option></arg>
-  <arg><option>--drv-path</option></arg>
-  <arg><option>--out-path</option></arg>
-  <arg><option>--description</option></arg>
-  <arg><option>--meta</option></arg>
-
-  <sbr />
-
-  <arg><option>--xml</option></arg>
-  <arg><option>--json</option></arg>
-  <arg>
-    <group choice='req'>
-      <arg choice='plain'><option>--prebuilt-only</option></arg>
-      <arg choice='plain'><option>-b</option></arg>
-    </group>
-  </arg>
-
-  <arg>
-    <group choice='req'>
-      <arg choice='plain'><option>--attr</option></arg>
-      <arg choice='plain'><option>-A</option></arg>
-    </group>
-    <replaceable>attribute-path</replaceable>
-  </arg>
-
-  <sbr />
-
-  <arg choice='plain' rep='repeat'><replaceable>names</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-
-<refsection><title>Description</title>
-
-<para>The query operation displays information about either the store
-paths that are installed in the current generation of the active
-profile (<option>--installed</option>), or the derivations that are
-available for installation in the active Nix expression
-(<option>--available</option>).  It only prints information about
-derivations whose symbolic name matches one of
-<replaceable>names</replaceable>.</para>
-
-<para>The derivations are sorted by their <literal>name</literal>
-attributes.</para>
-
-</refsection>
-
-
-<refsection><title>Source selection</title>
-
-<para>The following flags specify the set of things on which the query
-operates.</para>
-
-<variablelist>
-
-  <varlistentry><term><option>--installed</option></term>
-
-    <listitem><para>The query operates on the store paths that are
-    installed in the current generation of the active profile.  This
-    is the default.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--available</option></term>
-    <term><option>-a</option></term>
-
-    <listitem><para>The query operates on the derivations that are
-    available in the active Nix expression.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Queries</title>
-
-<para>The following flags specify what information to display about
-the selected derivations.  Multiple flags may be specified, in which
-case the information is shown in the order given here.  Note that the
-name of the derivation is shown unless <option>--no-name</option> is
-specified.</para>
-
-<!-- TODO: fix the terminology here; i.e., derivations, store paths,
-user environment elements, etc. -->
-
-<variablelist>
-
-  <varlistentry><term><option>--xml</option></term>
-
-    <listitem><para>Print the result in an XML representation suitable
-    for automatic processing by other tools.  The root element is
-    called <literal>items</literal>, which contains a
-    <literal>item</literal> element for each available or installed
-    derivation.  The fields discussed below are all stored in
-    attributes of the <literal>item</literal>
-    elements.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--json</option></term>
-
-    <listitem><para>Print the result in a JSON representation suitable
-    for automatic processing by other tools.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--prebuilt-only</option> / <option>-b</option></term>
-
-    <listitem><para>Show only derivations for which a substitute is
-    registered, i.e., there is a pre-built binary available that can
-    be downloaded in lieu of building the derivation.  Thus, this
-    shows all packages that probably can be installed
-    quickly.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--status</option></term>
-    <term><option>-s</option></term>
-
-    <listitem><para>Print the <emphasis>status</emphasis> of the
-    derivation.  The status consists of three characters.  The first
-    is <literal>I</literal> or <literal>-</literal>, indicating
-    whether the derivation is currently installed in the current
-    generation of the active profile.  This is by definition the case
-    for <option>--installed</option>, but not for
-    <option>--available</option>.  The second is <literal>P</literal>
-    or <literal>-</literal>, indicating whether the derivation is
-    present on the system.  This indicates whether installation of an
-    available derivation will require the derivation to be built.  The
-    third is <literal>S</literal> or <literal>-</literal>, indicating
-    whether a substitute is available for the
-    derivation.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--attr-path</option></term>
-    <term><option>-P</option></term>
-
-    <listitem><para>Print the <emphasis>attribute path</emphasis> of
-    the derivation, which can be used to unambiguously select it using
-    the <link linkend="opt-attr"><option>--attr</option> option</link>
-    available in commands that install derivations like
-    <literal>nix-env --install</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--no-name</option></term>
-
-    <listitem><para>Suppress printing of the <literal>name</literal>
-    attribute of each derivation.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--compare-versions</option> /
-  <option>-c</option></term>
-
-    <listitem><para>Compare installed versions to available versions,
-    or vice versa (if <option>--available</option> is given).  This is
-    useful for quickly seeing whether upgrades for installed
-    packages are available in a Nix expression.  A column is added
-    with the following meaning:
-
-    <variablelist>
-
-      <varlistentry><term><literal>&lt;</literal> <replaceable>version</replaceable></term>
-
-        <listitem><para>A newer version of the package is available
-        or installed.</para></listitem>
-
-      </varlistentry>
-
-      <varlistentry><term><literal>=</literal> <replaceable>version</replaceable></term>
-
-        <listitem><para>At most the same version of the package is
-        available or installed.</para></listitem>
-
-      </varlistentry>
-
-      <varlistentry><term><literal>></literal> <replaceable>version</replaceable></term>
-
-        <listitem><para>Only older versions of the package are
-        available or installed.</para></listitem>
-
-      </varlistentry>
-
-      <varlistentry><term><literal>- ?</literal></term>
-
-        <listitem><para>No version of the package is available or
-        installed.</para></listitem>
-
-      </varlistentry>
-
-    </variablelist>
-
-    </para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--system</option></term>
-
-    <listitem><para>Print the <literal>system</literal> attribute of
-    the derivation.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--drv-path</option></term>
-
-    <listitem><para>Print the path of the store
-    derivation.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--out-path</option></term>
-
-    <listitem><para>Print the output path of the
-    derivation.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--description</option></term>
-
-    <listitem><para>Print a short (one-line) description of the
-    derivation, if available.  The description is taken from the
-    <literal>meta.description</literal> attribute of the
-    derivation.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--meta</option></term>
-
-    <listitem><para>Print all of the meta-attributes of the
-    derivation.  This option is only available with
-    <option>--xml</option> or <option>--json</option>.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<para>To show installed packages:
-
-<screen>
-$ nix-env -q
-bison-1.875c
-docbook-xml-4.2
-firefox-1.0.4
-MPlayer-1.0pre7
-ORBit2-2.8.3
-<replaceable>…</replaceable>
-</screen>
-
-</para>
-
-<para>To show available packages:
-
-<screen>
-$ nix-env -qa
-firefox-1.0.7
-GConf-2.4.0.1
-MPlayer-1.0pre7
-ORBit2-2.8.3
-<replaceable>…</replaceable>
-</screen>
-
-</para>
-
-<para>To show the status of available packages:
-
-<screen>
-$ nix-env -qas
--P- firefox-1.0.7   <lineannotation>(not installed but present)</lineannotation>
---S GConf-2.4.0.1   <lineannotation>(not present, but there is a substitute for fast installation)</lineannotation>
---S MPlayer-1.0pre3 <lineannotation>(i.e., this is not the installed MPlayer, even though the version is the same!)</lineannotation>
-IP- ORBit2-2.8.3    <lineannotation>(installed and by definition present)</lineannotation>
-<replaceable>…</replaceable>
-</screen>
-
-</para>
-
-<para>To show available packages in the Nix expression <filename>foo.nix</filename>:
-
-<screen>
-$ nix-env -f ./foo.nix -qa
-foo-1.2.3
-</screen>
-
-</para>
-
-<para>To compare installed versions to what’s available:
-
-<screen>
-$ nix-env -qc
-<replaceable>...</replaceable>
-acrobat-reader-7.0 - ?      <lineannotation>(package is not available at all)</lineannotation>
-autoconf-2.59      = 2.59   <lineannotation>(same version)</lineannotation>
-firefox-1.0.4      &lt; 1.0.7  <lineannotation>(a more recent version is available)</lineannotation>
-<replaceable>...</replaceable>
-</screen>
-
-</para>
-
-<para>To show all packages with β€œ<literal>zip</literal>” in the name:
-
-<screen>
-$ nix-env -qa '.*zip.*'
-bzip2-1.0.6
-gzip-1.6
-zip-3.0
-<replaceable>…</replaceable>
-</screen>
-
-</para>
-
-<para>To show all packages with β€œ<literal>firefox</literal>” or
-β€œ<literal>chromium</literal>” in the name:
-
-<screen>
-$ nix-env -qa '.*(firefox|chromium).*'
-chromium-37.0.2062.94
-chromium-beta-38.0.2125.24
-firefox-32.0.3
-firefox-with-plugins-13.0.1
-<replaceable>…</replaceable>
-</screen>
-
-</para>
-
-<para>To show all packages in the latest revision of the Nixpkgs
-repository:
-
-<screen>
-$ nix-env -f https://github.com/NixOS/nixpkgs/archive/master.tar.gz -qa
-</screen>
-
-</para>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--switch-profile</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <group choice='req'>
-    <arg choice='plain'><option>--switch-profile</option></arg>
-    <arg choice='plain'><option>-S</option></arg>
-  </group>
-  <arg choice='req'><replaceable>path</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-
-<refsection><title>Description</title>
-
-<para>This operation makes <replaceable>path</replaceable> the current
-profile for the user.  That is, the symlink
-<filename>~/.nix-profile</filename> is made to point to
-<replaceable>path</replaceable>.</para>
-
-</refsection>
-
-<refsection><title>Examples</title>
-
-<screen>
-$ nix-env -S ~/my-profile</screen>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--list-generations</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <arg choice='plain'><option>--list-generations</option></arg>
-</cmdsynopsis>
-
-</refsection>
-
-
-<refsection><title>Description</title>
-
-<para>This operation print a list of all the currently existing
-generations for the active profile.  These may be switched to using
-the <option>--switch-generation</option> operation.  It also prints
-the creation date of the generation, and indicates the current
-generation.</para>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<screen>
-$ nix-env --list-generations
-  95   2004-02-06 11:48:24
-  96   2004-02-06 11:49:01
-  97   2004-02-06 16:22:45
-  98   2004-02-06 16:24:33   (current)</screen>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--delete-generations</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <arg choice='plain'><option>--delete-generations</option></arg>
-  <arg choice='plain' rep='repeat'><replaceable>generations</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-
-<refsection><title>Description</title>
-
-<para>This operation deletes the specified generations of the current
-profile.  The generations can be a list of generation numbers, the
-special value <literal>old</literal> to delete all non-current
-generations,  a value such as <literal>30d</literal> to delete all
-generations older than the specified number of days (except for the
-generation that was active at that point in time), or a value such as
-<literal>+5</literal> to keep the last <literal>5</literal> generations
-ignoring any newer than current, e.g., if <literal>30</literal> is the current
-generation <literal>+5</literal> will delete generation <literal>25</literal>
-and all older generations.
-Periodically deleting old generations is important to make garbage collection
-effective.</para>
-
-</refsection>
-
-<refsection><title>Examples</title>
-
-<screen>
-$ nix-env --delete-generations 3 4 8
-
-$ nix-env --delete-generations +5
-
-$ nix-env --delete-generations 30d
-
-$ nix-env -p other_profile --delete-generations old</screen>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--switch-generation</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <group choice='req'>
-    <arg choice='plain'><option>--switch-generation</option></arg>
-    <arg choice='plain'><option>-G</option></arg>
-  </group>
-  <arg choice='req'><replaceable>generation</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-
-<refsection><title>Description</title>
-
-<para>This operation makes generation number
-<replaceable>generation</replaceable> the current generation of the
-active profile.  That is, if the
-<filename><replaceable>profile</replaceable></filename> is the path to
-the active profile, then the symlink
-<filename><replaceable>profile</replaceable></filename> is made to
-point to
-<filename><replaceable>profile</replaceable>-<replaceable>generation</replaceable>-link</filename>,
-which is in turn a symlink to the actual user environment in the Nix
-store.</para>
-
-<para>Switching will fail if the specified generation does not exist.</para>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<screen>
-$ nix-env -G 42
-switching from generation 50 to 42</screen>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--rollback</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-env</command>
-  <arg choice='plain'><option>--rollback</option></arg>
-</cmdsynopsis>
-
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>This operation switches to the β€œprevious” generation of the
-active profile, that is, the highest numbered generation lower than
-the current generation, if it exists.  It is just a convenience
-wrapper around <option>--list-generations</option> and
-<option>--switch-generation</option>.</para>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<screen>
-$ nix-env --rollback
-switching from generation 92 to 91
-
-$ nix-env --rollback
-error: no generation older than the current (91) exists</screen>
-
-</refsection>
-
-</refsection>
-
-
-<refsection condition="manpage"><title>Environment variables</title>
-
-<variablelist>
-
-  <varlistentry><term><envar>NIX_PROFILE</envar></term>
-
-    <listitem><para>Location of the Nix profile.  Defaults to the
-    target of the symlink <filename>~/.nix-profile</filename>, if it
-    exists, or <filename>/nix/var/nix/profiles/default</filename>
-    otherwise.</para></listitem>
-
-  </varlistentry>
-
-  <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-hash.xml b/third_party/nix/doc/manual/command-ref/nix-hash.xml
deleted file mode 100644
index 80263e18e3..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-hash.xml
+++ /dev/null
@@ -1,176 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-hash">
-  
-<refmeta>
-  <refentrytitle>nix-hash</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-hash</refname>
-  <refpurpose>compute the cryptographic hash of a path</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-hash</command>
-    <arg><option>--flat</option></arg>
-    <arg><option>--base32</option></arg>
-    <arg><option>--truncate</option></arg>
-    <arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg>
-    <arg choice='plain' rep='repeat'><replaceable>path</replaceable></arg>
-  </cmdsynopsis>
-  <cmdsynopsis>
-    <command>nix-hash</command>
-    <arg choice='plain'><option>--to-base16</option></arg>
-    <arg choice='plain' rep='repeat'><replaceable>hash</replaceable></arg>
-  </cmdsynopsis>
-  <cmdsynopsis>
-    <command>nix-hash</command>
-    <arg choice='plain'><option>--to-base32</option></arg>
-    <arg choice='plain' rep='repeat'><replaceable>hash</replaceable></arg>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-
-<refsection><title>Description</title>
-
-<para>The command <command>nix-hash</command> computes the
-cryptographic hash of the contents of each
-<replaceable>path</replaceable> and prints it on standard output.  By
-default, it computes an MD5 hash, but other hash algorithms are
-available as well.  The hash is printed in hexadecimal.  To generate
-the same hash as <command>nix-prefetch-url</command> you have to
-specify multiple arguments, see below for an example.</para>
-
-<para>The hash is computed over a <emphasis>serialisation</emphasis>
-of each path: a dump of the file system tree rooted at the path.  This
-allows directories and symlinks to be hashed as well as regular files.
-The dump is in the <emphasis>NAR format</emphasis> produced by <link
-linkend="refsec-nix-store-dump"><command>nix-store</command>
-<option>--dump</option></link>.  Thus, <literal>nix-hash
-<replaceable>path</replaceable></literal> yields the same
-cryptographic hash as <literal>nix-store --dump
-<replaceable>path</replaceable> | md5sum</literal>.</para>
-
-</refsection>
-
-
-<refsection><title>Options</title>
-
-<variablelist>
-  
-  <varlistentry><term><option>--flat</option></term>
-
-    <listitem><para>Print the cryptographic hash of the contents of
-    each regular file <replaceable>path</replaceable>.  That is, do
-    not compute the hash over the dump of
-    <replaceable>path</replaceable>.  The result is identical to that
-    produced by the GNU commands <command>md5sum</command> and
-    <command>sha1sum</command>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--base32</option></term>
-
-    <listitem><para>Print the hash in a base-32 representation rather
-    than hexadecimal.  This base-32 representation is more compact and
-    can be used in Nix expressions (such as in calls to
-    <function>fetchurl</function>).</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--truncate</option></term>
-
-    <listitem><para>Truncate hashes longer than 160 bits (such as
-    SHA-256) to 160 bits.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
-
-    <listitem><para>Use the specified cryptographic hash algorithm,
-    which can be one of <literal>md5</literal>,
-    <literal>sha1</literal>, and
-    <literal>sha256</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--to-base16</option></term>
-
-    <listitem><para>Don’t hash anything, but convert the base-32 hash
-    representation <replaceable>hash</replaceable> to
-    hexadecimal.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--to-base32</option></term>
-
-    <listitem><para>Don’t hash anything, but convert the hexadecimal
-    hash representation <replaceable>hash</replaceable> to
-    base-32.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<para>Computing the same hash as <command>nix-prefetch-url</command>:
-<screen>
-$ nix-prefetch-url file://&lt;(echo test)
-1lkgqb6fclns49861dwk9rzb6xnfkxbpws74mxnx01z9qyv1pjpj
-$ nix-hash --type sha256 --flat --base32 &lt;(echo test)
-1lkgqb6fclns49861dwk9rzb6xnfkxbpws74mxnx01z9qyv1pjpj
-</screen>
-</para>
-
-<para>Computing hashes:
-
-<screen>
-$ mkdir test
-$ echo "hello" > test/world
-
-$ nix-hash test/ <lineannotation>(MD5 hash; default)</lineannotation>
-8179d3caeff1869b5ba1744e5a245c04
-
-$ nix-store --dump test/ | md5sum <lineannotation>(for comparison)</lineannotation>
-8179d3caeff1869b5ba1744e5a245c04  -
-
-$ nix-hash --type sha1 test/
-e4fd8ba5f7bbeaea5ace89fe10255536cd60dab6
-
-$ nix-hash --type sha1 --base32 test/
-nvd61k9nalji1zl9rrdfmsmvyyjqpzg4
-
-$ nix-hash --type sha256 --flat test/
-error: reading file `test/': Is a directory
-
-$ nix-hash --type sha256 --flat test/world
-5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03</screen>
-
-</para>
-
-<para>Converting between hexadecimal and base-32:
-
-<screen>
-$ nix-hash --type sha1 --to-base32 e4fd8ba5f7bbeaea5ace89fe10255536cd60dab6
-nvd61k9nalji1zl9rrdfmsmvyyjqpzg4
-
-$ nix-hash --type sha1 --to-base16 nvd61k9nalji1zl9rrdfmsmvyyjqpzg4
-e4fd8ba5f7bbeaea5ace89fe10255536cd60dab6</screen>
-
-</para>
-
-</refsection>
-
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-instantiate.xml b/third_party/nix/doc/manual/command-ref/nix-instantiate.xml
deleted file mode 100644
index 3fd2ef2a95..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-instantiate.xml
+++ /dev/null
@@ -1,278 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-instantiate">
-
-<refmeta>
-  <refentrytitle>nix-instantiate</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-instantiate</refname>
-  <refpurpose>instantiate store derivations from Nix expressions</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-instantiate</command>
-    <group>
-      <arg choice='plain'><option>--parse</option></arg>
-      <arg choice='plain'>
-        <option>--eval</option>
-        <arg><option>--strict</option></arg>
-        <arg><option>--json</option></arg>
-        <arg><option>--xml</option></arg>
-      </arg>
-    </group>
-    <arg><option>--read-write-mode</option></arg>
-    <arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
-    <arg>
-      <group choice='req'>
-        <arg choice='plain'><option>--attr</option></arg>
-        <arg choice='plain'><option>-A</option></arg>
-      </group>
-      <replaceable>attrPath</replaceable>
-    </arg>
-    <arg><option>--add-root</option> <replaceable>path</replaceable></arg>
-    <arg><option>--indirect</option></arg>
-    <arg><option>--<arg>no</arg>trace-file-access</option></arg>
-    <group>
-      <arg choice='plain'><option>--expr</option></arg>
-      <arg choice='plain'><option>-E</option></arg>
-    </group>
-    <arg choice='plain' rep='repeat'><replaceable>files</replaceable></arg>
-  </cmdsynopsis>
-  <cmdsynopsis>
-    <command>nix-instantiate</command>
-    <arg choice='plain'><option>--find-file</option></arg>
-    <arg choice='plain' rep='repeat'><replaceable>files</replaceable></arg>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-
-<refsection><title>Description</title>
-
-<para>The command <command>nix-instantiate</command> generates <link
-linkend="gloss-derivation">store derivations</link> from (high-level)
-Nix expressions.  It evaluates the Nix expressions in each of
-<replaceable>files</replaceable> (which defaults to
-<replaceable>./default.nix</replaceable>).  Each top-level expression
-should evaluate to a derivation, a list of derivations, or a set of
-derivations.  The paths of the resulting store derivations are printed
-on standard output.</para>
-
-<para>If <replaceable>files</replaceable> is the character
-<literal>-</literal>, then a Nix expression will be read from standard
-input.</para>
-
-<para condition="manual">See also <xref linkend="sec-common-options"
-/> for a list of common options.</para>
-
-</refsection>
-
-
-<refsection><title>Options</title>
-
-<variablelist>
-
-  <varlistentry>
-    <term><option>--add-root</option> <replaceable>path</replaceable></term>
-    <term><option>--indirect</option></term>
-
-    <listitem><para>See the <link linkend="opt-add-root">corresponding
-    options</link> in <command>nix-store</command>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--parse</option></term>
-
-    <listitem><para>Just parse the input files, and print their
-    abstract syntax trees on standard output in ATerm
-    format.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--eval</option></term>
-
-    <listitem><para>Just parse and evaluate the input files, and print
-    the resulting values on standard output.  No instantiation of
-    store derivations takes place.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--find-file</option></term>
-
-    <listitem><para>Look up the given files in Nix’s search path (as
-    specified by the <envar linkend="env-NIX_PATH">NIX_PATH</envar>
-    environment variable).  If found, print the corresponding absolute
-    paths on standard output.  For instance, if
-    <envar>NIX_PATH</envar> is
-    <literal>nixpkgs=/home/alice/nixpkgs</literal>, then
-    <literal>nix-instantiate --find-file nixpkgs/default.nix</literal>
-    will print
-    <literal>/home/alice/nixpkgs/default.nix</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--strict</option></term>
-
-    <listitem><para>When used with <option>--eval</option>,
-    recursively evaluate list elements and attributes.  Normally, such
-    sub-expressions are left unevaluated (since the Nix expression
-    language is lazy).</para>
-
-    <warning><para>This option can cause non-termination, because lazy
-    data structures can be infinitely large.</para></warning>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--<arg>no</arg>trace-file-access</option></term>
-    <listitem><para>While instantiating the expression, the evaluator will
-    print the full path to any files it reads with the prefix
-    <envar>trace-file-access: </envar> to the standard error.</para>
-    </listitem>
-  </varlistentry>
-
-  <varlistentry><term><option>--json</option></term>
-
-    <listitem><para>When used with <option>--eval</option>, print the resulting
-    value as an JSON representation of the abstract syntax tree rather
-    than as an ATerm.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--xml</option></term>
-
-    <listitem><para>When used with <option>--eval</option>, print the resulting
-    value as an XML representation of the abstract syntax tree rather than as
-    an ATerm. The schema is the same as that used by the <link
-    linkend="builtin-toXML"><function>toXML</function> built-in</link>.
-    </para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--read-write-mode</option></term>
-
-    <listitem><para>When used with <option>--eval</option>, perform
-    evaluation in read/write mode so nix language features that
-    require it will still work (at the cost of needing to do
-    instantiation of every evaluated derivation). If this option is
-    not enabled, there may be uninstantiated store paths in the final
-    output.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-<variablelist condition="manpage">
-  <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<para>Instantiating store derivations from a Nix expression, and
-building them using <command>nix-store</command>:
-
-<screen>
-$ nix-instantiate test.nix <lineannotation>(instantiate)</lineannotation>
-/nix/store/cigxbmvy6dzix98dxxh9b6shg7ar5bvs-perl-BerkeleyDB-0.26.drv
-
-$ nix-store -r $(nix-instantiate test.nix) <lineannotation>(build)</lineannotation>
-<replaceable>...</replaceable>
-/nix/store/qhqk4n8ci095g3sdp93x7rgwyh9rdvgk-perl-BerkeleyDB-0.26 <lineannotation>(output path)</lineannotation>
-
-$ ls -l /nix/store/qhqk4n8ci095g3sdp93x7rgwyh9rdvgk-perl-BerkeleyDB-0.26
-dr-xr-xr-x    2 eelco    users        4096 1970-01-01 01:00 lib
-...</screen>
-
-</para>
-
-<para>You can also give a Nix expression on the command line:
-
-<screen>
-$ nix-instantiate -E 'with import &lt;nixpkgs> { }; hello'
-/nix/store/j8s4zyv75a724q38cb0r87rlczaiag4y-hello-2.8.drv
-</screen>
-
-This is equivalent to:
-
-<screen>
-$ nix-instantiate '&lt;nixpkgs>' -A hello
-</screen>
-
-</para>
-
-<para>Parsing and evaluating Nix expressions:
-
-<screen>
-$ nix-instantiate --parse -E '1 + 2'
-1 + 2
-
-$ nix-instantiate --eval -E '1 + 2'
-3
-
-$ nix-instantiate --eval --xml -E '1 + 2'
-<![CDATA[<?xml version='1.0' encoding='utf-8'?>
-<expr>
-  <int value="3" />
-</expr>]]></screen>
-
-</para>
-
-<para>The difference between non-strict and strict evaluation:
-
-<screen>
-$ nix-instantiate --eval --xml -E 'rec { x = "foo"; y = x; }'
-<replaceable>...</replaceable><![CDATA[
-  <attr name="x">
-    <string value="foo" />
-  </attr>
-  <attr name="y">
-    <unevaluated />
-  </attr>]]>
-<replaceable>...</replaceable></screen>
-
-Note that <varname>y</varname> is left unevaluated (the XML
-representation doesn’t attempt to show non-normal forms).
-
-<screen>
-$ nix-instantiate --eval --xml --strict -E 'rec { x = "foo"; y = x; }'
-<replaceable>...</replaceable><![CDATA[
-  <attr name="x">
-    <string value="foo" />
-  </attr>
-  <attr name="y">
-    <string value="foo" />
-  </attr>]]>
-<replaceable>...</replaceable></screen>
-
-</para>
-
-</refsection>
-
-<refsection><title>Conformance</title>
-  <para>The <option>--trace-file-access</option> option is a nonstandard
-  extension added by Tvix in 2020.</para>
-</refsection>
-
-<refsection condition="manpage"><title>Environment variables</title>
-
-<variablelist>
-  <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-prefetch-url.xml b/third_party/nix/doc/manual/command-ref/nix-prefetch-url.xml
deleted file mode 100644
index 621ded72ec..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-prefetch-url.xml
+++ /dev/null
@@ -1,131 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-prefetch-url">
-
-<refmeta>
-  <refentrytitle>nix-prefetch-url</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-prefetch-url</refname>
-  <refpurpose>copy a file from a URL into the store and print its hash</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-prefetch-url</command>
-    <arg><option>--version</option></arg>
-    <arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg>
-    <arg><option>--print-path</option></arg>
-    <arg><option>--unpack</option></arg>
-    <arg><option>--name</option> <replaceable>name</replaceable></arg>
-    <arg choice='plain'><replaceable>url</replaceable></arg>
-    <arg><replaceable>hash</replaceable></arg>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-<refsection><title>Description</title>
-
-<para>The command <command>nix-prefetch-url</command> downloads the
-file referenced by the URL <replaceable>url</replaceable>, prints its
-cryptographic hash, and copies it into the Nix store.  The file name
-in the store is
-<filename><replaceable>hash</replaceable>-<replaceable>baseName</replaceable></filename>,
-where <replaceable>baseName</replaceable> is everything following the
-final slash in <replaceable>url</replaceable>.</para>
-
-<para>This command is just a convenience for Nix expression writers.
-Often a Nix expression fetches some source distribution from the
-network using the <literal>fetchurl</literal> expression contained in
-Nixpkgs.  However, <literal>fetchurl</literal> requires a
-cryptographic hash.  If you don't know the hash, you would have to
-download the file first, and then <literal>fetchurl</literal> would
-download it again when you build your Nix expression.  Since
-<literal>fetchurl</literal> uses the same name for the downloaded file
-as <command>nix-prefetch-url</command>, the redundant download can be
-avoided.</para>
-
-<para>If <replaceable>hash</replaceable> is specified, then a download
-is not performed if the Nix store already contains a file with the
-same hash and base name.  Otherwise, the file is downloaded, and an
-error is signaled if the actual hash of the file does not match the
-specified hash.</para>
-
-<para>This command prints the hash on standard output.  Additionally,
-if the option <option>--print-path</option> is used, the path of the
-downloaded file in the Nix store is also printed.</para>
-
-</refsection>
-
-
-<refsection><title>Options</title>
-
-<variablelist>
-
-  <varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
-
-    <listitem><para>Use the specified cryptographic hash algorithm,
-    which can be one of <literal>md5</literal>,
-    <literal>sha1</literal>, and
-    <literal>sha256</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--print-path</option></term>
-
-    <listitem><para>Print the store path of the downloaded file on
-    standard output.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--unpack</option></term>
-
-    <listitem><para>Unpack the archive (which must be a tarball or zip
-    file) and add the result to the Nix store. The resulting hash can
-    be used with functions such as Nixpkgs’s
-    <varname>fetchzip</varname> or
-    <varname>fetchFromGitHub</varname>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--name</option> <replaceable>name</replaceable></term>
-
-    <listitem><para>Override the name of the file in the Nix store. By
-    default, this is
-    <literal><replaceable>hash</replaceable>-<replaceable>basename</replaceable></literal>,
-    where <replaceable>basename</replaceable> is the last component of
-    <replaceable>url</replaceable>. Overriding the name is necessary
-    when <replaceable>basename</replaceable> contains characters that
-    are not allowed in Nix store paths.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<screen>
-$ nix-prefetch-url ftp://ftp.gnu.org/pub/gnu/hello/hello-2.10.tar.gz
-0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i
-
-$ nix-prefetch-url --print-path mirror://gnu/hello/hello-2.10.tar.gz
-0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i
-/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz
-
-$ nix-prefetch-url --unpack --print-path https://github.com/NixOS/patchelf/archive/0.8.tar.gz
-079agjlv0hrv7fxnx9ngipx14gyncbkllxrp9cccnh3a50fxcmy7
-/nix/store/19zrmhm3m40xxaw81c8cqm6aljgrnwj2-0.8.tar.gz
-</screen>
-
-</refsection>
-
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-shell.xml b/third_party/nix/doc/manual/command-ref/nix-shell.xml
deleted file mode 100644
index bb4a4e4201..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-shell.xml
+++ /dev/null
@@ -1,397 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-shell">
-
-<refmeta>
-  <refentrytitle>nix-shell</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-shell</refname>
-  <refpurpose>start an interactive shell based on a Nix expression</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-shell</command>
-    <arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
-    <arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
-    <arg>
-      <group choice='req'>
-        <arg choice='plain'><option>--attr</option></arg>
-        <arg choice='plain'><option>-A</option></arg>
-      </group>
-      <replaceable>attrPath</replaceable>
-    </arg>
-    <arg><option>--command</option> <replaceable>cmd</replaceable></arg>
-    <arg><option>--run</option> <replaceable>cmd</replaceable></arg>
-    <arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
-    <arg><option>--pure</option></arg>
-    <arg><option>--keep</option> <replaceable>name</replaceable></arg>
-    <group choice='req'>
-      <arg choice='plain'>
-        <group choice='req'>
-          <arg choice='plain'><option>--packages</option></arg>
-          <arg choice='plain'><option>-p</option></arg>
-        </group>
-        <arg choice='plain' rep='repeat'><replaceable>packages</replaceable></arg>
-      </arg>
-      <arg><replaceable>path</replaceable></arg>
-    </group>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-<refsection><title>Description</title>
-
-<para>The command <command>nix-shell</command> will build the
-dependencies of the specified derivation, but not the derivation
-itself.  It will then start an interactive shell in which all
-environment variables defined by the derivation
-<replaceable>path</replaceable> have been set to their corresponding
-values, and the script <literal>$stdenv/setup</literal> has been
-sourced.  This is useful for reproducing the environment of a
-derivation for development.</para>
-
-<para>If <replaceable>path</replaceable> is not given,
-<command>nix-shell</command> defaults to
-<filename>shell.nix</filename> if it exists, and
-<filename>default.nix</filename> otherwise.</para>
-
-<para>If <replaceable>path</replaceable> starts with
-<literal>http://</literal> or <literal>https://</literal>, it is
-interpreted as the URL of a tarball that will be downloaded and
-unpacked to a temporary location. The tarball must include a single
-top-level directory containing at least a file named
-<filename>default.nix</filename>.</para>
-
-<para>If the derivation defines the variable
-<varname>shellHook</varname>, it will be evaluated after
-<literal>$stdenv/setup</literal> has been sourced.  Since this hook is
-not executed by regular Nix builds, it allows you to perform
-initialisation specific to <command>nix-shell</command>.  For example,
-the derivation attribute
-
-<programlisting>
-shellHook =
-  ''
-    echo "Hello shell"
-  '';
-</programlisting>
-
-will cause <command>nix-shell</command> to print <literal>Hello shell</literal>.</para>
-
-</refsection>
-
-
-<refsection><title>Options</title>
-
-<para>All options not listed here are passed to <command>nix-store
---realise</command>, except for <option>--arg</option> and
-<option>--attr</option> / <option>-A</option> which are passed to
-<command>nix-instantiate</command>.  <phrase condition="manual">See
-also <xref linkend="sec-common-options" />.</phrase></para>
-
-<variablelist>
-
-  <varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
-
-    <listitem><para>In the environment of the derivation, run the
-    shell command <replaceable>cmd</replaceable>. This command is
-    executed in an interactive shell. (Use <option>--run</option> to
-    use a non-interactive shell instead.) However, a call to
-    <literal>exit</literal> is implicitly added to the command, so the
-    shell will exit after running the command. To prevent this, add
-    <literal>return</literal> at the end; e.g. <literal>--command
-    "echo Hello; return"</literal> will print <literal>Hello</literal>
-    and then drop you into the interactive shell. This can be useful
-    for doing any additional initialisation.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--run</option> <replaceable>cmd</replaceable></term>
-
-    <listitem><para>Like <option>--command</option>, but executes the
-    command in a non-interactive shell. This means (among other
-    things) that if you hit Ctrl-C while the command is running, the
-    shell exits.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--exclude</option> <replaceable>regexp</replaceable></term>
-
-    <listitem><para>Do not build any dependencies whose store path
-    matches the regular expression <replaceable>regexp</replaceable>.
-    This option may be specified multiple times.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--pure</option></term>
-
-    <listitem><para>If this flag is specified, the environment is
-    almost entirely cleared before the interactive shell is started,
-    so you get an environment that more closely corresponds to the
-    β€œreal” Nix build.  A few variables, in particular
-    <envar>HOME</envar>, <envar>USER</envar> and
-    <envar>DISPLAY</envar>, are retained.  Note that
-    <filename>~/.bashrc</filename> and (depending on your Bash
-    installation) <filename>/etc/bashrc</filename> are still sourced,
-    so any variables set there will affect the interactive
-    shell.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--packages</option> / <option>-p</option> <replaceable>packages</replaceable>…</term>
-
-    <listitem><para>Set up an environment in which the specified
-    packages are present.  The command line arguments are interpreted
-    as attribute names inside the Nix Packages collection.  Thus,
-    <literal>nix-shell -p libjpeg openjdk</literal> will start a shell
-    in which the packages denoted by the attribute names
-    <varname>libjpeg</varname> and <varname>openjdk</varname> are
-    present.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>-i</option> <replaceable>interpreter</replaceable></term>
-
-    <listitem><para>The chained script interpreter to be invoked by
-    <command>nix-shell</command>. Only applicable in
-    <literal>#!</literal>-scripts (described <link
-    linkend="ssec-nix-shell-shebang">below</link>).</para>
-
-    </listitem></varlistentry>
-
-  <varlistentry><term><option>--keep</option> <replaceable>name</replaceable></term>
-
-    <listitem><para>When a <option>--pure</option> shell is started,
-    keep the listed environment variables.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-<para>The following common options are supported:</para>
-
-<variablelist condition="manpage">
-  <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Environment variables</title>
-
-<variablelist>
-
-  <varlistentry><term><envar>NIX_BUILD_SHELL</envar></term>
-    
-    <listitem><para>Shell used to start the interactive environment. 
-    Defaults to the <command>bash</command> found in <envar>PATH</envar>.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<para>To build the dependencies of the package Pan, and start an
-interactive shell in which to build it:
-
-<screen>
-$ nix-shell '&lt;nixpkgs>' -A pan
-[nix-shell]$ unpackPhase
-[nix-shell]$ cd pan-*
-[nix-shell]$ configurePhase
-[nix-shell]$ buildPhase
-[nix-shell]$ ./pan/gui/pan
-</screen>
-
-To clear the environment first, and do some additional automatic
-initialisation of the interactive shell:
-
-<screen>
-$ nix-shell '&lt;nixpkgs>' -A pan --pure \
-    --command 'export NIX_DEBUG=1; export NIX_CORES=8; return'
-</screen>
-
-Nix expressions can also be given on the command line.  For instance,
-the following starts a shell containing the packages
-<literal>sqlite</literal> and <literal>libX11</literal>:
-
-<screen>
-$ nix-shell -E 'with import &lt;nixpkgs> { }; runCommand "dummy" { buildInputs = [ sqlite xorg.libX11 ]; } ""'
-</screen>
-
-A shorter way to do the same is:
-
-<screen>
-$ nix-shell -p sqlite xorg.libX11
-[nix-shell]$ echo $NIX_LDFLAGS
-… -L/nix/store/j1zg5v…-sqlite-3.8.0.2/lib -L/nix/store/0gmcz9…-libX11-1.6.1/lib …
-</screen>
-
-The <command>-p</command> flag looks up Nixpkgs in the Nix search
-path. You can override it by passing <option>-I</option> or setting
-<envar>NIX_PATH</envar>. For example, the following gives you a shell
-containing the Pan package from a specific revision of Nixpkgs:
-
-<screen>
-$ nix-shell -p pan -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/8a3eea054838b55aca962c3fbde9c83c102b8bf2.tar.gz
-
-[nix-shell:~]$ pan --version
-Pan 0.139
-</screen>
-
-</para>
-
-</refsection>
-
-
-<refsection xml:id="ssec-nix-shell-shebang"><title>Use as a <literal>#!</literal>-interpreter</title>
-
-<para>You can use <command>nix-shell</command> as a script interpreter
-to allow scripts written in arbitrary languages to obtain their own
-dependencies via Nix. This is done by starting the script with the
-following lines:
-
-<programlisting>
-#! /usr/bin/env nix-shell
-#! nix-shell -i <replaceable>real-interpreter</replaceable> -p <replaceable>packages</replaceable>
-</programlisting>
-
-where <replaceable>real-interpreter</replaceable> is the β€œreal” script
-interpreter that will be invoked by <command>nix-shell</command> after
-it has obtained the dependencies and initialised the environment, and
-<replaceable>packages</replaceable> are the attribute names of the
-dependencies in Nixpkgs.</para>
-
-<para>The lines starting with <literal>#! nix-shell</literal> specify
-<command>nix-shell</command> options (see above). Note that you cannot
-write <literal>#! /usr/bin/env nix-shell -i ...</literal> because
-many operating systems only allow one argument in
-<literal>#!</literal> lines.</para>
-
-<para>For example, here is a Python script that depends on Python and
-the <literal>prettytable</literal> package:
-
-<programlisting>
-#! /usr/bin/env nix-shell
-#! nix-shell -i python -p python pythonPackages.prettytable
-
-import prettytable
-
-# Print a simple table.
-t = prettytable.PrettyTable(["N", "N^2"])
-for n in range(1, 10): t.add_row([n, n * n])
-print t
-</programlisting>
-
-</para>
-
-<para>Similarly, the following is a Perl script that specifies that it
-requires Perl and the <literal>HTML::TokeParser::Simple</literal> and
-<literal>LWP</literal> packages:
-
-<programlisting>
-#! /usr/bin/env nix-shell
-#! nix-shell -i perl -p perl perlPackages.HTMLTokeParserSimple perlPackages.LWP
-
-use HTML::TokeParser::Simple;
-
-# Fetch nixos.org and print all hrefs.
-my $p = HTML::TokeParser::Simple->new(url => 'http://nixos.org/');
-
-while (my $token = $p->get_tag("a")) {
-    my $href = $token->get_attr("href");
-    print "$href\n" if $href;
-}
-</programlisting>
-
-</para>
-
-<para>Sometimes you need to pass a simple Nix expression to customize
-a package like Terraform:
-
-<programlisting><![CDATA[
-#! /usr/bin/env nix-shell
-#! nix-shell -i bash -p "terraform.withPlugins (plugins: [ plugins.openstack ])"
-
-terraform apply
-]]></programlisting>
-
-<note><para>You must use double quotes (<literal>"</literal>) when
-passing a simple Nix expression in a nix-shell shebang.</para></note>
-</para>
-
-<para>Finally, using the merging of multiple nix-shell shebangs the
-following Haskell script uses a specific branch of Nixpkgs/NixOS (the
-18.03 stable branch):
-
-<programlisting><![CDATA[
-#! /usr/bin/env nix-shell
-#! nix-shell -i runghc -p "haskellPackages.ghcWithPackages (ps: [ps.HTTP ps.tagsoup])"
-#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-18.03.tar.gz
-
-import Network.HTTP
-import Text.HTML.TagSoup
-
--- Fetch nixos.org and print all hrefs.
-main = do
-  resp <- Network.HTTP.simpleHTTP (getRequest "http://nixos.org/")
-  body <- getResponseBody resp
-  let tags = filter (isTagOpenName "a") $ parseTags body
-  let tags' = map (fromAttrib "href") tags
-  mapM_ putStrLn $ filter (/= "") tags'
-]]></programlisting>
-
-If you want to be even more precise, you can specify a specific
-revision of Nixpkgs:
-
-<programlisting>
-#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/0672315759b3e15e2121365f067c1c8c56bb4722.tar.gz
-</programlisting>
-
-</para>
-
-<para>The examples above all used <option>-p</option> to get
-dependencies from Nixpkgs. You can also use a Nix expression to build
-your own dependencies. For example, the Python example could have been
-written as:
-
-<programlisting>
-#! /usr/bin/env nix-shell
-#! nix-shell deps.nix -i python
-</programlisting>
-
-where the file <filename>deps.nix</filename> in the same directory
-as the <literal>#!</literal>-script contains:
-
-<programlisting>
-with import &lt;nixpkgs> {};
-
-runCommand "dummy" { buildInputs = [ python pythonPackages.prettytable ]; } ""
-</programlisting>
-
-</para>
-
-</refsection>
-
-
-<refsection condition="manpage"><title>Environment variables</title>
-
-<variablelist>
-  <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/nix-store.xml b/third_party/nix/doc/manual/command-ref/nix-store.xml
deleted file mode 100644
index 113a3c2e41..0000000000
--- a/third_party/nix/doc/manual/command-ref/nix-store.xml
+++ /dev/null
@@ -1,1525 +0,0 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-nix-store">
-
-<refmeta>
-  <refentrytitle>nix-store</refentrytitle>
-  <manvolnum>1</manvolnum>
-  <refmiscinfo class="source">Nix</refmiscinfo>
-  <refmiscinfo class="version"><xi:include href="../version.txt" parse="text"/></refmiscinfo>
-</refmeta>
-
-<refnamediv>
-  <refname>nix-store</refname>
-  <refpurpose>manipulate or query the Nix store</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
-    <arg><option>--add-root</option> <replaceable>path</replaceable></arg>
-    <arg><option>--indirect</option></arg>
-    <arg choice='plain'><replaceable>operation</replaceable></arg>
-    <arg rep='repeat'><replaceable>options</replaceable></arg>
-    <arg rep='repeat'><replaceable>arguments</replaceable></arg>
-  </cmdsynopsis>
-</refsynopsisdiv>
-
-
-<refsection><title>Description</title>
-
-<para>The command <command>nix-store</command> performs primitive
-operations on the Nix store.  You generally do not need to run this
-command manually.</para>
-
-<para><command>nix-store</command> takes exactly one
-<emphasis>operation</emphasis> flag which indicates the subcommand to
-be performed.  These are documented below.</para>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Common options</title>
-
-<para>This section lists the options that are common to all
-operations.  These options are allowed for every subcommand, though
-they may not always have an effect.  <phrase condition="manual">See
-also <xref linkend="sec-common-options" /> for a list of common
-options.</phrase></para>
-
-<variablelist>
-
-  <varlistentry xml:id="opt-add-root"><term><option>--add-root</option> <replaceable>path</replaceable></term>
-
-    <listitem><para>Causes the result of a realisation
-    (<option>--realise</option> and <option>--force-realise</option>)
-    to be registered as a root of the garbage collector<phrase
-    condition="manual"> (see <xref linkend="ssec-gc-roots"
-    />)</phrase>.  The root is stored in
-    <replaceable>path</replaceable>, which must be inside a directory
-    that is scanned for roots by the garbage collector (i.e.,
-    typically in a subdirectory of
-    <filename>/nix/var/nix/gcroots/</filename>)
-    <emphasis>unless</emphasis> the <option>--indirect</option> flag
-    is used.</para>
-
-    <para>If there are multiple results, then multiple symlinks will
-    be created by sequentially numbering symlinks beyond the first one
-    (e.g., <filename>foo</filename>, <filename>foo-2</filename>,
-    <filename>foo-3</filename>, and so on).</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--indirect</option></term>
-
-    <listitem>
-
-    <para>In conjunction with <option>--add-root</option>, this option
-    allows roots to be stored <emphasis>outside</emphasis> of the GC
-    roots directory.  This is useful for commands such as
-    <command>nix-build</command> that place a symlink to the build
-    result in the current directory; such a build result should not be
-    garbage-collected unless the symlink is removed.</para>
-
-    <para>The <option>--indirect</option> flag causes a uniquely named
-    symlink to <replaceable>path</replaceable> to be stored in
-    <filename>/nix/var/nix/gcroots/auto/</filename>.  For instance,
-
-    <screen>
-$ nix-store --add-root /home/eelco/bla/result --indirect -r <replaceable>...</replaceable>
-
-$ ls -l /nix/var/nix/gcroots/auto
-lrwxrwxrwx    1 ... 2005-03-13 21:10 dn54lcypm8f8... -> /home/eelco/bla/result
-
-$ ls -l /home/eelco/bla/result
-lrwxrwxrwx    1 ... 2005-03-13 21:10 /home/eelco/bla/result -> /nix/store/1r11343n6qd4...-f-spot-0.0.10</screen>
-
-    Thus, when <filename>/home/eelco/bla/result</filename> is removed,
-    the GC root in the <filename>auto</filename> directory becomes a
-    dangling symlink and will be ignored by the collector.</para>
-
-    <warning><para>Note that it is not possible to move or rename
-    indirect GC roots, since the symlink in the
-    <filename>auto</filename> directory will still point to the old
-    location.</para></warning>
-
-    </listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-<variablelist condition="manpage">
-  <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection xml:id='rsec-nix-store-realise'><title>Operation <option>--realise</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-store</command>
-  <group choice='req'>
-    <arg choice='plain'><option>--realise</option></arg>
-    <arg choice='plain'><option>-r</option></arg>
-  </group>
-  <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-  <arg><option>--dry-run</option></arg>
-</cmdsynopsis>
-
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--realise</option> essentially β€œbuilds”
-the specified store paths.  Realisation is a somewhat overloaded term:
-
-<itemizedlist>
-
-  <listitem><para>If the store path is a
-  <emphasis>derivation</emphasis>, realisation ensures that the output
-  paths of the derivation are <link
-  linkend="gloss-validity">valid</link> (i.e., the output path and its
-  closure exist in the file system).  This can be done in several
-  ways.  First, it is possible that the outputs are already valid, in
-  which case we are done immediately.  Otherwise, there may be <link
-  linkend="gloss-substitute">substitutes</link> that produce the
-  outputs (e.g., by downloading them).  Finally, the outputs can be
-  produced by performing the build action described by the
-  derivation.</para></listitem>
-
-  <listitem><para>If the store path is not a derivation, realisation
-  ensures that the specified path is valid (i.e., it and its closure
-  exist in the file system).  If the path is already valid, we are
-  done immediately.  Otherwise, the path and any missing paths in its
-  closure may be produced through substitutes.  If there are no
-  (successful) subsitutes, realisation fails.</para></listitem>
-
-</itemizedlist>
-
-</para>
-
-<para>The output path of each derivation is printed on standard
-output.  (For non-derivations argument, the argument itself is
-printed.)</para>
-
-<para>The following flags are available:</para>
-
-<variablelist>
-
-  <varlistentry><term><option>--dry-run</option></term>
-
-    <listitem><para>Print on standard error a description of what
-    packages would be built or downloaded, without actually performing
-    the operation.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--ignore-unknown</option></term>
-
-    <listitem><para>If a non-derivation path does not have a
-    substitute, then silently ignore it.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--check</option></term>
-
-    <listitem><para>This option allows you to check whether a
-    derivation is deterministic. It rebuilds the specified derivation
-    and checks whether the result is bitwise-identical with the
-    existing outputs, printing an error if that’s not the case. The
-    outputs of the specified derivation must already exist. When used
-    with <option>-K</option>, if an output path is not identical to
-    the corresponding output from the previous build, the new output
-    path is left in
-    <filename>/nix/store/<replaceable>name</replaceable>.check.</filename></para>
-
-    <para>See also the <option>build-repeat</option> configuration
-    option, which repeats a derivation a number of times and prevents
-    its outputs from being registered as β€œvalid” in the Nix store
-    unless they are identical.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-<para>Special exit codes:</para>
-
-<variablelist>
-
-  <varlistentry><term><literal>100</literal></term>
-    <listitem><para>Generic build failure, the builder process
-    returned with a non-zero exit code.</para></listitem>
-  </varlistentry>
-
-  <varlistentry><term><literal>101</literal></term>
-    <listitem><para>Build timeout, the build was aborted because it
-    did not complete within the specified <link
-    linkend='conf-timeout'><literal>timeout</literal></link>.
-    </para></listitem>
-  </varlistentry>
-
-  <varlistentry><term><literal>102</literal></term>
-    <listitem><para>Hash mismatch, the build output was rejected
-    because it does not match the specified <link
-    linkend="fixed-output-drvs"><varname>outputHash</varname></link>.
-    </para></listitem>
-  </varlistentry>
-
-  <varlistentry><term><literal>104</literal></term>
-    <listitem><para>Not deterministic, the build succeeded in check
-    mode but the resulting output is not binary reproducable.</para>
-    </listitem>
-  </varlistentry>
-
-</variablelist>
-
-<para>With the <option>--keep-going</option> flag it's possible for
-multiple failures to occur, in this case the 1xx status codes are or combined
-using binary or. <screen>
-1100100
-   ^^^^
-   |||`- timeout
-   ||`-- output hash mismatch
-   |`--- build failure
-   `---- not deterministic
-</screen></para>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<para>This operation is typically used to build store derivations
-produced by <link
-linkend="sec-nix-instantiate"><command>nix-instantiate</command></link>:
-
-<screen>
-$ nix-store -r $(nix-instantiate ./test.nix)
-/nix/store/31axcgrlbfsxzmfff1gyj1bf62hvkby2-aterm-2.3.1</screen>
-
-This is essentially what <link
-linkend="sec-nix-build"><command>nix-build</command></link> does.</para>
-
-<para>To test whether a previously-built derivation is deterministic:
-
-<screen>
-$ nix-build '&lt;nixpkgs>' -A hello --check -K
-</screen>
-
-</para>
-
-</refsection>
-
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection xml:id='rsec-nix-store-serve'><title>Operation <option>--serve</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-store</command>
-  <arg choice='plain'><option>--serve</option></arg>
-  <arg><option>--write</option></arg>
-</cmdsynopsis>
-
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--serve</option> provides access to
-the Nix store over stdin and stdout, and is intended to be used
-as a means of providing Nix store access to a restricted ssh user.
-</para>
-
-<para>The following flags are available:</para>
-
-<variablelist>
-
-  <varlistentry><term><option>--write</option></term>
-
-    <listitem><para>Allow the connected client to request the realization
-    of derivations. In effect, this can be used to make the host act
-    as a remote builder.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<para>To turn a host into a build server, the
-<filename>authorized_keys</filename> file can be used to provide build
-access to a given SSH public key:
-
-<screen>
-$ cat &lt;&lt;EOF >>/root/.ssh/authorized_keys
-command="nice -n20 nix-store --serve --write" ssh-rsa AAAAB3NzaC1yc2EAAAA...
-EOF
-</screen>
-
-</para>
-
-</refsection>
-
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection xml:id='rsec-nix-store-gc'><title>Operation <option>--gc</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-store</command>
-  <arg choice='plain'><option>--gc</option></arg>
-  <group>
-    <arg choice='plain'><option>--print-roots</option></arg>
-    <arg choice='plain'><option>--print-live</option></arg>
-    <arg choice='plain'><option>--print-dead</option></arg>
-    <arg choice='plain'><option>--delete</option></arg>
-  </group>
-  <arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>Without additional flags, the operation <option>--gc</option>
-performs a garbage collection on the Nix store.  That is, all paths in
-the Nix store not reachable via file system references from a set of
-β€œroots”, are deleted.</para>
-
-<para>The following suboperations may be specified:</para>
-
-<variablelist>
-
-  <varlistentry><term><option>--print-roots</option></term>
-
-    <listitem><para>This operation prints on standard output the set
-    of roots used by the garbage collector.  What constitutes a root
-    is described in <xref linkend="ssec-gc-roots"
-    />.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--print-live</option></term>
-
-    <listitem><para>This operation prints on standard output the set
-    of β€œlive” store paths, which are all the store paths reachable
-    from the roots.  Live paths should never be deleted, since that
-    would break consistency β€” it would become possible that
-    applications are installed that reference things that are no
-    longer present in the store.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--print-dead</option></term>
-
-    <listitem><para>This operation prints out on standard output the
-    set of β€œdead” store paths, which is just the opposite of the set
-    of live paths: any path in the store that is not live (with
-    respect to the roots) is dead.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--delete</option></term>
-
-    <listitem><para>This operation performs an actual garbage
-    collection.  All dead paths are removed from the
-    store.  This is the default.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-<para>By default, all unreachable paths are deleted.  The following
-options control what gets deleted and in what order:
-
-<variablelist>
-
-  <varlistentry><term><option>--max-freed</option> <replaceable>bytes</replaceable></term>
-
-    <listitem><para>Keep deleting paths until at least
-    <replaceable>bytes</replaceable> bytes have been deleted, then
-    stop.  The argument <replaceable>bytes</replaceable> can be
-    followed by the multiplicative suffix <literal>K</literal>,
-    <literal>M</literal>, <literal>G</literal> or
-    <literal>T</literal>, denoting KiB, MiB, GiB or TiB
-    units.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</para>
-
-<para>The behaviour of the collector is also influenced by the <link
-linkend="conf-keep-outputs"><literal>keep-outputs</literal></link>
-and <link
-linkend="conf-keep-derivations"><literal>keep-derivations</literal></link>
-variables in the Nix configuration file.</para>
-
-<para>With <option>--delete</option>, the collector prints the total
-number of freed bytes when it finishes (or when it is interrupted).
-With <option>--print-dead</option>, it prints the number of bytes that
-would be freed.</para>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<para>To delete all unreachable paths, just do:
-
-<screen>
-$ nix-store --gc
-deleting `/nix/store/kq82idx6g0nyzsp2s14gfsc38npai7lf-cairo-1.0.4.tar.gz.drv'
-<replaceable>...</replaceable>
-8825586 bytes freed (8.42 MiB)</screen>
-
-</para>
-
-<para>To delete at least 100 MiBs of unreachable paths:
-
-<screen>
-$ nix-store --gc --max-freed $((100 * 1024 * 1024))</screen>
-
-</para>
-
-</refsection>
-
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--delete</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-store</command>
-  <arg choice='plain'><option>--delete</option></arg>
-  <arg><option>--ignore-liveness</option></arg>
-  <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--delete</option> deletes the store paths
-<replaceable>paths</replaceable> from the Nix store, but only if it is
-safe to do so; that is, when the path is not reachable from a root of
-the garbage collector.  This means that you can only delete paths that
-would also be deleted by <literal>nix-store --gc</literal>.  Thus,
-<literal>--delete</literal> is a more targeted version of
-<literal>--gc</literal>.</para>
-
-<para>With the option <option>--ignore-liveness</option>, reachability
-from the roots is ignored.  However, the path still won’t be deleted
-if there are other paths in the store that refer to it (i.e., depend
-on it).</para>
-
-</refsection>
-
-<refsection><title>Example</title>
-
-<screen>
-$ nix-store --delete /nix/store/zq0h41l75vlb4z45kzgjjmsjxvcv1qk7-mesa-6.4
-0 bytes freed (0.00 MiB)
-error: cannot delete path `/nix/store/zq0h41l75vlb4z45kzgjjmsjxvcv1qk7-mesa-6.4' since it is still alive</screen>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection xml:id='refsec-nix-store-query'><title>Operation <option>--query</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-store</command>
-  <group choice='req'>
-    <arg choice='plain'><option>--query</option></arg>
-    <arg choice='plain'><option>-q</option></arg>
-  </group>
-  <group choice='req'>
-    <arg choice='plain'><option>--outputs</option></arg>
-    <arg choice='plain'><option>--requisites</option></arg>
-    <arg choice='plain'><option>-R</option></arg>
-    <arg choice='plain'><option>--references</option></arg>
-    <arg choice='plain'><option>--referrers</option></arg>
-    <arg choice='plain'><option>--referrers-closure</option></arg>
-    <arg choice='plain'><option>--deriver</option></arg>
-    <arg choice='plain'><option>-d</option></arg>
-    <arg choice='plain'><option>--graph</option></arg>
-    <arg choice='plain'><option>--tree</option></arg>
-    <arg choice='plain'><option>--binding</option> <replaceable>name</replaceable></arg>
-    <arg choice='plain'><option>-b</option> <replaceable>name</replaceable></arg>
-    <arg choice='plain'><option>--hash</option></arg>
-    <arg choice='plain'><option>--size</option></arg>
-    <arg choice='plain'><option>--roots</option></arg>
-  </group>
-  <arg><option>--use-output</option></arg>
-  <arg><option>-u</option></arg>
-  <arg><option>--force-realise</option></arg>
-  <arg><option>-f</option></arg>
-  <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--query</option> displays various bits of
-information about the store paths .  The queries are described below.  At
-most one query can be specified.  The default query is
-<option>--outputs</option>.</para>
-
-<para>The paths <replaceable>paths</replaceable> may also be symlinks
-from outside of the Nix store, to the Nix store.  In that case, the
-query is applied to the target of the symlink.</para>
-
-
-</refsection>
-
-
-<refsection><title>Common query options</title>
-
-<variablelist>
-
-  <varlistentry><term><option>--use-output</option></term>
-    <term><option>-u</option></term>
-
-    <listitem><para>For each argument to the query that is a store
-    derivation, apply the query to the output path of the derivation
-    instead.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--force-realise</option></term>
-    <term><option>-f</option></term>
-
-    <listitem><para>Realise each argument to the query first (see
-    <link linkend="rsec-nix-store-realise"><command>nix-store
-    --realise</command></link>).</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection xml:id='nixref-queries'><title>Queries</title>
-
-<variablelist>
-
-  <varlistentry><term><option>--outputs</option></term>
-
-    <listitem><para>Prints out the <link
-    linkend="gloss-output-path">output paths</link> of the store
-    derivations <replaceable>paths</replaceable>.  These are the paths
-    that will be produced when the derivation is
-    built.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--requisites</option></term>
-    <term><option>-R</option></term>
-
-    <listitem><para>Prints out the <link
-    linkend="gloss-closure">closure</link> of the store path
-    <replaceable>paths</replaceable>.</para>
-
-    <para>This query has one option:</para>
-
-    <variablelist>
-
-      <varlistentry><term><option>--include-outputs</option></term>
-
-        <listitem><para>Also include the output path of store
-        derivations, and their closures.</para></listitem>
-
-      </varlistentry>
-
-    </variablelist>
-
-    <para>This query can be used to implement various kinds of
-    deployment.  A <emphasis>source deployment</emphasis> is obtained
-    by distributing the closure of a store derivation.  A
-    <emphasis>binary deployment</emphasis> is obtained by distributing
-    the closure of an output path.  A <emphasis>cache
-    deployment</emphasis> (combined source/binary deployment,
-    including binaries of build-time-only dependencies) is obtained by
-    distributing the closure of a store derivation and specifying the
-    option <option>--include-outputs</option>.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--references</option></term>
-
-    <listitem><para>Prints the set of <link
-    linkend="gloss-reference">references</link> of the store paths
-    <replaceable>paths</replaceable>, that is, their immediate
-    dependencies.  (For <emphasis>all</emphasis> dependencies, use
-    <option>--requisites</option>.)</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--referrers</option></term>
-
-    <listitem><para>Prints the set of <emphasis>referrers</emphasis> of
-    the store paths <replaceable>paths</replaceable>, that is, the
-    store paths currently existing in the Nix store that refer to one
-    of <replaceable>paths</replaceable>.  Note that contrary to the
-    references, the set of referrers is not constant; it can change as
-    store paths are added or removed.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--referrers-closure</option></term>
-
-    <listitem><para>Prints the closure of the set of store paths
-    <replaceable>paths</replaceable> under the referrers relation; that
-    is, all store paths that directly or indirectly refer to one of
-    <replaceable>paths</replaceable>.  These are all the path currently
-    in the Nix store that are dependent on
-    <replaceable>paths</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--deriver</option></term>
-    <term><option>-d</option></term>
-
-    <listitem><para>Prints the <link
-    linkend="gloss-deriver">deriver</link> of the store paths
-    <replaceable>paths</replaceable>.  If the path has no deriver
-    (e.g., if it is a source file), or if the deriver is not known
-    (e.g., in the case of a binary-only deployment), the string
-    <literal>unknown-deriver</literal> is printed.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--graph</option></term>
-
-    <listitem><para>Prints the references graph of the store paths
-    <replaceable>paths</replaceable> in the format of the
-    <command>dot</command> tool of AT&amp;T's <link
-    xlink:href="http://www.graphviz.org/">Graphviz package</link>.
-    This can be used to visualise dependency graphs.  To obtain a
-    build-time dependency graph, apply this to a store derivation.  To
-    obtain a runtime dependency graph, apply it to an output
-    path.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--tree</option></term>
-
-    <listitem><para>Prints the references graph of the store paths
-    <replaceable>paths</replaceable> as a nested ASCII tree.
-    References are ordered by descending closure size; this tends to
-    flatten the tree, making it more readable.  The query only
-    recurses into a store path when it is first encountered; this
-    prevents a blowup of the tree representation of the
-    graph.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--graphml</option></term>
-
-    <listitem><para>Prints the references graph of the store paths
-    <replaceable>paths</replaceable> in the <link
-    xlink:href="http://graphml.graphdrawing.org/">GraphML</link> file format.
-    This can be used to visualise dependency graphs. To obtain a
-    build-time dependency graph, apply this to a store derivation. To
-    obtain a runtime dependency graph, apply it to an output
-    path.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--binding</option> <replaceable>name</replaceable></term>
-    <term><option>-b</option> <replaceable>name</replaceable></term>
-
-    <listitem><para>Prints the value of the attribute
-    <replaceable>name</replaceable> (i.e., environment variable) of
-    the store derivations <replaceable>paths</replaceable>.  It is an
-    error for a derivation to not have the specified
-    attribute.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--hash</option></term>
-
-    <listitem><para>Prints the SHA-256 hash of the contents of the
-    store paths <replaceable>paths</replaceable> (that is, the hash of
-    the output of <command>nix-store --dump</command> on the given
-    paths).  Since the hash is stored in the Nix database, this is a
-    fast operation.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--size</option></term>
-
-    <listitem><para>Prints the size in bytes of the contents of the
-    store paths <replaceable>paths</replaceable> β€” to be precise, the
-    size of the output of <command>nix-store --dump</command> on the
-    given paths.  Note that the actual disk space required by the
-    store paths may be higher, especially on filesystems with large
-    cluster sizes.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--roots</option></term>
-
-    <listitem><para>Prints the garbage collector roots that point,
-    directly or indirectly, at the store paths
-    <replaceable>paths</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</refsection>
-
-
-<refsection><title>Examples</title>
-
-<para>Print the closure (runtime dependencies) of the
-<command>svn</command> program in the current user environment:
-
-<screen>
-$ nix-store -qR $(which svn)
-/nix/store/5mbglq5ldqld8sj57273aljwkfvj22mc-subversion-1.1.4
-/nix/store/9lz9yc6zgmc0vlqmn2ipcpkjlmbi51vv-glibc-2.3.4
-<replaceable>...</replaceable></screen>
-
-</para>
-
-<para>Print the build-time dependencies of <command>svn</command>:
-
-<screen>
-$ nix-store -qR $(nix-store -qd $(which svn))
-/nix/store/02iizgn86m42q905rddvg4ja975bk2i4-grep-2.5.1.tar.bz2.drv
-/nix/store/07a2bzxmzwz5hp58nf03pahrv2ygwgs3-gcc-wrapper.sh
-/nix/store/0ma7c9wsbaxahwwl04gbw3fcd806ski4-glibc-2.3.4.drv
-<replaceable>... lots of other paths ...</replaceable></screen>
-
-The difference with the previous example is that we ask the closure of
-the derivation (<option>-qd</option>), not the closure of the output
-path that contains <command>svn</command>.</para>
-
-<para>Show the build-time dependencies as a tree:
-
-<screen>
-$ nix-store -q --tree $(nix-store -qd $(which svn))
-/nix/store/7i5082kfb6yjbqdbiwdhhza0am2xvh6c-subversion-1.1.4.drv
-+---/nix/store/d8afh10z72n8l1cr5w42366abiblgn54-builder.sh
-+---/nix/store/fmzxmpjx2lh849ph0l36snfj9zdibw67-bash-3.0.drv
-|   +---/nix/store/570hmhmx3v57605cqg9yfvvyh0nnb8k8-bash
-|   +---/nix/store/p3srsbd8dx44v2pg6nbnszab5mcwx03v-builder.sh
-<replaceable>...</replaceable></screen>
-
-</para>
-
-<para>Show all paths that depend on the same OpenSSL library as
-<command>svn</command>:
-
-<screen>
-$ nix-store -q --referrers $(nix-store -q --binding openssl $(nix-store -qd $(which svn)))
-/nix/store/23ny9l9wixx21632y2wi4p585qhva1q8-sylpheed-1.0.0
-/nix/store/5mbglq5ldqld8sj57273aljwkfvj22mc-subversion-1.1.4
-/nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3
-/nix/store/l51240xqsgg8a7yrbqdx1rfzyv6l26fx-lynx-2.8.5</screen>
-
-</para>
-
-<para>Show all paths that directly or indirectly depend on the Glibc
-(C library) used by <command>svn</command>:
-
-<screen>
-$ nix-store -q --referrers-closure $(ldd $(which svn) | grep /libc.so | awk '{print $3}')
-/nix/store/034a6h4vpz9kds5r6kzb9lhh81mscw43-libgnomeprintui-2.8.2
-/nix/store/15l3yi0d45prm7a82pcrknxdh6nzmxza-gawk-3.1.4
-<replaceable>...</replaceable></screen>
-
-Note that <command>ldd</command> is a command that prints out the
-dynamic libraries used by an ELF executable.</para>
-
-<para>Make a picture of the runtime dependency graph of the current
-user environment:
-
-<screen>
-$ nix-store -q --graph ~/.nix-profile | dot -Tps > graph.ps
-$ gv graph.ps</screen>
-
-</para>
-
-<para>Show every garbage collector root that points to a store path
-that depends on <command>svn</command>:
-
-<screen>
-$ nix-store -q --roots $(which svn)
-/nix/var/nix/profiles/default-81-link
-/nix/var/nix/profiles/default-82-link
-/nix/var/nix/profiles/per-user/eelco/profile-97-link
-</screen>
-
-</para>
-
-</refsection>
-
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<!--
-<refsection xml:id="rsec-nix-store-reg-val"><title>Operation <option>-XXX-register-validity</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-store</command>
-  <arg choice='plain'><option>-XXX-register-validity</option></arg>
-</cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>TODO</para>
-
-</refsection>
-
-</refsection>
--->
-
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--add</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-store</command>
-  <arg choice='plain'><option>--add</option></arg>
-  <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--add</option> adds the specified paths to
-the Nix store.  It prints the resulting paths in the Nix store on
-standard output.</para>
-
-</refsection>
-
-<refsection><title>Example</title>
-
-<screen>
-$ nix-store --add ./foo.c
-/nix/store/m7lrha58ph6rcnv109yzx1nk1cj7k7zf-foo.c</screen>
-
-</refsection>
-
-</refsection>
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--add-fixed</option></title>
-
-<refsection><title>Synopsis</title>
-
-<cmdsynopsis>
-  <command>nix-store</command>
-  <arg><option>--recursive</option></arg>
-  <arg choice='plain'><option>--add-fixed</option></arg>
-  <arg choice='plain'><replaceable>algorithm</replaceable></arg>
-  <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-</cmdsynopsis>
-
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--add-fixed</option> adds the specified paths to
-the Nix store.  Unlike <option>--add</option> paths are registered using the
-specified hashing algorithm, resulting in the same output path as a fixed output
-derivation.  This can be used for sources that are not available from a public
-url or broke since the download expression was written.
-</para>
-
-<para>This operation has the following options:
-
-<variablelist>
-
-  <varlistentry><term><option>--recursive</option></term>
-
-    <listitem><para>
-      Use recursive instead of flat hashing mode, used when adding directories
-      to the store.
-    </para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</para>
-
-</refsection>
-
-<refsection><title>Example</title>
-
-<screen>
-$ nix-store --add-fixed sha256 ./hello-2.10.tar.gz
-/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz</screen>
-
-</refsection>
-
-</refsection>
-
-
-
-<!--######################################################################-->
-
-<refsection xml:id='refsec-nix-store-verify'><title>Operation <option>--verify</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--verify</option></arg>
-    <arg><option>--check-contents</option></arg>
-    <arg><option>--repair</option></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--verify</option> verifies the internal
-consistency of the Nix database, and the consistency between the Nix
-database and the Nix store.  Any inconsistencies encountered are
-automatically repaired.  Inconsistencies are generally the result of
-the Nix store or database being modified by non-Nix tools, or of bugs
-in Nix itself.</para>
-
-<para>This operation has the following options:
-
-<variablelist>
-
-  <varlistentry><term><option>--check-contents</option></term>
-
-    <listitem><para>Checks that the contents of every valid store path
-    has not been altered by computing a SHA-256 hash of the contents
-    and comparing it with the hash stored in the Nix database at build
-    time.  Paths that have been modified are printed out.  For large
-    stores, <option>--check-contents</option> is obviously quite
-    slow.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><option>--repair</option></term>
-
-    <listitem><para>If any valid path is missing from the store, or
-    (if <option>--check-contents</option> is given) the contents of a
-    valid path has been modified, then try to repair the path by
-    redownloading it.  See <command>nix-store --repair-path</command>
-    for details.</para></listitem>
-
-  </varlistentry>
-
-</variablelist>
-
-</para>
-
-</refsection>
-
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--verify-path</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--verify-path</option></arg>
-    <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--verify-path</option> compares the
-contents of the given store paths to their cryptographic hashes stored
-in Nix’s database.  For every changed path, it prints a warning
-message.  The exit status is 0 if no path has changed, and 1
-otherwise.</para>
-
-</refsection>
-
-<refsection><title>Example</title>
-
-<para>To verify the integrity of the <command>svn</command> command and all its dependencies:
-
-<screen>
-$ nix-store --verify-path $(nix-store -qR $(which svn))
-</screen>
-
-</para>
-
-</refsection>
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--repair-path</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--repair-path</option></arg>
-    <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--repair-path</option> attempts to
-β€œrepair” the specified paths by redownloading them using the available
-substituters.  If no substitutes are available, then repair is not
-possible.</para>
-
-<warning><para>During repair, there is a very small time window during
-which the old path (if it exists) is moved out of the way and replaced
-with the new path.  If repair is interrupted in between, then the
-system may be left in a broken state (e.g., if the path contains a
-critical system component like the GNU C Library).</para></warning>
-
-</refsection>
-
-<refsection><title>Example</title>
-
-<screen>
-$ nix-store --verify-path /nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13
-path `/nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13' was modified!
-  expected hash `2db57715ae90b7e31ff1f2ecb8c12ec1cc43da920efcbe3b22763f36a1861588',
-  got `481c5aa5483ebc97c20457bb8bca24deea56550d3985cda0027f67fe54b808e4'
-
-$ nix-store --repair-path /nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13
-fetching path `/nix/store/d7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13'...
-…
-</screen>
-
-</refsection>
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection xml:id='refsec-nix-store-dump'><title>Operation <option>--dump</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--dump</option></arg>
-    <arg choice='plain'><replaceable>path</replaceable></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--dump</option> produces a NAR (Nix
-ARchive) file containing the contents of the file system tree rooted
-at <replaceable>path</replaceable>.  The archive is written to
-standard output.</para>
-
-<para>A NAR archive is like a TAR or Zip archive, but it contains only
-the information that Nix considers important.  For instance,
-timestamps are elided because all files in the Nix store have their
-timestamp set to 0 anyway.  Likewise, all permissions are left out
-except for the execute bit, because all files in the Nix store have
-644 or 755 permission.</para>
-
-<para>Also, a NAR archive is <emphasis>canonical</emphasis>, meaning
-that β€œequal” paths always produce the same NAR archive.  For instance,
-directory entries are always sorted so that the actual on-disk order
-doesn’t influence the result.  This means that the cryptographic hash
-of a NAR dump of a path is usable as a fingerprint of the contents of
-the path.  Indeed, the hashes of store paths stored in Nix’s database
-(see <link linkend="refsec-nix-store-query"><literal>nix-store -q
---hash</literal></link>) are SHA-256 hashes of the NAR dump of each
-store path.</para>
-
-<para>NAR archives support filenames of unlimited length and 64-bit
-file sizes.  They can contain regular files, directories, and symbolic
-links, but not other types of files (such as device nodes).</para>
-
-<para>A Nix archive can be unpacked using <literal>nix-store
---restore</literal>.</para>
-
-</refsection>
-
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--restore</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--restore</option></arg>
-    <arg choice='plain'><replaceable>path</replaceable></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--restore</option> unpacks a NAR archive
-to <replaceable>path</replaceable>, which must not already exist.  The
-archive is read from standard input.</para>
-
-</refsection>
-
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection xml:id='refsec-nix-store-export'><title>Operation <option>--export</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--export</option></arg>
-    <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--export</option> writes a serialisation
-of the specified store paths to standard output in a format that can
-be imported into another Nix store with <command
-linkend="refsec-nix-store-import">nix-store --import</command>.  This
-is like <command linkend="refsec-nix-store-dump">nix-store
---dump</command>, except that the NAR archive produced by that command
-doesn’t contain the necessary meta-information to allow it to be
-imported into another Nix store (namely, the set of references of the
-path).</para>
-
-<para>This command does not produce a <emphasis>closure</emphasis> of
-the specified paths, so if a store path references other store paths
-that are missing in the target Nix store, the import will fail.  To
-copy a whole closure, do something like:
-
-<screen>
-$ nix-store --export $(nix-store -qR <replaceable>paths</replaceable>) > out</screen>
-
-To import the whole closure again, run:
-
-<screen>
-$ nix-store --import &lt; out</screen>
-
-</para>
-
-</refsection>
-
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection xml:id='refsec-nix-store-import'><title>Operation <option>--import</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--import</option></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--import</option> reads a serialisation of
-a set of store paths produced by <command
-linkend="refsec-nix-store-export">nix-store --export</command> from
-standard input and adds those store paths to the Nix store.  Paths
-that already exist in the Nix store are ignored.  If a path refers to
-another path that doesn’t exist in the Nix store, the import
-fails.</para>
-
-</refsection>
-
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--optimise</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--optimise</option></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--optimise</option> reduces Nix store disk
-space usage by finding identical files in the store and hard-linking
-them to each other.  It typically reduces the size of the store by
-something like 25-35%.  Only regular files and symlinks are
-hard-linked in this manner.  Files are considered identical when they
-have the same NAR archive serialisation: that is, regular files must
-have the same contents and permission (executable or non-executable),
-and symlinks must have the same contents.</para>
-
-<para>After completion, or when the command is interrupted, a report
-on the achieved savings is printed on standard error.</para>
-
-<para>Use <option>-vv</option> or <option>-vvv</option> to get some
-progress indication.</para>
-
-</refsection>
-
-<refsection><title>Example</title>
-
-<screen>
-$ nix-store --optimise
-hashing files in `/nix/store/qhqx7l2f1kmwihc9bnxs7rc159hsxnf3-gcc-4.1.1'
-<replaceable>...</replaceable>
-541838819 bytes (516.74 MiB) freed by hard-linking 54143 files;
-there are 114486 files with equal contents out of 215894 files in total
-</screen>
-
-</refsection>
-
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--read-log</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <group choice='req'>
-      <arg choice='plain'><option>--read-log</option></arg>
-      <arg choice='plain'><option>-l</option></arg>
-    </group>
-    <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--read-log</option> prints the build log
-of the specified store paths on standard output.  The build log is
-whatever the builder of a derivation wrote to standard output and
-standard error.  If a store path is not a derivation, the deriver of
-the store path is used.</para>
-
-<para>Build logs are kept in
-<filename>/nix/var/log/nix/drvs</filename>.  However, there is no
-guarantee that a build log is available for any particular store path.
-For instance, if the path was downloaded as a pre-built binary through
-a substitute, then the log is unavailable.</para>
-
-</refsection>
-
-<refsection><title>Example</title>
-
-<screen>
-$ nix-store -l $(which ktorrent)
-building /nix/store/dhc73pvzpnzxhdgpimsd9sw39di66ph1-ktorrent-2.2.1
-unpacking sources
-unpacking source archive /nix/store/p8n1jpqs27mgkjw07pb5269717nzf5f8-ktorrent-2.2.1.tar.gz
-ktorrent-2.2.1/
-ktorrent-2.2.1/NEWS
-<replaceable>...</replaceable>
-</screen>
-
-</refsection>
-
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--dump-db</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--dump-db</option></arg>
-    <arg rep='repeat'><replaceable>paths</replaceable></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--dump-db</option> writes a dump of the
-Nix database to standard output.  It can be loaded into an empty Nix
-store using <option>--load-db</option>.  This is useful for making
-backups and when migrating to different database schemas.</para>
-
-<para>By default, <option>--dump-db</option> will dump the entire Nix
-database.  When one or more store paths is passed, only the subset of
-the Nix database for those store paths is dumped.  As with
-<option>--export</option>, the user is responsible for passing all the
-store paths for a closure.  See <option>--export</option> for an
-example.</para>
-
-</refsection>
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--load-db</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--load-db</option></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--load-db</option> reads a dump of the Nix
-database created by <option>--dump-db</option> from standard input and
-loads it into the Nix database.</para>
-
-</refsection>
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection><title>Operation <option>--print-env</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'><option>--print-env</option></arg>
-    <arg choice='plain'><replaceable>drvpath</replaceable></arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>The operation <option>--print-env</option> prints out the
-environment of a derivation in a format that can be evaluated by a
-shell.  The command line arguments of the builder are placed in the
-variable <envar>_args</envar>.</para>
-
-</refsection>
-
-<refsection><title>Example</title>
-
-<screen>
-$ nix-store --print-env $(nix-instantiate '&lt;nixpkgs>' -A firefox)
-<replaceable>…</replaceable>
-export src; src='/nix/store/plpj7qrwcz94z2psh6fchsi7s8yihc7k-firefox-12.0.source.tar.bz2'
-export stdenv; stdenv='/nix/store/7c8asx3yfrg5dg1gzhzyq2236zfgibnn-stdenv'
-export system; system='x86_64-linux'
-export _args; _args='-e /nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25c-default-builder.sh'
-</screen>
-
-</refsection>
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection xml:id='rsec-nix-store-generate-binary-cache-key'><title>Operation <option>--generate-binary-cache-key</option></title>
-
-<refsection>
-  <title>Synopsis</title>
-  <cmdsynopsis>
-    <command>nix-store</command>
-    <arg choice='plain'>
-      <option>--generate-binary-cache-key</option>
-      <option>key-name</option>
-      <option>secret-key-file</option>
-      <option>public-key-file</option>
-    </arg>
-  </cmdsynopsis>
-</refsection>
-
-<refsection><title>Description</title>
-
-<para>This command generates an <link
-xlink:href="http://ed25519.cr.yp.to/">Ed25519 key pair</link> that can
-be used to create a signed binary cache. It takes three mandatory
-parameters:
-
-<orderedlist>
-
-  <listitem><para>A key name, such as
-  <literal>cache.example.org-1</literal>, that is used to look up keys
-  on the client when it verifies signatures. It can be anything, but
-  it’s suggested to use the host name of your cache
-  (e.g. <literal>cache.example.org</literal>) with a suffix denoting
-  the number of the key (to be incremented every time you need to
-  revoke a key).</para></listitem>
-
-  <listitem><para>The file name where the secret key is to be
-  stored.</para></listitem>
-
-  <listitem><para>The file name where the public key is to be
-  stored.</para></listitem>
-
-</orderedlist>
-
-</para>
-
-</refsection>
-
-</refsection>
-
-
-<!--######################################################################-->
-
-<refsection condition="manpage"><title>Environment variables</title>
-
-<variablelist>
-  <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
-</variablelist>
-
-</refsection>
-
-
-</refentry>
diff --git a/third_party/nix/doc/manual/command-ref/opt-common-syn.xml b/third_party/nix/doc/manual/command-ref/opt-common-syn.xml
deleted file mode 100644
index b610b54b96..0000000000
--- a/third_party/nix/doc/manual/command-ref/opt-common-syn.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<nop xmlns="http://docbook.org/ns/docbook">
-  
-<arg><option>--help</option></arg>
-<arg><option>--version</option></arg>
-<arg rep='repeat'>
-  <group choice='req'>
-    <arg choice='plain'><option>--verbose</option></arg>
-    <arg choice='plain'><option>-v</option></arg>
-  </group>
-</arg>
-<arg>
-  <arg choice='plain'><option>--quiet</option></arg>
-</arg>
-<arg>
-  <group choice='plain'>
-    <arg choice='plain'><option>--no-build-output</option></arg>
-    <arg choice='plain'><option>-Q</option></arg>
-  </group>
-</arg>
-<arg>
-  <group choice='req'>
-    <arg choice='plain'><option>--max-jobs</option></arg>
-    <arg choice='plain'><option>-j</option></arg>
-  </group>
-  <replaceable>number</replaceable>
-</arg>
-<arg>
-  <option>--cores</option>
-  <replaceable>number</replaceable>
-</arg>
-<arg>
-  <option>--max-silent-time</option>
-  <replaceable>number</replaceable>
-</arg>
-<arg>
-  <option>--timeout</option>
-  <replaceable>number</replaceable>
-</arg>
-<arg>
-  <group choice='plain'>
-    <arg choice='plain'><option>--keep-going</option></arg>
-    <arg choice='plain'><option>-k</option></arg>
-  </group>
-</arg>
-<arg>
-  <group choice='plain'>
-    <arg choice='plain'><option>--keep-failed</option></arg>
-    <arg choice='plain'><option>-K</option></arg>
-  </group>
-</arg>
-<arg><option>--fallback</option></arg>
-<arg><option>--readonly-mode</option></arg>
-<arg>
-  <option>-I</option>
-  <replaceable>path</replaceable>
-</arg>
-<arg>
-  <option>--option</option>
-  <replaceable>name</replaceable>
-  <replaceable>value</replaceable>
-</arg>
-<sbr />
-
-</nop>
diff --git a/third_party/nix/doc/manual/command-ref/opt-common.xml b/third_party/nix/doc/manual/command-ref/opt-common.xml
deleted file mode 100644
index b8a2f260e8..0000000000
--- a/third_party/nix/doc/manual/command-ref/opt-common.xml
+++ /dev/null
@@ -1,366 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook" xml:id="sec-common-options">
-
-<title>Common Options</title>
-
-
-<para>Most Nix commands accept the following command-line options:</para>
-
-<variablelist xml:id="opt-common">
-
-<varlistentry><term><option>--help</option></term>
-
-  <listitem><para>Prints out a summary of the command syntax and
-  exits.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><option>--version</option></term>
-
-  <listitem><para>Prints out the Nix version number on standard output
-  and exits.</para></listitem>
-</varlistentry>
-
-
-<varlistentry><term><option>--verbose</option> / <option>-v</option></term>
-
-  <listitem>
-
-  <para>Increases the level of verbosity of diagnostic messages
-  printed on standard error.  For each Nix operation, the information
-  printed on standard output is well-defined; any diagnostic
-  information is printed on standard error, never on standard
-  output.</para>
-
-  <para>This option may be specified repeatedly.  Currently, the
-  following verbosity levels exist:</para>
-
-  <variablelist>
-
-    <varlistentry><term>0</term>
-    <listitem><para>β€œErrors only”: only print messages
-    explaining why the Nix invocation failed.</para></listitem>
-    </varlistentry>
-
-    <varlistentry><term>1</term>
-    <listitem><para>β€œInformational”: print
-    <emphasis>useful</emphasis> messages about what Nix is doing.
-    This is the default.</para></listitem>
-    </varlistentry>
-
-    <varlistentry><term>2</term>
-    <listitem><para>β€œTalkative”: print more informational
-    messages.</para></listitem>
-    </varlistentry>
-
-    <varlistentry><term>3</term>
-    <listitem><para>β€œChatty”: print even more
-    informational messages.</para></listitem>
-    </varlistentry>
-
-    <varlistentry><term>4</term>
-    <listitem><para>β€œDebug”: print debug
-    information.</para></listitem>
-    </varlistentry>
-
-    <varlistentry><term>5</term>
-    <listitem><para>β€œVomit”: print vast amounts of debug
-    information.</para></listitem>
-    </varlistentry>
-
-  </variablelist>
-
-  </listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><option>--quiet</option></term>
-
-  <listitem>
-
-  <para>Decreases the level of verbosity of diagnostic messages
-  printed on standard error.  This is the inverse option to
-  <option>-v</option> / <option>--verbose</option>.
-  </para>
-
-  <para>This option may be specified repeatedly.  See the previous
-  verbosity levels list.</para>
-
-  </listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><option>--no-build-output</option> / <option>-Q</option></term>
-
-  <listitem><para>By default, output written by builders to standard
-  output and standard error is echoed to the Nix command's standard
-  error.  This option suppresses this behaviour.  Note that the
-  builder's standard output and error are always written to a log file
-  in
-  <filename><replaceable>prefix</replaceable>/nix/var/log/nix</filename>.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry xml:id="opt-max-jobs"><term><option>--max-jobs</option> / <option>-j</option>
-<replaceable>number</replaceable></term>
-
-  <listitem>
-
-  <para>Sets the maximum number of build jobs that Nix will
-  perform in parallel to the specified number.  Specify
-  <literal>auto</literal> to use the number of CPUs in the system.
-  The default is specified by the <link
-  linkend='conf-max-jobs'><literal>max-jobs</literal></link>
-  configuration setting, which itself defaults to
-  <literal>1</literal>.  A higher value is useful on SMP systems or to
-  exploit I/O latency.</para>
-
-  <para> Setting it to <literal>0</literal> disallows building on the local
-  machine, which is useful when you want builds to happen only on remote
-  builders.</para>
-
-  </listitem>
-
-</varlistentry>
-
-
-<varlistentry xml:id="opt-cores"><term><option>--cores</option></term>
-
-  <listitem><para>Sets the value of the <envar>NIX_BUILD_CORES</envar>
-  environment variable in the invocation of builders.  Builders can
-  use this variable at their discretion to control the maximum amount
-  of parallelism.  For instance, in Nixpkgs, if the derivation
-  attribute <varname>enableParallelBuilding</varname> is set to
-  <literal>true</literal>, the builder passes the
-  <option>-j<replaceable>N</replaceable></option> flag to GNU Make.
-  It defaults to the value of the <link
-  linkend='conf-cores'><literal>cores</literal></link>
-  configuration setting, if set, or <literal>1</literal> otherwise.
-  The value <literal>0</literal> means that the builder should use all
-  available CPU cores in the system.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry xml:id="opt-max-silent-time"><term><option>--max-silent-time</option></term>
-
-  <listitem><para>Sets the maximum number of seconds that a builder
-  can go without producing any data on standard output or standard
-  error.  The default is specified by the <link
-  linkend='conf-max-silent-time'><literal>max-silent-time</literal></link>
-  configuration setting.  <literal>0</literal> means no
-  time-out.</para></listitem>
-
-</varlistentry>
-
-<varlistentry xml:id="opt-timeout"><term><option>--timeout</option></term>
-
-  <listitem><para>Sets the maximum number of seconds that a builder
-  can run.  The default is specified by the <link
-  linkend='conf-timeout'><literal>timeout</literal></link>
-  configuration setting.  <literal>0</literal> means no
-  timeout.</para></listitem>
-
-</varlistentry>
-
-<varlistentry><term><option>--keep-going</option> / <option>-k</option></term>
-
-  <listitem><para>Keep going in case of failed builds, to the
-  greatest extent possible.  That is, if building an input of some
-  derivation fails, Nix will still build the other inputs, but not the
-  derivation itself.  Without this option, Nix stops if any build
-  fails (except for builds of substitutes), possibly killing builds in
-  progress (in case of parallel or distributed builds).</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><option>--keep-failed</option> / <option>-K</option></term>
-
-  <listitem><para>Specifies that in case of a build failure, the
-  temporary directory (usually in <filename>/tmp</filename>) in which
-  the build takes place should not be deleted.  The path of the build
-  directory is printed as an informational message.
-    </para>
-  </listitem>
-</varlistentry>
-
-
-<varlistentry><term><option>--fallback</option></term>
-
-  <listitem>
-
-  <para>Whenever Nix attempts to build a derivation for which
-  substitutes are known for each output path, but realising the output
-  paths through the substitutes fails, fall back on building the
-  derivation.</para>
-
-  <para>The most common scenario in which this is useful is when we
-  have registered substitutes in order to perform binary distribution
-  from, say, a network repository.  If the repository is down, the
-  realisation of the derivation will fail.  When this option is
-  specified, Nix will build the derivation instead.  Thus,
-  installation from binaries falls back on installation from source.
-  This option is not the default since it is generally not desirable
-  for a transient failure in obtaining the substitutes to lead to a
-  full build from source (with the related consumption of
-  resources).</para>
-
-  </listitem>
-
-</varlistentry>
-
-<varlistentry><term><option>--no-build-hook</option></term>
-
-  <listitem>
-
-  <para>Disables the build hook mechanism.  This allows to ignore remote
-  builders if they are setup on the machine.</para>
-
-  <para>It's useful in cases where the bandwidth between the client and the
-  remote builder is too low.  In that case it can take more time to upload the
-  sources to the remote builder and fetch back the result than to do the
-  computation locally.</para>
-
-  </listitem>
-
-</varlistentry>
-
-
-
-<varlistentry><term><option>--readonly-mode</option></term>
-
-  <listitem><para>When this option is used, no attempt is made to open
-  the Nix database.  Most Nix operations do need database access, so
-  those operations will fail.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
-
-  <listitem><para>This option is accepted by
-  <command>nix-env</command>, <command>nix-instantiate</command> and
-  <command>nix-build</command>.  When evaluating Nix expressions, the
-  expression evaluator will automatically try to call functions that
-  it encounters.  It can automatically call functions for which every
-  argument has a <link linkend='ss-functions'>default value</link>
-  (e.g., <literal>{ <replaceable>argName</replaceable> ?
-  <replaceable>defaultValue</replaceable> }:
-  <replaceable>...</replaceable></literal>).  With
-  <option>--arg</option>, you can also call functions that have
-  arguments without a default value (or override a default value).
-  That is, if the evaluator encounters a function with an argument
-  named <replaceable>name</replaceable>, it will call it with value
-  <replaceable>value</replaceable>.</para>
-
-  <para>For instance, the top-level <literal>default.nix</literal> in
-  Nixpkgs is actually a function:
-
-<programlisting>
-{ # The system (e.g., `i686-linux') for which to build the packages.
-  system ? builtins.currentSystem
-  <replaceable>...</replaceable>
-}: <replaceable>...</replaceable></programlisting>
-
-  So if you call this Nix expression (e.g., when you do
-  <literal>nix-env -i <replaceable>pkgname</replaceable></literal>),
-  the function will be called automatically using the value <link
-  linkend='builtin-currentSystem'><literal>builtins.currentSystem</literal></link>
-  for the <literal>system</literal> argument.  You can override this
-  using <option>--arg</option>, e.g., <literal>nix-env -i
-  <replaceable>pkgname</replaceable> --arg system
-  \"i686-freebsd\"</literal>.  (Note that since the argument is a Nix
-  string literal, you have to escape the quotes.)</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
-
-  <listitem><para>This option is like <option>--arg</option>, only the
-  value is not a Nix expression but a string.  So instead of
-  <literal>--arg system \"i686-linux\"</literal> (the outer quotes are
-  to keep the shell happy) you can say <literal>--argstr system
-  i686-linux</literal>.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry xml:id="opt-attr"><term><option>--attr</option> / <option>-A</option>
-<replaceable>attrPath</replaceable></term>
-
-  <listitem><para>Select an attribute from the top-level Nix
-  expression being evaluated.  (<command>nix-env</command>,
-  <command>nix-instantiate</command>, <command>nix-build</command> and
-  <command>nix-shell</command> only.)  The <emphasis>attribute
-  path</emphasis> <replaceable>attrPath</replaceable> is a sequence of
-  attribute names separated by dots.  For instance, given a top-level
-  Nix expression <replaceable>e</replaceable>, the attribute path
-  <literal>xorg.xorgserver</literal> would cause the expression
-  <literal><replaceable>e</replaceable>.xorg.xorgserver</literal> to
-  be used.  See <link
-  linkend='refsec-nix-env-install-examples'><command>nix-env
-  --install</command></link> for some concrete examples.</para>
-
-  <para>In addition to attribute names, you can also specify array
-  indices.  For instance, the attribute path
-  <literal>foo.3.bar</literal> selects the <literal>bar</literal>
-  attribute of the fourth element of the array in the
-  <literal>foo</literal> attribute of the top-level
-  expression.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><option>--expr</option> / <option>-E</option></term>
-
-  <listitem><para>Interpret the command line arguments as a list of
-  Nix expressions to be parsed and evaluated, rather than as a list
-  of file names of Nix expressions.
-  (<command>nix-instantiate</command>, <command>nix-build</command>
-  and <command>nix-shell</command> only.)</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry xml:id="opt-I"><term><option>-I</option> <replaceable>path</replaceable></term>
-
-  <listitem><para>Add a path to the Nix expression search path.  This
-  option may be given multiple times.  See the <envar
-  linkend="env-NIX_PATH">NIX_PATH</envar> environment variable for
-  information on the semantics of the Nix search path.  Paths added
-  through <option>-I</option> take precedence over
-  <envar>NIX_PATH</envar>.</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><option>--option</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
-
-  <listitem><para>Set the Nix configuration option
-  <replaceable>name</replaceable> to <replaceable>value</replaceable>.
-  This overrides settings in the Nix configuration file (see
-  <citerefentry><refentrytitle>nix.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para></listitem>
-
-</varlistentry>
-
-
-<varlistentry><term><option>--repair</option></term>
-
-  <listitem><para>Fix corrupted or missing store paths by
-  redownloading or rebuilding them.  Note that this is slow because it
-  requires computing a cryptographic hash of the contents of every
-  path in the closure of the build.  Also note the warning under
-  <command>nix-store --repair-path</command>.</para></listitem>
-
-</varlistentry>
-
-
-</variablelist>
-
-
-</chapter>
diff --git a/third_party/nix/doc/manual/command-ref/opt-inst-syn.xml b/third_party/nix/doc/manual/command-ref/opt-inst-syn.xml
deleted file mode 100644
index e8c3f1ec6f..0000000000
--- a/third_party/nix/doc/manual/command-ref/opt-inst-syn.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<nop xmlns="http://docbook.org/ns/docbook">
-  
-  <arg>
-    <group choice='req'>
-      <arg choice='plain'><option>--prebuilt-only</option></arg>
-      <arg choice='plain'><option>-b</option></arg>
-    </group>
-  </arg>
-  
-  <arg>
-    <group choice='req'>
-      <arg choice='plain'><option>--attr</option></arg>
-      <arg choice='plain'><option>-A</option></arg>
-    </group>
-  </arg>
-
-  <arg><option>--from-expression</option></arg>
-  <arg><option>-E</option></arg>
-    
-  <arg><option>--from-profile</option> <replaceable>path</replaceable></arg>
-
-</nop>
diff --git a/third_party/nix/doc/manual/command-ref/utilities.xml b/third_party/nix/doc/manual/command-ref/utilities.xml
deleted file mode 100644
index 893f5b5b52..0000000000
--- a/third_party/nix/doc/manual/command-ref/utilities.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='ch-utilities'>
-
-<title>Utilities</title>
-
-<para>This section lists utilities that you can use when you
-work with Nix.</para>
-
-<xi:include href="nix-channel.xml" />
-<xi:include href="nix-collect-garbage.xml" />
-<xi:include href="nix-copy-closure.xml" />
-<xi:include href="nix-daemon.xml" />
-<xi:include href="nix-hash.xml" />
-<xi:include href="nix-instantiate.xml" />
-<xi:include href="nix-prefetch-url.xml" />
-
-</chapter>
diff --git a/third_party/nix/doc/manual/expressions/advanced-attributes.xml b/third_party/nix/doc/manual/expressions/advanced-attributes.xml
deleted file mode 100644
index 07b0d97d3f..0000000000
--- a/third_party/nix/doc/manual/expressions/advanced-attributes.xml
+++ /dev/null
@@ -1,340 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-advanced-attributes">
-
-<title>Advanced Attributes</title>
-
-<para>Derivations can declare some infrequently used optional
-attributes.</para>
-
-<variablelist>
-
-  <varlistentry><term><varname>allowedReferences</varname></term>
-
-    <listitem><para>The optional attribute
-    <varname>allowedReferences</varname> specifies a list of legal
-    references (dependencies) of the output of the builder.  For
-    example,
-
-<programlisting>
-allowedReferences = [];
-</programlisting>
-
-    enforces that the output of a derivation cannot have any runtime
-    dependencies on its inputs.  To allow an output to have a runtime
-    dependency on itself, use <literal>"out"</literal> as a list item.
-    This is used in NixOS to check that generated files such as
-    initial ramdisks for booting Linux don’t have accidental
-    dependencies on other paths in the Nix store.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry><term><varname>allowedRequisites</varname></term>
-
-    <listitem><para>This attribute is similar to
-    <varname>allowedReferences</varname>, but it specifies the legal
-    requisites of the whole closure, so all the dependencies
-    recursively.  For example,
-
-<programlisting>
-allowedRequisites = [ foobar ];
-</programlisting>
-
-    enforces that the output of a derivation cannot have any other
-    runtime dependency than <varname>foobar</varname>, and in addition
-    it enforces that <varname>foobar</varname> itself doesn't
-    introduce any other dependency itself.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><varname>disallowedReferences</varname></term>
-
-    <listitem><para>The optional attribute
-    <varname>disallowedReferences</varname> specifies a list of illegal
-    references (dependencies) of the output of the builder.  For
-    example,
-
-<programlisting>
-disallowedReferences = [ foo ];
-</programlisting>
-
-    enforces that the output of a derivation cannot have a direct runtime
-    dependencies on the derivation <varname>foo</varname>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry><term><varname>disallowedRequisites</varname></term>
-
-    <listitem><para>This attribute is similar to
-    <varname>disallowedReferences</varname>, but it specifies illegal
-    requisites for the whole closure, so all the dependencies
-    recursively.  For example,
-
-<programlisting>
-disallowedRequisites = [ foobar ];
-</programlisting>
-
-    enforces that the output of a derivation cannot have any
-    runtime dependency on <varname>foobar</varname> or any other derivation
-    depending recursively on <varname>foobar</varname>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry><term><varname>exportReferencesGraph</varname></term>
-
-    <listitem><para>This attribute allows builders access to the
-    references graph of their inputs.  The attribute is a list of
-    inputs in the Nix store whose references graph the builder needs
-    to know.  The value of this attribute should be a list of pairs
-    <literal>[ <replaceable>name1</replaceable>
-    <replaceable>path1</replaceable> <replaceable>name2</replaceable>
-    <replaceable>path2</replaceable> <replaceable>...</replaceable>
-    ]</literal>.  The references graph of each
-    <replaceable>pathN</replaceable> will be stored in a text file
-    <replaceable>nameN</replaceable> in the temporary build directory.
-    The text files have the format used by <command>nix-store
-    --register-validity</command> (with the deriver fields left
-    empty).  For example, when the following derivation is built:
-
-<programlisting>
-derivation {
-  ...
-  exportReferencesGraph = [ "libfoo-graph" libfoo ];
-};
-</programlisting>
-
-    the references graph of <literal>libfoo</literal> is placed in the
-    file <filename>libfoo-graph</filename> in the temporary build
-    directory.</para>
-
-    <para><varname>exportReferencesGraph</varname> is useful for
-    builders that want to do something with the closure of a store
-    path.  Examples include the builders in NixOS that generate the
-    initial ramdisk for booting Linux (a <command>cpio</command>
-    archive containing the closure of the boot script) and the
-    ISO-9660 image for the installation CD (which is populated with a
-    Nix store containing the closure of a bootable NixOS
-    configuration).</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry><term><varname>impureEnvVars</varname></term>
-
-    <listitem><para>This attribute allows you to specify a list of
-    environment variables that should be passed from the environment
-    of the calling user to the builder.  Usually, the environment is
-    cleared completely when the builder is executed, but with this
-    attribute you can allow specific environment variables to be
-    passed unmodified.  For example, <function>fetchurl</function> in
-    Nixpkgs has the line
-
-<programlisting>
-impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
-</programlisting>
-
-    to make it use the proxy server configuration specified by the
-    user in the environment variables <envar>http_proxy</envar> and
-    friends.</para>
-
-    <para>This attribute is only allowed in <link
-    linkend="fixed-output-drvs">fixed-output derivations</link>, where
-    impurities such as these are okay since (the hash of) the output
-    is known in advance.  It is ignored for all other
-    derivations.</para>
-
-    <warning><para><varname>impureEnvVars</varname> implementation takes
-    environment variables from the current builder process. When a daemon is
-    building its environmental variables are used. Without the daemon, the
-    environmental variables come from the environment of the
-    <command>nix-build</command>.</para></warning></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id="fixed-output-drvs">
-    <term><varname>outputHash</varname></term>
-    <term><varname>outputHashAlgo</varname></term>
-    <term><varname>outputHashMode</varname></term>
-
-    <listitem><para>These attributes declare that the derivation is a
-    so-called <emphasis>fixed-output derivation</emphasis>, which
-    means that a cryptographic hash of the output is already known in
-    advance.  When the build of a fixed-output derivation finishes,
-    Nix computes the cryptographic hash of the output and compares it
-    to the hash declared with these attributes.  If there is a
-    mismatch, the build fails.</para>
-
-    <para>The rationale for fixed-output derivations is derivations
-    such as those produced by the <function>fetchurl</function>
-    function.  This function downloads a file from a given URL.  To
-    ensure that the downloaded file has not been modified, the caller
-    must also specify a cryptographic hash of the file.  For example,
-
-<programlisting>
-fetchurl {
-  url = http://ftp.gnu.org/pub/gnu/hello/hello-2.1.1.tar.gz;
-  sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
-}
-</programlisting>
-
-    It sometimes happens that the URL of the file changes, e.g.,
-    because servers are reorganised or no longer available.  We then
-    must update the call to <function>fetchurl</function>, e.g.,
-
-<programlisting>
-fetchurl {
-  url = ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
-  sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
-}
-</programlisting>
-
-    If a <function>fetchurl</function> derivation was treated like a
-    normal derivation, the output paths of the derivation and
-    <emphasis>all derivations depending on it</emphasis> would change.
-    For instance, if we were to change the URL of the Glibc source
-    distribution in Nixpkgs (a package on which almost all other
-    packages depend) massive rebuilds would be needed.  This is
-    unfortunate for a change which we know cannot have a real effect
-    as it propagates upwards through the dependency graph.</para>
-
-    <para>For fixed-output derivations, on the other hand, the name of
-    the output path only depends on the <varname>outputHash*</varname>
-    and <varname>name</varname> attributes, while all other attributes
-    are ignored for the purpose of computing the output path.  (The
-    <varname>name</varname> attribute is included because it is part
-    of the path.)</para>
-
-    <para>As an example, here is the (simplified) Nix expression for
-    <varname>fetchurl</varname>:
-
-<programlisting>
-{ stdenv, curl }: # The <command>curl</command> program is used for downloading.
-
-{ url, sha256 }:
-
-stdenv.mkDerivation {
-  name = baseNameOf (toString url);
-  builder = ./builder.sh;
-  buildInputs = [ curl ];
-
-  # This is a fixed-output derivation; the output must be a regular
-  # file with SHA256 hash <varname>sha256</varname>.
-  outputHashMode = "flat";
-  outputHashAlgo = "sha256";
-  outputHash = sha256;
-
-  inherit url;
-}
-</programlisting>
-
-    </para>
-
-    <para>The <varname>outputHashAlgo</varname> attribute specifies
-    the hash algorithm used to compute the hash.  It can currently be
-    <literal>"sha1"</literal>, <literal>"sha256"</literal> or
-    <literal>"sha512"</literal>.</para>
-
-    <para>The <varname>outputHashMode</varname> attribute determines
-    how the hash is computed.  It must be one of the following two
-    values:
-
-    <variablelist>
-
-      <varlistentry><term><literal>"flat"</literal></term>
-
-        <listitem><para>The output must be a non-executable regular
-        file.  If it isn’t, the build fails.  The hash is simply
-        computed over the contents of that file (so it’s equal to what
-        Unix commands like <command>sha256sum</command> or
-        <command>sha1sum</command> produce).</para>
-
-        <para>This is the default.</para></listitem>
-
-      </varlistentry>
-
-      <varlistentry><term><literal>"recursive"</literal></term>
-
-        <listitem><para>The hash is computed over the NAR archive dump
-        of the output (i.e., the result of <link
-        linkend="refsec-nix-store-dump"><command>nix-store
-        --dump</command></link>).  In this case, the output can be
-        anything, including a directory tree.</para></listitem>
-
-      </varlistentry>
-
-    </variablelist>
-
-    </para>
-
-    <para>The <varname>outputHash</varname> attribute, finally, must
-    be a string containing the hash in either hexadecimal or base-32
-    notation.  (See the <link
-    linkend="sec-nix-hash"><command>nix-hash</command> command</link>
-    for information about converting to and from base-32
-    notation.)</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry><term><varname>passAsFile</varname></term>
-
-    <listitem><para>A list of names of attributes that should be
-    passed via files rather than environment variables.  For example,
-    if you have
-
-    <programlisting>
-passAsFile = ["big"];
-big = "a very long string";
-    </programlisting>
-
-    then when the builder runs, the environment variable
-    <envar>bigPath</envar> will contain the absolute path to a
-    temporary file containing <literal>a very long
-    string</literal>. That is, for any attribute
-    <replaceable>x</replaceable> listed in
-    <varname>passAsFile</varname>, Nix will pass an environment
-    variable <envar><replaceable>x</replaceable>Path</envar> holding
-    the path of the file containing the value of attribute
-    <replaceable>x</replaceable>. This is useful when you need to pass
-    large strings to a builder, since most operating systems impose a
-    limit on the size of the environment (typically, a few hundred
-    kilobyte).</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry><term><varname>preferLocalBuild</varname></term>
-
-    <listitem><para>If this attribute is set to
-    <literal>true</literal> and <link
-    linkend="chap-distributed-builds">distributed building is
-    enabled</link>, then, if possible, the derivaton will be built
-    locally instead of forwarded to a remote machine.  This is
-    appropriate for trivial builders where the cost of doing a
-    download or remote build would exceed the cost of building
-    locally.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry><term><varname>allowSubstitutes</varname></term>
-
-    <listitem><para>If this attribute is set to
-    <literal>false</literal>, then Nix will always build this
-    derivation; it will not try to substitute its outputs. This is
-    useful for very trivial derivations (such as
-    <function>writeText</function> in Nixpkgs) that are cheaper to
-    build than to substitute from a binary cache.</para></listitem>
-
-  </varlistentry>
-
-
-</variablelist>
-
-</section>
diff --git a/third_party/nix/doc/manual/expressions/arguments-variables.xml b/third_party/nix/doc/manual/expressions/arguments-variables.xml
deleted file mode 100644
index bf60cb7eef..0000000000
--- a/third_party/nix/doc/manual/expressions/arguments-variables.xml
+++ /dev/null
@@ -1,121 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='sec-arguments'>
-
-<title>Arguments and Variables</title>
-
-<example xml:id='ex-hello-composition'>
-
-<title>Composing GNU Hello
-(<filename>all-packages.nix</filename>)</title>
-<programlisting>
-...
-
-rec { <co xml:id='ex-hello-composition-co-1' />
-
-  hello = import ../applications/misc/hello/ex-1 <co xml:id='ex-hello-composition-co-2' /> { <co xml:id='ex-hello-composition-co-3' />
-    inherit fetchurl stdenv perl;
-  };
-
-  perl = import ../development/interpreters/perl { <co xml:id='ex-hello-composition-co-4' />
-    inherit fetchurl stdenv;
-  };
-
-  fetchurl = import ../build-support/fetchurl {
-    inherit stdenv; ...
-  };
-
-  stdenv = ...;
-
-}
-</programlisting>
-</example>
-
-<para>The Nix expression in <xref linkend='ex-hello-nix' /> is a
-function; it is missing some arguments that have to be filled in
-somewhere.  In the Nix Packages collection this is done in the file
-<filename>pkgs/top-level/all-packages.nix</filename>, where all
-Nix expressions for packages are imported and called with the
-appropriate arguments.  <xref linkend='ex-hello-composition' /> shows
-some fragments of
-<filename>all-packages.nix</filename>.</para>
-
-<calloutlist>
-
-  <callout arearefs='ex-hello-composition-co-1'>
-
-    <para>This file defines a set of attributes, all of which are
-    concrete derivations (i.e., not functions).  In fact, we define a
-    <emphasis>mutually recursive</emphasis> set of attributes.  That
-    is, the attributes can refer to each other.  This is precisely
-    what we want since we want to <quote>plug</quote> the
-    various packages into each other.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-composition-co-2'>
-
-    <para>Here we <emphasis>import</emphasis> the Nix expression for
-    GNU Hello.  The import operation just loads and returns the
-    specified Nix expression. In fact, we could just have put the
-    contents of <xref linkend='ex-hello-nix' /> in
-    <filename>all-packages.nix</filename> at this point.  That
-    would be completely equivalent, but it would make the file rather
-    bulky.</para>
-
-    <para>Note that we refer to
-    <filename>../applications/misc/hello/ex-1</filename>, not
-    <filename>../applications/misc/hello/ex-1/default.nix</filename>.
-    When you try to import a directory, Nix automatically appends
-    <filename>/default.nix</filename> to the file name.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-composition-co-3'>
-
-    <para>This is where the actual composition takes place.  Here we
-    <emphasis>call</emphasis> the function imported from
-    <filename>../applications/misc/hello/ex-1</filename> with a set
-    containing the things that the function expects, namely
-    <varname>fetchurl</varname>, <varname>stdenv</varname>, and
-    <varname>perl</varname>.  We use inherit again to use the
-    attributes defined in the surrounding scope (we could also have
-    written <literal>fetchurl = fetchurl;</literal>, etc.).</para>
-
-    <para>The result of this function call is an actual derivation
-    that can be built by Nix (since when we fill in the arguments of
-    the function, what we get is its body, which is the call to
-    <varname>stdenv.mkDerivation</varname> in <xref
-    linkend='ex-hello-nix' />).</para>
-
-    <note><para>Nixpkgs has a convenience function
-    <function>callPackage</function> that imports and calls a
-    function, filling in any missing arguments by passing the
-    corresponding attribute from the Nixpkgs set, like this:
-
-<programlisting>
-hello = callPackage ../applications/misc/hello/ex-1 { };
-</programlisting>
-
-    If necessary, you can set or override arguments:
-
-<programlisting>
-hello = callPackage ../applications/misc/hello/ex-1 { stdenv = myStdenv; };
-</programlisting>
-
-    </para></note>
-
-  </callout>
-
-  <callout arearefs='ex-hello-composition-co-4'>
-
-    <para>Likewise, we have to instantiate Perl,
-    <varname>fetchurl</varname>, and the standard environment.</para>
-
-  </callout>
-
-</calloutlist>
-
-</section>
\ No newline at end of file
diff --git a/third_party/nix/doc/manual/expressions/build-script.xml b/third_party/nix/doc/manual/expressions/build-script.xml
deleted file mode 100644
index 7bad8f808d..0000000000
--- a/third_party/nix/doc/manual/expressions/build-script.xml
+++ /dev/null
@@ -1,119 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='sec-build-script'>
-
-<title>Build Script</title>
-
-<example xml:id='ex-hello-builder'><title>Build script for GNU Hello
-(<filename>builder.sh</filename>)</title>
-<programlisting>
-source $stdenv/setup <co xml:id='ex-hello-builder-co-1' />
-
-PATH=$perl/bin:$PATH <co xml:id='ex-hello-builder-co-2' />
-
-tar xvfz $src <co xml:id='ex-hello-builder-co-3' />
-cd hello-*
-./configure --prefix=$out <co xml:id='ex-hello-builder-co-4' />
-make <co xml:id='ex-hello-builder-co-5' />
-make install</programlisting>
-</example>
-
-<para><xref linkend='ex-hello-builder' /> shows the builder referenced
-from Hello's Nix expression (stored in
-<filename>pkgs/applications/misc/hello/ex-1/builder.sh</filename>).
-The builder can actually be made a lot shorter by using the
-<emphasis>generic builder</emphasis> functions provided by
-<varname>stdenv</varname>, but here we write out the build steps to
-elucidate what a builder does.  It performs the following
-steps:</para>
-
-<calloutlist>
-
-  <callout arearefs='ex-hello-builder-co-1'>
-
-    <para>When Nix runs a builder, it initially completely clears the
-    environment (except for the attributes declared in the
-    derivation).  For instance, the <envar>PATH</envar> variable is
-    empty<footnote><para>Actually, it's initialised to
-    <filename>/path-not-set</filename> to prevent Bash from setting it
-    to a default value.</para></footnote>.  This is done to prevent
-    undeclared inputs from being used in the build process.  If for
-    example the <envar>PATH</envar> contained
-    <filename>/usr/bin</filename>, then you might accidentally use
-    <filename>/usr/bin/gcc</filename>.</para>
-
-    <para>So the first step is to set up the environment.  This is
-    done by calling the <filename>setup</filename> script of the
-    standard environment.  The environment variable
-    <envar>stdenv</envar> points to the location of the standard
-    environment being used.  (It wasn't specified explicitly as an
-    attribute in <xref linkend='ex-hello-nix' />, but
-    <varname>mkDerivation</varname> adds it automatically.)</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder-co-2'>
-
-    <para>Since Hello needs Perl, we have to make sure that Perl is in
-    the <envar>PATH</envar>.  The <envar>perl</envar> environment
-    variable points to the location of the Perl package (since it
-    was passed in as an attribute to the derivation), so
-    <filename><replaceable>$perl</replaceable>/bin</filename> is the
-    directory containing the Perl interpreter.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder-co-3'>
-
-    <para>Now we have to unpack the sources.  The
-    <varname>src</varname> attribute was bound to the result of
-    fetching the Hello source tarball from the network, so the
-    <envar>src</envar> environment variable points to the location in
-    the Nix store to which the tarball was downloaded.  After
-    unpacking, we <command>cd</command> to the resulting source
-    directory.</para>
-
-    <para>The whole build is performed in a temporary directory
-    created in <varname>/tmp</varname>, by the way.  This directory is
-    removed after the builder finishes, so there is no need to clean
-    up the sources afterwards.  Also, the temporary directory is
-    always newly created, so you don't have to worry about files from
-    previous builds interfering with the current build.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder-co-4'>
-
-    <para>GNU Hello is a typical Autoconf-based package, so we first
-    have to run its <filename>configure</filename> script.  In Nix
-    every package is stored in a separate location in the Nix store,
-    for instance
-    <filename>/nix/store/9a54ba97fb71b65fda531012d0443ce2-hello-2.1.1</filename>.
-    Nix computes this path by cryptographically hashing all attributes
-    of the derivation.  The path is passed to the builder through the
-    <envar>out</envar> environment variable.  So here we give
-    <filename>configure</filename> the parameter
-    <literal>--prefix=$out</literal> to cause Hello to be installed in
-    the expected location.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder-co-5'>
-
-    <para>Finally we build Hello (<literal>make</literal>) and install
-    it into the location specified by <envar>out</envar>
-    (<literal>make install</literal>).</para>
-
-  </callout>
-
-</calloutlist>
-
-<para>If you are wondering about the absence of error checking on the
-result of various commands called in the builder: this is because the
-shell script is evaluated with Bash's <option>-e</option> option,
-which causes the script to be aborted if any command fails without an
-error check.</para>
-
-</section>
\ No newline at end of file
diff --git a/third_party/nix/doc/manual/expressions/builder-syntax.xml b/third_party/nix/doc/manual/expressions/builder-syntax.xml
deleted file mode 100644
index e51bade44e..0000000000
--- a/third_party/nix/doc/manual/expressions/builder-syntax.xml
+++ /dev/null
@@ -1,119 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='sec-builder-syntax'>
-
-<title>Builder Syntax</title>
-
-<example xml:id='ex-hello-builder'><title>Build script for GNU Hello
-(<filename>builder.sh</filename>)</title>
-<programlisting>
-source $stdenv/setup <co xml:id='ex-hello-builder-co-1' />
-
-PATH=$perl/bin:$PATH <co xml:id='ex-hello-builder-co-2' />
-
-tar xvfz $src <co xml:id='ex-hello-builder-co-3' />
-cd hello-*
-./configure --prefix=$out <co xml:id='ex-hello-builder-co-4' />
-make <co xml:id='ex-hello-builder-co-5' />
-make install</programlisting>
-</example>
-
-<para><xref linkend='ex-hello-builder' /> shows the builder referenced
-from Hello's Nix expression (stored in
-<filename>pkgs/applications/misc/hello/ex-1/builder.sh</filename>).
-The builder can actually be made a lot shorter by using the
-<emphasis>generic builder</emphasis> functions provided by
-<varname>stdenv</varname>, but here we write out the build steps to
-elucidate what a builder does.  It performs the following
-steps:</para>
-
-<calloutlist>
-
-  <callout arearefs='ex-hello-builder-co-1'>
-
-    <para>When Nix runs a builder, it initially completely clears the
-    environment (except for the attributes declared in the
-    derivation).  For instance, the <envar>PATH</envar> variable is
-    empty<footnote><para>Actually, it's initialised to
-    <filename>/path-not-set</filename> to prevent Bash from setting it
-    to a default value.</para></footnote>.  This is done to prevent
-    undeclared inputs from being used in the build process.  If for
-    example the <envar>PATH</envar> contained
-    <filename>/usr/bin</filename>, then you might accidentally use
-    <filename>/usr/bin/gcc</filename>.</para>
-
-    <para>So the first step is to set up the environment.  This is
-    done by calling the <filename>setup</filename> script of the
-    standard environment.  The environment variable
-    <envar>stdenv</envar> points to the location of the standard
-    environment being used.  (It wasn't specified explicitly as an
-    attribute in <xref linkend='ex-hello-nix' />, but
-    <varname>mkDerivation</varname> adds it automatically.)</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder-co-2'>
-
-    <para>Since Hello needs Perl, we have to make sure that Perl is in
-    the <envar>PATH</envar>.  The <envar>perl</envar> environment
-    variable points to the location of the Perl package (since it
-    was passed in as an attribute to the derivation), so
-    <filename><replaceable>$perl</replaceable>/bin</filename> is the
-    directory containing the Perl interpreter.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder-co-3'>
-
-    <para>Now we have to unpack the sources.  The
-    <varname>src</varname> attribute was bound to the result of
-    fetching the Hello source tarball from the network, so the
-    <envar>src</envar> environment variable points to the location in
-    the Nix store to which the tarball was downloaded.  After
-    unpacking, we <command>cd</command> to the resulting source
-    directory.</para>
-
-    <para>The whole build is performed in a temporary directory
-    created in <varname>/tmp</varname>, by the way.  This directory is
-    removed after the builder finishes, so there is no need to clean
-    up the sources afterwards.  Also, the temporary directory is
-    always newly created, so you don't have to worry about files from
-    previous builds interfering with the current build.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder-co-4'>
-
-    <para>GNU Hello is a typical Autoconf-based package, so we first
-    have to run its <filename>configure</filename> script.  In Nix
-    every package is stored in a separate location in the Nix store,
-    for instance
-    <filename>/nix/store/9a54ba97fb71b65fda531012d0443ce2-hello-2.1.1</filename>.
-    Nix computes this path by cryptographically hashing all attributes
-    of the derivation.  The path is passed to the builder through the
-    <envar>out</envar> environment variable.  So here we give
-    <filename>configure</filename> the parameter
-    <literal>--prefix=$out</literal> to cause Hello to be installed in
-    the expected location.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder-co-5'>
-
-    <para>Finally we build Hello (<literal>make</literal>) and install
-    it into the location specified by <envar>out</envar>
-    (<literal>make install</literal>).</para>
-
-  </callout>
-
-</calloutlist>
-
-<para>If you are wondering about the absence of error checking on the
-result of various commands called in the builder: this is because the
-shell script is evaluated with Bash's <option>-e</option> option,
-which causes the script to be aborted if any command fails without an
-error check.</para>
-
-</section>
\ No newline at end of file
diff --git a/third_party/nix/doc/manual/expressions/builtins.xml b/third_party/nix/doc/manual/expressions/builtins.xml
deleted file mode 100644
index 394e1fc32c..0000000000
--- a/third_party/nix/doc/manual/expressions/builtins.xml
+++ /dev/null
@@ -1,1658 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='ssec-builtins'>
-
-<title>Built-in Functions</title>
-
-<para>This section lists the functions and constants built into the
-Nix expression evaluator.  (The built-in function
-<function>derivation</function> is discussed above.)  Some built-ins,
-such as <function>derivation</function>, are always in scope of every
-Nix expression; you can just access them right away.  But to prevent
-polluting the namespace too much, most built-ins are not in scope.
-Instead, you can access them through the <varname>builtins</varname>
-built-in value, which is a set that contains all built-in functions
-and values.  For instance, <function>derivation</function> is also
-available as <function>builtins.derivation</function>.</para>
-
-
-<variablelist>
-
-
-  <varlistentry xml:id='builtin-abort'>
-    <term><function>abort</function> <replaceable>s</replaceable></term>
-    <term><function>builtins.abort</function> <replaceable>s</replaceable></term>
-
-    <listitem><para>Abort Nix expression evaluation, print error
-    message <replaceable>s</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-add'>
-    <term><function>builtins.add</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable>
-    </term>
-
-    <listitem><para>Return the sum of the numbers
-    <replaceable>e1</replaceable> and
-    <replaceable>e2</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-all'>
-    <term><function>builtins.all</function>
-    <replaceable>pred</replaceable> <replaceable>list</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if the function
-    <replaceable>pred</replaceable> returns <literal>true</literal>
-    for all elements of <replaceable>list</replaceable>,
-    and <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-any'>
-    <term><function>builtins.any</function>
-    <replaceable>pred</replaceable> <replaceable>list</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if the function
-    <replaceable>pred</replaceable> returns <literal>true</literal>
-    for at least one element of <replaceable>list</replaceable>,
-    and <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-attrNames'>
-    <term><function>builtins.attrNames</function>
-    <replaceable>set</replaceable></term>
-
-    <listitem><para>Return the names of the attributes in the set
-    <replaceable>set</replaceable> in an alphabetically sorted list.  For instance,
-    <literal>builtins.attrNames { y = 1; x = "foo"; }</literal>
-    evaluates to <literal>[ "x" "y" ]</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-attrValues'>
-    <term><function>builtins.attrValues</function>
-    <replaceable>set</replaceable></term>
-
-    <listitem><para>Return the values of the attributes in the set
-    <replaceable>set</replaceable> in the order corresponding to the
-    sorted attribute names.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-baseNameOf'>
-    <term><function>baseNameOf</function> <replaceable>s</replaceable></term>
-
-    <listitem><para>Return the <emphasis>base name</emphasis> of the
-    string <replaceable>s</replaceable>, that is, everything following
-    the final slash in the string.  This is similar to the GNU
-    <command>basename</command> command.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-bitAnd'>
-    <term><function>builtins.bitAnd</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Return the bitwise AND of the integers
-    <replaceable>e1</replaceable> and
-    <replaceable>e2</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-bitOr'>
-    <term><function>builtins.bitOr</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Return the bitwise OR of the integers
-    <replaceable>e1</replaceable> and
-    <replaceable>e2</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-bitXor'>
-    <term><function>builtins.bitXor</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Return the bitwise XOR of the integers
-    <replaceable>e1</replaceable> and
-    <replaceable>e2</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-builtins'>
-    <term><varname>builtins</varname></term>
-
-    <listitem><para>The set <varname>builtins</varname> contains all
-    the built-in functions and values.  You can use
-    <varname>builtins</varname> to test for the availability of
-    features in the Nix installation, e.g.,
-
-<programlisting>
-if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
-
-    This allows a Nix expression to fall back gracefully on older Nix
-    installations that don’t have the desired built-in
-    function.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-compareVersions'>
-    <term><function>builtins.compareVersions</function>
-    <replaceable>s1</replaceable> <replaceable>s2</replaceable></term>
-
-    <listitem><para>Compare two strings representing versions and
-    return <literal>-1</literal> if version
-    <replaceable>s1</replaceable> is older than version
-    <replaceable>s2</replaceable>, <literal>0</literal> if they are
-    the same, and <literal>1</literal> if
-    <replaceable>s1</replaceable> is newer than
-    <replaceable>s2</replaceable>.  The version comparison algorithm
-    is the same as the one used by <link
-    linkend="ssec-version-comparisons"><command>nix-env
-    -u</command></link>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-concatLists'>
-    <term><function>builtins.concatLists</function>
-    <replaceable>lists</replaceable></term>
-
-    <listitem><para>Concatenate a list of lists into a single
-    list.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-concatStringsSep'>
-    <term><function>builtins.concatStringsSep</function>
-    <replaceable>separator</replaceable> <replaceable>list</replaceable></term>
-
-    <listitem><para>Concatenate a list of strings with a separator
-    between each element, e.g. <literal>concatStringsSep "/"
-    ["usr" "local" "bin"] == "usr/local/bin"</literal></para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-currentSystem'>
-    <term><varname>builtins.currentSystem</varname></term>
-
-    <listitem><para>The built-in value <varname>currentSystem</varname>
-    evaluates to the Nix platform identifier for the Nix installation
-    on which the expression is being evaluated, such as
-    <literal>"i686-linux"</literal> or
-    <literal>"x86_64-darwin"</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <!--
-  <varlistentry><term><function>currentTime</function></term>
-
-    <listitem><para>The built-in value <varname>currentTime</varname>
-    returns the current system time in seconds since 00:00:00 1/1/1970
-    UTC.  Due to the evaluation model of Nix expressions
-    (<emphasis>maximal laziness</emphasis>), it always yields the same
-    value within an execution of Nix.</para></listitem>
-
-  </varlistentry>
-  -->
-
-
-  <!--
-  <varlistentry><term><function>dependencyClosure</function></term>
-
-    <listitem><para>TODO</para></listitem>
-
-  </varlistentry>
-  -->
-
-
-  <varlistentry xml:id='builtin-deepSeq'>
-    <term><function>builtins.deepSeq</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>This is like <literal>seq
-    <replaceable>e1</replaceable>
-    <replaceable>e2</replaceable></literal>, except that
-    <replaceable>e1</replaceable> is evaluated
-    <emphasis>deeply</emphasis>: if it’s a list or set, its elements
-    or attributes are also evaluated recursively.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-derivation'>
-    <term><function>derivation</function>
-    <replaceable>attrs</replaceable></term>
-    <term><function>builtins.derivation</function>
-    <replaceable>attrs</replaceable></term>
-
-    <listitem><para><function>derivation</function> is described in
-    <xref linkend='ssec-derivation' />.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-dirOf'>
-    <term><function>dirOf</function> <replaceable>s</replaceable></term>
-    <term><function>builtins.dirOf</function> <replaceable>s</replaceable></term>
-
-    <listitem><para>Return the directory part of the string
-    <replaceable>s</replaceable>, that is, everything before the final
-    slash in the string.  This is similar to the GNU
-    <command>dirname</command> command.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-div'>
-    <term><function>builtins.div</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Return the quotient of the numbers
-    <replaceable>e1</replaceable> and
-    <replaceable>e2</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-elem'>
-    <term><function>builtins.elem</function>
-    <replaceable>x</replaceable> <replaceable>xs</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if a value equal to
-    <replaceable>x</replaceable> occurs in the list
-    <replaceable>xs</replaceable>, and <literal>false</literal>
-    otherwise.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-elemAt'>
-    <term><function>builtins.elemAt</function>
-    <replaceable>xs</replaceable> <replaceable>n</replaceable></term>
-
-    <listitem><para>Return element <replaceable>n</replaceable> from
-    the list <replaceable>xs</replaceable>.  Elements are counted
-    starting from 0.  A fatal error occurs if the index is out of
-    bounds.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-fetchurl'>
-    <term><function>builtins.fetchurl</function>
-    <replaceable>url</replaceable></term>
-
-    <listitem><para>Download the specified URL and return the path of
-    the downloaded file. This function is not available if <link
-    linkend="conf-restrict-eval">restricted evaluation mode</link> is
-    enabled.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-fetchTarball'>
-    <term><function>fetchTarball</function>
-    <replaceable>url</replaceable></term>
-    <term><function>builtins.fetchTarball</function>
-    <replaceable>url</replaceable></term>
-
-    <listitem><para>Download the specified URL, unpack it and return
-    the path of the unpacked tree. The file must be a tape archive
-    (<filename>.tar</filename>) compressed with
-    <literal>gzip</literal>, <literal>bzip2</literal> or
-    <literal>xz</literal>. The top-level path component of the files
-    in the tarball is removed, so it is best if the tarball contains a
-    single directory at top level. The typical use of the function is
-    to obtain external Nix expression dependencies, such as a
-    particular version of Nixpkgs, e.g.
-
-<programlisting>
-with import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz) {};
-
-stdenv.mkDerivation { … }
-</programlisting>
-    </para>
-
-    <para>The fetched tarball is cached for a certain amount of time
-    (1 hour by default) in <filename>~/.cache/nix/tarballs/</filename>.
-    You can change the cache timeout either on the command line with
-    <option>--option tarball-ttl <replaceable>number of seconds</replaceable></option> or
-    in the Nix configuration file with this option:
-    <literal><xref linkend="conf-tarball-ttl" /> <replaceable>number of seconds to cache</replaceable></literal>.
-    </para>
-
-    <para>Note that when obtaining the hash with <varname>nix-prefetch-url
-    </varname> the option <varname>--unpack</varname> is required.
-    </para>
-
-    <para>This function can also verify the contents against a hash.
-    In that case, the function takes a set instead of a URL. The set
-    requires the attribute <varname>url</varname> and the attribute
-    <varname>sha256</varname>, e.g.
-
-<programlisting>
-with import (fetchTarball {
-  url = https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz;
-  sha256 = "1jppksrfvbk5ypiqdz4cddxdl8z6zyzdb2srq8fcffr327ld5jj2";
-}) {};
-
-stdenv.mkDerivation { … }
-</programlisting>
-
-    </para>
-
-    <para>This function is not available if <link
-    linkend="conf-restrict-eval">restricted evaluation mode</link> is
-    enabled.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-fetchGit'>
-    <term>
-      <function>builtins.fetchGit</function>
-      <replaceable>args</replaceable>
-    </term>
-
-    <listitem>
-      <para>
-        Fetch a path from git. <replaceable>args</replaceable> can be
-        a URL, in which case the HEAD of the repo at that URL is
-        fetched. Otherwise, it can be an attribute with the following
-        attributes (all except <varname>url</varname> optional):
-      </para>
-
-      <variablelist>
-        <varlistentry>
-          <term>url</term>
-          <listitem>
-            <para>
-              The URL of the repo.
-            </para>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term>name</term>
-          <listitem>
-            <para>
-              The name of the directory the repo should be exported to
-              in the store. Defaults to the basename of the URL.
-            </para>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term>rev</term>
-          <listitem>
-            <para>
-              The git revision to fetch. Defaults to the tip of
-              <varname>ref</varname>.
-            </para>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term>ref</term>
-          <listitem>
-            <para>
-              The git ref to look for the requested revision under.
-              This is often a branch or tag name. Defaults to
-              <literal>HEAD</literal>.
-            </para>
-
-            <para>
-              By default, the <varname>ref</varname> value is prefixed
-              with <literal>refs/heads/</literal>. As of Nix 2.3.0
-              Nix will not prefix <literal>refs/heads/</literal> if
-              <varname>ref</varname> starts with <literal>refs/</literal>.
-            </para>
-          </listitem>
-        </varlistentry>
-      </variablelist>
-
-      <example>
-        <title>Fetching a private repository over SSH</title>
-        <programlisting>builtins.fetchGit {
-  url = "git@github.com:my-secret/repository.git";
-  ref = "master";
-  rev = "adab8b916a45068c044658c4158d81878f9ed1c3";
-}</programlisting>
-      </example>
-
-      <example>
-        <title>Fetching an arbitrary ref</title>
-        <programlisting>builtins.fetchGit {
-  url = "https://github.com/NixOS/nix.git";
-  ref = "refs/heads/0.5-release";
-}</programlisting>
-      </example>
-
-      <example>
-        <title>Fetching a repository's specific commit on an arbitrary branch</title>
-        <para>
-          If the revision you're looking for is in the default branch
-          of the git repository you don't strictly need to specify
-          the branch name in the <varname>ref</varname> attribute.
-        </para>
-        <para>
-          However, if the revision you're looking for is in a future
-          branch for the non-default branch you will need to specify
-          the the <varname>ref</varname> attribute as well.
-        </para>
-        <programlisting>builtins.fetchGit {
-  url = "https://github.com/nixos/nix.git";
-  rev = "841fcbd04755c7a2865c51c1e2d3b045976b7452";
-  ref = "1.11-maintenance";
-}</programlisting>
-        <note>
-          <para>
-            It is nice to always specify the branch which a revision
-            belongs to. Without the branch being specified, the
-            fetcher might fail if the default branch changes.
-            Additionally, it can be confusing to try a commit from a
-            non-default branch and see the fetch fail. If the branch
-            is specified the fault is much more obvious.
-          </para>
-        </note>
-      </example>
-
-      <example>
-        <title>Fetching a repository's specific commit on the default branch</title>
-        <para>
-          If the revision you're looking for is in the default branch
-          of the git repository you may omit the
-          <varname>ref</varname> attribute.
-        </para>
-        <programlisting>builtins.fetchGit {
-  url = "https://github.com/nixos/nix.git";
-  rev = "841fcbd04755c7a2865c51c1e2d3b045976b7452";
-}</programlisting>
-      </example>
-
-      <example>
-        <title>Fetching a tag</title>
-        <programlisting>builtins.fetchGit {
-  url = "https://github.com/nixos/nix.git";
-  ref = "refs/tags/1.9";
-}</programlisting>
-      </example>
-
-      <example>
-        <title>Fetching the latest version of a remote branch</title>
-        <para>
-          <function>builtins.fetchGit</function> can behave impurely
-           fetch the latest version of a remote branch.
-        </para>
-        <note><para>Nix will refetch the branch in accordance to
-        <xref linkend="conf-tarball-ttl" />.</para></note>
-        <note><para>This behavior is disabled in
-        <emphasis>Pure evaluation mode</emphasis>.</para></note>
-        <programlisting>builtins.fetchGit {
-  url = "ssh://git@github.com/nixos/nix.git";
-  ref = "master";
-}</programlisting>
-      </example>
-    </listitem>
-  </varlistentry>
-
-
-  <varlistentry><term><function>builtins.filter</function>
-  <replaceable>f</replaceable> <replaceable>xs</replaceable></term>
-
-    <listitem><para>Return a list consisting of the elements of
-    <replaceable>xs</replaceable> for which the function
-    <replaceable>f</replaceable> returns
-    <literal>true</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-filterSource'>
-    <term><function>builtins.filterSource</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem>
-
-      <para>This function allows you to copy sources into the Nix
-      store while filtering certain files.  For instance, suppose that
-      you want to use the directory <filename>source-dir</filename> as
-      an input to a Nix expression, e.g.
-
-<programlisting>
-stdenv.mkDerivation {
-  ...
-  src = ./source-dir;
-}
-</programlisting>
-
-      However, if <filename>source-dir</filename> is a Subversion
-      working copy, then all those annoying <filename>.svn</filename>
-      subdirectories will also be copied to the store.  Worse, the
-      contents of those directories may change a lot, causing lots of
-      spurious rebuilds.  With <function>filterSource</function> you
-      can filter out the <filename>.svn</filename> directories:
-
-<programlisting>
-  src = builtins.filterSource
-    (path: type: type != "directory" || baseNameOf path != ".svn")
-    ./source-dir;
-</programlisting>
-
-      </para>
-
-      <para>Thus, the first argument <replaceable>e1</replaceable>
-      must be a predicate function that is called for each regular
-      file, directory or symlink in the source tree
-      <replaceable>e2</replaceable>.  If the function returns
-      <literal>true</literal>, the file is copied to the Nix store,
-      otherwise it is omitted.  The function is called with two
-      arguments.  The first is the full path of the file.  The second
-      is a string that identifies the type of the file, which is
-      either <literal>"regular"</literal>,
-      <literal>"directory"</literal>, <literal>"symlink"</literal> or
-      <literal>"unknown"</literal> (for other kinds of files such as
-      device nodes or fifos β€” but note that those cannot be copied to
-      the Nix store, so if the predicate returns
-      <literal>true</literal> for them, the copy will fail). If you
-      exclude a directory, the entire corresponding subtree of
-      <replaceable>e2</replaceable> will be excluded.</para>
-
-    </listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-foldl-prime'>
-    <term><function>builtins.foldl’</function>
-    <replaceable>op</replaceable> <replaceable>nul</replaceable> <replaceable>list</replaceable></term>
-
-    <listitem><para>Reduce a list by applying a binary operator, from
-    left to right, e.g. <literal>foldl’ op nul [x0 x1 x2 ...] = op (op
-    (op nul x0) x1) x2) ...</literal>. The operator is applied
-    strictly, i.e., its arguments are evaluated first. For example,
-    <literal>foldl’ (x: y: x + y) 0 [1 2 3]</literal> evaluates to
-    6.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-functionArgs'>
-    <term><function>builtins.functionArgs</function>
-    <replaceable>f</replaceable></term>
-
-    <listitem><para>
-    Return a set containing the names of the formal arguments expected
-    by the function <replaceable>f</replaceable>.
-    The value of each attribute is a Boolean denoting whether the corresponding
-    argument has a default value.  For instance,
-    <literal>functionArgs ({ x, y ? 123}: ...)  =  { x = false; y = true; }</literal>.
-    </para>
-
-    <para>"Formal argument" here refers to the attributes pattern-matched by
-    the function.  Plain lambdas are not included, e.g.
-    <literal>functionArgs (x: ...)  =  { }</literal>.
-    </para></listitem>
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-fromJSON'>
-    <term><function>builtins.fromJSON</function> <replaceable>e</replaceable></term>
-
-    <listitem><para>Convert a JSON string to a Nix
-    value. For example,
-
-<programlisting>
-builtins.fromJSON ''{"x": [1, 2, 3], "y": null}''
-</programlisting>
-
-    returns the value <literal>{ x = [ 1 2 3 ]; y = null;
-    }</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-genList'>
-    <term><function>builtins.genList</function>
-    <replaceable>generator</replaceable> <replaceable>length</replaceable></term>
-
-    <listitem><para>Generate list of size
-    <replaceable>length</replaceable>, with each element
-    <replaceable>i</replaceable> equal to the value returned by
-    <replaceable>generator</replaceable> <literal>i</literal>. For
-    example,
-
-<programlisting>
-builtins.genList (x: x * x) 5
-</programlisting>
-
-    returns the list <literal>[ 0 1 4 9 16 ]</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-getAttr'>
-    <term><function>builtins.getAttr</function>
-    <replaceable>s</replaceable> <replaceable>set</replaceable></term>
-
-    <listitem><para><function>getAttr</function> returns the attribute
-    named <replaceable>s</replaceable> from
-    <replaceable>set</replaceable>.  Evaluation aborts if the
-    attribute doesn’t exist.  This is a dynamic version of the
-    <literal>.</literal> operator, since <replaceable>s</replaceable>
-    is an expression rather than an identifier.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-getEnv'>
-    <term><function>builtins.getEnv</function>
-    <replaceable>s</replaceable></term>
-
-    <listitem><para><function>getEnv</function> returns the value of
-    the environment variable <replaceable>s</replaceable>, or an empty
-    string if the variable doesn’t exist.  This function should be
-    used with care, as it can introduce all sorts of nasty environment
-    dependencies in your Nix expression.</para>
-
-    <para><function>getEnv</function> is used in Nix Packages to
-    locate the file <filename>~/.nixpkgs/config.nix</filename>, which
-    contains user-local settings for Nix Packages.  (That is, it does
-    a <literal>getEnv "HOME"</literal> to locate the user’s home
-    directory.)</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-hasAttr'>
-    <term><function>builtins.hasAttr</function>
-    <replaceable>s</replaceable> <replaceable>set</replaceable></term>
-
-    <listitem><para><function>hasAttr</function> returns
-    <literal>true</literal> if <replaceable>set</replaceable> has an
-    attribute named <replaceable>s</replaceable>, and
-    <literal>false</literal> otherwise.  This is a dynamic version of
-    the <literal>?</literal>  operator, since
-    <replaceable>s</replaceable> is an expression rather than an
-    identifier.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-hashString'>
-    <term><function>builtins.hashString</function>
-    <replaceable>type</replaceable> <replaceable>s</replaceable></term>
-
-    <listitem><para>Return a base-16 representation of the
-    cryptographic hash of string <replaceable>s</replaceable>.  The
-    hash algorithm specified by <replaceable>type</replaceable> must
-    be one of <literal>"md5"</literal>, <literal>"sha1"</literal>,
-    <literal>"sha256"</literal> or <literal>"sha512"</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-hashFile'>
-    <term><function>builtins.hashFile</function>
-    <replaceable>type</replaceable> <replaceable>p</replaceable></term>
-
-    <listitem><para>Return a base-16 representation of the
-    cryptographic hash of the file at path <replaceable>p</replaceable>.  The
-    hash algorithm specified by <replaceable>type</replaceable> must
-    be one of <literal>"md5"</literal>, <literal>"sha1"</literal>,
-    <literal>"sha256"</literal> or <literal>"sha512"</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-head'>
-    <term><function>builtins.head</function>
-    <replaceable>list</replaceable></term>
-
-    <listitem><para>Return the first element of a list; abort
-    evaluation if the argument isn’t a list or is an empty list.  You
-    can test whether a list is empty by comparing it with
-    <literal>[]</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-import'>
-    <term><function>import</function>
-    <replaceable>path</replaceable></term>
-    <term><function>builtins.import</function>
-    <replaceable>path</replaceable></term>
-
-    <listitem><para>Load, parse and return the Nix expression in the
-    file <replaceable>path</replaceable>.  If <replaceable>path
-    </replaceable> is a directory, the file <filename>default.nix
-    </filename> in that directory is loaded.  Evaluation aborts if the
-    file doesn’t exist or contains an incorrect Nix expression.
-    <function>import</function> implements Nix’s module system: you
-    can put any Nix expression (such as a set or a function) in a
-    separate file, and use it from Nix expressions in other
-    files.</para>
-
-    <note><para>Unlike some languages, <function>import</function> is a regular
-    function in Nix. Paths using the angle bracket syntax (e.g., <function>
-    import</function> <replaceable>&lt;foo&gt;</replaceable>) are normal path
-    values (see <xref linkend='ssec-values' />).</para></note>
-
-    <para>A Nix expression loaded by <function>import</function> must
-    not contain any <emphasis>free variables</emphasis> (identifiers
-    that are not defined in the Nix expression itself and are not
-    built-in).  Therefore, it cannot refer to variables that are in
-    scope at the call site.  For instance, if you have a calling
-    expression
-
-<programlisting>
-rec {
-  x = 123;
-  y = import ./foo.nix;
-}</programlisting>
-
-    then the following <filename>foo.nix</filename> will give an
-    error:
-
-<programlisting>
-x + 456</programlisting>
-
-    since <varname>x</varname> is not in scope in
-    <filename>foo.nix</filename>.  If you want <varname>x</varname>
-    to be available in <filename>foo.nix</filename>, you should pass
-    it as a function argument:
-
-<programlisting>
-rec {
-  x = 123;
-  y = import ./foo.nix x;
-}</programlisting>
-
-    and
-
-<programlisting>
-x: x + 456</programlisting>
-
-    (The function argument doesn’t have to be called
-    <varname>x</varname> in <filename>foo.nix</filename>; any name
-    would work.)</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-intersectAttrs'>
-    <term><function>builtins.intersectAttrs</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Return a set consisting of the attributes in the
-    set <replaceable>e2</replaceable> that also exist in the set
-    <replaceable>e1</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-isAttrs'>
-    <term><function>builtins.isAttrs</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if
-    <replaceable>e</replaceable> evaluates to a set, and
-    <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-isList'>
-    <term><function>builtins.isList</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if
-    <replaceable>e</replaceable> evaluates to a list, and
-    <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-isFunction'><term><function>builtins.isFunction</function>
-  <replaceable>e</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if
-    <replaceable>e</replaceable> evaluates to a function, and
-    <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-isString'>
-    <term><function>builtins.isString</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if
-    <replaceable>e</replaceable> evaluates to a string, and
-    <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-isInt'>
-    <term><function>builtins.isInt</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if
-    <replaceable>e</replaceable> evaluates to an int, and
-    <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-isFloat'>
-    <term><function>builtins.isFloat</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if
-    <replaceable>e</replaceable> evaluates to a float, and
-    <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-isBool'>
-    <term><function>builtins.isBool</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if
-    <replaceable>e</replaceable> evaluates to a bool, and
-    <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry><term><function>builtins.isPath</function>
-  <replaceable>e</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if
-    <replaceable>e</replaceable> evaluates to a path, and
-    <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-isNull'>
-    <term><function>isNull</function>
-    <replaceable>e</replaceable></term>
-    <term><function>builtins.isNull</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if
-    <replaceable>e</replaceable> evaluates to <literal>null</literal>,
-    and <literal>false</literal> otherwise.</para>
-
-    <warning><para>This function is <emphasis>deprecated</emphasis>;
-    just write <literal>e == null</literal> instead.</para></warning>
-
-    </listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-length'>
-    <term><function>builtins.length</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return the length of the list
-    <replaceable>e</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-lessThan'>
-    <term><function>builtins.lessThan</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if the number
-    <replaceable>e1</replaceable> is less than the number
-    <replaceable>e2</replaceable>, and <literal>false</literal>
-    otherwise.  Evaluation aborts if either
-    <replaceable>e1</replaceable> or <replaceable>e2</replaceable>
-    does not evaluate to a number.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-listToAttrs'>
-    <term><function>builtins.listToAttrs</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Construct a set from a list specifying the names
-    and values of each attribute.  Each element of the list should be
-    a set consisting of a string-valued attribute
-    <varname>name</varname> specifying the name of the attribute, and
-    an attribute <varname>value</varname> specifying its value.
-    Example:
-
-<programlisting>
-builtins.listToAttrs
-  [ { name = "foo"; value = 123; }
-    { name = "bar"; value = 456; }
-  ]
-</programlisting>
-
-    evaluates to
-
-<programlisting>
-{ foo = 123; bar = 456; }
-</programlisting>
-
-    </para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-map'>
-    <term><function>map</function>
-    <replaceable>f</replaceable> <replaceable>list</replaceable></term>
-    <term><function>builtins.map</function>
-    <replaceable>f</replaceable> <replaceable>list</replaceable></term>
-
-    <listitem><para>Apply the function <replaceable>f</replaceable> to
-    each element in the list <replaceable>list</replaceable>.  For
-    example,
-
-<programlisting>
-map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
-
-    evaluates to <literal>[ "foobar" "foobla" "fooabc"
-    ]</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-match'>
-    <term><function>builtins.match</function>
-    <replaceable>regex</replaceable> <replaceable>str</replaceable></term>
-
-    <listitem><para>Returns a list if the <link
-    xlink:href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04">extended
-    POSIX regular expression</link> <replaceable>regex</replaceable>
-    matches <replaceable>str</replaceable> precisely, otherwise returns
-    <literal>null</literal>.  Each item in the list is a regex group.
-
-<programlisting>
-builtins.match "ab" "abc"
-</programlisting>
-
-Evaluates to <literal>null</literal>.
-
-<programlisting>
-builtins.match "abc" "abc"
-</programlisting>
-
-Evaluates to <literal>[ ]</literal>.
-
-<programlisting>
-builtins.match "a(b)(c)" "abc"
-</programlisting>
-
-Evaluates to <literal>[ "b" "c" ]</literal>.
-
-<programlisting>
-builtins.match "[[:space:]]+([[:upper:]]+)[[:space:]]+" "  FOO   "
-</programlisting>
-
-Evaluates to <literal>[ "foo" ]</literal>.
-
-    </para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-mul'>
-    <term><function>builtins.mul</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Return the product of the numbers
-    <replaceable>e1</replaceable> and
-    <replaceable>e2</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-parseDrvName'>
-    <term><function>builtins.parseDrvName</function>
-    <replaceable>s</replaceable></term>
-
-    <listitem><para>Split the string <replaceable>s</replaceable> into
-    a package name and version.  The package name is everything up to
-    but not including the first dash followed by a digit, and the
-    version is everything following that dash.  The result is returned
-    in a set <literal>{ name, version }</literal>.  Thus,
-    <literal>builtins.parseDrvName "nix-0.12pre12876"</literal>
-    returns <literal>{ name = "nix"; version = "0.12pre12876";
-    }</literal>.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-path'>
-    <term>
-      <function>builtins.path</function>
-      <replaceable>args</replaceable>
-    </term>
-
-    <listitem>
-      <para>
-        An enrichment of the built-in path type, based on the attributes
-        present in <replaceable>args</replaceable>. All are optional
-        except <varname>path</varname>:
-      </para>
-
-      <variablelist>
-        <varlistentry>
-          <term>path</term>
-          <listitem>
-            <para>The underlying path.</para>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term>name</term>
-          <listitem>
-            <para>
-              The name of the path when added to the store. This can
-              used to reference paths that have nix-illegal characters
-              in their names, like <literal>@</literal>.
-            </para>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term>filter</term>
-          <listitem>
-            <para>
-              A function of the type expected by
-              <link linkend="builtin-filterSource">builtins.filterSource</link>,
-              with the same semantics.
-            </para>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term>recursive</term>
-          <listitem>
-            <para>
-              When <literal>false</literal>, when
-              <varname>path</varname> is added to the store it is with a
-              flat hash, rather than a hash of the NAR serialization of
-              the file. Thus, <varname>path</varname> must refer to a
-              regular file, not a directory. This allows similar
-              behavior to <literal>fetchurl</literal>. Defaults to
-              <literal>true</literal>.
-            </para>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term>sha256</term>
-          <listitem>
-            <para>
-              When provided, this is the expected hash of the file at
-              the path. Evaluation will fail if the hash is incorrect,
-              and providing a hash allows
-              <literal>builtins.path</literal> to be used even when the
-              <literal>pure-eval</literal> nix config option is on.
-            </para>
-          </listitem>
-        </varlistentry>
-      </variablelist>
-    </listitem>
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-pathExists'>
-    <term><function>builtins.pathExists</function>
-    <replaceable>path</replaceable></term>
-
-    <listitem><para>Return <literal>true</literal> if the path
-    <replaceable>path</replaceable> exists at evaluation time, and
-    <literal>false</literal> otherwise.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-placeholder'>
-    <term><function>builtins.placeholder</function>
-    <replaceable>output</replaceable></term>
-
-    <listitem><para>Return a placeholder string for the specified
-    <replaceable>output</replaceable> that will be substituted by the
-    corresponding output path at build time. Typical outputs would be
-    <literal>"out"</literal>, <literal>"bin"</literal> or
-    <literal>"dev"</literal>.</para></listitem>
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-readDir'>
-    <term><function>builtins.readDir</function>
-    <replaceable>path</replaceable></term>
-
-    <listitem><para>Return the contents of the directory
-    <replaceable>path</replaceable> as a set mapping directory entries
-    to the corresponding file type. For instance, if directory
-    <filename>A</filename> contains a regular file
-    <filename>B</filename> and another directory
-    <filename>C</filename>, then <literal>builtins.readDir
-    ./A</literal> will return the set
-
-<programlisting>
-{ B = "regular"; C = "directory"; }</programlisting>
-
-    The possible values for the file type are
-    <literal>"regular"</literal>, <literal>"directory"</literal>,
-    <literal>"symlink"</literal> and
-    <literal>"unknown"</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-readFile'>
-    <term><function>builtins.readFile</function>
-    <replaceable>path</replaceable></term>
-
-    <listitem><para>Return the contents of the file
-    <replaceable>path</replaceable> as a string.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-removeAttrs'>
-    <term><function>removeAttrs</function>
-    <replaceable>set</replaceable> <replaceable>list</replaceable></term>
-    <term><function>builtins.removeAttrs</function>
-    <replaceable>set</replaceable> <replaceable>list</replaceable></term>
-
-    <listitem><para>Remove the attributes listed in
-    <replaceable>list</replaceable> from
-    <replaceable>set</replaceable>.  The attributes don’t have to
-    exist in <replaceable>set</replaceable>. For instance,
-
-<programlisting>
-removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</programlisting>
-
-    evaluates to <literal>{ y = 2; }</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-replaceStrings'>
-    <term><function>builtins.replaceStrings</function>
-    <replaceable>from</replaceable> <replaceable>to</replaceable> <replaceable>s</replaceable></term>
-
-    <listitem><para>Given string <replaceable>s</replaceable>, replace
-    every occurrence of the strings in <replaceable>from</replaceable>
-    with the corresponding string in
-    <replaceable>to</replaceable>. For example,
-
-<programlisting>
-builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar"
-</programlisting>
-
-    evaluates to <literal>"fabir"</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-seq'>
-    <term><function>builtins.seq</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Evaluate <replaceable>e1</replaceable>, then
-    evaluate and return <replaceable>e2</replaceable>. This ensures
-    that a computation is strict in the value of
-    <replaceable>e1</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-sort'>
-    <term><function>builtins.sort</function>
-    <replaceable>comparator</replaceable> <replaceable>list</replaceable></term>
-
-    <listitem><para>Return <replaceable>list</replaceable> in sorted
-    order. It repeatedly calls the function
-    <replaceable>comparator</replaceable> with two elements. The
-    comparator should return <literal>true</literal> if the first
-    element is less than the second, and <literal>false</literal>
-    otherwise. For example,
-
-<programlisting>
-builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]
-</programlisting>
-
-    produces the list <literal>[ 42 77 147 249 483 526
-    ]</literal>.</para>
-
-    <para>This is a stable sort: it preserves the relative order of
-    elements deemed equal by the comparator.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-split'>
-    <term><function>builtins.split</function>
-    <replaceable>regex</replaceable> <replaceable>str</replaceable></term>
-
-    <listitem><para>Returns a list composed of non matched strings interleaved
-    with the lists of the <link
-    xlink:href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04">extended
-    POSIX regular expression</link> <replaceable>regex</replaceable> matches
-    of <replaceable>str</replaceable>. Each item in the lists of matched
-    sequences is a regex group.
-
-<programlisting>
-builtins.split "(a)b" "abc"
-</programlisting>
-
-Evaluates to <literal>[ "" [ "a" ] "c" ]</literal>.
-
-<programlisting>
-builtins.split "([ac])" "abc"
-</programlisting>
-
-Evaluates to <literal>[ "" [ "a" ] "b" [ "c" ] "" ]</literal>.
-
-<programlisting>
-builtins.split "(a)|(c)" "abc"
-</programlisting>
-
-Evaluates to <literal>[ "" [ "a" null ] "b" [ null "c" ] "" ]</literal>.
-
-<programlisting>
-builtins.split "([[:upper:]]+)" "  FOO   "
-</programlisting>
-
-Evaluates to <literal>[ "  " [ "FOO" ] "   " ]</literal>.
-
-    </para></listitem>
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-splitVersion'>
-    <term><function>builtins.splitVersion</function>
-    <replaceable>s</replaceable></term>
-
-    <listitem><para>Split a string representing a version into its
-    components, by the same version splitting logic underlying the
-    version comparison in <link linkend="ssec-version-comparisons">
-    <command>nix-env -u</command></link>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-stringLength'>
-    <term><function>builtins.stringLength</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return the length of the string
-    <replaceable>e</replaceable>.  If <replaceable>e</replaceable> is
-    not a string, evaluation is aborted.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-sub'>
-    <term><function>builtins.sub</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Return the difference between the numbers
-    <replaceable>e1</replaceable> and
-    <replaceable>e2</replaceable>.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-substring'>
-    <term><function>builtins.substring</function>
-    <replaceable>start</replaceable> <replaceable>len</replaceable>
-    <replaceable>s</replaceable></term>
-
-    <listitem><para>Return the substring of
-    <replaceable>s</replaceable> from character position
-    <replaceable>start</replaceable> (zero-based) up to but not
-    including <replaceable>start + len</replaceable>.  If
-    <replaceable>start</replaceable> is greater than the length of the
-    string, an empty string is returned, and if <replaceable>start +
-    len</replaceable> lies beyond the end of the string, only the
-    substring up to the end of the string is returned.
-    <replaceable>start</replaceable> must be
-    non-negative. For example,
-
-<programlisting>
-builtins.substring 0 3 "nixos"
-</programlisting>
-
-   evaluates to <literal>"nix"</literal>.
-   </para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-tail'>
-    <term><function>builtins.tail</function>
-    <replaceable>list</replaceable></term>
-
-    <listitem><para>Return the second to last elements of a list;
-    abort evaluation if the argument isn’t a list or is an empty
-    list.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-throw'>
-    <term><function>throw</function>
-    <replaceable>s</replaceable></term>
-    <term><function>builtins.throw</function>
-    <replaceable>s</replaceable></term>
-
-    <listitem><para>Throw an error message
-    <replaceable>s</replaceable>.  This usually aborts Nix expression
-    evaluation, but in <command>nix-env -qa</command> and other
-    commands that try to evaluate a set of derivations to get
-    information about those derivations, a derivation that throws an
-    error is silently skipped (which is not the case for
-    <function>abort</function>).</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-toFile'>
-    <term><function>builtins.toFile</function>
-    <replaceable>name</replaceable>
-    <replaceable>s</replaceable></term>
-
-    <listitem><para>Store the string <replaceable>s</replaceable> in a
-    file in the Nix store and return its path.  The file has suffix
-    <replaceable>name</replaceable>.  This file can be used as an
-    input to derivations.  One application is to write builders
-    β€œinline”.  For instance, the following Nix expression combines
-    <xref linkend='ex-hello-nix' /> and <xref
-    linkend='ex-hello-builder' /> into one file:
-
-<programlisting>
-{ stdenv, fetchurl, perl }:
-
-stdenv.mkDerivation {
-  name = "hello-2.1.1";
-
-  builder = builtins.toFile "builder.sh" "
-    source $stdenv/setup
-
-    PATH=$perl/bin:$PATH
-
-    tar xvfz $src
-    cd hello-*
-    ./configure --prefix=$out
-    make
-    make install
-  ";
-
-  src = fetchurl {
-    url = http://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
-    sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
-  };
-  inherit perl;
-}</programlisting>
-
-    </para>
-
-    <para>It is even possible for one file to refer to another, e.g.,
-
-<programlisting>
-  builder = let
-    configFile = builtins.toFile "foo.conf" "
-      # This is some dummy configuration file.
-      <replaceable>...</replaceable>
-    ";
-  in builtins.toFile "builder.sh" "
-    source $stdenv/setup
-    <replaceable>...</replaceable>
-    cp ${configFile} $out/etc/foo.conf
-  ";</programlisting>
-
-    Note that <literal>${configFile}</literal> is an antiquotation
-    (see <xref linkend='ssec-values' />), so the result of the
-    expression <literal>configFile</literal> (i.e., a path like
-    <filename>/nix/store/m7p7jfny445k...-foo.conf</filename>) will be
-    spliced into the resulting string.</para>
-
-    <para>It is however <emphasis>not</emphasis> allowed to have files
-    mutually referring to each other, like so:
-
-<programlisting>
-let
-  foo = builtins.toFile "foo" "...${bar}...";
-  bar = builtins.toFile "bar" "...${foo}...";
-in foo</programlisting>
-
-    This is not allowed because it would cause a cyclic dependency in
-    the computation of the cryptographic hashes for
-    <varname>foo</varname> and <varname>bar</varname>.</para>
-    <para>It is also not possible to reference the result of a derivation.
-    If you are using Nixpkgs, the <literal>writeTextFile</literal> function is able to
-    do that.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-toJSON'>
-    <term><function>builtins.toJSON</function> <replaceable>e</replaceable></term>
-
-    <listitem><para>Return a string containing a JSON representation
-    of <replaceable>e</replaceable>.  Strings, integers, floats, booleans,
-    nulls and lists are mapped to their JSON equivalents.  Sets
-    (except derivations) are represented as objects.  Derivations are
-    translated to a JSON string containing the derivation’s output
-    path.  Paths are copied to the store and represented as a JSON
-    string of the resulting store path.</para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-toPath'>
-    <term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
-
-    <listitem><para> DEPRECATED. Use <literal>/. + "/path"</literal>
-    to convert a string into an absolute path. For relative paths,
-    use <literal>./. + "/path"</literal>.
-    </para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-toString'>
-    <term><function>toString</function> <replaceable>e</replaceable></term>
-    <term><function>builtins.toString</function> <replaceable>e</replaceable></term>
-
-    <listitem><para>Convert the expression
-    <replaceable>e</replaceable> to a string.
-    <replaceable>e</replaceable> can be:</para>
-    <itemizedlist>
-      <listitem><para>A string (in which case the string is returned unmodified).</para></listitem>
-      <listitem><para>A path (e.g., <literal>toString /foo/bar</literal> yields <literal>"/foo/bar"</literal>.</para></listitem>
-      <listitem><para>A set containing <literal>{ __toString = self: ...; }</literal>.</para></listitem>
-      <listitem><para>An integer.</para></listitem>
-      <listitem><para>A list, in which case the string representations of its elements are joined with spaces.</para></listitem>
-      <listitem><para>A Boolean (<literal>false</literal> yields <literal>""</literal>, <literal>true</literal> yields <literal>"1"</literal>).</para></listitem>
-      <listitem><para><literal>null</literal>, which yields the empty string.</para></listitem>
-    </itemizedlist>
-    </listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-toXML'>
-    <term><function>builtins.toXML</function> <replaceable>e</replaceable></term>
-
-    <listitem><para>Return a string containing an XML representation
-    of <replaceable>e</replaceable>.  The main application for
-    <function>toXML</function> is to communicate information with the
-    builder in a more structured format than plain environment
-    variables.</para>
-
-    <!-- TODO: more formally describe the schema of the XML
-    representation -->
-
-    <para><xref linkend='ex-toxml' /> shows an example where this is
-    the case.  The builder is supposed to generate the configuration
-    file for a <link xlink:href='http://jetty.mortbay.org/'>Jetty
-    servlet container</link>.  A servlet container contains a number
-    of servlets (<filename>*.war</filename> files) each exported under
-    a specific URI prefix.  So the servlet configuration is a list of
-    sets containing the <varname>path</varname> and
-    <varname>war</varname> of the servlet (<xref
-    linkend='ex-toxml-co-servlets' />).  This kind of information is
-    difficult to communicate with the normal method of passing
-    information through an environment variable, which just
-    concatenates everything together into a string (which might just
-    work in this case, but wouldn’t work if fields are optional or
-    contain lists themselves).  Instead the Nix expression is
-    converted to an XML representation with
-    <function>toXML</function>, which is unambiguous and can easily be
-    processed with the appropriate tools.  For instance, in the
-    example an XSLT stylesheet (<xref linkend='ex-toxml-co-stylesheet'
-    />) is applied to it (<xref linkend='ex-toxml-co-apply' />) to
-    generate the XML configuration file for the Jetty server.  The XML
-    representation produced from <xref linkend='ex-toxml-co-servlets'
-    /> by <function>toXML</function> is shown in <xref
-    linkend='ex-toxml-result' />.</para>
-
-    <para>Note that <xref linkend='ex-toxml' /> uses the <function
-    linkend='builtin-toFile'>toFile</function> built-in to write the
-    builder and the stylesheet β€œinline” in the Nix expression.  The
-    path of the stylesheet is spliced into the builder at
-    <literal>xsltproc ${stylesheet}
-    <replaceable>...</replaceable></literal>.</para>
-
-    <example xml:id='ex-toxml'><title>Passing information to a builder
-    using <function>toXML</function></title>
-
-<programlisting><![CDATA[
-{ stdenv, fetchurl, libxslt, jira, uberwiki }:
-
-stdenv.mkDerivation (rec {
-  name = "web-server";
-
-  buildInputs = [ libxslt ];
-
-  builder = builtins.toFile "builder.sh" "
-    source $stdenv/setup
-    mkdir $out
-    echo "$servlets" | xsltproc ${stylesheet} - > $out/server-conf.xml]]> <co xml:id='ex-toxml-co-apply' /> <![CDATA[
-  ";
-
-  stylesheet = builtins.toFile "stylesheet.xsl"]]> <co xml:id='ex-toxml-co-stylesheet' /> <![CDATA[
-   "<?xml version='1.0' encoding='UTF-8'?>
-    <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
-      <xsl:template match='/'>
-        <Configure>
-          <xsl:for-each select='/expr/list/attrs'>
-            <Call name='addWebApplication'>
-              <Arg><xsl:value-of select=\"attr[@name = 'path']/string/@value\" /></Arg>
-              <Arg><xsl:value-of select=\"attr[@name = 'war']/path/@value\" /></Arg>
-            </Call>
-          </xsl:for-each>
-        </Configure>
-      </xsl:template>
-    </xsl:stylesheet>
-  ";
-
-  servlets = builtins.toXML []]> <co xml:id='ex-toxml-co-servlets' /> <![CDATA[
-    { path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; }
-    { path = "/wiki"; war = uberwiki + "/uberwiki.war"; }
-  ];
-})]]></programlisting>
-
-    </example>
-
-    <example xml:id='ex-toxml-result'><title>XML representation produced by
-    <function>toXML</function></title>
-
-<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
-<expr>
-  <list>
-    <attrs>
-      <attr name="path">
-        <string value="/bugtracker" />
-      </attr>
-      <attr name="war">
-        <path value="/nix/store/d1jh9pasa7k2...-jira/lib/atlassian-jira.war" />
-      </attr>
-    </attrs>
-    <attrs>
-      <attr name="path">
-        <string value="/wiki" />
-      </attr>
-      <attr name="war">
-        <path value="/nix/store/y6423b1yi4sx...-uberwiki/uberwiki.war" />
-      </attr>
-    </attrs>
-  </list>
-</expr>]]></programlisting>
-
-    </example>
-
-    </listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-trace'>
-    <term><function>builtins.trace</function>
-    <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
-
-    <listitem><para>Evaluate <replaceable>e1</replaceable> and print its
-    abstract syntax representation on standard error.  Then return
-    <replaceable>e2</replaceable>.  This function is useful for
-    debugging.</para></listitem>
-
-  </varlistentry>
-
-  <varlistentry xml:id='builtin-tryEval'>
-    <term><function>builtins.tryEval</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Try to shallowly evaluate <replaceable>e</replaceable>.
-    Return a set containing the attributes <literal>success</literal>
-    (<literal>true</literal> if <replaceable>e</replaceable> evaluated
-    successfully, <literal>false</literal> if an error was thrown) and
-    <literal>value</literal>, equalling <replaceable>e</replaceable>
-    if successful and <literal>false</literal> otherwise. Note that this
-    doesn't evaluate <replaceable>e</replaceable> deeply, so
-    <literal>let e = { x = throw ""; }; in (builtins.tryEval e).success
-    </literal> will be <literal>true</literal>. Using <literal>builtins.deepSeq
-    </literal> one can get the expected result: <literal>let e = { x = throw "";
-    }; in (builtins.tryEval (builtins.deepSeq e e)).success</literal> will be
-    <literal>false</literal>.
-    </para></listitem>
-
-  </varlistentry>
-
-
-  <varlistentry xml:id='builtin-typeOf'>
-    <term><function>builtins.typeOf</function>
-    <replaceable>e</replaceable></term>
-
-    <listitem><para>Return a string representing the type of the value
-    <replaceable>e</replaceable>, namely <literal>"int"</literal>,
-    <literal>"bool"</literal>, <literal>"string"</literal>,
-    <literal>"path"</literal>, <literal>"null"</literal>,
-    <literal>"set"</literal>, <literal>"list"</literal>,
-    <literal>"lambda"</literal> or
-    <literal>"float"</literal>.</para></listitem>
-
-  </varlistentry>
-
-
-</variablelist>
-
-
-</section>
diff --git a/third_party/nix/doc/manual/expressions/derivations.xml b/third_party/nix/doc/manual/expressions/derivations.xml
deleted file mode 100644
index 6f6297565c..0000000000
--- a/third_party/nix/doc/manual/expressions/derivations.xml
+++ /dev/null
@@ -1,211 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-derivation">
-
-<title>Derivations</title>
-
-<para>The most important built-in function is
-<function>derivation</function>, which is used to describe a single
-derivation (a build action).  It takes as input a set, the attributes
-of which specify the inputs of the build.</para>
-
-<itemizedlist>
-
-  <listitem xml:id="attr-system"><para>There must be an attribute named
-  <varname>system</varname> whose value must be a string specifying a
-  Nix platform identifier, such as <literal>"i686-linux"</literal> or
-  <literal>"x86_64-darwin"</literal><footnote><para>To figure out
-  your platform identifier, look at the line <quote>Checking for the
-  canonical Nix system name</quote> in the output of Nix's
-  <filename>configure</filename> script.</para></footnote> The build
-  can only be performed on a machine and operating system matching the
-  platform identifier.  (Nix can automatically forward builds for
-  other platforms by forwarding them to other machines; see <xref
-  linkend='chap-distributed-builds' />.)</para></listitem>
-
-  <listitem><para>There must be an attribute named
-  <varname>name</varname> whose value must be a string.  This is used
-  as a symbolic name for the package by <command>nix-env</command>,
-  and it is appended to the output paths of the
-  derivation.</para></listitem>
-
-  <listitem><para>There must be an attribute named
-  <varname>builder</varname> that identifies the program that is
-  executed to perform the build.  It can be either a derivation or a
-  source (a local file reference, e.g.,
-  <filename>./builder.sh</filename>).</para></listitem>
-
-  <listitem><para>Every attribute is passed as an environment variable
-  to the builder.  Attribute values are translated to environment
-  variables as follows:
-
-    <itemizedlist>
-
-      <listitem><para>Strings and numbers are just passed
-      verbatim.</para></listitem>
-
-      <listitem><para>A <emphasis>path</emphasis> (e.g.,
-      <filename>../foo/sources.tar</filename>) causes the referenced
-      file to be copied to the store; its location in the store is put
-      in the environment variable.  The idea is that all sources
-      should reside in the Nix store, since all inputs to a derivation
-      should reside in the Nix store.</para></listitem>
-
-      <listitem><para>A <emphasis>derivation</emphasis> causes that
-      derivation to be built prior to the present derivation; its
-      default output path is put in the environment
-      variable.</para></listitem>
-
-      <listitem><para>Lists of the previous types are also allowed.
-      They are simply concatenated, separated by
-      spaces.</para></listitem>
-
-      <listitem><para><literal>true</literal> is passed as the string
-      <literal>1</literal>, <literal>false</literal> and
-      <literal>null</literal> are passed as an empty string.
-      </para></listitem>
-    </itemizedlist>
-
-  </para></listitem>
-
-  <listitem><para>The optional attribute <varname>args</varname>
-  specifies command-line arguments to be passed to the builder.  It
-  should be a list.</para></listitem>
-
-  <listitem><para>The optional attribute <varname>outputs</varname>
-  specifies a list of symbolic outputs of the derivation.  By default,
-  a derivation produces a single output path, denoted as
-  <literal>out</literal>.  However, derivations can produce multiple
-  output paths.  This is useful because it allows outputs to be
-  downloaded or garbage-collected separately.  For instance, imagine a
-  library package that provides a dynamic library, header files, and
-  documentation.  A program that links against the library doesn’t
-  need the header files and documentation at runtime, and it doesn’t
-  need the documentation at build time.  Thus, the library package
-  could specify:
-<programlisting>
-outputs = [ "lib" "headers" "doc" ];
-</programlisting>
-  This will cause Nix to pass environment variables
-  <literal>lib</literal>, <literal>headers</literal> and
-  <literal>doc</literal> to the builder containing the intended store
-  paths of each output.  The builder would typically do something like
-<programlisting>
-./configure --libdir=$lib/lib --includedir=$headers/include --docdir=$doc/share/doc
-</programlisting>
-  for an Autoconf-style package.  You can refer to each output of a
-  derivation by selecting it as an attribute, e.g.
-<programlisting>
-buildInputs = [ pkg.lib pkg.headers ];
-</programlisting>
-  The first element of <varname>outputs</varname> determines the
-  <emphasis>default output</emphasis>.  Thus, you could also write
-<programlisting>
-buildInputs = [ pkg pkg.headers ];
-</programlisting>
-  since <literal>pkg</literal> is equivalent to
-  <literal>pkg.lib</literal>.</para></listitem>
-
-</itemizedlist>
-
-<para>The function <function>mkDerivation</function> in the Nixpkgs
-standard environment is a wrapper around
-<function>derivation</function> that adds a default value for
-<varname>system</varname> and always uses Bash as the builder, to
-which the supplied builder is passed as a command-line argument.  See
-the Nixpkgs manual for details.</para>
-
-<para>The builder is executed as follows:
-
-<itemizedlist>
-
-  <listitem><para>A temporary directory is created under the directory
-  specified by <envar>TMPDIR</envar> (default
-  <filename>/tmp</filename>) where the build will take place.  The
-  current directory is changed to this directory.</para></listitem>
-
-  <listitem><para>The environment is cleared and set to the derivation
-  attributes, as specified above.</para></listitem>
-
-  <listitem><para>In addition, the following variables are set:
-
-  <itemizedlist>
-
-    <listitem><para><envar>NIX_BUILD_TOP</envar> contains the path of
-    the temporary directory for this build.</para></listitem>
-
-    <listitem><para>Also, <envar>TMPDIR</envar>,
-    <envar>TEMPDIR</envar>, <envar>TMP</envar>, <envar>TEMP</envar>
-    are set to point to the temporary directory.  This is to prevent
-    the builder from accidentally writing temporary files anywhere
-    else.  Doing so might cause interference by other
-    processes.</para></listitem>
-
-    <listitem><para><envar>PATH</envar> is set to
-    <filename>/path-not-set</filename> to prevent shells from
-    initialising it to their built-in default value.</para></listitem>
-
-    <listitem><para><envar>HOME</envar> is set to
-    <filename>/homeless-shelter</filename> to prevent programs from
-    using <filename>/etc/passwd</filename> or the like to find the
-    user's home directory, which could cause impurity.  Usually, when
-    <envar>HOME</envar> is set, it is used as the location of the home
-    directory, even if it points to a non-existent
-    path.</para></listitem>
-
-    <listitem><para><envar>NIX_STORE</envar> is set to the path of the
-    top-level Nix store directory (typically,
-    <filename>/nix/store</filename>).</para></listitem>
-
-    <listitem><para>For each output declared in
-    <varname>outputs</varname>, the corresponding environment variable
-    is set to point to the intended path in the Nix store for that
-    output.  Each output path is a concatenation of the cryptographic
-    hash of all build inputs, the <varname>name</varname> attribute
-    and the output name.  (The output name is omitted if it’s
-    <literal>out</literal>.)</para></listitem>
-
-  </itemizedlist>
-
-  </para></listitem>
-
-  <listitem><para>If an output path already exists, it is removed.
-  Also, locks are acquired to prevent multiple Nix instances from
-  performing the same build at the same time.</para></listitem>
-
-  <listitem><para>A log of the combined standard output and error is
-  written to <filename>/nix/var/log/nix</filename>.</para></listitem>
-
-  <listitem><para>The builder is executed with the arguments specified
-  by the attribute <varname>args</varname>.  If it exits with exit
-  code 0, it is considered to have succeeded.</para></listitem>
-
-  <listitem><para>The temporary directory is removed (unless the
-  <option>-K</option> option was specified).</para></listitem>
-
-  <listitem><para>If the build was successful, Nix scans each output
-  path for references to input paths by looking for the hash parts of
-  the input paths.  Since these are potential runtime dependencies,
-  Nix registers them as dependencies of the output
-  paths.</para></listitem>
-
-  <listitem><para>After the build, Nix sets the last-modified
-  timestamp on all files in the build result to 1 (00:00:01 1/1/1970
-  UTC), sets the group to the default group, and sets the mode of the
-  file to 0444 or 0555 (i.e., read-only, with execute permission
-  enabled if the file was originally executable).  Note that possible
-  <literal>setuid</literal> and <literal>setgid</literal> bits are
-  cleared.  Setuid and setgid programs are not currently supported by
-  Nix.  This is because the Nix archives used in deployment have no
-  concept of ownership information, and because it makes the build
-  result dependent on the user performing the build.</para></listitem>
-
-</itemizedlist>
-
-</para>
-
-<xi:include href="advanced-attributes.xml" />
-
-</section>
diff --git a/third_party/nix/doc/manual/expressions/expression-language.xml b/third_party/nix/doc/manual/expressions/expression-language.xml
deleted file mode 100644
index 240ef80f14..0000000000
--- a/third_party/nix/doc/manual/expressions/expression-language.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-expression-language">
-
-<title>Nix Expression Language</title>
-
-<para>The Nix expression language is a pure, lazy, functional
-language.  Purity means that operations in the language don't have
-side-effects (for instance, there is no variable assignment).
-Laziness means that arguments to functions are evaluated only when
-they are needed.  Functional means that functions are
-<quote>normal</quote> values that can be passed around and manipulated
-in interesting ways.  The language is not a full-featured, general
-purpose language.  Its main job is to describe packages,
-compositions of packages, and the variability within
-packages.</para>
-
-<para>This section presents the various features of the
-language.</para>
-
-<xi:include href="language-values.xml" />
-<xi:include href="language-constructs.xml" />
-<xi:include href="language-operators.xml" />
-<xi:include href="derivations.xml" />
-<xi:include href="builtins.xml" />
-
-
-</chapter>
diff --git a/third_party/nix/doc/manual/expressions/expression-syntax.xml b/third_party/nix/doc/manual/expressions/expression-syntax.xml
deleted file mode 100644
index 42b9dca362..0000000000
--- a/third_party/nix/doc/manual/expressions/expression-syntax.xml
+++ /dev/null
@@ -1,148 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='sec-expression-syntax'>
-
-<title>Expression Syntax</title>
-
-<example xml:id='ex-hello-nix'><title>Nix expression for GNU Hello
-(<filename>default.nix</filename>)</title>
-<programlisting>
-{ stdenv, fetchurl, perl }: <co xml:id='ex-hello-nix-co-1' />
-
-stdenv.mkDerivation { <co xml:id='ex-hello-nix-co-2' />
-  name = "hello-2.1.1"; <co xml:id='ex-hello-nix-co-3' />
-  builder = ./builder.sh; <co xml:id='ex-hello-nix-co-4' />
-  src = fetchurl { <co xml:id='ex-hello-nix-co-5' />
-    url = ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
-    sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
-  };
-  inherit perl; <co xml:id='ex-hello-nix-co-6' />
-}</programlisting>
-</example>
-
-<para><xref linkend='ex-hello-nix' /> shows a Nix expression for GNU
-Hello.  It's actually already in the Nix Packages collection in
-<filename>pkgs/applications/misc/hello/ex-1/default.nix</filename>.
-It is customary to place each package in a separate directory and call
-the single Nix expression in that directory
-<filename>default.nix</filename>.  The file has the following elements
-(referenced from the figure by number):
-
-<calloutlist>
-
-  <callout arearefs='ex-hello-nix-co-1'>
-
-    <para>This states that the expression is a
-    <emphasis>function</emphasis> that expects to be called with three
-    arguments: <varname>stdenv</varname>, <varname>fetchurl</varname>,
-    and <varname>perl</varname>.  They are needed to build Hello, but
-    we don't know how to build them here; that's why they are function
-    arguments.  <varname>stdenv</varname> is a package that is used
-    by almost all Nix Packages packages; it provides a
-    <quote>standard</quote> environment consisting of the things you
-    would expect in a basic Unix environment: a C/C++ compiler (GCC,
-    to be precise), the Bash shell, fundamental Unix tools such as
-    <command>cp</command>, <command>grep</command>,
-    <command>tar</command>, etc.  <varname>fetchurl</varname> is a
-    function that downloads files.  <varname>perl</varname> is the
-    Perl interpreter.</para>
-
-    <para>Nix functions generally have the form <literal>{ x, y, ...,
-    z }: e</literal> where <varname>x</varname>, <varname>y</varname>,
-    etc. are the names of the expected arguments, and where
-    <replaceable>e</replaceable> is the body of the function.  So
-    here, the entire remainder of the file is the body of the
-    function; when given the required arguments, the body should
-    describe how to build an instance of the Hello package.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-nix-co-2'>
-
-    <para>So we have to build a package.  Building something from
-    other stuff is called a <emphasis>derivation</emphasis> in Nix (as
-    opposed to sources, which are built by humans instead of
-    computers).  We perform a derivation by calling
-    <varname>stdenv.mkDerivation</varname>.
-    <varname>mkDerivation</varname> is a function provided by
-    <varname>stdenv</varname> that builds a package from a set of
-    <emphasis>attributes</emphasis>.  A set is just a list of
-    key/value pairs where each key is a string and each value is an
-    arbitrary Nix expression.  They take the general form <literal>{
-    <replaceable>name1</replaceable> =
-    <replaceable>expr1</replaceable>; <replaceable>...</replaceable>
-    <replaceable>nameN</replaceable> =
-    <replaceable>exprN</replaceable>; }</literal>.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-nix-co-3'>
-
-    <para>The attribute <varname>name</varname> specifies the symbolic
-    name and version of the package.  Nix doesn't really care about
-    these things, but they are used by for instance <command>nix-env
-    -q</command> to show a <quote>human-readable</quote> name for
-    packages.  This attribute is required by
-    <varname>mkDerivation</varname>.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-nix-co-4'>
-
-    <para>The attribute <varname>builder</varname> specifies the
-    builder.  This attribute can sometimes be omitted, in which case
-    <varname>mkDerivation</varname> will fill in a default builder
-    (which does a <literal>configure; make; make install</literal>, in
-    essence).  Hello is sufficiently simple that the default builder
-    would suffice, but in this case, we will show an actual builder
-    for educational purposes.  The value
-    <command>./builder.sh</command> refers to the shell script shown
-    in <xref linkend='ex-hello-builder' />, discussed below.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-nix-co-5'>
-
-    <para>The builder has to know what the sources of the package
-    are.  Here, the attribute <varname>src</varname> is bound to the
-    result of a call to the <command>fetchurl</command> function.
-    Given a URL and a SHA-256 hash of the expected contents of the file
-    at that URL, this function builds a derivation that downloads the
-    file and checks its hash.  So the sources are a dependency that
-    like all other dependencies is built before Hello itself is
-    built.</para>
-
-    <para>Instead of <varname>src</varname> any other name could have
-    been used, and in fact there can be any number of sources (bound
-    to different attributes).  However, <varname>src</varname> is
-    customary, and it's also expected by the default builder (which we
-    don't use in this example).</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-nix-co-6'>
-
-    <para>Since the derivation requires Perl, we have to pass the
-    value of the <varname>perl</varname> function argument to the
-    builder.  All attributes in the set are actually passed as
-    environment variables to the builder, so declaring an attribute
-
-    <programlisting>
-perl = perl;</programlisting>
-
-    will do the trick: it binds an attribute <varname>perl</varname>
-    to the function argument which also happens to be called
-    <varname>perl</varname>.  However, it looks a bit silly, so there
-    is a shorter syntax.  The <literal>inherit</literal> keyword
-    causes the specified attributes to be bound to whatever variables
-    with the same name happen to be in scope.</para>
-
-  </callout>
-
-</calloutlist>
-
-</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/expressions/generic-builder.xml b/third_party/nix/doc/manual/expressions/generic-builder.xml
deleted file mode 100644
index db7ff405d8..0000000000
--- a/third_party/nix/doc/manual/expressions/generic-builder.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='sec-generic-builder'>
-
-<title>Generic Builder Syntax</title>
-
-<para>Recall from <xref linkend='ex-hello-builder' /> that the builder
-looked something like this:
-
-<programlisting>
-PATH=$perl/bin:$PATH
-tar xvfz $src
-cd hello-*
-./configure --prefix=$out
-make
-make install</programlisting>
-
-The builders for almost all Unix packages look like this β€” set up some
-environment variables, unpack the sources, configure, build, and
-install.  For this reason the standard environment provides some Bash
-functions that automate the build process.  A builder using the
-generic build facilities in shown in <xref linkend='ex-hello-builder2'
-/>.</para>
-
-<example xml:id='ex-hello-builder2'><title>Build script using the generic
-build functions</title>
-<programlisting>
-buildInputs="$perl" <co xml:id='ex-hello-builder2-co-1' />
-
-source $stdenv/setup <co xml:id='ex-hello-builder2-co-2' />
-
-genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
-</example>
-
-<calloutlist>
-
-  <callout arearefs='ex-hello-builder2-co-1'>
-
-    <para>The <envar>buildInputs</envar> variable tells
-    <filename>setup</filename> to use the indicated packages as
-    <quote>inputs</quote>.  This means that if a package provides a
-    <filename>bin</filename> subdirectory, it's added to
-    <envar>PATH</envar>; if it has a <filename>include</filename>
-    subdirectory, it's added to GCC's header search path; and so
-    on.<footnote><para>How does it work? <filename>setup</filename>
-    tries to source the file
-    <filename><replaceable>pkg</replaceable>/nix-support/setup-hook</filename>
-    of all dependencies.  These β€œsetup hooks” can then set up whatever
-    environment variables they want; for instance, the setup hook for
-    Perl sets the <envar>PERL5LIB</envar> environment variable to
-    contain the <filename>lib/site_perl</filename> directories of all
-    inputs.</para></footnote>
-    </para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder2-co-2'>
-
-    <para>The function <function>genericBuild</function> is defined in
-    the file <literal>$stdenv/setup</literal>.</para>
-
-  </callout>
-
-  <callout arearefs='ex-hello-builder2-co-3'>
-
-    <para>The final step calls the shell function
-    <function>genericBuild</function>, which performs the steps that
-    were done explicitly in <xref linkend='ex-hello-builder' />.  The
-    generic builder is smart enough to figure out whether to unpack
-    the sources using <command>gzip</command>,
-    <command>bzip2</command>, etc.  It can be customised in many ways;
-    see the Nixpkgs manual for details.</para>
-
-  </callout>
-
-</calloutlist>
-
-<para>Discerning readers will note that the
-<envar>buildInputs</envar> could just as well have been set in the Nix
-expression, like this:
-
-<programlisting>
-  buildInputs = [ perl ];</programlisting>
-
-The <varname>perl</varname> attribute can then be removed, and the
-builder becomes even shorter:
-
-<programlisting>
-source $stdenv/setup
-genericBuild</programlisting>
-
-In fact, <varname>mkDerivation</varname> provides a default builder
-that looks exactly like that, so it is actually possible to omit the
-builder for Hello entirely.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/expressions/language-constructs.xml b/third_party/nix/doc/manual/expressions/language-constructs.xml
deleted file mode 100644
index 0d0cbbe155..0000000000
--- a/third_party/nix/doc/manual/expressions/language-constructs.xml
+++ /dev/null
@@ -1,409 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-constructs">
-
-<title>Language Constructs</title>
-
-<simplesect><title>Recursive sets</title>
-
-<para>Recursive sets are just normal sets, but the attributes can
-refer to each other.  For example,
-
-<programlisting>
-rec {
-  x = y;
-  y = 123;
-}.x
-</programlisting>
-
-evaluates to <literal>123</literal>.  Note that without
-<literal>rec</literal> the binding <literal>x = y;</literal> would
-refer to the variable <varname>y</varname> in the surrounding scope,
-if one exists, and would be invalid if no such variable exists.  That
-is, in a normal (non-recursive) set, attributes are not added to the
-lexical scope; in a recursive set, they are.</para>
-
-<para>Recursive sets of course introduce the danger of infinite
-recursion.  For example,
-
-<programlisting>
-rec {
-  x = y;
-  y = x;
-}.x</programlisting>
-
-does not terminate<footnote><para>Actually, Nix detects infinite
-recursion in this case and aborts (<quote>infinite recursion
-encountered</quote>).</para></footnote>.</para>
-
-</simplesect>
-
-
-<simplesect xml:id="sect-let-expressions"><title>Let-expressions</title>
-
-<para>A let-expression allows you to define local variables for an
-expression.  For instance,
-
-<programlisting>
-let
-  x = "foo";
-  y = "bar";
-in x + y</programlisting>
-
-evaluates to <literal>"foobar"</literal>.
-
-</para>
-
-</simplesect>
-
-
-<simplesect><title>Inheriting attributes</title>
-
-<para>When defining a set or in a let-expression it is often convenient to copy variables
-from the surrounding lexical scope (e.g., when you want to propagate
-attributes).  This can be shortened using the
-<literal>inherit</literal> keyword.  For instance,
-
-<programlisting>
-let x = 123; in
-{ inherit x;
-  y = 456;
-}</programlisting>
-
-is equivalent to
-
-<programlisting>
-let x = 123; in
-{ x = x;
-  y = 456;
-}</programlisting>
-
-and both evaluate to <literal>{ x = 123; y = 456; }</literal>. (Note that
-this works because <varname>x</varname> is added to the lexical scope
-by the <literal>let</literal> construct.)  It is also possible to
-inherit attributes from another set.  For instance, in this fragment
-from <filename>all-packages.nix</filename>,
-
-<programlisting>
-  graphviz = (import ../tools/graphics/graphviz) {
-    inherit fetchurl stdenv libpng libjpeg expat x11 yacc;
-    inherit (xlibs) libXaw;
-  };
-
-  xlibs = {
-    libX11 = ...;
-    libXaw = ...;
-    ...
-  }
-
-  libpng = ...;
-  libjpg = ...;
-  ...</programlisting>
-
-the set used in the function call to the function defined in
-<filename>../tools/graphics/graphviz</filename> inherits a number of
-variables from the surrounding scope (<varname>fetchurl</varname>
-... <varname>yacc</varname>), but also inherits
-<varname>libXaw</varname> (the X Athena Widgets) from the
-<varname>xlibs</varname> (X11 client-side libraries) set.</para>
-
-<para>
-Summarizing the fragment
-
-<programlisting>
-...
-inherit x y z;
-inherit (src-set) a b c;
-...</programlisting>
-
-is equivalent to
-
-<programlisting>
-...
-x = x; y = y; z = z;
-a = src-set.a; b = src-set.b; c = src-set.c;
-...</programlisting>
-
-when used while defining local variables in a let-expression or
-while defining a set.</para>
-
-</simplesect>
-
-
-<simplesect xml:id="ss-functions"><title>Functions</title>
-
-<para>Functions have the following form:
-
-<programlisting>
-<replaceable>pattern</replaceable>: <replaceable>body</replaceable></programlisting>
-
-The pattern specifies what the argument of the function must look
-like, and binds variables in the body to (parts of) the
-argument.  There are three kinds of patterns:</para>
-
-<itemizedlist>
-
-
-  <listitem><para>If a pattern is a single identifier, then the
-  function matches any argument.  Example:
-
-  <programlisting>
-let negate = x: !x;
-    concat = x: y: x + y;
-in if negate true then concat "foo" "bar" else ""</programlisting>
-
-  Note that <function>concat</function> is a function that takes one
-  argument and returns a function that takes another argument.  This
-  allows partial parameterisation (i.e., only filling some of the
-  arguments of a function); e.g.,
-
-  <programlisting>
-map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
-
-  evaluates to <literal>[ "foobar" "foobla"
-  "fooabc" ]</literal>.</para></listitem>
-
-
-  <listitem><para>A <emphasis>set pattern</emphasis> of the form
-  <literal>{ name1, name2, …, nameN }</literal> matches a set
-  containing the listed attributes, and binds the values of those
-  attributes to variables in the function body.  For example, the
-  function
-
-<programlisting>
-{ x, y, z }: z + y + x</programlisting>
-
-  can only be called with a set containing exactly the attributes
-  <varname>x</varname>, <varname>y</varname> and
-  <varname>z</varname>.  No other attributes are allowed.  If you want
-  to allow additional arguments, you can use an ellipsis
-  (<literal>...</literal>):
-
-<programlisting>
-{ x, y, z, ... }: z + y + x</programlisting>
-
-  This works on any set that contains at least the three named
-  attributes.</para>
-
-  <para>It is possible to provide <emphasis>default values</emphasis>
-  for attributes, in which case they are allowed to be missing.  A
-  default value is specified by writing
-  <literal><replaceable>name</replaceable> ?
-  <replaceable>e</replaceable></literal>, where
-  <replaceable>e</replaceable> is an arbitrary expression.  For example,
-
-<programlisting>
-{ x, y ? "foo", z ? "bar" }: z + y + x</programlisting>
-
-  specifies a function that only requires an attribute named
-  <varname>x</varname>, but optionally accepts <varname>y</varname>
-  and <varname>z</varname>.</para></listitem>
-
-
-  <listitem><para>An <literal>@</literal>-pattern provides a means of referring
-  to the whole value being matched:
-
-<programlisting> args@{ x, y, z, ... }: z + y + x + args.a</programlisting>
-
-but can also be written as:
-
-<programlisting> { x, y, z, ... } @ args: z + y + x + args.a</programlisting>
-
-  Here <varname>args</varname> is bound to the entire argument, which
-  is further matched against the pattern <literal>{ x, y, z,
-  ... }</literal>. <literal>@</literal>-pattern makes mainly sense with an 
-  ellipsis(<literal>...</literal>) as you can access attribute names as 
-  <literal>a</literal>, using <literal>args.a</literal>, which was given as an
-  additional attribute to the function.
-  </para>
-
-  <warning>
-   <para>
-    The <literal>args@</literal> expression is bound to the argument passed to the function which
-    means that attributes with defaults that aren't explicitly specified in the function call
-    won't cause an evaluation error, but won't exist in <literal>args</literal>.
-   </para>
-   <para>
-    For instance
-<programlisting>
-let
-  function = args@{ a ? 23, ... }: args;
-in
- function {}
-</programlisting>
-    will evaluate to an empty attribute set.
-   </para>
-  </warning></listitem>
-
-</itemizedlist>
-
-<para>Note that functions do not have names.  If you want to give them
-a name, you can bind them to an attribute, e.g.,
-
-<programlisting>
-let concat = { x, y }: x + y;
-in concat { x = "foo"; y = "bar"; }</programlisting>
-
-</para>
-
-</simplesect>
-
-
-<simplesect><title>Conditionals</title>
-
-<para>Conditionals look like this:
-
-<programlisting>
-if <replaceable>e1</replaceable> then <replaceable>e2</replaceable> else <replaceable>e3</replaceable></programlisting>
-
-where <replaceable>e1</replaceable> is an expression that should
-evaluate to a Boolean value (<literal>true</literal> or
-<literal>false</literal>).</para>
-
-</simplesect>
-
-
-<simplesect><title>Assertions</title>
-
-<para>Assertions are generally used to check that certain requirements
-on or between features and dependencies hold.  They look like this:
-
-<programlisting>
-assert <replaceable>e1</replaceable>; <replaceable>e2</replaceable></programlisting>
-
-where <replaceable>e1</replaceable> is an expression that should
-evaluate to a Boolean value.  If it evaluates to
-<literal>true</literal>, <replaceable>e2</replaceable> is returned;
-otherwise expression evaluation is aborted and a backtrace is printed.</para>
-
-<example xml:id='ex-subversion-nix'><title>Nix expression for Subversion</title>
-<programlisting>
-{ localServer ? false
-, httpServer ? false
-, sslSupport ? false
-, pythonBindings ? false
-, javaSwigBindings ? false
-, javahlBindings ? false
-, stdenv, fetchurl
-, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null
-}:
-
-assert localServer -> db4 != null; <co xml:id='ex-subversion-nix-co-1' />
-assert httpServer -> httpd != null &amp;&amp; httpd.expat == expat; <co xml:id='ex-subversion-nix-co-2' />
-assert sslSupport -> openssl != null &amp;&amp; (httpServer -> httpd.openssl == openssl); <co xml:id='ex-subversion-nix-co-3' />
-assert pythonBindings -> swig != null &amp;&amp; swig.pythonSupport;
-assert javaSwigBindings -> swig != null &amp;&amp; swig.javaSupport;
-assert javahlBindings -> j2sdk != null;
-
-stdenv.mkDerivation {
-  name = "subversion-1.1.1";
-  ...
-  openssl = if sslSupport then openssl else null; <co xml:id='ex-subversion-nix-co-4' />
-  ...
-}</programlisting>
-</example>
-
-<para><xref linkend='ex-subversion-nix' /> show how assertions are
-used in the Nix expression for Subversion.</para>
-
-<calloutlist>
-
-  <callout arearefs='ex-subversion-nix-co-1'>
-    <para>This assertion states that if Subversion is to have support
-    for local repositories, then Berkeley DB is needed.  So if the
-    Subversion function is called with the
-    <varname>localServer</varname> argument set to
-    <literal>true</literal> but the <varname>db4</varname> argument
-    set to <literal>null</literal>, then the evaluation fails.</para>
-  </callout>
-
-  <callout arearefs='ex-subversion-nix-co-2'>
-    <para>This is a more subtle condition: if Subversion is built with
-    Apache (<literal>httpServer</literal>) support, then the Expat
-    library (an XML library) used by Subversion should be same as the
-    one used by Apache.  This is because in this configuration
-    Subversion code ends up being linked with Apache code, and if the
-    Expat libraries do not match, a build- or runtime link error or
-    incompatibility might occur.</para>
-  </callout>
-
-  <callout arearefs='ex-subversion-nix-co-3'>
-    <para>This assertion says that in order for Subversion to have SSL
-    support (so that it can access <literal>https</literal> URLs), an
-    OpenSSL library must be passed.  Additionally, it says that
-    <emphasis>if</emphasis> Apache support is enabled, then Apache's
-    OpenSSL should match Subversion's.  (Note that if Apache support
-    is not enabled, we don't care about Apache's OpenSSL.)</para>
-  </callout>
-
-  <callout arearefs='ex-subversion-nix-co-4'>
-    <para>The conditional here is not really related to assertions,
-    but is worth pointing out: it ensures that if SSL support is
-    disabled, then the Subversion derivation is not dependent on
-    OpenSSL, even if a non-<literal>null</literal> value was passed.
-    This prevents an unnecessary rebuild of Subversion if OpenSSL
-    changes.</para>
-  </callout>
-
-</calloutlist>
-
-</simplesect>
-
-
-
-<simplesect><title>With-expressions</title>
-
-<para>A <emphasis>with-expression</emphasis>,
-
-<programlisting>
-with <replaceable>e1</replaceable>; <replaceable>e2</replaceable></programlisting>
-
-introduces the set <replaceable>e1</replaceable> into the lexical
-scope of the expression <replaceable>e2</replaceable>.  For instance,
-
-<programlisting>
-let as = { x = "foo"; y = "bar"; };
-in with as; x + y</programlisting>
-
-evaluates to <literal>"foobar"</literal> since the
-<literal>with</literal> adds the <varname>x</varname> and
-<varname>y</varname> attributes of <varname>as</varname> to the
-lexical scope in the expression <literal>x + y</literal>.  The most
-common use of <literal>with</literal> is in conjunction with the
-<function>import</function> function.  E.g.,
-
-<programlisting>
-with (import ./definitions.nix); ...</programlisting>
-
-makes all attributes defined in the file
-<filename>definitions.nix</filename> available as if they were defined
-locally in a <literal>let</literal>-expression.</para>
-
-<para>The bindings introduced by <literal>with</literal> do not shadow bindings
-introduced by other means, e.g.
-
-<programlisting>
-let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ...</programlisting>
-
-establishes the same scope as
-
-<programlisting>
-let a = 1; in let a = 2; in let a = 3; in let a = 4; in ...</programlisting>
-
-</para>
-
-</simplesect>
-
-
-<simplesect><title>Comments</title>
-
-<para>Comments can be single-line, started with a <literal>#</literal>
-character, or inline/multi-line, enclosed within <literal>/*
-... */</literal>.</para>
-
-</simplesect>
-
-
-</section>
diff --git a/third_party/nix/doc/manual/expressions/language-operators.xml b/third_party/nix/doc/manual/expressions/language-operators.xml
deleted file mode 100644
index 4f11bf5293..0000000000
--- a/third_party/nix/doc/manual/expressions/language-operators.xml
+++ /dev/null
@@ -1,222 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-language-operators">
-
-<title>Operators</title>
-
-<para><xref linkend='table-operators' /> lists the operators in the
-Nix expression language, in order of precedence (from strongest to
-weakest binding).</para>
-
-<table xml:id='table-operators'>
-  <title>Operators</title>
-  <tgroup cols='3'>
-    <thead>
-      <row>
-        <entry>Name</entry>
-        <entry>Syntax</entry>
-        <entry>Associativity</entry>
-        <entry>Description</entry>
-        <entry>Precedence</entry>
-      </row>
-    </thead>
-    <tbody>
-      <row>
-        <entry>Select</entry>
-        <entry><replaceable>e</replaceable> <literal>.</literal>
-        <replaceable>attrpath</replaceable>
-        [ <literal>or</literal> <replaceable>def</replaceable> ]
-        </entry>
-        <entry>none</entry>
-        <entry>Select attribute denoted by the attribute path
-        <replaceable>attrpath</replaceable> from set
-        <replaceable>e</replaceable>.  (An attribute path is a
-        dot-separated list of attribute names.)  If the attribute
-        doesn’t exist, return <replaceable>def</replaceable> if
-        provided, otherwise abort evaluation.</entry>
-        <entry>1</entry>
-      </row>
-      <row>
-        <entry>Application</entry>
-        <entry><replaceable>e1</replaceable> <replaceable>e2</replaceable></entry>
-        <entry>left</entry>
-        <entry>Call function <replaceable>e1</replaceable> with
-        argument <replaceable>e2</replaceable>.</entry>
-        <entry>2</entry>
-      </row>
-      <row>
-        <entry>Arithmetic Negation</entry>
-        <entry><literal>-</literal> <replaceable>e</replaceable></entry>
-        <entry>none</entry>
-        <entry>Arithmetic negation.</entry>
-        <entry>3</entry>
-      </row>
-      <row>
-        <entry>Has Attribute</entry>
-        <entry><replaceable>e</replaceable> <literal>?</literal>
-        <replaceable>attrpath</replaceable></entry>
-        <entry>none</entry>
-        <entry>Test whether set <replaceable>e</replaceable> contains
-        the attribute denoted by <replaceable>attrpath</replaceable>;
-        return <literal>true</literal> or
-        <literal>false</literal>.</entry>
-        <entry>4</entry>
-      </row>
-      <row>
-        <entry>List Concatenation</entry>
-        <entry><replaceable>e1</replaceable> <literal>++</literal> <replaceable>e2</replaceable></entry>
-        <entry>right</entry>
-        <entry>List concatenation.</entry>
-        <entry>5</entry>
-      </row>
-      <row>
-        <entry>Multiplication</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>*</literal> <replaceable>e2</replaceable>,
-        </entry>
-        <entry>left</entry>
-        <entry>Arithmetic multiplication.</entry>
-        <entry>6</entry>
-      </row>
-      <row>
-        <entry>Division</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>/</literal> <replaceable>e2</replaceable>
-        </entry>
-        <entry>left</entry>
-        <entry>Arithmetic division.</entry>
-        <entry>6</entry>
-      </row>
-      <row>
-        <entry>Addition</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>+</literal> <replaceable>e2</replaceable>
-        </entry>
-        <entry>left</entry>
-        <entry>Arithmetic addition.</entry>
-        <entry>7</entry>
-      </row>
-      <row>
-        <entry>Subtraction</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>-</literal> <replaceable>e2</replaceable>
-        </entry>
-        <entry>left</entry>
-        <entry>Arithmetic subtraction.</entry>
-        <entry>7</entry>
-      </row>
-      <row>
-        <entry>String Concatenation</entry>
-        <entry>
-          <replaceable>string1</replaceable> <literal>+</literal> <replaceable>string2</replaceable>
-        </entry>
-        <entry>left</entry>
-        <entry>String concatenation.</entry>
-        <entry>7</entry>
-      </row>
-      <row>
-        <entry>Not</entry>
-        <entry><literal>!</literal> <replaceable>e</replaceable></entry>
-        <entry>none</entry>
-        <entry>Boolean negation.</entry>
-        <entry>8</entry>
-      </row>
-      <row>
-        <entry>Update</entry>
-        <entry><replaceable>e1</replaceable> <literal>//</literal>
-        <replaceable>e2</replaceable></entry>
-        <entry>right</entry>
-        <entry>Return a set consisting of the attributes in
-        <replaceable>e1</replaceable> and
-        <replaceable>e2</replaceable> (with the latter taking
-        precedence over the former in case of equally named
-        attributes).</entry>
-        <entry>9</entry>
-      </row>
-      <row>
-        <entry>Less Than</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>&lt;</literal> <replaceable>e2</replaceable>,
-        </entry>
-        <entry>none</entry>
-        <entry>Arithmetic comparison.</entry>
-        <entry>10</entry>
-      </row>
-      <row>
-        <entry>Less Than or Equal To</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>&lt;=</literal> <replaceable>e2</replaceable>
-        </entry>
-        <entry>none</entry>
-        <entry>Arithmetic comparison.</entry>
-        <entry>10</entry>
-      </row>
-      <row>
-        <entry>Greater Than</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>&gt;</literal> <replaceable>e2</replaceable>
-        </entry>
-        <entry>none</entry>
-        <entry>Arithmetic comparison.</entry>
-        <entry>10</entry>
-      </row>
-      <row>
-        <entry>Greater Than or Equal To</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>&gt;=</literal> <replaceable>e2</replaceable>
-        </entry>
-        <entry>none</entry>
-        <entry>Arithmetic comparison.</entry>
-        <entry>10</entry>
-      </row>
-      <row>
-        <entry>Equality</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>==</literal> <replaceable>e2</replaceable>
-        </entry>
-        <entry>none</entry>
-        <entry>Equality.</entry>
-        <entry>11</entry>
-      </row>
-      <row>
-        <entry>Inequality</entry>
-        <entry>
-          <replaceable>e1</replaceable> <literal>!=</literal> <replaceable>e2</replaceable>
-        </entry>
-        <entry>none</entry>
-        <entry>Inequality.</entry>
-        <entry>11</entry>
-      </row>
-      <row>
-        <entry>Logical AND</entry>
-        <entry><replaceable>e1</replaceable> <literal>&amp;&amp;</literal>
-        <replaceable>e2</replaceable></entry>
-        <entry>left</entry>
-        <entry>Logical AND.</entry>
-        <entry>12</entry>
-      </row>
-      <row>
-        <entry>Logical OR</entry>
-        <entry><replaceable>e1</replaceable> <literal>||</literal>
-        <replaceable>e2</replaceable></entry>
-        <entry>left</entry>
-        <entry>Logical OR.</entry>
-        <entry>13</entry>
-      </row>
-      <row>
-        <entry>Logical Implication</entry>
-        <entry><replaceable>e1</replaceable> <literal>-></literal>
-        <replaceable>e2</replaceable></entry>
-        <entry>none</entry>
-        <entry>Logical implication (equivalent to
-        <literal>!<replaceable>e1</replaceable> ||
-        <replaceable>e2</replaceable></literal>).</entry>
-        <entry>14</entry>
-      </row>
-    </tbody>
-  </tgroup>
-</table>
-
-</section>
diff --git a/third_party/nix/doc/manual/expressions/language-values.xml b/third_party/nix/doc/manual/expressions/language-values.xml
deleted file mode 100644
index bb2090c881..0000000000
--- a/third_party/nix/doc/manual/expressions/language-values.xml
+++ /dev/null
@@ -1,313 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='ssec-values'>
-
-<title>Values</title>
-
-
-<simplesect><title>Simple Values</title>
-
-<para>Nix has the following basic data types:
-
-<itemizedlist>
-
-  <listitem>
-
-    <para><emphasis>Strings</emphasis> can be written in three
-    ways.</para>
-
-    <para>The most common way is to enclose the string between double
-    quotes, e.g., <literal>"foo bar"</literal>.  Strings can span
-    multiple lines.  The special characters <literal>"</literal> and
-    <literal>\</literal> and the character sequence
-    <literal>${</literal> must be escaped by prefixing them with a
-    backslash (<literal>\</literal>).  Newlines, carriage returns and
-    tabs can be written as <literal>\n</literal>,
-    <literal>\r</literal> and <literal>\t</literal>,
-    respectively.</para>
-
-    <para>You can include the result of an expression into a string by
-    enclosing it in
-    <literal>${<replaceable>...</replaceable>}</literal>, a feature
-    known as <emphasis>antiquotation</emphasis>.  The enclosed
-    expression must evaluate to something that can be coerced into a
-    string (meaning that it must be a string, a path, or a
-    derivation).  For instance, rather than writing
-
-<programlisting>
-"--with-freetype2-library=" + freetype + "/lib"</programlisting>
-
-    (where <varname>freetype</varname> is a derivation), you can
-    instead write the more natural
-
-<programlisting>
-"--with-freetype2-library=${freetype}/lib"</programlisting>
-
-    The latter is automatically translated to the former.  A more
-    complicated example (from the Nix expression for <link
-    xlink:href='http://www.trolltech.com/products/qt'>Qt</link>):
-
-<programlisting>
-configureFlags = "
-  -system-zlib -system-libpng -system-libjpeg
-  ${if openglSupport then "-dlopen-opengl
-    -L${mesa}/lib -I${mesa}/include
-    -L${libXmu}/lib -I${libXmu}/include" else ""}
-  ${if threadSupport then "-thread" else "-no-thread"}
-";</programlisting>
-
-    Note that Nix expressions and strings can be arbitrarily nested;
-    in this case the outer string contains various antiquotations that
-    themselves contain strings (e.g., <literal>"-thread"</literal>),
-    some of which in turn contain expressions (e.g.,
-    <literal>${mesa}</literal>).</para>
-
-    <para>The second way to write string literals is as an
-    <emphasis>indented string</emphasis>, which is enclosed between
-    pairs of <emphasis>double single-quotes</emphasis>, like so:
-
-<programlisting>
-''
-  This is the first line.
-  This is the second line.
-    This is the third line.
-''</programlisting>
-
-    This kind of string literal intelligently strips indentation from
-    the start of each line.  To be precise, it strips from each line a
-    number of spaces equal to the minimal indentation of the string as
-    a whole (disregarding the indentation of empty lines).  For
-    instance, the first and second line are indented two space, while
-    the third line is indented four spaces.  Thus, two spaces are
-    stripped from each line, so the resulting string is
-
-<programlisting>
-"This is the first line.\nThis is the second line.\n  This is the third line.\n"</programlisting>
-
-    </para>
-
-    <para>Note that the whitespace and newline following the opening
-    <literal>''</literal> is ignored if there is no non-whitespace
-    text on the initial line.</para>
-
-    <para>Antiquotation
-    (<literal>${<replaceable>expr</replaceable>}</literal>) is
-    supported in indented strings.</para>
-
-    <para>Since <literal>${</literal> and <literal>''</literal> have
-    special meaning in indented strings, you need a way to quote them.
-    <literal>$</literal> can be escaped by prefixing it with
-    <literal>''</literal> (that is, two single quotes), i.e.,
-    <literal>''$</literal>. <literal>''</literal> can be escaped by
-    prefixing it with <literal>'</literal>, i.e.,
-    <literal>'''</literal>. <literal>$</literal> removes any special meaning
-    from the following <literal>$</literal>. Linefeed, carriage-return and tab
-    characters can be written as <literal>''\n</literal>,
-    <literal>''\r</literal>, <literal>''\t</literal>, and <literal>''\</literal>
-    escapes any other character.
-
-    </para>
-
-    <para>Indented strings are primarily useful in that they allow
-    multi-line string literals to follow the indentation of the
-    enclosing Nix expression, and that less escaping is typically
-    necessary for strings representing languages such as shell scripts
-    and configuration files because <literal>''</literal> is much less
-    common than <literal>"</literal>.  Example:
-
-<programlisting>
-stdenv.mkDerivation {
-  <replaceable>...</replaceable>
-  postInstall =
-    ''
-      mkdir $out/bin $out/etc
-      cp foo $out/bin
-      echo "Hello World" > $out/etc/foo.conf
-      ${if enableBar then "cp bar $out/bin" else ""}
-    '';
-  <replaceable>...</replaceable>
-}
-</programlisting>
-
-    </para>
-
-    <para>Finally, as a convenience, <emphasis>URIs</emphasis> as
-    defined in appendix B of <link
-    xlink:href='http://www.ietf.org/rfc/rfc2396.txt'>RFC 2396</link>
-    can be written <emphasis>as is</emphasis>, without quotes.  For
-    instance, the string
-    <literal>"http://example.org/foo.tar.bz2"</literal>
-    can also be written as
-    <literal>http://example.org/foo.tar.bz2</literal>.</para>
-
-  </listitem>
-
-  <listitem><para>Numbers, which can be <emphasis>integers</emphasis> (like
-  <literal>123</literal>) or <emphasis>floating point</emphasis> (like
-  <literal>123.43</literal> or <literal>.27e13</literal>).</para>
-
-  <para>Numbers are type-compatible: pure integer operations will always
-  return integers, whereas any operation involving at least one floating point
-  number will have a floating point number as a result.</para></listitem>
-
-  <listitem><para><emphasis>Paths</emphasis>, e.g.,
-  <filename>/bin/sh</filename> or <filename>./builder.sh</filename>.
-  A path must contain at least one slash to be recognised as such; for
-  instance, <filename>builder.sh</filename> is not a
-  path<footnote><para>It's parsed as an expression that selects the
-  attribute <varname>sh</varname> from the variable
-  <varname>builder</varname>.</para></footnote>.  If the file name is
-  relative, i.e., if it does not begin with a slash, it is made
-  absolute at parse time relative to the directory of the Nix
-  expression that contained it.  For instance, if a Nix expression in
-  <filename>/foo/bar/bla.nix</filename> refers to
-  <filename>../xyzzy/fnord.nix</filename>, the absolute path is
-  <filename>/foo/xyzzy/fnord.nix</filename>.</para>
-
-  <para>If the first component of a path is a <literal>~</literal>,
-  it is interpreted as if the rest of the path were relative to the
-  user's home directory. e.g. <filename>~/foo</filename> would be
-  equivalent to <filename>/home/edolstra/foo</filename> for a user
-  whose home directory is <filename>/home/edolstra</filename>.
-  </para>
-
-  <para>Paths can also be specified between angle brackets, e.g.
-  <literal>&lt;nixpkgs&gt;</literal>. This means that the directories
-  listed in the environment variable
-  <envar linkend="env-NIX_PATH">NIX_PATH</envar> will be searched
-  for the given file or directory name.
-  </para>
-
-  </listitem>
-
-  <listitem><para><emphasis>Booleans</emphasis> with values
-  <literal>true</literal> and
-  <literal>false</literal>.</para></listitem>
-
-  <listitem><para>The null value, denoted as
-  <literal>null</literal>.</para></listitem>
-
-</itemizedlist>
-
-</para>
-
-</simplesect>
-
-
-<simplesect><title>Lists</title>
-
-<para>Lists are formed by enclosing a whitespace-separated list of
-values between square brackets.  For example,
-
-<programlisting>
-[ 123 ./foo.nix "abc" (f { x = y; }) ]</programlisting>
-
-defines a list of four elements, the last being the result of a call
-to the function <varname>f</varname>.  Note that function calls have
-to be enclosed in parentheses.  If they had been omitted, e.g.,
-
-<programlisting>
-[ 123 ./foo.nix "abc" f { x = y; } ]</programlisting>
-
-the result would be a list of five elements, the fourth one being a
-function and the fifth being a set.</para>
-
-<para>Note that lists are only lazy in values, and they are strict in length.
-</para>
-
-</simplesect>
-
-
-<simplesect><title>Sets</title>
-
-<para>Sets are really the core of the language, since ultimately the
-Nix language is all about creating derivations, which are really just
-sets of attributes to be passed to build scripts.</para>
-
-<para>Sets are just a list of name/value pairs (called
-<emphasis>attributes</emphasis>) enclosed in curly brackets, where
-each value is an arbitrary expression terminated by a semicolon.  For
-example:
-
-<programlisting>
-{ x = 123;
-  text = "Hello";
-  y = f { bla = 456; };
-}</programlisting>
-
-This defines a set with attributes named <varname>x</varname>,
-<varname>text</varname>, <varname>y</varname>.  The order of the
-attributes is irrelevant.  An attribute name may only occur
-once.</para>
-
-<para>Attributes can be selected from a set using the
-<literal>.</literal> operator.  For instance,
-
-<programlisting>
-{ a = "Foo"; b = "Bar"; }.a</programlisting>
-
-evaluates to <literal>"Foo"</literal>.  It is possible to provide a
-default value in an attribute selection using the
-<literal>or</literal> keyword.  For example,
-
-<programlisting>
-{ a = "Foo"; b = "Bar"; }.c or "Xyzzy"</programlisting>
-
-will evaluate to <literal>"Xyzzy"</literal> because there is no
-<varname>c</varname> attribute in the set.</para>
-
-<para>You can use arbitrary double-quoted strings as attribute
-names:
-
-<programlisting>
-{ "foo ${bar}" = 123; "nix-1.0" = 456; }."foo ${bar}"
-</programlisting>
-
-This will evaluate to <literal>123</literal> (Assuming
-<literal>bar</literal> is antiquotable). In the case where an
-attribute name is just a single antiquotation, the quotes can be
-dropped:
-
-<programlisting>
-{ foo = 123; }.${bar} or 456 </programlisting>
-
-This will evaluate to <literal>123</literal> if
-<literal>bar</literal> evaluates to <literal>"foo"</literal> when
-coerced to a string and <literal>456</literal> otherwise (again
-assuming <literal>bar</literal> is antiquotable).</para>
-
-<para>In the special case where an attribute name inside of a set declaration
-evaluates to <literal>null</literal> (which is normally an error, as
-<literal>null</literal> is not antiquotable), that attribute is simply not
-added to the set:
-
-<programlisting>
-{ ${if foo then "bar" else null} = true; }</programlisting>
-
-This will evaluate to <literal>{}</literal> if <literal>foo</literal>
-evaluates to <literal>false</literal>.</para>
-
-<para>A set that has a <literal>__functor</literal> attribute whose value
-is callable (i.e. is itself a function or a set with a
-<literal>__functor</literal> attribute whose value is callable) can be
-applied as if it were a function, with the set itself passed in first
-, e.g.,
-
-<programlisting>
-let add = { __functor = self: x: x + self.x; };
-    inc = add // { x = 1; };
-in inc 1
-</programlisting>
-
-evaluates to <literal>2</literal>. This can be used to attach metadata to a
-function without the caller needing to treat it specially, or to implement
-a form of object-oriented programming, for example.
-
-</para>
-
-</simplesect>
-
-
-</section>
diff --git a/third_party/nix/doc/manual/expressions/simple-building-testing.xml b/third_party/nix/doc/manual/expressions/simple-building-testing.xml
deleted file mode 100644
index 7326a3e76a..0000000000
--- a/third_party/nix/doc/manual/expressions/simple-building-testing.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='sec-building-simple'>
-
-<title>Building and Testing</title>
-
-<para>You can now try to build Hello.  Of course, you could do
-<literal>nix-env -i hello</literal>, but you may not want to install a
-possibly broken package just yet.  The best way to test the package is by
-using the command <command linkend="sec-nix-build">nix-build</command>,
-which builds a Nix expression and creates a symlink named
-<filename>result</filename> in the current directory:
-
-<screen>
-$ nix-build -A hello
-building path `/nix/store/632d2b22514d...-hello-2.1.1'
-hello-2.1.1/
-hello-2.1.1/intl/
-hello-2.1.1/intl/ChangeLog
-<replaceable>...</replaceable>
-
-$ ls -l result
-lrwxrwxrwx ... 2006-09-29 10:43 result -> /nix/store/632d2b22514d...-hello-2.1.1
-
-$ ./result/bin/hello
-Hello, world!</screen>
-
-The <link linkend='opt-attr'><option>-A</option></link> option selects
-the <literal>hello</literal> attribute.  This is faster than using the
-symbolic package name specified by the <literal>name</literal>
-attribute (which also happens to be <literal>hello</literal>) and is
-unambiguous (there can be multiple packages with the symbolic name
-<literal>hello</literal>, but there can be only one attribute in a set
-named <literal>hello</literal>).</para>
-
-<para><command>nix-build</command> registers the
-<filename>./result</filename> symlink as a garbage collection root, so
-unless and until you delete the <filename>./result</filename> symlink,
-the output of the build will be safely kept on your system.  You can
-use <command>nix-build</command>’s <option
-linkend='opt-out-link'>-o</option> switch to give the symlink another
-name.</para>
-
-<para>Nix has transactional semantics.  Once a build finishes
-successfully, Nix makes a note of this in its database: it registers
-that the path denoted by <envar>out</envar> is now
-<quote>valid</quote>.  If you try to build the derivation again, Nix
-will see that the path is already valid and finish immediately.  If a
-build fails, either because it returns a non-zero exit code, because
-Nix or the builder are killed, or because the machine crashes, then
-the output paths will not be registered as valid.  If you try to build
-the derivation again, Nix will remove the output paths if they exist
-(e.g., because the builder died half-way through <literal>make
-install</literal>) and try again.  Note that there is no
-<quote>negative caching</quote>: Nix doesn't remember that a build
-failed, and so a failed build can always be repeated.  This is because
-Nix cannot distinguish between permanent failures (e.g., a compiler
-error due to a syntax error in the source) and transient failures
-(e.g., a disk full condition).</para>
-
-<para>Nix also performs locking.  If you run multiple Nix builds
-simultaneously, and they try to build the same derivation, the first
-Nix instance that gets there will perform the build, while the others
-block (or perform other derivations if available) until the build
-finishes:
-
-<screen>
-$ nix-build -A hello
-waiting for lock on `/nix/store/0h5b7hp8d4hqfrw8igvx97x1xawrjnac-hello-2.1.1x'</screen>
-
-So it is always safe to run multiple instances of Nix in parallel
-(which isn’t the case with, say, <command>make</command>).</para>
-
-<para>If you have a system with multiple CPUs, you may want to have
-Nix build different derivations in parallel (insofar as possible).
-Just pass the option <link linkend='opt-max-jobs'><option>-j
-<replaceable>N</replaceable></option></link>, where
-<replaceable>N</replaceable> is the maximum number of jobs to be run
-in parallel, or set.  Typically this should be the number of
-CPUs.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/expressions/simple-expression.xml b/third_party/nix/doc/manual/expressions/simple-expression.xml
deleted file mode 100644
index 29fd872eea..0000000000
--- a/third_party/nix/doc/manual/expressions/simple-expression.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-simple-expression">
-
-<title>A Simple Nix Expression</title>
-
-<para>This section shows how to add and test the <link
-xlink:href='http://www.gnu.org/software/hello/hello.html'>GNU Hello
-package</link> to the Nix Packages collection.  Hello is a program
-that prints out the text <quote>Hello, world!</quote>.</para>
-
-<para>To add a package to the Nix Packages collection, you generally
-need to do three things:
-
-<orderedlist>
-
-  <listitem><para>Write a Nix expression for the package.  This is a
-  file that describes all the inputs involved in building the package,
-  such as dependencies, sources, and so on.</para></listitem>
-
-  <listitem><para>Write a <emphasis>builder</emphasis>.  This is a
-  shell script<footnote><para>In fact, it can be written in any
-  language, but typically it's a <command>bash</command> shell
-  script.</para></footnote> that actually builds the package from
-  the inputs.</para></listitem>
-
-  <listitem><para>Add the package to the file
-  <filename>pkgs/top-level/all-packages.nix</filename>.  The Nix
-  expression written in the first step is a
-  <emphasis>function</emphasis>; it requires other packages in order
-  to build it.  In this step you put it all together, i.e., you call
-  the function with the right arguments to build the actual
-  package.</para></listitem>
-
-</orderedlist>
-
-</para>
-
-<xi:include href="expression-syntax.xml" />
-<xi:include href="build-script.xml" />
-<xi:include href="arguments-variables.xml" />
-<xi:include href="simple-building-testing.xml" />
-<xi:include href="generic-builder.xml" />
-
-</chapter>
diff --git a/third_party/nix/doc/manual/expressions/writing-nix-expressions.xml b/third_party/nix/doc/manual/expressions/writing-nix-expressions.xml
deleted file mode 100644
index 6646dddf08..0000000000
--- a/third_party/nix/doc/manual/expressions/writing-nix-expressions.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<part xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='chap-writing-nix-expressions'>
-
-<title>Writing Nix Expressions</title>
-
-<partintro>
-<para>This chapter shows you how to write Nix expressions, which 
-instruct Nix how to build packages.  It starts with a
-simple example (a Nix expression for GNU Hello), and then moves
-on to a more in-depth look at the Nix expression language.</para>
-
-<note><para>This chapter is mostly about the Nix expression language.
-For more extensive information on adding packages to the Nix Packages
-collection (such as functions in the standard environment and coding
-conventions), please consult <link
-xlink:href="http://nixos.org/nixpkgs/manual/">its
-manual</link>.</para></note>
-</partintro>
-
-<xi:include href="simple-expression.xml" />
-<xi:include href="expression-language.xml" />
-
-</part>
diff --git a/third_party/nix/doc/manual/figures/user-environments.png b/third_party/nix/doc/manual/figures/user-environments.png
deleted file mode 100644
index 1f781cf23c..0000000000
--- a/third_party/nix/doc/manual/figures/user-environments.png
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/figures/user-environments.sxd b/third_party/nix/doc/manual/figures/user-environments.sxd
deleted file mode 100644
index bc661b6406..0000000000
--- a/third_party/nix/doc/manual/figures/user-environments.sxd
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/glossary/glossary.xml b/third_party/nix/doc/manual/glossary/glossary.xml
deleted file mode 100644
index e3162ed8d4..0000000000
--- a/third_party/nix/doc/manual/glossary/glossary.xml
+++ /dev/null
@@ -1,199 +0,0 @@
-<appendix xmlns="http://docbook.org/ns/docbook"
-          xmlns:xlink="http://www.w3.org/1999/xlink"
-          xml:id="part-glossary">
-
-<title>Glossary</title>
-
-
-<glosslist>
-
-
-<glossentry xml:id="gloss-derivation"><glossterm>derivation</glossterm>
-
-  <glossdef><para>A description of a build action.  The result of a
-  derivation is a store object.  Derivations are typically specified
-  in Nix expressions using the <link
-  linkend="ssec-derivation"><function>derivation</function>
-  primitive</link>.  These are translated into low-level
-  <emphasis>store derivations</emphasis> (implicitly by
-  <command>nix-env</command> and <command>nix-build</command>, or
-  explicitly by <command>nix-instantiate</command>).</para></glossdef>
-
-</glossentry>
-
-
-<glossentry><glossterm>store</glossterm>
-
-  <glossdef><para>The location in the file system where store objects
-  live.  Typically <filename>/nix/store</filename>.</para></glossdef>
-
-</glossentry>
-
-
-<glossentry><glossterm>store path</glossterm>
-
-  <glossdef><para>The location in the file system of a store object,
-  i.e., an immediate child of the Nix store
-  directory.</para></glossdef>
-
-</glossentry>
-
-
-<glossentry><glossterm>store object</glossterm>
-
-  <glossdef><para>A file that is an immediate child of the Nix store
-  directory.  These can be regular files, but also entire directory
-  trees.  Store objects can be sources (objects copied from outside of
-  the store), derivation outputs (objects produced by running a build
-  action), or derivations (files describing a build
-  action).</para></glossdef>
-
-</glossentry>
-
-
-<glossentry xml:id="gloss-substitute"><glossterm>substitute</glossterm>
-
-  <glossdef><para>A substitute is a command invocation stored in the
-  Nix database that describes how to build a store object, bypassing
-  the normal build mechanism (i.e., derivations).  Typically, the
-  substitute builds the store object by downloading a pre-built
-  version of the store object from some server.</para></glossdef>
-
-</glossentry>
-
-
-<glossentry><glossterm>purity</glossterm>
-
-  <glossdef><para>The assumption that equal Nix derivations when run
-  always produce the same output.  This cannot be guaranteed in
-  general (e.g., a builder can rely on external inputs such as the
-  network or the system time) but the Nix model assumes
-  it.</para></glossdef>
-
-</glossentry>
-
-
-<glossentry><glossterm>Nix expression</glossterm>
-
-  <glossdef><para>A high-level description of software packages and
-  compositions thereof.  Deploying software using Nix entails writing
-  Nix expressions for your packages.  Nix expressions are translated
-  to derivations that are stored in the Nix store.  These derivations
-  can then be built.</para></glossdef>
-
-</glossentry>
-
-
-<glossentry xml:id="gloss-reference"><glossterm>reference</glossterm>
-
-  <glossdef>
-    <para>A store path <varname>P</varname> is said to have a
-    reference to a store path <varname>Q</varname> if the store object
-    at <varname>P</varname> contains the path <varname>Q</varname>
-    somewhere. The <emphasis>references</emphasis> of a store path are
-    the set of store paths to which it has a reference.
-    </para>
-    <para>A derivation can reference other derivations and sources
-    (but not output paths), whereas an output path only references other
-    output paths.
-    </para>
-  </glossdef>
-
-</glossentry>
-
-<glossentry xml:id="gloss-reachable"><glossterm>reachable</glossterm>
-
-  <glossdef><para>A store path <varname>Q</varname> is reachable from
-  another store path <varname>P</varname> if <varname>Q</varname> is in the
-  <link linkend="gloss-closure">closure</link> of the
-  <link linkend="gloss-reference">references</link> relation.
-  </para></glossdef>
-</glossentry>
-
-<glossentry xml:id="gloss-closure"><glossterm>closure</glossterm>
-
-  <glossdef><para>The closure of a store path is the set of store
-  paths that are directly or indirectly β€œreachable” from that store
-  path; that is, it’s the closure of the path under the <link
-  linkend="gloss-reference">references</link> relation. For a package, the
-  closure of its derivation is equivalent to the build-time
-  dependencies, while the closure of its output path is equivalent to its
-  runtime dependencies. For correct deployment it is necessary to deploy whole
-  closures, since otherwise at runtime files could be missing. The command
-  <command>nix-store -qR</command> prints out closures of store paths.
-  </para>
-  <para>As an example, if the store object at path <varname>P</varname> contains
-  a reference to path <varname>Q</varname>, then <varname>Q</varname> is
-  in the closure of <varname>P</varname>. Further, if <varname>Q</varname>
-  references <varname>R</varname> then <varname>R</varname> is also in
-  the closure of <varname>P</varname>.
-  </para></glossdef>
-
-</glossentry>
-
-
-<glossentry xml:id="gloss-output-path"><glossterm>output path</glossterm>
-
-  <glossdef><para>A store path produced by a derivation.</para></glossdef>
-
-</glossentry>
-
-
-<glossentry xml:id="gloss-deriver"><glossterm>deriver</glossterm>
-
-  <glossdef><para>The deriver of an <link
-  linkend="gloss-output-path">output path</link> is the store
-  derivation that built it.</para></glossdef>
-
-</glossentry>
-
-
-<glossentry xml:id="gloss-validity"><glossterm>validity</glossterm>
-
-  <glossdef><para>A store path is considered
-  <emphasis>valid</emphasis> if it exists in the file system, is
-  listed in the Nix database as being valid, and if all paths in its
-  closure are also valid.</para></glossdef>
-
-</glossentry>
-
-
-<glossentry xml:id="gloss-user-env"><glossterm>user environment</glossterm>
-
-  <glossdef><para>An automatically generated store object that
-  consists of a set of symlinks to β€œactive” applications, i.e., other
-  store paths.  These are generated automatically by <link
-  linkend="sec-nix-env"><command>nix-env</command></link>.  See <xref
-  linkend="sec-profiles" />.</para>
-
-  </glossdef>
-
-</glossentry>
-
-
-<glossentry xml:id="gloss-profile"><glossterm>profile</glossterm>
-
-  <glossdef><para>A symlink to the current <link
-  linkend="gloss-user-env">user environment</link> of a user, e.g.,
-  <filename>/nix/var/nix/profiles/default</filename>.</para></glossdef>
-
-</glossentry>
-
-
-<glossentry xml:id="gloss-nar"><glossterm>NAR</glossterm>
-
-  <glossdef><para>A <emphasis>N</emphasis>ix
-  <emphasis>AR</emphasis>chive.  This is a serialisation of a path in
-  the Nix store.  It can contain regular files, directories and
-  symbolic links.  NARs are generated and unpacked using
-  <command>nix-store --dump</command> and <command>nix-store
-  --restore</command>.</para></glossdef>
-
-</glossentry>
-
-
-
-</glosslist>
-
-
-</appendix>
diff --git a/third_party/nix/doc/manual/hacking.xml b/third_party/nix/doc/manual/hacking.xml
deleted file mode 100644
index b671811d3a..0000000000
--- a/third_party/nix/doc/manual/hacking.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<appendix xmlns="http://docbook.org/ns/docbook"
-          xmlns:xlink="http://www.w3.org/1999/xlink"
-          xml:id="chap-hacking">
-
-<title>Hacking</title>
-
-<para>This section provides some notes on how to hack on Nix.  To get
-the latest version of Nix from GitHub:
-<screen>
-$ git clone git://github.com/NixOS/nix.git
-$ cd nix
-</screen>
-</para>
-
-<para>To build it and its dependencies:
-<screen>
-$ nix-build release.nix -A build.x86_64-linux
-</screen>
-</para>
-
-<para>To build all dependencies and start a shell in which all
-environment variables are set up so that those dependencies can be
-found:
-<screen>
-$ nix-shell
-</screen>
-To build Nix itself in this shell:
-<screen>
-[nix-shell]$ ./bootstrap.sh
-[nix-shell]$ configurePhase
-[nix-shell]$ make
-</screen>
-To install it in <literal>$(pwd)/inst</literal> and test it:
-<screen>
-[nix-shell]$ make install
-[nix-shell]$ make installcheck
-</screen>
-
-</para>
-
-</appendix>
diff --git a/third_party/nix/doc/manual/images/callouts/1.gif b/third_party/nix/doc/manual/images/callouts/1.gif
deleted file mode 100644
index 9e7a87f754..0000000000
--- a/third_party/nix/doc/manual/images/callouts/1.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/10.gif b/third_party/nix/doc/manual/images/callouts/10.gif
deleted file mode 100644
index e80f7f8e63..0000000000
--- a/third_party/nix/doc/manual/images/callouts/10.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/11.gif b/third_party/nix/doc/manual/images/callouts/11.gif
deleted file mode 100644
index 67f91a239d..0000000000
--- a/third_party/nix/doc/manual/images/callouts/11.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/12.gif b/third_party/nix/doc/manual/images/callouts/12.gif
deleted file mode 100644
index 54c4b42f19..0000000000
--- a/third_party/nix/doc/manual/images/callouts/12.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/13.gif b/third_party/nix/doc/manual/images/callouts/13.gif
deleted file mode 100644
index dd5d7d9b64..0000000000
--- a/third_party/nix/doc/manual/images/callouts/13.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/14.gif b/third_party/nix/doc/manual/images/callouts/14.gif
deleted file mode 100644
index 3d7a952a31..0000000000
--- a/third_party/nix/doc/manual/images/callouts/14.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/15.gif b/third_party/nix/doc/manual/images/callouts/15.gif
deleted file mode 100644
index 1c9183d5bb..0000000000
--- a/third_party/nix/doc/manual/images/callouts/15.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/2.gif b/third_party/nix/doc/manual/images/callouts/2.gif
deleted file mode 100644
index 94d42a30f9..0000000000
--- a/third_party/nix/doc/manual/images/callouts/2.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/3.gif b/third_party/nix/doc/manual/images/callouts/3.gif
deleted file mode 100644
index dd3541a1bc..0000000000
--- a/third_party/nix/doc/manual/images/callouts/3.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/4.gif b/third_party/nix/doc/manual/images/callouts/4.gif
deleted file mode 100644
index 4bcbf7e31a..0000000000
--- a/third_party/nix/doc/manual/images/callouts/4.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/5.gif b/third_party/nix/doc/manual/images/callouts/5.gif
deleted file mode 100644
index 1c62b4f920..0000000000
--- a/third_party/nix/doc/manual/images/callouts/5.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/6.gif b/third_party/nix/doc/manual/images/callouts/6.gif
deleted file mode 100644
index 23bc5555d2..0000000000
--- a/third_party/nix/doc/manual/images/callouts/6.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/7.gif b/third_party/nix/doc/manual/images/callouts/7.gif
deleted file mode 100644
index e55ce89585..0000000000
--- a/third_party/nix/doc/manual/images/callouts/7.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/8.gif b/third_party/nix/doc/manual/images/callouts/8.gif
deleted file mode 100644
index 49375e09f4..0000000000
--- a/third_party/nix/doc/manual/images/callouts/8.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/images/callouts/9.gif b/third_party/nix/doc/manual/images/callouts/9.gif
deleted file mode 100644
index da12a4fe28..0000000000
--- a/third_party/nix/doc/manual/images/callouts/9.gif
+++ /dev/null
Binary files differdiff --git a/third_party/nix/doc/manual/installation/building-source.xml b/third_party/nix/doc/manual/installation/building-source.xml
deleted file mode 100644
index 772cda9cc3..0000000000
--- a/third_party/nix/doc/manual/installation/building-source.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-building-source">
-
-<title>Building Nix from Source</title>
-
-<para>After unpacking or checking out the Nix sources, issue the
-following commands:
-
-<screen>
-$ ./configure <replaceable>options...</replaceable>
-$ make
-$ make install</screen>
-
-Nix requires GNU Make so you may need to invoke
-<command>gmake</command> instead.</para>
-
-<para>When building from the Git repository, these should be preceded
-by the command:
-
-<screen>
-$ ./bootstrap.sh</screen>
-
-</para>
-
-<para>The installation path can be specified by passing the
-<option>--prefix=<replaceable>prefix</replaceable></option> to
-<command>configure</command>.  The default installation directory is
-<filename>/usr/local</filename>.  You can change this to any location
-you like.  You must have write permission to the
-<replaceable>prefix</replaceable> path.</para>
-
-<para>Nix keeps its <emphasis>store</emphasis> (the place where
-packages are stored) in <filename>/nix/store</filename> by default.
-This can be changed using
-<option>--with-store-dir=<replaceable>path</replaceable></option>.</para>
-
-<warning><para>It is best <emphasis>not</emphasis> to change the Nix
-store from its default, since doing so makes it impossible to use
-pre-built binaries from the standard Nixpkgs channels β€” that is, all
-packages will need to be built from source.</para></warning>
-
-<para>Nix keeps state (such as its database and log files) in
-<filename>/nix/var</filename> by default.  This can be changed using
-<option>--localstatedir=<replaceable>path</replaceable></option>.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/installation/env-variables.xml b/third_party/nix/doc/manual/installation/env-variables.xml
deleted file mode 100644
index e2b8fc867c..0000000000
--- a/third_party/nix/doc/manual/installation/env-variables.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-env-variables">
-
-<title>Environment Variables</title>
-
-<para>To use Nix, some environment variables should be set.  In
-particular, <envar>PATH</envar> should contain the directories
-<filename><replaceable>prefix</replaceable>/bin</filename> and
-<filename>~/.nix-profile/bin</filename>.  The first directory contains
-the Nix tools themselves, while <filename>~/.nix-profile</filename> is
-a symbolic link to the current <emphasis>user environment</emphasis>
-(an automatically generated package consisting of symlinks to
-installed packages).  The simplest way to set the required environment
-variables is to include the file
-<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
-in your <filename>~/.profile</filename> (or similar), like this:</para>
-
-<screen>
-source <replaceable>prefix</replaceable>/etc/profile.d/nix.sh</screen>
-
-<section xml:id="sec-nix-ssl-cert-file">
-
-<title><envar>NIX_SSL_CERT_FILE</envar></title>
-
-<para>If you need to specify a custom certificate bundle to account
-for an HTTPS-intercepting man in the middle proxy, you must specify
-the path to the certificate bundle in the environment variable
-<envar>NIX_SSL_CERT_FILE</envar>.</para>
-
-
-<para>If you don't specify a <envar>NIX_SSL_CERT_FILE</envar>
-manually, Nix will install and use its own certificate
-bundle.</para>
-
-<procedure>
-  <step><para>Set the environment variable and install Nix</para>
-    <screen>
-$ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
-$ sh &lt;(curl https://nixos.org/nix/install)
-</screen></step>
-
-  <step><para>In the shell profile and rc files (for example,
-  <filename>/etc/bashrc</filename>, <filename>/etc/zshrc</filename>),
-  add the following line:</para>
-<programlisting>
-export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
-</programlisting>
-</step>
-</procedure>
-
-<note><para>You must not add the export and then do the install, as
-the Nix installer will detect the presense of Nix configuration, and
-abort.</para></note>
-
-<section xml:id="sec-nix-ssl-cert-file-with-nix-daemon-and-macos">
-<title><envar>NIX_SSL_CERT_FILE</envar> with macOS and the Nix daemon</title>
-
-<para>On macOS you must specify the environment variable for the Nix
-daemon service, then restart it:</para>
-
-<screen>
-$ sudo launchctl setenv NIX_SSL_CERT_FILE /etc/ssl/my-certificate-bundle.crt
-$ sudo launchctl kickstart -k system/org.nixos.nix-daemon
-</screen>
-</section>
-
-<section xml:id="sec-installer-proxy-settings">
-
-<title>Proxy Environment Variables</title>
-
-<para>The Nix installer has special handling for these proxy-related
-environment variables:
-<varname>http_proxy</varname>, <varname>https_proxy</varname>,
-<varname>ftp_proxy</varname>, <varname>no_proxy</varname>,
-<varname>HTTP_PROXY</varname>, <varname>HTTPS_PROXY</varname>,
-<varname>FTP_PROXY</varname>, <varname>NO_PROXY</varname>.
-</para>
-<para>If any of these variables are set when running the Nix installer,
-then the installer will create an override file at
-<filename>/etc/systemd/system/nix-daemon.service.d/override.conf</filename>
-so <command>nix-daemon</command> will use them.
-</para>
-</section>
-
-</section>
-</chapter>
diff --git a/third_party/nix/doc/manual/installation/installation.xml b/third_party/nix/doc/manual/installation/installation.xml
deleted file mode 100644
index 8789593528..0000000000
--- a/third_party/nix/doc/manual/installation/installation.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<part xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="chap-installation">
-
-<title>Installation</title>
-
-<partintro>
-<para>This section describes how to install and configure Nix for first-time use.</para>
-</partintro>
-
-<xi:include href="supported-platforms.xml" />
-<xi:include href="installing-binary.xml" />
-<xi:include href="installing-source.xml" />
-<xi:include href="nix-security.xml" />
-<xi:include href="env-variables.xml" />
-
-<!-- TODO: should be updated
-<section><title>Upgrading Nix through Nix</title>
-
-<para>You can install the latest stable version of Nix through Nix
-itself by subscribing to the channel <link
-xlink:href="http://nixos.org/releases/nix/channels/nix-stable" />,
-or the latest unstable version by subscribing to the channel <link
-xlink:href="http://nixos.org/releases/nix/channels/nix-unstable" />.
-You can also do a <link linkend="sec-one-click">one-click
-installation</link> by clicking on the package links at <link
-xlink:href="http://nixos.org/releases/full-index-nix.html" />.</para>
-
-</section>
--->
-
-</part>
diff --git a/third_party/nix/doc/manual/installation/installing-binary.xml b/third_party/nix/doc/manual/installation/installing-binary.xml
deleted file mode 100644
index 394d8053b9..0000000000
--- a/third_party/nix/doc/manual/installation/installing-binary.xml
+++ /dev/null
@@ -1,190 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-installing-binary">
-
-<title>Installing a Binary Distribution</title>
-
-<para>If you are using Linux or macOS, the easiest way to install Nix
-is to run the following command:
-
-<screen>
-  $ sh &lt;(curl https://nixos.org/nix/install)
-</screen>
-
-As of Nix 2.1.0, the Nix installer will always default to creating a
-single-user installation, however opting in to the multi-user
-installation is highly recommended.
-</para>
-
-<section xml:id="sect-single-user-installation">
-  <title>Single User Installation</title>
-
-  <para>
-    To explicitly select a single-user installation on your system:
-
-    <screen>
-  sh &lt;(curl https://nixos.org/nix/install) --no-daemon
-</screen>
-  </para>
-
-<para>
-This will perform a single-user installation of Nix, meaning that
-<filename>/nix</filename> is owned by the invoking user.  You should
-run this under your usual user account, <emphasis>not</emphasis> as
-root.  The script will invoke <command>sudo</command> to create
-<filename>/nix</filename> if it doesn’t already exist.  If you don’t
-have <command>sudo</command>, you should manually create
-<command>/nix</command> first as root, e.g.:
-
-<screen>
-$ mkdir /nix
-$ chown alice /nix
-</screen>
-
-The install script will modify the first writable file from amongst
-<filename>.bash_profile</filename>, <filename>.bash_login</filename>
-and <filename>.profile</filename> to source
-<filename>~/.nix-profile/etc/profile.d/nix.sh</filename>. You can set
-the <command>NIX_INSTALLER_NO_MODIFY_PROFILE</command> environment
-variable before executing the install script to disable this
-behaviour.
-</para>
-
-
-<para>You can uninstall Nix simply by running:
-
-<screen>
-$ rm -rf /nix
-</screen>
-
-</para>
-</section>
-
-<section xml:id="sect-multi-user-installation">
-  <title>Multi User Installation</title>
-  <para>
-    The multi-user Nix installation creates system users, and a system
-    service for the Nix daemon.
-  </para>
-
-  <itemizedlist>
-    <title>Supported Systems</title>
-
-    <listitem>
-      <para>Linux running systemd, with SELinux disabled</para>
-    </listitem>
-    <listitem><para>macOS</para></listitem>
-  </itemizedlist>
-
-  <para>
-    You can instruct the installer to perform a multi-user
-    installation on your system:
-
-    <screen>
-  sh &lt;(curl https://nixos.org/nix/install) --daemon
-</screen>
-  </para>
-
-  <para>
-    The multi-user installation of Nix will create build users between
-    the user IDs 30001 and 30032, and a group with the group ID 30000.
-
-    You should run this under your usual user account,
-    <emphasis>not</emphasis> as root. The script will invoke
-    <command>sudo</command> as needed.
-  </para>
-
-  <note><para>
-    If you need Nix to use a different group ID or user ID set, you
-    will have to download the tarball manually and <link
-    linkend="sect-nix-install-binary-tarball">edit the install
-    script</link>.
-  </para></note>
-
-  <para>
-    The installer will modify <filename>/etc/bashrc</filename>, and
-    <filename>/etc/zshrc</filename> if they exist. The installer will
-    first back up these files with a
-    <literal>.backup-before-nix</literal> extension. The installer
-    will also create <filename>/etc/profile.d/nix.sh</filename>.
-  </para>
-
-  <para>You can uninstall Nix with the following commands:
-
-<screen>
-sudo rm -rf /etc/profile/nix.sh /etc/nix /nix ~root/.nix-profile ~root/.nix-defexpr ~root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
-
-# If you are on Linux with systemd, you will need to run:
-sudo systemctl stop nix-daemon.socket
-sudo systemctl stop nix-daemon.service
-sudo systemctl disable nix-daemon.socket
-sudo systemctl disable nix-daemon.service
-sudo systemctl daemon-reload
-
-# If you are on macOS, you will need to run:
-sudo launchctl unload /Library/LaunchDaemons/org.nixos.nix-daemon.plist
-sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
-</screen>
-
-    There may also be references to Nix in
-    <filename>/etc/profile</filename>,
-    <filename>/etc/bashrc</filename>, and
-    <filename>/etc/zshrc</filename> which you may remove.
-  </para>
-
-</section>
-
-<section xml:id="sect-nix-install-pinned-version-url">
-  <title>Installing a pinned Nix version from a URL</title>
-
-  <para>
-    NixOS.org hosts version-specific installation URLs for all Nix
-    versions since 1.11.16, at
-    <literal>https://nixos.org/releases/nix/nix-VERSION/install</literal>.
-  </para>
-
-  <para>
-    These install scripts can be used the same as the main
-  NixOS.org installation script:
-
-  <screen>
-  sh &lt;(curl https://nixos.org/nix/install)
-</screen>
-  </para>
-
-  <para>
-    In the same directory of the install script are sha256 sums, and
-    gpg signature files.
-  </para>
-</section>
-
-<section xml:id="sect-nix-install-binary-tarball">
-  <title>Installing from a binary tarball</title>
-
-  <para>
-    You can also download a binary tarball that contains Nix and all
-    its dependencies.  (This is what the install script at
-    <uri>https://nixos.org/nix/install</uri> does automatically.)  You
-    should unpack it somewhere (e.g. in <filename>/tmp</filename>),
-    and then run the script named <command>install</command> inside
-    the binary tarball:
-
-
-<screen>
-alice$ cd /tmp
-alice$ tar xfj nix-1.8-x86_64-darwin.tar.bz2
-alice$ cd nix-1.8-x86_64-darwin
-alice$ ./install
-</screen>
-  </para>
-
-  <para>
-    If you need to edit the multi-user installation script to use
-    different group ID or a different user ID range, modify the
-    variables set in the file named
-    <filename>install-multi-user</filename>.
-  </para>
-</section>
-</chapter>
diff --git a/third_party/nix/doc/manual/installation/installing-source.xml b/third_party/nix/doc/manual/installation/installing-source.xml
deleted file mode 100644
index c261a109d6..0000000000
--- a/third_party/nix/doc/manual/installation/installing-source.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-installing-source">
-
-<title>Installing Nix from Source</title>
-
-<para>If no binary package is available, you can download and compile
-a source distribution.</para>
-
-<xi:include href="prerequisites-source.xml" />
-<xi:include href="obtaining-source.xml" />
-<xi:include href="building-source.xml" />
-
-</chapter>
diff --git a/third_party/nix/doc/manual/installation/multi-user.xml b/third_party/nix/doc/manual/installation/multi-user.xml
deleted file mode 100644
index 69ae1ef270..0000000000
--- a/third_party/nix/doc/manual/installation/multi-user.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-multi-user">
-
-<title>Multi-User Mode</title>
-
-<para>To allow a Nix store to be shared safely among multiple users,
-it is important that users are not able to run builders that modify
-the Nix store or database in arbitrary ways, or that interfere with
-builds started by other users.  If they could do so, they could
-install a Trojan horse in some package and compromise the accounts of
-other users.</para>
-
-<para>To prevent this, the Nix store and database are owned by some
-privileged user (usually <literal>root</literal>) and builders are
-executed under special user accounts (usually named
-<literal>nixbld1</literal>, <literal>nixbld2</literal>, etc.).  When a
-unprivileged user runs a Nix command, actions that operate on the Nix
-store (such as builds) are forwarded to a <emphasis>Nix
-daemon</emphasis> running under the owner of the Nix store/database
-that performs the operation.</para>
-
-<note><para>Multi-user mode has one important limitation: only
-<systemitem class="username">root</systemitem> and a set of trusted
-users specified in <filename>nix.conf</filename> can specify arbitrary
-binary caches. So while unprivileged users may install packages from
-arbitrary Nix expressions, they may not get pre-built
-binaries.</para></note>
-
-
-<simplesect>
-
-<title>Setting up the build users</title>
-
-<para>The <emphasis>build users</emphasis> are the special UIDs under
-which builds are performed.  They should all be members of the
-<emphasis>build users group</emphasis> <literal>nixbld</literal>.
-This group should have no other members.  The build users should not
-be members of any other group. On Linux, you can create the group and
-users as follows:
-
-<screen>
-$ groupadd -r nixbld
-$ for n in $(seq 1 10); do useradd -c "Nix build user $n" \
-    -d /var/empty -g nixbld -G nixbld -M -N -r -s "$(which nologin)" \
-    nixbld$n; done
-</screen>
-
-This creates 10 build users. There can never be more concurrent builds
-than the number of build users, so you may want to increase this if
-you expect to do many builds at the same time.</para>
-
-</simplesect>
-
-
-<simplesect>
-
-<title>Running the daemon</title>
-
-<para>The <link linkend="sec-nix-daemon">Nix daemon</link> should be
-started as follows (as <literal>root</literal>):
-
-<screen>
-$ nix-daemon</screen>
-
-You’ll want to put that line somewhere in your system’s boot
-scripts.</para>
-
-<para>To let unprivileged users use the daemon, they should set the
-<link linkend="envar-remote"><envar>NIX_REMOTE</envar> environment
-variable</link> to <literal>daemon</literal>.  So you should put a
-line like
-
-<programlisting>
-export NIX_REMOTE=daemon</programlisting>
-
-into the users’ login scripts.</para>
-
-</simplesect>
-
-
-<simplesect>
-
-<title>Restricting access</title>
-
-<para>To limit which users can perform Nix operations, you can use the
-permissions on the directory
-<filename>/nix/var/nix/daemon-socket</filename>.  For instance, if you
-want to restrict the use of Nix to the members of a group called
-<literal>nix-users</literal>, do
-
-<screen>
-$ chgrp nix-users /nix/var/nix/daemon-socket
-$ chmod ug=rwx,o= /nix/var/nix/daemon-socket
-</screen>
-
-This way, users who are not in the <literal>nix-users</literal> group
-cannot connect to the Unix domain socket
-<filename>/nix/var/nix/daemon-socket/socket</filename>, so they cannot
-perform Nix operations.</para>
-
-</simplesect>
-
-
-</section>
diff --git a/third_party/nix/doc/manual/installation/nix-security.xml b/third_party/nix/doc/manual/installation/nix-security.xml
deleted file mode 100644
index d888ff14d4..0000000000
--- a/third_party/nix/doc/manual/installation/nix-security.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-nix-security">
-
-<title>Security</title>
-
-<para>Nix has two basic security models.  First, it can be used in
-β€œsingle-user mode”, which is similar to what most other package
-management tools do: there is a single user (typically <systemitem
-class="username">root</systemitem>) who performs all package
-management operations.  All other users can then use the installed
-packages, but they cannot perform package management operations
-themselves.</para>
-
-<para>Alternatively, you can configure Nix in β€œmulti-user mode”.  In
-this model, all users can perform package management operations β€” for
-instance, every user can install software without requiring root
-privileges.  Nix ensures that this is secure.  For instance, it’s not
-possible for one user to overwrite a package used by another user with
-a Trojan horse.</para>
-
-<xi:include href="single-user.xml" />
-<xi:include href="multi-user.xml" />
-
-</chapter>
\ No newline at end of file
diff --git a/third_party/nix/doc/manual/installation/obtaining-source.xml b/third_party/nix/doc/manual/installation/obtaining-source.xml
deleted file mode 100644
index 968822cc06..0000000000
--- a/third_party/nix/doc/manual/installation/obtaining-source.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-obtaining-source">
-
-<title>Obtaining a Source Distribution</title>
-
-<para>The source tarball of the most recent stable release can be
-downloaded from the <link
-xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>.
-You can also grab the <link
-xlink:href="http://hydra.nixos.org/job/nix/master/release/latest-finished#tabs-constituents">most
-recent development release</link>.</para>
-
-<para>Alternatively, the most recent sources of Nix can be obtained
-from its <link
-xlink:href="https://github.com/NixOS/nix">Git
-repository</link>.  For example, the following command will check out
-the latest revision into a directory called
-<filename>nix</filename>:</para>
-
-<screen>
-$ git clone https://github.com/NixOS/nix</screen>
-
-<para>Likewise, specific releases can be obtained from the <link
-xlink:href="https://github.com/NixOS/nix/tags">tags</link> of the
-repository.</para>
-
-</section>
\ No newline at end of file
diff --git a/third_party/nix/doc/manual/installation/prerequisites-source.xml b/third_party/nix/doc/manual/installation/prerequisites-source.xml
deleted file mode 100644
index e7bdcf966c..0000000000
--- a/third_party/nix/doc/manual/installation/prerequisites-source.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-prerequisites-source">
-
-<title>Prerequisites</title>
-
-<itemizedlist>
-
-  <listitem><para>GNU Make.</para></listitem>
-  
-  <listitem><para>Bash Shell. The <literal>./configure</literal> script
-  relies on bashisms, so Bash is required.</para></listitem>
-
-  <listitem><para>A version of GCC or Clang that supports C++17.</para></listitem>
-
-  <listitem><para><command>pkg-config</command> to locate
-  dependencies.  If your distribution does not provide it, you can get
-  it from <link
-  xlink:href="http://www.freedesktop.org/wiki/Software/pkg-config"
-  />.</para></listitem>
-
-  <listitem><para>The OpenSSL library to calculate cryptographic hashes.
-  If your distribution does not provide it, you can get it from <link
-  xlink:href="https://www.openssl.org"/>.</para></listitem>
-
-  <listitem><para>The <literal>libbrotlienc</literal> and
-  <literal>libbrotlidec</literal> libraries to provide implementation
-  of the Brotli compression algorithm. They are available for download
-  from the official repository <link
-  xlink:href="https://github.com/google/brotli" />.</para></listitem>
-
-  <listitem><para>The bzip2 compressor program and the
-  <literal>libbz2</literal> library.  Thus you must have bzip2
-  installed, including development headers and libraries.  If your
-  distribution does not provide these, you can obtain bzip2 from <link
-  xlink:href="https://web.archive.org/web/20180624184756/http://www.bzip.org/"
-  />.</para></listitem>
-
-  <listitem><para><literal>liblzma</literal>, which is provided by
-  XZ Utils. If your distribution does not provide this, you can
-  get it from <link xlink:href="https://tukaani.org/xz/"/>.</para></listitem>
-  
-  <listitem><para>cURL and its library. If your distribution does not
-  provide it, you can get it from <link
-  xlink:href="https://curl.haxx.se/"/>.</para></listitem>
-      
-  <listitem><para>The SQLite embedded database library, version 3.6.19
-  or higher.  If your distribution does not provide it, please install
-  it from <link xlink:href="http://www.sqlite.org/" />.</para></listitem>
-
-  <listitem><para>The <link
-  xlink:href="http://www.hboehm.info/gc/">Boehm
-  garbage collector</link> to reduce the evaluator’s memory
-  consumption (optional).  To enable it, install
-  <literal>pkgconfig</literal> and the Boehm garbage collector, and
-  pass the flag <option>--enable-gc</option> to
-  <command>configure</command>.</para></listitem>
-
-  <listitem><para>The <literal>boost</literal> library of version
-  1.66.0 or higher. It can be obtained from the official web site
-  <link xlink:href="https://www.boost.org/" />.</para></listitem>
-
-  <listitem><para>The <literal>editline</literal> library of version
-  1.14.0 or higher. It can be obtained from the its repository
-  <link xlink:href="https://github.com/troglobit/editline" />.</para></listitem>
-
-  <listitem><para>The <command>xmllint</command> and
-  <command>xsltproc</command> programs to build this manual and the
-  man-pages.  These are part of the <literal>libxml2</literal> and
-  <literal>libxslt</literal> packages, respectively.  You also need
-  the <link
-  xlink:href="http://docbook.sourceforge.net/projects/xsl/">DocBook
-  XSL stylesheets</link> and optionally the <link
-  xlink:href="http://www.docbook.org/schemas/5x"> DocBook 5.0 RELAX NG
-  schemas</link>.  Note that these are only required if you modify the
-  manual sources or when you are building from the Git
-  repository.</para></listitem>
-
-  <listitem><para>Recent versions of Bison and Flex to build the
-  parser.  (This is because Nix needs GLR support in Bison and
-  reentrancy support in Flex.)  For Bison, you need version 2.6, which
-  can be obtained from the <link
-  xlink:href="ftp://alpha.gnu.org/pub/gnu/bison">GNU FTP
-  server</link>.  For Flex, you need version 2.5.35, which is
-  available on <link
-  xlink:href="http://lex.sourceforge.net/">SourceForge</link>.
-  Slightly older versions may also work, but ancient versions like the
-  ubiquitous 2.5.4a won't.  Note that these are only required if you
-  modify the parser or when you are building from the Git
-  repository.</para></listitem>
-
-  <listitem><para>The <literal>libseccomp</literal> is used to provide
-  syscall filtering on Linux. This is an optional dependency and can
-  be disabled passing a <option>--disable-seccomp-sandboxing</option>
-  option to the <command>configure</command> script (Not recommended
-  unless your system doesn't support
-  <literal>libseccomp</literal>). To get the library, visit <link
-  xlink:href="https://github.com/seccomp/libseccomp"
-  />.</para></listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/installation/single-user.xml b/third_party/nix/doc/manual/installation/single-user.xml
deleted file mode 100644
index 09cdaa5d48..0000000000
--- a/third_party/nix/doc/manual/installation/single-user.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-single-user">
-
-<title>Single-User Mode</title>
-
-<para>In single-user mode, all Nix operations that access the database
-in <filename><replaceable>prefix</replaceable>/var/nix/db</filename>
-or modify the Nix store in
-<filename><replaceable>prefix</replaceable>/store</filename> must be
-performed under the user ID that owns those directories.  This is
-typically <systemitem class="username">root</systemitem>.  (If you
-install from RPM packages, that’s in fact the default ownership.)
-However, on single-user machines, it is often convenient to
-<command>chown</command> those directories to your normal user account
-so that you don’t have to <command>su</command> to <systemitem
-class="username">root</systemitem> all the time.</para>
-
-</section>
\ No newline at end of file
diff --git a/third_party/nix/doc/manual/installation/supported-platforms.xml b/third_party/nix/doc/manual/installation/supported-platforms.xml
deleted file mode 100644
index 3e74be49d1..0000000000
--- a/third_party/nix/doc/manual/installation/supported-platforms.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-supported-platforms">
-
-<title>Supported Platforms</title>
-
-<para>Nix is currently supported on the following platforms:
-
-<itemizedlist>
-
-  <listitem><para>Linux (i686, x86_64, aarch64).</para></listitem>
-
-  <listitem><para>macOS (x86_64).</para></listitem>
-
-  <!--
-  <listitem><para>FreeBSD (only tested on Intel).</para></listitem>
-  -->
-
-  <!--
-  <listitem><para>Windows through <link
-  xlink:href="http://www.cygwin.com/">Cygwin</link>.</para>
-
-  <warning><para>On Cygwin, Nix <emphasis>must</emphasis> be installed
-  on an NTFS partition.  It will not work correctly on a FAT
-  partition.</para></warning>
-
-  </listitem>
-  -->
-
-</itemizedlist>
-
-</para>
-
-</chapter>
diff --git a/third_party/nix/doc/manual/installation/upgrading.xml b/third_party/nix/doc/manual/installation/upgrading.xml
deleted file mode 100644
index 30670d7fec..0000000000
--- a/third_party/nix/doc/manual/installation/upgrading.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-upgrading-nix">
-
-  <title>Upgrading Nix</title>
-
-  <para>
-    Multi-user Nix users on macOS can upgrade Nix by running:
-    <command>sudo -i sh -c 'nix-channel --update &amp;&amp;
-    nix-env -iA nixpkgs.nix &amp;&amp;
-    launchctl remove org.nixos.nix-daemon &amp;&amp;
-    launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist'</command>
-  </para>
-
-
-  <para>
-    Single-user installations of Nix should run this:
-    <command>nix-channel --update; nix-env -iA nixpkgs.nix</command>
-  </para>
-</chapter>
diff --git a/third_party/nix/doc/manual/introduction/about-nix.xml b/third_party/nix/doc/manual/introduction/about-nix.xml
deleted file mode 100644
index c21ed34ddc..0000000000
--- a/third_party/nix/doc/manual/introduction/about-nix.xml
+++ /dev/null
@@ -1,268 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-about-nix">
-
-<title>About Nix</title>
-
-<para>Nix is a <emphasis>purely functional package manager</emphasis>.
-This means that it treats packages like values in purely functional
-programming languages such as Haskell β€” they are built by functions
-that don’t have side-effects, and they never change after they have
-been built.  Nix stores packages in the <emphasis>Nix
-store</emphasis>, usually the directory
-<filename>/nix/store</filename>, where each package has its own unique
-subdirectory such as
-
-<programlisting>
-/nix/store/b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z-firefox-33.1/
-</programlisting>
-
-where <literal>b6gvzjyb2pg0…</literal> is a unique identifier for the
-package that captures all its dependencies (it’s a cryptographic hash
-of the package’s build dependency graph).  This enables many powerful
-features.</para>
-
-
-<simplesect><title>Multiple versions</title>
-
-<para>You can have multiple versions or variants of a package
-installed at the same time.  This is especially important when
-different applications have dependencies on different versions of the
-same package β€” it prevents the β€œDLL hell”.  Because of the hashing
-scheme, different versions of a package end up in different paths in
-the Nix store, so they don’t interfere with each other.</para>
-
-<para>An important consequence is that operations like upgrading or
-uninstalling an application cannot break other applications, since
-these operations never β€œdestructively” update or delete files that are
-used by other packages.</para>
-
-</simplesect>
-
-
-<simplesect><title>Complete dependencies</title>
-
-<para>Nix helps you make sure that package dependency specifications
-are complete.  In general, when you’re making a package for a package
-management system like RPM, you have to specify for each package what
-its dependencies are, but there are no guarantees that this
-specification is complete.  If you forget a dependency, then the
-package will build and work correctly on <emphasis>your</emphasis>
-machine if you have the dependency installed, but not on the end
-user's machine if it's not there.</para>
-
-<para>Since Nix on the other hand doesn’t install packages in β€œglobal”
-locations like <filename>/usr/bin</filename> but in package-specific
-directories, the risk of incomplete dependencies is greatly reduced.
-This is because tools such as compilers don’t search in per-packages
-directories such as
-<filename>/nix/store/5lbfaxb722zp…-openssl-0.9.8d/include</filename>,
-so if a package builds correctly on your system, this is because you
-specified the dependency explicitly. This takes care of the build-time
-dependencies.</para>
-
-<para>Once a package is built, runtime dependencies are found by
-scanning binaries for the hash parts of Nix store paths (such as
-<literal>r8vvq9kq…</literal>).  This sounds risky, but it works
-extremely well.</para>
-
-</simplesect>
-
-
-<simplesect><title>Multi-user support</title>
-
-<para>Nix has multi-user support.  This means that non-privileged
-users can securely install software.  Each user can have a different
-<emphasis>profile</emphasis>, a set of packages in the Nix store that
-appear in the user’s <envar>PATH</envar>.  If a user installs a
-package that another user has already installed previously, the
-package won’t be built or downloaded a second time.  At the same time,
-it is not possible for one user to inject a Trojan horse into a
-package that might be used by another user.</para>
-
-</simplesect>
-
-
-<simplesect><title>Atomic upgrades and rollbacks</title>
-
-<para>Since package management operations never overwrite packages in
-the Nix store but just add new versions in different paths, they are
-<emphasis>atomic</emphasis>.  So during a package upgrade, there is no
-time window in which the package has some files from the old version
-and some files from the new version β€” which would be bad because a
-program might well crash if it’s started during that period.</para>
-
-<para>And since packages aren’t overwritten, the old versions are still
-there after an upgrade.  This means that you can <emphasis>roll
-back</emphasis> to the old version:</para>
-
-<screen>
-$ nix-env --upgrade <replaceable>some-packages</replaceable>
-$ nix-env --rollback
-</screen>
-
-</simplesect>
-
-
-<simplesect><title>Garbage collection</title>
-
-<para>When you uninstall a package like this…
-
-<screen>
-$ nix-env --uninstall firefox
-</screen>
-
-the package isn’t deleted from the system right away (after all, you
-might want to do a rollback, or it might be in the profiles of other
-users).  Instead, unused packages can be deleted safely by running the
-<emphasis>garbage collector</emphasis>:
-
-<screen>
-$ nix-collect-garbage
-</screen>
-
-This deletes all packages that aren’t in use by any user profile or by
-a currently running program.</para>
-
-</simplesect>
-
-
-<simplesect><title>Functional package language</title>
-
-<para>Packages are built from <emphasis>Nix expressions</emphasis>,
-which is a simple functional language.  A Nix expression describes
-everything that goes into a package build action (a β€œderivation”):
-other packages, sources, the build script, environment variables for
-the build script, etc.  Nix tries very hard to ensure that Nix
-expressions are <emphasis>deterministic</emphasis>: building a Nix
-expression twice should yield the same result.</para>
-
-<para>Because it’s a functional language, it’s easy to support
-building variants of a package: turn the Nix expression into a
-function and call it any number of times with the appropriate
-arguments.  Due to the hashing scheme, variants don’t conflict with
-each other in the Nix store.</para>
-
-</simplesect>
-
-
-<simplesect><title>Transparent source/binary deployment</title>
-
-<para>Nix expressions generally describe how to build a package from
-source, so an installation action like
-
-<screen>
-$ nix-env --install firefox
-</screen>
-
-<emphasis>could</emphasis> cause quite a bit of build activity, as not
-only Firefox but also all its dependencies (all the way up to the C
-library and the compiler) would have to built, at least if they are
-not already in the Nix store.  This is a <emphasis>source deployment
-model</emphasis>.  For most users, building from source is not very
-pleasant as it takes far too long.  However, Nix can automatically
-skip building from source and instead use a <emphasis>binary
-cache</emphasis>, a web server that provides pre-built binaries. For
-instance, when asked to build
-<literal>/nix/store/b6gvzjyb2pg0…-firefox-33.1</literal> from source,
-Nix would first check if the file
-<uri>https://cache.nixos.org/b6gvzjyb2pg0….narinfo</uri> exists, and
-if so, fetch the pre-built binary referenced from there; otherwise, it
-would fall back to building from source.</para>
-
-</simplesect>
-
-
-<!--
-<simplesect><title>Binary patching</title>
-
-<para>In addition to downloading binaries automatically if they’re
-available, Nix can download binary deltas that patch an existing
-package in the Nix store into a new version.  This speeds up
-upgrades.</para>
-
-</simplesect>
--->
-
-
-<simplesect><title>Nix Packages collection</title>
-
-<para>We provide a large set of Nix expressions containing hundreds of
-existing Unix packages, the <emphasis>Nix Packages
-collection</emphasis> (Nixpkgs).</para>
-
-</simplesect>
-
-
-<simplesect><title>Managing build environments</title>
-
-<para>Nix is extremely useful for developers as it makes it easy to
-automatically set up the build environment for a package. Given a
-Nix expression that describes the dependencies of your package, the
-command <command>nix-shell</command> will build or download those
-dependencies if they’re not already in your Nix store, and then start
-a Bash shell in which all necessary environment variables (such as
-compiler search paths) are set.</para>
-
-<para>For example, the following command gets all dependencies of the
-Pan newsreader, as described by <link
-xlink:href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/networking/newsreaders/pan/default.nix">its
-Nix expression</link>:</para>
-
-<screen>
-$ nix-shell '&lt;nixpkgs>' -A pan
-</screen>
-
-<para>You’re then dropped into a shell where you can edit, build and test
-the package:</para>
-
-<screen>
-[nix-shell]$ tar xf $src
-[nix-shell]$ cd pan-*
-[nix-shell]$ ./configure
-[nix-shell]$ make
-[nix-shell]$ ./pan/gui/pan
-</screen>
-
-<!--
-<para>Since Nix packages are reproducible and have complete dependency
-specifications, Nix makes an excellent basis for <a
-href="[%root%]hydra">a continuous build system</a>.</para>
--->
-
-</simplesect>
-
-
-<simplesect><title>Portability</title>
-
-<para>Nix runs on Linux and macOS.</para>
-
-</simplesect>
-
-
-<simplesect><title>NixOS</title>
-
-<para>NixOS is a Linux distribution based on Nix.  It uses Nix not
-just for package management but also to manage the system
-configuration (e.g., to build configuration files in
-<filename>/etc</filename>).  This means, among other things, that it
-is easy to roll back the entire configuration of the system to an
-earlier state.  Also, users can install software without root
-privileges.  For more information and downloads, see the <link
-xlink:href="http://nixos.org/">NixOS homepage</link>.</para>
-
-</simplesect>
-
-
-<simplesect><title>License</title>
-
-<para>Nix is released under the terms of the <link
-xlink:href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html">GNU
-LGPLv2.1 or (at your option) any later version</link>.</para>
-
-</simplesect>
-
-
-</chapter>
diff --git a/third_party/nix/doc/manual/introduction/introduction.xml b/third_party/nix/doc/manual/introduction/introduction.xml
deleted file mode 100644
index 12b2cc7610..0000000000
--- a/third_party/nix/doc/manual/introduction/introduction.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<part xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="chap-introduction">
-
-<title>Introduction</title>
-
-<xi:include href="about-nix.xml" />
-<xi:include href="quick-start.xml" />
-
-</part>
diff --git a/third_party/nix/doc/manual/introduction/quick-start.xml b/third_party/nix/doc/manual/introduction/quick-start.xml
deleted file mode 100644
index 1ce6c8d50a..0000000000
--- a/third_party/nix/doc/manual/introduction/quick-start.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="chap-quick-start">
-
-<title>Quick Start</title>
-
-<para>This chapter is for impatient people who don't like reading
-documentation.  For more in-depth information you are kindly referred
-to subsequent chapters.</para>
-
-<procedure>
-
-<step><para>Install single-user Nix by running the following:
-
-<screen>
-$ bash &lt;(curl https://nixos.org/nix/install)
-</screen>
-
-This will install Nix in <filename>/nix</filename>. The install script
-will create <filename>/nix</filename> using <command>sudo</command>,
-so make sure you have sufficient rights.  (For other installation
-methods, see <xref linkend="chap-installation"/>.)</para></step>
-
-<step><para>See what installable packages are currently available
-in the channel:
-
-<screen>
-$ nix-env -qa
-docbook-xml-4.3
-docbook-xml-4.5
-firefox-33.0.2
-hello-2.9
-libxslt-1.1.28
-<replaceable>...</replaceable></screen>
-
-</para></step>
-
-<step><para>Install some packages from the channel:
-
-<screen>
-$ nix-env -i hello</screen>
-
-This should download pre-built packages; it should not build them
-locally (if it does, something went wrong).</para></step>
-
-<step><para>Test that they work:
-
-<screen>
-$ which hello
-/home/eelco/.nix-profile/bin/hello
-$ hello
-Hello, world!
-</screen>
-
-</para></step>
-
-<step><para>Uninstall a package:
-
-<screen>
-$ nix-env -e hello</screen>
-
-</para></step>
-
-<step><para>You can also test a package without installing it:
-
-<screen>
-$ nix-shell -p hello
-</screen>
-
-This builds or downloads GNU Hello and its dependencies, then drops
-you into a Bash shell where the <command>hello</command> command is
-present, all without affecting your normal environment:
-
-<screen>
-[nix-shell:~]$ hello
-Hello, world!
-
-[nix-shell:~]$ exit
-
-$ hello
-hello: command not found
-</screen>
-
-</para></step>
-
-<step><para>To keep up-to-date with the channel, do:
-
-<screen>
-$ nix-channel --update nixpkgs
-$ nix-env -u '*'</screen>
-
-The latter command will upgrade each installed package for which there
-is a β€œnewer” version (as determined by comparing the version
-numbers).</para></step>
-
-<step><para>If you're unhappy with the result of a
-<command>nix-env</command> action (e.g., an upgraded package turned
-out not to work properly), you can go back:
-
-<screen>
-$ nix-env --rollback</screen>
-
-</para></step>
-
-<step><para>You should periodically run the Nix garbage collector
-to get rid of unused packages, since uninstalls or upgrades don't
-actually delete them:
-
-<screen>
-$ nix-collect-garbage -d</screen>
-
-<!--
-The first command deletes old β€œgenerations” of your profile (making
-rollbacks impossible, but also making the packages in those old
-generations available for garbage collection), while the second
-command actually deletes them.-->
-
-</para></step>
-
-</procedure>
-
-</chapter>
diff --git a/third_party/nix/doc/manual/manual.xml b/third_party/nix/doc/manual/manual.xml
deleted file mode 100644
index 87d9de28ab..0000000000
--- a/third_party/nix/doc/manual/manual.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<book xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0">
-
-  <info>
-    <title>Nix Package Manager Guide</title>
-    <subtitle>Version <xi:include href="version.txt" parse="text" /></subtitle>
-
-    <author>
-      <personname>
-        <firstname>Eelco</firstname>
-        <surname>Dolstra</surname>
-      </personname>
-      <contrib>Author</contrib>
-    </author>
-
-    <copyright>
-      <year>2004-2018</year>
-      <holder>Eelco Dolstra</holder>
-    </copyright>
-
-  </info>
-
-  <!--
-  <preface>
-    <title>Preface</title>
-    <para>This manual describes how to set up and use the Nix package
-    manager.</para>
-  </preface>
-  -->
-
-  <xi:include href="introduction/introduction.xml" />
-  <xi:include href="installation/installation.xml" />
-  <xi:include href="installation/upgrading.xml" />
-  <xi:include href="packages/package-management.xml" />
-  <xi:include href="expressions/writing-nix-expressions.xml" />
-  <xi:include href="advanced-topics/advanced-topics.xml" />
-  <xi:include href="command-ref/command-ref.xml" />
-  <xi:include href="glossary/glossary.xml" />
-  <xi:include href="hacking.xml" />
-  <xi:include href="release-notes/release-notes.xml" />
-
-<!--
-<appendix>
-    <title>Nix Release Notes</title>
-    <xi:include href="release-notes/release-notes.xml"
-                xpointer="xmlns(x=http://docbook.org/ns/docbook)xpointer(x:article/x:section)" />
-  </appendix>
--->
-
-</book>
diff --git a/third_party/nix/doc/manual/nix-lang-ref.xml b/third_party/nix/doc/manual/nix-lang-ref.xml
deleted file mode 100644
index 86273ac3d0..0000000000
--- a/third_party/nix/doc/manual/nix-lang-ref.xml
+++ /dev/null
@@ -1,182 +0,0 @@
-<appendix>
-  <title>Nix Language Reference</title>
-
-  <sect1>
-    <title>Grammar</title>
-
-    <productionset>
-      <title>Expressions</title>
-      
-      <production id="nix.expr">
-        <lhs>Expr</lhs>
-        <rhs>
-          <nonterminal def="#nix.expr_function" />
-        </rhs>
-      </production>
-      
-      <production id="nix.expr_function">
-        <lhs>ExprFunction</lhs>
-        <rhs>
-          '{' <nonterminal def="#nix.formals" /> '}' ':' <nonterminal def="#nix.expr_function" />
-          <sbr />|
-          <nonterminal def="#nix.expr_assert" />
-        </rhs>
-      </production>
-      
-      <production id="nix.expr_assert">
-        <lhs>ExprAssert</lhs>
-        <rhs>
-          'assert' <nonterminal def="#nix.expr" /> ';' <nonterminal def="#nix.expr_assert" />
-          <sbr />|
-          <nonterminal def="#nix.expr_if" />
-        </rhs>
-      </production>
-      
-      <production id="nix.expr_if">
-        <lhs>ExprIf</lhs>
-        <rhs>
-          'if' <nonterminal def="#nix.expr" /> 'then' <nonterminal def="#nix.expr" />
-          'else' <nonterminal def="#nix.expr" />
-          <sbr />|
-          <nonterminal def="#nix.expr_op" />
-        </rhs>
-      </production>
-      
-      <production id="nix.expr_op">
-        <lhs>ExprOp</lhs>
-        <rhs>
-          '!' <nonterminal def="#nix.expr_op" />
-          <sbr />|
-          <nonterminal def="#nix.expr_op" /> '==' <nonterminal def="#nix.expr_op" />
-          <sbr />|
-          <nonterminal def="#nix.expr_op" /> '!=' <nonterminal def="#nix.expr_op" />
-          <sbr />|
-          <nonterminal def="#nix.expr_op" /> '&amp;&amp;' <nonterminal def="#nix.expr_op" />
-          <sbr />|
-          <nonterminal def="#nix.expr_op" /> '||' <nonterminal def="#nix.expr_op" />
-          <sbr />|
-          <nonterminal def="#nix.expr_op" /> '->' <nonterminal def="#nix.expr_op" />
-          <sbr />|
-          <nonterminal def="#nix.expr_op" /> '//' <nonterminal def="#nix.expr_op" />
-          <sbr />|
-          <nonterminal def="#nix.expr_op" /> '~' <nonterminal def="#nix.expr_op" />
-          <sbr />|
-          <nonterminal def="#nix.expr_op" /> '?' <nonterminal def="#nix.id" />
-          <sbr />|
-          <nonterminal def="#nix.expr_app" />
-        </rhs>
-      </production>
-      
-      <production id="nix.expr_app">
-        <lhs>ExprApp</lhs>
-        <rhs>
-          <nonterminal def="#nix.expr_app" /> '.' <nonterminal def="#nix.expr_select" />
-          <sbr />|
-          <nonterminal def="#nix.expr_select" />
-        </rhs>
-      </production>
-      
-      <production id="nix.expr_select">
-        <lhs>ExprSelect</lhs>
-        <rhs>
-          <nonterminal def="#nix.expr_select" /> <nonterminal def="#nix.id" />
-          <sbr />|
-          <nonterminal def="#nix.expr_simple" />
-        </rhs>
-      </production>
-      
-      <production id="nix.expr_simple">
-        <lhs>ExprSimple</lhs>
-        <rhs>
-          <nonterminal def="#nix.id" /> |
-          <nonterminal def="#nix.int" /> |
-          <nonterminal def="#nix.str" /> |
-          <nonterminal def="#nix.path" /> |
-          <nonterminal def="#nix.uri" />
-          <sbr />|
-          'true' | 'false' | 'null'
-          <sbr />|
-          '(' <nonterminal def="#nix.expr" /> ')'
-          <sbr />|
-          '{' <nonterminal def="#nix.bind" />* '}'
-          <sbr />|
-          'let' '{' <nonterminal def="#nix.bind" />* '}'
-          <sbr />|
-          'rec' '{' <nonterminal def="#nix.bind" />* '}'
-          <sbr />|
-          '[' <nonterminal def="#nix.expr_select" />* ']'
-        </rhs>
-      </production>
-
-      <production id="nix.bind">
-        <lhs>Bind</lhs>
-        <rhs>
-          <nonterminal def="#nix.id" /> '=' <nonterminal def="#nix.expr" /> ';'
-          <sbr />|
-          'inherit' ('(' <nonterminal def="#nix.expr" /> ')')? <nonterminal def="#nix.id" />* ';'
-        </rhs>
-      </production>
-
-      <production id="nix.formals">
-        <lhs>Formals</lhs>
-        <rhs>
-          <nonterminal def="#nix.formal" /> ',' <nonterminal def="#nix.formals" />
-          | <nonterminal def="#nix.formal" />
-        </rhs>
-      </production>
-          
-      <production id="nix.formal">
-        <lhs>Formal</lhs>
-        <rhs>
-          <nonterminal def="#nix.id" />
-          <sbr />|
-          <nonterminal def="#nix.id" /> '?' <nonterminal def="#nix.expr" />
-        </rhs>
-      </production>
-          
-    </productionset>
-
-    <productionset>
-      <title>Terminals</title>
-
-      <production id="nix.id">
-        <lhs>Id</lhs>
-        <rhs>[a-zA-Z\_][a-zA-Z0-9\_\']*</rhs>
-      </production>
-    
-      <production id="nix.int">
-        <lhs>Int</lhs>
-        <rhs>[0-9]+</rhs>
-      </production>
-    
-      <production id="nix.str">
-        <lhs>Str</lhs>
-        <rhs>\"[^\n\"]*\"</rhs>
-      </production>
-
-      <production id="nix.path">
-        <lhs>Path</lhs>
-        <rhs>[a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+</rhs>
-      </production>
-    
-      <production id="nix.uri">
-        <lhs>Uri</lhs>
-        <rhs>[a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&amp;\=\+\$\,\-\_\.\!\~\*\']+</rhs>
-      </production>
-
-      <production id="nix.ws">
-        <lhs>Whitespace</lhs>
-        <rhs>
-          [ \t\n]+
-          <sbr />|
-          \#[^\n]*
-          <sbr />|
-          \/\*(.|\n)*\*\/
-        </rhs>
-      </production>
-
-    </productionset>
-    
-  </sect1>
-
-</appendix>
diff --git a/third_party/nix/doc/manual/packages/basic-package-mgmt.xml b/third_party/nix/doc/manual/packages/basic-package-mgmt.xml
deleted file mode 100644
index 0f21297f31..0000000000
--- a/third_party/nix/doc/manual/packages/basic-package-mgmt.xml
+++ /dev/null
@@ -1,194 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-basic-package-mgmt">
-
-<title>Basic Package Management</title>
-
-<para>The main command for package management is <link
-linkend="sec-nix-env"><command>nix-env</command></link>.  You can use
-it to install, upgrade, and erase packages, and to query what
-packages are installed or are available for installation.</para>
-
-<para>In Nix, different users can have different β€œviews”
-on the set of installed applications.  That is, there might be lots of
-applications present on the system (possibly in many different
-versions), but users can have a specific selection of those active β€”
-where β€œactive” just means that it appears in a directory
-in the user’s <envar>PATH</envar>.  Such a view on the set of
-installed applications is called a <emphasis>user
-environment</emphasis>, which is just a directory tree consisting of
-symlinks to the files of the active applications.  </para>
-
-<para>Components are installed from a set of <emphasis>Nix
-expressions</emphasis> that tell Nix how to build those packages,
-including, if necessary, their dependencies.  There is a collection of
-Nix expressions called the Nixpkgs package collection that contains
-packages ranging from basic development stuff such as GCC and Glibc,
-to end-user applications like Mozilla Firefox.  (Nix is however not
-tied to the Nixpkgs package collection; you could write your own Nix
-expressions based on Nixpkgs, or completely new ones.)</para>
-
-<para>You can manually download the latest version of Nixpkgs from
-<link xlink:href='http://nixos.org/nixpkgs/download.html'/>. However,
-it’s much more convenient to use the Nixpkgs
-<emphasis>channel</emphasis>, since it makes it easy to stay up to
-date with new versions of Nixpkgs. (Channels are described in more
-detail in <xref linkend="sec-channels"/>.) Nixpkgs is automatically
-added to your list of β€œsubscribed” channels when you install
-Nix. If this is not the case for some reason, you can add it as
-follows:
-
-<screen>
-$ nix-channel --add https://nixos.org/channels/nixpkgs-unstable
-$ nix-channel --update
-</screen>
-
-</para>
-
-<note><para>On NixOS, you’re automatically subscribed to a NixOS
-channel corresponding to your NixOS major release
-(e.g. <uri>http://nixos.org/channels/nixos-14.12</uri>). A NixOS
-channel is identical to the Nixpkgs channel, except that it contains
-only Linux binaries and is updated only if a set of regression tests
-succeed.</para></note>
-
-<para>You can view the set of available packages in Nixpkgs:
-
-<screen>
-$ nix-env -qa
-aterm-2.2
-bash-3.0
-binutils-2.15
-bison-1.875d
-blackdown-1.4.2
-bzip2-1.0.2
-…</screen>
-
-The flag <option>-q</option> specifies a query operation, and
-<option>-a</option> means that you want to show the β€œavailable” (i.e.,
-installable) packages, as opposed to the installed packages. If you
-downloaded Nixpkgs yourself, or if you checked it out from GitHub,
-then you need to pass the path to your Nixpkgs tree using the
-<option>-f</option> flag:
-
-<screen>
-$ nix-env -qaf <replaceable>/path/to/nixpkgs</replaceable>
-</screen>
-
-where <replaceable>/path/to/nixpkgs</replaceable> is where you’ve
-unpacked or checked out Nixpkgs.</para>
-
-<para>You can select specific packages by name:
-
-<screen>
-$ nix-env -qa firefox
-firefox-34.0.5
-firefox-with-plugins-34.0.5
-</screen>
-
-and using regular expressions:
-
-<screen>
-$ nix-env -qa 'firefox.*'
-</screen>
-
-</para>
-
-<para>It is also possible to see the <emphasis>status</emphasis> of
-available packages, i.e., whether they are installed into the user
-environment and/or present in the system:
-
-<screen>
-$ nix-env -qas
-…
--PS bash-3.0
---S binutils-2.15
-IPS bison-1.875d
-…</screen>
-
-The first character (<literal>I</literal>) indicates whether the
-package is installed in your current user environment.  The second
-(<literal>P</literal>) indicates whether it is present on your system
-(in which case installing it into your user environment would be a
-very quick operation).  The last one (<literal>S</literal>) indicates
-whether there is a so-called <emphasis>substitute</emphasis> for the
-package, which is Nix’s mechanism for doing binary deployment.  It
-just means that Nix knows that it can fetch a pre-built package from
-somewhere (typically a network server) instead of building it
-locally.</para>
-
-<para>You can install a package using <literal>nix-env -i</literal>.
-For instance,
-
-<screen>
-$ nix-env -i subversion</screen>
-
-will install the package called <literal>subversion</literal> (which
-is, of course, the <link
-xlink:href='http://subversion.tigris.org/'>Subversion version
-management system</link>).</para>
-
-<note><para>When you ask Nix to install a package, it will first try
-to get it in pre-compiled form from a <emphasis>binary
-cache</emphasis>. By default, Nix will use the binary cache
-<uri>https://cache.nixos.org</uri>; it contains binaries for most
-packages in Nixpkgs. Only if no binary is available in the binary
-cache, Nix will build the package from source. So if <literal>nix-env
--i subversion</literal> results in Nix building stuff from source,
-then either the package is not built for your platform by the Nixpkgs
-build servers, or your version of Nixpkgs is too old or too new. For
-instance, if you have a very recent checkout of Nixpkgs, then the
-Nixpkgs build servers may not have had a chance to build everything
-and upload the resulting binaries to
-<uri>https://cache.nixos.org</uri>. The Nixpkgs channel is only
-updated after all binaries have been uploaded to the cache, so if you
-stick to the Nixpkgs channel (rather than using a Git checkout of the
-Nixpkgs tree), you will get binaries for most packages.</para></note>
-
-<para>Naturally, packages can also be uninstalled:
-
-<screen>
-$ nix-env -e subversion</screen>
-
-</para>
-
-<para>Upgrading to a new version is just as easy.  If you have a new
-release of Nix Packages, you can do:
-
-<screen>
-$ nix-env -u subversion</screen>
-
-This will <emphasis>only</emphasis> upgrade Subversion if there is a
-β€œnewer” version in the new set of Nix expressions, as
-defined by some pretty arbitrary rules regarding ordering of version
-numbers (which generally do what you’d expect of them).  To just
-unconditionally replace Subversion with whatever version is in the Nix
-expressions, use <parameter>-i</parameter> instead of
-<parameter>-u</parameter>; <parameter>-i</parameter> will remove
-whatever version is already installed.</para>
-
-<para>You can also upgrade all packages for which there are newer
-versions:
-
-<screen>
-$ nix-env -u</screen>
-
-</para>
-
-<para>Sometimes it’s useful to be able to ask what
-<command>nix-env</command> would do, without actually doing it.  For
-instance, to find out what packages would be upgraded by
-<literal>nix-env -u</literal>, you can do
-
-<screen>
-$ nix-env -u --dry-run
-(dry run; not doing anything)
-upgrading `libxslt-1.1.0' to `libxslt-1.1.10'
-upgrading `graphviz-1.10' to `graphviz-1.12'
-upgrading `coreutils-5.0' to `coreutils-5.2.1'</screen>
-
-</para>
-
-</chapter>
diff --git a/third_party/nix/doc/manual/packages/binary-cache-substituter.xml b/third_party/nix/doc/manual/packages/binary-cache-substituter.xml
deleted file mode 100644
index c6ceb9c806..0000000000
--- a/third_party/nix/doc/manual/packages/binary-cache-substituter.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink"
-         xmlns:xi="http://www.w3.org/2001/XInclude"
-         version="5.0"
-         xml:id="ssec-binary-cache-substituter">
-
-<title>Serving a Nix store via HTTP</title>
-
-<para>You can easily share the Nix store of a machine via HTTP. This
-allows other machines to fetch store paths from that machine to speed
-up installations. It uses the same <emphasis>binary cache</emphasis>
-mechanism that Nix usually uses to fetch pre-built binaries from
-<uri>https://cache.nixos.org</uri>.</para>
-
-<para>The daemon that handles binary cache requests via HTTP,
-<command>nix-serve</command>, is not part of the Nix distribution, but
-you can install it from Nixpkgs:
-
-<screen>
-$ nix-env -i nix-serve
-</screen>
-
-You can then start the server, listening for HTTP connections on
-whatever port you like:
-
-<screen>
-$ nix-serve -p 8080
-</screen>
-
-To check whether it works, try the following on the client:
-
-<screen>
-$ curl http://avalon:8080/nix-cache-info
-</screen>
-
-which should print something like:
-
-<screen>
-StoreDir: /nix/store
-WantMassQuery: 1
-Priority: 30
-</screen>
-
-</para>
-
-<para>On the client side, you can tell Nix to use your binary cache
-using <option>--option extra-binary-caches</option>, e.g.:
-
-<screen>
-$ nix-env -i firefox --option extra-binary-caches http://avalon:8080/
-</screen>
-
-The option <option>extra-binary-caches</option> tells Nix to use this
-binary cache in addition to your default caches, such as
-<uri>https://cache.nixos.org</uri>. Thus, for any path in the closure
-of Firefox, Nix will first check if the path is available on the
-server <literal>avalon</literal> or another binary caches. If not, it
-will fall back to building from source.</para>
-
-<para>You can also tell Nix to always use your binary cache by adding
-a line to the <filename linkend="sec-conf-file">nix.conf</filename>
-configuration file like this:
-
-<programlisting>
-binary-caches = http://avalon:8080/ https://cache.nixos.org/
-</programlisting>
-
-</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/packages/channels.xml b/third_party/nix/doc/manual/packages/channels.xml
deleted file mode 100644
index 15c119fcb1..0000000000
--- a/third_party/nix/doc/manual/packages/channels.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-channels">
-
-<title>Channels</title>
-
-<para>If you want to stay up to date with a set of packages, it’s not
-very convenient to manually download the latest set of Nix expressions
-for those packages and upgrade using <command>nix-env</command>.
-Fortunately, there’s a better way: <emphasis>Nix
-channels</emphasis>.</para>
-
-<para>A Nix channel is just a URL that points to a place that contains
-a set of Nix expressions and a manifest.  Using the command <link
-linkend="sec-nix-channel"><command>nix-channel</command></link> you
-can automatically stay up to date with whatever is available at that
-URL.</para>
-
-<para>You can β€œsubscribe” to a channel using
-<command>nix-channel --add</command>, e.g.,
-
-<screen>
-$ nix-channel --add https://nixos.org/channels/nixpkgs-unstable</screen>
-
-subscribes you to a channel that always contains that latest version
-of the Nix Packages collection.  (Subscribing really just means that
-the URL is added to the file <filename>~/.nix-channels</filename>,
-where it is read by subsequent calls to <command>nix-channel
---update</command>.) You can β€œunsubscribe” using <command>nix-channel
---remove</command>:
-
-<screen>
-$ nix-channel --remove nixpkgs
-</screen>
-</para>
-
-<para>To obtain the latest Nix expressions available in a channel, do
-
-<screen>
-$ nix-channel --update</screen>
-
-This downloads and unpacks the Nix expressions in every channel
-(downloaded from <literal><replaceable>url</replaceable>/nixexprs.tar.bz2</literal>).
-It also makes the union of each channel’s Nix expressions available by
-default to <command>nix-env</command> operations (via the symlink
-<filename>~/.nix-defexpr/channels</filename>).  Consequently, you can
-then say
-
-<screen>
-$ nix-env -u</screen>
-
-to upgrade all packages in your profile to the latest versions
-available in the subscribed channels.</para>
-
-</chapter>
diff --git a/third_party/nix/doc/manual/packages/copy-closure.xml b/third_party/nix/doc/manual/packages/copy-closure.xml
deleted file mode 100644
index 012030e3eb..0000000000
--- a/third_party/nix/doc/manual/packages/copy-closure.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink"
-         xmlns:xi="http://www.w3.org/2001/XInclude"
-         version="5.0"
-         xml:id="ssec-copy-closure">
-
-<title>Copying Closures Via SSH</title>
-
-<para>The command <command
-linkend="sec-nix-copy-closure">nix-copy-closure</command> copies a Nix
-store path along with all its dependencies to or from another machine
-via the SSH protocol.  It doesn’t copy store paths that are already
-present on the target machine.  For example, the following command
-copies Firefox with all its dependencies:
-
-<screen>
-$ nix-copy-closure --to alice@itchy.example.org $(type -p firefox)</screen>
-
-See <xref linkend='sec-nix-copy-closure' /> for details.</para>
-
-<para>With <command linkend='refsec-nix-store-export'>nix-store
---export</command> and <command
-linkend='refsec-nix-store-import'>nix-store --import</command> you can
-write the closure of a store path (that is, the path and all its
-dependencies) to a file, and then unpack that file into another Nix
-store.  For example,
-
-<screen>
-$ nix-store --export $(nix-store -qR $(type -p firefox)) > firefox.closure</screen>
-
-writes the closure of Firefox to a file.  You can then copy this file
-to another machine and install the closure:
-
-<screen>
-$ nix-store --import &lt; firefox.closure</screen>
-
-Any store paths in the closure that are already present in the target
-store are ignored.  It is also possible to pipe the export into
-another command, e.g. to copy and install a closure directly to/on
-another machine:
-
-<screen>
-$ nix-store --export $(nix-store -qR $(type -p firefox)) | bzip2 | \
-    ssh alice@itchy.example.org "bunzip2 | nix-store --import"</screen>
-
-However, <command>nix-copy-closure</command> is generally more
-efficient because it only copies paths that are not already present in
-the target Nix store.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/packages/garbage-collection.xml b/third_party/nix/doc/manual/packages/garbage-collection.xml
deleted file mode 100644
index b506f22b03..0000000000
--- a/third_party/nix/doc/manual/packages/garbage-collection.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='sec-garbage-collection'>
-
-<title>Garbage Collection</title>
-
-<para><command>nix-env</command> operations such as upgrades
-(<option>-u</option>) and uninstall (<option>-e</option>) never
-actually delete packages from the system.  All they do (as shown
-above) is to create a new user environment that no longer contains
-symlinks to the β€œdeleted” packages.</para>
-
-<para>Of course, since disk space is not infinite, unused packages
-should be removed at some point.  You can do this by running the Nix
-garbage collector.  It will remove from the Nix store any package
-not used (directly or indirectly) by any generation of any
-profile.</para>
-
-<para>Note however that as long as old generations reference a
-package, it will not be deleted.  After all, we wouldn’t be able to
-do a rollback otherwise.  So in order for garbage collection to be
-effective, you should also delete (some) old generations.  Of course,
-this should only be done if you are certain that you will not need to
-roll back.</para>
-
-<para>To delete all old (non-current) generations of your current
-profile:
-
-<screen>
-$ nix-env --delete-generations old</screen>
-
-Instead of <literal>old</literal> you can also specify a list of
-generations, e.g.,
-
-<screen>
-$ nix-env --delete-generations 10 11 14</screen>
-
-To delete all generations older than a specified number of days
-(except the current generation), use the <literal>d</literal>
-suffix. For example,
-
-<screen>
-$ nix-env --delete-generations 14d</screen>
-
-deletes all generations older than two weeks.</para>
-
-<para>After removing appropriate old generations you can run the
-garbage collector as follows:
-
-<screen>
-$ nix-store --gc</screen>
-
-The behaviour of the gargage collector is affected by the 
-<literal>keep-derivations</literal> (default: true) and <literal>keep-outputs</literal>
-(default: false) options in the Nix configuration file. The defaults will ensure
-that all derivations that are build-time dependencies of garbage collector roots
-will be kept and that all output paths that are runtime dependencies
-will be kept as well. All other derivations or paths will be collected. 
-(This is usually what you want, but while you are developing
-it may make sense to keep outputs to ensure that rebuild times are quick.)
-
-If you are feeling uncertain, you can also first view what files would
-be deleted:
-
-<screen>
-$ nix-store --gc --print-dead</screen>
-
-Likewise, the option <option>--print-live</option> will show the paths
-that <emphasis>won’t</emphasis> be deleted.</para>
-
-<para>There is also a convenient little utility
-<command>nix-collect-garbage</command>, which when invoked with the
-<option>-d</option> (<option>--delete-old</option>) switch deletes all
-old generations of all profiles in
-<filename>/nix/var/nix/profiles</filename>.  So
-
-<screen>
-$ nix-collect-garbage -d</screen>
-
-is a quick and easy way to clean up your system.</para>
-
-<xi:include href="garbage-collector-roots.xml" />
-
-</chapter>
diff --git a/third_party/nix/doc/manual/packages/garbage-collector-roots.xml b/third_party/nix/doc/manual/packages/garbage-collector-roots.xml
deleted file mode 100644
index 8338e53920..0000000000
--- a/third_party/nix/doc/manual/packages/garbage-collector-roots.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-gc-roots">
-
-<title>Garbage Collector Roots</title>
-
-<para>The roots of the garbage collector are all store paths to which
-there are symlinks in the directory
-<filename><replaceable>prefix</replaceable>/nix/var/nix/gcroots</filename>.
-For instance, the following command makes the path
-<filename>/nix/store/d718ef...-foo</filename> a root of the collector:
-
-<screen>
-$ ln -s /nix/store/d718ef...-foo /nix/var/nix/gcroots/bar</screen>
-	
-That is, after this command, the garbage collector will not remove
-<filename>/nix/store/d718ef...-foo</filename> or any of its
-dependencies.</para>
-
-<para>Subdirectories of
-<filename><replaceable>prefix</replaceable>/nix/var/nix/gcroots</filename>
-are also searched for symlinks.  Symlinks to non-store paths are
-followed and searched for roots, but symlinks to non-store paths
-<emphasis>inside</emphasis> the paths reached in that way are not
-followed to prevent infinite recursion.</para>
-
-</section>
\ No newline at end of file
diff --git a/third_party/nix/doc/manual/packages/package-management.xml b/third_party/nix/doc/manual/packages/package-management.xml
deleted file mode 100644
index 61e55faeb3..0000000000
--- a/third_party/nix/doc/manual/packages/package-management.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<part xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id='chap-package-management'>
-
-<title>Package Management</title>
-
-<partintro>
-<para>This chapter discusses how to do package management with Nix,
-i.e., how to obtain, install, upgrade, and erase packages.  This is
-the β€œuser’s” perspective of the Nix system β€” people
-who want to <emphasis>create</emphasis> packages should consult
-<xref linkend='chap-writing-nix-expressions' />.</para>
-</partintro>
-
-<xi:include href="basic-package-mgmt.xml" />
-<xi:include href="profiles.xml" />
-<xi:include href="garbage-collection.xml" />
-<xi:include href="channels.xml" />
-<xi:include href="sharing-packages.xml" />
-
-</part>
diff --git a/third_party/nix/doc/manual/packages/profiles.xml b/third_party/nix/doc/manual/packages/profiles.xml
deleted file mode 100644
index 4d10319abe..0000000000
--- a/third_party/nix/doc/manual/packages/profiles.xml
+++ /dev/null
@@ -1,158 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-profiles">
-
-<title>Profiles</title>
-
-<para>Profiles and user environments are Nix’s mechanism for
-implementing the ability to allow different users to have different
-configurations, and to do atomic upgrades and rollbacks.  To
-understand how they work, it’s useful to know a bit about how Nix
-works.  In Nix, packages are stored in unique locations in the
-<emphasis>Nix store</emphasis> (typically,
-<filename>/nix/store</filename>).  For instance, a particular version
-of the Subversion package might be stored in a directory
-<filename>/nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3/</filename>,
-while another version might be stored in
-<filename>/nix/store/5mq2jcn36ldlmh93yj1n8s9c95pj7c5s-subversion-1.1.2</filename>.
-The long strings prefixed to the directory names are cryptographic
-hashes<footnote><para>160-bit truncations of SHA-256 hashes encoded in
-a base-32 notation, to be precise.</para></footnote> of
-<emphasis>all</emphasis> inputs involved in building the package β€”
-sources, dependencies, compiler flags, and so on.  So if two
-packages differ in any way, they end up in different locations in
-the file system, so they don’t interfere with each other.  <xref
-linkend='fig-user-environments' /> shows a part of a typical Nix
-store.</para>
-
-<figure xml:id='fig-user-environments'><title>User environments</title>
-  <mediaobject>
-    <imageobject>
-      <imagedata fileref='../figures/user-environments.png' format='PNG' />
-    </imageobject>
-  </mediaobject>
-</figure>
-
-<para>Of course, you wouldn’t want to type
-
-<screen>
-$ /nix/store/dpmvp969yhdq...-subversion-1.1.3/bin/svn</screen>
-
-every time you want to run Subversion.  Of course we could set up the
-<envar>PATH</envar> environment variable to include the
-<filename>bin</filename> directory of every package we want to use,
-but this is not very convenient since changing <envar>PATH</envar>
-doesn’t take effect for already existing processes.  The solution Nix
-uses is to create directory trees of symlinks to
-<emphasis>activated</emphasis> packages.  These are called
-<emphasis>user environments</emphasis> and they are packages
-themselves (though automatically generated by
-<command>nix-env</command>), so they too reside in the Nix store.  For
-instance, in <xref linkend='fig-user-environments' /> the user
-environment <filename>/nix/store/0c1p5z4kda11...-user-env</filename>
-contains a symlink to just Subversion 1.1.2 (arrows in the figure
-indicate symlinks).  This would be what we would obtain if we had done
-
-<screen>
-$ nix-env -i subversion</screen>
-
-on a set of Nix expressions that contained Subversion 1.1.2.</para>
-
-<para>This doesn’t in itself solve the problem, of course; you
-wouldn’t want to type
-<filename>/nix/store/0c1p5z4kda11...-user-env/bin/svn</filename>
-either.  That’s why there are symlinks outside of the store that point
-to the user environments in the store; for instance, the symlinks
-<filename>default-42-link</filename> and
-<filename>default-43-link</filename> in the example.  These are called
-<emphasis>generations</emphasis> since every time you perform a
-<command>nix-env</command> operation, a new user environment is
-generated based on the current one.  For instance, generation 43 was
-created from generation 42 when we did
-
-<screen>
-$ nix-env -i subversion firefox</screen>
-
-on a set of Nix expressions that contained Firefox and a new version
-of Subversion.</para>
-
-<para>Generations are grouped together into
-<emphasis>profiles</emphasis> so that different users don’t interfere
-with each other if they don’t want to.  For example:
-
-<screen>
-$ ls -l /nix/var/nix/profiles/
-...
-lrwxrwxrwx  1 eelco ... default-42-link -> /nix/store/0c1p5z4kda11...-user-env
-lrwxrwxrwx  1 eelco ... default-43-link -> /nix/store/3aw2pdyx2jfc...-user-env
-lrwxrwxrwx  1 eelco ... default -> default-43-link</screen>
-
-This shows a profile called <filename>default</filename>.  The file
-<filename>default</filename> itself is actually a symlink that points
-to the current generation.  When we do a <command>nix-env</command>
-operation, a new user environment and generation link are created
-based on the current one, and finally the <filename>default</filename>
-symlink is made to point at the new generation.  This last step is
-atomic on Unix, which explains how we can do atomic upgrades.  (Note
-that the building/installing of new packages doesn’t interfere in
-any way with old packages, since they are stored in different
-locations in the Nix store.)</para>
-
-<para>If you find that you want to undo a <command>nix-env</command>
-operation, you can just do
-
-<screen>
-$ nix-env --rollback</screen>
-
-which will just make the current generation link point at the previous
-link.  E.g., <filename>default</filename> would be made to point at
-<filename>default-42-link</filename>.  You can also switch to a
-specific generation:
-
-<screen>
-$ nix-env --switch-generation 43</screen>
-
-which in this example would roll forward to generation 43 again.  You
-can also see all available generations:
-
-<screen>
-$ nix-env --list-generations</screen></para>
-
-<para>You generally wouldn’t have
-<filename>/nix/var/nix/profiles/<replaceable>some-profile</replaceable>/bin</filename>
-in your <envar>PATH</envar>.  Rather, there is a symlink
-<filename>~/.nix-profile</filename> that points to your current
-profile.  This means that you should put
-<filename>~/.nix-profile/bin</filename> in your <envar>PATH</envar>
-(and indeed, that’s what the initialisation script
-<filename>/nix/etc/profile.d/nix.sh</filename> does).  This makes it
-easier to switch to a different profile.  You can do that using the
-command <command>nix-env --switch-profile</command>:
-
-<screen>
-$ nix-env --switch-profile /nix/var/nix/profiles/my-profile
-
-$ nix-env --switch-profile /nix/var/nix/profiles/default</screen>
-
-These commands switch to the <filename>my-profile</filename> and
-default profile, respectively.  If the profile doesn’t exist, it will
-be created automatically.  You should be careful about storing a
-profile in another location than the <filename>profiles</filename>
-directory, since otherwise it might not be used as a root of the
-garbage collector (see <xref linkend='sec-garbage-collection'
-/>).</para>
-
-<para>All <command>nix-env</command> operations work on the profile
-pointed to by <command>~/.nix-profile</command>, but you can override
-this using the <option>--profile</option> option (abbreviation
-<option>-p</option>):
-
-<screen>
-$ nix-env -p /nix/var/nix/profiles/other-profile -i subversion</screen>
-
-This will <emphasis>not</emphasis> change the
-<command>~/.nix-profile</command> symlink.</para>
-
-</chapter>
diff --git a/third_party/nix/doc/manual/packages/s3-substituter.xml b/third_party/nix/doc/manual/packages/s3-substituter.xml
deleted file mode 100644
index 868b5a66dc..0000000000
--- a/third_party/nix/doc/manual/packages/s3-substituter.xml
+++ /dev/null
@@ -1,182 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<section xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink"
-         xmlns:xi="http://www.w3.org/2001/XInclude"
-         version="5.0"
-         xml:id="ssec-s3-substituter">
-
-<title>Serving a Nix store via AWS S3 or S3-compatible Service</title>
-
-<para>Nix has built-in support for storing and fetching store paths
-from Amazon S3 and S3 compatible services. This uses the same
-<emphasis>binary</emphasis> cache mechanism that Nix usually uses to
-fetch prebuilt binaries from <uri>cache.nixos.org</uri>.</para>
-
-<para>The following options can be specified as URL parameters to
-the S3 URL:</para>
-
-<variablelist>
-  <varlistentry><term><literal>profile</literal></term>
-  <listitem>
-    <para>
-      The name of the AWS configuration profile to use. By default
-      Nix will use the <literal>default</literal> profile.
-    </para>
-  </listitem>
-  </varlistentry>
-
-  <varlistentry><term><literal>region</literal></term>
-  <listitem>
-    <para>
-      The region of the S3 bucket. <literal>us–east-1</literal> by
-      default.
-    </para>
-
-    <para>
-      If your bucket is not in <literal>us–east-1</literal>, you
-      should always explicitly specify the region parameter.
-    </para>
-  </listitem>
-  </varlistentry>
-
-  <varlistentry><term><literal>endpoint</literal></term>
-  <listitem>
-    <para>
-      The URL to your S3-compatible service, for when not using
-      Amazon S3. Do not specify this value if you're using Amazon
-      S3.
-    </para>
-    <note><para>This endpoint must support HTTPS and will use
-    path-based addressing instead of virtual host based
-    addressing.</para></note>
-  </listitem>
-  </varlistentry>
-
-  <varlistentry><term><literal>scheme</literal></term>
-  <listitem>
-    <para>
-      The scheme used for S3 requests, <literal>https</literal>
-      (default) or <literal>http</literal>.  This option allows you to
-      disable HTTPS for binary caches which don't support it.
-    </para>
-    <note><para>HTTPS should be used if the cache might contain
-    sensitive information.</para></note>
-  </listitem>
-  </varlistentry>
-</variablelist>
-
-<para>In this example we will use the bucket named
-<literal>example-nix-cache</literal>.</para>
-
-<section xml:id="ssec-s3-substituter-anonymous-reads">
-  <title>Anonymous Reads to your S3-compatible binary cache</title>
-
-  <para>If your binary cache is publicly accessible and does not
-  require authentication, the simplest and easiest way to use Nix with
-  your S3 compatible binary cache is to use the HTTP URL for that
-  cache.</para>
-
-  <para>For AWS S3 the binary cache URL for example bucket will be
-  exactly <uri>https://example-nix-cache.s3.amazonaws.com</uri> or
-  <uri>s3://example-nix-cache</uri>. For S3 compatible binary caches,
-  consult that cache's documentation.</para>
-
-  <para>Your bucket will need the following bucket policy:</para>
-
-  <programlisting><![CDATA[
-{
-    "Id": "DirectReads",
-    "Version": "2012-10-17",
-    "Statement": [
-        {
-            "Sid": "AllowDirectReads",
-            "Action": [
-                "s3:GetObject",
-                "s3:GetBucketLocation"
-            ],
-            "Effect": "Allow",
-            "Resource": [
-                "arn:aws:s3:::example-nix-cache",
-                "arn:aws:s3:::example-nix-cache/*"
-            ],
-            "Principal": "*"
-        }
-    ]
-}
-]]></programlisting>
-</section>
-
-<section xml:id="ssec-s3-substituter-authenticated-reads">
-  <title>Authenticated Reads to your S3 binary cache</title>
-
-  <para>For AWS S3 the binary cache URL for example bucket will be
-  exactly <uri>s3://example-nix-cache</uri>.</para>
-
-  <para>Nix will use the <link
-  xlink:href="https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/credentials.html">default
-  credential provider chain</link> for authenticating requests to
-  Amazon S3.</para>
-
-  <para>Nix supports authenticated reads from Amazon S3 and S3
-  compatible binary caches.</para>
-
-  <para>Your bucket will need a bucket policy allowing the desired
-  users to perform the <literal>s3:GetObject</literal> and
-  <literal>s3:GetBucketLocation</literal> action on all objects in the
-  bucket. The anonymous policy in <xref
-  linkend="ssec-s3-substituter-anonymous-reads" /> can be updated to
-  have a restricted <literal>Principal</literal> to support
-  this.</para>
-</section>
-
-
-<section xml:id="ssec-s3-substituter-authenticated-writes">
-  <title>Authenticated Writes to your S3-compatible binary cache</title>
-
-  <para>Nix support fully supports writing to Amazon S3 and S3
-  compatible buckets. The binary cache URL for our example bucket will
-  be <uri>s3://example-nix-cache</uri>.</para>
-
-  <para>Nix will use the <link
-  xlink:href="https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/credentials.html">default
-  credential provider chain</link> for authenticating requests to
-  Amazon S3.</para>
-
-  <para>Your account will need the following IAM policy to
-  upload to the cache:</para>
-
-  <programlisting><![CDATA[
-{
-  "Version": "2012-10-17",
-  "Statement": [
-    {
-      "Sid": "UploadToCache",
-      "Effect": "Allow",
-      "Action": [
-        "s3:AbortMultipartUpload",
-        "s3:GetBucketLocation",
-        "s3:GetObject",
-        "s3:ListBucket",
-        "s3:ListBucketMultipartUploads",
-        "s3:ListMultipartUploadParts",
-        "s3:PutObject"
-      ],
-      "Resource": [
-        "arn:aws:s3:::example-nix-cache",
-        "arn:aws:s3:::example-nix-cache/*"
-      ]
-    }
-  ]
-}
-]]></programlisting>
-
-
-  <example><title>Uploading with a specific credential profile for Amazon S3</title>
-    <para><command>nix copy --to 's3://example-nix-cache?profile=cache-upload&amp;region=eu-west-2' nixpkgs.hello</command></para>
-  </example>
-
-  <example><title>Uploading to an S3-Compatible Binary Cache</title>
-    <para><command>nix copy --to 's3://example-nix-cache?profile=cache-upload&amp;scheme=https&amp;endpoint=minio.example.com' nixpkgs.hello</command></para>
-  </example>
-</section>
-</section>
diff --git a/third_party/nix/doc/manual/packages/sharing-packages.xml b/third_party/nix/doc/manual/packages/sharing-packages.xml
deleted file mode 100644
index bb6c52b8f8..0000000000
--- a/third_party/nix/doc/manual/packages/sharing-packages.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-sharing-packages">
-
-<title>Sharing Packages Between Machines</title>
-
-<para>Sometimes you want to copy a package from one machine to
-another.  Or, you want to install some packages and you know that
-another machine already has some or all of those packages or their
-dependencies.  In that case there are mechanisms to quickly copy
-packages between machines.</para>
-
-<xi:include href="binary-cache-substituter.xml" />
-<xi:include href="copy-closure.xml" />
-<xi:include href="ssh-substituter.xml" />
-<xi:include href="s3-substituter.xml" />
-
-</chapter>
diff --git a/third_party/nix/doc/manual/packages/ssh-substituter.xml b/third_party/nix/doc/manual/packages/ssh-substituter.xml
deleted file mode 100644
index 8db3f96625..0000000000
--- a/third_party/nix/doc/manual/packages/ssh-substituter.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink"
-         xmlns:xi="http://www.w3.org/2001/XInclude"
-         version="5.0"
-         xml:id="ssec-ssh-substituter">
-
-<title>Serving a Nix store via SSH</title>
-
-<para>You can tell Nix to automatically fetch needed binaries from a
-remote Nix store via SSH. For example, the following installs Firefox,
-automatically fetching any store paths in Firefox’s closure if they
-are available on the server <literal>avalon</literal>:
-
-<screen>
-$ nix-env -i firefox --substituters ssh://alice@avalon
-</screen>
-
-This works similar to the binary cache substituter that Nix usually
-uses, only using SSH instead of HTTP: if a store path
-<literal>P</literal> is needed, Nix will first check if it’s available
-in the Nix store on <literal>avalon</literal>. If not, it will fall
-back to using the binary cache substituter, and then to building from
-source.</para>
-
-<note><para>The SSH substituter currently does not allow you to enter
-an SSH passphrase interactively. Therefore, you should use
-<command>ssh-add</command> to load the decrypted private key into
-<command>ssh-agent</command>.</para></note>
-
-<para>You can also copy the closure of some store path, without
-installing it into your profile, e.g.
-
-<screen>
-$ nix-store -r /nix/store/m85bxg…-firefox-34.0.5 --substituters ssh://alice@avalon
-</screen>
-
-This is essentially equivalent to doing
-
-<screen>
-$ nix-copy-closure --from alice@avalon /nix/store/m85bxg…-firefox-34.0.5
-</screen>
-
-</para>
-
-<para>You can use SSH’s <emphasis>forced command</emphasis> feature to
-set up a restricted user account for SSH substituter access, allowing
-read-only access to the local Nix store, but nothing more. For
-example, add the following lines to <filename>sshd_config</filename>
-to restrict the user <literal>nix-ssh</literal>:
-
-<programlisting>
-Match User nix-ssh
-  AllowAgentForwarding no
-  AllowTcpForwarding no
-  PermitTTY no
-  PermitTunnel no
-  X11Forwarding no
-  ForceCommand nix-store --serve
-Match All
-</programlisting>
-
-On NixOS, you can accomplish the same by adding the following to your
-<filename>configuration.nix</filename>:
-
-<programlisting>
-nix.sshServe.enable = true;
-nix.sshServe.keys = [ "ssh-dss AAAAB3NzaC1k... bob@example.org" ];
-</programlisting>
-
-where the latter line lists the public keys of users that are allowed
-to connect.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/quote-literals.xsl b/third_party/nix/doc/manual/quote-literals.xsl
deleted file mode 100644
index 5002643dbd..0000000000
--- a/third_party/nix/doc/manual/quote-literals.xsl
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0"?>
-
-<xsl:stylesheet
-  version="1.0"
-  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-  xmlns:str="http://exslt.org/strings"
-  extension-element-prefixes="str">
-
-  <xsl:output method="xml"/>
-
-  <xsl:template match="function|command|literal|varname|filename|option|quote">`<xsl:apply-templates/>'</xsl:template>
-
-  <xsl:template match="token"><xsl:text>    </xsl:text><xsl:apply-templates /><xsl:text>
-</xsl:text></xsl:template>
-
-  <xsl:template match="screen|programlisting">
-    <screen><xsl:apply-templates select="str:split(., '&#xA;')" /></screen>
-  </xsl:template>
-
-  <xsl:template match="section[following::section]">
-    <section>
-      <xsl:apply-templates />
-      <screen><xsl:text>
-      </xsl:text></screen>
-    </section>
-  </xsl:template>
-
-  <xsl:template match="*">
-    <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
-      <xsl:copy-of select="namespace::*" />
-      <xsl:for-each select="@*">
-	<xsl:attribute name="{name(.)}" namespace="{namespace-uri(.)}">
-	  <xsl:value-of select="."/>
-	</xsl:attribute>
-      </xsl:for-each>
-      <xsl:apply-templates/>
-    </xsl:element>
-  </xsl:template>
-
-</xsl:stylesheet>
diff --git a/third_party/nix/doc/manual/release-notes/release-notes.xml b/third_party/nix/doc/manual/release-notes/release-notes.xml
deleted file mode 100644
index 2655d68e35..0000000000
--- a/third_party/nix/doc/manual/release-notes/release-notes.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<appendix xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="sec-relnotes">
-
-<title>Nix Release Notes</title>
-
-<!--
-<partintro>
-<para>This section lists the release notes for each stable version of Nix.</para>
-</partintro>
--->
-
-<xi:include href="rl-2.3.xml" />
-<xi:include href="rl-2.2.xml" />
-<xi:include href="rl-2.1.xml" />
-<xi:include href="rl-2.0.xml" />
-<xi:include href="rl-1.11.10.xml" />
-<xi:include href="rl-1.11.xml" />
-<xi:include href="rl-1.10.xml" />
-<xi:include href="rl-1.9.xml" />
-<xi:include href="rl-1.8.xml" />
-<xi:include href="rl-1.7.xml" />
-<xi:include href="rl-1.6.1.xml" />
-<xi:include href="rl-1.6.xml" />
-<xi:include href="rl-1.5.2.xml" />
-<xi:include href="rl-1.5.xml" />
-<xi:include href="rl-1.4.xml" />
-<xi:include href="rl-1.3.xml" />
-<xi:include href="rl-1.2.xml" />
-<xi:include href="rl-1.1.xml" />
-<xi:include href="rl-1.0.xml" />
-<xi:include href="rl-0.16.xml" />
-<xi:include href="rl-0.15.xml" />
-<xi:include href="rl-0.14.xml" />
-<xi:include href="rl-0.13.xml" />
-<xi:include href="rl-0.12.xml" />
-<xi:include href="rl-0.11.xml" />
-<xi:include href="rl-0.10.1.xml" />
-<xi:include href="rl-0.10.xml" />
-<xi:include href="rl-0.9.2.xml" />
-<xi:include href="rl-0.9.1.xml" />
-<xi:include href="rl-0.9.xml" />
-<xi:include href="rl-0.8.1.xml" />
-<xi:include href="rl-0.8.xml" />
-<xi:include href="rl-0.7.xml" />
-<xi:include href="rl-0.6.xml" />
-<xi:include href="rl-0.5.xml" />
-
-</appendix>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.10.1.xml b/third_party/nix/doc/manual/release-notes/rl-0.10.1.xml
deleted file mode 100644
index 95829323d4..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.10.1.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.10.1">
-
-<title>Release 0.10.1 (2006-10-11)</title>
-
-<para>This release fixes two somewhat obscure bugs that occur when
-evaluating Nix expressions that are stored inside the Nix store
-(<literal>NIX-67</literal>).  These do not affect most users.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.10.xml b/third_party/nix/doc/manual/release-notes/rl-0.10.xml
deleted file mode 100644
index 9afec4de94..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.10.xml
+++ /dev/null
@@ -1,323 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.10">
-
-<title>Release 0.10 (2006-10-06)</title>
-
-<note><para>This version of Nix uses Berkeley DB 4.4 instead of 4.3.
-The database is upgraded automatically, but you should be careful not
-to use old versions of Nix that still use Berkeley DB 4.3.  In
-particular, if you use a Nix installed through Nix, you should run
-
-<screen>
-$ nix-store --clear-substitutes</screen>
-
-first.</para></note>
-
-<warning><para>Also, the database schema has changed slighted to fix a
-performance issue (see below).  When you run any Nix 0.10 command for
-the first time, the database will be upgraded automatically.  This is
-irreversible.</para></warning>
-
-<itemizedlist>
-
-
-  <!-- Usability / features -->
-
-
-  <listitem><para><command>nix-env</command> usability improvements:
-
-    <itemizedlist>
-
-      <listitem><para>An option <option>--compare-versions</option>
-      (or <option>-c</option>) has been added to <command>nix-env
-      --query</command> to allow you to compare installed versions of
-      packages to available versions, or vice versa.  An easy way to
-      see if you are up to date with what’s in your subscribed
-      channels is <literal>nix-env -qc \*</literal>.</para></listitem>
-
-      <listitem><para><literal>nix-env --query</literal> now takes as
-      arguments a list of package names about which to show
-      information, just like <option>--install</option>, etc.: for
-      example, <literal>nix-env -q gcc</literal>.  Note that to show
-      all derivations, you need to specify
-      <literal>\*</literal>.</para></listitem>
-
-      <listitem><para><literal>nix-env -i
-      <replaceable>pkgname</replaceable></literal> will now install
-      the highest available version of
-      <replaceable>pkgname</replaceable>, rather than installing all
-      available versions (which would probably give collisions)
-      (<literal>NIX-31</literal>).</para></listitem>
-
-      <listitem><para><literal>nix-env (-i|-u) --dry-run</literal> now
-      shows exactly which missing paths will be built or
-      substituted.</para></listitem>
-
-      <listitem><para><literal>nix-env -qa --description</literal>
-      shows human-readable descriptions of packages, provided that
-      they have a <literal>meta.description</literal> attribute (which
-      most packages in Nixpkgs don’t have yet).</para></listitem>
-
-    </itemizedlist>
-
-  </para></listitem>
-
-
-  <listitem><para>New language features:
-
-    <itemizedlist>
-
-      <listitem><para>Reference scanning (which happens after each
-      build) is much faster and takes a constant amount of
-      memory.</para></listitem>
-
-      <listitem><para>String interpolation.  Expressions like
-
-<programlisting>
-"--with-freetype2-library=" + freetype + "/lib"</programlisting>
-
-      can now be written as
-
-<programlisting>
-"--with-freetype2-library=${freetype}/lib"</programlisting>
-
-      You can write arbitrary expressions within
-      <literal>${<replaceable>...</replaceable>}</literal>, not just
-      identifiers.</para></listitem>
-
-      <listitem><para>Multi-line string literals.</para></listitem>
-
-      <listitem><para>String concatenations can now involve
-      derivations, as in the example <code>"--with-freetype2-library="
-      + freetype + "/lib"</code>.  This was not previously possible
-      because we need to register that a derivation that uses such a
-      string is dependent on <literal>freetype</literal>.  The
-      evaluator now properly propagates this information.
-      Consequently, the subpath operator (<literal>~</literal>) has
-      been deprecated.</para></listitem>
-
-      <listitem><para>Default values of function arguments can now
-      refer to other function arguments; that is, all arguments are in
-      scope in the default values
-      (<literal>NIX-45</literal>).</para></listitem>
-
-      <!--
-      <listitem><para>TODO: domain checks (r5895).</para></listitem>
-      -->
-
-      <listitem><para>Lots of new built-in primitives, such as
-      functions for list manipulation and integer arithmetic.  See the
-      manual for a complete list.  All primops are now available in
-      the set <varname>builtins</varname>, allowing one to test for
-      the availability of primop in a backwards-compatible
-      way.</para></listitem>
-
-      <listitem><para>Real let-expressions: <literal>let x = ...;
-      ... z = ...; in ...</literal>.</para></listitem>
-
-    </itemizedlist>
-
-  </para></listitem>
-
-
-  <listitem><para>New commands <command>nix-pack-closure</command> and
-  <command>nix-unpack-closure</command> than can be used to easily
-  transfer a store path with all its dependencies to another machine.
-  Very convenient whenever you have some package on your machine and
-  you want to copy it somewhere else.</para></listitem>
-
-
-  <listitem><para>XML support:
-
-    <itemizedlist>
-
-      <listitem><para><literal>nix-env -q --xml</literal> prints the
-      installed or available packages in an XML representation for
-      easy processing by other tools.</para></listitem>
-
-      <listitem><para><literal>nix-instantiate --eval-only
-      --xml</literal> prints an XML representation of the resulting
-      term.  (The new flag <option>--strict</option> forces β€˜deep’
-      evaluation of the result, i.e., list elements and attributes are
-      evaluated recursively.)</para></listitem>
-
-      <listitem><para>In Nix expressions, the primop
-      <function>builtins.toXML</function> converts a term to an XML
-      representation.  This is primarily useful for passing structured
-      information to builders.</para></listitem>
-
-    </itemizedlist>
-
-  </para></listitem>
-
-
-  <listitem><para>You can now unambiguously specify which derivation to
-  build or install in <command>nix-env</command>,
-  <command>nix-instantiate</command> and <command>nix-build</command>
-  using the <option>--attr</option> / <option>-A</option> flags, which
-  takes an attribute name as argument.  (Unlike symbolic package names
-  such as <literal>subversion-1.4.0</literal>, attribute names in an
-  attribute set are unique.)  For instance, a quick way to perform a
-  test build of a package in Nixpkgs is <literal>nix-build
-  pkgs/top-level/all-packages.nix -A
-  <replaceable>foo</replaceable></literal>.  <literal>nix-env -q
-  --attr</literal> shows the attribute names corresponding to each
-  derivation.</para></listitem>
-
-
-  <listitem><para>If the top-level Nix expression used by
-  <command>nix-env</command>, <command>nix-instantiate</command> or
-  <command>nix-build</command> evaluates to a function whose arguments
-  all have default values, the function will be called automatically.
-  Also, the new command-line switch <option>--arg
-  <replaceable>name</replaceable>
-  <replaceable>value</replaceable></option> can be used to specify
-  function arguments on the command line.</para></listitem>
-
-
-  <listitem><para><literal>nix-install-package --url
-  <replaceable>URL</replaceable></literal> allows a package to be
-  installed directly from the given URL.</para></listitem>
-
-
-  <listitem><para>Nix now works behind an HTTP proxy server; just set
-  the standard environment variables <envar>http_proxy</envar>,
-  <envar>https_proxy</envar>, <envar>ftp_proxy</envar> or
-  <envar>all_proxy</envar> appropriately.  Functions such as
-  <function>fetchurl</function> in Nixpkgs also respect these
-  variables.</para></listitem>
-
-
-  <listitem><para><literal>nix-build -o
-  <replaceable>symlink</replaceable></literal> allows the symlink to
-  the build result to be named something other than
-  <literal>result</literal>.</para></listitem>
-
-
-  <!-- Stability / performance / etc. -->
-
-
-  <listitem><para>Platform support:
-
-    <itemizedlist>
-
-      <listitem><para>Support for 64-bit platforms, provided a <link
-      xlink:href="http://bugzilla.sen.cwi.nl:8080/show_bug.cgi?id=606">suitably
-      patched ATerm library</link> is used.  Also, files larger than 2
-      GiB are now supported.</para></listitem>
-
-      <listitem><para>Added support for Cygwin (Windows,
-      <literal>i686-cygwin</literal>), Mac OS X on Intel
-      (<literal>i686-darwin</literal>) and Linux on PowerPC
-      (<literal>powerpc-linux</literal>).</para></listitem>
-
-      <listitem><para>Users of SMP and multicore machines will
-      appreciate that the number of builds to be performed in parallel
-      can now be specified in the configuration file in the
-      <literal>build-max-jobs</literal> setting.</para></listitem>
-
-    </itemizedlist>
-
-  </para></listitem>
-
-
-  <listitem><para>Garbage collector improvements:
-
-    <itemizedlist>
-
-      <listitem><para>Open files (such as running programs) are now
-      used as roots of the garbage collector.  This prevents programs
-      that have been uninstalled from being garbage collected while
-      they are still running.  The script that detects these
-      additional runtime roots
-      (<filename>find-runtime-roots.pl</filename>) is inherently
-      system-specific, but it should work on Linux and on all
-      platforms that have the <command>lsof</command>
-      utility.</para></listitem>
-
-      <listitem><para><literal>nix-store --gc</literal>
-      (a.k.a. <command>nix-collect-garbage</command>) prints out the
-      number of bytes freed on standard output.  <literal>nix-store
-      --gc --print-dead</literal> shows how many bytes would be freed
-      by an actual garbage collection.</para></listitem>
-
-      <listitem><para><literal>nix-collect-garbage -d</literal>
-      removes all old generations of <emphasis>all</emphasis> profiles
-      before calling the actual garbage collector (<literal>nix-store
-      --gc</literal>).  This is an easy way to get rid of all old
-      packages in the Nix store.</para></listitem>
-
-      <listitem><para><command>nix-store</command> now has an
-      operation <option>--delete</option> to delete specific paths
-      from the Nix store.  It won’t delete reachable (non-garbage)
-      paths unless <option>--ignore-liveness</option> is
-      specified.</para></listitem>
-
-    </itemizedlist>
-
-  </para></listitem>
-
-
-  <listitem><para>Berkeley DB 4.4’s process registry feature is used
-  to recover from crashed Nix processes.</para></listitem>
-
-  <!--  <listitem><para>TODO: shared stores.</para></listitem> -->
-
-  <listitem><para>A performance issue has been fixed with the
-  <literal>referer</literal> table, which stores the inverse of the
-  <literal>references</literal> table (i.e., it tells you what store
-  paths refer to a given path).  Maintaining this table could take a
-  quadratic amount of time, as well as a quadratic amount of Berkeley
-  DB log file space (in particular when running the garbage collector)
-  (<literal>NIX-23</literal>).</para></listitem>
-
-  <listitem><para>Nix now catches the <literal>TERM</literal> and
-  <literal>HUP</literal> signals in addition to the
-  <literal>INT</literal> signal.  So you can now do a <literal>killall
-  nix-store</literal> without triggering a database
-  recovery.</para></listitem>
-
-  <listitem><para><command>bsdiff</command> updated to version
-  4.3.</para></listitem>
-
-  <listitem><para>Substantial performance improvements in expression
-  evaluation and <literal>nix-env -qa</literal>, all thanks to <link
-  xlink:href="http://valgrind.org/">Valgrind</link>.  Memory use has
-  been reduced by a factor 8 or so.  Big speedup by memoisation of
-  path hashing.</para></listitem>
-
-  <listitem><para>Lots of bug fixes, notably:
-
-    <itemizedlist>
-
-      <listitem><para>Make sure that the garbage collector can run
-      successfully when the disk is full
-      (<literal>NIX-18</literal>).</para></listitem>
-
-      <listitem><para><command>nix-env</command> now locks the profile
-      to prevent races between concurrent <command>nix-env</command>
-      operations on the same profile
-      (<literal>NIX-7</literal>).</para></listitem>
-
-      <listitem><para>Removed misleading messages from
-      <literal>nix-env -i</literal> (e.g., <literal>installing
-      `foo'</literal> followed by <literal>uninstalling
-      `foo'</literal>) (<literal>NIX-17</literal>).</para></listitem>
-
-    </itemizedlist>
-
-  </para></listitem>
-
-  <listitem><para>Nix source distributions are a lot smaller now since
-  we no longer include a full copy of the Berkeley DB source
-  distribution (but only the bits we need).</para></listitem>
-
-  <listitem><para>Header files are now installed so that external
-  programs can use the Nix libraries.</para></listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.11.xml b/third_party/nix/doc/manual/release-notes/rl-0.11.xml
deleted file mode 100644
index 7ad0ab5b71..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.11.xml
+++ /dev/null
@@ -1,261 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-0.11">
-
-<title>Release 0.11 (2007-12-31)</title>
-
-<para>Nix 0.11 has many improvements over the previous stable release.
-The most important improvement is secure multi-user support.  It also
-features many usability enhancements and language extensions, many of
-them prompted by NixOS, the purely functional Linux distribution based
-on Nix.  Here is an (incomplete) list:</para>
-
-
-<itemizedlist>
-
-
-  <listitem><para>Secure multi-user support.  A single Nix store can
-  now be shared between multiple (possible untrusted) users.  This is
-  an important feature for NixOS, where it allows non-root users to
-  install software.  The old setuid method for sharing a store between
-  multiple users has been removed.  Details for setting up a
-  multi-user store can be found in the manual.</para></listitem>
-
-
-  <listitem><para>The new command <command>nix-copy-closure</command>
-  gives you an easy and efficient way to exchange software between
-  machines.  It copies the missing parts of the closure of a set of
-  store path to or from a remote machine via
-  <command>ssh</command>.</para></listitem>
-
-
-  <listitem><para>A new kind of string literal: strings between double
-  single-quotes (<literal>''</literal>) have indentation
-  β€œintelligently” removed.  This allows large strings (such as shell
-  scripts or configuration file fragments in NixOS) to cleanly follow
-  the indentation of the surrounding expression.  It also requires
-  much less escaping, since <literal>''</literal> is less common in
-  most languages than <literal>"</literal>.</para></listitem>
-
-
-  <listitem><para><command>nix-env</command> <option>--set</option>
-  modifies the current generation of a profile so that it contains
-  exactly the specified derivation, and nothing else.  For example,
-  <literal>nix-env -p /nix/var/nix/profiles/browser --set
-  firefox</literal> lets the profile named
-  <filename>browser</filename> contain just Firefox.</para></listitem>
-
-
-  <listitem><para><command>nix-env</command> now maintains
-  meta-information about installed packages in profiles.  The
-  meta-information is the contents of the <varname>meta</varname>
-  attribute of derivations, such as <varname>description</varname> or
-  <varname>homepage</varname>.  The command <literal>nix-env -q --xml
-  --meta</literal> shows all meta-information.</para></listitem>
-
-
-  <listitem><para><command>nix-env</command> now uses the
-  <varname>meta.priority</varname> attribute of derivations to resolve
-  filename collisions between packages.  Lower priority values denote
-  a higher priority.  For instance, the GCC wrapper package and the
-  Binutils package in Nixpkgs both have a file
-  <filename>bin/ld</filename>, so previously if you tried to install
-  both you would get a collision.  Now, on the other hand, the GCC
-  wrapper declares a higher priority than Binutils, so the former’s
-  <filename>bin/ld</filename> is symlinked in the user
-  environment.</para></listitem>
-
-
-  <listitem><para><command>nix-env -i / -u</command>: instead of
-  breaking package ties by version, break them by priority and version
-  number.  That is, if there are multiple packages with the same name,
-  then pick the package with the highest priority, and only use the
-  version if there are multiple packages with the same
-  priority.</para>
-
-  <para>This makes it possible to mark specific versions/variant in
-  Nixpkgs more or less desirable than others.  A typical example would
-  be a beta version of some package (e.g.,
-  <literal>gcc-4.2.0rc1</literal>) which should not be installed even
-  though it is the highest version, except when it is explicitly
-  selected (e.g., <literal>nix-env -i
-  gcc-4.2.0rc1</literal>).</para></listitem>
-
-
-  <listitem><para><command>nix-env --set-flag</command> allows meta
-  attributes of installed packages to be modified.  There are several
-  attributes that can be usefully modified, because they affect the
-  behaviour of <command>nix-env</command> or the user environment
-  build script:
-
-    <itemizedlist>
-
-      <listitem><para><varname>meta.priority</varname> can be changed
-      to resolve filename clashes (see above).</para></listitem>
-
-      <listitem><para><varname>meta.keep</varname> can be set to
-      <literal>true</literal> to prevent the package from being
-      upgraded or replaced.  Useful if you want to hang on to an older
-      version of a package.</para></listitem>
-
-      <listitem><para><varname>meta.active</varname> can be set to
-      <literal>false</literal> to β€œdisable” the package.  That is, no
-      symlinks will be generated to the files of the package, but it
-      remains part of the profile (so it won’t be garbage-collected).
-      Set it back to <literal>true</literal> to re-enable the
-      package.</para></listitem>
-
-    </itemizedlist>
-
-  </para></listitem>
-
-
-  <listitem><para><command>nix-env -q</command> now has a flag
-  <option>--prebuilt-only</option> (<option>-b</option>) that causes
-  <command>nix-env</command> to show only those derivations whose
-  output is already in the Nix store or that can be substituted (i.e.,
-  downloaded from somewhere).  In other words, it shows the packages
-  that can be installed β€œquickly”, i.e., don’t need to be built from
-  source.  The <option>-b</option> flag is also available in
-  <command>nix-env -i</command> and <command>nix-env -u</command> to
-  filter out derivations for which no pre-built binary is
-  available.</para></listitem>
-
-
-  <listitem><para>The new option <option>--argstr</option> (in
-  <command>nix-env</command>, <command>nix-instantiate</command> and
-  <command>nix-build</command>) is like <option>--arg</option>, except
-  that the value is a string.  For example, <literal>--argstr system
-  i686-linux</literal> is equivalent to <literal>--arg system
-  \"i686-linux\"</literal> (note that <option>--argstr</option>
-  prevents annoying quoting around shell arguments).</para></listitem>
-
-
-  <listitem><para><command>nix-store</command> has a new operation
-  <option>--read-log</option> (<option>-l</option>)
-  <parameter>paths</parameter> that shows the build log of the given
-  paths.</para></listitem>
-
-
-  <!--
-  <listitem><para>TODO: semantic cleanups of string concatenation
-  etc. (mostly in r6740).</para></listitem>
-  -->
-
-
-  <listitem><para>Nix now uses Berkeley DB 4.5.  The database is
-  upgraded automatically, but you should be careful not to use old
-  versions of Nix that still use Berkeley DB 4.4.</para></listitem>
-
-
-  <!-- foo
-  <listitem><para>TODO: option <option>- -reregister</option> in
-  <command>nix-store - -register-validity</command>.</para></listitem>
-  -->
-
-
-  <listitem><para>The option <option>--max-silent-time</option>
-  (corresponding to the configuration setting
-  <literal>build-max-silent-time</literal>) allows you to set a
-  timeout on builds β€” if a build produces no output on
-  <literal>stdout</literal> or <literal>stderr</literal> for the given
-  number of seconds, it is terminated.  This is useful for recovering
-  automatically from builds that are stuck in an infinite
-  loop.</para></listitem>
-
-
-  <listitem><para><command>nix-channel</command>: each subscribed
-  channel is its own attribute in the top-level expression generated
-  for the channel.  This allows disambiguation (e.g. <literal>nix-env
-  -i -A nixpkgs_unstable.firefox</literal>).</para></listitem>
-
-
-  <listitem><para>The substitutes table has been removed from the
-  database.  This makes operations such as <command>nix-pull</command>
-  and <command>nix-channel --update</command> much, much
-  faster.</para></listitem>
-
-
-  <listitem><para><command>nix-pull</command> now supports
-  bzip2-compressed manifests.  This speeds up
-  channels.</para></listitem>
-
-
-  <listitem><para><command>nix-prefetch-url</command> now has a
-  limited form of caching.  This is used by
-  <command>nix-channel</command> to prevent unnecessary downloads when
-  the channel hasn’t changed.</para></listitem>
-
-
-  <listitem><para><command>nix-prefetch-url</command> now by default
-  computes the SHA-256 hash of the file instead of the MD5 hash.  In
-  calls to <function>fetchurl</function> you should pass the
-  <literal>sha256</literal> attribute instead of
-  <literal>md5</literal>.  You can pass either a hexadecimal or a
-  base-32 encoding of the hash.</para></listitem>
-
-
-  <listitem><para>Nix can now perform builds in an automatically
-  generated β€œchroot”.  This prevents a builder from accessing stuff
-  outside of the Nix store, and thus helps ensure purity.  This is an
-  experimental feature.</para></listitem>
-
-
-  <listitem><para>The new command <command>nix-store
-  --optimise</command> reduces Nix store disk space usage by finding
-  identical files in the store and hard-linking them to each other.
-  It typically reduces the size of the store by something like
-  25-35%.</para></listitem>
-
-
-  <listitem><para><filename>~/.nix-defexpr</filename> can now be a
-  directory, in which case the Nix expressions in that directory are
-  combined into an attribute set, with the file names used as the
-  names of the attributes.  The command <command>nix-env
-  --import</command> (which set the
-  <filename>~/.nix-defexpr</filename> symlink) is
-  removed.</para></listitem>
-
-
-  <listitem><para>Derivations can specify the new special attribute
-  <varname>allowedReferences</varname> to enforce that the references
-  in the output of a derivation are a subset of a declared set of
-  paths.  For example, if <varname>allowedReferences</varname> is an
-  empty list, then the output must not have any references.  This is
-  used in NixOS to check that generated files such as initial ramdisks
-  for booting Linux don’t have any dependencies.</para></listitem>
-
-
-  <listitem><para>The new attribute
-  <varname>exportReferencesGraph</varname> allows builders access to
-  the references graph of their inputs.  This is used in NixOS for
-  tasks such as generating ISO-9660 images that contain a Nix store
-  populated with the closure of certain paths.</para></listitem>
-
-
-  <listitem><para>Fixed-output derivations (like
-  <function>fetchurl</function>) can define the attribute
-  <varname>impureEnvVars</varname> to allow external environment
-  variables to be passed to builders.  This is used in Nixpkgs to
-  support proxy configuration, among other things.</para></listitem>
-
-
-  <listitem><para>Several new built-in functions:
-  <function>builtins.attrNames</function>,
-  <function>builtins.filterSource</function>,
-  <function>builtins.isAttrs</function>,
-  <function>builtins.isFunction</function>,
-  <function>builtins.listToAttrs</function>,
-  <function>builtins.stringLength</function>,
-  <function>builtins.sub</function>,
-  <function>builtins.substring</function>,
-  <function>throw</function>,
-  <function>builtins.trace</function>,
-  <function>builtins.readFile</function>.</para></listitem>
-
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.12.xml b/third_party/nix/doc/manual/release-notes/rl-0.12.xml
deleted file mode 100644
index fdba8c4d57..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.12.xml
+++ /dev/null
@@ -1,175 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-0.12">
-
-<title>Release 0.12 (2008-11-20)</title>
-
-<itemizedlist>
-
-  <listitem>
-    <para>Nix no longer uses Berkeley DB to store Nix store metadata.
-    The principal advantages of the new storage scheme are: it works
-    properly over decent implementations of NFS (allowing Nix stores
-    to be shared between multiple machines); no recovery is needed
-    when a Nix process crashes; no write access is needed for
-    read-only operations; no more running out of Berkeley DB locks on
-    certain operations.</para>
-
-    <para>You still need to compile Nix with Berkeley DB support if
-    you want Nix to automatically convert your old Nix store to the
-    new schema.  If you don’t need this, you can build Nix with the
-    <filename>configure</filename> option
-    <option>--disable-old-db-compat</option>.</para>
-
-    <para>After the automatic conversion to the new schema, you can
-    delete the old Berkeley DB files:
-
-    <screen>
-$ cd /nix/var/nix/db
-$ rm __db* log.* derivers references referrers reserved validpaths DB_CONFIG</screen>
-
-    The new metadata is stored in the directories
-    <filename>/nix/var/nix/db/info</filename> and
-    <filename>/nix/var/nix/db/referrer</filename>.  Though the
-    metadata is stored in human-readable plain-text files, they are
-    not intended to be human-editable, as Nix is rather strict about
-    the format.</para>
-
-    <para>The new storage schema may or may not require less disk
-    space than the Berkeley DB environment, mostly depending on the
-    cluster size of your file system.  With 1 KiB clusters (which
-    seems to be the <literal>ext3</literal> default nowadays) it
-    usually takes up much less space.</para>
-  </listitem>
-
-  <listitem><para>There is a new substituter that copies paths
-  directly from other (remote) Nix stores mounted somewhere in the
-  filesystem.  For instance, you can speed up an installation by
-  mounting some remote Nix store that already has the packages in
-  question via NFS or <literal>sshfs</literal>.  The environment
-  variable <envar>NIX_OTHER_STORES</envar> specifies the locations of
-  the remote Nix directories,
-  e.g. <literal>/mnt/remote-fs/nix</literal>.</para></listitem>
-
-  <listitem><para>New <command>nix-store</command> operations
-  <option>--dump-db</option> and <option>--load-db</option> to dump
-  and reload the Nix database.</para></listitem>
-
-  <listitem><para>The garbage collector has a number of new options to
-  allow only some of the garbage to be deleted.  The option
-  <option>--max-freed <replaceable>N</replaceable></option> tells the
-  collector to stop after at least <replaceable>N</replaceable> bytes
-  have been deleted.  The option <option>--max-links
-  <replaceable>N</replaceable></option> tells it to stop after the
-  link count on <filename>/nix/store</filename> has dropped below
-  <replaceable>N</replaceable>.  This is useful for very large Nix
-  stores on filesystems with a 32000 subdirectories limit (like
-  <literal>ext3</literal>).  The option <option>--use-atime</option>
-  causes store paths to be deleted in order of ascending last access
-  time.  This allows non-recently used stuff to be deleted.  The
-  option <option>--max-atime <replaceable>time</replaceable></option>
-  specifies an upper limit to the last accessed time of paths that may
-  be deleted.  For instance,
-
-    <screen>
-    $ nix-store --gc -v --max-atime $(date +%s -d "2 months ago")</screen>
-
-  deletes everything that hasn’t been accessed in two months.</para></listitem>
-
-  <listitem><para><command>nix-env</command> now uses optimistic
-  profile locking when performing an operation like installing or
-  upgrading, instead of setting an exclusive lock on the profile.
-  This allows multiple <command>nix-env -i / -u / -e</command>
-  operations on the same profile in parallel.  If a
-  <command>nix-env</command> operation sees at the end that the profile
-  was changed in the meantime by another process, it will just
-  restart.  This is generally cheap because the build results are
-  still in the Nix store.</para></listitem>
-
-  <listitem><para>The option <option>--dry-run</option> is now
-  supported by <command>nix-store -r</command> and
-  <command>nix-build</command>.</para></listitem>
-
-  <listitem><para>The information previously shown by
-  <option>--dry-run</option> (i.e., which derivations will be built
-  and which paths will be substituted) is now always shown by
-  <command>nix-env</command>, <command>nix-store -r</command> and
-  <command>nix-build</command>.  The total download size of
-  substitutable paths is now also shown.  For instance, a build will
-  show something like
-
-    <screen>
-the following derivations will be built:
-  /nix/store/129sbxnk5n466zg6r1qmq1xjv9zymyy7-activate-configuration.sh.drv
-  /nix/store/7mzy971rdm8l566ch8hgxaf89x7lr7ik-upstart-jobs.drv
-  ...
-the following paths will be downloaded/copied (30.02 MiB):
-  /nix/store/4m8pvgy2dcjgppf5b4cj5l6wyshjhalj-samba-3.2.4
-  /nix/store/7h1kwcj29ip8vk26rhmx6bfjraxp0g4l-libunwind-0.98.6
-  ...</screen>
-
-  </para></listitem>
-
-  <listitem><para>Language features:
-
-    <itemizedlist>
-
-      <listitem><para>@-patterns as in Haskell.  For instance, in a
-      function definition
-
-      <programlisting>f = args @ {x, y, z}: <replaceable>...</replaceable>;</programlisting>
-
-      <varname>args</varname> refers to the argument as a whole, which
-      is further pattern-matched against the attribute set pattern
-      <literal>{x, y, z}</literal>.</para></listitem>
-
-      <listitem><para>β€œ<literal>...</literal>” (ellipsis) patterns.
-      An attribute set pattern can now say <literal>...</literal>  at
-      the end of the attribute name list to specify that the function
-      takes <emphasis>at least</emphasis> the listed attributes, while
-      ignoring additional attributes.  For instance,
-
-      <programlisting>{stdenv, fetchurl, fuse, ...}: <replaceable>...</replaceable></programlisting>
-
-      defines a function that accepts any attribute set that includes
-      at least the three listed attributes.</para></listitem>
-
-      <listitem><para>New primops:
-      <varname>builtins.parseDrvName</varname> (split a package name
-      string like <literal>"nix-0.12pre12876"</literal> into its name
-      and version components, e.g. <literal>"nix"</literal> and
-      <literal>"0.12pre12876"</literal>),
-      <varname>builtins.compareVersions</varname> (compare two version
-      strings using the same algorithm that <command>nix-env</command>
-      uses), <varname>builtins.length</varname> (efficiently compute
-      the length of a list), <varname>builtins.mul</varname> (integer
-      multiplication), <varname>builtins.div</varname> (integer
-      division).
-      <!-- <varname>builtins.genericClosure</varname> -->
-      </para></listitem>
-
-    </itemizedlist>
-
-  </para></listitem>
-
-  <listitem><para><command>nix-prefetch-url</command> now supports
-  <literal>mirror://</literal> URLs, provided that the environment
-  variable <envar>NIXPKGS_ALL</envar> points at a Nixpkgs
-  tree.</para></listitem>
-
-  <listitem><para>Removed the commands
-  <command>nix-pack-closure</command> and
-  <command>nix-unpack-closure</command>.   You can do almost the same
-  thing but much more efficiently by doing <literal>nix-store --export
-  $(nix-store -qR <replaceable>paths</replaceable>) > closure</literal> and
-  <literal>nix-store --import &lt;
-  closure</literal>.</para></listitem>
-
-  <listitem><para>Lots of bug fixes, including a big performance bug in
-  the handling of <literal>with</literal>-expressions.</para></listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.13.xml b/third_party/nix/doc/manual/release-notes/rl-0.13.xml
deleted file mode 100644
index cce2e4a26b..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.13.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-0.13">
-
-<title>Release 0.13 (2009-11-05)</title>
-
-<para>This is primarily a bug fix release.  It has some new
-features:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>Syntactic sugar for writing nested attribute sets.  Instead of
-
-<programlisting>
-{
-  foo = {
-    bar = 123;
-    xyzzy = true;
-  };
-  a = { b = { c = "d"; }; };
-}
-</programlisting>
-
-    you can write
-
-<programlisting>
-{
-  foo.bar = 123;
-  foo.xyzzy = true;
-  a.b.c = "d";
-}
-</programlisting>
-
-    This is useful, for instance, in NixOS configuration files.</para>
-
-  </listitem>
-
-  <listitem>
-    <para>Support for Nix channels generated by Hydra, the Nix-based
-    continuous build system.  (Hydra generates NAR archives on the
-    fly, so the size and hash of these archives isn’t known in
-    advance.)</para>
-  </listitem>
-
-  <listitem>
-    <para>Support <literal>i686-linux</literal> builds directly on
-    <literal>x86_64-linux</literal> Nix installations.  This is
-    implemented using the <function>personality()</function> syscall,
-    which causes <command>uname</command> to return
-    <literal>i686</literal> in child processes.</para>
-  </listitem>
-
-  <listitem>
-    <para>Various improvements to the <literal>chroot</literal>
-    support.  Building in a <literal>chroot</literal> works quite well
-    now.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix no longer blocks if it tries to build a path and another
-    process is already building the same path.  Instead it tries to
-    build another buildable path first.  This improves
-    parallelism.</para>
-  </listitem>
-
-  <listitem>
-    <para>Support for large (> 4 GiB) files in NAR archives.</para>
-  </listitem>
-
-  <listitem>
-    <para>Various (performance) improvements to the remote build
-    mechanism.</para>
-  </listitem>
-
-  <listitem>
-    <para>New primops: <varname>builtins.addErrorContext</varname> (to
-    add a string to stack traces β€” useful for debugging),
-    <varname>builtins.isBool</varname>,
-    <varname>builtins.isString</varname>,
-    <varname>builtins.isInt</varname>,
-    <varname>builtins.intersectAttrs</varname>.</para>
-  </listitem>
-
-  <listitem>
-    <para>OpenSolaris support (Sander van der Burg).</para>
-  </listitem>
-
-  <listitem>
-    <para>Stack traces are no longer displayed unless the
-    <option>--show-trace</option> option is used.</para>
-  </listitem>
-
-  <listitem>
-    <para>The scoping rules for <literal>inherit
-    (<replaceable>e</replaceable>) ...</literal> in recursive
-    attribute sets have changed.  The expression
-    <replaceable>e</replaceable> can now refer to the attributes
-    defined in the containing set.</para>
-  </listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.14.xml b/third_party/nix/doc/manual/release-notes/rl-0.14.xml
deleted file mode 100644
index e5fe9da78e..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.14.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-0.14">
-
-<title>Release 0.14 (2010-02-04)</title>
-
-<para>This release has the following improvements:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>The garbage collector now starts deleting garbage much
-    faster than before.  It no longer determines liveness of all paths
-    in the store, but does so on demand.</para>
-  </listitem>
-
-  <listitem>
-    <para>Added a new operation, <command>nix-store --query
-    --roots</command>, that shows the garbage collector roots that
-    directly or indirectly point to the given store paths.</para>
-  </listitem>
-
-  <listitem>
-    <para>Removed support for converting Berkeley DB-based Nix
-    databases to the new schema.</para>
-  </listitem>
-
-  <listitem>
-    <para>Removed the <option>--use-atime</option> and
-    <option>--max-atime</option> garbage collector options.  They were
-    not very useful in practice.</para>
-  </listitem>
-
-  <listitem>
-    <para>On Windows, Nix now requires Cygwin 1.7.x.</para>
-  </listitem>
-
-  <listitem>
-    <para>A few bug fixes.</para>
-  </listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.15.xml b/third_party/nix/doc/manual/release-notes/rl-0.15.xml
deleted file mode 100644
index 9f58a8efc5..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.15.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-0.15">
-
-<title>Release 0.15 (2010-03-17)</title>
-
-<para>This is a bug-fix release.  Among other things, it fixes
-building on Mac OS X (Snow Leopard), and improves the contents of
-<filename>/etc/passwd</filename> and <filename>/etc/group</filename>
-in <literal>chroot</literal> builds.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.16.xml b/third_party/nix/doc/manual/release-notes/rl-0.16.xml
deleted file mode 100644
index af1edc0ebb..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.16.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-0.16">
-
-<title>Release 0.16 (2010-08-17)</title>
-
-<para>This release has the following improvements:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>The Nix expression evaluator is now much faster in most
-    cases: typically, <link
-    xlink:href="http://www.mail-archive.com/nix-dev@cs.uu.nl/msg04113.html">3
-    to 8 times compared to the old implementation</link>.  It also
-    uses less memory.  It no longer depends on the ATerm
-    library.</para>
-  </listitem>
-
-  <listitem>
-    <para>
-      Support for configurable parallelism inside builders.  Build
-      scripts have always had the ability to perform multiple build
-      actions in parallel (for instance, by running <command>make -j
-      2</command>), but this was not desirable because the number of
-      actions to be performed in parallel was not configurable.  Nix
-      now has an option <option>--cores
-      <replaceable>N</replaceable></option> as well as a configuration
-      setting <varname>build-cores =
-      <replaceable>N</replaceable></varname> that causes the
-      environment variable <envar>NIX_BUILD_CORES</envar> to be set to
-      <replaceable>N</replaceable> when the builder is invoked.  The
-      builder can use this at its discretion to perform a parallel
-      build, e.g., by calling <command>make -j
-      <replaceable>N</replaceable></command>.  In Nixpkgs, this can be
-      enabled on a per-package basis by setting the derivation
-      attribute <varname>enableParallelBuilding</varname> to
-      <literal>true</literal>.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-store -q</command> now supports XML output
-    through the <option>--xml</option> flag.</para>
-  </listitem>
-
-  <listitem>
-    <para>Several bug fixes.</para>
-  </listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.5.xml b/third_party/nix/doc/manual/release-notes/rl-0.5.xml
deleted file mode 100644
index e9f8bf2701..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.5.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.5">
-
-<title>Release 0.5 and earlier</title>
-
-<para>Please refer to the Subversion commit log messages.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.6.xml b/third_party/nix/doc/manual/release-notes/rl-0.6.xml
deleted file mode 100644
index 6dc6521d3c..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.6.xml
+++ /dev/null
@@ -1,122 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.6">
-
-<title>Release 0.6 (2004-11-14)</title>
-
-<itemizedlist>
-
-  <listitem>
-    <para>Rewrite of the normalisation engine.
-
-    <itemizedlist>
-
-      <listitem><para>Multiple builds can now be performed in parallel
-      (option <option>-j</option>).</para></listitem>
-
-      <listitem><para>Distributed builds.  Nix can now call a shell
-      script to forward builds to Nix installations on remote
-      machines, which may or may not be of the same platform
-      type.</para></listitem>
-
-      <listitem><para>Option <option>--fallback</option> allows
-      recovery from broken substitutes.</para></listitem>
-
-      <listitem><para>Option <option>--keep-going</option> causes
-      building of other (unaffected) derivations to continue if one
-      failed.</para></listitem>
-
-    </itemizedlist>
-
-    </para>
-
-  </listitem>
-
-  <listitem><para>Improvements to the garbage collector (i.e., it
-  should actually work now).</para></listitem>
-
-  <listitem><para>Setuid Nix installations allow a Nix store to be
-  shared among multiple users.</para></listitem>
-
-  <listitem><para>Substitute registration is much faster
-  now.</para></listitem>
-
-  <listitem><para>A utility <command>nix-build</command> to build a
-  Nix expression and create a symlink to the result int the current
-  directory; useful for testing Nix derivations.</para></listitem>
-
-  <listitem><para>Manual updates.</para></listitem>
-
-  <listitem>
-
-    <para><command>nix-env</command> changes:
-
-    <itemizedlist>
-
-      <listitem><para>Derivations for other platforms are filtered out
-      (which can be overridden using
-      <option>--system-filter</option>).</para></listitem>
-
-      <listitem><para><option>--install</option> by default now
-      uninstall previous derivations with the same
-      name.</para></listitem>
-
-      <listitem><para><option>--upgrade</option> allows upgrading to a
-      specific version.</para></listitem>
-
-      <listitem><para>New operation
-      <option>--delete-generations</option> to remove profile
-      generations (necessary for effective garbage
-      collection).</para></listitem>
-
-      <listitem><para>Nicer output (sorted,
-      columnised).</para></listitem>
-
-    </itemizedlist>
-
-    </para>
-
-  </listitem>
-
-  <listitem><para>More sensible verbosity levels all around (builder
-  output is now shown always, unless <option>-Q</option> is
-  given).</para></listitem>
-
-  <listitem>
-
-    <para>Nix expression language changes:
-
-    <itemizedlist>
-
-      <listitem><para>New language construct: <literal>with
-      <replaceable>E1</replaceable>;
-      <replaceable>E2</replaceable></literal> brings all attributes
-      defined in the attribute set <replaceable>E1</replaceable> in
-      scope in <replaceable>E2</replaceable>.</para></listitem>
-
-      <listitem><para>Added a <function>map</function>
-      function.</para></listitem>
-
-      <listitem><para>Various new operators (e.g., string
-      concatenation).</para></listitem>
-
-    </itemizedlist>
-
-    </para>
-
-  </listitem>
-
-  <listitem><para>Expression evaluation is much
-  faster.</para></listitem>
-
-  <listitem><para>An Emacs mode for editing Nix expressions (with
-  syntax highlighting and indentation) has been
-  added.</para></listitem>
-
-  <listitem><para>Many bug fixes.</para></listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.7.xml b/third_party/nix/doc/manual/release-notes/rl-0.7.xml
deleted file mode 100644
index 6f95db4367..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.7.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.7">
-
-<title>Release 0.7 (2005-01-12)</title>
-
-<itemizedlist>
-
-  <listitem><para>Binary patching.  When upgrading components using
-  pre-built binaries (through nix-pull / nix-channel), Nix can
-  automatically download and apply binary patches to already installed
-  components instead of full downloads.  Patching is β€œsmart”: if there
-  is a <emphasis>sequence</emphasis> of patches to an installed
-  component, Nix will use it.  Patches are currently generated
-  automatically between Nixpkgs (pre-)releases.</para></listitem>
-
-  <listitem><para>Simplifications to the substitute
-  mechanism.</para></listitem>
-
-  <listitem><para>Nix-pull now stores downloaded manifests in
-  <filename>/nix/var/nix/manifests</filename>.</para></listitem>
-
-  <listitem><para>Metadata on files in the Nix store is canonicalised
-  after builds: the last-modified timestamp is set to 0 (00:00:00
-  1/1/1970), the mode is set to 0444 or 0555 (readable and possibly
-  executable by all; setuid/setgid bits are dropped), and the group is
-  set to the default.  This ensures that the result of a build and an
-  installation through a substitute is the same; and that timestamp
-  dependencies are revealed.</para></listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.8.1.xml b/third_party/nix/doc/manual/release-notes/rl-0.8.1.xml
deleted file mode 100644
index f7ffca0f8d..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.8.1.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.8.1">
-
-<title>Release 0.8.1 (2005-04-13)</title>
-
-<para>This is a bug fix release.</para>
-
-<itemizedlist>
-
-  <listitem><para>Patch downloading was broken.</para></listitem>
-
-  <listitem><para>The garbage collector would not delete paths that
-  had references from invalid (but substitutable)
-  paths.</para></listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.8.xml b/third_party/nix/doc/manual/release-notes/rl-0.8.xml
deleted file mode 100644
index 784b26c6b7..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.8.xml
+++ /dev/null
@@ -1,246 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.8">
-
-<title>Release 0.8 (2005-04-11)</title>
-
-<para>NOTE: the hashing scheme in Nix 0.8 changed (as detailed below).
-As a result, <command>nix-pull</command> manifests and channels built
-for Nix 0.7 and below will now work anymore.  However, the Nix
-expression language has not changed, so you can still build from
-source.  Also, existing user environments continue to work.  Nix 0.8
-will automatically upgrade the database schema of previous
-installations when it is first run.</para>
-
-<para>If you get the error message
-
-<screen>
-you have an old-style manifest `/nix/var/nix/manifests/[...]'; please
-delete it</screen>
-
-you should delete previously downloaded manifests:
-
-<screen>
-$ rm /nix/var/nix/manifests/*</screen>
-
-If <command>nix-channel</command> gives the error message
-
-<screen>
-manifest `http://catamaran.labs.cs.uu.nl/dist/nix/channels/[channel]/MANIFEST'
-is too old (i.e., for Nix &lt;= 0.7)</screen>
-
-then you should unsubscribe from the offending channel
-(<command>nix-channel --remove
-<replaceable>URL</replaceable></command>; leave out
-<literal>/MANIFEST</literal>), and subscribe to the same URL, with
-<literal>channels</literal> replaced by <literal>channels-v3</literal>
-(e.g., <link
-xlink:href='http://catamaran.labs.cs.uu.nl/dist/nix/channels-v3/nixpkgs-unstable'
-/>).</para>
-
-<para>Nix 0.8 has the following improvements:
-
-<itemizedlist>
-
-  <listitem><para>The cryptographic hashes used in store paths are now
-  160 bits long, but encoded in base-32 so that they are still only 32
-  characters long (e.g.,
-  <filename>/nix/store/csw87wag8bqlqk7ipllbwypb14xainap-atk-1.9.0</filename>).
-  (This is actually a 160 bit truncation of a SHA-256
-  hash.)</para></listitem>
-
-  <listitem><para>Big cleanups and simplifications of the basic store
-  semantics.  The notion of β€œclosure store expressions” is gone (and
-  so is the notion of β€œsuccessors”); the file system references of a
-  store path are now just stored in the database.</para>
-
-  <para>For instance, given any store path, you can query its closure:
-
-  <screen>
-$ nix-store -qR $(which firefox)
-... lots of paths ...</screen>
-
-  Also, Nix now remembers for each store path the derivation that
-  built it (the β€œderiver”):
-
-  <screen>
-$ nix-store -qR $(which firefox)
-/nix/store/4b0jx7vq80l9aqcnkszxhymsf1ffa5jd-firefox-1.0.1.drv</screen>
-
-  So to see the build-time dependencies, you can do
-
-  <screen>
-$ nix-store -qR $(nix-store -qd $(which firefox))</screen>
-
-  or, in a nicer format:
-
-  <screen>
-$ nix-store -q --tree $(nix-store -qd $(which firefox))</screen>
-
-  </para>
-
-  <para>File system references are also stored in reverse.  For
-  instance, you can query all paths that directly or indirectly use a
-  certain Glibc:
-
-  <screen>
-$ nix-store -q --referrers-closure \
-    /nix/store/8lz9yc6zgmc0vlqmn2ipcpkjlmbi51vv-glibc-2.3.4</screen>
-
-  </para>
-
-  </listitem>
-
-  <listitem><para>The concept of fixed-output derivations has been
-  formalised.  Previously, functions such as
-  <function>fetchurl</function> in Nixpkgs used a hack (namely,
-  explicitly specifying a store path hash) to prevent changes to, say,
-  the URL of the file from propagating upwards through the dependency
-  graph, causing rebuilds of everything.  This can now be done cleanly
-  by specifying the <varname>outputHash</varname> and
-  <varname>outputHashAlgo</varname> attributes.  Nix itself checks
-  that the content of the output has the specified hash.  (This is
-  important for maintaining certain invariants necessary for future
-  work on secure shared stores.)</para></listitem>
-
-  <listitem><para>One-click installation :-) It is now possible to
-  install any top-level component in Nixpkgs directly, through the web
-  β€” see, e.g., <link
-  xlink:href='http://catamaran.labs.cs.uu.nl/dist/nixpkgs-0.8/' />.
-  All you have to do is associate
-  <filename>/nix/bin/nix-install-package</filename> with the MIME type
-  <literal>application/nix-package</literal> (or the extension
-  <filename>.nixpkg</filename>), and clicking on a package link will
-  cause it to be installed, with all appropriate dependencies.  If you
-  just want to install some specific application, this is easier than
-  subscribing to a channel.</para></listitem>
-
-  <listitem><para><command>nix-store -r
-  <replaceable>PATHS</replaceable></command> now builds all the
-  derivations PATHS in parallel.  Previously it did them sequentially
-  (though exploiting possible parallelism between subderivations).
-  This is nice for build farms.</para></listitem>
-
-  <listitem><para><command>nix-channel</command> has new operations
-  <option>--list</option> and
-  <option>--remove</option>.</para></listitem>
-
-  <listitem><para>New ways of installing components into user
-  environments:
-
-  <itemizedlist>
-
-    <listitem><para>Copy from another user environment:
-
-    <screen>
-$ nix-env -i --from-profile .../other-profile firefox</screen>
-
-    </para></listitem>
-
-    <listitem><para>Install a store derivation directly (bypassing the
-    Nix expression language entirely):
-
-    <screen>
-$ nix-env -i /nix/store/z58v41v21xd3...-aterm-2.3.1.drv</screen>
-
-    (This is used to implement <command>nix-install-package</command>,
-    which is therefore immune to evolution in the Nix expression
-    language.)</para></listitem>
-
-    <listitem><para>Install an already built store path directly:
-
-    <screen>
-$ nix-env -i /nix/store/hsyj5pbn0d9i...-aterm-2.3.1</screen>
-
-    </para></listitem>
-
-    <listitem><para>Install the result of a Nix expression specified
-    as a command-line argument:
-
-    <screen>
-$ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
-
-    The difference with the normal installation mode is that
-    <option>-E</option> does not use the <varname>name</varname>
-    attributes of derivations.  Therefore, this can be used to
-    disambiguate multiple derivations with the same
-    name.</para></listitem>
-
-  </itemizedlist></para></listitem>
-
-  <listitem><para>A hash of the contents of a store path is now stored
-  in the database after a successful build.  This allows you to check
-  whether store paths have been tampered with: <command>nix-store
-  --verify --check-contents</command>.</para></listitem>
-
-  <listitem>
-
-    <para>Implemented a concurrent garbage collector.  It is now
-    always safe to run the garbage collector, even if other Nix
-    operations are happening simultaneously.</para>
-
-    <para>However, there can still be GC races if you use
-    <command>nix-instantiate</command> and <command>nix-store
-    --realise</command> directly to build things.  To prevent races,
-    use the <option>--add-root</option> flag of those commands.</para>
-
-  </listitem>
-
-  <listitem><para>The garbage collector now finally deletes paths in
-  the right order (i.e., topologically sorted under the β€œreferences”
-  relation), thus making it safe to interrupt the collector without
-  risking a store that violates the closure
-  invariant.</para></listitem>
-
-  <listitem><para>Likewise, the substitute mechanism now downloads
-  files in the right order, thus preserving the closure invariant at
-  all times.</para></listitem>
-
-  <listitem><para>The result of <command>nix-build</command> is now
-  registered as a root of the garbage collector.  If the
-  <filename>./result</filename> link is deleted, the GC root
-  disappears automatically.</para></listitem>
-
-  <listitem>
-
-    <para>The behaviour of the garbage collector can be changed
-    globally by setting options in
-    <filename>/nix/etc/nix/nix.conf</filename>.
-
-    <itemizedlist>
-
-      <listitem><para><literal>gc-keep-derivations</literal> specifies
-      whether deriver links should be followed when searching for live
-      paths.</para></listitem>
-
-      <listitem><para><literal>gc-keep-outputs</literal> specifies
-      whether outputs of derivations should be followed when searching
-      for live paths.</para></listitem>
-
-      <listitem><para><literal>env-keep-derivations</literal>
-      specifies whether user environments should store the paths of
-      derivations when they are added (thus keeping the derivations
-      alive).</para></listitem>
-
-    </itemizedlist>
-
-  </para></listitem>
-
-  <listitem><para>New <command>nix-env</command> query flags
-  <option>--drv-path</option> and
-  <option>--out-path</option>.</para></listitem>
-
-  <listitem><para><command>fetchurl</command> allows SHA-1 and SHA-256
-  in addition to MD5.  Just specify the attribute
-  <varname>sha1</varname> or <varname>sha256</varname> instead of
-  <varname>md5</varname>.</para></listitem>
-
-  <listitem><para>Manual updates.</para></listitem>
-
-</itemizedlist>
-
-</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.9.1.xml b/third_party/nix/doc/manual/release-notes/rl-0.9.1.xml
deleted file mode 100644
index 85d11f4168..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.9.1.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.9.1">
-
-<title>Release 0.9.1 (2005-09-20)</title>
-
-<para>This bug fix release addresses a problem with the ATerm library
-when the <option>--with-aterm</option> flag in
-<command>configure</command> was <emphasis>not</emphasis> used.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.9.2.xml b/third_party/nix/doc/manual/release-notes/rl-0.9.2.xml
deleted file mode 100644
index cb705e98ac..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.9.2.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.9.2">
-
-<title>Release 0.9.2 (2005-09-21)</title>
-
-<para>This bug fix release fixes two problems on Mac OS X:
-
-<itemizedlist>
-
-  <listitem><para>If Nix was linked against statically linked versions
-  of the ATerm or Berkeley DB library, there would be dynamic link
-  errors at runtime.</para></listitem>
-
-  <listitem><para><command>nix-pull</command> and
-  <command>nix-push</command> intermittently failed due to race
-  conditions involving pipes and child processes with error messages
-  such as <literal>open2: open(GLOB(0x180b2e4), >&amp;=9) failed: Bad
-  file descriptor at /nix/bin/nix-pull line 77</literal> (issue
-  <literal>NIX-14</literal>).</para></listitem>
-
-</itemizedlist>
-
-</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-0.9.xml b/third_party/nix/doc/manual/release-notes/rl-0.9.xml
deleted file mode 100644
index fd1e633f78..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-0.9.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ch-relnotes-0.9">
-
-<title>Release 0.9 (2005-09-16)</title>
-
-<para>NOTE: this version of Nix uses Berkeley DB 4.3 instead of 4.2.
-The database is upgraded automatically, but you should be careful not
-to use old versions of Nix that still use Berkeley DB 4.2.  In
-particular, if you use a Nix installed through Nix, you should run
-
-<screen>
-$ nix-store --clear-substitutes</screen>
-
-first.</para>
-
-
-<itemizedlist>
-
-  <listitem><para>Unpacking of patch sequences is much faster now
-  since we no longer do redundant unpacking and repacking of
-  intermediate paths.</para></listitem>
-
-  <listitem><para>Nix now uses Berkeley DB 4.3.</para></listitem>
-
-  <listitem><para>The <function>derivation</function> primitive is
-  lazier.  Attributes of dependent derivations can mutually refer to
-  each other (as long as there are no data dependencies on the
-  <varname>outPath</varname> and <varname>drvPath</varname> attributes
-  computed by <function>derivation</function>).</para>
-
-  <para>For example, the expression <literal>derivation
-  attrs</literal> now evaluates to (essentially)
-
-  <programlisting>
-attrs // {
-  type = "derivation";
-  outPath = derivation! attrs;
-  drvPath = derivation! attrs;
-}</programlisting>
-
-  where <function>derivation!</function> is a primop that does the
-  actual derivation instantiation (i.e., it does what
-  <function>derivation</function> used to do).  The advantage is that
-  it allows commands such as <command>nix-env -qa</command> and
-  <command>nix-env -i</command> to be much faster since they no longer
-  need to instantiate all derivations, just the
-  <varname>name</varname> attribute.</para>
-
-  <para>Also, it allows derivations to cyclically reference each
-  other, for example,
-
-  <programlisting>
-webServer = derivation {
-  ...
-  hostName = "svn.cs.uu.nl";
-  services = [svnService];
-};
-&#x20;
-svnService = derivation {
-  ...
-  hostName = webServer.hostName;
-};</programlisting>
-
-  Previously, this would yield a black hole (infinite recursion).</para>
-
-  </listitem>
-
-  <listitem><para><command>nix-build</command> now defaults to using
-  <filename>./default.nix</filename> if no Nix expression is
-  specified.</para></listitem>
-
-  <listitem><para><command>nix-instantiate</command>, when applied to
-  a Nix expression that evaluates to a function, will call the
-  function automatically if all its arguments have
-  defaults.</para></listitem>
-
-  <listitem><para>Nix now uses libtool to build dynamic libraries.
-  This reduces the size of executables.</para></listitem>
-
-  <listitem><para>A new list concatenation operator
-  <literal>++</literal>.  For example, <literal>[1 2 3] ++ [4 5
-  6]</literal> evaluates to <literal>[1 2 3 4 5
-  6]</literal>.</para></listitem>
-
-  <listitem><para>Some currently undocumented primops to support
-  low-level build management using Nix (i.e., using Nix as a Make
-  replacement).  See the commit messages for <literal>r3578</literal>
-  and <literal>r3580</literal>.</para></listitem>
-
-  <listitem><para>Various bug fixes and performance
-  improvements.</para></listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.0.xml b/third_party/nix/doc/manual/release-notes/rl-1.0.xml
deleted file mode 100644
index ff11168d09..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.0.xml
+++ /dev/null
@@ -1,119 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.0">
-
-<title>Release 1.0 (2012-05-11)</title>
-
-<para>There have been numerous improvements and bug fixes since the
-previous release.  Here are the most significant:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>Nix can now optionally use the Boehm garbage collector.
-    This significantly reduces the Nix evaluator’s memory footprint,
-    especially when evaluating large NixOS system configurations.  It
-    can be enabled using the <option>--enable-gc</option> configure
-    option.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now uses SQLite for its database.  This is faster and
-    more flexible than the old <emphasis>ad hoc</emphasis> format.
-    SQLite is also used to cache the manifests in
-    <filename>/nix/var/nix/manifests</filename>, resulting in a
-    significant speedup.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now has an search path for expressions.  The search path
-    is set using the environment variable <envar>NIX_PATH</envar> and
-    the <option>-I</option> command line option.  In Nix expressions,
-    paths between angle brackets are used to specify files that must
-    be looked up in the search path.  For instance, the expression
-    <literal>&lt;nixpkgs/default.nix></literal> looks for a file
-    <filename>nixpkgs/default.nix</filename> relative to every element
-    in the search path.</para>
-  </listitem>
-
-  <listitem>
-    <para>The new command <command>nix-build --run-env</command>
-    builds all dependencies of a derivation, then starts a shell in an
-    environment containing all variables from the derivation.  This is
-    useful for reproducing the environment of a derivation for
-    development.</para>
-  </listitem>
-
-  <listitem>
-    <para>The new command <command>nix-store --verify-path</command>
-    verifies that the contents of a store path have not
-    changed.</para>
-  </listitem>
-
-  <listitem>
-    <para>The new command <command>nix-store --print-env</command>
-    prints out the environment of a derivation in a format that can be
-    evaluated by a shell.</para>
-  </listitem>
-
-  <listitem>
-    <para>Attribute names can now be arbitrary strings.  For instance,
-    you can write <literal>{ "foo-1.2" = …; "bla bla" = …; }."bla
-    bla"</literal>.</para>
-  </listitem>
-
-  <listitem>
-    <para>Attribute selection can now provide a default value using
-    the <literal>or</literal> operator.  For instance, the expression
-    <literal>x.y.z or e</literal> evaluates to the attribute
-    <literal>x.y.z</literal> if it exists, and <literal>e</literal>
-    otherwise.</para>
-  </listitem>
-
-  <listitem>
-    <para>The right-hand side of the <literal>?</literal> operator can
-    now be an attribute path, e.g., <literal>attrs ?
-    a.b.c</literal>.</para>
-  </listitem>
-
-  <listitem>
-    <para>On Linux, Nix will now make files in the Nix store immutable
-    on filesystems that support it.  This prevents accidental
-    modification of files in the store by the root user.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix has preliminary support for derivations with multiple
-    outputs.  This is useful because it allows parts of a package to
-    be deployed and garbage-collected separately.  For instance,
-    development parts of a package such as header files or static
-    libraries would typically not be part of the closure of an
-    application, resulting in reduced disk usage and installation
-    time.</para>
-  </listitem>
-
-  <listitem>
-    <para>The Nix store garbage collector is faster and holds the
-    global lock for a shorter amount of time.</para>
-  </listitem>
-
-  <listitem>
-    <para>The option <option>--timeout</option> (corresponding to the
-    configuration setting <literal>build-timeout</literal>) allows you
-    to set an absolute timeout on builds β€” if a build runs for more than
-    the given number of seconds, it is terminated.  This is useful for
-    recovering automatically from builds that are stuck in an infinite
-    loop but keep producing output, and for which
-    <literal>--max-silent-time</literal> is ineffective.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix development has moved to GitHub (<link
-    xlink:href="https://github.com/NixOS/nix" />).</para>
-  </listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.1.xml b/third_party/nix/doc/manual/release-notes/rl-1.1.xml
deleted file mode 100644
index 2f26e7a242..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.1.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.1">
-
-<title>Release 1.1 (2012-07-18)</title>
-
-<para>This release has the following improvements:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>On Linux, when doing a chroot build, Nix now uses various
-    namespace features provided by the Linux kernel to improve
-    build isolation.  Namely:
-    <itemizedlist>
-      <listitem><para>The private network namespace ensures that
-      builders cannot talk to the outside world (or vice versa): each
-      build only sees a private loopback interface.  This also means
-      that two concurrent builds can listen on the same port (e.g. as
-      part of a test) without conflicting with each
-      other.</para></listitem>
-      <listitem><para>The PID namespace causes each build to start as
-      PID 1.  Processes outside of the chroot are not visible to those
-      on the inside.  On the other hand, processes inside the chroot
-      <emphasis>are</emphasis> visible from the outside (though with
-      different PIDs).</para></listitem>
-      <listitem><para>The IPC namespace prevents the builder from
-      communicating with outside processes using SysV IPC mechanisms
-      (shared memory, message queues, semaphores).  It also ensures
-      that all IPC objects are destroyed when the builder
-      exits.</para></listitem>
-      <listitem><para>The UTS namespace ensures that builders see a
-      hostname of <literal>localhost</literal> rather than the actual
-      hostname.</para></listitem>
-      <listitem><para>The private mount namespace was already used by
-      Nix to ensure that the bind-mounts used to set up the chroot are
-      cleaned up automatically.</para></listitem>
-    </itemizedlist>
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>Build logs are now compressed using
-    <command>bzip2</command>.  The command <command>nix-store
-    -l</command> decompresses them on the fly.  This can be disabled
-    by setting the option <literal>build-compress-log</literal> to
-    <literal>false</literal>.</para>
-  </listitem>
-
-  <listitem>
-    <para>The creation of build logs in
-    <filename>/nix/var/log/nix/drvs</filename> can be disabled by
-    setting the new option <literal>build-keep-log</literal> to
-    <literal>false</literal>.  This is useful, for instance, for Hydra
-    build machines.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now reserves some space in
-    <filename>/nix/var/nix/db/reserved</filename> to ensure that the
-    garbage collector can run successfully if the disk is full.  This
-    is necessary because SQLite transactions fail if the disk is
-    full.</para>
-  </listitem>
-
-  <listitem>
-    <para>Added a basic <function>fetchurl</function> function.  This
-    is not intended to replace the <function>fetchurl</function> in
-    Nixpkgs, but is useful for bootstrapping; e.g., it will allow us
-    to get rid of the bootstrap binaries in the Nixpkgs source tree
-    and download them instead.  You can use it by doing
-    <literal>import &lt;nix/fetchurl.nix> { url =
-    <replaceable>url</replaceable>; sha256 =
-    "<replaceable>hash</replaceable>"; }</literal>. (Shea Levy)</para>
-  </listitem>
-
-  <listitem>
-    <para>Improved RPM spec file. (Michel Alexandre Salim)</para>
-  </listitem>
-
-  <listitem>
-    <para>Support for on-demand socket-based activation in the Nix
-    daemon with <command>systemd</command>.</para>
-  </listitem>
-
-  <listitem>
-    <para>Added a manpage for
-    <citerefentry><refentrytitle>nix.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
-  </listitem>
-
-  <listitem>
-    <para>When using the Nix daemon, the <option>-s</option> flag in
-    <command>nix-env -qa</command> is now much faster.</para>
-  </listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.10.xml b/third_party/nix/doc/manual/release-notes/rl-1.10.xml
deleted file mode 100644
index 689a954663..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.10.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.10">
-
-<title>Release 1.10 (2015-09-03)</title>
-
-<para>This is primarily a bug fix release. It also has a number of new
-features:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>A number of builtin functions have been added to reduce
-    Nixpkgs/NixOS evaluation time and memory consumption:
-    <function>all</function>,
-    <function>any</function>,
-    <function>concatStringsSep</function>,
-    <function>foldl’</function>,
-    <function>genList</function>,
-    <function>replaceStrings</function>,
-    <function>sort</function>.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The garbage collector is more robust when the disk is full.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix supports a new API for building derivations that doesn’t
-    require a <literal>.drv</literal> file to be present on disk; it
-    only requires an in-memory representation of the derivation. This
-    is used by the Hydra continuous build system to make remote builds
-    more efficient.</para>
-  </listitem>
-
-  <listitem>
-    <para>The function <literal>&lt;nix/fetchurl.nix></literal> now
-    uses a <emphasis>builtin</emphasis> builder (i.e. it doesn’t
-    require starting an external process; the download is performed by
-    Nix itself). This ensures that derivation paths don’t change when
-    Nix is upgraded, and obviates the need for ugly hacks to support
-    chroot execution.</para>
-  </listitem>
-
-  <listitem>
-    <para><option>--version -v</option> now prints some configuration
-    information, in particular what compile-time optional features are
-    enabled, and the paths of various directories.</para>
-  </listitem>
-
-  <listitem>
-    <para>Build users have their supplementary groups set correctly.</para>
-  </listitem>
-
-</itemizedlist>
-
-<para>This release has contributions from Eelco Dolstra, Guillaume
-Maudoux, Iwan Aucamp, Jaka Hudoklin, Kirill Elagin, Ludovic Courtès,
-Manolis Ragkousis, Nicolas B. Pierron and Shea Levy.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.11.10.xml b/third_party/nix/doc/manual/release-notes/rl-1.11.10.xml
deleted file mode 100644
index 415388b3e2..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.11.10.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.11.10">
-
-<title>Release 1.11.10 (2017-06-12)</title>
-
-<para>This release fixes a security bug in Nix’s β€œbuild user” build
-isolation mechanism. Previously, Nix builders had the ability to
-create setuid binaries owned by a <literal>nixbld</literal>
-user. Such a binary could then be used by an attacker to assume a
-<literal>nixbld</literal> identity and interfere with subsequent
-builds running under the same UID.</para>
-
-<para>To prevent this issue, Nix now disallows builders to create
-setuid and setgid binaries. On Linux, this is done using a seccomp BPF
-filter. Note that this imposes a small performance penalty (e.g. 1%
-when building GNU Hello). Using seccomp, we now also prevent the
-creation of extended attributes and POSIX ACLs since these cannot be
-represented in the NAR format and (in the case of POSIX ACLs) allow
-bypassing regular Nix store permissions. On macOS, the restriction is
-implemented using the existing sandbox mechanism, which now uses a
-minimal β€œallow all except the creation of setuid/setgid binaries”
-profile when regular sandboxing is disabled. On other platforms, the
-β€œbuild user” mechanism is now disabled.</para>
-
-<para>Thanks go to Linus Heckemann for discovering and reporting this
-bug.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.11.xml b/third_party/nix/doc/manual/release-notes/rl-1.11.xml
deleted file mode 100644
index fe422dd1f8..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.11.xml
+++ /dev/null
@@ -1,141 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.11">
-
-<title>Release 1.11 (2016-01-19)</title>
-
-<para>This is primarily a bug fix release. It also has a number of new
-features:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para><command>nix-prefetch-url</command> can now download URLs
-    specified in a Nix expression. For example,
-
-<screen>
-$ nix-prefetch-url -A hello.src
-</screen>
-
-    will prefetch the file specified by the
-    <function>fetchurl</function> call in the attribute
-    <literal>hello.src</literal> from the Nix expression in the
-    current directory, and print the cryptographic hash of the
-    resulting file on stdout. This differs from <literal>nix-build -A
-    hello.src</literal> in that it doesn't verify the hash, and is
-    thus useful when you’re updating a Nix expression.</para>
-
-    <para>You can also prefetch the result of functions that unpack a
-    tarball, such as <function>fetchFromGitHub</function>. For example:
-
-<screen>
-$ nix-prefetch-url --unpack https://github.com/NixOS/patchelf/archive/0.8.tar.gz
-</screen>
-
-    or from a Nix expression:
-
-<screen>
-$ nix-prefetch-url -A nix-repl.src
-</screen>
-
-    </para>
-
-  </listitem>
-
-  <listitem>
-    <para>The builtin function
-    <function>&lt;nix/fetchurl.nix></function> now supports
-    downloading and unpacking NARs. This removes the need to have
-    multiple downloads in the Nixpkgs stdenv bootstrap process (like a
-    separate busybox binary for Linux, or curl/mkdir/sh/bzip2 for
-    Darwin). Now all those files can be combined into a single NAR,
-    optionally compressed using <command>xz</command>.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now supports SHA-512 hashes for verifying fixed-output
-    derivations, and in <function>builtins.hashString</function>.</para>
-  </listitem>
-
-  <listitem>
-    <para>
-      The new flag <option>--option build-repeat
-      <replaceable>N</replaceable></option> will cause every build to
-      be executed <replaceable>N</replaceable>+1 times. If the build
-      output differs between any round, the build is rejected, and the
-      output paths are not registered as valid. This is primarily
-      useful to verify build determinism. (We already had a
-      <option>--check</option> option to repeat a previously succeeded
-      build. However, with <option>--check</option>, non-deterministic
-      builds are registered in the DB. Preventing that is useful for
-      Hydra to ensure that non-deterministic builds don't end up
-      getting published to the binary cache.)
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>
-      The options <option>--check</option> and <option>--option
-      build-repeat <replaceable>N</replaceable></option>, if they
-      detect a difference between two runs of the same derivation and
-      <option>-K</option> is given, will make the output of the other
-      run available under
-      <filename><replaceable>store-path</replaceable>-check</filename>. This
-      makes it easier to investigate the non-determinism using tools
-      like <command>diffoscope</command>, e.g.,
-
-<screen>
-$ nix-build pkgs/stdenv/linux -A stage1.pkgs.zlib --check -K
-error: derivation β€˜/nix/store/l54i8wlw2265…-zlib-1.2.8.drv’ may not
-be deterministic: output β€˜/nix/store/11a27shh6n2i…-zlib-1.2.8’
-differs from β€˜/nix/store/11a27shh6n2i…-zlib-1.2.8-check’
-
-$ diffoscope /nix/store/11a27shh6n2i…-zlib-1.2.8 /nix/store/11a27shh6n2i…-zlib-1.2.8-check
-…
-β”œβ”€β”€ lib/libz.a
-β”‚   β”œβ”€β”€ metadata
-β”‚   β”‚ @@ -1,15 +1,15 @@
-β”‚   β”‚ -rw-r--r-- 30001/30000   3096 Jan 12 15:20 2016 adler32.o
-…
-β”‚   β”‚ +rw-r--r-- 30001/30000   3096 Jan 12 15:28 2016 adler32.o
-…
-</screen>
-
-    </para></listitem>
-
-  <listitem>
-    <para>Improved FreeBSD support.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-env -qa --xml --meta</command> now prints
-    license information.</para>
-  </listitem>
-
-  <listitem>
-    <para>The maximum number of parallel TCP connections that the
-    binary cache substituter will use has been decreased from 150 to
-    25. This should prevent upsetting some broken NAT routers, and
-    also improves performance.</para>
-  </listitem>
-
-  <listitem>
-    <para>All "chroot"-containing strings got renamed to "sandbox".
-      In particular, some Nix options got renamed, but the old names
-      are still accepted as lower-priority aliases.
-    </para>
-  </listitem>
-
-</itemizedlist>
-
-<para>This release has contributions from Anders Claesson, Anthony
-Cowley, BjΓΈrn Forsman, Brian McKenna, Danny Wilson, davidak, Eelco Dolstra,
-Fabian Schmitthenner, FrankHB, Ilya Novoselov, janus, Jim Garrison, John
-Ericson, Jude Taylor, Ludovic Courtès, Manuel Jacob, Mathnerd314,
-Pascal Wittmann, Peter Simons, Philip Potter, Preston Bennes, Rommel
-M. Martinez, Sander van der Burg, Shea Levy, Tim Cuthbertson, Tuomas
-Tynkkynen, Utku Demir and Vladimír ČunÑt.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.2.xml b/third_party/nix/doc/manual/release-notes/rl-1.2.xml
deleted file mode 100644
index 748fd9e670..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.2.xml
+++ /dev/null
@@ -1,157 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.2">
-
-<title>Release 1.2 (2012-12-06)</title>
-
-<para>This release has the following improvements and changes:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>Nix has a new binary substituter mechanism: the
-    <emphasis>binary cache</emphasis>.  A binary cache contains
-    pre-built binaries of Nix packages.  Whenever Nix wants to build a
-    missing Nix store path, it will check a set of binary caches to
-    see if any of them has a pre-built binary of that path.  The
-    configuration setting <option>binary-caches</option> contains a
-    list of URLs of binary caches.  For instance, doing
-<screen>
-$ nix-env -i thunderbird --option binary-caches http://cache.nixos.org
-</screen>
-    will install Thunderbird and its dependencies, using the available
-    pre-built binaries in <uri>http://cache.nixos.org</uri>.
-    The main advantage over the old β€œmanifest”-based method of getting
-    pre-built binaries is that you don’t have to worry about your
-    manifest being in sync with the Nix expressions you’re installing
-    from; i.e., you don’t need to run <command>nix-pull</command> to
-    update your manifest.  It’s also more scalable because you don’t
-    need to redownload a giant manifest file every time.
-    </para>
-
-    <para>A Nix channel can provide a binary cache URL that will be
-    used automatically if you subscribe to that channel.  If you use
-    the Nixpkgs or NixOS channels
-    (<uri>http://nixos.org/channels</uri>) you automatically get the
-    cache <uri>http://cache.nixos.org</uri>.</para>
-
-    <para>Binary caches are created using <command>nix-push</command>.
-    For details on the operation and format of binary caches, see the
-    <command>nix-push</command> manpage.  More details are provided in
-    <link xlink:href="https://nixos.org/nix-dev/2012-September/009826.html">this
-    nix-dev posting</link>.</para>
-  </listitem>
-
-  <listitem>
-    <para>Multiple output support should now be usable.  A derivation
-    can declare that it wants to produce multiple store paths by
-    saying something like
-<programlisting>
-outputs = [ "lib" "headers" "doc" ];
-</programlisting>
-    This will cause Nix to pass the intended store path of each output
-    to the builder through the environment variables
-    <literal>lib</literal>, <literal>headers</literal> and
-    <literal>doc</literal>.  Other packages can refer to a specific
-    output by referring to
-    <literal><replaceable>pkg</replaceable>.<replaceable>output</replaceable></literal>,
-    e.g.
-<programlisting>
-buildInputs = [ pkg.lib pkg.headers ];
-</programlisting>
-    If you install a package with multiple outputs using
-    <command>nix-env</command>, each output path will be symlinked
-    into the user environment.</para>
-  </listitem>
-
-  <listitem>
-    <para>Dashes are now valid as part of identifiers and attribute
-    names.</para>
-  </listitem>
-
-  <listitem>
-    <para>The new operation <command>nix-store --repair-path</command>
-    allows corrupted or missing store paths to be repaired by
-    redownloading them.  <command>nix-store --verify --check-contents
-    --repair</command> will scan and repair all paths in the Nix
-    store.  Similarly, <command>nix-env</command>,
-    <command>nix-build</command>, <command>nix-instantiate</command>
-    and <command>nix-store --realise</command> have a
-    <option>--repair</option> flag to detect and fix bad paths by
-    rebuilding or redownloading them.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix no longer sets the immutable bit on files in the Nix
-    store.  Instead, the recommended way to guard the Nix store
-    against accidental modification on Linux is to make it a read-only
-    bind mount, like this:
-
-<screen>
-$ mount --bind /nix/store /nix/store
-$ mount -o remount,ro,bind /nix/store
-</screen>
-
-    Nix will automatically make <filename>/nix/store</filename>
-    writable as needed (using a private mount namespace) to allow
-    modifications.</para>
-  </listitem>
-
-  <listitem>
-    <para>Store optimisation (replacing identical files in the store
-    with hard links) can now be done automatically every time a path
-    is added to the store.  This is enabled by setting the
-    configuration option <literal>auto-optimise-store</literal> to
-    <literal>true</literal> (disabled by default).</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now supports <command>xz</command> compression for NARs
-    in addition to <command>bzip2</command>.  It compresses about 30%
-    better on typical archives and decompresses about twice as
-    fast.</para>
-  </listitem>
-
-  <listitem>
-    <para>Basic Nix expression evaluation profiling: setting the
-    environment variable <envar>NIX_COUNT_CALLS</envar> to
-    <literal>1</literal> will cause Nix to print how many times each
-    primop or function was executed.</para>
-  </listitem>
-
-  <listitem>
-    <para>New primops: <varname>concatLists</varname>,
-    <varname>elem</varname>, <varname>elemAt</varname> and
-    <varname>filter</varname>.</para>
-  </listitem>
-
-  <listitem>
-    <para>The command <command>nix-copy-closure</command> has a new
-    flag <option>--use-substitutes</option> (<option>-s</option>) to
-    download missing paths on the target machine using the substitute
-    mechanism.</para>
-  </listitem>
-
-  <listitem>
-    <para>The command <command>nix-worker</command> has been renamed
-    to <command>nix-daemon</command>.  Support for running the Nix
-    worker in β€œslave” mode has been removed.</para>
-  </listitem>
-
-  <listitem>
-    <para>The <option>--help</option> flag of every Nix command now
-    invokes <command>man</command>.</para>
-  </listitem>
-
-  <listitem>
-    <para>Chroot builds are now supported on systemd machines.</para>
-  </listitem>
-
-</itemizedlist>
-
-<para>This release has contributions from Eelco Dolstra, Florian
-Friesdorf, Mats Erik Andersson and Shea Levy.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.3.xml b/third_party/nix/doc/manual/release-notes/rl-1.3.xml
deleted file mode 100644
index e2009ee3ba..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.3.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.3">
-
-<title>Release 1.3 (2013-01-04)</title>
-
-<para>This is primarily a bug fix release.  When this version is first
-run on Linux, it removes any immutable bits from the Nix store and
-increases the schema version of the Nix store.  (The previous release
-removed support for setting the immutable bit; this release clears any
-remaining immutable bits to make certain operations more
-efficient.)</para>
-
-<para>This release has contributions from Eelco Dolstra and Stuart
-Pernsteiner.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.4.xml b/third_party/nix/doc/manual/release-notes/rl-1.4.xml
deleted file mode 100644
index aefb22f2b9..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.4.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.4">
-
-<title>Release 1.4 (2013-02-26)</title>
-
-<para>This release fixes a security bug in multi-user operation.  It
-was possible for derivations to cause the mode of files outside of the
-Nix store to be changed to 444 (read-only but world-readable) by
-creating hard links to those files (<link
-xlink:href="https://github.com/NixOS/nix/commit/5526a282b5b44e9296e61e07d7d2626a79141ac4">details</link>).</para>
-
-<para>There are also the following improvements:</para>
-
-<itemizedlist>
-
-  <listitem><para>New built-in function:
-  <function>builtins.hashString</function>.</para></listitem>
-
-  <listitem><para>Build logs are now stored in
-  <filename>/nix/var/log/nix/drvs/<replaceable>XX</replaceable>/</filename>,
-  where <replaceable>XX</replaceable> is the first two characters of
-  the derivation.  This is useful on machines that keep a lot of build
-  logs (such as Hydra servers).</para></listitem>
-
-  <listitem><para>The function <function>corepkgs/fetchurl</function>
-  can now make the downloaded file executable.  This will allow
-  getting rid of all bootstrap binaries in the Nixpkgs source
-  tree.</para></listitem>
-
-  <listitem><para>Language change: The expression <literal>"${./path}
-  ..."</literal> now evaluates to a string instead of a
-  path.</para></listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.5.1.xml b/third_party/nix/doc/manual/release-notes/rl-1.5.1.xml
deleted file mode 100644
index 035c8dbcbb..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.5.1.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.5.1">
-
-<title>Release 1.5.1 (2013-02-28)</title>
-
-<para>The bug fix to the bug fix had a bug itself, of course.  But
-this time it will work for sure!</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.5.2.xml b/third_party/nix/doc/manual/release-notes/rl-1.5.2.xml
deleted file mode 100644
index 7e81dd2432..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.5.2.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.5.2">
-
-<title>Release 1.5.2 (2013-05-13)</title>
-
-<para>This is primarily a bug fix release.  It has contributions from
-Eelco Dolstra, LluΓ­s Batlle i Rossell and Shea Levy.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.5.xml b/third_party/nix/doc/manual/release-notes/rl-1.5.xml
deleted file mode 100644
index 8e279d7693..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.5.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.5">
-
-<title>Release 1.5 (2013-02-27)</title>
-
-<para>This is a brown paper bag release to fix a regression introduced
-by the hard link security fix in 1.4.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.6.1.xml b/third_party/nix/doc/manual/release-notes/rl-1.6.1.xml
deleted file mode 100644
index 9ecc527347..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.6.1.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.6.1">
-
-<title>Release 1.6.1 (2013-10-28)</title>
-
-<para>This is primarily a bug fix release.  Changes of interest
-are:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>Nix 1.6 accidentally changed the semantics of antiquoted
-    paths in strings, such as <literal>"${/foo}/bar"</literal>.  This
-    release reverts to the Nix 1.5.3 behaviour.</para>
-  </listitem>
-
-  <listitem>
-    <para>Previously, Nix optimised expressions such as
-    <literal>"${<replaceable>expr</replaceable>}"</literal> to
-    <replaceable>expr</replaceable>.  Thus it neither checked whether
-    <replaceable>expr</replaceable> could be coerced to a string, nor
-    applied such coercions.  This meant that
-    <literal>"${123}"</literal> evaluatued to <literal>123</literal>,
-    and <literal>"${./foo}"</literal> evaluated to
-    <literal>./foo</literal> (even though
-    <literal>"${./foo} "</literal> evaluates to
-    <literal>"/nix/store/<replaceable>hash</replaceable>-foo "</literal>).
-    Nix now checks the type of antiquoted expressions and
-    applies coercions.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now shows the exact position of undefined variables.  In
-    particular, undefined variable errors in a <literal>with</literal>
-    previously didn't show <emphasis>any</emphasis> position
-    information, so this makes it a lot easier to fix such
-    errors.</para>
-  </listitem>
-
-  <listitem>
-    <para>Undefined variables are now treated consistently.
-    Previously, the <function>tryEval</function> function would catch
-    undefined variables inside a <literal>with</literal> but not
-    outside.  Now <function>tryEval</function> never catches undefined
-    variables.</para>
-  </listitem>
-
-  <listitem>
-    <para>Bash completion in <command>nix-shell</command> now works
-    correctly.</para>
-  </listitem>
-
-  <listitem>
-    <para>Stack traces are less verbose: they no longer show calls to
-    builtin functions and only show a single line for each derivation
-    on the call stack.</para>
-  </listitem>
-
-  <listitem>
-    <para>New built-in function: <function>builtins.typeOf</function>,
-    which returns the type of its argument as a string.</para>
-  </listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.6.xml b/third_party/nix/doc/manual/release-notes/rl-1.6.xml
deleted file mode 100644
index 5805634209..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.6.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.6.0">
-
-<title>Release 1.6 (2013-09-10)</title>
-
-<para>In addition to the usual bug fixes, this release has several new
-features:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>The command <command>nix-build --run-env</command> has been
-    renamed to <command>nix-shell</command>.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-shell</command> now sources
-    <filename>$stdenv/setup</filename> <emphasis>inside</emphasis> the
-    interactive shell, rather than in a parent shell.  This ensures
-    that shell functions defined by <literal>stdenv</literal> can be
-    used in the interactive shell.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-shell</command> has a new flag
-    <option>--pure</option> to clear the environment, so you get an
-    environment that more closely corresponds to the β€œreal” Nix build.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-shell</command> now sets the shell prompt
-    (<envar>PS1</envar>) to ensure that Nix shells are distinguishable
-    from your regular shells.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-env</command> no longer requires a
-    <literal>*</literal> argument to match all packages, so
-    <literal>nix-env -qa</literal> is equivalent to <literal>nix-env
-    -qa '*'</literal>.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-env -i</command> has a new flag
-    <option>--remove-all</option> (<option>-r</option>) to remove all
-    previous packages from the profile.  This makes it easier to do
-    declarative package management similar to NixOS’s
-    <option>environment.systemPackages</option>.  For instance, if you
-    have a specification <filename>my-packages.nix</filename> like this:
-
-<programlisting>
-with import &lt;nixpkgs> {};
-[ thunderbird
-  geeqie
-  ...
-]
-</programlisting>
-
-    then after any change to this file, you can run:
-
-<screen>
-$ nix-env -f my-packages.nix -ir
-</screen>
-
-    to update your profile to match the specification.</para>
-  </listitem>
-
-  <listitem>
-    <para>The β€˜<literal>with</literal>’ language construct is now more
-    lazy.  It only evaluates its argument if a variable might actually
-    refer to an attribute in the argument.  For instance, this now
-    works:
-
-<programlisting>
-let
-  pkgs = with pkgs; { foo = "old"; bar = foo; } // overrides;
-  overrides = { foo = "new"; };
-in pkgs.bar
-</programlisting>
-
-    This evaluates to <literal>"new"</literal>, while previously it
-    gave an β€œinfinite recursion” error.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now has proper integer arithmetic operators. For
-    instance, you can write <literal>x + y</literal> instead of
-    <literal>builtins.add x y</literal>, or <literal>x &lt;
-    y</literal> instead of <literal>builtins.lessThan x y</literal>.
-    The comparison operators also work on strings.</para>
-  </listitem>
-
-  <listitem>
-    <para>On 64-bit systems, Nix integers are now 64 bits rather than
-    32 bits.</para>
-  </listitem>
-
-  <listitem>
-    <para>When using the Nix daemon, the <command>nix-daemon</command>
-    worker process now runs on the same CPU as the client, on systems
-    that support setting CPU affinity.  This gives a significant speedup
-    on some systems.</para>
-  </listitem>
-
-  <listitem>
-    <para>If a stack overflow occurs in the Nix evaluator, you now get
-    a proper error message (rather than β€œSegmentation fault”) on some
-    systems.</para>
-  </listitem>
-
-  <listitem>
-    <para>In addition to directories, you can now bind-mount regular
-    files in chroots through the (now misnamed) option
-    <option>build-chroot-dirs</option>.</para>
-  </listitem>
-
-</itemizedlist>
-
-<para>This release has contributions from Domen KoΕΎar, Eelco Dolstra,
-Florian Friesdorf, Gergely Risko, Ivan Kozik, Ludovic Courtès and Shea
-Levy.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.7.xml b/third_party/nix/doc/manual/release-notes/rl-1.7.xml
deleted file mode 100644
index 44ecaa78da..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.7.xml
+++ /dev/null
@@ -1,263 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.7">
-
-<title>Release 1.7 (2014-04-11)</title>
-
-<para>In addition to the usual bug fixes, this release has the
-following new features:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>Antiquotation is now allowed inside of quoted attribute
-    names (e.g. <literal>set."${foo}"</literal>). In the case where
-    the attribute name is just a single antiquotation, the quotes can
-    be dropped (e.g. the above example can be written
-    <literal>set.${foo}</literal>). If an attribute name inside of a
-    set declaration evaluates to <literal>null</literal> (e.g.
-    <literal>{ ${null} = false; }</literal>), then that attribute is
-    not added to the set.</para>
-  </listitem>
-
-  <listitem>
-    <para>Experimental support for cryptographically signed binary
-    caches.  See <link
-    xlink:href="https://github.com/NixOS/nix/commit/0fdf4da0e979f992db75cc17376e455ddc5a96d8">the
-    commit for details</link>.</para>
-  </listitem>
-
-  <listitem>
-    <para>An experimental new substituter,
-    <command>download-via-ssh</command>, that fetches binaries from
-    remote machines via SSH.  Specifying the flags <literal>--option
-    use-ssh-substituter true --option ssh-substituter-hosts
-    <replaceable>user@hostname</replaceable></literal> will cause Nix
-    to download binaries from the specified machine, if it has
-    them.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-store -r</command> and
-    <command>nix-build</command> have a new flag,
-    <option>--check</option>, that builds a previously built
-    derivation again, and prints an error message if the output is not
-    exactly the same. This helps to verify whether a derivation is
-    truly deterministic.  For example:
-
-<screen>
-$ nix-build '&lt;nixpkgs>' -A patchelf
-<replaceable>…</replaceable>
-$ nix-build '&lt;nixpkgs>' -A patchelf --check
-<replaceable>…</replaceable>
-error: derivation `/nix/store/1ipvxs…-patchelf-0.6' may not be deterministic:
-  hash mismatch in output `/nix/store/4pc1dm…-patchelf-0.6.drv'
-</screen>
-
-    </para>
-
-  </listitem>
-
-  <listitem>
-    <para>The <command>nix-instantiate</command> flags
-    <option>--eval-only</option> and <option>--parse-only</option>
-    have been renamed to <option>--eval</option> and
-    <option>--parse</option>, respectively.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-instantiate</command>,
-    <command>nix-build</command> and <command>nix-shell</command> now
-    have a flag <option>--expr</option> (or <option>-E</option>) that
-    allows you to specify the expression to be evaluated as a command
-    line argument.  For instance, <literal>nix-instantiate --eval -E
-    '1 + 2'</literal> will print <literal>3</literal>.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-shell</command> improvements:</para>
-
-    <itemizedlist>
-
-      <listitem>
-        <para>It has a new flag, <option>--packages</option> (or
-        <option>-p</option>), that sets up a build environment
-        containing the specified packages from Nixpkgs. For example,
-        the command
-
-<screen>
-$ nix-shell -p sqlite xorg.libX11 hello
-</screen>
-
-        will start a shell in which the given packages are
-        present.</para>
-      </listitem>
-
-      <listitem>
-        <para>It now uses <filename>shell.nix</filename> as the
-        default expression, falling back to
-        <filename>default.nix</filename> if the former doesn’t
-        exist.  This makes it convenient to have a
-        <filename>shell.nix</filename> in your project to set up a
-        nice development environment.</para>
-      </listitem>
-
-      <listitem>
-        <para>It evaluates the derivation attribute
-        <varname>shellHook</varname>, if set. Since
-        <literal>stdenv</literal> does not normally execute this hook,
-        it allows you to do <command>nix-shell</command>-specific
-        setup.</para>
-      </listitem>
-
-      <listitem>
-        <para>It preserves the user’s timezone setting.</para>
-      </listitem>
-
-    </itemizedlist>
-
-  </listitem>
-
-  <listitem>
-    <para>In chroots, Nix now sets up a <filename>/dev</filename>
-    containing only a minimal set of devices (such as
-    <filename>/dev/null</filename>). Note that it only does this if
-    you <emphasis>don’t</emphasis> have <filename>/dev</filename>
-    listed in your <option>build-chroot-dirs</option> setting;
-    otherwise, it will bind-mount the <literal>/dev</literal> from
-    outside the chroot.</para>
-
-    <para>Similarly, if you don’t have <filename>/dev/pts</filename> listed
-    in <option>build-chroot-dirs</option>, Nix will mount a private
-    <literal>devpts</literal> filesystem on the chroot’s
-    <filename>/dev/pts</filename>.</para>
-
-  </listitem>
-
-  <listitem>
-    <para>New built-in function: <function>builtins.toJSON</function>,
-    which returns a JSON representation of a value.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-env -q</command> has a new flag
-    <option>--json</option> to print a JSON representation of the
-    installed or available packages.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-env</command> now supports meta attributes with
-    more complex values, such as attribute sets.</para>
-  </listitem>
-
-  <listitem>
-    <para>The <option>-A</option> flag now allows attribute names with
-    dots in them, e.g.
-
-<screen>
-$ nix-instantiate --eval '&lt;nixos>' -A 'config.systemd.units."nscd.service".text'
-</screen>
-
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The <option>--max-freed</option> option to
-    <command>nix-store --gc</command> now accepts a unit
-    specifier. For example, <literal>nix-store --gc --max-freed
-    1G</literal> will free up to 1 gigabyte of disk space.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-collect-garbage</command> has a new flag
-    <option>--delete-older-than</option>
-    <replaceable>N</replaceable><literal>d</literal>, which deletes
-    all user environment generations older than
-    <replaceable>N</replaceable> days.  Likewise, <command>nix-env
-    --delete-generations</command> accepts a
-    <replaceable>N</replaceable><literal>d</literal> age limit.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now heuristically detects whether a build failure was
-    due to a disk-full condition. In that case, the build is not
-    flagged as β€œpermanently failed”. This is mostly useful for Hydra,
-    which needs to distinguish between permanent and transient build
-    failures.</para>
-  </listitem>
-
-  <listitem>
-    <para>There is a new symbol <literal>__curPos</literal> that
-    expands to an attribute set containing its file name and line and
-    column numbers, e.g. <literal>{ file = "foo.nix"; line = 10;
-    column = 5; }</literal>.  There also is a new builtin function,
-    <varname>unsafeGetAttrPos</varname>, that returns the position of
-    an attribute.  This is used by Nixpkgs to provide location
-    information in error messages, e.g.
-
-<screen>
-$ nix-build '&lt;nixpkgs>' -A libreoffice --argstr system x86_64-darwin
-error: the package β€˜libreoffice-4.0.5.2’ in β€˜.../applications/office/libreoffice/default.nix:263’
-  is not supported on β€˜x86_64-darwin’
-</screen>
-
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The garbage collector is now more concurrent with other Nix
-    processes because it releases certain locks earlier.</para>
-  </listitem>
-
-  <listitem>
-    <para>The binary tarball installer has been improved.  You can now
-    install Nix by running:
-
-<screen>
-$ bash &lt;(curl https://nixos.org/nix/install)
-</screen>
-
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>More evaluation errors include position information. For
-    instance, selecting a missing attribute will print something like
-
-<screen>
-error: attribute `nixUnstabl' missing, at /etc/nixos/configurations/misc/eelco/mandark.nix:216:15
-</screen>
-
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The command <command>nix-setuid-helper</command> is
-    gone.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix no longer uses Automake, but instead has a
-    non-recursive, GNU Make-based build system.</para>
-  </listitem>
-
-  <listitem>
-    <para>All installed libraries now have the prefix
-    <literal>libnix</literal>.  In particular, this gets rid of
-    <literal>libutil</literal>, which could clash with libraries with
-    the same name from other packages.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now requires a compiler that supports C++11.</para>
-  </listitem>
-
-</itemizedlist>
-
-<para>This release has contributions from Danny Wilson, Domen KoΕΎar,
-Eelco Dolstra, Ian-Woo Kim, Ludovic Courtès, Maxim Ivanov, Petr
-Rockai, Ricardo M. Correia and Shea Levy.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.8.xml b/third_party/nix/doc/manual/release-notes/rl-1.8.xml
deleted file mode 100644
index c854c5c5f8..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.8.xml
+++ /dev/null
@@ -1,123 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.8">
-
-<title>Release 1.8 (2014-12-14)</title>
-
-<itemizedlist>
-
-  <listitem><para>Breaking change: to address a race condition, the
-  remote build hook mechanism now uses <command>nix-store
-  --serve</command> on the remote machine. This requires build slaves
-  to be updated to Nix 1.8.</para></listitem>
-
-  <listitem><para>Nix now uses HTTPS instead of HTTP to access the
-  default binary cache,
-  <literal>cache.nixos.org</literal>.</para></listitem>
-
-  <listitem><para><command>nix-env</command> selectors are now regular
-  expressions. For instance, you can do
-
-<screen>
-$ nix-env -qa '.*zip.*'
-</screen>
-
-  to query all packages with a name containing
-  <literal>zip</literal>.</para></listitem>
-
-  <listitem><para><command>nix-store --read-log</command> can now
-  fetch remote build logs. If a build log is not available locally,
-  then β€˜nix-store -l’ will now try to download it from the servers
-  listed in the β€˜log-servers’ option in nix.conf. For instance, if you
-  have the configuration option
-
-<programlisting>
-log-servers = http://hydra.nixos.org/log
-</programlisting>
-
-then it will try to get logs from
-<literal>http://hydra.nixos.org/log/<replaceable>base name of the
-store path</replaceable></literal>. This allows you to do things like:
-
-<screen>
-$ nix-store -l $(which xterm)
-</screen>
-
-  and get a log even if <command>xterm</command> wasn't built
-  locally.</para></listitem>
-
-  <listitem><para>New builtin functions:
-  <function>attrValues</function>, <function>deepSeq</function>,
-  <function>fromJSON</function>, <function>readDir</function>,
-  <function>seq</function>.</para></listitem>
-
-  <listitem><para><command>nix-instantiate --eval</command> now has a
-  <option>--json</option> flag to print the resulting value in JSON
-  format.</para></listitem>
-
-  <listitem><para><command>nix-copy-closure</command> now uses
-  <command>nix-store --serve</command> on the remote side to send or
-  receive closures. This fixes a race condition between
-  <command>nix-copy-closure</command> and the garbage
-  collector.</para></listitem>
-
-  <listitem><para>Derivations can specify the new special attribute
-  <varname>allowedRequisites</varname>, which has a similar meaning to
-  <varname>allowedReferences</varname>. But instead of only enforcing
-  to explicitly specify the immediate references, it requires the
-  derivation to specify all the dependencies recursively (hence the
-  name, requisites) that are used by the resulting
-  output.</para></listitem>
-
-  <listitem><para>On Mac OS X, Nix now handles case collisions when
-  importing closures from case-sensitive file systems. This is mostly
-  useful for running NixOps on Mac OS X.</para></listitem>
-
-  <listitem><para>The Nix daemon has new configuration options
-  <option>allowed-users</option> (specifying the users and groups that
-  are allowed to connect to the daemon) and
-  <option>trusted-users</option> (specifying the users and groups that
-  can perform privileged operations like specifying untrusted binary
-  caches).</para></listitem>
-
-  <listitem><para>The configuration option
-  <option>build-cores</option> now defaults to the number of available
-  CPU cores.</para></listitem>
-
-  <listitem><para>Build users are now used by default when Nix is
-  invoked as root. This prevents builds from accidentally running as
-  root.</para></listitem>
-
-  <listitem><para>Nix now includes systemd units and Upstart
-  jobs.</para></listitem>
-
-  <listitem><para>Speed improvements to <command>nix-store
-  --optimise</command>.</para></listitem>
-
-  <listitem><para>Language change: the <literal>==</literal> operator
-  now ignores string contexts (the β€œdependencies” of a
-  string).</para></listitem>
-
-  <listitem><para>Nix now filters out Nix-specific ANSI escape
-  sequences on standard error. They are supposed to be invisible, but
-  some terminals show them anyway.</para></listitem>
-
-  <listitem><para>Various commands now automatically pipe their output
-  into the pager as specified by the <envar>PAGER</envar> environment
-  variable.</para></listitem>
-
-  <listitem><para>Several improvements to reduce memory consumption in
-  the evaluator.</para></listitem>
-
-</itemizedlist>
-
-<para>This release has contributions from Adam Szkoda, Aristid
-Breitkreuz, Bob van der Linden, Charles Strahan, darealshinji, Eelco
-Dolstra, Gergely Risko, Joel Taylor, Ludovic Courtès, Marko Durkovic,
-Mikey Ariel, Paul Colomiets, Ricardo M.  Correia, Ricky Elrod, Robert
-Helgesson, Rob Vermaas, Russell O'Connor, Shea Levy, Shell Turner,
-Sânke Hahn, Steve Purcell, Vladimír ČunÑt and Wout Mertens.</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-1.9.xml b/third_party/nix/doc/manual/release-notes/rl-1.9.xml
deleted file mode 100644
index c8406bd207..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-1.9.xml
+++ /dev/null
@@ -1,216 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-1.9">
-
-<title>Release 1.9 (2015-06-12)</title>
-
-<para>In addition to the usual bug fixes, this release has the
-following new features:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>Signed binary cache support. You can enable signature
-    checking by adding the following to <filename>nix.conf</filename>:
-
-<programlisting>
-signed-binary-caches = *
-binary-cache-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
-</programlisting>
-
-    This will prevent Nix from downloading any binary from the cache
-    that is not signed by one of the keys listed in
-    <option>binary-cache-public-keys</option>.</para>
-
-    <para>Signature checking is only supported if you built Nix with
-    the <literal>libsodium</literal> package.</para>
-
-    <para>Note that while Nix has had experimental support for signed
-    binary caches since version 1.7, this release changes the
-    signature format in a backwards-incompatible way.</para>
-
-  </listitem>
-
-  <listitem>
-
-    <para>Automatic downloading of Nix expression tarballs. In various
-    places, you can now specify the URL of a tarball containing Nix
-    expressions (such as Nixpkgs), which will be downloaded and
-    unpacked automatically. For example:</para>
-
-    <itemizedlist>
-
-      <listitem><para>In <command>nix-env</command>:
-
-<screen>
-$ nix-env -f https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz -iA firefox
-</screen>
-
-      This installs Firefox from the latest tested and built revision
-      of the NixOS 14.12 channel.</para></listitem>
-
-      <listitem><para>In <command>nix-build</command> and
-      <command>nix-shell</command>:
-
-<screen>
-$ nix-build https://github.com/NixOS/nixpkgs/archive/master.tar.gz -A hello
-</screen>
-
-      This builds GNU Hello from the latest revision of the Nixpkgs
-      master branch.</para></listitem>
-
-      <listitem><para>In the Nix search path (as specified via
-      <envar>NIX_PATH</envar> or <option>-I</option>). For example, to
-      start a shell containing the Pan package from a specific version
-      of Nixpkgs:
-
-<screen>
-$ nix-shell -p pan -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/8a3eea054838b55aca962c3fbde9c83c102b8bf2.tar.gz
-</screen>
-
-      </para></listitem>
-
-      <listitem><para>In <command>nixos-rebuild</command> (on NixOS):
-
-<screen>
-$ nixos-rebuild test -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-unstable.tar.gz
-</screen>
-
-      </para></listitem>
-
-      <listitem><para>In Nix expressions, via the new builtin function <function>fetchTarball</function>:
-
-<programlisting>
-with import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz) {}; …
-</programlisting>
-
-      (This is not allowed in restricted mode.)</para></listitem>
-
-    </itemizedlist>
-
-  </listitem>
-
-  <listitem>
-
-    <para><command>nix-shell</command> improvements:</para>
-
-    <itemizedlist>
-
-      <listitem><para><command>nix-shell</command> now has a flag
-      <option>--run</option> to execute a command in the
-      <command>nix-shell</command> environment,
-      e.g. <literal>nix-shell --run make</literal>. This is like
-      the existing <option>--command</option> flag, except that it
-      uses a non-interactive shell (ensuring that hitting Ctrl-C won’t
-      drop you into the child shell).</para></listitem>
-
-      <listitem><para><command>nix-shell</command> can now be used as
-      a <literal>#!</literal>-interpreter. This allows you to write
-      scripts that dynamically fetch their own dependencies. For
-      example, here is a Haskell script that, when invoked, first
-      downloads GHC and the Haskell packages on which it depends:
-
-<programlisting>
-#! /usr/bin/env nix-shell
-#! nix-shell -i runghc -p haskellPackages.ghc haskellPackages.HTTP
-
-import Network.HTTP
-
-main = do
-  resp &lt;- Network.HTTP.simpleHTTP (getRequest "http://nixos.org/")
-  body &lt;- getResponseBody resp
-  print (take 100 body)
-</programlisting>
-
-      Of course, the dependencies are cached in the Nix store, so the
-      second invocation of this script will be much
-      faster.</para></listitem>
-
-    </itemizedlist>
-
-  </listitem>
-
-  <listitem>
-
-    <para>Chroot improvements:</para>
-
-    <itemizedlist>
-
-      <listitem><para>Chroot builds are now supported on Mac OS X
-      (using its sandbox mechanism).</para></listitem>
-
-      <listitem><para>If chroots are enabled, they are now used for
-      all derivations, including fixed-output derivations (such as
-      <function>fetchurl</function>). The latter do have network
-      access, but can no longer access the host filesystem. If you
-      need the old behaviour, you can set the option
-      <option>build-use-chroot</option> to
-      <literal>relaxed</literal>.</para></listitem>
-
-      <listitem><para>On Linux, if chroots are enabled, builds are
-      performed in a private PID namespace once again. (This
-      functionality was lost in Nix 1.8.)</para></listitem>
-
-      <listitem><para>Store paths listed in
-      <option>build-chroot-dirs</option> are now automatically
-      expanded to their closure. For instance, if you want
-      <filename>/nix/store/…-bash/bin/sh</filename> mounted in your
-      chroot as <filename>/bin/sh</filename>, you only need to say
-      <literal>build-chroot-dirs =
-      /bin/sh=/nix/store/…-bash/bin/sh</literal>; it is no longer
-      necessary to specify the dependencies of Bash.</para></listitem>
-
-    </itemizedlist>
-
-  </listitem>
-
-  <listitem><para>The new derivation attribute
-  <varname>passAsFile</varname> allows you to specify that the
-  contents of derivation attributes should be passed via files rather
-  than environment variables. This is useful if you need to pass very
-  long strings that exceed the size limit of the environment. The
-  Nixpkgs function <function>writeTextFile</function> uses
-  this.</para></listitem>
-
-  <listitem><para>You can now use <literal>~</literal> in Nix file
-  names to refer to your home directory, e.g. <literal>import
-  ~/.nixpkgs/config.nix</literal>.</para></listitem>
-
-  <listitem><para>Nix has a new option <option>restrict-eval</option>
-  that allows limiting what paths the Nix evaluator has access to. By
-  passing <literal>--option restrict-eval true</literal> to Nix, the
-  evaluator will throw an exception if an attempt is made to access
-  any file outside of the Nix search path. This is primarily intended
-  for Hydra to ensure that a Hydra jobset only refers to its declared
-  inputs (and is therefore reproducible).</para></listitem>
-
-  <listitem><para><command>nix-env</command> now only creates a new
-  β€œgeneration” symlink in <filename>/nix/var/nix/profiles</filename>
-  if something actually changed.</para></listitem>
-
-  <listitem><para>The environment variable <envar>NIX_PAGER</envar>
-  can now be set to override <envar>PAGER</envar>. You can set it to
-  <literal>cat</literal> to disable paging for Nix commands
-  only.</para></listitem>
-
-  <listitem><para>Failing <literal>&lt;...></literal>
-  lookups now show position information.</para></listitem>
-
-  <listitem><para>Improved Boehm GC use: we disabled scanning for
-  interior pointers, which should reduce the β€œ<literal>Repeated
-  allocation of very large block</literal>” warnings and associated
-  retention of memory.</para></listitem>
-
-</itemizedlist>
-
-<para>This release has contributions from aszlig, Benjamin Staffin,
-Charles Strahan, Christian Theune, Daniel Hahler, Danylo Hlynskyi
-Daniel Peebles, Dan Peebles, Domen KoΕΎar, Eelco Dolstra, Harald van
-Dijk, Hoang Xuan Phu, Jaka Hudoklin, Jeff Ramnani, j-keck, Linquize,
-Luca Bruno, Michael Merickel, Oliver Dunkl, Rob Vermaas, Rok Garbas,
-Shea Levy, Tobias Geerinckx-Rice and William A. Kennington III.</para>
-
-</section>
-
diff --git a/third_party/nix/doc/manual/release-notes/rl-2.0.xml b/third_party/nix/doc/manual/release-notes/rl-2.0.xml
deleted file mode 100644
index fc9a77b08b..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-2.0.xml
+++ /dev/null
@@ -1,1012 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-2.0">
-
-<title>Release 2.0 (2018-02-22)</title>
-
-<para>The following incompatible changes have been made:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>The manifest-based substituter mechanism
-    (<command>download-using-manifests</command>) has been <link
-    xlink:href="https://github.com/NixOS/nix/commit/867967265b80946dfe1db72d40324b4f9af988ed">removed</link>. It
-    has been superseded by the binary cache substituter mechanism
-    since several years. As a result, the following programs have been
-    removed:
-
-    <itemizedlist>
-      <listitem><para><command>nix-pull</command></para></listitem>
-      <listitem><para><command>nix-generate-patches</command></para></listitem>
-      <listitem><para><command>bsdiff</command></para></listitem>
-      <listitem><para><command>bspatch</command></para></listitem>
-    </itemizedlist>
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The β€œcopy from other stores” substituter mechanism
-    (<command>copy-from-other-stores</command> and the
-    <envar>NIX_OTHER_STORES</envar> environment variable) has been
-    removed. It was primarily used by the NixOS installer to copy
-    available paths from the installation medium. The replacement is
-    to use a chroot store as a substituter
-    (e.g. <literal>--substituters /mnt</literal>), or to build into a
-    chroot store (e.g. <literal>--store /mnt --substituters /</literal>).</para>
-  </listitem>
-
-  <listitem>
-    <para>The command <command>nix-push</command> has been removed as
-    part of the effort to eliminate Nix's dependency on Perl. You can
-    use <command>nix copy</command> instead, e.g. <literal>nix copy
-    --to file:///tmp/my-binary-cache <replaceable>paths…</replaceable></literal></para>
-  </listitem>
-
-  <listitem>
-    <para>The β€œnested” log output feature (<option>--log-type
-    pretty</option>) has been removed. As a result,
-    <command>nix-log2xml</command> was also removed.</para>
-  </listitem>
-
-  <listitem>
-    <para>OpenSSL-based signing has been <link
-    xlink:href="https://github.com/NixOS/nix/commit/f435f8247553656774dd1b2c88e9de5d59cab203">removed</link>. This
-    feature was never well-supported. A better alternative is provided
-    by the <option>secret-key-files</option> and
-    <option>trusted-public-keys</option> options.</para>
-  </listitem>
-
-  <listitem>
-    <para>Failed build caching has been <link
-    xlink:href="https://github.com/NixOS/nix/commit/8cffec84859cec8b610a2a22ab0c4d462a9351ff">removed</link>. This
-    feature was introduced to support the Hydra continuous build
-    system, but Hydra no longer uses it.</para>
-  </listitem>
-
-  <listitem>
-    <para><filename>nix-mode.el</filename> has been removed from
-    Nix. It is now <link
-    xlink:href="https://github.com/NixOS/nix-mode">a separate
-    repository</link> and can be installed through the MELPA package
-    repository.</para>
-  </listitem>
-
-</itemizedlist>
-
-<para>This release has the following new features:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>It introduces a new command named <command>nix</command>,
-    which is intended to eventually replace all
-    <command>nix-*</command> commands with a more consistent and
-    better designed user interface. It currently provides replacements
-    for some (but not all) of the functionality provided by
-    <command>nix-store</command>, <command>nix-build</command>,
-    <command>nix-shell -p</command>, <command>nix-env -qa</command>,
-    <command>nix-instantiate --eval</command>,
-    <command>nix-push</command> and
-    <command>nix-copy-closure</command>. It has the following major
-    features:</para>
-
-    <itemizedlist>
-
-      <listitem>
-        <para>Unlike the legacy commands, it has a consistent way to
-        refer to packages and package-like arguments (like store
-        paths). For example, the following commands all copy the GNU
-        Hello package to a remote machine:
-
-        <screen>nix copy --to ssh://machine nixpkgs.hello</screen>
-        <screen>nix copy --to ssh://machine /nix/store/0i2jd68mp5g6h2sa5k9c85rb80sn8hi9-hello-2.10</screen>
-        <screen>nix copy --to ssh://machine '(with import &lt;nixpkgs> {}; hello)'</screen>
-
-        By contrast, <command>nix-copy-closure</command> only accepted
-        store paths as arguments.</para>
-      </listitem>
-
-      <listitem>
-        <para>It is self-documenting: <option>--help</option> shows
-        all available command-line arguments. If
-        <option>--help</option> is given after a subcommand, it shows
-        examples for that subcommand. <command>nix
-        --help-config</command> shows all configuration
-        options.</para>
-      </listitem>
-
-      <listitem>
-        <para>It is much less verbose. By default, it displays a
-        single-line progress indicator that shows how many packages
-        are left to be built or downloaded, and (if there are running
-        builds) the most recent line of builder output. If a build
-        fails, it shows the last few lines of builder output. The full
-        build log can be retrieved using <command>nix
-        log</command>.</para>
-      </listitem>
-
-      <listitem>
-        <para>It <link
-        xlink:href="https://github.com/NixOS/nix/commit/b8283773bd64d7da6859ed520ee19867742a03ba">provides</link>
-        all <filename>nix.conf</filename> configuration options as
-        command line flags. For example, instead of <literal>--option
-        http-connections 100</literal> you can write
-        <literal>--http-connections 100</literal>. Boolean options can
-        be written as
-        <literal>--<replaceable>foo</replaceable></literal> or
-        <literal>--no-<replaceable>foo</replaceable></literal>
-        (e.g. <option>--no-auto-optimise-store</option>).</para>
-      </listitem>
-
-      <listitem>
-        <para>Many subcommands have a <option>--json</option> flag to
-        write results to stdout in JSON format.</para>
-      </listitem>
-
-    </itemizedlist>
-
-    <warning><para>Please note that the <command>nix</command> command
-    is a work in progress and the interface is subject to
-    change.</para></warning>
-
-    <para>It provides the following high-level (β€œporcelain”)
-    subcommands:</para>
-
-    <itemizedlist>
-
-      <listitem>
-        <para><command>nix build</command> is a replacement for
-        <command>nix-build</command>.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix run</command> executes a command in an
-        environment in which the specified packages are available. It
-        is (roughly) a replacement for <command>nix-shell
-        -p</command>. Unlike that command, it does not execute the
-        command in a shell, and has a flag (<command>-c</command>)
-        that specifies the unquoted command line to be
-        executed.</para>
-
-        <para>It is particularly useful in conjunction with chroot
-        stores, allowing Linux users who do not have permission to
-        install Nix in <command>/nix/store</command> to still use
-        binary substitutes that assume
-        <command>/nix/store</command>. For example,
-
-        <screen>nix run --store ~/my-nix nixpkgs.hello -c hello --greeting 'Hi everybody!'</screen>
-
-        downloads (or if not substitutes are available, builds) the
-        GNU Hello package into
-        <filename>~/my-nix/nix/store</filename>, then runs
-        <command>hello</command> in a mount namespace where
-        <filename>~/my-nix/nix/store</filename> is mounted onto
-        <command>/nix/store</command>.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix search</command> replaces <command>nix-env
-        -qa</command>. It searches the available packages for
-        occurrences of a search string in the attribute name, package
-        name or description. Unlike <command>nix-env -qa</command>, it
-        has a cache to speed up subsequent searches.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix copy</command> copies paths between
-        arbitrary Nix stores, generalising
-        <command>nix-copy-closure</command> and
-        <command>nix-push</command>.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix repl</command> replaces the external
-        program <command>nix-repl</command>. It provides an
-        interactive environment for evaluating and building Nix
-        expressions. Note that it uses <literal>linenoise-ng</literal>
-        instead of GNU Readline.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix upgrade-nix</command> upgrades Nix to the
-        latest stable version. This requires that Nix is installed in
-        a profile. (Thus it won’t work on NixOS, or if it’s installed
-        outside of the Nix store.)</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix verify</command> checks whether store paths
-        are unmodified and/or β€œtrusted” (see below). It replaces
-        <command>nix-store --verify</command> and <command>nix-store
-        --verify-path</command>.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix log</command> shows the build log of a
-        package or path. If the build log is not available locally, it
-        will try to obtain it from the configured substituters (such
-        as <uri>cache.nixos.org</uri>, which now provides build
-        logs).</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix edit</command> opens the source code of a
-        package in your editor.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix eval</command> replaces
-        <command>nix-instantiate --eval</command>.</para>
-      </listitem>
-
-      <listitem>
-        <para><command
-        xlink:href="https://github.com/NixOS/nix/commit/d41c5eb13f4f3a37d80dbc6d3888644170c3b44a">nix
-        why-depends</command> shows why one store path has another in
-        its closure. This is primarily useful to finding the causes of
-        closure bloat. For example,
-
-        <screen>nix why-depends nixpkgs.vlc nixpkgs.libdrm.dev</screen>
-
-        shows a chain of files and fragments of file contents that
-        cause the VLC package to have the β€œdev” output of
-        <literal>libdrm</literal> in its closure β€” an undesirable
-        situation.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix path-info</command> shows information about
-        store paths, replacing <command>nix-store -q</command>. A
-        useful feature is the option <option>--closure-size</option>
-        (<option>-S</option>). For example, the following command show
-        the closure sizes of every path in the current NixOS system
-        closure, sorted by size:
-
-        <screen>nix path-info -rS /run/current-system | sort -nk2</screen>
-
-        </para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix optimise-store</command> replaces
-        <command>nix-store --optimise</command>. The main difference
-        is that it has a progress indicator.</para>
-      </listitem>
-
-    </itemizedlist>
-
-    <para>A number of low-level (β€œplumbing”) commands are also
-    available:</para>
-
-    <itemizedlist>
-
-      <listitem>
-        <para><command>nix ls-store</command> and <command>nix
-        ls-nar</command> list the contents of a store path or NAR
-        file. The former is primarily useful in conjunction with
-        remote stores, e.g.
-
-        <screen>nix ls-store --store https://cache.nixos.org/ -lR /nix/store/0i2jd68mp5g6h2sa5k9c85rb80sn8hi9-hello-2.10</screen>
-
-        lists the contents of path in a binary cache.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix cat-store</command> and <command>nix
-        cat-nar</command> allow extracting a file from a store path or
-        NAR file.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix dump-path</command> writes the contents of
-        a store path to stdout in NAR format. This replaces
-        <command>nix-store --dump</command>.</para>
-      </listitem>
-
-      <listitem>
-        <para><command
-        xlink:href="https://github.com/NixOS/nix/commit/e8d6ee7c1b90a2fe6d824f1a875acc56799ae6e2">nix
-        show-derivation</command> displays a store derivation in JSON
-        format. This is an alternative to
-        <command>pp-aterm</command>.</para>
-      </listitem>
-
-      <listitem>
-        <para><command
-        xlink:href="https://github.com/NixOS/nix/commit/970366266b8df712f5f9cedb45af183ef5a8357f">nix
-        add-to-store</command> replaces <command>nix-store
-        --add</command>.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix sign-paths</command> signs store
-        paths.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix copy-sigs</command> copies signatures from
-        one store to another.</para>
-      </listitem>
-
-      <listitem>
-        <para><command>nix show-config</command> shows all
-        configuration options and their current values.</para>
-      </listitem>
-
-    </itemizedlist>
-
-  </listitem>
-
-  <listitem>
-    <para>The store abstraction that Nix has had for a long time to
-    support store access via the Nix daemon has been extended
-    significantly. In particular, substituters (which used to be
-    external programs such as
-    <command>download-from-binary-cache</command>) are now subclasses
-    of the abstract <classname>Store</classname> class. This allows
-    many Nix commands to operate on such store types. For example,
-    <command>nix path-info</command> shows information about paths in
-    your local Nix store, while <command>nix path-info --store
-    https://cache.nixos.org/</command> shows information about paths
-    in the specified binary cache. Similarly,
-    <command>nix-copy-closure</command>, <command>nix-push</command>
-    and substitution are all instances of the general notion of
-    copying paths between different kinds of Nix stores.</para>
-
-    <para>Stores are specified using an URI-like syntax,
-    e.g. <uri>https://cache.nixos.org/</uri> or
-    <uri>ssh://machine</uri>. The following store types are supported:
-
-    <itemizedlist>
-
-      <listitem>
-
-        <para><classname>LocalStore</classname> (stori URI
-        <literal>local</literal> or an absolute path) and the misnamed
-        <classname>RemoteStore</classname> (<literal>daemon</literal>)
-        provide access to a local Nix store, the latter via the Nix
-        daemon. You can use <literal>auto</literal> or the empty
-        string to auto-select a local or daemon store depending on
-        whether you have write permission to the Nix store. It is no
-        longer necessary to set the <envar>NIX_REMOTE</envar>
-        environment variable to use the Nix daemon.</para>
-
-        <para>As noted above, <classname>LocalStore</classname> now
-        supports chroot builds, allowing the β€œphysical” location of
-        the Nix store
-        (e.g. <filename>/home/alice/nix/store</filename>) to differ
-        from its β€œlogical” location (typically
-        <filename>/nix/store</filename>). This allows non-root users
-        to use Nix while still getting the benefits from prebuilt
-        binaries from <uri>cache.nixos.org</uri>.</para>
-
-      </listitem>
-
-      <listitem>
-
-        <para><classname>BinaryCacheStore</classname> is the abstract
-        superclass of all binary cache stores. It supports writing
-        build logs and NAR content listings in JSON format.</para>
-
-      </listitem>
-
-      <listitem>
-
-        <para><classname>HttpBinaryCacheStore</classname>
-        (<literal>http://</literal>, <literal>https://</literal>)
-        supports binary caches via HTTP or HTTPS. If the server
-        supports <literal>PUT</literal> requests, it supports
-        uploading store paths via commands such as <command>nix
-        copy</command>.</para>
-
-      </listitem>
-
-      <listitem>
-
-        <para><classname>LocalBinaryCacheStore</classname>
-        (<literal>file://</literal>) supports binary caches in the
-        local filesystem.</para>
-
-      </listitem>
-
-      <listitem>
-
-        <para><classname>S3BinaryCacheStore</classname>
-        (<literal>s3://</literal>) supports binary caches stored in
-        Amazon S3, if enabled at compile time.</para>
-
-      </listitem>
-
-      <listitem>
-
-        <para><classname>LegacySSHStore</classname> (<literal>ssh://</literal>)
-        is used to implement remote builds and
-        <command>nix-copy-closure</command>.</para>
-
-      </listitem>
-
-      <listitem>
-
-        <para><classname>SSHStore</classname>
-        (<literal>ssh-ng://</literal>) supports arbitrary Nix
-        operations on a remote machine via the same protocol used by
-        <command>nix-daemon</command>.</para>
-
-      </listitem>
-
-    </itemizedlist>
-
-    </para>
-
-  </listitem>
-
-  <listitem>
-
-    <para>Security has been improved in various ways:
-
-    <itemizedlist>
-
-      <listitem>
-        <para>Nix now stores signatures for local store
-        paths. When paths are copied between stores (e.g., copied from
-        a binary cache to a local store), signatures are
-        propagated.</para>
-
-        <para>Locally-built paths are signed automatically using the
-        secret keys specified by the <option>secret-key-files</option>
-        store option. Secret/public key pairs can be generated using
-        <command>nix-store
-        --generate-binary-cache-key</command>.</para>
-
-        <para>In addition, locally-built store paths are marked as
-        β€œultimately trusted”, but this bit is not propagated when
-        paths are copied between stores.</para>
-      </listitem>
-
-      <listitem>
-        <para>Content-addressable store paths no longer require
-        signatures β€” they can be imported into a store by unprivileged
-        users even if they lack signatures.</para>
-      </listitem>
-
-      <listitem>
-        <para>The command <command>nix verify</command> checks whether
-        the specified paths are trusted, i.e., have a certain number
-        of trusted signatures, are ultimately trusted, or are
-        content-addressed.</para>
-      </listitem>
-
-      <listitem>
-        <para>Substitutions from binary caches <link
-        xlink:href="https://github.com/NixOS/nix/commit/ecbc3fedd3d5bdc5a0e1a0a51b29062f2874ac8b">now</link>
-        require signatures by default. This was already the case on
-        NixOS.</para>
-      </listitem>
-
-      <listitem>
-        <para>In Linux sandbox builds, we <link
-        xlink:href="https://github.com/NixOS/nix/commit/eba840c8a13b465ace90172ff76a0db2899ab11b">now</link>
-        use <filename>/build</filename> instead of
-        <filename>/tmp</filename> as the temporary build
-        directory. This fixes potential security problems when a build
-        accidentally stores its <envar>TMPDIR</envar> in some
-        security-sensitive place, such as an RPATH.</para>
-      </listitem>
-
-    </itemizedlist>
-
-    </para>
-
-  </listitem>
-
-  <listitem>
-    <para><emphasis>Pure evaluation mode</emphasis>. This is a variant
-    of the existing restricted evaluation mode. In pure mode, the Nix
-    evaluator forbids access to anything that could cause different
-    evaluations of the same command line arguments to produce a
-    different result. This includes builtin functions such as
-    <function>builtins.getEnv</function>, but more importantly,
-    <emphasis>all</emphasis> filesystem or network access unless a
-    content hash or commit hash is specified. For example, calls to
-    <function>builtins.fetchGit</function> are only allowed if a
-    <varname>rev</varname> attribute is specified.</para>
-
-    <para>The goal of this feature is to enable true reproducibility
-    and traceability of builds (including NixOS system configurations)
-    at the evaluation level. For example, in the future,
-    <command>nixos-rebuild</command> might build configurations from a
-    Nix expression in a Git repository in pure mode. That expression
-    might fetch other repositories such as Nixpkgs via
-    <function>builtins.fetchGit</function>. The commit hash of the
-    top-level repository then uniquely identifies a running system,
-    and, in conjunction with that repository, allows it to be
-    reproduced or modified.</para>
-
-  </listitem>
-
-  <listitem>
-    <para>There are several new features to support binary
-    reproducibility (i.e. to help ensure that multiple builds of the
-    same derivation produce exactly the same output). When
-    <option>enforce-determinism</option> is set to
-    <literal>false</literal>, it’s <link
-    xlink:href="https://github.com/NixOS/nix/commit/8bdf83f936adae6f2c907a6d2541e80d4120f051">no
-    longer</link> a fatal error if build rounds produce different
-    output. Also, a hook named <option>diff-hook</option> is <link
-    xlink:href="https://github.com/NixOS/nix/commit/9a313469a4bdea2d1e8df24d16289dc2a172a169">provided</link>
-    to allow you to run tools such as <command>diffoscope</command>
-    when build rounds produce different output.</para>
-  </listitem>
-
-  <listitem>
-    <para>Configuring remote builds is a lot easier now. Provided you
-    are not using the Nix daemon, you can now just specify a remote
-    build machine on the command line, e.g. <literal>--option builders
-    'ssh://my-mac x86_64-darwin'</literal>. The environment variable
-    <envar>NIX_BUILD_HOOK</envar> has been removed and is no longer
-    needed. The environment variable <envar>NIX_REMOTE_SYSTEMS</envar>
-    is still supported for compatibility, but it is also possible to
-    specify builders in <command>nix.conf</command> by setting the
-    option <literal>builders =
-    @<replaceable>path</replaceable></literal>.</para>
-  </listitem>
-
-  <listitem>
-    <para>If a fixed-output derivation produces a result with an
-    incorrect hash, the output path is moved to the location
-    corresponding to the actual hash and registered as valid. Thus, a
-    subsequent build of the fixed-output derivation with the correct
-    hash is unnecessary.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-shell</command> <link
-    xlink:href="https://github.com/NixOS/nix/commit/ea59f39326c8e9dc42dfed4bcbf597fbce58797c">now</link>
-    sets the <varname>IN_NIX_SHELL</varname> environment variable
-    during evaluation and in the shell itself. This can be used to
-    perform different actions depending on whether you’re in a Nix
-    shell or in a regular build. Nixpkgs provides
-    <varname>lib.inNixShell</varname> to check this variable during
-    evaluation.</para>
-  </listitem>
-
-  <listitem>
-    <para><envar>NIX_PATH</envar> is now lazy, so URIs in the path are
-    only downloaded if they are needed for evaluation.</para>
-  </listitem>
-
-  <listitem>
-    <para>You can now use
-    <uri>channel:<replaceable>channel-name</replaceable></uri> as a
-    short-hand for
-    <uri>https://nixos.org/channels/<replaceable>channel-name</replaceable>/nixexprs.tar.xz</uri>. For
-    example, <literal>nix-build channel:nixos-15.09 -A hello</literal>
-    will build the GNU Hello package from the
-    <literal>nixos-15.09</literal> channel. In the future, this may
-    use Git to fetch updates more efficiently.</para>
-  </listitem>
-
-  <listitem>
-    <para>When <option>--no-build-output</option> is given, the last
-    10 lines of the build log will be shown if a build
-    fails.</para>
-  </listitem>
-
-  <listitem>
-    <para>Networking has been improved:
-
-    <itemizedlist>
-
-      <listitem>
-        <para>HTTP/2 is now supported. This makes binary cache lookups
-        <link
-        xlink:href="https://github.com/NixOS/nix/commit/90ad02bf626b885a5dd8967894e2eafc953bdf92">much
-        more efficient</link>.</para>
-      </listitem>
-
-      <listitem>
-        <para>We now retry downloads on many HTTP errors, making
-        binary caches substituters more resilient to temporary
-        failures.</para>
-      </listitem>
-
-      <listitem>
-        <para>HTTP credentials can now be configured via the standard
-        <filename>netrc</filename> mechanism.</para>
-      </listitem>
-
-      <listitem>
-        <para>If S3 support is enabled at compile time,
-        <uri>s3://</uri> URIs are <link
-        xlink:href="https://github.com/NixOS/nix/commit/9ff9c3f2f80ba4108e9c945bbfda2c64735f987b">supported</link>
-        in all places where Nix allows URIs.</para>
-      </listitem>
-
-      <listitem>
-        <para>Brotli compression is now supported. In particular,
-        <uri>cache.nixos.org</uri> build logs are now compressed using
-        Brotli.</para>
-      </listitem>
-
-    </itemizedlist>
-
-    </para>
-
-  </listitem>
-
-  <listitem>
-    <para><command>nix-env</command> <link
-    xlink:href="https://github.com/NixOS/nix/commit/b0cb11722626e906a73f10dd9a0c9eea29faf43a">now</link>
-    ignores packages with bad derivation names (in particular those
-    starting with a digit or containing a dot).</para>
-  </listitem>
-
-  <listitem>
-    <para>Many configuration options have been renamed, either because
-    they were unnecessarily verbose
-    (e.g. <option>build-use-sandbox</option> is now just
-    <option>sandbox</option>) or to reflect generalised behaviour
-    (e.g. <option>binary-caches</option> is now
-    <option>substituters</option> because it allows arbitrary store
-    URIs). The old names are still supported for compatibility.</para>
-  </listitem>
-
-  <listitem>
-    <para>The <option>max-jobs</option> option can <link
-    xlink:href="https://github.com/NixOS/nix/commit/7251d048fa812d2551b7003bc9f13a8f5d4c95a5">now</link>
-    be set to <literal>auto</literal> to use the number of CPUs in the
-    system.</para>
-  </listitem>
-
-  <listitem>
-    <para>Hashes can <link
-    xlink:href="https://github.com/NixOS/nix/commit/c0015e87af70f539f24d2aa2bc224a9d8b84276b">now</link>
-    be specified in base-64 format, in addition to base-16 and the
-    non-standard base-32.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-shell</command> now uses
-    <varname>bashInteractive</varname> from Nixpkgs, rather than the
-    <command>bash</command> command that happens to be in the caller’s
-    <envar>PATH</envar>. This is especially important on macOS where
-    the <command>bash</command> provided by the system is seriously
-    outdated and cannot execute <literal>stdenv</literal>’s setup
-    script.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix can now automatically trigger a garbage collection if
-    free disk space drops below a certain level during a build. This
-    is configured using the <option>min-free</option> and
-    <option>max-free</option> options.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-store -q --roots</command> and
-    <command>nix-store --gc --print-roots</command> now show temporary
-    and in-memory roots.</para>
-  </listitem>
-
-  <listitem>
-    <para>
-      Nix can now be extended with plugins. See the documentation of
-      the <option>plugin-files</option> option for more details.
-    </para>
-  </listitem>
-
-</itemizedlist>
-
-<para>The Nix language has the following new features:
-
-<itemizedlist>
-
-  <listitem>
-    <para>It supports floating point numbers. They are based on the
-    C++ <literal>float</literal> type and are supported by the
-    existing numerical operators. Export and import to and from JSON
-    and XML works, too.</para>
-  </listitem>
-
-  <listitem>
-    <para>Derivation attributes can now reference the outputs of the
-    derivation using the <function>placeholder</function> builtin
-    function. For example, the attribute
-
-<programlisting>
-configureFlags = "--prefix=${placeholder "out"} --includedir=${placeholder "dev"}";
-</programlisting>
-
-    will cause the <envar>configureFlags</envar> environment variable
-    to contain the actual store paths corresponding to the
-    <literal>out</literal> and <literal>dev</literal> outputs.</para>
-  </listitem>
-
-</itemizedlist>
-
-</para>
-
-<para>The following builtin functions are new or extended:
-
-<itemizedlist>
-
-  <listitem>
-    <para><function
-    xlink:href="https://github.com/NixOS/nix/commit/38539b943a060d9cdfc24d6e5d997c0885b8aa2f">builtins.fetchGit</function>
-    allows Git repositories to be fetched at evaluation time. Thus it
-    differs from the <function>fetchgit</function> function in
-    Nixpkgs, which fetches at build time and cannot be used to fetch
-    Nix expressions during evaluation. A typical use case is to import
-    external NixOS modules from your configuration, e.g.
-
-    <programlisting>imports = [ (builtins.fetchGit https://github.com/edolstra/dwarffs + "/module.nix") ];</programlisting>
-
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>Similarly, <function>builtins.fetchMercurial</function>
-    allows you to fetch Mercurial repositories.</para>
-  </listitem>
-
-  <listitem>
-    <para><function>builtins.path</function> generalises
-    <function>builtins.filterSource</function> and path literals
-    (e.g. <literal>./foo</literal>). It allows specifying a store path
-    name that differs from the source path name
-    (e.g. <literal>builtins.path { path = ./foo; name = "bar";
-    }</literal>) and also supports filtering out unwanted
-    files.</para>
-  </listitem>
-
-  <listitem>
-    <para><function>builtins.fetchurl</function> and
-    <function>builtins.fetchTarball</function> now support
-    <varname>sha256</varname> and <varname>name</varname>
-    attributes.</para>
-  </listitem>
-
-  <listitem>
-    <para><function
-    xlink:href="https://github.com/NixOS/nix/commit/b8867a0239b1930a16f9ef3f7f3e864b01416dff">builtins.split</function>
-    splits a string using a POSIX extended regular expression as the
-    separator.</para>
-  </listitem>
-
-  <listitem>
-    <para><function
-    xlink:href="https://github.com/NixOS/nix/commit/26d92017d3b36cff940dcb7d1611c42232edb81a">builtins.partition</function>
-    partitions the elements of a list into two lists, depending on a
-    Boolean predicate.</para>
-  </listitem>
-
-  <listitem>
-    <para><literal>&lt;nix/fetchurl.nix&gt;</literal> now uses the
-    content-addressable tarball cache at
-    <uri>http://tarballs.nixos.org/</uri>, just like
-    <function>fetchurl</function> in
-    Nixpkgs. (f2682e6e18a76ecbfb8a12c17e3a0ca15c084197)</para>
-  </listitem>
-
-  <listitem>
-    <para>In restricted and pure evaluation mode, builtin functions
-    that download from the network (such as
-    <function>fetchGit</function>) are permitted to fetch underneath a
-    list of URI prefixes specified in the option
-    <option>allowed-uris</option>.</para>
-  </listitem>
-
-</itemizedlist>
-
-</para>
-
-<para>The Nix build environment has the following changes:
-
-<itemizedlist>
-
-  <listitem>
-    <para>Values such as Booleans, integers, (nested) lists and
-    attribute sets can <link
-    xlink:href="https://github.com/NixOS/nix/commit/6de33a9c675b187437a2e1abbcb290981a89ecb1">now</link>
-    be passed to builders in a non-lossy way. If the special attribute
-    <varname>__structuredAttrs</varname> is set to
-    <literal>true</literal>, the other derivation attributes are
-    serialised in JSON format and made available to the builder via
-    the file <envar>.attrs.json</envar> in the builder’s temporary
-    directory. This obviates the need for
-    <varname>passAsFile</varname> since JSON files have no size
-    restrictions, unlike process environments.</para>
-
-    <para><link
-    xlink:href="https://github.com/NixOS/nix/commit/2d5b1b24bf70a498e4c0b378704cfdb6471cc699">As
-    a convenience to Bash builders</link>, Nix writes a script named
-    <envar>.attrs.sh</envar> to the builder’s directory that
-    initialises shell variables corresponding to all attributes that
-    are representable in Bash. This includes non-nested (associative)
-    arrays. For example, the attribute <literal>hardening.format =
-    true</literal> ends up as the Bash associative array element
-    <literal>${hardening[format]}</literal>.</para>
-  </listitem>
-
-  <listitem>
-    <para>Builders can <link
-    xlink:href="https://github.com/NixOS/nix/commit/88e6bb76de5564b3217be9688677d1c89101b2a3">now</link>
-    communicate what build phase they are in by writing messages to
-    the file descriptor specified in <envar>NIX_LOG_FD</envar>. The
-    current phase is shown by the <command>nix</command> progress
-    indicator.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>In Linux sandbox builds, we <link
-    xlink:href="https://github.com/NixOS/nix/commit/a2d92bb20e82a0957067ede60e91fab256948b41">now</link>
-    provide a default <filename>/bin/sh</filename> (namely
-    <filename>ash</filename> from BusyBox).</para>
-  </listitem>
-
-  <listitem>
-    <para>In structured attribute mode,
-    <varname>exportReferencesGraph</varname> <link
-    xlink:href="https://github.com/NixOS/nix/commit/c2b0d8749f7e77afc1c4b3e8dd36b7ee9720af4a">exports</link>
-    extended information about closures in JSON format. In particular,
-    it includes the sizes and hashes of paths. This is primarily
-    useful for NixOS image builders.</para>
-  </listitem>
-
-  <listitem>
-    <para>Builds are <link
-    xlink:href="https://github.com/NixOS/nix/commit/21948deed99a3295e4d5666e027a6ca42dc00b40">now</link>
-    killed as soon as Nix receives EOF on the builder’s stdout or
-    stderr. This fixes a bug that allowed builds to hang Nix
-    indefinitely, regardless of
-    timeouts.</para>
-  </listitem>
-
-  <listitem>
-    <para>The <option>sandbox-paths</option> configuration
-    option can now specify optional paths by appending a
-    <literal>?</literal>, e.g. <literal>/dev/nvidiactl?</literal> will
-    bind-mount <varname>/dev/nvidiactl</varname> only if it
-    exists.</para>
-  </listitem>
-
-  <listitem>
-    <para>On Linux, builds are now executed in a user
-    namespace with UID 1000 and GID 100.</para>
-  </listitem>
-
-</itemizedlist>
-
-</para>
-
-<para>A number of significant internal changes were made:
-
-<itemizedlist>
-
-  <listitem>
-    <para>Nix no longer depends on Perl and all Perl components have
-    been rewritten in C++ or removed. The Perl bindings that used to
-    be part of Nix have been moved to a separate package,
-    <literal>nix-perl</literal>.</para>
-  </listitem>
-
-  <listitem>
-    <para>All <classname>Store</classname> classes are now
-    thread-safe. <classname>RemoteStore</classname> supports multiple
-    concurrent connections to the daemon. This is primarily useful in
-    multi-threaded programs such as
-    <command>hydra-queue-runner</command>.</para>
-  </listitem>
-
-</itemizedlist>
-
-</para>
-
-<para>This release has contributions from
-
-Adrien Devresse,
-Alexander Ried,
-Alex Cruice,
-Alexey Shmalko,
-AmineChikhaoui,
-Andy Wingo,
-Aneesh Agrawal,
-Anthony Cowley,
-Armijn Hemel,
-aszlig,
-Ben Gamari,
-Benjamin Hipple,
-Benjamin Staffin,
-Benno FΓΌnfstΓΌck,
-BjΓΈrn Forsman,
-Brian McKenna,
-Charles Strahan,
-Chase Adams,
-Chris Martin,
-Christian Theune,
-Chris Warburton,
-Daiderd Jordan,
-Dan Connolly,
-Daniel Peebles,
-Dan Peebles,
-davidak,
-David McFarland,
-Dmitry Kalinkin,
-Domen KoΕΎar,
-Eelco Dolstra,
-Emery Hemingway,
-Eric Litak,
-Eric Wolf,
-Fabian Schmitthenner,
-Frederik Rietdijk,
-Gabriel Gonzalez,
-Giorgio Gallo,
-Graham Christensen,
-Guillaume Maudoux,
-Harmen,
-Iavael,
-James Broadhead,
-James Earl Douglas,
-Janus Troelsen,
-Jeremy Shaw,
-Joachim Schiele,
-Joe Hermaszewski,
-Joel Moberg,
-Johannes 'fish' Ziemke,
-JΓΆrg Thalheim,
-Jude Taylor,
-kballou,
-Keshav Kini,
-Kjetil Orbekk,
-Langston Barrett,
-Linus Heckemann,
-Ludovic Courtès,
-Manav Rathi,
-Marc Scholten,
-Markus Hauck,
-Matt Audesse,
-Matthew Bauer,
-Matthias Beyer,
-Matthieu Coudron,
-N1X,
-Nathan Zadoks,
-Neil Mayhew,
-Nicolas B. Pierron,
-Niklas HambΓΌchen,
-Nikolay Amiantov,
-Ole JΓΈrgen BrΓΈnner,
-Orivej Desh,
-Peter Simons,
-Peter Stuart,
-Pyry Jahkola,
-regnat,
-Renzo Carbonara,
-Rhys,
-Robert Vollmert,
-Scott Olson,
-Scott R. Parish,
-Sergei Trofimovich,
-Shea Levy,
-Sheena Artrip,
-Spencer Baugh,
-Stefan Junker,
-Susan Potter,
-Thomas Tuegel,
-Timothy Allen,
-Tristan Hume,
-Tuomas Tynkkynen,
-tv,
-Tyson Whitehead,
-Vladimír ČunÑt,
-Will Dietz,
-wmertens,
-Wout Mertens,
-zimbatm and
-Zoran Plesivčak.
-</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-2.1.xml b/third_party/nix/doc/manual/release-notes/rl-2.1.xml
deleted file mode 100644
index 16c243fc19..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-2.1.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-2.1">
-
-<title>Release 2.1 (2018-09-02)</title>
-
-<para>This is primarily a bug fix release. It also reduces memory
-consumption in certain situations. In addition, it has the following
-new features:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>The Nix installer will no longer default to the Multi-User
-    installation for macOS. You can still <link
-    linkend="sect-multi-user-installation">instruct the installer to
-    run in multi-user mode</link>.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The Nix installer now supports performing a Multi-User
-    installation for Linux computers which are running systemd. You
-    can <link
-    linkend="sect-multi-user-installation">select a Multi-User installation</link> by passing the
-    <option>--daemon</option> flag to the installer: <command>sh &lt;(curl
-    https://nixos.org/nix/install) --daemon</command>.
-    </para>
-
-    <para>The multi-user installer cannot handle systems with SELinux.
-    If your system has SELinux enabled, you can <link
-    linkend="sect-single-user-installation">force the installer to run
-    in single-user mode</link>.</para>
-  </listitem>
-
-  <listitem>
-    <para>New builtin functions:
-    <literal>builtins.bitAnd</literal>,
-    <literal>builtins.bitOr</literal>,
-    <literal>builtins.bitXor</literal>,
-    <literal>builtins.fromTOML</literal>,
-    <literal>builtins.concatMap</literal>,
-    <literal>builtins.mapAttrs</literal>.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The S3 binary cache store now supports uploading NARs larger
-    than 5 GiB.</para>
-  </listitem>
-
-  <listitem>
-    <para>The S3 binary cache store now supports uploading to
-    S3-compatible services with the <literal>endpoint</literal>
-    option.</para>
-  </listitem>
-
-  <listitem>
-    <para>The flag <option>--fallback</option> is no longer required
-    to recover from disappeared NARs in binary caches.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix-daemon</command> now respects
-    <option>--store</option>.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix run</command> now respects
-    <varname>nix-support/propagated-user-env-packages</varname>.</para>
-  </listitem>
-
-</itemizedlist>
-
-<para>This release has contributions from
-
-Adrien Devresse,
-Aleksandr Pashkov,
-Alexandre Esteves,
-Amine Chikhaoui,
-Andrew Dunham,
-Asad Saeeduddin,
-aszlig,
-Ben Challenor,
-Ben Gamari,
-Benjamin Hipple,
-Bogdan Seniuc,
-Corey O'Connor,
-Daiderd Jordan,
-Daniel Peebles,
-Daniel Poelzleithner,
-Danylo Hlynskyi,
-Dmitry Kalinkin,
-Domen KoΕΎar,
-Doug Beardsley,
-Eelco Dolstra,
-Erik Arvstedt,
-FΓ©lix Baylac-JacquΓ©,
-Gleb Peregud,
-Graham Christensen,
-Guillaume Maudoux,
-Ivan Kozik,
-John Arnold,
-Justin Humm,
-Linus Heckemann,
-Lorenzo Manacorda,
-Matthew Justin Bauer,
-Matthew O'Gorman,
-Maximilian Bosch,
-Michael Bishop,
-Michael Fiano,
-Michael Mercier,
-Michael Raskin,
-Michael Weiss,
-Nicolas Dudebout,
-Peter Simons,
-Ryan Trinkle,
-Samuel Dionne-Riel,
-Sean Seefried,
-Shea Levy,
-Symphorien Gibol,
-Tim Engler,
-Tim Sears,
-Tuomas Tynkkynen,
-volth,
-Will Dietz,
-Yorick van Pelt and
-zimbatm.
-</para>
-
-</section>
diff --git a/third_party/nix/doc/manual/release-notes/rl-2.2.xml b/third_party/nix/doc/manual/release-notes/rl-2.2.xml
deleted file mode 100644
index d29eb87e82..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-2.2.xml
+++ /dev/null
@@ -1,143 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-2.2">
-
-<title>Release 2.2 (2019-01-11)</title>
-
-<para>This is primarily a bug fix release. It also has the following
-changes:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>In derivations that use structured attributes (i.e. that
-    specify set the <varname>__structuredAttrs</varname> attribute to
-    <literal>true</literal> to cause all attributes to be passed to
-    the builder in JSON format), you can now specify closure checks
-    per output, e.g.:
-
-<programlisting>
-outputChecks."out" = {
-  # The closure of 'out' must not be larger than 256 MiB.
-  maxClosureSize = 256 * 1024 * 1024;
-
-  # It must not refer to C compiler or to the 'dev' output.
-  disallowedRequisites = [ stdenv.cc "dev" ];
-};
-
-outputChecks."dev" = {
-  # The 'dev' output must not be larger than 128 KiB.
-  maxSize = 128 * 1024;
-};
-</programlisting>
-
-    </para>
-  </listitem>
-
-
-  <listitem>
-    <para>The derivation attribute
-    <varname>requiredSystemFeatures</varname> is now enforced for
-    local builds, and not just to route builds to remote builders.
-    The supported features of a machine can be specified through the
-    configuration setting <varname>system-features</varname>.</para>
-
-    <para>By default, <varname>system-features</varname> includes
-    <literal>kvm</literal> if <filename>/dev/kvm</filename>
-    exists. For compatibility, it also includes the pseudo-features
-    <literal>nixos-test</literal>, <literal>benchmark</literal> and
-    <literal>big-parallel</literal> which are used by Nixpkgs to route
-    builds to particular Hydra build machines.</para>
-
-  </listitem>
-
-  <listitem>
-    <para>Sandbox builds are now enabled by default on Linux.</para>
-  </listitem>
-
-  <listitem>
-    <para>The new command <command>nix doctor</command> shows
-    potential issues with your Nix installation.</para>
-  </listitem>
-
-  <listitem>
-    <para>The <literal>fetchGit</literal> builtin function now uses a
-    caching scheme that puts different remote repositories in distinct
-    local repositories, rather than a single shared repository. This
-    may require more disk space but is faster.</para>
-  </listitem>
-
-  <listitem>
-    <para>The <literal>dirOf</literal> builtin function now works on
-    relative paths.</para>
-  </listitem>
-
-  <listitem>
-    <para>Nix now supports <link
-    xlink:href="https://www.w3.org/TR/SRI/">SRI hashes</link>,
-    allowing the hash algorithm and hash to be specified in a single
-    string. For example, you can write:
-
-<programlisting>
-import &lt;nix/fetchurl.nix> {
-  url = https://nixos.org/releases/nix/nix-2.1.3/nix-2.1.3.tar.xz;
-  hash = "sha256-XSLa0FjVyADWWhFfkZ2iKTjFDda6mMXjoYMXLRSYQKQ=";
-};
-</programlisting>
-
-    instead of
-
-<programlisting>
-import &lt;nix/fetchurl.nix> {
-  url = https://nixos.org/releases/nix/nix-2.1.3/nix-2.1.3.tar.xz;
-  sha256 = "5d22dad058d5c800d65a115f919da22938c50dd6ba98c5e3a183172d149840a4";
-};
-</programlisting>
-
-    </para>
-
-    <para>In fixed-output derivations, the
-    <varname>outputHashAlgo</varname> attribute is no longer mandatory
-    if <varname>outputHash</varname> specifies the hash.</para>
-
-    <para><command>nix hash-file</command> and <command>nix
-    hash-path</command> now print hashes in SRI format by
-    default. They also use SHA-256 by default instead of SHA-512
-    because that's what we use most of the time in Nixpkgs.</para>
-  </listitem>
-
-  <listitem>
-    <para>Integers are now 64 bits on all platforms.</para>
-  </listitem>
-
-  <listitem>
-    <para>The evaluator now prints profiling statistics (enabled via
-    the <envar>NIX_SHOW_STATS</envar> and
-    <envar>NIX_COUNT_CALLS</envar> environment variables) in JSON
-    format.</para>
-  </listitem>
-
-  <listitem>
-    <para>The option <option>--xml</option> in <command>nix-store
-    --query</command> has been removed. Instead, there now is an
-    option <option>--graphml</option> to output the dependency graph
-    in GraphML format.</para>
-  </listitem>
-
-  <listitem>
-    <para>All <filename>nix-*</filename> commands are now symlinks to
-    <filename>nix</filename>. This saves a bit of disk space.</para>
-  </listitem>
-
-  <listitem>
-    <para><command>nix repl</command> now uses
-    <literal>libeditline</literal> or
-    <literal>libreadline</literal>.</para>
-  </listitem>
-
-</itemizedlist>
-
-</section>
-
diff --git a/third_party/nix/doc/manual/release-notes/rl-2.3.xml b/third_party/nix/doc/manual/release-notes/rl-2.3.xml
deleted file mode 100644
index 0ad7d641f8..0000000000
--- a/third_party/nix/doc/manual/release-notes/rl-2.3.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-<section xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      version="5.0"
-      xml:id="ssec-relnotes-2.3">
-
-<title>Release 2.3 (2019-09-04)</title>
-
-<para>This is primarily a bug fix release. However, it makes some
-incompatible changes:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para>Nix now uses BSD file locks instead of POSIX file
-    locks. Because of this, you should not use Nix 2.3 and previous
-    releases at the same time on a Nix store.</para>
-  </listitem>
-
-</itemizedlist>
-
-<para>It also has the following changes:</para>
-
-<itemizedlist>
-
-  <listitem>
-    <para><function>builtins.fetchGit</function>'s <varname>ref</varname>
-    argument now allows specifying an absolute remote ref.
-    Nix will automatically prefix <varname>ref</varname> with
-    <literal>refs/heads</literal> only if <varname>ref</varname> doesn't
-    already begin with <literal>refs/</literal>.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The installer now enables sandboxing by default on Linux when the
-    system has the necessary kernel support.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The <literal>max-jobs</literal> setting now defaults to 1.</para>
-  </listitem>
-
-  <listitem>
-    <para>New builtin functions:
-    <literal>builtins.isPath</literal>,
-    <literal>builtins.hashFile</literal>.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>The <command>nix</command> command has a new
-    <option>--print-build-logs</option> (<option>-L</option>) flag to
-    print build log output to stderr, rather than showing the last log
-    line in the progress bar. To distinguish between concurrent
-    builds, log lines are prefixed by the name of the package.
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>Builds are now executed in a pseudo-terminal, and the
-    <envar>TERM</envar> environment variable is set to
-    <literal>xterm-256color</literal>. This allows many programs
-    (e.g. <command>gcc</command>, <command>clang</command>,
-    <command>cmake</command>) to print colorized log output.</para>
-  </listitem>
-
-  <listitem>
-    <para>Add <option>--no-net</option> convenience flag. This flag
-    disables substituters; sets the <literal>tarball-ttl</literal>
-    setting to infinity (ensuring that any previously downloaded files
-    are considered current); and disables retrying downloads and sets
-    the connection timeout to the minimum. This flag is enabled
-    automatically if there are no configured non-loopback network
-    interfaces.</para>
-  </listitem>
-
-  <listitem>
-    <para>Add a <literal>post-build-hook</literal> setting to run a
-    program after a build has succeeded.</para>
-  </listitem>
-
-  <listitem>
-    <para>Add a <literal>trace-function-calls</literal> setting to log
-    the duration of Nix function calls to stderr.</para>
-  </listitem>
-
-</itemizedlist>
-
-</section>
diff --git a/third_party/nix/doc/manual/schemas.xml b/third_party/nix/doc/manual/schemas.xml
deleted file mode 100644
index 691a517b9c..0000000000
--- a/third_party/nix/doc/manual/schemas.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0"?>
-<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
-  <uri pattern="*.xml" typeId="DocBook"/>
-</locatingRules>
diff --git a/third_party/nix/misc/systemd/nix-daemon.service.in b/third_party/nix/misc/systemd/nix-daemon.service.in
deleted file mode 100644
index c3d2a4a39e..0000000000
--- a/third_party/nix/misc/systemd/nix-daemon.service.in
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-Description=Nix Daemon
-RequiresMountsFor=@storedir@
-RequiresMountsFor=@localstatedir@
-ConditionPathIsReadWrite=@localstatedir@/nix/daemon-socket
-
-[Service]
-ExecStart=@@bindir@/nix-daemon nix-daemon
-KillMode=process
-
-[Install]
-WantedBy=multi-user.target
diff --git a/third_party/nix/misc/systemd/nix-daemon.socket.in b/third_party/nix/misc/systemd/nix-daemon.socket.in
deleted file mode 100644
index 9ed39ffe6e..0000000000
--- a/third_party/nix/misc/systemd/nix-daemon.socket.in
+++ /dev/null
@@ -1,11 +0,0 @@
-[Unit]
-Description=Nix Daemon Socket
-Before=multi-user.target
-RequiresMountsFor=@storedir@
-ConditionPathIsReadWrite=@localstatedir@/nix/daemon-socket
-
-[Socket]
-ListenStream=@localstatedir@/nix/daemon-socket/socket
-
-[Install]
-WantedBy=sockets.target
diff --git a/third_party/nix/scripts/build.sh b/third_party/nix/scripts/build.sh
deleted file mode 100755
index 759c9e9f2c..0000000000
--- a/third_party/nix/scripts/build.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-
-# Run `nix build` using a local store, for use during development. Intended to
-# be run from the cmake build directory
-
-set -eo pipefail
-
-if [ $1 = "--debug" ]; then
-    run=(gdb --args)
-    shift 1
-elif [ "$1" = "--rr" ]; then
-    run=(rr record)
-    shift 1
-else
-    run=()
-fi
-
-make -j 10
-NIX_STORE_DIR=$(pwd)/nix/store \
-    NIX_LOG_DIR=$(pwd)/nix/var/log/nix \
-    NIX_STATE_DIR=$(pwd)/nix/var/nix \
-    XDG_CACHE_HOME=$(pwd)/cache \
-    NIX_REMOTE=daemon \
-    ${run[*]} ./src/nix build "$@"
diff --git a/third_party/nix/scripts/daemon.sh b/third_party/nix/scripts/daemon.sh
deleted file mode 100755
index 3daa0f1390..0000000000
--- a/third_party/nix/scripts/daemon.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-set -eo pipefail
-
-# Run a nix daemon using a local store, for use during development. Intended to
-# be run from the cmake build directory
-
-if [ $1 = "--debug" ]; then
-    run=(gdb --args)
-    shift 1
-elif [ "$1" = "--rr" ]; then
-    run=(rr record)
-    shift 1
-else
-    run=()
-fi
-
-make -j 10
-NIX_STORE_DIR=$(pwd)/nix/store \
-    NIX_LOG_DIR=$(pwd)/nix/var/log/nix \
-    NIX_STATE_DIR=$(pwd)/nix/var/nix \
-    XDG_CACHE_HOME=$(pwd)/cache \
-    NIX_LIBEXEC_DIR=$(pwd) \
-    GRPC_TRACE=all \
-    ${gdb[*]} ./src/nix-daemon/nix-daemon
diff --git a/third_party/nix/scripts/eval.sh b/third_party/nix/scripts/eval.sh
deleted file mode 100755
index f71d9f7931..0000000000
--- a/third_party/nix/scripts/eval.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env bash
-set -eo pipefail
-
-# Run `nix eval` using a local store, for use during development. Intended to
-# be run from the cmake build directory
-
-if [ "$#" -gt 0 ] && [ "$1" = "--debug" ]; then
-    gdb=(gdb --args)
-    shift 1
-elif [ "$1" = "--rr" ]; then
-    gdb=(rr record)
-    shift 1
-else
-    gdb=()
-fi
-
-make -j 10
-NIX_STORE_DIR=$(pwd)/nix/store \
-    NIX_LOG_DIR=$(pwd)/nix/var/log/nix \
-    NIX_STATE_DIR=$(pwd)/nix/var/nix \
-    XDG_CACHE_HOME=$(pwd)/cache \
-    NIX_REMOTE=daemon \
-    ${gdb[*]} ./src/nix eval "$@"
diff --git a/third_party/nix/scripts/install-darwin-multi-user.sh b/third_party/nix/scripts/install-darwin-multi-user.sh
deleted file mode 100644
index 49076bd5c0..0000000000
--- a/third_party/nix/scripts/install-darwin-multi-user.sh
+++ /dev/null
@@ -1,144 +0,0 @@
-#!/usr/bin/env bash
-
-set -eu
-set -o pipefail
-
-readonly PLIST_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
-
-dsclattr() {
-    /usr/bin/dscl . -read "$1" \
-        | awk "/$2/ { print \$2 }"
-}
-
-poly_validate_assumptions() {
-    if [ "$(uname -s)" != "Darwin" ]; then
-        failure "This script is for use with macOS!"
-    fi
-}
-
-poly_service_installed_check() {
-    [ -e "$PLIST_DEST" ]
-}
-
-poly_service_uninstall_directions() {
-        cat <<EOF
-$1. Delete $PLIST_DEST
-
-  sudo launchctl unload $PLIST_DEST
-  sudo rm $PLIST_DEST
-
-EOF
-}
-
-poly_service_setup_note() {
-    cat <<EOF
- - load and start a LaunchDaemon (at $PLIST_DEST) for nix-daemon
-
-EOF
-}
-
-poly_configure_nix_daemon_service() {
-    _sudo "to set up the nix-daemon as a LaunchDaemon" \
-          cp -f "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST"
-
-    _sudo "to load the LaunchDaemon plist for nix-daemon" \
-          launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
-
-    _sudo "to start the nix-daemon" \
-          launchctl start org.nixos.nix-daemon
-
-}
-
-poly_group_exists() {
-    /usr/bin/dscl . -read "/Groups/$1" > /dev/null 2>&1
-}
-
-poly_group_id_get() {
-    dsclattr "/Groups/$1" "PrimaryGroupID"
-}
-
-poly_create_build_group() {
-    _sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
-          /usr/sbin/dseditgroup -o create \
-          -r "Nix build group for nix-daemon" \
-          -i "$NIX_BUILD_GROUP_ID" \
-          "$NIX_BUILD_GROUP_NAME" >&2
-}
-
-poly_user_exists() {
-    /usr/bin/dscl . -read "/Users/$1" > /dev/null 2>&1
-}
-
-poly_user_id_get() {
-    dsclattr "/Users/$1" "UniqueID"
-}
-
-poly_user_hidden_get() {
-    dsclattr "/Users/$1" "IsHidden"
-}
-
-poly_user_hidden_set() {
-    _sudo "in order to make $1 a hidden user" \
-          /usr/bin/dscl . -create "/Users/$1" "IsHidden" "1"
-}
-
-poly_user_home_get() {
-    dsclattr "/Users/$1" "NFSHomeDirectory"
-}
-
-poly_user_home_set() {
-    _sudo "in order to give $1 a safe home directory" \
-          /usr/bin/dscl . -create "/Users/$1" "NFSHomeDirectory" "$2"
-}
-
-poly_user_note_get() {
-    dsclattr "/Users/$1" "RealName"
-}
-
-poly_user_note_set() {
-    _sudo "in order to give $username a useful note" \
-          /usr/bin/dscl . -create "/Users/$1" "RealName" "$2"
-}
-
-poly_user_shell_get() {
-    dsclattr "/Users/$1" "UserShell"
-}
-
-poly_user_shell_set() {
-    _sudo "in order to give $1 a safe home directory" \
-          /usr/bin/dscl . -create "/Users/$1" "UserShell" "$2"
-}
-
-poly_user_in_group_check() {
-    username=$1
-    group=$2
-    dseditgroup -o checkmember -m "$username" "$group" > /dev/null 2>&1
-}
-
-poly_user_in_group_set() {
-    username=$1
-    group=$2
-
-    _sudo "Add $username to the $group group"\
-          /usr/sbin/dseditgroup -o edit -t user \
-          -a "$username" "$group"
-}
-
-poly_user_primary_group_get() {
-    dsclattr "/Users/$1" "PrimaryGroupID"
-}
-
-poly_user_primary_group_set() {
-    _sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
-          /usr/bin/dscl . -create "/Users/$1" "PrimaryGroupID" "$2"
-}
-
-poly_create_build_user() {
-    username=$1
-    uid=$2
-    builder_num=$3
-
-    _sudo "Creating the Nix build user (#$builder_num), $username" \
-          /usr/bin/dscl . create "/Users/$username" \
-          UniqueID "${uid}"
-}
diff --git a/third_party/nix/scripts/install-multi-user.sh b/third_party/nix/scripts/install-multi-user.sh
deleted file mode 100644
index 5233762fa6..0000000000
--- a/third_party/nix/scripts/install-multi-user.sh
+++ /dev/null
@@ -1,798 +0,0 @@
-#!/usr/bin/env bash
-
-set -eu
-set -o pipefail
-
-# Sourced from:
-# - https://github.com/LnL7/nix-darwin/blob/8c29d0985d74b4a990238497c47a2542a5616b3c/bootstrap.sh
-# - https://gist.github.com/expipiplus1/e571ce88c608a1e83547c918591b149f/ac504c6c1b96e65505fbda437a28ce563408ecb0
-# - https://github.com/NixOS/nixos-org-configurations/blob/a122f418797713d519aadf02e677fce0dc1cb446/delft/scripts/nix-mac-installer.sh
-# - https://github.com/matthewbauer/macNixOS/blob/f6045394f9153edea417be90c216788e754feaba/install-macNixOS.sh
-# - https://gist.github.com/LnL7/9717bd6cdcb30b086fd7f2093e5f8494/86b26f852ce563e973acd30f796a9a416248c34a
-#
-# however tracking which bits came from which would be impossible.
-
-readonly ESC='\033[0m'
-readonly BOLD='\033[1m'
-readonly BLUE='\033[34m'
-readonly BLUE_UL='\033[4;34m'
-readonly GREEN='\033[32m'
-readonly GREEN_UL='\033[4;32m'
-readonly RED='\033[31m'
-
-readonly NIX_USER_COUNT="32"
-readonly NIX_BUILD_GROUP_ID="30000"
-readonly NIX_BUILD_GROUP_NAME="nixbld"
-readonly NIX_FIRST_BUILD_UID="30001"
-# Please don't change this. We don't support it, because the
-# default shell profile that comes with Nix doesn't support it.
-readonly NIX_ROOT="/nix"
-
-readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshrc")
-readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
-readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
-
-readonly NIX_INSTALLED_NIX="@nix@"
-readonly NIX_INSTALLED_CACERT="@cacert@"
-readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
-
-readonly ROOT_HOME=$(echo ~root)
-
-if [ -t 0 ]; then
-    readonly IS_HEADLESS='no'
-else
-    readonly IS_HEADLESS='yes'
-fi
-
-headless() {
-    if [ "$IS_HEADLESS" = "yes" ]; then
-        return 0
-    else
-        return 1
-    fi
-}
-
-contactme() {
-    echo "We'd love to help if you need it."
-    echo ""
-    echo "If you can, open an issue at https://github.com/nixos/nix/issues"
-    echo ""
-    echo "Or feel free to contact the team,"
-    echo " - on IRC #nixos on irc.freenode.net"
-    echo " - on twitter @nixos_org"
-}
-
-uninstall_directions() {
-    subheader "Uninstalling nix:"
-    local step=0
-
-    if poly_service_installed_check; then
-        step=$((step + 1))
-        poly_service_uninstall_directions "$step"
-    fi
-
-    for profile_target in "${PROFILE_TARGETS[@]}"; do
-        if [ -e "$profile_target" ] && [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
-            step=$((step + 1))
-            cat <<EOF
-$step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target
-
-  sudo mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
-
-(after this one, you may need to re-open any terminals that were
-opened while it existed.)
-
-EOF
-        fi
-    done
-
-    step=$((step + 1))
-    cat <<EOF
-$step. Delete the files Nix added to your system:
-
-  sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels
-
-and that is it.
-
-EOF
-
-}
-
-nix_user_for_core() {
-    printf "nixbld%d" "$1"
-}
-
-nix_uid_for_core() {
-    echo $((NIX_FIRST_BUILD_UID + $1 - 1))
-}
-
-_textout() {
-    echo -en "$1"
-    shift
-    if [ "$*" = "" ]; then
-        cat
-    else
-        echo "$@"
-    fi
-    echo -en "$ESC"
-}
-
-header() {
-    follow="---------------------------------------------------------"
-    header=$(echo "---- $* $follow$follow$follow" | head -c 80)
-    echo ""
-    _textout "$BLUE" "$header"
-}
-
-warningheader() {
-    follow="---------------------------------------------------------"
-    header=$(echo "---- $* $follow$follow$follow" | head -c 80)
-    echo ""
-    _textout "$RED" "$header"
-}
-
-subheader() {
-    echo ""
-    _textout "$BLUE_UL" "$*"
-}
-
-row() {
-    printf "$BOLD%s$ESC:\\t%s\\n" "$1" "$2"
-}
-
-task() {
-    echo ""
-    ok "~~> $1"
-}
-
-bold() {
-    echo "$BOLD$*$ESC"
-}
-
-ok() {
-    _textout "$GREEN" "$@"
-}
-
-warning() {
-    warningheader "warning!"
-    cat
-    echo ""
-}
-
-failure() {
-    header "oh no!"
-    _textout "$RED" "$@"
-    echo ""
-    _textout "$RED" "$(contactme)"
-    trap finish_cleanup EXIT
-    exit 1
-}
-
-ui_confirm() {
-    _textout "$GREEN$GREEN_UL" "$1"
-
-    if headless; then
-        echo "No TTY, assuming you would say yes :)"
-        return 0
-    fi
-
-    local prompt="[y/n] "
-    echo -n "$prompt"
-    while read -r y; do
-        if [ "$y" = "y" ]; then
-            echo ""
-            return 0
-        elif [ "$y" = "n" ]; then
-            echo ""
-            return 1
-        else
-            _textout "$RED" "Sorry, I didn't understand. I can only understand answers of y or n"
-            echo -n "$prompt"
-        fi
-    done
-    echo ""
-    return 1
-}
-
-__sudo() {
-    local expl="$1"
-    local cmd="$2"
-    shift
-    header "sudo execution"
-
-    echo "I am executing:"
-    echo ""
-    printf "    $ sudo %s\\n" "$cmd"
-    echo ""
-    echo "$expl"
-    echo ""
-
-    return 0
-}
-
-_sudo() {
-    local expl="$1"
-    shift
-    if ! headless; then
-        __sudo "$expl" "$*"
-    fi
-    sudo "$@"
-}
-
-
-readonly SCRATCH=$(mktemp -d -t tmp.XXXXXXXXXX)
-function finish_cleanup {
-    rm -rf "$SCRATCH"
-}
-
-function finish_fail {
-    finish_cleanup
-
-    failure <<EOF
-Jeeze, something went wrong. If you can take all the output and open
-an issue, we'd love to fix the problem so nobody else has this issue.
-
-:(
-EOF
-}
-trap finish_fail EXIT
-
-channel_update_failed=0
-function finish_success {
-    finish_cleanup
-
-    ok "Alright! We're done!"
-    if [ "x$channel_update_failed" = x1 ]; then
-        echo ""
-        echo "But fetching the nixpkgs channel failed. (Are you offline?)"
-        echo "To try again later, run \"sudo -i nix-channel --update nixpkgs\"."
-    fi
-    cat <<EOF
-
-Before Nix will work in your existing shells, you'll need to close
-them and open them again. Other than that, you should be ready to go.
-
-Try it! Open a new terminal, and type:
-
-  $ nix-shell -p nix-info --run "nix-info -m"
-
-Thank you for using this installer. If you have any feedback, don't
-hesitate:
-
-$(contactme)
-EOF
-}
-
-
-validate_starting_assumptions() {
-    poly_validate_assumptions
-
-    if [ $EUID -eq 0 ]; then
-        failure <<EOF
-Please do not run this script with root privileges. We will call sudo
-when we need to.
-EOF
-    fi
-
-    if type nix-env 2> /dev/null >&2; then
-        failure <<EOF
-Nix already appears to be installed, and this tool assumes it is
-_not_ yet installed.
-
-$(uninstall_directions)
-EOF
-    fi
-
-    if [ "${NIX_REMOTE:-}" != "" ]; then
-        failure <<EOF
-For some reason, \$NIX_REMOTE is set. It really should not be set
-before this installer runs, and it hints that Nix is currently
-installed. Please delete the old Nix installation and start again.
-
-Note: You might need to close your shell window and open a new shell
-to clear the variable.
-EOF
-    fi
-
-    if echo "${SSL_CERT_FILE:-}" | grep -qE "(nix/var/nix|nix-profile)"; then
-        failure <<EOF
-It looks like \$SSL_CERT_FILE is set to a path that used to be part of
-the old Nix installation. Please unset that variable and try again:
-
-  $ unset SSL_CERT_FILE
-
-EOF
-    fi
-
-    for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do
-        if [ -f "$file" ]; then
-            if grep -l "^[^#].*.nix-profile" "$file"; then
-                failure <<EOF
-I found a reference to a ".nix-profile" in $file.
-This has a high chance of breaking a new nix installation. It was most
-likely put there by a previous Nix installer.
-
-Please remove this reference and try running this again. You should
-also look for similar references in:
-
- - ~/.bash_profile
- - ~/.bash_login
- - ~/.profile
-
-or other shell init files that you may have.
-
-$(uninstall_directions)
-EOF
-            fi
-        fi
-    done
-
-    if [ -d /nix/store ] || [ -d /nix/var ]; then
-        failure <<EOF
-There are some relics of a previous installation of Nix at /nix, and
-this scripts assumes Nix is _not_ yet installed. Please delete the old
-Nix installation and start again.
-
-$(uninstall_directions)
-EOF
-    fi
-
-    if [ -d /etc/nix ]; then
-        failure <<EOF
-There are some relics of a previous installation of Nix at /etc/nix, and
-this scripts assumes Nix is _not_ yet installed. Please delete the old
-Nix installation and start again.
-
-$(uninstall_directions)
-EOF
-    fi
-
-    for profile_target in "${PROFILE_TARGETS[@]}"; do
-        if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
-        failure <<EOF
-When this script runs, it backs up the current $profile_target to
-$profile_target$PROFILE_BACKUP_SUFFIX. This backup file already exists, though.
-
-Please follow these instructions to clean up the old backup file:
-
-1. Copy $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX to another place, just
-in case.
-
-2. Take care to make sure that $profile_target$PROFILE_BACKUP_SUFFIX doesn't look like
-it has anything nix-related in it. If it does, something is probably
-quite wrong. Please open an issue or get in touch immediately.
-
-3. Take care to make sure that $profile_target doesn't look like it has
-anything nix-related in it. If it does, and $profile_target _did not_,
-run:
-
-  $ /usr/bin/sudo /bin/mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
-
-and try again.
-EOF
-        fi
-
-        if [ -e "$profile_target" ] && grep -qi "nix" "$profile_target"; then
-            failure <<EOF
-It looks like $profile_target already has some Nix configuration in
-there. There should be no reason to run this again. If you're having
-trouble, please open an issue.
-EOF
-        fi
-    done
-
-    danger_paths=("$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.nix-profile")
-    for danger_path in "${danger_paths[@]}"; do
-        if _sudo "making sure that $danger_path doesn't exist" \
-           test -e "$danger_path"; then
-            failure <<EOF
-I found a file at $danger_path, which is a relic of a previous
-installation. You must first delete this file before continuing.
-
-$(uninstall_directions)
-EOF
-        fi
-    done
-}
-
-setup_report() {
-    header "Nix config report"
-    row "        Temp Dir" "$SCRATCH"
-    row "        Nix Root" "$NIX_ROOT"
-    row "     Build Users" "$NIX_USER_COUNT"
-    row "  Build Group ID" "$NIX_BUILD_GROUP_ID"
-    row "Build Group Name" "$NIX_BUILD_GROUP_NAME"
-    if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" != "" ]; then
-        row "Preexisting Install" "Allowed"
-    fi
-
-    subheader "build users:"
-
-    row "    Username" "UID"
-    for i in $(seq 1 "$NIX_USER_COUNT"); do
-        row "     $(nix_user_for_core "$i")" "$(nix_uid_for_core "$i")"
-    done
-    echo ""
-}
-
-create_build_group() {
-    local primary_group_id
-
-    task "Setting up the build group $NIX_BUILD_GROUP_NAME"
-    if ! poly_group_exists "$NIX_BUILD_GROUP_NAME"; then
-        poly_create_build_group
-        row "            Created" "Yes"
-    else
-        primary_group_id=$(poly_group_id_get "$NIX_BUILD_GROUP_NAME")
-        if [ "$primary_group_id" -ne "$NIX_BUILD_GROUP_ID" ]; then
-            failure <<EOF
-It seems the build group $NIX_BUILD_GROUP_NAME already exists, but
-with the UID $primary_group_id. This script can't really handle
-that right now, so I'm going to give up.
-
-You can fix this by editing this script and changing the
-NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID
-to $primary_group_id and re-run.
-EOF
-        else
-            row "            Exists" "Yes"
-        fi
-    fi
-}
-
-create_build_user_for_core() {
-    local coreid
-    local username
-    local uid
-
-    coreid="$1"
-    username=$(nix_user_for_core "$coreid")
-    uid=$(nix_uid_for_core "$coreid")
-
-    task "Setting up the build user $username"
-
-    if ! poly_user_exists "$username"; then
-        poly_create_build_user "$username" "$uid" "$coreid"
-        row "           Created" "Yes"
-    else
-        actual_uid=$(poly_user_id_get "$username")
-        if [ "$actual_uid" != "$uid" ]; then
-            failure <<EOF
-It seems the build user $username already exists, but with the UID
-with the UID '$actual_uid'. This script can't really handle that right
-now, so I'm going to give up.
-
-If you already created the users and you know they start from
-$actual_uid and go up from there, you can edit this script and change
-NIX_FIRST_BUILD_UID near the top of the file to $actual_uid and try
-again.
-EOF
-        else
-            row "            Exists" "Yes"
-        fi
-    fi
-
-    if [ "$(poly_user_hidden_get "$username")" = "1" ]; then
-        row "            Hidden" "Yes"
-    else
-        poly_user_hidden_set "$username"
-        row "            Hidden" "Yes"
-    fi
-
-    if [ "$(poly_user_home_get "$username")" = "/var/empty" ]; then
-        row "    Home Directory" "/var/empty"
-    else
-        poly_user_home_set "$username" "/var/empty"
-        row "    Home Directory" "/var/empty"
-    fi
-
-    # We use grep instead of an equality check because it is difficult
-    # to extract _just_ the user's note, instead it is prefixed with
-    # some plist junk. This was causing the user note to always be set,
-    # even if there was no reason for it.
-    if ! poly_user_note_get "$username" | grep -q "Nix build user $coreid"; then
-        row "              Note" "Nix build user $coreid"
-    else
-        poly_user_note_set "$username" "Nix build user $coreid"
-        row "              Note" "Nix build user $coreid"
-    fi
-
-    if [ "$(poly_user_shell_get "$username")" = "/sbin/nologin" ]; then
-        row "   Logins Disabled" "Yes"
-    else
-        poly_user_shell_set "$username" "/sbin/nologin"
-        row "   Logins Disabled" "Yes"
-    fi
-
-    if poly_user_in_group_check "$username" "$NIX_BUILD_GROUP_NAME"; then
-        row "  Member of $NIX_BUILD_GROUP_NAME" "Yes"
-    else
-        poly_user_in_group_set "$username" "$NIX_BUILD_GROUP_NAME"
-        row "  Member of $NIX_BUILD_GROUP_NAME" "Yes"
-    fi
-
-    if [ "$(poly_user_primary_group_get "$username")" = "$NIX_BUILD_GROUP_ID" ]; then
-        row "    PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
-    else
-        poly_user_primary_group_set "$username" "$NIX_BUILD_GROUP_ID"
-        row "    PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
-    fi
-}
-
-create_build_users() {
-    for i in $(seq 1 "$NIX_USER_COUNT"); do
-        create_build_user_for_core "$i"
-    done
-}
-
-create_directories() {
-    # FIXME: remove all of this because it duplicates LocalStore::LocalStore().
-
-    _sudo "to make the basic directory structure of Nix (part 1)" \
-          mkdir -pv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool} /nix/var/nix/{gcroots,profiles}/per-user
-
-    _sudo "to make the basic directory structure of Nix (part 2)" \
-          mkdir -pv -m 1775 /nix/store
-
-    _sudo "to make the basic directory structure of Nix (part 3)" \
-          chgrp "$NIX_BUILD_GROUP_NAME" /nix/store
-
-    _sudo "to place the default nix daemon configuration (part 1)" \
-          mkdir -pv -m 0555 /etc/nix
-}
-
-place_channel_configuration() {
-    echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels"
-    _sudo "to set up the default system channel (part 1)" \
-          install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels"
-}
-
-welcome_to_nix() {
-    ok "Welcome to the Multi-User Nix Installation"
-
-    cat <<EOF
-
-This installation tool will set up your computer with the Nix package
-manager. This will happen in a few stages:
-
-1. Make sure your computer doesn't already have Nix. If it does, I
-   will show you instructions on how to clean up your old one.
-
-2. Show you what we are going to install and where. Then we will ask
-   if you are ready to continue.
-
-3. Create the system users and groups that the Nix daemon uses to run
-   builds.
-
-4. Perform the basic installation of the Nix files daemon.
-
-5. Configure your shell to import special Nix Profile files, so you
-   can use Nix.
-
-6. Start the Nix daemon.
-
-EOF
-
-    if ui_confirm "Would you like to see a more detailed list of what we will do?"; then
-        cat <<EOF
-
-We will:
-
- - make sure your computer doesn't already have Nix files
-   (if it does, I will tell you how to clean them up.)
- - create local users (see the list above for the users we'll make)
- - create a local group ($NIX_BUILD_GROUP_NAME)
- - install Nix in to $NIX_ROOT
- - create a configuration file in /etc/nix
- - set up the "default profile" by creating some Nix-related files in
-   $ROOT_HOME
-EOF
-        for profile_target in "${PROFILE_TARGETS[@]}"; do
-            if [ -e "$profile_target" ]; then
-                cat <<EOF
- - back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX
- - update $profile_target to include some Nix configuration
-EOF
-            fi
-        done
-        poly_service_setup_note
-        if ! ui_confirm "Ready to continue?"; then
-            failure <<EOF
-Okay, maybe you would like to talk to the team.
-EOF
-        fi
-    fi
-}
-
-chat_about_sudo() {
-    header "let's talk about sudo"
-
-    if headless; then
-        cat <<EOF
-This script is going to call sudo a lot. Normally, it would show you
-exactly what commands it is running and why. However, the script is
-run in a headless fashion, like this:
-
-  $ curl https://nixos.org/nix/install | sh
-
-or maybe in a CI pipeline. Because of that, we're going to skip the
-verbose output in the interest of brevity.
-
-If you would like to
-see the output, try like this:
-
-  $ curl -o install-nix https://nixos.org/nix/install
-  $ sh ./install-nix
-
-EOF
-        return 0
-    fi
-
-    cat <<EOF
-This script is going to call sudo a lot. Every time we do, it'll
-output exactly what it'll do, and why.
-
-Just like this:
-EOF
-
-    __sudo "to demonstrate how our sudo prompts look" \
-           echo "this is a sudo prompt"
-
-    cat <<EOF
-
-This might look scary, but everything can be undone by running just a
-few commands. We used to ask you to confirm each time sudo ran, but it
-was too many times. Instead, I'll just ask you this one time:
-
-EOF
-    if ui_confirm "Can we use sudo?"; then
-        ok "Yay! Thanks! Let's get going!"
-    else
-        failure <<EOF
-That is okay, but we can't install.
-EOF
-    fi
-}
-
-install_from_extracted_nix() {
-    (
-        cd "$EXTRACTED_NIX_PATH"
-
-        _sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \
-              rsync -rlpt ./store/* "$NIX_ROOT/store/"
-
-        if [ -d "$NIX_INSTALLED_NIX" ]; then
-            echo "      Alright! We have our first nix at $NIX_INSTALLED_NIX"
-        else
-            failure <<EOF
-Something went wrong, and I didn't find Nix installed at
-$NIX_INSTALLED_NIX.
-EOF
-        fi
-
-        cat ./.reginfo \
-            | _sudo "to load data for the first time in to the Nix Database" \
-                   "$NIX_INSTALLED_NIX/bin/nix-store" --load-db
-
-        echo "      Just finished getting the nix database ready."
-    )
-}
-
-shell_source_lines() {
-    cat <<EOF
-
-# Nix
-if [ -e '$PROFILE_NIX_FILE' ]; then
-  . '$PROFILE_NIX_FILE'
-fi
-# End Nix
-
-EOF
-}
-
-configure_shell_profile() {
-    # If there is an /etc/profile.d directory, we want to ensure there
-    # is a nix.sh within it, so we can use the following loop to add
-    # the source lines to it. Note that I'm _not_ adding the source
-    # lines here, because we want to be using the regular machinery.
-    #
-    # If we go around that machinery, it becomes more complicated and
-    # adds complications to the uninstall instruction generator and
-    # old instruction sniffer as well.
-    if [ -d /etc/profile.d ]; then
-        _sudo "create a stub /etc/profile.d/nix.sh which will be updated" \
-              touch /etc/profile.d/nix.sh
-    fi
-
-    for profile_target in "${PROFILE_TARGETS[@]}"; do
-        if [ -e "$profile_target" ]; then
-            _sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \
-                  cp "$profile_target" "$profile_target$PROFILE_BACKUP_SUFFIX"
-
-            shell_source_lines \
-                | _sudo "extend your $profile_target with nix-daemon settings" \
-                        tee -a "$profile_target"
-        fi
-    done
-}
-
-setup_default_profile() {
-    _sudo "to installing a bootstrapping Nix in to the default Profile" \
-          HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_NIX"
-
-    if [ -z "${NIX_SSL_CERT_FILE:-}" ] || ! [ -f "${NIX_SSL_CERT_FILE:-}" ]; then
-        _sudo "to installing a bootstrapping SSL certificate just for Nix in to the default Profile" \
-              HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_CACERT"
-        export NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt
-    fi
-
-    # Have to explicitly pass NIX_SSL_CERT_FILE as part of the sudo call,
-    # otherwise it will be lost in environments where sudo doesn't pass
-    # all the environment variables by default.
-    _sudo "to update the default channel in the default profile" \
-          HOME="$ROOT_HOME" NIX_SSL_CERT_FILE="$NIX_SSL_CERT_FILE" "$NIX_INSTALLED_NIX/bin/nix-channel" --update nixpkgs \
-          || channel_update_failed=1
-
-}
-
-
-place_nix_configuration() {
-    cat <<EOF > "$SCRATCH/nix.conf"
-build-users-group = $NIX_BUILD_GROUP_NAME
-EOF
-    _sudo "to place the default nix daemon configuration (part 2)" \
-          install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf
-}
-
-main() {
-    if [ "$(uname -s)" = "Darwin" ]; then
-        # shellcheck source=./install-darwin-multi-user.sh
-        . "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh"
-    elif [ "$(uname -s)" = "Linux" ]; then
-        if [ -e /run/systemd/system ]; then
-            # shellcheck source=./install-systemd-multi-user.sh
-            . "$EXTRACTED_NIX_PATH/install-systemd-multi-user.sh"
-        else
-            failure "Sorry, the multi-user installation requires systemd on Linux (detected using /run/systemd/system)"
-        fi
-    else
-        failure "Sorry, I don't know what to do on $(uname)"
-    fi
-
-    welcome_to_nix
-    chat_about_sudo
-
-    if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" = "" ]; then
-        validate_starting_assumptions
-    fi
-
-    setup_report
-
-    if ! ui_confirm "Ready to continue?"; then
-        ok "Alright, no changes have been made :)"
-        contactme
-        trap finish_cleanup EXIT
-        exit 1
-    fi
-
-    create_build_group
-    create_build_users
-    create_directories
-    place_channel_configuration
-    install_from_extracted_nix
-
-    configure_shell_profile
-
-    set +eu
-    . /etc/profile
-    set -eu
-
-    setup_default_profile
-    place_nix_configuration
-    poly_configure_nix_daemon_service
-
-    trap finish_success EXIT
-}
-
-
-main
diff --git a/third_party/nix/scripts/install-nix-from-closure.sh b/third_party/nix/scripts/install-nix-from-closure.sh
deleted file mode 100644
index 3f15818547..0000000000
--- a/third_party/nix/scripts/install-nix-from-closure.sh
+++ /dev/null
@@ -1,180 +0,0 @@
-#!/bin/sh
-
-set -e
-
-dest="/nix"
-self="$(dirname "$0")"
-nix="@nix@"
-cacert="@cacert@"
-
-
-if ! [ -e "$self/.reginfo" ]; then
-    echo "$0: incomplete installer (.reginfo is missing)" >&2
-fi
-
-if [ -z "$USER" ] && ! USER=$(id -u -n); then
-    echo "$0: \$USER is not set" >&2
-    exit 1
-fi
-
-if [ -z "$HOME" ]; then
-    echo "$0: \$HOME is not set" >&2
-    exit 1
-fi
-
-# macOS support for 10.12.6 or higher
-if [ "$(uname -s)" = "Darwin" ]; then
-    macos_major=$(sw_vers -productVersion | cut -d '.' -f 2)
-    macos_minor=$(sw_vers -productVersion | cut -d '.' -f 3)
-    if [ "$macos_major" -lt 12 ] || { [ "$macos_major" -eq 12 ] && [ "$macos_minor" -lt 6 ]; }; then
-        echo "$0: macOS $(sw_vers -productVersion) is not supported, upgrade to 10.12.6 or higher"
-        exit 1
-    fi
-fi
-
-# Determine if we could use the multi-user installer or not
-if [ "$(uname -s)" = "Darwin" ]; then
-    echo "Note: a multi-user installation is possible. See https://nixos.org/nix/manual/#sect-multi-user-installation" >&2
-elif [ "$(uname -s)" = "Linux" ] && [ -e /run/systemd/system ]; then
-    echo "Note: a multi-user installation is possible. See https://nixos.org/nix/manual/#sect-multi-user-installation" >&2
-fi
-
-INSTALL_MODE=no-daemon
-# Trivially handle the --daemon / --no-daemon options
-if [ "x${1:-}" = "x--no-daemon" ]; then
-    INSTALL_MODE=no-daemon
-elif [ "x${1:-}" = "x--daemon" ]; then
-    INSTALL_MODE=daemon
-elif [ "x${1:-}" != "x" ]; then
-    (
-        echo "Nix Installer [--daemon|--no-daemon]"
-
-        echo "Choose installation method."
-        echo ""
-        echo " --daemon:    Installs and configures a background daemon that manages the store,"
-        echo "              providing multi-user support and better isolation for local builds."
-        echo "              Both for security and reproducibility, this method is recommended if"
-        echo "              supported on your platform."
-        echo "              See https://nixos.org/nix/manual/#sect-multi-user-installation"
-        echo ""
-        echo " --no-daemon: Simple, single-user installation that does not require root and is"
-        echo "              trivial to uninstall."
-        echo "              (default)"
-        echo ""
-    ) >&2
-    exit
-fi
-
-if [ "$INSTALL_MODE" = "daemon" ]; then
-    printf '\e[1;31mSwitching to the Daemon-based Installer\e[0m\n'
-    exec "$self/install-multi-user"
-    exit 0
-fi
-
-if [ "$(id -u)" -eq 0 ]; then
-    printf '\e[1;31mwarning: installing Nix as root is not supported by this script!\e[0m\n'
-fi
-
-echo "performing a single-user installation of Nix..." >&2
-
-if ! [ -e $dest ]; then
-    cmd="mkdir -m 0755 $dest && chown $USER $dest"
-    echo "directory $dest does not exist; creating it by running '$cmd' using sudo" >&2
-    if ! sudo sh -c "$cmd"; then
-        echo "$0: please manually run '$cmd' as root to create $dest" >&2
-        exit 1
-    fi
-fi
-
-if ! [ -w $dest ]; then
-    echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see http://nixos.org/nix/manual/#ssec-multi-user. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
-    exit 1
-fi
-
-mkdir -p $dest/store
-
-printf "copying Nix to %s..." "${dest}/store" >&2
-
-for i in $(cd "$self/store" >/dev/null && echo ./*); do
-    printf "." >&2
-    i_tmp="$dest/store/$i.$$"
-    if [ -e "$i_tmp" ]; then
-        rm -rf "$i_tmp"
-    fi
-    if ! [ -e "$dest/store/$i" ]; then
-        cp -Rp "$self/store/$i" "$i_tmp"
-        chmod -R a-w "$i_tmp"
-        chmod +w "$i_tmp"
-        mv "$i_tmp" "$dest/store/$i"
-        chmod -w "$dest/store/$i"
-    fi
-done
-echo "" >&2
-
-if ! "$nix/bin/nix-store" --load-db < "$self/.reginfo"; then
-    echo "$0: unable to register valid paths" >&2
-    exit 1
-fi
-
-. "$nix/etc/profile.d/nix.sh"
-
-if ! "$nix/bin/nix-env" -i "$nix"; then
-    echo "$0: unable to install Nix into your default profile" >&2
-    exit 1
-fi
-
-# Install an SSL certificate bundle.
-if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
-    $nix/bin/nix-env -i "$cacert"
-    export NIX_SSL_CERT_FILE="$HOME/.nix-profile/etc/ssl/certs/ca-bundle.crt"
-fi
-
-# Subscribe the user to the Nixpkgs channel and fetch it.
-if ! $nix/bin/nix-channel --list | grep -q "^nixpkgs "; then
-    $nix/bin/nix-channel --add https://nixos.org/channels/nixpkgs-unstable
-fi
-if [ -z "$_NIX_INSTALLER_TEST" ]; then
-    if ! $nix/bin/nix-channel --update nixpkgs; then
-        echo "Fetching the nixpkgs channel failed. (Are you offline?)"
-        echo "To try again later, run \"nix-channel --update nixpkgs\"."
-    fi
-fi
-
-added=
-p=$HOME/.nix-profile/etc/profile.d/nix.sh
-if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
-    # Make the shell source nix.sh during login.
-    for i in .bash_profile .bash_login .profile; do
-        fn="$HOME/$i"
-        if [ -w "$fn" ]; then
-            if ! grep -q "$p" "$fn"; then
-                echo "modifying $fn..." >&2
-                echo "if [ -e $p ]; then . $p; fi # added by Nix installer" >> "$fn"
-            fi
-            added=1
-            break
-        fi
-    done
-fi
-
-if [ -z "$added" ]; then
-    cat >&2 <<EOF
-
-Installation finished!  To ensure that the necessary environment
-variables are set, please add the line
-
-  . $p
-
-to your shell profile (e.g. ~/.profile).
-EOF
-else
-    cat >&2 <<EOF
-
-Installation finished!  To ensure that the necessary environment
-variables are set, either log in again, or type
-
-  . $p
-
-in your shell.
-EOF
-fi
diff --git a/third_party/nix/scripts/install-systemd-multi-user.sh b/third_party/nix/scripts/install-systemd-multi-user.sh
deleted file mode 100755
index bef3ac4f99..0000000000
--- a/third_party/nix/scripts/install-systemd-multi-user.sh
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/usr/bin/env bash
-
-set -eu
-set -o pipefail
-
-readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service
-readonly SERVICE_DEST=/etc/systemd/system/nix-daemon.service
-
-readonly SOCKET_SRC=/lib/systemd/system/nix-daemon.socket
-readonly SOCKET_DEST=/etc/systemd/system/nix-daemon.socket
-
-
-# Path for the systemd override unit file to contain the proxy settings
-readonly SERVICE_OVERRIDE=${SERVICE_DEST}.d/override.conf
-
-create_systemd_override() {
-     header "Configuring proxy for the nix-daemon service"
-    _sudo "create directory for systemd unit override" mkdir -p "$(dirname $SERVICE_OVERRIDE)"
-    cat <<EOF | _sudo "create systemd unit override" tee "$SERVICE_OVERRIDE"
-[Service]
-$1
-EOF
-}
-
-# Gather all non-empty proxy environment variables into a string
-create_systemd_proxy_env() {
-    vars="http_proxy https_proxy ftp_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY"
-    for v in $vars; do
-        if [ "x${!v:-}" != "x" ]; then
-            echo "Environment=${v}=${!v}"
-        fi
-    done
-}
-
-handle_network_proxy() {
-    # Create a systemd unit override with proxy environment variables
-    # if any proxy environment variables are not empty.
-    PROXY_ENV_STRING=$(create_systemd_proxy_env)
-    if [ -n "${PROXY_ENV_STRING}" ]; then
-        create_systemd_override "${PROXY_ENV_STRING}"
-    fi
-}
-
-poly_validate_assumptions() {
-    if [ "$(uname -s)" != "Linux" ]; then
-        failure "This script is for use with Linux!"
-    fi
-}
-
-poly_service_installed_check() {
-    [ "$(systemctl is-enabled nix-daemon.service)" = "linked" ] \
-        || [ "$(systemctl is-enabled nix-daemon.socket)" = "enabled" ]
-}
-
-poly_service_uninstall_directions() {
-        cat <<EOF
-$1. Delete the systemd service and socket units
-
-  sudo systemctl stop nix-daemon.socket
-  sudo systemctl stop nix-daemon.service
-  sudo systemctl disable nix-daemon.socket
-  sudo systemctl disable nix-daemon.service
-  sudo systemctl daemon-reload
-EOF
-}
-
-poly_service_setup_note() {
-    cat <<EOF
- - load and start a service (at $SERVICE_DEST
-   and $SOCKET_DEST) for nix-daemon
-
-EOF
-}
-
-poly_configure_nix_daemon_service() {
-    _sudo "to set up the nix-daemon service" \
-          systemctl link "/nix/var/nix/profiles/default$SERVICE_SRC"
-
-    _sudo "to set up the nix-daemon socket service" \
-          systemctl enable "/nix/var/nix/profiles/default$SOCKET_SRC"
-
-    handle_network_proxy
-
-    _sudo "to load the systemd unit for nix-daemon" \
-          systemctl daemon-reload
-
-    _sudo "to start the nix-daemon.socket" \
-          systemctl start nix-daemon.socket
-
-    _sudo "to start the nix-daemon.service" \
-          systemctl start nix-daemon.service
-
-}
-
-poly_group_exists() {
-    getent group "$1" > /dev/null 2>&1
-}
-
-poly_group_id_get() {
-    getent group "$1" | cut -d: -f3
-}
-
-poly_create_build_group() {
-    _sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
-          groupadd -g "$NIX_BUILD_GROUP_ID" --system \
-          "$NIX_BUILD_GROUP_NAME" >&2
-}
-
-poly_user_exists() {
-    getent passwd "$1" > /dev/null 2>&1
-}
-
-poly_user_id_get() {
-    getent passwd "$1" | cut -d: -f3
-}
-
-poly_user_hidden_get() {
-    echo "1"
-}
-
-poly_user_hidden_set() {
-    true
-}
-
-poly_user_home_get() {
-    getent passwd "$1" | cut -d: -f6
-}
-
-poly_user_home_set() {
-    _sudo "in order to give $1 a safe home directory" \
-          usermod --home "$2" "$1"
-}
-
-poly_user_note_get() {
-    getent passwd "$1" | cut -d: -f5
-}
-
-poly_user_note_set() {
-    _sudo "in order to give $1 a useful comment" \
-          usermod --comment "$2" "$1"
-}
-
-poly_user_shell_get() {
-    getent passwd "$1" | cut -d: -f7
-}
-
-poly_user_shell_set() {
-    _sudo "in order to prevent $1 from logging in" \
-          usermod --shell "$2" "$1"
-}
-
-poly_user_in_group_check() {
-    groups "$1" | grep -q "$2" > /dev/null 2>&1
-}
-
-poly_user_in_group_set() {
-    _sudo "Add $1 to the $2 group"\
-          usermod --append --groups "$2" "$1"
-}
-
-poly_user_primary_group_get() {
-    getent passwd "$1" | cut -d: -f4
-}
-
-poly_user_primary_group_set() {
-    _sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
-          usermod --gid "$2" "$1"
-
-}
-
-poly_create_build_user() {
-    username=$1
-    uid=$2
-    builder_num=$3
-
-    _sudo "Creating the Nix build user, $username" \
-          useradd \
-          --home-dir /var/empty \
-          --comment "Nix build user $builder_num" \
-          --gid "$NIX_BUILD_GROUP_ID" \
-          --groups "$NIX_BUILD_GROUP_NAME" \
-          --no-user-group \
-          --system \
-          --shell /sbin/nologin \
-          --uid "$uid" \
-          --password "!" \
-          "$username"
-}
diff --git a/third_party/nix/scripts/install.in b/third_party/nix/scripts/install.in
deleted file mode 100644
index 902758b138..0000000000
--- a/third_party/nix/scripts/install.in
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/sh
-
-# This script installs the Nix package manager on your system by
-# downloading a binary distribution and running its installer script
-# (which in turn creates and populates /nix).
-
-{ # Prevent execution if this script was only partially downloaded
-oops() {
-    echo "$0:" "$@" >&2
-    exit 1
-}
-
-tmpDir="$(mktemp -d -t nix-binary-tarball-unpack.XXXXXXXXXX || \
-          oops "Can't create temporary directory for downloading the Nix binary tarball")"
-cleanup() {
-    rm -rf "$tmpDir"
-}
-trap cleanup EXIT INT QUIT TERM
-
-require_util() {
-    command -v "$1" > /dev/null 2>&1 ||
-        oops "you do not have '$1' installed, which I need to $2"
-}
-
-case "$(uname -s).$(uname -m)" in
-    Linux.x86_64) system=x86_64-linux; hash=@binaryTarball_x86_64-linux@;;
-    Linux.i?86) system=i686-linux; hash=@binaryTarball_i686-linux@;;
-    Linux.aarch64) system=aarch64-linux; hash=@binaryTarball_aarch64-linux@;;
-    Darwin.x86_64) system=x86_64-darwin; hash=@binaryTarball_x86_64-darwin@;;
-    *) oops "sorry, there is no binary distribution of Nix for your platform";;
-esac
-
-url="https://nixos.org/releases/nix/nix-@nixVersion@/nix-@nixVersion@-$system.tar.xz"
-
-tarball="$tmpDir/$(basename "$tmpDir/nix-@nixVersion@-$system.tar.xz")"
-
-require_util curl "download the binary tarball"
-require_util tar "unpack the binary tarball"
-
-echo "downloading Nix @nixVersion@ binary tarball for $system from '$url' to '$tmpDir'..."
-curl -L "$url" -o "$tarball" || oops "failed to download '$url'"
-
-if command -v sha256sum > /dev/null 2>&1; then
-    hash2="$(sha256sum -b "$tarball" | cut -c1-64)"
-elif command -v shasum > /dev/null 2>&1; then
-    hash2="$(shasum -a 256 -b "$tarball" | cut -c1-64)"
-elif command -v openssl > /dev/null 2>&1; then
-    hash2="$(openssl dgst -r -sha256 "$tarball" | cut -c1-64)"
-else
-    oops "cannot verify the SHA-256 hash of '$url'; you need one of 'shasum', 'sha256sum', or 'openssl'"
-fi
-
-if [ "$hash" != "$hash2" ]; then
-    oops "SHA-256 hash mismatch in '$url'; expected $hash, got $hash2"
-fi
-
-unpack=$tmpDir/unpack
-mkdir -p "$unpack"
-tar -xf "$tarball" -C "$unpack" || oops "failed to unpack '$url'"
-
-script=$(echo "$unpack"/*/install)
-
-[ -e "$script" ] || oops "installation script is missing from the binary tarball!"
-"$script" "$@"
-
-} # End of wrapping
diff --git a/third_party/nix/scripts/nix-http-export.cgi.in b/third_party/nix/scripts/nix-http-export.cgi.in
deleted file mode 100755
index 19a505af1c..0000000000
--- a/third_party/nix/scripts/nix-http-export.cgi.in
+++ /dev/null
@@ -1,51 +0,0 @@
-#! /bin/sh
-
-export HOME=/tmp
-export NIX_REMOTE=daemon
-
-TMP_DIR="${TMP_DIR:-/tmp/nix-export}"
-
-@coreutils@/mkdir -p "$TMP_DIR" || true
-@coreutils@/chmod a+r "$TMP_DIR"
-
-needed_path="?$QUERY_STRING"
-needed_path="${needed_path#*[?&]needed_path=}"
-needed_path="${needed_path%%&*}"
-#needed_path="$(echo $needed_path  | ./unhttp)"
-needed_path="${needed_path//%2B/+}"
-needed_path="${needed_path//%3D/=}"
-
-echo needed_path: "$needed_path" >&2
-
-NIX_STORE="${NIX_STORE_DIR:-/nix/store}"
-
-echo NIX_STORE: "${NIX_STORE}" >&2
-
-full_path="${NIX_STORE}"/"$needed_path"
-
-if [ "$needed_path" != "${needed_path%.drv}" ]; then
-	echo "Status: 403 You should create the derivation file yourself"
-	echo "Content-Type: text/plain"
-	echo
-	echo "Refusing to disclose derivation contents"
-	exit
-fi
-
-if @bindir@/nix-store --check-validity "$full_path"; then
-	if ! [ -e nix-export/"$needed_path".nar.gz ]; then
-		@bindir@/nix-store --export "$full_path" | @gzip@ > "$TMP_DIR"/"$needed_path".nar.gz
-		@coreutils@/ln -fs  "$TMP_DIR"/"$needed_path".nar.gz nix-export/"$needed_path".nar.gz 
-	fi;
-	echo "Status: 301 Moved"
-	echo "Location: nix-export/"$needed_path".nar.gz"
-	echo
-else 
-	echo "Status: 404 No such path found"
-	echo "Content-Type: text/plain"
-	echo
-	echo "Path not found:"
-	echo "$needed_path"
-	echo "checked:"
-	echo "$full_path"
-fi
-
diff --git a/third_party/nix/scripts/nix-profile-daemon.sh.in b/third_party/nix/scripts/nix-profile-daemon.sh.in
deleted file mode 100644
index 47655080a6..0000000000
--- a/third_party/nix/scripts/nix-profile-daemon.sh.in
+++ /dev/null
@@ -1,29 +0,0 @@
-# Only execute this file once per shell.
-if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
-__ETC_PROFILE_NIX_SOURCED=1
-
-export NIX_USER_PROFILE_DIR="@localstatedir@/nix/profiles/per-user/$USER"
-export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
-
-# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
-if [ ! -z "${NIX_SSL_CERT_FILE:-}" ]; then
-    : # Allow users to override the NIX_SSL_CERT_FILE
-elif [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
-    export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
-elif [ -e /etc/ssl/ca-bundle.pem ]; then # openSUSE Tumbleweed
-    export NIX_SSL_CERT_FILE=/etc/ssl/ca-bundle.pem
-elif [ -e /etc/ssl/certs/ca-bundle.crt ]; then # Old NixOS
-    export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
-elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
-    export NIX_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
-else
-  # Fall back to what is in the nix profiles, favouring whatever is defined last.
-  for i in $NIX_PROFILES; do
-    if [ -e $i/etc/ssl/certs/ca-bundle.crt ]; then
-      export NIX_SSL_CERT_FILE=$i/etc/ssl/certs/ca-bundle.crt
-    fi
-  done
-fi
-
-export NIX_PATH="nixpkgs=@localstatedir@/nix/profiles/per-user/root/channels/nixpkgs:@localstatedir@/nix/profiles/per-user/root/channels"
-export PATH="$HOME/.nix-profile/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
diff --git a/third_party/nix/scripts/nix-profile.sh.in b/third_party/nix/scripts/nix-profile.sh.in
deleted file mode 100644
index e15f7cd46b..0000000000
--- a/third_party/nix/scripts/nix-profile.sh.in
+++ /dev/null
@@ -1,39 +0,0 @@
-if [ -n "$HOME" ] && [ -n "$USER" ]; then
-
-    # Set up the per-user profile.
-    # This part should be kept in sync with nixpkgs:nixos/modules/programs/shell.nix
-
-    NIX_LINK=$HOME/.nix-profile
-
-    NIX_USER_PROFILE_DIR=@localstatedir@/nix/profiles/per-user/$USER
-
-    # Append ~/.nix-defexpr/channels to $NIX_PATH so that <nixpkgs>
-    # paths work when the user has fetched the Nixpkgs channel.
-    export NIX_PATH=${NIX_PATH:+$NIX_PATH:}$HOME/.nix-defexpr/channels
-
-    # Set up environment.
-    # This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
-    export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
-
-    # Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
-    if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
-        export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
-    elif [ -e /etc/ssl/ca-bundle.pem ]; then # openSUSE Tumbleweed
-        export NIX_SSL_CERT_FILE=/etc/ssl/ca-bundle.pem
-    elif [ -e /etc/ssl/certs/ca-bundle.crt ]; then # Old NixOS
-        export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
-    elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
-        export NIX_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
-    elif [ -e "$NIX_LINK/etc/ssl/certs/ca-bundle.crt" ]; then # fall back to cacert in Nix profile
-        export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
-    elif [ -e "$NIX_LINK/etc/ca-bundle.crt" ]; then # old cacert in Nix profile
-        export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ca-bundle.crt"
-    fi
-
-    if [ -n "${MANPATH-}" ]; then
-        export MANPATH="$NIX_LINK/share/man:$MANPATH"
-    fi
-
-    export PATH="$NIX_LINK/bin:$PATH"
-    unset NIX_LINK NIX_USER_PROFILE_DIR
-fi
diff --git a/third_party/nix/scripts/nix-reduce-build.in b/third_party/nix/scripts/nix-reduce-build.in
deleted file mode 100755
index 50beb9d10b..0000000000
--- a/third_party/nix/scripts/nix-reduce-build.in
+++ /dev/null
@@ -1,171 +0,0 @@
-#! @bash@
-
-WORKING_DIRECTORY=$(mktemp -d "${TMPDIR:-/tmp}"/nix-reduce-build-XXXXXX);
-cd "$WORKING_DIRECTORY";
-
-if test -z "$1" || test "a--help" = "a$1" ; then
-	echo 'nix-reduce-build (paths or Nix expressions) -- (package sources)' >&2
-	echo As in: >&2
-	echo nix-reduce-build /etc/nixos/nixos -- ssh://user@somewhere.nowhere.example.org >&2
-	echo nix-reduce-build /etc/nixos/nixos -- \\
-	echo "   " \''http://somewhere.nowhere.example.org/nix/nix-http-export.cgi?needed_path='\' >&2
-	echo "  store path name will be added into the end of the URL" >&2
-	echo nix-reduce-build /etc/nixos/nixos -- file://home/user/nar/ >&2
-	echo "  that should be a directory where gzipped 'nix-store --export' ">&2
-	echo "  files are located (they should have .nar.gz extension)"  >&2
-	echo "        Or all together: " >&2
-	echo -e nix-reduce-build /expr.nix /e2.nix -- \\\\\\\n\
-	"    ssh://a@b.example.com http://n.example.com/get-nar?q= file://nar/" >&2
-	echo "        Also supports best-effort local builds of failing expression set:" >&2
-	echo "nix-reduce-build /e.nix -- nix-daemon:// nix-self://" >&2
-	echo "  nix-daemon:// builds using daemon"
-	echo "  nix-self:// builds directly using nix-store from current installation" >&2
-	echo "  nix-daemon-fixed:// and nix-self-fixed:// do the same, but only for" >&2;
-	echo "derivations with specified output hash (sha256, sha1 or md5)." >&2
-	echo "  nix-daemon-substitute:// and nix-self-substitute:// try to substitute" >&2;
-	echo "maximum amount of paths" >&2;
-	echo "  nix-daemon-build:// and nix-self-build:// try to build (not substitute)" >&2;
-	echo "maximum amount of paths" >&2;
-	echo "        If no package sources are specified, required paths are listed." >&2;
-	exit;
-fi;
-
-while ! test "$1" = "--" || test "$1" = "" ; do 
-	echo "$1" >> initial; >&2
-	shift;
-done
-shift;
-echo Will work on $(cat initial | wc -l) targets. >&2
-
-while read ; do
-	case "$REPLY" in 
-		${NIX_STORE_DIR:-/nix/store}/*)
-			echo "$REPLY" >> paths; >&2
-			;;
-		*)
-			(
-				IFS=: ;
-				nix-instantiate $REPLY >> paths;
-			);
-			;;
-	esac;
-done < initial;
-echo Proceeding $(cat paths | wc -l) paths. >&2
-
-while read; do
-	case "$REPLY" in
-		*.drv)
-			echo "$REPLY" >> derivers; >&2
-			;;
-		*)
-			nix-store --query --deriver "$REPLY" >>derivers;
-			;;
-	esac;
-done < paths;
-echo Found $(cat derivers | wc -l) derivers. >&2
-
-cat derivers | xargs nix-store --query -R > derivers-closure;
-echo Proceeding at most $(cat derivers-closure | wc -l) derivers. >&2
-
-cat derivers-closure | egrep '[.]drv$' | xargs nix-store --query --outputs > wanted-paths;
-cat derivers-closure | egrep -v '[.]drv$' >> wanted-paths;
-echo Prepared $(cat wanted-paths | wc -l) paths to get. >&2
-
-cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
-echo We need $(cat needed-paths | wc -l) paths. >&2
-
-egrep '[.]drv$' derivers-closure > critical-derivers;
-
-if test -z "$1" ; then
-	cat needed-paths;	
-fi;
-
-refresh_critical_derivers() {
-    echo "Finding needed derivers..." >&2;
-    cat critical-derivers | while read; do
-        if ! (nix-store --query --outputs "$REPLY" | xargs nix-store --check-validity &> /dev/null;); then
-            echo "$REPLY";
-        fi;
-    done > new-critical-derivers;
-    mv new-critical-derivers critical-derivers;
-    echo The needed paths are realized by $(cat critical-derivers | wc -l) derivers. >&2
-}
-
-build_here() {
-    cat critical-derivers | while read; do 
-        echo "Realising $REPLY using nix-daemon" >&2
-        @bindir@/nix-store -r "${REPLY}"
-    done;
-}
-
-try_to_substitute(){
-    cat needed-paths | while read ; do 
-        echo "Building $REPLY using nix-daemon" >&2
-        @bindir@/nix-store -r "${NIX_STORE_DIR:-/nix/store}/${REPLY##*/}"
-    done;
-}
-
-for i in "$@"; do 
-	sshHost="${i#ssh://}";
-	httpHost="${i#http://}";
-	httpsHost="${i#https://}";
-	filePath="${i#file:/}";
-	if [ "$i" != "$sshHost" ]; then
-		cat needed-paths | while read; do 
-			echo "Getting $REPLY and its closure over ssh" >&2
-			nix-copy-closure --from "$sshHost" --gzip "$REPLY" </dev/null || true; 
-		done;
-	elif [ "$i" != "$httpHost" ] || [ "$i" != "$httpsHost" ]; then
-		cat needed-paths | while read; do
-			echo "Getting $REPLY over http/https" >&2
-			curl ${BAD_CERTIFICATE:+-k} -L "$i${REPLY##*/}" | gunzip | nix-store --import;
-		done;
-	elif [ "$i" != "$filePath" ] ; then
-		cat needed-paths | while read; do 
-			echo "Installing $REPLY from file" >&2
-			gunzip < "$filePath/${REPLY##*/}".nar.gz | nix-store --import;
-		done;
-	elif [ "$i" = "nix-daemon://" ] ; then
-		NIX_REMOTE=daemon try_to_substitute;
-		refresh_critical_derivers;
-		NIX_REMOTE=daemon build_here;
-	elif [ "$i" = "nix-self://" ] ; then
-		NIX_REMOTE= try_to_substitute;
-		refresh_critical_derivers;
-		NIX_REMOTE= build_here;
-	elif [ "$i" = "nix-daemon-fixed://" ] ; then
-		refresh_critical_derivers;
-
-		cat critical-derivers | while read; do 
-			if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
-				echo "Realising $REPLY using nix-daemon" >&2
-				NIX_REMOTE=daemon @bindir@/nix-store -r "${REPLY}"
-			fi;
-		done;
-	elif [ "$i" = "nix-self-fixed://" ] ; then
-		refresh_critical_derivers;
-
-		cat critical-derivers | while read; do 
-			if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
-				echo "Realising $REPLY using direct Nix build" >&2
-				NIX_REMOTE= @bindir@/nix-store -r "${REPLY}"
-			fi;
-		done;
-	elif [ "$i" = "nix-daemon-substitute://" ] ; then
-		NIX_REMOTE=daemon try_to_substitute;
-	elif [ "$i" = "nix-self-substitute://" ] ; then
-		NIX_REMOTE= try_to_substitute;
-	elif [ "$i" = "nix-daemon-build://" ] ; then
-		refresh_critical_derivers;
-		NIX_REMOTE=daemon build_here;
-	elif [ "$i" = "nix-self-build://" ] ; then
-		refresh_critical_derivers;
-		NIX_REMOTE= build_here;
-	fi;
-	mv needed-paths wanted-paths;
-	cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
-	echo We still need $(cat needed-paths | wc -l) paths. >&2
-done;
-
-cd /
-rm -r "$WORKING_DIRECTORY"
diff --git a/third_party/nix/scripts/repl.sh b/third_party/nix/scripts/repl.sh
deleted file mode 100755
index d068e80790..0000000000
--- a/third_party/nix/scripts/repl.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env bash
-set -eo pipefail
-
-# Run `nix repl` using a local store, for use during development. Intended to
-# be run from the cmake build directory
-
-if [ "$#" -gt 0 ] && [ "$1" = "--debug" ]; then
-    gdb=(gdb --args)
-    shift 1
-elif [ "$1" = "--rr" ]; then
-    gdb=(rr record)
-    shift 1
-else
-    gdb=()
-fi
-
-make -j 10
-NIX_STORE_DIR=$(pwd)/nix/store \
-    NIX_LOG_DIR=$(pwd)/nix/var/log/nix \
-    NIX_STATE_DIR=$(pwd)/nix/var/nix \
-    XDG_CACHE_HOME=$(pwd)/cache \
-    NIX_REMOTE=daemon \
-    ${gdb[*]} ./src/nix repl "$@"
diff --git a/third_party/nix/scripts/setup_store.sh b/third_party/nix/scripts/setup_store.sh
deleted file mode 100755
index ee96c8d3b8..0000000000
--- a/third_party/nix/scripts/setup_store.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-# Setup a store for local development rooted at the current directory, and
-# compatible with the scripts in this directory (repl.sh, build.sh, eval.sh,
-# daemon.sh, etc). Intended to be run from the cmake build directory
-
-mkdir -p nix/store nix/var/nix nix/var/log/nix
-ln -s $(pwd)/src/nix ./nix/build-remote
-mkdir -p $(dirname "$(pwd)${SANDBOX_SHELL}")
-cp "${SANDBOX_SHELL}" "$(pwd)${SANDBOX_SHELL}"
diff --git a/third_party/nix/src/CMakeLists.txt b/third_party/nix/src/CMakeLists.txt
deleted file mode 100644
index 486c69fa2a..0000000000
--- a/third_party/nix/src/CMakeLists.txt
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- mode: cmake; -*-
-
-# The 'nix' binary is composed of various sources below this
-# directory. In the previous build system, they were all built from
-# this location and this setup mimics that (with the exception of the
-# various Nix libraries).
-
-add_subdirectory(proto)
-add_subdirectory(libutil)
-add_subdirectory(libstore)
-add_subdirectory(libmain)
-add_subdirectory(libexpr)
-add_subdirectory(nix-daemon)
-
-if (PACKAGE_TESTS)
-  add_subdirectory(tests)
-endif()
-
-add_executable(nix)
-set_property(TARGET nix PROPERTY CXX_STANDARD 17)
-include_directories(${PROJECT_BINARY_DIR})
-target_include_directories(nix PUBLIC "${nix_SOURCE_DIR}/src")
-
-target_sources(nix
-  PRIVATE
-    nix/command.hh
-    nix/legacy.hh
-    nix-env/user-env.hh
-    nix-store/dotgraph.hh
-    nix-store/graphml.hh
-
-    nix/add-to-store.cc
-    nix/build.cc
-    nix/cat.cc
-    nix/command.cc
-    nix/copy.cc
-    nix/doctor.cc
-    nix/dump-path.cc
-    nix/edit.cc
-    nix/eval.cc
-    nix/hash.cc
-    nix/installables.cc
-    nix/legacy.cc
-    nix/log.cc
-    nix/ls.cc
-    nix/main.cc
-    nix/optimise-store.cc
-    nix/path-info.cc
-    nix/ping-store.cc
-    nix/repl.cc
-    nix/run.cc
-    nix/search.cc
-    nix/show-config.cc
-    nix/show-derivation.cc
-    nix/sigs.cc
-    nix/upgrade-nix.cc
-    nix/verify.cc
-    nix/why-depends.cc
-
-    build-remote/build-remote.cc
-    nix-build/nix-build.cc
-    nix-channel/nix-channel.cc
-    nix-collect-garbage/nix-collect-garbage.cc
-    nix-copy-closure/nix-copy-closure.cc
-    nix-env/nix-env.cc
-    nix-env/user-env.cc
-    nix-instantiate/nix-instantiate.cc
-    nix-prefetch-url/nix-prefetch-url.cc
-    nix-store/dotgraph.cc
-    nix-store/graphml.cc
-    nix-store/nix-store.cc
-)
-
-target_link_libraries(nix
-  nixexpr
-  nixmain
-  nixstore
-  nixutil
-
-  absl::strings
-  editline
-  glog
-)
-
-INSTALL(TARGETS nix DESTINATION bin)
diff --git a/third_party/nix/src/build-remote/build-remote.cc b/third_party/nix/src/build-remote/build-remote.cc
deleted file mode 100644
index 43564a5eb7..0000000000
--- a/third_party/nix/src/build-remote/build-remote.cc
+++ /dev/null
@@ -1,274 +0,0 @@
-#include <algorithm>
-#include <cstdlib>
-#include <cstring>
-#include <iomanip>
-#include <memory>
-#include <set>
-#include <tuple>
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/match.h>
-#include <absl/strings/str_cat.h>
-#include <glog/logging.h>
-
-#include "libmain/shared.hh"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/local-store.hh"
-#include "libstore/machines.hh"
-#include "libstore/pathlocks.hh"
-#include "libstore/store-api.hh"
-#include "libutil/serialise.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-using std::cin;
-
-static void handleAlarm(int sig) {}
-
-std::string escapeUri(std::string uri) {
-  std::replace(uri.begin(), uri.end(), '/', '_');
-  return uri;
-}
-
-static std::string currentLoad;
-
-static AutoCloseFD openSlotLock(const Machine& m, unsigned long long slot) {
-  return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri), slot),
-                      true);
-}
-
-static bool allSupportedLocally(const std::set<std::string>& requiredFeatures) {
-  for (auto& feature : requiredFeatures) {
-    if (settings.systemFeatures.get().count(feature) == 0u) {
-      return false;
-    }
-  }
-  return true;
-}
-
-static int _main(int argc, char* argv[]) {
-  {
-    /* Ensure we don't get any SSH passphrase or host key popups. */
-    unsetenv("DISPLAY");
-    unsetenv("SSH_ASKPASS");
-
-    FdSource source(STDIN_FILENO);
-
-    /* Read the parent's settings. */
-    while (readInt(source) != 0u) {
-      auto name = readString(source);
-      auto value = readString(source);
-      settings.set(name, value);
-    }
-
-    settings.maxBuildJobs.set("1");  // hack to make tests with local?root= work
-
-    auto store = openStore().cast<LocalStore>();
-
-    /* It would be more appropriate to use $XDG_RUNTIME_DIR, since
-       that gets cleared on reboot, but it wouldn't work on macOS. */
-    currentLoad = store->stateDir + "/current-load";
-
-    std::shared_ptr<Store> sshStore;
-    AutoCloseFD bestSlotLock;
-
-    auto machines = getMachines();
-    DLOG(INFO) << "got " << machines.size() << " remote builders";
-
-    if (machines.empty()) {
-      std::cerr << "# decline-permanently\n";
-      return 0;
-    }
-
-    std::string drvPath;
-    std::string storeUri;
-
-    while (true) {
-      try {
-        auto s = readString(source);
-        if (s != "try") {
-          return 0;
-        }
-      } catch (EndOfFile&) {
-        return 0;
-      }
-
-      auto amWilling = readInt(source);
-      auto neededSystem = readString(source);
-      source >> drvPath;
-      auto requiredFeatures = readStrings<std::set<std::string>>(source);
-
-      auto canBuildLocally =
-          (amWilling != 0u) &&
-          (neededSystem == settings.thisSystem ||
-           settings.extraPlatforms.get().count(neededSystem) > 0) &&
-          allSupportedLocally(requiredFeatures);
-
-      /* Error ignored here, will be caught later */
-      mkdir(currentLoad.c_str(), 0777);
-
-      while (true) {
-        bestSlotLock = AutoCloseFD(-1);
-        AutoCloseFD lock(openLockFile(currentLoad + "/main-lock", true));
-        lockFile(lock.get(), ltWrite, true);
-
-        bool rightType = false;
-
-        Machine* bestMachine = nullptr;
-        unsigned long long bestLoad = 0;
-        for (auto& m : machines) {
-          DLOG(INFO) << "considering building on remote machine '" << m.storeUri
-                     << "'";
-
-          if (m.enabled &&
-              std::find(m.systemTypes.begin(), m.systemTypes.end(),
-                        neededSystem) != m.systemTypes.end() &&
-              m.allSupported(requiredFeatures) &&
-              m.mandatoryMet(requiredFeatures)) {
-            rightType = true;
-            AutoCloseFD free;
-            unsigned long long load = 0;
-            for (unsigned long long slot = 0; slot < m.maxJobs; ++slot) {
-              auto slotLock = openSlotLock(m, slot);
-              if (lockFile(slotLock.get(), ltWrite, false)) {
-                if (!free) {
-                  free = std::move(slotLock);
-                }
-              } else {
-                ++load;
-              }
-            }
-            if (!free) {
-              continue;
-            }
-            bool best = false;
-            if (!bestMachine || !bestSlotLock) {
-              best = true;
-            } else if (load / m.speedFactor <
-                       bestLoad / bestMachine->speedFactor) {
-              best = true;
-            } else if (load / m.speedFactor ==
-                       bestLoad / bestMachine->speedFactor) {
-              if (m.speedFactor > bestMachine->speedFactor) {
-                best = true;
-              } else if (m.speedFactor == bestMachine->speedFactor) {
-                if (load < bestLoad) {
-                  best = true;
-                }
-              }
-            }
-            if (best) {
-              bestLoad = load;
-              bestSlotLock = std::move(free);
-              bestMachine = &m;
-            }
-          }
-        }
-
-        if (!bestSlotLock || !bestMachine) {
-          if (rightType && !canBuildLocally) {
-            std::cerr << "# postpone\n";
-          } else {
-            std::cerr << "# decline\n";
-          }
-          break;
-        }
-
-        futimens(bestSlotLock.get(), nullptr);
-
-        lock = AutoCloseFD(-1);
-
-        try {
-          DLOG(INFO) << "connecting to '" << bestMachine->storeUri << "'";
-
-          Store::Params storeParams;
-          if (absl::StartsWith(bestMachine->storeUri, "ssh://")) {
-            storeParams["max-connections"] = "1";
-            storeParams["log-fd"] = "4";
-            if (!bestMachine->sshKey.empty()) {
-              storeParams["ssh-key"] = bestMachine->sshKey;
-            }
-          }
-
-          sshStore = openStore(bestMachine->storeUri, storeParams);
-          sshStore->connect();
-          storeUri = bestMachine->storeUri;
-
-        } catch (std::exception& e) {
-          auto msg = absl::StripTrailingAsciiWhitespace(drainFD(5, false));
-          LOG(ERROR) << "cannot build on '" << bestMachine->storeUri
-                     << "': " << e.what()
-                     << (msg.empty() ? "" : absl::StrCat(": ", msg));
-          bestMachine->enabled = false;
-          continue;
-        }
-
-        goto connected;
-      }
-    }
-
-  connected:
-    close(5);
-
-    std::cerr << "# accept\n" << storeUri << "\n";
-
-    auto inputs = readStrings<PathSet>(source);
-    auto outputs = readStrings<PathSet>(source);
-
-    AutoCloseFD uploadLock = openLockFile(
-        currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true);
-
-    {
-      DLOG(INFO) << "waiting for the upload lock to '" << storeUri << "'";
-
-      auto old = signal(SIGALRM, handleAlarm);
-      alarm(15 * 60);
-      if (!lockFile(uploadLock.get(), ltWrite, true)) {
-        LOG(ERROR) << "somebody is hogging the upload lock, continuing...";
-      }
-      alarm(0);
-      signal(SIGALRM, old);
-    }
-
-    auto substitute =
-        settings.buildersUseSubstitutes ? Substitute : NoSubstitute;
-
-    {
-      DLOG(INFO) << "copying dependencies to '" << storeUri << "'";
-      copyPaths(store, ref<Store>(sshStore), inputs, NoRepair, NoCheckSigs,
-                substitute);
-    }
-
-    uploadLock = AutoCloseFD(-1);
-
-    BasicDerivation drv(
-        readDerivation(store->realStoreDir + "/" + baseNameOf(drvPath)));
-    drv.inputSrcs = inputs;
-
-    auto result = sshStore->buildDerivation(std::cerr, drvPath, drv);
-
-    if (!result.success()) {
-      throw Error("build of '%s' on '%s' failed: %s", drvPath, storeUri,
-                  result.errorMsg);
-    }
-
-    PathSet missing;
-    for (auto& path : outputs) {
-      if (!store->isValidPath(path)) {
-        missing.insert(path);
-      }
-    }
-
-    if (!missing.empty()) {
-      DLOG(INFO) << "copying outputs from '" << storeUri << "'";
-      store->locksHeld.insert(missing.begin(), missing.end()); /* FIXME: ugly */
-      copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs,
-                NoSubstitute);
-    }
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("build-remote", _main);
diff --git a/third_party/nix/src/cpptoml/LICENSE b/third_party/nix/src/cpptoml/LICENSE
deleted file mode 100644
index 8802c4fa5a..0000000000
--- a/third_party/nix/src/cpptoml/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
-Copyright (c) 2014 Chase Geigle
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/third_party/nix/src/cpptoml/cpptoml.h b/third_party/nix/src/cpptoml/cpptoml.h
deleted file mode 100644
index 150b53ff86..0000000000
--- a/third_party/nix/src/cpptoml/cpptoml.h
+++ /dev/null
@@ -1,3668 +0,0 @@
-/**
- * @file cpptoml.h
- * @author Chase Geigle
- * @date May 2013
- */
-
-#ifndef CPPTOML_H
-#define CPPTOML_H
-
-#include <algorithm>
-#include <cassert>
-#include <clocale>
-#include <cstdint>
-#include <cstring>
-#include <fstream>
-#include <iomanip>
-#include <map>
-#include <memory>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#if __cplusplus > 201103L
-#define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
-#elif defined(__clang__)
-#define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason)))
-#elif defined(__GNUG__)
-#define CPPTOML_DEPRECATED(reason) __attribute__((deprecated))
-#elif defined(_MSC_VER)
-#if _MSC_VER < 1910
-#define CPPTOML_DEPRECATED(reason) __declspec(deprecated)
-#else
-#define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
-#endif
-#endif
-
-namespace cpptoml
-{
-class writer; // forward declaration
-class base;   // forward declaration
-#if defined(CPPTOML_USE_MAP)
-// a std::map will ensure that entries a sorted, albeit at a slight
-// performance penalty relative to the (default) unordered_map
-using string_to_base_map = std::map<std::string, std::shared_ptr<base>>;
-#else
-// by default an unordered_map is used for best performance as the
-// toml specification does not require entries to be sorted
-using string_to_base_map
-    = std::unordered_map<std::string, std::shared_ptr<base>>;
-#endif
-
-// if defined, `base` will retain type information in form of an enum class
-// such that static_cast can be used instead of dynamic_cast
-// #define CPPTOML_NO_RTTI
-
-template <class T>
-class option
-{
-  public:
-    option() : empty_{true}
-    {
-        // nothing
-    }
-
-    option(T value) : empty_{false}, value_(std::move(value))
-    {
-        // nothing
-    }
-
-    explicit operator bool() const
-    {
-        return !empty_;
-    }
-
-    const T& operator*() const
-    {
-        return value_;
-    }
-
-    const T* operator->() const
-    {
-        return &value_;
-    }
-
-    template <class U>
-    T value_or(U&& alternative) const
-    {
-        if (!empty_)
-            return value_;
-        return static_cast<T>(std::forward<U>(alternative));
-    }
-
-  private:
-    bool empty_;
-    T value_;
-};
-
-struct local_date
-{
-    int year = 0;
-    int month = 0;
-    int day = 0;
-};
-
-struct local_time
-{
-    int hour = 0;
-    int minute = 0;
-    int second = 0;
-    int microsecond = 0;
-};
-
-struct zone_offset
-{
-    int hour_offset = 0;
-    int minute_offset = 0;
-};
-
-struct local_datetime : local_date, local_time
-{
-};
-
-struct offset_datetime : local_datetime, zone_offset
-{
-    static inline struct offset_datetime from_zoned(const struct tm& t)
-    {
-        offset_datetime dt;
-        dt.year = t.tm_year + 1900;
-        dt.month = t.tm_mon + 1;
-        dt.day = t.tm_mday;
-        dt.hour = t.tm_hour;
-        dt.minute = t.tm_min;
-        dt.second = t.tm_sec;
-
-        char buf[16];
-        strftime(buf, 16, "%z", &t);
-
-        int offset = std::stoi(buf);
-        dt.hour_offset = offset / 100;
-        dt.minute_offset = offset % 100;
-        return dt;
-    }
-
-    CPPTOML_DEPRECATED("from_local has been renamed to from_zoned")
-    static inline struct offset_datetime from_local(const struct tm& t)
-    {
-        return from_zoned(t);
-    }
-
-    static inline struct offset_datetime from_utc(const struct tm& t)
-    {
-        offset_datetime dt;
-        dt.year = t.tm_year + 1900;
-        dt.month = t.tm_mon + 1;
-        dt.day = t.tm_mday;
-        dt.hour = t.tm_hour;
-        dt.minute = t.tm_min;
-        dt.second = t.tm_sec;
-        return dt;
-    }
-};
-
-CPPTOML_DEPRECATED("datetime has been renamed to offset_datetime")
-typedef offset_datetime datetime;
-
-class fill_guard
-{
-  public:
-    fill_guard(std::ostream& os) : os_(os), fill_{os.fill()}
-    {
-        // nothing
-    }
-
-    ~fill_guard()
-    {
-        os_.fill(fill_);
-    }
-
-  private:
-    std::ostream& os_;
-    std::ostream::char_type fill_;
-};
-
-inline std::ostream& operator<<(std::ostream& os, const local_date& dt)
-{
-    fill_guard g{os};
-    os.fill('0');
-
-    using std::setw;
-    os << setw(4) << dt.year << "-" << setw(2) << dt.month << "-" << setw(2)
-       << dt.day;
-
-    return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, const local_time& ltime)
-{
-    fill_guard g{os};
-    os.fill('0');
-
-    using std::setw;
-    os << setw(2) << ltime.hour << ":" << setw(2) << ltime.minute << ":"
-       << setw(2) << ltime.second;
-
-    if (ltime.microsecond > 0)
-    {
-        os << ".";
-        int power = 100000;
-        for (int curr_us = ltime.microsecond; curr_us; power /= 10)
-        {
-            auto num = curr_us / power;
-            os << num;
-            curr_us -= num * power;
-        }
-    }
-
-    return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, const zone_offset& zo)
-{
-    fill_guard g{os};
-    os.fill('0');
-
-    using std::setw;
-
-    if (zo.hour_offset != 0 || zo.minute_offset != 0)
-    {
-        if (zo.hour_offset > 0)
-        {
-            os << "+";
-        }
-        else
-        {
-            os << "-";
-        }
-        os << setw(2) << std::abs(zo.hour_offset) << ":" << setw(2)
-           << std::abs(zo.minute_offset);
-    }
-    else
-    {
-        os << "Z";
-    }
-
-    return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, const local_datetime& dt)
-{
-    return os << static_cast<const local_date&>(dt) << "T"
-              << static_cast<const local_time&>(dt);
-}
-
-inline std::ostream& operator<<(std::ostream& os, const offset_datetime& dt)
-{
-    return os << static_cast<const local_datetime&>(dt)
-              << static_cast<const zone_offset&>(dt);
-}
-
-template <class T, class... Ts>
-struct is_one_of;
-
-template <class T, class V>
-struct is_one_of<T, V> : std::is_same<T, V>
-{
-};
-
-template <class T, class V, class... Ts>
-struct is_one_of<T, V, Ts...>
-{
-    const static bool value
-        = std::is_same<T, V>::value || is_one_of<T, Ts...>::value;
-};
-
-template <class T>
-class value;
-
-template <class T>
-struct valid_value
-    : is_one_of<T, std::string, int64_t, double, bool, local_date, local_time,
-                local_datetime, offset_datetime>
-{
-};
-
-template <class T, class Enable = void>
-struct value_traits;
-
-template <class T>
-struct valid_value_or_string_convertible
-{
-
-    const static bool value = valid_value<typename std::decay<T>::type>::value
-                              || std::is_convertible<T, std::string>::value;
-};
-
-template <class T>
-struct value_traits<T, typename std::enable_if<
-                           valid_value_or_string_convertible<T>::value>::type>
-{
-    using value_type = typename std::conditional<
-        valid_value<typename std::decay<T>::type>::value,
-        typename std::decay<T>::type, std::string>::type;
-
-    using type = value<value_type>;
-
-    static value_type construct(T&& val)
-    {
-        return value_type(val);
-    }
-};
-
-template <class T>
-struct value_traits<
-    T,
-    typename std::enable_if<
-        !valid_value_or_string_convertible<T>::value
-        && std::is_floating_point<typename std::decay<T>::type>::value>::type>
-{
-    using value_type = typename std::decay<T>::type;
-
-    using type = value<double>;
-
-    static value_type construct(T&& val)
-    {
-        return value_type(val);
-    }
-};
-
-template <class T>
-struct value_traits<
-    T, typename std::enable_if<
-           !valid_value_or_string_convertible<T>::value
-           && !std::is_floating_point<typename std::decay<T>::type>::value
-           && std::is_signed<typename std::decay<T>::type>::value>::type>
-{
-    using value_type = int64_t;
-
-    using type = value<int64_t>;
-
-    static value_type construct(T&& val)
-    {
-        if (val < (std::numeric_limits<int64_t>::min)())
-            throw std::underflow_error{"constructed value cannot be "
-                                       "represented by a 64-bit signed "
-                                       "integer"};
-
-        if (val > (std::numeric_limits<int64_t>::max)())
-            throw std::overflow_error{"constructed value cannot be represented "
-                                      "by a 64-bit signed integer"};
-
-        return static_cast<int64_t>(val);
-    }
-};
-
-template <class T>
-struct value_traits<
-    T, typename std::enable_if<
-           !valid_value_or_string_convertible<T>::value
-           && std::is_unsigned<typename std::decay<T>::type>::value>::type>
-{
-    using value_type = int64_t;
-
-    using type = value<int64_t>;
-
-    static value_type construct(T&& val)
-    {
-        if (val > static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
-            throw std::overflow_error{"constructed value cannot be represented "
-                                      "by a 64-bit signed integer"};
-
-        return static_cast<int64_t>(val);
-    }
-};
-
-class array;
-class table;
-class table_array;
-
-template <class T>
-struct array_of_trait
-{
-    using return_type = option<std::vector<T>>;
-};
-
-template <>
-struct array_of_trait<array>
-{
-    using return_type = option<std::vector<std::shared_ptr<array>>>;
-};
-
-template <class T>
-inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
-inline std::shared_ptr<array> make_array();
-
-namespace detail
-{
-template <class T>
-inline std::shared_ptr<T> make_element();
-}
-
-inline std::shared_ptr<table> make_table();
-inline std::shared_ptr<table_array> make_table_array(bool is_inline = false);
-
-#if defined(CPPTOML_NO_RTTI)
-/// Base type used to store underlying data type explicitly if RTTI is disabled
-enum class base_type
-{
-    NONE,
-    STRING,
-    LOCAL_TIME,
-    LOCAL_DATE,
-    LOCAL_DATETIME,
-    OFFSET_DATETIME,
-    INT,
-    FLOAT,
-    BOOL,
-    TABLE,
-    ARRAY,
-    TABLE_ARRAY
-};
-
-/// Type traits class to convert C++ types to enum member
-template <class T>
-struct base_type_traits;
-
-template <>
-struct base_type_traits<std::string>
-{
-    static const base_type type = base_type::STRING;
-};
-
-template <>
-struct base_type_traits<local_time>
-{
-    static const base_type type = base_type::LOCAL_TIME;
-};
-
-template <>
-struct base_type_traits<local_date>
-{
-    static const base_type type = base_type::LOCAL_DATE;
-};
-
-template <>
-struct base_type_traits<local_datetime>
-{
-    static const base_type type = base_type::LOCAL_DATETIME;
-};
-
-template <>
-struct base_type_traits<offset_datetime>
-{
-    static const base_type type = base_type::OFFSET_DATETIME;
-};
-
-template <>
-struct base_type_traits<int64_t>
-{
-    static const base_type type = base_type::INT;
-};
-
-template <>
-struct base_type_traits<double>
-{
-    static const base_type type = base_type::FLOAT;
-};
-
-template <>
-struct base_type_traits<bool>
-{
-    static const base_type type = base_type::BOOL;
-};
-
-template <>
-struct base_type_traits<table>
-{
-    static const base_type type = base_type::TABLE;
-};
-
-template <>
-struct base_type_traits<array>
-{
-    static const base_type type = base_type::ARRAY;
-};
-
-template <>
-struct base_type_traits<table_array>
-{
-    static const base_type type = base_type::TABLE_ARRAY;
-};
-#endif
-
-/**
- * A generic base TOML value used for type erasure.
- */
-class base : public std::enable_shared_from_this<base>
-{
-  public:
-    virtual ~base() = default;
-
-    virtual std::shared_ptr<base> clone() const = 0;
-
-    /**
-     * Determines if the given TOML element is a value.
-     */
-    virtual bool is_value() const
-    {
-        return false;
-    }
-
-    /**
-     * Determines if the given TOML element is a table.
-     */
-    virtual bool is_table() const
-    {
-        return false;
-    }
-
-    /**
-     * Converts the TOML element into a table.
-     */
-    std::shared_ptr<table> as_table()
-    {
-        if (is_table())
-            return std::static_pointer_cast<table>(shared_from_this());
-        return nullptr;
-    }
-    /**
-     * Determines if the TOML element is an array of "leaf" elements.
-     */
-    virtual bool is_array() const
-    {
-        return false;
-    }
-
-    /**
-     * Converts the TOML element to an array.
-     */
-    std::shared_ptr<array> as_array()
-    {
-        if (is_array())
-            return std::static_pointer_cast<array>(shared_from_this());
-        return nullptr;
-    }
-
-    /**
-     * Determines if the given TOML element is an array of tables.
-     */
-    virtual bool is_table_array() const
-    {
-        return false;
-    }
-
-    /**
-     * Converts the TOML element into a table array.
-     */
-    std::shared_ptr<table_array> as_table_array()
-    {
-        if (is_table_array())
-            return std::static_pointer_cast<table_array>(shared_from_this());
-        return nullptr;
-    }
-
-    /**
-     * Attempts to coerce the TOML element into a concrete TOML value
-     * of type T.
-     */
-    template <class T>
-    std::shared_ptr<value<T>> as();
-
-    template <class T>
-    std::shared_ptr<const value<T>> as() const;
-
-    template <class Visitor, class... Args>
-    void accept(Visitor&& visitor, Args&&... args) const;
-
-#if defined(CPPTOML_NO_RTTI)
-    base_type type() const
-    {
-        return type_;
-    }
-
-  protected:
-    base(const base_type t) : type_(t)
-    {
-        // nothing
-    }
-
-  private:
-    const base_type type_ = base_type::NONE;
-
-#else
-  protected:
-    base()
-    {
-        // nothing
-    }
-#endif
-};
-
-/**
- * A concrete TOML value representing the "leaves" of the "tree".
- */
-template <class T>
-class value : public base
-{
-    struct make_shared_enabler
-    {
-        // nothing; this is a private key accessible only to friends
-    };
-
-    template <class U>
-    friend std::shared_ptr<typename value_traits<U>::type>
-    cpptoml::make_value(U&& val);
-
-  public:
-    static_assert(valid_value<T>::value, "invalid value type");
-
-    std::shared_ptr<base> clone() const override;
-
-    value(const make_shared_enabler&, const T& val) : value(val)
-    {
-        // nothing; note that users cannot actually invoke this function
-        // because they lack access to the make_shared_enabler.
-    }
-
-    bool is_value() const override
-    {
-        return true;
-    }
-
-    /**
-     * Gets the data associated with this value.
-     */
-    T& get()
-    {
-        return data_;
-    }
-
-    /**
-     * Gets the data associated with this value. Const version.
-     */
-    const T& get() const
-    {
-        return data_;
-    }
-
-  private:
-    T data_;
-
-    /**
-     * Constructs a value from the given data.
-     */
-#if defined(CPPTOML_NO_RTTI)
-    value(const T& val) : base(base_type_traits<T>::type), data_(val)
-    {
-    }
-#else
-    value(const T& val) : data_(val)
-    {
-    }
-#endif
-
-    value(const value& val) = delete;
-    value& operator=(const value& val) = delete;
-};
-
-template <class T>
-std::shared_ptr<typename value_traits<T>::type> make_value(T&& val)
-{
-    using value_type = typename value_traits<T>::type;
-    using enabler = typename value_type::make_shared_enabler;
-    return std::make_shared<value_type>(
-        enabler{}, value_traits<T>::construct(std::forward<T>(val)));
-}
-
-template <class T>
-inline std::shared_ptr<value<T>> base::as()
-{
-#if defined(CPPTOML_NO_RTTI)
-    if (type() == base_type_traits<T>::type)
-        return std::static_pointer_cast<value<T>>(shared_from_this());
-    else
-        return nullptr;
-#else
-    return std::dynamic_pointer_cast<value<T>>(shared_from_this());
-#endif
-}
-
-// special case value<double> to allow getting an integer parameter as a
-// double value
-template <>
-inline std::shared_ptr<value<double>> base::as()
-{
-#if defined(CPPTOML_NO_RTTI)
-    if (type() == base_type::FLOAT)
-        return std::static_pointer_cast<value<double>>(shared_from_this());
-
-    if (type() == base_type::INT)
-    {
-        auto v = std::static_pointer_cast<value<int64_t>>(shared_from_this());
-        return make_value<double>(static_cast<double>(v->get()));
-    }
-#else
-    if (auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this()))
-        return v;
-
-    if (auto v = std::dynamic_pointer_cast<value<int64_t>>(shared_from_this()))
-        return make_value<double>(static_cast<double>(v->get()));
-#endif
-
-    return nullptr;
-}
-
-template <class T>
-inline std::shared_ptr<const value<T>> base::as() const
-{
-#if defined(CPPTOML_NO_RTTI)
-    if (type() == base_type_traits<T>::type)
-        return std::static_pointer_cast<const value<T>>(shared_from_this());
-    else
-        return nullptr;
-#else
-    return std::dynamic_pointer_cast<const value<T>>(shared_from_this());
-#endif
-}
-
-// special case value<double> to allow getting an integer parameter as a
-// double value
-template <>
-inline std::shared_ptr<const value<double>> base::as() const
-{
-#if defined(CPPTOML_NO_RTTI)
-    if (type() == base_type::FLOAT)
-        return std::static_pointer_cast<const value<double>>(
-            shared_from_this());
-
-    if (type() == base_type::INT)
-    {
-        auto v = as<int64_t>();
-        // the below has to be a non-const value<double> due to a bug in
-        // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843
-        return make_value<double>(static_cast<double>(v->get()));
-    }
-#else
-    if (auto v
-        = std::dynamic_pointer_cast<const value<double>>(shared_from_this()))
-        return v;
-
-    if (auto v = as<int64_t>())
-    {
-        // the below has to be a non-const value<double> due to a bug in
-        // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843
-        return make_value<double>(static_cast<double>(v->get()));
-    }
-#endif
-
-    return nullptr;
-}
-
-/**
- * Exception class for array insertion errors.
- */
-class array_exception : public std::runtime_error
-{
-  public:
-    array_exception(const std::string& err) : std::runtime_error{err}
-    {
-    }
-};
-
-class array : public base
-{
-  public:
-    friend std::shared_ptr<array> make_array();
-
-    std::shared_ptr<base> clone() const override;
-
-    virtual bool is_array() const override
-    {
-        return true;
-    }
-
-    using size_type = std::size_t;
-
-    /**
-     * arrays can be iterated over
-     */
-    using iterator = std::vector<std::shared_ptr<base>>::iterator;
-
-    /**
-     * arrays can be iterated over.  Const version.
-     */
-    using const_iterator = std::vector<std::shared_ptr<base>>::const_iterator;
-
-    iterator begin()
-    {
-        return values_.begin();
-    }
-
-    const_iterator begin() const
-    {
-        return values_.begin();
-    }
-
-    iterator end()
-    {
-        return values_.end();
-    }
-
-    const_iterator end() const
-    {
-        return values_.end();
-    }
-
-    /**
-     * Obtains the array (vector) of base values.
-     */
-    std::vector<std::shared_ptr<base>>& get()
-    {
-        return values_;
-    }
-
-    /**
-     * Obtains the array (vector) of base values. Const version.
-     */
-    const std::vector<std::shared_ptr<base>>& get() const
-    {
-        return values_;
-    }
-
-    std::shared_ptr<base> at(size_t idx) const
-    {
-        return values_.at(idx);
-    }
-
-    /**
-     * Obtains an array of value<T>s. Note that elements may be
-     * nullptr if they cannot be converted to a value<T>.
-     */
-    template <class T>
-    std::vector<std::shared_ptr<value<T>>> array_of() const
-    {
-        std::vector<std::shared_ptr<value<T>>> result(values_.size());
-
-        std::transform(values_.begin(), values_.end(), result.begin(),
-                       [&](std::shared_ptr<base> v) { return v->as<T>(); });
-
-        return result;
-    }
-
-    /**
-     * Obtains a option<std::vector<T>>. The option will be empty if the array
-     * contains values that are not of type T.
-     */
-    template <class T>
-    inline typename array_of_trait<T>::return_type get_array_of() const
-    {
-        std::vector<T> result;
-        result.reserve(values_.size());
-
-        for (const auto& val : values_)
-        {
-            if (auto v = val->as<T>())
-                result.push_back(v->get());
-            else
-                return {};
-        }
-
-        return {std::move(result)};
-    }
-
-    /**
-     * Obtains an array of arrays. Note that elements may be nullptr
-     * if they cannot be converted to a array.
-     */
-    std::vector<std::shared_ptr<array>> nested_array() const
-    {
-        std::vector<std::shared_ptr<array>> result(values_.size());
-
-        std::transform(values_.begin(), values_.end(), result.begin(),
-                       [&](std::shared_ptr<base> v) -> std::shared_ptr<array> {
-                           if (v->is_array())
-                               return std::static_pointer_cast<array>(v);
-                           return std::shared_ptr<array>{};
-                       });
-
-        return result;
-    }
-
-    /**
-     * Add a value to the end of the array
-     */
-    template <class T>
-    void push_back(const std::shared_ptr<value<T>>& val)
-    {
-        if (values_.empty() || values_[0]->as<T>())
-        {
-            values_.push_back(val);
-        }
-        else
-        {
-            throw array_exception{"Arrays must be homogenous."};
-        }
-    }
-
-    /**
-     * Add an array to the end of the array
-     */
-    void push_back(const std::shared_ptr<array>& val)
-    {
-        if (values_.empty() || values_[0]->is_array())
-        {
-            values_.push_back(val);
-        }
-        else
-        {
-            throw array_exception{"Arrays must be homogenous."};
-        }
-    }
-
-    /**
-     * Convenience function for adding a simple element to the end
-     * of the array.
-     */
-    template <class T>
-    void push_back(T&& val, typename value_traits<T>::type* = 0)
-    {
-        push_back(make_value(std::forward<T>(val)));
-    }
-
-    /**
-     * Insert a value into the array
-     */
-    template <class T>
-    iterator insert(iterator position, const std::shared_ptr<value<T>>& value)
-    {
-        if (values_.empty() || values_[0]->as<T>())
-        {
-            return values_.insert(position, value);
-        }
-        else
-        {
-            throw array_exception{"Arrays must be homogenous."};
-        }
-    }
-
-    /**
-     * Insert an array into the array
-     */
-    iterator insert(iterator position, const std::shared_ptr<array>& value)
-    {
-        if (values_.empty() || values_[0]->is_array())
-        {
-            return values_.insert(position, value);
-        }
-        else
-        {
-            throw array_exception{"Arrays must be homogenous."};
-        }
-    }
-
-    /**
-     * Convenience function for inserting a simple element in the array
-     */
-    template <class T>
-    iterator insert(iterator position, T&& val,
-                    typename value_traits<T>::type* = 0)
-    {
-        return insert(position, make_value(std::forward<T>(val)));
-    }
-
-    /**
-     * Erase an element from the array
-     */
-    iterator erase(iterator position)
-    {
-        return values_.erase(position);
-    }
-
-    /**
-     * Clear the array
-     */
-    void clear()
-    {
-        values_.clear();
-    }
-
-    /**
-     * Reserve space for n values.
-     */
-    void reserve(size_type n)
-    {
-        values_.reserve(n);
-    }
-
-  private:
-#if defined(CPPTOML_NO_RTTI)
-    array() : base(base_type::ARRAY)
-    {
-        // empty
-    }
-#else
-    array() = default;
-#endif
-
-    template <class InputIterator>
-    array(InputIterator begin, InputIterator end) : values_{begin, end}
-    {
-        // nothing
-    }
-
-    array(const array& obj) = delete;
-    array& operator=(const array& obj) = delete;
-
-    std::vector<std::shared_ptr<base>> values_;
-};
-
-inline std::shared_ptr<array> make_array()
-{
-    struct make_shared_enabler : public array
-    {
-        make_shared_enabler()
-        {
-            // nothing
-        }
-    };
-
-    return std::make_shared<make_shared_enabler>();
-}
-
-namespace detail
-{
-template <>
-inline std::shared_ptr<array> make_element<array>()
-{
-    return make_array();
-}
-} // namespace detail
-
-/**
- * Obtains a option<std::vector<T>>. The option will be empty if the array
- * contains values that are not of type T.
- */
-template <>
-inline typename array_of_trait<array>::return_type
-array::get_array_of<array>() const
-{
-    std::vector<std::shared_ptr<array>> result;
-    result.reserve(values_.size());
-
-    for (const auto& val : values_)
-    {
-        if (auto v = val->as_array())
-            result.push_back(v);
-        else
-            return {};
-    }
-
-    return {std::move(result)};
-}
-
-class table;
-
-class table_array : public base
-{
-    friend class table;
-    friend std::shared_ptr<table_array> make_table_array(bool);
-
-  public:
-    std::shared_ptr<base> clone() const override;
-
-    using size_type = std::size_t;
-
-    /**
-     * arrays can be iterated over
-     */
-    using iterator = std::vector<std::shared_ptr<table>>::iterator;
-
-    /**
-     * arrays can be iterated over.  Const version.
-     */
-    using const_iterator = std::vector<std::shared_ptr<table>>::const_iterator;
-
-    iterator begin()
-    {
-        return array_.begin();
-    }
-
-    const_iterator begin() const
-    {
-        return array_.begin();
-    }
-
-    iterator end()
-    {
-        return array_.end();
-    }
-
-    const_iterator end() const
-    {
-        return array_.end();
-    }
-
-    virtual bool is_table_array() const override
-    {
-        return true;
-    }
-
-    std::vector<std::shared_ptr<table>>& get()
-    {
-        return array_;
-    }
-
-    const std::vector<std::shared_ptr<table>>& get() const
-    {
-        return array_;
-    }
-
-    /**
-     * Add a table to the end of the array
-     */
-    void push_back(const std::shared_ptr<table>& val)
-    {
-        array_.push_back(val);
-    }
-
-    /**
-     * Insert a table into the array
-     */
-    iterator insert(iterator position, const std::shared_ptr<table>& value)
-    {
-        return array_.insert(position, value);
-    }
-
-    /**
-     * Erase an element from the array
-     */
-    iterator erase(iterator position)
-    {
-        return array_.erase(position);
-    }
-
-    /**
-     * Clear the array
-     */
-    void clear()
-    {
-        array_.clear();
-    }
-
-    /**
-     * Reserve space for n tables.
-     */
-    void reserve(size_type n)
-    {
-        array_.reserve(n);
-    }
-
-    /**
-     * Whether or not the table array is declared inline. This mostly
-     * matters for parsing, where statically defined arrays cannot be
-     * appended to using the array-of-table syntax.
-     */
-    bool is_inline() const
-    {
-        return is_inline_;
-    }
-
-  private:
-#if defined(CPPTOML_NO_RTTI)
-    table_array(bool is_inline = false)
-        : base(base_type::TABLE_ARRAY), is_inline_(is_inline)
-    {
-        // nothing
-    }
-#else
-    table_array(bool is_inline = false) : is_inline_(is_inline)
-    {
-        // nothing
-    }
-#endif
-
-    table_array(const table_array& obj) = delete;
-    table_array& operator=(const table_array& rhs) = delete;
-
-    std::vector<std::shared_ptr<table>> array_;
-    const bool is_inline_ = false;
-};
-
-inline std::shared_ptr<table_array> make_table_array(bool is_inline)
-{
-    struct make_shared_enabler : public table_array
-    {
-        make_shared_enabler(bool mse_is_inline) : table_array(mse_is_inline)
-        {
-            // nothing
-        }
-    };
-
-    return std::make_shared<make_shared_enabler>(is_inline);
-}
-
-namespace detail
-{
-template <>
-inline std::shared_ptr<table_array> make_element<table_array>()
-{
-    return make_table_array(true);
-}
-} // namespace detail
-
-// The below are overloads for fetching specific value types out of a value
-// where special casting behavior (like bounds checking) is desired
-
-template <class T>
-typename std::enable_if<!std::is_floating_point<T>::value
-                            && std::is_signed<T>::value,
-                        option<T>>::type
-get_impl(const std::shared_ptr<base>& elem)
-{
-    if (auto v = elem->as<int64_t>())
-    {
-        if (v->get() < (std::numeric_limits<T>::min)())
-            throw std::underflow_error{
-                "T cannot represent the value requested in get"};
-
-        if (v->get() > (std::numeric_limits<T>::max)())
-            throw std::overflow_error{
-                "T cannot represent the value requested in get"};
-
-        return {static_cast<T>(v->get())};
-    }
-    else
-    {
-        return {};
-    }
-}
-
-template <class T>
-typename std::enable_if<!std::is_same<T, bool>::value
-                            && std::is_unsigned<T>::value,
-                        option<T>>::type
-get_impl(const std::shared_ptr<base>& elem)
-{
-    if (auto v = elem->as<int64_t>())
-    {
-        if (v->get() < 0)
-            throw std::underflow_error{"T cannot store negative value in get"};
-
-        if (static_cast<uint64_t>(v->get()) > (std::numeric_limits<T>::max)())
-            throw std::overflow_error{
-                "T cannot represent the value requested in get"};
-
-        return {static_cast<T>(v->get())};
-    }
-    else
-    {
-        return {};
-    }
-}
-
-template <class T>
-typename std::enable_if<!std::is_integral<T>::value
-                            || std::is_same<T, bool>::value,
-                        option<T>>::type
-get_impl(const std::shared_ptr<base>& elem)
-{
-    if (auto v = elem->as<T>())
-    {
-        return {v->get()};
-    }
-    else
-    {
-        return {};
-    }
-}
-
-/**
- * Represents a TOML keytable.
- */
-class table : public base
-{
-  public:
-    friend class table_array;
-    friend std::shared_ptr<table> make_table();
-
-    std::shared_ptr<base> clone() const override;
-
-    /**
-     * tables can be iterated over.
-     */
-    using iterator = string_to_base_map::iterator;
-
-    /**
-     * tables can be iterated over. Const version.
-     */
-    using const_iterator = string_to_base_map::const_iterator;
-
-    iterator begin()
-    {
-        return map_.begin();
-    }
-
-    const_iterator begin() const
-    {
-        return map_.begin();
-    }
-
-    iterator end()
-    {
-        return map_.end();
-    }
-
-    const_iterator end() const
-    {
-        return map_.end();
-    }
-
-    bool is_table() const override
-    {
-        return true;
-    }
-
-    bool empty() const
-    {
-        return map_.empty();
-    }
-
-    /**
-     * Determines if this key table contains the given key.
-     */
-    bool contains(const std::string& key) const
-    {
-        return map_.find(key) != map_.end();
-    }
-
-    /**
-     * Determines if this key table contains the given key. Will
-     * resolve "qualified keys". Qualified keys are the full access
-     * path separated with dots like "grandparent.parent.child".
-     */
-    bool contains_qualified(const std::string& key) const
-    {
-        return resolve_qualified(key);
-    }
-
-    /**
-     * Obtains the base for a given key.
-     * @throw std::out_of_range if the key does not exist
-     */
-    std::shared_ptr<base> get(const std::string& key) const
-    {
-        return map_.at(key);
-    }
-
-    /**
-     * Obtains the base for a given key. Will resolve "qualified
-     * keys". Qualified keys are the full access path separated with
-     * dots like "grandparent.parent.child".
-     *
-     * @throw std::out_of_range if the key does not exist
-     */
-    std::shared_ptr<base> get_qualified(const std::string& key) const
-    {
-        std::shared_ptr<base> p;
-        resolve_qualified(key, &p);
-        return p;
-    }
-
-    /**
-     * Obtains a table for a given key, if possible.
-     */
-    std::shared_ptr<table> get_table(const std::string& key) const
-    {
-        if (contains(key) && get(key)->is_table())
-            return std::static_pointer_cast<table>(get(key));
-        return nullptr;
-    }
-
-    /**
-     * Obtains a table for a given key, if possible. Will resolve
-     * "qualified keys".
-     */
-    std::shared_ptr<table> get_table_qualified(const std::string& key) const
-    {
-        if (contains_qualified(key) && get_qualified(key)->is_table())
-            return std::static_pointer_cast<table>(get_qualified(key));
-        return nullptr;
-    }
-
-    /**
-     * Obtains an array for a given key.
-     */
-    std::shared_ptr<array> get_array(const std::string& key) const
-    {
-        if (!contains(key))
-            return nullptr;
-        return get(key)->as_array();
-    }
-
-    /**
-     * Obtains an array for a given key. Will resolve "qualified keys".
-     */
-    std::shared_ptr<array> get_array_qualified(const std::string& key) const
-    {
-        if (!contains_qualified(key))
-            return nullptr;
-        return get_qualified(key)->as_array();
-    }
-
-    /**
-     * Obtains a table_array for a given key, if possible.
-     */
-    std::shared_ptr<table_array> get_table_array(const std::string& key) const
-    {
-        if (!contains(key))
-            return nullptr;
-        return get(key)->as_table_array();
-    }
-
-    /**
-     * Obtains a table_array for a given key, if possible. Will resolve
-     * "qualified keys".
-     */
-    std::shared_ptr<table_array>
-    get_table_array_qualified(const std::string& key) const
-    {
-        if (!contains_qualified(key))
-            return nullptr;
-        return get_qualified(key)->as_table_array();
-    }
-
-    /**
-     * Helper function that attempts to get a value corresponding
-     * to the template parameter from a given key.
-     */
-    template <class T>
-    option<T> get_as(const std::string& key) const
-    {
-        try
-        {
-            return get_impl<T>(get(key));
-        }
-        catch (const std::out_of_range&)
-        {
-            return {};
-        }
-    }
-
-    /**
-     * Helper function that attempts to get a value corresponding
-     * to the template parameter from a given key. Will resolve "qualified
-     * keys".
-     */
-    template <class T>
-    option<T> get_qualified_as(const std::string& key) const
-    {
-        try
-        {
-            return get_impl<T>(get_qualified(key));
-        }
-        catch (const std::out_of_range&)
-        {
-            return {};
-        }
-    }
-
-    /**
-     * Helper function that attempts to get an array of values of a given
-     * type corresponding to the template parameter for a given key.
-     *
-     * If the key doesn't exist, doesn't exist as an array type, or one or
-     * more keys inside the array type are not of type T, an empty option
-     * is returned. Otherwise, an option containing a vector of the values
-     * is returned.
-     */
-    template <class T>
-    inline typename array_of_trait<T>::return_type
-    get_array_of(const std::string& key) const
-    {
-        if (auto v = get_array(key))
-        {
-            std::vector<T> result;
-            result.reserve(v->get().size());
-
-            for (const auto& b : v->get())
-            {
-                if (auto val = b->as<T>())
-                    result.push_back(val->get());
-                else
-                    return {};
-            }
-            return {std::move(result)};
-        }
-
-        return {};
-    }
-
-    /**
-     * Helper function that attempts to get an array of values of a given
-     * type corresponding to the template parameter for a given key. Will
-     * resolve "qualified keys".
-     *
-     * If the key doesn't exist, doesn't exist as an array type, or one or
-     * more keys inside the array type are not of type T, an empty option
-     * is returned. Otherwise, an option containing a vector of the values
-     * is returned.
-     */
-    template <class T>
-    inline typename array_of_trait<T>::return_type
-    get_qualified_array_of(const std::string& key) const
-    {
-        if (auto v = get_array_qualified(key))
-        {
-            std::vector<T> result;
-            result.reserve(v->get().size());
-
-            for (const auto& b : v->get())
-            {
-                if (auto val = b->as<T>())
-                    result.push_back(val->get());
-                else
-                    return {};
-            }
-            return {std::move(result)};
-        }
-
-        return {};
-    }
-
-    /**
-     * Adds an element to the keytable.
-     */
-    void insert(const std::string& key, const std::shared_ptr<base>& value)
-    {
-        map_[key] = value;
-    }
-
-    /**
-     * Convenience shorthand for adding a simple element to the
-     * keytable.
-     */
-    template <class T>
-    void insert(const std::string& key, T&& val,
-                typename value_traits<T>::type* = 0)
-    {
-        insert(key, make_value(std::forward<T>(val)));
-    }
-
-    /**
-     * Removes an element from the table.
-     */
-    void erase(const std::string& key)
-    {
-        map_.erase(key);
-    }
-
-  private:
-#if defined(CPPTOML_NO_RTTI)
-    table() : base(base_type::TABLE)
-    {
-        // nothing
-    }
-#else
-    table()
-    {
-        // nothing
-    }
-#endif
-
-    table(const table& obj) = delete;
-    table& operator=(const table& rhs) = delete;
-
-    std::vector<std::string> split(const std::string& value,
-                                   char separator) const
-    {
-        std::vector<std::string> result;
-        std::string::size_type p = 0;
-        std::string::size_type q;
-        while ((q = value.find(separator, p)) != std::string::npos)
-        {
-            result.emplace_back(value, p, q - p);
-            p = q + 1;
-        }
-        result.emplace_back(value, p);
-        return result;
-    }
-
-    // If output parameter p is specified, fill it with the pointer to the
-    // specified entry and throw std::out_of_range if it couldn't be found.
-    //
-    // Otherwise, just return true if the entry could be found or false
-    // otherwise and do not throw.
-    bool resolve_qualified(const std::string& key,
-                           std::shared_ptr<base>* p = nullptr) const
-    {
-        auto parts = split(key, '.');
-        auto last_key = parts.back();
-        parts.pop_back();
-
-        auto cur_table = this;
-        for (const auto& part : parts)
-        {
-            cur_table = cur_table->get_table(part).get();
-            if (!cur_table)
-            {
-                if (!p)
-                    return false;
-
-                throw std::out_of_range{key + " is not a valid key"};
-            }
-        }
-
-        if (!p)
-            return cur_table->map_.count(last_key) != 0;
-
-        *p = cur_table->map_.at(last_key);
-        return true;
-    }
-
-    string_to_base_map map_;
-};
-
-/**
- * Helper function that attempts to get an array of arrays for a given
- * key.
- *
- * If the key doesn't exist, doesn't exist as an array type, or one or
- * more keys inside the array type are not of type T, an empty option
- * is returned. Otherwise, an option containing a vector of the values
- * is returned.
- */
-template <>
-inline typename array_of_trait<array>::return_type
-table::get_array_of<array>(const std::string& key) const
-{
-    if (auto v = get_array(key))
-    {
-        std::vector<std::shared_ptr<array>> result;
-        result.reserve(v->get().size());
-
-        for (const auto& b : v->get())
-        {
-            if (auto val = b->as_array())
-                result.push_back(val);
-            else
-                return {};
-        }
-
-        return {std::move(result)};
-    }
-
-    return {};
-}
-
-/**
- * Helper function that attempts to get an array of arrays for a given
- * key. Will resolve "qualified keys".
- *
- * If the key doesn't exist, doesn't exist as an array type, or one or
- * more keys inside the array type are not of type T, an empty option
- * is returned. Otherwise, an option containing a vector of the values
- * is returned.
- */
-template <>
-inline typename array_of_trait<array>::return_type
-table::get_qualified_array_of<array>(const std::string& key) const
-{
-    if (auto v = get_array_qualified(key))
-    {
-        std::vector<std::shared_ptr<array>> result;
-        result.reserve(v->get().size());
-
-        for (const auto& b : v->get())
-        {
-            if (auto val = b->as_array())
-                result.push_back(val);
-            else
-                return {};
-        }
-
-        return {std::move(result)};
-    }
-
-    return {};
-}
-
-std::shared_ptr<table> make_table()
-{
-    struct make_shared_enabler : public table
-    {
-        make_shared_enabler()
-        {
-            // nothing
-        }
-    };
-
-    return std::make_shared<make_shared_enabler>();
-}
-
-namespace detail
-{
-template <>
-inline std::shared_ptr<table> make_element<table>()
-{
-    return make_table();
-}
-} // namespace detail
-
-template <class T>
-std::shared_ptr<base> value<T>::clone() const
-{
-    return make_value(data_);
-}
-
-inline std::shared_ptr<base> array::clone() const
-{
-    auto result = make_array();
-    result->reserve(values_.size());
-    for (const auto& ptr : values_)
-        result->values_.push_back(ptr->clone());
-    return result;
-}
-
-inline std::shared_ptr<base> table_array::clone() const
-{
-    auto result = make_table_array(is_inline());
-    result->reserve(array_.size());
-    for (const auto& ptr : array_)
-        result->array_.push_back(ptr->clone()->as_table());
-    return result;
-}
-
-inline std::shared_ptr<base> table::clone() const
-{
-    auto result = make_table();
-    for (const auto& pr : map_)
-        result->insert(pr.first, pr.second->clone());
-    return result;
-}
-
-/**
- * Exception class for all TOML parsing errors.
- */
-class parse_exception : public std::runtime_error
-{
-  public:
-    parse_exception(const std::string& err) : std::runtime_error{err}
-    {
-    }
-
-    parse_exception(const std::string& err, std::size_t line_number)
-        : std::runtime_error{err + " at line " + std::to_string(line_number)}
-    {
-    }
-};
-
-inline bool is_number(char c)
-{
-    return c >= '0' && c <= '9';
-}
-
-inline bool is_hex(char c)
-{
-    return is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-
-/**
- * Helper object for consuming expected characters.
- */
-template <class OnError>
-class consumer
-{
-  public:
-    consumer(std::string::iterator& it, const std::string::iterator& end,
-             OnError&& on_error)
-        : it_(it), end_(end), on_error_(std::forward<OnError>(on_error))
-    {
-        // nothing
-    }
-
-    void operator()(char c)
-    {
-        if (it_ == end_ || *it_ != c)
-            on_error_();
-        ++it_;
-    }
-
-    template <std::size_t N>
-    void operator()(const char (&str)[N])
-    {
-        std::for_each(std::begin(str), std::end(str) - 1,
-                      [&](char c) { (*this)(c); });
-    }
-
-    void eat_or(char a, char b)
-    {
-        if (it_ == end_ || (*it_ != a && *it_ != b))
-            on_error_();
-        ++it_;
-    }
-
-    int eat_digits(int len)
-    {
-        int val = 0;
-        for (int i = 0; i < len; ++i)
-        {
-            if (!is_number(*it_) || it_ == end_)
-                on_error_();
-            val = 10 * val + (*it_++ - '0');
-        }
-        return val;
-    }
-
-    void error()
-    {
-        on_error_();
-    }
-
-  private:
-    std::string::iterator& it_;
-    const std::string::iterator& end_;
-    OnError on_error_;
-};
-
-template <class OnError>
-consumer<OnError> make_consumer(std::string::iterator& it,
-                                const std::string::iterator& end,
-                                OnError&& on_error)
-{
-    return consumer<OnError>(it, end, std::forward<OnError>(on_error));
-}
-
-// replacement for std::getline to handle incorrectly line-ended files
-// https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
-namespace detail
-{
-inline std::istream& getline(std::istream& input, std::string& line)
-{
-    line.clear();
-
-    std::istream::sentry sentry{input, true};
-    auto sb = input.rdbuf();
-
-    while (true)
-    {
-        auto c = sb->sbumpc();
-        if (c == '\r')
-        {
-            if (sb->sgetc() == '\n')
-                c = sb->sbumpc();
-        }
-
-        if (c == '\n')
-            return input;
-
-        if (c == std::istream::traits_type::eof())
-        {
-            if (line.empty())
-                input.setstate(std::ios::eofbit);
-            return input;
-        }
-
-        line.push_back(static_cast<char>(c));
-    }
-}
-} // namespace detail
-
-/**
- * The parser class.
- */
-class parser
-{
-  public:
-    /**
-     * Parsers are constructed from streams.
-     */
-    parser(std::istream& stream) : input_(stream)
-    {
-        // nothing
-    }
-
-    parser& operator=(const parser& parser) = delete;
-
-    /**
-     * Parses the stream this parser was created on until EOF.
-     * @throw parse_exception if there are errors in parsing
-     */
-    std::shared_ptr<table> parse()
-    {
-        std::shared_ptr<table> root = make_table();
-
-        table* curr_table = root.get();
-
-        while (detail::getline(input_, line_))
-        {
-            line_number_++;
-            auto it = line_.begin();
-            auto end = line_.end();
-            consume_whitespace(it, end);
-            if (it == end || *it == '#')
-                continue;
-            if (*it == '[')
-            {
-                curr_table = root.get();
-                parse_table(it, end, curr_table);
-            }
-            else
-            {
-                parse_key_value(it, end, curr_table);
-                consume_whitespace(it, end);
-                eol_or_comment(it, end);
-            }
-        }
-        return root;
-    }
-
-  private:
-#if defined _MSC_VER
-    __declspec(noreturn)
-#elif defined __GNUC__
-    __attribute__((noreturn))
-#endif
-        void throw_parse_exception(const std::string& err)
-    {
-        throw parse_exception{err, line_number_};
-    }
-
-    void parse_table(std::string::iterator& it,
-                     const std::string::iterator& end, table*& curr_table)
-    {
-        // remove the beginning keytable marker
-        ++it;
-        if (it == end)
-            throw_parse_exception("Unexpected end of table");
-        if (*it == '[')
-            parse_table_array(it, end, curr_table);
-        else
-            parse_single_table(it, end, curr_table);
-    }
-
-    void parse_single_table(std::string::iterator& it,
-                            const std::string::iterator& end,
-                            table*& curr_table)
-    {
-        if (it == end || *it == ']')
-            throw_parse_exception("Table name cannot be empty");
-
-        std::string full_table_name;
-        bool inserted = false;
-
-        auto key_end = [](char c) { return c == ']'; };
-
-        auto key_part_handler = [&](const std::string& part) {
-            if (part.empty())
-                throw_parse_exception("Empty component of table name");
-
-            if (!full_table_name.empty())
-                full_table_name += '.';
-            full_table_name += part;
-
-            if (curr_table->contains(part))
-            {
-#if !defined(__PGI)
-                auto b = curr_table->get(part);
-#else
-                // Workaround for PGI compiler
-                std::shared_ptr<base> b = curr_table->get(part);
-#endif
-                if (b->is_table())
-                    curr_table = static_cast<table*>(b.get());
-                else if (b->is_table_array())
-                    curr_table = std::static_pointer_cast<table_array>(b)
-                                     ->get()
-                                     .back()
-                                     .get();
-                else
-                    throw_parse_exception("Key " + full_table_name
-                                          + "already exists as a value");
-            }
-            else
-            {
-                inserted = true;
-                curr_table->insert(part, make_table());
-                curr_table = static_cast<table*>(curr_table->get(part).get());
-            }
-        };
-
-        key_part_handler(parse_key(it, end, key_end, key_part_handler));
-
-        if (it == end)
-            throw_parse_exception(
-                "Unterminated table declaration; did you forget a ']'?");
-
-        if (*it != ']')
-        {
-            std::string errmsg{"Unexpected character in table definition: "};
-            errmsg += '"';
-            errmsg += *it;
-            errmsg += '"';
-            throw_parse_exception(errmsg);
-        }
-
-        // table already existed
-        if (!inserted)
-        {
-            auto is_value
-                = [](const std::pair<const std::string&,
-                                     const std::shared_ptr<base>&>& p) {
-                      return p.second->is_value();
-                  };
-
-            // if there are any values, we can't add values to this table
-            // since it has already been defined. If there aren't any
-            // values, then it was implicitly created by something like
-            // [a.b]
-            if (curr_table->empty()
-                || std::any_of(curr_table->begin(), curr_table->end(),
-                               is_value))
-            {
-                throw_parse_exception("Redefinition of table "
-                                      + full_table_name);
-            }
-        }
-
-        ++it;
-        consume_whitespace(it, end);
-        eol_or_comment(it, end);
-    }
-
-    void parse_table_array(std::string::iterator& it,
-                           const std::string::iterator& end, table*& curr_table)
-    {
-        ++it;
-        if (it == end || *it == ']')
-            throw_parse_exception("Table array name cannot be empty");
-
-        auto key_end = [](char c) { return c == ']'; };
-
-        std::string full_ta_name;
-        auto key_part_handler = [&](const std::string& part) {
-            if (part.empty())
-                throw_parse_exception("Empty component of table array name");
-
-            if (!full_ta_name.empty())
-                full_ta_name += '.';
-            full_ta_name += part;
-
-            if (curr_table->contains(part))
-            {
-#if !defined(__PGI)
-                auto b = curr_table->get(part);
-#else
-                // Workaround for PGI compiler
-                std::shared_ptr<base> b = curr_table->get(part);
-#endif
-
-                // if this is the end of the table array name, add an
-                // element to the table array that we just looked up,
-                // provided it was not declared inline
-                if (it != end && *it == ']')
-                {
-                    if (!b->is_table_array())
-                    {
-                        throw_parse_exception("Key " + full_ta_name
-                                              + " is not a table array");
-                    }
-
-                    auto v = b->as_table_array();
-
-                    if (v->is_inline())
-                    {
-                        throw_parse_exception("Static array " + full_ta_name
-                                              + " cannot be appended to");
-                    }
-
-                    v->get().push_back(make_table());
-                    curr_table = v->get().back().get();
-                }
-                // otherwise, just keep traversing down the key name
-                else
-                {
-                    if (b->is_table())
-                        curr_table = static_cast<table*>(b.get());
-                    else if (b->is_table_array())
-                        curr_table = std::static_pointer_cast<table_array>(b)
-                                         ->get()
-                                         .back()
-                                         .get();
-                    else
-                        throw_parse_exception("Key " + full_ta_name
-                                              + " already exists as a value");
-                }
-            }
-            else
-            {
-                // if this is the end of the table array name, add a new
-                // table array and a new table inside that array for us to
-                // add keys to next
-                if (it != end && *it == ']')
-                {
-                    curr_table->insert(part, make_table_array());
-                    auto arr = std::static_pointer_cast<table_array>(
-                        curr_table->get(part));
-                    arr->get().push_back(make_table());
-                    curr_table = arr->get().back().get();
-                }
-                // otherwise, create the implicitly defined table and move
-                // down to it
-                else
-                {
-                    curr_table->insert(part, make_table());
-                    curr_table
-                        = static_cast<table*>(curr_table->get(part).get());
-                }
-            }
-        };
-
-        key_part_handler(parse_key(it, end, key_end, key_part_handler));
-
-        // consume the last "]]"
-        auto eat = make_consumer(it, end, [this]() {
-            throw_parse_exception("Unterminated table array name");
-        });
-        eat(']');
-        eat(']');
-
-        consume_whitespace(it, end);
-        eol_or_comment(it, end);
-    }
-
-    void parse_key_value(std::string::iterator& it, std::string::iterator& end,
-                         table* curr_table)
-    {
-        auto key_end = [](char c) { return c == '='; };
-
-        auto key_part_handler = [&](const std::string& part) {
-            // two cases: this key part exists already, in which case it must
-            // be a table, or it doesn't exist in which case we must create
-            // an implicitly defined table
-            if (curr_table->contains(part))
-            {
-                auto val = curr_table->get(part);
-                if (val->is_table())
-                {
-                    curr_table = static_cast<table*>(val.get());
-                }
-                else
-                {
-                    throw_parse_exception("Key " + part
-                                          + " already exists as a value");
-                }
-            }
-            else
-            {
-                auto newtable = make_table();
-                curr_table->insert(part, newtable);
-                curr_table = newtable.get();
-            }
-        };
-
-        auto key = parse_key(it, end, key_end, key_part_handler);
-
-        if (curr_table->contains(key))
-            throw_parse_exception("Key " + key + " already present");
-        if (it == end || *it != '=')
-            throw_parse_exception("Value must follow after a '='");
-        ++it;
-        consume_whitespace(it, end);
-        curr_table->insert(key, parse_value(it, end));
-        consume_whitespace(it, end);
-    }
-
-    template <class KeyEndFinder, class KeyPartHandler>
-    std::string
-    parse_key(std::string::iterator& it, const std::string::iterator& end,
-              KeyEndFinder&& key_end, KeyPartHandler&& key_part_handler)
-    {
-        // parse the key as a series of one or more simple-keys joined with '.'
-        while (it != end && !key_end(*it))
-        {
-            auto part = parse_simple_key(it, end);
-            consume_whitespace(it, end);
-
-            if (it == end || key_end(*it))
-            {
-                return part;
-            }
-
-            if (*it != '.')
-            {
-                std::string errmsg{"Unexpected character in key: "};
-                errmsg += '"';
-                errmsg += *it;
-                errmsg += '"';
-                throw_parse_exception(errmsg);
-            }
-
-            key_part_handler(part);
-
-            // consume the dot
-            ++it;
-        }
-
-        throw_parse_exception("Unexpected end of key");
-    }
-
-    std::string parse_simple_key(std::string::iterator& it,
-                                 const std::string::iterator& end)
-    {
-        consume_whitespace(it, end);
-
-        if (it == end)
-            throw_parse_exception("Unexpected end of key (blank key?)");
-
-        if (*it == '"' || *it == '\'')
-        {
-            return string_literal(it, end, *it);
-        }
-        else
-        {
-            auto bke = std::find_if(it, end, [](char c) {
-                return c == '.' || c == '=' || c == ']';
-            });
-            return parse_bare_key(it, bke);
-        }
-    }
-
-    std::string parse_bare_key(std::string::iterator& it,
-                               const std::string::iterator& end)
-    {
-        if (it == end)
-        {
-            throw_parse_exception("Bare key missing name");
-        }
-
-        auto key_end = end;
-        --key_end;
-        consume_backwards_whitespace(key_end, it);
-        ++key_end;
-        std::string key{it, key_end};
-
-        if (std::find(it, key_end, '#') != key_end)
-        {
-            throw_parse_exception("Bare key " + key + " cannot contain #");
-        }
-
-        if (std::find_if(it, key_end,
-                         [](char c) { return c == ' ' || c == '\t'; })
-            != key_end)
-        {
-            throw_parse_exception("Bare key " + key
-                                  + " cannot contain whitespace");
-        }
-
-        if (std::find_if(it, key_end,
-                         [](char c) { return c == '[' || c == ']'; })
-            != key_end)
-        {
-            throw_parse_exception("Bare key " + key
-                                  + " cannot contain '[' or ']'");
-        }
-
-        it = end;
-        return key;
-    }
-
-    enum class parse_type
-    {
-        STRING = 1,
-        LOCAL_TIME,
-        LOCAL_DATE,
-        LOCAL_DATETIME,
-        OFFSET_DATETIME,
-        INT,
-        FLOAT,
-        BOOL,
-        ARRAY,
-        INLINE_TABLE
-    };
-
-    std::shared_ptr<base> parse_value(std::string::iterator& it,
-                                      std::string::iterator& end)
-    {
-        parse_type type = determine_value_type(it, end);
-        switch (type)
-        {
-            case parse_type::STRING:
-                return parse_string(it, end);
-            case parse_type::LOCAL_TIME:
-                return parse_time(it, end);
-            case parse_type::LOCAL_DATE:
-            case parse_type::LOCAL_DATETIME:
-            case parse_type::OFFSET_DATETIME:
-                return parse_date(it, end);
-            case parse_type::INT:
-            case parse_type::FLOAT:
-                return parse_number(it, end);
-            case parse_type::BOOL:
-                return parse_bool(it, end);
-            case parse_type::ARRAY:
-                return parse_array(it, end);
-            case parse_type::INLINE_TABLE:
-                return parse_inline_table(it, end);
-            default:
-                throw_parse_exception("Failed to parse value");
-        }
-    }
-
-    parse_type determine_value_type(const std::string::iterator& it,
-                                    const std::string::iterator& end)
-    {
-        if (it == end)
-        {
-            throw_parse_exception("Failed to parse value type");
-        }
-        if (*it == '"' || *it == '\'')
-        {
-            return parse_type::STRING;
-        }
-        else if (is_time(it, end))
-        {
-            return parse_type::LOCAL_TIME;
-        }
-        else if (auto dtype = date_type(it, end))
-        {
-            return *dtype;
-        }
-        else if (is_number(*it) || *it == '-' || *it == '+'
-                 || (*it == 'i' && it + 1 != end && it[1] == 'n'
-                     && it + 2 != end && it[2] == 'f')
-                 || (*it == 'n' && it + 1 != end && it[1] == 'a'
-                     && it + 2 != end && it[2] == 'n'))
-        {
-            return determine_number_type(it, end);
-        }
-        else if (*it == 't' || *it == 'f')
-        {
-            return parse_type::BOOL;
-        }
-        else if (*it == '[')
-        {
-            return parse_type::ARRAY;
-        }
-        else if (*it == '{')
-        {
-            return parse_type::INLINE_TABLE;
-        }
-        throw_parse_exception("Failed to parse value type");
-    }
-
-    parse_type determine_number_type(const std::string::iterator& it,
-                                     const std::string::iterator& end)
-    {
-        // determine if we are an integer or a float
-        auto check_it = it;
-        if (*check_it == '-' || *check_it == '+')
-            ++check_it;
-
-        if (check_it == end)
-            throw_parse_exception("Malformed number");
-
-        if (*check_it == 'i' || *check_it == 'n')
-            return parse_type::FLOAT;
-
-        while (check_it != end && is_number(*check_it))
-            ++check_it;
-        if (check_it != end && *check_it == '.')
-        {
-            ++check_it;
-            while (check_it != end && is_number(*check_it))
-                ++check_it;
-            return parse_type::FLOAT;
-        }
-        else
-        {
-            return parse_type::INT;
-        }
-    }
-
-    std::shared_ptr<value<std::string>> parse_string(std::string::iterator& it,
-                                                     std::string::iterator& end)
-    {
-        auto delim = *it;
-        assert(delim == '"' || delim == '\'');
-
-        // end is non-const here because we have to be able to potentially
-        // parse multiple lines in a string, not just one
-        auto check_it = it;
-        ++check_it;
-        if (check_it != end && *check_it == delim)
-        {
-            ++check_it;
-            if (check_it != end && *check_it == delim)
-            {
-                it = ++check_it;
-                return parse_multiline_string(it, end, delim);
-            }
-        }
-        return make_value<std::string>(string_literal(it, end, delim));
-    }
-
-    std::shared_ptr<value<std::string>>
-    parse_multiline_string(std::string::iterator& it,
-                           std::string::iterator& end, char delim)
-    {
-        std::stringstream ss;
-
-        auto is_ws = [](char c) { return c == ' ' || c == '\t'; };
-
-        bool consuming = false;
-        std::shared_ptr<value<std::string>> ret;
-
-        auto handle_line = [&](std::string::iterator& local_it,
-                               std::string::iterator& local_end) {
-            if (consuming)
-            {
-                local_it = std::find_if_not(local_it, local_end, is_ws);
-
-                // whole line is whitespace
-                if (local_it == local_end)
-                    return;
-            }
-
-            consuming = false;
-
-            while (local_it != local_end)
-            {
-                // handle escaped characters
-                if (delim == '"' && *local_it == '\\')
-                {
-                    auto check = local_it;
-                    // check if this is an actual escape sequence or a
-                    // whitespace escaping backslash
-                    ++check;
-                    consume_whitespace(check, local_end);
-                    if (check == local_end)
-                    {
-                        consuming = true;
-                        break;
-                    }
-
-                    ss << parse_escape_code(local_it, local_end);
-                    continue;
-                }
-
-                // if we can end the string
-                if (std::distance(local_it, local_end) >= 3)
-                {
-                    auto check = local_it;
-                    // check for """
-                    if (*check++ == delim && *check++ == delim
-                        && *check++ == delim)
-                    {
-                        local_it = check;
-                        ret = make_value<std::string>(ss.str());
-                        break;
-                    }
-                }
-
-                ss << *local_it++;
-            }
-        };
-
-        // handle the remainder of the current line
-        handle_line(it, end);
-        if (ret)
-            return ret;
-
-        // start eating lines
-        while (detail::getline(input_, line_))
-        {
-            ++line_number_;
-
-            it = line_.begin();
-            end = line_.end();
-
-            handle_line(it, end);
-
-            if (ret)
-                return ret;
-
-            if (!consuming)
-                ss << std::endl;
-        }
-
-        throw_parse_exception("Unterminated multi-line basic string");
-    }
-
-    std::string string_literal(std::string::iterator& it,
-                               const std::string::iterator& end, char delim)
-    {
-        ++it;
-        std::string val;
-        while (it != end)
-        {
-            // handle escaped characters
-            if (delim == '"' && *it == '\\')
-            {
-                val += parse_escape_code(it, end);
-            }
-            else if (*it == delim)
-            {
-                ++it;
-                consume_whitespace(it, end);
-                return val;
-            }
-            else
-            {
-                val += *it++;
-            }
-        }
-        throw_parse_exception("Unterminated string literal");
-    }
-
-    std::string parse_escape_code(std::string::iterator& it,
-                                  const std::string::iterator& end)
-    {
-        ++it;
-        if (it == end)
-            throw_parse_exception("Invalid escape sequence");
-        char value;
-        if (*it == 'b')
-        {
-            value = '\b';
-        }
-        else if (*it == 't')
-        {
-            value = '\t';
-        }
-        else if (*it == 'n')
-        {
-            value = '\n';
-        }
-        else if (*it == 'f')
-        {
-            value = '\f';
-        }
-        else if (*it == 'r')
-        {
-            value = '\r';
-        }
-        else if (*it == '"')
-        {
-            value = '"';
-        }
-        else if (*it == '\\')
-        {
-            value = '\\';
-        }
-        else if (*it == 'u' || *it == 'U')
-        {
-            return parse_unicode(it, end);
-        }
-        else
-        {
-            throw_parse_exception("Invalid escape sequence");
-        }
-        ++it;
-        return std::string(1, value);
-    }
-
-    std::string parse_unicode(std::string::iterator& it,
-                              const std::string::iterator& end)
-    {
-        bool large = *it++ == 'U';
-        auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000);
-
-        if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff)
-        {
-            throw_parse_exception(
-                "Unicode escape sequence is not a Unicode scalar value");
-        }
-
-        std::string result;
-        // See Table 3-6 of the Unicode standard
-        if (codepoint <= 0x7f)
-        {
-            // 1-byte codepoints: 00000000 0xxxxxxx
-            // repr: 0xxxxxxx
-            result += static_cast<char>(codepoint & 0x7f);
-        }
-        else if (codepoint <= 0x7ff)
-        {
-            // 2-byte codepoints: 00000yyy yyxxxxxx
-            // repr: 110yyyyy 10xxxxxx
-            //
-            // 0x1f = 00011111
-            // 0xc0 = 11000000
-            //
-            result += static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f));
-            //
-            // 0x80 = 10000000
-            // 0x3f = 00111111
-            //
-            result += static_cast<char>(0x80 | (codepoint & 0x3f));
-        }
-        else if (codepoint <= 0xffff)
-        {
-            // 3-byte codepoints: zzzzyyyy yyxxxxxx
-            // repr: 1110zzzz 10yyyyyy 10xxxxxx
-            //
-            // 0xe0 = 11100000
-            // 0x0f = 00001111
-            //
-            result += static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f));
-            result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x1f));
-            result += static_cast<char>(0x80 | (codepoint & 0x3f));
-        }
-        else
-        {
-            // 4-byte codepoints: 000uuuuu zzzzyyyy yyxxxxxx
-            // repr: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
-            //
-            // 0xf0 = 11110000
-            // 0x07 = 00000111
-            //
-            result += static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07));
-            result += static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f));
-            result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));
-            result += static_cast<char>(0x80 | (codepoint & 0x3f));
-        }
-        return result;
-    }
-
-    uint32_t parse_hex(std::string::iterator& it,
-                       const std::string::iterator& end, uint32_t place)
-    {
-        uint32_t value = 0;
-        while (place > 0)
-        {
-            if (it == end)
-                throw_parse_exception("Unexpected end of unicode sequence");
-
-            if (!is_hex(*it))
-                throw_parse_exception("Invalid unicode escape sequence");
-
-            value += place * hex_to_digit(*it++);
-            place /= 16;
-        }
-        return value;
-    }
-
-    uint32_t hex_to_digit(char c)
-    {
-        if (is_number(c))
-            return static_cast<uint32_t>(c - '0');
-        return 10
-               + static_cast<uint32_t>(c
-                                       - ((c >= 'a' && c <= 'f') ? 'a' : 'A'));
-    }
-
-    std::shared_ptr<base> parse_number(std::string::iterator& it,
-                                       const std::string::iterator& end)
-    {
-        auto check_it = it;
-        auto check_end = find_end_of_number(it, end);
-
-        auto eat_sign = [&]() {
-            if (check_it != end && (*check_it == '-' || *check_it == '+'))
-                ++check_it;
-        };
-
-        auto check_no_leading_zero = [&]() {
-            if (check_it != end && *check_it == '0' && check_it + 1 != check_end
-                && check_it[1] != '.')
-            {
-                throw_parse_exception("Numbers may not have leading zeros");
-            }
-        };
-
-        auto eat_digits = [&](bool (*check_char)(char)) {
-            auto beg = check_it;
-            while (check_it != end && check_char(*check_it))
-            {
-                ++check_it;
-                if (check_it != end && *check_it == '_')
-                {
-                    ++check_it;
-                    if (check_it == end || !check_char(*check_it))
-                        throw_parse_exception("Malformed number");
-                }
-            }
-
-            if (check_it == beg)
-                throw_parse_exception("Malformed number");
-        };
-
-        auto eat_hex = [&]() { eat_digits(&is_hex); };
-
-        auto eat_numbers = [&]() { eat_digits(&is_number); };
-
-        if (check_it != end && *check_it == '0' && check_it + 1 != check_end
-            && (check_it[1] == 'x' || check_it[1] == 'o' || check_it[1] == 'b'))
-        {
-            ++check_it;
-            char base = *check_it;
-            ++check_it;
-            if (base == 'x')
-            {
-                eat_hex();
-                return parse_int(it, check_it, 16);
-            }
-            else if (base == 'o')
-            {
-                auto start = check_it;
-                eat_numbers();
-                auto val = parse_int(start, check_it, 8, "0");
-                it = start;
-                return val;
-            }
-            else // if (base == 'b')
-            {
-                auto start = check_it;
-                eat_numbers();
-                auto val = parse_int(start, check_it, 2);
-                it = start;
-                return val;
-            }
-        }
-
-        eat_sign();
-        check_no_leading_zero();
-
-        if (check_it != end && check_it + 1 != end && check_it + 2 != end)
-        {
-            if (check_it[0] == 'i' && check_it[1] == 'n' && check_it[2] == 'f')
-            {
-                auto val = std::numeric_limits<double>::infinity();
-                if (*it == '-')
-                    val = -val;
-                it = check_it + 3;
-                return make_value(val);
-            }
-            else if (check_it[0] == 'n' && check_it[1] == 'a'
-                     && check_it[2] == 'n')
-            {
-                auto val = std::numeric_limits<double>::quiet_NaN();
-                if (*it == '-')
-                    val = -val;
-                it = check_it + 3;
-                return make_value(val);
-            }
-        }
-
-        eat_numbers();
-
-        if (check_it != end
-            && (*check_it == '.' || *check_it == 'e' || *check_it == 'E'))
-        {
-            bool is_exp = *check_it == 'e' || *check_it == 'E';
-
-            ++check_it;
-            if (check_it == end)
-                throw_parse_exception("Floats must have trailing digits");
-
-            auto eat_exp = [&]() {
-                eat_sign();
-                check_no_leading_zero();
-                eat_numbers();
-            };
-
-            if (is_exp)
-                eat_exp();
-            else
-                eat_numbers();
-
-            if (!is_exp && check_it != end
-                && (*check_it == 'e' || *check_it == 'E'))
-            {
-                ++check_it;
-                eat_exp();
-            }
-
-            return parse_float(it, check_it);
-        }
-        else
-        {
-            return parse_int(it, check_it);
-        }
-    }
-
-    std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,
-                                              const std::string::iterator& end,
-                                              int base = 10,
-                                              const char* prefix = "")
-    {
-        std::string v{it, end};
-        v = prefix + v;
-        v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
-        it = end;
-        try
-        {
-            return make_value<int64_t>(std::stoll(v, nullptr, base));
-        }
-        catch (const std::invalid_argument& ex)
-        {
-            throw_parse_exception("Malformed number (invalid argument: "
-                                  + std::string{ex.what()} + ")");
-        }
-        catch (const std::out_of_range& ex)
-        {
-            throw_parse_exception("Malformed number (out of range: "
-                                  + std::string{ex.what()} + ")");
-        }
-    }
-
-    std::shared_ptr<value<double>> parse_float(std::string::iterator& it,
-                                               const std::string::iterator& end)
-    {
-        std::string v{it, end};
-        v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
-        it = end;
-        char decimal_point = std::localeconv()->decimal_point[0];
-        std::replace(v.begin(), v.end(), '.', decimal_point);
-        try
-        {
-            return make_value<double>(std::stod(v));
-        }
-        catch (const std::invalid_argument& ex)
-        {
-            throw_parse_exception("Malformed number (invalid argument: "
-                                  + std::string{ex.what()} + ")");
-        }
-        catch (const std::out_of_range& ex)
-        {
-            throw_parse_exception("Malformed number (out of range: "
-                                  + std::string{ex.what()} + ")");
-        }
-    }
-
-    std::shared_ptr<value<bool>> parse_bool(std::string::iterator& it,
-                                            const std::string::iterator& end)
-    {
-        auto eat = make_consumer(it, end, [this]() {
-            throw_parse_exception("Attempted to parse invalid boolean value");
-        });
-
-        if (*it == 't')
-        {
-            eat("true");
-            return make_value<bool>(true);
-        }
-        else if (*it == 'f')
-        {
-            eat("false");
-            return make_value<bool>(false);
-        }
-
-        eat.error();
-        return nullptr;
-    }
-
-    std::string::iterator find_end_of_number(std::string::iterator it,
-                                             std::string::iterator end)
-    {
-        auto ret = std::find_if(it, end, [](char c) {
-            return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E'
-                   && c != '-' && c != '+' && c != 'x' && c != 'o' && c != 'b';
-        });
-        if (ret != end && ret + 1 != end && ret + 2 != end)
-        {
-            if ((ret[0] == 'i' && ret[1] == 'n' && ret[2] == 'f')
-                || (ret[0] == 'n' && ret[1] == 'a' && ret[2] == 'n'))
-            {
-                ret = ret + 3;
-            }
-        }
-        return ret;
-    }
-
-    std::string::iterator find_end_of_date(std::string::iterator it,
-                                           std::string::iterator end)
-    {
-        auto end_of_date = std::find_if(it, end, [](char c) {
-            return !is_number(c) && c != '-';
-        });
-        if (end_of_date != end && *end_of_date == ' ' && end_of_date + 1 != end
-            && is_number(end_of_date[1]))
-            end_of_date++;
-        return std::find_if(end_of_date, end, [](char c) {
-            return !is_number(c) && c != 'T' && c != 'Z' && c != ':'
-                   && c != '-' && c != '+' && c != '.';
-        });
-    }
-
-    std::string::iterator find_end_of_time(std::string::iterator it,
-                                           std::string::iterator end)
-    {
-        return std::find_if(it, end, [](char c) {
-            return !is_number(c) && c != ':' && c != '.';
-        });
-    }
-
-    local_time read_time(std::string::iterator& it,
-                         const std::string::iterator& end)
-    {
-        auto time_end = find_end_of_time(it, end);
-
-        auto eat = make_consumer(
-            it, time_end, [&]() { throw_parse_exception("Malformed time"); });
-
-        local_time ltime;
-
-        ltime.hour = eat.eat_digits(2);
-        eat(':');
-        ltime.minute = eat.eat_digits(2);
-        eat(':');
-        ltime.second = eat.eat_digits(2);
-
-        int power = 100000;
-        if (it != time_end && *it == '.')
-        {
-            ++it;
-            while (it != time_end && is_number(*it))
-            {
-                ltime.microsecond += power * (*it++ - '0');
-                power /= 10;
-            }
-        }
-
-        if (it != time_end)
-            throw_parse_exception("Malformed time");
-
-        return ltime;
-    }
-
-    std::shared_ptr<value<local_time>>
-    parse_time(std::string::iterator& it, const std::string::iterator& end)
-    {
-        return make_value(read_time(it, end));
-    }
-
-    std::shared_ptr<base> parse_date(std::string::iterator& it,
-                                     const std::string::iterator& end)
-    {
-        auto date_end = find_end_of_date(it, end);
-
-        auto eat = make_consumer(
-            it, date_end, [&]() { throw_parse_exception("Malformed date"); });
-
-        local_date ldate;
-        ldate.year = eat.eat_digits(4);
-        eat('-');
-        ldate.month = eat.eat_digits(2);
-        eat('-');
-        ldate.day = eat.eat_digits(2);
-
-        if (it == date_end)
-            return make_value(ldate);
-
-        eat.eat_or('T', ' ');
-
-        local_datetime ldt;
-        static_cast<local_date&>(ldt) = ldate;
-        static_cast<local_time&>(ldt) = read_time(it, date_end);
-
-        if (it == date_end)
-            return make_value(ldt);
-
-        offset_datetime dt;
-        static_cast<local_datetime&>(dt) = ldt;
-
-        int hoff = 0;
-        int moff = 0;
-        if (*it == '+' || *it == '-')
-        {
-            auto plus = *it == '+';
-            ++it;
-
-            hoff = eat.eat_digits(2);
-            dt.hour_offset = (plus) ? hoff : -hoff;
-            eat(':');
-            moff = eat.eat_digits(2);
-            dt.minute_offset = (plus) ? moff : -moff;
-        }
-        else if (*it == 'Z')
-        {
-            ++it;
-        }
-
-        if (it != date_end)
-            throw_parse_exception("Malformed date");
-
-        return make_value(dt);
-    }
-
-    std::shared_ptr<base> parse_array(std::string::iterator& it,
-                                      std::string::iterator& end)
-    {
-        // this gets ugly because of the "homogeneity" restriction:
-        // arrays can either be of only one type, or contain arrays
-        // (each of those arrays could be of different types, though)
-        //
-        // because of the latter portion, we don't really have a choice
-        // but to represent them as arrays of base values...
-        ++it;
-
-        // ugh---have to read the first value to determine array type...
-        skip_whitespace_and_comments(it, end);
-
-        // edge case---empty array
-        if (*it == ']')
-        {
-            ++it;
-            return make_array();
-        }
-
-        auto val_end = std::find_if(
-            it, end, [](char c) { return c == ',' || c == ']' || c == '#'; });
-        parse_type type = determine_value_type(it, val_end);
-        switch (type)
-        {
-            case parse_type::STRING:
-                return parse_value_array<std::string>(it, end);
-            case parse_type::LOCAL_TIME:
-                return parse_value_array<local_time>(it, end);
-            case parse_type::LOCAL_DATE:
-                return parse_value_array<local_date>(it, end);
-            case parse_type::LOCAL_DATETIME:
-                return parse_value_array<local_datetime>(it, end);
-            case parse_type::OFFSET_DATETIME:
-                return parse_value_array<offset_datetime>(it, end);
-            case parse_type::INT:
-                return parse_value_array<int64_t>(it, end);
-            case parse_type::FLOAT:
-                return parse_value_array<double>(it, end);
-            case parse_type::BOOL:
-                return parse_value_array<bool>(it, end);
-            case parse_type::ARRAY:
-                return parse_object_array<array>(&parser::parse_array, '[', it,
-                                                 end);
-            case parse_type::INLINE_TABLE:
-                return parse_object_array<table_array>(
-                    &parser::parse_inline_table, '{', it, end);
-            default:
-                throw_parse_exception("Unable to parse array");
-        }
-    }
-
-    template <class Value>
-    std::shared_ptr<array> parse_value_array(std::string::iterator& it,
-                                             std::string::iterator& end)
-    {
-        auto arr = make_array();
-        while (it != end && *it != ']')
-        {
-            auto val = parse_value(it, end);
-            if (auto v = val->as<Value>())
-                arr->get().push_back(val);
-            else
-                throw_parse_exception("Arrays must be homogeneous");
-            skip_whitespace_and_comments(it, end);
-            if (*it != ',')
-                break;
-            ++it;
-            skip_whitespace_and_comments(it, end);
-        }
-        if (it != end)
-            ++it;
-        return arr;
-    }
-
-    template <class Object, class Function>
-    std::shared_ptr<Object> parse_object_array(Function&& fun, char delim,
-                                               std::string::iterator& it,
-                                               std::string::iterator& end)
-    {
-        auto arr = detail::make_element<Object>();
-
-        while (it != end && *it != ']')
-        {
-            if (*it != delim)
-                throw_parse_exception("Unexpected character in array");
-
-            arr->get().push_back(((*this).*fun)(it, end));
-            skip_whitespace_and_comments(it, end);
-
-            if (it == end || *it != ',')
-                break;
-
-            ++it;
-            skip_whitespace_and_comments(it, end);
-        }
-
-        if (it == end || *it != ']')
-            throw_parse_exception("Unterminated array");
-
-        ++it;
-        return arr;
-    }
-
-    std::shared_ptr<table> parse_inline_table(std::string::iterator& it,
-                                              std::string::iterator& end)
-    {
-        auto tbl = make_table();
-        do
-        {
-            ++it;
-            if (it == end)
-                throw_parse_exception("Unterminated inline table");
-
-            consume_whitespace(it, end);
-            if (it != end && *it != '}')
-            {
-                parse_key_value(it, end, tbl.get());
-                consume_whitespace(it, end);
-            }
-        } while (*it == ',');
-
-        if (it == end || *it != '}')
-            throw_parse_exception("Unterminated inline table");
-
-        ++it;
-        consume_whitespace(it, end);
-
-        return tbl;
-    }
-
-    void skip_whitespace_and_comments(std::string::iterator& start,
-                                      std::string::iterator& end)
-    {
-        consume_whitespace(start, end);
-        while (start == end || *start == '#')
-        {
-            if (!detail::getline(input_, line_))
-                throw_parse_exception("Unclosed array");
-            line_number_++;
-            start = line_.begin();
-            end = line_.end();
-            consume_whitespace(start, end);
-        }
-    }
-
-    void consume_whitespace(std::string::iterator& it,
-                            const std::string::iterator& end)
-    {
-        while (it != end && (*it == ' ' || *it == '\t'))
-            ++it;
-    }
-
-    void consume_backwards_whitespace(std::string::iterator& back,
-                                      const std::string::iterator& front)
-    {
-        while (back != front && (*back == ' ' || *back == '\t'))
-            --back;
-    }
-
-    void eol_or_comment(const std::string::iterator& it,
-                        const std::string::iterator& end)
-    {
-        if (it != end && *it != '#')
-            throw_parse_exception("Unidentified trailing character '"
-                                  + std::string{*it}
-                                  + "'---did you forget a '#'?");
-    }
-
-    bool is_time(const std::string::iterator& it,
-                 const std::string::iterator& end)
-    {
-        auto time_end = find_end_of_time(it, end);
-        auto len = std::distance(it, time_end);
-
-        if (len < 8)
-            return false;
-
-        if (it[2] != ':' || it[5] != ':')
-            return false;
-
-        if (len > 8)
-            return it[8] == '.' && len > 9;
-
-        return true;
-    }
-
-    option<parse_type> date_type(const std::string::iterator& it,
-                                 const std::string::iterator& end)
-    {
-        auto date_end = find_end_of_date(it, end);
-        auto len = std::distance(it, date_end);
-
-        if (len < 10)
-            return {};
-
-        if (it[4] != '-' || it[7] != '-')
-            return {};
-
-        if (len >= 19 && (it[10] == 'T' || it[10] == ' ')
-            && is_time(it + 11, date_end))
-        {
-            // datetime type
-            auto time_end = find_end_of_time(it + 11, date_end);
-            if (time_end == date_end)
-                return {parse_type::LOCAL_DATETIME};
-            else
-                return {parse_type::OFFSET_DATETIME};
-        }
-        else if (len == 10)
-        {
-            // just a regular date
-            return {parse_type::LOCAL_DATE};
-        }
-
-        return {};
-    }
-
-    std::istream& input_;
-    std::string line_;
-    std::size_t line_number_ = 0;
-};
-
-/**
- * Utility function to parse a file as a TOML file. Returns the root table.
- * Throws a parse_exception if the file cannot be opened.
- */
-inline std::shared_ptr<table> parse_file(const std::string& filename)
-{
-#if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP)
-    boost::nowide::ifstream file{filename.c_str()};
-#elif defined(NOWIDE_FSTREAM_INCLUDED_HPP)
-    nowide::ifstream file{filename.c_str()};
-#else
-    std::ifstream file{filename};
-#endif
-    if (!file.is_open())
-        throw parse_exception{filename + " could not be opened for parsing"};
-    parser p{file};
-    return p.parse();
-}
-
-template <class... Ts>
-struct value_accept;
-
-template <>
-struct value_accept<>
-{
-    template <class Visitor, class... Args>
-    static void accept(const base&, Visitor&&, Args&&...)
-    {
-        // nothing
-    }
-};
-
-template <class T, class... Ts>
-struct value_accept<T, Ts...>
-{
-    template <class Visitor, class... Args>
-    static void accept(const base& b, Visitor&& visitor, Args&&... args)
-    {
-        if (auto v = b.as<T>())
-        {
-            visitor.visit(*v, std::forward<Args>(args)...);
-        }
-        else
-        {
-            value_accept<Ts...>::accept(b, std::forward<Visitor>(visitor),
-                                        std::forward<Args>(args)...);
-        }
-    }
-};
-
-/**
- * base implementation of accept() that calls visitor.visit() on the concrete
- * class.
- */
-template <class Visitor, class... Args>
-void base::accept(Visitor&& visitor, Args&&... args) const
-{
-    if (is_value())
-    {
-        using value_acceptor
-            = value_accept<std::string, int64_t, double, bool, local_date,
-                           local_time, local_datetime, offset_datetime>;
-        value_acceptor::accept(*this, std::forward<Visitor>(visitor),
-                               std::forward<Args>(args)...);
-    }
-    else if (is_table())
-    {
-        visitor.visit(static_cast<const table&>(*this),
-                      std::forward<Args>(args)...);
-    }
-    else if (is_array())
-    {
-        visitor.visit(static_cast<const array&>(*this),
-                      std::forward<Args>(args)...);
-    }
-    else if (is_table_array())
-    {
-        visitor.visit(static_cast<const table_array&>(*this),
-                      std::forward<Args>(args)...);
-    }
-}
-
-/**
- * Writer that can be passed to accept() functions of cpptoml objects and
- * will output valid TOML to a stream.
- */
-class toml_writer
-{
-  public:
-    /**
-     * Construct a toml_writer that will write to the given stream
-     */
-    toml_writer(std::ostream& s, const std::string& indent_space = "\t")
-        : stream_(s), indent_(indent_space), has_naked_endline_(false)
-    {
-        // nothing
-    }
-
-  public:
-    /**
-     * Output a base value of the TOML tree.
-     */
-    template <class T>
-    void visit(const value<T>& v, bool = false)
-    {
-        write(v);
-    }
-
-    /**
-     * Output a table element of the TOML tree
-     */
-    void visit(const table& t, bool in_array = false)
-    {
-        write_table_header(in_array);
-        std::vector<std::string> values;
-        std::vector<std::string> tables;
-
-        for (const auto& i : t)
-        {
-            if (i.second->is_table() || i.second->is_table_array())
-            {
-                tables.push_back(i.first);
-            }
-            else
-            {
-                values.push_back(i.first);
-            }
-        }
-
-        for (unsigned int i = 0; i < values.size(); ++i)
-        {
-            path_.push_back(values[i]);
-
-            if (i > 0)
-                endline();
-
-            write_table_item_header(*t.get(values[i]));
-            t.get(values[i])->accept(*this, false);
-            path_.pop_back();
-        }
-
-        for (unsigned int i = 0; i < tables.size(); ++i)
-        {
-            path_.push_back(tables[i]);
-
-            if (values.size() > 0 || i > 0)
-                endline();
-
-            write_table_item_header(*t.get(tables[i]));
-            t.get(tables[i])->accept(*this, false);
-            path_.pop_back();
-        }
-
-        endline();
-    }
-
-    /**
-     * Output an array element of the TOML tree
-     */
-    void visit(const array& a, bool = false)
-    {
-        write("[");
-
-        for (unsigned int i = 0; i < a.get().size(); ++i)
-        {
-            if (i > 0)
-                write(", ");
-
-            if (a.get()[i]->is_array())
-            {
-                a.get()[i]->as_array()->accept(*this, true);
-            }
-            else
-            {
-                a.get()[i]->accept(*this, true);
-            }
-        }
-
-        write("]");
-    }
-
-    /**
-     * Output a table_array element of the TOML tree
-     */
-    void visit(const table_array& t, bool = false)
-    {
-        for (unsigned int j = 0; j < t.get().size(); ++j)
-        {
-            if (j > 0)
-                endline();
-
-            t.get()[j]->accept(*this, true);
-        }
-
-        endline();
-    }
-
-    /**
-     * Escape a string for output.
-     */
-    static std::string escape_string(const std::string& str)
-    {
-        std::string res;
-        for (auto it = str.begin(); it != str.end(); ++it)
-        {
-            if (*it == '\b')
-            {
-                res += "\\b";
-            }
-            else if (*it == '\t')
-            {
-                res += "\\t";
-            }
-            else if (*it == '\n')
-            {
-                res += "\\n";
-            }
-            else if (*it == '\f')
-            {
-                res += "\\f";
-            }
-            else if (*it == '\r')
-            {
-                res += "\\r";
-            }
-            else if (*it == '"')
-            {
-                res += "\\\"";
-            }
-            else if (*it == '\\')
-            {
-                res += "\\\\";
-            }
-            else if (static_cast<uint32_t>(*it) <= UINT32_C(0x001f))
-            {
-                res += "\\u";
-                std::stringstream ss;
-                ss << std::hex << static_cast<uint32_t>(*it);
-                res += ss.str();
-            }
-            else
-            {
-                res += *it;
-            }
-        }
-        return res;
-    }
-
-  protected:
-    /**
-     * Write out a string.
-     */
-    void write(const value<std::string>& v)
-    {
-        write("\"");
-        write(escape_string(v.get()));
-        write("\"");
-    }
-
-    /**
-     * Write out a double.
-     */
-    void write(const value<double>& v)
-    {
-        std::stringstream ss;
-        ss << std::showpoint
-           << std::setprecision(std::numeric_limits<double>::max_digits10)
-           << v.get();
-
-        auto double_str = ss.str();
-        auto pos = double_str.find("e0");
-        if (pos != std::string::npos)
-            double_str.replace(pos, 2, "e");
-        pos = double_str.find("e-0");
-        if (pos != std::string::npos)
-            double_str.replace(pos, 3, "e-");
-
-        stream_ << double_str;
-        has_naked_endline_ = false;
-    }
-
-    /**
-     * Write out an integer, local_date, local_time, local_datetime, or
-     * offset_datetime.
-     */
-    template <class T>
-    typename std::enable_if<
-        is_one_of<T, int64_t, local_date, local_time, local_datetime,
-                  offset_datetime>::value>::type
-    write(const value<T>& v)
-    {
-        write(v.get());
-    }
-
-    /**
-     * Write out a boolean.
-     */
-    void write(const value<bool>& v)
-    {
-        write((v.get() ? "true" : "false"));
-    }
-
-    /**
-     * Write out the header of a table.
-     */
-    void write_table_header(bool in_array = false)
-    {
-        if (!path_.empty())
-        {
-            indent();
-
-            write("[");
-
-            if (in_array)
-            {
-                write("[");
-            }
-
-            for (unsigned int i = 0; i < path_.size(); ++i)
-            {
-                if (i > 0)
-                {
-                    write(".");
-                }
-
-                if (path_[i].find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
-                                               "fghijklmnopqrstuvwxyz0123456789"
-                                               "_-")
-                    == std::string::npos)
-                {
-                    write(path_[i]);
-                }
-                else
-                {
-                    write("\"");
-                    write(escape_string(path_[i]));
-                    write("\"");
-                }
-            }
-
-            if (in_array)
-            {
-                write("]");
-            }
-
-            write("]");
-            endline();
-        }
-    }
-
-    /**
-     * Write out the identifier for an item in a table.
-     */
-    void write_table_item_header(const base& b)
-    {
-        if (!b.is_table() && !b.is_table_array())
-        {
-            indent();
-
-            if (path_.back().find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
-                                               "fghijklmnopqrstuvwxyz0123456789"
-                                               "_-")
-                == std::string::npos)
-            {
-                write(path_.back());
-            }
-            else
-            {
-                write("\"");
-                write(escape_string(path_.back()));
-                write("\"");
-            }
-
-            write(" = ");
-        }
-    }
-
-  private:
-    /**
-     * Indent the proper number of tabs given the size of
-     * the path.
-     */
-    void indent()
-    {
-        for (std::size_t i = 1; i < path_.size(); ++i)
-            write(indent_);
-    }
-
-    /**
-     * Write a value out to the stream.
-     */
-    template <class T>
-    void write(const T& v)
-    {
-        stream_ << v;
-        has_naked_endline_ = false;
-    }
-
-    /**
-     * Write an endline out to the stream
-     */
-    void endline()
-    {
-        if (!has_naked_endline_)
-        {
-            stream_ << "\n";
-            has_naked_endline_ = true;
-        }
-    }
-
-  private:
-    std::ostream& stream_;
-    const std::string indent_;
-    std::vector<std::string> path_;
-    bool has_naked_endline_;
-};
-
-inline std::ostream& operator<<(std::ostream& stream, const base& b)
-{
-    toml_writer writer{stream};
-    b.accept(writer);
-    return stream;
-}
-
-template <class T>
-std::ostream& operator<<(std::ostream& stream, const value<T>& v)
-{
-    toml_writer writer{stream};
-    v.accept(writer);
-    return stream;
-}
-
-inline std::ostream& operator<<(std::ostream& stream, const table& t)
-{
-    toml_writer writer{stream};
-    t.accept(writer);
-    return stream;
-}
-
-inline std::ostream& operator<<(std::ostream& stream, const table_array& t)
-{
-    toml_writer writer{stream};
-    t.accept(writer);
-    return stream;
-}
-
-inline std::ostream& operator<<(std::ostream& stream, const array& a)
-{
-    toml_writer writer{stream};
-    a.accept(writer);
-    return stream;
-}
-} // namespace cpptoml
-#endif // CPPTOML_H
diff --git a/third_party/nix/src/libexpr/CMakeLists.txt b/third_party/nix/src/libexpr/CMakeLists.txt
deleted file mode 100644
index 8cb7143d2c..0000000000
--- a/third_party/nix/src/libexpr/CMakeLists.txt
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- mode: cmake; -*-
-add_library(nixexpr SHARED)
-set_property(TARGET nixexpr PROPERTY CXX_STANDARD 17)
-include_directories(${PROJECT_BINARY_DIR}) # for 'generated/'
-target_include_directories(nixexpr PUBLIC "${nix_SOURCE_DIR}/src")
-
-# Generate lexer & parser for inclusion:
-find_package(BISON)
-find_package(FLEX)
-
-BISON_TARGET(NixParser parser.y
-  ${PROJECT_BINARY_DIR}/generated/parser-tab.cc
-  DEFINES_FILE ${PROJECT_BINARY_DIR}/generated/parser-tab.hh)
-
-FLEX_TARGET(NixLexer lexer.l
-  ${PROJECT_BINARY_DIR}/generated/lexer-tab.cc
-  DEFINES_FILE ${PROJECT_BINARY_DIR}/generated/lexer-tab.hh)
-
-ADD_FLEX_BISON_DEPENDENCY(NixLexer NixParser)
-
-set(HEADER_FILES
-    attr-path.hh
-    attr-set.hh
-    common-eval-args.hh
-    eval.hh
-    eval-inline.hh
-    function-trace.hh
-    get-drvs.hh
-    json-to-value.hh
-    names.hh
-    nixexpr.hh
-    parser.hh
-    primops.hh
-    symbol-table.hh
-    value.hh
-    value-to-json.hh
-    value-to-xml.hh
-)
-
-target_sources(nixexpr
-  PUBLIC
-    ${HEADER_FILES}
-
-  PRIVATE
-    ${PROJECT_BINARY_DIR}/generated/parser-tab.hh
-    ${PROJECT_BINARY_DIR}/generated/parser-tab.cc
-    ${PROJECT_BINARY_DIR}/generated/lexer-tab.hh
-    ${PROJECT_BINARY_DIR}/generated/lexer-tab.cc
-    primops/context.cc
-    primops/fetchGit.cc
-    primops/fetchMercurial.cc
-    primops/fromTOML.cc
-    attr-path.cc
-    attr-set.cc
-    common-eval-args.cc
-    eval.cc
-    function-trace.cc
-    get-drvs.cc
-    json-to-value.cc
-    names.cc
-    nixexpr.cc
-    parser.cc
-    primops.cc
-    symbol-table.cc
-    value.cc
-    value-to-json.cc
-    value-to-xml.cc
-)
-
-target_link_libraries(nixexpr
-  nixmain
-  nixstore
-  nixutil
-
-  absl::btree
-  absl::flat_hash_set
-  absl::node_hash_set
-  absl::strings
-)
-
-configure_file("nix-expr.pc.in" "${PROJECT_BINARY_DIR}/nix-expr.pc" @ONLY)
-INSTALL(FILES "${PROJECT_BINARY_DIR}/nix-expr.pc" DESTINATION "${PKGCONFIG_INSTALL_DIR}")
-
-INSTALL(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nix/libexpr)
-INSTALL(TARGETS nixexpr DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/third_party/nix/src/libexpr/attr-path.cc b/third_party/nix/src/libexpr/attr-path.cc
deleted file mode 100644
index 86ebeec2fb..0000000000
--- a/third_party/nix/src/libexpr/attr-path.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-#include "libexpr/attr-path.hh"
-
-#include <absl/strings/numbers.h>
-
-#include "libexpr/eval-inline.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-static Strings parseAttrPath(const std::string& s) {
-  Strings res;
-  std::string cur;
-  std::string::const_iterator i = s.begin();
-  while (i != s.end()) {
-    if (*i == '.') {
-      res.push_back(cur);
-      cur.clear();
-    } else if (*i == '"') {
-      ++i;
-      while (true) {
-        if (i == s.end()) {
-          throw Error(format("missing closing quote in selection path '%1%'") %
-                      s);
-        }
-        if (*i == '"') {
-          break;
-        }
-        cur.push_back(*i++);
-      }
-    } else {
-      cur.push_back(*i);
-    }
-    ++i;
-  }
-  if (!cur.empty()) {
-    res.push_back(cur);
-  }
-  return res;
-}
-
-Value* findAlongAttrPath(EvalState& state, const std::string& attrPath,
-                         Bindings* autoArgs, Value& vIn) {
-  Strings tokens = parseAttrPath(attrPath);
-
-  Error attrError =
-      Error(format("attribute selection path '%1%' does not match expression") %
-            attrPath);
-
-  Value* v = &vIn;
-
-  for (auto& attr : tokens) {
-    /* Is i an index (integer) or a normal attribute name? */
-    enum { apAttr, apIndex } apType = apAttr;
-    unsigned int attrIndex;
-    if (absl::SimpleAtoi(attr, &attrIndex)) {
-      apType = apIndex;
-    }
-
-    /* Evaluate the expression. */
-    Value* vNew = state.allocValue();
-    state.autoCallFunction(autoArgs, *v, *vNew);
-    v = vNew;
-    state.forceValue(*v);
-
-    /* It should evaluate to either a set or an expression,
-       according to what is specified in the attrPath. */
-
-    if (apType == apAttr) {
-      if (v->type != tAttrs) {
-        throw TypeError(format("the expression selected by the selection path "
-                               "'%1%' should be a set but is %2%") %
-                        attrPath % showType(*v));
-      }
-
-      if (attr.empty()) {
-        throw Error(format("empty attribute name in selection path '%1%'") %
-                    attrPath);
-      }
-
-      Bindings::iterator a = v->attrs->find(state.symbols.Create(attr));
-      if (a == v->attrs->end()) {
-        throw Error(
-            format("attribute '%1%' in selection path '%2%' not found") % attr %
-            attrPath);
-      }
-      v = &*(a->second).value;
-    }
-
-    else if (apType == apIndex) {
-      if (!v->isList()) {
-        throw TypeError(format("the expression selected by the selection path "
-                               "'%1%' should be a list but is %2%") %
-                        attrPath % showType(*v));
-      }
-
-      if (attrIndex >= v->listSize()) {
-        throw Error(
-            format("list index %1% in selection path '%2%' is out of range") %
-            attrIndex % attrPath);
-      }
-
-      v = (*v->list)[attrIndex];
-    }
-  }
-
-  return v;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/attr-path.hh b/third_party/nix/src/libexpr/attr-path.hh
deleted file mode 100644
index 97170be840..0000000000
--- a/third_party/nix/src/libexpr/attr-path.hh
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include <map>
-#include <string>
-
-#include "libexpr/eval.hh"
-
-namespace nix {
-
-Value* findAlongAttrPath(EvalState& state, const std::string& attrPath,
-                         Bindings* autoArgs, Value& vIn);
-
-}
diff --git a/third_party/nix/src/libexpr/attr-set.cc b/third_party/nix/src/libexpr/attr-set.cc
deleted file mode 100644
index b1617c981f..0000000000
--- a/third_party/nix/src/libexpr/attr-set.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-#include "libexpr/attr-set.hh"
-
-#include <new>
-
-#include <absl/container/btree_map.h>
-#include <glog/logging.h>
-
-#include "libexpr/eval-inline.hh"
-
-namespace nix {
-
-// This function inherits its name from previous implementations, in
-// which Bindings was backed by an array of elements which was scanned
-// linearly.
-//
-// In that setup, inserting duplicate elements would always yield the
-// first element (until the next sort, which wasn't stable, after
-// which things are more or less undefined).
-//
-// This behaviour is mimicked by using .insert(), which will *not*
-// override existing values.
-void Bindings::push_back(const Attr& attr) {
-  auto [_, inserted] = attributes_.insert({attr.name, attr});
-
-  if (!inserted) {
-    DLOG(WARNING) << "attempted to insert duplicate attribute for key '"
-                  << attr.name << "'";
-  }
-}
-
-size_t Bindings::size() const { return attributes_.size(); }
-
-bool Bindings::empty() { return attributes_.empty(); }
-
-Bindings::iterator Bindings::find(const Symbol& name) {
-  return attributes_.find(name);
-}
-
-bool Bindings::Equal(const Bindings* other, EvalState& state) const {
-  if (this == other) {
-    return true;
-  }
-
-  if (this->attributes_.size() != other->attributes_.size()) {
-    return false;
-  }
-
-  Bindings::const_iterator i;
-  Bindings::const_iterator j;
-  for (i = this->cbegin(), j = other->cbegin(); i != this->cend(); ++i, ++j) {
-    if (i->second.name != j->second.name ||
-        !state.eqValues(*i->second.value, *j->second.value)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-Bindings::iterator Bindings::begin() { return attributes_.begin(); }
-Bindings::iterator Bindings::end() { return attributes_.end(); }
-
-Bindings::const_iterator Bindings::cbegin() const {
-  return attributes_.cbegin();
-}
-
-Bindings::const_iterator Bindings::cend() const { return attributes_.cend(); }
-
-std::unique_ptr<Bindings> Bindings::New(size_t capacity) {
-  if (capacity == 0) {
-    // TODO(tazjin): A lot of 0-capacity Bindings are allocated.
-    // It would be nice to optimize that.
-  }
-
-  return std::make_unique<Bindings>();
-}
-
-std::unique_ptr<Bindings> Bindings::Merge(const Bindings& lhs,
-                                          const Bindings& rhs) {
-  auto bindings = New(lhs.size() + rhs.size());
-
-  // Values are merged by inserting the entire iterator range of both
-  // input sets. The right-hand set (the values of which take
-  // precedence) is inserted *first* because the range insertion
-  // method does not override values.
-  bindings->attributes_.insert(rhs.attributes_.cbegin(),
-                               rhs.attributes_.cend());
-  bindings->attributes_.insert(lhs.attributes_.cbegin(),
-                               lhs.attributes_.cend());
-
-  return bindings;
-}
-
-void EvalState::mkAttrs(Value& v, size_t capacity) {
-  clearValue(v);
-  v.type = tAttrs;
-  v.attrs = Bindings::New(capacity);
-  nrAttrsets++;
-  nrAttrsInAttrsets += capacity;
-}
-
-/* Create a new attribute named 'name' on an existing attribute set stored
-   in 'vAttrs' and return the newly allocated Value which is associated with
-   this attribute. */
-Value* EvalState::allocAttr(Value& vAttrs, const Symbol& name) {
-  Value* v = allocValue();
-  vAttrs.attrs->push_back(Attr(name, v));
-  return v;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/attr-set.hh b/third_party/nix/src/libexpr/attr-set.hh
deleted file mode 100644
index 5d77e0907c..0000000000
--- a/third_party/nix/src/libexpr/attr-set.hh
+++ /dev/null
@@ -1,69 +0,0 @@
-// This file implements the underlying structure of Nix attribute sets.
-#pragma once
-
-#include <absl/container/btree_map.h>
-
-#include "libexpr/nixexpr.hh"
-#include "libexpr/symbol-table.hh"
-#include "libutil/types.hh"
-
-namespace nix {  // TODO(tazjin): ::expr
-
-class EvalState;
-struct Value;
-
-/* Map one attribute name to its value. */
-struct Attr {
-  Symbol name;
-  Value* value;  // TODO(tazjin): Who owns this?
-  Pos* pos;      // TODO(tazjin): Who owns this?
-  Attr(Symbol name, Value* value, Pos* pos = &noPos)
-      : name(name), value(value), pos(pos){};
-};
-
-using AttributeMap = absl::btree_map<Symbol, Attr>;
-
-class Bindings {
- public:
-  using iterator = AttributeMap::iterator;
-  using const_iterator = AttributeMap::const_iterator;
-
-  // Allocate a new attribute set that is visible to the garbage
-  // collector.
-  static std::unique_ptr<Bindings> New(size_t capacity = 0);
-
-  // Create a new attribute set by merging two others. This is used to
-  // implement the `//` operator in Nix.
-  static std::unique_ptr<Bindings> Merge(const Bindings& lhs,
-                                         const Bindings& rhs);
-
-  // Return the number of contained elements.
-  size_t size() const;
-
-  // Is this attribute set empty?
-  bool empty();
-
-  // Insert, but do not replace, values in the attribute set.
-  void push_back(const Attr& attr);
-
-  // Are these two attribute sets deeply equal?
-  // Note: Does not special-case derivations. Use state.eqValues() to check
-  // attrsets that may be derivations.
-  bool Equal(const Bindings* other, EvalState& state) const;
-
-  // Look up a specific element of the attribute set.
-  iterator find(const Symbol& name);
-
-  iterator begin();
-  const_iterator cbegin() const;
-  iterator end();
-  const_iterator cend() const;
-
-  // oh no
-  friend class EvalState;
-
- private:
-  AttributeMap attributes_;
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/common-eval-args.cc b/third_party/nix/src/libexpr/common-eval-args.cc
deleted file mode 100644
index f63d3f8276..0000000000
--- a/third_party/nix/src/libexpr/common-eval-args.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-#include "libexpr/common-eval-args.hh"
-
-#include "libexpr/eval.hh"
-#include "libmain/shared.hh"
-#include "libstore/download.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-MixEvalArgs::MixEvalArgs() {
-  mkFlag()
-      .longName("arg")
-      .description("argument to be passed to Nix functions")
-      .labels({"name", "expr"})
-      .handler([&](std::vector<std::string> ss) {
-        auto_args_[ss[0]] = std::make_pair(kArgTypeExpr, ss[1]);
-      });
-
-  mkFlag()
-      .longName("argstr")
-      .description("string-valued argument to be passed to Nix functions")
-      .labels({"name", "string"})
-      .handler([&](std::vector<std::string> ss) {
-        auto_args_[ss[0]] = std::make_pair(kArgTypeString, ss[1]);
-      });
-
-  mkFlag()
-      .shortName('I')
-      .longName("include")
-      .description(
-          "add a path to the list of locations used to look up <...> file "
-          "names")
-      .label("path")
-      .handler([&](const std::string& s) { searchPath.push_back(s); });
-}
-
-std::unique_ptr<Bindings> MixEvalArgs::getAutoArgs(EvalState& state) {
-  auto res = Bindings::New(auto_args_.size());
-  for (auto& [arg, arg_value] : auto_args_) {
-    Value* v = state.allocValue();
-    switch (arg_value.first) {
-      case kArgTypeExpr: {
-        state.mkThunk_(
-            *v, state.parseExprFromString(arg_value.second, absPath(".")));
-        break;
-      }
-      case kArgTypeString: {
-        mkString(*v, arg_value.second);
-        break;
-      }
-    }
-
-    res->push_back(Attr(state.symbols.Create(arg), v));
-  }
-  return res;
-}
-
-Path lookupFileArg(EvalState& state, std::string s) {
-  if (isUri(s)) {
-    CachedDownloadRequest request(s);
-    request.unpack = true;
-    return getDownloader()->downloadCached(state.store, request).path;
-  }
-  if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
-    Path p = s.substr(1, s.size() - 2);
-    return state.findFile(p);
-  } else {
-    return absPath(s);
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/common-eval-args.hh b/third_party/nix/src/libexpr/common-eval-args.hh
deleted file mode 100644
index 5e0e8af79c..0000000000
--- a/third_party/nix/src/libexpr/common-eval-args.hh
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include "libutil/args.hh"
-
-namespace nix {
-
-class Store;
-class EvalState;
-class Bindings;
-
-enum ArgType { kArgTypeString, kArgTypeExpr };
-
-struct MixEvalArgs : virtual Args {
-  MixEvalArgs();
-
-  std::unique_ptr<Bindings> getAutoArgs(EvalState& state);
-
-  Strings searchPath;
-
- private:
-  std::map<std::string, std::pair<ArgType, std::string>> auto_args_;
-};
-
-Path lookupFileArg(EvalState& state, std::string s);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/eval-inline.hh b/third_party/nix/src/libexpr/eval-inline.hh
deleted file mode 100644
index 5162ab3971..0000000000
--- a/third_party/nix/src/libexpr/eval-inline.hh
+++ /dev/null
@@ -1,90 +0,0 @@
-#pragma once
-
-#include "libexpr/eval.hh"
-
-#define LocalNoInline(f)              \
-  static f __attribute__((noinline)); \
-  f
-#define LocalNoInlineNoReturn(f)                \
-  static f __attribute__((noinline, noreturn)); \
-  f
-
-namespace nix {
-
-LocalNoInlineNoReturn(void throwEvalError(const char* s, const Pos& pos)) {
-  throw EvalError(format(s) % pos);
-}
-
-LocalNoInlineNoReturn(void throwTypeError(const char* s, const Value& v)) {
-  throw TypeError(format(s) % showType(v));
-}
-
-LocalNoInlineNoReturn(void throwTypeError(const char* s, const Value& v,
-                                          const Pos& pos)) {
-  throw TypeError(format(s) % showType(v) % pos);
-}
-
-void EvalState::forceValue(Value& v, const Pos& pos) {
-  if (v.type == tThunk) {
-    Env* env = v.thunk.env;
-    Expr* expr = v.thunk.expr;
-    try {
-      v.type = tBlackhole;
-      // checkInterrupt();
-      expr->eval(*this, *env, v);
-    } catch (...) {
-      v.type = tThunk;
-      v.thunk.env = env;
-      v.thunk.expr = expr;
-      throw;
-    }
-  } else if (v.type == tApp) {
-    callFunction(*v.app.left, *v.app.right, v, noPos);
-  } else if (v.type == tBlackhole) {
-    throwEvalError("infinite recursion encountered, at %1%", pos);
-  }
-}
-
-inline void EvalState::forceAttrs(Value& v) {
-  forceValue(v);
-  if (v.type != tAttrs) {
-    throwTypeError("value is %1% while a set was expected", v);
-  }
-}
-
-inline void EvalState::forceAttrs(Value& v, const Pos& pos) {
-  forceValue(v);
-  if (v.type != tAttrs) {
-    throwTypeError("value is %1% while a set was expected, at %2%", v, pos);
-  }
-}
-
-inline void EvalState::forceList(Value& v) {
-  forceValue(v);
-  if (!v.isList()) {
-    throwTypeError("value is %1% while a list was expected", v);
-  }
-}
-
-inline void EvalState::forceList(Value& v, const Pos& pos) {
-  forceValue(v);
-  if (!v.isList()) {
-    throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
-  }
-}
-
-/* Note: Various places expect the allocated memory to be zeroed. */
-inline void* allocBytes(size_t n) {
-  void* p;
-#if HAVE_BOEHMGC
-  p = GC_MALLOC(n);
-#else
-  p = calloc(n, 1);
-#endif
-  if (!p) {
-    throw std::bad_alloc();
-  }
-  return p;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/eval.cc b/third_party/nix/src/libexpr/eval.cc
deleted file mode 100644
index 682ea64832..0000000000
--- a/third_party/nix/src/libexpr/eval.cc
+++ /dev/null
@@ -1,1878 +0,0 @@
-#include "libexpr/eval.hh"
-
-#include <algorithm>
-#include <chrono>
-#include <cstdint>
-#include <cstring>
-#include <fstream>
-#include <iostream>
-#include <memory>
-#include <new>
-#include <optional>
-#include <variant>
-
-#include <absl/base/call_once.h>
-#include <absl/container/flat_hash_set.h>
-#include <absl/strings/match.h>
-#include <glog/logging.h>
-#include <sys/resource.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#include "libexpr/eval-inline.hh"
-#include "libexpr/function-trace.hh"
-#include "libexpr/value.hh"
-#include "libstore/derivations.hh"
-#include "libstore/download.hh"
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libutil/hash.hh"
-#include "libutil/json.hh"
-#include "libutil/util.hh"
-#include "libutil/visitor.hh"
-
-namespace nix {
-namespace {
-
-void ConfigureGc() { /* This function intentionally left blank. */
-}
-
-}  // namespace
-
-namespace expr {
-
-absl::once_flag gc_flag;
-
-void InitGC() { absl::call_once(gc_flag, &ConfigureGc); }
-
-}  // namespace expr
-
-static char* dupString(const char* s) {
-  char* t;
-  t = strdup(s);
-  if (t == nullptr) {
-    throw std::bad_alloc();
-  }
-  return t;
-}
-
-std::shared_ptr<Value*> allocRootValue(Value* v) {
-  return std::make_shared<Value*>(v);
-}
-
-static void printValue(std::ostream& str, std::set<const Value*>& active,
-                       const Value& v) {
-  checkInterrupt();
-
-  if (active.find(&v) != active.end()) {
-    str << "<CYCLE>";
-    return;
-  }
-  active.insert(&v);
-
-  switch (v.type) {
-    case tInt:
-      str << v.integer;
-      break;
-    case tBool:
-      str << (v.boolean ? "true" : "false");
-      break;
-    case tString:
-      str << "\"";
-      for (const char* i = v.string.s; *i != 0; i++) {
-        if (*i == '\"' || *i == '\\') {
-          str << "\\" << *i;
-        } else if (*i == '\n') {
-          str << "\\n";
-        } else if (*i == '\r') {
-          str << "\\r";
-        } else if (*i == '\t') {
-          str << "\\t";
-        } else {
-          str << *i;
-        }
-      }
-      str << "\"";
-      break;
-    case tPath:
-      str << v.path;  // !!! escaping?
-      break;
-    case tNull:
-      str << "null";
-      break;
-    case tAttrs: {
-      str << "{ ";
-      for (const auto& [key, value] : *v.attrs) {
-        str << key << " = ";
-        printValue(str, active, *value.value);
-        str << "; ";
-      }
-      str << "}";
-      break;
-    }
-    case tList:
-      str << "[ ";
-      for (unsigned int n = 0; n < v.listSize(); ++n) {
-        printValue(str, active, *(*v.list)[n]);
-        str << " ";
-      }
-      str << "]";
-      break;
-    case tThunk:
-    case tApp:
-      str << "<CODE>";
-      break;
-    case tLambda:
-      str << "<LAMBDA>";
-      break;
-    case tPrimOp:
-      str << "<PRIMOP>";
-      break;
-    case tPrimOpApp:
-      str << "<PRIMOP-APP>";
-      break;
-    case tFloat:
-      str << v.fpoint;
-      break;
-    default:
-      throw Error(
-          absl::StrCat("invalid value of type ", static_cast<int>(v.type)));
-  }
-
-  active.erase(&v);
-}
-
-std::ostream& operator<<(std::ostream& str, const Value& v) {
-  std::set<const Value*> active;
-  printValue(str, active, v);
-  return str;
-}
-
-const Value* getPrimOp(const Value& v) {
-  const Value* primOp = &v;
-  while (primOp->type == tPrimOpApp) {
-    primOp = primOp->primOpApp.left;
-  }
-  assert(primOp->type == tPrimOp);
-  return primOp;
-}
-
-std::string showType(const Value& v) {
-  switch (v.type) {
-    case tInt:
-      return "an integer";
-    case tBool:
-      return "a boolean";
-    case tString:
-      return v.string.context != nullptr ? "a string with context" : "a string";
-    case tPath:
-      return "a path";
-    case tNull:
-      return "null";
-    case tAttrs:
-      return "a set";
-    case tList:
-      return "a list";
-    case tThunk:
-      return "a thunk";
-    case tApp:
-      return "a function application";
-    case tLambda:
-      return "a function";
-    case tBlackhole:
-      return "a black hole";
-    case tPrimOp:
-      return fmt("the built-in function '%s'", std::string(v.primOp->name));
-    case tPrimOpApp:
-      return fmt("the partially applied built-in function '%s'",
-                 std::string(getPrimOp(v)->primOp->name));
-    case _reserved1:
-      LOG(FATAL) << "attempted to show the type string of the deprecated "
-                    "tExternal value";
-      break;
-    case tFloat:
-      return "a float";
-  }
-  LOG(FATAL)
-      << "attempted to determine the type string of an unknown type number ("
-      << static_cast<int>(v.type) << ")";
-  abort();
-}
-
-static Symbol getName(const AttrName& name, EvalState& state, Env& env) {
-  return std::visit(
-      util::overloaded{[&](const Symbol& name) -> Symbol { return name; },
-                       [&](Expr* expr) -> Symbol {
-                         Value nameValue;
-                         expr->eval(state, env, nameValue);
-                         state.forceStringNoCtx(nameValue);
-                         return state.symbols.Create(nameValue.string.s);
-                       }},
-      name);
-}
-
-/* Very hacky way to parse $NIX_PATH, which is colon-separated, but
-   can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */
-static Strings parseNixPath(const std::string& s) {
-  Strings res;
-
-  auto p = s.begin();
-
-  while (p != s.end()) {
-    auto start = p;
-    auto start2 = p;
-
-    while (p != s.end() && *p != ':') {
-      if (*p == '=') {
-        start2 = p + 1;
-      }
-      ++p;
-    }
-
-    if (p == s.end()) {
-      if (p != start) {
-        res.push_back(std::string(start, p));
-      }
-      break;
-    }
-
-    if (*p == ':') {
-      if (isUri(std::string(start2, s.end()))) {
-        ++p;
-        while (p != s.end() && *p != ':') {
-          ++p;
-        }
-      }
-      res.push_back(std::string(start, p));
-      if (p == s.end()) {
-        break;
-      }
-    }
-
-    ++p;
-  }
-
-  return res;
-}
-
-EvalState::EvalState(const Strings& _searchPath, const ref<Store>& store)
-    : sWith(symbols.Create("<with>")),
-      sOutPath(symbols.Create("outPath")),
-      sDrvPath(symbols.Create("drvPath")),
-      sType(symbols.Create("type")),
-      sMeta(symbols.Create("meta")),
-      sName(symbols.Create("name")),
-      sValue(symbols.Create("value")),
-      sSystem(symbols.Create("system")),
-      sOutputs(symbols.Create("outputs")),
-      sOutputName(symbols.Create("outputName")),
-      sIgnoreNulls(symbols.Create("__ignoreNulls")),
-      sFile(symbols.Create("file")),
-      sLine(symbols.Create("line")),
-      sColumn(symbols.Create("column")),
-      sFunctor(symbols.Create("__functor")),
-      sToString(symbols.Create("__toString")),
-      sRight(symbols.Create("right")),
-      sWrong(symbols.Create("wrong")),
-      sStructuredAttrs(symbols.Create("__structuredAttrs")),
-      sBuilder(symbols.Create("builder")),
-      sArgs(symbols.Create("args")),
-      sOutputHash(symbols.Create("outputHash")),
-      sOutputHashAlgo(symbols.Create("outputHashAlgo")),
-      sOutputHashMode(symbols.Create("outputHashMode")),
-      sDerivationNix(std::nullopt),
-      repair(NoRepair),
-      store(store),
-      baseEnv(allocEnv(128)),
-      staticBaseEnv(false, nullptr) {
-  expr::InitGC();
-
-  countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0";
-
-  /* Initialise the Nix expression search path. */
-  if (!evalSettings.pureEval) {
-    Strings paths = parseNixPath(getEnv("NIX_PATH").value_or(""));
-    for (auto& i : _searchPath) {
-      addToSearchPath(i);
-    }
-    for (auto& i : paths) {
-      addToSearchPath(i);
-    }
-  }
-  addToSearchPath("nix=" +
-                  canonPath(settings.nixDataDir + "/nix/corepkgs", true));
-
-  if (evalSettings.restrictEval || evalSettings.pureEval) {
-    allowedPaths = PathSet();
-
-    for (auto& i : searchPath) {
-      auto r = resolveSearchPathElem(i);
-      if (!r.first) {
-        continue;
-      }
-
-      auto path = r.second;
-
-      if (store->isInStore(r.second)) {
-        PathSet closure;
-        store->computeFSClosure(store->toStorePath(r.second), closure);
-        for (auto& path : closure) {
-          allowedPaths->insert(path);
-        }
-      } else {
-        allowedPaths->insert(r.second);
-      }
-    }
-  }
-
-  createBaseEnv();
-}
-
-EvalState::~EvalState() = default;
-
-Path EvalState::checkSourcePath(const Path& path_) {
-  TraceFileAccess(path_);
-  if (!allowedPaths) {
-    return path_;
-  }
-
-  auto i = resolvedPaths.find(path_);
-  if (i != resolvedPaths.end()) {
-    return i->second;
-  }
-
-  bool found = false;
-
-  /* First canonicalize the path without symlinks, so we make sure an
-   * attacker can't append ../../... to a path that would be in allowedPaths
-   * and thus leak symlink targets.
-   */
-  Path abspath = canonPath(path_);
-
-  for (auto& i : *allowedPaths) {
-    if (isDirOrInDir(abspath, i)) {
-      found = true;
-      break;
-    }
-  }
-
-  if (!found) {
-    throw RestrictedPathError(
-        "access to path '%1%' is forbidden in restricted mode", abspath);
-  }
-
-  /* Resolve symlinks. */
-  DLOG(INFO) << "checking access to '" << abspath << "'";
-  Path path = canonPath(abspath, true);
-
-  for (auto& i : *allowedPaths) {
-    if (isDirOrInDir(path, i)) {
-      resolvedPaths[path_] = path;
-      return path;
-    }
-  }
-
-  throw RestrictedPathError(
-      "access to path '%1%' is forbidden in restricted mode", path);
-}
-
-void EvalState::checkURI(const std::string& uri) {
-  if (!evalSettings.restrictEval) {
-    return;
-  }
-
-  /* 'uri' should be equal to a prefix, or in a subdirectory of a
-     prefix. Thus, the prefix https://github.co does not permit
-     access to https://github.com. Note: this allows 'http://' and
-     'https://' as prefixes for any http/https URI. */
-  for (auto& prefix : evalSettings.allowedUris.get()) {
-    if (uri == prefix ||
-        (uri.size() > prefix.size() && !prefix.empty() &&
-         absl::StartsWith(uri, prefix) &&
-         (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/'))) {
-      return;
-    }
-  }
-
-  /* If the URI is a path, then check it against allowedPaths as
-     well. */
-  if (absl::StartsWith(uri, "/")) {
-    checkSourcePath(uri);
-    return;
-  }
-
-  if (absl::StartsWith(uri, "file://")) {
-    checkSourcePath(std::string(uri, 7));
-    return;
-  }
-
-  throw RestrictedPathError(
-      "access to URI '%s' is forbidden in restricted mode", uri);
-}
-
-Path EvalState::toRealPath(const Path& path, const PathSet& context) {
-  // FIXME: check whether 'path' is in 'context'.
-  return !context.empty() && store->isInStore(path) ? store->toRealPath(path)
-                                                    : path;
-};
-
-Value* EvalState::addConstant(const std::string& name, Value& v) {
-  Value* v2 = allocValue();
-  *v2 = v;
-  staticBaseEnv.vars[symbols.Create(name)] = baseEnvDispl;
-  baseEnv.values[baseEnvDispl++] = v2;
-  std::string name2 =
-      std::string(name, 0, 2) == "__" ? std::string(name, 2) : name;
-  baseEnv.values[0]->attrs->push_back(Attr(symbols.Create(name2), v2));
-  return v2;
-}
-
-Value* EvalState::addPrimOp(const std::string& name, size_t arity,
-                            PrimOpFun primOp) {
-  if (arity == 0) {
-    Value v;
-    primOp(*this, noPos, nullptr, v);
-    return addConstant(name, v);
-  }
-  std::string name2 =
-      std::string(name, 0, 2) == "__" ? std::string(name, 2) : name;
-  Symbol sym = symbols.Create(name2);
-  Value* v = allocValue();
-  v->type = tPrimOp;
-  v->primOp = std::make_shared<PrimOp>(primOp, arity, sym);
-  staticBaseEnv.vars[symbols.Create(name)] = baseEnvDispl;
-  baseEnv.values[baseEnvDispl++] = v;
-  baseEnv.values[0]->attrs->push_back(Attr(sym, v));
-  return v;
-}
-
-Value& EvalState::getBuiltin(const std::string& name) {
-  return *baseEnv.values[0]->attrs->find(symbols.Create(name))->second.value;
-}
-
-/* Every "format" object (even temporary) takes up a few hundred bytes
-   of stack space, which is a real killer in the recursive
-   evaluator.  So here are some helper functions for throwing
-   exceptions. */
-
-LocalNoInlineNoReturn(void throwEvalError(const char* s,
-                                          const std::string& s2)) {
-  throw EvalError(format(s) % s2);
-}
-
-LocalNoInlineNoReturn(void throwEvalError(const char* s, const std::string& s2,
-                                          const Pos& pos)) {
-  throw EvalError(format(s) % s2 % pos);
-}
-
-LocalNoInlineNoReturn(void throwEvalError(const char* s, const std::string& s2,
-                                          const std::string& s3)) {
-  throw EvalError(format(s) % s2 % s3);
-}
-
-LocalNoInlineNoReturn(void throwEvalError(const char* s, const std::string& s2,
-                                          const std::string& s3,
-                                          const Pos& pos)) {
-  throw EvalError(format(s) % s2 % s3 % pos);
-}
-
-LocalNoInlineNoReturn(void throwEvalError(const char* s, const Symbol& sym,
-                                          const Pos& p1, const Pos& p2)) {
-  throw EvalError(format(s) % sym % p1 % p2);
-}
-
-LocalNoInlineNoReturn(void throwTypeError(const char* s, const Pos& pos)) {
-  throw TypeError(format(s) % pos);
-}
-
-LocalNoInlineNoReturn(void throwTypeError(const char* s,
-                                          const std::string& s1)) {
-  throw TypeError(format(s) % s1);
-}
-
-LocalNoInlineNoReturn(void throwTypeError(const char* s, const ExprLambda& fun,
-                                          const Symbol& s2, const Pos& pos)) {
-  throw TypeError(format(s) % fun.showNamePos() % s2 % pos);
-}
-
-LocalNoInlineNoReturn(void throwAssertionError(const char* s,
-                                               const std::string& s1,
-                                               const Pos& pos)) {
-  throw AssertionError(format(s) % s1 % pos);
-}
-
-LocalNoInlineNoReturn(void throwUndefinedVarError(const char* s,
-                                                  const std::string& s1,
-                                                  const Pos& pos)) {
-  throw UndefinedVarError(format(s) % s1 % pos);
-}
-
-LocalNoInline(void addErrorPrefix(Error& e, const char* s,
-                                  const std::string& s2)) {
-  e.addPrefix(format(s) % s2);
-}
-
-LocalNoInline(void addErrorPrefix(Error& e, const char* s,
-                                  const ExprLambda& fun, const Pos& pos)) {
-  e.addPrefix(format(s) % fun.showNamePos() % pos);
-}
-
-LocalNoInline(void addErrorPrefix(Error& e, const char* s,
-                                  const std::string& s2, const Pos& pos)) {
-  e.addPrefix(format(s) % s2 % pos);
-}
-
-void mkString(Value& v, const char* s) { mkStringNoCopy(v, dupString(s)); }
-
-Value& mkString(Value& v, const std::string& s, const PathSet& context) {
-  mkString(v, s.c_str());
-  if (!context.empty()) {
-    size_t n = 0;
-    v.string.context = static_cast<const char**>(
-        allocBytes((context.size() + 1) * sizeof(char*)));
-    for (auto& i : context) {
-      v.string.context[n++] = dupString(i.c_str());
-    }
-    v.string.context[n] = nullptr;
-  }
-  return v;
-}
-
-void mkPath(Value& v, const char* s) { mkPathNoCopy(v, dupString(s)); }
-
-inline Value* EvalState::lookupVar(Env* env, const ExprVar& var, bool noEval) {
-  for (size_t l = var.level; l != 0u; --l, env = env->up) {
-    ;
-  }
-
-  if (!var.fromWith) {
-    return env->values[var.displ];
-  }
-
-  while (true) {
-    if (env->type == Env::HasWithExpr) {
-      if (noEval) {
-        return nullptr;
-      }
-      if (!env->withAttrsExpr) {
-        CHECK(false) << "HasWithExpr evaluated twice";
-      }
-      Value* v = allocValue();
-      evalAttrs(*env->up, env->withAttrsExpr, *v);
-      env->values[0] = v;
-      env->withAttrsExpr = nullptr;
-      env->type = Env::HasWithAttrs;
-    }
-    Bindings::iterator j = env->values[0]->attrs->find(var.name);
-    if (j != env->values[0]->attrs->end()) {
-      if (countCalls && (j->second.pos != nullptr)) {
-        attrSelects[*j->second.pos]++;
-      }
-      return j->second.value;
-    }
-    if (env->prevWith == 0u) {
-      throwUndefinedVarError("undefined variable '%1%' at %2%", var.name,
-                             var.pos);
-    }
-    for (size_t l = env->prevWith; l != 0u; --l, env = env->up) {
-    }
-  }
-}
-
-Value* EvalState::allocValue() {
-  nrValues++;
-  return new Value;
-}
-
-Env& EvalState::allocEnv(size_t size) {
-  if (size > std::numeric_limits<decltype(Env::size)>::max()) {
-    throw Error("environment size %d is too big", size);
-  }
-
-  nrEnvs++;
-  nrValuesInEnvs += size;
-  Env* env = new Env(size);
-  env->type = Env::Plain;
-
-  return *env;
-}
-
-void EvalState::mkList(Value& v, std::shared_ptr<NixList> list) {
-  nrListElems += list->size();
-  clearValue(v);
-  v.type = tList;
-  v.list = list;
-}
-
-void EvalState::mkList(Value& v, size_t size) {
-  EvalState::mkList(v, std::make_shared<NixList>(size));
-}
-
-unsigned long nrThunks = 0;
-
-static inline void mkThunk(Value& v, Env& env, Expr* expr) {
-  v.type = tThunk;
-  v.thunk.env = &env;
-  v.thunk.expr = expr;
-  nrThunks++;
-}
-
-void EvalState::mkThunk_(Value& v, Expr* expr) { mkThunk(v, baseEnv, expr); }
-
-void EvalState::mkPos(Value& v, Pos* pos) {
-  if ((pos != nullptr) && pos->file.has_value() && pos->file.value().set()) {
-    mkAttrs(v, 3);
-    mkString(*allocAttr(v, sFile), pos->file.value());
-    mkInt(*allocAttr(v, sLine), pos->line);
-    mkInt(*allocAttr(v, sColumn), pos->column);
-  } else {
-    mkNull(v);
-  }
-}
-
-/* Create a thunk for the delayed computation of the given expression
-   in the given environment.  But if the expression is a variable,
-   then look it up right away.  This significantly reduces the number
-   of thunks allocated. */
-Value* Expr::maybeThunk(EvalState& state, Env& env) {
-  Value* v = state.allocValue();
-  mkThunk(*v, env, this);
-  return v;
-}
-
-unsigned long nrAvoided = 0;
-
-Value* ExprVar::maybeThunk(EvalState& state, Env& env) {
-  Value* v = state.lookupVar(&env, *this, true);
-  /* The value might not be initialised in the environment yet.
-     In that case, ignore it. */
-  if (v != nullptr) {
-    nrAvoided++;
-    return v;
-  }
-  return Expr::maybeThunk(state, env);
-}
-
-Value* ExprString::maybeThunk(EvalState& state, Env& env) {
-  nrAvoided++;
-  return &v;
-}
-
-Value* ExprInt::maybeThunk(EvalState& state, Env& env) {
-  nrAvoided++;
-  return &v;
-}
-
-Value* ExprFloat::maybeThunk(EvalState& state, Env& env) {
-  nrAvoided++;
-  return &v;
-}
-
-Value* ExprPath::maybeThunk(EvalState& state, Env& env) {
-  nrAvoided++;
-  return &v;
-}
-
-void EvalState::evalFile(const Path& path_, Value& v) {
-  auto path = checkSourcePath(path_);
-
-  FileEvalCache::iterator i;
-  if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
-    v = i->second;
-    return;
-  }
-
-  Path path2 = resolveExprPath(path);
-  if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) {
-    v = i->second;
-    return;
-  }
-
-  VLOG(2) << "evaluating file '" << path2 << "'";
-  Expr* e = nullptr;
-
-  auto j = fileParseCache.find(path2);
-  if (j != fileParseCache.end()) {
-    e = j->second;
-  }
-
-  if (e == nullptr) {
-    e = parseExprFromFile(checkSourcePath(path2));
-  }
-
-  fileParseCache[path2] = e;
-
-  try {
-    eval(e, v);
-  } catch (Error& e) {
-    addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);
-    throw;
-  }
-
-  fileEvalCache[path2] = v;
-  if (path != path2) {
-    fileEvalCache[path] = v;
-  }
-}
-
-void EvalState::resetFileCache() {
-  fileEvalCache.clear();
-  fileParseCache.clear();
-}
-
-void EvalState::eval(Expr* e, Value& v) { e->eval(*this, baseEnv, v); }
-
-inline bool EvalState::evalBool(Env& env, Expr* e) {
-  Value v;
-  e->eval(*this, env, v);
-  if (v.type != tBool) {
-    throwTypeError("value is %1% while a Boolean was expected", v);
-  }
-  return v.boolean;
-}
-
-inline bool EvalState::evalBool(Env& env, Expr* e, const Pos& pos) {
-  Value v;
-  e->eval(*this, env, v);
-  if (v.type != tBool) {
-    throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
-  }
-  return v.boolean;
-}
-
-inline void EvalState::evalAttrs(Env& env, Expr* e, Value& v) {
-  e->eval(*this, env, v);
-  if (v.type != tAttrs) {
-    throwTypeError("value is %1% while a set was expected", v);
-  }
-}
-
-void Expr::eval(EvalState& state, Env& env, Value& v) { abort(); }
-
-void ExprInt::eval(EvalState& state, Env& env, Value& v) { v = this->v; }
-
-void ExprFloat::eval(EvalState& state, Env& env, Value& v) { v = this->v; }
-
-void ExprString::eval(EvalState& state, Env& env, Value& v) { v = this->v; }
-
-void ExprPath::eval(EvalState& state, Env& env, Value& v) { v = this->v; }
-
-void ExprAttrs::eval(EvalState& state, Env& env, Value& value) {
-  state.mkAttrs(value, attrs.size() + dynamicAttrs.size());
-  Env* dynamicEnv = &env;
-
-  if (recursive) {
-    /* Create a new environment that contains the attributes in
-       this `rec'. */
-    Env& env2(state.allocEnv(attrs.size()));
-    env2.up = &env;
-    dynamicEnv = &env2;
-
-    /* The recursive attributes are evaluated in the new
-       environment, while the inherited attributes are evaluated
-       in the original environment. */
-    size_t displ = 0;
-    for (auto& attr : attrs) {
-      Value* vAttr;
-      vAttr =
-          attr.second.e->maybeThunk(state, attr.second.inherited ? env : env2);
-      env2.values[displ++] = vAttr;
-      value.attrs->push_back(Attr(attr.first, vAttr, &attr.second.pos));
-    }
-  } else {
-    // TODO(tazjin): insert range
-    for (auto& i : attrs) {
-      value.attrs->push_back(
-          Attr(i.first, i.second.e->maybeThunk(state, env), &i.second.pos));
-    }
-  }
-
-  /* Dynamic attrs apply *after* rec. */
-  for (auto& i : dynamicAttrs) {
-    Value nameVal;
-    i.nameExpr->eval(state, *dynamicEnv, nameVal);
-    state.forceValue(nameVal, i.pos);
-    if (nameVal.type == tNull) {
-      continue;
-    }
-    state.forceStringNoCtx(nameVal);
-    Symbol nameSym = state.symbols.Create(nameVal.string.s);
-    Bindings::iterator j = value.attrs->find(nameSym);
-    if (j != value.attrs->end()) {
-      throwEvalError("dynamic attribute '%1%' at %2% already defined at %3%",
-                     nameSym, i.pos, *j->second.pos);
-    }
-
-    value.attrs->push_back(
-        Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), &i.pos));
-  }
-}
-
-void ExprLet::eval(EvalState& state, Env& env, Value& v) {
-  /* Create a new environment that contains the attributes in this
-     `let'. */
-  Env& env2(state.allocEnv(attrs->attrs.size()));
-  env2.up = &env;
-
-  /* The recursive attributes are evaluated in the new environment,
-     while the inherited attributes are evaluated in the original
-     environment. */
-  size_t displ = 0;
-  for (auto& i : attrs->attrs) {
-    env2.values[displ++] =
-        i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
-  }
-
-  body->eval(state, env2, v);
-}
-
-void ExprList::eval(EvalState& state, Env& env, Value& v) {
-  state.mkList(v, elems.size());
-  for (size_t n = 0; n < elems.size(); ++n) {
-    (*v.list)[n] = elems[n]->maybeThunk(state, env);
-  }
-}
-
-void ExprVar::eval(EvalState& state, Env& env, Value& v) {
-  Value* v2 = state.lookupVar(&env, *this, false);
-  state.forceValue(*v2, pos);
-  v = *v2;
-}
-
-static std::string showAttrPath(EvalState& state, Env& env,
-                                const AttrPath& attrPath) {
-  std::ostringstream out;
-  bool first = true;
-  for (auto& i : attrPath) {
-    if (!first) {
-      out << '.';
-    } else {
-      first = false;
-    }
-    out << getName(i, state, env);
-  }
-  return out.str();
-}
-
-uint64_t nrLookups = 0;
-
-void ExprSelect::eval(EvalState& state, Env& env, Value& v) {
-  Value vTmp;
-  Pos* pos2 = nullptr;
-  Value* vAttrs = &vTmp;
-
-  e->eval(state, env, vTmp);
-
-  try {
-    for (auto& i : attrPath) {
-      nrLookups++;
-      Bindings::iterator j;
-      Symbol name = getName(i, state, env);
-      if (def != nullptr) {
-        state.forceValue(*vAttrs, pos);
-        if (vAttrs->type != tAttrs ||
-            (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
-          def->eval(state, env, v);
-          return;
-        }
-      } else {
-        state.forceAttrs(*vAttrs, pos);
-        if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
-          throwEvalError("attribute '%1%' missing, at %2%", name, pos);
-        }
-      }
-      vAttrs = j->second.value;
-      pos2 = j->second.pos;
-      if (state.countCalls && (pos2 != nullptr)) {
-        state.attrSelects[*pos2]++;
-      }
-    }
-
-    state.forceValue(*vAttrs, (pos2 != nullptr ? *pos2 : this->pos));
-
-  } catch (Error& e) {
-    // This code relies on 'sDerivationNix' being correcty mutated at
-    // some prior point (it would previously otherwise have been a
-    // nullptr).
-    //
-    // We haven't seen this fail, so for now the contained value is
-    // just accessed at the risk of potentially crashing.
-    if ((pos2 != nullptr) && pos2->file != state.sDerivationNix.value()) {
-      addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n",
-                     showAttrPath(state, env, attrPath), *pos2);
-    }
-    throw;
-  }
-
-  v = *vAttrs;
-}
-
-void ExprOpHasAttr::eval(EvalState& state, Env& env, Value& v) {
-  Value vTmp;
-  Value* vAttrs = &vTmp;
-
-  e->eval(state, env, vTmp);
-
-  for (auto& i : attrPath) {
-    state.forceValue(*vAttrs);
-    Bindings::iterator j;
-    Symbol name = getName(i, state, env);
-    if (vAttrs->type != tAttrs ||
-        (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
-      mkBool(v, false);
-      return;
-    }
-    vAttrs = j->second.value;
-  }
-
-  mkBool(v, true);
-}
-
-void ExprLambda::eval(EvalState& state, Env& env, Value& v) {
-  v.type = tLambda;
-  v.lambda.env = &env;
-  v.lambda.fun = this;
-}
-
-void ExprApp::eval(EvalState& state, Env& env, Value& v) {
-  /* FIXME: vFun prevents GCC from doing tail call optimisation. */
-  Value vFun;
-  e1->eval(state, env, vFun);
-  state.callFunction(vFun, *(e2->maybeThunk(state, env)), v, pos);
-}
-
-void EvalState::callPrimOp(Value& fun, Value& arg, Value& v, const Pos& pos) {
-  /* Figure out the number of arguments still needed. */
-  size_t argsDone = 0;
-  Value* primOp = &fun;
-  while (primOp->type == tPrimOpApp) {
-    argsDone++;
-    primOp = primOp->primOpApp.left;
-  }
-  assert(primOp->type == tPrimOp);
-  auto arity = primOp->primOp->arity;
-  auto argsLeft = arity - argsDone;
-
-  if (argsLeft == 1) {
-    /* We have all the arguments, so call the primop. */
-
-    /* Put all the arguments in an array. */
-    Value* vArgs[arity];
-    auto n = arity - 1;
-    vArgs[n--] = &arg;
-    for (Value* arg = &fun; arg->type == tPrimOpApp;
-         arg = arg->primOpApp.left) {
-      vArgs[n--] = arg->primOpApp.right;
-    }
-
-    /* And call the primop. */
-    nrPrimOpCalls++;
-    if (countCalls) {
-      primOpCalls[primOp->primOp->name]++;
-    }
-    primOp->primOp->fun(*this, pos, vArgs, v);
-  } else {
-    Value* fun2 = allocValue();
-    *fun2 = fun;
-    v.type = tPrimOpApp;
-    v.primOpApp.left = fun2;
-    v.primOpApp.right = &arg;
-  }
-}
-
-void EvalState::callFunction(Value& fun, Value& arg, Value& v, const Pos& pos) {
-  auto trace = evalSettings.traceFunctionCalls
-                   ? std::make_unique<FunctionCallTrace>(pos)
-                   : nullptr;
-
-  forceValue(fun, pos);
-
-  if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
-    callPrimOp(fun, arg, v, pos);
-    return;
-  }
-
-  // If the value to be called is an attribute set, check whether it
-  // contains an appropriate function in the '__functor' element and
-  // use that.
-  if (fun.type == tAttrs) {
-    auto found = fun.attrs->find(sFunctor);
-    if (found != fun.attrs->end()) {
-      // fun may be allocated on the stack of the calling function,
-      // but for functors we may keep a reference, so heap-allocate a
-      // copy and use that instead
-      auto& fun2 = *allocValue();
-      fun2 = fun;
-      /* !!! Should we use the attr pos here? */
-      Value v2;
-      // functors are called with the element itself as the first
-      // parameter, which is partially applied here
-      callFunction(*found->second.value, fun2, v2, pos);
-      return callFunction(v2, arg, v, pos);
-    }
-  }
-
-  if (fun.type != tLambda) {
-    throwTypeError(
-        "attempt to call something which is not a function but %1%, at %2%",
-        fun, pos);
-  }
-
-  ExprLambda& lambda(*fun.lambda.fun);
-
-  auto size = (lambda.arg.empty() ? 0 : 1) +
-              (lambda.matchAttrs ? lambda.formals->formals.size() : 0);
-  Env& env2(allocEnv(size));
-  env2.up = fun.lambda.env;
-
-  size_t displ = 0;
-
-  if (!lambda.matchAttrs) {
-    env2.values[displ++] = &arg;
-
-  } else {
-    forceAttrs(arg, pos);
-
-    if (!lambda.arg.empty()) {
-      env2.values[displ++] = &arg;
-    }
-
-    /* For each formal argument, get the actual argument.  If
-       there is no matching actual argument but the formal
-       argument has a default, use the default. */
-    size_t attrsUsed = 0;
-    for (auto& i : lambda.formals->formals) {
-      Bindings::iterator j = arg.attrs->find(i.name);
-      if (j == arg.attrs->end()) {
-        if (i.def == nullptr) {
-          throwTypeError("%1% called without required argument '%2%', at %3%",
-                         lambda, i.name, pos);
-        }
-        env2.values[displ++] = i.def->maybeThunk(*this, env2);
-      } else {
-        attrsUsed++;
-        env2.values[displ++] = j->second.value;
-      }
-    }
-
-    /* Check that each actual argument is listed as a formal
-       argument (unless the attribute match specifies a `...'). */
-    if (!lambda.formals->ellipsis && attrsUsed != arg.attrs->size()) {
-      /* Nope, so show the first unexpected argument to the
-         user. */
-      for (auto& i : *arg.attrs) {
-        if (lambda.formals->argNames.find(i.second.name) ==
-            lambda.formals->argNames.end()) {
-          throwTypeError("%1% called with unexpected argument '%2%', at %3%",
-                         lambda, i.second.name, pos);
-        }
-      }
-      abort();  // shouldn't happen
-    }
-  }
-
-  nrFunctionCalls++;
-  if (countCalls) {
-    incrFunctionCall(&lambda);
-  }
-
-  /* Evaluate the body.  This is conditional on showTrace, because
-     catching exceptions makes this function not tail-recursive. */
-  if (settings.showTrace) {
-    try {
-      lambda.body->eval(*this, env2, v);
-    } catch (Error& e) {
-      addErrorPrefix(e, "while evaluating %1%, called from %2%:\n", lambda,
-                     pos);
-      throw;
-    }
-  } else {
-    fun.lambda.fun->body->eval(*this, env2, v);
-  }
-}
-
-// Lifted out of callFunction() because it creates a temporary that
-// prevents tail-call optimisation.
-void EvalState::incrFunctionCall(ExprLambda* fun) { functionCalls[fun]++; }
-
-void EvalState::autoCallFunction(Bindings* args, Value& fun, Value& res) {
-  forceValue(fun);
-
-  if (fun.type == tAttrs) {
-    auto found = fun.attrs->find(sFunctor);
-    if (found != fun.attrs->end()) {
-      Value* v = allocValue();
-      callFunction(*found->second.value, fun, *v, noPos);
-      forceValue(*v);
-      return autoCallFunction(args, *v, res);
-    }
-  }
-
-  if (fun.type != tLambda || !fun.lambda.fun->matchAttrs) {
-    res = fun;
-    return;
-  }
-
-  Value* actualArgs = allocValue();
-  mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size());
-
-  if (fun.lambda.fun->formals->ellipsis) {
-    // If the formals have an ellipsis (eg the function accepts extra args) pass
-    // all available automatic arguments (which includes arguments specified on
-    // the command line via --arg/--argstr)
-    for (auto& [_, v] : *args) {
-      actualArgs->attrs->push_back(v);
-    }
-  } else {
-    // Otherwise, only pass the arguments that the function accepts
-    for (auto& i : fun.lambda.fun->formals->formals) {
-      Bindings::iterator j = args->find(i.name);
-      if (j != args->end()) {
-        actualArgs->attrs->push_back(j->second);
-      } else if (i.def == nullptr) {
-        throwTypeError(
-            "cannot auto-call a function that has an argument without a "
-            "default "
-            "value ('%1%')",
-            i.name);
-      }
-    }
-  }
-
-  callFunction(fun, *actualArgs, res, noPos);
-}
-
-void ExprWith::eval(EvalState& state, Env& env, Value& v) {
-  Env& env2(state.allocEnv(1));
-  env2.up = &env;
-  env2.prevWith = prevWith;
-  env2.type = Env::HasWithExpr;
-  /* placeholder for result of attrs */
-  env2.values[0] = nullptr;
-  env2.withAttrsExpr = this->attrs;
-
-  body->eval(state, env2, v);
-}
-
-void ExprIf::eval(EvalState& state, Env& env, Value& v) {
-  (state.evalBool(env, cond) ? then : else_)->eval(state, env, v);
-}
-
-void ExprAssert::eval(EvalState& state, Env& env, Value& v) {
-  if (!state.evalBool(env, cond, pos)) {
-    std::ostringstream out;
-    cond->show(out);
-    throwAssertionError("assertion %1% failed at %2%", out.str(), pos);
-  }
-  body->eval(state, env, v);
-}
-
-void ExprOpNot::eval(EvalState& state, Env& env, Value& v) {
-  mkBool(v, !state.evalBool(env, e));
-}
-
-void ExprOpEq::eval(EvalState& state, Env& env, Value& v) {
-  Value v1;
-  e1->eval(state, env, v1);
-  Value v2;
-  e2->eval(state, env, v2);
-  mkBool(v, state.eqValues(v1, v2));
-}
-
-void ExprOpNEq::eval(EvalState& state, Env& env, Value& v) {
-  Value v1;
-  e1->eval(state, env, v1);
-  Value v2;
-  e2->eval(state, env, v2);
-  mkBool(v, !state.eqValues(v1, v2));
-}
-
-void ExprOpAnd::eval(EvalState& state, Env& env, Value& v) {
-  mkBool(v, state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos));
-}
-
-void ExprOpOr::eval(EvalState& state, Env& env, Value& v) {
-  mkBool(v, state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
-}
-
-void ExprOpImpl::eval(EvalState& state, Env& env, Value& v) {
-  mkBool(v, !state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
-}
-
-void ExprOpUpdate::eval(EvalState& state, Env& env, Value& dest) {
-  Value v1;
-  Value v2;
-  state.evalAttrs(env, e1, v1);
-  state.evalAttrs(env, e2, v2);
-
-  state.nrOpUpdates++;
-
-  clearValue(dest);
-  dest.type = tAttrs;
-  dest.attrs = Bindings::Merge(*v1.attrs, *v2.attrs);
-}
-
-void ExprOpConcatLists::eval(EvalState& state, Env& env, Value& v) {
-  Value v1;
-  e1->eval(state, env, v1);
-  Value v2;
-  e2->eval(state, env, v2);
-  state.concatLists(v, {&v1, &v2}, pos);
-}
-
-void EvalState::concatLists(Value& v, const NixList& lists, const Pos& pos) {
-  nrListConcats++;
-
-  auto outlist = std::make_shared<NixList>();
-
-  for (Value* list : lists) {
-    forceList(*list, pos);
-    outlist->insert(outlist->end(), list->list->begin(), list->list->end());
-  }
-
-  mkList(v, outlist);
-}
-
-void ExprConcatStrings::eval(EvalState& state, Env& env, Value& v) {
-  PathSet context;
-  std::ostringstream s;
-  NixInt n = 0;
-  NixFloat nf = 0;
-
-  bool first = !forceString;
-  ValueType firstType = tString;
-
-  for (auto& i : *es) {
-    Value vTmp;
-    i->eval(state, env, vTmp);
-
-    /* If the first element is a path, then the result will also
-       be a path, we don't copy anything (yet - that's done later,
-       since paths are copied when they are used in a derivation),
-       and none of the strings are allowed to have contexts. */
-    if (first) {
-      firstType = vTmp.type;
-      first = false;
-    }
-
-    if (firstType == tInt) {
-      if (vTmp.type == tInt) {
-        n += vTmp.integer;
-      } else if (vTmp.type == tFloat) {
-        // Upgrade the type from int to float;
-        firstType = tFloat;
-        nf = n;
-        nf += vTmp.fpoint;
-      } else {
-        throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp),
-                       pos);
-      }
-    } else if (firstType == tFloat) {
-      if (vTmp.type == tInt) {
-        nf += vTmp.integer;
-      } else if (vTmp.type == tFloat) {
-        nf += vTmp.fpoint;
-      } else {
-        throwEvalError("cannot add %1% to a float, at %2%", showType(vTmp),
-                       pos);
-      }
-    } else {
-      s << state.coerceToString(pos, vTmp, context, false,
-                                firstType == tString);
-    }
-  }
-
-  if (firstType == tInt) {
-    mkInt(v, n);
-  } else if (firstType == tFloat) {
-    mkFloat(v, nf);
-  } else if (firstType == tPath) {
-    if (!context.empty()) {
-      throwEvalError(
-          "a string that refers to a store path cannot be appended to a path, "
-          "at %1%",
-          pos);
-    }
-    auto path = canonPath(s.str());
-    mkPath(v, path.c_str());
-  } else {
-    mkString(v, s.str(), context);
-  }
-}
-
-void ExprPos::eval(EvalState& state, Env& env, Value& v) {
-  state.mkPos(v, &pos);
-}
-
-template <typename T>
-using traceable_flat_hash_set = absl::flat_hash_set<T>;
-
-void EvalState::forceValueDeep(Value& v) {
-  traceable_flat_hash_set<const Value*> seen;
-
-  std::function<void(Value & v)> recurse;
-
-  recurse = [&](Value& v) {
-    if (seen.find(&v) != seen.end()) {
-      return;
-    }
-    seen.insert(&v);
-
-    forceValue(v);
-
-    if (v.type == tAttrs) {
-      for (auto& i : *v.attrs) {
-        try {
-          recurse(*i.second.value);
-        } catch (Error& e) {
-          addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n",
-                         i.second.name, *i.second.pos);
-          throw;
-        }
-      }
-    } else if (v.isList()) {
-      for (size_t n = 0; n < v.listSize(); ++n) {
-        recurse(*(*v.list)[n]);
-      }
-    }
-  };
-
-  recurse(v);
-}
-
-NixInt EvalState::forceInt(Value& v, const Pos& pos) {
-  forceValue(v, pos);
-  if (v.type != tInt) {
-    throwTypeError("value is %1% while an integer was expected, at %2%", v,
-                   pos);
-  }
-  return v.integer;
-}
-
-NixFloat EvalState::forceFloat(Value& v, const Pos& pos) {
-  forceValue(v, pos);
-  if (v.type == tInt) {
-    return static_cast<NixFloat>(v.integer);
-  }
-  if (v.type != tFloat) {
-    throwTypeError("value is %1% while a float was expected, at %2%", v, pos);
-  }
-  return v.fpoint;
-}
-
-bool EvalState::forceBool(Value& v, const Pos& pos) {
-  forceValue(v);
-  if (v.type != tBool) {
-    throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
-  }
-  return v.boolean;
-}
-
-bool EvalState::isFunctor(Value& fun) {
-  return fun.type == tAttrs && fun.attrs->find(sFunctor) != fun.attrs->end();
-}
-
-void EvalState::forceFunction(Value& v, const Pos& pos) {
-  forceValue(v);
-  if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp &&
-      !isFunctor(v)) {
-    throwTypeError("value is %1% while a function was expected, at %2%", v,
-                   pos);
-  }
-}
-
-std::string EvalState::forceString(Value& v, const Pos& pos) {
-  forceValue(v, pos);
-  if (v.type != tString) {
-    if (pos) {
-      throwTypeError("value is %1% while a string was expected, at %2%", v,
-                     pos);
-    } else {
-      throwTypeError("value is %1% while a string was expected", v);
-    }
-  }
-  return std::string(v.string.s);
-}
-
-void copyContext(const Value& v, PathSet& context) {
-  if (v.string.context != nullptr) {
-    for (const char** p = v.string.context; *p != nullptr; ++p) {
-      context.insert(*p);
-    }
-  }
-}
-
-std::string EvalState::forceString(Value& v, PathSet& context, const Pos& pos) {
-  std::string s = forceString(v, pos);
-  copyContext(v, context);
-  return s;
-}
-
-std::string EvalState::forceStringNoCtx(Value& v, const Pos& pos) {
-  std::string s = forceString(v, pos);
-  if (v.string.context != nullptr) {
-    if (pos) {
-      throwEvalError(
-          "the string '%1%' is not allowed to refer to a store path (such as "
-          "'%2%'), at %3%",
-          v.string.s, v.string.context[0], pos);
-    } else {
-      throwEvalError(
-          "the string '%1%' is not allowed to refer to a store path (such as "
-          "'%2%')",
-          v.string.s, v.string.context[0]);
-    }
-  }
-  return s;
-}
-
-bool EvalState::isDerivation(Value& v) {
-  if (v.type != tAttrs) {
-    return false;
-  }
-  Bindings::iterator i = v.attrs->find(sType);
-  if (i == v.attrs->end()) {
-    return false;
-  }
-  forceValue(*i->second.value);
-  if (i->second.value->type != tString) {
-    return false;
-  }
-  return strcmp(i->second.value->string.s, "derivation") == 0;
-}
-
-std::optional<std::string> EvalState::tryAttrsToString(const Pos& pos, Value& v,
-                                                       PathSet& context,
-                                                       bool coerceMore,
-                                                       bool copyToStore) {
-  auto i = v.attrs->find(sToString);
-  if (i != v.attrs->end()) {
-    Value v1;
-    callFunction(*i->second.value, v, v1, pos);
-    return coerceToString(pos, v1, context, coerceMore, copyToStore);
-  }
-
-  return {};
-}
-
-std::string EvalState::coerceToString(const Pos& pos, Value& v,
-                                      PathSet& context, bool coerceMore,
-                                      bool copyToStore) {
-  forceValue(v);
-
-  std::string s;
-
-  if (v.type == tString) {
-    copyContext(v, context);
-    return v.string.s;
-  }
-
-  if (v.type == tPath) {
-    Path path(canonPath(v.path));
-    return copyToStore ? copyPathToStore(context, path) : path;
-  }
-
-  if (v.type == tAttrs) {
-    auto maybeString =
-        tryAttrsToString(pos, v, context, coerceMore, copyToStore);
-    if (maybeString) {
-      return *maybeString;
-    }
-    auto i = v.attrs->find(sOutPath);
-    if (i == v.attrs->end()) {
-      throwTypeError("cannot coerce a set to a string, at %1%", pos);
-    }
-    return coerceToString(pos, *i->second.value, context, coerceMore,
-                          copyToStore);
-  }
-
-  if (coerceMore) {
-    /* Note that `false' is represented as an empty string for
-       shell scripting convenience, just like `null'. */
-    if (v.type == tBool && v.boolean) {
-      return "1";
-    }
-    if (v.type == tBool && !v.boolean) {
-      return "";
-    }
-    if (v.type == tInt) {
-      return std::to_string(v.integer);
-    }
-    if (v.type == tFloat) {
-      return std::to_string(v.fpoint);
-    }
-    if (v.type == tNull) {
-      return "";
-    }
-
-    if (v.isList()) {
-      std::string result;
-      for (size_t n = 0; n < v.listSize(); ++n) {
-        result += coerceToString(pos, *(*v.list)[n], context, coerceMore,
-                                 copyToStore);
-        if (n < v.listSize() - 1
-            /* !!! not quite correct */
-            && (!(*v.list)[n]->isList() || (*v.list)[n]->listSize() != 0)) {
-          result += " ";
-        }
-      }
-      return result;
-    }
-  }
-
-  throwTypeError("cannot coerce %1% to a string, at %2%", v, pos);
-}
-
-std::string EvalState::copyPathToStore(PathSet& context, const Path& path) {
-  if (nix::isDerivation(path)) {
-    throwEvalError("file names are not allowed to end in '%1%'", drvExtension);
-  }
-
-  Path dstPath;
-  if (!srcToStore[path].empty()) {
-    dstPath = srcToStore[path];
-  } else {
-    dstPath =
-        settings.readOnlyMode
-            ? store
-                  ->computeStorePathForPath(baseNameOf(path),
-                                            checkSourcePath(path))
-                  .first
-            : store->addToStore(baseNameOf(path), checkSourcePath(path), true,
-                                htSHA256, defaultPathFilter, repair);
-    srcToStore[path] = dstPath;
-    VLOG(2) << "copied source '" << path << "' -> '" << dstPath << "'";
-  }
-
-  context.insert(dstPath);
-  return dstPath;
-}
-
-Path EvalState::coerceToPath(const Pos& pos, Value& v, PathSet& context) {
-  std::string path = coerceToString(pos, v, context, false, false);
-  if (path.empty() || path[0] != '/') {
-    throwEvalError("string '%1%' doesn't represent an absolute path, at %2%",
-                   path, pos);
-  }
-  return path;
-}
-
-bool EvalState::eqValues(Value& v1, Value& v2) {
-  forceValue(v1);
-  forceValue(v2);
-
-  /* !!! Hack to support some old broken code that relies on pointer
-     equality tests between sets.  (Specifically, builderDefs calls
-     uniqList on a list of sets.)  Will remove this eventually. */
-  if (&v1 == &v2) {
-    return true;
-  }
-
-  // Special case type-compatibility between float and int
-  if (v1.type == tInt && v2.type == tFloat) {
-    return v1.integer == v2.fpoint;
-  }
-  if (v1.type == tFloat && v2.type == tInt) {
-    return v1.fpoint == v2.integer;
-  }
-
-  // All other types are not compatible with each other.
-  if (v1.type != v2.type) {
-    return false;
-  }
-
-  switch (v1.type) {
-    case tInt:
-      return v1.integer == v2.integer;
-
-    case tBool:
-      return v1.boolean == v2.boolean;
-
-    case tString:
-      return strcmp(v1.string.s, v2.string.s) == 0;
-
-    case tPath:
-      return strcmp(v1.path, v2.path) == 0;
-
-    case tNull:
-      return true;
-
-    case tList:
-      if (v1.listSize() != v2.listSize()) {
-        return false;
-      }
-      for (size_t n = 0; n < v1.listSize(); ++n) {
-        if (!eqValues(*(*v1.list)[n], *(*v2.list)[n])) {
-          return false;
-        }
-      }
-      return true;
-
-    case tAttrs: {
-      // As an optimisation if both values are pointing towards the
-      // same attribute set, we can skip all this extra work.
-      if (v1.attrs == v2.attrs) {
-        return true;
-      }
-
-      /* If both sets denote a derivation (type = "derivation"),
-         then compare their outPaths. */
-      if (isDerivation(v1) && isDerivation(v2)) {
-        Bindings::iterator i = v1.attrs->find(sOutPath);
-        Bindings::iterator j = v2.attrs->find(sOutPath);
-        if (i != v1.attrs->end() && j != v2.attrs->end()) {
-          return eqValues(*i->second.value, *j->second.value);
-        }
-      }
-
-      return v1.attrs->Equal(v2.attrs.get(), *this);
-    }
-
-    /* Functions are incomparable. */
-    case tLambda:
-    case tPrimOp:
-    case tPrimOpApp:
-      return false;
-
-    case tFloat:
-      return v1.fpoint == v2.fpoint;
-
-    default:
-      throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
-  }
-}
-
-void EvalState::printStats() {
-  bool showStats = getEnv("NIX_SHOW_STATS").value_or("0") != "0";
-
-  struct rusage buf;
-  getrusage(RUSAGE_SELF, &buf);
-  float cpuTime = buf.ru_utime.tv_sec +
-                  (static_cast<float>(buf.ru_utime.tv_usec) / 1000000);
-
-  uint64_t bEnvs = nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value*);
-  uint64_t bLists = nrListElems * sizeof(Value*);
-  uint64_t bValues = nrValues * sizeof(Value);
-  uint64_t bAttrsets =
-      nrAttrsets * sizeof(Bindings) + nrAttrsInAttrsets * sizeof(Attr);
-
-  if (showStats) {
-    auto outPath = getEnv("NIX_SHOW_STATS_PATH").value_or("-");
-    std::fstream fs;
-    if (outPath != "-") {
-      fs.open(outPath, std::fstream::out);
-    }
-    JSONObject topObj(outPath == "-" ? std::cerr : fs, true);
-    topObj.attr("cpuTime", cpuTime);
-    {
-      auto envs = topObj.object("envs");
-      envs.attr("number", nrEnvs);
-      envs.attr("elements", nrValuesInEnvs);
-      envs.attr("bytes", bEnvs);
-    }
-    {
-      auto lists = topObj.object("list");
-      lists.attr("elements", nrListElems);
-      lists.attr("bytes", bLists);
-      lists.attr("concats", nrListConcats);
-    }
-    {
-      auto values = topObj.object("values");
-      values.attr("number", nrValues);
-      values.attr("bytes", bValues);
-    }
-    {
-      auto syms = topObj.object("symbols");
-      syms.attr("number", symbols.Size());
-      syms.attr("bytes", symbols.TotalSize());
-    }
-    {
-      auto sets = topObj.object("sets");
-      sets.attr("number", nrAttrsets);
-      sets.attr("bytes", bAttrsets);
-      sets.attr("elements", nrAttrsInAttrsets);
-    }
-    {
-      auto sizes = topObj.object("sizes");
-      sizes.attr("Env", sizeof(Env));
-      sizes.attr("Value", sizeof(Value));
-      sizes.attr("Bindings", sizeof(Bindings));
-      sizes.attr("Attr", sizeof(Attr));
-    }
-    topObj.attr("nrOpUpdates", nrOpUpdates);
-    topObj.attr("nrOpUpdateValuesCopied", nrOpUpdateValuesCopied);
-    topObj.attr("nrThunks", nrThunks);
-    topObj.attr("nrAvoided", nrAvoided);
-    topObj.attr("nrLookups", nrLookups);
-    topObj.attr("nrPrimOpCalls", nrPrimOpCalls);
-    topObj.attr("nrFunctionCalls", nrFunctionCalls);
-
-    if (countCalls) {
-      {
-        auto obj = topObj.object("primops");
-        for (auto& i : primOpCalls) {
-          obj.attr(i.first, i.second);
-        }
-      }
-      {
-        auto list = topObj.list("functions");
-        for (auto& i : functionCalls) {
-          auto obj = list.object();
-          if (i.first->name.has_value()) {
-            obj.attr("name", (const std::string&)i.first->name.value());
-          } else {
-            obj.attr("name", nullptr);
-          }
-          if (i.first->pos) {
-            obj.attr("file", (const std::string&)i.first->pos.file);
-            obj.attr("line", i.first->pos.line);
-            obj.attr("column", i.first->pos.column);
-          }
-          obj.attr("count", i.second);
-        }
-      }
-      {
-        auto list = topObj.list("attributes");
-        for (auto& i : attrSelects) {
-          auto obj = list.object();
-          if (i.first) {
-            obj.attr("file", (const std::string&)i.first.file);
-            obj.attr("line", i.first.line);
-            obj.attr("column", i.first.column);
-          }
-          obj.attr("count", i.second);
-        }
-      }
-    }
-
-    // TODO(tazjin): what is this? commented out because .dump() is gone.
-    // if (getEnv("NIX_SHOW_SYMBOLS", "0") != "0") {
-    //   auto list = topObj.list("symbols");
-    //   symbols.dump([&](const std::string& s) { list.elem(s); });
-    // }
-  }
-}
-
-void EvalState::TraceFileAccess(const Path& realPath) {
-  if (file_access_trace_fn) {
-    if (last_traced_file != realPath) {
-      file_access_trace_fn(realPath);
-      // Basic deduplication.
-      last_traced_file = std::string(realPath);
-    }
-  }
-}
-
-void EvalState::EnableFileAccessTracing(std::function<void(const Path&)> fn) {
-  file_access_trace_fn = fn;
-}
-
-size_t valueSize(const Value& v) {
-  traceable_flat_hash_set<const Bindings*> seenBindings;
-  traceable_flat_hash_set<const Env*> seenEnvs;
-  traceable_flat_hash_set<const NixList*> seenLists;
-  traceable_flat_hash_set<const char*> seenStrings;
-  traceable_flat_hash_set<const Value*> seenValues;
-
-  auto doString = [&](const char* s) -> size_t {
-    if (seenStrings.find(s) != seenStrings.end()) {
-      return 0;
-    }
-    seenStrings.insert(s);
-    return strlen(s) + 1;
-  };
-
-  std::function<size_t(const Value& v)> doValue;
-  std::function<size_t(const Env& v)> doEnv;
-
-  doValue = [&](const Value& v) -> size_t {
-    if (seenValues.find(&v) != seenValues.end()) {
-      return 0;
-    }
-    seenValues.insert(&v);
-
-    size_t sz = sizeof(Value);
-
-    switch (v.type) {
-      case tString:
-        sz += doString(v.string.s);
-        if (v.string.context != nullptr) {
-          for (const char** p = v.string.context; *p != nullptr; ++p) {
-            sz += doString(*p);
-          }
-        }
-        break;
-      case tPath:
-        sz += doString(v.path);
-        break;
-      case tAttrs:
-        if (seenBindings.find(v.attrs.get()) == seenBindings.end()) {
-          seenBindings.insert(v.attrs.get());
-          sz += sizeof(Bindings);
-          for (const auto& i : *v.attrs) {
-            sz += doValue(*i.second.value);
-          }
-        }
-        break;
-      case tList:
-        if (seenLists.find(v.list.get()) == seenLists.end()) {
-          seenLists.insert(v.list.get());
-          sz += v.listSize() * sizeof(Value*);
-          for (const Value* v : *v.list) {
-            sz += doValue(*v);
-          }
-        }
-        break;
-      case tThunk:
-        sz += doEnv(*v.thunk.env);
-        break;
-      case tApp:
-        sz += doValue(*v.app.left);
-        sz += doValue(*v.app.right);
-        break;
-      case tLambda:
-        sz += doEnv(*v.lambda.env);
-        break;
-      case tPrimOpApp:
-        sz += doValue(*v.primOpApp.left);
-        sz += doValue(*v.primOpApp.right);
-        break;
-      default:;
-    }
-
-    return sz;
-  };
-
-  doEnv = [&](const Env& env) -> size_t {
-    if (seenEnvs.find(&env) != seenEnvs.end()) {
-      return 0;
-    }
-    seenEnvs.insert(&env);
-
-    size_t sz = sizeof(Env) + sizeof(Value*) * env.size;
-
-    if (env.type != Env::HasWithExpr) {
-      for (const Value* v : env.values) {
-        if (v != nullptr) {
-          sz += doValue(*v);
-        }
-      }
-    } else {
-      // TODO(kanepyork): trace ExprWith? how important is this accounting?
-    }
-
-    if (env.up != nullptr) {
-      sz += doEnv(*env.up);
-    }
-
-    return sz;
-  };
-
-  return doValue(v);
-}
-
-EvalSettings evalSettings;
-
-static GlobalConfig::Register r1(&evalSettings);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/eval.hh b/third_party/nix/src/libexpr/eval.hh
deleted file mode 100644
index 0352a89a2a..0000000000
--- a/third_party/nix/src/libexpr/eval.hh
+++ /dev/null
@@ -1,365 +0,0 @@
-#pragma once
-
-#include <map>
-#include <optional>
-#include <unordered_map>
-#include <vector>
-
-#include "libexpr/attr-set.hh"
-#include "libexpr/nixexpr.hh"
-#include "libexpr/symbol-table.hh"
-#include "libexpr/value.hh"
-#include "libutil/config.hh"
-#include "libutil/hash.hh"
-
-namespace nix {
-namespace expr {
-
-// Initialise the Boehm GC once per program instance. This should be
-// called in places that require the garbage collector.
-void InitGC();
-
-}  // namespace expr
-
-class Store;
-class EvalState;
-enum RepairFlag : bool;
-
-typedef void (*PrimOpFun)(EvalState& state, const Pos& pos, Value** args,
-                          Value& v);
-
-struct PrimOp {
-  PrimOpFun fun;
-  size_t arity;
-  Symbol name;
-  PrimOp(PrimOpFun fun, size_t arity, Symbol name)
-      : fun(fun), arity(arity), name(name) {}
-};
-
-struct Env {
-  Env(unsigned short size) : size(size) { values = std::vector<Value*>(size); }
-
-  Env* up;
-  unsigned short size;           // used by β€˜valueSize’
-  unsigned short prevWith : 14;  // nr of levels up to next `with' environment
-  enum { Plain = 0, HasWithExpr, HasWithAttrs } type : 2;
-  std::vector<Value*> values;
-  Expr* withAttrsExpr = nullptr;
-};
-
-Value& mkString(Value& v, const std::string& s,
-                const PathSet& context = PathSet());
-
-void copyContext(const Value& v, PathSet& context);
-
-/* Cache for calls to addToStore(); maps source paths to the store
-   paths. */
-using SrcToStore = std::map<Path, Path>;
-
-std::ostream& operator<<(std::ostream& str, const Value& v);
-
-using SearchPathElem = std::pair<std::string, std::string>;
-using SearchPath = std::list<SearchPathElem>;
-
-using FileParseCache = std::map<Path, Expr*>;
-
-class EvalState {
- public:
-  SymbolTable symbols;
-
-  const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, sSystem,
-      sOutputs, sOutputName, sIgnoreNulls, sFile, sLine, sColumn, sFunctor,
-      sToString, sRight, sWrong, sStructuredAttrs, sBuilder, sArgs, sOutputHash,
-      sOutputHashAlgo, sOutputHashMode;
-
-  // Symbol representing the path to the built-in 'derivation.nix'
-  // file, set during primops initialisation.
-  std::optional<Symbol> sDerivationNix;
-
-  /* If set, force copying files to the Nix store even if they
-     already exist there. */
-  RepairFlag repair;
-
-  /* The allowed filesystem paths in restricted or pure evaluation
-     mode. */
-  std::optional<PathSet> allowedPaths;
-
-  const ref<Store> store;
-
- private:
-  SrcToStore srcToStore;
-
-  /* A cache from path names to parse trees. */
-  FileParseCache fileParseCache;
-
-  /* A cache from path names to values. */
-  using FileEvalCache = std::map<Path, Value>;
-  FileEvalCache fileEvalCache;
-
-  SearchPath searchPath;
-
-  std::map<std::string, std::pair<bool, std::string>> searchPathResolved;
-
-  /* Cache used by checkSourcePath(). */
-  std::unordered_map<Path, Path> resolvedPaths;
-
- public:
-  EvalState(const Strings& _searchPath, const ref<Store>& store);
-  ~EvalState();
-
-  void addToSearchPath(const std::string& s);
-
-  SearchPath getSearchPath() { return searchPath; }
-
-  Path checkSourcePath(const Path& path);
-
-  void checkURI(const std::string& uri);
-
-  /* When using a diverted store and 'path' is in the Nix store, map
-     'path' to the diverted location (e.g. /nix/store/foo is mapped
-     to /home/alice/my-nix/nix/store/foo). However, this is only
-     done if the context is not empty, since otherwise we're
-     probably trying to read from the actual /nix/store. This is
-     intended to distinguish between import-from-derivation and
-     sources stored in the actual /nix/store. */
-  Path toRealPath(const Path& path, const PathSet& context);
-
-  /* Parse a Nix expression from the specified file. */
-  Expr* parseExprFromFile(const Path& path);
-  Expr* parseExprFromFile(const Path& path, StaticEnv& staticEnv);
-
-  /* Parse a Nix expression from the specified string. */
-  Expr* parseExprFromString(const std::string& s, const Path& basePath,
-                            StaticEnv& staticEnv);
-  Expr* parseExprFromString(const std::string& s, const Path& basePath);
-
-  Expr* parseStdin();
-
-  /* Evaluate an expression read from the given file to normal
-     form. */
-  void evalFile(const Path& path, Value& v);
-
-  void resetFileCache();
-
-  /* Look up a file in the search path. */
-  Path findFile(const std::string& path);
-  Path findFile(SearchPath& searchPath, const std::string& path,
-                const Pos& pos = noPos);
-
-  /* If the specified search path element is a URI, download it. */
-  std::pair<bool, std::string> resolveSearchPathElem(
-      const SearchPathElem& elem);
-
-  /* Evaluate an expression to normal form, storing the result in
-     value `v'. */
-  void eval(Expr* e, Value& v);
-
-  /* Evaluation the expression, then verify that it has the expected
-     type. */
-  inline bool evalBool(Env& env, Expr* e);
-  inline bool evalBool(Env& env, Expr* e, const Pos& pos);
-  inline void evalAttrs(Env& env, Expr* e, Value& v);
-
-  /* If `v' is a thunk, enter it and overwrite `v' with the result
-     of the evaluation of the thunk.  If `v' is a delayed function
-     application, call the function and overwrite `v' with the
-     result.  Otherwise, this is a no-op. */
-  inline void forceValue(Value& v, const Pos& pos = noPos);
-
-  /* Force a value, then recursively force list elements and
-     attributes. */
-  void forceValueDeep(Value& v);
-
-  /* Force `v', and then verify that it has the expected type. */
-  NixInt forceInt(Value& v, const Pos& pos);
-  NixFloat forceFloat(Value& v, const Pos& pos);
-  bool forceBool(Value& v, const Pos& pos);
-  inline void forceAttrs(Value& v);
-  inline void forceAttrs(Value& v, const Pos& pos);
-  inline void forceList(Value& v);
-  inline void forceList(Value& v, const Pos& pos);
-  void forceFunction(Value& v, const Pos& pos);  // either lambda or primop
-  std::string forceString(Value& v, const Pos& pos = noPos);
-  std::string forceString(Value& v, PathSet& context, const Pos& pos = noPos);
-  std::string forceStringNoCtx(Value& v, const Pos& pos = noPos);
-
-  /* Return true iff the value `v' denotes a derivation (i.e. a
-     set with attribute `type = "derivation"'). */
-  bool isDerivation(Value& v);
-
-  std::optional<std::string> tryAttrsToString(const Pos& pos, Value& v,
-                                              PathSet& context,
-                                              bool coerceMore = false,
-                                              bool copyToStore = true);
-
-  /* String coercion.  Converts strings, paths and derivations to a
-     string.  If `coerceMore' is set, also converts nulls, integers,
-     booleans and lists to a string.  If `copyToStore' is set,
-     referenced paths are copied to the Nix store as a side effect. */
-  std::string coerceToString(const Pos& pos, Value& v, PathSet& context,
-                             bool coerceMore = false, bool copyToStore = true);
-
-  std::string copyPathToStore(PathSet& context, const Path& path);
-
-  /* Path coercion.  Converts strings, paths and derivations to a
-     path.  The result is guaranteed to be a canonicalised, absolute
-     path.  Nothing is copied to the store. */
-  Path coerceToPath(const Pos& pos, Value& v, PathSet& context);
-
- public:
-  /* The base environment, containing the builtin functions and
-     values. */
-  Env& baseEnv;
-
-  /* The same, but used during parsing to resolve variables. */
-  StaticEnv staticBaseEnv;  // !!! should be private
-
- private:
-  unsigned int baseEnvDispl = 0;
-
-  void createBaseEnv();
-
-  Value* addConstant(const std::string& name, Value& v);
-
-  Value* addPrimOp(const std::string& name, size_t arity, PrimOpFun primOp);
-
- public:
-  Value& getBuiltin(const std::string& name);
-
- private:
-  inline Value* lookupVar(Env* env, const ExprVar& var, bool noEval);
-
-  friend struct ExprVar;
-  friend struct ExprAttrs;
-  friend struct ExprLet;
-
-  Expr* parse(const char* text, const Path& path, const Path& basePath,
-              StaticEnv& staticEnv);
-
- public:
-  /* Do a deep equality test between two values.  That is, list
-     elements and attributes are compared recursively. */
-  bool eqValues(Value& v1, Value& v2);
-
-  bool isFunctor(Value& fun);
-
-  void callFunction(Value& fun, Value& arg, Value& v, const Pos& pos);
-  void callPrimOp(Value& fun, Value& arg, Value& v, const Pos& pos);
-
-  /* Automatically call a function for which each argument has a
-     default value or has a binding in the `args' map.  'args' need
-     not live past the end of the call. */
-  void autoCallFunction(Bindings* args, Value& fun, Value& res);
-
-  /* Allocation primitives. */
-  Value* allocValue();
-  Env& allocEnv(size_t size);
-
-  Value* allocAttr(Value& vAttrs, const Symbol& name);
-
-  // Create a list value from the specified vector.
-  void mkList(Value& v, std::shared_ptr<NixList> list);
-
-  // Create a list value, allocating as many elements as specified in
-  // size. This is used for the many cases in this codebase where
-  // assignment happens into the preallocated list.
-  void mkList(Value& v, size_t size = 0);
-
-  void mkAttrs(Value& v, size_t capacity);
-  void mkThunk_(Value& v, Expr* expr);
-  void mkPos(Value& v, Pos* pos);
-
-  void concatLists(Value& v, const NixList& lists, const Pos& pos);
-
-  /* Print statistics. */
-  void printStats();
-
-  void realiseContext(const PathSet& context);
-
-  /* File access tracing. */
-  void TraceFileAccess(const Path& path);
-  void EnableFileAccessTracing(std::function<void(const Path&)> fn);
-
- private:
-  unsigned long nrEnvs = 0;
-  unsigned long nrValuesInEnvs = 0;
-  unsigned long nrValues = 0;
-  unsigned long nrListElems = 0;
-  unsigned long nrAttrsets = 0;
-  unsigned long nrAttrsInAttrsets = 0;
-  unsigned long nrOpUpdates = 0;
-  unsigned long nrOpUpdateValuesCopied = 0;
-  unsigned long nrListConcats = 0;
-  unsigned long nrPrimOpCalls = 0;
-  unsigned long nrFunctionCalls = 0;
-
-  bool countCalls;
-
-  std::function<void(const Path&)> file_access_trace_fn = nullptr;
-  Path last_traced_file = "";
-
-  using PrimOpCalls = std::map<Symbol, size_t>;
-  PrimOpCalls primOpCalls;
-
-  using FunctionCalls = std::map<ExprLambda*, size_t>;
-  FunctionCalls functionCalls;
-
-  void incrFunctionCall(ExprLambda* fun);
-
-  using AttrSelects = std::map<Pos, size_t>;
-  AttrSelects attrSelects;
-
-  friend struct ExprOpUpdate;
-  friend struct ExprOpConcatLists;
-  friend struct ExprSelect;
-  friend void prim_getAttr(EvalState& state, const Pos& pos, Value** args,
-                           Value& v);
-};
-
-/* Return a string representing the type of the value `v'. */
-std::string showType(const Value& v);
-
-/* Decode a context string β€˜!<name>!<path>’ into a pair <path,
-   name>. */
-std::pair<std::string, std::string> decodeContext(const std::string& s);
-
-/* If `path' refers to a directory, then append "/default.nix". */
-Path resolveExprPath(Path path);
-
-struct InvalidPathError : EvalError {
-  Path path;
-  InvalidPathError(const Path& path);
-#ifdef EXCEPTION_NEEDS_THROW_SPEC
-  ~InvalidPathError() noexcept {};
-#endif
-};
-
-struct EvalSettings : Config {
-  Setting<bool> restrictEval{
-      this, false, "restrict-eval",
-      "Whether to restrict file system access to paths in $NIX_PATH, "
-      "and network access to the URI prefixes listed in 'allowed-uris'."};
-
-  Setting<bool> pureEval{this, false, "pure-eval",
-                         "Whether to restrict file system and network access "
-                         "to files specified by cryptographic hash."};
-
-  Setting<bool> enableImportFromDerivation{
-      this, true, "allow-import-from-derivation",
-      "Whether the evaluator allows importing the result of a derivation."};
-
-  Setting<Strings> allowedUris{
-      this,
-      {},
-      "allowed-uris",
-      "Prefixes of URIs that builtin functions such as fetchurl and fetchGit "
-      "are allowed to fetch."};
-
-  Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
-                                   "Emit log messages for each function entry "
-                                   "and exit at the 'vomit' log level (-vvvv)"};
-};
-
-extern EvalSettings evalSettings;
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/function-trace.cc b/third_party/nix/src/libexpr/function-trace.cc
deleted file mode 100644
index b1b856965c..0000000000
--- a/third_party/nix/src/libexpr/function-trace.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "libexpr/function-trace.hh"
-
-#include <glog/logging.h>
-
-namespace nix {
-
-FunctionCallTrace::FunctionCallTrace(const Pos& pos) : pos(pos) {
-  auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
-  auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
-  LOG(INFO) << "function-trace entered " << pos << " at " << ns.count();
-}
-
-FunctionCallTrace::~FunctionCallTrace() {
-  auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
-  auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
-  LOG(INFO) << "function-trace exited " << pos << " at " << ns.count();
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/function-trace.hh b/third_party/nix/src/libexpr/function-trace.hh
deleted file mode 100644
index 6b810159b8..0000000000
--- a/third_party/nix/src/libexpr/function-trace.hh
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <chrono>
-
-#include "libexpr/eval.hh"
-
-namespace nix {
-
-struct FunctionCallTrace {
-  const Pos& pos;
-  FunctionCallTrace(const Pos& pos);
-  ~FunctionCallTrace();
-};
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/get-drvs.cc b/third_party/nix/src/libexpr/get-drvs.cc
deleted file mode 100644
index 164c1e54f3..0000000000
--- a/third_party/nix/src/libexpr/get-drvs.cc
+++ /dev/null
@@ -1,446 +0,0 @@
-#include "libexpr/get-drvs.hh"
-
-#include <cstring>
-#include <regex>
-#include <utility>
-
-#include <absl/container/flat_hash_set.h>
-#include <absl/strings/numbers.h>
-#include <glog/logging.h>
-
-#include "libexpr/eval-inline.hh"
-#include "libstore/derivations.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-DrvInfo::DrvInfo(EvalState& state, std::string attrPath,
-                 std::shared_ptr<Bindings> attrs)
-    : state(&state), attrs(attrs), attrPath(std::move(attrPath)) {}
-
-DrvInfo::DrvInfo(EvalState& state, const ref<Store>& store,
-                 const std::string& drvPathWithOutputs)
-    : state(&state), attrPath("") {
-  auto spec = parseDrvPathWithOutputs(drvPathWithOutputs);
-
-  drvPath = spec.first;
-
-  auto drv = store->derivationFromPath(drvPath);
-
-  name = storePathToName(drvPath);
-
-  if (spec.second.size() > 1) {
-    throw Error(
-        "building more than one derivation output is not supported, in '%s'",
-        drvPathWithOutputs);
-  }
-
-  outputName = spec.second.empty() ? get(drv.env, "outputName", "out")
-                                   : *spec.second.begin();
-
-  auto i = drv.outputs.find(outputName);
-  if (i == drv.outputs.end()) {
-    throw Error("derivation '%s' does not have output '%s'", drvPath,
-                outputName);
-  }
-
-  outPath = i->second.path;
-}
-
-std::string DrvInfo::queryName() const {
-  if (name.empty() && (attrs != nullptr)) {
-    auto i = attrs->find(state->sName);
-    if (i == attrs->end()) {
-      throw TypeError("derivation name missing");
-    }
-    name = state->forceStringNoCtx(*i->second.value);
-  }
-  return name;
-}
-
-std::string DrvInfo::querySystem() const {
-  if (system.empty() && (attrs != nullptr)) {
-    auto i = attrs->find(state->sSystem);
-    system = i == attrs->end()
-                 ? "unknown"
-                 : state->forceStringNoCtx(*i->second.value, *i->second.pos);
-  }
-  return system;
-}
-
-std::string DrvInfo::queryDrvPath() const {
-  if (drvPath.empty() && (attrs != nullptr)) {
-    Bindings::iterator i = attrs->find(state->sDrvPath);
-    PathSet context;
-    drvPath = i != attrs->end() ? state->coerceToPath(*i->second.pos,
-                                                      *i->second.value, context)
-                                : "";
-  }
-  return drvPath;
-}
-
-std::string DrvInfo::queryOutPath() const {
-  if (outPath.empty() && (attrs != nullptr)) {
-    Bindings::iterator i = attrs->find(state->sOutPath);
-    PathSet context;
-    outPath = i != attrs->end() ? state->coerceToPath(*i->second.pos,
-                                                      *i->second.value, context)
-                                : "";
-  }
-  return outPath;
-}
-
-DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) {
-  if (outputs.empty()) {
-    /* Get the β€˜outputs’ list. */
-    Bindings::iterator i;
-    if ((attrs != nullptr) &&
-        (i = attrs->find(state->sOutputs)) != attrs->end()) {
-      state->forceList(*i->second.value, *i->second.pos);
-
-      /* For each output... */
-      for (unsigned int j = 0; j < i->second.value->listSize(); ++j) {
-        /* Evaluate the corresponding set. */
-        std::string name = state->forceStringNoCtx(*(*i->second.value->list)[j],
-                                                   *i->second.pos);
-        Bindings::iterator out = attrs->find(state->symbols.Create(name));
-        if (out == attrs->end()) {
-          continue;  // FIXME: throw error?
-        }
-        state->forceAttrs(*out->second.value);
-
-        /* And evaluate its β€˜outPath’ attribute. */
-        Bindings::iterator outPath =
-            out->second.value->attrs->find(state->sOutPath);
-        if (outPath == out->second.value->attrs->end()) {
-          continue;  // FIXME: throw error?
-        }
-        PathSet context;
-        outputs[name] = state->coerceToPath(*outPath->second.pos,
-                                            *outPath->second.value, context);
-      }
-    } else {
-      outputs["out"] = queryOutPath();
-    }
-  }
-  if (!onlyOutputsToInstall || (attrs == nullptr)) {
-    return outputs;
-  }
-
-  /* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
-  const Value* outTI = queryMeta("outputsToInstall");
-  if (outTI == nullptr) {
-    return outputs;
-  }
-  const auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
-  /* ^ this shows during `nix-env -i` right under the bad derivation */
-  if (!outTI->isList()) {
-    throw errMsg;
-  }
-  Outputs result;
-
-  for (Value* i : *outTI->list) {
-    if (i->type != tString) {
-      throw errMsg;
-    }
-    auto out = outputs.find(i->string.s);
-    if (out == outputs.end()) {
-      throw errMsg;
-    }
-    result.insert(*out);
-  }
-  return result;
-}
-
-std::string DrvInfo::queryOutputName() const {
-  if (outputName.empty() && (attrs != nullptr)) {
-    Bindings::iterator i = attrs->find(state->sOutputName);
-    outputName =
-        i != attrs->end() ? state->forceStringNoCtx(*i->second.value) : "";
-  }
-  return outputName;
-}
-
-Bindings* DrvInfo::getMeta() {
-  if (meta != nullptr) {
-    return meta.get();
-  }
-  if (attrs == nullptr) {
-    return nullptr;
-  }
-  Bindings::iterator a = attrs->find(state->sMeta);
-  if (a == attrs->end()) {
-    return nullptr;
-  }
-  state->forceAttrs(*a->second.value, *a->second.pos);
-  meta = a->second.value->attrs;
-  return meta.get();
-}
-
-StringSet DrvInfo::queryMetaNames() {
-  StringSet res;
-  if (getMeta() == nullptr) {
-    return res;
-  }
-  for (auto& i : *meta) {
-    res.insert(i.second.name);
-  }
-  return res;
-}
-
-bool DrvInfo::checkMeta(Value& v) {
-  state->forceValue(v);
-  if (v.isList()) {
-    for (unsigned int n = 0; n < v.listSize(); ++n) {
-      if (!checkMeta(*(*v.list)[n])) {
-        return false;
-      }
-    }
-    return true;
-  }
-  if (v.type == tAttrs) {
-    Bindings::iterator i = v.attrs->find(state->sOutPath);
-    if (i != v.attrs->end()) {
-      return false;
-    }
-    for (auto& i : *v.attrs) {
-      if (!checkMeta(*i.second.value)) {
-        return false;
-      }
-    }
-    return true;
-  } else {
-    return v.type == tInt || v.type == tBool || v.type == tString ||
-           v.type == tFloat;
-  }
-}
-
-Value* DrvInfo::queryMeta(const std::string& name) {
-  if (getMeta() == nullptr) {
-    return nullptr;
-  }
-  Bindings::iterator a = meta->find(state->symbols.Create(name));
-  if (a == meta->end() || !checkMeta(*a->second.value)) {
-    return nullptr;
-  }
-  return a->second.value;
-}
-
-std::string DrvInfo::queryMetaString(const std::string& name) {
-  Value* v = queryMeta(name);
-  if ((v == nullptr) || v->type != tString) {
-    return "";
-  }
-  return v->string.s;
-}
-
-NixInt DrvInfo::queryMetaInt(const std::string& name, NixInt def) {
-  Value* v = queryMeta(name);
-  if (v == nullptr) {
-    return def;
-  }
-  if (v->type == tInt) {
-    return v->integer;
-  }
-  if (v->type == tString) {
-    /* Backwards compatibility with before we had support for
-       integer meta fields. */
-    NixInt n;
-    if (absl::SimpleAtoi(v->string.s, &n)) {
-      return n;
-    }
-  }
-  return def;
-}
-
-NixFloat DrvInfo::queryMetaFloat(const std::string& name, NixFloat def) {
-  Value* v = queryMeta(name);
-  if (v == nullptr) {
-    return def;
-  }
-  if (v->type == tFloat) {
-    return v->fpoint;
-  }
-  if (v->type == tString) {
-    /* Backwards compatibility with before we had support for
-       float meta fields. */
-    NixFloat n;
-    if (string2Float(v->string.s, n)) {
-      return n;
-    }
-  }
-  return def;
-}
-
-bool DrvInfo::queryMetaBool(const std::string& name, bool def) {
-  Value* v = queryMeta(name);
-  if (v == nullptr) {
-    return def;
-  }
-  if (v->type == tBool) {
-    return v->boolean;
-  }
-  if (v->type == tString) {
-    /* Backwards compatibility with before we had support for
-       Boolean meta fields. */
-    if (strcmp(v->string.s, "true") == 0) {
-      return true;
-    }
-    if (strcmp(v->string.s, "false") == 0) {
-      return false;
-    }
-  }
-  return def;
-}
-
-void DrvInfo::setMeta(const std::string& name, Value* v) {
-  std::shared_ptr<Bindings> old = meta;
-  meta = std::shared_ptr<Bindings>(Bindings::New(old->size() + 1).release());
-  Symbol sym = state->symbols.Create(name);
-  if (old != nullptr) {
-    for (auto i : *old) {
-      if (i.second.name != sym) {
-        meta->push_back(i.second);
-      }
-    }
-  }
-  if (v != nullptr) {
-    meta->push_back(Attr(sym, v));
-  }
-}
-
-/* Cache for already considered attrsets. */
-using Done = absl::flat_hash_set<std::shared_ptr<Bindings>>;
-
-/* Evaluate value `v'.  If it evaluates to a set of type `derivation',
-   then put information about it in `drvs' (unless it's already in `done').
-   The result boolean indicates whether it makes sense
-   for the caller to recursively search for derivations in `v'. */
-static bool getDerivation(EvalState& state, Value& v,
-                          const std::string& attrPath, DrvInfos& drvs,
-                          Done& done, bool ignoreAssertionFailures) {
-  try {
-    state.forceValue(v);
-    if (!state.isDerivation(v)) {
-      return true;
-    }
-
-    /* Remove spurious duplicates (e.g., a set like `rec { x =
-       derivation {...}; y = x;}'. */
-    if (done.find(v.attrs) != done.end()) {
-      return false;
-    }
-    done.insert(v.attrs);
-
-    DrvInfo drv(state, attrPath, v.attrs);
-
-    drv.queryName();
-
-    drvs.push_back(drv);
-
-    return false;
-
-  } catch (AssertionError& e) {
-    if (ignoreAssertionFailures) {
-      return false;
-    }
-    throw;
-  }
-}
-
-std::optional<DrvInfo> getDerivation(EvalState& state, Value& v,
-                                     bool ignoreAssertionFailures) {
-  Done done;
-  DrvInfos drvs;
-  getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
-  if (drvs.size() != 1) {
-    return {};
-  }
-  return std::move(drvs.front());
-}
-
-static std::string addToPath(const std::string& s1, const std::string& s2) {
-  return s1.empty() ? s2 : s1 + "." + s2;
-}
-
-static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
-
-static void getDerivations(EvalState& state, Value& vIn,
-                           const std::string& pathPrefix, Bindings* autoArgs,
-                           DrvInfos& drvs, Done& done,
-                           bool ignoreAssertionFailures) {
-  Value v;
-  state.autoCallFunction(autoArgs, vIn, v);
-
-  /* Process the expression. */
-  if (!getDerivation(state, v, pathPrefix, drvs, done,
-                     ignoreAssertionFailures)) {
-    ;
-
-  } else if (v.type == tAttrs) {
-    /* !!! undocumented hackery to support combining channels in
-       nix-env.cc. */
-    bool combineChannels =
-        v.attrs->find(state.symbols.Create("_combineChannels")) !=
-        v.attrs->end();
-
-    /* Consider the attributes in sorted order to get more
-       deterministic behaviour in nix-env operations (e.g. when
-       there are names clashes between derivations, the derivation
-       bound to the attribute with the "lower" name should take
-       precedence). */
-    for (auto& [_, i] : *v.attrs) {
-      DLOG(INFO) << "evaluating attribute '" << i.name << "'";
-      if (!std::regex_match(std::string(i.name), attrRegex)) {
-        continue;
-      }
-      std::string pathPrefix2 = addToPath(pathPrefix, i.name);
-      if (combineChannels) {
-        getDerivations(state, *i.value, pathPrefix2, autoArgs, drvs, done,
-                       ignoreAssertionFailures);
-      } else if (getDerivation(state, *i.value, pathPrefix2, drvs, done,
-                               ignoreAssertionFailures)) {
-        /* If the value of this attribute is itself a set,
-           should we recurse into it?  => Only if it has a
-           `recurseForDerivations = true' attribute. */
-        if (i.value->type == tAttrs) {
-          Bindings::iterator j = i.value->attrs->find(
-              state.symbols.Create("recurseForDerivations"));
-          if (j != i.value->attrs->end() &&
-              state.forceBool(*j->second.value, *j->second.pos)) {
-            getDerivations(state, *i.value, pathPrefix2, autoArgs, drvs, done,
-                           ignoreAssertionFailures);
-          }
-        }
-      }
-    }
-  }
-
-  else if (v.isList()) {
-    for (unsigned int n = 0; n < v.listSize(); ++n) {
-      std::string pathPrefix2 =
-          addToPath(pathPrefix, (format("%1%") % n).str());
-      if (getDerivation(state, *(*v.list)[n], pathPrefix2, drvs, done,
-                        ignoreAssertionFailures)) {
-        getDerivations(state, *(*v.list)[n], pathPrefix2, autoArgs, drvs, done,
-                       ignoreAssertionFailures);
-      }
-    }
-  }
-
-  else {
-    throw TypeError(
-        "expression does not evaluate to a derivation (or a set or list of "
-        "those)");
-  }
-}
-
-void getDerivations(EvalState& state, Value& v, const std::string& pathPrefix,
-                    Bindings* autoArgs, DrvInfos& drvs,
-                    bool ignoreAssertionFailures) {
-  Done done;
-  getDerivations(state, v, pathPrefix, autoArgs, drvs, done,
-                 ignoreAssertionFailures);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/get-drvs.hh b/third_party/nix/src/libexpr/get-drvs.hh
deleted file mode 100644
index 3de266d0c0..0000000000
--- a/third_party/nix/src/libexpr/get-drvs.hh
+++ /dev/null
@@ -1,83 +0,0 @@
-#pragma once
-
-#include <map>
-#include <string>
-
-#include "libexpr/eval.hh"
-
-namespace nix {
-
-struct DrvInfo {
- public:
-  typedef std::map<std::string, Path> Outputs;
-
- private:
-  EvalState* state;
-
-  mutable std::string name;
-  mutable std::string system;
-  mutable std::string drvPath;
-  mutable std::string outPath;
-  mutable std::string outputName;
-  Outputs outputs;
-
-  bool failed = false;  // set if we get an AssertionError
-
-  std::shared_ptr<Bindings> attrs = nullptr;
-  std::shared_ptr<Bindings> meta = nullptr;
-
-  Bindings* getMeta();
-
-  bool checkMeta(Value& v);
-
- public:
-  std::string attrPath; /* path towards the derivation */
-
-  DrvInfo(EvalState& state) : state(&state){};
-  DrvInfo(EvalState& state, std::string attrPath,
-          std::shared_ptr<Bindings> attrs);
-  DrvInfo(EvalState& state, const ref<Store>& store,
-          const std::string& drvPathWithOutputs);
-
-  std::string queryName() const;
-  std::string querySystem() const;
-  std::string queryDrvPath() const;
-  std::string queryOutPath() const;
-  std::string queryOutputName() const;
-  /** Return the list of outputs. The "outputs to install" are determined by
-   * `meta.outputsToInstall`. */
-  Outputs queryOutputs(bool onlyOutputsToInstall = false);
-
-  StringSet queryMetaNames();
-  Value* queryMeta(const std::string& name);
-  std::string queryMetaString(const std::string& name);
-  NixInt queryMetaInt(const std::string& name, NixInt def);
-  NixFloat queryMetaFloat(const std::string& name, NixFloat def);
-  bool queryMetaBool(const std::string& name, bool def);
-  void setMeta(const std::string& name, Value* v);
-
-  /*
-  MetaInfo queryMetaInfo(EvalState & state) const;
-  MetaValue queryMetaInfo(EvalState & state, const std::string & name) const;
-  */
-
-  void setName(const std::string& s) { name = s; }
-  void setDrvPath(const std::string& s) { drvPath = s; }
-  void setOutPath(const std::string& s) { outPath = s; }
-
-  void setFailed() { failed = true; };
-  bool hasFailed() { return failed; };
-};
-
-using DrvInfos = std::list<DrvInfo>;
-
-/* If value `v' denotes a derivation, return a DrvInfo object
-   describing it. Otherwise return nothing. */
-std::optional<DrvInfo> getDerivation(EvalState& state, Value& v,
-                                     bool ignoreAssertionFailures);
-
-void getDerivations(EvalState& state, Value& v, const std::string& pathPrefix,
-                    Bindings* autoArgs, DrvInfos& drvs,
-                    bool ignoreAssertionFailures);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/json-to-value.cc b/third_party/nix/src/libexpr/json-to-value.cc
deleted file mode 100644
index 043f8c64cd..0000000000
--- a/third_party/nix/src/libexpr/json-to-value.cc
+++ /dev/null
@@ -1,152 +0,0 @@
-#include "libexpr/json-to-value.hh"
-
-#include <nlohmann/json.hpp>
-#include <variant>
-#include <vector>
-
-#include "libexpr/value.hh"
-
-using json = nlohmann::json;
-
-namespace nix {
-
-// for more information, refer to
-// https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp
-class JSONSax : nlohmann::json_sax<json> {
-  class JSONState {
-   protected:
-    std::unique_ptr<JSONState> parent;
-    std::shared_ptr<Value*> v;
-
-   public:
-    virtual std::unique_ptr<JSONState> resolve(EvalState&) {
-      throw std::logic_error("tried to close toplevel json parser state");
-    }
-    explicit JSONState(std::unique_ptr<JSONState>&& p) : parent(std::move(p)) {}
-    explicit JSONState(Value* v) : v(allocRootValue(v)) {}
-    JSONState(JSONState& p) = delete;
-    Value& value(EvalState& state) {
-      if (!v) v = allocRootValue(state.allocValue());
-      return **v;
-    }
-    virtual ~JSONState() {}
-    virtual void add() {}
-  };
-
-  class JSONObjectState : public JSONState {
-    using JSONState::JSONState;
-    ValueMap attrs = ValueMap();
-    std::unique_ptr<JSONState> resolve(EvalState& state) override {
-      Value& v = parent->value(state);
-      state.mkAttrs(v, attrs.size());
-      for (auto& i : attrs) v.attrs->push_back(Attr(i.first, i.second));
-      return std::move(parent);
-    }
-    void add() override { v = nullptr; };
-
-   public:
-    void key(string_t& name, EvalState& state) {
-      attrs[state.symbols.Create(name)] = &value(state);
-    }
-  };
-
-  class JSONListState : public JSONState {
-    using JSONState::JSONState;
-    std::vector<Value*> values;
-    std::unique_ptr<JSONState> resolve(EvalState& state) override {
-      Value& v = parent->value(state);
-      state.mkList(v, values.size());
-      for (size_t n = 0; n < values.size(); ++n) {
-        (*v.list)[n] = values[n];
-      }
-      return std::move(parent);
-    }
-    void add() override {
-      values.push_back(*v);
-      v = nullptr;
-    };
-
-   public:
-    JSONListState(std::unique_ptr<JSONState>&& p, std::size_t reserve)
-        : JSONState(std::move(p)) {
-      values.reserve(reserve);
-    }
-  };
-
-  EvalState& state;
-  std::unique_ptr<JSONState> rs;
-
-  template <typename T, typename... Args>
-  inline bool handle_value(T f, Args... args) {
-    f(rs->value(state), args...);
-    rs->add();
-    return true;
-  }
-
- public:
-  JSONSax(EvalState& state, Value& v) : state(state), rs(new JSONState(&v)){};
-
-  bool null() override { return handle_value(mkNull); }
-
-  bool boolean(bool val) override { return handle_value(mkBool, val); }
-
-  bool number_integer(number_integer_t val) override {
-    return handle_value(mkInt, val);
-  }
-
-  bool number_unsigned(number_unsigned_t val) override {
-    return handle_value(mkInt, val);
-  }
-
-  bool number_float(number_float_t val, const string_t&) override {
-    return handle_value(mkFloat, val);
-  }
-
-  bool string(string_t& val) override {
-    return handle_value<void(Value&, const char*)>(mkString, val.c_str());
-  }
-
-#if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8
-  bool binary(binary_t&) {
-    // This function ought to be unreachable
-    assert(false);
-    return true;
-  }
-#endif
-
-  bool start_object(std::size_t) override {
-    rs = std::make_unique<JSONObjectState>(std::move(rs));
-    return true;
-  }
-
-  bool key(string_t& name) override {
-    dynamic_cast<JSONObjectState*>(rs.get())->key(name, state);
-    return true;
-  }
-
-  bool end_object() override {
-    rs = rs->resolve(state);
-    rs->add();
-    return true;
-  }
-
-  bool end_array() override { return end_object(); }
-
-  bool start_array(size_t len) override {
-    rs = std::make_unique<JSONListState>(
-        std::move(rs), len != std::numeric_limits<size_t>::max() ? len : 128);
-    return true;
-  }
-
-  bool parse_error(std::size_t, const std::string&,
-                   const nlohmann::detail::exception& ex) override {
-    throw JSONParseError(ex.what());
-  }
-};
-
-void parseJSON(EvalState& state, const std::string& s_, Value& v) {
-  JSONSax parser(state, v);
-  bool res = json::sax_parse(s_, &parser);
-  if (!res) throw JSONParseError("Invalid JSON Value");
-}
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/json-to-value.hh b/third_party/nix/src/libexpr/json-to-value.hh
deleted file mode 100644
index 7f258f2137..0000000000
--- a/third_party/nix/src/libexpr/json-to-value.hh
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include <string>
-
-#include "libexpr/eval.hh"
-
-namespace nix {
-
-MakeError(JSONParseError, EvalError);
-
-void parseJSON(EvalState& state, const std::string& s, Value& v);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/lexer.l b/third_party/nix/src/libexpr/lexer.l
deleted file mode 100644
index d5b8a45936..0000000000
--- a/third_party/nix/src/libexpr/lexer.l
+++ /dev/null
@@ -1,193 +0,0 @@
-%option reentrant bison-bridge bison-locations
-%option noyywrap
-%option never-interactive
-%option stack
-%option nodefault
-%option nounput noyy_top_state
-
-
-%s DEFAULT
-%x STRING
-%x IND_STRING
-
-
-%{
-#include <boost/lexical_cast.hpp>
-
-#include "generated/parser-tab.hh"
-#include "libexpr/nixexpr.hh"
-#include "libexpr/parser.hh"
-
-using namespace nix;
-
-namespace nix {
-
-static void initLoc(YYLTYPE* loc) {
-  loc->first_line = loc->last_line = 1;
-  loc->first_column = loc->last_column = 1;
-}
-
-static void adjustLoc(YYLTYPE* loc, const char* s, size_t len) {
-  loc->first_line = loc->last_line;
-  loc->first_column = loc->last_column;
-
-  while (len--) {
-    switch (*s++) {
-      case '\r':
-        if (*s == '\n') /* cr/lf */
-          s++;
-        /* fall through */
-      case '\n':
-        ++loc->last_line;
-        loc->last_column = 1;
-        break;
-      default:
-        ++loc->last_column;
-    }
-  }
-}
-
-}
-
-#define YY_USER_INIT initLoc(yylloc)
-#define YY_USER_ACTION adjustLoc(yylloc, yytext, yyleng);
-
-#define PUSH_STATE(state) yy_push_state(state, yyscanner)
-#define POP_STATE() yy_pop_state(yyscanner)
-
-%}
-
-
-ANY         .|\n
-ID          [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
-INT         [0-9]+
-FLOAT       (([1-9][0-9]*\.[0-9]*)|(0?\.[0-9]+))([Ee][+-]?[0-9]+)?
-PATH        [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+\/?
-HPATH       \~(\/[a-zA-Z0-9\.\_\-\+]+)+\/?
-SPATH       \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
-URI         [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+
-
-
-%%
-
-
-if          { return IF; }
-then        { return THEN; }
-else        { return ELSE; }
-assert      { return ASSERT; }
-with        { return WITH; }
-let         { return LET; }
-in          { return IN; }
-rec         { return REC; }
-inherit     { return INHERIT; }
-or          { return OR_KW; }
-\.\.\.      { return ELLIPSIS; }
-
-\=\=        { return EQ; }
-\!\=        { return NEQ; }
-\<\=        { return LEQ; }
-\>\=        { return GEQ; }
-\&\&        { return AND; }
-\|\|        { return OR; }
-\-\>        { return IMPL; }
-\/\/        { return UPDATE; }
-\+\+        { return CONCAT; }
-
-{ID}        { yylval->id = strdup(yytext); return ID; }
-{INT}       { errno = 0;
-              try {
-                  yylval->n = boost::lexical_cast<int64_t>(yytext);
-              } catch (const boost::bad_lexical_cast &) {
-                  throw ParseError(format("invalid integer '%1%'") % yytext);
-              }
-              return INT;
-            }
-{FLOAT}     { errno = 0;
-              yylval->nf = strtod(yytext, 0);
-              if (errno != 0)
-                  throw ParseError(format("invalid float '%1%'") % yytext);
-              return FLOAT;
-            }
-
-\$\{        { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
-
-\}          { /* State INITIAL only exists at the bottom of the stack and is
-                 used as a marker. DEFAULT replaces it everywhere else.
-                 Popping when in INITIAL state causes an empty stack exception,
-                 so don't */
-              if (YYSTATE != INITIAL)
-                POP_STATE();
-              return '}';
-            }
-\{          { PUSH_STATE(DEFAULT); return '{'; }
-
-\"          { PUSH_STATE(STRING); return '"'; }
-<STRING>([^\$\"\\]|\$[^\{\"\\]|\\{ANY}|\$\\{ANY})*\$/\" |
-<STRING>([^\$\"\\]|\$[^\{\"\\]|\\{ANY}|\$\\{ANY})+ {
-                /* It is impossible to match strings ending with '$' with one
-                   regex because trailing contexts are only valid at the end
-                   of a rule. (A sane but undocumented limitation.) */
-                yylval->e = unescapeStr(data->symbols, yytext, yyleng);
-                return STR;
-              }
-<STRING>\$\{  { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
-<STRING>\"    { POP_STATE(); return '"'; }
-<STRING>\$|\\|\$\\ {
-                /* This can only occur when we reach EOF, otherwise the above
-                   (...|\$[^\{\"\\]|\\.|\$\\.)+ would have triggered.
-                   This is technically invalid, but we leave the problem to the
-                   parser who fails with exact location. */
-                return STR;
-              }
-
-\'\'(\ *\n)?     { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
-<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
-                   yylval->e = new ExprIndStr(yytext);
-                   return IND_STR;
-                 }
-<IND_STRING>\'\'\$ |
-<IND_STRING>\$   {
-                   yylval->e = new ExprIndStr("$");
-                   return IND_STR;
-                 }
-<IND_STRING>\'\'\' {
-                   yylval->e = new ExprIndStr("''");
-                   return IND_STR;
-                 }
-<IND_STRING>\'\'\\{ANY} {
-                   yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
-                   return IND_STR;
-                 }
-<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
-<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
-<IND_STRING>\'   {
-                   yylval->e = new ExprIndStr("'");
-                   return IND_STR;
-                 }
-
-
-{PATH}      { if (yytext[yyleng-1] == '/')
-                  throw ParseError("path '%s' has a trailing slash", yytext);
-              yylval->path = strdup(yytext);
-              return PATH;
-            }
-{HPATH}     { if (yytext[yyleng-1] == '/')
-                  throw ParseError("path '%s' has a trailing slash", yytext);
-              yylval->path = strdup(yytext);
-              return HPATH;
-            }
-{SPATH}     { yylval->path = strdup(yytext); return SPATH; }
-{URI}       { yylval->uri = strdup(yytext); return URI; }
-
-[ \t\r\n]+    /* eat up whitespace */
-\#[^\r\n]*    /* single-line comments */
-\/\*([^*]|\*+[^*/])*\*+\/  /* long comments */
-
-{ANY}       {
-              /* Don't return a negative number, as this will cause
-                 Bison to stop parsing without an error. */
-              return (unsigned char) yytext[0];
-            }
-
-%%
-
diff --git a/third_party/nix/src/libexpr/names.cc b/third_party/nix/src/libexpr/names.cc
deleted file mode 100644
index 1e9c2f2f4a..0000000000
--- a/third_party/nix/src/libexpr/names.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-#include "libexpr/names.hh"
-
-#include <memory>
-
-#include <absl/strings/numbers.h>
-
-#include "libutil/util.hh"
-
-namespace nix {
-
-DrvName::DrvName() { name = ""; }
-
-/* Parse a derivation name.  The `name' part of a derivation name is
-   everything up to but not including the first dash *not* followed by
-   a letter.  The `version' part is the rest (excluding the separating
-   dash).  E.g., `apache-httpd-2.0.48' is parsed to (`apache-httpd',
-   '2.0.48'). */
-DrvName::DrvName(const std::string& s) : hits(0) {
-  name = fullName = s;
-  for (unsigned int i = 0; i < s.size(); ++i) {
-    /* !!! isalpha/isdigit are affected by the locale. */
-    if (s[i] == '-' && i + 1 < s.size() && (isalpha(s[i + 1]) == 0)) {
-      name = std::string(s, 0, i);
-      version = std::string(s, i + 1);
-      break;
-    }
-  }
-}
-
-bool DrvName::matches(DrvName& n) {
-  if (name != "*") {
-    if (!regex) {
-      regex = std::make_unique<std::regex>(name, std::regex::extended);
-    }
-    if (!std::regex_match(n.name, *regex)) {
-      return false;
-    }
-  }
-  return !(!version.empty() && version != n.version);
-}
-
-std::string nextComponent(std::string::const_iterator& p,
-                          const std::string::const_iterator end) {
-  /* Skip any dots and dashes (component separators). */
-  while (p != end && (*p == '.' || *p == '-')) {
-    ++p;
-  }
-
-  if (p == end) {
-    return "";
-  }
-
-  /* If the first character is a digit, consume the longest sequence
-     of digits.  Otherwise, consume the longest sequence of
-     non-digit, non-separator characters. */
-  std::string s;
-  if (isdigit(*p) != 0) {
-    while (p != end && (isdigit(*p) != 0)) {
-      s += *p++;
-    }
-  } else {
-    while (p != end && ((isdigit(*p) == 0) && *p != '.' && *p != '-')) {
-      s += *p++;
-    }
-  }
-
-  return s;
-}
-
-static bool componentsLT(const std::string& c1, const std::string& c2) {
-  int n1;
-  int n2;
-  bool c1Num = absl::SimpleAtoi(c1, &n1);
-  bool c2Num = absl::SimpleAtoi(c2, &n2);
-
-  if (c1Num && c2Num) {
-    return n1 < n2;
-  }
-  if (c1.empty() && c2Num) {
-    return true;
-  } else if (c1 == "pre" && c2 != "pre") {
-    return true;
-  } else if (c2 == "pre") {
-    return false;
-    /* Assume that `2.3a' < `2.3.1'. */
-  } else if (c2Num) {
-    return true;
-  } else if (c1Num) {
-    return false;
-  } else {
-    return c1 < c2;
-  }
-}
-
-int compareVersions(const std::string& v1, const std::string& v2) {
-  std::string::const_iterator p1 = v1.begin();
-  std::string::const_iterator p2 = v2.begin();
-
-  while (p1 != v1.end() || p2 != v2.end()) {
-    std::string c1 = nextComponent(p1, v1.end());
-    std::string c2 = nextComponent(p2, v2.end());
-    if (componentsLT(c1, c2)) {
-      return -1;
-    }
-    if (componentsLT(c2, c1)) {
-      return 1;
-    }
-  }
-
-  return 0;
-}
-
-DrvNames drvNamesFromArgs(const Strings& opArgs) {
-  DrvNames result;
-  for (auto& i : opArgs) {
-    result.push_back(DrvName(i));
-  }
-  return result;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/names.hh b/third_party/nix/src/libexpr/names.hh
deleted file mode 100644
index 061388d517..0000000000
--- a/third_party/nix/src/libexpr/names.hh
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <regex>
-
-#include "libutil/types.hh"
-
-namespace nix {
-
-struct DrvName {
-  std::string fullName;
-  std::string name;
-  std::string version;
-  unsigned int hits;
-
-  DrvName();
-  DrvName(const std::string& s);
-  bool matches(DrvName& n);
-
- private:
-  std::unique_ptr<std::regex> regex;
-};
-
-typedef std::list<DrvName> DrvNames;
-
-std::string nextComponent(std::string::const_iterator& p,
-                          const std::string::const_iterator end);
-int compareVersions(const std::string& v1, const std::string& v2);
-DrvNames drvNamesFromArgs(const Strings& opArgs);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/nix-expr.pc.in b/third_party/nix/src/libexpr/nix-expr.pc.in
deleted file mode 100644
index 99b0ae2c68..0000000000
--- a/third_party/nix/src/libexpr/nix-expr.pc.in
+++ /dev/null
@@ -1,10 +0,0 @@
-prefix=@CMAKE_INSTALL_PREFIX@
-libdir=@CMAKE_INSTALL_FULL_LIBDIR@
-includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
-
-Name: Nix
-Description: Nix Package Manager
-Version: @PACKAGE_VERSION@
-Requires: nix-store bdw-gc
-Libs: -L${libdir} -lnixexpr
-Cflags: -I${includedir}/nix
diff --git a/third_party/nix/src/libexpr/nixexpr.cc b/third_party/nix/src/libexpr/nixexpr.cc
deleted file mode 100644
index 391f068205..0000000000
--- a/third_party/nix/src/libexpr/nixexpr.cc
+++ /dev/null
@@ -1,414 +0,0 @@
-#include "libexpr/nixexpr.hh"
-
-#include <cstdlib>
-#include <variant>
-
-#include "libstore/derivations.hh"
-#include "libutil/util.hh"
-#include "libutil/visitor.hh"
-
-namespace nix {
-
-/* Displaying abstract syntax trees. */
-
-std::ostream& operator<<(std::ostream& str, const Expr& e) {
-  e.show(str);
-  return str;
-}
-
-static void showString(std::ostream& str, const std::string& s) {
-  str << '"';
-  for (auto c : std::string(s)) {
-    if (c == '"' || c == '\\' || c == '$') {
-      str << "\\" << c;
-    } else if (c == '\n') {
-      str << "\\n";
-    } else if (c == '\r') {
-      str << "\\r";
-    } else if (c == '\t') {
-      str << "\\t";
-    } else {
-      str << c;
-    }
-  }
-  str << '"';
-}
-
-static void showId(std::ostream& str, const std::string& s) {
-  if (s.empty()) {
-    str << "\"\"";
-  } else if (s == "if") {  // FIXME: handle other keywords
-    str << '"' << s << '"';
-  } else {
-    char c = s[0];
-    if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
-      showString(str, s);
-      return;
-    }
-    for (auto c : s) {
-      if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
-            (c >= '0' && c <= '9') || c == '_' || c == '\'' || c == '-')) {
-        showString(str, s);
-        return;
-      }
-    }
-    str << s;
-  }
-}
-
-std::ostream& operator<<(std::ostream& str, const Symbol& sym) {
-  showId(str, *sym.s);
-  return str;
-}
-
-void Expr::show(std::ostream& str) const { abort(); }
-
-void ExprInt::show(std::ostream& str) const { str << n; }
-
-void ExprFloat::show(std::ostream& str) const { str << nf; }
-
-void ExprString::show(std::ostream& str) const { showString(str, s); }
-
-void ExprPath::show(std::ostream& str) const { str << s; }
-
-void ExprVar::show(std::ostream& str) const { str << name; }
-
-void ExprSelect::show(std::ostream& str) const {
-  str << "(" << *e << ")." << showAttrPath(attrPath);
-  if (def != nullptr) {
-    str << " or (" << *def << ")";
-  }
-}
-
-void ExprOpHasAttr::show(std::ostream& str) const {
-  str << "((" << *e << ") ? " << showAttrPath(attrPath) << ")";
-}
-
-void ExprAttrs::show(std::ostream& str) const {
-  if (recursive) {
-    str << "rec ";
-  }
-  str << "{ ";
-  for (auto& i : attrs) {
-    if (i.second.inherited) {
-      str << "inherit " << i.first << " "
-          << "; ";
-    } else {
-      str << i.first << " = " << *i.second.e << "; ";
-    }
-  }
-  for (auto& i : dynamicAttrs) {
-    str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
-  }
-  str << "}";
-}
-
-void ExprList::show(std::ostream& str) const {
-  str << "[ ";
-  for (auto& i : elems) {
-    str << "(" << *i << ") ";
-  }
-  str << "]";
-}
-
-void ExprLambda::show(std::ostream& str) const {
-  str << "(";
-  if (matchAttrs) {
-    str << "{ ";
-    bool first = true;
-    for (auto& i : formals->formals) {
-      if (first) {
-        first = false;
-      } else {
-        str << ", ";
-      }
-      str << i.name;
-      if (i.def != nullptr) {
-        str << " ? " << *i.def;
-      }
-    }
-    if (formals->ellipsis) {
-      if (!first) {
-        str << ", ";
-      }
-      str << "...";
-    }
-    str << " }";
-    if (!arg.empty()) {
-      str << " @ ";
-    }
-  }
-  if (!arg.empty()) {
-    str << arg;
-  }
-  str << ": " << *body << ")";
-}
-
-void ExprLet::show(std::ostream& str) const {
-  str << "(let ";
-  for (auto& i : attrs->attrs) {
-    if (i.second.inherited) {
-      str << "inherit " << i.first << "; ";
-    } else {
-      str << i.first << " = " << *i.second.e << "; ";
-    }
-  }
-  str << "in " << *body << ")";
-}
-
-void ExprWith::show(std::ostream& str) const {
-  str << "(with " << *attrs << "; " << *body << ")";
-}
-
-void ExprIf::show(std::ostream& str) const {
-  str << "(if " << *cond << " then " << *then << " else " << *else_ << ")";
-}
-
-void ExprAssert::show(std::ostream& str) const {
-  str << "assert " << *cond << "; " << *body;
-}
-
-void ExprOpNot::show(std::ostream& str) const { str << "(! " << *e << ")"; }
-
-void ExprConcatStrings::show(std::ostream& str) const {
-  bool first = true;
-  str << "(";
-  for (auto& i : *es) {
-    if (first) {
-      first = false;
-    } else {
-      str << " + ";
-    }
-    str << *i;
-  }
-  str << ")";
-}
-
-void ExprPos::show(std::ostream& str) const { str << "__curPos"; }
-
-std::ostream& operator<<(std::ostream& str, const Pos& pos) {
-  if (!pos || !pos.file.has_value()) {
-    str << "undefined position";
-  } else {
-    str << (format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%") %
-            std::string(pos.file.value()) % pos.line % pos.column)
-               .str();
-  }
-  return str;
-}
-
-std::string showAttrPath(const AttrPath& attrPath) {
-  std::ostringstream out;
-  bool first = true;
-  for (auto& attr : attrPath) {
-    if (!first) {
-      out << '.';
-    } else {
-      first = false;
-    }
-
-    std::visit(util::overloaded{
-                   [&](const Symbol& sym) { out << sym; },
-                   [&](const Expr* expr) { out << "\"${" << *expr << "}\""; }},
-               attr);
-  }
-  return out.str();
-}
-
-Pos noPos;
-
-/* Computing levels/displacements for variables. */
-
-void Expr::bindVars(const StaticEnv& env) { abort(); }
-
-void ExprInt::bindVars(const StaticEnv& env) {}
-
-void ExprFloat::bindVars(const StaticEnv& env) {}
-
-void ExprString::bindVars(const StaticEnv& env) {}
-
-void ExprPath::bindVars(const StaticEnv& env) {}
-
-void ExprVar::bindVars(const StaticEnv& env) {
-  /* Check whether the variable appears in the environment.  If so,
-     set its level and displacement. */
-  const StaticEnv* curEnv;
-  unsigned int level;
-  std::optional<unsigned int> withLevel = std::nullopt;
-  for (curEnv = &env, level = 0; curEnv != nullptr;
-       curEnv = curEnv->up, level++) {
-    if (curEnv->isWith) {
-      if (!withLevel.has_value()) {
-        withLevel = level;
-      }
-    } else {
-      auto i = curEnv->vars.find(name);
-      if (i != curEnv->vars.end()) {
-        fromWith = false;
-        this->level = level;
-        displ = i->second;
-        return;
-      }
-    }
-  }
-
-  /* Otherwise, the variable must be obtained from the nearest
-     enclosing `with'.  If there is no `with', then we can issue an
-     "undefined variable" error now. */
-  if (!withLevel.has_value()) {
-    throw UndefinedVarError(format("undefined variable '%1%' at %2%") % name %
-                            pos);
-  }
-
-  fromWith = true;
-  this->level = withLevel.value();
-}
-
-void ExprSelect::bindVars(const StaticEnv& env) {
-  e->bindVars(env);
-  if (def != nullptr) {
-    def->bindVars(env);
-  }
-  for (auto& i : attrPath) {
-    if (auto* expr = std::get_if<Expr*>(&i)) {
-      (*expr)->bindVars(env);
-    }
-  }
-}
-
-void ExprOpHasAttr::bindVars(const StaticEnv& env) {
-  e->bindVars(env);
-  for (auto& i : attrPath) {
-    if (auto* expr = std::get_if<Expr*>(&i)) {
-      (*expr)->bindVars(env);
-    }
-  }
-}
-
-void ExprAttrs::bindVars(const StaticEnv& env) {
-  const StaticEnv* dynamicEnv = &env;
-  StaticEnv newEnv(/* isWith = */ false, &env);
-
-  if (recursive) {
-    dynamicEnv = &newEnv;
-
-    unsigned int displ = 0;
-    for (auto& i : attrs) {
-      newEnv.vars[i.first] = i.second.displ = displ++;
-    }
-
-    for (auto& i : attrs) {
-      i.second.e->bindVars(i.second.inherited ? env : newEnv);
-    }
-  }
-
-  else {
-    for (auto& i : attrs) {
-      i.second.e->bindVars(env);
-    }
-  }
-
-  for (auto& i : dynamicAttrs) {
-    i.nameExpr->bindVars(*dynamicEnv);
-    i.valueExpr->bindVars(*dynamicEnv);
-  }
-}
-
-void ExprList::bindVars(const StaticEnv& env) {
-  for (auto& i : elems) {
-    i->bindVars(env);
-  }
-}
-
-void ExprLambda::bindVars(const StaticEnv& env) {
-  StaticEnv newEnv(false, &env);
-
-  unsigned int displ = 0;
-
-  if (!arg.empty()) {
-    newEnv.vars[arg] = displ++;
-  }
-
-  if (matchAttrs) {
-    for (auto& i : formals->formals) {
-      newEnv.vars[i.name] = displ++;
-    }
-
-    for (auto& i : formals->formals) {
-      if (i.def != nullptr) {
-        i.def->bindVars(newEnv);
-      }
-    }
-  }
-
-  body->bindVars(newEnv);
-}
-
-void ExprLet::bindVars(const StaticEnv& env) {
-  StaticEnv newEnv(false, &env);
-
-  unsigned int displ = 0;
-  for (auto& i : attrs->attrs) {
-    newEnv.vars[i.first] = i.second.displ = displ++;
-  }
-
-  for (auto& i : attrs->attrs) {
-    i.second.e->bindVars(i.second.inherited ? env : newEnv);
-  }
-
-  body->bindVars(newEnv);
-}
-
-void ExprWith::bindVars(const StaticEnv& env) {
-  /* Does this `with' have an enclosing `with'?  If so, record its
-     level so that `lookupVar' can look up variables in the previous
-     `with' if this one doesn't contain the desired attribute. */
-  const StaticEnv* curEnv;
-  unsigned int level;
-  prevWith = 0;
-  for (curEnv = &env, level = 1; curEnv != nullptr;
-       curEnv = curEnv->up, level++) {
-    if (curEnv->isWith) {
-      prevWith = level;
-      break;
-    }
-  }
-
-  attrs->bindVars(env);
-  StaticEnv newEnv(true, &env);
-  body->bindVars(newEnv);
-}
-
-void ExprIf::bindVars(const StaticEnv& env) {
-  cond->bindVars(env);
-  then->bindVars(env);
-  else_->bindVars(env);
-}
-
-void ExprAssert::bindVars(const StaticEnv& env) {
-  cond->bindVars(env);
-  body->bindVars(env);
-}
-
-void ExprOpNot::bindVars(const StaticEnv& env) { e->bindVars(env); }
-
-void ExprConcatStrings::bindVars(const StaticEnv& env) {
-  for (auto& i : *es) {
-    i->bindVars(env);
-  }
-}
-
-void ExprPos::bindVars(const StaticEnv& env) {}
-
-/* Storing function names. */
-void ExprLambda::setName(Symbol& name) { this->name = name; }
-
-std::string ExprLambda::showNamePos() const {
-  return (format("%1% at %2%") %
-          (name.has_value() ? "'" + std::string(name.value()) + "'"
-                            : "anonymous function") %
-          pos)
-      .str();
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/nixexpr.hh b/third_party/nix/src/libexpr/nixexpr.hh
deleted file mode 100644
index 16b58dec2e..0000000000
--- a/third_party/nix/src/libexpr/nixexpr.hh
+++ /dev/null
@@ -1,361 +0,0 @@
-#pragma once
-
-#include <map>
-#include <optional>
-#include <variant>
-
-#include <absl/container/flat_hash_map.h>
-
-#include "libexpr/symbol-table.hh"
-#include "libexpr/value.hh"
-#include "libutil/types.hh"  // TODO(tazjin): audit this include
-
-namespace nix {
-
-MakeError(EvalError, Error);
-MakeError(ParseError, Error);
-MakeError(AssertionError, EvalError);
-MakeError(ThrownError, AssertionError);
-MakeError(Abort, EvalError);
-MakeError(TypeError, EvalError);
-MakeError(UndefinedVarError, Error);
-MakeError(RestrictedPathError, Error);
-
-/* Position objects. */
-
-struct Pos {
-  std::optional<Symbol> file;
-  unsigned int line, column;
-  Pos(const std::optional<Symbol>& file, unsigned int line, unsigned int column)
-      : file(file), line(line), column(column){};
-
-  // TODO(tazjin): remove this - empty pos is never useful
-  Pos() : file(std::nullopt), line(0), column(0){};
-
-  operator bool() const { return line != 0; }
-
-  bool operator<(const Pos& p2) const {
-    if (!file.has_value()) {
-      return true;
-    }
-
-    if (!line) {
-      return p2.line;
-    }
-    if (!p2.line) {
-      return false;
-    }
-    int d = ((std::string)file.value()).compare((std::string)p2.file.value());
-    if (d < 0) {
-      return true;
-    }
-    if (d > 0) {
-      return false;
-    }
-    if (line < p2.line) {
-      return true;
-    }
-    if (line > p2.line) {
-      return false;
-    }
-    return column < p2.column;
-  }
-};
-
-extern Pos noPos;
-
-std::ostream& operator<<(std::ostream& str, const Pos& pos);
-
-struct Env;
-struct Value;
-class EvalState;
-struct StaticEnv;
-
-/* An attribute path is a sequence of attribute names. */
-using AttrName = std::variant<Symbol, Expr*>;
-using AttrPath = std::vector<AttrName>;
-using AttrNameVector = std::vector<AttrName>;
-
-using VectorExprs = std::vector<nix::Expr*>;
-
-std::string showAttrPath(const AttrPath& attrPath);
-
-/* Abstract syntax of Nix expressions. */
-
-struct Expr {
-  virtual ~Expr(){};
-  virtual void show(std::ostream& str) const;
-  virtual void bindVars(const StaticEnv& env);
-  virtual void eval(EvalState& state, Env& env, Value& v);
-  virtual Value* maybeThunk(EvalState& state, Env& env);
-};
-
-std::ostream& operator<<(std::ostream& str, const Expr& e);
-
-#define COMMON_METHODS                             \
-  void show(std::ostream& str) const;              \
-  void eval(EvalState& state, Env& env, Value& v); \
-  void bindVars(const StaticEnv& env);
-
-struct ExprInt : Expr {
-  NixInt n;
-  Value v;
-  ExprInt(NixInt n) : n(n) { mkInt(v, n); };
-  COMMON_METHODS
-  Value* maybeThunk(EvalState& state, Env& env);
-};
-
-struct ExprFloat : Expr {
-  NixFloat nf;
-  Value v;
-  ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); };
-  COMMON_METHODS
-  Value* maybeThunk(EvalState& state, Env& env);
-};
-
-struct ExprString : Expr {
-  Symbol s;
-  Value v;
-  ExprString(const Symbol& s) : s(s) { mkString(v, s); };
-  COMMON_METHODS
-  Value* maybeThunk(EvalState& state, Env& env);
-};
-
-/* Temporary class used during parsing of indented strings. */
-struct ExprIndStr : Expr {
-  std::string s;
-  ExprIndStr(const std::string& s) : s(s){};
-};
-
-struct ExprPath : Expr {
-  std::string s;
-  Value v;
-  ExprPath(const std::string& s) : s(s) { mkPathNoCopy(v, this->s.c_str()); };
-  COMMON_METHODS
-  Value* maybeThunk(EvalState& state, Env& env);
-};
-
-struct ExprVar : Expr {
-  Pos pos;
-  Symbol name;
-
-  /* Whether the variable comes from an environment (e.g. a rec, let
-     or function argument) or from a "with". */
-  bool fromWith;
-
-  /* In the former case, the value is obtained by going `level'
-     levels up from the current environment and getting the
-     `displ'th value in that environment.  In the latter case, the
-     value is obtained by getting the attribute named `name' from
-     the set stored in the environment that is `level' levels up
-     from the current one.*/
-  unsigned int level;
-  unsigned int displ;
-
-  ExprVar(const Symbol& name) : name(name){};
-  ExprVar(const Pos& pos, const Symbol& name) : pos(pos), name(name){};
-  COMMON_METHODS
-  Value* maybeThunk(EvalState& state, Env& env);
-};
-
-// [tazjin] I *think* that this struct describes the syntactic
-// construct for "selecting" something out of an attribute set, e.g.
-// `a.b.c` => ExprSelect{"b", "c"}.
-//
-// Each path element has got a pointer to an expression, which seems
-// to be the thing preceding its period, but afaict that is only set
-// for the first one in a path.
-struct ExprSelect : Expr {
-  Pos pos;
-  Expr *e, *def;
-  AttrPath attrPath;
-  ExprSelect(const Pos& pos, Expr* e, const AttrPath& attrPath, Expr* def)
-      : pos(pos), e(e), def(def), attrPath(attrPath){};
-  ExprSelect(const Pos& pos, Expr* e, const Symbol& name)
-      : pos(pos), e(e), def(0) {
-    attrPath.push_back(AttrName(name));
-  };
-  COMMON_METHODS
-};
-
-struct ExprOpHasAttr : Expr {
-  Pos pos;
-  Expr* e;
-  AttrPath attrPath;
-  ExprOpHasAttr(Expr* e, const AttrPath& attrPath) : e(e), attrPath(attrPath){};
-  ExprOpHasAttr(const Pos& pos, Expr* e, const AttrPath& attrPath)
-      : pos(pos), e(e), attrPath(attrPath){};
-  COMMON_METHODS
-};
-
-struct ExprAttrs : Expr {
-  bool recursive;
-
-  struct AttrDef {
-    bool inherited;
-    Expr* e;
-    Pos pos;
-    unsigned int displ;  // displacement
-    AttrDef(Expr* e, const Pos& pos, bool inherited = false)
-        : inherited(inherited), e(e), pos(pos), displ(0){};
-    AttrDef(){};
-  };
-
-  using AttrDefs = absl::flat_hash_map<Symbol, AttrDef>;
-  AttrDefs attrs;
-
-  struct DynamicAttrDef {
-    Expr *nameExpr, *valueExpr;
-    Pos pos;
-    DynamicAttrDef(Expr* nameExpr, Expr* valueExpr, const Pos& pos)
-        : nameExpr(nameExpr), valueExpr(valueExpr), pos(pos){};
-  };
-
-  using DynamicAttrDefs = std::vector<DynamicAttrDef>;
-  DynamicAttrDefs dynamicAttrs;
-
-  ExprAttrs() : recursive(false){};
-  COMMON_METHODS
-};
-
-struct ExprList : Expr {
-  VectorExprs elems;
-  ExprList(){};
-  COMMON_METHODS
-};
-
-struct Formal {
-  Symbol name;
-  Expr* def;  // def = default, not definition
-  Formal(const Symbol& name, Expr* def) : name(name), def(def){};
-};
-
-// Describes structured function arguments (e.g. `{ a }: ...`)
-struct Formals {
-  using Formals_ = std::list<Formal>;
-  Formals_ formals;
-  std::set<Symbol> argNames;  // used during parsing
-  bool ellipsis;
-};
-
-struct ExprLambda : Expr {
- public:
-  Pos pos;
-  std::optional<Symbol> name;
-  Symbol arg;
-  bool matchAttrs;
-  Formals* formals;
-  Expr* body;
-  ExprLambda(const Pos& pos, const Symbol& arg, bool matchAttrs,
-             Formals* formals, Expr* body)
-      : pos(pos),
-        arg(arg),
-        matchAttrs(matchAttrs),
-        formals(formals),
-        body(body) {
-    if (!arg.empty() && formals &&
-        formals->argNames.find(arg) != formals->argNames.end()) {
-      throw ParseError(
-          format("duplicate formal function argument '%1%' at %2%") % arg %
-          pos);
-    }
-  };
-  void setName(Symbol& name);
-  std::string showNamePos() const;
-  COMMON_METHODS
-};
-
-struct ExprLet : Expr {
-  ExprAttrs* attrs;
-  Expr* body;
-  ExprLet(ExprAttrs* attrs, Expr* body) : attrs(attrs), body(body){};
-  COMMON_METHODS
-};
-
-struct ExprWith : Expr {
-  Pos pos;
-  Expr *attrs, *body;
-  size_t prevWith;
-  ExprWith(const Pos& pos, Expr* attrs, Expr* body)
-      : pos(pos), attrs(attrs), body(body){};
-  COMMON_METHODS
-};
-
-struct ExprIf : Expr {
-  Pos pos;
-  Expr *cond, *then, *else_;
-  ExprIf(Expr* cond, Expr* then, Expr* else_)
-      : cond(cond), then(then), else_(else_){};
-  ExprIf(const Pos& pos, Expr* cond, Expr* then, Expr* else_)
-      : pos(pos), cond(cond), then(then), else_(else_){};
-  COMMON_METHODS
-};
-
-struct ExprAssert : Expr {
-  Pos pos;
-  Expr *cond, *body;
-  ExprAssert(const Pos& pos, Expr* cond, Expr* body)
-      : pos(pos), cond(cond), body(body){};
-  COMMON_METHODS
-};
-
-struct ExprOpNot : Expr {
-  Pos pos;
-  Expr* e;
-  explicit ExprOpNot(Expr* e) : e(e){};
-  ExprOpNot(const Pos& pos, Expr* e) : pos(pos), e(e){};
-  COMMON_METHODS
-};
-
-#define MakeBinOp(name, s)                                                 \
-  struct name : Expr {                                                     \
-    Pos pos;                                                               \
-    Expr *e1, *e2;                                                         \
-    name(Expr* e1, Expr* e2) : e1(e1), e2(e2){};                           \
-    name(const Pos& pos, Expr* e1, Expr* e2) : pos(pos), e1(e1), e2(e2){}; \
-    void show(std::ostream& str) const {                                   \
-      str << "(" << *e1 << " " s " " << *e2 << ")";                        \
-    }                                                                      \
-    void bindVars(const StaticEnv& env) {                                  \
-      e1->bindVars(env);                                                   \
-      e2->bindVars(env);                                                   \
-    }                                                                      \
-    void eval(EvalState& state, Env& env, Value& v);                       \
-  };
-
-MakeBinOp(ExprApp, "");
-MakeBinOp(ExprOpEq, "==");
-MakeBinOp(ExprOpNEq, "!=");
-MakeBinOp(ExprOpAnd, "&&");
-MakeBinOp(ExprOpOr, "||");
-MakeBinOp(ExprOpImpl, "->");
-MakeBinOp(ExprOpUpdate, "//");
-MakeBinOp(ExprOpConcatLists, "++");
-
-struct ExprConcatStrings : Expr {
-  Pos pos;
-  bool forceString;
-  nix::VectorExprs* es;
-  ExprConcatStrings(const Pos& pos, bool forceString, nix::VectorExprs* es)
-      : pos(pos), forceString(forceString), es(es){};
-  COMMON_METHODS
-};
-
-struct ExprPos : Expr {
-  Pos pos;
-  ExprPos(const Pos& pos) : pos(pos){};
-  COMMON_METHODS
-};
-
-/* Static environments are used to map variable names onto (level,
-   displacement) pairs used to obtain the value of the variable at
-   runtime. */
-struct StaticEnv {
-  bool isWith;
-  const StaticEnv* up;
-  typedef absl::flat_hash_map<Symbol, unsigned int> Vars;
-  Vars vars;
-  StaticEnv(bool isWith, const StaticEnv* up) : isWith(isWith), up(up){};
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/parser.cc b/third_party/nix/src/libexpr/parser.cc
deleted file mode 100644
index aea6cec7e4..0000000000
--- a/third_party/nix/src/libexpr/parser.cc
+++ /dev/null
@@ -1,332 +0,0 @@
-#include "libexpr/parser.hh"
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libexpr/eval.hh"
-#include "libstore/download.hh"
-#include "libstore/store-api.hh"
-
-namespace nix {
-
-void addAttr(ExprAttrs* attrs, AttrPath& attrPath, Expr* e, const Pos& pos) {
-  AttrPath::iterator i;
-  // All attrpaths have at least one attr
-  assert(!attrPath.empty());
-  // Checking attrPath validity.
-  // ===========================
-  for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
-    if (const auto* sym = std::get_if<Symbol>(&(*i)); sym && sym->set()) {
-      ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*sym);
-      if (j != attrs->attrs.end()) {
-        if (!j->second.inherited) {
-          ExprAttrs* attrs2 = dynamic_cast<ExprAttrs*>(j->second.e);
-          if (!attrs2) {
-            dupAttr(attrPath, pos, j->second.pos);
-          }
-          attrs = attrs2;
-        } else {
-          dupAttr(attrPath, pos, j->second.pos);
-        }
-      } else {
-        ExprAttrs* nested = new ExprAttrs;
-        attrs->attrs[*sym] = ExprAttrs::AttrDef(nested, pos);
-        attrs = nested;
-      }
-    } else {
-      // Yes, this code does not handle all conditions
-      // exhaustively. We use std::get to throw if the condition
-      // that isn't covered happens, which is potentially a
-      // behaviour change from the previous default constructed
-      // Symbol. It should alert us about anything untoward going
-      // on here.
-      auto* expr = std::get<Expr*>(*i);
-
-      ExprAttrs* nested = new ExprAttrs;
-      attrs->dynamicAttrs.push_back(
-          ExprAttrs::DynamicAttrDef(expr, nested, pos));
-      attrs = nested;
-    }
-  }
-  // Expr insertion.
-  // ==========================
-  if (auto* sym = std::get_if<Symbol>(&(*i)); sym && sym->set()) {
-    ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*sym);
-    if (j != attrs->attrs.end()) {
-      // This attr path is already defined. However, if both
-      // e and the expr pointed by the attr path are two attribute sets,
-      // we want to merge them.
-      // Otherwise, throw an error.
-      auto ae = dynamic_cast<ExprAttrs*>(e);
-      auto jAttrs = dynamic_cast<ExprAttrs*>(j->second.e);
-      if (jAttrs && ae) {
-        for (auto& ad : ae->attrs) {
-          auto j2 = jAttrs->attrs.find(ad.first);
-          if (j2 !=
-              jAttrs->attrs.end()) {  // Attr already defined in iAttrs, error.
-            dupAttr(ad.first, j2->second.pos, ad.second.pos);
-          }
-          jAttrs->attrs[ad.first] = ad.second;
-        }
-      } else {
-        dupAttr(attrPath, pos, j->second.pos);
-      }
-    } else {
-      // This attr path is not defined. Let's create it.
-      attrs->attrs[*sym] = ExprAttrs::AttrDef(e, pos);
-    }
-  } else {
-    // Same caveat as the identical line above.
-    auto* expr = std::get<Expr*>(*i);
-    attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(expr, e, pos));
-  }
-}
-
-void addFormal(const Pos& pos, Formals* formals, const Formal& formal) {
-  if (formals->argNames.find(formal.name) != formals->argNames.end()) {
-    throw ParseError(format("duplicate formal function argument '%1%' at %2%") %
-                     formal.name % pos);
-  }
-  formals->formals.push_front(formal);
-  formals->argNames.insert(formal.name);
-}
-
-Expr* stripIndentation(const Pos& pos, SymbolTable& symbols, VectorExprs& es) {
-  if (es.empty()) {
-    return new ExprString(symbols.Create(""));
-  }
-
-  /* Figure out the minimum indentation.  Note that by design
-     whitespace-only final lines are not taken into account.  (So
-     the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
-  bool atStartOfLine = true; /* = seen only whitespace in the current line */
-  size_t minIndent = 1000000;
-  size_t curIndent = 0;
-  for (auto& i : es) {
-    ExprIndStr* e = dynamic_cast<ExprIndStr*>(i);
-    if (!e) {
-      /* Anti-quotations end the current start-of-line whitespace. */
-      if (atStartOfLine) {
-        atStartOfLine = false;
-        if (curIndent < minIndent) {
-          minIndent = curIndent;
-        }
-      }
-      continue;
-    }
-    for (size_t j = 0; j < e->s.size(); ++j) {
-      if (atStartOfLine) {
-        if (e->s[j] == ' ') {
-          curIndent++;
-        } else if (e->s[j] == '\n') {
-          /* Empty line, doesn't influence minimum
-             indentation. */
-          curIndent = 0;
-        } else {
-          atStartOfLine = false;
-          if (curIndent < minIndent) {
-            minIndent = curIndent;
-          }
-        }
-      } else if (e->s[j] == '\n') {
-        atStartOfLine = true;
-        curIndent = 0;
-      }
-    }
-  }
-
-  /* Strip spaces from each line. */
-  VectorExprs* es2 = new VectorExprs;
-  atStartOfLine = true;
-  size_t curDropped = 0;
-  size_t n = es.size();
-  for (VectorExprs::iterator i = es.begin(); i != es.end(); ++i, --n) {
-    ExprIndStr* e = dynamic_cast<ExprIndStr*>(*i);
-    if (!e) {
-      atStartOfLine = false;
-      curDropped = 0;
-      es2->push_back(*i);
-      continue;
-    }
-
-    std::string s2;
-    for (size_t j = 0; j < e->s.size(); ++j) {
-      if (atStartOfLine) {
-        if (e->s[j] == ' ') {
-          if (curDropped++ >= minIndent) {
-            s2 += e->s[j];
-          }
-        } else if (e->s[j] == '\n') {
-          curDropped = 0;
-          s2 += e->s[j];
-        } else {
-          atStartOfLine = false;
-          curDropped = 0;
-          s2 += e->s[j];
-        }
-      } else {
-        s2 += e->s[j];
-        if (e->s[j] == '\n') {
-          atStartOfLine = true;
-        }
-      }
-    }
-
-    /* Remove the last line if it is empty and consists only of
-       spaces. */
-    if (n == 1) {
-      std::string::size_type p = s2.find_last_of('\n');
-      if (p != std::string::npos &&
-          s2.find_first_not_of(' ', p + 1) == std::string::npos) {
-        s2 = std::string(s2, 0, p + 1);
-      }
-    }
-
-    es2->push_back(new ExprString(symbols.Create(s2)));
-  }
-
-  /* If this is a single string, then don't do a concatenation. */
-  return es2->size() == 1 && dynamic_cast<ExprString*>((*es2)[0])
-             ? (*es2)[0]
-             : new ExprConcatStrings(pos, true, es2);
-}
-
-Path resolveExprPath(Path path) {
-  assert(path[0] == '/');
-
-  /* If `path' is a symlink, follow it.  This is so that relative
-     path references work. */
-  struct stat st;
-  while (true) {
-    if (lstat(path.c_str(), &st)) {
-      throw SysError(format("getting status of '%1%'") % path);
-    }
-    if (!S_ISLNK(st.st_mode)) {
-      break;
-    }
-    path = absPath(readLink(path), dirOf(path));
-  }
-
-  /* If `path' refers to a directory, append `/default.nix'. */
-  if (S_ISDIR(st.st_mode)) {
-    path = canonPath(path + "/default.nix");
-  }
-
-  return path;
-}
-
-// These methods are actually declared in eval.hh, and were - for some
-// reason - previously implemented in parser.y.
-
-Expr* EvalState::parseExprFromFile(const Path& path) {
-  return parseExprFromFile(path, staticBaseEnv);
-}
-
-Expr* EvalState::parseExprFromFile(const Path& path, StaticEnv& staticEnv) {
-  return parse(readFile(path).c_str(), path, dirOf(path), staticEnv);
-}
-
-Expr* EvalState::parseExprFromString(const std::string& s, const Path& basePath,
-                                     StaticEnv& staticEnv) {
-  return parse(s.c_str(), "(std::string)", basePath, staticEnv);
-}
-
-Expr* EvalState::parseExprFromString(const std::string& s,
-                                     const Path& basePath) {
-  return parseExprFromString(s, basePath, staticBaseEnv);
-}
-
-Expr* EvalState::parseStdin() {
-  // Activity act(*logger, lvlTalkative, format("parsing standard input"));
-  return parseExprFromString(drainFD(0), absPath("."));
-}
-
-void EvalState::addToSearchPath(const std::string& s) {
-  size_t pos = s.find('=');
-  std::string prefix;
-  Path path;
-  if (pos == std::string::npos) {
-    path = s;
-  } else {
-    prefix = std::string(s, 0, pos);
-    path = std::string(s, pos + 1);
-  }
-
-  searchPath.emplace_back(prefix, path);
-}
-
-Path EvalState::findFile(const std::string& path) {
-  return findFile(searchPath, path);
-}
-
-Path EvalState::findFile(SearchPath& searchPath, const std::string& path,
-                         const Pos& pos) {
-  for (auto& i : searchPath) {
-    std::string suffix;
-    if (i.first.empty()) {
-      suffix = "/" + path;
-    } else {
-      auto s = i.first.size();
-      if (path.compare(0, s, i.first) != 0 ||
-          (path.size() > s && path[s] != '/')) {
-        continue;
-      }
-      suffix = path.size() == s ? "" : "/" + std::string(path, s);
-    }
-    auto r = resolveSearchPathElem(i);
-    if (!r.first) {
-      continue;
-    }
-    Path res = r.second + suffix;
-    if (pathExists(res)) {
-      return canonPath(res);
-    }
-  }
-  format f = format(
-      "file '%1%' was not found in the Nix search path (add it using $NIX_PATH "
-      "or -I)" +
-      std::string(pos ? ", at %2%" : ""));
-  f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
-  throw ThrownError(f % path % pos);
-}
-
-std::pair<bool, std::string> EvalState::resolveSearchPathElem(
-    const SearchPathElem& elem) {
-  auto i = searchPathResolved.find(elem.second);
-  if (i != searchPathResolved.end()) {
-    return i->second;
-  }
-
-  std::pair<bool, std::string> res;
-
-  if (isUri(elem.second)) {
-    try {
-      CachedDownloadRequest request(elem.second);
-      request.unpack = true;
-      res = {true, getDownloader()->downloadCached(store, request).path};
-    } catch (DownloadError& e) {
-      LOG(WARNING) << "Nix search path entry '" << elem.second
-                   << "' cannot be downloaded, ignoring";
-      res = {false, ""};
-    }
-  } else {
-    auto path = absPath(elem.second);
-    if (pathExists(path)) {
-      res = {true, path};
-    } else {
-      LOG(WARNING) << "Nix search path entry '" << elem.second
-                   << "' does not exist, ignoring";
-      res = {false, ""};
-    }
-  }
-
-  VLOG(2) << "resolved search path element '" << elem.second << "' to '"
-          << res.second << "'";
-
-  searchPathResolved[elem.second] = res;
-  return res;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/parser.hh b/third_party/nix/src/libexpr/parser.hh
deleted file mode 100644
index 70b5450b5a..0000000000
--- a/third_party/nix/src/libexpr/parser.hh
+++ /dev/null
@@ -1,100 +0,0 @@
-// Parser utilities for use in parser.y
-#pragma once
-
-// TODO(tazjin): Audit these includes, they were in parser.y
-#include <optional>
-#include <variant>
-
-#include <glog/logging.h>
-
-#include "libexpr/eval.hh"
-#include "libexpr/nixexpr.hh"
-#include "libutil/util.hh"
-
-#define YY_DECL                                                               \
-  int yylex(YYSTYPE* yylval_param, YYLTYPE* yylloc_param, yyscan_t yyscanner, \
-            nix::ParseData* data)
-
-#define CUR_POS makeCurPos(*yylocp, data)
-
-namespace nix {
-
-struct ParseData {
-  EvalState& state;
-  SymbolTable& symbols;
-  Expr* result;
-  Path basePath;
-  std::optional<Symbol> path;
-  std::string error;
-  Symbol sLetBody;
-
-  ParseData(EvalState& state)
-      : state(state),
-        symbols(state.symbols),
-        sLetBody(symbols.Create("<let-body>")){};
-};
-
-// Clang fails to identify these functions as used, probably because
-// of some interaction between the lexer/parser codegen and something
-// else.
-//
-// To avoid warnings for that we disable -Wunused-function in this block.
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
-
-// TODO(tazjin): move dupAttr to anonymous namespace
-static void dupAttr(const AttrPath& attrPath, const Pos& pos,
-                    const Pos& prevPos) {
-  throw ParseError(format("attribute '%1%' at %2% already defined at %3%") %
-                   showAttrPath(attrPath) % pos % prevPos);
-}
-
-static void dupAttr(Symbol attr, const Pos& pos, const Pos& prevPos) {
-  throw ParseError(format("attribute '%1%' at %2% already defined at %3%") %
-                   attr % pos % prevPos);
-}
-
-void addAttr(ExprAttrs* attrs, AttrPath& attrPath, Expr* e, const Pos& pos);
-
-void addFormal(const Pos& pos, Formals* formals, const Formal& formal);
-
-Expr* stripIndentation(const Pos& pos, SymbolTable& symbols, VectorExprs& es);
-
-Path resolveExprPath(Path path);
-
-// implementations originally from lexer.l
-
-static Expr* unescapeStr(SymbolTable& symbols, const char* s, size_t length) {
-  std::string t;
-  t.reserve(length);
-  char c;
-  while ((c = *s++)) {
-    if (c == '\\') {
-      assert(*s);
-      c = *s++;
-      if (c == 'n') {
-        t += '\n';
-      } else if (c == 'r') {
-        t += '\r';
-      } else if (c == 't') {
-        t += '\t';
-      } else {
-        t += c;
-      }
-    } else if (c == '\r') {
-      /* Normalise CR and CR/LF into LF. */
-      t += '\n';
-      if (*s == '\n') {
-        s++;
-      } /* cr/lf */
-    } else {
-      t += c;
-    }
-  }
-  return new ExprString(symbols.Create(t));
-}
-
-#pragma clang diagnostic pop  // re-enable -Wunused-function
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/parser.y b/third_party/nix/src/libexpr/parser.y
deleted file mode 100644
index a8af06802f..0000000000
--- a/third_party/nix/src/libexpr/parser.y
+++ /dev/null
@@ -1,359 +0,0 @@
-%glr-parser
-%locations
-%define parse.error verbose
-%define api.pure true
-%defines
-/* %no-lines */
-%parse-param { void * scanner }
-%parse-param { nix::ParseData * data }
-%lex-param { void * scanner }
-%lex-param { nix::ParseData * data }
-%expect 1
-%expect-rr 1
-
-%code requires {
-#define YY_NO_INPUT 1 // disable unused yyinput features
-#include "libexpr/parser.hh"
-
-struct YYSTYPE {
-  union {
-    nix::Expr * e;
-    nix::ExprList * list;
-    nix::ExprAttrs * attrs;
-    nix::Formals * formals;
-    nix::Formal * formal;
-    nix::NixInt n;
-    nix::NixFloat nf;
-    const char * id; // !!! -> Symbol
-    char * path;
-    char * uri;
-    nix::AttrNameVector * attrNames;
-    nix::VectorExprs * string_parts;
-  };
-};
-
-}
-
-%{
-
-#include "generated/parser-tab.hh"
-#include "generated/lexer-tab.hh"
-
-YY_DECL;
-
-using namespace nix;
-
-namespace nix {
-
-static inline Pos makeCurPos(const YYLTYPE& loc, ParseData* data) {
-  return Pos(data->path, loc.first_line, loc.first_column);
-}
-
-void yyerror(YYLTYPE* loc, yyscan_t scanner, ParseData* data,
-             const char* error) {
-  data->error = (format("%1%, at %2%") % error % makeCurPos(*loc, data)).str();
-}
-
-}
-
-%}
-
-%type <e> start expr expr_function expr_if expr_op
-%type <e> expr_app expr_select expr_simple
-%type <list> expr_list
-%type <attrs> binds
-%type <formals> formals
-%type <formal> formal
-%type <attrNames> attrs attrpath
-%type <string_parts> string_parts_interpolated ind_string_parts
-%type <e> string_parts string_attr
-%type <id> attr
-%token <id> ID ATTRPATH
-%token <e> STR IND_STR
-%token <n> INT
-%token <nf> FLOAT
-%token <path> PATH HPATH SPATH
-%token <uri> URI
-%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
-%token DOLLAR_CURLY /* == ${ */
-%token IND_STRING_OPEN IND_STRING_CLOSE
-%token ELLIPSIS
-
-%right IMPL
-%left OR
-%left AND
-%nonassoc EQ NEQ
-%nonassoc '<' '>' LEQ GEQ
-%right UPDATE
-%left NOT
-%left '+' '-'
-%left '*' '/'
-%right CONCAT
-%nonassoc '?'
-%nonassoc NEGATE
-
-%%
-
-start: expr { data->result = $1; };
-
-expr: expr_function;
-
-expr_function
-  : ID ':' expr_function
-    { $$ = new ExprLambda(CUR_POS, data->symbols.Create($1), false, 0, $3); }
-  | '{' formals '}' ':' expr_function
-    { $$ = new ExprLambda(CUR_POS, data->symbols.Create(""), true, $2, $5); }
-  | '{' formals '}' '@' ID ':' expr_function
-    { $$ = new ExprLambda(CUR_POS, data->symbols.Create($5), true, $2, $7); }
-  | ID '@' '{' formals '}' ':' expr_function
-    { $$ = new ExprLambda(CUR_POS, data->symbols.Create($1), true, $4, $7); }
-  | ASSERT expr ';' expr_function
-    { $$ = new ExprAssert(CUR_POS, $2, $4); }
-  | WITH expr ';' expr_function
-    { $$ = new ExprWith(CUR_POS, $2, $4); }
-  | LET binds IN expr_function
-    { if (!$2->dynamicAttrs.empty())
-        throw ParseError(format("dynamic attributes not allowed in let at %1%")
-            % CUR_POS);
-      $$ = new ExprLet($2, $4);
-    }
-  | expr_if
-  ;
-
-expr_if
-  : IF expr THEN expr ELSE expr { $$ = new ExprIf(CUR_POS, $2, $4, $6); }
-  | expr_op
-  ;
-
-expr_op
-  : '!' expr_op %prec NOT { $$ = new ExprOpNot(CUR_POS, $2); }
-  | '-' expr_op %prec NEGATE { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.Create("__sub")), new ExprInt(0)), $2); }
-  | expr_op EQ expr_op { $$ = new ExprOpEq(CUR_POS, $1, $3); }
-  | expr_op NEQ expr_op { $$ = new ExprOpNEq(CUR_POS, $1, $3); }
-  | expr_op '<' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.Create("__lessThan")), $1), $3); }
-  | expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.Create("__lessThan")), $3), $1)); }
-  | expr_op '>' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.Create("__lessThan")), $3), $1); }
-  | expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.Create("__lessThan")), $1), $3)); }
-  | expr_op AND expr_op { $$ = new ExprOpAnd(CUR_POS, $1, $3); }
-  | expr_op OR expr_op { $$ = new ExprOpOr(CUR_POS, $1, $3); }
-  | expr_op IMPL expr_op { $$ = new ExprOpImpl(CUR_POS, $1, $3); }
-  | expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
-  | expr_op '?' attrpath { $$ = new ExprOpHasAttr(CUR_POS, $1, *$3); }
-  | expr_op '+' expr_op
-    { $$ = new ExprConcatStrings(CUR_POS, false, new nix::VectorExprs({$1, $3})); }
-  | expr_op '-' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.Create("__sub")), $1), $3); }
-  | expr_op '*' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.Create("__mul")), $1), $3); }
-  | expr_op '/' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.Create("__div")), $1), $3); }
-  | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(CUR_POS, $1, $3); }
-  | expr_app
-  ;
-
-expr_app
-  : expr_app expr_select
-    { $$ = new ExprApp(CUR_POS, $1, $2); }
-  | expr_select { $$ = $1; }
-  ;
-
-expr_select
-  : expr_simple '.' attrpath
-    { $$ = new ExprSelect(CUR_POS, $1, *$3, 0); }
-  | expr_simple '.' attrpath OR_KW expr_select
-    { $$ = new ExprSelect(CUR_POS, $1, *$3, $5); }
-  | /* Backwards compatibility: because Nixpkgs has a rarely used
-       function named β€˜or’, allow stuff like β€˜map or [...]’. */
-    expr_simple OR_KW
-    { $$ = new ExprApp(CUR_POS, $1, new ExprVar(CUR_POS, data->symbols.Create("or"))); }
-  | expr_simple { $$ = $1; }
-  ;
-
-expr_simple
-  : ID {
-      if (strcmp($1, "__curPos") == 0)
-          $$ = new ExprPos(CUR_POS);
-      else
-          $$ = new ExprVar(CUR_POS, data->symbols.Create($1));
-  }
-  | INT { $$ = new ExprInt($1); }
-  | FLOAT { $$ = new ExprFloat($1); }
-  | '"' string_parts '"' { $$ = $2; }
-  | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
-      $$ = stripIndentation(CUR_POS, data->symbols, *$2);
-  }
-  | PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
-  | HPATH { $$ = new ExprPath(getHome() + std::string{$1 + 1}); }
-  | SPATH {
-      std::string path($1 + 1, strlen($1) - 2);
-      $$ = new ExprApp(CUR_POS,
-          new ExprApp(new ExprVar(data->symbols.Create("__findFile")),
-              new ExprVar(data->symbols.Create("__nixPath"))),
-          new ExprString(data->symbols.Create(path)));
-  }
-  | URI { $$ = new ExprString(data->symbols.Create($1)); }
-  | '(' expr ')' { $$ = $2; }
-  /* Let expressions `let {..., body = ...}' are just desugared
-     into `(rec {..., body = ...}).body'. */
-  | LET '{' binds '}'
-    { $3->recursive = true; $$ = new ExprSelect(noPos, $3, data->symbols.Create("body")); }
-  | REC '{' binds '}'
-    { $3->recursive = true; $$ = $3; }
-  | '{' binds '}'
-    { $$ = $2; }
-  | '[' expr_list ']' { $$ = $2; }
-  ;
-
-string_parts
-  : STR
-  | string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
-  | { $$ = new ExprString(data->symbols.Create("")); }
-  ;
-
-string_parts_interpolated
-  : string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
-  | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
-  | DOLLAR_CURLY expr '}' { $$ = new nix::VectorExprs; $$->push_back($2); }
-  | STR DOLLAR_CURLY expr '}' {
-      $$ = new nix::VectorExprs;
-      $$->push_back($1);
-      $$->push_back($3);
-    }
-  ;
-
-ind_string_parts
-  : ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
-  | ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
-  | { $$ = new nix::VectorExprs; }
-  ;
-
-binds
-  : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
-  | binds INHERIT attrs ';'
-    { $$ = $1;
-      for (auto & i : *$3) {
-          auto sym = std::get<Symbol>(i);
-          if ($$->attrs.find(sym) != $$->attrs.end()) {
-              dupAttr(sym, makeCurPos(@3, data), $$->attrs[sym].pos);
-          }
-          Pos pos = makeCurPos(@3, data);
-          $$->attrs[sym] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, sym), pos, true);
-      }
-    }
-  | binds INHERIT '(' expr ')' attrs ';'
-    { $$ = $1;
-      /* !!! Should ensure sharing of the expression in $4. */
-      for (auto & i : *$6) {
-          auto sym = std::get<Symbol>(i);
-          if ($$->attrs.find(sym) != $$->attrs.end()) {
-            dupAttr(sym, makeCurPos(@6, data), $$->attrs[sym].pos);
-          }
-          $$->attrs[sym] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, sym), makeCurPos(@6, data));
-      }
-    }
-  | { $$ = new ExprAttrs; }
-  ;
-
-attrs
-  : attrs attr { $$ = $1; $1->push_back(AttrName(data->symbols.Create($2))); }
-  | attrs string_attr
-    { $$ = $1;
-      ExprString * str = dynamic_cast<ExprString *>($2);
-      if (str) {
-          $$->push_back(AttrName(str->s));
-          delete str;
-      } else
-          throw ParseError(format("dynamic attributes not allowed in inherit at %1%")
-              % makeCurPos(@2, data));
-    }
-  | { $$ = new AttrPath; }
-  ;
-
-attrpath
-  : attrpath '.' attr { $$ = $1; $1->push_back(AttrName(data->symbols.Create($3))); }
-  | attrpath '.' string_attr
-    { $$ = $1;
-      ExprString * str = dynamic_cast<ExprString *>($3);
-      if (str) {
-          $$->push_back(AttrName(str->s));
-          delete str;
-      } else {
-          $$->push_back(AttrName($3));
-      }
-    }
-  | attr { $$ = new nix::AttrNameVector; $$->push_back(AttrName(data->symbols.Create($1))); }
-  | string_attr
-    { $$ = new nix::AttrNameVector;
-      ExprString *str = dynamic_cast<ExprString *>($1);
-      if (str) {
-          $$->push_back(AttrName(str->s));
-          delete str;
-      } else
-          $$->push_back(AttrName($1));
-    }
-  ;
-
-attr
-  : ID { $$ = $1; }
-  | OR_KW { $$ = "or"; }
-  ;
-
-string_attr
-  : '"' string_parts '"' { $$ = $2; }
-  | DOLLAR_CURLY expr '}' { $$ = $2; }
-  ;
-
-expr_list
-  : expr_list expr_select { $$ = $1; $1->elems.push_back($2); /* !!! dangerous */ }
-  | { $$ = new ExprList; }
-  ;
-
-formals
-  : formal ',' formals
-    { $$ = $3; addFormal(CUR_POS, $$, *$1); }
-  | formal
-    { $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
-  |
-    { $$ = new Formals; $$->ellipsis = false; }
-  | ELLIPSIS
-    { $$ = new Formals; $$->ellipsis = true; }
-  ;
-
-formal
-  : ID { $$ = new Formal(data->symbols.Create($1), 0); }
-  | ID '?' expr { $$ = new Formal(data->symbols.Create($1), $3); }
-  ;
-
-%%
-
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "libexpr/eval.hh"
-#include "libstore/store-api.hh"
-
-
-namespace nix {
-
-Expr* EvalState::parse(const char* text, const Path& path, const Path& basePath,
-                       StaticEnv& staticEnv) {
-  yyscan_t scanner;
-  ParseData data(*this);
-  data.basePath = basePath;
-  data.path = data.symbols.Create(path);
-
-  yylex_init(&scanner);
-  yy_scan_string(text, scanner);
-  int res = yyparse(scanner, &data);
-  yylex_destroy(scanner);
-
-  if (res) {
-    throw ParseError(data.error);
-  }
-
-  data.result->bindVars(staticEnv);
-
-  return data.result;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/primops.cc b/third_party/nix/src/libexpr/primops.cc
deleted file mode 100644
index 710d214ce7..0000000000
--- a/third_party/nix/src/libexpr/primops.cc
+++ /dev/null
@@ -1,2336 +0,0 @@
-#include "libexpr/primops.hh"
-
-#include <algorithm>
-#include <cstring>
-#include <iostream>
-#include <regex>
-
-#include <absl/strings/str_split.h>
-#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libexpr/eval-inline.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/json-to-value.hh"
-#include "libexpr/names.hh"
-#include "libexpr/value-to-json.hh"
-#include "libexpr/value-to-xml.hh"
-#include "libstore/derivations.hh"
-#include "libstore/download.hh"
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libutil/archive.hh"
-#include "libutil/json.hh"
-#include "libutil/status.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-/*************************************************************
- * Miscellaneous
- *************************************************************/
-
-/* Decode a context string β€˜!<name>!<path>’ into a pair <path,
-   name>. */
-std::pair<std::string, std::string> decodeContext(const std::string& s) {
-  if (s.at(0) == '!') {
-    size_t index = s.find('!', 1);
-    return std::pair<std::string, std::string>(std::string(s, index + 1),
-                                               std::string(s, 1, index - 1));
-  }
-  return std::pair<std::string, std::string>(
-      s.at(0) == '/' ? s : std::string(s, 1), "");
-}
-
-InvalidPathError::InvalidPathError(const Path& path)
-    : EvalError(format("path '%1%' is not valid") % path), path(path) {}
-
-void EvalState::realiseContext(const PathSet& context) {
-  PathSet drvs;
-
-  for (auto& i : context) {
-    std::pair<std::string, std::string> decoded = decodeContext(i);
-    Path ctx = decoded.first;
-    assert(store->isStorePath(ctx));
-    if (!store->isValidPath(ctx)) {
-      throw InvalidPathError(ctx);
-    }
-    if (!decoded.second.empty() && nix::isDerivation(ctx)) {
-      drvs.insert(decoded.first + "!" + decoded.second);
-
-      /* Add the output of this derivation to the allowed
-         paths. */
-      if (allowedPaths) {
-        auto drv = store->derivationFromPath(decoded.first);
-        auto i = drv.outputs.find(decoded.second);
-        if (i == drv.outputs.end()) {
-          throw Error("derivation '%s' does not have an output named '%s'",
-                      decoded.first, decoded.second);
-        }
-        allowedPaths->insert(i->second.path);
-      }
-    }
-  }
-
-  if (drvs.empty()) {
-    return;
-  }
-
-  if (!evalSettings.enableImportFromDerivation) {
-    throw EvalError(format("attempted to realize '%1%' during evaluation but "
-                           "'allow-import-from-derivation' is false") %
-                    *(drvs.begin()));
-  }
-
-  /* For performance, prefetch all substitute info. */
-  PathSet willBuild;
-  PathSet willSubstitute;
-  PathSet unknown;
-  unsigned long long downloadSize;
-  unsigned long long narSize;
-  store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize,
-                      narSize);
-
-  nix::util::OkOrThrow(store->buildPaths(std::cerr, drvs));
-}
-
-/* Load and evaluate an expression from path specified by the
-   argument. */
-static void prim_scopedImport(EvalState& state, const Pos& pos, Value** args,
-                              Value& v) {
-  PathSet context;
-  Path path = state.coerceToPath(pos, *args[1], context);
-
-  try {
-    state.realiseContext(context);
-  } catch (InvalidPathError& e) {
-    throw EvalError(
-        format("cannot import '%1%', since path '%2%' is not valid, at %3%") %
-        path % e.path % pos);
-  }
-
-  Path realPath = state.checkSourcePath(state.toRealPath(path, context));
-
-  if (state.store->isStorePath(path) && state.store->isValidPath(path) &&
-      isDerivation(path)) {
-    Derivation drv = readDerivation(realPath);
-    Value& w = *state.allocValue();
-    state.mkAttrs(w, 3 + drv.outputs.size());
-    Value* v2 = state.allocAttr(w, state.sDrvPath);
-    mkString(*v2, path, {"=" + path});
-    v2 = state.allocAttr(w, state.sName);
-    mkString(*v2, drv.env["name"]);
-    Value* outputsVal = state.allocAttr(w, state.symbols.Create("outputs"));
-    state.mkList(*outputsVal, drv.outputs.size());
-    unsigned int outputs_index = 0;
-
-    for (const auto& o : drv.outputs) {
-      v2 = state.allocAttr(w, state.symbols.Create(o.first));
-      mkString(*v2, o.second.path, {"!" + o.first + "!" + path});
-      (*outputsVal->list)[outputs_index] = state.allocValue();
-      mkString(*((*outputsVal->list)[outputs_index++]), o.first);
-    }
-
-    Value fun;
-    state.evalFile(
-        settings.nixDataDir + "/nix/corepkgs/imported-drv-to-derivation.nix",
-        fun);
-    state.forceFunction(fun, pos);
-    mkApp(v, fun, w);
-    state.forceAttrs(v, pos);
-  } else {
-    state.forceAttrs(*args[0]);
-    if (args[0]->attrs->empty()) {
-      state.evalFile(realPath, v);
-    } else {
-      Env* env = &state.allocEnv(args[0]->attrs->size());
-      env->up = &state.baseEnv;
-
-      StaticEnv staticEnv(false, &state.staticBaseEnv);
-
-      unsigned int displ = 0;
-      for (auto& attr : *args[0]->attrs) {
-        staticEnv.vars[attr.second.name] = displ;
-        env->values[displ++] = attr.second.value;
-      }
-
-      DLOG(INFO) << "evaluating file '" << realPath << "'";
-      Expr* e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv);
-
-      e->eval(state, *env, v);
-    }
-  }
-}
-
-/* Return a string representing the type of the expression. */
-static void prim_typeOf(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  state.forceValue(*args[0]);
-  std::string t;
-  switch (args[0]->type) {
-    case tInt:
-      t = "int";
-      break;
-    case tBool:
-      t = "bool";
-      break;
-    case tString:
-      t = "string";
-      break;
-    case tPath:
-      t = "path";
-      break;
-    case tNull:
-      t = "null";
-      break;
-    case tAttrs:
-      t = "set";
-      break;
-    case tList:
-      t = "list";
-      break;
-    case tLambda:
-    case tPrimOp:
-    case tPrimOpApp:
-      t = "lambda";
-      break;
-    case tFloat:
-      t = "float";
-      break;
-    default:
-      abort();
-  }
-  mkString(v, state.symbols.Create(t));
-}
-
-/* Determine whether the argument is the null value. */
-static void prim_isNull(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  state.forceValue(*args[0]);
-  mkBool(v, args[0]->type == tNull);
-}
-
-/* Determine whether the argument is a function. */
-static void prim_isFunction(EvalState& state, const Pos& pos, Value** args,
-                            Value& v) {
-  state.forceValue(*args[0]);
-  bool res;
-  switch (args[0]->type) {
-    case tLambda:
-    case tPrimOp:
-    case tPrimOpApp:
-      res = true;
-      break;
-    default:
-      res = false;
-      break;
-  }
-  mkBool(v, res);
-}
-
-/* Determine whether the argument is an integer. */
-static void prim_isInt(EvalState& state, const Pos& pos, Value** args,
-                       Value& v) {
-  state.forceValue(*args[0]);
-  mkBool(v, args[0]->type == tInt);
-}
-
-/* Determine whether the argument is a float. */
-static void prim_isFloat(EvalState& state, const Pos& pos, Value** args,
-                         Value& v) {
-  state.forceValue(*args[0]);
-  mkBool(v, args[0]->type == tFloat);
-}
-
-/* Determine whether the argument is a string. */
-static void prim_isString(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  state.forceValue(*args[0]);
-  mkBool(v, args[0]->type == tString);
-}
-
-/* Determine whether the argument is a Boolean. */
-static void prim_isBool(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  state.forceValue(*args[0]);
-  mkBool(v, args[0]->type == tBool);
-}
-
-/* Determine whether the argument is a path. */
-static void prim_isPath(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  state.forceValue(*args[0]);
-  mkBool(v, args[0]->type == tPath);
-}
-
-struct CompareValues {
-  bool operator()(const Value* v1, const Value* v2) const {
-    if (v1->type == tFloat && v2->type == tInt) {
-      return v1->fpoint < v2->integer;
-    }
-    if (v1->type == tInt && v2->type == tFloat) {
-      return v1->integer < v2->fpoint;
-    }
-    if (v1->type != v2->type) {
-      throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) %
-                      showType(*v2));
-    }
-    switch (v1->type) {
-      case tInt:
-        return v1->integer < v2->integer;
-      case tFloat:
-        return v1->fpoint < v2->fpoint;
-      case tString:
-        return strcmp(v1->string.s, v2->string.s) < 0;
-      case tPath:
-        return strcmp(v1->path, v2->path) < 0;
-      default:
-        throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) %
-                        showType(*v2));
-    }
-  }
-};
-
-typedef std::list<Value*> ValueList;
-
-static void prim_genericClosure(EvalState& state, const Pos& pos, Value** args,
-                                Value& v) {
-  state.forceAttrs(*args[0], pos);
-
-  /* Get the start set. */
-  Bindings::iterator startSet =
-      args[0]->attrs->find(state.symbols.Create("startSet"));
-  if (startSet == args[0]->attrs->end()) {
-    throw EvalError(format("attribute 'startSet' required, at %1%") % pos);
-  }
-  state.forceList(*startSet->second.value, pos);
-
-  ValueList workSet;
-  for (Value* elem : *startSet->second.value->list) {
-    workSet.push_back(elem);
-  }
-
-  /* Get the operator. */
-  Bindings::iterator op =
-      args[0]->attrs->find(state.symbols.Create("operator"));
-  if (op == args[0]->attrs->end()) {
-    throw EvalError(format("attribute 'operator' required, at %1%") % pos);
-  }
-  state.forceValue(*op->second.value);
-
-  /* Construct the closure by applying the operator to element of
-     `workSet', adding the result to `workSet', continuing until
-     no new elements are found. */
-  ValueList res;
-  // `doneKeys' doesn't need to be a GC root, because its values are
-  // reachable from res.
-  std::set<Value*, CompareValues> doneKeys;
-  while (!workSet.empty()) {
-    Value* e = *(workSet.begin());
-    workSet.pop_front();
-
-    state.forceAttrs(*e, pos);
-
-    Bindings::iterator key = e->attrs->find(state.symbols.Create("key"));
-    if (key == e->attrs->end()) {
-      throw EvalError(format("attribute 'key' required, at %1%") % pos);
-    }
-    state.forceValue(*key->second.value);
-
-    if (doneKeys.find(key->second.value) != doneKeys.end()) {
-      continue;
-    }
-    doneKeys.insert(key->second.value);
-    res.push_back(e);
-
-    /* Call the `operator' function with `e' as argument. */
-    Value call;
-    mkApp(call, *op->second.value, *e);
-    state.forceList(call, pos);
-
-    /* Add the values returned by the operator to the work set. */
-    for (unsigned int n = 0; n < call.listSize(); ++n) {
-      state.forceValue(*(*call.list)[n]);
-      workSet.push_back((*call.list)[n]);
-    }
-  }
-
-  /* Create the result list. */
-  state.mkList(v, res.size());
-  unsigned int n = 0;
-  for (auto& i : res) {
-    (*v.list)[n++] = i;
-  }
-}
-
-static void prim_abort(EvalState& state, const Pos& pos, Value** args,
-                       Value& v) {
-  PathSet context;
-  std::string s = state.coerceToString(pos, *args[0], context);
-  throw Abort(
-      format("evaluation aborted with the following error message: '%1%'") % s);
-}
-
-static void prim_throw(EvalState& state, const Pos& pos, Value** args,
-                       Value& v) {
-  PathSet context;
-  std::string s = state.coerceToString(pos, *args[0], context);
-  throw ThrownError(s);
-}
-
-static void prim_addErrorContext(EvalState& state, const Pos& pos, Value** args,
-                                 Value& v) {
-  try {
-    state.forceValue(*args[1]);
-    v = *args[1];
-  } catch (Error& e) {
-    PathSet context;
-    e.addPrefix(format("%1%\n") % state.coerceToString(pos, *args[0], context));
-    throw;
-  }
-}
-
-/* Try evaluating the argument. Success => {success=true; value=something;},
- * else => {success=false; value=false;} */
-static void prim_tryEval(EvalState& state, const Pos& pos, Value** args,
-                         Value& v) {
-  state.mkAttrs(v, 2);
-  try {
-    state.forceValue(*args[0]);
-    v.attrs->push_back(Attr(state.sValue, args[0]));
-    mkBool(*state.allocAttr(v, state.symbols.Create("success")), true);
-  } catch (AssertionError& e) {
-    mkBool(*state.allocAttr(v, state.sValue), false);
-    mkBool(*state.allocAttr(v, state.symbols.Create("success")), false);
-  }
-}
-
-/* Return an environment variable.  Use with care. */
-static void prim_getEnv(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  std::string name = state.forceStringNoCtx(*args[0], pos);
-  mkString(v, evalSettings.restrictEval || evalSettings.pureEval
-                  ? ""
-                  : getEnv(name).value_or(""));
-}
-
-/* Evaluate the first argument, then return the second argument. */
-static void prim_seq(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  state.forceValue(*args[0]);
-  state.forceValue(*args[1]);
-  v = *args[1];
-}
-
-/* Evaluate the first argument deeply (i.e. recursing into lists and
-   attrsets), then return the second argument. */
-static void prim_deepSeq(EvalState& state, const Pos& pos, Value** args,
-                         Value& v) {
-  state.forceValueDeep(*args[0]);
-  state.forceValue(*args[1]);
-  v = *args[1];
-}
-
-/* Evaluate the first expression and print it on standard error.  Then
-   return the second expression.  Useful for debugging. */
-static void prim_trace(EvalState& state, const Pos& pos, Value** args,
-                       Value& v) {
-  state.forceValue(*args[0]);
-  if (args[0]->type == tString) {
-    LOG(INFO) << "trace: " << args[0]->string.s;
-  } else {
-    LOG(INFO) << "trace: " << *args[0];
-  }
-  state.forceValue(*args[1]);
-  v = *args[1];
-}
-
-void prim_valueSize(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  /* We're not forcing the argument on purpose. */
-  mkInt(v, valueSize(*args[0]));
-}
-
-/*************************************************************
- * Derivations
- *************************************************************/
-
-/* Construct (as a unobservable side effect) a Nix derivation
-   expression that performs the derivation described by the argument
-   set.  Returns the original set extended with the following
-   attributes: `outPath' containing the primary output path of the
-   derivation; `drvPath' containing the path of the Nix expression;
-   and `type' set to `derivation' to indicate that this is a
-   derivation. */
-static void prim_derivationStrict(EvalState& state, const Pos& pos,
-                                  Value** args, Value& v) {
-  state.forceAttrs(*args[0], pos);
-
-  /* Figure out the name first (for stack backtraces). */
-  Bindings::iterator attr = args[0]->attrs->find(state.sName);
-  if (attr == args[0]->attrs->end()) {
-    throw EvalError(format("required attribute 'name' missing, at %1%") % pos);
-  }
-  std::string drvName;
-  Pos& posDrvName(*attr->second.pos);
-  try {
-    drvName = state.forceStringNoCtx(*attr->second.value, pos);
-  } catch (Error& e) {
-    e.addPrefix(
-        format("while evaluating the derivation attribute 'name' at %1%:\n") %
-        posDrvName);
-    throw;
-  }
-
-  /* Check whether attributes should be passed as a JSON file. */
-  std::ostringstream jsonBuf;
-  std::unique_ptr<JSONObject> jsonObject;
-  attr = args[0]->attrs->find(state.sStructuredAttrs);
-  if (attr != args[0]->attrs->end() &&
-      state.forceBool(*attr->second.value, pos)) {
-    jsonObject = std::make_unique<JSONObject>(jsonBuf);
-  }
-
-  /* Check whether null attributes should be ignored. */
-  bool ignoreNulls = false;
-  attr = args[0]->attrs->find(state.sIgnoreNulls);
-  if (attr != args[0]->attrs->end()) {
-    ignoreNulls = state.forceBool(*attr->second.value, pos);
-  }
-
-  /* Build the derivation expression by processing the attributes. */
-  Derivation drv;
-
-  PathSet context;
-
-  std::optional<std::string> outputHash;
-  std::string outputHashAlgo;
-  bool outputHashRecursive = false;
-
-  StringSet outputs;
-  outputs.insert("out");
-
-  for (auto& [_, i] : *args[0]->attrs) {
-    if (i.name == state.sIgnoreNulls) {
-      continue;
-    }
-    const std::string& key = i.name;
-
-    auto handleHashMode = [&](const std::string& s) {
-      if (s == "recursive") {
-        outputHashRecursive = true;
-      } else if (s == "flat") {
-        outputHashRecursive = false;
-      } else {
-        throw EvalError(
-            "invalid value '%s' for 'outputHashMode' attribute, at %s", s,
-            posDrvName);
-      }
-    };
-
-    auto handleOutputs = [&](const Strings& ss) {
-      outputs.clear();
-      for (auto& j : ss) {
-        if (outputs.find(j) != outputs.end()) {
-          throw EvalError(format("duplicate derivation output '%1%', at %2%") %
-                          j % posDrvName);
-        }
-        /* !!! Check whether j is a valid attribute
-           name. */
-        /* Derivations cannot be named β€˜drv’, because
-           then we'd have an attribute β€˜drvPath’ in
-           the resulting set. */
-        if (j == "drv") {
-          throw EvalError(
-              format("invalid derivation output name 'drv', at %1%") %
-              posDrvName);
-        }
-        outputs.insert(j);
-      }
-      if (outputs.empty()) {
-        throw EvalError(
-            format("derivation cannot have an empty set of outputs, at %1%") %
-            posDrvName);
-      }
-    };
-
-    try {
-      if (ignoreNulls) {
-        state.forceValue(*i.value);
-        if (i.value->type == tNull) {
-          continue;
-        }
-      }
-
-      /* The `args' attribute is special: it supplies the
-         command-line arguments to the builder. */
-      if (i.name == state.sArgs) {
-        state.forceList(*i.value, pos);
-        for (unsigned int n = 0; n < i.value->listSize(); ++n) {
-          std::string s = state.coerceToString(posDrvName, *(*i.value->list)[n],
-                                               context, true);
-          drv.args.push_back(s);
-        }
-      }
-
-      /* All other attributes are passed to the builder through
-         the environment. */
-      else {
-        if (jsonObject) {
-          if (i.name == state.sStructuredAttrs) {
-            continue;
-          }
-
-          auto placeholder(jsonObject->placeholder(key));
-          printValueAsJSON(state, true, *i.value, placeholder, context);
-
-          if (i.name == state.sBuilder) {
-            drv.builder = state.forceString(*i.value, context, posDrvName);
-          } else if (i.name == state.sSystem) {
-            drv.platform = state.forceStringNoCtx(*i.value, posDrvName);
-          } else if (i.name == state.sOutputHash) {
-            outputHash = state.forceStringNoCtx(*i.value, posDrvName);
-          } else if (i.name == state.sOutputHashAlgo) {
-            outputHashAlgo = state.forceStringNoCtx(*i.value, posDrvName);
-          } else if (i.name == state.sOutputHashMode) {
-            handleHashMode(state.forceStringNoCtx(*i.value, posDrvName));
-          } else if (i.name == state.sOutputs) {
-            /* Require β€˜outputs’ to be a list of strings. */
-            state.forceList(*i.value, posDrvName);
-            Strings ss;
-            for (unsigned int n = 0; n < i.value->listSize(); ++n) {
-              ss.emplace_back(
-                  state.forceStringNoCtx(*(*i.value->list)[n], posDrvName));
-            }
-            handleOutputs(ss);
-          }
-
-        } else {
-          auto s = state.coerceToString(posDrvName, *i.value, context, true);
-          drv.env.emplace(key, s);
-          if (i.name == state.sBuilder) {
-            drv.builder = s;
-          } else if (i.name == state.sSystem) {
-            drv.platform = s;
-          } else if (i.name == state.sOutputHash) {
-            outputHash = s;
-          } else if (i.name == state.sOutputHashAlgo) {
-            outputHashAlgo = s;
-          } else if (i.name == state.sOutputHashMode) {
-            handleHashMode(s);
-          } else if (i.name == state.sOutputs) {
-            handleOutputs(absl::StrSplit(s, absl::ByAnyChar(" \t\n\r"),
-                                         absl::SkipEmpty()));
-          }
-        }
-      }
-
-    } catch (Error& e) {
-      e.addPrefix(format("while evaluating the attribute '%1%' of the "
-                         "derivation '%2%' at %3%:\n") %
-                  key % drvName % posDrvName);
-      throw;
-    }
-  }
-
-  if (jsonObject) {
-    jsonObject.reset();
-    drv.env.emplace("__json", jsonBuf.str());
-  }
-
-  /* Everything in the context of the strings in the derivation
-     attributes should be added as dependencies of the resulting
-     derivation. */
-  for (auto& path : context) {
-    /* Paths marked with `=' denote that the path of a derivation
-       is explicitly passed to the builder.  Since that allows the
-       builder to gain access to every path in the dependency
-       graph of the derivation (including all outputs), all paths
-       in the graph must be added to this derivation's list of
-       inputs to ensure that they are available when the builder
-       runs. */
-    if (path.at(0) == '=') {
-      /* !!! This doesn't work if readOnlyMode is set. */
-      PathSet refs;
-      state.store->computeFSClosure(std::string(path, 1), refs);
-      for (auto& j : refs) {
-        drv.inputSrcs.insert(j);
-        if (isDerivation(j)) {
-          drv.inputDrvs[j] = state.store->queryDerivationOutputNames(j);
-        }
-      }
-    }
-
-    /* Handle derivation outputs of the form β€˜!<name>!<path>’. */
-    else if (path.at(0) == '!') {
-      std::pair<std::string, std::string> ctx = decodeContext(path);
-      drv.inputDrvs[ctx.first].insert(ctx.second);
-    }
-
-    /* Otherwise it's a source file. */
-    else {
-      drv.inputSrcs.insert(path);
-    }
-  }
-
-  /* Do we have all required attributes? */
-  if (drv.builder.empty()) {
-    throw EvalError(format("required attribute 'builder' missing, at %1%") %
-                    posDrvName);
-  }
-  if (drv.platform.empty()) {
-    throw EvalError(format("required attribute 'system' missing, at %1%") %
-                    posDrvName);
-  }
-
-  /* Check whether the derivation name is valid. */
-  checkStoreName(drvName);
-  if (isDerivation(drvName)) {
-    throw EvalError(
-        format("derivation names are not allowed to end in '%1%', at %2%") %
-        drvExtension % posDrvName);
-  }
-
-  if (outputHash) {
-    /* Handle fixed-output derivations. */
-    if (outputs.size() != 1 || *(outputs.begin()) != "out") {
-      throw Error(format("multiple outputs are not supported in fixed-output "
-                         "derivations, at %1%") %
-                  posDrvName);
-    }
-
-    HashType ht =
-        outputHashAlgo.empty() ? htUnknown : parseHashType(outputHashAlgo);
-    auto hash_ = Hash::deserialize(*outputHash, ht);
-    auto h = Hash::unwrap_throw(hash_);
-
-    Path outPath =
-        state.store->makeFixedOutputPath(outputHashRecursive, h, drvName);
-    if (!jsonObject) {
-      drv.env["out"] = outPath;
-    }
-    drv.outputs["out"] = DerivationOutput(
-        outPath, (outputHashRecursive ? "r:" : "") + printHashType(h.type),
-        h.to_string(Base16, false));
-  }
-
-  else {
-    /* Construct the "masked" store derivation, which is the final
-       one except that in the list of outputs, the output paths
-       are empty, and the corresponding environment variables have
-       an empty value.  This ensures that changes in the set of
-       output names do get reflected in the hash. */
-    for (auto& i : outputs) {
-      if (!jsonObject) {
-        drv.env[i] = "";
-      }
-      drv.outputs[i] = DerivationOutput("", "", "");
-    }
-
-    /* Use the masked derivation expression to compute the output
-       path. */
-    Hash h = hashDerivationModulo(*state.store, drv);
-
-    for (auto& i : drv.outputs) {
-      if (i.second.path.empty()) {
-        Path outPath = state.store->makeOutputPath(i.first, h, drvName);
-        if (!jsonObject) {
-          drv.env[i.first] = outPath;
-        }
-        i.second.path = outPath;
-      }
-    }
-  }
-
-  /* Write the resulting term into the Nix store directory. */
-  Path drvPath = writeDerivation(state.store, drv, drvName, state.repair);
-
-  VLOG(2) << "instantiated '" << drvName << "' -> '" << drvPath << "'";
-
-  /* Optimisation, but required in read-only mode! because in that
-     case we don't actually write store derivations, so we can't
-     read them later. */
-  drvHashes[drvPath] = hashDerivationModulo(*state.store, drv);
-
-  state.mkAttrs(v, 1 + drv.outputs.size());
-  mkString(*state.allocAttr(v, state.sDrvPath), drvPath, {"=" + drvPath});
-  for (auto& i : drv.outputs) {
-    mkString(*state.allocAttr(v, state.symbols.Create(i.first)), i.second.path,
-             {"!" + i.first + "!" + drvPath});
-  }
-}
-
-/* Return a placeholder string for the specified output that will be
-   substituted by the corresponding output path at build time. For
-   example, 'placeholder "out"' returns the string
-   /1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build
-   time, any occurence of this string in an derivation attribute will
-   be replaced with the concrete path in the Nix store of the output
-   β€˜out’. */
-static void prim_placeholder(EvalState& state, const Pos& pos, Value** args,
-                             Value& v) {
-  mkString(v, hashPlaceholder(state.forceStringNoCtx(*args[0], pos)));
-}
-
-/*************************************************************
- * Paths
- *************************************************************/
-
-/* Convert the argument to a path.  !!! obsolete? */
-static void prim_toPath(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  PathSet context;
-  Path path = state.coerceToPath(pos, *args[0], context);
-  mkString(v, canonPath(path), context);
-}
-
-/* Allow a valid store path to be used in an expression.  This is
-   useful in some generated expressions such as in nix-push, which
-   generates a call to a function with an already existing store path
-   as argument.  You don't want to use `toPath' here because it copies
-   the path to the Nix store, which yields a copy like
-   /nix/store/newhash-oldhash-oldname.  In the past, `toPath' had
-   special case behaviour for store paths, but that created weird
-   corner cases. */
-static void prim_storePath(EvalState& state, const Pos& pos, Value** args,
-                           Value& v) {
-  PathSet context;
-  Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context));
-  /* Resolve symlinks in β€˜path’, unless β€˜path’ itself is a symlink
-     directly in the store.  The latter condition is necessary so
-     e.g. nix-push does the right thing. */
-  if (!state.store->isStorePath(path)) {
-    path = canonPath(path, true);
-  }
-  if (!state.store->isInStore(path)) {
-    throw EvalError(format("path '%1%' is not in the Nix store, at %2%") %
-                    path % pos);
-  }
-  Path path2 = state.store->toStorePath(path);
-  if (!settings.readOnlyMode) {
-    state.store->ensurePath(path2);
-  }
-  context.insert(path2);
-  mkString(v, path, context);
-}
-
-static void prim_pathExists(EvalState& state, const Pos& pos, Value** args,
-                            Value& v) {
-  PathSet context;
-  Path path = state.coerceToPath(pos, *args[0], context);
-  try {
-    state.realiseContext(context);
-  } catch (InvalidPathError& e) {
-    throw EvalError(format("cannot check the existence of '%1%', since path "
-                           "'%2%' is not valid, at %3%") %
-                    path % e.path % pos);
-  }
-
-  try {
-    mkBool(v, pathExists(state.checkSourcePath(path)));
-  } catch (SysError& e) {
-    /* Don't give away info from errors while canonicalising
-       β€˜path’ in restricted mode. */
-    mkBool(v, false);
-  } catch (RestrictedPathError& e) {
-    mkBool(v, false);
-  }
-}
-
-/* Return the base name of the given string, i.e., everything
-   following the last slash. */
-static void prim_baseNameOf(EvalState& state, const Pos& pos, Value** args,
-                            Value& v) {
-  PathSet context;
-  mkString(
-      v, baseNameOf(state.coerceToString(pos, *args[0], context, false, false)),
-      context);
-}
-
-/* Return the directory of the given path, i.e., everything before the
-   last slash.  Return either a path or a string depending on the type
-   of the argument. */
-static void prim_dirOf(EvalState& state, const Pos& pos, Value** args,
-                       Value& v) {
-  PathSet context;
-  Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false));
-  if (args[0]->type == tPath) {
-    mkPath(v, dir.c_str());
-  } else {
-    mkString(v, dir, context);
-  }
-}
-
-/* Return the contents of a file as a string. */
-static void prim_readFile(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  PathSet context;
-  Path path = state.coerceToPath(pos, *args[0], context);
-  try {
-    state.realiseContext(context);
-  } catch (InvalidPathError& e) {
-    throw EvalError(
-        format("cannot read '%1%', since path '%2%' is not valid, at %3%") %
-        path % e.path % pos);
-  }
-  std::string s =
-      readFile(state.checkSourcePath(state.toRealPath(path, context)));
-  if (s.find(static_cast<char>(0)) != std::string::npos) {
-    throw Error(format("the contents of the file '%1%' cannot be represented "
-                       "as a Nix string") %
-                path);
-  }
-  mkString(v, s.c_str());
-}
-
-/* Find a file in the Nix search path. Used to implement <x> paths,
-   which are desugared to 'findFile __nixPath "x"'. */
-static void prim_findFile(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  state.forceList(*args[0], pos);
-
-  SearchPath searchPath;
-
-  for (unsigned int n = 0; n < args[0]->listSize(); ++n) {
-    Value& v2(*(*args[0]->list)[n]);
-    state.forceAttrs(v2, pos);
-
-    std::string prefix;
-    Bindings::iterator i = v2.attrs->find(state.symbols.Create("prefix"));
-    if (i != v2.attrs->end()) {
-      prefix = state.forceStringNoCtx(*i->second.value, pos);
-    }
-
-    i = v2.attrs->find(state.symbols.Create("path"));
-    if (i == v2.attrs->end()) {
-      throw EvalError(format("attribute 'path' missing, at %1%") % pos);
-    }
-
-    PathSet context;
-    std::string path =
-        state.coerceToString(pos, *i->second.value, context, false, false);
-
-    try {
-      state.realiseContext(context);
-    } catch (InvalidPathError& e) {
-      throw EvalError(
-          format("cannot find '%1%', since path '%2%' is not valid, at %3%") %
-          path % e.path % pos);
-    }
-
-    searchPath.emplace_back(prefix, path);
-  }
-
-  std::string path = state.forceStringNoCtx(*args[1], pos);
-
-  mkPath(v,
-         state.checkSourcePath(state.findFile(searchPath, path, pos)).c_str());
-}
-
-/* Return the cryptographic hash of a file in base-16. */
-static void prim_hashFile(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  std::string type = state.forceStringNoCtx(*args[0], pos);
-  HashType ht = parseHashType(type);
-  if (ht == htUnknown) {
-    throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
-  }
-
-  PathSet context;  // discarded
-  Path p = state.coerceToPath(pos, *args[1], context);
-
-  mkString(v, hashFile(ht, state.checkSourcePath(p)).to_string(Base16, false),
-           context);
-}
-
-/* Read a directory (without . or ..) */
-static void prim_readDir(EvalState& state, const Pos& pos, Value** args,
-                         Value& v) {
-  PathSet ctx;
-  Path path = state.coerceToPath(pos, *args[0], ctx);
-  try {
-    state.realiseContext(ctx);
-  } catch (InvalidPathError& e) {
-    throw EvalError(
-        format("cannot read '%1%', since path '%2%' is not valid, at %3%") %
-        path % e.path % pos);
-  }
-
-  DirEntries entries = readDirectory(state.checkSourcePath(path));
-  state.mkAttrs(v, entries.size());
-
-  for (auto& ent : entries) {
-    Value* ent_val = state.allocAttr(v, state.symbols.Create(ent.name));
-    if (ent.type == DT_UNKNOWN) {
-      ent.type = getFileType(path + "/" + ent.name);
-    }
-    mkStringNoCopy(*ent_val, ent.type == DT_REG   ? "regular"
-                             : ent.type == DT_DIR ? "directory"
-                             : ent.type == DT_LNK ? "symlink"
-                                                  : "unknown");
-  }
-}
-
-/*************************************************************
- * Creating files
- *************************************************************/
-
-/* Convert the argument (which can be any Nix expression) to an XML
-   representation returned in a string.  Not all Nix expressions can
-   be sensibly or completely represented (e.g., functions). */
-static void prim_toXML(EvalState& state, const Pos& pos, Value** args,
-                       Value& v) {
-  std::ostringstream out;
-  PathSet context;
-  printValueAsXML(state, true, false, *args[0], out, context);
-  mkString(v, out.str(), context);
-}
-
-/* Convert the argument (which can be any Nix expression) to a JSON
-   string.  Not all Nix expressions can be sensibly or completely
-   represented (e.g., functions). */
-static void prim_toJSON(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  std::ostringstream out;
-  PathSet context;
-  printValueAsJSON(state, true, *args[0], out, context);
-  mkString(v, out.str(), context);
-}
-
-/* Parse a JSON string to a value. */
-static void prim_fromJSON(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  std::string s = state.forceStringNoCtx(*args[0], pos);
-  parseJSON(state, s, v);
-}
-
-/* Store a string in the Nix store as a source file that can be used
-   as an input by derivations. */
-static void prim_toFile(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  PathSet context;
-  std::string name = state.forceStringNoCtx(*args[0], pos);
-  std::string contents = state.forceString(*args[1], context, pos);
-
-  PathSet refs;
-
-  for (auto path : context) {
-    if (path.at(0) != '/') {
-      throw EvalError(format("in 'toFile': the file '%1%' cannot refer to "
-                             "derivation outputs, at %2%") %
-                      name % pos);
-    }
-    refs.insert(path);
-  }
-
-  Path storePath =
-      settings.readOnlyMode
-          ? state.store->computeStorePathForText(name, contents, refs)
-          : state.store->addTextToStore(name, contents, refs, state.repair);
-
-  /* Note: we don't need to add `context' to the context of the
-     result, since `storePath' itself has references to the paths
-     used in args[1]. */
-
-  mkString(v, storePath, {storePath});
-}
-
-static void addPath(EvalState& state, const Pos& pos, const std::string& name,
-                    const Path& path_, Value* filterFun, bool recursive,
-                    const Hash& expectedHash, Value& v) {
-  const auto path = evalSettings.pureEval && expectedHash
-                        ? path_
-                        : state.checkSourcePath(path_);
-  PathFilter filter = filterFun != nullptr ? ([&](const Path& path) {
-    auto st = lstat(path);
-
-    /* Call the filter function.  The first argument is the path,
-       the second is a string indicating the type of the file. */
-    Value arg1;
-    mkString(arg1, path);
-
-    Value fun2;
-    state.callFunction(*filterFun, arg1, fun2, noPos);
-
-    Value arg2;
-    mkString(arg2, S_ISREG(st.st_mode)   ? "regular"
-                   : S_ISDIR(st.st_mode) ? "directory"
-                   : S_ISLNK(st.st_mode)
-                       ? "symlink"
-                       : "unknown" /* not supported, will fail! */);
-
-    Value res;
-    state.callFunction(fun2, arg2, res, noPos);
-
-    return state.forceBool(res, pos);
-  })
-                                           : defaultPathFilter;
-
-  Path expectedStorePath;
-  if (expectedHash) {
-    expectedStorePath =
-        state.store->makeFixedOutputPath(recursive, expectedHash, name);
-  }
-  Path dstPath;
-  if (!expectedHash || !state.store->isValidPath(expectedStorePath)) {
-    dstPath = settings.readOnlyMode
-                  ? state.store
-                        ->computeStorePathForPath(name, path, recursive,
-                                                  htSHA256, filter)
-                        .first
-                  : state.store->addToStore(name, path, recursive, htSHA256,
-                                            filter, state.repair);
-    if (expectedHash && expectedStorePath != dstPath) {
-      throw Error(format("store path mismatch in (possibly filtered) path "
-                         "added from '%1%'") %
-                  path);
-    }
-  } else {
-    dstPath = expectedStorePath;
-  }
-
-  mkString(v, dstPath, {dstPath});
-}
-
-static void prim_filterSource(EvalState& state, const Pos& pos, Value** args,
-                              Value& v) {
-  PathSet context;
-  Path path = state.coerceToPath(pos, *args[1], context);
-  if (!context.empty()) {
-    throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") %
-                    path % pos);
-  }
-
-  state.forceValue(*args[0]);
-  if (args[0]->type != tLambda) {
-    throw TypeError(format("first argument in call to 'filterSource' is not a "
-                           "function but %1%, at %2%") %
-                    showType(*args[0]) % pos);
-  }
-
-  addPath(state, pos, baseNameOf(path), path, args[0], true, Hash(), v);
-}
-
-static void prim_path(EvalState& state, const Pos& pos, Value** args,
-                      Value& v) {
-  state.forceAttrs(*args[0], pos);
-  Path path;
-  std::string name;
-  Value* filterFun = nullptr;
-  auto recursive = true;
-  Hash expectedHash;
-
-  for (auto& attr : *args[0]->attrs) {
-    const std::string& n(attr.second.name);
-    if (n == "path") {
-      PathSet context;
-      path = state.coerceToPath(*attr.second.pos, *attr.second.value, context);
-      if (!context.empty()) {
-        throw EvalError(
-            format("string '%1%' cannot refer to other paths, at %2%") % path %
-            *attr.second.pos);
-      }
-    } else if (attr.second.name == state.sName) {
-      name = state.forceStringNoCtx(*attr.second.value, *attr.second.pos);
-    } else if (n == "filter") {
-      state.forceValue(*attr.second.value);
-      filterFun = attr.second.value;
-    } else if (n == "recursive") {
-      recursive = state.forceBool(*attr.second.value, *attr.second.pos);
-    } else if (n == "sha256") {
-      auto hash_ = Hash::deserialize(
-          state.forceStringNoCtx(*attr.second.value, *attr.second.pos),
-          htSHA256);
-      expectedHash = Hash::unwrap_throw(hash_);
-    } else {
-      throw EvalError(
-          format("unsupported argument '%1%' to 'addPath', at %2%") %
-          attr.second.name % *attr.second.pos);
-    }
-  }
-  if (path.empty()) {
-    throw EvalError(format("'path' required, at %1%") % pos);
-  }
-  if (name.empty()) {
-    name = baseNameOf(path);
-  }
-
-  addPath(state, pos, name, path, filterFun, recursive, expectedHash, v);
-}
-
-/*************************************************************
- * Sets
- *************************************************************/
-
-/* Return the names of the attributes in a set as a sorted list of
-   strings. */
-static void prim_attrNames(EvalState& state, const Pos& pos, Value** args,
-                           Value& v) {
-  state.forceAttrs(*args[0], pos);
-
-  state.mkList(v, args[0]->attrs->size());
-
-  unsigned int n = 0;
-  for (auto& [key, value] : *args[0]->attrs) {
-    mkString(*((*v.list)[n++] = state.allocValue()), key);
-  }
-}
-
-/* Return the values of the attributes in a set as a list, in the same
-   order as attrNames. */
-static void prim_attrValues(EvalState& state, const Pos& pos, Value** input,
-                            Value& output) {
-  state.forceAttrs(*input[0], pos);
-
-  state.mkList(output, input[0]->attrs->size());
-
-  unsigned int n = 0;
-  for (auto& [key, value] : *input[0]->attrs) {
-    (*output.list)[n++] = value.value;
-  }
-}
-
-/* Dynamic version of the `.' operator. */
-void prim_getAttr(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  std::string attr = state.forceStringNoCtx(*args[0], pos);
-  state.forceAttrs(*args[1], pos);
-  // !!! Should we create a symbol here or just do a lookup?
-  Bindings::iterator i = args[1]->attrs->find(state.symbols.Create(attr));
-  if (i == args[1]->attrs->end()) {
-    throw EvalError(format("attribute '%1%' missing, at %2%") % attr % pos);
-  }
-  // !!! add to stack trace?
-  if (state.countCalls && (i->second.pos != nullptr)) {
-    state.attrSelects[*i->second.pos]++;
-  }
-  state.forceValue(*i->second.value);
-  v = *i->second.value;
-}
-
-/* Return position information of the specified attribute. */
-void prim_unsafeGetAttrPos(EvalState& state, const Pos& pos, Value** args,
-                           Value& v) {
-  std::string attr = state.forceStringNoCtx(*args[0], pos);
-  state.forceAttrs(*args[1], pos);
-  Bindings::iterator i = args[1]->attrs->find(state.symbols.Create(attr));
-  if (i == args[1]->attrs->end()) {
-    mkNull(v);
-  } else {
-    state.mkPos(v, i->second.pos);
-  }
-}
-
-/* Dynamic version of the `?' operator. */
-static void prim_hasAttr(EvalState& state, const Pos& pos, Value** args,
-                         Value& v) {
-  std::string attr = state.forceStringNoCtx(*args[0], pos);
-  state.forceAttrs(*args[1], pos);
-  mkBool(v, args[1]->attrs->find(state.symbols.Create(attr)) !=
-                args[1]->attrs->end());
-}
-
-/* Determine whether the argument is a set. */
-static void prim_isAttrs(EvalState& state, const Pos& pos, Value** args,
-                         Value& v) {
-  state.forceValue(*args[0]);
-  mkBool(v, args[0]->type == tAttrs);
-}
-
-static void prim_removeAttrs(EvalState& state, const Pos& pos, Value** args,
-                             Value& v) {
-  state.forceAttrs(*args[0], pos);
-  state.forceList(*args[1], pos);
-
-  /* Get the attribute names to be removed. */
-  std::set<Symbol> names;
-  for (unsigned int i = 0; i < args[1]->listSize(); ++i) {
-    state.forceStringNoCtx(*(*args[1]->list)[i], pos);
-    names.insert(state.symbols.Create((*args[1]->list)[i]->string.s));
-  }
-
-  /* Copy all attributes not in that set.  Note that we don't need
-     to sort v.attrs because it's a subset of an already sorted
-     vector. */
-  state.mkAttrs(v, args[0]->attrs->size());
-  for (auto& i : *args[0]->attrs) {
-    if (names.find(i.second.name) == names.end()) {
-      v.attrs->push_back(i.second);
-    }
-  }
-}
-
-/* Builds a set from a list specifying (name, value) pairs.  To be
-   precise, a list [{name = "name1"; value = value1;} ... {name =
-   "nameN"; value = valueN;}] is transformed to {name1 = value1;
-   ... nameN = valueN;}.  In case of duplicate occurences of the same
-   name, the first takes precedence. */
-static void prim_listToAttrs(EvalState& state, const Pos& pos, Value** args,
-                             Value& v) {
-  state.forceList(*args[0], pos);
-
-  state.mkAttrs(v, args[0]->listSize());
-
-  std::set<Symbol> seen;
-
-  for (unsigned int i = 0; i < args[0]->listSize(); ++i) {
-    Value& v2(*(*args[0]->list)[i]);
-    state.forceAttrs(v2, pos);
-
-    Bindings::iterator j = v2.attrs->find(state.sName);
-    if (j == v2.attrs->end()) {
-      throw TypeError(
-          format(
-              "'name' attribute missing in a call to 'listToAttrs', at %1%") %
-          pos);
-    }
-    std::string name = state.forceStringNoCtx(*j->second.value, pos);
-
-    Symbol sym = state.symbols.Create(name);
-    if (seen.find(sym) == seen.end()) {
-      Bindings::iterator j2 =
-          // TODO(tazjin): this line used to construct the symbol again:
-          // state.symbols.Create(state.sValue));
-          // Why?
-          v2.attrs->find(state.sValue);
-      if (j2 == v2.attrs->end()) {
-        throw TypeError(format("'value' attribute missing in a call to "
-                               "'listToAttrs', at %1%") %
-                        pos);
-      }
-
-      v.attrs->push_back(Attr(sym, j2->second.value, j2->second.pos));
-      seen.insert(sym);
-    }
-  }
-}
-
-/* Return the right-biased intersection of two sets as1 and as2,
-   i.e. a set that contains every attribute from as2 that is also a
-   member of as1. */
-static void prim_intersectAttrs(EvalState& state, const Pos& pos, Value** args,
-                                Value& v) {
-  state.forceAttrs(*args[0], pos);
-  state.forceAttrs(*args[1], pos);
-
-  state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size()));
-
-  for (auto& i : *args[0]->attrs) {
-    Bindings::iterator j = args[1]->attrs->find(i.second.name);
-    if (j != args[1]->attrs->end()) {
-      v.attrs->push_back(j->second);
-    }
-  }
-}
-
-/* Collect each attribute named `attr' from a list of attribute sets.
-   Sets that don't contain the named attribute are ignored.
-
-   Example:
-     catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}]
-     => [1 2]
-*/
-static void prim_catAttrs(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  Symbol attrName = state.symbols.Create(state.forceStringNoCtx(*args[0], pos));
-  state.forceList(*args[1], pos);
-
-  Value* res[args[1]->listSize()];
-  unsigned int found = 0;
-
-  for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
-    Value& v2(*(*args[1]->list)[n]);
-    state.forceAttrs(v2, pos);
-    Bindings::iterator i = v2.attrs->find(attrName);
-    if (i != v2.attrs->end()) {
-      res[found++] = i->second.value;
-    }
-  }
-
-  state.mkList(v, found);
-  for (unsigned int n = 0; n < found; ++n) {
-    (*v.list)[n] = res[n];
-  }
-}
-
-/* Return a set containing the names of the formal arguments expected
-   by the function `f'.  The value of each attribute is a Boolean
-   denoting whether the corresponding argument has a default value.  For
-   instance,
-
-      functionArgs ({ x, y ? 123}: ...)
-   => { x = false; y = true; }
-
-   "Formal argument" here refers to the attributes pattern-matched by
-   the function.  Plain lambdas are not included, e.g.
-
-      functionArgs (x: ...)
-   => { }
-*/
-static void prim_functionArgs(EvalState& state, const Pos& pos, Value** args,
-                              Value& v) {
-  state.forceValue(*args[0]);
-  if (args[0]->type == tPrimOpApp || args[0]->type == tPrimOp) {
-    // TODO(sterni): return set of formal arguments for fetch* primops
-    state.mkAttrs(v, 0);
-    return;
-  }
-  if (args[0]->type != tLambda) {
-    throw TypeError(format("'functionArgs' requires a function, at %1%") % pos);
-  }
-
-  if (!args[0]->lambda.fun->matchAttrs) {
-    state.mkAttrs(v, 0);
-    return;
-  }
-
-  state.mkAttrs(v, args[0]->lambda.fun->formals->formals.size());
-  for (auto& i : args[0]->lambda.fun->formals->formals) {
-    // !!! should optimise booleans (allocate only once)
-    // TODO(tazjin): figure out what the above comment means
-    mkBool(*state.allocAttr(v, i.name), i.def != nullptr);
-  }
-}
-
-/* Apply a function to every element of an attribute set. */
-static void prim_mapAttrs(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  state.forceAttrs(*args[1], pos);
-
-  state.mkAttrs(v, args[1]->attrs->size());
-
-  for (auto& i : *args[1]->attrs) {
-    Value* vName = state.allocValue();
-    Value* vFun2 = state.allocValue();
-    mkString(*vName, i.second.name);
-    mkApp(*vFun2, *args[0], *vName);
-    mkApp(*state.allocAttr(v, i.second.name), *vFun2, *i.second.value);
-  }
-}
-
-/*************************************************************
- * Lists
- *************************************************************/
-
-/* Determine whether the argument is a list. */
-static void prim_isList(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  state.forceValue(*args[0]);
-  mkBool(v, args[0]->isList());
-}
-
-static void elemAt(EvalState& state, const Pos& pos, Value& list, int n,
-                   Value& v) {
-  state.forceList(list, pos);
-  if (n < 0 || static_cast<unsigned int>(n) >= list.listSize()) {
-    throw Error(format("list index %1% is out of bounds, at %2%") % n % pos);
-  }
-  state.forceValue(*(*list.list)[n]);
-  v = *(*list.list)[n];
-}
-
-/* Return the n-1'th element of a list. */
-static void prim_elemAt(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  elemAt(state, pos, *args[0], state.forceInt(*args[1], pos), v);
-}
-
-/* Return the first element of a list. */
-static void prim_head(EvalState& state, const Pos& pos, Value** args,
-                      Value& v) {
-  elemAt(state, pos, *args[0], 0, v);
-}
-
-/* Return a list consisting of everything but the first element of
-   a list.  Warning: this function takes O(n) time, so you probably
-   don't want to use it!  */
-static void prim_tail(EvalState& state, const Pos& pos, Value** args,
-                      Value& v) {
-  state.forceList(*args[0], pos);
-  if (args[0]->listSize() == 0) {
-    throw Error(format("'tail' called on an empty list, at %1%") % pos);
-  }
-  state.mkList(v, args[0]->listSize() - 1);
-  for (unsigned int n = 0; n < v.listSize(); ++n) {
-    (*v.list)[n] = (*args[0]->list)[n + 1];
-  }
-}
-
-/* Apply a function to every element of a list. */
-static void prim_map(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  state.forceList(*args[1], pos);
-
-  state.mkList(v, args[1]->listSize());
-
-  for (unsigned int n = 0; n < v.listSize(); ++n) {
-    mkApp(*((*v.list)[n] = state.allocValue()), *args[0], *(*args[1]->list)[n]);
-  }
-}
-
-/* Filter a list using a predicate; that is, return a list containing
-   every element from the list for which the predicate function
-   returns true. */
-static void prim_filter(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  state.forceFunction(*args[0], pos);
-  state.forceList(*args[1], pos);
-
-  // FIXME: putting this on the stack is risky.
-  Value* vs[args[1]->listSize()];
-  unsigned int k = 0;
-
-  bool same = true;
-  for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
-    Value res;
-    state.callFunction(*args[0], *(*args[1]->list)[n], res, noPos);
-    if (state.forceBool(res, pos)) {
-      vs[k++] = (*args[1]->list)[n];
-    } else {
-      same = false;
-    }
-  }
-
-  if (same) {
-    v = *args[1];
-  } else {
-    state.mkList(v, k);
-    for (unsigned int n = 0; n < k; ++n) {
-      (*v.list)[n] = vs[n];
-    }
-  }
-}
-
-/* Return true if a list contains a given element. */
-static void prim_elem(EvalState& state, const Pos& pos, Value** args,
-                      Value& v) {
-  bool res = false;
-  state.forceList(*args[1], pos);
-  for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
-    if (state.eqValues(*args[0], *(*args[1]->list)[n])) {
-      res = true;
-      break;
-    }
-  }
-  mkBool(v, res);
-}
-
-/* Concatenate a list of lists. */
-static void prim_concatLists(EvalState& state, const Pos& pos, Value** args,
-                             Value& v) {
-  state.forceList(*args[0], pos);
-  state.concatLists(v, *args[0]->list, pos);
-}
-
-/* Return the length of a list.  This is an O(1) time operation. */
-static void prim_length(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  state.forceList(*args[0], pos);
-  mkInt(v, args[0]->listSize());
-}
-
-/* Reduce a list by applying a binary operator, from left to
-   right. The operator is applied strictly. */
-static void prim_foldlStrict(EvalState& state, const Pos& pos, Value** args,
-                             Value& v) {
-  state.forceFunction(*args[0], pos);
-  state.forceList(*args[2], pos);
-
-  if (args[2]->listSize() != 0u) {
-    Value* vCur = args[1];
-
-    for (unsigned int n = 0; n < args[2]->listSize(); ++n) {
-      Value vTmp;
-      state.callFunction(*args[0], *vCur, vTmp, pos);
-      vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
-      state.callFunction(vTmp, *(*args[2]->list)[n], *vCur, pos);
-    }
-    state.forceValue(v);
-  } else {
-    state.forceValue(*args[1]);
-    v = *args[1];
-  }
-}
-
-static void anyOrAll(bool any, EvalState& state, const Pos& pos, Value** args,
-                     Value& v) {
-  state.forceFunction(*args[0], pos);
-  state.forceList(*args[1], pos);
-
-  Value vTmp;
-  for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
-    state.callFunction(*args[0], *(*args[1]->list)[n], vTmp, pos);
-    bool res = state.forceBool(vTmp, pos);
-    if (res == any) {
-      mkBool(v, any);
-      return;
-    }
-  }
-
-  mkBool(v, !any);
-}
-
-static void prim_any(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  anyOrAll(true, state, pos, args, v);
-}
-
-static void prim_all(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  anyOrAll(false, state, pos, args, v);
-}
-
-static void prim_genList(EvalState& state, const Pos& pos, Value** args,
-                         Value& v) {
-  auto len = state.forceInt(*args[1], pos);
-
-  if (len < 0) {
-    throw EvalError(format("cannot create list of size %1%, at %2%") % len %
-                    pos);
-  }
-
-  state.mkList(v, len);
-
-  for (unsigned int n = 0; n < static_cast<unsigned int>(len); ++n) {
-    Value* arg = state.allocValue();
-    mkInt(*arg, n);
-    mkApp(*((*v.list)[n] = state.allocValue()), *args[0], *arg);
-  }
-}
-
-static void prim_lessThan(EvalState& state, const Pos& pos, Value** args,
-                          Value& v);
-
-static void prim_sort(EvalState& state, const Pos& pos, Value** args,
-                      Value& v) {
-  state.forceFunction(*args[0], pos);
-  state.forceList(*args[1], pos);
-
-  // Copy of the input list which can be sorted in place.
-  v.type = tList;
-  v.list = std::make_shared<NixList>(*args[1]->list);
-
-  std::for_each(v.list->begin(), v.list->end(),
-                [&](Value* val) { state.forceValue(*val); });
-
-  auto comparator = [&](Value* a, Value* b) {
-    /* Optimization: if the comparator is lessThan, bypass
-       callFunction. */
-    if (args[0]->type == tPrimOp && args[0]->primOp->fun == prim_lessThan) {
-      return CompareValues()(a, b);
-    }
-
-    Value vTmp1{};
-    Value vTmp2{};
-    state.callFunction(*args[0], *a, vTmp1, pos);
-    state.callFunction(vTmp1, *b, vTmp2, pos);
-    return state.forceBool(vTmp2, pos);
-  };
-
-  /* FIXME: std::sort can segfault if the comparator is not a strict
-     weak ordering. What to do? std::stable_sort() seems more
-     resilient, but no guarantees... */
-  std::stable_sort(v.list->begin(), v.list->end(), comparator);
-}
-
-static void prim_partition(EvalState& state, const Pos& pos, Value** args,
-                           Value& v) {
-  state.forceFunction(*args[0], pos);
-  state.forceList(*args[1], pos);
-
-  std::shared_ptr<NixList> right = std::make_shared<NixList>();
-  std::shared_ptr<NixList> wrong = std::make_shared<NixList>();
-
-  for (Value* elem : *args[1]->list) {
-    state.forceValue(*elem, pos);
-
-    Value res;
-    state.callFunction(*args[0], *elem, res, pos);
-    if (state.forceBool(res, pos)) {
-      right->push_back(elem);
-    } else {
-      wrong->push_back(elem);
-    }
-  }
-
-  state.mkAttrs(v, 2);
-
-  Value* vRight = state.allocAttr(v, state.sRight);
-  state.mkList(*vRight, right);
-
-  Value* vWrong = state.allocAttr(v, state.sWrong);
-  state.mkList(*vWrong, wrong);
-}
-
-/* concatMap = f: list: concatLists (map f list); */
-/* C++-version is to avoid allocating `mkApp', call `f' eagerly */
-static void prim_concatMap(EvalState& state, const Pos& pos, Value** args,
-                           Value& v) {
-  state.forceFunction(*args[0], pos);
-  state.forceList(*args[1], pos);
-
-  std::shared_ptr<NixList> outlist = std::make_shared<NixList>();
-
-  for (Value* elem : *args[1]->list) {
-    auto out = state.allocValue();
-    state.callFunction(*args[0], *elem, *out, pos);
-    state.forceList(*out, pos);
-
-    outlist->insert(outlist->end(), out->list->begin(), out->list->end());
-  }
-
-  state.mkList(v, outlist);
-}
-
-/*************************************************************
- * Integer arithmetic
- *************************************************************/
-
-static void prim_add(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  state.forceValue(*args[0], pos);
-  state.forceValue(*args[1], pos);
-  if (args[0]->type == tFloat || args[1]->type == tFloat) {
-    mkFloat(v,
-            state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos));
-  } else {
-    mkInt(v, state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos));
-  }
-}
-
-static void prim_sub(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  state.forceValue(*args[0], pos);
-  state.forceValue(*args[1], pos);
-  if (args[0]->type == tFloat || args[1]->type == tFloat) {
-    mkFloat(v,
-            state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos));
-  } else {
-    mkInt(v, state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos));
-  }
-}
-
-static void prim_mul(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  state.forceValue(*args[0], pos);
-  state.forceValue(*args[1], pos);
-  if (args[0]->type == tFloat || args[1]->type == tFloat) {
-    mkFloat(v,
-            state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos));
-  } else {
-    mkInt(v, state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos));
-  }
-}
-
-static void prim_div(EvalState& state, const Pos& pos, Value** args, Value& v) {
-  state.forceValue(*args[0], pos);
-  state.forceValue(*args[1], pos);
-
-  NixFloat f2 = state.forceFloat(*args[1], pos);
-  if (f2 == 0) {
-    throw EvalError(format("division by zero, at %1%") % pos);
-  }
-
-  if (args[0]->type == tFloat || args[1]->type == tFloat) {
-    mkFloat(v,
-            state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
-  } else {
-    NixInt i1 = state.forceInt(*args[0], pos);
-    NixInt i2 = state.forceInt(*args[1], pos);
-    /* Avoid division overflow as it might raise SIGFPE. */
-    if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1) {
-      throw EvalError(format("overflow in integer division, at %1%") % pos);
-    }
-    mkInt(v, i1 / i2);
-  }
-}
-
-static void prim_bitAnd(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  mkInt(v, state.forceInt(*args[0], pos) & state.forceInt(*args[1], pos));
-}
-
-static void prim_bitOr(EvalState& state, const Pos& pos, Value** args,
-                       Value& v) {
-  mkInt(v, state.forceInt(*args[0], pos) | state.forceInt(*args[1], pos));
-}
-
-static void prim_bitXor(EvalState& state, const Pos& pos, Value** args,
-                        Value& v) {
-  mkInt(v, state.forceInt(*args[0], pos) ^ state.forceInt(*args[1], pos));
-}
-
-static void prim_lessThan(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  state.forceValue(*args[0]);
-  state.forceValue(*args[1]);
-  CompareValues comp;
-  mkBool(v, comp(args[0], args[1]));
-}
-
-/*************************************************************
- * String manipulation
- *************************************************************/
-
-/* Convert the argument to a string.  Paths are *not* copied to the
-   store, so `toString /foo/bar' yields `"/foo/bar"', not
-   `"/nix/store/whatever..."'. */
-static void prim_toString(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  PathSet context;
-  std::string s = state.coerceToString(pos, *args[0], context, true, false);
-  mkString(v, s, context);
-}
-
-/* `substring start len str' returns the substring of `str' starting
-   at character position `min(start, stringLength str)' inclusive and
-   ending at `min(start + len, stringLength str)'.  `start' must be
-   non-negative. */
-static void prim_substring(EvalState& state, const Pos& pos, Value** args,
-                           Value& v) {
-  int start = state.forceInt(*args[0], pos);
-  int len = state.forceInt(*args[1], pos);
-  PathSet context;
-  std::string s = state.coerceToString(pos, *args[2], context);
-
-  if (start < 0) {
-    throw EvalError(format("negative start position in 'substring', at %1%") %
-                    pos);
-  }
-
-  mkString(v,
-           static_cast<unsigned int>(start) >= s.size()
-               ? ""
-               : std::string(s, start, len),
-           context);
-}
-
-static void prim_stringLength(EvalState& state, const Pos& pos, Value** args,
-                              Value& v) {
-  PathSet context;
-  std::string s = state.coerceToString(pos, *args[0], context);
-  mkInt(v, s.size());
-}
-
-/* Return the cryptographic hash of a string in base-16. */
-static void prim_hashString(EvalState& state, const Pos& pos, Value** args,
-                            Value& v) {
-  std::string type = state.forceStringNoCtx(*args[0], pos);
-  HashType ht = parseHashType(type);
-  if (ht == htUnknown) {
-    throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
-  }
-
-  PathSet context;  // discarded
-  std::string s = state.forceString(*args[1], context, pos);
-
-  mkString(v, hashString(ht, s).to_string(Base16, false), context);
-}
-
-/* Match a regular expression against a string and return either
-   β€˜null’ or a list containing substring matches. */
-static void prim_match(EvalState& state, const Pos& pos, Value** args,
-                       Value& v) {
-  auto re = state.forceStringNoCtx(*args[0], pos);
-
-  try {
-    std::regex regex(re, std::regex::extended);
-
-    PathSet context;
-    const std::string str = state.forceString(*args[1], context, pos);
-
-    std::smatch match;
-    if (!std::regex_match(str, match, regex)) {
-      mkNull(v);
-      return;
-    }
-
-    // the first match is the whole string
-    const size_t len = match.size() - 1;
-    state.mkList(v, len);
-    for (size_t i = 0; i < len; ++i) {
-      if (!match[i + 1].matched) {
-        mkNull(*((*v.list)[i] = state.allocValue()));
-      } else {
-        mkString(*((*v.list)[i] = state.allocValue()),
-                 match[i + 1].str().c_str());
-      }
-    }
-
-  } catch (std::regex_error& e) {
-    if (e.code() == std::regex_constants::error_space) {
-      // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
-      throw EvalError("memory limit exceeded by regular expression '%s', at %s",
-                      re, pos);
-    }
-    throw EvalError("invalid regular expression '%s', at %s", re, pos);
-  }
-}
-
-/* Split a std::string with a regular expression, and return a list of the
-   non-matching parts interleaved by the lists of the matching groups. */
-static void prim_split(EvalState& state, const Pos& pos, Value** args,
-                       Value& v) {
-  auto re = state.forceStringNoCtx(*args[0], pos);
-
-  try {
-    std::regex regex(re, std::regex::extended);
-
-    PathSet context;
-    const std::string str = state.forceString(*args[1], context, pos);
-
-    auto begin = std::sregex_iterator(str.begin(), str.end(), regex);
-    auto end = std::sregex_iterator();
-
-    // Any matches results are surrounded by non-matching results.
-    const size_t len = std::distance(begin, end);
-    state.mkList(v, 2 * len + 1);
-    size_t idx = 0;
-    Value* elem;
-
-    if (len == 0) {
-      (*v.list)[idx++] = args[1];
-      return;
-    }
-
-    for (std::sregex_iterator i = begin; i != end; ++i) {
-      assert(idx <= 2 * len + 1 - 3);
-      std::smatch match = *i;
-
-      // Add a string for non-matched characters.
-      elem = (*v.list)[idx++] = state.allocValue();
-      mkString(*elem, match.prefix().str().c_str());
-
-      // Add a list for matched substrings.
-      const size_t slen = match.size() - 1;
-      elem = (*v.list)[idx++] = state.allocValue();
-
-      // Start at 1, beacause the first match is the whole string.
-      state.mkList(*elem, slen);
-      for (size_t si = 0; si < slen; ++si) {
-        if (!match[si + 1].matched) {
-          mkNull(*((*elem->list)[si] = state.allocValue()));
-        } else {
-          mkString(*((*elem->list)[si] = state.allocValue()),
-                   match[si + 1].str().c_str());
-        }
-      }
-
-      // Add a string for non-matched suffix characters.
-      if (idx == 2 * len) {
-        elem = (*v.list)[idx++] = state.allocValue();
-        mkString(*elem, match.suffix().str().c_str());
-      }
-    }
-    assert(idx == 2 * len + 1);
-
-  } catch (std::regex_error& e) {
-    if (e.code() == std::regex_constants::error_space) {
-      // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
-      throw EvalError("memory limit exceeded by regular expression '%s', at %s",
-                      re, pos);
-    }
-    throw EvalError("invalid regular expression '%s', at %s", re, pos);
-  }
-}
-
-static void prim_concatStringSep(EvalState& state, const Pos& pos, Value** args,
-                                 Value& v) {
-  PathSet context;
-
-  auto sep = state.forceString(*args[0], context, pos);
-  state.forceList(*args[1], pos);
-
-  std::string res;
-  res.reserve((args[1]->listSize() + 32) * sep.size());
-  bool first = true;
-
-  for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
-    if (first) {
-      first = false;
-    } else {
-      res += sep;
-    }
-
-    res += state.coerceToString(pos, *(*args[1]->list)[n], context);
-  }
-
-  mkString(v, res, context);
-}
-
-static void prim_replaceStrings(EvalState& state, const Pos& pos, Value** args,
-                                Value& v) {
-  state.forceList(*args[0], pos);
-  state.forceList(*args[1], pos);
-  if (args[0]->listSize() != args[1]->listSize()) {
-    throw EvalError(format("'from' and 'to' arguments to 'replaceStrings' have "
-                           "different lengths, at %1%") %
-                    pos);
-  }
-
-  std::vector<std::string> from;
-  from.reserve(args[0]->listSize());
-  for (unsigned int n = 0; n < args[0]->listSize(); ++n) {
-    from.push_back(state.forceString(*(*args[0]->list)[n], pos));
-  }
-
-  std::vector<std::pair<std::string, PathSet>> to;
-  to.reserve(args[1]->listSize());
-  for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
-    PathSet ctx;
-    auto s = state.forceString(*(*args[1]->list)[n], ctx, pos);
-    to.emplace_back(std::move(s), std::move(ctx));
-  }
-
-  PathSet context;
-  auto s = state.forceString(*args[2], context, pos);
-
-  std::string res;
-  // Loops one past last character to handle the case where 'from' contains an
-  // empty string.
-  for (size_t p = 0; p <= s.size();) {
-    bool found = false;
-    auto i = from.begin();
-    auto j = to.begin();
-    for (; i != from.end(); ++i, ++j) {
-      if (s.compare(p, i->size(), *i) == 0) {
-        found = true;
-        res += j->first;
-        if (i->empty()) {
-          if (p < s.size()) {
-            res += s[p];
-          }
-          p++;
-        } else {
-          p += i->size();
-        }
-        for (auto& path : j->second) {
-          context.insert(path);
-        }
-        j->second.clear();
-        break;
-      }
-    }
-    if (!found) {
-      if (p < s.size()) {
-        res += s[p];
-      }
-      p++;
-    }
-  }
-
-  mkString(v, res, context);
-}
-
-/*************************************************************
- * Versions
- *************************************************************/
-
-static void prim_parseDrvName(EvalState& state, const Pos& pos, Value** args,
-                              Value& v) {
-  std::string name = state.forceStringNoCtx(*args[0], pos);
-  DrvName parsed(name);
-  state.mkAttrs(v, 2);
-  mkString(*state.allocAttr(v, state.sName), parsed.name);
-  mkString(*state.allocAttr(v, state.symbols.Create("version")),
-           parsed.version);
-}
-
-static void prim_compareVersions(EvalState& state, const Pos& pos, Value** args,
-                                 Value& v) {
-  std::string version1 = state.forceStringNoCtx(*args[0], pos);
-  std::string version2 = state.forceStringNoCtx(*args[1], pos);
-  mkInt(v, compareVersions(version1, version2));
-}
-
-static void prim_splitVersion(EvalState& state, const Pos& pos, Value** args,
-                              Value& v) {
-  std::string version = state.forceStringNoCtx(*args[0], pos);
-  auto iter = version.cbegin();
-  Strings components;
-  while (iter != version.cend()) {
-    auto component = nextComponent(iter, version.cend());
-    if (component.empty()) {
-      break;
-    }
-    components.emplace_back(std::move(component));
-  }
-  state.mkList(v, components.size());
-  unsigned int n = 0;
-  for (auto& component : components) {
-    auto listElem = (*v.list)[n++] = state.allocValue();
-    mkString(*listElem, component);
-  }
-}
-
-/*************************************************************
- * Networking
- *************************************************************/
-
-void fetch(EvalState& state, const Pos& pos, Value** args, Value& v,
-           const std::string& who, bool unpack,
-           const std::string& defaultName) {
-  CachedDownloadRequest request("");
-  request.unpack = unpack;
-  request.name = defaultName;
-
-  state.forceValue(*args[0]);
-
-  if (args[0]->type == tAttrs) {
-    state.forceAttrs(*args[0], pos);
-
-    for (auto& attr : *args[0]->attrs) {
-      std::string n(attr.second.name);
-      if (n == "url") {
-        request.uri =
-            state.forceStringNoCtx(*attr.second.value, *attr.second.pos);
-      } else if (n == "sha256") {
-        auto hash_ = Hash::deserialize(
-            state.forceStringNoCtx(*attr.second.value, *attr.second.pos),
-            htSHA256);
-        request.expectedHash = Hash::unwrap_throw(hash_);
-      } else if (n == "name") {
-        request.name =
-            state.forceStringNoCtx(*attr.second.value, *attr.second.pos);
-      } else {
-        throw EvalError(format("unsupported argument '%1%' to '%2%', at %3%") %
-                        attr.second.name % who % attr.second.pos);
-      }
-    }
-
-    if (request.uri.empty()) {
-      throw EvalError(format("'url' argument required, at %1%") % pos);
-    }
-
-  } else {
-    request.uri = state.forceStringNoCtx(*args[0], pos);
-  }
-
-  state.checkURI(request.uri);
-
-  if (evalSettings.pureEval && !request.expectedHash) {
-    throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument",
-                who);
-  }
-
-  auto res = getDownloader()->downloadCached(state.store, request);
-
-  if (state.allowedPaths) {
-    state.allowedPaths->insert(res.path);
-  }
-
-  mkString(v, res.storePath, PathSet({res.storePath}));
-}
-
-static void prim_fetchurl(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  fetch(state, pos, args, v, "fetchurl", false, "");
-}
-
-static void prim_fetchTarball(EvalState& state, const Pos& pos, Value** args,
-                              Value& v) {
-  fetch(state, pos, args, v, "fetchTarball", true, "source");
-}
-
-/*************************************************************
- * Primop registration
- *************************************************************/
-
-RegisterPrimOp::PrimOps* RegisterPrimOp::primOps;
-
-RegisterPrimOp::RegisterPrimOp(const std::string& name, size_t arity,
-                               PrimOpFun fun) {
-  if (primOps == nullptr) {
-    primOps = new PrimOps;
-  }
-  primOps->emplace_back(name, arity, fun);
-}
-
-void EvalState::createBaseEnv() {
-  baseEnv.up = nullptr;
-
-  /* Add global constants such as `true' to the base environment. */
-  Value v;
-
-  /* `builtins' must be first! */
-  mkAttrs(v, 128);
-  addConstant("builtins", v);
-
-  mkBool(v, true);
-  addConstant("true", v);
-
-  mkBool(v, false);
-  addConstant("false", v);
-
-  mkNull(v);
-  addConstant("null", v);
-
-  auto vThrow = addPrimOp("throw", 1, prim_throw);
-
-  auto addPurityError = [&](const std::string& name) {
-    Value* v2 = allocValue();
-    mkString(*v2, fmt("'%s' is not allowed in pure evaluation mode", name));
-    mkApp(v, *vThrow, *v2);
-    addConstant(name, v);
-  };
-
-  if (!evalSettings.pureEval) {
-    mkInt(v, time(nullptr));
-    addConstant("__currentTime", v);
-  }
-
-  if (!evalSettings.pureEval) {
-    mkString(v, settings.thisSystem);
-    addConstant("__currentSystem", v);
-  }
-
-  mkString(v, nixVersion);
-  addConstant("__nixVersion", v);
-
-  mkString(v, store->storeDir);
-  addConstant("__storeDir", v);
-
-  /* Language version.  This should be increased every time a new
-     language feature gets added.  It's not necessary to increase it
-     when primops get added, because you can just use `builtins ?
-     primOp' to check. */
-  mkInt(v, 5);
-  addConstant("__langVersion", v);
-
-  // Miscellaneous
-  auto vScopedImport = addPrimOp("scopedImport", 2, prim_scopedImport);
-  Value* v2 = allocValue();
-  mkAttrs(*v2, 0);
-  mkApp(v, *vScopedImport, *v2);
-  forceValue(v);
-  addConstant("import", v);
-  addPrimOp("__typeOf", 1, prim_typeOf);
-  addPrimOp("isNull", 1, prim_isNull);
-  addPrimOp("__isFunction", 1, prim_isFunction);
-  addPrimOp("__isString", 1, prim_isString);
-  addPrimOp("__isInt", 1, prim_isInt);
-  addPrimOp("__isFloat", 1, prim_isFloat);
-  addPrimOp("__isBool", 1, prim_isBool);
-  addPrimOp("__isPath", 1, prim_isPath);
-  addPrimOp("__genericClosure", 1, prim_genericClosure);
-  addPrimOp("abort", 1, prim_abort);
-  addPrimOp("__addErrorContext", 2, prim_addErrorContext);
-  addPrimOp("__tryEval", 1, prim_tryEval);
-  addPrimOp("__getEnv", 1, prim_getEnv);
-
-  // Strictness
-  addPrimOp("__seq", 2, prim_seq);
-  addPrimOp("__deepSeq", 2, prim_deepSeq);
-
-  // Debugging
-  addPrimOp("__trace", 2, prim_trace);
-  addPrimOp("__valueSize", 1, prim_valueSize);
-
-  // Paths
-  addPrimOp("__toPath", 1, prim_toPath);
-  if (evalSettings.pureEval) {
-    addPurityError("__storePath");
-  } else {
-    addPrimOp("__storePath", 1, prim_storePath);
-  }
-  addPrimOp("__pathExists", 1, prim_pathExists);
-  addPrimOp("baseNameOf", 1, prim_baseNameOf);
-  addPrimOp("dirOf", 1, prim_dirOf);
-  addPrimOp("__readFile", 1, prim_readFile);
-  addPrimOp("__readDir", 1, prim_readDir);
-  addPrimOp("__findFile", 2, prim_findFile);
-  addPrimOp("__hashFile", 2, prim_hashFile);
-
-  // Creating files
-  addPrimOp("__toXML", 1, prim_toXML);
-  addPrimOp("__toJSON", 1, prim_toJSON);
-  addPrimOp("__fromJSON", 1, prim_fromJSON);
-  addPrimOp("__toFile", 2, prim_toFile);
-  addPrimOp("__filterSource", 2, prim_filterSource);
-  addPrimOp("__path", 1, prim_path);
-
-  // Sets
-  addPrimOp("__attrNames", 1, prim_attrNames);
-  addPrimOp("__attrValues", 1, prim_attrValues);
-  addPrimOp("__getAttr", 2, prim_getAttr);
-  addPrimOp("__unsafeGetAttrPos", 2, prim_unsafeGetAttrPos);
-  addPrimOp("__hasAttr", 2, prim_hasAttr);
-  addPrimOp("__isAttrs", 1, prim_isAttrs);
-  addPrimOp("removeAttrs", 2, prim_removeAttrs);
-  addPrimOp("__listToAttrs", 1, prim_listToAttrs);
-  addPrimOp("__intersectAttrs", 2, prim_intersectAttrs);
-  addPrimOp("__catAttrs", 2, prim_catAttrs);
-  addPrimOp("__functionArgs", 1, prim_functionArgs);
-  addPrimOp("__mapAttrs", 2, prim_mapAttrs);
-
-  // Lists
-  addPrimOp("__isList", 1, prim_isList);
-  addPrimOp("__elemAt", 2, prim_elemAt);
-  addPrimOp("__head", 1, prim_head);
-  addPrimOp("__tail", 1, prim_tail);
-  addPrimOp("map", 2, prim_map);
-  addPrimOp("__filter", 2, prim_filter);
-  addPrimOp("__elem", 2, prim_elem);
-  addPrimOp("__concatLists", 1, prim_concatLists);
-  addPrimOp("__length", 1, prim_length);
-  addPrimOp("__foldl'", 3, prim_foldlStrict);
-  addPrimOp("__any", 2, prim_any);
-  addPrimOp("__all", 2, prim_all);
-  addPrimOp("__genList", 2, prim_genList);
-  addPrimOp("__sort", 2, prim_sort);
-  addPrimOp("__partition", 2, prim_partition);
-  addPrimOp("__concatMap", 2, prim_concatMap);
-
-  // Integer arithmetic
-  addPrimOp("__add", 2, prim_add);
-  addPrimOp("__sub", 2, prim_sub);
-  addPrimOp("__mul", 2, prim_mul);
-  addPrimOp("__div", 2, prim_div);
-  addPrimOp("__bitAnd", 2, prim_bitAnd);
-  addPrimOp("__bitOr", 2, prim_bitOr);
-  addPrimOp("__bitXor", 2, prim_bitXor);
-  addPrimOp("__lessThan", 2, prim_lessThan);
-
-  // String manipulation
-  addPrimOp("toString", 1, prim_toString);
-  addPrimOp("__substring", 3, prim_substring);
-  addPrimOp("__stringLength", 1, prim_stringLength);
-  addPrimOp("__hashString", 2, prim_hashString);
-  addPrimOp("__match", 2, prim_match);
-  addPrimOp("__split", 2, prim_split);
-  addPrimOp("__concatStringsSep", 2, prim_concatStringSep);
-  addPrimOp("__replaceStrings", 3, prim_replaceStrings);
-
-  // Versions
-  addPrimOp("__parseDrvName", 1, prim_parseDrvName);
-  addPrimOp("__compareVersions", 2, prim_compareVersions);
-  addPrimOp("__splitVersion", 1, prim_splitVersion);
-
-  // Derivations
-  addPrimOp("derivationStrict", 1, prim_derivationStrict);
-  addPrimOp("placeholder", 1, prim_placeholder);
-
-  // Networking
-  addPrimOp("__fetchurl", 1, prim_fetchurl);
-  addPrimOp("fetchTarball", 1, prim_fetchTarball);
-
-  /* Add a wrapper around the derivation primop that computes the
-     `drvPath' and `outPath' attributes lazily. */
-  std::string path =
-      canonPath(settings.nixDataDir + "/nix/corepkgs/derivation.nix", true);
-  sDerivationNix = symbols.Create(path);
-  evalFile(path, v);
-  addConstant("derivation", v);
-
-  /* Add a value containing the current Nix expression search path. */
-  mkList(v, searchPath.size());
-  int n = 0;
-  for (auto& i : searchPath) {
-    v2 = (*v.list)[n++] = allocValue();
-    mkAttrs(*v2, 2);
-    mkString(*allocAttr(*v2, symbols.Create("path")), i.second);
-    mkString(*allocAttr(*v2, symbols.Create("prefix")), i.first);
-  }
-  addConstant("__nixPath", v);
-
-  if (RegisterPrimOp::primOps != nullptr) {
-    for (auto& primOp : *RegisterPrimOp::primOps) {
-      addPrimOp(std::get<0>(primOp), std::get<1>(primOp), std::get<2>(primOp));
-    }
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/primops.hh b/third_party/nix/src/libexpr/primops.hh
deleted file mode 100644
index ab5f647202..0000000000
--- a/third_party/nix/src/libexpr/primops.hh
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <tuple>
-#include <vector>
-
-#include "libexpr/eval.hh"
-
-namespace nix {
-
-struct RegisterPrimOp {
-  using PrimOps = std::vector<std::tuple<std::string, size_t, PrimOpFun> >;
-  static PrimOps* primOps;
-  /* You can register a constant by passing an arity of 0. fun
-     will get called during EvalState initialization, so there
-     may be primops not yet added and builtins is not yet sorted. */
-  RegisterPrimOp(const std::string& name, size_t arity, PrimOpFun fun);
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/primops/context.cc b/third_party/nix/src/libexpr/primops/context.cc
deleted file mode 100644
index fb8879ead1..0000000000
--- a/third_party/nix/src/libexpr/primops/context.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-#include "libexpr/eval-inline.hh"
-#include "libexpr/primops.hh"
-#include "libstore/derivations.hh"
-
-namespace nix {
-
-static void prim_unsafeDiscardStringContext(EvalState& state, const Pos& pos,
-                                            Value** args, Value& v) {
-  PathSet context;
-  std::string s = state.coerceToString(pos, *args[0], context);
-  mkString(v, s, PathSet());
-}
-
-static RegisterPrimOp r1("__unsafeDiscardStringContext", 1,
-                         prim_unsafeDiscardStringContext);
-
-static void prim_hasContext(EvalState& state, const Pos& pos, Value** args,
-                            Value& v) {
-  PathSet context;
-  state.forceString(*args[0], context, pos);
-  mkBool(v, !context.empty());
-}
-
-static RegisterPrimOp r2("__hasContext", 1, prim_hasContext);
-
-/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
-   builder without causing the derivation to be built (for instance,
-   in the derivation that builds NARs in nix-push, when doing
-   source-only deployment).  This primop marks the string context so
-   that builtins.derivation adds the path to drv.inputSrcs rather than
-   drv.inputDrvs. */
-static void prim_unsafeDiscardOutputDependency(EvalState& state, const Pos& pos,
-                                               Value** args, Value& v) {
-  PathSet context;
-  std::string s = state.coerceToString(pos, *args[0], context);
-
-  PathSet context2;
-  for (auto& p : context) {
-    context2.insert(p.at(0) == '=' ? std::string(p, 1) : p);
-  }
-
-  mkString(v, s, context2);
-}
-
-static RegisterPrimOp r3("__unsafeDiscardOutputDependency", 1,
-                         prim_unsafeDiscardOutputDependency);
-
-/* Extract the context of a string as a structured Nix value.
-
-   The context is represented as an attribute set whose keys are the
-   paths in the context set and whose values are attribute sets with
-   the following keys:
-     path: True if the relevant path is in the context as a plain store
-           path (i.e. the kind of context you get when interpolating
-           a Nix path (e.g. ./.) into a string). False if missing.
-     allOutputs: True if the relevant path is a derivation and it is
-                  in the context as a drv file with all of its outputs
-                  (i.e. the kind of context you get when referencing
-                  .drvPath of some derivation). False if missing.
-     outputs: If a non-empty list, the relevant path is a derivation
-              and the provided outputs are referenced in the context
-              (i.e. the kind of context you get when referencing
-              .outPath of some derivation). Empty list if missing.
-   Note that for a given path any combination of the above attributes
-   may be present.
-*/
-static void prim_getContext(EvalState& state, const Pos& pos, Value** args,
-                            Value& v) {
-  struct ContextInfo {
-    bool path = false;
-    bool allOutputs = false;
-    Strings outputs;
-  };
-  PathSet context;
-  state.forceString(*args[0], context, pos);
-  auto contextInfos = std::map<Path, ContextInfo>();
-  for (const auto& p : context) {
-    Path drv;
-    std::string output;
-    const Path* path = &p;
-    if (p.at(0) == '=') {
-      drv = std::string(p, 1);
-      path = &drv;
-    } else if (p.at(0) == '!') {
-      std::pair<std::string, std::string> ctx = decodeContext(p);
-      drv = ctx.first;
-      output = ctx.second;
-      path = &drv;
-    }
-    auto isPath = drv.empty();
-    auto isAllOutputs = (!drv.empty()) && output.empty();
-
-    auto iter = contextInfos.find(*path);
-    if (iter == contextInfos.end()) {
-      contextInfos.emplace(
-          *path,
-          ContextInfo{isPath, isAllOutputs,
-                      output.empty() ? Strings{} : Strings{std::move(output)}});
-    } else {
-      if (isPath) {
-        iter->second.path = true;
-      } else if (isAllOutputs) {
-        iter->second.allOutputs = true;
-      } else {
-        iter->second.outputs.emplace_back(std::move(output));
-      }
-    }
-  }
-
-  state.mkAttrs(v, contextInfos.size());
-
-  auto sPath = state.symbols.Create("path");
-  auto sAllOutputs = state.symbols.Create("allOutputs");
-  for (const auto& info : contextInfos) {
-    auto& infoVal = *state.allocAttr(v, state.symbols.Create(info.first));
-    state.mkAttrs(infoVal, 3);
-    if (info.second.path) {
-      mkBool(*state.allocAttr(infoVal, sPath), true);
-    }
-    if (info.second.allOutputs) {
-      mkBool(*state.allocAttr(infoVal, sAllOutputs), true);
-    }
-    if (!info.second.outputs.empty()) {
-      auto& outputsVal = *state.allocAttr(infoVal, state.sOutputs);
-      state.mkList(outputsVal, info.second.outputs.size());
-      size_t i = 0;
-      for (const auto& output : info.second.outputs) {
-        mkString(*((*outputsVal.list)[i++] = state.allocValue()), output);
-      }
-    }
-  }
-}
-
-static RegisterPrimOp r4("__getContext", 1, prim_getContext);
-
-/* Append the given context to a given string.
-
-   See the commentary above unsafeGetContext for details of the
-   context representation.
-*/
-static void prim_appendContext(EvalState& state, const Pos& pos, Value** args,
-                               Value& v) {
-  PathSet context;
-  auto orig = state.forceString(*args[0], context, pos);
-
-  state.forceAttrs(*args[1], pos);
-
-  auto sPath = state.symbols.Create("path");
-  auto sAllOutputs = state.symbols.Create("allOutputs");
-  for (const auto& attr_iter : *args[1]->attrs) {
-    const Attr* i = &attr_iter.second;  // TODO(tazjin): get rid of this
-    if (!state.store->isStorePath(i->name)) {
-      throw EvalError("Context key '%s' is not a store path, at %s", i->name,
-                      i->pos);
-    }
-    if (!settings.readOnlyMode) {
-      state.store->ensurePath(i->name);
-    }
-    state.forceAttrs(*i->value, *i->pos);
-    auto iter = i->value->attrs->find(sPath);
-    if (iter != i->value->attrs->end()) {
-      if (state.forceBool(*iter->second.value, *iter->second.pos)) {
-        context.insert(i->name);
-      }
-    }
-
-    iter = i->value->attrs->find(sAllOutputs);
-    if (iter != i->value->attrs->end()) {
-      if (state.forceBool(*iter->second.value, *iter->second.pos)) {
-        if (!isDerivation(i->name)) {
-          throw EvalError(
-              "Tried to add all-outputs context of %s, which is not a "
-              "derivation, to a string, at %s",
-              i->name, i->pos);
-        }
-        context.insert("=" + std::string(i->name));
-      }
-    }
-
-    iter = i->value->attrs->find(state.sOutputs);
-    if (iter != i->value->attrs->end()) {
-      state.forceList(*iter->second.value, *iter->second.pos);
-      if (iter->second.value->listSize() && !isDerivation(i->name)) {
-        throw EvalError(
-            "Tried to add derivation output context of %s, which is not a "
-            "derivation, to a string, at %s",
-            i->name, i->pos);
-      }
-      for (unsigned int n = 0; n < iter->second.value->listSize(); ++n) {
-        auto name = state.forceStringNoCtx(*(*iter->second.value->list)[n],
-                                           *iter->second.pos);
-        context.insert("!" + name + "!" + std::string(i->name));
-      }
-    }
-  }
-
-  mkString(v, orig, context);
-}
-
-static RegisterPrimOp r5("__appendContext", 2, prim_appendContext);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/primops/fetchGit.cc b/third_party/nix/src/libexpr/primops/fetchGit.cc
deleted file mode 100644
index da4d683401..0000000000
--- a/third_party/nix/src/libexpr/primops/fetchGit.cc
+++ /dev/null
@@ -1,277 +0,0 @@
-#include <nlohmann/json.hpp>
-#include <regex>
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/match.h>
-#include <absl/strings/str_split.h>
-#include <glog/logging.h>
-#include <sys/time.h>
-
-#include "libexpr/eval-inline.hh"
-#include "libexpr/primops.hh"
-#include "libstore/download.hh"
-#include "libstore/pathlocks.hh"
-#include "libstore/store-api.hh"
-#include "libutil/hash.hh"
-
-using namespace std::string_literals;
-
-namespace nix {
-
-struct GitInfo {
-  Path storePath;
-  std::string rev;
-  std::string shortRev;
-  uint64_t revCount = 0;
-};
-
-std::regex revRegex("^[0-9a-fA-F]{40}$");
-
-GitInfo exportGit(ref<Store> store, const std::string& uri,
-                  std::optional<std::string> ref, std::string rev,
-                  const std::string& name) {
-  if (evalSettings.pureEval && rev == "") {
-    throw Error("in pure evaluation mode, 'fetchGit' requires a Git revision");
-  }
-
-  if (!ref && rev == "" && absl::StartsWith(uri, "/") &&
-      pathExists(uri + "/.git")) {
-    bool clean = true;
-
-    try {
-      runProgram("git", true,
-                 {"-C", uri, "diff-index", "--quiet", "HEAD", "--"});
-    } catch (ExecError& e) {
-      if (!WIFEXITED(e.status) || WEXITSTATUS(e.status) != 1) {
-        throw;
-      }
-      clean = false;
-    }
-
-    if (!clean) {
-      /* This is an unclean working tree. So copy all tracked
-         files. */
-
-      GitInfo gitInfo;
-      gitInfo.rev = "0000000000000000000000000000000000000000";
-      gitInfo.shortRev = std::string(gitInfo.rev, 0, 7);
-
-      std::set<std::string> files =
-          absl::StrSplit(runProgram("git", true, {"-C", uri, "ls-files", "-z"}),
-                         absl::ByChar('\0'), absl::SkipEmpty());
-
-      PathFilter filter = [&](const Path& p) -> bool {
-        assert(absl::StartsWith(p, uri));
-        std::string file(p, uri.size() + 1);
-
-        auto st = lstat(p);
-
-        if (S_ISDIR(st.st_mode)) {
-          auto prefix = file + "/";
-          auto i = files.lower_bound(prefix);
-          return i != files.end() && absl::StartsWith(*i, prefix);
-        }
-
-        return files.count(file);
-      };
-
-      gitInfo.storePath =
-          store->addToStore("source", uri, true, htSHA256, filter);
-
-      return gitInfo;
-    }
-
-    // clean working tree, but no ref or rev specified.  Use 'HEAD'.
-    rev = absl::StripTrailingAsciiWhitespace(
-        runProgram("git", true, {"-C", uri, "rev-parse", "HEAD"}));
-    ref = "HEAD"s;
-  }
-
-  if (!ref) {
-    ref = "HEAD"s;
-  }
-
-  if (rev != "" && !std::regex_match(rev, revRegex)) {
-    throw Error("invalid Git revision '%s'", rev);
-  }
-
-  deletePath(getCacheDir() + "/nix/git");
-
-  Path cacheDir = getCacheDir() + "/nix/gitv2/" +
-                  hashString(htSHA256, uri).to_string(Base32, false);
-
-  if (!pathExists(cacheDir)) {
-    createDirs(dirOf(cacheDir));
-    runProgram("git", true, {"init", "--bare", cacheDir});
-  }
-
-  Path localRefFile;
-  if (ref->compare(0, 5, "refs/") == 0) {
-    localRefFile = cacheDir + "/" + *ref;
-  } else {
-    localRefFile = cacheDir + "/refs/heads/" + *ref;
-  }
-
-  bool doFetch;
-  time_t now = time(0);
-  /* If a rev was specified, we need to fetch if it's not in the
-     repo. */
-  if (rev != "") {
-    try {
-      runProgram("git", true, {"-C", cacheDir, "cat-file", "-e", rev});
-      doFetch = false;
-    } catch (ExecError& e) {
-      if (WIFEXITED(e.status)) {
-        doFetch = true;
-      } else {
-        throw;
-      }
-    }
-  } else {
-    /* If the local ref is older than β€˜tarball-ttl’ seconds, do a
-       git fetch to update the local ref to the remote ref. */
-    struct stat st;
-    doFetch = stat(localRefFile.c_str(), &st) != 0 ||
-              static_cast<uint64_t>(st.st_mtime) + settings.tarballTtl <=
-                  static_cast<uint64_t>(now);
-  }
-  if (doFetch) {
-    DLOG(INFO) << "fetching Git repository '" << uri << "'";
-
-    // FIXME: git stderr messes up our progress indicator, so
-    // we're using --quiet for now. Should process its stderr.
-    runProgram("git", true,
-               {"-C", cacheDir, "fetch", "--quiet", "--force", "--", uri,
-                fmt("%s:%s", *ref, *ref)});
-
-    struct timeval times[2];
-    times[0].tv_sec = now;
-    times[0].tv_usec = 0;
-    times[1].tv_sec = now;
-    times[1].tv_usec = 0;
-
-    utimes(localRefFile.c_str(), times);
-  }
-
-  // FIXME: check whether rev is an ancestor of ref.
-  GitInfo gitInfo;
-  gitInfo.rev =
-      rev != "" ? rev
-                : absl::StripTrailingAsciiWhitespace(readFile(localRefFile));
-  gitInfo.shortRev = std::string(gitInfo.rev, 0, 7);
-
-  VLOG(2) << "using revision " << gitInfo.rev << " of repo '" << uri << "'";
-
-  std::string storeLinkName =
-      hashString(htSHA512, name + std::string("\0"s) + gitInfo.rev)
-          .to_string(Base32, false);
-  Path storeLink = cacheDir + "/" + storeLinkName + ".link";
-  PathLocks storeLinkLock({storeLink}, fmt("waiting for lock on '%1%'...",
-                                           storeLink));  // FIXME: broken
-
-  try {
-    auto json = nlohmann::json::parse(readFile(storeLink));
-
-    assert(json["name"] == name && json["rev"] == gitInfo.rev);
-
-    gitInfo.storePath = json["storePath"];
-
-    if (store->isValidPath(gitInfo.storePath)) {
-      gitInfo.revCount = json["revCount"];
-      return gitInfo;
-    }
-
-  } catch (SysError& e) {
-    if (e.errNo != ENOENT) {
-      throw;
-    }
-  }
-
-  // FIXME: should pipe this, or find some better way to extract a
-  // revision.
-  auto tar = runProgram("git", true, {"-C", cacheDir, "archive", gitInfo.rev});
-
-  Path tmpDir = createTempDir();
-  AutoDelete delTmpDir(tmpDir, true);
-
-  runProgram("tar", true, {"x", "-C", tmpDir}, tar);
-
-  gitInfo.storePath = store->addToStore(name, tmpDir);
-
-  gitInfo.revCount = std::stoull(runProgram(
-      "git", true, {"-C", cacheDir, "rev-list", "--count", gitInfo.rev}));
-
-  nlohmann::json json;
-  json["storePath"] = gitInfo.storePath;
-  json["uri"] = uri;
-  json["name"] = name;
-  json["rev"] = gitInfo.rev;
-  json["revCount"] = gitInfo.revCount;
-
-  writeFile(storeLink, json.dump());
-
-  return gitInfo;
-}
-
-static void prim_fetchGit(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  std::string url;
-  std::optional<std::string> ref;
-  std::string rev;
-  std::string name = "source";
-  PathSet context;
-
-  state.forceValue(*args[0]);
-
-  if (args[0]->type == tAttrs) {
-    state.forceAttrs(*args[0], pos);
-
-    for (auto& attr_iter : *args[0]->attrs) {
-      auto& attr = attr_iter.second;
-      std::string n(attr.name);
-      if (n == "url") {
-        url =
-            state.coerceToString(*attr.pos, *attr.value, context, false, false);
-      } else if (n == "ref") {
-        ref = state.forceStringNoCtx(*attr.value, *attr.pos);
-      } else if (n == "rev") {
-        rev = state.forceStringNoCtx(*attr.value, *attr.pos);
-      } else if (n == "name") {
-        name = state.forceStringNoCtx(*attr.value, *attr.pos);
-      } else {
-        throw EvalError("unsupported argument '%s' to 'fetchGit', at %s",
-                        attr.name, *attr.pos);
-      }
-    }
-
-    if (url.empty()) {
-      throw EvalError(format("'url' argument required, at %1%") % pos);
-    }
-
-  } else {
-    url = state.coerceToString(pos, *args[0], context, false, false);
-  }
-
-  // FIXME: git externals probably can be used to bypass the URI
-  // whitelist. Ah well.
-  state.checkURI(url);
-
-  auto gitInfo = exportGit(state.store, url, ref, rev, name);
-
-  state.mkAttrs(v, 8);
-  mkString(*state.allocAttr(v, state.sOutPath), gitInfo.storePath,
-           PathSet({gitInfo.storePath}));
-  mkString(*state.allocAttr(v, state.symbols.Create("rev")), gitInfo.rev);
-  mkString(*state.allocAttr(v, state.symbols.Create("shortRev")),
-           gitInfo.shortRev);
-  mkInt(*state.allocAttr(v, state.symbols.Create("revCount")),
-        gitInfo.revCount);
-
-  if (state.allowedPaths) {
-    state.allowedPaths->insert(state.store->toRealPath(gitInfo.storePath));
-  }
-}
-
-static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/primops/fetchMercurial.cc b/third_party/nix/src/libexpr/primops/fetchMercurial.cc
deleted file mode 100644
index 13dc61766f..0000000000
--- a/third_party/nix/src/libexpr/primops/fetchMercurial.cc
+++ /dev/null
@@ -1,246 +0,0 @@
-#include <nlohmann/json.hpp>
-#include <regex>
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/match.h>
-#include <absl/strings/str_split.h>
-#include <glog/logging.h>
-#include <sys/time.h>
-
-#include "libexpr/eval-inline.hh"
-#include "libexpr/primops.hh"
-#include "libstore/download.hh"
-#include "libstore/pathlocks.hh"
-#include "libstore/store-api.hh"
-
-using namespace std::string_literals;
-
-namespace nix {
-
-struct HgInfo {
-  Path storePath;
-  std::string branch;
-  std::string rev;
-  uint64_t revCount = 0;
-};
-
-std::regex commitHashRegex("^[0-9a-fA-F]{40}$");
-
-HgInfo exportMercurial(ref<Store> store, const std::string& uri,
-                       std::string rev, const std::string& name) {
-  if (evalSettings.pureEval && rev == "") {
-    throw Error(
-        "in pure evaluation mode, 'fetchMercurial' requires a Mercurial "
-        "revision");
-  }
-
-  if (rev == "" && absl::StartsWith(uri, "/") && pathExists(uri + "/.hg")) {
-    bool clean = runProgram("hg", true,
-                            {"status", "-R", uri, "--modified", "--added",
-                             "--removed"}) == "";
-
-    if (!clean) {
-      /* This is an unclean working tree. So copy all tracked
-         files. */
-
-      DLOG(INFO) << "copying unclean Mercurial working tree '" << uri << "'";
-
-      HgInfo hgInfo;
-      hgInfo.rev = "0000000000000000000000000000000000000000";
-      hgInfo.branch = absl::StripTrailingAsciiWhitespace(
-          runProgram("hg", true, {"branch", "-R", uri}));
-
-      std::set<std::string> files = absl::StrSplit(
-          runProgram("hg", true,
-                     {"status", "-R", uri, "--clean", "--modified", "--added",
-                      "--no-status", "--print0"}),
-          absl::ByChar('\0'), absl::SkipEmpty());
-
-      PathFilter filter = [&](const Path& p) -> bool {
-        assert(absl::StartsWith(p, uri));
-        std::string file(p, uri.size() + 1);
-
-        auto st = lstat(p);
-
-        if (S_ISDIR(st.st_mode)) {
-          auto prefix = file + "/";
-          auto i = files.lower_bound(prefix);
-          return i != files.end() && absl::StartsWith(*i, prefix);
-        }
-
-        return files.count(file);
-      };
-
-      hgInfo.storePath =
-          store->addToStore("source", uri, true, htSHA256, filter);
-
-      return hgInfo;
-    }
-  }
-
-  if (rev == "") {
-    rev = "default";
-  }
-
-  Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(),
-                      hashString(htSHA256, uri).to_string(Base32, false));
-
-  Path stampFile = fmt("%s/.hg/%s.stamp", cacheDir,
-                       hashString(htSHA512, rev).to_string(Base32, false));
-
-  /* If we haven't pulled this repo less than β€˜tarball-ttl’ seconds,
-     do so now. */
-  time_t now = time(0);
-  struct stat st;
-  if (stat(stampFile.c_str(), &st) != 0 ||
-      static_cast<uint64_t>(st.st_mtime) + settings.tarballTtl <=
-          static_cast<uint64_t>(now)) {
-    /* Except that if this is a commit hash that we already have,
-       we don't have to pull again. */
-    if (!(std::regex_match(rev, commitHashRegex) && pathExists(cacheDir) &&
-          runProgram(RunOptions("hg", {"log", "-R", cacheDir, "-r", rev,
-                                       "--template", "1"})
-                         .killStderr(true))
-                  .second == "1")) {
-      DLOG(INFO) << "fetching Mercurial repository '" << uri << "'";
-
-      if (pathExists(cacheDir)) {
-        try {
-          runProgram("hg", true, {"pull", "-R", cacheDir, "--", uri});
-        } catch (ExecError& e) {
-          std::string transJournal = cacheDir + "/.hg/store/journal";
-          /* hg throws "abandoned transaction" error only if this file exists */
-          if (pathExists(transJournal)) {
-            runProgram("hg", true, {"recover", "-R", cacheDir});
-            runProgram("hg", true, {"pull", "-R", cacheDir, "--", uri});
-          } else {
-            throw ExecError(e.status,
-                            fmt("'hg pull' %s", statusToString(e.status)));
-          }
-        }
-      } else {
-        createDirs(dirOf(cacheDir));
-        runProgram("hg", true, {"clone", "--noupdate", "--", uri, cacheDir});
-      }
-    }
-
-    writeFile(stampFile, "");
-  }
-
-  std::vector<std::string> tokens =
-      absl::StrSplit(runProgram("hg", true,
-                                {"log", "-R", cacheDir, "-r", rev, "--template",
-                                 "{node} {rev} {branch}"}),
-                     absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-  assert(tokens.size() == 3);
-
-  HgInfo hgInfo;
-  hgInfo.rev = tokens[0];
-  hgInfo.revCount = std::stoull(tokens[1]);
-  hgInfo.branch = tokens[2];
-
-  std::string storeLinkName =
-      hashString(htSHA512, name + std::string("\0"s) + hgInfo.rev)
-          .to_string(Base32, false);
-  Path storeLink = fmt("%s/.hg/%s.link", cacheDir, storeLinkName);
-
-  try {
-    auto json = nlohmann::json::parse(readFile(storeLink));
-
-    assert(json["name"] == name && json["rev"] == hgInfo.rev);
-
-    hgInfo.storePath = json["storePath"];
-
-    if (store->isValidPath(hgInfo.storePath)) {
-      DLOG(INFO) << "using cached Mercurial store path '" << hgInfo.storePath
-                 << "'";
-      return hgInfo;
-    }
-
-  } catch (SysError& e) {
-    if (e.errNo != ENOENT) {
-      throw;
-    }
-  }
-
-  Path tmpDir = createTempDir();
-  AutoDelete delTmpDir(tmpDir, true);
-
-  runProgram("hg", true, {"archive", "-R", cacheDir, "-r", rev, tmpDir});
-
-  deletePath(tmpDir + "/.hg_archival.txt");
-
-  hgInfo.storePath = store->addToStore(name, tmpDir);
-
-  nlohmann::json json;
-  json["storePath"] = hgInfo.storePath;
-  json["uri"] = uri;
-  json["name"] = name;
-  json["branch"] = hgInfo.branch;
-  json["rev"] = hgInfo.rev;
-  json["revCount"] = hgInfo.revCount;
-
-  writeFile(storeLink, json.dump());
-
-  return hgInfo;
-}
-
-static void prim_fetchMercurial(EvalState& state, const Pos& pos, Value** args,
-                                Value& v) {
-  std::string url;
-  std::string rev;
-  std::string name = "source";
-  PathSet context;
-
-  state.forceValue(*args[0]);
-
-  if (args[0]->type == tAttrs) {
-    state.forceAttrs(*args[0], pos);
-
-    for (auto& attr_iter : *args[0]->attrs) {
-      auto& attr = attr_iter.second;
-      std::string n(attr.name);
-      if (n == "url") {
-        url =
-            state.coerceToString(*attr.pos, *attr.value, context, false, false);
-      } else if (n == "rev") {
-        rev = state.forceStringNoCtx(*attr.value, *attr.pos);
-      } else if (n == "name") {
-        name = state.forceStringNoCtx(*attr.value, *attr.pos);
-      } else {
-        throw EvalError("unsupported argument '%s' to 'fetchMercurial', at %s",
-                        attr.name, *attr.pos);
-      }
-    }
-
-    if (url.empty()) {
-      throw EvalError(format("'url' argument required, at %1%") % pos);
-    }
-
-  } else {
-    url = state.coerceToString(pos, *args[0], context, false, false);
-  }
-
-  // FIXME: git externals probably can be used to bypass the URI
-  // whitelist. Ah well.
-  state.checkURI(url);
-
-  auto hgInfo = exportMercurial(state.store, url, rev, name);
-
-  state.mkAttrs(v, 8);
-  mkString(*state.allocAttr(v, state.sOutPath), hgInfo.storePath,
-           PathSet({hgInfo.storePath}));
-  mkString(*state.allocAttr(v, state.symbols.Create("branch")), hgInfo.branch);
-  mkString(*state.allocAttr(v, state.symbols.Create("rev")), hgInfo.rev);
-  mkString(*state.allocAttr(v, state.symbols.Create("shortRev")),
-           std::string(hgInfo.rev, 0, 12));
-  mkInt(*state.allocAttr(v, state.symbols.Create("revCount")), hgInfo.revCount);
-
-  if (state.allowedPaths) {
-    state.allowedPaths->insert(state.store->toRealPath(hgInfo.storePath));
-  }
-}
-
-static RegisterPrimOp r("fetchMercurial", 1, prim_fetchMercurial);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/primops/fromTOML.cc b/third_party/nix/src/libexpr/primops/fromTOML.cc
deleted file mode 100644
index e3d2a49407..0000000000
--- a/third_party/nix/src/libexpr/primops/fromTOML.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-#include "cpptoml/cpptoml.h"
-#include "libexpr/eval-inline.hh"
-#include "libexpr/primops.hh"
-
-namespace nix {
-
-static void prim_fromTOML(EvalState& state, const Pos& pos, Value** args,
-                          Value& v) {
-  using namespace cpptoml;
-
-  auto toml = state.forceStringNoCtx(*args[0], pos);
-
-  std::istringstream tomlStream(toml);
-
-  std::function<void(Value&, std::shared_ptr<base>)> visit;
-
-  visit = [&](Value& v, std::shared_ptr<base> t) {
-    if (auto t2 = t->as_table()) {
-      size_t size = 0;
-      for (auto& i : *t2) {
-        (void)i;
-        size++;
-      }
-
-      state.mkAttrs(v, size);
-
-      for (auto& i : *t2) {
-        auto& v2 = *state.allocAttr(v, state.symbols.Create(i.first));
-
-        if (auto i2 = i.second->as_table_array()) {
-          size_t size2 = i2->get().size();
-          state.mkList(v2, size2);
-          for (size_t j = 0; j < size2; ++j) {
-            visit(*((*v2.list)[j] = state.allocValue()), i2->get()[j]);
-          }
-        } else {
-          visit(v2, i.second);
-        }
-      }
-    }
-
-    else if (auto t2 = t->as_array()) {
-      size_t size = t2->get().size();
-
-      state.mkList(v, size);
-
-      for (size_t i = 0; i < size; ++i) {
-        visit(*((*v.list)[i] = state.allocValue()), t2->get()[i]);
-      }
-    }
-
-    // Handle cases like 'a = [[{ a = true }]]', which IMHO should be
-    // parsed as a array containing an array containing a table,
-    // but instead are parsed as an array containing a table array
-    // containing a table.
-    else if (auto t2 = t->as_table_array()) {
-      size_t size = t2->get().size();
-
-      state.mkList(v, size);
-
-      for (size_t j = 0; j < size; ++j) {
-        visit(*((*v.list)[j] = state.allocValue()), t2->get()[j]);
-      }
-    }
-
-    else if (t->is_value()) {
-      if (auto val = t->as<int64_t>()) {
-        mkInt(v, val->get());
-      } else if (auto val = t->as<NixFloat>()) {
-        mkFloat(v, val->get());
-      } else if (auto val = t->as<bool>()) {
-        mkBool(v, val->get());
-      } else if (auto val = t->as<std::string>()) {
-        mkString(v, val->get());
-      } else {
-        throw EvalError("unsupported value type in TOML");
-      }
-    }
-
-    else {
-      abort();
-    }
-  };
-
-  try {
-    visit(v, parser(tomlStream).parse());
-  } catch (std::runtime_error& e) {
-    throw EvalError("while parsing a TOML string at %s: %s", pos, e.what());
-  }
-}
-
-static RegisterPrimOp r("fromTOML", 1, prim_fromTOML);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/symbol-table.cc b/third_party/nix/src/libexpr/symbol-table.cc
deleted file mode 100644
index 2b27ca54c2..0000000000
--- a/third_party/nix/src/libexpr/symbol-table.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "libexpr/symbol-table.hh"
-
-#include <absl/container/node_hash_set.h>
-#include <absl/strings/string_view.h>
-
-namespace nix {
-
-Symbol SymbolTable::Create(absl::string_view sym) {
-  auto it = symbols_.emplace(sym);
-  const std::string* ptr = &(*it.first);
-  return Symbol(ptr);
-}
-
-size_t SymbolTable::Size() const { return symbols_.size(); }
-
-size_t SymbolTable::TotalSize() const {
-  size_t n = 0;
-  for (auto& i : symbols_) {
-    n += i.size();
-  }
-  return n;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/symbol-table.hh b/third_party/nix/src/libexpr/symbol-table.hh
deleted file mode 100644
index c259965885..0000000000
--- a/third_party/nix/src/libexpr/symbol-table.hh
+++ /dev/null
@@ -1,69 +0,0 @@
-#pragma once
-
-#include <absl/container/node_hash_set.h>
-#include <absl/strings/string_view.h>
-
-namespace nix {  // TODO(tazjin): ::expr
-
-// TODO(tazjin): Replace with a simpler struct, or get rid of.
-class Symbol {
- private:
-  const std::string* s;  // pointer into SymbolTable
-  Symbol(const std::string* s) : s(s){};
-  friend class SymbolTable;
-
- public:
-  bool operator==(const Symbol& s2) const { return s == s2.s; }
-
-  bool operator!=(const Symbol& s2) const { return s != s2.s; }
-
-  bool operator<(const Symbol& s2) const { return *s < *s2.s; }
-
-  operator const std::string&() const { return *s; }
-
-  bool set() const { return s; }
-
-  bool empty() const { return s->empty(); }
-
-  friend std::ostream& operator<<(std::ostream& str, const Symbol& sym);
-
-  template <typename H>
-  friend H AbslHashValue(H h, const Symbol& c) {
-    return H::combine(std::move(h), c.s);
-  }
-};
-
-// SymbolTable is a hash-set based symbol-interning mechanism.
-//
-// TODO(tazjin): Figure out which things use this. AttrSets, ...?
-// Is it possible this only exists because AttrSet wasn't a map?
-//
-// Original comment:
-//
-// Symbol table used by the parser and evaluator to represent and look
-// up identifiers and attributes efficiently. SymbolTable::create()
-// converts a string into a symbol. Symbols have the property that
-// they can be compared efficiently (using a pointer equality test),
-// because the symbol table stores only one copy of each string.
-class SymbolTable {
- public:
-  // Create a new symbol in this table by emplacing the provided
-  // string into it.
-  //
-  // The symbol will reference an existing symbol if the symbol is
-  // already interned.
-  Symbol Create(absl::string_view sym);
-
-  // Return the number of symbols interned.
-  size_t Size() const;
-
-  // Return the total size (in bytes)
-  size_t TotalSize() const;
-
- private:
-  // flat_hash_set does not retain pointer stability on rehashing,
-  // hence "interned" strings/symbols are stored on the heap.
-  absl::node_hash_set<std::string> symbols_;
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/value-to-json.cc b/third_party/nix/src/libexpr/value-to-json.cc
deleted file mode 100644
index a338d4eed7..0000000000
--- a/third_party/nix/src/libexpr/value-to-json.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "libexpr/value-to-json.hh"
-
-#include <cstdlib>
-#include <iomanip>
-
-#include "libexpr/eval-inline.hh"
-#include "libutil/json.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-void printValueAsJSON(EvalState& state, bool strict, Value& v,
-                      JSONPlaceholder& out, PathSet& context) {
-  checkInterrupt();
-
-  if (strict) {
-    state.forceValue(v);
-  }
-
-  switch (v.type) {
-    case tInt:
-      out.write(v.integer);
-      break;
-
-    case tBool:
-      out.write(v.boolean);
-      break;
-
-    case tString:
-      copyContext(v, context);
-      out.write(v.string.s);
-      break;
-
-    case tPath:
-      out.write(state.copyPathToStore(context, v.path));
-      break;
-
-    case tNull:
-      out.write(nullptr);
-      break;
-
-    case tAttrs: {
-      auto maybeString =
-          state.tryAttrsToString(noPos, v, context, false, false);
-      if (maybeString) {
-        out.write(*maybeString);
-        break;
-      }
-      auto i = v.attrs->find(state.sOutPath);
-      if (i == v.attrs->end()) {
-        auto obj(out.object());
-        StringSet names;
-        for (auto& j : *v.attrs) {
-          names.insert(j.second.name);
-        }
-        for (auto& j : names) {
-          auto [_, a] = *v.attrs->find(state.symbols.Create(j));
-          auto placeholder(obj.placeholder(j));
-          printValueAsJSON(state, strict, *a.value, placeholder, context);
-        }
-      } else {
-        printValueAsJSON(state, strict, *i->second.value, out, context);
-      }
-      break;
-    }
-
-    case tList: {
-      auto list(out.list());
-      for (unsigned int n = 0; n < v.listSize(); ++n) {
-        auto placeholder(list.placeholder());
-        printValueAsJSON(state, strict, *(*v.list)[n], placeholder, context);
-      }
-      break;
-    }
-
-    case tFloat:
-      out.write(v.fpoint);
-      break;
-
-    default:
-      throw TypeError(format("cannot convert %1% to JSON") % showType(v));
-  }
-}
-
-void printValueAsJSON(EvalState& state, bool strict, Value& v,
-                      std::ostream& str, PathSet& context) {
-  JSONPlaceholder out(str);
-  printValueAsJSON(state, strict, v, out, context);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/value-to-json.hh b/third_party/nix/src/libexpr/value-to-json.hh
deleted file mode 100644
index 294d776045..0000000000
--- a/third_party/nix/src/libexpr/value-to-json.hh
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <map>
-#include <string>
-
-#include "libexpr/eval.hh"
-#include "libexpr/nixexpr.hh"
-
-namespace nix {
-
-class JSONPlaceholder;
-
-void printValueAsJSON(EvalState& state, bool strict, Value& v,
-                      JSONPlaceholder& out, PathSet& context);
-
-void printValueAsJSON(EvalState& state, bool strict, Value& v,
-                      std::ostream& str, PathSet& context);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/value-to-xml.cc b/third_party/nix/src/libexpr/value-to-xml.cc
deleted file mode 100644
index 921973881f..0000000000
--- a/third_party/nix/src/libexpr/value-to-xml.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-#include "libexpr/value-to-xml.hh"
-
-#include <cstdlib>
-
-#include "libexpr/eval-inline.hh"
-#include "libutil/util.hh"
-#include "libutil/xml-writer.hh"
-
-namespace nix {
-
-static XMLAttrs singletonAttrs(const std::string& name,
-                               const std::string& value) {
-  XMLAttrs attrs;
-  attrs[name] = value;
-  return attrs;
-}
-
-static void printValueAsXML(EvalState& state, bool strict, bool location,
-                            Value& v, XMLWriter& doc, PathSet& context,
-                            PathSet& drvsSeen);
-
-static void posToXML(XMLAttrs& xmlAttrs, const Pos& pos) {
-  xmlAttrs["path"] = pos.file.value();
-  xmlAttrs["line"] = (format("%1%") % pos.line).str();
-  xmlAttrs["column"] = (format("%1%") % pos.column).str();
-}
-
-static void showAttrs(EvalState& state, bool strict, bool location,
-                      Bindings& attrs, XMLWriter& doc, PathSet& context,
-                      PathSet& drvsSeen) {
-  StringSet names;
-
-  for (auto& i : attrs) {
-    names.insert(i.second.name);
-  }
-
-  for (auto& i : names) {
-    auto& [_, a] = *attrs.find(state.symbols.Create(i));
-
-    XMLAttrs xmlAttrs;
-    xmlAttrs["name"] = i;
-    if (location && a.pos != &noPos) {
-      posToXML(xmlAttrs, *a.pos);
-    }
-
-    XMLOpenElement elem(doc, "attr", xmlAttrs);
-    printValueAsXML(state, strict, location, *a.value, doc, context, drvsSeen);
-  }
-}
-
-static void printValueAsXML(EvalState& state, bool strict, bool location,
-                            Value& v, XMLWriter& doc, PathSet& context,
-                            PathSet& drvsSeen) {
-  checkInterrupt();
-
-  if (strict) {
-    state.forceValue(v);
-  }
-
-  switch (v.type) {
-    case tInt:
-      doc.writeEmptyElement(
-          "int", singletonAttrs("value", (format("%1%") % v.integer).str()));
-      break;
-
-    case tBool:
-      doc.writeEmptyElement(
-          "bool", singletonAttrs("value", v.boolean ? "true" : "false"));
-      break;
-
-    case tString:
-      /* !!! show the context? */
-      copyContext(v, context);
-      doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
-      break;
-
-    case tPath:
-      doc.writeEmptyElement("path", singletonAttrs("value", v.path));
-      break;
-
-    case tNull:
-      doc.writeEmptyElement("null");
-      break;
-
-    case tAttrs:
-      if (state.isDerivation(v)) {
-        XMLAttrs xmlAttrs;
-
-        Bindings::iterator a =
-            v.attrs->find(state.symbols.Create("derivation"));
-
-        Path drvPath;
-        a = v.attrs->find(state.sDrvPath);
-        if (a != v.attrs->end()) {
-          if (strict) {
-            state.forceValue(*a->second.value);
-          }
-          if (a->second.value->type == tString) {
-            xmlAttrs["drvPath"] = drvPath = a->second.value->string.s;
-          }
-        }
-
-        a = v.attrs->find(state.sOutPath);
-        if (a != v.attrs->end()) {
-          if (strict) {
-            state.forceValue(*a->second.value);
-          }
-          if (a->second.value->type == tString) {
-            xmlAttrs["outPath"] = a->second.value->string.s;
-          }
-        }
-
-        XMLOpenElement _(doc, "derivation", xmlAttrs);
-
-        if (!drvPath.empty() && drvsSeen.find(drvPath) == drvsSeen.end()) {
-          drvsSeen.insert(drvPath);
-          showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
-        } else {
-          doc.writeEmptyElement("repeated");
-        }
-      }
-
-      else {
-        XMLOpenElement _(doc, "attrs");
-        showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
-      }
-
-      break;
-
-    case tList: {
-      XMLOpenElement _(doc, "list");
-      for (unsigned int n = 0; n < v.listSize(); ++n) {
-        printValueAsXML(state, strict, location, *(*v.list)[n], doc, context,
-                        drvsSeen);
-      }
-      break;
-    }
-
-    case tLambda: {
-      XMLAttrs xmlAttrs;
-      if (location) {
-        posToXML(xmlAttrs, v.lambda.fun->pos);
-      }
-      XMLOpenElement _(doc, "function", xmlAttrs);
-
-      if (v.lambda.fun->matchAttrs) {
-        XMLAttrs attrs;
-        if (!v.lambda.fun->arg.empty()) {
-          attrs["name"] = v.lambda.fun->arg;
-        }
-        if (v.lambda.fun->formals->ellipsis) {
-          attrs["ellipsis"] = "1";
-        }
-        XMLOpenElement _(doc, "attrspat", attrs);
-        for (auto& i : v.lambda.fun->formals->formals) {
-          doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
-        }
-      } else {
-        doc.writeEmptyElement("varpat",
-                              singletonAttrs("name", v.lambda.fun->arg));
-      }
-
-      break;
-    }
-
-    case tFloat:
-      doc.writeEmptyElement(
-          "float", singletonAttrs("value", (format("%1%") % v.fpoint).str()));
-      break;
-
-    default:
-      doc.writeEmptyElement("unevaluated");
-  }
-}
-
-void printValueAsXML(EvalState& state, bool strict, bool location, Value& v,
-                     std::ostream& out, PathSet& context) {
-  XMLWriter doc(true, out);
-  XMLOpenElement root(doc, "expr");
-  PathSet drvsSeen;
-  printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/value-to-xml.hh b/third_party/nix/src/libexpr/value-to-xml.hh
deleted file mode 100644
index 18c5279236..0000000000
--- a/third_party/nix/src/libexpr/value-to-xml.hh
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <map>
-#include <string>
-
-#include "libexpr/eval.hh"
-#include "libexpr/nixexpr.hh"
-
-namespace nix {
-
-void printValueAsXML(EvalState& state, bool strict, bool location, Value& v,
-                     std::ostream& out, PathSet& context);
-
-}
diff --git a/third_party/nix/src/libexpr/value.cc b/third_party/nix/src/libexpr/value.cc
deleted file mode 100644
index 93fe187478..0000000000
--- a/third_party/nix/src/libexpr/value.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-#include "libexpr/value.hh"
-
-#include <glog/logging.h>
-
-namespace nix {
-
-Value::Value(const Value& copy) { *this = copy; }
-
-Value::Value(Value&& move) { *this = move; }
-
-Value& Value::operator=(const Value& copy) {
-  if (type != copy.type) {
-    memset(this, 0, sizeof(*this));
-  }
-  type = copy.type;
-  switch (type) {
-    case tInt:
-      integer = copy.integer;
-      break;
-    case tBool:
-      boolean = copy.boolean;
-      break;
-    case tString:
-      string = copy.string;
-      break;
-    case tPath:
-      path = copy.path;
-      break;
-    case tNull:
-      /* no fields */
-      break;
-    case tAttrs:
-      attrs = copy.attrs;
-      break;
-    case tList:
-      list = copy.list;
-      break;
-    case tThunk:
-      thunk = copy.thunk;
-      break;
-    case tApp:
-      app = copy.app;
-      break;
-    case tLambda:
-      lambda = copy.lambda;
-      break;
-    case tBlackhole:
-      /* no fields */
-      break;
-    case tPrimOp:
-      primOp = copy.primOp;
-      break;
-    case tPrimOpApp:
-      primOpApp = copy.primOpApp;
-      break;
-    case _reserved1:
-      LOG(FATAL) << "attempted to assign a tExternal value";
-      break;
-    case tFloat:
-      fpoint = copy.fpoint;
-      break;
-  }
-  return *this;
-}
-
-Value& Value::operator=(Value&& move) {
-  if (type != move.type) {
-    memset(this, 0, sizeof(*this));
-  }
-  type = move.type;
-  switch (type) {
-    case tInt:
-      integer = move.integer;
-      break;
-    case tBool:
-      boolean = move.boolean;
-      break;
-    case tString:
-      string = move.string;
-      break;
-    case tPath:
-      path = move.path;
-      break;
-    case tNull:
-      /* no fields */
-      break;
-    case tAttrs:
-      attrs = move.attrs;
-      break;
-    case tList:
-      list = move.list;
-      break;
-    case tThunk:
-      thunk = move.thunk;
-      break;
-    case tApp:
-      app = move.app;
-      break;
-    case tLambda:
-      lambda = move.lambda;
-      break;
-    case tBlackhole:
-      /* no fields */
-      break;
-    case tPrimOp:
-      primOp = move.primOp;
-      break;
-    case tPrimOpApp:
-      primOpApp = move.primOpApp;
-      break;
-    case _reserved1:
-      LOG(FATAL) << "attempted to assign a tExternal value";
-      break;
-    case tFloat:
-      fpoint = move.fpoint;
-      break;
-  }
-  return *this;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libexpr/value.hh b/third_party/nix/src/libexpr/value.hh
deleted file mode 100644
index 82021c77c4..0000000000
--- a/third_party/nix/src/libexpr/value.hh
+++ /dev/null
@@ -1,191 +0,0 @@
-#pragma once
-
-#include <tuple>
-#include <vector>
-
-#include "libexpr/symbol-table.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-using ValueType = enum {
-  tInt = 1,
-  tBool,
-  tString,
-  tPath,
-  tNull,
-  tAttrs,
-  tList,
-  tThunk,
-  tApp,
-  tLambda,
-  tBlackhole,
-  tPrimOp,
-  tPrimOpApp,
-  _reserved1,  // formerly tExternal
-  tFloat
-};
-
-class Bindings;
-struct Env;
-struct Expr;
-struct ExprLambda;
-struct PrimOp;
-struct PrimOp;
-class Symbol;
-
-typedef int64_t NixInt;
-typedef double NixFloat;
-
-// Forward declaration of Value is required because the following
-// types are mutually recursive.
-//
-// TODO(tazjin): Really, these types need some serious refactoring.
-struct Value;
-
-/* Strings in the evaluator carry a so-called `context' which
-   is a list of strings representing store paths.  This is to
-   allow users to write things like
-
-   "--with-freetype2-library=" + freetype + "/lib"
-
-   where `freetype' is a derivation (or a source to be copied
-   to the store).  If we just concatenated the strings without
-   keeping track of the referenced store paths, then if the
-   string is used as a derivation attribute, the derivation
-   will not have the correct dependencies in its inputDrvs and
-   inputSrcs.
-
-   The semantics of the context is as follows: when a string
-   with context C is used as a derivation attribute, then the
-   derivations in C will be added to the inputDrvs of the
-   derivation, and the other store paths in C will be added to
-   the inputSrcs of the derivations.
-
-   For canonicity, the store paths should be in sorted order. */
-struct NixString {
-  const char* s;
-  const char** context;  // must be in sorted order
-};
-
-struct NixThunk {
-  Env* env;
-  Expr* expr;
-};
-
-struct NixApp {
-  Value *left, *right;
-};
-
-struct NixLambda {
-  Env* env;
-  ExprLambda* fun;
-};
-
-struct NixPrimOpApp {
-  Value *left, *right;
-};
-
-using NixList = std::vector<Value*>;
-
-struct Value {
-  ValueType type;
-  union {  // TODO(tazjin): std::variant
-    NixInt integer;
-    bool boolean;
-    NixString string;
-    const char* path;
-    std::shared_ptr<Bindings> attrs;
-    std::shared_ptr<NixList> list;
-    NixThunk thunk;
-    NixApp app;  // TODO(tazjin): "app"?
-    NixLambda lambda;
-    std::shared_ptr<PrimOp> primOp;
-    NixPrimOpApp primOpApp;
-    NixFloat fpoint;
-  };
-
-  Value() : type(tInt), attrs(nullptr) {
-    static_assert(offsetof(Value, attrs) + sizeof(attrs) == sizeof(Value));
-  }
-
-  Value(const Value& copy);
-  Value(Value&& move);
-  ~Value() {}
-  Value& operator=(const Value& copy);
-  Value& operator=(Value&& move);
-
-  bool isList() const { return type == tList; }
-
-  size_t listSize() const { return list->size(); }
-};
-
-/* After overwriting an app node, be sure to clear pointers in the
-   Value to ensure that the target isn't kept alive unnecessarily. */
-static inline void clearValue(Value& v) { v.app.left = v.app.right = 0; }
-
-static inline void mkInt(Value& v, NixInt n) {
-  clearValue(v);
-  v.type = tInt;
-  v.integer = n;
-}
-
-static inline void mkFloat(Value& v, NixFloat n) {
-  clearValue(v);
-  v.type = tFloat;
-  v.fpoint = n;
-}
-
-static inline void mkBool(Value& v, bool b) {
-  clearValue(v);
-  v.type = tBool;
-  v.boolean = b;
-}
-
-static inline void mkNull(Value& v) {
-  clearValue(v);
-  v.type = tNull;
-}
-
-static inline void mkApp(Value& v, Value& left, Value& right) {
-  v.type = tApp;
-  v.app.left = &left;
-  v.app.right = &right;
-}
-
-static inline void mkPrimOpApp(Value& v, Value& left, Value& right) {
-  v.type = tPrimOpApp;
-  v.app.left = &left;
-  v.app.right = &right;
-}
-
-static inline void mkStringNoCopy(Value& v, const char* s) {
-  v.type = tString;
-  v.string.s = s;
-  v.string.context = 0;
-}
-
-static inline void mkString(Value& v, const Symbol& s) {
-  mkStringNoCopy(v, ((const std::string&)s).c_str());
-}
-
-void mkString(Value& v, const char* s);
-
-static inline void mkPathNoCopy(Value& v, const char* s) {
-  clearValue(v);
-  v.type = tPath;
-  v.path = s;
-}
-
-void mkPath(Value& v, const char* s);
-
-/* Compute the size in bytes of the given value, including all values
-   and environments reachable from it. Static expressions (Exprs) are
-   not included. */
-size_t valueSize(const Value& v);
-
-using ValueMap = std::map<Symbol, Value*>;
-
-std::shared_ptr<Value*> allocRootValue(Value* v);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libmain/CMakeLists.txt b/third_party/nix/src/libmain/CMakeLists.txt
deleted file mode 100644
index a95128c131..0000000000
--- a/third_party/nix/src/libmain/CMakeLists.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- mode: cmake; -*-
-add_library(nixmain SHARED)
-set_property(TARGET nixmain PROPERTY CXX_STANDARD 17)
-include_directories(${PROJECT_BINARY_DIR}) # for config.h
-target_include_directories(nixmain PUBLIC "${nix_SOURCE_DIR}/src")
-
-set(HEADER_FILES
-    common-args.hh
-    shared.hh
-)
-
-target_sources(nixmain
-  PUBLIC
-    ${HEADER_FILES}
-  PRIVATE
-    common-args.cc
-    shared.cc
-    stack.cc
-)
-
-target_link_libraries(nixmain
-  nixstore
-  nixutil
-
-  absl::strings
-  glog
-)
-
-configure_file("nix-main.pc.in" "${PROJECT_BINARY_DIR}/nix-main.pc" @ONLY)
-INSTALL(FILES "${PROJECT_BINARY_DIR}/nix-main.pc" DESTINATION "${PKGCONFIG_INSTALL_DIR}")
-
-INSTALL(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nix/libmain)
-INSTALL(TARGETS nixmain DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/third_party/nix/src/libmain/common-args.cc b/third_party/nix/src/libmain/common-args.cc
deleted file mode 100644
index 729e026f19..0000000000
--- a/third_party/nix/src/libmain/common-args.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "libmain/common-args.hh"
-
-#include <glog/logging.h>
-
-#include "libstore/globals.hh"
-
-namespace nix {
-
-MixCommonArgs::MixCommonArgs(const std::string& programName)
-    : programName(programName) {
-  mkFlag()
-      .longName("verbose")
-      .shortName('v')
-      .description("increase verbosity level")
-      .handler([]() {
-        FLAGS_stderrthreshold = google::GLOG_INFO;
-        FLAGS_v += 1;
-      });
-
-  mkFlag()
-      .longName("quiet")
-      .description("silence all log output")
-      .handler([]() { FLAGS_stderrthreshold = google::GLOG_FATAL; });
-
-  mkFlag()
-      .longName("option")
-      .labels({"name", "value"})
-      .description("set a Nix configuration option (overriding nix.conf)")
-      .arity(2)
-      .handler([](std::vector<std::string> ss) {
-        try {
-          globalConfig.set(ss[0], ss[1]);
-        } catch (UsageError& e) {
-          LOG(WARNING) << e.what();
-        }
-      });
-
-  mkFlag()
-      .longName("max-jobs")
-      .shortName('j')
-      .label("jobs")
-      .description("maximum number of parallel builds")
-      .handler([=](const std::string& s) { settings.set("max-jobs", s); });
-
-  std::string cat = "config";
-  globalConfig.convertToArgs(*this, cat);
-
-  // Backward compatibility hack: nix-env already had a --system flag.
-  if (programName == "nix-env") {
-    longFlags.erase("system");
-  }
-
-  hiddenCategories.insert(cat);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libmain/common-args.hh b/third_party/nix/src/libmain/common-args.hh
deleted file mode 100644
index f1c7c84813..0000000000
--- a/third_party/nix/src/libmain/common-args.hh
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include "libutil/args.hh"
-
-namespace nix {
-
-struct MixCommonArgs : virtual Args {
-  std::string programName;
-  MixCommonArgs(const std::string& programName);
-};
-
-struct MixDryRun : virtual Args {
-  bool dryRun = false;
-
-  MixDryRun() {
-    mkFlag(0, "dry-run", "show what this command would do without doing it",
-           &dryRun);
-  }
-};
-
-struct MixJSON : virtual Args {
-  bool json = false;
-
-  MixJSON() { mkFlag(0, "json", "produce JSON output", &json); }
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libmain/nix-main.pc.in b/third_party/nix/src/libmain/nix-main.pc.in
deleted file mode 100644
index 9876a3d1b7..0000000000
--- a/third_party/nix/src/libmain/nix-main.pc.in
+++ /dev/null
@@ -1,9 +0,0 @@
-prefix=@CMAKE_INSTALL_PREFIX@
-libdir=@CMAKE_INSTALL_FULL_LIBDIR@
-includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
-
-Name: Nix
-Description: Nix Package Manager
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lnixmain
-Cflags: -I${includedir}/nix
diff --git a/third_party/nix/src/libmain/shared.cc b/third_party/nix/src/libmain/shared.cc
deleted file mode 100644
index 331ea6b3a9..0000000000
--- a/third_party/nix/src/libmain/shared.cc
+++ /dev/null
@@ -1,386 +0,0 @@
-#include "libmain/shared.hh"
-
-#include <algorithm>
-#include <cctype>
-#include <csignal>
-#include <cstdlib>
-#include <exception>
-#include <iostream>
-#include <mutex>
-#include <utility>
-
-#include <glog/logging.h>
-#include <openssl/crypto.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-static bool gcWarning = true;
-
-void printGCWarning() {
-  if (!gcWarning) {
-    return;
-  }
-
-  static bool haveWarned = false;
-  if (!haveWarned) {
-    haveWarned = true;
-    LOG(WARNING) << "you did not specify '--add-root'; "
-                 << "the result might be removed by the garbage collector";
-  }
-}
-
-void printMissing(const ref<Store>& store, const PathSet& paths) {
-  unsigned long long downloadSize;
-  unsigned long long narSize;
-  PathSet willBuild;
-  PathSet willSubstitute;
-  PathSet unknown;
-  store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize,
-                      narSize);
-  printMissing(store, willBuild, willSubstitute, unknown, downloadSize,
-               narSize);
-}
-
-void printMissing(const ref<Store>& store, const PathSet& willBuild,
-                  const PathSet& willSubstitute, const PathSet& unknown,
-                  unsigned long long downloadSize, unsigned long long narSize) {
-  if (!willBuild.empty()) {
-    LOG(INFO) << "these derivations will be built:";
-    Paths sorted = store->topoSortPaths(willBuild);
-    reverse(sorted.begin(), sorted.end());
-    for (auto& i : sorted) {
-      LOG(INFO) << "  " << i;
-    }
-  }
-
-  if (!willSubstitute.empty()) {
-    LOG(INFO) << "these paths will be fetched ("
-              << (downloadSize / (1024.0 * 1024.0)) << " MiB download, "
-              << (narSize / (1024.0 * 1024.0)) << "MiB unpacked):";
-
-    for (auto& i : willSubstitute) {
-      LOG(INFO) << i;
-    }
-  }
-
-  if (!unknown.empty()) {
-    LOG(INFO) << "don't know how to build these paths"
-              << (settings.readOnlyMode
-                      ? " (may be caused by read-only store access)"
-                      : "")
-              << ":";
-
-    for (auto& i : unknown) {
-      LOG(INFO) << i;
-    }
-  }
-}
-
-std::string getArg(const std::string& opt, Strings::iterator& i,
-                   const Strings::iterator& end) {
-  ++i;
-  if (i == end) {
-    throw UsageError(format("'%1%' requires an argument") % opt);
-  }
-
-  return *i;
-}
-
-#if OPENSSL_VERSION_NUMBER < 0x10101000L
-/* OpenSSL is not thread-safe by default - it will randomly crash
-   unless the user supplies a mutex locking function. So let's do
-   that. */
-static std::vector<std::mutex> opensslLocks;
-
-static void opensslLockCallback(int mode, int type, const char* file,
-                                int line) {
-  if (mode & CRYPTO_LOCK)
-    opensslLocks[type].lock();
-  else
-    opensslLocks[type].unlock();
-}
-#endif
-
-static void sigHandler(int signo) {}
-
-void initNix() {
-  /* Turn on buffering for cerr. */
-#if HAVE_PUBSETBUF
-  static char buf[1024];
-  std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
-#endif
-
-#if OPENSSL_VERSION_NUMBER < 0x10101000L
-  /* Initialise OpenSSL locking. */
-  opensslLocks = std::vector<std::mutex>(CRYPTO_num_locks());
-  CRYPTO_set_locking_callback(opensslLockCallback);
-#endif
-
-  loadConfFile();
-
-  startSignalHandlerThread();
-
-  /* Reset SIGCHLD to its default. */
-  struct sigaction act;
-  sigemptyset(&act.sa_mask);
-  act.sa_handler = SIG_DFL;
-  act.sa_flags = 0;
-  if (sigaction(SIGCHLD, &act, nullptr) != 0) {
-    throw SysError("resetting SIGCHLD");
-  }
-
-  /* Install a dummy SIGUSR1 handler for use with pthread_kill(). */
-  act.sa_handler = sigHandler;
-  if (sigaction(SIGUSR1, &act, nullptr) != 0) {
-    throw SysError("handling SIGUSR1");
-  }
-
-  /* Register a SIGSEGV handler to detect stack overflows. */
-  detectStackOverflow();
-
-  /* There is no privacy in the Nix system ;-)  At least not for
-     now.  In particular, store objects should be readable by
-     everybody. */
-  umask(0022);
-
-  /* Initialise the PRNG. */
-  struct timeval tv;
-  gettimeofday(&tv, nullptr);
-  srandom(tv.tv_usec);
-}
-
-LegacyArgs::LegacyArgs(
-    const std::string& programName,
-    std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
-        parseArg)
-    : MixCommonArgs(programName), parseArg(std::move(parseArg)) {
-  mkFlag()
-      .longName("no-build-output")
-      .shortName('Q')
-      .description("do not show build output")
-      .set(&settings.verboseBuild, false);
-
-  mkFlag()
-      .longName("keep-failed")
-      .shortName('K')
-      .description("keep temporary directories of failed builds")
-      .set(&(bool&)settings.keepFailed, true);
-
-  mkFlag()
-      .longName("keep-going")
-      .shortName('k')
-      .description("keep going after a build fails")
-      .set(&(bool&)settings.keepGoing, true);
-
-  mkFlag()
-      .longName("fallback")
-      .description("build from source if substitution fails")
-      .set(&(bool&)settings.tryFallback, true);
-
-  auto intSettingAlias = [&](char shortName, const std::string& longName,
-                             const std::string& description,
-                             const std::string& dest) {
-    mkFlag<unsigned int>(shortName, longName, description, [=](unsigned int n) {
-      settings.set(dest, std::to_string(n));
-    });
-  };
-
-  intSettingAlias(0, "cores",
-                  "maximum number of CPU cores to use inside a build", "cores");
-  intSettingAlias(0, "max-silent-time",
-                  "number of seconds of silence before a build is killed",
-                  "max-silent-time");
-  intSettingAlias(0, "timeout", "number of seconds before a build is killed",
-                  "timeout");
-
-  mkFlag(0, "readonly-mode", "do not write to the Nix store",
-         &settings.readOnlyMode);
-
-  mkFlag(0, "no-gc-warning", "disable warning about not using '--add-root'",
-         &gcWarning, false);
-
-  mkFlag()
-      .longName("store")
-      .label("store-uri")
-      .description("URI of the Nix store to use")
-      .dest(&(std::string&)settings.storeUri);
-}
-
-bool LegacyArgs::processFlag(Strings::iterator& pos, Strings::iterator end) {
-  if (MixCommonArgs::processFlag(pos, end)) {
-    return true;
-  }
-  bool res = parseArg(pos, end);
-  if (res) {
-    ++pos;
-  }
-  return res;
-}
-
-bool LegacyArgs::processArgs(const Strings& args, bool finish) {
-  if (args.empty()) {
-    return true;
-  }
-  assert(args.size() == 1);
-  Strings ss(args);
-  auto pos = ss.begin();
-  if (!parseArg(pos, ss.end())) {
-    throw UsageError(format("unexpected argument '%1%'") % args.front());
-  }
-  return true;
-}
-
-void parseCmdLine(
-    int argc, char** argv,
-    std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
-        parseArg) {
-  parseCmdLine(baseNameOf(argv[0]), argvToStrings(argc, argv),
-               std::move(parseArg));
-}
-
-void parseCmdLine(
-    const std::string& programName, const Strings& args,
-    std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
-        parseArg) {
-  LegacyArgs(programName, std::move(parseArg)).parseCmdline(args);
-}
-
-void printVersion(const std::string& programName) {
-  std::cout << format("%1% (Tvix) %2%") % programName % nixVersion << std::endl;
-
-  // TODO(tazjin): figure out what the fuck this is
-  /*if (verbosity > lvlInfo) {
-    Strings cfg;
-#if HAVE_BOEHMGC
-    cfg.push_back("gc");
-#endif
-#if HAVE_SODIUM
-    cfg.push_back("signed-caches");
-#endif
-    std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n";
-    std::cout << "Configuration file: " << settings.nixConfDir + "/nix.conf"
-              << "\n";
-    std::cout << "Store directory: " << settings.nixStore << "\n";
-    std::cout << "State directory: " << settings.nixStateDir << "\n";
-    } */
-  throw Exit();
-}
-
-void showManPage(const std::string& name) {
-  restoreSignals();
-  setenv("MANPATH", settings.nixManDir.c_str(), 1);
-  execlp("man", "man", name.c_str(), nullptr);
-  throw SysError(format("command 'man %1%' failed") % name.c_str());
-}
-
-int handleExceptions(const std::string& programName,
-                     const std::function<void()>& fun) {
-  ReceiveInterrupts receiveInterrupts;  // FIXME: need better place for this
-
-  std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
-  try {
-    try {
-      fun();
-    } catch (...) {
-      /* Subtle: we have to make sure that any `interrupted'
-         condition is discharged before we reach printMsg()
-         below, since otherwise it will throw an (uncaught)
-         exception. */
-      setInterruptThrown();
-      throw;
-    }
-  } catch (Exit& e) {
-    return e.status;
-  } catch (UsageError& e) {
-    LOG(INFO) << e.what();
-    LOG(INFO) << "Try '" << programName << " --help' for more information.";
-    return 1;
-  } catch (BaseError& e) {
-    LOG(ERROR) << error << (settings.showTrace ? e.prefix() : "") << e.msg();
-    if (!e.prefix().empty() && !settings.showTrace) {
-      LOG(INFO) << "(use '--show-trace' to show detailed location information)";
-    }
-    return static_cast<int>(e.status);
-  } catch (std::bad_alloc& e) {
-    LOG(ERROR) << error << "failed to allocate: " << e.what();
-    return 1;
-  } catch (std::exception& e) {
-    LOG(ERROR) << error << e.what();
-    return 1;
-  }
-
-  return 0;
-}
-
-RunPager::RunPager() {
-  if (isatty(STDOUT_FILENO) == 0) {
-    return;
-  }
-  char* pager = getenv("NIX_PAGER");
-  if (pager == nullptr) {
-    pager = getenv("PAGER");
-  }
-  if (pager && (std::string(pager) == "" || std::string(pager) == "cat")) {
-    return;
-  }
-
-  Pipe toPager;
-  toPager.create();
-
-  pid = startProcess([&]() {
-    if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1) {
-      throw SysError("dupping stdin");
-    }
-    if (getenv("LESS") == nullptr) {
-      setenv("LESS", "FRSXMK", 1);
-    }
-    restoreSignals();
-    if (pager != nullptr) {
-      execl("/bin/sh", "sh", "-c", pager, nullptr);
-    }
-    execlp("pager", "pager", nullptr);
-    execlp("less", "less", nullptr);
-    execlp("more", "more", nullptr);
-    throw SysError(format("executing '%1%'") % pager);
-  });
-
-  pid.setKillSignal(SIGINT);
-
-  if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1) {
-    throw SysError("dupping stdout");
-  }
-}
-
-RunPager::~RunPager() {
-  try {
-    if (pid != Pid(-1)) {
-      std::cout.flush();
-      close(STDOUT_FILENO);
-      pid.wait();
-    }
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-std::string showBytes(unsigned long long bytes) {
-  return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str();
-}
-
-PrintFreed::~PrintFreed() {
-  if (show) {
-    std::cout << format("%1% store paths deleted, %2% freed\n") %
-                     results.paths.size() % showBytes(results.bytesFreed);
-  }
-}
-
-Exit::~Exit() = default;
-
-}  // namespace nix
diff --git a/third_party/nix/src/libmain/shared.hh b/third_party/nix/src/libmain/shared.hh
deleted file mode 100644
index d1061d5e04..0000000000
--- a/third_party/nix/src/libmain/shared.hh
+++ /dev/null
@@ -1,134 +0,0 @@
-#pragma once
-
-#include <locale>
-
-#include <absl/strings/numbers.h>
-#include <signal.h>
-
-#include "libmain/common-args.hh"
-#include "libutil/args.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-class Exit : public std::exception {
- public:
-  int status;
-  Exit() : status(0) {}
-  Exit(int status) : status(status) {}
-  virtual ~Exit();
-};
-
-int handleExceptions(const std::string& programName,
-                     const std::function<void()>& fun);
-
-void initNix();
-
-void parseCmdLine(
-    int argc, char** argv,
-    std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
-        parseArg);
-
-void parseCmdLine(
-    const std::string& programName, const Strings& args,
-    std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
-        parseArg);
-
-void printVersion(const std::string& programName);
-
-/* Ugh.  No better place to put this. */
-void printGCWarning();
-
-class Store;
-
-void printMissing(const ref<Store>& store, const PathSet& paths);
-
-void printMissing(const ref<Store>& store, const PathSet& willBuild,
-                  const PathSet& willSubstitute, const PathSet& unknown,
-                  unsigned long long downloadSize, unsigned long long narSize);
-
-std::string getArg(const std::string& opt, Strings::iterator& i,
-                   const Strings::iterator& end);
-
-template <class N>
-N getIntArg(const std::string& opt, Strings::iterator& i,
-            const Strings::iterator& end, bool allowUnit) {
-  ++i;
-  if (i == end) {
-    throw UsageError(format("'%1%' requires an argument") % opt);
-  }
-  std::string s = *i;
-  N multiplier = 1;
-  if (allowUnit && !s.empty()) {
-    char u = std::toupper(*s.rbegin());
-    if (std::isalpha(u)) {
-      if (u == 'K') {
-        multiplier = 1ULL << 10;
-      } else if (u == 'M') {
-        multiplier = 1ULL << 20;
-      } else if (u == 'G') {
-        multiplier = 1ULL << 30;
-      } else if (u == 'T') {
-        multiplier = 1ULL << 40;
-      } else {
-        throw UsageError(format("invalid unit specifier '%1%'") % u);
-      }
-
-      s.resize(s.size() - 1);
-    }
-  }
-  N n;
-  if (!absl::SimpleAtoi(s, &n)) {
-    throw UsageError(format("'%1%' requires an integer argument") % opt);
-  }
-  return n * multiplier;
-}
-
-struct LegacyArgs : public MixCommonArgs {
-  std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
-      parseArg;
-
-  LegacyArgs(
-      const std::string& programName,
-      std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
-          parseArg);
-
-  bool processFlag(Strings::iterator& pos, Strings::iterator end) override;
-
-  bool processArgs(const Strings& args, bool finish) override;
-};
-
-/* Show the manual page for the specified program. */
-void showManPage(const std::string& name);
-
-/* The constructor of this class starts a pager if stdout is a
-   terminal and $PAGER is set. Stdout is redirected to the pager. */
-class RunPager {
- public:
-  RunPager();
-  ~RunPager();
-
- private:
-  Pid pid;
-};
-
-extern volatile ::sig_atomic_t blockInt;
-
-/* GC helpers. */
-
-std::string showBytes(unsigned long long bytes);
-
-struct GCResults;
-
-struct PrintFreed {
-  bool show;
-  const GCResults& results;
-  PrintFreed(bool show, const GCResults& results)
-      : show(show), results(results) {}
-  ~PrintFreed();
-};
-
-/* Install a SIGSEGV handler to detect stack overflows. */
-void detectStackOverflow();
-
-}  // namespace nix
diff --git a/third_party/nix/src/libmain/stack.cc b/third_party/nix/src/libmain/stack.cc
deleted file mode 100644
index 628b6313a8..0000000000
--- a/third_party/nix/src/libmain/stack.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-#include <csignal>
-#include <cstddef>
-#include <cstdlib>
-#include <cstring>
-
-#include <unistd.h>
-
-#include "libutil/types.hh"
-
-namespace nix {
-
-static void sigsegvHandler(int signo, siginfo_t* info, void* ctx) {
-  /* Detect stack overflows by comparing the faulting address with
-     the stack pointer.  Unfortunately, getting the stack pointer is
-     not portable. */
-  bool haveSP = true;
-  char* sp = nullptr;
-#if defined(__x86_64__) && defined(REG_RSP)
-  sp = (char*)(static_cast<ucontext_t*>(ctx))->uc_mcontext.gregs[REG_RSP];
-#elif defined(REG_ESP)
-  sp = (char*)((ucontext_t*)ctx)->uc_mcontext.gregs[REG_ESP];
-#else
-  haveSP = false;
-#endif
-
-  if (haveSP) {
-    ptrdiff_t diff = static_cast<char*>(info->si_addr) - sp;
-    if (diff < 0) {
-      diff = -diff;
-    }
-    if (diff < 4096) {
-      char msg[] = "error: stack overflow (possible infinite recursion)\n";
-      [[gnu::unused]] auto res = write(2, msg, strlen(msg));
-      _exit(1);  // maybe abort instead?
-    }
-  }
-
-  /* Restore default behaviour (i.e. segfault and dump core). */
-  struct sigaction act;
-  sigfillset(&act.sa_mask);
-  act.sa_handler = SIG_DFL;
-  act.sa_flags = 0;
-  if (sigaction(SIGSEGV, &act, nullptr) != 0) {
-    abort();
-  }
-}
-
-void detectStackOverflow() {
-#if defined(SA_SIGINFO) && defined(SA_ONSTACK)
-  /* Install a SIGSEGV handler to detect stack overflows.  This
-     requires an alternative stack, otherwise the signal cannot be
-     delivered when we're out of stack space. */
-  stack_t stack;
-  stack.ss_size = 4096 * 4 + MINSIGSTKSZ;
-  static auto stackBuf = std::make_unique<std::vector<char>>(stack.ss_size);
-  stack.ss_sp = stackBuf->data();
-  if (stack.ss_sp == nullptr) {
-    throw Error("cannot allocate alternative stack");
-  }
-  stack.ss_flags = 0;
-  if (sigaltstack(&stack, nullptr) == -1) {
-    throw SysError("cannot set alternative stack");
-  }
-
-  struct sigaction act;
-  sigfillset(&act.sa_mask);
-  act.sa_sigaction = sigsegvHandler;
-  act.sa_flags = SA_SIGINFO | SA_ONSTACK;
-  if (sigaction(SIGSEGV, &act, nullptr) != 0) {
-    throw SysError("resetting SIGSEGV");
-  }
-#endif
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/CMakeLists.txt b/third_party/nix/src/libstore/CMakeLists.txt
deleted file mode 100644
index 246377cc9b..0000000000
--- a/third_party/nix/src/libstore/CMakeLists.txt
+++ /dev/null
@@ -1,127 +0,0 @@
-# -*- mode: cmake; -*-
-add_library(nixstore SHARED)
-add_library(nixstoremock SHARED)
-set_property(TARGET nixstore PROPERTY CXX_STANDARD 17)
-set_property(TARGET nixstoremock PROPERTY CXX_STANDARD 17)
-include_directories(${PROJECT_BINARY_DIR}) # for config.h
-target_include_directories(nixstore PUBLIC "${nix_SOURCE_DIR}/src")
-target_include_directories(nixstoremock PUBLIC "${nix_SOURCE_DIR}/src")
-
-# The database schema is stored in schema.sql, but needs to be
-# available during the build as static data.
-#
-# These commands create an includeable source-file out of it.
-file(READ "schema.sql" NIX_SCHEMA)
-
-string(CONFIGURE
-  "#pragma once
-   namespace nix {
-   constexpr char kNixSqlSchema[] = R\"(${NIX_SCHEMA})\";
-   }"
-  NIX_SCHEMA_GEN)
-
-file(WRITE ${PROJECT_BINARY_DIR}/generated/schema.sql.hh "${NIX_SCHEMA_GEN}")
-
-set(HEADER_FILES
-    binary-cache-store.hh
-    builtins.hh
-    crypto.hh
-    derivations.hh
-    download.hh
-    fs-accessor.hh
-    globals.hh
-    local-store.hh
-    machines.hh
-    nar-accessor.hh
-    nar-info-disk-cache.hh
-    nar-info.hh
-    parsed-derivations.hh
-    pathlocks.hh
-    profiles.hh
-    references.hh
-    remote-fs-accessor.hh
-    remote-store.hh
-    rpc-store.hh
-    s3-binary-cache-store.hh
-    s3.hh
-    serve-protocol.hh
-    sqlite.hh
-    ssh.hh
-    store-api.hh
-    worker-protocol.hh
-)
-
-target_sources(nixstore
-  PUBLIC
-    ${HEADER_FILES}
-
-  PRIVATE
-    ${PROJECT_BINARY_DIR}/generated/schema.sql.hh
-    binary-cache-store.cc
-    build.cc
-    crypto.cc
-    derivations.cc
-    download.cc
-    export-import.cc
-    gc.cc
-    globals.cc
-    http-binary-cache-store.cc
-    legacy-ssh-store.cc
-    local-binary-cache-store.cc
-    local-fs-store.cc
-    local-store.cc
-    machines.cc
-    misc.cc
-    nar-accessor.cc
-    nar-info.cc
-    nar-info-disk-cache.cc
-    optimise-store.cc
-    parsed-derivations.cc
-    pathlocks.cc
-    profiles.cc
-    references.cc
-    remote-fs-accessor.cc
-    remote-store.cc
-    rpc-store.cc
-    s3-binary-cache-store.cc
-    sqlite.cc
-    ssh.cc
-    ssh-store.cc
-    store-api.cc
-    builtins/buildenv.cc
-    builtins/fetchurl.cc
-)
-
-target_link_libraries(nixstore
-  nixproto
-  nixutil
-
-  CURL::libcurl
-  SQLite::SQLite3
-  absl::strings
-  glog
-  seccomp
-  sodium
-)
-
-target_sources(nixstoremock
-  PUBLIC
-    mock-binary-cache-store.hh
-
-  PRIVATE
-    mock-binary-cache-store.cc
-)
-
-target_link_libraries(nixstoremock
-  nixstore
-
-  absl::btree
-  absl::flat_hash_map
-  glog
-)
-
-configure_file("nix-store.pc.in" "${PROJECT_BINARY_DIR}/nix-store.pc" @ONLY)
-INSTALL(FILES "${PROJECT_BINARY_DIR}/nix-store.pc" DESTINATION "${PKGCONFIG_INSTALL_DIR}")
-
-INSTALL(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nix/libstore)
-INSTALL(TARGETS nixstore nixstoremock DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/third_party/nix/src/libstore/binary-cache-store.cc b/third_party/nix/src/libstore/binary-cache-store.cc
deleted file mode 100644
index 0b04e972da..0000000000
--- a/third_party/nix/src/libstore/binary-cache-store.cc
+++ /dev/null
@@ -1,396 +0,0 @@
-#include "libstore/binary-cache-store.hh"
-
-#include <chrono>
-#include <future>
-#include <memory>
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/numbers.h>
-#include <absl/strings/str_split.h>
-#include <glog/logging.h>
-
-#include "libstore/derivations.hh"
-#include "libstore/fs-accessor.hh"
-#include "libstore/globals.hh"
-#include "libstore/nar-accessor.hh"
-#include "libstore/nar-info-disk-cache.hh"
-#include "libstore/nar-info.hh"
-#include "libstore/remote-fs-accessor.hh"
-#include "libutil/archive.hh"
-#include "libutil/compression.hh"
-#include "libutil/json.hh"
-#include "libutil/sync.hh"
-
-namespace nix {
-
-BinaryCacheStore::BinaryCacheStore(const Params& params) : Store(params) {
-  if (secretKeyFile != "") {
-    const std::string& secret_key_file = secretKeyFile;
-    secretKey = std::make_unique<SecretKey>(readFile(secret_key_file));
-  }
-
-  StringSink sink;
-  sink << std::string(kNarVersionMagic1);
-  narMagic = *sink.s;
-}
-
-void BinaryCacheStore::init() {
-  std::string cacheInfoFile = "nix-cache-info";
-
-  auto cacheInfo = getFile(cacheInfoFile);
-  if (!cacheInfo) {
-    upsertFile(cacheInfoFile, "StoreDir: " + storeDir + "\n",
-               "text/x-nix-cache-info");
-  } else {
-    for (auto& line :
-         absl::StrSplit(*cacheInfo, absl::ByChar('\n'), absl::SkipEmpty())) {
-      size_t colon = line.find(':');
-      if (colon == std::string::npos) {
-        continue;
-      }
-      auto name = line.substr(0, colon);
-      auto value =
-          absl::StripAsciiWhitespace(line.substr(colon + 1, std::string::npos));
-      if (name == "StoreDir") {
-        if (value != storeDir) {
-          throw Error(format("binary cache '%s' is for Nix stores with prefix "
-                             "'%s', not '%s'") %
-                      getUri() % value % storeDir);
-        }
-      } else if (name == "WantMassQuery") {
-        wantMassQuery_ = value == "1";
-      } else if (name == "Priority") {
-        if (!absl::SimpleAtoi(value, &priority)) {
-          LOG(WARNING) << "Invalid 'Priority' value: " << value;
-        }
-      }
-    }
-  }
-}
-
-void BinaryCacheStore::getFile(
-    const std::string& path,
-    Callback<std::shared_ptr<std::string>> callback) noexcept {
-  try {
-    callback(getFile(path));
-  } catch (...) {
-    callback.rethrow();
-  }
-}
-
-void BinaryCacheStore::getFile(const std::string& path, Sink& sink) {
-  std::promise<std::shared_ptr<std::string>> promise;
-  getFile(path, Callback<std::shared_ptr<std::string>>{
-                    [&](std::future<std::shared_ptr<std::string>> result) {
-                      try {
-                        promise.set_value(result.get());
-                      } catch (...) {
-                        promise.set_exception(std::current_exception());
-                      }
-                    }});
-  auto data = promise.get_future().get();
-  sink(reinterpret_cast<unsigned char*>(data->data()), data->size());
-}
-
-std::shared_ptr<std::string> BinaryCacheStore::getFile(
-    const std::string& path) {
-  StringSink sink;
-  try {
-    getFile(path, sink);
-  } catch (NoSuchBinaryCacheFile&) {
-    return nullptr;
-  }
-  return sink.s;
-}
-
-Path BinaryCacheStore::narInfoFileFor(const Path& storePath) {
-  assertStorePath(storePath);
-  return storePathToHash(storePath) + ".narinfo";
-}
-
-void BinaryCacheStore::writeNarInfo(const ref<NarInfo>& narInfo) {
-  auto narInfoFile = narInfoFileFor(narInfo->path);
-
-  upsertFile(narInfoFile, narInfo->to_string(), "text/x-nix-narinfo");
-
-  auto hashPart = storePathToHash(narInfo->path);
-
-  {
-    auto state_(state.lock());
-    state_->pathInfoCache.upsert(hashPart, std::shared_ptr<NarInfo>(narInfo));
-  }
-
-  if (diskCache) {
-    diskCache->upsertNarInfo(getUri(), hashPart,
-                             std::shared_ptr<NarInfo>(narInfo));
-  }
-}
-
-void BinaryCacheStore::addToStore(const ValidPathInfo& info,
-                                  const ref<std::string>& nar,
-                                  RepairFlag repair, CheckSigsFlag checkSigs,
-                                  std::shared_ptr<FSAccessor> accessor) {
-  if ((repair == 0u) && isValidPath(info.path)) {
-    return;
-  }
-
-  /* Verify that all references are valid. This may do some .narinfo
-     reads, but typically they'll already be cached. */
-  for (auto& ref : info.references) {
-    try {
-      if (ref != info.path) {
-        queryPathInfo(ref);
-      }
-    } catch (InvalidPath&) {
-      throw Error(format("cannot add '%s' to the binary cache because the "
-                         "reference '%s' is not valid") %
-                  info.path % ref);
-    }
-  }
-
-  assert(nar->compare(0, narMagic.size(), narMagic) == 0);
-
-  auto narInfo = make_ref<NarInfo>(info);
-
-  narInfo->narSize = nar->size();
-  narInfo->narHash = hashString(htSHA256, *nar);
-
-  if (info.narHash && info.narHash != narInfo->narHash) {
-    throw Error(
-        format("refusing to copy corrupted path '%1%' to binary cache") %
-        info.path);
-  }
-
-  auto accessor_ = std::dynamic_pointer_cast<RemoteFSAccessor>(accessor);
-
-  /* Optionally write a JSON file containing a listing of the
-     contents of the NAR. */
-  if (writeNARListing) {
-    std::ostringstream jsonOut;
-
-    {
-      JSONObject jsonRoot(jsonOut);
-      jsonRoot.attr("version", 1);
-
-      auto narAccessor = makeNarAccessor(nar);
-
-      if (accessor_) {
-        accessor_->addToCache(info.path, *nar, narAccessor);
-      }
-
-      {
-        auto res = jsonRoot.placeholder("root");
-        listNar(res, narAccessor, "", true);
-      }
-    }
-
-    upsertFile(storePathToHash(info.path) + ".ls", jsonOut.str(),
-               "application/json");
-  }
-
-  else {
-    if (accessor_) {
-      accessor_->addToCache(info.path, *nar, makeNarAccessor(nar));
-    }
-  }
-
-  /* Compress the NAR. */
-  narInfo->compression = compression;
-  auto now1 = std::chrono::steady_clock::now();
-  auto narCompressed = compress(compression, *nar, parallelCompression);
-  auto now2 = std::chrono::steady_clock::now();
-  narInfo->fileHash = hashString(htSHA256, *narCompressed);
-  narInfo->fileSize = narCompressed->size();
-
-  auto duration =
-      std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1)
-          .count();
-  DLOG(INFO) << "copying path '" << narInfo->path << "' (" << narInfo->narSize
-             << " bytes, compressed "
-             << ((1.0 -
-                  static_cast<double>(narCompressed->size()) / nar->size()) *
-                 100.0)
-             << "% in " << duration << "ms) to binary cache";
-
-  /* Atomically write the NAR file. */
-  narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar" +
-                 (compression == "xz"      ? ".xz"
-                  : compression == "bzip2" ? ".bz2"
-                  : compression == "br"    ? ".br"
-                                           : "");
-  if ((repair != 0u) || !fileExists(narInfo->url)) {
-    stats.narWrite++;
-    upsertFile(narInfo->url, *narCompressed, "application/x-nix-nar");
-  } else {
-    stats.narWriteAverted++;
-  }
-
-  stats.narWriteBytes += nar->size();
-  stats.narWriteCompressedBytes += narCompressed->size();
-  stats.narWriteCompressionTimeMs += duration;
-
-  /* Atomically write the NAR info file.*/
-  if (secretKey) {
-    narInfo->sign(*secretKey);
-  }
-
-  writeNarInfo(narInfo);
-
-  stats.narInfoWrite++;
-}
-
-bool BinaryCacheStore::isValidPathUncached(const Path& storePath) {
-  // FIXME: this only checks whether a .narinfo with a matching hash
-  // part exists. So β€˜f4kb...-foo’ matches β€˜f4kb...-bar’, even
-  // though they shouldn't. Not easily fixed.
-  return fileExists(narInfoFileFor(storePath));
-}
-
-void BinaryCacheStore::narFromPath(const Path& storePath, Sink& sink) {
-  auto info = queryPathInfo(storePath).cast<const NarInfo>();
-
-  uint64_t narSize = 0;
-
-  LambdaSink wrapperSink([&](const unsigned char* data, size_t len) {
-    sink(data, len);
-    narSize += len;
-  });
-
-  auto decompressor = makeDecompressionSink(info->compression, wrapperSink);
-
-  try {
-    getFile(info->url, *decompressor);
-  } catch (NoSuchBinaryCacheFile& e) {
-    throw SubstituteGone(e.what());
-  }
-
-  decompressor->finish();
-
-  stats.narRead++;
-  // stats.narReadCompressedBytes += nar->size(); // FIXME
-  stats.narReadBytes += narSize;
-}
-
-void BinaryCacheStore::queryPathInfoUncached(
-    const Path& storePath,
-    Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept {
-  auto uri = getUri();
-  LOG(INFO) << "querying info about '" << storePath << "' on '" << uri << "'";
-
-  auto narInfoFile = narInfoFileFor(storePath);
-
-  auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
-
-  getFile(narInfoFile,
-          Callback<std::shared_ptr<std::string>>(
-              [=](std::future<std::shared_ptr<std::string>> fut) {
-                try {
-                  auto data = fut.get();
-
-                  if (!data) {
-                    return (*callbackPtr)(nullptr);
-                  }
-
-                  stats.narInfoRead++;
-
-                  (*callbackPtr)(std::shared_ptr<ValidPathInfo>(
-                      std::make_shared<NarInfo>(*this, *data, narInfoFile)));
-
-                } catch (...) {
-                  callbackPtr->rethrow();
-                }
-              }));
-}
-
-Path BinaryCacheStore::addToStore(const std::string& name, const Path& srcPath,
-                                  bool recursive, HashType hashAlgo,
-                                  PathFilter& filter, RepairFlag repair) {
-  // FIXME: some cut&paste from LocalStore::addToStore().
-
-  /* Read the whole path into memory. This is not a very scalable
-     method for very large paths, but `copyPath' is mainly used for
-     small files. */
-  StringSink sink;
-  Hash h;
-  if (recursive) {
-    dumpPath(srcPath, sink, filter);
-    h = hashString(hashAlgo, *sink.s);
-  } else {
-    auto s = readFile(srcPath);
-    dumpString(s, sink);
-    h = hashString(hashAlgo, s);
-  }
-
-  ValidPathInfo info;
-  info.path = makeFixedOutputPath(recursive, h, name);
-
-  addToStore(info, sink.s, repair, CheckSigs, nullptr);
-
-  return info.path;
-}
-
-Path BinaryCacheStore::addTextToStore(const std::string& name,
-                                      const std::string& s,
-                                      const PathSet& references,
-                                      RepairFlag repair) {
-  ValidPathInfo info;
-  info.path = computeStorePathForText(name, s, references);
-  info.references = references;
-
-  if ((repair != 0u) || !isValidPath(info.path)) {
-    StringSink sink;
-    dumpString(s, sink);
-    addToStore(info, sink.s, repair, CheckSigs, nullptr);
-  }
-
-  return info.path;
-}
-
-ref<FSAccessor> BinaryCacheStore::getFSAccessor() {
-  return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()),
-                                    localNarCache);
-}
-
-void BinaryCacheStore::addSignatures(const Path& storePath,
-                                     const StringSet& sigs) {
-  /* Note: this is inherently racy since there is no locking on
-     binary caches. In particular, with S3 this unreliable, even
-     when addSignatures() is called sequentially on a path, because
-     S3 might return an outdated cached version. */
-
-  auto narInfo = make_ref<NarInfo>((NarInfo&)*queryPathInfo(storePath));
-
-  narInfo->sigs.insert(sigs.begin(), sigs.end());
-
-  auto narInfoFile = narInfoFileFor(narInfo->path);
-
-  writeNarInfo(narInfo);
-}
-
-std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const Path& path) {
-  Path drvPath;
-
-  if (isDerivation(path)) {
-    drvPath = path;
-  } else {
-    try {
-      auto info = queryPathInfo(path);
-      // FIXME: add a "Log" field to .narinfo
-      if (info->deriver.empty()) {
-        return nullptr;
-      }
-      drvPath = info->deriver;
-    } catch (InvalidPath&) {
-      return nullptr;
-    }
-  }
-
-  auto logPath = "log/" + baseNameOf(drvPath);
-
-  DLOG(INFO) << "fetching build log from binary cache '" << getUri() << "/"
-             << logPath << "'";
-
-  return getFile(logPath);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/binary-cache-store.hh b/third_party/nix/src/libstore/binary-cache-store.hh
deleted file mode 100644
index 40c636f60a..0000000000
--- a/third_party/nix/src/libstore/binary-cache-store.hh
+++ /dev/null
@@ -1,115 +0,0 @@
-#pragma once
-
-#include <atomic>
-
-#include "libstore/crypto.hh"
-#include "libstore/store-api.hh"
-#include "libutil/pool.hh"
-
-namespace nix {
-
-struct NarInfo;
-
-class BinaryCacheStore : public Store {
- public:
-  const Setting<std::string> compression{
-      this, "xz", "compression",
-      "NAR compression method ('xz', 'bzip2', or 'none')"};
-  const Setting<bool> writeNARListing{
-      this, false, "write-nar-listing",
-      "whether to write a JSON file listing the files in each NAR"};
-  const Setting<Path> secretKeyFile{
-      this, "", "secret-key",
-      "path to secret key used to sign the binary cache"};
-  const Setting<Path> localNarCache{this, "", "local-nar-cache",
-                                    "path to a local cache of NARs"};
-  const Setting<bool> parallelCompression{
-      this, false, "parallel-compression",
-      "enable multi-threading compression, available for xz only currently"};
-
- private:
-  std::unique_ptr<SecretKey> secretKey;
-
- protected:
-  BinaryCacheStore(const Params& params);
-
- public:
-  virtual bool fileExists(const std::string& path) = 0;
-
-  virtual void upsertFile(const std::string& path, const std::string& data,
-                          const std::string& mimeType) = 0;
-
-  /* Note: subclasses must implement at least one of the two
-     following getFile() methods. */
-
-  /* Dump the contents of the specified file to a sink. */
-  virtual void getFile(const std::string& path, Sink& sink);
-
-  /* Fetch the specified file and call the specified callback with
-     the result. A subclass may implement this asynchronously. */
-  virtual void getFile(
-      const std::string& path,
-      Callback<std::shared_ptr<std::string>> callback) noexcept;
-
-  std::shared_ptr<std::string> getFile(const std::string& path);
-
- protected:
-  bool wantMassQuery_ = false;
-  int priority = 50;
-
- public:
-  virtual void init();
-
- private:
-  std::string narMagic;
-
-  std::string narInfoFileFor(const Path& storePath);
-
-  void writeNarInfo(const ref<NarInfo>& narInfo);
-
- public:
-  bool isValidPathUncached(const Path& path) override;
-
-  void queryPathInfoUncached(
-      const Path& path,
-      Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept override;
-
-  Path queryPathFromHashPart(const std::string& hashPart) override {
-    unsupported("queryPathFromHashPart");
-  }
-
-  bool wantMassQuery() override { return wantMassQuery_; }
-
-  void addToStore(const ValidPathInfo& info, const ref<std::string>& nar,
-                  RepairFlag repair, CheckSigsFlag checkSigs,
-                  std::shared_ptr<FSAccessor> accessor) override;
-
-  Path addToStore(const std::string& name, const Path& srcPath, bool recursive,
-                  HashType hashAlgo, PathFilter& filter,
-                  RepairFlag repair) override;
-
-  Path addTextToStore(const std::string& name, const std::string& s,
-                      const PathSet& references, RepairFlag repair) override;
-
-  void narFromPath(const Path& path, Sink& sink) override;
-
-  BuildResult buildDerivation(std::ostream& /*log_sink*/, const Path& drvPath,
-                              const BasicDerivation& drv,
-                              BuildMode buildMode) override {
-    unsupported("buildDerivation");
-  }
-
-  void ensurePath(const Path& path) override { unsupported("ensurePath"); }
-
-  ref<FSAccessor> getFSAccessor() override;
-
-  void addSignatures(const Path& storePath, const StringSet& sigs) override;
-
-  std::shared_ptr<std::string> getBuildLog(const Path& path) override;
-
-  int getPriority() override { return priority; }
-};
-
-MakeError(NoSuchBinaryCacheFile, Error);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/build.cc b/third_party/nix/src/libstore/build.cc
deleted file mode 100644
index 1f5752a168..0000000000
--- a/third_party/nix/src/libstore/build.cc
+++ /dev/null
@@ -1,4820 +0,0 @@
-#include <algorithm>
-#include <cerrno>
-#include <chrono>
-#include <climits>
-#include <cstring>
-#include <future>
-#include <iostream>
-#include <map>
-#include <memory>
-#include <ostream>
-#include <queue>
-#include <regex>
-#include <sstream>
-#include <string>
-#include <thread>
-
-#include <absl/status/status.h>
-#include <absl/strings/ascii.h>
-#include <absl/strings/numbers.h>
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_format.h>
-#include <absl/strings/str_split.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <grp.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <sys/resource.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/utsname.h>
-#include <sys/wait.h>
-#include <termios.h>
-#include <unistd.h>
-
-#include "libstore/builtins.hh"
-#include "libstore/download.hh"
-#include "libstore/globals.hh"
-#include "libstore/local-store.hh"
-#include "libstore/machines.hh"
-#include "libstore/nar-info.hh"
-#include "libstore/parsed-derivations.hh"
-#include "libstore/pathlocks.hh"
-#include "libstore/references.hh"
-#include "libstore/store-api.hh"
-#include "libutil/affinity.hh"
-#include "libutil/archive.hh"
-#include "libutil/compression.hh"
-#include "libutil/finally.hh"
-#include "libutil/json.hh"
-#include "libutil/util.hh"
-
-/* Includes required for chroot support. */
-#if __linux__
-#include <net/if.h>
-#include <netinet/ip.h>
-#include <sched.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/param.h>
-#include <sys/personality.h>
-#include <sys/socket.h>
-#include <sys/syscall.h>
-#if HAVE_SECCOMP
-#include <seccomp.h>
-#endif
-#define pivot_root(new_root, put_old) \
-  (syscall(SYS_pivot_root, new_root, put_old))
-#endif
-
-#if HAVE_STATVFS
-#include <sys/statvfs.h>
-#endif
-
-#include <nlohmann/json.hpp>
-#include <utility>
-
-namespace nix {
-
-constexpr std::string_view kPathNullDevice = "/dev/null";
-
-/* Forward definition. */
-class Worker;
-struct HookInstance;
-
-/* A pointer to a goal. */
-class Goal;
-class DerivationGoal;
-using GoalPtr = std::shared_ptr<Goal>;
-using WeakGoalPtr = std::weak_ptr<Goal>;
-
-struct CompareGoalPtrs {
-  bool operator()(const GoalPtr& a, const GoalPtr& b) const;
-};
-
-/* Set of goals. */
-using Goals = std::set<GoalPtr, CompareGoalPtrs>;
-using WeakGoals = std::list<WeakGoalPtr>;
-
-/* A map of paths to goals (and the other way around). */
-using WeakGoalMap = std::map<Path, WeakGoalPtr>;
-
-class Goal : public std::enable_shared_from_this<Goal> {
- public:
-  using ExitCode = enum {
-    ecBusy,
-    ecSuccess,
-    ecFailed,
-    ecNoSubstituters,
-    ecIncompleteClosure
-  };
-
- protected:
-  /* Backlink to the worker. */
-  Worker& worker;
-
-  /* Goals that this goal is waiting for. */
-  Goals waitees;
-
-  /* Goals waiting for this one to finish.  Must use weak pointers
-     here to prevent cycles. */
-  WeakGoals waiters;
-
-  /* Number of goals we are/were waiting for that have failed. */
-  unsigned int nrFailed;
-
-  /* Number of substitution goals we are/were waiting for that
-     failed because there are no substituters. */
-  unsigned int nrNoSubstituters;
-
-  /* Number of substitution goals we are/were waiting for that
-     failed because othey had unsubstitutable references. */
-  unsigned int nrIncompleteClosure;
-
-  /* Name of this goal for debugging purposes. */
-  std::string name;
-
-  /* Whether the goal is finished. */
-  ExitCode exitCode;
-
-  // Output stream for build logs.
-  // TODO(tazjin): Rename all build_log instances to log_sink.
-  std::ostream& log_sink() const;
-
-  explicit Goal(Worker& worker) : worker(worker) {
-    nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
-    exitCode = ecBusy;
-  }
-
-  virtual ~Goal() { trace("goal destroyed"); }
-
- public:
-  virtual void work() = 0;
-
-  void addWaitee(const GoalPtr& waitee);
-
-  virtual void waiteeDone(GoalPtr waitee, ExitCode result);
-
-  virtual void handleChildOutput(int fd, const std::string& data) { abort(); }
-
-  virtual void handleEOF(int fd) { abort(); }
-
-  void trace(const FormatOrString& fs);
-
-  std::string getName() { return name; }
-
-  ExitCode getExitCode() { return exitCode; }
-
-  /* Callback in case of a timeout.  It should wake up its waiters,
-     get rid of any running child processes that are being monitored
-     by the worker (important!), etc. */
-  virtual void timedOut() = 0;
-
-  virtual std::string key() = 0;
-
- protected:
-  virtual void amDone(ExitCode result);
-};
-
-bool CompareGoalPtrs::operator()(const GoalPtr& a, const GoalPtr& b) const {
-  std::string s1 = a->key();
-  std::string s2 = b->key();
-  return s1 < s2;
-}
-
-using steady_time_point = std::chrono::time_point<std::chrono::steady_clock>;
-
-/* A mapping used to remember for each child process to what goal it
-   belongs, and file descriptors for receiving log data and output
-   path creation commands. */
-struct Child {
-  WeakGoalPtr goal;
-  Goal* goal2;  // ugly hackery
-  std::set<int> fds;
-  bool respectTimeouts;
-  bool inBuildSlot;
-  steady_time_point lastOutput; /* time we last got output on stdout/stderr */
-  steady_time_point timeStarted;
-};
-
-/* The worker class. */
-class Worker {
- private:
-  /* Note: the worker should only have strong pointers to the
-     top-level goals. */
-
-  /* The top-level goals of the worker. */
-  Goals topGoals;
-
-  /* Goals that are ready to do some work. */
-  WeakGoals awake;
-
-  /* Goals waiting for a build slot. */
-  WeakGoals wantingToBuild;
-
-  /* Child processes currently running. */
-  std::list<Child> children;
-
-  /* Number of build slots occupied.  This includes local builds and
-     substitutions but not remote builds via the build hook. */
-  unsigned int nrLocalBuilds;
-
-  /* Maps used to prevent multiple instantiations of a goal for the
-     same derivation / path. */
-  WeakGoalMap derivationGoals;
-  WeakGoalMap substitutionGoals;
-
-  /* Goals waiting for busy paths to be unlocked. */
-  WeakGoals waitingForAnyGoal;
-
-  /* Goals sleeping for a few seconds (polling a lock). */
-  WeakGoals waitingForAWhile;
-
-  /* Last time the goals in `waitingForAWhile' where woken up. */
-  steady_time_point lastWokenUp;
-
-  /* Cache for pathContentsGood(). */
-  std::map<Path, bool> pathContentsGoodCache;
-
-  std::ostream& log_sink_;
-
- public:
-  /* Set if at least one derivation had a BuildError (i.e. permanent
-     failure). */
-  bool permanentFailure;
-
-  /* Set if at least one derivation had a timeout. */
-  bool timedOut;
-
-  /* Set if at least one derivation fails with a hash mismatch. */
-  bool hashMismatch;
-
-  /* Set if at least one derivation is not deterministic in check mode. */
-  bool checkMismatch;
-
-  LocalStore& store;
-
-  std::unique_ptr<HookInstance> hook;
-
-  uint64_t expectedBuilds = 0;
-  uint64_t doneBuilds = 0;
-  uint64_t failedBuilds = 0;
-  uint64_t runningBuilds = 0;
-
-  uint64_t expectedSubstitutions = 0;
-  uint64_t doneSubstitutions = 0;
-  uint64_t failedSubstitutions = 0;
-  uint64_t runningSubstitutions = 0;
-  uint64_t expectedDownloadSize = 0;
-  uint64_t doneDownloadSize = 0;
-  uint64_t expectedNarSize = 0;
-  uint64_t doneNarSize = 0;
-
-  /* Whether to ask the build hook if it can build a derivation. If
-     it answers with "decline-permanently", we don't try again. */
-  bool tryBuildHook = true;
-
-  Worker(LocalStore& store, std::ostream& log_sink);
-  ~Worker();
-
-  /* Make a goal (with caching). */
-  GoalPtr makeDerivationGoal(const Path& drvPath,
-                             const StringSet& wantedOutputs,
-                             BuildMode buildMode);
-
-  std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
-      const Path& drvPath, const BasicDerivation& drv, BuildMode buildMode);
-
-  GoalPtr makeSubstitutionGoal(const Path& storePath,
-                               RepairFlag repair = NoRepair);
-
-  /* Remove a dead goal. */
-  void removeGoal(const GoalPtr& goal);
-
-  /* Wake up a goal (i.e., there is something for it to do). */
-  void wakeUp(const GoalPtr& goal);
-
-  /* Return the number of local build and substitution processes
-     currently running (but not remote builds via the build
-     hook). */
-  unsigned int getNrLocalBuilds();
-
-  /* Registers a running child process.  `inBuildSlot' means that
-     the process counts towards the jobs limit. */
-  void childStarted(const GoalPtr& goal, const std::set<int>& fds,
-                    bool inBuildSlot, bool respectTimeouts);
-
-  /* Unregisters a running child process.  `wakeSleepers' should be
-     false if there is no sense in waking up goals that are sleeping
-     because they can't run yet (e.g., there is no free build slot,
-     or the hook would still say `postpone'). */
-  void childTerminated(Goal* goal, bool wakeSleepers = true);
-
-  /* Put `goal' to sleep until a build slot becomes available (which
-     might be right away). */
-  void waitForBuildSlot(const GoalPtr& goal);
-
-  /* Wait for any goal to finish.  Pretty indiscriminate way to
-     wait for some resource that some other goal is holding. */
-  void waitForAnyGoal(GoalPtr goal);
-
-  /* Wait for a few seconds and then retry this goal.  Used when
-     waiting for a lock held by another process.  This kind of
-     polling is inefficient, but POSIX doesn't really provide a way
-     to wait for multiple locks in the main select() loop. */
-  void waitForAWhile(GoalPtr goal);
-
-  /* Loop until the specified top-level goals have finished. */
-  void run(const Goals& topGoals);
-
-  /* Wait for input to become available. */
-  void waitForInput();
-
-  unsigned int exitStatus();
-
-  /* Check whether the given valid path exists and has the right
-     contents. */
-  bool pathContentsGood(const Path& path);
-
-  void markContentsGood(const Path& path);
-
-  std::ostream& log_sink() const { return log_sink_; };
-};
-
-//////////////////////////////////////////////////////////////////////
-
-void addToWeakGoals(WeakGoals& goals, const GoalPtr& p) {
-  // FIXME: necessary?
-  // FIXME: O(n)
-  for (auto& i : goals) {
-    if (i.lock() == p) {
-      return;
-    }
-  }
-  goals.push_back(p);
-}
-
-std::ostream& Goal::log_sink() const { return worker.log_sink(); }
-
-void Goal::addWaitee(const GoalPtr& waitee) {
-  waitees.insert(waitee);
-  addToWeakGoals(waitee->waiters, shared_from_this());
-}
-
-void Goal::waiteeDone(GoalPtr waitee, ExitCode result) {
-  assert(waitees.find(waitee) != waitees.end());
-  waitees.erase(waitee);
-
-  trace(format("waitee '%1%' done; %2% left") % waitee->name % waitees.size());
-
-  if (result == ecFailed || result == ecNoSubstituters ||
-      result == ecIncompleteClosure) {
-    ++nrFailed;
-  }
-
-  if (result == ecNoSubstituters) {
-    ++nrNoSubstituters;
-  }
-
-  if (result == ecIncompleteClosure) {
-    ++nrIncompleteClosure;
-  }
-
-  if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) {
-    /* If we failed and keepGoing is not set, we remove all
-       remaining waitees. */
-    for (auto& goal : waitees) {
-      WeakGoals waiters2;
-      for (auto& j : goal->waiters) {
-        if (j.lock() != shared_from_this()) {
-          waiters2.push_back(j);
-        }
-      }
-      goal->waiters = waiters2;
-    }
-    waitees.clear();
-
-    worker.wakeUp(shared_from_this());
-  }
-}
-
-void Goal::amDone(ExitCode result) {
-  trace("done");
-  assert(exitCode == ecBusy);
-  assert(result == ecSuccess || result == ecFailed ||
-         result == ecNoSubstituters || result == ecIncompleteClosure);
-  exitCode = result;
-  for (auto& i : waiters) {
-    GoalPtr goal = i.lock();
-    if (goal) {
-      goal->waiteeDone(shared_from_this(), result);
-    }
-  }
-  waiters.clear();
-  worker.removeGoal(shared_from_this());
-}
-
-void Goal::trace(const FormatOrString& fs) {
-  DLOG(INFO) << name << ": " << fs.s;
-}
-
-//////////////////////////////////////////////////////////////////////
-
-/* Common initialisation performed in child processes. */
-static void commonChildInit(Pipe& logPipe) {
-  restoreSignals();
-
-  /* Put the child in a separate session (and thus a separate
-     process group) so that it has no controlling terminal (meaning
-     that e.g. ssh cannot open /dev/tty) and it doesn't receive
-     terminal signals. */
-  if (setsid() == -1) {
-    throw SysError(format("creating a new session"));
-  }
-
-  /* Dup the write side of the logger pipe into stderr. */
-  if (dup2(logPipe.writeSide.get(), STDERR_FILENO) == -1) {
-    throw SysError("cannot pipe standard error into log file");
-  }
-
-  /* Dup stderr to stdout. */
-  if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1) {
-    throw SysError("cannot dup stderr into stdout");
-  }
-
-  /* Reroute stdin to /dev/null. */
-  int fdDevNull = open(kPathNullDevice.begin(), O_RDWR);
-  if (fdDevNull == -1) {
-    throw SysError(format("cannot open '%1%'") % kPathNullDevice);
-  }
-  if (dup2(fdDevNull, STDIN_FILENO) == -1) {
-    throw SysError("cannot dup null device into stdin");
-  }
-  close(fdDevNull);
-}
-
-void handleDiffHook(uid_t uid, uid_t gid, Path tryA, Path tryB, Path drvPath,
-                    Path tmpDir, std::ostream& log_sink) {
-  auto diffHook = settings.diffHook;
-  if (diffHook != "" && settings.runDiffHook) {
-    try {
-      RunOptions diffHookOptions(
-          diffHook, {std::move(tryA), std::move(tryB), std::move(drvPath),
-                     std::move(tmpDir)});
-      diffHookOptions.searchPath = true;
-      diffHookOptions.uid = uid;
-      diffHookOptions.gid = gid;
-      diffHookOptions.chdir = "/";
-
-      auto diffRes = runProgram(diffHookOptions);
-      if (!statusOk(diffRes.first)) {
-        throw ExecError(diffRes.first,
-                        fmt("diff-hook program '%1%' %2%", diffHook,
-                            statusToString(diffRes.first)));
-      }
-
-      if (!diffRes.second.empty()) {
-        log_sink << absl::StripTrailingAsciiWhitespace(diffRes.second);
-      }
-    } catch (Error& error) {
-      log_sink << "diff hook execution failed: " << error.what();
-    }
-  }
-}
-
-//////////////////////////////////////////////////////////////////////
-
-class UserLock {
- private:
-  /* POSIX locks suck.  If we have a lock on a file, and we open and
-     close that file again (without closing the original file
-     descriptor), we lose the lock.  So we have to be *very* careful
-     not to open a lock file on which we are holding a lock. */
-  static Sync<PathSet> lockedPaths_;
-
-  Path fnUserLock;
-  AutoCloseFD fdUserLock;
-
-  std::string user;
-  uid_t uid;
-  gid_t gid;
-  std::vector<gid_t> supplementaryGIDs;
-
- public:
-  UserLock();
-  ~UserLock();
-
-  void kill();
-
-  std::string getUser() { return user; }
-  uid_t getUID() {
-    assert(uid);
-    return uid;
-  }
-  uid_t getGID() {
-    assert(gid);
-    return gid;
-  }
-  std::vector<gid_t> getSupplementaryGIDs() { return supplementaryGIDs; }
-
-  bool enabled() { return uid != 0; }
-};
-
-Sync<PathSet> UserLock::lockedPaths_;
-
-UserLock::UserLock() {
-  assert(settings.buildUsersGroup != "");
-
-  /* Get the members of the build-users-group. */
-  struct group* gr = getgrnam(settings.buildUsersGroup.get().c_str());
-  if (gr == nullptr) {
-    throw Error(
-        format(
-            "the group '%1%' specified in 'build-users-group' does not exist") %
-        settings.buildUsersGroup);
-  }
-  gid = gr->gr_gid;
-
-  /* Copy the result of getgrnam. */
-  Strings users;
-  for (char** p = gr->gr_mem; *p != nullptr; ++p) {
-    DLOG(INFO) << "found build user " << *p;
-    users.push_back(*p);
-  }
-
-  if (users.empty()) {
-    throw Error(format("the build users group '%1%' has no members") %
-                settings.buildUsersGroup);
-  }
-
-  /* Find a user account that isn't currently in use for another
-     build. */
-  for (auto& i : users) {
-    DLOG(INFO) << "trying user " << i;
-
-    struct passwd* pw = getpwnam(i.c_str());
-    if (pw == nullptr) {
-      throw Error(format("the user '%1%' in the group '%2%' does not exist") %
-                  i % settings.buildUsersGroup);
-    }
-
-    createDirs(settings.nixStateDir + "/userpool");
-
-    fnUserLock =
-        (format("%1%/userpool/%2%") % settings.nixStateDir % pw->pw_uid).str();
-
-    {
-      auto lockedPaths(lockedPaths_.lock());
-      if (lockedPaths->count(fnUserLock) != 0u) {
-        /* We already have a lock on this one. */
-        continue;
-      }
-      lockedPaths->insert(fnUserLock);
-    }
-
-    try {
-      AutoCloseFD fd(
-          open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600));
-      if (!fd) {
-        throw SysError(format("opening user lock '%1%'") % fnUserLock);
-      }
-
-      if (lockFile(fd.get(), ltWrite, false)) {
-        fdUserLock = std::move(fd);
-        user = i;
-        uid = pw->pw_uid;
-
-        /* Sanity check... */
-        if (uid == getuid() || uid == geteuid()) {
-          throw Error(format("the Nix user should not be a member of '%1%'") %
-                      settings.buildUsersGroup);
-        }
-
-#if __linux__
-        /* Get the list of supplementary groups of this build user.  This
-           is usually either empty or contains a group such as "kvm".  */
-        supplementaryGIDs.resize(10);
-        int ngroups = supplementaryGIDs.size();
-        int err = getgrouplist(pw->pw_name, pw->pw_gid,
-                               supplementaryGIDs.data(), &ngroups);
-        if (err == -1) {
-          throw Error(
-              format("failed to get list of supplementary groups for '%1%'") %
-              pw->pw_name);
-        }
-
-        supplementaryGIDs.resize(ngroups);
-#endif
-
-        return;
-      }
-
-    } catch (...) {
-      lockedPaths_.lock()->erase(fnUserLock);
-    }
-  }
-
-  throw Error(format("all build users are currently in use; "
-                     "consider creating additional users and adding them to "
-                     "the '%1%' group") %
-              settings.buildUsersGroup);
-}
-
-UserLock::~UserLock() {
-  auto lockedPaths(lockedPaths_.lock());
-  assert(lockedPaths->count(fnUserLock));
-  lockedPaths->erase(fnUserLock);
-}
-
-void UserLock::kill() { killUser(uid); }
-
-//////////////////////////////////////////////////////////////////////
-
-struct HookInstance {
-  /* Pipes for talking to the build hook. */
-  Pipe toHook;
-
-  /* Pipe for the hook's standard output/error. */
-  Pipe fromHook;
-
-  /* Pipe for the builder's standard output/error. */
-  Pipe builderOut;
-
-  /* The process ID of the hook. */
-  Pid pid;
-
-  FdSink sink;
-
-  HookInstance();
-
-  ~HookInstance();
-};
-
-HookInstance::HookInstance() {
-  DLOG(INFO) << "starting build hook " << settings.buildHook;
-
-  /* Create a pipe to get the output of the child. */
-  fromHook.create();
-
-  /* Create the communication pipes. */
-  toHook.create();
-
-  /* Create a pipe to get the output of the builder. */
-  builderOut.create();
-
-  /* Fork the hook. */
-  pid = startProcess([&]() {
-    commonChildInit(fromHook);
-
-    if (chdir("/") == -1) {
-      throw SysError("changing into /");
-    }
-
-    /* Dup the communication pipes. */
-    if (dup2(toHook.readSide.get(), STDIN_FILENO) == -1) {
-      throw SysError("dupping to-hook read side");
-    }
-
-    /* Use fd 4 for the builder's stdout/stderr. */
-    if (dup2(builderOut.writeSide.get(), 4) == -1) {
-      throw SysError("dupping builder's stdout/stderr");
-    }
-
-    /* Hack: pass the read side of that fd to allow build-remote
-       to read SSH error messages. */
-    if (dup2(builderOut.readSide.get(), 5) == -1) {
-      throw SysError("dupping builder's stdout/stderr");
-    }
-
-    Strings args = {
-        baseNameOf(settings.buildHook),
-        // std::to_string(verbosity), // TODO(tazjin): what?
-    };
-
-    execv(settings.buildHook.get().c_str(), stringsToCharPtrs(args).data());
-
-    throw SysError("executing '%s'", settings.buildHook);
-  });
-
-  pid.setSeparatePG(true);
-  fromHook.writeSide = AutoCloseFD(-1);
-  toHook.readSide = AutoCloseFD(-1);
-
-  sink = FdSink(toHook.writeSide.get());
-  std::map<std::string, Config::SettingInfo> settings;
-  globalConfig.getSettings(settings);
-  for (auto& setting : settings) {
-    sink << 1 << setting.first << setting.second.value;
-  }
-  sink << 0;
-}
-
-HookInstance::~HookInstance() {
-  try {
-    toHook.writeSide = AutoCloseFD(-1);
-    if (pid != Pid(-1)) {
-      pid.kill();
-    }
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-//////////////////////////////////////////////////////////////////////
-
-using StringRewrites = std::map<std::string, std::string>;
-
-std::string rewriteStrings(std::string s, const StringRewrites& rewrites) {
-  for (auto& i : rewrites) {
-    size_t j = 0;
-    while ((j = s.find(i.first, j)) != std::string::npos) {
-      s.replace(j, i.first.size(), i.second);
-    }
-  }
-  return s;
-}
-
-//////////////////////////////////////////////////////////////////////
-
-using HookReply = enum { rpAccept, rpDecline, rpPostpone };
-
-class SubstitutionGoal;
-
-class DerivationGoal : public Goal {
- private:
-  /* Whether to use an on-disk .drv file. */
-  bool useDerivation;
-
-  /* The path of the derivation. */
-  Path drvPath;
-
-  /* The specific outputs that we need to build.  Empty means all of
-     them. */
-  StringSet wantedOutputs;
-
-  /* Whether additional wanted outputs have been added. */
-  bool needRestart = false;
-
-  /* Whether to retry substituting the outputs after building the
-     inputs. */
-  bool retrySubstitution;
-
-  /* The derivation stored at drvPath. */
-  std::unique_ptr<BasicDerivation> drv;
-
-  std::unique_ptr<ParsedDerivation> parsedDrv;
-
-  /* The remainder is state held during the build. */
-
-  /* Locks on the output paths. */
-  PathLocks outputLocks;
-
-  /* All input paths (that is, the union of FS closures of the
-     immediate input paths). */
-  PathSet inputPaths;
-
-  /* Referenceable paths (i.e., input and output paths). */
-  PathSet allPaths;
-
-  /* Outputs that are already valid.  If we're repairing, these are
-     the outputs that are valid *and* not corrupt. */
-  PathSet validPaths;
-
-  /* Outputs that are corrupt or not valid. */
-  PathSet missingPaths;
-
-  /* User selected for running the builder. */
-  std::unique_ptr<UserLock> buildUser;
-
-  /* The process ID of the builder. */
-  Pid pid;
-
-  /* The temporary directory. */
-  Path tmpDir;
-
-  /* The path of the temporary directory in the sandbox. */
-  Path tmpDirInSandbox;
-
-  /* File descriptor for the log file. */
-  AutoCloseFD fdLogFile;
-  std::shared_ptr<BufferedSink> logFileSink, logSink;
-
-  /* Number of bytes received from the builder's stdout/stderr. */
-  unsigned long logSize;
-
-  /* The most recent log lines. */
-  std::list<std::string> logTail;
-
-  std::string currentLogLine;
-  size_t currentLogLinePos = 0;  // to handle carriage return
-
-  std::string currentHookLine;
-
-  /* Pipe for the builder's standard output/error. */
-  Pipe builderOut;
-
-  /* Pipe for synchronising updates to the builder user namespace. */
-  Pipe userNamespaceSync;
-
-  /* The build hook. */
-  std::unique_ptr<HookInstance> hook;
-
-  /* Whether we're currently doing a chroot build. */
-  bool useChroot = false;
-
-  Path chrootRootDir;
-
-  /* RAII object to delete the chroot directory. */
-  std::shared_ptr<AutoDelete> autoDelChroot;
-
-  /* Whether this is a fixed-output derivation. */
-  bool fixedOutput;
-
-  /* Whether to run the build in a private network namespace. */
-  bool privateNetwork = false;
-
-  using GoalState = void (DerivationGoal::*)();
-  GoalState state;
-
-  /* Stuff we need to pass to initChild(). */
-  struct ChrootPath {
-    Path source;
-    bool optional;
-    explicit ChrootPath(Path source = "", bool optional = false)
-        : source(std::move(source)), optional(optional) {}
-  };
-  using DirsInChroot =
-      std::map<Path, ChrootPath>;  // maps target path to source path
-  DirsInChroot dirsInChroot;
-
-  using Environment = std::map<std::string, std::string>;
-  Environment env;
-
-  /* Hash rewriting. */
-  StringRewrites inputRewrites, outputRewrites;
-  using RedirectedOutputs = std::map<Path, Path>;
-  RedirectedOutputs redirectedOutputs;
-
-  BuildMode buildMode;
-
-  /* If we're repairing without a chroot, there may be outputs that
-     are valid but corrupt.  So we redirect these outputs to
-     temporary paths. */
-  PathSet redirectedBadOutputs;
-
-  BuildResult result;
-
-  /* The current round, if we're building multiple times. */
-  size_t curRound = 1;
-
-  size_t nrRounds;
-
-  /* Path registration info from the previous round, if we're
-     building multiple times. Since this contains the hash, it
-     allows us to compare whether two rounds produced the same
-     result. */
-  std::map<Path, ValidPathInfo> prevInfos;
-
-  const uid_t sandboxUid = 1000;
-  const gid_t sandboxGid = 100;
-
-  const static Path homeDir;
-
-  std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds, mcRunningBuilds;
-
-  /* The remote machine on which we're building. */
-  std::string machineName;
-
- public:
-  DerivationGoal(Worker& worker, const Path& drvPath, StringSet wantedOutputs,
-                 BuildMode buildMode);
-
-  DerivationGoal(Worker& worker, const Path& drvPath,
-                 const BasicDerivation& drv, BuildMode buildMode);
-
-  ~DerivationGoal() override;
-
-  /* Whether we need to perform hash rewriting if there are valid output paths.
-   */
-  bool needsHashRewrite();
-
-  void timedOut() override;
-
-  std::string key() override {
-    /* Ensure that derivations get built in order of their name,
-       i.e. a derivation named "aardvark" always comes before
-       "baboon". And substitution goals always happen before
-       derivation goals (due to "b$"). */
-    return "b$" + storePathToName(drvPath) + "$" + drvPath;
-  }
-
-  void work() override;
-
-  Path getDrvPath() { return drvPath; }
-
-  /* Add wanted outputs to an already existing derivation goal. */
-  void addWantedOutputs(const StringSet& outputs);
-
-  BuildResult getResult() { return result; }
-
- private:
-  /* The states. */
-  void getDerivation();
-  void loadDerivation();
-  void haveDerivation();
-  void outputsSubstituted();
-  void closureRepaired();
-  void inputsRealised();
-  void tryToBuild();
-  void buildDone();
-
-  /* Is the build hook willing to perform the build? */
-  HookReply tryBuildHook();
-
-  /* Start building a derivation. */
-  void startBuilder();
-
-  /* Fill in the environment for the builder. */
-  void initEnv();
-
-  /* Setup tmp dir location. */
-  void initTmpDir();
-
-  /* Write a JSON file containing the derivation attributes. */
-  void writeStructuredAttrs();
-
-  /* Make a file owned by the builder. */
-  void chownToBuilder(const Path& path);
-
-  /* Run the builder's process. */
-  void runChild();
-
-  friend int childEntry(void* /*arg*/);
-
-  /* Check that the derivation outputs all exist and register them
-     as valid. */
-  void registerOutputs();
-
-  /* Check that an output meets the requirements specified by the
-     'outputChecks' attribute (or the legacy
-     '{allowed,disallowed}{References,Requisites}' attributes). */
-  void checkOutputs(const std::map<std::string, ValidPathInfo>& outputs);
-
-  /* Open a log file and a pipe to it. */
-  Path openLogFile();
-
-  /* Close the log file. */
-  void closeLogFile();
-
-  /* Delete the temporary directory, if we have one. */
-  void deleteTmpDir(bool force);
-
-  /* Callback used by the worker to write to the log. */
-  void handleChildOutput(int fd, const std::string& data) override;
-  void handleEOF(int fd) override;
-  void flushLine();
-
-  /* Return the set of (in)valid paths. */
-  PathSet checkPathValidity(bool returnValid, bool checkHash);
-
-  /* Abort the goal if `path' failed to build. */
-  bool pathFailed(const Path& path);
-
-  /* Forcibly kill the child process, if any. */
-  void killChild();
-
-  Path addHashRewrite(const Path& path);
-
-  void repairClosure();
-
-  void amDone(ExitCode result) override { Goal::amDone(result); }
-
-  void done(BuildResult::Status status, const std::string& msg = "");
-
-  PathSet exportReferences(const PathSet& storePaths);
-};
-
-const Path DerivationGoal::homeDir = "/homeless-shelter";
-
-DerivationGoal::DerivationGoal(Worker& worker, const Path& drvPath,
-                               StringSet wantedOutputs, BuildMode buildMode)
-    : Goal(worker),
-      useDerivation(true),
-      drvPath(drvPath),
-      wantedOutputs(std::move(wantedOutputs)),
-      buildMode(buildMode) {
-  state = &DerivationGoal::getDerivation;
-  name = (format("building of '%1%'") % drvPath).str();
-  trace("created");
-
-  mcExpectedBuilds =
-      std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
-}
-
-DerivationGoal::DerivationGoal(Worker& worker, const Path& drvPath,
-                               const BasicDerivation& drv, BuildMode buildMode)
-    : Goal(worker),
-      useDerivation(false),
-      drvPath(drvPath),
-      buildMode(buildMode) {
-  this->drv = std::make_unique<BasicDerivation>(drv);
-  state = &DerivationGoal::haveDerivation;
-  name = (format("building of %1%") % showPaths(drv.outputPaths())).str();
-  trace("created");
-
-  mcExpectedBuilds =
-      std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
-
-  /* Prevent the .chroot directory from being
-     garbage-collected. (See isActiveTempFile() in gc.cc.) */
-  worker.store.addTempRoot(drvPath);
-}
-
-DerivationGoal::~DerivationGoal() {
-  /* Careful: we should never ever throw an exception from a
-     destructor. */
-  try {
-    killChild();
-  } catch (...) {
-    ignoreException();
-  }
-  try {
-    deleteTmpDir(false);
-  } catch (...) {
-    ignoreException();
-  }
-  try {
-    closeLogFile();
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-inline bool DerivationGoal::needsHashRewrite() { return !useChroot; }
-
-void DerivationGoal::killChild() {
-  if (pid != Pid(-1)) {
-    worker.childTerminated(this);
-
-    if (buildUser) {
-      /* If we're using a build user, then there is a tricky
-         race condition: if we kill the build user before the
-         child has done its setuid() to the build user uid, then
-         it won't be killed, and we'll potentially lock up in
-         pid.wait().  So also send a conventional kill to the
-         child. */
-      ::kill(-static_cast<pid_t>(pid), SIGKILL); /* ignore the result */
-      buildUser->kill();
-      pid.wait();
-    } else {
-      pid.kill();
-    }
-
-    assert(pid == Pid(-1));
-  }
-
-  hook.reset();
-}
-
-void DerivationGoal::timedOut() {
-  killChild();
-  done(BuildResult::TimedOut);
-}
-
-void DerivationGoal::work() { (this->*state)(); }
-
-void DerivationGoal::addWantedOutputs(const StringSet& outputs) {
-  /* If we already want all outputs, there is nothing to do. */
-  if (wantedOutputs.empty()) {
-    return;
-  }
-
-  if (outputs.empty()) {
-    wantedOutputs.clear();
-    needRestart = true;
-  } else {
-    for (auto& i : outputs) {
-      if (wantedOutputs.find(i) == wantedOutputs.end()) {
-        wantedOutputs.insert(i);
-        needRestart = true;
-      }
-    }
-  }
-}
-
-void DerivationGoal::getDerivation() {
-  trace("init");
-
-  /* The first thing to do is to make sure that the derivation
-     exists.  If it doesn't, it may be created through a
-     substitute. */
-  if (buildMode == bmNormal && worker.store.isValidPath(drvPath)) {
-    loadDerivation();
-    return;
-  }
-
-  addWaitee(worker.makeSubstitutionGoal(drvPath));
-
-  state = &DerivationGoal::loadDerivation;
-}
-
-void DerivationGoal::loadDerivation() {
-  trace("loading derivation");
-
-  if (nrFailed != 0) {
-    log_sink() << "cannot build missing derivation '" << drvPath << "'"
-               << std::endl;
-    done(BuildResult::MiscFailure);
-    return;
-  }
-
-  /* `drvPath' should already be a root, but let's be on the safe
-     side: if the user forgot to make it a root, we wouldn't want
-     things being garbage collected while we're busy. */
-  worker.store.addTempRoot(drvPath);
-
-  assert(worker.store.isValidPath(drvPath));
-
-  /* Get the derivation. */
-  drv = std::unique_ptr<BasicDerivation>(
-      new Derivation(worker.store.derivationFromPath(drvPath)));
-
-  haveDerivation();
-}
-
-void DerivationGoal::haveDerivation() {
-  trace("have derivation");
-
-  retrySubstitution = false;
-
-  for (auto& i : drv->outputs) {
-    worker.store.addTempRoot(i.second.path);
-  }
-
-  /* Check what outputs paths are not already valid. */
-  PathSet invalidOutputs = checkPathValidity(false, buildMode == bmRepair);
-
-  /* If they are all valid, then we're done. */
-  if (invalidOutputs.empty() && buildMode == bmNormal) {
-    done(BuildResult::AlreadyValid);
-    return;
-  }
-
-  parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
-
-  /* We are first going to try to create the invalid output paths
-     through substitutes.  If that doesn't work, we'll build
-     them. */
-  if (settings.useSubstitutes && parsedDrv->substitutesAllowed()) {
-    for (auto& i : invalidOutputs) {
-      addWaitee(worker.makeSubstitutionGoal(
-          i, buildMode == bmRepair ? Repair : NoRepair));
-    }
-  }
-
-  if (waitees.empty()) { /* to prevent hang (no wake-up event) */
-    outputsSubstituted();
-  } else {
-    state = &DerivationGoal::outputsSubstituted;
-  }
-}
-
-void DerivationGoal::outputsSubstituted() {
-  trace("all outputs substituted (maybe)");
-
-  if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure &&
-      !settings.tryFallback) {
-    done(BuildResult::TransientFailure,
-         (format("some substitutes for the outputs of derivation '%1%' failed "
-                 "(usually happens due to networking issues); try '--fallback' "
-                 "to build derivation from source ") %
-          drvPath)
-             .str());
-    return;
-  }
-
-  /*  If the substitutes form an incomplete closure, then we should
-      build the dependencies of this derivation, but after that, we
-      can still use the substitutes for this derivation itself. */
-  if (nrIncompleteClosure > 0) {
-    retrySubstitution = true;
-  }
-
-  nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
-
-  if (needRestart) {
-    needRestart = false;
-    haveDerivation();
-    return;
-  }
-
-  auto nrInvalid = checkPathValidity(false, buildMode == bmRepair).size();
-  if (buildMode == bmNormal && nrInvalid == 0) {
-    done(BuildResult::Substituted);
-    return;
-  }
-  if (buildMode == bmRepair && nrInvalid == 0) {
-    repairClosure();
-    return;
-  }
-  if (buildMode == bmCheck && nrInvalid > 0) {
-    throw Error(format("some outputs of '%1%' are not valid, so checking is "
-                       "not possible") %
-                drvPath);
-  }
-
-  /* Otherwise, at least one of the output paths could not be
-     produced using a substitute.  So we have to build instead. */
-
-  /* Make sure checkPathValidity() from now on checks all
-     outputs. */
-  wantedOutputs = PathSet();
-
-  /* The inputs must be built before we can build this goal. */
-  if (useDerivation) {
-    for (auto& i : dynamic_cast<Derivation*>(drv.get())->inputDrvs) {
-      addWaitee(worker.makeDerivationGoal(
-          i.first, i.second, buildMode == bmRepair ? bmRepair : bmNormal));
-    }
-  }
-
-  for (auto& i : drv->inputSrcs) {
-    if (worker.store.isValidPath(i)) {
-      continue;
-    }
-    if (!settings.useSubstitutes) {
-      throw Error(format("dependency '%1%' of '%2%' does not exist, and "
-                         "substitution is disabled") %
-                  i % drvPath);
-    }
-    addWaitee(worker.makeSubstitutionGoal(i));
-  }
-
-  if (waitees.empty()) { /* to prevent hang (no wake-up event) */
-    inputsRealised();
-  } else {
-    state = &DerivationGoal::inputsRealised;
-  }
-}
-
-void DerivationGoal::repairClosure() {
-  /* If we're repairing, we now know that our own outputs are valid.
-     Now check whether the other paths in the outputs closure are
-     good.  If not, then start derivation goals for the derivations
-     that produced those outputs. */
-
-  /* Get the output closure. */
-  PathSet outputClosure;
-  for (auto& i : drv->outputs) {
-    if (!wantOutput(i.first, wantedOutputs)) {
-      continue;
-    }
-    worker.store.computeFSClosure(i.second.path, outputClosure);
-  }
-
-  /* Filter out our own outputs (which we have already checked). */
-  for (auto& i : drv->outputs) {
-    outputClosure.erase(i.second.path);
-  }
-
-  /* Get all dependencies of this derivation so that we know which
-     derivation is responsible for which path in the output
-     closure. */
-  PathSet inputClosure;
-  if (useDerivation) {
-    worker.store.computeFSClosure(drvPath, inputClosure);
-  }
-  std::map<Path, Path> outputsToDrv;
-  for (auto& i : inputClosure) {
-    if (isDerivation(i)) {
-      Derivation drv = worker.store.derivationFromPath(i);
-      for (auto& j : drv.outputs) {
-        outputsToDrv[j.second.path] = i;
-      }
-    }
-  }
-
-  /* Check each path (slow!). */
-  PathSet broken;
-  for (auto& i : outputClosure) {
-    if (worker.pathContentsGood(i)) {
-      continue;
-    }
-    log_sink() << "found corrupted or missing path '" << i
-               << "' in the output closure of '" << drvPath << "'" << std::endl;
-    Path drvPath2 = outputsToDrv[i];
-    if (drvPath2.empty()) {
-      addWaitee(worker.makeSubstitutionGoal(i, Repair));
-    } else {
-      addWaitee(worker.makeDerivationGoal(drvPath2, PathSet(), bmRepair));
-    }
-  }
-
-  if (waitees.empty()) {
-    done(BuildResult::AlreadyValid);
-    return;
-  }
-
-  state = &DerivationGoal::closureRepaired;
-}
-
-void DerivationGoal::closureRepaired() {
-  trace("closure repaired");
-  if (nrFailed > 0) {
-    throw Error(format("some paths in the output closure of derivation '%1%' "
-                       "could not be repaired") %
-                drvPath);
-  }
-  done(BuildResult::AlreadyValid);
-}
-
-void DerivationGoal::inputsRealised() {
-  trace("all inputs realised");
-
-  if (nrFailed != 0) {
-    if (!useDerivation) {
-      throw Error(format("some dependencies of '%1%' are missing") % drvPath);
-    }
-    log_sink() << "cannot build derivation '" << drvPath << "': " << nrFailed
-               << " dependencies couldn't be built" << std::endl;
-    done(BuildResult::DependencyFailed);
-    return;
-  }
-
-  if (retrySubstitution) {
-    haveDerivation();
-    return;
-  }
-
-  /* Gather information necessary for computing the closure and/or
-     running the build hook. */
-
-  /* The outputs are referenceable paths. */
-  for (auto& i : drv->outputs) {
-    log_sink() << "building path " << i.second.path << std::endl;
-    allPaths.insert(i.second.path);
-  }
-
-  /* Determine the full set of input paths. */
-
-  /* First, the input derivations. */
-  if (useDerivation) {
-    for (auto& i : dynamic_cast<Derivation*>(drv.get())->inputDrvs) {
-      /* Add the relevant output closures of the input derivation
-         `i' as input paths.  Only add the closures of output paths
-         that are specified as inputs. */
-      assert(worker.store.isValidPath(i.first));
-      Derivation inDrv = worker.store.derivationFromPath(i.first);
-      for (auto& j : i.second) {
-        if (inDrv.outputs.find(j) != inDrv.outputs.end()) {
-          worker.store.computeFSClosure(inDrv.outputs[j].path, inputPaths);
-        } else {
-          throw Error(format("derivation '%1%' requires non-existent output "
-                             "'%2%' from input derivation '%3%'") %
-                      drvPath % j % i.first);
-        }
-      }
-    }
-  }
-
-  /* Second, the input sources. */
-  worker.store.computeFSClosure(drv->inputSrcs, inputPaths);
-
-  DLOG(INFO) << "added input paths " << showPaths(inputPaths);
-
-  allPaths.insert(inputPaths.begin(), inputPaths.end());
-
-  /* Is this a fixed-output derivation? */
-  fixedOutput = drv->isFixedOutput();
-
-  /* Don't repeat fixed-output derivations since they're already
-     verified by their output hash.*/
-  nrRounds = fixedOutput ? 1 : settings.buildRepeat + 1;
-
-  /* Okay, try to build.  Note that here we don't wait for a build
-     slot to become available, since we don't need one if there is a
-     build hook. */
-  state = &DerivationGoal::tryToBuild;
-  worker.wakeUp(shared_from_this());
-
-  result = BuildResult();
-}
-
-void DerivationGoal::tryToBuild() {
-  trace("trying to build");
-
-  /* Obtain locks on all output paths.  The locks are automatically
-     released when we exit this function or Nix crashes.  If we
-     can't acquire the lock, then continue; hopefully some other
-     goal can start a build, and if not, the main loop will sleep a
-     few seconds and then retry this goal. */
-  PathSet lockFiles;
-  for (auto& outPath : drv->outputPaths()) {
-    lockFiles.insert(worker.store.toRealPath(outPath));
-  }
-
-  if (!outputLocks.lockPaths(lockFiles, "", false)) {
-    worker.waitForAWhile(shared_from_this());
-    return;
-  }
-
-  /* Now check again whether the outputs are valid.  This is because
-     another process may have started building in parallel.  After
-     it has finished and released the locks, we can (and should)
-     reuse its results.  (Strictly speaking the first check can be
-     omitted, but that would be less efficient.)  Note that since we
-     now hold the locks on the output paths, no other process can
-     build this derivation, so no further checks are necessary. */
-  validPaths = checkPathValidity(true, buildMode == bmRepair);
-  if (buildMode != bmCheck && validPaths.size() == drv->outputs.size()) {
-    DLOG(INFO) << "skipping build of derivation '" << drvPath
-               << "', someone beat us to it";
-    outputLocks.setDeletion(true);
-    done(BuildResult::AlreadyValid);
-    return;
-  }
-
-  missingPaths = drv->outputPaths();
-  if (buildMode != bmCheck) {
-    for (auto& i : validPaths) {
-      missingPaths.erase(i);
-    }
-  }
-
-  /* If any of the outputs already exist but are not valid, delete
-     them. */
-  for (auto& i : drv->outputs) {
-    Path path = i.second.path;
-    if (worker.store.isValidPath(path)) {
-      continue;
-    }
-    DLOG(INFO) << "removing invalid path " << path;
-    deletePath(worker.store.toRealPath(path));
-  }
-
-  /* Don't do a remote build if the derivation has the attribute
-     `preferLocalBuild' set.  Also, check and repair modes are only
-     supported for local builds. */
-  bool buildLocally = buildMode != bmNormal || parsedDrv->willBuildLocally();
-
-  auto started = [&]() {
-    std::string msg;
-    if (buildMode == bmRepair) {
-      msg = absl::StrFormat("repairing outputs of '%s'", drvPath);
-    } else if (buildMode == bmCheck) {
-      msg = absl::StrFormat("checking outputs of '%s'", drvPath);
-    } else if (nrRounds > 1) {
-      msg = absl::StrFormat("building '%s' (round %d/%d)", drvPath, curRound,
-                            nrRounds);
-    } else {
-      msg = absl::StrFormat("building '%s'", drvPath);
-    }
-
-    if (hook) {
-      absl::StrAppend(&msg, absl::StrFormat(" on '%s'", machineName));
-    }
-
-    log_sink() << msg << std::endl;
-    mcRunningBuilds =
-        std::make_unique<MaintainCount<uint64_t>>(worker.runningBuilds);
-  };
-
-  /* Is the build hook willing to accept this job? */
-  if (!buildLocally) {
-    switch (tryBuildHook()) {
-      case rpAccept:
-        /* Yes, it has started doing so.  Wait until we get
-           EOF from the hook. */
-        result.startTime = time(nullptr);  // inexact
-        state = &DerivationGoal::buildDone;
-        started();
-        return;
-      case rpPostpone:
-        /* Not now; wait until at least one child finishes or
-           the wake-up timeout expires. */
-        worker.waitForAWhile(shared_from_this());
-        outputLocks.unlock();
-        return;
-      case rpDecline:
-        /* We should do it ourselves. */
-        break;
-    }
-  }
-
-  /* Make sure that we are allowed to start a build.  If this
-     derivation prefers to be done locally, do it even if
-     maxBuildJobs is 0. */
-  unsigned int curBuilds = worker.getNrLocalBuilds();
-  if (curBuilds >= settings.maxBuildJobs && !(buildLocally && curBuilds == 0)) {
-    worker.waitForBuildSlot(shared_from_this());
-    outputLocks.unlock();
-    return;
-  }
-
-  try {
-    /* Okay, we have to build. */
-    startBuilder();
-
-  } catch (BuildError& e) {
-    log_sink() << e.msg() << std::endl;
-    outputLocks.unlock();
-    buildUser.reset();
-    worker.permanentFailure = true;
-    done(BuildResult::InputRejected, e.msg());
-    return;
-  }
-
-  /* This state will be reached when we get EOF on the child's
-     log pipe. */
-  state = &DerivationGoal::buildDone;
-
-  started();
-}
-
-void replaceValidPath(const Path& storePath, const Path& tmpPath) {
-  /* We can't atomically replace storePath (the original) with
-     tmpPath (the replacement), so we have to move it out of the
-     way first.  We'd better not be interrupted here, because if
-     we're repairing (say) Glibc, we end up with a broken system. */
-  Path oldPath =
-      (format("%1%.old-%2%-%3%") % storePath % getpid() % random()).str();
-  if (pathExists(storePath)) {
-    rename(storePath.c_str(), oldPath.c_str());
-  }
-  if (rename(tmpPath.c_str(), storePath.c_str()) == -1) {
-    throw SysError(format("moving '%1%' to '%2%'") % tmpPath % storePath);
-  }
-  deletePath(oldPath);
-}
-
-MakeError(NotDeterministic, BuildError);
-
-void DerivationGoal::buildDone() {
-  trace("build done");
-
-  /* Release the build user at the end of this function. We don't do
-     it right away because we don't want another build grabbing this
-     uid and then messing around with our output. */
-  Finally releaseBuildUser([&]() { buildUser.reset(); });
-
-  /* Since we got an EOF on the logger pipe, the builder is presumed
-     to have terminated.  In fact, the builder could also have
-     simply have closed its end of the pipe, so just to be sure,
-     kill it. */
-  int status = hook ? hook->pid.kill() : pid.kill();
-
-  DLOG(INFO) << "builder process for '" << drvPath << "' finished";
-
-  result.timesBuilt++;
-  result.stopTime = time(nullptr);
-
-  /* So the child is gone now. */
-  worker.childTerminated(this);
-
-  /* Close the read side of the logger pipe. */
-  if (hook) {
-    hook->builderOut.readSide = AutoCloseFD(-1);
-    hook->fromHook.readSide = AutoCloseFD(-1);
-  } else {
-    builderOut.readSide = AutoCloseFD(-1);
-  }
-
-  /* Close the log file. */
-  closeLogFile();
-
-  /* When running under a build user, make sure that all processes
-     running under that uid are gone.  This is to prevent a
-     malicious user from leaving behind a process that keeps files
-     open and modifies them after they have been chown'ed to
-     root. */
-  if (buildUser) {
-    buildUser->kill();
-  }
-
-  bool diskFull = false;
-
-  try {
-    /* Check the exit status. */
-    if (!statusOk(status)) {
-      /* Heuristically check whether the build failure may have
-         been caused by a disk full condition.  We have no way
-         of knowing whether the build actually got an ENOSPC.
-         So instead, check if the disk is (nearly) full now.  If
-         so, we don't mark this build as a permanent failure. */
-#if HAVE_STATVFS
-      unsigned long long required =
-          8ULL * 1024 * 1024;  // FIXME: make configurable
-      struct statvfs st;
-      if (statvfs(worker.store.realStoreDir.c_str(), &st) == 0 &&
-          static_cast<unsigned long long>(st.f_bavail) * st.f_bsize <
-              required) {
-        diskFull = true;
-      }
-      if (statvfs(tmpDir.c_str(), &st) == 0 &&
-          static_cast<unsigned long long>(st.f_bavail) * st.f_bsize <
-              required) {
-        diskFull = true;
-      }
-#endif
-
-      deleteTmpDir(false);
-
-      /* Move paths out of the chroot for easier debugging of
-         build failures. */
-      if (useChroot && buildMode == bmNormal) {
-        for (auto& i : missingPaths) {
-          if (pathExists(chrootRootDir + i)) {
-            rename((chrootRootDir + i).c_str(), i.c_str());
-          }
-        }
-      }
-
-      std::string msg =
-          (format("builder for '%1%' %2%") % drvPath % statusToString(status))
-              .str();
-
-      if (!settings.verboseBuild && !logTail.empty()) {
-        msg += (format("; last %d log lines:") % logTail.size()).str();
-        for (auto& line : logTail) {
-          msg += "\n  " + line;
-        }
-      }
-
-      if (diskFull) {
-        msg +=
-            "\nnote: build failure may have been caused by lack of free disk "
-            "space";
-      }
-
-      throw BuildError(msg);
-    }
-
-    /* Compute the FS closure of the outputs and register them as
-       being valid. */
-    registerOutputs();
-
-    if (settings.postBuildHook != "") {
-      log_sink() << "running post-build-hook '" << settings.postBuildHook
-                 << "' [" << drvPath << "]" << std::endl;
-      auto outputPaths = drv->outputPaths();
-      std::map<std::string, std::string> hookEnvironment = getEnv();
-
-      hookEnvironment.emplace("DRV_PATH", drvPath);
-      hookEnvironment.emplace("OUT_PATHS",
-                              absl::StripTrailingAsciiWhitespace(
-                                  concatStringsSep(" ", outputPaths)));
-
-      RunOptions opts(settings.postBuildHook, {});
-      opts.environment = hookEnvironment;
-
-      struct LogSink : Sink {
-        std::string currentLine;
-
-        void operator()(const unsigned char* data, size_t len) override {
-          for (size_t i = 0; i < len; i++) {
-            auto c = data[i];
-
-            if (c == '\n') {
-              flushLine();
-            } else {
-              currentLine += c;
-            }
-          }
-        }
-
-        void flushLine() {
-          if (settings.verboseBuild) {
-            LOG(ERROR) << "post-build-hook: " << currentLine;
-          }
-          currentLine.clear();
-        }
-
-        ~LogSink() override {
-          if (!currentLine.empty()) {
-            currentLine += '\n';
-            flushLine();
-          }
-        }
-      };
-      LogSink sink;
-
-      opts.standardOut = &sink;
-      opts.mergeStderrToStdout = true;
-      runProgram2(opts);
-    }
-
-    if (buildMode == bmCheck) {
-      done(BuildResult::Built);
-      return;
-    }
-
-    /* Delete unused redirected outputs (when doing hash rewriting). */
-    for (auto& i : redirectedOutputs) {
-      deletePath(i.second);
-    }
-
-    /* Delete the chroot (if we were using one). */
-    autoDelChroot.reset(); /* this runs the destructor */
-
-    deleteTmpDir(true);
-
-    /* Repeat the build if necessary. */
-    if (curRound++ < nrRounds) {
-      outputLocks.unlock();
-      state = &DerivationGoal::tryToBuild;
-      worker.wakeUp(shared_from_this());
-      return;
-    }
-
-    /* It is now safe to delete the lock files, since all future
-       lockers will see that the output paths are valid; they will
-       not create new lock files with the same names as the old
-       (unlinked) lock files. */
-    outputLocks.setDeletion(true);
-    outputLocks.unlock();
-
-  } catch (BuildError& e) {
-    log_sink() << e.msg() << std::endl;
-
-    outputLocks.unlock();
-
-    BuildResult::Status st = BuildResult::MiscFailure;
-
-    if (hook && WIFEXITED(status) && WEXITSTATUS(status) == 101) {
-      st = BuildResult::TimedOut;
-
-    } else if (hook && (!WIFEXITED(status) || WEXITSTATUS(status) != 100)) {
-    }
-
-    else {
-      st = dynamic_cast<NotDeterministic*>(&e) != nullptr
-               ? BuildResult::NotDeterministic
-           : statusOk(status)        ? BuildResult::OutputRejected
-           : fixedOutput || diskFull ? BuildResult::TransientFailure
-                                     : BuildResult::PermanentFailure;
-    }
-
-    done(st, e.msg());
-    return;
-  }
-
-  done(BuildResult::Built);
-}
-
-HookReply DerivationGoal::tryBuildHook() {
-  if (!worker.tryBuildHook || !useDerivation) {
-    return rpDecline;
-  }
-
-  if (!worker.hook) {
-    worker.hook = std::make_unique<HookInstance>();
-  }
-
-  try {
-    /* Send the request to the hook. */
-    worker.hook->sink << "try"
-                      << (worker.getNrLocalBuilds() < settings.maxBuildJobs ? 1
-                                                                            : 0)
-                      << drv->platform << drvPath
-                      << parsedDrv->getRequiredSystemFeatures();
-    worker.hook->sink.flush();
-
-    /* Read the first line of input, which should be a word indicating
-       whether the hook wishes to perform the build. */
-    std::string reply;
-    while (true) {
-      std::string s = readLine(worker.hook->fromHook.readSide.get());
-      if (std::string(s, 0, 2) == "# ") {
-        reply = std::string(s, 2);
-        break;
-      }
-      s += "\n";
-      std::cerr << s;
-    }
-
-    DLOG(INFO) << "hook reply is " << reply;
-
-    if (reply == "decline") {
-      return rpDecline;
-    }
-    if (reply == "decline-permanently") {
-      worker.tryBuildHook = false;
-      worker.hook = nullptr;
-      return rpDecline;
-    } else if (reply == "postpone") {
-      return rpPostpone;
-    } else if (reply != "accept") {
-      throw Error(format("bad hook reply '%1%'") % reply);
-    }
-  } catch (SysError& e) {
-    if (e.errNo == EPIPE) {
-      log_sink() << "build hook died unexpectedly: "
-                 << absl::StripTrailingAsciiWhitespace(
-                        drainFD(worker.hook->fromHook.readSide.get()))
-                 << std::endl;
-      worker.hook = nullptr;
-      return rpDecline;
-    }
-    throw;
-  }
-
-  hook = std::move(worker.hook);
-
-  machineName = readLine(hook->fromHook.readSide.get());
-
-  /* Tell the hook all the inputs that have to be copied to the
-     remote system. */
-  hook->sink << inputPaths;
-
-  /* Tell the hooks the missing outputs that have to be copied back
-     from the remote system. */
-  hook->sink << missingPaths;
-
-  hook->sink = FdSink();
-  hook->toHook.writeSide = AutoCloseFD(-1);
-
-  /* Create the log file and pipe. */
-  Path logFile = openLogFile();
-
-  std::set<int> fds;
-  fds.insert(hook->fromHook.readSide.get());
-  fds.insert(hook->builderOut.readSide.get());
-  worker.childStarted(shared_from_this(), fds, false, false);
-
-  return rpAccept;
-}
-
-void chmod_(const Path& path, mode_t mode) {
-  if (chmod(path.c_str(), mode) == -1) {
-    throw SysError(format("setting permissions on '%1%'") % path);
-  }
-}
-
-int childEntry(void* arg) {
-  (static_cast<DerivationGoal*>(arg))->runChild();
-  return 1;
-}
-
-PathSet DerivationGoal::exportReferences(const PathSet& storePaths) {
-  PathSet paths;
-
-  for (auto storePath : storePaths) {
-    /* Check that the store path is valid. */
-    if (!worker.store.isInStore(storePath)) {
-      throw BuildError(
-          format("'exportReferencesGraph' contains a non-store path '%1%'") %
-          storePath);
-    }
-
-    storePath = worker.store.toStorePath(storePath);
-
-    if (inputPaths.count(storePath) == 0u) {
-      throw BuildError(
-          "cannot export references of path '%s' because it is not in the "
-          "input closure of the derivation",
-          storePath);
-    }
-
-    worker.store.computeFSClosure(storePath, paths);
-  }
-
-  /* If there are derivations in the graph, then include their
-     outputs as well.  This is useful if you want to do things
-     like passing all build-time dependencies of some path to a
-     derivation that builds a NixOS DVD image. */
-  PathSet paths2(paths);
-
-  for (auto& j : paths2) {
-    if (isDerivation(j)) {
-      Derivation drv = worker.store.derivationFromPath(j);
-      for (auto& k : drv.outputs) {
-        worker.store.computeFSClosure(k.second.path, paths);
-      }
-    }
-  }
-
-  return paths;
-}
-
-static std::once_flag dns_resolve_flag;
-
-static void preloadNSS() {
-  /* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a
-     dynamic library load of one of the glibc NSS libraries in a sandboxed
-     child, which will fail unless the library's already been loaded in the
-     parent. So we force a lookup of an invalid domain to force the NSS
-     machinery to
-     load its lookup libraries in the parent before any child gets a chance to.
-   */
-  std::call_once(dns_resolve_flag, []() {
-    struct addrinfo* res = nullptr;
-
-    if (getaddrinfo("this.pre-initializes.the.dns.resolvers.invalid.", "http",
-                    nullptr, &res) != 0) {
-      if (res != nullptr) {
-        freeaddrinfo(res);
-      }
-    }
-  });
-}
-
-void DerivationGoal::startBuilder() {
-  /* Right platform? */
-  if (!parsedDrv->canBuildLocally()) {
-    throw Error(
-        "a '%s' with features {%s} is required to build '%s', but I am a '%s' "
-        "with features {%s}",
-        drv->platform,
-        concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), drvPath,
-        settings.thisSystem, concatStringsSep(", ", settings.systemFeatures));
-  }
-
-  if (drv->isBuiltin()) {
-    preloadNSS();
-  }
-
-  /* Are we doing a chroot build? */
-  {
-    auto noChroot = parsedDrv->getBoolAttr("__noChroot");
-    if (settings.sandboxMode == smEnabled) {
-      if (noChroot) {
-        throw Error(format("derivation '%1%' has '__noChroot' set, "
-                           "but that's not allowed when 'sandbox' is 'true'") %
-                    drvPath);
-      }
-      useChroot = true;
-    } else if (settings.sandboxMode == smDisabled) {
-      useChroot = false;
-    } else if (settings.sandboxMode == smRelaxed) {
-      useChroot = !fixedOutput && !noChroot;
-    }
-  }
-
-  if (worker.store.storeDir != worker.store.realStoreDir) {
-    useChroot = true;
-  }
-
-  /* If `build-users-group' is not empty, then we have to build as
-     one of the members of that group. */
-  if (settings.buildUsersGroup != "" && getuid() == 0) {
-    buildUser = std::make_unique<UserLock>();
-
-    /* Make sure that no other processes are executing under this
-       uid. */
-    buildUser->kill();
-  }
-
-  /* Create a temporary directory where the build will take
-     place. */
-  auto drvName = storePathToName(drvPath);
-  tmpDir = createTempDir("", "nix-build-" + drvName, false, false, 0700);
-
-  chownToBuilder(tmpDir);
-
-  /* Substitute output placeholders with the actual output paths. */
-  for (auto& output : drv->outputs) {
-    inputRewrites[hashPlaceholder(output.first)] = output.second.path;
-  }
-
-  /* Construct the environment passed to the builder. */
-  initEnv();
-
-  writeStructuredAttrs();
-
-  /* Handle exportReferencesGraph(), if set. */
-  if (!parsedDrv->getStructuredAttrs()) {
-    /* The `exportReferencesGraph' feature allows the references graph
-       to be passed to a builder.  This attribute should be a list of
-       pairs [name1 path1 name2 path2 ...].  The references graph of
-       each `pathN' will be stored in a text file `nameN' in the
-       temporary build directory.  The text files have the format used
-       by `nix-store --register-validity'.  However, the deriver
-       fields are left empty. */
-    std::string s = get(drv->env, "exportReferencesGraph");
-    std::vector<std::string> ss =
-        absl::StrSplit(s, absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-    if (ss.size() % 2 != 0) {
-      throw BuildError(absl::StrFormat(
-          "odd number of tokens %d in 'exportReferencesGraph': '%s'", ss.size(),
-          s));
-    }
-    for (auto i = ss.begin(); i != ss.end();) {
-      std::string fileName = *i++;
-      checkStoreName(fileName); /* !!! abuse of this function */
-      Path storePath = *i++;
-
-      /* Write closure info to <fileName>. */
-      writeFile(tmpDir + "/" + fileName,
-                worker.store.makeValidityRegistration(
-                    exportReferences({storePath}), false, false));
-    }
-  }
-
-  if (useChroot) {
-    /* Allow a user-configurable set of directories from the
-       host file system. */
-    PathSet dirs = settings.sandboxPaths;
-    PathSet dirs2 = settings.extraSandboxPaths;
-    dirs.insert(dirs2.begin(), dirs2.end());
-
-    dirsInChroot.clear();
-
-    for (auto i : dirs) {
-      if (i.empty()) {
-        continue;
-      }
-      bool optional = false;
-      if (i[i.size() - 1] == '?') {
-        optional = true;
-        i.pop_back();
-      }
-      size_t p = i.find('=');
-      if (p == std::string::npos) {
-        dirsInChroot[i] = ChrootPath(i, optional);
-      } else {
-        dirsInChroot[std::string(i, 0, p)] =
-            ChrootPath(std::string(i, p + 1), optional);
-      }
-    }
-    dirsInChroot[tmpDirInSandbox] = ChrootPath(tmpDir);
-
-    /* Add the closure of store paths to the chroot. */
-    PathSet closure;
-    for (auto& i : dirsInChroot) {
-      try {
-        if (worker.store.isInStore(i.second.source)) {
-          worker.store.computeFSClosure(
-              worker.store.toStorePath(i.second.source), closure);
-        }
-      } catch (InvalidPath& e) {
-      } catch (Error& e) {
-        throw Error(format("while processing 'sandbox-paths': %s") % e.what());
-      }
-    }
-    for (auto& i : closure) {
-      dirsInChroot[i] = ChrootPath(i);
-    }
-
-    PathSet allowedPaths = settings.allowedImpureHostPrefixes;
-
-    /* This works like the above, except on a per-derivation level */
-    auto impurePaths =
-        parsedDrv->getStringsAttr("__impureHostDeps").value_or(Strings());
-
-    for (auto& i : impurePaths) {
-      bool found = false;
-      /* Note: we're not resolving symlinks here to prevent
-         giving a non-root user info about inaccessible
-         files. */
-      Path canonI = canonPath(i);
-      /* If only we had a trie to do this more efficiently :) luckily, these are
-       * generally going to be pretty small */
-      for (auto& a : allowedPaths) {
-        Path canonA = canonPath(a);
-        if (canonI == canonA || isInDir(canonI, canonA)) {
-          found = true;
-          break;
-        }
-      }
-      if (!found) {
-        throw Error(format("derivation '%1%' requested impure path '%2%', but "
-                           "it was not in allowed-impure-host-deps") %
-                    drvPath % i);
-      }
-
-      dirsInChroot[i] = ChrootPath(i);
-    }
-
-    /* Create a temporary directory in which we set up the chroot
-       environment using bind-mounts.  We put it in the Nix store
-       to ensure that we can create hard-links to non-directory
-       inputs in the fake Nix store in the chroot (see below). */
-    chrootRootDir = worker.store.toRealPath(drvPath) + ".chroot";
-    deletePath(chrootRootDir);
-
-    /* Clean up the chroot directory automatically. */
-    autoDelChroot = std::make_shared<AutoDelete>(chrootRootDir);
-
-    DLOG(INFO) << "setting up chroot environment in '" << chrootRootDir << "'";
-
-    if (mkdir(chrootRootDir.c_str(), 0750) == -1) {
-      throw SysError(format("cannot create '%1%'") % chrootRootDir);
-    }
-
-    if (buildUser &&
-        chown(chrootRootDir.c_str(), 0, buildUser->getGID()) == -1) {
-      throw SysError(format("cannot change ownership of '%1%'") %
-                     chrootRootDir);
-    }
-
-    /* Create a writable /tmp in the chroot.  Many builders need
-       this.  (Of course they should really respect $TMPDIR
-       instead.) */
-    Path chrootTmpDir = chrootRootDir + "/tmp";
-    createDirs(chrootTmpDir);
-    chmod_(chrootTmpDir, 01777);
-
-    /* Create a /etc/passwd with entries for the build user and the
-       nobody account.  The latter is kind of a hack to support
-       Samba-in-QEMU. */
-    createDirs(chrootRootDir + "/etc");
-
-    writeFile(chrootRootDir + "/etc/passwd",
-              fmt("root:x:0:0:Nix build user:%3%:/noshell\n"
-                  "nixbld:x:%1%:%2%:Nix build user:%3%:/noshell\n"
-                  "nobody:x:65534:65534:Nobody:/:/noshell\n",
-                  sandboxUid, sandboxGid, settings.sandboxBuildDir));
-
-    /* Declare the build user's group so that programs get a consistent
-       view of the system (e.g., "id -gn"). */
-    writeFile(chrootRootDir + "/etc/group", (format("root:x:0:\n"
-                                                    "nixbld:!:%1%:\n"
-                                                    "nogroup:x:65534:\n") %
-                                             sandboxGid)
-                                                .str());
-
-    /* Create /etc/hosts with localhost entry. */
-    if (!fixedOutput) {
-      writeFile(chrootRootDir + "/etc/hosts",
-                "127.0.0.1 localhost\n::1 localhost\n");
-    }
-
-    /* Make the closure of the inputs available in the chroot,
-       rather than the whole Nix store.  This prevents any access
-       to undeclared dependencies.  Directories are bind-mounted,
-       while other inputs are hard-linked (since only directories
-       can be bind-mounted).  !!! As an extra security
-       precaution, make the fake Nix store only writable by the
-       build user. */
-    Path chrootStoreDir = chrootRootDir + worker.store.storeDir;
-    createDirs(chrootStoreDir);
-    chmod_(chrootStoreDir, 01775);
-
-    if (buildUser &&
-        chown(chrootStoreDir.c_str(), 0, buildUser->getGID()) == -1) {
-      throw SysError(format("cannot change ownership of '%1%'") %
-                     chrootStoreDir);
-    }
-
-    for (auto& i : inputPaths) {
-      Path r = worker.store.toRealPath(i);
-      struct stat st;
-      if (lstat(r.c_str(), &st) != 0) {
-        throw SysError(format("getting attributes of path '%1%'") % i);
-      }
-      if (S_ISDIR(st.st_mode)) {
-        dirsInChroot[i] = ChrootPath(r);
-      } else {
-        Path p = chrootRootDir + i;
-        DLOG(INFO) << "linking '" << p << "' to '" << r << "'";
-        if (link(r.c_str(), p.c_str()) == -1) {
-          /* Hard-linking fails if we exceed the maximum
-             link count on a file (e.g. 32000 of ext3),
-             which is quite possible after a `nix-store
-             --optimise'. */
-          if (errno != EMLINK) {
-            throw SysError(format("linking '%1%' to '%2%'") % p % i);
-          }
-          StringSink sink;
-          dumpPath(r, sink);
-          StringSource source(*sink.s);
-          restorePath(p, source);
-        }
-      }
-    }
-
-    /* If we're repairing, checking or rebuilding part of a
-       multiple-outputs derivation, it's possible that we're
-       rebuilding a path that is in settings.dirsInChroot
-       (typically the dependencies of /bin/sh).  Throw them
-       out. */
-    for (auto& i : drv->outputs) {
-      dirsInChroot.erase(i.second.path);
-    }
-  }
-
-  if (needsHashRewrite()) {
-    if (pathExists(homeDir)) {
-      throw Error(format("directory '%1%' exists; please remove it") % homeDir);
-    }
-
-    /* We're not doing a chroot build, but we have some valid
-       output paths.  Since we can't just overwrite or delete
-       them, we have to do hash rewriting: i.e. in the
-       environment/arguments passed to the build, we replace the
-       hashes of the valid outputs with unique dummy strings;
-       after the build, we discard the redirected outputs
-       corresponding to the valid outputs, and rewrite the
-       contents of the new outputs to replace the dummy strings
-       with the actual hashes. */
-    if (!validPaths.empty()) {
-      for (auto& i : validPaths) {
-        addHashRewrite(i);
-      }
-    }
-
-    /* If we're repairing, then we don't want to delete the
-       corrupt outputs in advance.  So rewrite them as well. */
-    if (buildMode == bmRepair) {
-      for (auto& i : missingPaths) {
-        if (worker.store.isValidPath(i) && pathExists(i)) {
-          addHashRewrite(i);
-          redirectedBadOutputs.insert(i);
-        }
-      }
-    }
-  }
-
-  if (useChroot && settings.preBuildHook != "" &&
-      (dynamic_cast<Derivation*>(drv.get()) != nullptr)) {
-    DLOG(INFO) << "executing pre-build hook '" << settings.preBuildHook << "'";
-    auto args =
-        useChroot ? Strings({drvPath, chrootRootDir}) : Strings({drvPath});
-    enum BuildHookState { stBegin, stExtraChrootDirs };
-    auto state = stBegin;
-    auto lines = runProgram(settings.preBuildHook, false, args);
-    auto lastPos = std::string::size_type{0};
-    for (auto nlPos = lines.find('\n'); nlPos != std::string::npos;
-         nlPos = lines.find('\n', lastPos)) {
-      auto line = std::string{lines, lastPos, nlPos - lastPos};
-      lastPos = nlPos + 1;
-      if (state == stBegin) {
-        if (line == "extra-sandbox-paths" || line == "extra-chroot-dirs") {
-          state = stExtraChrootDirs;
-        } else {
-          throw Error(format("unknown pre-build hook command '%1%'") % line);
-        }
-      } else if (state == stExtraChrootDirs) {
-        if (line.empty()) {
-          state = stBegin;
-        } else {
-          auto p = line.find('=');
-          if (p == std::string::npos) {
-            dirsInChroot[line] = ChrootPath(line);
-          } else {
-            dirsInChroot[std::string(line, 0, p)] =
-                ChrootPath(std::string(line, p + 1));
-          }
-        }
-      }
-    }
-  }
-
-  /* Run the builder. */
-  DLOG(INFO) << "executing builder '" << drv->builder << "'";
-
-  /* Create the log file. */
-  Path logFile = openLogFile();
-
-  /* Create a pipe to get the output of the builder. */
-  // builderOut.create();
-
-  builderOut.readSide = AutoCloseFD(posix_openpt(O_RDWR | O_NOCTTY));
-  if (!builderOut.readSide) {
-    throw SysError("opening pseudoterminal master");
-  }
-
-  std::string slaveName(ptsname(builderOut.readSide.get()));
-
-  if (buildUser) {
-    if (chmod(slaveName.c_str(), 0600) != 0) {
-      throw SysError("changing mode of pseudoterminal slave");
-    }
-
-    if (chown(slaveName.c_str(), buildUser->getUID(), 0) != 0) {
-      throw SysError("changing owner of pseudoterminal slave");
-    }
-  } else {
-    if (grantpt(builderOut.readSide.get()) != 0) {
-      throw SysError("granting access to pseudoterminal slave");
-    }
-  }
-
-#if 0
-    // Mount the pt in the sandbox so that the "tty" command works.
-    // FIXME: this doesn't work with the new devpts in the sandbox.
-    if (useChroot)
-        dirsInChroot[slaveName] = {slaveName, false};
-#endif
-
-  if (unlockpt(builderOut.readSide.get()) != 0) {
-    throw SysError("unlocking pseudoterminal");
-  }
-
-  builderOut.writeSide =
-      AutoCloseFD(open(slaveName.c_str(), O_RDWR | O_NOCTTY));
-  if (!builderOut.writeSide) {
-    throw SysError("opening pseudoterminal slave");
-  }
-
-  // Put the pt into raw mode to prevent \n -> \r\n translation.
-  struct termios term;
-  if (tcgetattr(builderOut.writeSide.get(), &term) != 0) {
-    throw SysError("getting pseudoterminal attributes");
-  }
-
-  cfmakeraw(&term);
-
-  if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term) != 0) {
-    throw SysError("putting pseudoterminal into raw mode");
-  }
-
-  result.startTime = time(nullptr);
-
-  /* Fork a child to build the package. */
-  ProcessOptions options;
-
-#if __linux__
-  if (useChroot) {
-    /* Set up private namespaces for the build:
-
-       - The PID namespace causes the build to start as PID 1.
-         Processes outside of the chroot are not visible to those
-         on the inside, but processes inside the chroot are
-         visible from the outside (though with different PIDs).
-
-       - The private mount namespace ensures that all the bind
-         mounts we do will only show up in this process and its
-         children, and will disappear automatically when we're
-         done.
-
-       - The private network namespace ensures that the builder
-         cannot talk to the outside world (or vice versa).  It
-         only has a private loopback interface. (Fixed-output
-         derivations are not run in a private network namespace
-         to allow functions like fetchurl to work.)
-
-       - The IPC namespace prevents the builder from communicating
-         with outside processes using SysV IPC mechanisms (shared
-         memory, message queues, semaphores).  It also ensures
-         that all IPC objects are destroyed when the builder
-         exits.
-
-       - The UTS namespace ensures that builders see a hostname of
-         localhost rather than the actual hostname.
-
-       We use a helper process to do the clone() to work around
-       clone() being broken in multi-threaded programs due to
-       at-fork handlers not being run. Note that we use
-       CLONE_PARENT to ensure that the real builder is parented to
-       us.
-    */
-
-    if (!fixedOutput) {
-      privateNetwork = true;
-    }
-
-    userNamespaceSync.create();
-
-    Pid helper(startProcess(
-        [&]() {
-          /* Drop additional groups here because we can't do it
-             after we've created the new user namespace.  FIXME:
-             this means that if we're not root in the parent
-             namespace, we can't drop additional groups; they will
-             be mapped to nogroup in the child namespace. There does
-             not seem to be a workaround for this. (But who can tell
-             from reading user_namespaces(7)?)
-             See also https://lwn.net/Articles/621612/. */
-          if (getuid() == 0 && setgroups(0, nullptr) == -1) {
-            throw SysError("setgroups failed");
-          }
-
-          size_t stackSize = 1 * 1024 * 1024;
-          char* stack = static_cast<char*>(
-              mmap(nullptr, stackSize, PROT_WRITE | PROT_READ,
-                   MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0));
-          if (stack == MAP_FAILED) {
-            throw SysError("allocating stack");
-          }
-
-          int flags = CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS |
-                      CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
-          if (privateNetwork) {
-            flags |= CLONE_NEWNET;
-          }
-
-          pid_t child = clone(childEntry, stack + stackSize, flags, this);
-          if (child == -1 && errno == EINVAL) {
-            /* Fallback for Linux < 2.13 where CLONE_NEWPID and
-               CLONE_PARENT are not allowed together. */
-            flags &= ~CLONE_NEWPID;
-            child = clone(childEntry, stack + stackSize, flags, this);
-          }
-          if (child == -1 && (errno == EPERM || errno == EINVAL)) {
-            /* Some distros patch Linux to not allow unpriveleged
-             * user namespaces. If we get EPERM or EINVAL, try
-             * without CLONE_NEWUSER and see if that works.
-             */
-            flags &= ~CLONE_NEWUSER;
-            child = clone(childEntry, stack + stackSize, flags, this);
-          }
-          /* Otherwise exit with EPERM so we can handle this in the
-             parent. This is only done when sandbox-fallback is set
-             to true (the default). */
-          if (child == -1 && (errno == EPERM || errno == EINVAL) &&
-              settings.sandboxFallback) {
-            _exit(1);
-          }
-          if (child == -1) {
-            throw SysError("cloning builder process");
-          }
-
-          writeFull(builderOut.writeSide.get(), std::to_string(child) + "\n");
-          _exit(0);
-        },
-        options));
-
-    int res = helper.wait();
-    if (res != 0 && settings.sandboxFallback) {
-      useChroot = false;
-      initTmpDir();
-      goto fallback;
-    } else if (res != 0) {
-      throw Error("unable to start build process");
-    }
-
-    userNamespaceSync.readSide = AutoCloseFD(-1);
-
-    pid_t tmp;
-    if (!absl::SimpleAtoi(readLine(builderOut.readSide.get()), &tmp)) {
-      abort();
-    }
-    pid = tmp;
-
-    /* Set the UID/GID mapping of the builder's user namespace
-       such that the sandbox user maps to the build user, or to
-       the calling user (if build users are disabled). */
-    uid_t hostUid = buildUser ? buildUser->getUID() : getuid();
-    uid_t hostGid = buildUser ? buildUser->getGID() : getgid();
-
-    writeFile("/proc/" + std::to_string(static_cast<pid_t>(pid)) + "/uid_map",
-              (format("%d %d 1") % sandboxUid % hostUid).str());
-
-    writeFile("/proc/" + std::to_string(static_cast<pid_t>(pid)) + "/setgroups",
-              "deny");
-
-    writeFile("/proc/" + std::to_string(static_cast<pid_t>(pid)) + "/gid_map",
-              (format("%d %d 1") % sandboxGid % hostGid).str());
-
-    /* Signal the builder that we've updated its user
-       namespace. */
-    writeFull(userNamespaceSync.writeSide.get(), "1");
-    userNamespaceSync.writeSide = AutoCloseFD(-1);
-
-  } else
-#endif
-  {
-  fallback:
-    pid = startProcess([&]() { runChild(); }, options);
-  }
-
-  /* parent */
-  pid.setSeparatePG(true);
-  builderOut.writeSide = AutoCloseFD(-1);
-  worker.childStarted(shared_from_this(), {builderOut.readSide.get()}, true,
-                      true);
-
-  /* Check if setting up the build environment failed. */
-  while (true) {
-    std::string msg = readLine(builderOut.readSide.get());
-    if (std::string(msg, 0, 1) == "\1") {
-      if (msg.size() == 1) {
-        break;
-      }
-      throw Error(std::string(msg, 1));
-    }
-    DLOG(INFO) << msg;
-  }
-}
-
-void DerivationGoal::initTmpDir() {
-  /* In a sandbox, for determinism, always use the same temporary
-     directory. */
-#if __linux__
-  tmpDirInSandbox = useChroot ? settings.sandboxBuildDir : tmpDir;
-#else
-  tmpDirInSandbox = tmpDir;
-#endif
-
-  /* In non-structured mode, add all bindings specified in the
-     derivation via the environment, except those listed in the
-     passAsFile attribute. Those are passed as file names pointing
-     to temporary files containing the contents. Note that
-     passAsFile is ignored in structure mode because it's not
-     needed (attributes are not passed through the environment, so
-     there is no size constraint). */
-  if (!parsedDrv->getStructuredAttrs()) {
-    std::set<std::string> passAsFile =
-        absl::StrSplit(get(drv->env, "passAsFile"), absl::ByAnyChar(" \t\n\r"),
-                       absl::SkipEmpty());
-    for (auto& i : drv->env) {
-      if (passAsFile.find(i.first) == passAsFile.end()) {
-        env[i.first] = i.second;
-      } else {
-        auto hash = hashString(htSHA256, i.first);
-        std::string fn = ".attr-" + hash.to_string(Base32, false);
-        Path p = tmpDir + "/" + fn;
-        writeFile(p, rewriteStrings(i.second, inputRewrites));
-        chownToBuilder(p);
-        env[i.first + "Path"] = tmpDirInSandbox + "/" + fn;
-      }
-    }
-  }
-
-  /* For convenience, set an environment pointing to the top build
-     directory. */
-  env["NIX_BUILD_TOP"] = tmpDirInSandbox;
-
-  /* Also set TMPDIR and variants to point to this directory. */
-  env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmpDirInSandbox;
-
-  /* Explicitly set PWD to prevent problems with chroot builds.  In
-     particular, dietlibc cannot figure out the cwd because the
-     inode of the current directory doesn't appear in .. (because
-     getdents returns the inode of the mount point). */
-  env["PWD"] = tmpDirInSandbox;
-}
-
-void DerivationGoal::initEnv() {
-  env.clear();
-
-  /* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
-     PATH is not set.  We don't want this, so we fill it in with some dummy
-     value. */
-  env["PATH"] = "/path-not-set";
-
-  /* Set HOME to a non-existing path to prevent certain programs from using
-     /etc/passwd (or NIS, or whatever) to locate the home directory (for
-     example, wget looks for ~/.wgetrc).  I.e., these tools use /etc/passwd
-     if HOME is not set, but they will just assume that the settings file
-     they are looking for does not exist if HOME is set but points to some
-     non-existing path. */
-  env["HOME"] = homeDir;
-
-  /* Tell the builder where the Nix store is.  Usually they
-     shouldn't care, but this is useful for purity checking (e.g.,
-     the compiler or linker might only want to accept paths to files
-     in the store or in the build directory). */
-  env["NIX_STORE"] = worker.store.storeDir;
-
-  /* The maximum number of cores to utilize for parallel building. */
-  env["NIX_BUILD_CORES"] = (format("%d") % settings.buildCores).str();
-
-  initTmpDir();
-
-  /* Compatibility hack with Nix <= 0.7: if this is a fixed-output
-     derivation, tell the builder, so that for instance `fetchurl'
-     can skip checking the output.  On older Nixes, this environment
-     variable won't be set, so `fetchurl' will do the check. */
-  if (fixedOutput) {
-    env["NIX_OUTPUT_CHECKED"] = "1";
-  }
-
-  /* *Only* if this is a fixed-output derivation, propagate the
-     values of the environment variables specified in the
-     `impureEnvVars' attribute to the builder.  This allows for
-     instance environment variables for proxy configuration such as
-     `http_proxy' to be easily passed to downloaders like
-     `fetchurl'.  Passing such environment variables from the caller
-     to the builder is generally impure, but the output of
-     fixed-output derivations is by definition pure (since we
-     already know the cryptographic hash of the output). */
-  if (fixedOutput) {
-    for (auto& i :
-         parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings())) {
-      env[i] = getEnv(i).value_or("");
-    }
-  }
-
-  /* Currently structured log messages piggyback on stderr, but we
-     may change that in the future. So tell the builder which file
-     descriptor to use for that. */
-  env["NIX_LOG_FD"] = "2";
-
-  /* Trigger colored output in various tools. */
-  env["TERM"] = "xterm-256color";
-}
-
-static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*");
-
-void DerivationGoal::writeStructuredAttrs() {
-  auto& structuredAttrs = parsedDrv->getStructuredAttrs();
-  if (!structuredAttrs) {
-    return;
-  }
-
-  auto json = *structuredAttrs;
-
-  /* Add an "outputs" object containing the output paths. */
-  nlohmann::json outputs;
-  for (auto& i : drv->outputs) {
-    outputs[i.first] = rewriteStrings(i.second.path, inputRewrites);
-  }
-  json["outputs"] = outputs;
-
-  /* Handle exportReferencesGraph. */
-  auto e = json.find("exportReferencesGraph");
-  if (e != json.end() && e->is_object()) {
-    for (auto i = e->begin(); i != e->end(); ++i) {
-      std::ostringstream str;
-      {
-        JSONPlaceholder jsonRoot(str, true);
-        PathSet storePaths;
-        for (auto& p : *i) {
-          storePaths.insert(p.get<std::string>());
-        }
-        worker.store.pathInfoToJSON(jsonRoot, exportReferences(storePaths),
-                                    false, true);
-      }
-      json[i.key()] = nlohmann::json::parse(str.str());  // urgh
-    }
-  }
-
-  writeFile(tmpDir + "/.attrs.json",
-            rewriteStrings(json.dump(), inputRewrites));
-  chownToBuilder(tmpDir + "/.attrs.json");
-
-  /* As a convenience to bash scripts, write a shell file that
-     maps all attributes that are representable in bash -
-     namely, strings, integers, nulls, Booleans, and arrays and
-     objects consisting entirely of those values. (So nested
-     arrays or objects are not supported.) */
-
-  auto handleSimpleType =
-      [](const nlohmann::json& value) -> std::optional<std::string> {
-    if (value.is_string()) {
-      return shellEscape(value);
-    }
-
-    if (value.is_number()) {
-      auto f = value.get<float>();
-      if (std::ceil(f) == f) {
-        return std::to_string(value.get<int>());
-      }
-    }
-
-    if (value.is_null()) {
-      return std::string("''");
-    }
-
-    if (value.is_boolean()) {
-      return value.get<bool>() ? std::string("1") : std::string("");
-    }
-
-    return {};
-  };
-
-  std::string jsonSh;
-
-  for (auto i = json.begin(); i != json.end(); ++i) {
-    if (!std::regex_match(i.key(), shVarName)) {
-      continue;
-    }
-
-    auto& value = i.value();
-
-    auto s = handleSimpleType(value);
-    if (s) {
-      jsonSh += fmt("declare %s=%s\n", i.key(), *s);
-
-    } else if (value.is_array()) {
-      std::string s2;
-      bool good = true;
-
-      for (auto i = value.begin(); i != value.end(); ++i) {
-        auto s3 = handleSimpleType(i.value());
-        if (!s3) {
-          good = false;
-          break;
-        }
-        s2 += *s3;
-        s2 += ' ';
-      }
-
-      if (good) {
-        jsonSh += fmt("declare -a %s=(%s)\n", i.key(), s2);
-      }
-    }
-
-    else if (value.is_object()) {
-      std::string s2;
-      bool good = true;
-
-      for (auto i = value.begin(); i != value.end(); ++i) {
-        auto s3 = handleSimpleType(i.value());
-        if (!s3) {
-          good = false;
-          break;
-        }
-        s2 += fmt("[%s]=%s ", shellEscape(i.key()), *s3);
-      }
-
-      if (good) {
-        jsonSh += fmt("declare -A %s=(%s)\n", i.key(), s2);
-      }
-    }
-  }
-
-  writeFile(tmpDir + "/.attrs.sh", rewriteStrings(jsonSh, inputRewrites));
-  chownToBuilder(tmpDir + "/.attrs.sh");
-}
-
-void DerivationGoal::chownToBuilder(const Path& path) {
-  if (!buildUser) {
-    return;
-  }
-  if (chown(path.c_str(), buildUser->getUID(), buildUser->getGID()) == -1) {
-    throw SysError(format("cannot change ownership of '%1%'") % path);
-  }
-}
-
-void setupSeccomp() {
-#if __linux__
-  if (!settings.filterSyscalls) {
-    return;
-  }
-#if HAVE_SECCOMP
-  scmp_filter_ctx ctx;
-
-  if ((ctx = seccomp_init(SCMP_ACT_ALLOW)) == nullptr) {
-    throw SysError("unable to initialize seccomp mode 2");
-  }
-
-  Finally cleanup([&]() { seccomp_release(ctx); });
-
-  if (nativeSystem == "x86_64-linux" &&
-      seccomp_arch_add(ctx, SCMP_ARCH_X86) != 0) {
-    throw SysError("unable to add 32-bit seccomp architecture");
-  }
-
-  if (nativeSystem == "x86_64-linux" &&
-      seccomp_arch_add(ctx, SCMP_ARCH_X32) != 0) {
-    throw SysError("unable to add X32 seccomp architecture");
-  }
-
-  if (nativeSystem == "aarch64-linux" &&
-      seccomp_arch_add(ctx, SCMP_ARCH_ARM) != 0) {
-    LOG(ERROR) << "unable to add ARM seccomp architecture; this may result in "
-               << "spurious build failures if running 32-bit ARM processes";
-  }
-
-  /* Prevent builders from creating setuid/setgid binaries. */
-  for (int perm : {S_ISUID, S_ISGID}) {
-    if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1,
-                         SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t)perm,
-                                 (scmp_datum_t)perm)) != 0) {
-      throw SysError("unable to add seccomp rule");
-    }
-
-    if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 1,
-                         SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t)perm,
-                                 (scmp_datum_t)perm)) != 0) {
-      throw SysError("unable to add seccomp rule");
-    }
-
-    if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1,
-                         SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t)perm,
-                                 (scmp_datum_t)perm)) != 0) {
-      throw SysError("unable to add seccomp rule");
-    }
-  }
-
-  /* Prevent builders from creating EAs or ACLs. Not all filesystems
-     support these, and they're not allowed in the Nix store because
-     they're not representable in the NAR serialisation. */
-  if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) !=
-          0 ||
-      seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) !=
-          0 ||
-      seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) !=
-          0) {
-    throw SysError("unable to add seccomp rule");
-  }
-
-  if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP,
-                       settings.allowNewPrivileges ? 0 : 1) != 0) {
-    throw SysError("unable to set 'no new privileges' seccomp attribute");
-  }
-
-  if (seccomp_load(ctx) != 0) {
-    throw SysError("unable to load seccomp BPF program");
-  }
-#else
-  throw Error(
-      "seccomp is not supported on this platform; "
-      "you can bypass this error by setting the option 'filter-syscalls' to "
-      "false, but note that untrusted builds can then create setuid binaries!");
-#endif
-#endif
-}
-
-void DerivationGoal::runChild() {
-  /* Warning: in the child we should absolutely not make any SQLite
-     calls! */
-
-  try { /* child */
-
-    commonChildInit(builderOut);
-
-    try {
-      setupSeccomp();
-    } catch (...) {
-      if (buildUser) {
-        throw;
-      }
-    }
-
-    bool setUser = true;
-
-    /* Make the contents of netrc available to builtin:fetchurl
-       (which may run under a different uid and/or in a sandbox). */
-    std::string netrcData;
-    try {
-      if (drv->isBuiltin() && drv->builder == "builtin:fetchurl") {
-        const std::string& netrc_file = settings.netrcFile;
-        netrcData = readFile(netrc_file);
-      }
-    } catch (SysError&) {
-    }
-
-#if __linux__
-    if (useChroot) {
-      userNamespaceSync.writeSide = AutoCloseFD(-1);
-
-      if (drainFD(userNamespaceSync.readSide.get()) != "1") {
-        throw Error("user namespace initialisation failed");
-      }
-
-      userNamespaceSync.readSide = AutoCloseFD(-1);
-
-      if (privateNetwork) {
-        /* Initialise the loopback interface. */
-        AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP));
-        if (!fd) {
-          throw SysError("cannot open IP socket");
-        }
-
-        struct ifreq ifr;
-        strncpy(ifr.ifr_name, "lo", sizeof("lo"));
-        ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
-        if (ioctl(fd.get(), SIOCSIFFLAGS, &ifr) == -1) {
-          throw SysError("cannot set loopback interface flags");
-        }
-      }
-
-      /* Set the hostname etc. to fixed values. */
-      char hostname[] = "localhost";
-      if (sethostname(hostname, sizeof(hostname)) == -1) {
-        throw SysError("cannot set host name");
-      }
-      char domainname[] = "(none)";  // kernel default
-      if (setdomainname(domainname, sizeof(domainname)) == -1) {
-        throw SysError("cannot set domain name");
-      }
-
-      /* Make all filesystems private.  This is necessary
-         because subtrees may have been mounted as "shared"
-         (MS_SHARED).  (Systemd does this, for instance.)  Even
-         though we have a private mount namespace, mounting
-         filesystems on top of a shared subtree still propagates
-         outside of the namespace.  Making a subtree private is
-         local to the namespace, though, so setting MS_PRIVATE
-         does not affect the outside world. */
-      if (mount(nullptr, "/", nullptr, MS_REC | MS_PRIVATE, nullptr) == -1) {
-        throw SysError("unable to make '/' private mount");
-      }
-
-      /* Bind-mount chroot directory to itself, to treat it as a
-         different filesystem from /, as needed for pivot_root. */
-      if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), nullptr, MS_BIND,
-                nullptr) == -1) {
-        throw SysError(format("unable to bind mount '%1%'") % chrootRootDir);
-      }
-
-      /* Set up a nearly empty /dev, unless the user asked to
-         bind-mount the host /dev. */
-      Strings ss;
-      if (dirsInChroot.find("/dev") == dirsInChroot.end()) {
-        createDirs(chrootRootDir + "/dev/shm");
-        createDirs(chrootRootDir + "/dev/pts");
-        ss.push_back("/dev/full");
-        if ((settings.systemFeatures.get().count("kvm") != 0u) &&
-            pathExists("/dev/kvm")) {
-          ss.push_back("/dev/kvm");
-        }
-        ss.push_back("/dev/null");
-        ss.push_back("/dev/random");
-        ss.push_back("/dev/tty");
-        ss.push_back("/dev/urandom");
-        ss.push_back("/dev/zero");
-        createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd");
-        createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/stdin");
-        createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/stdout");
-        createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/stderr");
-      }
-
-      /* Fixed-output derivations typically need to access the
-         network, so give them access to /etc/resolv.conf and so
-         on. */
-      if (fixedOutput) {
-        ss.push_back("/etc/resolv.conf");
-
-        // Only use nss functions to resolve hosts and
-        // services. Don’t use it for anything else that may
-        // be configured for this system. This limits the
-        // potential impurities introduced in fixed outputs.
-        writeFile(chrootRootDir + "/etc/nsswitch.conf",
-                  "hosts: files dns\nservices: files\n");
-
-        ss.push_back("/etc/services");
-        ss.push_back("/etc/hosts");
-        if (pathExists("/var/run/nscd/socket")) {
-          ss.push_back("/var/run/nscd/socket");
-        }
-      }
-
-      for (auto& i : ss) {
-        dirsInChroot.emplace(i, i);
-      }
-
-      /* Bind-mount all the directories from the "host"
-         filesystem that we want in the chroot
-         environment. */
-      auto doBind = [&](const Path& source, const Path& target,
-                        bool optional = false) {
-        DLOG(INFO) << "bind mounting '" << source << "' to '" << target << "'";
-        struct stat st;
-        if (stat(source.c_str(), &st) == -1) {
-          if (optional && errno == ENOENT) {
-            return;
-          }
-          throw SysError("getting attributes of path '%1%'", source);
-        }
-        if (S_ISDIR(st.st_mode)) {
-          createDirs(target);
-        } else {
-          createDirs(dirOf(target));
-          writeFile(target, "");
-        }
-        if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC,
-                  nullptr) == -1) {
-          throw SysError("bind mount from '%1%' to '%2%' failed", source,
-                         target);
-        }
-      };
-
-      for (auto& i : dirsInChroot) {
-        if (i.second.source == "/proc") {
-          continue;
-        }  // backwards compatibility
-        doBind(i.second.source, chrootRootDir + i.first, i.second.optional);
-      }
-
-      /* Bind a new instance of procfs on /proc. */
-      createDirs(chrootRootDir + "/proc");
-      if (mount("none", (chrootRootDir + "/proc").c_str(), "proc", 0,
-                nullptr) == -1) {
-        throw SysError("mounting /proc");
-      }
-
-      /* Mount a new tmpfs on /dev/shm to ensure that whatever
-         the builder puts in /dev/shm is cleaned up automatically. */
-      if (pathExists("/dev/shm") &&
-          mount("none", (chrootRootDir + "/dev/shm").c_str(), "tmpfs", 0,
-                fmt("size=%s", settings.sandboxShmSize).c_str()) == -1) {
-        throw SysError("mounting /dev/shm");
-      }
-
-      /* Mount a new devpts on /dev/pts.  Note that this
-         requires the kernel to be compiled with
-         CONFIG_DEVPTS_MULTIPLE_INSTANCES=y (which is the case
-         if /dev/ptx/ptmx exists). */
-      if (pathExists("/dev/pts/ptmx") &&
-          !pathExists(chrootRootDir + "/dev/ptmx") &&
-          (dirsInChroot.count("/dev/pts") == 0u)) {
-        if (mount("none", (chrootRootDir + "/dev/pts").c_str(), "devpts", 0,
-                  "newinstance,mode=0620") == 0) {
-          createSymlink("/dev/pts/ptmx", chrootRootDir + "/dev/ptmx");
-
-          /* Make sure /dev/pts/ptmx is world-writable.  With some
-             Linux versions, it is created with permissions 0.  */
-          chmod_(chrootRootDir + "/dev/pts/ptmx", 0666);
-        } else {
-          if (errno != EINVAL) {
-            throw SysError("mounting /dev/pts");
-          }
-          doBind("/dev/pts", chrootRootDir + "/dev/pts");
-          doBind("/dev/ptmx", chrootRootDir + "/dev/ptmx");
-        }
-      }
-
-      /* Do the chroot(). */
-      if (chdir(chrootRootDir.c_str()) == -1) {
-        throw SysError(format("cannot change directory to '%1%'") %
-                       chrootRootDir);
-      }
-
-      if (mkdir("real-root", 0) == -1) {
-        throw SysError("cannot create real-root directory");
-      }
-
-      if (pivot_root(".", "real-root") == -1) {
-        throw SysError(format("cannot pivot old root directory onto '%1%'") %
-                       (chrootRootDir + "/real-root"));
-      }
-
-      if (chroot(".") == -1) {
-        throw SysError(format("cannot change root directory to '%1%'") %
-                       chrootRootDir);
-      }
-
-      if (umount2("real-root", MNT_DETACH) == -1) {
-        throw SysError("cannot unmount real root filesystem");
-      }
-
-      if (rmdir("real-root") == -1) {
-        throw SysError("cannot remove real-root directory");
-      }
-
-      /* Switch to the sandbox uid/gid in the user namespace,
-         which corresponds to the build user or calling user in
-         the parent namespace. */
-      if (setgid(sandboxGid) == -1) {
-        throw SysError("setgid failed");
-      }
-      if (setuid(sandboxUid) == -1) {
-        throw SysError("setuid failed");
-      }
-
-      setUser = false;
-    }
-#endif
-
-    if (chdir(tmpDirInSandbox.c_str()) == -1) {
-      throw SysError(format("changing into '%1%'") % tmpDir);
-    }
-
-    /* Close all other file descriptors. */
-    closeMostFDs({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO});
-
-#if __linux__
-    /* Change the personality to 32-bit if we're doing an
-       i686-linux build on an x86_64-linux machine. */
-    struct utsname utsbuf;
-    uname(&utsbuf);
-    if (drv->platform == "i686-linux" &&
-        (settings.thisSystem == "x86_64-linux" ||
-         ((strcmp(utsbuf.sysname, "Linux") == 0) &&
-          (strcmp(utsbuf.machine, "x86_64") == 0)))) {
-      if (personality(PER_LINUX32) == -1) {
-        throw SysError("cannot set i686-linux personality");
-      }
-    }
-
-    /* Impersonate a Linux 2.6 machine to get some determinism in
-       builds that depend on the kernel version. */
-    if ((drv->platform == "i686-linux" || drv->platform == "x86_64-linux") &&
-        settings.impersonateLinux26) {
-      int cur = personality(0xffffffff);
-      if (cur != -1) {
-        personality(cur | 0x0020000 /* == UNAME26 */);
-      }
-    }
-
-    /* Disable address space randomization for improved
-       determinism. */
-    int cur = personality(0xffffffff);
-    if (cur != -1) {
-      personality(cur | ADDR_NO_RANDOMIZE);
-    }
-#endif
-
-    /* Disable core dumps by default. */
-    struct rlimit limit = {0, RLIM_INFINITY};
-    setrlimit(RLIMIT_CORE, &limit);
-
-    // FIXME: set other limits to deterministic values?
-
-    /* Fill in the environment. */
-    Strings envStrs;
-    for (auto& i : env) {
-      envStrs.push_back(
-          rewriteStrings(i.first + "=" + i.second, inputRewrites));
-    }
-
-    /* If we are running in `build-users' mode, then switch to the
-       user we allocated above.  Make sure that we drop all root
-       privileges.  Note that above we have closed all file
-       descriptors except std*, so that's safe.  Also note that
-       setuid() when run as root sets the real, effective and
-       saved UIDs. */
-    if (setUser && buildUser) {
-      /* Preserve supplementary groups of the build user, to allow
-         admins to specify groups such as "kvm".  */
-      if (!buildUser->getSupplementaryGIDs().empty() &&
-          setgroups(buildUser->getSupplementaryGIDs().size(),
-                    buildUser->getSupplementaryGIDs().data()) == -1) {
-        throw SysError("cannot set supplementary groups of build user");
-      }
-
-      if (setgid(buildUser->getGID()) == -1 ||
-          getgid() != buildUser->getGID() || getegid() != buildUser->getGID()) {
-        throw SysError("setgid failed");
-      }
-
-      if (setuid(buildUser->getUID()) == -1 ||
-          getuid() != buildUser->getUID() || geteuid() != buildUser->getUID()) {
-        throw SysError("setuid failed");
-      }
-    }
-
-    /* Fill in the arguments. */
-    Strings args;
-
-    const char* builder = "invalid";
-
-    if (!drv->isBuiltin()) {
-      builder = drv->builder.c_str();
-      std::string builderBasename = baseNameOf(drv->builder);
-      args.push_back(builderBasename);
-    }
-
-    for (auto& i : drv->args) {
-      args.push_back(rewriteStrings(i, inputRewrites));
-    }
-
-    /* Indicate that we managed to set up the build environment. */
-    writeFull(STDERR_FILENO, std::string("\1\n"));
-
-    /* Execute the program.  This should not return. */
-    if (drv->isBuiltin()) {
-      try {
-        BasicDerivation drv2(*drv);
-        for (auto& e : drv2.env) {
-          e.second = rewriteStrings(e.second, inputRewrites);
-        }
-
-        if (drv->builder == "builtin:fetchurl") {
-          builtinFetchurl(drv2, netrcData);
-        } else if (drv->builder == "builtin:buildenv") {
-          builtinBuildenv(drv2);
-        } else {
-          throw Error(format("unsupported builtin function '%1%'") %
-                      std::string(drv->builder, 8));
-        }
-        _exit(0);
-      } catch (std::exception& e) {
-        writeFull(STDERR_FILENO, "error: " + std::string(e.what()) + "\n");
-        _exit(1);
-      }
-    }
-
-    execve(builder, stringsToCharPtrs(args).data(),
-           stringsToCharPtrs(envStrs).data());
-
-    throw SysError(format("executing '%1%'") % drv->builder);
-
-  } catch (std::exception& e) {
-    writeFull(STDERR_FILENO, "\1while setting up the build environment: " +
-                                 std::string(e.what()) + "\n");
-    _exit(1);
-  }
-}
-
-/* Parse a list of reference specifiers.  Each element must either be
-   a store path, or the symbolic name of the output of the derivation
-   (such as `out'). */
-PathSet parseReferenceSpecifiers(Store& store, const BasicDerivation& drv,
-                                 const Strings& paths) {
-  PathSet result;
-  for (auto& i : paths) {
-    if (store.isStorePath(i)) {
-      result.insert(i);
-    } else if (drv.outputs.find(i) != drv.outputs.end()) {
-      result.insert(drv.outputs.find(i)->second.path);
-    } else {
-      throw BuildError(
-          format("derivation contains an illegal reference specifier '%1%'") %
-          i);
-    }
-  }
-  return result;
-}
-
-void DerivationGoal::registerOutputs() {
-  /* When using a build hook, the build hook can register the output
-     as valid (by doing `nix-store --import').  If so we don't have
-     to do anything here. */
-  if (hook) {
-    bool allValid = true;
-    for (auto& i : drv->outputs) {
-      if (!worker.store.isValidPath(i.second.path)) {
-        allValid = false;
-      }
-    }
-    if (allValid) {
-      return;
-    }
-  }
-
-  std::map<std::string, ValidPathInfo> infos;
-
-  /* Set of inodes seen during calls to canonicalisePathMetaData()
-     for this build's outputs.  This needs to be shared between
-     outputs to allow hard links between outputs. */
-  InodesSeen inodesSeen;
-
-  Path checkSuffix = ".check";
-  bool keepPreviousRound = settings.keepFailed || settings.runDiffHook;
-
-  std::exception_ptr delayedException;
-
-  /* Check whether the output paths were created, and grep each
-     output path to determine what other paths it references.  Also make all
-     output paths read-only. */
-  for (auto& i : drv->outputs) {
-    Path path = i.second.path;
-    if (missingPaths.find(path) == missingPaths.end()) {
-      continue;
-    }
-
-    ValidPathInfo info;
-
-    Path actualPath = path;
-    if (useChroot) {
-      actualPath = chrootRootDir + path;
-      if (pathExists(actualPath)) {
-        /* Move output paths from the chroot to the Nix store. */
-        if (buildMode == bmRepair) {
-          replaceValidPath(path, actualPath);
-        } else if (buildMode != bmCheck &&
-                   rename(actualPath.c_str(),
-                          worker.store.toRealPath(path).c_str()) == -1) {
-          throw SysError(format("moving build output '%1%' from the sandbox to "
-                                "the Nix store") %
-                         path);
-        }
-      }
-      if (buildMode != bmCheck) {
-        actualPath = worker.store.toRealPath(path);
-      }
-    }
-
-    if (needsHashRewrite()) {
-      Path redirected = redirectedOutputs[path];
-      if (buildMode == bmRepair &&
-          redirectedBadOutputs.find(path) != redirectedBadOutputs.end() &&
-          pathExists(redirected)) {
-        replaceValidPath(path, redirected);
-      }
-      if (buildMode == bmCheck && !redirected.empty()) {
-        actualPath = redirected;
-      }
-    }
-
-    struct stat st;
-    if (lstat(actualPath.c_str(), &st) == -1) {
-      if (errno == ENOENT) {
-        throw BuildError(
-            format("builder for '%1%' failed to produce output path '%2%'") %
-            drvPath % path);
-      }
-      throw SysError(format("getting attributes of path '%1%'") % actualPath);
-    }
-
-#ifndef __CYGWIN__
-    /* Check that the output is not group or world writable, as
-       that means that someone else can have interfered with the
-       build.  Also, the output should be owned by the build
-       user. */
-    if ((!S_ISLNK(st.st_mode) && ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0u)) ||
-        (buildUser && st.st_uid != buildUser->getUID())) {
-      throw BuildError(format("suspicious ownership or permission on '%1%'; "
-                              "rejecting this build output") %
-                       path);
-    }
-#endif
-
-    /* Apply hash rewriting if necessary. */
-    bool rewritten = false;
-    if (!outputRewrites.empty()) {
-      LOG(WARNING) << "rewriting hashes in '" << path << "'; cross fingers";
-
-      /* Canonicalise first.  This ensures that the path we're
-         rewriting doesn't contain a hard link to /etc/shadow or
-         something like that. */
-      canonicalisePathMetaData(actualPath, buildUser ? buildUser->getUID() : -1,
-                               inodesSeen);
-
-      /* FIXME: this is in-memory. */
-      StringSink sink;
-      dumpPath(actualPath, sink);
-      deletePath(actualPath);
-      sink.s = make_ref<std::string>(rewriteStrings(*sink.s, outputRewrites));
-      StringSource source(*sink.s);
-      restorePath(actualPath, source);
-
-      rewritten = true;
-    }
-
-    /* Check that fixed-output derivations produced the right
-       outputs (i.e., the content hash should match the specified
-       hash). */
-    if (fixedOutput) {
-      bool recursive;
-      Hash h;
-      i.second.parseHashInfo(recursive, h);
-
-      if (!recursive) {
-        /* The output path should be a regular file without
-           execute permission. */
-        if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0) {
-          throw BuildError(
-              format(
-                  "output path '%1%' should be a non-executable regular file") %
-              path);
-        }
-      }
-
-      /* Check the hash. In hash mode, move the path produced by
-         the derivation to its content-addressed location. */
-      Hash h2 = recursive ? hashPath(h.type, actualPath).first
-                          : hashFile(h.type, actualPath);
-
-      Path dest = worker.store.makeFixedOutputPath(recursive, h2,
-                                                   storePathToName(path));
-
-      if (h != h2) {
-        /* Throw an error after registering the path as
-           valid. */
-        worker.hashMismatch = true;
-        delayedException = std::make_exception_ptr(
-            BuildError("hash mismatch in fixed-output derivation '%s':\n  "
-                       "wanted: %s\n  got:    %s",
-                       dest, h.to_string(), h2.to_string()));
-
-        Path actualDest = worker.store.toRealPath(dest);
-
-        if (worker.store.isValidPath(dest)) {
-          std::rethrow_exception(delayedException);
-        }
-
-        if (actualPath != actualDest) {
-          PathLocks outputLocks({actualDest});
-          deletePath(actualDest);
-          if (rename(actualPath.c_str(), actualDest.c_str()) == -1) {
-            throw SysError(format("moving '%1%' to '%2%'") % actualPath % dest);
-          }
-        }
-
-        path = dest;
-        actualPath = actualDest;
-      } else {
-        assert(path == dest);
-      }
-
-      info.ca = makeFixedOutputCA(recursive, h2);
-    }
-
-    /* Get rid of all weird permissions.  This also checks that
-       all files are owned by the build user, if applicable. */
-    canonicalisePathMetaData(actualPath,
-                             buildUser && !rewritten ? buildUser->getUID() : -1,
-                             inodesSeen);
-
-    /* For this output path, find the references to other paths
-       contained in it.  Compute the SHA-256 NAR hash at the same
-       time.  The hash is stored in the database so that we can
-       verify later on whether nobody has messed with the store. */
-    DLOG(INFO) << "scanning for references inside '" << path << "'";
-    HashResult hash;
-    PathSet references = scanForReferences(actualPath, allPaths, hash);
-
-    if (buildMode == bmCheck) {
-      if (!worker.store.isValidPath(path)) {
-        continue;
-      }
-      auto info = *worker.store.queryPathInfo(path);
-      if (hash.first != info.narHash) {
-        worker.checkMismatch = true;
-        if (settings.runDiffHook || settings.keepFailed) {
-          Path dst = worker.store.toRealPath(path + checkSuffix);
-          deletePath(dst);
-          if (rename(actualPath.c_str(), dst.c_str()) != 0) {
-            throw SysError(format("renaming '%1%' to '%2%'") % actualPath %
-                           dst);
-          }
-
-          handleDiffHook(buildUser ? buildUser->getUID() : getuid(),
-                         buildUser ? buildUser->getGID() : getgid(), path, dst,
-                         drvPath, tmpDir, log_sink());
-
-          throw NotDeterministic(
-              format("derivation '%1%' may not be deterministic: output '%2%' "
-                     "differs from '%3%'") %
-              drvPath % path % dst);
-        }
-        throw NotDeterministic(format("derivation '%1%' may not be "
-                                      "deterministic: output '%2%' differs") %
-                               drvPath % path);
-      }
-
-      /* Since we verified the build, it's now ultimately
-         trusted. */
-      if (!info.ultimate) {
-        info.ultimate = true;
-        worker.store.signPathInfo(info);
-        worker.store.registerValidPaths({info});
-      }
-
-      continue;
-    }
-
-    /* For debugging, print out the referenced and unreferenced
-       paths. */
-    for (auto& i : inputPaths) {
-      auto j = references.find(i);
-      if (j == references.end()) {
-        DLOG(INFO) << "unreferenced input: '" << i << "'";
-      } else {
-        DLOG(INFO) << "referenced input: '" << i << "'";
-      }
-    }
-
-    if (curRound == nrRounds) {
-      worker.store.optimisePath(
-          actualPath);  // FIXME: combine with scanForReferences()
-      worker.markContentsGood(path);
-    }
-
-    info.path = path;
-    info.narHash = hash.first;
-    info.narSize = hash.second;
-    info.references = references;
-    info.deriver = drvPath;
-    info.ultimate = true;
-    worker.store.signPathInfo(info);
-
-    if (!info.references.empty()) {
-      info.ca.clear();
-    }
-
-    infos[i.first] = info;
-  }
-
-  if (buildMode == bmCheck) {
-    return;
-  }
-
-  /* Apply output checks. */
-  checkOutputs(infos);
-
-  /* Compare the result with the previous round, and report which
-     path is different, if any.*/
-  if (curRound > 1 && prevInfos != infos) {
-    assert(prevInfos.size() == infos.size());
-    for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end();
-         ++i, ++j) {
-      if (!(*i == *j)) {
-        result.isNonDeterministic = true;
-        Path prev = i->second.path + checkSuffix;
-        bool prevExists = keepPreviousRound && pathExists(prev);
-        auto msg =
-            prevExists
-                ? fmt("output '%1%' of '%2%' differs from '%3%' from previous "
-                      "round",
-                      i->second.path, drvPath, prev)
-                : fmt("output '%1%' of '%2%' differs from previous round",
-                      i->second.path, drvPath);
-
-        handleDiffHook(buildUser ? buildUser->getUID() : getuid(),
-                       buildUser ? buildUser->getGID() : getgid(), prev,
-                       i->second.path, drvPath, tmpDir, log_sink());
-
-        if (settings.enforceDeterminism) {
-          throw NotDeterministic(msg);
-        }
-
-        log_sink() << msg << std::endl;
-        curRound = nrRounds;  // we know enough, bail out early
-      }
-    }
-  }
-
-  /* If this is the first round of several, then move the output out
-     of the way. */
-  if (nrRounds > 1 && curRound == 1 && curRound < nrRounds &&
-      keepPreviousRound) {
-    for (auto& i : drv->outputs) {
-      Path prev = i.second.path + checkSuffix;
-      deletePath(prev);
-      Path dst = i.second.path + checkSuffix;
-      if (rename(i.second.path.c_str(), dst.c_str()) != 0) {
-        throw SysError(format("renaming '%1%' to '%2%'") % i.second.path % dst);
-      }
-    }
-  }
-
-  if (curRound < nrRounds) {
-    prevInfos = infos;
-    return;
-  }
-
-  /* Remove the .check directories if we're done. FIXME: keep them
-     if the result was not determistic? */
-  if (curRound == nrRounds) {
-    for (auto& i : drv->outputs) {
-      Path prev = i.second.path + checkSuffix;
-      deletePath(prev);
-    }
-  }
-
-  /* Register each output path as valid, and register the sets of
-     paths referenced by each of them.  If there are cycles in the
-     outputs, this will fail. */
-  {
-    ValidPathInfos infos2;
-    for (auto& i : infos) {
-      infos2.push_back(i.second);
-    }
-    worker.store.registerValidPaths(infos2);
-  }
-
-  /* In case of a fixed-output derivation hash mismatch, throw an
-     exception now that we have registered the output as valid. */
-  if (delayedException) {
-    std::rethrow_exception(delayedException);
-  }
-}
-
-void DerivationGoal::checkOutputs(
-    const std::map<Path, ValidPathInfo>& outputs) {
-  std::map<Path, const ValidPathInfo&> outputsByPath;
-  for (auto& output : outputs) {
-    outputsByPath.emplace(output.second.path, output.second);
-  }
-
-  for (auto& output : outputs) {
-    auto& outputName = output.first;
-    auto& info = output.second;
-
-    struct Checks {
-      bool ignoreSelfRefs = false;
-      std::optional<uint64_t> maxSize, maxClosureSize;
-      std::optional<Strings> allowedReferences, allowedRequisites,
-          disallowedReferences, disallowedRequisites;
-    };
-
-    /* Compute the closure and closure size of some output. This
-       is slightly tricky because some of its references (namely
-       other outputs) may not be valid yet. */
-    auto getClosure = [&](const Path& path) {
-      uint64_t closureSize = 0;
-      PathSet pathsDone;
-      std::queue<Path> pathsLeft;
-      pathsLeft.push(path);
-
-      while (!pathsLeft.empty()) {
-        auto path = pathsLeft.front();
-        pathsLeft.pop();
-        if (!pathsDone.insert(path).second) {
-          continue;
-        }
-
-        auto i = outputsByPath.find(path);
-        if (i != outputsByPath.end()) {
-          closureSize += i->second.narSize;
-          for (auto& ref : i->second.references) {
-            pathsLeft.push(ref);
-          }
-        } else {
-          auto info = worker.store.queryPathInfo(path);
-          closureSize += info->narSize;
-          for (auto& ref : info->references) {
-            pathsLeft.push(ref);
-          }
-        }
-      }
-
-      return std::make_pair(pathsDone, closureSize);
-    };
-
-    auto applyChecks = [&](const Checks& checks) {
-      if (checks.maxSize && info.narSize > *checks.maxSize) {
-        throw BuildError(
-            "path '%s' is too large at %d bytes; limit is %d bytes", info.path,
-            info.narSize, *checks.maxSize);
-      }
-
-      if (checks.maxClosureSize) {
-        uint64_t closureSize = getClosure(info.path).second;
-        if (closureSize > *checks.maxClosureSize) {
-          throw BuildError(
-              "closure of path '%s' is too large at %d bytes; limit is %d "
-              "bytes",
-              info.path, closureSize, *checks.maxClosureSize);
-        }
-      }
-
-      auto checkRefs = [&](const std::optional<Strings>& value, bool allowed,
-                           bool recursive) {
-        if (!value) {
-          return;
-        }
-
-        PathSet spec = parseReferenceSpecifiers(worker.store, *drv, *value);
-
-        PathSet used =
-            recursive ? getClosure(info.path).first : info.references;
-
-        if (recursive && checks.ignoreSelfRefs) {
-          used.erase(info.path);
-        }
-
-        PathSet badPaths;
-
-        for (auto& i : used) {
-          if (allowed) {
-            if (spec.count(i) == 0u) {
-              badPaths.insert(i);
-            }
-          } else {
-            if (spec.count(i) != 0u) {
-              badPaths.insert(i);
-            }
-          }
-        }
-
-        if (!badPaths.empty()) {
-          std::string badPathsStr;
-          for (auto& i : badPaths) {
-            badPathsStr += "\n  ";
-            badPathsStr += i;
-          }
-          throw BuildError(
-              "output '%s' is not allowed to refer to the following paths:%s",
-              info.path, badPathsStr);
-        }
-      };
-
-      checkRefs(checks.allowedReferences, true, false);
-      checkRefs(checks.allowedRequisites, true, true);
-      checkRefs(checks.disallowedReferences, false, false);
-      checkRefs(checks.disallowedRequisites, false, true);
-    };
-
-    if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
-      auto outputChecks = structuredAttrs->find("outputChecks");
-      if (outputChecks != structuredAttrs->end()) {
-        auto output = outputChecks->find(outputName);
-
-        if (output != outputChecks->end()) {
-          Checks checks;
-
-          auto maxSize = output->find("maxSize");
-          if (maxSize != output->end()) {
-            checks.maxSize = maxSize->get<uint64_t>();
-          }
-
-          auto maxClosureSize = output->find("maxClosureSize");
-          if (maxClosureSize != output->end()) {
-            checks.maxClosureSize = maxClosureSize->get<uint64_t>();
-          }
-
-          auto get = [&](const std::string& name) -> std::optional<Strings> {
-            auto i = output->find(name);
-            if (i != output->end()) {
-              Strings res;
-              for (auto& j : *i) {
-                if (!j.is_string()) {
-                  throw Error(
-                      "attribute '%s' of derivation '%s' must be a list of "
-                      "strings",
-                      name, drvPath);
-                }
-                res.push_back(j.get<std::string>());
-              }
-              checks.disallowedRequisites = res;
-              return res;
-            }
-            return {};
-          };
-
-          checks.allowedReferences = get("allowedReferences");
-          checks.allowedRequisites = get("allowedRequisites");
-          checks.disallowedReferences = get("disallowedReferences");
-          checks.disallowedRequisites = get("disallowedRequisites");
-
-          applyChecks(checks);
-        }
-      }
-    } else {
-      // legacy non-structured-attributes case
-      Checks checks;
-      checks.ignoreSelfRefs = true;
-      checks.allowedReferences = parsedDrv->getStringsAttr("allowedReferences");
-      checks.allowedRequisites = parsedDrv->getStringsAttr("allowedRequisites");
-      checks.disallowedReferences =
-          parsedDrv->getStringsAttr("disallowedReferences");
-      checks.disallowedRequisites =
-          parsedDrv->getStringsAttr("disallowedRequisites");
-      applyChecks(checks);
-    }
-  }
-}
-
-Path DerivationGoal::openLogFile() {
-  logSize = 0;
-
-  if (!settings.keepLog) {
-    return "";
-  }
-
-  std::string baseName = baseNameOf(drvPath);
-
-  /* Create a log file. */
-  Path dir = fmt("%s/%s/%s/", worker.store.logDir, nix::LocalStore::drvsLogDir,
-                 std::string(baseName, 0, 2));
-  createDirs(dir);
-
-  Path logFileName = fmt("%s/%s%s", dir, std::string(baseName, 2),
-                         settings.compressLog ? ".bz2" : "");
-
-  fdLogFile = AutoCloseFD(open(logFileName.c_str(),
-                               O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666));
-  if (!fdLogFile) {
-    throw SysError(format("creating log file '%1%'") % logFileName);
-  }
-
-  logFileSink = std::make_shared<FdSink>(fdLogFile.get());
-
-  if (settings.compressLog) {
-    logSink = std::shared_ptr<CompressionSink>(
-        makeCompressionSink("bzip2", *logFileSink));
-  } else {
-    logSink = logFileSink;
-  }
-
-  return logFileName;
-}
-
-void DerivationGoal::closeLogFile() {
-  auto logSink2 = std::dynamic_pointer_cast<CompressionSink>(logSink);
-  if (logSink2) {
-    logSink2->finish();
-  }
-  if (logFileSink) {
-    logFileSink->flush();
-  }
-  logSink = logFileSink = nullptr;
-  fdLogFile = AutoCloseFD(-1);
-}
-
-void DerivationGoal::deleteTmpDir(bool force) {
-  if (!tmpDir.empty()) {
-    /* Don't keep temporary directories for builtins because they
-       might have privileged stuff (like a copy of netrc). */
-    if (settings.keepFailed && !force && !drv->isBuiltin()) {
-      log_sink() << "note: keeping build directory '" << tmpDir << "'"
-                 << std::endl;
-      chmod(tmpDir.c_str(), 0755);
-    } else {
-      deletePath(tmpDir);
-    }
-    tmpDir = "";
-  }
-}
-
-// TODO(tazjin): What ... what does this function ... do?
-void DerivationGoal::handleChildOutput(int fd, const std::string& data) {
-  if ((hook && fd == hook->builderOut.readSide.get()) ||
-      (!hook && fd == builderOut.readSide.get())) {
-    logSize += data.size();
-    if (settings.maxLogSize && logSize > settings.maxLogSize) {
-      log_sink() << getName() << " killed after writing more than "
-                 << settings.maxLogSize << " bytes of log output" << std::endl;
-      killChild();
-      done(BuildResult::LogLimitExceeded);
-      return;
-    }
-
-    for (auto c : data) {
-      if (c == '\r') {
-        currentLogLinePos = 0;
-      } else if (c == '\n') {
-        flushLine();
-      } else {
-        if (currentLogLinePos >= currentLogLine.size()) {
-          currentLogLine.resize(currentLogLinePos + 1);
-        }
-        currentLogLine[currentLogLinePos++] = c;
-      }
-    }
-
-    if (logSink) {
-      (*logSink)(data);
-    }
-  }
-
-  if (hook && fd == hook->fromHook.readSide.get()) {
-    for (auto c : data) {
-      if (c == '\n') {
-        currentHookLine.clear();
-      } else {
-        currentHookLine += c;
-      }
-    }
-  }
-}
-
-void DerivationGoal::handleEOF(int /* fd */) {
-  if (!currentLogLine.empty()) {
-    flushLine();
-  }
-  worker.wakeUp(shared_from_this());
-}
-
-void DerivationGoal::flushLine() {
-  if (settings.verboseBuild &&
-      (settings.printRepeatedBuilds || curRound == 1)) {
-    log_sink() << currentLogLine << std::endl;
-  } else {
-    logTail.push_back(currentLogLine);
-    if (logTail.size() > settings.logLines) {
-      logTail.pop_front();
-    }
-  }
-
-  currentLogLine = "";
-  currentLogLinePos = 0;
-}
-
-PathSet DerivationGoal::checkPathValidity(bool returnValid, bool checkHash) {
-  PathSet result;
-  for (auto& i : drv->outputs) {
-    if (!wantOutput(i.first, wantedOutputs)) {
-      continue;
-    }
-    bool good = worker.store.isValidPath(i.second.path) &&
-                (!checkHash || worker.pathContentsGood(i.second.path));
-    if (good == returnValid) {
-      result.insert(i.second.path);
-    }
-  }
-  return result;
-}
-
-Path DerivationGoal::addHashRewrite(const Path& path) {
-  std::string h1 = std::string(path, worker.store.storeDir.size() + 1, 32);
-  std::string h2 =
-      std::string(hashString(htSHA256, "rewrite:" + drvPath + ":" + path)
-                      .to_string(Base32, false),
-                  0, 32);
-  Path p = worker.store.storeDir + "/" + h2 +
-           std::string(path, worker.store.storeDir.size() + 33);
-  deletePath(p);
-  assert(path.size() == p.size());
-  inputRewrites[h1] = h2;
-  outputRewrites[h2] = h1;
-  redirectedOutputs[path] = p;
-  return p;
-}
-
-void DerivationGoal::done(BuildResult::Status status, const std::string& msg) {
-  result.status = status;
-  result.errorMsg = msg;
-  amDone(result.success() ? ecSuccess : ecFailed);
-  if (result.status == BuildResult::TimedOut) {
-    worker.timedOut = true;
-  }
-  if (result.status == BuildResult::PermanentFailure) {
-    worker.permanentFailure = true;
-  }
-
-  mcExpectedBuilds.reset();
-  mcRunningBuilds.reset();
-
-  if (result.success()) {
-    if (status == BuildResult::Built) {
-      worker.doneBuilds++;
-    }
-  } else {
-    if (status != BuildResult::DependencyFailed) {
-      worker.failedBuilds++;
-    }
-  }
-}
-
-//////////////////////////////////////////////////////////////////////
-
-class SubstitutionGoal : public Goal {
-  friend class Worker;
-
- private:
-  /* The store path that should be realised through a substitute. */
-  Path storePath;
-
-  /* The remaining substituters. */
-  std::list<ref<Store>> subs;
-
-  /* The current substituter. */
-  std::shared_ptr<Store> sub;
-
-  /* Whether a substituter failed. */
-  bool substituterFailed = false;
-
-  /* Path info returned by the substituter's query info operation. */
-  std::shared_ptr<const ValidPathInfo> info;
-
-  /* Pipe for the substituter's standard output. */
-  Pipe outPipe;
-
-  /* The substituter thread. */
-  std::thread thr;
-
-  std::promise<void> promise;
-
-  /* Whether to try to repair a valid path. */
-  RepairFlag repair;
-
-  /* Location where we're downloading the substitute.  Differs from
-     storePath when doing a repair. */
-  Path destPath;
-
-  std::unique_ptr<MaintainCount<uint64_t>> maintainExpectedSubstitutions,
-      maintainRunningSubstitutions, maintainExpectedNar,
-      maintainExpectedDownload;
-
-  using GoalState = void (SubstitutionGoal::*)();
-  GoalState state;
-
- public:
-  SubstitutionGoal(Worker& worker, const Path& storePath,
-                   RepairFlag repair = NoRepair);
-
-  ~SubstitutionGoal() override;
-
-  void timedOut() override { abort(); };
-
-  std::string key() override {
-    /* "a$" ensures substitution goals happen before derivation
-       goals. */
-    return "a$" + storePathToName(storePath) + "$" + storePath;
-  }
-
-  void work() override;
-
-  /* The states. */
-  void init();
-  void tryNext();
-  void gotInfo();
-  void referencesValid();
-  void tryToRun();
-  void finished();
-
-  /* Callback used by the worker to write to the log. */
-  void handleChildOutput(int fd, const std::string& data) override;
-  void handleEOF(int fd) override;
-
-  Path getStorePath() { return storePath; }
-
-  void amDone(ExitCode result) override { Goal::amDone(result); }
-};
-
-SubstitutionGoal::SubstitutionGoal(Worker& worker, const Path& storePath,
-                                   RepairFlag repair)
-    : Goal(worker), repair(repair) {
-  this->storePath = storePath;
-  state = &SubstitutionGoal::init;
-  name = absl::StrCat("substitution of ", storePath);
-  trace("created");
-  maintainExpectedSubstitutions =
-      std::make_unique<MaintainCount<uint64_t>>(worker.expectedSubstitutions);
-}
-
-SubstitutionGoal::~SubstitutionGoal() {
-  try {
-    if (thr.joinable()) {
-      // FIXME: signal worker thread to quit.
-      thr.join();
-      worker.childTerminated(this);
-    }
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-void SubstitutionGoal::work() { (this->*state)(); }
-
-void SubstitutionGoal::init() {
-  trace("init");
-
-  worker.store.addTempRoot(storePath);
-
-  /* If the path already exists we're done. */
-  if ((repair == 0u) && worker.store.isValidPath(storePath)) {
-    amDone(ecSuccess);
-    return;
-  }
-
-  if (settings.readOnlyMode) {
-    throw Error(
-        format(
-            "cannot substitute path '%1%' - no write access to the Nix store") %
-        storePath);
-  }
-
-  subs = settings.useSubstitutes ? getDefaultSubstituters()
-                                 : std::list<ref<Store>>();
-
-  tryNext();
-}
-
-void SubstitutionGoal::tryNext() {
-  trace("trying next substituter");
-
-  if (subs.empty()) {
-    /* None left.  Terminate this goal and let someone else deal
-       with it. */
-    DLOG(WARNING)
-        << "path '" << storePath
-        << "' is required, but there is no substituter that can build it";
-
-    /* Hack: don't indicate failure if there were no substituters.
-       In that case the calling derivation should just do a
-       build. */
-    amDone(substituterFailed ? ecFailed : ecNoSubstituters);
-
-    if (substituterFailed) {
-      worker.failedSubstitutions++;
-    }
-
-    return;
-  }
-
-  sub = subs.front();
-  subs.pop_front();
-
-  if (sub->storeDir != worker.store.storeDir) {
-    tryNext();
-    return;
-  }
-
-  try {
-    // FIXME: make async
-    info = sub->queryPathInfo(storePath);
-  } catch (InvalidPath&) {
-    tryNext();
-    return;
-  } catch (SubstituterDisabled&) {
-    if (settings.tryFallback) {
-      tryNext();
-      return;
-    }
-    throw;
-  } catch (Error& e) {
-    if (settings.tryFallback) {
-      log_sink() << e.what() << std::endl;
-      tryNext();
-      return;
-    }
-    throw;
-  }
-
-  /* Update the total expected download size. */
-  auto narInfo = std::dynamic_pointer_cast<const NarInfo>(info);
-
-  maintainExpectedNar = std::make_unique<MaintainCount<uint64_t>>(
-      worker.expectedNarSize, info->narSize);
-
-  maintainExpectedDownload =
-      narInfo && (narInfo->fileSize != 0u)
-          ? std::make_unique<MaintainCount<uint64_t>>(
-                worker.expectedDownloadSize, narInfo->fileSize)
-          : nullptr;
-
-  /* Bail out early if this substituter lacks a valid
-     signature. LocalStore::addToStore() also checks for this, but
-     only after we've downloaded the path. */
-  if (worker.store.requireSigs && !sub->isTrusted &&
-      (info->checkSignatures(worker.store, worker.store.getPublicKeys()) ==
-       0u)) {
-    log_sink() << "substituter '" << sub->getUri()
-               << "' does not have a valid signature for path '" << storePath
-               << "'" << std::endl;
-    tryNext();
-    return;
-  }
-
-  /* To maintain the closure invariant, we first have to realise the
-     paths referenced by this one. */
-  for (auto& i : info->references) {
-    if (i != storePath) { /* ignore self-references */
-      addWaitee(worker.makeSubstitutionGoal(i));
-    }
-  }
-
-  if (waitees.empty()) { /* to prevent hang (no wake-up event) */
-    referencesValid();
-  } else {
-    state = &SubstitutionGoal::referencesValid;
-  }
-}
-
-void SubstitutionGoal::referencesValid() {
-  trace("all references realised");
-
-  if (nrFailed > 0) {
-    DLOG(WARNING) << "some references of path '" << storePath
-                  << "' could not be realised";
-    amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure
-                                                           : ecFailed);
-    return;
-  }
-
-  for (auto& i : info->references) {
-    if (i != storePath) { /* ignore self-references */
-      assert(worker.store.isValidPath(i));
-    }
-  }
-
-  state = &SubstitutionGoal::tryToRun;
-  worker.wakeUp(shared_from_this());
-}
-
-void SubstitutionGoal::tryToRun() {
-  trace("trying to run");
-
-  /* Make sure that we are allowed to start a build.  Note that even
-     if maxBuildJobs == 0 (no local builds allowed), we still allow
-     a substituter to run.  This is because substitutions cannot be
-     distributed to another machine via the build hook. */
-  if (worker.getNrLocalBuilds() >=
-      std::max(1U, (unsigned int)settings.maxBuildJobs)) {
-    worker.waitForBuildSlot(shared_from_this());
-    return;
-  }
-
-  maintainRunningSubstitutions =
-      std::make_unique<MaintainCount<uint64_t>>(worker.runningSubstitutions);
-
-  outPipe.create();
-
-  promise = std::promise<void>();
-
-  thr = std::thread([this]() {
-    try {
-      /* Wake up the worker loop when we're done. */
-      Finally updateStats([this]() { outPipe.writeSide = AutoCloseFD(-1); });
-
-      copyStorePath(ref<Store>(sub),
-                    ref<Store>(worker.store.shared_from_this()), storePath,
-                    repair, sub->isTrusted ? NoCheckSigs : CheckSigs);
-
-      promise.set_value();
-    } catch (...) {
-      promise.set_exception(std::current_exception());
-    }
-  });
-
-  worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true,
-                      false);
-
-  state = &SubstitutionGoal::finished;
-}
-
-void SubstitutionGoal::finished() {
-  trace("substitute finished");
-
-  thr.join();
-  worker.childTerminated(this);
-
-  try {
-    promise.get_future().get();
-  } catch (std::exception& e) {
-    log_sink() << e.what() << std::endl;
-
-    /* Cause the parent build to fail unless --fallback is given,
-       or the substitute has disappeared. The latter case behaves
-       the same as the substitute never having existed in the
-       first place. */
-    try {
-      throw;
-    } catch (SubstituteGone&) {
-    } catch (...) {
-      substituterFailed = true;
-    }
-
-    /* Try the next substitute. */
-    state = &SubstitutionGoal::tryNext;
-    worker.wakeUp(shared_from_this());
-    return;
-  }
-
-  worker.markContentsGood(storePath);
-
-  DLOG(INFO) << "substitution of path '" << storePath << "' succeeded";
-
-  maintainRunningSubstitutions.reset();
-
-  maintainExpectedSubstitutions.reset();
-  worker.doneSubstitutions++;
-
-  if (maintainExpectedDownload) {
-    auto fileSize = maintainExpectedDownload->delta;
-    maintainExpectedDownload.reset();
-    worker.doneDownloadSize += fileSize;
-  }
-
-  worker.doneNarSize += maintainExpectedNar->delta;
-  maintainExpectedNar.reset();
-
-  amDone(ecSuccess);
-}
-
-void SubstitutionGoal::handleChildOutput(int fd, const std::string& data) {}
-
-void SubstitutionGoal::handleEOF(int fd) {
-  if (fd == outPipe.readSide.get()) {
-    worker.wakeUp(shared_from_this());
-  }
-}
-
-//////////////////////////////////////////////////////////////////////
-
-ABSL_CONST_INIT static thread_local bool working = false;
-
-Worker::Worker(LocalStore& store, std::ostream& log_sink)
-    : log_sink_(log_sink), store(store) {
-  // Debugging: prevent recursive workers.
-  // TODO(grfn): Do we need this?
-  CHECK(!working) << "Worker initialized during execution of a worker";
-  working = true;
-  nrLocalBuilds = 0;
-  lastWokenUp = steady_time_point::min();
-  permanentFailure = false;
-  timedOut = false;
-  hashMismatch = false;
-  checkMismatch = false;
-}
-
-Worker::~Worker() {
-  working = false;
-
-  /* Explicitly get rid of all strong pointers now.  After this all
-     goals that refer to this worker should be gone.  (Otherwise we
-     are in trouble, since goals may call childTerminated() etc. in
-     their destructors). */
-  topGoals.clear();
-
-  assert(expectedSubstitutions == 0);
-  assert(expectedDownloadSize == 0);
-  assert(expectedNarSize == 0);
-}
-
-GoalPtr Worker::makeDerivationGoal(const Path& drv_path,
-                                   const StringSet& wantedOutputs,
-                                   BuildMode buildMode) {
-  GoalPtr goal = derivationGoals[drv_path].lock();
-  if (!goal) {
-    goal = std::make_shared<DerivationGoal>(*this, drv_path, wantedOutputs,
-                                            buildMode);
-    derivationGoals[drv_path] = goal;
-    wakeUp(goal);
-  } else {
-    (dynamic_cast<DerivationGoal*>(goal.get()))
-        ->addWantedOutputs(wantedOutputs);
-  }
-  return goal;
-}
-
-std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(
-    const Path& drvPath, const BasicDerivation& drv, BuildMode buildMode) {
-  std::shared_ptr<DerivationGoal> goal =
-      std::make_shared<DerivationGoal>(*this, drvPath, drv, buildMode);
-  wakeUp(goal);
-  return goal;
-}
-
-GoalPtr Worker::makeSubstitutionGoal(const Path& path, RepairFlag repair) {
-  GoalPtr goal = substitutionGoals[path].lock();
-  if (!goal) {
-    goal = std::make_shared<SubstitutionGoal>(*this, path, repair);
-    substitutionGoals[path] = goal;
-    wakeUp(goal);
-  }
-  return goal;
-}
-
-static void removeGoal(const GoalPtr& goal, WeakGoalMap& goalMap) {
-  /* !!! inefficient */
-  for (auto i = goalMap.begin(); i != goalMap.end();) {
-    if (i->second.lock() == goal) {
-      auto j = i;
-      ++j;
-      goalMap.erase(i);
-      i = j;
-    } else {
-      ++i;
-    }
-  }
-}
-
-void Worker::removeGoal(const GoalPtr& goal) {
-  nix::removeGoal(goal, derivationGoals);
-  nix::removeGoal(goal, substitutionGoals);
-  if (topGoals.find(goal) != topGoals.end()) {
-    topGoals.erase(goal);
-    /* If a top-level goal failed, then kill all other goals
-       (unless keepGoing was set). */
-    if (goal->getExitCode() == Goal::ecFailed && !settings.keepGoing) {
-      topGoals.clear();
-    }
-  }
-
-  /* Wake up goals waiting for any goal to finish. */
-  for (auto& i : waitingForAnyGoal) {
-    GoalPtr goal = i.lock();
-    if (goal) {
-      wakeUp(goal);
-    }
-  }
-
-  waitingForAnyGoal.clear();
-}
-
-void Worker::wakeUp(const GoalPtr& goal) {
-  goal->trace("woken up");
-  addToWeakGoals(awake, goal);
-}
-
-unsigned Worker::getNrLocalBuilds() { return nrLocalBuilds; }
-
-void Worker::childStarted(const GoalPtr& goal, const std::set<int>& fds,
-                          bool inBuildSlot, bool respectTimeouts) {
-  Child child;
-  child.goal = goal;
-  child.goal2 = goal.get();
-  child.fds = fds;
-  child.timeStarted = child.lastOutput = steady_time_point::clock::now();
-  child.inBuildSlot = inBuildSlot;
-  child.respectTimeouts = respectTimeouts;
-  children.emplace_back(child);
-  if (inBuildSlot) {
-    nrLocalBuilds++;
-  }
-}
-
-void Worker::childTerminated(Goal* goal, bool wakeSleepers) {
-  auto i =
-      std::find_if(children.begin(), children.end(),
-                   [&](const Child& child) { return child.goal2 == goal; });
-  if (i == children.end()) {
-    return;
-  }
-
-  if (i->inBuildSlot) {
-    assert(nrLocalBuilds > 0);
-    nrLocalBuilds--;
-  }
-
-  children.erase(i);
-
-  if (wakeSleepers) {
-    /* Wake up goals waiting for a build slot. */
-    for (auto& j : wantingToBuild) {
-      GoalPtr goal = j.lock();
-      if (goal) {
-        wakeUp(goal);
-      }
-    }
-
-    wantingToBuild.clear();
-  }
-}
-
-void Worker::waitForBuildSlot(const GoalPtr& goal) {
-  DLOG(INFO) << "wait for build slot";
-  if (getNrLocalBuilds() < settings.maxBuildJobs) {
-    wakeUp(goal); /* we can do it right away */
-  } else {
-    addToWeakGoals(wantingToBuild, goal);
-  }
-}
-
-void Worker::waitForAnyGoal(GoalPtr goal) {
-  DLOG(INFO) << "wait for any goal";
-  addToWeakGoals(waitingForAnyGoal, std::move(goal));
-}
-
-void Worker::waitForAWhile(GoalPtr goal) {
-  DLOG(INFO) << "wait for a while";
-  addToWeakGoals(waitingForAWhile, std::move(goal));
-}
-
-void Worker::run(const Goals& _topGoals) {
-  for (auto& i : _topGoals) {
-    topGoals.insert(i);
-  }
-
-  DLOG(INFO) << "entered goal loop";
-
-  while (true) {
-    checkInterrupt();
-
-    store.autoGC(false);
-
-    /* Call every wake goal (in the ordering established by
-       CompareGoalPtrs). */
-    while (!awake.empty() && !topGoals.empty()) {
-      Goals awake2;
-      for (auto& i : awake) {
-        GoalPtr goal = i.lock();
-        if (goal) {
-          awake2.insert(goal);
-        }
-      }
-      awake.clear();
-      for (auto& goal : awake2) {
-        checkInterrupt();
-        goal->work();
-        if (topGoals.empty()) {
-          break;
-        }  // stuff may have been cancelled
-      }
-    }
-
-    if (topGoals.empty()) {
-      break;
-    }
-
-    /* Wait for input. */
-    if (!children.empty() || !waitingForAWhile.empty()) {
-      waitForInput();
-    } else {
-      if (awake.empty() && 0 == settings.maxBuildJobs) {
-        throw Error(
-            "unable to start any build; either increase '--max-jobs' "
-            "or enable remote builds");
-      }
-      assert(!awake.empty());
-    }
-  }
-
-  /* If --keep-going is not set, it's possible that the main goal
-     exited while some of its subgoals were still active.  But if
-     --keep-going *is* set, then they must all be finished now. */
-  assert(!settings.keepGoing || awake.empty());
-  assert(!settings.keepGoing || wantingToBuild.empty());
-  assert(!settings.keepGoing || children.empty());
-}
-
-void Worker::waitForInput() {
-  DLOG(INFO) << "waiting for children";
-
-  /* Process output from the file descriptors attached to the
-     children, namely log output and output path creation commands.
-     We also use this to detect child termination: if we get EOF on
-     the logger pipe of a build, we assume that the builder has
-     terminated. */
-
-  bool useTimeout = false;
-  struct timeval timeout;
-  timeout.tv_usec = 0;
-  auto before = steady_time_point::clock::now();
-
-  /* If we're monitoring for silence on stdout/stderr, or if there
-     is a build timeout, then wait for input until the first
-     deadline for any child. */
-  auto nearest = steady_time_point::max();  // nearest deadline
-  if (settings.minFree.get() != 0) {
-    // Periodicallty wake up to see if we need to run the garbage collector.
-    nearest = before + std::chrono::seconds(10);
-  }
-  for (auto& i : children) {
-    if (!i.respectTimeouts) {
-      continue;
-    }
-    if (0 != settings.maxSilentTime) {
-      nearest = std::min(
-          nearest, i.lastOutput + std::chrono::seconds(settings.maxSilentTime));
-    }
-    if (0 != settings.buildTimeout) {
-      nearest = std::min(
-          nearest, i.timeStarted + std::chrono::seconds(settings.buildTimeout));
-    }
-  }
-  if (nearest != steady_time_point::max()) {
-    timeout.tv_sec = std::max(
-        1L, static_cast<long>(std::chrono::duration_cast<std::chrono::seconds>(
-                                  nearest - before)
-                                  .count()));
-    useTimeout = true;
-  }
-
-  /* If we are polling goals that are waiting for a lock, then wake
-     up after a few seconds at most. */
-  if (!waitingForAWhile.empty()) {
-    useTimeout = true;
-    if (lastWokenUp == steady_time_point::min()) {
-      DLOG(WARNING) << "waiting for locks or build slots...";
-    }
-    if (lastWokenUp == steady_time_point::min() || lastWokenUp > before) {
-      lastWokenUp = before;
-    }
-    timeout.tv_sec = std::max(
-        1L, static_cast<long>(std::chrono::duration_cast<std::chrono::seconds>(
-                                  lastWokenUp +
-                                  std::chrono::seconds(settings.pollInterval) -
-                                  before)
-                                  .count()));
-  } else {
-    lastWokenUp = steady_time_point::min();
-  }
-
-  if (useTimeout) {
-    DLOG(INFO) << "sleeping " << timeout.tv_sec << " seconds";
-  }
-
-  /* Use select() to wait for the input side of any logger pipe to
-     become `available'.  Note that `available' (i.e., non-blocking)
-     includes EOF. */
-  fd_set fds;
-  FD_ZERO(&fds);
-  int fdMax = 0;
-  for (auto& i : children) {
-    for (auto& j : i.fds) {
-      if (j >= FD_SETSIZE) {
-        throw Error("reached FD_SETSIZE limit");
-      }
-      FD_SET(j, &fds);
-      if (j >= fdMax) {
-        fdMax = j + 1;
-      }
-    }
-  }
-
-  if (select(fdMax, &fds, nullptr, nullptr, useTimeout ? &timeout : nullptr) ==
-      -1) {
-    if (errno == EINTR) {
-      return;
-    }
-    throw SysError("waiting for input");
-  }
-
-  auto after = steady_time_point::clock::now();
-
-  /* Process all available file descriptors. FIXME: this is
-     O(children * fds). */
-  decltype(children)::iterator i;
-  for (auto j = children.begin(); j != children.end(); j = i) {
-    i = std::next(j);
-
-    checkInterrupt();
-
-    GoalPtr goal = j->goal.lock();
-    assert(goal);
-
-    std::set<int> fds2(j->fds);
-    std::vector<unsigned char> buffer(4096);
-    for (auto& k : fds2) {
-      if (FD_ISSET(k, &fds)) {
-        ssize_t rd = read(k, buffer.data(), buffer.size());
-        // FIXME: is there a cleaner way to handle pt close
-        // than EIO? Is this even standard?
-        if (rd == 0 || (rd == -1 && errno == EIO)) {
-          DLOG(WARNING) << goal->getName() << ": got EOF";
-          goal->handleEOF(k);
-          j->fds.erase(k);
-        } else if (rd == -1) {
-          if (errno != EINTR) {
-            throw SysError("%s: read failed", goal->getName());
-          }
-        } else {
-          DLOG(INFO) << goal->getName() << ": read " << rd << " bytes";
-          std::string data(reinterpret_cast<char*>(buffer.data()), rd);
-          j->lastOutput = after;
-          goal->handleChildOutput(k, data);
-        }
-      }
-    }
-
-    if (goal->getExitCode() == Goal::ecBusy && 0 != settings.maxSilentTime &&
-        j->respectTimeouts &&
-        after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime)) {
-      log_sink_ << goal->getName() << " timed out after "
-                << settings.maxSilentTime << " seconds of silence";
-      goal->timedOut();
-    }
-
-    else if (goal->getExitCode() == Goal::ecBusy &&
-             0 != settings.buildTimeout && j->respectTimeouts &&
-             after - j->timeStarted >=
-                 std::chrono::seconds(settings.buildTimeout)) {
-      log_sink_ << goal->getName() << " timed out after "
-                << settings.buildTimeout << " seconds";
-      goal->timedOut();
-    }
-  }
-
-  if (!waitingForAWhile.empty() &&
-      lastWokenUp + std::chrono::seconds(settings.pollInterval) <= after) {
-    lastWokenUp = after;
-    for (auto& i : waitingForAWhile) {
-      GoalPtr goal = i.lock();
-      if (goal) {
-        wakeUp(goal);
-      }
-    }
-    waitingForAWhile.clear();
-  }
-}
-
-unsigned int Worker::exitStatus() {
-  /*
-   * 1100100
-   *    ^^^^
-   *    |||`- timeout
-   *    ||`-- output hash mismatch
-   *    |`--- build failure
-   *    `---- not deterministic
-   */
-  unsigned int mask = 0;
-  bool buildFailure = permanentFailure || timedOut || hashMismatch;
-  if (buildFailure) {
-    mask |= 0x04;  // 100
-  }
-  if (timedOut) {
-    mask |= 0x01;  // 101
-  }
-  if (hashMismatch) {
-    mask |= 0x02;  // 102
-  }
-  if (checkMismatch) {
-    mask |= 0x08;  // 104
-  }
-
-  if (mask != 0u) {
-    mask |= 0x60;
-  }
-  return mask != 0u ? mask : 1;
-}
-
-bool Worker::pathContentsGood(const Path& path) {
-  auto i = pathContentsGoodCache.find(path);
-  if (i != pathContentsGoodCache.end()) {
-    return i->second;
-  }
-  log_sink_ << "checking path '" << path << "'...";
-  auto info = store.queryPathInfo(path);
-  bool res;
-  if (!pathExists(path)) {
-    res = false;
-  } else {
-    HashResult current = hashPath(info->narHash.type, path);
-    Hash nullHash(htSHA256);
-    res = info->narHash == nullHash || info->narHash == current.first;
-  }
-  pathContentsGoodCache[path] = res;
-  if (!res) {
-    log_sink_ << "path '" << path << "' is corrupted or missing!";
-  }
-  return res;
-}
-
-void Worker::markContentsGood(const Path& path) {
-  pathContentsGoodCache[path] = true;
-}
-
-//////////////////////////////////////////////////////////////////////
-
-static void primeCache(Store& store, const PathSet& paths) {
-  PathSet willBuild;
-  PathSet willSubstitute;
-  PathSet unknown;
-  unsigned long long downloadSize;
-  unsigned long long narSize;
-  store.queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize,
-                     narSize);
-
-  if (!willBuild.empty() && 0 == settings.maxBuildJobs &&
-      getMachines().empty()) {
-    throw Error(
-        "%d derivations need to be built, but neither local builds "
-        "('--max-jobs') "
-        "nor remote builds ('--builders') are enabled",
-        willBuild.size());
-  }
-}
-
-absl::Status LocalStore::buildPaths(std::ostream& log_sink,
-                                    const PathSet& drvPaths,
-                                    BuildMode build_mode) {
-  Worker worker(*this, log_sink);
-
-  primeCache(*this, drvPaths);
-
-  Goals goals;
-  for (auto& i : drvPaths) {
-    DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i);
-    if (isDerivation(i2.first)) {
-      goals.insert(worker.makeDerivationGoal(i2.first, i2.second, build_mode));
-    } else {
-      goals.insert(worker.makeSubstitutionGoal(
-          i, build_mode == bmRepair ? Repair : NoRepair));
-    }
-  }
-
-  worker.run(goals);
-
-  PathSet failed;
-  for (auto& i : goals) {
-    if (i->getExitCode() != Goal::ecSuccess) {
-      auto* i2 = dynamic_cast<DerivationGoal*>(i.get());
-      if (i2 != nullptr) {
-        failed.insert(i2->getDrvPath());
-      } else {
-        failed.insert(dynamic_cast<SubstitutionGoal*>(i.get())->getStorePath());
-      }
-    }
-  }
-
-  if (!failed.empty()) {
-    return absl::Status(
-        absl::StatusCode::kInternal,
-        absl::StrFormat("build of %s failed (exit code %d)", showPaths(failed),
-                        worker.exitStatus()));
-  }
-  return absl::OkStatus();
-}
-
-BuildResult LocalStore::buildDerivation(std::ostream& log_sink,
-                                        const Path& drvPath,
-                                        const BasicDerivation& drv,
-                                        BuildMode buildMode) {
-  Worker worker(*this, log_sink);
-  auto goal = worker.makeBasicDerivationGoal(drvPath, drv, buildMode);
-
-  BuildResult result;
-
-  try {
-    worker.run(Goals{goal});
-    result = goal->getResult();
-  } catch (Error& e) {
-    result.status = BuildResult::MiscFailure;
-    result.errorMsg = e.msg();
-  }
-
-  return result;
-}
-
-void LocalStore::ensurePath(const Path& path) {
-  /* If the path is already valid, we're done. */
-  if (isValidPath(path)) {
-    return;
-  }
-
-  primeCache(*this, {path});
-
-  auto discard_logs = DiscardLogsSink();
-  Worker worker(*this, discard_logs);
-  GoalPtr goal = worker.makeSubstitutionGoal(path);
-  Goals goals = {goal};
-
-  worker.run(goals);
-
-  if (goal->getExitCode() != Goal::ecSuccess) {
-    throw Error(worker.exitStatus(),
-                "path '%s' does not exist and cannot be created", path);
-  }
-}
-
-void LocalStore::repairPath(const Path& path) {
-  auto discard_logs = DiscardLogsSink();
-  Worker worker(*this, discard_logs);
-  GoalPtr goal = worker.makeSubstitutionGoal(path, Repair);
-  Goals goals = {goal};
-
-  worker.run(goals);
-
-  if (goal->getExitCode() != Goal::ecSuccess) {
-    /* Since substituting the path didn't work, if we have a valid
-       deriver, then rebuild the deriver. */
-    auto deriver = queryPathInfo(path)->deriver;
-    if (!deriver.empty() && isValidPath(deriver)) {
-      goals.clear();
-      goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair));
-      worker.run(goals);
-    } else {
-      throw Error(worker.exitStatus(), "cannot repair path '%s'", path);
-    }
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/builtins.hh b/third_party/nix/src/libstore/builtins.hh
deleted file mode 100644
index bc53e78ebc..0000000000
--- a/third_party/nix/src/libstore/builtins.hh
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "libstore/derivations.hh"
-
-namespace nix {
-
-// TODO: make pluggable.
-void builtinFetchurl(const BasicDerivation& drv, const std::string& netrcData);
-void builtinBuildenv(const BasicDerivation& drv);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/builtins/buildenv.cc b/third_party/nix/src/libstore/builtins/buildenv.cc
deleted file mode 100644
index 433082a0f9..0000000000
--- a/third_party/nix/src/libstore/builtins/buildenv.cc
+++ /dev/null
@@ -1,240 +0,0 @@
-#include <algorithm>
-
-#include <absl/strings/match.h>
-#include <absl/strings/str_split.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "libstore/builtins.hh"
-
-namespace nix {
-
-typedef std::map<Path, int> Priorities;
-
-// FIXME: change into local variables.
-
-static Priorities priorities;
-
-static unsigned long symlinks;
-
-/* For each activated package, create symlinks */
-static void createLinks(const Path& srcDir, const Path& dstDir, int priority) {
-  DirEntries srcFiles;
-
-  try {
-    srcFiles = readDirectory(srcDir);
-  } catch (SysError& e) {
-    if (e.errNo == ENOTDIR) {
-      LOG(ERROR) << "warning: not including '" << srcDir
-                 << "' in the user environment because it's not a directory";
-      return;
-    }
-    throw;
-  }
-
-  for (const auto& ent : srcFiles) {
-    if (ent.name[0] == '.') { /* not matched by glob */
-      continue;
-    }
-    auto srcFile = srcDir + "/" + ent.name;
-    auto dstFile = dstDir + "/" + ent.name;
-
-    struct stat srcSt;
-    try {
-      if (stat(srcFile.c_str(), &srcSt) == -1) {
-        throw SysError("getting status of '%1%'", srcFile);
-      }
-    } catch (SysError& e) {
-      if (e.errNo == ENOENT || e.errNo == ENOTDIR) {
-        LOG(ERROR) << "warning: skipping dangling symlink '" << dstFile << "'";
-        continue;
-      }
-      throw;
-    }
-
-    /* The files below are special-cased to that they don't show up
-     * in user profiles, either because they are useless, or
-     * because they would cauase pointless collisions (e.g., each
-     * Python package brings its own
-     * `$out/lib/pythonX.Y/site-packages/easy-install.pth'.)
-     */
-    if (absl::EndsWith(srcFile, "/propagated-build-inputs") ||
-        absl::EndsWith(srcFile, "/nix-support") ||
-        absl::EndsWith(srcFile, "/perllocal.pod") ||
-        absl::EndsWith(srcFile, "/info/dir") ||
-        absl::EndsWith(srcFile, "/log")) {
-      continue;
-
-    } else if (S_ISDIR(srcSt.st_mode)) {
-      struct stat dstSt;
-      auto res = lstat(dstFile.c_str(), &dstSt);
-      if (res == 0) {
-        if (S_ISDIR(dstSt.st_mode)) {
-          createLinks(srcFile, dstFile, priority);
-          continue;
-        } else if (S_ISLNK(dstSt.st_mode)) {
-          auto target = canonPath(dstFile, true);
-          if (!S_ISDIR(lstat(target).st_mode)) {
-            throw Error("collision between '%1%' and non-directory '%2%'",
-                        srcFile, target);
-          }
-          if (unlink(dstFile.c_str()) == -1) {
-            throw SysError(format("unlinking '%1%'") % dstFile);
-          }
-          if (mkdir(dstFile.c_str(), 0755) == -1) {
-            throw SysError(format("creating directory '%1%'"));
-          }
-          createLinks(target, dstFile, priorities[dstFile]);
-          createLinks(srcFile, dstFile, priority);
-          continue;
-        }
-      } else if (errno != ENOENT) {
-        throw SysError(format("getting status of '%1%'") % dstFile);
-      }
-    }
-
-    else {
-      struct stat dstSt;
-      auto res = lstat(dstFile.c_str(), &dstSt);
-      if (res == 0) {
-        if (S_ISLNK(dstSt.st_mode)) {
-          auto prevPriority = priorities[dstFile];
-          if (prevPriority == priority) {
-            throw Error(
-                "packages '%1%' and '%2%' have the same priority %3%; "
-                "use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' "
-                "to change the priority of one of the conflicting packages"
-                " (0 being the highest priority)",
-                srcFile, readLink(dstFile), priority);
-          }
-          if (prevPriority < priority) {
-            continue;
-          }
-          if (unlink(dstFile.c_str()) == -1) {
-            throw SysError(format("unlinking '%1%'") % dstFile);
-          }
-        } else if (S_ISDIR(dstSt.st_mode)) {
-          throw Error(
-              "collision between non-directory '%1%' and directory '%2%'",
-              srcFile, dstFile);
-        }
-      } else if (errno != ENOENT) {
-        throw SysError(format("getting status of '%1%'") % dstFile);
-      }
-    }
-
-    createSymlink(srcFile, dstFile);
-    priorities[dstFile] = priority;
-    symlinks++;
-  }
-}
-
-using FileProp = std::set<Path>;
-
-static FileProp done;
-static FileProp postponed = FileProp{};
-
-static Path out;
-
-static void addPkg(const Path& pkgDir, int priority) {
-  if (done.count(pkgDir)) {
-    return;
-  }
-  done.insert(pkgDir);
-  createLinks(pkgDir, out, priority);
-
-  try {
-    for (auto p : absl::StrSplit(
-             readFile(pkgDir + "/nix-support/propagated-user-env-packages"),
-             absl::ByAnyChar(" \n"), absl::SkipEmpty())) {
-      auto pkg = std::string(p);
-      if (!done.count(pkg)) {
-        postponed.insert(pkg);
-      }
-    }
-  } catch (SysError& e) {
-    if (e.errNo != ENOENT && e.errNo != ENOTDIR) {
-      throw;
-    }
-  }
-}
-
-struct Package {
-  Path path;
-  bool active;
-  int priority;
-  Package(Path path, bool active, int priority)
-      : path{path}, active{active}, priority{priority} {}
-};
-
-using Packages = std::vector<Package>;
-
-void builtinBuildenv(const BasicDerivation& drv) {
-  auto getAttr = [&](const std::string& name) {
-    auto i = drv.env.find(name);
-    if (i == drv.env.end()) {
-      throw Error("attribute '%s' missing", name);
-    }
-    return i->second;
-  };
-
-  out = getAttr("out");
-  createDirs(out);
-
-  /* Convert the stuff we get from the environment back into a
-   * coherent data type. */
-  Packages pkgs;
-  Strings derivations = absl::StrSplit(
-      getAttr("derivations"), absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-  while (!derivations.empty()) {
-    /* !!! We're trusting the caller to structure derivations env var correctly
-     */
-    auto active = derivations.front();
-    derivations.pop_front();
-    auto priority = stoi(derivations.front());
-    derivations.pop_front();
-    auto outputs = stoi(derivations.front());
-    derivations.pop_front();
-    for (auto n = 0; n < outputs; n++) {
-      auto path = derivations.front();
-      derivations.pop_front();
-      pkgs.emplace_back(path, active != "false", priority);
-    }
-  }
-
-  /* Symlink to the packages that have been installed explicitly by the
-   * user. Process in priority order to reduce unnecessary
-   * symlink/unlink steps.
-   */
-  std::sort(pkgs.begin(), pkgs.end(), [](const Package& a, const Package& b) {
-    return a.priority < b.priority ||
-           (a.priority == b.priority && a.path < b.path);
-  });
-  for (const auto& pkg : pkgs) {
-    if (pkg.active) {
-      addPkg(pkg.path, pkg.priority);
-    }
-  }
-
-  /* Symlink to the packages that have been "propagated" by packages
-   * installed by the user (i.e., package X declares that it wants Y
-   * installed as well). We do these later because they have a lower
-   * priority in case of collisions.
-   */
-  auto priorityCounter = 1000;
-  while (!postponed.empty()) {
-    auto pkgDirs = postponed;
-    postponed = FileProp{};
-    for (const auto& pkgDir : pkgDirs) {
-      addPkg(pkgDir, priorityCounter++);
-    }
-  }
-
-  LOG(INFO) << "created " << symlinks << " symlinks in user environment";
-
-  createSymlink(getAttr("manifest"), out + "/manifest.nix");
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/builtins/fetchurl.cc b/third_party/nix/src/libstore/builtins/fetchurl.cc
deleted file mode 100644
index 961d081423..0000000000
--- a/third_party/nix/src/libstore/builtins/fetchurl.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-#include <absl/strings/match.h>
-#include <glog/logging.h>
-
-#include "libstore/builtins.hh"
-#include "libstore/download.hh"
-#include "libstore/store-api.hh"
-#include "libutil/archive.hh"
-#include "libutil/compression.hh"
-
-namespace nix {
-
-void builtinFetchurl(const BasicDerivation& drv, const std::string& netrcData) {
-  /* Make the host's netrc data available. Too bad curl requires
-     this to be stored in a file. It would be nice if we could just
-     pass a pointer to the data. */
-  if (netrcData != "") {
-    settings.netrcFile = "netrc";
-    writeFile(settings.netrcFile, netrcData, 0600);
-  }
-
-  auto getAttr = [&](const std::string& name) {
-    auto i = drv.env.find(name);
-    if (i == drv.env.end()) {
-      throw Error(format("attribute '%s' missing") % name);
-    }
-    return i->second;
-  };
-
-  Path storePath = getAttr("out");
-  auto mainUrl = getAttr("url");
-  bool unpack = get(drv.env, "unpack", "") == "1";
-
-  /* Note: have to use a fresh downloader here because we're in
-     a forked process. */
-  auto downloader = makeDownloader();
-
-  auto fetch = [&](const std::string& url) {
-    auto source = sinkToSource([&](Sink& sink) {
-      /* No need to do TLS verification, because we check the hash of
-         the result anyway. */
-      DownloadRequest request(url);
-      request.verifyTLS = false;
-      request.decompress = false;
-
-      auto decompressor = makeDecompressionSink(
-          unpack && absl::EndsWith(mainUrl, ".xz") ? "xz" : "none", sink);
-      downloader->download(std::move(request), *decompressor);
-      decompressor->finish();
-    });
-
-    if (unpack) {
-      restorePath(storePath, *source);
-    } else {
-      writeFile(storePath, *source);
-    }
-
-    auto executable = drv.env.find("executable");
-    if (executable != drv.env.end() && executable->second == "1") {
-      if (chmod(storePath.c_str(), 0755) == -1) {
-        throw SysError(format("making '%1%' executable") % storePath);
-      }
-    }
-  };
-
-  /* Try the hashed mirrors first. */
-  if (getAttr("outputHashMode") == "flat") {
-    auto hash_ = Hash::deserialize(getAttr("outputHash"),
-                                   parseHashType(getAttr("outputHashAlgo")));
-    if (hash_.ok()) {
-      auto h = *hash_;
-      for (auto hashedMirror : settings.hashedMirrors.get()) {
-        try {
-          if (!absl::EndsWith(hashedMirror, "/")) {
-            hashedMirror += '/';
-          }
-          fetch(hashedMirror + printHashType(h.type) + "/" +
-                h.to_string(Base16, false));
-          return;
-        } catch (Error& e) {
-          LOG(ERROR) << e.what();
-        }
-      }
-    } else {
-      LOG(ERROR) << "checking mirrors for '" << mainUrl
-                 << "': " << hash_.status().ToString();
-    }
-  }
-
-  /* Otherwise try the specified URL. */
-  fetch(mainUrl);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/crypto.cc b/third_party/nix/src/libstore/crypto.cc
deleted file mode 100644
index 0a2795cb0a..0000000000
--- a/third_party/nix/src/libstore/crypto.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-#include "libstore/crypto.hh"
-
-#include <absl/strings/escaping.h>
-
-#include "libstore/globals.hh"
-#include "libutil/util.hh"
-
-#if HAVE_SODIUM
-#include <sodium.h>
-#endif
-
-namespace nix {
-
-// TODO(riking): convert to string_view to reduce allocations
-static std::pair<std::string, std::string> split(const std::string& s) {
-  size_t colon = s.find(':');
-  if (colon == std::string::npos || colon == 0) {
-    return {"", ""};
-  }
-  return {std::string(s, 0, colon), std::string(s, colon + 1)};
-}
-
-Key::Key(const std::string& s) {
-  auto ss = split(s);
-
-  name = ss.first;
-  std::string keyb64 = ss.second;
-
-  if (name.empty() || keyb64.empty()) {
-    throw Error("secret key is corrupt");
-  }
-
-  if (!absl::Base64Unescape(keyb64, &key)) {
-    // TODO(grfn): replace this with StatusOr
-    throw Error("Invalid Base64");
-  }
-}
-
-SecretKey::SecretKey(const std::string& s) : Key(s) {
-#if HAVE_SODIUM
-  if (key.size() != crypto_sign_SECRETKEYBYTES) {
-    throw Error("secret key is not valid");
-  }
-#endif
-}
-
-#if !HAVE_SODIUM
-[[noreturn]] static void noSodium() {
-  throw Error(
-      "Nix was not compiled with libsodium, required for signed binary cache "
-      "support");
-}
-#endif
-
-std::string SecretKey::signDetached(const std::string& data) const {
-#if HAVE_SODIUM
-  unsigned char sig[crypto_sign_BYTES];
-  unsigned long long sigLen;
-  crypto_sign_detached(sig, &sigLen, (unsigned char*)data.data(), data.size(),
-                       (unsigned char*)key.data());
-  return name + ":" +
-         absl::Base64Escape(std::string(reinterpret_cast<char*>(sig), sigLen));
-#else
-  noSodium();
-#endif
-}
-
-PublicKey SecretKey::toPublicKey() const {
-#if HAVE_SODIUM
-  unsigned char pk[crypto_sign_PUBLICKEYBYTES];
-  crypto_sign_ed25519_sk_to_pk(pk, (unsigned char*)key.data());
-  return PublicKey(name, std::string(reinterpret_cast<char*>(pk),
-                                     crypto_sign_PUBLICKEYBYTES));
-#else
-  noSodium();
-#endif
-}
-
-PublicKey::PublicKey(const std::string& s) : Key(s) {
-#if HAVE_SODIUM
-  if (key.size() != crypto_sign_PUBLICKEYBYTES) {
-    throw Error("public key is not valid");
-  }
-#endif
-}
-
-bool verifyDetached(const std::string& data, const std::string& sig,
-                    const PublicKeys& publicKeys) {
-#if HAVE_SODIUM
-  auto ss = split(sig);
-
-  auto key = publicKeys.find(ss.first);
-  if (key == publicKeys.end()) {
-    return false;
-  }
-
-  std::string sig2;
-  if (!absl::Base64Unescape(ss.second, &sig2)) {
-    // TODO(grfn): replace this with StatusOr
-    throw Error("Invalid Base64");
-  }
-  if (sig2.size() != crypto_sign_BYTES) {
-    throw Error("signature is not valid");
-  }
-
-  return crypto_sign_verify_detached(
-             reinterpret_cast<unsigned char*>(sig2.data()),
-             (unsigned char*)data.data(), data.size(),
-             (unsigned char*)key->second.key.data()) == 0;
-#else
-  noSodium();
-#endif
-}
-
-PublicKeys getDefaultPublicKeys() {
-  PublicKeys publicKeys;
-
-  // FIXME: filter duplicates
-
-  for (const auto& s : settings.trustedPublicKeys.get()) {
-    PublicKey key(s);
-    publicKeys.emplace(key.name, key);
-  }
-
-  for (const auto& secretKeyFile : settings.secretKeyFiles.get()) {
-    try {
-      SecretKey secretKey(readFile(secretKeyFile));
-      publicKeys.emplace(secretKey.name, secretKey.toPublicKey());
-    } catch (SysError& e) {
-      /* Ignore unreadable key files. That's normal in a
-         multi-user installation. */
-    }
-  }
-
-  return publicKeys;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/crypto.hh b/third_party/nix/src/libstore/crypto.hh
deleted file mode 100644
index e282f4f8ef..0000000000
--- a/third_party/nix/src/libstore/crypto.hh
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-#include <map>
-
-#include "libutil/types.hh"
-
-namespace nix {
-
-struct Key {
-  std::string name;
-  std::string key;
-
-  /* Construct Key from a string in the format
-     β€˜<name>:<key-in-base64>’. */
-  Key(const std::string& s);
-
- protected:
-  Key(const std::string& name, const std::string& key) : name(name), key(key) {}
-};
-
-struct PublicKey;
-
-struct SecretKey : Key {
-  SecretKey(const std::string& s);
-
-  /* Return a detached signature of the given string. */
-  std::string signDetached(const std::string& data) const;
-
-  PublicKey toPublicKey() const;
-};
-
-struct PublicKey : Key {
-  PublicKey(const std::string& s);
-
- private:
-  PublicKey(const std::string& name, const std::string& key) : Key(name, key) {}
-  friend struct SecretKey;
-};
-
-typedef std::map<std::string, PublicKey> PublicKeys;
-
-/* Return true iff β€˜sig’ is a correct signature over β€˜data’ using one
-   of the given public keys. */
-bool verifyDetached(const std::string& data, const std::string& sig,
-                    const PublicKeys& publicKeys);
-
-PublicKeys getDefaultPublicKeys();
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/derivations.cc b/third_party/nix/src/libstore/derivations.cc
deleted file mode 100644
index 9c344502f3..0000000000
--- a/third_party/nix/src/libstore/derivations.cc
+++ /dev/null
@@ -1,520 +0,0 @@
-#include "libstore/derivations.hh"
-
-#include <absl/strings/match.h>
-#include <absl/strings/str_split.h>
-#include <absl/strings/string_view.h>
-#include <glog/logging.h>
-
-#include "libproto/worker.pb.h"
-#include "libstore/fs-accessor.hh"
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libstore/worker-protocol.hh"
-#include "libutil/istringstream_nocopy.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-// TODO(#statusor): looks like easy absl::Status conversion
-void DerivationOutput::parseHashInfo(bool& recursive, Hash& hash) const {
-  recursive = false;
-  std::string algo = hashAlgo;
-
-  if (std::string(algo, 0, 2) == "r:") {
-    recursive = true;
-    algo = std::string(algo, 2);
-  }
-
-  HashType hashType = parseHashType(algo);
-  if (hashType == htUnknown) {
-    throw Error(format("unknown hash algorithm '%1%'") % algo);
-  }
-
-  auto hash_ = Hash::deserialize(this->hash, hashType);
-  hash = Hash::unwrap_throw(hash_);
-}
-
-nix::proto::Derivation_DerivationOutput DerivationOutput::to_proto() const {
-  nix::proto::Derivation_DerivationOutput result;
-  result.mutable_path()->set_path(path);
-  result.set_hash_algo(hashAlgo);
-  result.set_hash(hash);
-  return result;
-}
-
-BasicDerivation BasicDerivation::from_proto(
-    const nix::proto::Derivation* proto_derivation) {
-  BasicDerivation result;
-  result.platform = proto_derivation->platform();
-  result.builder = proto_derivation->builder().path();
-
-  for (auto [k, v] : proto_derivation->outputs()) {
-    result.outputs.emplace(k, v);
-  }
-
-  result.inputSrcs.insert(proto_derivation->input_sources().paths().begin(),
-                          proto_derivation->input_sources().paths().end());
-
-  result.args.insert(result.args.end(), proto_derivation->args().begin(),
-                     proto_derivation->args().end());
-
-  for (auto [k, v] : proto_derivation->env()) {
-    result.env.emplace(k, v);
-  }
-
-  return result;
-}
-
-nix::proto::Derivation BasicDerivation::to_proto() const {
-  nix::proto::Derivation result;
-  for (const auto& [key, output] : outputs) {
-    result.mutable_outputs()->insert({key, output.to_proto()});
-  }
-  for (const auto& input_src : inputSrcs) {
-    *result.mutable_input_sources()->add_paths() = input_src;
-  }
-  result.set_platform(platform);
-  result.mutable_builder()->set_path(builder);
-  for (const auto& arg : args) {
-    result.add_args(arg);
-  }
-
-  for (const auto& [key, value] : env) {
-    result.mutable_env()->insert({key, value});
-  }
-
-  return result;
-}
-
-Path BasicDerivation::findOutput(const std::string& id) const {
-  auto i = outputs.find(id);
-  if (i == outputs.end()) {
-    throw Error(format("derivation has no output '%1%'") % id);
-  }
-  return i->second.path;
-}
-
-bool BasicDerivation::isBuiltin() const {
-  return std::string(builder, 0, 8) == "builtin:";
-}
-
-Path writeDerivation(const ref<Store>& store, const Derivation& drv,
-                     const std::string& name, RepairFlag repair) {
-  PathSet references;
-  references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
-  for (auto& i : drv.inputDrvs) {
-    references.insert(i.first);
-  }
-  /* Note that the outputs of a derivation are *not* references
-     (that can be missing (of course) and should not necessarily be
-     held during a garbage collection). */
-  std::string suffix = name + drvExtension;
-  std::string contents = drv.unparse();
-  return settings.readOnlyMode
-             ? store->computeStorePathForText(suffix, contents, references)
-             : store->addTextToStore(suffix, contents, references, repair);
-}
-
-/* Read string `s' from stream `str'. */
-static void expect(std::istream& str, const std::string& s) {
-  char s2[s.size()];
-  str.read(s2, s.size());
-  if (std::string(s2, s.size()) != s) {
-    throw FormatError(format("expected string '%1%'") % s);
-  }
-}
-
-/* Read a C-style string from stream `str'. */
-static std::string parseString(std::istream& str) {
-  std::string res;
-  expect(str, "\"");
-  int c;
-  while ((c = str.get()) != '"' && c != EOF) {
-    if (c == '\\') {
-      c = str.get();
-      if (c == 'n') {
-        res += '\n';
-      } else if (c == 'r') {
-        res += '\r';
-      } else if (c == 't') {
-        res += '\t';
-      } else if (c == EOF) {
-        throw FormatError("unexpected EOF while parsing C-style escape");
-      } else {
-        res += static_cast<char>(c);
-      }
-    } else {
-      res += static_cast<char>(c);
-    }
-  }
-  return res;
-}
-
-static Path parsePath(std::istream& str) {
-  std::string s = parseString(str);
-  if (s.empty() || s[0] != '/') {
-    throw FormatError(format("bad path '%1%' in derivation") % s);
-  }
-  return s;
-}
-
-static bool endOfList(std::istream& str) {
-  if (str.peek() == ',') {
-    str.get();
-    return false;
-  }
-  if (str.peek() == ']') {
-    str.get();
-    return true;
-  }
-  return false;
-}
-
-static StringSet parseStrings(std::istream& str, bool arePaths) {
-  StringSet res;
-  while (!endOfList(str)) {
-    res.insert(arePaths ? parsePath(str) : parseString(str));
-  }
-  return res;
-}
-
-Derivation parseDerivation(const std::string& s) {
-  Derivation drv;
-  istringstream_nocopy str(s);
-  expect(str, "Derive([");
-
-  /* Parse the list of outputs. */
-  while (!endOfList(str)) {
-    DerivationOutput out;
-    expect(str, "(");
-    std::string id = parseString(str);
-    expect(str, ",");
-    out.path = parsePath(str);
-    expect(str, ",");
-    out.hashAlgo = parseString(str);
-    expect(str, ",");
-    out.hash = parseString(str);
-    expect(str, ")");
-    drv.outputs[id] = out;
-  }
-
-  /* Parse the list of input derivations. */
-  expect(str, ",[");
-  while (!endOfList(str)) {
-    expect(str, "(");
-    Path drvPath = parsePath(str);
-    expect(str, ",[");
-    drv.inputDrvs[drvPath] = parseStrings(str, false);
-    expect(str, ")");
-  }
-
-  expect(str, ",[");
-  drv.inputSrcs = parseStrings(str, true);
-  expect(str, ",");
-  drv.platform = parseString(str);
-  expect(str, ",");
-  drv.builder = parseString(str);
-
-  /* Parse the builder arguments. */
-  expect(str, ",[");
-  while (!endOfList(str)) {
-    drv.args.push_back(parseString(str));
-  }
-
-  /* Parse the environment variables. */
-  expect(str, ",[");
-  while (!endOfList(str)) {
-    expect(str, "(");
-    std::string name = parseString(str);
-    expect(str, ",");
-    std::string value = parseString(str);
-    expect(str, ")");
-    drv.env[name] = value;
-  }
-
-  expect(str, ")");
-  return drv;
-}
-
-Derivation readDerivation(const Path& drvPath) {
-  try {
-    return parseDerivation(readFile(drvPath));
-  } catch (FormatError& e) {
-    throw Error(format("error parsing derivation '%1%': %2%") % drvPath %
-                e.msg());
-  }
-}
-
-Derivation Store::derivationFromPath(const Path& drvPath) {
-  assertStorePath(drvPath);
-  ensurePath(drvPath);
-  auto accessor = getFSAccessor();
-  try {
-    return parseDerivation(accessor->readFile(drvPath));
-  } catch (FormatError& e) {
-    throw Error(format("error parsing derivation '%1%': %2%") % drvPath %
-                e.msg());
-  }
-}
-
-const char* findChunk(const char* begin) {
-  while (*begin != 0 && *begin != '\"' && *begin != '\\' && *begin != '\n' &&
-         *begin != '\r' && *begin != '\t') {
-    begin++;
-  }
-
-  return begin;
-}
-
-static void printString(std::string& res, const std::string& s) {
-  res += '"';
-
-  const char* it = s.c_str();
-  while (*it != 0) {
-    const char* end = findChunk(it);
-    std::copy(it, end, std::back_inserter(res));
-
-    it = end;
-
-    switch (*it) {
-      case '"':
-      case '\\':
-        res += "\\";
-        res += *it;
-        break;
-      case '\n':
-        res += "\\n";
-        break;
-      case '\r':
-        res += "\\r";
-        break;
-      case '\t':
-        res += "\\t";
-        break;
-      default:
-        continue;
-    }
-
-    it++;
-  }
-
-  res += '"';
-}
-
-template <class ForwardIterator>
-static void printStrings(std::string& res, ForwardIterator i,
-                         ForwardIterator j) {
-  res += '[';
-  bool first = true;
-  for (; i != j; ++i) {
-    if (first) {
-      first = false;
-    } else {
-      res += ',';
-    }
-    printString(res, *i);
-  }
-  res += ']';
-}
-
-std::string Derivation::unparse() const {
-  std::string s;
-  s.reserve(65536);
-  s += "Derive([";
-
-  bool first = true;
-  for (auto& i : outputs) {
-    if (first) {
-      first = false;
-    } else {
-      s += ',';
-    }
-    s += '(';
-    printString(s, i.first);
-    s += ',';
-    printString(s, i.second.path);
-    s += ',';
-    printString(s, i.second.hashAlgo);
-    s += ',';
-    printString(s, i.second.hash);
-    s += ')';
-  }
-
-  s += "],[";
-  first = true;
-  for (auto& i : inputDrvs) {
-    if (first) {
-      first = false;
-    } else {
-      s += ',';
-    }
-    s += '(';
-    printString(s, i.first);
-    s += ',';
-    printStrings(s, i.second.begin(), i.second.end());
-    s += ')';
-  }
-
-  s += "],";
-  printStrings(s, inputSrcs.begin(), inputSrcs.end());
-
-  s += ',';
-  printString(s, platform);
-  s += ',';
-  printString(s, builder);
-  s += ',';
-  printStrings(s, args.begin(), args.end());
-
-  s += ",[";
-  first = true;
-  for (auto& i : env) {
-    if (first) {
-      first = false;
-    } else {
-      s += ',';
-    }
-    s += '(';
-    printString(s, i.first);
-    s += ',';
-    printString(s, i.second);
-    s += ')';
-  }
-
-  s += "])";
-
-  return s;
-}
-
-bool isDerivation(const std::string& fileName) {
-  return absl::EndsWith(fileName, drvExtension);
-}
-
-bool BasicDerivation::isFixedOutput() const {
-  return outputs.size() == 1 && outputs.begin()->first == "out" &&
-         !outputs.begin()->second.hash.empty();
-}
-
-DrvHashes drvHashes;
-
-/* Returns the hash of a derivation modulo fixed-output
-   subderivations.  A fixed-output derivation is a derivation with one
-   output (`out') for which an expected hash and hash algorithm are
-   specified (using the `outputHash' and `outputHashAlgo'
-   attributes).  We don't want changes to such derivations to
-   propagate upwards through the dependency graph, changing output
-   paths everywhere.
-
-   For instance, if we change the url in a call to the `fetchurl'
-   function, we do not want to rebuild everything depending on it
-   (after all, (the hash of) the file being downloaded is unchanged).
-   So the *output paths* should not change.  On the other hand, the
-   *derivation paths* should change to reflect the new dependency
-   graph.
-
-   That's what this function does: it returns a hash which is just the
-   hash of the derivation ATerm, except that any input derivation
-   paths have been replaced by the result of a recursive call to this
-   function, and that for fixed-output derivations we return a hash of
-   its output path. */
-Hash hashDerivationModulo(Store& store, Derivation drv) {
-  /* Return a fixed hash for fixed-output derivations. */
-  if (drv.isFixedOutput()) {
-    auto i = drv.outputs.begin();
-    return hashString(htSHA256, "fixed:out:" + i->second.hashAlgo + ":" +
-                                    i->second.hash + ":" + i->second.path);
-  }
-
-  /* For other derivations, replace the inputs paths with recursive
-     calls to this function.*/
-  DerivationInputs inputs2;
-  for (auto& i : drv.inputDrvs) {
-    Hash h = drvHashes[i.first];
-    if (!h) {
-      assert(store.isValidPath(i.first));
-      Derivation drv2 = readDerivation(store.toRealPath(i.first));
-      h = hashDerivationModulo(store, drv2);
-      drvHashes[i.first] = h;
-    }
-    inputs2[h.to_string(Base16, false)] = i.second;
-  }
-  drv.inputDrvs = inputs2;
-
-  return hashString(htSHA256, drv.unparse());
-}
-
-DrvPathWithOutputs parseDrvPathWithOutputs(absl::string_view path) {
-  auto pos = path.find('!');
-  if (pos == absl::string_view::npos) {
-    return DrvPathWithOutputs(path, std::set<std::string>());
-  }
-
-  return DrvPathWithOutputs(
-      path.substr(0, pos),
-      absl::StrSplit(path.substr(pos + 1), absl::ByChar(','),
-                     absl::SkipEmpty()));
-}
-
-Path makeDrvPathWithOutputs(const Path& drvPath,
-                            const std::set<std::string>& outputs) {
-  return outputs.empty() ? drvPath
-                         : drvPath + "!" + concatStringsSep(",", outputs);
-}
-
-bool wantOutput(const std::string& output,
-                const std::set<std::string>& wanted) {
-  return wanted.empty() || wanted.find(output) != wanted.end();
-}
-
-PathSet BasicDerivation::outputPaths() const {
-  PathSet paths;
-  for (auto& i : outputs) {
-    paths.insert(i.second.path);
-  }
-  return paths;
-}
-
-Source& readDerivation(Source& in, Store& store, BasicDerivation& drv) {
-  drv.outputs.clear();
-  auto nr = readNum<size_t>(in);
-  for (size_t n = 0; n < nr; n++) {
-    auto name = readString(in);
-    DerivationOutput o;
-    in >> o.path >> o.hashAlgo >> o.hash;
-    store.assertStorePath(o.path);
-    drv.outputs[name] = o;
-  }
-
-  drv.inputSrcs = readStorePaths<PathSet>(store, in);
-  in >> drv.platform >> drv.builder;
-  drv.args = readStrings<Strings>(in);
-
-  nr = readNum<size_t>(in);
-  for (size_t n = 0; n < nr; n++) {
-    auto key = readString(in);
-    auto value = readString(in);
-    drv.env[key] = value;
-  }
-
-  return in;
-}
-
-Sink& operator<<(Sink& out, const BasicDerivation& drv) {
-  out << drv.outputs.size();
-  for (auto& i : drv.outputs) {
-    out << i.first << i.second.path << i.second.hashAlgo << i.second.hash;
-  }
-  out << drv.inputSrcs << drv.platform << drv.builder << drv.args;
-  out << drv.env.size();
-  for (auto& i : drv.env) {
-    out << i.first << i.second;
-  }
-  return out;
-}
-
-std::string hashPlaceholder(const std::string& outputName) {
-  // FIXME: memoize?
-  return "/" + hashString(htSHA256, "nix-output:" + outputName)
-                   .to_string(Base32, false);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/derivations.hh b/third_party/nix/src/libstore/derivations.hh
deleted file mode 100644
index 4966b858d3..0000000000
--- a/third_party/nix/src/libstore/derivations.hh
+++ /dev/null
@@ -1,130 +0,0 @@
-#pragma once
-
-#include <map>
-
-#include <absl/container/btree_map.h>
-
-#include "libproto/worker.pb.h"
-#include "libstore/store-api.hh"
-#include "libutil/hash.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-/* Extension of derivations in the Nix store. */
-const std::string drvExtension = ".drv";
-
-/* Abstract syntax of derivations. */
-
-struct DerivationOutput {
-  Path path;
-  // TODO(grfn): make these two fields a Hash
-  std::string hashAlgo; /* hash used for expected hash computation */
-  std::string hash;     /* expected hash, may be null */
-  DerivationOutput() {}
-  DerivationOutput(Path path, std::string hashAlgo, std::string hash) {
-    this->path = path;
-    this->hashAlgo = hashAlgo;
-    this->hash = hash;
-  }
-
-  explicit DerivationOutput(
-      const nix::proto::Derivation_DerivationOutput& proto_derivation_output)
-      : path(proto_derivation_output.path().path()),
-        hashAlgo(proto_derivation_output.hash_algo()),
-        hash(proto_derivation_output.hash()) {}
-
-  void parseHashInfo(bool& recursive, Hash& hash) const;
-
-  [[nodiscard]] nix::proto::Derivation_DerivationOutput to_proto() const;
-};
-
-// TODO(tazjin): Determine whether this actually needs to be ordered.
-using DerivationOutputs = absl::btree_map<std::string, DerivationOutput>;
-
-/* For inputs that are sub-derivations, we specify exactly which
-   output IDs we are interested in. */
-using DerivationInputs = absl::btree_map<Path, StringSet>;
-
-using StringPairs = absl::btree_map<std::string, std::string>;
-
-struct BasicDerivation {
-  DerivationOutputs outputs; /* keyed on symbolic IDs */
-  PathSet inputSrcs;         /* inputs that are sources */
-  std::string platform;
-  Path builder;
-  Strings args;
-  StringPairs env;
-
-  BasicDerivation() = default;
-
-  // Convert the given proto derivation to a BasicDerivation
-  static BasicDerivation from_proto(
-      const nix::proto::Derivation* proto_derivation);
-
-  [[nodiscard]] nix::proto::Derivation to_proto() const;
-
-  virtual ~BasicDerivation(){};
-
-  /* Return the path corresponding to the output identifier `id' in
-     the given derivation. */
-  Path findOutput(const std::string& id) const;
-
-  bool isBuiltin() const;
-
-  /* Return true iff this is a fixed-output derivation. */
-  bool isFixedOutput() const;
-
-  /* Return the output paths of a derivation. */
-  PathSet outputPaths() const;
-};
-
-struct Derivation : BasicDerivation {
-  DerivationInputs inputDrvs; /* inputs that are sub-derivations */
-
-  /* Print a derivation. */
-  std::string unparse() const;
-};
-
-class Store;
-
-/* Write a derivation to the Nix store, and return its path. */
-Path writeDerivation(const ref<Store>& store, const Derivation& drv,
-                     const std::string& name, RepairFlag repair = NoRepair);
-
-/* Read a derivation from a file. */
-Derivation readDerivation(const Path& drvPath);
-
-Derivation parseDerivation(const std::string& s);
-
-/* Check whether a file name ends with the extension for
-   derivations. */
-bool isDerivation(const std::string& fileName);
-
-Hash hashDerivationModulo(Store& store, Derivation drv);
-
-/* Memoisation of hashDerivationModulo(). */
-typedef std::map<Path, Hash> DrvHashes;
-
-extern DrvHashes drvHashes;  // FIXME: global, not thread-safe
-
-/* Split a string specifying a derivation and a set of outputs
-   (/nix/store/hash-foo!out1,out2,...) into the derivation path and
-   the outputs. */
-using DrvPathWithOutputs = std::pair<std::string, std::set<std::string> >;
-DrvPathWithOutputs parseDrvPathWithOutputs(absl::string_view path);
-
-Path makeDrvPathWithOutputs(const Path& drvPath,
-                            const std::set<std::string>& outputs);
-
-bool wantOutput(const std::string& output, const std::set<std::string>& wanted);
-
-struct Source;
-struct Sink;
-
-Source& readDerivation(Source& in, Store& store, BasicDerivation& drv);
-Sink& operator<<(Sink& out, const BasicDerivation& drv);
-
-std::string hashPlaceholder(const std::string& outputName);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/download.cc b/third_party/nix/src/libstore/download.cc
deleted file mode 100644
index fd472713e6..0000000000
--- a/third_party/nix/src/libstore/download.cc
+++ /dev/null
@@ -1,1024 +0,0 @@
-#include "libstore/download.hh"
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/match.h>
-#include <absl/strings/numbers.h>
-#include <absl/strings/str_split.h>
-
-#include "libstore/globals.hh"
-#include "libstore/pathlocks.hh"
-#include "libstore/s3.hh"
-#include "libstore/store-api.hh"
-#include "libutil/archive.hh"
-#include "libutil/compression.hh"
-#include "libutil/finally.hh"
-#include "libutil/hash.hh"
-#include "libutil/util.hh"
-
-#ifdef ENABLE_S3
-#include <aws/core/client/ClientConfiguration.h>
-#endif
-
-#include <algorithm>
-#include <cmath>
-#include <cstring>
-#include <iostream>
-#include <queue>
-#include <random>
-#include <thread>
-
-#include <curl/curl.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <unistd.h>
-
-using namespace std::string_literals;
-
-namespace nix {
-
-DownloadSettings downloadSettings;
-
-static GlobalConfig::Register r1(&downloadSettings);
-
-std::string resolveUri(const std::string& uri) {
-  if (uri.compare(0, 8, "channel:") == 0) {
-    return "https://nixos.org/channels/" + std::string(uri, 8) +
-           "/nixexprs.tar.xz";
-  }
-  return uri;
-}
-
-struct CurlDownloader : public Downloader {
-  CURLM* curlm = nullptr;
-
-  std::random_device rd;
-  std::mt19937 mt19937;
-
-  struct DownloadItem : public std::enable_shared_from_this<DownloadItem> {
-    CurlDownloader& downloader;
-    DownloadRequest request;
-    DownloadResult result;
-    bool done = false;  // whether either the success or failure function has
-                        // been called
-    Callback<DownloadResult> callback;
-    CURL* req = nullptr;
-    bool active =
-        false;  // whether the handle has been added to the multi object
-    std::string status;
-
-    unsigned int attempt = 0;
-
-    /* Don't start this download until the specified time point
-       has been reached. */
-    std::chrono::steady_clock::time_point embargo;
-
-    struct curl_slist* requestHeaders = nullptr;
-
-    std::string encoding;
-
-    bool acceptRanges = false;
-
-    curl_off_t writtenToSink = 0;
-
-    DownloadItem(CurlDownloader& downloader, const DownloadRequest& request,
-                 Callback<DownloadResult>&& callback)
-        : downloader(downloader),
-          request(request),
-          callback(std::move(callback)),
-          finalSink([this](const unsigned char* data, size_t len) {
-            if (this->request.dataCallback) {
-              long httpStatus = 0;
-              curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus);
-
-              /* Only write data to the sink if this is a
-                 successful response. */
-              if (httpStatus == 0 || httpStatus == 200 || httpStatus == 201 ||
-                  httpStatus == 206) {
-                writtenToSink += len;
-                this->request.dataCallback((char*)data, len);
-              }
-            } else {
-              this->result.data->append((char*)data, len);
-            }
-          }) {
-      LOG(INFO) << (request.data ? "uploading '" : "downloading '")
-                << request.uri << "'";
-
-      if (!request.expectedETag.empty()) {
-        requestHeaders = curl_slist_append(
-            requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
-      }
-      if (!request.mimeType.empty()) {
-        requestHeaders = curl_slist_append(
-            requestHeaders, ("Content-Type: " + request.mimeType).c_str());
-      }
-    }
-
-    ~DownloadItem() {
-      if (req != nullptr) {
-        if (active) {
-          curl_multi_remove_handle(downloader.curlm, req);
-        }
-        curl_easy_cleanup(req);
-      }
-      if (requestHeaders != nullptr) {
-        curl_slist_free_all(requestHeaders);
-      }
-      try {
-        if (!done) {
-          fail(DownloadError(
-              Interrupted,
-              format("download of '%s' was interrupted") % request.uri));
-        }
-      } catch (...) {
-        ignoreException();
-      }
-    }
-
-    void failEx(const std::exception_ptr& ex) {
-      assert(!done);
-      done = true;
-      callback.rethrow(ex);
-    }
-
-    template <class T>
-    void fail(const T& e) {
-      failEx(std::make_exception_ptr(e));
-    }
-
-    LambdaSink finalSink;
-    std::shared_ptr<CompressionSink> decompressionSink;
-
-    std::exception_ptr writeException;
-
-    size_t writeCallback(void* contents, size_t size, size_t nmemb) {
-      try {
-        size_t realSize = size * nmemb;
-        result.bodySize += realSize;
-
-        if (!decompressionSink) {
-          decompressionSink = makeDecompressionSink(encoding, finalSink);
-        }
-
-        (*decompressionSink)(static_cast<unsigned char*>(contents), realSize);
-
-        return realSize;
-      } catch (...) {
-        writeException = std::current_exception();
-        return 0;
-      }
-    }
-
-    static size_t writeCallbackWrapper(void* contents, size_t size,
-                                       size_t nmemb, void* userp) {
-      return (static_cast<DownloadItem*>(userp))
-          ->writeCallback(contents, size, nmemb);
-    }
-
-    size_t headerCallback(void* contents, size_t size, size_t nmemb) {
-      size_t realSize = size * nmemb;
-      std::string line(static_cast<char*>(contents), realSize);
-      DLOG(INFO) << "got header for '" << request.uri
-                 << "': " << absl::StripAsciiWhitespace(line);
-      if (line.compare(0, 5, "HTTP/") == 0) {  // new response starts
-        result.etag = "";
-        std::vector<std::string> ss =
-            absl::StrSplit(line, absl::ByChar(' '), absl::SkipEmpty());
-        status = ss.size() >= 2 ? ss[1] : "";
-        result.data = std::make_shared<std::string>();
-        result.bodySize = 0;
-        acceptRanges = false;
-        encoding = "";
-      } else {
-        auto i = line.find(':');
-        if (i != std::string::npos) {
-          std::string name = absl::AsciiStrToLower(
-              absl::StripAsciiWhitespace(std::string(line, 0, i)));
-          if (name == "etag") {
-            result.etag = absl::StripAsciiWhitespace(std::string(line, i + 1));
-            /* Hack to work around a GitHub bug: it sends
-               ETags, but ignores If-None-Match. So if we get
-               the expected ETag on a 200 response, then shut
-               down the connection because we already have the
-               data. */
-            if (result.etag == request.expectedETag && status == "200") {
-              DLOG(INFO)
-                  << "shutting down on 200 HTTP response with expected ETag";
-              return 0;
-            }
-          } else if (name == "content-encoding") {
-            encoding = absl::StripAsciiWhitespace(std::string(line, i + 1));
-          } else if (name == "accept-ranges" &&
-                     absl::AsciiStrToLower(absl::StripAsciiWhitespace(
-                         std::string(line, i + 1))) == "bytes") {
-            acceptRanges = true;
-          }
-        }
-      }
-      return realSize;
-    }
-
-    static size_t headerCallbackWrapper(void* contents, size_t size,
-                                        size_t nmemb, void* userp) {
-      return (static_cast<DownloadItem*>(userp))
-          ->headerCallback(contents, size, nmemb);
-    }
-
-    static int debugCallback(CURL* handle, curl_infotype type, char* data,
-                             size_t size, void* userptr) {
-      if (type == CURLINFO_TEXT) {
-        DLOG(INFO) << "curl: "
-                   << absl::StripTrailingAsciiWhitespace(
-                          std::string(data, size));
-      }
-      return 0;
-    }
-
-    size_t readOffset = 0;
-    size_t readCallback(char* buffer, size_t size, size_t nitems) {
-      if (readOffset == request.data->length()) {
-        return 0;
-      }
-      auto count = std::min(size * nitems, request.data->length() - readOffset);
-      assert(count);
-      memcpy(buffer, request.data->data() + readOffset, count);
-      readOffset += count;
-      return count;
-    }
-
-    static size_t readCallbackWrapper(char* buffer, size_t size, size_t nitems,
-                                      void* userp) {
-      return (static_cast<DownloadItem*>(userp))
-          ->readCallback(buffer, size, nitems);
-    }
-
-    void init() {
-      if (req == nullptr) {
-        req = curl_easy_init();
-      }
-
-      curl_easy_reset(req);
-
-      // TODO(tazjin): Add an Abseil flag for this
-      // if (verbosity >= lvlVomit) {
-      //   curl_easy_setopt(req, CURLOPT_VERBOSE, 1);
-      //   curl_easy_setopt(req, CURLOPT_DEBUGFUNCTION,
-      //                    DownloadItem::debugCallback);
-      // }
-
-      curl_easy_setopt(req, CURLOPT_URL, request.uri.c_str());
-      curl_easy_setopt(req, CURLOPT_FOLLOWLOCATION, 1L);
-      curl_easy_setopt(req, CURLOPT_MAXREDIRS, 10);
-      curl_easy_setopt(req, CURLOPT_NOSIGNAL, 1);
-      curl_easy_setopt(req, CURLOPT_USERAGENT,
-                       ("curl/" LIBCURL_VERSION " Nix/" + nixVersion +
-                        (downloadSettings.userAgentSuffix != ""
-                             ? " " + downloadSettings.userAgentSuffix.get()
-                             : ""))
-                           .c_str());
-#if LIBCURL_VERSION_NUM >= 0x072b00
-      curl_easy_setopt(req, CURLOPT_PIPEWAIT, 1);
-#endif
-#if LIBCURL_VERSION_NUM >= 0x072f00
-      if (downloadSettings.enableHttp2) {
-        curl_easy_setopt(req, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
-      } else {
-        curl_easy_setopt(req, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
-      }
-#endif
-      curl_easy_setopt(req, CURLOPT_WRITEFUNCTION,
-                       DownloadItem::writeCallbackWrapper);
-      curl_easy_setopt(req, CURLOPT_WRITEDATA, this);
-      curl_easy_setopt(req, CURLOPT_HEADERFUNCTION,
-                       DownloadItem::headerCallbackWrapper);
-      curl_easy_setopt(req, CURLOPT_HEADERDATA, this);
-
-      curl_easy_setopt(req, CURLOPT_HTTPHEADER, requestHeaders);
-
-      if (request.head) {
-        curl_easy_setopt(req, CURLOPT_NOBODY, 1);
-      }
-
-      if (request.data) {
-        curl_easy_setopt(req, CURLOPT_UPLOAD, 1L);
-        curl_easy_setopt(req, CURLOPT_READFUNCTION, readCallbackWrapper);
-        curl_easy_setopt(req, CURLOPT_READDATA, this);
-        curl_easy_setopt(req, CURLOPT_INFILESIZE_LARGE,
-                         (curl_off_t)request.data->length());
-      }
-
-      if (request.verifyTLS) {
-        if (!settings.caFile.empty()) {
-          curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.c_str());
-        }
-      } else {
-        curl_easy_setopt(req, CURLOPT_SSL_VERIFYPEER, 0);
-        curl_easy_setopt(req, CURLOPT_SSL_VERIFYHOST, 0);
-      }
-
-      curl_easy_setopt(req, CURLOPT_CONNECTTIMEOUT,
-                       downloadSettings.connectTimeout.get());
-
-      curl_easy_setopt(req, CURLOPT_LOW_SPEED_LIMIT, 1L);
-      curl_easy_setopt(req, CURLOPT_LOW_SPEED_TIME,
-                       downloadSettings.stalledDownloadTimeout.get());
-
-      /* If no file exist in the specified path, curl continues to work
-         anyway as if netrc support was disabled. */
-      curl_easy_setopt(req, CURLOPT_NETRC_FILE,
-                       settings.netrcFile.get().c_str());
-      curl_easy_setopt(req, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
-
-      if (writtenToSink != 0) {
-        curl_easy_setopt(req, CURLOPT_RESUME_FROM_LARGE, writtenToSink);
-      }
-
-      result.data = std::make_shared<std::string>();
-      result.bodySize = 0;
-    }
-
-    void finish(CURLcode code) {
-      long httpStatus = 0;
-      curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus);
-
-      char* effectiveUriCStr;
-      curl_easy_getinfo(req, CURLINFO_EFFECTIVE_URL, &effectiveUriCStr);
-      if (effectiveUriCStr != nullptr) {
-        result.effectiveUri = effectiveUriCStr;
-      }
-
-      DLOG(INFO) << "finished " << request.verb() << " of " << request.uri
-                 << "; curl status = " << code
-                 << ", HTTP status = " << httpStatus
-                 << ", body = " << result.bodySize << " bytes";
-
-      if (decompressionSink) {
-        try {
-          decompressionSink->finish();
-        } catch (...) {
-          writeException = std::current_exception();
-        }
-      }
-
-      if (code == CURLE_WRITE_ERROR && result.etag == request.expectedETag) {
-        code = CURLE_OK;
-        httpStatus = 304;
-      }
-
-      if (writeException) {
-        failEx(writeException);
-
-      } else if (code == CURLE_OK &&
-                 (httpStatus == 200 || httpStatus == 201 || httpStatus == 204 ||
-                  httpStatus == 206 || httpStatus == 304 ||
-                  httpStatus == 226 /* FTP */ ||
-                  httpStatus == 0 /* other protocol */)) {
-        result.cached = httpStatus == 304;
-        done = true;
-        callback(std::move(result));
-      }
-
-      else {
-        // We treat most errors as transient, but won't retry when hopeless
-        Error err = Transient;
-
-        if (httpStatus == 404 || httpStatus == 410 ||
-            code == CURLE_FILE_COULDNT_READ_FILE) {
-          // The file is definitely not there
-          err = NotFound;
-        } else if (httpStatus == 401 || httpStatus == 403 ||
-                   httpStatus == 407) {
-          // Don't retry on authentication/authorization failures
-          err = Forbidden;
-        } else if (httpStatus >= 400 && httpStatus < 500 && httpStatus != 408 &&
-                   httpStatus != 429) {
-          // Most 4xx errors are client errors and are probably not worth
-          // retrying:
-          //   * 408 means the server timed out waiting for us, so we try again
-          //   * 429 means too many requests, so we retry (with a delay)
-          err = Misc;
-        } else if (httpStatus == 501 || httpStatus == 505 ||
-                   httpStatus == 511) {
-          // Let's treat most 5xx (server) errors as transient, except for a
-          // handful:
-          //   * 501 not implemented
-          //   * 505 http version not supported
-          //   * 511 we're behind a captive portal
-          err = Misc;
-        } else {
-          // Don't bother retrying on certain cURL errors either
-          switch (code) {
-            case CURLE_FAILED_INIT:
-            case CURLE_URL_MALFORMAT:
-            case CURLE_NOT_BUILT_IN:
-            case CURLE_REMOTE_ACCESS_DENIED:
-            case CURLE_FILE_COULDNT_READ_FILE:
-            case CURLE_FUNCTION_NOT_FOUND:
-            case CURLE_ABORTED_BY_CALLBACK:
-            case CURLE_BAD_FUNCTION_ARGUMENT:
-            case CURLE_INTERFACE_FAILED:
-            case CURLE_UNKNOWN_OPTION:
-            case CURLE_SSL_CACERT_BADFILE:
-            case CURLE_TOO_MANY_REDIRECTS:
-            case CURLE_WRITE_ERROR:
-            case CURLE_UNSUPPORTED_PROTOCOL:
-              err = Misc;
-              break;
-            default:  // Shut up warnings
-              break;
-          }
-        }
-
-        attempt++;
-
-        auto exc =
-            code == CURLE_ABORTED_BY_CALLBACK && _isInterrupted
-                ? DownloadError(Interrupted, fmt("%s of '%s' was interrupted",
-                                                 request.verb(), request.uri))
-            : httpStatus != 0
-                ? DownloadError(
-                      err,
-                      fmt("unable to %s '%s': HTTP error %d", request.verb(),
-                          request.uri, httpStatus) +
-                          (code == CURLE_OK ? ""
-                                            : fmt(" (curl error: %s)",
-                                                  curl_easy_strerror(code))))
-                : DownloadError(
-                      err, fmt("unable to %s '%s': %s (%d)", request.verb(),
-                               request.uri, curl_easy_strerror(code), code));
-
-        /* If this is a transient error, then maybe retry the
-           download after a while. If we're writing to a
-           sink, we can only retry if the server supports
-           ranged requests. */
-        if (err == Transient && attempt < request.tries &&
-            (!this->request.dataCallback || writtenToSink == 0 ||
-             (acceptRanges && encoding.empty()))) {
-          int ms = request.baseRetryTimeMs *
-                   std::pow(2.0F, attempt - 1 +
-                                      std::uniform_real_distribution<>(
-                                          0.0, 0.5)(downloader.mt19937));
-          if (writtenToSink != 0) {
-            LOG(WARNING) << exc.what() << "; retrying from offset "
-                         << writtenToSink << " in " << ms << "ms";
-          } else {
-            LOG(WARNING) << exc.what() << "; retrying in " << ms << "ms";
-          }
-          embargo =
-              std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
-          downloader.enqueueItem(shared_from_this());
-        } else {
-          fail(exc);
-        }
-      }
-    }
-  };
-
-  struct State {
-    struct EmbargoComparator {
-      bool operator()(const std::shared_ptr<DownloadItem>& i1,
-                      const std::shared_ptr<DownloadItem>& i2) {
-        return i1->embargo > i2->embargo;
-      }
-    };
-    bool quit = false;
-    std::priority_queue<std::shared_ptr<DownloadItem>,
-                        std::vector<std::shared_ptr<DownloadItem>>,
-                        EmbargoComparator>
-        incoming;
-  };
-
-  Sync<State> state_;
-
-  /* We can't use a std::condition_variable to wake up the curl
-     thread, because it only monitors file descriptors. So use a
-     pipe instead. */
-  Pipe wakeupPipe;
-
-  std::thread workerThread;
-
-  CurlDownloader() : mt19937(rd()) {
-    static std::once_flag globalInit;
-    std::call_once(globalInit, curl_global_init, CURL_GLOBAL_ALL);
-
-    curlm = curl_multi_init();
-
-#if LIBCURL_VERSION_NUM >= 0x072b00  // Multiplex requires >= 7.43.0
-    curl_multi_setopt(curlm, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
-#endif
-#if LIBCURL_VERSION_NUM >= 0x071e00  // Max connections requires >= 7.30.0
-    curl_multi_setopt(curlm, CURLMOPT_MAX_TOTAL_CONNECTIONS,
-                      downloadSettings.httpConnections.get());
-#endif
-
-    wakeupPipe.create();
-    fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK);
-
-    workerThread = std::thread([&]() { workerThreadEntry(); });
-  }
-
-  ~CurlDownloader() override {
-    stopWorkerThread();
-
-    workerThread.join();
-
-    if (curlm != nullptr) {
-      curl_multi_cleanup(curlm);
-    }
-  }
-
-  void stopWorkerThread() {
-    /* Signal the worker thread to exit. */
-    {
-      auto state(state_.lock());
-      state->quit = true;
-    }
-    writeFull(wakeupPipe.writeSide.get(), " ", false);
-  }
-
-  void workerThreadMain() {
-    /* Cause this thread to be notified on SIGINT. */
-    auto callback = createInterruptCallback([&]() { stopWorkerThread(); });
-
-    std::map<CURL*, std::shared_ptr<DownloadItem>> items;
-
-    bool quit = false;
-
-    std::chrono::steady_clock::time_point nextWakeup;
-
-    while (!quit) {
-      checkInterrupt();
-
-      /* Let curl do its thing. */
-      int running;
-      CURLMcode mc = curl_multi_perform(curlm, &running);
-      if (mc != CURLM_OK) {
-        throw nix::Error(
-            format("unexpected error from curl_multi_perform(): %s") %
-            curl_multi_strerror(mc));
-      }
-
-      /* Set the promises of any finished requests. */
-      CURLMsg* msg;
-      int left;
-      while ((msg = curl_multi_info_read(curlm, &left)) != nullptr) {
-        if (msg->msg == CURLMSG_DONE) {
-          auto i = items.find(msg->easy_handle);
-          assert(i != items.end());
-          i->second->finish(msg->data.result);
-          curl_multi_remove_handle(curlm, i->second->req);
-          i->second->active = false;
-          items.erase(i);
-        }
-      }
-
-      /* Wait for activity, including wakeup events. */
-      int numfds = 0;
-      struct curl_waitfd extraFDs[1];
-      extraFDs[0].fd = wakeupPipe.readSide.get();
-      extraFDs[0].events = CURL_WAIT_POLLIN;
-      extraFDs[0].revents = 0;
-      long maxSleepTimeMs = items.empty() ? 10000 : 100;
-      auto sleepTimeMs =
-          nextWakeup != std::chrono::steady_clock::time_point()
-              ? std::max(
-                    0,
-                    static_cast<int>(
-                        std::chrono::duration_cast<std::chrono::milliseconds>(
-                            nextWakeup - std::chrono::steady_clock::now())
-                            .count()))
-              : maxSleepTimeMs;
-      VLOG(2) << "download thread waiting for " << sleepTimeMs << " ms";
-      mc = curl_multi_wait(curlm, extraFDs, 1, sleepTimeMs, &numfds);
-      if (mc != CURLM_OK) {
-        throw nix::Error(format("unexpected error from curl_multi_wait(): %s") %
-                         curl_multi_strerror(mc));
-      }
-
-      nextWakeup = std::chrono::steady_clock::time_point();
-
-      /* Add new curl requests from the incoming requests queue,
-         except for requests that are embargoed (waiting for a
-         retry timeout to expire). */
-      if ((extraFDs[0].revents & CURL_WAIT_POLLIN) != 0) {
-        char buf[1024];
-        auto res = read(extraFDs[0].fd, buf, sizeof(buf));
-        if (res == -1 && errno != EINTR) {
-          throw SysError("reading curl wakeup socket");
-        }
-      }
-
-      std::vector<std::shared_ptr<DownloadItem>> incoming;
-      auto now = std::chrono::steady_clock::now();
-
-      {
-        auto state(state_.lock());
-        while (!state->incoming.empty()) {
-          auto item = state->incoming.top();
-          if (item->embargo <= now) {
-            incoming.push_back(item);
-            state->incoming.pop();
-          } else {
-            if (nextWakeup == std::chrono::steady_clock::time_point() ||
-                item->embargo < nextWakeup) {
-              nextWakeup = item->embargo;
-            }
-            break;
-          }
-        }
-        quit = state->quit;
-      }
-
-      for (auto& item : incoming) {
-        DLOG(INFO) << "starting " << item->request.verb() << " of "
-                   << item->request.uri;
-        item->init();
-        curl_multi_add_handle(curlm, item->req);
-        item->active = true;
-        items[item->req] = item;
-      }
-    }
-
-    DLOG(INFO) << "download thread shutting down";
-  }
-
-  void workerThreadEntry() {
-    try {
-      workerThreadMain();
-    } catch (nix::Interrupted& e) {
-    } catch (std::exception& e) {
-      LOG(ERROR) << "unexpected error in download thread: " << e.what();
-    }
-
-    {
-      auto state(state_.lock());
-      while (!state->incoming.empty()) {
-        state->incoming.pop();
-      }
-      state->quit = true;
-    }
-  }
-
-  void enqueueItem(const std::shared_ptr<DownloadItem>& item) {
-    if (item->request.data && !absl::StartsWith(item->request.uri, "http://") &&
-        !absl::StartsWith(item->request.uri, "https://")) {
-      throw nix::Error("uploading to '%s' is not supported", item->request.uri);
-    }
-
-    {
-      auto state(state_.lock());
-      if (state->quit) {
-        throw nix::Error(
-            "cannot enqueue download request because the download thread is "
-            "shutting down");
-      }
-      state->incoming.push(item);
-    }
-    writeFull(wakeupPipe.writeSide.get(), " ");
-  }
-
-#ifdef ENABLE_S3
-  std::tuple<std::string, std::string, Store::Params> parseS3Uri(
-      std::string uri) {
-    auto [path, params] = splitUriAndParams(uri);
-
-    auto slash = path.find('/', 5);  // 5 is the length of "s3://" prefix
-    if (slash == std::string::npos) {
-      throw nix::Error("bad S3 URI '%s'", path);
-    }
-
-    std::string bucketName(path, 5, slash - 5);
-    std::string key(path, slash + 1);
-
-    return {bucketName, key, params};
-  }
-#endif
-
-  void enqueueDownload(const DownloadRequest& request,
-                       Callback<DownloadResult> callback) override {
-    /* Ugly hack to support s3:// URIs. */
-    if (absl::StartsWith(request.uri, "s3://")) {
-      // FIXME: do this on a worker thread
-      try {
-#ifdef ENABLE_S3
-        auto [bucketName, key, params] = parseS3Uri(request.uri);
-
-        std::string profile = get(params, "profile", "");
-        std::string region = get(params, "region", Aws::Region::US_EAST_1);
-        std::string scheme = get(params, "scheme", "");
-        std::string endpoint = get(params, "endpoint", "");
-
-        S3Helper s3Helper(profile, region, scheme, endpoint);
-
-        // FIXME: implement ETag
-        auto s3Res = s3Helper.getObject(bucketName, key);
-        DownloadResult res;
-        if (!s3Res.data)
-          throw DownloadError(
-              NotFound, fmt("S3 object '%s' does not exist", request.uri));
-        res.data = s3Res.data;
-        callback(std::move(res));
-#else
-        throw nix::Error(
-            "cannot download '%s' because Nix is not built with S3 support",
-            request.uri);
-#endif
-      } catch (...) {
-        callback.rethrow();
-      }
-      return;
-    }
-
-    enqueueItem(
-        std::make_shared<DownloadItem>(*this, request, std::move(callback)));
-  }
-};
-
-ref<Downloader> getDownloader() {
-  static ref<Downloader> downloader = makeDownloader();
-  return downloader;
-}
-
-ref<Downloader> makeDownloader() { return make_ref<CurlDownloader>(); }
-
-std::future<DownloadResult> Downloader::enqueueDownload(
-    const DownloadRequest& request) {
-  auto promise = std::make_shared<std::promise<DownloadResult>>();
-  enqueueDownload(
-      request,
-      Callback<DownloadResult>([promise](std::future<DownloadResult> fut) {
-        try {
-          promise->set_value(fut.get());
-        } catch (...) {
-          promise->set_exception(std::current_exception());
-        }
-      }));
-  return promise->get_future();
-}
-
-DownloadResult Downloader::download(const DownloadRequest& request) {
-  return enqueueDownload(request).get();
-}
-
-void Downloader::download(DownloadRequest&& request, Sink& sink) {
-  /* Note: we can't call 'sink' via request.dataCallback, because
-     that would cause the sink to execute on the downloader
-     thread. If 'sink' is a coroutine, this will fail. Also, if the
-     sink is expensive (e.g. one that does decompression and writing
-     to the Nix store), it would stall the download thread too much.
-     Therefore we use a buffer to communicate data between the
-     download thread and the calling thread. */
-
-  struct State {
-    bool quit = false;
-    std::exception_ptr exc;
-    std::string data;
-    std::condition_variable avail, request;
-  };
-
-  auto _state = std::make_shared<Sync<State>>();
-
-  /* In case of an exception, wake up the download thread. FIXME:
-     abort the download request. */
-  Finally finally([&]() {
-    auto state(_state->lock());
-    state->quit = true;
-    state->request.notify_one();
-  });
-
-  request.dataCallback = [_state](char* buf, size_t len) {
-    auto state(_state->lock());
-
-    if (state->quit) {
-      return;
-    }
-
-    /* If the buffer is full, then go to sleep until the calling
-       thread wakes us up (i.e. when it has removed data from the
-       buffer). We don't wait forever to prevent stalling the
-       download thread. (Hopefully sleeping will throttle the
-       sender.) */
-    if (state->data.size() > 1024 * 1024) {
-      DLOG(INFO) << "download buffer is full; going to sleep";
-      state.wait_for(state->request, std::chrono::seconds(10));
-    }
-
-    /* Append data to the buffer and wake up the calling
-       thread. */
-    state->data.append(buf, len);
-    state->avail.notify_one();
-  };
-
-  enqueueDownload(request, Callback<DownloadResult>(
-                               [_state](std::future<DownloadResult> fut) {
-                                 auto state(_state->lock());
-                                 state->quit = true;
-                                 try {
-                                   fut.get();
-                                 } catch (...) {
-                                   state->exc = std::current_exception();
-                                 }
-                                 state->avail.notify_one();
-                                 state->request.notify_one();
-                               }));
-
-  while (true) {
-    checkInterrupt();
-
-    std::string chunk;
-
-    /* Grab data if available, otherwise wait for the download
-       thread to wake us up. */
-    {
-      auto state(_state->lock());
-
-      while (state->data.empty()) {
-        if (state->quit) {
-          if (state->exc) {
-            std::rethrow_exception(state->exc);
-          }
-          return;
-        }
-
-        state.wait(state->avail);
-      }
-
-      chunk = std::move(state->data);
-      state->data = std::string();
-
-      state->request.notify_one();
-    }
-
-    /* Flush the data to the sink and wake up the download thread
-       if it's blocked on a full buffer. We don't hold the state
-       lock while doing this to prevent blocking the download
-       thread if sink() takes a long time. */
-    sink(reinterpret_cast<unsigned char*>(chunk.data()), chunk.size());
-  }
-}
-
-CachedDownloadResult Downloader::downloadCached(
-    const ref<Store>& store, const CachedDownloadRequest& request) {
-  auto url = resolveUri(request.uri);
-
-  auto name = request.name;
-  if (name.empty()) {
-    auto p = url.rfind('/');
-    if (p != std::string::npos) {
-      name = std::string(url, p + 1);
-    }
-  }
-
-  Path expectedStorePath;
-  if (request.expectedHash) {
-    expectedStorePath =
-        store->makeFixedOutputPath(request.unpack, request.expectedHash, name);
-    if (store->isValidPath(expectedStorePath)) {
-      CachedDownloadResult result;
-      result.storePath = expectedStorePath;
-      result.path = store->toRealPath(expectedStorePath);
-      return result;
-    }
-  }
-
-  Path cacheDir = getCacheDir() + "/nix/tarballs";
-  createDirs(cacheDir);
-
-  std::string urlHash = hashString(htSHA256, name + std::string("\0"s) + url)
-                            .to_string(Base32, false);
-
-  Path dataFile = cacheDir + "/" + urlHash + ".info";
-  Path fileLink = cacheDir + "/" + urlHash + "-file";
-
-  PathLocks lock({fileLink}, fmt("waiting for lock on '%1%'...", fileLink));
-
-  Path storePath;
-
-  std::string expectedETag;
-
-  bool skip = false;
-
-  CachedDownloadResult result;
-
-  if (pathExists(fileLink) && pathExists(dataFile)) {
-    storePath = readLink(fileLink);
-    store->addTempRoot(storePath);
-    if (store->isValidPath(storePath)) {
-      std::vector<std::string> ss = absl::StrSplit(
-          readFile(dataFile), absl::ByChar('\n'), absl::SkipEmpty());
-      if (ss.size() >= 3 && ss[0] == url) {
-        time_t lastChecked;
-        if (absl::SimpleAtoi(ss[2], &lastChecked) &&
-            static_cast<uint64_t>(lastChecked) + request.ttl >=
-                static_cast<uint64_t>(time(nullptr))) {
-          skip = true;
-          result.effectiveUri = request.uri;
-          result.etag = ss[1];
-        } else if (!ss[1].empty()) {
-          DLOG(INFO) << "verifying previous ETag: " << ss[1];
-          expectedETag = ss[1];
-        }
-      }
-    } else {
-      storePath = "";
-    }
-  }
-
-  if (!skip) {
-    try {
-      DownloadRequest request2(url);
-      request2.expectedETag = expectedETag;
-      auto res = download(request2);
-      result.effectiveUri = res.effectiveUri;
-      result.etag = res.etag;
-
-      if (!res.cached) {
-        ValidPathInfo info;
-        StringSink sink;
-        dumpString(*res.data, sink);
-        Hash hash = hashString(
-            request.expectedHash ? request.expectedHash.type : htSHA256,
-            *res.data);
-        info.path = store->makeFixedOutputPath(false, hash, name);
-        info.narHash = hashString(htSHA256, *sink.s);
-        info.narSize = sink.s->size();
-        info.ca = makeFixedOutputCA(false, hash);
-        store->addToStore(info, sink.s, NoRepair, NoCheckSigs);
-        storePath = info.path;
-      }
-
-      assert(!storePath.empty());
-      replaceSymlink(storePath, fileLink);
-
-      writeFile(dataFile, url + "\n" + res.etag + "\n" +
-                              std::to_string(time(nullptr)) + "\n");
-    } catch (DownloadError& e) {
-      if (storePath.empty()) {
-        throw;
-      }
-      LOG(WARNING) << e.msg() << "; using cached result";
-      result.etag = expectedETag;
-    }
-  }
-
-  if (request.unpack) {
-    Path unpackedLink = cacheDir + "/" + baseNameOf(storePath) + "-unpacked";
-    PathLocks lock2({unpackedLink},
-                    fmt("waiting for lock on '%1%'...", unpackedLink));
-    Path unpackedStorePath;
-    if (pathExists(unpackedLink)) {
-      unpackedStorePath = readLink(unpackedLink);
-      store->addTempRoot(unpackedStorePath);
-      if (!store->isValidPath(unpackedStorePath)) {
-        unpackedStorePath = "";
-      }
-    }
-    if (unpackedStorePath.empty()) {
-      LOG(INFO) << "unpacking '" << url << "' ...";
-      Path tmpDir = createTempDir();
-      AutoDelete autoDelete(tmpDir, true);
-      // FIXME: this requires GNU tar for decompression.
-      runProgram("tar", true,
-                 {"xf", store->toRealPath(storePath), "-C", tmpDir,
-                  "--strip-components", "1"});
-      unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256,
-                                            defaultPathFilter, NoRepair);
-    }
-    replaceSymlink(unpackedStorePath, unpackedLink);
-    storePath = unpackedStorePath;
-  }
-
-  if (!expectedStorePath.empty() && storePath != expectedStorePath) {
-    unsigned int statusCode = 102;
-    Hash gotHash =
-        request.unpack
-            ? hashPath(request.expectedHash.type, store->toRealPath(storePath))
-                  .first
-            : hashFile(request.expectedHash.type, store->toRealPath(storePath));
-    throw nix::Error(statusCode,
-                     "hash mismatch in file downloaded from '%s':\n  wanted: "
-                     "%s\n  got:    %s",
-                     url, request.expectedHash.to_string(),
-                     gotHash.to_string());
-  }
-
-  result.storePath = storePath;
-  result.path = store->toRealPath(storePath);
-  return result;
-}
-
-bool isUri(const std::string& s) {
-  if (s.compare(0, 8, "channel:") == 0) {
-    return true;
-  }
-  size_t pos = s.find("://");
-  if (pos == std::string::npos) {
-    return false;
-  }
-  std::string scheme(s, 0, pos);
-  return scheme == "http" || scheme == "https" || scheme == "file" ||
-         scheme == "channel" || scheme == "git" || scheme == "s3" ||
-         scheme == "ssh";
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/download.hh b/third_party/nix/src/libstore/download.hh
deleted file mode 100644
index cbfab5f40d..0000000000
--- a/third_party/nix/src/libstore/download.hh
+++ /dev/null
@@ -1,133 +0,0 @@
-#pragma once
-
-#include <future>
-#include <string>
-
-#include "libstore/globals.hh"
-#include "libutil/hash.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-struct DownloadSettings : Config {
-  Setting<bool> enableHttp2{this, true, "http2",
-                            "Whether to enable HTTP/2 support."};
-
-  Setting<std::string> userAgentSuffix{
-      this, "", "user-agent-suffix",
-      "String appended to the user agent in HTTP requests."};
-
-  Setting<size_t> httpConnections{this,
-                                  25,
-                                  "http-connections",
-                                  "Number of parallel HTTP connections.",
-                                  {"binary-caches-parallel-connections"}};
-
-  Setting<unsigned long> connectTimeout{
-      this, 0, "connect-timeout",
-      "Timeout for connecting to servers during downloads. 0 means use curl's "
-      "builtin default."};
-
-  Setting<unsigned long> stalledDownloadTimeout{
-      this, 300, "stalled-download-timeout",
-      "Timeout (in seconds) for receiving data from servers during download. "
-      "Nix cancels idle downloads after this timeout's duration."};
-
-  Setting<unsigned int> tries{
-      this, 5, "download-attempts",
-      "How often Nix will attempt to download a file before giving up."};
-};
-
-extern DownloadSettings downloadSettings;
-
-struct DownloadRequest {
-  std::string uri;
-  std::string expectedETag;
-  bool verifyTLS = true;
-  bool head = false;
-  size_t tries = downloadSettings.tries;
-  unsigned int baseRetryTimeMs = 250;
-  bool decompress = true;
-  std::shared_ptr<std::string> data;
-  std::string mimeType;
-  std::function<void(char*, size_t)> dataCallback;
-
-  DownloadRequest(const std::string& uri) : uri(uri) {}
-
-  std::string verb() { return data ? "upload" : "download"; }
-};
-
-struct DownloadResult {
-  bool cached = false;
-  std::string etag;
-  std::string effectiveUri;
-  std::shared_ptr<std::string> data;
-  uint64_t bodySize = 0;
-};
-
-struct CachedDownloadRequest {
-  std::string uri;
-  bool unpack = false;
-  std::string name;
-  Hash expectedHash;
-  unsigned int ttl = settings.tarballTtl;
-
-  CachedDownloadRequest(const std::string& uri) : uri(uri) {}
-};
-
-struct CachedDownloadResult {
-  // Note: 'storePath' may be different from 'path' when using a
-  // chroot store.
-  Path storePath;
-  Path path;
-  std::optional<std::string> etag;
-  std::string effectiveUri;
-};
-
-class Store;
-
-struct Downloader {
-  virtual ~Downloader() {}
-
-  /* Enqueue a download request, returning a future to the result of
-     the download. The future may throw a DownloadError
-     exception. */
-  virtual void enqueueDownload(const DownloadRequest& request,
-                               Callback<DownloadResult> callback) = 0;
-
-  std::future<DownloadResult> enqueueDownload(const DownloadRequest& request);
-
-  /* Synchronously download a file. */
-  DownloadResult download(const DownloadRequest& request);
-
-  /* Download a file, writing its data to a sink. The sink will be
-     invoked on the thread of the caller. */
-  void download(DownloadRequest&& request, Sink& sink);
-
-  /* Check if the specified file is already in ~/.cache/nix/tarballs
-     and is more recent than β€˜tarball-ttl’ seconds. Otherwise,
-     use the recorded ETag to verify if the server has a more
-     recent version, and if so, download it to the Nix store. */
-  CachedDownloadResult downloadCached(const ref<Store>& store,
-                                      const CachedDownloadRequest& request);
-
-  enum Error { NotFound, Forbidden, Misc, Transient, Interrupted };
-};
-
-/* Return a shared Downloader object. Using this object is preferred
-   because it enables connection reuse and HTTP/2 multiplexing. */
-ref<Downloader> getDownloader();
-
-/* Return a new Downloader object. */
-ref<Downloader> makeDownloader();
-
-class DownloadError : public Error {
- public:
-  Downloader::Error error;
-  DownloadError(Downloader::Error error, const FormatOrString& fs)
-      : Error(fs), error(error) {}
-};
-
-bool isUri(const std::string& s);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/export-import.cc b/third_party/nix/src/libstore/export-import.cc
deleted file mode 100644
index 8e93144339..0000000000
--- a/third_party/nix/src/libstore/export-import.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-#include <algorithm>
-
-#include "libstore/store-api.hh"
-#include "libstore/worker-protocol.hh"
-#include "libutil/archive.hh"
-
-namespace nix {
-
-struct HashAndWriteSink : Sink {
-  Sink& writeSink;
-  HashSink hashSink;
-  explicit HashAndWriteSink(Sink& writeSink)
-      : writeSink(writeSink), hashSink(htSHA256) {}
-  void operator()(const unsigned char* data, size_t len) override {
-    writeSink(data, len);
-    hashSink(data, len);
-  }
-  Hash currentHash() { return hashSink.currentHash().first; }
-};
-
-void Store::exportPaths(const Paths& paths, Sink& sink) {
-  Paths sorted = topoSortPaths(PathSet(paths.begin(), paths.end()));
-  std::reverse(sorted.begin(), sorted.end());
-
-  std::string doneLabel("paths exported");
-  // logger->incExpected(doneLabel, sorted.size());
-
-  for (auto& path : sorted) {
-    // Activity act(*logger, lvlInfo, format("exporting path '%s'") % path);
-    sink << 1;
-    exportPath(path, sink);
-    // logger->incProgress(doneLabel);
-  }
-
-  sink << 0;
-}
-
-void Store::exportPath(const Path& path, Sink& sink) {
-  auto info = queryPathInfo(path);
-
-  HashAndWriteSink hashAndWriteSink(sink);
-
-  narFromPath(path, hashAndWriteSink);
-
-  /* Refuse to export paths that have changed.  This prevents
-     filesystem corruption from spreading to other machines.
-     Don't complain if the stored hash is zero (unknown). */
-  Hash hash = hashAndWriteSink.currentHash();
-  if (hash != info->narHash && info->narHash != Hash(info->narHash.type)) {
-    throw Error(format("hash of path '%1%' has changed from '%2%' to '%3%'!") %
-                path % info->narHash.to_string() % hash.to_string());
-  }
-
-  hashAndWriteSink << exportMagic << path << info->references << info->deriver
-                   << 0;
-}
-
-Paths Store::importPaths(Source& source,
-                         const std::shared_ptr<FSAccessor>& accessor,
-                         CheckSigsFlag checkSigs) {
-  Paths res;
-  while (true) {
-    auto n = readNum<uint64_t>(source);
-    if (n == 0) {
-      break;
-    }
-    if (n != 1) {
-      throw Error(
-          "input doesn't look like something created by 'nix-store --export'");
-    }
-
-    /* Extract the NAR from the source. */
-    TeeSink tee(source);
-    parseDump(tee, tee.source);
-
-    uint32_t magic = readInt(source);
-    if (magic != exportMagic) {
-      throw Error("Nix archive cannot be imported; wrong format");
-    }
-
-    ValidPathInfo info;
-
-    info.path = readStorePath(*this, source);
-
-    // Activity act(*logger, lvlInfo, format("importing path '%s'") %
-    // info.path);
-
-    info.references = readStorePaths<PathSet>(*this, source);
-
-    info.deriver = readString(source);
-    if (!info.deriver.empty()) {
-      assertStorePath(info.deriver);
-    }
-
-    info.narHash = hashString(htSHA256, *tee.source.data);
-    info.narSize = tee.source.data->size();
-
-    // Ignore optional legacy signature.
-    if (readInt(source) == 1) {
-      readString(source);
-    }
-
-    addToStore(info, tee.source.data, NoRepair, checkSigs, accessor);
-
-    res.push_back(info.path);
-  }
-
-  return res;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/fs-accessor.hh b/third_party/nix/src/libstore/fs-accessor.hh
deleted file mode 100644
index 1bc1373dcb..0000000000
--- a/third_party/nix/src/libstore/fs-accessor.hh
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include "libutil/types.hh"
-
-namespace nix {
-
-/* An abstract class for accessing a filesystem-like structure, such
-   as a (possibly remote) Nix store or the contents of a NAR file. */
-class FSAccessor {
- public:
-  enum Type { tMissing, tRegular, tSymlink, tDirectory };
-
-  struct Stat {
-    Type type = tMissing;
-    uint64_t fileSize = 0;      // regular files only
-    bool isExecutable = false;  // regular files only
-    uint64_t narOffset = 0;     // regular files only
-  };
-
-  virtual ~FSAccessor() {}
-
-  virtual Stat stat(const Path& path) = 0;
-
-  virtual StringSet readDirectory(const Path& path) = 0;
-
-  virtual std::string readFile(const Path& path) = 0;
-
-  virtual std::string readLink(const Path& path) = 0;
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/gc.cc b/third_party/nix/src/libstore/gc.cc
deleted file mode 100644
index 07dc10629a..0000000000
--- a/third_party/nix/src/libstore/gc.cc
+++ /dev/null
@@ -1,997 +0,0 @@
-#include <algorithm>
-#include <cerrno>
-#include <climits>
-#include <functional>
-#include <queue>
-#include <random>
-#include <regex>
-
-#include <absl/strings/match.h>
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_split.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/local-store.hh"
-#include "libutil/finally.hh"
-
-namespace nix {
-
-constexpr std::string_view kGcLockName = "gc.lock";
-constexpr std::string_view kGcRootsDir = "gcroots";
-
-/* Acquire the global GC lock.  This is used to prevent new Nix
-   processes from starting after the temporary root files have been
-   read.  To be precise: when they try to create a new temporary root
-   file, they will block until the garbage collector has finished /
-   yielded the GC lock. */
-AutoCloseFD LocalStore::openGCLock(LockType lockType) {
-  Path fnGCLock = absl::StrCat(stateDir.get(), "/", kGcLockName);
-
-  DLOG(INFO) << "acquiring global GC lock " << fnGCLock;
-
-  AutoCloseFD fdGCLock(
-      open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600));
-
-  if (!fdGCLock) {
-    throw SysError(format("opening global GC lock '%1%'") % fnGCLock);
-  }
-
-  if (!lockFile(fdGCLock.get(), lockType, false)) {
-    LOG(ERROR) << "waiting for the big garbage collector lock...";
-    lockFile(fdGCLock.get(), lockType, true);
-  }
-
-  /* !!! Restrict read permission on the GC root.  Otherwise any
-     process that can open the file for reading can DoS the
-     collector. */
-
-  return fdGCLock;
-}
-
-static void makeSymlink(const Path& link, const Path& target) {
-  /* Create directories up to `gcRoot'. */
-  createDirs(dirOf(link));
-
-  /* Create the new symlink. */
-  Path tempLink =
-      (format("%1%.tmp-%2%-%3%") % link % getpid() % random()).str();
-  createSymlink(target, tempLink);
-
-  /* Atomically replace the old one. */
-  if (rename(tempLink.c_str(), link.c_str()) == -1) {
-    throw SysError(format("cannot rename '%1%' to '%2%'") % tempLink % link);
-  }
-}
-
-void LocalStore::syncWithGC() { AutoCloseFD fdGCLock = openGCLock(ltRead); }
-
-void LocalStore::addIndirectRoot(const Path& path) {
-  std::string hash = hashString(htSHA1, path).to_string(Base32, false);
-  Path realRoot =
-      canonPath(absl::StrCat(stateDir.get(), "/", kGcRootsDir, "/auto/", hash));
-  makeSymlink(realRoot, path);
-}
-
-Path LocalFSStore::addPermRoot(const Path& _storePath, const Path& _gcRoot,
-                               bool indirect, bool allowOutsideRootsDir) {
-  Path storePath(canonPath(_storePath));
-  Path gcRoot(canonPath(_gcRoot));
-  assertStorePath(storePath);
-
-  if (isInStore(gcRoot)) {
-    throw Error(format("creating a garbage collector root (%1%) in the Nix "
-                       "store is forbidden "
-                       "(are you running nix-build inside the store?)") %
-                gcRoot);
-  }
-
-  if (indirect) {
-    /* Don't clobber the link if it already exists and doesn't
-       point to the Nix store. */
-    if (pathExists(gcRoot) &&
-        (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) {
-      throw Error(format("cannot create symlink '%1%'; already exists") %
-                  gcRoot);
-    }
-    makeSymlink(gcRoot, storePath);
-    addIndirectRoot(gcRoot);
-  }
-
-  else {
-    if (!allowOutsideRootsDir) {
-      Path rootsDir = canonPath(absl::StrCat(stateDir.get(), "/", kGcRootsDir));
-
-      if (std::string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/") {
-        throw Error(format("path '%1%' is not a valid garbage collector root; "
-                           "it's not in the directory '%2%'") %
-                    gcRoot % rootsDir);
-      }
-    }
-
-    if (baseNameOf(gcRoot) == baseNameOf(storePath)) {
-      writeFile(gcRoot, "");
-    } else {
-      makeSymlink(gcRoot, storePath);
-    }
-  }
-
-  /* Check that the root can be found by the garbage collector.
-     !!! This can be very slow on machines that have many roots.
-     Instead of reading all the roots, it would be more efficient to
-     check if the root is in a directory in or linked from the
-     gcroots directory. */
-  if (settings.checkRootReachability) {
-    Roots roots = findRoots(false);
-    if (roots[storePath].count(gcRoot) == 0) {
-      LOG(ERROR) << "warning: '" << gcRoot
-                 << "' is not in a directory where the garbage "
-                 << "collector looks for roots; therefore, '" << storePath
-                 << "' might be removed by the garbage collector";
-    }
-  }
-
-  /* Grab the global GC root, causing us to block while a GC is in
-     progress.  This prevents the set of permanent roots from
-     increasing while a GC is in progress. */
-  syncWithGC();
-
-  return gcRoot;
-}
-
-void LocalStore::addTempRoot(const Path& path) {
-  auto state(_state.lock());
-
-  /* Create the temporary roots file for this process. */
-  if (!state->fdTempRoots) {
-    while (true) {
-      AutoCloseFD fdGCLock = openGCLock(ltRead);
-
-      if (pathExists(fnTempRoots)) {
-        /* It *must* be stale, since there can be no two
-           processes with the same pid. */
-        unlink(fnTempRoots.c_str());
-      }
-
-      state->fdTempRoots = openLockFile(fnTempRoots, true);
-
-      fdGCLock = AutoCloseFD(-1);
-
-      DLOG(INFO) << "acquiring read lock on " << fnTempRoots;
-      lockFile(state->fdTempRoots.get(), ltRead, true);
-
-      /* Check whether the garbage collector didn't get in our
-         way. */
-      struct stat st;
-      if (fstat(state->fdTempRoots.get(), &st) == -1) {
-        throw SysError(format("statting '%1%'") % fnTempRoots);
-      }
-      if (st.st_size == 0) {
-        break;
-      }
-
-      /* The garbage collector deleted this file before we could
-         get a lock.  (It won't delete the file after we get a
-         lock.)  Try again. */
-    }
-  }
-
-  /* Upgrade the lock to a write lock.  This will cause us to block
-     if the garbage collector is holding our lock. */
-  DLOG(INFO) << "acquiring write lock on " << fnTempRoots;
-  lockFile(state->fdTempRoots.get(), ltWrite, true);
-
-  std::string s = path + '\0';
-  writeFull(state->fdTempRoots.get(), s);
-
-  /* Downgrade to a read lock. */
-  DLOG(INFO) << "downgrading to read lock on " << fnTempRoots;
-  lockFile(state->fdTempRoots.get(), ltRead, true);
-}
-
-constexpr std::string_view kCensored = "{censored}";
-
-void LocalStore::findTempRoots(FDs& fds, Roots& tempRoots, bool censor) {
-  /* Read the `temproots' directory for per-process temporary root
-     files. */
-  for (auto& i : readDirectory(tempRootsDir)) {
-    Path path = tempRootsDir + "/" + i.name;
-
-    pid_t pid = std::stoi(i.name);
-
-    DLOG(INFO) << "reading temporary root file " << path;
-    FDPtr fd(new AutoCloseFD(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666)));
-    if (!*fd) {
-      /* It's okay if the file has disappeared. */
-      if (errno == ENOENT) {
-        continue;
-      }
-      throw SysError(format("opening temporary roots file '%1%'") % path);
-    }
-
-    /* This should work, but doesn't, for some reason. */
-    // FDPtr fd(new AutoCloseFD(openLockFile(path, false)));
-    // if (*fd == -1) { continue; }
-
-    /* Try to acquire a write lock without blocking.  This can
-       only succeed if the owning process has died.  In that case
-       we don't care about its temporary roots. */
-    if (lockFile(fd->get(), ltWrite, false)) {
-      LOG(ERROR) << "removing stale temporary roots file " << path;
-      unlink(path.c_str());
-      writeFull(fd->get(), "d");
-      continue;
-    }
-
-    /* Acquire a read lock.  This will prevent the owning process
-       from upgrading to a write lock, therefore it will block in
-       addTempRoot(). */
-    DLOG(INFO) << "waiting for read lock on " << path;
-    lockFile(fd->get(), ltRead, true);
-
-    /* Read the entire file. */
-    std::string contents = readFile(fd->get());
-
-    /* Extract the roots. */
-    std::string::size_type pos = 0;
-    std::string::size_type end;
-
-    while ((end = contents.find(static_cast<char>(0), pos)) !=
-           std::string::npos) {
-      Path root(contents, pos, end - pos);
-      DLOG(INFO) << "got temporary root " << root;
-      assertStorePath(root);
-      tempRoots[root].emplace(censor ? kCensored : fmt("{temp:%d}", pid));
-      pos = end + 1;
-    }
-
-    fds.push_back(fd); /* keep open */
-  }
-}
-
-void LocalStore::findRoots(const Path& path, unsigned char type, Roots& roots) {
-  auto foundRoot = [&](const Path& path, const Path& target) {
-    Path storePath = toStorePath(target);
-    if (isStorePath(storePath) && isValidPath(storePath)) {
-      roots[storePath].emplace(path);
-    } else {
-      LOG(INFO) << "skipping invalid root from '" << path << "' to '"
-                << storePath << "'";
-    }
-  };
-
-  try {
-    if (type == DT_UNKNOWN) {
-      type = getFileType(path);
-    }
-
-    if (type == DT_DIR) {
-      for (auto& i : readDirectory(path)) {
-        findRoots(path + "/" + i.name, i.type, roots);
-      }
-    }
-
-    else if (type == DT_LNK) {
-      Path target = readLink(path);
-      if (isInStore(target)) {
-        foundRoot(path, target);
-      }
-
-      /* Handle indirect roots. */
-      else {
-        target = absPath(target, dirOf(path));
-        if (!pathExists(target)) {
-          if (isInDir(path, absl::StrCat(stateDir.get(), "/", kGcRootsDir,
-                                         "/auto"))) {
-            LOG(INFO) << "removing stale link from '" << path << "' to '"
-                      << target << "'";
-            unlink(path.c_str());
-          }
-        } else {
-          struct stat st2 = lstat(target);
-          if (!S_ISLNK(st2.st_mode)) {
-            return;
-          }
-          Path target2 = readLink(target);
-          if (isInStore(target2)) {
-            foundRoot(target, target2);
-          }
-        }
-      }
-    }
-
-    else if (type == DT_REG) {
-      Path storePath = storeDir + "/" + baseNameOf(path);
-      if (isStorePath(storePath) && isValidPath(storePath)) {
-        roots[storePath].emplace(path);
-      }
-    }
-
-  }
-
-  catch (SysError& e) {
-    /* We only ignore permanent failures. */
-    if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR) {
-      LOG(INFO) << "cannot read potential root '" << path << "'";
-    } else {
-      throw;
-    }
-  }
-}
-
-void LocalStore::findRootsNoTemp(Roots& roots, bool censor) {
-  /* Process direct roots in {gcroots,profiles}. */
-  findRoots(absl::StrCat(stateDir.get(), "/", kGcRootsDir), DT_UNKNOWN, roots);
-  findRoots(stateDir + "/profiles", DT_UNKNOWN, roots);
-
-  /* Add additional roots returned by different platforms-specific
-     heuristics.  This is typically used to add running programs to
-     the set of roots (to prevent them from being garbage collected). */
-  findRuntimeRoots(roots, censor);
-}
-
-Roots LocalStore::findRoots(bool censor) {
-  Roots roots;
-  findRootsNoTemp(roots, censor);
-
-  FDs fds;
-  findTempRoots(fds, roots, censor);
-
-  return roots;
-}
-
-static void readProcLink(const std::string& file, Roots& roots) {
-  /* 64 is the starting buffer size gnu readlink uses... */
-  auto bufsiz = ssize_t{64};
-try_again:
-  char buf[bufsiz];
-  auto res = readlink(file.c_str(), buf, bufsiz);
-  if (res == -1) {
-    if (errno == ENOENT || errno == EACCES || errno == ESRCH) {
-      return;
-    }
-    throw SysError("reading symlink");
-  }
-  if (res == bufsiz) {
-    if (SSIZE_MAX / 2 < bufsiz) {
-      throw Error("stupidly long symlink");
-    }
-    bufsiz *= 2;
-    goto try_again;
-  }
-  if (res > 0 && buf[0] == '/') {
-    roots[std::string(static_cast<char*>(buf), res)].emplace(file);
-  }
-}
-
-static std::string quoteRegexChars(const std::string& raw) {
-  static auto specialRegex = std::regex(R"([.^$\\*+?()\[\]{}|])");
-  return std::regex_replace(raw, specialRegex, R"(\$&)");
-}
-
-static void readFileRoots(const char* path, Roots& roots) {
-  try {
-    roots[readFile(path)].emplace(path);
-  } catch (SysError& e) {
-    if (e.errNo != ENOENT && e.errNo != EACCES) {
-      throw;
-    }
-  }
-}
-
-void LocalStore::findRuntimeRoots(Roots& roots, bool censor) {
-  Roots unchecked;
-
-  auto procDir = AutoCloseDir{opendir("/proc")};
-  if (procDir) {
-    struct dirent* ent;
-    auto digitsRegex = std::regex(R"(^\d+$)");
-    auto mapRegex =
-        std::regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)");
-    auto storePathRegex = std::regex(quoteRegexChars(storeDir) +
-                                     R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)");
-    while (errno = 0, ent = readdir(procDir.get())) {
-      checkInterrupt();
-      if (std::regex_match(ent->d_name, digitsRegex)) {
-        readProcLink(fmt("/proc/%s/exe", ent->d_name), unchecked);
-        readProcLink(fmt("/proc/%s/cwd", ent->d_name), unchecked);
-
-        auto fdStr = fmt("/proc/%s/fd", ent->d_name);
-        auto fdDir = AutoCloseDir(opendir(fdStr.c_str()));
-        if (!fdDir) {
-          if (errno == ENOENT || errno == EACCES) {
-            continue;
-          }
-          throw SysError(format("opening %1%") % fdStr);
-        }
-        struct dirent* fd_ent;
-        while (errno = 0, fd_ent = readdir(fdDir.get())) {
-          if (fd_ent->d_name[0] != '.') {
-            readProcLink(fmt("%s/%s", fdStr, fd_ent->d_name), unchecked);
-          }
-        }
-        if (errno) {
-          if (errno == ESRCH) {
-            continue;
-          }
-          throw SysError(format("iterating /proc/%1%/fd") % ent->d_name);
-        }
-        fdDir.reset();
-
-        try {
-          auto mapFile = fmt("/proc/%s/maps", ent->d_name);
-          std::vector<std::string> mapLines = absl::StrSplit(
-              readFile(mapFile, true), absl::ByChar('\n'), absl::SkipEmpty());
-          for (const auto& line : mapLines) {
-            auto match = std::smatch{};
-            if (std::regex_match(line, match, mapRegex)) {
-              unchecked[match[1]].emplace(mapFile);
-            }
-          }
-
-          auto envFile = fmt("/proc/%s/environ", ent->d_name);
-          auto envString = readFile(envFile, true);
-          auto env_end = std::sregex_iterator{};
-          for (auto i = std::sregex_iterator{envString.begin(), envString.end(),
-                                             storePathRegex};
-               i != env_end; ++i) {
-            unchecked[i->str()].emplace(envFile);
-          }
-        } catch (SysError& e) {
-          if (errno == ENOENT || errno == EACCES || errno == ESRCH) {
-            continue;
-          }
-          throw;
-        }
-      }
-    }
-    if (errno) {
-      throw SysError("iterating /proc");
-    }
-  }
-
-  readFileRoots("/proc/sys/kernel/modprobe", unchecked);
-  readFileRoots("/proc/sys/kernel/fbsplash", unchecked);
-  readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked);
-
-  for (auto& [target, links] : unchecked) {
-    if (isInStore(target)) {
-      Path path = toStorePath(target);
-      if (isStorePath(path) && isValidPath(path)) {
-        DLOG(INFO) << "got additional root " << path;
-        if (censor) {
-          roots[path].insert(std::string(kCensored));
-        } else {
-          roots[path].insert(links.begin(), links.end());
-        }
-      }
-    }
-  }
-}
-
-struct GCLimitReached {};
-
-struct LocalStore::GCState {
-  GCOptions options;
-  GCResults& results;
-  PathSet roots;
-  PathSet tempRoots;
-  PathSet dead;
-  PathSet alive;
-  bool gcKeepOutputs;
-  bool gcKeepDerivations;
-  unsigned long long bytesInvalidated;
-  bool moveToTrash = true;
-  bool shouldDelete;
-  explicit GCState(GCResults& results_)
-      : results(results_), bytesInvalidated(0) {}
-};
-
-bool LocalStore::isActiveTempFile(const GCState& state, const Path& path,
-                                  const std::string& suffix) {
-  return absl::EndsWith(path, suffix) &&
-         state.tempRoots.find(std::string(
-             path, 0, path.size() - suffix.size())) != state.tempRoots.end();
-}
-
-void LocalStore::deleteGarbage(GCState& state, const Path& path) {
-  unsigned long long bytesFreed;
-  deletePath(path, bytesFreed);
-  state.results.bytesFreed += bytesFreed;
-}
-
-void LocalStore::deletePathRecursive(GCState& state, const Path& path) {
-  checkInterrupt();
-
-  unsigned long long size = 0;
-
-  if (isStorePath(path) && isValidPath(path)) {
-    PathSet referrers;
-    queryReferrers(path, referrers);
-    for (auto& i : referrers) {
-      if (i != path) {
-        deletePathRecursive(state, i);
-      }
-    }
-    size = queryPathInfo(path)->narSize;
-    invalidatePathChecked(path);
-  }
-
-  Path realPath = realStoreDir + "/" + baseNameOf(path);
-
-  struct stat st;
-  if (lstat(realPath.c_str(), &st) != 0) {
-    if (errno == ENOENT) {
-      return;
-    }
-    throw SysError(format("getting status of %1%") % realPath);
-  }
-
-  LOG(INFO) << "deleting '" << path << "'";
-
-  state.results.paths.insert(path);
-
-  /* If the path is not a regular file or symlink, move it to the
-     trash directory.  The move is to ensure that later (when we're
-     not holding the global GC lock) we can delete the path without
-     being afraid that the path has become alive again.  Otherwise
-     delete it right away. */
-  if (state.moveToTrash && S_ISDIR(st.st_mode)) {
-    // Estimate the amount freed using the narSize field.  FIXME:
-    // if the path was not valid, need to determine the actual
-    // size.
-    try {
-      if (chmod(realPath.c_str(), st.st_mode | S_IWUSR) == -1) {
-        throw SysError(format("making '%1%' writable") % realPath);
-      }
-      Path tmp = trashDir + "/" + baseNameOf(path);
-      if (rename(realPath.c_str(), tmp.c_str()) != 0) {
-        throw SysError(format("unable to rename '%1%' to '%2%'") % realPath %
-                       tmp);
-      }
-      state.bytesInvalidated += size;
-    } catch (SysError& e) {
-      if (e.errNo == ENOSPC) {
-        LOG(INFO) << "note: can't create move '" << realPath
-                  << "': " << e.msg();
-        deleteGarbage(state, realPath);
-      }
-    }
-  } else {
-    deleteGarbage(state, realPath);
-  }
-
-  if (state.results.bytesFreed + state.bytesInvalidated >
-      state.options.maxFreed) {
-    LOG(INFO) << "deleted or invalidated more than " << state.options.maxFreed
-              << " bytes; stopping";
-    throw GCLimitReached();
-  }
-}
-
-bool LocalStore::canReachRoot(GCState& state, PathSet& visited,
-                              const Path& path) {
-  if (visited.count(path) != 0u) {
-    return false;
-  }
-
-  if (state.alive.count(path) != 0u) {
-    return true;
-  }
-
-  if (state.dead.count(path) != 0u) {
-    return false;
-  }
-
-  if (state.roots.count(path) != 0u) {
-    DLOG(INFO) << "cannot delete '" << path << "' because it's a root";
-    state.alive.insert(path);
-    return true;
-  }
-
-  visited.insert(path);
-
-  if (!isStorePath(path) || !isValidPath(path)) {
-    return false;
-  }
-
-  PathSet incoming;
-
-  /* Don't delete this path if any of its referrers are alive. */
-  queryReferrers(path, incoming);
-
-  /* If keep-derivations is set and this is a derivation, then
-     don't delete the derivation if any of the outputs are alive. */
-  if (state.gcKeepDerivations && isDerivation(path)) {
-    PathSet outputs = queryDerivationOutputs(path);
-    for (auto& i : outputs) {
-      if (isValidPath(i) && queryPathInfo(i)->deriver == path) {
-        incoming.insert(i);
-      }
-    }
-  }
-
-  /* If keep-outputs is set, then don't delete this path if there
-     are derivers of this path that are not garbage. */
-  if (state.gcKeepOutputs) {
-    PathSet derivers = queryValidDerivers(path);
-    for (auto& i : derivers) {
-      incoming.insert(i);
-    }
-  }
-
-  for (auto& i : incoming) {
-    if (i != path) {
-      if (canReachRoot(state, visited, i)) {
-        state.alive.insert(path);
-        return true;
-      }
-    }
-  }
-
-  return false;
-}
-
-void LocalStore::tryToDelete(GCState& state, const Path& path) {
-  checkInterrupt();
-
-  auto realPath = realStoreDir + "/" + baseNameOf(path);
-  if (realPath == linksDir || realPath == trashDir) {
-    return;
-  }
-
-  // Activity act(*logger, lvlDebug, format("considering whether to delete
-  // '%1%'") % path);
-
-  if (!isStorePath(path) || !isValidPath(path)) {
-    /* A lock file belonging to a path that we're building right
-       now isn't garbage. */
-    if (isActiveTempFile(state, path, ".lock")) {
-      return;
-    }
-
-    /* Don't delete .chroot directories for derivations that are
-       currently being built. */
-    if (isActiveTempFile(state, path, ".chroot")) {
-      return;
-    }
-
-    /* Don't delete .check directories for derivations that are
-       currently being built, because we may need to run
-       diff-hook. */
-    if (isActiveTempFile(state, path, ".check")) {
-      return;
-    }
-  }
-
-  PathSet visited;
-
-  if (canReachRoot(state, visited, path)) {
-    DLOG(INFO) << "cannot delete '" << path << "' because it's still reachable";
-  } else {
-    /* No path we visited was a root, so everything is garbage.
-       But we only delete β€˜path’ and its referrers here so that
-       β€˜nix-store --delete’ doesn't have the unexpected effect of
-       recursing into derivations and outputs. */
-    state.dead.insert(visited.begin(), visited.end());
-    if (state.shouldDelete) {
-      deletePathRecursive(state, path);
-    }
-  }
-}
-
-/* Unlink all files in /nix/store/.links that have a link count of 1,
-   which indicates that there are no other links and so they can be
-   safely deleted.  FIXME: race condition with optimisePath(): we
-   might see a link count of 1 just before optimisePath() increases
-   the link count. */
-void LocalStore::removeUnusedLinks(const GCState& state) {
-  AutoCloseDir dir(opendir(linksDir.c_str()));
-  if (!dir) {
-    throw SysError(format("opening directory '%1%'") % linksDir);
-  }
-
-  long long actualSize = 0;
-  long long unsharedSize = 0;
-
-  struct dirent* dirent;
-  while (errno = 0, dirent = readdir(dir.get())) {
-    checkInterrupt();
-    std::string name = dirent->d_name;
-    if (name == "." || name == "..") {
-      continue;
-    }
-    Path path = linksDir + "/" + name;
-
-    struct stat st;
-    if (lstat(path.c_str(), &st) == -1) {
-      throw SysError(format("statting '%1%'") % path);
-    }
-
-    if (st.st_nlink != 1) {
-      actualSize += st.st_size;
-      unsharedSize += (st.st_nlink - 1) * st.st_size;
-      continue;
-    }
-
-    LOG(INFO) << "deleting unused link " << path;
-
-    if (unlink(path.c_str()) == -1) {
-      throw SysError(format("deleting '%1%'") % path);
-    }
-
-    state.results.bytesFreed += st.st_size;
-  }
-
-  struct stat st;
-  if (stat(linksDir.c_str(), &st) == -1) {
-    throw SysError(format("statting '%1%'") % linksDir);
-  }
-
-  long long overhead = st.st_blocks * 512ULL;
-
-  // TODO(tazjin): absl::StrFormat %.2f
-  LOG(INFO) << "note: currently hard linking saves "
-            << ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0))
-            << " MiB";
-}
-
-void LocalStore::collectGarbage(const GCOptions& options, GCResults& results) {
-  GCState state(results);
-  state.options = options;
-  state.gcKeepOutputs = settings.gcKeepOutputs;
-  state.gcKeepDerivations = settings.gcKeepDerivations;
-
-  /* Using `--ignore-liveness' with `--delete' can have unintended
-     consequences if `keep-outputs' or `keep-derivations' are true
-     (the garbage collector will recurse into deleting the outputs
-     or derivers, respectively).  So disable them. */
-  if (options.action == GCOptions::gcDeleteSpecific && options.ignoreLiveness) {
-    state.gcKeepOutputs = false;
-    state.gcKeepDerivations = false;
-  }
-
-  state.shouldDelete = options.action == GCOptions::gcDeleteDead ||
-                       options.action == GCOptions::gcDeleteSpecific;
-
-  if (state.shouldDelete) {
-    deletePath(reservedPath);
-  }
-
-  /* Acquire the global GC root.  This prevents
-     a) New roots from being added.
-     b) Processes from creating new temporary root files. */
-  AutoCloseFD fdGCLock = openGCLock(ltWrite);
-
-  /* Find the roots.  Since we've grabbed the GC lock, the set of
-     permanent roots cannot increase now. */
-  LOG(INFO) << "finding garbage collector roots...";
-  Roots rootMap;
-  if (!options.ignoreLiveness) {
-    findRootsNoTemp(rootMap, true);
-  }
-
-  for (auto& i : rootMap) {
-    state.roots.insert(i.first);
-  }
-
-  /* Read the temporary roots.  This acquires read locks on all
-     per-process temporary root files.  So after this point no paths
-     can be added to the set of temporary roots. */
-  FDs fds;
-  Roots tempRoots;
-  findTempRoots(fds, tempRoots, true);
-  for (auto& root : tempRoots) {
-    state.tempRoots.insert(root.first);
-  }
-  state.roots.insert(state.tempRoots.begin(), state.tempRoots.end());
-
-  /* After this point the set of roots or temporary roots cannot
-     increase, since we hold locks on everything.  So everything
-     that is not reachable from `roots' is garbage. */
-
-  if (state.shouldDelete) {
-    if (pathExists(trashDir)) {
-      deleteGarbage(state, trashDir);
-    }
-    try {
-      createDirs(trashDir);
-    } catch (SysError& e) {
-      if (e.errNo == ENOSPC) {
-        LOG(INFO) << "note: can't create trash directory: " << e.msg();
-        state.moveToTrash = false;
-      }
-    }
-  }
-
-  /* Now either delete all garbage paths, or just the specified
-     paths (for gcDeleteSpecific). */
-
-  if (options.action == GCOptions::gcDeleteSpecific) {
-    for (auto& i : options.pathsToDelete) {
-      assertStorePath(i);
-      tryToDelete(state, i);
-      if (state.dead.find(i) == state.dead.end()) {
-        throw Error(format("cannot delete path '%1%' since it is still alive") %
-                    i);
-      }
-    }
-
-  } else if (options.maxFreed > 0) {
-    if (state.shouldDelete) {
-      LOG(INFO) << "deleting garbage...";
-    } else {
-      LOG(ERROR) << "determining live/dead paths...";
-    }
-
-    try {
-      AutoCloseDir dir(opendir(realStoreDir.c_str()));
-      if (!dir) {
-        throw SysError(format("opening directory '%1%'") % realStoreDir);
-      }
-
-      /* Read the store and immediately delete all paths that
-         aren't valid.  When using --max-freed etc., deleting
-         invalid paths is preferred over deleting unreachable
-         paths, since unreachable paths could become reachable
-         again.  We don't use readDirectory() here so that GCing
-         can start faster. */
-      Paths entries;
-      struct dirent* dirent;
-      while (errno = 0, dirent = readdir(dir.get())) {
-        checkInterrupt();
-        std::string name = dirent->d_name;
-        if (name == "." || name == "..") {
-          continue;
-        }
-        Path path = storeDir + "/" + name;
-        if (isStorePath(path) && isValidPath(path)) {
-          entries.push_back(path);
-        } else {
-          tryToDelete(state, path);
-        }
-      }
-
-      dir.reset();
-
-      /* Now delete the unreachable valid paths.  Randomise the
-         order in which we delete entries to make the collector
-         less biased towards deleting paths that come
-         alphabetically first (e.g. /nix/store/000...).  This
-         matters when using --max-freed etc. */
-      std::vector<Path> entries_(entries.begin(), entries.end());
-      std::mt19937 gen(1);
-      std::shuffle(entries_.begin(), entries_.end(), gen);
-
-      for (auto& i : entries_) {
-        tryToDelete(state, i);
-      }
-
-    } catch (GCLimitReached& e) {
-    }
-  }
-
-  if (state.options.action == GCOptions::gcReturnLive) {
-    state.results.paths = state.alive;
-    return;
-  }
-
-  if (state.options.action == GCOptions::gcReturnDead) {
-    state.results.paths = state.dead;
-    return;
-  }
-
-  /* Allow other processes to add to the store from here on. */
-  fdGCLock = AutoCloseFD(-1);
-  fds.clear();
-
-  /* Delete the trash directory. */
-  LOG(INFO) << "deleting " << trashDir;
-  deleteGarbage(state, trashDir);
-
-  /* Clean up the links directory. */
-  if (options.action == GCOptions::gcDeleteDead ||
-      options.action == GCOptions::gcDeleteSpecific) {
-    LOG(INFO) << "deleting unused links...";
-    removeUnusedLinks(state);
-  }
-
-  /* While we're at it, vacuum the database. */
-  // if (options.action == GCOptions::gcDeleteDead) { vacuumDB(); }
-}
-
-void LocalStore::autoGC(bool sync) {
-  static auto fakeFreeSpaceFile =
-      getEnv("_NIX_TEST_FREE_SPACE_FILE").value_or("");
-
-  auto getAvail = [this]() -> uint64_t {
-    if (!fakeFreeSpaceFile.empty()) {
-      return std::stoll(readFile(fakeFreeSpaceFile));
-    }
-
-    struct statvfs st;
-    if (statvfs(realStoreDir.c_str(), &st) != 0) {
-      throw SysError("getting filesystem info about '%s'", realStoreDir);
-    }
-
-    return static_cast<uint64_t>(st.f_bavail) * st.f_bsize;
-  };
-
-  std::shared_future<void> future;
-
-  {
-    auto state(_state.lock());
-
-    if (state->gcRunning) {
-      future = state->gcFuture;
-      DLOG(INFO) << "waiting for auto-GC to finish";
-      goto sync;
-    }
-
-    auto now = std::chrono::steady_clock::now();
-
-    if (now < state->lastGCCheck +
-                  std::chrono::seconds(settings.minFreeCheckInterval)) {
-      return;
-    }
-
-    auto avail = getAvail();
-
-    state->lastGCCheck = now;
-
-    if (avail >= settings.minFree || avail >= settings.maxFree) {
-      return;
-    }
-
-    if (avail > state->availAfterGC * 0.97) {
-      return;
-    }
-
-    state->gcRunning = true;
-
-    std::promise<void> promise;
-    future = state->gcFuture = promise.get_future().share();
-
-    std::thread([promise{std::move(promise)}, this, avail, getAvail]() mutable {
-      try {
-        /* Wake up any threads waiting for the auto-GC to finish. */
-        Finally wakeup([&]() {
-          auto state(_state.lock());
-          state->gcRunning = false;
-          state->lastGCCheck = std::chrono::steady_clock::now();
-          promise.set_value();
-        });
-
-        GCOptions options;
-        options.maxFreed = settings.maxFree - avail;
-
-        LOG(INFO) << "running auto-GC to free " << options.maxFreed << " bytes";
-
-        GCResults results;
-
-        collectGarbage(options, results);
-
-        _state.lock()->availAfterGC = getAvail();
-
-      } catch (...) {
-        // FIXME: we could propagate the exception to the
-        // future, but we don't really care.
-        ignoreException();
-      }
-    }).detach();
-  }
-
-sync:
-  // Wait for the future outside of the state lock.
-  if (sync) {
-    future.get();
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/globals.cc b/third_party/nix/src/libstore/globals.cc
deleted file mode 100644
index 6babb4589f..0000000000
--- a/third_party/nix/src/libstore/globals.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-#include "libstore/globals.hh"
-
-#include <algorithm>
-#include <filesystem>
-#include <map>
-#include <thread>
-
-#include <absl/strings/numbers.h>
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_split.h>
-#include <dlfcn.h>
-
-#include "libutil/archive.hh"
-#include "libutil/args.hh"
-#include "libutil/util.hh"
-#include "nix_config.h"
-
-namespace nix {
-
-/* The default location of the daemon socket, relative to nixStateDir.
-   The socket is in a directory to allow you to control access to the
-   Nix daemon by setting the mode/ownership of the directory
-   appropriately.  (This wouldn't work on the socket itself since it
-   must be deleted and recreated on startup.) */
-#define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
-
-Settings settings;
-
-static GlobalConfig::Register r1(&settings);
-
-Settings::Settings()
-    : nixPrefix(NIX_PREFIX),
-      nixStore(canonPath(
-          getEnv("NIX_STORE_DIR")
-              .value_or(getEnv("NIX_STORE").value_or(NIX_STORE_DIR)))),
-      nixDataDir(canonPath(getEnv("NIX_DATA_DIR").value_or(NIX_DATA_DIR))),
-      nixLogDir(canonPath(getEnv("NIX_LOG_DIR").value_or(NIX_LOG_DIR))),
-      nixStateDir(canonPath(getEnv("NIX_STATE_DIR").value_or(NIX_STATE_DIR))),
-      nixConfDir(canonPath(getEnv("NIX_CONF_DIR").value_or(NIX_CONF_DIR))),
-      nixLibexecDir(
-          canonPath(getEnv("NIX_LIBEXEC_DIR").value_or(NIX_LIBEXEC_DIR))),
-      nixBinDir(canonPath(getEnv("NIX_BIN_DIR").value_or(NIX_BIN_DIR))),
-      nixManDir(canonPath(NIX_MAN_DIR)),
-      nixDaemonSocketFile(canonPath(nixStateDir + DEFAULT_SOCKET_PATH)) {
-  buildUsersGroup = getuid() == 0 ? "nixbld" : "";
-  lockCPU = getEnv("NIX_AFFINITY_HACK").value_or("1") == "1";
-
-  caFile = getEnv("NIX_SSL_CERT_FILE")
-               .value_or(getEnv("SSL_CERT_FILE").value_or(""));
-  if (caFile.empty()) {
-    for (auto& fn :
-         {"/etc/ssl/certs/ca-certificates.crt",
-          "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"}) {
-      if (pathExists(fn)) {
-        caFile = fn;
-        break;
-      }
-    }
-  }
-
-  /* Backwards compatibility. */
-  // TODO(tazjin): still?
-  auto s = getEnv("NIX_REMOTE_SYSTEMS");
-  if (s) {
-    Strings ss;
-    for (auto p : absl::StrSplit(*s, absl::ByChar(':'), absl::SkipEmpty())) {
-      ss.push_back(absl::StrCat("@", p));
-    }
-    builders = concatStringsSep(" ", ss);
-  }
-
-  sandboxPaths = absl::StrSplit("/bin/sh=" SANDBOX_SHELL,
-                                absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-}
-
-void loadConfFile() {
-  if (std::filesystem::exists(settings.nixConfDir + "/nix.conf")) {
-    globalConfig.applyConfigFile(settings.nixConfDir + "/nix.conf");
-  }
-
-  /* We only want to send overrides to the daemon, i.e. stuff from
-     ~/.nix/nix.conf or the command line. */
-  globalConfig.resetOverriden();
-
-  auto dirs = getConfigDirs();
-  // Iterate over them in reverse so that the ones appearing first in the path
-  // take priority
-  for (auto dir = dirs.rbegin(); dir != dirs.rend(); dir++) {
-    if (std::filesystem::exists(*dir + "/nix.conf")) {
-      globalConfig.applyConfigFile(*dir + "/nix/nix.conf");
-    }
-  }
-}
-
-unsigned int Settings::getDefaultCores() {
-  return std::max(1U, std::thread::hardware_concurrency());
-}
-
-StringSet Settings::getDefaultSystemFeatures() {
-  /* For backwards compatibility, accept some "features" that are
-     used in Nixpkgs to route builds to certain machines but don't
-     actually require anything special on the machines. */
-  StringSet features{"nixos-test", "benchmark", "big-parallel"};
-
-#if __linux__
-  if (access("/dev/kvm", R_OK | W_OK) == 0) {
-    features.insert("kvm");
-  }
-#endif
-
-  return features;
-}
-
-const std::string nixVersion = PACKAGE_VERSION;
-
-template <>
-void BaseSetting<SandboxMode>::set(const std::string& str) {
-  if (str == "true") {
-    value = smEnabled;
-  } else if (str == "relaxed") {
-    value = smRelaxed;
-  } else if (str == "false") {
-    value = smDisabled;
-  } else {
-    throw UsageError("option '%s' has invalid value '%s'", name, str);
-  }
-}
-
-template <>
-std::string BaseSetting<SandboxMode>::to_string() {
-  if (value == smEnabled) {
-    return "true";
-  }
-  if (value == smRelaxed) {
-    return "relaxed";
-  } else if (value == smDisabled) {
-    return "false";
-  } else {
-    abort();
-  }
-}
-
-template <>
-void BaseSetting<SandboxMode>::toJSON(JSONPlaceholder& out) {
-  AbstractSetting::toJSON(out);
-}
-
-template <>
-void BaseSetting<SandboxMode>::convertToArg(Args& args,
-                                            const std::string& category) {
-  args.mkFlag()
-      .longName(name)
-      .description("Enable sandboxing.")
-      .handler([=](const std::vector<std::string>& ss) { override(smEnabled); })
-      .category(category);
-  args.mkFlag()
-      .longName("no-" + name)
-      .description("Disable sandboxing.")
-      .handler(
-          [=](const std::vector<std::string>& ss) { override(smDisabled); })
-      .category(category);
-  args.mkFlag()
-      .longName("relaxed-" + name)
-      .description("Enable sandboxing, but allow builds to disable it.")
-      .handler([=](const std::vector<std::string>& ss) { override(smRelaxed); })
-      .category(category);
-}
-
-void MaxBuildJobsSetting::set(const std::string& str) {
-  if (str == "auto") {
-    value = std::max(1U, std::thread::hardware_concurrency());
-  } else if (!absl::SimpleAtoi(str, &value)) {
-    throw UsageError(
-        "configuration setting '%s' should be 'auto' or an integer", name);
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/globals.hh b/third_party/nix/src/libstore/globals.hh
deleted file mode 100644
index ed9b6a338e..0000000000
--- a/third_party/nix/src/libstore/globals.hh
+++ /dev/null
@@ -1,464 +0,0 @@
-#pragma once
-
-#include <limits>
-#include <map>
-
-#include <sys/types.h>
-
-#include "libutil/config.hh"
-#include "libutil/types.hh"
-#include "libutil/util.hh"
-#include "nix_config.h"
-
-namespace nix {
-
-typedef enum { smEnabled, smRelaxed, smDisabled } SandboxMode;
-
-struct MaxBuildJobsSetting : public BaseSetting<unsigned int> {
-  MaxBuildJobsSetting(Config* options, unsigned int def,
-                      const std::string& name, const std::string& description,
-                      const std::set<std::string>& aliases = {})
-      : BaseSetting<unsigned int>(def, name, description, aliases) {
-    options->addSetting(this);
-  }
-
-  void set(const std::string& str) override;
-};
-
-class Settings : public Config {
-  static unsigned int getDefaultCores();
-
-  static StringSet getDefaultSystemFeatures();
-
- public:
-  Settings();
-
-  Path nixPrefix;
-
-  /* The directory where we store sources and derived files. */
-  Path nixStore;
-
-  Path nixDataDir; /* !!! fix */
-
-  /* The directory where we log various operations. */
-  Path nixLogDir;
-
-  /* The directory where state is stored. */
-  Path nixStateDir;
-
-  /* The directory where configuration files are stored. */
-  Path nixConfDir;
-
-  /* The directory where internal helper programs are stored. */
-  Path nixLibexecDir;
-
-  /* The directory where the main programs are stored. */
-  Path nixBinDir;
-
-  /* The directory where the man pages are stored. */
-  Path nixManDir;
-
-  /* File name of the socket the daemon listens to.  */
-  Path nixDaemonSocketFile;
-
-  Setting<std::string> storeUri{this, getEnv("NIX_REMOTE").value_or("auto"),
-                                "store", "The default Nix store to use."};
-
-  Setting<bool> keepFailed{
-      this, false, "keep-failed",
-      "Whether to keep temporary directories of failed builds."};
-
-  Setting<bool> keepGoing{
-      this, false, "keep-going",
-      "Whether to keep building derivations when another build fails."};
-
-  Setting<bool> tryFallback{
-      this,
-      false,
-      "fallback",
-      "Whether to fall back to building when substitution fails.",
-      {"build-fallback"}};
-
-  /* Whether to show build log output in real time. */
-  bool verboseBuild = true;
-
-  Setting<size_t> logLines{
-      this, 10, "log-lines",
-      "If verbose-build is false, the number of lines of the tail of "
-      "the log to show if a build fails."};
-
-  MaxBuildJobsSetting maxBuildJobs{this,
-                                   1,
-                                   "max-jobs",
-                                   "Maximum number of parallel build jobs. "
-                                   "\"auto\" means use number of cores.",
-                                   {"build-max-jobs"}};
-
-  Setting<unsigned int> buildCores{
-      this,
-      getDefaultCores(),
-      "cores",
-      "Number of CPU cores to utilize in parallel within a build, "
-      "i.e. by passing this number to Make via '-j'. 0 means that the "
-      "number of actual CPU cores on the local host ought to be "
-      "auto-detected.",
-      {"build-cores"}};
-
-  /* Read-only mode.  Don't copy stuff to the store, don't change
-     the database. */
-  bool readOnlyMode = false;
-
-  Setting<std::string> thisSystem{this, SYSTEM, "system",
-                                  "The canonical Nix system name."};
-
-  Setting<time_t> maxSilentTime{
-      this,
-      0,
-      "max-silent-time",
-      "The maximum time in seconds that a builer can go without "
-      "producing any output on stdout/stderr before it is killed. "
-      "0 means infinity.",
-      {"build-max-silent-time"}};
-
-  Setting<time_t> buildTimeout{
-      this,
-      0,
-      "timeout",
-      "The maximum duration in seconds that a builder can run. "
-      "0 means infinity.",
-      {"build-timeout"}};
-
-  PathSetting buildHook{this, true, nixLibexecDir + "/nix/build-remote",
-                        "build-hook",
-                        "The path of the helper program that executes builds "
-                        "to remote machines."};
-
-  Setting<std::string> builders{this, "@" + nixConfDir + "/machines",
-                                "builders",
-                                "A semicolon-separated list of build machines, "
-                                "in the format of nix.machines."};
-
-  Setting<bool> buildersUseSubstitutes{
-      this, false, "builders-use-substitutes",
-      "Whether build machines should use their own substitutes for obtaining "
-      "build dependencies if possible, rather than waiting for this host to "
-      "upload them."};
-
-  Setting<off_t> reservedSize{
-      this, 8 * 1024 * 1024, "gc-reserved-space",
-      "Amount of reserved disk space for the garbage collector."};
-
-  Setting<bool> fsyncMetadata{this, true, "fsync-metadata",
-                              "Whether SQLite should use fsync()."};
-
-  Setting<bool> useSQLiteWAL{this, true, "use-sqlite-wal",
-                             "Whether SQLite should use WAL mode."};
-
-  Setting<bool> syncBeforeRegistering{
-      this, false, "sync-before-registering",
-      "Whether to call sync() before registering a path as valid."};
-
-  Setting<bool> useSubstitutes{this,
-                               true,
-                               "substitute",
-                               "Whether to use substitutes.",
-                               {"build-use-substitutes"}};
-
-  Setting<std::string> buildUsersGroup{
-      this, "", "build-users-group",
-      "The Unix group that contains the build users."};
-
-  Setting<bool> impersonateLinux26{
-      this,
-      false,
-      "impersonate-linux-26",
-      "Whether to impersonate a Linux 2.6 machine on newer kernels.",
-      {"build-impersonate-linux-26"}};
-
-  Setting<bool> keepLog{this,
-                        true,
-                        "keep-build-log",
-                        "Whether to store build logs.",
-                        {"build-keep-log"}};
-
-  Setting<bool> compressLog{this,
-                            true,
-                            "compress-build-log",
-                            "Whether to compress logs.",
-                            {"build-compress-log"}};
-
-  Setting<unsigned long> maxLogSize{
-      this,
-      0,
-      "max-build-log-size",
-      "Maximum number of bytes a builder can write to stdout/stderr "
-      "before being killed (0 means no limit).",
-      {"build-max-log-size"}};
-
-  /* When buildRepeat > 0 and verboseBuild == true, whether to print
-     repeated builds (i.e. builds other than the first one) to
-     stderr. Hack to prevent Hydra logs from being polluted. */
-  bool printRepeatedBuilds = true;
-
-  Setting<unsigned int> pollInterval{
-      this, 5, "build-poll-interval",
-      "How often (in seconds) to poll for locks."};
-
-  Setting<bool> checkRootReachability{
-      this, false, "gc-check-reachability",
-      "Whether to check if new GC roots can in fact be found by the "
-      "garbage collector."};
-
-  Setting<bool> gcKeepOutputs{
-      this,
-      false,
-      "keep-outputs",
-      "Whether the garbage collector should keep outputs of live derivations.",
-      {"gc-keep-outputs"}};
-
-  Setting<bool> gcKeepDerivations{
-      this,
-      true,
-      "keep-derivations",
-      "Whether the garbage collector should keep derivers of live paths.",
-      {"gc-keep-derivations"}};
-
-  Setting<bool> autoOptimiseStore{this, false, "auto-optimise-store",
-                                  "Whether to automatically replace files with "
-                                  "identical contents with hard links."};
-
-  Setting<bool> envKeepDerivations{
-      this,
-      false,
-      "keep-env-derivations",
-      "Whether to add derivations as a dependency of user environments "
-      "(to prevent them from being GCed).",
-      {"env-keep-derivations"}};
-
-  /* Whether to lock the Nix client and worker to the same CPU. */
-  bool lockCPU;
-
-  /* Whether to show a stack trace if Nix evaluation fails. */
-  Setting<bool> showTrace{
-      this, false, "show-trace",
-      "Whether to show a stack trace on evaluation errors."};
-
-  Setting<SandboxMode> sandboxMode {
-    this,
-#if __linux__
-        smEnabled
-#else
-        smDisabled
-#endif
-        ,
-        "sandbox",
-        "Whether to enable sandboxed builds. Can be \"true\", \"false\" or "
-        "\"relaxed\".",
-    {
-      "build-use-chroot", "build-use-sandbox"
-    }
-  };
-
-  Setting<PathSet> sandboxPaths{
-      this,
-      {},
-      "sandbox-paths",
-      "The paths to make available inside the build sandbox.",
-      {"build-chroot-dirs", "build-sandbox-paths"}};
-
-  Setting<bool> sandboxFallback{
-      this, true, "sandbox-fallback",
-      "Whether to disable sandboxing when the kernel doesn't allow it."};
-
-  Setting<PathSet> extraSandboxPaths{
-      this,
-      {},
-      "extra-sandbox-paths",
-      "Additional paths to make available inside the build sandbox.",
-      {"build-extra-chroot-dirs", "build-extra-sandbox-paths"}};
-
-  Setting<size_t> buildRepeat{
-      this,
-      0,
-      "repeat",
-      "The number of times to repeat a build in order to verify determinism.",
-      {"build-repeat"}};
-
-#if __linux__
-  Setting<std::string> sandboxShmSize{
-      this, "50%", "sandbox-dev-shm-size",
-      "The size of /dev/shm in the build sandbox."};
-
-  Setting<Path> sandboxBuildDir{this, "/build", "sandbox-build-dir",
-                                "The build directory inside the sandbox."};
-#endif
-
-  Setting<PathSet> allowedImpureHostPrefixes{
-      this,
-      {},
-      "allowed-impure-host-deps",
-      "Which prefixes to allow derivations to ask for access to (primarily for "
-      "Darwin)."};
-
-  Setting<bool> runDiffHook{
-      this, false, "run-diff-hook",
-      "Whether to run the program specified by the diff-hook setting "
-      "repeated builds produce a different result. Typically used to "
-      "plug in diffoscope."};
-
-  PathSetting diffHook{
-      this, true, "", "diff-hook",
-      "A program that prints out the differences between the two paths "
-      "specified on its command line."};
-
-  Setting<bool> enforceDeterminism{
-      this, true, "enforce-determinism",
-      "Whether to fail if repeated builds produce different output."};
-
-  Setting<Strings> trustedPublicKeys{
-      this,
-      {"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="},
-      "trusted-public-keys",
-      "Trusted public keys for secure substitution.",
-      {"binary-cache-public-keys"}};
-
-  Setting<Strings> secretKeyFiles{
-      this,
-      {},
-      "secret-key-files",
-      "Secret keys with which to sign local builds."};
-
-  Setting<unsigned int> tarballTtl{
-      this, 60 * 60, "tarball-ttl",
-      "How long downloaded files are considered up-to-date."};
-
-  Setting<bool> requireSigs{
-      this, true, "require-sigs",
-      "Whether to check that any non-content-addressed path added to the "
-      "Nix store has a valid signature (that is, one signed using a key "
-      "listed in 'trusted-public-keys'."};
-
-  Setting<StringSet> extraPlatforms{
-      this,
-      std::string{SYSTEM} == "x86_64-linux" ? StringSet{"i686-linux"}
-                                            : StringSet{},
-      "extra-platforms",
-      "Additional platforms that can be built on the local system. "
-      "These may be supported natively (e.g. armv7 on some aarch64 CPUs "
-      "or using hacks like qemu-user."};
-
-  Setting<StringSet> systemFeatures{
-      this, getDefaultSystemFeatures(), "system-features",
-      "Optional features that this system implements (like \"kvm\")."};
-
-  Setting<Strings> substituters{
-      this,
-      nixStore == "/nix/store" ? Strings{"https://cache.nixos.org/"}
-                               : Strings(),
-      "substituters",
-      "The URIs of substituters (such as https://cache.nixos.org/).",
-      {"binary-caches"}};
-
-  // FIXME: provide a way to add to option values.
-  Setting<Strings> extraSubstituters{this,
-                                     {},
-                                     "extra-substituters",
-                                     "Additional URIs of substituters.",
-                                     {"extra-binary-caches"}};
-
-  Setting<StringSet> trustedSubstituters{
-      this,
-      {},
-      "trusted-substituters",
-      "Disabled substituters that may be enabled via the substituters option "
-      "by untrusted users.",
-      {"trusted-binary-caches"}};
-
-  Setting<Strings> trustedUsers{this,
-                                {"root"},
-                                "trusted-users",
-                                "Which users or groups are trusted to ask the "
-                                "daemon to do unsafe things."};
-
-  Setting<unsigned int> ttlNegativeNarInfoCache{
-      this, 3600, "narinfo-cache-negative-ttl",
-      "The TTL in seconds for negative lookups in the disk cache i.e binary "
-      "cache lookups that "
-      "return an invalid path result"};
-
-  Setting<unsigned int> ttlPositiveNarInfoCache{
-      this, 30 * 24 * 3600, "narinfo-cache-positive-ttl",
-      "The TTL in seconds for positive lookups in the disk cache i.e binary "
-      "cache lookups that "
-      "return a valid path result."};
-
-  /* ?Who we trust to use the daemon in safe ways */
-  Setting<Strings> allowedUsers{
-      this,
-      {"*"},
-      "allowed-users",
-      "Which users or groups are allowed to connect to the daemon."};
-
-  Setting<bool> printMissing{
-      this, true, "print-missing",
-      "Whether to print what paths need to be built or downloaded."};
-
-  Setting<std::string> preBuildHook{
-      this, "", "pre-build-hook",
-      "A program to run just before a build to set derivation-specific build "
-      "settings."};
-
-  Setting<std::string> postBuildHook{
-      this, "", "post-build-hook",
-      "A program to run just after each successful build."};
-
-  Setting<std::string> netrcFile{this, fmt("%s/%s", nixConfDir, "netrc"),
-                                 "netrc-file",
-                                 "Path to the netrc file used to obtain "
-                                 "usernames/passwords for downloads."};
-
-  /* Path to the SSL CA file used */
-  Path caFile;
-
-#if __linux__
-  Setting<bool> filterSyscalls{
-      this, true, "filter-syscalls",
-      "Whether to prevent certain dangerous system calls, such as "
-      "creation of setuid/setgid files or adding ACLs or extended "
-      "attributes. Only disable this if you're aware of the "
-      "security implications."};
-
-  Setting<bool> allowNewPrivileges{
-      this, false, "allow-new-privileges",
-      "Whether builders can acquire new privileges by calling programs with "
-      "setuid/setgid bits or with file capabilities."};
-#endif
-
-  Setting<Strings> hashedMirrors{
-      this,
-      {"http://tarballs.nixos.org/"},
-      "hashed-mirrors",
-      "A list of servers used by builtins.fetchurl to fetch files by hash."};
-
-  Setting<uint64_t> minFree{this, 0, "min-free",
-                            "Automatically run the garbage collector when free "
-                            "disk space drops below the specified amount."};
-
-  Setting<uint64_t> maxFree{this, std::numeric_limits<uint64_t>::max(),
-                            "max-free",
-                            "Stop deleting garbage when free disk space is "
-                            "above the specified amount."};
-
-  Setting<uint64_t> minFreeCheckInterval{
-      this, 5, "min-free-check-interval",
-      "Number of seconds between checking free disk space."};
-};
-
-// FIXME: don't use a global variable.
-extern Settings settings;
-
-void loadConfFile();
-
-extern const std::string nixVersion;
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/http-binary-cache-store.cc b/third_party/nix/src/libstore/http-binary-cache-store.cc
deleted file mode 100644
index c713ac43c4..0000000000
--- a/third_party/nix/src/libstore/http-binary-cache-store.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-#include <utility>
-
-#include <glog/logging.h>
-
-#include "libstore/binary-cache-store.hh"
-#include "libstore/download.hh"
-#include "libstore/globals.hh"
-#include "libstore/nar-info-disk-cache.hh"
-
-namespace nix {
-
-MakeError(UploadToHTTP, Error);
-
-class HttpBinaryCacheStore : public BinaryCacheStore {
- private:
-  Path cacheUri;
-
-  struct State {
-    bool enabled = true;
-    std::chrono::steady_clock::time_point disabledUntil;
-  };
-
-  Sync<State> _state;
-
- public:
-  HttpBinaryCacheStore(const Params& params, Path _cacheUri)
-      : BinaryCacheStore(params), cacheUri(std::move(_cacheUri)) {
-    if (cacheUri.back() == '/') {
-      cacheUri.pop_back();
-    }
-
-    diskCache = getNarInfoDiskCache();
-  }
-
-  std::string getUri() override { return cacheUri; }
-
-  void init() override {
-    // FIXME: do this lazily?
-    if (!diskCache->cacheExists(cacheUri, wantMassQuery_, priority)) {
-      try {
-        BinaryCacheStore::init();
-      } catch (UploadToHTTP&) {
-        throw Error("'%s' does not appear to be a binary cache", cacheUri);
-      }
-      diskCache->createCache(cacheUri, storeDir, wantMassQuery_, priority);
-    }
-  }
-
- protected:
-  void maybeDisable() {
-    auto state(_state.lock());
-    if (state->enabled && settings.tryFallback) {
-      int t = 60;
-      LOG(WARNING) << "disabling binary cache '" << getUri() << "' for " << t
-                   << " seconds";
-      state->enabled = false;
-      state->disabledUntil =
-          std::chrono::steady_clock::now() + std::chrono::seconds(t);
-    }
-  }
-
-  void checkEnabled() {
-    auto state(_state.lock());
-    if (state->enabled) {
-      return;
-    }
-    if (std::chrono::steady_clock::now() > state->disabledUntil) {
-      state->enabled = true;
-      DLOG(INFO) << "re-enabling binary cache '" << getUri() << "'";
-      return;
-    }
-    throw SubstituterDisabled("substituter '%s' is disabled", getUri());
-  }
-
-  bool fileExists(const std::string& path) override {
-    checkEnabled();
-
-    try {
-      DownloadRequest request(cacheUri + "/" + path);
-      request.head = true;
-      getDownloader()->download(request);
-      return true;
-    } catch (DownloadError& e) {
-      /* S3 buckets return 403 if a file doesn't exist and the
-         bucket is unlistable, so treat 403 as 404. */
-      if (e.error == Downloader::NotFound || e.error == Downloader::Forbidden) {
-        return false;
-      }
-      maybeDisable();
-      throw;
-    }
-  }
-
-  void upsertFile(const std::string& path, const std::string& data,
-                  const std::string& mimeType) override {
-    auto req = DownloadRequest(cacheUri + "/" + path);
-    req.data = std::make_shared<std::string>(data);  // FIXME: inefficient
-    req.mimeType = mimeType;
-    try {
-      getDownloader()->download(req);
-    } catch (DownloadError& e) {
-      throw UploadToHTTP("while uploading to HTTP binary cache at '%s': %s",
-                         cacheUri, e.msg());
-    }
-  }
-
-  DownloadRequest makeRequest(const std::string& path) {
-    DownloadRequest request(cacheUri + "/" + path);
-    return request;
-  }
-
-  void getFile(const std::string& path, Sink& sink) override {
-    checkEnabled();
-    auto request(makeRequest(path));
-    try {
-      getDownloader()->download(std::move(request), sink);
-    } catch (DownloadError& e) {
-      if (e.error == Downloader::NotFound || e.error == Downloader::Forbidden) {
-        throw NoSuchBinaryCacheFile(
-            "file '%s' does not exist in binary cache '%s'", path, getUri());
-      }
-      maybeDisable();
-      throw;
-    }
-  }
-
-  void getFile(
-      const std::string& path,
-      Callback<std::shared_ptr<std::string>> callback) noexcept override {
-    checkEnabled();
-
-    auto request(makeRequest(path));
-
-    auto callbackPtr =
-        std::make_shared<decltype(callback)>(std::move(callback));
-
-    getDownloader()->enqueueDownload(
-        request,
-        Callback<DownloadResult>{
-            [callbackPtr, this](std::future<DownloadResult> result) {
-              try {
-                (*callbackPtr)(result.get().data);
-              } catch (DownloadError& e) {
-                if (e.error == Downloader::NotFound ||
-                    e.error == Downloader::Forbidden) {
-                  return (*callbackPtr)(std::shared_ptr<std::string>());
-                }
-                maybeDisable();
-                callbackPtr->rethrow();
-              } catch (...) {
-                callbackPtr->rethrow();
-              }
-            }});
-  }
-};
-
-static RegisterStoreImplementation regStore(
-    [](const std::string& uri,
-       const Store::Params& params) -> std::shared_ptr<Store> {
-      if (std::string(uri, 0, 7) != "http://" &&
-          std::string(uri, 0, 8) != "https://" &&
-          (getEnv("_NIX_FORCE_HTTP_BINARY_CACHE_STORE") != "1" ||
-           std::string(uri, 0, 7) != "file://")) {
-        return nullptr;
-      }
-      auto store = std::make_shared<HttpBinaryCacheStore>(params, uri);
-      store->init();
-      return store;
-    });
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/legacy-ssh-store.cc b/third_party/nix/src/libstore/legacy-ssh-store.cc
deleted file mode 100644
index 8163258179..0000000000
--- a/third_party/nix/src/libstore/legacy-ssh-store.cc
+++ /dev/null
@@ -1,282 +0,0 @@
-#include <absl/strings/match.h>
-#include <absl/strings/str_cat.h>
-#include <glog/logging.h>
-
-#include "libstore/derivations.hh"
-#include "libstore/remote-store.hh"
-#include "libstore/serve-protocol.hh"
-#include "libstore/ssh.hh"
-#include "libstore/store-api.hh"
-#include "libstore/worker-protocol.hh"
-#include "libutil/archive.hh"
-#include "libutil/pool.hh"
-
-namespace nix {
-
-constexpr std::string_view kUriScheme = "ssh://";
-
-struct LegacySSHStore : public Store {
-  const Setting<int> maxConnections{
-      this, 1, "max-connections",
-      "maximum number of concurrent SSH connections"};
-  const Setting<Path> sshKey{this, "", "ssh-key", "path to an SSH private key"};
-  const Setting<bool> compress{this, false, "compress",
-                               "whether to compress the connection"};
-  const Setting<Path> remoteProgram{
-      this, "nix-store", "remote-program",
-      "path to the nix-store executable on the remote system"};
-  const Setting<std::string> remoteStore{
-      this, "", "remote-store", "URI of the store on the remote system"};
-
-  // Hack for getting remote build log output.
-  const Setting<int> logFD{
-      this, -1, "log-fd", "file descriptor to which SSH's stderr is connected"};
-
-  struct Connection {
-    std::unique_ptr<SSHMaster::Connection> sshConn;
-    FdSink to;
-    FdSource from;
-    int remoteVersion;
-    bool good = true;
-  };
-
-  std::string host;
-
-  ref<Pool<Connection>> connections;
-
-  SSHMaster master;
-
-  LegacySSHStore(const std::string& host, const Params& params)
-      : Store(params),
-        host(host),
-        connections(make_ref<Pool<Connection>>(
-            std::max(1, (int)maxConnections),
-            [this]() { return openConnection(); },
-            [](const ref<Connection>& r) { return r->good; })),
-        master(host, sshKey,
-               // Use SSH master only if using more than 1 connection.
-               connections->capacity() > 1, compress, logFD) {}
-
-  ref<Connection> openConnection() {
-    auto conn = make_ref<Connection>();
-    conn->sshConn = master.startCommand(
-        fmt("%s --serve --write", remoteProgram) +
-        (remoteStore.get().empty()
-             ? ""
-             : " --store " + shellEscape(remoteStore.get())));
-    conn->to = FdSink(conn->sshConn->in.get());
-    conn->from = FdSource(conn->sshConn->out.get());
-
-    try {
-      conn->to << SERVE_MAGIC_1 << SERVE_PROTOCOL_VERSION;
-      conn->to.flush();
-
-      unsigned int magic = readInt(conn->from);
-      if (magic != SERVE_MAGIC_2) {
-        throw Error("protocol mismatch with 'nix-store --serve' on '%s'", host);
-      }
-      conn->remoteVersion = readInt(conn->from);
-      if (GET_PROTOCOL_MAJOR(conn->remoteVersion) != 0x200) {
-        throw Error("unsupported 'nix-store --serve' protocol version on '%s'",
-                    host);
-      }
-
-    } catch (EndOfFile& e) {
-      throw Error("cannot connect to '%1%'", host);
-    }
-
-    return conn;
-  };
-
-  std::string getUri() override { return absl::StrCat(kUriScheme, host); }
-
-  void queryPathInfoUncached(
-      const Path& path,
-      Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept override {
-    try {
-      auto conn(connections->get());
-
-      DLOG(INFO) << "querying remote host '" << host << "' for info on '"
-                 << path << "'";
-
-      conn->to << cmdQueryPathInfos << PathSet{path};
-      conn->to.flush();
-
-      auto info = std::make_shared<ValidPathInfo>();
-      conn->from >> info->path;
-      if (info->path.empty()) {
-        return callback(nullptr);
-      }
-      assert(path == info->path);
-
-      PathSet references;
-      conn->from >> info->deriver;
-      info->references = readStorePaths<PathSet>(*this, conn->from);
-      readLongLong(conn->from);  // download size
-      info->narSize = readLongLong(conn->from);
-
-      if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 4) {
-        auto s = readString(conn->from);
-        if (s.empty()) {
-          info->narHash = Hash();
-        } else {
-          auto hash_ = Hash::deserialize(s);
-          info->narHash = Hash::unwrap_throw(hash_);
-        }
-        conn->from >> info->ca;
-        info->sigs = readStrings<StringSet>(conn->from);
-      }
-
-      auto s = readString(conn->from);
-      assert(s.empty());
-
-      callback(std::move(info));
-    } catch (...) {
-      callback.rethrow();
-    }
-  }
-
-  void addToStore(const ValidPathInfo& info, Source& source, RepairFlag repair,
-                  CheckSigsFlag checkSigs,
-                  std::shared_ptr<FSAccessor> accessor) override {
-    DLOG(INFO) << "adding path '" << info.path << "' to remote host '" << host
-               << "'";
-
-    auto conn(connections->get());
-
-    if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 5) {
-      conn->to << cmdAddToStoreNar << info.path << info.deriver
-               << info.narHash.to_string(Base16, false) << info.references
-               << info.registrationTime << info.narSize
-               << static_cast<uint64_t>(info.ultimate) << info.sigs << info.ca;
-      try {
-        copyNAR(source, conn->to);
-      } catch (...) {
-        conn->good = false;
-        throw;
-      }
-      conn->to.flush();
-
-    } else {
-      conn->to << cmdImportPaths << 1;
-      try {
-        copyNAR(source, conn->to);
-      } catch (...) {
-        conn->good = false;
-        throw;
-      }
-      conn->to << exportMagic << info.path << info.references << info.deriver
-               << 0 << 0;
-      conn->to.flush();
-    }
-
-    if (readInt(conn->from) != 1) {
-      throw Error(
-          "failed to add path '%s' to remote host '%s', info.path, host");
-    }
-  }
-
-  void narFromPath(const Path& path, Sink& sink) override {
-    auto conn(connections->get());
-
-    conn->to << cmdDumpStorePath << path;
-    conn->to.flush();
-    copyNAR(conn->from, sink);
-  }
-
-  Path queryPathFromHashPart(const std::string& hashPart) override {
-    unsupported("queryPathFromHashPart");
-  }
-
-  Path addToStore(const std::string& name, const Path& srcPath, bool recursive,
-                  HashType hashAlgo, PathFilter& filter,
-                  RepairFlag repair) override {
-    unsupported("addToStore");
-  }
-
-  Path addTextToStore(const std::string& name, const std::string& s,
-                      const PathSet& references, RepairFlag repair) override {
-    unsupported("addTextToStore");
-  }
-
-  BuildResult buildDerivation(std::ostream& /*log_sink*/, const Path& drvPath,
-                              const BasicDerivation& drv,
-                              BuildMode buildMode) override {
-    auto conn(connections->get());
-
-    conn->to << cmdBuildDerivation << drvPath << drv << settings.maxSilentTime
-             << settings.buildTimeout;
-    if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 2) {
-      conn->to << settings.maxLogSize;
-    }
-    if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 3) {
-      conn->to << settings.buildRepeat
-               << static_cast<uint64_t>(settings.enforceDeterminism);
-    }
-
-    conn->to.flush();
-
-    BuildResult status;
-    status.status = static_cast<BuildResult::Status>(readInt(conn->from));
-    conn->from >> status.errorMsg;
-
-    if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 3) {
-      conn->from >> status.timesBuilt >> status.isNonDeterministic >>
-          status.startTime >> status.stopTime;
-    }
-
-    return status;
-  }
-
-  void ensurePath(const Path& path) override { unsupported("ensurePath"); }
-
-  void computeFSClosure(const PathSet& paths, PathSet& out,
-                        bool flipDirection = false, bool includeOutputs = false,
-                        bool includeDerivers = false) override {
-    if (flipDirection || includeDerivers) {
-      Store::computeFSClosure(paths, out, flipDirection, includeOutputs,
-                              includeDerivers);
-      return;
-    }
-
-    auto conn(connections->get());
-
-    conn->to << cmdQueryClosure << static_cast<uint64_t>(includeOutputs)
-             << paths;
-    conn->to.flush();
-
-    auto res = readStorePaths<PathSet>(*this, conn->from);
-
-    out.insert(res.begin(), res.end());
-  }
-
-  PathSet queryValidPaths(const PathSet& paths, SubstituteFlag maybeSubstitute =
-                                                    NoSubstitute) override {
-    auto conn(connections->get());
-
-    conn->to << cmdQueryValidPaths << 0u  // lock
-             << maybeSubstitute << paths;
-    conn->to.flush();
-
-    return readStorePaths<PathSet>(*this, conn->from);
-  }
-
-  void connect() override { auto conn(connections->get()); }
-
-  unsigned int getProtocol() override {
-    auto conn(connections->get());
-    return conn->remoteVersion;
-  }
-};
-
-static RegisterStoreImplementation regStore(
-    [](const std::string& uri,
-       const Store::Params& params) -> std::shared_ptr<Store> {
-      if (!absl::StartsWith(uri, kUriScheme)) {
-        return nullptr;
-      }
-      return std::make_shared<LegacySSHStore>(
-          std::string(uri, kUriScheme.size()), params);
-    });
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/local-binary-cache-store.cc b/third_party/nix/src/libstore/local-binary-cache-store.cc
deleted file mode 100644
index 4555de5047..0000000000
--- a/third_party/nix/src/libstore/local-binary-cache-store.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-#include <utility>
-
-#include <absl/strings/match.h>
-
-#include "libstore/binary-cache-store.hh"
-#include "libstore/globals.hh"
-#include "libstore/nar-info-disk-cache.hh"
-
-namespace nix {
-
-class LocalBinaryCacheStore : public BinaryCacheStore {
- private:
-  Path binaryCacheDir;
-
- public:
-  LocalBinaryCacheStore(const Params& params, Path binaryCacheDir)
-      : BinaryCacheStore(params), binaryCacheDir(std::move(binaryCacheDir)) {}
-
-  void init() override;
-
-  std::string getUri() override { return "file://" + binaryCacheDir; }
-
- protected:
-  bool fileExists(const std::string& path) override;
-
-  void upsertFile(const std::string& path, const std::string& data,
-                  const std::string& mimeType) override;
-
-  void getFile(const std::string& path, Sink& sink) override {
-    try {
-      readFile(binaryCacheDir + "/" + path, sink);
-    } catch (SysError& e) {
-      if (e.errNo == ENOENT) {
-        throw NoSuchBinaryCacheFile("file '%s' does not exist in binary cache",
-                                    path);
-      }
-    }
-  }
-
-  PathSet queryAllValidPaths() override {
-    PathSet paths;
-
-    for (auto& entry : readDirectory(binaryCacheDir)) {
-      if (entry.name.size() != 40 || !absl::EndsWith(entry.name, ".narinfo")) {
-        continue;
-      }
-      paths.insert(storeDir + "/" +
-                   entry.name.substr(0, entry.name.size() - 8));
-    }
-
-    return paths;
-  }
-};
-
-void LocalBinaryCacheStore::init() {
-  createDirs(binaryCacheDir + "/nar");
-  BinaryCacheStore::init();
-}
-
-static void atomicWrite(const Path& path, const std::string& s) {
-  Path tmp = path + ".tmp." + std::to_string(getpid());
-  AutoDelete del(tmp, false);
-  writeFile(tmp, s);
-  if (rename(tmp.c_str(), path.c_str()) != 0) {
-    throw SysError(format("renaming '%1%' to '%2%'") % tmp % path);
-  }
-  del.cancel();
-}
-
-bool LocalBinaryCacheStore::fileExists(const std::string& path) {
-  return pathExists(binaryCacheDir + "/" + path);
-}
-
-void LocalBinaryCacheStore::upsertFile(const std::string& path,
-                                       const std::string& data,
-                                       const std::string& mimeType) {
-  atomicWrite(binaryCacheDir + "/" + path, data);
-}
-
-static RegisterStoreImplementation regStore(
-    [](const std::string& uri,
-       const Store::Params& params) -> std::shared_ptr<Store> {
-      if (getEnv("_NIX_FORCE_HTTP_BINARY_CACHE_STORE") == "1" ||
-          std::string(uri, 0, 7) != "file://") {
-        return nullptr;
-      }
-      auto store =
-          std::make_shared<LocalBinaryCacheStore>(params, std::string(uri, 7));
-      store->init();
-      return store;
-    });
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/local-fs-store.cc b/third_party/nix/src/libstore/local-fs-store.cc
deleted file mode 100644
index f2235bad76..0000000000
--- a/third_party/nix/src/libstore/local-fs-store.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-#include "libstore/derivations.hh"
-#include "libstore/fs-accessor.hh"
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libutil/archive.hh"
-#include "libutil/compression.hh"
-
-namespace nix {
-
-LocalFSStore::LocalFSStore(const Params& params) : Store(params) {}
-
-struct LocalStoreAccessor : public FSAccessor {
-  ref<LocalFSStore> store;
-
-  explicit LocalStoreAccessor(const ref<LocalFSStore>& store) : store(store) {}
-
-  Path toRealPath(const Path& path) {
-    Path storePath = store->toStorePath(path);
-    if (!store->isValidPath(storePath)) {
-      throw InvalidPath(format("path '%1%' is not a valid store path") %
-                        storePath);
-    }
-    return store->getRealStoreDir() + std::string(path, store->storeDir.size());
-  }
-
-  FSAccessor::Stat stat(const Path& path) override {
-    auto realPath = toRealPath(path);
-
-    struct stat st;
-    if (lstat(realPath.c_str(), &st) != 0) {
-      if (errno == ENOENT || errno == ENOTDIR) {
-        return {Type::tMissing, 0, false};
-      }
-      throw SysError(format("getting status of '%1%'") % path);
-    }
-
-    if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode)) {
-      throw Error(format("file '%1%' has unsupported type") % path);
-    }
-
-    return {S_ISREG(st.st_mode)   ? Type::tRegular
-            : S_ISLNK(st.st_mode) ? Type::tSymlink
-                                  : Type::tDirectory,
-            S_ISREG(st.st_mode) ? static_cast<uint64_t>(st.st_size) : 0,
-            S_ISREG(st.st_mode) && ((st.st_mode & S_IXUSR) != 0u)};
-  }
-
-  StringSet readDirectory(const Path& path) override {
-    auto realPath = toRealPath(path);
-
-    auto entries = nix::readDirectory(realPath);
-
-    StringSet res;
-    for (auto& entry : entries) {
-      res.insert(entry.name);
-    }
-
-    return res;
-  }
-
-  std::string readFile(const Path& path) override {
-    return nix::readFile(toRealPath(path));
-  }
-
-  std::string readLink(const Path& path) override {
-    return nix::readLink(toRealPath(path));
-  }
-};
-
-ref<FSAccessor> LocalFSStore::getFSAccessor() {
-  return make_ref<LocalStoreAccessor>(ref<LocalFSStore>(
-      std::dynamic_pointer_cast<LocalFSStore>(shared_from_this())));
-}
-
-void LocalFSStore::narFromPath(const Path& path, Sink& sink) {
-  if (!isValidPath(path)) {
-    throw Error(format("path '%s' is not valid") % path);
-  }
-  dumpPath(getRealStoreDir() + std::string(path, storeDir.size()), sink);
-}
-
-const std::string LocalFSStore::drvsLogDir = "drvs";
-
-std::shared_ptr<std::string> LocalFSStore::getBuildLog(const Path& path_) {
-  auto path(path_);
-
-  assertStorePath(path);
-
-  if (!isDerivation(path)) {
-    try {
-      path = queryPathInfo(path)->deriver;
-    } catch (InvalidPath&) {
-      return nullptr;
-    }
-    if (path.empty()) {
-      return nullptr;
-    }
-  }
-
-  std::string baseName = baseNameOf(path);
-
-  for (int j = 0; j < 2; j++) {
-    Path logPath =
-        j == 0 ? fmt("%s/%s/%s/%s", logDir, drvsLogDir,
-                     std::string(baseName, 0, 2), std::string(baseName, 2))
-               : fmt("%s/%s/%s", logDir, drvsLogDir, baseName);
-    Path logBz2Path = logPath + ".bz2";
-
-    if (pathExists(logPath)) {
-      return std::make_shared<std::string>(readFile(logPath));
-    }
-    if (pathExists(logBz2Path)) {
-      try {
-        return decompress("bzip2", readFile(logBz2Path));
-      } catch (Error&) {
-      }
-    }
-  }
-
-  return nullptr;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/local-store.cc b/third_party/nix/src/libstore/local-store.cc
deleted file mode 100644
index aca305e1a5..0000000000
--- a/third_party/nix/src/libstore/local-store.cc
+++ /dev/null
@@ -1,1519 +0,0 @@
-#include "libstore/local-store.hh"
-
-#include <algorithm>
-#include <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <ctime>
-#include <iostream>
-
-#include <absl/strings/numbers.h>
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_split.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <grp.h>
-#include <sched.h>
-#include <sqlite3.h>
-#include <sys/ioctl.h>
-#include <sys/mount.h>
-#include <sys/select.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/xattr.h>
-#include <unistd.h>
-#include <utime.h>
-
-#include "generated/schema.sql.hh"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/nar-info.hh"
-#include "libstore/pathlocks.hh"
-#include "libstore/worker-protocol.hh"
-#include "libutil/archive.hh"
-
-namespace nix {
-
-LocalStore::LocalStore(const Params& params)
-    : Store(params),
-      LocalFSStore(params),
-      realStoreDir_{this, false,
-                    rootDir != "" ? rootDir + "/nix/store" : storeDir, "real",
-                    "physical path to the Nix store"},
-      realStoreDir(realStoreDir_),
-      dbDir(stateDir + "/db"),
-      linksDir(realStoreDir + "/.links"),
-      reservedPath(dbDir + "/reserved"),
-      schemaPath(dbDir + "/schema"),
-      trashDir(realStoreDir + "/trash"),
-      tempRootsDir(stateDir + "/temproots"),
-      fnTempRoots(fmt("%s/%d", tempRootsDir, getpid())) {
-  auto state(_state.lock());
-
-  /* Create missing state directories if they don't already exist. */
-  createDirs(realStoreDir);
-  makeStoreWritable();
-  createDirs(linksDir);
-  Path profilesDir = stateDir + "/profiles";
-  createDirs(profilesDir);
-  createDirs(tempRootsDir);
-  createDirs(dbDir);
-  Path gcRootsDir = stateDir + "/gcroots";
-  if (!pathExists(gcRootsDir)) {
-    createDirs(gcRootsDir);
-    createSymlink(profilesDir, gcRootsDir + "/profiles");
-  }
-
-  for (auto& perUserDir :
-       {profilesDir + "/per-user", gcRootsDir + "/per-user"}) {
-    createDirs(perUserDir);
-    if (chmod(perUserDir.c_str(), 0755) == -1) {
-      throw SysError("could not set permissions on '%s' to 755", perUserDir);
-    }
-  }
-
-  // TODO(kanepyork): migrate to external constructor, this bypasses virtual
-  // dispatch
-  // NOLINTNEXTLINE clang-analyzer-optin.cplusplus.VirtualCall
-  createUser(getUserName(), getuid());
-
-  /* Optionally, create directories and set permissions for a
-     multi-user install. */
-  if (getuid() == 0 && settings.buildUsersGroup != "") {
-    mode_t perm = 01775;
-
-    struct group* gr = getgrnam(settings.buildUsersGroup.get().c_str());
-    if (gr == nullptr) {
-      LOG(ERROR) << "warning: the group '" << settings.buildUsersGroup
-                 << "' specified in 'build-users-group' does not exist";
-    } else {
-      struct stat st;
-      if (stat(realStoreDir.c_str(), &st) != 0) {
-        throw SysError(format("getting attributes of path '%1%'") %
-                       realStoreDir);
-      }
-
-      if (st.st_uid != 0 || st.st_gid != gr->gr_gid ||
-          (st.st_mode & ~S_IFMT) != perm) {
-        if (chown(realStoreDir.c_str(), 0, gr->gr_gid) == -1) {
-          throw SysError(format("changing ownership of path '%1%'") %
-                         realStoreDir);
-        }
-        if (chmod(realStoreDir.c_str(), perm) == -1) {
-          throw SysError(format("changing permissions on path '%1%'") %
-                         realStoreDir);
-        }
-      }
-    }
-  }
-
-  /* Ensure that the store and its parents are not symlinks. */
-  if (getEnv("NIX_IGNORE_SYMLINK_STORE") != "1") {
-    Path path = realStoreDir;
-    struct stat st;
-    while (path != "/") {
-      if (lstat(path.c_str(), &st) != 0) {
-        throw SysError(format("getting status of '%1%'") % path);
-      }
-      if (S_ISLNK(st.st_mode)) {
-        throw Error(format("the path '%1%' is a symlink; "
-                           "this is not allowed for the Nix store and its "
-                           "parent directories") %
-                    path);
-      }
-      path = dirOf(path);
-    }
-  }
-
-  /* We can't open a SQLite database if the disk is full.  Since
-     this prevents the garbage collector from running when it's most
-     needed, we reserve some dummy space that we can free just
-     before doing a garbage collection. */
-  try {
-    struct stat st;
-    if (stat(reservedPath.c_str(), &st) == -1 ||
-        st.st_size != settings.reservedSize) {
-      AutoCloseFD fd(
-          open(reservedPath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600));
-      int res = -1;
-#if HAVE_POSIX_FALLOCATE
-      res = posix_fallocate(fd.get(), 0, settings.reservedSize);
-#endif
-      if (res == -1) {
-        writeFull(fd.get(), std::string(settings.reservedSize, 'X'));
-        [[gnu::unused]] auto res2 = ftruncate(fd.get(), settings.reservedSize);
-      }
-    }
-  } catch (SysError& e) { /* don't care about errors */
-  }
-
-  /* Acquire the big fat lock in shared mode to make sure that no
-     schema upgrade is in progress. */
-  Path globalLockPath = dbDir + "/big-lock";
-  globalLock = openLockFile(globalLockPath, true);
-
-  if (!lockFile(globalLock.get(), ltRead, false)) {
-    LOG(INFO) << "waiting for the big Nix store lock...";
-    lockFile(globalLock.get(), ltRead, true);
-  }
-
-  /* Check the current database schema and if necessary do an
-     upgrade.  */
-  int curSchema = getSchema();
-  if (curSchema > nixSchemaVersion) {
-    throw Error(
-        format(
-            "current Nix store schema is version %1%, but I only support %2%") %
-        curSchema % nixSchemaVersion);
-  }
-  if (curSchema == 0) { /* new store */
-    curSchema = nixSchemaVersion;
-    openDB(*state, true);
-    writeFile(schemaPath, (format("%1%") % curSchema).str());
-  } else if (curSchema < nixSchemaVersion) {
-    if (curSchema < 5) {
-      throw Error(
-          "Your Nix store has a database in Berkeley DB format,\n"
-          "which is no longer supported. To convert to the new format,\n"
-          "please upgrade Nix to version 0.12 first.");
-    }
-
-    if (curSchema < 6) {
-      throw Error(
-          "Your Nix store has a database in flat file format,\n"
-          "which is no longer supported. To convert to the new format,\n"
-          "please upgrade Nix to version 1.11 first.");
-    }
-
-    if (!lockFile(globalLock.get(), ltWrite, false)) {
-      LOG(INFO) << "waiting for exclusive access to the Nix store...";
-      lockFile(globalLock.get(), ltWrite, true);
-    }
-
-    /* Get the schema version again, because another process may
-       have performed the upgrade already. */
-    curSchema = getSchema();
-
-    if (curSchema < 7) {
-      upgradeStore7();
-    }
-
-    openDB(*state, false);
-
-    if (curSchema < 8) {
-      SQLiteTxn txn(state->db);
-      state->db.exec("alter table ValidPaths add column ultimate integer");
-      state->db.exec("alter table ValidPaths add column sigs text");
-      txn.commit();
-    }
-
-    if (curSchema < 9) {
-      SQLiteTxn txn(state->db);
-      state->db.exec("drop table FailedPaths");
-      txn.commit();
-    }
-
-    if (curSchema < 10) {
-      SQLiteTxn txn(state->db);
-      state->db.exec("alter table ValidPaths add column ca text");
-      txn.commit();
-    }
-
-    writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
-
-    lockFile(globalLock.get(), ltRead, true);
-  } else {
-    openDB(*state, false);
-  }
-
-  /* Prepare SQL statements. */
-  state->stmtRegisterValidPath.create(
-      state->db,
-      "insert into ValidPaths (path, hash, registrationTime, deriver, narSize, "
-      "ultimate, sigs, ca) values (?, ?, ?, ?, ?, ?, ?, ?);");
-  state->stmtUpdatePathInfo.create(
-      state->db,
-      "update ValidPaths set narSize = ?, hash = ?, ultimate = ?, sigs = ?, ca "
-      "= ? where path = ?;");
-  state->stmtAddReference.create(
-      state->db,
-      "insert or replace into Refs (referrer, reference) values (?, ?);");
-  state->stmtQueryPathInfo.create(
-      state->db,
-      "select id, hash, registrationTime, deriver, narSize, ultimate, sigs, ca "
-      "from ValidPaths where path = ?;");
-  state->stmtQueryReferences.create(state->db,
-                                    "select path from Refs join ValidPaths on "
-                                    "reference = id where referrer = ?;");
-  state->stmtQueryReferrers.create(
-      state->db,
-      "select path from Refs join ValidPaths on referrer = id where reference "
-      "= (select id from ValidPaths where path = ?);");
-  state->stmtInvalidatePath.create(state->db,
-                                   "delete from ValidPaths where path = ?;");
-  state->stmtAddDerivationOutput.create(
-      state->db,
-      "insert or replace into DerivationOutputs (drv, id, path) values (?, ?, "
-      "?);");
-  state->stmtQueryValidDerivers.create(
-      state->db,
-      "select v.id, v.path from DerivationOutputs d join ValidPaths v on d.drv "
-      "= v.id where d.path = ?;");
-  state->stmtQueryDerivationOutputs.create(
-      state->db, "select id, path from DerivationOutputs where drv = ?;");
-  // Use "path >= ?" with limit 1 rather than "path like '?%'" to
-  // ensure efficient lookup.
-  state->stmtQueryPathFromHashPart.create(
-      state->db, "select path from ValidPaths where path >= ? limit 1;");
-  state->stmtQueryValidPaths.create(state->db, "select path from ValidPaths");
-}
-
-LocalStore::~LocalStore() {
-  std::shared_future<void> future;
-
-  {
-    auto state(_state.lock());
-    if (state->gcRunning) {
-      future = state->gcFuture;
-    }
-  }
-
-  if (future.valid()) {
-    LOG(INFO) << "waiting for auto-GC to finish on exit...";
-    future.get();
-  }
-
-  try {
-    auto state(_state.lock());
-    if (state->fdTempRoots) {
-      state->fdTempRoots = AutoCloseFD(-1);
-      unlink(fnTempRoots.c_str());
-    }
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-std::string LocalStore::getUri() { return "local"; }
-
-int LocalStore::getSchema() {
-  int curSchema = 0;
-  if (pathExists(schemaPath)) {
-    std::string s = readFile(schemaPath);
-    if (!absl::SimpleAtoi(s, &curSchema)) {
-      throw Error(format("'%1%' is corrupt") % schemaPath);
-    }
-  }
-  return curSchema;
-}
-
-void LocalStore::openDB(State& state, bool create) {
-  if (access(dbDir.c_str(), R_OK | W_OK) != 0) {
-    throw SysError(format("Nix database directory '%1%' is not writable") %
-                   dbDir);
-  }
-
-  /* Open the Nix database. */
-  std::string dbPath = dbDir + "/db.sqlite";
-  auto& db(state.db);
-  if (sqlite3_open_v2(dbPath.c_str(), &db.db,
-                      SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0),
-                      nullptr) != SQLITE_OK) {
-    throw Error(format("cannot open Nix database '%1%'") % dbPath);
-  }
-
-  if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK) {
-    throwSQLiteError(db, "setting timeout");
-  }
-
-  db.exec("pragma foreign_keys = 1");
-
-  /* !!! check whether sqlite has been built with foreign key
-     support */
-
-  /* Whether SQLite should fsync().  "Normal" synchronous mode
-     should be safe enough.  If the user asks for it, don't sync at
-     all.  This can cause database corruption if the system
-     crashes. */
-  std::string syncMode = settings.fsyncMetadata ? "normal" : "off";
-  db.exec("pragma synchronous = " + syncMode);
-
-  /* Set the SQLite journal mode.  WAL mode is fastest, so it's the
-     default. */
-  std::string mode = settings.useSQLiteWAL ? "wal" : "truncate";
-  std::string prevMode;
-  {
-    SQLiteStmt stmt;
-    stmt.create(db, "pragma main.journal_mode;");
-    if (sqlite3_step(stmt) != SQLITE_ROW) {
-      throwSQLiteError(db, "querying journal mode");
-    }
-    prevMode = std::string(
-        reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)));
-  }
-  if (prevMode != mode &&
-      sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(),
-                   nullptr, nullptr, nullptr) != SQLITE_OK) {
-    throwSQLiteError(db, "setting journal mode");
-  }
-
-  /* Increase the auto-checkpoint interval to 40000 pages.  This
-     seems enough to ensure that instantiating the NixOS system
-     derivation is done in a single fsync(). */
-  if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;",
-                                    nullptr, nullptr, nullptr) != SQLITE_OK) {
-    throwSQLiteError(db, "setting autocheckpoint interval");
-  }
-
-  /* Initialise the database schema, if necessary. */
-  if (create) {
-    db.exec(kNixSqlSchema);
-  }
-}
-
-/* To improve purity, users may want to make the Nix store a read-only
-   bind mount.  So make the Nix store writable for this process. */
-void LocalStore::makeStoreWritable() {
-  if (getuid() != 0) {
-    return;
-  }
-  /* Check if /nix/store is on a read-only mount. */
-  struct statvfs stat;
-  if (statvfs(realStoreDir.c_str(), &stat) != 0) {
-    throw SysError("getting info about the Nix store mount point");
-  }
-
-  if ((stat.f_flag & ST_RDONLY) != 0u) {
-    if (unshare(CLONE_NEWNS) == -1) {
-      throw SysError("setting up a private mount namespace");
-    }
-
-    if (mount(nullptr, realStoreDir.c_str(), "none", MS_REMOUNT | MS_BIND,
-              nullptr) == -1) {
-      throw SysError(format("remounting %1% writable") % realStoreDir);
-    }
-  }
-}
-
-const time_t mtimeStore = 1; /* 1 second into the epoch */
-
-static void canonicaliseTimestampAndPermissions(const Path& path,
-                                                const struct stat& st) {
-  if (!S_ISLNK(st.st_mode)) {
-    /* Mask out all type related bits. */
-    mode_t mode = st.st_mode & ~S_IFMT;
-
-    if (mode != 0444 && mode != 0555) {
-      mode = (st.st_mode & S_IFMT) | 0444 |
-             ((st.st_mode & S_IXUSR) != 0u ? 0111 : 0);
-      if (chmod(path.c_str(), mode) == -1) {
-        throw SysError(format("changing mode of '%1%' to %2$o") % path % mode);
-      }
-    }
-  }
-
-  if (st.st_mtime != mtimeStore) {
-    struct timeval times[2];
-    times[0].tv_sec = st.st_atime;
-    times[0].tv_usec = 0;
-    times[1].tv_sec = mtimeStore;
-    times[1].tv_usec = 0;
-#if HAVE_LUTIMES
-    if (lutimes(path.c_str(), times) == -1) {
-      if (errno != ENOSYS ||
-          (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)) {
-#else
-    if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1) {
-#endif
-        throw SysError(format("changing modification time of '%1%'") % path);
-      }
-    }
-  }  // namespace nix
-}  // namespace nix
-
-void canonicaliseTimestampAndPermissions(const Path& path) {
-  struct stat st;
-  if (lstat(path.c_str(), &st) != 0) {
-    throw SysError(format("getting attributes of path '%1%'") % path);
-  }
-  canonicaliseTimestampAndPermissions(path, st);
-}
-
-static void canonicalisePathMetaData_(const Path& path, uid_t fromUid,
-                                      InodesSeen& inodesSeen) {
-  checkInterrupt();
-
-  struct stat st;
-  if (lstat(path.c_str(), &st) != 0) {
-    throw SysError(format("getting attributes of path '%1%'") % path);
-  }
-
-  /* Really make sure that the path is of a supported type. */
-  if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) {
-    throw Error(format("file '%1%' has an unsupported type") % path);
-  }
-
-  /* Remove extended attributes / ACLs. */
-  ssize_t eaSize = llistxattr(path.c_str(), nullptr, 0);
-
-  if (eaSize < 0) {
-    if (errno != ENOTSUP && errno != ENODATA) {
-      throw SysError("querying extended attributes of '%s'", path);
-    }
-  } else if (eaSize > 0) {
-    std::vector<char> eaBuf(eaSize);
-
-    if ((eaSize = llistxattr(path.c_str(), eaBuf.data(), eaBuf.size())) < 0) {
-      throw SysError("querying extended attributes of '%s'", path);
-    }
-
-    for (auto& eaName : absl::StrSplit(std::string(eaBuf.data(), eaSize),
-                                       absl::ByString(std::string("\000", 1)),
-                                       absl::SkipEmpty())) {
-      /* Ignore SELinux security labels since these cannot be
-         removed even by root. */
-      if (eaName == "security.selinux") {
-        continue;
-      }
-      if (lremovexattr(path.c_str(), std::string(eaName).c_str()) == -1) {
-        throw SysError("removing extended attribute '%s' from '%s'", eaName,
-                       path);
-      }
-    }
-  }
-
-  /* Fail if the file is not owned by the build user.  This prevents
-     us from messing up the ownership/permissions of files
-     hard-linked into the output (e.g. "ln /etc/shadow $out/foo").
-     However, ignore files that we chown'ed ourselves previously to
-     ensure that we don't fail on hard links within the same build
-     (i.e. "touch $out/foo; ln $out/foo $out/bar"). */
-  if (fromUid != static_cast<uid_t>(-1) && st.st_uid != fromUid) {
-    if (S_ISDIR(st.st_mode)) {
-      throw BuildError(format("invalid file '%1%': is a directory") % path);
-    }
-    if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end()) {
-      throw BuildError(format("invalid ownership on file '%1%'") % path);
-    }
-    if (!(S_ISLNK(st.st_mode) ||
-          (st.st_uid == geteuid() &&
-           ((st.st_mode & ~S_IFMT) == 0444 || (st.st_mode & ~S_IFMT) == 0555) &&
-           st.st_mtime == mtimeStore))) {
-      throw BuildError(
-          format("invalid permissions on file '%1%', should be 0444/0555") %
-          path);
-    }
-
-    return;
-  }
-
-  inodesSeen.insert(Inode(st.st_dev, st.st_ino));
-
-  canonicaliseTimestampAndPermissions(path, st);
-
-  /* Change ownership to the current uid.  If it's a symlink, use
-     lchown if available, otherwise don't bother.  Wrong ownership
-     of a symlink doesn't matter, since the owning user can't change
-     the symlink and can't delete it because the directory is not
-     writable.  The only exception is top-level paths in the Nix
-     store (since that directory is group-writable for the Nix build
-     users group); we check for this case below. */
-  if (st.st_uid != geteuid()) {
-#if HAVE_LCHOWN
-    if (lchown(path.c_str(), geteuid(), getegid()) == -1) {
-#else
-    if (!S_ISLNK(st.st_mode) && chown(path.c_str(), geteuid(), getegid()) == -1)
-#endif
-      throw SysError(format("changing owner of '%1%' to %2%") % path %
-                     geteuid());
-    }
-  }
-
-  if (S_ISDIR(st.st_mode)) {
-    DirEntries entries = readDirectory(path);
-    for (auto& i : entries) {
-      canonicalisePathMetaData_(path + "/" + i.name, fromUid, inodesSeen);
-    }
-  }
-}
-
-void canonicalisePathMetaData(const Path& path, uid_t fromUid,
-                              InodesSeen& inodesSeen) {
-  canonicalisePathMetaData_(path, fromUid, inodesSeen);
-
-  /* On platforms that don't have lchown(), the top-level path can't
-     be a symlink, since we can't change its ownership. */
-  struct stat st;
-  if (lstat(path.c_str(), &st) != 0) {
-    throw SysError(format("getting attributes of path '%1%'") % path);
-  }
-
-  if (st.st_uid != geteuid()) {
-    assert(S_ISLNK(st.st_mode));
-    throw Error(format("wrong ownership of top-level store path '%1%'") % path);
-  }
-}
-
-void canonicalisePathMetaData(const Path& path, uid_t fromUid) {
-  InodesSeen inodesSeen;
-  canonicalisePathMetaData(path, fromUid, inodesSeen);
-}
-
-void LocalStore::checkDerivationOutputs(const Path& drvPath,
-                                        const Derivation& drv) {
-  std::string drvName = storePathToName(drvPath);
-  assert(isDerivation(drvName));
-  drvName = std::string(drvName, 0, drvName.size() - drvExtension.size());
-
-  if (drv.isFixedOutput()) {
-    auto out = drv.outputs.find("out");
-    if (out == drv.outputs.end()) {
-      throw Error(
-          format("derivation '%1%' does not have an output named 'out'") %
-          drvPath);
-    }
-
-    bool recursive;
-    Hash h;
-    out->second.parseHashInfo(recursive, h);
-    Path outPath = makeFixedOutputPath(recursive, h, drvName);
-
-    auto j = drv.env.find("out");
-    if (out->second.path != outPath || j == drv.env.end() ||
-        j->second != outPath) {
-      throw Error(
-          format(
-              "derivation '%1%' has incorrect output '%2%', should be '%3%'") %
-          drvPath % out->second.path % outPath);
-    }
-  }
-
-  else {
-    Derivation drvCopy(drv);
-    for (auto& i : drvCopy.outputs) {
-      i.second.path = "";
-      drvCopy.env[i.first] = "";
-    }
-
-    Hash h = hashDerivationModulo(*this, drvCopy);
-
-    for (auto& i : drv.outputs) {
-      Path outPath = makeOutputPath(i.first, h, drvName);
-      auto j = drv.env.find(i.first);
-      if (i.second.path != outPath || j == drv.env.end() ||
-          j->second != outPath) {
-        throw Error(format("derivation '%1%' has incorrect output '%2%', "
-                           "should be '%3%'") %
-                    drvPath % i.second.path % outPath);
-      }
-    }
-  }
-}
-
-uint64_t LocalStore::addValidPath(State& state, const ValidPathInfo& info,
-                                  bool checkOutputs) {
-  if (!info.ca.empty() && !info.isContentAddressed(*this)) {
-    throw Error(
-        "cannot add path '%s' to the Nix store because it claims to be "
-        "content-addressed but isn't",
-        info.path);
-  }
-
-  state.stmtRegisterValidPath
-      .use()(info.path)(info.narHash.to_string(Base16))(
-          info.registrationTime == 0 ? time(nullptr) : info.registrationTime)(
-          info.deriver, !info.deriver.empty())(info.narSize, info.narSize != 0)(
-          info.ultimate ? 1 : 0, info.ultimate)(
-          concatStringsSep(" ", info.sigs), !info.sigs.empty())(
-          info.ca, !info.ca.empty())
-      .exec();
-  uint64_t id = sqlite3_last_insert_rowid(state.db);
-
-  /* If this is a derivation, then store the derivation outputs in
-     the database.  This is useful for the garbage collector: it can
-     efficiently query whether a path is an output of some
-     derivation. */
-  if (isDerivation(info.path)) {
-    Derivation drv = readDerivation(realStoreDir + "/" + baseNameOf(info.path));
-
-    /* Verify that the output paths in the derivation are correct
-       (i.e., follow the scheme for computing output paths from
-       derivations).  Note that if this throws an error, then the
-       DB transaction is rolled back, so the path validity
-       registration above is undone. */
-    if (checkOutputs) {
-      checkDerivationOutputs(info.path, drv);
-    }
-
-    for (auto& i : drv.outputs) {
-      state.stmtAddDerivationOutput.use()(id)(i.first)(i.second.path).exec();
-    }
-  }
-
-  {
-    auto state_(Store::state.lock());
-    state_->pathInfoCache.upsert(storePathToHash(info.path),
-                                 std::make_shared<ValidPathInfo>(info));
-  }
-
-  return id;
-}
-
-void LocalStore::queryPathInfoUncached(
-    const Path& path,
-    Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept {
-  try {
-    auto info = std::make_shared<ValidPathInfo>();
-    info->path = path;
-
-    assertStorePath(path);
-
-    callback(retrySQLite<std::shared_ptr<ValidPathInfo>>([&]() {
-      auto state(_state.lock());
-
-      /* Get the path info. */
-      auto useQueryPathInfo(state->stmtQueryPathInfo.use()(path));
-
-      if (!useQueryPathInfo.next()) {
-        return std::shared_ptr<ValidPathInfo>();
-      }
-
-      info->id = useQueryPathInfo.getInt(0);
-
-      auto hash_ = Hash::deserialize(useQueryPathInfo.getStr(1));
-      if (!hash_.ok()) {
-        throw Error(absl::StrCat("in valid-path entry for '", path,
-                                 "': ", hash_.status().ToString()));
-      }
-      info->narHash = *hash_;
-
-      info->registrationTime = useQueryPathInfo.getInt(2);
-
-      auto s = reinterpret_cast<const char*>(
-          sqlite3_column_text(state->stmtQueryPathInfo, 3));
-      if (s != nullptr) {
-        info->deriver = s;
-      }
-
-      /* Note that narSize = NULL yields 0. */
-      info->narSize = useQueryPathInfo.getInt(4);
-
-      info->ultimate = useQueryPathInfo.getInt(5) == 1;
-
-      s = reinterpret_cast<const char*>(
-          sqlite3_column_text(state->stmtQueryPathInfo, 6));
-      if (s != nullptr) {
-        info->sigs = absl::StrSplit(s, absl::ByChar(' '), absl::SkipEmpty());
-      }
-
-      s = reinterpret_cast<const char*>(
-          sqlite3_column_text(state->stmtQueryPathInfo, 7));
-      if (s != nullptr) {
-        info->ca = s;
-      }
-
-      /* Get the references. */
-      auto useQueryReferences(state->stmtQueryReferences.use()(info->id));
-
-      while (useQueryReferences.next()) {
-        info->references.insert(useQueryReferences.getStr(0));
-      }
-
-      return info;
-    }));
-
-  } catch (...) {
-    callback.rethrow();
-  }
-}
-
-/* Update path info in the database. */
-void LocalStore::updatePathInfo(State& state, const ValidPathInfo& info) {
-  state.stmtUpdatePathInfo
-      .use()(info.narSize, info.narSize != 0)(info.narHash.to_string(Base16))(
-          info.ultimate ? 1 : 0, info.ultimate)(
-          concatStringsSep(" ", info.sigs), !info.sigs.empty())(
-          info.ca, !info.ca.empty())(info.path)
-      .exec();
-}
-
-uint64_t LocalStore::queryValidPathId(State& state, const Path& path) {
-  auto use(state.stmtQueryPathInfo.use()(path));
-  if (!use.next()) {
-    throw Error(format("path '%1%' is not valid") % path);
-  }
-  return use.getInt(0);
-}
-
-bool LocalStore::isValidPath_(State& state, const Path& path) {
-  return state.stmtQueryPathInfo.use()(path).next();
-}
-
-bool LocalStore::isValidPathUncached(const Path& path) {
-  return retrySQLite<bool>([&]() {
-    auto state(_state.lock());
-    return isValidPath_(*state, path);
-  });
-}
-
-PathSet LocalStore::queryValidPaths(const PathSet& paths,
-                                    SubstituteFlag maybeSubstitute) {
-  PathSet res;
-  for (auto& i : paths) {
-    if (isValidPath(i)) {
-      res.insert(i);
-    }
-  }
-  return res;
-}
-
-PathSet LocalStore::queryAllValidPaths() {
-  return retrySQLite<PathSet>([&]() {
-    auto state(_state.lock());
-    auto use(state->stmtQueryValidPaths.use());
-    PathSet res;
-    while (use.next()) {
-      res.insert(use.getStr(0));
-    }
-    return res;
-  });
-}
-
-void LocalStore::queryReferrers(State& state, const Path& path,
-                                PathSet& referrers) {
-  auto useQueryReferrers(state.stmtQueryReferrers.use()(path));
-
-  while (useQueryReferrers.next()) {
-    referrers.insert(useQueryReferrers.getStr(0));
-  }
-}
-
-void LocalStore::queryReferrers(const Path& path, PathSet& referrers) {
-  assertStorePath(path);
-  return retrySQLite<void>([&]() {
-    auto state(_state.lock());
-    queryReferrers(*state, path, referrers);
-  });
-}
-
-PathSet LocalStore::queryValidDerivers(const Path& path) {
-  assertStorePath(path);
-
-  return retrySQLite<PathSet>([&]() {
-    auto state(_state.lock());
-
-    auto useQueryValidDerivers(state->stmtQueryValidDerivers.use()(path));
-
-    PathSet derivers;
-    while (useQueryValidDerivers.next()) {
-      derivers.insert(useQueryValidDerivers.getStr(1));
-    }
-
-    return derivers;
-  });
-}
-
-PathSet LocalStore::queryDerivationOutputs(const Path& path) {
-  return retrySQLite<PathSet>([&]() {
-    auto state(_state.lock());
-
-    auto useQueryDerivationOutputs(state->stmtQueryDerivationOutputs.use()(
-        queryValidPathId(*state, path)));
-
-    PathSet outputs;
-    while (useQueryDerivationOutputs.next()) {
-      outputs.insert(useQueryDerivationOutputs.getStr(1));
-    }
-
-    return outputs;
-  });
-}
-
-StringSet LocalStore::queryDerivationOutputNames(const Path& path) {
-  return retrySQLite<StringSet>([&]() {
-    auto state(_state.lock());
-
-    auto useQueryDerivationOutputs(state->stmtQueryDerivationOutputs.use()(
-        queryValidPathId(*state, path)));
-
-    StringSet outputNames;
-    while (useQueryDerivationOutputs.next()) {
-      outputNames.insert(useQueryDerivationOutputs.getStr(0));
-    }
-
-    return outputNames;
-  });
-}
-
-Path LocalStore::queryPathFromHashPart(const std::string& hashPart) {
-  if (hashPart.size() != storePathHashLen) {
-    throw Error("invalid hash part");
-  }
-
-  Path prefix = storeDir + "/" + hashPart;
-
-  return retrySQLite<Path>([&]() -> std::string {
-    auto state(_state.lock());
-
-    auto useQueryPathFromHashPart(
-        state->stmtQueryPathFromHashPart.use()(prefix));
-
-    if (!useQueryPathFromHashPart.next()) {
-      return "";
-    }
-
-    const char* s = reinterpret_cast<const char*>(
-        sqlite3_column_text(state->stmtQueryPathFromHashPart, 0));
-    return (s != nullptr) &&
-                   prefix.compare(0, prefix.size(), s, prefix.size()) == 0
-               ? s
-               : "";
-  });
-}
-
-PathSet LocalStore::querySubstitutablePaths(const PathSet& paths) {
-  if (!settings.useSubstitutes) {
-    return PathSet();
-  }
-
-  auto remaining = paths;
-  PathSet res;
-
-  for (auto& sub : getDefaultSubstituters()) {
-    if (remaining.empty()) {
-      break;
-    }
-    if (sub->storeDir != storeDir) {
-      continue;
-    }
-    if (!sub->wantMassQuery()) {
-      continue;
-    }
-
-    auto valid = sub->queryValidPaths(remaining);
-
-    PathSet remaining2;
-    for (auto& path : remaining) {
-      if (valid.count(path) != 0u) {
-        res.insert(path);
-      } else {
-        remaining2.insert(path);
-      }
-    }
-
-    std::swap(remaining, remaining2);
-  }
-
-  return res;
-}
-
-void LocalStore::querySubstitutablePathInfos(const PathSet& paths,
-                                             SubstitutablePathInfos& infos) {
-  if (!settings.useSubstitutes) {
-    return;
-  }
-  for (auto& sub : getDefaultSubstituters()) {
-    if (sub->storeDir != storeDir) {
-      continue;
-    }
-    for (auto& path : paths) {
-      if (infos.count(path) != 0u) {
-        continue;
-      }
-      DLOG(INFO) << "checking substituter '" << sub->getUri() << "' for path '"
-                 << path << "'";
-      try {
-        auto info = sub->queryPathInfo(path);
-        auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
-            std::shared_ptr<const ValidPathInfo>(info));
-        infos[path] = SubstitutablePathInfo{info->deriver, info->references,
-                                            narInfo ? narInfo->fileSize : 0,
-                                            info->narSize};
-      } catch (InvalidPath&) {
-      } catch (SubstituterDisabled&) {
-      } catch (Error& e) {
-        if (settings.tryFallback) {
-          LOG(ERROR) << e.what();
-        } else {
-          throw;
-        }
-      }
-    }
-  }
-}
-
-void LocalStore::registerValidPath(const ValidPathInfo& info) {
-  ValidPathInfos infos;
-  infos.push_back(info);
-  registerValidPaths(infos);
-}
-
-void LocalStore::registerValidPaths(const ValidPathInfos& infos) {
-  /* SQLite will fsync by default, but the new valid paths may not
-     be fsync-ed.  So some may want to fsync them before registering
-     the validity, at the expense of some speed of the path
-     registering operation. */
-  if (settings.syncBeforeRegistering) {
-    sync();
-  }
-
-  return retrySQLite<void>([&]() {
-    auto state(_state.lock());
-
-    SQLiteTxn txn(state->db);
-    PathSet paths;
-
-    for (auto& i : infos) {
-      assert(i.narHash.type == htSHA256);
-      if (isValidPath_(*state, i.path)) {
-        updatePathInfo(*state, i);
-      } else {
-        addValidPath(*state, i, false);
-      }
-      paths.insert(i.path);
-    }
-
-    for (auto& i : infos) {
-      auto referrer = queryValidPathId(*state, i.path);
-      for (auto& j : i.references) {
-        state->stmtAddReference.use()(referrer)(queryValidPathId(*state, j))
-            .exec();
-      }
-    }
-
-    /* Check that the derivation outputs are correct.  We can't do
-       this in addValidPath() above, because the references might
-       not be valid yet. */
-    for (auto& i : infos) {
-      if (isDerivation(i.path)) {
-        // FIXME: inefficient; we already loaded the
-        // derivation in addValidPath().
-        Derivation drv =
-            readDerivation(realStoreDir + "/" + baseNameOf(i.path));
-        checkDerivationOutputs(i.path, drv);
-      }
-    }
-
-    /* Do a topological sort of the paths.  This will throw an
-       error if a cycle is detected and roll back the
-       transaction.  Cycles can only occur when a derivation
-       has multiple outputs. */
-    topoSortPaths(paths);
-
-    txn.commit();
-  });
-}
-
-/* Invalidate a path.  The caller is responsible for checking that
-   there are no referrers. */
-void LocalStore::invalidatePath(State& state, const Path& path) {
-  LOG(INFO) << "invalidating path '" << path << "'";
-
-  state.stmtInvalidatePath.use()(path).exec();
-
-  /* Note that the foreign key constraints on the Refs table take
-     care of deleting the references entries for `path'. */
-
-  {
-    auto state_(Store::state.lock());
-    state_->pathInfoCache.erase(storePathToHash(path));
-  }
-}
-
-const PublicKeys& LocalStore::getPublicKeys() {
-  auto state(_state.lock());
-  if (!state->publicKeys) {
-    state->publicKeys = std::make_unique<PublicKeys>(getDefaultPublicKeys());
-  }
-  return *state->publicKeys;
-}
-
-void LocalStore::addToStore(const ValidPathInfo& info, Source& source,
-                            RepairFlag repair, CheckSigsFlag checkSigs,
-                            std::shared_ptr<FSAccessor> accessor) {
-  if (!info.narHash) {
-    throw Error("cannot add path '%s' because it lacks a hash", info.path);
-  }
-
-  if (requireSigs && (checkSigs != 0u) &&
-      (info.checkSignatures(*this, getPublicKeys()) == 0u)) {
-    throw Error("cannot add path '%s' because it lacks a valid signature",
-                info.path);
-  }
-
-  addTempRoot(info.path);
-
-  if ((repair != 0u) || !isValidPath(info.path)) {
-    PathLocks outputLock;
-
-    Path realPath = realStoreDir + "/" + baseNameOf(info.path);
-
-    /* Lock the output path.  But don't lock if we're being called
-       from a build hook (whose parent process already acquired a
-       lock on this path). */
-    if (locksHeld.count(info.path) == 0u) {
-      outputLock.lockPaths({realPath});
-    }
-
-    if ((repair != 0u) || !isValidPath(info.path)) {
-      deletePath(realPath);
-
-      /* While restoring the path from the NAR, compute the hash
-         of the NAR. */
-      HashSink hashSink(htSHA256);
-
-      LambdaSource wrapperSource(
-          [&](unsigned char* data, size_t len) -> size_t {
-            size_t n = source.read(data, len);
-            hashSink(data, n);
-            return n;
-          });
-
-      restorePath(realPath, wrapperSource);
-
-      auto hashResult = hashSink.finish();
-
-      if (hashResult.first != info.narHash) {
-        throw Error(
-            "hash mismatch importing path '%s';\n  wanted: %s\n  got:    %s",
-            info.path, info.narHash.to_string(), hashResult.first.to_string());
-      }
-
-      if (hashResult.second != info.narSize) {
-        throw Error(
-            "size mismatch importing path '%s';\n  wanted: %s\n  got:   %s",
-            info.path, info.narSize, hashResult.second);
-      }
-
-      autoGC();
-
-      canonicalisePathMetaData(realPath, -1);
-
-      optimisePath(realPath);  // FIXME: combine with hashPath()
-
-      registerValidPath(info);
-    }
-
-    outputLock.setDeletion(true);
-  }
-}
-
-Path LocalStore::addToStoreFromDump(const std::string& dump,
-                                    const std::string& name, bool recursive,
-                                    HashType hashAlgo, RepairFlag repair) {
-  Hash h = hashString(hashAlgo, dump);
-
-  Path dstPath = makeFixedOutputPath(recursive, h, name);
-
-  addTempRoot(dstPath);
-
-  if ((repair != 0u) || !isValidPath(dstPath)) {
-    /* The first check above is an optimisation to prevent
-       unnecessary lock acquisition. */
-
-    Path realPath = realStoreDir + "/" + baseNameOf(dstPath);
-
-    PathLocks outputLock({realPath});
-
-    if ((repair != 0u) || !isValidPath(dstPath)) {
-      deletePath(realPath);
-
-      autoGC();
-
-      if (recursive) {
-        StringSource source(dump);
-        restorePath(realPath, source);
-      } else {
-        writeFile(realPath, dump);
-      }
-
-      canonicalisePathMetaData(realPath, -1);
-
-      /* Register the SHA-256 hash of the NAR serialisation of
-         the path in the database.  We may just have computed it
-         above (if called with recursive == true and hashAlgo ==
-         sha256); otherwise, compute it here. */
-      HashResult hash;
-      if (recursive) {
-        hash.first = hashAlgo == htSHA256 ? h : hashString(htSHA256, dump);
-        hash.second = dump.size();
-      } else {
-        hash = hashPath(htSHA256, realPath);
-      }
-
-      optimisePath(realPath);  // FIXME: combine with hashPath()
-
-      ValidPathInfo info;
-      info.path = dstPath;
-      info.narHash = hash.first;
-      info.narSize = hash.second;
-      info.ca = makeFixedOutputCA(recursive, h);
-      registerValidPath(info);
-    }
-
-    outputLock.setDeletion(true);
-  }
-
-  return dstPath;
-}
-
-Path LocalStore::addToStore(const std::string& name, const Path& _srcPath,
-                            bool recursive, HashType hashAlgo,
-                            PathFilter& filter, RepairFlag repair) {
-  Path srcPath(absPath(_srcPath));
-
-  /* Read the whole path into memory. This is not a very scalable
-     method for very large paths, but `copyPath' is mainly used for
-     small files. */
-  StringSink sink;
-  if (recursive) {
-    dumpPath(srcPath, sink, filter);
-  } else {
-    sink.s = make_ref<std::string>(readFile(srcPath));
-  }
-
-  return addToStoreFromDump(*sink.s, name, recursive, hashAlgo, repair);
-}
-
-Path LocalStore::addTextToStore(const std::string& name, const std::string& s,
-                                const PathSet& references, RepairFlag repair) {
-  auto hash = hashString(htSHA256, s);
-  auto dstPath = makeTextPath(name, hash, references);
-
-  addTempRoot(dstPath);
-
-  if ((repair != 0u) || !isValidPath(dstPath)) {
-    Path realPath = realStoreDir + "/" + baseNameOf(dstPath);
-
-    PathLocks outputLock({realPath});
-
-    if ((repair != 0u) || !isValidPath(dstPath)) {
-      deletePath(realPath);
-
-      autoGC();
-
-      writeFile(realPath, s);
-
-      canonicalisePathMetaData(realPath, -1);
-
-      StringSink sink;
-      dumpString(s, sink);
-      auto narHash = hashString(htSHA256, *sink.s);
-
-      optimisePath(realPath);
-
-      ValidPathInfo info;
-      info.path = dstPath;
-      info.narHash = narHash;
-      info.narSize = sink.s->size();
-      info.references = references;
-      info.ca = "text:" + hash.to_string();
-      registerValidPath(info);
-    }
-
-    outputLock.setDeletion(true);
-  }
-
-  return dstPath;
-}
-
-/* Create a temporary directory in the store that won't be
-   garbage-collected. */
-Path LocalStore::createTempDirInStore() {
-  Path tmpDir;
-  do {
-    /* There is a slight possibility that `tmpDir' gets deleted by
-       the GC between createTempDir() and addTempRoot(), so repeat
-       until `tmpDir' exists. */
-    tmpDir = createTempDir(realStoreDir);
-    addTempRoot(tmpDir);
-  } while (!pathExists(tmpDir));
-  return tmpDir;
-}
-
-void LocalStore::invalidatePathChecked(const Path& path) {
-  assertStorePath(path);
-
-  retrySQLite<void>([&]() {
-    auto state(_state.lock());
-
-    SQLiteTxn txn(state->db);
-
-    if (isValidPath_(*state, path)) {
-      PathSet referrers;
-      queryReferrers(*state, path, referrers);
-      referrers.erase(path); /* ignore self-references */
-      if (!referrers.empty()) {
-        throw PathInUse(
-            format("cannot delete path '%1%' because it is in use by %2%") %
-            path % showPaths(referrers));
-      }
-      invalidatePath(*state, path);
-    }
-
-    txn.commit();
-  });
-}
-
-bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) {
-  LOG(INFO) << "reading the Nix store...";
-
-  bool errors = false;
-
-  /* Acquire the global GC lock to get a consistent snapshot of
-     existing and valid paths. */
-  AutoCloseFD fdGCLock = openGCLock(ltWrite);
-
-  PathSet store;
-  for (auto& i : readDirectory(realStoreDir)) {
-    store.insert(i.name);
-  }
-
-  /* Check whether all valid paths actually exist. */
-  LOG(INFO) << "checking path existence...";
-
-  PathSet validPaths2 = queryAllValidPaths();
-  PathSet validPaths;
-  PathSet done;
-
-  fdGCLock = AutoCloseFD(-1);
-
-  for (auto& i : validPaths2) {
-    verifyPath(i, store, done, validPaths, repair, errors);
-  }
-
-  /* Optionally, check the content hashes (slow). */
-  if (checkContents) {
-    LOG(INFO) << "checking hashes...";
-
-    Hash nullHash(htSHA256);
-
-    for (auto& i : validPaths) {
-      try {
-        auto info = std::const_pointer_cast<ValidPathInfo>(
-            std::shared_ptr<const ValidPathInfo>(queryPathInfo(i)));
-
-        /* Check the content hash (optionally - slow). */
-        DLOG(INFO) << "checking contents of '" << i << "'";
-        HashResult current = hashPath(info->narHash.type, toRealPath(i));
-
-        if (info->narHash != nullHash && info->narHash != current.first) {
-          LOG(ERROR) << "path '" << i << "' was modified! expected hash '"
-                     << info->narHash.to_string() << "', got '"
-                     << current.first.to_string() << "'";
-          if (repair != 0u) {
-            repairPath(i);
-          } else {
-            errors = true;
-          }
-        } else {
-          bool update = false;
-
-          /* Fill in missing hashes. */
-          if (info->narHash == nullHash) {
-            LOG(WARNING) << "fixing missing hash on '" << i << "'";
-            info->narHash = current.first;
-            update = true;
-          }
-
-          /* Fill in missing narSize fields (from old stores). */
-          if (info->narSize == 0) {
-            LOG(ERROR) << "updating size field on '" << i << "' to "
-                       << current.second;
-            info->narSize = current.second;
-            update = true;
-          }
-
-          if (update) {
-            auto state(_state.lock());
-            updatePathInfo(*state, *info);
-          }
-        }
-
-      } catch (Error& e) {
-        /* It's possible that the path got GC'ed, so ignore
-           errors on invalid paths. */
-        if (isValidPath(i)) {
-          LOG(ERROR) << e.msg();
-        } else {
-          LOG(WARNING) << e.msg();
-        }
-        errors = true;
-      }
-    }
-  }
-
-  return errors;
-}
-
-void LocalStore::verifyPath(const Path& path, const PathSet& store,
-                            PathSet& done, PathSet& validPaths,
-                            RepairFlag repair, bool& errors) {
-  checkInterrupt();
-
-  if (done.find(path) != done.end()) {
-    return;
-  }
-  done.insert(path);
-
-  if (!isStorePath(path)) {
-    LOG(ERROR) << "path '" << path << "' is not in the Nix store";
-    auto state(_state.lock());
-    invalidatePath(*state, path);
-    return;
-  }
-
-  if (store.find(baseNameOf(path)) == store.end()) {
-    /* Check any referrers first.  If we can invalidate them
-       first, then we can invalidate this path as well. */
-    bool canInvalidate = true;
-    PathSet referrers;
-    queryReferrers(path, referrers);
-    for (auto& i : referrers) {
-      if (i != path) {
-        verifyPath(i, store, done, validPaths, repair, errors);
-        if (validPaths.find(i) != validPaths.end()) {
-          canInvalidate = false;
-        }
-      }
-    }
-
-    if (canInvalidate) {
-      LOG(WARNING) << "path '" << path
-                   << "' disappeared, removing from database...";
-      auto state(_state.lock());
-      invalidatePath(*state, path);
-    } else {
-      LOG(ERROR) << "path '" << path
-                 << "' disappeared, but it still has valid referrers!";
-      if (repair != 0u) {
-        try {
-          repairPath(path);
-        } catch (Error& e) {
-          LOG(WARNING) << e.msg();
-          errors = true;
-        }
-      } else {
-        errors = true;
-      }
-    }
-
-    return;
-  }
-
-  validPaths.insert(path);
-}
-
-unsigned int LocalStore::getProtocol() { return PROTOCOL_VERSION; }
-
-#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && \
-    defined(FS_IMMUTABLE_FL)
-
-static void makeMutable(const Path& path) {
-  checkInterrupt();
-
-  struct stat st = lstat(path);
-
-  if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) {
-    return;
-  }
-
-  if (S_ISDIR(st.st_mode)) {
-    for (auto& i : readDirectory(path)) {
-      makeMutable(path + "/" + i.name);
-    }
-  }
-
-  /* The O_NOFOLLOW is important to prevent us from changing the
-     mutable bit on the target of a symlink (which would be a
-     security hole). */
-  AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
-  if (fd == -1) {
-    if (errno == ELOOP) {
-      return;
-    }  // it's a symlink
-    throw SysError(format("opening file '%1%'") % path);
-  }
-
-  unsigned int flags = 0, old;
-
-  /* Silently ignore errors getting/setting the immutable flag so
-     that we work correctly on filesystems that don't support it. */
-  if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) {
-    return;
-  }
-  old = flags;
-  flags &= ~FS_IMMUTABLE_FL;
-  if (old == flags) {
-    return;
-  }
-  if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) {
-    return;
-  }
-}
-
-/* Upgrade from schema 6 (Nix 0.15) to schema 7 (Nix >= 1.3). */
-void LocalStore::upgradeStore7() {
-  if (getuid() != 0) {
-    return;
-  }
-  printError(
-      "removing immutable bits from the Nix store (this may take a while)...");
-  makeMutable(realStoreDir);
-}
-
-#else
-
-void LocalStore::upgradeStore7() {}
-
-#endif
-
-void LocalStore::vacuumDB() {
-  auto state(_state.lock());
-  state->db.exec("vacuum");
-}
-
-void LocalStore::addSignatures(const Path& storePath, const StringSet& sigs) {
-  retrySQLite<void>([&]() {
-    auto state(_state.lock());
-
-    SQLiteTxn txn(state->db);
-
-    auto info = std::const_pointer_cast<ValidPathInfo>(
-        std::shared_ptr<const ValidPathInfo>(queryPathInfo(storePath)));
-
-    info->sigs.insert(sigs.begin(), sigs.end());
-
-    updatePathInfo(*state, *info);
-
-    txn.commit();
-  });
-}
-
-void LocalStore::signPathInfo(ValidPathInfo& info) {
-  // FIXME: keep secret keys in memory.
-
-  auto secretKeyFiles = settings.secretKeyFiles;
-
-  for (auto& secretKeyFile : secretKeyFiles.get()) {
-    SecretKey secretKey(readFile(secretKeyFile));
-    info.sign(secretKey);
-  }
-}
-
-void LocalStore::createUser(const std::string& userName, uid_t userId) {
-  for (auto& dir : {fmt("%s/profiles/per-user/%s", stateDir, userName),
-                    fmt("%s/gcroots/per-user/%s", stateDir, userName)}) {
-    createDirs(dir);
-    if (chmod(dir.c_str(), 0755) == -1) {
-      throw SysError("changing permissions of directory '%s'", dir);
-    }
-    if (chown(dir.c_str(), userId, getgid()) == -1) {
-      throw SysError("changing owner of directory '%s'", dir);
-    }
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/local-store.hh b/third_party/nix/src/libstore/local-store.hh
deleted file mode 100644
index a7c49079d2..0000000000
--- a/third_party/nix/src/libstore/local-store.hh
+++ /dev/null
@@ -1,319 +0,0 @@
-#pragma once
-
-#include <chrono>
-#include <future>
-#include <string>
-#include <unordered_set>
-
-#include <absl/status/status.h>
-#include <absl/strings/str_split.h>
-
-#include "libstore/pathlocks.hh"
-#include "libstore/sqlite.hh"
-#include "libstore/store-api.hh"
-#include "libutil/sync.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-/* Nix store and database schema version.  Version 1 (or 0) was Nix <=
-   0.7.  Version 2 was Nix 0.8 and 0.9.  Version 3 is Nix 0.10.
-   Version 4 is Nix 0.11.  Version 5 is Nix 0.12-0.16.  Version 6 is
-   Nix 1.0.  Version 7 is Nix 1.3. Version 10 is 2.0. */
-const int nixSchemaVersion = 10;
-
-struct Derivation;
-
-struct OptimiseStats {
-  unsigned long filesLinked = 0;
-  unsigned long long bytesFreed = 0;
-  unsigned long long blocksFreed = 0;
-};
-
-class LocalStore : public LocalFSStore {
- private:
-  /* Lock file used for upgrading. */
-  AutoCloseFD globalLock;
-
-  struct State {
-    /* The SQLite database object. */
-    SQLite db;
-
-    /* Some precompiled SQLite statements. */
-    SQLiteStmt stmtRegisterValidPath;
-    SQLiteStmt stmtUpdatePathInfo;
-    SQLiteStmt stmtAddReference;
-    SQLiteStmt stmtQueryPathInfo;
-    SQLiteStmt stmtQueryReferences;
-    SQLiteStmt stmtQueryReferrers;
-    SQLiteStmt stmtInvalidatePath;
-    SQLiteStmt stmtAddDerivationOutput;
-    SQLiteStmt stmtQueryValidDerivers;
-    SQLiteStmt stmtQueryDerivationOutputs;
-    SQLiteStmt stmtQueryPathFromHashPart;
-    SQLiteStmt stmtQueryValidPaths;
-
-    /* The file to which we write our temporary roots. */
-    AutoCloseFD fdTempRoots;
-
-    /* The last time we checked whether to do an auto-GC, or an
-       auto-GC finished. */
-    std::chrono::time_point<std::chrono::steady_clock> lastGCCheck;
-
-    /* Whether auto-GC is running. If so, get gcFuture to wait for
-       the GC to finish. */
-    bool gcRunning = false;
-    std::shared_future<void> gcFuture;
-
-    /* How much disk space was available after the previous
-       auto-GC. If the current available disk space is below
-       minFree but not much below availAfterGC, then there is no
-       point in starting a new GC. */
-    uint64_t availAfterGC = std::numeric_limits<uint64_t>::max();
-
-    std::unique_ptr<PublicKeys> publicKeys;
-  };
-
-  Sync<State, std::recursive_mutex> _state;
-
- public:
-  PathSetting realStoreDir_;
-
-  const Path realStoreDir;
-  const Path dbDir;
-  const Path linksDir;
-  const Path reservedPath;
-  const Path schemaPath;
-  const Path trashDir;
-  const Path tempRootsDir;
-  const Path fnTempRoots;
-
- private:
-  Setting<bool> requireSigs{
-      (Store*)this, settings.requireSigs, "require-sigs",
-      "whether store paths should have a trusted signature on import"};
-
-  const PublicKeys& getPublicKeys();
-
- public:
-  // Hack for build-remote.cc.
-  // TODO(tazjin): remove this when we've got gRPC
-  PathSet locksHeld =
-      absl::StrSplit(getEnv("NIX_HELD_LOCKS").value_or(""),
-                     absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-
-  /* Initialise the local store, upgrading the schema if
-     necessary. */
-  LocalStore(const Params& params);
-
-  ~LocalStore();
-
-  /* Implementations of abstract store API methods. */
-
-  std::string getUri() override;
-
-  bool isValidPathUncached(const Path& path) override;
-
-  PathSet queryValidPaths(const PathSet& paths, SubstituteFlag maybeSubstitute =
-                                                    NoSubstitute) override;
-
-  PathSet queryAllValidPaths() override;
-
-  void queryPathInfoUncached(
-      const Path& path,
-      Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept override;
-
-  void queryReferrers(const Path& path, PathSet& referrers) override;
-
-  PathSet queryValidDerivers(const Path& path) override;
-
-  PathSet queryDerivationOutputs(const Path& path) override;
-
-  StringSet queryDerivationOutputNames(const Path& path) override;
-
-  Path queryPathFromHashPart(const std::string& hashPart) override;
-
-  PathSet querySubstitutablePaths(const PathSet& paths) override;
-
-  void querySubstitutablePathInfos(const PathSet& paths,
-                                   SubstitutablePathInfos& infos) override;
-
-  void addToStore(const ValidPathInfo& info, Source& source, RepairFlag repair,
-                  CheckSigsFlag checkSigs,
-                  std::shared_ptr<FSAccessor> accessor) override;
-
-  Path addToStore(const std::string& name, const Path& srcPath, bool recursive,
-                  HashType hashAlgo, PathFilter& filter,
-                  RepairFlag repair) override;
-
-  /* Like addToStore(), but the contents of the path are contained
-     in `dump', which is either a NAR serialisation (if recursive ==
-     true) or simply the contents of a regular file (if recursive ==
-     false). */
-  Path addToStoreFromDump(const std::string& dump, const std::string& name,
-                          bool recursive = true, HashType hashAlgo = htSHA256,
-                          RepairFlag repair = NoRepair);
-
-  Path addTextToStore(const std::string& name, const std::string& s,
-                      const PathSet& references, RepairFlag repair) override;
-
-  absl::Status buildPaths(std::ostream& log_sink, const PathSet& paths,
-                          BuildMode build_mode) override;
-
-  BuildResult buildDerivation(std::ostream& log_sink, const Path& drvPath,
-                              const BasicDerivation& drv,
-                              BuildMode buildMode) override;
-
-  void ensurePath(const Path& path) override;
-
-  void addTempRoot(const Path& path) override;
-
-  void addIndirectRoot(const Path& path) override;
-
-  void syncWithGC() override;
-
- private:
-  typedef std::shared_ptr<AutoCloseFD> FDPtr;
-  using FDs = std::list<FDPtr>;
-
-  void findTempRoots(FDs& fds, Roots& roots, bool censor);
-
- public:
-  Roots findRoots(bool censor) override;
-
-  void collectGarbage(const GCOptions& options, GCResults& results) override;
-
-  /* Optimise the disk space usage of the Nix store by hard-linking
-     files with the same contents. */
-  void optimiseStore(OptimiseStats& stats);
-
-  void optimiseStore() override;
-
-  /* Optimise a single store path. */
-  void optimisePath(const Path& path);
-
-  bool verifyStore(bool checkContents, RepairFlag repair) override;
-
-  /* Register the validity of a path, i.e., that `path' exists, that
-     the paths referenced by it exists, and in the case of an output
-     path of a derivation, that it has been produced by a successful
-     execution of the derivation (or something equivalent).  Also
-     register the hash of the file system contents of the path.  The
-     hash must be a SHA-256 hash. */
-  void registerValidPath(const ValidPathInfo& info);
-
-  void registerValidPaths(const ValidPathInfos& infos);
-
-  unsigned int getProtocol() override;
-
-  void vacuumDB();
-
-  /* Repair the contents of the given path by redownloading it using
-     a substituter (if available). */
-  void repairPath(const Path& path);
-
-  void addSignatures(const Path& storePath, const StringSet& sigs) override;
-
-  /* If free disk space in /nix/store if below minFree, delete
-     garbage until it exceeds maxFree. */
-  void autoGC(bool sync = true);
-
- private:
-  int getSchema();
-
-  void openDB(State& state, bool create);
-
-  void makeStoreWritable();
-
-  static uint64_t queryValidPathId(State& state, const Path& path);
-
-  uint64_t addValidPath(State& state, const ValidPathInfo& info,
-                        bool checkOutputs = true);
-
-  void invalidatePath(State& state, const Path& path);
-
-  /* Delete a path from the Nix store. */
-  void invalidatePathChecked(const Path& path);
-
-  void verifyPath(const Path& path, const PathSet& store, PathSet& done,
-                  PathSet& validPaths, RepairFlag repair, bool& errors);
-
-  static void updatePathInfo(State& state, const ValidPathInfo& info);
-
-  void upgradeStore6();
-  void upgradeStore7();
-  PathSet queryValidPathsOld();
-  ValidPathInfo queryPathInfoOld(const Path& path);
-
-  struct GCState;
-
-  static void deleteGarbage(GCState& state, const Path& path);
-
-  void tryToDelete(GCState& state, const Path& path);
-
-  bool canReachRoot(GCState& state, PathSet& visited, const Path& path);
-
-  void deletePathRecursive(GCState& state, const Path& path);
-
-  static bool isActiveTempFile(const GCState& state, const Path& path,
-                               const std::string& suffix);
-
-  AutoCloseFD openGCLock(LockType lockType);
-
-  void findRoots(const Path& path, unsigned char type, Roots& roots);
-
-  void findRootsNoTemp(Roots& roots, bool censor);
-
-  void findRuntimeRoots(Roots& roots, bool censor);
-
-  void removeUnusedLinks(const GCState& state);
-
-  Path createTempDirInStore();
-
-  void checkDerivationOutputs(const Path& drvPath, const Derivation& drv);
-
-  using InodeHash = std::unordered_set<ino_t>;
-
-  InodeHash loadInodeHash();
-  static Strings readDirectoryIgnoringInodes(const Path& path,
-                                             const InodeHash& inodeHash);
-  void optimisePath_(OptimiseStats& stats, const Path& path,
-                     InodeHash& inodeHash);
-
-  // Internal versions that are not wrapped in retry_sqlite.
-  static bool isValidPath_(State& state, const Path& path);
-  static void queryReferrers(State& state, const Path& path,
-                             PathSet& referrers);
-
-  /* Add signatures to a ValidPathInfo using the secret keys
-     specified by the β€˜secret-key-files’ option. */
-  static void signPathInfo(ValidPathInfo& info);
-
-  Path getRealStoreDir() override { return realStoreDir; }
-
-  void createUser(const std::string& userName, uid_t userId) override;
-
-  friend class DerivationGoal;
-  friend class SubstitutionGoal;
-};
-
-using Inode = std::pair<dev_t, ino_t>;
-using InodesSeen = std::set<Inode>;
-
-/* "Fix", or canonicalise, the meta-data of the files in a store path
-   after it has been built.  In particular:
-   - the last modification date on each file is set to 1 (i.e.,
-     00:00:01 1/1/1970 UTC)
-   - the permissions are set of 444 or 555 (i.e., read-only with or
-     without execute permission; setuid bits etc. are cleared)
-   - the owner and group are set to the Nix user and group, if we're
-     running as root. */
-void canonicalisePathMetaData(const Path& path, uid_t fromUid,
-                              InodesSeen& inodesSeen);
-void canonicalisePathMetaData(const Path& path, uid_t fromUid);
-
-void canonicaliseTimestampAndPermissions(const Path& path);
-
-MakeError(PathInUse, Error);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/machines.cc b/third_party/nix/src/libstore/machines.cc
deleted file mode 100644
index 57c89e0692..0000000000
--- a/third_party/nix/src/libstore/machines.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-#include "libstore/machines.hh"
-
-#include <algorithm>
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/match.h>
-#include <absl/strings/str_split.h>
-#include <absl/strings/string_view.h>
-#include <glog/logging.h>
-
-#include "libstore/globals.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-Machine::Machine(decltype(storeUri)& storeUri,
-                 decltype(systemTypes)& systemTypes, decltype(sshKey)& sshKey,
-                 decltype(maxJobs) maxJobs, decltype(speedFactor) speedFactor,
-                 decltype(supportedFeatures)& supportedFeatures,
-                 decltype(mandatoryFeatures)& mandatoryFeatures,
-                 decltype(sshPublicHostKey)& sshPublicHostKey)
-    : storeUri(
-          // Backwards compatibility: if the URI is a hostname,
-          // prepend ssh://.
-          storeUri.find("://") != std::string::npos ||
-                  absl::StartsWith(storeUri, "local") ||
-                  absl::StartsWith(storeUri, "remote") ||
-                  absl::StartsWith(storeUri, "auto") ||
-                  absl::StartsWith(storeUri, "/")
-              ? storeUri
-              : "ssh://" + storeUri),
-      systemTypes(systemTypes),
-      sshKey(sshKey),
-      maxJobs(maxJobs),
-      speedFactor(std::max(1U, speedFactor)),
-      supportedFeatures(supportedFeatures),
-      mandatoryFeatures(mandatoryFeatures),
-      sshPublicHostKey(sshPublicHostKey) {}
-
-bool Machine::allSupported(const std::set<std::string>& features) const {
-  return std::all_of(features.begin(), features.end(),
-                     [&](const std::string& feature) {
-                       return (supportedFeatures.count(feature) != 0u) ||
-                              (mandatoryFeatures.count(feature) != 0u);
-                     });
-}
-
-bool Machine::mandatoryMet(const std::set<std::string>& features) const {
-  return std::all_of(
-      mandatoryFeatures.begin(), mandatoryFeatures.end(),
-      [&](const std::string& feature) { return features.count(feature); });
-}
-
-void parseMachines(const std::string& s, Machines& machines) {
-  for (auto line :
-       absl::StrSplit(s, absl::ByAnyChar("\n;"), absl::SkipEmpty())) {
-    // Skip empty lines & comments
-    line = absl::StripAsciiWhitespace(line);
-    if (line.empty() || line[line.find_first_not_of(" \t")] == '#') {
-      continue;
-    }
-
-    if (line[0] == '@') {
-      auto file = absl::StripAsciiWhitespace(line.substr(1));
-      try {
-        parseMachines(readFile(file), machines);
-      } catch (const SysError& e) {
-        if (e.errNo != ENOENT) {
-          throw;
-        }
-        DLOG(INFO) << "cannot find machines file: " << file;
-      }
-      continue;
-    }
-
-    std::vector<std::string> tokens =
-        absl::StrSplit(line, absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-    auto sz = tokens.size();
-    if (sz < 1) {
-      throw FormatError("bad machine specification '%s'", line);
-    }
-
-    auto isSet = [&](size_t n) {
-      return tokens.size() > n && !tokens[n].empty() && tokens[n] != "-";
-    };
-
-    // TODO(tazjin): what???
-    machines.emplace_back(
-        tokens[0],
-        isSet(1)
-            ? absl::StrSplit(tokens[1], absl::ByChar(','), absl::SkipEmpty())
-            : std::vector<std::string>{settings.thisSystem},
-        isSet(2) ? tokens[2] : "", isSet(3) ? std::stoull(tokens[3]) : 1LL,
-        isSet(4) ? std::stoull(tokens[4]) : 1LL,
-        isSet(5)
-            ? absl::StrSplit(tokens[5], absl::ByChar(','), absl::SkipEmpty())
-            : std::set<std::string>{},
-        isSet(6)
-            ? absl::StrSplit(tokens[6], absl::ByChar(','), absl::SkipEmpty())
-            : std::set<std::string>{},
-        isSet(7) ? tokens[7] : "");
-  }
-}
-
-Machines getMachines() {
-  static auto machines = [&]() {
-    Machines machines;
-    parseMachines(settings.builders, machines);
-    return machines;
-  }();
-  return machines;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/machines.hh b/third_party/nix/src/libstore/machines.hh
deleted file mode 100644
index 0e72697237..0000000000
--- a/third_party/nix/src/libstore/machines.hh
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include "libutil/types.hh"
-
-namespace nix {
-
-struct Machine {
-  const std::string storeUri;
-  const std::vector<std::string> systemTypes;
-  const std::string sshKey;
-  const unsigned int maxJobs;
-  const unsigned int speedFactor;
-  const std::set<std::string> supportedFeatures;
-  const std::set<std::string> mandatoryFeatures;
-  const std::string sshPublicHostKey;
-  bool enabled = true;
-
-  bool allSupported(const std::set<std::string>& features) const;
-
-  bool mandatoryMet(const std::set<std::string>& features) const;
-
-  Machine(decltype(storeUri)& storeUri, decltype(systemTypes)& systemTypes,
-          decltype(sshKey)& sshKey, decltype(maxJobs) maxJobs,
-          decltype(speedFactor) speedFactor,
-          decltype(supportedFeatures)& supportedFeatures,
-          decltype(mandatoryFeatures)& mandatoryFeatures,
-          decltype(sshPublicHostKey)& sshPublicHostKey);
-};
-
-typedef std::vector<Machine> Machines;
-
-void parseMachines(const std::string& s, Machines& machines);
-
-Machines getMachines();
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/misc.cc b/third_party/nix/src/libstore/misc.cc
deleted file mode 100644
index 44e67ada36..0000000000
--- a/third_party/nix/src/libstore/misc.cc
+++ /dev/null
@@ -1,331 +0,0 @@
-#include <glog/logging.h>
-
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/local-store.hh"
-#include "libstore/parsed-derivations.hh"
-#include "libstore/store-api.hh"
-#include "libutil/thread-pool.hh"
-
-namespace nix {
-
-void Store::computeFSClosure(const PathSet& startPaths, PathSet& paths_,
-                             bool flipDirection, bool includeOutputs,
-                             bool includeDerivers) {
-  struct State {
-    size_t pending;
-    PathSet& paths;
-    std::exception_ptr exc;
-  };
-
-  Sync<State> state_(State{0, paths_, nullptr});
-
-  std::function<void(const Path&)> enqueue;
-
-  std::condition_variable done;
-
-  enqueue = [&](const Path& path) -> void {
-    {
-      auto state(state_.lock());
-      if (state->exc) {
-        return;
-      }
-      if (state->paths.count(path) != 0u) {
-        return;
-      }
-      state->paths.insert(path);
-      state->pending++;
-    }
-
-    queryPathInfo(
-        path,
-        Callback<ref<ValidPathInfo>>(
-            [&, path](std::future<ref<ValidPathInfo>> fut) {
-              // FIXME: calls to isValidPath() should be async
-
-              try {
-                auto info = fut.get();
-
-                if (flipDirection) {
-                  PathSet referrers;
-                  queryReferrers(path, referrers);
-                  for (auto& ref : referrers) {
-                    if (ref != path) {
-                      enqueue(ref);
-                    }
-                  }
-
-                  if (includeOutputs) {
-                    for (auto& i : queryValidDerivers(path)) {
-                      enqueue(i);
-                    }
-                  }
-
-                  if (includeDerivers && isDerivation(path)) {
-                    for (auto& i : queryDerivationOutputs(path)) {
-                      if (isValidPath(i) && queryPathInfo(i)->deriver == path) {
-                        enqueue(i);
-                      }
-                    }
-                  }
-
-                } else {
-                  for (auto& ref : info->references) {
-                    if (ref != path) {
-                      enqueue(ref);
-                    }
-                  }
-
-                  if (includeOutputs && isDerivation(path)) {
-                    for (auto& i : queryDerivationOutputs(path)) {
-                      if (isValidPath(i)) {
-                        enqueue(i);
-                      }
-                    }
-                  }
-
-                  if (includeDerivers && isValidPath(info->deriver)) {
-                    enqueue(info->deriver);
-                  }
-                }
-
-                {
-                  auto state(state_.lock());
-                  assert(state->pending);
-                  if (--state->pending == 0u) {
-                    done.notify_one();
-                  }
-                }
-
-              } catch (...) {
-                auto state(state_.lock());
-                if (!state->exc) {
-                  state->exc = std::current_exception();
-                }
-                assert(state->pending);
-                if (--state->pending == 0u) {
-                  done.notify_one();
-                }
-              };
-            }));
-  };
-
-  for (auto& startPath : startPaths) {
-    enqueue(startPath);
-  }
-
-  {
-    auto state(state_.lock());
-    while (state->pending != 0u) {
-      state.wait(done);
-    }
-    if (state->exc) {
-      std::rethrow_exception(state->exc);
-    }
-  }
-}
-
-void Store::computeFSClosure(const Path& startPath, PathSet& paths_,
-                             bool flipDirection, bool includeOutputs,
-                             bool includeDerivers) {
-  computeFSClosure(PathSet{startPath}, paths_, flipDirection, includeOutputs,
-                   includeDerivers);
-}
-
-void Store::queryMissing(const PathSet& targets, PathSet& willBuild_,
-                         PathSet& willSubstitute_, PathSet& unknown_,
-                         unsigned long long& downloadSize_,
-                         unsigned long long& narSize_) {
-  LOG(INFO) << "querying info about missing paths";
-
-  downloadSize_ = narSize_ = 0;
-
-  ThreadPool pool;
-
-  struct State {
-    PathSet done;
-    PathSet &unknown, &willSubstitute, &willBuild;
-    unsigned long long& downloadSize;
-    unsigned long long& narSize;
-  };
-
-  struct DrvState {
-    size_t left;
-    bool done = false;
-    PathSet outPaths;
-    explicit DrvState(size_t left) : left(left) {}
-  };
-
-  Sync<State> state_(State{PathSet(), unknown_, willSubstitute_, willBuild_,
-                           downloadSize_, narSize_});
-
-  std::function<void(Path)> doPath;
-
-  auto mustBuildDrv = [&](const Path& drvPath, const Derivation& drv) {
-    {
-      auto state(state_.lock());
-      state->willBuild.insert(drvPath);
-    }
-
-    for (auto& i : drv.inputDrvs) {
-      pool.enqueue(
-          std::bind(doPath, makeDrvPathWithOutputs(i.first, i.second)));
-    }
-  };
-
-  auto checkOutput = [&](const Path& drvPath, const ref<Derivation>& drv,
-                         const Path& outPath,
-                         const ref<Sync<DrvState>>& drvState_) {
-    if (drvState_->lock()->done) {
-      return;
-    }
-
-    SubstitutablePathInfos infos;
-    querySubstitutablePathInfos({outPath}, infos);
-
-    if (infos.empty()) {
-      drvState_->lock()->done = true;
-      mustBuildDrv(drvPath, *drv);
-    } else {
-      {
-        auto drvState(drvState_->lock());
-        if (drvState->done) {
-          return;
-        }
-        assert(drvState->left);
-        drvState->left--;
-        drvState->outPaths.insert(outPath);
-        if (drvState->left == 0u) {
-          for (auto& path : drvState->outPaths) {
-            pool.enqueue(std::bind(doPath, path));
-          }
-        }
-      }
-    }
-  };
-
-  doPath = [&](const Path& path) {
-    {
-      auto state(state_.lock());
-      if (state->done.count(path) != 0u) {
-        return;
-      }
-      state->done.insert(path);
-    }
-
-    DrvPathWithOutputs i2 = parseDrvPathWithOutputs(path);
-
-    if (isDerivation(i2.first)) {
-      if (!isValidPath(i2.first)) {
-        // FIXME: we could try to substitute the derivation.
-        auto state(state_.lock());
-        state->unknown.insert(path);
-        return;
-      }
-
-      Derivation drv = derivationFromPath(i2.first);
-      ParsedDerivation parsedDrv(i2.first, drv);
-
-      PathSet invalid;
-      for (auto& j : drv.outputs) {
-        if (wantOutput(j.first, i2.second) && !isValidPath(j.second.path)) {
-          invalid.insert(j.second.path);
-        }
-      }
-      if (invalid.empty()) {
-        return;
-      }
-
-      if (settings.useSubstitutes && parsedDrv.substitutesAllowed()) {
-        auto drvState = make_ref<Sync<DrvState>>(DrvState(invalid.size()));
-        for (auto& output : invalid) {
-          pool.enqueue(std::bind(checkOutput, i2.first,
-                                 make_ref<Derivation>(drv), output, drvState));
-        }
-      } else {
-        mustBuildDrv(i2.first, drv);
-      }
-
-    } else {
-      if (isValidPath(path)) {
-        return;
-      }
-
-      SubstitutablePathInfos infos;
-      querySubstitutablePathInfos({path}, infos);
-
-      if (infos.empty()) {
-        auto state(state_.lock());
-        state->unknown.insert(path);
-        return;
-      }
-
-      auto info = infos.find(path);
-      assert(info != infos.end());
-
-      {
-        auto state(state_.lock());
-        state->willSubstitute.insert(path);
-        state->downloadSize += info->second.downloadSize;
-        state->narSize += info->second.narSize;
-      }
-
-      for (auto& ref : info->second.references) {
-        pool.enqueue(std::bind(doPath, ref));
-      }
-    }
-  };
-
-  for (auto& path : targets) {
-    pool.enqueue(std::bind(doPath, path));
-  }
-
-  pool.process();
-}
-
-Paths Store::topoSortPaths(const PathSet& paths) {
-  Paths sorted;
-  PathSet visited;
-  PathSet parents;
-
-  std::function<void(const Path& path, const Path* parent)> dfsVisit;
-
-  dfsVisit = [&](const Path& path, const Path* parent) {
-    if (parents.find(path) != parents.end()) {
-      throw BuildError(
-          format("cycle detected in the references of '%1%' from '%2%'") %
-          path % *parent);
-    }
-
-    if (visited.find(path) != visited.end()) {
-      return;
-    }
-    visited.insert(path);
-    parents.insert(path);
-
-    PathSet references;
-    try {
-      references = queryPathInfo(path)->references;
-    } catch (InvalidPath&) {
-    }
-
-    for (auto& i : references) {
-      /* Don't traverse into paths that don't exist.  That can
-         happen due to substitutes for non-existent paths. */
-      if (i != path && paths.find(i) != paths.end()) {
-        dfsVisit(i, &path);
-      }
-    }
-
-    sorted.push_front(path);
-    parents.erase(path);
-  };
-
-  for (auto& i : paths) {
-    dfsVisit(i, nullptr);
-  }
-
-  return sorted;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/mock-binary-cache-store.cc b/third_party/nix/src/libstore/mock-binary-cache-store.cc
deleted file mode 100644
index 995d61521c..0000000000
--- a/third_party/nix/src/libstore/mock-binary-cache-store.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "libstore/mock-binary-cache-store.hh"
-
-#include <glog/logging.h>
-
-namespace nix {
-
-MockBinaryCacheStore::MockBinaryCacheStore(const Params& params)
-    : BinaryCacheStore(params), contents_(), errorInjections_() {}
-
-std::string MockBinaryCacheStore::getUri() { return "mock://1"; }
-
-bool MockBinaryCacheStore::fileExists(const std::string& path) {
-  ThrowInjectedErrors(path);
-
-  return contents_.find(path) != contents_.end();
-};
-
-void MockBinaryCacheStore::upsertFile(const std::string& path,
-                                      const std::string& data,
-                                      const std::string& mimeType) {
-  ThrowInjectedErrors(path);
-
-  contents_[path] = MemoryFile{data, mimeType};
-}
-
-void MockBinaryCacheStore::getFile(
-    const std::string& path,
-    Callback<std::shared_ptr<std::string>> callback) noexcept {
-  auto eit = errorInjections_.find(path);
-  if (eit != errorInjections_.end()) {
-    try {
-      eit->second();
-      LOG(FATAL) << "thrower failed to throw";
-    } catch (...) {
-      callback.rethrow();
-    }
-    return;
-  }
-
-  auto it = contents_.find(path);
-  if (it == contents_.end()) {
-    try {
-      throw NoSuchBinaryCacheFile(absl::StrCat(
-          "file '", path, "' was not added to the MockBinaryCache"));
-    } catch (...) {
-      callback.rethrow();
-    }
-    return;
-  }
-  callback(std::make_shared<std::string>(it->second.data));
-}
-
-PathSet MockBinaryCacheStore::queryAllValidPaths() {
-  PathSet paths;
-
-  for (auto it : contents_) {
-    paths.insert(it.first);
-  }
-
-  return paths;
-}
-
-void MockBinaryCacheStore::DeleteFile(const std::string& path) {
-  contents_.erase(path);
-}
-
-// Same as upsert, but bypasses injected errors.
-void MockBinaryCacheStore::SetFileContentsForTest(const std::string& path,
-                                                  const std::string& data,
-                                                  const std::string& mimeType) {
-  contents_[path] = MemoryFile{data, mimeType};
-}
-
-void MockBinaryCacheStore::PrepareErrorInjection(
-    const std::string& path, std::function<void()> err_factory) {
-  errorInjections_[path] = err_factory;
-}
-
-void MockBinaryCacheStore::CancelErrorInjection(const std::string& path) {
-  errorInjections_.erase(path);
-}
-
-void MockBinaryCacheStore::ThrowInjectedErrors(const std::string& path) {
-  auto it = errorInjections_.find(path);
-  if (it != errorInjections_.end()) {
-    it->second();
-    LOG(FATAL) << "thrower failed to throw";
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/mock-binary-cache-store.hh b/third_party/nix/src/libstore/mock-binary-cache-store.hh
deleted file mode 100644
index 419077b6bb..0000000000
--- a/third_party/nix/src/libstore/mock-binary-cache-store.hh
+++ /dev/null
@@ -1,59 +0,0 @@
-#pragma once
-
-#include <absl/container/btree_map.h>
-#include <absl/container/flat_hash_map.h>
-
-#include "libstore/binary-cache-store.hh"
-
-namespace nix {
-
-// MockBinaryCacheStore implements a memory-based BinaryCacheStore, for use in
-// tests.
-class MockBinaryCacheStore : public BinaryCacheStore {
- public:
-  MockBinaryCacheStore(const Params& params);
-
-  // Store API
-
-  std::string getUri() override;
-
-  bool fileExists(const std::string& path) override;
-
-  void upsertFile(const std::string& path, const std::string& data,
-                  const std::string& mimeType) override;
-
-  void getFile(
-      const std::string& path,
-      Callback<std::shared_ptr<std::string>> callback) noexcept override;
-
-  PathSet queryAllValidPaths() override;
-
-  // Test API
-
-  // Remove a file from the store.
-  void DeleteFile(const std::string& path);
-
-  // Same as upsert, but bypasses injected errors.
-  void SetFileContentsForTest(const std::string& path, const std::string& data,
-                              const std::string& mimeType);
-
-  void PrepareErrorInjection(const std::string& path,
-                             std::function<void()> throw_func);
-
-  void CancelErrorInjection(const std::string& path);
-
-  // Internals
-
- private:
-  void ThrowInjectedErrors(const std::string& path);
-
-  struct MemoryFile {
-    std::string data;
-    std::string mimeType;
-  };
-
-  absl::btree_map<std::string, MemoryFile> contents_;
-  absl::flat_hash_map<std::string, std::function<void()>> errorInjections_;
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/nar-accessor.cc b/third_party/nix/src/libstore/nar-accessor.cc
deleted file mode 100644
index cfd3d50b32..0000000000
--- a/third_party/nix/src/libstore/nar-accessor.cc
+++ /dev/null
@@ -1,268 +0,0 @@
-#include "libstore/nar-accessor.hh"
-
-#include <algorithm>
-#include <map>
-#include <nlohmann/json.hpp>
-#include <stack>
-#include <utility>
-
-#include "libutil/archive.hh"
-#include "libutil/json.hh"
-
-namespace nix {
-
-struct NarMember {
-  FSAccessor::Type type = FSAccessor::Type::tMissing;
-
-  bool isExecutable = false;
-
-  /* If this is a regular file, position of the contents of this
-     file in the NAR. */
-  size_t start = 0, size = 0;
-
-  std::string target;
-
-  /* If this is a directory, all the children of the directory. */
-  std::map<std::string, NarMember> children;
-};
-
-struct NarAccessor : public FSAccessor {
-  std::shared_ptr<const std::string> nar;
-
-  GetNarBytes getNarBytes;
-
-  NarMember root;
-
-  struct NarIndexer : ParseSink, StringSource {
-    NarAccessor& acc;
-
-    std::stack<NarMember*> parents;
-
-    std::string currentStart;
-    bool isExec = false;
-
-    NarIndexer(NarAccessor& acc, const std::string& nar)
-        : StringSource(nar), acc(acc) {}
-
-    void createMember(const Path& path, NarMember member) {
-      size_t level = std::count(path.begin(), path.end(), '/');
-      while (parents.size() > level) {
-        parents.pop();
-      }
-
-      if (parents.empty()) {
-        acc.root = std::move(member);
-        parents.push(&acc.root);
-      } else {
-        if (parents.top()->type != FSAccessor::Type::tDirectory) {
-          throw Error("NAR file missing parent directory of path '%s'", path);
-        }
-        auto result = parents.top()->children.emplace(baseNameOf(path),
-                                                      std::move(member));
-        parents.push(&result.first->second);
-      }
-    }
-
-    void createDirectory(const Path& path) override {
-      createMember(path, {FSAccessor::Type::tDirectory, false, 0, 0});
-    }
-
-    void createRegularFile(const Path& path) override {
-      createMember(path, {FSAccessor::Type::tRegular, false, 0, 0});
-    }
-
-    void isExecutable() override { parents.top()->isExecutable = true; }
-
-    void preallocateContents(unsigned long long size) override {
-      currentStart = std::string(s, pos, 16);
-      assert(size <= std::numeric_limits<size_t>::max());
-      parents.top()->size = static_cast<size_t>(size);
-      parents.top()->start = pos;
-    }
-
-    void receiveContents(unsigned char* data, unsigned int len) override {
-      // Sanity check
-      if (!currentStart.empty()) {
-        assert(len < 16 || currentStart == std::string((char*)data, 16));
-        currentStart.clear();
-      }
-    }
-
-    void createSymlink(const Path& path, const std::string& target) override {
-      createMember(path,
-                   NarMember{FSAccessor::Type::tSymlink, false, 0, 0, target});
-    }
-  };
-
-  explicit NarAccessor(const ref<const std::string>& nar) : nar(nar) {
-    NarIndexer indexer(*this, *nar);
-    parseDump(indexer, indexer);
-  }
-
-  NarAccessor(const std::string& listing, GetNarBytes getNarBytes)
-      : getNarBytes(std::move(getNarBytes)) {
-    using json = nlohmann::json;
-
-    std::function<void(NarMember&, json&)> recurse;
-
-    recurse = [&](NarMember& member, json& v) {
-      std::string type = v["type"];
-
-      if (type == "directory") {
-        member.type = FSAccessor::Type::tDirectory;
-        for (auto i = v["entries"].begin(); i != v["entries"].end(); ++i) {
-          const std::string& name = i.key();
-          recurse(member.children[name], i.value());
-        }
-      } else if (type == "regular") {
-        member.type = FSAccessor::Type::tRegular;
-        member.size = v["size"];
-        member.isExecutable = v.value("executable", false);
-        member.start = v["narOffset"];
-      } else if (type == "symlink") {
-        member.type = FSAccessor::Type::tSymlink;
-        member.target = v.value("target", "");
-      } else {
-        return;
-      }
-    };
-
-    json v = json::parse(listing);
-    recurse(root, v);
-  }
-
-  NarMember* find(const Path& path) {
-    Path canon = path.empty() ? "" : canonPath(path);
-    NarMember* current = &root;
-    auto end = path.end();
-    for (auto it = path.begin(); it != end;) {
-      // because it != end, the remaining component is non-empty so we need
-      // a directory
-      if (current->type != FSAccessor::Type::tDirectory) {
-        return nullptr;
-      }
-
-      // skip slash (canonPath above ensures that this is always a slash)
-      assert(*it == '/');
-      it += 1;
-
-      // lookup current component
-      auto next = std::find(it, end, '/');
-      auto child = current->children.find(std::string(it, next));
-      if (child == current->children.end()) {
-        return nullptr;
-      }
-      current = &child->second;
-
-      it = next;
-    }
-
-    return current;
-  }
-
-  NarMember& get(const Path& path) {
-    auto result = find(path);
-    if (result == nullptr) {
-      throw Error("NAR file does not contain path '%1%'", path);
-    }
-    return *result;
-  }
-
-  Stat stat(const Path& path) override {
-    auto i = find(path);
-    if (i == nullptr) {
-      return {FSAccessor::Type::tMissing, 0, false};
-    }
-    return {i->type, i->size, i->isExecutable, i->start};
-  }
-
-  StringSet readDirectory(const Path& path) override {
-    auto i = get(path);
-
-    if (i.type != FSAccessor::Type::tDirectory) {
-      throw Error(format("path '%1%' inside NAR file is not a directory") %
-                  path);
-    }
-
-    StringSet res;
-    for (auto& child : i.children) {
-      res.insert(child.first);
-    }
-
-    return res;
-  }
-
-  std::string readFile(const Path& path) override {
-    auto i = get(path);
-    if (i.type != FSAccessor::Type::tRegular) {
-      throw Error(format("path '%1%' inside NAR file is not a regular file") %
-                  path);
-    }
-
-    if (getNarBytes) {
-      return getNarBytes(i.start, i.size);
-    }
-
-    assert(nar);
-    return std::string(*nar, i.start, i.size);
-  }
-
-  std::string readLink(const Path& path) override {
-    auto i = get(path);
-    if (i.type != FSAccessor::Type::tSymlink) {
-      throw Error(format("path '%1%' inside NAR file is not a symlink") % path);
-    }
-    return i.target;
-  }
-};
-
-ref<FSAccessor> makeNarAccessor(ref<const std::string> nar) {
-  return make_ref<NarAccessor>(nar);
-}
-
-ref<FSAccessor> makeLazyNarAccessor(const std::string& listing,
-                                    GetNarBytes getNarBytes) {
-  return make_ref<NarAccessor>(listing, getNarBytes);
-}
-
-void listNar(JSONPlaceholder& res, const ref<FSAccessor>& accessor,
-             const Path& path, bool recurse) {
-  auto st = accessor->stat(path);
-
-  auto obj = res.object();
-
-  switch (st.type) {
-    case FSAccessor::Type::tRegular:
-      obj.attr("type", "regular");
-      obj.attr("size", st.fileSize);
-      if (st.isExecutable) {
-        obj.attr("executable", true);
-      }
-      if (st.narOffset != 0u) {
-        obj.attr("narOffset", st.narOffset);
-      }
-      break;
-    case FSAccessor::Type::tDirectory:
-      obj.attr("type", "directory");
-      {
-        auto res2 = obj.object("entries");
-        for (auto& name : accessor->readDirectory(path)) {
-          if (recurse) {
-            auto res3 = res2.placeholder(name);
-            listNar(res3, accessor, path + "/" + name, true);
-          } else {
-            res2.object(name);
-          }
-        }
-      }
-      break;
-    case FSAccessor::Type::tSymlink:
-      obj.attr("type", "symlink");
-      obj.attr("target", accessor->readLink(path));
-      break;
-    default:
-      throw Error("path '%s' does not exist in NAR", path);
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/nar-accessor.hh b/third_party/nix/src/libstore/nar-accessor.hh
deleted file mode 100644
index 0906a4606e..0000000000
--- a/third_party/nix/src/libstore/nar-accessor.hh
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-
-#include <functional>
-
-#include "libstore/fs-accessor.hh"
-
-namespace nix {
-
-/* Return an object that provides access to the contents of a NAR
-   file. */
-ref<FSAccessor> makeNarAccessor(ref<const std::string> nar);
-
-/* Create a NAR accessor from a NAR listing (in the format produced by
-   listNar()). The callback getNarBytes(offset, length) is used by the
-   readFile() method of the accessor to get the contents of files
-   inside the NAR. */
-typedef std::function<std::string(uint64_t, uint64_t)> GetNarBytes;
-
-ref<FSAccessor> makeLazyNarAccessor(const std::string& listing,
-                                    GetNarBytes getNarBytes);
-
-class JSONPlaceholder;
-
-/* Write a JSON representation of the contents of a NAR (except file
-   contents). */
-void listNar(JSONPlaceholder& res, const ref<FSAccessor>& accessor,
-             const Path& path, bool recurse);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/nar-info-disk-cache.cc b/third_party/nix/src/libstore/nar-info-disk-cache.cc
deleted file mode 100644
index 90ea20a893..0000000000
--- a/third_party/nix/src/libstore/nar-info-disk-cache.cc
+++ /dev/null
@@ -1,295 +0,0 @@
-#include "libstore/nar-info-disk-cache.hh"
-
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_split.h>
-#include <glog/logging.h>
-#include <sqlite3.h>
-
-#include "libstore/globals.hh"
-#include "libstore/sqlite.hh"
-#include "libutil/sync.hh"
-
-namespace nix {
-
-static const char* schema = R"sql(
-
-create table if not exists BinaryCaches (
-    id        integer primary key autoincrement not null,
-    url       text unique not null,
-    timestamp integer not null,
-    storeDir  text not null,
-    wantMassQuery integer not null,
-    priority  integer not null
-);
-
-create table if not exists NARs (
-    cache            integer not null,
-    hashPart         text not null,
-    namePart         text,
-    url              text,
-    compression      text,
-    fileHash         text,
-    fileSize         integer,
-    narHash          text,
-    narSize          integer,
-    refs             text,
-    deriver          text,
-    sigs             text,
-    ca               text,
-    timestamp        integer not null,
-    present          integer not null,
-    primary key (cache, hashPart),
-    foreign key (cache) references BinaryCaches(id) on delete cascade
-);
-
-create table if not exists LastPurge (
-    dummy            text primary key,
-    value            integer
-);
-
-)sql";
-
-class NarInfoDiskCacheImpl final : public NarInfoDiskCache {
- public:
-  /* How often to purge expired entries from the cache. */
-  const int purgeInterval = 24 * 3600;
-
-  struct Cache {
-    int id;
-    Path storeDir;
-    bool wantMassQuery;
-    int priority;
-  };
-
-  struct State {
-    SQLite db;
-    SQLiteStmt insertCache, queryCache, insertNAR, insertMissingNAR, queryNAR,
-        purgeCache;
-    std::map<std::string, Cache> caches;
-  };
-
-  Sync<State> _state;
-
-  NarInfoDiskCacheImpl() {
-    auto state(_state.lock());
-
-    Path dbPath = getCacheDir() + "/nix/binary-cache-v6.sqlite";
-    createDirs(dirOf(dbPath));
-
-    state->db = SQLite(dbPath);
-
-    if (sqlite3_busy_timeout(state->db, 60 * 60 * 1000) != SQLITE_OK) {
-      throwSQLiteError(state->db, "setting timeout");
-    }
-
-    // We can always reproduce the cache.
-    state->db.exec("pragma synchronous = off");
-    state->db.exec("pragma main.journal_mode = truncate");
-
-    state->db.exec(schema);
-
-    state->insertCache.create(
-        state->db,
-        "insert or replace into BinaryCaches(url, timestamp, storeDir, "
-        "wantMassQuery, priority) values (?, ?, ?, ?, ?)");
-
-    state->queryCache.create(state->db,
-                             "select id, storeDir, wantMassQuery, priority "
-                             "from BinaryCaches where url = ?");
-
-    state->insertNAR.create(
-        state->db,
-        "insert or replace into NARs(cache, hashPart, namePart, url, "
-        "compression, fileHash, fileSize, narHash, "
-        "narSize, refs, deriver, sigs, ca, timestamp, present) values (?, ?, "
-        "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)");
-
-    state->insertMissingNAR.create(
-        state->db,
-        "insert or replace into NARs(cache, hashPart, timestamp, present) "
-        "values (?, ?, ?, 0)");
-
-    state->queryNAR.create(
-        state->db,
-        "select present, namePart, url, compression, fileHash, fileSize, "
-        "narHash, narSize, refs, deriver, sigs, ca from NARs where cache = ? "
-        "and hashPart = ? and ((present = 0 and timestamp > ?) or (present = 1 "
-        "and timestamp > ?))");
-
-    /* Periodically purge expired entries from the database. */
-    retrySQLite<void>([&]() {
-      auto now = time(nullptr);
-
-      SQLiteStmt queryLastPurge(state->db, "select value from LastPurge");
-      auto queryLastPurge_(queryLastPurge.use());
-
-      if (!queryLastPurge_.next() ||
-          queryLastPurge_.getInt(0) < now - purgeInterval) {
-        SQLiteStmt(state->db,
-                   "delete from NARs where ((present = 0 and timestamp < ?) or "
-                   "(present = 1 and timestamp < ?))")
-            .use()(now - settings.ttlNegativeNarInfoCache)(
-                now - settings.ttlPositiveNarInfoCache)
-            .exec();
-
-        DLOG(INFO) << "deleted " << sqlite3_changes(state->db)
-                   << " entries from the NAR info disk cache";
-
-        SQLiteStmt(
-            state->db,
-            "insert or replace into LastPurge(dummy, value) values ('', ?)")
-            .use()(now)
-            .exec();
-      }
-    });
-  }
-
-  static Cache& getCache(State& state, const std::string& uri) {
-    auto i = state.caches.find(uri);
-    if (i == state.caches.end()) {
-      abort();
-    }
-    return i->second;
-  }
-
-  void createCache(const std::string& uri, const Path& storeDir,
-                   bool wantMassQuery, int priority) override {
-    retrySQLite<void>([&]() {
-      auto state(_state.lock());
-
-      // FIXME: race
-
-      state->insertCache
-          .use()(uri)(time(nullptr))(storeDir)(
-              static_cast<int64_t>(wantMassQuery))(priority)
-          .exec();
-      assert(sqlite3_changes(state->db) == 1);
-      state->caches[uri] =
-          Cache{static_cast<int>(sqlite3_last_insert_rowid(state->db)),
-                storeDir, wantMassQuery, priority};
-    });
-  }
-
-  bool cacheExists(const std::string& uri, bool& wantMassQuery,
-                   int& priority) override {
-    return retrySQLite<bool>([&]() {
-      auto state(_state.lock());
-
-      auto i = state->caches.find(uri);
-      if (i == state->caches.end()) {
-        auto queryCache(state->queryCache.use()(uri));
-        if (!queryCache.next()) {
-          return false;
-        }
-        state->caches.emplace(
-            uri, Cache{static_cast<int>(queryCache.getInt(0)),
-                       queryCache.getStr(1), queryCache.getInt(2) != 0,
-                       static_cast<int>(queryCache.getInt(3))});
-      }
-
-      auto& cache(getCache(*state, uri));
-
-      wantMassQuery = cache.wantMassQuery;
-      priority = cache.priority;
-
-      return true;
-    });
-  }
-
-  std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
-      const std::string& uri, const std::string& hashPart) override {
-    return retrySQLite<std::pair<Outcome, std::shared_ptr<NarInfo>>>(
-        [&]() -> std::pair<Outcome, std::shared_ptr<NarInfo>> {
-          auto state(_state.lock());
-
-          auto& cache(getCache(*state, uri));
-
-          auto now = time(nullptr);
-
-          auto queryNAR(state->queryNAR.use()(cache.id)(hashPart)(
-              now - settings.ttlNegativeNarInfoCache)(
-              now - settings.ttlPositiveNarInfoCache));
-
-          if (!queryNAR.next()) {
-            return {oUnknown, nullptr};
-          }
-
-          if (queryNAR.getInt(0) == 0) {
-            return {oInvalid, nullptr};
-          }
-
-          auto narInfo = make_ref<NarInfo>();
-
-          auto namePart = queryNAR.getStr(1);
-          narInfo->path = cache.storeDir + "/" + hashPart +
-                          (namePart.empty() ? "" : "-" + namePart);
-          narInfo->url = queryNAR.getStr(2);
-          narInfo->compression = queryNAR.getStr(3);
-          if (!queryNAR.isNull(4)) {
-            auto hash_ = Hash::deserialize(queryNAR.getStr(4));
-            // TODO(#statusor): does this throw mess with retrySQLite?
-            narInfo->fileHash = Hash::unwrap_throw(hash_);
-          }
-          narInfo->fileSize = queryNAR.getInt(5);
-          auto hash_ = Hash::deserialize(queryNAR.getStr(6));
-          narInfo->narHash = Hash::unwrap_throw(hash_);
-          narInfo->narSize = queryNAR.getInt(7);
-          for (auto r : absl::StrSplit(queryNAR.getStr(8), absl::ByChar(' '),
-                                       absl::SkipEmpty())) {
-            narInfo->references.insert(absl::StrCat(cache.storeDir, "/", r));
-          }
-          if (!queryNAR.isNull(9)) {
-            narInfo->deriver = cache.storeDir + "/" + queryNAR.getStr(9);
-          }
-          for (auto& sig : absl::StrSplit(
-                   queryNAR.getStr(10), absl::ByChar(' '), absl::SkipEmpty())) {
-            narInfo->sigs.insert(std::string(sig));
-          }
-          narInfo->ca = queryNAR.getStr(11);
-
-          return {oValid, narInfo};
-        });
-  }
-
-  void upsertNarInfo(const std::string& uri, const std::string& hashPart,
-                     std::shared_ptr<ValidPathInfo> info) override {
-    retrySQLite<void>([&]() {
-      auto state(_state.lock());
-
-      auto& cache(getCache(*state, uri));
-
-      if (info) {
-        auto narInfo = std::dynamic_pointer_cast<NarInfo>(info);
-
-        assert(hashPart == storePathToHash(info->path));
-
-        state->insertNAR
-            .use()(cache.id)(hashPart)(storePathToName(info->path))(
-                narInfo ? narInfo->url : "", narInfo != nullptr)(
-                narInfo ? narInfo->compression : "", narInfo != nullptr)(
-                narInfo && narInfo->fileHash ? narInfo->fileHash.to_string()
-                                             : "",
-                narInfo && narInfo->fileHash)(
-                narInfo ? narInfo->fileSize : 0,
-                narInfo != nullptr &&
-                    (narInfo->fileSize != 0u))(info->narHash.to_string())(
-                info->narSize)(concatStringsSep(" ", info->shortRefs()))(
-                !info->deriver.empty() ? baseNameOf(info->deriver) : "",
-                !info->deriver.empty())(concatStringsSep(" ", info->sigs))(
-                info->ca)(time(nullptr))
-            .exec();
-
-      } else {
-        state->insertMissingNAR.use()(cache.id)(hashPart)(time(nullptr)).exec();
-      }
-    });
-  }
-};
-
-std::shared_ptr<NarInfoDiskCache> getNarInfoDiskCache() {
-  static std::shared_ptr<NarInfoDiskCache> cache =
-      std::make_shared<NarInfoDiskCacheImpl>();
-  return cache;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/nar-info-disk-cache.hh b/third_party/nix/src/libstore/nar-info-disk-cache.hh
deleted file mode 100644
index 8eeab7635a..0000000000
--- a/third_party/nix/src/libstore/nar-info-disk-cache.hh
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include "libstore/nar-info.hh"
-#include "libutil/ref.hh"
-
-namespace nix {
-
-class NarInfoDiskCache {
- public:
-  typedef enum { oValid, oInvalid, oUnknown } Outcome;
-
-  virtual void createCache(const std::string& uri, const Path& storeDir,
-                           bool wantMassQuery, int priority) = 0;
-
-  virtual bool cacheExists(const std::string& uri, bool& wantMassQuery,
-                           int& priority) = 0;
-
-  virtual std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
-      const std::string& uri, const std::string& hashPart) = 0;
-
-  virtual void upsertNarInfo(const std::string& uri,
-                             const std::string& hashPart,
-                             std::shared_ptr<ValidPathInfo> info) = 0;
-};
-
-/* Return a singleton cache object that can be used concurrently by
-   multiple threads. */
-std::shared_ptr<NarInfoDiskCache> getNarInfoDiskCache();
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/nar-info.cc b/third_party/nix/src/libstore/nar-info.cc
deleted file mode 100644
index d42167dbfa..0000000000
--- a/third_party/nix/src/libstore/nar-info.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "libstore/nar-info.hh"
-
-#include <absl/strings/numbers.h>
-#include <absl/strings/str_split.h>
-
-#include "libstore/globals.hh"
-
-namespace nix {
-
-NarInfo::NarInfo(const Store& store, const std::string& s,
-                 const std::string& whence) {
-  auto corrupt = [&]() {
-    throw Error(format("NAR info file '%1%' is corrupt") % whence);
-  };
-
-  auto parseHashField = [&](const std::string& s) {
-    auto hash_ = Hash::deserialize(s);
-    if (hash_.ok()) {
-      return *hash_;
-    } else {
-      // TODO(#statusor): return an actual error
-      corrupt();
-      return Hash();
-    }
-  };
-
-  size_t pos = 0;
-  while (pos < s.size()) {
-    size_t colon = s.find(':', pos);
-    if (colon == std::string::npos) {
-      corrupt();
-    }
-
-    std::string name(s, pos, colon - pos);
-
-    size_t eol = s.find('\n', colon + 2);
-    if (eol == std::string::npos) {
-      corrupt();
-    }
-
-    std::string value(s, colon + 2, eol - colon - 2);
-
-    if (name == "StorePath") {
-      if (!store.isStorePath(value)) {
-        corrupt();
-      }
-      path = value;
-    } else if (name == "URL") {
-      url = value;
-    } else if (name == "Compression") {
-      compression = value;
-    } else if (name == "FileHash") {
-      fileHash = parseHashField(value);
-    } else if (name == "FileSize") {
-      if (!absl::SimpleAtoi(value, &fileSize)) {
-        corrupt();
-      }
-    } else if (name == "NarHash") {
-      narHash = parseHashField(value);
-    } else if (name == "NarSize") {
-      if (!absl::SimpleAtoi(value, &narSize)) {
-        corrupt();
-      }
-    } else if (name == "References") {
-      std::vector<std::string> refs =
-          absl::StrSplit(value, absl::ByChar(' '), absl::SkipEmpty());
-      if (!references.empty()) {
-        corrupt();
-      }
-      for (auto& r : refs) {
-        auto r2 = store.storeDir + "/" + r;
-        if (!store.isStorePath(r2)) {
-          corrupt();
-        }
-        references.insert(r2);
-      }
-    } else if (name == "Deriver") {
-      if (value != "unknown-deriver") {
-        auto p = store.storeDir + "/" + value;
-        if (!store.isStorePath(p)) {
-          corrupt();
-        }
-        deriver = p;
-      }
-    } else if (name == "System") {
-      system = value;
-    } else if (name == "Sig") {
-      sigs.insert(value);
-    } else if (name == "CA") {
-      if (!ca.empty()) {
-        corrupt();
-      }
-      ca = value;
-    }
-
-    pos = eol + 1;
-  }
-
-  if (compression.empty()) {
-    compression = "bzip2";
-  }
-
-  if (path.empty() || url.empty() || narSize == 0 || !narHash) {
-    corrupt();
-  }
-}
-
-std::string NarInfo::to_string() const {
-  std::string res;
-  res += "StorePath: " + path + "\n";
-  res += "URL: " + url + "\n";
-  assert(!compression.empty());
-  res += "Compression: " + compression + "\n";
-  assert(fileHash.type == htSHA256);
-  res += "FileHash: " + fileHash.to_string(Base32) + "\n";
-  res += "FileSize: " + std::to_string(fileSize) + "\n";
-  assert(narHash.type == htSHA256);
-  res += "NarHash: " + narHash.to_string(Base32) + "\n";
-  res += "NarSize: " + std::to_string(narSize) + "\n";
-
-  res += "References: " + concatStringsSep(" ", shortRefs()) + "\n";
-
-  if (!deriver.empty()) {
-    res += "Deriver: " + baseNameOf(deriver) + "\n";
-  }
-
-  if (!system.empty()) {
-    res += "System: " + system + "\n";
-  }
-
-  for (const auto& sig : sigs) {
-    res += "Sig: " + sig + "\n";
-  }
-
-  if (!ca.empty()) {
-    res += "CA: " + ca + "\n";
-  }
-
-  return res;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/nar-info.hh b/third_party/nix/src/libstore/nar-info.hh
deleted file mode 100644
index 48eccf8302..0000000000
--- a/third_party/nix/src/libstore/nar-info.hh
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include "libstore/store-api.hh"
-#include "libutil/hash.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-struct NarInfo : ValidPathInfo {
-  std::string url;
-  std::string compression;
-  Hash fileHash;
-  uint64_t fileSize = 0;
-  std::string system;
-
-  NarInfo() {}
-  NarInfo(const ValidPathInfo& info) : ValidPathInfo(info) {}
-  NarInfo(const Store& store, const std::string& s, const std::string& whence);
-
-  std::string to_string() const;
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/nix-store.pc.in b/third_party/nix/src/libstore/nix-store.pc.in
deleted file mode 100644
index b204776b37..0000000000
--- a/third_party/nix/src/libstore/nix-store.pc.in
+++ /dev/null
@@ -1,9 +0,0 @@
-prefix=@CMAKE_INSTALL_PREFIX@
-libdir=@CMAKE_INSTALL_FULL_LIBDIR@
-includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
-
-Name: Nix
-Description: Nix Package Manager
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lnixstore -lnixutil
-Cflags: -I${includedir}/nix
diff --git a/third_party/nix/src/libstore/optimise-store.cc b/third_party/nix/src/libstore/optimise-store.cc
deleted file mode 100644
index eb24633c18..0000000000
--- a/third_party/nix/src/libstore/optimise-store.cc
+++ /dev/null
@@ -1,296 +0,0 @@
-#include <cerrno>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <regex>
-#include <utility>
-
-#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libstore/globals.hh"
-#include "libstore/local-store.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-static void makeWritable(const Path& path) {
-  struct stat st;
-  if (lstat(path.c_str(), &st) != 0) {
-    throw SysError(format("getting attributes of path '%1%'") % path);
-  }
-  if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1) {
-    throw SysError(format("changing writability of '%1%'") % path);
-  }
-}
-
-struct MakeReadOnly {
-  Path path;
-  explicit MakeReadOnly(Path path) : path(std::move(path)) {}
-  ~MakeReadOnly() {
-    try {
-      /* This will make the path read-only. */
-      if (!path.empty()) {
-        canonicaliseTimestampAndPermissions(path);
-      }
-    } catch (...) {
-      ignoreException();
-    }
-  }
-};
-
-LocalStore::InodeHash LocalStore::loadInodeHash() {
-  DLOG(INFO) << "loading hash inodes in memory";
-  InodeHash inodeHash;
-
-  AutoCloseDir dir(opendir(linksDir.c_str()));
-  if (!dir) {
-    throw SysError(format("opening directory '%1%'") % linksDir);
-  }
-
-  struct dirent* dirent;
-  while (errno = 0, dirent = readdir(dir.get())) { /* sic */
-    checkInterrupt();
-    // We don't care if we hit non-hash files, anything goes
-    inodeHash.insert(dirent->d_ino);
-  }
-  if (errno) {
-    throw SysError(format("reading directory '%1%'") % linksDir);
-  }
-
-  DLOG(INFO) << "loaded " << inodeHash.size() << " hash inodes";
-
-  return inodeHash;
-}
-
-Strings LocalStore::readDirectoryIgnoringInodes(const Path& path,
-                                                const InodeHash& inodeHash) {
-  Strings names;
-
-  AutoCloseDir dir(opendir(path.c_str()));
-  if (!dir) {
-    throw SysError(format("opening directory '%1%'") % path);
-  }
-
-  struct dirent* dirent;
-  while (errno = 0, dirent = readdir(dir.get())) { /* sic */
-    checkInterrupt();
-
-    if (inodeHash.count(dirent->d_ino) != 0u) {
-      DLOG(WARNING) << dirent->d_name << " is already linked";
-      continue;
-    }
-
-    std::string name = dirent->d_name;
-    if (name == "." || name == "..") {
-      continue;
-    }
-    names.push_back(name);
-  }
-  if (errno) {
-    throw SysError(format("reading directory '%1%'") % path);
-  }
-
-  return names;
-}
-
-void LocalStore::optimisePath_(OptimiseStats& stats, const Path& path,
-                               InodeHash& inodeHash) {
-  checkInterrupt();
-
-  struct stat st;
-  if (lstat(path.c_str(), &st) != 0) {
-    throw SysError(format("getting attributes of path '%1%'") % path);
-  }
-
-  if (S_ISDIR(st.st_mode)) {
-    Strings names = readDirectoryIgnoringInodes(path, inodeHash);
-    for (auto& i : names) {
-      optimisePath_(stats, path + "/" + i, inodeHash);
-    }
-    return;
-  }
-
-  /* We can hard link regular files and maybe symlinks. */
-  if (!S_ISREG(st.st_mode)
-#if CAN_LINK_SYMLINK
-      && !S_ISLNK(st.st_mode)
-#endif
-  )
-    return;
-
-  /* Sometimes SNAFUs can cause files in the Nix store to be
-     modified, in particular when running programs as root under
-     NixOS (example: $fontconfig/var/cache being modified).  Skip
-     those files.  FIXME: check the modification time. */
-  if (S_ISREG(st.st_mode) && ((st.st_mode & S_IWUSR) != 0u)) {
-    LOG(WARNING) << "skipping suspicious writable file '" << path << "'";
-    return;
-  }
-
-  /* This can still happen on top-level files. */
-  if (st.st_nlink > 1 && (inodeHash.count(st.st_ino) != 0u)) {
-    DLOG(INFO) << path << " is already linked, with " << (st.st_nlink - 2)
-               << " other file(s)";
-    return;
-  }
-
-  /* Hash the file.  Note that hashPath() returns the hash over the
-     NAR serialisation, which includes the execute bit on the file.
-     Thus, executable and non-executable files with the same
-     contents *won't* be linked (which is good because otherwise the
-     permissions would be screwed up).
-
-     Also note that if `path' is a symlink, then we're hashing the
-     contents of the symlink (i.e. the result of readlink()), not
-     the contents of the target (which may not even exist). */
-  Hash hash = hashPath(htSHA256, path).first;
-  LOG(INFO) << path << " has hash " << hash.to_string();
-
-  /* Check if this is a known hash. */
-  Path linkPath = linksDir + "/" + hash.to_string(Base32, false);
-
-retry:
-  if (!pathExists(linkPath)) {
-    /* Nope, create a hard link in the links directory. */
-    if (link(path.c_str(), linkPath.c_str()) == 0) {
-      inodeHash.insert(st.st_ino);
-      return;
-    }
-
-    switch (errno) {
-      case EEXIST:
-        /* Fall through if another process created β€˜linkPath’ before
-           we did. */
-        break;
-
-      case ENOSPC:
-        /* On ext4, that probably means the directory index is
-           full.  When that happens, it's fine to ignore it: we
-           just effectively disable deduplication of this
-           file.  */
-        LOG(WARNING) << "cannot link '" << linkPath << " to " << path << ": "
-                     << strerror(errno);
-
-        return;
-
-      default:
-        throw SysError("cannot link '%1%' to '%2%'", linkPath, path);
-    }
-  }
-
-  /* Yes!  We've seen a file with the same contents.  Replace the
-     current file with a hard link to that file. */
-  struct stat stLink;
-  if (lstat(linkPath.c_str(), &stLink) != 0) {
-    throw SysError(format("getting attributes of path '%1%'") % linkPath);
-  }
-
-  if (st.st_ino == stLink.st_ino) {
-    DLOG(INFO) << path << " is already linked to " << linkPath;
-    return;
-  }
-
-  if (st.st_size != stLink.st_size) {
-    LOG(WARNING) << "removing corrupted link '" << linkPath << "'";
-    unlink(linkPath.c_str());
-    goto retry;
-  }
-
-  DLOG(INFO) << "linking '" << path << "' to '" << linkPath << "'";
-
-  /* Make the containing directory writable, but only if it's not
-     the store itself (we don't want or need to mess with its
-     permissions). */
-  bool mustToggle = dirOf(path) != realStoreDir;
-  if (mustToggle) {
-    makeWritable(dirOf(path));
-  }
-
-  /* When we're done, make the directory read-only again and reset
-     its timestamp back to 0. */
-  MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
-
-  Path tempLink =
-      (format("%1%/.tmp-link-%2%-%3%") % realStoreDir % getpid() % random())
-          .str();
-
-  if (link(linkPath.c_str(), tempLink.c_str()) == -1) {
-    if (errno == EMLINK) {
-      /* Too many links to the same file (>= 32000 on most file
-         systems).  This is likely to happen with empty files.
-         Just shrug and ignore. */
-      if (st.st_size != 0) {
-        LOG(WARNING) << linkPath << " has maximum number of links";
-      }
-      return;
-    }
-    throw SysError("cannot link '%1%' to '%2%'", tempLink, linkPath);
-  }
-
-  /* Atomically replace the old file with the new hard link. */
-  if (rename(tempLink.c_str(), path.c_str()) == -1) {
-    if (unlink(tempLink.c_str()) == -1) {
-      LOG(ERROR) << "unable to unlink '" << tempLink << "'";
-    }
-    if (errno == EMLINK) {
-      /* Some filesystems generate too many links on the rename,
-         rather than on the original link.  (Probably it
-         temporarily increases the st_nlink field before
-         decreasing it again.) */
-      DLOG(WARNING) << "'" << linkPath
-                    << "' has reached maximum number of links";
-      return;
-    }
-    throw SysError(format("cannot rename '%1%' to '%2%'") % tempLink % path);
-  }
-
-  stats.filesLinked++;
-  stats.bytesFreed += st.st_size;
-  stats.blocksFreed += st.st_blocks;
-}
-
-void LocalStore::optimiseStore(OptimiseStats& stats) {
-  PathSet paths = queryAllValidPaths();
-  InodeHash inodeHash = loadInodeHash();
-
-  uint64_t done = 0;
-
-  for (auto& i : paths) {
-    addTempRoot(i);
-    if (!isValidPath(i)) {
-      continue;
-    } /* path was GC'ed, probably */
-    {
-      LOG(INFO) << "optimising path '" << i << "'";
-      optimisePath_(stats, realStoreDir + "/" + baseNameOf(i), inodeHash);
-    }
-    done++;
-  }
-}
-
-static std::string showBytes(unsigned long long bytes) {
-  return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str();
-}
-
-void LocalStore::optimiseStore() {
-  OptimiseStats stats;
-
-  optimiseStore(stats);
-
-  LOG(INFO) << showBytes(stats.bytesFreed) << " freed by hard-linking "
-            << stats.filesLinked << " files";
-}
-
-void LocalStore::optimisePath(const Path& path) {
-  OptimiseStats stats;
-  InodeHash inodeHash;
-
-  if (settings.autoOptimiseStore) {
-    optimisePath_(stats, path, inodeHash);
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/parsed-derivations.cc b/third_party/nix/src/libstore/parsed-derivations.cc
deleted file mode 100644
index 6989a21fee..0000000000
--- a/third_party/nix/src/libstore/parsed-derivations.cc
+++ /dev/null
@@ -1,128 +0,0 @@
-#include "libstore/parsed-derivations.hh"
-
-#include <absl/strings/str_split.h>
-
-namespace nix {
-
-ParsedDerivation::ParsedDerivation(const Path& drvPath, BasicDerivation& drv)
-    : drvPath(drvPath), drv(drv) {
-  /* Parse the __json attribute, if any. */
-  auto jsonAttr = drv.env.find("__json");
-  if (jsonAttr != drv.env.end()) {
-    try {
-      structuredAttrs = nlohmann::json::parse(jsonAttr->second);
-    } catch (std::exception& e) {
-      throw Error("cannot process __json attribute of '%s': %s", drvPath,
-                  e.what());
-    }
-  }
-}
-
-std::optional<std::string> ParsedDerivation::getStringAttr(
-    const std::string& name) const {
-  if (structuredAttrs) {
-    auto i = structuredAttrs->find(name);
-    if (i == structuredAttrs->end()) {
-      return {};
-    }
-    if (!i->is_string()) {
-      throw Error("attribute '%s' of derivation '%s' must be a string", name,
-                  drvPath);
-    }
-    return i->get<std::string>();
-
-  } else {
-    auto i = drv.env.find(name);
-    if (i == drv.env.end()) {
-      return {};
-    }
-    return i->second;
-  }
-}
-
-bool ParsedDerivation::getBoolAttr(const std::string& name, bool def) const {
-  if (structuredAttrs) {
-    auto i = structuredAttrs->find(name);
-    if (i == structuredAttrs->end()) {
-      return def;
-    }
-    if (!i->is_boolean()) {
-      throw Error("attribute '%s' of derivation '%s' must be a Boolean", name,
-                  drvPath);
-    }
-    return i->get<bool>();
-
-  } else {
-    auto i = drv.env.find(name);
-    if (i == drv.env.end()) {
-      return def;
-    }
-    return i->second == "1";
-  }
-}
-
-std::optional<Strings> ParsedDerivation::getStringsAttr(
-    const std::string& name) const {
-  if (structuredAttrs) {
-    auto i = structuredAttrs->find(name);
-    if (i == structuredAttrs->end()) {
-      return {};
-    }
-    if (!i->is_array()) {
-      throw Error("attribute '%s' of derivation '%s' must be a list of strings",
-                  name, drvPath);
-    }
-    Strings res;
-    for (const auto& j : *i) {
-      if (!j.is_string()) {
-        throw Error(
-            "attribute '%s' of derivation '%s' must be a list of strings", name,
-            drvPath);
-      }
-      res.push_back(j.get<std::string>());
-    }
-    return res;
-
-  } else {
-    auto i = drv.env.find(name);
-    if (i == drv.env.end()) {
-      return {};
-    }
-    return absl::StrSplit(i->second, absl::ByAnyChar(" \t\n\r"),
-                          absl::SkipEmpty());
-  }
-}
-
-StringSet ParsedDerivation::getRequiredSystemFeatures() const {
-  StringSet res;
-  for (auto& i : getStringsAttr("requiredSystemFeatures").value_or(Strings())) {
-    res.insert(i);
-  }
-  return res;
-}
-
-bool ParsedDerivation::canBuildLocally() const {
-  if (drv.platform != settings.thisSystem.get() &&
-      (settings.extraPlatforms.get().count(drv.platform) == 0u) &&
-      !drv.isBuiltin()) {
-    return false;
-  }
-
-  for (auto& feature : getRequiredSystemFeatures()) {
-    if (settings.systemFeatures.get().count(feature) == 0u) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-bool ParsedDerivation::willBuildLocally() const {
-  return getBoolAttr("preferLocalBuild") && canBuildLocally();
-}
-
-bool ParsedDerivation::substitutesAllowed() const {
-  return getBoolAttr("allowSubstitutes", true);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/parsed-derivations.hh b/third_party/nix/src/libstore/parsed-derivations.hh
deleted file mode 100644
index 7cd3d36f67..0000000000
--- a/third_party/nix/src/libstore/parsed-derivations.hh
+++ /dev/null
@@ -1,34 +0,0 @@
-#include <nlohmann/json.hpp>
-
-#include "libstore/derivations.hh"
-
-namespace nix {
-
-class ParsedDerivation {
-  Path drvPath;
-  BasicDerivation& drv;
-  std::optional<nlohmann::json> structuredAttrs;
-
- public:
-  ParsedDerivation(const Path& drvPath, BasicDerivation& drv);
-
-  const std::optional<nlohmann::json>& getStructuredAttrs() const {
-    return structuredAttrs;
-  }
-
-  std::optional<std::string> getStringAttr(const std::string& name) const;
-
-  bool getBoolAttr(const std::string& name, bool def = false) const;
-
-  std::optional<Strings> getStringsAttr(const std::string& name) const;
-
-  StringSet getRequiredSystemFeatures() const;
-
-  bool canBuildLocally() const;
-
-  bool willBuildLocally() const;
-
-  bool substitutesAllowed() const;
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/pathlocks.cc b/third_party/nix/src/libstore/pathlocks.cc
deleted file mode 100644
index 09dec08c45..0000000000
--- a/third_party/nix/src/libstore/pathlocks.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-#include "libstore/pathlocks.hh"
-
-#include <cerrno>
-#include <cstdlib>
-
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "libutil/sync.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-AutoCloseFD openLockFile(const Path& path, bool create) {
-  AutoCloseFD fd(
-      open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600));
-
-  if (!fd && (create || errno != ENOENT)) {
-    throw SysError(format("opening lock file '%1%'") % path);
-  }
-
-  return fd;
-}
-
-void deleteLockFile(const Path& path, int fd) {
-  /* Get rid of the lock file.  Have to be careful not to introduce
-     races.  Write a (meaningless) token to the file to indicate to
-     other processes waiting on this lock that the lock is stale
-     (deleted). */
-  unlink(path.c_str());
-  writeFull(fd, "d");
-  /* Note that the result of unlink() is ignored; removing the lock
-     file is an optimisation, not a necessity. */
-}
-
-bool lockFile(int fd, LockType lockType, bool wait) {
-  int type;
-  if (lockType == ltRead) {
-    type = LOCK_SH;
-  } else if (lockType == ltWrite) {
-    type = LOCK_EX;
-  } else if (lockType == ltNone) {
-    type = LOCK_UN;
-  } else {
-    abort();
-  }
-
-  if (wait) {
-    while (flock(fd, type) != 0) {
-      checkInterrupt();
-      if (errno != EINTR) {
-        throw SysError(format("acquiring/releasing lock"));
-      }
-      return false;
-    }
-  } else {
-    while (flock(fd, type | LOCK_NB) != 0) {
-      checkInterrupt();
-      if (errno == EWOULDBLOCK) {
-        return false;
-      }
-      if (errno != EINTR) {
-        throw SysError(format("acquiring/releasing lock"));
-      }
-    }
-  }
-
-  return true;
-}
-
-PathLocks::PathLocks() : deletePaths(false) {}
-
-PathLocks::PathLocks(const PathSet& paths, const std::string& waitMsg)
-    : deletePaths(false) {
-  lockPaths(paths, waitMsg);
-}
-
-bool PathLocks::lockPaths(const PathSet& paths, const std::string& waitMsg,
-                          bool wait) {
-  assert(fds.empty());
-
-  /* Note that `fds' is built incrementally so that the destructor
-     will only release those locks that we have already acquired. */
-
-  /* Acquire the lock for each path in sorted order. This ensures
-     that locks are always acquired in the same order, thus
-     preventing deadlocks. */
-  for (auto& path : paths) {
-    checkInterrupt();
-    Path lockPath = path + ".lock";
-
-    VLOG(2) << "locking path '" << path << "'";
-
-    AutoCloseFD fd;
-
-    while (true) {
-      /* Open/create the lock file. */
-      fd = openLockFile(lockPath, true);
-
-      /* Acquire an exclusive lock. */
-      if (!lockFile(fd.get(), ltWrite, false)) {
-        if (wait) {
-          if (!waitMsg.empty()) {
-            LOG(WARNING) << waitMsg;
-          }
-          lockFile(fd.get(), ltWrite, true);
-        } else {
-          /* Failed to lock this path; release all other
-             locks. */
-          unlock();
-          return false;
-        }
-      }
-
-      VLOG(2) << "lock acquired on '" << lockPath << "'";
-
-      /* Check that the lock file hasn't become stale (i.e.,
-         hasn't been unlinked). */
-      struct stat st;
-      if (fstat(fd.get(), &st) == -1) {
-        throw SysError(format("statting lock file '%1%'") % lockPath);
-      }
-      if (st.st_size != 0) {
-        /* This lock file has been unlinked, so we're holding
-           a lock on a deleted file.  This means that other
-           processes may create and acquire a lock on
-           `lockPath', and proceed.  So we must retry. */
-        DLOG(INFO) << "open lock file '" << lockPath << "' has become stale";
-      } else {
-        break;
-      }
-    }
-
-    /* Use borrow so that the descriptor isn't closed. */
-    fds.emplace_back(fd.release(), lockPath);
-  }
-
-  return true;
-}
-
-PathLocks::~PathLocks() {
-  try {
-    unlock();
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-void PathLocks::unlock() {
-  for (auto& i : fds) {
-    if (deletePaths) {
-      deleteLockFile(i.second, i.first);
-    }
-
-    if (close(i.first) == -1) {
-      LOG(WARNING) << "cannot close lock file on '" << i.second << "'";
-    }
-
-    VLOG(2) << "lock released on '" << i.second << "'";
-  }
-
-  fds.clear();
-}
-
-void PathLocks::setDeletion(bool deletePaths) {
-  this->deletePaths = deletePaths;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/pathlocks.hh b/third_party/nix/src/libstore/pathlocks.hh
deleted file mode 100644
index d515963e76..0000000000
--- a/third_party/nix/src/libstore/pathlocks.hh
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include "libutil/util.hh"
-
-namespace nix {
-
-/* Open (possibly create) a lock file and return the file descriptor.
-   -1 is returned if create is false and the lock could not be opened
-   because it doesn't exist.  Any other error throws an exception. */
-AutoCloseFD openLockFile(const Path& path, bool create);
-
-/* Delete an open lock file. */
-void deleteLockFile(const Path& path, int fd);
-
-enum LockType { ltRead, ltWrite, ltNone };
-
-bool lockFile(int fd, LockType lockType, bool wait);
-
-class PathLocks {
- private:
-  typedef std::pair<int, Path> FDPair;
-  std::list<FDPair> fds;
-  bool deletePaths;
-
- public:
-  PathLocks();
-  PathLocks(const PathSet& paths, const std::string& waitMsg = "");
-  bool lockPaths(const PathSet& _paths, const std::string& waitMsg = "",
-                 bool wait = true);
-  ~PathLocks();
-  void unlock();
-  void setDeletion(bool deletePaths);
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/profiles.cc b/third_party/nix/src/libstore/profiles.cc
deleted file mode 100644
index 0d44c60cc4..0000000000
--- a/third_party/nix/src/libstore/profiles.cc
+++ /dev/null
@@ -1,252 +0,0 @@
-#include "libstore/profiles.hh"
-
-#include <cerrno>
-#include <cstdio>
-
-#include <absl/strings/numbers.h>
-#include <absl/strings/string_view.h>
-#include <absl/strings/strip.h>
-#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libstore/store-api.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-static bool cmpGensByNumber(const Generation& a, const Generation& b) {
-  return a.number < b.number;
-}
-
-// Parse a generation out of the format
-// `<profilename>-<generation>-link'.
-static int parseName(absl::string_view profileName, absl::string_view name) {
-  // Consume the `<profilename>-' prefix and and `-link' suffix.
-  if (!(absl::ConsumePrefix(&name, profileName) &&
-        absl::ConsumePrefix(&name, "-") &&
-        absl::ConsumeSuffix(&name, "-link"))) {
-    return -1;
-  }
-
-  int n;
-  if (!absl::SimpleAtoi(name, &n) || n < 0) {
-    return -1;
-  }
-
-  return n;
-}
-
-Generations findGenerations(const Path& profile, int& curGen) {
-  Generations gens;
-
-  Path profileDir = dirOf(profile);
-  std::string profileName = baseNameOf(profile);
-
-  for (auto& i : readDirectory(profileDir)) {
-    int n;
-    if ((n = parseName(profileName, i.name)) != -1) {
-      Generation gen;
-      gen.path = profileDir + "/" + i.name;
-      gen.number = n;
-      struct stat st;
-      if (lstat(gen.path.c_str(), &st) != 0) {
-        throw SysError(format("statting '%1%'") % gen.path);
-      }
-      gen.creationTime = st.st_mtime;
-      gens.push_back(gen);
-    }
-  }
-
-  gens.sort(cmpGensByNumber);
-
-  curGen = pathExists(profile) ? parseName(profileName, readLink(profile)) : -1;
-
-  return gens;
-}
-
-static void makeName(const Path& profile, unsigned int num, Path& outLink) {
-  Path prefix = (format("%1%-%2%") % profile % num).str();
-  outLink = prefix + "-link";
-}
-
-Path createGeneration(const ref<LocalFSStore>& store, const Path& profile,
-                      const Path& outPath) {
-  /* The new generation number should be higher than old the
-     previous ones. */
-  int dummy;
-  Generations gens = findGenerations(profile, dummy);
-
-  unsigned int num;
-  if (!gens.empty()) {
-    Generation last = gens.back();
-
-    if (readLink(last.path) == outPath) {
-      /* We only create a new generation symlink if it differs
-         from the last one.
-
-         This helps keeping gratuitous installs/rebuilds from piling
-         up uncontrolled numbers of generations, cluttering up the
-         UI like grub. */
-      return last.path;
-    }
-
-    num = gens.back().number;
-  } else {
-    num = 0;
-  }
-
-  /* Create the new generation.  Note that addPermRoot() blocks if
-     the garbage collector is running to prevent the stuff we've
-     built from moving from the temporary roots (which the GC knows)
-     to the permanent roots (of which the GC would have a stale
-     view).  If we didn't do it this way, the GC might remove the
-     user environment etc. we've just built. */
-  Path generation;
-  makeName(profile, num + 1, generation);
-  store->addPermRoot(outPath, generation, false, true);
-
-  return generation;
-}
-
-static void removeFile(const Path& path) {
-  if (remove(path.c_str()) == -1) {
-    throw SysError(format("cannot unlink '%1%'") % path);
-  }
-}
-
-void deleteGeneration(const Path& profile, unsigned int gen) {
-  Path generation;
-  makeName(profile, gen, generation);
-  removeFile(generation);
-}
-
-static void deleteGeneration2(const Path& profile, unsigned int gen,
-                              bool dryRun) {
-  if (dryRun) {
-    LOG(INFO) << "would remove generation " << gen;
-  } else {
-    LOG(INFO) << "removing generation " << gen;
-    deleteGeneration(profile, gen);
-  }
-}
-
-void deleteGenerations(const Path& profile,
-                       const std::set<unsigned int>& gensToDelete,
-                       bool dryRun) {
-  PathLocks lock;
-  lockProfile(lock, profile);
-
-  int curGen;
-  Generations gens = findGenerations(profile, curGen);
-
-  if (gensToDelete.find(curGen) != gensToDelete.end()) {
-    throw Error(format("cannot delete current generation of profile %1%'") %
-                profile);
-  }
-
-  for (auto& i : gens) {
-    if (gensToDelete.find(i.number) == gensToDelete.end()) {
-      continue;
-    }
-    deleteGeneration2(profile, i.number, dryRun);
-  }
-}
-
-void deleteGenerationsGreaterThan(const Path& profile, int max, bool dryRun) {
-  PathLocks lock;
-  lockProfile(lock, profile);
-
-  int curGen;
-  bool fromCurGen = false;
-  Generations gens = findGenerations(profile, curGen);
-  for (auto i = gens.rbegin(); i != gens.rend(); ++i) {
-    if (i->number == curGen) {
-      fromCurGen = true;
-      max--;
-      continue;
-    }
-    if (fromCurGen) {
-      if (max != 0) {
-        max--;
-        continue;
-      }
-      deleteGeneration2(profile, i->number, dryRun);
-    }
-  }
-}
-
-void deleteOldGenerations(const Path& profile, bool dryRun) {
-  PathLocks lock;
-  lockProfile(lock, profile);
-
-  int curGen;
-  Generations gens = findGenerations(profile, curGen);
-
-  for (auto& i : gens) {
-    if (i.number != curGen) {
-      deleteGeneration2(profile, i.number, dryRun);
-    }
-  }
-}
-
-void deleteGenerationsOlderThan(const Path& profile, time_t t, bool dryRun) {
-  PathLocks lock;
-  lockProfile(lock, profile);
-
-  int curGen;
-  Generations gens = findGenerations(profile, curGen);
-
-  bool canDelete = false;
-  for (auto i = gens.rbegin(); i != gens.rend(); ++i) {
-    if (canDelete) {
-      assert(i->creationTime < t);
-      if (i->number != curGen) {
-        deleteGeneration2(profile, i->number, dryRun);
-      }
-    } else if (i->creationTime < t) {
-      /* We may now start deleting generations, but we don't
-         delete this generation yet, because this generation was
-         still the one that was active at the requested point in
-         time. */
-      canDelete = true;
-    }
-  }
-}
-
-void deleteGenerationsOlderThan(const Path& profile,
-                                const std::string& timeSpec, bool dryRun) {
-  time_t curTime = time(nullptr);
-  std::string strDays = std::string(timeSpec, 0, timeSpec.size() - 1);
-  int days;
-
-  if (!absl::SimpleAtoi(strDays, &days) || days < 1) {
-    throw Error(format("invalid number of days specifier '%1%'") % timeSpec);
-  }
-
-  time_t oldTime = curTime - days * 24 * 3600;
-
-  deleteGenerationsOlderThan(profile, oldTime, dryRun);
-}
-
-void switchLink(const Path& link, Path target) {
-  /* Hacky. */
-  if (dirOf(target) == dirOf(link)) {
-    target = baseNameOf(target);
-  }
-
-  replaceSymlink(target, link);
-}
-
-void lockProfile(PathLocks& lock, const Path& profile) {
-  lock.lockPaths({profile},
-                 (format("waiting for lock on profile '%1%'") % profile).str());
-  lock.setDeletion(true);
-}
-
-std::string optimisticLockProfile(const Path& profile) {
-  return pathExists(profile) ? readLink(profile) : "";
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/profiles.hh b/third_party/nix/src/libstore/profiles.hh
deleted file mode 100644
index ff63990409..0000000000
--- a/third_party/nix/src/libstore/profiles.hh
+++ /dev/null
@@ -1,61 +0,0 @@
-#pragma once
-
-#include <time.h>
-
-#include "libstore/pathlocks.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-struct Generation {
-  int number;
-  Path path;
-  time_t creationTime;
-  Generation() { number = -1; }
-  operator bool() const { return number != -1; }
-};
-
-typedef std::list<Generation> Generations;
-
-/* Returns the list of currently present generations for the specified
-   profile, sorted by generation number. */
-Generations findGenerations(const Path& profile, int& curGen);
-
-class LocalFSStore;
-
-Path createGeneration(const ref<LocalFSStore>& store, const Path& profile,
-                      const Path& outPath);
-
-void deleteGeneration(const Path& profile, unsigned int gen);
-
-void deleteGenerations(const Path& profile,
-                       const std::set<unsigned int>& gensToDelete, bool dryRun);
-
-void deleteGenerationsGreaterThan(const Path& profile, const int max,
-                                  bool dryRun);
-
-void deleteOldGenerations(const Path& profile, bool dryRun);
-
-void deleteGenerationsOlderThan(const Path& profile, time_t t, bool dryRun);
-
-void deleteGenerationsOlderThan(const Path& profile,
-                                const std::string& timeSpec, bool dryRun);
-
-void switchLink(const Path& link, Path target);
-
-/* Ensure exclusive access to a profile.  Any command that modifies
-   the profile first acquires this lock. */
-void lockProfile(PathLocks& lock, const Path& profile);
-
-/* Optimistic locking is used by long-running operations like `nix-env
-   -i'.  Instead of acquiring the exclusive lock for the entire
-   duration of the operation, we just perform the operation
-   optimistically (without an exclusive lock), and check at the end
-   whether the profile changed while we were busy (i.e., the symlink
-   target changed).  If so, the operation is restarted.  Restarting is
-   generally cheap, since the build results are still in the Nix
-   store.  Most of the time, only the user environment has to be
-   rebuilt. */
-std::string optimisticLockProfile(const Path& profile);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/references.cc b/third_party/nix/src/libstore/references.cc
deleted file mode 100644
index f120439c10..0000000000
--- a/third_party/nix/src/libstore/references.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-#include "libstore/references.hh"
-
-#include <cstdlib>
-#include <map>
-
-#include <glog/logging.h>
-
-#include "libutil/archive.hh"
-#include "libutil/hash.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-constexpr unsigned int kRefLength = 32; /* characters */
-
-static void search(const unsigned char* s, size_t len, StringSet& hashes,
-                   StringSet& seen) {
-  static bool initialised = false;
-  static bool isBase32[256];
-  if (!initialised) {
-    for (bool& i : isBase32) {
-      i = false;
-    }
-    for (char base32Char : base32Chars) {
-      isBase32[static_cast<unsigned char>(base32Char)] = true;
-    }
-    initialised = true;
-  }
-
-  for (size_t i = 0; i + kRefLength <= len;) {
-    int j = 0;
-    bool match = true;
-    for (j = kRefLength - 1; j >= 0; --j) {
-      if (!isBase32[s[i + j]]) {
-        i += j + 1;
-        match = false;
-        break;
-      }
-    }
-    if (!match) {
-      continue;
-    }
-    std::string ref(reinterpret_cast<const char*>(s) + i, kRefLength);
-    if (hashes.find(ref) != hashes.end()) {
-      DLOG(INFO) << "found reference to '" << ref << "' at offset " << i;
-      seen.insert(ref);
-      hashes.erase(ref);
-    }
-    ++i;
-  }
-}
-
-struct RefScanSink : Sink {
-  HashSink hashSink;
-  StringSet hashes;
-  StringSet seen;
-
-  std::string tail;
-
-  RefScanSink() : hashSink(htSHA256) {}
-
-  void operator()(const unsigned char* data, size_t len) override;
-};
-
-void RefScanSink::operator()(const unsigned char* data, size_t len) {
-  hashSink(data, len);
-
-  /* It's possible that a reference spans the previous and current
-     fragment, so search in the concatenation of the tail of the
-     previous fragment and the start of the current fragment. */
-  std::string s = tail + std::string(reinterpret_cast<const char*>(data),
-                                     len > kRefLength ? kRefLength : len);
-  search(reinterpret_cast<const unsigned char*>(s.data()), s.size(), hashes,
-         seen);
-
-  search(data, len, hashes, seen);
-
-  size_t tailLen = len <= kRefLength ? len : kRefLength;
-  tail =
-      std::string(tail, tail.size() < kRefLength - tailLen
-                            ? 0
-                            : tail.size() - (kRefLength - tailLen)) +
-      std::string(reinterpret_cast<const char*>(data) + len - tailLen, tailLen);
-}
-
-PathSet scanForReferences(const std::string& path, const PathSet& refs,
-                          HashResult& hash) {
-  RefScanSink sink;
-  std::map<std::string, Path> backMap;
-
-  /* For efficiency (and a higher hit rate), just search for the
-     hash part of the file name.  (This assumes that all references
-     have the form `HASH-bla'). */
-  for (auto& i : refs) {
-    std::string baseName = baseNameOf(i);
-    std::string::size_type pos = baseName.find('-');
-    if (pos == std::string::npos) {
-      throw Error(format("bad reference '%1%'") % i);
-    }
-    std::string s = std::string(baseName, 0, pos);
-    assert(s.size() == kRefLength);
-    assert(backMap.find(s) == backMap.end());
-    // parseHash(htSHA256, s);
-    sink.hashes.insert(s);
-    backMap[s] = i;
-  }
-
-  /* Look for the hashes in the NAR dump of the path. */
-  dumpPath(path, sink);
-
-  /* Map the hashes found back to their store paths. */
-  PathSet found;
-  for (auto& i : sink.seen) {
-    std::map<std::string, Path>::iterator j;
-    if ((j = backMap.find(i)) == backMap.end()) {
-      abort();
-    }
-    found.insert(j->second);
-  }
-
-  hash = sink.hashSink.finish();
-
-  return found;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/references.hh b/third_party/nix/src/libstore/references.hh
deleted file mode 100644
index 94ac5200bd..0000000000
--- a/third_party/nix/src/libstore/references.hh
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "libutil/hash.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-PathSet scanForReferences(const Path& path, const PathSet& refs,
-                          HashResult& hash);
-
-}
diff --git a/third_party/nix/src/libstore/remote-fs-accessor.cc b/third_party/nix/src/libstore/remote-fs-accessor.cc
deleted file mode 100644
index 4178030b55..0000000000
--- a/third_party/nix/src/libstore/remote-fs-accessor.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-#include "libstore/remote-fs-accessor.hh"
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "libstore/nar-accessor.hh"
-#include "libutil/json.hh"
-
-namespace nix {
-
-RemoteFSAccessor::RemoteFSAccessor(const ref<Store>& store,
-                                   const Path& cacheDir)
-    : store(store), cacheDir(cacheDir) {
-  if (!cacheDir.empty()) {
-    createDirs(cacheDir);
-  }
-}
-
-Path RemoteFSAccessor::makeCacheFile(const Path& storePath,
-                                     const std::string& ext) {
-  assert(!cacheDir.empty());
-  return fmt("%s/%s.%s", cacheDir, storePathToHash(storePath), ext);
-}
-
-void RemoteFSAccessor::addToCache(const Path& storePath, const std::string& nar,
-                                  const ref<FSAccessor>& narAccessor) {
-  nars.emplace(storePath, narAccessor);
-
-  if (!cacheDir.empty()) {
-    try {
-      std::ostringstream str;
-      JSONPlaceholder jsonRoot(str);
-      listNar(jsonRoot, narAccessor, "", true);
-      writeFile(makeCacheFile(storePath, "ls"), str.str());
-
-      /* FIXME: do this asynchronously. */
-      writeFile(makeCacheFile(storePath, "nar"), nar);
-
-    } catch (...) {
-      ignoreException();
-    }
-  }
-}
-
-std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path& path_) {
-  auto path = canonPath(path_);
-
-  auto storePath = store->toStorePath(path);
-  std::string restPath = std::string(path, storePath.size());
-
-  if (!store->isValidPath(storePath)) {
-    throw InvalidPath(format("path '%1%' is not a valid store path") %
-                      storePath);
-  }
-
-  auto i = nars.find(storePath);
-  if (i != nars.end()) {
-    return {i->second, restPath};
-  }
-
-  StringSink sink;
-  std::string listing;
-  Path cacheFile;
-
-  if (!cacheDir.empty() &&
-      pathExists(cacheFile = makeCacheFile(storePath, "nar"))) {
-    try {
-      listing = nix::readFile(makeCacheFile(storePath, "ls"));
-
-      auto narAccessor = makeLazyNarAccessor(
-          listing, [cacheFile](uint64_t offset, uint64_t length) {
-            AutoCloseFD fd(open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC));
-            if (!fd) {
-              throw SysError("opening NAR cache file '%s'", cacheFile);
-            }
-
-            if (lseek(fd.get(), offset, SEEK_SET) !=
-                static_cast<off_t>(offset)) {
-              throw SysError("seeking in '%s'", cacheFile);
-            }
-
-            std::string buf(length, 0);
-            readFull(fd.get(), reinterpret_cast<unsigned char*>(buf.data()),
-                     length);
-
-            return buf;
-          });
-
-      nars.emplace(storePath, narAccessor);
-      return {narAccessor, restPath};
-
-    } catch (SysError&) {
-    }
-
-    try {
-      *sink.s = nix::readFile(cacheFile);
-
-      auto narAccessor = makeNarAccessor(sink.s);
-      nars.emplace(storePath, narAccessor);
-      return {narAccessor, restPath};
-
-    } catch (SysError&) {
-    }
-  }
-
-  store->narFromPath(storePath, sink);
-  auto narAccessor = makeNarAccessor(sink.s);
-  addToCache(storePath, *sink.s, narAccessor);
-  return {narAccessor, restPath};
-}
-
-FSAccessor::Stat RemoteFSAccessor::stat(const Path& path) {
-  auto res = fetch(path);
-  return res.first->stat(res.second);
-}
-
-StringSet RemoteFSAccessor::readDirectory(const Path& path) {
-  auto res = fetch(path);
-  return res.first->readDirectory(res.second);
-}
-
-std::string RemoteFSAccessor::readFile(const Path& path) {
-  auto res = fetch(path);
-  return res.first->readFile(res.second);
-}
-
-std::string RemoteFSAccessor::readLink(const Path& path) {
-  auto res = fetch(path);
-  return res.first->readLink(res.second);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/remote-fs-accessor.hh b/third_party/nix/src/libstore/remote-fs-accessor.hh
deleted file mode 100644
index c4f6e89c97..0000000000
--- a/third_party/nix/src/libstore/remote-fs-accessor.hh
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include "libstore/fs-accessor.hh"
-#include "libstore/store-api.hh"
-#include "libutil/ref.hh"
-
-namespace nix {
-
-class RemoteFSAccessor : public FSAccessor {
-  ref<Store> store;
-
-  std::map<Path, ref<FSAccessor>> nars;
-
-  Path cacheDir;
-
-  std::pair<ref<FSAccessor>, Path> fetch(const Path& path_);
-
-  friend class BinaryCacheStore;
-
-  Path makeCacheFile(const Path& storePath, const std::string& ext);
-
-  void addToCache(const Path& storePath, const std::string& nar,
-                  const ref<FSAccessor>& narAccessor);
-
- public:
-  RemoteFSAccessor(const ref<Store>& store,
-                   const /* FIXME: use std::optional */ Path& cacheDir = "");
-
-  Stat stat(const Path& path) override;
-
-  StringSet readDirectory(const Path& path) override;
-
-  std::string readFile(const Path& path) override;
-
-  std::string readLink(const Path& path) override;
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/remote-store.cc b/third_party/nix/src/libstore/remote-store.cc
deleted file mode 100644
index cb6cc808c6..0000000000
--- a/third_party/nix/src/libstore/remote-store.cc
+++ /dev/null
@@ -1,686 +0,0 @@
-#include "libstore/remote-store.hh"
-
-#include <cerrno>
-#include <cstring>
-
-#include <absl/status/status.h>
-#include <absl/strings/ascii.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/worker-protocol.hh"
-#include "libutil/affinity.hh"
-#include "libutil/archive.hh"
-#include "libutil/finally.hh"
-#include "libutil/pool.hh"
-#include "libutil/serialise.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-Path readStorePath(Store& store, Source& from) {
-  Path path = readString(from);
-  store.assertStorePath(path);
-  return path;
-}
-
-template <class T>
-T readStorePaths(Store& store, Source& from) {
-  T paths = readStrings<T>(from);
-  for (auto& i : paths) {
-    store.assertStorePath(i);
-  }
-  return paths;
-}
-
-template PathSet readStorePaths(Store& store, Source& from);
-template Paths readStorePaths(Store& store, Source& from);
-
-/* TODO: Separate these store impls into different files, give them better names
- */
-RemoteStore::RemoteStore(const Params& params)
-    : Store(params),
-      connections(make_ref<Pool<Connection>>(
-          std::max(1, (int)maxConnections),
-          [this]() { return openConnectionWrapper(); },
-          [this](const ref<Connection>& r) {
-            return r->to.good() && r->from.good() &&
-                   std::chrono::duration_cast<std::chrono::seconds>(
-                       std::chrono::steady_clock::now() - r->startTime)
-                           .count() < maxConnectionAge;
-          })) {}
-
-ref<RemoteStore::Connection> RemoteStore::openConnectionWrapper() {
-  if (failed) {
-    throw Error("opening a connection to remote store '%s' previously failed",
-                getUri());
-  }
-  try {
-    return openConnection();
-  } catch (...) {
-    failed = true;
-    throw;
-  }
-}
-
-void RemoteStore::initConnection(Connection& conn) {
-  /* Send the magic greeting, check for the reply. */
-  try {
-    conn.to << WORKER_MAGIC_1;
-    conn.to.flush();
-    unsigned int magic = readInt(conn.from);
-    if (magic != WORKER_MAGIC_2) {
-      throw Error("protocol mismatch");
-    }
-
-    conn.from >> conn.daemonVersion;
-    if (GET_PROTOCOL_MAJOR(conn.daemonVersion) !=
-        GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) {
-      throw Error("Nix daemon protocol version not supported");
-    }
-    if (GET_PROTOCOL_MINOR(conn.daemonVersion) < 10) {
-      throw Error("the Nix daemon version is too old");
-    }
-    conn.to << PROTOCOL_VERSION;
-
-    if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 14) {
-      int cpu = sameMachine() && settings.lockCPU ? lockToCurrentCPU() : -1;
-      if (cpu != -1) {
-        conn.to << 1 << cpu;
-      } else {
-        conn.to << 0;
-      }
-    }
-
-    if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 11) {
-      conn.to << 0u;
-    }
-
-    auto ex = conn.processStderr();
-    if (ex) {
-      std::rethrow_exception(ex);
-    }
-  } catch (Error& e) {
-    throw Error("cannot open connection to remote store '%s': %s", getUri(),
-                e.what());
-  }
-
-  setOptions(conn);
-}
-
-void RemoteStore::setOptions(Connection& conn) {
-  conn.to << wopSetOptions << static_cast<uint64_t>(settings.keepFailed)
-          << static_cast<uint64_t>(settings.keepGoing)
-          << static_cast<uint64_t>(settings.tryFallback)
-          << /* previously: verbosity = */ 0 << settings.maxBuildJobs
-          << settings.maxSilentTime << 1u
-          << /* previously: remote verbosity = */ 0 << 0  // obsolete log type
-          << 0 /* obsolete print build trace */
-          << settings.buildCores
-          << static_cast<uint64_t>(settings.useSubstitutes);
-
-  if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 12) {
-    std::map<std::string, Config::SettingInfo> overrides;
-    globalConfig.getSettings(overrides, true);
-    overrides.erase(settings.keepFailed.name);
-    overrides.erase(settings.keepGoing.name);
-    overrides.erase(settings.tryFallback.name);
-    overrides.erase(settings.maxBuildJobs.name);
-    overrides.erase(settings.maxSilentTime.name);
-    overrides.erase(settings.buildCores.name);
-    overrides.erase(settings.useSubstitutes.name);
-    overrides.erase(settings.showTrace.name);
-    conn.to << overrides.size();
-    for (auto& i : overrides) {
-      conn.to << i.first << i.second.value;
-    }
-  }
-
-  auto ex = conn.processStderr();
-  if (ex) {
-    std::rethrow_exception(ex);
-  }
-}
-
-/* A wrapper around Pool<RemoteStore::Connection>::Handle that marks
-   the connection as bad (causing it to be closed) if a non-daemon
-   exception is thrown before the handle is closed. Such an exception
-   causes a deviation from the expected protocol and therefore a
-   desynchronization between the client and daemon. */
-struct ConnectionHandle {
-  Pool<RemoteStore::Connection>::Handle handle;
-  bool daemonException = false;
-
-  explicit ConnectionHandle(Pool<RemoteStore::Connection>::Handle&& handle)
-      : handle(std::move(handle)) {}
-
-  ConnectionHandle(ConnectionHandle&& h) : handle(std::move(h.handle)) {}
-
-  ~ConnectionHandle() {
-    if (!daemonException && (std::uncaught_exceptions() != 0)) {
-      handle.markBad();
-      // TODO(tazjin): are these types of things supposed to be DEBUG?
-      DLOG(INFO) << "closing daemon connection because of an exception";
-    }
-  }
-
-  RemoteStore::Connection* operator->() { return &*handle; }
-
-  void processStderr(Sink* sink = nullptr, Source* source = nullptr) {
-    auto ex = handle->processStderr(sink, source);
-    if (ex) {
-      daemonException = true;
-      std::rethrow_exception(ex);
-    }
-  }
-};
-
-ConnectionHandle RemoteStore::getConnection() {
-  return ConnectionHandle(connections->get());
-}
-
-bool RemoteStore::isValidPathUncached(const Path& path) {
-  auto conn(getConnection());
-  conn->to << wopIsValidPath << path;
-  conn.processStderr();
-  return readInt(conn->from) != 0u;
-}
-
-PathSet RemoteStore::queryValidPaths(const PathSet& paths,
-                                     SubstituteFlag maybeSubstitute) {
-  auto conn(getConnection());
-  if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) {
-    PathSet res;
-    for (auto& i : paths) {
-      if (isValidPath(i)) {
-        res.insert(i);
-      }
-    }
-    return res;
-  }
-  conn->to << wopQueryValidPaths << paths;
-  conn.processStderr();
-  return readStorePaths<PathSet>(*this, conn->from);
-}
-
-PathSet RemoteStore::queryAllValidPaths() {
-  auto conn(getConnection());
-  conn->to << wopQueryAllValidPaths;
-  conn.processStderr();
-  return readStorePaths<PathSet>(*this, conn->from);
-}
-
-PathSet RemoteStore::querySubstitutablePaths(const PathSet& paths) {
-  auto conn(getConnection());
-  if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) {
-    PathSet res;
-    for (auto& i : paths) {
-      conn->to << wopHasSubstitutes << i;
-      conn.processStderr();
-      if (readInt(conn->from) != 0u) {
-        res.insert(i);
-      }
-    }
-    return res;
-  }
-  conn->to << wopQuerySubstitutablePaths << paths;
-  conn.processStderr();
-  return readStorePaths<PathSet>(*this, conn->from);
-}
-
-void RemoteStore::querySubstitutablePathInfos(const PathSet& paths,
-                                              SubstitutablePathInfos& infos) {
-  if (paths.empty()) {
-    return;
-  }
-
-  auto conn(getConnection());
-
-  if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) {
-    for (auto& i : paths) {
-      SubstitutablePathInfo info;
-      conn->to << wopQuerySubstitutablePathInfo << i;
-      conn.processStderr();
-      unsigned int reply = readInt(conn->from);
-      if (reply == 0) {
-        continue;
-      }
-      info.deriver = readString(conn->from);
-      if (!info.deriver.empty()) {
-        assertStorePath(info.deriver);
-      }
-      info.references = readStorePaths<PathSet>(*this, conn->from);
-      info.downloadSize = readLongLong(conn->from);
-      info.narSize = readLongLong(conn->from);
-      infos[i] = info;
-    }
-
-  } else {
-    conn->to << wopQuerySubstitutablePathInfos << paths;
-    conn.processStderr();
-    auto count = readNum<size_t>(conn->from);
-    for (size_t n = 0; n < count; n++) {
-      Path path = readStorePath(*this, conn->from);
-      SubstitutablePathInfo& info(infos[path]);
-      info.deriver = readString(conn->from);
-      if (!info.deriver.empty()) {
-        assertStorePath(info.deriver);
-      }
-      info.references = readStorePaths<PathSet>(*this, conn->from);
-      info.downloadSize = readLongLong(conn->from);
-      info.narSize = readLongLong(conn->from);
-    }
-  }
-}
-
-void RemoteStore::queryPathInfoUncached(
-    const Path& path,
-    Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept {
-  try {
-    std::shared_ptr<ValidPathInfo> info;
-    {
-      auto conn(getConnection());
-      conn->to << wopQueryPathInfo << path;
-      try {
-        conn.processStderr();
-      } catch (Error& e) {
-        // Ugly backwards compatibility hack.
-        if (e.msg().find("is not valid") != std::string::npos) {
-          throw InvalidPath(e.what());
-        }
-        throw;
-      }
-      if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 17) {
-        bool valid;
-        conn->from >> valid;
-        if (!valid) {
-          throw InvalidPath(format("path '%s' is not valid") % path);
-        }
-      }
-      info = std::make_shared<ValidPathInfo>();
-      info->path = path;
-      info->deriver = readString(conn->from);
-      if (!info->deriver.empty()) {
-        assertStorePath(info->deriver);
-      }
-      auto hash_ = Hash::deserialize(readString(conn->from), htSHA256);
-      info->narHash = Hash::unwrap_throw(hash_);
-      info->references = readStorePaths<PathSet>(*this, conn->from);
-      conn->from >> info->registrationTime >> info->narSize;
-      if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) {
-        conn->from >> info->ultimate;
-        info->sigs = readStrings<StringSet>(conn->from);
-        conn->from >> info->ca;
-      }
-    }
-    callback(std::move(info));
-  } catch (...) {
-    callback.rethrow();
-  }
-}
-
-void RemoteStore::queryReferrers(const Path& path, PathSet& referrers) {
-  auto conn(getConnection());
-  conn->to << wopQueryReferrers << path;
-  conn.processStderr();
-  auto referrers2 = readStorePaths<PathSet>(*this, conn->from);
-  referrers.insert(referrers2.begin(), referrers2.end());
-}
-
-PathSet RemoteStore::queryValidDerivers(const Path& path) {
-  auto conn(getConnection());
-  conn->to << wopQueryValidDerivers << path;
-  conn.processStderr();
-  return readStorePaths<PathSet>(*this, conn->from);
-}
-
-PathSet RemoteStore::queryDerivationOutputs(const Path& path) {
-  auto conn(getConnection());
-  conn->to << wopQueryDerivationOutputs << path;
-  conn.processStderr();
-  return readStorePaths<PathSet>(*this, conn->from);
-}
-
-PathSet RemoteStore::queryDerivationOutputNames(const Path& path) {
-  auto conn(getConnection());
-  conn->to << wopQueryDerivationOutputNames << path;
-  conn.processStderr();
-  return readStrings<PathSet>(conn->from);
-}
-
-Path RemoteStore::queryPathFromHashPart(const std::string& hashPart) {
-  auto conn(getConnection());
-  conn->to << wopQueryPathFromHashPart << hashPart;
-  conn.processStderr();
-  Path path = readString(conn->from);
-  if (!path.empty()) {
-    assertStorePath(path);
-  }
-  return path;
-}
-
-void RemoteStore::addToStore(const ValidPathInfo& info, Source& source,
-                             RepairFlag repair, CheckSigsFlag checkSigs,
-                             std::shared_ptr<FSAccessor> accessor) {
-  auto conn(getConnection());
-
-  if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 18) {
-    conn->to << wopImportPaths;
-
-    auto source2 = sinkToSource([&](Sink& sink) {
-      sink << 1  // == path follows
-          ;
-      copyNAR(source, sink);
-      sink << exportMagic << info.path << info.references << info.deriver
-           << 0  // == no legacy signature
-           << 0  // == no path follows
-          ;
-    });
-
-    conn.processStderr(nullptr, source2.get());
-
-    auto importedPaths = readStorePaths<PathSet>(*this, conn->from);
-    assert(importedPaths.size() <= 1);
-  }
-
-  else {
-    conn->to << wopAddToStoreNar << info.path << info.deriver
-             << info.narHash.to_string(Base16, false) << info.references
-             << info.registrationTime << info.narSize << info.ultimate
-             << info.sigs << info.ca << repair << !checkSigs;
-    bool tunnel = GET_PROTOCOL_MINOR(conn->daemonVersion) >= 21;
-    if (!tunnel) {
-      copyNAR(source, conn->to);
-    }
-    conn.processStderr(nullptr, tunnel ? &source : nullptr);
-  }
-}
-
-Path RemoteStore::addToStore(const std::string& name, const Path& _srcPath,
-                             bool recursive, HashType hashAlgo,
-                             PathFilter& filter, RepairFlag repair) {
-  if (repair != 0u) {
-    throw Error(
-        "repairing is not supported when building through the Nix daemon");
-  }
-
-  auto conn(getConnection());
-
-  Path srcPath(absPath(_srcPath));
-
-  conn->to << wopAddToStore << name
-           << ((hashAlgo == htSHA256 && recursive)
-                   ? 0
-                   : 1) /* backwards compatibility hack */
-           << (recursive ? 1 : 0) << printHashType(hashAlgo);
-
-  try {
-    conn->to.written = 0;
-    conn->to.warn = true;
-    connections->incCapacity();
-    {
-      Finally cleanup([&]() { connections->decCapacity(); });
-      dumpPath(srcPath, conn->to, filter);
-    }
-    conn->to.warn = false;
-    conn.processStderr();
-  } catch (SysError& e) {
-    /* Daemon closed while we were sending the path. Probably OOM
-       or I/O error. */
-    if (e.errNo == EPIPE) {
-      try {
-        conn.processStderr();
-      } catch (EndOfFile& e) {
-      }
-    }
-    throw;
-  }
-
-  return readStorePath(*this, conn->from);
-}
-
-Path RemoteStore::addTextToStore(const std::string& name, const std::string& s,
-                                 const PathSet& references, RepairFlag repair) {
-  if (repair != 0u) {
-    throw Error(
-        "repairing is not supported when building through the Nix daemon");
-  }
-
-  auto conn(getConnection());
-  conn->to << wopAddTextToStore << name << s << references;
-
-  conn.processStderr();
-  return readStorePath(*this, conn->from);
-}
-
-absl::Status RemoteStore::buildPaths(std::ostream& /* log_sink */,
-                                     const PathSet& drvPaths,
-                                     BuildMode build_mode) {
-  auto conn(getConnection());
-  conn->to << wopBuildPaths;
-  if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 13) {
-    conn->to << drvPaths;
-    if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 15) {
-      conn->to << build_mode;
-    } else if (build_mode != bmNormal) {
-      /* Old daemons did not take a 'buildMode' parameter, so we
-         need to validate it here on the client side.  */
-      return absl::Status(
-          absl::StatusCode::kInvalidArgument,
-          "repairing or checking is not supported when building through the "
-          "Nix daemon");
-    }
-  } else {
-    /* For backwards compatibility with old daemons, strip output
-       identifiers. */
-    PathSet drvPaths2;
-    for (auto& i : drvPaths) {
-      drvPaths2.insert(std::string(i, 0, i.find('!')));
-    }
-    conn->to << drvPaths2;
-  }
-  conn.processStderr();
-  readInt(conn->from);
-
-  return absl::OkStatus();
-}
-
-BuildResult RemoteStore::buildDerivation(std::ostream& /*log_sink*/,
-                                         const Path& drvPath,
-                                         const BasicDerivation& drv,
-                                         BuildMode buildMode) {
-  auto conn(getConnection());
-  conn->to << wopBuildDerivation << drvPath << drv << buildMode;
-  conn.processStderr();
-  BuildResult res;
-  unsigned int status;
-  conn->from >> status >> res.errorMsg;
-  res.status = static_cast<BuildResult::Status>(status);
-  return res;
-}
-
-void RemoteStore::ensurePath(const Path& path) {
-  auto conn(getConnection());
-  conn->to << wopEnsurePath << path;
-  conn.processStderr();
-  readInt(conn->from);
-}
-
-void RemoteStore::addTempRoot(const Path& path) {
-  auto conn(getConnection());
-  conn->to << wopAddTempRoot << path;
-  conn.processStderr();
-  readInt(conn->from);
-}
-
-void RemoteStore::addIndirectRoot(const Path& path) {
-  auto conn(getConnection());
-  conn->to << wopAddIndirectRoot << path;
-  conn.processStderr();
-  readInt(conn->from);
-}
-
-void RemoteStore::syncWithGC() {
-  auto conn(getConnection());
-  conn->to << wopSyncWithGC;
-  conn.processStderr();
-  readInt(conn->from);
-}
-
-Roots RemoteStore::findRoots(bool censor) {
-  auto conn(getConnection());
-  conn->to << wopFindRoots;
-  conn.processStderr();
-  auto count = readNum<size_t>(conn->from);
-  Roots result;
-  while ((count--) != 0u) {
-    Path link = readString(conn->from);
-    Path target = readStorePath(*this, conn->from);
-    result[target].emplace(link);
-  }
-  return result;
-}
-
-void RemoteStore::collectGarbage(const GCOptions& options, GCResults& results) {
-  auto conn(getConnection());
-
-  conn->to << wopCollectGarbage << options.action << options.pathsToDelete
-           << static_cast<uint64_t>(options.ignoreLiveness)
-           << options.maxFreed
-           /* removed options */
-           << 0 << 0 << 0;
-
-  conn.processStderr();
-
-  results.paths = readStrings<PathSet>(conn->from);
-  results.bytesFreed = readLongLong(conn->from);
-  readLongLong(conn->from);  // obsolete
-
-  {
-    auto state_(Store::state.lock());
-    state_->pathInfoCache.clear();
-  }
-}
-
-void RemoteStore::optimiseStore() {
-  auto conn(getConnection());
-  conn->to << wopOptimiseStore;
-  conn.processStderr();
-  readInt(conn->from);
-}
-
-bool RemoteStore::verifyStore(bool checkContents, RepairFlag repair) {
-  auto conn(getConnection());
-  conn->to << wopVerifyStore << static_cast<uint64_t>(checkContents) << repair;
-  conn.processStderr();
-  return readInt(conn->from) != 0u;
-}
-
-void RemoteStore::addSignatures(const Path& storePath, const StringSet& sigs) {
-  auto conn(getConnection());
-  conn->to << wopAddSignatures << storePath << sigs;
-  conn.processStderr();
-  readInt(conn->from);
-}
-
-void RemoteStore::queryMissing(const PathSet& targets, PathSet& willBuild,
-                               PathSet& willSubstitute, PathSet& unknown,
-                               unsigned long long& downloadSize,
-                               unsigned long long& narSize) {
-  {
-    auto conn(getConnection());
-    if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 19) {
-      // Don't hold the connection handle in the fallback case
-      // to prevent a deadlock.
-      goto fallback;
-    }
-    conn->to << wopQueryMissing << targets;
-    conn.processStderr();
-    willBuild = readStorePaths<PathSet>(*this, conn->from);
-    willSubstitute = readStorePaths<PathSet>(*this, conn->from);
-    unknown = readStorePaths<PathSet>(*this, conn->from);
-    conn->from >> downloadSize >> narSize;
-    return;
-  }
-
-fallback:
-  return Store::queryMissing(targets, willBuild, willSubstitute, unknown,
-                             downloadSize, narSize);
-}
-
-void RemoteStore::connect() { auto conn(getConnection()); }
-
-unsigned int RemoteStore::getProtocol() {
-  auto conn(connections->get());
-  return conn->daemonVersion;
-}
-
-void RemoteStore::flushBadConnections() { connections->flushBad(); }
-
-RemoteStore::Connection::~Connection() {
-  try {
-    to.flush();
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-std::exception_ptr RemoteStore::Connection::processStderr(Sink* sink,
-                                                          Source* source) {
-  to.flush();
-
-  while (true) {
-    auto msg = readNum<uint64_t>(from);
-
-    if (msg == STDERR_WRITE) {
-      std::string s = readString(from);
-      if (sink == nullptr) {
-        throw Error("no sink");
-      }
-      (*sink)(s);
-    }
-
-    else if (msg == STDERR_READ) {
-      if (source == nullptr) {
-        throw Error("no source");
-      }
-      auto len = readNum<size_t>(from);
-      auto buf = std::make_unique<unsigned char[]>(len);
-      writeString(buf.get(), source->read(buf.get(), len), to);
-      to.flush();
-    }
-
-    else if (msg == STDERR_ERROR) {
-      std::string error = readString(from);
-      unsigned int status = readInt(from);
-      return std::make_exception_ptr(Error(status, error));
-    }
-
-    else if (msg == STDERR_NEXT) {
-      LOG(ERROR) << absl::StripTrailingAsciiWhitespace(readString(from));
-    }
-
-    else if (msg == STDERR_START_ACTIVITY) {
-      LOG(INFO) << readString(from);
-    }
-
-    else if (msg == STDERR_LAST) {
-      break;
-    }
-
-    else {
-      throw Error("got unknown message type %x from Nix daemon", msg);
-    }
-  }
-
-  return nullptr;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/remote-store.hh b/third_party/nix/src/libstore/remote-store.hh
deleted file mode 100644
index c360055b6e..0000000000
--- a/third_party/nix/src/libstore/remote-store.hh
+++ /dev/null
@@ -1,141 +0,0 @@
-#pragma once
-
-#include <limits>
-#include <string>
-
-#include "libstore/store-api.hh"
-
-namespace nix {
-
-class Pipe;
-class Pid;
-struct FdSink;
-struct FdSource;
-template <typename T>
-class Pool;
-struct ConnectionHandle;
-
-/* FIXME: RemoteStore is a misnomer - should be something like
-   DaemonStore. */
-class RemoteStore : public virtual Store {
- public:
-  const Setting<int> maxConnections{
-      (Store*)this, 1, "max-connections",
-      "maximum number of concurrent connections to the Nix daemon"};
-
-  const Setting<unsigned int> maxConnectionAge{
-      (Store*)this, std::numeric_limits<unsigned int>::max(),
-      "max-connection-age", "number of seconds to reuse a connection"};
-
-  virtual bool sameMachine() = 0;
-
-  RemoteStore(const Params& params);
-
-  /* Implementations of abstract store API methods. */
-
-  bool isValidPathUncached(const Path& path) override;
-
-  PathSet queryValidPaths(const PathSet& paths, SubstituteFlag maybeSubstitute =
-                                                    NoSubstitute) override;
-
-  PathSet queryAllValidPaths() override;
-
-  void queryPathInfoUncached(
-      const Path& path,
-      Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept override;
-
-  void queryReferrers(const Path& path, PathSet& referrers) override;
-
-  PathSet queryValidDerivers(const Path& path) override;
-
-  PathSet queryDerivationOutputs(const Path& path) override;
-
-  StringSet queryDerivationOutputNames(const Path& path) override;
-
-  Path queryPathFromHashPart(const std::string& hashPart) override;
-
-  PathSet querySubstitutablePaths(const PathSet& paths) override;
-
-  void querySubstitutablePathInfos(const PathSet& paths,
-                                   SubstitutablePathInfos& infos) override;
-
-  void addToStore(const ValidPathInfo& info, Source& source, RepairFlag repair,
-                  CheckSigsFlag checkSigs,
-                  std::shared_ptr<FSAccessor> accessor) override;
-
-  Path addToStore(const std::string& name, const Path& srcPath,
-                  bool recursive = true, HashType hashAlgo = htSHA256,
-                  PathFilter& filter = defaultPathFilter,
-                  RepairFlag repair = NoRepair) override;
-
-  Path addTextToStore(const std::string& name, const std::string& s,
-                      const PathSet& references, RepairFlag repair) override;
-
-  absl::Status buildPaths(std::ostream& log_sink, const PathSet& paths,
-                          BuildMode build_mode) override;
-
-  BuildResult buildDerivation(std::ostream& log_sink, const Path& drvPath,
-                              const BasicDerivation& drv,
-                              BuildMode buildMode) override;
-
-  void ensurePath(const Path& path) override;
-
-  void addTempRoot(const Path& path) override;
-
-  void addIndirectRoot(const Path& path) override;
-
-  void syncWithGC() override;
-
-  Roots findRoots(bool censor) override;
-
-  void collectGarbage(const GCOptions& options, GCResults& results) override;
-
-  void optimiseStore() override;
-
-  bool verifyStore(bool checkContents, RepairFlag repair) override;
-
-  void addSignatures(const Path& storePath, const StringSet& sigs) override;
-
-  void queryMissing(const PathSet& targets, PathSet& willBuild,
-                    PathSet& willSubstitute, PathSet& unknown,
-                    unsigned long long& downloadSize,
-                    unsigned long long& narSize) override;
-
-  void connect() override;
-
-  unsigned int getProtocol() override;
-
-  void flushBadConnections();
-
- protected:
-  struct Connection {
-    AutoCloseFD fd;
-    FdSink to;
-    FdSource from;
-    unsigned int daemonVersion;
-    std::chrono::time_point<std::chrono::steady_clock> startTime;
-
-    virtual ~Connection();
-
-    std::exception_ptr processStderr(Sink* sink = 0, Source* source = 0);
-  };
-
-  ref<Connection> openConnectionWrapper();
-
-  virtual ref<Connection> openConnection() = 0;
-
-  void initConnection(Connection& conn);
-
-  ref<Pool<Connection>> connections;
-
-  virtual void setOptions(Connection& conn);
-
-  ConnectionHandle getConnection();
-
-  friend struct ConnectionHandle;
-
- private:
-  std::atomic_bool failed{false};
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/rpc-store.cc b/third_party/nix/src/libstore/rpc-store.cc
deleted file mode 100644
index c29bd059de..0000000000
--- a/third_party/nix/src/libstore/rpc-store.cc
+++ /dev/null
@@ -1,549 +0,0 @@
-#include "rpc-store.hh"
-
-#include <algorithm>
-#include <filesystem>
-#include <memory>
-#include <optional>
-#include <ostream>
-#include <string_view>
-
-#include <absl/status/status.h>
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_format.h>
-#include <absl/strings/string_view.h>
-#include <glog/logging.h>
-#include <google/protobuf/empty.pb.h>
-#include <google/protobuf/util/time_util.h>
-#include <grpcpp/create_channel.h>
-#include <grpcpp/impl/codegen/async_unary_call.h>
-#include <grpcpp/impl/codegen/client_context.h>
-#include <grpcpp/impl/codegen/completion_queue.h>
-#include <grpcpp/impl/codegen/status.h>
-#include <grpcpp/impl/codegen/status_code_enum.h>
-#include <grpcpp/impl/codegen/sync_stream.h>
-#include <grpcpp/security/credentials.h>
-#include <sys/ucontext.h>
-
-#include "libproto/worker.grpc.pb.h"
-#include "libproto/worker.pb.h"
-#include "libstore/derivations.hh"
-#include "libstore/store-api.hh"
-#include "libstore/worker-protocol.hh"
-#include "libutil/archive.hh"
-#include "libutil/hash.hh"
-#include "libutil/proto.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-namespace store {
-
-// Should be set to the bandwidth delay product between the client and the
-// daemon. The current value, which should eventually be determined dynamically,
-// has currently been set to a developer's deskop computer, rounded up
-constexpr size_t kChunkSize = 1024 * 64;
-
-using google::protobuf::util::TimeUtil;
-using grpc::ClientContext;
-using nix::proto::WorkerService;
-
-static google::protobuf::Empty kEmpty;
-
-template <typename Request>
-class RPCSink : public BufferedSink {
- public:
-  using Writer = grpc::ClientWriter<Request>;
-  explicit RPCSink(std::unique_ptr<Writer>&& writer)
-      : writer_(std::move(writer)), good_(true) {}
-
-  bool good() override { return good_; }
-
-  void write(const unsigned char* data, size_t len) override {
-    Request req;
-    req.set_data(data, len);
-    if (!writer_->Write(req)) {
-      good_ = false;
-    }
-  }
-
-  ~RPCSink() override { flush(); }
-
-  grpc::Status Finish() {
-    flush();
-    return writer_->Finish();
-  }
-
- private:
-  std::unique_ptr<Writer> writer_;
-  bool good_;
-};
-
-// TODO(grfn): Obviously this should go away and be replaced by StatusOr... but
-// that would require refactoring the entire store api, which we don't feel like
-// doing right now. We should at some point though
-void const RpcStore::SuccessOrThrow(const grpc::Status& status,
-                                    const absl::string_view& call) const {
-  if (!status.ok()) {
-    auto uri = uri_.value_or("unknown URI");
-    switch (status.error_code()) {
-      case grpc::StatusCode::UNIMPLEMENTED:
-        throw Unsupported(
-            absl::StrFormat("operation %s is not supported by store at %s: %s",
-                            call, uri, status.error_message()));
-      default:
-        throw Error(absl::StrFormat(
-            "Rpc call %s to %s failed (%s): %s ", call, uri,
-            util::proto::GRPCStatusCodeDescription(status.error_code()),
-            status.error_message()));
-    }
-  }
-}
-
-bool RpcStore::isValidPathUncached(const Path& path) {
-  ClientContext ctx;
-  proto::IsValidPathResponse resp;
-  SuccessOrThrow(stub_->IsValidPath(&ctx, util::proto::StorePath(path), &resp),
-                 __FUNCTION__);
-  return resp.is_valid();
-}
-
-PathSet RpcStore::queryAllValidPaths() {
-  ClientContext ctx;
-  proto::StorePaths paths;
-  SuccessOrThrow(stub_->QueryAllValidPaths(&ctx, kEmpty, &paths), __FUNCTION__);
-  return util::proto::FillFrom<PathSet>(paths.paths());
-}
-
-PathSet RpcStore::queryValidPaths(const PathSet& paths,
-                                  SubstituteFlag maybeSubstitute) {
-  ClientContext ctx;
-  proto::StorePaths store_paths;
-  for (const auto& path : paths) {
-    store_paths.add_paths(path);
-  }
-  proto::StorePaths result_paths;
-  SuccessOrThrow(stub_->QueryValidPaths(&ctx, store_paths, &result_paths),
-                 __FUNCTION__);
-  return util::proto::FillFrom<PathSet>(result_paths.paths());
-}
-
-void RpcStore::queryPathInfoUncached(
-    const Path& path,
-    Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept {
-  ClientContext ctx;
-  proto::StorePath store_path;
-  store_path.set_path(path);
-
-  try {
-    proto::PathInfo path_info;
-    auto result = stub_->QueryPathInfo(&ctx, store_path, &path_info);
-    if (result.error_code() == grpc::INVALID_ARGUMENT) {
-      throw InvalidPath(absl::StrFormat("path '%s' is not valid", path));
-    }
-    SuccessOrThrow(result);
-
-    std::shared_ptr<ValidPathInfo> info;
-
-    if (!path_info.is_valid()) {
-      throw InvalidPath(absl::StrFormat("path '%s' is not valid", path));
-    }
-
-    info = std::make_shared<ValidPathInfo>();
-    info->path = path;
-    info->deriver = path_info.deriver().path();
-    if (!info->deriver.empty()) {
-      assertStorePath(info->deriver);
-    }
-    auto hash_ = Hash::deserialize(path_info.nar_hash(), htSHA256);
-    info->narHash = Hash::unwrap_throw(hash_);
-    info->references.insert(path_info.references().begin(),
-                            path_info.references().end());
-    info->registrationTime =
-        TimeUtil::TimestampToTimeT(path_info.registration_time());
-    info->narSize = path_info.nar_size();
-    info->ultimate = path_info.ultimate();
-    info->sigs.insert(path_info.sigs().begin(), path_info.sigs().end());
-    info->ca = path_info.ca();
-
-    callback(std::move(info));
-  } catch (...) {
-    callback.rethrow();
-  }
-}
-
-void RpcStore::queryReferrers(const Path& path, PathSet& referrers) {
-  ClientContext ctx;
-  proto::StorePaths paths;
-  SuccessOrThrow(
-      stub_->QueryReferrers(&ctx, util::proto::StorePath(path), &paths),
-      __FUNCTION__);
-  referrers.insert(paths.paths().begin(), paths.paths().end());
-}
-
-PathSet RpcStore::queryValidDerivers(const Path& path) {
-  ClientContext ctx;
-  proto::StorePaths paths;
-  SuccessOrThrow(
-      stub_->QueryValidDerivers(&ctx, util::proto::StorePath(path), &paths),
-      __FUNCTION__);
-  return util::proto::FillFrom<PathSet>(paths.paths());
-}
-
-PathSet RpcStore::queryDerivationOutputs(const Path& path) {
-  ClientContext ctx;
-  proto::StorePaths paths;
-  SuccessOrThrow(
-      stub_->QueryDerivationOutputs(&ctx, util::proto::StorePath(path), &paths),
-      __FUNCTION__);
-  return util::proto::FillFrom<PathSet>(paths.paths());
-}
-
-StringSet RpcStore::queryDerivationOutputNames(const Path& path) {
-  ClientContext ctx;
-  proto::DerivationOutputNames output_names;
-  SuccessOrThrow(stub_->QueryDerivationOutputNames(
-      &ctx, util::proto::StorePath(path), &output_names));
-  return util::proto::FillFrom<StringSet>(output_names.names());
-}
-
-Path RpcStore::queryPathFromHashPart(const std::string& hashPart) {
-  ClientContext ctx;
-  proto::StorePath path;
-  proto::HashPart proto_hash_part;
-  proto_hash_part.set_hash_part(hashPart);
-  SuccessOrThrow(stub_->QueryPathFromHashPart(&ctx, proto_hash_part, &path),
-                 __FUNCTION__);
-  return path.path();
-}
-
-PathSet RpcStore::querySubstitutablePaths(const PathSet& paths) {
-  ClientContext ctx;
-  proto::StorePaths result;
-  SuccessOrThrow(stub_->QuerySubstitutablePaths(
-      &ctx, util::proto::StorePaths(paths), &result));
-  return util::proto::FillFrom<PathSet>(result.paths());
-}
-
-void RpcStore::querySubstitutablePathInfos(const PathSet& paths,
-                                           SubstitutablePathInfos& infos) {
-  ClientContext ctx;
-  proto::SubstitutablePathInfos result;
-  SuccessOrThrow(stub_->QuerySubstitutablePathInfos(
-      &ctx, util::proto::StorePaths(paths), &result));
-
-  for (const auto& path_info : result.path_infos()) {
-    auto path = path_info.path().path();
-    SubstitutablePathInfo& info(infos[path]);
-    info.deriver = path_info.deriver().path();
-    if (!info.deriver.empty()) {
-      assertStorePath(info.deriver);
-    }
-    info.references = util::proto::FillFrom<PathSet>(path_info.references());
-    info.downloadSize = path_info.download_size();
-    info.narSize = path_info.nar_size();
-  }
-}
-
-void RpcStore::addToStore(const ValidPathInfo& info, Source& narSource,
-                          RepairFlag repair, CheckSigsFlag checkSigs,
-                          std::shared_ptr<FSAccessor> accessor) {
-  ClientContext ctx;
-  google::protobuf::Empty response;
-  auto writer = stub_->AddToStoreNar(&ctx, &response);
-
-  proto::AddToStoreNarRequest path_info_req;
-  path_info_req.mutable_path_info()->mutable_path()->set_path(info.path);
-  path_info_req.mutable_path_info()->mutable_deriver()->set_path(info.deriver);
-  path_info_req.mutable_path_info()->set_nar_hash(
-      info.narHash.to_string(Base16, false));
-  for (const auto& ref : info.references) {
-    path_info_req.mutable_path_info()->add_references(ref);
-  }
-  *path_info_req.mutable_path_info()->mutable_registration_time() =
-      TimeUtil::TimeTToTimestamp(info.registrationTime);
-  path_info_req.mutable_path_info()->set_nar_size(info.narSize);
-  path_info_req.mutable_path_info()->set_ultimate(info.ultimate);
-  for (const auto& sig : info.sigs) {
-    path_info_req.mutable_path_info()->add_sigs(sig);
-  }
-  path_info_req.mutable_path_info()->set_ca(info.ca);
-  path_info_req.mutable_path_info()->set_repair(repair);
-  path_info_req.mutable_path_info()->set_check_sigs(checkSigs);
-
-  if (!writer->Write(path_info_req)) {
-    throw Error("Could not write to nix daemon");
-  }
-
-  RPCSink sink(std::move(writer));
-  copyNAR(narSource, sink);
-  SuccessOrThrow(sink.Finish(), __FUNCTION__);
-}
-
-Path RpcStore::addToStore(const std::string& name, const Path& srcPath,
-                          bool recursive, HashType hashAlgo, PathFilter& filter,
-                          RepairFlag repair) {
-  if (repair != 0u) {
-    throw Error(
-        "repairing is not supported when building through the Nix daemon");
-  }
-
-  ClientContext ctx;
-  proto::StorePath response;
-  auto writer = stub_->AddToStore(&ctx, &response);
-
-  proto::AddToStoreRequest metadata_req;
-  metadata_req.mutable_meta()->set_base_name(name);
-  // TODO(grfn): what is fixed?
-  metadata_req.mutable_meta()->set_fixed(!(hashAlgo == htSHA256 && recursive));
-  metadata_req.mutable_meta()->set_recursive(recursive);
-  metadata_req.mutable_meta()->set_hash_type(HashTypeToProto(hashAlgo));
-
-  if (!writer->Write(metadata_req)) {
-    throw Error("Could not write to nix daemon");
-  }
-
-  RPCSink sink(std::move(writer));
-  dumpPath(std::filesystem::absolute(srcPath), sink);
-  sink.flush();
-  SuccessOrThrow(sink.Finish(), __FUNCTION__);
-
-  return response.path();
-}
-
-Path RpcStore::addTextToStore(const std::string& name,
-                              const std::string& content,
-                              const PathSet& references, RepairFlag repair) {
-  if (repair != 0u) {
-    throw Error(
-        "repairing is not supported when building through the Nix daemon");
-  }
-  ClientContext ctx;
-  proto::StorePath result;
-  auto writer = stub_->AddTextToStore(&ctx, &result);
-
-  proto::AddTextToStoreRequest meta;
-  meta.mutable_meta()->set_name(name);
-  meta.mutable_meta()->set_size(content.size());
-  for (const auto& ref : references) {
-    meta.mutable_meta()->add_references(ref);
-  }
-  writer->Write(meta);
-
-  for (int i = 0; i <= content.size(); i += kChunkSize) {
-    auto len = std::min(kChunkSize, content.size() - i);
-    proto::AddTextToStoreRequest data;
-    data.set_data(content.data() + i, len);
-    if (!writer->Write(data)) {
-      // Finish() below will error
-      break;
-    }
-  }
-
-  writer->WritesDone();
-  SuccessOrThrow(writer->Finish(), __FUNCTION__);
-  return result.path();
-}
-
-absl::Status RpcStore::buildPaths(std::ostream& log_sink, const PathSet& paths,
-                                  BuildMode build_mode) {
-  ClientContext ctx;
-  proto::BuildPathsRequest request;
-  for (const auto& path : paths) {
-    request.add_drvs(path);
-  }
-
-  google::protobuf::Empty response;
-  request.set_mode(nix::BuildModeToProto(build_mode));
-
-  std::unique_ptr<grpc::ClientReader<proto::BuildEvent>> reader =
-      stub_->BuildPaths(&ctx, request);
-
-  proto::BuildEvent event;
-  while (reader->Read(&event)) {
-    if (event.has_build_log()) {
-      // TODO(tazjin): Include .path()?
-      log_sink << event.build_log().line();
-    } else {
-      log_sink << "Building path: " << event.building_path().path()
-               << std::endl;
-    }
-
-    // has_result() is not in use in this call (for now)
-  }
-
-  return nix::util::proto::GRPCStatusToAbsl(reader->Finish());
-}
-
-BuildResult RpcStore::buildDerivation(std::ostream& log_sink,
-                                      const Path& drvPath,
-                                      const BasicDerivation& drv,
-                                      BuildMode buildMode) {
-  ClientContext ctx;
-  proto::BuildDerivationRequest request;
-  request.mutable_drv_path()->set_path(drvPath);
-  proto::Derivation proto_drv = drv.to_proto();
-  *request.mutable_derivation() = proto_drv;
-  request.set_build_mode(BuildModeToProto(buildMode));
-
-  std::unique_ptr<grpc::ClientReader<proto::BuildEvent>> reader =
-      stub_->BuildDerivation(&ctx, request);
-
-  std::optional<BuildResult> result;
-
-  proto::BuildEvent event;
-  while (reader->Read(&event)) {
-    if (event.has_build_log()) {
-      log_sink << event.build_log().line();
-    } else if (event.has_result()) {
-      result = BuildResult::FromProto(event.result());
-    }
-  }
-  SuccessOrThrow(reader->Finish(), __FUNCTION__);
-
-  if (!result.has_value()) {
-    throw Error("Invalid response from daemon for buildDerivation");
-  }
-  return result.value();
-}
-
-void RpcStore::ensurePath(const Path& path) {
-  ClientContext ctx;
-  google::protobuf::Empty response;
-  SuccessOrThrow(
-      stub_->EnsurePath(&ctx, util::proto::StorePath(path), &response),
-      __FUNCTION__);
-}
-
-void RpcStore::addTempRoot(const Path& path) {
-  ClientContext ctx;
-  google::protobuf::Empty response;
-  SuccessOrThrow(
-      stub_->AddTempRoot(&ctx, util::proto::StorePath(path), &response),
-      __FUNCTION__);
-}
-
-void RpcStore::addIndirectRoot(const Path& path) {
-  ClientContext ctx;
-  google::protobuf::Empty response;
-  SuccessOrThrow(
-      stub_->AddIndirectRoot(&ctx, util::proto::StorePath(path), &response),
-      __FUNCTION__);
-}
-
-void RpcStore::syncWithGC() {
-  ClientContext ctx;
-  google::protobuf::Empty response;
-  SuccessOrThrow(stub_->SyncWithGC(&ctx, kEmpty, &response), __FUNCTION__);
-}
-
-Roots RpcStore::findRoots(bool censor) {
-  ClientContext ctx;
-  proto::FindRootsResponse response;
-  SuccessOrThrow(stub_->FindRoots(&ctx, kEmpty, &response), __FUNCTION__);
-  Roots result;
-
-  for (const auto& [target, links] : response.roots()) {
-    auto link_paths =
-        util::proto::FillFrom<std::unordered_set<std::string>>(links.paths());
-    result.insert({target, link_paths});
-  }
-
-  return result;
-}
-
-void RpcStore::collectGarbage(const GCOptions& options, GCResults& results) {
-  ClientContext ctx;
-  proto::CollectGarbageRequest request;
-  request.set_action(options.ActionToProto());
-  for (const auto& path : options.pathsToDelete) {
-    request.add_paths_to_delete(path);
-  }
-  request.set_ignore_liveness(options.ignoreLiveness);
-  request.set_max_freed(options.maxFreed);
-
-  proto::CollectGarbageResponse response;
-  SuccessOrThrow(stub_->CollectGarbage(&ctx, request, &response), __FUNCTION__);
-
-  for (const auto& path : response.deleted_paths()) {
-    results.paths.insert(path);
-  }
-  results.bytesFreed = response.bytes_freed();
-}
-
-void RpcStore::optimiseStore() {
-  ClientContext ctx;
-  google::protobuf::Empty response;
-  SuccessOrThrow(stub_->OptimiseStore(&ctx, kEmpty, &response), __FUNCTION__);
-}
-
-bool RpcStore::verifyStore(bool checkContents, RepairFlag repair) {
-  ClientContext ctx;
-  proto::VerifyStoreRequest request;
-  request.set_check_contents(checkContents);
-  request.set_repair(repair);
-  proto::VerifyStoreResponse response;
-  SuccessOrThrow(stub_->VerifyStore(&ctx, request, &response), __FUNCTION__);
-  return response.errors();
-}
-
-void RpcStore::addSignatures(const Path& storePath, const StringSet& sigs) {
-  ClientContext ctx;
-  proto::AddSignaturesRequest request;
-  request.mutable_path()->set_path(storePath);
-  for (const auto& sig : sigs) {
-    request.mutable_sigs()->add_sigs(sig);
-  }
-  google::protobuf::Empty response;
-  SuccessOrThrow(stub_->AddSignatures(&ctx, request, &response), __FUNCTION__);
-}
-
-void RpcStore::queryMissing(const PathSet& targets, PathSet& willBuild,
-                            PathSet& willSubstitute, PathSet& unknown,
-                            unsigned long long& downloadSize,
-                            unsigned long long& narSize) {
-  ClientContext ctx;
-  proto::QueryMissingResponse response;
-  SuccessOrThrow(
-      stub_->QueryMissing(&ctx, util::proto::StorePaths(targets), &response),
-      __FUNCTION__);
-
-  willBuild = util::proto::FillFrom<PathSet>(response.will_build());
-  willSubstitute = util::proto::FillFrom<PathSet>(response.will_substitute());
-  unknown = util::proto::FillFrom<PathSet>(response.unknown());
-  downloadSize = response.download_size();
-  narSize = response.nar_size();
-}
-
-std::shared_ptr<std::string> RpcStore::getBuildLog(const Path& path) {
-  ClientContext ctx;
-  proto::BuildLog response;
-  SuccessOrThrow(
-      stub_->GetBuildLog(&ctx, util::proto::StorePath(path), &response),
-      __FUNCTION__);
-
-  auto build_log = response.build_log();
-  if (build_log.empty()) {
-    return nullptr;
-  }
-  return std::make_shared<std::string>(build_log);
-}
-
-unsigned int RpcStore::getProtocol() { return PROTOCOL_VERSION; }
-
-}  // namespace store
-
-constexpr std::string_view kUriScheme = "unix://";
-
-// TODO(grfn): Make this a function that we call from main rather than... this
-static RegisterStoreImplementation regStore([](const std::string& uri,
-                                               const Store::Params& params)
-                                                -> std::shared_ptr<Store> {
-  if (std::string(uri, 0, kUriScheme.size()) != kUriScheme) {
-    return nullptr;
-  }
-  auto channel = grpc::CreateChannel(uri, grpc::InsecureChannelCredentials());
-  return std::make_shared<store::RpcStore>(
-      uri, params, proto::WorkerService::NewStub(channel));
-});
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/rpc-store.hh b/third_party/nix/src/libstore/rpc-store.hh
deleted file mode 100644
index 679ceac7af..0000000000
--- a/third_party/nix/src/libstore/rpc-store.hh
+++ /dev/null
@@ -1,129 +0,0 @@
-#pragma once
-
-#include <absl/strings/string_view.h>
-
-#include "libproto/worker.grpc.pb.h"
-#include "libproto/worker.pb.h"
-#include "libstore/remote-store.hh"
-#include "libstore/store-api.hh"
-
-namespace nix::store {
-
-// TODO(grfn): Currently, since the RPCStore is only used for the connection to
-// the nix daemon over a unix socket, it inherits from the LocalFSStore since it
-// shares a filesystem with the daemon. This will not always be the case, at
-// which point we should tease these two things apart.
-class RpcStore : public LocalFSStore, public virtual Store {
- public:
-  RpcStore(const Params& params,
-           std::unique_ptr<nix::proto::WorkerService::Stub> stub)
-      : Store(params), LocalFSStore(params), stub_(std::move(stub)) {}
-
-  RpcStore(std::string uri, const Params& params,
-           std::unique_ptr<nix::proto::WorkerService::Stub> stub)
-      : Store(params),
-        LocalFSStore(params),
-        uri_(uri),
-        stub_(std::move(stub)) {}
-
-  std::string getUri() override {
-    if (uri_.has_value()) {
-      return uri_.value();
-    } else {
-      return "daemon";
-    }
-  };
-
-  virtual PathSet queryAllValidPaths() override;
-
-  virtual void queryReferrers(const Path& path, PathSet& referrers) override;
-
-  virtual PathSet queryValidDerivers(const Path& path) override;
-
-  virtual PathSet queryDerivationOutputs(const Path& path) override;
-
-  virtual StringSet queryDerivationOutputNames(const Path& path) override;
-
-  virtual Path queryPathFromHashPart(const std::string& hashPart) override;
-
-  virtual PathSet querySubstitutablePaths(const PathSet& paths) override;
-
-  virtual void querySubstitutablePathInfos(
-      const PathSet& paths, SubstitutablePathInfos& infos) override;
-
-  virtual bool wantMassQuery() override { return true; }
-
-  virtual void addToStore(const ValidPathInfo& info, Source& narSource,
-                          RepairFlag repair = NoRepair,
-                          CheckSigsFlag checkSigs = CheckSigs,
-                          std::shared_ptr<FSAccessor> accessor = 0) override;
-
-  virtual Path addToStore(const std::string& name, const Path& srcPath,
-                          bool recursive = true, HashType hashAlgo = htSHA256,
-                          PathFilter& filter = defaultPathFilter,
-                          RepairFlag repair = NoRepair) override;
-
-  virtual Path addTextToStore(const std::string& name, const std::string& s,
-                              const PathSet& references,
-                              RepairFlag repair = NoRepair) override;
-
-  absl::Status buildPaths(std::ostream& log_sink, const PathSet& paths,
-                          BuildMode build_mode) override;
-
-  virtual BuildResult buildDerivation(std::ostream& log_sink,
-                                      const Path& drvPath,
-                                      const BasicDerivation& drv,
-                                      BuildMode buildMode) override;
-
-  virtual void ensurePath(const Path& path) override;
-
-  virtual void addTempRoot(const Path& path) override;
-
-  virtual void addIndirectRoot(const Path& path) override;
-
-  virtual void syncWithGC() override;
-
-  virtual Roots findRoots(bool censor) override;
-
-  virtual void collectGarbage(const GCOptions& options,
-                              GCResults& results) override;
-
-  virtual void optimiseStore() override;
-
-  virtual bool verifyStore(bool checkContents,
-                           RepairFlag repair = NoRepair) override;
-
-  virtual void addSignatures(const Path& storePath,
-                             const StringSet& sigs) override;
-
-  virtual void queryMissing(const PathSet& targets, PathSet& willBuild,
-                            PathSet& willSubstitute, PathSet& unknown,
-                            unsigned long long& downloadSize,
-                            unsigned long long& narSize) override;
-
-  virtual std::shared_ptr<std::string> getBuildLog(const Path& path) override;
-
-  void connect() override{};
-
-  virtual unsigned int getProtocol() override;
-
- protected:
-  virtual bool isValidPathUncached(const Path& path) override;
-
-  virtual PathSet queryValidPaths(
-      const PathSet& paths,
-      SubstituteFlag maybeSubstitute = NoSubstitute) override;
-
-  virtual void queryPathInfoUncached(
-      const Path& path,
-      Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept override;
-
- private:
-  std::optional<std::string> uri_;
-  std::unique_ptr<nix::proto::WorkerService::Stub> stub_;
-
-  void const SuccessOrThrow(const grpc::Status& status,
-                            const absl::string_view& call = "") const;
-};
-
-}  // namespace nix::store
diff --git a/third_party/nix/src/libstore/s3-binary-cache-store.cc b/third_party/nix/src/libstore/s3-binary-cache-store.cc
deleted file mode 100644
index 0c13039b52..0000000000
--- a/third_party/nix/src/libstore/s3-binary-cache-store.cc
+++ /dev/null
@@ -1,431 +0,0 @@
-#if ENABLE_S3
-
-#include "libstore/s3-binary-cache-store.hh"
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/match.h>
-#include <aws/core/Aws.h>
-#include <aws/core/VersionConfig.h>
-#include <aws/core/auth/AWSCredentialsProvider.h>
-#include <aws/core/auth/AWSCredentialsProviderChain.h>
-#include <aws/core/client/ClientConfiguration.h>
-#include <aws/core/client/DefaultRetryStrategy.h>
-#include <aws/core/utils/logging/FormattedLogSystem.h>
-#include <aws/core/utils/logging/LogMacros.h>
-#include <aws/core/utils/threading/Executor.h>
-#include <aws/s3/S3Client.h>
-#include <aws/s3/model/GetObjectRequest.h>
-#include <aws/s3/model/HeadObjectRequest.h>
-#include <aws/s3/model/ListObjectsRequest.h>
-#include <aws/s3/model/PutObjectRequest.h>
-#include <aws/transfer/TransferManager.h>
-
-#include "libstore/download.hh"
-#include "libstore/globals.hh"
-#include "libstore/nar-info-disk-cache.hh"
-#include "libstore/nar-info.hh"
-#include "libstore/s3.hh"
-#include "libutil/compression.hh"
-#include "libutil/istringstream_nocopy.hh"
-
-using namespace Aws::Transfer;
-
-namespace nix {
-
-struct S3Error : public Error {
-  Aws::S3::S3Errors err;
-  S3Error(Aws::S3::S3Errors err, const FormatOrString& fs)
-      : Error(fs), err(err){};
-};
-
-/* Helper: given an Outcome<R, E>, return R in case of success, or
-   throw an exception in case of an error. */
-template <typename R, typename E>
-R&& checkAws(const FormatOrString& fs, Aws::Utils::Outcome<R, E>&& outcome) {
-  if (!outcome.IsSuccess())
-    throw S3Error(outcome.GetError().GetErrorType(),
-                  fs.s + ": " + outcome.GetError().GetMessage());
-  return outcome.GetResultWithOwnership();
-}
-
-class AwsLogger : public Aws::Utils::Logging::FormattedLogSystem {
-  using Aws::Utils::Logging::FormattedLogSystem::FormattedLogSystem;
-
-  void ProcessFormattedStatement(Aws::String&& statement) override {
-    debug("AWS: %s", absl::StripTrailingAsciiWhitespace(statement));
-  }
-};
-
-static void initAWS() {
-  static std::once_flag flag;
-  std::call_once(flag, []() {
-    Aws::SDKOptions options;
-
-    /* We install our own OpenSSL locking function (see
-       shared.cc), so don't let aws-sdk-cpp override it. */
-    options.cryptoOptions.initAndCleanupOpenSSL = false;
-
-    if (verbosity >= lvlDebug) {
-      options.loggingOptions.logLevel =
-          verbosity == lvlDebug ? Aws::Utils::Logging::LogLevel::Debug
-                                : Aws::Utils::Logging::LogLevel::Trace;
-      options.loggingOptions.logger_create_fn = [options]() {
-        return std::make_shared<AwsLogger>(options.loggingOptions.logLevel);
-      };
-    }
-
-    Aws::InitAPI(options);
-  });
-}
-
-S3Helper::S3Helper(const std::string& profile, const std::string& region,
-                   const std::string& scheme, const std::string& endpoint)
-    : config(makeConfig(region, scheme, endpoint)),
-      client(make_ref<Aws::S3::S3Client>(
-          profile == ""
-              ? std::dynamic_pointer_cast<Aws::Auth::AWSCredentialsProvider>(
-                    std::make_shared<
-                        Aws::Auth::DefaultAWSCredentialsProviderChain>())
-              : std::dynamic_pointer_cast<Aws::Auth::AWSCredentialsProvider>(
-                    std::make_shared<
-                        Aws::Auth::ProfileConfigFileAWSCredentialsProvider>(
-                        profile.c_str())),
-          *config,
-// FIXME: https://github.com/aws/aws-sdk-cpp/issues/759
-#if AWS_VERSION_MAJOR == 1 && AWS_VERSION_MINOR < 3
-          false,
-#else
-          Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never,
-#endif
-          endpoint.empty())) {
-}
-
-/* Log AWS retries. */
-class RetryStrategy : public Aws::Client::DefaultRetryStrategy {
-  bool ShouldRetry(const Aws::Client::AWSError<Aws::Client::CoreErrors>& error,
-                   long attemptedRetries) const override {
-    auto retry =
-        Aws::Client::DefaultRetryStrategy::ShouldRetry(error, attemptedRetries);
-    if (retry)
-      printError("AWS error '%s' (%s), will retry in %d ms",
-                 error.GetExceptionName(), error.GetMessage(),
-                 CalculateDelayBeforeNextRetry(error, attemptedRetries));
-    return retry;
-  }
-};
-
-ref<Aws::Client::ClientConfiguration> S3Helper::makeConfig(
-    const std::string& region, const std::string& scheme,
-    const std::string& endpoint) {
-  initAWS();
-  auto res = make_ref<Aws::Client::ClientConfiguration>();
-  res->region = region;
-  if (!scheme.empty()) {
-    res->scheme = Aws::Http::SchemeMapper::FromString(scheme.c_str());
-  }
-  if (!endpoint.empty()) {
-    res->endpointOverride = endpoint;
-  }
-  res->requestTimeoutMs = 600 * 1000;
-  res->connectTimeoutMs = 5 * 1000;
-  res->retryStrategy = std::make_shared<RetryStrategy>();
-  res->caFile = settings.caFile;
-  return res;
-}
-
-S3Helper::DownloadResult S3Helper::getObject(const std::string& bucketName,
-                                             const std::string& key) {
-  debug("fetching 's3://%s/%s'...", bucketName, key);
-
-  auto request =
-      Aws::S3::Model::GetObjectRequest().WithBucket(bucketName).WithKey(key);
-
-  request.SetResponseStreamFactory(
-      [&]() { return Aws::New<std::stringstream>("STRINGSTREAM"); });
-
-  DownloadResult res;
-
-  auto now1 = std::chrono::steady_clock::now();
-
-  try {
-    auto result = checkAws(fmt("AWS error fetching '%s'", key),
-                           client->GetObject(request));
-
-    res.data =
-        decompress(result.GetContentEncoding(),
-                   dynamic_cast<std::stringstream&>(result.GetBody()).str());
-
-  } catch (S3Error& e) {
-    if (e.err != Aws::S3::S3Errors::NO_SUCH_KEY) {
-      throw;
-    }
-  }
-
-  auto now2 = std::chrono::steady_clock::now();
-
-  res.durationMs =
-      std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1)
-          .count();
-
-  return res;
-}
-
-struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore {
-  const Setting<std::string> profile{
-      this, "", "profile", "The name of the AWS configuration profile to use."};
-  const Setting<std::string> region{
-      this, Aws::Region::US_EAST_1, "region", {"aws-region"}};
-  const Setting<std::string> scheme{
-      this, "", "scheme",
-      "The scheme to use for S3 requests, https by default."};
-  const Setting<std::string> endpoint{
-      this, "", "endpoint",
-      "An optional override of the endpoint to use when talking to S3."};
-  const Setting<std::string> narinfoCompression{
-      this, "", "narinfo-compression", "compression method for .narinfo files"};
-  const Setting<std::string> lsCompression{this, "", "ls-compression",
-                                           "compression method for .ls files"};
-  const Setting<std::string> logCompression{
-      this, "", "log-compression", "compression method for log/* files"};
-  const Setting<bool> multipartUpload{this, false, "multipart-upload",
-                                      "whether to use multi-part uploads"};
-  const Setting<uint64_t> bufferSize{
-      this, 5 * 1024 * 1024, "buffer-size",
-      "size (in bytes) of each part in multi-part uploads"};
-
-  std::string bucketName;
-
-  Stats stats;
-
-  S3Helper s3Helper;
-
-  S3BinaryCacheStoreImpl(const Params& params, const std::string& bucketName)
-      : S3BinaryCacheStore(params),
-        bucketName(bucketName),
-        s3Helper(profile, region, scheme, endpoint) {
-    diskCache = getNarInfoDiskCache();
-  }
-
-  std::string getUri() override { return "s3://" + bucketName; }
-
-  void init() override {
-    if (!diskCache->cacheExists(getUri(), wantMassQuery_, priority)) {
-      BinaryCacheStore::init();
-
-      diskCache->createCache(getUri(), storeDir, wantMassQuery_, priority);
-    }
-  }
-
-  const Stats& getS3Stats() override { return stats; }
-
-  /* This is a specialisation of isValidPath() that optimistically
-     fetches the .narinfo file, rather than first checking for its
-     existence via a HEAD request. Since .narinfos are small, doing
-     a GET is unlikely to be slower than HEAD. */
-  bool isValidPathUncached(const Path& storePath) override {
-    try {
-      queryPathInfo(storePath);
-      return true;
-    } catch (InvalidPath& e) {
-      return false;
-    }
-  }
-
-  bool fileExists(const std::string& path) override {
-    stats.head++;
-
-    auto res = s3Helper.client->HeadObject(Aws::S3::Model::HeadObjectRequest()
-                                               .WithBucket(bucketName)
-                                               .WithKey(path));
-
-    if (!res.IsSuccess()) {
-      auto& error = res.GetError();
-      if (error.GetErrorType() == Aws::S3::S3Errors::RESOURCE_NOT_FOUND ||
-          error.GetErrorType() == Aws::S3::S3Errors::NO_SUCH_KEY
-          // If bucket listing is disabled, 404s turn into 403s
-          || error.GetErrorType() == Aws::S3::S3Errors::ACCESS_DENIED)
-        return false;
-      throw Error(format("AWS error fetching '%s': %s") % path %
-                  error.GetMessage());
-    }
-
-    return true;
-  }
-
-  std::shared_ptr<TransferManager> transferManager;
-  std::once_flag transferManagerCreated;
-
-  void uploadFile(const std::string& path, const std::string& data,
-                  const std::string& mimeType,
-                  const std::string& contentEncoding) {
-    auto stream = std::make_shared<istringstream_nocopy>(data);
-
-    auto maxThreads = std::thread::hardware_concurrency();
-
-    static std::shared_ptr<Aws::Utils::Threading::PooledThreadExecutor>
-        executor =
-            std::make_shared<Aws::Utils::Threading::PooledThreadExecutor>(
-                maxThreads);
-
-    std::call_once(transferManagerCreated, [&]() {
-      if (multipartUpload) {
-        TransferManagerConfiguration transferConfig(executor.get());
-
-        transferConfig.s3Client = s3Helper.client;
-        transferConfig.bufferSize = bufferSize;
-
-        transferConfig.uploadProgressCallback =
-            [](const TransferManager* transferManager,
-               const std::shared_ptr<const TransferHandle>& transferHandle) {
-              // FIXME: find a way to properly abort the multipart upload.
-              // checkInterrupt();
-              debug("upload progress ('%s'): '%d' of '%d' bytes",
-                    transferHandle->GetKey(),
-                    transferHandle->GetBytesTransferred(),
-                    transferHandle->GetBytesTotalSize());
-            };
-
-        transferManager = TransferManager::Create(transferConfig);
-      }
-    });
-
-    auto now1 = std::chrono::steady_clock::now();
-
-    if (transferManager) {
-      if (contentEncoding != "")
-        throw Error(
-            "setting a content encoding is not supported with S3 multi-part "
-            "uploads");
-
-      std::shared_ptr<TransferHandle> transferHandle =
-          transferManager->UploadFile(stream, bucketName, path, mimeType,
-                                      Aws::Map<Aws::String, Aws::String>(),
-                                      nullptr /*, contentEncoding */);
-
-      transferHandle->WaitUntilFinished();
-
-      if (transferHandle->GetStatus() == TransferStatus::FAILED)
-        throw Error("AWS error: failed to upload 's3://%s/%s': %s", bucketName,
-                    path, transferHandle->GetLastError().GetMessage());
-
-      if (transferHandle->GetStatus() != TransferStatus::COMPLETED)
-        throw Error(
-            "AWS error: transfer status of 's3://%s/%s' in unexpected state",
-            bucketName, path);
-
-    } else {
-      auto request = Aws::S3::Model::PutObjectRequest()
-                         .WithBucket(bucketName)
-                         .WithKey(path);
-
-      request.SetContentType(mimeType);
-
-      if (contentEncoding != "") {
-        request.SetContentEncoding(contentEncoding);
-      }
-
-      auto stream = std::make_shared<istringstream_nocopy>(data);
-
-      request.SetBody(stream);
-
-      auto result = checkAws(fmt("AWS error uploading '%s'", path),
-                             s3Helper.client->PutObject(request));
-    }
-
-    auto now2 = std::chrono::steady_clock::now();
-
-    auto duration =
-        std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1)
-            .count();
-
-    printInfo(format("uploaded 's3://%1%/%2%' (%3% bytes) in %4% ms") %
-              bucketName % path % data.size() % duration);
-
-    stats.putTimeMs += duration;
-    stats.putBytes += data.size();
-    stats.put++;
-  }
-
-  void upsertFile(const std::string& path, const std::string& data,
-                  const std::string& mimeType) override {
-    if (narinfoCompression != "" && absl::EndsWith(path, ".narinfo"))
-      uploadFile(path, *compress(narinfoCompression, data), mimeType,
-                 narinfoCompression);
-    else if (lsCompression != "" && absl::EndsWith(path, ".ls"))
-      uploadFile(path, *compress(lsCompression, data), mimeType, lsCompression);
-    else if (logCompression != "" && absl::StartsWith(path, "log/"))
-      uploadFile(path, *compress(logCompression, data), mimeType,
-                 logCompression);
-    else
-      uploadFile(path, data, mimeType, "");
-  }
-
-  void getFile(const std::string& path, Sink& sink) override {
-    stats.get++;
-
-    // FIXME: stream output to sink.
-    auto res = s3Helper.getObject(bucketName, path);
-
-    stats.getBytes += res.data ? res.data->size() : 0;
-    stats.getTimeMs += res.durationMs;
-
-    if (res.data) {
-      printTalkative("downloaded 's3://%s/%s' (%d bytes) in %d ms", bucketName,
-                     path, res.data->size(), res.durationMs);
-
-      sink((unsigned char*)res.data->data(), res.data->size());
-    } else
-      throw NoSuchBinaryCacheFile(
-          "file '%s' does not exist in binary cache '%s'", path, getUri());
-  }
-
-  PathSet queryAllValidPaths() override {
-    PathSet paths;
-    std::string marker;
-
-    do {
-      debug(format("listing bucket 's3://%s' from key '%s'...") % bucketName %
-            marker);
-
-      auto res = checkAws(
-          format("AWS error listing bucket '%s'") % bucketName,
-          s3Helper.client->ListObjects(Aws::S3::Model::ListObjectsRequest()
-                                           .WithBucket(bucketName)
-                                           .WithDelimiter("/")
-                                           .WithMarker(marker)));
-
-      auto& contents = res.GetContents();
-
-      debug(format("got %d keys, next marker '%s'") % contents.size() %
-            res.GetNextMarker());
-
-      for (auto object : contents) {
-        auto& key = object.GetKey();
-        if (key.size() != 40 || !absl::EndsWith(key, ".narinfo")) {
-          continue;
-        }
-        paths.insert(storeDir + "/" + key.substr(0, key.size() - 8));
-      }
-
-      marker = res.GetNextMarker();
-    } while (!marker.empty());
-
-    return paths;
-  }
-};
-
-static RegisterStoreImplementation regStore(
-    [](const std::string& uri,
-       const Store::Params& params) -> std::shared_ptr<Store> {
-      if (std::string(uri, 0, 5) != "s3://") {
-        return 0;
-      }
-      auto store =
-          std::make_shared<S3BinaryCacheStoreImpl>(params, std::string(uri, 5));
-      store->init();
-      return store;
-    });
-
-}  // namespace nix
-
-#endif
diff --git a/third_party/nix/src/libstore/s3-binary-cache-store.hh b/third_party/nix/src/libstore/s3-binary-cache-store.hh
deleted file mode 100644
index 3d0d0b3c44..0000000000
--- a/third_party/nix/src/libstore/s3-binary-cache-store.hh
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include <atomic>
-
-#include "libstore/binary-cache-store.hh"
-
-namespace nix {
-
-class S3BinaryCacheStore : public BinaryCacheStore {
- protected:
-  S3BinaryCacheStore(const Params& params) : BinaryCacheStore(params) {}
-
- public:
-  struct Stats {
-    std::atomic<uint64_t> put{0};
-    std::atomic<uint64_t> putBytes{0};
-    std::atomic<uint64_t> putTimeMs{0};
-    std::atomic<uint64_t> get{0};
-    std::atomic<uint64_t> getBytes{0};
-    std::atomic<uint64_t> getTimeMs{0};
-    std::atomic<uint64_t> head{0};
-  };
-
-  virtual const Stats& getS3Stats() = 0;
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/s3.hh b/third_party/nix/src/libstore/s3.hh
deleted file mode 100644
index 4f1852dc3d..0000000000
--- a/third_party/nix/src/libstore/s3.hh
+++ /dev/null
@@ -1,42 +0,0 @@
-#pragma once
-
-#if ENABLE_S3
-
-#include "libutil/ref.hh"
-
-namespace Aws {
-namespace Client {
-class ClientConfiguration;
-}
-}  // namespace Aws
-namespace Aws {
-namespace S3 {
-class S3Client;
-}
-}  // namespace Aws
-
-namespace nix {
-
-struct S3Helper {
-  ref<Aws::Client::ClientConfiguration> config;
-  ref<Aws::S3::S3Client> client;
-
-  S3Helper(const std::string& profile, const std::string& region,
-           const std::string& scheme, const std::string& endpoint);
-
-  ref<Aws::Client::ClientConfiguration> makeConfig(const std::string& region,
-                                                   const std::string& scheme,
-                                                   const std::string& endpoint);
-
-  struct DownloadResult {
-    std::shared_ptr<std::string> data;
-    unsigned int durationMs;
-  };
-
-  DownloadResult getObject(const std::string& bucketName,
-                           const std::string& key);
-};
-
-}  // namespace nix
-
-#endif
diff --git a/third_party/nix/src/libstore/sandbox-defaults.sb b/third_party/nix/src/libstore/sandbox-defaults.sb
deleted file mode 100644
index 0299d1ee45..0000000000
--- a/third_party/nix/src/libstore/sandbox-defaults.sb
+++ /dev/null
@@ -1,87 +0,0 @@
-(define TMPDIR (param "_GLOBAL_TMP_DIR"))
-
-(deny default)
-
-; Disallow creating setuid/setgid binaries, since that
-; would allow breaking build user isolation.
-(deny file-write-setugid)
-
-; Allow forking.
-(allow process-fork)
-
-; Allow reading system information like #CPUs, etc.
-(allow sysctl-read)
-
-; Allow POSIX semaphores and shared memory.
-(allow ipc-posix*)
-
-; Allow socket creation.
-(allow system-socket)
-
-; Allow sending signals within the sandbox.
-(allow signal (target same-sandbox))
-
-; Allow getpwuid.
-(allow mach-lookup (global-name "com.apple.system.opendirectoryd.libinfo"))
-
-; Access to /tmp.
-; The network-outbound/network-inbound ones are for unix domain sockets, which
-; we allow access to in TMPDIR (but if we allow them more broadly, you could in
-; theory escape the sandbox)
-(allow file* process-exec network-outbound network-inbound
-       (literal "/tmp") (subpath TMPDIR))
-
-; Some packages like to read the system version.
-(allow file-read* (literal "/System/Library/CoreServices/SystemVersion.plist"))
-
-; Without this line clang cannot write to /dev/null, breaking some configure tests.
-(allow file-read-metadata (literal "/dev"))
-
-; Many packages like to do local networking in their test suites, but let's only
-; allow it if the package explicitly asks for it.
-(if (param "_ALLOW_LOCAL_NETWORKING")
-    (begin
-      (allow network* (local ip) (local tcp) (local udp))
-
-      ; Allow access to /etc/resolv.conf (which is a symlink to
-      ; /private/var/run/resolv.conf).
-      ; TODO: deduplicate with sandbox-network.sb
-      (allow file-read-metadata
-             (literal "/var")
-             (literal "/etc")
-             (literal "/etc/resolv.conf")
-             (literal "/private/etc/resolv.conf"))
-
-      (allow file-read*
-             (literal "/private/var/run/resolv.conf"))
-
-      ; Allow DNS lookups. This is even needed for localhost, which lots of tests rely on
-      (allow file-read-metadata (literal "/etc/hosts"))
-      (allow file-read*         (literal "/private/etc/hosts"))
-      (allow network-outbound (remote unix-socket (path-literal "/private/var/run/mDNSResponder")))))
-
-; Standard devices.
-(allow file*
-       (literal "/dev/null")
-       (literal "/dev/random")
-       (literal "/dev/stdin")
-       (literal "/dev/stdout")
-       (literal "/dev/tty")
-       (literal "/dev/urandom")
-       (literal "/dev/zero")
-       (subpath "/dev/fd"))
-
-; Does nothing, but reduces build noise.
-(allow file* (literal "/dev/dtracehelper"))
-
-; Allow access to zoneinfo since libSystem needs it.
-(allow file-read* (subpath "/usr/share/zoneinfo"))
-
-(allow file-read* (subpath "/usr/share/locale"))
-
-; This is mostly to get more specific log messages when builds try to
-; access something in /etc or /var.
-(allow file-read-metadata
-       (literal "/etc")
-       (literal "/var")
-       (literal "/private/var/tmp"))
diff --git a/third_party/nix/src/libstore/sandbox-minimal.sb b/third_party/nix/src/libstore/sandbox-minimal.sb
deleted file mode 100644
index 65f5108b39..0000000000
--- a/third_party/nix/src/libstore/sandbox-minimal.sb
+++ /dev/null
@@ -1,5 +0,0 @@
-(allow default)
-
-; Disallow creating setuid/setgid binaries, since that
-; would allow breaking build user isolation.
-(deny file-write-setugid)
diff --git a/third_party/nix/src/libstore/sandbox-network.sb b/third_party/nix/src/libstore/sandbox-network.sb
deleted file mode 100644
index 56beec761f..0000000000
--- a/third_party/nix/src/libstore/sandbox-network.sb
+++ /dev/null
@@ -1,16 +0,0 @@
-; Allow local and remote network traffic.
-(allow network* (local ip) (remote ip))
-
-; Allow access to /etc/resolv.conf (which is a symlink to
-; /private/var/run/resolv.conf).
-(allow file-read-metadata
-       (literal "/var")
-       (literal "/etc")
-       (literal "/etc/resolv.conf")
-       (literal "/private/etc/resolv.conf"))
-
-(allow file-read*
-       (literal "/private/var/run/resolv.conf"))
-
-; Allow DNS lookups.
-(allow network-outbound (remote unix-socket (path-literal "/private/var/run/mDNSResponder")))
diff --git a/third_party/nix/src/libstore/schema.sql b/third_party/nix/src/libstore/schema.sql
deleted file mode 100644
index 09c71a2b8d..0000000000
--- a/third_party/nix/src/libstore/schema.sql
+++ /dev/null
@@ -1,42 +0,0 @@
-create table if not exists ValidPaths (
-    id               integer primary key autoincrement not null,
-    path             text unique not null,
-    hash             text not null,
-    registrationTime integer not null,
-    deriver          text,
-    narSize          integer,
-    ultimate         integer, -- null implies "false"
-    sigs             text, -- space-separated
-    ca               text -- if not null, an assertion that the path is content-addressed; see ValidPathInfo
-);
-
-create table if not exists Refs (
-    referrer  integer not null,
-    reference integer not null,
-    primary key (referrer, reference),
-    foreign key (referrer) references ValidPaths(id) on delete cascade,
-    foreign key (reference) references ValidPaths(id) on delete restrict
-);
-
-create index if not exists IndexReferrer on Refs(referrer);
-create index if not exists IndexReference on Refs(reference);
-
--- Paths can refer to themselves, causing a tuple (N, N) in the Refs
--- table.  This causes a deletion of the corresponding row in
--- ValidPaths to cause a foreign key constraint violation (due to `on
--- delete restrict' on the `reference' column).  Therefore, explicitly
--- get rid of self-references.
-create trigger if not exists DeleteSelfRefs before delete on ValidPaths
-  begin
-    delete from Refs where referrer = old.id and reference = old.id;
-  end;
-
-create table if not exists DerivationOutputs (
-    drv  integer not null,
-    id   text not null, -- symbolic output id, usually "out"
-    path text not null,
-    primary key (drv, id),
-    foreign key (drv) references ValidPaths(id) on delete cascade
-);
-
-create index if not exists IndexDerivationOutputs on DerivationOutputs(path);
diff --git a/third_party/nix/src/libstore/serve-protocol.hh b/third_party/nix/src/libstore/serve-protocol.hh
deleted file mode 100644
index 04c92e63f6..0000000000
--- a/third_party/nix/src/libstore/serve-protocol.hh
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-namespace nix {
-
-#define SERVE_MAGIC_1 0x390c9deb
-#define SERVE_MAGIC_2 0x5452eecb
-
-#define SERVE_PROTOCOL_VERSION 0x205
-#define GET_PROTOCOL_MAJOR(x) ((x)&0xff00)
-#define GET_PROTOCOL_MINOR(x) ((x)&0x00ff)
-
-using ServeCommand = enum {
-  cmdQueryValidPaths = 1,
-  cmdQueryPathInfos = 2,
-  cmdDumpStorePath = 3,
-  cmdImportPaths = 4,
-  cmdExportPaths = 5,
-  cmdBuildPaths = 6,
-  cmdQueryClosure = 7,
-  cmdBuildDerivation = 8,
-  cmdAddToStoreNar = 9,
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/sqlite.cc b/third_party/nix/src/libstore/sqlite.cc
deleted file mode 100644
index 0fb32326f5..0000000000
--- a/third_party/nix/src/libstore/sqlite.cc
+++ /dev/null
@@ -1,195 +0,0 @@
-#include "libstore/sqlite.hh"
-
-#include <atomic>
-
-#include <glog/logging.h>
-#include <sqlite3.h>
-
-#include "libutil/util.hh"
-
-namespace nix {
-
-[[noreturn]] void throwSQLiteError(sqlite3* db, const FormatOrString& fs) {
-  int err = sqlite3_errcode(db);
-  int exterr = sqlite3_extended_errcode(db);
-
-  auto path = sqlite3_db_filename(db, nullptr);
-  if (path == nullptr) {
-    path = "(in-memory)";
-  }
-
-  if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
-    throw SQLiteBusy(
-        err == SQLITE_PROTOCOL
-            ? fmt("SQLite database '%s' is busy (SQLITE_PROTOCOL)", path)
-            : fmt("SQLite database '%s' is busy", path));
-  }
-  throw SQLiteError("%s: %s (in '%s')", fs.s, sqlite3_errstr(exterr), path);
-}
-
-SQLite::SQLite(const Path& path) {
-  if (sqlite3_open_v2(path.c_str(), &db,
-                      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
-                      nullptr) != SQLITE_OK) {
-    throw Error(format("cannot open SQLite database '%s'") % path);
-  }
-}
-
-SQLite::~SQLite() {
-  try {
-    if ((db != nullptr) && sqlite3_close(db) != SQLITE_OK) {
-      throwSQLiteError(db, "closing database");
-    }
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-void SQLite::exec(const std::string& stmt) {
-  retrySQLite<void>([&]() {
-    if (sqlite3_exec(db, stmt.c_str(), nullptr, nullptr, nullptr) !=
-        SQLITE_OK) {
-      throwSQLiteError(db, format("executing SQLite statement '%s'") % stmt);
-    }
-  });
-}
-
-void SQLiteStmt::create(sqlite3* db, const std::string& sql) {
-  checkInterrupt();
-  assert(!stmt);
-  if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
-    throwSQLiteError(db, fmt("creating statement '%s'", sql));
-  }
-  this->db = db;
-  this->sql = sql;
-}
-
-SQLiteStmt::~SQLiteStmt() {
-  try {
-    if ((stmt != nullptr) && sqlite3_finalize(stmt) != SQLITE_OK) {
-      throwSQLiteError(db, fmt("finalizing statement '%s'", sql));
-    }
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-SQLiteStmt::Use::Use(SQLiteStmt& stmt) : stmt(stmt) {
-  assert(stmt.stmt);
-  /* Note: sqlite3_reset() returns the error code for the most
-     recent call to sqlite3_step().  So ignore it. */
-  sqlite3_reset(stmt);
-}
-
-SQLiteStmt::Use::~Use() { sqlite3_reset(stmt); }
-
-SQLiteStmt::Use& SQLiteStmt::Use::operator()(const std::string& value,
-                                             bool notNull) {
-  if (notNull) {
-    if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1,
-                          SQLITE_TRANSIENT) != SQLITE_OK) {
-      throwSQLiteError(stmt.db, "binding argument");
-    }
-  } else {
-    bind();
-  }
-  return *this;
-}
-
-SQLiteStmt::Use& SQLiteStmt::Use::operator()(int64_t value, bool notNull) {
-  if (notNull) {
-    if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK) {
-      throwSQLiteError(stmt.db, "binding argument");
-    }
-  } else {
-    bind();
-  }
-  return *this;
-}
-
-SQLiteStmt::Use& SQLiteStmt::Use::bind() {
-  if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK) {
-    throwSQLiteError(stmt.db, "binding argument");
-  }
-  return *this;
-}
-
-int SQLiteStmt::Use::step() { return sqlite3_step(stmt); }
-
-void SQLiteStmt::Use::exec() {
-  int r = step();
-  assert(r != SQLITE_ROW);
-  if (r != SQLITE_DONE) {
-    throwSQLiteError(stmt.db, fmt("executing SQLite statement '%s'", stmt.sql));
-  }
-}
-
-bool SQLiteStmt::Use::next() {
-  int r = step();
-  if (r != SQLITE_DONE && r != SQLITE_ROW) {
-    throwSQLiteError(stmt.db, fmt("executing SQLite query '%s'", stmt.sql));
-  }
-  return r == SQLITE_ROW;
-}
-
-std::string SQLiteStmt::Use::getStr(int col) {
-  auto s = reinterpret_cast<const char*>(sqlite3_column_text(stmt, col));
-  assert(s);
-  return s;
-}
-
-int64_t SQLiteStmt::Use::getInt(int col) {
-  // FIXME: detect nulls?
-  return sqlite3_column_int64(stmt, col);
-}
-
-bool SQLiteStmt::Use::isNull(int col) {
-  return sqlite3_column_type(stmt, col) == SQLITE_NULL;
-}
-
-SQLiteTxn::SQLiteTxn(sqlite3* db) {
-  this->db = db;
-  if (sqlite3_exec(db, "begin;", nullptr, nullptr, nullptr) != SQLITE_OK) {
-    throwSQLiteError(db, "starting transaction");
-  }
-  active = true;
-}
-
-void SQLiteTxn::commit() {
-  if (sqlite3_exec(db, "commit;", nullptr, nullptr, nullptr) != SQLITE_OK) {
-    throwSQLiteError(db, "committing transaction");
-  }
-  active = false;
-}
-
-SQLiteTxn::~SQLiteTxn() {
-  try {
-    if (active &&
-        sqlite3_exec(db, "rollback;", nullptr, nullptr, nullptr) != SQLITE_OK) {
-      throwSQLiteError(db, "aborting transaction");
-    }
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-void handleSQLiteBusy(const SQLiteBusy& e) {
-  static std::atomic<time_t> lastWarned{0};
-
-  time_t now = time(nullptr);
-
-  if (now > lastWarned + 10) {
-    lastWarned = now;
-    LOG(ERROR) << e.what();
-  }
-
-  /* Sleep for a while since retrying the transaction right away
-     is likely to fail again. */
-  checkInterrupt();
-  struct timespec t;
-  t.tv_sec = 0;
-  t.tv_nsec = (random() % 100) * 1000 * 1000; /* <= 0.1s */
-  nanosleep(&t, nullptr);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/sqlite.hh b/third_party/nix/src/libstore/sqlite.hh
deleted file mode 100644
index cad78aed45..0000000000
--- a/third_party/nix/src/libstore/sqlite.hh
+++ /dev/null
@@ -1,109 +0,0 @@
-#pragma once
-
-#include <functional>
-#include <string>
-
-#include "libutil/types.hh"
-
-class sqlite3;
-class sqlite3_stmt;
-
-namespace nix {
-
-/* RAII wrapper to close a SQLite database automatically. */
-struct SQLite {
-  sqlite3* db = 0;
-  SQLite() {}
-  SQLite(const Path& path);
-  SQLite(const SQLite& from) = delete;
-  SQLite& operator=(const SQLite& from) = delete;
-  SQLite& operator=(SQLite&& from) {
-    db = from.db;
-    from.db = 0;
-    return *this;
-  }
-  ~SQLite();
-  operator sqlite3*() { return db; }
-
-  void exec(const std::string& stmt);
-};
-
-/* RAII wrapper to create and destroy SQLite prepared statements. */
-struct SQLiteStmt {
-  sqlite3* db = 0;
-  sqlite3_stmt* stmt = 0;
-  std::string sql;
-  SQLiteStmt() {}
-  SQLiteStmt(sqlite3* db, const std::string& sql) { create(db, sql); }
-  void create(sqlite3* db, const std::string& s);
-  ~SQLiteStmt();
-  operator sqlite3_stmt*() { return stmt; }
-
-  /* Helper for binding / executing statements. */
-  class Use {
-    friend struct SQLiteStmt;
-
-   private:
-    SQLiteStmt& stmt;
-    int curArg = 1;
-    Use(SQLiteStmt& stmt);
-
-   public:
-    ~Use();
-
-    /* Bind the next parameter. */
-    Use& operator()(const std::string& value, bool notNull = true);
-    Use& operator()(int64_t value, bool notNull = true);
-    Use& bind();  // null
-
-    int step();
-
-    /* Execute a statement that does not return rows. */
-    void exec();
-
-    /* For statements that return 0 or more rows. Returns true iff
-       a row is available. */
-    bool next();
-
-    std::string getStr(int col);
-    int64_t getInt(int col);
-    bool isNull(int col);
-  };
-
-  Use use() { return Use(*this); }
-};
-
-/* RAII helper that ensures transactions are aborted unless explicitly
-   committed. */
-struct SQLiteTxn {
-  bool active = false;
-  sqlite3* db;
-
-  SQLiteTxn(sqlite3* db);
-
-  void commit();
-
-  ~SQLiteTxn();
-};
-
-MakeError(SQLiteError, Error);
-MakeError(SQLiteBusy, SQLiteError);
-
-[[noreturn]] void throwSQLiteError(sqlite3* db, const FormatOrString& fs);
-
-void handleSQLiteBusy(const SQLiteBusy& e);
-
-/* Convenience function for retrying a SQLite transaction when the
-   database is busy. */
-template <typename T>
-T retrySQLite(std::function<T()> fun) {
-  while (true) {
-    try {
-      return fun();
-    } catch (SQLiteBusy& e) {
-      handleSQLiteBusy(e);
-    }
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/ssh-store.cc b/third_party/nix/src/libstore/ssh-store.cc
deleted file mode 100644
index 96adb3660d..0000000000
--- a/third_party/nix/src/libstore/ssh-store.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-#include <absl/strings/str_cat.h>
-
-#include "libstore/remote-fs-accessor.hh"
-#include "libstore/remote-store.hh"
-#include "libstore/ssh.hh"
-#include "libstore/store-api.hh"
-#include "libstore/worker-protocol.hh"
-#include "libutil/archive.hh"
-#include "libutil/pool.hh"
-
-namespace nix {
-
-constexpr std::string_view kUriScheme = "ssh-ng://";
-
-class SSHStore : public RemoteStore {
- public:
-  const Setting<Path> sshKey{(Store*)this, "", "ssh-key",
-                             "path to an SSH private key"};
-  const Setting<bool> compress{(Store*)this, false, "compress",
-                               "whether to compress the connection"};
-
-  SSHStore(const std::string& host, const Params& params)
-      : Store(params),
-        RemoteStore(params),
-        host(host),
-        master(host, sshKey,
-               // Use SSH master only if using more than 1 connection.
-               connections->capacity() > 1, compress) {}
-
-  std::string getUri() override { return absl::StrCat(kUriScheme, host); }
-
-  bool sameMachine() override { return false; }
-
-  void narFromPath(const Path& path, Sink& sink) override;
-
-  ref<FSAccessor> getFSAccessor() override;
-
- private:
-  struct Connection : RemoteStore::Connection {
-    std::unique_ptr<SSHMaster::Connection> sshConn;
-  };
-
-  ref<RemoteStore::Connection> openConnection() override;
-
-  std::string host;
-
-  SSHMaster master;
-
-  void setOptions(RemoteStore::Connection& conn) override{
-      /* TODO Add a way to explicitly ask for some options to be
-         forwarded. One option: A way to query the daemon for its
-         settings, and then a series of params to SSHStore like
-         forward-cores or forward-overridden-cores that only
-         override the requested settings.
-      */
-  };
-};
-
-void SSHStore::narFromPath(const Path& path, Sink& sink) {
-  auto conn(connections->get());
-  conn->to << wopNarFromPath << path;
-  conn->processStderr();
-  copyNAR(conn->from, sink);
-}
-
-ref<FSAccessor> SSHStore::getFSAccessor() {
-  return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()));
-}
-
-ref<RemoteStore::Connection> SSHStore::openConnection() {
-  auto conn = make_ref<Connection>();
-  conn->sshConn = master.startCommand("nix-daemon --pipe");
-  conn->to = FdSink(conn->sshConn->in.get());
-  conn->from = FdSource(conn->sshConn->out.get());
-  initConnection(*conn);
-  return conn;
-}
-
-static RegisterStoreImplementation regStore(
-    [](const std::string& uri,
-       const Store::Params& params) -> std::shared_ptr<Store> {
-      if (std::string(uri, 0, kUriScheme.size()) != kUriScheme) {
-        return nullptr;
-      }
-      return std::make_shared<SSHStore>(std::string(uri, kUriScheme.size()),
-                                        params);
-    });
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/ssh.cc b/third_party/nix/src/libstore/ssh.cc
deleted file mode 100644
index 6043e584dd..0000000000
--- a/third_party/nix/src/libstore/ssh.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-#include "libstore/ssh.hh"
-
-#include <utility>
-
-#include <absl/strings/match.h>
-#include <absl/strings/str_split.h>
-
-namespace nix {
-
-SSHMaster::SSHMaster(const std::string& host, std::string keyFile,
-                     bool useMaster, bool compress, int logFD)
-    : host(host),
-      fakeSSH(host == "localhost"),
-      keyFile(std::move(keyFile)),
-      useMaster(useMaster && !fakeSSH),
-      compress(compress),
-      logFD(logFD) {
-  if (host.empty() || absl::StartsWith(host, "-")) {
-    throw Error("invalid SSH host name '%s'", host);
-  }
-}
-
-void SSHMaster::addCommonSSHOpts(Strings& args) {
-  for (auto& i :
-       absl::StrSplit(getEnv("NIX_SSHOPTS").value_or(""),
-                      absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty())) {
-    args.push_back(std::string(i));
-  }
-  if (!keyFile.empty()) {
-    args.insert(args.end(), {"-i", keyFile});
-  }
-  if (compress) {
-    args.push_back("-C");
-  }
-}
-
-std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
-    const std::string& command) {
-  Path socketPath = startMaster();
-
-  Pipe in;
-  Pipe out;
-  in.create();
-  out.create();
-
-  auto conn = std::make_unique<Connection>();
-  ProcessOptions options;
-  options.dieWithParent = false;
-
-  conn->sshPid = startProcess(
-      [&]() {
-        restoreSignals();
-
-        close(in.writeSide.get());
-        close(out.readSide.get());
-
-        if (dup2(in.readSide.get(), STDIN_FILENO) == -1) {
-          throw SysError("duping over stdin");
-        }
-        if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) {
-          throw SysError("duping over stdout");
-        }
-        if (logFD != -1 && dup2(logFD, STDERR_FILENO) == -1) {
-          throw SysError("duping over stderr");
-        }
-
-        Strings args;
-
-        if (fakeSSH) {
-          args = {"bash", "-c"};
-        } else {
-          args = {"ssh", host, "-x", "-a"};
-          addCommonSSHOpts(args);
-          if (!socketPath.empty()) {
-            args.insert(args.end(), {"-S", socketPath});
-          }
-          // TODO(tazjin): Abseil verbosity flag
-          /*if (verbosity >= lvlChatty) {
-              args.push_back("-v");
-              }*/
-        }
-
-        args.push_back(command);
-        execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
-
-        // could not exec ssh/bash
-        throw SysError("unable to execute '%s'", args.front());
-      },
-      options);
-
-  in.readSide = AutoCloseFD(-1);
-  out.writeSide = AutoCloseFD(-1);
-
-  conn->out = std::move(out.readSide);
-  conn->in = std::move(in.writeSide);
-
-  return conn;
-}
-
-Path SSHMaster::startMaster() {
-  if (!useMaster) {
-    return "";
-  }
-
-  auto state(state_.lock());
-
-  if (state->sshMaster != Pid(-1)) {
-    return state->socketPath;
-  }
-
-  state->tmpDir =
-      std::make_unique<AutoDelete>(createTempDir("", "nix", true, true, 0700));
-
-  state->socketPath = Path(*state->tmpDir) + "/ssh.sock";
-
-  Pipe out;
-  out.create();
-
-  ProcessOptions options;
-  options.dieWithParent = false;
-
-  state->sshMaster = startProcess(
-      [&]() {
-        restoreSignals();
-
-        close(out.readSide.get());
-
-        if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) {
-          throw SysError("duping over stdout");
-        }
-
-        Strings args = {"ssh", host,
-                        "-M",  "-N",
-                        "-S",  state->socketPath,
-                        "-o",  "LocalCommand=echo started",
-                        "-o",  "PermitLocalCommand=yes"};
-        // if (verbosity >= lvlChatty) { args.push_back("-v"); }
-        addCommonSSHOpts(args);
-        execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
-
-        throw SysError("unable to execute '%s'", args.front());
-      },
-      options);
-
-  out.writeSide = AutoCloseFD(-1);
-
-  std::string reply;
-  try {
-    reply = readLine(out.readSide.get());
-  } catch (EndOfFile& e) {
-  }
-
-  if (reply != "started") {
-    throw Error("failed to start SSH master connection to '%s'", host);
-  }
-
-  return state->socketPath;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/ssh.hh b/third_party/nix/src/libstore/ssh.hh
deleted file mode 100644
index 9844f89d35..0000000000
--- a/third_party/nix/src/libstore/ssh.hh
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include "libutil/sync.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-class SSHMaster {
- private:
-  const std::string host;
-  bool fakeSSH;
-  const std::string keyFile;
-  const bool useMaster;
-  const bool compress;
-  const int logFD;
-
-  struct State {
-    Pid sshMaster;
-    std::unique_ptr<AutoDelete> tmpDir;
-    Path socketPath;
-  };
-
-  Sync<State> state_;
-
-  void addCommonSSHOpts(Strings& args);
-
- public:
-  SSHMaster(const std::string& host, std::string keyFile, bool useMaster,
-            bool compress, int logFD = -1);
-
-  struct Connection {
-    Pid sshPid;
-    AutoCloseFD out, in;
-  };
-
-  std::unique_ptr<Connection> startCommand(const std::string& command);
-
-  Path startMaster();
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/store-api.cc b/third_party/nix/src/libstore/store-api.cc
deleted file mode 100644
index d8dbea18e9..0000000000
--- a/third_party/nix/src/libstore/store-api.cc
+++ /dev/null
@@ -1,1167 +0,0 @@
-#include "libstore/store-api.hh"
-
-#include <future>
-#include <ostream>
-#include <streambuf>
-#include <utility>
-
-#include <absl/status/status.h>
-#include <absl/strings/match.h>
-#include <absl/strings/numbers.h>
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_split.h>
-#include <glog/logging.h>
-#include <grpcpp/create_channel.h>
-
-#include "libproto/worker.pb.h"
-#include "libstore/crypto.hh"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/nar-info-disk-cache.hh"
-#include "libstore/rpc-store.hh"
-#include "libutil/json.hh"
-#include "libutil/thread-pool.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-namespace {
-class NullStream : public std::streambuf {
- public:
-  int overflow(int c) override { return c; }
-};
-
-static NullStream NULL_STREAM{};
-}  // namespace
-
-std::ostream DiscardLogsSink() { return std::ostream(&NULL_STREAM); }
-
-std::optional<BuildMode> BuildModeFrom(nix::proto::BuildMode mode) {
-  switch (mode) {
-    case nix::proto::BuildMode::Normal:
-      return BuildMode::bmNormal;
-    case nix::proto::BuildMode::Repair:
-      return BuildMode::bmRepair;
-    case nix::proto::BuildMode::Check:
-      return BuildMode::bmCheck;
-    default:
-      return {};
-  }
-}
-
-nix::proto::BuildMode BuildModeToProto(BuildMode mode) {
-  switch (mode) {
-    case BuildMode::bmNormal:
-      return nix::proto::BuildMode::Normal;
-    case BuildMode::bmRepair:
-      return nix::proto::BuildMode::Repair;
-    case BuildMode::bmCheck:
-      return nix::proto::BuildMode::Check;
-  }
-}
-
-nix::proto::BuildStatus BuildResult::status_to_proto() {
-  switch (status) {
-    case BuildResult::Status::Built:
-      return proto::BuildStatus::Built;
-    case BuildResult::Status::Substituted:
-      return proto::BuildStatus::Substituted;
-    case BuildResult::Status::AlreadyValid:
-      return proto::BuildStatus::AlreadyValid;
-    case BuildResult::Status::PermanentFailure:
-      return proto::BuildStatus::PermanentFailure;
-    case BuildResult::Status::InputRejected:
-      return proto::BuildStatus::InputRejected;
-    case BuildResult::Status::OutputRejected:
-      return proto::BuildStatus::OutputRejected;
-    case BuildResult::Status::TransientFailure:
-      return proto::BuildStatus::TransientFailure;
-    case BuildResult::Status::CachedFailure:
-      return proto::BuildStatus::CachedFailure;
-    case BuildResult::Status::TimedOut:
-      return proto::BuildStatus::TimedOut;
-    case BuildResult::Status::MiscFailure:
-      return proto::BuildStatus::MiscFailure;
-    case BuildResult::Status::DependencyFailed:
-      return proto::BuildStatus::DependencyFailed;
-    case BuildResult::Status::LogLimitExceeded:
-      return proto::BuildStatus::LogLimitExceeded;
-    case BuildResult::Status::NotDeterministic:
-      return proto::BuildStatus::NotDeterministic;
-  }
-}
-
-std::optional<BuildResult> BuildResult::FromProto(
-    const nix::proto::BuildResult& resp) {
-  BuildResult result;
-  switch (resp.status()) {
-    case proto::BuildStatus::Built:
-      result.status = BuildResult::Status::Built;
-      break;
-    case proto::BuildStatus::Substituted:
-      result.status = BuildResult::Status::Substituted;
-      break;
-    case proto::BuildStatus::AlreadyValid:
-      result.status = BuildResult::Status::AlreadyValid;
-      break;
-    case proto::BuildStatus::PermanentFailure:
-      result.status = BuildResult::Status::PermanentFailure;
-      break;
-    case proto::BuildStatus::InputRejected:
-      result.status = BuildResult::Status::InputRejected;
-      break;
-    case proto::BuildStatus::OutputRejected:
-      result.status = BuildResult::Status::OutputRejected;
-      break;
-    case proto::BuildStatus::TransientFailure:
-      result.status = BuildResult::Status::TransientFailure;
-      break;
-    case proto::BuildStatus::CachedFailure:
-      result.status = BuildResult::Status::CachedFailure;
-      break;
-    case proto::BuildStatus::TimedOut:
-      result.status = BuildResult::Status::TimedOut;
-      break;
-    case proto::BuildStatus::MiscFailure:
-      result.status = BuildResult::Status::MiscFailure;
-      break;
-    case proto::BuildStatus::DependencyFailed:
-      result.status = BuildResult::Status::DependencyFailed;
-      break;
-    case proto::BuildStatus::LogLimitExceeded:
-      result.status = BuildResult::Status::LogLimitExceeded;
-      break;
-    case proto::BuildStatus::NotDeterministic:
-      result.status = BuildResult::Status::NotDeterministic;
-      break;
-    default:
-      return {};
-  }
-
-  result.errorMsg = resp.msg();
-  return result;
-}
-
-std::optional<GCOptions::GCAction> GCActionFromProto(
-    nix::proto::GCAction gc_action) {
-  switch (gc_action) {
-    case nix::proto::GCAction::ReturnLive:
-      return GCOptions::GCAction::gcReturnLive;
-    case nix::proto::GCAction::ReturnDead:
-      return GCOptions::GCAction::gcReturnDead;
-    case nix::proto::GCAction::DeleteDead:
-      return GCOptions::GCAction::gcDeleteDead;
-    case nix::proto::GCAction::DeleteSpecific:
-      return GCOptions::GCAction::gcDeleteSpecific;
-    default:
-      return {};
-  }
-}
-
-[[nodiscard]] const proto::GCAction GCOptions::ActionToProto() const {
-  switch (action) {
-    case GCOptions::GCAction::gcReturnLive:
-      return nix::proto::GCAction::ReturnLive;
-    case GCOptions::GCAction::gcReturnDead:
-      return nix::proto::GCAction::ReturnDead;
-    case GCOptions::GCAction::gcDeleteDead:
-      return nix::proto::GCAction::DeleteDead;
-    case GCOptions::GCAction::gcDeleteSpecific:
-      return nix::proto::GCAction::DeleteSpecific;
-  }
-}
-
-bool Store::isInStore(const Path& path) const {
-  return isInDir(path, storeDir);
-}
-
-bool Store::isStorePath(const Path& path) const {
-  return isInStore(path) &&
-         path.size() >= storeDir.size() + 1 + storePathHashLen &&
-         path.find('/', storeDir.size() + 1) == Path::npos;
-}
-
-void Store::assertStorePath(const Path& path) const {
-  if (!isStorePath(path)) {
-    throw Error(format("path '%1%' is not in the Nix store") % path);
-  }
-}
-
-Path Store::toStorePath(const Path& path) const {
-  if (!isInStore(path)) {
-    throw Error(format("path '%1%' is not in the Nix store") % path);
-  }
-  Path::size_type slash = path.find('/', storeDir.size() + 1);
-  if (slash == Path::npos) {
-    return path;
-  }
-  return Path(path, 0, slash);
-}
-
-Path Store::followLinksToStore(const Path& _path) const {
-  Path path = absPath(_path);
-  while (!isInStore(path)) {
-    if (!isLink(path)) {
-      break;
-    }
-    std::string target = readLink(path);
-    path = absPath(target, dirOf(path));
-  }
-  if (!isInStore(path)) {
-    throw Error(format("path '%1%' is not in the Nix store") % path);
-  }
-  return path;
-}
-
-Path Store::followLinksToStorePath(const Path& path) const {
-  return toStorePath(followLinksToStore(path));
-}
-
-std::string storePathToName(const Path& path) {
-  auto base = baseNameOf(path);
-
-  // The base name of the store path must be `storePathHashLen` characters long,
-  // if it is not `storePathHashLen` long then the next character, following
-  // the hash part, MUST be a dash (`-`).
-  const bool hasLengthMismatch = base.size() != storePathHashLen;
-  const bool hasInvalidSuffix =
-      base.size() > storePathHashLen && base[storePathHashLen] != '-';
-  if (hasLengthMismatch && hasInvalidSuffix) {
-    throw Error(format("path '%1%' is not a valid store path") % path);
-  }
-
-  return base.size() == storePathHashLen
-             ? ""
-             : std::string(base, storePathHashLen + 1);
-}
-
-std::string storePathToHash(const Path& path) {
-  auto base = baseNameOf(path);
-  assert(base.size() >= storePathHashLen);
-  return std::string(base, 0, storePathHashLen);
-}
-
-void checkStoreName(const std::string& name) {
-  std::string validChars = "+-._?=";
-
-  auto baseError =
-      format(
-          "The path name '%2%' is invalid: %3%. "
-          "Path names are alphanumeric and can include the symbols %1% "
-          "and must not begin with a period. "
-          "Note: If '%2%' is a source file and you cannot rename it on "
-          "disk, builtins.path { name = ... } can be used to give it an "
-          "alternative name.") %
-      validChars % name;
-
-  /* Disallow names starting with a dot for possible security
-     reasons (e.g., "." and ".."). */
-  if (std::string(name, 0, 1) == ".") {
-    throw Error(baseError % "it is illegal to start the name with a period");
-  }
-  /* Disallow names longer than 211 characters. ext4’s max is 256,
-     but we need extra space for the hash and .chroot extensions. */
-  if (name.length() > 211) {
-    throw Error(baseError % "name must be less than 212 characters");
-  }
-  for (auto& i : name) {
-    if (!((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') ||
-          (i >= '0' && i <= '9') || validChars.find(i) != std::string::npos)) {
-      throw Error(baseError % (format("the '%1%' character is invalid") % i));
-    }
-  }
-}
-
-/* Store paths have the following form:
-
-   <store>/<h>-<name>
-
-   where
-
-   <store> = the location of the Nix store, usually /nix/store
-
-   <name> = a human readable name for the path, typically obtained
-     from the name attribute of the derivation, or the name of the
-     source file from which the store path is created.  For derivation
-     outputs other than the default "out" output, the string "-<id>"
-     is suffixed to <name>.
-
-   <h> = base-32 representation of the first 160 bits of a SHA-256
-     hash of <s>; the hash part of the store name
-
-   <s> = the string "<type>:sha256:<h2>:<store>:<name>";
-     note that it includes the location of the store as well as the
-     name to make sure that changes to either of those are reflected
-     in the hash (e.g. you won't get /nix/store/<h>-name1 and
-     /nix/store/<h>-name2 with equal hash parts).
-
-   <type> = one of:
-     "text:<r1>:<r2>:...<rN>"
-       for plain text files written to the store using
-       addTextToStore(); <r1> ... <rN> are the references of the
-       path.
-     "source"
-       for paths copied to the store using addToStore() when recursive
-       = true and hashAlgo = "sha256"
-     "output:<id>"
-       for either the outputs created by derivations, OR paths copied
-       to the store using addToStore() with recursive != true or
-       hashAlgo != "sha256" (in that case "source" is used; it's
-       silly, but it's done that way for compatibility).  <id> is the
-       name of the output (usually, "out").
-
-   <h2> = base-16 representation of a SHA-256 hash of:
-     if <type> = "text:...":
-       the string written to the resulting store path
-     if <type> = "source":
-       the serialisation of the path from which this store path is
-       copied, as returned by hashPath()
-     if <type> = "output:<id>":
-       for non-fixed derivation outputs:
-         the derivation (see hashDerivationModulo() in
-         primops.cc)
-       for paths copied by addToStore() or produced by fixed-output
-       derivations:
-         the string "fixed:out:<rec><algo>:<hash>:", where
-           <rec> = "r:" for recursive (path) hashes, or "" for flat
-             (file) hashes
-           <algo> = "md5", "sha1" or "sha256"
-           <hash> = base-16 representation of the path or flat hash of
-             the contents of the path (or expected contents of the
-             path for fixed-output derivations)
-
-   It would have been nicer to handle fixed-output derivations under
-   "source", e.g. have something like "source:<rec><algo>", but we're
-   stuck with this for now...
-
-   The main reason for this way of computing names is to prevent name
-   collisions (for security).  For instance, it shouldn't be feasible
-   to come up with a derivation whose output path collides with the
-   path for a copied source.  The former would have a <s> starting with
-   "output:out:", while the latter would have a <s> starting with
-   "source:".
-*/
-
-Path Store::makeStorePath(const std::string& type, const Hash& hash,
-                          const std::string& name) const {
-  /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
-  std::string s =
-      type + ":" + hash.to_string(Base16) + ":" + storeDir + ":" + name;
-
-  checkStoreName(name);
-
-  return absl::StrCat(storeDir, "/", hashString(htSHA256, s).ToStorePathHash(),
-                      "-", name);
-}
-
-Path Store::makeOutputPath(const std::string& id, const Hash& hash,
-                           const std::string& name) const {
-  return makeStorePath("output:" + id, hash,
-                       name + (id == "out" ? "" : "-" + id));
-}
-
-Path Store::makeFixedOutputPath(bool recursive, const Hash& hash,
-                                const std::string& name) const {
-  return hash.type == htSHA256 && recursive
-             ? makeStorePath("source", hash, name)
-             : makeStorePath(
-                   "output:out",
-                   hashString(
-                       htSHA256,
-                       absl::StrCat("fixed:out:", (recursive ? "r:" : ""),
-                                    hash.to_string(Base16), ":")),
-                   name);
-}
-
-Path Store::makeTextPath(const std::string& name, const Hash& hash,
-                         const PathSet& references) const {
-  assert(hash.type == htSHA256);
-  /* Stuff the references (if any) into the type.  This is a bit
-     hacky, but we can't put them in `s' since that would be
-     ambiguous. */
-  std::string type = "text";
-  for (auto& i : references) {
-    type += ":";
-    type += i;
-  }
-  return makeStorePath(type, hash, name);
-}
-
-std::pair<Path, Hash> Store::computeStorePathForPath(const std::string& name,
-                                                     const Path& srcPath,
-                                                     bool recursive,
-                                                     HashType hashAlgo,
-                                                     PathFilter& filter) const {
-  Hash h = recursive ? hashPath(hashAlgo, srcPath, filter).first
-                     : hashFile(hashAlgo, srcPath);
-  Path dstPath = makeFixedOutputPath(recursive, h, name);
-  return std::pair<Path, Hash>(dstPath, h);
-}
-
-Path Store::computeStorePathForText(const std::string& name,
-                                    const std::string& s,
-                                    const PathSet& references) const {
-  return makeTextPath(name, hashString(htSHA256, s), references);
-}
-
-Store::Store(const Params& params)
-    : Config(params),
-      state(Sync<State>{
-          State{LRUCache<std::string, std::shared_ptr<ValidPathInfo>>(
-              (size_t)pathInfoCacheSize)}}) {}
-
-std::string Store::getUri() { return ""; }
-
-bool Store::isValidPath(const Path& storePath) {
-  assertStorePath(storePath);
-
-  auto hashPart = storePathToHash(storePath);
-
-  {
-    auto state_(state.lock());
-    auto res = state_->pathInfoCache.get(hashPart);
-    if (res) {
-      stats.narInfoReadAverted++;
-      return *res != nullptr;
-    }
-  }
-
-  if (diskCache) {
-    auto res = diskCache->lookupNarInfo(getUri(), hashPart);
-    if (res.first != NarInfoDiskCache::oUnknown) {
-      stats.narInfoReadAverted++;
-      auto state_(state.lock());
-      state_->pathInfoCache.upsert(
-          hashPart,
-          res.first == NarInfoDiskCache::oInvalid ? nullptr : res.second);
-      return res.first == NarInfoDiskCache::oValid;
-    }
-  }
-
-  bool valid = isValidPathUncached(storePath);
-
-  if (diskCache && !valid) {
-    // FIXME: handle valid = true case.
-    diskCache->upsertNarInfo(getUri(), hashPart, nullptr);
-  }
-
-  return valid;
-}
-
-/* Default implementation for stores that only implement
-   queryPathInfoUncached(). */
-bool Store::isValidPathUncached(const Path& path) {
-  try {
-    queryPathInfo(path);
-    return true;
-  } catch (InvalidPath&) {
-    return false;
-  }
-}
-
-ref<const ValidPathInfo> Store::queryPathInfo(const Path& storePath) {
-  std::promise<ref<ValidPathInfo>> promise;
-
-  queryPathInfo(
-      storePath,
-      Callback<ref<ValidPathInfo>>([&](std::future<ref<ValidPathInfo>> result) {
-        try {
-          promise.set_value(result.get());
-        } catch (...) {
-          promise.set_exception(std::current_exception());
-        }
-      }));
-
-  return promise.get_future().get();
-}
-
-void Store::queryPathInfo(const Path& storePath,
-                          Callback<ref<ValidPathInfo>> callback) noexcept {
-  std::string hashPart;
-
-  try {
-    assertStorePath(storePath);
-
-    hashPart = storePathToHash(storePath);
-
-    {
-      auto res = state.lock()->pathInfoCache.get(hashPart);
-      if (res) {
-        stats.narInfoReadAverted++;
-        if (!*res) {
-          throw InvalidPath(format("path '%s' is not valid") % storePath);
-        }
-        return callback(ref<ValidPathInfo>(*res));
-      }
-    }
-
-    if (diskCache) {
-      auto res = diskCache->lookupNarInfo(getUri(), hashPart);
-      if (res.first != NarInfoDiskCache::oUnknown) {
-        stats.narInfoReadAverted++;
-        {
-          auto state_(state.lock());
-          state_->pathInfoCache.upsert(
-              hashPart,
-              res.first == NarInfoDiskCache::oInvalid ? nullptr : res.second);
-          if (res.first == NarInfoDiskCache::oInvalid ||
-              (res.second->path != storePath &&
-               !storePathToName(storePath).empty())) {
-            throw InvalidPath(format("path '%s' is not valid") % storePath);
-          }
-        }
-        return callback(ref<ValidPathInfo>(res.second));
-      }
-    }
-
-  } catch (...) {
-    return callback.rethrow();
-  }
-
-  auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
-
-  queryPathInfoUncached(
-      storePath,
-      Callback<std::shared_ptr<ValidPathInfo>>{
-          [this, storePath, hashPart,
-           callbackPtr](std::future<std::shared_ptr<ValidPathInfo>> fut) {
-            try {
-              auto info = fut.get();
-
-              if (diskCache) {
-                diskCache->upsertNarInfo(getUri(), hashPart, info);
-              }
-
-              {
-                auto state_(state.lock());
-                state_->pathInfoCache.upsert(hashPart, info);
-              }
-
-              if (!info || (info->path != storePath &&
-                            !storePathToName(storePath).empty())) {
-                stats.narInfoMissing++;
-                throw InvalidPath("path '%s' is not valid", storePath);
-              }
-
-              (*callbackPtr)(ref<ValidPathInfo>(info));
-            } catch (...) {
-              callbackPtr->rethrow();
-            }
-          }});
-}
-
-PathSet Store::queryValidPaths(const PathSet& paths,
-                               SubstituteFlag maybeSubstitute) {
-  struct State {
-    size_t left;
-    PathSet valid;
-    std::exception_ptr exc;
-  };
-
-  Sync<State> state_(State{paths.size(), PathSet()});
-
-  std::condition_variable wakeup;
-  ThreadPool pool;
-
-  auto doQuery = [&](const Path& path) {
-    checkInterrupt();
-    queryPathInfo(path, Callback<ref<ValidPathInfo>>(
-                            [path, &state_,
-                             &wakeup](std::future<ref<ValidPathInfo>> fut) {
-                              auto state(state_.lock());
-                              try {
-                                auto info = fut.get();
-                                state->valid.insert(path);
-                              } catch (InvalidPath&) {
-                              } catch (...) {
-                                state->exc = std::current_exception();
-                              }
-                              assert(state->left);
-                              if (--state->left == 0u) {
-                                wakeup.notify_one();
-                              }
-                            }));
-  };
-
-  for (auto& path : paths) {
-    pool.enqueue(std::bind(doQuery, path));
-  }
-
-  pool.process();
-
-  while (true) {
-    auto state(state_.lock());
-    if (state->left == 0u) {
-      if (state->exc) {
-        std::rethrow_exception(state->exc);
-      }
-      return state->valid;
-    }
-    state.wait(wakeup);
-  }
-}
-
-/* Return a string accepted by decodeValidPathInfo() that
-   registers the specified paths as valid.  Note: it's the
-   responsibility of the caller to provide a closure. */
-std::string Store::makeValidityRegistration(const PathSet& paths,
-                                            bool showDerivers, bool showHash) {
-  std::string s;
-
-  for (auto& i : paths) {
-    s += i + "\n";
-
-    auto info = queryPathInfo(i);
-
-    if (showHash) {
-      s += info->narHash.to_string(Base16, false) + "\n";
-      s += (format("%1%\n") % info->narSize).str();
-    }
-
-    Path deriver = showDerivers ? info->deriver : "";
-    s += deriver + "\n";
-
-    s += (format("%1%\n") % info->references.size()).str();
-
-    for (auto& j : info->references) {
-      s += j + "\n";
-    }
-  }
-
-  return s;
-}
-
-void Store::pathInfoToJSON(JSONPlaceholder& jsonOut, const PathSet& storePaths,
-                           bool includeImpureInfo, bool showClosureSize,
-                           AllowInvalidFlag allowInvalid) {
-  auto jsonList = jsonOut.list();
-
-  for (auto storePath : storePaths) {
-    auto jsonPath = jsonList.object();
-    jsonPath.attr("path", storePath);
-
-    try {
-      auto info = queryPathInfo(storePath);
-      storePath = info->path;
-
-      jsonPath.attr("narHash", info->narHash.to_string())
-          .attr("narSize", info->narSize);
-
-      {
-        auto jsonRefs = jsonPath.list("references");
-        for (auto& ref : info->references) {
-          jsonRefs.elem(ref);
-        }
-      }
-
-      if (!info->ca.empty()) {
-        jsonPath.attr("ca", info->ca);
-      }
-
-      std::pair<uint64_t, uint64_t> closureSizes;
-
-      if (showClosureSize) {
-        closureSizes = getClosureSize(storePath);
-        jsonPath.attr("closureSize", closureSizes.first);
-      }
-
-      if (includeImpureInfo) {
-        if (!info->deriver.empty()) {
-          jsonPath.attr("deriver", info->deriver);
-        }
-
-        if (info->registrationTime != 0) {
-          jsonPath.attr("registrationTime", info->registrationTime);
-        }
-
-        if (info->ultimate) {
-          jsonPath.attr("ultimate", info->ultimate);
-        }
-
-        if (!info->sigs.empty()) {
-          auto jsonSigs = jsonPath.list("signatures");
-          for (auto& sig : info->sigs) {
-            jsonSigs.elem(sig);
-          }
-        }
-
-        auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
-            std::shared_ptr<const ValidPathInfo>(info));
-
-        if (narInfo) {
-          if (!narInfo->url.empty()) {
-            jsonPath.attr("url", narInfo->url);
-          }
-          if (narInfo->fileHash) {
-            jsonPath.attr("downloadHash", narInfo->fileHash.to_string());
-          }
-          if (narInfo->fileSize != 0u) {
-            jsonPath.attr("downloadSize", narInfo->fileSize);
-          }
-          if (showClosureSize) {
-            jsonPath.attr("closureDownloadSize", closureSizes.second);
-          }
-        }
-      }
-
-    } catch (InvalidPath&) {
-      jsonPath.attr("valid", false);
-    }
-  }
-}
-
-std::pair<uint64_t, uint64_t> Store::getClosureSize(const Path& storePath) {
-  uint64_t totalNarSize = 0;
-  uint64_t totalDownloadSize = 0;
-  PathSet closure;
-  computeFSClosure(storePath, closure, false, false);
-  for (auto& p : closure) {
-    auto info = queryPathInfo(p);
-    totalNarSize += info->narSize;
-    auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
-        std::shared_ptr<const ValidPathInfo>(info));
-    if (narInfo) {
-      totalDownloadSize += narInfo->fileSize;
-    }
-  }
-  return {totalNarSize, totalDownloadSize};
-}
-
-const Store::Stats& Store::getStats() {
-  {
-    auto state_(state.lock());
-    stats.pathInfoCacheSize = state_->pathInfoCache.size();
-  }
-  return stats;
-}
-
-absl::Status Store::buildPaths(std::ostream& /* log_sink */,
-                               const PathSet& paths, BuildMode) {
-  for (auto& path : paths) {
-    if (isDerivation(path)) {
-      return absl::Status(absl::StatusCode::kUnimplemented,
-                          "buildPaths is unsupported");
-    }
-  }
-
-  if (queryValidPaths(paths).size() != paths.size()) {
-    return absl::Status(absl::StatusCode::kUnimplemented,
-                        "buildPaths is unsupported");
-  }
-
-  return absl::OkStatus();
-}
-
-void copyStorePath(ref<Store> srcStore, const ref<Store>& dstStore,
-                   const Path& storePath, RepairFlag repair,
-                   CheckSigsFlag checkSigs) {
-  auto srcUri = srcStore->getUri();
-  auto dstUri = dstStore->getUri();
-
-  if (srcUri == "local" || srcUri == "daemon") {
-    LOG(INFO) << "copying path '" << storePath << "' to '" << dstUri << "'";
-  } else {
-    if (dstUri == "local" || dstUri == "daemon") {
-      LOG(INFO) << "copying path '" << storePath << "' from '" << srcUri << "'";
-    } else {
-      LOG(INFO) << "copying path '" << storePath << "' from '" << srcUri
-                << "' to '" << dstUri << "'";
-    }
-  }
-
-  auto info = srcStore->queryPathInfo(storePath);
-
-  uint64_t total = 0;
-
-  if (!info->narHash) {
-    StringSink sink;
-    srcStore->narFromPath({storePath}, sink);
-    auto info2 = make_ref<ValidPathInfo>(*info);
-    info2->narHash = hashString(htSHA256, *sink.s);
-    if (info->narSize == 0u) {
-      info2->narSize = sink.s->size();
-    }
-    if (info->ultimate) {
-      info2->ultimate = false;
-    }
-    info = info2;
-
-    StringSource source(*sink.s);
-    dstStore->addToStore(*info, source, repair, checkSigs);
-    return;
-  }
-
-  if (info->ultimate) {
-    auto info2 = make_ref<ValidPathInfo>(*info);
-    info2->ultimate = false;
-    info = info2;
-  }
-
-  auto source = sinkToSource(
-      [&](Sink& sink) {
-        LambdaSink wrapperSink([&](const unsigned char* data, size_t len) {
-          sink(data, len);
-          total += len;
-        });
-        srcStore->narFromPath({storePath}, wrapperSink);
-      },
-      [&]() {
-        throw EndOfFile("NAR for '%s' fetched from '%s' is incomplete",
-                        storePath, srcStore->getUri());
-      });
-
-  dstStore->addToStore(*info, *source, repair, checkSigs);
-}
-
-void copyPaths(ref<Store> srcStore, ref<Store> dstStore,
-               const PathSet& storePaths, RepairFlag repair,
-               CheckSigsFlag checkSigs, SubstituteFlag substitute) {
-  PathSet valid = dstStore->queryValidPaths(storePaths, substitute);
-
-  PathSet missing;
-  for (auto& path : storePaths) {
-    if (valid.count(path) == 0u) {
-      missing.insert(path);
-    }
-  }
-
-  if (missing.empty()) {
-    return;
-  }
-
-  LOG(INFO) << "copying " << missing.size() << " paths";
-
-  std::atomic<size_t> nrDone{0};
-  std::atomic<size_t> nrFailed{0};
-  std::atomic<uint64_t> bytesExpected{0};
-  std::atomic<uint64_t> nrRunning{0};
-
-  ThreadPool pool;
-
-  processGraph<Path>(
-      pool, PathSet(missing.begin(), missing.end()),
-
-      [&](const Path& storePath) {
-        if (dstStore->isValidPath(storePath)) {
-          nrDone++;
-          return PathSet();
-        }
-
-        auto info = srcStore->queryPathInfo(storePath);
-
-        bytesExpected += info->narSize;
-
-        return info->references;
-      },
-
-      [&](const Path& storePath) {
-        checkInterrupt();
-
-        if (!dstStore->isValidPath(storePath)) {
-          MaintainCount<decltype(nrRunning)> mc(nrRunning);
-          try {
-            copyStorePath(srcStore, dstStore, storePath, repair, checkSigs);
-          } catch (Error& e) {
-            nrFailed++;
-            if (!settings.keepGoing) {
-              throw e;
-            }
-            LOG(ERROR) << "could not copy " << storePath << ": " << e.what();
-            return;
-          }
-        }
-
-        nrDone++;
-      });
-}
-
-void copyClosure(const ref<Store>& srcStore, const ref<Store>& dstStore,
-                 const PathSet& storePaths, RepairFlag repair,
-                 CheckSigsFlag checkSigs, SubstituteFlag substitute) {
-  PathSet closure;
-  srcStore->computeFSClosure({storePaths}, closure);
-  copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
-}
-
-ValidPathInfo decodeValidPathInfo(std::istream& str, bool hashGiven) {
-  ValidPathInfo info;
-  getline(str, info.path);
-  if (str.eof()) {
-    info.path = "";
-    return info;
-  }
-  if (hashGiven) {
-    std::string s;
-    getline(str, s);
-    auto hash_ = Hash::deserialize(s, htSHA256);
-    info.narHash = Hash::unwrap_throw(hash_);
-    getline(str, s);
-    if (!absl::SimpleAtoi(s, &info.narSize)) {
-      throw Error("number expected");
-    }
-  }
-  getline(str, info.deriver);
-  std::string s;
-  int n;
-  getline(str, s);
-  if (!absl::SimpleAtoi(s, &n)) {
-    throw Error("number expected");
-  }
-  while ((n--) != 0) {
-    getline(str, s);
-    info.references.insert(s);
-  }
-  if (!str || str.eof()) {
-    throw Error("missing input");
-  }
-  return info;
-}
-
-std::string showPaths(const PathSet& paths) {
-  std::string s;
-  for (auto& i : paths) {
-    if (!s.empty()) {
-      s += ", ";
-    }
-    s += "'" + i + "'";
-  }
-  return s;
-}
-
-std::string ValidPathInfo::fingerprint() const {
-  if (narSize == 0 || !narHash) {
-    throw Error(format("cannot calculate fingerprint of path '%s' because its "
-                       "size/hash is not known") %
-                path);
-  }
-  return "1;" + path + ";" + narHash.to_string(Base32) + ";" +
-         std::to_string(narSize) + ";" + concatStringsSep(",", references);
-}
-
-void ValidPathInfo::sign(const SecretKey& secretKey) {
-  sigs.insert(secretKey.signDetached(fingerprint()));
-}
-
-bool ValidPathInfo::isContentAddressed(const Store& store) const {
-  auto warn = [&]() {
-    LOG(ERROR) << "warning: path '" << path
-               << "' claims to be content-addressed but isn't";
-  };
-
-  if (absl::StartsWith(ca, "text:")) {
-    auto hash_ = Hash::deserialize(std::string_view(ca).substr(5));
-    Hash hash = Hash::unwrap_throw(hash_);
-    if (store.makeTextPath(storePathToName(path), hash, references) == path) {
-      return true;
-    }
-    warn();
-
-  }
-
-  else if (absl::StartsWith(ca, "fixed:")) {
-    bool recursive = ca.compare(6, 2, "r:") == 0;
-    auto hash_ =
-        Hash::deserialize(std::string_view(ca).substr(recursive ? 8 : 6));
-    Hash hash = Hash::unwrap_throw(hash_);
-    if (references.empty() &&
-        store.makeFixedOutputPath(recursive, hash, storePathToName(path)) ==
-            path) {
-      return true;
-    }
-    warn();
-  }
-
-  return false;
-}
-
-size_t ValidPathInfo::checkSignatures(const Store& store,
-                                      const PublicKeys& publicKeys) const {
-  if (isContentAddressed(store)) {
-    return maxSigs;
-  }
-
-  size_t good = 0;
-  for (auto& sig : sigs) {
-    if (checkSignature(publicKeys, sig)) {
-      good++;
-    }
-  }
-  return good;
-}
-
-bool ValidPathInfo::checkSignature(const PublicKeys& publicKeys,
-                                   const std::string& sig) const {
-  return verifyDetached(fingerprint(), sig, publicKeys);
-}
-
-Strings ValidPathInfo::shortRefs() const {
-  Strings refs;
-  for (auto& r : references) {
-    refs.push_back(baseNameOf(r));
-  }
-  return refs;
-}
-
-std::string makeFixedOutputCA(bool recursive, const Hash& hash) {
-  return "fixed:" + (recursive ? std::string("r:") : "") + hash.to_string();
-}
-
-void Store::addToStore(const ValidPathInfo& info, Source& narSource,
-                       RepairFlag repair, CheckSigsFlag checkSigs,
-                       std::shared_ptr<FSAccessor> accessor) {
-  addToStore(info, make_ref<std::string>(narSource.drain()), repair, checkSigs,
-             std::move(accessor));
-}
-
-void Store::addToStore(const ValidPathInfo& info, const ref<std::string>& nar,
-                       RepairFlag repair, CheckSigsFlag checkSigs,
-                       std::shared_ptr<FSAccessor> accessor) {
-  StringSource source(*nar);
-  addToStore(info, source, repair, checkSigs, std::move(accessor));
-}
-
-}  // namespace nix
-
-#include "libstore/local-store.hh"
-#include "libstore/remote-store.hh"
-
-namespace nix {
-
-RegisterStoreImplementation::Implementations*
-    RegisterStoreImplementation::implementations = nullptr;
-
-/* Split URI into protocol+hierarchy part and its parameter set. */
-std::pair<std::string, Store::Params> splitUriAndParams(
-    const std::string& uri_) {
-  auto uri(uri_);
-  Store::Params params;
-  auto q = uri.find('?');
-  if (q != std::string::npos) {
-    Strings parts =
-        absl::StrSplit(uri.substr(q + 1), absl::ByChar('&'), absl::SkipEmpty());
-    for (const auto& s : parts) {
-      auto e = s.find('=');
-      if (e != std::string::npos) {
-        auto value = s.substr(e + 1);
-        std::string decoded;
-        for (size_t i = 0; i < value.size();) {
-          if (value[i] == '%') {
-            if (i + 2 >= value.size()) {
-              throw Error("invalid URI parameter '%s'", value);
-            }
-            try {
-              decoded += std::stoul(std::string(value, i + 1, 2), nullptr, 16);
-              i += 3;
-            } catch (...) {
-              throw Error("invalid URI parameter '%s'", value);
-            }
-          } else {
-            decoded += value[i++];
-          }
-        }
-        params[s.substr(0, e)] = decoded;
-      }
-    }
-    uri = uri_.substr(0, q);
-  }
-  return {uri, params};
-}
-
-ref<Store> openStore(const std::string& uri_,
-                     const Store::Params& extraParams) {
-  auto [uri, uriParams] = splitUriAndParams(uri_);
-  auto params = extraParams;
-  params.insert(uriParams.begin(), uriParams.end());
-
-  for (const auto& fun : *RegisterStoreImplementation::implementations) {
-    auto store = fun(uri, params);
-    if (store) {
-      store->warnUnknownSettings();
-      return ref<Store>(store);
-    }
-  }
-
-  throw Error("don't know how to open Nix store '%s'", uri);
-}
-
-StoreType getStoreType(const std::string& uri, const std::string& stateDir) {
-  if (uri == "daemon") {
-    return tDaemon;
-  }
-  if (uri == "local" || absl::StartsWith(uri, "/")) {
-    return tLocal;
-  } else if (uri.empty() || uri == "auto") {
-    if (access(stateDir.c_str(), R_OK | W_OK) == 0) {
-      return tLocal;
-    }
-    if (pathExists(settings.nixDaemonSocketFile)) {
-      return tDaemon;
-    } else {
-      return tLocal;
-    }
-  } else {
-    return tOther;
-  }
-}
-
-static RegisterStoreImplementation regStore([](const std::string& uri,
-                                               const Store::Params& params)
-                                                -> std::shared_ptr<Store> {
-  switch (getStoreType(uri, get(params, "state", settings.nixStateDir))) {
-    case tDaemon: {
-      auto daemon_socket_uri =
-          absl::StrCat("unix://", settings.nixDaemonSocketFile);
-      auto channel = grpc::CreateChannel(daemon_socket_uri,
-                                         grpc::InsecureChannelCredentials());
-      return std::shared_ptr<Store>(std::make_shared<nix::store::RpcStore>(
-          daemon_socket_uri, params, proto::WorkerService::NewStub(channel)));
-    }
-    case tLocal: {
-      Store::Params params2 = params;
-      if (absl::StartsWith(uri, "/")) {
-        params2["root"] = uri;
-      }
-      return std::shared_ptr<Store>(std::make_shared<LocalStore>(params2));
-    }
-    default:
-      return nullptr;
-  }
-});
-
-std::list<ref<Store>> getDefaultSubstituters() {
-  static auto stores([]() {
-    std::list<ref<Store>> stores;
-
-    StringSet done;
-
-    auto addStore = [&](const std::string& uri) {
-      if (done.count(uri) != 0u) {
-        return;
-      }
-      done.insert(uri);
-      try {
-        stores.push_back(openStore(uri));
-      } catch (Error& e) {
-        LOG(WARNING) << e.what();
-      }
-    };
-
-    for (const auto& uri : settings.substituters.get()) {
-      addStore(uri);
-    }
-
-    for (const auto& uri : settings.extraSubstituters.get()) {
-      addStore(uri);
-    }
-
-    stores.sort([](ref<Store>& a, ref<Store>& b) {
-      return a->getPriority() < b->getPriority();
-    });
-
-    return stores;
-  }());
-
-  return stores;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/store-api.hh b/third_party/nix/src/libstore/store-api.hh
deleted file mode 100644
index eb18511e60..0000000000
--- a/third_party/nix/src/libstore/store-api.hh
+++ /dev/null
@@ -1,816 +0,0 @@
-#pragma once
-
-#include <atomic>
-#include <limits>
-#include <map>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "libproto/worker.pb.h"
-#include "libstore/crypto.hh"
-#include "libstore/globals.hh"
-#include "libutil/config.hh"
-#include "libutil/hash.hh"
-#include "libutil/lru-cache.hh"
-#include "libutil/serialise.hh"
-#include "libutil/sync.hh"
-
-namespace nix {
-
-// Create a no-op stream buffer used to discard build output in cases
-// where we don't have a build log sink to thread through.
-//
-// TODO(tazjin): Get rid of this and do *something* with those logs.
-std::ostream DiscardLogsSink();
-
-MakeError(SubstError, Error);
-MakeError(BuildError, Error); /* denotes a permanent build failure */
-MakeError(InvalidPath, Error);
-MakeError(Unsupported, Error);
-MakeError(SubstituteGone, Error);
-MakeError(SubstituterDisabled, Error);
-
-struct BasicDerivation;
-struct Derivation;
-class FSAccessor;
-class NarInfoDiskCache;
-class Store;
-class JSONPlaceholder;
-
-enum RepairFlag : bool { NoRepair = false, Repair = true };
-enum CheckSigsFlag : bool { NoCheckSigs = false, CheckSigs = true };
-enum SubstituteFlag : bool { NoSubstitute = false, Substitute = true };
-enum AllowInvalidFlag : bool { DisallowInvalid = false, AllowInvalid = true };
-
-/* Size of the hash part of store paths, in base-32 characters. */
-const size_t storePathHashLen = 32;  // i.e. 160 bits
-
-/* Magic header of exportPath() output (obsolete). */
-const uint32_t exportMagic = 0x4558494e;
-
-using Roots = std::unordered_map<Path, std::unordered_set<std::string>>;
-
-struct GCOptions {
-  /* Garbage collector operation:
-
-     - `gcReturnLive': return the set of paths reachable from
-       (i.e. in the closure of) the roots.
-
-     - `gcReturnDead': return the set of paths not reachable from
-       the roots.
-
-     - `gcDeleteDead': actually delete the latter set.
-
-     - `gcDeleteSpecific': delete the paths listed in
-        `pathsToDelete', insofar as they are not reachable.
-  */
-  using GCAction = enum {
-    gcReturnLive,
-    gcReturnDead,
-    gcDeleteDead,
-    gcDeleteSpecific,
-  };
-
-  GCAction action{gcDeleteDead};
-
-  /* If `ignoreLiveness' is set, then reachability from the roots is
-     ignored (dangerous!).  However, the paths must still be
-     unreferenced *within* the store (i.e., there can be no other
-     store paths that depend on them). */
-  bool ignoreLiveness{false};
-
-  /* For `gcDeleteSpecific', the paths to delete. */
-  PathSet pathsToDelete;
-
-  /* Stop after at least `maxFreed' bytes have been freed. */
-  unsigned long long maxFreed{std::numeric_limits<unsigned long long>::max()};
-
-  [[nodiscard]] const proto::GCAction ActionToProto() const;
-};
-
-std::optional<GCOptions::GCAction> GCActionFromProto(
-    nix::proto::GCAction gc_action);
-
-struct GCResults {
-  /* Depending on the action, the GC roots, or the paths that would
-     be or have been deleted. */
-  PathSet paths;
-
-  /* For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the
-     number of bytes that would be or was freed. */
-  unsigned long long bytesFreed = 0;
-};
-
-struct SubstitutablePathInfo {
-  Path deriver;
-  PathSet references;
-  unsigned long long downloadSize; /* 0 = unknown or inapplicable */
-  unsigned long long narSize;      /* 0 = unknown */
-};
-
-using SubstitutablePathInfos = std::map<Path, SubstitutablePathInfo>;
-
-struct ValidPathInfo {
-  Path path;
-  Path deriver;
-  Hash narHash;
-  PathSet references;
-  time_t registrationTime = 0;
-  uint64_t narSize = 0;  // 0 = unknown
-  uint64_t id;           // internal use only
-
-  /* Whether the path is ultimately trusted, that is, it's a
-     derivation output that was built locally. */
-  bool ultimate = false;
-
-  StringSet sigs;  // note: not necessarily verified
-
-  /* If non-empty, an assertion that the path is content-addressed,
-     i.e., that the store path is computed from a cryptographic hash
-     of the contents of the path, plus some other bits of data like
-     the "name" part of the path. Such a path doesn't need
-     signatures, since we don't have to trust anybody's claim that
-     the path is the output of a particular derivation. (In the
-     extensional store model, we have to trust that the *contents*
-     of an output path of a derivation were actually produced by
-     that derivation. In the intensional model, we have to trust
-     that a particular output path was produced by a derivation; the
-     path then implies the contents.)
-
-     Ideally, the content-addressability assertion would just be a
-     Boolean, and the store path would be computed from
-     β€˜storePathToName(path)’, β€˜narHash’ and β€˜references’. However,
-     1) we've accumulated several types of content-addressed paths
-     over the years; and 2) fixed-output derivations support
-     multiple hash algorithms and serialisation methods (flat file
-     vs NAR). Thus, β€˜ca’ has one of the following forms:
-
-     * β€˜text:sha256:<sha256 hash of file contents>’: For paths
-       computed by makeTextPath() / addTextToStore().
-
-     * β€˜fixed:<r?>:<ht>:<h>’: For paths computed by
-       makeFixedOutputPath() / addToStore().
-  */
-  std::string ca;
-
-  bool operator==(const ValidPathInfo& i) const {
-    return path == i.path && narHash == i.narHash && references == i.references;
-  }
-
-  /* Return a fingerprint of the store path to be used in binary
-     cache signatures. It contains the store path, the base-32
-     SHA-256 hash of the NAR serialisation of the path, the size of
-     the NAR, and the sorted references. The size field is strictly
-     speaking superfluous, but might prevent endless/excessive data
-     attacks. */
-  std::string fingerprint() const;
-
-  void sign(const SecretKey& secretKey);
-
-  /* Return true iff the path is verifiably content-addressed. */
-  bool isContentAddressed(const Store& store) const;
-
-  static const size_t maxSigs = std::numeric_limits<size_t>::max();
-
-  /* Return the number of signatures on this .narinfo that were
-     produced by one of the specified keys, or maxSigs if the path
-     is content-addressed. */
-  size_t checkSignatures(const Store& store,
-                         const PublicKeys& publicKeys) const;
-
-  /* Verify a single signature. */
-  bool checkSignature(const PublicKeys& publicKeys,
-                      const std::string& sig) const;
-
-  Strings shortRefs() const;
-
-  virtual ~ValidPathInfo() {}
-};
-
-using ValidPathInfos = std::list<ValidPathInfo>;
-
-enum BuildMode { bmNormal, bmRepair, bmCheck };
-
-// Convert the proto version of a `nix::proto::BuildMode` to its corresponding
-// nix `BuildMode`
-std::optional<BuildMode> BuildModeFrom(nix::proto::BuildMode mode);
-
-// Convert a `nix::BuildMode` to its corresponding proto representation
-nix::proto::BuildMode BuildModeToProto(BuildMode mode);
-
-struct BuildResult {
-  /* Note: don't remove status codes, and only add new status codes
-     at the end of the list, to prevent client/server
-     incompatibilities in the nix-store --serve protocol. */
-  enum Status {
-    Built = 0,
-    Substituted,
-    AlreadyValid,
-    PermanentFailure,
-    InputRejected,
-    OutputRejected,
-    TransientFailure,  // possibly transient
-    CachedFailure,     // no longer used
-    TimedOut,
-    MiscFailure,
-    DependencyFailed,
-    LogLimitExceeded,
-    NotDeterministic,
-  } status = MiscFailure;
-  std::string errorMsg;
-
-  /* How many times this build was performed. */
-  unsigned int timesBuilt = 0;
-
-  /* If timesBuilt > 1, whether some builds did not produce the same
-     result. (Note that 'isNonDeterministic = false' does not mean
-     the build is deterministic, just that we don't have evidence of
-     non-determinism.) */
-  bool isNonDeterministic = false;
-
-  /* The start/stop times of the build (or one of the rounds, if it
-     was repeated). */
-  time_t startTime = 0, stopTime = 0;
-
-  bool success() {
-    return status == Built || status == Substituted || status == AlreadyValid;
-  }
-
-  // Convert the status of this `BuildResult` to its corresponding
-  // `nix::proto::BuildStatus`
-  nix::proto::BuildStatus status_to_proto();
-
-  static std::optional<BuildResult> FromProto(
-      const nix::proto::BuildResult& resp);
-};
-
-class Store : public std::enable_shared_from_this<Store>, public Config {
- public:
-  using Params = std::map<std::string, std::string>;
-
-  const PathSetting storeDir_{this, false, settings.nixStore, "store",
-                              "path to the Nix store"};
-  const Path storeDir = storeDir_;
-
-  const Setting<int> pathInfoCacheSize{
-      this, 65536, "path-info-cache-size",
-      "size of the in-memory store path information cache"};
-
-  const Setting<bool> isTrusted{
-      this, false, "trusted",
-      "whether paths from this store can be used as substitutes even when they "
-      "lack trusted signatures"};
-
- protected:
-  struct State {
-    LRUCache<std::string, std::shared_ptr<ValidPathInfo>> pathInfoCache;
-  };
-
-  Sync<State> state;
-
-  std::shared_ptr<NarInfoDiskCache> diskCache;
-
-  Store(const Params& params);
-
- public:
-  virtual ~Store() {}
-
-  virtual std::string getUri() = 0;
-
-  /* Return true if β€˜path’ is in the Nix store (but not the Nix
-     store itself). */
-  bool isInStore(const Path& path) const;
-
-  /* Return true if β€˜path’ is a store path, i.e. a direct child of
-     the Nix store. */
-  bool isStorePath(const Path& path) const;
-
-  /* Throw an exception if β€˜path’ is not a store path. */
-  void assertStorePath(const Path& path) const;
-
-  /* Chop off the parts after the top-level store name, e.g.,
-     /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */
-  Path toStorePath(const Path& path) const;
-
-  /* Follow symlinks until we end up with a path in the Nix store. */
-  Path followLinksToStore(const Path& path) const;
-
-  /* Same as followLinksToStore(), but apply toStorePath() to the
-     result. */
-  Path followLinksToStorePath(const Path& path) const;
-
-  /* Constructs a unique store path name. */
-  Path makeStorePath(const std::string& type, const Hash& hash,
-                     const std::string& name) const;
-
-  Path makeOutputPath(const std::string& id, const Hash& hash,
-                      const std::string& name) const;
-
-  Path makeFixedOutputPath(bool recursive, const Hash& hash,
-                           const std::string& name) const;
-
-  Path makeTextPath(const std::string& name, const Hash& hash,
-                    const PathSet& references) const;
-
-  /* This is the preparatory part of addToStore(); it computes the
-     store path to which srcPath is to be copied.  Returns the store
-     path and the cryptographic hash of the contents of srcPath. */
-  std::pair<Path, Hash> computeStorePathForPath(
-      const std::string& name, const Path& srcPath, bool recursive = true,
-      HashType hashAlgo = htSHA256,
-      PathFilter& filter = defaultPathFilter) const;
-
-  /* Preparatory part of addTextToStore().
-
-     !!! Computation of the path should take the references given to
-     addTextToStore() into account, otherwise we have a (relatively
-     minor) security hole: a caller can register a source file with
-     bogus references.  If there are too many references, the path may
-     not be garbage collected when it has to be (not really a problem,
-     the caller could create a root anyway), or it may be garbage
-     collected when it shouldn't be (more serious).
-
-     Hashing the references would solve this (bogus references would
-     simply yield a different store path, so other users wouldn't be
-     affected), but it has some backwards compatibility issues (the
-     hashing scheme changes), so I'm not doing that for now. */
-  Path computeStorePathForText(const std::string& name, const std::string& s,
-                               const PathSet& references) const;
-
-  /* Check whether a path is valid. */
-  bool isValidPath(const Path& path);
-
- protected:
-  virtual bool isValidPathUncached(const Path& path);
-
- public:
-  /* Query which of the given paths is valid. Optionally, try to
-     substitute missing paths. */
-  virtual PathSet queryValidPaths(const PathSet& paths,
-                                  SubstituteFlag maybeSubstitute);
-
-  PathSet queryValidPaths(const PathSet& paths) {
-    return queryValidPaths(paths, NoSubstitute);
-  }
-
-  /* Query the set of all valid paths. Note that for some store
-     backends, the name part of store paths may be omitted
-     (i.e. you'll get /nix/store/<hash> rather than
-     /nix/store/<hash>-<name>). Use queryPathInfo() to obtain the
-     full store path. */
-  virtual PathSet queryAllValidPaths() { unsupported("queryAllValidPaths"); }
-
-  /* Query information about a valid path. It is permitted to omit
-     the name part of the store path. */
-  ref<const ValidPathInfo> queryPathInfo(const Path& path);
-
-  /* Asynchronous version of queryPathInfo(). */
-  void queryPathInfo(const Path& path,
-                     Callback<ref<ValidPathInfo>> callback) noexcept;
-
- protected:
-  virtual void queryPathInfoUncached(
-      const Path& path,
-      Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept = 0;
-
- public:
-  /* Queries the set of incoming FS references for a store path.
-     The result is not cleared. */
-  virtual void queryReferrers(const Path& path, PathSet& referrers) {
-    unsupported("queryReferrers");
-  }
-
-  /* Return all currently valid derivations that have `path' as an
-     output.  (Note that the result of `queryDeriver()' is the
-     derivation that was actually used to produce `path', which may
-     not exist anymore.) */
-  virtual PathSet queryValidDerivers(const Path& path) { return {}; };
-
-  /* Query the outputs of the derivation denoted by `path'. */
-  virtual PathSet queryDerivationOutputs(const Path& path) {
-    unsupported("queryDerivationOutputs");
-  }
-
-  /* Query the output names of the derivation denoted by `path'. */
-  virtual StringSet queryDerivationOutputNames(const Path& path) {
-    unsupported("queryDerivationOutputNames");
-  }
-
-  /* Query the full store path given the hash part of a valid store
-     path, or "" if the path doesn't exist. */
-  virtual Path queryPathFromHashPart(const std::string& hashPart) = 0;
-
-  /* Query which of the given paths have substitutes. */
-  virtual PathSet querySubstitutablePaths(const PathSet& paths) { return {}; };
-
-  /* Query substitute info (i.e. references, derivers and download
-     sizes) of a set of paths.  If a path does not have substitute
-     info, it's omitted from the resulting β€˜infos’ map. */
-  virtual void querySubstitutablePathInfos(const PathSet& paths,
-                                           SubstitutablePathInfos& infos) {
-    return;
-  };
-
-  virtual bool wantMassQuery() { return false; }
-
-  /* Import a path into the store. */
-  virtual void addToStore(const ValidPathInfo& info, Source& narSource,
-                          RepairFlag repair = NoRepair,
-                          CheckSigsFlag checkSigs = CheckSigs,
-                          std::shared_ptr<FSAccessor> accessor = 0);
-
-  // FIXME: remove
-  virtual void addToStore(const ValidPathInfo& info,
-                          const ref<std::string>& nar,
-                          RepairFlag repair = NoRepair,
-                          CheckSigsFlag checkSigs = CheckSigs,
-                          std::shared_ptr<FSAccessor> accessor = 0);
-
-  /* Copy the contents of a path to the store and register the
-     validity of the resulting path.  The resulting path is returned.
-     The function object `filter' can be used to exclude files (see
-     libutil/archive.hh). If recursive is set to true, the path will be treated
-     as a directory (eg cp -r vs cp) */
-  virtual Path addToStore(const std::string& name, const Path& srcPath,
-                          bool recursive = true, HashType hashAlgo = htSHA256,
-                          PathFilter& filter = defaultPathFilter,
-                          RepairFlag repair = NoRepair) = 0;
-
-  /* Like addToStore, but the contents written to the output path is
-     a regular file containing the given string. */
-  virtual Path addTextToStore(const std::string& name, const std::string& s,
-                              const PathSet& references,
-                              RepairFlag repair = NoRepair) = 0;
-
-  /* Write a NAR dump of a store path. */
-  virtual void narFromPath(const Path& path, Sink& sink) = 0;
-
-  /* For each path, if it's a derivation, build it.  Building a
-     derivation means ensuring that the output paths are valid.  If
-     they are already valid, this is a no-op.  Otherwise, validity
-     can be reached in two ways.  First, if the output paths is
-     substitutable, then build the path that way.  Second, the
-     output paths can be created by running the builder, after
-     recursively building any sub-derivations. For inputs that are
-     not derivations, substitute them. */
-  [[nodiscard]] virtual absl::Status buildPaths(std::ostream& log_sink,
-                                                const PathSet& paths,
-                                                BuildMode build_mode);
-
-  [[nodiscard]] absl::Status buildPaths(std::ostream& log_sink,
-                                        const PathSet& paths) {
-    return buildPaths(log_sink, paths, bmNormal);
-  }
-
-  /* Build a single non-materialized derivation (i.e. not from an
-     on-disk .drv file). Note that β€˜drvPath’ is only used for
-     informational purposes. */
-  // TODO(tazjin): Thread std::ostream through here, too.
-  virtual BuildResult buildDerivation(std::ostream& log_sink,
-                                      const Path& drvPath,
-                                      const BasicDerivation& drv,
-                                      BuildMode buildMode) = 0;
-
-  BuildResult buildDerivation(std::ostream& log_sink, const Path& drvPath,
-                              const BasicDerivation& drv) {
-    return buildDerivation(log_sink, drvPath, drv, bmNormal);
-  }
-
-  /* Ensure that a path is valid.  If it is not currently valid, it
-     may be made valid by running a substitute (if defined for the
-     path). */
-  virtual void ensurePath(const Path& path) = 0;
-
-  /* Add a store path as a temporary root of the garbage collector.
-     The root disappears as soon as we exit. */
-  virtual void addTempRoot(const Path& path) { unsupported("addTempRoot"); }
-
-  /* Add an indirect root, which is merely a symlink to `path' from
-     /nix/var/nix/gcroots/auto/<hash of `path'>.  `path' is supposed
-     to be a symlink to a store path.  The garbage collector will
-     automatically remove the indirect root when it finds that
-     `path' has disappeared. */
-  virtual void addIndirectRoot(const Path& path) {
-    unsupported("addIndirectRoot");
-  }
-
-  /* Acquire the global GC lock, then immediately release it.  This
-     function must be called after registering a new permanent root,
-     but before exiting.  Otherwise, it is possible that a running
-     garbage collector doesn't see the new root and deletes the
-     stuff we've just built.  By acquiring the lock briefly, we
-     ensure that either:
-
-     - The collector is already running, and so we block until the
-       collector is finished.  The collector will know about our
-       *temporary* locks, which should include whatever it is we
-       want to register as a permanent lock.
-
-     - The collector isn't running, or it's just started but hasn't
-       acquired the GC lock yet.  In that case we get and release
-       the lock right away, then exit.  The collector scans the
-       permanent root and sees our's.
-
-     In either case the permanent root is seen by the collector. */
-  virtual void syncWithGC(){};
-
-  /* Find the roots of the garbage collector.  Each root is a pair
-     (link, storepath) where `link' is the path of the symlink
-     outside of the Nix store that point to `storePath'. If
-     'censor' is true, privacy-sensitive information about roots
-     found in /proc is censored. */
-  virtual Roots findRoots(bool censor) { unsupported("findRoots"); }
-
-  /* Perform a garbage collection. */
-  virtual void collectGarbage(const GCOptions& options, GCResults& results) {
-    unsupported("collectGarbage");
-  }
-
-  /* Return a string representing information about the path that
-     can be loaded into the database using `nix-store --load-db' or
-     `nix-store --register-validity'. */
-  std::string makeValidityRegistration(const PathSet& paths, bool showDerivers,
-                                       bool showHash);
-
-  /* Write a JSON representation of store path metadata, such as the
-     hash and the references. If β€˜includeImpureInfo’ is true,
-     variable elements such as the registration time are
-     included. If β€˜showClosureSize’ is true, the closure size of
-     each path is included. */
-  void pathInfoToJSON(JSONPlaceholder& jsonOut, const PathSet& storePaths,
-                      bool includeImpureInfo, bool showClosureSize,
-                      AllowInvalidFlag allowInvalid = DisallowInvalid);
-
-  /* Return the size of the closure of the specified path, that is,
-     the sum of the size of the NAR serialisation of each path in
-     the closure. */
-  std::pair<uint64_t, uint64_t> getClosureSize(const Path& storePath);
-
-  /* Optimise the disk space usage of the Nix store by hard-linking files
-     with the same contents. */
-  virtual void optimiseStore(){};
-
-  /* Check the integrity of the Nix store.  Returns true if errors
-     remain. */
-  virtual bool verifyStore(bool checkContents, RepairFlag repair = NoRepair) {
-    return false;
-  };
-
-  /* Return an object to access files in the Nix store. */
-  virtual ref<FSAccessor> getFSAccessor() { unsupported("getFSAccessor"); }
-
-  /* Add signatures to the specified store path. The signatures are
-     not verified. */
-  virtual void addSignatures(const Path& storePath, const StringSet& sigs) {
-    unsupported("addSignatures");
-  }
-
-  /* Utility functions. */
-
-  /* Read a derivation, after ensuring its existence through
-     ensurePath(). */
-  Derivation derivationFromPath(const Path& drvPath);
-
-  /* Place in `out' the set of all store paths in the file system
-     closure of `storePath'; that is, all paths than can be directly
-     or indirectly reached from it.  `out' is not cleared.  If
-     `flipDirection' is true, the set of paths that can reach
-     `storePath' is returned; that is, the closures under the
-     `referrers' relation instead of the `references' relation is
-     returned. */
-  virtual void computeFSClosure(const PathSet& paths, PathSet& paths_,
-                                bool flipDirection = false,
-                                bool includeOutputs = false,
-                                bool includeDerivers = false);
-
-  void computeFSClosure(const Path& path, PathSet& paths_,
-                        bool flipDirection = false, bool includeOutputs = false,
-                        bool includeDerivers = false);
-
-  /* Given a set of paths that are to be built, return the set of
-     derivations that will be built, and the set of output paths
-     that will be substituted. */
-  virtual void queryMissing(const PathSet& targets, PathSet& willBuild,
-                            PathSet& willSubstitute, PathSet& unknown,
-                            unsigned long long& downloadSize,
-                            unsigned long long& narSize);
-
-  /* Sort a set of paths topologically under the references
-     relation.  If p refers to q, then p precedes q in this list. */
-  Paths topoSortPaths(const PathSet& paths);
-
-  /* Export multiple paths in the format expected by β€˜nix-store
-     --import’. */
-  void exportPaths(const Paths& paths, Sink& sink);
-
-  void exportPath(const Path& path, Sink& sink);
-
-  /* Import a sequence of NAR dumps created by exportPaths() into
-     the Nix store. Optionally, the contents of the NARs are
-     preloaded into the specified FS accessor to speed up subsequent
-     access. */
-  Paths importPaths(Source& source, const std::shared_ptr<FSAccessor>& accessor,
-                    CheckSigsFlag checkSigs = CheckSigs);
-
-  struct Stats {
-    std::atomic<uint64_t> narInfoRead{0};
-    std::atomic<uint64_t> narInfoReadAverted{0};
-    std::atomic<uint64_t> narInfoMissing{0};
-    std::atomic<uint64_t> narInfoWrite{0};
-    std::atomic<uint64_t> pathInfoCacheSize{0};
-    std::atomic<uint64_t> narRead{0};
-    std::atomic<uint64_t> narReadBytes{0};
-    std::atomic<uint64_t> narReadCompressedBytes{0};
-    std::atomic<uint64_t> narWrite{0};
-    std::atomic<uint64_t> narWriteAverted{0};
-    std::atomic<uint64_t> narWriteBytes{0};
-    std::atomic<uint64_t> narWriteCompressedBytes{0};
-    std::atomic<uint64_t> narWriteCompressionTimeMs{0};
-  };
-
-  const Stats& getStats();
-
-  /* Return the build log of the specified store path, if available,
-     or null otherwise. */
-  virtual std::shared_ptr<std::string> getBuildLog(const Path& path) {
-    return nullptr;
-  }
-
-  /* Hack to allow long-running processes like hydra-queue-runner to
-     occasionally flush their path info cache. */
-  void clearPathInfoCache() { state.lock()->pathInfoCache.clear(); }
-
-  /* Establish a connection to the store, for store types that have
-     a notion of connection. Otherwise this is a no-op. */
-  virtual void connect(){};
-
-  /* Get the protocol version of this store or it's connection. */
-  virtual unsigned int getProtocol() { return 0; };
-
-  /* Get the priority of the store, used to order substituters. In
-     particular, binary caches can specify a priority field in their
-     "nix-cache-info" file. Lower value means higher priority. */
-  virtual int getPriority() { return 0; }
-
-  virtual Path toRealPath(const Path& storePath) { return storePath; }
-
-  virtual void createUser(const std::string& userName, uid_t userId) {}
-
- protected:
-  Stats stats;
-
-  /* Unsupported methods. */
-  [[noreturn]] void unsupported(const std::string& op) {
-    throw Unsupported("operation '%s' is not supported by store '%s'", op,
-                      getUri());
-  }
-};
-
-class LocalFSStore : public virtual Store {
- public:
-  // FIXME: the (Store*) cast works around a bug in gcc that causes
-  // it to emit the call to the Option constructor. Clang works fine
-  // either way.
-  const PathSetting rootDir{(Store*)this, true, "", "root",
-                            "directory prefixed to all other paths"};
-  const PathSetting stateDir{
-      (Store*)this, false,
-      rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir, "state",
-      "directory where Nix will store state"};
-  const PathSetting logDir{
-      (Store*)this, false,
-      rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir, "log",
-      "directory where Nix will store state"};
-
-  const static std::string drvsLogDir;
-
-  LocalFSStore(const Params& params);
-
-  void narFromPath(const Path& path, Sink& sink) override;
-  ref<FSAccessor> getFSAccessor() override;
-
-  /* Register a permanent GC root. */
-  Path addPermRoot(const Path& storePath, const Path& gcRoot, bool indirect,
-                   bool allowOutsideRootsDir = false);
-
-  virtual Path getRealStoreDir() { return storeDir; }
-
-  Path toRealPath(const Path& storePath) override {
-    assert(isInStore(storePath));
-    return getRealStoreDir() + "/" +
-           std::string(storePath, storeDir.size() + 1);
-  }
-
-  std::shared_ptr<std::string> getBuildLog(const Path& path) override;
-};
-
-/* Extract the name part of the given store path. */
-std::string storePathToName(const Path& path);
-
-/* Extract the hash part of the given store path. */
-std::string storePathToHash(const Path& path);
-
-/* Check whether β€˜name’ is a valid store path name part, i.e. contains
-   only the characters [a-zA-Z0-9\+\-\.\_\?\=] and doesn't start with
-   a dot. */
-void checkStoreName(const std::string& name);
-
-/* Copy a path from one store to another. */
-void copyStorePath(ref<Store> srcStore, const ref<Store>& dstStore,
-                   const Path& storePath, RepairFlag repair = NoRepair,
-                   CheckSigsFlag checkSigs = CheckSigs);
-
-/* Copy store paths from one store to another. The paths may be copied
-   in parallel. They are copied in a topologically sorted order
-   (i.e. if A is a reference of B, then A is copied before B), but
-   the set of store paths is not automatically closed; use
-   copyClosure() for that. */
-void copyPaths(ref<Store> srcStore, ref<Store> dstStore,
-               const PathSet& storePaths, RepairFlag repair = NoRepair,
-               CheckSigsFlag checkSigs = CheckSigs,
-               SubstituteFlag substitute = NoSubstitute);
-
-/* Copy the closure of the specified paths from one store to another. */
-void copyClosure(const ref<Store>& srcStore, const ref<Store>& dstStore,
-                 const PathSet& storePaths, RepairFlag repair = NoRepair,
-                 CheckSigsFlag checkSigs = CheckSigs,
-                 SubstituteFlag substitute = NoSubstitute);
-
-/* Remove the temporary roots file for this process.  Any temporary
-   root becomes garbage after this point unless it has been registered
-   as a (permanent) root. */
-void removeTempRoots();
-
-/* Return a Store object to access the Nix store denoted by
-   β€˜uri’ (slight misnomer...). Supported values are:
-
-   * β€˜local’: The Nix store in /nix/store and database in
-     /nix/var/nix/db, accessed directly.
-
-   * β€˜daemon’: The Nix store accessed via a Unix domain socket
-     connection to nix-daemon.
-
-   * β€˜unix://<path>’: The Nix store accessed via a Unix domain socket
-     connection to nix-daemon, with the socket located at <path>.
-
-   * β€˜auto’ or β€˜β€™: Equivalent to β€˜local’ or β€˜daemon’ depending on
-     whether the user has write access to the local Nix
-     store/database.
-
-   * β€˜file://<path>’: A binary cache stored in <path>.
-
-   * β€˜https://<path>’: A binary cache accessed via HTTP.
-
-   * β€˜s3://<path>’: A writable binary cache stored on Amazon's Simple
-     Storage Service.
-
-   * β€˜ssh://[user@]<host>’: A remote Nix store accessed by running
-     β€˜nix-store --serve’ via SSH.
-
-   You can pass parameters to the store implementation by appending
-   β€˜?key=value&key=value&...’ to the URI.
-*/
-ref<Store> openStore(const std::string& uri = settings.storeUri.get(),
-                     const Store::Params& extraParams = Store::Params());
-
-enum StoreType { tDaemon, tLocal, tOther };
-
-StoreType getStoreType(const std::string& uri = settings.storeUri.get(),
-                       const std::string& stateDir = settings.nixStateDir);
-
-/* Return the default substituter stores, defined by the
-   β€˜substituters’ option and various legacy options. */
-std::list<ref<Store>> getDefaultSubstituters();
-
-/* Store implementation registration. */
-using OpenStore = std::function<std::shared_ptr<Store>(const std::string&,
-                                                       const Store::Params&)>;
-
-struct RegisterStoreImplementation {
-  using Implementations = std::vector<OpenStore>;
-  static Implementations* implementations;
-
-  RegisterStoreImplementation(OpenStore fun) {
-    if (!implementations) {
-      implementations = new Implementations;
-    }
-    implementations->push_back(fun);
-  }
-};
-
-/* Display a set of paths in human-readable form (i.e., between quotes
-   and separated by commas). */
-std::string showPaths(const PathSet& paths);
-
-ValidPathInfo decodeValidPathInfo(std::istream& str, bool hashGiven = false);
-
-/* Compute the content-addressability assertion (ValidPathInfo::ca)
-   for paths created by makeFixedOutputPath() / addToStore(). */
-std::string makeFixedOutputCA(bool recursive, const Hash& hash);
-
-/* Split URI into protocol+hierarchy part and its parameter set. */
-std::pair<std::string, Store::Params> splitUriAndParams(const std::string& uri);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libstore/worker-protocol.hh b/third_party/nix/src/libstore/worker-protocol.hh
deleted file mode 100644
index e2f40a449d..0000000000
--- a/third_party/nix/src/libstore/worker-protocol.hh
+++ /dev/null
@@ -1,68 +0,0 @@
-#pragma once
-
-#include "libstore/store-api.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-#define WORKER_MAGIC_1 0x6e697863
-#define WORKER_MAGIC_2 0x6478696f
-
-#define PROTOCOL_VERSION 0x115
-#define GET_PROTOCOL_MAJOR(x) ((x)&0xff00)
-#define GET_PROTOCOL_MINOR(x) ((x)&0x00ff)
-
-typedef enum {
-  wopIsValidPath = 1,
-  wopHasSubstitutes = 3,
-  wopQueryPathHash = 4,    // obsolete
-  wopQueryReferences = 5,  // obsolete
-  wopQueryReferrers = 6,
-  wopAddToStore = 7,
-  wopAddTextToStore = 8,
-  wopBuildPaths = 9,
-  wopEnsurePath = 10,
-  wopAddTempRoot = 11,
-  wopAddIndirectRoot = 12,
-  wopSyncWithGC = 13,
-  wopFindRoots = 14,
-  wopExportPath = 16,    // obsolete
-  wopQueryDeriver = 18,  // obsolete
-  wopSetOptions = 19,
-  wopCollectGarbage = 20,
-  wopQuerySubstitutablePathInfo = 21,
-  wopQueryDerivationOutputs = 22,
-  wopQueryAllValidPaths = 23,
-  wopQueryFailedPaths = 24,  // obsolete
-  wopClearFailedPaths = 25,  // obsolete
-  wopQueryPathInfo = 26,
-  wopImportPaths = 27,  // obsolete
-  wopQueryDerivationOutputNames = 28,
-  wopQueryPathFromHashPart = 29,
-  wopQuerySubstitutablePathInfos = 30,
-  wopQueryValidPaths = 31,
-  wopQuerySubstitutablePaths = 32,
-  wopQueryValidDerivers = 33,
-  wopOptimiseStore = 34,
-  wopVerifyStore = 35,
-  wopBuildDerivation = 36,
-  wopAddSignatures = 37,
-  wopNarFromPath = 38,
-  wopAddToStoreNar = 39,
-  wopQueryMissing = 40,
-} WorkerOp;
-
-#define STDERR_NEXT 0x6f6c6d67
-#define STDERR_READ 0x64617461   // data needed from source
-#define STDERR_WRITE 0x64617416  // data for sink
-#define STDERR_LAST 0x616c7473
-#define STDERR_ERROR 0x63787470
-#define STDERR_START_ACTIVITY 0x53545254
-#define STDERR_STOP_ACTIVITY 0x53544f50
-#define STDERR_RESULT 0x52534c54
-
-Path readStorePath(Store& store, Source& from);
-template <class T>
-T readStorePaths(Store& store, Source& from);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/CMakeLists.txt b/third_party/nix/src/libutil/CMakeLists.txt
deleted file mode 100644
index 0b36929218..0000000000
--- a/third_party/nix/src/libutil/CMakeLists.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-# -*- mode: cmake; -*-
-add_library(nixutil SHARED)
-set_property(TARGET nixutil PROPERTY CXX_STANDARD 17)
-include_directories(${PROJECT_BINARY_DIR}) # for config.h
-target_compile_features(nixutil PUBLIC cxx_std_17)
-
-set(HEADER_FILES
-    affinity.hh
-    archive.hh
-    args.hh
-    compression.hh
-    config.hh
-    finally.hh
-    hash.hh
-    istringstream_nocopy.hh
-    json.hh
-    lazy.hh
-    lru-cache.hh
-    monitor-fd.hh
-    pool.hh
-    proto.hh
-    ref.hh
-    serialise.hh
-    status.hh
-    sync.hh
-    thread-pool.hh
-    types.hh
-    util.hh
-    visitor.hh
-    xml-writer.hh
-)
-
-target_sources(nixutil
-  PUBLIC
-    ${HEADER_FILES}
-
-  PRIVATE
-    affinity.cc
-    archive.cc
-    args.cc
-    compression.cc
-    config.cc
-    hash.cc
-    json.cc
-    serialise.cc
-    thread-pool.cc
-    util.cc
-    xml-writer.cc
-)
-
-target_link_libraries(nixutil
-  nixproto
-  absl::strings
-  absl::statusor
-  glog
-  BZip2::BZip2
-  LibLZMA::LibLZMA
-  Boost::context
-  brotlienc
-  brotlidec
-  ssl
-)
-
-# Install header files to include/libutil and mark them for automatic
-# inclusion in targets that link to this one.
-target_include_directories(nixutil PUBLIC "${nix_SOURCE_DIR}/src")
-INSTALL(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nix/libutil)
-INSTALL(TARGETS nixutil DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/third_party/nix/src/libutil/affinity.cc b/third_party/nix/src/libutil/affinity.cc
deleted file mode 100644
index 03fbe12439..0000000000
--- a/third_party/nix/src/libutil/affinity.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "libutil/affinity.hh"
-
-#include <glog/logging.h>
-
-#include "libutil/types.hh"
-#include "libutil/util.hh"
-
-#if __linux__
-#include <sched.h>
-#endif
-
-namespace nix {
-
-#if __linux__
-static bool didSaveAffinity = false;
-static cpu_set_t savedAffinity;
-#endif
-
-void setAffinityTo(int cpu) {
-#if __linux__
-  if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) {
-    return;
-  }
-
-  didSaveAffinity = true;
-  DLOG(INFO) << "locking this thread to CPU " << cpu;
-  cpu_set_t newAffinity;
-  CPU_ZERO(&newAffinity);
-  CPU_SET(cpu, &newAffinity);
-  if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1) {
-    LOG(ERROR) << "failed to lock thread to CPU " << cpu;
-  }
-#endif
-}
-
-int lockToCurrentCPU() {
-#if __linux__
-  int cpu = sched_getcpu();
-  if (cpu != -1) {
-    setAffinityTo(cpu);
-  }
-  return cpu;
-#else
-  return -1;
-#endif
-}
-
-void restoreAffinity() {
-#if __linux__
-  if (!didSaveAffinity) {
-    return;
-  }
-
-  if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) {
-    LOG(ERROR) << "failed to restore affinity";
-  }
-#endif
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/affinity.hh b/third_party/nix/src/libutil/affinity.hh
deleted file mode 100644
index 5e5ef9b0de..0000000000
--- a/third_party/nix/src/libutil/affinity.hh
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-namespace nix {
-
-void setAffinityTo(int cpu);
-int lockToCurrentCPU();
-void restoreAffinity();
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/archive.cc b/third_party/nix/src/libutil/archive.cc
deleted file mode 100644
index e470ad7be6..0000000000
--- a/third_party/nix/src/libutil/archive.cc
+++ /dev/null
@@ -1,398 +0,0 @@
-#include "libutil/archive.hh"
-
-#include <algorithm>
-#include <cerrno>
-#include <map>
-#include <vector>
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <strings.h>  // for strcasecmp
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libutil/config.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-struct ArchiveSettings : Config {
-  Setting<bool> useCaseHack {
-    this,
-#if __APPLE__
-        true,
-#else
-        false,
-#endif
-        "use-case-hack",
-        "Whether to enable a Darwin-specific hack for dealing with file name "
-        "collisions."
-  };
-};
-
-static ArchiveSettings archiveSettings;
-
-static GlobalConfig::Register r1(&archiveSettings);
-
-constexpr std::string_view kCaseHackSuffix = "~nix~case~hack~";
-
-PathFilter defaultPathFilter = [](const Path& /*unused*/) { return true; };
-
-static void dumpContents(const Path& path, size_t size, Sink& sink) {
-  sink << "contents" << size;
-
-  AutoCloseFD fd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
-  if (!fd) {
-    throw SysError(format("opening file '%1%'") % path);
-  }
-
-  std::vector<unsigned char> buf(65536);
-  size_t left = size;
-
-  while (left > 0) {
-    auto n = std::min(left, buf.size());
-    readFull(fd.get(), buf.data(), n);
-    left -= n;
-    sink(buf.data(), n);
-  }
-
-  writePadding(size, sink);
-}
-
-static void dump(const Path& path, Sink& sink, PathFilter& filter) {
-  checkInterrupt();
-
-  struct stat st;
-  if (lstat(path.c_str(), &st) != 0) {
-    throw SysError(format("getting attributes of path '%1%'") % path);
-  }
-
-  sink << "(";
-
-  if (S_ISREG(st.st_mode)) {
-    sink << "type"
-         << "regular";
-    if ((st.st_mode & S_IXUSR) != 0u) {
-      sink << "executable"
-           << "";
-    }
-    dumpContents(path, static_cast<size_t>(st.st_size), sink);
-  }
-
-  else if (S_ISDIR(st.st_mode)) {
-    sink << "type"
-         << "directory";
-
-    /* If we're on a case-insensitive system like macOS, undo
-       the case hack applied by restorePath(). */
-    std::map<std::string, std::string> unhacked;
-    for (auto& i : readDirectory(path)) {
-      if (archiveSettings.useCaseHack) {
-        std::string name(i.name);
-        size_t pos = i.name.find(kCaseHackSuffix);
-        if (pos != std::string::npos) {
-          DLOG(INFO) << "removing case hack suffix from " << path << "/"
-                     << i.name;
-
-          name.erase(pos);
-        }
-        if (unhacked.find(name) != unhacked.end()) {
-          throw Error(format("file name collision in between '%1%' and '%2%'") %
-                      (path + "/" + unhacked[name]) % (path + "/" + i.name));
-        }
-        unhacked[name] = i.name;
-      } else {
-        unhacked[i.name] = i.name;
-      }
-    }
-
-    for (auto& i : unhacked) {
-      if (filter(path + "/" + i.first)) {
-        sink << "entry"
-             << "("
-             << "name" << i.first << "node";
-        dump(path + "/" + i.second, sink, filter);
-        sink << ")";
-      }
-    }
-  }
-
-  else if (S_ISLNK(st.st_mode)) {
-    sink << "type"
-         << "symlink"
-         << "target" << readLink(path);
-
-  } else {
-    throw Error(format("file '%1%' has an unsupported type") % path);
-  }
-
-  sink << ")";
-}
-
-void dumpPath(const Path& path, Sink& sink, PathFilter& filter) {
-  sink << std::string(kNarVersionMagic1);
-  dump(path, sink, filter);
-}
-
-void dumpString(const std::string& s, Sink& sink) {
-  sink << std::string(kNarVersionMagic1) << "("
-       << "type"
-       << "regular"
-       << "contents" << s << ")";
-}
-
-static SerialisationError badArchive(const std::string& s) {
-  return SerialisationError("bad archive: " + s);
-}
-
-#if 0
-static void skipGeneric(Source & source)
-{
-    if (readString(source) == "(") {
-        while (readString(source) != ")")
-            skipGeneric(source);
-    }
-}
-#endif
-
-static void parseContents(ParseSink& sink, Source& source, const Path& path) {
-  unsigned long long size = readLongLong(source);
-
-  sink.preallocateContents(size);
-
-  unsigned long long left = size;
-  std::vector<unsigned char> buf(65536);
-
-  while (left != 0u) {
-    checkInterrupt();
-    auto n = buf.size();
-    if (static_cast<unsigned long long>(n) > left) {
-      n = left;
-    }
-    source(buf.data(), n);
-    sink.receiveContents(buf.data(), n);
-    left -= n;
-  }
-
-  readPadding(size, source);
-}
-
-struct CaseInsensitiveCompare {
-  bool operator()(const std::string& a, const std::string& b) const {
-    return strcasecmp(a.c_str(), b.c_str()) < 0;
-  }
-};
-
-static void parse(ParseSink& sink, Source& source, const Path& path) {
-  std::string s;
-
-  s = readString(source);
-  if (s != "(") {
-    throw badArchive("expected open tag");
-  }
-
-  enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
-
-  std::map<Path, int, CaseInsensitiveCompare> names;
-
-  while (true) {
-    checkInterrupt();
-
-    s = readString(source);
-
-    if (s == ")") {
-      break;
-    }
-
-    if (s == "type") {
-      if (type != tpUnknown) {
-        throw badArchive("multiple type fields");
-      }
-      std::string t = readString(source);
-
-      if (t == "regular") {
-        type = tpRegular;
-        sink.createRegularFile(path);
-      }
-
-      else if (t == "directory") {
-        sink.createDirectory(path);
-        type = tpDirectory;
-      }
-
-      else if (t == "symlink") {
-        type = tpSymlink;
-      }
-
-      else {
-        throw badArchive("unknown file type " + t);
-      }
-
-    }
-
-    else if (s == "contents" && type == tpRegular) {
-      parseContents(sink, source, path);
-    }
-
-    else if (s == "executable" && type == tpRegular) {
-      auto s = readString(source);
-      if (!s.empty()) {
-        throw badArchive("executable marker has non-empty value");
-      }
-      sink.isExecutable();
-    }
-
-    else if (s == "entry" && type == tpDirectory) {
-      std::string name;
-      std::string prevName;
-
-      s = readString(source);
-      if (s != "(") {
-        throw badArchive("expected open tag");
-      }
-
-      while (true) {
-        checkInterrupt();
-
-        s = readString(source);
-
-        if (s == ")") {
-          break;
-        }
-        if (s == "name") {
-          name = readString(source);
-          if (name.empty() || name == "." || name == ".." ||
-              name.find('/') != std::string::npos ||
-              name.find(static_cast<char>(0)) != std::string::npos) {
-            throw Error(format("NAR contains invalid file name '%1%'") % name);
-          }
-          if (name <= prevName) {
-            throw Error("NAR directory is not sorted");
-          }
-          prevName = name;
-          if (archiveSettings.useCaseHack) {
-            auto i = names.find(name);
-            if (i != names.end()) {
-              DLOG(INFO) << "case collision between '" << i->first << "' and '"
-                         << name << "'";
-              name += kCaseHackSuffix;
-              name += std::to_string(++i->second);
-            } else {
-              names[name] = 0;
-            }
-          }
-        } else if (s == "node") {
-          if (s.empty()) {
-            throw badArchive("entry name missing");
-          }
-          parse(sink, source, path + "/" + name);
-        } else {
-          throw badArchive("unknown field " + s);
-        }
-      }
-    }
-
-    else if (s == "target" && type == tpSymlink) {
-      std::string target = readString(source);
-      sink.createSymlink(path, target);
-    }
-
-    else {
-      throw badArchive("unknown field " + s);
-    }
-  }
-}
-
-void parseDump(ParseSink& sink, Source& source) {
-  std::string version;
-  try {
-    version = readString(source, kNarVersionMagic1.size());
-  } catch (SerialisationError& e) {
-    /* This generally means the integer at the start couldn't be
-       decoded.  Ignore and throw the exception below. */
-  }
-  if (version != kNarVersionMagic1) {
-    throw badArchive("input doesn't look like a Nix archive");
-  }
-  parse(sink, source, "");
-}
-
-struct RestoreSink : ParseSink {
-  Path dstPath;
-  AutoCloseFD fd;
-
-  void createDirectory(const Path& path) override {
-    Path p = dstPath + path;
-    if (mkdir(p.c_str(), 0777) == -1) {
-      throw SysError(format("creating directory '%1%'") % p);
-    }
-  };
-
-  void createRegularFile(const Path& path) override {
-    Path p = dstPath + path;
-    fd = AutoCloseFD(
-        open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666));
-    if (!fd) {
-      throw SysError(format("creating file '%1%'") % p);
-    }
-  }
-
-  void isExecutable() override {
-    struct stat st;
-    if (fstat(fd.get(), &st) == -1) {
-      throw SysError("fstat");
-    }
-    if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1) {
-      throw SysError("fchmod");
-    }
-  }
-
-  void preallocateContents(unsigned long long len) override {
-#if HAVE_POSIX_FALLOCATE
-    if (len != 0u) {
-      errno = posix_fallocate(fd.get(), 0, len);
-      /* Note that EINVAL may indicate that the underlying
-         filesystem doesn't support preallocation (e.g. on
-         OpenSolaris).  Since preallocation is just an
-         optimisation, ignore it. */
-      if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS) {
-        throw SysError(format("preallocating file of %1% bytes") % len);
-      }
-    }
-#endif
-  }
-
-  void receiveContents(unsigned char* data, unsigned int len) override {
-    writeFull(fd.get(), data, len);
-  }
-
-  void createSymlink(const Path& path, const std::string& target) override {
-    Path p = dstPath + path;
-    nix::createSymlink(target, p);
-  }
-};
-
-void restorePath(const Path& path, Source& source) {
-  RestoreSink sink;
-  sink.dstPath = path;
-  parseDump(sink, source);
-}
-
-void copyNAR(Source& source, Sink& sink) {
-  // FIXME: if 'source' is the output of dumpPath() followed by EOF,
-  // we should just forward all data directly without parsing.
-
-  ParseSink parseSink; /* null sink; just parse the NAR */
-
-  LambdaSource wrapper([&](unsigned char* data, size_t len) {
-    auto n = source.read(data, len);
-    sink(data, n);
-    return n;
-  });
-
-  parseDump(parseSink, wrapper);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/archive.hh b/third_party/nix/src/libutil/archive.hh
deleted file mode 100644
index 3966278785..0000000000
--- a/third_party/nix/src/libutil/archive.hh
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-
-#include "libutil/serialise.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-/* dumpPath creates a Nix archive of the specified path.  The format
-   is as follows:
-
-   IF path points to a REGULAR FILE:
-     dump(path) = attrs(
-       [ ("type", "regular")
-       , ("contents", contents(path))
-       ])
-
-   IF path points to a DIRECTORY:
-     dump(path) = attrs(
-       [ ("type", "directory")
-       , ("entries", concat(map(f, sort(entries(path)))))
-       ])
-       where f(fn) = attrs(
-         [ ("name", fn)
-         , ("file", dump(path + "/" + fn))
-         ])
-
-   where:
-
-     attrs(as) = concat(map(attr, as)) + encN(0)
-     attrs((a, b)) = encS(a) + encS(b)
-
-     encS(s) = encN(len(s)) + s + (padding until next 64-bit boundary)
-
-     encN(n) = 64-bit little-endian encoding of n.
-
-     contents(path) = the contents of a regular file.
-
-     sort(strings) = lexicographic sort by 8-bit value (strcmp).
-
-     entries(path) = the entries of a directory, without `.' and
-     `..'.
-
-     `+' denotes string concatenation. */
-
-void dumpPath(const Path& path, Sink& sink,
-              PathFilter& filter = defaultPathFilter);
-
-void dumpString(const std::string& s, Sink& sink);
-
-/* FIXME: fix this API, it sucks. */
-struct ParseSink {
-  virtual void createDirectory(const Path& path){};
-
-  virtual void createRegularFile(const Path& path){};
-  virtual void isExecutable(){};
-  virtual void preallocateContents(unsigned long long size){};
-  virtual void receiveContents(unsigned char* data, unsigned int len){};
-
-  virtual void createSymlink(const Path& path, const std::string& target){};
-};
-
-struct TeeSink : ParseSink {
-  TeeSource source;
-
-  explicit TeeSink(Source& source) : source(source) {}
-};
-
-void parseDump(ParseSink& sink, Source& source);
-
-void restorePath(const Path& path, Source& source);
-
-/* Read a NAR from 'source' and write it to 'sink'. */
-void copyNAR(Source& source, Sink& sink);
-
-constexpr std::string_view kNarVersionMagic1 = "nix-archive-1";
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/args.cc b/third_party/nix/src/libutil/args.cc
deleted file mode 100644
index 2be8a1b0ce..0000000000
--- a/third_party/nix/src/libutil/args.cc
+++ /dev/null
@@ -1,219 +0,0 @@
-#include "libutil/args.hh"
-
-#include "libutil/hash.hh"
-
-namespace nix {
-
-Args::FlagMaker Args::mkFlag() { return FlagMaker(*this); }
-
-Args::FlagMaker::~FlagMaker() {
-  assert(!flag->longName.empty());
-  args.longFlags[flag->longName] = flag;
-  if (flag->shortName != 0) {
-    args.shortFlags[flag->shortName] = flag;
-  }
-}
-
-void Args::parseCmdline(const Strings& _cmdline) {
-  Strings pendingArgs;
-  bool dashDash = false;
-
-  Strings cmdline(_cmdline);
-
-  for (auto pos = cmdline.begin(); pos != cmdline.end();) {
-    auto arg = *pos;
-
-    /* Expand compound dash options (i.e., `-qlf' -> `-q -l -f',
-       `-j3` -> `-j 3`). */
-    if (!dashDash && arg.length() > 2 && arg[0] == '-' && arg[1] != '-' &&
-        (isalpha(arg[1]) != 0)) {
-      *pos = std::string("-") + arg[1];
-      auto next = pos;
-      ++next;
-      for (unsigned int j = 2; j < arg.length(); j++) {
-        if (isalpha(arg[j]) != 0) {
-          cmdline.insert(next, std::string("-") + arg[j]);
-        } else {
-          cmdline.insert(next, std::string(arg, j));
-          break;
-        }
-      }
-      arg = *pos;
-    }
-
-    if (!dashDash && arg == "--") {
-      dashDash = true;
-      ++pos;
-    } else if (!dashDash && std::string(arg, 0, 1) == "-") {
-      if (!processFlag(pos, cmdline.end())) {
-        throw UsageError(format("unrecognised flag '%1%'") % arg);
-      }
-    } else {
-      pendingArgs.push_back(*pos++);
-      if (processArgs(pendingArgs, false)) {
-        pendingArgs.clear();
-      }
-    }
-  }
-
-  processArgs(pendingArgs, true);
-}
-
-void Args::printHelp(const std::string& programName, std::ostream& out) {
-  std::cout << "Usage: " << programName << " <FLAGS>...";
-  for (auto& exp : expectedArgs) {
-    std::cout << renderLabels({exp.label});
-    // FIXME: handle arity > 1
-    if (exp.arity == 0) {
-      std::cout << "...";
-    }
-    if (exp.optional) {
-      std::cout << "?";
-    }
-  }
-  std::cout << "\n";
-
-  auto s = description();
-  if (!s.empty()) {
-    std::cout << "\nSummary: " << s << ".\n";
-  }
-
-  if (!longFlags.empty() != 0u) {
-    std::cout << "\n";
-    std::cout << "Flags:\n";
-    printFlags(out);
-  }
-}
-
-void Args::printFlags(std::ostream& out) {
-  Table2 table;
-  for (auto& flag : longFlags) {
-    if (hiddenCategories.count(flag.second->category) != 0u) {
-      continue;
-    }
-    table.push_back(std::make_pair(
-        (flag.second->shortName != 0
-             ? std::string("-") + flag.second->shortName + ", "
-             : "    ") +
-            "--" + flag.first + renderLabels(flag.second->labels),
-        flag.second->description));
-  }
-  printTable(out, table);
-}
-
-bool Args::processFlag(Strings::iterator& pos, Strings::iterator end) {
-  assert(pos != end);
-
-  auto process = [&](const std::string& name, const Flag& flag) -> bool {
-    ++pos;
-    std::vector<std::string> args;
-    for (size_t n = 0; n < flag.arity; ++n) {
-      if (pos == end) {
-        if (flag.arity == ArityAny) {
-          break;
-        }
-        throw UsageError(format("flag '%1%' requires %2% argument(s)") % name %
-                         flag.arity);
-      }
-      args.push_back(*pos++);
-    }
-    flag.handler(std::move(args));
-    return true;
-  };
-
-  if (std::string(*pos, 0, 2) == "--") {
-    auto i = longFlags.find(std::string(*pos, 2));
-    if (i == longFlags.end()) {
-      return false;
-    }
-    return process("--" + i->first, *i->second);
-  }
-
-  if (std::string(*pos, 0, 1) == "-" && pos->size() == 2) {
-    auto c = (*pos)[1];
-    auto i = shortFlags.find(c);
-    if (i == shortFlags.end()) {
-      return false;
-    }
-    return process(std::string("-") + c, *i->second);
-  }
-
-  return false;
-}
-
-bool Args::processArgs(const Strings& args, bool finish) {
-  if (expectedArgs.empty()) {
-    if (!args.empty()) {
-      throw UsageError(format("unexpected argument '%1%'") % args.front());
-    }
-    return true;
-  }
-
-  auto& exp = expectedArgs.front();
-
-  bool res = false;
-
-  if ((exp.arity == 0 && finish) ||
-      (exp.arity > 0 && args.size() == exp.arity)) {
-    std::vector<std::string> ss;
-    for (auto& s : args) {
-      ss.push_back(s);
-    }
-    exp.handler(std::move(ss));
-    expectedArgs.pop_front();
-    res = true;
-  }
-
-  if (finish && !expectedArgs.empty() && !expectedArgs.front().optional) {
-    throw UsageError("more arguments are required");
-  }
-
-  return res;
-}
-
-Args::FlagMaker& Args::FlagMaker::mkHashTypeFlag(HashType* ht) {
-  arity(1);
-  label("type");
-  description("hash algorithm ('md5', 'sha1', 'sha256', or 'sha512')");
-  handler([ht](const std::string& s) {
-    *ht = parseHashType(s);
-    if (*ht == htUnknown) {
-      throw UsageError("unknown hash type '%1%'", s);
-    }
-  });
-  return *this;
-}
-
-Strings argvToStrings(int argc, char** argv) {
-  Strings args;
-  argc--;
-  argv++;
-  while ((argc--) != 0) {
-    args.push_back(*argv++);
-  }
-  return args;
-}
-
-std::string renderLabels(const Strings& labels) {
-  std::string res;
-  for (auto label : labels) {
-    for (auto& c : label) {
-      c = std::toupper(c);
-    }
-    res += " <" + label + ">";
-  }
-  return res;
-}
-
-void printTable(std::ostream& out, const Table2& table) {
-  size_t max = 0;
-  for (auto& row : table) {
-    max = std::max(max, row.first.size());
-  }
-  for (auto& row : table) {
-    out << "  " << row.first << std::string(max - row.first.size() + 2, ' ')
-        << row.second << "\n";
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/args.hh b/third_party/nix/src/libutil/args.hh
deleted file mode 100644
index bb1ef43912..0000000000
--- a/third_party/nix/src/libutil/args.hh
+++ /dev/null
@@ -1,221 +0,0 @@
-#pragma once
-
-#include <iostream>
-#include <map>
-#include <memory>
-
-#include <absl/strings/numbers.h>
-
-#include "libutil/util.hh"
-
-namespace nix {
-
-MakeError(UsageError, Error);
-
-enum HashType : char;
-
-class Args {
- public:
-  /* Parse the command line, throwing a UsageError if something goes
-     wrong. */
-  void parseCmdline(const Strings& cmdline);
-
-  virtual void printHelp(const std::string& programName, std::ostream& out);
-
-  virtual std::string description() { return ""; }
-
- protected:
-  static const size_t ArityAny = std::numeric_limits<size_t>::max();
-
-  /* Flags. */
-  struct Flag {
-    typedef std::shared_ptr<Flag> ptr;
-    std::string longName;
-    char shortName = 0;
-    std::string description;
-    Strings labels;
-    size_t arity = 0;
-    std::function<void(std::vector<std::string>)> handler;
-    std::string category;
-  };
-
-  std::map<std::string, Flag::ptr> longFlags;
-  std::map<char, Flag::ptr> shortFlags;
-
-  virtual bool processFlag(Strings::iterator& pos, Strings::iterator end);
-
-  virtual void printFlags(std::ostream& out);
-
-  /* Positional arguments. */
-  struct ExpectedArg {
-    std::string label;
-    size_t arity;  // 0 = any
-    bool optional;
-    std::function<void(std::vector<std::string>)> handler;
-  };
-
-  std::list<ExpectedArg> expectedArgs;
-
-  virtual bool processArgs(const Strings& args, bool finish);
-
-  std::set<std::string> hiddenCategories;
-
- public:
-  class FlagMaker {
-    Args& args;
-    Flag::ptr flag;
-    friend class Args;
-    explicit FlagMaker(Args& args)
-        : args(args), flag(std::make_shared<Flag>()){};
-
-   public:
-    ~FlagMaker();
-    FlagMaker& longName(const std::string& s) {
-      flag->longName = s;
-      return *this;
-    };
-    FlagMaker& shortName(char s) {
-      flag->shortName = s;
-      return *this;
-    };
-    FlagMaker& description(const std::string& s) {
-      flag->description = s;
-      return *this;
-    };
-    FlagMaker& label(const std::string& l) {
-      flag->arity = 1;
-      flag->labels = {l};
-      return *this;
-    };
-    FlagMaker& labels(const Strings& ls) {
-      flag->arity = ls.size();
-      flag->labels = ls;
-      return *this;
-    };
-    FlagMaker& arity(size_t arity) {
-      flag->arity = arity;
-      return *this;
-    };
-    FlagMaker& handler(std::function<void(std::vector<std::string>)> handler) {
-      flag->handler = handler;
-      return *this;
-    };
-    FlagMaker& handler(std::function<void()> handler) {
-      flag->handler = [handler](std::vector<std::string>) { handler(); };
-      return *this;
-    };
-    FlagMaker& handler(std::function<void(std::string)> handler) {
-      flag->arity = 1;
-      flag->handler = [handler](std::vector<std::string> ss) {
-        handler(std::move(ss[0]));
-      };
-      return *this;
-    };
-    FlagMaker& category(const std::string& s) {
-      flag->category = s;
-      return *this;
-    };
-
-    template <class T>
-    FlagMaker& dest(T* dest) {
-      flag->arity = 1;
-      flag->handler = [=](std::vector<std::string> ss) { *dest = ss[0]; };
-      return *this;
-    }
-
-    template <class T>
-    FlagMaker& set(T* dest, const T& val) {
-      flag->arity = 0;
-      flag->handler = [=](std::vector<std::string> ss) { *dest = val; };
-      return *this;
-    }
-
-    FlagMaker& mkHashTypeFlag(HashType* ht);
-  };
-
-  FlagMaker mkFlag();
-
-  /* Helper functions for constructing flags / positional
-     arguments. */
-
-  void mkFlag1(char shortName, const std::string& longName,
-               const std::string& label, const std::string& description,
-               std::function<void(std::string)> fun) {
-    mkFlag()
-        .shortName(shortName)
-        .longName(longName)
-        .labels({label})
-        .description(description)
-        .arity(1)
-        .handler([=](std::vector<std::string> ss) { fun(ss[0]); });
-  }
-
-  void mkFlag(char shortName, const std::string& name,
-              const std::string& description, bool* dest) {
-    mkFlag(shortName, name, description, dest, true);
-  }
-
-  template <class T>
-  void mkFlag(char shortName, const std::string& longName,
-              const std::string& description, T* dest, const T& value) {
-    mkFlag()
-        .shortName(shortName)
-        .longName(longName)
-        .description(description)
-        .handler([=](std::vector<std::string> ss) { *dest = value; });
-  }
-
-  template <class I>
-  void mkIntFlag(char shortName, const std::string& longName,
-                 const std::string& description, I* dest) {
-    mkFlag<I>(shortName, longName, description, [=](I n) { *dest = n; });
-  }
-
-  template <class I>
-  void mkFlag(char shortName, const std::string& longName,
-              const std::string& description, std::function<void(I)> fun) {
-    mkFlag()
-        .shortName(shortName)
-        .longName(longName)
-        .labels({"N"})
-        .description(description)
-        .arity(1)
-        .handler([=](std::vector<std::string> ss) {
-          I n;
-          if (!absl::SimpleAtoi(ss[0], &n)) {
-            throw UsageError("flag '--%s' requires a integer argument",
-                             longName);
-          }
-          fun(n);
-        });
-  }
-
-  /* Expect a string argument. */
-  void expectArg(const std::string& label, std::string* dest,
-                 bool optional = false) {
-    expectedArgs.push_back(
-        ExpectedArg{label, 1, optional,
-                    [=](std::vector<std::string> ss) { *dest = ss[0]; }});
-  }
-
-  /* Expect 0 or more arguments. */
-  void expectArgs(const std::string& label, std::vector<std::string>* dest) {
-    expectedArgs.push_back(ExpectedArg{
-        label, 0, false,
-        [=](std::vector<std::string> ss) { *dest = std::move(ss); }});
-  }
-
-  friend class MultiCommand;
-};
-
-Strings argvToStrings(int argc, char** argv);
-
-/* Helper function for rendering argument labels. */
-std::string renderLabels(const Strings& labels);
-
-/* Helper function for printing 2-column tables. */
-using Table2 = std::vector<std::pair<std::string, std::string> >;
-
-void printTable(std::ostream& out, const Table2& table);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/compression.cc b/third_party/nix/src/libutil/compression.cc
deleted file mode 100644
index d0895ca5fd..0000000000
--- a/third_party/nix/src/libutil/compression.cc
+++ /dev/null
@@ -1,400 +0,0 @@
-#include "libutil/compression.hh"
-
-#include <cstdio>
-#include <cstring>
-#include <iostream>
-
-#include <brotli/decode.h>
-#include <brotli/encode.h>
-#include <bzlib.h>
-#include <glog/logging.h>
-#include <lzma.h>
-
-#include "libutil/finally.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-// Don't feed brotli too much at once.
-struct ChunkedCompressionSink : CompressionSink {
-  uint8_t outbuf[32 * 1024];
-
-  void write(const unsigned char* data, size_t len) override {
-    const size_t CHUNK_SIZE = sizeof(outbuf) << 2;
-    while (len != 0u) {
-      size_t n = std::min(CHUNK_SIZE, len);
-      writeInternal(data, n);
-      data += n;
-      len -= n;
-    }
-  }
-
-  virtual void writeInternal(const unsigned char* data, size_t len) = 0;
-};
-
-struct NoneSink : CompressionSink {
-  Sink& nextSink;
-  explicit NoneSink(Sink& nextSink) : nextSink(nextSink) {}
-  void finish() override { flush(); }
-  void write(const unsigned char* data, size_t len) override {
-    nextSink(data, len);
-  }
-};
-
-struct XzDecompressionSink : CompressionSink {
-  Sink& nextSink;
-  uint8_t outbuf[BUFSIZ];
-  lzma_stream strm = LZMA_STREAM_INIT;
-  bool finished = false;
-
-  explicit XzDecompressionSink(Sink& nextSink) : nextSink(nextSink) {
-    lzma_ret ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
-    if (ret != LZMA_OK) {
-      throw CompressionError("unable to initialise lzma decoder");
-    }
-
-    strm.next_out = outbuf;
-    strm.avail_out = sizeof(outbuf);
-  }
-
-  ~XzDecompressionSink() override { lzma_end(&strm); }
-
-  void finish() override {
-    CompressionSink::flush();
-    write(nullptr, 0);
-  }
-
-  void write(const unsigned char* data, size_t len) override {
-    strm.next_in = data;
-    strm.avail_in = len;
-
-    while (!finished && ((data == nullptr) || (strm.avail_in != 0u))) {
-      checkInterrupt();
-
-      lzma_ret ret = lzma_code(&strm, data != nullptr ? LZMA_RUN : LZMA_FINISH);
-      if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
-        throw CompressionError("error %d while decompressing xz file", ret);
-      }
-
-      finished = ret == LZMA_STREAM_END;
-
-      if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) {
-        nextSink(outbuf, sizeof(outbuf) - strm.avail_out);
-        strm.next_out = outbuf;
-        strm.avail_out = sizeof(outbuf);
-      }
-    }
-  }
-};
-
-struct BzipDecompressionSink : ChunkedCompressionSink {
-  Sink& nextSink;
-  bz_stream strm;
-  bool finished = false;
-
-  explicit BzipDecompressionSink(Sink& nextSink) : nextSink(nextSink) {
-    memset(&strm, 0, sizeof(strm));
-    int ret = BZ2_bzDecompressInit(&strm, 0, 0);
-    if (ret != BZ_OK) {
-      throw CompressionError("unable to initialise bzip2 decoder");
-    }
-
-    strm.next_out = reinterpret_cast<char*>(outbuf);
-    strm.avail_out = sizeof(outbuf);
-  }
-
-  ~BzipDecompressionSink() override { BZ2_bzDecompressEnd(&strm); }
-
-  void finish() override {
-    flush();
-    write(nullptr, 0);
-  }
-
-  void writeInternal(const unsigned char* data, size_t len) override {
-    assert(len <= std::numeric_limits<decltype(strm.avail_in)>::max());
-
-    strm.next_in = (char*)data;
-    strm.avail_in = len;
-
-    while (strm.avail_in != 0u) {
-      checkInterrupt();
-
-      int ret = BZ2_bzDecompress(&strm);
-      if (ret != BZ_OK && ret != BZ_STREAM_END) {
-        throw CompressionError("error while decompressing bzip2 file");
-      }
-
-      finished = ret == BZ_STREAM_END;
-
-      if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) {
-        nextSink(outbuf, sizeof(outbuf) - strm.avail_out);
-        strm.next_out = reinterpret_cast<char*>(outbuf);
-        strm.avail_out = sizeof(outbuf);
-      }
-    }
-  }
-};
-
-struct BrotliDecompressionSink : ChunkedCompressionSink {
-  Sink& nextSink;
-  BrotliDecoderState* state;
-  bool finished = false;
-
-  explicit BrotliDecompressionSink(Sink& nextSink) : nextSink(nextSink) {
-    state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
-    if (state == nullptr) {
-      throw CompressionError("unable to initialize brotli decoder");
-    }
-  }
-
-  ~BrotliDecompressionSink() override { BrotliDecoderDestroyInstance(state); }
-
-  void finish() override {
-    flush();
-    writeInternal(nullptr, 0);
-  }
-
-  void writeInternal(const unsigned char* data, size_t len) override {
-    const uint8_t* next_in = data;
-    size_t avail_in = len;
-    uint8_t* next_out = outbuf;
-    size_t avail_out = sizeof(outbuf);
-
-    while (!finished && ((data == nullptr) || (avail_in != 0u))) {
-      checkInterrupt();
-
-      if (BrotliDecoderDecompressStream(state, &avail_in, &next_in, &avail_out,
-                                        &next_out, nullptr) == 0u) {
-        throw CompressionError("error while decompressing brotli file");
-      }
-
-      if (avail_out < sizeof(outbuf) || avail_in == 0) {
-        nextSink(outbuf, sizeof(outbuf) - avail_out);
-        next_out = outbuf;
-        avail_out = sizeof(outbuf);
-      }
-
-      finished = (BrotliDecoderIsFinished(state) != 0);
-    }
-  }
-};
-
-ref<std::string> decompress(const std::string& method, const std::string& in) {
-  StringSink ssink;
-  auto sink = makeDecompressionSink(method, ssink);
-  (*sink)(in);
-  sink->finish();
-  return ssink.s;
-}
-
-ref<CompressionSink> makeDecompressionSink(const std::string& method,
-                                           Sink& nextSink) {
-  if (method == "none" || method.empty()) {
-    return make_ref<NoneSink>(nextSink);
-  }
-  if (method == "xz") {
-    return make_ref<XzDecompressionSink>(nextSink);
-  } else if (method == "bzip2") {
-    return make_ref<BzipDecompressionSink>(nextSink);
-  } else if (method == "br") {
-    return make_ref<BrotliDecompressionSink>(nextSink);
-  } else {
-    throw UnknownCompressionMethod("unknown compression method '%s'", method);
-  }
-}
-
-struct XzCompressionSink : CompressionSink {
-  Sink& nextSink;
-  uint8_t outbuf[BUFSIZ];
-  lzma_stream strm = LZMA_STREAM_INIT;
-  bool finished = false;
-
-  XzCompressionSink(Sink& nextSink, bool parallel) : nextSink(nextSink) {
-    lzma_ret ret;
-    bool done = false;
-
-    if (parallel) {
-      lzma_mt mt_options = {};
-      mt_options.flags = 0;
-      mt_options.timeout = 300;  // Using the same setting as the xz cmd line
-      mt_options.preset = LZMA_PRESET_DEFAULT;
-      mt_options.filters = NULL;
-      mt_options.check = LZMA_CHECK_CRC64;
-      mt_options.threads = lzma_cputhreads();
-      mt_options.block_size = 0;
-      if (mt_options.threads == 0) {
-        mt_options.threads = 1;
-      }
-      // FIXME: maybe use lzma_stream_encoder_mt_memusage() to control the
-      // number of threads.
-      ret = lzma_stream_encoder_mt(&strm, &mt_options);
-      done = true;
-    }
-
-    if (!done) {
-      ret = lzma_easy_encoder(&strm, 6, LZMA_CHECK_CRC64);
-    }
-
-    if (ret != LZMA_OK) {
-      throw CompressionError("unable to initialise lzma encoder");
-    }
-
-    // FIXME: apply the x86 BCJ filter?
-
-    strm.next_out = outbuf;
-    strm.avail_out = sizeof(outbuf);
-  }
-
-  ~XzCompressionSink() override { lzma_end(&strm); }
-
-  void finish() override {
-    CompressionSink::flush();
-    write(nullptr, 0);
-  }
-
-  void write(const unsigned char* data, size_t len) override {
-    strm.next_in = data;
-    strm.avail_in = len;
-
-    while (!finished && ((data == nullptr) || (strm.avail_in != 0u))) {
-      checkInterrupt();
-
-      lzma_ret ret = lzma_code(&strm, data != nullptr ? LZMA_RUN : LZMA_FINISH);
-      if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
-        throw CompressionError("error %d while compressing xz file", ret);
-      }
-
-      finished = ret == LZMA_STREAM_END;
-
-      if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) {
-        nextSink(outbuf, sizeof(outbuf) - strm.avail_out);
-        strm.next_out = outbuf;
-        strm.avail_out = sizeof(outbuf);
-      }
-    }
-  }
-};
-
-struct BzipCompressionSink : ChunkedCompressionSink {
-  Sink& nextSink;
-  bz_stream strm;
-  bool finished = false;
-
-  explicit BzipCompressionSink(Sink& nextSink) : nextSink(nextSink) {
-    memset(&strm, 0, sizeof(strm));
-    int ret = BZ2_bzCompressInit(&strm, 9, 0, 30);
-    if (ret != BZ_OK) {
-      throw CompressionError("unable to initialise bzip2 encoder");
-    }
-
-    strm.next_out = reinterpret_cast<char*>(outbuf);
-    strm.avail_out = sizeof(outbuf);
-  }
-
-  ~BzipCompressionSink() override { BZ2_bzCompressEnd(&strm); }
-
-  void finish() override {
-    flush();
-    writeInternal(nullptr, 0);
-  }
-
-  void writeInternal(const unsigned char* data, size_t len) override {
-    assert(len <= std::numeric_limits<decltype(strm.avail_in)>::max());
-
-    strm.next_in = (char*)data;
-    strm.avail_in = len;
-
-    while (!finished && ((data == nullptr) || (strm.avail_in != 0u))) {
-      checkInterrupt();
-
-      int ret = BZ2_bzCompress(&strm, data != nullptr ? BZ_RUN : BZ_FINISH);
-      if (ret != BZ_RUN_OK && ret != BZ_FINISH_OK && ret != BZ_STREAM_END) {
-        throw CompressionError("error %d while compressing bzip2 file", ret);
-      }
-
-      finished = ret == BZ_STREAM_END;
-
-      if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) {
-        nextSink(outbuf, sizeof(outbuf) - strm.avail_out);
-        strm.next_out = reinterpret_cast<char*>(outbuf);
-        strm.avail_out = sizeof(outbuf);
-      }
-    }
-  }
-};
-
-struct BrotliCompressionSink : ChunkedCompressionSink {
-  Sink& nextSink;
-  uint8_t outbuf[BUFSIZ];
-  BrotliEncoderState* state;
-  bool finished = false;
-
-  explicit BrotliCompressionSink(Sink& nextSink) : nextSink(nextSink) {
-    state = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
-    if (state == nullptr) {
-      throw CompressionError("unable to initialise brotli encoder");
-    }
-  }
-
-  ~BrotliCompressionSink() override { BrotliEncoderDestroyInstance(state); }
-
-  void finish() override {
-    flush();
-    writeInternal(nullptr, 0);
-  }
-
-  void writeInternal(const unsigned char* data, size_t len) override {
-    const uint8_t* next_in = data;
-    size_t avail_in = len;
-    uint8_t* next_out = outbuf;
-    size_t avail_out = sizeof(outbuf);
-
-    while (!finished && ((data == nullptr) || (avail_in != 0u))) {
-      checkInterrupt();
-
-      if (BrotliEncoderCompressStream(state,
-                                      data != nullptr ? BROTLI_OPERATION_PROCESS
-                                                      : BROTLI_OPERATION_FINISH,
-                                      &avail_in, &next_in, &avail_out,
-                                      &next_out, nullptr) == 0) {
-        throw CompressionError("error while compressing brotli compression");
-      }
-
-      if (avail_out < sizeof(outbuf) || avail_in == 0) {
-        nextSink(outbuf, sizeof(outbuf) - avail_out);
-        next_out = outbuf;
-        avail_out = sizeof(outbuf);
-      }
-
-      finished = (BrotliEncoderIsFinished(state) != 0);
-    }
-  }
-};
-
-ref<CompressionSink> makeCompressionSink(const std::string& method,
-                                         Sink& nextSink, const bool parallel) {
-  if (method == "none") {
-    return make_ref<NoneSink>(nextSink);
-  }
-  if (method == "xz") {
-    return make_ref<XzCompressionSink>(nextSink, parallel);
-  } else if (method == "bzip2") {
-    return make_ref<BzipCompressionSink>(nextSink);
-  } else if (method == "br") {
-    return make_ref<BrotliCompressionSink>(nextSink);
-  } else {
-    throw UnknownCompressionMethod(format("unknown compression method '%s'") %
-                                   method);
-  }
-}
-
-ref<std::string> compress(const std::string& method, const std::string& in,
-                          const bool parallel) {
-  StringSink ssink;
-  auto sink = makeCompressionSink(method, ssink, parallel);
-  (*sink)(in);
-  sink->finish();
-  return ssink.s;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/compression.hh b/third_party/nix/src/libutil/compression.hh
deleted file mode 100644
index 8ec340ab74..0000000000
--- a/third_party/nix/src/libutil/compression.hh
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include <string>
-
-#include "libutil/ref.hh"
-#include "libutil/serialise.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-struct CompressionSink : BufferedSink {
-  virtual void finish() = 0;
-};
-
-ref<std::string> decompress(const std::string& method, const std::string& in);
-
-ref<CompressionSink> makeDecompressionSink(const std::string& method,
-                                           Sink& nextSink);
-
-ref<std::string> compress(const std::string& method, const std::string& in,
-                          const bool parallel = false);
-
-ref<CompressionSink> makeCompressionSink(const std::string& method,
-                                         Sink& nextSink,
-                                         const bool parallel = false);
-
-MakeError(UnknownCompressionMethod, Error);
-
-MakeError(CompressionError, Error);
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/config.cc b/third_party/nix/src/libutil/config.cc
deleted file mode 100644
index 7c6e7af487..0000000000
--- a/third_party/nix/src/libutil/config.cc
+++ /dev/null
@@ -1,370 +0,0 @@
-#include "libutil/config.hh"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <absl/strings/numbers.h>
-#include <absl/strings/str_split.h>
-#include <absl/strings/string_view.h>
-#include <glog/logging.h>
-
-#include "libutil/args.hh"
-#include "libutil/json.hh"
-
-namespace nix {
-
-bool Config::set(const std::string& name, const std::string& value) {
-  auto i = _settings.find(name);
-  if (i == _settings.end()) {
-    return false;
-  }
-  i->second.setting->set(value);
-  i->second.setting->overriden = true;
-  return true;
-}
-
-void Config::addSetting(AbstractSetting* setting) {
-  _settings.emplace(setting->name, Config::SettingData(false, setting));
-  for (auto& alias : setting->aliases) {
-    _settings.emplace(alias, Config::SettingData(true, setting));
-  }
-
-  bool set = false;
-
-  auto i = unknownSettings.find(setting->name);
-  if (i != unknownSettings.end()) {
-    setting->set(i->second);
-    setting->overriden = true;
-    unknownSettings.erase(i);
-    set = true;
-  }
-
-  for (auto& alias : setting->aliases) {
-    auto i = unknownSettings.find(alias);
-    if (i != unknownSettings.end()) {
-      if (set) {
-        LOG(WARNING) << "setting '" << alias
-                     << "' is set, but it's an alias of '" << setting->name
-                     << "', which is also set";
-      }
-
-      else {
-        setting->set(i->second);
-        setting->overriden = true;
-        unknownSettings.erase(i);
-        set = true;
-      }
-    }
-  }
-}
-
-void AbstractConfig::warnUnknownSettings() {
-  for (auto& s : unknownSettings) {
-    LOG(WARNING) << "unknown setting: " << s.first;
-  }
-}
-
-void AbstractConfig::reapplyUnknownSettings() {
-  auto unknownSettings2 = std::move(unknownSettings);
-  for (auto& s : unknownSettings2) {
-    set(s.first, s.second);
-  }
-}
-
-void Config::getSettings(std::map<std::string, SettingInfo>& res,
-                         bool overridenOnly) {
-  for (auto& opt : _settings) {
-    if (!opt.second.isAlias &&
-        (!overridenOnly || opt.second.setting->overriden)) {
-      res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(),
-                                         opt.second.setting->description});
-    }
-  }
-}
-
-void AbstractConfig::applyConfigFile(const Path& path) {
-  try {
-    std::string contents = readFile(path);
-
-    unsigned int pos = 0;
-
-    while (pos < contents.size()) {
-      std::string line;
-      while (pos < contents.size() && contents[pos] != '\n') {
-        line += contents[pos++];
-      }
-      pos++;
-
-      std::string::size_type hash = line.find('#');
-      if (hash != std::string::npos) {
-        line = std::string(line, 0, hash);
-      }
-
-      // TODO(tazjin): absl::string_view after path functions are fixed.
-      std::vector<std::string> tokens = absl::StrSplit(
-          line, absl::ByAnyChar(" \t\n\r"), absl::SkipWhitespace());
-      if (tokens.empty()) {
-        continue;
-      }
-
-      if (tokens.size() < 2) {
-        throw UsageError("illegal configuration line '%1%' in '%2%'", line,
-                         path);
-      }
-
-      auto include = false;
-      auto ignoreMissing = false;
-      if (tokens[0] == "include") {
-        include = true;
-      } else if (tokens[0] == "!include") {
-        include = true;
-        ignoreMissing = true;
-      }
-
-      if (include) {
-        if (tokens.size() != 2) {
-          throw UsageError("illegal configuration line '%1%' in '%2%'", line,
-                           path);
-        }
-        auto p = absPath(tokens[1], dirOf(path));
-        if (pathExists(p)) {
-          applyConfigFile(p);
-        } else if (!ignoreMissing) {
-          throw Error("file '%1%' included from '%2%' not found", p, path);
-        }
-        continue;
-      }
-
-      if (tokens[1] != "=") {
-        throw UsageError("illegal configuration line '%1%' in '%2%'", line,
-                         path);
-      }
-
-      std::string name = tokens[0];
-
-      auto i = tokens.begin();
-      advance(i, 2);
-
-      set(name,
-          concatStringsSep(" ", Strings(i, tokens.end())));  // FIXME: slow
-    };
-  } catch (SysError&) {
-  }
-}
-
-void Config::resetOverriden() {
-  for (auto& s : _settings) {
-    s.second.setting->overriden = false;
-  }
-}
-
-void Config::toJSON(JSONObject& out) {
-  for (auto& s : _settings) {
-    if (!s.second.isAlias) {
-      JSONObject out2(out.object(s.first));
-      out2.attr("description", s.second.setting->description);
-      JSONPlaceholder out3(out2.placeholder("value"));
-      s.second.setting->toJSON(out3);
-    }
-  }
-}
-
-void Config::convertToArgs(Args& args, const std::string& category) {
-  for (auto& s : _settings) {
-    if (!s.second.isAlias) {
-      s.second.setting->convertToArg(args, category);
-    }
-  }
-}
-
-AbstractSetting::AbstractSetting(std::string name, std::string description,
-                                 std::set<std::string> aliases)
-    : name(std::move(name)),
-      description(std::move(description)),
-      aliases(std::move(aliases)) {}
-
-void AbstractSetting::toJSON(JSONPlaceholder& out) { out.write(to_string()); }
-
-void AbstractSetting::convertToArg(Args& args, const std::string& category) {}
-
-template <typename T>
-void BaseSetting<T>::toJSON(JSONPlaceholder& out) {
-  out.write(value);
-}
-
-template <typename T>
-void BaseSetting<T>::convertToArg(Args& args, const std::string& category) {
-  args.mkFlag()
-      .longName(name)
-      .description(description)
-      .arity(1)
-      .handler([=](std::vector<std::string> ss) {
-        overriden = true;
-        set(ss[0]);
-      })
-      .category(category);
-}
-
-template <>
-void BaseSetting<std::string>::set(const std::string& str) {
-  value = str;
-}
-
-template <>
-std::string BaseSetting<std::string>::to_string() {
-  return value;
-}
-
-template <typename T>
-void BaseSetting<T>::set(const std::string& str) {
-  static_assert(std::is_integral<T>::value, "Integer required.");
-  if (!absl::SimpleAtoi(str, &value)) {
-    throw UsageError("setting '%s' has invalid value '%s'", name, str);
-  }
-}
-
-template <typename T>
-std::string BaseSetting<T>::to_string() {
-  static_assert(std::is_integral<T>::value, "Integer required.");
-  return std::to_string(value);
-}
-
-template <>
-void BaseSetting<bool>::set(const std::string& str) {
-  if (str == "true" || str == "yes" || str == "1") {
-    value = true;
-  } else if (str == "false" || str == "no" || str == "0") {
-    value = false;
-  } else {
-    throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str);
-  }
-}
-
-template <>
-std::string BaseSetting<bool>::to_string() {
-  return value ? "true" : "false";
-}
-
-template <>
-void BaseSetting<bool>::convertToArg(Args& args, const std::string& category) {
-  args.mkFlag()
-      .longName(name)
-      .description(description)
-      .handler([=](const std::vector<std::string>& ss) { override(true); })
-      .category(category);
-  args.mkFlag()
-      .longName("no-" + name)
-      .description(description)
-      .handler([=](const std::vector<std::string>& ss) { override(false); })
-      .category(category);
-}
-
-template <>
-void BaseSetting<Strings>::set(const std::string& str) {
-  value = absl::StrSplit(str, absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-}
-
-template <>
-std::string BaseSetting<Strings>::to_string() {
-  return concatStringsSep(" ", value);
-}
-
-template <>
-void BaseSetting<Strings>::toJSON(JSONPlaceholder& out) {
-  JSONList list(out.list());
-  for (auto& s : value) {
-    list.elem(s);
-  }
-}
-
-template <>
-void BaseSetting<StringSet>::set(const std::string& str) {
-  value = absl::StrSplit(str, absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-}
-
-template <>
-std::string BaseSetting<StringSet>::to_string() {
-  return concatStringsSep(" ", value);
-}
-
-template <>
-void BaseSetting<StringSet>::toJSON(JSONPlaceholder& out) {
-  JSONList list(out.list());
-  for (auto& s : value) {
-    list.elem(s);
-  }
-}
-
-template class BaseSetting<int>;
-template class BaseSetting<unsigned int>;
-template class BaseSetting<long>;
-template class BaseSetting<unsigned long>;
-template class BaseSetting<long long>;
-template class BaseSetting<unsigned long long>;
-template class BaseSetting<bool>;
-template class BaseSetting<std::string>;
-template class BaseSetting<Strings>;
-template class BaseSetting<StringSet>;
-
-void PathSetting::set(const std::string& str) {
-  if (str.empty()) {
-    if (allowEmpty) {
-      value = "";
-    } else {
-      throw UsageError("setting '%s' cannot be empty", name);
-    }
-  } else {
-    value = canonPath(str);
-  }
-}
-
-bool GlobalConfig::set(const std::string& name, const std::string& value) {
-  for (auto& config : *configRegistrations) {
-    if (config->set(name, value)) {
-      return true;
-    }
-  }
-
-  unknownSettings.emplace(name, value);
-
-  return false;
-}
-
-void GlobalConfig::getSettings(std::map<std::string, SettingInfo>& res,
-                               bool overridenOnly) {
-  for (auto& config : *configRegistrations) {
-    config->getSettings(res, overridenOnly);
-  }
-}
-
-void GlobalConfig::resetOverriden() {
-  for (auto& config : *configRegistrations) {
-    config->resetOverriden();
-  }
-}
-
-void GlobalConfig::toJSON(JSONObject& out) {
-  for (auto& config : *configRegistrations) {
-    config->toJSON(out);
-  }
-}
-
-void GlobalConfig::convertToArgs(Args& args, const std::string& category) {
-  for (auto& config : *configRegistrations) {
-    config->convertToArgs(args, category);
-  }
-}
-
-GlobalConfig globalConfig;
-
-GlobalConfig::ConfigRegistrations* GlobalConfig::configRegistrations;
-
-GlobalConfig::Register::Register(Config* config) {
-  if (configRegistrations == nullptr) {
-    configRegistrations = new ConfigRegistrations;
-  }
-  configRegistrations->emplace_back(config);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/config.hh b/third_party/nix/src/libutil/config.hh
deleted file mode 100644
index 81b1c80e0e..0000000000
--- a/third_party/nix/src/libutil/config.hh
+++ /dev/null
@@ -1,228 +0,0 @@
-#include <map>
-#include <set>
-
-#include "libutil/types.hh"
-
-#pragma once
-
-namespace nix {
-
-class Args;
-class AbstractSetting;
-class JSONPlaceholder;
-class JSONObject;
-
-class AbstractConfig {
- protected:
-  StringMap unknownSettings;
-
-  explicit AbstractConfig(const StringMap& initials = {})
-      : unknownSettings(initials) {}
-
- public:
-  virtual bool set(const std::string& name, const std::string& value) = 0;
-
-  struct SettingInfo {
-    std::string value;
-    std::string description;
-  };
-
-  virtual void getSettings(std::map<std::string, SettingInfo>& res,
-                           bool overridenOnly = false) = 0;
-
-  void applyConfigFile(const Path& path);
-
-  virtual void resetOverriden() = 0;
-
-  virtual void toJSON(JSONObject& out) = 0;
-
-  virtual void convertToArgs(Args& args, const std::string& category) = 0;
-
-  void warnUnknownSettings();
-
-  void reapplyUnknownSettings();
-};
-
-/* A class to simplify providing configuration settings. The typical
-   use is to inherit Config and add Setting<T> members:
-
-   class MyClass : private Config
-   {
-     Setting<int> foo{this, 123, "foo", "the number of foos to use"};
-     Setting<std::string> bar{this, "blabla", "bar", "the name of the bar"};
-
-     MyClass() : Config(readConfigFile("/etc/my-app.conf"))
-     {
-       std::cout << foo << "\n"; // will print 123 unless overriden
-     }
-   };
-*/
-
-class Config : public AbstractConfig {
-  friend class AbstractSetting;
-
- public:
-  struct SettingData {
-    bool isAlias;
-    AbstractSetting* setting;
-    SettingData(bool isAlias, AbstractSetting* setting)
-        : isAlias(isAlias), setting(setting) {}
-  };
-
-  typedef std::map<std::string, SettingData> Settings;
-
- private:
-  Settings _settings;
-
- public:
-  explicit Config(const StringMap& initials = {}) : AbstractConfig(initials) {}
-
-  bool set(const std::string& name, const std::string& value) override;
-
-  void addSetting(AbstractSetting* setting);
-
-  void getSettings(std::map<std::string, SettingInfo>& res,
-                   bool overridenOnly = false) override;
-
-  void resetOverriden() override;
-
-  void toJSON(JSONObject& out) override;
-
-  void convertToArgs(Args& args, const std::string& category) override;
-};
-
-class AbstractSetting {
-  friend class Config;
-
- public:
-  const std::string name;
-  const std::string description;
-  const std::set<std::string> aliases;
-
-  int created = 123;
-
-  bool overriden = false;
-
- protected:
-  AbstractSetting(std::string name, std::string description,
-                  std::set<std::string> aliases);
-
-  virtual ~AbstractSetting() {
-    // Check against a gcc miscompilation causing our constructor
-    // not to run (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431).
-    assert(created == 123);
-  }
-
-  virtual void set(const std::string& value) = 0;
-
-  virtual std::string to_string() = 0;
-
-  virtual void toJSON(JSONPlaceholder& out);
-
-  virtual void convertToArg(Args& args, const std::string& category);
-
-  bool isOverriden() { return overriden; }
-};
-
-/* A setting of type T. */
-template <typename T>
-class BaseSetting : public AbstractSetting {
- protected:
-  T value;
-
- public:
-  BaseSetting(const T& def, const std::string& name,
-              const std::string& description,
-              const std::set<std::string>& aliases = {})
-      : AbstractSetting(name, description, aliases), value(def) {}
-
-  operator const T&() const { return value; }
-  operator T&() { return value; }
-  const T& get() const { return value; }
-  bool operator==(const T& v2) const { return value == v2; }
-  bool operator!=(const T& v2) const { return value != v2; }
-  void operator=(const T& v) { assign(v); }
-  virtual void assign(const T& v) { value = v; }
-
-  void set(const std::string& str) override;
-
-  virtual void override(const T& v) {
-    overriden = true;
-    value = v;
-  }
-
-  std::string to_string() override;
-
-  void convertToArg(Args& args, const std::string& category) override;
-
-  void toJSON(JSONPlaceholder& out) override;
-};
-
-template <typename T>
-std::ostream& operator<<(std::ostream& str, const BaseSetting<T>& opt) {
-  str << (const T&)opt;
-  return str;
-}
-
-template <typename T>
-bool operator==(const T& v1, const BaseSetting<T>& v2) {
-  return v1 == (const T&)v2;
-}
-
-template <typename T>
-class Setting : public BaseSetting<T> {
- public:
-  Setting(Config* options, const T& def, const std::string& name,
-          const std::string& description,
-          const std::set<std::string>& aliases = {})
-      : BaseSetting<T>(def, name, description, aliases) {
-    options->addSetting(this);
-  }
-
-  void operator=(const T& v) { this->assign(v); }
-};
-
-/* A special setting for Paths. These are automatically canonicalised
-   (e.g. "/foo//bar/" becomes "/foo/bar"). */
-class PathSetting : public BaseSetting<Path> {
-  bool allowEmpty;
-
- public:
-  PathSetting(Config* options, bool allowEmpty, const Path& def,
-              const std::string& name, const std::string& description,
-              const std::set<std::string>& aliases = {})
-      : BaseSetting<Path>(def, name, description, aliases),
-        allowEmpty(allowEmpty) {
-    options->addSetting(this);
-  }
-
-  void set(const std::string& str) override;
-
-  Path operator+(const char* p) const { return value + p; }
-
-  void operator=(const Path& v) { this->assign(v); }
-};
-
-struct GlobalConfig : public AbstractConfig {
-  using ConfigRegistrations = std::vector<Config*>;
-  static ConfigRegistrations* configRegistrations;
-
-  bool set(const std::string& name, const std::string& value) override;
-
-  void getSettings(std::map<std::string, SettingInfo>& res,
-                   bool overridenOnly = false) override;
-
-  void resetOverriden() override;
-
-  void toJSON(JSONObject& out) override;
-
-  void convertToArgs(Args& args, const std::string& category) override;
-
-  struct Register {
-    explicit Register(Config* config);
-  };
-};
-
-extern GlobalConfig globalConfig;
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/finally.hh b/third_party/nix/src/libutil/finally.hh
deleted file mode 100644
index 2ead8661a6..0000000000
--- a/third_party/nix/src/libutil/finally.hh
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include <functional>
-
-/* A trivial class to run a function at the end of a scope. */
-class Finally {
- private:
-  std::function<void()> fun;
-
- public:
-  explicit Finally(std::function<void()> fun) : fun(fun) {}
-  ~Finally() { fun(); }
-};
diff --git a/third_party/nix/src/libutil/hash.cc b/third_party/nix/src/libutil/hash.cc
deleted file mode 100644
index ba61254392..0000000000
--- a/third_party/nix/src/libutil/hash.cc
+++ /dev/null
@@ -1,484 +0,0 @@
-#include "libutil/hash.hh"
-
-#include <cstring>
-#include <iostream>
-
-#include <absl/strings/escaping.h>
-#include <absl/strings/str_format.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <openssl/md5.h>
-#include <openssl/sha.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "libutil/archive.hh"
-#include "libutil/istringstream_nocopy.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-std::optional<HashType> hash_type_from(nix::proto::HashType hash_type) {
-  switch (hash_type) {
-    case nix::proto::HashType::UNKNOWN:
-      return HashType::htUnknown;
-    case nix::proto::HashType::MD5:
-      return HashType::htMD5;
-    case nix::proto::HashType::SHA1:
-      return HashType::htSHA1;
-    case nix::proto::HashType::SHA256:
-      return HashType::htSHA256;
-    case nix::proto::HashType::SHA512:
-      return HashType::htSHA512;
-    default:
-      return {};
-  }
-}
-
-nix::proto::HashType HashTypeToProto(HashType hash_type) {
-  switch (hash_type) {
-    case HashType::htMD5:
-      return nix::proto::HashType::MD5;
-    case HashType::htSHA1:
-      return nix::proto::HashType::SHA1;
-    case HashType::htSHA256:
-      return nix::proto::HashType::SHA256;
-    case HashType::htSHA512:
-      return nix::proto::HashType::SHA512;
-    default:
-      return nix::proto::HashType::UNKNOWN;
-  }
-}
-
-void Hash::init() {
-  if (type == htMD5) {
-    hashSize = md5HashSize;
-  } else if (type == htSHA1) {
-    hashSize = sha1HashSize;
-  } else if (type == htSHA256) {
-    hashSize = sha256HashSize;
-  } else if (type == htSHA512) {
-    hashSize = sha512HashSize;
-  } else {
-    abort();
-  }
-  assert(hashSize <= maxHashSize);
-  memset(hash, 0, maxHashSize);
-}
-
-bool Hash::operator==(const Hash& h2) const {
-  if (hashSize != h2.hashSize) {
-    return false;
-  }
-  for (unsigned int i = 0; i < hashSize; i++) {
-    if (hash[i] != h2.hash[i]) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool Hash::operator!=(const Hash& h2) const { return !(*this == h2); }
-
-bool Hash::operator<(const Hash& h) const {
-  if (hashSize < h.hashSize) {
-    return true;
-  }
-  if (hashSize > h.hashSize) {
-    return false;
-  }
-  for (unsigned int i = 0; i < hashSize; i++) {
-    if (hash[i] < h.hash[i]) {
-      return true;
-    }
-    if (hash[i] > h.hash[i]) {
-      return false;
-    }
-  }
-  return false;
-}
-
-const std::string base16Chars = "0123456789abcdef";
-
-static std::string printHash16(const Hash& hash) {
-  char buf[hash.hashSize * 2];
-  for (unsigned int i = 0; i < hash.hashSize; i++) {
-    buf[i * 2] = base16Chars[hash.hash[i] >> 4];
-    buf[i * 2 + 1] = base16Chars[hash.hash[i] & 0x0f];
-  }
-  return std::string(buf, hash.hashSize * 2);
-}
-
-bool Hash::IsValidBase16(absl::string_view s) {
-  for (char c : s) {
-    if ('0' <= c && c <= '9') {
-      continue;
-    }
-    if ('a' <= c && c <= 'f') {
-      continue;
-    }
-    if ('A' <= c && c <= 'F') {
-      continue;
-    }
-    return false;
-  }
-  return true;
-}
-
-constexpr signed char kUnBase32[] = {
-    -1, -1, -1, -1, -1, -1, -1, -1, /* unprintables */
-    -1, -1, -1, -1, -1, -1, -1, -1, /* unprintables */
-    -1, -1, -1, -1, -1, -1, -1, -1, /* unprintables */
-    -1, -1, -1, -1, -1, -1, -1, -1, /* unprintables */
-    -1, -1, -1, -1, -1, -1, -1, -1, /* SP..' */
-    -1, -1, -1, -1, -1, -1, -1, -1, /* (../ */
-    0,  1,  2,  3,  4,  5,  6,  7,  /* 0..7 */
-    8,  9,  -1, -1, -1, -1, -1, -1, /* 8..? */
-    -1, -1, -1, -1, -1, -1, -1, -1, /* @..G */
-    -1, -1, -1, -1, -1, -1, -1, -1, /* H..O */
-    -1, -1, -1, -1, -1, -1, -1, -1, /* P..W */
-    -1, -1, -1, -1, -1, -1, -1, -1, /* X.._ */
-    -1, 10, 11, 12, 13, -1, 14, 15, /* `..g */
-    16, 17, 18, 19, 20, 21, 22, -1, /* h..o */
-    23, 24, 25, 26, -1, -1, 27, 28, /* p..w */
-    29, 30, 31, -1, -1, -1, -1, -1, /* x..DEL */
-
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* high */
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* high */
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* high */
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* high */
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* high */
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* high */
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* high */
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* high */
-};
-
-bool Hash::IsValidBase32(absl::string_view s) {
-  static_assert(sizeof(kUnBase32) == 256);
-
-  for (char c : s) {
-    if (kUnBase32[static_cast<unsigned char>(c)] == -1) {
-      return false;
-    }
-  }
-  return true;
-}
-
-std::string Hash::ToStorePathHash() const {
-  return compressHash(*this, kStorePathHashSize).to_string(Base32, false);
-}
-
-static std::string printHash32(const Hash& hash) {
-  assert(hash.hashSize);
-  size_t len = hash.base32Len();
-  assert(len);
-
-  std::string s;
-  s.reserve(len);
-
-  for (int n = static_cast<int>(len) - 1; n >= 0; n--) {
-    unsigned int b = n * 5;
-    unsigned int i = b / 8;
-    unsigned int j = b % 8;
-    unsigned char c =
-        (hash.hash[i] >> j) |
-        (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
-    s.push_back(base32Chars[c & 0x1f]);
-  }
-
-  return s;
-}
-
-std::string printHash16or32(const Hash& hash) {
-  return hash.to_string(hash.type == htMD5 ? Base16 : Base32, false);
-}
-
-std::string Hash::to_string(Base base, bool includeType) const {
-  std::string s;
-  if (base == SRI || includeType) {
-    s += printHashType(type);
-    s += base == SRI ? '-' : ':';
-  }
-  switch (base) {
-    case Base16:
-      s += printHash16(*this);
-      break;
-    case Base32:
-      s += printHash32(*this);
-      break;
-    case Base64:
-    case SRI:
-      std::string b64;
-      absl::Base64Escape(
-          std::string(reinterpret_cast<const char*>(hash), hashSize), &b64);
-      s += b64;
-      break;
-  }
-  return s;
-}
-
-Hash::Hash(std::string_view s, HashType type) : type(type) {
-  absl::StatusOr<Hash> result = deserialize(s, type);
-  *this = unwrap_throw(result);
-}
-
-// TODO(riking): change ht to an optional
-absl::StatusOr<Hash> Hash::deserialize(std::string_view s, HashType type) {
-  size_t pos = 0;
-  bool isSRI = false;
-
-  auto sep = s.find(':');
-  if (sep == std::string::npos) {
-    sep = s.find('-');
-    if (sep != std::string::npos) {
-      isSRI = true;
-    } else if (type == htUnknown) {
-      return absl::InvalidArgumentError(
-          absl::StrCat("hash string '", s, " does not include a type"));
-    }
-  }
-
-  HashType parsedType = type;
-  if (sep != std::string::npos) {
-    std::string hts = std::string(s, 0, sep);
-    parsedType = parseHashType(hts);
-    if (type != htUnknown && parsedType != type) {
-      return absl::InvalidArgumentError(
-          absl::StrCat("hash '", s, "' should have type '", printHashType(type),
-                       "', found '", printHashType(parsedType), "'"));
-    }
-    pos = sep + 1;
-  }
-
-  Hash dest(parsedType);
-
-  size_t size = s.size() - pos;
-  absl::string_view sv(s.data() + pos, size);
-
-  if (!isSRI && size == dest.base16Len()) {
-    std::string bytes;
-    if (!IsValidBase16(sv)) {
-      return absl::InvalidArgumentError(
-          absl::StrCat("invalid base-16 hash: bad character in '", s, "'"));
-    }
-    bytes = absl::HexStringToBytes(sv);
-    if (bytes.size() != dest.hashSize) {
-      return absl::InvalidArgumentError(
-          absl::StrCat("hash '", s, "' has wrong length for base16 ",
-                       printHashType(dest.type)));
-    }
-    memcpy(dest.hash, bytes.data(), dest.hashSize);
-  }
-
-  else if (!isSRI && size == dest.base32Len()) {
-    for (unsigned int n = 0; n < size; ++n) {
-      char c = sv[size - n - 1];
-      // range: -1, 0..31
-      signed char digit = kUnBase32[static_cast<unsigned char>(c)];
-      if (digit < 0) {
-        return absl::InvalidArgumentError(
-            absl::StrCat("invalid base-32 hash: bad character ",
-                         absl::CEscape(absl::string_view(&c, 1))));
-      }
-      unsigned int b = n * 5;
-      unsigned int i = b / 8;
-      unsigned int j = b % 8;
-      dest.hash[i] |= digit << j;
-
-      if (i < dest.hashSize - 1) {
-        dest.hash[i + 1] |= digit >> (8 - j);
-      } else {
-        if ((digit >> (8 - j)) != 0) {
-          return absl::InvalidArgumentError(
-              absl::StrCat("invalid base-32 hash '", s, "'"));
-        }
-      }
-    }
-  }
-
-  else if (isSRI || size == dest.base64Len()) {
-    std::string decoded;
-    if (!absl::Base64Unescape(sv, &decoded)) {
-      return absl::InvalidArgumentError("invalid base-64 hash");
-    }
-    if (decoded.size() != dest.hashSize) {
-      return absl::InvalidArgumentError(
-          absl::StrCat("hash '", s, "' has wrong length for base64 ",
-                       printHashType(dest.type)));
-    }
-    memcpy(dest.hash, decoded.data(), dest.hashSize);
-  }
-
-  else {
-    return absl::InvalidArgumentError(absl::StrCat(
-        "hash '", s, "' has wrong length for ", printHashType(dest.type)));
-  }
-
-  return dest;
-}
-
-Hash Hash::unwrap_throw(absl::StatusOr<Hash> hash) {
-  if (hash.ok()) {
-    return *hash;
-  } else {
-    throw BadHash(hash.status().message());
-  }
-}
-
-namespace hash {
-
-union Ctx {
-  MD5_CTX md5;
-  SHA_CTX sha1;
-  SHA256_CTX sha256;
-  SHA512_CTX sha512;
-};
-
-static void start(HashType ht, Ctx& ctx) {
-  if (ht == htMD5) {
-    MD5_Init(&ctx.md5);
-  } else if (ht == htSHA1) {
-    SHA1_Init(&ctx.sha1);
-  } else if (ht == htSHA256) {
-    SHA256_Init(&ctx.sha256);
-  } else if (ht == htSHA512) {
-    SHA512_Init(&ctx.sha512);
-  }
-}
-
-static void update(HashType ht, Ctx& ctx, const unsigned char* bytes,
-                   size_t len) {
-  if (ht == htMD5) {
-    MD5_Update(&ctx.md5, bytes, len);
-  } else if (ht == htSHA1) {
-    SHA1_Update(&ctx.sha1, bytes, len);
-  } else if (ht == htSHA256) {
-    SHA256_Update(&ctx.sha256, bytes, len);
-  } else if (ht == htSHA512) {
-    SHA512_Update(&ctx.sha512, bytes, len);
-  }
-}
-
-static void finish(HashType ht, Ctx& ctx, unsigned char* hash) {
-  if (ht == htMD5) {
-    MD5_Final(hash, &ctx.md5);
-  } else if (ht == htSHA1) {
-    SHA1_Final(hash, &ctx.sha1);
-  } else if (ht == htSHA256) {
-    SHA256_Final(hash, &ctx.sha256);
-  } else if (ht == htSHA512) {
-    SHA512_Final(hash, &ctx.sha512);
-  }
-}
-
-}  // namespace hash
-
-Hash hashString(HashType ht, const std::string& s) {
-  hash::Ctx ctx{};
-  Hash hash(ht);
-  start(ht, ctx);
-  update(ht, ctx, reinterpret_cast<const unsigned char*>(s.data()), s.length());
-  finish(ht, ctx, hash.hash);
-  return hash;
-}
-
-Hash hashFile(HashType ht, const Path& path) {
-  hash::Ctx ctx{};
-  Hash hash(ht);
-  start(ht, ctx);
-
-  AutoCloseFD fd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
-  if (!fd) {
-    throw SysError(format("opening file '%1%'") % path);
-  }
-
-  std::vector<unsigned char> buf(8192);
-  ssize_t n;
-  while ((n = read(fd.get(), buf.data(), buf.size())) != 0) {
-    checkInterrupt();
-    if (n == -1) {
-      throw SysError(format("reading file '%1%'") % path);
-    }
-    update(ht, ctx, buf.data(), n);
-  }
-
-  finish(ht, ctx, hash.hash);
-  return hash;
-}
-
-HashSink::HashSink(HashType ht)
-    : ht(ht), ctx(std::make_unique<hash::Ctx>()), bytes(0) {
-  start(ht, *ctx);
-}
-
-HashSink::~HashSink() { bufPos = 0; }
-
-void HashSink::write(const unsigned char* data, size_t len) {
-  bytes += len;
-  nix::hash::update(ht, *ctx, data, len);
-}
-
-HashResult HashSink::finish() {
-  flush();
-  Hash hash(ht);
-  nix::hash::finish(ht, *ctx, hash.hash);
-  return HashResult(hash, bytes);
-}
-
-HashResult HashSink::currentHash() {
-  flush();
-  nix::hash::Ctx ctx2 = *ctx;
-  Hash hash(ht);
-  nix::hash::finish(ht, ctx2, hash.hash);
-  return HashResult(hash, bytes);
-}
-
-HashResult hashPath(HashType ht, const Path& path, PathFilter& filter) {
-  HashSink sink(ht);
-  dumpPath(path, sink, filter);
-  return sink.finish();
-}
-
-Hash compressHash(const Hash& hash, unsigned int newSize) {
-  Hash h;
-  h.hashSize = newSize;
-  for (unsigned int i = 0; i < hash.hashSize; ++i) {
-    h.hash[i % newSize] ^= hash.hash[i];
-  }
-  return h;
-}
-
-HashType parseHashType(const std::string& s) {
-  if (s == "md5") {
-    return htMD5;
-  }
-  if (s == "sha1") {
-    return htSHA1;
-  } else if (s == "sha256") {
-    return htSHA256;
-  } else if (s == "sha512") {
-    return htSHA512;
-  } else {
-    return htUnknown;
-  }
-}
-
-std::string printHashType(HashType ht) {
-  if (ht == htMD5) {
-    return "md5";
-  }
-  if (ht == htSHA1) {
-    return "sha1";
-  } else if (ht == htSHA256) {
-    return "sha256";
-  } else if (ht == htSHA512) {
-    return "sha512";
-  } else if (ht == htUnknown) {
-    return "<unknown>";
-  } else {
-    LOG(FATAL) << "Unrecognized hash type: " << static_cast<int>(ht);
-    abort();
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/hash.hh b/third_party/nix/src/libutil/hash.hh
deleted file mode 100644
index 8b52ac657e..0000000000
--- a/third_party/nix/src/libutil/hash.hh
+++ /dev/null
@@ -1,147 +0,0 @@
-#pragma once
-
-#include <absl/status/statusor.h>
-
-#include "libproto/worker.grpc.pb.h"
-#include "libutil/serialise.hh"
-#include "libutil/types.hh"
-
-namespace nix {
-
-// Size of the hashes rendered in store paths, in bytes
-constexpr unsigned int kStorePathHashSize = 20;
-
-MakeError(BadHash, Error);
-
-// TODO(grfn): Replace this with the hash type enum from the daemon proto so we
-// don't have to juggle two different types
-enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 };
-
-std::optional<HashType> hash_type_from(nix::proto::HashType hash_type);
-
-nix::proto::HashType HashTypeToProto(HashType hash_type);
-
-const int md5HashSize = 16;
-const int sha1HashSize = 20;
-const int sha256HashSize = 32;
-const int sha512HashSize = 64;
-
-// omitted: E O U T
-constexpr char base32Chars[] = "0123456789abcdfghijklmnpqrsvwxyz";
-
-enum Base : int { Base64, Base32, Base16, SRI };
-
-struct Hash {
-  static const unsigned int maxHashSize = 64;
-  unsigned int hashSize = 0;
-  unsigned char hash[maxHashSize] = {};
-
-  HashType type = htUnknown;
-
-  /* Create an unset hash object. */
-  Hash(){};
-
-  /* Create a zero-filled hash object. */
-  explicit Hash(HashType type) : type(type) { init(); };
-
-  /* Initialize the hash from a string representation, in the format
-     "[<type>:]<base16|base32|base64>" or "<type>-<base64>" (a
-     Subresource Integrity hash expression). If the 'type' argument
-     is htUnknown, then the hash type must be specified in the
-     string. */
-  explicit Hash(std::string_view s, HashType type = htUnknown);
-
-  /* Status-returning version of above constructor */
-  static absl::StatusOr<Hash> deserialize(std::string_view s,
-                                          HashType type = htUnknown);
-
-  // Legacy unwrapper for StatusOr. Throws BadHash.
-  static Hash unwrap_throw(absl::StatusOr<Hash> hash) noexcept(false);
-
-  void init();
-
-  /* Check whether a hash is set. */
-  explicit operator bool() const { return type != htUnknown; }
-
-  /* Check whether two hash are equal. */
-  bool operator==(const Hash& h2) const;
-
-  /* Check whether two hash are not equal. */
-  bool operator!=(const Hash& h2) const;
-
-  /* For sorting. */
-  bool operator<(const Hash& h) const;
-
-  /* Returns the length of a base-16 representation of this hash. */
-  size_t base16Len() const { return hashSize * 2; }
-
-  /* Returns the length of a base-32 representation of this hash. */
-  size_t base32Len() const { return (hashSize * 8 - 1) / 5 + 1; }
-
-  /* Returns the length of a base-64 representation of this hash. */
-  size_t base64Len() const { return ((4 * hashSize / 3) + 3) & ~3; }
-
-  /* Return a string representation of the hash, in base-16, base-32
-     or base-64. By default, this is prefixed by the hash type
-     (e.g. "sha256:"). */
-  std::string to_string(Base base = Base32, bool includeType = true) const;
-
-  /* Returns whether the passed string contains entirely valid base16
-     characters. */
-  static bool IsValidBase16(absl::string_view s);
-
-  /* Returns whether the passed string contains entirely valid base32
-     characters. */
-  static bool IsValidBase32(absl::string_view s);
-
-  // Convert this Hash to the format expected in store paths
-  [[nodiscard]] std::string ToStorePathHash() const;
-};
-
-/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
-std::string printHash16or32(const Hash& hash);
-
-/* Compute the hash of the given string. */
-Hash hashString(HashType ht, const std::string& s);
-
-/* Compute the hash of the given file. */
-Hash hashFile(HashType ht, const Path& path);
-
-/* A pair of the Hash, and the number of bytes consumed. */
-typedef std::pair<Hash, unsigned long long> HashResult;
-
-/* Compute the hash of the given path.  The hash is defined as
-   (essentially) hashString(ht, dumpPath(path)). */
-HashResult hashPath(HashType ht, const Path& path,
-                    PathFilter& filter = defaultPathFilter);
-
-/* Compress a hash to the specified number of bytes by cyclically
-   XORing bytes together. */
-Hash compressHash(const Hash& hash, unsigned int newSize);
-
-/* Parse a string representing a hash type. */
-HashType parseHashType(const std::string& s);
-
-/* And the reverse. */
-std::string printHashType(HashType ht);
-
-namespace hash {
-union Ctx;
-}
-
-class HashSink : public BufferedSink {
- private:
-  HashType ht;
-  std::unique_ptr<hash::Ctx> ctx;
-  unsigned long long bytes;
-
- public:
-  explicit HashSink(HashType ht);
-  HashSink(const HashSink& h);
-  ~HashSink();
-  void write(const unsigned char* data, size_t len);
-  HashResult finish();
-  HashResult currentHash();
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/istringstream_nocopy.hh b/third_party/nix/src/libutil/istringstream_nocopy.hh
deleted file mode 100644
index 31683d37c9..0000000000
--- a/third_party/nix/src/libutil/istringstream_nocopy.hh
+++ /dev/null
@@ -1,85 +0,0 @@
-/* This file provides a variant of std::istringstream that doesn't
-   copy its string argument. This is useful for large strings. The
-   caller must ensure that the string object is not destroyed while
-   it's referenced by this object. */
-
-#pragma once
-
-#include <iostream>
-#include <string>
-
-template <class CharT, class Traits = std::char_traits<CharT>,
-          class Allocator = std::allocator<CharT>>
-class basic_istringbuf_nocopy : public std::basic_streambuf<CharT, Traits> {
- public:
-  using string_type = std::basic_string<CharT, Traits, Allocator>;
-
-  using off_type = typename std::basic_streambuf<CharT, Traits>::off_type;
-
-  using pos_type = typename std::basic_streambuf<CharT, Traits>::pos_type;
-
-  using int_type = typename std::basic_streambuf<CharT, Traits>::int_type;
-
-  using traits_type = typename std::basic_streambuf<CharT, Traits>::traits_type;
-
- private:
-  const string_type& s;
-
-  off_type off;
-
- public:
-  explicit basic_istringbuf_nocopy(const string_type& s) : s{s}, off{0} {}
-
- private:
-  pos_type seekoff(off_type off, std::ios_base::seekdir dir,
-                   std::ios_base::openmode which) {
-    if (which & std::ios_base::in) {
-      this->off =
-          dir == std::ios_base::beg
-              ? off
-              : (dir == std::ios_base::end ? s.size() + off : this->off + off);
-    }
-    return pos_type(this->off);
-  }
-
-  pos_type seekpos(pos_type pos, std::ios_base::openmode which) {
-    return seekoff(pos, std::ios_base::beg, which);
-  }
-
-  std::streamsize showmanyc() { return s.size() - off; }
-
-  int_type underflow() {
-    if (typename string_type::size_type(off) == s.size()) {
-      return traits_type::eof();
-    }
-    return traits_type::to_int_type(s[off]);
-  }
-
-  int_type uflow() {
-    if (typename string_type::size_type(off) == s.size()) {
-      return traits_type::eof();
-    }
-    return traits_type::to_int_type(s[off++]);
-  }
-
-  int_type pbackfail(int_type ch) {
-    if (off == 0 || (ch != traits_type::eof() && ch != s[off - 1])) {
-      return traits_type::eof();
-    }
-
-    return traits_type::to_int_type(s[--off]);
-  }
-};
-
-template <class CharT, class Traits = std::char_traits<CharT>,
-          class Allocator = std::allocator<CharT>>
-class basic_istringstream_nocopy : public std::basic_iostream<CharT, Traits> {
-  using buf_type = basic_istringbuf_nocopy<CharT, Traits, Allocator>;
-  buf_type buf;
-
- public:
-  explicit basic_istringstream_nocopy(const typename buf_type::string_type& s)
-      : std::basic_iostream<CharT, Traits>(&buf), buf(s){};
-};
-
-using istringstream_nocopy = basic_istringstream_nocopy<char>;
diff --git a/third_party/nix/src/libutil/json.cc b/third_party/nix/src/libutil/json.cc
deleted file mode 100644
index 59ff74f579..0000000000
--- a/third_party/nix/src/libutil/json.cc
+++ /dev/null
@@ -1,198 +0,0 @@
-#include "libutil/json.hh"
-
-#include <cstring>
-#include <iomanip>
-
-namespace nix {
-
-void toJSON(std::ostream& str, const char* start, const char* end) {
-  str << '"';
-  for (auto i = start; i != end; i++) {
-    if (*i == '\"' || *i == '\\') {
-      str << '\\' << *i;
-    } else if (*i == '\n') {
-      str << "\\n";
-    } else if (*i == '\r') {
-      str << "\\r";
-    } else if (*i == '\t') {
-      str << "\\t";
-    } else if (*i >= 0 && *i < 32) {
-      str << "\\u" << std::setfill('0') << std::setw(4) << std::hex
-          << static_cast<uint16_t>(*i) << std::dec;
-    } else {
-      str << *i;
-    }
-  }
-  str << '"';
-}
-
-void toJSON(std::ostream& str, const char* s) {
-  if (s == nullptr) {
-    str << "null";
-  } else {
-    toJSON(str, s, s + strlen(s));
-  }
-}
-
-template <>
-void toJSON<int>(std::ostream& str, const int& n) {
-  str << n;
-}
-template <>
-void toJSON<unsigned int>(std::ostream& str, const unsigned int& n) {
-  str << n;
-}
-template <>
-void toJSON<long>(std::ostream& str, const long& n) {
-  str << n;
-}
-template <>
-void toJSON<unsigned long>(std::ostream& str, const unsigned long& n) {
-  str << n;
-}
-template <>
-void toJSON<long long>(std::ostream& str, const long long& n) {
-  str << n;
-}
-template <>
-void toJSON<unsigned long long>(std::ostream& str,
-                                const unsigned long long& n) {
-  str << n;
-}
-template <>
-void toJSON<float>(std::ostream& str, const float& n) {
-  str << n;
-}
-template <>
-void toJSON<double>(std::ostream& str, const double& n) {
-  str << n;
-}
-
-template <>
-void toJSON<std::string>(std::ostream& str, const std::string& s) {
-  toJSON(str, s.c_str(), s.c_str() + s.size());
-}
-
-template <>
-void toJSON<bool>(std::ostream& str, const bool& b) {
-  str << (b ? "true" : "false");
-}
-
-template <>
-void toJSON<std::nullptr_t>(std::ostream& str, const std::nullptr_t& b) {
-  str << "null";
-}
-
-JSONWriter::JSONWriter(std::ostream& str, bool indent)
-    : state(new JSONState(str, indent)) {
-  state->stack++;
-}
-
-JSONWriter::JSONWriter(JSONState* state) : state(state) { state->stack++; }
-
-JSONWriter::~JSONWriter() {
-  if (state != nullptr) {
-    assertActive();
-    state->stack--;
-    if (state->stack == 0) {
-      delete state;
-    }
-  }
-}
-
-void JSONWriter::comma() {
-  assertActive();
-  if (first) {
-    first = false;
-  } else {
-    state->str << ',';
-  }
-  if (state->indent) {
-    indent();
-  }
-}
-
-void JSONWriter::indent() {
-  state->str << '\n' << std::string(state->depth * 2, ' ');
-}
-
-void JSONList::open() {
-  state->depth++;
-  state->str << '[';
-}
-
-JSONList::~JSONList() {
-  state->depth--;
-  if (state->indent && !first) {
-    indent();
-  }
-  state->str << "]";
-}
-
-JSONList JSONList::list() {
-  comma();
-  return JSONList(state);
-}
-
-JSONObject JSONList::object() {
-  comma();
-  return JSONObject(state);
-}
-
-JSONPlaceholder JSONList::placeholder() {
-  comma();
-  return JSONPlaceholder(state);
-}
-
-void JSONObject::open() {
-  state->depth++;
-  state->str << '{';
-}
-
-JSONObject::~JSONObject() {
-  if (state != nullptr) {
-    state->depth--;
-    if (state->indent && !first) {
-      indent();
-    }
-    state->str << "}";
-  }
-}
-
-void JSONObject::attr(const std::string& s) {
-  comma();
-  toJSON(state->str, s);
-  state->str << ':';
-  if (state->indent) {
-    state->str << ' ';
-  }
-}
-
-JSONList JSONObject::list(const std::string& name) {
-  attr(name);
-  return JSONList(state);
-}
-
-JSONObject JSONObject::object(const std::string& name) {
-  attr(name);
-  return JSONObject(state);
-}
-
-JSONPlaceholder JSONObject::placeholder(const std::string& name) {
-  attr(name);
-  return JSONPlaceholder(state);
-}
-
-JSONList JSONPlaceholder::list() {
-  assertValid();
-  first = false;
-  return JSONList(state);
-}
-
-JSONObject JSONPlaceholder::object() {
-  assertValid();
-  first = false;
-  return JSONObject(state);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/json.hh b/third_party/nix/src/libutil/json.hh
deleted file mode 100644
index 14d61d8a57..0000000000
--- a/third_party/nix/src/libutil/json.hh
+++ /dev/null
@@ -1,144 +0,0 @@
-#pragma once
-
-#include <cassert>
-#include <iostream>
-#include <vector>
-
-namespace nix {
-
-void toJSON(std::ostream& str, const char* start, const char* end);
-void toJSON(std::ostream& str, const char* s);
-
-template <typename T>
-void toJSON(std::ostream& str, const T& n);
-
-class JSONWriter {
- protected:
-  struct JSONState {
-    std::ostream& str;
-    bool indent;
-    size_t depth = 0;
-    size_t stack = 0;
-    JSONState(std::ostream& str, bool indent) : str(str), indent(indent) {}
-    ~JSONState() { assert(stack == 0); }
-  };
-
-  JSONState* state;
-
-  bool first = true;
-
-  JSONWriter(std::ostream& str, bool indent);
-
-  explicit JSONWriter(JSONState* state);
-
-  ~JSONWriter();
-
-  void assertActive() { assert(state->stack != 0); }
-
-  void comma();
-
-  void indent();
-};
-
-class JSONObject;
-class JSONPlaceholder;
-
-class JSONList : JSONWriter {
- private:
-  friend class JSONObject;
-  friend class JSONPlaceholder;
-
-  void open();
-
-  explicit JSONList(JSONState* state) : JSONWriter(state) { open(); }
-
- public:
-  explicit JSONList(std::ostream& str, bool indent = false)
-      : JSONWriter(str, indent) {
-    open();
-  }
-
-  ~JSONList();
-
-  template <typename T>
-  JSONList& elem(const T& v) {
-    comma();
-    toJSON(state->str, v);
-    return *this;
-  }
-
-  JSONList list();
-
-  JSONObject object();
-
-  JSONPlaceholder placeholder();
-};
-
-class JSONObject : JSONWriter {
- private:
-  friend class JSONList;
-  friend class JSONPlaceholder;
-
-  void open();
-
-  explicit JSONObject(JSONState* state) : JSONWriter(state) { open(); }
-
-  void attr(const std::string& s);
-
- public:
-  explicit JSONObject(std::ostream& str, bool indent = false)
-      : JSONWriter(str, indent) {
-    open();
-  }
-
-  JSONObject(const JSONObject& obj) = delete;
-
-  JSONObject(JSONObject&& obj) : JSONWriter(obj.state) { obj.state = 0; }
-
-  ~JSONObject();
-
-  template <typename T>
-  JSONObject& attr(const std::string& name, const T& v) {
-    attr(name);
-    toJSON(state->str, v);
-    return *this;
-  }
-
-  JSONList list(const std::string& name);
-
-  JSONObject object(const std::string& name);
-
-  JSONPlaceholder placeholder(const std::string& name);
-};
-
-class JSONPlaceholder : JSONWriter {
- private:
-  friend class JSONList;
-  friend class JSONObject;
-
-  explicit JSONPlaceholder(JSONState* state) : JSONWriter(state) {}
-
-  void assertValid() {
-    assertActive();
-    assert(first);
-  }
-
- public:
-  explicit JSONPlaceholder(std::ostream& str, bool indent = false)
-      : JSONWriter(str, indent) {}
-
-  ~JSONPlaceholder() { assert(!first || std::uncaught_exception()); }
-
-  template <typename T>
-  void write(const T& v) {
-    assertValid();
-    first = false;
-    toJSON(state->str, v);
-  }
-
-  JSONList list();
-
-  JSONObject object();
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/lazy.hh b/third_party/nix/src/libutil/lazy.hh
deleted file mode 100644
index 5c6ff5d8df..0000000000
--- a/third_party/nix/src/libutil/lazy.hh
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <exception>
-#include <functional>
-#include <mutex>
-
-namespace nix {
-
-/* A helper class for lazily-initialized variables.
-
-     Lazy<T> var([]() { return value; });
-
-   declares a variable of type T that is initialized to 'value' (in a
-   thread-safe way) on first use, that is, when var() is first
-   called. If the initialiser code throws an exception, then all
-   subsequent calls to var() will rethrow that exception. */
-template <typename T>
-class Lazy {
-  typedef std::function<T()> Init;
-
-  Init init;
-
-  std::once_flag done;
-
-  T value;
-
-  std::exception_ptr ex;
-
- public:
-  explicit Lazy(Init init) : init(init) {}
-
-  const T& operator()() {
-    std::call_once(done, [&]() {
-      try {
-        value = init();
-      } catch (...) {
-        ex = std::current_exception();
-      }
-    });
-    if (ex) {
-      std::rethrow_exception(ex);
-    }
-    return value;
-  }
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/lru-cache.hh b/third_party/nix/src/libutil/lru-cache.hh
deleted file mode 100644
index 1832c54244..0000000000
--- a/third_party/nix/src/libutil/lru-cache.hh
+++ /dev/null
@@ -1,90 +0,0 @@
-#pragma once
-
-#include <list>
-#include <map>
-#include <optional>
-
-namespace nix {
-
-/* A simple least-recently used cache. Not thread-safe. */
-template <typename Key, typename Value>
-class LRUCache {
- private:
-  size_t capacity;
-
-  // Stupid wrapper to get around circular dependency between Data
-  // and LRU.
-  struct LRUIterator;
-
-  using Data = std::map<Key, std::pair<LRUIterator, Value>>;
-  using LRU = std::list<typename Data::iterator>;
-
-  struct LRUIterator {
-    typename LRU::iterator it;
-  };
-
-  Data data;
-  LRU lru;
-
- public:
-  explicit LRUCache(size_t capacity) : capacity(capacity) {}
-
-  /* Insert or upsert an item in the cache. */
-  void upsert(const Key& key, const Value& value) {
-    if (capacity == 0) {
-      return;
-    }
-
-    erase(key);
-
-    if (data.size() >= capacity) {
-      /* Retire the oldest item. */
-      auto oldest = lru.begin();
-      data.erase(*oldest);
-      lru.erase(oldest);
-    }
-
-    auto res = data.emplace(key, std::make_pair(LRUIterator(), value));
-    assert(res.second);
-    auto& i(res.first);
-
-    auto j = lru.insert(lru.end(), i);
-
-    i->second.first.it = j;
-  }
-
-  bool erase(const Key& key) {
-    auto i = data.find(key);
-    if (i == data.end()) {
-      return false;
-    }
-    lru.erase(i->second.first.it);
-    data.erase(i);
-    return true;
-  }
-
-  /* Look up an item in the cache. If it exists, it becomes the most
-     recently used item. */
-  std::optional<Value> get(const Key& key) {
-    auto i = data.find(key);
-    if (i == data.end()) {
-      return {};
-    }
-
-    /* Move this item to the back of the LRU list. */
-    lru.erase(i->second.first.it);
-    auto j = lru.insert(lru.end(), i);
-    i->second.first.it = j;
-
-    return i->second.second;
-  }
-
-  size_t size() { return data.size(); }
-
-  void clear() {
-    data.clear();
-    lru.clear();
-  }
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/monitor-fd.hh b/third_party/nix/src/libutil/monitor-fd.hh
deleted file mode 100644
index c818c58261..0000000000
--- a/third_party/nix/src/libutil/monitor-fd.hh
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#include <atomic>
-#include <cstdlib>
-#include <thread>
-
-#include <poll.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-namespace nix {
-
-class MonitorFdHup {
- private:
-  std::thread thread;
-
- public:
-  MonitorFdHup(int fd) {
-    thread = std::thread([fd]() {
-      while (true) {
-        /* Wait indefinitely until a POLLHUP occurs. */
-        struct pollfd fds[1];
-        fds[0].fd = fd;
-        /* This shouldn't be necessary, but macOS doesn't seem to
-           like a zeroed out events field.
-           See rdar://37537852.
-        */
-        fds[0].events = POLLHUP;
-        auto count = poll(fds, 1, -1);
-        if (count == -1) {
-          abort();
-        }  // can't happen
-        /* This shouldn't happen, but can on macOS due to a bug.
-           See rdar://37550628.
-
-           This may eventually need a delay or further
-           coordination with the main thread if spinning proves
-           too harmful.
-         */
-        if (count == 0) {
-          continue;
-        }
-        assert(fds[0].revents & POLLHUP);
-        triggerInterrupt();
-        break;
-      }
-    });
-  };
-
-  ~MonitorFdHup() {
-    pthread_cancel(thread.native_handle());
-    thread.join();
-  }
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/pool.hh b/third_party/nix/src/libutil/pool.hh
deleted file mode 100644
index b5c3c4b5c4..0000000000
--- a/third_party/nix/src/libutil/pool.hh
+++ /dev/null
@@ -1,176 +0,0 @@
-#pragma once
-
-#include <cassert>
-#include <functional>
-#include <limits>
-#include <list>
-#include <memory>
-
-#include "libutil/ref.hh"
-#include "libutil/sync.hh"
-
-namespace nix {
-
-/* This template class implements a simple pool manager of resources
-   of some type R, such as database connections. It is used as
-   follows:
-
-     class Connection { ... };
-
-     Pool<Connection> pool;
-
-     {
-       auto conn(pool.get());
-       conn->exec("select ...");
-     }
-
-   Here, the Connection object referenced by β€˜conn’ is automatically
-   returned to the pool when β€˜conn’ goes out of scope.
-*/
-
-template <class R>
-class Pool {
- public:
-  /* A function that produces new instances of R on demand. */
-  typedef std::function<ref<R>()> Factory;
-
-  /* A function that checks whether an instance of R is still
-     usable. Unusable instances are removed from the pool. */
-  using Validator = std::function<bool(const ref<R>&)>;
-
- private:
-  Factory factory;
-  Validator validator;
-
-  struct State {
-    size_t inUse = 0;
-    size_t max;
-    std::vector<ref<R>> idle;
-  };
-
-  Sync<State> state;
-
-  std::condition_variable wakeup;
-
- public:
-  explicit Pool(
-      size_t max = std::numeric_limits<size_t>::max(),
-      const Factory& factory = []() { return make_ref<R>(); },
-      const Validator& validator = [](ref<R> r) { return true; })
-      : factory(factory), validator(validator) {
-    auto state_(state.lock());
-    state_->max = max;
-  }
-
-  void incCapacity() {
-    auto state_(state.lock());
-    state_->max++;
-    /* we could wakeup here, but this is only used when we're
-     * about to nest Pool usages, and we want to save the slot for
-     * the nested use if we can
-     */
-  }
-
-  void decCapacity() {
-    auto state_(state.lock());
-    state_->max--;
-  }
-
-  ~Pool() {
-    auto state_(state.lock());
-    assert(!state_->inUse);
-    state_->max = 0;
-    state_->idle.clear();
-  }
-
-  class Handle {
-   private:
-    Pool& pool;
-    std::shared_ptr<R> r;
-    bool bad = false;
-
-    friend Pool;
-
-    Handle(Pool& pool, std::shared_ptr<R> r) : pool(pool), r(r) {}
-
-   public:
-    Handle(Handle&& h) : pool(h.pool), r(h.r) { h.r.reset(); }
-
-    Handle(const Handle& l) = delete;
-
-    ~Handle() {
-      if (!r) {
-        return;
-      }
-      {
-        auto state_(pool.state.lock());
-        if (!bad) {
-          state_->idle.push_back(ref<R>(r));
-        }
-        assert(state_->inUse);
-        state_->inUse--;
-      }
-      pool.wakeup.notify_one();
-    }
-
-    R* operator->() { return &*r; }
-    R& operator*() { return *r; }
-
-    void markBad() { bad = true; }
-  };
-
-  Handle get() {
-    {
-      auto state_(state.lock());
-
-      /* If we're over the maximum number of instance, we need
-         to wait until a slot becomes available. */
-      while (state_->idle.empty() && state_->inUse >= state_->max) {
-        state_.wait(wakeup);
-      }
-
-      while (!state_->idle.empty()) {
-        auto p = state_->idle.back();
-        state_->idle.pop_back();
-        if (validator(p)) {
-          state_->inUse++;
-          return Handle(*this, p);
-        }
-      }
-
-      state_->inUse++;
-    }
-
-    /* We need to create a new instance. Because that might take a
-       while, we don't hold the lock in the meantime. */
-    try {
-      Handle h(*this, factory());
-      return h;
-    } catch (...) {
-      auto state_(state.lock());
-      state_->inUse--;
-      wakeup.notify_one();
-      throw;
-    }
-  }
-
-  size_t count() {
-    auto state_(state.lock());
-    return state_->idle.size() + state_->inUse;
-  }
-
-  size_t capacity() { return state.lock()->max; }
-
-  void flushBad() {
-    auto state_(state.lock());
-    std::vector<ref<R>> left;
-    for (auto& p : state_->idle) {
-      if (validator(p)) {
-        left.push_back(p);
-      }
-    }
-    std::swap(state_->idle, left);
-  }
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/proto.hh b/third_party/nix/src/libutil/proto.hh
deleted file mode 100644
index 058cb7b7b4..0000000000
--- a/third_party/nix/src/libutil/proto.hh
+++ /dev/null
@@ -1,174 +0,0 @@
-#pragma once
-
-#include <absl/status/status.h>
-#include <grpcpp/impl/codegen/status.h>
-#include <grpcpp/impl/codegen/status_code_enum.h>
-
-#include "libproto/worker.pb.h"
-#include "libutil/types.hh"
-
-namespace nix::util::proto {
-
-inline ::nix::proto::StorePath StorePath(const Path& path) {
-  ::nix::proto::StorePath store_path;
-  store_path.set_path(path);
-  return store_path;
-}
-
-inline ::nix::proto::StorePaths StorePaths(const PathSet& paths) {
-  ::nix::proto::StorePaths result;
-  for (const auto& path : paths) {
-    result.add_paths(path);
-  }
-  return result;
-}
-
-template <typename T, typename U>
-T FillFrom(const U& src) {
-  T result;
-  result.insert(src.begin(), src.end());
-  return result;
-}
-
-constexpr absl::StatusCode GRPCStatusCodeToAbsl(grpc::StatusCode code) {
-  switch (code) {
-    case grpc::StatusCode::OK:
-      return absl::StatusCode::kOk;
-    case grpc::StatusCode::CANCELLED:
-      return absl::StatusCode::kCancelled;
-    case grpc::StatusCode::UNKNOWN:
-      return absl::StatusCode::kUnknown;
-    case grpc::StatusCode::INVALID_ARGUMENT:
-      return absl::StatusCode::kInvalidArgument;
-    case grpc::StatusCode::DEADLINE_EXCEEDED:
-      return absl::StatusCode::kDeadlineExceeded;
-    case grpc::StatusCode::NOT_FOUND:
-      return absl::StatusCode::kNotFound;
-    case grpc::StatusCode::ALREADY_EXISTS:
-      return absl::StatusCode::kAlreadyExists;
-    case grpc::StatusCode::PERMISSION_DENIED:
-      return absl::StatusCode::kPermissionDenied;
-    case grpc::StatusCode::UNAUTHENTICATED:
-      return absl::StatusCode::kUnauthenticated;
-    case grpc::StatusCode::RESOURCE_EXHAUSTED:
-      return absl::StatusCode::kResourceExhausted;
-    case grpc::StatusCode::FAILED_PRECONDITION:
-      return absl::StatusCode::kFailedPrecondition;
-    case grpc::StatusCode::ABORTED:
-      return absl::StatusCode::kAborted;
-    case grpc::StatusCode::OUT_OF_RANGE:
-      return absl::StatusCode::kOutOfRange;
-    case grpc::StatusCode::UNIMPLEMENTED:
-      return absl::StatusCode::kUnimplemented;
-    case grpc::StatusCode::INTERNAL:
-      return absl::StatusCode::kInternal;
-    case grpc::StatusCode::UNAVAILABLE:
-      return absl::StatusCode::kUnavailable;
-    case grpc::StatusCode::DATA_LOSS:
-      return absl::StatusCode::kDataLoss;
-    default:
-      return absl::StatusCode::kInternal;
-  }
-}
-
-constexpr grpc::StatusCode AbslStatusCodeToGRPC(absl::StatusCode code) {
-  switch (code) {
-    case absl::StatusCode::kOk:
-      return grpc::StatusCode::OK;
-    case absl::StatusCode::kCancelled:
-      return grpc::StatusCode::CANCELLED;
-    case absl::StatusCode::kUnknown:
-      return grpc::StatusCode::UNKNOWN;
-    case absl::StatusCode::kInvalidArgument:
-      return grpc::StatusCode::INVALID_ARGUMENT;
-    case absl::StatusCode::kDeadlineExceeded:
-      return grpc::StatusCode::DEADLINE_EXCEEDED;
-    case absl::StatusCode::kNotFound:
-      return grpc::StatusCode::NOT_FOUND;
-    case absl::StatusCode::kAlreadyExists:
-      return grpc::StatusCode::ALREADY_EXISTS;
-    case absl::StatusCode::kPermissionDenied:
-      return grpc::StatusCode::PERMISSION_DENIED;
-    case absl::StatusCode::kUnauthenticated:
-      return grpc::StatusCode::UNAUTHENTICATED;
-    case absl::StatusCode::kResourceExhausted:
-      return grpc::StatusCode::RESOURCE_EXHAUSTED;
-    case absl::StatusCode::kFailedPrecondition:
-      return grpc::StatusCode::FAILED_PRECONDITION;
-    case absl::StatusCode::kAborted:
-      return grpc::StatusCode::ABORTED;
-    case absl::StatusCode::kOutOfRange:
-      return grpc::StatusCode::OUT_OF_RANGE;
-    case absl::StatusCode::kUnimplemented:
-      return grpc::StatusCode::UNIMPLEMENTED;
-    case absl::StatusCode::kInternal:
-      return grpc::StatusCode::INTERNAL;
-    case absl::StatusCode::kUnavailable:
-      return grpc::StatusCode::UNAVAILABLE;
-    case absl::StatusCode::kDataLoss:
-      return grpc::StatusCode::DATA_LOSS;
-    default:
-      return grpc::StatusCode::INTERNAL;
-  }
-}
-
-constexpr absl::string_view GRPCStatusCodeDescription(grpc::StatusCode code) {
-  switch (code) {
-    case grpc::StatusCode::OK:
-      return "OK";
-    case grpc::StatusCode::CANCELLED:
-      return "CANCELLED";
-    case grpc::StatusCode::UNKNOWN:
-      return "UNKNOWN";
-    case grpc::StatusCode::INVALID_ARGUMENT:
-      return "INVALID_ARGUMENT";
-    case grpc::StatusCode::DEADLINE_EXCEEDED:
-      return "DEADLINE_EXCEEDED";
-    case grpc::StatusCode::NOT_FOUND:
-      return "NOT_FOUND";
-    case grpc::StatusCode::ALREADY_EXISTS:
-      return "ALREADY_EXISTS";
-    case grpc::StatusCode::PERMISSION_DENIED:
-      return "PERMISSION_DENIED";
-    case grpc::StatusCode::UNAUTHENTICATED:
-      return "UNAUTHENTICATED";
-    case grpc::StatusCode::RESOURCE_EXHAUSTED:
-      return "RESOURCE_EXHAUSTED";
-    case grpc::StatusCode::FAILED_PRECONDITION:
-      return "FAILED_PRECONDITION";
-    case grpc::StatusCode::ABORTED:
-      return "ABORTED";
-    case grpc::StatusCode::OUT_OF_RANGE:
-      return "OUT_OF_RANGE";
-    case grpc::StatusCode::UNIMPLEMENTED:
-      return "UNIMPLEMENTED";
-    case grpc::StatusCode::INTERNAL:
-      return "INTERNAL";
-    case grpc::StatusCode::UNAVAILABLE:
-      return "UNAVAILABLE";
-    case grpc::StatusCode::DATA_LOSS:
-      return "DATA_LOSS";
-    default:
-      return "<BAD ERROR CODE>";
-  };
-}
-
-inline absl::Status GRPCStatusToAbsl(grpc::Status status) {
-  if (status.ok()) {
-    return absl::OkStatus();
-  }
-
-  return absl::Status(GRPCStatusCodeToAbsl(status.error_code()),
-                      status.error_message());
-}
-
-inline grpc::Status AbslToGRPCStatus(absl::Status status) {
-  if (status.ok()) {
-    return grpc::Status::OK;
-  }
-
-  return grpc::Status(AbslStatusCodeToGRPC(status.code()),
-                      std::string(status.message()));
-}
-
-}  // namespace nix::util::proto
diff --git a/third_party/nix/src/libutil/ref.hh b/third_party/nix/src/libutil/ref.hh
deleted file mode 100644
index 3c375491fd..0000000000
--- a/third_party/nix/src/libutil/ref.hh
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-
-#include <exception>
-#include <memory>
-#include <stdexcept>
-
-namespace nix {
-
-/* A simple non-nullable reference-counted pointer. Actually a wrapper
-   around std::shared_ptr that prevents non-null constructions. */
-template <typename T>
-class ref {  // TODO(tazjin): rename to brainworm_ref or something
- private:
-  std::shared_ptr<T> p;
-
- public:
-  ref<T>(const ref<T>& r) : p(r.p) {}
-
-  explicit ref<T>(const std::shared_ptr<T>& p) : p(p) {
-    if (!p) {
-      throw std::invalid_argument("null pointer cast to ref");
-    }
-  }
-
-  explicit ref<T>(T* p) : p(p) {
-    if (!p) {
-      throw std::invalid_argument("null pointer cast to ref");
-    }
-  }
-
-  T* operator->() const { return &*p; }
-
-  T& operator*() const { return *p; }
-
-  operator std::shared_ptr<T>() const { return p; }
-
-  std::shared_ptr<T> get_ptr() const { return p; }
-
-  template <typename T2>
-  ref<T2> cast() const {
-    return ref<T2>(std::dynamic_pointer_cast<T2>(p));
-  }
-
-  template <typename T2>
-  std::shared_ptr<T2> dynamic_pointer_cast() const {
-    return std::dynamic_pointer_cast<T2>(p);
-  }
-
-  template <typename T2>
-  operator ref<T2>() const {
-    return ref<T2>((std::shared_ptr<T2>)p);
-  }
-
- private:
-  template <typename T2, typename... Args>
-  friend ref<T2> make_ref(Args&&... args);
-};
-
-template <typename T, typename... Args>
-inline ref<T> make_ref(Args&&... args) {
-  auto p = std::make_shared<T>(std::forward<Args>(args)...);
-  return ref<T>(p);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/serialise.cc b/third_party/nix/src/libutil/serialise.cc
deleted file mode 100644
index 288255089b..0000000000
--- a/third_party/nix/src/libutil/serialise.cc
+++ /dev/null
@@ -1,311 +0,0 @@
-#include "libutil/serialise.hh"
-
-#include <boost/coroutine2/coroutine.hpp>
-#include <cerrno>
-#include <cstring>
-#include <memory>
-#include <utility>
-
-#include <glog/logging.h>
-
-#include "libutil/util.hh"
-
-namespace nix {
-
-void BufferedSink::operator()(const unsigned char* data, size_t len) {
-  if (!buffer) {
-    buffer = decltype(buffer)(new unsigned char[bufSize]);
-  }
-
-  while (len != 0u) {
-    /* Optimisation: bypass the buffer if the data exceeds the
-       buffer size. */
-    if (bufPos + len >= bufSize) {
-      flush();
-      write(data, len);
-      break;
-    }
-    /* Otherwise, copy the bytes to the buffer.  Flush the buffer
-       when it's full. */
-    size_t n = bufPos + len > bufSize ? bufSize - bufPos : len;
-    memcpy(buffer.get() + bufPos, data, n);
-    data += n;
-    bufPos += n;
-    len -= n;
-    if (bufPos == bufSize) {
-      flush();
-    }
-  }
-}
-
-void BufferedSink::flush() {
-  if (bufPos == 0) {
-    return;
-  }
-  size_t n = bufPos;
-  bufPos = 0;  // don't trigger the assert() in ~BufferedSink()
-  write(buffer.get(), n);
-}
-
-FdSink::~FdSink() {
-  try {
-    flush();
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-size_t threshold = 256 * 1024 * 1024;
-
-static void warnLargeDump() {
-  LOG(WARNING)
-      << "dumping very large path (> 256 MiB); this may run out of memory";
-}
-
-void FdSink::write(const unsigned char* data, size_t len) {
-  written += len;
-  static bool warned = false;
-  if (warn && !warned) {
-    if (written > threshold) {
-      warnLargeDump();
-      warned = true;
-    }
-  }
-  try {
-    writeFull(fd, data, len);
-  } catch (SysError& e) {
-    _good = false;
-    throw;
-  }
-}
-
-bool FdSink::good() { return _good; }
-
-void Source::operator()(unsigned char* data, size_t len) {
-  while (len != 0u) {
-    size_t n = read(data, len);
-    data += n;
-    len -= n;
-  }
-}
-
-std::string Source::drain() {
-  std::string s;
-  std::vector<unsigned char> buf(8192);
-  while (true) {
-    size_t n;
-    try {
-      n = read(buf.data(), buf.size());
-      s.append(reinterpret_cast<char*>(buf.data()), n);
-    } catch (EndOfFile&) {
-      break;
-    }
-  }
-  return s;
-}
-
-size_t BufferedSource::read(unsigned char* data, size_t len) {
-  if (!buffer) {
-    buffer = decltype(buffer)(new unsigned char[bufSize]);
-  }
-
-  if (bufPosIn == 0u) {
-    bufPosIn = readUnbuffered(buffer.get(), bufSize);
-  }
-
-  /* Copy out the data in the buffer. */
-  size_t n = len > bufPosIn - bufPosOut ? bufPosIn - bufPosOut : len;
-  memcpy(data, buffer.get() + bufPosOut, n);
-  bufPosOut += n;
-  if (bufPosIn == bufPosOut) {
-    bufPosIn = bufPosOut = 0;
-  }
-  return n;
-}
-
-bool BufferedSource::hasData() { return bufPosOut < bufPosIn; }
-
-size_t FdSource::readUnbuffered(unsigned char* data, size_t len) {
-  ssize_t n;
-  do {
-    checkInterrupt();
-    n = ::read(fd, reinterpret_cast<char*>(data), len);
-  } while (n == -1 && errno == EINTR);
-  if (n == -1) {
-    _good = false;
-    throw SysError("reading from file");
-  }
-  if (n == 0) {
-    _good = false;
-    throw EndOfFile("unexpected end-of-file");
-  }
-  read += n;
-  return n;
-}
-
-bool FdSource::good() { return _good; }
-
-size_t StringSource::read(unsigned char* data, size_t len) {
-  if (pos == s.size()) {
-    throw EndOfFile("end of string reached");
-  }
-  size_t n = s.copy(reinterpret_cast<char*>(data), len, pos);
-  pos += n;
-  return n;
-}
-
-#if BOOST_VERSION >= 106300 && BOOST_VERSION < 106600
-#error Coroutines are broken in this version of Boost!
-#endif
-
-std::unique_ptr<Source> sinkToSource(const std::function<void(Sink&)>& fun,
-                                     const std::function<void()>& eof) {
-  struct SinkToSource : Source {
-    using coro_t = boost::coroutines2::coroutine<std::string>;
-
-    std::function<void(Sink&)> fun;
-    std::function<void()> eof;
-    std::optional<coro_t::pull_type> coro;
-    bool started = false;
-
-    SinkToSource(std::function<void(Sink&)> fun, std::function<void()> eof)
-        : fun(std::move(fun)), eof(std::move(eof)) {}
-
-    std::string cur;
-    size_t pos = 0;
-
-    size_t read(unsigned char* data, size_t len) override {
-      if (!coro) {
-        coro = coro_t::pull_type([&](coro_t::push_type& yield) {
-          LambdaSink sink([&](const unsigned char* data, size_t len) {
-            if (len != 0u) {
-              yield(std::string(reinterpret_cast<const char*>(data), len));
-            }
-          });
-          fun(sink);
-        });
-      }
-
-      if (!*coro) {
-        eof();
-        abort();
-      }
-
-      if (pos == cur.size()) {
-        if (!cur.empty()) {
-          (*coro)();
-        }
-        cur = coro->get();
-        pos = 0;
-      }
-
-      auto n = std::min(cur.size() - pos, len);
-      memcpy(data, reinterpret_cast<unsigned char*>(cur.data()) + pos, n);
-      pos += n;
-
-      return n;
-    }
-  };
-
-  return std::make_unique<SinkToSource>(fun, eof);
-}
-
-void writePadding(size_t len, Sink& sink) {
-  if ((len % 8) != 0u) {
-    unsigned char zero[8];
-    memset(zero, 0, sizeof(zero));
-    sink(zero, 8 - (len % 8));
-  }
-}
-
-void writeString(const unsigned char* buf, size_t len, Sink& sink) {
-  sink << len;
-  sink(buf, len);
-  writePadding(len, sink);
-}
-
-Sink& operator<<(Sink& sink, const std::string& s) {
-  writeString(reinterpret_cast<const unsigned char*>(s.data()), s.size(), sink);
-  return sink;
-}
-
-template <class T>
-void writeStrings(const T& ss, Sink& sink) {
-  sink << ss.size();
-  for (auto& i : ss) {
-    sink << i;
-  }
-}
-
-Sink& operator<<(Sink& sink, const Strings& s) {
-  writeStrings(s, sink);
-  return sink;
-}
-
-Sink& operator<<(Sink& sink, const StringSet& s) {
-  writeStrings(s, sink);
-  return sink;
-}
-
-void readPadding(size_t len, Source& source) {
-  if ((len % 8) != 0u) {
-    unsigned char zero[8];
-    size_t n = 8 - (len % 8);
-    source(zero, n);
-    for (unsigned int i = 0; i < n; i++) {
-      if (zero[i] != 0u) {
-        throw SerialisationError("non-zero padding");
-      }
-    }
-  }
-}
-
-size_t readString(unsigned char* buf, size_t max, Source& source) {
-  auto len = readNum<size_t>(source);
-  if (len > max) {
-    throw SerialisationError("string is too long");
-  }
-  source(buf, len);
-  readPadding(len, source);
-  return len;
-}
-
-std::string readString(Source& source, size_t max) {
-  auto len = readNum<size_t>(source);
-  if (len > max) {
-    throw SerialisationError("string is too long");
-  }
-  std::string res(len, 0);
-  source(reinterpret_cast<unsigned char*>(res.data()), len);
-  readPadding(len, source);
-  return res;
-}
-
-Source& operator>>(Source& in, std::string& s) {
-  s = readString(in);
-  return in;
-}
-
-template <class T>
-T readStrings(Source& source) {
-  auto count = readNum<size_t>(source);
-  T ss;
-  while (count--) {
-    ss.insert(ss.end(), readString(source));
-  }
-  return ss;
-}
-
-template Paths readStrings(Source& source);
-template PathSet readStrings(Source& source);
-
-void StringSink::operator()(const unsigned char* data, size_t len) {
-  static bool warned = false;
-  if (!warned && s->size() > threshold) {
-    warnLargeDump();
-    warned = true;
-  }
-  s->append(reinterpret_cast<const char*>(data), len);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/serialise.hh b/third_party/nix/src/libutil/serialise.hh
deleted file mode 100644
index c6d1d814db..0000000000
--- a/third_party/nix/src/libutil/serialise.hh
+++ /dev/null
@@ -1,289 +0,0 @@
-#pragma once
-
-#include <memory>
-
-#include "libutil/types.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-/* Abstract destination of binary data. */
-struct Sink {
-  virtual ~Sink() {}
-  virtual void operator()(const unsigned char* data, size_t len) = 0;
-  virtual bool good() { return true; }
-
-  void operator()(const std::string& s) {
-    (*this)((const unsigned char*)s.data(), s.size());
-  }
-};
-
-/* A buffered abstract sink. */
-struct BufferedSink : Sink {
-  size_t bufSize, bufPos;
-  std::unique_ptr<unsigned char[]> buffer;
-
-  explicit BufferedSink(size_t bufSize = 32 * 1024)
-      : bufSize(bufSize), bufPos(0), buffer(nullptr) {}
-
-  void operator()(const unsigned char* data, size_t len) override;
-
-  void operator()(const std::string& s) { Sink::operator()(s); }
-
-  void flush();
-
-  virtual void write(const unsigned char* data, size_t len) = 0;
-};
-
-/* Abstract source of binary data. */
-struct Source {
-  virtual ~Source() {}
-
-  /* Store exactly β€˜len’ bytes in the buffer pointed to by β€˜data’.
-     It blocks until all the requested data is available, or throws
-     an error if it is not going to be available.   */
-  void operator()(unsigned char* data, size_t len);
-
-  /* Store up to β€˜len’ in the buffer pointed to by β€˜data’, and
-     return the number of bytes stored.  It blocks until at least
-     one byte is available. */
-  virtual size_t read(unsigned char* data, size_t len) = 0;
-
-  virtual bool good() { return true; }
-
-  std::string drain();
-};
-
-/* A buffered abstract source. */
-struct BufferedSource : Source {
-  size_t bufSize, bufPosIn, bufPosOut;
-  std::unique_ptr<unsigned char[]> buffer;
-
-  explicit BufferedSource(size_t bufSize = 32 * 1024)
-      : bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(nullptr) {}
-
-  size_t read(unsigned char* data, size_t len) override;
-
-  bool hasData();
-
- protected:
-  /* Underlying read call, to be overridden. */
-  virtual size_t readUnbuffered(unsigned char* data, size_t len) = 0;
-};
-
-/* A sink that writes data to a file descriptor. */
-struct FdSink : BufferedSink {
-  int fd;
-  bool warn = false;
-  size_t written = 0;
-
-  FdSink() : fd(-1) {}
-  explicit FdSink(int fd) : fd(fd) {}
-  FdSink(FdSink&&) = default;
-
-  FdSink& operator=(FdSink&& s) {
-    flush();
-    fd = s.fd;
-    s.fd = -1;
-    warn = s.warn;
-    written = s.written;
-    return *this;
-  }
-
-  ~FdSink();
-
-  void write(const unsigned char* data, size_t len) override;
-
-  bool good() override;
-
- private:
-  bool _good = true;
-};
-
-/* A source that reads data from a file descriptor. */
-struct FdSource : BufferedSource {
-  int fd;
-  size_t read = 0;
-
-  FdSource() : fd(-1) {}
-  explicit FdSource(int fd) : fd(fd) {}
-  FdSource(FdSource&&) = default;
-
-  FdSource& operator=(FdSource&& s) {
-    fd = s.fd;
-    s.fd = -1;
-    read = s.read;
-    return *this;
-  }
-
-  bool good() override;
-
- protected:
-  size_t readUnbuffered(unsigned char* data, size_t len) override;
-
- private:
-  bool _good = true;
-};
-
-/* A sink that writes data to a string. */
-struct StringSink : Sink {
-  ref<std::string> s;
-  StringSink() : s(make_ref<std::string>()){};
-  explicit StringSink(ref<std::string> s) : s(s){};
-  void operator()(const unsigned char* data, size_t len) override;
-};
-
-/* A source that reads data from a string. */
-struct StringSource : Source {
-  const std::string& s;
-  size_t pos;
-  explicit StringSource(const std::string& _s) : s(_s), pos(0) {}
-  size_t read(unsigned char* data, size_t len) override;
-};
-
-/* Adapter class of a Source that saves all data read to `s'. */
-struct TeeSource : Source {
-  Source& orig;
-  ref<std::string> data;
-  explicit TeeSource(Source& orig)
-      : orig(orig), data(make_ref<std::string>()) {}
-  size_t read(unsigned char* data, size_t len) {
-    size_t n = orig.read(data, len);
-    this->data->append((const char*)data, n);
-    return n;
-  }
-};
-
-/* A reader that consumes the original Source until 'size'. */
-struct SizedSource : Source {
-  Source& orig;
-  size_t remain;
-  SizedSource(Source& orig, size_t size) : orig(orig), remain(size) {}
-  size_t read(unsigned char* data, size_t len) {
-    if (this->remain <= 0) {
-      throw EndOfFile("sized: unexpected end-of-file");
-    }
-    len = std::min(len, this->remain);
-    size_t n = this->orig.read(data, len);
-    this->remain -= n;
-    return n;
-  }
-
-  /* Consume the original source until no remain data is left to consume. */
-  size_t drainAll() {
-    std::vector<unsigned char> buf(8192);
-    size_t sum = 0;
-    while (this->remain > 0) {
-      size_t n = read(buf.data(), buf.size());
-      sum += n;
-    }
-    return sum;
-  }
-};
-
-/* Convert a function into a sink. */
-struct LambdaSink : Sink {
-  typedef std::function<void(const unsigned char*, size_t)> lambda_t;
-
-  lambda_t lambda;
-
-  explicit LambdaSink(const lambda_t& lambda) : lambda(lambda) {}
-
-  virtual void operator()(const unsigned char* data, size_t len) {
-    lambda(data, len);
-  }
-};
-
-/* Convert a function into a source. */
-struct LambdaSource : Source {
-  using lambda_t = std::function<size_t(unsigned char*, size_t)>;
-
-  lambda_t lambda;
-
-  explicit LambdaSource(const lambda_t& lambda) : lambda(lambda) {}
-
-  size_t read(unsigned char* data, size_t len) override {
-    return lambda(data, len);
-  }
-};
-
-/* Convert a function that feeds data into a Sink into a Source. The
-   Source executes the function as a coroutine. */
-std::unique_ptr<Source> sinkToSource(
-    const std::function<void(Sink&)>& fun,
-    const std::function<void()>& eof = []() {
-      throw EndOfFile("coroutine has finished");
-    });
-
-void writePadding(size_t len, Sink& sink);
-void writeString(const unsigned char* buf, size_t len, Sink& sink);
-
-inline Sink& operator<<(Sink& sink, uint64_t n) {
-  unsigned char buf[8];
-  buf[0] = n & 0xff;
-  buf[1] = (n >> 8) & 0xff;
-  buf[2] = (n >> 16) & 0xff;
-  buf[3] = (n >> 24) & 0xff;
-  buf[4] = (n >> 32) & 0xff;
-  buf[5] = (n >> 40) & 0xff;
-  buf[6] = (n >> 48) & 0xff;
-  buf[7] = (unsigned char)(n >> 56) & 0xff;
-  sink(buf, sizeof(buf));
-  return sink;
-}
-
-Sink& operator<<(Sink& sink, const std::string& s);
-Sink& operator<<(Sink& sink, const Strings& s);
-Sink& operator<<(Sink& sink, const StringSet& s);
-
-MakeError(SerialisationError, Error);
-
-template <typename T>
-T readNum(Source& source) {
-  unsigned char buf[8];
-  source(buf, sizeof(buf));
-
-  uint64_t n =
-      ((unsigned long long)buf[0]) | ((unsigned long long)buf[1] << 8) |
-      ((unsigned long long)buf[2] << 16) | ((unsigned long long)buf[3] << 24) |
-      ((unsigned long long)buf[4] << 32) | ((unsigned long long)buf[5] << 40) |
-      ((unsigned long long)buf[6] << 48) | ((unsigned long long)buf[7] << 56);
-
-  if (n > std::numeric_limits<T>::max()) {
-    throw SerialisationError("serialised integer %d is too large for type '%s'",
-                             n, typeid(T).name());
-  }
-
-  return (T)n;
-}
-
-inline unsigned int readInt(Source& source) {
-  return readNum<unsigned int>(source);
-}
-
-inline uint64_t readLongLong(Source& source) {
-  return readNum<uint64_t>(source);
-}
-
-void readPadding(size_t len, Source& source);
-size_t readString(unsigned char* buf, size_t max, Source& source);
-std::string readString(Source& source,
-                       size_t max = std::numeric_limits<size_t>::max());
-template <class T>
-T readStrings(Source& source);
-
-Source& operator>>(Source& in, std::string& s);
-
-template <typename T>
-Source& operator>>(Source& in, T& n) {
-  n = readNum<T>(in);
-  return in;
-}
-
-template <typename T>
-Source& operator>>(Source& in, bool& b) {
-  b = readNum<uint64_t>(in);
-  return in;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/status.hh b/third_party/nix/src/libutil/status.hh
deleted file mode 100644
index aeee0f5022..0000000000
--- a/third_party/nix/src/libutil/status.hh
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include <absl/status/status.h>
-#include <absl/strings/str_format.h>
-#include <absl/strings/string_view.h>
-
-#include "libutil/types.hh"
-
-namespace nix::util {
-
-inline void OkOrThrow(absl::Status status) {
-  if (!status.ok()) {
-    throw Error(absl::StrFormat("Operation failed: %s", status.ToString()));
-  }
-}
-
-}  // namespace nix::util
diff --git a/third_party/nix/src/libutil/sync.hh b/third_party/nix/src/libutil/sync.hh
deleted file mode 100644
index ef640d5b56..0000000000
--- a/third_party/nix/src/libutil/sync.hh
+++ /dev/null
@@ -1,84 +0,0 @@
-#pragma once
-
-#include <cassert>
-#include <condition_variable>
-#include <cstdlib>
-#include <mutex>
-
-namespace nix {
-
-/* This template class ensures synchronized access to a value of type
-   T. It is used as follows:
-
-     struct Data { int x; ... };
-
-     Sync<Data> data;
-
-     {
-       auto data_(data.lock());
-       data_->x = 123;
-     }
-
-   Here, "data" is automatically unlocked when "data_" goes out of
-   scope.
-*/
-
-template <class T, class M = std::mutex>
-class Sync {
- private:
-  M mutex;
-  T data;
-
- public:
-  Sync() {}
-  explicit Sync(const T& data) : data(data) {}
-  explicit Sync(T&& data) noexcept : data(std::move(data)) {}
-
-  class Lock {
-   private:
-    Sync* s;
-    std::unique_lock<M> lk;
-    friend Sync;
-    explicit Lock(Sync* s) : s(s), lk(s->mutex) {}
-
-   public:
-    Lock(Lock&& l) : s(l.s) { abort(); }
-    Lock(const Lock& l) = delete;
-    ~Lock() {}
-    T* operator->() { return &s->data; }
-    T& operator*() { return s->data; }
-
-    void wait(std::condition_variable& cv) {
-      assert(s);
-      cv.wait(lk);
-    }
-
-    template <class Rep, class Period>
-    std::cv_status wait_for(
-        std::condition_variable& cv,
-        const std::chrono::duration<Rep, Period>& duration) {
-      assert(s);
-      return cv.wait_for(lk, duration);
-    }
-
-    template <class Rep, class Period, class Predicate>
-    bool wait_for(std::condition_variable& cv,
-                  const std::chrono::duration<Rep, Period>& duration,
-                  Predicate pred) {
-      assert(s);
-      return cv.wait_for(lk, duration, pred);
-    }
-
-    template <class Clock, class Duration>
-    std::cv_status wait_until(
-        std::condition_variable& cv,
-        const std::chrono::time_point<Clock, Duration>& duration) {
-      assert(s);
-      return cv.wait_until(lk, duration);
-    }
-  };
-
-  Lock lock() { return Lock(this); }
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/thread-pool.cc b/third_party/nix/src/libutil/thread-pool.cc
deleted file mode 100644
index 7c6b0a1b46..0000000000
--- a/third_party/nix/src/libutil/thread-pool.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-#include "libutil/thread-pool.hh"
-
-#include <glog/logging.h>
-
-#include "libutil/affinity.hh"
-
-namespace nix {
-
-ThreadPool::ThreadPool(size_t _maxThreads) : maxThreads(_maxThreads) {
-  restoreAffinity();  // FIXME
-
-  if (maxThreads == 0u) {
-    maxThreads = std::thread::hardware_concurrency();
-    if (maxThreads == 0u) {
-      maxThreads = 1;
-    }
-  }
-
-  DLOG(INFO) << "starting pool of " << maxThreads - 1 << " threads";
-}
-
-ThreadPool::~ThreadPool() { shutdown(); }
-
-void ThreadPool::shutdown() {
-  std::vector<std::thread> workers;
-  {
-    auto state(state_.lock());
-    quit = true;
-    std::swap(workers, state->workers);
-  }
-
-  if (workers.empty()) {
-    return;
-  }
-
-  DLOG(INFO) << "reaping " << workers.size() << " worker threads";
-
-  work.notify_all();
-
-  for (auto& thr : workers) {
-    thr.join();
-  }
-}
-
-void ThreadPool::enqueue(const work_t& t) {
-  auto state(state_.lock());
-  if (quit) {
-    throw ThreadPoolShutDown(
-        "cannot enqueue a work item while the thread pool is shutting down");
-  }
-  state->pending.push(t);
-  /* Note: process() also executes items, so count it as a worker. */
-  if (state->pending.size() > state->workers.size() + 1 &&
-      state->workers.size() + 1 < maxThreads) {
-    state->workers.emplace_back(&ThreadPool::doWork, this, false);
-  }
-  work.notify_one();
-}
-
-void ThreadPool::process() {
-  state_.lock()->draining = true;
-
-  /* Do work until no more work is pending or active. */
-  try {
-    doWork(true);
-
-    auto state(state_.lock());
-
-    assert(quit);
-
-    if (state->exception) {
-      std::rethrow_exception(state->exception);
-    }
-
-  } catch (...) {
-    /* In the exceptional case, some workers may still be
-       active. They may be referencing the stack frame of the
-       caller. So wait for them to finish. (~ThreadPool also does
-       this, but it might be destroyed after objects referenced by
-       the work item lambdas.) */
-    shutdown();
-    throw;
-  }
-}
-
-void ThreadPool::doWork(bool mainThread) {
-  if (!mainThread) {
-    interruptCheck = [&]() { return (bool)quit; };
-  }
-
-  bool didWork = false;
-  std::exception_ptr exc;
-
-  while (true) {
-    work_t w;
-    {
-      auto state(state_.lock());
-
-      if (didWork) {
-        assert(state->active);
-        state->active--;
-
-        if (exc) {
-          if (!state->exception) {
-            state->exception = exc;
-            // Tell the other workers to quit.
-            quit = true;
-            work.notify_all();
-          } else {
-            /* Print the exception, since we can't
-               propagate it. */
-            try {
-              std::rethrow_exception(exc);
-            } catch (std::exception& e) {
-              if ((dynamic_cast<Interrupted*>(&e) == nullptr) &&
-                  (dynamic_cast<ThreadPoolShutDown*>(&e) == nullptr)) {
-                ignoreException();
-              }
-            } catch (...) {
-            }
-          }
-        }
-      }
-
-      /* Wait until a work item is available or we're asked to
-         quit. */
-      while (true) {
-        if (quit) {
-          return;
-        }
-
-        if (!state->pending.empty()) {
-          break;
-        }
-
-        /* If there are no active or pending items, and the
-           main thread is running process(), then no new items
-           can be added. So exit. */
-        if ((state->active == 0u) && state->draining) {
-          quit = true;
-          work.notify_all();
-          return;
-        }
-
-        state.wait(work);
-      }
-
-      w = std::move(state->pending.front());
-      state->pending.pop();
-      state->active++;
-    }
-
-    try {
-      w();
-    } catch (...) {
-      exc = std::current_exception();
-    }
-
-    didWork = true;
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/thread-pool.hh b/third_party/nix/src/libutil/thread-pool.hh
deleted file mode 100644
index 0efc4c1bfc..0000000000
--- a/third_party/nix/src/libutil/thread-pool.hh
+++ /dev/null
@@ -1,140 +0,0 @@
-#pragma once
-
-#include <atomic>
-#include <functional>
-#include <map>
-#include <queue>
-#include <thread>
-
-#include "libutil/sync.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-MakeError(ThreadPoolShutDown, Error);
-
-/* A simple thread pool that executes a queue of work items
-   (lambdas). */
-class ThreadPool {
- public:
-  explicit ThreadPool(size_t maxThreads = 0);
-
-  ~ThreadPool();
-
-  // FIXME: use std::packaged_task?
-  typedef std::function<void()> work_t;
-
-  /* Enqueue a function to be executed by the thread pool. */
-  void enqueue(const work_t& t);
-
-  /* Execute work items until the queue is empty. Note that work
-     items are allowed to add new items to the queue; this is
-     handled correctly. Queue processing stops prematurely if any
-     work item throws an exception. This exception is propagated to
-     the calling thread. If multiple work items throw an exception
-     concurrently, only one item is propagated; the others are
-     printed on stderr and otherwise ignored. */
-  void process();
-
- private:
-  size_t maxThreads;
-
-  struct State {
-    std::queue<work_t> pending;
-    size_t active = 0;
-    std::exception_ptr exception;
-    std::vector<std::thread> workers;
-    bool draining = false;
-  };
-
-  std::atomic_bool quit{false};
-
-  Sync<State> state_;
-
-  std::condition_variable work;
-
-  void doWork(bool mainThread);
-
-  void shutdown();
-};
-
-/* Process in parallel a set of items of type T that have a partial
-   ordering between them. Thus, any item is only processed after all
-   its dependencies have been processed. */
-template <typename T>
-void processGraph(ThreadPool& pool, const std::set<T>& nodes,
-                  std::function<std::set<T>(const T&)> getEdges,
-                  std::function<void(const T&)> processNode) {
-  struct Graph {
-    std::set<T> left;
-    std::map<T, std::set<T>> refs, rrefs;
-  };
-
-  Sync<Graph> graph_(Graph{nodes, {}, {}});
-
-  std::function<void(const T&)> worker;
-
-  worker = [&](const T& node) {
-    {
-      auto graph(graph_.lock());
-      auto i = graph->refs.find(node);
-      if (i == graph->refs.end()) {
-        goto getRefs;
-      }
-      goto doWork;
-    }
-
-  getRefs : {
-    auto refs = getEdges(node);
-    refs.erase(node);
-
-    {
-      auto graph(graph_.lock());
-      for (auto& ref : refs) {
-        if (graph->left.count(ref)) {
-          graph->refs[node].insert(ref);
-          graph->rrefs[ref].insert(node);
-        }
-      }
-      if (graph->refs[node].empty()) {
-        goto doWork;
-      }
-    }
-  }
-
-    return;
-
-  doWork:
-    processNode(node);
-
-    /* Enqueue work for all nodes that were waiting on this one
-       and have no unprocessed dependencies. */
-    {
-      auto graph(graph_.lock());
-      for (auto& rref : graph->rrefs[node]) {
-        auto& refs(graph->refs[rref]);
-        auto i = refs.find(node);
-        assert(i != refs.end());
-        refs.erase(i);
-        if (refs.empty()) {
-          pool.enqueue(std::bind(worker, rref));
-        }
-      }
-      graph->left.erase(node);
-      graph->refs.erase(node);
-      graph->rrefs.erase(node);
-    }
-  };
-
-  for (auto& node : nodes) {
-    pool.enqueue(std::bind(worker, std::ref(node)));
-  }
-
-  pool.process();
-
-  if (!graph_.lock()->left.empty()) {
-    throw Error("graph processing incomplete (cyclic reference?)");
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/types.hh b/third_party/nix/src/libutil/types.hh
deleted file mode 100644
index bf95206d08..0000000000
--- a/third_party/nix/src/libutil/types.hh
+++ /dev/null
@@ -1,118 +0,0 @@
-#pragma once
-
-#include <boost/format.hpp>
-#include <list>
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-
-#include "libutil/ref.hh"
-
-/* Before 4.7, gcc's std::exception uses empty throw() specifiers for
- * its (virtual) destructor and what() in c++11 mode, in violation of spec
- */
-#ifdef __GNUC__
-#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
-#define EXCEPTION_NEEDS_THROW_SPEC
-#endif
-#endif
-
-namespace nix {
-
-/* Inherit some names from other namespaces for convenience. */
-using boost::format;
-
-/* A variadic template that does nothing. Useful to call a function
-   for all variadic arguments but ignoring the result. */
-struct nop {
-  template <typename... T>
-  explicit nop(T...) {}
-};
-
-struct FormatOrString {
-  std::string s;
-  FormatOrString(const std::string& s) : s(s){};
-  FormatOrString(const format& f) : s(f.str()){};
-  FormatOrString(const char* s) : s(s){};
-};
-
-/* A helper for formatting strings. β€˜fmt(format, a_0, ..., a_n)’ is
-   equivalent to β€˜boost::format(format) % a_0 % ... %
-   ... a_n’. However, β€˜fmt(s)’ is equivalent to β€˜s’ (so no %-expansion
-   takes place). */
-
-inline std::string fmt(const std::string& s) { return s; }
-
-inline std::string fmt(std::string_view s) { return std::string(s); }
-
-inline std::string fmt(const char* s) { return s; }
-
-inline std::string fmt(const FormatOrString& fs) { return fs.s; }
-
-template <typename... Args>
-inline std::string fmt(const std::string& fs, Args... args) {
-  boost::format f(fs);
-  f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
-  nop{boost::io::detail::feed(f, args)...};
-  return f.str();
-}
-
-/* BaseError should generally not be caught, as it has Interrupted as
-   a subclass. Catch Error instead. */
-class BaseError : public std::exception {
- protected:
-  std::string prefix_;  // used for location traces etc.
-  std::string err;
-
- public:
-  unsigned int status = 1;  // exit status
-
-  template <typename... Args>
-  explicit BaseError(unsigned int status, Args... args)
-      : err(fmt(args...)), status(status) {}
-
-  template <typename... Args>
-  explicit BaseError(Args... args) : err(fmt(args...)) {}
-
-#ifdef EXCEPTION_NEEDS_THROW_SPEC
-  ~BaseError() noexcept {};
-  const char* what() const noexcept { return err.c_str(); }
-#else
-  const char* what() const noexcept { return err.c_str(); }
-#endif
-
-  const std::string& msg() const { return err; }
-  const std::string& prefix() const { return prefix_; }
-  BaseError& addPrefix(const FormatOrString& fs);
-};
-
-#define MakeError(newClass, superClass) \
-  class newClass : public superClass {  \
-   public:                              \
-    using superClass::superClass;       \
-  };
-
-MakeError(Error, BaseError);
-
-class SysError : public Error {
- public:
-  int errNo;
-
-  template <typename... Args>
-  explicit SysError(Args... args) : Error(addErrno(fmt(args...))) {}
-
- private:
-  std::string addErrno(const std::string& s);
-};
-
-typedef std::list<std::string> Strings;
-using StringSet = std::set<std::string>;
-using StringMap = std::map<std::string, std::string>;
-
-/* Paths are just strings. */
-using Path = std::string;
-using Paths = std::list<Path>;
-using PathSet = std::set<Path>;
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/util.cc b/third_party/nix/src/libutil/util.cc
deleted file mode 100644
index aea1e68e3c..0000000000
--- a/third_party/nix/src/libutil/util.cc
+++ /dev/null
@@ -1,1426 +0,0 @@
-#include "libutil/util.hh"
-
-#include <cctype>
-#include <cerrno>
-#include <climits>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <future>
-#include <iostream>
-#include <sstream>
-#include <thread>
-#include <utility>
-
-#include <absl/strings/str_split.h>
-#include <absl/strings/string_view.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/ioctl.h>
-#include <sys/prctl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "libutil/affinity.hh"
-#include "libutil/finally.hh"
-#include "libutil/lazy.hh"
-#include "libutil/serialise.hh"
-#include "libutil/sync.hh"
-#include "nix_config.h"
-
-namespace nix {
-
-const std::string nativeSystem = SYSTEM;
-
-BaseError& BaseError::addPrefix(const FormatOrString& fs) {
-  prefix_ = fs.s + prefix_;
-  return *this;
-}
-
-std::string SysError::addErrno(const std::string& s) {
-  errNo = errno;
-  return s + ": " + strerror(errNo);
-}
-
-std::optional<std::string> getEnv(const std::string& key) {
-  char* value = getenv(key.c_str());
-  if (value == nullptr) return {};
-  return std::string(value);
-}
-
-std::map<std::string, std::string> getEnv() {
-  std::map<std::string, std::string> env;
-  for (size_t i = 0; environ[i] != nullptr; ++i) {
-    auto s = environ[i];
-    auto eq = strchr(s, '=');
-    if (eq == nullptr) {
-      // invalid env, just keep going
-      continue;
-    }
-    env.emplace(std::string(s, eq), std::string(eq + 1));
-  }
-  return env;
-}
-
-void clearEnv() {
-  for (auto& name : getEnv()) {
-    unsetenv(name.first.c_str());
-  }
-}
-
-void replaceEnv(const std::map<std::string, std::string>& newEnv) {
-  clearEnv();
-  for (const auto& newEnvVar : newEnv) {
-    setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1);
-  }
-}
-
-Path absPath(Path path, Path dir) {
-  if (path[0] != '/') {
-    if (dir.empty()) {
-#ifdef __GNU__
-      /* GNU (aka. GNU/Hurd) doesn't have any limitation on path
-         lengths and doesn't define `PATH_MAX'.  */
-      char* buf = getcwd(NULL, 0);
-      if (buf == NULL)
-#else
-      char buf[PATH_MAX];
-      if (getcwd(buf, sizeof(buf)) == nullptr) {
-#endif
-        throw SysError("cannot get cwd");
-    }
-    dir = buf;
-#ifdef __GNU__
-    free(buf);
-#endif
-  }
-  path = dir + "/" + path;
-}
-return canonPath(path);
-}  // namespace nix
-
-Path canonPath(const Path& path, bool resolveSymlinks) {
-  assert(!path.empty());
-
-  std::string s;
-
-  if (path[0] != '/') {
-    throw Error(format("not an absolute path: '%1%'") % path);
-  }
-
-  std::string::const_iterator i = path.begin();
-  std::string::const_iterator end = path.end();
-  std::string temp;
-
-  /* Count the number of times we follow a symlink and stop at some
-     arbitrary (but high) limit to prevent infinite loops. */
-  unsigned int followCount = 0;
-  unsigned int maxFollow = 1024;
-
-  while (true) {
-    /* Skip slashes. */
-    while (i != end && *i == '/') {
-      i++;
-    }
-    if (i == end) {
-      break;
-    }
-
-    /* Ignore `.'. */
-    if (*i == '.' && (i + 1 == end || i[1] == '/')) {
-      i++;
-    }
-
-    /* If `..', delete the last component. */
-    else if (*i == '.' && i + 1 < end && i[1] == '.' &&
-             (i + 2 == end || i[2] == '/')) {
-      if (!s.empty()) {
-        s.erase(s.rfind('/'));
-      }
-      i += 2;
-    }
-
-    /* Normal component; copy it. */
-    else {
-      s += '/';
-      while (i != end && *i != '/') {
-        s += *i++;
-      }
-
-      /* If s points to a symlink, resolve it and restart (since
-         the symlink target might contain new symlinks). */
-      if (resolveSymlinks && isLink(s)) {
-        if (++followCount >= maxFollow) {
-          throw Error(format("infinite symlink recursion in path '%1%'") %
-                      path);
-        }
-        temp = absPath(readLink(s), dirOf(s)) + std::string(i, end);
-        i = temp.begin(); /* restart */
-        end = temp.end();
-        s = "";
-      }
-    }
-  }
-
-  return s.empty() ? "/" : s;
-}
-
-// TODO(grfn) remove in favor of std::filesystem::path::parent_path()
-Path dirOf(absl::string_view path) {
-  Path::size_type pos = path.rfind('/');
-  if (pos == std::string::npos) {
-    return ".";
-  }
-  return pos == 0 ? "/" : Path(path, 0, pos);
-}
-
-// TODO(grfn) remove in favor of std::filesystem::path::root_name()
-std::string baseNameOf(const Path& path) {
-  if (path.empty()) {
-    return "";
-  }
-
-  Path::size_type last = path.length() - 1;
-  if (path[last] == '/' && last > 0) {
-    last -= 1;
-  }
-
-  Path::size_type pos = path.rfind('/', last);
-  if (pos == std::string::npos) {
-    pos = 0;
-  } else {
-    pos += 1;
-  }
-
-  return std::string(path, pos, last - pos + 1);
-}
-
-bool isInDir(const Path& path, const Path& dir) {
-  return path[0] == '/' && std::string(path, 0, dir.size()) == dir &&
-         path.size() >= dir.size() + 2 && path[dir.size()] == '/';
-}
-
-bool isDirOrInDir(const Path& path, const Path& dir) {
-  return path == dir || isInDir(path, dir);
-}
-
-struct stat lstat(const Path& path) {
-  struct stat st;
-  if (lstat(path.c_str(), &st) != 0) {
-    throw SysError(format("getting status of '%1%'") % path);
-  }
-  return st;
-}
-
-bool pathExists(const Path& path) {
-  int res;
-  struct stat st;
-  res = lstat(path.c_str(), &st);
-  if (res == 0) {
-    return true;
-  }
-  if (errno != ENOENT && errno != ENOTDIR) {
-    throw SysError(format("getting status of %1%") % path);
-  }
-  return false;
-}
-
-Path readLink(const Path& path) {
-  checkInterrupt();
-  std::vector<char> buf;
-  for (ssize_t bufSize = PATH_MAX / 4; true; bufSize += bufSize / 2) {
-    buf.resize(bufSize);
-    ssize_t rlSize = readlink(path.c_str(), buf.data(), bufSize);
-    if (rlSize == -1) {
-      if (errno == EINVAL) {
-        throw Error("'%1%' is not a symlink", path);
-      }
-      throw SysError("reading symbolic link '%1%'", path);
-
-    } else if (rlSize < bufSize) {
-      return std::string(buf.data(), rlSize);
-    }
-  }
-}
-
-bool isLink(const Path& path) {
-  struct stat st = lstat(path);
-  return S_ISLNK(st.st_mode);
-}
-
-DirEntries readDirectory(DIR* dir, const Path& path) {
-  DirEntries entries;
-  entries.reserve(64);
-
-  struct dirent* dirent;
-  while (errno = 0, dirent = readdir(dir)) { /* sic */
-    checkInterrupt();
-    std::string name = dirent->d_name;
-    if (name == "." || name == "..") {
-      continue;
-    }
-    entries.emplace_back(name, dirent->d_ino,
-#ifdef HAVE_STRUCT_DIRENT_D_TYPE
-                         dirent->d_type
-#else
-                         DT_UNKNOWN
-#endif
-    );
-  }
-  if (errno) {
-    throw SysError(format("reading directory '%1%'") % path);
-  }
-
-  return entries;
-}
-
-DirEntries readDirectory(const Path& path) {
-  AutoCloseDir dir(opendir(path.c_str()));
-  if (!dir) {
-    throw SysError(format("opening directory '%1%'") % path);
-  }
-
-  return readDirectory(dir.get(), path);
-}
-
-unsigned char getFileType(const Path& path) {
-  struct stat st = lstat(path);
-  if (S_ISDIR(st.st_mode)) {
-    return DT_DIR;
-  }
-  if (S_ISLNK(st.st_mode)) {
-    return DT_LNK;
-  }
-  if (S_ISREG(st.st_mode)) {
-    return DT_REG;
-  }
-  return DT_UNKNOWN;
-}
-
-std::string readFile(int fd) {
-  struct stat st;
-  if (fstat(fd, &st) == -1) {
-    throw SysError("statting file");
-  }
-
-  std::vector<unsigned char> buf(st.st_size);
-  readFull(fd, buf.data(), st.st_size);
-
-  return std::string(reinterpret_cast<char*>(buf.data()), st.st_size);
-}
-
-std::string readFile(absl::string_view path, bool drain) {
-  AutoCloseFD fd(open(std::string(path).c_str(), O_RDONLY | O_CLOEXEC));
-  if (!fd) {
-    throw SysError(format("opening file '%1%'") % path);
-  }
-  return drain ? drainFD(fd.get()) : readFile(fd.get());
-}
-
-void readFile(absl::string_view path, Sink& sink) {
-  // TODO(tazjin): use stdlib functions for this stuff
-  AutoCloseFD fd(open(std::string(path).c_str(), O_RDONLY | O_CLOEXEC));
-  if (!fd) {
-    throw SysError("opening file '%s'", path);
-  }
-  drainFD(fd.get(), sink);
-}
-
-void writeFile(const Path& path, const std::string& s, mode_t mode) {
-  AutoCloseFD fd(
-      open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode));
-  if (!fd) {
-    throw SysError(format("opening file '%1%'") % path);
-  }
-  writeFull(fd.get(), s);
-}
-
-void writeFile(const Path& path, Source& source, mode_t mode) {
-  AutoCloseFD fd(
-      open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode));
-  if (!fd) {
-    throw SysError(format("opening file '%1%'") % path);
-  }
-
-  std::vector<unsigned char> buf(64 * 1024);
-
-  while (true) {
-    try {
-      auto n = source.read(buf.data(), buf.size());
-      writeFull(fd.get(), static_cast<unsigned char*>(buf.data()), n);
-    } catch (EndOfFile&) {
-      break;
-    }
-  }
-}
-
-std::string readLine(int fd) {
-  std::string s;
-  while (true) {
-    checkInterrupt();
-    char ch;
-    // FIXME: inefficient
-    ssize_t rd = read(fd, &ch, 1);
-    if (rd == -1) {
-      if (errno != EINTR) {
-        throw SysError("reading a line");
-      }
-    } else if (rd == 0) {
-      throw EndOfFile("unexpected EOF reading a line");
-    } else {
-      if (ch == '\n') {
-        return s;
-      }
-      s += ch;
-    }
-  }
-}
-
-void writeLine(int fd, std::string s) {
-  s += '\n';
-  writeFull(fd, s);
-}
-
-static void _deletePath(int parentfd, const Path& path,
-                        unsigned long long& bytesFreed) {
-  checkInterrupt();
-
-  std::string name(baseNameOf(path));
-
-  struct stat st;
-  if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) {
-    if (errno == ENOENT) {
-      return;
-    }
-    throw SysError(format("getting status of '%1%'") % path);
-  }
-
-  if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) {
-    bytesFreed += st.st_size;
-  }
-
-  if (S_ISDIR(st.st_mode)) {
-    /* Make the directory accessible. */
-    const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR;
-    if ((st.st_mode & PERM_MASK) != PERM_MASK) {
-      if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1) {
-        throw SysError(format("chmod '%1%'") % path);
-      }
-    }
-
-    int fd = openat(parentfd, path.c_str(), O_RDONLY);
-    if (!fd) {
-      throw SysError(format("opening directory '%1%'") % path);
-    }
-    AutoCloseDir dir(fdopendir(fd));
-    if (!dir) {
-      throw SysError(format("opening directory '%1%'") % path);
-    }
-    for (auto& i : readDirectory(dir.get(), path)) {
-      _deletePath(dirfd(dir.get()), path + "/" + i.name, bytesFreed);
-    }
-  }
-
-  int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0;
-  if (unlinkat(parentfd, name.c_str(), flags) == -1) {
-    if (errno == ENOENT) {
-      return;
-    }
-    throw SysError(format("cannot unlink '%1%'") % path);
-  }
-}
-
-static void _deletePath(const Path& path, unsigned long long& bytesFreed) {
-  Path dir = dirOf(path);
-  if (dir == "") {
-    dir = "/";
-  }
-
-  AutoCloseFD dirfd(open(dir.c_str(), O_RDONLY));
-  if (!dirfd) {
-    // This really shouldn't fail silently, but it's left this way
-    // for backwards compatibility.
-    if (errno == ENOENT) {
-      return;
-    }
-
-    throw SysError(format("opening directory '%1%'") % path);
-  }
-
-  _deletePath(dirfd.get(), path, bytesFreed);
-}
-
-void deletePath(const Path& path) {
-  unsigned long long dummy;
-  deletePath(path, dummy);
-}
-
-void deletePath(const Path& path, unsigned long long& bytesFreed) {
-  // Activity act(*logger, lvlDebug, format("recursively deleting path '%1%'") %
-  // path);
-  bytesFreed = 0;
-  _deletePath(path, bytesFreed);
-}
-
-static Path tempName(Path tmpRoot, const Path& prefix, bool includePid,
-                     int& counter) {
-  tmpRoot = canonPath(
-      tmpRoot.empty() ? getEnv("TMPDIR").value_or("/tmp") : tmpRoot, true);
-
-  if (includePid) {
-    return (format("%1%/%2%-%3%-%4%") % tmpRoot % prefix % getpid() % counter++)
-        .str();
-  }
-  return (format("%1%/%2%-%3%") % tmpRoot % prefix % counter++).str();
-}
-
-Path createTempDir(const Path& tmpRoot, const Path& prefix, bool includePid,
-                   bool useGlobalCounter, mode_t mode) {
-  static int globalCounter = 0;
-  int localCounter = 0;
-  int& counter(useGlobalCounter ? globalCounter : localCounter);
-
-  while (true) {
-    checkInterrupt();
-    Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
-    if (mkdir(tmpDir.c_str(), mode) == 0) {
-#if __FreeBSD__
-      /* Explicitly set the group of the directory.  This is to
-         work around around problems caused by BSD's group
-         ownership semantics (directories inherit the group of
-         the parent).  For instance, the group of /tmp on
-         FreeBSD is "wheel", so all directories created in /tmp
-         will be owned by "wheel"; but if the user is not in
-         "wheel", then "tar" will fail to unpack archives that
-         have the setgid bit set on directories. */
-      if (chown(tmpDir.c_str(), (uid_t)-1, getegid()) != 0)
-        throw SysError(format("setting group of directory '%1%'") % tmpDir);
-#endif
-      return tmpDir;
-    }
-    if (errno != EEXIST) {
-      throw SysError(format("creating directory '%1%'") % tmpDir);
-    }
-  }
-}
-
-std::string getUserName() {
-  auto pw = getpwuid(geteuid());
-  std::optional<std::string> name =
-      pw != nullptr ? pw->pw_name : getEnv("USER");
-  if (!name.has_value()) {
-    throw Error("cannot figure out user name");
-  }
-  return *name;
-}
-
-static Lazy<Path> getHome2([]() {
-  std::optional<Path> homeDir = getEnv("HOME");
-  if (!homeDir) {
-    std::vector<char> buf(16384);
-    struct passwd pwbuf;
-    struct passwd* pw;
-    if (getpwuid_r(geteuid(), &pwbuf, buf.data(), buf.size(), &pw) != 0 ||
-        (pw == nullptr) || (pw->pw_dir == nullptr) || (pw->pw_dir[0] == 0)) {
-      throw Error("cannot determine user's home directory");
-    }
-    return std::string(pw->pw_dir);
-  }
-  return homeDir.value();
-});
-
-Path getHome() { return getHome2(); }
-
-Path getCacheDir() {
-  return getEnv("XDG_CACHE_HOME").value_or(getHome() + "/.cache");
-}
-
-Path getConfigDir() {
-  return getEnv("XDG_CONFIG_HOME").value_or(getHome() + "/.config");
-}
-
-std::vector<Path> getConfigDirs() {
-  Path configHome = getConfigDir();
-  std::string configDirs = getEnv("XDG_CONFIG_DIRS").value_or("");
-  std::vector<std::string> result =
-      absl::StrSplit(configDirs, absl::ByChar(':'), absl::SkipEmpty());
-  result.insert(result.begin(), configHome);
-  return result;
-}
-
-Path getDataDir() {
-  return getEnv("XDG_DATA_HOME").value_or(getHome() + "/.local/share");
-}
-
-// TODO(grfn): Remove in favor of std::filesystem::create_directories
-Paths createDirs(const Path& path) {
-  Paths created;
-  if (path == "/") {
-    return created;
-  }
-
-  struct stat st;
-  if (lstat(path.c_str(), &st) == -1) {
-    created = createDirs(dirOf(path));
-    if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST) {
-      throw SysError(format("creating directory '%1%'") % path);
-    }
-    st = lstat(path);
-    created.push_back(path);
-  }
-
-  if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1) {
-    throw SysError(format("statting symlink '%1%'") % path);
-  }
-
-  if (!S_ISDIR(st.st_mode)) {
-    throw Error(format("'%1%' is not a directory") % path);
-  }
-
-  return created;
-}
-
-void createSymlink(const Path& target, const Path& link) {
-  if (symlink(target.c_str(), link.c_str()) != 0) {
-    throw SysError(format("creating symlink from '%1%' to '%2%'") % link %
-                   target);
-  }
-}
-
-void replaceSymlink(const Path& target, const Path& link) {
-  for (unsigned int n = 0; true; n++) {
-    Path tmp = canonPath(fmt("%s/.%d_%s", dirOf(link), n, baseNameOf(link)));
-
-    try {
-      createSymlink(target, tmp);
-    } catch (SysError& e) {
-      if (e.errNo == EEXIST) {
-        continue;
-      }
-      throw;
-    }
-
-    if (rename(tmp.c_str(), link.c_str()) != 0) {
-      throw SysError(format("renaming '%1%' to '%2%'") % tmp % link);
-    }
-
-    break;
-  }
-}
-
-void readFull(int fd, unsigned char* buf, size_t count) {
-  while (count != 0u) {
-    checkInterrupt();
-    ssize_t res = read(fd, reinterpret_cast<char*>(buf), count);
-    if (res == -1) {
-      if (errno == EINTR) {
-        continue;
-      }
-      throw SysError("reading from file");
-    }
-    if (res == 0) {
-      throw EndOfFile("unexpected end-of-file");
-    }
-    count -= res;
-    buf += res;
-  }
-}
-
-void writeFull(int fd, const unsigned char* buf, size_t count,
-               bool allowInterrupts) {
-  while (count != 0u) {
-    if (allowInterrupts) {
-      checkInterrupt();
-    }
-    ssize_t res = write(fd, (char*)buf, count);
-    if (res == -1 && errno != EINTR) {
-      throw SysError("writing to file");
-    }
-    if (res > 0) {
-      count -= res;
-      buf += res;
-    }
-  }
-}
-
-void writeFull(int fd, const std::string& s, bool allowInterrupts) {
-  writeFull(fd, reinterpret_cast<const unsigned char*>(s.data()), s.size(),
-            allowInterrupts);
-}
-
-std::string drainFD(int fd, bool block) {
-  StringSink sink;
-  drainFD(fd, sink, block);
-  return std::move(*sink.s);
-}
-
-void drainFD(int fd, Sink& sink, bool block) {
-  int saved;
-
-  Finally finally([&]() {
-    if (!block) {
-      if (fcntl(fd, F_SETFL, saved) == -1) {
-        throw SysError("making file descriptor blocking");
-      }
-    }
-  });
-
-  if (!block) {
-    saved = fcntl(fd, F_GETFL);
-    if (fcntl(fd, F_SETFL, saved | O_NONBLOCK) == -1) {
-      throw SysError("making file descriptor non-blocking");
-    }
-  }
-
-  std::vector<unsigned char> buf(64 * 1024);
-  while (true) {
-    checkInterrupt();
-    ssize_t rd = read(fd, buf.data(), buf.size());
-    if (rd == -1) {
-      if (!block && (errno == EAGAIN || errno == EWOULDBLOCK)) {
-        break;
-      }
-      if (errno != EINTR) {
-        throw SysError("reading from file");
-      }
-    } else if (rd == 0) {
-      break;
-    } else {
-      sink(buf.data(), rd);
-    }
-  }
-}
-
-//////////////////////////////////////////////////////////////////////
-
-AutoDelete::AutoDelete() : del{false} {}
-
-AutoDelete::AutoDelete(std::string p, bool recursive) : path(std::move(p)) {
-  del = true;
-  this->recursive = recursive;
-}
-
-AutoDelete::~AutoDelete() {
-  try {
-    if (del) {
-      if (recursive) {
-        deletePath(path);
-      } else {
-        if (remove(path.c_str()) == -1) {
-          throw SysError(format("cannot unlink '%1%'") % path);
-        }
-      }
-    }
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-void AutoDelete::cancel() { del = false; }
-
-void AutoDelete::reset(const Path& p, bool recursive) {
-  path = p;
-  this->recursive = recursive;
-  del = true;
-}
-
-//////////////////////////////////////////////////////////////////////
-
-AutoCloseFD::AutoCloseFD() : fd{-1} {}
-
-AutoCloseFD::AutoCloseFD(int fd) : fd{fd} {}
-
-AutoCloseFD::AutoCloseFD(AutoCloseFD&& that) : fd{that.fd} { that.fd = -1; }
-
-AutoCloseFD& AutoCloseFD::operator=(AutoCloseFD&& that) {
-  close();
-  fd = that.fd;
-  that.fd = -1;
-  return *this;
-}
-
-AutoCloseFD::~AutoCloseFD() {
-  try {
-    close();
-  } catch (...) {
-    ignoreException();
-  }
-}
-
-int AutoCloseFD::get() const { return fd; }
-
-void AutoCloseFD::close() {
-  if (fd != -1) {
-    if (::close(fd) == -1) { /* This should never happen. */
-      throw SysError(format("closing file descriptor %1%") % fd);
-    }
-  }
-}
-
-AutoCloseFD::operator bool() const { return fd != -1; }
-
-int AutoCloseFD::release() {
-  int oldFD = fd;
-  fd = -1;
-  return oldFD;
-}
-
-void Pipe::create() {
-  int fds[2];
-#if HAVE_PIPE2
-  if (pipe2(fds, O_CLOEXEC) != 0) {
-    throw SysError("creating pipe");
-  }
-#else
-  if (pipe(fds) != 0) {
-    throw SysError("creating pipe");
-  }
-  closeOnExec(fds[0]);
-  closeOnExec(fds[1]);
-#endif
-  readSide = AutoCloseFD(fds[0]);
-  writeSide = AutoCloseFD(fds[1]);
-}
-
-//////////////////////////////////////////////////////////////////////
-
-Pid::Pid() = default;
-
-Pid::Pid(pid_t pid) : pid(pid) {}
-
-Pid::~Pid() {
-  if (pid != -1) {
-    kill();
-  }
-}
-
-void Pid::operator=(pid_t pid) {
-  if (this->pid != -1 && this->pid != pid) {
-    kill();
-  }
-  this->pid = pid;
-  killSignal = SIGKILL;  // reset signal to default
-}
-
-Pid::operator pid_t() { return pid; }
-
-int Pid::kill() {
-  assert(pid != -1);
-
-  DLOG(INFO) << "killing process " << pid;
-
-  /* Send the requested signal to the child.  If it has its own
-     process group, send the signal to every process in the child
-     process group (which hopefully includes *all* its children). */
-  if (::kill(separatePG ? -pid : pid, killSignal) != 0) {
-    LOG(ERROR) << SysError("killing process %d", pid).msg();
-  }
-
-  return wait();
-}
-
-int Pid::wait() {
-  assert(pid != -1);
-  while (true) {
-    int status;
-    int res = waitpid(pid, &status, 0);
-    if (res == pid) {
-      pid = -1;
-      return status;
-    }
-    if (errno != EINTR) {
-      throw SysError("cannot get child exit status");
-    }
-    checkInterrupt();
-  }
-}
-
-void Pid::setSeparatePG(bool separatePG) { this->separatePG = separatePG; }
-
-void Pid::setKillSignal(int signal) { this->killSignal = signal; }
-
-pid_t Pid::release() {
-  pid_t p = pid;
-  pid = -1;
-  return p;
-}
-
-void killUser(uid_t uid) {
-  DLOG(INFO) << "killing all processes running under UID " << uid;
-
-  assert(uid != 0); /* just to be safe... */
-
-  /* The system call kill(-1, sig) sends the signal `sig' to all
-     users to which the current process can send signals.  So we
-     fork a process, switch to uid, and send a mass kill. */
-
-  ProcessOptions options;
-
-  Pid pid(startProcess(
-      [&]() {
-        if (setuid(uid) == -1) {
-          throw SysError("setting uid");
-        }
-
-        while (true) {
-          if (kill(-1, SIGKILL) == 0) {
-            break;
-          }
-          if (errno == ESRCH) {
-            break;
-          } /* no more processes */
-          if (errno != EINTR) {
-            throw SysError(format("cannot kill processes for uid '%1%'") % uid);
-          }
-        }
-
-        _exit(0);
-      },
-      options));
-
-  int status = pid.wait();
-  if (status != 0) {
-    throw Error(format("cannot kill processes for uid '%1%': %2%") % uid %
-                statusToString(status));
-  }
-
-  /* !!! We should really do some check to make sure that there are
-     no processes left running under `uid', but there is no portable
-     way to do so (I think).  The most reliable way may be `ps -eo
-     uid | grep -q $uid'. */
-}
-
-//////////////////////////////////////////////////////////////////////
-
-/*
- * Please note that it is not legal for this function to call vfork().  If the
- * process created by vfork() returns from the function in which vfork() was
- * called, or calls any other function before successfully calling _exit() or
- * one of the exec*() family of functions, the behavior is undefined.
- */
-static pid_t doFork(const std::function<void()>& fun) __attribute__((noinline));
-static pid_t doFork(const std::function<void()>& fun) {
-#ifdef __linux__
-  // TODO(kanepyork): call clone() instead for faster forking
-#endif
-
-  pid_t pid = fork();
-  if (pid != 0) {
-    return pid;
-  }
-  fun();
-  abort();
-}
-
-pid_t startProcess(std::function<void()> fun, const ProcessOptions& options) {
-  auto wrapper = [&]() {
-    try {
-#if __linux__
-      if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) {
-        throw SysError("setting death signal");
-      }
-#endif
-      restoreAffinity();
-      fun();
-    } catch (std::exception& e) {
-      try {
-        LOG(ERROR) << options.errorPrefix << e.what();
-      } catch (...) {
-      }
-    } catch (...) {
-    }
-    if (options.runExitHandlers) {
-      exit(1);
-    } else {
-      _exit(1);
-    }
-  };
-
-  pid_t pid = doFork(wrapper);
-  if (pid == -1) {
-    throw SysError("unable to fork");
-  }
-
-  return pid;
-}
-
-std::vector<char*> stringsToCharPtrs(const Strings& ss) {
-  std::vector<char*> res;
-  for (auto& s : ss) {
-    res.push_back(const_cast<char*>(s.c_str()));
-  }
-  res.push_back(nullptr);
-  return res;
-}
-
-std::string runProgram(const Path& program, bool searchPath,
-                       const Strings& args,
-                       const std::optional<std::string>& input) {
-  RunOptions opts(program, args);
-  opts.searchPath = searchPath;
-  opts.input = input;
-
-  auto res = runProgram(opts);
-
-  if (!statusOk(res.first)) {
-    throw ExecError(res.first, fmt("program '%1%' %2%", program,
-                                   statusToString(res.first)));
-  }
-
-  return res.second;
-}
-
-std::pair<int, std::string> runProgram(const RunOptions& options_) {
-  RunOptions options(options_);
-  StringSink sink;
-  options.standardOut = &sink;
-
-  int status = 0;
-
-  try {
-    runProgram2(options);
-  } catch (ExecError& e) {
-    status = e.status;
-  }
-
-  return {status, std::move(*sink.s)};
-}
-
-void runProgram2(const RunOptions& options) {
-  checkInterrupt();
-
-  assert(!(options.standardIn && options.input));
-
-  std::unique_ptr<Source> source_;
-  Source* source = options.standardIn;
-
-  if (options.input) {
-    source_ = std::make_unique<StringSource>(*options.input);
-    source = source_.get();
-  }
-
-  /* Create a pipe. */
-  Pipe out;
-  Pipe in;
-  if (options.standardOut != nullptr) {
-    out.create();
-  }
-  if (source != nullptr) {
-    in.create();
-  }
-
-  ProcessOptions processOptions;
-
-  /* Fork. */
-  Pid pid(startProcess(
-      [&]() {
-        if (options.environment) {
-          replaceEnv(*options.environment);
-        }
-        if ((options.standardOut != nullptr) &&
-            dup2(out.writeSide.get(), STDOUT_FILENO) == -1) {
-          throw SysError("dupping stdout");
-        }
-        if (options.mergeStderrToStdout) {
-          if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
-            throw SysError("cannot dup stdout into stderr");
-          }
-        }
-        if ((source != nullptr) &&
-            dup2(in.readSide.get(), STDIN_FILENO) == -1) {
-          throw SysError("dupping stdin");
-        }
-
-        if (options.chdir && chdir((*options.chdir).c_str()) == -1) {
-          throw SysError("chdir failed");
-        }
-        if (options.gid && setgid(*options.gid) == -1) {
-          throw SysError("setgid failed");
-        }
-        /* Drop all other groups if we're setgid. */
-        if (options.gid && setgroups(0, nullptr) == -1) {
-          throw SysError("setgroups failed");
-        }
-        if (options.uid && setuid(*options.uid) == -1) {
-          throw SysError("setuid failed");
-        }
-
-        Strings args_(options.args);
-        args_.push_front(options.program);
-
-        restoreSignals();
-
-        if (options.searchPath) {
-          execvp(options.program.c_str(), stringsToCharPtrs(args_).data());
-        } else {
-          execv(options.program.c_str(), stringsToCharPtrs(args_).data());
-        }
-
-        throw SysError("executing '%1%'", options.program);
-      },
-      processOptions));
-
-  out.writeSide = AutoCloseFD(-1);
-
-  std::thread writerThread;
-
-  std::promise<void> promise;
-
-  Finally doJoin([&]() {
-    if (writerThread.joinable()) {
-      writerThread.join();
-    }
-  });
-
-  if (source != nullptr) {
-    in.readSide = AutoCloseFD(-1);
-    writerThread = std::thread([&]() {
-      try {
-        std::vector<unsigned char> buf(8 * 1024);
-        while (true) {
-          size_t n;
-          try {
-            n = source->read(buf.data(), buf.size());
-          } catch (EndOfFile&) {
-            break;
-          }
-          writeFull(in.writeSide.get(), buf.data(), n);
-        }
-        promise.set_value();
-      } catch (...) {
-        promise.set_exception(std::current_exception());
-      }
-      in.writeSide = AutoCloseFD(-1);
-    });
-  }
-
-  if (options.standardOut != nullptr) {
-    drainFD(out.readSide.get(), *options.standardOut);
-  }
-
-  /* Wait for the child to finish. */
-  int status = pid.wait();
-
-  /* Wait for the writer thread to finish. */
-  if (source != nullptr) {
-    promise.get_future().get();
-  }
-
-  if (status != 0) {
-    throw ExecError(status, fmt("program '%1%' %2%", options.program,
-                                statusToString(status)));
-  }
-}
-
-void closeMostFDs(const std::set<int>& exceptions) {
-#if __linux__
-  try {
-    for (auto& s : readDirectory("/proc/self/fd")) {
-      auto fd = std::stoi(s.name);
-      if (exceptions.count(fd) == 0u) {
-        DLOG(INFO) << "closing leaked FD " << fd;
-        close(fd);
-      }
-    }
-    return;
-  } catch (SysError&) {
-  }
-#endif
-
-  int maxFD = 0;
-  maxFD = sysconf(_SC_OPEN_MAX);
-  for (int fd = 0; fd < maxFD; ++fd) {
-    if (exceptions.count(fd) == 0u) {
-      close(fd);
-    } /* ignore result */
-  }
-}
-
-void closeOnExec(int fd) {
-  int prev;
-  if ((prev = fcntl(fd, F_GETFD, 0)) == -1 ||
-      fcntl(fd, F_SETFD, prev | FD_CLOEXEC) == -1) {
-    throw SysError("setting close-on-exec flag");
-  }
-}
-
-//////////////////////////////////////////////////////////////////////
-
-bool _isInterrupted = false;
-
-static thread_local bool interruptThrown = false;
-thread_local std::function<bool()> interruptCheck;
-
-void setInterruptThrown() { interruptThrown = true; }
-
-void _interrupted() {
-  /* Block user interrupts while an exception is being handled.
-     Throwing an exception while another exception is being handled
-     kills the program! */
-  if (!interruptThrown && (std::uncaught_exceptions() == 0)) {
-    interruptThrown = true;
-    throw Interrupted("interrupted by the user");
-  }
-}
-
-//////////////////////////////////////////////////////////////////////
-
-std::string concatStringsSep(const std::string& sep, const Strings& ss) {
-  std::string s;
-  for (auto& i : ss) {
-    if (!s.empty()) {
-      s += sep;
-    }
-    s += i;
-  }
-  return s;
-}
-
-std::string concatStringsSep(const std::string& sep, const StringSet& ss) {
-  std::string s;
-  for (auto& i : ss) {
-    if (!s.empty()) {
-      s += sep;
-    }
-    s += i;
-  }
-  return s;
-}
-
-std::string replaceStrings(const std::string& s, const std::string& from,
-                           const std::string& to) {
-  if (from.empty()) {
-    return s;
-  }
-  std::string res = s;
-  size_t pos = 0;
-  while ((pos = res.find(from, pos)) != std::string::npos) {
-    res.replace(pos, from.size(), to);
-    pos += to.size();
-  }
-  return res;
-}
-
-std::string statusToString(int status) {
-  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-    if (WIFEXITED(status)) {
-      return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
-    }
-    if (WIFSIGNALED(status)) {
-      int sig = WTERMSIG(status);
-#if HAVE_STRSIGNAL
-      const char* description = strsignal(sig);
-      return (format("failed due to signal %1% (%2%)") % sig % description)
-          .str();
-#else
-      return (format("failed due to signal %1%") % sig).str();
-#endif
-    } else {
-      return "died abnormally";
-    }
-  } else {
-    return "succeeded";
-  }
-}
-
-bool statusOk(int status) {
-  return WIFEXITED(status) && WEXITSTATUS(status) == 0;
-}
-
-std::string toLower(const std::string& s) {
-  std::string r(s);
-  for (auto& c : r) {
-    c = std::tolower(c);
-  }
-  return r;
-}
-
-std::string shellEscape(const std::string& s) {
-  std::string r = "'";
-  for (auto& i : s) {
-    if (i == '\'') {
-      r += "'\\''";
-    } else {
-      r += i;
-    }
-  }
-  r += '\'';
-  return r;
-}
-
-void ignoreException() {
-  try {
-    throw;
-  } catch (std::exception& e) {
-    LOG(ERROR) << "error (ignored): " << e.what();
-  }
-}
-
-std::string filterANSIEscapes(const std::string& s, bool filterAll,
-                              unsigned int width) {
-  std::string t;
-  std::string e;
-  size_t w = 0;
-  auto i = s.begin();
-
-  while (w < static_cast<size_t>(width) && i != s.end()) {
-    if (*i == '\e') {
-      std::string e;
-      e += *i++;
-      char last = 0;
-
-      if (i != s.end() && *i == '[') {
-        e += *i++;
-        // eat parameter bytes
-        while (i != s.end() && *i >= 0x30 && *i <= 0x3f) {
-          e += *i++;
-        }
-        // eat intermediate bytes
-        while (i != s.end() && *i >= 0x20 && *i <= 0x2f) {
-          e += *i++;
-        }
-        // eat final byte
-        if (i != s.end() && *i >= 0x40 && *i <= 0x7e) {
-          e += last = *i++;
-        }
-      } else {
-        if (i != s.end() && *i >= 0x40 && *i <= 0x5f) {
-          e += *i++;
-        }
-      }
-
-      if (!filterAll && last == 'm') {
-        t += e;
-      }
-    }
-
-    else if (*i == '\t') {
-      i++;
-      t += ' ';
-      w++;
-      while (w < static_cast<size_t>(width) && ((w % 8) != 0u)) {
-        t += ' ';
-        w++;
-      }
-    }
-
-    else if (*i == '\r') {
-      // do nothing for now
-      i++;
-
-    } else {
-      t += *i++;
-      w++;
-    }
-  }
-
-  return t;
-}
-
-void callFailure(const std::function<void(std::exception_ptr exc)>& failure,
-                 const std::exception_ptr& exc) {
-  try {
-    failure(exc);
-  } catch (std::exception& e) {
-    LOG(ERROR) << "uncaught exception: " << e.what();
-    abort();
-  }
-}
-
-static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
-
-static void updateWindowSize() {
-  struct winsize ws;
-  if (ioctl(2, TIOCGWINSZ, &ws) == 0) {
-    auto windowSize_(windowSize.lock());
-    windowSize_->first = ws.ws_row;
-    windowSize_->second = ws.ws_col;
-  }
-}
-
-std::pair<unsigned short, unsigned short> getWindowSize() {
-  return *windowSize.lock();
-}
-
-static Sync<std::list<std::function<void()>>> _interruptCallbacks;
-
-static void signalHandlerThread(sigset_t set) {
-  while (true) {
-    int signal = 0;
-    sigwait(&set, &signal);
-
-    if (signal == SIGINT || signal == SIGTERM || signal == SIGHUP) {
-      triggerInterrupt();
-
-    } else if (signal == SIGWINCH) {
-      updateWindowSize();
-    }
-  }
-}
-
-void triggerInterrupt() {
-  _isInterrupted = true;
-
-  {
-    auto interruptCallbacks(_interruptCallbacks.lock());
-    for (auto& callback : *interruptCallbacks) {
-      try {
-        callback();
-      } catch (...) {
-        ignoreException();
-      }
-    }
-  }
-}
-
-static sigset_t savedSignalMask;
-
-void startSignalHandlerThread() {
-  updateWindowSize();
-
-  if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask) != 0) {
-    throw SysError("quering signal mask");
-  }
-
-  sigset_t set;
-  sigemptyset(&set);
-  sigaddset(&set, SIGINT);
-  sigaddset(&set, SIGTERM);
-  sigaddset(&set, SIGHUP);
-  sigaddset(&set, SIGPIPE);
-  sigaddset(&set, SIGWINCH);
-  if (pthread_sigmask(SIG_BLOCK, &set, nullptr) != 0) {
-    throw SysError("blocking signals");
-  }
-
-  std::thread(signalHandlerThread, set).detach();
-}
-
-void restoreSignals() {
-  if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr) != 0) {
-    throw SysError("restoring signals");
-  }
-}
-
-/* RAII helper to automatically deregister a callback. */
-struct InterruptCallbackImpl : InterruptCallback {
-  std::list<std::function<void()>>::iterator it;
-  ~InterruptCallbackImpl() override { _interruptCallbacks.lock()->erase(it); }
-};
-
-std::unique_ptr<InterruptCallback> createInterruptCallback(
-    const std::function<void()>& callback) {
-  auto interruptCallbacks(_interruptCallbacks.lock());
-  interruptCallbacks->push_back(callback);
-
-  auto res = std::make_unique<InterruptCallbackImpl>();
-  res->it = interruptCallbacks->end();
-  res->it--;
-
-  return std::unique_ptr<InterruptCallback>(res.release());
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/util.hh b/third_party/nix/src/libutil/util.hh
deleted file mode 100644
index b3349c4f39..0000000000
--- a/third_party/nix/src/libutil/util.hh
+++ /dev/null
@@ -1,476 +0,0 @@
-#pragma once
-
-#include <cstdio>
-#include <functional>
-#include <future>
-#include <limits>
-#include <map>
-#include <optional>
-#include <sstream>
-
-#include <absl/strings/string_view.h>
-#include <dirent.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libutil/types.hh"
-
-namespace nix {
-
-struct Sink;
-struct Source;
-
-/* The system for which Nix is compiled. */
-extern const std::string nativeSystem;
-
-/* Return an environment variable. */
-std::optional<std::string> getEnv(const std::string& key);
-
-/* Get the entire environment. */
-std::map<std::string, std::string> getEnv();
-
-/* Clear the environment. */
-void clearEnv();
-
-/* Return an absolutized path, resolving paths relative to the
-   specified directory, or the current directory otherwise.  The path
-   is also canonicalised. */
-Path absPath(Path path, Path dir = "");
-
-/* Canonicalise a path by removing all `.' or `..' components and
-   double or trailing slashes.  Optionally resolves all symlink
-   components such that each component of the resulting path is *not*
-   a symbolic link. */
-Path canonPath(const Path& path, bool resolveSymlinks = false);
-
-/* Return the directory part of the given canonical path, i.e.,
-   everything before the final `/'.  If the path is the root or an
-   immediate child thereof (e.g., `/foo'), this means an empty string
-   is returned. */
-Path dirOf(absl::string_view path);
-
-/* Return the base name of the given canonical path, i.e., everything
-   following the final `/'. */
-std::string baseNameOf(const Path& path);
-
-/* Check whether 'path' is a descendant of 'dir'. */
-bool isInDir(const Path& path, const Path& dir);
-
-/* Check whether 'path' is equal to 'dir' or a descendant of 'dir'. */
-bool isDirOrInDir(const Path& path, const Path& dir);
-
-/* Get status of `path'. */
-struct stat lstat(const Path& path);
-
-/* Return true iff the given path exists. */
-bool pathExists(const Path& path);
-
-/* Read the contents (target) of a symbolic link.  The result is not
-   in any way canonicalised. */
-Path readLink(const Path& path);
-
-bool isLink(const Path& path);
-
-/* Read the contents of a directory.  The entries `.' and `..' are
-   removed. */
-struct DirEntry {
-  std::string name;
-  ino_t ino;
-  unsigned char type;  // one of DT_*
-  DirEntry(const std::string& name, ino_t ino, unsigned char type)
-      : name(name), ino(ino), type(type) {}
-};
-
-typedef std::vector<DirEntry> DirEntries;
-
-DirEntries readDirectory(const Path& path);
-
-unsigned char getFileType(const Path& path);
-
-/* Read the contents of a file into a string. */
-std::string readFile(int fd);
-std::string readFile(absl::string_view path, bool drain = false);
-void readFile(absl::string_view path, Sink& sink);
-
-/* Write a string to a file. */
-void writeFile(const Path& path, const std::string& s, mode_t mode = 0666);
-
-void writeFile(const Path& path, Source& source, mode_t mode = 0666);
-
-/* Read a line from a file descriptor. */
-std::string readLine(int fd);
-
-/* Write a line to a file descriptor. */
-void writeLine(int fd, std::string s);
-
-/* Delete a path; i.e., in the case of a directory, it is deleted
-   recursively. It's not an error if the path does not exist. The
-   second variant returns the number of bytes and blocks freed. */
-void deletePath(const Path& path);
-
-void deletePath(const Path& path, unsigned long long& bytesFreed);
-
-/* Create a temporary directory. */
-Path createTempDir(const Path& tmpRoot = "", const Path& prefix = "nix",
-                   bool includePid = true, bool useGlobalCounter = true,
-                   mode_t mode = 0755);
-
-std::string getUserName();
-
-/* Return $HOME or the user's home directory from /etc/passwd. */
-Path getHome();
-
-/* Return $XDG_CACHE_HOME or $HOME/.cache. */
-Path getCacheDir();
-
-/* Return $XDG_CONFIG_HOME or $HOME/.config. */
-Path getConfigDir();
-
-/* Return the directories to search for user configuration files */
-std::vector<Path> getConfigDirs();
-
-/* Return $XDG_DATA_HOME or $HOME/.local/share. */
-Path getDataDir();
-
-/* Create a directory and all its parents, if necessary.  Returns the
-   list of created directories, in order of creation. */
-Paths createDirs(const Path& path);
-
-/* Create a symlink. */
-void createSymlink(const Path& target, const Path& link);
-
-/* Atomically create or replace a symlink. */
-void replaceSymlink(const Path& target, const Path& link);
-
-/* Wrappers arount read()/write() that read/write exactly the
-   requested number of bytes. */
-void readFull(int fd, unsigned char* buf, size_t count);
-void writeFull(int fd, const unsigned char* buf, size_t count,
-               bool allowInterrupts = true);
-void writeFull(int fd, const std::string& s, bool allowInterrupts = true);
-
-MakeError(EndOfFile, Error);
-
-/* Read a file descriptor until EOF occurs. */
-std::string drainFD(int fd, bool block = true);
-
-void drainFD(int fd, Sink& sink, bool block = true);
-
-/* Automatic cleanup of resources. */
-
-class AutoDelete {
-  Path path;
-  bool del;
-  bool recursive;
-
- public:
-  AutoDelete();
-  explicit AutoDelete(Path p, bool recursive = true);
-  ~AutoDelete();
-  void cancel();
-  void reset(const Path& p, bool recursive = true);
-  explicit operator Path() const { return path; }
-};
-
-class AutoCloseFD {
-  int fd;
-  void close();
-
- public:
-  AutoCloseFD();
-  explicit AutoCloseFD(int fd);
-  AutoCloseFD(const AutoCloseFD& fd) = delete;
-  AutoCloseFD(AutoCloseFD&& that);
-  ~AutoCloseFD();
-  AutoCloseFD& operator=(const AutoCloseFD& fd) = delete;
-  AutoCloseFD& operator=(AutoCloseFD&& that);
-  int get() const;
-  explicit operator bool() const;
-  int release();
-};
-
-class Pipe {
- public:
-  AutoCloseFD readSide, writeSide;
-  void create();
-};
-
-struct DIRDeleter {
-  void operator()(DIR* dir) const { closedir(dir); }
-};
-
-using AutoCloseDir = std::unique_ptr<DIR, DIRDeleter>;
-
-class Pid {
-  pid_t pid = -1;
-  bool separatePG = false;
-  int killSignal = SIGKILL;
-
- public:
-  Pid();
-  explicit Pid(pid_t pid);
-  ~Pid();
-  void operator=(pid_t pid);
-  explicit operator pid_t();
-  int kill();
-  int wait();
-
-  void setSeparatePG(bool separatePG);
-  void setKillSignal(int signal);
-  pid_t release();
-
-  friend bool operator==(const Pid& lhs, const Pid& rhs) {
-    return lhs.pid == rhs.pid;
-  }
-
-  friend bool operator!=(const Pid& lhs, const Pid& rhs) {
-    return !(lhs == rhs);
-  }
-};
-
-/* Kill all processes running under the specified uid by sending them
-   a SIGKILL. */
-void killUser(uid_t uid);
-
-/* Fork a process that runs the given function, and return the child
-   pid to the caller. */
-struct ProcessOptions {
-  std::string errorPrefix = "error: ";
-  bool dieWithParent = true;
-  bool runExitHandlers = false;
-};
-
-pid_t startProcess(std::function<void()> fun,
-                   const ProcessOptions& options = ProcessOptions());
-
-/* Run a program and return its stdout in a string (i.e., like the
-   shell backtick operator). */
-std::string runProgram(const Path& program, bool searchPath = false,
-                       const Strings& args = Strings(),
-                       const std::optional<std::string>& input = {});
-
-struct RunOptions {
-  std::optional<uid_t> uid;
-  std::optional<uid_t> gid;
-  std::optional<Path> chdir;
-  std::optional<std::map<std::string, std::string>> environment;
-  Path program;
-  bool searchPath = true;
-  Strings args;
-  std::optional<std::string> input;
-  Source* standardIn = nullptr;
-  Sink* standardOut = nullptr;
-  bool mergeStderrToStdout = false;
-  bool _killStderr = false;
-
-  RunOptions(const Path& program, const Strings& args)
-      : program(program), args(args){};
-
-  RunOptions& killStderr(bool v) {
-    _killStderr = true;
-    return *this;
-  }
-};
-
-std::pair<int, std::string> runProgram(const RunOptions& options);
-
-void runProgram2(const RunOptions& options);
-
-class ExecError : public Error {
- public:
-  int status;
-
-  template <typename... Args>
-  explicit ExecError(int status, Args... args)
-      : Error(args...), status(status) {}
-};
-
-/* Convert a list of strings to a null-terminated vector of char
-   *'s. The result must not be accessed beyond the lifetime of the
-   list of strings. */
-std::vector<char*> stringsToCharPtrs(const Strings& ss);
-
-/* Close all file descriptors except those listed in the given set.
-   Good practice in child processes. */
-void closeMostFDs(const std::set<int>& exceptions);
-
-/* Set the close-on-exec flag for the given file descriptor. */
-void closeOnExec(int fd);
-
-/* User interruption. */
-
-extern bool _isInterrupted;
-
-extern thread_local std::function<bool()> interruptCheck;
-
-void setInterruptThrown();
-
-void _interrupted();
-
-void inline checkInterrupt() {
-  if (_isInterrupted || (interruptCheck && interruptCheck())) {
-    _interrupted();
-  }
-}
-
-MakeError(Interrupted, BaseError);
-
-MakeError(FormatError, Error);
-
-/* Concatenate the given strings with a separator between the
-   elements. */
-std::string concatStringsSep(const std::string& sep, const Strings& ss);
-std::string concatStringsSep(const std::string& sep, const StringSet& ss);
-
-/* Replace all occurrences of a string inside another string. */
-std::string replaceStrings(const std::string& s, const std::string& from,
-                           const std::string& to);
-
-/* Convert the exit status of a child as returned by wait() into an
-   error string. */
-std::string statusToString(int status);
-
-bool statusOk(int status);
-
-/* Parse a string into a float. */
-template <class N>
-bool string2Float(const std::string& s, N& n) {
-  std::istringstream str(s);
-  str >> n;
-  return str && str.get() == EOF;
-}
-
-/* Convert a string to lower case. */
-std::string toLower(const std::string& s);
-
-/* Escape a string as a shell word. */
-std::string shellEscape(const std::string& s);
-
-/* Exception handling in destructors: print an error message, then
-   ignore the exception. */
-void ignoreException();
-
-/* Some ANSI escape sequences. */
-#define ANSI_NORMAL "\e[0m"
-#define ANSI_BOLD "\e[1m"
-#define ANSI_FAINT "\e[2m"
-#define ANSI_RED "\e[31;1m"
-#define ANSI_GREEN "\e[32;1m"
-#define ANSI_BLUE "\e[34;1m"
-
-/* Truncate a string to 'width' printable characters. If 'filterAll'
-   is true, all ANSI escape sequences are filtered out. Otherwise,
-   some escape sequences (such as colour setting) are copied but not
-   included in the character count. Also, tabs are expanded to
-   spaces. */
-std::string filterANSIEscapes(
-    const std::string& s, bool filterAll = false,
-    unsigned int width = std::numeric_limits<unsigned int>::max());
-
-/* Get a value for the specified key from an associate container, or a
-   default value if the key doesn't exist. */
-template <class T>
-std::string get(const T& map, const std::string& key,
-                const std::string& def = "") {
-  auto i = map.find(key);
-  return i == map.end() ? def : i->second;
-}
-
-/* A callback is a wrapper around a lambda that accepts a valid of
-   type T or an exception. (We abuse std::future<T> to pass the value or
-   exception.) */
-template <typename T>
-class Callback {
-  std::function<void(std::future<T>)> fun;
-  std::atomic_flag done = ATOMIC_FLAG_INIT;
-
- public:
-  explicit Callback(std::function<void(std::future<T>)> fun) : fun(fun) {}
-
-  Callback(Callback&& callback) : fun(std::move(callback.fun)) {
-    auto prev = callback.done.test_and_set();
-    if (prev) {
-      done.test_and_set();
-    }
-  }
-
-// The unused-variable assert is disabled in this block because the
-// `prev` variables are only used in debug mode (in the asserts).
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-variable"
-
-  void operator()(T&& t) noexcept {
-    auto prev = done.test_and_set();
-    assert(!prev);
-    std::promise<T> promise;
-    promise.set_value(std::move(t));
-    fun(promise.get_future());
-  }
-
-  void rethrow(
-      const std::exception_ptr& exc = std::current_exception()) noexcept {
-    auto prev = done.test_and_set();
-    assert(!prev);
-    std::promise<T> promise;
-    promise.set_exception(exc);
-    fun(promise.get_future());
-  }
-
-#pragma clang diagnostic pop
-};
-
-/* Start a thread that handles various signals. Also block those signals
-   on the current thread (and thus any threads created by it). */
-void startSignalHandlerThread();
-
-/* Restore default signal handling. */
-void restoreSignals();
-
-struct InterruptCallback {
-  virtual ~InterruptCallback(){};
-};
-
-/* Register a function that gets called on SIGINT (in a non-signal
-   context). */
-std::unique_ptr<InterruptCallback> createInterruptCallback(
-    const std::function<void()>& callback);
-
-void triggerInterrupt();
-
-/* A RAII class that causes the current thread to receive SIGUSR1 when
-   the signal handler thread receives SIGINT. That is, this allows
-   SIGINT to be multiplexed to multiple threads. */
-struct ReceiveInterrupts {
-  pthread_t target;
-  std::unique_ptr<InterruptCallback> callback;
-
-  ReceiveInterrupts()
-      : target(pthread_self()), callback(createInterruptCallback([&]() {
-          pthread_kill(target, SIGUSR1);
-        })) {}
-};
-
-/* A RAII helper that increments a counter on construction and
-   decrements it on destruction. */
-template <typename T>
-struct MaintainCount {
-  T& counter;
-  long delta;
-  explicit MaintainCount(T& counter, long delta = 1)
-      : counter(counter), delta(delta) {
-    counter += delta;
-  }
-  ~MaintainCount() { counter -= delta; }
-};
-
-/* Return the number of rows and columns of the terminal. */
-std::pair<unsigned short, unsigned short> getWindowSize();
-
-/* Used in various places. */
-using PathFilter = std::function<bool(const Path&)>;
-
-extern PathFilter defaultPathFilter;
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/visitor.hh b/third_party/nix/src/libutil/visitor.hh
deleted file mode 100644
index bf1d665af7..0000000000
--- a/third_party/nix/src/libutil/visitor.hh
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-namespace nix::util {
-
-// Helper class used for visiting std::variants by creating a variadic
-// list of lambda expressions that delegates calls to each of the
-// callables.
-//
-// See e.g.
-// https://dev.to/tmr232/that-overloaded-trick-overloading-lambdas-in-c17
-template <class... Ts>
-struct overloaded : Ts... {
-  using Ts::operator()...;
-};
-
-template <class... Ts>
-overloaded(Ts...) -> overloaded<Ts...>;
-
-}  // namespace nix::util
diff --git a/third_party/nix/src/libutil/xml-writer.cc b/third_party/nix/src/libutil/xml-writer.cc
deleted file mode 100644
index 8274ed769e..0000000000
--- a/third_party/nix/src/libutil/xml-writer.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-#include "libutil/xml-writer.hh"
-
-#include <cassert>
-
-namespace nix {
-
-XMLWriter::XMLWriter(bool indent, std::ostream& output)
-    : output(output), indent(indent) {
-  output << "<?xml version='1.0' encoding='utf-8'?>" << std::endl;
-  closed = false;
-}
-
-XMLWriter::~XMLWriter() { close(); }
-
-void XMLWriter::close() {
-  if (closed) {
-    return;
-  }
-  while (!pendingElems.empty()) {
-    closeElement();
-  }
-  closed = true;
-}
-
-void XMLWriter::indent_(size_t depth) {
-  if (!indent) {
-    return;
-  }
-  output << std::string(depth * 2, ' ');
-}
-
-void XMLWriter::openElement(const std::string& name, const XMLAttrs& attrs) {
-  assert(!closed);
-  indent_(pendingElems.size());
-  output << "<" << name;
-  writeAttrs(attrs);
-  output << ">";
-  if (indent) {
-    output << std::endl;
-  }
-  pendingElems.push_back(name);
-}
-
-void XMLWriter::closeElement() {
-  assert(!pendingElems.empty());
-  indent_(pendingElems.size() - 1);
-  output << "</" << pendingElems.back() << ">";
-  if (indent) {
-    output << std::endl;
-  }
-  pendingElems.pop_back();
-  if (pendingElems.empty()) {
-    closed = true;
-  }
-}
-
-void XMLWriter::writeEmptyElement(const std::string& name,
-                                  const XMLAttrs& attrs) {
-  assert(!closed);
-  indent_(pendingElems.size());
-  output << "<" << name;
-  writeAttrs(attrs);
-  output << " />";
-  if (indent) {
-    output << std::endl;
-  }
-}
-
-void XMLWriter::writeAttrs(const XMLAttrs& attrs) {
-  for (auto& i : attrs) {
-    output << " " << i.first << "=\"";
-    for (char c : i.second) {
-      if (c == '"') {
-        output << "&quot;";
-      } else if (c == '<') {
-        output << "&lt;";
-      } else if (c == '>') {
-        output << "&gt;";
-      } else if (c == '&') {
-        output << "&amp;";
-        /* Escape newlines to prevent attribute normalisation (see
-           XML spec, section 3.3.3. */
-      } else if (c == '\n') {
-        output << "&#xA;";
-      } else {
-        output << c;
-      }
-    }
-    output << "\"";
-  }
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/libutil/xml-writer.hh b/third_party/nix/src/libutil/xml-writer.hh
deleted file mode 100644
index d6f7cddb35..0000000000
--- a/third_party/nix/src/libutil/xml-writer.hh
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include <iostream>
-#include <list>
-#include <map>
-#include <string>
-
-namespace nix {
-
-typedef std::map<std::string, std::string> XMLAttrs;
-
-class XMLWriter {
- private:
-  std::ostream& output;
-
-  bool indent;
-  bool closed;
-
-  std::list<std::string> pendingElems;
-
- public:
-  XMLWriter(bool indent, std::ostream& output);
-  ~XMLWriter();
-
-  void close();
-
-  void openElement(const std::string& name, const XMLAttrs& attrs = XMLAttrs());
-  void closeElement();
-
-  void writeEmptyElement(const std::string& name,
-                         const XMLAttrs& attrs = XMLAttrs());
-
- private:
-  void writeAttrs(const XMLAttrs& attrs);
-
-  void indent_(size_t depth);
-};
-
-class XMLOpenElement {
- private:
-  XMLWriter& writer;
-
- public:
-  XMLOpenElement(XMLWriter& writer, const std::string& name,
-                 const XMLAttrs& attrs = XMLAttrs())
-      : writer(writer) {
-    writer.openElement(name, attrs);
-  }
-  ~XMLOpenElement() { writer.closeElement(); }
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix-build/nix-build.cc b/third_party/nix/src/nix-build/nix-build.cc
deleted file mode 100644
index 26c3089677..0000000000
--- a/third_party/nix/src/nix-build/nix-build.cc
+++ /dev/null
@@ -1,581 +0,0 @@
-#include <cstring>
-#include <fstream>
-#include <iostream>
-#include <regex>
-#include <sstream>
-#include <vector>
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/str_split.h>
-#include <glog/logging.h>
-
-#include "libexpr/attr-path.hh"
-#include "libexpr/common-eval-args.hh"
-#include "libexpr/eval-inline.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/get-drvs.hh"
-#include "libmain/shared.hh"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libutil/affinity.hh"
-#include "libutil/status.hh"
-#include "libutil/util.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-using namespace std::string_literals;
-
-/* Recreate the effect of the perl shellwords function, breaking up a
- * string into arguments like a shell word, including escapes
- */
-std::vector<std::string> shellwords(const std::string& s) {
-  std::regex whitespace("^(\\s+).*");
-  auto begin = s.cbegin();
-  std::vector<std::string> res;
-  std::string cur;
-  enum state { sBegin, sQuote };
-  state st = sBegin;
-  auto it = begin;
-  for (; it != s.cend(); ++it) {
-    if (st == sBegin) {
-      std::smatch match;
-      if (regex_search(it, s.cend(), match, whitespace)) {
-        cur.append(begin, it);
-        res.push_back(cur);
-        cur.clear();
-        it = match[1].second;
-        begin = it;
-      }
-    }
-    switch (*it) {
-      case '"':
-        cur.append(begin, it);
-        begin = it + 1;
-        st = st == sBegin ? sQuote : sBegin;
-        break;
-      case '\\':
-        /* perl shellwords mostly just treats the next char as part of the
-         * string with no special processing */
-        cur.append(begin, it);
-        begin = ++it;
-        break;
-    }
-  }
-  cur.append(begin, it);
-  if (!cur.empty()) {
-    res.push_back(cur);
-  }
-  return res;
-}
-
-static void _main(int argc, char** argv) {
-  auto dryRun = false;
-  auto runEnv = std::regex_search(argv[0], std::regex("nix-shell$"));
-  auto pure = false;
-  auto fromArgs = false;
-  auto packages = false;
-  // Same condition as bash uses for interactive shells
-  auto interactive =
-      (isatty(STDIN_FILENO) != 0) && (isatty(STDERR_FILENO) != 0);
-  Strings attrPaths;
-  Strings left;
-  RepairFlag repair = NoRepair;
-  Path gcRoot;
-  BuildMode buildMode = bmNormal;
-  bool readStdin = false;
-
-  std::string envCommand;  // interactive shell
-  Strings envExclude;
-
-  auto myName = runEnv ? "nix-shell" : "nix-build";
-
-  auto inShebang = false;
-  std::string script;
-  std::vector<std::string> savedArgs;
-
-  AutoDelete tmpDir(createTempDir("", myName));
-
-  std::string outLink = "./result";
-
-  // List of environment variables kept for --pure
-  std::set<std::string> keepVars{
-      "HOME",         "USER", "LOGNAME", "DISPLAY",         "PATH", "TERM",
-      "IN_NIX_SHELL", "TZ",   "PAGER",   "NIX_BUILD_SHELL", "SHLVL"};
-
-  Strings args;
-  for (int i = 1; i < argc; ++i) {
-    args.push_back(argv[i]);
-  }
-
-  // Heuristic to see if we're invoked as a shebang script, namely,
-  // if we have at least one argument, it's the name of an
-  // executable file, and it starts with "#!".
-  if (runEnv && argc > 1 &&
-      !std::regex_search(argv[1], std::regex("nix-shell"))) {
-    script = argv[1];
-    try {
-      Strings lines = absl::StrSplit(readFile(script), absl::ByChar('\n'),
-                                     absl::SkipEmpty());
-      if (std::regex_search(lines.front(), std::regex("^#!"))) {
-        lines.pop_front();
-        inShebang = true;
-        for (int i = 2; i < argc; ++i) {
-          savedArgs.emplace_back(argv[i]);
-        }
-        args.clear();
-        for (auto line : lines) {
-          line = absl::StripTrailingAsciiWhitespace(line);
-          std::smatch match;
-          if (std::regex_match(line, match,
-                               std::regex("^#!\\s*nix-shell (.*)$"))) {
-            for (const auto& word : shellwords(match[1].str())) {
-              args.push_back(word);
-            }
-          }
-        }
-      }
-    } catch (SysError&) {
-    }
-  }
-
-  struct MyArgs : LegacyArgs, MixEvalArgs {
-    using LegacyArgs::LegacyArgs;
-  };
-
-  MyArgs myArgs(
-      myName, [&](Strings::iterator& arg, const Strings::iterator& end) {
-        if (*arg == "--help") {
-          deletePath(Path(tmpDir));
-          showManPage(myName);
-        }
-
-        else if (*arg == "--version") {
-          printVersion(myName);
-
-        } else if (*arg == "--add-drv-link" || *arg == "--indirect") {
-          ;  // obsolete
-
-        } else if (*arg == "--no-out-link" || *arg == "--no-link") {
-          outLink = Path(tmpDir) + "/result";
-
-        } else if (*arg == "--attr" || *arg == "-A") {
-          attrPaths.push_back(getArg(*arg, arg, end));
-
-        } else if (*arg == "--drv-link") {
-          getArg(*arg, arg, end);  // obsolete
-
-        } else if (*arg == "--out-link" || *arg == "-o") {
-          outLink = getArg(*arg, arg, end);
-
-        } else if (*arg == "--add-root") {
-          gcRoot = getArg(*arg, arg, end);
-
-        } else if (*arg == "--dry-run") {
-          dryRun = true;
-
-        } else if (*arg == "--repair") {
-          repair = Repair;
-          buildMode = bmRepair;
-        }
-
-        else if (*arg == "--run-env") {  // obsolete
-          runEnv = true;
-
-        } else if (*arg == "--command" || *arg == "--run") {
-          if (*arg == "--run") {
-            interactive = false;
-          }
-          envCommand = getArg(*arg, arg, end) + "\nexit";
-        }
-
-        else if (*arg == "--check") {
-          buildMode = bmCheck;
-
-        } else if (*arg == "--exclude") {
-          envExclude.push_back(getArg(*arg, arg, end));
-
-        } else if (*arg == "--expr" || *arg == "-E") {
-          fromArgs = true;
-
-        } else if (runEnv && *arg == "--pure") {
-          pure = true;
-        } else if (runEnv && *arg == "--impure") {
-          pure = false;
-
-        } else if (*arg == "--packages" || *arg == "-p") {
-          packages = true;
-
-        } else if (inShebang && *arg == "-i") {
-          auto interpreter = getArg(*arg, arg, end);
-          interactive = false;
-          auto execArgs = "";
-
-          // Überhack to support Perl. Perl examines the shebang and
-          // executes it unless it contains the string "perl" or "indir",
-          // or (undocumented) argv[0] does not contain "perl". Exploit
-          // the latter by doing "exec -a".
-          if (std::regex_search(interpreter, std::regex("perl"))) {
-            execArgs = "-a PERL";
-          }
-
-          std::ostringstream joined;
-          for (const auto& i : savedArgs) {
-            joined << shellEscape(i) << ' ';
-          }
-
-          if (std::regex_search(interpreter, std::regex("ruby"))) {
-            // Hack for Ruby. Ruby also examines the shebang. It tries to
-            // read the shebang to understand which packages to read from. Since
-            // this is handled via nix-shell -p, we wrap our ruby script
-            // execution in ruby -e 'load' which ignores the shebangs.
-            envCommand = (format("exec %1% %2% -e 'load(\"%3%\")' -- %4%") %
-                          execArgs % interpreter % script % joined.str())
-                             .str();
-          } else {
-            envCommand = (format("exec %1% %2% %3% %4%") % execArgs %
-                          interpreter % script % joined.str())
-                             .str();
-          }
-        }
-
-        else if (*arg == "--keep") {
-          keepVars.insert(getArg(*arg, arg, end));
-
-        } else if (*arg == "-") {
-          readStdin = true;
-
-        } else if (*arg != "" && arg->at(0) == '-') {
-          return false;
-
-        } else {
-          left.push_back(*arg);
-        }
-
-        return true;
-      });
-
-  myArgs.parseCmdline(args);
-
-  if (packages && fromArgs) {
-    throw UsageError("'-p' and '-E' are mutually exclusive");
-  }
-
-  auto store = openStore();
-
-  auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
-  state->repair = repair;
-
-  std::unique_ptr<Bindings> autoArgs = myArgs.getAutoArgs(*state);
-
-  if (packages) {
-    std::ostringstream joined;
-    // TODO(grfn): Generate a syntax tree here, not a string
-    joined << "with import <nixpkgs> { }; (pkgs.runCommandCC or "
-              "pkgs.runCommand) \"shell\" { buildInputs = [ ";
-    for (const auto& i : left) {
-      joined << '(' << i << ") ";
-    }
-    joined << "]; } \"\"";
-    fromArgs = true;
-    left = {joined.str()};
-  } else if (!fromArgs) {
-    if (left.empty() && runEnv && pathExists("shell.nix")) {
-      left = {"shell.nix"};
-    }
-    if (left.empty()) {
-      left = {"default.nix"};
-    }
-  }
-
-  if (runEnv) {
-    setenv("IN_NIX_SHELL", pure ? "pure" : "impure", 1);
-  }
-
-  DrvInfos drvs;
-
-  /* Parse the expressions. */
-  std::vector<Expr*> exprs;
-
-  if (readStdin) {
-    exprs = {state->parseStdin()};
-  } else {
-    for (const auto& i : left) {
-      if (fromArgs) {
-        exprs.push_back(state->parseExprFromString(i, absPath(".")));
-      } else {
-        auto absolute = i;
-        try {
-          absolute = canonPath(absPath(i), true);
-        } catch (Error& e) {
-        };
-        if (store->isStorePath(absolute) &&
-            std::regex_match(absolute, std::regex(".*\\.drv(!.*)?"))) {
-          drvs.push_back(DrvInfo(*state, store, absolute));
-        } else {
-          /* If we're in a #! script, interpret filenames
-             relative to the script. */
-          exprs.push_back(
-              state->parseExprFromFile(resolveExprPath(state->checkSourcePath(
-                  lookupFileArg(*state, inShebang && !packages
-                                            ? absPath(i, absPath(dirOf(script)))
-                                            : i)))));
-        }
-      }
-    }
-  }
-
-  /* Evaluate them into derivations. */
-  if (attrPaths.empty()) {
-    attrPaths = {""};
-  }
-
-  for (auto e : exprs) {
-    Value vRoot;
-    state->eval(e, vRoot);
-
-    for (auto& i : attrPaths) {
-      Value& v(*findAlongAttrPath(*state, i, autoArgs.get(), vRoot));
-      state->forceValue(v);
-      getDerivations(*state, v, "", autoArgs.get(), drvs, false);
-    }
-  }
-
-  state->printStats();
-
-  auto buildPaths = [&](const PathSet& paths) {
-    /* Note: we do this even when !printMissing to efficiently
-       fetch binary cache data. */
-    unsigned long long downloadSize;
-    unsigned long long narSize;
-    PathSet willBuild;
-    PathSet willSubstitute;
-    PathSet unknown;
-    store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize,
-                        narSize);
-
-    if (settings.printMissing) {
-      printMissing(ref<Store>(store), willBuild, willSubstitute, unknown,
-                   downloadSize, narSize);
-    }
-
-    if (!dryRun) {
-      util::OkOrThrow(store->buildPaths(std::cerr, paths, buildMode));
-    }
-  };
-
-  if (runEnv) {
-    if (drvs.size() != 1) {
-      throw UsageError("nix-shell requires a single derivation");
-    }
-
-    auto& drvInfo = drvs.front();
-    auto drv = store->derivationFromPath(drvInfo.queryDrvPath());
-
-    PathSet pathsToBuild;
-
-    /* Figure out what bash shell to use. If $NIX_BUILD_SHELL
-       is not set, then build bashInteractive from
-       <nixpkgs>. */
-    auto opt_shell = getEnv("NIX_BUILD_SHELL");
-    std::string shell;
-
-    if (opt_shell.has_value()) {
-      shell = opt_shell.value();
-    } else {
-      try {
-        auto expr = state->parseExprFromString(
-            "(import <nixpkgs> {}).bashInteractive", absPath("."));
-
-        Value v;
-        state->eval(expr, v);
-
-        auto drv = getDerivation(*state, v, false);
-        if (!drv) {
-          throw Error(
-              "the 'bashInteractive' attribute in <nixpkgs> did not evaluate "
-              "to a derivation");
-        }
-
-        pathsToBuild.insert(drv->queryDrvPath());
-
-        shell = drv->queryOutPath() + "/bin/bash";
-
-      } catch (Error& e) {
-        LOG(WARNING) << e.what() << "; will use bash from your environment";
-        shell = "bash";
-      }
-    }
-
-    // Build or fetch all dependencies of the derivation.
-    for (const auto& input : drv.inputDrvs) {
-      if (std::all_of(envExclude.cbegin(), envExclude.cend(),
-                      [&](const std::string& exclude) {
-                        return !std::regex_search(input.first,
-                                                  std::regex(exclude));
-                      })) {
-        pathsToBuild.insert(makeDrvPathWithOutputs(input.first, input.second));
-      }
-    }
-    for (const auto& src : drv.inputSrcs) {
-      pathsToBuild.insert(src);
-    }
-
-    buildPaths(pathsToBuild);
-
-    if (dryRun) {
-      return;
-    }
-
-    // Set the environment.
-    auto env = getEnv();
-
-    auto tmp =
-        getEnv("TMPDIR").value_or(getEnv("XDG_RUNTIME_DIR").value_or("/tmp"));
-
-    if (pure) {
-      decltype(env) newEnv;
-      for (auto& i : env) {
-        if (keepVars.count(i.first) != 0u) {
-          newEnv.emplace(i);
-        }
-      }
-      env = newEnv;
-      // NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
-      env["__ETC_PROFILE_SOURCED"] = "1";
-    }
-
-    env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] =
-        env["TEMP"] = tmp;
-    env["NIX_STORE"] = store->storeDir;
-    env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores);
-
-    StringSet passAsFile =
-        absl::StrSplit(get(drv.env, "passAsFile", ""),
-                       absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-
-    bool keepTmp = false;
-    int fileNr = 0;
-
-    for (auto& var : drv.env) {
-      if (passAsFile.count(var.first) != 0u) {
-        keepTmp = true;
-        std::string fn = ".attr-" + std::to_string(fileNr++);
-        Path p = Path(tmpDir) + "/" + fn;
-        writeFile(p, var.second);
-        env[var.first + "Path"] = p;
-      } else {
-        env[var.first] = var.second;
-      }
-    }
-
-    restoreAffinity();
-
-    /* Run a shell using the derivation's environment.  For
-       convenience, source $stdenv/setup to setup additional
-       environment variables and shell functions.  Also don't
-       lose the current $PATH directories. */
-    auto rcfile = Path(tmpDir) + "/rc";
-    writeFile(
-        rcfile,
-        fmt((keepTmp ? "" : "rm -rf '%1%'; "s) +
-                "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc; "
-                "%2%"
-                "dontAddDisableDepTrack=1; "
-                "[ -e $stdenv/setup ] && source $stdenv/setup; "
-                "%3%"
-                "PATH=\"%4%:$PATH\"; "
-                "SHELL=%5%; "
-                "set +e; "
-                R"s([ -n "$PS1" ] && PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s"
-                "if [ \"$(type -t runHook)\" = function ]; then runHook "
-                "shellHook; fi; "
-                "unset NIX_ENFORCE_PURITY; "
-                "shopt -u nullglob; "
-                "unset TZ; %6%"
-                "%7%",
-            Path(tmpDir), (pure ? "" : "p=$PATH; "),
-            (pure ? "" : "PATH=$PATH:$p; unset p; "), dirOf(shell), shell,
-            (getenv("TZ") != nullptr
-                 ? (std::string("export TZ='") + getenv("TZ") + "'; ")
-                 : ""),
-            envCommand));
-
-    Strings envStrs;
-    for (auto& i : env) {
-      envStrs.push_back(i.first + "=" + i.second);
-    }
-
-    auto args = interactive ? Strings{"bash", "--rcfile", rcfile}
-                            : Strings{"bash", rcfile};
-
-    auto envPtrs = stringsToCharPtrs(envStrs);
-
-    environ = envPtrs.data();
-
-    auto argPtrs = stringsToCharPtrs(args);
-
-    restoreSignals();
-
-    execvp(shell.c_str(), argPtrs.data());
-
-    throw SysError("executing shell '%s'", shell);
-  }
-
-  PathSet pathsToBuild;
-
-  std::map<Path, Path> drvPrefixes;
-  std::map<Path, Path> resultSymlinks;
-  std::vector<Path> outPaths;
-
-  for (auto& drvInfo : drvs) {
-    auto drvPath = drvInfo.queryDrvPath();
-    auto outPath = drvInfo.queryOutPath();
-
-    auto outputName = drvInfo.queryOutputName();
-    if (outputName.empty()) {
-      throw Error("derivation '%s' lacks an 'outputName' attribute", drvPath);
-    }
-
-    pathsToBuild.insert(drvPath + "!" + outputName);
-
-    std::string drvPrefix;
-    auto i = drvPrefixes.find(drvPath);
-    if (i != drvPrefixes.end()) {
-      drvPrefix = i->second;
-    } else {
-      drvPrefix = outLink;
-      if (!drvPrefixes.empty() != 0u) {
-        drvPrefix += fmt("-%d", drvPrefixes.size() + 1);
-      }
-      drvPrefixes[drvPath] = drvPrefix;
-    }
-
-    std::string symlink = drvPrefix;
-    if (outputName != "out") {
-      symlink += "-" + outputName;
-    }
-
-    resultSymlinks[symlink] = outPath;
-    outPaths.push_back(outPath);
-  }
-
-  buildPaths(pathsToBuild);
-
-  if (dryRun) {
-    return;
-  }
-
-  for (auto& symlink : resultSymlinks) {
-    if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) {
-      store2->addPermRoot(symlink.second, absPath(symlink.first), true);
-    }
-  }
-
-  for (auto& path : outPaths) {
-    std::cout << path << '\n';
-  }
-}
-
-static RegisterLegacyCommand s1("nix-build", _main);
-static RegisterLegacyCommand s2("nix-shell", _main);
diff --git a/third_party/nix/src/nix-channel/nix-channel.cc b/third_party/nix/src/nix-channel/nix-channel.cc
deleted file mode 100644
index 5cc16d1b4b..0000000000
--- a/third_party/nix/src/nix-channel/nix-channel.cc
+++ /dev/null
@@ -1,275 +0,0 @@
-#include <regex>
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/str_split.h>
-#include <fcntl.h>
-#include <pwd.h>
-
-#include "libmain/shared.hh"
-#include "libstore/download.hh"
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-
-typedef std::map<std::string, std::string> Channels;
-
-static Channels channels;
-static Path channelsList;
-
-// Reads the list of channels.
-static void readChannels() {
-  if (!pathExists(channelsList)) {
-    return;
-  }
-  auto channelsFile = readFile(channelsList);
-
-  std::vector<std::string> lines =
-      absl::StrSplit(channelsFile, absl::ByChar('\n'), absl::SkipEmpty());
-
-  for (auto& line : lines) {
-    line = absl::StripTrailingAsciiWhitespace(line);
-    if (std::regex_search(line, std::regex("^\\s*\\#"))) {
-      continue;
-    }
-    std::vector<std::string> split =
-        absl::StrSplit(line, absl::ByChar(' '), absl::SkipEmpty());
-    auto url = std::regex_replace(split[0], std::regex("/*$"), "");
-    auto name = split.size() > 1 ? split[1] : baseNameOf(url);
-    channels[name] = url;
-  }
-}
-
-// Writes the list of channels.
-static void writeChannels() {
-  auto channelsFD = AutoCloseFD{open(
-      channelsList.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, 0644)};
-  if (!channelsFD) {
-    throw SysError(format("opening '%1%' for writing") % channelsList);
-  }
-  for (const auto& channel : channels) {
-    writeFull(channelsFD.get(), channel.second + " " + channel.first + "\n");
-  }
-}
-
-// Adds a channel.
-static void addChannel(const std::string& url, const std::string& name) {
-  if (!regex_search(url, std::regex("^(file|http|https)://"))) {
-    throw Error(format("invalid channel URL '%1%'") % url);
-  }
-  if (!regex_search(name, std::regex("^[a-zA-Z0-9_][a-zA-Z0-9_\\.-]*$"))) {
-    throw Error(format("invalid channel identifier '%1%'") % name);
-  }
-  readChannels();
-  channels[name] = url;
-  writeChannels();
-}
-
-static Path profile;
-
-// Remove a channel.
-static void removeChannel(const std::string& name) {
-  readChannels();
-  channels.erase(name);
-  writeChannels();
-
-  runProgram(settings.nixBinDir + "/nix-env", true,
-             {"--profile", profile, "--uninstall", name});
-}
-
-static Path nixDefExpr;
-
-// Fetch Nix expressions and binary cache URLs from the subscribed channels.
-static void update(const StringSet& channelNames) {
-  readChannels();
-
-  auto store = openStore();
-
-  // Download each channel.
-  Strings exprs;
-  for (const auto& channel : channels) {
-    auto name = channel.first;
-    auto url = channel.second;
-    if (!(channelNames.empty() || (channelNames.count(name) != 0u))) {
-      continue;
-    }
-
-    // We want to download the url to a file to see if it's a tarball while also
-    // checking if we got redirected in the process, so that we can grab the
-    // various parts of a nix channel definition from a consistent location if
-    // the redirect changes mid-download.
-    CachedDownloadRequest request(url);
-    request.ttl = 0;
-    auto dl = getDownloader();
-    auto result = dl->downloadCached(store, request);
-    auto filename = result.path;
-    url = absl::StripTrailingAsciiWhitespace(result.effectiveUri);
-
-    // If the URL contains a version number, append it to the name
-    // attribute (so that "nix-env -q" on the channels profile
-    // shows something useful).
-    auto cname = name;
-    std::smatch match;
-    auto urlBase = baseNameOf(url);
-    if (std::regex_search(urlBase, match, std::regex("(-\\d.*)$"))) {
-      cname = cname + std::string(match[1]);
-    }
-
-    std::string extraAttrs;
-
-    bool unpacked = false;
-    if (std::regex_search(filename, std::regex("\\.tar\\.(gz|bz2|xz)$"))) {
-      runProgram(settings.nixBinDir + "/nix-build", false,
-                 {"--no-out-link", "--expr",
-                  "import <nix/unpack-channel.nix> "
-                  "{ name = \"" +
-                      cname + "\"; channelName = \"" + name +
-                      "\"; src = builtins.storePath \"" + filename + "\"; }"});
-      unpacked = true;
-    }
-
-    if (!unpacked) {
-      // Download the channel tarball.
-      try {
-        filename = dl->downloadCached(
-                         store, CachedDownloadRequest(url + "/nixexprs.tar.xz"))
-                       .path;
-      } catch (DownloadError& e) {
-        filename =
-            dl->downloadCached(store,
-                               CachedDownloadRequest(url + "/nixexprs.tar.bz2"))
-                .path;
-      }
-      filename = absl::StripTrailingAsciiWhitespace(filename);
-    }
-
-    // Regardless of where it came from, add the expression representing this
-    // channel to accumulated expression
-    exprs.push_back("f: f { name = \"" + cname + "\"; channelName = \"" + name +
-                    "\"; src = builtins.storePath \"" + filename + "\"; " +
-                    extraAttrs + " }");
-  }
-
-  // Unpack the channel tarballs into the Nix store and install them
-  // into the channels profile.
-  std::cerr << "unpacking channels...\n";
-  Strings envArgs{"--profile", profile,
-                  "--file",    "<nix/unpack-channel.nix>",
-                  "--install", "--from-expression"};
-  for (auto& expr : exprs) {
-    envArgs.push_back(std::move(expr));
-  }
-  envArgs.push_back("--quiet");
-  runProgram(settings.nixBinDir + "/nix-env", false, envArgs);
-
-  // Make the channels appear in nix-env.
-  struct stat st;
-  if (lstat(nixDefExpr.c_str(), &st) == 0) {
-    if (S_ISLNK(st.st_mode)) {
-      // old-skool ~/.nix-defexpr
-      if (unlink(nixDefExpr.c_str()) == -1) {
-        throw SysError(format("unlinking %1%") % nixDefExpr);
-      }
-    }
-  } else if (errno != ENOENT) {
-    throw SysError(format("getting status of %1%") % nixDefExpr);
-  }
-  createDirs(nixDefExpr);
-  auto channelLink = nixDefExpr + "/channels";
-  replaceSymlink(profile, channelLink);
-}
-
-static int _main(int argc, char** argv) {
-  {
-    // Figure out the name of the `.nix-channels' file to use
-    auto home = getHome();
-    channelsList = home + "/.nix-channels";
-    nixDefExpr = home + "/.nix-defexpr";
-
-    // Figure out the name of the channels profile.
-    profile = fmt("%s/profiles/per-user/%s/channels", settings.nixStateDir,
-                  getUserName());
-
-    enum { cNone, cAdd, cRemove, cList, cUpdate, cRollback } cmd = cNone;
-    std::vector<std::string> args;
-    parseCmdLine(argc, argv,
-                 [&](Strings::iterator& arg, const Strings::iterator& end) {
-                   if (*arg == "--help") {
-                     showManPage("nix-channel");
-                   } else if (*arg == "--version") {
-                     printVersion("nix-channel");
-                   } else if (*arg == "--add") {
-                     cmd = cAdd;
-                   } else if (*arg == "--remove") {
-                     cmd = cRemove;
-                   } else if (*arg == "--list") {
-                     cmd = cList;
-                   } else if (*arg == "--update") {
-                     cmd = cUpdate;
-                   } else if (*arg == "--rollback") {
-                     cmd = cRollback;
-                   } else {
-                     args.push_back(std::move(*arg));
-                   }
-                   return true;
-                 });
-
-    switch (cmd) {
-      case cNone:
-        throw UsageError("no command specified");
-      case cAdd:
-        if (args.empty() || args.size() > 2) {
-          throw UsageError("'--add' requires one or two arguments");
-        }
-        {
-          auto url = args[0];
-          std::string name;
-          if (args.size() == 2) {
-            name = args[1];
-          } else {
-            name = baseNameOf(url);
-            name = std::regex_replace(name, std::regex("-unstable$"), "");
-            name = std::regex_replace(name, std::regex("-stable$"), "");
-          }
-          addChannel(url, name);
-        }
-        break;
-      case cRemove:
-        if (args.size() != 1) {
-          throw UsageError("'--remove' requires one argument");
-        }
-        removeChannel(args[0]);
-        break;
-      case cList:
-        if (!args.empty()) {
-          throw UsageError("'--list' expects no arguments");
-        }
-        readChannels();
-        for (const auto& channel : channels) {
-          std::cout << channel.first << ' ' << channel.second << '\n';
-        }
-        break;
-      case cUpdate:
-        update(StringSet(args.begin(), args.end()));
-        break;
-      case cRollback:
-        if (args.size() > 1) {
-          throw UsageError("'--rollback' has at most one argument");
-        }
-        Strings envArgs{"--profile", profile};
-        if (args.size() == 1) {
-          envArgs.push_back("--switch-generation");
-          envArgs.push_back(args[0]);
-        } else {
-          envArgs.push_back("--rollback");
-        }
-        runProgram(settings.nixBinDir + "/nix-env", false, envArgs);
-        break;
-    }
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("nix-channel", _main);
diff --git a/third_party/nix/src/nix-collect-garbage/nix-collect-garbage.cc b/third_party/nix/src/nix-collect-garbage/nix-collect-garbage.cc
deleted file mode 100644
index ac8c7d9399..0000000000
--- a/third_party/nix/src/nix-collect-garbage/nix-collect-garbage.cc
+++ /dev/null
@@ -1,103 +0,0 @@
-#include <cerrno>
-#include <iostream>
-
-#include <glog/logging.h>
-
-#include "libmain/shared.hh"
-#include "libstore/globals.hh"
-#include "libstore/profiles.hh"
-#include "libstore/store-api.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-
-std::string deleteOlderThan;
-bool dryRun = false;
-
-/* If `-d' was specified, remove all old generations of all profiles.
- * Of course, this makes rollbacks to before this point in time
- * impossible. */
-
-void removeOldGenerations(const std::string& dir) {
-  if (access(dir.c_str(), R_OK) != 0) {
-    return;
-  }
-
-  bool canWrite = access(dir.c_str(), W_OK) == 0;
-
-  for (auto& i : readDirectory(dir)) {
-    checkInterrupt();
-
-    auto path = dir + "/" + i.name;
-    auto type = i.type == DT_UNKNOWN ? getFileType(path) : i.type;
-
-    if (type == DT_LNK && canWrite) {
-      std::string link;
-      try {
-        link = readLink(path);
-      } catch (SysError& e) {
-        if (e.errNo == ENOENT) {
-          continue;
-        }
-      }
-      if (link.find("link") != std::string::npos) {
-        LOG(INFO) << "removing old generations of profile " << path;
-        if (!deleteOlderThan.empty()) {
-          deleteGenerationsOlderThan(path, deleteOlderThan, dryRun);
-        } else {
-          deleteOldGenerations(path, dryRun);
-        }
-      }
-    } else if (type == DT_DIR) {
-      removeOldGenerations(path);
-    }
-  }
-}
-
-static int _main(int argc, char** argv) {
-  {
-    bool removeOld = false;
-
-    GCOptions options;
-
-    parseCmdLine(argc, argv,
-                 [&](Strings::iterator& arg, const Strings::iterator& end) {
-                   if (*arg == "--help") {
-                     showManPage("nix-collect-garbage");
-                   } else if (*arg == "--version") {
-                     printVersion("nix-collect-garbage");
-                   } else if (*arg == "--delete-old" || *arg == "-d") {
-                     removeOld = true;
-                   } else if (*arg == "--delete-older-than") {
-                     removeOld = true;
-                     deleteOlderThan = getArg(*arg, arg, end);
-                   } else if (*arg == "--dry-run") {
-                     dryRun = true;
-                   } else if (*arg == "--max-freed") {
-                     auto maxFreed = getIntArg<long long>(*arg, arg, end, true);
-                     options.maxFreed = maxFreed >= 0 ? maxFreed : 0;
-                   } else {
-                     return false;
-                   }
-                   return true;
-                 });
-
-    auto profilesDir = settings.nixStateDir + "/profiles";
-    if (removeOld) {
-      removeOldGenerations(profilesDir);
-    }
-
-    // Run the actual garbage collector.
-    if (!dryRun) {
-      auto store = openStore();
-      options.action = GCOptions::gcDeleteDead;
-      GCResults results;
-      PrintFreed freed(true, results);
-      store->collectGarbage(options, results);
-    }
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("nix-collect-garbage", _main);
diff --git a/third_party/nix/src/nix-copy-closure/nix-copy-closure.cc b/third_party/nix/src/nix-copy-closure/nix-copy-closure.cc
deleted file mode 100644
index 3dbe29f224..0000000000
--- a/third_party/nix/src/nix-copy-closure/nix-copy-closure.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-#include <glog/logging.h>
-
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-
-static int _main(int argc, char** argv) {
-  {
-    auto gzip = false;
-    auto toMode = true;
-    auto includeOutputs = false;
-    auto dryRun = false;
-    auto useSubstitutes = NoSubstitute;
-    std::string sshHost;
-    PathSet storePaths;
-
-    parseCmdLine(
-        argc, argv, [&](Strings::iterator& arg, const Strings::iterator& end) {
-          if (*arg == "--help") {
-            showManPage("nix-copy-closure");
-          } else if (*arg == "--version") {
-            printVersion("nix-copy-closure");
-          } else if (*arg == "--gzip" || *arg == "--bzip2" || *arg == "--xz") {
-            if (*arg != "--gzip") {
-              LOG(WARNING) << "'" << *arg
-                           << "' is not implemented, falling back to gzip";
-            }
-            gzip = true;
-          } else if (*arg == "--from") {
-            toMode = false;
-          } else if (*arg == "--to") {
-            toMode = true;
-          } else if (*arg == "--include-outputs") {
-            includeOutputs = true;
-          } else if (*arg == "--show-progress") {
-            LOG(WARNING) << "'--show-progress' is not implemented";
-          } else if (*arg == "--dry-run") {
-            dryRun = true;
-          } else if (*arg == "--use-substitutes" || *arg == "-s") {
-            useSubstitutes = Substitute;
-          } else if (sshHost.empty()) {
-            sshHost = *arg;
-          } else {
-            storePaths.insert(*arg);
-          }
-          return true;
-        });
-
-    if (sshHost.empty()) {
-      throw UsageError("no host name specified");
-    }
-
-    auto remoteUri = "ssh://" + sshHost + (gzip ? "?compress=true" : "");
-    auto to = toMode ? openStore(remoteUri) : openStore();
-    auto from = toMode ? openStore() : openStore(remoteUri);
-
-    PathSet storePaths2;
-    for (auto& path : storePaths) {
-      storePaths2.insert(from->followLinksToStorePath(path));
-    }
-
-    PathSet closure;
-    from->computeFSClosure(storePaths2, closure, false, includeOutputs);
-
-    copyPaths(from, to, closure, NoRepair, NoCheckSigs, useSubstitutes);
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("nix-copy-closure", _main);
diff --git a/third_party/nix/src/nix-daemon/CMakeLists.txt b/third_party/nix/src/nix-daemon/CMakeLists.txt
deleted file mode 100644
index 63125a9b26..0000000000
--- a/third_party/nix/src/nix-daemon/CMakeLists.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- mode: cmake; -*-
-
-# The nix-daemon is the binary running the gRPC server component to
-# which other components of Nix talk to perform store and builder
-# operations.
-
-add_executable(nix-daemon)
-include_directories(${PROJECT_BINARY_DIR}) # for config.h
-set_property(TARGET nix-daemon PROPERTY CXX_STANDARD 17)
-
-pkg_check_modules(systemd REQUIRED)
-
-target_sources(nix-daemon
-  PRIVATE
-    nix-daemon-proto.hh
-    nix-daemon-proto.cc
-    nix-daemon.cc
-)
-
-target_link_libraries(nix-daemon
-  nixutil
-  nixstore
-  nixmain
-  absl::flags
-  absl::flags_parse
-  systemd
-)
-
-install(TARGETS nix-daemon DESTINATION bin)
diff --git a/third_party/nix/src/nix-daemon/nix-daemon-legacy.cc b/third_party/nix/src/nix-daemon/nix-daemon-legacy.cc
deleted file mode 100644
index 97cf5195d3..0000000000
--- a/third_party/nix/src/nix-daemon/nix-daemon-legacy.cc
+++ /dev/null
@@ -1,1185 +0,0 @@
-/*
-  NOTE: You are looking at the *previous* implementation of the Nix
-  daemon. This file is not in use, is only left in here for reference
-  and will be deleted from the codebase eventually.
- */
-
-#include <algorithm>
-#include <cerrno>
-#include <climits>
-#include <csignal>
-#include <cstring>
-
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "libmain/shared.hh"
-#include "libproto/worker.pb.h"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/local-store.hh"
-#include "libstore/worker-protocol.hh"
-#include "libutil/affinity.hh"
-#include "libutil/archive.hh"
-#include "libutil/finally.hh"
-#include "libutil/monitor-fd.hh"
-#include "libutil/serialise.hh"
-#include "libutil/util.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-
-#ifndef __linux__
-#define SPLICE_F_MOVE 0
-static ssize_t splice(int fd_in, void* off_in, int fd_out, void* off_out,
-                      size_t len, unsigned int flags) {
-  /* We ignore most parameters, we just have them for conformance with the linux
-   * syscall */
-  std::vector<char> buf(8192);
-  auto read_count = read(fd_in, buf.data(), buf.size());
-  if (read_count == -1) {
-    return read_count;
-  }
-  auto write_count = decltype(read_count)(0);
-  while (write_count < read_count) {
-    auto res =
-        write(fd_out, buf.data() + write_count, read_count - write_count);
-    if (res == -1) {
-      return res;
-    }
-    write_count += res;
-  }
-  return read_count;
-}
-#endif
-
-static FdSource from(STDIN_FILENO);
-static FdSink to(STDOUT_FILENO);
-
-/* Logger that forwards log messages to the client, *if* we're in a
-   state where the protocol allows it (i.e., when canSendStderr is
-   true). */
-struct TunnelLogger {
-  struct State {
-    bool canSendStderr = false;
-    std::vector<std::string> pendingMsgs;
-  };
-
-  Sync<State> state_;
-
-  unsigned int clientVersion;
-
-  explicit TunnelLogger(unsigned int clientVersion)
-      : clientVersion(clientVersion) {}
-
-  void enqueueMsg(const std::string& s) {
-    auto state(state_.lock());
-
-    if (state->canSendStderr) {
-      assert(state->pendingMsgs.empty());
-      try {
-        to(s);
-        to.flush();
-      } catch (...) {
-        /* Write failed; that means that the other side is
-           gone. */
-        state->canSendStderr = false;
-        throw;
-      }
-    } else {
-      state->pendingMsgs.push_back(s);
-    }
-  }
-
-  void log(const FormatOrString& fs) {
-    StringSink buf;
-    buf << STDERR_NEXT << (fs.s + "\n");
-    enqueueMsg(*buf.s);
-  }
-
-  /* startWork() means that we're starting an operation for which we
-    want to send out stderr to the client. */
-  void startWork() {
-    auto state(state_.lock());
-    state->canSendStderr = true;
-
-    for (auto& msg : state->pendingMsgs) {
-      to(msg);
-    }
-
-    state->pendingMsgs.clear();
-
-    to.flush();
-  }
-
-  /* stopWork() means that we're done; stop sending stderr to the
-     client. */
-  void stopWork(bool success = true, const std::string& msg = "",
-                unsigned int status = 0) {
-    auto state(state_.lock());
-
-    state->canSendStderr = false;
-
-    if (success) {
-      to << STDERR_LAST;
-    } else {
-      to << STDERR_ERROR << msg;
-      if (status != 0) {
-        to << status;
-      }
-    }
-  }
-
-  void startActivity(const std::string& s) {
-    DLOG(INFO) << "startActivity(" << s << ")";
-    if (GET_PROTOCOL_MINOR(clientVersion) < 20) {
-      if (!s.empty()) {
-        LOG(INFO) << s;
-      }
-      return;
-    }
-
-    StringSink buf;
-    buf << STDERR_START_ACTIVITY << s;
-    enqueueMsg(*buf.s);
-  }
-};
-
-struct TunnelSink : Sink {
-  Sink& to;
-  explicit TunnelSink(Sink& to) : to(to) {}
-  void operator()(const unsigned char* data, size_t len) override {
-    to << STDERR_WRITE;
-    writeString(data, len, to);
-  }
-};
-
-struct TunnelSource : BufferedSource {
-  Source& from;
-  explicit TunnelSource(Source& from) : from(from) {}
-
- protected:
-  size_t readUnbuffered(unsigned char* data, size_t len) override {
-    to << STDERR_READ << len;
-    to.flush();
-    size_t n = readString(data, len, from);
-    if (n == 0) {
-      throw EndOfFile("unexpected end-of-file");
-    }
-    return n;
-  }
-};
-
-/* If the NAR archive contains a single file at top-level, then save
-   the contents of the file to `s'.  Otherwise barf. */
-struct RetrieveRegularNARSink : ParseSink {
-  bool regular{true};
-  std::string s;
-
-  RetrieveRegularNARSink() {}
-
-  void createDirectory(const Path& path) override { regular = false; }
-
-  void receiveContents(unsigned char* data, unsigned int len) override {
-    s.append((const char*)data, len);
-  }
-
-  void createSymlink(const Path& path, const std::string& target) override {
-    regular = false;
-  }
-};
-
-static void performOp(TunnelLogger* logger, const ref<Store>& store,
-                      bool trusted, unsigned int clientVersion, Source& from,
-                      Sink& to, unsigned int op) {
-  switch (op) {
-    case wopIsValidPath: {
-      /* 'readStorePath' could raise an error leading to the connection
-         being closed.  To be able to recover from an invalid path error,
-         call 'startWork' early, and do 'assertStorePath' afterwards so
-         that the 'Error' exception handler doesn't close the
-         connection.  */
-      Path path = readString(from);
-      logger->startWork();
-      store->assertStorePath(path);
-      bool result = store->isValidPath(path);
-      logger->stopWork();
-      to << static_cast<uint64_t>(result);
-      break;
-    }
-
-    case wopQueryValidPaths: {
-      auto paths = readStorePaths<PathSet>(*store, from);
-      logger->startWork();
-      PathSet res = store->queryValidPaths(paths);
-      logger->stopWork();
-      to << res;
-      break;
-    }
-
-    case wopHasSubstitutes: {
-      Path path = readStorePath(*store, from);
-      logger->startWork();
-      PathSet res = store->querySubstitutablePaths({path});
-      logger->stopWork();
-      to << static_cast<uint64_t>(res.find(path) != res.end());
-      break;
-    }
-
-    case wopQuerySubstitutablePaths: {
-      auto paths = readStorePaths<PathSet>(*store, from);
-      logger->startWork();
-      PathSet res = store->querySubstitutablePaths(paths);
-      logger->stopWork();
-      to << res;
-      break;
-    }
-
-    case wopQueryPathHash: {
-      Path path = readStorePath(*store, from);
-      logger->startWork();
-      auto hash = store->queryPathInfo(path)->narHash;
-      logger->stopWork();
-      to << hash.to_string(Base16, false);
-      break;
-    }
-
-    case wopQueryReferences:
-    case wopQueryReferrers:
-    case wopQueryValidDerivers:
-    case wopQueryDerivationOutputs: {
-      Path path = readStorePath(*store, from);
-      logger->startWork();
-      PathSet paths;
-      if (op == wopQueryReferences) {
-        paths = store->queryPathInfo(path)->references;
-      } else if (op == wopQueryReferrers) {
-        store->queryReferrers(path, paths);
-      } else if (op == wopQueryValidDerivers) {
-        paths = store->queryValidDerivers(path);
-      } else {
-        paths = store->queryDerivationOutputs(path);
-      }
-      logger->stopWork();
-      to << paths;
-      break;
-    }
-
-    case wopQueryDerivationOutputNames: {
-      Path path = readStorePath(*store, from);
-      logger->startWork();
-      StringSet names;
-      names = store->queryDerivationOutputNames(path);
-      logger->stopWork();
-      to << names;
-      break;
-    }
-
-    case wopQueryDeriver: {
-      Path path = readStorePath(*store, from);
-      logger->startWork();
-      auto deriver = store->queryPathInfo(path)->deriver;
-      logger->stopWork();
-      to << deriver;
-      break;
-    }
-
-    case wopQueryPathFromHashPart: {
-      std::string hashPart = readString(from);
-      logger->startWork();
-      Path path = store->queryPathFromHashPart(hashPart);
-      logger->stopWork();
-      to << path;
-      break;
-    }
-
-    case wopAddToStore: {
-      bool fixed = false;
-      bool recursive = false;
-      std::string hashType;
-      std::string baseName;
-      from >> baseName >> fixed /* obsolete */ >> recursive >> hashType;
-      /* Compatibility hack. */
-      if (!fixed) {
-        hashType = "sha256";
-        recursive = true;
-      }
-      HashType hashAlgo = parseHashType(hashType);
-
-      TeeSource savedNAR(from);
-      RetrieveRegularNARSink savedRegular;
-
-      if (recursive) {
-        /* Get the entire NAR dump from the client and save it to
-           a string so that we can pass it to
-           addToStoreFromDump(). */
-        ParseSink sink; /* null sink; just parse the NAR */
-        parseDump(sink, savedNAR);
-      } else {
-        parseDump(savedRegular, from);
-      }
-
-      logger->startWork();
-      if (!savedRegular.regular) {
-        throw Error("regular file expected");
-      }
-
-      auto store2 = store.dynamic_pointer_cast<LocalStore>();
-      if (!store2) {
-        throw Error("operation is only supported by LocalStore");
-      }
-
-      Path path = store2->addToStoreFromDump(
-          recursive ? *savedNAR.data : savedRegular.s, baseName, recursive,
-          hashAlgo);
-      logger->stopWork();
-
-      to << path;
-      break;
-    }
-
-    case wopAddTextToStore: {
-      std::string suffix = readString(from);
-      std::string s = readString(from);
-      auto refs = readStorePaths<PathSet>(*store, from);
-      logger->startWork();
-      Path path = store->addTextToStore(suffix, s, refs, NoRepair);
-      logger->stopWork();
-      to << path;
-      break;
-    }
-
-    case wopExportPath: {
-      Path path = readStorePath(*store, from);
-      readInt(from);  // obsolete
-      logger->startWork();
-      TunnelSink sink(to);
-      store->exportPath(path, sink);
-      logger->stopWork();
-      to << 1;
-      break;
-    }
-
-    case wopImportPaths: {
-      logger->startWork();
-      TunnelSource source(from);
-      Paths paths = store->importPaths(source, nullptr,
-                                       trusted ? NoCheckSigs : CheckSigs);
-      logger->stopWork();
-      to << paths;
-      break;
-    }
-
-    case wopBuildPaths: {
-      auto drvs = readStorePaths<PathSet>(*store, from);
-      BuildMode mode = bmNormal;
-      if (GET_PROTOCOL_MINOR(clientVersion) >= 15) {
-        mode = (BuildMode)readInt(from);
-
-        /* Repairing is not atomic, so disallowed for "untrusted"
-           clients.  */
-        if (mode == bmRepair && !trusted) {
-          throw Error(
-              "repairing is not allowed because you are not in "
-              "'trusted-users'");
-        }
-      }
-      logger->startWork();
-      store->buildPaths(drvs, mode);
-      logger->stopWork();
-      to << 1;
-      break;
-    }
-
-    case wopBuildDerivation: {
-      Path drvPath = readStorePath(*store, from);
-      BasicDerivation drv;
-      readDerivation(from, *store, drv);
-      auto buildMode = (BuildMode)readInt(from);
-      logger->startWork();
-      if (!trusted) {
-        throw Error("you are not privileged to build derivations");
-      }
-      auto res = store->buildDerivation(drvPath, drv, buildMode);
-      logger->stopWork();
-      to << res.status << res.errorMsg;
-      break;
-    }
-
-    case wopEnsurePath: {
-      Path path = readStorePath(*store, from);
-      logger->startWork();
-      store->ensurePath(path);
-      logger->stopWork();
-      to << 1;
-      break;
-    }
-
-    case wopAddTempRoot: {
-      Path path = readStorePath(*store, from);
-      logger->startWork();
-      store->addTempRoot(path);
-      logger->stopWork();
-      to << 1;
-      break;
-    }
-
-    case wopAddIndirectRoot: {
-      Path path = absPath(readString(from));
-      logger->startWork();
-      store->addIndirectRoot(path);
-      logger->stopWork();
-      to << 1;
-      break;
-    }
-
-    case wopSyncWithGC: {
-      logger->startWork();
-      store->syncWithGC();
-      logger->stopWork();
-      to << 1;
-      break;
-    }
-
-    case wopFindRoots: {
-      logger->startWork();
-      Roots roots = store->findRoots(!trusted);
-      logger->stopWork();
-
-      size_t size = 0;
-      for (auto& i : roots) {
-        size += i.second.size();
-      }
-
-      to << size;
-
-      for (auto& [target, links] : roots) {
-        for (auto& link : links) {
-          to << link << target;
-        }
-      }
-
-      break;
-    }
-
-    case wopCollectGarbage: {
-      GCOptions options;
-      options.action = (GCOptions::GCAction)readInt(from);
-      options.pathsToDelete = readStorePaths<PathSet>(*store, from);
-      from >> options.ignoreLiveness >> options.maxFreed;
-      // obsolete fields
-      readInt(from);
-      readInt(from);
-      readInt(from);
-
-      GCResults results;
-
-      logger->startWork();
-      if (options.ignoreLiveness) {
-        throw Error("you are not allowed to ignore liveness");
-      }
-      store->collectGarbage(options, results);
-      logger->stopWork();
-
-      to << results.paths << results.bytesFreed << 0 /* obsolete */;
-
-      break;
-    }
-
-    case wopSetOptions: {
-      settings.keepFailed = readInt(from) != 0u;
-      settings.keepGoing = readInt(from) != 0u;
-      settings.tryFallback = readInt(from) != 0u;
-      readInt(from);  // obsolete verbosity
-      settings.maxBuildJobs.assign(readInt(from));
-      settings.maxSilentTime = readInt(from);
-      readInt(from);  // obsolete useBuildHook
-      settings.verboseBuild = 0 == readInt(from);
-      readInt(from);  // obsolete logType
-      readInt(from);  // obsolete printBuildTrace
-      settings.buildCores = readInt(from);
-      settings.useSubstitutes = readInt(from) != 0u;
-
-      StringMap overrides;
-      if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
-        unsigned int n = readInt(from);
-        for (unsigned int i = 0; i < n; i++) {
-          std::string name = readString(from);
-          std::string value = readString(from);
-          overrides.emplace(name, value);
-        }
-      }
-
-      logger->startWork();
-
-      for (auto& i : overrides) {
-        auto& name(i.first);
-        auto& value(i.second);
-
-        auto setSubstituters = [&](Setting<Strings>& res) {
-          if (name != res.name && res.aliases.count(name) == 0) {
-            return false;
-          }
-          StringSet trusted = settings.trustedSubstituters;
-          for (auto& s : settings.substituters.get()) {
-            trusted.insert(s);
-          }
-          Strings subs;
-          Strings ss = absl::StrSplit(value, absl::ByAnyChar(" \t\n\r"),
-                                      absl::SkipEmpty());
-          for (auto& s : ss) {
-            if (trusted.count(s) != 0u) {
-              subs.push_back(s);
-            } else {
-              LOG(WARNING) << "ignoring untrusted substituter '" << s << "'";
-            }
-          }
-          res = subs;
-          return true;
-        };
-
-        try {
-          if (name == "ssh-auth-sock") {  // obsolete
-            ;
-          } else if (trusted || name == settings.buildTimeout.name ||
-                     name == "connect-timeout" ||
-                     (name == "builders" && value.empty())) {
-            settings.set(name, value);
-          } else if (setSubstituters(settings.substituters)) {
-            ;
-          } else if (setSubstituters(settings.extraSubstituters)) {
-            ;
-          } else {
-            LOG(WARNING) << "ignoring the user-specified setting '" << name
-                         << "', because it is a "
-                         << "restricted setting and you are not a trusted user";
-          }
-        } catch (UsageError& e) {
-          LOG(WARNING) << e.what();
-        }
-      }
-
-      logger->stopWork();
-      break;
-    }
-
-    case wopQuerySubstitutablePathInfo: {
-      Path path = absPath(readString(from));
-      logger->startWork();
-      SubstitutablePathInfos infos;
-      store->querySubstitutablePathInfos({path}, infos);
-      logger->stopWork();
-      auto i = infos.find(path);
-      if (i == infos.end()) {
-        to << 0;
-      } else {
-        to << 1 << i->second.deriver << i->second.references
-           << i->second.downloadSize << i->second.narSize;
-      }
-      break;
-    }
-
-    case wopQuerySubstitutablePathInfos: {
-      auto paths = readStorePaths<PathSet>(*store, from);
-      logger->startWork();
-      SubstitutablePathInfos infos;
-      store->querySubstitutablePathInfos(paths, infos);
-      logger->stopWork();
-      to << infos.size();
-      for (auto& i : infos) {
-        to << i.first << i.second.deriver << i.second.references
-           << i.second.downloadSize << i.second.narSize;
-      }
-      break;
-    }
-
-    case wopQueryAllValidPaths: {
-      logger->startWork();
-      PathSet paths = store->queryAllValidPaths();
-      logger->stopWork();
-      to << paths;
-      break;
-    }
-
-    case wopQueryPathInfo: {
-      Path path = readStorePath(*store, from);
-      std::shared_ptr<const ValidPathInfo> info;
-      logger->startWork();
-      try {
-        info = store->queryPathInfo(path);
-      } catch (InvalidPath&) {
-        if (GET_PROTOCOL_MINOR(clientVersion) < 17) {
-          throw;
-        }
-      }
-      logger->stopWork();
-      if (info) {
-        if (GET_PROTOCOL_MINOR(clientVersion) >= 17) {
-          to << 1;
-        }
-        to << info->deriver << info->narHash.to_string(Base16, false)
-           << info->references << info->registrationTime << info->narSize;
-        if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
-          to << static_cast<uint64_t>(info->ultimate) << info->sigs << info->ca;
-        }
-      } else {
-        assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
-        to << 0;
-      }
-      break;
-    }
-
-    case wopOptimiseStore: {
-      logger->startWork();
-      store->optimiseStore();
-      logger->stopWork();
-      to << 1;
-      break;
-    }
-
-    case wopVerifyStore: {
-      bool checkContents;
-      bool repair;
-      from >> checkContents >> repair;
-      logger->startWork();
-      if (repair && !trusted) {
-        throw Error("you are not privileged to repair paths");
-      }
-      bool errors = store->verifyStore(checkContents, (RepairFlag)repair);
-      logger->stopWork();
-      to << static_cast<uint64_t>(errors);
-      break;
-    }
-
-    case wopAddSignatures: {
-      Path path = readStorePath(*store, from);
-      auto sigs = readStrings<StringSet>(from);
-      logger->startWork();
-      if (!trusted) {
-        throw Error("you are not privileged to add signatures");
-      }
-      store->addSignatures(path, sigs);
-      logger->stopWork();
-      to << 1;
-      break;
-    }
-
-    case wopNarFromPath: {
-      auto path = readStorePath(*store, from);
-      logger->startWork();
-      logger->stopWork();
-      dumpPath(path, to);
-      break;
-    }
-
-    case wopAddToStoreNar: {
-      bool repair;
-      bool dontCheckSigs;
-      ValidPathInfo info;
-      info.path = readStorePath(*store, from);
-      from >> info.deriver;
-      if (!info.deriver.empty()) {
-        store->assertStorePath(info.deriver);
-      }
-      info.narHash = Hash(readString(from), htSHA256);
-      info.references = readStorePaths<PathSet>(*store, from);
-      from >> info.registrationTime >> info.narSize >> info.ultimate;
-      info.sigs = readStrings<StringSet>(from);
-      from >> info.ca >> repair >> dontCheckSigs;
-      if (!trusted && dontCheckSigs) {
-        dontCheckSigs = false;
-      }
-      if (!trusted) {
-        info.ultimate = false;
-      }
-
-      std::string saved;
-      std::unique_ptr<Source> source;
-      if (GET_PROTOCOL_MINOR(clientVersion) >= 21) {
-        source = std::make_unique<TunnelSource>(from);
-      } else {
-        TeeSink tee(from);
-        parseDump(tee, tee.source);
-        saved = std::move(*tee.source.data);
-        source = std::make_unique<StringSource>(saved);
-      }
-
-      logger->startWork();
-
-      // FIXME: race if addToStore doesn't read source?
-      store->addToStore(info, *source, (RepairFlag)repair,
-                        dontCheckSigs ? NoCheckSigs : CheckSigs, nullptr);
-
-      logger->stopWork();
-      break;
-    }
-
-    case wopQueryMissing: {
-      auto targets = readStorePaths<PathSet>(*store, from);
-      logger->startWork();
-      PathSet willBuild;
-      PathSet willSubstitute;
-      PathSet unknown;
-      unsigned long long downloadSize;
-      unsigned long long narSize;
-      store->queryMissing(targets, willBuild, willSubstitute, unknown,
-                          downloadSize, narSize);
-      logger->stopWork();
-      to << willBuild << willSubstitute << unknown << downloadSize << narSize;
-      break;
-    }
-
-    default:
-      throw Error(format("invalid operation %1%") % op);
-  }
-}
-
-static void processConnection(bool trusted, const std::string& userName,
-                              uid_t userId) {
-  MonitorFdHup monitor(from.fd);
-
-  /* Exchange the greeting. */
-  unsigned int magic = readInt(from);
-  if (magic != WORKER_MAGIC_1) {
-    throw Error("protocol mismatch");
-  }
-  to << WORKER_MAGIC_2 << PROTOCOL_VERSION;
-  to.flush();
-  unsigned int clientVersion = readInt(from);
-
-  if (clientVersion < 0x10a) {
-    throw Error("the Nix client version is too old");
-  }
-
-  auto tunnelLogger = new TunnelLogger(clientVersion);
-  // logger = tunnelLogger;
-
-  unsigned int opCount = 0;
-
-  Finally finally([&]() {
-    _isInterrupted = false;
-    DLOG(INFO) << opCount << " operations";
-  });
-
-  if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && (readInt(from) != 0u)) {
-    setAffinityTo(readInt(from));
-  }
-
-  readInt(from);  // obsolete reserveSpace
-
-  /* Send startup error messages to the client. */
-  tunnelLogger->startWork();
-
-  try {
-    /* If we can't accept clientVersion, then throw an error
-     *here* (not above). */
-
-#if 0
-        /* Prevent users from doing something very dangerous. */
-        if (geteuid() == 0 &&
-            querySetting("build-users-group", "") == "")
-            throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!");
-#endif
-
-    /* Open the store. */
-    Store::Params params;  // FIXME: get params from somewhere
-    // Disable caching since the client already does that.
-    params["path-info-cache-size"] = "0";
-    auto store = openStore(settings.storeUri, params);
-
-    store->createUser(userName, userId);
-
-    tunnelLogger->stopWork();
-    to.flush();
-
-    /* Process client requests. */
-    while (true) {
-      WorkerOp op;
-      try {
-        op = (WorkerOp)readInt(from);
-      } catch (Interrupted& e) {
-        break;
-      } catch (EndOfFile& e) {
-        break;
-      }
-
-      opCount++;
-
-      try {
-        performOp(tunnelLogger, store, trusted, clientVersion, from, to, op);
-      } catch (Error& e) {
-        /* If we're not in a state where we can send replies, then
-           something went wrong processing the input of the
-           client.  This can happen especially if I/O errors occur
-           during addTextToStore() / importPath().  If that
-           happens, just send the error message and exit. */
-        bool errorAllowed = tunnelLogger->state_.lock()->canSendStderr;
-        tunnelLogger->stopWork(false, e.msg(), e.status);
-        if (!errorAllowed) {
-          throw;
-        }
-      } catch (std::bad_alloc& e) {
-        tunnelLogger->stopWork(false, "Nix daemon out of memory", 1);
-        throw;
-      }
-
-      to.flush();
-
-      assert(!tunnelLogger->state_.lock()->canSendStderr);
-    };
-
-  } catch (std::exception& e) {
-    tunnelLogger->stopWork(false, e.what(), 1);
-    to.flush();
-    return;
-  }
-}
-
-static void sigChldHandler(int sigNo) {
-  // Ensure we don't modify errno of whatever we've interrupted
-  auto saved_errno = errno;
-  /* Reap all dead children. */
-  while (waitpid(-1, nullptr, WNOHANG) > 0) {
-    ;
-  }
-  errno = saved_errno;
-}
-
-static void setSigChldAction(bool autoReap) {
-  struct sigaction act;
-  struct sigaction oact;
-  act.sa_handler = autoReap ? sigChldHandler : SIG_DFL;
-  sigfillset(&act.sa_mask);
-  act.sa_flags = 0;
-  if (sigaction(SIGCHLD, &act, &oact) != 0) {
-    throw SysError("setting SIGCHLD handler");
-  }
-}
-
-bool matchUser(const std::string& user, const std::string& group,
-               const Strings& users) {
-  if (find(users.begin(), users.end(), "*") != users.end()) {
-    return true;
-  }
-
-  if (find(users.begin(), users.end(), user) != users.end()) {
-    return true;
-  }
-
-  for (auto& i : users) {
-    if (std::string(i, 0, 1) == "@") {
-      if (group == std::string(i, 1)) {
-        return true;
-      }
-      struct group* gr = getgrnam(i.c_str() + 1);
-      if (gr == nullptr) {
-        continue;
-      }
-      for (char** mem = gr->gr_mem; *mem != nullptr; mem++) {
-        if (user == std::string(*mem)) {
-          return true;
-        }
-      }
-    }
-  }
-
-  return false;
-}
-
-struct PeerInfo {
-  bool pidKnown;
-  pid_t pid;
-  bool uidKnown;
-  uid_t uid;
-  bool gidKnown;
-  gid_t gid;
-};
-
-/* Get the identity of the caller, if possible. */
-static PeerInfo getPeerInfo(int remote) {
-  PeerInfo peer = {false, 0, false, 0, false, 0};
-
-#if defined(SO_PEERCRED)
-
-  ucred cred;
-  socklen_t credLen = sizeof(cred);
-  if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1) {
-    throw SysError("getting peer credentials");
-  }
-  peer = {true, cred.pid, true, cred.uid, true, cred.gid};
-
-#elif defined(LOCAL_PEERCRED)
-
-#if !defined(SOL_LOCAL)
-#define SOL_LOCAL 0
-#endif
-
-  xucred cred;
-  socklen_t credLen = sizeof(cred);
-  if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == -1)
-    throw SysError("getting peer credentials");
-  peer = {false, 0, true, cred.cr_uid, false, 0};
-
-#endif
-
-  return peer;
-}
-
-#define SD_LISTEN_FDS_START 3
-
-static void daemonLoop(char** argv) {
-  if (chdir("/") == -1) {
-    throw SysError("cannot change current directory");
-  }
-
-  /* Get rid of children automatically; don't let them become
-     zombies. */
-  setSigChldAction(true);
-
-  AutoCloseFD fdSocket;
-
-  /* Handle socket-based activation by systemd. */
-  if (!getEnv("LISTEN_FDS").empty()) {
-    if (getEnv("LISTEN_PID") != std::to_string(getpid()) ||
-        getEnv("LISTEN_FDS") != "1") {
-      throw Error("unexpected systemd environment variables");
-    }
-    fdSocket = SD_LISTEN_FDS_START;
-  }
-
-  /* Otherwise, create and bind to a Unix domain socket. */
-  else {
-    /* Create and bind to a Unix domain socket. */
-    fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
-    if (!fdSocket) {
-      throw SysError("cannot create Unix domain socket");
-    }
-
-    std::string socketPath = settings.nixDaemonSocketFile;
-
-    createDirs(dirOf(socketPath));
-
-    /* Urgh, sockaddr_un allows path names of only 108 characters.
-       So chdir to the socket directory so that we can pass a
-       relative path name. */
-    if (chdir(dirOf(socketPath).c_str()) == -1) {
-      throw SysError("cannot change current directory");
-    }
-    Path socketPathRel = "./" + baseNameOf(socketPath);
-
-    struct sockaddr_un addr;
-    addr.sun_family = AF_UNIX;
-    strncpy(addr.sun_path, socketPathRel.c_str(), sizeof(addr.sun_path));
-    if (addr.sun_path[sizeof(addr.sun_path) - 1] != '\0') {
-      throw Error(format("socket path '%1%' is too long") % socketPathRel);
-    }
-
-    unlink(socketPath.c_str());
-
-    /* Make sure that the socket is created with 0666 permission
-       (everybody can connect --- provided they have access to the
-       directory containing the socket). */
-    mode_t oldMode = umask(0111);
-    int res = bind(fdSocket.get(), (struct sockaddr*)&addr, sizeof(addr));
-    umask(oldMode);
-    if (res == -1) {
-      throw SysError(format("cannot bind to socket '%1%'") % socketPath);
-    }
-
-    if (chdir("/") == -1) { /* back to the root */
-      throw SysError("cannot change current directory");
-    }
-
-    if (listen(fdSocket.get(), 5) == -1) {
-      throw SysError(format("cannot listen on socket '%1%'") % socketPath);
-    }
-  }
-
-  closeOnExec(fdSocket.get());
-
-  /* Loop accepting connections. */
-  while (true) {
-    try {
-      /* Accept a connection. */
-      struct sockaddr_un remoteAddr;
-      socklen_t remoteAddrLen = sizeof(remoteAddr);
-
-      AutoCloseFD remote =
-          accept(fdSocket.get(), (struct sockaddr*)&remoteAddr, &remoteAddrLen);
-      checkInterrupt();
-      if (!remote) {
-        if (errno == EINTR) {
-          continue;
-        }
-        throw SysError("accepting connection");
-      }
-
-      closeOnExec(remote.get());
-
-      bool trusted = false;
-      PeerInfo peer = getPeerInfo(remote.get());
-
-      struct passwd* pw = peer.uidKnown ? getpwuid(peer.uid) : nullptr;
-      std::string user = pw != nullptr ? pw->pw_name : std::to_string(peer.uid);
-
-      struct group* gr = peer.gidKnown ? getgrgid(peer.gid) : nullptr;
-      std::string group =
-          gr != nullptr ? gr->gr_name : std::to_string(peer.gid);
-
-      Strings trustedUsers = settings.trustedUsers;
-      Strings allowedUsers = settings.allowedUsers;
-
-      if (matchUser(user, group, trustedUsers)) {
-        trusted = true;
-      }
-
-      if ((!trusted && !matchUser(user, group, allowedUsers)) ||
-          group == settings.buildUsersGroup) {
-        throw Error(
-            format("user '%1%' is not allowed to connect to the Nix daemon") %
-            user);
-      }
-
-      LOG(INFO) << "accepted connection from pid "
-                << (peer.pidKnown ? std::to_string(peer.pid) : "<unknown>")
-                << ", user " << (peer.uidKnown ? user : "<unknown>")
-                << (trusted ? " (trusted)" : "");
-
-      /* Fork a child to handle the connection. */
-      ProcessOptions options;
-      options.errorPrefix = "unexpected Nix daemon error: ";
-      options.dieWithParent = false;
-      options.runExitHandlers = true;
-      startProcess(
-          [&]() {
-            fdSocket = -1;
-
-            /* Background the daemon. */
-            if (setsid() == -1) {
-              throw SysError(format("creating a new session"));
-            }
-
-            /* Restore normal handling of SIGCHLD. */
-            setSigChldAction(false);
-
-            /* For debugging, stuff the pid into argv[1]. */
-            if (peer.pidKnown && (argv[1] != nullptr)) {
-              std::string processName = std::to_string(peer.pid);
-              strncpy(argv[1], processName.c_str(), strlen(argv[1]));
-            }
-
-            /* Handle the connection. */
-            from.fd = remote.get();
-            to.fd = remote.get();
-            processConnection(trusted, user, peer.uid);
-
-            exit(0);
-          },
-          options);
-
-    } catch (Interrupted& e) {
-      return;
-    } catch (Error& e) {
-      LOG(ERROR) << "error processing connection: " << e.msg();
-    }
-  }
-}
-
-static int _main(int argc, char** argv) {
-  {
-    auto stdio = false;
-
-    parseCmdLine(argc, argv,
-                 [&](Strings::iterator& arg, const Strings::iterator& end) {
-                   if (*arg == "--daemon") {
-                     ; /* ignored for backwards compatibility */
-                   } else if (*arg == "--help") {
-                     showManPage("nix-daemon");
-                   } else if (*arg == "--version") {
-                     printVersion("nix-daemon");
-                   } else if (*arg == "--stdio") {
-                     stdio = true;
-                   } else {
-                     return false;
-                   }
-                   return true;
-                 });
-
-    if (stdio) {
-      if (getStoreType() == tDaemon) {
-        /* Forward on this connection to the real daemon */
-        auto socketPath = settings.nixDaemonSocketFile;
-        auto s = socket(PF_UNIX, SOCK_STREAM, 0);
-        if (s == -1) {
-          throw SysError("creating Unix domain socket");
-        }
-
-        auto socketDir = dirOf(socketPath);
-        if (chdir(socketDir.c_str()) == -1) {
-          throw SysError(format("changing to socket directory '%1%'") %
-                         socketDir);
-        }
-
-        auto socketName = baseNameOf(socketPath);
-        auto addr = sockaddr_un{};
-        addr.sun_family = AF_UNIX;
-        strncpy(addr.sun_path, socketName.c_str(), sizeof(addr.sun_path));
-        if (addr.sun_path[sizeof(addr.sun_path) - 1] != '\0') {
-          throw Error(format("socket name %1% is too long") % socketName);
-        }
-
-        if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
-          throw SysError(format("cannot connect to daemon at %1%") %
-                         socketPath);
-        }
-
-        auto nfds = (s > STDIN_FILENO ? s : STDIN_FILENO) + 1;
-        while (true) {
-          fd_set fds;
-          FD_ZERO(&fds);
-          FD_SET(s, &fds);
-          FD_SET(STDIN_FILENO, &fds);
-          if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1) {
-            throw SysError("waiting for data from client or server");
-          }
-          if (FD_ISSET(s, &fds)) {
-            auto res = splice(s, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX,
-                              SPLICE_F_MOVE);
-            if (res == -1) {
-              throw SysError("splicing data from daemon socket to stdout");
-            }
-            if (res == 0) {
-              throw EndOfFile("unexpected EOF from daemon socket");
-            }
-          }
-          if (FD_ISSET(STDIN_FILENO, &fds)) {
-            auto res = splice(STDIN_FILENO, nullptr, s, nullptr, SSIZE_MAX,
-                              SPLICE_F_MOVE);
-            if (res == -1) {
-              throw SysError("splicing data from stdin to daemon socket");
-            }
-            if (res == 0) {
-              return 0;
-            }
-          }
-        }
-      } else {
-        processConnection(true, "root", 0);
-      }
-    } else {
-      daemonLoop(argv);
-    }
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("nix-daemon", _main);
diff --git a/third_party/nix/src/nix-daemon/nix-daemon-proto.cc b/third_party/nix/src/nix-daemon/nix-daemon-proto.cc
deleted file mode 100644
index d6498e77c2..0000000000
--- a/third_party/nix/src/nix-daemon/nix-daemon-proto.cc
+++ /dev/null
@@ -1,799 +0,0 @@
-#include "nix-daemon-proto.hh"
-
-#include <filesystem>
-#include <ostream>
-#include <sstream>
-#include <streambuf>
-#include <string>
-
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_format.h>
-#include <google/protobuf/empty.pb.h>
-#include <google/protobuf/util/time_util.h>
-#include <grpcpp/impl/codegen/server_context.h>
-#include <grpcpp/impl/codegen/status.h>
-#include <grpcpp/impl/codegen/status_code_enum.h>
-#include <grpcpp/impl/codegen/sync_stream.h>
-
-#include "libmain/shared.hh"
-#include "libproto/worker.grpc.pb.h"
-#include "libproto/worker.pb.h"
-#include "libstore/derivations.hh"
-#include "libstore/local-store.hh"
-#include "libstore/store-api.hh"
-#include "libutil/archive.hh"
-#include "libutil/hash.hh"
-#include "libutil/proto.hh"
-#include "libutil/serialise.hh"
-#include "libutil/types.hh"
-
-namespace nix::daemon {
-
-using ::google::protobuf::util::TimeUtil;
-using ::grpc::Status;
-using ::nix::proto::PathInfo;
-using ::nix::proto::StorePath;
-using ::nix::proto::StorePaths;
-using ::nix::proto::WorkerService;
-
-template <typename Request>
-class RPCSource final : public Source {
- public:
-  using Reader = grpc::ServerReader<Request>;
-  explicit RPCSource(Reader* reader) : reader_(reader) {}
-
-  size_t read(unsigned char* data, size_t len) override {
-    auto got = buffer_.sgetn(reinterpret_cast<char*>(data), len);
-    if (got < len) {
-      Request msg;
-      if (!reader_->Read(&msg)) {
-        return got;
-      }
-      if (msg.add_oneof_case() != Request::kData) {
-        // TODO(grfn): Make Source::read return a StatusOr and get rid of this
-        // throw
-        throw Error(
-            "Invalid AddToStoreRequest: all messages except the first must "
-            "contain data");
-      }
-      buffer_.sputn(msg.data().data(), msg.data().length());
-      return got + read(data + got, len - got);
-    }
-    return got;
-  };
-
- private:
-  std::stringbuf buffer_;
-  Reader* reader_;
-};
-
-// TODO(grfn): Make this some sort of pipe so we don't have to store data in
-// memory
-/* If the NAR archive contains a single file at top-level, then save
-   the contents of the file to `s'.  Otherwise barf. */
-struct RetrieveRegularNARSink : ParseSink {
-  bool regular{true};
-  std::string s;
-
-  RetrieveRegularNARSink() {}
-
-  void createDirectory(const Path& path) override { regular = false; }
-
-  void receiveContents(unsigned char* data, unsigned int len) override {
-    s.append(reinterpret_cast<const char*>(data), len);
-  }
-
-  void createSymlink(const Path& path, const std::string& target) override {
-    regular = false;
-  }
-};
-
-#define ASSERT_INPUT_STORE_PATH(path)                                          \
-  if (!store_->isStorePath(path)) {                                            \
-    return Status(grpc::StatusCode::INVALID_ARGUMENT,                          \
-                  absl::StrFormat("path '%s' is not in the Nix store", path)); \
-  }
-
-class BuildLogStreambuf final : public std::streambuf {
- public:
-  using Writer = grpc::ServerWriter<nix::proto::BuildEvent>;
-  explicit BuildLogStreambuf(Writer* writer) : writer_(writer) {}
-
-  // TODO(grfn): buffer with a timeout so we don't have too many messages
-  std::streamsize xsputn(const char_type* s, std::streamsize n) override {
-    nix::proto::BuildEvent event;
-    event.mutable_build_log()->set_line(s, n);
-    writer_->Write(event);
-    return n;
-  }
-
-  int_type overflow(int_type ch) override {
-    if (ch != traits_type::eof()) {
-      nix::proto::BuildEvent event;
-      event.mutable_build_log()->set_line(std::string(1, ch));
-      writer_->Write(event);
-    }
-    return ch;
-  }
-
- private:
-  Writer* writer_{};
-};
-
-class WorkerServiceImpl final : public WorkerService::Service {
- public:
-  WorkerServiceImpl(nix::Store& store) : store_(&store) {}
-
-  Status IsValidPath(grpc::ServerContext* context, const StorePath* request,
-                     nix::proto::IsValidPathResponse* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          const auto& path = request->path();
-          response->set_is_valid(store_->isValidPath(path));
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status HasSubstitutes(grpc::ServerContext* context, const StorePath* request,
-                        nix::proto::HasSubstitutesResponse* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          const auto& path = request->path();
-          ASSERT_INPUT_STORE_PATH(path);
-          PathSet res = store_->querySubstitutablePaths({path});
-          response->set_has_substitutes(res.find(path) != res.end());
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QueryReferrers(grpc::ServerContext* context, const StorePath* request,
-                        StorePaths* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          const auto& path = request->path();
-          ASSERT_INPUT_STORE_PATH(path);
-
-          PathSet paths;
-          store_->queryReferrers(path, paths);
-
-          for (const auto& path : paths) {
-            response->add_paths(path);
-          }
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status AddToStore(grpc::ServerContext* context,
-                    grpc::ServerReader<nix::proto::AddToStoreRequest>* reader,
-                    nix::proto::StorePath* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          proto::AddToStoreRequest metadata_request;
-          auto has_metadata = reader->Read(&metadata_request);
-
-          if (!has_metadata || !metadata_request.has_meta()) {
-            return Status(grpc::StatusCode::INVALID_ARGUMENT,
-                          "Metadata must be set before sending file content");
-          }
-
-          auto meta = metadata_request.meta();
-          RPCSource source(reader);
-          auto opt_hash_type = hash_type_from(meta.hash_type());
-          if (!opt_hash_type) {
-            return Status(grpc::StatusCode::INVALID_ARGUMENT,
-                          "Invalid hash type");
-          }
-
-          std::string* data;
-          RetrieveRegularNARSink nar;
-          TeeSource saved_nar(source);
-
-          if (meta.recursive()) {
-            // TODO(grfn): Don't store the full data in memory, instead just
-            // make addToStoreFromDump take a Source
-            ParseSink sink;
-            parseDump(sink, saved_nar);
-            data = &(*saved_nar.data);
-          } else {
-            parseDump(nar, source);
-            if (!nar.regular) {
-              return Status(grpc::StatusCode::INVALID_ARGUMENT,
-                            "Regular file expected");
-            }
-            data = &nar.s;
-          }
-
-          auto local_store = store_.dynamic_pointer_cast<LocalStore>();
-          if (!local_store) {
-            return Status(grpc::StatusCode::FAILED_PRECONDITION,
-                          "operation is only supported by LocalStore");
-          }
-
-          auto path = local_store->addToStoreFromDump(
-              *data, meta.base_name(), meta.recursive(), opt_hash_type.value());
-
-          response->set_path(path);
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status AddToStoreNar(
-      grpc::ServerContext* context,
-      grpc::ServerReader<nix::proto::AddToStoreNarRequest>* reader,
-      google::protobuf::Empty*) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          proto::AddToStoreNarRequest path_info_request;
-          auto has_path_info = reader->Read(&path_info_request);
-          if (!has_path_info || !path_info_request.has_path_info()) {
-            return Status(grpc::StatusCode::INVALID_ARGUMENT,
-                          "Path info must be set before sending nar content");
-          }
-
-          auto path_info = path_info_request.path_info();
-
-          ValidPathInfo info;
-          info.path = path_info.path().path();
-          info.deriver = path_info.deriver().path();
-
-          if (!info.deriver.empty()) {
-            ASSERT_INPUT_STORE_PATH(info.deriver);
-          }
-
-          auto nar_hash = Hash::deserialize(path_info.nar_hash(), htSHA256);
-
-          if (!nar_hash.ok()) {
-            return Status(grpc::StatusCode::INVALID_ARGUMENT,
-                          std::string(nar_hash.status().message()));
-          }
-
-          info.narHash = *nar_hash;
-          for (const auto& ref : path_info.references()) {
-            info.references.insert(ref);
-          }
-          info.registrationTime =
-              TimeUtil::TimestampToTimeT(path_info.registration_time());
-          info.narSize = path_info.nar_size();
-          info.ultimate = path_info.ultimate();
-          for (const auto& sig : path_info.sigs()) {
-            info.sigs.insert(sig);
-          }
-          info.ca = path_info.ca();
-
-          auto repair = path_info.repair();
-          auto check_sigs = path_info.check_sigs();
-
-          std::string saved;
-          RPCSource source(reader);
-          store_->addToStore(info, source, repair ? Repair : NoRepair,
-                             check_sigs ? CheckSigs : NoCheckSigs, nullptr);
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status AddTextToStore(
-      grpc::ServerContext*,
-      grpc::ServerReader<nix::proto::AddTextToStoreRequest>* reader,
-      nix::proto::StorePath* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          proto::AddTextToStoreRequest request;
-          auto has_metadata = reader->Read(&request);
-          if (!has_metadata || !request.has_meta()) {
-            return Status(grpc::StatusCode::INVALID_ARGUMENT,
-                          "Metadata must be set before sending content");
-          }
-
-          proto::AddTextToStoreRequest_Metadata meta = request.meta();
-
-          PathSet references;
-          for (const auto& ref : meta.references()) {
-            references.insert(ref);
-          }
-
-          std::string content;
-          content.reserve(meta.size());
-          while (reader->Read(&request)) {
-            if (request.add_oneof_case() != request.kData) {
-              return Status(grpc::StatusCode::INVALID_ARGUMENT,
-                            "All requests except the first must contain data");
-            }
-
-            content.append(request.data());
-          }
-
-          auto path = store_->addTextToStore(meta.name(), content, references);
-          response->set_path(path);
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status BuildPaths(
-      grpc::ServerContext*, const nix::proto::BuildPathsRequest* request,
-      grpc::ServerWriter<nix::proto::BuildEvent>* writer) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          PathSet drvs;
-          for (const auto& drv : request->drvs()) {
-            drvs.insert(drv);
-          }
-          auto mode = BuildModeFrom(request->mode());
-
-          if (!mode.has_value()) {
-            return Status(grpc::StatusCode::INTERNAL, "Invalid build mode");
-          }
-
-          BuildLogStreambuf log_buffer(writer);
-          std::ostream log_sink(&log_buffer);
-
-          // TODO(grfn): If mode is repair and not trusted, we need to return an
-          // error here (but we can't yet because we don't know anything about
-          // trusted users)
-          return nix::util::proto::AbslToGRPCStatus(
-              store_->buildPaths(log_sink, drvs, mode.value()));
-        },
-        __FUNCTION__);
-  }
-
-  Status EnsurePath(grpc::ServerContext* context,
-                    const nix::proto::StorePath* request,
-                    google::protobuf::Empty*) override {
-    auto path = request->path();
-    ASSERT_INPUT_STORE_PATH(path);
-    return HandleExceptions(
-        [&]() -> Status {
-          store_->ensurePath(path);
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status AddTempRoot(grpc::ServerContext*, const nix::proto::StorePath* request,
-                     google::protobuf::Empty*) override {
-    auto path = request->path();
-    ASSERT_INPUT_STORE_PATH(path);
-
-    return HandleExceptions(
-        [&]() -> Status {
-          store_->addTempRoot(path);
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status AddIndirectRoot(grpc::ServerContext*,
-                         const nix::proto::StorePath* request,
-                         google::protobuf::Empty*) override {
-    auto path = std::filesystem::canonical(request->path());
-    ASSERT_INPUT_STORE_PATH(path);
-
-    return HandleExceptions(
-        [&]() -> Status {
-          store_->addIndirectRoot(path);
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status SyncWithGC(grpc::ServerContext*, const google::protobuf::Empty*,
-                    google::protobuf::Empty*) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          store_->syncWithGC();
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status FindRoots(grpc::ServerContext*, const google::protobuf::Empty*,
-                   nix::proto::FindRootsResponse* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          auto roots = store_->findRoots(false);
-          for (const auto& [target, links] : roots) {
-            StorePaths link_paths;
-            for (const auto& link : links) {
-              link_paths.add_paths(link);
-            }
-            response->mutable_roots()->insert({target, link_paths});
-          }
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status CollectGarbage(grpc::ServerContext*,
-                        const proto::CollectGarbageRequest* request,
-                        proto::CollectGarbageResponse* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          GCOptions options;
-          auto action = GCActionFromProto(request->action());
-          if (!action.has_value()) {
-            return Status(grpc::StatusCode::INVALID_ARGUMENT,
-                          "Invalid GC action");
-          }
-
-          options.action = action.value();
-          for (const auto& path : request->paths_to_delete()) {
-            options.pathsToDelete.insert(path);
-          }
-          options.ignoreLiveness = request->ignore_liveness();
-          options.maxFreed = request->max_freed();
-
-          if (options.ignoreLiveness) {
-            return Status(grpc::StatusCode::INVALID_ARGUMENT,
-                          "you are not allowed to ignore liveness");
-          }
-
-          GCResults results;
-          store_->collectGarbage(options, results);
-
-          for (const auto& path : results.paths) {
-            response->add_deleted_paths(path);
-          }
-          response->set_bytes_freed(results.bytesFreed);
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QuerySubstitutablePathInfos(
-      grpc::ServerContext*, const StorePaths* request,
-      nix::proto::SubstitutablePathInfos* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          SubstitutablePathInfos infos;
-          PathSet paths;
-          for (const auto& path : request->paths()) {
-            paths.insert(path);
-          }
-          store_->querySubstitutablePathInfos(paths, infos);
-          for (const auto& [path, path_info] : infos) {
-            auto proto_path_info = response->add_path_infos();
-            proto_path_info->mutable_path()->set_path(path);
-            proto_path_info->mutable_deriver()->set_path(path_info.deriver);
-            for (const auto& ref : path_info.references) {
-              proto_path_info->add_references(ref);
-            }
-            proto_path_info->set_download_size(path_info.downloadSize);
-            proto_path_info->set_nar_size(path_info.narSize);
-          }
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QueryValidDerivers(grpc::ServerContext* context,
-                            const StorePath* request,
-                            StorePaths* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          const auto& path = request->path();
-          ASSERT_INPUT_STORE_PATH(path);
-
-          PathSet paths = store_->queryValidDerivers(path);
-
-          for (const auto& path : paths) {
-            response->add_paths(path);
-          }
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QueryDerivationOutputs(grpc::ServerContext* context,
-                                const StorePath* request,
-                                StorePaths* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          const auto& path = request->path();
-          ASSERT_INPUT_STORE_PATH(path);
-
-          PathSet paths = store_->queryDerivationOutputs(path);
-
-          for (const auto& path : paths) {
-            response->add_paths(path);
-          }
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QueryAllValidPaths(grpc::ServerContext* context,
-                            const google::protobuf::Empty* request,
-                            StorePaths* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          const auto paths = store_->queryAllValidPaths();
-          for (const auto& path : paths) {
-            response->add_paths(path);
-          }
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QueryPathInfo(grpc::ServerContext* context, const StorePath* request,
-                       PathInfo* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          auto path = request->path();
-          ASSERT_INPUT_STORE_PATH(path);
-
-          response->mutable_path()->set_path(path);
-          try {
-            auto info = store_->queryPathInfo(path);
-            response->mutable_deriver()->set_path(info->deriver);
-            response->set_nar_hash(
-                reinterpret_cast<const char*>(&info->narHash.hash[0]),
-                info->narHash.hashSize);
-
-            for (const auto& reference : info->references) {
-              response->add_references(reference);
-            }
-
-            *response->mutable_registration_time() =
-                google::protobuf::util::TimeUtil::TimeTToTimestamp(
-                    info->registrationTime);
-
-            response->set_nar_size(info->narSize);
-            response->set_ultimate(info->ultimate);
-
-            for (const auto& sig : info->sigs) {
-              response->add_sigs(sig);
-            }
-
-            response->set_ca(info->ca);
-
-            return Status::OK;
-          } catch (InvalidPath& e) {
-            return Status(grpc::StatusCode::INVALID_ARGUMENT, e.msg());
-          }
-        },
-        __FUNCTION__);
-  }
-
-  Status QueryDerivationOutputNames(
-      grpc::ServerContext* context, const StorePath* request,
-      nix::proto::DerivationOutputNames* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          auto path = request->path();
-          ASSERT_INPUT_STORE_PATH(path);
-          auto names = store_->queryDerivationOutputNames(path);
-          for (const auto& name : names) {
-            response->add_names(name);
-          }
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QueryPathFromHashPart(grpc::ServerContext* context,
-                               const nix::proto::HashPart* request,
-                               StorePath* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          auto hash_part = request->hash_part();
-          auto path = store_->queryPathFromHashPart(hash_part);
-          ASSERT_INPUT_STORE_PATH(path);
-          response->set_path(path);
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QueryValidPaths(grpc::ServerContext* context,
-                         const StorePaths* request,
-                         StorePaths* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          std::set<Path> paths;
-          for (const auto& path : request->paths()) {
-            ASSERT_INPUT_STORE_PATH(path);
-            paths.insert(path);
-          }
-
-          auto res = store_->queryValidPaths(paths);
-
-          for (const auto& path : res) {
-            response->add_paths(path);
-          }
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QuerySubstitutablePaths(grpc::ServerContext* context,
-                                 const StorePaths* request,
-                                 StorePaths* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          std::set<Path> paths;
-          for (const auto& path : request->paths()) {
-            ASSERT_INPUT_STORE_PATH(path);
-            paths.insert(path);
-          }
-
-          auto res = store_->querySubstitutablePaths(paths);
-
-          for (const auto& path : res) {
-            response->add_paths(path);
-          }
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status OptimiseStore(grpc::ServerContext* context,
-                       const google::protobuf::Empty* request,
-                       google::protobuf::Empty* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          store_->optimiseStore();
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status VerifyStore(grpc::ServerContext* context,
-                     const nix::proto::VerifyStoreRequest* request,
-                     nix::proto::VerifyStoreResponse* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          auto errors =
-              store_->verifyStore(request->check_contents(),
-                                  static_cast<RepairFlag>(request->repair()));
-
-          response->set_errors(errors);
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status BuildDerivation(
-      grpc::ServerContext*, const nix::proto::BuildDerivationRequest* request,
-      grpc::ServerWriter<nix::proto::BuildEvent>* writer) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          auto drv_path = request->drv_path().path();
-          ASSERT_INPUT_STORE_PATH(drv_path);
-          auto drv = BasicDerivation::from_proto(&request->derivation());
-
-          auto build_mode = nix::BuildModeFrom(request->build_mode());
-          if (!build_mode) {
-            return Status(grpc::StatusCode::INTERNAL, "Invalid build mode");
-          }
-
-          BuildLogStreambuf log_buffer(writer);
-          std::ostream log_sink(&log_buffer);
-          BuildResult res =
-              store_->buildDerivation(log_sink, drv_path, drv, *build_mode);
-
-          proto::BuildResult proto_res{};
-          proto_res.set_status(res.status_to_proto());
-
-          if (!res.errorMsg.empty()) {
-            proto_res.set_msg(res.errorMsg);
-          }
-
-          proto::BuildEvent event{};
-          *event.mutable_result() = proto_res;
-
-          writer->Write(event);
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status AddSignatures(grpc::ServerContext* context,
-                       const nix::proto::AddSignaturesRequest* request,
-                       google::protobuf::Empty* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          auto path = request->path().path();
-          ASSERT_INPUT_STORE_PATH(path);
-
-          StringSet sigs;
-          sigs.insert(request->sigs().sigs().begin(),
-                      request->sigs().sigs().end());
-
-          store_->addSignatures(path, sigs);
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
-  Status QueryMissing(grpc::ServerContext* context, const StorePaths* request,
-                      nix::proto::QueryMissingResponse* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          std::set<Path> targets;
-          for (auto& path : request->paths()) {
-            ASSERT_INPUT_STORE_PATH(path);
-            targets.insert(path);
-          }
-          PathSet will_build;
-          PathSet will_substitute;
-          PathSet unknown;
-          // TODO(grfn): Switch to concrete size type
-          unsigned long long download_size;
-          unsigned long long nar_size;
-
-          store_->queryMissing(targets, will_build, will_substitute, unknown,
-                               download_size, nar_size);
-          for (auto& path : will_build) {
-            response->add_will_build(path);
-          }
-          for (auto& path : will_substitute) {
-            response->add_will_substitute(path);
-          }
-          for (auto& path : unknown) {
-            response->add_unknown(path);
-          }
-          response->set_download_size(download_size);
-          response->set_nar_size(nar_size);
-
-          return Status::OK;
-        },
-        __FUNCTION__);
-  };
-
-  Status GetBuildLog(grpc::ServerContext* context, const StorePath* request,
-                     proto::BuildLog* response) override {
-    return HandleExceptions(
-        [&]() -> Status {
-          const auto log = store_->getBuildLog(request->path());
-          if (log) {
-            response->set_build_log(*log);
-          }
-          return Status::OK;
-        },
-        __FUNCTION__);
-  }
-
- private:
-  Status HandleExceptions(std::function<Status(void)> fn,
-                          absl::string_view methodName) {
-    try {
-      return fn();
-    } catch (Unsupported& e) {
-      return Status(grpc::StatusCode::UNIMPLEMENTED,
-                    absl::StrCat(methodName, " is not supported: ", e.what()));
-    } catch (Error& e) {
-      return Status(grpc::StatusCode::INTERNAL, e.what());
-    }
-    // add more specific Error-Status mappings above
-  }
-
-  ref<nix::Store> store_;
-};
-
-WorkerService::Service* NewWorkerService(nix::Store& store) {
-  return new WorkerServiceImpl(store);
-}
-
-}  // namespace nix::daemon
diff --git a/third_party/nix/src/nix-daemon/nix-daemon-proto.hh b/third_party/nix/src/nix-daemon/nix-daemon-proto.hh
deleted file mode 100644
index ca871213eb..0000000000
--- a/third_party/nix/src/nix-daemon/nix-daemon-proto.hh
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-
-#include <memory>
-
-#include "libproto/worker.grpc.pb.h"
-#include "libstore/store-api.hh"
-
-namespace nix::daemon {
-
-nix::proto::WorkerService::Service* NewWorkerService(nix::Store&);
-
-}  // namespace nix::daemon
diff --git a/third_party/nix/src/nix-daemon/nix-daemon.cc b/third_party/nix/src/nix-daemon/nix-daemon.cc
deleted file mode 100644
index 0551625a3e..0000000000
--- a/third_party/nix/src/nix-daemon/nix-daemon.cc
+++ /dev/null
@@ -1,201 +0,0 @@
-#include <filesystem>
-
-#include <absl/flags/flag.h>
-#include <absl/flags/parse.h>
-#include <absl/flags/usage_config.h>
-#include <absl/strings/str_format.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <grpcpp/security/server_credentials.h>
-#include <grpcpp/server.h>
-#include <grpcpp/server_builder.h>
-#include <grpcpp/server_posix.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <systemd/sd-daemon.h>
-
-#include "libmain/shared.hh"  // TODO(tazjin): can this be removed?
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libutil/util.hh"
-#include "nix-daemon-proto.hh"
-#include "nix-daemon/nix-daemon-proto.hh"
-#include "nix/legacy.hh"
-
-ABSL_FLAG(bool, pipe, false, "Use pipes for daemon communication");
-
-namespace nix::daemon {
-
-using grpc::Server;
-using grpc::ServerBuilder;
-
-namespace {
-
-// TODO(grfn): There has to be a better way to do this - this was ported
-// verbatim from the old daemon implementation without much critical evaluation.
-static int ForwardToSocket(nix::Path socket_path) {
-  // Forward on this connection to the real daemon
-  int sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
-  if (sockfd == -1) {
-    throw SysError("creating Unix domain socket");
-  }
-
-  auto socketDir = dirOf(socket_path);
-  if (chdir(socketDir.c_str()) == -1) {
-    throw SysError(format("changing to socket directory '%1%'") % socketDir);
-  }
-
-  auto socketName = baseNameOf(socket_path);
-  auto addr = sockaddr_un{};
-  addr.sun_family = AF_UNIX;
-  if (socketName.size() + 1 >= sizeof(addr.sun_path)) {
-    throw Error(format("socket name %1% is too long") % socketName);
-  }
-  strncpy(addr.sun_path, socketName.c_str(), sizeof(addr.sun_family));
-
-  if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&addr),
-              sizeof(addr)) == -1) {
-    throw SysError(format("cannot connect to daemon at %1%") % socket_path);
-  }
-
-  auto nfds = (sockfd > STDIN_FILENO ? sockfd : STDIN_FILENO) + 1;
-  while (true) {
-    fd_set fds;
-    FD_ZERO(&fds);
-    FD_SET(sockfd, &fds);
-    FD_SET(STDIN_FILENO, &fds);
-    if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1) {
-      throw SysError("waiting for data from client or server");
-    }
-    if (FD_ISSET(sockfd, &fds)) {
-      auto res = splice(sockfd, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX,
-                        SPLICE_F_MOVE);
-      if (res == -1) {
-        throw SysError("splicing data from daemon socket to stdout");
-      }
-      if (res == 0) {
-        throw EndOfFile("unexpected EOF from daemon socket");
-      }
-    }
-    if (FD_ISSET(STDIN_FILENO, &fds)) {
-      auto res = splice(STDIN_FILENO, nullptr, sockfd, nullptr, SSIZE_MAX,
-                        SPLICE_F_MOVE);
-      if (res == -1) {
-        throw SysError("splicing data from stdin to daemon socket");
-      }
-      if (res == 0) {
-        return 0;
-      }
-    }
-  }
-}
-
-void SetNonBlocking(int fd) {
-  int flags = fcntl(fd, F_GETFL);  // NOLINT
-  PCHECK(flags != 0) << "Error getting socket flags";
-  PCHECK(fcntl(  // NOLINT
-             fd, F_SETFL, flags | O_NONBLOCK) == 0)
-      << "Could not set socket flags";
-}
-
-}  // namespace
-
-int RunServer() {
-  Store::Params params;
-  params["path-info-cache-size"] = "0";
-  auto store = openStore(settings.storeUri, params);
-  auto worker = NewWorkerService(*store);
-  ServerBuilder builder;
-  builder.RegisterService(worker);
-
-  auto n_fds = sd_listen_fds(0);
-
-  if (n_fds > 1) {
-    LOG(FATAL) << "Too many file descriptors (" << n_fds
-               << ") received from systemd socket activation";
-  }
-
-  std::filesystem::path socket_path;
-
-  if (n_fds == 0) {
-    socket_path = settings.nixDaemonSocketFile;
-    std::filesystem::create_directories(socket_path.parent_path());
-    auto socket_addr = absl::StrFormat("unix://%s", socket_path);
-    builder.AddListeningPort(socket_addr, grpc::InsecureServerCredentials());
-  }
-
-  std::unique_ptr<Server> server(builder.BuildAndStart());
-
-  if (!server) {
-    LOG(FATAL) << "Error building server";
-    return 1;
-  }
-
-  // We have been systemd socket-activated - instead of asking grpc to make the
-  // socket path for us, start our own accept loop and pass file descriptors to
-  // grpc.
-  //
-  // This approach was *somewhat* adapted from
-  // https://gist.github.com/yorickvP/8d523a4df2b10c5812fa7789e82b7c1b - at some
-  // point we'd like gRPC to do it for us, though - see
-  // https://github.com/grpc/grpc/issues/19133
-  if (n_fds == 1) {
-    int socket_fd = SD_LISTEN_FDS_START;
-    // Only used for logging
-    socket_path = readLink(absl::StrFormat("/proc/self/fd/%d", socket_fd));
-
-    PCHECK(sd_notify(0, "READY=1") == 0) << "Error notifying systemd";
-    for (;;) {
-      try {
-        struct sockaddr_un remote_addr {};
-        socklen_t remote_addr_len = sizeof(remote_addr);
-        int remote_fd =
-            accept(socket_fd,
-                   reinterpret_cast<struct sockaddr*>(&remote_addr),  // NOLINT
-                   &remote_addr_len);
-        checkInterrupt();
-        if (!remote_fd) {
-          if (errno == EINTR) {
-            continue;
-          }
-          PCHECK(false) << "error accepting connection";
-        }
-
-        LOG(INFO) << "Accepted remote connection on fd " << remote_fd;
-        SetNonBlocking(remote_fd);
-        grpc::AddInsecureChannelFromFd(server.get(), remote_fd);
-      } catch (Interrupted& e) {
-        return -1;
-      } catch (Error& e) {
-        LOG(ERROR) << "error processing connection: " << e.msg();
-      }
-    }
-  }
-
-  LOG(INFO) << "Nix daemon listening at " << socket_path;
-  server->Wait();
-  return 0;
-}
-
-}  // namespace nix::daemon
-
-int main(int argc, char** argv) {  // NOLINT
-  FLAGS_logtostderr = true;
-  google::InitGoogleLogging(argv[0]);  // NOLINT
-
-  absl::SetFlagsUsageConfig({.version_string = [] { return nix::nixVersion; }});
-  absl::ParseCommandLine(argc, argv);
-
-  if (absl::GetFlag(FLAGS_pipe)) {
-    if (nix::getStoreType() == nix::tDaemon) {
-      return nix::daemon::ForwardToSocket(nix::settings.nixDaemonSocketFile);
-    } else {
-      // TODO(grfn): Need to launch a server on stdin here - upstream calls
-      // processConnection(true, "root", 0);
-      LOG(ERROR) << "not implemented";
-      return 1;
-    }
-  }
-
-  return nix::daemon::RunServer();
-}
diff --git a/third_party/nix/src/nix-env/nix-env.cc b/third_party/nix/src/nix-env/nix-env.cc
deleted file mode 100644
index 15f12abd97..0000000000
--- a/third_party/nix/src/nix-env/nix-env.cc
+++ /dev/null
@@ -1,1543 +0,0 @@
-#include <algorithm>
-#include <cerrno>
-#include <ctime>
-#include <iostream>
-#include <sstream>
-
-#include <absl/strings/match.h>
-#include <absl/strings/numbers.h>
-#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libexpr/attr-path.hh"
-#include "libexpr/common-eval-args.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/get-drvs.hh"
-#include "libexpr/names.hh"
-#include "libexpr/value-to-json.hh"
-#include "libmain/shared.hh"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/profiles.hh"
-#include "libstore/store-api.hh"
-#include "libutil/json.hh"
-#include "libutil/status.hh"
-#include "libutil/util.hh"
-#include "libutil/xml-writer.hh"
-#include "nix-env/user-env.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-using std::cout;
-
-using InstallSourceType = enum {
-  srcNixExprDrvs,
-  srcNixExprs,
-  srcStorePaths,
-  srcProfile,
-  srcAttrPath,
-  srcUnknown
-};
-
-struct InstallSourceInfo {
-  InstallSourceType type;
-  Path nixExprPath;         /* for srcNixExprDrvs, srcNixExprs */
-  Path profile;             /* for srcProfile */
-  std::string systemFilter; /* for srcNixExprDrvs */
-  std::unique_ptr<Bindings> autoArgs;
-};
-
-struct Globals {
-  InstallSourceInfo instSource;
-  Path profile;
-  std::shared_ptr<EvalState> state;
-  bool dryRun;
-  bool preserveInstalled;
-  bool removeAll;
-  std::string forceName;
-  bool prebuiltOnly;
-};
-
-using Operation = void (*)(Globals&, Strings, Strings);
-
-static std::string needArg(Strings::iterator& i, Strings& args,
-                           const std::string& arg) {
-  if (i == args.end()) {
-    throw UsageError(format("'%1%' requires an argument") % arg);
-  }
-  return *i++;
-}
-
-static bool parseInstallSourceOptions(Globals& globals, Strings::iterator& i,
-                                      Strings& args, const std::string& arg) {
-  if (arg == "--from-expression" || arg == "-E") {
-    globals.instSource.type = srcNixExprs;
-  } else if (arg == "--from-profile") {
-    globals.instSource.type = srcProfile;
-    globals.instSource.profile = needArg(i, args, arg);
-  } else if (arg == "--attr" || arg == "-A") {
-    globals.instSource.type = srcAttrPath;
-  } else {
-    return false;
-  }
-  return true;
-}
-
-static bool isNixExpr(const Path& path, struct stat& st) {
-  return S_ISREG(st.st_mode) ||
-         (S_ISDIR(st.st_mode) && pathExists(path + "/default.nix"));
-}
-
-static void getAllExprs(EvalState& state, const Path& path, StringSet& attrs,
-                        Value& v) {
-  StringSet namesSorted;
-  for (auto& i : readDirectory(path)) {
-    namesSorted.insert(i.name);
-  }
-
-  for (auto& i : namesSorted) {
-    /* Ignore the manifest.nix used by profiles.  This is
-       necessary to prevent it from showing up in channels (which
-       are implemented using profiles). */
-    if (i == "manifest.nix") {
-      continue;
-    }
-
-    Path path2 = path + "/" + i;
-
-    struct stat st;
-    if (stat(path2.c_str(), &st) == -1) {
-      continue;  // ignore dangling symlinks in ~/.nix-defexpr
-    }
-
-    if (isNixExpr(path2, st) &&
-        (!S_ISREG(st.st_mode) || absl::EndsWith(path2, ".nix"))) {
-      /* Strip off the `.nix' filename suffix (if applicable),
-         otherwise the attribute cannot be selected with the
-         `-A' option.  Useful if you want to stick a Nix
-         expression directly in ~/.nix-defexpr. */
-      std::string attrName = i;
-      if (absl::EndsWith(attrName, ".nix")) {
-        attrName = std::string(attrName, 0, attrName.size() - 4);
-      }
-      if (attrs.find(attrName) != attrs.end()) {
-        LOG(WARNING) << "name collision in input Nix expressions, skipping '"
-                     << path2 << "'";
-        continue;
-      }
-      attrs.insert(attrName);
-      /* Load the expression on demand. */
-      Value& vFun = state.getBuiltin("import");
-      Value& vArg(*state.allocValue());
-      mkString(vArg, path2);
-      mkApp(*state.allocAttr(v, state.symbols.Create(attrName)), vFun, vArg);
-    } else if (S_ISDIR(st.st_mode)) {
-      /* `path2' is a directory (with no default.nix in it);
-         recurse into it. */
-      getAllExprs(state, path2, attrs, v);
-    }
-  }
-}
-
-static void loadSourceExpr(EvalState& state, const Path& path, Value& v) {
-  struct stat st;
-  if (stat(path.c_str(), &st) == -1) {
-    throw SysError(format("getting information about '%1%'") % path);
-  }
-
-  if (isNixExpr(path, st)) {
-    state.evalFile(path, v);
-  }
-
-  /* The path is a directory.  Put the Nix expressions in the
-     directory in a set, with the file name of each expression as
-     the attribute name.  Recurse into subdirectories (but keep the
-     set flat, not nested, to make it easier for a user to have a
-     ~/.nix-defexpr directory that includes some system-wide
-     directory). */
-  else if (S_ISDIR(st.st_mode)) {
-    state.mkAttrs(v, 1024);
-    state.mkList(*state.allocAttr(v, state.symbols.Create("_combineChannels")));
-    StringSet attrs;
-    getAllExprs(state, path, attrs, v);
-  }
-
-  else {
-    throw Error("path '%s' is not a directory or a Nix expression", path);
-  }
-}
-
-static void loadDerivations(EvalState& state, const Path& nixExprPath,
-                            const std::string& systemFilter, Bindings* autoArgs,
-                            const std::string& pathPrefix, DrvInfos& elems) {
-  Value vRoot;
-  loadSourceExpr(state, nixExprPath, vRoot);
-
-  Value& v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot));
-
-  getDerivations(state, v, pathPrefix, autoArgs, elems, true);
-
-  /* Filter out all derivations not applicable to the current
-     system. */
-  for (DrvInfos::iterator i = elems.begin(), j; i != elems.end(); i = j) {
-    j = i;
-    j++;
-    if (systemFilter != "*" && i->querySystem() != systemFilter) {
-      elems.erase(i);
-    }
-  }
-}
-
-static long getPriority(EvalState& state, DrvInfo& drv) {
-  return drv.queryMetaInt("priority", 0);
-}
-
-static long comparePriorities(EvalState& state, DrvInfo& drv1, DrvInfo& drv2) {
-  return getPriority(state, drv2) - getPriority(state, drv1);
-}
-
-// FIXME: this function is rather slow since it checks a single path
-// at a time.
-static bool isPrebuilt(EvalState& state, DrvInfo& elem) {
-  Path path = elem.queryOutPath();
-  if (state.store->isValidPath(path)) {
-    return true;
-  }
-  PathSet ps = state.store->querySubstitutablePaths({path});
-  return ps.find(path) != ps.end();
-}
-
-static void checkSelectorUse(DrvNames& selectors) {
-  /* Check that all selectors have been used. */
-  for (auto& i : selectors) {
-    if (i.hits == 0 && i.fullName != "*") {
-      throw Error(format("selector '%1%' matches no derivations") % i.fullName);
-    }
-  }
-}
-
-static DrvInfos filterBySelector(EvalState& state, const DrvInfos& allElems,
-                                 const Strings& args, bool newestOnly) {
-  DrvNames selectors = drvNamesFromArgs(args);
-  if (selectors.empty()) {
-    selectors.push_back(DrvName("*"));
-  }
-
-  DrvInfos elems;
-  std::set<unsigned int> done;
-
-  for (auto& i : selectors) {
-    using Matches = std::list<std::pair<DrvInfo, unsigned int> >;
-    Matches matches;
-    unsigned int n = 0;
-    for (auto j = allElems.begin(); j != allElems.end(); ++j, ++n) {
-      DrvName drvName(j->queryName());
-      if (i.matches(drvName)) {
-        i.hits++;
-        matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
-      }
-    }
-
-    /* If `newestOnly', if a selector matches multiple derivations
-       with the same name, pick the one matching the current
-       system.  If there are still multiple derivations, pick the
-       one with the highest priority.  If there are still multiple
-       derivations, pick the one with the highest version.
-       Finally, if there are still multiple derivations,
-       arbitrarily pick the first one. */
-    if (newestOnly) {
-      /* Map from package names to derivations. */
-      using Newest = std::map<std::string, std::pair<DrvInfo, unsigned int> >;
-      Newest newest;
-      StringSet multiple;
-
-      for (auto& j : matches) {
-        DrvName drvName(j.first.queryName());
-        long d = 1;
-
-        auto k = newest.find(drvName.name);
-
-        if (k != newest.end()) {
-          d = j.first.querySystem() == k->second.first.querySystem() ? 0
-              : j.first.querySystem() == settings.thisSystem         ? 1
-              : k->second.first.querySystem() == settings.thisSystem ? -1
-                                                                     : 0;
-          if (d == 0) {
-            d = comparePriorities(state, j.first, k->second.first);
-          }
-          if (d == 0) {
-            d = compareVersions(drvName.version,
-                                DrvName(k->second.first.queryName()).version);
-          }
-        }
-
-        if (d > 0) {
-          newest.erase(drvName.name);
-          newest.insert(Newest::value_type(drvName.name, j));
-          multiple.erase(j.first.queryName());
-        } else if (d == 0) {
-          multiple.insert(j.first.queryName());
-        }
-      }
-
-      matches.clear();
-      for (auto& j : newest) {
-        if (multiple.find(j.second.first.queryName()) != multiple.end()) {
-          LOG(WARNING) << "warning: there are multiple derivations named '"
-                       << j.second.first.queryName()
-                       << "'; using the first one";
-        }
-        matches.push_back(j.second);
-      }
-    }
-
-    /* Insert only those elements in the final list that we
-       haven't inserted before. */
-    for (auto& j : matches) {
-      if (done.find(j.second) == done.end()) {
-        done.insert(j.second);
-        elems.push_back(j.first);
-      }
-    }
-  }
-
-  checkSelectorUse(selectors);
-
-  return elems;
-}
-
-static bool isPath(const std::string& s) {
-  return s.find('/') != std::string::npos;
-}
-
-static void queryInstSources(EvalState& state, InstallSourceInfo& instSource,
-                             const Strings& args, DrvInfos& elems,
-                             bool newestOnly) {
-  InstallSourceType type = instSource.type;
-  if (type == srcUnknown && !args.empty() && isPath(args.front())) {
-    type = srcStorePaths;
-  }
-
-  switch (type) {
-    /* Get the available user environment elements from the
-       derivations specified in a Nix expression, including only
-       those with names matching any of the names in `args'. */
-    case srcUnknown:
-    case srcNixExprDrvs: {
-      /* Load the derivations from the (default or specified)
-         Nix expression. */
-      DrvInfos allElems;
-      loadDerivations(state, instSource.nixExprPath, instSource.systemFilter,
-                      instSource.autoArgs.get(), "", allElems);
-
-      elems = filterBySelector(state, allElems, args, newestOnly);
-
-      break;
-    }
-
-    /* Get the available user environment elements from the Nix
-       expressions specified on the command line; these should be
-       functions that take the default Nix expression file as
-       argument, e.g., if the file is `./foo.nix', then the
-       argument `x: x.bar' is equivalent to `(x: x.bar)
-       (import ./foo.nix)' = `(import ./foo.nix).bar'. */
-    case srcNixExprs: {
-      Value vArg;
-      loadSourceExpr(state, instSource.nixExprPath, vArg);
-
-      for (auto& i : args) {
-        Expr* eFun = state.parseExprFromString(i, absPath("."));
-        Value vFun;
-        Value vTmp;
-        state.eval(eFun, vFun);
-        mkApp(vTmp, vFun, vArg);
-        getDerivations(state, vTmp, "", instSource.autoArgs.get(), elems, true);
-      }
-
-      break;
-    }
-
-    /* The available user environment elements are specified as a
-       list of store paths (which may or may not be
-       derivations). */
-    case srcStorePaths: {
-      for (auto& i : args) {
-        Path path = state.store->followLinksToStorePath(i);
-
-        std::string name = baseNameOf(path);
-        std::string::size_type dash = name.find('-');
-        if (dash != std::string::npos) {
-          name = std::string(name, dash + 1);
-        }
-
-        DrvInfo elem(state, "", nullptr);
-        elem.setName(name);
-
-        if (isDerivation(path)) {
-          elem.setDrvPath(path);
-          elem.setOutPath(
-              state.store->derivationFromPath(path).findOutput("out"));
-          if (name.size() >= drvExtension.size() &&
-              std::string(name, name.size() - drvExtension.size()) ==
-                  drvExtension) {
-            name = std::string(name, 0, name.size() - drvExtension.size());
-          }
-        } else {
-          elem.setOutPath(path);
-        }
-
-        elems.push_back(elem);
-      }
-
-      break;
-    }
-
-    /* Get the available user environment elements from another
-       user environment.  These are then filtered as in the
-       `srcNixExprDrvs' case. */
-    case srcProfile: {
-      elems = filterBySelector(state, queryInstalled(state, instSource.profile),
-                               args, newestOnly);
-      break;
-    }
-
-    case srcAttrPath: {
-      Value vRoot;
-      loadSourceExpr(state, instSource.nixExprPath, vRoot);
-      for (auto& i : args) {
-        Value& v(
-            *findAlongAttrPath(state, i, instSource.autoArgs.get(), vRoot));
-        getDerivations(state, v, "", instSource.autoArgs.get(), elems, true);
-      }
-      break;
-    }
-  }
-}
-
-static void printMissing(EvalState& state, DrvInfos& elems) {
-  PathSet targets;
-  for (auto& i : elems) {
-    Path drvPath = i.queryDrvPath();
-    if (!drvPath.empty()) {
-      targets.insert(drvPath);
-    } else {
-      targets.insert(i.queryOutPath());
-    }
-  }
-
-  printMissing(state.store, targets);
-}
-
-static bool keep(DrvInfo& drv) { return drv.queryMetaBool("keep", false); }
-
-static void installDerivations(Globals& globals, const Strings& args,
-                               const Path& profile) {
-  DLOG(INFO) << "installing derivations";
-
-  /* Get the set of user environment elements to be installed. */
-  DrvInfos newElems;
-  DrvInfos newElemsTmp;
-  queryInstSources(*globals.state, globals.instSource, args, newElemsTmp, true);
-
-  /* If --prebuilt-only is given, filter out source-only packages. */
-  for (auto& i : newElemsTmp) {
-    if (!globals.prebuiltOnly || isPrebuilt(*globals.state, i)) {
-      newElems.push_back(i);
-    }
-  }
-
-  StringSet newNames;
-  for (auto& i : newElems) {
-    /* `forceName' is a hack to get package names right in some
-       one-click installs, namely those where the name used in the
-       path is not the one we want (e.g., `java-front' versus
-       `java-front-0.9pre15899'). */
-    if (!globals.forceName.empty()) {
-      i.setName(globals.forceName);
-    }
-    newNames.insert(DrvName(i.queryName()).name);
-  }
-
-  while (true) {
-    std::string lockToken = optimisticLockProfile(profile);
-
-    DrvInfos allElems(newElems);
-
-    /* Add in the already installed derivations, unless they have
-       the same name as a to-be-installed element. */
-    if (!globals.removeAll) {
-      DrvInfos installedElems = queryInstalled(*globals.state, profile);
-
-      for (auto& i : installedElems) {
-        DrvName drvName(i.queryName());
-        if (!globals.preserveInstalled &&
-            newNames.find(drvName.name) != newNames.end() && !keep(i)) {
-          LOG(INFO) << "replacing old '" << i.queryName() << "'";
-        } else {
-          allElems.push_back(i);
-        }
-      }
-
-      for (auto& i : newElems) {
-        LOG(INFO) << "installing " << i.queryName();
-      }
-    }
-
-    printMissing(*globals.state, newElems);
-
-    if (globals.dryRun) {
-      return;
-    }
-
-    if (createUserEnv(*globals.state, allElems, profile,
-                      settings.envKeepDerivations, lockToken)) {
-      break;
-    }
-  }
-}
-
-static void opInstall(Globals& globals, Strings opFlags, Strings opArgs) {
-  for (auto i = opFlags.begin(); i != opFlags.end();) {
-    std::string arg = *i++;
-    if (parseInstallSourceOptions(globals, i, opFlags, arg)) {
-      ;
-    } else if (arg == "--preserve-installed" || arg == "-P") {
-      globals.preserveInstalled = true;
-    } else if (arg == "--remove-all" || arg == "-r") {
-      globals.removeAll = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % arg);
-    }
-  }
-
-  installDerivations(globals, opArgs, globals.profile);
-}
-
-typedef enum { utLt, utLeq, utEq, utAlways } UpgradeType;
-
-static void upgradeDerivations(Globals& globals, const Strings& args,
-                               UpgradeType upgradeType) {
-  DLOG(INFO) << "upgrading derivations";
-
-  /* Upgrade works as follows: we take all currently installed
-     derivations, and for any derivation matching any selector, look
-     for a derivation in the input Nix expression that has the same
-     name and a higher version number. */
-
-  while (true) {
-    std::string lockToken = optimisticLockProfile(globals.profile);
-
-    DrvInfos installedElems = queryInstalled(*globals.state, globals.profile);
-
-    /* Fetch all derivations from the input file. */
-    DrvInfos availElems;
-    queryInstSources(*globals.state, globals.instSource, args, availElems,
-                     false);
-
-    /* Go through all installed derivations. */
-    DrvInfos newElems;
-    for (auto& i : installedElems) {
-      DrvName drvName(i.queryName());
-
-      try {
-        if (keep(i)) {
-          newElems.push_back(i);
-          continue;
-        }
-
-        /* Find the derivation in the input Nix expression
-           with the same name that satisfies the version
-           constraints specified by upgradeType.  If there are
-           multiple matches, take the one with the highest
-           priority.  If there are still multiple matches,
-           take the one with the highest version.
-           Do not upgrade if it would decrease the priority. */
-        auto bestElem = availElems.end();
-        std::string bestVersion;
-        for (auto j = availElems.begin(); j != availElems.end(); ++j) {
-          if (comparePriorities(*globals.state, i, *j) > 0) {
-            continue;
-          }
-          DrvName newName(j->queryName());
-          if (newName.name == drvName.name) {
-            int d = compareVersions(drvName.version, newName.version);
-            if ((upgradeType == utLt && d < 0) ||
-                (upgradeType == utLeq && d <= 0) ||
-                (upgradeType == utEq && d == 0) || upgradeType == utAlways) {
-              long d2 = -1;
-              if (bestElem != availElems.end()) {
-                d2 = comparePriorities(*globals.state, *bestElem, *j);
-                if (d2 == 0) {
-                  d2 = compareVersions(bestVersion, newName.version);
-                }
-              }
-              if (d2 < 0 &&
-                  (!globals.prebuiltOnly || isPrebuilt(*globals.state, *j))) {
-                bestElem = j;
-                bestVersion = newName.version;
-              }
-            }
-          }
-        }
-
-        if (bestElem != availElems.end() &&
-            i.queryOutPath() != bestElem->queryOutPath()) {
-          const char* action =
-              compareVersions(drvName.version, bestVersion) <= 0
-                  ? "upgrading"
-                  : "downgrading";
-          LOG(INFO) << action << " '" << i.queryName() << "' to '"
-                    << bestElem->queryName() << "'";
-          newElems.push_back(*bestElem);
-        } else {
-          newElems.push_back(i);
-        }
-
-      } catch (Error& e) {
-        e.addPrefix(
-            fmt("while trying to find an upgrade for '%s':\n", i.queryName()));
-        throw;
-      }
-    }
-
-    printMissing(*globals.state, newElems);
-
-    if (globals.dryRun) {
-      return;
-    }
-
-    if (createUserEnv(*globals.state, newElems, globals.profile,
-                      settings.envKeepDerivations, lockToken)) {
-      break;
-    }
-  }
-}
-
-static void opUpgrade(Globals& globals, Strings opFlags, Strings opArgs) {
-  UpgradeType upgradeType = utLt;
-  for (auto i = opFlags.begin(); i != opFlags.end();) {
-    std::string arg = *i++;
-    if (parseInstallSourceOptions(globals, i, opFlags, arg)) {
-      ;
-    } else if (arg == "--lt") {
-      upgradeType = utLt;
-    } else if (arg == "--leq") {
-      upgradeType = utLeq;
-    } else if (arg == "--eq") {
-      upgradeType = utEq;
-    } else if (arg == "--always") {
-      upgradeType = utAlways;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % arg);
-    }
-  }
-
-  upgradeDerivations(globals, opArgs, upgradeType);
-}
-
-static void setMetaFlag(EvalState& state, DrvInfo& drv, const std::string& name,
-                        const std::string& value) {
-  Value* v = state.allocValue();
-  mkString(*v, value.c_str());
-  drv.setMeta(name, v);
-}
-
-static void opSetFlag(Globals& globals, Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (opArgs.size() < 2) {
-    throw UsageError("not enough arguments to '--set-flag'");
-  }
-
-  auto arg = opArgs.begin();
-  std::string flagName = *arg++;
-  std::string flagValue = *arg++;
-  DrvNames selectors = drvNamesFromArgs(Strings(arg, opArgs.end()));
-
-  while (true) {
-    std::string lockToken = optimisticLockProfile(globals.profile);
-
-    DrvInfos installedElems = queryInstalled(*globals.state, globals.profile);
-
-    /* Update all matching derivations. */
-    for (auto& i : installedElems) {
-      DrvName drvName(i.queryName());
-      for (auto& j : selectors) {
-        if (j.matches(drvName)) {
-          LOG(INFO) << "setting flag on '" << i.queryName() << "'";
-          j.hits++;
-          setMetaFlag(*globals.state, i, flagName, flagValue);
-          break;
-        }
-      }
-    }
-
-    checkSelectorUse(selectors);
-
-    /* Write the new user environment. */
-    if (createUserEnv(*globals.state, installedElems, globals.profile,
-                      settings.envKeepDerivations, lockToken)) {
-      break;
-    }
-  }
-}
-
-static void opSet(Globals& globals, Strings opFlags, Strings opArgs) {
-  auto store2 = globals.state->store.dynamic_pointer_cast<LocalFSStore>();
-  if (!store2) {
-    throw Error("--set is not supported for this Nix store");
-  }
-
-  for (auto i = opFlags.begin(); i != opFlags.end();) {
-    std::string arg = *i++;
-    if (parseInstallSourceOptions(globals, i, opFlags, arg)) {
-      ;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % arg);
-    }
-  }
-
-  DrvInfos elems;
-  queryInstSources(*globals.state, globals.instSource, opArgs, elems, true);
-
-  if (elems.size() != 1) {
-    throw Error("--set requires exactly one derivation");
-  }
-
-  DrvInfo& drv(elems.front());
-
-  if (!globals.forceName.empty()) {
-    drv.setName(globals.forceName);
-  }
-
-  if (!drv.queryDrvPath().empty()) {
-    PathSet paths = {drv.queryDrvPath()};
-    printMissing(globals.state->store, paths);
-    if (globals.dryRun) {
-      return;
-    }
-    nix::util::OkOrThrow(globals.state->store->buildPaths(
-        std::cerr, paths, globals.state->repair != 0u ? bmRepair : bmNormal));
-  } else {
-    printMissing(globals.state->store, {drv.queryOutPath()});
-    if (globals.dryRun) {
-      return;
-    }
-    globals.state->store->ensurePath(drv.queryOutPath());
-  }
-
-  DLOG(INFO) << "switching to new user environment";
-  Path generation = createGeneration(ref<LocalFSStore>(store2), globals.profile,
-                                     drv.queryOutPath());
-  switchLink(globals.profile, generation);
-}
-
-static void uninstallDerivations(Globals& globals, Strings& selectors,
-                                 Path& profile) {
-  while (true) {
-    std::string lockToken = optimisticLockProfile(profile);
-
-    DrvInfos installedElems = queryInstalled(*globals.state, profile);
-    DrvInfos newElems;
-
-    for (auto& i : installedElems) {
-      DrvName drvName(i.queryName());
-      bool found = false;
-      for (auto& j : selectors) {
-        /* !!! the repeated calls to followLinksToStorePath()
-           are expensive, should pre-compute them. */
-        if ((isPath(j) &&
-             i.queryOutPath() ==
-                 globals.state->store->followLinksToStorePath(j)) ||
-            DrvName(j).matches(drvName)) {
-          LOG(INFO) << "uninstalling '" << i.queryName() << "'";
-          found = true;
-          break;
-        }
-      }
-      if (!found) {
-        newElems.push_back(i);
-      }
-    }
-
-    if (globals.dryRun) {
-      return;
-    }
-
-    if (createUserEnv(*globals.state, newElems, profile,
-                      settings.envKeepDerivations, lockToken)) {
-      break;
-    }
-  }
-}
-
-static void opUninstall(Globals& globals, Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  uninstallDerivations(globals, opArgs, globals.profile);
-}
-
-static bool cmpChars(char a, char b) { return toupper(a) < toupper(b); }
-
-static bool cmpElemByName(const DrvInfo& a, const DrvInfo& b) {
-  auto a_name = a.queryName();
-  auto b_name = b.queryName();
-  return lexicographical_compare(a_name.begin(), a_name.end(), b_name.begin(),
-                                 b_name.end(), cmpChars);
-}
-
-using Table = std::list<Strings>;
-
-void printTable(Table& table) {
-  auto nrColumns = !table.empty() ? table.front().size() : 0;
-
-  std::vector<size_t> widths;
-  widths.resize(nrColumns);
-
-  for (auto& i : table) {
-    assert(i.size() == nrColumns);
-    Strings::iterator j;
-    size_t column;
-    for (j = i.begin(), column = 0; j != i.end(); ++j, ++column) {
-      if (j->size() > widths[column]) {
-        widths[column] = j->size();
-      }
-    }
-  }
-
-  for (auto& i : table) {
-    Strings::iterator j;
-    size_t column;
-    for (j = i.begin(), column = 0; j != i.end(); ++j, ++column) {
-      std::string s = *j;
-      replace(s.begin(), s.end(), '\n', ' ');
-      cout << s;
-      if (column < nrColumns - 1) {
-        cout << std::string(widths[column] - s.size() + 2, ' ');
-      }
-    }
-    cout << std::endl;
-  }
-}
-
-/* This function compares the version of an element against the
-   versions in the given set of elements.  `cvLess' means that only
-   lower versions are in the set, `cvEqual' means that at most an
-   equal version is in the set, and `cvGreater' means that there is at
-   least one element with a higher version in the set.  `cvUnavail'
-   means that there are no elements with the same name in the set. */
-
-using VersionDiff = enum { cvLess, cvEqual, cvGreater, cvUnavail };
-
-static VersionDiff compareVersionAgainstSet(const DrvInfo& elem,
-                                            const DrvInfos& elems,
-                                            std::string& version) {
-  DrvName name(elem.queryName());
-
-  VersionDiff diff = cvUnavail;
-  version = "?";
-
-  for (auto& i : elems) {
-    DrvName name2(i.queryName());
-    if (name.name == name2.name) {
-      int d = compareVersions(name.version, name2.version);
-      if (d < 0) {
-        diff = cvGreater;
-        version = name2.version;
-      } else if (diff != cvGreater && d == 0) {
-        diff = cvEqual;
-        version = name2.version;
-      } else if (diff != cvGreater && diff != cvEqual && d > 0) {
-        diff = cvLess;
-        if (version.empty() || compareVersions(version, name2.version) < 0) {
-          version = name2.version;
-        }
-      }
-    }
-  }
-
-  return diff;
-}
-
-static void queryJSON(Globals& globals, std::vector<DrvInfo>& elems) {
-  JSONObject topObj(cout, true);
-  for (auto& i : elems) {
-    JSONObject pkgObj = topObj.object(i.attrPath);
-
-    auto drvName = DrvName(i.queryName());
-    pkgObj.attr("name", drvName.fullName);
-    pkgObj.attr("pname", drvName.name);
-    pkgObj.attr("version", drvName.version);
-    pkgObj.attr("system", i.querySystem());
-
-    JSONObject metaObj = pkgObj.object("meta");
-    StringSet metaNames = i.queryMetaNames();
-    for (auto& j : metaNames) {
-      auto placeholder = metaObj.placeholder(j);
-      Value* v = i.queryMeta(j);
-      if (v == nullptr) {
-        LOG(ERROR) << "derivation '" << i.queryName()
-                   << "' has invalid meta attribute '" << j << "'";
-        placeholder.write(nullptr);
-      } else {
-        PathSet context;
-        printValueAsJSON(*globals.state, true, *v, placeholder, context);
-      }
-    }
-  }
-}
-
-static void opQuery(Globals& globals, Strings opFlags, Strings opArgs) {
-  Strings remaining;
-  std::string attrPath;
-
-  bool printStatus = false;
-  bool printName = true;
-  bool printAttrPath = false;
-  bool printSystem = false;
-  bool printDrvPath = false;
-  bool printOutPath = false;
-  bool printDescription = false;
-  bool printMeta = false;
-  bool compareVersions = false;
-  bool xmlOutput = false;
-  bool jsonOutput = false;
-
-  enum { sInstalled, sAvailable } source = sInstalled;
-
-  settings.readOnlyMode = true; /* makes evaluation a bit faster */
-
-  for (auto i = opFlags.begin(); i != opFlags.end();) {
-    std::string arg = *i++;
-    if (arg == "--status" || arg == "-s") {
-      printStatus = true;
-    } else if (arg == "--no-name") {
-      printName = false;
-    } else if (arg == "--system") {
-      printSystem = true;
-    } else if (arg == "--description") {
-      printDescription = true;
-    } else if (arg == "--compare-versions" || arg == "-c") {
-      compareVersions = true;
-    } else if (arg == "--drv-path") {
-      printDrvPath = true;
-    } else if (arg == "--out-path") {
-      printOutPath = true;
-    } else if (arg == "--meta") {
-      printMeta = true;
-    } else if (arg == "--installed") {
-      source = sInstalled;
-    } else if (arg == "--available" || arg == "-a") {
-      source = sAvailable;
-    } else if (arg == "--xml") {
-      xmlOutput = true;
-    } else if (arg == "--json") {
-      jsonOutput = true;
-    } else if (arg == "--attr-path" || arg == "-P") {
-      printAttrPath = true;
-    } else if (arg == "--attr" || arg == "-A") {
-      attrPath = needArg(i, opFlags, arg);
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % arg);
-    }
-  }
-
-  /* Obtain derivation information from the specified source. */
-  DrvInfos availElems;
-  DrvInfos installedElems;
-
-  if (source == sInstalled || compareVersions || printStatus) {
-    installedElems = queryInstalled(*globals.state, globals.profile);
-  }
-
-  if (source == sAvailable || compareVersions) {
-    loadDerivations(*globals.state, globals.instSource.nixExprPath,
-                    globals.instSource.systemFilter,
-                    globals.instSource.autoArgs.get(), attrPath, availElems);
-  }
-
-  DrvInfos elems_ = filterBySelector(
-      *globals.state, source == sInstalled ? installedElems : availElems,
-      opArgs, false);
-
-  DrvInfos& otherElems(source == sInstalled ? availElems : installedElems);
-
-  /* Sort them by name. */
-  /* !!! */
-  std::vector<DrvInfo> elems;
-  for (auto& i : elems_) {
-    elems.push_back(i);
-  }
-  sort(elems.begin(), elems.end(), cmpElemByName);
-
-  /* We only need to know the installed paths when we are querying
-     the status of the derivation. */
-  PathSet installed; /* installed paths */
-
-  if (printStatus) {
-    for (auto& i : installedElems) {
-      installed.insert(i.queryOutPath());
-    }
-  }
-
-  /* Query which paths have substitutes. */
-  PathSet validPaths;
-  PathSet substitutablePaths;
-  if (printStatus || globals.prebuiltOnly) {
-    PathSet paths;
-    for (auto& i : elems) {
-      try {
-        paths.insert(i.queryOutPath());
-      } catch (AssertionError& e) {
-        DLOG(WARNING) << "skipping derivation named '" << i.queryName()
-                      << "' which gives an assertion failure";
-        i.setFailed();
-      }
-    }
-    validPaths = globals.state->store->queryValidPaths(paths);
-    substitutablePaths = globals.state->store->querySubstitutablePaths(paths);
-  }
-
-  /* Print the desired columns, or XML output. */
-  if (jsonOutput) {
-    queryJSON(globals, elems);
-    return;
-  }
-
-  bool tty = isatty(STDOUT_FILENO) != 0;
-  RunPager pager;
-
-  Table table;
-  std::ostringstream dummy;
-  XMLWriter xml(true, *(xmlOutput ? &cout : &dummy));
-  XMLOpenElement xmlRoot(xml, "items");
-
-  for (auto& i : elems) {
-    try {
-      if (i.hasFailed()) {
-        continue;
-      }
-
-      // Activity act(*logger, lvlDebug, format("outputting query result '%1%'")
-      // % i.attrPath);
-
-      if (globals.prebuiltOnly &&
-          validPaths.find(i.queryOutPath()) == validPaths.end() &&
-          substitutablePaths.find(i.queryOutPath()) ==
-              substitutablePaths.end()) {
-        continue;
-      }
-
-      /* For table output. */
-      Strings columns;
-
-      /* For XML output. */
-      XMLAttrs attrs;
-
-      if (printStatus) {
-        Path outPath = i.queryOutPath();
-        bool hasSubs =
-            substitutablePaths.find(outPath) != substitutablePaths.end();
-        bool isInstalled = installed.find(outPath) != installed.end();
-        bool isValid = validPaths.find(outPath) != validPaths.end();
-        if (xmlOutput) {
-          attrs["installed"] = isInstalled ? "1" : "0";
-          attrs["valid"] = isValid ? "1" : "0";
-          attrs["substitutable"] = hasSubs ? "1" : "0";
-        } else {
-          columns.push_back(absl::StrCat((isInstalled ? "I" : "-"),
-                                         (isValid ? "P" : "-"),
-                                         (hasSubs ? "S" : "-")));
-        }
-      }
-
-      if (xmlOutput) {
-        attrs["attrPath"] = i.attrPath;
-      } else if (printAttrPath) {
-        columns.push_back(i.attrPath);
-      }
-
-      if (xmlOutput) {
-        auto drvName = DrvName(i.queryName());
-        attrs["name"] = drvName.fullName;
-        attrs["pname"] = drvName.name;
-        attrs["version"] = drvName.version;
-      } else if (printName) {
-        columns.push_back(i.queryName());
-      }
-
-      if (compareVersions) {
-        /* Compare this element against the versions of the
-           same named packages in either the set of available
-           elements, or the set of installed elements.  !!!
-           This is O(N * M), should be O(N * lg M). */
-        std::string version;
-        VersionDiff diff = compareVersionAgainstSet(i, otherElems, version);
-
-        char ch;
-        switch (diff) {
-          case cvLess:
-            ch = '>';
-            break;
-          case cvEqual:
-            ch = '=';
-            break;
-          case cvGreater:
-            ch = '<';
-            break;
-          case cvUnavail:
-            ch = '-';
-            break;
-          default:
-            abort();
-        }
-
-        if (xmlOutput) {
-          if (diff != cvUnavail) {
-            attrs["versionDiff"] = ch;
-            attrs["maxComparedVersion"] = version;
-          }
-        } else {
-          std::string column = std::to_string(ch) + " " + version;
-          if (diff == cvGreater && tty) {
-            column = ANSI_RED + column + ANSI_NORMAL;
-          }
-          columns.push_back(column);
-        }
-      }
-
-      if (xmlOutput) {
-        if (!i.querySystem().empty()) {
-          attrs["system"] = i.querySystem();
-        }
-      } else if (printSystem) {
-        columns.push_back(i.querySystem());
-      }
-
-      if (printDrvPath) {
-        std::string drvPath = i.queryDrvPath();
-        if (xmlOutput) {
-          if (!drvPath.empty()) {
-            attrs["drvPath"] = drvPath;
-          }
-        } else {
-          columns.push_back(drvPath.empty() ? "-" : drvPath);
-        }
-      }
-
-      if (printOutPath && !xmlOutput) {
-        DrvInfo::Outputs outputs = i.queryOutputs();
-        std::string s;
-        for (auto& j : outputs) {
-          if (!s.empty()) {
-            s += ';';
-          }
-          if (j.first != "out") {
-            s += j.first;
-            s += "=";
-          }
-          s += j.second;
-        }
-        columns.push_back(s);
-      }
-
-      if (printDescription) {
-        std::string descr = i.queryMetaString("description");
-        if (xmlOutput) {
-          if (!descr.empty()) {
-            attrs["description"] = descr;
-          }
-        } else {
-          columns.push_back(descr);
-        }
-      }
-
-      if (xmlOutput) {
-        if (printOutPath || printMeta) {
-          XMLOpenElement item(xml, "item", attrs);
-          if (printOutPath) {
-            DrvInfo::Outputs outputs = i.queryOutputs();
-            for (auto& j : outputs) {
-              XMLAttrs attrs2;
-              attrs2["name"] = j.first;
-              attrs2["path"] = j.second;
-              xml.writeEmptyElement("output", attrs2);
-            }
-          }
-          if (printMeta) {
-            StringSet metaNames = i.queryMetaNames();
-            for (auto& j : metaNames) {
-              XMLAttrs attrs2;
-              attrs2["name"] = j;
-              Value* v = i.queryMeta(j);
-              if (v == nullptr) {
-                LOG(ERROR) << "derivation '" << i.queryName()
-                           << "' has invalid meta attribute '" << j << "'";
-              } else {
-                if (v->type == tString) {
-                  attrs2["type"] = "string";
-                  attrs2["value"] = v->string.s;
-                  xml.writeEmptyElement("meta", attrs2);
-                } else if (v->type == tInt) {
-                  attrs2["type"] = "int";
-                  attrs2["value"] = (format("%1%") % v->integer).str();
-                  xml.writeEmptyElement("meta", attrs2);
-                } else if (v->type == tFloat) {
-                  attrs2["type"] = "float";
-                  attrs2["value"] = (format("%1%") % v->fpoint).str();
-                  xml.writeEmptyElement("meta", attrs2);
-                } else if (v->type == tBool) {
-                  attrs2["type"] = "bool";
-                  attrs2["value"] = v->boolean ? "true" : "false";
-                  xml.writeEmptyElement("meta", attrs2);
-                } else if (v->isList()) {
-                  attrs2["type"] = "strings";
-                  XMLOpenElement m(xml, "meta", attrs2);
-                  for (unsigned int j = 0; j < v->listSize(); ++j) {
-                    if ((*v->list)[j]->type != tString) {
-                      continue;
-                    }
-                    XMLAttrs attrs3;
-                    attrs3["value"] = (*v->list)[j]->string.s;
-                    xml.writeEmptyElement("string", attrs3);
-                  }
-                } else if (v->type == tAttrs) {
-                  attrs2["type"] = "strings";
-                  XMLOpenElement m(xml, "meta", attrs2);
-                  Bindings& attrs = *v->attrs;
-                  for (auto& [name, a] : attrs) {
-                    if (a.value->type != tString) {
-                      continue;
-                    }
-                    XMLAttrs attrs3;
-                    attrs3["type"] = name;
-                    attrs3["value"] = a.value->string.s;
-                    xml.writeEmptyElement("string", attrs3);
-                  }
-                }
-              }
-            }
-          }
-        } else {
-          xml.writeEmptyElement("item", attrs);
-        }
-      } else {
-        table.push_back(columns);
-      }
-
-      cout.flush();
-
-    } catch (AssertionError& e) {
-      DLOG(WARNING) << "skipping derivation named '" << i.queryName()
-                    << "' which gives an assertion failure";
-    } catch (Error& e) {
-      e.addPrefix(
-          fmt("while querying the derivation named '%1%':\n", i.queryName()));
-      throw;
-    }
-  }
-
-  if (!xmlOutput) {
-    printTable(table);
-  }
-}
-
-static void opSwitchProfile(Globals& globals, Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (opArgs.size() != 1) {
-    throw UsageError(format("exactly one argument expected"));
-  }
-
-  Path profile = absPath(opArgs.front());
-  Path profileLink = getHome() + "/.nix-profile";
-
-  switchLink(profileLink, profile);
-}
-
-static const int prevGen = -2;
-
-static void switchGeneration(Globals& globals, int dstGen) {
-  PathLocks lock;
-  lockProfile(lock, globals.profile);
-
-  int curGen;
-  Generations gens = findGenerations(globals.profile, curGen);
-
-  Generation dst;
-  for (auto& i : gens) {
-    if ((dstGen == prevGen && i.number < curGen) ||
-        (dstGen >= 0 && i.number == dstGen)) {
-      dst = i;
-    }
-  }
-
-  if (!dst) {
-    if (dstGen == prevGen) {
-      throw Error(format("no generation older than the current (%1%) exists") %
-                  curGen);
-    }
-    throw Error(format("generation %1% does not exist") % dstGen);
-  }
-
-  LOG(INFO) << "switching from generation " << curGen << " to " << dst.number;
-
-  if (globals.dryRun) {
-    return;
-  }
-
-  switchLink(globals.profile, dst.path);
-}
-
-static void opSwitchGeneration(Globals& globals, Strings opFlags,
-                               Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (opArgs.size() != 1) {
-    throw UsageError(format("exactly one argument expected"));
-  }
-
-  int dstGen;
-  if (!absl::SimpleAtoi(opArgs.front(), &dstGen)) {
-    throw UsageError(format("expected a generation number"));
-  }
-
-  switchGeneration(globals, dstGen);
-}
-
-static void opRollback(Globals& globals, Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (!opArgs.empty()) {
-    throw UsageError(format("no arguments expected"));
-  }
-
-  switchGeneration(globals, prevGen);
-}
-
-static void opListGenerations(Globals& globals, Strings opFlags,
-                              Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (!opArgs.empty()) {
-    throw UsageError(format("no arguments expected"));
-  }
-
-  PathLocks lock;
-  lockProfile(lock, globals.profile);
-
-  int curGen;
-  Generations gens = findGenerations(globals.profile, curGen);
-
-  RunPager pager;
-
-  for (auto& i : gens) {
-    tm t;
-    if (localtime_r(&i.creationTime, &t) == nullptr) {
-      throw Error("cannot convert time");
-    }
-    cout << format("%|4|   %|4|-%|02|-%|02| %|02|:%|02|:%|02|   %||\n") %
-                i.number % (t.tm_year + 1900) % (t.tm_mon + 1) % t.tm_mday %
-                t.tm_hour % t.tm_min % t.tm_sec %
-                (i.number == curGen ? "(current)" : "");
-  }
-}
-
-static void opDeleteGenerations(Globals& globals, Strings opFlags,
-                                Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-
-  if (opArgs.size() == 1 && opArgs.front() == "old") {
-    deleteOldGenerations(globals.profile, globals.dryRun);
-  } else if (opArgs.size() == 1 &&
-             opArgs.front().find('d') != std::string::npos) {
-    deleteGenerationsOlderThan(globals.profile, opArgs.front(), globals.dryRun);
-  } else if (opArgs.size() == 1 &&
-             opArgs.front().find('+') != std::string::npos) {
-    if (opArgs.front().size() < 2) {
-      throw Error(format("invalid number of generations β€˜%1%’") %
-                  opArgs.front());
-    }
-    std::string str_max = std::string(opArgs.front(), 1, opArgs.front().size());
-    int max;
-    if (!absl::SimpleAtoi(str_max, &max) || max == 0) {
-      throw Error(format("invalid number of generations to keep β€˜%1%’") %
-                  opArgs.front());
-    }
-    deleteGenerationsGreaterThan(globals.profile, max, globals.dryRun);
-  } else {
-    std::set<unsigned int> gens;
-    for (auto& i : opArgs) {
-      unsigned int n;
-      if (!absl::SimpleAtoi(i, &n)) {
-        throw UsageError(format("invalid generation number '%1%'") % i);
-      }
-      gens.insert(n);
-    }
-    deleteGenerations(globals.profile, gens, globals.dryRun);
-  }
-}
-
-static void opVersion(Globals& globals, Strings opFlags, Strings opArgs) {
-  printVersion("nix-env");
-}
-
-static int _main(int argc, char** argv) {
-  {
-    Strings opFlags;
-    Strings opArgs;
-    Operation op = nullptr;
-    RepairFlag repair = NoRepair;
-    std::string file;
-
-    Globals globals;
-
-    globals.instSource.type = srcUnknown;
-    globals.instSource.nixExprPath = getHome() + "/.nix-defexpr";
-    globals.instSource.systemFilter = "*";
-
-    if (!pathExists(globals.instSource.nixExprPath)) {
-      try {
-        createDirs(globals.instSource.nixExprPath);
-        replaceSymlink(fmt("%s/profiles/per-user/%s/channels",
-                           settings.nixStateDir, getUserName()),
-                       globals.instSource.nixExprPath + "/channels");
-        if (getuid() != 0) {
-          replaceSymlink(
-              fmt("%s/profiles/per-user/root/channels", settings.nixStateDir),
-              globals.instSource.nixExprPath + "/channels_root");
-        }
-      } catch (Error&) {
-      }
-    }
-
-    globals.dryRun = false;
-    globals.preserveInstalled = false;
-    globals.removeAll = false;
-    globals.prebuiltOnly = false;
-
-    struct MyArgs : LegacyArgs, MixEvalArgs {
-      using LegacyArgs::LegacyArgs;
-    };
-
-    MyArgs myArgs(baseNameOf(argv[0]), [&](Strings::iterator& arg,
-                                           const Strings::iterator& end) {
-      Operation oldOp = op;
-
-      if (*arg == "--help") {
-        showManPage("nix-env");
-      } else if (*arg == "--version") {
-        op = opVersion;
-      } else if (*arg == "--install" || *arg == "-i") {
-        op = opInstall;
-      } else if (*arg ==
-                 "--force-name") {  // undocumented flag for nix-install-package
-        globals.forceName = getArg(*arg, arg, end);
-      } else if (*arg == "--uninstall" || *arg == "-e") {
-        op = opUninstall;
-      } else if (*arg == "--upgrade" || *arg == "-u") {
-        op = opUpgrade;
-      } else if (*arg == "--set-flag") {
-        op = opSetFlag;
-      } else if (*arg == "--set") {
-        op = opSet;
-      } else if (*arg == "--query" || *arg == "-q") {
-        op = opQuery;
-      } else if (*arg == "--profile" || *arg == "-p") {
-        globals.profile = absPath(getArg(*arg, arg, end));
-      } else if (*arg == "--file" || *arg == "-f") {
-        file = getArg(*arg, arg, end);
-      } else if (*arg == "--switch-profile" || *arg == "-S") {
-        op = opSwitchProfile;
-      } else if (*arg == "--switch-generation" || *arg == "-G") {
-        op = opSwitchGeneration;
-      } else if (*arg == "--rollback") {
-        op = opRollback;
-      } else if (*arg == "--list-generations") {
-        op = opListGenerations;
-      } else if (*arg == "--delete-generations") {
-        op = opDeleteGenerations;
-      } else if (*arg == "--dry-run") {
-        LOG(INFO) << "(dry run; not doing anything)";
-        globals.dryRun = true;
-      } else if (*arg == "--system-filter") {
-        globals.instSource.systemFilter = getArg(*arg, arg, end);
-      } else if (*arg == "--prebuilt-only" || *arg == "-b") {
-        globals.prebuiltOnly = true;
-      } else if (*arg == "--repair") {
-        repair = Repair;
-      } else if (*arg != "" && arg->at(0) == '-') {
-        opFlags.push_back(*arg);
-        /* FIXME: hacky */
-        if (*arg == "--from-profile" ||
-            (op == opQuery && (*arg == "--attr" || *arg == "-A"))) {
-          opFlags.push_back(getArg(*arg, arg, end));
-        }
-      } else {
-        opArgs.push_back(*arg);
-      }
-
-      if ((oldOp != nullptr) && oldOp != op) {
-        throw UsageError("only one operation may be specified");
-      }
-
-      return true;
-    });
-
-    myArgs.parseCmdline(argvToStrings(argc, argv));
-
-    if (op == nullptr) {
-      throw UsageError("no operation specified");
-    }
-
-    auto store = openStore();
-
-    globals.state =
-        std::shared_ptr<EvalState>(new EvalState(myArgs.searchPath, store));
-    globals.state->repair = repair;
-
-    if (!file.empty()) {
-      globals.instSource.nixExprPath = lookupFileArg(*globals.state, file);
-    }
-
-    globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state);
-
-    if (globals.profile.empty()) {
-      globals.profile = getEnv("NIX_PROFILE").value_or("");
-    }
-
-    if (globals.profile.empty()) {
-      Path profileLink = getHome() + "/.nix-profile";
-      try {
-        if (!pathExists(profileLink)) {
-          replaceSymlink(getuid() == 0
-                             ? settings.nixStateDir + "/profiles/default"
-                             : fmt("%s/profiles/per-user/%s/profile",
-                                   settings.nixStateDir, getUserName()),
-                         profileLink);
-        }
-        globals.profile = absPath(readLink(profileLink), dirOf(profileLink));
-      } catch (Error&) {
-        globals.profile = profileLink;
-      }
-    }
-
-    op(globals, opFlags, opArgs);
-
-    globals.state->printStats();
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("nix-env", _main);
diff --git a/third_party/nix/src/nix-env/user-env.cc b/third_party/nix/src/nix-env/user-env.cc
deleted file mode 100644
index bce5c44f95..0000000000
--- a/third_party/nix/src/nix-env/user-env.cc
+++ /dev/null
@@ -1,169 +0,0 @@
-#include "nix-env/user-env.hh"
-
-#include <iostream>
-
-#include <glog/logging.h>
-
-#include "libexpr/eval-inline.hh"
-#include "libexpr/eval.hh"
-#include "libmain/shared.hh"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/profiles.hh"
-#include "libstore/store-api.hh"
-#include "libutil/status.hh"
-#include "libutil/util.hh"
-
-namespace nix {
-
-DrvInfos queryInstalled(EvalState& state, const Path& userEnv) {
-  DrvInfos elems;
-  Path manifestFile = userEnv + "/manifest.nix";
-  if (pathExists(manifestFile)) {
-    Value v;
-    state.evalFile(manifestFile, v);
-    std::unique_ptr<Bindings> bindings(Bindings::New());
-    getDerivations(state, v, "", bindings.get(), elems, false);
-  }
-  return elems;
-}
-
-bool createUserEnv(EvalState& state, DrvInfos& elems, const Path& profile,
-                   bool keepDerivations, const std::string& lockToken) {
-  /* Build the components in the user environment, if they don't
-     exist already. */
-  PathSet drvsToBuild;
-  for (auto& i : elems) {
-    if (!i.queryDrvPath().empty()) {
-      drvsToBuild.insert(i.queryDrvPath());
-    }
-  }
-
-  DLOG(INFO) << "building user environment dependencies";
-  util::OkOrThrow(state.store->buildPaths(
-      std::cerr, drvsToBuild, state.repair != 0u ? bmRepair : bmNormal));
-
-  /* Construct the whole top level derivation. */
-  PathSet references;
-  Value manifest;
-  state.mkList(manifest, elems.size());
-  unsigned int n = 0;
-  for (auto& i : elems) {
-    /* Create a pseudo-derivation containing the name, system,
-       output paths, and optionally the derivation path, as well
-       as the meta attributes. */
-    Path drvPath = keepDerivations ? i.queryDrvPath() : "";
-
-    Value* v = state.allocValue();
-    (*manifest.list)[n++] = v;
-    state.mkAttrs(*v, 16);
-
-    mkString(*state.allocAttr(*v, state.sType), "derivation");
-    mkString(*state.allocAttr(*v, state.sName), i.queryName());
-    auto system = i.querySystem();
-    if (!system.empty()) {
-      mkString(*state.allocAttr(*v, state.sSystem), system);
-    }
-    mkString(*state.allocAttr(*v, state.sOutPath), i.queryOutPath());
-    if (!drvPath.empty()) {
-      mkString(*state.allocAttr(*v, state.sDrvPath), i.queryDrvPath());
-    }
-
-    // Copy each output meant for installation.
-    DrvInfo::Outputs outputs = i.queryOutputs(true);
-    Value& vOutputs = *state.allocAttr(*v, state.sOutputs);
-    state.mkList(vOutputs, outputs.size());
-    unsigned int m = 0;
-    for (auto& j : outputs) {
-      mkString(*((*vOutputs.list)[m++] = state.allocValue()), j.first);
-      Value& vOutputs = *state.allocAttr(*v, state.symbols.Create(j.first));
-      state.mkAttrs(vOutputs, 2);
-      mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second);
-
-      /* This is only necessary when installing store paths, e.g.,
-         `nix-env -i /nix/store/abcd...-foo'. */
-      state.store->addTempRoot(j.second);
-      state.store->ensurePath(j.second);
-
-      references.insert(j.second);
-    }
-
-    // Copy the meta attributes.
-    Value& vMeta = *state.allocAttr(*v, state.sMeta);
-    state.mkAttrs(vMeta, 16);
-    StringSet metaNames = i.queryMetaNames();
-    for (auto& j : metaNames) {
-      Value* v = i.queryMeta(j);
-      if (v == nullptr) {
-        continue;
-      }
-      vMeta.attrs->push_back(Attr(state.symbols.Create(j), v));
-    }
-
-    if (!drvPath.empty()) {
-      references.insert(drvPath);
-    }
-  }
-
-  /* Also write a copy of the list of user environment elements to
-     the store; we need it for future modifications of the
-     environment. */
-  Path manifestFile = state.store->addTextToStore(
-      "env-manifest.nix", (format("%1%") % manifest).str(), references);
-
-  /* Get the environment builder expression. */
-  Value envBuilder;
-  state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder);
-
-  /* Construct a Nix expression that calls the user environment
-     builder with the manifest as argument. */
-  Value args;
-  Value topLevel;
-  state.mkAttrs(args, 3);
-  mkString(*state.allocAttr(args, state.symbols.Create("manifest")),
-           manifestFile, {manifestFile});
-  args.attrs->push_back(Attr(state.symbols.Create("derivations"), &manifest));
-  mkApp(topLevel, envBuilder, args);
-
-  /* Evaluate it. */
-  DLOG(INFO) << "evaluating user environment builder";
-  state.forceValue(topLevel);
-  PathSet context;
-  Attr& aDrvPath(topLevel.attrs->find(state.sDrvPath)->second);
-  Path topLevelDrv =
-      state.coerceToPath(aDrvPath.pos != nullptr ? *(aDrvPath.pos) : noPos,
-                         *(aDrvPath.value), context);
-  Attr& aOutPath(topLevel.attrs->find(state.sOutPath)->second);
-  Path topLevelOut =
-      state.coerceToPath(aOutPath.pos != nullptr ? *(aOutPath.pos) : noPos,
-                         *(aOutPath.value), context);
-
-  /* Realise the resulting store expression. */
-  DLOG(INFO) << "building user environment";
-  util::OkOrThrow(state.store->buildPaths(
-      std::cerr, {topLevelDrv}, state.repair != 0u ? bmRepair : bmNormal));
-
-  /* Switch the current user environment to the output path. */
-  auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
-
-  if (store2) {
-    PathLocks lock;
-    lockProfile(lock, profile);
-
-    Path lockTokenCur = optimisticLockProfile(profile);
-    if (lockToken != lockTokenCur) {
-      LOG(WARNING) << "profile '" << profile
-                   << "' changed while we were busy; restarting";
-      return false;
-    }
-
-    DLOG(INFO) << "switching to new user environment";
-    Path generation =
-        createGeneration(ref<LocalFSStore>(store2), profile, topLevelOut);
-    switchLink(profile, generation);
-  }
-
-  return true;
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix-env/user-env.hh b/third_party/nix/src/nix-env/user-env.hh
deleted file mode 100644
index 95919a6c87..0000000000
--- a/third_party/nix/src/nix-env/user-env.hh
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-
-#include "libexpr/get-drvs.hh"
-
-namespace nix {
-
-DrvInfos queryInstalled(EvalState& state, const Path& userEnv);
-
-bool createUserEnv(EvalState& state, DrvInfos& elems, const Path& profile,
-                   bool keepDerivations, const std::string& lockToken);
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix-instantiate/nix-instantiate.cc b/third_party/nix/src/nix-instantiate/nix-instantiate.cc
deleted file mode 100644
index 236037299d..0000000000
--- a/third_party/nix/src/nix-instantiate/nix-instantiate.cc
+++ /dev/null
@@ -1,219 +0,0 @@
-#include <iostream>
-#include <map>
-
-#include "libexpr/attr-path.hh"
-#include "libexpr/common-eval-args.hh"
-#include "libexpr/eval-inline.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/get-drvs.hh"
-#include "libexpr/value-to-json.hh"
-#include "libexpr/value-to-xml.hh"
-#include "libmain/shared.hh"
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libutil/util.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-
-static Path gcRoot;
-static int rootNr = 0;
-static bool indirectRoot = false;
-
-enum OutputKind { okPlain, okXML, okJSON };
-
-void processExpr(EvalState& state, const Strings& attrPaths, bool parseOnly,
-                 bool strict, Bindings* autoArgs, bool evalOnly,
-                 OutputKind output, bool location, Expr* e) {
-  if (parseOnly) {
-    std::cout << format("%1%\n") % *e;
-    return;
-  }
-
-  Value vRoot;
-  state.eval(e, vRoot);
-
-  for (auto& i : attrPaths) {
-    Value& v(*findAlongAttrPath(state, i, autoArgs, vRoot));
-    state.forceValue(v);
-
-    PathSet context;
-    if (evalOnly) {
-      Value vRes;
-      if (autoArgs->empty()) {
-        vRes = v;
-      } else {
-        state.autoCallFunction(autoArgs, v, vRes);
-      }
-      if (output == okXML) {
-        printValueAsXML(state, strict, location, vRes, std::cout, context);
-      } else if (output == okJSON) {
-        printValueAsJSON(state, strict, vRes, std::cout, context);
-      } else {
-        if (strict) {
-          state.forceValueDeep(vRes);
-        }
-        std::cout << vRes << std::endl;
-      }
-    } else {
-      DrvInfos drvs;
-      getDerivations(state, v, "", autoArgs, drvs, false);
-      for (auto& i : drvs) {
-        Path drvPath = i.queryDrvPath();
-
-        /* What output do we want? */
-        std::string outputName = i.queryOutputName();
-        if (outputName.empty()) {
-          throw Error(
-              format("derivation '%1%' lacks an 'outputName' attribute ") %
-              drvPath);
-        }
-
-        if (gcRoot.empty()) {
-          printGCWarning();
-        } else {
-          Path rootName = indirectRoot ? absPath(gcRoot) : gcRoot;
-          if (++rootNr > 1) {
-            rootName += "-" + std::to_string(rootNr);
-          }
-          auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
-          if (store2) {
-            drvPath = store2->addPermRoot(drvPath, rootName, indirectRoot);
-          }
-        }
-        std::cout << format("%1%%2%\n") % drvPath %
-                         (outputName != "out" ? "!" + outputName : "");
-      }
-    }
-  }
-}
-
-static int _main(int argc, char** argv) {
-  {
-    Strings files;
-    bool readStdin = false;
-    bool fromArgs = false;
-    bool findFile = false;
-    bool evalOnly = false;
-    bool parseOnly = false;
-    bool traceFileAccess = false;
-    OutputKind outputKind = okPlain;
-    bool xmlOutputSourceLocation = true;
-    bool strict = false;
-    Strings attrPaths;
-    bool wantsReadWrite = false;
-    RepairFlag repair = NoRepair;
-
-    struct MyArgs : LegacyArgs, MixEvalArgs {
-      using LegacyArgs::LegacyArgs;
-    };
-
-    MyArgs myArgs(baseNameOf(argv[0]),
-                  [&](Strings::iterator& arg, const Strings::iterator& end) {
-                    if (*arg == "--help") {
-                      showManPage("nix-instantiate");
-                    } else if (*arg == "--version") {
-                      printVersion("nix-instantiate");
-                    } else if (*arg == "-") {
-                      readStdin = true;
-                    } else if (*arg == "--expr" || *arg == "-E") {
-                      fromArgs = true;
-                    } else if (*arg == "--eval" || *arg == "--eval-only") {
-                      evalOnly = true;
-                    } else if (*arg == "--read-write-mode") {
-                      wantsReadWrite = true;
-                    } else if (*arg == "--parse" || *arg == "--parse-only") {
-                      parseOnly = evalOnly = true;
-                    } else if (*arg == "--find-file") {
-                      findFile = true;
-                    } else if (*arg == "--attr" || *arg == "-A") {
-                      attrPaths.push_back(getArg(*arg, arg, end));
-                    } else if (*arg == "--add-root") {
-                      gcRoot = getArg(*arg, arg, end);
-                    } else if (*arg == "--indirect") {
-                      indirectRoot = true;
-                    } else if (*arg == "--xml") {
-                      outputKind = okXML;
-                    } else if (*arg == "--json") {
-                      outputKind = okJSON;
-                    } else if (*arg == "--no-location") {
-                      xmlOutputSourceLocation = false;
-                    } else if (*arg == "--strict") {
-                      strict = true;
-                    } else if (*arg == "--repair") {
-                      repair = Repair;
-                    } else if (*arg == "--dry-run") {
-                      settings.readOnlyMode = true;
-                    } else if (*arg == "--trace-file-access") {
-                      traceFileAccess = true;
-                    } else if (*arg == "--trace-file-access=true") {
-                      traceFileAccess = true;
-                    } else if (*arg == "--trace-file-access=false") {
-                      traceFileAccess = false;
-                    } else if (*arg == "--notrace-file-access") {
-                      traceFileAccess = false;
-                    } else if (*arg != "" && arg->at(0) == '-') {
-                      return false;
-                    } else {
-                      files.push_back(*arg);
-                    }
-                    return true;
-                  });
-
-    myArgs.parseCmdline(argvToStrings(argc, argv));
-
-    if (evalOnly && !wantsReadWrite) {
-      settings.readOnlyMode = true;
-    }
-
-    auto store = openStore();
-
-    auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
-    state->repair = repair;
-    if (traceFileAccess) {
-      state->EnableFileAccessTracing([](const Path& path) {
-        std::cerr << "trace: depot-scan: " << path << "\n";
-      });
-    }
-
-    std::unique_ptr<Bindings> autoArgs = myArgs.getAutoArgs(*state);
-
-    if (attrPaths.empty()) {
-      attrPaths = {""};
-    }
-
-    if (findFile) {
-      for (auto& i : files) {
-        Path p = state->findFile(i);
-        if (p.empty()) {
-          throw Error(format("unable to find '%1%'") % i);
-        }
-        std::cout << p << std::endl;
-      }
-      return 0;
-    }
-
-    if (readStdin) {
-      Expr* e = state->parseStdin();
-      processExpr(*state, attrPaths, parseOnly, strict, autoArgs.get(),
-                  evalOnly, outputKind, xmlOutputSourceLocation, e);
-    } else if (files.empty() && !fromArgs) {
-      files.push_back("./default.nix");
-    }
-
-    for (auto& i : files) {
-      Expr* e = fromArgs
-                    ? state->parseExprFromString(i, absPath("."))
-                    : state->parseExprFromFile(resolveExprPath(
-                          state->checkSourcePath(lookupFileArg(*state, i))));
-      processExpr(*state, attrPaths, parseOnly, strict, autoArgs.get(),
-                  evalOnly, outputKind, xmlOutputSourceLocation, e);
-    }
-
-    state->printStats();
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("nix-instantiate", _main);
diff --git a/third_party/nix/src/nix-prefetch-url/nix-prefetch-url.cc b/third_party/nix/src/nix-prefetch-url/nix-prefetch-url.cc
deleted file mode 100644
index b61a38a7f1..0000000000
--- a/third_party/nix/src/nix-prefetch-url/nix-prefetch-url.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-#include <iostream>
-
-#include <absl/strings/match.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "libexpr/attr-path.hh"
-#include "libexpr/common-eval-args.hh"
-#include "libexpr/eval-inline.hh"
-#include "libexpr/eval.hh"
-#include "libmain/shared.hh"
-#include "libstore/download.hh"
-#include "libstore/store-api.hh"
-#include "libutil/finally.hh"
-#include "libutil/hash.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-
-/* If β€˜uri’ starts with β€˜mirror://’, then resolve it using the list of
-   mirrors defined in Nixpkgs. */
-std::string resolveMirrorUri(EvalState& state, std::string uri) {
-  if (std::string(uri, 0, 9) != "mirror://") {
-    return uri;
-  }
-
-  std::string s(uri, 9);
-  auto p = s.find('/');
-  if (p == std::string::npos) {
-    throw Error("invalid mirror URI");
-  }
-  std::string mirrorName(s, 0, p);
-
-  Value vMirrors;
-  state.eval(
-      state.parseExprFromString(
-          "import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."),
-      vMirrors);
-  state.forceAttrs(vMirrors);
-
-  auto mirrorList = vMirrors.attrs->find(state.symbols.Create(mirrorName));
-  if (mirrorList == vMirrors.attrs->end()) {
-    throw Error(format("unknown mirror name '%1%'") % mirrorName);
-  }
-  state.forceList(*mirrorList->second.value);
-
-  if (mirrorList->second.value->listSize() < 1) {
-    throw Error(format("mirror URI '%1%' did not expand to anything") % uri);
-  }
-
-  std::string mirror = state.forceString(*(*mirrorList->second.value->list)[0]);
-  return mirror + (absl::EndsWith(mirror, "/") ? "" : "/") +
-         std::string(s, p + 1);
-}
-
-static int _main(int argc, char** argv) {
-  {
-    HashType ht = htSHA256;
-    std::vector<std::string> args;
-    bool printPath = getEnv("PRINT_PATH").has_value();
-    bool fromExpr = false;
-    std::string attrPath;
-    bool unpack = false;
-    std::string name;
-
-    struct MyArgs : LegacyArgs, MixEvalArgs {
-      using LegacyArgs::LegacyArgs;
-    };
-
-    MyArgs myArgs(baseNameOf(argv[0]),
-                  [&](Strings::iterator& arg, const Strings::iterator& end) {
-                    if (*arg == "--help") {
-                      showManPage("nix-prefetch-url");
-                    } else if (*arg == "--version") {
-                      printVersion("nix-prefetch-url");
-                    } else if (*arg == "--type") {
-                      std::string s = getArg(*arg, arg, end);
-                      ht = parseHashType(s);
-                      if (ht == htUnknown) {
-                        throw UsageError(format("unknown hash type '%1%'") % s);
-                      }
-                    } else if (*arg == "--print-path") {
-                      printPath = true;
-                    } else if (*arg == "--attr" || *arg == "-A") {
-                      fromExpr = true;
-                      attrPath = getArg(*arg, arg, end);
-                    } else if (*arg == "--unpack") {
-                      unpack = true;
-                    } else if (*arg == "--name") {
-                      name = getArg(*arg, arg, end);
-                    } else if (*arg != "" && arg->at(0) == '-') {
-                      return false;
-                    } else {
-                      args.push_back(*arg);
-                    }
-                    return true;
-                  });
-
-    myArgs.parseCmdline(argvToStrings(argc, argv));
-
-    if (args.size() > 2) {
-      throw UsageError("too many arguments");
-    }
-
-    auto store = openStore();
-    auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
-
-    std::unique_ptr<Bindings> autoArgs = myArgs.getAutoArgs(*state);
-
-    /* If -A is given, get the URI from the specified Nix
-       expression. */
-    std::string uri;
-    if (!fromExpr) {
-      if (args.empty()) {
-        throw UsageError("you must specify a URI");
-      }
-      uri = args[0];
-    } else {
-      Path path =
-          resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0]));
-      Value vRoot;
-      state->evalFile(path, vRoot);
-      Value& v(*findAlongAttrPath(*state, attrPath, autoArgs.get(), vRoot));
-      state->forceAttrs(v);
-
-      /* Extract the URI. */
-      auto attr = v.attrs->find(state->symbols.Create("urls"));
-      if (attr == v.attrs->end()) {
-        throw Error("attribute set does not contain a 'urls' attribute");
-      }
-      state->forceList(*attr->second.value);
-      if (attr->second.value->listSize() < 1) {
-        throw Error("'urls' list is empty");
-      }
-      uri = state->forceString(*(*attr->second.value->list)[0]);
-
-      /* Extract the hash mode. */
-      attr = v.attrs->find(state->symbols.Create("outputHashMode"));
-      if (attr == v.attrs->end()) {
-        LOG(WARNING) << "this does not look like a fetchurl call";
-      } else {
-        unpack = state->forceString(*attr->second.value) == "recursive";
-      }
-
-      /* Extract the name. */
-      if (name.empty()) {
-        attr = v.attrs->find(state->symbols.Create("name"));
-        if (attr != v.attrs->end()) {
-          name = state->forceString(*attr->second.value);
-        }
-      }
-    }
-
-    /* Figure out a name in the Nix store. */
-    if (name.empty()) {
-      name = baseNameOf(uri);
-    }
-    if (name.empty()) {
-      throw Error(format("cannot figure out file name for '%1%'") % uri);
-    }
-
-    /* If an expected hash is given, the file may already exist in
-       the store. */
-    Hash hash;
-    Hash expectedHash(ht);
-    Path storePath;
-    if (args.size() == 2) {
-      auto expectedHash_ = Hash::deserialize(args[1], ht);
-      expectedHash = Hash::unwrap_throw(expectedHash);
-      storePath = store->makeFixedOutputPath(unpack, expectedHash, name);
-      if (store->isValidPath(storePath)) {
-        hash = expectedHash;
-      } else {
-        storePath.clear();
-      }
-    }
-
-    if (storePath.empty()) {
-      auto actualUri = resolveMirrorUri(*state, uri);
-
-      AutoDelete tmpDir(createTempDir(), true);
-      Path tmpFile = Path(tmpDir) + "/tmp";
-
-      /* Download the file. */
-      {
-        AutoCloseFD fd(
-            open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0600));
-        if (!fd) {
-          throw SysError("creating temporary file '%s'", tmpFile);
-        }
-
-        FdSink sink(fd.get());
-
-        DownloadRequest req(actualUri);
-        req.decompress = false;
-        getDownloader()->download(std::move(req), sink);
-      }
-
-      /* Optionally unpack the file. */
-      if (unpack) {
-        LOG(INFO) << "unpacking...";
-        Path unpacked = Path(tmpDir) + "/unpacked";
-        createDirs(unpacked);
-        if (absl::EndsWith(baseNameOf(uri), ".zip")) {
-          runProgram("unzip", true, {"-qq", tmpFile, "-d", unpacked});
-        } else {
-          // FIXME: this requires GNU tar for decompression.
-          runProgram("tar", true, {"xf", tmpFile, "-C", unpacked});
-        }
-
-        /* If the archive unpacks to a single file/directory, then use
-           that as the top-level. */
-        auto entries = readDirectory(unpacked);
-        if (entries.size() == 1) {
-          tmpFile = unpacked + "/" + entries[0].name;
-        } else {
-          tmpFile = unpacked;
-        }
-      }
-
-      /* FIXME: inefficient; addToStore() will also hash
-         this. */
-      hash = unpack ? hashPath(ht, tmpFile).first : hashFile(ht, tmpFile);
-
-      if (expectedHash != Hash(ht) && expectedHash != hash) {
-        throw Error(format("hash mismatch for '%1%'") % uri);
-      }
-
-      /* Copy the file to the Nix store. FIXME: if RemoteStore
-         implemented addToStoreFromDump() and downloadFile()
-         supported a sink, we could stream the download directly
-         into the Nix store. */
-      storePath = store->addToStore(name, tmpFile, unpack, ht);
-
-      assert(storePath == store->makeFixedOutputPath(unpack, hash, name));
-    }
-
-    if (!printPath) {
-      LOG(INFO) << "path is '" << storePath << "'";
-    }
-
-    std::cout << printHash16or32(hash) << std::endl;
-    if (printPath) {
-      std::cout << storePath << std::endl;
-    }
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("nix-prefetch-url", _main);
diff --git a/third_party/nix/src/nix-store/dotgraph.cc b/third_party/nix/src/nix-store/dotgraph.cc
deleted file mode 100644
index 2500b8f4b0..0000000000
--- a/third_party/nix/src/nix-store/dotgraph.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-#include "nix-store/dotgraph.hh"
-
-#include <iostream>
-
-#include "libstore/store-api.hh"
-#include "libutil/util.hh"
-
-using std::cout;
-
-namespace nix {
-
-static std::string dotQuote(const std::string& s) { return "\"" + s + "\""; }
-
-static std::string nextColour() {
-  static int n = 0;
-  static std::string colours[] = {"black", "red",     "green",
-                                  "blue",  "magenta", "burlywood"};
-  return colours[n++ % (sizeof(colours) / sizeof(std::string))];
-}
-
-static std::string makeEdge(const std::string& src, const std::string& dst) {
-  format f = format("%1% -> %2% [color = %3%];\n") % dotQuote(src) %
-             dotQuote(dst) % dotQuote(nextColour());
-  return f.str();
-}
-
-static std::string makeNode(const std::string& id, const std::string& label,
-                            const std::string& colour) {
-  format f = format(
-                 "%1% [label = %2%, shape = box, "
-                 "style = filled, fillcolor = %3%];\n") %
-             dotQuote(id) % dotQuote(label) % dotQuote(colour);
-  return f.str();
-}
-
-static std::string symbolicName(const std::string& path) {
-  std::string p = baseNameOf(path);
-  return std::string(p, p.find('-') + 1);
-}
-
-#if 0
-std::string pathLabel(const Path & nePath, const std::string & elemPath)
-{
-    return (std::string) nePath + "-" + elemPath;
-}
-
-
-void printClosure(const Path & nePath, const StoreExpr & fs)
-{
-    PathSet workList(fs.closure.roots);
-    PathSet doneSet;
-
-    for (PathSet::iterator i = workList.begin(); i != workList.end(); ++i) {
-        cout << makeEdge(pathLabel(nePath, *i), nePath);
-    }
-
-    while (!workList.empty()) {
-        Path path = *(workList.begin());
-        workList.erase(path);
-
-        if (doneSet.find(path) == doneSet.end()) {
-            doneSet.insert(path);
-
-            ClosureElems::const_iterator elem = fs.closure.elems.find(path);
-            if (elem == fs.closure.elems.end())
-                throw Error(format("bad closure, missing path '%1%'") % path);
-
-            for (StringSet::const_iterator i = elem->second.refs.begin();
-                 i != elem->second.refs.end(); ++i)
-            {
-                workList.insert(*i);
-                cout << makeEdge(pathLabel(nePath, *i), pathLabel(nePath, path));
-            }
-
-            cout << makeNode(pathLabel(nePath, path),
-                symbolicName(path), "#ff0000");
-        }
-    }
-}
-#endif
-
-void printDotGraph(const ref<Store>& store, const PathSet& roots) {
-  PathSet workList(roots);
-  PathSet doneSet;
-
-  cout << "digraph G {\n";
-
-  while (!workList.empty()) {
-    Path path = *(workList.begin());
-    workList.erase(path);
-
-    if (doneSet.find(path) != doneSet.end()) {
-      continue;
-    }
-    doneSet.insert(path);
-
-    cout << makeNode(path, symbolicName(path), "#ff0000");
-
-    for (auto& p : store->queryPathInfo(path)->references) {
-      if (p != path) {
-        workList.insert(p);
-        cout << makeEdge(p, path);
-      }
-    }
-
-#if 0
-        StoreExpr ne = storeExprFromPath(path);
-
-        string label, colour;
-
-        if (ne.type == StoreExpr::neDerivation) {
-            for (PathSet::iterator i = ne.derivation.inputs.begin();
-                 i != ne.derivation.inputs.end(); ++i)
-            {
-                workList.insert(*i);
-                cout << makeEdge(*i, path);
-            }
-
-            label = "derivation";
-            colour = "#00ff00";
-            for (StringPairs::iterator i = ne.derivation.env.begin();
-                 i != ne.derivation.env.end(); ++i)
-                if (i->first == "name") { label = i->second; }
-        }
-
-        else if (ne.type == StoreExpr::neClosure) {
-            label = "<closure>";
-            colour = "#00ffff";
-            printClosure(path, ne);
-        }
-
-        else abort();
-
-        cout << makeNode(path, label, colour);
-#endif
-  }
-
-  cout << "}\n";
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix-store/dotgraph.hh b/third_party/nix/src/nix-store/dotgraph.hh
deleted file mode 100644
index 40c2686854..0000000000
--- a/third_party/nix/src/nix-store/dotgraph.hh
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "libutil/types.hh"
-
-namespace nix {
-
-class Store;
-
-void printDotGraph(const ref<Store>& store, const PathSet& roots);
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix-store/graphml.cc b/third_party/nix/src/nix-store/graphml.cc
deleted file mode 100644
index ada4aaf6d0..0000000000
--- a/third_party/nix/src/nix-store/graphml.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-#include "nix-store/graphml.hh"
-
-#include <iostream>
-
-#include "libstore/derivations.hh"
-#include "libstore/store-api.hh"
-#include "libutil/util.hh"
-
-using std::cout;
-
-namespace nix {
-
-static inline const std::string& xmlQuote(const std::string& s) {
-  // Luckily, store paths shouldn't contain any character that needs to be
-  // quoted.
-  return s;
-}
-
-static std::string symbolicName(const std::string& path) {
-  std::string p = baseNameOf(path);
-  return std::string(p, p.find('-') + 1);
-}
-
-static std::string makeEdge(const std::string& src, const std::string& dst) {
-  return fmt("  <edge source=\"%1%\" target=\"%2%\"/>\n", xmlQuote(src),
-             xmlQuote(dst));
-}
-
-static std::string makeNode(const ValidPathInfo& info) {
-  return fmt(
-      "  <node id=\"%1%\">\n"
-      "    <data key=\"narSize\">%2%</data>\n"
-      "    <data key=\"name\">%3%</data>\n"
-      "    <data key=\"type\">%4%</data>\n"
-      "  </node>\n",
-      info.path, info.narSize, symbolicName(info.path),
-      (isDerivation(info.path) ? "derivation" : "output-path"));
-}
-
-void printGraphML(const ref<Store>& store, const PathSet& roots) {
-  PathSet workList(roots);
-  PathSet doneSet;
-  std::pair<PathSet::iterator, bool> ret;
-
-  cout << "<?xml version='1.0' encoding='utf-8'?>\n"
-       << "<graphml xmlns='http://graphml.graphdrawing.org/xmlns'\n"
-       << "    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n"
-       << "    "
-          "xsi:schemaLocation='http://graphml.graphdrawing.org/xmlns/1.0/"
-          "graphml.xsd'>\n"
-       << "<key id='narSize' for='node' attr.name='narSize' attr.type='int'/>"
-       << "<key id='name' for='node' attr.name='name' attr.type='string'/>"
-       << "<key id='type' for='node' attr.name='type' attr.type='string'/>"
-       << "<graph id='G' edgedefault='directed'>\n";
-
-  while (!workList.empty()) {
-    Path path = *(workList.begin());
-    workList.erase(path);
-
-    ret = doneSet.insert(path);
-    if (!ret.second) {
-      continue;
-    }
-
-    ValidPathInfo info = *(store->queryPathInfo(path));
-    cout << makeNode(info);
-
-    for (auto& p : store->queryPathInfo(path)->references) {
-      if (p != path) {
-        workList.insert(p);
-        cout << makeEdge(path, p);
-      }
-    }
-  }
-
-  cout << "</graph>\n";
-  cout << "</graphml>\n";
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix-store/graphml.hh b/third_party/nix/src/nix-store/graphml.hh
deleted file mode 100644
index be07904d0f..0000000000
--- a/third_party/nix/src/nix-store/graphml.hh
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "libutil/types.hh"
-
-namespace nix {
-
-class Store;
-
-void printGraphML(const ref<Store>& store, const PathSet& roots);
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix-store/nix-store.cc b/third_party/nix/src/nix-store/nix-store.cc
deleted file mode 100644
index 532f60b7b7..0000000000
--- a/third_party/nix/src/nix-store/nix-store.cc
+++ /dev/null
@@ -1,1302 +0,0 @@
-#include <algorithm>
-#include <cstdio>
-#include <iostream>
-
-#include <absl/strings/escaping.h>
-#include <fcntl.h>
-#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "libmain/shared.hh"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/local-store.hh"
-#include "libstore/serve-protocol.hh"
-#include "libstore/worker-protocol.hh"
-#include "libutil/archive.hh"
-#include "libutil/monitor-fd.hh"
-#include "libutil/status.hh"
-#include "libutil/util.hh"
-#include "nix-store/dotgraph.hh"
-#include "nix-store/graphml.hh"
-#include "nix/legacy.hh"
-
-#if HAVE_SODIUM
-#include <sodium.h>
-#endif
-
-using namespace nix;
-using std::cin;
-using std::cout;
-
-// TODO(tazjin): clang-tidy's performance lints don't like this, but
-// the automatic fixes fail (it seems that some of the ops want to own
-// the args for whatever reason)
-using Operation = void (*)(Strings, Strings);
-
-static Path gcRoot;
-static int rootNr = 0;
-static bool indirectRoot = false;
-static bool noOutput = false;
-static std::shared_ptr<Store> store;
-
-ref<LocalStore> ensureLocalStore() {
-  auto store2 = std::dynamic_pointer_cast<LocalStore>(store);
-  if (!store2) {
-    throw Error("you don't have sufficient rights to use this command");
-  }
-  return ref<LocalStore>(store2);
-}
-
-static Path useDeriver(Path path) {
-  if (isDerivation(path)) {
-    return path;
-  }
-  Path drvPath = store->queryPathInfo(path)->deriver;
-  if (drvPath.empty()) {
-    throw Error(format("deriver of path '%1%' is not known") % path);
-  }
-  return drvPath;
-}
-
-/* Realise the given path.  For a derivation that means build it; for
-   other paths it means ensure their validity. */
-static PathSet realisePath(Path path, bool build = true) {
-  DrvPathWithOutputs p = parseDrvPathWithOutputs(path);
-
-  auto store2 = std::dynamic_pointer_cast<LocalFSStore>(store);
-
-  if (isDerivation(p.first)) {
-    if (build) {
-      util::OkOrThrow(store->buildPaths(std::cerr, {path}));
-    }
-    Derivation drv = store->derivationFromPath(p.first);
-    rootNr++;
-
-    if (p.second.empty()) {
-      for (auto& i : drv.outputs) {
-        p.second.insert(i.first);
-      }
-    }
-
-    PathSet outputs;
-    for (auto& j : p.second) {
-      auto i = drv.outputs.find(j);
-      if (i == drv.outputs.end()) {
-        throw Error(
-            format("derivation '%1%' does not have an output named '%2%'") %
-            p.first % j);
-      }
-      Path outPath = i->second.path;
-      if (store2) {
-        if (gcRoot.empty()) {
-          printGCWarning();
-        } else {
-          Path rootName = gcRoot;
-          if (rootNr > 1) {
-            rootName += "-" + std::to_string(rootNr);
-          }
-          if (i->first != "out") {
-            rootName += "-" + i->first;
-          }
-          outPath = store2->addPermRoot(outPath, rootName, indirectRoot);
-        }
-      }
-      outputs.insert(outPath);
-    }
-    return outputs;
-  }
-
-  if (build) {
-    store->ensurePath(path);
-  } else if (!store->isValidPath(path)) {
-    throw Error(format("path '%1%' does not exist and cannot be created") %
-                path);
-  }
-  if (store2) {
-    if (gcRoot.empty()) {
-      printGCWarning();
-    } else {
-      Path rootName = gcRoot;
-      rootNr++;
-      if (rootNr > 1) {
-        rootName += "-" + std::to_string(rootNr);
-      }
-      path = store2->addPermRoot(path, rootName, indirectRoot);
-    }
-  }
-  return {path};
-}
-
-/* Realise the given paths. */
-static void opRealise(Strings opFlags, Strings opArgs) {
-  bool dryRun = false;
-  BuildMode buildMode = bmNormal;
-  bool ignoreUnknown = false;
-
-  for (auto& i : opFlags) {
-    if (i == "--dry-run") {
-      dryRun = true;
-    } else if (i == "--repair") {
-      buildMode = bmRepair;
-    } else if (i == "--check") {
-      buildMode = bmCheck;
-    } else if (i == "--ignore-unknown") {
-      ignoreUnknown = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % i);
-    }
-  }
-
-  Paths paths;
-  for (auto& i : opArgs) {
-    DrvPathWithOutputs p = parseDrvPathWithOutputs(i);
-    paths.push_back(makeDrvPathWithOutputs(
-        store->followLinksToStorePath(p.first), p.second));
-  }
-
-  unsigned long long downloadSize;
-  unsigned long long narSize;
-  PathSet willBuild;
-  PathSet willSubstitute;
-  PathSet unknown;
-  store->queryMissing(PathSet(paths.begin(), paths.end()), willBuild,
-                      willSubstitute, unknown, downloadSize, narSize);
-
-  if (ignoreUnknown) {
-    Paths paths2;
-    for (auto& i : paths) {
-      if (unknown.find(i) == unknown.end()) {
-        paths2.push_back(i);
-      }
-    }
-    paths = paths2;
-    unknown = PathSet();
-  }
-
-  if (settings.printMissing) {
-    printMissing(ref<Store>(store), willBuild, willSubstitute, unknown,
-                 downloadSize, narSize);
-  }
-
-  if (dryRun) {
-    return;
-  }
-
-  /* Build all paths at the same time to exploit parallelism. */
-  util::OkOrThrow(store->buildPaths(
-      std::cerr, PathSet(paths.begin(), paths.end()), buildMode));
-
-  if (!ignoreUnknown) {
-    for (auto& i : paths) {
-      PathSet paths = realisePath(i, false);
-      if (!noOutput) {
-        for (auto& j : paths) {
-          cout << format("%1%\n") % j;
-        }
-      }
-    }
-  }
-}
-
-/* Add files to the Nix store and print the resulting paths. */
-static void opAdd(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("unknown flag");
-  }
-
-  for (auto& i : opArgs) {
-    cout << format("%1%\n") % store->addToStore(baseNameOf(i), i);
-  }
-}
-
-/* Preload the output of a fixed-output derivation into the Nix
-   store. */
-static void opAddFixed(Strings opFlags, Strings opArgs) {
-  bool recursive = false;
-
-  for (auto& i : opFlags) {
-    if (i == "--recursive") {
-      recursive = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % i);
-    }
-  }
-
-  if (opArgs.empty()) {
-    throw UsageError("first argument must be hash algorithm");
-  }
-
-  HashType hashAlgo = parseHashType(opArgs.front());
-  opArgs.pop_front();
-
-  for (auto& i : opArgs) {
-    cout << format("%1%\n") %
-                store->addToStore(baseNameOf(i), i, recursive, hashAlgo);
-  }
-}
-
-/* Hack to support caching in `nix-prefetch-url'. */
-static void opPrintFixedPath(Strings opFlags, Strings opArgs) {
-  bool recursive = false;
-
-  for (auto i : opFlags) {
-    if (i == "--recursive") {
-      recursive = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % i);
-    }
-  }
-
-  if (opArgs.size() != 3) {
-    throw UsageError(format("'--print-fixed-path' requires three arguments"));
-  }
-
-  auto i = opArgs.begin();
-  HashType hashAlgo = parseHashType(*i++);
-  std::string hash = *i++;
-  std::string name = *i++;
-
-  auto hash_ = Hash::deserialize(hash, hashAlgo);
-  Hash::unwrap_throw(hash_);
-
-  cout << absl::StrCat(store->makeFixedOutputPath(recursive, *hash_, name),
-                       "\n");
-}
-
-static PathSet maybeUseOutputs(const Path& storePath, bool useOutput,
-                               bool forceRealise) {
-  if (forceRealise) {
-    realisePath(storePath);
-  }
-  if (useOutput && isDerivation(storePath)) {
-    Derivation drv = store->derivationFromPath(storePath);
-    PathSet outputs;
-    for (auto& i : drv.outputs) {
-      outputs.insert(i.second.path);
-    }
-    return outputs;
-  }
-  return {storePath};
-}
-
-/* Some code to print a tree representation of a derivation dependency
-   graph.  Topological sorting is used to keep the tree relatively
-   flat. */
-
-const std::string treeConn = "+---";
-const std::string treeLine = "|   ";
-const std::string treeNull = "    ";
-
-static void printTree(const Path& path, const std::string& firstPad,
-                      const std::string& tailPad, PathSet& done) {
-  if (done.find(path) != done.end()) {
-    cout << format("%1%%2% [...]\n") % firstPad % path;
-    return;
-  }
-  done.insert(path);
-
-  cout << format("%1%%2%\n") % firstPad % path;
-
-  auto references = store->queryPathInfo(path)->references;
-
-  /* Topologically sort under the relation A < B iff A \in
-     closure(B).  That is, if derivation A is an (possibly indirect)
-     input of B, then A is printed first.  This has the effect of
-     flattening the tree, preventing deeply nested structures.  */
-  Paths sorted = store->topoSortPaths(references);
-  reverse(sorted.begin(), sorted.end());
-
-  for (auto i = sorted.begin(); i != sorted.end(); ++i) {
-    auto j = i;
-    ++j;
-    printTree(*i, tailPad + treeConn,
-              j == sorted.end() ? tailPad + treeNull : tailPad + treeLine,
-              done);
-  }
-}
-
-/* Perform various sorts of queries. */
-static void opQuery(Strings opFlags, Strings opArgs) {
-  enum QueryType {
-    qDefault,
-    qOutputs,
-    qRequisites,
-    qReferences,
-    qReferrers,
-    qReferrersClosure,
-    qDeriver,
-    qBinding,
-    qHash,
-    qSize,
-    qTree,
-    qGraph,
-    qGraphML,
-    qResolve,
-    qRoots
-  };
-  QueryType query = qDefault;
-  bool useOutput = false;
-  bool includeOutputs = false;
-  bool forceRealise = false;
-  std::string bindingName;
-
-  for (auto& i : opFlags) {
-    QueryType prev = query;
-    if (i == "--outputs") {
-      query = qOutputs;
-    } else if (i == "--requisites" || i == "-R") {
-      query = qRequisites;
-    } else if (i == "--references") {
-      query = qReferences;
-    } else if (i == "--referrers" || i == "--referers") {
-      query = qReferrers;
-    } else if (i == "--referrers-closure" || i == "--referers-closure") {
-      query = qReferrersClosure;
-    } else if (i == "--deriver" || i == "-d") {
-      query = qDeriver;
-    } else if (i == "--binding" || i == "-b") {
-      if (opArgs.empty()) {
-        throw UsageError("expected binding name");
-      }
-      bindingName = opArgs.front();
-      opArgs.pop_front();
-      query = qBinding;
-    } else if (i == "--hash") {
-      query = qHash;
-    } else if (i == "--size") {
-      query = qSize;
-    } else if (i == "--tree") {
-      query = qTree;
-    } else if (i == "--graph") {
-      query = qGraph;
-    } else if (i == "--graphml") {
-      query = qGraphML;
-    } else if (i == "--resolve") {
-      query = qResolve;
-    } else if (i == "--roots") {
-      query = qRoots;
-    } else if (i == "--use-output" || i == "-u") {
-      useOutput = true;
-    } else if (i == "--force-realise" || i == "--force-realize" || i == "-f") {
-      forceRealise = true;
-    } else if (i == "--include-outputs") {
-      includeOutputs = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % i);
-    }
-    if (prev != qDefault && prev != query) {
-      throw UsageError(format("query type '%1%' conflicts with earlier flag") %
-                       i);
-    }
-  }
-
-  if (query == qDefault) {
-    query = qOutputs;
-  }
-
-  RunPager pager;
-
-  switch (query) {
-    case qOutputs: {
-      for (auto& i : opArgs) {
-        i = store->followLinksToStorePath(i);
-        if (forceRealise) {
-          realisePath(i);
-        }
-        Derivation drv = store->derivationFromPath(i);
-        for (auto& j : drv.outputs) {
-          cout << format("%1%\n") % j.second.path;
-        }
-      }
-      break;
-    }
-
-    case qRequisites:
-    case qReferences:
-    case qReferrers:
-    case qReferrersClosure: {
-      PathSet paths;
-      for (auto& i : opArgs) {
-        PathSet ps = maybeUseOutputs(store->followLinksToStorePath(i),
-                                     useOutput, forceRealise);
-        for (auto& j : ps) {
-          if (query == qRequisites) {
-            store->computeFSClosure(j, paths, false, includeOutputs);
-          } else if (query == qReferences) {
-            for (auto& p : store->queryPathInfo(j)->references) {
-              paths.insert(p);
-            }
-          } else if (query == qReferrers) {
-            store->queryReferrers(j, paths);
-          } else if (query == qReferrersClosure) {
-            store->computeFSClosure(j, paths, true);
-          }
-        }
-      }
-      Paths sorted = store->topoSortPaths(paths);
-      for (auto i = sorted.rbegin(); i != sorted.rend(); ++i) {
-        cout << format("%s\n") % *i;
-      }
-      break;
-    }
-
-    case qDeriver:
-      for (auto& i : opArgs) {
-        Path deriver =
-            store->queryPathInfo(store->followLinksToStorePath(i))->deriver;
-        cout << format("%1%\n") %
-                    (deriver.empty() ? "unknown-deriver" : deriver);
-      }
-      break;
-
-    case qBinding:
-      for (auto& i : opArgs) {
-        Path path = useDeriver(store->followLinksToStorePath(i));
-        Derivation drv = store->derivationFromPath(path);
-        auto j = drv.env.find(bindingName);
-        if (j == drv.env.end()) {
-          throw Error(
-              format(
-                  "derivation '%1%' has no environment binding named '%2%'") %
-              path % bindingName);
-        }
-        cout << format("%1%\n") % j->second;
-      }
-      break;
-
-    case qHash:
-    case qSize:
-      for (auto& i : opArgs) {
-        PathSet paths = maybeUseOutputs(store->followLinksToStorePath(i),
-                                        useOutput, forceRealise);
-        for (auto& j : paths) {
-          auto info = store->queryPathInfo(j);
-          if (query == qHash) {
-            assert(info->narHash.type == htSHA256);
-            cout << fmt("%s\n", info->narHash.to_string(Base32));
-          } else if (query == qSize) {
-            cout << fmt("%d\n", info->narSize);
-          }
-        }
-      }
-      break;
-
-    case qTree: {
-      PathSet done;
-      for (auto& i : opArgs) {
-        printTree(store->followLinksToStorePath(i), "", "", done);
-      }
-      break;
-    }
-
-    case qGraph: {
-      PathSet roots;
-      for (auto& i : opArgs) {
-        PathSet paths = maybeUseOutputs(store->followLinksToStorePath(i),
-                                        useOutput, forceRealise);
-        roots.insert(paths.begin(), paths.end());
-      }
-      printDotGraph(ref<Store>(store), roots);
-      break;
-    }
-
-    case qGraphML: {
-      PathSet roots;
-      for (auto& i : opArgs) {
-        PathSet paths = maybeUseOutputs(store->followLinksToStorePath(i),
-                                        useOutput, forceRealise);
-        roots.insert(paths.begin(), paths.end());
-      }
-      printGraphML(ref<Store>(store), roots);
-      break;
-    }
-
-    case qResolve: {
-      for (auto& i : opArgs) {
-        cout << format("%1%\n") % store->followLinksToStorePath(i);
-      }
-      break;
-    }
-
-    case qRoots: {
-      PathSet referrers;
-      for (auto& i : opArgs) {
-        store->computeFSClosure(
-            maybeUseOutputs(store->followLinksToStorePath(i), useOutput,
-                            forceRealise),
-            referrers, true, settings.gcKeepOutputs,
-            settings.gcKeepDerivations);
-      }
-      Roots roots = store->findRoots(false);
-      for (auto& [target, links] : roots) {
-        if (referrers.find(target) != referrers.end()) {
-          for (auto& link : links) {
-            cout << format("%1% -> %2%\n") % link % target;
-          }
-        }
-      }
-      break;
-    }
-
-    default:
-      abort();
-  }
-}
-
-static void opPrintEnv(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("unknown flag");
-  }
-  if (opArgs.size() != 1) {
-    throw UsageError("'--print-env' requires one derivation store path");
-  }
-
-  Path drvPath = opArgs.front();
-  Derivation drv = store->derivationFromPath(drvPath);
-
-  /* Print each environment variable in the derivation in a format
-     that can be sourced by the shell. */
-  for (auto& i : drv.env) {
-    cout << format("export %1%; %1%=%2%\n") % i.first % shellEscape(i.second);
-  }
-
-  /* Also output the arguments.  This doesn't preserve whitespace in
-     arguments. */
-  cout << "export _args; _args='";
-  bool first = true;
-  for (auto& i : drv.args) {
-    if (!first) {
-      cout << ' ';
-    }
-    first = false;
-    cout << shellEscape(i);
-  }
-  cout << "'\n";
-}
-
-static void opReadLog(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("unknown flag");
-  }
-
-  RunPager pager;
-
-  for (auto& i : opArgs) {
-    auto path = store->followLinksToStorePath(i);
-    auto log = store->getBuildLog(path);
-    if (!log) {
-      throw Error("build log of derivation '%s' is not available", path);
-    }
-    std::cout << *log;
-  }
-}
-
-static void opDumpDB(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("unknown flag");
-  }
-  if (!opArgs.empty()) {
-    for (auto& i : opArgs) {
-      i = store->followLinksToStorePath(i);
-    }
-    for (auto& i : opArgs) {
-      cout << store->makeValidityRegistration({i}, true, true);
-    }
-  } else {
-    PathSet validPaths = store->queryAllValidPaths();
-    for (auto& i : validPaths) {
-      cout << store->makeValidityRegistration({i}, true, true);
-    }
-  }
-}
-
-static void registerValidity(bool reregister, bool hashGiven,
-                             bool canonicalise) {
-  ValidPathInfos infos;
-
-  while (true) {
-    ValidPathInfo info = decodeValidPathInfo(cin, hashGiven);
-    if (info.path.empty()) {
-      break;
-    }
-    if (!store->isValidPath(info.path) || reregister) {
-      /* !!! races */
-      if (canonicalise) {
-        canonicalisePathMetaData(info.path, -1);
-      }
-      if (!hashGiven) {
-        HashResult hash = hashPath(htSHA256, info.path);
-        info.narHash = hash.first;
-        info.narSize = hash.second;
-      }
-      infos.push_back(info);
-    }
-  }
-
-  ensureLocalStore()->registerValidPaths(infos);
-}
-
-static void opLoadDB(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("unknown flag");
-  }
-  if (!opArgs.empty()) {
-    throw UsageError("no arguments expected");
-  }
-  registerValidity(true, true, false);
-}
-
-static void opRegisterValidity(Strings opFlags, Strings opArgs) {
-  bool reregister = false;  // !!! maybe this should be the default
-  bool hashGiven = false;
-
-  for (auto& i : opFlags) {
-    if (i == "--reregister") {
-      reregister = true;
-    } else if (i == "--hash-given") {
-      hashGiven = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % i);
-    }
-  }
-
-  if (!opArgs.empty()) {
-    throw UsageError("no arguments expected");
-  }
-
-  registerValidity(reregister, hashGiven, true);
-}
-
-static void opCheckValidity(Strings opFlags, Strings opArgs) {
-  bool printInvalid = false;
-
-  for (auto& i : opFlags) {
-    if (i == "--print-invalid") {
-      printInvalid = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % i);
-    }
-  }
-
-  for (auto& i : opArgs) {
-    Path path = store->followLinksToStorePath(i);
-    if (!store->isValidPath(path)) {
-      if (printInvalid) {
-        cout << format("%1%\n") % path;
-      } else {
-        throw Error(format("path '%1%' is not valid") % path);
-      }
-    }
-  }
-}
-
-static void opGC(Strings opFlags, Strings opArgs) {
-  bool printRoots = false;
-  GCOptions options;
-  options.action = GCOptions::gcDeleteDead;
-
-  GCResults results;
-
-  /* Do what? */
-  for (auto i = opFlags.begin(); i != opFlags.end(); ++i) {
-    if (*i == "--print-roots") {
-      printRoots = true;
-    } else if (*i == "--print-live") {
-      options.action = GCOptions::gcReturnLive;
-    } else if (*i == "--print-dead") {
-      options.action = GCOptions::gcReturnDead;
-    } else if (*i == "--delete") {
-      options.action = GCOptions::gcDeleteDead;
-    } else if (*i == "--max-freed") {
-      auto maxFreed = getIntArg<long long>(*i, i, opFlags.end(), true);
-      options.maxFreed = maxFreed >= 0 ? maxFreed : 0;
-    } else {
-      throw UsageError(format("bad sub-operation '%1%' in GC") % *i);
-    }
-  }
-
-  if (!opArgs.empty()) {
-    throw UsageError("no arguments expected");
-  }
-
-  if (printRoots) {
-    Roots roots = store->findRoots(false);
-    std::set<std::pair<Path, Path>> roots2;
-    // Transpose and sort the roots.
-    for (auto& [target, links] : roots) {
-      for (auto& link : links) {
-        roots2.emplace(link, target);
-      }
-    }
-    for (auto& [link, target] : roots2) {
-      std::cout << link << " -> " << target << "\n";
-    }
-  }
-
-  else {
-    PrintFreed freed(options.action == GCOptions::gcDeleteDead, results);
-    store->collectGarbage(options, results);
-
-    if (options.action != GCOptions::gcDeleteDead) {
-      for (auto& i : results.paths) {
-        cout << i << std::endl;
-      }
-    }
-  }
-}
-
-/* Remove paths from the Nix store if possible (i.e., if they do not
-   have any remaining referrers and are not reachable from any GC
-   roots). */
-static void opDelete(Strings opFlags, Strings opArgs) {
-  GCOptions options;
-  options.action = GCOptions::gcDeleteSpecific;
-
-  for (auto& i : opFlags) {
-    if (i == "--ignore-liveness") {
-      options.ignoreLiveness = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % i);
-    }
-  }
-
-  for (auto& i : opArgs) {
-    options.pathsToDelete.insert(store->followLinksToStorePath(i));
-  }
-
-  GCResults results;
-  PrintFreed freed(true, results);
-  store->collectGarbage(options, results);
-}
-
-/* Dump a path as a Nix archive.  The archive is written to standard
-   output. */
-static void opDump(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("unknown flag");
-  }
-  if (opArgs.size() != 1) {
-    throw UsageError("only one argument allowed");
-  }
-
-  FdSink sink(STDOUT_FILENO);
-  std::string path = *opArgs.begin();
-  dumpPath(path, sink);
-  sink.flush();
-}
-
-/* Restore a value from a Nix archive.  The archive is read from
-   standard input. */
-static void opRestore(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("unknown flag");
-  }
-  if (opArgs.size() != 1) {
-    throw UsageError("only one argument allowed");
-  }
-
-  FdSource source(STDIN_FILENO);
-  restorePath(*opArgs.begin(), source);
-}
-
-static void opExport(Strings opFlags, Strings opArgs) {
-  for (auto& i : opFlags) {
-    throw UsageError(format("unknown flag '%1%'") % i);
-  }
-
-  for (auto& i : opArgs) {
-    i = store->followLinksToStorePath(i);
-  }
-
-  FdSink sink(STDOUT_FILENO);
-  store->exportPaths(opArgs, sink);
-  sink.flush();
-}
-
-static void opImport(Strings opFlags, Strings opArgs) {
-  for (auto& i : opFlags) {
-    throw UsageError(format("unknown flag '%1%'") % i);
-  }
-
-  if (!opArgs.empty()) {
-    throw UsageError("no arguments expected");
-  }
-
-  FdSource source(STDIN_FILENO);
-  Paths paths = store->importPaths(source, nullptr, NoCheckSigs);
-
-  for (auto& i : paths) {
-    cout << format("%1%\n") % i << std::flush;
-  }
-}
-
-/* Initialise the Nix databases. */
-static void opInit(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("unknown flag");
-  }
-  if (!opArgs.empty()) {
-    throw UsageError("no arguments expected");
-  }
-  /* Doesn't do anything right now; database tables are initialised
-     automatically. */
-}
-
-/* Verify the consistency of the Nix environment. */
-static void opVerify(Strings opFlags, Strings opArgs) {
-  if (!opArgs.empty()) {
-    throw UsageError("no arguments expected");
-  }
-
-  bool checkContents = false;
-  RepairFlag repair = NoRepair;
-
-  for (auto& i : opFlags) {
-    if (i == "--check-contents") {
-      checkContents = true;
-    } else if (i == "--repair") {
-      repair = Repair;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % i);
-    }
-  }
-
-  if (store->verifyStore(checkContents, repair)) {
-    LOG(WARNING) << "not all errors were fixed";
-    throw Exit(1);
-  }
-}
-
-/* Verify whether the contents of the given store path have not changed. */
-static void opVerifyPath(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("no flags expected");
-  }
-
-  int status = 0;
-
-  for (auto& i : opArgs) {
-    Path path = store->followLinksToStorePath(i);
-    LOG(INFO) << "checking path '" << path << "'...";
-    auto info = store->queryPathInfo(path);
-    HashSink sink(info->narHash.type);
-    store->narFromPath(path, sink);
-    auto current = sink.finish();
-    if (current.first != info->narHash) {
-      LOG(ERROR) << "path '" << path << "' was modified! expected hash '"
-                 << info->narHash.to_string() << "', got '"
-                 << current.first.to_string() << "'";
-      status = 1;
-    }
-  }
-
-  throw Exit(status);
-}
-
-/* Repair the contents of the given path by redownloading it using a
-   substituter (if available). */
-static void opRepairPath(Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError("no flags expected");
-  }
-
-  for (auto& i : opArgs) {
-    Path path = store->followLinksToStorePath(i);
-    ensureLocalStore()->repairPath(path);
-  }
-}
-
-/* Optimise the disk space usage of the Nix store by hard-linking
-   files with the same contents. */
-static void opOptimise(Strings opFlags, Strings opArgs) {
-  if (!opArgs.empty() || !opFlags.empty()) {
-    throw UsageError("no arguments expected");
-  }
-
-  store->optimiseStore();
-}
-
-/* Serve the nix store in a way usable by a restricted ssh user. */
-static void opServe(Strings opFlags, Strings opArgs) {
-  bool writeAllowed = false;
-  for (auto& i : opFlags) {
-    if (i == "--write") {
-      writeAllowed = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % i);
-    }
-  }
-
-  if (!opArgs.empty()) {
-    throw UsageError("no arguments expected");
-  }
-
-  FdSource in(STDIN_FILENO);
-  FdSink out(STDOUT_FILENO);
-
-  /* Exchange the greeting. */
-  unsigned int magic = readInt(in);
-  if (magic != SERVE_MAGIC_1) {
-    throw Error("protocol mismatch");
-  }
-  out << SERVE_MAGIC_2 << SERVE_PROTOCOL_VERSION;
-  out.flush();
-  unsigned int clientVersion = readInt(in);
-
-  auto getBuildSettings = [&]() {
-    // FIXME: changing options here doesn't work if we're
-    // building through the daemon.
-    settings.keepLog = false;
-    settings.useSubstitutes = false;
-    settings.maxSilentTime = readInt(in);
-    settings.buildTimeout = readInt(in);
-    if (GET_PROTOCOL_MINOR(clientVersion) >= 2) {
-      settings.maxLogSize = readNum<unsigned long>(in);
-    }
-    if (GET_PROTOCOL_MINOR(clientVersion) >= 3) {
-      settings.buildRepeat = readInt(in);
-      settings.enforceDeterminism = readInt(in) != 0u;
-      settings.runDiffHook = true;
-    }
-    settings.printRepeatedBuilds = false;
-  };
-
-  while (true) {
-    ServeCommand cmd;
-    try {
-      cmd = static_cast<ServeCommand>(readInt(in));
-    } catch (EndOfFile& e) {
-      break;
-    }
-
-    switch (cmd) {
-      case cmdQueryValidPaths: {
-        bool lock = readInt(in) != 0u;
-        bool substitute = readInt(in) != 0u;
-        auto paths = readStorePaths<PathSet>(*store, in);
-        if (lock && writeAllowed) {
-          for (auto& path : paths) {
-            store->addTempRoot(path);
-          }
-        }
-
-        /* If requested, substitute missing paths. This
-           implements nix-copy-closure's --use-substitutes
-           flag. */
-        if (substitute && writeAllowed) {
-          /* Filter out .drv files (we don't want to build anything). */
-          PathSet paths2;
-          for (auto& path : paths) {
-            if (!isDerivation(path)) {
-              paths2.insert(path);
-            }
-          }
-          unsigned long long downloadSize;
-          unsigned long long narSize;
-          PathSet willBuild;
-          PathSet willSubstitute;
-          PathSet unknown;
-          store->queryMissing(PathSet(paths2.begin(), paths2.end()), willBuild,
-                              willSubstitute, unknown, downloadSize, narSize);
-          /* FIXME: should use ensurePath(), but it only
-             does one path at a time. */
-          if (!willSubstitute.empty()) {
-            try {
-              util::OkOrThrow(store->buildPaths(std::cerr, willSubstitute));
-            } catch (Error& e) {
-              LOG(WARNING) << e.msg();
-            }
-          }
-        }
-
-        out << store->queryValidPaths(paths);
-        break;
-      }
-
-      case cmdQueryPathInfos: {
-        auto paths = readStorePaths<PathSet>(*store, in);
-        // !!! Maybe we want a queryPathInfos?
-        for (auto& i : paths) {
-          try {
-            auto info = store->queryPathInfo(i);
-            out << info->path << info->deriver << info->references;
-            // !!! Maybe we want compression?
-            out << info->narSize  // downloadSize
-                << info->narSize;
-            if (GET_PROTOCOL_MINOR(clientVersion) >= 4) {
-              out << (info->narHash ? info->narHash.to_string() : "")
-                  << info->ca << info->sigs;
-            }
-          } catch (InvalidPath&) {
-          }
-        }
-        out << "";
-        break;
-      }
-
-      case cmdDumpStorePath:
-        store->narFromPath(readStorePath(*store, in), out);
-        break;
-
-      case cmdImportPaths: {
-        if (!writeAllowed) {
-          throw Error("importing paths is not allowed");
-        }
-        store->importPaths(in, nullptr,
-                           NoCheckSigs);  // FIXME: should we skip sig checking?
-        out << 1;                         // indicate success
-        break;
-      }
-
-      case cmdExportPaths: {
-        readInt(in);  // obsolete
-        store->exportPaths(readStorePaths<Paths>(*store, in), out);
-        break;
-      }
-
-      case cmdBuildPaths: {
-        if (!writeAllowed) {
-          throw Error("building paths is not allowed");
-        }
-        auto paths = readStorePaths<PathSet>(*store, in);
-
-        getBuildSettings();
-
-        try {
-          MonitorFdHup monitor(in.fd);
-          util::OkOrThrow(store->buildPaths(std::cerr, paths));
-          out << 0;
-        } catch (Error& e) {
-          assert(e.status);
-          out << e.status << e.msg();
-        }
-        break;
-      }
-
-      case cmdBuildDerivation: { /* Used by hydra-queue-runner. */
-
-        if (!writeAllowed) {
-          throw Error("building paths is not allowed");
-        }
-
-        Path drvPath = readStorePath(*store, in);  // informational only
-        BasicDerivation drv;
-        readDerivation(in, *store, drv);
-
-        getBuildSettings();
-
-        MonitorFdHup monitor(in.fd);
-        auto status = store->buildDerivation(std::cerr, drvPath, drv);
-
-        out << status.status << status.errorMsg;
-
-        if (GET_PROTOCOL_MINOR(clientVersion) >= 3) {
-          out << status.timesBuilt
-              << static_cast<uint64_t>(status.isNonDeterministic)
-              << status.startTime << status.stopTime;
-        }
-
-        break;
-      }
-
-      case cmdQueryClosure: {
-        bool includeOutputs = readInt(in) != 0u;
-        PathSet closure;
-        store->computeFSClosure(readStorePaths<PathSet>(*store, in), closure,
-                                false, includeOutputs);
-        out << closure;
-        break;
-      }
-
-      case cmdAddToStoreNar: {
-        if (!writeAllowed) {
-          throw Error("importing paths is not allowed");
-        }
-
-        ValidPathInfo info;
-        info.path = readStorePath(*store, in);
-        in >> info.deriver;
-        if (!info.deriver.empty()) {
-          store->assertStorePath(info.deriver);
-        }
-        auto hash_ = Hash::deserialize(readString(in), htSHA256);
-        info.narHash = Hash::unwrap_throw(hash_);
-        info.references = readStorePaths<PathSet>(*store, in);
-        in >> info.registrationTime >> info.narSize >> info.ultimate;
-        info.sigs = readStrings<StringSet>(in);
-        in >> info.ca;
-
-        if (info.narSize == 0) {
-          throw Error("narInfo is too old and missing the narSize field");
-        }
-
-        SizedSource sizedSource(in, info.narSize);
-
-        store->addToStore(info, sizedSource, NoRepair, NoCheckSigs);
-
-        // consume all the data that has been sent before continuing.
-        sizedSource.drainAll();
-
-        out << 1;  // indicate success
-
-        break;
-      }
-
-      default:
-        throw Error(format("unknown serve command %1%") % cmd);
-    }
-
-    out.flush();
-  }
-}
-
-static void opGenerateBinaryCacheKey(Strings opFlags, Strings opArgs) {
-  for (auto& i : opFlags) {
-    throw UsageError(format("unknown flag '%1%'") % i);
-  }
-
-  if (opArgs.size() != 3) {
-    throw UsageError("three arguments expected");
-  }
-  auto i = opArgs.begin();
-  std::string keyName = *i++;
-  std::string secretKeyFile = *i++;
-  std::string publicKeyFile = *i++;
-
-#if HAVE_SODIUM
-  if (sodium_init() == -1) {
-    throw Error("could not initialise libsodium");
-  }
-
-  unsigned char pk[crypto_sign_PUBLICKEYBYTES];
-  unsigned char sk[crypto_sign_SECRETKEYBYTES];
-  if (crypto_sign_keypair(pk, sk) != 0) {
-    throw Error("key generation failed");
-  }
-
-  writeFile(publicKeyFile,
-            keyName + ":" +
-                absl::Base64Escape(std::string(reinterpret_cast<char*>(pk),
-                                               crypto_sign_PUBLICKEYBYTES)));
-  umask(0077);
-  writeFile(secretKeyFile,
-            keyName + ":" +
-                absl::Base64Escape(std::string(reinterpret_cast<char*>(sk),
-                                               crypto_sign_SECRETKEYBYTES)));
-#else
-  throw Error(
-      "Nix was not compiled with libsodium, required for signed binary cache "
-      "support");
-#endif
-}
-
-static void opVersion(Strings opFlags, Strings opArgs) {
-  printVersion("nix-store");
-}
-
-/* Scan the arguments; find the operation, set global flags, put all
-   other flags in a list, and put all other arguments in another
-   list. */
-static int _main(int argc, char** argv) {
-  {
-    Strings opFlags;
-    Strings opArgs;
-    Operation op = nullptr;
-
-    parseCmdLine(argc, argv,
-                 [&](Strings::iterator& arg, const Strings::iterator& end) {
-                   Operation oldOp = op;
-
-                   if (*arg == "--help") {
-                     showManPage("nix-store");
-                   } else if (*arg == "--version") {
-                     op = opVersion;
-                   } else if (*arg == "--realise" || *arg == "--realize" ||
-                              *arg == "-r") {
-                     op = opRealise;
-                   } else if (*arg == "--add" || *arg == "-A") {
-                     op = opAdd;
-                   } else if (*arg == "--add-fixed") {
-                     op = opAddFixed;
-                   } else if (*arg == "--print-fixed-path") {
-                     op = opPrintFixedPath;
-                   } else if (*arg == "--delete") {
-                     op = opDelete;
-                   } else if (*arg == "--query" || *arg == "-q") {
-                     op = opQuery;
-                   } else if (*arg == "--print-env") {
-                     op = opPrintEnv;
-                   } else if (*arg == "--read-log" || *arg == "-l") {
-                     op = opReadLog;
-                   } else if (*arg == "--dump-db") {
-                     op = opDumpDB;
-                   } else if (*arg == "--load-db") {
-                     op = opLoadDB;
-                   } else if (*arg == "--register-validity") {
-                     op = opRegisterValidity;
-                   } else if (*arg == "--check-validity") {
-                     op = opCheckValidity;
-                   } else if (*arg == "--gc") {
-                     op = opGC;
-                   } else if (*arg == "--dump") {
-                     op = opDump;
-                   } else if (*arg == "--restore") {
-                     op = opRestore;
-                   } else if (*arg == "--export") {
-                     op = opExport;
-                   } else if (*arg == "--import") {
-                     op = opImport;
-                   } else if (*arg == "--init") {
-                     op = opInit;
-                   } else if (*arg == "--verify") {
-                     op = opVerify;
-                   } else if (*arg == "--verify-path") {
-                     op = opVerifyPath;
-                   } else if (*arg == "--repair-path") {
-                     op = opRepairPath;
-                   } else if (*arg == "--optimise" || *arg == "--optimize") {
-                     op = opOptimise;
-                   } else if (*arg == "--serve") {
-                     op = opServe;
-                   } else if (*arg == "--generate-binary-cache-key") {
-                     op = opGenerateBinaryCacheKey;
-                   } else if (*arg == "--add-root") {
-                     gcRoot = absPath(getArg(*arg, arg, end));
-                   } else if (*arg == "--indirect") {
-                     indirectRoot = true;
-                   } else if (*arg == "--no-output") {
-                     noOutput = true;
-                   } else if (*arg != "" && arg->at(0) == '-') {
-                     opFlags.push_back(*arg);
-                     if (*arg == "--max-freed" || *arg == "--max-links" ||
-                         *arg == "--max-atime") { /* !!! hack */
-                       opFlags.push_back(getArg(*arg, arg, end));
-                     }
-                   } else {
-                     opArgs.push_back(*arg);
-                   }
-
-                   if ((oldOp != nullptr) && oldOp != op) {
-                     throw UsageError("only one operation may be specified");
-                   }
-
-                   return true;
-                 });
-
-    if (op == nullptr) {
-      throw UsageError("no operation specified");
-    }
-
-    if (op != opDump && op != opRestore) { /* !!! hack */
-      store = openStore();
-    }
-
-    op(opFlags, opArgs);
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("nix-store", _main);
diff --git a/third_party/nix/src/nix/add-to-store.cc b/third_party/nix/src/nix/add-to-store.cc
deleted file mode 100644
index 53641f120e..0000000000
--- a/third_party/nix/src/nix/add-to-store.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "libmain/common-args.hh"
-#include "libstore/store-api.hh"
-#include "libutil/archive.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdAddToStore final : MixDryRun, StoreCommand {
-  Path path;
-  std::optional<std::string> namePart;
-
-  CmdAddToStore() {
-    expectArg("path", &path);
-
-    mkFlag()
-        .longName("name")
-        .shortName('n')
-        .description("name component of the store path")
-        .labels({"name"})
-        .dest(&namePart);
-  }
-
-  std::string name() override { return "add-to-store"; }
-
-  std::string description() override { return "add a path to the Nix store"; }
-
-  Examples examples() override { return {}; }
-
-  void run(ref<Store> store) override {
-    if (!namePart) {
-      namePart = baseNameOf(path);
-    }
-
-    StringSink sink;
-    dumpPath(path, sink);
-
-    ValidPathInfo info;
-    info.narHash = hashString(htSHA256, *sink.s);
-    info.narSize = sink.s->size();
-    info.path = store->makeFixedOutputPath(true, info.narHash, *namePart);
-    info.ca = makeFixedOutputCA(true, info.narHash);
-
-    if (!dryRun) {
-      store->addToStore(info, sink.s);
-    }
-
-    std::cout << fmt("%s\n", info.path);
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdAddToStore>());
diff --git a/third_party/nix/src/nix/build.cc b/third_party/nix/src/nix/build.cc
deleted file mode 100644
index 3fe74b7ffd..0000000000
--- a/third_party/nix/src/nix/build.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-#include "libmain/common-args.hh"
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdBuild final : MixDryRun, InstallablesCommand {
-  Path outLink = "result";
-
-  CmdBuild() {
-    mkFlag()
-        .longName("out-link")
-        .shortName('o')
-        .description("path of the symlink to the build result")
-        .labels({"path"})
-        .dest(&outLink);
-
-    mkFlag()
-        .longName("no-link")
-        .description("do not create a symlink to the build result")
-        .set(&outLink, Path(""));
-  }
-
-  std::string name() override { return "build"; }
-
-  std::string description() override {
-    return "build a derivation or fetch a store path";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To build and run GNU Hello from NixOS 17.03:",
-                "nix build -f channel:nixos-17.03 hello; ./result/bin/hello"},
-        Example{"To build the build.x86_64-linux attribute from release.nix:",
-                "nix build -f release.nix build.x86_64-linux"},
-    };
-  }
-
-  void run(ref<Store> store) override {
-    auto buildables = build(store, dryRun ? DryRun : Build, installables);
-
-    if (dryRun) {
-      return;
-    }
-
-    for (size_t i = 0; i < buildables.size(); ++i) {
-      auto& b(buildables[i]);
-
-      if (!outLink.empty()) {
-        for (auto& output : b.outputs) {
-          if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) {
-            std::string symlink = outLink;
-            if (i != 0u) {
-              symlink += fmt("-%d", i);
-            }
-            if (output.first != "out") {
-              symlink += fmt("-%s", output.first);
-            }
-            store2->addPermRoot(output.second, absPath(symlink), true);
-          }
-        }
-      }
-    }
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdBuild>());
diff --git a/third_party/nix/src/nix/cat.cc b/third_party/nix/src/nix/cat.cc
deleted file mode 100644
index 7788707eae..0000000000
--- a/third_party/nix/src/nix/cat.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "libstore/fs-accessor.hh"
-#include "libstore/nar-accessor.hh"
-#include "libstore/store-api.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct MixCat : virtual Args {
-  std::string path;
-
-  void cat(const ref<FSAccessor>& accessor) {
-    auto st = accessor->stat(path);
-    if (st.type == FSAccessor::Type::tMissing) {
-      throw Error(format("path '%1%' does not exist") % path);
-    }
-    if (st.type != FSAccessor::Type::tRegular) {
-      throw Error(format("path '%1%' is not a regular file") % path);
-    }
-
-    std::cout << accessor->readFile(path);
-  }
-};
-
-struct CmdCatStore final : StoreCommand, MixCat {
-  CmdCatStore() { expectArg("path", &path); }
-
-  std::string name() override { return "cat-store"; }
-
-  std::string description() override {
-    return "print the contents of a store file on stdout";
-  }
-
-  void run(ref<Store> store) override { cat(store->getFSAccessor()); }
-};
-
-struct CmdCatNar final : StoreCommand, MixCat {
-  Path narPath;
-
-  CmdCatNar() {
-    expectArg("nar", &narPath);
-    expectArg("path", &path);
-  }
-
-  std::string name() override { return "cat-nar"; }
-
-  std::string description() override {
-    return "print the contents of a file inside a NAR file";
-  }
-
-  void run(ref<Store> store) override {
-    cat(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdCatStore>());
-static nix::RegisterCommand r2(nix::make_ref<nix::CmdCatNar>());
diff --git a/third_party/nix/src/nix/command.cc b/third_party/nix/src/nix/command.cc
deleted file mode 100644
index f7f183ab0a..0000000000
--- a/third_party/nix/src/nix/command.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-#include "nix/command.hh"
-
-#include <utility>
-
-#include "libstore/derivations.hh"
-#include "libstore/store-api.hh"
-
-namespace nix {
-
-Commands* RegisterCommand::commands = nullptr;
-
-void Command::printHelp(const std::string& programName, std::ostream& out) {
-  Args::printHelp(programName, out);
-
-  auto exs = examples();
-  if (!exs.empty()) {
-    out << "\n";
-    out << "Examples:\n";
-    for (auto& ex : exs) {
-      out << "\n"
-          << "  " << ex.description << "\n"  // FIXME: wrap
-          << "  $ " << ex.command << "\n";
-    }
-  }
-}
-
-MultiCommand::MultiCommand(Commands _commands)
-    : commands(std::move(_commands)) {
-  expectedArgs.push_back(ExpectedArg{
-      "command", 1, true, [=](std::vector<std::string> ss) {
-        assert(!command);
-        auto i = commands.find(ss[0]);
-        if (i == commands.end()) {
-          throw UsageError("'%s' is not a recognised command", ss[0]);
-        }
-        command = i->second;
-      }});
-}
-
-void MultiCommand::printHelp(const std::string& programName,
-                             std::ostream& out) {
-  if (command) {
-    command->printHelp(programName + " " + command->name(), out);
-    return;
-  }
-
-  out << "Usage: " << programName << " <COMMAND> <FLAGS>... <ARGS>...\n";
-
-  out << "\n";
-  out << "Common flags:\n";
-  printFlags(out);
-
-  out << "\n";
-  out << "Available commands:\n";
-
-  Table2 table;
-  for (auto& command : commands) {
-    auto descr = command.second->description();
-    if (!descr.empty()) {
-      table.push_back(std::make_pair(command.second->name(), descr));
-    }
-  }
-  printTable(out, table);
-
-#if 0
-    out << "\n";
-    out << "For full documentation, run 'man " << programName << "' or 'man " << programName << "-<COMMAND>'.\n";
-#endif
-}
-
-bool MultiCommand::processFlag(Strings::iterator& pos, Strings::iterator end) {
-  if (Args::processFlag(pos, end)) {
-    return true;
-  }
-  if (command && command->processFlag(pos, end)) {
-    return true;
-  }
-  return false;
-}
-
-bool MultiCommand::processArgs(const Strings& args, bool finish) {
-  if (command) {
-    return command->processArgs(args, finish);
-  }
-  return Args::processArgs(args, finish);
-}
-
-StoreCommand::StoreCommand() = default;
-
-ref<Store> StoreCommand::getStore() {
-  if (!_store) {
-    _store = createStore();
-  }
-  return ref<Store>(_store);
-}
-
-ref<Store> StoreCommand::createStore() { return openStore(); }
-
-void StoreCommand::run() { run(getStore()); }
-
-StorePathsCommand::StorePathsCommand(bool recursive) : recursive(recursive) {
-  if (recursive) {
-    mkFlag()
-        .longName("no-recursive")
-        .description("apply operation to specified paths only")
-        .set(&this->recursive, false);
-  } else {
-    mkFlag()
-        .longName("recursive")
-        .shortName('r')
-        .description("apply operation to closure of the specified paths")
-        .set(&this->recursive, true);
-  }
-
-  mkFlag(0, "all", "apply operation to the entire store", &all);
-}
-
-void StorePathsCommand::run(ref<Store> store) {
-  Paths storePaths;
-
-  if (all) {
-    if (!installables.empty() != 0u) {
-      throw UsageError("'--all' does not expect arguments");
-    }
-    for (auto& p : store->queryAllValidPaths()) {
-      storePaths.push_back(p);
-    }
-  }
-
-  else {
-    for (auto& p : toStorePaths(store, NoBuild, installables)) {
-      storePaths.push_back(p);
-    }
-
-    if (recursive) {
-      PathSet closure;
-      store->computeFSClosure(PathSet(storePaths.begin(), storePaths.end()),
-                              closure, false, false);
-      storePaths = Paths(closure.begin(), closure.end());
-    }
-  }
-
-  run(store, storePaths);
-}
-
-void StorePathCommand::run(ref<Store> store) {
-  auto storePaths = toStorePaths(store, NoBuild, installables);
-
-  if (storePaths.size() != 1) {
-    throw UsageError("this command requires exactly one store path");
-  }
-
-  run(store, *storePaths.begin());
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix/command.hh b/third_party/nix/src/nix/command.hh
deleted file mode 100644
index 87e2fbe9d2..0000000000
--- a/third_party/nix/src/nix/command.hh
+++ /dev/null
@@ -1,194 +0,0 @@
-#pragma once
-
-#include <memory>
-
-#include "libexpr/common-eval-args.hh"
-#include "libutil/args.hh"
-
-namespace nix {
-
-extern std::string programPath;
-
-struct Value;
-class Bindings;
-class EvalState;
-
-/* A command is an argument parser that can be executed by calling its
-   run() method. */
-struct Command : virtual Args {
-  virtual std::string name() = 0;
-  virtual void prepare(){};
-  virtual void run() = 0;
-
-  struct Example {
-    std::string description;
-    std::string command;
-  };
-
-  typedef std::list<Example> Examples;
-
-  virtual Examples examples() { return Examples(); }
-
-  void printHelp(const std::string& programName, std::ostream& out) override;
-};
-
-class Store;
-
-/* A command that require a Nix store. */
-struct StoreCommand : virtual Command {
-  StoreCommand();
-  void run() override;
-  ref<Store> getStore();
-  virtual ref<Store> createStore();
-  virtual void run(ref<Store>) = 0;
-
- private:
-  std::shared_ptr<Store> _store;
-};
-
-struct Buildable {
-  Path drvPath;  // may be empty
-  std::map<std::string, Path> outputs;
-};
-
-using Buildables = std::vector<Buildable>;
-
-struct Installable {
-  virtual std::string what() = 0;
-
-  virtual Buildables toBuildables() {
-    throw Error("argument '%s' cannot be built", what());
-  }
-
-  Buildable toBuildable();
-
-  virtual Value* toValue(EvalState& state) {
-    throw Error("argument '%s' cannot be evaluated", what());
-  }
-};
-
-struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs {
-  Path file;
-
-  SourceExprCommand();
-
-  /* Return a value representing the Nix expression from which we
-     are installing. This is either the file specified by β€˜--file’,
-     or an attribute set constructed from $NIX_PATH, e.g. β€˜{ nixpkgs
-     = import ...; bla = import ...; }’. */
-  Value* getSourceExpr(EvalState& state);
-
-  ref<EvalState> getEvalState();
-
- private:
-  std::shared_ptr<EvalState> evalState;
-  std::shared_ptr<Value*> vSourceExpr;
-};
-
-enum RealiseMode { Build, NoBuild, DryRun };
-
-/* A command that operates on a list of "installables", which can be
-   store paths, attribute paths, Nix expressions, etc. */
-struct InstallablesCommand : virtual Args, SourceExprCommand {
-  std::vector<std::shared_ptr<Installable>> installables;
-
-  InstallablesCommand() { expectArgs("installables", &_installables); }
-
-  void prepare() override;
-
-  virtual bool useDefaultInstallables() { return true; }
-
- private:
-  std::vector<std::string> _installables;
-};
-
-struct InstallableCommand : virtual Args, SourceExprCommand {
-  std::shared_ptr<Installable> installable;
-
-  InstallableCommand() { expectArg("installable", &_installable); }
-
-  void prepare() override;
-
- private:
-  std::string _installable;
-};
-
-/* A command that operates on zero or more store paths. */
-struct StorePathsCommand : public InstallablesCommand {
- private:
-  bool recursive = false;
-  bool all = false;
-
- public:
-  StorePathsCommand(bool recursive = false);
-
-  using StoreCommand::run;
-
-  virtual void run(ref<Store> store, Paths storePaths) = 0;
-
-  void run(ref<Store> store) override;
-
-  bool useDefaultInstallables() override { return !all; }
-};
-
-/* A command that operates on exactly one store path. */
-struct StorePathCommand : public InstallablesCommand {
-  using StoreCommand::run;
-
-  virtual void run(ref<Store> store, const Path& storePath) = 0;
-
-  void run(ref<Store> store) override;
-};
-
-using Commands = std::map<std::string, ref<Command>>;
-
-/* An argument parser that supports multiple subcommands,
-   i.e. β€˜<command> <subcommand>’. */
-class MultiCommand : virtual Args {
- public:
-  Commands commands;
-
-  std::shared_ptr<Command> command;
-
-  MultiCommand(Commands commands);
-
-  void printHelp(const std::string& programName, std::ostream& out) override;
-
-  bool processFlag(Strings::iterator& pos, Strings::iterator end) override;
-
-  bool processArgs(const Strings& args, bool finish) override;
-};
-
-/* A helper class for registering commands globally. */
-struct RegisterCommand {
-  static Commands* commands;
-
-  RegisterCommand(ref<Command> command) {
-    if (!commands) {
-      commands = new Commands;
-    }
-    commands->emplace(command->name(), command);
-  }
-};
-
-std::shared_ptr<Installable> parseInstallable(SourceExprCommand& cmd,
-                                              const ref<Store>& store,
-                                              const std::string& installable,
-                                              bool useDefaultInstallables);
-
-Buildables build(const ref<Store>& store, RealiseMode mode,
-                 const std::vector<std::shared_ptr<Installable>>& installables);
-
-PathSet toStorePaths(
-    const ref<Store>& store, RealiseMode mode,
-    const std::vector<std::shared_ptr<Installable>>& installables);
-
-Path toStorePath(const ref<Store>& store, RealiseMode mode,
-                 const std::shared_ptr<Installable>& installable);
-
-PathSet toDerivations(
-    const ref<Store>& store,
-    const std::vector<std::shared_ptr<Installable>>& installables,
-    bool useDeriver = false);
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix/copy.cc b/third_party/nix/src/nix/copy.cc
deleted file mode 100644
index 75c85698d1..0000000000
--- a/third_party/nix/src/nix/copy.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-#include <atomic>
-
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "libutil/sync.hh"
-#include "libutil/thread-pool.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdCopy final : StorePathsCommand {
-  std::string srcUri, dstUri;
-
-  CheckSigsFlag checkSigs = CheckSigs;
-
-  SubstituteFlag substitute = NoSubstitute;
-
-  CmdCopy() : StorePathsCommand(true) {
-    mkFlag()
-        .longName("from")
-        .labels({"store-uri"})
-        .description("URI of the source Nix store")
-        .dest(&srcUri);
-    mkFlag()
-        .longName("to")
-        .labels({"store-uri"})
-        .description("URI of the destination Nix store")
-        .dest(&dstUri);
-
-    mkFlag()
-        .longName("no-check-sigs")
-        .description("do not require that paths are signed by trusted keys")
-        .set(&checkSigs, NoCheckSigs);
-
-    mkFlag()
-        .longName("substitute-on-destination")
-        .shortName('s')
-        .description(
-            "whether to try substitutes on the destination store (only "
-            "supported by SSH)")
-        .set(&substitute, Substitute);
-  }
-
-  std::string name() override { return "copy"; }
-
-  std::string description() override { return "copy paths between Nix stores"; }
-
-  Examples examples() override {
-    return {
-        Example{"To copy Firefox from the local store to a binary cache in "
-                "file:///tmp/cache:",
-                "nix copy --to file:///tmp/cache $(type -p firefox)"},
-        Example{"To copy the entire current NixOS system closure to another "
-                "machine via SSH:",
-                "nix copy --to ssh://server /run/current-system"},
-        Example{"To copy a closure from another machine via SSH:",
-                "nix copy --from ssh://server "
-                "/nix/store/a6cnl93nk1wxnq84brbbwr6hxw9gp2w9-blender-2.79-rc2"},
-#ifdef ENABLE_S3
-        Example{"To copy Hello to an S3 binary cache:",
-                "nix copy --to s3://my-bucket?region=eu-west-1 nixpkgs.hello"},
-        Example{"To copy Hello to an S3-compatible binary cache:",
-                "nix copy --to "
-                "s3://my-bucket?region=eu-west-1&endpoint=example.com "
-                "nixpkgs.hello"},
-#endif
-    };
-  }
-
-  ref<Store> createStore() override {
-    return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
-  }
-
-  void run(ref<Store> srcStore, Paths storePaths) override {
-    if (srcUri.empty() && dstUri.empty()) {
-      throw UsageError("you must pass '--from' and/or '--to'");
-    }
-
-    ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
-
-    copyPaths(srcStore, dstStore, PathSet(storePaths.begin(), storePaths.end()),
-              NoRepair, checkSigs, substitute);
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdCopy>());
diff --git a/third_party/nix/src/nix/doctor.cc b/third_party/nix/src/nix/doctor.cc
deleted file mode 100644
index d0b4c2b588..0000000000
--- a/third_party/nix/src/nix/doctor.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-#include <absl/strings/match.h>
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_split.h>
-
-#include "libmain/shared.hh"
-#include "libstore/serve-protocol.hh"
-#include "libstore/store-api.hh"
-#include "libstore/worker-protocol.hh"
-#include "nix/command.hh"
-
-namespace nix {
-static std::string formatProtocol(unsigned int proto) {
-  if (proto != 0u) {
-    auto major = GET_PROTOCOL_MAJOR(proto) >> 8;
-    auto minor = GET_PROTOCOL_MINOR(proto);
-    return (format("%1%.%2%") % major % minor).str();
-  }
-  return "unknown";
-}
-
-struct CmdDoctor final : StoreCommand {
-  bool success = true;
-
-  std::string name() override { return "doctor"; }
-
-  std::string description() override {
-    return "check your system for potential problems";
-  }
-
-  void run(ref<Store> store) override {
-    std::cout << "Store uri: " << store->getUri() << std::endl;
-    std::cout << std::endl;
-
-    auto type = getStoreType();
-
-    if (type < tOther) {
-      success &= checkNixInPath();
-      success &= checkProfileRoots(store);
-    }
-    success &= checkStoreProtocol(store->getProtocol());
-
-    if (!success) {
-      throw Exit(2);
-    }
-  }
-
-  static bool checkNixInPath() {
-    PathSet dirs;
-
-    for (auto& dir : absl::StrSplit(getEnv("PATH").value_or(""),
-                                    absl::ByChar(':'), absl::SkipEmpty())) {
-      if (pathExists(absl::StrCat(dir, "/nix-env"))) {
-        dirs.insert(dirOf(canonPath(absl::StrCat(dir, "/nix-env"), true)));
-      }
-    }
-
-    if (dirs.size() != 1) {
-      std::cout << "Warning: multiple versions of nix found in PATH."
-                << std::endl;
-      std::cout << std::endl;
-      for (auto& dir : dirs) {
-        std::cout << "  " << dir << std::endl;
-      }
-      std::cout << std::endl;
-      return false;
-    }
-
-    return true;
-  }
-
-  static bool checkProfileRoots(const ref<Store>& store) {
-    PathSet dirs;
-
-    for (auto dir : absl::StrSplit(getEnv("PATH").value_or(""),
-                                   absl::ByChar(':'), absl::SkipEmpty())) {
-      Path profileDir = dirOf(dir);
-      try {
-        Path userEnv = canonPath(profileDir, true);
-
-        if (store->isStorePath(userEnv) &&
-            absl::EndsWith(userEnv, "user-environment")) {
-          while (profileDir.find("/profiles/") == std::string::npos &&
-                 isLink(profileDir)) {
-            profileDir = absPath(readLink(profileDir), dirOf(profileDir));
-          }
-
-          if (profileDir.find("/profiles/") == std::string::npos) {
-            dirs.insert(std::string(dir));
-          }
-        }
-      } catch (SysError&) {
-      }
-    }
-
-    if (!dirs.empty()) {
-      std::cout << "Warning: found profiles outside of " << settings.nixStateDir
-                << "/profiles." << std::endl;
-      std::cout << "The generation this profile points to might not have a "
-                   "gcroot and could be"
-                << std::endl;
-      std::cout << "garbage collected, resulting in broken symlinks."
-                << std::endl;
-      std::cout << std::endl;
-      for (auto& dir : dirs) {
-        std::cout << "  " << dir << std::endl;
-      }
-      std::cout << std::endl;
-      return false;
-    }
-
-    return true;
-  }
-
-  static bool checkStoreProtocol(unsigned int storeProto) {
-    unsigned int clientProto = GET_PROTOCOL_MAJOR(SERVE_PROTOCOL_VERSION) ==
-                                       GET_PROTOCOL_MAJOR(storeProto)
-                                   ? SERVE_PROTOCOL_VERSION
-                                   : PROTOCOL_VERSION;
-
-    if (clientProto != storeProto) {
-      std::cout << "Warning: protocol version of this client does not match "
-                   "the store."
-                << std::endl;
-      std::cout << "While this is not necessarily a problem it's recommended "
-                   "to keep the client in"
-                << std::endl;
-      std::cout << "sync with the daemon." << std::endl;
-      std::cout << std::endl;
-      std::cout << "Client protocol: " << formatProtocol(clientProto)
-                << std::endl;
-      std::cout << "Store protocol: " << formatProtocol(storeProto)
-                << std::endl;
-      std::cout << std::endl;
-      return false;
-    }
-
-    return true;
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdDoctor>());
diff --git a/third_party/nix/src/nix/dump-path.cc b/third_party/nix/src/nix/dump-path.cc
deleted file mode 100644
index 1d0a996e56..0000000000
--- a/third_party/nix/src/nix/dump-path.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "libstore/store-api.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdDumpPath final : StorePathCommand {
-  std::string name() override { return "dump-path"; }
-
-  std::string description() override {
-    return "dump a store path to stdout (in NAR format)";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To get a NAR from the binary cache https://cache.nixos.org/:",
-                "nix dump-path --store https://cache.nixos.org/ "
-                "/nix/store/7crrmih8c52r8fbnqb933dxrsp44md93-glibc-2.25"},
-    };
-  }
-
-  void run(ref<Store> store, const Path& storePath) override {
-    FdSink sink(STDOUT_FILENO);
-    store->narFromPath(storePath, sink);
-    sink.flush();
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdDumpPath>());
diff --git a/third_party/nix/src/nix/edit.cc b/third_party/nix/src/nix/edit.cc
deleted file mode 100644
index 04c67acb94..0000000000
--- a/third_party/nix/src/nix/edit.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-#include <absl/strings/str_split.h>
-#include <glog/logging.h>
-#include <unistd.h>
-
-#include "libexpr/attr-path.hh"
-#include "libexpr/eval.hh"
-#include "libmain/shared.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdEdit final : InstallableCommand {
-  std::string name() override { return "edit"; }
-
-  std::string description() override {
-    return "open the Nix expression of a Nix package in $EDITOR";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To open the Nix expression of the GNU Hello package:",
-                "nix edit nixpkgs.hello"},
-    };
-  }
-
-  void run(ref<Store> store) override {
-    auto state = getEvalState();
-
-    auto v = installable->toValue(*state);
-
-    Value* v2;
-    try {
-      auto dummyArgs = Bindings::New();
-      v2 = findAlongAttrPath(*state, "meta.position", dummyArgs.get(), *v);
-    } catch (Error&) {
-      throw Error("package '%s' has no source location information",
-                  installable->what());
-    }
-
-    auto pos = state->forceString(*v2);
-    DLOG(INFO) << "position is " << pos;
-
-    auto colon = pos.rfind(':');
-    if (colon == std::string::npos) {
-      throw Error("cannot parse meta.position attribute '%s'", pos);
-    }
-
-    std::string filename(pos, 0, colon);
-    int lineno;
-    try {
-      lineno = std::stoi(std::string(pos, colon + 1));
-    } catch (std::invalid_argument& e) {
-      throw Error("cannot parse line number '%s'", pos);
-    }
-
-    auto editor = getEnv("EDITOR").value_or("cat");
-
-    Strings args =
-        absl::StrSplit(editor, absl::ByAnyChar(" \t\n\r"), absl::SkipEmpty());
-
-    if (editor.find("emacs") != std::string::npos ||
-        editor.find("nano") != std::string::npos ||
-        editor.find("vim") != std::string::npos) {
-      args.push_back(fmt("+%d", lineno));
-    }
-
-    args.push_back(filename);
-
-    execvp(args.front().c_str(), stringsToCharPtrs(args).data());
-
-    throw SysError("cannot run editor '%s'", editor);
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdEdit>());
diff --git a/third_party/nix/src/nix/eval.cc b/third_party/nix/src/nix/eval.cc
deleted file mode 100644
index 72fcbd8271..0000000000
--- a/third_party/nix/src/nix/eval.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "libexpr/eval.hh"
-
-#include "libexpr/value-to-json.hh"
-#include "libmain/common-args.hh"
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "libutil/json.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdEval final : MixJSON, InstallableCommand {
-  bool raw = false;
-
-  CmdEval() { mkFlag(0, "raw", "print strings unquoted", &raw); }
-
-  std::string name() override { return "eval"; }
-
-  std::string description() override { return "evaluate a Nix expression"; }
-
-  Examples examples() override {
-    return {
-        Example{"To evaluate a Nix expression given on the command line:",
-                "nix eval '(1 + 2)'"},
-        Example{"To evaluate a Nix expression from a file or URI:",
-                "nix eval -f channel:nixos-17.09 hello.name"},
-        Example{"To get the current version of Nixpkgs:",
-                "nix eval --raw nixpkgs.lib.nixpkgsVersion"},
-        Example{"To print the store path of the Hello package:",
-                "nix eval --raw nixpkgs.hello"},
-    };
-  }
-
-  void run(ref<Store> store) override {
-    if (raw && json) {
-      throw UsageError("--raw and --json are mutually exclusive");
-    }
-
-    auto state = getEvalState();
-
-    auto v = installable->toValue(*state);
-    PathSet context;
-
-    if (raw) {
-      std::cout << state->coerceToString(noPos, *v, context);
-    } else if (json) {
-      JSONPlaceholder jsonOut(std::cout);
-      printValueAsJSON(*state, true, *v, jsonOut, context);
-    } else {
-      state->forceValueDeep(*v);
-      std::cout << *v << "\n";
-    }
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdEval>());
diff --git a/third_party/nix/src/nix/hash.cc b/third_party/nix/src/nix/hash.cc
deleted file mode 100644
index 4fb262f1a8..0000000000
--- a/third_party/nix/src/nix/hash.cc
+++ /dev/null
@@ -1,152 +0,0 @@
-#include "libutil/hash.hh"
-
-#include "libmain/shared.hh"
-#include "nix/command.hh"
-#include "nix/legacy.hh"
-
-namespace nix {
-struct CmdHash final : Command {
-  enum Mode { mFile, mPath };
-  Mode mode;
-  Base base = SRI;
-  bool truncate = false;
-  HashType ht = htSHA256;
-  std::vector<std::string> paths;
-
-  explicit CmdHash(Mode mode) : mode(mode) {
-    mkFlag(0, "sri", "print hash in SRI format", &base, SRI);
-    mkFlag(0, "base64", "print hash in base-64", &base, Base64);
-    mkFlag(0, "base32", "print hash in base-32 (Nix-specific)", &base, Base32);
-    mkFlag(0, "base16", "print hash in base-16", &base, Base16);
-    mkFlag().longName("type").mkHashTypeFlag(&ht);
-    expectArgs("paths", &paths);
-  }
-
-  std::string name() override {
-    return mode == mFile ? "hash-file" : "hash-path";
-  }
-
-  std::string description() override {
-    return mode == mFile
-               ? "print cryptographic hash of a regular file"
-               : "print cryptographic hash of the NAR serialisation of a path";
-  }
-
-  void run() override {
-    for (const auto& path : paths) {
-      Hash h = mode == mFile ? hashFile(ht, path) : hashPath(ht, path).first;
-      if (truncate && h.hashSize > nix::kStorePathHashSize) {
-        h = compressHash(h, nix::kStorePathHashSize);
-      }
-      std::cout << format("%1%\n") % h.to_string(base, base == SRI);
-    }
-  }
-};
-
-static RegisterCommand r1(make_ref<CmdHash>(CmdHash::mFile));
-static RegisterCommand r2(make_ref<CmdHash>(CmdHash::mPath));
-
-struct CmdToBase final : Command {
-  Base base;
-  HashType ht = htUnknown;
-  std::vector<std::string> args;
-
-  explicit CmdToBase(Base base) : base(base) {
-    mkFlag().longName("type").mkHashTypeFlag(&ht);
-    expectArgs("strings", &args);
-  }
-
-  std::string name() override {
-    return base == Base16   ? "to-base16"
-           : base == Base32 ? "to-base32"
-           : base == Base64 ? "to-base64"
-                            : "to-sri";
-  }
-
-  std::string description() override {
-    return fmt("convert a hash to %s representation",
-               base == Base16   ? "base-16"
-               : base == Base32 ? "base-32"
-               : base == Base64 ? "base-64"
-                                : "SRI");
-  }
-
-  void run() override {
-    for (const auto& s : args) {
-      auto hash_ = Hash::deserialize(s, ht);
-      if (hash_.ok()) {
-        std::cout << hash_->to_string(base, base == SRI) << "\n";
-      } else {
-        std::cerr << "failed to parse: " << hash_.status().ToString() << "\n";
-        // create a matching blank line, for scripting
-        std::cout << "\n";
-      }
-    }
-  }
-};
-
-static RegisterCommand r3(make_ref<CmdToBase>(Base16));
-static RegisterCommand r4(make_ref<CmdToBase>(Base32));
-static RegisterCommand r5(make_ref<CmdToBase>(Base64));
-static RegisterCommand r6(make_ref<CmdToBase>(SRI));
-
-/* Legacy nix-hash command. */
-static int compatNixHash(int argc, char** argv) {
-  HashType ht = htMD5;
-  bool flat = false;
-  bool base32 = false;
-  bool truncate = false;
-  enum { opHash, opTo32, opTo16 } op = opHash;
-  std::vector<std::string> ss;
-
-  parseCmdLine(argc, argv,
-               [&](Strings::iterator& arg, const Strings::iterator& end) {
-                 if (*arg == "--help") {
-                   showManPage("nix-hash");
-                 } else if (*arg == "--version") {
-                   printVersion("nix-hash");
-                 } else if (*arg == "--flat") {
-                   flat = true;
-                 } else if (*arg == "--base32") {
-                   base32 = true;
-                 } else if (*arg == "--truncate") {
-                   truncate = true;
-                 } else if (*arg == "--type") {
-                   std::string s = getArg(*arg, arg, end);
-                   ht = parseHashType(s);
-                   if (ht == htUnknown) {
-                     throw UsageError(format("unknown hash type '%1%'") % s);
-                   }
-                 } else if (*arg == "--to-base16") {
-                   op = opTo16;
-                 } else if (*arg == "--to-base32") {
-                   op = opTo32;
-                 } else if (*arg != "" && arg->at(0) == '-') {
-                   return false;
-                 } else {
-                   ss.push_back(*arg);
-                 }
-                 return true;
-               });
-
-  if (op == opHash) {
-    CmdHash cmd(flat ? CmdHash::mFile : CmdHash::mPath);
-    cmd.ht = ht;
-    cmd.base = base32 ? Base32 : Base16;
-    cmd.truncate = truncate;
-    cmd.paths = ss;
-    cmd.run();
-  }
-
-  else {
-    CmdToBase cmd(op == opTo32 ? Base32 : Base16);
-    cmd.args = ss;
-    cmd.ht = ht;
-    cmd.run();
-  }
-
-  return 0;
-}
-
-static RegisterLegacyCommand s1("nix-hash", compatNixHash);
-}  // namespace nix
diff --git a/third_party/nix/src/nix/installables.cc b/third_party/nix/src/nix/installables.cc
deleted file mode 100644
index 7aa26b0dee..0000000000
--- a/third_party/nix/src/nix/installables.cc
+++ /dev/null
@@ -1,349 +0,0 @@
-#include <iostream>
-#include <regex>
-#include <utility>
-
-#include "libexpr/attr-path.hh"
-#include "libexpr/common-eval-args.hh"
-#include "libexpr/eval-inline.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/get-drvs.hh"
-#include "libmain/shared.hh"
-#include "libstore/derivations.hh"
-#include "libstore/store-api.hh"
-#include "libutil/status.hh"
-#include "nix/command.hh"
-
-namespace nix {
-
-SourceExprCommand::SourceExprCommand() {
-  mkFlag()
-      .shortName('f')
-      .longName("file")
-      .label("file")
-      .description("evaluate FILE rather than the default")
-      .dest(&file);
-}
-
-Value* SourceExprCommand::getSourceExpr(EvalState& state) {
-  if (vSourceExpr != nullptr) {
-    return *vSourceExpr;
-  }
-
-  auto sToplevel = state.symbols.Create("_toplevel");
-
-  // Allocate the vSourceExpr Value as uncollectable. Boehm GC doesn't
-  // consider the member variable "alive" during execution causing it to be
-  // GC'ed in the middle of evaluation.
-  vSourceExpr = allocRootValue(state.allocValue());
-
-  if (!file.empty()) {
-    state.evalFile(lookupFileArg(state, file), **vSourceExpr);
-  } else {
-    /* Construct the installation source from $NIX_PATH. */
-
-    auto searchPath = state.getSearchPath();
-
-    state.mkAttrs(**vSourceExpr, 1024);
-
-    mkBool(*state.allocAttr(**vSourceExpr, sToplevel), true);
-
-    std::unordered_set<std::string> seen;
-
-    auto addEntry = [&](const std::string& name) {
-      if (name.empty()) {
-        return;
-      }
-      if (!seen.insert(name).second) {
-        return;
-      }
-      Value* v1 = state.allocValue();
-      mkPrimOpApp(*v1, state.getBuiltin("findFile"),
-                  state.getBuiltin("nixPath"));
-      Value* v2 = state.allocValue();
-      mkApp(*v2, *v1, mkString(*state.allocValue(), name));
-      mkApp(*state.allocAttr(**vSourceExpr, state.symbols.Create(name)),
-            state.getBuiltin("import"), *v2);
-    };
-
-    for (auto& i : searchPath) { /* Hack to handle channels. */
-      if (i.first.empty() && pathExists(i.second + "/manifest.nix")) {
-        for (auto& j : readDirectory(i.second)) {
-          if (j.name != "manifest.nix" &&
-              pathExists(fmt("%s/%s/default.nix", i.second, j.name))) {
-            addEntry(j.name);
-          }
-        }
-      } else {
-        addEntry(i.first);
-      }
-    }
-  }
-
-  return *vSourceExpr;
-}
-
-ref<EvalState> SourceExprCommand::getEvalState() {
-  if (!evalState) {
-    evalState = std::make_shared<EvalState>(searchPath, getStore());
-  }
-  return ref<EvalState>(evalState);
-}
-
-Buildable Installable::toBuildable() {
-  auto buildables = toBuildables();
-  if (buildables.size() != 1) {
-    throw Error(
-        "installable '%s' evaluates to %d derivations, where only one is "
-        "expected",
-        what(), buildables.size());
-  }
-  return std::move(buildables[0]);
-}
-
-struct InstallableStorePath final : Installable {
-  Path storePath;
-
-  explicit InstallableStorePath(Path storePath)
-      : storePath(std::move(storePath)) {}
-
-  std::string what() override { return storePath; }
-
-  Buildables toBuildables() override {
-    return {{isDerivation(storePath) ? storePath : "", {{"out", storePath}}}};
-  }
-};
-
-struct InstallableValue : Installable {
-  SourceExprCommand& cmd;
-
-  explicit InstallableValue(SourceExprCommand& cmd) : cmd(cmd) {}
-
-  Buildables toBuildables() override {
-    auto state = cmd.getEvalState();
-
-    auto v = toValue(*state);
-
-    std::unique_ptr<Bindings> autoArgs = cmd.getAutoArgs(*state);
-
-    DrvInfos drvs;
-    getDerivations(*state, *v, "", autoArgs.get(), drvs, false);
-
-    Buildables res;
-
-    PathSet drvPaths;
-
-    for (auto& drv : drvs) {
-      Buildable b{drv.queryDrvPath()};
-      drvPaths.insert(b.drvPath);
-
-      auto outputName = drv.queryOutputName();
-      if (outputName.empty()) {
-        throw Error("derivation '%s' lacks an 'outputName' attribute",
-                    b.drvPath);
-      }
-
-      b.outputs.emplace(outputName, drv.queryOutPath());
-
-      res.push_back(std::move(b));
-    }
-
-    // Hack to recognize .all: if all drvs have the same drvPath,
-    // merge the buildables.
-    if (drvPaths.size() == 1) {
-      Buildable b{*drvPaths.begin()};
-      for (auto& b2 : res) {
-        b.outputs.insert(b2.outputs.begin(), b2.outputs.end());
-      }
-      return {b};
-    }
-    return res;
-  }
-};
-
-struct InstallableExpr final : InstallableValue {
-  std::string text;
-
-  InstallableExpr(SourceExprCommand& cmd, std::string text)
-      : InstallableValue(cmd), text(std::move(text)) {}
-
-  std::string what() override { return text; }
-
-  Value* toValue(EvalState& state) override {
-    auto v = state.allocValue();
-    state.eval(state.parseExprFromString(text, absPath(".")), *v);
-    return v;
-  }
-};
-
-struct InstallableAttrPath final : InstallableValue {
-  std::string attrPath;
-
-  InstallableAttrPath(SourceExprCommand& cmd, std::string attrPath)
-      : InstallableValue(cmd), attrPath(std::move(attrPath)) {}
-
-  std::string what() override { return attrPath; }
-
-  Value* toValue(EvalState& state) override {
-    auto source = cmd.getSourceExpr(state);
-
-    std::unique_ptr<Bindings> autoArgs = cmd.getAutoArgs(state);
-
-    Value* v = findAlongAttrPath(state, attrPath, autoArgs.get(), *source);
-    state.forceValue(*v);
-
-    return v;
-  }
-};
-
-// FIXME: extend
-std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)";
-static std::regex attrPathRegex(fmt(R"(%1%(\.%1%)*)", attrRegex));
-
-static std::vector<std::shared_ptr<Installable>> parseInstallables(
-    SourceExprCommand& cmd, const ref<Store>& store,
-    std::vector<std::string> ss, bool useDefaultInstallables) {
-  std::vector<std::shared_ptr<Installable>> result;
-
-  if (ss.empty() && useDefaultInstallables) {
-    if (cmd.file.empty()) {
-      cmd.file = ".";
-    }
-    ss = {""};
-  }
-
-  for (auto& s : ss) {
-    if (s.compare(0, 1, "(") == 0) {
-      result.push_back(std::make_shared<InstallableExpr>(cmd, s));
-
-    } else if (s.find('/') != std::string::npos) {
-      auto path = store->toStorePath(store->followLinksToStore(s));
-
-      if (store->isStorePath(path)) {
-        result.push_back(std::make_shared<InstallableStorePath>(path));
-      }
-    }
-
-    else if (s.empty() || std::regex_match(s, attrPathRegex)) {
-      result.push_back(std::make_shared<InstallableAttrPath>(cmd, s));
-
-    } else {
-      throw UsageError("don't know what to do with argument '%s'", s);
-    }
-  }
-
-  return result;
-}
-
-std::shared_ptr<Installable> parseInstallable(SourceExprCommand& cmd,
-                                              const ref<Store>& store,
-                                              const std::string& installable,
-                                              bool useDefaultInstallables) {
-  auto installables = parseInstallables(cmd, store, {installable}, false);
-  assert(installables.size() == 1);
-  return installables.front();
-}
-
-Buildables build(
-    const ref<Store>& store, RealiseMode mode,
-    const std::vector<std::shared_ptr<Installable>>& installables) {
-  if (mode != Build) {
-    settings.readOnlyMode = true;
-  }
-
-  Buildables buildables;
-
-  PathSet pathsToBuild;
-
-  for (auto& i : installables) {
-    for (auto& b : i->toBuildables()) {
-      if (!b.drvPath.empty()) {
-        StringSet outputNames;
-        for (auto& output : b.outputs) {
-          outputNames.insert(output.first);
-        }
-        pathsToBuild.insert(b.drvPath + "!" +
-                            concatStringsSep(",", outputNames));
-      } else {
-        for (auto& output : b.outputs) {
-          pathsToBuild.insert(output.second);
-        }
-      }
-      buildables.push_back(std::move(b));
-    }
-  }
-
-  if (mode == DryRun) {
-    printMissing(store, pathsToBuild);
-  } else if (mode == Build) {
-    util::OkOrThrow(store->buildPaths(std::cerr, pathsToBuild));
-  }
-
-  return buildables;
-}
-
-PathSet toStorePaths(
-    const ref<Store>& store, RealiseMode mode,
-    const std::vector<std::shared_ptr<Installable>>& installables) {
-  PathSet outPaths;
-
-  for (auto& b : build(store, mode, installables)) {
-    for (auto& output : b.outputs) {
-      outPaths.insert(output.second);
-    }
-  }
-
-  return outPaths;
-}
-
-Path toStorePath(const ref<Store>& store, RealiseMode mode,
-                 const std::shared_ptr<Installable>& installable) {
-  auto paths = toStorePaths(store, mode, {installable});
-
-  if (paths.size() != 1) {
-    throw Error("argument '%s' should evaluate to one store path",
-                installable->what());
-  }
-
-  return *paths.begin();
-}
-
-PathSet toDerivations(
-    const ref<Store>& store,
-    const std::vector<std::shared_ptr<Installable>>& installables,
-    bool useDeriver) {
-  PathSet drvPaths;
-
-  for (auto& i : installables) {
-    for (auto& b : i->toBuildables()) {
-      if (b.drvPath.empty()) {
-        if (!useDeriver) {
-          throw Error("argument '%s' did not evaluate to a derivation",
-                      i->what());
-        }
-        for (auto& output : b.outputs) {
-          auto derivers = store->queryValidDerivers(output.second);
-          if (derivers.empty()) {
-            throw Error("'%s' does not have a known deriver", i->what());
-          }
-          // FIXME: use all derivers?
-          drvPaths.insert(*derivers.begin());
-        }
-      } else {
-        drvPaths.insert(b.drvPath);
-      }
-    }
-  }
-
-  return drvPaths;
-}
-
-void InstallablesCommand::prepare() {
-  installables = parseInstallables(*this, getStore(), _installables,
-                                   useDefaultInstallables());
-}
-
-void InstallableCommand::prepare() {
-  installable = parseInstallable(*this, getStore(), _installable, false);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix/legacy.cc b/third_party/nix/src/nix/legacy.cc
deleted file mode 100644
index a0f9fc65b3..0000000000
--- a/third_party/nix/src/nix/legacy.cc
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "nix/legacy.hh"
-
-namespace nix {
-
-RegisterLegacyCommand::Commands* RegisterLegacyCommand::commands = nullptr;
-
-}
diff --git a/third_party/nix/src/nix/legacy.hh b/third_party/nix/src/nix/legacy.hh
deleted file mode 100644
index a0fc88da24..0000000000
--- a/third_party/nix/src/nix/legacy.hh
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include <functional>
-#include <map>
-#include <string>
-
-namespace nix {
-
-typedef std::function<void(int, char**)> MainFunction;
-
-struct RegisterLegacyCommand {
-  using Commands = std::map<std::string, MainFunction>;
-  static Commands* commands;
-
-  RegisterLegacyCommand(const std::string& name, MainFunction fun) {
-    if (!commands) {
-      commands = new Commands;
-    }
-    (*commands)[name] = fun;
-  }
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix/log.cc b/third_party/nix/src/nix/log.cc
deleted file mode 100644
index 84207d8576..0000000000
--- a/third_party/nix/src/nix/log.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-#include <glog/logging.h>
-
-#include "libmain/common-args.hh"
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdLog final : InstallableCommand {
-  CmdLog() = default;
-
-  std::string name() override { return "log"; }
-
-  std::string description() override {
-    return "show the build log of the specified packages or paths, if "
-           "available";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To get the build log of GNU Hello:", "nix log nixpkgs.hello"},
-        Example{
-            "To get the build log of a specific path:",
-            "nix log "
-            "/nix/store/lmngj4wcm9rkv3w4dfhzhcyij3195hiq-thunderbird-52.2.1"},
-        Example{"To get a build log from a specific binary cache:",
-                "nix log --store https://cache.nixos.org nixpkgs.hello"},
-    };
-  }
-
-  void run(ref<Store> store) override {
-    settings.readOnlyMode = true;
-
-    auto subs = getDefaultSubstituters();
-
-    subs.push_front(store);
-
-    auto b = installable->toBuildable();
-
-    RunPager pager;
-    for (auto& sub : subs) {
-      auto log = !b.drvPath.empty() ? sub->getBuildLog(b.drvPath) : nullptr;
-      for (auto& output : b.outputs) {
-        if (log) {
-          break;
-        }
-        log = sub->getBuildLog(output.second);
-      }
-      if (!log) {
-        continue;
-      }
-      LOG(INFO) << "got build log for '" << installable->what() << "' from '"
-                << sub->getUri() << "'";
-      std::cout << *log;
-      return;
-    }
-
-    throw Error("build log of '%s' is not available", installable->what());
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdLog>());
diff --git a/third_party/nix/src/nix/ls.cc b/third_party/nix/src/nix/ls.cc
deleted file mode 100644
index 1da722babb..0000000000
--- a/third_party/nix/src/nix/ls.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-#include "libmain/common-args.hh"
-#include "libstore/fs-accessor.hh"
-#include "libstore/nar-accessor.hh"
-#include "libstore/store-api.hh"
-#include "libutil/json.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct MixLs : virtual Args, MixJSON {
-  std::string path;
-
-  bool recursive = false;
-  bool verbose = false;
-  bool showDirectory = false;
-
-  MixLs() {
-    mkFlag('R', "recursive", "list subdirectories recursively", &recursive);
-    mkFlag('l', "long", "show more file information", &verbose);
-    mkFlag('d', "directory", "show directories rather than their contents",
-           &showDirectory);
-  }
-
-  void listText(ref<FSAccessor> accessor) {
-    std::function<void(const FSAccessor::Stat&, const Path&, const std::string&,
-                       bool)>
-        doPath;
-
-    auto showFile = [&](const Path& curPath, const std::string& relPath) {
-      if (verbose) {
-        auto st = accessor->stat(curPath);
-        std::string tp = st.type == FSAccessor::Type::tRegular
-                             ? (st.isExecutable ? "-r-xr-xr-x" : "-r--r--r--")
-                         : st.type == FSAccessor::Type::tSymlink ? "lrwxrwxrwx"
-                                                                 : "dr-xr-xr-x";
-        std::cout << (format("%s %20d %s") % tp % st.fileSize % relPath);
-        if (st.type == FSAccessor::Type::tSymlink) {
-          std::cout << " -> " << accessor->readLink(curPath);
-        }
-        std::cout << "\n";
-        if (recursive && st.type == FSAccessor::Type::tDirectory) {
-          doPath(st, curPath, relPath, false);
-        }
-      } else {
-        std::cout << relPath << "\n";
-        if (recursive) {
-          auto st = accessor->stat(curPath);
-          if (st.type == FSAccessor::Type::tDirectory) {
-            doPath(st, curPath, relPath, false);
-          }
-        }
-      }
-    };
-
-    doPath = [&](const FSAccessor::Stat& st, const Path& curPath,
-                 const std::string& relPath, bool showDirectory) {
-      if (st.type == FSAccessor::Type::tDirectory && !showDirectory) {
-        auto names = accessor->readDirectory(curPath);
-        for (auto& name : names) {
-          showFile(curPath + "/" + name, relPath + "/" + name);
-        }
-      } else {
-        showFile(curPath, relPath);
-      }
-    };
-
-    auto st = accessor->stat(path);
-    if (st.type == FSAccessor::Type::tMissing) {
-      throw Error(format("path '%1%' does not exist") % path);
-    }
-    doPath(st, path,
-           st.type == FSAccessor::Type::tDirectory ? "." : baseNameOf(path),
-           showDirectory);
-  }
-
-  void list(const ref<FSAccessor>& accessor) {
-    if (path == "/") {
-      path = "";
-    }
-
-    if (json) {
-      JSONPlaceholder jsonRoot(std::cout);
-      listNar(jsonRoot, accessor, path, recursive);
-    } else {
-      listText(accessor);
-    }
-  }
-};
-
-struct CmdLsStore final : StoreCommand, MixLs {
-  CmdLsStore() { expectArg("path", &path); }
-
-  Examples examples() override {
-    return {
-        Example{"To list the contents of a store path in a binary cache:",
-                "nix ls-store --store https://cache.nixos.org/ -lR "
-                "/nix/store/0i2jd68mp5g6h2sa5k9c85rb80sn8hi9-hello-2.10"},
-    };
-  }
-
-  std::string name() override { return "ls-store"; }
-
-  std::string description() override {
-    return "show information about a store path";
-  }
-
-  void run(ref<Store> store) override { list(store->getFSAccessor()); }
-};
-
-struct CmdLsNar final : Command, MixLs {
-  Path narPath;
-
-  CmdLsNar() {
-    expectArg("nar", &narPath);
-    expectArg("path", &path);
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To list a specific file in a NAR:",
-                "nix ls-nar -l hello.nar /bin/hello"},
-    };
-  }
-
-  std::string name() override { return "ls-nar"; }
-
-  std::string description() override {
-    return "show information about the contents of a NAR file";
-  }
-
-  void run() override {
-    list(makeNarAccessor(make_ref<std::string>(readFile(narPath, true))));
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdLsStore>());
-static nix::RegisterCommand r2(nix::make_ref<nix::CmdLsNar>());
diff --git a/third_party/nix/src/nix/main.cc b/third_party/nix/src/nix/main.cc
deleted file mode 100644
index 08390fd24b..0000000000
--- a/third_party/nix/src/nix/main.cc
+++ /dev/null
@@ -1,185 +0,0 @@
-#include <algorithm>
-
-#include <glog/logging.h>
-#include <ifaddrs.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include "libexpr/eval.hh"
-#include "libmain/common-args.hh"
-#include "libmain/shared.hh"
-#include "libstore/download.hh"
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libutil/finally.hh"
-#include "nix/command.hh"
-#include "nix/legacy.hh"
-
-extern std::string chrootHelperName;
-
-void chrootHelper(int argc, char** argv);
-
-namespace nix {
-
-/* Check if we have a non-loopback/link-local network interface. */
-static bool haveInternet() {
-  struct ifaddrs* addrs;
-
-  if (getifaddrs(&addrs) != 0) {
-    return true;
-  }
-
-  Finally free([&]() { freeifaddrs(addrs); });
-
-  for (auto i = addrs; i != nullptr; i = i->ifa_next) {
-    if (i->ifa_addr == nullptr) {
-      continue;
-    }
-    if (i->ifa_addr->sa_family == AF_INET) {
-      if (ntohl(
-              (reinterpret_cast<sockaddr_in*>(i->ifa_addr))->sin_addr.s_addr) !=
-          INADDR_LOOPBACK) {
-        return true;
-      }
-    } else if (i->ifa_addr->sa_family == AF_INET6) {
-      if (!IN6_IS_ADDR_LOOPBACK(&((sockaddr_in6*)i->ifa_addr)->sin6_addr) &&
-          !IN6_IS_ADDR_LINKLOCAL(&((sockaddr_in6*)i->ifa_addr)->sin6_addr)) {
-        return true;
-      }
-    }
-  }
-
-  return false;
-}
-
-std::string programPath;
-
-struct NixArgs : virtual MultiCommand, virtual MixCommonArgs {
-  bool printBuildLogs = false;
-  bool useNet = true;
-
-  NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix") {
-    mkFlag()
-        .longName("help")
-        .description("show usage information")
-        .handler([&]() { showHelpAndExit(); });
-
-    mkFlag()
-        .longName("help-config")
-        .description("show configuration options")
-        .handler([&]() {
-          std::cout << "The following configuration options are available:\n\n";
-          Table2 tbl;
-          std::map<std::string, Config::SettingInfo> settings;
-          globalConfig.getSettings(settings);
-          for (const auto& s : settings) {
-            tbl.emplace_back(s.first, s.second.description);
-          }
-          printTable(std::cout, tbl);
-          throw Exit();
-        });
-
-    mkFlag()
-        .longName("print-build-logs")
-        .shortName('L')
-        .description("print full build logs on stderr")
-        .set(&printBuildLogs, true);
-
-    mkFlag()
-        .longName("version")
-        .description("show version information")
-        .handler([&]() { printVersion(programName); });
-
-    mkFlag()
-        .longName("no-net")
-        .description(
-            "disable substituters and consider all previously downloaded files "
-            "up-to-date")
-        .handler([&]() { useNet = false; });
-  }
-
-  void printFlags(std::ostream& out) override {
-    Args::printFlags(out);
-    std::cout << "\n"
-                 "In addition, most configuration settings can be overriden "
-                 "using '--<name> <value>'.\n"
-                 "Boolean settings can be overriden using '--<name>' or "
-                 "'--no-<name>'. See 'nix\n"
-                 "--help-config' for a list of configuration settings.\n";
-  }
-
-  void showHelpAndExit() {
-    printHelp(programName, std::cout);
-    std::cout
-        << "\nNote: this program is EXPERIMENTAL and subject to change.\n";
-    throw Exit();
-  }
-};
-
-void mainWrapped(int argc, char** argv) {
-  /* The chroot helper needs to be run before any threads have been
-     started. */
-  if (argc > 0 && argv[0] == chrootHelperName) {
-    chrootHelper(argc, argv);
-    return;
-  }
-
-  initNix();
-
-  programPath = argv[0];
-  std::string programName = baseNameOf(programPath);
-
-  {
-    auto legacy = (*RegisterLegacyCommand::commands)[programName];
-    if (legacy) {
-      return legacy(argc, argv);
-    }
-  }
-
-  settings.verboseBuild = false;
-
-  NixArgs args;
-
-  args.parseCmdline(argvToStrings(argc, argv));
-
-  if (!args.command) {
-    args.showHelpAndExit();
-  }
-
-  if (args.useNet && !haveInternet()) {
-    LOG(WARNING) << "you don't have Internet access; "
-                 << "disabling some network-dependent features";
-    args.useNet = false;
-  }
-
-  if (!args.useNet) {
-    // FIXME: should check for command line overrides only.
-    if (!settings.useSubstitutes.overriden) {
-      settings.useSubstitutes = false;
-    }
-    if (!settings.tarballTtl.overriden) {
-      settings.tarballTtl = std::numeric_limits<unsigned int>::max();
-    }
-    if (!downloadSettings.tries.overriden) {
-      downloadSettings.tries = 0;
-    }
-    if (!downloadSettings.connectTimeout.overriden) {
-      downloadSettings.connectTimeout = 1;
-    }
-  }
-
-  args.command->prepare();
-  args.command->run();
-}
-
-}  // namespace nix
-
-int main(int argc, char* argv[]) {
-  FLAGS_logtostderr = true;
-  google::InitGoogleLogging(argv[0]);
-
-  return nix::handleExceptions(argv[0],
-                               [&]() { nix::mainWrapped(argc, argv); });
-}
diff --git a/third_party/nix/src/nix/optimise-store.cc b/third_party/nix/src/nix/optimise-store.cc
deleted file mode 100644
index ceb53aa77b..0000000000
--- a/third_party/nix/src/nix/optimise-store.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <atomic>
-
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdOptimiseStore final : StoreCommand {
-  CmdOptimiseStore() = default;
-
-  std::string name() override { return "optimise-store"; }
-
-  std::string description() override {
-    return "replace identical files in the store by hard links";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To optimise the Nix store:", "nix optimise-store"},
-    };
-  }
-
-  void run(ref<Store> store) override { store->optimiseStore(); }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdOptimiseStore>());
diff --git a/third_party/nix/src/nix/path-info.cc b/third_party/nix/src/nix/path-info.cc
deleted file mode 100644
index fcf060d50d..0000000000
--- a/third_party/nix/src/nix/path-info.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-#include <algorithm>
-#include <array>
-
-#include "libmain/common-args.hh"
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "libutil/json.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdPathInfo final : StorePathsCommand, MixJSON {
-  bool showSize = false;
-  bool showClosureSize = false;
-  bool humanReadable = false;
-  bool showSigs = false;
-
-  CmdPathInfo() {
-    mkFlag('s', "size", "print size of the NAR dump of each path", &showSize);
-    mkFlag('S', "closure-size",
-           "print sum size of the NAR dumps of the closure of each path",
-           &showClosureSize);
-    mkFlag('h', "human-readable",
-           "with -s and -S, print sizes like 1K 234M 5.67G etc.",
-           &humanReadable);
-    mkFlag(0, "sigs", "show signatures", &showSigs);
-  }
-
-  std::string name() override { return "path-info"; }
-
-  std::string description() override {
-    return "query information about store paths";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To show the closure sizes of every path in the current NixOS "
-                "system closure, sorted by size:",
-                "nix path-info -rS /run/current-system | sort -nk2"},
-        Example{"To show a package's closure size and all its dependencies "
-                "with human readable sizes:",
-                "nix path-info -rsSh nixpkgs.rust"},
-        Example{"To check the existence of a path in a binary cache:",
-                "nix path-info -r /nix/store/7qvk5c91...-geeqie-1.1 --store "
-                "https://cache.nixos.org/"},
-        Example{"To print the 10 most recently added paths (using --json and "
-                "the jq(1) command):",
-                "nix path-info --json --all | jq -r "
-                "'sort_by(.registrationTime)[-11:-1][].path'"},
-        Example{"To show the size of the entire Nix store:",
-                "nix path-info --json --all | jq 'map(.narSize) | add'"},
-        Example{"To show every path whose closure is bigger than 1 GB, sorted "
-                "by closure size:",
-                "nix path-info --json --all -S | jq 'map(select(.closureSize > "
-                "1e9)) | sort_by(.closureSize) | map([.path, .closureSize])'"},
-    };
-  }
-
-  void printSize(unsigned long long value) {
-    if (!humanReadable) {
-      std::cout << fmt("\t%11d", value);
-      return;
-    }
-
-    static const std::array<char, 9> idents{
-        {' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'}};
-    size_t power = 0;
-    double res = value;
-    while (res > 1024 && power < idents.size()) {
-      ++power;
-      res /= 1024;
-    }
-    std::cout << fmt("\t%6.1f%c", res, idents.at(power));
-  }
-
-  void run(ref<Store> store, Paths storePaths) override {
-    size_t pathLen = 0;
-    for (auto& storePath : storePaths) {
-      pathLen = std::max(pathLen, storePath.size());
-    }
-
-    if (json) {
-      JSONPlaceholder jsonRoot(std::cout);
-      store->pathInfoToJSON(jsonRoot,
-                            // FIXME: preserve order?
-                            PathSet(storePaths.begin(), storePaths.end()), true,
-                            showClosureSize, AllowInvalid);
-    }
-
-    else {
-      for (auto storePath : storePaths) {
-        auto info = store->queryPathInfo(storePath);
-        storePath = info->path;  // FIXME: screws up padding
-
-        std::cout << storePath;
-
-        if (showSize || showClosureSize || showSigs) {
-          std::cout << std::string(
-              std::max(0, static_cast<int>(pathLen) -
-                              static_cast<int>(storePath.size())),
-              ' ');
-        }
-
-        if (showSize) {
-          printSize(info->narSize);
-        }
-
-        if (showClosureSize) {
-          printSize(store->getClosureSize(storePath).first);
-        }
-
-        if (showSigs) {
-          std::cout << '\t';
-          Strings ss;
-          if (info->ultimate) {
-            ss.push_back("ultimate");
-          }
-          if (!info->ca.empty()) {
-            ss.push_back("ca:" + info->ca);
-          }
-          for (auto& sig : info->sigs) {
-            ss.push_back(sig);
-          }
-          std::cout << concatStringsSep(" ", ss);
-        }
-
-        std::cout << std::endl;
-      }
-    }
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdPathInfo>());
diff --git a/third_party/nix/src/nix/ping-store.cc b/third_party/nix/src/nix/ping-store.cc
deleted file mode 100644
index 4a33486bf8..0000000000
--- a/third_party/nix/src/nix/ping-store.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdPingStore final : StoreCommand {
-  std::string name() override { return "ping-store"; }
-
-  std::string description() override {
-    return "test whether a store can be opened";
-  }
-
-  Examples examples() override {
-    return {
-        Example{
-            "To test whether connecting to a remote Nix store via SSH works:",
-            "nix ping-store --store ssh://mac1"},
-    };
-  }
-
-  void run(ref<Store> store) override { store->connect(); }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdPingStore>());
diff --git a/third_party/nix/src/nix/repl.cc b/third_party/nix/src/nix/repl.cc
deleted file mode 100644
index b926d195ae..0000000000
--- a/third_party/nix/src/nix/repl.cc
+++ /dev/null
@@ -1,819 +0,0 @@
-#include <climits>
-#include <csetjmp>
-#include <cstdlib>
-#include <cstring>
-#include <iostream>
-#include <utility>
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/match.h>
-#include <editline.h>
-#include <glog/logging.h>
-
-#include "libexpr/common-eval-args.hh"
-#include "libexpr/eval-inline.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/get-drvs.hh"
-#include "libmain/shared.hh"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/store-api.hh"
-#include "libutil/affinity.hh"
-#include "libutil/finally.hh"
-#include "nix/command.hh"
-
-namespace nix {
-
-#define ESC_RED "\033[31m"
-#define ESC_GRE "\033[32m"
-#define ESC_YEL "\033[33m"
-#define ESC_BLU "\033[34;1m"
-#define ESC_MAG "\033[35m"
-#define ESC_CYA "\033[36m"
-#define ESC_END "\033[0m"
-
-struct NixRepl {
-  std::string curDir;
-  EvalState state;
-  std::unique_ptr<Bindings> autoArgs;
-
-  Strings loadedFiles;
-
-  const static int envSize = 32768;
-  StaticEnv staticEnv;
-  Env* env;
-  int displ;
-  StringSet varNames;
-
-  const Path historyFile;
-
-  NixRepl(const Strings& searchPath, const nix::ref<Store>& store);
-  ~NixRepl();
-  void mainLoop(const std::vector<std::string>& files);
-  StringSet completePrefix(const std::string& prefix);
-  static bool getLine(std::string& input, const std::string& prompt);
-  Path getDerivationPath(Value& v);
-  bool processLine(std::string line);
-  void loadFile(const Path& path);
-  void initEnv();
-  void reloadFiles();
-  void addAttrsToScope(Value& attrs);
-  void addVarToScope(const Symbol& name, Value& v);
-  Expr* parseString(const std::string& s);
-  void evalString(std::string s, Value& v);
-
-  using ValuesSeen = std::set<Value*>;
-  std::ostream& printValue(std::ostream& str, Value& v, unsigned int maxDepth);
-  std::ostream& printValue(std::ostream& str, Value& v, unsigned int maxDepth,
-                           ValuesSeen& seen);
-};
-
-void printHelp() {
-  std::cout << "Usage: nix-repl [--help] [--version] [-I path] paths...\n"
-            << "\n"
-            << "nix-repl is a simple read-eval-print loop (REPL) for the Nix "
-               "package manager.\n"
-            << "\n"
-            << "Options:\n"
-            << "    --help\n"
-            << "        Prints out a summary of the command syntax and exits.\n"
-            << "\n"
-            << "    --version\n"
-            << "        Prints out the Nix version number on standard output "
-               "and exits.\n"
-            << "\n"
-            << "    -I path\n"
-            << "        Add a path to the Nix expression search path. This "
-               "option may be given\n"
-            << "        multiple times. See the NIX_PATH environment variable "
-               "for information on\n"
-            << "        the semantics of the Nix search path. Paths added "
-               "through -I take\n"
-            << "        precedence over NIX_PATH.\n"
-            << "\n"
-            << "    paths...\n"
-            << "        A list of paths to files containing Nix expressions "
-               "which nix-repl will\n"
-            << "        load and add to its scope.\n"
-            << "\n"
-            << "        A path surrounded in < and > will be looked up in the "
-               "Nix expression search\n"
-            << "        path, as in the Nix language itself.\n"
-            << "\n"
-            << "        If an element of paths starts with http:// or "
-               "https://, it is interpreted\n"
-            << "        as the URL of a tarball that will be downloaded and "
-               "unpacked to a temporary\n"
-            << "        location. The tarball must include a single top-level "
-               "directory containing\n"
-            << "        at least a file named default.nix.\n";
-}
-
-std::string removeWhitespace(std::string s) {
-  s = absl::StripTrailingAsciiWhitespace(s);
-  size_t n = s.find_first_not_of(" \n\r\t");
-  if (n != std::string::npos) {
-    s = std::string(s, n);
-  }
-  return s;
-}
-
-NixRepl::NixRepl(const Strings& searchPath, const nix::ref<Store>& store)
-    : state(searchPath, store),
-      staticEnv(false, &state.staticBaseEnv),
-      historyFile(getDataDir() + "/nix/repl-history") {
-  curDir = absPath(".");
-}
-
-NixRepl::~NixRepl() { write_history(historyFile.c_str()); }
-
-static NixRepl* curRepl;  // ugly
-
-static char* completionCallback(char* s, int* match) {
-  auto possible = curRepl->completePrefix(s);
-  if (possible.size() == 1) {
-    *match = 1;
-    auto* res = strdup(possible.begin()->c_str() + strlen(s));
-    if (res == nullptr) {
-      throw Error("allocation failure");
-    }
-    return res;
-  }
-  if (possible.size() > 1) {
-    auto checkAllHaveSameAt = [&](size_t pos) {
-      auto& first = *possible.begin();
-      for (auto& p : possible) {
-        if (p.size() <= pos || p[pos] != first[pos]) {
-          return false;
-        }
-      }
-      return true;
-    };
-    size_t start = strlen(s);
-    size_t len = 0;
-    while (checkAllHaveSameAt(start + len)) {
-      ++len;
-    }
-    if (len > 0) {
-      *match = 1;
-      auto* res = strdup(std::string(*possible.begin(), start, len).c_str());
-      if (res == nullptr) {
-        throw Error("allocation failure");
-      }
-      return res;
-    }
-  }
-
-  *match = 0;
-  return nullptr;
-}
-
-static int listPossibleCallback(char* s, char*** avp) {
-  auto possible = curRepl->completePrefix(s);
-
-  if (possible.size() > (INT_MAX / sizeof(char*))) {
-    throw Error("too many completions");
-  }
-
-  int ac = 0;
-  char** vp = nullptr;
-
-  auto check = [&](auto* p) {
-    if (!p) {
-      if (vp) {
-        while (--ac >= 0) {
-          free(vp[ac]);
-        }
-        free(vp);
-      }
-      throw Error("allocation failure");
-    }
-    return p;
-  };
-
-  vp = check(static_cast<char**>(malloc(possible.size() * sizeof(char*))));
-
-  for (auto& p : possible) {
-    vp[ac++] = check(strdup(p.c_str()));
-  }
-
-  *avp = vp;
-
-  return ac;
-}
-
-namespace {
-// Used to communicate to NixRepl::getLine whether a signal occurred in
-// ::readline.
-volatile sig_atomic_t g_signal_received = 0;
-
-void sigintHandler(int signo) { g_signal_received = signo; }
-}  // namespace
-
-void NixRepl::mainLoop(const std::vector<std::string>& files) {
-  std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
-  std::cout << "Welcome to Nix version " << nixVersion << ". Type :? for help."
-            << std::endl
-            << std::endl;
-
-  for (auto& i : files) {
-    loadedFiles.push_back(i);
-  }
-
-  reloadFiles();
-  if (!loadedFiles.empty()) {
-    std::cout << std::endl;
-  }
-
-  // Allow nix-repl specific settings in .inputrc
-  rl_readline_name = "nix-repl";
-  createDirs(dirOf(historyFile));
-  el_hist_size = 1000;
-  read_history(historyFile.c_str());
-  curRepl = this;
-  rl_set_complete_func(completionCallback);
-  rl_set_list_possib_func(listPossibleCallback);
-
-  std::string input;
-
-  while (true) {
-    // When continuing input from previous lines, don't print a prompt, just
-    // align to the same number of chars as the prompt.
-    if (!getLine(input, input.empty() ? "nix-repl> " : "          ")) {
-      break;
-    }
-
-    try {
-      if (!removeWhitespace(input).empty() && !processLine(input)) {
-        return;
-      }
-    } catch (ParseError& e) {
-      if (e.msg().find("unexpected $end") != std::string::npos) {
-        // For parse errors on incomplete input, we continue waiting for the
-        // next line of input without clearing the input so far.
-        continue;
-      }
-      LOG(ERROR) << error << (settings.showTrace ? e.prefix() : "") << e.msg();
-
-    } catch (Error& e) {
-      LOG(ERROR) << error << (settings.showTrace ? e.prefix() : "") << e.msg();
-    } catch (Interrupted& e) {
-      LOG(ERROR) << error << (settings.showTrace ? e.prefix() : "") << e.msg();
-    }
-
-    // We handled the current input fully, so we should clear it
-    // and read brand new input.
-    input.clear();
-    std::cout << std::endl;
-  }
-}
-
-bool NixRepl::getLine(std::string& input, const std::string& prompt) {
-  struct sigaction act;
-  struct sigaction old;
-  sigset_t savedSignalMask;
-  sigset_t set;
-
-  auto setupSignals = [&]() {
-    act.sa_handler = sigintHandler;
-    sigfillset(&act.sa_mask);
-    act.sa_flags = 0;
-    if (sigaction(SIGINT, &act, &old) != 0) {
-      throw SysError("installing handler for SIGINT");
-    }
-
-    sigemptyset(&set);
-    sigaddset(&set, SIGINT);
-    if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask) != 0) {
-      throw SysError("unblocking SIGINT");
-    }
-  };
-  auto restoreSignals = [&]() {
-    if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr) != 0) {
-      throw SysError("restoring signals");
-    }
-
-    if (sigaction(SIGINT, &old, nullptr) != 0) {
-      throw SysError("restoring handler for SIGINT");
-    }
-  };
-
-  setupSignals();
-  char* s = readline(prompt.c_str());
-  Finally doFree([&]() { free(s); });
-  restoreSignals();
-
-  if (g_signal_received != 0) {
-    g_signal_received = 0;
-    input.clear();
-    return true;
-  }
-
-  if (s == nullptr) {
-    return false;
-  }
-  input += s;
-  input += '\n';
-  return true;
-}
-
-StringSet NixRepl::completePrefix(const std::string& prefix) {
-  StringSet completions;
-
-  size_t start = prefix.find_last_of(" \n\r\t(){}[]");
-  std::string prev;
-  std::string cur;
-  if (start == std::string::npos) {
-    prev = "";
-    cur = prefix;
-  } else {
-    prev = std::string(prefix, 0, start + 1);
-    cur = std::string(prefix, start + 1);
-  }
-
-  size_t slash;
-  size_t dot;
-
-  if ((slash = cur.rfind('/')) != std::string::npos) {
-    try {
-      auto dir = std::string(cur, 0, slash);
-      auto prefix2 = std::string(cur, slash + 1);
-      for (auto& entry : readDirectory(dir.empty() ? "/" : dir)) {
-        if (entry.name[0] != '.' && absl::StartsWith(entry.name, prefix2)) {
-          completions.insert(prev + dir + "/" + entry.name);
-        }
-      }
-    } catch (Error&) {
-    }
-  } else if ((dot = cur.rfind('.')) == std::string::npos) {
-    /* This is a variable name; look it up in the current scope. */
-    auto i = varNames.lower_bound(cur);
-    while (i != varNames.end()) {
-      if (std::string(*i, 0, cur.size()) != cur) {
-        break;
-      }
-      completions.insert(prev + *i);
-      i++;
-    }
-  } else {
-    try {
-      /* This is an expression that should evaluate to an
-         attribute set.  Evaluate it to get the names of the
-         attributes. */
-      std::string expr(cur, 0, dot);
-      std::string cur2 = std::string(cur, dot + 1);
-
-      Expr* e = parseString(expr);
-      Value v;
-      e->eval(state, *env, v);
-      state.forceAttrs(v);
-
-      for (auto& i : *v.attrs) {
-        std::string name = i.second.name;
-        if (std::string(name, 0, cur2.size()) != cur2) {
-          continue;
-        }
-        completions.insert(prev + expr + "." + name);
-      }
-
-    } catch (ParseError& e) {
-      // Quietly ignore parse errors.
-    } catch (EvalError& e) {
-      // Quietly ignore evaluation errors.
-    } catch (UndefinedVarError& e) {
-      // Quietly ignore undefined variable errors.
-    }
-  }
-
-  return completions;
-}
-
-static int runProgram(const std::string& program, const Strings& args) {
-  Strings args2(args);
-  args2.push_front(program);
-
-  Pid pid;
-  pid = fork();
-  if (pid == Pid(-1)) {
-    throw SysError("forking");
-  }
-  if (pid == Pid(0)) {
-    restoreAffinity();
-    execvp(program.c_str(), stringsToCharPtrs(args2).data());
-    _exit(1);
-  }
-
-  return pid.wait();
-}
-
-bool isVarName(const std::string& s) {
-  if (s.empty()) {
-    return false;
-  }
-  char c = s[0];
-  if ((c >= '0' && c <= '9') || c == '-' || c == '\'') {
-    return false;
-  }
-  for (auto& i : s) {
-    if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') ||
-          (i >= '0' && i <= '9') || i == '_' || i == '-' || i == '\'')) {
-      return false;
-    }
-  }
-  return true;
-}
-
-Path NixRepl::getDerivationPath(Value& v) {
-  auto drvInfo = getDerivation(state, v, false);
-  if (!drvInfo) {
-    throw Error(
-        "expression does not evaluate to a derivation, so I can't build it");
-  }
-  Path drvPath = drvInfo->queryDrvPath();
-  if (drvPath.empty() || !state.store->isValidPath(drvPath)) {
-    throw Error("expression did not evaluate to a valid derivation");
-  }
-  return drvPath;
-}
-
-bool NixRepl::processLine(std::string line) {
-  if (line.empty()) {
-    return true;
-  }
-
-  std::string command;
-  std::string arg;
-
-  if (line[0] == ':') {
-    size_t p = line.find_first_of(" \n\r\t");
-    command = std::string(line, 0, p);
-    if (p != std::string::npos) {
-      arg = removeWhitespace(std::string(line, p));
-    }
-  } else {
-    arg = line;
-  }
-
-  if (command == ":?" || command == ":help") {
-    std::cout << "The following commands are available:\n"
-              << "\n"
-              << "  <expr>        Evaluate and print expression\n"
-              << "  <x> = <expr>  Bind expression to variable\n"
-              << "  :a <expr>     Add attributes from resulting set to scope\n"
-              << "  :b <expr>     Build derivation\n"
-              << "  :i <expr>     Build derivation, then install result into "
-                 "current profile\n"
-              << "  :l <path>     Load Nix expression and add it to scope\n"
-              << "  :p <expr>     Evaluate and print expression recursively\n"
-              << "  :q            Exit nix-repl\n"
-              << "  :r            Reload all files\n"
-              << "  :s <expr>     Build dependencies of derivation, then start "
-                 "nix-shell\n"
-              << "  :t <expr>     Describe result of evaluation\n"
-              << "  :u <expr>     Build derivation, then start nix-shell\n";
-  }
-
-  else if (command == ":a" || command == ":add") {
-    Value v;
-    evalString(arg, v);
-    addAttrsToScope(v);
-  }
-
-  else if (command == ":l" || command == ":load") {
-    state.resetFileCache();
-    loadFile(arg);
-  }
-
-  else if (command == ":r" || command == ":reload") {
-    state.resetFileCache();
-    reloadFiles();
-  }
-
-  else if (command == ":t") {
-    Value v;
-    evalString(arg, v);
-    std::cout << showType(v) << std::endl;
-
-  } else if (command == ":u") {
-    Value v;
-    Value f;
-    Value result;
-    evalString(arg, v);
-    evalString(
-        "drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv "
-        "]; } \"\"",
-        f);
-    state.callFunction(f, v, result, Pos());
-
-    Path drvPath = getDerivationPath(result);
-    runProgram(settings.nixBinDir + "/nix-shell", Strings{drvPath});
-  }
-
-  else if (command == ":b" || command == ":i" || command == ":s") {
-    Value v;
-    evalString(arg, v);
-    Path drvPath = getDerivationPath(v);
-
-    if (command == ":b") {
-      /* We could do the build in this process using buildPaths(),
-         but doing it in a child makes it easier to recover from
-         problems / SIGINT. */
-      if (runProgram(settings.nixBinDir + "/nix",
-                     Strings{"build", "--no-link", drvPath}) == 0) {
-        Derivation drv = readDerivation(drvPath);
-        std::cout << std::endl
-                  << "this derivation produced the following outputs:"
-                  << std::endl;
-        for (auto& i : drv.outputs) {
-          std::cout << format("  %1% -> %2%") % i.first % i.second.path
-                    << std::endl;
-        }
-      }
-    } else if (command == ":i") {
-      runProgram(settings.nixBinDir + "/nix-env", Strings{"-i", drvPath});
-    } else {
-      runProgram(settings.nixBinDir + "/nix-shell", Strings{drvPath});
-    }
-  }
-
-  else if (command == ":p" || command == ":print") {
-    Value v;
-    evalString(arg, v);
-    printValue(std::cout, v, 1000000000) << std::endl;
-  }
-
-  else if (command == ":q" || command == ":quit") {
-    return false;
-
-  } else if (!command.empty()) {
-    throw Error(format("unknown command '%1%'") % command);
-
-  } else {
-    size_t p = line.find('=');
-    std::string name;
-    if (p != std::string::npos && p < line.size() && line[p + 1] != '=' &&
-        isVarName(name = removeWhitespace(std::string(line, 0, p)))) {
-      Expr* e = parseString(std::string(line, p + 1));
-      Value& v(*state.allocValue());
-      v.type = tThunk;
-      v.thunk.env = env;
-      v.thunk.expr = e;
-      addVarToScope(state.symbols.Create(name), v);
-    } else {
-      Value v;
-      evalString(line, v);
-      printValue(std::cout, v, 1) << std::endl;
-    }
-  }
-
-  return true;
-}
-
-void NixRepl::loadFile(const Path& path) {
-  loadedFiles.remove(path);
-  loadedFiles.push_back(path);
-  Value v;
-  Value v2;
-  state.evalFile(lookupFileArg(state, path), v);
-  state.autoCallFunction(autoArgs.get(), v, v2);
-  addAttrsToScope(v2);
-}
-
-void NixRepl::initEnv() {
-  env = &state.allocEnv(envSize);
-  env->up = &state.baseEnv;
-  displ = 0;
-  staticEnv.vars.clear();
-
-  varNames.clear();
-  for (auto& i : state.staticBaseEnv.vars) {
-    varNames.insert(i.first);
-  }
-}
-
-void NixRepl::reloadFiles() {
-  initEnv();
-
-  Strings old = loadedFiles;
-  loadedFiles.clear();
-
-  bool first = true;
-  for (auto& i : old) {
-    if (!first) {
-      std::cout << std::endl;
-    }
-    first = false;
-    std::cout << format("Loading '%1%'...") % i << std::endl;
-    loadFile(i);
-  }
-}
-
-void NixRepl::addAttrsToScope(Value& attrs) {
-  state.forceAttrs(attrs);
-  for (auto& i : *attrs.attrs) {
-    addVarToScope(i.second.name, *i.second.value);
-  }
-  std::cout << format("Added %1% variables.") % attrs.attrs->size()
-            << std::endl;
-}
-
-void NixRepl::addVarToScope(const Symbol& name, Value& v) {
-  if (displ >= envSize) {
-    throw Error("environment full; cannot add more variables");
-  }
-  staticEnv.vars[name] = displ;
-  env->values[displ++] = &v;
-  varNames.insert(std::string(name));
-}
-
-Expr* NixRepl::parseString(const std::string& s) {
-  Expr* e = state.parseExprFromString(s, curDir, staticEnv);
-  return e;
-}
-
-void NixRepl::evalString(std::string s, Value& v) {
-  Expr* e = parseString(std::move(s));
-  e->eval(state, *env, v);
-  state.forceValue(v);
-}
-
-std::ostream& NixRepl::printValue(std::ostream& str, Value& v,
-                                  unsigned int maxDepth) {
-  ValuesSeen seen;
-  return printValue(str, v, maxDepth, seen);
-}
-
-std::ostream& printStringValue(std::ostream& str, const char* string) {
-  str << "\"";
-  for (const char* i = string; *i != 0; i++) {
-    if (*i == '\"' || *i == '\\') {
-      str << "\\" << *i;
-    } else if (*i == '\n') {
-      str << "\\n";
-    } else if (*i == '\r') {
-      str << "\\r";
-    } else if (*i == '\t') {
-      str << "\\t";
-    } else {
-      str << *i;
-    }
-  }
-  str << "\"";
-  return str;
-}
-
-// FIXME: lot of cut&paste from Nix's eval.cc.
-std::ostream& NixRepl::printValue(std::ostream& str, Value& v,
-                                  unsigned int maxDepth, ValuesSeen& seen) {
-  str.flush();
-  checkInterrupt();
-
-  state.forceValue(v);
-
-  switch (v.type) {
-    case tInt:
-      str << ESC_CYA << v.integer << ESC_END;
-      break;
-
-    case tBool:
-      str << ESC_CYA << (v.boolean ? "true" : "false") << ESC_END;
-      break;
-
-    case tString:
-      str << ESC_YEL;
-      printStringValue(str, v.string.s);
-      str << ESC_END;
-      break;
-
-    case tPath:
-      str << ESC_GRE << v.path << ESC_END;  // !!! escaping?
-      break;
-
-    case tNull:
-      str << ESC_CYA "null" ESC_END;
-      break;
-
-    case tAttrs: {
-      seen.insert(&v);
-
-      bool isDrv = state.isDerivation(v);
-
-      if (isDrv) {
-        str << "Β«derivation ";
-        Bindings::iterator i = v.attrs->find(state.sDrvPath);
-        PathSet context;
-        Path drvPath =
-            i != v.attrs->end()
-                ? state.coerceToPath(*i->second.pos, *i->second.value, context)
-                : "???";
-        str << drvPath << "Β»";
-      }
-
-      else if (maxDepth > 0) {
-        str << "{ ";
-
-        typedef std::map<std::string, Value*> Sorted;
-        Sorted sorted;
-        for (auto& i : *v.attrs) {
-          sorted[i.second.name] = i.second.value;
-        }
-
-        for (auto& i : sorted) {
-          if (isVarName(i.first)) {
-            str << i.first;
-          } else {
-            printStringValue(str, i.first.c_str());
-          }
-          str << " = ";
-          if (seen.find(i.second) != seen.end()) {
-            str << "Β«repeatedΒ»";
-          } else {
-            try {
-              printValue(str, *i.second, maxDepth - 1, seen);
-            } catch (AssertionError& e) {
-              str << ESC_RED "Β«error: " << e.msg() << "Β»" ESC_END;
-            }
-          }
-          str << "; ";
-        }
-
-        str << "}";
-      } else {
-        str << "{ ... }";
-      }
-
-      break;
-    }
-
-    case tList:
-      seen.insert(&v);
-
-      str << "[ ";
-      if (maxDepth > 0) {
-        for (unsigned int n = 0; n < v.listSize(); ++n) {
-          if (seen.find((*v.list)[n]) != seen.end()) {
-            str << "Β«repeatedΒ»";
-          } else {
-            try {
-              printValue(str, *(*v.list)[n], maxDepth - 1, seen);
-            } catch (AssertionError& e) {
-              str << ESC_RED "Β«error: " << e.msg() << "Β»" ESC_END;
-            }
-          }
-          str << " ";
-        }
-      } else {
-        str << "... ";
-      }
-
-      str << "]";
-      break;
-
-    case tLambda: {
-      std::ostringstream s;
-      s << v.lambda.fun->pos;
-      str << ESC_BLU "Β«lambda @ " << filterANSIEscapes(s.str()) << "Β»" ESC_END;
-      break;
-    }
-
-    case tPrimOp:
-      str << ESC_MAG "Β«primopΒ»" ESC_END;
-      break;
-
-    case tPrimOpApp:
-      str << ESC_BLU "Β«primop-appΒ»" ESC_END;
-      break;
-
-    case tFloat:
-      str << v.fpoint;
-      break;
-
-    default:
-      str << ESC_RED "Β«unknownΒ»" ESC_END;
-      break;
-  }
-
-  return str;
-}
-
-struct CmdRepl final : StoreCommand, MixEvalArgs {
-  std::vector<std::string> files;
-
-  CmdRepl() { expectArgs("files", &files); }
-
-  std::string name() override { return "repl"; }
-
-  std::string description() override {
-    return "start an interactive environment for evaluating Nix expressions";
-  }
-
-  void run(ref<Store> store) override {
-    auto repl = std::make_unique<NixRepl>(searchPath, openStore());
-    repl->autoArgs = getAutoArgs(repl->state);
-    repl->mainLoop(files);
-  }
-};
-
-static RegisterCommand r1(make_ref<CmdRepl>());
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix/run.cc b/third_party/nix/src/nix/run.cc
deleted file mode 100644
index b3b54f300b..0000000000
--- a/third_party/nix/src/nix/run.cc
+++ /dev/null
@@ -1,283 +0,0 @@
-#include <queue>
-
-#include <absl/strings/str_split.h>
-#include <sys/mount.h>
-
-#include "libmain/common-args.hh"
-#include "libmain/shared.hh"
-#include "libstore/derivations.hh"
-#include "libstore/fs-accessor.hh"
-#include "libstore/local-store.hh"
-#include "libstore/store-api.hh"
-#include "libutil/affinity.hh"
-#include "libutil/finally.hh"
-#include "nix/command.hh"
-
-// note: exported in header file
-std::string chrootHelperName = "__run_in_chroot";
-
-namespace nix {
-struct CmdRun final : InstallablesCommand {
-  std::vector<std::string> command = {"bash"};
-  StringSet keep, unset;
-  bool ignoreEnvironment = false;
-
-  CmdRun() {
-    mkFlag()
-        .longName("command")
-        .shortName('c')
-        .description("command and arguments to be executed; defaults to 'bash'")
-        .labels({"command", "args"})
-        .arity(ArityAny)
-        .handler([&](const std::vector<std::string>& ss) {
-          if (ss.empty()) {
-            throw UsageError("--command requires at least one argument");
-          }
-          command = ss;
-        });
-
-    mkFlag()
-        .longName("ignore-environment")
-        .shortName('i')
-        .description(
-            "clear the entire environment (except those specified with --keep)")
-        .set(&ignoreEnvironment, true);
-
-    mkFlag()
-        .longName("keep")
-        .shortName('k')
-        .description("keep specified environment variable")
-        .arity(1)
-        .labels({"name"})
-        .handler([&](std::vector<std::string> ss) { keep.insert(ss.front()); });
-
-    mkFlag()
-        .longName("unset")
-        .shortName('u')
-        .description("unset specified environment variable")
-        .arity(1)
-        .labels({"name"})
-        .handler(
-            [&](std::vector<std::string> ss) { unset.insert(ss.front()); });
-  }
-
-  std::string name() override { return "run"; }
-
-  std::string description() override {
-    return "run a shell in which the specified packages are available";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To start a shell providing GNU Hello from NixOS 17.03:",
-                "nix run -f channel:nixos-17.03 hello"},
-        Example{"To start a shell providing youtube-dl from your 'nixpkgs' "
-                "channel:",
-                "nix run nixpkgs.youtube-dl"},
-        Example{"To run GNU Hello:",
-                "nix run nixpkgs.hello -c hello --greeting 'Hi everybody!'"},
-        Example{"To run GNU Hello in a chroot store:",
-                "nix run --store ~/my-nix nixpkgs.hello -c hello"},
-    };
-  }
-
-  void run(ref<Store> store) override {
-    auto outPaths = toStorePaths(store, Build, installables);
-
-    auto accessor = store->getFSAccessor();
-
-    if (ignoreEnvironment) {
-      if (!unset.empty()) {
-        throw UsageError(
-            "--unset does not make sense with --ignore-environment");
-      }
-
-      std::map<std::string, std::string> kept;
-      for (auto& var : keep) {
-        auto s = getenv(var.c_str());
-        if (s != nullptr) {
-          kept[var] = s;
-        }
-      }
-
-      clearEnv();
-
-      for (auto& var : kept) {
-        setenv(var.first.c_str(), var.second.c_str(), 1);
-      }
-
-    } else {
-      if (!keep.empty()) {
-        throw UsageError(
-            "--keep does not make sense without --ignore-environment");
-      }
-
-      for (auto& var : unset) {
-        unsetenv(var.c_str());
-      }
-    }
-
-    std::unordered_set<Path> done;
-    std::queue<Path> todo;
-    for (auto& path : outPaths) {
-      todo.push(path);
-    }
-
-    Strings unixPath = absl::StrSplit(getEnv("PATH").value_or(""),
-                                      absl::ByChar(':'), absl::SkipEmpty());
-
-    while (!todo.empty()) {
-      Path path = todo.front();
-      todo.pop();
-      if (!done.insert(path).second) {
-        continue;
-      }
-
-      { unixPath.push_front(path + "/bin"); }
-
-      auto propPath = path + "/nix-support/propagated-user-env-packages";
-      if (accessor->stat(propPath).type == FSAccessor::tRegular) {
-        for (auto p :
-             absl::StrSplit(readFile(propPath), absl::ByAnyChar(" \t\n\r"),
-                            absl::SkipEmpty())) {
-          todo.push(std::string(p));
-        }
-      }
-    }
-
-    setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1);
-
-    std::string cmd = *command.begin();
-    Strings args;
-    for (auto& arg : command) {
-      args.push_back(arg);
-    }
-
-    restoreSignals();
-
-    restoreAffinity();
-
-    /* If this is a diverted store (i.e. its "logical" location
-       (typically /nix/store) differs from its "physical" location
-       (e.g. /home/eelco/nix/store), then run the command in a
-       chroot. For non-root users, this requires running it in new
-       mount and user namespaces. Unfortunately,
-       unshare(CLONE_NEWUSER) doesn't work in a multithreaded
-       program (which "nix" is), so we exec() a single-threaded
-       helper program (chrootHelper() below) to do the work. */
-    auto store2 = store.dynamic_pointer_cast<LocalStore>();
-
-    if (store2 && store->storeDir != store2->realStoreDir) {
-      Strings helperArgs = {chrootHelperName, store->storeDir,
-                            store2->realStoreDir, cmd};
-      for (auto& arg : args) {
-        helperArgs.push_back(arg);
-      }
-
-      execv(readLink("/proc/self/exe").c_str(),
-            stringsToCharPtrs(helperArgs).data());
-
-      throw SysError("could not execute chroot helper");
-    }
-
-    execvp(cmd.c_str(), stringsToCharPtrs(args).data());
-
-    throw SysError("unable to exec '%s'", cmd);
-  }
-};
-
-static RegisterCommand r1(make_ref<CmdRun>());
-}  // namespace nix
-
-void chrootHelper(int argc, char** argv) {
-  int p = 1;
-  std::string storeDir = argv[p++];
-  std::string realStoreDir = argv[p++];
-  std::string cmd = argv[p++];
-  nix::Strings args;
-  while (p < argc) {
-    args.push_back(argv[p++]);
-  }
-
-#if __linux__
-  uid_t uid = getuid();
-  uid_t gid = getgid();
-
-  if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == -1) {
-    /* Try with just CLONE_NEWNS in case user namespaces are
-       specifically disabled. */
-    if (unshare(CLONE_NEWNS) == -1) {
-      throw nix::SysError("setting up a private mount namespace");
-    }
-  }
-
-  /* Bind-mount realStoreDir on /nix/store. If the latter mount
-     point doesn't already exists, we have to create a chroot
-     environment containing the mount point and bind mounts for the
-     children of /. Would be nice if we could use overlayfs here,
-     but that doesn't work in a user namespace yet (Ubuntu has a
-     patch for this:
-     https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1478578). */
-  if (!nix::pathExists(storeDir)) {
-    // FIXME: Use overlayfs?
-
-    nix::Path tmpDir = nix::createTempDir();
-
-    nix::createDirs(tmpDir + storeDir);
-
-    if (mount(realStoreDir.c_str(), (tmpDir + storeDir).c_str(), "", MS_BIND,
-              nullptr) == -1) {
-      throw nix::SysError("mounting '%s' on '%s'", realStoreDir, storeDir);
-    }
-
-    for (const auto& entry : nix::readDirectory("/")) {
-      auto src = "/" + entry.name;
-      auto st = nix::lstat(src);
-      if (!S_ISDIR(st.st_mode)) {
-        continue;
-      }
-      nix::Path dst = tmpDir + "/" + entry.name;
-      if (nix::pathExists(dst)) {
-        continue;
-      }
-      if (mkdir(dst.c_str(), 0700) == -1) {
-        throw nix::SysError("creating directory '%s'", dst);
-      }
-      if (mount(src.c_str(), dst.c_str(), "", MS_BIND | MS_REC, nullptr) ==
-          -1) {
-        throw nix::SysError("mounting '%s' on '%s'", src, dst);
-      }
-    }
-
-    char* cwd = getcwd(nullptr, 0);
-    if (cwd == nullptr) {
-      throw nix::SysError("getting current directory");
-    }
-    ::Finally freeCwd([&]() { free(cwd); });
-
-    if (chroot(tmpDir.c_str()) == -1) {
-      throw nix::SysError(nix::format("chrooting into '%s'") % tmpDir);
-    }
-
-    if (chdir(cwd) == -1) {
-      throw nix::SysError(nix::format("chdir to '%s' in chroot") % cwd);
-    }
-  } else if (mount(realStoreDir.c_str(), storeDir.c_str(), "", MS_BIND,
-                   nullptr) == -1) {
-    throw nix::SysError("mounting '%s' on '%s'", realStoreDir, storeDir);
-  }
-
-  nix::writeFile("/proc/self/setgroups", "deny");
-  nix::writeFile("/proc/self/uid_map", nix::fmt("%d %d %d", uid, uid, 1));
-  nix::writeFile("/proc/self/gid_map", nix::fmt("%d %d %d", gid, gid, 1));
-
-  execvp(cmd.c_str(), nix::stringsToCharPtrs(args).data());
-
-  throw nix::SysError("unable to exec '%s'", cmd);
-
-#else
-  throw nix::Error(
-      "mounting the Nix store on '%s' is not supported on this platform",
-      storeDir);
-#endif
-}
diff --git a/third_party/nix/src/nix/search.cc b/third_party/nix/src/nix/search.cc
deleted file mode 100644
index 5a6bae6a11..0000000000
--- a/third_party/nix/src/nix/search.cc
+++ /dev/null
@@ -1,276 +0,0 @@
-#include <fstream>
-#include <regex>
-
-#include <glog/logging.h>
-
-#include "libexpr/eval-inline.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/get-drvs.hh"
-#include "libexpr/json-to-value.hh"
-#include "libexpr/names.hh"
-#include "libmain/common-args.hh"
-#include "libmain/shared.hh"
-#include "libstore/globals.hh"
-#include "libutil/json.hh"
-#include "nix/command.hh"
-
-namespace {
-std::string wrap(const std::string& prefix, const std::string& s) {
-  return prefix + s + ANSI_NORMAL;
-}
-
-std::string hilite(const std::string& s, const std::smatch& m,
-                   const std::string& postfix) {
-  return m.empty() ? s
-                   : std::string(m.prefix()) + ANSI_RED + std::string(m.str()) +
-                         postfix + std::string(m.suffix());
-}
-}  // namespace
-
-namespace nix {
-struct CmdSearch final : SourceExprCommand, MixJSON {
-  std::vector<std::string> res;
-
-  bool writeCache = true;
-  bool useCache = true;
-
-  CmdSearch() {
-    expectArgs("regex", &res);
-
-    mkFlag()
-        .longName("update-cache")
-        .shortName('u')
-        .description("update the package search cache")
-        .handler([&]() {
-          writeCache = true;
-          useCache = false;
-        });
-
-    mkFlag()
-        .longName("no-cache")
-        .description("do not use or update the package search cache")
-        .handler([&]() {
-          writeCache = false;
-          useCache = false;
-        });
-  }
-
-  std::string name() override { return "search"; }
-
-  std::string description() override { return "query available packages"; }
-
-  Examples examples() override {
-    return {Example{"To show all available packages:", "nix search"},
-            Example{"To show any packages containing 'blender' in its name or "
-                    "description:",
-                    "nix search blender"},
-            Example{"To search for Firefox or Chromium:",
-                    "nix search 'firefox|chromium'"},
-            Example{"To search for git and frontend or gui:",
-                    "nix search git 'frontend|gui'"}};
-  }
-
-  void run(ref<Store> store) override {
-    settings.readOnlyMode = true;
-
-    // Empty search string should match all packages
-    // Use "^" here instead of ".*" due to differences in resulting highlighting
-    // (see #1893 -- libc++ claims empty search string is not in POSIX grammar)
-    if (res.empty()) {
-      res.emplace_back("^");
-    }
-
-    std::vector<std::regex> regexes;
-    regexes.reserve(res.size());
-
-    for (auto& re : res) {
-      regexes.emplace_back(re, std::regex::extended | std::regex::icase);
-    }
-
-    auto state = getEvalState();
-
-    auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr;
-
-    auto sToplevel = state->symbols.Create("_toplevel");
-    auto sRecurse = state->symbols.Create("recurseForDerivations");
-
-    bool fromCache = false;
-
-    std::map<std::string, std::string> results;
-
-    std::function<void(Value*, std::string, bool, JSONObject*)> doExpr;
-
-    doExpr = [&](Value* v, const std::string& attrPath, bool toplevel,
-                 JSONObject* cache) {
-      DLOG(INFO) << "at attribute '" << attrPath << "'";
-
-      try {
-        uint found = 0;
-
-        state->forceValue(*v);
-
-        if (v->type == tLambda && toplevel) {
-          Value* v2 = state->allocValue();
-          auto dummyArgs = Bindings::New();
-          state->autoCallFunction(dummyArgs.get(), *v, *v2);
-          v = v2;
-          state->forceValue(*v);
-        }
-
-        if (state->isDerivation(*v)) {
-          DrvInfo drv(*state, attrPath, v->attrs);
-          std::string description;
-          std::smatch attrPathMatch;
-          std::smatch descriptionMatch;
-          std::smatch nameMatch;
-          std::string name;
-
-          DrvName parsed(drv.queryName());
-
-          for (auto& regex : regexes) {
-            std::regex_search(attrPath, attrPathMatch, regex);
-
-            name = parsed.name;
-            std::regex_search(name, nameMatch, regex);
-
-            description = drv.queryMetaString("description");
-            std::replace(description.begin(), description.end(), '\n', ' ');
-            std::regex_search(description, descriptionMatch, regex);
-
-            if (!attrPathMatch.empty() || !nameMatch.empty() ||
-                !descriptionMatch.empty()) {
-              found++;
-            }
-          }
-
-          if (found == res.size()) {
-            if (json) {
-              auto jsonElem = jsonOut->object(attrPath);
-
-              jsonElem.attr("pkgName", parsed.name);
-              jsonElem.attr("version", parsed.version);
-              jsonElem.attr("description", description);
-
-            } else {
-              auto name = hilite(parsed.name, nameMatch, "\e[0;2m") +
-                          std::string(parsed.fullName, parsed.name.length());
-              results[attrPath] = fmt(
-                  "* %s (%s)\n  %s\n",
-                  wrap("\e[0;1m", hilite(attrPath, attrPathMatch, "\e[0;1m")),
-                  wrap("\e[0;2m", hilite(name, nameMatch, "\e[0;2m")),
-                  hilite(description, descriptionMatch, ANSI_NORMAL));
-            }
-          }
-
-          if (cache != nullptr) {
-            cache->attr("type", "derivation");
-            cache->attr("name", drv.queryName());
-            cache->attr("system", drv.querySystem());
-            if (!description.empty()) {
-              auto meta(cache->object("meta"));
-              meta.attr("description", description);
-            }
-          }
-        }
-
-        else if (v->type == tAttrs) {
-          if (!toplevel) {
-            auto attrs = v->attrs;
-            Bindings::iterator j = attrs->find(sRecurse);
-            if (j == attrs->end() ||
-                !state->forceBool(*j->second.value, *j->second.pos)) {
-              DLOG(INFO) << "skip attribute '" << attrPath << "'";
-              return;
-            }
-          }
-
-          bool toplevel2 = false;
-          if (!fromCache) {
-            Bindings::iterator j = v->attrs->find(sToplevel);
-            toplevel2 = j != v->attrs->end() &&
-                        state->forceBool(*j->second.value, *j->second.pos);
-          }
-
-          for (auto& i : *v->attrs) {
-            auto cache2 =
-                cache != nullptr
-                    ? std::make_unique<JSONObject>(cache->object(i.second.name))
-                    : nullptr;
-            doExpr(i.second.value,
-                   attrPath.empty()
-                       ? std::string(i.second.name)
-                       : attrPath + "." + std::string(i.second.name),
-                   toplevel2 || fromCache, cache2 ? cache2.get() : nullptr);
-          }
-        }
-
-      } catch (AssertionError& e) {
-      } catch (Error& e) {
-        if (!toplevel) {
-          e.addPrefix(fmt("While evaluating the attribute '%s':\n", attrPath));
-          throw;
-        }
-      }
-    };
-
-    Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json";
-
-    if (useCache && pathExists(jsonCacheFileName)) {
-      LOG(WARNING) << "using cached results; pass '-u' to update the cache";
-
-      Value vRoot;
-      parseJSON(*state, readFile(jsonCacheFileName), vRoot);
-
-      fromCache = true;
-
-      doExpr(&vRoot, "", true, nullptr);
-    }
-
-    else {
-      createDirs(dirOf(jsonCacheFileName));
-
-      Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid());
-
-      std::ofstream jsonCacheFile;
-
-      try {
-        // iostream considered harmful
-        jsonCacheFile.exceptions(std::ofstream::failbit);
-        jsonCacheFile.open(tmpFile);
-
-        auto cache = writeCache
-                         ? std::make_unique<JSONObject>(jsonCacheFile, false)
-                         : nullptr;
-
-        doExpr(getSourceExpr(*state), "", true, cache.get());
-
-      } catch (std::exception&) {
-        /* Fun fact: catching std::ios::failure does not work
-           due to C++11 ABI shenanigans.
-           https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145 */
-        if (!jsonCacheFile) {
-          throw Error("error writing to %s", tmpFile);
-        }
-        throw;
-      }
-
-      if (writeCache &&
-          rename(tmpFile.c_str(), jsonCacheFileName.c_str()) == -1) {
-        throw SysError("cannot rename '%s' to '%s'", tmpFile,
-                       jsonCacheFileName);
-      }
-    }
-
-    if (results.empty()) {
-      throw Error("no results for the given search term(s)!");
-    }
-
-    RunPager pager;
-    for (const auto& el : results) {
-      std::cout << el.second << "\n";
-    }
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdSearch>());
diff --git a/third_party/nix/src/nix/show-config.cc b/third_party/nix/src/nix/show-config.cc
deleted file mode 100644
index fd92e481e8..0000000000
--- a/third_party/nix/src/nix/show-config.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "libmain/common-args.hh"
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "libutil/json.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdShowConfig final : Command, MixJSON {
-  CmdShowConfig() = default;
-
-  std::string name() override { return "show-config"; }
-
-  std::string description() override { return "show the Nix configuration"; }
-
-  void run() override {
-    if (json) {
-      // FIXME: use appropriate JSON types (bool, ints, etc).
-      JSONObject jsonObj(std::cout);
-      globalConfig.toJSON(jsonObj);
-    } else {
-      std::map<std::string, Config::SettingInfo> settings;
-      globalConfig.getSettings(settings);
-      for (auto& s : settings) {
-        std::cout << s.first + " = " + s.second.value + "\n";
-      }
-    }
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdShowConfig>());
diff --git a/third_party/nix/src/nix/show-derivation.cc b/third_party/nix/src/nix/show-derivation.cc
deleted file mode 100644
index efe554710f..0000000000
--- a/third_party/nix/src/nix/show-derivation.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// FIXME: integrate this with nix path-info?
-
-#include "libmain/common-args.hh"
-#include "libstore/derivations.hh"
-#include "libstore/store-api.hh"
-#include "libutil/archive.hh"
-#include "libutil/json.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdShowDerivation final : InstallablesCommand {
-  bool recursive = false;
-
-  CmdShowDerivation() {
-    mkFlag()
-        .longName("recursive")
-        .shortName('r')
-        .description("include the dependencies of the specified derivations")
-        .set(&recursive, true);
-  }
-
-  std::string name() override { return "show-derivation"; }
-
-  std::string description() override {
-    return "show the contents of a store derivation";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To show the store derivation that results from evaluating the "
-                "Hello package:",
-                "nix show-derivation nixpkgs.hello"},
-        Example{"To show the full derivation graph (if available) that "
-                "produced your NixOS system:",
-                "nix show-derivation -r /run/current-system"},
-    };
-  }
-
-  void run(ref<Store> store) override {
-    auto drvPaths = toDerivations(store, installables, true);
-
-    if (recursive) {
-      PathSet closure;
-      store->computeFSClosure(drvPaths, closure);
-      drvPaths = closure;
-    }
-
-    {
-      JSONObject jsonRoot(std::cout, true);
-
-      for (auto& drvPath : drvPaths) {
-        if (!isDerivation(drvPath)) {
-          continue;
-        }
-
-        auto drvObj(jsonRoot.object(drvPath));
-
-        auto drv = readDerivation(drvPath);
-
-        {
-          auto outputsObj(drvObj.object("outputs"));
-          for (auto& output : drv.outputs) {
-            auto outputObj(outputsObj.object(output.first));
-            outputObj.attr("path", output.second.path);
-            if (!output.second.hash.empty()) {
-              outputObj.attr("hashAlgo", output.second.hashAlgo);
-              outputObj.attr("hash", output.second.hash);
-            }
-          }
-        }
-
-        {
-          auto inputsList(drvObj.list("inputSrcs"));
-          for (auto& input : drv.inputSrcs) {
-            inputsList.elem(input);
-          }
-        }
-
-        {
-          auto inputDrvsObj(drvObj.object("inputDrvs"));
-          for (auto& input : drv.inputDrvs) {
-            auto inputList(inputDrvsObj.list(input.first));
-            for (auto& outputId : input.second) {
-              inputList.elem(outputId);
-            }
-          }
-        }
-
-        drvObj.attr("platform", drv.platform);
-        drvObj.attr("builder", drv.builder);
-
-        {
-          auto argsList(drvObj.list("args"));
-          for (auto& arg : drv.args) {
-            argsList.elem(arg);
-          }
-        }
-
-        {
-          auto envObj(drvObj.object("env"));
-          for (auto& var : drv.env) {
-            envObj.attr(var.first, var.second);
-          }
-        }
-      }
-    }
-
-    std::cout << "\n";
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdShowDerivation>());
diff --git a/third_party/nix/src/nix/sigs.cc b/third_party/nix/src/nix/sigs.cc
deleted file mode 100644
index cc42613d07..0000000000
--- a/third_party/nix/src/nix/sigs.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-#include <atomic>
-
-#include <glog/logging.h>
-
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "libutil/thread-pool.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdCopySigs final : StorePathsCommand {
-  Strings substituterUris;
-
-  CmdCopySigs() {
-    mkFlag()
-        .longName("substituter")
-        .shortName('s')
-        .labels({"store-uri"})
-        .description("use signatures from specified store")
-        .arity(1)
-        .handler([&](std::vector<std::string> ss) {
-          substituterUris.push_back(ss[0]);
-        });
-  }
-
-  std::string name() override { return "copy-sigs"; }
-
-  std::string description() override {
-    return "copy path signatures from substituters (like binary caches)";
-  }
-
-  void run(ref<Store> store, Paths storePaths) override {
-    if (substituterUris.empty()) {
-      throw UsageError("you must specify at least one substituter using '-s'");
-    }
-
-    // FIXME: factor out commonality with MixVerify.
-    std::vector<ref<Store>> substituters;
-    for (auto& s : substituterUris) {
-      substituters.push_back(openStore(s));
-    }
-
-    ThreadPool pool;
-
-    std::string doneLabel = "done";
-    std::atomic<size_t> added{0};
-
-    // logger->setExpected(doneLabel, storePaths.size());
-
-    auto doPath = [&](const Path& storePath) {
-      // Activity act(*logger, lvlInfo, format("getting signatures for '%s'") %
-      // storePath);
-
-      checkInterrupt();
-
-      auto info = store->queryPathInfo(storePath);
-
-      StringSet newSigs;
-
-      for (auto& store2 : substituters) {
-        try {
-          auto info2 = store2->queryPathInfo(storePath);
-
-          /* Don't import signatures that don't match this
-             binary. */
-          if (info->narHash != info2->narHash ||
-              info->narSize != info2->narSize ||
-              info->references != info2->references) {
-            continue;
-          }
-
-          for (auto& sig : info2->sigs) {
-            if (info->sigs.count(sig) == 0u) {
-              newSigs.insert(sig);
-            }
-          }
-        } catch (InvalidPath&) {
-        }
-      }
-
-      if (!newSigs.empty()) {
-        store->addSignatures(storePath, newSigs);
-        added += newSigs.size();
-      }
-
-      // logger->incProgress(doneLabel);
-    };
-
-    for (auto& storePath : storePaths) {
-      pool.enqueue(std::bind(doPath, storePath));
-    }
-
-    pool.process();
-
-    LOG(INFO) << "imported " << added << " signatures";
-  }
-};
-
-static nix::RegisterCommand r1(make_ref<CmdCopySigs>());
-
-struct CmdSignPaths final : StorePathsCommand {
-  Path secretKeyFile;
-
-  CmdSignPaths() {
-    mkFlag()
-        .shortName('k')
-        .longName("key-file")
-        .label("file")
-        .description("file containing the secret signing key")
-        .dest(&secretKeyFile);
-  }
-
-  std::string name() override { return "sign-paths"; }
-
-  std::string description() override { return "sign the specified paths"; }
-
-  void run(ref<Store> store, Paths storePaths) override {
-    if (secretKeyFile.empty()) {
-      throw UsageError("you must specify a secret key file using '-k'");
-    }
-
-    SecretKey secretKey(readFile(secretKeyFile));
-
-    size_t added{0};
-
-    for (auto& storePath : storePaths) {
-      auto info = store->queryPathInfo(storePath);
-
-      auto info2(*info);
-      info2.sigs.clear();
-      info2.sign(secretKey);
-      assert(!info2.sigs.empty());
-
-      if (info->sigs.count(*info2.sigs.begin()) == 0u) {
-        store->addSignatures(storePath, info2.sigs);
-        added++;
-      }
-    }
-
-    LOG(INFO) << "added " << added << " signatures";
-  }
-};
-
-static RegisterCommand r3(make_ref<CmdSignPaths>());
-
-}  // namespace nix
diff --git a/third_party/nix/src/nix/upgrade-nix.cc b/third_party/nix/src/nix/upgrade-nix.cc
deleted file mode 100644
index c7f654d648..0000000000
--- a/third_party/nix/src/nix/upgrade-nix.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-#include <absl/strings/match.h>
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_split.h>
-#include <glog/logging.h>
-
-#include "libexpr/attr-path.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/names.hh"
-#include "libmain/common-args.hh"
-#include "libstore/download.hh"
-#include "libstore/store-api.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdUpgradeNix final : MixDryRun, StoreCommand {
-  Path profileDir;
-  std::string storePathsUrl =
-      "https://github.com/NixOS/nixpkgs/raw/master/nixos/modules/installer/"
-      "tools/nix-fallback-paths.nix";
-
-  CmdUpgradeNix() {
-    mkFlag()
-        .longName("profile")
-        .shortName('p')
-        .labels({"profile-dir"})
-        .description("the Nix profile to upgrade")
-        .dest(&profileDir);
-
-    mkFlag()
-        .longName("nix-store-paths-url")
-        .labels({"url"})
-        .description(
-            "URL of the file that contains the store paths of the latest Nix "
-            "release")
-        .dest(&storePathsUrl);
-  }
-
-  std::string name() override { return "upgrade-nix"; }
-
-  std::string description() override {
-    return "upgrade Nix to the latest stable version";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To upgrade Nix to the latest stable version:",
-                "nix upgrade-nix"},
-        Example{
-            "To upgrade Nix in a specific profile:",
-            "nix upgrade-nix -p /nix/var/nix/profiles/per-user/alice/profile"},
-    };
-  }
-
-  void run(ref<Store> store) override {
-    evalSettings.pureEval = true;
-
-    if (profileDir.empty()) {
-      profileDir = getProfileDir(store);
-    }
-
-    LOG(INFO) << "upgrading Nix in profile '" << profileDir << "'";
-
-    Path storePath;
-    {
-      LOG(INFO) << "querying latest Nix version";
-      storePath = getLatestNix(store);
-    }
-
-    auto version = DrvName(storePathToName(storePath)).version;
-
-    if (dryRun) {
-      LOG(ERROR) << "would upgrade to version " << version;
-      return;
-    }
-
-    {
-      LOG(INFO) << "downloading '" << storePath << "'...";
-      store->ensurePath(storePath);
-    }
-
-    {
-      LOG(INFO) << "verifying that '" << storePath << "' works...";
-      auto program = storePath + "/bin/nix-env";
-      auto s = runProgram(program, false, {"--version"});
-      if (s.find("Nix") == std::string::npos) {
-        throw Error("could not verify that '%s' works", program);
-      }
-    }
-
-    {
-      LOG(INFO) << "installing '" << storePath << "' into profile '"
-                << profileDir << "'...";
-      runProgram(settings.nixBinDir + "/nix-env", false,
-                 {"--profile", profileDir, "-i", storePath, "--no-sandbox"});
-    }
-
-    LOG(INFO) << ANSI_GREEN << "upgrade to version " << version << " done"
-              << ANSI_NORMAL;
-  }
-
-  /* Return the profile in which Nix is installed. */
-  static Path getProfileDir(const ref<Store>& store) {
-    Path where;
-
-    for (auto& dir : absl::StrSplit(getEnv("PATH").value_or(""),
-                                    absl::ByChar(':'), absl::SkipEmpty())) {
-      if (pathExists(absl::StrCat(dir, "/nix-env"))) {
-        where = dir;
-        break;
-      }
-    }
-
-    if (where.empty()) {
-      throw Error(
-          "couldn't figure out how Nix is installed, so I can't upgrade it");
-    }
-
-    LOG(INFO) << "found Nix in '" << where << "'";
-
-    if (absl::StartsWith(where, "/run/current-system")) {
-      throw Error("Nix on NixOS must be upgraded via 'nixos-rebuild'");
-    }
-
-    Path profileDir = dirOf(where);
-
-    // Resolve profile to /nix/var/nix/profiles/<name> link.
-    while (canonPath(profileDir).find("/profiles/") == std::string::npos &&
-           isLink(profileDir)) {
-      profileDir = readLink(profileDir);
-    }
-
-    LOG(INFO) << "found profile '" << profileDir << "'";
-
-    Path userEnv = canonPath(profileDir, true);
-
-    if (baseNameOf(where) != "bin" ||
-        !absl::EndsWith(userEnv, "user-environment")) {
-      throw Error("directory '%s' does not appear to be part of a Nix profile",
-                  where);
-    }
-
-    if (!store->isValidPath(userEnv)) {
-      throw Error("directory '%s' is not in the Nix store", userEnv);
-    }
-
-    return profileDir;
-  }
-
-  /* Return the store path of the latest stable Nix. */
-  Path getLatestNix(const ref<Store>& store) {
-    // FIXME: use nixos.org?
-    auto req = DownloadRequest(storePathsUrl);
-    auto res = getDownloader()->download(req);
-
-    auto state = std::make_unique<EvalState>(Strings(), store);
-    auto v = state->allocValue();
-    state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v);
-    std::unique_ptr<Bindings> bindings(Bindings::New());
-    auto v2 =
-        findAlongAttrPath(*state, settings.thisSystem, bindings.get(), *v);
-
-    return state->forceString(*v2);
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdUpgradeNix>());
diff --git a/third_party/nix/src/nix/verify.cc b/third_party/nix/src/nix/verify.cc
deleted file mode 100644
index 7de46f2a9c..0000000000
--- a/third_party/nix/src/nix/verify.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-#include <atomic>
-
-#include <glog/logging.h>
-
-#include "libmain/shared.hh"
-#include "libstore/store-api.hh"
-#include "libutil/sync.hh"
-#include "libutil/thread-pool.hh"
-#include "nix/command.hh"
-
-namespace nix {
-struct CmdVerify final : StorePathsCommand {
-  bool noContents = false;
-  bool noTrust = false;
-  Strings substituterUris;
-  size_t sigsNeeded = 0;
-
-  CmdVerify() {
-    mkFlag(0, "no-contents", "do not verify the contents of each store path",
-           &noContents);
-    mkFlag(0, "no-trust", "do not verify whether each store path is trusted",
-           &noTrust);
-    mkFlag()
-        .longName("substituter")
-        .shortName('s')
-        .labels({"store-uri"})
-        .description("use signatures from specified store")
-        .arity(1)
-        .handler([&](std::vector<std::string> ss) {
-          substituterUris.push_back(ss[0]);
-        });
-    mkIntFlag('n', "sigs-needed",
-              "require that each path has at least N valid signatures",
-              &sigsNeeded);
-  }
-
-  std::string name() override { return "verify"; }
-
-  std::string description() override {
-    return "verify the integrity of store paths";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To verify the entire Nix store:", "nix verify --all"},
-        Example{"To check whether each path in the closure of Firefox has at "
-                "least 2 signatures:",
-                "nix verify -r -n2 --no-contents $(type -p firefox)"},
-    };
-  }
-
-  void run(ref<Store> store, Paths storePaths) override {
-    std::vector<ref<Store>> substituters;
-    for (auto& s : substituterUris) {
-      substituters.push_back(openStore(s));
-    }
-
-    auto publicKeys = getDefaultPublicKeys();
-
-    std::atomic<size_t> done{0};
-    std::atomic<size_t> untrusted{0};
-    std::atomic<size_t> corrupted{0};
-    std::atomic<size_t> failed{0};
-    std::atomic<size_t> active{0};
-
-    ThreadPool pool;
-
-    auto doPath = [&](const Path& storePath) {
-      try {
-        checkInterrupt();
-
-        LOG(INFO) << "checking '" << storePath << "'";
-
-        MaintainCount<std::atomic<size_t>> mcActive(active);
-
-        auto info = store->queryPathInfo(storePath);
-
-        if (!noContents) {
-          HashSink sink(info->narHash.type);
-          store->narFromPath(info->path, sink);
-
-          auto hash = sink.finish();
-
-          if (hash.first != info->narHash) {
-            corrupted++;
-            LOG(WARNING) << "path '" << info->path
-                         << "' was modified! expected hash '"
-                         << info->narHash.to_string() << "', got '"
-                         << hash.first.to_string() << "'";
-          }
-        }
-
-        if (!noTrust) {
-          bool good = false;
-
-          if (info->ultimate && (sigsNeeded == 0u)) {
-            good = true;
-
-          } else {
-            StringSet sigsSeen;
-            size_t actualSigsNeeded =
-                std::max(sigsNeeded, static_cast<size_t>(1));
-            size_t validSigs = 0;
-
-            auto doSigs = [&](const StringSet& sigs) {
-              for (const auto& sig : sigs) {
-                if (sigsSeen.count(sig) != 0u) {
-                  continue;
-                }
-                sigsSeen.insert(sig);
-                if (validSigs < ValidPathInfo::maxSigs &&
-                    info->checkSignature(publicKeys, sig)) {
-                  validSigs++;
-                }
-              }
-            };
-
-            if (info->isContentAddressed(*store)) {
-              validSigs = ValidPathInfo::maxSigs;
-            }
-
-            doSigs(info->sigs);
-
-            for (auto& store2 : substituters) {
-              if (validSigs >= actualSigsNeeded) {
-                break;
-              }
-              try {
-                auto info2 = store2->queryPathInfo(info->path);
-                if (info2->isContentAddressed(*store)) {
-                  validSigs = ValidPathInfo::maxSigs;
-                }
-                doSigs(info2->sigs);
-              } catch (InvalidPath&) {
-              } catch (Error& e) {
-                LOG(ERROR) << e.what();
-              }
-            }
-
-            if (validSigs >= actualSigsNeeded) {
-              good = true;
-            }
-          }
-
-          if (!good) {
-            untrusted++;
-            LOG(WARNING) << "path '" << info->path << "' is untrusted";
-          }
-        }
-
-        done++;
-
-      } catch (Error& e) {
-        LOG(ERROR) << e.what();
-        failed++;
-      }
-    };
-
-    for (auto& storePath : storePaths) {
-      pool.enqueue(std::bind(doPath, storePath));
-    }
-
-    pool.process();
-
-    throw Exit((corrupted != 0u ? 1 : 0) | (untrusted != 0u ? 2 : 0) |
-               (failed != 0u ? 4 : 0));
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdVerify>());
diff --git a/third_party/nix/src/nix/why-depends.cc b/third_party/nix/src/nix/why-depends.cc
deleted file mode 100644
index 954d619ef3..0000000000
--- a/third_party/nix/src/nix/why-depends.cc
+++ /dev/null
@@ -1,269 +0,0 @@
-#include <queue>
-
-#include <glog/logging.h>
-
-#include "libmain/shared.hh"
-#include "libstore/fs-accessor.hh"
-#include "libstore/store-api.hh"
-#include "nix/command.hh"
-
-namespace {
-static std::string hilite(const std::string& s, size_t pos, size_t len,
-                          const std::string& colour = ANSI_RED) {
-  return std::string(s, 0, pos) + colour + std::string(s, pos, len) +
-         ANSI_NORMAL + std::string(s, pos + len);
-}
-
-static std::string filterPrintable(const std::string& s) {
-  std::string res;
-  for (char c : s) {
-    res += isprint(c) != 0 ? c : '.';
-  }
-  return res;
-}
-}  // namespace
-
-namespace nix {
-struct CmdWhyDepends final : SourceExprCommand {
-  std::string _package, _dependency;
-  bool all = false;
-
-  CmdWhyDepends() {
-    expectArg("package", &_package);
-    expectArg("dependency", &_dependency);
-
-    mkFlag()
-        .longName("all")
-        .shortName('a')
-        .description(
-            "show all edges in the dependency graph leading from 'package' to "
-            "'dependency', rather than just a shortest path")
-        .set(&all, true);
-  }
-
-  std::string name() override { return "why-depends"; }
-
-  std::string description() override {
-    return "show why a package has another package in its closure";
-  }
-
-  Examples examples() override {
-    return {
-        Example{"To show one path through the dependency graph leading from "
-                "Hello to Glibc:",
-                "nix why-depends nixpkgs.hello nixpkgs.glibc"},
-        Example{
-            "To show all files and paths in the dependency graph leading from "
-            "Thunderbird to libX11:",
-            "nix why-depends --all nixpkgs.thunderbird nixpkgs.xorg.libX11"},
-        Example{"To show why Glibc depends on itself:",
-                "nix why-depends nixpkgs.glibc nixpkgs.glibc"},
-    };
-  }
-
-  void run(ref<Store> store) override {
-    auto package = parseInstallable(*this, store, _package, false);
-    auto packagePath = toStorePath(store, Build, package);
-    auto dependency = parseInstallable(*this, store, _dependency, false);
-    auto dependencyPath = toStorePath(store, NoBuild, dependency);
-    auto dependencyPathHash = storePathToHash(dependencyPath);
-
-    PathSet closure;
-    store->computeFSClosure({packagePath}, closure, false, false);
-
-    if (closure.count(dependencyPath) == 0u) {
-      LOG(WARNING) << "'" << package->what() << "' does not depend on '"
-                   << dependency->what() << "'";
-      return;
-    }
-
-    auto accessor = store->getFSAccessor();
-
-    auto const inf = std::numeric_limits<size_t>::max();
-
-    struct Node {
-      Path path;
-      PathSet refs;
-      PathSet rrefs;
-      size_t dist = inf;
-      Node* prev = nullptr;
-      bool queued = false;
-      bool visited = false;
-    };
-
-    std::map<Path, Node> graph;
-
-    for (auto& path : closure) {
-      graph.emplace(path, Node{path, store->queryPathInfo(path)->references});
-    }
-
-    // Transpose the graph.
-    for (auto& node : graph) {
-      for (auto& ref : node.second.refs) {
-        graph[ref].rrefs.insert(node.first);
-      }
-    }
-
-    /* Run Dijkstra's shortest path algorithm to get the distance
-       of every path in the closure to 'dependency'. */
-    graph[dependencyPath].dist = 0;
-
-    std::priority_queue<Node*> queue;
-
-    queue.push(&graph.at(dependencyPath));
-
-    while (!queue.empty()) {
-      auto& node = *queue.top();
-      queue.pop();
-
-      for (auto& rref : node.rrefs) {
-        auto& node2 = graph.at(rref);
-        auto dist = node.dist + 1;
-        if (dist < node2.dist) {
-          node2.dist = dist;
-          node2.prev = &node;
-          if (!node2.queued) {
-            node2.queued = true;
-            queue.push(&node2);
-          }
-        }
-      }
-    }
-
-    /* Print the subgraph of nodes that have 'dependency' in their
-       closure (i.e., that have a non-infinite distance to
-       'dependency'). Print every edge on a path between `package`
-       and `dependency`. */
-    std::function<void(Node&, const std::string&, const std::string&)>
-        printNode;
-
-    const std::string treeConn = "╠═══";
-    const std::string treeLast = "β•šβ•β•β•";
-    const std::string treeLine = "β•‘   ";
-    const std::string treeNull = "    ";
-
-    struct BailOut {};
-
-    printNode = [&](Node& node, const std::string& firstPad,
-                    const std::string& tailPad) {
-      assert(node.dist != inf);
-      std::cout << fmt("%s%s%s%s" ANSI_NORMAL "\n", firstPad,
-                       node.visited ? "\e[38;5;244m" : "",
-                       !firstPad.empty() ? "=> " : "", node.path);
-
-      if (node.path == dependencyPath && !all &&
-          packagePath != dependencyPath) {
-        throw BailOut();
-      }
-
-      if (node.visited) {
-        return;
-      }
-      node.visited = true;
-
-      /* Sort the references by distance to `dependency` to
-         ensure that the shortest path is printed first. */
-      std::multimap<size_t, Node*> refs;
-      std::set<std::string> hashes;
-
-      for (auto& ref : node.refs) {
-        if (ref == node.path && packagePath != dependencyPath) {
-          continue;
-        }
-        auto& node2 = graph.at(ref);
-        if (node2.dist == inf) {
-          continue;
-        }
-        refs.emplace(node2.dist, &node2);
-        hashes.insert(storePathToHash(node2.path));
-      }
-
-      /* For each reference, find the files and symlinks that
-         contain the reference. */
-      std::map<std::string, Strings> hits;
-
-      std::function<void(const Path&)> visitPath;
-
-      visitPath = [&](const Path& p) {
-        auto st = accessor->stat(p);
-
-        auto p2 = p == node.path ? "/" : std::string(p, node.path.size() + 1);
-
-        auto getColour = [&](const std::string& hash) {
-          return hash == dependencyPathHash ? ANSI_GREEN : ANSI_BLUE;
-        };
-
-        if (st.type == FSAccessor::Type::tDirectory) {
-          auto names = accessor->readDirectory(p);
-          for (auto& name : names) {
-            visitPath(p + "/" + name);
-          }
-        }
-
-        else if (st.type == FSAccessor::Type::tRegular) {
-          auto contents = accessor->readFile(p);
-
-          for (auto& hash : hashes) {
-            auto pos = contents.find(hash);
-            if (pos != std::string::npos) {
-              size_t margin = 32;
-              auto pos2 = pos >= margin ? pos - margin : 0;
-              hits[hash].emplace_back(fmt(
-                  "%s: …%s…\n", p2,
-                  hilite(
-                      filterPrintable(std::string(
-                          contents, pos2, pos - pos2 + hash.size() + margin)),
-                      pos - pos2, storePathHashLen, getColour(hash))));
-            }
-          }
-        }
-
-        else if (st.type == FSAccessor::Type::tSymlink) {
-          auto target = accessor->readLink(p);
-
-          for (auto& hash : hashes) {
-            auto pos = target.find(hash);
-            if (pos != std::string::npos) {
-              hits[hash].emplace_back(
-                  fmt("%s -> %s\n", p2,
-                      hilite(target, pos, storePathHashLen, getColour(hash))));
-            }
-          }
-        }
-      };
-
-      // FIXME: should use scanForReferences().
-
-      visitPath(node.path);
-
-      RunPager pager;
-      for (auto& ref : refs) {
-        auto hash = storePathToHash(ref.second->path);
-
-        bool last = all ? ref == *refs.rbegin() : true;
-
-        for (auto& hit : hits[hash]) {
-          bool first = hit == *hits[hash].begin();
-          std::cout << tailPad
-                    << (first ? (last ? treeLast : treeConn)
-                              : (last ? treeNull : treeLine))
-                    << hit;
-          if (!all) {
-            break;
-          }
-        }
-
-        printNode(*ref.second, tailPad + (last ? treeNull : treeLine),
-                  tailPad + (last ? treeNull : treeLine));
-      }
-    };
-
-    try {
-      printNode(graph.at(packagePath), "", "");
-    } catch (BailOut&) {
-    }
-  }
-};
-}  // namespace nix
-
-static nix::RegisterCommand r1(nix::make_ref<nix::CmdWhyDepends>());
diff --git a/third_party/nix/src/nlohmann/json.hpp b/third_party/nix/src/nlohmann/json.hpp
deleted file mode 100644
index c9af0bed36..0000000000
--- a/third_party/nix/src/nlohmann/json.hpp
+++ /dev/null
@@ -1,20406 +0,0 @@
-/*
-    __ _____ _____ _____
- __|  |   __|     |   | |  JSON for Modern C++
-|  |  |__   |  |  | | | |  version 3.5.0
-|_____|_____|_____|_|___|  https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-SPDX-License-Identifier: MIT
-Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
-
-Permission is hereby  granted, free of charge, to any  person obtaining a copy
-of this software and associated  documentation files (the "Software"), to deal
-in the Software  without restriction, including without  limitation the rights
-to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
-copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
-IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
-FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
-AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
-LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-#ifndef NLOHMANN_JSON_HPP
-#define NLOHMANN_JSON_HPP
-
-#define NLOHMANN_JSON_VERSION_MAJOR 3
-#define NLOHMANN_JSON_VERSION_MINOR 5
-#define NLOHMANN_JSON_VERSION_PATCH 0
-
-#include <algorithm> // all_of, find, for_each
-#include <cassert> // assert
-#include <ciso646> // and, not, or
-#include <cstddef> // nullptr_t, ptrdiff_t, size_t
-#include <functional> // hash, less
-#include <initializer_list> // initializer_list
-#include <iosfwd> // istream, ostream
-#include <iterator> // random_access_iterator_tag
-#include <numeric> // accumulate
-#include <string> // string, stoi, to_string
-#include <utility> // declval, forward, move, pair, swap
-
-// #include <nlohmann/json_fwd.hpp>
-#ifndef NLOHMANN_JSON_FWD_HPP
-#define NLOHMANN_JSON_FWD_HPP
-
-#include <cstdint> // int64_t, uint64_t
-#include <map> // map
-#include <memory> // allocator
-#include <string> // string
-#include <vector> // vector
-
-/*!
-@brief namespace for Niels Lohmann
-@see https://github.com/nlohmann
-@since version 1.0.0
-*/
-namespace nlohmann
-{
-/*!
-@brief default JSONSerializer template argument
-
-This serializer ignores the template arguments and uses ADL
-([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
-for serialization.
-*/
-template<typename T = void, typename SFINAE = void>
-struct adl_serializer;
-
-template<template<typename U, typename V, typename... Args> class ObjectType =
-         std::map,
-         template<typename U, typename... Args> class ArrayType = std::vector,
-         class StringType = std::string, class BooleanType = bool,
-         class NumberIntegerType = std::int64_t,
-         class NumberUnsignedType = std::uint64_t,
-         class NumberFloatType = double,
-         template<typename U> class AllocatorType = std::allocator,
-         template<typename T, typename SFINAE = void> class JSONSerializer =
-         adl_serializer>
-class basic_json;
-
-/*!
-@brief JSON Pointer
-
-A JSON pointer defines a string syntax for identifying a specific value
-within a JSON document. It can be used with functions `at` and
-`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
-
-@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
-
-@since version 2.0.0
-*/
-template<typename BasicJsonType>
-class json_pointer;
-
-/*!
-@brief default JSON class
-
-This type is the default specialization of the @ref basic_json class which
-uses the standard template types.
-
-@since version 1.0.0
-*/
-using json = basic_json<>;
-}  // namespace nlohmann
-
-#endif
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-
-// This file contains all internal macro definitions
-// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
-
-// exclude unsupported compilers
-#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
-    #if defined(__clang__)
-        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
-            #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
-        #endif
-    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
-        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
-            #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
-        #endif
-    #endif
-#endif
-
-// disable float-equal warnings on GCC/clang
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
-    #pragma GCC diagnostic push
-    #pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-
-// disable documentation warnings on clang
-#if defined(__clang__)
-    #pragma GCC diagnostic push
-    #pragma GCC diagnostic ignored "-Wdocumentation"
-#endif
-
-// allow for portable deprecation warnings
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
-    #define JSON_DEPRECATED __attribute__((deprecated))
-#elif defined(_MSC_VER)
-    #define JSON_DEPRECATED __declspec(deprecated)
-#else
-    #define JSON_DEPRECATED
-#endif
-
-// allow to disable exceptions
-#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
-    #define JSON_THROW(exception) throw exception
-    #define JSON_TRY try
-    #define JSON_CATCH(exception) catch(exception)
-    #define JSON_INTERNAL_CATCH(exception) catch(exception)
-#else
-    #define JSON_THROW(exception) std::abort()
-    #define JSON_TRY if(true)
-    #define JSON_CATCH(exception) if(false)
-    #define JSON_INTERNAL_CATCH(exception) if(false)
-#endif
-
-// override exception macros
-#if defined(JSON_THROW_USER)
-    #undef JSON_THROW
-    #define JSON_THROW JSON_THROW_USER
-#endif
-#if defined(JSON_TRY_USER)
-    #undef JSON_TRY
-    #define JSON_TRY JSON_TRY_USER
-#endif
-#if defined(JSON_CATCH_USER)
-    #undef JSON_CATCH
-    #define JSON_CATCH JSON_CATCH_USER
-    #undef JSON_INTERNAL_CATCH
-    #define JSON_INTERNAL_CATCH JSON_CATCH_USER
-#endif
-#if defined(JSON_INTERNAL_CATCH_USER)
-    #undef JSON_INTERNAL_CATCH
-    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
-#endif
-
-// manual branch prediction
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
-    #define JSON_LIKELY(x)      __builtin_expect(!!(x), 1)
-    #define JSON_UNLIKELY(x)    __builtin_expect(!!(x), 0)
-#else
-    #define JSON_LIKELY(x)      x
-    #define JSON_UNLIKELY(x)    x
-#endif
-
-// C++ language standard detection
-#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
-    #define JSON_HAS_CPP_17
-    #define JSON_HAS_CPP_14
-#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
-    #define JSON_HAS_CPP_14
-#endif
-
-/*!
-@brief macro to briefly define a mapping between an enum and JSON
-@def NLOHMANN_JSON_SERIALIZE_ENUM
-@since version 3.4.0
-*/
-#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                           \
-    template<typename BasicJsonType>                                                           \
-    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                  \
-    {                                                                                          \
-        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");         \
-        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                    \
-        auto it = std::find_if(std::begin(m), std::end(m),                                     \
-                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
-        {                                                                                      \
-            return ej_pair.first == e;                                                         \
-        });                                                                                    \
-        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                \
-    }                                                                                          \
-    template<typename BasicJsonType>                                                           \
-    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                \
-    {                                                                                          \
-        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");         \
-        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                    \
-        auto it = std::find_if(std::begin(m), std::end(m),                                     \
-                               [j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
-        {                                                                                      \
-            return ej_pair.second == j;                                                        \
-        });                                                                                    \
-        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                 \
-    }
-
-// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
-// may be removed in the future once the class is split.
-
-#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \
-    template<template<typename, typename, typename...> class ObjectType,   \
-             template<typename, typename...> class ArrayType,              \
-             class StringType, class BooleanType, class NumberIntegerType, \
-             class NumberUnsignedType, class NumberFloatType,              \
-             template<typename> class AllocatorType,                       \
-             template<typename, typename = void> class JSONSerializer>
-
-#define NLOHMANN_BASIC_JSON_TPL                                            \
-    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \
-    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \
-    AllocatorType, JSONSerializer>
-
-// #include <nlohmann/detail/meta/cpp_future.hpp>
-
-
-#include <ciso646> // not
-#include <cstddef> // size_t
-#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
-
-namespace nlohmann
-{
-namespace detail
-{
-// alias templates to reduce boilerplate
-template<bool B, typename T = void>
-using enable_if_t = typename std::enable_if<B, T>::type;
-
-template<typename T>
-using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
-
-// implementation of C++14 index_sequence and affiliates
-// source: https://stackoverflow.com/a/32223343
-template<std::size_t... Ints>
-struct index_sequence
-{
-    using type = index_sequence;
-    using value_type = std::size_t;
-    static constexpr std::size_t size() noexcept
-    {
-        return sizeof...(Ints);
-    }
-};
-
-template<class Sequence1, class Sequence2>
-struct merge_and_renumber;
-
-template<std::size_t... I1, std::size_t... I2>
-struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
-        : index_sequence < I1..., (sizeof...(I1) + I2)... > {};
-
-template<std::size_t N>
-struct make_index_sequence
-    : merge_and_renumber < typename make_index_sequence < N / 2 >::type,
-      typename make_index_sequence < N - N / 2 >::type > {};
-
-template<> struct make_index_sequence<0> : index_sequence<> {};
-template<> struct make_index_sequence<1> : index_sequence<0> {};
-
-template<typename... Ts>
-using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
-
-// dispatch utility (taken from ranges-v3)
-template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
-template<> struct priority_tag<0> {};
-
-// taken from ranges-v3
-template<typename T>
-struct static_const
-{
-    static constexpr T value{};
-};
-
-template<typename T>
-constexpr T static_const<T>::value;
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/meta/type_traits.hpp>
-
-
-#include <ciso646> // not
-#include <limits> // numeric_limits
-#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
-#include <utility> // declval
-
-// #include <nlohmann/json_fwd.hpp>
-
-// #include <nlohmann/detail/iterators/iterator_traits.hpp>
-
-
-#include <iterator> // random_access_iterator_tag
-
-// #include <nlohmann/detail/meta/void_t.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-template <typename ...Ts> struct make_void
-{
-    using type = void;
-};
-template <typename ...Ts> using void_t = typename make_void<Ts...>::type;
-} // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/meta/cpp_future.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-template <typename It, typename = void>
-struct iterator_types {};
-
-template <typename It>
-struct iterator_types <
-    It,
-    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
-    typename It::reference, typename It::iterator_category >>
-{
-    using difference_type = typename It::difference_type;
-    using value_type = typename It::value_type;
-    using pointer = typename It::pointer;
-    using reference = typename It::reference;
-    using iterator_category = typename It::iterator_category;
-};
-
-// This is required as some compilers implement std::iterator_traits in a way that
-// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
-template <typename T, typename = void>
-struct iterator_traits
-{
-};
-
-template <typename T>
-struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
-            : iterator_types<T>
-{
-};
-
-template <typename T>
-struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
-{
-    using iterator_category = std::random_access_iterator_tag;
-    using value_type = T;
-    using difference_type = ptrdiff_t;
-    using pointer = T*;
-    using reference = T&;
-};
-}
-}
-
-// #include <nlohmann/detail/meta/cpp_future.hpp>
-
-// #include <nlohmann/detail/meta/detected.hpp>
-
-
-#include <type_traits>
-
-// #include <nlohmann/detail/meta/void_t.hpp>
-
-
-// http://en.cppreference.com/w/cpp/experimental/is_detected
-namespace nlohmann
-{
-namespace detail
-{
-struct nonesuch
-{
-    nonesuch() = delete;
-    ~nonesuch() = delete;
-    nonesuch(nonesuch const&) = delete;
-    void operator=(nonesuch const&) = delete;
-};
-
-template <class Default,
-          class AlwaysVoid,
-          template <class...> class Op,
-          class... Args>
-struct detector
-{
-    using value_t = std::false_type;
-    using type = Default;
-};
-
-template <class Default, template <class...> class Op, class... Args>
-struct detector<Default, void_t<Op<Args...>>, Op, Args...>
-{
-    using value_t = std::true_type;
-    using type = Op<Args...>;
-};
-
-template <template <class...> class Op, class... Args>
-using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
-
-template <template <class...> class Op, class... Args>
-using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
-
-template <class Default, template <class...> class Op, class... Args>
-using detected_or = detector<Default, void, Op, Args...>;
-
-template <class Default, template <class...> class Op, class... Args>
-using detected_or_t = typename detected_or<Default, Op, Args...>::type;
-
-template <class Expected, template <class...> class Op, class... Args>
-using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
-
-template <class To, template <class...> class Op, class... Args>
-using is_detected_convertible =
-    std::is_convertible<detected_t<Op, Args...>, To>;
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-
-namespace nlohmann
-{
-/*!
-@brief detail namespace with internal helper functions
-
-This namespace collects functions that should not be exposed,
-implementations of some @ref basic_json methods, and meta-programming helpers.
-
-@since version 2.1.0
-*/
-namespace detail
-{
-/////////////
-// helpers //
-/////////////
-
-// Note to maintainers:
-//
-// Every trait in this file expects a non CV-qualified type.
-// The only exceptions are in the 'aliases for detected' section
-// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
-//
-// In this case, T has to be properly CV-qualified to constraint the function arguments
-// (e.g. to_json(BasicJsonType&, const T&))
-
-template<typename> struct is_basic_json : std::false_type {};
-
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
-
-//////////////////////////
-// aliases for detected //
-//////////////////////////
-
-template <typename T>
-using mapped_type_t = typename T::mapped_type;
-
-template <typename T>
-using key_type_t = typename T::key_type;
-
-template <typename T>
-using value_type_t = typename T::value_type;
-
-template <typename T>
-using difference_type_t = typename T::difference_type;
-
-template <typename T>
-using pointer_t = typename T::pointer;
-
-template <typename T>
-using reference_t = typename T::reference;
-
-template <typename T>
-using iterator_category_t = typename T::iterator_category;
-
-template <typename T>
-using iterator_t = typename T::iterator;
-
-template <typename T, typename... Args>
-using to_json_function = decltype(T::to_json(std::declval<Args>()...));
-
-template <typename T, typename... Args>
-using from_json_function = decltype(T::from_json(std::declval<Args>()...));
-
-template <typename T, typename U>
-using get_template_function = decltype(std::declval<T>().template get<U>());
-
-// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
-template <typename BasicJsonType, typename T, typename = void>
-struct has_from_json : std::false_type {};
-
-template <typename BasicJsonType, typename T>
-struct has_from_json<BasicJsonType, T,
-           enable_if_t<not is_basic_json<T>::value>>
-{
-    using serializer = typename BasicJsonType::template json_serializer<T, void>;
-
-    static constexpr bool value =
-        is_detected_exact<void, from_json_function, serializer,
-        const BasicJsonType&, T&>::value;
-};
-
-// This trait checks if JSONSerializer<T>::from_json(json const&) exists
-// this overload is used for non-default-constructible user-defined-types
-template <typename BasicJsonType, typename T, typename = void>
-struct has_non_default_from_json : std::false_type {};
-
-template<typename BasicJsonType, typename T>
-struct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>>
-{
-    using serializer = typename BasicJsonType::template json_serializer<T, void>;
-
-    static constexpr bool value =
-        is_detected_exact<T, from_json_function, serializer,
-        const BasicJsonType&>::value;
-};
-
-// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
-// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
-template <typename BasicJsonType, typename T, typename = void>
-struct has_to_json : std::false_type {};
-
-template <typename BasicJsonType, typename T>
-struct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>>
-{
-    using serializer = typename BasicJsonType::template json_serializer<T, void>;
-
-    static constexpr bool value =
-        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
-        T>::value;
-};
-
-
-///////////////////
-// is_ functions //
-///////////////////
-
-template <typename T, typename = void>
-struct is_iterator_traits : std::false_type {};
-
-template <typename T>
-struct is_iterator_traits<iterator_traits<T>>
-{
-  private:
-    using traits = iterator_traits<T>;
-
-  public:
-    static constexpr auto value =
-        is_detected<value_type_t, traits>::value &&
-        is_detected<difference_type_t, traits>::value &&
-        is_detected<pointer_t, traits>::value &&
-        is_detected<iterator_category_t, traits>::value &&
-        is_detected<reference_t, traits>::value;
-};
-
-// source: https://stackoverflow.com/a/37193089/4116453
-
-template <typename T, typename = void>
-struct is_complete_type : std::false_type {};
-
-template <typename T>
-struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
-
-template <typename BasicJsonType, typename CompatibleObjectType,
-          typename = void>
-struct is_compatible_object_type_impl : std::false_type {};
-
-template <typename BasicJsonType, typename CompatibleObjectType>
-struct is_compatible_object_type_impl <
-    BasicJsonType, CompatibleObjectType,
-    enable_if_t<is_detected<mapped_type_t, CompatibleObjectType>::value and
-    is_detected<key_type_t, CompatibleObjectType>::value >>
-{
-
-    using object_t = typename BasicJsonType::object_t;
-
-    // macOS's is_constructible does not play well with nonesuch...
-    static constexpr bool value =
-        std::is_constructible<typename object_t::key_type,
-        typename CompatibleObjectType::key_type>::value and
-        std::is_constructible<typename object_t::mapped_type,
-        typename CompatibleObjectType::mapped_type>::value;
-};
-
-template <typename BasicJsonType, typename CompatibleObjectType>
-struct is_compatible_object_type
-    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
-
-template <typename BasicJsonType, typename ConstructibleObjectType,
-          typename = void>
-struct is_constructible_object_type_impl : std::false_type {};
-
-template <typename BasicJsonType, typename ConstructibleObjectType>
-struct is_constructible_object_type_impl <
-    BasicJsonType, ConstructibleObjectType,
-    enable_if_t<is_detected<mapped_type_t, ConstructibleObjectType>::value and
-    is_detected<key_type_t, ConstructibleObjectType>::value >>
-{
-    using object_t = typename BasicJsonType::object_t;
-
-    static constexpr bool value =
-        (std::is_constructible<typename ConstructibleObjectType::key_type, typename object_t::key_type>::value and
-         std::is_same<typename object_t::mapped_type, typename ConstructibleObjectType::mapped_type>::value) or
-        (has_from_json<BasicJsonType, typename ConstructibleObjectType::mapped_type>::value or
-         has_non_default_from_json<BasicJsonType, typename ConstructibleObjectType::mapped_type >::value);
-};
-
-template <typename BasicJsonType, typename ConstructibleObjectType>
-struct is_constructible_object_type
-    : is_constructible_object_type_impl<BasicJsonType,
-      ConstructibleObjectType> {};
-
-template <typename BasicJsonType, typename CompatibleStringType,
-          typename = void>
-struct is_compatible_string_type_impl : std::false_type {};
-
-template <typename BasicJsonType, typename CompatibleStringType>
-struct is_compatible_string_type_impl <
-    BasicJsonType, CompatibleStringType,
-    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,
-    value_type_t, CompatibleStringType>::value >>
-{
-    static constexpr auto value =
-        std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
-};
-
-template <typename BasicJsonType, typename ConstructibleStringType>
-struct is_compatible_string_type
-    : is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
-
-template <typename BasicJsonType, typename ConstructibleStringType,
-          typename = void>
-struct is_constructible_string_type_impl : std::false_type {};
-
-template <typename BasicJsonType, typename ConstructibleStringType>
-struct is_constructible_string_type_impl <
-    BasicJsonType, ConstructibleStringType,
-    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,
-    value_type_t, ConstructibleStringType>::value >>
-{
-    static constexpr auto value =
-        std::is_constructible<ConstructibleStringType,
-        typename BasicJsonType::string_t>::value;
-};
-
-template <typename BasicJsonType, typename ConstructibleStringType>
-struct is_constructible_string_type
-    : is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
-
-template <typename BasicJsonType, typename CompatibleArrayType, typename = void>
-struct is_compatible_array_type_impl : std::false_type {};
-
-template <typename BasicJsonType, typename CompatibleArrayType>
-struct is_compatible_array_type_impl <
-    BasicJsonType, CompatibleArrayType,
-    enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and
-    is_detected<iterator_t, CompatibleArrayType>::value and
-// This is needed because json_reverse_iterator has a ::iterator type...
-// Therefore it is detected as a CompatibleArrayType.
-// The real fix would be to have an Iterable concept.
-    not is_iterator_traits<
-    iterator_traits<CompatibleArrayType>>::value >>
-{
-    static constexpr bool value =
-        std::is_constructible<BasicJsonType,
-        typename CompatibleArrayType::value_type>::value;
-};
-
-template <typename BasicJsonType, typename CompatibleArrayType>
-struct is_compatible_array_type
-    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
-
-template <typename BasicJsonType, typename ConstructibleArrayType, typename = void>
-struct is_constructible_array_type_impl : std::false_type {};
-
-template <typename BasicJsonType, typename ConstructibleArrayType>
-struct is_constructible_array_type_impl <
-    BasicJsonType, ConstructibleArrayType,
-    enable_if_t<std::is_same<ConstructibleArrayType,
-    typename BasicJsonType::value_type>::value >>
-            : std::true_type {};
-
-template <typename BasicJsonType, typename ConstructibleArrayType>
-struct is_constructible_array_type_impl <
-    BasicJsonType, ConstructibleArrayType,
-    enable_if_t<not std::is_same<ConstructibleArrayType,
-    typename BasicJsonType::value_type>::value and
-    is_detected<value_type_t, ConstructibleArrayType>::value and
-    is_detected<iterator_t, ConstructibleArrayType>::value and
-    is_complete_type<
-    detected_t<value_type_t, ConstructibleArrayType>>::value >>
-{
-    static constexpr bool value =
-        // This is needed because json_reverse_iterator has a ::iterator type,
-        // furthermore, std::back_insert_iterator (and other iterators) have a base class `iterator`...
-        // Therefore it is detected as a ConstructibleArrayType.
-        // The real fix would be to have an Iterable concept.
-        not is_iterator_traits <
-        iterator_traits<ConstructibleArrayType >>::value and
-
-        (std::is_same<typename ConstructibleArrayType::value_type, typename BasicJsonType::array_t::value_type>::value or
-         has_from_json<BasicJsonType,
-         typename ConstructibleArrayType::value_type>::value or
-         has_non_default_from_json <
-         BasicJsonType, typename ConstructibleArrayType::value_type >::value);
-};
-
-template <typename BasicJsonType, typename ConstructibleArrayType>
-struct is_constructible_array_type
-    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
-
-template <typename RealIntegerType, typename CompatibleNumberIntegerType,
-          typename = void>
-struct is_compatible_integer_type_impl : std::false_type {};
-
-template <typename RealIntegerType, typename CompatibleNumberIntegerType>
-struct is_compatible_integer_type_impl <
-    RealIntegerType, CompatibleNumberIntegerType,
-    enable_if_t<std::is_integral<RealIntegerType>::value and
-    std::is_integral<CompatibleNumberIntegerType>::value and
-    not std::is_same<bool, CompatibleNumberIntegerType>::value >>
-{
-    // is there an assert somewhere on overflows?
-    using RealLimits = std::numeric_limits<RealIntegerType>;
-    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
-
-    static constexpr auto value =
-        std::is_constructible<RealIntegerType,
-        CompatibleNumberIntegerType>::value and
-        CompatibleLimits::is_integer and
-        RealLimits::is_signed == CompatibleLimits::is_signed;
-};
-
-template <typename RealIntegerType, typename CompatibleNumberIntegerType>
-struct is_compatible_integer_type
-    : is_compatible_integer_type_impl<RealIntegerType,
-      CompatibleNumberIntegerType> {};
-
-template <typename BasicJsonType, typename CompatibleType, typename = void>
-struct is_compatible_type_impl: std::false_type {};
-
-template <typename BasicJsonType, typename CompatibleType>
-struct is_compatible_type_impl <
-    BasicJsonType, CompatibleType,
-    enable_if_t<is_complete_type<CompatibleType>::value >>
-{
-    static constexpr bool value =
-        has_to_json<BasicJsonType, CompatibleType>::value;
-};
-
-template <typename BasicJsonType, typename CompatibleType>
-struct is_compatible_type
-    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/exceptions.hpp>
-
-
-#include <exception> // exception
-#include <stdexcept> // runtime_error
-#include <string> // to_string
-
-// #include <nlohmann/detail/input/position_t.hpp>
-
-
-#include <cstddef> // size_t
-
-namespace nlohmann
-{
-namespace detail
-{
-/// struct to capture the start position of the current token
-struct position_t
-{
-    /// the total number of characters read
-    std::size_t chars_read_total = 0;
-    /// the number of characters read in the current line
-    std::size_t chars_read_current_line = 0;
-    /// the number of lines read
-    std::size_t lines_read = 0;
-
-    /// conversion to size_t to preserve SAX interface
-    constexpr operator size_t() const
-    {
-        return chars_read_total;
-    }
-};
-
-}
-}
-
-
-namespace nlohmann
-{
-namespace detail
-{
-////////////////
-// exceptions //
-////////////////
-
-/*!
-@brief general exception of the @ref basic_json class
-
-This class is an extension of `std::exception` objects with a member @a id for
-exception ids. It is used as the base class for all exceptions thrown by the
-@ref basic_json class. This class can hence be used as "wildcard" to catch
-exceptions.
-
-Subclasses:
-- @ref parse_error for exceptions indicating a parse error
-- @ref invalid_iterator for exceptions indicating errors with iterators
-- @ref type_error for exceptions indicating executing a member function with
-                  a wrong type
-- @ref out_of_range for exceptions indicating access out of the defined range
-- @ref other_error for exceptions indicating other library errors
-
-@internal
-@note To have nothrow-copy-constructible exceptions, we internally use
-      `std::runtime_error` which can cope with arbitrary-length error messages.
-      Intermediate strings are built with static functions and then passed to
-      the actual constructor.
-@endinternal
-
-@liveexample{The following code shows how arbitrary library exceptions can be
-caught.,exception}
-
-@since version 3.0.0
-*/
-class exception : public std::exception
-{
-  public:
-    /// returns the explanatory string
-    const char* what() const noexcept override
-    {
-        return m.what();
-    }
-
-    /// the id of the exception
-    const int id;
-
-  protected:
-    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}
-
-    static std::string name(const std::string& ename, int id_)
-    {
-        return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
-    }
-
-  private:
-    /// an exception object as storage for error messages
-    std::runtime_error m;
-};
-
-/*!
-@brief exception indicating a parse error
-
-This exception is thrown by the library when a parse error occurs. Parse errors
-can occur during the deserialization of JSON text, CBOR, MessagePack, as well
-as when using JSON Patch.
-
-Member @a byte holds the byte index of the last read character in the input
-file.
-
-Exceptions have ids 1xx.
-
-name / id                      | example message | description
------------------------------- | --------------- | -------------------------
-json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.
-json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.
-json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.
-json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.
-json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors.
-json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.
-json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.
-json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
-json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.
-json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.
-json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.
-json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.
-json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet).
-
-@note For an input with n bytes, 1 is the index of the first character and n+1
-      is the index of the terminating null byte or the end of file. This also
-      holds true when reading a byte vector (CBOR or MessagePack).
-
-@liveexample{The following code shows how a `parse_error` exception can be
-caught.,parse_error}
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref invalid_iterator for exceptions indicating errors with iterators
-@sa @ref type_error for exceptions indicating executing a member function with
-                    a wrong type
-@sa @ref out_of_range for exceptions indicating access out of the defined range
-@sa @ref other_error for exceptions indicating other library errors
-
-@since version 3.0.0
-*/
-class parse_error : public exception
-{
-  public:
-    /*!
-    @brief create a parse error exception
-    @param[in] id_       the id of the exception
-    @param[in] position  the position where the error occurred (or with
-                         chars_read_total=0 if the position cannot be
-                         determined)
-    @param[in] what_arg  the explanatory string
-    @return parse_error object
-    */
-    static parse_error create(int id_, const position_t& pos, const std::string& what_arg)
-    {
-        std::string w = exception::name("parse_error", id_) + "parse error" +
-                        position_string(pos) + ": " + what_arg;
-        return parse_error(id_, pos.chars_read_total, w.c_str());
-    }
-
-    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)
-    {
-        std::string w = exception::name("parse_error", id_) + "parse error" +
-                        (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") +
-                        ": " + what_arg;
-        return parse_error(id_, byte_, w.c_str());
-    }
-
-    /*!
-    @brief byte index of the parse error
-
-    The byte index of the last read character in the input file.
-
-    @note For an input with n bytes, 1 is the index of the first character and
-          n+1 is the index of the terminating null byte or the end of file.
-          This also holds true when reading a byte vector (CBOR or MessagePack).
-    */
-    const std::size_t byte;
-
-  private:
-    parse_error(int id_, std::size_t byte_, const char* what_arg)
-        : exception(id_, what_arg), byte(byte_) {}
-
-    static std::string position_string(const position_t& pos)
-    {
-        return " at line " + std::to_string(pos.lines_read + 1) +
-               ", column " + std::to_string(pos.chars_read_current_line);
-    }
-};
-
-/*!
-@brief exception indicating errors with iterators
-
-This exception is thrown if iterators passed to a library function do not match
-the expected semantics.
-
-Exceptions have ids 2xx.
-
-name / id                           | example message | description
------------------------------------ | --------------- | -------------------------
-json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
-json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
-json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
-json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
-json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
-json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
-json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
-json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
-json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
-json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
-json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.
-json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.
-json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.
-json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
-
-@liveexample{The following code shows how an `invalid_iterator` exception can be
-caught.,invalid_iterator}
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref parse_error for exceptions indicating a parse error
-@sa @ref type_error for exceptions indicating executing a member function with
-                    a wrong type
-@sa @ref out_of_range for exceptions indicating access out of the defined range
-@sa @ref other_error for exceptions indicating other library errors
-
-@since version 3.0.0
-*/
-class invalid_iterator : public exception
-{
-  public:
-    static invalid_iterator create(int id_, const std::string& what_arg)
-    {
-        std::string w = exception::name("invalid_iterator", id_) + what_arg;
-        return invalid_iterator(id_, w.c_str());
-    }
-
-  private:
-    invalid_iterator(int id_, const char* what_arg)
-        : exception(id_, what_arg) {}
-};
-
-/*!
-@brief exception indicating executing a member function with a wrong type
-
-This exception is thrown in case of a type error; that is, a library function is
-executed on a JSON value whose type does not match the expected semantics.
-
-Exceptions have ids 3xx.
-
-name / id                     | example message | description
------------------------------ | --------------- | -------------------------
-json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.
-json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
-json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&.
-json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.
-json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.
-json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.
-json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.
-json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.
-json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.
-json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.
-json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.
-json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.
-json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.
-json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
-json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
-json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
-json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |
-
-@liveexample{The following code shows how a `type_error` exception can be
-caught.,type_error}
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref parse_error for exceptions indicating a parse error
-@sa @ref invalid_iterator for exceptions indicating errors with iterators
-@sa @ref out_of_range for exceptions indicating access out of the defined range
-@sa @ref other_error for exceptions indicating other library errors
-
-@since version 3.0.0
-*/
-class type_error : public exception
-{
-  public:
-    static type_error create(int id_, const std::string& what_arg)
-    {
-        std::string w = exception::name("type_error", id_) + what_arg;
-        return type_error(id_, w.c_str());
-    }
-
-  private:
-    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
-};
-
-/*!
-@brief exception indicating access out of the defined range
-
-This exception is thrown in case a library function is called on an input
-parameter that exceeds the expected range, for instance in case of array
-indices or nonexisting object keys.
-
-Exceptions have ids 4xx.
-
-name / id                       | example message | description
-------------------------------- | --------------- | -------------------------
-json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.
-json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.
-json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.
-json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
-json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
-json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
-json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. |
-json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |
-json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |
-
-@liveexample{The following code shows how an `out_of_range` exception can be
-caught.,out_of_range}
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref parse_error for exceptions indicating a parse error
-@sa @ref invalid_iterator for exceptions indicating errors with iterators
-@sa @ref type_error for exceptions indicating executing a member function with
-                    a wrong type
-@sa @ref other_error for exceptions indicating other library errors
-
-@since version 3.0.0
-*/
-class out_of_range : public exception
-{
-  public:
-    static out_of_range create(int id_, const std::string& what_arg)
-    {
-        std::string w = exception::name("out_of_range", id_) + what_arg;
-        return out_of_range(id_, w.c_str());
-    }
-
-  private:
-    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
-};
-
-/*!
-@brief exception indicating other library errors
-
-This exception is thrown in case of errors that cannot be classified with the
-other exception types.
-
-Exceptions have ids 5xx.
-
-name / id                      | example message | description
------------------------------- | --------------- | -------------------------
-json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
-
-@sa @ref exception for the base class of the library exceptions
-@sa @ref parse_error for exceptions indicating a parse error
-@sa @ref invalid_iterator for exceptions indicating errors with iterators
-@sa @ref type_error for exceptions indicating executing a member function with
-                    a wrong type
-@sa @ref out_of_range for exceptions indicating access out of the defined range
-
-@liveexample{The following code shows how an `other_error` exception can be
-caught.,other_error}
-
-@since version 3.0.0
-*/
-class other_error : public exception
-{
-  public:
-    static other_error create(int id_, const std::string& what_arg)
-    {
-        std::string w = exception::name("other_error", id_) + what_arg;
-        return other_error(id_, w.c_str());
-    }
-
-  private:
-    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/value_t.hpp>
-
-
-#include <array> // array
-#include <ciso646> // and
-#include <cstddef> // size_t
-#include <cstdint> // uint8_t
-
-namespace nlohmann
-{
-namespace detail
-{
-///////////////////////////
-// JSON type enumeration //
-///////////////////////////
-
-/*!
-@brief the JSON type enumeration
-
-This enumeration collects the different JSON types. It is internally used to
-distinguish the stored values, and the functions @ref basic_json::is_null(),
-@ref basic_json::is_object(), @ref basic_json::is_array(),
-@ref basic_json::is_string(), @ref basic_json::is_boolean(),
-@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
-@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
-@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
-@ref basic_json::is_structured() rely on it.
-
-@note There are three enumeration entries (number_integer, number_unsigned, and
-number_float), because the library distinguishes these three types for numbers:
-@ref basic_json::number_unsigned_t is used for unsigned integers,
-@ref basic_json::number_integer_t is used for signed integers, and
-@ref basic_json::number_float_t is used for floating-point numbers or to
-approximate integers which do not fit in the limits of their respective type.
-
-@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON
-value with the default value for a given type
-
-@since version 1.0.0
-*/
-enum class value_t : std::uint8_t
-{
-    null,             ///< null value
-    object,           ///< object (unordered set of name/value pairs)
-    array,            ///< array (ordered collection of values)
-    string,           ///< string value
-    boolean,          ///< boolean value
-    number_integer,   ///< number value (signed integer)
-    number_unsigned,  ///< number value (unsigned integer)
-    number_float,     ///< number value (floating-point)
-    discarded         ///< discarded by the the parser callback function
-};
-
-/*!
-@brief comparison operator for JSON types
-
-Returns an ordering that is similar to Python:
-- order: null < boolean < number < object < array < string
-- furthermore, each type is not smaller than itself
-- discarded values are not comparable
-
-@since version 1.0.0
-*/
-inline bool operator<(const value_t lhs, const value_t rhs) noexcept
-{
-    static constexpr std::array<std::uint8_t, 8> order = {{
-            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
-            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */
-        }
-    };
-
-    const auto l_index = static_cast<std::size_t>(lhs);
-    const auto r_index = static_cast<std::size_t>(rhs);
-    return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];
-}
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/conversions/from_json.hpp>
-
-
-#include <algorithm> // transform
-#include <array> // array
-#include <ciso646> // and, not
-#include <forward_list> // forward_list
-#include <iterator> // inserter, front_inserter, end
-#include <map> // map
-#include <string> // string
-#include <tuple> // tuple, make_tuple
-#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
-#include <unordered_map> // unordered_map
-#include <utility> // pair, declval
-#include <valarray> // valarray
-
-// #include <nlohmann/detail/exceptions.hpp>
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-// #include <nlohmann/detail/meta/cpp_future.hpp>
-
-// #include <nlohmann/detail/meta/type_traits.hpp>
-
-// #include <nlohmann/detail/value_t.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
-{
-    if (JSON_UNLIKELY(not j.is_null()))
-    {
-        JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name())));
-    }
-    n = nullptr;
-}
-
-// overloads for basic_json template parameters
-template<typename BasicJsonType, typename ArithmeticType,
-         enable_if_t<std::is_arithmetic<ArithmeticType>::value and
-                     not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
-                     int> = 0>
-void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
-{
-    switch (static_cast<value_t>(j))
-    {
-        case value_t::number_unsigned:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
-            break;
-        }
-        case value_t::number_integer:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
-            break;
-        }
-        case value_t::number_float:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
-            break;
-        }
-
-        default:
-            JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
-    }
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
-{
-    if (JSON_UNLIKELY(not j.is_boolean()))
-    {
-        JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name())));
-    }
-    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
-{
-    if (JSON_UNLIKELY(not j.is_string()))
-    {
-        JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
-    }
-    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
-}
-
-template <
-    typename BasicJsonType, typename ConstructibleStringType,
-    enable_if_t <
-        is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value and
-        not std::is_same<typename BasicJsonType::string_t,
-                         ConstructibleStringType>::value,
-        int > = 0 >
-void from_json(const BasicJsonType& j, ConstructibleStringType& s)
-{
-    if (JSON_UNLIKELY(not j.is_string()))
-    {
-        JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
-    }
-
-    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
-{
-    get_arithmetic_value(j, val);
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
-{
-    get_arithmetic_value(j, val);
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
-{
-    get_arithmetic_value(j, val);
-}
-
-template<typename BasicJsonType, typename EnumType,
-         enable_if_t<std::is_enum<EnumType>::value, int> = 0>
-void from_json(const BasicJsonType& j, EnumType& e)
-{
-    typename std::underlying_type<EnumType>::type val;
-    get_arithmetic_value(j, val);
-    e = static_cast<EnumType>(val);
-}
-
-// forward_list doesn't have an insert method
-template<typename BasicJsonType, typename T, typename Allocator,
-         enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
-void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
-{
-    if (JSON_UNLIKELY(not j.is_array()))
-    {
-        JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
-    }
-    std::transform(j.rbegin(), j.rend(),
-                   std::front_inserter(l), [](const BasicJsonType & i)
-    {
-        return i.template get<T>();
-    });
-}
-
-// valarray doesn't have an insert method
-template<typename BasicJsonType, typename T,
-         enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
-void from_json(const BasicJsonType& j, std::valarray<T>& l)
-{
-    if (JSON_UNLIKELY(not j.is_array()))
-    {
-        JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
-    }
-    l.resize(j.size());
-    std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l));
-}
-
-template<typename BasicJsonType>
-void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
-{
-    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
-}
-
-template <typename BasicJsonType, typename T, std::size_t N>
-auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
-                          priority_tag<2> /*unused*/)
--> decltype(j.template get<T>(), void())
-{
-    for (std::size_t i = 0; i < N; ++i)
-    {
-        arr[i] = j.at(i).template get<T>();
-    }
-}
-
-template<typename BasicJsonType, typename ConstructibleArrayType>
-auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
--> decltype(
-    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
-    j.template get<typename ConstructibleArrayType::value_type>(),
-    void())
-{
-    using std::end;
-
-    arr.reserve(j.size());
-    std::transform(j.begin(), j.end(),
-                   std::inserter(arr, end(arr)), [](const BasicJsonType & i)
-    {
-        // get<BasicJsonType>() returns *this, this won't call a from_json
-        // method when value_type is BasicJsonType
-        return i.template get<typename ConstructibleArrayType::value_type>();
-    });
-}
-
-template <typename BasicJsonType, typename ConstructibleArrayType>
-void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
-                          priority_tag<0> /*unused*/)
-{
-    using std::end;
-
-    std::transform(
-        j.begin(), j.end(), std::inserter(arr, end(arr)),
-        [](const BasicJsonType & i)
-    {
-        // get<BasicJsonType>() returns *this, this won't call a from_json
-        // method when value_type is BasicJsonType
-        return i.template get<typename ConstructibleArrayType::value_type>();
-    });
-}
-
-template <typename BasicJsonType, typename ConstructibleArrayType,
-          enable_if_t <
-              is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value and
-              not is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value and
-              not is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value and
-              not is_basic_json<ConstructibleArrayType>::value,
-              int > = 0 >
-
-auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
--> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
-j.template get<typename ConstructibleArrayType::value_type>(),
-void())
-{
-    if (JSON_UNLIKELY(not j.is_array()))
-    {
-        JSON_THROW(type_error::create(302, "type must be array, but is " +
-                                      std::string(j.type_name())));
-    }
-
-    from_json_array_impl(j, arr, priority_tag<3> {});
-}
-
-template<typename BasicJsonType, typename ConstructibleObjectType,
-         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
-void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
-{
-    if (JSON_UNLIKELY(not j.is_object()))
-    {
-        JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
-    }
-
-    auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
-    using value_type = typename ConstructibleObjectType::value_type;
-    std::transform(
-        inner_object->begin(), inner_object->end(),
-        std::inserter(obj, obj.begin()),
-        [](typename BasicJsonType::object_t::value_type const & p)
-    {
-        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
-    });
-}
-
-// overload for arithmetic types, not chosen for basic_json template arguments
-// (BooleanType, etc..); note: Is it really necessary to provide explicit
-// overloads for boolean_t etc. in case of a custom BooleanType which is not
-// an arithmetic type?
-template<typename BasicJsonType, typename ArithmeticType,
-         enable_if_t <
-             std::is_arithmetic<ArithmeticType>::value and
-             not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
-             not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
-             not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
-             not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
-             int> = 0>
-void from_json(const BasicJsonType& j, ArithmeticType& val)
-{
-    switch (static_cast<value_t>(j))
-    {
-        case value_t::number_unsigned:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
-            break;
-        }
-        case value_t::number_integer:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
-            break;
-        }
-        case value_t::number_float:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
-            break;
-        }
-        case value_t::boolean:
-        {
-            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
-            break;
-        }
-
-        default:
-            JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
-    }
-}
-
-template<typename BasicJsonType, typename A1, typename A2>
-void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
-{
-    p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
-}
-
-template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
-void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...> /*unused*/)
-{
-    t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
-}
-
-template<typename BasicJsonType, typename... Args>
-void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
-{
-    from_json_tuple_impl(j, t, index_sequence_for<Args...> {});
-}
-
-template <typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
-          typename = enable_if_t<not std::is_constructible<
-                                     typename BasicJsonType::string_t, Key>::value>>
-void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
-{
-    if (JSON_UNLIKELY(not j.is_array()))
-    {
-        JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
-    }
-    for (const auto& p : j)
-    {
-        if (JSON_UNLIKELY(not p.is_array()))
-        {
-            JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name())));
-        }
-        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
-    }
-}
-
-template <typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
-          typename = enable_if_t<not std::is_constructible<
-                                     typename BasicJsonType::string_t, Key>::value>>
-void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
-{
-    if (JSON_UNLIKELY(not j.is_array()))
-    {
-        JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
-    }
-    for (const auto& p : j)
-    {
-        if (JSON_UNLIKELY(not p.is_array()))
-        {
-            JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name())));
-        }
-        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
-    }
-}
-
-struct from_json_fn
-{
-    template<typename BasicJsonType, typename T>
-    auto operator()(const BasicJsonType& j, T& val) const
-    noexcept(noexcept(from_json(j, val)))
-    -> decltype(from_json(j, val), void())
-    {
-        return from_json(j, val);
-    }
-};
-}  // namespace detail
-
-/// namespace to hold default `from_json` function
-/// to see why this is required:
-/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
-namespace
-{
-constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
-} // namespace
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/conversions/to_json.hpp>
-
-
-#include <ciso646> // or, and, not
-#include <iterator> // begin, end
-#include <tuple> // tuple, get
-#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
-#include <utility> // move, forward, declval, pair
-#include <valarray> // valarray
-#include <vector> // vector
-
-// #include <nlohmann/detail/meta/cpp_future.hpp>
-
-// #include <nlohmann/detail/meta/type_traits.hpp>
-
-// #include <nlohmann/detail/value_t.hpp>
-
-// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
-
-
-#include <cstddef> // size_t
-#include <string> // string, to_string
-#include <iterator> // input_iterator_tag
-#include <tuple> // tuple_size, get, tuple_element
-
-// #include <nlohmann/detail/value_t.hpp>
-
-// #include <nlohmann/detail/meta/type_traits.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-template <typename IteratorType> class iteration_proxy_value
-{
-  public:
-    using difference_type = std::ptrdiff_t;
-    using value_type = iteration_proxy_value;
-    using pointer = value_type * ;
-    using reference = value_type & ;
-    using iterator_category = std::input_iterator_tag;
-
-  private:
-    /// the iterator
-    IteratorType anchor;
-    /// an index for arrays (used to create key names)
-    std::size_t array_index = 0;
-    /// last stringified array index
-    mutable std::size_t array_index_last = 0;
-    /// a string representation of the array index
-    mutable std::string array_index_str = "0";
-    /// an empty string (to return a reference for primitive values)
-    const std::string empty_str = "";
-
-  public:
-    explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {}
-
-    /// dereference operator (needed for range-based for)
-    iteration_proxy_value& operator*()
-    {
-        return *this;
-    }
-
-    /// increment operator (needed for range-based for)
-    iteration_proxy_value& operator++()
-    {
-        ++anchor;
-        ++array_index;
-
-        return *this;
-    }
-
-    /// equality operator (needed for InputIterator)
-    bool operator==(const iteration_proxy_value& o) const noexcept
-    {
-        return anchor == o.anchor;
-    }
-
-    /// inequality operator (needed for range-based for)
-    bool operator!=(const iteration_proxy_value& o) const noexcept
-    {
-        return anchor != o.anchor;
-    }
-
-    /// return key of the iterator
-    const std::string& key() const
-    {
-        assert(anchor.m_object != nullptr);
-
-        switch (anchor.m_object->type())
-        {
-            // use integer array index as key
-            case value_t::array:
-            {
-                if (array_index != array_index_last)
-                {
-                    array_index_str = std::to_string(array_index);
-                    array_index_last = array_index;
-                }
-                return array_index_str;
-            }
-
-            // use key from the object
-            case value_t::object:
-                return anchor.key();
-
-            // use an empty key for all primitive types
-            default:
-                return empty_str;
-        }
-    }
-
-    /// return value of the iterator
-    typename IteratorType::reference value() const
-    {
-        return anchor.value();
-    }
-};
-
-/// proxy class for the items() function
-template<typename IteratorType> class iteration_proxy
-{
-  private:
-    /// the container to iterate
-    typename IteratorType::reference container;
-
-  public:
-    /// construct iteration proxy from a container
-    explicit iteration_proxy(typename IteratorType::reference cont) noexcept
-        : container(cont) {}
-
-    /// return iterator begin (needed for range-based for)
-    iteration_proxy_value<IteratorType> begin() noexcept
-    {
-        return iteration_proxy_value<IteratorType>(container.begin());
-    }
-
-    /// return iterator end (needed for range-based for)
-    iteration_proxy_value<IteratorType> end() noexcept
-    {
-        return iteration_proxy_value<IteratorType>(container.end());
-    }
-};
-// Structured Bindings Support
-// For further reference see https://blog.tartanllama.xyz/structured-bindings/
-// And see https://github.com/nlohmann/json/pull/1391
-template <std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
-auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
-{
-    return i.key();
-}
-// Structured Bindings Support
-// For further reference see https://blog.tartanllama.xyz/structured-bindings/
-// And see https://github.com/nlohmann/json/pull/1391
-template <std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
-auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
-{
-    return i.value();
-}
-}  // namespace detail
-}  // namespace nlohmann
-
-// The Addition to the STD Namespace is required to add
-// Structured Bindings Support to the iteration_proxy_value class
-// For further reference see https://blog.tartanllama.xyz/structured-bindings/
-// And see https://github.com/nlohmann/json/pull/1391
-namespace std
-{
-template <typename IteratorType>
-class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>
-            : public std::integral_constant<std::size_t, 2> {};
-
-template <std::size_t N, typename IteratorType>
-class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
-{
-  public:
-    using type = decltype(
-                     get<N>(std::declval <
-                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
-};
-}
-
-namespace nlohmann
-{
-namespace detail
-{
-//////////////////
-// constructors //
-//////////////////
-
-template<value_t> struct external_constructor;
-
-template<>
-struct external_constructor<value_t::boolean>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
-    {
-        j.m_type = value_t::boolean;
-        j.m_value = b;
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::string>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
-    {
-        j.m_type = value_t::string;
-        j.m_value = s;
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
-    {
-        j.m_type = value_t::string;
-        j.m_value = std::move(s);
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType, typename CompatibleStringType,
-             enable_if_t<not std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
-                         int> = 0>
-    static void construct(BasicJsonType& j, const CompatibleStringType& str)
-    {
-        j.m_type = value_t::string;
-        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::number_float>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
-    {
-        j.m_type = value_t::number_float;
-        j.m_value = val;
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::number_unsigned>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
-    {
-        j.m_type = value_t::number_unsigned;
-        j.m_value = val;
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::number_integer>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
-    {
-        j.m_type = value_t::number_integer;
-        j.m_value = val;
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::array>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
-    {
-        j.m_type = value_t::array;
-        j.m_value = arr;
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
-    {
-        j.m_type = value_t::array;
-        j.m_value = std::move(arr);
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType, typename CompatibleArrayType,
-             enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
-                         int> = 0>
-    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
-    {
-        using std::begin;
-        using std::end;
-        j.m_type = value_t::array;
-        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, const std::vector<bool>& arr)
-    {
-        j.m_type = value_t::array;
-        j.m_value = value_t::array;
-        j.m_value.array->reserve(arr.size());
-        for (const bool x : arr)
-        {
-            j.m_value.array->push_back(x);
-        }
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType, typename T,
-             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
-    static void construct(BasicJsonType& j, const std::valarray<T>& arr)
-    {
-        j.m_type = value_t::array;
-        j.m_value = value_t::array;
-        j.m_value.array->resize(arr.size());
-        std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
-        j.assert_invariant();
-    }
-};
-
-template<>
-struct external_constructor<value_t::object>
-{
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
-    {
-        j.m_type = value_t::object;
-        j.m_value = obj;
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType>
-    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
-    {
-        j.m_type = value_t::object;
-        j.m_value = std::move(obj);
-        j.assert_invariant();
-    }
-
-    template<typename BasicJsonType, typename CompatibleObjectType,
-             enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0>
-    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
-    {
-        using std::begin;
-        using std::end;
-
-        j.m_type = value_t::object;
-        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
-        j.assert_invariant();
-    }
-};
-
-/////////////
-// to_json //
-/////////////
-
-template<typename BasicJsonType, typename T,
-         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
-void to_json(BasicJsonType& j, T b) noexcept
-{
-    external_constructor<value_t::boolean>::construct(j, b);
-}
-
-template<typename BasicJsonType, typename CompatibleString,
-         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
-void to_json(BasicJsonType& j, const CompatibleString& s)
-{
-    external_constructor<value_t::string>::construct(j, s);
-}
-
-template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
-{
-    external_constructor<value_t::string>::construct(j, std::move(s));
-}
-
-template<typename BasicJsonType, typename FloatType,
-         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
-void to_json(BasicJsonType& j, FloatType val) noexcept
-{
-    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
-}
-
-template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
-         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
-void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
-{
-    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
-}
-
-template<typename BasicJsonType, typename CompatibleNumberIntegerType,
-         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
-void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
-{
-    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
-}
-
-template<typename BasicJsonType, typename EnumType,
-         enable_if_t<std::is_enum<EnumType>::value, int> = 0>
-void to_json(BasicJsonType& j, EnumType e) noexcept
-{
-    using underlying_type = typename std::underlying_type<EnumType>::type;
-    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
-}
-
-template<typename BasicJsonType>
-void to_json(BasicJsonType& j, const std::vector<bool>& e)
-{
-    external_constructor<value_t::array>::construct(j, e);
-}
-
-template <typename BasicJsonType, typename CompatibleArrayType,
-          enable_if_t<is_compatible_array_type<BasicJsonType,
-                      CompatibleArrayType>::value and
-                      not is_compatible_object_type<
-                          BasicJsonType, CompatibleArrayType>::value and
-                      not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and
-                      not is_basic_json<CompatibleArrayType>::value,
-                      int> = 0>
-void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
-{
-    external_constructor<value_t::array>::construct(j, arr);
-}
-
-template<typename BasicJsonType, typename T,
-         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
-void to_json(BasicJsonType& j, const std::valarray<T>& arr)
-{
-    external_constructor<value_t::array>::construct(j, std::move(arr));
-}
-
-template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
-{
-    external_constructor<value_t::array>::construct(j, std::move(arr));
-}
-
-template<typename BasicJsonType, typename CompatibleObjectType,
-         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value and not is_basic_json<CompatibleObjectType>::value, int> = 0>
-void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
-{
-    external_constructor<value_t::object>::construct(j, obj);
-}
-
-template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
-{
-    external_constructor<value_t::object>::construct(j, std::move(obj));
-}
-
-template <
-    typename BasicJsonType, typename T, std::size_t N,
-    enable_if_t<not std::is_constructible<typename BasicJsonType::string_t,
-                const T(&)[N]>::value,
-                int> = 0 >
-void to_json(BasicJsonType& j, const T(&arr)[N])
-{
-    external_constructor<value_t::array>::construct(j, arr);
-}
-
-template<typename BasicJsonType, typename... Args>
-void to_json(BasicJsonType& j, const std::pair<Args...>& p)
-{
-    j = { p.first, p.second };
-}
-
-// for https://github.com/nlohmann/json/pull/1134
-template < typename BasicJsonType, typename T,
-           enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
-void to_json(BasicJsonType& j, const T& b)
-{
-    j = { {b.key(), b.value()} };
-}
-
-template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
-void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
-{
-    j = { std::get<Idx>(t)... };
-}
-
-template<typename BasicJsonType, typename... Args>
-void to_json(BasicJsonType& j, const std::tuple<Args...>& t)
-{
-    to_json_tuple_impl(j, t, index_sequence_for<Args...> {});
-}
-
-struct to_json_fn
-{
-    template<typename BasicJsonType, typename T>
-    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
-    -> decltype(to_json(j, std::forward<T>(val)), void())
-    {
-        return to_json(j, std::forward<T>(val));
-    }
-};
-}  // namespace detail
-
-/// namespace to hold default `to_json` function
-namespace
-{
-constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
-} // namespace
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/input/input_adapters.hpp>
-
-
-#include <cassert> // assert
-#include <cstddef> // size_t
-#include <cstring> // strlen
-#include <istream> // istream
-#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
-#include <memory> // shared_ptr, make_shared, addressof
-#include <numeric> // accumulate
-#include <string> // string, char_traits
-#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
-#include <utility> // pair, declval
-#include <cstdio> //FILE *
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-/// the supported input formats
-enum class input_format_t { json, cbor, msgpack, ubjson, bson };
-
-////////////////////
-// input adapters //
-////////////////////
-
-/*!
-@brief abstract input adapter interface
-
-Produces a stream of std::char_traits<char>::int_type characters from a
-std::istream, a buffer, or some other input type. Accepts the return of
-exactly one non-EOF character for future input. The int_type characters
-returned consist of all valid char values as positive values (typically
-unsigned char), plus an EOF value outside that range, specified by the value
-of the function std::char_traits<char>::eof(). This value is typically -1, but
-could be any arbitrary value which is not a valid char value.
-*/
-struct input_adapter_protocol
-{
-    /// get a character [0,255] or std::char_traits<char>::eof().
-    virtual std::char_traits<char>::int_type get_character() = 0;
-    virtual ~input_adapter_protocol() = default;
-};
-
-/// a type to simplify interfaces
-using input_adapter_t = std::shared_ptr<input_adapter_protocol>;
-
-/*!
-Input adapter for stdio file access. This adapter read only 1 byte and do not use any
- buffer. This adapter is a very low level adapter.
-*/
-class file_input_adapter : public input_adapter_protocol
-{
-  public:
-    explicit file_input_adapter(std::FILE* f)  noexcept
-        : m_file(f)
-    {}
-
-    std::char_traits<char>::int_type get_character() noexcept override
-    {
-        return std::fgetc(m_file);
-    }
-  private:
-    /// the file pointer to read from
-    std::FILE* m_file;
-};
-
-
-/*!
-Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
-beginning of input. Does not support changing the underlying std::streambuf
-in mid-input. Maintains underlying std::istream and std::streambuf to support
-subsequent use of standard std::istream operations to process any input
-characters following those used in parsing the JSON input.  Clears the
-std::istream flags; any input errors (e.g., EOF) will be detected by the first
-subsequent call for input from the std::istream.
-*/
-class input_stream_adapter : public input_adapter_protocol
-{
-  public:
-    ~input_stream_adapter() override
-    {
-        // clear stream flags; we use underlying streambuf I/O, do not
-        // maintain ifstream flags, except eof
-        is.clear(is.rdstate() & std::ios::eofbit);
-    }
-
-    explicit input_stream_adapter(std::istream& i)
-        : is(i), sb(*i.rdbuf())
-    {}
-
-    // delete because of pointer members
-    input_stream_adapter(const input_stream_adapter&) = delete;
-    input_stream_adapter& operator=(input_stream_adapter&) = delete;
-    input_stream_adapter(input_stream_adapter&&) = delete;
-    input_stream_adapter& operator=(input_stream_adapter&&) = delete;
-
-    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
-    // ensure that std::char_traits<char>::eof() and the character 0xFF do not
-    // end up as the same value, eg. 0xFFFFFFFF.
-    std::char_traits<char>::int_type get_character() override
-    {
-        auto res = sb.sbumpc();
-        // set eof manually, as we don't use the istream interface.
-        if (res == EOF)
-        {
-            is.clear(is.rdstate() | std::ios::eofbit);
-        }
-        return res;
-    }
-
-  private:
-    /// the associated input stream
-    std::istream& is;
-    std::streambuf& sb;
-};
-
-/// input adapter for buffer input
-class input_buffer_adapter : public input_adapter_protocol
-{
-  public:
-    input_buffer_adapter(const char* b, const std::size_t l) noexcept
-        : cursor(b), limit(b + l)
-    {}
-
-    // delete because of pointer members
-    input_buffer_adapter(const input_buffer_adapter&) = delete;
-    input_buffer_adapter& operator=(input_buffer_adapter&) = delete;
-    input_buffer_adapter(input_buffer_adapter&&) = delete;
-    input_buffer_adapter& operator=(input_buffer_adapter&&) = delete;
-    ~input_buffer_adapter() override = default;
-
-    std::char_traits<char>::int_type get_character() noexcept override
-    {
-        if (JSON_LIKELY(cursor < limit))
-        {
-            return std::char_traits<char>::to_int_type(*(cursor++));
-        }
-
-        return std::char_traits<char>::eof();
-    }
-
-  private:
-    /// pointer to the current character
-    const char* cursor;
-    /// pointer past the last character
-    const char* const limit;
-};
-
-template<typename WideStringType, size_t T>
-struct wide_string_input_helper
-{
-    // UTF-32
-    static void fill_buffer(const WideStringType& str, size_t& current_wchar, std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled)
-    {
-        utf8_bytes_index = 0;
-
-        if (current_wchar == str.size())
-        {
-            utf8_bytes[0] = std::char_traits<char>::eof();
-            utf8_bytes_filled = 1;
-        }
-        else
-        {
-            // get the current character
-            const auto wc = static_cast<int>(str[current_wchar++]);
-
-            // UTF-32 to UTF-8 encoding
-            if (wc < 0x80)
-            {
-                utf8_bytes[0] = wc;
-                utf8_bytes_filled = 1;
-            }
-            else if (wc <= 0x7FF)
-            {
-                utf8_bytes[0] = 0xC0 | ((wc >> 6) & 0x1F);
-                utf8_bytes[1] = 0x80 | (wc & 0x3F);
-                utf8_bytes_filled = 2;
-            }
-            else if (wc <= 0xFFFF)
-            {
-                utf8_bytes[0] = 0xE0 | ((wc >> 12) & 0x0F);
-                utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F);
-                utf8_bytes[2] = 0x80 | (wc & 0x3F);
-                utf8_bytes_filled = 3;
-            }
-            else if (wc <= 0x10FFFF)
-            {
-                utf8_bytes[0] = 0xF0 | ((wc >> 18) & 0x07);
-                utf8_bytes[1] = 0x80 | ((wc >> 12) & 0x3F);
-                utf8_bytes[2] = 0x80 | ((wc >> 6) & 0x3F);
-                utf8_bytes[3] = 0x80 | (wc & 0x3F);
-                utf8_bytes_filled = 4;
-            }
-            else
-            {
-                // unknown character
-                utf8_bytes[0] = wc;
-                utf8_bytes_filled = 1;
-            }
-        }
-    }
-};
-
-template<typename WideStringType>
-struct wide_string_input_helper<WideStringType, 2>
-{
-    // UTF-16
-    static void fill_buffer(const WideStringType& str, size_t& current_wchar, std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled)
-    {
-        utf8_bytes_index = 0;
-
-        if (current_wchar == str.size())
-        {
-            utf8_bytes[0] = std::char_traits<char>::eof();
-            utf8_bytes_filled = 1;
-        }
-        else
-        {
-            // get the current character
-            const auto wc = static_cast<int>(str[current_wchar++]);
-
-            // UTF-16 to UTF-8 encoding
-            if (wc < 0x80)
-            {
-                utf8_bytes[0] = wc;
-                utf8_bytes_filled = 1;
-            }
-            else if (wc <= 0x7FF)
-            {
-                utf8_bytes[0] = 0xC0 | ((wc >> 6));
-                utf8_bytes[1] = 0x80 | (wc & 0x3F);
-                utf8_bytes_filled = 2;
-            }
-            else if (0xD800 > wc or wc >= 0xE000)
-            {
-                utf8_bytes[0] = 0xE0 | ((wc >> 12));
-                utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F);
-                utf8_bytes[2] = 0x80 | (wc & 0x3F);
-                utf8_bytes_filled = 3;
-            }
-            else
-            {
-                if (current_wchar < str.size())
-                {
-                    const auto wc2 = static_cast<int>(str[current_wchar++]);
-                    const int charcode = 0x10000 + (((wc & 0x3FF) << 10) | (wc2 & 0x3FF));
-                    utf8_bytes[0] = 0xf0 | (charcode >> 18);
-                    utf8_bytes[1] = 0x80 | ((charcode >> 12) & 0x3F);
-                    utf8_bytes[2] = 0x80 | ((charcode >> 6) & 0x3F);
-                    utf8_bytes[3] = 0x80 | (charcode & 0x3F);
-                    utf8_bytes_filled = 4;
-                }
-                else
-                {
-                    // unknown character
-                    ++current_wchar;
-                    utf8_bytes[0] = wc;
-                    utf8_bytes_filled = 1;
-                }
-            }
-        }
-    }
-};
-
-template<typename WideStringType>
-class wide_string_input_adapter : public input_adapter_protocol
-{
-  public:
-    explicit wide_string_input_adapter(const WideStringType& w)  noexcept
-        : str(w)
-    {}
-
-    std::char_traits<char>::int_type get_character() noexcept override
-    {
-        // check if buffer needs to be filled
-        if (utf8_bytes_index == utf8_bytes_filled)
-        {
-            fill_buffer<sizeof(typename WideStringType::value_type)>();
-
-            assert(utf8_bytes_filled > 0);
-            assert(utf8_bytes_index == 0);
-        }
-
-        // use buffer
-        assert(utf8_bytes_filled > 0);
-        assert(utf8_bytes_index < utf8_bytes_filled);
-        return utf8_bytes[utf8_bytes_index++];
-    }
-
-  private:
-    template<size_t T>
-    void fill_buffer()
-    {
-        wide_string_input_helper<WideStringType, T>::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
-    }
-
-    /// the wstring to process
-    const WideStringType& str;
-
-    /// index of the current wchar in str
-    std::size_t current_wchar = 0;
-
-    /// a buffer for UTF-8 bytes
-    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
-
-    /// index to the utf8_codes array for the next valid byte
-    std::size_t utf8_bytes_index = 0;
-    /// number of valid bytes in the utf8_codes array
-    std::size_t utf8_bytes_filled = 0;
-};
-
-class input_adapter
-{
-  public:
-    // native support
-    input_adapter(std::FILE* file)
-        : ia(std::make_shared<file_input_adapter>(file)) {}
-    /// input adapter for input stream
-    input_adapter(std::istream& i)
-        : ia(std::make_shared<input_stream_adapter>(i)) {}
-
-    /// input adapter for input stream
-    input_adapter(std::istream&& i)
-        : ia(std::make_shared<input_stream_adapter>(i)) {}
-
-    input_adapter(const std::wstring& ws)
-        : ia(std::make_shared<wide_string_input_adapter<std::wstring>>(ws)) {}
-
-    input_adapter(const std::u16string& ws)
-        : ia(std::make_shared<wide_string_input_adapter<std::u16string>>(ws)) {}
-
-    input_adapter(const std::u32string& ws)
-        : ia(std::make_shared<wide_string_input_adapter<std::u32string>>(ws)) {}
-
-    /// input adapter for buffer
-    template<typename CharT,
-             typename std::enable_if<
-                 std::is_pointer<CharT>::value and
-                 std::is_integral<typename std::remove_pointer<CharT>::type>::value and
-                 sizeof(typename std::remove_pointer<CharT>::type) == 1,
-                 int>::type = 0>
-    input_adapter(CharT b, std::size_t l)
-        : ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {}
-
-    // derived support
-
-    /// input adapter for string literal
-    template<typename CharT,
-             typename std::enable_if<
-                 std::is_pointer<CharT>::value and
-                 std::is_integral<typename std::remove_pointer<CharT>::type>::value and
-                 sizeof(typename std::remove_pointer<CharT>::type) == 1,
-                 int>::type = 0>
-    input_adapter(CharT b)
-        : input_adapter(reinterpret_cast<const char*>(b),
-                        std::strlen(reinterpret_cast<const char*>(b))) {}
-
-    /// input adapter for iterator range with contiguous storage
-    template<class IteratorType,
-             typename std::enable_if<
-                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
-                 int>::type = 0>
-    input_adapter(IteratorType first, IteratorType last)
-    {
-#ifndef NDEBUG
-        // assertion to check that the iterator range is indeed contiguous,
-        // see http://stackoverflow.com/a/35008842/266378 for more discussion
-        const auto is_contiguous = std::accumulate(
-                                       first, last, std::pair<bool, int>(true, 0),
-                                       [&first](std::pair<bool, int> res, decltype(*first) val)
-        {
-            res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
-            return res;
-        }).first;
-        assert(is_contiguous);
-#endif
-
-        // assertion to check that each element is 1 byte long
-        static_assert(
-            sizeof(typename iterator_traits<IteratorType>::value_type) == 1,
-            "each element in the iterator range must have the size of 1 byte");
-
-        const auto len = static_cast<size_t>(std::distance(first, last));
-        if (JSON_LIKELY(len > 0))
-        {
-            // there is at least one element: use the address of first
-            ia = std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(&(*first)), len);
-        }
-        else
-        {
-            // the address of first cannot be used: use nullptr
-            ia = std::make_shared<input_buffer_adapter>(nullptr, len);
-        }
-    }
-
-    /// input adapter for array
-    template<class T, std::size_t N>
-    input_adapter(T (&array)[N])
-        : input_adapter(std::begin(array), std::end(array)) {}
-
-    /// input adapter for contiguous container
-    template<class ContiguousContainer, typename
-             std::enable_if<not std::is_pointer<ContiguousContainer>::value and
-                            std::is_base_of<std::random_access_iterator_tag, typename iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
-                            int>::type = 0>
-    input_adapter(const ContiguousContainer& c)
-        : input_adapter(std::begin(c), std::end(c)) {}
-
-    operator input_adapter_t()
-    {
-        return ia;
-    }
-
-  private:
-    /// the actual adapter
-    input_adapter_t ia = nullptr;
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/input/lexer.hpp>
-
-
-#include <clocale> // localeconv
-#include <cstddef> // size_t
-#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
-#include <cstdio> // snprintf
-#include <initializer_list> // initializer_list
-#include <string> // char_traits, string
-#include <vector> // vector
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-// #include <nlohmann/detail/input/input_adapters.hpp>
-
-// #include <nlohmann/detail/input/position_t.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-///////////
-// lexer //
-///////////
-
-/*!
-@brief lexical analysis
-
-This class organizes the lexical analysis during JSON deserialization.
-*/
-template<typename BasicJsonType>
-class lexer
-{
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    using number_float_t = typename BasicJsonType::number_float_t;
-    using string_t = typename BasicJsonType::string_t;
-
-  public:
-    /// token types for the parser
-    enum class token_type
-    {
-        uninitialized,    ///< indicating the scanner is uninitialized
-        literal_true,     ///< the `true` literal
-        literal_false,    ///< the `false` literal
-        literal_null,     ///< the `null` literal
-        value_string,     ///< a string -- use get_string() for actual value
-        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value
-        value_integer,    ///< a signed integer -- use get_number_integer() for actual value
-        value_float,      ///< an floating point number -- use get_number_float() for actual value
-        begin_array,      ///< the character for array begin `[`
-        begin_object,     ///< the character for object begin `{`
-        end_array,        ///< the character for array end `]`
-        end_object,       ///< the character for object end `}`
-        name_separator,   ///< the name separator `:`
-        value_separator,  ///< the value separator `,`
-        parse_error,      ///< indicating a parse error
-        end_of_input,     ///< indicating the end of the input buffer
-        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)
-    };
-
-    /// return name of values of type token_type (only used for errors)
-    static const char* token_type_name(const token_type t) noexcept
-    {
-        switch (t)
-        {
-            case token_type::uninitialized:
-                return "<uninitialized>";
-            case token_type::literal_true:
-                return "true literal";
-            case token_type::literal_false:
-                return "false literal";
-            case token_type::literal_null:
-                return "null literal";
-            case token_type::value_string:
-                return "string literal";
-            case lexer::token_type::value_unsigned:
-            case lexer::token_type::value_integer:
-            case lexer::token_type::value_float:
-                return "number literal";
-            case token_type::begin_array:
-                return "'['";
-            case token_type::begin_object:
-                return "'{'";
-            case token_type::end_array:
-                return "']'";
-            case token_type::end_object:
-                return "'}'";
-            case token_type::name_separator:
-                return "':'";
-            case token_type::value_separator:
-                return "','";
-            case token_type::parse_error:
-                return "<parse error>";
-            case token_type::end_of_input:
-                return "end of input";
-            case token_type::literal_or_value:
-                return "'[', '{', or a literal";
-            // LCOV_EXCL_START
-            default: // catch non-enum values
-                return "unknown token";
-                // LCOV_EXCL_STOP
-        }
-    }
-
-    explicit lexer(detail::input_adapter_t&& adapter)
-        : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {}
-
-    // delete because of pointer members
-    lexer(const lexer&) = delete;
-    lexer(lexer&&) = delete;
-    lexer& operator=(lexer&) = delete;
-    lexer& operator=(lexer&&) = delete;
-    ~lexer() = default;
-
-  private:
-    /////////////////////
-    // locales
-    /////////////////////
-
-    /// return the locale-dependent decimal point
-    static char get_decimal_point() noexcept
-    {
-        const auto loc = localeconv();
-        assert(loc != nullptr);
-        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
-    }
-
-    /////////////////////
-    // scan functions
-    /////////////////////
-
-    /*!
-    @brief get codepoint from 4 hex characters following `\u`
-
-    For input "\u c1 c2 c3 c4" the codepoint is:
-      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
-    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
-
-    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
-    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
-    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
-    between the ASCII value of the character and the desired integer value.
-
-    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
-            non-hex character)
-    */
-    int get_codepoint()
-    {
-        // this function only makes sense after reading `\u`
-        assert(current == 'u');
-        int codepoint = 0;
-
-        const auto factors = { 12, 8, 4, 0 };
-        for (const auto factor : factors)
-        {
-            get();
-
-            if (current >= '0' and current <= '9')
-            {
-                codepoint += ((current - 0x30) << factor);
-            }
-            else if (current >= 'A' and current <= 'F')
-            {
-                codepoint += ((current - 0x37) << factor);
-            }
-            else if (current >= 'a' and current <= 'f')
-            {
-                codepoint += ((current - 0x57) << factor);
-            }
-            else
-            {
-                return -1;
-            }
-        }
-
-        assert(0x0000 <= codepoint and codepoint <= 0xFFFF);
-        return codepoint;
-    }
-
-    /*!
-    @brief check if the next byte(s) are inside a given range
-
-    Adds the current byte and, for each passed range, reads a new byte and
-    checks if it is inside the range. If a violation was detected, set up an
-    error message and return false. Otherwise, return true.
-
-    @param[in] ranges  list of integers; interpreted as list of pairs of
-                       inclusive lower and upper bound, respectively
-
-    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
-         1, 2, or 3 pairs. This precondition is enforced by an assertion.
-
-    @return true if and only if no range violation was detected
-    */
-    bool next_byte_in_range(std::initializer_list<int> ranges)
-    {
-        assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
-        add(current);
-
-        for (auto range = ranges.begin(); range != ranges.end(); ++range)
-        {
-            get();
-            if (JSON_LIKELY(*range <= current and current <= *(++range)))
-            {
-                add(current);
-            }
-            else
-            {
-                error_message = "invalid string: ill-formed UTF-8 byte";
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /*!
-    @brief scan a string literal
-
-    This function scans a string according to Sect. 7 of RFC 7159. While
-    scanning, bytes are escaped and copied into buffer token_buffer. Then the
-    function returns successfully, token_buffer is *not* null-terminated (as it
-    may contain \0 bytes), and token_buffer.size() is the number of bytes in the
-    string.
-
-    @return token_type::value_string if string could be successfully scanned,
-            token_type::parse_error otherwise
-
-    @note In case of errors, variable error_message contains a textual
-          description.
-    */
-    token_type scan_string()
-    {
-        // reset token_buffer (ignore opening quote)
-        reset();
-
-        // we entered the function by reading an open quote
-        assert(current == '\"');
-
-        while (true)
-        {
-            // get next character
-            switch (get())
-            {
-                // end of file while parsing string
-                case std::char_traits<char>::eof():
-                {
-                    error_message = "invalid string: missing closing quote";
-                    return token_type::parse_error;
-                }
-
-                // closing quote
-                case '\"':
-                {
-                    return token_type::value_string;
-                }
-
-                // escapes
-                case '\\':
-                {
-                    switch (get())
-                    {
-                        // quotation mark
-                        case '\"':
-                            add('\"');
-                            break;
-                        // reverse solidus
-                        case '\\':
-                            add('\\');
-                            break;
-                        // solidus
-                        case '/':
-                            add('/');
-                            break;
-                        // backspace
-                        case 'b':
-                            add('\b');
-                            break;
-                        // form feed
-                        case 'f':
-                            add('\f');
-                            break;
-                        // line feed
-                        case 'n':
-                            add('\n');
-                            break;
-                        // carriage return
-                        case 'r':
-                            add('\r');
-                            break;
-                        // tab
-                        case 't':
-                            add('\t');
-                            break;
-
-                        // unicode escapes
-                        case 'u':
-                        {
-                            const int codepoint1 = get_codepoint();
-                            int codepoint = codepoint1; // start with codepoint1
-
-                            if (JSON_UNLIKELY(codepoint1 == -1))
-                            {
-                                error_message = "invalid string: '\\u' must be followed by 4 hex digits";
-                                return token_type::parse_error;
-                            }
-
-                            // check if code point is a high surrogate
-                            if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF)
-                            {
-                                // expect next \uxxxx entry
-                                if (JSON_LIKELY(get() == '\\' and get() == 'u'))
-                                {
-                                    const int codepoint2 = get_codepoint();
-
-                                    if (JSON_UNLIKELY(codepoint2 == -1))
-                                    {
-                                        error_message = "invalid string: '\\u' must be followed by 4 hex digits";
-                                        return token_type::parse_error;
-                                    }
-
-                                    // check if codepoint2 is a low surrogate
-                                    if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF))
-                                    {
-                                        // overwrite codepoint
-                                        codepoint =
-                                            // high surrogate occupies the most significant 22 bits
-                                            (codepoint1 << 10)
-                                            // low surrogate occupies the least significant 15 bits
-                                            + codepoint2
-                                            // there is still the 0xD800, 0xDC00 and 0x10000 noise
-                                            // in the result so we have to subtract with:
-                                            // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
-                                            - 0x35FDC00;
-                                    }
-                                    else
-                                    {
-                                        error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
-                                        return token_type::parse_error;
-                                    }
-                                }
-                                else
-                                {
-                                    error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
-                                    return token_type::parse_error;
-                                }
-                            }
-                            else
-                            {
-                                if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF))
-                                {
-                                    error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
-                                    return token_type::parse_error;
-                                }
-                            }
-
-                            // result of the above calculation yields a proper codepoint
-                            assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
-
-                            // translate codepoint into bytes
-                            if (codepoint < 0x80)
-                            {
-                                // 1-byte characters: 0xxxxxxx (ASCII)
-                                add(codepoint);
-                            }
-                            else if (codepoint <= 0x7FF)
-                            {
-                                // 2-byte characters: 110xxxxx 10xxxxxx
-                                add(0xC0 | (codepoint >> 6));
-                                add(0x80 | (codepoint & 0x3F));
-                            }
-                            else if (codepoint <= 0xFFFF)
-                            {
-                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
-                                add(0xE0 | (codepoint >> 12));
-                                add(0x80 | ((codepoint >> 6) & 0x3F));
-                                add(0x80 | (codepoint & 0x3F));
-                            }
-                            else
-                            {
-                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
-                                add(0xF0 | (codepoint >> 18));
-                                add(0x80 | ((codepoint >> 12) & 0x3F));
-                                add(0x80 | ((codepoint >> 6) & 0x3F));
-                                add(0x80 | (codepoint & 0x3F));
-                            }
-
-                            break;
-                        }
-
-                        // other characters after escape
-                        default:
-                            error_message = "invalid string: forbidden character after backslash";
-                            return token_type::parse_error;
-                    }
-
-                    break;
-                }
-
-                // invalid control characters
-                case 0x00:
-                {
-                    error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
-                    return token_type::parse_error;
-                }
-
-                case 0x01:
-                {
-                    error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
-                    return token_type::parse_error;
-                }
-
-                case 0x02:
-                {
-                    error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
-                    return token_type::parse_error;
-                }
-
-                case 0x03:
-                {
-                    error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
-                    return token_type::parse_error;
-                }
-
-                case 0x04:
-                {
-                    error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
-                    return token_type::parse_error;
-                }
-
-                case 0x05:
-                {
-                    error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
-                    return token_type::parse_error;
-                }
-
-                case 0x06:
-                {
-                    error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
-                    return token_type::parse_error;
-                }
-
-                case 0x07:
-                {
-                    error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
-                    return token_type::parse_error;
-                }
-
-                case 0x08:
-                {
-                    error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
-                    return token_type::parse_error;
-                }
-
-                case 0x09:
-                {
-                    error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
-                    return token_type::parse_error;
-                }
-
-                case 0x0A:
-                {
-                    error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
-                    return token_type::parse_error;
-                }
-
-                case 0x0B:
-                {
-                    error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
-                    return token_type::parse_error;
-                }
-
-                case 0x0C:
-                {
-                    error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
-                    return token_type::parse_error;
-                }
-
-                case 0x0D:
-                {
-                    error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
-                    return token_type::parse_error;
-                }
-
-                case 0x0E:
-                {
-                    error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
-                    return token_type::parse_error;
-                }
-
-                case 0x0F:
-                {
-                    error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
-                    return token_type::parse_error;
-                }
-
-                case 0x10:
-                {
-                    error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
-                    return token_type::parse_error;
-                }
-
-                case 0x11:
-                {
-                    error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
-                    return token_type::parse_error;
-                }
-
-                case 0x12:
-                {
-                    error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
-                    return token_type::parse_error;
-                }
-
-                case 0x13:
-                {
-                    error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
-                    return token_type::parse_error;
-                }
-
-                case 0x14:
-                {
-                    error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
-                    return token_type::parse_error;
-                }
-
-                case 0x15:
-                {
-                    error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
-                    return token_type::parse_error;
-                }
-
-                case 0x16:
-                {
-                    error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
-                    return token_type::parse_error;
-                }
-
-                case 0x17:
-                {
-                    error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
-                    return token_type::parse_error;
-                }
-
-                case 0x18:
-                {
-                    error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
-                    return token_type::parse_error;
-                }
-
-                case 0x19:
-                {
-                    error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
-                    return token_type::parse_error;
-                }
-
-                case 0x1A:
-                {
-                    error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
-                    return token_type::parse_error;
-                }
-
-                case 0x1B:
-                {
-                    error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
-                    return token_type::parse_error;
-                }
-
-                case 0x1C:
-                {
-                    error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
-                    return token_type::parse_error;
-                }
-
-                case 0x1D:
-                {
-                    error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
-                    return token_type::parse_error;
-                }
-
-                case 0x1E:
-                {
-                    error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
-                    return token_type::parse_error;
-                }
-
-                case 0x1F:
-                {
-                    error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
-                    return token_type::parse_error;
-                }
-
-                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
-                case 0x20:
-                case 0x21:
-                case 0x23:
-                case 0x24:
-                case 0x25:
-                case 0x26:
-                case 0x27:
-                case 0x28:
-                case 0x29:
-                case 0x2A:
-                case 0x2B:
-                case 0x2C:
-                case 0x2D:
-                case 0x2E:
-                case 0x2F:
-                case 0x30:
-                case 0x31:
-                case 0x32:
-                case 0x33:
-                case 0x34:
-                case 0x35:
-                case 0x36:
-                case 0x37:
-                case 0x38:
-                case 0x39:
-                case 0x3A:
-                case 0x3B:
-                case 0x3C:
-                case 0x3D:
-                case 0x3E:
-                case 0x3F:
-                case 0x40:
-                case 0x41:
-                case 0x42:
-                case 0x43:
-                case 0x44:
-                case 0x45:
-                case 0x46:
-                case 0x47:
-                case 0x48:
-                case 0x49:
-                case 0x4A:
-                case 0x4B:
-                case 0x4C:
-                case 0x4D:
-                case 0x4E:
-                case 0x4F:
-                case 0x50:
-                case 0x51:
-                case 0x52:
-                case 0x53:
-                case 0x54:
-                case 0x55:
-                case 0x56:
-                case 0x57:
-                case 0x58:
-                case 0x59:
-                case 0x5A:
-                case 0x5B:
-                case 0x5D:
-                case 0x5E:
-                case 0x5F:
-                case 0x60:
-                case 0x61:
-                case 0x62:
-                case 0x63:
-                case 0x64:
-                case 0x65:
-                case 0x66:
-                case 0x67:
-                case 0x68:
-                case 0x69:
-                case 0x6A:
-                case 0x6B:
-                case 0x6C:
-                case 0x6D:
-                case 0x6E:
-                case 0x6F:
-                case 0x70:
-                case 0x71:
-                case 0x72:
-                case 0x73:
-                case 0x74:
-                case 0x75:
-                case 0x76:
-                case 0x77:
-                case 0x78:
-                case 0x79:
-                case 0x7A:
-                case 0x7B:
-                case 0x7C:
-                case 0x7D:
-                case 0x7E:
-                case 0x7F:
-                {
-                    add(current);
-                    break;
-                }
-
-                // U+0080..U+07FF: bytes C2..DF 80..BF
-                case 0xC2:
-                case 0xC3:
-                case 0xC4:
-                case 0xC5:
-                case 0xC6:
-                case 0xC7:
-                case 0xC8:
-                case 0xC9:
-                case 0xCA:
-                case 0xCB:
-                case 0xCC:
-                case 0xCD:
-                case 0xCE:
-                case 0xCF:
-                case 0xD0:
-                case 0xD1:
-                case 0xD2:
-                case 0xD3:
-                case 0xD4:
-                case 0xD5:
-                case 0xD6:
-                case 0xD7:
-                case 0xD8:
-                case 0xD9:
-                case 0xDA:
-                case 0xDB:
-                case 0xDC:
-                case 0xDD:
-                case 0xDE:
-                case 0xDF:
-                {
-                    if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF})))
-                    {
-                        return token_type::parse_error;
-                    }
-                    break;
-                }
-
-                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
-                case 0xE0:
-                {
-                    if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
-                    {
-                        return token_type::parse_error;
-                    }
-                    break;
-                }
-
-                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
-                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
-                case 0xE1:
-                case 0xE2:
-                case 0xE3:
-                case 0xE4:
-                case 0xE5:
-                case 0xE6:
-                case 0xE7:
-                case 0xE8:
-                case 0xE9:
-                case 0xEA:
-                case 0xEB:
-                case 0xEC:
-                case 0xEE:
-                case 0xEF:
-                {
-                    if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
-                    {
-                        return token_type::parse_error;
-                    }
-                    break;
-                }
-
-                // U+D000..U+D7FF: bytes ED 80..9F 80..BF
-                case 0xED:
-                {
-                    if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
-                    {
-                        return token_type::parse_error;
-                    }
-                    break;
-                }
-
-                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
-                case 0xF0:
-                {
-                    if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
-                    {
-                        return token_type::parse_error;
-                    }
-                    break;
-                }
-
-                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
-                case 0xF1:
-                case 0xF2:
-                case 0xF3:
-                {
-                    if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
-                    {
-                        return token_type::parse_error;
-                    }
-                    break;
-                }
-
-                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
-                case 0xF4:
-                {
-                    if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
-                    {
-                        return token_type::parse_error;
-                    }
-                    break;
-                }
-
-                // remaining bytes (80..C1 and F5..FF) are ill-formed
-                default:
-                {
-                    error_message = "invalid string: ill-formed UTF-8 byte";
-                    return token_type::parse_error;
-                }
-            }
-        }
-    }
-
-    static void strtof(float& f, const char* str, char** endptr) noexcept
-    {
-        f = std::strtof(str, endptr);
-    }
-
-    static void strtof(double& f, const char* str, char** endptr) noexcept
-    {
-        f = std::strtod(str, endptr);
-    }
-
-    static void strtof(long double& f, const char* str, char** endptr) noexcept
-    {
-        f = std::strtold(str, endptr);
-    }
-
-    /*!
-    @brief scan a number literal
-
-    This function scans a string according to Sect. 6 of RFC 7159.
-
-    The function is realized with a deterministic finite state machine derived
-    from the grammar described in RFC 7159. Starting in state "init", the
-    input is read and used to determined the next state. Only state "done"
-    accepts the number. State "error" is a trap state to model errors. In the
-    table below, "anything" means any character but the ones listed before.
-
-    state    | 0        | 1-9      | e E      | +       | -       | .        | anything
-    ---------|----------|----------|----------|---------|---------|----------|-----------
-    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]
-    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]
-    zero     | done     | done     | exponent | done    | done    | decimal1 | done
-    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done
-    decimal1 | decimal2 | [error]  | [error]  | [error] | [error] | [error]  | [error]
-    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done
-    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]
-    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]
-    any2     | any2     | any2     | done     | done    | done    | done     | done
-
-    The state machine is realized with one label per state (prefixed with
-    "scan_number_") and `goto` statements between them. The state machine
-    contains cycles, but any cycle can be left when EOF is read. Therefore,
-    the function is guaranteed to terminate.
-
-    During scanning, the read bytes are stored in token_buffer. This string is
-    then converted to a signed integer, an unsigned integer, or a
-    floating-point number.
-
-    @return token_type::value_unsigned, token_type::value_integer, or
-            token_type::value_float if number could be successfully scanned,
-            token_type::parse_error otherwise
-
-    @note The scanner is independent of the current locale. Internally, the
-          locale's decimal point is used instead of `.` to work with the
-          locale-dependent converters.
-    */
-    token_type scan_number()  // lgtm [cpp/use-of-goto]
-    {
-        // reset token_buffer to store the number's bytes
-        reset();
-
-        // the type of the parsed number; initially set to unsigned; will be
-        // changed if minus sign, decimal point or exponent is read
-        token_type number_type = token_type::value_unsigned;
-
-        // state (init): we just found out we need to scan a number
-        switch (current)
-        {
-            case '-':
-            {
-                add(current);
-                goto scan_number_minus;
-            }
-
-            case '0':
-            {
-                add(current);
-                goto scan_number_zero;
-            }
-
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-            {
-                add(current);
-                goto scan_number_any1;
-            }
-
-            // LCOV_EXCL_START
-            default:
-            {
-                // all other characters are rejected outside scan_number()
-                assert(false);
-            }
-                // LCOV_EXCL_STOP
-        }
-
-scan_number_minus:
-        // state: we just parsed a leading minus sign
-        number_type = token_type::value_integer;
-        switch (get())
-        {
-            case '0':
-            {
-                add(current);
-                goto scan_number_zero;
-            }
-
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-            {
-                add(current);
-                goto scan_number_any1;
-            }
-
-            default:
-            {
-                error_message = "invalid number; expected digit after '-'";
-                return token_type::parse_error;
-            }
-        }
-
-scan_number_zero:
-        // state: we just parse a zero (maybe with a leading minus sign)
-        switch (get())
-        {
-            case '.':
-            {
-                add(decimal_point_char);
-                goto scan_number_decimal1;
-            }
-
-            case 'e':
-            case 'E':
-            {
-                add(current);
-                goto scan_number_exponent;
-            }
-
-            default:
-                goto scan_number_done;
-        }
-
-scan_number_any1:
-        // state: we just parsed a number 0-9 (maybe with a leading minus sign)
-        switch (get())
-        {
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-            {
-                add(current);
-                goto scan_number_any1;
-            }
-
-            case '.':
-            {
-                add(decimal_point_char);
-                goto scan_number_decimal1;
-            }
-
-            case 'e':
-            case 'E':
-            {
-                add(current);
-                goto scan_number_exponent;
-            }
-
-            default:
-                goto scan_number_done;
-        }
-
-scan_number_decimal1:
-        // state: we just parsed a decimal point
-        number_type = token_type::value_float;
-        switch (get())
-        {
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-            {
-                add(current);
-                goto scan_number_decimal2;
-            }
-
-            default:
-            {
-                error_message = "invalid number; expected digit after '.'";
-                return token_type::parse_error;
-            }
-        }
-
-scan_number_decimal2:
-        // we just parsed at least one number after a decimal point
-        switch (get())
-        {
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-            {
-                add(current);
-                goto scan_number_decimal2;
-            }
-
-            case 'e':
-            case 'E':
-            {
-                add(current);
-                goto scan_number_exponent;
-            }
-
-            default:
-                goto scan_number_done;
-        }
-
-scan_number_exponent:
-        // we just parsed an exponent
-        number_type = token_type::value_float;
-        switch (get())
-        {
-            case '+':
-            case '-':
-            {
-                add(current);
-                goto scan_number_sign;
-            }
-
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-            {
-                add(current);
-                goto scan_number_any2;
-            }
-
-            default:
-            {
-                error_message =
-                    "invalid number; expected '+', '-', or digit after exponent";
-                return token_type::parse_error;
-            }
-        }
-
-scan_number_sign:
-        // we just parsed an exponent sign
-        switch (get())
-        {
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-            {
-                add(current);
-                goto scan_number_any2;
-            }
-
-            default:
-            {
-                error_message = "invalid number; expected digit after exponent sign";
-                return token_type::parse_error;
-            }
-        }
-
-scan_number_any2:
-        // we just parsed a number after the exponent or exponent sign
-        switch (get())
-        {
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-            {
-                add(current);
-                goto scan_number_any2;
-            }
-
-            default:
-                goto scan_number_done;
-        }
-
-scan_number_done:
-        // unget the character after the number (we only read it to know that
-        // we are done scanning a number)
-        unget();
-
-        char* endptr = nullptr;
-        errno = 0;
-
-        // try to parse integers first and fall back to floats
-        if (number_type == token_type::value_unsigned)
-        {
-            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
-
-            // we checked the number format before
-            assert(endptr == token_buffer.data() + token_buffer.size());
-
-            if (errno == 0)
-            {
-                value_unsigned = static_cast<number_unsigned_t>(x);
-                if (value_unsigned == x)
-                {
-                    return token_type::value_unsigned;
-                }
-            }
-        }
-        else if (number_type == token_type::value_integer)
-        {
-            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
-
-            // we checked the number format before
-            assert(endptr == token_buffer.data() + token_buffer.size());
-
-            if (errno == 0)
-            {
-                value_integer = static_cast<number_integer_t>(x);
-                if (value_integer == x)
-                {
-                    return token_type::value_integer;
-                }
-            }
-        }
-
-        // this code is reached if we parse a floating-point number or if an
-        // integer conversion above failed
-        strtof(value_float, token_buffer.data(), &endptr);
-
-        // we checked the number format before
-        assert(endptr == token_buffer.data() + token_buffer.size());
-
-        return token_type::value_float;
-    }
-
-    /*!
-    @param[in] literal_text  the literal text to expect
-    @param[in] length        the length of the passed literal text
-    @param[in] return_type   the token type to return on success
-    */
-    token_type scan_literal(const char* literal_text, const std::size_t length,
-                            token_type return_type)
-    {
-        assert(current == literal_text[0]);
-        for (std::size_t i = 1; i < length; ++i)
-        {
-            if (JSON_UNLIKELY(get() != literal_text[i]))
-            {
-                error_message = "invalid literal";
-                return token_type::parse_error;
-            }
-        }
-        return return_type;
-    }
-
-    /////////////////////
-    // input management
-    /////////////////////
-
-    /// reset token_buffer; current character is beginning of token
-    void reset() noexcept
-    {
-        token_buffer.clear();
-        token_string.clear();
-        token_string.push_back(std::char_traits<char>::to_char_type(current));
-    }
-
-    /*
-    @brief get next character from the input
-
-    This function provides the interface to the used input adapter. It does
-    not throw in case the input reached EOF, but returns a
-    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters
-    for use in error messages.
-
-    @return character read from the input
-    */
-    std::char_traits<char>::int_type get()
-    {
-        ++position.chars_read_total;
-        ++position.chars_read_current_line;
-
-        if (next_unget)
-        {
-            // just reset the next_unget variable and work with current
-            next_unget = false;
-        }
-        else
-        {
-            current = ia->get_character();
-        }
-
-        if (JSON_LIKELY(current != std::char_traits<char>::eof()))
-        {
-            token_string.push_back(std::char_traits<char>::to_char_type(current));
-        }
-
-        if (current == '\n')
-        {
-            ++position.lines_read;
-            ++position.chars_read_current_line = 0;
-        }
-
-        return current;
-    }
-
-    /*!
-    @brief unget current character (read it again on next get)
-
-    We implement unget by setting variable next_unget to true. The input is not
-    changed - we just simulate ungetting by modifying chars_read_total,
-    chars_read_current_line, and token_string. The next call to get() will
-    behave as if the unget character is read again.
-    */
-    void unget()
-    {
-        next_unget = true;
-
-        --position.chars_read_total;
-
-        // in case we "unget" a newline, we have to also decrement the lines_read
-        if (position.chars_read_current_line == 0)
-        {
-            if (position.lines_read > 0)
-            {
-                --position.lines_read;
-            }
-        }
-        else
-        {
-            --position.chars_read_current_line;
-        }
-
-        if (JSON_LIKELY(current != std::char_traits<char>::eof()))
-        {
-            assert(token_string.size() != 0);
-            token_string.pop_back();
-        }
-    }
-
-    /// add a character to token_buffer
-    void add(int c)
-    {
-        token_buffer.push_back(std::char_traits<char>::to_char_type(c));
-    }
-
-  public:
-    /////////////////////
-    // value getters
-    /////////////////////
-
-    /// return integer value
-    constexpr number_integer_t get_number_integer() const noexcept
-    {
-        return value_integer;
-    }
-
-    /// return unsigned integer value
-    constexpr number_unsigned_t get_number_unsigned() const noexcept
-    {
-        return value_unsigned;
-    }
-
-    /// return floating-point value
-    constexpr number_float_t get_number_float() const noexcept
-    {
-        return value_float;
-    }
-
-    /// return current string value (implicitly resets the token; useful only once)
-    string_t& get_string()
-    {
-        return token_buffer;
-    }
-
-    /////////////////////
-    // diagnostics
-    /////////////////////
-
-    /// return position of last read token
-    constexpr position_t get_position() const noexcept
-    {
-        return position;
-    }
-
-    /// return the last read token (for errors only).  Will never contain EOF
-    /// (an arbitrary value that is not a valid char value, often -1), because
-    /// 255 may legitimately occur.  May contain NUL, which should be escaped.
-    std::string get_token_string() const
-    {
-        // escape control characters
-        std::string result;
-        for (const auto c : token_string)
-        {
-            if ('\x00' <= c and c <= '\x1F')
-            {
-                // escape control characters
-                char cs[9];
-                (std::snprintf)(cs, 9, "<U+%.4X>", static_cast<unsigned char>(c));
-                result += cs;
-            }
-            else
-            {
-                // add character as is
-                result.push_back(c);
-            }
-        }
-
-        return result;
-    }
-
-    /// return syntax error message
-    constexpr const char* get_error_message() const noexcept
-    {
-        return error_message;
-    }
-
-    /////////////////////
-    // actual scanner
-    /////////////////////
-
-    /*!
-    @brief skip the UTF-8 byte order mark
-    @return true iff there is no BOM or the correct BOM has been skipped
-    */
-    bool skip_bom()
-    {
-        if (get() == 0xEF)
-        {
-            // check if we completely parse the BOM
-            return get() == 0xBB and get() == 0xBF;
-        }
-
-        // the first character is not the beginning of the BOM; unget it to
-        // process is later
-        unget();
-        return true;
-    }
-
-    token_type scan()
-    {
-        // initially, skip the BOM
-        if (position.chars_read_total == 0 and not skip_bom())
-        {
-            error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given";
-            return token_type::parse_error;
-        }
-
-        // read next character and ignore whitespace
-        do
-        {
-            get();
-        }
-        while (current == ' ' or current == '\t' or current == '\n' or current == '\r');
-
-        switch (current)
-        {
-            // structural characters
-            case '[':
-                return token_type::begin_array;
-            case ']':
-                return token_type::end_array;
-            case '{':
-                return token_type::begin_object;
-            case '}':
-                return token_type::end_object;
-            case ':':
-                return token_type::name_separator;
-            case ',':
-                return token_type::value_separator;
-
-            // literals
-            case 't':
-                return scan_literal("true", 4, token_type::literal_true);
-            case 'f':
-                return scan_literal("false", 5, token_type::literal_false);
-            case 'n':
-                return scan_literal("null", 4, token_type::literal_null);
-
-            // string
-            case '\"':
-                return scan_string();
-
-            // number
-            case '-':
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-                return scan_number();
-
-            // end of input (the null byte is needed when parsing from
-            // string literals)
-            case '\0':
-            case std::char_traits<char>::eof():
-                return token_type::end_of_input;
-
-            // error
-            default:
-                error_message = "invalid literal";
-                return token_type::parse_error;
-        }
-    }
-
-  private:
-    /// input adapter
-    detail::input_adapter_t ia = nullptr;
-
-    /// the current character
-    std::char_traits<char>::int_type current = std::char_traits<char>::eof();
-
-    /// whether the next get() call should just return current
-    bool next_unget = false;
-
-    /// the start position of the current token
-    position_t position;
-
-    /// raw input token string (for error messages)
-    std::vector<char> token_string {};
-
-    /// buffer for variable-length tokens (numbers, strings)
-    string_t token_buffer {};
-
-    /// a description of occurred lexer errors
-    const char* error_message = "";
-
-    // number values
-    number_integer_t value_integer = 0;
-    number_unsigned_t value_unsigned = 0;
-    number_float_t value_float = 0;
-
-    /// the decimal point
-    const char decimal_point_char = '.';
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/input/parser.hpp>
-
-
-#include <cassert> // assert
-#include <cmath> // isfinite
-#include <cstdint> // uint8_t
-#include <functional> // function
-#include <string> // string
-#include <utility> // move
-
-// #include <nlohmann/detail/exceptions.hpp>
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-// #include <nlohmann/detail/meta/is_sax.hpp>
-
-
-#include <cstdint> // size_t
-#include <utility> // declval
-
-// #include <nlohmann/detail/meta/detected.hpp>
-
-// #include <nlohmann/detail/meta/type_traits.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-template <typename T>
-using null_function_t = decltype(std::declval<T&>().null());
-
-template <typename T>
-using boolean_function_t =
-    decltype(std::declval<T&>().boolean(std::declval<bool>()));
-
-template <typename T, typename Integer>
-using number_integer_function_t =
-    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
-
-template <typename T, typename Unsigned>
-using number_unsigned_function_t =
-    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));
-
-template <typename T, typename Float, typename String>
-using number_float_function_t = decltype(std::declval<T&>().number_float(
-                                    std::declval<Float>(), std::declval<const String&>()));
-
-template <typename T, typename String>
-using string_function_t =
-    decltype(std::declval<T&>().string(std::declval<String&>()));
-
-template <typename T>
-using start_object_function_t =
-    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));
-
-template <typename T, typename String>
-using key_function_t =
-    decltype(std::declval<T&>().key(std::declval<String&>()));
-
-template <typename T>
-using end_object_function_t = decltype(std::declval<T&>().end_object());
-
-template <typename T>
-using start_array_function_t =
-    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
-
-template <typename T>
-using end_array_function_t = decltype(std::declval<T&>().end_array());
-
-template <typename T, typename Exception>
-using parse_error_function_t = decltype(std::declval<T&>().parse_error(
-        std::declval<std::size_t>(), std::declval<const std::string&>(),
-        std::declval<const Exception&>()));
-
-template <typename SAX, typename BasicJsonType>
-struct is_sax
-{
-  private:
-    static_assert(is_basic_json<BasicJsonType>::value,
-                  "BasicJsonType must be of type basic_json<...>");
-
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    using number_float_t = typename BasicJsonType::number_float_t;
-    using string_t = typename BasicJsonType::string_t;
-    using exception_t = typename BasicJsonType::exception;
-
-  public:
-    static constexpr bool value =
-        is_detected_exact<bool, null_function_t, SAX>::value &&
-        is_detected_exact<bool, boolean_function_t, SAX>::value &&
-        is_detected_exact<bool, number_integer_function_t, SAX,
-        number_integer_t>::value &&
-        is_detected_exact<bool, number_unsigned_function_t, SAX,
-        number_unsigned_t>::value &&
-        is_detected_exact<bool, number_float_function_t, SAX, number_float_t,
-        string_t>::value &&
-        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
-        is_detected_exact<bool, start_object_function_t, SAX>::value &&
-        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
-        is_detected_exact<bool, end_object_function_t, SAX>::value &&
-        is_detected_exact<bool, start_array_function_t, SAX>::value &&
-        is_detected_exact<bool, end_array_function_t, SAX>::value &&
-        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
-};
-
-template <typename SAX, typename BasicJsonType>
-struct is_sax_static_asserts
-{
-  private:
-    static_assert(is_basic_json<BasicJsonType>::value,
-                  "BasicJsonType must be of type basic_json<...>");
-
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    using number_float_t = typename BasicJsonType::number_float_t;
-    using string_t = typename BasicJsonType::string_t;
-    using exception_t = typename BasicJsonType::exception;
-
-  public:
-    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,
-                  "Missing/invalid function: bool null()");
-    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
-                  "Missing/invalid function: bool boolean(bool)");
-    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
-                  "Missing/invalid function: bool boolean(bool)");
-    static_assert(
-        is_detected_exact<bool, number_integer_function_t, SAX,
-        number_integer_t>::value,
-        "Missing/invalid function: bool number_integer(number_integer_t)");
-    static_assert(
-        is_detected_exact<bool, number_unsigned_function_t, SAX,
-        number_unsigned_t>::value,
-        "Missing/invalid function: bool number_unsigned(number_unsigned_t)");
-    static_assert(is_detected_exact<bool, number_float_function_t, SAX,
-                  number_float_t, string_t>::value,
-                  "Missing/invalid function: bool number_float(number_float_t, const string_t&)");
-    static_assert(
-        is_detected_exact<bool, string_function_t, SAX, string_t>::value,
-        "Missing/invalid function: bool string(string_t&)");
-    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,
-                  "Missing/invalid function: bool start_object(std::size_t)");
-    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,
-                  "Missing/invalid function: bool key(string_t&)");
-    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,
-                  "Missing/invalid function: bool end_object()");
-    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,
-                  "Missing/invalid function: bool start_array(std::size_t)");
-    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,
-                  "Missing/invalid function: bool end_array()");
-    static_assert(
-        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,
-        "Missing/invalid function: bool parse_error(std::size_t, const "
-        "std::string&, const exception&)");
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/input/input_adapters.hpp>
-
-// #include <nlohmann/detail/input/json_sax.hpp>
-
-
-#include <cstddef>
-#include <string>
-#include <vector>
-
-// #include <nlohmann/detail/input/parser.hpp>
-
-// #include <nlohmann/detail/exceptions.hpp>
-
-
-namespace nlohmann
-{
-
-/*!
-@brief SAX interface
-
-This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
-Each function is called in different situations while the input is parsed. The
-boolean return value informs the parser whether to continue processing the
-input.
-*/
-template<typename BasicJsonType>
-struct json_sax
-{
-    /// type for (signed) integers
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    /// type for unsigned integers
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    /// type for floating-point numbers
-    using number_float_t = typename BasicJsonType::number_float_t;
-    /// type for strings
-    using string_t = typename BasicJsonType::string_t;
-
-    /*!
-    @brief a null value was read
-    @return whether parsing should proceed
-    */
-    virtual bool null() = 0;
-
-    /*!
-    @brief a boolean value was read
-    @param[in] val  boolean value
-    @return whether parsing should proceed
-    */
-    virtual bool boolean(bool val) = 0;
-
-    /*!
-    @brief an integer number was read
-    @param[in] val  integer value
-    @return whether parsing should proceed
-    */
-    virtual bool number_integer(number_integer_t val) = 0;
-
-    /*!
-    @brief an unsigned integer number was read
-    @param[in] val  unsigned integer value
-    @return whether parsing should proceed
-    */
-    virtual bool number_unsigned(number_unsigned_t val) = 0;
-
-    /*!
-    @brief an floating-point number was read
-    @param[in] val  floating-point value
-    @param[in] s    raw token value
-    @return whether parsing should proceed
-    */
-    virtual bool number_float(number_float_t val, const string_t& s) = 0;
-
-    /*!
-    @brief a string was read
-    @param[in] val  string value
-    @return whether parsing should proceed
-    @note It is safe to move the passed string.
-    */
-    virtual bool string(string_t& val) = 0;
-
-    /*!
-    @brief the beginning of an object was read
-    @param[in] elements  number of object elements or -1 if unknown
-    @return whether parsing should proceed
-    @note binary formats may report the number of elements
-    */
-    virtual bool start_object(std::size_t elements) = 0;
-
-    /*!
-    @brief an object key was read
-    @param[in] val  object key
-    @return whether parsing should proceed
-    @note It is safe to move the passed string.
-    */
-    virtual bool key(string_t& val) = 0;
-
-    /*!
-    @brief the end of an object was read
-    @return whether parsing should proceed
-    */
-    virtual bool end_object() = 0;
-
-    /*!
-    @brief the beginning of an array was read
-    @param[in] elements  number of array elements or -1 if unknown
-    @return whether parsing should proceed
-    @note binary formats may report the number of elements
-    */
-    virtual bool start_array(std::size_t elements) = 0;
-
-    /*!
-    @brief the end of an array was read
-    @return whether parsing should proceed
-    */
-    virtual bool end_array() = 0;
-
-    /*!
-    @brief a parse error occurred
-    @param[in] position    the position in the input where the error occurs
-    @param[in] last_token  the last read token
-    @param[in] ex          an exception object describing the error
-    @return whether parsing should proceed (must return false)
-    */
-    virtual bool parse_error(std::size_t position,
-                             const std::string& last_token,
-                             const detail::exception& ex) = 0;
-
-    virtual ~json_sax() = default;
-};
-
-
-namespace detail
-{
-/*!
-@brief SAX implementation to create a JSON value from SAX events
-
-This class implements the @ref json_sax interface and processes the SAX events
-to create a JSON value which makes it basically a DOM parser. The structure or
-hierarchy of the JSON value is managed by the stack `ref_stack` which contains
-a pointer to the respective array or object for each recursion depth.
-
-After successful parsing, the value that is passed by reference to the
-constructor contains the parsed value.
-
-@tparam BasicJsonType  the JSON type
-*/
-template<typename BasicJsonType>
-class json_sax_dom_parser
-{
-  public:
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    using number_float_t = typename BasicJsonType::number_float_t;
-    using string_t = typename BasicJsonType::string_t;
-
-    /*!
-    @param[in, out] r  reference to a JSON value that is manipulated while
-                       parsing
-    @param[in] allow_exceptions_  whether parse errors yield exceptions
-    */
-    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
-        : root(r), allow_exceptions(allow_exceptions_)
-    {}
-
-    bool null()
-    {
-        handle_value(nullptr);
-        return true;
-    }
-
-    bool boolean(bool val)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool number_integer(number_integer_t val)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool number_unsigned(number_unsigned_t val)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool number_float(number_float_t val, const string_t& /*unused*/)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool string(string_t& val)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool start_object(std::size_t len)
-    {
-        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
-
-        if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
-        {
-            JSON_THROW(out_of_range::create(408,
-                                            "excessive object size: " + std::to_string(len)));
-        }
-
-        return true;
-    }
-
-    bool key(string_t& val)
-    {
-        // add null at given key and store the reference for later
-        object_element = &(ref_stack.back()->m_value.object->operator[](val));
-        return true;
-    }
-
-    bool end_object()
-    {
-        ref_stack.pop_back();
-        return true;
-    }
-
-    bool start_array(std::size_t len)
-    {
-        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
-
-        if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
-        {
-            JSON_THROW(out_of_range::create(408,
-                                            "excessive array size: " + std::to_string(len)));
-        }
-
-        return true;
-    }
-
-    bool end_array()
-    {
-        ref_stack.pop_back();
-        return true;
-    }
-
-    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
-                     const detail::exception& ex)
-    {
-        errored = true;
-        if (allow_exceptions)
-        {
-            // determine the proper exception type from the id
-            switch ((ex.id / 100) % 100)
-            {
-                case 1:
-                    JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex));
-                case 4:
-                    JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex));
-                // LCOV_EXCL_START
-                case 2:
-                    JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex));
-                case 3:
-                    JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex));
-                case 5:
-                    JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex));
-                default:
-                    assert(false);
-                    // LCOV_EXCL_STOP
-            }
-        }
-        return false;
-    }
-
-    constexpr bool is_errored() const
-    {
-        return errored;
-    }
-
-  private:
-    /*!
-    @invariant If the ref stack is empty, then the passed value will be the new
-               root.
-    @invariant If the ref stack contains a value, then it is an array or an
-               object to which we can add elements
-    */
-    template<typename Value>
-    BasicJsonType* handle_value(Value&& v)
-    {
-        if (ref_stack.empty())
-        {
-            root = BasicJsonType(std::forward<Value>(v));
-            return &root;
-        }
-
-        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
-
-        if (ref_stack.back()->is_array())
-        {
-            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
-            return &(ref_stack.back()->m_value.array->back());
-        }
-        else
-        {
-            assert(object_element);
-            *object_element = BasicJsonType(std::forward<Value>(v));
-            return object_element;
-        }
-    }
-
-    /// the parsed JSON value
-    BasicJsonType& root;
-    /// stack to model hierarchy of values
-    std::vector<BasicJsonType*> ref_stack;
-    /// helper to hold the reference for the next object element
-    BasicJsonType* object_element = nullptr;
-    /// whether a syntax error occurred
-    bool errored = false;
-    /// whether to throw exceptions in case of errors
-    const bool allow_exceptions = true;
-};
-
-template<typename BasicJsonType>
-class json_sax_dom_callback_parser
-{
-  public:
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    using number_float_t = typename BasicJsonType::number_float_t;
-    using string_t = typename BasicJsonType::string_t;
-    using parser_callback_t = typename BasicJsonType::parser_callback_t;
-    using parse_event_t = typename BasicJsonType::parse_event_t;
-
-    json_sax_dom_callback_parser(BasicJsonType& r,
-                                 const parser_callback_t cb,
-                                 const bool allow_exceptions_ = true)
-        : root(r), callback(cb), allow_exceptions(allow_exceptions_)
-    {
-        keep_stack.push_back(true);
-    }
-
-    bool null()
-    {
-        handle_value(nullptr);
-        return true;
-    }
-
-    bool boolean(bool val)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool number_integer(number_integer_t val)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool number_unsigned(number_unsigned_t val)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool number_float(number_float_t val, const string_t& /*unused*/)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool string(string_t& val)
-    {
-        handle_value(val);
-        return true;
-    }
-
-    bool start_object(std::size_t len)
-    {
-        // check callback for object start
-        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
-        keep_stack.push_back(keep);
-
-        auto val = handle_value(BasicJsonType::value_t::object, true);
-        ref_stack.push_back(val.second);
-
-        // check object limit
-        if (ref_stack.back())
-        {
-            if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
-            {
-                JSON_THROW(out_of_range::create(408,
-                                                "excessive object size: " + std::to_string(len)));
-            }
-        }
-
-        return true;
-    }
-
-    bool key(string_t& val)
-    {
-        BasicJsonType k = BasicJsonType(val);
-
-        // check callback for key
-        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
-        key_keep_stack.push_back(keep);
-
-        // add discarded value at given key and store the reference for later
-        if (keep and ref_stack.back())
-        {
-            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
-        }
-
-        return true;
-    }
-
-    bool end_object()
-    {
-        if (ref_stack.back())
-        {
-            if (not callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
-            {
-                // discard object
-                *ref_stack.back() = discarded;
-            }
-        }
-
-        assert(not ref_stack.empty());
-        assert(not keep_stack.empty());
-        ref_stack.pop_back();
-        keep_stack.pop_back();
-
-        if (not ref_stack.empty() and ref_stack.back())
-        {
-            // remove discarded value
-            if (ref_stack.back()->is_object())
-            {
-                for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
-                {
-                    if (it->is_discarded())
-                    {
-                        ref_stack.back()->erase(it);
-                        break;
-                    }
-                }
-            }
-        }
-
-        return true;
-    }
-
-    bool start_array(std::size_t len)
-    {
-        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
-        keep_stack.push_back(keep);
-
-        auto val = handle_value(BasicJsonType::value_t::array, true);
-        ref_stack.push_back(val.second);
-
-        // check array limit
-        if (ref_stack.back())
-        {
-            if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
-            {
-                JSON_THROW(out_of_range::create(408,
-                                                "excessive array size: " + std::to_string(len)));
-            }
-        }
-
-        return true;
-    }
-
-    bool end_array()
-    {
-        bool keep = true;
-
-        if (ref_stack.back())
-        {
-            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
-            if (not keep)
-            {
-                // discard array
-                *ref_stack.back() = discarded;
-            }
-        }
-
-        assert(not ref_stack.empty());
-        assert(not keep_stack.empty());
-        ref_stack.pop_back();
-        keep_stack.pop_back();
-
-        // remove discarded value
-        if (not keep and not ref_stack.empty())
-        {
-            if (ref_stack.back()->is_array())
-            {
-                ref_stack.back()->m_value.array->pop_back();
-            }
-        }
-
-        return true;
-    }
-
-    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
-                     const detail::exception& ex)
-    {
-        errored = true;
-        if (allow_exceptions)
-        {
-            // determine the proper exception type from the id
-            switch ((ex.id / 100) % 100)
-            {
-                case 1:
-                    JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex));
-                case 4:
-                    JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex));
-                // LCOV_EXCL_START
-                case 2:
-                    JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex));
-                case 3:
-                    JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex));
-                case 5:
-                    JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex));
-                default:
-                    assert(false);
-                    // LCOV_EXCL_STOP
-            }
-        }
-        return false;
-    }
-
-    constexpr bool is_errored() const
-    {
-        return errored;
-    }
-
-  private:
-    /*!
-    @param[in] v  value to add to the JSON value we build during parsing
-    @param[in] skip_callback  whether we should skip calling the callback
-               function; this is required after start_array() and
-               start_object() SAX events, because otherwise we would call the
-               callback function with an empty array or object, respectively.
-
-    @invariant If the ref stack is empty, then the passed value will be the new
-               root.
-    @invariant If the ref stack contains a value, then it is an array or an
-               object to which we can add elements
-
-    @return pair of boolean (whether value should be kept) and pointer (to the
-            passed value in the ref_stack hierarchy; nullptr if not kept)
-    */
-    template<typename Value>
-    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
-    {
-        assert(not keep_stack.empty());
-
-        // do not handle this value if we know it would be added to a discarded
-        // container
-        if (not keep_stack.back())
-        {
-            return {false, nullptr};
-        }
-
-        // create value
-        auto value = BasicJsonType(std::forward<Value>(v));
-
-        // check callback
-        const bool keep = skip_callback or callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
-
-        // do not handle this value if we just learnt it shall be discarded
-        if (not keep)
-        {
-            return {false, nullptr};
-        }
-
-        if (ref_stack.empty())
-        {
-            root = std::move(value);
-            return {true, &root};
-        }
-
-        // skip this value if we already decided to skip the parent
-        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
-        if (not ref_stack.back())
-        {
-            return {false, nullptr};
-        }
-
-        // we now only expect arrays and objects
-        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
-
-        if (ref_stack.back()->is_array())
-        {
-            ref_stack.back()->m_value.array->push_back(std::move(value));
-            return {true, &(ref_stack.back()->m_value.array->back())};
-        }
-        else
-        {
-            // check if we should store an element for the current key
-            assert(not key_keep_stack.empty());
-            const bool store_element = key_keep_stack.back();
-            key_keep_stack.pop_back();
-
-            if (not store_element)
-            {
-                return {false, nullptr};
-            }
-
-            assert(object_element);
-            *object_element = std::move(value);
-            return {true, object_element};
-        }
-    }
-
-    /// the parsed JSON value
-    BasicJsonType& root;
-    /// stack to model hierarchy of values
-    std::vector<BasicJsonType*> ref_stack;
-    /// stack to manage which values to keep
-    std::vector<bool> keep_stack;
-    /// stack to manage which object keys to keep
-    std::vector<bool> key_keep_stack;
-    /// helper to hold the reference for the next object element
-    BasicJsonType* object_element = nullptr;
-    /// whether a syntax error occurred
-    bool errored = false;
-    /// callback function
-    const parser_callback_t callback = nullptr;
-    /// whether to throw exceptions in case of errors
-    const bool allow_exceptions = true;
-    /// a discarded value for the callback
-    BasicJsonType discarded = BasicJsonType::value_t::discarded;
-};
-
-template<typename BasicJsonType>
-class json_sax_acceptor
-{
-  public:
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    using number_float_t = typename BasicJsonType::number_float_t;
-    using string_t = typename BasicJsonType::string_t;
-
-    bool null()
-    {
-        return true;
-    }
-
-    bool boolean(bool /*unused*/)
-    {
-        return true;
-    }
-
-    bool number_integer(number_integer_t /*unused*/)
-    {
-        return true;
-    }
-
-    bool number_unsigned(number_unsigned_t /*unused*/)
-    {
-        return true;
-    }
-
-    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
-    {
-        return true;
-    }
-
-    bool string(string_t& /*unused*/)
-    {
-        return true;
-    }
-
-    bool start_object(std::size_t  /*unused*/ = std::size_t(-1))
-    {
-        return true;
-    }
-
-    bool key(string_t& /*unused*/)
-    {
-        return true;
-    }
-
-    bool end_object()
-    {
-        return true;
-    }
-
-    bool start_array(std::size_t  /*unused*/ = std::size_t(-1))
-    {
-        return true;
-    }
-
-    bool end_array()
-    {
-        return true;
-    }
-
-    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
-    {
-        return false;
-    }
-};
-}  // namespace detail
-
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/input/lexer.hpp>
-
-// #include <nlohmann/detail/value_t.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-////////////
-// parser //
-////////////
-
-/*!
-@brief syntax analysis
-
-This class implements a recursive decent parser.
-*/
-template<typename BasicJsonType>
-class parser
-{
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    using number_float_t = typename BasicJsonType::number_float_t;
-    using string_t = typename BasicJsonType::string_t;
-    using lexer_t = lexer<BasicJsonType>;
-    using token_type = typename lexer_t::token_type;
-
-  public:
-    enum class parse_event_t : uint8_t
-    {
-        /// the parser read `{` and started to process a JSON object
-        object_start,
-        /// the parser read `}` and finished processing a JSON object
-        object_end,
-        /// the parser read `[` and started to process a JSON array
-        array_start,
-        /// the parser read `]` and finished processing a JSON array
-        array_end,
-        /// the parser read a key of a value in an object
-        key,
-        /// the parser finished reading a JSON value
-        value
-    };
-
-    using parser_callback_t =
-        std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;
-
-    /// a parser reading from an input adapter
-    explicit parser(detail::input_adapter_t&& adapter,
-                    const parser_callback_t cb = nullptr,
-                    const bool allow_exceptions_ = true)
-        : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_)
-    {
-        // read first token
-        get_token();
-    }
-
-    /*!
-    @brief public parser interface
-
-    @param[in] strict      whether to expect the last token to be EOF
-    @param[in,out] result  parsed JSON value
-
-    @throw parse_error.101 in case of an unexpected token
-    @throw parse_error.102 if to_unicode fails or surrogate error
-    @throw parse_error.103 if to_unicode fails
-    */
-    void parse(const bool strict, BasicJsonType& result)
-    {
-        if (callback)
-        {
-            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
-            sax_parse_internal(&sdp);
-            result.assert_invariant();
-
-            // in strict mode, input must be completely read
-            if (strict and (get_token() != token_type::end_of_input))
-            {
-                sdp.parse_error(m_lexer.get_position(),
-                                m_lexer.get_token_string(),
-                                parse_error::create(101, m_lexer.get_position(),
-                                                    exception_message(token_type::end_of_input, "value")));
-            }
-
-            // in case of an error, return discarded value
-            if (sdp.is_errored())
-            {
-                result = value_t::discarded;
-                return;
-            }
-
-            // set top-level value to null if it was discarded by the callback
-            // function
-            if (result.is_discarded())
-            {
-                result = nullptr;
-            }
-        }
-        else
-        {
-            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
-            sax_parse_internal(&sdp);
-            result.assert_invariant();
-
-            // in strict mode, input must be completely read
-            if (strict and (get_token() != token_type::end_of_input))
-            {
-                sdp.parse_error(m_lexer.get_position(),
-                                m_lexer.get_token_string(),
-                                parse_error::create(101, m_lexer.get_position(),
-                                                    exception_message(token_type::end_of_input, "value")));
-            }
-
-            // in case of an error, return discarded value
-            if (sdp.is_errored())
-            {
-                result = value_t::discarded;
-                return;
-            }
-        }
-    }
-
-    /*!
-    @brief public accept interface
-
-    @param[in] strict  whether to expect the last token to be EOF
-    @return whether the input is a proper JSON text
-    */
-    bool accept(const bool strict = true)
-    {
-        json_sax_acceptor<BasicJsonType> sax_acceptor;
-        return sax_parse(&sax_acceptor, strict);
-    }
-
-    template <typename SAX>
-    bool sax_parse(SAX* sax, const bool strict = true)
-    {
-        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
-        const bool result = sax_parse_internal(sax);
-
-        // strict mode: next byte must be EOF
-        if (result and strict and (get_token() != token_type::end_of_input))
-        {
-            return sax->parse_error(m_lexer.get_position(),
-                                    m_lexer.get_token_string(),
-                                    parse_error::create(101, m_lexer.get_position(),
-                                            exception_message(token_type::end_of_input, "value")));
-        }
-
-        return result;
-    }
-
-  private:
-    template <typename SAX>
-    bool sax_parse_internal(SAX* sax)
-    {
-        // stack to remember the hierarchy of structured values we are parsing
-        // true = array; false = object
-        std::vector<bool> states;
-        // value to avoid a goto (see comment where set to true)
-        bool skip_to_state_evaluation = false;
-
-        while (true)
-        {
-            if (not skip_to_state_evaluation)
-            {
-                // invariant: get_token() was called before each iteration
-                switch (last_token)
-                {
-                    case token_type::begin_object:
-                    {
-                        if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1))))
-                        {
-                            return false;
-                        }
-
-                        // closing } -> we are done
-                        if (get_token() == token_type::end_object)
-                        {
-                            if (JSON_UNLIKELY(not sax->end_object()))
-                            {
-                                return false;
-                            }
-                            break;
-                        }
-
-                        // parse key
-                        if (JSON_UNLIKELY(last_token != token_type::value_string))
-                        {
-                            return sax->parse_error(m_lexer.get_position(),
-                                                    m_lexer.get_token_string(),
-                                                    parse_error::create(101, m_lexer.get_position(),
-                                                            exception_message(token_type::value_string, "object key")));
-                        }
-                        if (JSON_UNLIKELY(not sax->key(m_lexer.get_string())))
-                        {
-                            return false;
-                        }
-
-                        // parse separator (:)
-                        if (JSON_UNLIKELY(get_token() != token_type::name_separator))
-                        {
-                            return sax->parse_error(m_lexer.get_position(),
-                                                    m_lexer.get_token_string(),
-                                                    parse_error::create(101, m_lexer.get_position(),
-                                                            exception_message(token_type::name_separator, "object separator")));
-                        }
-
-                        // remember we are now inside an object
-                        states.push_back(false);
-
-                        // parse values
-                        get_token();
-                        continue;
-                    }
-
-                    case token_type::begin_array:
-                    {
-                        if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1))))
-                        {
-                            return false;
-                        }
-
-                        // closing ] -> we are done
-                        if (get_token() == token_type::end_array)
-                        {
-                            if (JSON_UNLIKELY(not sax->end_array()))
-                            {
-                                return false;
-                            }
-                            break;
-                        }
-
-                        // remember we are now inside an array
-                        states.push_back(true);
-
-                        // parse values (no need to call get_token)
-                        continue;
-                    }
-
-                    case token_type::value_float:
-                    {
-                        const auto res = m_lexer.get_number_float();
-
-                        if (JSON_UNLIKELY(not std::isfinite(res)))
-                        {
-                            return sax->parse_error(m_lexer.get_position(),
-                                                    m_lexer.get_token_string(),
-                                                    out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'"));
-                        }
-                        else
-                        {
-                            if (JSON_UNLIKELY(not sax->number_float(res, m_lexer.get_string())))
-                            {
-                                return false;
-                            }
-                            break;
-                        }
-                    }
-
-                    case token_type::literal_false:
-                    {
-                        if (JSON_UNLIKELY(not sax->boolean(false)))
-                        {
-                            return false;
-                        }
-                        break;
-                    }
-
-                    case token_type::literal_null:
-                    {
-                        if (JSON_UNLIKELY(not sax->null()))
-                        {
-                            return false;
-                        }
-                        break;
-                    }
-
-                    case token_type::literal_true:
-                    {
-                        if (JSON_UNLIKELY(not sax->boolean(true)))
-                        {
-                            return false;
-                        }
-                        break;
-                    }
-
-                    case token_type::value_integer:
-                    {
-                        if (JSON_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer())))
-                        {
-                            return false;
-                        }
-                        break;
-                    }
-
-                    case token_type::value_string:
-                    {
-                        if (JSON_UNLIKELY(not sax->string(m_lexer.get_string())))
-                        {
-                            return false;
-                        }
-                        break;
-                    }
-
-                    case token_type::value_unsigned:
-                    {
-                        if (JSON_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned())))
-                        {
-                            return false;
-                        }
-                        break;
-                    }
-
-                    case token_type::parse_error:
-                    {
-                        // using "uninitialized" to avoid "expected" message
-                        return sax->parse_error(m_lexer.get_position(),
-                                                m_lexer.get_token_string(),
-                                                parse_error::create(101, m_lexer.get_position(),
-                                                        exception_message(token_type::uninitialized, "value")));
-                    }
-
-                    default: // the last token was unexpected
-                    {
-                        return sax->parse_error(m_lexer.get_position(),
-                                                m_lexer.get_token_string(),
-                                                parse_error::create(101, m_lexer.get_position(),
-                                                        exception_message(token_type::literal_or_value, "value")));
-                    }
-                }
-            }
-            else
-            {
-                skip_to_state_evaluation = false;
-            }
-
-            // we reached this line after we successfully parsed a value
-            if (states.empty())
-            {
-                // empty stack: we reached the end of the hierarchy: done
-                return true;
-            }
-            else
-            {
-                if (states.back())  // array
-                {
-                    // comma -> next value
-                    if (get_token() == token_type::value_separator)
-                    {
-                        // parse a new value
-                        get_token();
-                        continue;
-                    }
-
-                    // closing ]
-                    if (JSON_LIKELY(last_token == token_type::end_array))
-                    {
-                        if (JSON_UNLIKELY(not sax->end_array()))
-                        {
-                            return false;
-                        }
-
-                        // We are done with this array. Before we can parse a
-                        // new value, we need to evaluate the new state first.
-                        // By setting skip_to_state_evaluation to false, we
-                        // are effectively jumping to the beginning of this if.
-                        assert(not states.empty());
-                        states.pop_back();
-                        skip_to_state_evaluation = true;
-                        continue;
-                    }
-                    else
-                    {
-                        return sax->parse_error(m_lexer.get_position(),
-                                                m_lexer.get_token_string(),
-                                                parse_error::create(101, m_lexer.get_position(),
-                                                        exception_message(token_type::end_array, "array")));
-                    }
-                }
-                else  // object
-                {
-                    // comma -> next value
-                    if (get_token() == token_type::value_separator)
-                    {
-                        // parse key
-                        if (JSON_UNLIKELY(get_token() != token_type::value_string))
-                        {
-                            return sax->parse_error(m_lexer.get_position(),
-                                                    m_lexer.get_token_string(),
-                                                    parse_error::create(101, m_lexer.get_position(),
-                                                            exception_message(token_type::value_string, "object key")));
-                        }
-                        else
-                        {
-                            if (JSON_UNLIKELY(not sax->key(m_lexer.get_string())))
-                            {
-                                return false;
-                            }
-                        }
-
-                        // parse separator (:)
-                        if (JSON_UNLIKELY(get_token() != token_type::name_separator))
-                        {
-                            return sax->parse_error(m_lexer.get_position(),
-                                                    m_lexer.get_token_string(),
-                                                    parse_error::create(101, m_lexer.get_position(),
-                                                            exception_message(token_type::name_separator, "object separator")));
-                        }
-
-                        // parse values
-                        get_token();
-                        continue;
-                    }
-
-                    // closing }
-                    if (JSON_LIKELY(last_token == token_type::end_object))
-                    {
-                        if (JSON_UNLIKELY(not sax->end_object()))
-                        {
-                            return false;
-                        }
-
-                        // We are done with this object. Before we can parse a
-                        // new value, we need to evaluate the new state first.
-                        // By setting skip_to_state_evaluation to false, we
-                        // are effectively jumping to the beginning of this if.
-                        assert(not states.empty());
-                        states.pop_back();
-                        skip_to_state_evaluation = true;
-                        continue;
-                    }
-                    else
-                    {
-                        return sax->parse_error(m_lexer.get_position(),
-                                                m_lexer.get_token_string(),
-                                                parse_error::create(101, m_lexer.get_position(),
-                                                        exception_message(token_type::end_object, "object")));
-                    }
-                }
-            }
-        }
-    }
-
-    /// get next token from lexer
-    token_type get_token()
-    {
-        return (last_token = m_lexer.scan());
-    }
-
-    std::string exception_message(const token_type expected, const std::string& context)
-    {
-        std::string error_msg = "syntax error ";
-
-        if (not context.empty())
-        {
-            error_msg += "while parsing " + context + " ";
-        }
-
-        error_msg += "- ";
-
-        if (last_token == token_type::parse_error)
-        {
-            error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
-                         m_lexer.get_token_string() + "'";
-        }
-        else
-        {
-            error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
-        }
-
-        if (expected != token_type::uninitialized)
-        {
-            error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
-        }
-
-        return error_msg;
-    }
-
-  private:
-    /// callback function
-    const parser_callback_t callback = nullptr;
-    /// the type of the last read token
-    token_type last_token = token_type::uninitialized;
-    /// the lexer
-    lexer_t m_lexer;
-    /// whether to throw exceptions in case of errors
-    const bool allow_exceptions = true;
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
-
-
-#include <cstddef> // ptrdiff_t
-#include <limits>  // numeric_limits
-
-namespace nlohmann
-{
-namespace detail
-{
-/*
-@brief an iterator for primitive JSON types
-
-This class models an iterator for primitive JSON types (boolean, number,
-string). It's only purpose is to allow the iterator/const_iterator classes
-to "iterate" over primitive values. Internally, the iterator is modeled by
-a `difference_type` variable. Value begin_value (`0`) models the begin,
-end_value (`1`) models past the end.
-*/
-class primitive_iterator_t
-{
-  private:
-    using difference_type = std::ptrdiff_t;
-    static constexpr difference_type begin_value = 0;
-    static constexpr difference_type end_value = begin_value + 1;
-
-    /// iterator as signed integer type
-    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
-
-  public:
-    constexpr difference_type get_value() const noexcept
-    {
-        return m_it;
-    }
-
-    /// set iterator to a defined beginning
-    void set_begin() noexcept
-    {
-        m_it = begin_value;
-    }
-
-    /// set iterator to a defined past the end
-    void set_end() noexcept
-    {
-        m_it = end_value;
-    }
-
-    /// return whether the iterator can be dereferenced
-    constexpr bool is_begin() const noexcept
-    {
-        return m_it == begin_value;
-    }
-
-    /// return whether the iterator is at end
-    constexpr bool is_end() const noexcept
-    {
-        return m_it == end_value;
-    }
-
-    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
-    {
-        return lhs.m_it == rhs.m_it;
-    }
-
-    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
-    {
-        return lhs.m_it < rhs.m_it;
-    }
-
-    primitive_iterator_t operator+(difference_type n) noexcept
-    {
-        auto result = *this;
-        result += n;
-        return result;
-    }
-
-    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
-    {
-        return lhs.m_it - rhs.m_it;
-    }
-
-    primitive_iterator_t& operator++() noexcept
-    {
-        ++m_it;
-        return *this;
-    }
-
-    primitive_iterator_t const operator++(int) noexcept
-    {
-        auto result = *this;
-        ++m_it;
-        return result;
-    }
-
-    primitive_iterator_t& operator--() noexcept
-    {
-        --m_it;
-        return *this;
-    }
-
-    primitive_iterator_t const operator--(int) noexcept
-    {
-        auto result = *this;
-        --m_it;
-        return result;
-    }
-
-    primitive_iterator_t& operator+=(difference_type n) noexcept
-    {
-        m_it += n;
-        return *this;
-    }
-
-    primitive_iterator_t& operator-=(difference_type n) noexcept
-    {
-        m_it -= n;
-        return *this;
-    }
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/iterators/internal_iterator.hpp>
-
-
-// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-/*!
-@brief an iterator value
-
-@note This structure could easily be a union, but MSVC currently does not allow
-unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
-*/
-template<typename BasicJsonType> struct internal_iterator
-{
-    /// iterator for JSON objects
-    typename BasicJsonType::object_t::iterator object_iterator {};
-    /// iterator for JSON arrays
-    typename BasicJsonType::array_t::iterator array_iterator {};
-    /// generic iterator for all other types
-    primitive_iterator_t primitive_iterator {};
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/iterators/iter_impl.hpp>
-
-
-#include <ciso646> // not
-#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
-#include <type_traits> // conditional, is_const, remove_const
-
-// #include <nlohmann/detail/exceptions.hpp>
-
-// #include <nlohmann/detail/iterators/internal_iterator.hpp>
-
-// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-// #include <nlohmann/detail/meta/cpp_future.hpp>
-
-// #include <nlohmann/detail/value_t.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-// forward declare, to be able to friend it later on
-template<typename IteratorType> class iteration_proxy;
-template<typename IteratorType> class iteration_proxy_value;
-
-/*!
-@brief a template for a bidirectional iterator for the @ref basic_json class
-This class implements a both iterators (iterator and const_iterator) for the
-@ref basic_json class.
-@note An iterator is called *initialized* when a pointer to a JSON value has
-      been set (e.g., by a constructor or a copy assignment). If the iterator is
-      default-constructed, it is *uninitialized* and most methods are undefined.
-      **The library uses assertions to detect calls on uninitialized iterators.**
-@requirement The class satisfies the following concept requirements:
--
-[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
-  The iterator that can be moved can be moved in both directions (i.e.
-  incremented and decremented).
-@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
-       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
-*/
-template<typename BasicJsonType>
-class iter_impl
-{
-    /// allow basic_json to access private members
-    friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
-    friend BasicJsonType;
-    friend iteration_proxy<iter_impl>;
-    friend iteration_proxy_value<iter_impl>;
-
-    using object_t = typename BasicJsonType::object_t;
-    using array_t = typename BasicJsonType::array_t;
-    // make sure BasicJsonType is basic_json or const basic_json
-    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
-                  "iter_impl only accepts (const) basic_json");
-
-  public:
-
-    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
-    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
-    /// A user-defined iterator should provide publicly accessible typedefs named
-    /// iterator_category, value_type, difference_type, pointer, and reference.
-    /// Note that value_type is required to be non-const, even for constant iterators.
-    using iterator_category = std::bidirectional_iterator_tag;
-
-    /// the type of the values when the iterator is dereferenced
-    using value_type = typename BasicJsonType::value_type;
-    /// a type to represent differences between iterators
-    using difference_type = typename BasicJsonType::difference_type;
-    /// defines a pointer to the type iterated over (value_type)
-    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
-          typename BasicJsonType::const_pointer,
-          typename BasicJsonType::pointer>::type;
-    /// defines a reference to the type iterated over (value_type)
-    using reference =
-        typename std::conditional<std::is_const<BasicJsonType>::value,
-        typename BasicJsonType::const_reference,
-        typename BasicJsonType::reference>::type;
-
-    /// default constructor
-    iter_impl() = default;
-
-    /*!
-    @brief constructor for a given JSON instance
-    @param[in] object  pointer to a JSON object for this iterator
-    @pre object != nullptr
-    @post The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    explicit iter_impl(pointer object) noexcept : m_object(object)
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                m_it.object_iterator = typename object_t::iterator();
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_it.array_iterator = typename array_t::iterator();
-                break;
-            }
-
-            default:
-            {
-                m_it.primitive_iterator = primitive_iterator_t();
-                break;
-            }
-        }
-    }
-
-    /*!
-    @note The conventional copy constructor and copy assignment are implicitly
-          defined. Combined with the following converting constructor and
-          assignment, they support: (1) copy from iterator to iterator, (2)
-          copy from const iterator to const iterator, and (3) conversion from
-          iterator to const iterator. However conversion from const iterator
-          to iterator is not defined.
-    */
-
-    /*!
-    @brief converting constructor
-    @param[in] other  non-const iterator to copy from
-    @note It is not checked whether @a other is initialized.
-    */
-    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
-        : m_object(other.m_object), m_it(other.m_it) {}
-
-    /*!
-    @brief converting assignment
-    @param[in,out] other  non-const iterator to copy from
-    @return const/non-const iterator
-    @note It is not checked whether @a other is initialized.
-    */
-    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
-    {
-        m_object = other.m_object;
-        m_it = other.m_it;
-        return *this;
-    }
-
-  private:
-    /*!
-    @brief set the iterator to the first value
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    void set_begin() noexcept
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                m_it.object_iterator = m_object->m_value.object->begin();
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_it.array_iterator = m_object->m_value.array->begin();
-                break;
-            }
-
-            case value_t::null:
-            {
-                // set to end so begin()==end() is true: null is empty
-                m_it.primitive_iterator.set_end();
-                break;
-            }
-
-            default:
-            {
-                m_it.primitive_iterator.set_begin();
-                break;
-            }
-        }
-    }
-
-    /*!
-    @brief set the iterator past the last value
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    void set_end() noexcept
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                m_it.object_iterator = m_object->m_value.object->end();
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_it.array_iterator = m_object->m_value.array->end();
-                break;
-            }
-
-            default:
-            {
-                m_it.primitive_iterator.set_end();
-                break;
-            }
-        }
-    }
-
-  public:
-    /*!
-    @brief return a reference to the value pointed to by the iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    reference operator*() const
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                assert(m_it.object_iterator != m_object->m_value.object->end());
-                return m_it.object_iterator->second;
-            }
-
-            case value_t::array:
-            {
-                assert(m_it.array_iterator != m_object->m_value.array->end());
-                return *m_it.array_iterator;
-            }
-
-            case value_t::null:
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-
-            default:
-            {
-                if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
-                {
-                    return *m_object;
-                }
-
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-            }
-        }
-    }
-
-    /*!
-    @brief dereference the iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    pointer operator->() const
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                assert(m_it.object_iterator != m_object->m_value.object->end());
-                return &(m_it.object_iterator->second);
-            }
-
-            case value_t::array:
-            {
-                assert(m_it.array_iterator != m_object->m_value.array->end());
-                return &*m_it.array_iterator;
-            }
-
-            default:
-            {
-                if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
-                {
-                    return m_object;
-                }
-
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-            }
-        }
-    }
-
-    /*!
-    @brief post-increment (it++)
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl const operator++(int)
-    {
-        auto result = *this;
-        ++(*this);
-        return result;
-    }
-
-    /*!
-    @brief pre-increment (++it)
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl& operator++()
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                std::advance(m_it.object_iterator, 1);
-                break;
-            }
-
-            case value_t::array:
-            {
-                std::advance(m_it.array_iterator, 1);
-                break;
-            }
-
-            default:
-            {
-                ++m_it.primitive_iterator;
-                break;
-            }
-        }
-
-        return *this;
-    }
-
-    /*!
-    @brief post-decrement (it--)
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl const operator--(int)
-    {
-        auto result = *this;
-        --(*this);
-        return result;
-    }
-
-    /*!
-    @brief pre-decrement (--it)
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl& operator--()
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-            {
-                std::advance(m_it.object_iterator, -1);
-                break;
-            }
-
-            case value_t::array:
-            {
-                std::advance(m_it.array_iterator, -1);
-                break;
-            }
-
-            default:
-            {
-                --m_it.primitive_iterator;
-                break;
-            }
-        }
-
-        return *this;
-    }
-
-    /*!
-    @brief  comparison: equal
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator==(const iter_impl& other) const
-    {
-        // if objects are not the same, the comparison is undefined
-        if (JSON_UNLIKELY(m_object != other.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
-        }
-
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                return (m_it.object_iterator == other.m_it.object_iterator);
-
-            case value_t::array:
-                return (m_it.array_iterator == other.m_it.array_iterator);
-
-            default:
-                return (m_it.primitive_iterator == other.m_it.primitive_iterator);
-        }
-    }
-
-    /*!
-    @brief  comparison: not equal
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator!=(const iter_impl& other) const
-    {
-        return not operator==(other);
-    }
-
-    /*!
-    @brief  comparison: smaller
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator<(const iter_impl& other) const
-    {
-        // if objects are not the same, the comparison is undefined
-        if (JSON_UNLIKELY(m_object != other.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
-        }
-
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators"));
-
-            case value_t::array:
-                return (m_it.array_iterator < other.m_it.array_iterator);
-
-            default:
-                return (m_it.primitive_iterator < other.m_it.primitive_iterator);
-        }
-    }
-
-    /*!
-    @brief  comparison: less than or equal
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator<=(const iter_impl& other) const
-    {
-        return not other.operator < (*this);
-    }
-
-    /*!
-    @brief  comparison: greater than
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator>(const iter_impl& other) const
-    {
-        return not operator<=(other);
-    }
-
-    /*!
-    @brief  comparison: greater than or equal
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    bool operator>=(const iter_impl& other) const
-    {
-        return not operator<(other);
-    }
-
-    /*!
-    @brief  add to iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl& operator+=(difference_type i)
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
-
-            case value_t::array:
-            {
-                std::advance(m_it.array_iterator, i);
-                break;
-            }
-
-            default:
-            {
-                m_it.primitive_iterator += i;
-                break;
-            }
-        }
-
-        return *this;
-    }
-
-    /*!
-    @brief  subtract from iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl& operator-=(difference_type i)
-    {
-        return operator+=(-i);
-    }
-
-    /*!
-    @brief  add to iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl operator+(difference_type i) const
-    {
-        auto result = *this;
-        result += i;
-        return result;
-    }
-
-    /*!
-    @brief  addition of distance and iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    friend iter_impl operator+(difference_type i, const iter_impl& it)
-    {
-        auto result = it;
-        result += i;
-        return result;
-    }
-
-    /*!
-    @brief  subtract from iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    iter_impl operator-(difference_type i) const
-    {
-        auto result = *this;
-        result -= i;
-        return result;
-    }
-
-    /*!
-    @brief  return difference
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    difference_type operator-(const iter_impl& other) const
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
-
-            case value_t::array:
-                return m_it.array_iterator - other.m_it.array_iterator;
-
-            default:
-                return m_it.primitive_iterator - other.m_it.primitive_iterator;
-        }
-    }
-
-    /*!
-    @brief  access to successor
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    reference operator[](difference_type n) const
-    {
-        assert(m_object != nullptr);
-
-        switch (m_object->m_type)
-        {
-            case value_t::object:
-                JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
-
-            case value_t::array:
-                return *std::next(m_it.array_iterator, n);
-
-            case value_t::null:
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-
-            default:
-            {
-                if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n))
-                {
-                    return *m_object;
-                }
-
-                JSON_THROW(invalid_iterator::create(214, "cannot get value"));
-            }
-        }
-    }
-
-    /*!
-    @brief  return the key of an object iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    const typename object_t::key_type& key() const
-    {
-        assert(m_object != nullptr);
-
-        if (JSON_LIKELY(m_object->is_object()))
-        {
-            return m_it.object_iterator->first;
-        }
-
-        JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators"));
-    }
-
-    /*!
-    @brief  return the value of an iterator
-    @pre The iterator is initialized; i.e. `m_object != nullptr`.
-    */
-    reference value() const
-    {
-        return operator*();
-    }
-
-  private:
-    /// associated JSON instance
-    pointer m_object = nullptr;
-    /// the actual iterator of the associated instance
-    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it;
-};
-}  // namespace detail
-} // namespace nlohmann
-// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
-
-// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
-
-
-#include <cstddef> // ptrdiff_t
-#include <iterator> // reverse_iterator
-#include <utility> // declval
-
-namespace nlohmann
-{
-namespace detail
-{
-//////////////////////
-// reverse_iterator //
-//////////////////////
-
-/*!
-@brief a template for a reverse iterator class
-
-@tparam Base the base iterator type to reverse. Valid types are @ref
-iterator (to create @ref reverse_iterator) and @ref const_iterator (to
-create @ref const_reverse_iterator).
-
-@requirement The class satisfies the following concept requirements:
--
-[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
-  The iterator that can be moved can be moved in both directions (i.e.
-  incremented and decremented).
-- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
-  It is possible to write to the pointed-to element (only if @a Base is
-  @ref iterator).
-
-@since version 1.0.0
-*/
-template<typename Base>
-class json_reverse_iterator : public std::reverse_iterator<Base>
-{
-  public:
-    using difference_type = std::ptrdiff_t;
-    /// shortcut to the reverse iterator adapter
-    using base_iterator = std::reverse_iterator<Base>;
-    /// the reference type for the pointed-to element
-    using reference = typename Base::reference;
-
-    /// create reverse iterator from iterator
-    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
-        : base_iterator(it) {}
-
-    /// create reverse iterator from base class
-    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
-
-    /// post-increment (it++)
-    json_reverse_iterator const operator++(int)
-    {
-        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
-    }
-
-    /// pre-increment (++it)
-    json_reverse_iterator& operator++()
-    {
-        return static_cast<json_reverse_iterator&>(base_iterator::operator++());
-    }
-
-    /// post-decrement (it--)
-    json_reverse_iterator const operator--(int)
-    {
-        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
-    }
-
-    /// pre-decrement (--it)
-    json_reverse_iterator& operator--()
-    {
-        return static_cast<json_reverse_iterator&>(base_iterator::operator--());
-    }
-
-    /// add to iterator
-    json_reverse_iterator& operator+=(difference_type i)
-    {
-        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
-    }
-
-    /// add to iterator
-    json_reverse_iterator operator+(difference_type i) const
-    {
-        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
-    }
-
-    /// subtract from iterator
-    json_reverse_iterator operator-(difference_type i) const
-    {
-        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
-    }
-
-    /// return difference
-    difference_type operator-(const json_reverse_iterator& other) const
-    {
-        return base_iterator(*this) - base_iterator(other);
-    }
-
-    /// access to successor
-    reference operator[](difference_type n) const
-    {
-        return *(this->operator+(n));
-    }
-
-    /// return the key of an object iterator
-    auto key() const -> decltype(std::declval<Base>().key())
-    {
-        auto it = --this->base();
-        return it.key();
-    }
-
-    /// return the value of an iterator
-    reference value() const
-    {
-        auto it = --this->base();
-        return it.operator * ();
-    }
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/output/output_adapters.hpp>
-
-
-#include <algorithm> // copy
-#include <cstddef> // size_t
-#include <ios> // streamsize
-#include <iterator> // back_inserter
-#include <memory> // shared_ptr, make_shared
-#include <ostream> // basic_ostream
-#include <string> // basic_string
-#include <vector> // vector
-
-namespace nlohmann
-{
-namespace detail
-{
-/// abstract output adapter interface
-template<typename CharType> struct output_adapter_protocol
-{
-    virtual void write_character(CharType c) = 0;
-    virtual void write_characters(const CharType* s, std::size_t length) = 0;
-    virtual ~output_adapter_protocol() = default;
-};
-
-/// a type to simplify interfaces
-template<typename CharType>
-using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
-
-/// output adapter for byte vectors
-template<typename CharType>
-class output_vector_adapter : public output_adapter_protocol<CharType>
-{
-  public:
-    explicit output_vector_adapter(std::vector<CharType>& vec) noexcept
-        : v(vec)
-    {}
-
-    void write_character(CharType c) override
-    {
-        v.push_back(c);
-    }
-
-    void write_characters(const CharType* s, std::size_t length) override
-    {
-        std::copy(s, s + length, std::back_inserter(v));
-    }
-
-  private:
-    std::vector<CharType>& v;
-};
-
-/// output adapter for output streams
-template<typename CharType>
-class output_stream_adapter : public output_adapter_protocol<CharType>
-{
-  public:
-    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept
-        : stream(s)
-    {}
-
-    void write_character(CharType c) override
-    {
-        stream.put(c);
-    }
-
-    void write_characters(const CharType* s, std::size_t length) override
-    {
-        stream.write(s, static_cast<std::streamsize>(length));
-    }
-
-  private:
-    std::basic_ostream<CharType>& stream;
-};
-
-/// output adapter for basic_string
-template<typename CharType, typename StringType = std::basic_string<CharType>>
-class output_string_adapter : public output_adapter_protocol<CharType>
-{
-  public:
-    explicit output_string_adapter(StringType& s) noexcept
-        : str(s)
-    {}
-
-    void write_character(CharType c) override
-    {
-        str.push_back(c);
-    }
-
-    void write_characters(const CharType* s, std::size_t length) override
-    {
-        str.append(s, length);
-    }
-
-  private:
-    StringType& str;
-};
-
-template<typename CharType, typename StringType = std::basic_string<CharType>>
-class output_adapter
-{
-  public:
-    output_adapter(std::vector<CharType>& vec)
-        : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}
-
-    output_adapter(std::basic_ostream<CharType>& s)
-        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
-
-    output_adapter(StringType& s)
-        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
-
-    operator output_adapter_t<CharType>()
-    {
-        return oa;
-    }
-
-  private:
-    output_adapter_t<CharType> oa = nullptr;
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/input/binary_reader.hpp>
-
-
-#include <algorithm> // generate_n
-#include <array> // array
-#include <cassert> // assert
-#include <cmath> // ldexp
-#include <cstddef> // size_t
-#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
-#include <cstdio> // snprintf
-#include <cstring> // memcpy
-#include <iterator> // back_inserter
-#include <limits> // numeric_limits
-#include <string> // char_traits, string
-#include <utility> // make_pair, move
-
-// #include <nlohmann/detail/input/input_adapters.hpp>
-
-// #include <nlohmann/detail/input/json_sax.hpp>
-
-// #include <nlohmann/detail/exceptions.hpp>
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-// #include <nlohmann/detail/meta/is_sax.hpp>
-
-// #include <nlohmann/detail/value_t.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-///////////////////
-// binary reader //
-///////////////////
-
-/*!
-@brief deserialization of CBOR, MessagePack, and UBJSON values
-*/
-template<typename BasicJsonType, typename SAX = json_sax_dom_parser<BasicJsonType>>
-class binary_reader
-{
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    using number_float_t = typename BasicJsonType::number_float_t;
-    using string_t = typename BasicJsonType::string_t;
-    using json_sax_t = SAX;
-
-  public:
-    /*!
-    @brief create a binary reader
-
-    @param[in] adapter  input adapter to read from
-    */
-    explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter))
-    {
-        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
-        assert(ia);
-    }
-
-    /*!
-    @param[in] format  the binary format to parse
-    @param[in] sax_    a SAX event processor
-    @param[in] strict  whether to expect the input to be consumed completed
-
-    @return
-    */
-    bool sax_parse(const input_format_t format,
-                   json_sax_t* sax_,
-                   const bool strict = true)
-    {
-        sax = sax_;
-        bool result = false;
-
-        switch (format)
-        {
-            case input_format_t::bson:
-                result = parse_bson_internal();
-                break;
-
-            case input_format_t::cbor:
-                result = parse_cbor_internal();
-                break;
-
-            case input_format_t::msgpack:
-                result = parse_msgpack_internal();
-                break;
-
-            case input_format_t::ubjson:
-                result = parse_ubjson_internal();
-                break;
-
-            // LCOV_EXCL_START
-            default:
-                assert(false);
-                // LCOV_EXCL_STOP
-        }
-
-        // strict mode: next byte must be EOF
-        if (result and strict)
-        {
-            if (format == input_format_t::ubjson)
-            {
-                get_ignore_noop();
-            }
-            else
-            {
-                get();
-            }
-
-            if (JSON_UNLIKELY(current != std::char_traits<char>::eof()))
-            {
-                return sax->parse_error(chars_read, get_token_string(),
-                                        parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value")));
-            }
-        }
-
-        return result;
-    }
-
-    /*!
-    @brief determine system byte order
-
-    @return true if and only if system's byte order is little endian
-
-    @note from http://stackoverflow.com/a/1001328/266378
-    */
-    static constexpr bool little_endianess(int num = 1) noexcept
-    {
-        return (*reinterpret_cast<char*>(&num) == 1);
-    }
-
-  private:
-    //////////
-    // BSON //
-    //////////
-
-    /*!
-    @brief Reads in a BSON-object and passes it to the SAX-parser.
-    @return whether a valid BSON-value was passed to the SAX parser
-    */
-    bool parse_bson_internal()
-    {
-        std::int32_t document_size;
-        get_number<std::int32_t, true>(input_format_t::bson, document_size);
-
-        if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1))))
-        {
-            return false;
-        }
-
-        if (JSON_UNLIKELY(not parse_bson_element_list(/*is_array*/false)))
-        {
-            return false;
-        }
-
-        return sax->end_object();
-    }
-
-    /*!
-    @brief Parses a C-style string from the BSON input.
-    @param[in, out] result  A reference to the string variable where the read
-                            string is to be stored.
-    @return `true` if the \x00-byte indicating the end of the string was
-             encountered before the EOF; false` indicates an unexpected EOF.
-    */
-    bool get_bson_cstr(string_t& result)
-    {
-        auto out = std::back_inserter(result);
-        while (true)
-        {
-            get();
-            if (JSON_UNLIKELY(not unexpect_eof(input_format_t::bson, "cstring")))
-            {
-                return false;
-            }
-            if (current == 0x00)
-            {
-                return true;
-            }
-            *out++ = static_cast<char>(current);
-        }
-
-        return true;
-    }
-
-    /*!
-    @brief Parses a zero-terminated string of length @a len from the BSON
-           input.
-    @param[in] len  The length (including the zero-byte at the end) of the
-                    string to be read.
-    @param[in, out] result  A reference to the string variable where the read
-                            string is to be stored.
-    @tparam NumberType The type of the length @a len
-    @pre len >= 1
-    @return `true` if the string was successfully parsed
-    */
-    template<typename NumberType>
-    bool get_bson_string(const NumberType len, string_t& result)
-    {
-        if (JSON_UNLIKELY(len < 1))
-        {
-            auto last_token = get_token_string();
-            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string")));
-        }
-
-        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) and get() != std::char_traits<char>::eof();
-    }
-
-    /*!
-    @brief Read a BSON document element of the given @a element_type.
-    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html
-    @param[in] element_type_parse_position The position in the input stream,
-               where the `element_type` was read.
-    @warning Not all BSON element types are supported yet. An unsupported
-             @a element_type will give rise to a parse_error.114:
-             Unsupported BSON record type 0x...
-    @return whether a valid BSON-object/array was passed to the SAX parser
-    */
-    bool parse_bson_element_internal(const int element_type,
-                                     const std::size_t element_type_parse_position)
-    {
-        switch (element_type)
-        {
-            case 0x01: // double
-            {
-                double number;
-                return get_number<double, true>(input_format_t::bson, number) and sax->number_float(static_cast<number_float_t>(number), "");
-            }
-
-            case 0x02: // string
-            {
-                std::int32_t len;
-                string_t value;
-                return get_number<std::int32_t, true>(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value);
-            }
-
-            case 0x03: // object
-            {
-                return parse_bson_internal();
-            }
-
-            case 0x04: // array
-            {
-                return parse_bson_array();
-            }
-
-            case 0x08: // boolean
-            {
-                return sax->boolean(get() != 0);
-            }
-
-            case 0x0A: // null
-            {
-                return sax->null();
-            }
-
-            case 0x10: // int32
-            {
-                std::int32_t value;
-                return get_number<std::int32_t, true>(input_format_t::bson, value) and sax->number_integer(value);
-            }
-
-            case 0x12: // int64
-            {
-                std::int64_t value;
-                return get_number<std::int64_t, true>(input_format_t::bson, value) and sax->number_integer(value);
-            }
-
-            default: // anything else not supported (yet)
-            {
-                char cr[3];
-                (std::snprintf)(cr, sizeof(cr), "%.2hhX", static_cast<unsigned char>(element_type));
-                return sax->parse_error(element_type_parse_position, std::string(cr), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr)));
-            }
-        }
-    }
-
-    /*!
-    @brief Read a BSON element list (as specified in the BSON-spec)
-
-    The same binary layout is used for objects and arrays, hence it must be
-    indicated with the argument @a is_array which one is expected
-    (true --> array, false --> object).
-
-    @param[in] is_array Determines if the element list being read is to be
-                        treated as an object (@a is_array == false), or as an
-                        array (@a is_array == true).
-    @return whether a valid BSON-object/array was passed to the SAX parser
-    */
-    bool parse_bson_element_list(const bool is_array)
-    {
-        string_t key;
-        while (int element_type = get())
-        {
-            if (JSON_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list")))
-            {
-                return false;
-            }
-
-            const std::size_t element_type_parse_position = chars_read;
-            if (JSON_UNLIKELY(not get_bson_cstr(key)))
-            {
-                return false;
-            }
-
-            if (not is_array)
-            {
-                if (not sax->key(key))
-                {
-                    return false;
-                }
-            }
-
-            if (JSON_UNLIKELY(not parse_bson_element_internal(element_type, element_type_parse_position)))
-            {
-                return false;
-            }
-
-            // get_bson_cstr only appends
-            key.clear();
-        }
-
-        return true;
-    }
-
-    /*!
-    @brief Reads an array from the BSON input and passes it to the SAX-parser.
-    @return whether a valid BSON-array was passed to the SAX parser
-    */
-    bool parse_bson_array()
-    {
-        std::int32_t document_size;
-        get_number<std::int32_t, true>(input_format_t::bson, document_size);
-
-        if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1))))
-        {
-            return false;
-        }
-
-        if (JSON_UNLIKELY(not parse_bson_element_list(/*is_array*/true)))
-        {
-            return false;
-        }
-
-        return sax->end_array();
-    }
-
-    //////////
-    // CBOR //
-    //////////
-
-    /*!
-    @param[in] get_char  whether a new character should be retrieved from the
-                         input (true, default) or whether the last read
-                         character should be considered instead
-
-    @return whether a valid CBOR value was passed to the SAX parser
-    */
-    bool parse_cbor_internal(const bool get_char = true)
-    {
-        switch (get_char ? get() : current)
-        {
-            // EOF
-            case std::char_traits<char>::eof():
-                return unexpect_eof(input_format_t::cbor, "value");
-
-            // Integer 0x00..0x17 (0..23)
-            case 0x00:
-            case 0x01:
-            case 0x02:
-            case 0x03:
-            case 0x04:
-            case 0x05:
-            case 0x06:
-            case 0x07:
-            case 0x08:
-            case 0x09:
-            case 0x0A:
-            case 0x0B:
-            case 0x0C:
-            case 0x0D:
-            case 0x0E:
-            case 0x0F:
-            case 0x10:
-            case 0x11:
-            case 0x12:
-            case 0x13:
-            case 0x14:
-            case 0x15:
-            case 0x16:
-            case 0x17:
-                return sax->number_unsigned(static_cast<number_unsigned_t>(current));
-
-            case 0x18: // Unsigned integer (one-byte uint8_t follows)
-            {
-                uint8_t number;
-                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);
-            }
-
-            case 0x19: // Unsigned integer (two-byte uint16_t follows)
-            {
-                uint16_t number;
-                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);
-            }
-
-            case 0x1A: // Unsigned integer (four-byte uint32_t follows)
-            {
-                uint32_t number;
-                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);
-            }
-
-            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
-            {
-                uint64_t number;
-                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);
-            }
-
-            // Negative integer -1-0x00..-1-0x17 (-1..-24)
-            case 0x20:
-            case 0x21:
-            case 0x22:
-            case 0x23:
-            case 0x24:
-            case 0x25:
-            case 0x26:
-            case 0x27:
-            case 0x28:
-            case 0x29:
-            case 0x2A:
-            case 0x2B:
-            case 0x2C:
-            case 0x2D:
-            case 0x2E:
-            case 0x2F:
-            case 0x30:
-            case 0x31:
-            case 0x32:
-            case 0x33:
-            case 0x34:
-            case 0x35:
-            case 0x36:
-            case 0x37:
-                return sax->number_integer(static_cast<int8_t>(0x20 - 1 - current));
-
-            case 0x38: // Negative integer (one-byte uint8_t follows)
-            {
-                uint8_t number;
-                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);
-            }
-
-            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
-            {
-                uint16_t number;
-                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);
-            }
-
-            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
-            {
-                uint32_t number;
-                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);
-            }
-
-            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
-            {
-                uint64_t number;
-                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1)
-                        - static_cast<number_integer_t>(number));
-            }
-
-            // UTF-8 string (0x00..0x17 bytes follow)
-            case 0x60:
-            case 0x61:
-            case 0x62:
-            case 0x63:
-            case 0x64:
-            case 0x65:
-            case 0x66:
-            case 0x67:
-            case 0x68:
-            case 0x69:
-            case 0x6A:
-            case 0x6B:
-            case 0x6C:
-            case 0x6D:
-            case 0x6E:
-            case 0x6F:
-            case 0x70:
-            case 0x71:
-            case 0x72:
-            case 0x73:
-            case 0x74:
-            case 0x75:
-            case 0x76:
-            case 0x77:
-            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
-            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
-            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
-            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
-            case 0x7F: // UTF-8 string (indefinite length)
-            {
-                string_t s;
-                return get_cbor_string(s) and sax->string(s);
-            }
-
-            // array (0x00..0x17 data items follow)
-            case 0x80:
-            case 0x81:
-            case 0x82:
-            case 0x83:
-            case 0x84:
-            case 0x85:
-            case 0x86:
-            case 0x87:
-            case 0x88:
-            case 0x89:
-            case 0x8A:
-            case 0x8B:
-            case 0x8C:
-            case 0x8D:
-            case 0x8E:
-            case 0x8F:
-            case 0x90:
-            case 0x91:
-            case 0x92:
-            case 0x93:
-            case 0x94:
-            case 0x95:
-            case 0x96:
-            case 0x97:
-                return get_cbor_array(static_cast<std::size_t>(current & 0x1F));
-
-            case 0x98: // array (one-byte uint8_t for n follows)
-            {
-                uint8_t len;
-                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));
-            }
-
-            case 0x99: // array (two-byte uint16_t for n follow)
-            {
-                uint16_t len;
-                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));
-            }
-
-            case 0x9A: // array (four-byte uint32_t for n follow)
-            {
-                uint32_t len;
-                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));
-            }
-
-            case 0x9B: // array (eight-byte uint64_t for n follow)
-            {
-                uint64_t len;
-                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));
-            }
-
-            case 0x9F: // array (indefinite length)
-                return get_cbor_array(std::size_t(-1));
-
-            // map (0x00..0x17 pairs of data items follow)
-            case 0xA0:
-            case 0xA1:
-            case 0xA2:
-            case 0xA3:
-            case 0xA4:
-            case 0xA5:
-            case 0xA6:
-            case 0xA7:
-            case 0xA8:
-            case 0xA9:
-            case 0xAA:
-            case 0xAB:
-            case 0xAC:
-            case 0xAD:
-            case 0xAE:
-            case 0xAF:
-            case 0xB0:
-            case 0xB1:
-            case 0xB2:
-            case 0xB3:
-            case 0xB4:
-            case 0xB5:
-            case 0xB6:
-            case 0xB7:
-                return get_cbor_object(static_cast<std::size_t>(current & 0x1F));
-
-            case 0xB8: // map (one-byte uint8_t for n follows)
-            {
-                uint8_t len;
-                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));
-            }
-
-            case 0xB9: // map (two-byte uint16_t for n follow)
-            {
-                uint16_t len;
-                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));
-            }
-
-            case 0xBA: // map (four-byte uint32_t for n follow)
-            {
-                uint32_t len;
-                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));
-            }
-
-            case 0xBB: // map (eight-byte uint64_t for n follow)
-            {
-                uint64_t len;
-                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));
-            }
-
-            case 0xBF: // map (indefinite length)
-                return get_cbor_object(std::size_t(-1));
-
-            case 0xF4: // false
-                return sax->boolean(false);
-
-            case 0xF5: // true
-                return sax->boolean(true);
-
-            case 0xF6: // null
-                return sax->null();
-
-            case 0xF9: // Half-Precision Float (two-byte IEEE 754)
-            {
-                const int byte1_raw = get();
-                if (JSON_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number")))
-                {
-                    return false;
-                }
-                const int byte2_raw = get();
-                if (JSON_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number")))
-                {
-                    return false;
-                }
-
-                const auto byte1 = static_cast<unsigned char>(byte1_raw);
-                const auto byte2 = static_cast<unsigned char>(byte2_raw);
-
-                // code from RFC 7049, Appendix D, Figure 3:
-                // As half-precision floating-point numbers were only added
-                // to IEEE 754 in 2008, today's programming platforms often
-                // still only have limited support for them. It is very
-                // easy to include at least decoding support for them even
-                // without such support. An example of a small decoder for
-                // half-precision floating-point numbers in the C language
-                // is shown in Fig. 3.
-                const int half = (byte1 << 8) + byte2;
-                const double val = [&half]
-                {
-                    const int exp = (half >> 10) & 0x1F;
-                    const int mant = half & 0x3FF;
-                    assert(0 <= exp and exp <= 32);
-                    assert(0 <= mant and mant <= 1024);
-                    switch (exp)
-                    {
-                        case 0:
-                            return std::ldexp(mant, -24);
-                        case 31:
-                            return (mant == 0)
-                            ? std::numeric_limits<double>::infinity()
-                            : std::numeric_limits<double>::quiet_NaN();
-                        default:
-                            return std::ldexp(mant + 1024, exp - 25);
-                    }
-                }();
-                return sax->number_float((half & 0x8000) != 0
-                                         ? static_cast<number_float_t>(-val)
-                                         : static_cast<number_float_t>(val), "");
-            }
-
-            case 0xFA: // Single-Precision Float (four-byte IEEE 754)
-            {
-                float number;
-                return get_number(input_format_t::cbor, number) and sax->number_float(static_cast<number_float_t>(number), "");
-            }
-
-            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
-            {
-                double number;
-                return get_number(input_format_t::cbor, number) and sax->number_float(static_cast<number_float_t>(number), "");
-            }
-
-            default: // anything else (0xFF is handled inside the other types)
-            {
-                auto last_token = get_token_string();
-                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value")));
-            }
-        }
-    }
-
-    /*!
-    @brief reads a CBOR string
-
-    This function first reads starting bytes to determine the expected
-    string length and then copies this number of bytes into a string.
-    Additionally, CBOR's strings with indefinite lengths are supported.
-
-    @param[out] result  created string
-
-    @return whether string creation completed
-    */
-    bool get_cbor_string(string_t& result)
-    {
-        if (JSON_UNLIKELY(not unexpect_eof(input_format_t::cbor, "string")))
-        {
-            return false;
-        }
-
-        switch (current)
-        {
-            // UTF-8 string (0x00..0x17 bytes follow)
-            case 0x60:
-            case 0x61:
-            case 0x62:
-            case 0x63:
-            case 0x64:
-            case 0x65:
-            case 0x66:
-            case 0x67:
-            case 0x68:
-            case 0x69:
-            case 0x6A:
-            case 0x6B:
-            case 0x6C:
-            case 0x6D:
-            case 0x6E:
-            case 0x6F:
-            case 0x70:
-            case 0x71:
-            case 0x72:
-            case 0x73:
-            case 0x74:
-            case 0x75:
-            case 0x76:
-            case 0x77:
-            {
-                return get_string(input_format_t::cbor, current & 0x1F, result);
-            }
-
-            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
-            {
-                uint8_t len;
-                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);
-            }
-
-            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
-            {
-                uint16_t len;
-                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);
-            }
-
-            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
-            {
-                uint32_t len;
-                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);
-            }
-
-            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
-            {
-                uint64_t len;
-                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);
-            }
-
-            case 0x7F: // UTF-8 string (indefinite length)
-            {
-                while (get() != 0xFF)
-                {
-                    string_t chunk;
-                    if (not get_cbor_string(chunk))
-                    {
-                        return false;
-                    }
-                    result.append(chunk);
-                }
-                return true;
-            }
-
-            default:
-            {
-                auto last_token = get_token_string();
-                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string")));
-            }
-        }
-    }
-
-    /*!
-    @param[in] len  the length of the array or std::size_t(-1) for an
-                    array of indefinite size
-    @return whether array creation completed
-    */
-    bool get_cbor_array(const std::size_t len)
-    {
-        if (JSON_UNLIKELY(not sax->start_array(len)))
-        {
-            return false;
-        }
-
-        if (len != std::size_t(-1))
-        {
-            for (std::size_t i = 0; i < len; ++i)
-            {
-                if (JSON_UNLIKELY(not parse_cbor_internal()))
-                {
-                    return false;
-                }
-            }
-        }
-        else
-        {
-            while (get() != 0xFF)
-            {
-                if (JSON_UNLIKELY(not parse_cbor_internal(false)))
-                {
-                    return false;
-                }
-            }
-        }
-
-        return sax->end_array();
-    }
-
-    /*!
-    @param[in] len  the length of the object or std::size_t(-1) for an
-                    object of indefinite size
-    @return whether object creation completed
-    */
-    bool get_cbor_object(const std::size_t len)
-    {
-        if (not JSON_UNLIKELY(sax->start_object(len)))
-        {
-            return false;
-        }
-
-        string_t key;
-        if (len != std::size_t(-1))
-        {
-            for (std::size_t i = 0; i < len; ++i)
-            {
-                get();
-                if (JSON_UNLIKELY(not get_cbor_string(key) or not sax->key(key)))
-                {
-                    return false;
-                }
-
-                if (JSON_UNLIKELY(not parse_cbor_internal()))
-                {
-                    return false;
-                }
-                key.clear();
-            }
-        }
-        else
-        {
-            while (get() != 0xFF)
-            {
-                if (JSON_UNLIKELY(not get_cbor_string(key) or not sax->key(key)))
-                {
-                    return false;
-                }
-
-                if (JSON_UNLIKELY(not parse_cbor_internal()))
-                {
-                    return false;
-                }
-                key.clear();
-            }
-        }
-
-        return sax->end_object();
-    }
-
-    /////////////
-    // MsgPack //
-    /////////////
-
-    /*!
-    @return whether a valid MessagePack value was passed to the SAX parser
-    */
-    bool parse_msgpack_internal()
-    {
-        switch (get())
-        {
-            // EOF
-            case std::char_traits<char>::eof():
-                return unexpect_eof(input_format_t::msgpack, "value");
-
-            // positive fixint
-            case 0x00:
-            case 0x01:
-            case 0x02:
-            case 0x03:
-            case 0x04:
-            case 0x05:
-            case 0x06:
-            case 0x07:
-            case 0x08:
-            case 0x09:
-            case 0x0A:
-            case 0x0B:
-            case 0x0C:
-            case 0x0D:
-            case 0x0E:
-            case 0x0F:
-            case 0x10:
-            case 0x11:
-            case 0x12:
-            case 0x13:
-            case 0x14:
-            case 0x15:
-            case 0x16:
-            case 0x17:
-            case 0x18:
-            case 0x19:
-            case 0x1A:
-            case 0x1B:
-            case 0x1C:
-            case 0x1D:
-            case 0x1E:
-            case 0x1F:
-            case 0x20:
-            case 0x21:
-            case 0x22:
-            case 0x23:
-            case 0x24:
-            case 0x25:
-            case 0x26:
-            case 0x27:
-            case 0x28:
-            case 0x29:
-            case 0x2A:
-            case 0x2B:
-            case 0x2C:
-            case 0x2D:
-            case 0x2E:
-            case 0x2F:
-            case 0x30:
-            case 0x31:
-            case 0x32:
-            case 0x33:
-            case 0x34:
-            case 0x35:
-            case 0x36:
-            case 0x37:
-            case 0x38:
-            case 0x39:
-            case 0x3A:
-            case 0x3B:
-            case 0x3C:
-            case 0x3D:
-            case 0x3E:
-            case 0x3F:
-            case 0x40:
-            case 0x41:
-            case 0x42:
-            case 0x43:
-            case 0x44:
-            case 0x45:
-            case 0x46:
-            case 0x47:
-            case 0x48:
-            case 0x49:
-            case 0x4A:
-            case 0x4B:
-            case 0x4C:
-            case 0x4D:
-            case 0x4E:
-            case 0x4F:
-            case 0x50:
-            case 0x51:
-            case 0x52:
-            case 0x53:
-            case 0x54:
-            case 0x55:
-            case 0x56:
-            case 0x57:
-            case 0x58:
-            case 0x59:
-            case 0x5A:
-            case 0x5B:
-            case 0x5C:
-            case 0x5D:
-            case 0x5E:
-            case 0x5F:
-            case 0x60:
-            case 0x61:
-            case 0x62:
-            case 0x63:
-            case 0x64:
-            case 0x65:
-            case 0x66:
-            case 0x67:
-            case 0x68:
-            case 0x69:
-            case 0x6A:
-            case 0x6B:
-            case 0x6C:
-            case 0x6D:
-            case 0x6E:
-            case 0x6F:
-            case 0x70:
-            case 0x71:
-            case 0x72:
-            case 0x73:
-            case 0x74:
-            case 0x75:
-            case 0x76:
-            case 0x77:
-            case 0x78:
-            case 0x79:
-            case 0x7A:
-            case 0x7B:
-            case 0x7C:
-            case 0x7D:
-            case 0x7E:
-            case 0x7F:
-                return sax->number_unsigned(static_cast<number_unsigned_t>(current));
-
-            // fixmap
-            case 0x80:
-            case 0x81:
-            case 0x82:
-            case 0x83:
-            case 0x84:
-            case 0x85:
-            case 0x86:
-            case 0x87:
-            case 0x88:
-            case 0x89:
-            case 0x8A:
-            case 0x8B:
-            case 0x8C:
-            case 0x8D:
-            case 0x8E:
-            case 0x8F:
-                return get_msgpack_object(static_cast<std::size_t>(current & 0x0F));
-
-            // fixarray
-            case 0x90:
-            case 0x91:
-            case 0x92:
-            case 0x93:
-            case 0x94:
-            case 0x95:
-            case 0x96:
-            case 0x97:
-            case 0x98:
-            case 0x99:
-            case 0x9A:
-            case 0x9B:
-            case 0x9C:
-            case 0x9D:
-            case 0x9E:
-            case 0x9F:
-                return get_msgpack_array(static_cast<std::size_t>(current & 0x0F));
-
-            // fixstr
-            case 0xA0:
-            case 0xA1:
-            case 0xA2:
-            case 0xA3:
-            case 0xA4:
-            case 0xA5:
-            case 0xA6:
-            case 0xA7:
-            case 0xA8:
-            case 0xA9:
-            case 0xAA:
-            case 0xAB:
-            case 0xAC:
-            case 0xAD:
-            case 0xAE:
-            case 0xAF:
-            case 0xB0:
-            case 0xB1:
-            case 0xB2:
-            case 0xB3:
-            case 0xB4:
-            case 0xB5:
-            case 0xB6:
-            case 0xB7:
-            case 0xB8:
-            case 0xB9:
-            case 0xBA:
-            case 0xBB:
-            case 0xBC:
-            case 0xBD:
-            case 0xBE:
-            case 0xBF:
-            {
-                string_t s;
-                return get_msgpack_string(s) and sax->string(s);
-            }
-
-            case 0xC0: // nil
-                return sax->null();
-
-            case 0xC2: // false
-                return sax->boolean(false);
-
-            case 0xC3: // true
-                return sax->boolean(true);
-
-            case 0xCA: // float 32
-            {
-                float number;
-                return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), "");
-            }
-
-            case 0xCB: // float 64
-            {
-                double number;
-                return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), "");
-            }
-
-            case 0xCC: // uint 8
-            {
-                uint8_t number;
-                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);
-            }
-
-            case 0xCD: // uint 16
-            {
-                uint16_t number;
-                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);
-            }
-
-            case 0xCE: // uint 32
-            {
-                uint32_t number;
-                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);
-            }
-
-            case 0xCF: // uint 64
-            {
-                uint64_t number;
-                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);
-            }
-
-            case 0xD0: // int 8
-            {
-                int8_t number;
-                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);
-            }
-
-            case 0xD1: // int 16
-            {
-                int16_t number;
-                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);
-            }
-
-            case 0xD2: // int 32
-            {
-                int32_t number;
-                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);
-            }
-
-            case 0xD3: // int 64
-            {
-                int64_t number;
-                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);
-            }
-
-            case 0xD9: // str 8
-            case 0xDA: // str 16
-            case 0xDB: // str 32
-            {
-                string_t s;
-                return get_msgpack_string(s) and sax->string(s);
-            }
-
-            case 0xDC: // array 16
-            {
-                uint16_t len;
-                return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len));
-            }
-
-            case 0xDD: // array 32
-            {
-                uint32_t len;
-                return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len));
-            }
-
-            case 0xDE: // map 16
-            {
-                uint16_t len;
-                return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len));
-            }
-
-            case 0xDF: // map 32
-            {
-                uint32_t len;
-                return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len));
-            }
-
-            // negative fixint
-            case 0xE0:
-            case 0xE1:
-            case 0xE2:
-            case 0xE3:
-            case 0xE4:
-            case 0xE5:
-            case 0xE6:
-            case 0xE7:
-            case 0xE8:
-            case 0xE9:
-            case 0xEA:
-            case 0xEB:
-            case 0xEC:
-            case 0xED:
-            case 0xEE:
-            case 0xEF:
-            case 0xF0:
-            case 0xF1:
-            case 0xF2:
-            case 0xF3:
-            case 0xF4:
-            case 0xF5:
-            case 0xF6:
-            case 0xF7:
-            case 0xF8:
-            case 0xF9:
-            case 0xFA:
-            case 0xFB:
-            case 0xFC:
-            case 0xFD:
-            case 0xFE:
-            case 0xFF:
-                return sax->number_integer(static_cast<int8_t>(current));
-
-            default: // anything else
-            {
-                auto last_token = get_token_string();
-                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value")));
-            }
-        }
-    }
-
-    /*!
-    @brief reads a MessagePack string
-
-    This function first reads starting bytes to determine the expected
-    string length and then copies this number of bytes into a string.
-
-    @param[out] result  created string
-
-    @return whether string creation completed
-    */
-    bool get_msgpack_string(string_t& result)
-    {
-        if (JSON_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "string")))
-        {
-            return false;
-        }
-
-        switch (current)
-        {
-            // fixstr
-            case 0xA0:
-            case 0xA1:
-            case 0xA2:
-            case 0xA3:
-            case 0xA4:
-            case 0xA5:
-            case 0xA6:
-            case 0xA7:
-            case 0xA8:
-            case 0xA9:
-            case 0xAA:
-            case 0xAB:
-            case 0xAC:
-            case 0xAD:
-            case 0xAE:
-            case 0xAF:
-            case 0xB0:
-            case 0xB1:
-            case 0xB2:
-            case 0xB3:
-            case 0xB4:
-            case 0xB5:
-            case 0xB6:
-            case 0xB7:
-            case 0xB8:
-            case 0xB9:
-            case 0xBA:
-            case 0xBB:
-            case 0xBC:
-            case 0xBD:
-            case 0xBE:
-            case 0xBF:
-            {
-                return get_string(input_format_t::msgpack, current & 0x1F, result);
-            }
-
-            case 0xD9: // str 8
-            {
-                uint8_t len;
-                return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);
-            }
-
-            case 0xDA: // str 16
-            {
-                uint16_t len;
-                return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);
-            }
-
-            case 0xDB: // str 32
-            {
-                uint32_t len;
-                return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);
-            }
-
-            default:
-            {
-                auto last_token = get_token_string();
-                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string")));
-            }
-        }
-    }
-
-    /*!
-    @param[in] len  the length of the array
-    @return whether array creation completed
-    */
-    bool get_msgpack_array(const std::size_t len)
-    {
-        if (JSON_UNLIKELY(not sax->start_array(len)))
-        {
-            return false;
-        }
-
-        for (std::size_t i = 0; i < len; ++i)
-        {
-            if (JSON_UNLIKELY(not parse_msgpack_internal()))
-            {
-                return false;
-            }
-        }
-
-        return sax->end_array();
-    }
-
-    /*!
-    @param[in] len  the length of the object
-    @return whether object creation completed
-    */
-    bool get_msgpack_object(const std::size_t len)
-    {
-        if (JSON_UNLIKELY(not sax->start_object(len)))
-        {
-            return false;
-        }
-
-        string_t key;
-        for (std::size_t i = 0; i < len; ++i)
-        {
-            get();
-            if (JSON_UNLIKELY(not get_msgpack_string(key) or not sax->key(key)))
-            {
-                return false;
-            }
-
-            if (JSON_UNLIKELY(not parse_msgpack_internal()))
-            {
-                return false;
-            }
-            key.clear();
-        }
-
-        return sax->end_object();
-    }
-
-    ////////////
-    // UBJSON //
-    ////////////
-
-    /*!
-    @param[in] get_char  whether a new character should be retrieved from the
-                         input (true, default) or whether the last read
-                         character should be considered instead
-
-    @return whether a valid UBJSON value was passed to the SAX parser
-    */
-    bool parse_ubjson_internal(const bool get_char = true)
-    {
-        return get_ubjson_value(get_char ? get_ignore_noop() : current);
-    }
-
-    /*!
-    @brief reads a UBJSON string
-
-    This function is either called after reading the 'S' byte explicitly
-    indicating a string, or in case of an object key where the 'S' byte can be
-    left out.
-
-    @param[out] result   created string
-    @param[in] get_char  whether a new character should be retrieved from the
-                         input (true, default) or whether the last read
-                         character should be considered instead
-
-    @return whether string creation completed
-    */
-    bool get_ubjson_string(string_t& result, const bool get_char = true)
-    {
-        if (get_char)
-        {
-            get();  // TODO: may we ignore N here?
-        }
-
-        if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value")))
-        {
-            return false;
-        }
-
-        switch (current)
-        {
-            case 'U':
-            {
-                uint8_t len;
-                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
-            }
-
-            case 'i':
-            {
-                int8_t len;
-                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
-            }
-
-            case 'I':
-            {
-                int16_t len;
-                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
-            }
-
-            case 'l':
-            {
-                int32_t len;
-                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
-            }
-
-            case 'L':
-            {
-                int64_t len;
-                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
-            }
-
-            default:
-                auto last_token = get_token_string();
-                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string")));
-        }
-    }
-
-    /*!
-    @param[out] result  determined size
-    @return whether size determination completed
-    */
-    bool get_ubjson_size_value(std::size_t& result)
-    {
-        switch (get_ignore_noop())
-        {
-            case 'U':
-            {
-                uint8_t number;
-                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))
-                {
-                    return false;
-                }
-                result = static_cast<std::size_t>(number);
-                return true;
-            }
-
-            case 'i':
-            {
-                int8_t number;
-                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))
-                {
-                    return false;
-                }
-                result = static_cast<std::size_t>(number);
-                return true;
-            }
-
-            case 'I':
-            {
-                int16_t number;
-                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))
-                {
-                    return false;
-                }
-                result = static_cast<std::size_t>(number);
-                return true;
-            }
-
-            case 'l':
-            {
-                int32_t number;
-                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))
-                {
-                    return false;
-                }
-                result = static_cast<std::size_t>(number);
-                return true;
-            }
-
-            case 'L':
-            {
-                int64_t number;
-                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))
-                {
-                    return false;
-                }
-                result = static_cast<std::size_t>(number);
-                return true;
-            }
-
-            default:
-            {
-                auto last_token = get_token_string();
-                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size")));
-            }
-        }
-    }
-
-    /*!
-    @brief determine the type and size for a container
-
-    In the optimized UBJSON format, a type and a size can be provided to allow
-    for a more compact representation.
-
-    @param[out] result  pair of the size and the type
-
-    @return whether pair creation completed
-    */
-    bool get_ubjson_size_type(std::pair<std::size_t, int>& result)
-    {
-        result.first = string_t::npos; // size
-        result.second = 0; // type
-
-        get_ignore_noop();
-
-        if (current == '$')
-        {
-            result.second = get();  // must not ignore 'N', because 'N' maybe the type
-            if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "type")))
-            {
-                return false;
-            }
-
-            get_ignore_noop();
-            if (JSON_UNLIKELY(current != '#'))
-            {
-                if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value")))
-                {
-                    return false;
-                }
-                auto last_token = get_token_string();
-                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size")));
-            }
-
-            return get_ubjson_size_value(result.first);
-        }
-        else if (current == '#')
-        {
-            return get_ubjson_size_value(result.first);
-        }
-        return true;
-    }
-
-    /*!
-    @param prefix  the previously read or set type prefix
-    @return whether value creation completed
-    */
-    bool get_ubjson_value(const int prefix)
-    {
-        switch (prefix)
-        {
-            case std::char_traits<char>::eof():  // EOF
-                return unexpect_eof(input_format_t::ubjson, "value");
-
-            case 'T':  // true
-                return sax->boolean(true);
-            case 'F':  // false
-                return sax->boolean(false);
-
-            case 'Z':  // null
-                return sax->null();
-
-            case 'U':
-            {
-                uint8_t number;
-                return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number);
-            }
-
-            case 'i':
-            {
-                int8_t number;
-                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);
-            }
-
-            case 'I':
-            {
-                int16_t number;
-                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);
-            }
-
-            case 'l':
-            {
-                int32_t number;
-                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);
-            }
-
-            case 'L':
-            {
-                int64_t number;
-                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);
-            }
-
-            case 'd':
-            {
-                float number;
-                return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), "");
-            }
-
-            case 'D':
-            {
-                double number;
-                return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), "");
-            }
-
-            case 'C':  // char
-            {
-                get();
-                if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "char")))
-                {
-                    return false;
-                }
-                if (JSON_UNLIKELY(current > 127))
-                {
-                    auto last_token = get_token_string();
-                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char")));
-                }
-                string_t s(1, static_cast<char>(current));
-                return sax->string(s);
-            }
-
-            case 'S':  // string
-            {
-                string_t s;
-                return get_ubjson_string(s) and sax->string(s);
-            }
-
-            case '[':  // array
-                return get_ubjson_array();
-
-            case '{':  // object
-                return get_ubjson_object();
-
-            default: // anything else
-            {
-                auto last_token = get_token_string();
-                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value")));
-            }
-        }
-    }
-
-    /*!
-    @return whether array creation completed
-    */
-    bool get_ubjson_array()
-    {
-        std::pair<std::size_t, int> size_and_type;
-        if (JSON_UNLIKELY(not get_ubjson_size_type(size_and_type)))
-        {
-            return false;
-        }
-
-        if (size_and_type.first != string_t::npos)
-        {
-            if (JSON_UNLIKELY(not sax->start_array(size_and_type.first)))
-            {
-                return false;
-            }
-
-            if (size_and_type.second != 0)
-            {
-                if (size_and_type.second != 'N')
-                {
-                    for (std::size_t i = 0; i < size_and_type.first; ++i)
-                    {
-                        if (JSON_UNLIKELY(not get_ubjson_value(size_and_type.second)))
-                        {
-                            return false;
-                        }
-                    }
-                }
-            }
-            else
-            {
-                for (std::size_t i = 0; i < size_and_type.first; ++i)
-                {
-                    if (JSON_UNLIKELY(not parse_ubjson_internal()))
-                    {
-                        return false;
-                    }
-                }
-            }
-        }
-        else
-        {
-            if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1))))
-            {
-                return false;
-            }
-
-            while (current != ']')
-            {
-                if (JSON_UNLIKELY(not parse_ubjson_internal(false)))
-                {
-                    return false;
-                }
-                get_ignore_noop();
-            }
-        }
-
-        return sax->end_array();
-    }
-
-    /*!
-    @return whether object creation completed
-    */
-    bool get_ubjson_object()
-    {
-        std::pair<std::size_t, int> size_and_type;
-        if (JSON_UNLIKELY(not get_ubjson_size_type(size_and_type)))
-        {
-            return false;
-        }
-
-        string_t key;
-        if (size_and_type.first != string_t::npos)
-        {
-            if (JSON_UNLIKELY(not sax->start_object(size_and_type.first)))
-            {
-                return false;
-            }
-
-            if (size_and_type.second != 0)
-            {
-                for (std::size_t i = 0; i < size_and_type.first; ++i)
-                {
-                    if (JSON_UNLIKELY(not get_ubjson_string(key) or not sax->key(key)))
-                    {
-                        return false;
-                    }
-                    if (JSON_UNLIKELY(not get_ubjson_value(size_and_type.second)))
-                    {
-                        return false;
-                    }
-                    key.clear();
-                }
-            }
-            else
-            {
-                for (std::size_t i = 0; i < size_and_type.first; ++i)
-                {
-                    if (JSON_UNLIKELY(not get_ubjson_string(key) or not sax->key(key)))
-                    {
-                        return false;
-                    }
-                    if (JSON_UNLIKELY(not parse_ubjson_internal()))
-                    {
-                        return false;
-                    }
-                    key.clear();
-                }
-            }
-        }
-        else
-        {
-            if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1))))
-            {
-                return false;
-            }
-
-            while (current != '}')
-            {
-                if (JSON_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key)))
-                {
-                    return false;
-                }
-                if (JSON_UNLIKELY(not parse_ubjson_internal()))
-                {
-                    return false;
-                }
-                get_ignore_noop();
-                key.clear();
-            }
-        }
-
-        return sax->end_object();
-    }
-
-    ///////////////////////
-    // Utility functions //
-    ///////////////////////
-
-    /*!
-    @brief get next character from the input
-
-    This function provides the interface to the used input adapter. It does
-    not throw in case the input reached EOF, but returns a -'ve valued
-    `std::char_traits<char>::eof()` in that case.
-
-    @return character read from the input
-    */
-    int get()
-    {
-        ++chars_read;
-        return (current = ia->get_character());
-    }
-
-    /*!
-    @return character read from the input after ignoring all 'N' entries
-    */
-    int get_ignore_noop()
-    {
-        do
-        {
-            get();
-        }
-        while (current == 'N');
-
-        return current;
-    }
-
-    /*
-    @brief read a number from the input
-
-    @tparam NumberType the type of the number
-    @param[in] format   the current format (for diagnostics)
-    @param[out] result  number of type @a NumberType
-
-    @return whether conversion completed
-
-    @note This function needs to respect the system's endianess, because
-          bytes in CBOR, MessagePack, and UBJSON are stored in network order
-          (big endian) and therefore need reordering on little endian systems.
-    */
-    template<typename NumberType, bool InputIsLittleEndian = false>
-    bool get_number(const input_format_t format, NumberType& result)
-    {
-        // step 1: read input into array with system's byte order
-        std::array<uint8_t, sizeof(NumberType)> vec;
-        for (std::size_t i = 0; i < sizeof(NumberType); ++i)
-        {
-            get();
-            if (JSON_UNLIKELY(not unexpect_eof(format, "number")))
-            {
-                return false;
-            }
-
-            // reverse byte order prior to conversion if necessary
-            if (is_little_endian && !InputIsLittleEndian)
-            {
-                vec[sizeof(NumberType) - i - 1] = static_cast<uint8_t>(current);
-            }
-            else
-            {
-                vec[i] = static_cast<uint8_t>(current); // LCOV_EXCL_LINE
-            }
-        }
-
-        // step 2: convert array into number of type T and return
-        std::memcpy(&result, vec.data(), sizeof(NumberType));
-        return true;
-    }
-
-    /*!
-    @brief create a string by reading characters from the input
-
-    @tparam NumberType the type of the number
-    @param[in] format the current format (for diagnostics)
-    @param[in] len number of characters to read
-    @param[out] result string created by reading @a len bytes
-
-    @return whether string creation completed
-
-    @note We can not reserve @a len bytes for the result, because @a len
-          may be too large. Usually, @ref unexpect_eof() detects the end of
-          the input before we run out of string memory.
-    */
-    template<typename NumberType>
-    bool get_string(const input_format_t format,
-                    const NumberType len,
-                    string_t& result)
-    {
-        bool success = true;
-        std::generate_n(std::back_inserter(result), len, [this, &success, &format]()
-        {
-            get();
-            if (JSON_UNLIKELY(not unexpect_eof(format, "string")))
-            {
-                success = false;
-            }
-            return static_cast<char>(current);
-        });
-        return success;
-    }
-
-    /*!
-    @param[in] format   the current format (for diagnostics)
-    @param[in] context  further context information (for diagnostics)
-    @return whether the last read character is not EOF
-    */
-    bool unexpect_eof(const input_format_t format, const char* context) const
-    {
-        if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))
-        {
-            return sax->parse_error(chars_read, "<end of file>",
-                                    parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context)));
-        }
-        return true;
-    }
-
-    /*!
-    @return a string representation of the last read byte
-    */
-    std::string get_token_string() const
-    {
-        char cr[3];
-        (std::snprintf)(cr, 3, "%.2hhX", static_cast<unsigned char>(current));
-        return std::string{cr};
-    }
-
-    /*!
-    @param[in] format   the current format
-    @param[in] detail   a detailed error message
-    @param[in] context  further contect information
-    @return a message string to use in the parse_error exceptions
-    */
-    std::string exception_message(const input_format_t format,
-                                  const std::string& detail,
-                                  const std::string& context) const
-    {
-        std::string error_msg = "syntax error while parsing ";
-
-        switch (format)
-        {
-            case input_format_t::cbor:
-                error_msg += "CBOR";
-                break;
-
-            case input_format_t::msgpack:
-                error_msg += "MessagePack";
-                break;
-
-            case input_format_t::ubjson:
-                error_msg += "UBJSON";
-                break;
-
-            case input_format_t::bson:
-                error_msg += "BSON";
-                break;
-
-            // LCOV_EXCL_START
-            default:
-                assert(false);
-                // LCOV_EXCL_STOP
-        }
-
-        return error_msg + " " + context + ": " + detail;
-    }
-
-  private:
-    /// input adapter
-    input_adapter_t ia = nullptr;
-
-    /// the current character
-    int current = std::char_traits<char>::eof();
-
-    /// the number of characters read
-    std::size_t chars_read = 0;
-
-    /// whether we can assume little endianess
-    const bool is_little_endian = little_endianess();
-
-    /// the SAX parser
-    json_sax_t* sax = nullptr;
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/output/binary_writer.hpp>
-
-
-#include <algorithm> // reverse
-#include <array> // array
-#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
-#include <cstring> // memcpy
-#include <limits> // numeric_limits
-
-// #include <nlohmann/detail/input/binary_reader.hpp>
-
-// #include <nlohmann/detail/output/output_adapters.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-///////////////////
-// binary writer //
-///////////////////
-
-/*!
-@brief serialization to CBOR and MessagePack values
-*/
-template<typename BasicJsonType, typename CharType>
-class binary_writer
-{
-    using string_t = typename BasicJsonType::string_t;
-
-  public:
-    /*!
-    @brief create a binary writer
-
-    @param[in] adapter  output adapter to write to
-    */
-    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
-    {
-        assert(oa);
-    }
-
-    /*!
-    @param[in] j  JSON value to serialize
-    @pre       j.type() == value_t::object
-    */
-    void write_bson(const BasicJsonType& j)
-    {
-        switch (j.type())
-        {
-            case value_t::object:
-            {
-                write_bson_object(*j.m_value.object);
-                break;
-            }
-
-            default:
-            {
-                JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name())));
-            }
-        }
-    }
-
-    /*!
-    @param[in] j  JSON value to serialize
-    */
-    void write_cbor(const BasicJsonType& j)
-    {
-        switch (j.type())
-        {
-            case value_t::null:
-            {
-                oa->write_character(to_char_type(0xF6));
-                break;
-            }
-
-            case value_t::boolean:
-            {
-                oa->write_character(j.m_value.boolean
-                                    ? to_char_type(0xF5)
-                                    : to_char_type(0xF4));
-                break;
-            }
-
-            case value_t::number_integer:
-            {
-                if (j.m_value.number_integer >= 0)
-                {
-                    // CBOR does not differentiate between positive signed
-                    // integers and unsigned integers. Therefore, we used the
-                    // code from the value_t::number_unsigned case here.
-                    if (j.m_value.number_integer <= 0x17)
-                    {
-                        write_number(static_cast<uint8_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
-                    {
-                        oa->write_character(to_char_type(0x18));
-                        write_number(static_cast<uint8_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())
-                    {
-                        oa->write_character(to_char_type(0x19));
-                        write_number(static_cast<uint16_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())
-                    {
-                        oa->write_character(to_char_type(0x1A));
-                        write_number(static_cast<uint32_t>(j.m_value.number_integer));
-                    }
-                    else
-                    {
-                        oa->write_character(to_char_type(0x1B));
-                        write_number(static_cast<uint64_t>(j.m_value.number_integer));
-                    }
-                }
-                else
-                {
-                    // The conversions below encode the sign in the first
-                    // byte, and the value is converted to a positive number.
-                    const auto positive_number = -1 - j.m_value.number_integer;
-                    if (j.m_value.number_integer >= -24)
-                    {
-                        write_number(static_cast<uint8_t>(0x20 + positive_number));
-                    }
-                    else if (positive_number <= (std::numeric_limits<uint8_t>::max)())
-                    {
-                        oa->write_character(to_char_type(0x38));
-                        write_number(static_cast<uint8_t>(positive_number));
-                    }
-                    else if (positive_number <= (std::numeric_limits<uint16_t>::max)())
-                    {
-                        oa->write_character(to_char_type(0x39));
-                        write_number(static_cast<uint16_t>(positive_number));
-                    }
-                    else if (positive_number <= (std::numeric_limits<uint32_t>::max)())
-                    {
-                        oa->write_character(to_char_type(0x3A));
-                        write_number(static_cast<uint32_t>(positive_number));
-                    }
-                    else
-                    {
-                        oa->write_character(to_char_type(0x3B));
-                        write_number(static_cast<uint64_t>(positive_number));
-                    }
-                }
-                break;
-            }
-
-            case value_t::number_unsigned:
-            {
-                if (j.m_value.number_unsigned <= 0x17)
-                {
-                    write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
-                }
-                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x18));
-                    write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
-                }
-                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x19));
-                    write_number(static_cast<uint16_t>(j.m_value.number_unsigned));
-                }
-                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x1A));
-                    write_number(static_cast<uint32_t>(j.m_value.number_unsigned));
-                }
-                else
-                {
-                    oa->write_character(to_char_type(0x1B));
-                    write_number(static_cast<uint64_t>(j.m_value.number_unsigned));
-                }
-                break;
-            }
-
-            case value_t::number_float:
-            {
-                oa->write_character(get_cbor_float_prefix(j.m_value.number_float));
-                write_number(j.m_value.number_float);
-                break;
-            }
-
-            case value_t::string:
-            {
-                // step 1: write control byte and the string length
-                const auto N = j.m_value.string->size();
-                if (N <= 0x17)
-                {
-                    write_number(static_cast<uint8_t>(0x60 + N));
-                }
-                else if (N <= (std::numeric_limits<uint8_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x78));
-                    write_number(static_cast<uint8_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint16_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x79));
-                    write_number(static_cast<uint16_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint32_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x7A));
-                    write_number(static_cast<uint32_t>(N));
-                }
-                // LCOV_EXCL_START
-                else if (N <= (std::numeric_limits<uint64_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x7B));
-                    write_number(static_cast<uint64_t>(N));
-                }
-                // LCOV_EXCL_STOP
-
-                // step 2: write the string
-                oa->write_characters(
-                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
-                    j.m_value.string->size());
-                break;
-            }
-
-            case value_t::array:
-            {
-                // step 1: write control byte and the array size
-                const auto N = j.m_value.array->size();
-                if (N <= 0x17)
-                {
-                    write_number(static_cast<uint8_t>(0x80 + N));
-                }
-                else if (N <= (std::numeric_limits<uint8_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x98));
-                    write_number(static_cast<uint8_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint16_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x99));
-                    write_number(static_cast<uint16_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint32_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x9A));
-                    write_number(static_cast<uint32_t>(N));
-                }
-                // LCOV_EXCL_START
-                else if (N <= (std::numeric_limits<uint64_t>::max)())
-                {
-                    oa->write_character(to_char_type(0x9B));
-                    write_number(static_cast<uint64_t>(N));
-                }
-                // LCOV_EXCL_STOP
-
-                // step 2: write each element
-                for (const auto& el : *j.m_value.array)
-                {
-                    write_cbor(el);
-                }
-                break;
-            }
-
-            case value_t::object:
-            {
-                // step 1: write control byte and the object size
-                const auto N = j.m_value.object->size();
-                if (N <= 0x17)
-                {
-                    write_number(static_cast<uint8_t>(0xA0 + N));
-                }
-                else if (N <= (std::numeric_limits<uint8_t>::max)())
-                {
-                    oa->write_character(to_char_type(0xB8));
-                    write_number(static_cast<uint8_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint16_t>::max)())
-                {
-                    oa->write_character(to_char_type(0xB9));
-                    write_number(static_cast<uint16_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint32_t>::max)())
-                {
-                    oa->write_character(to_char_type(0xBA));
-                    write_number(static_cast<uint32_t>(N));
-                }
-                // LCOV_EXCL_START
-                else if (N <= (std::numeric_limits<uint64_t>::max)())
-                {
-                    oa->write_character(to_char_type(0xBB));
-                    write_number(static_cast<uint64_t>(N));
-                }
-                // LCOV_EXCL_STOP
-
-                // step 2: write each element
-                for (const auto& el : *j.m_value.object)
-                {
-                    write_cbor(el.first);
-                    write_cbor(el.second);
-                }
-                break;
-            }
-
-            default:
-                break;
-        }
-    }
-
-    /*!
-    @param[in] j  JSON value to serialize
-    */
-    void write_msgpack(const BasicJsonType& j)
-    {
-        switch (j.type())
-        {
-            case value_t::null: // nil
-            {
-                oa->write_character(to_char_type(0xC0));
-                break;
-            }
-
-            case value_t::boolean: // true and false
-            {
-                oa->write_character(j.m_value.boolean
-                                    ? to_char_type(0xC3)
-                                    : to_char_type(0xC2));
-                break;
-            }
-
-            case value_t::number_integer:
-            {
-                if (j.m_value.number_integer >= 0)
-                {
-                    // MessagePack does not differentiate between positive
-                    // signed integers and unsigned integers. Therefore, we used
-                    // the code from the value_t::number_unsigned case here.
-                    if (j.m_value.number_unsigned < 128)
-                    {
-                        // positive fixnum
-                        write_number(static_cast<uint8_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
-                    {
-                        // uint 8
-                        oa->write_character(to_char_type(0xCC));
-                        write_number(static_cast<uint8_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
-                    {
-                        // uint 16
-                        oa->write_character(to_char_type(0xCD));
-                        write_number(static_cast<uint16_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
-                    {
-                        // uint 32
-                        oa->write_character(to_char_type(0xCE));
-                        write_number(static_cast<uint32_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
-                    {
-                        // uint 64
-                        oa->write_character(to_char_type(0xCF));
-                        write_number(static_cast<uint64_t>(j.m_value.number_integer));
-                    }
-                }
-                else
-                {
-                    if (j.m_value.number_integer >= -32)
-                    {
-                        // negative fixnum
-                        write_number(static_cast<int8_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and
-                             j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
-                    {
-                        // int 8
-                        oa->write_character(to_char_type(0xD0));
-                        write_number(static_cast<int8_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and
-                             j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
-                    {
-                        // int 16
-                        oa->write_character(to_char_type(0xD1));
-                        write_number(static_cast<int16_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and
-                             j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
-                    {
-                        // int 32
-                        oa->write_character(to_char_type(0xD2));
-                        write_number(static_cast<int32_t>(j.m_value.number_integer));
-                    }
-                    else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and
-                             j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())
-                    {
-                        // int 64
-                        oa->write_character(to_char_type(0xD3));
-                        write_number(static_cast<int64_t>(j.m_value.number_integer));
-                    }
-                }
-                break;
-            }
-
-            case value_t::number_unsigned:
-            {
-                if (j.m_value.number_unsigned < 128)
-                {
-                    // positive fixnum
-                    write_number(static_cast<uint8_t>(j.m_value.number_integer));
-                }
-                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
-                {
-                    // uint 8
-                    oa->write_character(to_char_type(0xCC));
-                    write_number(static_cast<uint8_t>(j.m_value.number_integer));
-                }
-                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
-                {
-                    // uint 16
-                    oa->write_character(to_char_type(0xCD));
-                    write_number(static_cast<uint16_t>(j.m_value.number_integer));
-                }
-                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
-                {
-                    // uint 32
-                    oa->write_character(to_char_type(0xCE));
-                    write_number(static_cast<uint32_t>(j.m_value.number_integer));
-                }
-                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
-                {
-                    // uint 64
-                    oa->write_character(to_char_type(0xCF));
-                    write_number(static_cast<uint64_t>(j.m_value.number_integer));
-                }
-                break;
-            }
-
-            case value_t::number_float:
-            {
-                oa->write_character(get_msgpack_float_prefix(j.m_value.number_float));
-                write_number(j.m_value.number_float);
-                break;
-            }
-
-            case value_t::string:
-            {
-                // step 1: write control byte and the string length
-                const auto N = j.m_value.string->size();
-                if (N <= 31)
-                {
-                    // fixstr
-                    write_number(static_cast<uint8_t>(0xA0 | N));
-                }
-                else if (N <= (std::numeric_limits<uint8_t>::max)())
-                {
-                    // str 8
-                    oa->write_character(to_char_type(0xD9));
-                    write_number(static_cast<uint8_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint16_t>::max)())
-                {
-                    // str 16
-                    oa->write_character(to_char_type(0xDA));
-                    write_number(static_cast<uint16_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint32_t>::max)())
-                {
-                    // str 32
-                    oa->write_character(to_char_type(0xDB));
-                    write_number(static_cast<uint32_t>(N));
-                }
-
-                // step 2: write the string
-                oa->write_characters(
-                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
-                    j.m_value.string->size());
-                break;
-            }
-
-            case value_t::array:
-            {
-                // step 1: write control byte and the array size
-                const auto N = j.m_value.array->size();
-                if (N <= 15)
-                {
-                    // fixarray
-                    write_number(static_cast<uint8_t>(0x90 | N));
-                }
-                else if (N <= (std::numeric_limits<uint16_t>::max)())
-                {
-                    // array 16
-                    oa->write_character(to_char_type(0xDC));
-                    write_number(static_cast<uint16_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint32_t>::max)())
-                {
-                    // array 32
-                    oa->write_character(to_char_type(0xDD));
-                    write_number(static_cast<uint32_t>(N));
-                }
-
-                // step 2: write each element
-                for (const auto& el : *j.m_value.array)
-                {
-                    write_msgpack(el);
-                }
-                break;
-            }
-
-            case value_t::object:
-            {
-                // step 1: write control byte and the object size
-                const auto N = j.m_value.object->size();
-                if (N <= 15)
-                {
-                    // fixmap
-                    write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
-                }
-                else if (N <= (std::numeric_limits<uint16_t>::max)())
-                {
-                    // map 16
-                    oa->write_character(to_char_type(0xDE));
-                    write_number(static_cast<uint16_t>(N));
-                }
-                else if (N <= (std::numeric_limits<uint32_t>::max)())
-                {
-                    // map 32
-                    oa->write_character(to_char_type(0xDF));
-                    write_number(static_cast<uint32_t>(N));
-                }
-
-                // step 2: write each element
-                for (const auto& el : *j.m_value.object)
-                {
-                    write_msgpack(el.first);
-                    write_msgpack(el.second);
-                }
-                break;
-            }
-
-            default:
-                break;
-        }
-    }
-
-    /*!
-    @param[in] j  JSON value to serialize
-    @param[in] use_count   whether to use '#' prefixes (optimized format)
-    @param[in] use_type    whether to use '$' prefixes (optimized format)
-    @param[in] add_prefix  whether prefixes need to be used for this value
-    */
-    void write_ubjson(const BasicJsonType& j, const bool use_count,
-                      const bool use_type, const bool add_prefix = true)
-    {
-        switch (j.type())
-        {
-            case value_t::null:
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(to_char_type('Z'));
-                }
-                break;
-            }
-
-            case value_t::boolean:
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(j.m_value.boolean
-                                        ? to_char_type('T')
-                                        : to_char_type('F'));
-                }
-                break;
-            }
-
-            case value_t::number_integer:
-            {
-                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);
-                break;
-            }
-
-            case value_t::number_unsigned:
-            {
-                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);
-                break;
-            }
-
-            case value_t::number_float:
-            {
-                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);
-                break;
-            }
-
-            case value_t::string:
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(to_char_type('S'));
-                }
-                write_number_with_ubjson_prefix(j.m_value.string->size(), true);
-                oa->write_characters(
-                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
-                    j.m_value.string->size());
-                break;
-            }
-
-            case value_t::array:
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(to_char_type('['));
-                }
-
-                bool prefix_required = true;
-                if (use_type and not j.m_value.array->empty())
-                {
-                    assert(use_count);
-                    const CharType first_prefix = ubjson_prefix(j.front());
-                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
-                                                         [this, first_prefix](const BasicJsonType & v)
-                    {
-                        return ubjson_prefix(v) == first_prefix;
-                    });
-
-                    if (same_prefix)
-                    {
-                        prefix_required = false;
-                        oa->write_character(to_char_type('$'));
-                        oa->write_character(first_prefix);
-                    }
-                }
-
-                if (use_count)
-                {
-                    oa->write_character(to_char_type('#'));
-                    write_number_with_ubjson_prefix(j.m_value.array->size(), true);
-                }
-
-                for (const auto& el : *j.m_value.array)
-                {
-                    write_ubjson(el, use_count, use_type, prefix_required);
-                }
-
-                if (not use_count)
-                {
-                    oa->write_character(to_char_type(']'));
-                }
-
-                break;
-            }
-
-            case value_t::object:
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(to_char_type('{'));
-                }
-
-                bool prefix_required = true;
-                if (use_type and not j.m_value.object->empty())
-                {
-                    assert(use_count);
-                    const CharType first_prefix = ubjson_prefix(j.front());
-                    const bool same_prefix = std::all_of(j.begin(), j.end(),
-                                                         [this, first_prefix](const BasicJsonType & v)
-                    {
-                        return ubjson_prefix(v) == first_prefix;
-                    });
-
-                    if (same_prefix)
-                    {
-                        prefix_required = false;
-                        oa->write_character(to_char_type('$'));
-                        oa->write_character(first_prefix);
-                    }
-                }
-
-                if (use_count)
-                {
-                    oa->write_character(to_char_type('#'));
-                    write_number_with_ubjson_prefix(j.m_value.object->size(), true);
-                }
-
-                for (const auto& el : *j.m_value.object)
-                {
-                    write_number_with_ubjson_prefix(el.first.size(), true);
-                    oa->write_characters(
-                        reinterpret_cast<const CharType*>(el.first.c_str()),
-                        el.first.size());
-                    write_ubjson(el.second, use_count, use_type, prefix_required);
-                }
-
-                if (not use_count)
-                {
-                    oa->write_character(to_char_type('}'));
-                }
-
-                break;
-            }
-
-            default:
-                break;
-        }
-    }
-
-  private:
-    //////////
-    // BSON //
-    //////////
-
-    /*!
-    @return The size of a BSON document entry header, including the id marker
-            and the entry name size (and its null-terminator).
-    */
-    static std::size_t calc_bson_entry_header_size(const string_t& name)
-    {
-        const auto it = name.find(static_cast<typename string_t::value_type>(0));
-        if (JSON_UNLIKELY(it != BasicJsonType::string_t::npos))
-        {
-            JSON_THROW(out_of_range::create(409,
-                                            "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")"));
-        }
-
-        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
-    }
-
-    /*!
-    @brief Writes the given @a element_type and @a name to the output adapter
-    */
-    void write_bson_entry_header(const string_t& name,
-                                 const std::uint8_t element_type)
-    {
-        oa->write_character(to_char_type(element_type)); // boolean
-        oa->write_characters(
-            reinterpret_cast<const CharType*>(name.c_str()),
-            name.size() + 1u);
-    }
-
-    /*!
-    @brief Writes a BSON element with key @a name and boolean value @a value
-    */
-    void write_bson_boolean(const string_t& name,
-                            const bool value)
-    {
-        write_bson_entry_header(name, 0x08);
-        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));
-    }
-
-    /*!
-    @brief Writes a BSON element with key @a name and double value @a value
-    */
-    void write_bson_double(const string_t& name,
-                           const double value)
-    {
-        write_bson_entry_header(name, 0x01);
-        write_number<double, true>(value);
-    }
-
-    /*!
-    @return The size of the BSON-encoded string in @a value
-    */
-    static std::size_t calc_bson_string_size(const string_t& value)
-    {
-        return sizeof(std::int32_t) + value.size() + 1ul;
-    }
-
-    /*!
-    @brief Writes a BSON element with key @a name and string value @a value
-    */
-    void write_bson_string(const string_t& name,
-                           const string_t& value)
-    {
-        write_bson_entry_header(name, 0x02);
-
-        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));
-        oa->write_characters(
-            reinterpret_cast<const CharType*>(value.c_str()),
-            value.size() + 1);
-    }
-
-    /*!
-    @brief Writes a BSON element with key @a name and null value
-    */
-    void write_bson_null(const string_t& name)
-    {
-        write_bson_entry_header(name, 0x0A);
-    }
-
-    /*!
-    @return The size of the BSON-encoded integer @a value
-    */
-    static std::size_t calc_bson_integer_size(const std::int64_t value)
-    {
-        if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
-        {
-            return sizeof(std::int32_t);
-        }
-        else
-        {
-            return sizeof(std::int64_t);
-        }
-    }
-
-    /*!
-    @brief Writes a BSON element with key @a name and integer @a value
-    */
-    void write_bson_integer(const string_t& name,
-                            const std::int64_t value)
-    {
-        if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
-        {
-            write_bson_entry_header(name, 0x10); // int32
-            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
-        }
-        else
-        {
-            write_bson_entry_header(name, 0x12); // int64
-            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
-        }
-    }
-
-    /*!
-    @return The size of the BSON-encoded unsigned integer in @a j
-    */
-    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
-    {
-        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
-               ? sizeof(std::int32_t)
-               : sizeof(std::int64_t);
-    }
-
-    /*!
-    @brief Writes a BSON element with key @a name and unsigned @a value
-    */
-    void write_bson_unsigned(const string_t& name,
-                             const std::uint64_t value)
-    {
-        if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
-        {
-            write_bson_entry_header(name, 0x10 /* int32 */);
-            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
-        }
-        else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
-        {
-            write_bson_entry_header(name, 0x12 /* int64 */);
-            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
-        }
-        else
-        {
-            JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64"));
-        }
-    }
-
-    /*!
-    @brief Writes a BSON element with key @a name and object @a value
-    */
-    void write_bson_object_entry(const string_t& name,
-                                 const typename BasicJsonType::object_t& value)
-    {
-        write_bson_entry_header(name, 0x03); // object
-        write_bson_object(value);
-    }
-
-    /*!
-    @return The size of the BSON-encoded array @a value
-    */
-    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
-    {
-        std::size_t embedded_document_size = 0ul;
-        std::size_t array_index = 0ul;
-
-        for (const auto& el : value)
-        {
-            embedded_document_size += calc_bson_element_size(std::to_string(array_index++), el);
-        }
-
-        return sizeof(std::int32_t) + embedded_document_size + 1ul;
-    }
-
-    /*!
-    @brief Writes a BSON element with key @a name and array @a value
-    */
-    void write_bson_array(const string_t& name,
-                          const typename BasicJsonType::array_t& value)
-    {
-        write_bson_entry_header(name, 0x04); // array
-        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));
-
-        std::size_t array_index = 0ul;
-
-        for (const auto& el : value)
-        {
-            write_bson_element(std::to_string(array_index++), el);
-        }
-
-        oa->write_character(to_char_type(0x00));
-    }
-
-    /*!
-    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name
-    @return The calculated size for the BSON document entry for @a j with the given @a name.
-    */
-    static std::size_t calc_bson_element_size(const string_t& name,
-            const BasicJsonType& j)
-    {
-        const auto header_size = calc_bson_entry_header_size(name);
-        switch (j.type())
-        {
-            case value_t::object:
-                return header_size + calc_bson_object_size(*j.m_value.object);
-
-            case value_t::array:
-                return header_size + calc_bson_array_size(*j.m_value.array);
-
-            case value_t::boolean:
-                return header_size + 1ul;
-
-            case value_t::number_float:
-                return header_size + 8ul;
-
-            case value_t::number_integer:
-                return header_size + calc_bson_integer_size(j.m_value.number_integer);
-
-            case value_t::number_unsigned:
-                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);
-
-            case value_t::string:
-                return header_size + calc_bson_string_size(*j.m_value.string);
-
-            case value_t::null:
-                return header_size + 0ul;
-
-            // LCOV_EXCL_START
-            default:
-                assert(false);
-                return 0ul;
-                // LCOV_EXCL_STOP
-        };
-    }
-
-    /*!
-    @brief Serializes the JSON value @a j to BSON and associates it with the
-           key @a name.
-    @param name The name to associate with the JSON entity @a j within the
-                current BSON document
-    @return The size of the BSON entry
-    */
-    void write_bson_element(const string_t& name,
-                            const BasicJsonType& j)
-    {
-        switch (j.type())
-        {
-            case value_t::object:
-                return write_bson_object_entry(name, *j.m_value.object);
-
-            case value_t::array:
-                return write_bson_array(name, *j.m_value.array);
-
-            case value_t::boolean:
-                return write_bson_boolean(name, j.m_value.boolean);
-
-            case value_t::number_float:
-                return write_bson_double(name, j.m_value.number_float);
-
-            case value_t::number_integer:
-                return write_bson_integer(name, j.m_value.number_integer);
-
-            case value_t::number_unsigned:
-                return write_bson_unsigned(name, j.m_value.number_unsigned);
-
-            case value_t::string:
-                return write_bson_string(name, *j.m_value.string);
-
-            case value_t::null:
-                return write_bson_null(name);
-
-            // LCOV_EXCL_START
-            default:
-                assert(false);
-                return;
-                // LCOV_EXCL_STOP
-        };
-    }
-
-    /*!
-    @brief Calculates the size of the BSON serialization of the given
-           JSON-object @a j.
-    @param[in] j  JSON value to serialize
-    @pre       j.type() == value_t::object
-    */
-    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
-    {
-        std::size_t document_size = std::accumulate(value.begin(), value.end(), 0ul,
-                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)
-        {
-            return result += calc_bson_element_size(el.first, el.second);
-        });
-
-        return sizeof(std::int32_t) + document_size + 1ul;
-    }
-
-    /*!
-    @param[in] j  JSON value to serialize
-    @pre       j.type() == value_t::object
-    */
-    void write_bson_object(const typename BasicJsonType::object_t& value)
-    {
-        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));
-
-        for (const auto& el : value)
-        {
-            write_bson_element(el.first, el.second);
-        }
-
-        oa->write_character(to_char_type(0x00));
-    }
-
-    //////////
-    // CBOR //
-    //////////
-
-    static constexpr CharType get_cbor_float_prefix(float /*unused*/)
-    {
-        return to_char_type(0xFA);  // Single-Precision Float
-    }
-
-    static constexpr CharType get_cbor_float_prefix(double /*unused*/)
-    {
-        return to_char_type(0xFB);  // Double-Precision Float
-    }
-
-    /////////////
-    // MsgPack //
-    /////////////
-
-    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
-    {
-        return to_char_type(0xCA);  // float 32
-    }
-
-    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
-    {
-        return to_char_type(0xCB);  // float 64
-    }
-
-    ////////////
-    // UBJSON //
-    ////////////
-
-    // UBJSON: write number (floating point)
-    template<typename NumberType, typename std::enable_if<
-                 std::is_floating_point<NumberType>::value, int>::type = 0>
-    void write_number_with_ubjson_prefix(const NumberType n,
-                                         const bool add_prefix)
-    {
-        if (add_prefix)
-        {
-            oa->write_character(get_ubjson_float_prefix(n));
-        }
-        write_number(n);
-    }
-
-    // UBJSON: write number (unsigned integer)
-    template<typename NumberType, typename std::enable_if<
-                 std::is_unsigned<NumberType>::value, int>::type = 0>
-    void write_number_with_ubjson_prefix(const NumberType n,
-                                         const bool add_prefix)
-    {
-        if (n <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('i'));  // int8
-            }
-            write_number(static_cast<uint8_t>(n));
-        }
-        else if (n <= (std::numeric_limits<uint8_t>::max)())
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('U'));  // uint8
-            }
-            write_number(static_cast<uint8_t>(n));
-        }
-        else if (n <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('I'));  // int16
-            }
-            write_number(static_cast<int16_t>(n));
-        }
-        else if (n <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('l'));  // int32
-            }
-            write_number(static_cast<int32_t>(n));
-        }
-        else if (n <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('L'));  // int64
-            }
-            write_number(static_cast<int64_t>(n));
-        }
-        else
-        {
-            JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
-        }
-    }
-
-    // UBJSON: write number (signed integer)
-    template<typename NumberType, typename std::enable_if<
-                 std::is_signed<NumberType>::value and
-                 not std::is_floating_point<NumberType>::value, int>::type = 0>
-    void write_number_with_ubjson_prefix(const NumberType n,
-                                         const bool add_prefix)
-    {
-        if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('i'));  // int8
-            }
-            write_number(static_cast<int8_t>(n));
-        }
-        else if (static_cast<int64_t>((std::numeric_limits<uint8_t>::min)()) <= n and n <= static_cast<int64_t>((std::numeric_limits<uint8_t>::max)()))
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('U'));  // uint8
-            }
-            write_number(static_cast<uint8_t>(n));
-        }
-        else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('I'));  // int16
-            }
-            write_number(static_cast<int16_t>(n));
-        }
-        else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('l'));  // int32
-            }
-            write_number(static_cast<int32_t>(n));
-        }
-        else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
-        {
-            if (add_prefix)
-            {
-                oa->write_character(to_char_type('L'));  // int64
-            }
-            write_number(static_cast<int64_t>(n));
-        }
-        // LCOV_EXCL_START
-        else
-        {
-            JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
-        }
-        // LCOV_EXCL_STOP
-    }
-
-    /*!
-    @brief determine the type prefix of container values
-
-    @note This function does not need to be 100% accurate when it comes to
-          integer limits. In case a number exceeds the limits of int64_t,
-          this will be detected by a later call to function
-          write_number_with_ubjson_prefix. Therefore, we return 'L' for any
-          value that does not fit the previous limits.
-    */
-    CharType ubjson_prefix(const BasicJsonType& j) const noexcept
-    {
-        switch (j.type())
-        {
-            case value_t::null:
-                return 'Z';
-
-            case value_t::boolean:
-                return j.m_value.boolean ? 'T' : 'F';
-
-            case value_t::number_integer:
-            {
-                if ((std::numeric_limits<int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
-                {
-                    return 'i';
-                }
-                if ((std::numeric_limits<uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
-                {
-                    return 'U';
-                }
-                if ((std::numeric_limits<int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
-                {
-                    return 'I';
-                }
-                if ((std::numeric_limits<int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
-                {
-                    return 'l';
-                }
-                // no check and assume int64_t (see note above)
-                return 'L';
-            }
-
-            case value_t::number_unsigned:
-            {
-                if (j.m_value.number_unsigned <= (std::numeric_limits<int8_t>::max)())
-                {
-                    return 'i';
-                }
-                if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
-                {
-                    return 'U';
-                }
-                if (j.m_value.number_unsigned <= (std::numeric_limits<int16_t>::max)())
-                {
-                    return 'I';
-                }
-                if (j.m_value.number_unsigned <= (std::numeric_limits<int32_t>::max)())
-                {
-                    return 'l';
-                }
-                // no check and assume int64_t (see note above)
-                return 'L';
-            }
-
-            case value_t::number_float:
-                return get_ubjson_float_prefix(j.m_value.number_float);
-
-            case value_t::string:
-                return 'S';
-
-            case value_t::array:
-                return '[';
-
-            case value_t::object:
-                return '{';
-
-            default:  // discarded values
-                return 'N';
-        }
-    }
-
-    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
-    {
-        return 'd';  // float 32
-    }
-
-    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
-    {
-        return 'D';  // float 64
-    }
-
-    ///////////////////////
-    // Utility functions //
-    ///////////////////////
-
-    /*
-    @brief write a number to output input
-    @param[in] n number of type @a NumberType
-    @tparam NumberType the type of the number
-    @tparam OutputIsLittleEndian Set to true if output data is
-                                 required to be little endian
-
-    @note This function needs to respect the system's endianess, because bytes
-          in CBOR, MessagePack, and UBJSON are stored in network order (big
-          endian) and therefore need reordering on little endian systems.
-    */
-    template<typename NumberType, bool OutputIsLittleEndian = false>
-    void write_number(const NumberType n)
-    {
-        // step 1: write number to array of length NumberType
-        std::array<CharType, sizeof(NumberType)> vec;
-        std::memcpy(vec.data(), &n, sizeof(NumberType));
-
-        // step 2: write array to output (with possible reordering)
-        if (is_little_endian and not OutputIsLittleEndian)
-        {
-            // reverse byte order prior to conversion if necessary
-            std::reverse(vec.begin(), vec.end());
-        }
-
-        oa->write_characters(vec.data(), sizeof(NumberType));
-    }
-
-  public:
-    // The following to_char_type functions are implement the conversion
-    // between uint8_t and CharType. In case CharType is not unsigned,
-    // such a conversion is required to allow values greater than 128.
-    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.
-    template < typename C = CharType,
-               enable_if_t < std::is_signed<C>::value and std::is_signed<char>::value > * = nullptr >
-    static constexpr CharType to_char_type(std::uint8_t x) noexcept
-    {
-        return *reinterpret_cast<char*>(&x);
-    }
-
-    template < typename C = CharType,
-               enable_if_t < std::is_signed<C>::value and std::is_unsigned<char>::value > * = nullptr >
-    static CharType to_char_type(std::uint8_t x) noexcept
-    {
-        static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
-        static_assert(std::is_pod<CharType>::value, "CharType must be POD");
-        CharType result;
-        std::memcpy(&result, &x, sizeof(x));
-        return result;
-    }
-
-    template<typename C = CharType,
-             enable_if_t<std::is_unsigned<C>::value>* = nullptr>
-    static constexpr CharType to_char_type(std::uint8_t x) noexcept
-    {
-        return x;
-    }
-
-    template < typename InputCharType, typename C = CharType,
-               enable_if_t <
-                   std::is_signed<C>::value and
-                   std::is_signed<char>::value and
-                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value
-                   > * = nullptr >
-    static constexpr CharType to_char_type(InputCharType x) noexcept
-    {
-        return x;
-    }
-
-  private:
-    /// whether we can assume little endianess
-    const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
-
-    /// the output
-    output_adapter_t<CharType> oa = nullptr;
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/output/serializer.hpp>
-
-
-#include <algorithm> // reverse, remove, fill, find, none_of
-#include <array> // array
-#include <cassert> // assert
-#include <ciso646> // and, or
-#include <clocale> // localeconv, lconv
-#include <cmath> // labs, isfinite, isnan, signbit
-#include <cstddef> // size_t, ptrdiff_t
-#include <cstdint> // uint8_t
-#include <cstdio> // snprintf
-#include <limits> // numeric_limits
-#include <string> // string
-#include <type_traits> // is_same
-
-// #include <nlohmann/detail/exceptions.hpp>
-
-// #include <nlohmann/detail/conversions/to_chars.hpp>
-
-
-#include <cassert> // assert
-#include <ciso646> // or, and, not
-#include <cmath>   // signbit, isfinite
-#include <cstdint> // intN_t, uintN_t
-#include <cstring> // memcpy, memmove
-
-namespace nlohmann
-{
-namespace detail
-{
-
-/*!
-@brief implements the Grisu2 algorithm for binary to decimal floating-point
-conversion.
-
-This implementation is a slightly modified version of the reference
-implementation which may be obtained from
-http://florian.loitsch.com/publications (bench.tar.gz).
-
-The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
-
-For a detailed description of the algorithm see:
-
-[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
-    Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
-    Language Design and Implementation, PLDI 2010
-[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
-    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
-    Design and Implementation, PLDI 1996
-*/
-namespace dtoa_impl
-{
-
-template <typename Target, typename Source>
-Target reinterpret_bits(const Source source)
-{
-    static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
-
-    Target target;
-    std::memcpy(&target, &source, sizeof(Source));
-    return target;
-}
-
-struct diyfp // f * 2^e
-{
-    static constexpr int kPrecision = 64; // = q
-
-    uint64_t f = 0;
-    int e = 0;
-
-    constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
-
-    /*!
-    @brief returns x - y
-    @pre x.e == y.e and x.f >= y.f
-    */
-    static diyfp sub(const diyfp& x, const diyfp& y) noexcept
-    {
-        assert(x.e == y.e);
-        assert(x.f >= y.f);
-
-        return {x.f - y.f, x.e};
-    }
-
-    /*!
-    @brief returns x * y
-    @note The result is rounded. (Only the upper q bits are returned.)
-    */
-    static diyfp mul(const diyfp& x, const diyfp& y) noexcept
-    {
-        static_assert(kPrecision == 64, "internal error");
-
-        // Computes:
-        //  f = round((x.f * y.f) / 2^q)
-        //  e = x.e + y.e + q
-
-        // Emulate the 64-bit * 64-bit multiplication:
-        //
-        // p = u * v
-        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)
-        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )
-        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )
-        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )
-        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)
-        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )
-        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )
-        //
-        // (Since Q might be larger than 2^32 - 1)
-        //
-        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)
-        //
-        // (Q_hi + H does not overflow a 64-bit int)
-        //
-        //   = p_lo + 2^64 p_hi
-
-        const uint64_t u_lo = x.f & 0xFFFFFFFF;
-        const uint64_t u_hi = x.f >> 32;
-        const uint64_t v_lo = y.f & 0xFFFFFFFF;
-        const uint64_t v_hi = y.f >> 32;
-
-        const uint64_t p0 = u_lo * v_lo;
-        const uint64_t p1 = u_lo * v_hi;
-        const uint64_t p2 = u_hi * v_lo;
-        const uint64_t p3 = u_hi * v_hi;
-
-        const uint64_t p0_hi = p0 >> 32;
-        const uint64_t p1_lo = p1 & 0xFFFFFFFF;
-        const uint64_t p1_hi = p1 >> 32;
-        const uint64_t p2_lo = p2 & 0xFFFFFFFF;
-        const uint64_t p2_hi = p2 >> 32;
-
-        uint64_t Q = p0_hi + p1_lo + p2_lo;
-
-        // The full product might now be computed as
-        //
-        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)
-        // p_lo = p0_lo + (Q << 32)
-        //
-        // But in this particular case here, the full p_lo is not required.
-        // Effectively we only need to add the highest bit in p_lo to p_hi (and
-        // Q_hi + 1 does not overflow).
-
-        Q += uint64_t{1} << (64 - 32 - 1); // round, ties up
-
-        const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32);
-
-        return {h, x.e + y.e + 64};
-    }
-
-    /*!
-    @brief normalize x such that the significand is >= 2^(q-1)
-    @pre x.f != 0
-    */
-    static diyfp normalize(diyfp x) noexcept
-    {
-        assert(x.f != 0);
-
-        while ((x.f >> 63) == 0)
-        {
-            x.f <<= 1;
-            x.e--;
-        }
-
-        return x;
-    }
-
-    /*!
-    @brief normalize x such that the result has the exponent E
-    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.
-    */
-    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
-    {
-        const int delta = x.e - target_exponent;
-
-        assert(delta >= 0);
-        assert(((x.f << delta) >> delta) == x.f);
-
-        return {x.f << delta, target_exponent};
-    }
-};
-
-struct boundaries
-{
-    diyfp w;
-    diyfp minus;
-    diyfp plus;
-};
-
-/*!
-Compute the (normalized) diyfp representing the input number 'value' and its
-boundaries.
-
-@pre value must be finite and positive
-*/
-template <typename FloatType>
-boundaries compute_boundaries(FloatType value)
-{
-    assert(std::isfinite(value));
-    assert(value > 0);
-
-    // Convert the IEEE representation into a diyfp.
-    //
-    // If v is denormal:
-    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))
-    // If v is normalized:
-    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))
-
-    static_assert(std::numeric_limits<FloatType>::is_iec559,
-                  "internal error: dtoa_short requires an IEEE-754 floating-point implementation");
-
-    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)
-    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);
-    constexpr int      kMinExp    = 1 - kBias;
-    constexpr uint64_t kHiddenBit = uint64_t{1} << (kPrecision - 1); // = 2^(p-1)
-
-    using bits_type = typename std::conditional< kPrecision == 24, uint32_t, uint64_t >::type;
-
-    const uint64_t bits = reinterpret_bits<bits_type>(value);
-    const uint64_t E = bits >> (kPrecision - 1);
-    const uint64_t F = bits & (kHiddenBit - 1);
-
-    const bool is_denormal = (E == 0);
-    const diyfp v = is_denormal
-                    ? diyfp(F, kMinExp)
-                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
-
-    // Compute the boundaries m- and m+ of the floating-point value
-    // v = f * 2^e.
-    //
-    // Determine v- and v+, the floating-point predecessor and successor if v,
-    // respectively.
-    //
-    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)
-    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)
-    //
-    //      v+ = v + 2^e
-    //
-    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_
-    // between m- and m+ round to v, regardless of how the input rounding
-    // algorithm breaks ties.
-    //
-    //      ---+-------------+-------------+-------------+-------------+---  (A)
-    //         v-            m-            v             m+            v+
-    //
-    //      -----------------+------+------+-------------+-------------+---  (B)
-    //                       v-     m-     v             m+            v+
-
-    const bool lower_boundary_is_closer = (F == 0 and E > 1);
-    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
-    const diyfp m_minus = lower_boundary_is_closer
-                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)
-                          : diyfp(2 * v.f - 1, v.e - 1); // (A)
-
-    // Determine the normalized w+ = m+.
-    const diyfp w_plus = diyfp::normalize(m_plus);
-
-    // Determine w- = m- such that e_(w-) = e_(w+).
-    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);
-
-    return {diyfp::normalize(v), w_minus, w_plus};
-}
-
-// Given normalized diyfp w, Grisu needs to find a (normalized) cached
-// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies
-// within a certain range [alpha, gamma] (Definition 3.2 from [1])
-//
-//      alpha <= e = e_c + e_w + q <= gamma
-//
-// or
-//
-//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q
-//                          <= f_c * f_w * 2^gamma
-//
-// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies
-//
-//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma
-//
-// or
-//
-//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)
-//
-// The choice of (alpha,gamma) determines the size of the table and the form of
-// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well
-// in practice:
-//
-// The idea is to cut the number c * w = f * 2^e into two parts, which can be
-// processed independently: An integral part p1, and a fractional part p2:
-//
-//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e
-//              = (f div 2^-e) + (f mod 2^-e) * 2^e
-//              = p1 + p2 * 2^e
-//
-// The conversion of p1 into decimal form requires a series of divisions and
-// modulos by (a power of) 10. These operations are faster for 32-bit than for
-// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be
-// achieved by choosing
-//
-//      -e >= 32   or   e <= -32 := gamma
-//
-// In order to convert the fractional part
-//
-//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...
-//
-// into decimal form, the fraction is repeatedly multiplied by 10 and the digits
-// d[-i] are extracted in order:
-//
-//      (10 * p2) div 2^-e = d[-1]
-//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...
-//
-// The multiplication by 10 must not overflow. It is sufficient to choose
-//
-//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.
-//
-// Since p2 = f mod 2^-e < 2^-e,
-//
-//      -e <= 60   or   e >= -60 := alpha
-
-constexpr int kAlpha = -60;
-constexpr int kGamma = -32;
-
-struct cached_power // c = f * 2^e ~= 10^k
-{
-    uint64_t f;
-    int e;
-    int k;
-};
-
-/*!
-For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
-power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
-satisfies (Definition 3.2 from [1])
-
-     alpha <= e_c + e + q <= gamma.
-*/
-inline cached_power get_cached_power_for_binary_exponent(int e)
-{
-    // Now
-    //
-    //      alpha <= e_c + e + q <= gamma                                    (1)
-    //      ==> f_c * 2^alpha <= c * 2^e * 2^q
-    //
-    // and since the c's are normalized, 2^(q-1) <= f_c,
-    //
-    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)
-    //      ==> 2^(alpha - e - 1) <= c
-    //
-    // If c were an exakt power of ten, i.e. c = 10^k, one may determine k as
-    //
-    //      k = ceil( log_10( 2^(alpha - e - 1) ) )
-    //        = ceil( (alpha - e - 1) * log_10(2) )
-    //
-    // From the paper:
-    // "In theory the result of the procedure could be wrong since c is rounded,
-    //  and the computation itself is approximated [...]. In practice, however,
-    //  this simple function is sufficient."
-    //
-    // For IEEE double precision floating-point numbers converted into
-    // normalized diyfp's w = f * 2^e, with q = 64,
-    //
-    //      e >= -1022      (min IEEE exponent)
-    //           -52        (p - 1)
-    //           -52        (p - 1, possibly normalize denormal IEEE numbers)
-    //           -11        (normalize the diyfp)
-    //         = -1137
-    //
-    // and
-    //
-    //      e <= +1023      (max IEEE exponent)
-    //           -52        (p - 1)
-    //           -11        (normalize the diyfp)
-    //         = 960
-    //
-    // This binary exponent range [-1137,960] results in a decimal exponent
-    // range [-307,324]. One does not need to store a cached power for each
-    // k in this range. For each such k it suffices to find a cached power
-    // such that the exponent of the product lies in [alpha,gamma].
-    // This implies that the difference of the decimal exponents of adjacent
-    // table entries must be less than or equal to
-    //
-    //      floor( (gamma - alpha) * log_10(2) ) = 8.
-    //
-    // (A smaller distance gamma-alpha would require a larger table.)
-
-    // NB:
-    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.
-
-    constexpr int kCachedPowersSize = 79;
-    constexpr int kCachedPowersMinDecExp = -300;
-    constexpr int kCachedPowersDecStep = 8;
-
-    static constexpr cached_power kCachedPowers[] =
-    {
-        { 0xAB70FE17C79AC6CA, -1060, -300 },
-        { 0xFF77B1FCBEBCDC4F, -1034, -292 },
-        { 0xBE5691EF416BD60C, -1007, -284 },
-        { 0x8DD01FAD907FFC3C,  -980, -276 },
-        { 0xD3515C2831559A83,  -954, -268 },
-        { 0x9D71AC8FADA6C9B5,  -927, -260 },
-        { 0xEA9C227723EE8BCB,  -901, -252 },
-        { 0xAECC49914078536D,  -874, -244 },
-        { 0x823C12795DB6CE57,  -847, -236 },
-        { 0xC21094364DFB5637,  -821, -228 },
-        { 0x9096EA6F3848984F,  -794, -220 },
-        { 0xD77485CB25823AC7,  -768, -212 },
-        { 0xA086CFCD97BF97F4,  -741, -204 },
-        { 0xEF340A98172AACE5,  -715, -196 },
-        { 0xB23867FB2A35B28E,  -688, -188 },
-        { 0x84C8D4DFD2C63F3B,  -661, -180 },
-        { 0xC5DD44271AD3CDBA,  -635, -172 },
-        { 0x936B9FCEBB25C996,  -608, -164 },
-        { 0xDBAC6C247D62A584,  -582, -156 },
-        { 0xA3AB66580D5FDAF6,  -555, -148 },
-        { 0xF3E2F893DEC3F126,  -529, -140 },
-        { 0xB5B5ADA8AAFF80B8,  -502, -132 },
-        { 0x87625F056C7C4A8B,  -475, -124 },
-        { 0xC9BCFF6034C13053,  -449, -116 },
-        { 0x964E858C91BA2655,  -422, -108 },
-        { 0xDFF9772470297EBD,  -396, -100 },
-        { 0xA6DFBD9FB8E5B88F,  -369,  -92 },
-        { 0xF8A95FCF88747D94,  -343,  -84 },
-        { 0xB94470938FA89BCF,  -316,  -76 },
-        { 0x8A08F0F8BF0F156B,  -289,  -68 },
-        { 0xCDB02555653131B6,  -263,  -60 },
-        { 0x993FE2C6D07B7FAC,  -236,  -52 },
-        { 0xE45C10C42A2B3B06,  -210,  -44 },
-        { 0xAA242499697392D3,  -183,  -36 },
-        { 0xFD87B5F28300CA0E,  -157,  -28 },
-        { 0xBCE5086492111AEB,  -130,  -20 },
-        { 0x8CBCCC096F5088CC,  -103,  -12 },
-        { 0xD1B71758E219652C,   -77,   -4 },
-        { 0x9C40000000000000,   -50,    4 },
-        { 0xE8D4A51000000000,   -24,   12 },
-        { 0xAD78EBC5AC620000,     3,   20 },
-        { 0x813F3978F8940984,    30,   28 },
-        { 0xC097CE7BC90715B3,    56,   36 },
-        { 0x8F7E32CE7BEA5C70,    83,   44 },
-        { 0xD5D238A4ABE98068,   109,   52 },
-        { 0x9F4F2726179A2245,   136,   60 },
-        { 0xED63A231D4C4FB27,   162,   68 },
-        { 0xB0DE65388CC8ADA8,   189,   76 },
-        { 0x83C7088E1AAB65DB,   216,   84 },
-        { 0xC45D1DF942711D9A,   242,   92 },
-        { 0x924D692CA61BE758,   269,  100 },
-        { 0xDA01EE641A708DEA,   295,  108 },
-        { 0xA26DA3999AEF774A,   322,  116 },
-        { 0xF209787BB47D6B85,   348,  124 },
-        { 0xB454E4A179DD1877,   375,  132 },
-        { 0x865B86925B9BC5C2,   402,  140 },
-        { 0xC83553C5C8965D3D,   428,  148 },
-        { 0x952AB45CFA97A0B3,   455,  156 },
-        { 0xDE469FBD99A05FE3,   481,  164 },
-        { 0xA59BC234DB398C25,   508,  172 },
-        { 0xF6C69A72A3989F5C,   534,  180 },
-        { 0xB7DCBF5354E9BECE,   561,  188 },
-        { 0x88FCF317F22241E2,   588,  196 },
-        { 0xCC20CE9BD35C78A5,   614,  204 },
-        { 0x98165AF37B2153DF,   641,  212 },
-        { 0xE2A0B5DC971F303A,   667,  220 },
-        { 0xA8D9D1535CE3B396,   694,  228 },
-        { 0xFB9B7CD9A4A7443C,   720,  236 },
-        { 0xBB764C4CA7A44410,   747,  244 },
-        { 0x8BAB8EEFB6409C1A,   774,  252 },
-        { 0xD01FEF10A657842C,   800,  260 },
-        { 0x9B10A4E5E9913129,   827,  268 },
-        { 0xE7109BFBA19C0C9D,   853,  276 },
-        { 0xAC2820D9623BF429,   880,  284 },
-        { 0x80444B5E7AA7CF85,   907,  292 },
-        { 0xBF21E44003ACDD2D,   933,  300 },
-        { 0x8E679C2F5E44FF8F,   960,  308 },
-        { 0xD433179D9C8CB841,   986,  316 },
-        { 0x9E19DB92B4E31BA9,  1013,  324 },
-    };
-
-    // This computation gives exactly the same results for k as
-    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)
-    // for |e| <= 1500, but doesn't require floating-point operations.
-    // NB: log_10(2) ~= 78913 / 2^18
-    assert(e >= -1500);
-    assert(e <=  1500);
-    const int f = kAlpha - e - 1;
-    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);
-
-    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
-    assert(index >= 0);
-    assert(index < kCachedPowersSize);
-    static_cast<void>(kCachedPowersSize); // Fix warning.
-
-    const cached_power cached = kCachedPowers[index];
-    assert(kAlpha <= cached.e + e + 64);
-    assert(kGamma >= cached.e + e + 64);
-
-    return cached;
-}
-
-/*!
-For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
-For n == 0, returns 1 and sets pow10 := 1.
-*/
-inline int find_largest_pow10(const uint32_t n, uint32_t& pow10)
-{
-    // LCOV_EXCL_START
-    if (n >= 1000000000)
-    {
-        pow10 = 1000000000;
-        return 10;
-    }
-    // LCOV_EXCL_STOP
-    else if (n >= 100000000)
-    {
-        pow10 = 100000000;
-        return  9;
-    }
-    else if (n >= 10000000)
-    {
-        pow10 = 10000000;
-        return  8;
-    }
-    else if (n >= 1000000)
-    {
-        pow10 = 1000000;
-        return  7;
-    }
-    else if (n >= 100000)
-    {
-        pow10 = 100000;
-        return  6;
-    }
-    else if (n >= 10000)
-    {
-        pow10 = 10000;
-        return  5;
-    }
-    else if (n >= 1000)
-    {
-        pow10 = 1000;
-        return  4;
-    }
-    else if (n >= 100)
-    {
-        pow10 = 100;
-        return  3;
-    }
-    else if (n >= 10)
-    {
-        pow10 = 10;
-        return  2;
-    }
-    else
-    {
-        pow10 = 1;
-        return 1;
-    }
-}
-
-inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta,
-                         uint64_t rest, uint64_t ten_k)
-{
-    assert(len >= 1);
-    assert(dist <= delta);
-    assert(rest <= delta);
-    assert(ten_k > 0);
-
-    //               <--------------------------- delta ---->
-    //                                  <---- dist --------->
-    // --------------[------------------+-------------------]--------------
-    //               M-                 w                   M+
-    //
-    //                                  ten_k
-    //                                <------>
-    //                                       <---- rest ---->
-    // --------------[------------------+----+--------------]--------------
-    //                                  w    V
-    //                                       = buf * 10^k
-    //
-    // ten_k represents a unit-in-the-last-place in the decimal representation
-    // stored in buf.
-    // Decrement buf by ten_k while this takes buf closer to w.
-
-    // The tests are written in this order to avoid overflow in unsigned
-    // integer arithmetic.
-
-    while (rest < dist
-            and delta - rest >= ten_k
-            and (rest + ten_k < dist or dist - rest > rest + ten_k - dist))
-    {
-        assert(buf[len - 1] != '0');
-        buf[len - 1]--;
-        rest += ten_k;
-    }
-}
-
-/*!
-Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
-M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
-*/
-inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
-                             diyfp M_minus, diyfp w, diyfp M_plus)
-{
-    static_assert(kAlpha >= -60, "internal error");
-    static_assert(kGamma <= -32, "internal error");
-
-    // Generates the digits (and the exponent) of a decimal floating-point
-    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's
-    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.
-    //
-    //               <--------------------------- delta ---->
-    //                                  <---- dist --------->
-    // --------------[------------------+-------------------]--------------
-    //               M-                 w                   M+
-    //
-    // Grisu2 generates the digits of M+ from left to right and stops as soon as
-    // V is in [M-,M+].
-
-    assert(M_plus.e >= kAlpha);
-    assert(M_plus.e <= kGamma);
-
-    uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
-    uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)
-
-    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):
-    //
-    //      M+ = f * 2^e
-    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e
-    //         = ((p1        ) * 2^-e + (p2        )) * 2^e
-    //         = p1 + p2 * 2^e
-
-    const diyfp one(uint64_t{1} << -M_plus.e, M_plus.e);
-
-    auto p1 = static_cast<uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
-    uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e
-
-    // 1)
-    //
-    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
-
-    assert(p1 > 0);
-
-    uint32_t pow10;
-    const int k = find_largest_pow10(p1, pow10);
-
-    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
-    //
-    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))
-    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))
-    //
-    //      M+ = p1                                             + p2 * 2^e
-    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e
-    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e
-    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e
-    //
-    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)
-    //
-    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]
-    //
-    // but stop as soon as
-    //
-    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e
-
-    int n = k;
-    while (n > 0)
-    {
-        // Invariants:
-        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)
-        //      pow10 = 10^(n-1) <= p1 < 10^n
-        //
-        const uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)
-        const uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)
-        //
-        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
-        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
-        //
-        assert(d <= 9);
-        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
-        //
-        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
-        //
-        p1 = r;
-        n--;
-        //
-        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)
-        //      pow10 = 10^n
-        //
-
-        // Now check if enough digits have been generated.
-        // Compute
-        //
-        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e
-        //
-        // Note:
-        // Since rest and delta share the same exponent e, it suffices to
-        // compare the significands.
-        const uint64_t rest = (uint64_t{p1} << -one.e) + p2;
-        if (rest <= delta)
-        {
-            // V = buffer * 10^n, with M- <= V <= M+.
-
-            decimal_exponent += n;
-
-            // We may now just stop. But instead look if the buffer could be
-            // decremented to bring V closer to w.
-            //
-            // pow10 = 10^n is now 1 ulp in the decimal representation V.
-            // The rounding procedure works with diyfp's with an implicit
-            // exponent of e.
-            //
-            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e
-            //
-            const uint64_t ten_n = uint64_t{pow10} << -one.e;
-            grisu2_round(buffer, length, dist, delta, rest, ten_n);
-
-            return;
-        }
-
-        pow10 /= 10;
-        //
-        //      pow10 = 10^(n-1) <= p1 < 10^n
-        // Invariants restored.
-    }
-
-    // 2)
-    //
-    // The digits of the integral part have been generated:
-    //
-    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e
-    //         = buffer            + p2 * 2^e
-    //
-    // Now generate the digits of the fractional part p2 * 2^e.
-    //
-    // Note:
-    // No decimal point is generated: the exponent is adjusted instead.
-    //
-    // p2 actually represents the fraction
-    //
-    //      p2 * 2^e
-    //          = p2 / 2^-e
-    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...
-    //
-    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)
-    //
-    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m
-    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)
-    //
-    // using
-    //
-    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)
-    //                = (                   d) * 2^-e + (                   r)
-    //
-    // or
-    //      10^m * p2 * 2^e = d + r * 2^e
-    //
-    // i.e.
-    //
-    //      M+ = buffer + p2 * 2^e
-    //         = buffer + 10^-m * (d + r * 2^e)
-    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e
-    //
-    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
-
-    assert(p2 > delta);
-
-    int m = 0;
-    for (;;)
-    {
-        // Invariant:
-        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e
-        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e
-        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e
-        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
-        //
-        assert(p2 <= UINT64_MAX / 10);
-        p2 *= 10;
-        const uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e
-        const uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
-        //
-        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e
-        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
-        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
-        //
-        assert(d <= 9);
-        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
-        //
-        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
-        //
-        p2 = r;
-        m++;
-        //
-        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e
-        // Invariant restored.
-
-        // Check if enough digits have been generated.
-        //
-        //      10^-m * p2 * 2^e <= delta * 2^e
-        //              p2 * 2^e <= 10^m * delta * 2^e
-        //                    p2 <= 10^m * delta
-        delta *= 10;
-        dist  *= 10;
-        if (p2 <= delta)
-        {
-            break;
-        }
-    }
-
-    // V = buffer * 10^-m, with M- <= V <= M+.
-
-    decimal_exponent -= m;
-
-    // 1 ulp in the decimal representation is now 10^-m.
-    // Since delta and dist are now scaled by 10^m, we need to do the
-    // same with ulp in order to keep the units in sync.
-    //
-    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e
-    //
-    const uint64_t ten_m = one.f;
-    grisu2_round(buffer, length, dist, delta, p2, ten_m);
-
-    // By construction this algorithm generates the shortest possible decimal
-    // number (Loitsch, Theorem 6.2) which rounds back to w.
-    // For an input number of precision p, at least
-    //
-    //      N = 1 + ceil(p * log_10(2))
-    //
-    // decimal digits are sufficient to identify all binary floating-point
-    // numbers (Matula, "In-and-Out conversions").
-    // This implies that the algorithm does not produce more than N decimal
-    // digits.
-    //
-    //      N = 17 for p = 53 (IEEE double precision)
-    //      N = 9  for p = 24 (IEEE single precision)
-}
-
-/*!
-v = buf * 10^decimal_exponent
-len is the length of the buffer (number of decimal digits)
-The buffer must be large enough, i.e. >= max_digits10.
-*/
-inline void grisu2(char* buf, int& len, int& decimal_exponent,
-                   diyfp m_minus, diyfp v, diyfp m_plus)
-{
-    assert(m_plus.e == m_minus.e);
-    assert(m_plus.e == v.e);
-
-    //  --------(-----------------------+-----------------------)--------    (A)
-    //          m-                      v                       m+
-    //
-    //  --------------------(-----------+-----------------------)--------    (B)
-    //                      m-          v                       m+
-    //
-    // First scale v (and m- and m+) such that the exponent is in the range
-    // [alpha, gamma].
-
-    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);
-
-    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k
-
-    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]
-    const diyfp w       = diyfp::mul(v,       c_minus_k);
-    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);
-    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);
-
-    //  ----(---+---)---------------(---+---)---------------(---+---)----
-    //          w-                      w                       w+
-    //          = c*m-                  = c*v                   = c*m+
-    //
-    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and
-    // w+ are now off by a small amount.
-    // In fact:
-    //
-    //      w - v * 10^k < 1 ulp
-    //
-    // To account for this inaccuracy, add resp. subtract 1 ulp.
-    //
-    //  --------+---[---------------(---+---)---------------]---+--------
-    //          w-  M-                  w                   M+  w+
-    //
-    // Now any number in [M-, M+] (bounds included) will round to w when input,
-    // regardless of how the input rounding algorithm breaks ties.
-    //
-    // And digit_gen generates the shortest possible such number in [M-, M+].
-    // Note that this does not mean that Grisu2 always generates the shortest
-    // possible number in the interval (m-, m+).
-    const diyfp M_minus(w_minus.f + 1, w_minus.e);
-    const diyfp M_plus (w_plus.f  - 1, w_plus.e );
-
-    decimal_exponent = -cached.k; // = -(-k) = k
-
-    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
-}
-
-/*!
-v = buf * 10^decimal_exponent
-len is the length of the buffer (number of decimal digits)
-The buffer must be large enough, i.e. >= max_digits10.
-*/
-template <typename FloatType>
-void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
-{
-    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
-                  "internal error: not enough precision");
-
-    assert(std::isfinite(value));
-    assert(value > 0);
-
-    // If the neighbors (and boundaries) of 'value' are always computed for double-precision
-    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
-    // decimal representations are not exactly "short".
-    //
-    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)
-    // says "value is converted to a string as if by std::sprintf in the default ("C") locale"
-    // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'
-    // does.
-    // On the other hand, the documentation for 'std::to_chars' requires that "parsing the
-    // representation using the corresponding std::from_chars function recovers value exactly". That
-    // indicates that single precision floating-point numbers should be recovered using
-    // 'std::strtof'.
-    //
-    // NB: If the neighbors are computed for single-precision numbers, there is a single float
-    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision
-    //     value is off by 1 ulp.
-#if 0
-    const boundaries w = compute_boundaries(static_cast<double>(value));
-#else
-    const boundaries w = compute_boundaries(value);
-#endif
-
-    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
-}
-
-/*!
-@brief appends a decimal representation of e to buf
-@return a pointer to the element following the exponent.
-@pre -1000 < e < 1000
-*/
-inline char* append_exponent(char* buf, int e)
-{
-    assert(e > -1000);
-    assert(e <  1000);
-
-    if (e < 0)
-    {
-        e = -e;
-        *buf++ = '-';
-    }
-    else
-    {
-        *buf++ = '+';
-    }
-
-    auto k = static_cast<uint32_t>(e);
-    if (k < 10)
-    {
-        // Always print at least two digits in the exponent.
-        // This is for compatibility with printf("%g").
-        *buf++ = '0';
-        *buf++ = static_cast<char>('0' + k);
-    }
-    else if (k < 100)
-    {
-        *buf++ = static_cast<char>('0' + k / 10);
-        k %= 10;
-        *buf++ = static_cast<char>('0' + k);
-    }
-    else
-    {
-        *buf++ = static_cast<char>('0' + k / 100);
-        k %= 100;
-        *buf++ = static_cast<char>('0' + k / 10);
-        k %= 10;
-        *buf++ = static_cast<char>('0' + k);
-    }
-
-    return buf;
-}
-
-/*!
-@brief prettify v = buf * 10^decimal_exponent
-
-If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
-notation. Otherwise it will be printed in exponential notation.
-
-@pre min_exp < 0
-@pre max_exp > 0
-*/
-inline char* format_buffer(char* buf, int len, int decimal_exponent,
-                           int min_exp, int max_exp)
-{
-    assert(min_exp < 0);
-    assert(max_exp > 0);
-
-    const int k = len;
-    const int n = len + decimal_exponent;
-
-    // v = buf * 10^(n-k)
-    // k is the length of the buffer (number of decimal digits)
-    // n is the position of the decimal point relative to the start of the buffer.
-
-    if (k <= n and n <= max_exp)
-    {
-        // digits[000]
-        // len <= max_exp + 2
-
-        std::memset(buf + k, '0', static_cast<size_t>(n - k));
-        // Make it look like a floating-point number (#362, #378)
-        buf[n + 0] = '.';
-        buf[n + 1] = '0';
-        return buf + (n + 2);
-    }
-
-    if (0 < n and n <= max_exp)
-    {
-        // dig.its
-        // len <= max_digits10 + 1
-
-        assert(k > n);
-
-        std::memmove(buf + (n + 1), buf + n, static_cast<size_t>(k - n));
-        buf[n] = '.';
-        return buf + (k + 1);
-    }
-
-    if (min_exp < n and n <= 0)
-    {
-        // 0.[000]digits
-        // len <= 2 + (-min_exp - 1) + max_digits10
-
-        std::memmove(buf + (2 + -n), buf, static_cast<size_t>(k));
-        buf[0] = '0';
-        buf[1] = '.';
-        std::memset(buf + 2, '0', static_cast<size_t>(-n));
-        return buf + (2 + (-n) + k);
-    }
-
-    if (k == 1)
-    {
-        // dE+123
-        // len <= 1 + 5
-
-        buf += 1;
-    }
-    else
-    {
-        // d.igitsE+123
-        // len <= max_digits10 + 1 + 5
-
-        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k - 1));
-        buf[1] = '.';
-        buf += 1 + k;
-    }
-
-    *buf++ = 'e';
-    return append_exponent(buf, n - 1);
-}
-
-} // namespace dtoa_impl
-
-/*!
-@brief generates a decimal representation of the floating-point number value in [first, last).
-
-The format of the resulting decimal representation is similar to printf's %g
-format. Returns an iterator pointing past-the-end of the decimal representation.
-
-@note The input number must be finite, i.e. NaN's and Inf's are not supported.
-@note The buffer must be large enough.
-@note The result is NOT null-terminated.
-*/
-template <typename FloatType>
-char* to_chars(char* first, const char* last, FloatType value)
-{
-    static_cast<void>(last); // maybe unused - fix warning
-    assert(std::isfinite(value));
-
-    // Use signbit(value) instead of (value < 0) since signbit works for -0.
-    if (std::signbit(value))
-    {
-        value = -value;
-        *first++ = '-';
-    }
-
-    if (value == 0) // +-0
-    {
-        *first++ = '0';
-        // Make it look like a floating-point number (#362, #378)
-        *first++ = '.';
-        *first++ = '0';
-        return first;
-    }
-
-    assert(last - first >= std::numeric_limits<FloatType>::max_digits10);
-
-    // Compute v = buffer * 10^decimal_exponent.
-    // The decimal digits are stored in the buffer, which needs to be interpreted
-    // as an unsigned decimal integer.
-    // len is the length of the buffer, i.e. the number of decimal digits.
-    int len = 0;
-    int decimal_exponent = 0;
-    dtoa_impl::grisu2(first, len, decimal_exponent, value);
-
-    assert(len <= std::numeric_limits<FloatType>::max_digits10);
-
-    // Format the buffer like printf("%.*g", prec, value)
-    constexpr int kMinExp = -4;
-    // Use digits10 here to increase compatibility with version 2.
-    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
-
-    assert(last - first >= kMaxExp + 2);
-    assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
-    assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
-
-    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
-}
-
-} // namespace detail
-} // namespace nlohmann
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-// #include <nlohmann/detail/meta/cpp_future.hpp>
-
-// #include <nlohmann/detail/output/binary_writer.hpp>
-
-// #include <nlohmann/detail/output/output_adapters.hpp>
-
-// #include <nlohmann/detail/value_t.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-///////////////////
-// serialization //
-///////////////////
-
-/// how to treat decoding errors
-enum class error_handler_t
-{
-    strict,  ///< throw a type_error exception in case of invalid UTF-8
-    replace, ///< replace invalid UTF-8 sequences with U+FFFD
-    ignore   ///< ignore invalid UTF-8 sequences
-};
-
-template<typename BasicJsonType>
-class serializer
-{
-    using string_t = typename BasicJsonType::string_t;
-    using number_float_t = typename BasicJsonType::number_float_t;
-    using number_integer_t = typename BasicJsonType::number_integer_t;
-    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-    static constexpr uint8_t UTF8_ACCEPT = 0;
-    static constexpr uint8_t UTF8_REJECT = 1;
-
-  public:
-    /*!
-    @param[in] s  output stream to serialize to
-    @param[in] ichar  indentation character to use
-    @param[in] error_handler_  how to react on decoding errors
-    */
-    serializer(output_adapter_t<char> s, const char ichar,
-               error_handler_t error_handler_ = error_handler_t::strict)
-        : o(std::move(s))
-        , loc(std::localeconv())
-        , thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep))
-        , decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point))
-        , indent_char(ichar)
-        , indent_string(512, indent_char)
-        , error_handler(error_handler_)
-    {}
-
-    // delete because of pointer members
-    serializer(const serializer&) = delete;
-    serializer& operator=(const serializer&) = delete;
-    serializer(serializer&&) = delete;
-    serializer& operator=(serializer&&) = delete;
-    ~serializer() = default;
-
-    /*!
-    @brief internal implementation of the serialization function
-
-    This function is called by the public member function dump and organizes
-    the serialization internally. The indentation level is propagated as
-    additional parameter. In case of arrays and objects, the function is
-    called recursively.
-
-    - strings and object keys are escaped using `escape_string()`
-    - integer numbers are converted implicitly via `operator<<`
-    - floating-point numbers are converted to a string using `"%g"` format
-
-    @param[in] val             value to serialize
-    @param[in] pretty_print    whether the output shall be pretty-printed
-    @param[in] indent_step     the indent level
-    @param[in] current_indent  the current indent level (only used internally)
-    */
-    void dump(const BasicJsonType& val, const bool pretty_print,
-              const bool ensure_ascii,
-              const unsigned int indent_step,
-              const unsigned int current_indent = 0)
-    {
-        switch (val.m_type)
-        {
-            case value_t::object:
-            {
-                if (val.m_value.object->empty())
-                {
-                    o->write_characters("{}", 2);
-                    return;
-                }
-
-                if (pretty_print)
-                {
-                    o->write_characters("{\n", 2);
-
-                    // variable to hold indentation for recursive calls
-                    const auto new_indent = current_indent + indent_step;
-                    if (JSON_UNLIKELY(indent_string.size() < new_indent))
-                    {
-                        indent_string.resize(indent_string.size() * 2, ' ');
-                    }
-
-                    // first n-1 elements
-                    auto i = val.m_value.object->cbegin();
-                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
-                    {
-                        o->write_characters(indent_string.c_str(), new_indent);
-                        o->write_character('\"');
-                        dump_escaped(i->first, ensure_ascii);
-                        o->write_characters("\": ", 3);
-                        dump(i->second, true, ensure_ascii, indent_step, new_indent);
-                        o->write_characters(",\n", 2);
-                    }
-
-                    // last element
-                    assert(i != val.m_value.object->cend());
-                    assert(std::next(i) == val.m_value.object->cend());
-                    o->write_characters(indent_string.c_str(), new_indent);
-                    o->write_character('\"');
-                    dump_escaped(i->first, ensure_ascii);
-                    o->write_characters("\": ", 3);
-                    dump(i->second, true, ensure_ascii, indent_step, new_indent);
-
-                    o->write_character('\n');
-                    o->write_characters(indent_string.c_str(), current_indent);
-                    o->write_character('}');
-                }
-                else
-                {
-                    o->write_character('{');
-
-                    // first n-1 elements
-                    auto i = val.m_value.object->cbegin();
-                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
-                    {
-                        o->write_character('\"');
-                        dump_escaped(i->first, ensure_ascii);
-                        o->write_characters("\":", 2);
-                        dump(i->second, false, ensure_ascii, indent_step, current_indent);
-                        o->write_character(',');
-                    }
-
-                    // last element
-                    assert(i != val.m_value.object->cend());
-                    assert(std::next(i) == val.m_value.object->cend());
-                    o->write_character('\"');
-                    dump_escaped(i->first, ensure_ascii);
-                    o->write_characters("\":", 2);
-                    dump(i->second, false, ensure_ascii, indent_step, current_indent);
-
-                    o->write_character('}');
-                }
-
-                return;
-            }
-
-            case value_t::array:
-            {
-                if (val.m_value.array->empty())
-                {
-                    o->write_characters("[]", 2);
-                    return;
-                }
-
-                if (pretty_print)
-                {
-                    o->write_characters("[\n", 2);
-
-                    // variable to hold indentation for recursive calls
-                    const auto new_indent = current_indent + indent_step;
-                    if (JSON_UNLIKELY(indent_string.size() < new_indent))
-                    {
-                        indent_string.resize(indent_string.size() * 2, ' ');
-                    }
-
-                    // first n-1 elements
-                    for (auto i = val.m_value.array->cbegin();
-                            i != val.m_value.array->cend() - 1; ++i)
-                    {
-                        o->write_characters(indent_string.c_str(), new_indent);
-                        dump(*i, true, ensure_ascii, indent_step, new_indent);
-                        o->write_characters(",\n", 2);
-                    }
-
-                    // last element
-                    assert(not val.m_value.array->empty());
-                    o->write_characters(indent_string.c_str(), new_indent);
-                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
-
-                    o->write_character('\n');
-                    o->write_characters(indent_string.c_str(), current_indent);
-                    o->write_character(']');
-                }
-                else
-                {
-                    o->write_character('[');
-
-                    // first n-1 elements
-                    for (auto i = val.m_value.array->cbegin();
-                            i != val.m_value.array->cend() - 1; ++i)
-                    {
-                        dump(*i, false, ensure_ascii, indent_step, current_indent);
-                        o->write_character(',');
-                    }
-
-                    // last element
-                    assert(not val.m_value.array->empty());
-                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
-
-                    o->write_character(']');
-                }
-
-                return;
-            }
-
-            case value_t::string:
-            {
-                o->write_character('\"');
-                dump_escaped(*val.m_value.string, ensure_ascii);
-                o->write_character('\"');
-                return;
-            }
-
-            case value_t::boolean:
-            {
-                if (val.m_value.boolean)
-                {
-                    o->write_characters("true", 4);
-                }
-                else
-                {
-                    o->write_characters("false", 5);
-                }
-                return;
-            }
-
-            case value_t::number_integer:
-            {
-                dump_integer(val.m_value.number_integer);
-                return;
-            }
-
-            case value_t::number_unsigned:
-            {
-                dump_integer(val.m_value.number_unsigned);
-                return;
-            }
-
-            case value_t::number_float:
-            {
-                dump_float(val.m_value.number_float);
-                return;
-            }
-
-            case value_t::discarded:
-            {
-                o->write_characters("<discarded>", 11);
-                return;
-            }
-
-            case value_t::null:
-            {
-                o->write_characters("null", 4);
-                return;
-            }
-        }
-    }
-
-  private:
-    /*!
-    @brief dump escaped string
-
-    Escape a string by replacing certain special characters by a sequence of an
-    escape character (backslash) and another character and other control
-    characters by a sequence of "\u" followed by a four-digit hex
-    representation. The escaped string is written to output stream @a o.
-
-    @param[in] s  the string to escape
-    @param[in] ensure_ascii  whether to escape non-ASCII characters with
-                             \uXXXX sequences
-
-    @complexity Linear in the length of string @a s.
-    */
-    void dump_escaped(const string_t& s, const bool ensure_ascii)
-    {
-        uint32_t codepoint;
-        uint8_t state = UTF8_ACCEPT;
-        std::size_t bytes = 0;  // number of bytes written to string_buffer
-
-        // number of bytes written at the point of the last valid byte
-        std::size_t bytes_after_last_accept = 0;
-        std::size_t undumped_chars = 0;
-
-        for (std::size_t i = 0; i < s.size(); ++i)
-        {
-            const auto byte = static_cast<uint8_t>(s[i]);
-
-            switch (decode(state, codepoint, byte))
-            {
-                case UTF8_ACCEPT:  // decode found a new code point
-                {
-                    switch (codepoint)
-                    {
-                        case 0x08: // backspace
-                        {
-                            string_buffer[bytes++] = '\\';
-                            string_buffer[bytes++] = 'b';
-                            break;
-                        }
-
-                        case 0x09: // horizontal tab
-                        {
-                            string_buffer[bytes++] = '\\';
-                            string_buffer[bytes++] = 't';
-                            break;
-                        }
-
-                        case 0x0A: // newline
-                        {
-                            string_buffer[bytes++] = '\\';
-                            string_buffer[bytes++] = 'n';
-                            break;
-                        }
-
-                        case 0x0C: // formfeed
-                        {
-                            string_buffer[bytes++] = '\\';
-                            string_buffer[bytes++] = 'f';
-                            break;
-                        }
-
-                        case 0x0D: // carriage return
-                        {
-                            string_buffer[bytes++] = '\\';
-                            string_buffer[bytes++] = 'r';
-                            break;
-                        }
-
-                        case 0x22: // quotation mark
-                        {
-                            string_buffer[bytes++] = '\\';
-                            string_buffer[bytes++] = '\"';
-                            break;
-                        }
-
-                        case 0x5C: // reverse solidus
-                        {
-                            string_buffer[bytes++] = '\\';
-                            string_buffer[bytes++] = '\\';
-                            break;
-                        }
-
-                        default:
-                        {
-                            // escape control characters (0x00..0x1F) or, if
-                            // ensure_ascii parameter is used, non-ASCII characters
-                            if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))
-                            {
-                                if (codepoint <= 0xFFFF)
-                                {
-                                    (std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
-                                                    static_cast<uint16_t>(codepoint));
-                                    bytes += 6;
-                                }
-                                else
-                                {
-                                    (std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
-                                                    static_cast<uint16_t>(0xD7C0 + (codepoint >> 10)),
-                                                    static_cast<uint16_t>(0xDC00 + (codepoint & 0x3FF)));
-                                    bytes += 12;
-                                }
-                            }
-                            else
-                            {
-                                // copy byte to buffer (all previous bytes
-                                // been copied have in default case above)
-                                string_buffer[bytes++] = s[i];
-                            }
-                            break;
-                        }
-                    }
-
-                    // write buffer and reset index; there must be 13 bytes
-                    // left, as this is the maximal number of bytes to be
-                    // written ("\uxxxx\uxxxx\0") for one code point
-                    if (string_buffer.size() - bytes < 13)
-                    {
-                        o->write_characters(string_buffer.data(), bytes);
-                        bytes = 0;
-                    }
-
-                    // remember the byte position of this accept
-                    bytes_after_last_accept = bytes;
-                    undumped_chars = 0;
-                    break;
-                }
-
-                case UTF8_REJECT:  // decode found invalid UTF-8 byte
-                {
-                    switch (error_handler)
-                    {
-                        case error_handler_t::strict:
-                        {
-                            std::string sn(3, '\0');
-                            (std::snprintf)(&sn[0], sn.size(), "%.2X", byte);
-                            JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn));
-                        }
-
-                        case error_handler_t::ignore:
-                        case error_handler_t::replace:
-                        {
-                            // in case we saw this character the first time, we
-                            // would like to read it again, because the byte
-                            // may be OK for itself, but just not OK for the
-                            // previous sequence
-                            if (undumped_chars > 0)
-                            {
-                                --i;
-                            }
-
-                            // reset length buffer to the last accepted index;
-                            // thus removing/ignoring the invalid characters
-                            bytes = bytes_after_last_accept;
-
-                            if (error_handler == error_handler_t::replace)
-                            {
-                                // add a replacement character
-                                if (ensure_ascii)
-                                {
-                                    string_buffer[bytes++] = '\\';
-                                    string_buffer[bytes++] = 'u';
-                                    string_buffer[bytes++] = 'f';
-                                    string_buffer[bytes++] = 'f';
-                                    string_buffer[bytes++] = 'f';
-                                    string_buffer[bytes++] = 'd';
-                                }
-                                else
-                                {
-                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
-                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
-                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
-                                }
-                                bytes_after_last_accept = bytes;
-                            }
-
-                            undumped_chars = 0;
-
-                            // continue processing the string
-                            state = UTF8_ACCEPT;
-                            break;
-                        }
-                    }
-                    break;
-                }
-
-                default:  // decode found yet incomplete multi-byte code point
-                {
-                    if (not ensure_ascii)
-                    {
-                        // code point will not be escaped - copy byte to buffer
-                        string_buffer[bytes++] = s[i];
-                    }
-                    ++undumped_chars;
-                    break;
-                }
-            }
-        }
-
-        // we finished processing the string
-        if (JSON_LIKELY(state == UTF8_ACCEPT))
-        {
-            // write buffer
-            if (bytes > 0)
-            {
-                o->write_characters(string_buffer.data(), bytes);
-            }
-        }
-        else
-        {
-            // we finish reading, but do not accept: string was incomplete
-            switch (error_handler)
-            {
-                case error_handler_t::strict:
-                {
-                    std::string sn(3, '\0');
-                    (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<uint8_t>(s.back()));
-                    JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn));
-                }
-
-                case error_handler_t::ignore:
-                {
-                    // write all accepted bytes
-                    o->write_characters(string_buffer.data(), bytes_after_last_accept);
-                    break;
-                }
-
-                case error_handler_t::replace:
-                {
-                    // write all accepted bytes
-                    o->write_characters(string_buffer.data(), bytes_after_last_accept);
-                    // add a replacement character
-                    if (ensure_ascii)
-                    {
-                        o->write_characters("\\ufffd", 6);
-                    }
-                    else
-                    {
-                        o->write_characters("\xEF\xBF\xBD", 3);
-                    }
-                    break;
-                }
-            }
-        }
-    }
-
-    /*!
-    @brief dump an integer
-
-    Dump a given integer to output stream @a o. Works internally with
-    @a number_buffer.
-
-    @param[in] x  integer number (signed or unsigned) to dump
-    @tparam NumberType either @a number_integer_t or @a number_unsigned_t
-    */
-    template<typename NumberType, detail::enable_if_t<
-                 std::is_same<NumberType, number_unsigned_t>::value or
-                 std::is_same<NumberType, number_integer_t>::value,
-                 int> = 0>
-    void dump_integer(NumberType x)
-    {
-        // special case for "0"
-        if (x == 0)
-        {
-            o->write_character('0');
-            return;
-        }
-
-        const bool is_negative = std::is_same<NumberType, number_integer_t>::value and not (x >= 0);  // see issue #755
-        std::size_t i = 0;
-
-        while (x != 0)
-        {
-            // spare 1 byte for '\0'
-            assert(i < number_buffer.size() - 1);
-
-            const auto digit = std::labs(static_cast<long>(x % 10));
-            number_buffer[i++] = static_cast<char>('0' + digit);
-            x /= 10;
-        }
-
-        if (is_negative)
-        {
-            // make sure there is capacity for the '-'
-            assert(i < number_buffer.size() - 2);
-            number_buffer[i++] = '-';
-        }
-
-        std::reverse(number_buffer.begin(), number_buffer.begin() + i);
-        o->write_characters(number_buffer.data(), i);
-    }
-
-    /*!
-    @brief dump a floating-point number
-
-    Dump a given floating-point number to output stream @a o. Works internally
-    with @a number_buffer.
-
-    @param[in] x  floating-point number to dump
-    */
-    void dump_float(number_float_t x)
-    {
-        // NaN / inf
-        if (not std::isfinite(x))
-        {
-            o->write_characters("null", 4);
-            return;
-        }
-
-        // If number_float_t is an IEEE-754 single or double precision number,
-        // use the Grisu2 algorithm to produce short numbers which are
-        // guaranteed to round-trip, using strtof and strtod, resp.
-        //
-        // NB: The test below works if <long double> == <double>.
-        static constexpr bool is_ieee_single_or_double
-            = (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 24 and std::numeric_limits<number_float_t>::max_exponent == 128) or
-              (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 53 and std::numeric_limits<number_float_t>::max_exponent == 1024);
-
-        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
-    }
-
-    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
-    {
-        char* begin = number_buffer.data();
-        char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
-
-        o->write_characters(begin, static_cast<size_t>(end - begin));
-    }
-
-    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
-    {
-        // get number of digits for a float -> text -> float round-trip
-        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
-
-        // the actual conversion
-        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
-
-        // negative value indicates an error
-        assert(len > 0);
-        // check if buffer was large enough
-        assert(static_cast<std::size_t>(len) < number_buffer.size());
-
-        // erase thousands separator
-        if (thousands_sep != '\0')
-        {
-            const auto end = std::remove(number_buffer.begin(),
-                                         number_buffer.begin() + len, thousands_sep);
-            std::fill(end, number_buffer.end(), '\0');
-            assert((end - number_buffer.begin()) <= len);
-            len = (end - number_buffer.begin());
-        }
-
-        // convert decimal point to '.'
-        if (decimal_point != '\0' and decimal_point != '.')
-        {
-            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
-            if (dec_pos != number_buffer.end())
-            {
-                *dec_pos = '.';
-            }
-        }
-
-        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
-
-        // determine if need to append ".0"
-        const bool value_is_int_like =
-            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
-                         [](char c)
-        {
-            return (c == '.' or c == 'e');
-        });
-
-        if (value_is_int_like)
-        {
-            o->write_characters(".0", 2);
-        }
-    }
-
-    /*!
-    @brief check whether a string is UTF-8 encoded
-
-    The function checks each byte of a string whether it is UTF-8 encoded. The
-    result of the check is stored in the @a state parameter. The function must
-    be called initially with state 0 (accept). State 1 means the string must
-    be rejected, because the current byte is not allowed. If the string is
-    completely processed, but the state is non-zero, the string ended
-    prematurely; that is, the last byte indicated more bytes should have
-    followed.
-
-    @param[in,out] state  the state of the decoding
-    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)
-    @param[in] byte       next byte to decode
-    @return               new state
-
-    @note The function has been edited: a std::array is used.
-
-    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
-    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
-    */
-    static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept
-    {
-        static const std::array<uint8_t, 400> utf8d =
-        {
-            {
-                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
-                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
-                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
-                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
-                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
-                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
-                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
-                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
-                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
-                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
-                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
-                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
-                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
-                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
-            }
-        };
-
-        const uint8_t type = utf8d[byte];
-
-        codep = (state != UTF8_ACCEPT)
-                ? (byte & 0x3fu) | (codep << 6)
-                : static_cast<uint32_t>(0xff >> type) & (byte);
-
-        state = utf8d[256u + state * 16u + type];
-        return state;
-    }
-
-  private:
-    /// the output of the serializer
-    output_adapter_t<char> o = nullptr;
-
-    /// a (hopefully) large enough character buffer
-    std::array<char, 64> number_buffer{{}};
-
-    /// the locale
-    const std::lconv* loc = nullptr;
-    /// the locale's thousand separator character
-    const char thousands_sep = '\0';
-    /// the locale's decimal point character
-    const char decimal_point = '\0';
-
-    /// string buffer
-    std::array<char, 512> string_buffer{{}};
-
-    /// the indentation character
-    const char indent_char;
-    /// the indentation string
-    string_t indent_string;
-
-    /// error_handler how to react on decoding errors
-    const error_handler_t error_handler;
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/json_ref.hpp>
-
-
-#include <initializer_list>
-#include <utility>
-
-// #include <nlohmann/detail/meta/type_traits.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-template<typename BasicJsonType>
-class json_ref
-{
-  public:
-    using value_type = BasicJsonType;
-
-    json_ref(value_type&& value)
-        : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)
-    {}
-
-    json_ref(const value_type& value)
-        : value_ref(const_cast<value_type*>(&value)), is_rvalue(false)
-    {}
-
-    json_ref(std::initializer_list<json_ref> init)
-        : owned_value(init), value_ref(&owned_value), is_rvalue(true)
-    {}
-
-    template <
-        class... Args,
-        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >
-    json_ref(Args && ... args)
-        : owned_value(std::forward<Args>(args)...), value_ref(&owned_value),
-          is_rvalue(true) {}
-
-    // class should be movable only
-    json_ref(json_ref&&) = default;
-    json_ref(const json_ref&) = delete;
-    json_ref& operator=(const json_ref&) = delete;
-    json_ref& operator=(json_ref&&) = delete;
-    ~json_ref() = default;
-
-    value_type moved_or_copied() const
-    {
-        if (is_rvalue)
-        {
-            return std::move(*value_ref);
-        }
-        return *value_ref;
-    }
-
-    value_type const& operator*() const
-    {
-        return *static_cast<value_type const*>(value_ref);
-    }
-
-    value_type const* operator->() const
-    {
-        return static_cast<value_type const*>(value_ref);
-    }
-
-  private:
-    mutable value_type owned_value = nullptr;
-    value_type* value_ref = nullptr;
-    const bool is_rvalue;
-};
-}  // namespace detail
-}  // namespace nlohmann
-
-// #include <nlohmann/detail/json_pointer.hpp>
-
-
-#include <cassert> // assert
-#include <numeric> // accumulate
-#include <string> // string
-#include <vector> // vector
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-// #include <nlohmann/detail/exceptions.hpp>
-
-// #include <nlohmann/detail/value_t.hpp>
-
-
-namespace nlohmann
-{
-template<typename BasicJsonType>
-class json_pointer
-{
-    // allow basic_json to access private members
-    NLOHMANN_BASIC_JSON_TPL_DECLARATION
-    friend class basic_json;
-
-  public:
-    /*!
-    @brief create JSON pointer
-
-    Create a JSON pointer according to the syntax described in
-    [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
-
-    @param[in] s  string representing the JSON pointer; if omitted, the empty
-                  string is assumed which references the whole JSON value
-
-    @throw parse_error.107 if the given JSON pointer @a s is nonempty and does
-                           not begin with a slash (`/`); see example below
-
-    @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is
-    not followed by `0` (representing `~`) or `1` (representing `/`); see
-    example below
-
-    @liveexample{The example shows the construction several valid JSON pointers
-    as well as the exceptional behavior.,json_pointer}
-
-    @since version 2.0.0
-    */
-    explicit json_pointer(const std::string& s = "")
-        : reference_tokens(split(s))
-    {}
-
-    /*!
-    @brief return a string representation of the JSON pointer
-
-    @invariant For each JSON pointer `ptr`, it holds:
-    @code {.cpp}
-    ptr == json_pointer(ptr.to_string());
-    @endcode
-
-    @return a string representation of the JSON pointer
-
-    @liveexample{The example shows the result of `to_string`.,
-    json_pointer__to_string}
-
-    @since version 2.0.0
-    */
-    std::string to_string() const
-    {
-        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
-                               std::string{},
-                               [](const std::string & a, const std::string & b)
-        {
-            return a + "/" + escape(b);
-        });
-    }
-
-    /// @copydoc to_string()
-    operator std::string() const
-    {
-        return to_string();
-    }
-
-    /*!
-    @param[in] s  reference token to be converted into an array index
-
-    @return integer representation of @a s
-
-    @throw out_of_range.404 if string @a s could not be converted to an integer
-    */
-    static int array_index(const std::string& s)
-    {
-        std::size_t processed_chars = 0;
-        const int res = std::stoi(s, &processed_chars);
-
-        // check if the string was completely read
-        if (JSON_UNLIKELY(processed_chars != s.size()))
-        {
-            JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
-        }
-
-        return res;
-    }
-
-  private:
-    /*!
-    @brief remove and return last reference pointer
-    @throw out_of_range.405 if JSON pointer has no parent
-    */
-    std::string pop_back()
-    {
-        if (JSON_UNLIKELY(is_root()))
-        {
-            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
-        }
-
-        auto last = reference_tokens.back();
-        reference_tokens.pop_back();
-        return last;
-    }
-
-    /// return whether pointer points to the root document
-    bool is_root() const noexcept
-    {
-        return reference_tokens.empty();
-    }
-
-    json_pointer top() const
-    {
-        if (JSON_UNLIKELY(is_root()))
-        {
-            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
-        }
-
-        json_pointer result = *this;
-        result.reference_tokens = {reference_tokens[0]};
-        return result;
-    }
-
-    /*!
-    @brief create and return a reference to the pointed to value
-
-    @complexity Linear in the number of reference tokens.
-
-    @throw parse_error.109 if array index is not a number
-    @throw type_error.313 if value cannot be unflattened
-    */
-    BasicJsonType& get_and_create(BasicJsonType& j) const
-    {
-        using size_type = typename BasicJsonType::size_type;
-        auto result = &j;
-
-        // in case no reference tokens exist, return a reference to the JSON value
-        // j which will be overwritten by a primitive value
-        for (const auto& reference_token : reference_tokens)
-        {
-            switch (result->m_type)
-            {
-                case detail::value_t::null:
-                {
-                    if (reference_token == "0")
-                    {
-                        // start a new array if reference token is 0
-                        result = &result->operator[](0);
-                    }
-                    else
-                    {
-                        // start a new object otherwise
-                        result = &result->operator[](reference_token);
-                    }
-                    break;
-                }
-
-                case detail::value_t::object:
-                {
-                    // create an entry in the object
-                    result = &result->operator[](reference_token);
-                    break;
-                }
-
-                case detail::value_t::array:
-                {
-                    // create an entry in the array
-                    JSON_TRY
-                    {
-                        result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
-                    }
-                    JSON_CATCH(std::invalid_argument&)
-                    {
-                        JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
-                    }
-                    break;
-                }
-
-                /*
-                The following code is only reached if there exists a reference
-                token _and_ the current value is primitive. In this case, we have
-                an error situation, because primitive values may only occur as
-                single value; that is, with an empty list of reference tokens.
-                */
-                default:
-                    JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
-            }
-        }
-
-        return *result;
-    }
-
-    /*!
-    @brief return a reference to the pointed to value
-
-    @note This version does not throw if a value is not present, but tries to
-          create nested values instead. For instance, calling this function
-          with pointer `"/this/that"` on a null value is equivalent to calling
-          `operator[]("this").operator[]("that")` on that value, effectively
-          changing the null value to an object.
-
-    @param[in] ptr  a JSON value
-
-    @return reference to the JSON value pointed to by the JSON pointer
-
-    @complexity Linear in the length of the JSON pointer.
-
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-    */
-    BasicJsonType& get_unchecked(BasicJsonType* ptr) const
-    {
-        using size_type = typename BasicJsonType::size_type;
-        for (const auto& reference_token : reference_tokens)
-        {
-            // convert null values to arrays or objects before continuing
-            if (ptr->m_type == detail::value_t::null)
-            {
-                // check if reference token is a number
-                const bool nums =
-                    std::all_of(reference_token.begin(), reference_token.end(),
-                                [](const char x)
-                {
-                    return (x >= '0' and x <= '9');
-                });
-
-                // change value to array for numbers or "-" or to object otherwise
-                *ptr = (nums or reference_token == "-")
-                       ? detail::value_t::array
-                       : detail::value_t::object;
-            }
-
-            switch (ptr->m_type)
-            {
-                case detail::value_t::object:
-                {
-                    // use unchecked object access
-                    ptr = &ptr->operator[](reference_token);
-                    break;
-                }
-
-                case detail::value_t::array:
-                {
-                    // error condition (cf. RFC 6901, Sect. 4)
-                    if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
-                    {
-                        JSON_THROW(detail::parse_error::create(106, 0,
-                                                               "array index '" + reference_token +
-                                                               "' must not begin with '0'"));
-                    }
-
-                    if (reference_token == "-")
-                    {
-                        // explicitly treat "-" as index beyond the end
-                        ptr = &ptr->operator[](ptr->m_value.array->size());
-                    }
-                    else
-                    {
-                        // convert array index to number; unchecked access
-                        JSON_TRY
-                        {
-                            ptr = &ptr->operator[](
-                                static_cast<size_type>(array_index(reference_token)));
-                        }
-                        JSON_CATCH(std::invalid_argument&)
-                        {
-                            JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
-                        }
-                    }
-                    break;
-                }
-
-                default:
-                    JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
-            }
-        }
-
-        return *ptr;
-    }
-
-    /*!
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.402  if the array index '-' is used
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-    */
-    BasicJsonType& get_checked(BasicJsonType* ptr) const
-    {
-        using size_type = typename BasicJsonType::size_type;
-        for (const auto& reference_token : reference_tokens)
-        {
-            switch (ptr->m_type)
-            {
-                case detail::value_t::object:
-                {
-                    // note: at performs range check
-                    ptr = &ptr->at(reference_token);
-                    break;
-                }
-
-                case detail::value_t::array:
-                {
-                    if (JSON_UNLIKELY(reference_token == "-"))
-                    {
-                        // "-" always fails the range check
-                        JSON_THROW(detail::out_of_range::create(402,
-                                                                "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
-                                                                ") is out of range"));
-                    }
-
-                    // error condition (cf. RFC 6901, Sect. 4)
-                    if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
-                    {
-                        JSON_THROW(detail::parse_error::create(106, 0,
-                                                               "array index '" + reference_token +
-                                                               "' must not begin with '0'"));
-                    }
-
-                    // note: at performs range check
-                    JSON_TRY
-                    {
-                        ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
-                    }
-                    JSON_CATCH(std::invalid_argument&)
-                    {
-                        JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
-                    }
-                    break;
-                }
-
-                default:
-                    JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
-            }
-        }
-
-        return *ptr;
-    }
-
-    /*!
-    @brief return a const reference to the pointed to value
-
-    @param[in] ptr  a JSON value
-
-    @return const reference to the JSON value pointed to by the JSON
-    pointer
-
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.402  if the array index '-' is used
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-    */
-    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
-    {
-        using size_type = typename BasicJsonType::size_type;
-        for (const auto& reference_token : reference_tokens)
-        {
-            switch (ptr->m_type)
-            {
-                case detail::value_t::object:
-                {
-                    // use unchecked object access
-                    ptr = &ptr->operator[](reference_token);
-                    break;
-                }
-
-                case detail::value_t::array:
-                {
-                    if (JSON_UNLIKELY(reference_token == "-"))
-                    {
-                        // "-" cannot be used for const access
-                        JSON_THROW(detail::out_of_range::create(402,
-                                                                "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
-                                                                ") is out of range"));
-                    }
-
-                    // error condition (cf. RFC 6901, Sect. 4)
-                    if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
-                    {
-                        JSON_THROW(detail::parse_error::create(106, 0,
-                                                               "array index '" + reference_token +
-                                                               "' must not begin with '0'"));
-                    }
-
-                    // use unchecked array access
-                    JSON_TRY
-                    {
-                        ptr = &ptr->operator[](
-                            static_cast<size_type>(array_index(reference_token)));
-                    }
-                    JSON_CATCH(std::invalid_argument&)
-                    {
-                        JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
-                    }
-                    break;
-                }
-
-                default:
-                    JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
-            }
-        }
-
-        return *ptr;
-    }
-
-    /*!
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.402  if the array index '-' is used
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-    */
-    const BasicJsonType& get_checked(const BasicJsonType* ptr) const
-    {
-        using size_type = typename BasicJsonType::size_type;
-        for (const auto& reference_token : reference_tokens)
-        {
-            switch (ptr->m_type)
-            {
-                case detail::value_t::object:
-                {
-                    // note: at performs range check
-                    ptr = &ptr->at(reference_token);
-                    break;
-                }
-
-                case detail::value_t::array:
-                {
-                    if (JSON_UNLIKELY(reference_token == "-"))
-                    {
-                        // "-" always fails the range check
-                        JSON_THROW(detail::out_of_range::create(402,
-                                                                "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
-                                                                ") is out of range"));
-                    }
-
-                    // error condition (cf. RFC 6901, Sect. 4)
-                    if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
-                    {
-                        JSON_THROW(detail::parse_error::create(106, 0,
-                                                               "array index '" + reference_token +
-                                                               "' must not begin with '0'"));
-                    }
-
-                    // note: at performs range check
-                    JSON_TRY
-                    {
-                        ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
-                    }
-                    JSON_CATCH(std::invalid_argument&)
-                    {
-                        JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
-                    }
-                    break;
-                }
-
-                default:
-                    JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
-            }
-        }
-
-        return *ptr;
-    }
-
-    /*!
-    @brief split the string input to reference tokens
-
-    @note This function is only called by the json_pointer constructor.
-          All exceptions below are documented there.
-
-    @throw parse_error.107  if the pointer is not empty or begins with '/'
-    @throw parse_error.108  if character '~' is not followed by '0' or '1'
-    */
-    static std::vector<std::string> split(const std::string& reference_string)
-    {
-        std::vector<std::string> result;
-
-        // special case: empty reference string -> no reference tokens
-        if (reference_string.empty())
-        {
-            return result;
-        }
-
-        // check if nonempty reference string begins with slash
-        if (JSON_UNLIKELY(reference_string[0] != '/'))
-        {
-            JSON_THROW(detail::parse_error::create(107, 1,
-                                                   "JSON pointer must be empty or begin with '/' - was: '" +
-                                                   reference_string + "'"));
-        }
-
-        // extract the reference tokens:
-        // - slash: position of the last read slash (or end of string)
-        // - start: position after the previous slash
-        for (
-            // search for the first slash after the first character
-            std::size_t slash = reference_string.find_first_of('/', 1),
-            // set the beginning of the first reference token
-            start = 1;
-            // we can stop if start == 0 (if slash == std::string::npos)
-            start != 0;
-            // set the beginning of the next reference token
-            // (will eventually be 0 if slash == std::string::npos)
-            start = (slash == std::string::npos) ? 0 : slash + 1,
-            // find next slash
-            slash = reference_string.find_first_of('/', start))
-        {
-            // use the text between the beginning of the reference token
-            // (start) and the last slash (slash).
-            auto reference_token = reference_string.substr(start, slash - start);
-
-            // check reference tokens are properly escaped
-            for (std::size_t pos = reference_token.find_first_of('~');
-                    pos != std::string::npos;
-                    pos = reference_token.find_first_of('~', pos + 1))
-            {
-                assert(reference_token[pos] == '~');
-
-                // ~ must be followed by 0 or 1
-                if (JSON_UNLIKELY(pos == reference_token.size() - 1 or
-                                  (reference_token[pos + 1] != '0' and
-                                   reference_token[pos + 1] != '1')))
-                {
-                    JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
-                }
-            }
-
-            // finally, store the reference token
-            unescape(reference_token);
-            result.push_back(reference_token);
-        }
-
-        return result;
-    }
-
-    /*!
-    @brief replace all occurrences of a substring by another string
-
-    @param[in,out] s  the string to manipulate; changed so that all
-                   occurrences of @a f are replaced with @a t
-    @param[in]     f  the substring to replace with @a t
-    @param[in]     t  the string to replace @a f
-
-    @pre The search string @a f must not be empty. **This precondition is
-    enforced with an assertion.**
-
-    @since version 2.0.0
-    */
-    static void replace_substring(std::string& s, const std::string& f,
-                                  const std::string& t)
-    {
-        assert(not f.empty());
-        for (auto pos = s.find(f);                // find first occurrence of f
-                pos != std::string::npos;         // make sure f was found
-                s.replace(pos, f.size(), t),      // replace with t, and
-                pos = s.find(f, pos + t.size()))  // find next occurrence of f
-        {}
-    }
-
-    /// escape "~" to "~0" and "/" to "~1"
-    static std::string escape(std::string s)
-    {
-        replace_substring(s, "~", "~0");
-        replace_substring(s, "/", "~1");
-        return s;
-    }
-
-    /// unescape "~1" to tilde and "~0" to slash (order is important!)
-    static void unescape(std::string& s)
-    {
-        replace_substring(s, "~1", "/");
-        replace_substring(s, "~0", "~");
-    }
-
-    /*!
-    @param[in] reference_string  the reference string to the current value
-    @param[in] value             the value to consider
-    @param[in,out] result        the result object to insert values to
-
-    @note Empty objects or arrays are flattened to `null`.
-    */
-    static void flatten(const std::string& reference_string,
-                        const BasicJsonType& value,
-                        BasicJsonType& result)
-    {
-        switch (value.m_type)
-        {
-            case detail::value_t::array:
-            {
-                if (value.m_value.array->empty())
-                {
-                    // flatten empty array as null
-                    result[reference_string] = nullptr;
-                }
-                else
-                {
-                    // iterate array and use index as reference string
-                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
-                    {
-                        flatten(reference_string + "/" + std::to_string(i),
-                                value.m_value.array->operator[](i), result);
-                    }
-                }
-                break;
-            }
-
-            case detail::value_t::object:
-            {
-                if (value.m_value.object->empty())
-                {
-                    // flatten empty object as null
-                    result[reference_string] = nullptr;
-                }
-                else
-                {
-                    // iterate object and use keys as reference string
-                    for (const auto& element : *value.m_value.object)
-                    {
-                        flatten(reference_string + "/" + escape(element.first), element.second, result);
-                    }
-                }
-                break;
-            }
-
-            default:
-            {
-                // add primitive value with its reference string
-                result[reference_string] = value;
-                break;
-            }
-        }
-    }
-
-    /*!
-    @param[in] value  flattened JSON
-
-    @return unflattened JSON
-
-    @throw parse_error.109 if array index is not a number
-    @throw type_error.314  if value is not an object
-    @throw type_error.315  if object values are not primitive
-    @throw type_error.313  if value cannot be unflattened
-    */
-    static BasicJsonType
-    unflatten(const BasicJsonType& value)
-    {
-        if (JSON_UNLIKELY(not value.is_object()))
-        {
-            JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
-        }
-
-        BasicJsonType result;
-
-        // iterate the JSON object values
-        for (const auto& element : *value.m_value.object)
-        {
-            if (JSON_UNLIKELY(not element.second.is_primitive()))
-            {
-                JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
-            }
-
-            // assign value to reference pointed to by JSON pointer; Note that if
-            // the JSON pointer is "" (i.e., points to the whole value), function
-            // get_and_create returns a reference to result itself. An assignment
-            // will then create a primitive value.
-            json_pointer(element.first).get_and_create(result) = element.second;
-        }
-
-        return result;
-    }
-
-    friend bool operator==(json_pointer const& lhs,
-                           json_pointer const& rhs) noexcept
-    {
-        return (lhs.reference_tokens == rhs.reference_tokens);
-    }
-
-    friend bool operator!=(json_pointer const& lhs,
-                           json_pointer const& rhs) noexcept
-    {
-        return not (lhs == rhs);
-    }
-
-    /// the reference tokens
-    std::vector<std::string> reference_tokens;
-};
-}  // namespace nlohmann
-
-// #include <nlohmann/adl_serializer.hpp>
-
-
-#include <utility>
-
-// #include <nlohmann/detail/conversions/from_json.hpp>
-
-// #include <nlohmann/detail/conversions/to_json.hpp>
-
-
-namespace nlohmann
-{
-
-template<typename, typename>
-struct adl_serializer
-{
-    /*!
-    @brief convert a JSON value to any value type
-
-    This function is usually called by the `get()` function of the
-    @ref basic_json class (either explicit or via conversion operators).
-
-    @param[in] j        JSON value to read from
-    @param[in,out] val  value to write to
-    */
-    template<typename BasicJsonType, typename ValueType>
-    static auto from_json(BasicJsonType&& j, ValueType& val) noexcept(
-        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
-    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
-    {
-        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
-    }
-
-    /*!
-    @brief convert any value type to a JSON value
-
-    This function is usually called by the constructors of the @ref basic_json
-    class.
-
-    @param[in,out] j  JSON value to write to
-    @param[in] val    value to read from
-    */
-    template <typename BasicJsonType, typename ValueType>
-    static auto to_json(BasicJsonType& j, ValueType&& val) noexcept(
-        noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
-    -> decltype(::nlohmann::to_json(j, std::forward<ValueType>(val)), void())
-    {
-        ::nlohmann::to_json(j, std::forward<ValueType>(val));
-    }
-};
-
-}  // namespace nlohmann
-
-
-/*!
-@brief namespace for Niels Lohmann
-@see https://github.com/nlohmann
-@since version 1.0.0
-*/
-namespace nlohmann
-{
-
-/*!
-@brief a class to store JSON values
-
-@tparam ObjectType type for JSON objects (`std::map` by default; will be used
-in @ref object_t)
-@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used
-in @ref array_t)
-@tparam StringType type for JSON strings and object keys (`std::string` by
-default; will be used in @ref string_t)
-@tparam BooleanType type for JSON booleans (`bool` by default; will be used
-in @ref boolean_t)
-@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by
-default; will be used in @ref number_integer_t)
-@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c
-`uint64_t` by default; will be used in @ref number_unsigned_t)
-@tparam NumberFloatType type for JSON floating-point numbers (`double` by
-default; will be used in @ref number_float_t)
-@tparam AllocatorType type of the allocator to use (`std::allocator` by
-default)
-@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`
-and `from_json()` (@ref adl_serializer by default)
-
-@requirement The class satisfies the following concept requirements:
-- Basic
- - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):
-   JSON values can be default constructed. The result will be a JSON null
-   value.
- - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible):
-   A JSON value can be constructed from an rvalue argument.
- - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible):
-   A JSON value can be copy-constructed from an lvalue expression.
- - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable):
-   A JSON value van be assigned from an rvalue argument.
- - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):
-   A JSON value can be copy-assigned from an lvalue expression.
- - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible):
-   JSON values can be destructed.
-- Layout
- - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType):
-   JSON values have
-   [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout):
-   All non-static data members are private and standard layout types, the
-   class has no virtual functions or (virtual) base classes.
-- Library-wide
- - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable):
-   JSON values can be compared with `==`, see @ref
-   operator==(const_reference,const_reference).
- - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):
-   JSON values can be compared with `<`, see @ref
-   operator<(const_reference,const_reference).
- - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable):
-   Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of
-   other compatible types, using unqualified function call @ref swap().
- - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer):
-   JSON values can be compared against `std::nullptr_t` objects which are used
-   to model the `null` value.
-- Container
- - [Container](https://en.cppreference.com/w/cpp/named_req/Container):
-   JSON values can be used like STL containers and provide iterator access.
- - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer);
-   JSON values can be used like STL containers and provide reverse iterator
-   access.
-
-@invariant The member variables @a m_value and @a m_type have the following
-relationship:
-- If `m_type == value_t::object`, then `m_value.object != nullptr`.
-- If `m_type == value_t::array`, then `m_value.array != nullptr`.
-- If `m_type == value_t::string`, then `m_value.string != nullptr`.
-The invariants are checked by member function assert_invariant().
-
-@internal
-@note ObjectType trick from http://stackoverflow.com/a/9860911
-@endinternal
-
-@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange
-Format](http://rfc7159.net/rfc7159)
-
-@since version 1.0.0
-
-@nosubgrouping
-*/
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-class basic_json
-{
-  private:
-    template<detail::value_t> friend struct detail::external_constructor;
-    friend ::nlohmann::json_pointer<basic_json>;
-    friend ::nlohmann::detail::parser<basic_json>;
-    friend ::nlohmann::detail::serializer<basic_json>;
-    template<typename BasicJsonType>
-    friend class ::nlohmann::detail::iter_impl;
-    template<typename BasicJsonType, typename CharType>
-    friend class ::nlohmann::detail::binary_writer;
-    template<typename BasicJsonType, typename SAX>
-    friend class ::nlohmann::detail::binary_reader;
-    template<typename BasicJsonType>
-    friend class ::nlohmann::detail::json_sax_dom_parser;
-    template<typename BasicJsonType>
-    friend class ::nlohmann::detail::json_sax_dom_callback_parser;
-
-    /// workaround type for MSVC
-    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
-
-    // convenience aliases for types residing in namespace detail;
-    using lexer = ::nlohmann::detail::lexer<basic_json>;
-    using parser = ::nlohmann::detail::parser<basic_json>;
-
-    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
-    template<typename BasicJsonType>
-    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
-    template<typename BasicJsonType>
-    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;
-    template<typename Iterator>
-    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;
-    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;
-
-    template<typename CharType>
-    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;
-
-    using binary_reader = ::nlohmann::detail::binary_reader<basic_json>;
-    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
-
-    using serializer = ::nlohmann::detail::serializer<basic_json>;
-
-  public:
-    using value_t = detail::value_t;
-    /// JSON Pointer, see @ref nlohmann::json_pointer
-    using json_pointer = ::nlohmann::json_pointer<basic_json>;
-    template<typename T, typename SFINAE>
-    using json_serializer = JSONSerializer<T, SFINAE>;
-    /// how to treat decoding errors
-    using error_handler_t = detail::error_handler_t;
-    /// helper type for initializer lists of basic_json values
-    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
-
-    using input_format_t = detail::input_format_t;
-    /// SAX interface type, see @ref nlohmann::json_sax
-    using json_sax_t = json_sax<basic_json>;
-
-    ////////////////
-    // exceptions //
-    ////////////////
-
-    /// @name exceptions
-    /// Classes to implement user-defined exceptions.
-    /// @{
-
-    /// @copydoc detail::exception
-    using exception = detail::exception;
-    /// @copydoc detail::parse_error
-    using parse_error = detail::parse_error;
-    /// @copydoc detail::invalid_iterator
-    using invalid_iterator = detail::invalid_iterator;
-    /// @copydoc detail::type_error
-    using type_error = detail::type_error;
-    /// @copydoc detail::out_of_range
-    using out_of_range = detail::out_of_range;
-    /// @copydoc detail::other_error
-    using other_error = detail::other_error;
-
-    /// @}
-
-
-    /////////////////////
-    // container types //
-    /////////////////////
-
-    /// @name container types
-    /// The canonic container types to use @ref basic_json like any other STL
-    /// container.
-    /// @{
-
-    /// the type of elements in a basic_json container
-    using value_type = basic_json;
-
-    /// the type of an element reference
-    using reference = value_type&;
-    /// the type of an element const reference
-    using const_reference = const value_type&;
-
-    /// a type to represent differences between iterators
-    using difference_type = std::ptrdiff_t;
-    /// a type to represent container sizes
-    using size_type = std::size_t;
-
-    /// the allocator type
-    using allocator_type = AllocatorType<basic_json>;
-
-    /// the type of an element pointer
-    using pointer = typename std::allocator_traits<allocator_type>::pointer;
-    /// the type of an element const pointer
-    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
-
-    /// an iterator for a basic_json container
-    using iterator = iter_impl<basic_json>;
-    /// a const iterator for a basic_json container
-    using const_iterator = iter_impl<const basic_json>;
-    /// a reverse iterator for a basic_json container
-    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;
-    /// a const reverse iterator for a basic_json container
-    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;
-
-    /// @}
-
-
-    /*!
-    @brief returns the allocator associated with the container
-    */
-    static allocator_type get_allocator()
-    {
-        return allocator_type();
-    }
-
-    /*!
-    @brief returns version information on the library
-
-    This function returns a JSON object with information about the library,
-    including the version number and information on the platform and compiler.
-
-    @return JSON object holding version information
-    key         | description
-    ----------- | ---------------
-    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).
-    `copyright` | The copyright line for the library as string.
-    `name`      | The name of the library as string.
-    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.
-    `url`       | The URL of the project as string.
-    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).
-
-    @liveexample{The following code shows an example output of the `meta()`
-    function.,meta}
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @complexity Constant.
-
-    @since 2.1.0
-    */
-    static basic_json meta()
-    {
-        basic_json result;
-
-        result["copyright"] = "(C) 2013-2017 Niels Lohmann";
-        result["name"] = "JSON for Modern C++";
-        result["url"] = "https://github.com/nlohmann/json";
-        result["version"]["string"] =
-            std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." +
-            std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." +
-            std::to_string(NLOHMANN_JSON_VERSION_PATCH);
-        result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
-        result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
-        result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
-
-#ifdef _WIN32
-        result["platform"] = "win32";
-#elif defined __linux__
-        result["platform"] = "linux";
-#elif defined __APPLE__
-        result["platform"] = "apple";
-#elif defined __unix__
-        result["platform"] = "unix";
-#else
-        result["platform"] = "unknown";
-#endif
-
-#if defined(__ICC) || defined(__INTEL_COMPILER)
-        result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
-#elif defined(__clang__)
-        result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
-#elif defined(__GNUC__) || defined(__GNUG__)
-        result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
-#elif defined(__HP_cc) || defined(__HP_aCC)
-        result["compiler"] = "hp"
-#elif defined(__IBMCPP__)
-        result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
-#elif defined(_MSC_VER)
-        result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
-#elif defined(__PGI)
-        result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
-#elif defined(__SUNPRO_CC)
-        result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
-#else
-        result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
-#endif
-
-#ifdef __cplusplus
-        result["compiler"]["c++"] = std::to_string(__cplusplus);
-#else
-        result["compiler"]["c++"] = "unknown";
-#endif
-        return result;
-    }
-
-
-    ///////////////////////////
-    // JSON value data types //
-    ///////////////////////////
-
-    /// @name JSON value data types
-    /// The data types to store a JSON value. These types are derived from
-    /// the template arguments passed to class @ref basic_json.
-    /// @{
-
-#if defined(JSON_HAS_CPP_14)
-    // Use transparent comparator if possible, combined with perfect forwarding
-    // on find() and count() calls prevents unnecessary string construction.
-    using object_comparator_t = std::less<>;
-#else
-    using object_comparator_t = std::less<StringType>;
-#endif
-
-    /*!
-    @brief a type for an object
-
-    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows:
-    > An object is an unordered collection of zero or more name/value pairs,
-    > where a name is a string and a value is a string, number, boolean, null,
-    > object, or array.
-
-    To store objects in C++, a type is defined by the template parameters
-    described below.
-
-    @tparam ObjectType  the container to store objects (e.g., `std::map` or
-    `std::unordered_map`)
-    @tparam StringType the type of the keys or names (e.g., `std::string`).
-    The comparison function `std::less<StringType>` is used to order elements
-    inside the container.
-    @tparam AllocatorType the allocator to use for objects (e.g.,
-    `std::allocator`)
-
-    #### Default type
-
-    With the default values for @a ObjectType (`std::map`), @a StringType
-    (`std::string`), and @a AllocatorType (`std::allocator`), the default
-    value for @a object_t is:
-
-    @code {.cpp}
-    std::map<
-      std::string, // key_type
-      basic_json, // value_type
-      std::less<std::string>, // key_compare
-      std::allocator<std::pair<const std::string, basic_json>> // allocator_type
-    >
-    @endcode
-
-    #### Behavior
-
-    The choice of @a object_t influences the behavior of the JSON class. With
-    the default type, objects have the following behavior:
-
-    - When all names are unique, objects will be interoperable in the sense
-      that all software implementations receiving that object will agree on
-      the name-value mappings.
-    - When the names within an object are not unique, it is unspecified which
-      one of the values for a given key will be chosen. For instance,
-      `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or
-      `{"key": 2}`.
-    - Internally, name/value pairs are stored in lexicographical order of the
-      names. Objects will also be serialized (see @ref dump) in this order.
-      For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored
-      and serialized as `{"a": 2, "b": 1}`.
-    - When comparing objects, the order of the name/value pairs is irrelevant.
-      This makes objects interoperable in the sense that they will not be
-      affected by these differences. For instance, `{"b": 1, "a": 2}` and
-      `{"a": 2, "b": 1}` will be treated as equal.
-
-    #### Limits
-
-    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
-    > An implementation may set limits on the maximum depth of nesting.
-
-    In this class, the object's limit of nesting is not explicitly constrained.
-    However, a maximum depth of nesting may be introduced by the compiler or
-    runtime environment. A theoretical limit can be queried by calling the
-    @ref max_size function of a JSON object.
-
-    #### Storage
-
-    Objects are stored as pointers in a @ref basic_json type. That is, for any
-    access to object values, a pointer of type `object_t*` must be
-    dereferenced.
-
-    @sa @ref array_t -- type for an array value
-
-    @since version 1.0.0
-
-    @note The order name/value pairs are added to the object is *not*
-    preserved by the library. Therefore, iterating an object may return
-    name/value pairs in a different order than they were originally stored. In
-    fact, keys will be traversed in alphabetical order as `std::map` with
-    `std::less` is used by default. Please note this behavior conforms to [RFC
-    7159](http://rfc7159.net/rfc7159), because any order implements the
-    specified "unordered" nature of JSON objects.
-    */
-    using object_t = ObjectType<StringType,
-          basic_json,
-          object_comparator_t,
-          AllocatorType<std::pair<const StringType,
-          basic_json>>>;
-
-    /*!
-    @brief a type for an array
-
-    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows:
-    > An array is an ordered sequence of zero or more values.
-
-    To store objects in C++, a type is defined by the template parameters
-    explained below.
-
-    @tparam ArrayType  container type to store arrays (e.g., `std::vector` or
-    `std::list`)
-    @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)
-
-    #### Default type
-
-    With the default values for @a ArrayType (`std::vector`) and @a
-    AllocatorType (`std::allocator`), the default value for @a array_t is:
-
-    @code {.cpp}
-    std::vector<
-      basic_json, // value_type
-      std::allocator<basic_json> // allocator_type
-    >
-    @endcode
-
-    #### Limits
-
-    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
-    > An implementation may set limits on the maximum depth of nesting.
-
-    In this class, the array's limit of nesting is not explicitly constrained.
-    However, a maximum depth of nesting may be introduced by the compiler or
-    runtime environment. A theoretical limit can be queried by calling the
-    @ref max_size function of a JSON array.
-
-    #### Storage
-
-    Arrays are stored as pointers in a @ref basic_json type. That is, for any
-    access to array values, a pointer of type `array_t*` must be dereferenced.
-
-    @sa @ref object_t -- type for an object value
-
-    @since version 1.0.0
-    */
-    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
-
-    /*!
-    @brief a type for a string
-
-    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows:
-    > A string is a sequence of zero or more Unicode characters.
-
-    To store objects in C++, a type is defined by the template parameter
-    described below. Unicode values are split by the JSON class into
-    byte-sized characters during deserialization.
-
-    @tparam StringType  the container to store strings (e.g., `std::string`).
-    Note this container is used for keys/names in objects, see @ref object_t.
-
-    #### Default type
-
-    With the default values for @a StringType (`std::string`), the default
-    value for @a string_t is:
-
-    @code {.cpp}
-    std::string
-    @endcode
-
-    #### Encoding
-
-    Strings are stored in UTF-8 encoding. Therefore, functions like
-    `std::string::size()` or `std::string::length()` return the number of
-    bytes in the string rather than the number of characters or glyphs.
-
-    #### String comparison
-
-    [RFC 7159](http://rfc7159.net/rfc7159) states:
-    > Software implementations are typically required to test names of object
-    > members for equality. Implementations that transform the textual
-    > representation into sequences of Unicode code units and then perform the
-    > comparison numerically, code unit by code unit, are interoperable in the
-    > sense that implementations will agree in all cases on equality or
-    > inequality of two strings. For example, implementations that compare
-    > strings with escaped characters unconverted may incorrectly find that
-    > `"a\\b"` and `"a\u005Cb"` are not equal.
-
-    This implementation is interoperable as it does compare strings code unit
-    by code unit.
-
-    #### Storage
-
-    String values are stored as pointers in a @ref basic_json type. That is,
-    for any access to string values, a pointer of type `string_t*` must be
-    dereferenced.
-
-    @since version 1.0.0
-    */
-    using string_t = StringType;
-
-    /*!
-    @brief a type for a boolean
-
-    [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a
-    type which differentiates the two literals `true` and `false`.
-
-    To store objects in C++, a type is defined by the template parameter @a
-    BooleanType which chooses the type to use.
-
-    #### Default type
-
-    With the default values for @a BooleanType (`bool`), the default value for
-    @a boolean_t is:
-
-    @code {.cpp}
-    bool
-    @endcode
-
-    #### Storage
-
-    Boolean values are stored directly inside a @ref basic_json type.
-
-    @since version 1.0.0
-    */
-    using boolean_t = BooleanType;
-
-    /*!
-    @brief a type for a number (integer)
-
-    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
-    > The representation of numbers is similar to that used in most
-    > programming languages. A number is represented in base 10 using decimal
-    > digits. It contains an integer component that may be prefixed with an
-    > optional minus sign, which may be followed by a fraction part and/or an
-    > exponent part. Leading zeros are not allowed. (...) Numeric values that
-    > cannot be represented in the grammar below (such as Infinity and NaN)
-    > are not permitted.
-
-    This description includes both integer and floating-point numbers.
-    However, C++ allows more precise storage if it is known whether the number
-    is a signed integer, an unsigned integer or a floating-point number.
-    Therefore, three different types, @ref number_integer_t, @ref
-    number_unsigned_t and @ref number_float_t are used.
-
-    To store integer numbers in C++, a type is defined by the template
-    parameter @a NumberIntegerType which chooses the type to use.
-
-    #### Default type
-
-    With the default values for @a NumberIntegerType (`int64_t`), the default
-    value for @a number_integer_t is:
-
-    @code {.cpp}
-    int64_t
-    @endcode
-
-    #### Default behavior
-
-    - The restrictions about leading zeros is not enforced in C++. Instead,
-      leading zeros in integer literals lead to an interpretation as octal
-      number. Internally, the value will be stored as decimal number. For
-      instance, the C++ integer literal `010` will be serialized to `8`.
-      During deserialization, leading zeros yield an error.
-    - Not-a-number (NaN) values will be serialized to `null`.
-
-    #### Limits
-
-    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
-    > An implementation may set limits on the range and precision of numbers.
-
-    When the default type is used, the maximal integer number that can be
-    stored is `9223372036854775807` (INT64_MAX) and the minimal integer number
-    that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers
-    that are out of range will yield over/underflow when used in a
-    constructor. During deserialization, too large or small integer numbers
-    will be automatically be stored as @ref number_unsigned_t or @ref
-    number_float_t.
-
-    [RFC 7159](http://rfc7159.net/rfc7159) further states:
-    > Note that when such software is used, numbers that are integers and are
-    > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense
-    > that implementations will agree exactly on their numeric values.
-
-    As this range is a subrange of the exactly supported range [INT64_MIN,
-    INT64_MAX], this class's integer type is interoperable.
-
-    #### Storage
-
-    Integer number values are stored directly inside a @ref basic_json type.
-
-    @sa @ref number_float_t -- type for number values (floating-point)
-
-    @sa @ref number_unsigned_t -- type for number values (unsigned integer)
-
-    @since version 1.0.0
-    */
-    using number_integer_t = NumberIntegerType;
-
-    /*!
-    @brief a type for a number (unsigned)
-
-    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
-    > The representation of numbers is similar to that used in most
-    > programming languages. A number is represented in base 10 using decimal
-    > digits. It contains an integer component that may be prefixed with an
-    > optional minus sign, which may be followed by a fraction part and/or an
-    > exponent part. Leading zeros are not allowed. (...) Numeric values that
-    > cannot be represented in the grammar below (such as Infinity and NaN)
-    > are not permitted.
-
-    This description includes both integer and floating-point numbers.
-    However, C++ allows more precise storage if it is known whether the number
-    is a signed integer, an unsigned integer or a floating-point number.
-    Therefore, three different types, @ref number_integer_t, @ref
-    number_unsigned_t and @ref number_float_t are used.
-
-    To store unsigned integer numbers in C++, a type is defined by the
-    template parameter @a NumberUnsignedType which chooses the type to use.
-
-    #### Default type
-
-    With the default values for @a NumberUnsignedType (`uint64_t`), the
-    default value for @a number_unsigned_t is:
-
-    @code {.cpp}
-    uint64_t
-    @endcode
-
-    #### Default behavior
-
-    - The restrictions about leading zeros is not enforced in C++. Instead,
-      leading zeros in integer literals lead to an interpretation as octal
-      number. Internally, the value will be stored as decimal number. For
-      instance, the C++ integer literal `010` will be serialized to `8`.
-      During deserialization, leading zeros yield an error.
-    - Not-a-number (NaN) values will be serialized to `null`.
-
-    #### Limits
-
-    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
-    > An implementation may set limits on the range and precision of numbers.
-
-    When the default type is used, the maximal integer number that can be
-    stored is `18446744073709551615` (UINT64_MAX) and the minimal integer
-    number that can be stored is `0`. Integer numbers that are out of range
-    will yield over/underflow when used in a constructor. During
-    deserialization, too large or small integer numbers will be automatically
-    be stored as @ref number_integer_t or @ref number_float_t.
-
-    [RFC 7159](http://rfc7159.net/rfc7159) further states:
-    > Note that when such software is used, numbers that are integers and are
-    > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense
-    > that implementations will agree exactly on their numeric values.
-
-    As this range is a subrange (when considered in conjunction with the
-    number_integer_t type) of the exactly supported range [0, UINT64_MAX],
-    this class's integer type is interoperable.
-
-    #### Storage
-
-    Integer number values are stored directly inside a @ref basic_json type.
-
-    @sa @ref number_float_t -- type for number values (floating-point)
-    @sa @ref number_integer_t -- type for number values (integer)
-
-    @since version 2.0.0
-    */
-    using number_unsigned_t = NumberUnsignedType;
-
-    /*!
-    @brief a type for a number (floating-point)
-
-    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
-    > The representation of numbers is similar to that used in most
-    > programming languages. A number is represented in base 10 using decimal
-    > digits. It contains an integer component that may be prefixed with an
-    > optional minus sign, which may be followed by a fraction part and/or an
-    > exponent part. Leading zeros are not allowed. (...) Numeric values that
-    > cannot be represented in the grammar below (such as Infinity and NaN)
-    > are not permitted.
-
-    This description includes both integer and floating-point numbers.
-    However, C++ allows more precise storage if it is known whether the number
-    is a signed integer, an unsigned integer or a floating-point number.
-    Therefore, three different types, @ref number_integer_t, @ref
-    number_unsigned_t and @ref number_float_t are used.
-
-    To store floating-point numbers in C++, a type is defined by the template
-    parameter @a NumberFloatType which chooses the type to use.
-
-    #### Default type
-
-    With the default values for @a NumberFloatType (`double`), the default
-    value for @a number_float_t is:
-
-    @code {.cpp}
-    double
-    @endcode
-
-    #### Default behavior
-
-    - The restrictions about leading zeros is not enforced in C++. Instead,
-      leading zeros in floating-point literals will be ignored. Internally,
-      the value will be stored as decimal number. For instance, the C++
-      floating-point literal `01.2` will be serialized to `1.2`. During
-      deserialization, leading zeros yield an error.
-    - Not-a-number (NaN) values will be serialized to `null`.
-
-    #### Limits
-
-    [RFC 7159](http://rfc7159.net/rfc7159) states:
-    > This specification allows implementations to set limits on the range and
-    > precision of numbers accepted. Since software that implements IEEE
-    > 754-2008 binary64 (double precision) numbers is generally available and
-    > widely used, good interoperability can be achieved by implementations
-    > that expect no more precision or range than these provide, in the sense
-    > that implementations will approximate JSON numbers within the expected
-    > precision.
-
-    This implementation does exactly follow this approach, as it uses double
-    precision floating-point numbers. Note values smaller than
-    `-1.79769313486232e+308` and values greater than `1.79769313486232e+308`
-    will be stored as NaN internally and be serialized to `null`.
-
-    #### Storage
-
-    Floating-point number values are stored directly inside a @ref basic_json
-    type.
-
-    @sa @ref number_integer_t -- type for number values (integer)
-
-    @sa @ref number_unsigned_t -- type for number values (unsigned integer)
-
-    @since version 1.0.0
-    */
-    using number_float_t = NumberFloatType;
-
-    /// @}
-
-  private:
-
-    /// helper for exception-safe object creation
-    template<typename T, typename... Args>
-    static T* create(Args&& ... args)
-    {
-        AllocatorType<T> alloc;
-        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;
-
-        auto deleter = [&](T * object)
-        {
-            AllocatorTraits::deallocate(alloc, object, 1);
-        };
-        std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);
-        AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);
-        assert(object != nullptr);
-        return object.release();
-    }
-
-    ////////////////////////
-    // JSON value storage //
-    ////////////////////////
-
-    /*!
-    @brief a JSON value
-
-    The actual storage for a JSON value of the @ref basic_json class. This
-    union combines the different storage types for the JSON value types
-    defined in @ref value_t.
-
-    JSON type | value_t type    | used type
-    --------- | --------------- | ------------------------
-    object    | object          | pointer to @ref object_t
-    array     | array           | pointer to @ref array_t
-    string    | string          | pointer to @ref string_t
-    boolean   | boolean         | @ref boolean_t
-    number    | number_integer  | @ref number_integer_t
-    number    | number_unsigned | @ref number_unsigned_t
-    number    | number_float    | @ref number_float_t
-    null      | null            | *no value is stored*
-
-    @note Variable-length types (objects, arrays, and strings) are stored as
-    pointers. The size of the union should not exceed 64 bits if the default
-    value types are used.
-
-    @since version 1.0.0
-    */
-    union json_value
-    {
-        /// object (stored with pointer to save storage)
-        object_t* object;
-        /// array (stored with pointer to save storage)
-        array_t* array;
-        /// string (stored with pointer to save storage)
-        string_t* string;
-        /// boolean
-        boolean_t boolean;
-        /// number (integer)
-        number_integer_t number_integer;
-        /// number (unsigned integer)
-        number_unsigned_t number_unsigned;
-        /// number (floating-point)
-        number_float_t number_float;
-
-        /// default constructor (for null values)
-        json_value() = default;
-        /// constructor for booleans
-        json_value(boolean_t v) noexcept : boolean(v) {}
-        /// constructor for numbers (integer)
-        json_value(number_integer_t v) noexcept : number_integer(v) {}
-        /// constructor for numbers (unsigned)
-        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}
-        /// constructor for numbers (floating-point)
-        json_value(number_float_t v) noexcept : number_float(v) {}
-        /// constructor for empty values of a given type
-        json_value(value_t t)
-        {
-            switch (t)
-            {
-                case value_t::object:
-                {
-                    object = create<object_t>();
-                    break;
-                }
-
-                case value_t::array:
-                {
-                    array = create<array_t>();
-                    break;
-                }
-
-                case value_t::string:
-                {
-                    string = create<string_t>("");
-                    break;
-                }
-
-                case value_t::boolean:
-                {
-                    boolean = boolean_t(false);
-                    break;
-                }
-
-                case value_t::number_integer:
-                {
-                    number_integer = number_integer_t(0);
-                    break;
-                }
-
-                case value_t::number_unsigned:
-                {
-                    number_unsigned = number_unsigned_t(0);
-                    break;
-                }
-
-                case value_t::number_float:
-                {
-                    number_float = number_float_t(0.0);
-                    break;
-                }
-
-                case value_t::null:
-                {
-                    object = nullptr;  // silence warning, see #821
-                    break;
-                }
-
-                default:
-                {
-                    object = nullptr;  // silence warning, see #821
-                    if (JSON_UNLIKELY(t == value_t::null))
-                    {
-                        JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.5.0")); // LCOV_EXCL_LINE
-                    }
-                    break;
-                }
-            }
-        }
-
-        /// constructor for strings
-        json_value(const string_t& value)
-        {
-            string = create<string_t>(value);
-        }
-
-        /// constructor for rvalue strings
-        json_value(string_t&& value)
-        {
-            string = create<string_t>(std::move(value));
-        }
-
-        /// constructor for objects
-        json_value(const object_t& value)
-        {
-            object = create<object_t>(value);
-        }
-
-        /// constructor for rvalue objects
-        json_value(object_t&& value)
-        {
-            object = create<object_t>(std::move(value));
-        }
-
-        /// constructor for arrays
-        json_value(const array_t& value)
-        {
-            array = create<array_t>(value);
-        }
-
-        /// constructor for rvalue arrays
-        json_value(array_t&& value)
-        {
-            array = create<array_t>(std::move(value));
-        }
-
-        void destroy(value_t t) noexcept
-        {
-            switch (t)
-            {
-                case value_t::object:
-                {
-                    AllocatorType<object_t> alloc;
-                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
-                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
-                    break;
-                }
-
-                case value_t::array:
-                {
-                    AllocatorType<array_t> alloc;
-                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
-                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
-                    break;
-                }
-
-                case value_t::string:
-                {
-                    AllocatorType<string_t> alloc;
-                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
-                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
-                    break;
-                }
-
-                default:
-                {
-                    break;
-                }
-            }
-        }
-    };
-
-    /*!
-    @brief checks the class invariants
-
-    This function asserts the class invariants. It needs to be called at the
-    end of every constructor to make sure that created objects respect the
-    invariant. Furthermore, it has to be called each time the type of a JSON
-    value is changed, because the invariant expresses a relationship between
-    @a m_type and @a m_value.
-    */
-    void assert_invariant() const noexcept
-    {
-        assert(m_type != value_t::object or m_value.object != nullptr);
-        assert(m_type != value_t::array or m_value.array != nullptr);
-        assert(m_type != value_t::string or m_value.string != nullptr);
-    }
-
-  public:
-    //////////////////////////
-    // JSON parser callback //
-    //////////////////////////
-
-    /*!
-    @brief parser event types
-
-    The parser callback distinguishes the following events:
-    - `object_start`: the parser read `{` and started to process a JSON object
-    - `key`: the parser read a key of a value in an object
-    - `object_end`: the parser read `}` and finished processing a JSON object
-    - `array_start`: the parser read `[` and started to process a JSON array
-    - `array_end`: the parser read `]` and finished processing a JSON array
-    - `value`: the parser finished reading a JSON value
-
-    @image html callback_events.png "Example when certain parse events are triggered"
-
-    @sa @ref parser_callback_t for more information and examples
-    */
-    using parse_event_t = typename parser::parse_event_t;
-
-    /*!
-    @brief per-element parser callback type
-
-    With a parser callback function, the result of parsing a JSON text can be
-    influenced. When passed to @ref parse, it is called on certain events
-    (passed as @ref parse_event_t via parameter @a event) with a set recursion
-    depth @a depth and context JSON value @a parsed. The return value of the
-    callback function is a boolean indicating whether the element that emitted
-    the callback shall be kept or not.
-
-    We distinguish six scenarios (determined by the event type) in which the
-    callback function can be called. The following table describes the values
-    of the parameters @a depth, @a event, and @a parsed.
-
-    parameter @a event | description | parameter @a depth | parameter @a parsed
-    ------------------ | ----------- | ------------------ | -------------------
-    parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded
-    parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key
-    parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object
-    parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded
-    parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array
-    parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value
-
-    @image html callback_events.png "Example when certain parse events are triggered"
-
-    Discarding a value (i.e., returning `false`) has different effects
-    depending on the context in which function was called:
-
-    - Discarded values in structured types are skipped. That is, the parser
-      will behave as if the discarded value was never read.
-    - In case a value outside a structured type is skipped, it is replaced
-      with `null`. This case happens if the top-level element is skipped.
-
-    @param[in] depth  the depth of the recursion during parsing
-
-    @param[in] event  an event of type parse_event_t indicating the context in
-    the callback function has been called
-
-    @param[in,out] parsed  the current intermediate parse result; note that
-    writing to this value has no effect for parse_event_t::key events
-
-    @return Whether the JSON value which called the function during parsing
-    should be kept (`true`) or not (`false`). In the latter case, it is either
-    skipped completely or replaced by an empty discarded object.
-
-    @sa @ref parse for examples
-
-    @since version 1.0.0
-    */
-    using parser_callback_t = typename parser::parser_callback_t;
-
-    //////////////////
-    // constructors //
-    //////////////////
-
-    /// @name constructors and destructors
-    /// Constructors of class @ref basic_json, copy/move constructor, copy
-    /// assignment, static functions creating objects, and the destructor.
-    /// @{
-
-    /*!
-    @brief create an empty value with a given type
-
-    Create an empty JSON value with a given type. The value will be default
-    initialized with an empty value which depends on the type:
-
-    Value type  | initial value
-    ----------- | -------------
-    null        | `null`
-    boolean     | `false`
-    string      | `""`
-    number      | `0`
-    object      | `{}`
-    array       | `[]`
-
-    @param[in] v  the type of the value to create
-
-    @complexity Constant.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The following code shows the constructor for different @ref
-    value_t values,basic_json__value_t}
-
-    @sa @ref clear() -- restores the postcondition of this constructor
-
-    @since version 1.0.0
-    */
-    basic_json(const value_t v)
-        : m_type(v), m_value(v)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a null object
-
-    Create a `null` JSON value. It either takes a null pointer as parameter
-    (explicitly creating `null`) or no parameter (implicitly creating `null`).
-    The passed null pointer itself is not read -- it is only used to choose
-    the right constructor.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this constructor never throws
-    exceptions.
-
-    @liveexample{The following code shows the constructor with and without a
-    null pointer parameter.,basic_json__nullptr_t}
-
-    @since version 1.0.0
-    */
-    basic_json(std::nullptr_t = nullptr) noexcept
-        : basic_json(value_t::null)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a JSON value
-
-    This is a "catch all" constructor for all compatible JSON types; that is,
-    types for which a `to_json()` method exists. The constructor forwards the
-    parameter @a val to that method (to `json_serializer<U>::to_json` method
-    with `U = uncvref_t<CompatibleType>`, to be exact).
-
-    Template type @a CompatibleType includes, but is not limited to, the
-    following types:
-    - **arrays**: @ref array_t and all kinds of compatible containers such as
-      `std::vector`, `std::deque`, `std::list`, `std::forward_list`,
-      `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,
-      `std::multiset`, and `std::unordered_multiset` with a `value_type` from
-      which a @ref basic_json value can be constructed.
-    - **objects**: @ref object_t and all kinds of compatible associative
-      containers such as `std::map`, `std::unordered_map`, `std::multimap`,
-      and `std::unordered_multimap` with a `key_type` compatible to
-      @ref string_t and a `value_type` from which a @ref basic_json value can
-      be constructed.
-    - **strings**: @ref string_t, string literals, and all compatible string
-      containers can be used.
-    - **numbers**: @ref number_integer_t, @ref number_unsigned_t,
-      @ref number_float_t, and all convertible number types such as `int`,
-      `size_t`, `int64_t`, `float` or `double` can be used.
-    - **boolean**: @ref boolean_t / `bool` can be used.
-
-    See the examples below.
-
-    @tparam CompatibleType a type such that:
-    - @a CompatibleType is not derived from `std::istream`,
-    - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
-         constructors),
-    - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)
-    - @a CompatibleType is not a @ref basic_json nested type (e.g.,
-         @ref json_pointer, @ref iterator, etc ...)
-    - @ref @ref json_serializer<U> has a
-         `to_json(basic_json_t&, CompatibleType&&)` method
-
-    @tparam U = `uncvref_t<CompatibleType>`
-
-    @param[in] val the value to be forwarded to the respective constructor
-
-    @complexity Usually linear in the size of the passed @a val, also
-                depending on the implementation of the called `to_json()`
-                method.
-
-    @exceptionsafety Depends on the called constructor. For types directly
-    supported by the library (i.e., all types for which no `to_json()` function
-    was provided), strong guarantee holds: if an exception is thrown, there are
-    no changes to any JSON value.
-
-    @liveexample{The following code shows the constructor with several
-    compatible types.,basic_json__CompatibleType}
-
-    @since version 2.1.0
-    */
-    template <typename CompatibleType,
-              typename U = detail::uncvref_t<CompatibleType>,
-              detail::enable_if_t<
-                  not detail::is_basic_json<U>::value and detail::is_compatible_type<basic_json_t, U>::value, int> = 0>
-    basic_json(CompatibleType && val) noexcept(noexcept(
-                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
-                                           std::forward<CompatibleType>(val))))
-    {
-        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a JSON value from an existing one
-
-    This is a constructor for existing @ref basic_json types.
-    It does not hijack copy/move constructors, since the parameter has different
-    template arguments than the current ones.
-
-    The constructor tries to convert the internal @ref m_value of the parameter.
-
-    @tparam BasicJsonType a type such that:
-    - @a BasicJsonType is a @ref basic_json type.
-    - @a BasicJsonType has different template arguments than @ref basic_json_t.
-
-    @param[in] val the @ref basic_json value to be converted.
-
-    @complexity Usually linear in the size of the passed @a val, also
-                depending on the implementation of the called `to_json()`
-                method.
-
-    @exceptionsafety Depends on the called constructor. For types directly
-    supported by the library (i.e., all types for which no `to_json()` function
-    was provided), strong guarantee holds: if an exception is thrown, there are
-    no changes to any JSON value.
-
-    @since version 3.2.0
-    */
-    template <typename BasicJsonType,
-              detail::enable_if_t<
-                  detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0>
-    basic_json(const BasicJsonType& val)
-    {
-        using other_boolean_t = typename BasicJsonType::boolean_t;
-        using other_number_float_t = typename BasicJsonType::number_float_t;
-        using other_number_integer_t = typename BasicJsonType::number_integer_t;
-        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
-        using other_string_t = typename BasicJsonType::string_t;
-        using other_object_t = typename BasicJsonType::object_t;
-        using other_array_t = typename BasicJsonType::array_t;
-
-        switch (val.type())
-        {
-            case value_t::boolean:
-                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
-                break;
-            case value_t::number_float:
-                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
-                break;
-            case value_t::number_integer:
-                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
-                break;
-            case value_t::number_unsigned:
-                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
-                break;
-            case value_t::string:
-                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
-                break;
-            case value_t::object:
-                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
-                break;
-            case value_t::array:
-                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
-                break;
-            case value_t::null:
-                *this = nullptr;
-                break;
-            case value_t::discarded:
-                m_type = value_t::discarded;
-                break;
-        }
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a container (array or object) from an initializer list
-
-    Creates a JSON value of type array or object from the passed initializer
-    list @a init. In case @a type_deduction is `true` (default), the type of
-    the JSON value to be created is deducted from the initializer list @a init
-    according to the following rules:
-
-    1. If the list is empty, an empty JSON object value `{}` is created.
-    2. If the list consists of pairs whose first element is a string, a JSON
-       object value is created where the first elements of the pairs are
-       treated as keys and the second elements are as values.
-    3. In all other cases, an array is created.
-
-    The rules aim to create the best fit between a C++ initializer list and
-    JSON values. The rationale is as follows:
-
-    1. The empty initializer list is written as `{}` which is exactly an empty
-       JSON object.
-    2. C++ has no way of describing mapped types other than to list a list of
-       pairs. As JSON requires that keys must be of type string, rule 2 is the
-       weakest constraint one can pose on initializer lists to interpret them
-       as an object.
-    3. In all other cases, the initializer list could not be interpreted as
-       JSON object type, so interpreting it as JSON array type is safe.
-
-    With the rules described above, the following JSON values cannot be
-    expressed by an initializer list:
-
-    - the empty array (`[]`): use @ref array(initializer_list_t)
-      with an empty initializer list in this case
-    - arrays whose elements satisfy rule 2: use @ref
-      array(initializer_list_t) with the same initializer list
-      in this case
-
-    @note When used without parentheses around an empty initializer list, @ref
-    basic_json() is called instead of this function, yielding the JSON null
-    value.
-
-    @param[in] init  initializer list with JSON values
-
-    @param[in] type_deduction internal parameter; when set to `true`, the type
-    of the JSON value is deducted from the initializer list @a init; when set
-    to `false`, the type provided via @a manual_type is forced. This mode is
-    used by the functions @ref array(initializer_list_t) and
-    @ref object(initializer_list_t).
-
-    @param[in] manual_type internal parameter; when @a type_deduction is set
-    to `false`, the created JSON value will use the provided type (only @ref
-    value_t::array and @ref value_t::object are valid); when @a type_deduction
-    is set to `true`, this parameter has no effect
-
-    @throw type_error.301 if @a type_deduction is `false`, @a manual_type is
-    `value_t::object`, but @a init contains an element which is not a pair
-    whose first element is a string. In this case, the constructor could not
-    create an object. If @a type_deduction would have be `true`, an array
-    would have been created. See @ref object(initializer_list_t)
-    for an example.
-
-    @complexity Linear in the size of the initializer list @a init.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The example below shows how JSON values are created from
-    initializer lists.,basic_json__list_init_t}
-
-    @sa @ref array(initializer_list_t) -- create a JSON array
-    value from an initializer list
-    @sa @ref object(initializer_list_t) -- create a JSON object
-    value from an initializer list
-
-    @since version 1.0.0
-    */
-    basic_json(initializer_list_t init,
-               bool type_deduction = true,
-               value_t manual_type = value_t::array)
-    {
-        // check if each element is an array with two elements whose first
-        // element is a string
-        bool is_an_object = std::all_of(init.begin(), init.end(),
-                                        [](const detail::json_ref<basic_json>& element_ref)
-        {
-            return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string());
-        });
-
-        // adjust type if type deduction is not wanted
-        if (not type_deduction)
-        {
-            // if array is wanted, do not create an object though possible
-            if (manual_type == value_t::array)
-            {
-                is_an_object = false;
-            }
-
-            // if object is wanted but impossible, throw an exception
-            if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object))
-            {
-                JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
-            }
-        }
-
-        if (is_an_object)
-        {
-            // the initializer list is a list of pairs -> create object
-            m_type = value_t::object;
-            m_value = value_t::object;
-
-            std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref)
-            {
-                auto element = element_ref.moved_or_copied();
-                m_value.object->emplace(
-                    std::move(*((*element.m_value.array)[0].m_value.string)),
-                    std::move((*element.m_value.array)[1]));
-            });
-        }
-        else
-        {
-            // the initializer list describes an array -> create array
-            m_type = value_t::array;
-            m_value.array = create<array_t>(init.begin(), init.end());
-        }
-
-        assert_invariant();
-    }
-
-    /*!
-    @brief explicitly create an array from an initializer list
-
-    Creates a JSON array value from a given initializer list. That is, given a
-    list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the
-    initializer list is empty, the empty array `[]` is created.
-
-    @note This function is only needed to express two edge cases that cannot
-    be realized with the initializer list constructor (@ref
-    basic_json(initializer_list_t, bool, value_t)). These cases
-    are:
-    1. creating an array whose elements are all pairs whose first element is a
-    string -- in this case, the initializer list constructor would create an
-    object, taking the first elements as keys
-    2. creating an empty array -- passing the empty initializer list to the
-    initializer list constructor yields an empty object
-
-    @param[in] init  initializer list with JSON values to create an array from
-    (optional)
-
-    @return JSON array value
-
-    @complexity Linear in the size of @a init.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The following code shows an example for the `array`
-    function.,array}
-
-    @sa @ref basic_json(initializer_list_t, bool, value_t) --
-    create a JSON value from an initializer list
-    @sa @ref object(initializer_list_t) -- create a JSON object
-    value from an initializer list
-
-    @since version 1.0.0
-    */
-    static basic_json array(initializer_list_t init = {})
-    {
-        return basic_json(init, false, value_t::array);
-    }
-
-    /*!
-    @brief explicitly create an object from an initializer list
-
-    Creates a JSON object value from a given initializer list. The initializer
-    lists elements must be pairs, and their first elements must be strings. If
-    the initializer list is empty, the empty object `{}` is created.
-
-    @note This function is only added for symmetry reasons. In contrast to the
-    related function @ref array(initializer_list_t), there are
-    no cases which can only be expressed by this function. That is, any
-    initializer list @a init can also be passed to the initializer list
-    constructor @ref basic_json(initializer_list_t, bool, value_t).
-
-    @param[in] init  initializer list to create an object from (optional)
-
-    @return JSON object value
-
-    @throw type_error.301 if @a init is not a list of pairs whose first
-    elements are strings. In this case, no object can be created. When such a
-    value is passed to @ref basic_json(initializer_list_t, bool, value_t),
-    an array would have been created from the passed initializer list @a init.
-    See example below.
-
-    @complexity Linear in the size of @a init.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The following code shows an example for the `object`
-    function.,object}
-
-    @sa @ref basic_json(initializer_list_t, bool, value_t) --
-    create a JSON value from an initializer list
-    @sa @ref array(initializer_list_t) -- create a JSON array
-    value from an initializer list
-
-    @since version 1.0.0
-    */
-    static basic_json object(initializer_list_t init = {})
-    {
-        return basic_json(init, false, value_t::object);
-    }
-
-    /*!
-    @brief construct an array with count copies of given value
-
-    Constructs a JSON array value by creating @a cnt copies of a passed value.
-    In case @a cnt is `0`, an empty array is created.
-
-    @param[in] cnt  the number of JSON copies of @a val to create
-    @param[in] val  the JSON value to copy
-
-    @post `std::distance(begin(),end()) == cnt` holds.
-
-    @complexity Linear in @a cnt.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The following code shows examples for the @ref
-    basic_json(size_type\, const basic_json&)
-    constructor.,basic_json__size_type_basic_json}
-
-    @since version 1.0.0
-    */
-    basic_json(size_type cnt, const basic_json& val)
-        : m_type(value_t::array)
-    {
-        m_value.array = create<array_t>(cnt, val);
-        assert_invariant();
-    }
-
-    /*!
-    @brief construct a JSON container given an iterator range
-
-    Constructs the JSON value with the contents of the range `[first, last)`.
-    The semantics depends on the different types a JSON value can have:
-    - In case of a null type, invalid_iterator.206 is thrown.
-    - In case of other primitive types (number, boolean, or string), @a first
-      must be `begin()` and @a last must be `end()`. In this case, the value is
-      copied. Otherwise, invalid_iterator.204 is thrown.
-    - In case of structured types (array, object), the constructor behaves as
-      similar versions for `std::vector` or `std::map`; that is, a JSON array
-      or object is constructed from the values in the range.
-
-    @tparam InputIT an input iterator type (@ref iterator or @ref
-    const_iterator)
-
-    @param[in] first begin of the range to copy from (included)
-    @param[in] last end of the range to copy from (excluded)
-
-    @pre Iterators @a first and @a last must be initialized. **This
-         precondition is enforced with an assertion (see warning).** If
-         assertions are switched off, a violation of this precondition yields
-         undefined behavior.
-
-    @pre Range `[first, last)` is valid. Usually, this precondition cannot be
-         checked efficiently. Only certain edge cases are detected; see the
-         description of the exceptions below. A violation of this precondition
-         yields undefined behavior.
-
-    @warning A precondition is enforced with a runtime assertion that will
-             result in calling `std::abort` if this precondition is not met.
-             Assertions can be disabled by defining `NDEBUG` at compile time.
-             See https://en.cppreference.com/w/cpp/error/assert for more
-             information.
-
-    @throw invalid_iterator.201 if iterators @a first and @a last are not
-    compatible (i.e., do not belong to the same JSON value). In this case,
-    the range `[first, last)` is undefined.
-    @throw invalid_iterator.204 if iterators @a first and @a last belong to a
-    primitive type (number, boolean, or string), but @a first does not point
-    to the first element any more. In this case, the range `[first, last)` is
-    undefined. See example code below.
-    @throw invalid_iterator.206 if iterators @a first and @a last belong to a
-    null value. In this case, the range `[first, last)` is undefined.
-
-    @complexity Linear in distance between @a first and @a last.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @liveexample{The example below shows several ways to create JSON values by
-    specifying a subrange with iterators.,basic_json__InputIt_InputIt}
-
-    @since version 1.0.0
-    */
-    template<class InputIT, typename std::enable_if<
-                 std::is_same<InputIT, typename basic_json_t::iterator>::value or
-                 std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>
-    basic_json(InputIT first, InputIT last)
-    {
-        assert(first.m_object != nullptr);
-        assert(last.m_object != nullptr);
-
-        // make sure iterator fits the current value
-        if (JSON_UNLIKELY(first.m_object != last.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(201, "iterators are not compatible"));
-        }
-
-        // copy type from first iterator
-        m_type = first.m_object->m_type;
-
-        // check if iterator range is complete for primitive values
-        switch (m_type)
-        {
-            case value_t::boolean:
-            case value_t::number_float:
-            case value_t::number_integer:
-            case value_t::number_unsigned:
-            case value_t::string:
-            {
-                if (JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin()
-                                  or not last.m_it.primitive_iterator.is_end()))
-                {
-                    JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
-                }
-                break;
-            }
-
-            default:
-                break;
-        }
-
-        switch (m_type)
-        {
-            case value_t::number_integer:
-            {
-                m_value.number_integer = first.m_object->m_value.number_integer;
-                break;
-            }
-
-            case value_t::number_unsigned:
-            {
-                m_value.number_unsigned = first.m_object->m_value.number_unsigned;
-                break;
-            }
-
-            case value_t::number_float:
-            {
-                m_value.number_float = first.m_object->m_value.number_float;
-                break;
-            }
-
-            case value_t::boolean:
-            {
-                m_value.boolean = first.m_object->m_value.boolean;
-                break;
-            }
-
-            case value_t::string:
-            {
-                m_value = *first.m_object->m_value.string;
-                break;
-            }
-
-            case value_t::object:
-            {
-                m_value.object = create<object_t>(first.m_it.object_iterator,
-                                                  last.m_it.object_iterator);
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_value.array = create<array_t>(first.m_it.array_iterator,
-                                                last.m_it.array_iterator);
-                break;
-            }
-
-            default:
-                JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " +
-                                                    std::string(first.m_object->type_name())));
-        }
-
-        assert_invariant();
-    }
-
-
-    ///////////////////////////////////////
-    // other constructors and destructor //
-    ///////////////////////////////////////
-
-    /// @private
-    basic_json(const detail::json_ref<basic_json>& ref)
-        : basic_json(ref.moved_or_copied())
-    {}
-
-    /*!
-    @brief copy constructor
-
-    Creates a copy of a given JSON value.
-
-    @param[in] other  the JSON value to copy
-
-    @post `*this == other`
-
-    @complexity Linear in the size of @a other.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes to any JSON value.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is linear.
-    - As postcondition, it holds: `other == basic_json(other)`.
-
-    @liveexample{The following code shows an example for the copy
-    constructor.,basic_json__basic_json}
-
-    @since version 1.0.0
-    */
-    basic_json(const basic_json& other)
-        : m_type(other.m_type)
-    {
-        // check of passed value is valid
-        other.assert_invariant();
-
-        switch (m_type)
-        {
-            case value_t::object:
-            {
-                m_value = *other.m_value.object;
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_value = *other.m_value.array;
-                break;
-            }
-
-            case value_t::string:
-            {
-                m_value = *other.m_value.string;
-                break;
-            }
-
-            case value_t::boolean:
-            {
-                m_value = other.m_value.boolean;
-                break;
-            }
-
-            case value_t::number_integer:
-            {
-                m_value = other.m_value.number_integer;
-                break;
-            }
-
-            case value_t::number_unsigned:
-            {
-                m_value = other.m_value.number_unsigned;
-                break;
-            }
-
-            case value_t::number_float:
-            {
-                m_value = other.m_value.number_float;
-                break;
-            }
-
-            default:
-                break;
-        }
-
-        assert_invariant();
-    }
-
-    /*!
-    @brief move constructor
-
-    Move constructor. Constructs a JSON value with the contents of the given
-    value @a other using move semantics. It "steals" the resources from @a
-    other and leaves it as JSON null value.
-
-    @param[in,out] other  value to move to this object
-
-    @post `*this` has the same value as @a other before the call.
-    @post @a other is a JSON null value.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this constructor never throws
-    exceptions.
-
-    @requirement This function helps `basic_json` satisfying the
-    [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible)
-    requirements.
-
-    @liveexample{The code below shows the move constructor explicitly called
-    via std::move.,basic_json__moveconstructor}
-
-    @since version 1.0.0
-    */
-    basic_json(basic_json&& other) noexcept
-        : m_type(std::move(other.m_type)),
-          m_value(std::move(other.m_value))
-    {
-        // check that passed value is valid
-        other.assert_invariant();
-
-        // invalidate payload
-        other.m_type = value_t::null;
-        other.m_value = {};
-
-        assert_invariant();
-    }
-
-    /*!
-    @brief copy assignment
-
-    Copy assignment operator. Copies a JSON value via the "copy and swap"
-    strategy: It is expressed in terms of the copy constructor, destructor,
-    and the `swap()` member function.
-
-    @param[in] other  value to copy from
-
-    @complexity Linear.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is linear.
-
-    @liveexample{The code below shows and example for the copy assignment. It
-    creates a copy of value `a` which is then swapped with `b`. Finally\, the
-    copy of `a` (which is the null value after the swap) is
-    destroyed.,basic_json__copyassignment}
-
-    @since version 1.0.0
-    */
-    basic_json& operator=(basic_json other) noexcept (
-        std::is_nothrow_move_constructible<value_t>::value and
-        std::is_nothrow_move_assignable<value_t>::value and
-        std::is_nothrow_move_constructible<json_value>::value and
-        std::is_nothrow_move_assignable<json_value>::value
-    )
-    {
-        // check that passed value is valid
-        other.assert_invariant();
-
-        using std::swap;
-        swap(m_type, other.m_type);
-        swap(m_value, other.m_value);
-
-        assert_invariant();
-        return *this;
-    }
-
-    /*!
-    @brief destructor
-
-    Destroys the JSON value and frees all allocated memory.
-
-    @complexity Linear.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is linear.
-    - All stored elements are destroyed and all memory is freed.
-
-    @since version 1.0.0
-    */
-    ~basic_json() noexcept
-    {
-        assert_invariant();
-        m_value.destroy(m_type);
-    }
-
-    /// @}
-
-  public:
-    ///////////////////////
-    // object inspection //
-    ///////////////////////
-
-    /// @name object inspection
-    /// Functions to inspect the type of a JSON value.
-    /// @{
-
-    /*!
-    @brief serialization
-
-    Serialization function for JSON values. The function tries to mimic
-    Python's `json.dumps()` function, and currently supports its @a indent
-    and @a ensure_ascii parameters.
-
-    @param[in] indent If indent is nonnegative, then array elements and object
-    members will be pretty-printed with that indent level. An indent level of
-    `0` will only insert newlines. `-1` (the default) selects the most compact
-    representation.
-    @param[in] indent_char The character to use for indentation if @a indent is
-    greater than `0`. The default is ` ` (space).
-    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
-    in the output are escaped with `\uXXXX` sequences, and the result consists
-    of ASCII characters only.
-    @param[in] error_handler  how to react on decoding errors; there are three
-    possible values: `strict` (throws and exception in case a decoding error
-    occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),
-    and `ignore` (ignore invalid UTF-8 sequences during serialization).
-
-    @return string containing the serialization of the JSON value
-
-    @throw type_error.316 if a string stored inside the JSON value is not
-                          UTF-8 encoded
-
-    @complexity Linear.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @liveexample{The following example shows the effect of different @a indent\,
-    @a indent_char\, and @a ensure_ascii parameters to the result of the
-    serialization.,dump}
-
-    @see https://docs.python.org/2/library/json.html#json.dump
-
-    @since version 1.0.0; indentation character @a indent_char, option
-           @a ensure_ascii and exceptions added in version 3.0.0; error
-           handlers added in version 3.4.0.
-    */
-    string_t dump(const int indent = -1,
-                  const char indent_char = ' ',
-                  const bool ensure_ascii = false,
-                  const error_handler_t error_handler = error_handler_t::strict) const
-    {
-        string_t result;
-        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);
-
-        if (indent >= 0)
-        {
-            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
-        }
-        else
-        {
-            s.dump(*this, false, ensure_ascii, 0);
-        }
-
-        return result;
-    }
-
-    /*!
-    @brief return the type of the JSON value (explicit)
-
-    Return the type of the JSON value as a value from the @ref value_t
-    enumeration.
-
-    @return the type of the JSON value
-            Value type                | return value
-            ------------------------- | -------------------------
-            null                      | value_t::null
-            boolean                   | value_t::boolean
-            string                    | value_t::string
-            number (integer)          | value_t::number_integer
-            number (unsigned integer) | value_t::number_unsigned
-            number (floating-point)   | value_t::number_float
-            object                    | value_t::object
-            array                     | value_t::array
-            discarded                 | value_t::discarded
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `type()` for all JSON
-    types.,type}
-
-    @sa @ref operator value_t() -- return the type of the JSON value (implicit)
-    @sa @ref type_name() -- return the type as string
-
-    @since version 1.0.0
-    */
-    constexpr value_t type() const noexcept
-    {
-        return m_type;
-    }
-
-    /*!
-    @brief return whether type is primitive
-
-    This function returns true if and only if the JSON type is primitive
-    (string, number, boolean, or null).
-
-    @return `true` if type is primitive (string, number, boolean, or null),
-    `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_primitive()` for all JSON
-    types.,is_primitive}
-
-    @sa @ref is_structured() -- returns whether JSON value is structured
-    @sa @ref is_null() -- returns whether JSON value is `null`
-    @sa @ref is_string() -- returns whether JSON value is a string
-    @sa @ref is_boolean() -- returns whether JSON value is a boolean
-    @sa @ref is_number() -- returns whether JSON value is a number
-
-    @since version 1.0.0
-    */
-    constexpr bool is_primitive() const noexcept
-    {
-        return is_null() or is_string() or is_boolean() or is_number();
-    }
-
-    /*!
-    @brief return whether type is structured
-
-    This function returns true if and only if the JSON type is structured
-    (array or object).
-
-    @return `true` if type is structured (array or object), `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_structured()` for all JSON
-    types.,is_structured}
-
-    @sa @ref is_primitive() -- returns whether value is primitive
-    @sa @ref is_array() -- returns whether value is an array
-    @sa @ref is_object() -- returns whether value is an object
-
-    @since version 1.0.0
-    */
-    constexpr bool is_structured() const noexcept
-    {
-        return is_array() or is_object();
-    }
-
-    /*!
-    @brief return whether value is null
-
-    This function returns true if and only if the JSON value is null.
-
-    @return `true` if type is null, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_null()` for all JSON
-    types.,is_null}
-
-    @since version 1.0.0
-    */
-    constexpr bool is_null() const noexcept
-    {
-        return (m_type == value_t::null);
-    }
-
-    /*!
-    @brief return whether value is a boolean
-
-    This function returns true if and only if the JSON value is a boolean.
-
-    @return `true` if type is boolean, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_boolean()` for all JSON
-    types.,is_boolean}
-
-    @since version 1.0.0
-    */
-    constexpr bool is_boolean() const noexcept
-    {
-        return (m_type == value_t::boolean);
-    }
-
-    /*!
-    @brief return whether value is a number
-
-    This function returns true if and only if the JSON value is a number. This
-    includes both integer (signed and unsigned) and floating-point values.
-
-    @return `true` if type is number (regardless whether integer, unsigned
-    integer or floating-type), `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_number()` for all JSON
-    types.,is_number}
-
-    @sa @ref is_number_integer() -- check if value is an integer or unsigned
-    integer number
-    @sa @ref is_number_unsigned() -- check if value is an unsigned integer
-    number
-    @sa @ref is_number_float() -- check if value is a floating-point number
-
-    @since version 1.0.0
-    */
-    constexpr bool is_number() const noexcept
-    {
-        return is_number_integer() or is_number_float();
-    }
-
-    /*!
-    @brief return whether value is an integer number
-
-    This function returns true if and only if the JSON value is a signed or
-    unsigned integer number. This excludes floating-point values.
-
-    @return `true` if type is an integer or unsigned integer number, `false`
-    otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_number_integer()` for all
-    JSON types.,is_number_integer}
-
-    @sa @ref is_number() -- check if value is a number
-    @sa @ref is_number_unsigned() -- check if value is an unsigned integer
-    number
-    @sa @ref is_number_float() -- check if value is a floating-point number
-
-    @since version 1.0.0
-    */
-    constexpr bool is_number_integer() const noexcept
-    {
-        return (m_type == value_t::number_integer or m_type == value_t::number_unsigned);
-    }
-
-    /*!
-    @brief return whether value is an unsigned integer number
-
-    This function returns true if and only if the JSON value is an unsigned
-    integer number. This excludes floating-point and signed integer values.
-
-    @return `true` if type is an unsigned integer number, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_number_unsigned()` for all
-    JSON types.,is_number_unsigned}
-
-    @sa @ref is_number() -- check if value is a number
-    @sa @ref is_number_integer() -- check if value is an integer or unsigned
-    integer number
-    @sa @ref is_number_float() -- check if value is a floating-point number
-
-    @since version 2.0.0
-    */
-    constexpr bool is_number_unsigned() const noexcept
-    {
-        return (m_type == value_t::number_unsigned);
-    }
-
-    /*!
-    @brief return whether value is a floating-point number
-
-    This function returns true if and only if the JSON value is a
-    floating-point number. This excludes signed and unsigned integer values.
-
-    @return `true` if type is a floating-point number, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_number_float()` for all
-    JSON types.,is_number_float}
-
-    @sa @ref is_number() -- check if value is number
-    @sa @ref is_number_integer() -- check if value is an integer number
-    @sa @ref is_number_unsigned() -- check if value is an unsigned integer
-    number
-
-    @since version 1.0.0
-    */
-    constexpr bool is_number_float() const noexcept
-    {
-        return (m_type == value_t::number_float);
-    }
-
-    /*!
-    @brief return whether value is an object
-
-    This function returns true if and only if the JSON value is an object.
-
-    @return `true` if type is object, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_object()` for all JSON
-    types.,is_object}
-
-    @since version 1.0.0
-    */
-    constexpr bool is_object() const noexcept
-    {
-        return (m_type == value_t::object);
-    }
-
-    /*!
-    @brief return whether value is an array
-
-    This function returns true if and only if the JSON value is an array.
-
-    @return `true` if type is array, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_array()` for all JSON
-    types.,is_array}
-
-    @since version 1.0.0
-    */
-    constexpr bool is_array() const noexcept
-    {
-        return (m_type == value_t::array);
-    }
-
-    /*!
-    @brief return whether value is a string
-
-    This function returns true if and only if the JSON value is a string.
-
-    @return `true` if type is string, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_string()` for all JSON
-    types.,is_string}
-
-    @since version 1.0.0
-    */
-    constexpr bool is_string() const noexcept
-    {
-        return (m_type == value_t::string);
-    }
-
-    /*!
-    @brief return whether value is discarded
-
-    This function returns true if and only if the JSON value was discarded
-    during parsing with a callback function (see @ref parser_callback_t).
-
-    @note This function will always be `false` for JSON values after parsing.
-    That is, discarded values can only occur during parsing, but will be
-    removed when inside a structured value or replaced by null in other cases.
-
-    @return `true` if type is discarded, `false` otherwise.
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies `is_discarded()` for all JSON
-    types.,is_discarded}
-
-    @since version 1.0.0
-    */
-    constexpr bool is_discarded() const noexcept
-    {
-        return (m_type == value_t::discarded);
-    }
-
-    /*!
-    @brief return the type of the JSON value (implicit)
-
-    Implicitly return the type of the JSON value as a value from the @ref
-    value_t enumeration.
-
-    @return the type of the JSON value
-
-    @complexity Constant.
-
-    @exceptionsafety No-throw guarantee: this member function never throws
-    exceptions.
-
-    @liveexample{The following code exemplifies the @ref value_t operator for
-    all JSON types.,operator__value_t}
-
-    @sa @ref type() -- return the type of the JSON value (explicit)
-    @sa @ref type_name() -- return the type as string
-
-    @since version 1.0.0
-    */
-    constexpr operator value_t() const noexcept
-    {
-        return m_type;
-    }
-
-    /// @}
-
-  private:
-    //////////////////
-    // value access //
-    //////////////////
-
-    /// get a boolean (explicit)
-    boolean_t get_impl(boolean_t* /*unused*/) const
-    {
-        if (JSON_LIKELY(is_boolean()))
-        {
-            return m_value.boolean;
-        }
-
-        JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name())));
-    }
-
-    /// get a pointer to the value (object)
-    object_t* get_impl_ptr(object_t* /*unused*/) noexcept
-    {
-        return is_object() ? m_value.object : nullptr;
-    }
-
-    /// get a pointer to the value (object)
-    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
-    {
-        return is_object() ? m_value.object : nullptr;
-    }
-
-    /// get a pointer to the value (array)
-    array_t* get_impl_ptr(array_t* /*unused*/) noexcept
-    {
-        return is_array() ? m_value.array : nullptr;
-    }
-
-    /// get a pointer to the value (array)
-    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
-    {
-        return is_array() ? m_value.array : nullptr;
-    }
-
-    /// get a pointer to the value (string)
-    string_t* get_impl_ptr(string_t* /*unused*/) noexcept
-    {
-        return is_string() ? m_value.string : nullptr;
-    }
-
-    /// get a pointer to the value (string)
-    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
-    {
-        return is_string() ? m_value.string : nullptr;
-    }
-
-    /// get a pointer to the value (boolean)
-    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
-    {
-        return is_boolean() ? &m_value.boolean : nullptr;
-    }
-
-    /// get a pointer to the value (boolean)
-    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
-    {
-        return is_boolean() ? &m_value.boolean : nullptr;
-    }
-
-    /// get a pointer to the value (integer number)
-    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
-    {
-        return is_number_integer() ? &m_value.number_integer : nullptr;
-    }
-
-    /// get a pointer to the value (integer number)
-    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
-    {
-        return is_number_integer() ? &m_value.number_integer : nullptr;
-    }
-
-    /// get a pointer to the value (unsigned number)
-    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
-    {
-        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
-    }
-
-    /// get a pointer to the value (unsigned number)
-    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
-    {
-        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
-    }
-
-    /// get a pointer to the value (floating-point number)
-    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
-    {
-        return is_number_float() ? &m_value.number_float : nullptr;
-    }
-
-    /// get a pointer to the value (floating-point number)
-    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
-    {
-        return is_number_float() ? &m_value.number_float : nullptr;
-    }
-
-    /*!
-    @brief helper function to implement get_ref()
-
-    This function helps to implement get_ref() without code duplication for
-    const and non-const overloads
-
-    @tparam ThisType will be deduced as `basic_json` or `const basic_json`
-
-    @throw type_error.303 if ReferenceType does not match underlying value
-    type of the current JSON
-    */
-    template<typename ReferenceType, typename ThisType>
-    static ReferenceType get_ref_impl(ThisType& obj)
-    {
-        // delegate the call to get_ptr<>()
-        auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
-
-        if (JSON_LIKELY(ptr != nullptr))
-        {
-            return *ptr;
-        }
-
-        JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name())));
-    }
-
-  public:
-    /// @name value access
-    /// Direct access to the stored value of a JSON value.
-    /// @{
-
-    /*!
-    @brief get special-case overload
-
-    This overloads avoids a lot of template boilerplate, it can be seen as the
-    identity method
-
-    @tparam BasicJsonType == @ref basic_json
-
-    @return a copy of *this
-
-    @complexity Constant.
-
-    @since version 2.1.0
-    */
-    template<typename BasicJsonType, detail::enable_if_t<
-                 std::is_same<typename std::remove_const<BasicJsonType>::type, basic_json_t>::value,
-                 int> = 0>
-    basic_json get() const
-    {
-        return *this;
-    }
-
-    /*!
-    @brief get special-case overload
-
-    This overloads converts the current @ref basic_json in a different
-    @ref basic_json type
-
-    @tparam BasicJsonType == @ref basic_json
-
-    @return a copy of *this, converted into @tparam BasicJsonType
-
-    @complexity Depending on the implementation of the called `from_json()`
-                method.
-
-    @since version 3.2.0
-    */
-    template<typename BasicJsonType, detail::enable_if_t<
-                 not std::is_same<BasicJsonType, basic_json>::value and
-                 detail::is_basic_json<BasicJsonType>::value, int> = 0>
-    BasicJsonType get() const
-    {
-        return *this;
-    }
-
-    /*!
-    @brief get a value (explicit)
-
-    Explicit type conversion between the JSON value and a compatible value
-    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
-    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
-    The value is converted by calling the @ref json_serializer<ValueType>
-    `from_json()` method.
-
-    The function is equivalent to executing
-    @code {.cpp}
-    ValueType ret;
-    JSONSerializer<ValueType>::from_json(*this, ret);
-    return ret;
-    @endcode
-
-    This overloads is chosen if:
-    - @a ValueType is not @ref basic_json,
-    - @ref json_serializer<ValueType> has a `from_json()` method of the form
-      `void from_json(const basic_json&, ValueType&)`, and
-    - @ref json_serializer<ValueType> does not have a `from_json()` method of
-      the form `ValueType from_json(const basic_json&)`
-
-    @tparam ValueTypeCV the provided value type
-    @tparam ValueType the returned value type
-
-    @return copy of the JSON value, converted to @a ValueType
-
-    @throw what @ref json_serializer<ValueType> `from_json()` method throws
-
-    @liveexample{The example below shows several conversions from JSON values
-    to other types. There a few things to note: (1) Floating-point numbers can
-    be converted to integers\, (2) A JSON array can be converted to a standard
-    `std::vector<short>`\, (3) A JSON object can be converted to C++
-    associative containers such as `std::unordered_map<std::string\,
-    json>`.,get__ValueType_const}
-
-    @since version 2.1.0
-    */
-    template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
-             detail::enable_if_t <
-                 not detail::is_basic_json<ValueType>::value and
-                 detail::has_from_json<basic_json_t, ValueType>::value and
-                 not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
-                 int> = 0>
-    ValueType get() const noexcept(noexcept(
-                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
-    {
-        // we cannot static_assert on ValueTypeCV being non-const, because
-        // there is support for get<const basic_json_t>(), which is why we
-        // still need the uncvref
-        static_assert(not std::is_reference<ValueTypeCV>::value,
-                      "get() cannot be used with reference types, you might want to use get_ref()");
-        static_assert(std::is_default_constructible<ValueType>::value,
-                      "types must be DefaultConstructible when used with get()");
-
-        ValueType ret;
-        JSONSerializer<ValueType>::from_json(*this, ret);
-        return ret;
-    }
-
-    /*!
-    @brief get a value (explicit); special case
-
-    Explicit type conversion between the JSON value and a compatible value
-    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
-    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
-    The value is converted by calling the @ref json_serializer<ValueType>
-    `from_json()` method.
-
-    The function is equivalent to executing
-    @code {.cpp}
-    return JSONSerializer<ValueTypeCV>::from_json(*this);
-    @endcode
-
-    This overloads is chosen if:
-    - @a ValueType is not @ref basic_json and
-    - @ref json_serializer<ValueType> has a `from_json()` method of the form
-      `ValueType from_json(const basic_json&)`
-
-    @note If @ref json_serializer<ValueType> has both overloads of
-    `from_json()`, this one is chosen.
-
-    @tparam ValueTypeCV the provided value type
-    @tparam ValueType the returned value type
-
-    @return copy of the JSON value, converted to @a ValueType
-
-    @throw what @ref json_serializer<ValueType> `from_json()` method throws
-
-    @since version 2.1.0
-    */
-    template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
-             detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and
-                                 detail::has_non_default_from_json<basic_json_t, ValueType>::value,
-                                 int> = 0>
-    ValueType get() const noexcept(noexcept(
-                                       JSONSerializer<ValueTypeCV>::from_json(std::declval<const basic_json_t&>())))
-    {
-        static_assert(not std::is_reference<ValueTypeCV>::value,
-                      "get() cannot be used with reference types, you might want to use get_ref()");
-        return JSONSerializer<ValueTypeCV>::from_json(*this);
-    }
-
-    /*!
-    @brief get a value (explicit)
-
-    Explicit type conversion between the JSON value and a compatible value.
-    The value is filled into the input parameter by calling the @ref json_serializer<ValueType>
-    `from_json()` method.
-
-    The function is equivalent to executing
-    @code {.cpp}
-    ValueType v;
-    JSONSerializer<ValueType>::from_json(*this, v);
-    @endcode
-
-    This overloads is chosen if:
-    - @a ValueType is not @ref basic_json,
-    - @ref json_serializer<ValueType> has a `from_json()` method of the form
-      `void from_json(const basic_json&, ValueType&)`, and
-
-    @tparam ValueType the input parameter type.
-
-    @return the input parameter, allowing chaining calls.
-
-    @throw what @ref json_serializer<ValueType> `from_json()` method throws
-
-    @liveexample{The example below shows several conversions from JSON values
-    to other types. There a few things to note: (1) Floating-point numbers can
-    be converted to integers\, (2) A JSON array can be converted to a standard
-    `std::vector<short>`\, (3) A JSON object can be converted to C++
-    associative containers such as `std::unordered_map<std::string\,
-    json>`.,get_to}
-
-    @since version 3.3.0
-    */
-    template<typename ValueType,
-             detail::enable_if_t <
-                 not detail::is_basic_json<ValueType>::value and
-                 detail::has_from_json<basic_json_t, ValueType>::value,
-                 int> = 0>
-    ValueType & get_to(ValueType& v) const noexcept(noexcept(
-                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
-    {
-        JSONSerializer<ValueType>::from_json(*this, v);
-        return v;
-    }
-
-
-    /*!
-    @brief get a pointer value (implicit)
-
-    Implicit pointer access to the internally stored JSON value. No copies are
-    made.
-
-    @warning Writing data to the pointee of the result yields an undefined
-    state.
-
-    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
-    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
-    @ref number_unsigned_t, or @ref number_float_t. Enforced by a static
-    assertion.
-
-    @return pointer to the internally stored JSON value if the requested
-    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how pointers to internal values of a
-    JSON value can be requested. Note that no type conversions are made and a
-    `nullptr` is returned if the value and the requested pointer type does not
-    match.,get_ptr}
-
-    @since version 1.0.0
-    */
-    template<typename PointerType, typename std::enable_if<
-                 std::is_pointer<PointerType>::value, int>::type = 0>
-    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
-    {
-        // delegate the call to get_impl_ptr<>()
-        return get_impl_ptr(static_cast<PointerType>(nullptr));
-    }
-
-    /*!
-    @brief get a pointer value (implicit)
-    @copydoc get_ptr()
-    */
-    template<typename PointerType, typename std::enable_if<
-                 std::is_pointer<PointerType>::value and
-                 std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
-    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
-    {
-        // delegate the call to get_impl_ptr<>() const
-        return get_impl_ptr(static_cast<PointerType>(nullptr));
-    }
-
-    /*!
-    @brief get a pointer value (explicit)
-
-    Explicit pointer access to the internally stored JSON value. No copies are
-    made.
-
-    @warning The pointer becomes invalid if the underlying JSON object
-    changes.
-
-    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
-    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
-    @ref number_unsigned_t, or @ref number_float_t.
-
-    @return pointer to the internally stored JSON value if the requested
-    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how pointers to internal values of a
-    JSON value can be requested. Note that no type conversions are made and a
-    `nullptr` is returned if the value and the requested pointer type does not
-    match.,get__PointerType}
-
-    @sa @ref get_ptr() for explicit pointer-member access
-
-    @since version 1.0.0
-    */
-    template<typename PointerType, typename std::enable_if<
-                 std::is_pointer<PointerType>::value, int>::type = 0>
-    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
-    {
-        // delegate the call to get_ptr
-        return get_ptr<PointerType>();
-    }
-
-    /*!
-    @brief get a pointer value (explicit)
-    @copydoc get()
-    */
-    template<typename PointerType, typename std::enable_if<
-                 std::is_pointer<PointerType>::value, int>::type = 0>
-    constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
-    {
-        // delegate the call to get_ptr
-        return get_ptr<PointerType>();
-    }
-
-    /*!
-    @brief get a reference value (implicit)
-
-    Implicit reference access to the internally stored JSON value. No copies
-    are made.
-
-    @warning Writing data to the referee of the result yields an undefined
-    state.
-
-    @tparam ReferenceType reference type; must be a reference to @ref array_t,
-    @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or
-    @ref number_float_t. Enforced by static assertion.
-
-    @return reference to the internally stored JSON value if the requested
-    reference type @a ReferenceType fits to the JSON value; throws
-    type_error.303 otherwise
-
-    @throw type_error.303 in case passed type @a ReferenceType is incompatible
-    with the stored JSON value; see example below
-
-    @complexity Constant.
-
-    @liveexample{The example shows several calls to `get_ref()`.,get_ref}
-
-    @since version 1.1.0
-    */
-    template<typename ReferenceType, typename std::enable_if<
-                 std::is_reference<ReferenceType>::value, int>::type = 0>
-    ReferenceType get_ref()
-    {
-        // delegate call to get_ref_impl
-        return get_ref_impl<ReferenceType>(*this);
-    }
-
-    /*!
-    @brief get a reference value (implicit)
-    @copydoc get_ref()
-    */
-    template<typename ReferenceType, typename std::enable_if<
-                 std::is_reference<ReferenceType>::value and
-                 std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>
-    ReferenceType get_ref() const
-    {
-        // delegate call to get_ref_impl
-        return get_ref_impl<ReferenceType>(*this);
-    }
-
-    /*!
-    @brief get a value (implicit)
-
-    Implicit type conversion between the JSON value and a compatible value.
-    The call is realized by calling @ref get() const.
-
-    @tparam ValueType non-pointer type compatible to the JSON value, for
-    instance `int` for JSON integer numbers, `bool` for JSON booleans, or
-    `std::vector` types for JSON arrays. The character type of @ref string_t
-    as well as an initializer list of this type is excluded to avoid
-    ambiguities as these types implicitly convert to `std::string`.
-
-    @return copy of the JSON value, converted to type @a ValueType
-
-    @throw type_error.302 in case passed type @a ValueType is incompatible
-    to the JSON value type (e.g., the JSON value is of type boolean, but a
-    string is requested); see example below
-
-    @complexity Linear in the size of the JSON value.
-
-    @liveexample{The example below shows several conversions from JSON values
-    to other types. There a few things to note: (1) Floating-point numbers can
-    be converted to integers\, (2) A JSON array can be converted to a standard
-    `std::vector<short>`\, (3) A JSON object can be converted to C++
-    associative containers such as `std::unordered_map<std::string\,
-    json>`.,operator__ValueType}
-
-    @since version 1.0.0
-    */
-    template < typename ValueType, typename std::enable_if <
-                   not std::is_pointer<ValueType>::value and
-                   not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
-                   not std::is_same<ValueType, typename string_t::value_type>::value and
-                   not detail::is_basic_json<ValueType>::value
-
-#ifndef _MSC_VER  // fix for issue #167 operator<< ambiguity under VS2015
-                   and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
-#if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914
-                   and not std::is_same<ValueType, typename std::string_view>::value
-#endif
-#endif
-                   and detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
-                   , int >::type = 0 >
-    operator ValueType() const
-    {
-        // delegate the call to get<>() const
-        return get<ValueType>();
-    }
-
-    /// @}
-
-
-    ////////////////////
-    // element access //
-    ////////////////////
-
-    /// @name element access
-    /// Access to the JSON value.
-    /// @{
-
-    /*!
-    @brief access specified array element with bounds checking
-
-    Returns a reference to the element at specified location @a idx, with
-    bounds checking.
-
-    @param[in] idx  index of the element to access
-
-    @return reference to the element at index @a idx
-
-    @throw type_error.304 if the JSON value is not an array; in this case,
-    calling `at` with an index makes no sense. See example below.
-    @throw out_of_range.401 if the index @a idx is out of range of the array;
-    that is, `idx >= size()`. See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 1.0.0
-
-    @liveexample{The example below shows how array elements can be read and
-    written using `at()`. It also demonstrates the different exceptions that
-    can be thrown.,at__size_type}
-    */
-    reference at(size_type idx)
-    {
-        // at only works for arrays
-        if (JSON_LIKELY(is_array()))
-        {
-            JSON_TRY
-            {
-                return m_value.array->at(idx);
-            }
-            JSON_CATCH (std::out_of_range&)
-            {
-                // create better exception explanation
-                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
-            }
-        }
-        else
-        {
-            JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
-        }
-    }
-
-    /*!
-    @brief access specified array element with bounds checking
-
-    Returns a const reference to the element at specified location @a idx,
-    with bounds checking.
-
-    @param[in] idx  index of the element to access
-
-    @return const reference to the element at index @a idx
-
-    @throw type_error.304 if the JSON value is not an array; in this case,
-    calling `at` with an index makes no sense. See example below.
-    @throw out_of_range.401 if the index @a idx is out of range of the array;
-    that is, `idx >= size()`. See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 1.0.0
-
-    @liveexample{The example below shows how array elements can be read using
-    `at()`. It also demonstrates the different exceptions that can be thrown.,
-    at__size_type_const}
-    */
-    const_reference at(size_type idx) const
-    {
-        // at only works for arrays
-        if (JSON_LIKELY(is_array()))
-        {
-            JSON_TRY
-            {
-                return m_value.array->at(idx);
-            }
-            JSON_CATCH (std::out_of_range&)
-            {
-                // create better exception explanation
-                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
-            }
-        }
-        else
-        {
-            JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
-        }
-    }
-
-    /*!
-    @brief access specified object element with bounds checking
-
-    Returns a reference to the element at with specified key @a key, with
-    bounds checking.
-
-    @param[in] key  key of the element to access
-
-    @return reference to the element at key @a key
-
-    @throw type_error.304 if the JSON value is not an object; in this case,
-    calling `at` with a key makes no sense. See example below.
-    @throw out_of_range.403 if the key @a key is is not stored in the object;
-    that is, `find(key) == end()`. See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Logarithmic in the size of the container.
-
-    @sa @ref operator[](const typename object_t::key_type&) for unchecked
-    access by reference
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.0.0
-
-    @liveexample{The example below shows how object elements can be read and
-    written using `at()`. It also demonstrates the different exceptions that
-    can be thrown.,at__object_t_key_type}
-    */
-    reference at(const typename object_t::key_type& key)
-    {
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            JSON_TRY
-            {
-                return m_value.object->at(key);
-            }
-            JSON_CATCH (std::out_of_range&)
-            {
-                // create better exception explanation
-                JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
-            }
-        }
-        else
-        {
-            JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
-        }
-    }
-
-    /*!
-    @brief access specified object element with bounds checking
-
-    Returns a const reference to the element at with specified key @a key,
-    with bounds checking.
-
-    @param[in] key  key of the element to access
-
-    @return const reference to the element at key @a key
-
-    @throw type_error.304 if the JSON value is not an object; in this case,
-    calling `at` with a key makes no sense. See example below.
-    @throw out_of_range.403 if the key @a key is is not stored in the object;
-    that is, `find(key) == end()`. See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Logarithmic in the size of the container.
-
-    @sa @ref operator[](const typename object_t::key_type&) for unchecked
-    access by reference
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.0.0
-
-    @liveexample{The example below shows how object elements can be read using
-    `at()`. It also demonstrates the different exceptions that can be thrown.,
-    at__object_t_key_type_const}
-    */
-    const_reference at(const typename object_t::key_type& key) const
-    {
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            JSON_TRY
-            {
-                return m_value.object->at(key);
-            }
-            JSON_CATCH (std::out_of_range&)
-            {
-                // create better exception explanation
-                JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
-            }
-        }
-        else
-        {
-            JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
-        }
-    }
-
-    /*!
-    @brief access specified array element
-
-    Returns a reference to the element at specified location @a idx.
-
-    @note If @a idx is beyond the range of the array (i.e., `idx >= size()`),
-    then the array is silently filled up with `null` values to make `idx` a
-    valid reference to the last stored element.
-
-    @param[in] idx  index of the element to access
-
-    @return reference to the element at index @a idx
-
-    @throw type_error.305 if the JSON value is not an array or null; in that
-    cases, using the [] operator with an index makes no sense.
-
-    @complexity Constant if @a idx is in the range of the array. Otherwise
-    linear in `idx - size()`.
-
-    @liveexample{The example below shows how array elements can be read and
-    written using `[]` operator. Note the addition of `null`
-    values.,operatorarray__size_type}
-
-    @since version 1.0.0
-    */
-    reference operator[](size_type idx)
-    {
-        // implicitly convert null value to an empty array
-        if (is_null())
-        {
-            m_type = value_t::array;
-            m_value.array = create<array_t>();
-            assert_invariant();
-        }
-
-        // operator[] only works for arrays
-        if (JSON_LIKELY(is_array()))
-        {
-            // fill up array with null values if given idx is outside range
-            if (idx >= m_value.array->size())
-            {
-                m_value.array->insert(m_value.array->end(),
-                                      idx - m_value.array->size() + 1,
-                                      basic_json());
-            }
-
-            return m_value.array->operator[](idx);
-        }
-
-        JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief access specified array element
-
-    Returns a const reference to the element at specified location @a idx.
-
-    @param[in] idx  index of the element to access
-
-    @return const reference to the element at index @a idx
-
-    @throw type_error.305 if the JSON value is not an array; in that case,
-    using the [] operator with an index makes no sense.
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how array elements can be read using
-    the `[]` operator.,operatorarray__size_type_const}
-
-    @since version 1.0.0
-    */
-    const_reference operator[](size_type idx) const
-    {
-        // const operator[] only works for arrays
-        if (JSON_LIKELY(is_array()))
-        {
-            return m_value.array->operator[](idx);
-        }
-
-        JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief access specified object element
-
-    Returns a reference to the element at with specified key @a key.
-
-    @note If @a key is not found in the object, then it is silently added to
-    the object and filled with a `null` value to make `key` a valid reference.
-    In case the value was `null` before, it is converted to an object.
-
-    @param[in] key  key of the element to access
-
-    @return reference to the element at key @a key
-
-    @throw type_error.305 if the JSON value is not an object or null; in that
-    cases, using the [] operator with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be read and
-    written using the `[]` operator.,operatorarray__key_type}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.0.0
-    */
-    reference operator[](const typename object_t::key_type& key)
-    {
-        // implicitly convert null value to an empty object
-        if (is_null())
-        {
-            m_type = value_t::object;
-            m_value.object = create<object_t>();
-            assert_invariant();
-        }
-
-        // operator[] only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            return m_value.object->operator[](key);
-        }
-
-        JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief read-only access specified object element
-
-    Returns a const reference to the element at with specified key @a key. No
-    bounds checking is performed.
-
-    @warning If the element with key @a key does not exist, the behavior is
-    undefined.
-
-    @param[in] key  key of the element to access
-
-    @return const reference to the element at key @a key
-
-    @pre The element with key @a key must exist. **This precondition is
-         enforced with an assertion.**
-
-    @throw type_error.305 if the JSON value is not an object; in that case,
-    using the [] operator with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be read using
-    the `[]` operator.,operatorarray__key_type_const}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.0.0
-    */
-    const_reference operator[](const typename object_t::key_type& key) const
-    {
-        // const operator[] only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            assert(m_value.object->find(key) != m_value.object->end());
-            return m_value.object->find(key)->second;
-        }
-
-        JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief access specified object element
-
-    Returns a reference to the element at with specified key @a key.
-
-    @note If @a key is not found in the object, then it is silently added to
-    the object and filled with a `null` value to make `key` a valid reference.
-    In case the value was `null` before, it is converted to an object.
-
-    @param[in] key  key of the element to access
-
-    @return reference to the element at key @a key
-
-    @throw type_error.305 if the JSON value is not an object or null; in that
-    cases, using the [] operator with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be read and
-    written using the `[]` operator.,operatorarray__key_type}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.1.0
-    */
-    template<typename T>
-    reference operator[](T* key)
-    {
-        // implicitly convert null to object
-        if (is_null())
-        {
-            m_type = value_t::object;
-            m_value = value_t::object;
-            assert_invariant();
-        }
-
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            return m_value.object->operator[](key);
-        }
-
-        JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief read-only access specified object element
-
-    Returns a const reference to the element at with specified key @a key. No
-    bounds checking is performed.
-
-    @warning If the element with key @a key does not exist, the behavior is
-    undefined.
-
-    @param[in] key  key of the element to access
-
-    @return const reference to the element at key @a key
-
-    @pre The element with key @a key must exist. **This precondition is
-         enforced with an assertion.**
-
-    @throw type_error.305 if the JSON value is not an object; in that case,
-    using the [] operator with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be read using
-    the `[]` operator.,operatorarray__key_type_const}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref value() for access by value with a default value
-
-    @since version 1.1.0
-    */
-    template<typename T>
-    const_reference operator[](T* key) const
-    {
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            assert(m_value.object->find(key) != m_value.object->end());
-            return m_value.object->find(key)->second;
-        }
-
-        JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief access specified object element with default value
-
-    Returns either a copy of an object's element at the specified key @a key
-    or a given default value if no element with key @a key exists.
-
-    The function is basically equivalent to executing
-    @code {.cpp}
-    try {
-        return at(key);
-    } catch(out_of_range) {
-        return default_value;
-    }
-    @endcode
-
-    @note Unlike @ref at(const typename object_t::key_type&), this function
-    does not throw if the given key @a key was not found.
-
-    @note Unlike @ref operator[](const typename object_t::key_type& key), this
-    function does not implicitly add an element to the position defined by @a
-    key. This function is furthermore also applicable to const objects.
-
-    @param[in] key  key of the element to access
-    @param[in] default_value  the value to return if @a key is not found
-
-    @tparam ValueType type compatible to JSON values, for instance `int` for
-    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for
-    JSON arrays. Note the type of the expected value at @a key and the default
-    value @a default_value must be compatible.
-
-    @return copy of the element at key @a key or @a default_value if @a key
-    is not found
-
-    @throw type_error.306 if the JSON value is not an object; in that case,
-    using `value()` with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be queried
-    with a default value.,basic_json__value}
-
-    @sa @ref at(const typename object_t::key_type&) for access by reference
-    with range checking
-    @sa @ref operator[](const typename object_t::key_type&) for unchecked
-    access by reference
-
-    @since version 1.0.0
-    */
-    template<class ValueType, typename std::enable_if<
-                 std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
-    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
-    {
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            // if key is found, return value and given default value otherwise
-            const auto it = find(key);
-            if (it != end())
-            {
-                return *it;
-            }
-
-            return default_value;
-        }
-
-        JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief overload for a default value of type const char*
-    @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const
-    */
-    string_t value(const typename object_t::key_type& key, const char* default_value) const
-    {
-        return value(key, string_t(default_value));
-    }
-
-    /*!
-    @brief access specified object element via JSON Pointer with default value
-
-    Returns either a copy of an object's element at the specified key @a key
-    or a given default value if no element with key @a key exists.
-
-    The function is basically equivalent to executing
-    @code {.cpp}
-    try {
-        return at(ptr);
-    } catch(out_of_range) {
-        return default_value;
-    }
-    @endcode
-
-    @note Unlike @ref at(const json_pointer&), this function does not throw
-    if the given key @a key was not found.
-
-    @param[in] ptr  a JSON pointer to the element to access
-    @param[in] default_value  the value to return if @a ptr found no value
-
-    @tparam ValueType type compatible to JSON values, for instance `int` for
-    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for
-    JSON arrays. Note the type of the expected value at @a key and the default
-    value @a default_value must be compatible.
-
-    @return copy of the element at key @a key or @a default_value if @a key
-    is not found
-
-    @throw type_error.306 if the JSON value is not an object; in that case,
-    using `value()` with a key makes no sense.
-
-    @complexity Logarithmic in the size of the container.
-
-    @liveexample{The example below shows how object elements can be queried
-    with a default value.,basic_json__value_ptr}
-
-    @sa @ref operator[](const json_pointer&) for unchecked access by reference
-
-    @since version 2.0.2
-    */
-    template<class ValueType, typename std::enable_if<
-                 std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
-    ValueType value(const json_pointer& ptr, const ValueType& default_value) const
-    {
-        // at only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            // if pointer resolves a value, return it or use default value
-            JSON_TRY
-            {
-                return ptr.get_checked(this);
-            }
-            JSON_INTERNAL_CATCH (out_of_range&)
-            {
-                return default_value;
-            }
-        }
-
-        JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief overload for a default value of type const char*
-    @copydoc basic_json::value(const json_pointer&, ValueType) const
-    */
-    string_t value(const json_pointer& ptr, const char* default_value) const
-    {
-        return value(ptr, string_t(default_value));
-    }
-
-    /*!
-    @brief access the first element
-
-    Returns a reference to the first element in the container. For a JSON
-    container `c`, the expression `c.front()` is equivalent to `*c.begin()`.
-
-    @return In case of a structured type (array or object), a reference to the
-    first element is returned. In case of number, string, or boolean values, a
-    reference to the value is returned.
-
-    @complexity Constant.
-
-    @pre The JSON value must not be `null` (would throw `std::out_of_range`)
-    or an empty array or object (undefined behavior, **guarded by
-    assertions**).
-    @post The JSON value remains unchanged.
-
-    @throw invalid_iterator.214 when called on `null` value
-
-    @liveexample{The following code shows an example for `front()`.,front}
-
-    @sa @ref back() -- access the last element
-
-    @since version 1.0.0
-    */
-    reference front()
-    {
-        return *begin();
-    }
-
-    /*!
-    @copydoc basic_json::front()
-    */
-    const_reference front() const
-    {
-        return *cbegin();
-    }
-
-    /*!
-    @brief access the last element
-
-    Returns a reference to the last element in the container. For a JSON
-    container `c`, the expression `c.back()` is equivalent to
-    @code {.cpp}
-    auto tmp = c.end();
-    --tmp;
-    return *tmp;
-    @endcode
-
-    @return In case of a structured type (array or object), a reference to the
-    last element is returned. In case of number, string, or boolean values, a
-    reference to the value is returned.
-
-    @complexity Constant.
-
-    @pre The JSON value must not be `null` (would throw `std::out_of_range`)
-    or an empty array or object (undefined behavior, **guarded by
-    assertions**).
-    @post The JSON value remains unchanged.
-
-    @throw invalid_iterator.214 when called on a `null` value. See example
-    below.
-
-    @liveexample{The following code shows an example for `back()`.,back}
-
-    @sa @ref front() -- access the first element
-
-    @since version 1.0.0
-    */
-    reference back()
-    {
-        auto tmp = end();
-        --tmp;
-        return *tmp;
-    }
-
-    /*!
-    @copydoc basic_json::back()
-    */
-    const_reference back() const
-    {
-        auto tmp = cend();
-        --tmp;
-        return *tmp;
-    }
-
-    /*!
-    @brief remove element given an iterator
-
-    Removes the element specified by iterator @a pos. The iterator @a pos must
-    be valid and dereferenceable. Thus the `end()` iterator (which is valid,
-    but is not dereferenceable) cannot be used as a value for @a pos.
-
-    If called on a primitive type other than `null`, the resulting JSON value
-    will be `null`.
-
-    @param[in] pos iterator to the element to remove
-    @return Iterator following the last removed element. If the iterator @a
-    pos refers to the last element, the `end()` iterator is returned.
-
-    @tparam IteratorType an @ref iterator or @ref const_iterator
-
-    @post Invalidates iterators and references at or after the point of the
-    erase, including the `end()` iterator.
-
-    @throw type_error.307 if called on a `null` value; example: `"cannot use
-    erase() with null"`
-    @throw invalid_iterator.202 if called on an iterator which does not belong
-    to the current JSON value; example: `"iterator does not fit current
-    value"`
-    @throw invalid_iterator.205 if called on a primitive type with invalid
-    iterator (i.e., any iterator which is not `begin()`); example: `"iterator
-    out of range"`
-
-    @complexity The complexity depends on the type:
-    - objects: amortized constant
-    - arrays: linear in distance between @a pos and the end of the container
-    - strings: linear in the length of the string
-    - other types: constant
-
-    @liveexample{The example shows the result of `erase()` for different JSON
-    types.,erase__IteratorType}
-
-    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
-    the given range
-    @sa @ref erase(const typename object_t::key_type&) -- removes the element
-    from an object at the given key
-    @sa @ref erase(const size_type) -- removes the element from an array at
-    the given index
-
-    @since version 1.0.0
-    */
-    template<class IteratorType, typename std::enable_if<
-                 std::is_same<IteratorType, typename basic_json_t::iterator>::value or
-                 std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
-             = 0>
-    IteratorType erase(IteratorType pos)
-    {
-        // make sure iterator fits the current value
-        if (JSON_UNLIKELY(this != pos.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
-        }
-
-        IteratorType result = end();
-
-        switch (m_type)
-        {
-            case value_t::boolean:
-            case value_t::number_float:
-            case value_t::number_integer:
-            case value_t::number_unsigned:
-            case value_t::string:
-            {
-                if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin()))
-                {
-                    JSON_THROW(invalid_iterator::create(205, "iterator out of range"));
-                }
-
-                if (is_string())
-                {
-                    AllocatorType<string_t> alloc;
-                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
-                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
-                    m_value.string = nullptr;
-                }
-
-                m_type = value_t::null;
-                assert_invariant();
-                break;
-            }
-
-            case value_t::object:
-            {
-                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
-                break;
-            }
-
-            case value_t::array:
-            {
-                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
-                break;
-            }
-
-            default:
-                JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
-        }
-
-        return result;
-    }
-
-    /*!
-    @brief remove elements given an iterator range
-
-    Removes the element specified by the range `[first; last)`. The iterator
-    @a first does not need to be dereferenceable if `first == last`: erasing
-    an empty range is a no-op.
-
-    If called on a primitive type other than `null`, the resulting JSON value
-    will be `null`.
-
-    @param[in] first iterator to the beginning of the range to remove
-    @param[in] last iterator past the end of the range to remove
-    @return Iterator following the last removed element. If the iterator @a
-    second refers to the last element, the `end()` iterator is returned.
-
-    @tparam IteratorType an @ref iterator or @ref const_iterator
-
-    @post Invalidates iterators and references at or after the point of the
-    erase, including the `end()` iterator.
-
-    @throw type_error.307 if called on a `null` value; example: `"cannot use
-    erase() with null"`
-    @throw invalid_iterator.203 if called on iterators which does not belong
-    to the current JSON value; example: `"iterators do not fit current value"`
-    @throw invalid_iterator.204 if called on a primitive type with invalid
-    iterators (i.e., if `first != begin()` and `last != end()`); example:
-    `"iterators out of range"`
-
-    @complexity The complexity depends on the type:
-    - objects: `log(size()) + std::distance(first, last)`
-    - arrays: linear in the distance between @a first and @a last, plus linear
-      in the distance between @a last and end of the container
-    - strings: linear in the length of the string
-    - other types: constant
-
-    @liveexample{The example shows the result of `erase()` for different JSON
-    types.,erase__IteratorType_IteratorType}
-
-    @sa @ref erase(IteratorType) -- removes the element at a given position
-    @sa @ref erase(const typename object_t::key_type&) -- removes the element
-    from an object at the given key
-    @sa @ref erase(const size_type) -- removes the element from an array at
-    the given index
-
-    @since version 1.0.0
-    */
-    template<class IteratorType, typename std::enable_if<
-                 std::is_same<IteratorType, typename basic_json_t::iterator>::value or
-                 std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
-             = 0>
-    IteratorType erase(IteratorType first, IteratorType last)
-    {
-        // make sure iterator fits the current value
-        if (JSON_UNLIKELY(this != first.m_object or this != last.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value"));
-        }
-
-        IteratorType result = end();
-
-        switch (m_type)
-        {
-            case value_t::boolean:
-            case value_t::number_float:
-            case value_t::number_integer:
-            case value_t::number_unsigned:
-            case value_t::string:
-            {
-                if (JSON_LIKELY(not first.m_it.primitive_iterator.is_begin()
-                                or not last.m_it.primitive_iterator.is_end()))
-                {
-                    JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
-                }
-
-                if (is_string())
-                {
-                    AllocatorType<string_t> alloc;
-                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
-                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
-                    m_value.string = nullptr;
-                }
-
-                m_type = value_t::null;
-                assert_invariant();
-                break;
-            }
-
-            case value_t::object:
-            {
-                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
-                                              last.m_it.object_iterator);
-                break;
-            }
-
-            case value_t::array:
-            {
-                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
-                                             last.m_it.array_iterator);
-                break;
-            }
-
-            default:
-                JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
-        }
-
-        return result;
-    }
-
-    /*!
-    @brief remove element from a JSON object given a key
-
-    Removes elements from a JSON object with the key value @a key.
-
-    @param[in] key value of the elements to remove
-
-    @return Number of elements removed. If @a ObjectType is the default
-    `std::map` type, the return value will always be `0` (@a key was not
-    found) or `1` (@a key was found).
-
-    @post References and iterators to the erased elements are invalidated.
-    Other references and iterators are not affected.
-
-    @throw type_error.307 when called on a type other than JSON object;
-    example: `"cannot use erase() with null"`
-
-    @complexity `log(size()) + count(key)`
-
-    @liveexample{The example shows the effect of `erase()`.,erase__key_type}
-
-    @sa @ref erase(IteratorType) -- removes the element at a given position
-    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
-    the given range
-    @sa @ref erase(const size_type) -- removes the element from an array at
-    the given index
-
-    @since version 1.0.0
-    */
-    size_type erase(const typename object_t::key_type& key)
-    {
-        // this erase only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            return m_value.object->erase(key);
-        }
-
-        JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief remove element from a JSON array given an index
-
-    Removes element from a JSON array at the index @a idx.
-
-    @param[in] idx index of the element to remove
-
-    @throw type_error.307 when called on a type other than JSON object;
-    example: `"cannot use erase() with null"`
-    @throw out_of_range.401 when `idx >= size()`; example: `"array index 17
-    is out of range"`
-
-    @complexity Linear in distance between @a idx and the end of the container.
-
-    @liveexample{The example shows the effect of `erase()`.,erase__size_type}
-
-    @sa @ref erase(IteratorType) -- removes the element at a given position
-    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
-    the given range
-    @sa @ref erase(const typename object_t::key_type&) -- removes the element
-    from an object at the given key
-
-    @since version 1.0.0
-    */
-    void erase(const size_type idx)
-    {
-        // this erase only works for arrays
-        if (JSON_LIKELY(is_array()))
-        {
-            if (JSON_UNLIKELY(idx >= size()))
-            {
-                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
-            }
-
-            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
-        }
-        else
-        {
-            JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
-        }
-    }
-
-    /// @}
-
-
-    ////////////
-    // lookup //
-    ////////////
-
-    /// @name lookup
-    /// @{
-
-    /*!
-    @brief find an element in a JSON object
-
-    Finds an element in a JSON object with key equivalent to @a key. If the
-    element is not found or the JSON value is not an object, end() is
-    returned.
-
-    @note This method always returns @ref end() when executed on a JSON type
-          that is not an object.
-
-    @param[in] key key value of the element to search for.
-
-    @return Iterator to an element with key equivalent to @a key. If no such
-    element is found or the JSON value is not an object, past-the-end (see
-    @ref end()) iterator is returned.
-
-    @complexity Logarithmic in the size of the JSON object.
-
-    @liveexample{The example shows how `find()` is used.,find__key_type}
-
-    @since version 1.0.0
-    */
-    template<typename KeyT>
-    iterator find(KeyT&& key)
-    {
-        auto result = end();
-
-        if (is_object())
-        {
-            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));
-        }
-
-        return result;
-    }
-
-    /*!
-    @brief find an element in a JSON object
-    @copydoc find(KeyT&&)
-    */
-    template<typename KeyT>
-    const_iterator find(KeyT&& key) const
-    {
-        auto result = cend();
-
-        if (is_object())
-        {
-            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));
-        }
-
-        return result;
-    }
-
-    /*!
-    @brief returns the number of occurrences of a key in a JSON object
-
-    Returns the number of elements with key @a key. If ObjectType is the
-    default `std::map` type, the return value will always be `0` (@a key was
-    not found) or `1` (@a key was found).
-
-    @note This method always returns `0` when executed on a JSON type that is
-          not an object.
-
-    @param[in] key key value of the element to count
-
-    @return Number of elements with key @a key. If the JSON value is not an
-    object, the return value will be `0`.
-
-    @complexity Logarithmic in the size of the JSON object.
-
-    @liveexample{The example shows how `count()` is used.,count}
-
-    @since version 1.0.0
-    */
-    template<typename KeyT>
-    size_type count(KeyT&& key) const
-    {
-        // return 0 for all nonobject types
-        return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0;
-    }
-
-    /// @}
-
-
-    ///////////////
-    // iterators //
-    ///////////////
-
-    /// @name iterators
-    /// @{
-
-    /*!
-    @brief returns an iterator to the first element
-
-    Returns an iterator to the first element.
-
-    @image html range-begin-end.svg "Illustration from cppreference.com"
-
-    @return iterator to the first element
-
-    @complexity Constant.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is constant.
-
-    @liveexample{The following code shows an example for `begin()`.,begin}
-
-    @sa @ref cbegin() -- returns a const iterator to the beginning
-    @sa @ref end() -- returns an iterator to the end
-    @sa @ref cend() -- returns a const iterator to the end
-
-    @since version 1.0.0
-    */
-    iterator begin() noexcept
-    {
-        iterator result(this);
-        result.set_begin();
-        return result;
-    }
-
-    /*!
-    @copydoc basic_json::cbegin()
-    */
-    const_iterator begin() const noexcept
-    {
-        return cbegin();
-    }
-
-    /*!
-    @brief returns a const iterator to the first element
-
-    Returns a const iterator to the first element.
-
-    @image html range-begin-end.svg "Illustration from cppreference.com"
-
-    @return const iterator to the first element
-
-    @complexity Constant.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `const_cast<const basic_json&>(*this).begin()`.
-
-    @liveexample{The following code shows an example for `cbegin()`.,cbegin}
-
-    @sa @ref begin() -- returns an iterator to the beginning
-    @sa @ref end() -- returns an iterator to the end
-    @sa @ref cend() -- returns a const iterator to the end
-
-    @since version 1.0.0
-    */
-    const_iterator cbegin() const noexcept
-    {
-        const_iterator result(this);
-        result.set_begin();
-        return result;
-    }
-
-    /*!
-    @brief returns an iterator to one past the last element
-
-    Returns an iterator to one past the last element.
-
-    @image html range-begin-end.svg "Illustration from cppreference.com"
-
-    @return iterator one past the last element
-
-    @complexity Constant.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is constant.
-
-    @liveexample{The following code shows an example for `end()`.,end}
-
-    @sa @ref cend() -- returns a const iterator to the end
-    @sa @ref begin() -- returns an iterator to the beginning
-    @sa @ref cbegin() -- returns a const iterator to the beginning
-
-    @since version 1.0.0
-    */
-    iterator end() noexcept
-    {
-        iterator result(this);
-        result.set_end();
-        return result;
-    }
-
-    /*!
-    @copydoc basic_json::cend()
-    */
-    const_iterator end() const noexcept
-    {
-        return cend();
-    }
-
-    /*!
-    @brief returns a const iterator to one past the last element
-
-    Returns a const iterator to one past the last element.
-
-    @image html range-begin-end.svg "Illustration from cppreference.com"
-
-    @return const iterator one past the last element
-
-    @complexity Constant.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `const_cast<const basic_json&>(*this).end()`.
-
-    @liveexample{The following code shows an example for `cend()`.,cend}
-
-    @sa @ref end() -- returns an iterator to the end
-    @sa @ref begin() -- returns an iterator to the beginning
-    @sa @ref cbegin() -- returns a const iterator to the beginning
-
-    @since version 1.0.0
-    */
-    const_iterator cend() const noexcept
-    {
-        const_iterator result(this);
-        result.set_end();
-        return result;
-    }
-
-    /*!
-    @brief returns an iterator to the reverse-beginning
-
-    Returns an iterator to the reverse-beginning; that is, the last element.
-
-    @image html range-rbegin-rend.svg "Illustration from cppreference.com"
-
-    @complexity Constant.
-
-    @requirement This function helps `basic_json` satisfying the
-    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `reverse_iterator(end())`.
-
-    @liveexample{The following code shows an example for `rbegin()`.,rbegin}
-
-    @sa @ref crbegin() -- returns a const reverse iterator to the beginning
-    @sa @ref rend() -- returns a reverse iterator to the end
-    @sa @ref crend() -- returns a const reverse iterator to the end
-
-    @since version 1.0.0
-    */
-    reverse_iterator rbegin() noexcept
-    {
-        return reverse_iterator(end());
-    }
-
-    /*!
-    @copydoc basic_json::crbegin()
-    */
-    const_reverse_iterator rbegin() const noexcept
-    {
-        return crbegin();
-    }
-
-    /*!
-    @brief returns an iterator to the reverse-end
-
-    Returns an iterator to the reverse-end; that is, one before the first
-    element.
-
-    @image html range-rbegin-rend.svg "Illustration from cppreference.com"
-
-    @complexity Constant.
-
-    @requirement This function helps `basic_json` satisfying the
-    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `reverse_iterator(begin())`.
-
-    @liveexample{The following code shows an example for `rend()`.,rend}
-
-    @sa @ref crend() -- returns a const reverse iterator to the end
-    @sa @ref rbegin() -- returns a reverse iterator to the beginning
-    @sa @ref crbegin() -- returns a const reverse iterator to the beginning
-
-    @since version 1.0.0
-    */
-    reverse_iterator rend() noexcept
-    {
-        return reverse_iterator(begin());
-    }
-
-    /*!
-    @copydoc basic_json::crend()
-    */
-    const_reverse_iterator rend() const noexcept
-    {
-        return crend();
-    }
-
-    /*!
-    @brief returns a const reverse iterator to the last element
-
-    Returns a const iterator to the reverse-beginning; that is, the last
-    element.
-
-    @image html range-rbegin-rend.svg "Illustration from cppreference.com"
-
-    @complexity Constant.
-
-    @requirement This function helps `basic_json` satisfying the
-    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `const_cast<const basic_json&>(*this).rbegin()`.
-
-    @liveexample{The following code shows an example for `crbegin()`.,crbegin}
-
-    @sa @ref rbegin() -- returns a reverse iterator to the beginning
-    @sa @ref rend() -- returns a reverse iterator to the end
-    @sa @ref crend() -- returns a const reverse iterator to the end
-
-    @since version 1.0.0
-    */
-    const_reverse_iterator crbegin() const noexcept
-    {
-        return const_reverse_iterator(cend());
-    }
-
-    /*!
-    @brief returns a const reverse iterator to one before the first
-
-    Returns a const reverse iterator to the reverse-end; that is, one before
-    the first element.
-
-    @image html range-rbegin-rend.svg "Illustration from cppreference.com"
-
-    @complexity Constant.
-
-    @requirement This function helps `basic_json` satisfying the
-    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `const_cast<const basic_json&>(*this).rend()`.
-
-    @liveexample{The following code shows an example for `crend()`.,crend}
-
-    @sa @ref rend() -- returns a reverse iterator to the end
-    @sa @ref rbegin() -- returns a reverse iterator to the beginning
-    @sa @ref crbegin() -- returns a const reverse iterator to the beginning
-
-    @since version 1.0.0
-    */
-    const_reverse_iterator crend() const noexcept
-    {
-        return const_reverse_iterator(cbegin());
-    }
-
-  public:
-    /*!
-    @brief wrapper to access iterator member functions in range-based for
-
-    This function allows to access @ref iterator::key() and @ref
-    iterator::value() during range-based for loops. In these loops, a
-    reference to the JSON values is returned, so there is no access to the
-    underlying iterator.
-
-    For loop without iterator_wrapper:
-
-    @code{cpp}
-    for (auto it = j_object.begin(); it != j_object.end(); ++it)
-    {
-        std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
-    }
-    @endcode
-
-    Range-based for loop without iterator proxy:
-
-    @code{cpp}
-    for (auto it : j_object)
-    {
-        // "it" is of type json::reference and has no key() member
-        std::cout << "value: " << it << '\n';
-    }
-    @endcode
-
-    Range-based for loop with iterator proxy:
-
-    @code{cpp}
-    for (auto it : json::iterator_wrapper(j_object))
-    {
-        std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
-    }
-    @endcode
-
-    @note When iterating over an array, `key()` will return the index of the
-          element as string (see example).
-
-    @param[in] ref  reference to a JSON value
-    @return iteration proxy object wrapping @a ref with an interface to use in
-            range-based for loops
-
-    @liveexample{The following code shows how the wrapper is used,iterator_wrapper}
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @note The name of this function is not yet final and may change in the
-    future.
-
-    @deprecated This stream operator is deprecated and will be removed in
-                future 4.0.0 of the library. Please use @ref items() instead;
-                that is, replace `json::iterator_wrapper(j)` with `j.items()`.
-    */
-    JSON_DEPRECATED
-    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept
-    {
-        return ref.items();
-    }
-
-    /*!
-    @copydoc iterator_wrapper(reference)
-    */
-    JSON_DEPRECATED
-    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept
-    {
-        return ref.items();
-    }
-
-    /*!
-    @brief helper to access iterator member functions in range-based for
-
-    This function allows to access @ref iterator::key() and @ref
-    iterator::value() during range-based for loops. In these loops, a
-    reference to the JSON values is returned, so there is no access to the
-    underlying iterator.
-
-    For loop without `items()` function:
-
-    @code{cpp}
-    for (auto it = j_object.begin(); it != j_object.end(); ++it)
-    {
-        std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
-    }
-    @endcode
-
-    Range-based for loop without `items()` function:
-
-    @code{cpp}
-    for (auto it : j_object)
-    {
-        // "it" is of type json::reference and has no key() member
-        std::cout << "value: " << it << '\n';
-    }
-    @endcode
-
-    Range-based for loop with `items()` function:
-
-    @code{cpp}
-    for (auto& el : j_object.items())
-    {
-        std::cout << "key: " << el.key() << ", value:" << el.value() << '\n';
-    }
-    @endcode
-
-    The `items()` function also allows to use
-    [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding)
-    (C++17):
-
-    @code{cpp}
-    for (auto& [key, val] : j_object.items())
-    {
-        std::cout << "key: " << key << ", value:" << val << '\n';
-    }
-    @endcode
-
-    @note When iterating over an array, `key()` will return the index of the
-          element as string (see example). For primitive types (e.g., numbers),
-          `key()` returns an empty string.
-
-    @return iteration proxy object wrapping @a ref with an interface to use in
-            range-based for loops
-
-    @liveexample{The following code shows how the function is used.,items}
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 3.1.0, structured bindings support since 3.5.0.
-    */
-    iteration_proxy<iterator> items() noexcept
-    {
-        return iteration_proxy<iterator>(*this);
-    }
-
-    /*!
-    @copydoc items()
-    */
-    iteration_proxy<const_iterator> items() const noexcept
-    {
-        return iteration_proxy<const_iterator>(*this);
-    }
-
-    /// @}
-
-
-    //////////////
-    // capacity //
-    //////////////
-
-    /// @name capacity
-    /// @{
-
-    /*!
-    @brief checks whether the container is empty.
-
-    Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).
-
-    @return The return value depends on the different types and is
-            defined as follows:
-            Value type  | return value
-            ----------- | -------------
-            null        | `true`
-            boolean     | `false`
-            string      | `false`
-            number      | `false`
-            object      | result of function `object_t::empty()`
-            array       | result of function `array_t::empty()`
-
-    @liveexample{The following code uses `empty()` to check if a JSON
-    object contains any elements.,empty}
-
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
-    the Container concept; that is, their `empty()` functions have constant
-    complexity.
-
-    @iterators No changes.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @note This function does not return whether a string stored as JSON value
-    is empty - it returns whether the JSON container itself is empty which is
-    false in the case of a string.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `begin() == end()`.
-
-    @sa @ref size() -- returns the number of elements
-
-    @since version 1.0.0
-    */
-    bool empty() const noexcept
-    {
-        switch (m_type)
-        {
-            case value_t::null:
-            {
-                // null values are empty
-                return true;
-            }
-
-            case value_t::array:
-            {
-                // delegate call to array_t::empty()
-                return m_value.array->empty();
-            }
-
-            case value_t::object:
-            {
-                // delegate call to object_t::empty()
-                return m_value.object->empty();
-            }
-
-            default:
-            {
-                // all other types are nonempty
-                return false;
-            }
-        }
-    }
-
-    /*!
-    @brief returns the number of elements
-
-    Returns the number of elements in a JSON value.
-
-    @return The return value depends on the different types and is
-            defined as follows:
-            Value type  | return value
-            ----------- | -------------
-            null        | `0`
-            boolean     | `1`
-            string      | `1`
-            number      | `1`
-            object      | result of function object_t::size()
-            array       | result of function array_t::size()
-
-    @liveexample{The following code calls `size()` on the different value
-    types.,size}
-
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
-    the Container concept; that is, their size() functions have constant
-    complexity.
-
-    @iterators No changes.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @note This function does not return the length of a string stored as JSON
-    value - it returns the number of elements in the JSON value which is 1 in
-    the case of a string.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of `std::distance(begin(), end())`.
-
-    @sa @ref empty() -- checks whether the container is empty
-    @sa @ref max_size() -- returns the maximal number of elements
-
-    @since version 1.0.0
-    */
-    size_type size() const noexcept
-    {
-        switch (m_type)
-        {
-            case value_t::null:
-            {
-                // null values are empty
-                return 0;
-            }
-
-            case value_t::array:
-            {
-                // delegate call to array_t::size()
-                return m_value.array->size();
-            }
-
-            case value_t::object:
-            {
-                // delegate call to object_t::size()
-                return m_value.object->size();
-            }
-
-            default:
-            {
-                // all other types have size 1
-                return 1;
-            }
-        }
-    }
-
-    /*!
-    @brief returns the maximum possible number of elements
-
-    Returns the maximum number of elements a JSON value is able to hold due to
-    system or library implementation limitations, i.e. `std::distance(begin(),
-    end())` for the JSON value.
-
-    @return The return value depends on the different types and is
-            defined as follows:
-            Value type  | return value
-            ----------- | -------------
-            null        | `0` (same as `size()`)
-            boolean     | `1` (same as `size()`)
-            string      | `1` (same as `size()`)
-            number      | `1` (same as `size()`)
-            object      | result of function `object_t::max_size()`
-            array       | result of function `array_t::max_size()`
-
-    @liveexample{The following code calls `max_size()` on the different value
-    types. Note the output is implementation specific.,max_size}
-
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
-    the Container concept; that is, their `max_size()` functions have constant
-    complexity.
-
-    @iterators No changes.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @requirement This function helps `basic_json` satisfying the
-    [Container](https://en.cppreference.com/w/cpp/named_req/Container)
-    requirements:
-    - The complexity is constant.
-    - Has the semantics of returning `b.size()` where `b` is the largest
-      possible JSON value.
-
-    @sa @ref size() -- returns the number of elements
-
-    @since version 1.0.0
-    */
-    size_type max_size() const noexcept
-    {
-        switch (m_type)
-        {
-            case value_t::array:
-            {
-                // delegate call to array_t::max_size()
-                return m_value.array->max_size();
-            }
-
-            case value_t::object:
-            {
-                // delegate call to object_t::max_size()
-                return m_value.object->max_size();
-            }
-
-            default:
-            {
-                // all other types have max_size() == size()
-                return size();
-            }
-        }
-    }
-
-    /// @}
-
-
-    ///////////////
-    // modifiers //
-    ///////////////
-
-    /// @name modifiers
-    /// @{
-
-    /*!
-    @brief clears the contents
-
-    Clears the content of a JSON value and resets it to the default value as
-    if @ref basic_json(value_t) would have been called with the current value
-    type from @ref type():
-
-    Value type  | initial value
-    ----------- | -------------
-    null        | `null`
-    boolean     | `false`
-    string      | `""`
-    number      | `0`
-    object      | `{}`
-    array       | `[]`
-
-    @post Has the same effect as calling
-    @code {.cpp}
-    *this = basic_json(type());
-    @endcode
-
-    @liveexample{The example below shows the effect of `clear()` to different
-    JSON types.,clear}
-
-    @complexity Linear in the size of the JSON value.
-
-    @iterators All iterators, pointers and references related to this container
-               are invalidated.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @sa @ref basic_json(value_t) -- constructor that creates an object with the
-        same value than calling `clear()`
-
-    @since version 1.0.0
-    */
-    void clear() noexcept
-    {
-        switch (m_type)
-        {
-            case value_t::number_integer:
-            {
-                m_value.number_integer = 0;
-                break;
-            }
-
-            case value_t::number_unsigned:
-            {
-                m_value.number_unsigned = 0;
-                break;
-            }
-
-            case value_t::number_float:
-            {
-                m_value.number_float = 0.0;
-                break;
-            }
-
-            case value_t::boolean:
-            {
-                m_value.boolean = false;
-                break;
-            }
-
-            case value_t::string:
-            {
-                m_value.string->clear();
-                break;
-            }
-
-            case value_t::array:
-            {
-                m_value.array->clear();
-                break;
-            }
-
-            case value_t::object:
-            {
-                m_value.object->clear();
-                break;
-            }
-
-            default:
-                break;
-        }
-    }
-
-    /*!
-    @brief add an object to an array
-
-    Appends the given element @a val to the end of the JSON value. If the
-    function is called on a JSON null value, an empty array is created before
-    appending @a val.
-
-    @param[in] val the value to add to the JSON array
-
-    @throw type_error.308 when called on a type other than JSON array or
-    null; example: `"cannot use push_back() with number"`
-
-    @complexity Amortized constant.
-
-    @liveexample{The example shows how `push_back()` and `+=` can be used to
-    add elements to a JSON array. Note how the `null` value was silently
-    converted to a JSON array.,push_back}
-
-    @since version 1.0.0
-    */
-    void push_back(basic_json&& val)
-    {
-        // push_back only works for null objects or arrays
-        if (JSON_UNLIKELY(not(is_null() or is_array())))
-        {
-            JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
-        }
-
-        // transform null object into an array
-        if (is_null())
-        {
-            m_type = value_t::array;
-            m_value = value_t::array;
-            assert_invariant();
-        }
-
-        // add element to array (move semantics)
-        m_value.array->push_back(std::move(val));
-        // invalidate object
-        val.m_type = value_t::null;
-    }
-
-    /*!
-    @brief add an object to an array
-    @copydoc push_back(basic_json&&)
-    */
-    reference operator+=(basic_json&& val)
-    {
-        push_back(std::move(val));
-        return *this;
-    }
-
-    /*!
-    @brief add an object to an array
-    @copydoc push_back(basic_json&&)
-    */
-    void push_back(const basic_json& val)
-    {
-        // push_back only works for null objects or arrays
-        if (JSON_UNLIKELY(not(is_null() or is_array())))
-        {
-            JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
-        }
-
-        // transform null object into an array
-        if (is_null())
-        {
-            m_type = value_t::array;
-            m_value = value_t::array;
-            assert_invariant();
-        }
-
-        // add element to array
-        m_value.array->push_back(val);
-    }
-
-    /*!
-    @brief add an object to an array
-    @copydoc push_back(basic_json&&)
-    */
-    reference operator+=(const basic_json& val)
-    {
-        push_back(val);
-        return *this;
-    }
-
-    /*!
-    @brief add an object to an object
-
-    Inserts the given element @a val to the JSON object. If the function is
-    called on a JSON null value, an empty object is created before inserting
-    @a val.
-
-    @param[in] val the value to add to the JSON object
-
-    @throw type_error.308 when called on a type other than JSON object or
-    null; example: `"cannot use push_back() with number"`
-
-    @complexity Logarithmic in the size of the container, O(log(`size()`)).
-
-    @liveexample{The example shows how `push_back()` and `+=` can be used to
-    add elements to a JSON object. Note how the `null` value was silently
-    converted to a JSON object.,push_back__object_t__value}
-
-    @since version 1.0.0
-    */
-    void push_back(const typename object_t::value_type& val)
-    {
-        // push_back only works for null objects or objects
-        if (JSON_UNLIKELY(not(is_null() or is_object())))
-        {
-            JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
-        }
-
-        // transform null object into an object
-        if (is_null())
-        {
-            m_type = value_t::object;
-            m_value = value_t::object;
-            assert_invariant();
-        }
-
-        // add element to array
-        m_value.object->insert(val);
-    }
-
-    /*!
-    @brief add an object to an object
-    @copydoc push_back(const typename object_t::value_type&)
-    */
-    reference operator+=(const typename object_t::value_type& val)
-    {
-        push_back(val);
-        return *this;
-    }
-
-    /*!
-    @brief add an object to an object
-
-    This function allows to use `push_back` with an initializer list. In case
-
-    1. the current value is an object,
-    2. the initializer list @a init contains only two elements, and
-    3. the first element of @a init is a string,
-
-    @a init is converted into an object element and added using
-    @ref push_back(const typename object_t::value_type&). Otherwise, @a init
-    is converted to a JSON value and added using @ref push_back(basic_json&&).
-
-    @param[in] init  an initializer list
-
-    @complexity Linear in the size of the initializer list @a init.
-
-    @note This function is required to resolve an ambiguous overload error,
-          because pairs like `{"key", "value"}` can be both interpreted as
-          `object_t::value_type` or `std::initializer_list<basic_json>`, see
-          https://github.com/nlohmann/json/issues/235 for more information.
-
-    @liveexample{The example shows how initializer lists are treated as
-    objects when possible.,push_back__initializer_list}
-    */
-    void push_back(initializer_list_t init)
-    {
-        if (is_object() and init.size() == 2 and (*init.begin())->is_string())
-        {
-            basic_json&& key = init.begin()->moved_or_copied();
-            push_back(typename object_t::value_type(
-                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));
-        }
-        else
-        {
-            push_back(basic_json(init));
-        }
-    }
-
-    /*!
-    @brief add an object to an object
-    @copydoc push_back(initializer_list_t)
-    */
-    reference operator+=(initializer_list_t init)
-    {
-        push_back(init);
-        return *this;
-    }
-
-    /*!
-    @brief add an object to an array
-
-    Creates a JSON value from the passed parameters @a args to the end of the
-    JSON value. If the function is called on a JSON null value, an empty array
-    is created before appending the value created from @a args.
-
-    @param[in] args arguments to forward to a constructor of @ref basic_json
-    @tparam Args compatible types to create a @ref basic_json object
-
-    @throw type_error.311 when called on a type other than JSON array or
-    null; example: `"cannot use emplace_back() with number"`
-
-    @complexity Amortized constant.
-
-    @liveexample{The example shows how `push_back()` can be used to add
-    elements to a JSON array. Note how the `null` value was silently converted
-    to a JSON array.,emplace_back}
-
-    @since version 2.0.8
-    */
-    template<class... Args>
-    void emplace_back(Args&& ... args)
-    {
-        // emplace_back only works for null objects or arrays
-        if (JSON_UNLIKELY(not(is_null() or is_array())))
-        {
-            JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name())));
-        }
-
-        // transform null object into an array
-        if (is_null())
-        {
-            m_type = value_t::array;
-            m_value = value_t::array;
-            assert_invariant();
-        }
-
-        // add element to array (perfect forwarding)
-        m_value.array->emplace_back(std::forward<Args>(args)...);
-    }
-
-    /*!
-    @brief add an object to an object if key does not exist
-
-    Inserts a new element into a JSON object constructed in-place with the
-    given @a args if there is no element with the key in the container. If the
-    function is called on a JSON null value, an empty object is created before
-    appending the value created from @a args.
-
-    @param[in] args arguments to forward to a constructor of @ref basic_json
-    @tparam Args compatible types to create a @ref basic_json object
-
-    @return a pair consisting of an iterator to the inserted element, or the
-            already-existing element if no insertion happened, and a bool
-            denoting whether the insertion took place.
-
-    @throw type_error.311 when called on a type other than JSON object or
-    null; example: `"cannot use emplace() with number"`
-
-    @complexity Logarithmic in the size of the container, O(log(`size()`)).
-
-    @liveexample{The example shows how `emplace()` can be used to add elements
-    to a JSON object. Note how the `null` value was silently converted to a
-    JSON object. Further note how no value is added if there was already one
-    value stored with the same key.,emplace}
-
-    @since version 2.0.8
-    */
-    template<class... Args>
-    std::pair<iterator, bool> emplace(Args&& ... args)
-    {
-        // emplace only works for null objects or arrays
-        if (JSON_UNLIKELY(not(is_null() or is_object())))
-        {
-            JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name())));
-        }
-
-        // transform null object into an object
-        if (is_null())
-        {
-            m_type = value_t::object;
-            m_value = value_t::object;
-            assert_invariant();
-        }
-
-        // add element to array (perfect forwarding)
-        auto res = m_value.object->emplace(std::forward<Args>(args)...);
-        // create result iterator and set iterator to the result of emplace
-        auto it = begin();
-        it.m_it.object_iterator = res.first;
-
-        // return pair of iterator and boolean
-        return {it, res.second};
-    }
-
-    /// Helper for insertion of an iterator
-    /// @note: This uses std::distance to support GCC 4.8,
-    ///        see https://github.com/nlohmann/json/pull/1257
-    template<typename... Args>
-    iterator insert_iterator(const_iterator pos, Args&& ... args)
-    {
-        iterator result(this);
-        assert(m_value.array != nullptr);
-
-        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);
-        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);
-        result.m_it.array_iterator = m_value.array->begin() + insert_pos;
-
-        // This could have been written as:
-        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
-        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.
-
-        return result;
-    }
-
-    /*!
-    @brief inserts element
-
-    Inserts element @a val before iterator @a pos.
-
-    @param[in] pos iterator before which the content will be inserted; may be
-    the end() iterator
-    @param[in] val element to insert
-    @return iterator pointing to the inserted @a val.
-
-    @throw type_error.309 if called on JSON values other than arrays;
-    example: `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if @a pos is not an iterator of *this;
-    example: `"iterator does not fit current value"`
-
-    @complexity Constant plus linear in the distance between @a pos and end of
-    the container.
-
-    @liveexample{The example shows how `insert()` is used.,insert}
-
-    @since version 1.0.0
-    */
-    iterator insert(const_iterator pos, const basic_json& val)
-    {
-        // insert only works for arrays
-        if (JSON_LIKELY(is_array()))
-        {
-            // check if iterator pos fits to this JSON value
-            if (JSON_UNLIKELY(pos.m_object != this))
-            {
-                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
-            }
-
-            // insert to array and return iterator
-            return insert_iterator(pos, val);
-        }
-
-        JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief inserts element
-    @copydoc insert(const_iterator, const basic_json&)
-    */
-    iterator insert(const_iterator pos, basic_json&& val)
-    {
-        return insert(pos, val);
-    }
-
-    /*!
-    @brief inserts elements
-
-    Inserts @a cnt copies of @a val before iterator @a pos.
-
-    @param[in] pos iterator before which the content will be inserted; may be
-    the end() iterator
-    @param[in] cnt number of copies of @a val to insert
-    @param[in] val element to insert
-    @return iterator pointing to the first element inserted, or @a pos if
-    `cnt==0`
-
-    @throw type_error.309 if called on JSON values other than arrays; example:
-    `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if @a pos is not an iterator of *this;
-    example: `"iterator does not fit current value"`
-
-    @complexity Linear in @a cnt plus linear in the distance between @a pos
-    and end of the container.
-
-    @liveexample{The example shows how `insert()` is used.,insert__count}
-
-    @since version 1.0.0
-    */
-    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
-    {
-        // insert only works for arrays
-        if (JSON_LIKELY(is_array()))
-        {
-            // check if iterator pos fits to this JSON value
-            if (JSON_UNLIKELY(pos.m_object != this))
-            {
-                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
-            }
-
-            // insert to array and return iterator
-            return insert_iterator(pos, cnt, val);
-        }
-
-        JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
-    }
-
-    /*!
-    @brief inserts elements
-
-    Inserts elements from range `[first, last)` before iterator @a pos.
-
-    @param[in] pos iterator before which the content will be inserted; may be
-    the end() iterator
-    @param[in] first begin of the range of elements to insert
-    @param[in] last end of the range of elements to insert
-
-    @throw type_error.309 if called on JSON values other than arrays; example:
-    `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if @a pos is not an iterator of *this;
-    example: `"iterator does not fit current value"`
-    @throw invalid_iterator.210 if @a first and @a last do not belong to the
-    same JSON value; example: `"iterators do not fit"`
-    @throw invalid_iterator.211 if @a first or @a last are iterators into
-    container for which insert is called; example: `"passed iterators may not
-    belong to container"`
-
-    @return iterator pointing to the first element inserted, or @a pos if
-    `first==last`
-
-    @complexity Linear in `std::distance(first, last)` plus linear in the
-    distance between @a pos and end of the container.
-
-    @liveexample{The example shows how `insert()` is used.,insert__range}
-
-    @since version 1.0.0
-    */
-    iterator insert(const_iterator pos, const_iterator first, const_iterator last)
-    {
-        // insert only works for arrays
-        if (JSON_UNLIKELY(not is_array()))
-        {
-            JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
-        }
-
-        // check if iterator pos fits to this JSON value
-        if (JSON_UNLIKELY(pos.m_object != this))
-        {
-            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
-        }
-
-        // check if range iterators belong to the same JSON object
-        if (JSON_UNLIKELY(first.m_object != last.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
-        }
-
-        if (JSON_UNLIKELY(first.m_object == this))
-        {
-            JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
-        }
-
-        // insert to array and return iterator
-        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
-    }
-
-    /*!
-    @brief inserts elements
-
-    Inserts elements from initializer list @a ilist before iterator @a pos.
-
-    @param[in] pos iterator before which the content will be inserted; may be
-    the end() iterator
-    @param[in] ilist initializer list to insert the values from
-
-    @throw type_error.309 if called on JSON values other than arrays; example:
-    `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if @a pos is not an iterator of *this;
-    example: `"iterator does not fit current value"`
-
-    @return iterator pointing to the first element inserted, or @a pos if
-    `ilist` is empty
-
-    @complexity Linear in `ilist.size()` plus linear in the distance between
-    @a pos and end of the container.
-
-    @liveexample{The example shows how `insert()` is used.,insert__ilist}
-
-    @since version 1.0.0
-    */
-    iterator insert(const_iterator pos, initializer_list_t ilist)
-    {
-        // insert only works for arrays
-        if (JSON_UNLIKELY(not is_array()))
-        {
-            JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
-        }
-
-        // check if iterator pos fits to this JSON value
-        if (JSON_UNLIKELY(pos.m_object != this))
-        {
-            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
-        }
-
-        // insert to array and return iterator
-        return insert_iterator(pos, ilist.begin(), ilist.end());
-    }
-
-    /*!
-    @brief inserts elements
-
-    Inserts elements from range `[first, last)`.
-
-    @param[in] first begin of the range of elements to insert
-    @param[in] last end of the range of elements to insert
-
-    @throw type_error.309 if called on JSON values other than objects; example:
-    `"cannot use insert() with string"`
-    @throw invalid_iterator.202 if iterator @a first or @a last does does not
-    point to an object; example: `"iterators first and last must point to
-    objects"`
-    @throw invalid_iterator.210 if @a first and @a last do not belong to the
-    same JSON value; example: `"iterators do not fit"`
-
-    @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number
-    of elements to insert.
-
-    @liveexample{The example shows how `insert()` is used.,insert__range_object}
-
-    @since version 3.0.0
-    */
-    void insert(const_iterator first, const_iterator last)
-    {
-        // insert only works for objects
-        if (JSON_UNLIKELY(not is_object()))
-        {
-            JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
-        }
-
-        // check if range iterators belong to the same JSON object
-        if (JSON_UNLIKELY(first.m_object != last.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
-        }
-
-        // passed iterators must belong to objects
-        if (JSON_UNLIKELY(not first.m_object->is_object()))
-        {
-            JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
-        }
-
-        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
-    }
-
-    /*!
-    @brief updates a JSON object from another object, overwriting existing keys
-
-    Inserts all values from JSON object @a j and overwrites existing keys.
-
-    @param[in] j  JSON object to read values from
-
-    @throw type_error.312 if called on JSON values other than objects; example:
-    `"cannot use update() with string"`
-
-    @complexity O(N*log(size() + N)), where N is the number of elements to
-                insert.
-
-    @liveexample{The example shows how `update()` is used.,update}
-
-    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
-
-    @since version 3.0.0
-    */
-    void update(const_reference j)
-    {
-        // implicitly convert null value to an empty object
-        if (is_null())
-        {
-            m_type = value_t::object;
-            m_value.object = create<object_t>();
-            assert_invariant();
-        }
-
-        if (JSON_UNLIKELY(not is_object()))
-        {
-            JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
-        }
-        if (JSON_UNLIKELY(not j.is_object()))
-        {
-            JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name())));
-        }
-
-        for (auto it = j.cbegin(); it != j.cend(); ++it)
-        {
-            m_value.object->operator[](it.key()) = it.value();
-        }
-    }
-
-    /*!
-    @brief updates a JSON object from another object, overwriting existing keys
-
-    Inserts all values from from range `[first, last)` and overwrites existing
-    keys.
-
-    @param[in] first begin of the range of elements to insert
-    @param[in] last end of the range of elements to insert
-
-    @throw type_error.312 if called on JSON values other than objects; example:
-    `"cannot use update() with string"`
-    @throw invalid_iterator.202 if iterator @a first or @a last does does not
-    point to an object; example: `"iterators first and last must point to
-    objects"`
-    @throw invalid_iterator.210 if @a first and @a last do not belong to the
-    same JSON value; example: `"iterators do not fit"`
-
-    @complexity O(N*log(size() + N)), where N is the number of elements to
-                insert.
-
-    @liveexample{The example shows how `update()` is used__range.,update}
-
-    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
-
-    @since version 3.0.0
-    */
-    void update(const_iterator first, const_iterator last)
-    {
-        // implicitly convert null value to an empty object
-        if (is_null())
-        {
-            m_type = value_t::object;
-            m_value.object = create<object_t>();
-            assert_invariant();
-        }
-
-        if (JSON_UNLIKELY(not is_object()))
-        {
-            JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
-        }
-
-        // check if range iterators belong to the same JSON object
-        if (JSON_UNLIKELY(first.m_object != last.m_object))
-        {
-            JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
-        }
-
-        // passed iterators must belong to objects
-        if (JSON_UNLIKELY(not first.m_object->is_object()
-                          or not last.m_object->is_object()))
-        {
-            JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
-        }
-
-        for (auto it = first; it != last; ++it)
-        {
-            m_value.object->operator[](it.key()) = it.value();
-        }
-    }
-
-    /*!
-    @brief exchanges the values
-
-    Exchanges the contents of the JSON value with those of @a other. Does not
-    invoke any move, copy, or swap operations on individual elements. All
-    iterators and references remain valid. The past-the-end iterator is
-    invalidated.
-
-    @param[in,out] other JSON value to exchange the contents with
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how JSON values can be swapped with
-    `swap()`.,swap__reference}
-
-    @since version 1.0.0
-    */
-    void swap(reference other) noexcept (
-        std::is_nothrow_move_constructible<value_t>::value and
-        std::is_nothrow_move_assignable<value_t>::value and
-        std::is_nothrow_move_constructible<json_value>::value and
-        std::is_nothrow_move_assignable<json_value>::value
-    )
-    {
-        std::swap(m_type, other.m_type);
-        std::swap(m_value, other.m_value);
-        assert_invariant();
-    }
-
-    /*!
-    @brief exchanges the values
-
-    Exchanges the contents of a JSON array with those of @a other. Does not
-    invoke any move, copy, or swap operations on individual elements. All
-    iterators and references remain valid. The past-the-end iterator is
-    invalidated.
-
-    @param[in,out] other array to exchange the contents with
-
-    @throw type_error.310 when JSON value is not an array; example: `"cannot
-    use swap() with string"`
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how arrays can be swapped with
-    `swap()`.,swap__array_t}
-
-    @since version 1.0.0
-    */
-    void swap(array_t& other)
-    {
-        // swap only works for arrays
-        if (JSON_LIKELY(is_array()))
-        {
-            std::swap(*(m_value.array), other);
-        }
-        else
-        {
-            JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
-        }
-    }
-
-    /*!
-    @brief exchanges the values
-
-    Exchanges the contents of a JSON object with those of @a other. Does not
-    invoke any move, copy, or swap operations on individual elements. All
-    iterators and references remain valid. The past-the-end iterator is
-    invalidated.
-
-    @param[in,out] other object to exchange the contents with
-
-    @throw type_error.310 when JSON value is not an object; example:
-    `"cannot use swap() with string"`
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how objects can be swapped with
-    `swap()`.,swap__object_t}
-
-    @since version 1.0.0
-    */
-    void swap(object_t& other)
-    {
-        // swap only works for objects
-        if (JSON_LIKELY(is_object()))
-        {
-            std::swap(*(m_value.object), other);
-        }
-        else
-        {
-            JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
-        }
-    }
-
-    /*!
-    @brief exchanges the values
-
-    Exchanges the contents of a JSON string with those of @a other. Does not
-    invoke any move, copy, or swap operations on individual elements. All
-    iterators and references remain valid. The past-the-end iterator is
-    invalidated.
-
-    @param[in,out] other string to exchange the contents with
-
-    @throw type_error.310 when JSON value is not a string; example: `"cannot
-    use swap() with boolean"`
-
-    @complexity Constant.
-
-    @liveexample{The example below shows how strings can be swapped with
-    `swap()`.,swap__string_t}
-
-    @since version 1.0.0
-    */
-    void swap(string_t& other)
-    {
-        // swap only works for strings
-        if (JSON_LIKELY(is_string()))
-        {
-            std::swap(*(m_value.string), other);
-        }
-        else
-        {
-            JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
-        }
-    }
-
-    /// @}
-
-  public:
-    //////////////////////////////////////////
-    // lexicographical comparison operators //
-    //////////////////////////////////////////
-
-    /// @name lexicographical comparison operators
-    /// @{
-
-    /*!
-    @brief comparison: equal
-
-    Compares two JSON values for equality according to the following rules:
-    - Two JSON values are equal if (1) they are from the same type and (2)
-      their stored values are the same according to their respective
-      `operator==`.
-    - Integer and floating-point numbers are automatically converted before
-      comparison. Note than two NaN values are always treated as unequal.
-    - Two JSON null values are equal.
-
-    @note Floating-point inside JSON values numbers are compared with
-    `json::number_float_t::operator==` which is `double::operator==` by
-    default. To compare floating-point while respecting an epsilon, an alternative
-    [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39)
-    could be used, for instance
-    @code {.cpp}
-    template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>
-    inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept
-    {
-        return std::abs(a - b) <= epsilon;
-    }
-    @endcode
-
-    @note NaN values never compare equal to themselves or to other NaN values.
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether the values @a lhs and @a rhs are equal
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @complexity Linear.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__equal}
-
-    @since version 1.0.0
-    */
-    friend bool operator==(const_reference lhs, const_reference rhs) noexcept
-    {
-        const auto lhs_type = lhs.type();
-        const auto rhs_type = rhs.type();
-
-        if (lhs_type == rhs_type)
-        {
-            switch (lhs_type)
-            {
-                case value_t::array:
-                    return (*lhs.m_value.array == *rhs.m_value.array);
-
-                case value_t::object:
-                    return (*lhs.m_value.object == *rhs.m_value.object);
-
-                case value_t::null:
-                    return true;
-
-                case value_t::string:
-                    return (*lhs.m_value.string == *rhs.m_value.string);
-
-                case value_t::boolean:
-                    return (lhs.m_value.boolean == rhs.m_value.boolean);
-
-                case value_t::number_integer:
-                    return (lhs.m_value.number_integer == rhs.m_value.number_integer);
-
-                case value_t::number_unsigned:
-                    return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned);
-
-                case value_t::number_float:
-                    return (lhs.m_value.number_float == rhs.m_value.number_float);
-
-                default:
-                    return false;
-            }
-        }
-        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
-        {
-            return (static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float);
-        }
-        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)
-        {
-            return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer));
-        }
-        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)
-        {
-            return (static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float);
-        }
-        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)
-        {
-            return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned));
-        }
-        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)
-        {
-            return (static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer);
-        }
-        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)
-        {
-            return (lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned));
-        }
-
-        return false;
-    }
-
-    /*!
-    @brief comparison: equal
-    @copydoc operator==(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept
-    {
-        return (lhs == basic_json(rhs));
-    }
-
-    /*!
-    @brief comparison: equal
-    @copydoc operator==(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept
-    {
-        return (basic_json(lhs) == rhs);
-    }
-
-    /*!
-    @brief comparison: not equal
-
-    Compares two JSON values for inequality by calculating `not (lhs == rhs)`.
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether the values @a lhs and @a rhs are not equal
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__notequal}
-
-    @since version 1.0.0
-    */
-    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
-    {
-        return not (lhs == rhs);
-    }
-
-    /*!
-    @brief comparison: not equal
-    @copydoc operator!=(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept
-    {
-        return (lhs != basic_json(rhs));
-    }
-
-    /*!
-    @brief comparison: not equal
-    @copydoc operator!=(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept
-    {
-        return (basic_json(lhs) != rhs);
-    }
-
-    /*!
-    @brief comparison: less than
-
-    Compares whether one JSON value @a lhs is less than another JSON value @a
-    rhs according to the following rules:
-    - If @a lhs and @a rhs have the same type, the values are compared using
-      the default `<` operator.
-    - Integer and floating-point numbers are automatically converted before
-      comparison
-    - In case @a lhs and @a rhs have different types, the values are ignored
-      and the order of the types is considered, see
-      @ref operator<(const value_t, const value_t).
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether @a lhs is less than @a rhs
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__less}
-
-    @since version 1.0.0
-    */
-    friend bool operator<(const_reference lhs, const_reference rhs) noexcept
-    {
-        const auto lhs_type = lhs.type();
-        const auto rhs_type = rhs.type();
-
-        if (lhs_type == rhs_type)
-        {
-            switch (lhs_type)
-            {
-                case value_t::array:
-                    return (*lhs.m_value.array) < (*rhs.m_value.array);
-
-                case value_t::object:
-                    return *lhs.m_value.object < *rhs.m_value.object;
-
-                case value_t::null:
-                    return false;
-
-                case value_t::string:
-                    return *lhs.m_value.string < *rhs.m_value.string;
-
-                case value_t::boolean:
-                    return lhs.m_value.boolean < rhs.m_value.boolean;
-
-                case value_t::number_integer:
-                    return lhs.m_value.number_integer < rhs.m_value.number_integer;
-
-                case value_t::number_unsigned:
-                    return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned;
-
-                case value_t::number_float:
-                    return lhs.m_value.number_float < rhs.m_value.number_float;
-
-                default:
-                    return false;
-            }
-        }
-        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)
-        {
-            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);
-        }
-        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)
-        {
-            return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
-        }
-        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);
-        }
-        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)
-        {
-            return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);
-        }
-        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)
-        {
-            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
-        }
-
-        // We only reach this line if we cannot compare values. In that case,
-        // we compare types. Note we have to call the operator explicitly,
-        // because MSVC has problems otherwise.
-        return operator<(lhs_type, rhs_type);
-    }
-
-    /*!
-    @brief comparison: less than
-    @copydoc operator<(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept
-    {
-        return (lhs < basic_json(rhs));
-    }
-
-    /*!
-    @brief comparison: less than
-    @copydoc operator<(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept
-    {
-        return (basic_json(lhs) < rhs);
-    }
-
-    /*!
-    @brief comparison: less than or equal
-
-    Compares whether one JSON value @a lhs is less than or equal to another
-    JSON value by calculating `not (rhs < lhs)`.
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether @a lhs is less than or equal to @a rhs
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__greater}
-
-    @since version 1.0.0
-    */
-    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
-    {
-        return not (rhs < lhs);
-    }
-
-    /*!
-    @brief comparison: less than or equal
-    @copydoc operator<=(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept
-    {
-        return (lhs <= basic_json(rhs));
-    }
-
-    /*!
-    @brief comparison: less than or equal
-    @copydoc operator<=(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept
-    {
-        return (basic_json(lhs) <= rhs);
-    }
-
-    /*!
-    @brief comparison: greater than
-
-    Compares whether one JSON value @a lhs is greater than another
-    JSON value by calculating `not (lhs <= rhs)`.
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether @a lhs is greater than to @a rhs
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__lessequal}
-
-    @since version 1.0.0
-    */
-    friend bool operator>(const_reference lhs, const_reference rhs) noexcept
-    {
-        return not (lhs <= rhs);
-    }
-
-    /*!
-    @brief comparison: greater than
-    @copydoc operator>(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept
-    {
-        return (lhs > basic_json(rhs));
-    }
-
-    /*!
-    @brief comparison: greater than
-    @copydoc operator>(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept
-    {
-        return (basic_json(lhs) > rhs);
-    }
-
-    /*!
-    @brief comparison: greater than or equal
-
-    Compares whether one JSON value @a lhs is greater than or equal to another
-    JSON value by calculating `not (lhs < rhs)`.
-
-    @param[in] lhs  first JSON value to consider
-    @param[in] rhs  second JSON value to consider
-    @return whether @a lhs is greater than or equal to @a rhs
-
-    @complexity Linear.
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @liveexample{The example demonstrates comparing several JSON
-    types.,operator__greaterequal}
-
-    @since version 1.0.0
-    */
-    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
-    {
-        return not (lhs < rhs);
-    }
-
-    /*!
-    @brief comparison: greater than or equal
-    @copydoc operator>=(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept
-    {
-        return (lhs >= basic_json(rhs));
-    }
-
-    /*!
-    @brief comparison: greater than or equal
-    @copydoc operator>=(const_reference, const_reference)
-    */
-    template<typename ScalarType, typename std::enable_if<
-                 std::is_scalar<ScalarType>::value, int>::type = 0>
-    friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept
-    {
-        return (basic_json(lhs) >= rhs);
-    }
-
-    /// @}
-
-    ///////////////////
-    // serialization //
-    ///////////////////
-
-    /// @name serialization
-    /// @{
-
-    /*!
-    @brief serialize to stream
-
-    Serialize the given JSON value @a j to the output stream @a o. The JSON
-    value will be serialized using the @ref dump member function.
-
-    - The indentation of the output can be controlled with the member variable
-      `width` of the output stream @a o. For instance, using the manipulator
-      `std::setw(4)` on @a o sets the indentation level to `4` and the
-      serialization result is the same as calling `dump(4)`.
-
-    - The indentation character can be controlled with the member variable
-      `fill` of the output stream @a o. For instance, the manipulator
-      `std::setfill('\\t')` sets indentation to use a tab character rather than
-      the default space character.
-
-    @param[in,out] o  stream to serialize to
-    @param[in] j  JSON value to serialize
-
-    @return the stream @a o
-
-    @throw type_error.316 if a string stored inside the JSON value is not
-                          UTF-8 encoded
-
-    @complexity Linear.
-
-    @liveexample{The example below shows the serialization with different
-    parameters to `width` to adjust the indentation level.,operator_serialize}
-
-    @since version 1.0.0; indentation character added in version 3.0.0
-    */
-    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
-    {
-        // read width member and use it as indentation parameter if nonzero
-        const bool pretty_print = (o.width() > 0);
-        const auto indentation = (pretty_print ? o.width() : 0);
-
-        // reset width to 0 for subsequent calls to this stream
-        o.width(0);
-
-        // do the actual serialization
-        serializer s(detail::output_adapter<char>(o), o.fill());
-        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));
-        return o;
-    }
-
-    /*!
-    @brief serialize to stream
-    @deprecated This stream operator is deprecated and will be removed in
-                future 4.0.0 of the library. Please use
-                @ref operator<<(std::ostream&, const basic_json&)
-                instead; that is, replace calls like `j >> o;` with `o << j;`.
-    @since version 1.0.0; deprecated since version 3.0.0
-    */
-    JSON_DEPRECATED
-    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
-    {
-        return o << j;
-    }
-
-    /// @}
-
-
-    /////////////////////
-    // deserialization //
-    /////////////////////
-
-    /// @name deserialization
-    /// @{
-
-    /*!
-    @brief deserialize from a compatible input
-
-    This function reads from a compatible input. Examples are:
-    - an array of 1-byte values
-    - strings with character/literal type with size of 1 byte
-    - input streams
-    - container with contiguous storage of 1-byte values. Compatible container
-      types include `std::vector`, `std::string`, `std::array`,
-      `std::valarray`, and `std::initializer_list`. Furthermore, C-style
-      arrays can be used with `std::begin()`/`std::end()`. User-defined
-      containers can be used as long as they implement random-access iterators
-      and a contiguous storage.
-
-    @pre Each element of the container has a size of 1 byte. Violating this
-    precondition yields undefined behavior. **This precondition is enforced
-    with a static assertion.**
-
-    @pre The container storage is contiguous. Violating this precondition
-    yields undefined behavior. **This precondition is enforced with an
-    assertion.**
-    @pre Each element of the container has a size of 1 byte. Violating this
-    precondition yields undefined behavior. **This precondition is enforced
-    with a static assertion.**
-
-    @warning There is no way to enforce all preconditions at compile-time. If
-             the function is called with a noncompliant container and with
-             assertions switched off, the behavior is undefined and will most
-             likely yield segmentation violation.
-
-    @param[in] i  input to read from
-    @param[in] cb  a parser callback function of type @ref parser_callback_t
-    which is used to control the deserialization by filtering unwanted values
-    (optional)
-    @param[in] allow_exceptions  whether to throw exceptions in case of a
-    parse error (optional, true by default)
-
-    @return result of the deserialization
-
-    @throw parse_error.101 if a parse error occurs; example: `""unexpected end
-    of input; expected string literal""`
-    @throw parse_error.102 if to_unicode fails or surrogate error
-    @throw parse_error.103 if to_unicode fails
-
-    @complexity Linear in the length of the input. The parser is a predictive
-    LL(1) parser. The complexity can be higher if the parser callback function
-    @a cb has a super-linear complexity.
-
-    @note A UTF-8 byte order mark is silently ignored.
-
-    @liveexample{The example below demonstrates the `parse()` function reading
-    from an array.,parse__array__parser_callback_t}
-
-    @liveexample{The example below demonstrates the `parse()` function with
-    and without callback function.,parse__string__parser_callback_t}
-
-    @liveexample{The example below demonstrates the `parse()` function with
-    and without callback function.,parse__istream__parser_callback_t}
-
-    @liveexample{The example below demonstrates the `parse()` function reading
-    from a contiguous container.,parse__contiguouscontainer__parser_callback_t}
-
-    @since version 2.0.3 (contiguous containers)
-    */
-    static basic_json parse(detail::input_adapter&& i,
-                            const parser_callback_t cb = nullptr,
-                            const bool allow_exceptions = true)
-    {
-        basic_json result;
-        parser(i, cb, allow_exceptions).parse(true, result);
-        return result;
-    }
-
-    static bool accept(detail::input_adapter&& i)
-    {
-        return parser(i).accept(true);
-    }
-
-    /*!
-    @brief generate SAX events
-
-    The SAX event lister must follow the interface of @ref json_sax.
-
-    This function reads from a compatible input. Examples are:
-    - an array of 1-byte values
-    - strings with character/literal type with size of 1 byte
-    - input streams
-    - container with contiguous storage of 1-byte values. Compatible container
-      types include `std::vector`, `std::string`, `std::array`,
-      `std::valarray`, and `std::initializer_list`. Furthermore, C-style
-      arrays can be used with `std::begin()`/`std::end()`. User-defined
-      containers can be used as long as they implement random-access iterators
-      and a contiguous storage.
-
-    @pre Each element of the container has a size of 1 byte. Violating this
-    precondition yields undefined behavior. **This precondition is enforced
-    with a static assertion.**
-
-    @pre The container storage is contiguous. Violating this precondition
-    yields undefined behavior. **This precondition is enforced with an
-    assertion.**
-    @pre Each element of the container has a size of 1 byte. Violating this
-    precondition yields undefined behavior. **This precondition is enforced
-    with a static assertion.**
-
-    @warning There is no way to enforce all preconditions at compile-time. If
-             the function is called with a noncompliant container and with
-             assertions switched off, the behavior is undefined and will most
-             likely yield segmentation violation.
-
-    @param[in] i  input to read from
-    @param[in,out] sax  SAX event listener
-    @param[in] format  the format to parse (JSON, CBOR, MessagePack, or UBJSON)
-    @param[in] strict  whether the input has to be consumed completely
-
-    @return return value of the last processed SAX event
-
-    @throw parse_error.101 if a parse error occurs; example: `""unexpected end
-    of input; expected string literal""`
-    @throw parse_error.102 if to_unicode fails or surrogate error
-    @throw parse_error.103 if to_unicode fails
-
-    @complexity Linear in the length of the input. The parser is a predictive
-    LL(1) parser. The complexity can be higher if the SAX consumer @a sax has
-    a super-linear complexity.
-
-    @note A UTF-8 byte order mark is silently ignored.
-
-    @liveexample{The example below demonstrates the `sax_parse()` function
-    reading from string and processing the events with a user-defined SAX
-    event consumer.,sax_parse}
-
-    @since version 3.2.0
-    */
-    template <typename SAX>
-    static bool sax_parse(detail::input_adapter&& i, SAX* sax,
-                          input_format_t format = input_format_t::json,
-                          const bool strict = true)
-    {
-        assert(sax);
-        switch (format)
-        {
-            case input_format_t::json:
-                return parser(std::move(i)).sax_parse(sax, strict);
-            default:
-                return detail::binary_reader<basic_json, SAX>(std::move(i)).sax_parse(format, sax, strict);
-        }
-    }
-
-    /*!
-    @brief deserialize from an iterator range with contiguous storage
-
-    This function reads from an iterator range of a container with contiguous
-    storage of 1-byte values. Compatible container types include
-    `std::vector`, `std::string`, `std::array`, `std::valarray`, and
-    `std::initializer_list`. Furthermore, C-style arrays can be used with
-    `std::begin()`/`std::end()`. User-defined containers can be used as long
-    as they implement random-access iterators and a contiguous storage.
-
-    @pre The iterator range is contiguous. Violating this precondition yields
-    undefined behavior. **This precondition is enforced with an assertion.**
-    @pre Each element in the range has a size of 1 byte. Violating this
-    precondition yields undefined behavior. **This precondition is enforced
-    with a static assertion.**
-
-    @warning There is no way to enforce all preconditions at compile-time. If
-             the function is called with noncompliant iterators and with
-             assertions switched off, the behavior is undefined and will most
-             likely yield segmentation violation.
-
-    @tparam IteratorType iterator of container with contiguous storage
-    @param[in] first  begin of the range to parse (included)
-    @param[in] last  end of the range to parse (excluded)
-    @param[in] cb  a parser callback function of type @ref parser_callback_t
-    which is used to control the deserialization by filtering unwanted values
-    (optional)
-    @param[in] allow_exceptions  whether to throw exceptions in case of a
-    parse error (optional, true by default)
-
-    @return result of the deserialization
-
-    @throw parse_error.101 in case of an unexpected token
-    @throw parse_error.102 if to_unicode fails or surrogate error
-    @throw parse_error.103 if to_unicode fails
-
-    @complexity Linear in the length of the input. The parser is a predictive
-    LL(1) parser. The complexity can be higher if the parser callback function
-    @a cb has a super-linear complexity.
-
-    @note A UTF-8 byte order mark is silently ignored.
-
-    @liveexample{The example below demonstrates the `parse()` function reading
-    from an iterator range.,parse__iteratortype__parser_callback_t}
-
-    @since version 2.0.3
-    */
-    template<class IteratorType, typename std::enable_if<
-                 std::is_base_of<
-                     std::random_access_iterator_tag,
-                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
-    static basic_json parse(IteratorType first, IteratorType last,
-                            const parser_callback_t cb = nullptr,
-                            const bool allow_exceptions = true)
-    {
-        basic_json result;
-        parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result);
-        return result;
-    }
-
-    template<class IteratorType, typename std::enable_if<
-                 std::is_base_of<
-                     std::random_access_iterator_tag,
-                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
-    static bool accept(IteratorType first, IteratorType last)
-    {
-        return parser(detail::input_adapter(first, last)).accept(true);
-    }
-
-    template<class IteratorType, class SAX, typename std::enable_if<
-                 std::is_base_of<
-                     std::random_access_iterator_tag,
-                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
-    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax)
-    {
-        return parser(detail::input_adapter(first, last)).sax_parse(sax);
-    }
-
-    /*!
-    @brief deserialize from stream
-    @deprecated This stream operator is deprecated and will be removed in
-                version 4.0.0 of the library. Please use
-                @ref operator>>(std::istream&, basic_json&)
-                instead; that is, replace calls like `j << i;` with `i >> j;`.
-    @since version 1.0.0; deprecated since version 3.0.0
-    */
-    JSON_DEPRECATED
-    friend std::istream& operator<<(basic_json& j, std::istream& i)
-    {
-        return operator>>(i, j);
-    }
-
-    /*!
-    @brief deserialize from stream
-
-    Deserializes an input stream to a JSON value.
-
-    @param[in,out] i  input stream to read a serialized JSON value from
-    @param[in,out] j  JSON value to write the deserialized input to
-
-    @throw parse_error.101 in case of an unexpected token
-    @throw parse_error.102 if to_unicode fails or surrogate error
-    @throw parse_error.103 if to_unicode fails
-
-    @complexity Linear in the length of the input. The parser is a predictive
-    LL(1) parser.
-
-    @note A UTF-8 byte order mark is silently ignored.
-
-    @liveexample{The example below shows how a JSON value is constructed by
-    reading a serialization from a stream.,operator_deserialize}
-
-    @sa parse(std::istream&, const parser_callback_t) for a variant with a
-    parser callback function to filter values while parsing
-
-    @since version 1.0.0
-    */
-    friend std::istream& operator>>(std::istream& i, basic_json& j)
-    {
-        parser(detail::input_adapter(i)).parse(false, j);
-        return i;
-    }
-
-    /// @}
-
-    ///////////////////////////
-    // convenience functions //
-    ///////////////////////////
-
-    /*!
-    @brief return the type as string
-
-    Returns the type name as string to be used in error messages - usually to
-    indicate that a function was called on a wrong JSON type.
-
-    @return a string representation of a the @a m_type member:
-            Value type  | return value
-            ----------- | -------------
-            null        | `"null"`
-            boolean     | `"boolean"`
-            string      | `"string"`
-            number      | `"number"` (for all number types)
-            object      | `"object"`
-            array       | `"array"`
-            discarded   | `"discarded"`
-
-    @exceptionsafety No-throw guarantee: this function never throws exceptions.
-
-    @complexity Constant.
-
-    @liveexample{The following code exemplifies `type_name()` for all JSON
-    types.,type_name}
-
-    @sa @ref type() -- return the type of the JSON value
-    @sa @ref operator value_t() -- return the type of the JSON value (implicit)
-
-    @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`
-    since 3.0.0
-    */
-    const char* type_name() const noexcept
-    {
-        {
-            switch (m_type)
-            {
-                case value_t::null:
-                    return "null";
-                case value_t::object:
-                    return "object";
-                case value_t::array:
-                    return "array";
-                case value_t::string:
-                    return "string";
-                case value_t::boolean:
-                    return "boolean";
-                case value_t::discarded:
-                    return "discarded";
-                default:
-                    return "number";
-            }
-        }
-    }
-
-
-  private:
-    //////////////////////
-    // member variables //
-    //////////////////////
-
-    /// the type of the current element
-    value_t m_type = value_t::null;
-
-    /// the value of the current element
-    json_value m_value = {};
-
-    //////////////////////////////////////////
-    // binary serialization/deserialization //
-    //////////////////////////////////////////
-
-    /// @name binary serialization/deserialization support
-    /// @{
-
-  public:
-    /*!
-    @brief create a CBOR serialization of a given JSON value
-
-    Serializes a given JSON value @a j to a byte vector using the CBOR (Concise
-    Binary Object Representation) serialization format. CBOR is a binary
-    serialization format which aims to be more compact than JSON itself, yet
-    more efficient to parse.
-
-    The library uses the following mapping from JSON values types to
-    CBOR types according to the CBOR specification (RFC 7049):
-
-    JSON value type | value/range                                | CBOR type                          | first byte
-    --------------- | ------------------------------------------ | ---------------------------------- | ---------------
-    null            | `null`                                     | Null                               | 0xF6
-    boolean         | `true`                                     | True                               | 0xF5
-    boolean         | `false`                                    | False                              | 0xF4
-    number_integer  | -9223372036854775808..-2147483649          | Negative integer (8 bytes follow)  | 0x3B
-    number_integer  | -2147483648..-32769                        | Negative integer (4 bytes follow)  | 0x3A
-    number_integer  | -32768..-129                               | Negative integer (2 bytes follow)  | 0x39
-    number_integer  | -128..-25                                  | Negative integer (1 byte follow)   | 0x38
-    number_integer  | -24..-1                                    | Negative integer                   | 0x20..0x37
-    number_integer  | 0..23                                      | Integer                            | 0x00..0x17
-    number_integer  | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18
-    number_integer  | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19
-    number_integer  | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A
-    number_integer  | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B
-    number_unsigned | 0..23                                      | Integer                            | 0x00..0x17
-    number_unsigned | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18
-    number_unsigned | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19
-    number_unsigned | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A
-    number_unsigned | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B
-    number_float    | *any value*                                | Double-Precision Float             | 0xFB
-    string          | *length*: 0..23                            | UTF-8 string                       | 0x60..0x77
-    string          | *length*: 23..255                          | UTF-8 string (1 byte follow)       | 0x78
-    string          | *length*: 256..65535                       | UTF-8 string (2 bytes follow)      | 0x79
-    string          | *length*: 65536..4294967295                | UTF-8 string (4 bytes follow)      | 0x7A
-    string          | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow)      | 0x7B
-    array           | *size*: 0..23                              | array                              | 0x80..0x97
-    array           | *size*: 23..255                            | array (1 byte follow)              | 0x98
-    array           | *size*: 256..65535                         | array (2 bytes follow)             | 0x99
-    array           | *size*: 65536..4294967295                  | array (4 bytes follow)             | 0x9A
-    array           | *size*: 4294967296..18446744073709551615   | array (8 bytes follow)             | 0x9B
-    object          | *size*: 0..23                              | map                                | 0xA0..0xB7
-    object          | *size*: 23..255                            | map (1 byte follow)                | 0xB8
-    object          | *size*: 256..65535                         | map (2 bytes follow)               | 0xB9
-    object          | *size*: 65536..4294967295                  | map (4 bytes follow)               | 0xBA
-    object          | *size*: 4294967296..18446744073709551615   | map (8 bytes follow)               | 0xBB
-
-    @note The mapping is **complete** in the sense that any JSON value type
-          can be converted to a CBOR value.
-
-    @note If NaN or Infinity are stored inside a JSON number, they are
-          serialized properly. This behavior differs from the @ref dump()
-          function which serializes NaN or Infinity to `null`.
-
-    @note The following CBOR types are not used in the conversion:
-          - byte strings (0x40..0x5F)
-          - UTF-8 strings terminated by "break" (0x7F)
-          - arrays terminated by "break" (0x9F)
-          - maps terminated by "break" (0xBF)
-          - date/time (0xC0..0xC1)
-          - bignum (0xC2..0xC3)
-          - decimal fraction (0xC4)
-          - bigfloat (0xC5)
-          - tagged items (0xC6..0xD4, 0xD8..0xDB)
-          - expected conversions (0xD5..0xD7)
-          - simple values (0xE0..0xF3, 0xF8)
-          - undefined (0xF7)
-          - half and single-precision floats (0xF9-0xFA)
-          - break (0xFF)
-
-    @param[in] j  JSON value to serialize
-    @return MessagePack serialization as byte vector
-
-    @complexity Linear in the size of the JSON value @a j.
-
-    @liveexample{The example shows the serialization of a JSON value to a byte
-    vector in CBOR format.,to_cbor}
-
-    @sa http://cbor.io
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
-        analogous deserialization
-    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
-    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
-             related UBJSON format
-
-    @since version 2.0.9
-    */
-    static std::vector<uint8_t> to_cbor(const basic_json& j)
-    {
-        std::vector<uint8_t> result;
-        to_cbor(j, result);
-        return result;
-    }
-
-    static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o)
-    {
-        binary_writer<uint8_t>(o).write_cbor(j);
-    }
-
-    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)
-    {
-        binary_writer<char>(o).write_cbor(j);
-    }
-
-    /*!
-    @brief create a MessagePack serialization of a given JSON value
-
-    Serializes a given JSON value @a j to a byte vector using the MessagePack
-    serialization format. MessagePack is a binary serialization format which
-    aims to be more compact than JSON itself, yet more efficient to parse.
-
-    The library uses the following mapping from JSON values types to
-    MessagePack types according to the MessagePack specification:
-
-    JSON value type | value/range                       | MessagePack type | first byte
-    --------------- | --------------------------------- | ---------------- | ----------
-    null            | `null`                            | nil              | 0xC0
-    boolean         | `true`                            | true             | 0xC3
-    boolean         | `false`                           | false            | 0xC2
-    number_integer  | -9223372036854775808..-2147483649 | int64            | 0xD3
-    number_integer  | -2147483648..-32769               | int32            | 0xD2
-    number_integer  | -32768..-129                      | int16            | 0xD1
-    number_integer  | -128..-33                         | int8             | 0xD0
-    number_integer  | -32..-1                           | negative fixint  | 0xE0..0xFF
-    number_integer  | 0..127                            | positive fixint  | 0x00..0x7F
-    number_integer  | 128..255                          | uint 8           | 0xCC
-    number_integer  | 256..65535                        | uint 16          | 0xCD
-    number_integer  | 65536..4294967295                 | uint 32          | 0xCE
-    number_integer  | 4294967296..18446744073709551615  | uint 64          | 0xCF
-    number_unsigned | 0..127                            | positive fixint  | 0x00..0x7F
-    number_unsigned | 128..255                          | uint 8           | 0xCC
-    number_unsigned | 256..65535                        | uint 16          | 0xCD
-    number_unsigned | 65536..4294967295                 | uint 32          | 0xCE
-    number_unsigned | 4294967296..18446744073709551615  | uint 64          | 0xCF
-    number_float    | *any value*                       | float 64         | 0xCB
-    string          | *length*: 0..31                   | fixstr           | 0xA0..0xBF
-    string          | *length*: 32..255                 | str 8            | 0xD9
-    string          | *length*: 256..65535              | str 16           | 0xDA
-    string          | *length*: 65536..4294967295       | str 32           | 0xDB
-    array           | *size*: 0..15                     | fixarray         | 0x90..0x9F
-    array           | *size*: 16..65535                 | array 16         | 0xDC
-    array           | *size*: 65536..4294967295         | array 32         | 0xDD
-    object          | *size*: 0..15                     | fix map          | 0x80..0x8F
-    object          | *size*: 16..65535                 | map 16           | 0xDE
-    object          | *size*: 65536..4294967295         | map 32           | 0xDF
-
-    @note The mapping is **complete** in the sense that any JSON value type
-          can be converted to a MessagePack value.
-
-    @note The following values can **not** be converted to a MessagePack value:
-          - strings with more than 4294967295 bytes
-          - arrays with more than 4294967295 elements
-          - objects with more than 4294967295 elements
-
-    @note The following MessagePack types are not used in the conversion:
-          - bin 8 - bin 32 (0xC4..0xC6)
-          - ext 8 - ext 32 (0xC7..0xC9)
-          - float 32 (0xCA)
-          - fixext 1 - fixext 16 (0xD4..0xD8)
-
-    @note Any MessagePack output created @ref to_msgpack can be successfully
-          parsed by @ref from_msgpack.
-
-    @note If NaN or Infinity are stored inside a JSON number, they are
-          serialized properly. This behavior differs from the @ref dump()
-          function which serializes NaN or Infinity to `null`.
-
-    @param[in] j  JSON value to serialize
-    @return MessagePack serialization as byte vector
-
-    @complexity Linear in the size of the JSON value @a j.
-
-    @liveexample{The example shows the serialization of a JSON value to a byte
-    vector in MessagePack format.,to_msgpack}
-
-    @sa http://msgpack.org
-    @sa @ref from_msgpack for the analogous deserialization
-    @sa @ref to_cbor(const basic_json& for the related CBOR format
-    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
-             related UBJSON format
-
-    @since version 2.0.9
-    */
-    static std::vector<uint8_t> to_msgpack(const basic_json& j)
-    {
-        std::vector<uint8_t> result;
-        to_msgpack(j, result);
-        return result;
-    }
-
-    static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o)
-    {
-        binary_writer<uint8_t>(o).write_msgpack(j);
-    }
-
-    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)
-    {
-        binary_writer<char>(o).write_msgpack(j);
-    }
-
-    /*!
-    @brief create a UBJSON serialization of a given JSON value
-
-    Serializes a given JSON value @a j to a byte vector using the UBJSON
-    (Universal Binary JSON) serialization format. UBJSON aims to be more compact
-    than JSON itself, yet more efficient to parse.
-
-    The library uses the following mapping from JSON values types to
-    UBJSON types according to the UBJSON specification:
-
-    JSON value type | value/range                       | UBJSON type | marker
-    --------------- | --------------------------------- | ----------- | ------
-    null            | `null`                            | null        | `Z`
-    boolean         | `true`                            | true        | `T`
-    boolean         | `false`                           | false       | `F`
-    number_integer  | -9223372036854775808..-2147483649 | int64       | `L`
-    number_integer  | -2147483648..-32769               | int32       | `l`
-    number_integer  | -32768..-129                      | int16       | `I`
-    number_integer  | -128..127                         | int8        | `i`
-    number_integer  | 128..255                          | uint8       | `U`
-    number_integer  | 256..32767                        | int16       | `I`
-    number_integer  | 32768..2147483647                 | int32       | `l`
-    number_integer  | 2147483648..9223372036854775807   | int64       | `L`
-    number_unsigned | 0..127                            | int8        | `i`
-    number_unsigned | 128..255                          | uint8       | `U`
-    number_unsigned | 256..32767                        | int16       | `I`
-    number_unsigned | 32768..2147483647                 | int32       | `l`
-    number_unsigned | 2147483648..9223372036854775807   | int64       | `L`
-    number_float    | *any value*                       | float64     | `D`
-    string          | *with shortest length indicator*  | string      | `S`
-    array           | *see notes on optimized format*   | array       | `[`
-    object          | *see notes on optimized format*   | map         | `{`
-
-    @note The mapping is **complete** in the sense that any JSON value type
-          can be converted to a UBJSON value.
-
-    @note The following values can **not** be converted to a UBJSON value:
-          - strings with more than 9223372036854775807 bytes (theoretical)
-          - unsigned integer numbers above 9223372036854775807
-
-    @note The following markers are not used in the conversion:
-          - `Z`: no-op values are not created.
-          - `C`: single-byte strings are serialized with `S` markers.
-
-    @note Any UBJSON output created @ref to_ubjson can be successfully parsed
-          by @ref from_ubjson.
-
-    @note If NaN or Infinity are stored inside a JSON number, they are
-          serialized properly. This behavior differs from the @ref dump()
-          function which serializes NaN or Infinity to `null`.
-
-    @note The optimized formats for containers are supported: Parameter
-          @a use_size adds size information to the beginning of a container and
-          removes the closing marker. Parameter @a use_type further checks
-          whether all elements of a container have the same type and adds the
-          type marker to the beginning of the container. The @a use_type
-          parameter must only be used together with @a use_size = true. Note
-          that @a use_size = true alone may result in larger representations -
-          the benefit of this parameter is that the receiving side is
-          immediately informed on the number of elements of the container.
-
-    @param[in] j  JSON value to serialize
-    @param[in] use_size  whether to add size annotations to container types
-    @param[in] use_type  whether to add type annotations to container types
-                         (must be combined with @a use_size = true)
-    @return UBJSON serialization as byte vector
-
-    @complexity Linear in the size of the JSON value @a j.
-
-    @liveexample{The example shows the serialization of a JSON value to a byte
-    vector in UBJSON format.,to_ubjson}
-
-    @sa http://ubjson.org
-    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the
-        analogous deserialization
-    @sa @ref to_cbor(const basic_json& for the related CBOR format
-    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
-
-    @since version 3.1.0
-    */
-    static std::vector<uint8_t> to_ubjson(const basic_json& j,
-                                          const bool use_size = false,
-                                          const bool use_type = false)
-    {
-        std::vector<uint8_t> result;
-        to_ubjson(j, result, use_size, use_type);
-        return result;
-    }
-
-    static void to_ubjson(const basic_json& j, detail::output_adapter<uint8_t> o,
-                          const bool use_size = false, const bool use_type = false)
-    {
-        binary_writer<uint8_t>(o).write_ubjson(j, use_size, use_type);
-    }
-
-    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
-                          const bool use_size = false, const bool use_type = false)
-    {
-        binary_writer<char>(o).write_ubjson(j, use_size, use_type);
-    }
-
-
-    /*!
-    @brief Serializes the given JSON object `j` to BSON and returns a vector
-           containing the corresponding BSON-representation.
-
-    BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are
-    stored as a single entity (a so-called document).
-
-    The library uses the following mapping from JSON values types to BSON types:
-
-    JSON value type | value/range                       | BSON type   | marker
-    --------------- | --------------------------------- | ----------- | ------
-    null            | `null`                            | null        | 0x0A
-    boolean         | `true`, `false`                   | boolean     | 0x08
-    number_integer  | -9223372036854775808..-2147483649 | int64       | 0x12
-    number_integer  | -2147483648..2147483647           | int32       | 0x10
-    number_integer  | 2147483648..9223372036854775807   | int64       | 0x12
-    number_unsigned | 0..2147483647                     | int32       | 0x10
-    number_unsigned | 2147483648..9223372036854775807   | int64       | 0x12
-    number_unsigned | 9223372036854775808..18446744073709551615| --   | --
-    number_float    | *any value*                       | double      | 0x01
-    string          | *any value*                       | string      | 0x02
-    array           | *any value*                       | document    | 0x04
-    object          | *any value*                       | document    | 0x03
-
-    @warning The mapping is **incomplete**, since only JSON-objects (and things
-    contained therein) can be serialized to BSON.
-    Also, integers larger than 9223372036854775807 cannot be serialized to BSON,
-    and the keys may not contain U+0000, since they are serialized a
-    zero-terminated c-strings.
-
-    @throw out_of_range.407  if `j.is_number_unsigned() && j.get<std::uint64_t>() > 9223372036854775807`
-    @throw out_of_range.409  if a key in `j` contains a NULL (U+0000)
-    @throw type_error.317    if `!j.is_object()`
-
-    @pre The input `j` is required to be an object: `j.is_object() == true`.
-
-    @note Any BSON output created via @ref to_bson can be successfully parsed
-          by @ref from_bson.
-
-    @param[in] j  JSON value to serialize
-    @return BSON serialization as byte vector
-
-    @complexity Linear in the size of the JSON value @a j.
-
-    @liveexample{The example shows the serialization of a JSON value to a byte
-    vector in BSON format.,to_bson}
-
-    @sa http://bsonspec.org/spec.html
-    @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the
-        analogous deserialization
-    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
-             related UBJSON format
-    @sa @ref to_cbor(const basic_json&) for the related CBOR format
-    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
-    */
-    static std::vector<uint8_t> to_bson(const basic_json& j)
-    {
-        std::vector<uint8_t> result;
-        to_bson(j, result);
-        return result;
-    }
-
-    /*!
-    @brief Serializes the given JSON object `j` to BSON and forwards the
-           corresponding BSON-representation to the given output_adapter `o`.
-    @param j The JSON object to convert to BSON.
-    @param o The output adapter that receives the binary BSON representation.
-    @pre The input `j` shall be an object: `j.is_object() == true`
-    @sa @ref to_bson(const basic_json&)
-    */
-    static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)
-    {
-        binary_writer<uint8_t>(o).write_bson(j);
-    }
-
-    /*!
-    @copydoc to_bson(const basic_json&, detail::output_adapter<uint8_t>)
-    */
-    static void to_bson(const basic_json& j, detail::output_adapter<char> o)
-    {
-        binary_writer<char>(o).write_bson(j);
-    }
-
-
-    /*!
-    @brief create a JSON value from an input in CBOR format
-
-    Deserializes a given input @a i to a JSON value using the CBOR (Concise
-    Binary Object Representation) serialization format.
-
-    The library maps CBOR types to JSON value types as follows:
-
-    CBOR type              | JSON value type | first byte
-    ---------------------- | --------------- | ----------
-    Integer                | number_unsigned | 0x00..0x17
-    Unsigned integer       | number_unsigned | 0x18
-    Unsigned integer       | number_unsigned | 0x19
-    Unsigned integer       | number_unsigned | 0x1A
-    Unsigned integer       | number_unsigned | 0x1B
-    Negative integer       | number_integer  | 0x20..0x37
-    Negative integer       | number_integer  | 0x38
-    Negative integer       | number_integer  | 0x39
-    Negative integer       | number_integer  | 0x3A
-    Negative integer       | number_integer  | 0x3B
-    Negative integer       | number_integer  | 0x40..0x57
-    UTF-8 string           | string          | 0x60..0x77
-    UTF-8 string           | string          | 0x78
-    UTF-8 string           | string          | 0x79
-    UTF-8 string           | string          | 0x7A
-    UTF-8 string           | string          | 0x7B
-    UTF-8 string           | string          | 0x7F
-    array                  | array           | 0x80..0x97
-    array                  | array           | 0x98
-    array                  | array           | 0x99
-    array                  | array           | 0x9A
-    array                  | array           | 0x9B
-    array                  | array           | 0x9F
-    map                    | object          | 0xA0..0xB7
-    map                    | object          | 0xB8
-    map                    | object          | 0xB9
-    map                    | object          | 0xBA
-    map                    | object          | 0xBB
-    map                    | object          | 0xBF
-    False                  | `false`         | 0xF4
-    True                   | `true`          | 0xF5
-    Null                   | `null`          | 0xF6
-    Half-Precision Float   | number_float    | 0xF9
-    Single-Precision Float | number_float    | 0xFA
-    Double-Precision Float | number_float    | 0xFB
-
-    @warning The mapping is **incomplete** in the sense that not all CBOR
-             types can be converted to a JSON value. The following CBOR types
-             are not supported and will yield parse errors (parse_error.112):
-             - byte strings (0x40..0x5F)
-             - date/time (0xC0..0xC1)
-             - bignum (0xC2..0xC3)
-             - decimal fraction (0xC4)
-             - bigfloat (0xC5)
-             - tagged items (0xC6..0xD4, 0xD8..0xDB)
-             - expected conversions (0xD5..0xD7)
-             - simple values (0xE0..0xF3, 0xF8)
-             - undefined (0xF7)
-
-    @warning CBOR allows map keys of any type, whereas JSON only allows
-             strings as keys in object values. Therefore, CBOR maps with keys
-             other than UTF-8 strings are rejected (parse_error.113).
-
-    @note Any CBOR output created @ref to_cbor can be successfully parsed by
-          @ref from_cbor.
-
-    @param[in] i  an input in CBOR format convertible to an input adapter
-    @param[in] strict  whether to expect the input to be consumed until EOF
-                       (true by default)
-    @param[in] allow_exceptions  whether to throw exceptions in case of a
-    parse error (optional, true by default)
-
-    @return deserialized JSON value
-
-    @throw parse_error.110 if the given input ends prematurely or the end of
-    file was not reached when @a strict was set to true
-    @throw parse_error.112 if unsupported features from CBOR were
-    used in the given input @a v or if the input is not valid CBOR
-    @throw parse_error.113 if a string was expected as map key, but not found
-
-    @complexity Linear in the size of the input @a i.
-
-    @liveexample{The example shows the deserialization of a byte vector in CBOR
-    format to a JSON value.,from_cbor}
-
-    @sa http://cbor.io
-    @sa @ref to_cbor(const basic_json&) for the analogous serialization
-    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the
-        related MessagePack format
-    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the
-        related UBJSON format
-
-    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
-           consume input adapters, removed start_index parameter, and added
-           @a strict parameter since 3.0.0; added @a allow_exceptions parameter
-           since 3.2.0
-    */
-    static basic_json from_cbor(detail::input_adapter&& i,
-                                const bool strict = true,
-                                const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::cbor, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-    /*!
-    @copydoc from_cbor(detail::input_adapter&&, const bool, const bool)
-    */
-    template<typename A1, typename A2,
-             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
-    static basic_json from_cbor(A1 && a1, A2 && a2,
-                                const bool strict = true,
-                                const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::cbor, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-    /*!
-    @brief create a JSON value from an input in MessagePack format
-
-    Deserializes a given input @a i to a JSON value using the MessagePack
-    serialization format.
-
-    The library maps MessagePack types to JSON value types as follows:
-
-    MessagePack type | JSON value type | first byte
-    ---------------- | --------------- | ----------
-    positive fixint  | number_unsigned | 0x00..0x7F
-    fixmap           | object          | 0x80..0x8F
-    fixarray         | array           | 0x90..0x9F
-    fixstr           | string          | 0xA0..0xBF
-    nil              | `null`          | 0xC0
-    false            | `false`         | 0xC2
-    true             | `true`          | 0xC3
-    float 32         | number_float    | 0xCA
-    float 64         | number_float    | 0xCB
-    uint 8           | number_unsigned | 0xCC
-    uint 16          | number_unsigned | 0xCD
-    uint 32          | number_unsigned | 0xCE
-    uint 64          | number_unsigned | 0xCF
-    int 8            | number_integer  | 0xD0
-    int 16           | number_integer  | 0xD1
-    int 32           | number_integer  | 0xD2
-    int 64           | number_integer  | 0xD3
-    str 8            | string          | 0xD9
-    str 16           | string          | 0xDA
-    str 32           | string          | 0xDB
-    array 16         | array           | 0xDC
-    array 32         | array           | 0xDD
-    map 16           | object          | 0xDE
-    map 32           | object          | 0xDF
-    negative fixint  | number_integer  | 0xE0-0xFF
-
-    @warning The mapping is **incomplete** in the sense that not all
-             MessagePack types can be converted to a JSON value. The following
-             MessagePack types are not supported and will yield parse errors:
-              - bin 8 - bin 32 (0xC4..0xC6)
-              - ext 8 - ext 32 (0xC7..0xC9)
-              - fixext 1 - fixext 16 (0xD4..0xD8)
-
-    @note Any MessagePack output created @ref to_msgpack can be successfully
-          parsed by @ref from_msgpack.
-
-    @param[in] i  an input in MessagePack format convertible to an input
-                  adapter
-    @param[in] strict  whether to expect the input to be consumed until EOF
-                       (true by default)
-    @param[in] allow_exceptions  whether to throw exceptions in case of a
-    parse error (optional, true by default)
-
-    @return deserialized JSON value
-
-    @throw parse_error.110 if the given input ends prematurely or the end of
-    file was not reached when @a strict was set to true
-    @throw parse_error.112 if unsupported features from MessagePack were
-    used in the given input @a i or if the input is not valid MessagePack
-    @throw parse_error.113 if a string was expected as map key, but not found
-
-    @complexity Linear in the size of the input @a i.
-
-    @liveexample{The example shows the deserialization of a byte vector in
-    MessagePack format to a JSON value.,from_msgpack}
-
-    @sa http://msgpack.org
-    @sa @ref to_msgpack(const basic_json&) for the analogous serialization
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
-        related CBOR format
-    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for
-        the related UBJSON format
-    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for
-        the related BSON format
-
-    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
-           consume input adapters, removed start_index parameter, and added
-           @a strict parameter since 3.0.0; added @a allow_exceptions parameter
-           since 3.2.0
-    */
-    static basic_json from_msgpack(detail::input_adapter&& i,
-                                   const bool strict = true,
-                                   const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::msgpack, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-    /*!
-    @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool)
-    */
-    template<typename A1, typename A2,
-             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
-    static basic_json from_msgpack(A1 && a1, A2 && a2,
-                                   const bool strict = true,
-                                   const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::msgpack, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-    /*!
-    @brief create a JSON value from an input in UBJSON format
-
-    Deserializes a given input @a i to a JSON value using the UBJSON (Universal
-    Binary JSON) serialization format.
-
-    The library maps UBJSON types to JSON value types as follows:
-
-    UBJSON type | JSON value type                         | marker
-    ----------- | --------------------------------------- | ------
-    no-op       | *no value, next value is read*          | `N`
-    null        | `null`                                  | `Z`
-    false       | `false`                                 | `F`
-    true        | `true`                                  | `T`
-    float32     | number_float                            | `d`
-    float64     | number_float                            | `D`
-    uint8       | number_unsigned                         | `U`
-    int8        | number_integer                          | `i`
-    int16       | number_integer                          | `I`
-    int32       | number_integer                          | `l`
-    int64       | number_integer                          | `L`
-    string      | string                                  | `S`
-    char        | string                                  | `C`
-    array       | array (optimized values are supported)  | `[`
-    object      | object (optimized values are supported) | `{`
-
-    @note The mapping is **complete** in the sense that any UBJSON value can
-          be converted to a JSON value.
-
-    @param[in] i  an input in UBJSON format convertible to an input adapter
-    @param[in] strict  whether to expect the input to be consumed until EOF
-                       (true by default)
-    @param[in] allow_exceptions  whether to throw exceptions in case of a
-    parse error (optional, true by default)
-
-    @return deserialized JSON value
-
-    @throw parse_error.110 if the given input ends prematurely or the end of
-    file was not reached when @a strict was set to true
-    @throw parse_error.112 if a parse error occurs
-    @throw parse_error.113 if a string could not be parsed successfully
-
-    @complexity Linear in the size of the input @a i.
-
-    @liveexample{The example shows the deserialization of a byte vector in
-    UBJSON format to a JSON value.,from_ubjson}
-
-    @sa http://ubjson.org
-    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
-             analogous serialization
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
-        related CBOR format
-    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for
-        the related MessagePack format
-    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for
-        the related BSON format
-
-    @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0
-    */
-    static basic_json from_ubjson(detail::input_adapter&& i,
-                                  const bool strict = true,
-                                  const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::ubjson, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-    /*!
-    @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool)
-    */
-    template<typename A1, typename A2,
-             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
-    static basic_json from_ubjson(A1 && a1, A2 && a2,
-                                  const bool strict = true,
-                                  const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::ubjson, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-    /*!
-    @brief Create a JSON value from an input in BSON format
-
-    Deserializes a given input @a i to a JSON value using the BSON (Binary JSON)
-    serialization format.
-
-    The library maps BSON record types to JSON value types as follows:
-
-    BSON type       | BSON marker byte | JSON value type
-    --------------- | ---------------- | ---------------------------
-    double          | 0x01             | number_float
-    string          | 0x02             | string
-    document        | 0x03             | object
-    array           | 0x04             | array
-    binary          | 0x05             | still unsupported
-    undefined       | 0x06             | still unsupported
-    ObjectId        | 0x07             | still unsupported
-    boolean         | 0x08             | boolean
-    UTC Date-Time   | 0x09             | still unsupported
-    null            | 0x0A             | null
-    Regular Expr.   | 0x0B             | still unsupported
-    DB Pointer      | 0x0C             | still unsupported
-    JavaScript Code | 0x0D             | still unsupported
-    Symbol          | 0x0E             | still unsupported
-    JavaScript Code | 0x0F             | still unsupported
-    int32           | 0x10             | number_integer
-    Timestamp       | 0x11             | still unsupported
-    128-bit decimal float | 0x13       | still unsupported
-    Max Key         | 0x7F             | still unsupported
-    Min Key         | 0xFF             | still unsupported
-
-    @warning The mapping is **incomplete**. The unsupported mappings
-             are indicated in the table above.
-
-    @param[in] i  an input in BSON format convertible to an input adapter
-    @param[in] strict  whether to expect the input to be consumed until EOF
-                       (true by default)
-    @param[in] allow_exceptions  whether to throw exceptions in case of a
-    parse error (optional, true by default)
-
-    @return deserialized JSON value
-
-    @throw parse_error.114 if an unsupported BSON record type is encountered
-
-    @complexity Linear in the size of the input @a i.
-
-    @liveexample{The example shows the deserialization of a byte vector in
-    BSON format to a JSON value.,from_bson}
-
-    @sa http://bsonspec.org/spec.html
-    @sa @ref to_bson(const basic_json&) for the analogous serialization
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
-        related CBOR format
-    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for
-        the related MessagePack format
-    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the
-        related UBJSON format
-    */
-    static basic_json from_bson(detail::input_adapter&& i,
-                                const bool strict = true,
-                                const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::bson, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-    /*!
-    @copydoc from_bson(detail::input_adapter&&, const bool, const bool)
-    */
-    template<typename A1, typename A2,
-             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
-    static basic_json from_bson(A1 && a1, A2 && a2,
-                                const bool strict = true,
-                                const bool allow_exceptions = true)
-    {
-        basic_json result;
-        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
-        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::bson, &sdp, strict);
-        return res ? result : basic_json(value_t::discarded);
-    }
-
-
-
-    /// @}
-
-    //////////////////////////
-    // JSON Pointer support //
-    //////////////////////////
-
-    /// @name JSON Pointer functions
-    /// @{
-
-    /*!
-    @brief access specified element via JSON Pointer
-
-    Uses a JSON pointer to retrieve a reference to the respective JSON value.
-    No bound checking is performed. Similar to @ref operator[](const typename
-    object_t::key_type&), `null` values are created in arrays and objects if
-    necessary.
-
-    In particular:
-    - If the JSON pointer points to an object key that does not exist, it
-      is created an filled with a `null` value before a reference to it
-      is returned.
-    - If the JSON pointer points to an array index that does not exist, it
-      is created an filled with a `null` value before a reference to it
-      is returned. All indices between the current maximum and the given
-      index are also filled with `null`.
-    - The special value `-` is treated as a synonym for the index past the
-      end.
-
-    @param[in] ptr  a JSON pointer
-
-    @return reference to the element pointed to by @a ptr
-
-    @complexity Constant.
-
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-
-    @liveexample{The behavior is shown in the example.,operatorjson_pointer}
-
-    @since version 2.0.0
-    */
-    reference operator[](const json_pointer& ptr)
-    {
-        return ptr.get_unchecked(this);
-    }
-
-    /*!
-    @brief access specified element via JSON Pointer
-
-    Uses a JSON pointer to retrieve a reference to the respective JSON value.
-    No bound checking is performed. The function does not change the JSON
-    value; no `null` values are created. In particular, the the special value
-    `-` yields an exception.
-
-    @param[in] ptr  JSON pointer to the desired element
-
-    @return const reference to the element pointed to by @a ptr
-
-    @complexity Constant.
-
-    @throw parse_error.106   if an array index begins with '0'
-    @throw parse_error.109   if an array index was not a number
-    @throw out_of_range.402  if the array index '-' is used
-    @throw out_of_range.404  if the JSON pointer can not be resolved
-
-    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}
-
-    @since version 2.0.0
-    */
-    const_reference operator[](const json_pointer& ptr) const
-    {
-        return ptr.get_unchecked(this);
-    }
-
-    /*!
-    @brief access specified element via JSON Pointer
-
-    Returns a reference to the element at with specified JSON pointer @a ptr,
-    with bounds checking.
-
-    @param[in] ptr  JSON pointer to the desired element
-
-    @return reference to the element pointed to by @a ptr
-
-    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr
-    begins with '0'. See example below.
-
-    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr
-    is not a number. See example below.
-
-    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr
-    is out of range. See example below.
-
-    @throw out_of_range.402 if the array index '-' is used in the passed JSON
-    pointer @a ptr. As `at` provides checked access (and no elements are
-    implicitly inserted), the index '-' is always invalid. See example below.
-
-    @throw out_of_range.403 if the JSON pointer describes a key of an object
-    which cannot be found. See example below.
-
-    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.
-    See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 2.0.0
-
-    @liveexample{The behavior is shown in the example.,at_json_pointer}
-    */
-    reference at(const json_pointer& ptr)
-    {
-        return ptr.get_checked(this);
-    }
-
-    /*!
-    @brief access specified element via JSON Pointer
-
-    Returns a const reference to the element at with specified JSON pointer @a
-    ptr, with bounds checking.
-
-    @param[in] ptr  JSON pointer to the desired element
-
-    @return reference to the element pointed to by @a ptr
-
-    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr
-    begins with '0'. See example below.
-
-    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr
-    is not a number. See example below.
-
-    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr
-    is out of range. See example below.
-
-    @throw out_of_range.402 if the array index '-' is used in the passed JSON
-    pointer @a ptr. As `at` provides checked access (and no elements are
-    implicitly inserted), the index '-' is always invalid. See example below.
-
-    @throw out_of_range.403 if the JSON pointer describes a key of an object
-    which cannot be found. See example below.
-
-    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.
-    See example below.
-
-    @exceptionsafety Strong guarantee: if an exception is thrown, there are no
-    changes in the JSON value.
-
-    @complexity Constant.
-
-    @since version 2.0.0
-
-    @liveexample{The behavior is shown in the example.,at_json_pointer_const}
-    */
-    const_reference at(const json_pointer& ptr) const
-    {
-        return ptr.get_checked(this);
-    }
-
-    /*!
-    @brief return flattened JSON value
-
-    The function creates a JSON object whose keys are JSON pointers (see [RFC
-    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all
-    primitive. The original JSON value can be restored using the @ref
-    unflatten() function.
-
-    @return an object that maps JSON pointers to primitive values
-
-    @note Empty objects and arrays are flattened to `null` and will not be
-          reconstructed correctly by the @ref unflatten() function.
-
-    @complexity Linear in the size the JSON value.
-
-    @liveexample{The following code shows how a JSON object is flattened to an
-    object whose keys consist of JSON pointers.,flatten}
-
-    @sa @ref unflatten() for the reverse function
-
-    @since version 2.0.0
-    */
-    basic_json flatten() const
-    {
-        basic_json result(value_t::object);
-        json_pointer::flatten("", *this, result);
-        return result;
-    }
-
-    /*!
-    @brief unflatten a previously flattened JSON value
-
-    The function restores the arbitrary nesting of a JSON value that has been
-    flattened before using the @ref flatten() function. The JSON value must
-    meet certain constraints:
-    1. The value must be an object.
-    2. The keys must be JSON pointers (see
-       [RFC 6901](https://tools.ietf.org/html/rfc6901))
-    3. The mapped values must be primitive JSON types.
-
-    @return the original JSON from a flattened version
-
-    @note Empty objects and arrays are flattened by @ref flatten() to `null`
-          values and can not unflattened to their original type. Apart from
-          this example, for a JSON value `j`, the following is always true:
-          `j == j.flatten().unflatten()`.
-
-    @complexity Linear in the size the JSON value.
-
-    @throw type_error.314  if value is not an object
-    @throw type_error.315  if object values are not primitive
-
-    @liveexample{The following code shows how a flattened JSON object is
-    unflattened into the original nested JSON object.,unflatten}
-
-    @sa @ref flatten() for the reverse function
-
-    @since version 2.0.0
-    */
-    basic_json unflatten() const
-    {
-        return json_pointer::unflatten(*this);
-    }
-
-    /// @}
-
-    //////////////////////////
-    // JSON Patch functions //
-    //////////////////////////
-
-    /// @name JSON Patch functions
-    /// @{
-
-    /*!
-    @brief applies a JSON patch
-
-    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for
-    expressing a sequence of operations to apply to a JSON) document. With
-    this function, a JSON Patch is applied to the current JSON value by
-    executing all operations from the patch.
-
-    @param[in] json_patch  JSON patch document
-    @return patched document
-
-    @note The application of a patch is atomic: Either all operations succeed
-          and the patched document is returned or an exception is thrown. In
-          any case, the original value is not changed: the patch is applied
-          to a copy of the value.
-
-    @throw parse_error.104 if the JSON patch does not consist of an array of
-    objects
-
-    @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory
-    attributes are missing); example: `"operation add must have member path"`
-
-    @throw out_of_range.401 if an array index is out of range.
-
-    @throw out_of_range.403 if a JSON pointer inside the patch could not be
-    resolved successfully in the current JSON value; example: `"key baz not
-    found"`
-
-    @throw out_of_range.405 if JSON pointer has no parent ("add", "remove",
-    "move")
-
-    @throw other_error.501 if "test" operation was unsuccessful
-
-    @complexity Linear in the size of the JSON value and the length of the
-    JSON patch. As usually only a fraction of the JSON value is affected by
-    the patch, the complexity can usually be neglected.
-
-    @liveexample{The following code shows how a JSON patch is applied to a
-    value.,patch}
-
-    @sa @ref diff -- create a JSON patch by comparing two JSON values
-
-    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
-    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)
-
-    @since version 2.0.0
-    */
-    basic_json patch(const basic_json& json_patch) const
-    {
-        // make a working copy to apply the patch to
-        basic_json result = *this;
-
-        // the valid JSON Patch operations
-        enum class patch_operations {add, remove, replace, move, copy, test, invalid};
-
-        const auto get_op = [](const std::string & op)
-        {
-            if (op == "add")
-            {
-                return patch_operations::add;
-            }
-            if (op == "remove")
-            {
-                return patch_operations::remove;
-            }
-            if (op == "replace")
-            {
-                return patch_operations::replace;
-            }
-            if (op == "move")
-            {
-                return patch_operations::move;
-            }
-            if (op == "copy")
-            {
-                return patch_operations::copy;
-            }
-            if (op == "test")
-            {
-                return patch_operations::test;
-            }
-
-            return patch_operations::invalid;
-        };
-
-        // wrapper for "add" operation; add value at ptr
-        const auto operation_add = [&result](json_pointer & ptr, basic_json val)
-        {
-            // adding to the root of the target document means replacing it
-            if (ptr.is_root())
-            {
-                result = val;
-            }
-            else
-            {
-                // make sure the top element of the pointer exists
-                json_pointer top_pointer = ptr.top();
-                if (top_pointer != ptr)
-                {
-                    result.at(top_pointer);
-                }
-
-                // get reference to parent of JSON pointer ptr
-                const auto last_path = ptr.pop_back();
-                basic_json& parent = result[ptr];
-
-                switch (parent.m_type)
-                {
-                    case value_t::null:
-                    case value_t::object:
-                    {
-                        // use operator[] to add value
-                        parent[last_path] = val;
-                        break;
-                    }
-
-                    case value_t::array:
-                    {
-                        if (last_path == "-")
-                        {
-                            // special case: append to back
-                            parent.push_back(val);
-                        }
-                        else
-                        {
-                            const auto idx = json_pointer::array_index(last_path);
-                            if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
-                            {
-                                // avoid undefined behavior
-                                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
-                            }
-
-                            // default case: insert add offset
-                            parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
-                        }
-                        break;
-                    }
-
-                    // LCOV_EXCL_START
-                    default:
-                    {
-                        // if there exists a parent it cannot be primitive
-                        assert(false);
-                    }
-                        // LCOV_EXCL_STOP
-                }
-            }
-        };
-
-        // wrapper for "remove" operation; remove value at ptr
-        const auto operation_remove = [&result](json_pointer & ptr)
-        {
-            // get reference to parent of JSON pointer ptr
-            const auto last_path = ptr.pop_back();
-            basic_json& parent = result.at(ptr);
-
-            // remove child
-            if (parent.is_object())
-            {
-                // perform range check
-                auto it = parent.find(last_path);
-                if (JSON_LIKELY(it != parent.end()))
-                {
-                    parent.erase(it);
-                }
-                else
-                {
-                    JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found"));
-                }
-            }
-            else if (parent.is_array())
-            {
-                // note erase performs range check
-                parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
-            }
-        };
-
-        // type check: top level value must be an array
-        if (JSON_UNLIKELY(not json_patch.is_array()))
-        {
-            JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
-        }
-
-        // iterate and apply the operations
-        for (const auto& val : json_patch)
-        {
-            // wrapper to get a value for an operation
-            const auto get_value = [&val](const std::string & op,
-                                          const std::string & member,
-                                          bool string_type) -> basic_json &
-            {
-                // find value
-                auto it = val.m_value.object->find(member);
-
-                // context-sensitive error message
-                const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
-
-                // check if desired value is present
-                if (JSON_UNLIKELY(it == val.m_value.object->end()))
-                {
-                    JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'"));
-                }
-
-                // check if result is of type string
-                if (JSON_UNLIKELY(string_type and not it->second.is_string()))
-                {
-                    JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'"));
-                }
-
-                // no error: return value
-                return it->second;
-            };
-
-            // type check: every element of the array must be an object
-            if (JSON_UNLIKELY(not val.is_object()))
-            {
-                JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
-            }
-
-            // collect mandatory members
-            const std::string op = get_value("op", "op", true);
-            const std::string path = get_value(op, "path", true);
-            json_pointer ptr(path);
-
-            switch (get_op(op))
-            {
-                case patch_operations::add:
-                {
-                    operation_add(ptr, get_value("add", "value", false));
-                    break;
-                }
-
-                case patch_operations::remove:
-                {
-                    operation_remove(ptr);
-                    break;
-                }
-
-                case patch_operations::replace:
-                {
-                    // the "path" location must exist - use at()
-                    result.at(ptr) = get_value("replace", "value", false);
-                    break;
-                }
-
-                case patch_operations::move:
-                {
-                    const std::string from_path = get_value("move", "from", true);
-                    json_pointer from_ptr(from_path);
-
-                    // the "from" location must exist - use at()
-                    basic_json v = result.at(from_ptr);
-
-                    // The move operation is functionally identical to a
-                    // "remove" operation on the "from" location, followed
-                    // immediately by an "add" operation at the target
-                    // location with the value that was just removed.
-                    operation_remove(from_ptr);
-                    operation_add(ptr, v);
-                    break;
-                }
-
-                case patch_operations::copy:
-                {
-                    const std::string from_path = get_value("copy", "from", true);
-                    const json_pointer from_ptr(from_path);
-
-                    // the "from" location must exist - use at()
-                    basic_json v = result.at(from_ptr);
-
-                    // The copy is functionally identical to an "add"
-                    // operation at the target location using the value
-                    // specified in the "from" member.
-                    operation_add(ptr, v);
-                    break;
-                }
-
-                case patch_operations::test:
-                {
-                    bool success = false;
-                    JSON_TRY
-                    {
-                        // check if "value" matches the one at "path"
-                        // the "path" location must exist - use at()
-                        success = (result.at(ptr) == get_value("test", "value", false));
-                    }
-                    JSON_INTERNAL_CATCH (out_of_range&)
-                    {
-                        // ignore out of range errors: success remains false
-                    }
-
-                    // throw an exception if test fails
-                    if (JSON_UNLIKELY(not success))
-                    {
-                        JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump()));
-                    }
-
-                    break;
-                }
-
-                case patch_operations::invalid:
-                {
-                    // op must be "add", "remove", "replace", "move", "copy", or
-                    // "test"
-                    JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid"));
-                }
-            }
-        }
-
-        return result;
-    }
-
-    /*!
-    @brief creates a diff as a JSON patch
-
-    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can
-    be changed into the value @a target by calling @ref patch function.
-
-    @invariant For two JSON values @a source and @a target, the following code
-    yields always `true`:
-    @code {.cpp}
-    source.patch(diff(source, target)) == target;
-    @endcode
-
-    @note Currently, only `remove`, `add`, and `replace` operations are
-          generated.
-
-    @param[in] source  JSON value to compare from
-    @param[in] target  JSON value to compare against
-    @param[in] path    helper value to create JSON pointers
-
-    @return a JSON patch to convert the @a source to @a target
-
-    @complexity Linear in the lengths of @a source and @a target.
-
-    @liveexample{The following code shows how a JSON patch is created as a
-    diff for two JSON values.,diff}
-
-    @sa @ref patch -- apply a JSON patch
-    @sa @ref merge_patch -- apply a JSON Merge Patch
-
-    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
-
-    @since version 2.0.0
-    */
-    static basic_json diff(const basic_json& source, const basic_json& target,
-                           const std::string& path = "")
-    {
-        // the patch
-        basic_json result(value_t::array);
-
-        // if the values are the same, return empty patch
-        if (source == target)
-        {
-            return result;
-        }
-
-        if (source.type() != target.type())
-        {
-            // different types: replace value
-            result.push_back(
-            {
-                {"op", "replace"}, {"path", path}, {"value", target}
-            });
-        }
-        else
-        {
-            switch (source.type())
-            {
-                case value_t::array:
-                {
-                    // first pass: traverse common elements
-                    std::size_t i = 0;
-                    while (i < source.size() and i < target.size())
-                    {
-                        // recursive call to compare array values at index i
-                        auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i));
-                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());
-                        ++i;
-                    }
-
-                    // i now reached the end of at least one array
-                    // in a second pass, traverse the remaining elements
-
-                    // remove my remaining elements
-                    const auto end_index = static_cast<difference_type>(result.size());
-                    while (i < source.size())
-                    {
-                        // add operations in reverse order to avoid invalid
-                        // indices
-                        result.insert(result.begin() + end_index, object(
-                        {
-                            {"op", "remove"},
-                            {"path", path + "/" + std::to_string(i)}
-                        }));
-                        ++i;
-                    }
-
-                    // add other remaining elements
-                    while (i < target.size())
-                    {
-                        result.push_back(
-                        {
-                            {"op", "add"},
-                            {"path", path + "/" + std::to_string(i)},
-                            {"value", target[i]}
-                        });
-                        ++i;
-                    }
-
-                    break;
-                }
-
-                case value_t::object:
-                {
-                    // first pass: traverse this object's elements
-                    for (auto it = source.cbegin(); it != source.cend(); ++it)
-                    {
-                        // escape the key name to be used in a JSON patch
-                        const auto key = json_pointer::escape(it.key());
-
-                        if (target.find(it.key()) != target.end())
-                        {
-                            // recursive call to compare object values at key it
-                            auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key);
-                            result.insert(result.end(), temp_diff.begin(), temp_diff.end());
-                        }
-                        else
-                        {
-                            // found a key that is not in o -> remove it
-                            result.push_back(object(
-                            {
-                                {"op", "remove"}, {"path", path + "/" + key}
-                            }));
-                        }
-                    }
-
-                    // second pass: traverse other object's elements
-                    for (auto it = target.cbegin(); it != target.cend(); ++it)
-                    {
-                        if (source.find(it.key()) == source.end())
-                        {
-                            // found a key that is not in this -> add it
-                            const auto key = json_pointer::escape(it.key());
-                            result.push_back(
-                            {
-                                {"op", "add"}, {"path", path + "/" + key},
-                                {"value", it.value()}
-                            });
-                        }
-                    }
-
-                    break;
-                }
-
-                default:
-                {
-                    // both primitive type: replace value
-                    result.push_back(
-                    {
-                        {"op", "replace"}, {"path", path}, {"value", target}
-                    });
-                    break;
-                }
-            }
-        }
-
-        return result;
-    }
-
-    /// @}
-
-    ////////////////////////////////
-    // JSON Merge Patch functions //
-    ////////////////////////////////
-
-    /// @name JSON Merge Patch functions
-    /// @{
-
-    /*!
-    @brief applies a JSON Merge Patch
-
-    The merge patch format is primarily intended for use with the HTTP PATCH
-    method as a means of describing a set of modifications to a target
-    resource's content. This function applies a merge patch to the current
-    JSON value.
-
-    The function implements the following algorithm from Section 2 of
-    [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):
-
-    ```
-    define MergePatch(Target, Patch):
-      if Patch is an Object:
-        if Target is not an Object:
-          Target = {} // Ignore the contents and set it to an empty Object
-        for each Name/Value pair in Patch:
-          if Value is null:
-            if Name exists in Target:
-              remove the Name/Value pair from Target
-          else:
-            Target[Name] = MergePatch(Target[Name], Value)
-        return Target
-      else:
-        return Patch
-    ```
-
-    Thereby, `Target` is the current object; that is, the patch is applied to
-    the current value.
-
-    @param[in] apply_patch  the patch to apply
-
-    @complexity Linear in the lengths of @a patch.
-
-    @liveexample{The following code shows how a JSON Merge Patch is applied to
-    a JSON document.,merge_patch}
-
-    @sa @ref patch -- apply a JSON patch
-    @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)
-
-    @since version 3.0.0
-    */
-    void merge_patch(const basic_json& apply_patch)
-    {
-        if (apply_patch.is_object())
-        {
-            if (not is_object())
-            {
-                *this = object();
-            }
-            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)
-            {
-                if (it.value().is_null())
-                {
-                    erase(it.key());
-                }
-                else
-                {
-                    operator[](it.key()).merge_patch(it.value());
-                }
-            }
-        }
-        else
-        {
-            *this = apply_patch;
-        }
-    }
-
-    /// @}
-};
-} // namespace nlohmann
-
-///////////////////////
-// nonmember support //
-///////////////////////
-
-// specialization of std::swap, and std::hash
-namespace std
-{
-
-/// hash value for JSON objects
-template<>
-struct hash<nlohmann::json>
-{
-    /*!
-    @brief return a hash value for a JSON object
-
-    @since version 1.0.0
-    */
-    std::size_t operator()(const nlohmann::json& j) const
-    {
-        // a naive hashing via the string representation
-        const auto& h = hash<nlohmann::json::string_t>();
-        return h(j.dump());
-    }
-};
-
-/// specialization for std::less<value_t>
-/// @note: do not remove the space after '<',
-///        see https://github.com/nlohmann/json/pull/679
-template<>
-struct less< ::nlohmann::detail::value_t>
-{
-    /*!
-    @brief compare two value_t enum values
-    @since version 3.0.0
-    */
-    bool operator()(nlohmann::detail::value_t lhs,
-                    nlohmann::detail::value_t rhs) const noexcept
-    {
-        return nlohmann::detail::operator<(lhs, rhs);
-    }
-};
-
-/*!
-@brief exchanges the values of two JSON objects
-
-@since version 1.0.0
-*/
-template<>
-inline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept(
-    is_nothrow_move_constructible<nlohmann::json>::value and
-    is_nothrow_move_assignable<nlohmann::json>::value
-)
-{
-    j1.swap(j2);
-}
-
-} // namespace std
-
-/*!
-@brief user-defined string literal for JSON values
-
-This operator implements a user-defined string literal for JSON objects. It
-can be used by adding `"_json"` to a string literal and returns a JSON object
-if no parse error occurred.
-
-@param[in] s  a string representation of a JSON object
-@param[in] n  the length of string @a s
-@return a JSON object
-
-@since version 1.0.0
-*/
-inline nlohmann::json operator "" _json(const char* s, std::size_t n)
-{
-    return nlohmann::json::parse(s, s + n);
-}
-
-/*!
-@brief user-defined string literal for JSON pointer
-
-This operator implements a user-defined string literal for JSON Pointers. It
-can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer
-object if no parse error occurred.
-
-@param[in] s  a string representation of a JSON Pointer
-@param[in] n  the length of string @a s
-@return a JSON pointer object
-
-@since version 2.0.0
-*/
-inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
-{
-    return nlohmann::json::json_pointer(std::string(s, n));
-}
-
-// #include <nlohmann/detail/macro_unscope.hpp>
-
-
-// restore GCC/clang diagnostic settings
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
-    #pragma GCC diagnostic pop
-#endif
-#if defined(__clang__)
-    #pragma GCC diagnostic pop
-#endif
-
-// clean up
-#undef JSON_INTERNAL_CATCH
-#undef JSON_CATCH
-#undef JSON_THROW
-#undef JSON_TRY
-#undef JSON_LIKELY
-#undef JSON_UNLIKELY
-#undef JSON_DEPRECATED
-#undef JSON_HAS_CPP_14
-#undef JSON_HAS_CPP_17
-#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
-#undef NLOHMANN_BASIC_JSON_TPL
-
-
-#endif
diff --git a/third_party/nix/src/proto/CMakeLists.txt b/third_party/nix/src/proto/CMakeLists.txt
deleted file mode 100644
index 726ca8742f..0000000000
--- a/third_party/nix/src/proto/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- mode: cmake; -*-
-#
-# The proto generation happens outside of CMake and the path to the
-# generated files is passed in via the environment variable
-# $NIX_PROTO_SRCS.
-#
-# This configuration defines a library target that compiles these
-# sources and makes the headers available.
-
-add_library(nixproto SHARED)
-set_property(TARGET nixproto PROPERTY CXX_STANDARD 17)
-
-set(HEADER_FILES
-  $ENV{NIX_PROTO_SRCS}/libproto/worker.grpc.pb.h
-  $ENV{NIX_PROTO_SRCS}/libproto/worker.pb.h
-)
-
-target_sources(nixproto
-  PUBLIC
-    ${HEADER_FILES}
-
-  PRIVATE
-    $ENV{NIX_PROTO_SRCS}/libproto/worker.grpc.pb.cc
-    $ENV{NIX_PROTO_SRCS}/libproto/worker.pb.cc
-)
-
-target_link_libraries(nixproto
-  gRPC::grpc++_reflection
-  protobuf::libprotobuf
-)
-
-target_include_directories(nixproto
-  INTERFACE $ENV{NIX_PROTO_SRCS}
-)
-
-INSTALL(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nix/libproto)
-INSTALL(TARGETS nixproto DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/third_party/nix/src/proto/worker.proto b/third_party/nix/src/proto/worker.proto
deleted file mode 100644
index b0dc32afbe..0000000000
--- a/third_party/nix/src/proto/worker.proto
+++ /dev/null
@@ -1,374 +0,0 @@
-syntax = "proto3";
-
-import "google/protobuf/empty.proto";
-import "google/protobuf/timestamp.proto";
-
-package nix.proto;
-
-// Service representing a worker used for building and interfacing
-// with the Nix store.
-service WorkerService {
-  // Validates whether the supplied path is a valid store path.
-  rpc IsValidPath(StorePath) returns (IsValidPathResponse);
-
-  // Checks whether any substitutes exist for the given path.
-  rpc HasSubstitutes(StorePath) returns (HasSubstitutesResponse);
-
-  // Query referrers for a given path.
-  rpc QueryReferrers(StorePath) returns (StorePaths);
-
-  // Add a path to the store. The first stream request
-  // should be a message indicating metadata, the rest should be file
-  // chunks.
-  rpc AddToStore(stream AddToStoreRequest) returns (StorePath);
-
-  // Adds the supplied string to the store, as a text file.
-  rpc AddTextToStore(stream AddTextToStoreRequest) returns (StorePath);
-
-  // Build the specified derivations in one of the specified build
-  // modes, defaulting to a normal build.
-  rpc BuildPaths(BuildPathsRequest) returns (stream BuildEvent);
-
-  // TODO: What does this do?
-  rpc EnsurePath(StorePath) returns (google.protobuf.Empty);
-
-  // TODO: What does this do?
-  // TODO(grfn): This should not actually take a StorePath, as it's not a
-  // StorePath
-  rpc AddTempRoot(StorePath) returns (google.protobuf.Empty);
-
-  // TODO: What does this do?
-  rpc AddIndirectRoot(StorePath) returns (google.protobuf.Empty);
-
-  // TODO: What does this do?
-  rpc SyncWithGC(google.protobuf.Empty) returns (google.protobuf.Empty);
-
-  // TODO: What does this do?
-  rpc FindRoots(google.protobuf.Empty) returns (FindRootsResponse);
-
-  // TODO: What does this do?
-  rpc SetOptions(SetOptionsRequest) returns (google.protobuf.Empty);
-
-  // Ask the store to perform a garbage collection, based on the
-  // specified parameters. See `GCAction` for the possible actions.
-  rpc CollectGarbage(CollectGarbageRequest) returns (CollectGarbageResponse);
-
-  // TODO: What does this do?
-  rpc QuerySubstitutablePathInfos(StorePaths) returns (SubstitutablePathInfos);
-
-  // TODO: What does this do?
-  rpc QueryDerivationOutputs(StorePath) returns (StorePaths);
-
-  // Query all valid paths in the store
-  rpc QueryAllValidPaths(google.protobuf.Empty) returns (StorePaths);
-
-  // TODO: What does this do?
-  rpc QueryPathInfo(StorePath) returns (PathInfo);
-
-  // Query the output names of the given derivation
-  rpc QueryDerivationOutputNames(StorePath) returns (DerivationOutputNames);
-
-  // TODO: What is a HashPart?
-  rpc QueryPathFromHashPart(HashPart) returns (StorePath);
-
-  // Query which of the given paths is valid.
-  rpc QueryValidPaths(StorePaths) returns (StorePaths);
-
-  // Query which of the given paths have substitutes.
-  rpc QuerySubstitutablePaths(StorePaths) returns (StorePaths);
-
-  // Return all currently valid derivations that have the given store path as an
-  // output.
-  rpc QueryValidDerivers(StorePath) returns (StorePaths);
-
-  // Optimise the disk space usage of the Nix store by hard-linking files
-  // with the same contents.
-  rpc OptimiseStore(google.protobuf.Empty) returns (google.protobuf.Empty);
-
-  // Check the integrity of the Nix store
-  rpc VerifyStore(VerifyStoreRequest) returns (VerifyStoreResponse);
-
-  // Build a single non-materialized derivation (i.e. not from an
-  // on-disk .drv file).
-  rpc BuildDerivation(BuildDerivationRequest) returns (stream BuildEvent);
-
-  // Add signatures to the specified store path. The signatures are not
-  // verified.
-  rpc AddSignatures(AddSignaturesRequest) returns (google.protobuf.Empty);
-
-  // TODO: What does this do?
-  rpc NarFromPath(StorePath) returns (StorePath);
-
-  // Upload & add a NAR to the daemon's Nix store.
-  rpc AddToStoreNar(stream AddToStoreNarRequest)
-      returns (google.protobuf.Empty);
-
-  // Given a set of paths that are to be built, return the set of
-  // derivations that will be built, and the set of output paths that
-  // will be substituted.
-  rpc QueryMissing(StorePaths) returns (QueryMissingResponse);
-
-  // Return the build log of the specified store path, if available
-  rpc GetBuildLog(StorePath) returns (BuildLog);
-}
-
-enum HashType {
-  UNKNOWN = 0;
-  MD5 = 1;  // TODO(tazjin): still needed?
-  SHA1 = 2;
-  SHA256 = 3;
-  SHA512 = 4;
-}
-
-enum BuildMode {
-  Normal = 0;
-  Repair = 1;
-  Check = 2;
-}
-
-enum GCAction {
-  // Return the set of paths reachable from (i.e. in the closure of)
-  // the roots.
-  ReturnLive = 0;
-
-  // Return the set of paths not reachable from the roots.
-  ReturnDead = 1;
-
-  // Actually delete the latter set.
-  DeleteDead = 2;
-
-  // Delete the paths listed in `pathsToDelete', insofar as they are
-  // not reachable.
-  DeleteSpecific = 3;
-}
-
-enum BuildStatus {
-  Built = 0;
-  Substituted = 1;
-  AlreadyValid = 2;
-  PermanentFailure = 3;
-  InputRejected = 4;
-  OutputRejected = 5;
-  TransientFailure = 6;  // possibly transient
-  CachedFailure = 7;     // no longer used
-  TimedOut = 8;
-  MiscFailure = 9;
-  DependencyFailed = 10;
-  LogLimitExceeded = 11;
-  NotDeterministic = 12;
-};
-
-// Generic type for any RPC call that just reads or returns a single
-// store path.
-message StorePath {
-  string path = 1;
-}
-
-// Generic type for any RPC call that just reads or returns a list of
-// store paths.
-message StorePaths {
-  repeated string paths = 1;
-}
-
-message Signatures {
-  repeated string sigs = 1;
-}
-
-// Represents the outcome of a build for a single store path.
-message BuildResult {
-  StorePath path = 1;
-  BuildStatus status = 2;
-  string msg = 3;
-}
-
-// Represents an event occuring during a build.
-message BuildEvent {
-  message LogLine {
-    string line = 1;
-    StorePath path = 2;
-  }
-
-  oneof result_type {
-    // Build for a store path has finished
-    BuildResult result = 1;
-
-    // A line of build log output was produced
-    LogLine build_log = 2;
-
-    // Build for a store path has started
-    StorePath building_path = 3;
-  }
-}
-
-message IsValidPathResponse {
-  bool is_valid = 1;
-}
-
-message HasSubstitutesResponse {
-  bool has_substitutes = 1;
-}
-
-message AddToStoreRequest {
-  message Metadata {
-    bool fixed = 1;
-    bool recursive = 2;  // TODO(tazjin): what is this? "obsolete" comment?
-    HashType hash_type = 3;
-    string base_name = 4;
-  }
-
-  oneof add_oneof {
-    Metadata meta = 1;
-    bytes data = 3;
-  }
-}
-
-message AddTextToStoreRequest {
-  message Metadata {
-    string name = 1;
-    repeated string references = 2;
-    uint64 size = 3;
-  }
-
-  oneof add_oneof {
-    Metadata meta = 4;
-    bytes data = 5;
-  }
-}
-
-message BuildPathsRequest {
-  repeated string drvs = 1;
-  BuildMode mode = 2;
-}
-
-message FindRootsResponse {
-  map<string, StorePaths> roots = 1;
-}
-
-message SetOptionsRequest {
-  bool keep_failed = 1;
-  bool keep_going = 2;
-  bool try_fallback = 3;
-  uint32 max_build_jobs = 4;
-  uint32 verbose_build =
-      5;                   // TODO(tazjin): Maybe this should be bool, unclear.
-  uint32 build_cores = 6;  // TODO(tazjin): Difference from max_build_jobs?
-  bool use_substitutes = 7;
-  map<string, string> overrides = 8;  // TODO(tazjin): better name?
-}
-
-message CollectGarbageRequest {
-  // GC action that should be performed.
-  GCAction action = 1;
-
-  // For `DeleteSpecific', the paths to delete.
-  repeated string paths_to_delete = 2;
-
-  // If `ignore_liveness' is set, then reachability from the roots is
-  // ignored (dangerous!). However, the paths must still be
-  // unreferenced *within* the store (i.e., there can be no other
-  // store paths that depend on them).
-  bool ignore_liveness = 3;
-
-  // Stop after at least `max_freed' bytes have been freed.
-  uint64 max_freed = 4;
-}
-
-message CollectGarbageResponse {
-  // Depending on the action, the GC roots, or the paths that would be
-  // or have been deleted.
-  repeated string deleted_paths = 1;
-
-  // For `ReturnDead', `DeleteDead' and `DeleteSpecific', the number
-  // of bytes that would be or was freed.
-  uint64 bytes_freed = 2;
-}
-
-message PathInfo {
-  StorePath path = 10;
-  bool is_valid = 9;
-  StorePath deriver = 1;
-  bytes nar_hash = 2;
-  repeated string references = 3;
-  google.protobuf.Timestamp registration_time = 4;
-  uint64 download_size = 11;
-  uint64 nar_size = 5;
-  // Whether the path is ultimately trusted, that is, it's a derivation
-  // output that was built locally.
-  bool ultimate = 6;
-  repeated string sigs = 7;
-  // If non-empty, an assertion that the path is content-addressed
-  string ca = 8;
-
-  // Only used for AddToStoreNarRequest
-  bool repair = 12;
-  bool check_sigs = 13;
-}
-
-message SubstitutablePathInfos {
-  repeated PathInfo path_infos = 1;
-}
-
-message DerivationOutputNames {
-  repeated string names = 1;
-}
-
-message HashPart {
-  string hash_part = 1;
-}
-
-message VerifyStoreRequest {
-  bool check_contents = 1;
-  bool repair = 2;
-  // TODO(grfn): Remove double-negative
-  bool dont_check_sigs = 3;
-}
-
-message VerifyStoreResponse {
-  // True if errors remain (???)
-  bool errors = 1;
-}
-
-message Derivation {
-  message DerivationOutput {
-    StorePath path = 1;
-    string hash_algo = 2;
-    bytes hash = 3;
-  }
-  map<string, DerivationOutput> outputs = 1;
-  StorePaths input_sources = 2;
-  string platform = 3;
-  StorePath builder = 4;
-  repeated string args = 5;
-  map<string, string> env = 6;
-}
-
-message BuildDerivationRequest {
-  // Only used for informational purposes.
-  StorePath drv_path = 1;
-  Derivation derivation = 2;
-  BuildMode build_mode = 3;
-}
-
-message AddSignaturesRequest {
-  StorePath path = 1;
-  Signatures sigs = 2;
-}
-
-message AddToStoreNarRequest {
-  oneof add_oneof {
-    PathInfo path_info = 1;
-    bytes data = 2;
-  }
-}
-
-message QueryMissingResponse {
-  repeated string will_build = 1;
-  repeated string will_substitute = 2;
-  repeated string unknown = 3;
-  uint64 download_size = 4;
-  uint64 nar_size = 5;
-}
-
-message BuildLog {
-  string build_log = 1;
-}
diff --git a/third_party/nix/src/tests/CMakeLists.txt b/third_party/nix/src/tests/CMakeLists.txt
deleted file mode 100644
index f8158d06c3..0000000000
--- a/third_party/nix/src/tests/CMakeLists.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-# -*- mode: cmake; -*-
-include_directories(${PROJECT_BINARY_DIR}) # for 'generated/'
-
-add_executable(attr-set attr-set.cc)
-target_link_libraries(attr-set
-  nixexpr
-  rapidcheck
-  rapidcheck_gtest
-  GTest::gtest_main
-)
-
-gtest_discover_tests(attr-set)
-
-add_executable(derivations_test derivations_test.cc)
-target_link_libraries(derivations_test
-  nixexpr
-  nixstore
-  rapidcheck
-  rapidcheck_gtest
-  GTest::gtest_main
-)
-
-gtest_discover_tests(derivations_test)
-
-add_executable(hash_test hash_test.cc)
-target_link_libraries(hash_test
-  nixutil
-  GTest::gtest_main
-)
-
-gtest_discover_tests(hash_test)
-
-add_executable(references_test references_test.cc)
-target_link_libraries(references_test
-  nixstore
-  rapidcheck
-  rapidcheck_gtest
-  GTest::gtest_main
-)
-
-gtest_discover_tests(references_test)
-
-add_executable(store_test store_tests.cc)
-target_link_libraries(store_test
-  nixstore
-  nixstoremock
-  GTest::gtest_main
-)
-
-gtest_discover_tests(store_test)
-
-add_executable(value-to-json value-to-json.cc)
-target_link_libraries(value-to-json
-  nixexpr
-  nixstore
-  GTest::gtest_main
-)
-
-gtest_discover_tests(value-to-json)
-
-add_executable(language-tests language-tests.cc)
-target_link_libraries(language-tests
-  nixexpr
-  nixstore
-  GTest::gtest_main
-)
-
-gtest_discover_tests(language-tests)
-
-add_executable(store-api-test store-api-test.cc)
-target_link_libraries(store-api-test
-  nixstore
-  rapidcheck
-  rapidcheck_gtest
-  GTest::gtest_main
-)
-
-gtest_discover_tests(store-api-test)
diff --git a/third_party/nix/src/tests/arbitrary.hh b/third_party/nix/src/tests/arbitrary.hh
deleted file mode 100644
index 026f8522cf..0000000000
--- a/third_party/nix/src/tests/arbitrary.hh
+++ /dev/null
@@ -1,176 +0,0 @@
-#pragma once
-
-#include <rapidcheck.h>
-#include <rapidcheck/Gen.h>
-#include <rapidcheck/gen/Arbitrary.h>
-
-#include "libexpr/attr-set.hh"
-#include "libexpr/nixexpr.hh"
-#include "libstore/derivations.hh"
-#include "libutil/hash.hh"
-
-namespace nix::tests {
-static nix::SymbolTable* symbol_table;
-}
-
-namespace rc {
-
-using nix::Derivation;
-using nix::DerivationOutput;
-using nix::Pos;
-using nix::Value;
-
-template <>
-struct Arbitrary<nix::Symbol> {
-  static Gen<nix::Symbol> arbitrary() {
-    return gen::map(gen::arbitrary<std::string>(), [](std::string s) {
-      return nix::tests::symbol_table->Create(s);
-    });
-  }
-};
-
-template <>
-struct Arbitrary<Value> {
-  static Gen<nix::Value> arbitrary() {
-    return gen::build(gen::construct<Value>(),
-                      // TODO(grfn) generalize to more types
-                      gen::set(&Value::type, gen::just(nix::ValueType::tInt)),
-                      gen::set(&Value::integer, gen::arbitrary<int64_t>()));
-  }
-};
-
-template <>
-struct Arbitrary<Value*> {
-  static Gen<nix::Value*> arbitrary() {
-    return gen::apply(
-        [](nix::ValueType typ, int i) {
-          auto ret = new Value();
-          ret->type = typ;
-          ret->integer = i;
-          return ret;
-        },
-        gen::just(nix::ValueType::tInt), gen::arbitrary<int64_t>());
-  }
-};
-
-template <>
-struct Arbitrary<nix::Pos> {
-  static Gen<nix::Pos> arbitrary() {
-    return gen::construct<nix::Pos>(gen::arbitrary<nix::Symbol>(),
-                                    gen::arbitrary<unsigned int>(),
-                                    gen::arbitrary<unsigned int>());
-  }
-};
-
-template <>
-struct Arbitrary<nix::Pos*> {
-  static Gen<nix::Pos*> arbitrary() {
-    return gen::apply(
-        [](unsigned int line, unsigned int column) {
-          return new Pos({}, line, column);
-        },
-        gen::arbitrary<unsigned int>(), gen::arbitrary<unsigned int>());
-  }
-};
-
-template <>
-struct Arbitrary<nix::Attr> {
-  static Gen<nix::Attr> arbitrary() {
-    return gen::construct<nix::Attr>(gen::arbitrary<nix::Symbol>(),
-                                     gen::arbitrary<Value*>(),
-                                     gen::arbitrary<nix::Pos*>());
-  }
-};
-
-template <>
-struct Arbitrary<nix::Bindings> {
-  static Gen<nix::Bindings> arbitrary() {
-    return gen::map(gen::arbitrary<std::vector<nix::Attr>>(), [](auto attrs) {
-      nix::Bindings res;
-      for (const auto& attr : attrs) {
-        res.push_back(attr);
-      }
-      return res;
-    });
-  }
-};
-
-template <class K, class V>
-struct Arbitrary<absl::btree_map<K, V>> {
-  static Gen<absl::btree_map<K, V>> arbitrary() {
-    return gen::map(gen::arbitrary<std::map<K, V>>(), [](std::map<K, V> map) {
-      absl::btree_map<K, V> out_map;
-      out_map.insert(map.begin(), map.end());
-      return out_map;
-    });
-  }
-};
-
-template <>
-struct Arbitrary<nix::Base> {
-  static Gen<nix::Base> arbitrary() {
-    return gen::element(nix::Base16, nix::Base32, nix::Base64);
-  }
-};
-
-template <>
-struct Arbitrary<DerivationOutput> {
-  static Gen<DerivationOutput> arbitrary() {
-    return gen::apply(
-        [](std::string content, std::string path, std::string hash_algo,
-           bool recursive, bool include_algo_in_hash, nix::Base base) {
-          auto hash_type = nix::parseHashType(hash_algo);
-          auto hash = nix::hashString(hash_type, content);
-          return DerivationOutput(
-              path, recursive ? absl::StrCat("r:", hash_algo) : hash_algo,
-              hash.to_string(base, include_algo_in_hash));
-        },
-        gen::arbitrary<std::string>(),
-        gen::map(gen::arbitrary<std::string>(),
-                 [](std::string s) { return absl::StrCat("/", s); }),
-        gen::element<std::string>("md5", "sha1", "sha256", "sha512"),
-        gen::arbitrary<bool>(), gen::arbitrary<bool>(),
-        gen::arbitrary<nix::Base>());
-  }
-};
-
-template <>
-struct Arbitrary<Derivation> {
-  static Gen<Derivation> arbitrary() {
-    auto gen_path = gen::map(gen::arbitrary<std::string>(), [](std::string s) {
-      return absl::StrCat("/", s);
-    });
-
-    return gen::build<Derivation>(
-        gen::set(&nix::BasicDerivation::outputs),
-        gen::set(&nix::BasicDerivation::inputSrcs,
-                 gen::container<nix::PathSet>(gen_path)),
-        gen::set(&nix::BasicDerivation::platform),
-        gen::set(&nix::BasicDerivation::builder, gen_path),
-        gen::set(&nix::BasicDerivation::args),
-        gen::set(&nix::BasicDerivation::env),
-        gen::set(&Derivation::inputDrvs,
-                 gen::container<nix::DerivationInputs>(
-                     gen_path, gen::arbitrary<nix::StringSet>())));
-  }
-};
-
-template <>
-struct Arbitrary<nix::BuildResult::Status> {
-  static Gen<nix::BuildResult::Status> arbitrary() {
-    return gen::element(nix::BuildResult::Status::Built,
-                        nix::BuildResult::Status::Substituted,
-                        nix::BuildResult::Status::AlreadyValid,
-                        nix::BuildResult::Status::PermanentFailure,
-                        nix::BuildResult::Status::InputRejected,
-                        nix::BuildResult::Status::OutputRejected,
-                        nix::BuildResult::Status::TransientFailure,
-                        nix::BuildResult::Status::CachedFailure,
-                        nix::BuildResult::Status::TimedOut,
-                        nix::BuildResult::Status::MiscFailure,
-                        nix::BuildResult::Status::DependencyFailed,
-                        nix::BuildResult::Status::LogLimitExceeded,
-                        nix::BuildResult::Status::NotDeterministic);
-  }
-};
-}  // namespace rc
diff --git a/third_party/nix/src/tests/attr-set.cc b/third_party/nix/src/tests/attr-set.cc
deleted file mode 100644
index 35932bbeff..0000000000
--- a/third_party/nix/src/tests/attr-set.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "libexpr/attr-set.hh"
-
-#include <cstdio>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <absl/container/btree_map.h>
-#include <bits/stdint-intn.h>
-#include <gtest/gtest.h>
-#include <rapidcheck.h>
-#include <rapidcheck/Assertions.h>
-#include <rapidcheck/gen/Arbitrary.h>
-#include <rapidcheck/gen/Build.h>
-#include <rapidcheck/gen/Create.h>
-#include <rapidcheck/gen/Transform.h>
-#include <rapidcheck/gtest.h>
-
-#include "libexpr/eval.hh"
-#include "libexpr/nixexpr.hh"
-#include "libexpr/symbol-table.hh"
-#include "libexpr/value.hh"
-#include "tests/arbitrary.hh"
-#include "tests/dummy-store.hh"
-
-namespace nix {
-
-using nix::tests::DummyStore;
-
-class AttrSetTest : public ::testing::Test {
- protected:
-  EvalState* eval_state_;
-  void SetUp() override {
-    nix::expr::InitGC();
-    auto store = std::make_shared<DummyStore>();
-    eval_state_ = new EvalState({"."}, ref<Store>(store));
-    tests::symbol_table = &eval_state_->symbols;
-  }
-
-  void assert_bindings_equal(nix::Bindings* lhs, nix::Bindings* rhs) {
-    RC_ASSERT(lhs->Equal(rhs, *eval_state_));
-  }
-};
-
-class AttrSetMonoidTest : public AttrSetTest {};
-
-RC_GTEST_FIXTURE_PROP(AttrSetMonoidTest, mergeLeftIdentity,
-                      (nix::Bindings && bindings)) {
-  auto empty_bindings = nix::Bindings::New();
-  auto result = Bindings::Merge(*empty_bindings, bindings);
-  assert_bindings_equal(result.get(), &bindings);
-}
-
-RC_GTEST_FIXTURE_PROP(AttrSetMonoidTest, mergeRightIdentity,
-                      (nix::Bindings && bindings)) {
-  auto empty_bindings = nix::Bindings::New();
-  auto result = Bindings::Merge(bindings, *empty_bindings);
-  assert_bindings_equal(result.get(), &bindings);
-}
-
-RC_GTEST_FIXTURE_PROP(AttrSetMonoidTest, mergeAssociative,
-                      (nix::Bindings && bindings_1, nix::Bindings&& bindings_2,
-                       nix::Bindings&& bindings_3)) {
-  auto b231 =
-      Bindings::Merge(bindings_1, *Bindings::Merge(bindings_2, bindings_3));
-  auto b123 =
-      Bindings::Merge(*Bindings::Merge(bindings_1, bindings_2), bindings_3);
-  assert_bindings_equal(b231.get(), b123.get());
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/tests/derivations_test.cc b/third_party/nix/src/tests/derivations_test.cc
deleted file mode 100644
index 6ebe17824c..0000000000
--- a/third_party/nix/src/tests/derivations_test.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-#include "libstore/derivations.hh"
-
-#include <memory>
-
-#include <absl/strings/str_cat.h>
-#include <gtest/gtest.h>
-#include <rapidcheck.h>
-#include <rapidcheck/Assertions.h>
-#include <rapidcheck/gen/Arbitrary.h>
-#include <rapidcheck/gen/Build.h>
-#include <rapidcheck/gen/Container.h>
-#include <rapidcheck/gen/Tuple.h>
-#include <rapidcheck/gtest.h>
-#include <rapidcheck/state.h>
-
-#include "libexpr/eval.hh"
-#include "libutil/hash.hh"
-#include "libutil/types.hh"
-#include "tests/arbitrary.hh"
-
-namespace nix {
-
-void AssertDerivationsEqual(const Derivation& lhs, const Derivation& rhs) {
-  RC_ASSERT(lhs.outputs.size() == rhs.outputs.size());
-  for (const auto& [k, lhs_v] : lhs.outputs) {
-    auto rhs_v = rhs.outputs.find(k);
-    RC_ASSERT(rhs_v != rhs.outputs.end());
-    RC_ASSERT(lhs_v.path == rhs_v->second.path);
-    RC_ASSERT(lhs_v.hashAlgo == rhs_v->second.hashAlgo);
-    RC_ASSERT(lhs_v.hash == rhs_v->second.hash);
-  }
-
-  RC_ASSERT(lhs.inputSrcs == rhs.inputSrcs);
-  RC_ASSERT(lhs.platform == rhs.platform);
-  RC_ASSERT(lhs.builder == rhs.builder);
-  RC_ASSERT(lhs.args == rhs.args);
-  RC_ASSERT(lhs.env == rhs.env);
-  RC_ASSERT(lhs.inputDrvs == rhs.inputDrvs);
-}
-
-class DerivationsTest : public ::testing::Test {};
-
-// NOLINTNEXTLINE
-RC_GTEST_FIXTURE_PROP(DerivationsTest, UnparseParseRoundTrip,
-                      (Derivation && drv)) {
-  auto unparsed = drv.unparse();
-  auto parsed = parseDerivation(unparsed);
-  AssertDerivationsEqual(drv, parsed);
-}
-
-// NOLINTNEXTLINE
-RC_GTEST_FIXTURE_PROP(DerivationsTest, ToProtoPreservesInput,
-                      (Derivation && drv)) {
-  auto proto = drv.to_proto();
-
-  RC_ASSERT(proto.outputs_size() == drv.outputs.size());
-  RC_ASSERT(proto.input_sources().paths_size() == drv.inputSrcs.size());
-  auto paths = proto.input_sources().paths();
-  for (const auto& input_src : drv.inputSrcs) {
-    RC_ASSERT(std::find(paths.begin(), paths.end(), input_src) != paths.end());
-  }
-
-  RC_ASSERT(proto.platform() == drv.platform);
-  RC_ASSERT(proto.builder().path() == drv.builder);
-
-  RC_ASSERT(proto.args_size() == drv.args.size());
-  auto args = proto.args();
-  for (const auto& arg : drv.args) {
-    RC_ASSERT(std::find(args.begin(), args.end(), arg) != args.end());
-  }
-
-  RC_ASSERT(proto.env_size() == drv.env.size());
-  auto env = proto.env();
-  for (const auto& [key, value] : drv.env) {
-    RC_ASSERT(env.at(key) == value);
-  }
-}
-
-class ParseDrvPathWithOutputsTest : public DerivationsTest {};
-
-TEST(ParseDrvPathWithOutputsTest, ParseDrvPathWithOutputs) {
-  auto input = "/nix/store/my51f75kp056md84gq2v08pd140pcz57-test.drv!out";
-  auto result = nix::parseDrvPathWithOutputs(input);
-
-  ASSERT_EQ(result.first,
-            "/nix/store/my51f75kp056md84gq2v08pd140pcz57-test.drv");
-  ASSERT_EQ(result.second, nix::PathSet{"out"});
-}
-
-TEST(ParseDrvPathWithOutputsTest, ParseDrvPathWithMultipleOutputs) {
-  auto input = "/nix/store/my51f75kp056md84gq2v08pd140pcz57-test.drv!out,dev";
-  auto result = nix::parseDrvPathWithOutputs(input);
-
-  nix::PathSet expected = {"out", "dev"};
-
-  ASSERT_EQ(result.first,
-            "/nix/store/my51f75kp056md84gq2v08pd140pcz57-test.drv");
-  ASSERT_EQ(result.second, expected);
-}
-
-TEST(ParseDrvPathWithOutputsTest, ParseDrvPathWithNoOutputs) {
-  auto input = "/nix/store/my51f75kp056md84gq2v08pd140pcz57-test";
-  auto result = nix::parseDrvPathWithOutputs(input);
-
-  ASSERT_EQ(result.first, "/nix/store/my51f75kp056md84gq2v08pd140pcz57-test");
-  ASSERT_EQ(result.second, nix::PathSet());
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/tests/dummy-store.hh b/third_party/nix/src/tests/dummy-store.hh
deleted file mode 100644
index 8047d25727..0000000000
--- a/third_party/nix/src/tests/dummy-store.hh
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#include <filesystem>
-
-#include "libstore/store-api.hh"
-
-namespace nix::tests {
-
-class DummyStore final : public Store {
- public:
-  explicit DummyStore() : Store(Store::Params{}) {}
-
-  std::string getUri() { return ""; }
-
-  void queryPathInfoUncached(
-      const Path& path,
-      Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept {}
-
-  Path queryPathFromHashPart(const std::string& hashPart) { return ""; }
-
-  Path addToStore(const std::string& name, const Path& srcPath,
-                  bool recursive = true, HashType hashAlgo = htSHA256,
-                  PathFilter& filter = defaultPathFilter,
-                  RepairFlag repair = NoRepair) {
-    if (srcPath == "/exists-for-tests") {
-      return "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x";
-    }
-
-    throw SysError("file does not exist");
-  }
-
-  Path addTextToStore(const std::string& name, const std::string& s,
-                      const PathSet& references, RepairFlag repair = NoRepair) {
-    return "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x";
-  }
-
-  void narFromPath(const Path& path, Sink& sink) {}
-
-  BuildResult buildDerivation(std::ostream& log_sink, const Path& drvPath,
-                              const BasicDerivation& drv,
-                              BuildMode buildMode = bmNormal) {
-    return BuildResult{};
-  }
-
-  void ensurePath(const Path& path) {}
-};
-
-}  // namespace nix::tests
diff --git a/third_party/nix/src/tests/hash_test.cc b/third_party/nix/src/tests/hash_test.cc
deleted file mode 100644
index caa77f5d6b..0000000000
--- a/third_party/nix/src/tests/hash_test.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-#include "libutil/hash.hh"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-class HashTest : public ::testing::Test {};
-
-using testing::EndsWith;
-using testing::HasSubstr;
-
-namespace nix {
-
-TEST(HashTest, SHA256) {
-  auto hash = hashString(HashType::htSHA256, "foo");
-  ASSERT_EQ(hash.base64Len(), 44);
-  ASSERT_EQ(hash.base32Len(), 52);
-  ASSERT_EQ(hash.base16Len(), 64);
-
-  ASSERT_EQ(hash.to_string(Base16),
-            "sha256:"
-            "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae");
-  ASSERT_EQ(hash.to_string(Base32),
-            "sha256:1bp7cri8hplaz6hbz0v4f0nl44rl84q1sg25kgwqzipzd1mv89ic");
-  ASSERT_EQ(hash.to_string(Base64),
-            "sha256:LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm564=");
-}
-
-TEST(HashTest, SHA256Decode) {
-  auto hash = hashString(HashType::htSHA256, "foo");
-
-  std::unique_ptr<Hash> base16 = std::make_unique<Hash>(
-      "sha256:"
-      "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
-      HashType::htSHA256);
-  std::unique_ptr<Hash> base32 = std::make_unique<Hash>(
-      "sha256:1bp7cri8hplaz6hbz0v4f0nl44rl84q1sg25kgwqzipzd1mv89ic",
-      HashType::htSHA256);
-  std::unique_ptr<Hash> base64 = std::make_unique<Hash>(
-      "sha256:LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm564=",
-      HashType::htSHA256);
-
-  ASSERT_EQ(hash, *base16);
-  ASSERT_EQ(hash, *base32);
-  ASSERT_EQ(hash, *base64);
-}
-
-TEST(HashTest, SHA256DecodeFail) {
-  EXPECT_THAT(
-      Hash::deserialize("sha256:LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm56==",
-                        HashType::htSHA256)
-          .status()
-          .message(),
-      HasSubstr("wrong length"));
-  EXPECT_THAT(
-      Hash::deserialize("sha256:LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm56,=",
-                        HashType::htSHA256)
-          .status()
-          .message(),
-      HasSubstr("invalid base-64"));
-
-  EXPECT_THAT(Hash::deserialize(
-                  "sha256:1bp7cri8hplaz6hbz0v4f0nl44rl84q1sg25kgwqzipzd1mv89i",
-                  HashType::htSHA256)
-                  .status()
-                  .message(),
-              HasSubstr("wrong length"));
-  absl::StatusOr<Hash> badB32Char = Hash::deserialize(
-      "sha256:1bp7cri8hplaz6hbz0v4f0nl44rl84q1sg25kgwqzipzd1mv89i,",
-      HashType::htSHA256);
-  EXPECT_THAT(badB32Char.status().message(), HasSubstr("invalid base-32"));
-  EXPECT_THAT(badB32Char.status().message(), EndsWith(","));
-
-  EXPECT_THAT(
-      Hash::deserialize(
-          "sha256:"
-          "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7  ",
-          HashType::htSHA256)
-          .status()
-          .message(),
-      HasSubstr("invalid base-16"));
-}
-
-TEST(HashSink, SHA256) {
-  HashSink sink(htSHA256);
-
-  sink.write(reinterpret_cast<const unsigned char*>("fo"), 2);
-  HashResult partial = sink.currentHash();
-  EXPECT_EQ(partial.first.to_string(Base16),
-            "sha256:"
-            "9c3aee7110b787f0fb5f81633a36392bd277ea945d44c874a9a23601aefe20cf");
-  EXPECT_EQ(partial.second, 2);
-
-  sink.write(reinterpret_cast<const unsigned char*>("o"), 1);
-  HashResult end = sink.finish();
-  EXPECT_EQ(end.first.to_string(Base16),
-            "sha256:"
-            "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae");
-  EXPECT_EQ(end.second, 3);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/tests/lang/disabled/README.txt b/third_party/nix/src/tests/lang/disabled/README.txt
deleted file mode 100644
index 50225deb63..0000000000
--- a/third_party/nix/src/tests/lang/disabled/README.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-These tests are disabled primarily because the DummyStore used for
-tests does not interact with real files on disk at the moment, but the
-tests expect it to.
-
-Once we have a solution for this (potentially just reading & hashing
-the files, but not writing them anywhere) these tests will be enabled
-again.
diff --git a/third_party/nix/src/tests/lang/disabled/eval-okay-search-path.nix b/third_party/nix/src/tests/lang/disabled/eval-okay-search-path.nix
deleted file mode 100644
index cca41f821f..0000000000
--- a/third_party/nix/src/tests/lang/disabled/eval-okay-search-path.nix
+++ /dev/null
@@ -1,11 +0,0 @@
-with import ./lib.nix;
-with builtins;
-
-assert pathExists <nix/buildenv.nix>;
-
-assert length __nixPath == 6;
-assert length (filter (x: x.prefix == "nix") __nixPath) == 1;
-assert length (filter (x: baseNameOf x.path == "dir4") __nixPath) == 1;
-
-import <a.nix> + import <b.nix> + import <c.nix> + import <dir5/c.nix>
-  + (let __nixPath = [ { path = ./dir2; } { path = ./dir1; } ]; in import <a.nix>)
diff --git a/third_party/nix/src/tests/lang/disabled/eval-okay-xml.exp b/third_party/nix/src/tests/lang/disabled/eval-okay-xml.exp
deleted file mode 100644
index 92b75e0b8b..0000000000
--- a/third_party/nix/src/tests/lang/disabled/eval-okay-xml.exp
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version='1.0' encoding='utf-8'?>
-<expr>
-  <attrs>
-    <attr name="a">
-      <string value="foo" />
-    </attr>
-    <attr name="at">
-      <function>
-        <attrspat name="args">
-          <attr name="x" />
-          <attr name="y" />
-          <attr name="z" />
-        </attrspat>
-      </function>
-    </attr>
-    <attr name="b">
-      <string value="bar" />
-    </attr>
-    <attr name="c">
-      <string value="foobar" />
-    </attr>
-    <attr name="ellipsis">
-      <function>
-        <attrspat ellipsis="1">
-          <attr name="x" />
-          <attr name="y" />
-          <attr name="z" />
-        </attrspat>
-      </function>
-    </attr>
-    <attr name="f">
-      <function>
-        <attrspat>
-          <attr name="z" />
-          <attr name="x" />
-          <attr name="y" />
-        </attrspat>
-      </function>
-    </attr>
-    <attr name="id">
-      <function>
-        <varpat name="x" />
-      </function>
-    </attr>
-    <attr name="x">
-      <int value="123" />
-    </attr>
-    <attr name="y">
-      <float value="567.89" />
-    </attr>
-  </attrs>
-</expr>
diff --git a/third_party/nix/src/tests/lang/eval-okay-autoargs.flags b/third_party/nix/src/tests/lang/eval-okay-autoargs.flags
deleted file mode 100644
index ae37622544..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-autoargs.flags
+++ /dev/null
@@ -1 +0,0 @@
---arg lib import(lang/lib.nix) --argstr xyzzy xyzzy! -A result
diff --git a/third_party/nix/src/tests/lang/eval-okay-fromjson.nix b/third_party/nix/src/tests/lang/eval-okay-fromjson.nix
deleted file mode 100644
index 102ee82b5e..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-fromjson.nix
+++ /dev/null
@@ -1,36 +0,0 @@
-# RFC 7159, section 13.
-builtins.fromJSON
-  ''
-    {
-      "Image": {
-          "Width":  800,
-          "Height": 600,
-          "Title":  "View from 15th Floor",
-          "Thumbnail": {
-              "Url":    "http://www.example.com/image/481989943",
-              "Height": 125,
-              "Width":  100
-          },
-          "Animated" : false,
-          "IDs": [116, 943, 234, 38793, true  ,false,null, -100],
-          "Latitude":  37.7668,
-          "Longitude": -122.3959
-        }
-    }
-  ''
-==
-  { Image =
-    { Width = 800;
-      Height = 600;
-      Title = "View from 15th Floor";
-      Thumbnail =
-        { Url = http://www.example.com/image/481989943;
-          Height = 125;
-          Width = 100;
-        };
-      Animated = false;
-      IDs = [ 116 943 234 38793 true false null (0-100) ];
-      Latitude = 37.7668;
-      Longitude = -122.3959;
-    };
-  }
diff --git a/third_party/nix/src/tests/lang/eval-okay-functionargs.exp b/third_party/nix/src/tests/lang/eval-okay-functionargs.exp
deleted file mode 100644
index f8ddea8e0b..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-functionargs.exp
+++ /dev/null
@@ -1 +0,0 @@
-[ { aterm = false; fetchurl = false; stdenv = false; } { color = false; name = true; } { } { } { } "stdenv" "fetchurl" "aterm-stdenv" "aterm-stdenv2" "libX11" "libXv" "mplayer-stdenv2.libXv-libX11" "mplayer-stdenv2.libXv-libX11_2" "nix-stdenv-aterm-stdenv" "nix-stdenv2-aterm2-stdenv2" ]
diff --git a/third_party/nix/src/tests/lang/eval-okay-functionargs.nix b/third_party/nix/src/tests/lang/eval-okay-functionargs.nix
deleted file mode 100644
index 205bbf307a..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-functionargs.nix
+++ /dev/null
@@ -1,89 +0,0 @@
-let
-
-  stdenvFun = { }: { name = "stdenv"; };
-  stdenv2Fun = { }: { name = "stdenv2"; };
-  fetchurlFun = { stdenv }: assert stdenv.name == "stdenv"; { name = "fetchurl"; };
-  atermFun = { stdenv, fetchurl }: { name = "aterm-${stdenv.name}"; };
-  aterm2Fun = { stdenv, fetchurl }: { name = "aterm2-${stdenv.name}"; };
-  nixFun = { stdenv, fetchurl, aterm }: { name = "nix-${stdenv.name}-${aterm.name}"; };
-
-  trivialFunctionArgsUsage = [
-    (builtins.functionArgs nixFun)
-    (builtins.functionArgs ({ name ? "Karl", color }: "${name} is ${color}"))
-    (builtins.functionArgs (x: y: x + y))
-    (builtins.functionArgs builtins.map)
-    (builtins.functionArgs builtins.fetchurl)
-  ];
-  
-  mplayerFun =
-    { stdenv, fetchurl, enableX11 ? false, xorg ? null, enableFoo ? true, foo ? null  }:
-    assert stdenv.name == "stdenv2";
-    assert enableX11 -> xorg.libXv.name == "libXv";
-    assert enableFoo -> foo != null;
-    { name = "mplayer-${stdenv.name}.${xorg.libXv.name}-${xorg.libX11.name}"; };
-
-  makeOverridable = f: origArgs: f origArgs //
-    { override = newArgs:
-        makeOverridable f (origArgs // (if builtins.isFunction newArgs then newArgs origArgs else newArgs));
-    };
-    
-  callPackage_ = pkgs: f: args:
-    makeOverridable f ((builtins.intersectAttrs (builtins.functionArgs f) pkgs) // args);
-
-  allPackages =
-    { overrides ? (pkgs: pkgsPrev: { }) }:
-    let
-      callPackage = callPackage_ pkgs;
-      pkgs = pkgsStd // (overrides pkgs pkgsStd);
-      pkgsStd = {
-        inherit pkgs;
-        stdenv = callPackage stdenvFun { };
-        stdenv2 = callPackage stdenv2Fun { };
-        fetchurl = callPackage fetchurlFun { };
-        aterm = callPackage atermFun { };
-        xorg = callPackage xorgFun { };
-        mplayer = callPackage mplayerFun { stdenv = pkgs.stdenv2; enableFoo = false; };
-        nix = callPackage nixFun { };
-      };
-    in pkgs;
-
-  libX11Fun = { stdenv, fetchurl }: { name = "libX11"; };
-  libX11_2Fun = { stdenv, fetchurl }: { name = "libX11_2"; };
-  libXvFun = { stdenv, fetchurl, libX11 }: { name = "libXv"; };
-  
-  xorgFun =
-    { pkgs }:
-    let callPackage = callPackage_ (pkgs // pkgs.xorg); in
-    {
-      libX11 = callPackage libX11Fun { };
-      libXv = callPackage libXvFun { };
-    };
-
-in
-
-let
-
-  pkgs = allPackages { };
-  
-  pkgs2 = allPackages {
-    overrides = pkgs: pkgsPrev: {
-      stdenv = pkgs.stdenv2;
-      nix = pkgsPrev.nix.override { aterm = aterm2Fun { inherit (pkgs) stdenv fetchurl; }; };
-      xorg = pkgsPrev.xorg // { libX11 = libX11_2Fun { inherit (pkgs) stdenv fetchurl; }; };
-    };
-  };
-  
-in
-
-  trivialFunctionArgsUsage ++ [
-    pkgs.stdenv.name
-    pkgs.fetchurl.name
-    pkgs.aterm.name
-    pkgs2.aterm.name
-    pkgs.xorg.libX11.name
-    pkgs.xorg.libXv.name
-    pkgs.mplayer.name
-    pkgs2.mplayer.name
-    pkgs.nix.name
-    pkgs2.nix.name
-  ]
diff --git a/third_party/nix/src/tests/lang/eval-okay-getenv.nix b/third_party/nix/src/tests/lang/eval-okay-getenv.nix
deleted file mode 100644
index ea8bb9f0a6..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-getenv.nix
+++ /dev/null
@@ -1 +0,0 @@
-builtins.getEnv "NIX_TEST_VAR" + (if builtins.getEnv "NO_SUCH_VAR" == "" then "bar" else "bla")
diff --git a/third_party/nix/src/tests/lang/eval-okay-import.nix b/third_party/nix/src/tests/lang/eval-okay-import.nix
deleted file mode 100644
index 0b18d94131..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-import.nix
+++ /dev/null
@@ -1,11 +0,0 @@
-let
-
-  overrides = {
-    import = fn: scopedImport overrides fn;
-
-    scopedImport = attrs: fn: scopedImport (overrides // attrs) fn;
-
-    builtins = builtins // overrides;
-  } // import ./lib.nix;
-
-in scopedImport overrides ./imported.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-ind-string.exp b/third_party/nix/src/tests/lang/eval-okay-ind-string.exp
deleted file mode 100644
index 9cf4bd2ee7..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-ind-string.exp
+++ /dev/null
@@ -1 +0,0 @@
-"This is an indented multi-line string\nliteral.  An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed.  Thus,\nin this case four spaces will be\nstripped from each line, even though\n  THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n  followed by a newline, it's stripped, but\n  that's not the case here. Two spaces are\n  stripped because of the \"  \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n  The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n   Tabs are not interpreted as whitespace (since we can't guess\n   what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n   space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored.  But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n  Similarly you can force an indentation level,\n  in this case to 2 spaces.  This works because the anti-quote\n  is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n  rm -f /var/run/opengl-driver\n  ln -sf 123 /var/run/opengl-driver\n\n  rm -f /var/log/slim.log\n   \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf  \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin         \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/          # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\ncut -d $'\\t' -f 1\nending dollar $$\n"
diff --git a/third_party/nix/src/tests/lang/eval-okay-replacestrings.exp b/third_party/nix/src/tests/lang/eval-okay-replacestrings.exp
deleted file mode 100644
index 72e8274d8c..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-replacestrings.exp
+++ /dev/null
@@ -1 +0,0 @@
-[ "faabar" "fbar" "fubar" "faboor" "fubar" "XaXbXcX" "X" "a_b" ]
diff --git a/third_party/nix/src/tests/lang/eval-okay-sort.exp b/third_party/nix/src/tests/lang/eval-okay-sort.exp
deleted file mode 100644
index 148b935163..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-sort.exp
+++ /dev/null
@@ -1 +0,0 @@
-[ [ 42 77 147 249 483 526 ] [ 526 483 249 147 77 42 ] [ "bar" "fnord" "foo" "xyzzy" ] [ { key = 1; value = "foo"; } { key = 1; value = "fnord"; } { key = 2; value = "bar"; } ] ]
diff --git a/third_party/nix/src/tests/lang/eval-okay-sort.nix b/third_party/nix/src/tests/lang/eval-okay-sort.nix
deleted file mode 100644
index 8299c3a4a3..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-sort.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-with builtins;
-
-[ (sort lessThan [ 483 249 526 147 42 77 ])
-  (sort (x: y: y < x) [ 483 249 526 147 42 77 ])
-  (sort lessThan [ "foo" "bar" "xyzzy" "fnord" ])
-  (sort (x: y: x.key < y.key)
-    [ { key = 1; value = "foo"; } { key = 2; value = "bar"; } { key = 1; value = "fnord"; } ]) 
-]
diff --git a/third_party/nix/src/tests/lang/eval-okay-types.exp b/third_party/nix/src/tests/lang/eval-okay-types.exp
deleted file mode 100644
index 882c16dbfe..0000000000
--- a/third_party/nix/src/tests/lang/eval-okay-types.exp
+++ /dev/null
@@ -1 +0,0 @@
-[ true false true true false true false true false true true true true true true true true true true true false true true true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]
diff --git a/third_party/nix/src/tests/lang/evalstore-okay-context-introspection.nix b/third_party/nix/src/tests/lang/evalstore-okay-context-introspection.nix
deleted file mode 100644
index d11aad38c7..0000000000
--- a/third_party/nix/src/tests/lang/evalstore-okay-context-introspection.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-let
-  drv = derivation {
-    name = "fail";
-    builder = "/bin/false";
-    system = "x86_64-linux";
-    outputs = [ "out" "foo" ];
-  };
-
-  path = "${./evalstore-okay-context-introspection.nix}";
-
-  desired-context = {
-    "${builtins.unsafeDiscardStringContext path}" = {
-      path = true;
-    };
-    "${builtins.unsafeDiscardStringContext drv.drvPath}" = {
-      outputs = [ "foo" "out" ];
-      allOutputs = true;
-    };
-  };
-
-  legit-context = builtins.getContext "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
-
-  constructed-context = builtins.getContext (builtins.appendContext "" desired-context);
-in legit-context == constructed-context
diff --git a/third_party/nix/src/tests/lang/evalstore-okay-context.exp b/third_party/nix/src/tests/lang/evalstore-okay-context.exp
deleted file mode 100644
index f8088f9e17..0000000000
--- a/third_party/nix/src/tests/lang/evalstore-okay-context.exp
+++ /dev/null
@@ -1 +0,0 @@
-"foo evalstore-okay-context.nix bar"
diff --git a/third_party/nix/src/tests/lang/evalstore-okay-context.nix b/third_party/nix/src/tests/lang/evalstore-okay-context.nix
deleted file mode 100644
index 90f82abe1c..0000000000
--- a/third_party/nix/src/tests/lang/evalstore-okay-context.nix
+++ /dev/null
@@ -1,6 +0,0 @@
-let s = "foo ${builtins.substring 33 100 (baseNameOf "${./evalstore-okay-context.nix}")} bar";
-in
-  if s != "foo evalstore-okay-context.nix bar"
-  then abort "context not discarded"
-  else builtins.unsafeDiscardStringContext s
-
diff --git a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-1.nix b/third_party/nix/src/tests/lang/parse-fail-dup-attrs-1.nix
deleted file mode 100644
index e590e8a04e..0000000000
--- a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-1.nix
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  x = 123;
-  y = 456;
-  x = 789;
-}
diff --git a/third_party/nix/src/tests/language-tests.cc b/third_party/nix/src/tests/language-tests.cc
deleted file mode 100644
index 46e8c6ea80..0000000000
--- a/third_party/nix/src/tests/language-tests.cc
+++ /dev/null
@@ -1,290 +0,0 @@
-// This file defines the language test suite. Language tests are run
-// by evaluating a small snippet of Nix code (against a fake store),
-// serialising it to a string and comparing that to a known output.
-//
-// This test suite is a port of the previous language integration test
-// suite, and it's previous structure is retained.
-//
-// Test cases are written in nix files under lang/, following one of
-// four possible filename patterns which trigger different behaviours:
-//
-// 1. parse-fail-*.nix: These files contain expressions which should
-//    cause a parser failure.
-//
-// 2. parse-okay-*.nix: These files contain expressions which should
-//    parse fine.
-//
-// 3. eval-fail-*.nix: These files contain expressions which should
-//    parse, but fail to evaluate.
-//
-// 4. eval-okay-*.nix: These files contain expressions which should
-//    parse and evaluate fine. They have accompanying .exp files which
-//    contain the expected string representation of the evaluation.
-
-#include <algorithm>
-#include <filesystem>
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <memory>
-#include <optional>
-#include <sstream>
-#include <string>
-
-#include <absl/strings/ascii.h>
-#include <absl/strings/match.h>
-#include <absl/strings/str_cat.h>
-#include <absl/strings/str_split.h>
-#include <absl/strings/string_view.h>
-#include <glog/logging.h>
-#include <gtest/gtest-param-test.h>
-#include <gtest/gtest.h>
-#include <gtest/internal/gtest-param-util.h>
-
-#include "libexpr/eval-inline.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/nixexpr.hh"
-#include "nix_config.h"
-#include "tests/dummy-store.hh"
-#include "tests/store-util.hh"
-
-namespace nix::tests {
-namespace {
-
-// List all the language test .nix files matching the given prefix.
-std::vector<std::filesystem::path> TestFilesFor(absl::string_view prefix) {
-  std::vector<std::filesystem::path> matching_files;
-
-  auto dir_iter =
-      std::filesystem::directory_iterator(NIX_SRC_DIR "/src/tests/lang");
-
-  for (auto& entry : dir_iter) {
-    if (!entry.is_regular_file()) {
-      continue;
-    }
-
-    auto filename = entry.path().filename().string();
-    if (absl::StartsWith(filename, prefix) &&
-        absl::EndsWith(filename, ".nix")) {
-      matching_files.push_back(entry.path());
-    }
-  }
-
-  std::sort(matching_files.begin(), matching_files.end());
-  return matching_files;
-}
-
-// Construct a test name from a path parameter, re-casing its name to
-// PascalCase. Googletest only accepts alphanumeric test-names, but
-// the file names are in kebab-case.
-std::string TestNameFor(
-    const testing::TestParamInfo<std::filesystem::path>& info) {
-  std::string name;
-
-  for (auto part :
-       absl::StrSplit(info.param.stem().string(), '-', absl::SkipEmpty())) {
-    std::string part_owned(part);
-    part_owned[0] = absl::ascii_toupper(part_owned[0]);
-    absl::StrAppend(&name, part_owned);
-  }
-
-  return name;
-}
-
-// Load the expected output of a given test as a string.
-std::string ExpectedOutputFor(absl::string_view stem) {
-  std::filesystem::path path(
-      absl::StrCat(NIX_SRC_DIR, "/src/tests/lang/", stem, ".exp"));
-
-  EXPECT_TRUE(std::filesystem::exists(path))
-      << stem << ": expected output file should exist";
-
-  std::ifstream input(path);
-  std::stringstream buffer;
-  buffer << input.rdbuf();
-  return std::string(absl::StripTrailingAsciiWhitespace(buffer.str()));
-}
-
-}  // namespace
-
-using nix::tests::DummyStore;
-
-class NixEnvironment : public testing::Environment {
- public:
-  void SetUp() override {
-    google::InitGoogleLogging("--logtostderr=false");
-    nix::expr::InitGC();
-  }
-};
-
-::testing::Environment* const nix_env =
-    ::testing::AddGlobalTestEnvironment(new NixEnvironment);
-
-class ParserFailureTest : public testing::TestWithParam<std::filesystem::path> {
-};
-
-// Test pattern for files that should fail to parse.
-TEST_P(ParserFailureTest, Fails) {
-  std::shared_ptr<Store> store = std::make_shared<DummyStore>();
-  EvalState state({}, ref<Store>(store));
-  auto path = GetParam();
-
-  // There are multiple types of exceptions that the parser can throw,
-  // and the tests don't define which one they expect, so we need to
-  // allow all of these - but fail on other errors.
-  try {
-    state.parseExprFromFile(GetParam().string());
-    FAIL() << path.stem().string() << ": parsing should not succeed";
-  } catch (ParseError e) {
-    SUCCEED();
-  } catch (UndefinedVarError e) {
-    SUCCEED();
-  } catch (const std::exception& e) {
-    FAIL() << path.stem().string()
-           << ": unexpected parser exception: " << e.what();
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(Parser, ParserFailureTest,
-                         testing::ValuesIn(TestFilesFor("parse-fail-")),
-                         TestNameFor);
-
-class ParserSuccessTest : public testing::TestWithParam<std::filesystem::path> {
-};
-
-// Test pattern for files that should parse successfully.
-TEST_P(ParserSuccessTest, Parses) {
-  std::shared_ptr<Store> store = std::make_shared<DummyStore>();
-  EvalState state({}, ref<Store>(store));
-  auto path = GetParam();
-
-  EXPECT_NO_THROW(state.parseExprFromFile(GetParam().string()))
-      << path.stem().string() << ": parsing should succeed";
-
-  SUCCEED();
-}
-
-INSTANTIATE_TEST_SUITE_P(Parser, ParserSuccessTest,
-                         testing::ValuesIn(TestFilesFor("parse-okay-")),
-                         TestNameFor);
-
-class EvalFailureTest : public testing::TestWithParam<std::filesystem::path> {};
-
-// Test pattern for files that should fail to evaluate.
-TEST_P(EvalFailureTest, Fails) {
-  std::shared_ptr<Store> store = std::make_shared<DummyStore>();
-  EvalState state({}, ref<Store>(store));
-  auto path = GetParam();
-
-  Expr* expr = nullptr;
-  EXPECT_NO_THROW(expr = state.parseExprFromFile(GetParam().string()))
-      << path.stem().string() << ": should parse successfully";
-
-  // Again, there are multiple expected exception types and the tests
-  // don't specify which ones they are looking for.
-  try {
-    Value result;
-    state.eval(expr, result);
-    state.forceValue(result);
-    std::cout << result;
-    FAIL() << path.stem().string() << ": evaluating should not succeed";
-  } catch (AssertionError e) {
-    SUCCEED();
-  } catch (EvalError e) {
-    SUCCEED();
-  } catch (SysError e) {
-    SUCCEED();
-  } catch (ParseError /* sic! */ e) {
-    SUCCEED();
-  } catch (const std::exception& e) {
-    FAIL() << path.stem().string()
-           << ": unexpected evaluator exception: " << e.what();
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(Eval, EvalFailureTest,
-                         testing::ValuesIn(TestFilesFor("eval-fail-")),
-                         TestNameFor);
-
-class EvalSuccessTest : public testing::TestWithParam<std::filesystem::path> {};
-
-// Test pattern for files that should evaluate successfully.
-TEST_P(EvalSuccessTest, Succeeds) {
-  std::shared_ptr<Store> store = std::make_shared<DummyStore>();
-  EvalState state({}, ref<Store>(store));
-  auto path = GetParam();
-
-  Expr* expr = nullptr;
-  ASSERT_NO_THROW(expr = state.parseExprFromFile(GetParam().string()))
-      << path.stem().string() << ": should parse successfully";
-
-  Value result;
-
-  ASSERT_NO_THROW({
-    state.eval(expr, result);
-    state.forceValueDeep(result);
-  }) << path.stem().string()
-     << ": should evaluate successfully";
-
-  auto expected = ExpectedOutputFor(path.stem().string());
-  std::ostringstream value_str;
-  value_str << result;
-
-  EXPECT_EQ(expected, value_str.str()) << "evaluator output should match";
-}
-
-INSTANTIATE_TEST_SUITE_P(Eval, EvalSuccessTest,
-                         testing::ValuesIn(TestFilesFor("eval-okay-")),
-                         TestNameFor);
-
-class BlankStoreTest : public nix::StoreTest {
-  virtual void TestBody() override{};
-};
-
-class EvalStoreSuccessTest
-    : public testing::TestWithParam<std::filesystem::path> {
- public:
-  virtual void TearDown() { store_test_.TearDown(); }
-
-  absl::StatusOr<std::unique_ptr<nix::LocalStore>> OpenTemporaryStore() {
-    return store_test_.OpenTemporaryStore();
-  }
-
- private:
-  BlankStoreTest store_test_;
-};
-
-// Test pattern for files that should evaluate successfully but require a real
-// store.
-TEST_P(EvalStoreSuccessTest, Succeeds) {
-  absl::StatusOr<std::unique_ptr<nix::LocalStore>> store_ =
-      OpenTemporaryStore();
-  CHECK(store_.ok()) << "failed to open temporary store";
-  ref<Store> store = ref<Store>(store_->release());
-  EvalState state({}, store);
-  auto path = GetParam();
-
-  Expr* expr = nullptr;
-  ASSERT_NO_THROW(expr = state.parseExprFromFile(GetParam().string()))
-      << path.stem().string() << ": should parse successfully";
-
-  Value result;
-
-  ASSERT_NO_THROW({
-    state.eval(expr, result);
-    state.forceValueDeep(result);
-  }) << path.stem().string()
-     << ": should evaluate successfully";
-
-  auto expected = ExpectedOutputFor(path.stem().string());
-  std::ostringstream value_str;
-  value_str << result;
-
-  EXPECT_EQ(expected, value_str.str()) << "evaluator output should match";
-}
-
-INSTANTIATE_TEST_SUITE_P(Eval, EvalStoreSuccessTest,
-                         testing::ValuesIn(TestFilesFor("evalstore-okay-")),
-                         TestNameFor);
-
-}  // namespace nix::tests
diff --git a/third_party/nix/src/tests/references_test.cc b/third_party/nix/src/tests/references_test.cc
deleted file mode 100644
index 8dcb3ed37a..0000000000
--- a/third_party/nix/src/tests/references_test.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "libstore/references.hh"
-
-#include <cstdio>
-#include <fstream>
-#include <ostream>
-#include <unordered_set>
-
-#include <absl/strings/str_format.h>
-#include <gtest/gtest.h>
-#include <rapidcheck.h>
-#include <rapidcheck/gtest.h>
-
-#include "libutil/hash.hh"
-
-class ReferencesTest : public ::testing::Test {};
-
-namespace nix {
-
-TEST(ReferencesTest, ScanForOneReferenceNotFound) {
-  char path[] = "store_XXXXXXX";
-  auto f = mkstemp(path);
-
-  auto hash = hashString(htSHA256, "foo");
-  auto ref = absl::StrFormat("/nix/store/%s-foo", hash.ToStorePathHash());
-
-  HashResult hr;
-  auto result = scanForReferences(path, {ref}, hr);
-
-  ASSERT_EQ(result.find(ref), result.end());
-
-  EXPECT_EQ(close(f), 0);
-}
-
-TEST(ReferencesTest, ScanForOneReferenceFound) {
-  char path[] = "store_XXXXXXX";
-  auto f = mkstemp(path);
-
-  auto hash = hashString(htSHA256, "foo");
-  auto ref = absl::StrFormat("/nix/store/%s-foo", hash.ToStorePathHash());
-
-  EXPECT_GT(write(f, ref.c_str(), sizeof(char) * ref.size()), 0);
-
-  HashResult hr;
-  auto result = scanForReferences(path, {ref}, hr);
-
-  ASSERT_NE(result.find(ref), result.end());
-
-  ASSERT_EQ(close(f), 0);
-}
-
-RC_GTEST_PROP(ReferencesTest, ScanForReferences,
-              (std::unordered_set<std::string> strs)) {
-  char path[] = "store_XXXXXXX";
-  auto f = mkstemp(path);
-
-  PathSet refs;
-  for (const auto& s : strs) {
-    auto hash = hashString(htSHA256, s);
-    auto ref = absl::StrFormat("/nix/store/%s-foo", hash.ToStorePathHash());
-    refs.insert(ref);
-    RC_ASSERT(write(f, ref.c_str(), sizeof(char) * ref.size()) > 0);
-  }
-
-  HashResult hr;
-  auto result = scanForReferences(path, refs, hr);
-
-  for (const auto& ref : refs) {
-    RC_ASSERT(result.find(ref) != result.end());
-  }
-
-  RC_ASSERT(close(f) == 0);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/tests/status_helpers.h b/third_party/nix/src/tests/status_helpers.h
deleted file mode 100644
index ca596dbb52..0000000000
--- a/third_party/nix/src/tests/status_helpers.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#pragma once
-
-#include <absl/status/status.h>
-#include <absl/status/statusor.h>
-#include <absl/strings/str_cat.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-namespace testing {
-
-/*
- * This file contains gtest matchers for absl::Status.
- *
- * Example usage:
- *
- *   EXPECT_OK(status); -- fails the test if 'status' is an error
- *   ASSERT_OK(status); -- instantly fails the test if error
- *
- *   using ::testing::IsStatusCode;
- *   EXPECT_THAT(status, IsStatusCode(absl::StatusCode::kInternal));
- */
-
-namespace nix_internal {
-
-using ::testing::MakeMatcher;
-using ::testing::Matcher;
-using ::testing::MatcherInterface;
-using ::testing::MatchResultListener;
-
-MATCHER_P(IsStatusCode, code, "") { return arg.code() == code; }
-
-class StatusCodeMatcher {
- public:
-  StatusCodeMatcher(absl::StatusCode code) : code_(code) {}
-
-  // Match on absl::Status.
-  template <class T,
-            typename std::enable_if<std::is_same<T, absl::Status>::value,
-                                    int>::type int_ = 0>
-  bool MatchAndExplain(const T& status,
-                       MatchResultListener* /* listener */) const {
-    return status.code() == code_;
-  }
-
-  // Match on absl::StatusOr.
-  //
-  // note: I check for the return value of ConsumeValueOrDie because it's the
-  // only non-overloaded member I could figure out how to select. Checking for
-  // the presence of .status() didn't work because it's overloaded, so
-  // std::invoke_result can't pick which overload to use.
-  template <class T,
-            typename std::enable_if<
-                std::is_same<typename std::invoke_result<
-                                 decltype(&T::ConsumeValueOrDie), T>::type,
-                             typename T::value_type>::value,
-                int>::type int_ = 0>
-  bool MatchAndExplain(const T& statusor,
-                       MatchResultListener* /* listener */) const {
-    return statusor.status().code() == code_;
-  }
-
-  void DescribeTo(std::ostream* os) const { *os << "is " << code_; }
-
-  void DescribeNegationTo(std::ostream* os) const { *os << "isn't " << code_; }
-
- private:
-  absl::StatusCode code_;
-};
-
-}  // namespace nix_internal
-
-PolymorphicMatcher<nix_internal::StatusCodeMatcher> IsStatusCode(
-    absl::StatusCode code) {
-  return MakePolymorphicMatcher(nix_internal::StatusCodeMatcher(code));
-}
-
-#define EXPECT_OK(status) \
-  EXPECT_THAT((status), testing::IsStatusCode(absl::StatusCode::kOk))
-
-#define ASSERT_OK(status) \
-  ASSERT_THAT((status), testing::IsStatusCode(absl::StatusCode::kOk))
-
-}  // namespace testing
diff --git a/third_party/nix/src/tests/store-api-test.cc b/third_party/nix/src/tests/store-api-test.cc
deleted file mode 100644
index 259e4b991b..0000000000
--- a/third_party/nix/src/tests/store-api-test.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "libstore/store-api.hh"
-
-#include <gtest/gtest.h>
-#include <rapidcheck/Assertions.h>
-#include <rapidcheck/gtest.h>
-
-#include "libproto/worker.pb.h"
-#include "tests/arbitrary.hh"
-
-namespace nix {
-
-class BuildResultTest : public ::testing::Test {};
-
-RC_GTEST_PROP(BuildResultTest, StatusToFromProtoRoundTrip,
-              (BuildResult::Status && status)) {
-  BuildResult br;
-  br.status = status;
-
-  auto proto_status = br.status_to_proto();
-  nix::proto::BuildResult br_proto;
-  br_proto.set_status(proto_status);
-
-  auto result = BuildResult::FromProto(br_proto);
-
-  RC_ASSERT(result.value().status == status);
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/tests/store-util.hh b/third_party/nix/src/tests/store-util.hh
deleted file mode 100644
index b31bb0edcb..0000000000
--- a/third_party/nix/src/tests/store-util.hh
+++ /dev/null
@@ -1,76 +0,0 @@
-#pragma once
-
-#include <filesystem>
-
-#include <absl/status/statusor.h>
-#include <absl/strings/escaping.h>
-#include <glog/logging.h>
-#include <sys/random.h>
-
-#include "libstore/local-store.hh"
-
-namespace nix {
-
-class StoreTest : public ::testing::Test {
- public:
-  virtual void TearDown() {
-    for (auto fn : cleanup_funcs_) {
-      try {
-        fn();
-      } catch (std::exception e) {
-        LOG(ERROR) << e.what();
-      }
-    }
-  }
-
-  absl::StatusOr<std::filesystem::path> OpenTempDir(
-      std::filesystem::path parent = std::filesystem::temp_directory_path()) {
-    for (;;) {
-      constexpr int kByteCnt = 9;
-      std::array<char, kByteCnt> randBytes;
-      if (getrandom(randBytes.data(), kByteCnt, 0) < 0) {
-        return absl::InternalError("getrandom() failed");
-      }
-      std::string suffix = absl::WebSafeBase64Escape(
-          absl::string_view(randBytes.data(), kByteCnt));
-      CHECK(suffix != "");
-
-      // Workaround for stdlib bug: use .assign() and ::errc
-      // https://stackoverflow.com/a/52401295/1210278
-      std::error_code ec_exists;
-      ec_exists.assign(EEXIST, std::system_category());
-
-      std::error_code ec;
-      std::filesystem::path candidate =
-          parent / absl::StrCat("nixtest-", suffix);
-      if (std::filesystem::create_directory(candidate, ec)) {
-        cleanup_funcs_.push_back(
-            [candidate]() { std::filesystem::remove_all(candidate); });
-        return candidate;
-      } else if (ec == ec_exists || ec == std::errc::file_exists) {
-        // Directory existed, retry
-        continue;
-      } else {
-        return absl::InternalError(absl::StrCat(
-            "could not create dir ", candidate.c_str(), ": ", ec.message()));
-      }
-    }
-  }
-
-  absl::StatusOr<std::unique_ptr<nix::LocalStore>> OpenTemporaryStore() {
-    absl::StatusOr<std::filesystem::path> storePath = OpenTempDir();
-    if (!storePath.ok()) {
-      return storePath.status();
-    }
-
-    nix::Store::Params params;
-    params["root"] = *storePath;
-
-    return std::make_unique<nix::LocalStore>(params);
-  }
-
- private:
-  std::vector<std::function<void(void)>> cleanup_funcs_;
-};
-
-}  // namespace nix
diff --git a/third_party/nix/src/tests/store_tests.cc b/third_party/nix/src/tests/store_tests.cc
deleted file mode 100644
index a6ffb715a9..0000000000
--- a/third_party/nix/src/tests/store_tests.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-#include <filesystem>
-
-#include <absl/container/btree_map.h>
-#include <absl/container/flat_hash_map.h>
-#include <absl/strings/escaping.h>
-#include <glog/logging.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <sys/random.h>
-
-#include "libstore/binary-cache-store.hh"
-#include "libstore/mock-binary-cache-store.hh"
-#include "tests/store-util.hh"
-
-using ::testing::HasSubstr;
-
-namespace nix {
-
-MakeError(InjectedError, Error);
-
-class BinaryCacheStoreTest : public StoreTest {};
-
-constexpr absl::string_view kXZHeader = "7zXZ";
-
-constexpr absl::string_view kRootFileName = "myRootFile";
-constexpr absl::string_view kDep1FileName = "dep1";
-constexpr absl::string_view kDep1FileContents = "==dep1 contents==";
-constexpr absl::string_view kDep1NarCache =
-    "nar/0hfdc95cy6mxi4c15pp0frdf97r7yvd8c141qzvpms2f8x17p2ig.nar.xz";
-constexpr absl::string_view kBogusPath =
-    "/nix/store/g1ghizdg18k0d00000000000000z3v32-doesNotExist";
-
-struct TestTree {
-  Path rootPath;
-  Path dep1Path;
-};
-
-TestTree AddTestTreeToStore(Store& store) {
-  TestTree results;
-  results.rootPath =
-      store.addTextToStore(std::string(kRootFileName), "1", PathSet());
-
-  PathSet onlyRoot;
-  onlyRoot.insert(results.rootPath);
-  results.dep1Path = store.addTextToStore(
-      std::string(kDep1FileName), std::string(kDep1FileContents), onlyRoot);
-
-  return results;
-}
-
-TEST_F(BinaryCacheStoreTest, BasicStorage) {
-  MockBinaryCacheStore::Params params;
-  MockBinaryCacheStore store(params);
-
-  store.init();
-
-  auto tree = AddTestTreeToStore(store);
-
-  EXPECT_TRUE(store.isValidPath(tree.rootPath));
-  EXPECT_TRUE(store.isValidPath(tree.dep1Path));
-
-  StringSink sink;
-  store.narFromPath(tree.dep1Path, sink);
-  EXPECT_THAT(*sink.s, HasSubstr(kDep1FileContents));
-
-  EXPECT_THAT(*store.BinaryCacheStore::getFile(Path(kDep1NarCache)),
-              HasSubstr(kXZHeader));
-}
-
-TEST_F(BinaryCacheStoreTest, BasicErrors) {
-  MockBinaryCacheStore::Params params;
-  MockBinaryCacheStore store(params);
-
-  store.init();
-
-  auto tree = AddTestTreeToStore(store);
-  store.PrepareErrorInjection(std::string(kDep1NarCache),
-                              []() { throw InjectedError("injected"); });
-
-  {
-    StringSink sink;
-    EXPECT_THROW(store.narFromPath(tree.dep1Path, sink), InjectedError);
-  }
-  {
-    StringSink sink;
-    EXPECT_THROW(store.narFromPath(std::string(kBogusPath), sink),
-                 NoSuchBinaryCacheFile);
-  }
-}
-
-// ./tests/add.sh
-TEST_F(StoreTest, AddFileHashes) {
-  auto store_ = OpenTemporaryStore();
-  CHECK(store_.ok()) << "failed to open temporary store";
-  nix::Store* store = store_->release();
-  nix::Path dataPath = NIX_SRC_DIR "/src/tests/lang/data";
-  std::string dataName = "data";
-
-  nix::Path path1 = store->addToStore(dataName, dataPath);
-
-  nix::Path path2 = store->addToStore(dataName, dataPath, /*recursive=*/true,
-                                      HashType::htSHA256);
-
-  EXPECT_EQ(path1, path2) << "nix-store --add and --add-fixed mismatch";
-
-  nix::Path path3 = store->addToStore(dataName, dataPath, /*recursive=*/false,
-                                      HashType::htSHA256);
-  EXPECT_NE(path1, path3);
-
-  nix::Path path4 =
-      store->addToStore(dataName, dataPath, false, HashType::htSHA1);
-  EXPECT_NE(path1, path4);
-
-  auto info1 = store->queryPathInfo(store->followLinksToStorePath(path1));
-  ASSERT_EQ(info1->narHash.type, HashType::htSHA256);
-
-  Hash h = nix::hashPath(HashType::htSHA256, dataPath).first;
-
-  EXPECT_EQ(info1->narHash.to_string(), h.to_string());
-}
-
-}  // namespace nix
diff --git a/third_party/nix/src/tests/value-to-json.cc b/third_party/nix/src/tests/value-to-json.cc
deleted file mode 100644
index 4542742530..0000000000
--- a/third_party/nix/src/tests/value-to-json.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-#include "libexpr/value-to-json.hh"
-
-#include <set>
-#include <sstream>
-
-#include <gtest/gtest.h>
-
-#include "libexpr/value-to-xml.hh"
-#include "libexpr/value.hh"
-#include "libstore/store-api.hh"
-#include "tests/dummy-store.hh"
-
-class ValueTest : public ::testing::Test {
- protected:
-  static void SetUpTestCase() { nix::expr::InitGC(); }
-
-  static void TearDownTestCase() {}
-};
-
-class JSONValueTest : public ValueTest {};
-class XMLValueTest : public ValueTest {};
-
-namespace nix {
-
-using nix::tests::DummyStore;
-
-TEST_F(JSONValueTest, null) {
-  std::stringstream ss;
-  Value v;
-  PathSet ps;
-  std::shared_ptr<Store> store = std::make_shared<DummyStore>();
-  EvalState s({}, ref<Store>(store));
-
-  mkNull(v);
-  printValueAsJSON(s, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), "null");
-}
-
-TEST_F(JSONValueTest, BoolFalse) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkBool(v, false);
-  printValueAsJSON(s, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), "false");
-}
-
-TEST_F(JSONValueTest, BoolTrue) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkBool(v, true);
-  printValueAsJSON(s, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), "true");
-}
-
-TEST_F(JSONValueTest, IntPositive) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkInt(v, 100);
-  printValueAsJSON(s, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), "100");
-}
-
-TEST_F(JSONValueTest, IntNegative) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkInt(v, -100);
-  printValueAsJSON(s, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), "-100");
-}
-
-TEST_F(JSONValueTest, String) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkString(v, "test");
-  printValueAsJSON(s, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), "\"test\"");
-}
-
-TEST_F(JSONValueTest, StringQuotes) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkString(v, "test\"");
-  printValueAsJSON(s, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), "\"test\\\"\"");
-}
-
-TEST_F(JSONValueTest, Path) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkPathNoCopy(v, "/exists-for-tests");
-  printValueAsJSON(s, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\"");
-}
-
-TEST_F(JSONValueTest, PathNoCopy) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkPathNoCopy(v, "/exists-for-tests");
-  printValueAsJSON(s, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\"");
-}
-
-/*
- * Value to XMl tests
- */
-
-#define XML(v) \
-  ("<?xml version='1.0' encoding='utf-8'?>\n<expr>\n" v "\n</expr>\n")
-
-TEST_F(XMLValueTest, null) {
-  std::stringstream ss;
-  Value v;
-  PathSet ps;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({}, ref<Store>(store));
-
-  mkNull(v);
-  printValueAsXML(s, true, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), XML("  <null />"));
-}
-
-TEST_F(XMLValueTest, BoolFalse) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkBool(v, false);
-  printValueAsXML(s, true, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), XML("  <bool value=\"false\" />"));
-}
-
-TEST_F(XMLValueTest, BoolTrue) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkBool(v, true);
-  printValueAsXML(s, true, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), XML("  <bool value=\"true\" />"));
-}
-
-TEST_F(XMLValueTest, IntPositive) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkInt(v, 100);
-  printValueAsXML(s, true, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), XML("  <int value=\"100\" />"));
-}
-
-TEST_F(XMLValueTest, IntNegative) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkInt(v, -100);
-  printValueAsXML(s, true, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), XML("  <int value=\"-100\" />"));
-}
-
-TEST_F(XMLValueTest, String) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkString(v, "test-value");
-  printValueAsXML(s, true, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), XML("  <string value=\"test-value\" />"));
-}
-
-TEST_F(XMLValueTest, StringQuotes) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkString(v, "test-value\"");
-  printValueAsXML(s, true, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), XML("  <string value=\"test-value&quot;\" />"));
-}
-
-/*
- * FIXME: This function returns the original input path while the JSON version
- * of the same actually touches the store to create a /nix/store path.
- */
-TEST_F(XMLValueTest, Path) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkPath(v, "some-path");
-  printValueAsXML(s, true, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), XML("  <path value=\"some-path\" />"));
-}
-
-/*
- * FIXME: This function returns the original input path while the JSON version
- * of the same actually touches the store to create a /nix/store path.
- */
-TEST_F(XMLValueTest, PathNoCopy) {
-  std::stringstream ss;
-  auto store = std::make_shared<DummyStore>();
-  EvalState s({"."}, ref<Store>(store));
-  Value v;
-  PathSet ps;
-
-  mkPathNoCopy(v, "some-other-path");
-  printValueAsXML(s, true, true, v, ss, ps);
-  ASSERT_EQ(ss.str(), XML("  <path value=\"some-other-path\" />"));
-}
-}  // namespace nix
diff --git a/third_party/nix/test-vm.nix b/third_party/nix/test-vm.nix
deleted file mode 100644
index e5f8690fcb..0000000000
--- a/third_party/nix/test-vm.nix
+++ /dev/null
@@ -1,19 +0,0 @@
-{ depot, pkgs, ... }:
-
-let
-  configuration = { ... }: {
-    imports = [
-      "${pkgs.path}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix"
-    ];
-
-    nix.package = depot.third_party.nix;
-
-    virtualisation.qemu.options = [ "-nographic" "-curses" ];
-
-    nix.nixPath = [
-      "depot=${depot.path}"
-    ];
-  };
-
-  system = depot.third_party.nixos { inherit configuration; };
-in system.vm
diff --git a/third_party/nix/tests/add.sh b/third_party/nix/tests/add.sh
deleted file mode 100644
index e26e05843d..0000000000
--- a/third_party/nix/tests/add.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-source common.sh
-
-path1=$(nix-store --add ./dummy)
-echo $path1
-
-path2=$(nix-store --add-fixed sha256 --recursive ./dummy)
-echo $path2
-
-if test "$path1" != "$path2"; then
-    echo "nix-store --add and --add-fixed mismatch"
-    exit 1
-fi    
-
-path3=$(nix-store --add-fixed sha256 ./dummy)
-echo $path3
-test "$path1" != "$path3" || exit 1
-
-path4=$(nix-store --add-fixed sha1 --recursive ./dummy)
-echo $path4
-test "$path1" != "$path4" || exit 1
-
-hash1=$(nix-store -q --hash $path1)
-echo $hash1
-
-hash2=$(nix-hash --type sha256 --base32 ./dummy)
-echo $hash2
-
-test "$hash1" = "sha256:$hash2"
diff --git a/third_party/nix/tests/binary-cache.sh b/third_party/nix/tests/binary-cache.sh
deleted file mode 100644
index eb58ae7c12..0000000000
--- a/third_party/nix/tests/binary-cache.sh
+++ /dev/null
@@ -1,170 +0,0 @@
-source common.sh
-
-clearStore
-clearCache
-
-# Create the binary cache.
-outPath=$(nix-build dependencies.nix --no-out-link)
-
-nix copy --to file://$cacheDir $outPath
-
-
-basicTests() {
-
-    # By default, a binary cache doesn't support "nix-env -qas", but does
-    # support installation.
-    clearStore
-    clearCacheCache
-
-    nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "---"
-
-    nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath
-
-    [ -x $outPath/program ]
-
-
-    # But with the right configuration, "nix-env -qas" should also work.
-    clearStore
-    clearCacheCache
-    echo "WantMassQuery: 1" >> $cacheDir/nix-cache-info
-
-    nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S"
-    nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S"
-
-    x=$(nix-env -f dependencies.nix -qas \* --prebuilt-only)
-    [ -z "$x" ]
-
-    nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath
-
-    nix-store --check-validity $outPath
-    nix-store -qR $outPath | grep input-2
-
-    echo "WantMassQuery: 0" >> $cacheDir/nix-cache-info
-}
-
-
-# Test LocalBinaryCacheStore.
-basicTests
-
-
-# Test HttpBinaryCacheStore.
-export _NIX_FORCE_HTTP_BINARY_CACHE_STORE=1
-basicTests
-
-
-# Test whether Nix notices if the NAR doesn't match the hash in the NAR info.
-clearStore
-
-nar=$(ls $cacheDir/nar/*.nar.xz | head -n1)
-mv $nar $nar.good
-mkdir -p $TEST_ROOT/empty
-nix-store --dump $TEST_ROOT/empty | xz > $nar
-
-nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
-grep -q "hash mismatch" $TEST_ROOT/log
-
-mv $nar.good $nar
-
-
-# Test whether this unsigned cache is rejected if the user requires signed caches.
-clearStore
-clearCacheCache
-
-if nix-store --substituters "file://$cacheDir" -r $outPath; then
-    echo "unsigned binary cache incorrectly accepted"
-    exit 1
-fi
-
-
-# Test whether fallback works if a NAR has disappeared. This does not require --fallback.
-clearStore
-
-mv $cacheDir/nar $cacheDir/nar2
-
-nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result
-
-mv $cacheDir/nar2 $cacheDir/nar
-
-
-# Test whether fallback works if a NAR is corrupted. This does require --fallback.
-clearStore
-
-mv $cacheDir/nar $cacheDir/nar2
-mkdir $cacheDir/nar
-for i in $(cd $cacheDir/nar2 && echo *); do touch $cacheDir/nar/$i; done
-
-(! nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result)
-
-nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result --fallback
-
-rm -rf $cacheDir/nar
-mv $cacheDir/nar2 $cacheDir/nar
-
-
-# Test whether building works if the binary cache contains an
-# incomplete closure.
-clearStore
-
-rm $(grep -l "StorePath:.*dependencies-input-2" $cacheDir/*.narinfo)
-
-nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
-grep -q "copying path" $TEST_ROOT/log
-
-
-if [ -n "$HAVE_SODIUM" ]; then
-
-# Create a signed binary cache.
-clearCache
-clearCacheCache
-
-declare -a res=($(nix-store --generate-binary-cache-key test.nixos.org-1 $TEST_ROOT/sk1 $TEST_ROOT/pk1 ))
-publicKey="$(cat $TEST_ROOT/pk1)"
-
-res=($(nix-store --generate-binary-cache-key test.nixos.org-1 $TEST_ROOT/sk2 $TEST_ROOT/pk2))
-badKey="$(cat $TEST_ROOT/pk2)"
-
-res=($(nix-store --generate-binary-cache-key foo.nixos.org-1 $TEST_ROOT/sk3 $TEST_ROOT/pk3))
-otherKey="$(cat $TEST_ROOT/pk3)"
-
-_NIX_FORCE_HTTP_BINARY_CACHE_STORE= nix copy --to file://$cacheDir?secret-key=$TEST_ROOT/sk1 $outPath
-
-
-# Downloading should fail if we don't provide a key.
-clearStore
-clearCacheCache
-
-(! nix-store -r $outPath --substituters "file://$cacheDir")
-
-
-# And it should fail if we provide an incorrect key.
-clearStore
-clearCacheCache
-
-(! nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$badKey")
-
-
-# It should succeed if we provide the correct key.
-nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$otherKey $publicKey"
-
-
-# It should fail if we corrupt the .narinfo.
-clearStore
-
-cacheDir2=$TEST_ROOT/binary-cache-2
-rm -rf $cacheDir2
-cp -r $cacheDir $cacheDir2
-
-for i in $cacheDir2/*.narinfo; do
-    grep -v References $i > $i.tmp
-    mv $i.tmp $i
-done
-
-clearCacheCache
-
-(! nix-store -r $outPath --substituters "file://$cacheDir2" --trusted-public-keys "$publicKey")
-
-# If we provide a bad and a good binary cache, it should succeed.
-
-nix-store -r $outPath --substituters "file://$cacheDir2 file://$cacheDir" --trusted-public-keys "$publicKey"
-
-fi # HAVE_LIBSODIUM
diff --git a/third_party/nix/tests/brotli.sh b/third_party/nix/tests/brotli.sh
deleted file mode 100644
index a3c6e55a8f..0000000000
--- a/third_party/nix/tests/brotli.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-source common.sh
-
-clearStore
-clearCache
-
-cacheURI="file://$cacheDir?compression=br"
-
-outPath=$(nix-build dependencies.nix --no-out-link)
-
-nix copy --to $cacheURI $outPath
-
-HASH=$(nix hash-path $outPath)
-
-clearStore
-clearCacheCache
-
-nix copy --from $cacheURI $outPath --no-check-sigs
-
-HASH2=$(nix hash-path $outPath)
-
-[[ $HASH = $HASH2 ]]
diff --git a/third_party/nix/tests/build-dry.sh b/third_party/nix/tests/build-dry.sh
deleted file mode 100644
index e72533e706..0000000000
--- a/third_party/nix/tests/build-dry.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-source common.sh
-
-###################################################
-# Check that --dry-run isn't confused with read-only mode
-# https://github.com/NixOS/nix/issues/1795
-
-clearStore
-clearCache
-
-# Ensure this builds successfully first
-nix build --no-link -f dependencies.nix
-
-clearStore
-clearCache
-
-# Try --dry-run using old command first
-nix-build --no-out-link dependencies.nix --dry-run 2>&1 | grep "will be built"
-# Now new command:
-nix build -f dependencies.nix --dry-run 2>&1 | grep "will be built"
-
-# TODO: XXX: FIXME: #1793
-# Disable this part of the test until the problem is resolved:
-if [ -n "$ISSUE_1795_IS_FIXED" ]; then
-clearStore
-clearCache
-
-# Try --dry-run using new command first
-nix build -f dependencies.nix --dry-run 2>&1 | grep "will be built"
-# Now old command:
-nix-build --no-out-link dependencies.nix --dry-run 2>&1 | grep "will be built"
-fi
-
-###################################################
-# Check --dry-run doesn't create links with --dry-run
-# https://github.com/NixOS/nix/issues/1849
-clearStore
-clearCache
-
-RESULT=$TEST_ROOT/result-link
-rm -f $RESULT
-
-nix-build dependencies.nix -o $RESULT --dry-run
-
-[[ ! -h $RESULT ]] || fail "nix-build --dry-run created output link"
-
-nix build -f dependencies.nix -o $RESULT --dry-run
-
-[[ ! -h $RESULT ]] || fail "nix build --dry-run created output link"
-
-nix build -f dependencies.nix -o $RESULT
-
-[[ -h $RESULT ]]
diff --git a/third_party/nix/tests/build-hook.nix b/third_party/nix/tests/build-hook.nix
deleted file mode 100644
index 8bff0fe790..0000000000
--- a/third_party/nix/tests/build-hook.nix
+++ /dev/null
@@ -1,23 +0,0 @@
-with import ./config.nix;
-
-let
-
-  input1 = mkDerivation {
-    name = "build-hook-input-1";
-    builder = ./dependencies.builder1.sh;
-    requiredSystemFeatures = ["foo"];
-  };
-
-  input2 = mkDerivation {
-    name = "build-hook-input-2";
-    builder = ./dependencies.builder2.sh;
-  };
-
-in
-
-  mkDerivation {
-    name = "build-hook";
-    builder = ./dependencies.builder0.sh;
-    input1 = " " + input1 + "/.";
-    input2 = " ${input2}/.";
-  }
diff --git a/third_party/nix/tests/build-remote.sh b/third_party/nix/tests/build-remote.sh
deleted file mode 100644
index ddd68f327a..0000000000
--- a/third_party/nix/tests/build-remote.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-source common.sh
-
-clearStore
-
-if ! canUseSandbox; then exit; fi
-if [[ ! $SHELL =~ /nix/store ]]; then exit; fi
-
-chmod -R u+w $TEST_ROOT/store0 || true
-chmod -R u+w $TEST_ROOT/store1 || true
-rm -rf $TEST_ROOT/store0 $TEST_ROOT/store1
-
-nix build -f build-hook.nix -o $TEST_ROOT/result --max-jobs 0 \
-  --sandbox-paths /nix/store --sandbox-build-dir /build-tmp \
-  --builders "$TEST_ROOT/store0; $TEST_ROOT/store1 - - 1 1 foo" \
-  --system-features foo
-
-outPath=$TEST_ROOT/result
-
-cat $outPath/foobar | grep FOOBAR
-
-# Ensure that input1 was built on store1 due to the required feature.
-p=$(readlink -f $outPath/input-2)
-(! nix path-info --store $TEST_ROOT/store0 --all | grep dependencies.builder1.sh)
-nix path-info --store $TEST_ROOT/store1 --all | grep dependencies.builder1.sh
diff --git a/third_party/nix/tests/case-hack.sh b/third_party/nix/tests/case-hack.sh
deleted file mode 100644
index 61bf9b94bf..0000000000
--- a/third_party/nix/tests/case-hack.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-source common.sh
-
-clearStore
-
-rm -rf $TEST_ROOT/case
-
-opts="--option use-case-hack true"
-
-# Check whether restoring and dumping a NAR that contains case
-# collisions is round-tripping, even on a case-insensitive system.
-nix-store $opts  --restore $TEST_ROOT/case < case.nar
-nix-store $opts --dump $TEST_ROOT/case > $TEST_ROOT/case.nar
-cmp case.nar $TEST_ROOT/case.nar
-[ "$(nix-hash $opts --type sha256 $TEST_ROOT/case)" = "$(nix-hash --flat --type sha256 case.nar)" ]
-
-# Check whether we detect true collisions (e.g. those remaining after
-# removal of the suffix).
-touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3"
-(! nix-store $opts --dump $TEST_ROOT/case > /dev/null)
diff --git a/third_party/nix/tests/case.nar b/third_party/nix/tests/case.nar
deleted file mode 100644
index 22ff26db5a..0000000000
--- a/third_party/nix/tests/case.nar
+++ /dev/null
Binary files differdiff --git a/third_party/nix/tests/check-refs.nix b/third_party/nix/tests/check-refs.nix
deleted file mode 100644
index 9d90b09205..0000000000
--- a/third_party/nix/tests/check-refs.nix
+++ /dev/null
@@ -1,70 +0,0 @@
-with import ./config.nix;
-
-rec {
-
-  dep = import ./dependencies.nix;
-
-  makeTest = nr: args: mkDerivation ({
-    name = "check-refs-" + toString nr;
-  } // args);
-
-  src = builtins.toFile "aux-ref" "bla bla";
-
-  test1 = makeTest 1 {
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
-    inherit dep;
-  };
-
-  test2 = makeTest 2 {
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${src} $out/link";
-    inherit dep;
-  };
-
-  test3 = makeTest 3 {
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
-    allowedReferences = [];
-    inherit dep;
-  };
-
-  test4 = makeTest 4 {
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
-    allowedReferences = [dep];
-    inherit dep;
-  };
-
-  test5 = makeTest 5 {
-    builder = builtins.toFile "builder.sh" "mkdir $out";
-    allowedReferences = [];
-    inherit dep;
-  };
-
-  test6 = makeTest 6 {
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
-    allowedReferences = [];
-    inherit dep;
-  };
-
-  test7 = makeTest 7 {
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
-    allowedReferences = ["out"];
-    inherit dep;
-  };
-
-  test8 = makeTest 8 {
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${test1} $out/link";
-    inherit dep;
-  };
-
-  test9 = makeTest 9 {
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
-    inherit dep;
-    disallowedReferences = [dep];
-  };
-
-  test10 = makeTest 10 {
-    builder = builtins.toFile "builder.sh" "mkdir $out; echo $test5; ln -s $dep $out/link";
-    inherit dep test5;
-    disallowedReferences = [test5];
-  };
-
-}
diff --git a/third_party/nix/tests/check-refs.sh b/third_party/nix/tests/check-refs.sh
deleted file mode 100644
index 16bbabc409..0000000000
--- a/third_party/nix/tests/check-refs.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-source common.sh
-
-clearStore
-
-RESULT=$TEST_ROOT/result
-
-dep=$(nix-build -o $RESULT check-refs.nix -A dep)
-
-# test1 references dep, not itself.
-test1=$(nix-build -o $RESULT check-refs.nix -A test1)
-(! nix-store -q --references $test1 | grep -q $test1)
-nix-store -q --references $test1 | grep -q $dep
-
-# test2 references src, not itself nor dep.
-test2=$(nix-build -o $RESULT check-refs.nix -A test2)
-(! nix-store -q --references $test2 | grep -q $test2)
-(! nix-store -q --references $test2 | grep -q $dep)
-nix-store -q --references $test2 | grep -q aux-ref
-
-# test3 should fail (unallowed ref).
-(! nix-build -o $RESULT check-refs.nix -A test3)
-
-# test4 should succeed.
-nix-build -o $RESULT check-refs.nix -A test4
-
-# test5 should succeed.
-nix-build -o $RESULT check-refs.nix -A test5
-
-# test6 should fail (unallowed self-ref).
-(! nix-build -o $RESULT check-refs.nix -A test6)
-
-# test7 should succeed (allowed self-ref).
-nix-build -o $RESULT check-refs.nix -A test7
-
-# test8 should fail (toFile depending on derivation output).
-(! nix-build -o $RESULT check-refs.nix -A test8)
-
-# test9 should fail (disallowed reference).
-(! nix-build -o $RESULT check-refs.nix -A test9)
-
-# test10 should succeed (no disallowed references).
-nix-build -o $RESULT check-refs.nix -A test10
diff --git a/third_party/nix/tests/check-reqs.nix b/third_party/nix/tests/check-reqs.nix
deleted file mode 100644
index 41436cb48e..0000000000
--- a/third_party/nix/tests/check-reqs.nix
+++ /dev/null
@@ -1,57 +0,0 @@
-with import ./config.nix;
-
-rec {
-  dep1 = mkDerivation {
-    name = "check-reqs-dep1";
-    builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file1";
-  };
-
-  dep2 = mkDerivation {
-    name = "check-reqs-dep2";
-    builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file2";
-  };
-
-  deps = mkDerivation {
-    name = "check-reqs-deps";
-    dep1 = dep1;
-    dep2 = dep2;
-    builder = builtins.toFile "builder.sh" ''
-      mkdir $out
-      ln -s $dep1/file1 $out/file1
-      ln -s $dep2/file2 $out/file2
-    '';
-  };
-
-  makeTest = nr: allowreqs: mkDerivation {
-    name = "check-reqs-" + toString nr;
-    inherit deps;
-    builder = builtins.toFile "builder.sh" ''
-      mkdir $out
-      ln -s $deps $out/depdir1
-    '';
-    allowedRequisites = allowreqs;
-  };
-
-  # When specifying all the requisites, the build succeeds.
-  test1 = makeTest 1 [ dep1 dep2 deps ];
-
-  # But missing anything it fails.
-  test2 = makeTest 2 [ dep2 deps ];
-  test3 = makeTest 3 [ dep1 deps ];
-  test4 = makeTest 4 [ deps ];
-  test5 = makeTest 5 [];
-
-  test6 = mkDerivation {
-    name = "check-reqs";
-    inherit deps;
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $deps $out/depdir1";
-    disallowedRequisites = [dep1];
-  };
-
-  test7 = mkDerivation {
-    name = "check-reqs";
-    inherit deps;
-    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $deps $out/depdir1";
-    disallowedRequisites = [test1];
-  };
-}
diff --git a/third_party/nix/tests/check-reqs.sh b/third_party/nix/tests/check-reqs.sh
deleted file mode 100644
index e9f65fc2a6..0000000000
--- a/third_party/nix/tests/check-reqs.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-source common.sh
-
-clearStore
-
-RESULT=$TEST_ROOT/result
-
-nix-build -o $RESULT check-reqs.nix -A test1
-
-(! nix-build -o $RESULT check-reqs.nix -A test2)
-(! nix-build -o $RESULT check-reqs.nix -A test3)
-(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grep -q 'check-reqs-dep1'
-(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grep -q 'check-reqs-dep2'
-(! nix-build -o $RESULT check-reqs.nix -A test5)
-(! nix-build -o $RESULT check-reqs.nix -A test6)
-
-nix-build -o $RESULT check-reqs.nix -A test7
diff --git a/third_party/nix/tests/check.nix b/third_party/nix/tests/check.nix
deleted file mode 100644
index 56c82e565a..0000000000
--- a/third_party/nix/tests/check.nix
+++ /dev/null
@@ -1,22 +0,0 @@
-with import ./config.nix;
-
-{
-  nondeterministic = mkDerivation {
-    name = "nondeterministic";
-    buildCommand =
-      ''
-        mkdir $out
-        date +%s.%N > $out/date
-      '';
-  };
-
-  hashmismatch = import <nix/fetchurl.nix> {
-    url = "file://" + toString ./dummy;
-    sha256 = "0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73";
-  };
-
-  fetchurl = import <nix/fetchurl.nix> {
-    url = "file://" + toString ./lang/eval-okay-xml.exp.xml;
-    sha256 = "0kg4sla7ihm8ijr8cb3117fhl99zrc2bwy1jrngsfmkh8bav4m0v";
-  };
-}
diff --git a/third_party/nix/tests/check.sh b/third_party/nix/tests/check.sh
deleted file mode 100644
index bc23a6634c..0000000000
--- a/third_party/nix/tests/check.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-source common.sh
-
-clearStore
-
-nix-build dependencies.nix --no-out-link
-nix-build dependencies.nix --no-out-link --check
-
-nix-build check.nix -A nondeterministic --no-out-link
-nix-build check.nix -A nondeterministic --no-out-link --check 2> $TEST_ROOT/log || status=$?
-grep 'may not be deterministic' $TEST_ROOT/log
-[ "$status" = "104" ]
-
-clearStore
-
-nix-build dependencies.nix --no-out-link --repeat 3
-
-nix-build check.nix -A nondeterministic --no-out-link --repeat 1 2> $TEST_ROOT/log || status=$?
-[ "$status" = "1" ]
-grep 'differs from previous round' $TEST_ROOT/log
-
-path=$(nix-build check.nix -A fetchurl --no-out-link --hashed-mirrors '')
-
-chmod +w $path
-echo foo > $path
-chmod -w $path
-
-nix-build check.nix -A fetchurl --no-out-link --check --hashed-mirrors ''
-# Note: "check" doesn't repair anything, it just compares to the hash stored in the database.
-[[ $(cat $path) = foo ]]
-
-nix-build check.nix -A fetchurl --no-out-link --repair --hashed-mirrors ''
-[[ $(cat $path) != foo ]]
-
-nix-build check.nix -A hashmismatch --no-out-link --hashed-mirrors '' || status=$?
-[ "$status" = "102" ]
-
-echo -n > ./dummy
-nix-build check.nix -A hashmismatch --no-out-link --hashed-mirrors ''
-echo 'Hello World' > ./dummy
-
-nix-build check.nix -A hashmismatch --no-out-link --check --hashed-mirrors '' || status=$?
-[ "$status" = "102" ]
-
-# Multiple failures with --keep-going
-nix-build check.nix -A nondeterministic --no-out-link
-nix-build check.nix -A nondeterministic -A hashmismatch --no-out-link --check --keep-going --hashed-mirrors '' || status=$?
-[ "$status" = "110" ]
diff --git a/third_party/nix/tests/common.sh.in b/third_party/nix/tests/common.sh.in
deleted file mode 100644
index 15d7b1ef91..0000000000
--- a/third_party/nix/tests/common.sh.in
+++ /dev/null
@@ -1,118 +0,0 @@
-set -e
-
-export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)
-export NIX_STORE_DIR
-if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then
-    # Maybe the build directory is symlinked.
-    export NIX_IGNORE_SYMLINK_STORE=1
-    NIX_STORE_DIR=$TEST_ROOT/store
-fi
-export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
-export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
-export NIX_STATE_DIR=$TEST_ROOT/var/nix
-export NIX_CONF_DIR=$TEST_ROOT/etc
-export _NIX_TEST_SHARED=$TEST_ROOT/shared
-if [[ -n $NIX_STORE ]]; then
-    export _NIX_TEST_NO_SANDBOX=1
-fi
-export _NIX_IN_TEST=$TEST_ROOT/shared
-export _NIX_TEST_NO_LSOF=1
-export NIX_REMOTE=$NIX_REMOTE_
-unset NIX_PATH
-export TEST_HOME=$TEST_ROOT/test-home
-export HOME=$TEST_HOME
-unset XDG_CACHE_HOME
-mkdir -p $TEST_HOME
-
-export PATH=@bindir@:$PATH
-coreutils=@coreutils@
-
-export dot=@dot@
-export xmllint="@xmllint@"
-export SHELL="@bash@"
-export PAGER=cat
-export HAVE_SODIUM="@HAVE_SODIUM@"
-
-export version=@PACKAGE_VERSION@
-export system=@system@
-
-cacheDir=$TEST_ROOT/binary-cache
-
-readLink() {
-    ls -l "$1" | sed 's/.*->\ //'
-}
-
-clearProfiles() {
-    profiles="$NIX_STATE_DIR"/profiles
-    rm -rf $profiles
-}
-
-clearStore() {
-    echo "clearing store..."
-    chmod -R +w "$NIX_STORE_DIR"
-    rm -rf "$NIX_STORE_DIR"
-    mkdir "$NIX_STORE_DIR"
-    rm -rf "$NIX_STATE_DIR"
-    mkdir "$NIX_STATE_DIR"
-    nix-store --init
-    clearProfiles
-}
-
-clearCache() {
-    rm -rf "$cacheDir"
-}
-
-clearCacheCache() {
-    rm -f $TEST_HOME/.cache/nix/binary-cache*
-}
-
-startDaemon() {
-    # Start the daemon, wait for the socket to appear.  !!!
-    # β€˜nix-daemon’ should have an option to fork into the background.
-    rm -f $NIX_STATE_DIR/daemon-socket/socket
-    nix-daemon &
-    for ((i = 0; i < 30; i++)); do
-        if [ -e $NIX_STATE_DIR/daemon-socket/socket ]; then break; fi
-        sleep 1
-    done
-    pidDaemon=$!
-    trap "kill -9 $pidDaemon" EXIT
-    export NIX_REMOTE=daemon
-}
-
-killDaemon() {
-    kill -9 $pidDaemon
-    wait $pidDaemon || true
-    trap "" EXIT
-}
-
-if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then
-    _canUseSandbox=1
-fi
-
-canUseSandbox() {
-    if [[ ! $_canUseSandbox ]]; then
-        echo "Sandboxing not supported, skipping this test..."
-        return 1
-    fi
-
-    return 0
-}
-
-fail() {
-    echo "$1"
-    exit 1
-}
-
-expect() {
-    local expected res
-    expected="$1"
-    shift
-    set +e
-    "$@"
-    res="$?"
-    set -e
-    [[ $res -eq $expected ]]
-}
-
-set -x
diff --git a/third_party/nix/tests/config.nix b/third_party/nix/tests/config.nix
deleted file mode 100644
index 6ba91065b8..0000000000
--- a/third_party/nix/tests/config.nix
+++ /dev/null
@@ -1,20 +0,0 @@
-with import <nix/config.nix>;
-
-rec {
-  inherit shell;
-
-  path = coreutils;
-
-  system = builtins.currentSystem;
-
-  shared = builtins.getEnv "_NIX_TEST_SHARED";
-
-  mkDerivation = args:
-    derivation ({
-      inherit system;
-      builder = shell;
-      args = ["-e" args.builder or (builtins.toFile "builder.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")];
-      PATH = path;
-    } // removeAttrs args ["builder" "meta"])
-    // { meta = args.meta or {}; };
-}
diff --git a/third_party/nix/tests/dependencies.builder0.sh b/third_party/nix/tests/dependencies.builder0.sh
deleted file mode 100644
index c37bf909a5..0000000000
--- a/third_party/nix/tests/dependencies.builder0.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-[ "${input1: -2}" = /. ]
-[ "${input2: -2}" = /. ]
-
-mkdir $out
-echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
-
-ln -s $input2 $out/input-2
-
-# Self-reference.
-ln -s $out $out/self
-
-# Executable.
-echo program > $out/program
-chmod +x $out/program
-
-echo FOO
diff --git a/third_party/nix/tests/dependencies.builder1.sh b/third_party/nix/tests/dependencies.builder1.sh
deleted file mode 100644
index 4b006a17d7..0000000000
--- a/third_party/nix/tests/dependencies.builder1.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-mkdir $out
-echo FOO > $out/foo
diff --git a/third_party/nix/tests/dependencies.builder2.sh b/third_party/nix/tests/dependencies.builder2.sh
deleted file mode 100644
index 4f886fdb3a..0000000000
--- a/third_party/nix/tests/dependencies.builder2.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-mkdir $out
-echo BAR > $out/bar
diff --git a/third_party/nix/tests/dependencies.nix b/third_party/nix/tests/dependencies.nix
deleted file mode 100644
index eca4b2964c..0000000000
--- a/third_party/nix/tests/dependencies.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-with import ./config.nix;
-
-let {
-
-  input1 = mkDerivation {
-    name = "dependencies-input-1";
-    builder = ./dependencies.builder1.sh;
-  };
-
-  input2 = mkDerivation {
-    name = "dependencies-input-2";
-    builder = "${./dependencies.builder2.sh}";
-  };
-
-  body = mkDerivation {
-    name = "dependencies";
-    builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
-    input1 = input1 + "/.";
-    input2 = "${input2}/.";
-    input1_drv = input1;
-    meta.description = "Random test package";
-  };
-
-}
diff --git a/third_party/nix/tests/dependencies.sh b/third_party/nix/tests/dependencies.sh
deleted file mode 100644
index df204d185d..0000000000
--- a/third_party/nix/tests/dependencies.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-source common.sh
-
-clearStore
-
-drvPath=$(nix-instantiate dependencies.nix)
-
-echo "derivation is $drvPath"
-
-nix-store -q --tree "$drvPath" | grep '   +---.*builder1.sh'
-
-# Test Graphviz graph generation.
-nix-store -q --graph "$drvPath" > $TEST_ROOT/graph
-if test -n "$dot"; then
-    # Does it parse?
-    $dot < $TEST_ROOT/graph
-fi
-
-outPath=$(nix-store -rvv "$drvPath") || fail "build failed"
-
-# Test Graphviz graph generation.
-nix-store -q --graph "$outPath" > $TEST_ROOT/graph
-if test -n "$dot"; then
-    # Does it parse?
-    $dot < $TEST_ROOT/graph
-fi    
-
-nix-store -q --tree "$outPath" | grep '+---.*dependencies-input-2'
-
-echo "output path is $outPath"
-
-text=$(cat "$outPath"/foobar)
-if test "$text" != "FOOBAR"; then exit 1; fi
-
-deps=$(nix-store -quR "$drvPath")
-
-echo "output closure contains $deps"
-
-# The output path should be in the closure.
-echo "$deps" | grep -q "$outPath"
-
-# Input-1 is not retained.
-if echo "$deps" | grep -q "dependencies-input-1"; then exit 1; fi
-
-# Input-2 is retained.
-input2OutPath=$(echo "$deps" | grep "dependencies-input-2")
-
-# The referrers closure of input-2 should include outPath.
-nix-store -q --referrers-closure "$input2OutPath" | grep "$outPath"
-
-# Check that the derivers are set properly.
-test $(nix-store -q --deriver "$outPath") = "$drvPath"
-nix-store -q --deriver "$input2OutPath" | grep -q -- "-input-2.drv" 
diff --git a/third_party/nix/tests/dump-db.sh b/third_party/nix/tests/dump-db.sh
deleted file mode 100644
index d6eea42aa0..0000000000
--- a/third_party/nix/tests/dump-db.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-source common.sh
-
-clearStore
-
-path=$(nix-build dependencies.nix -o $TEST_ROOT/result)
-
-deps="$(nix-store -qR $TEST_ROOT/result)"
-
-nix-store --dump-db > $TEST_ROOT/dump
-
-rm -rf $NIX_STATE_DIR/db
-
-nix-store --load-db < $TEST_ROOT/dump
-
-deps2="$(nix-store -qR $TEST_ROOT/result)"
-
-[ "$deps" = "$deps2" ];
-
-nix-store --dump-db > $TEST_ROOT/dump2
-cmp $TEST_ROOT/dump $TEST_ROOT/dump2
diff --git a/third_party/nix/tests/export-graph.nix b/third_party/nix/tests/export-graph.nix
deleted file mode 100644
index fdac9583db..0000000000
--- a/third_party/nix/tests/export-graph.nix
+++ /dev/null
@@ -1,29 +0,0 @@
-with import ./config.nix;
-
-rec {
-
-  printRefs =
-    ''
-      echo $exportReferencesGraph
-      while read path; do
-          read drv
-          read nrRefs
-          echo "$path has $nrRefs references"
-          echo "$path" >> $out
-          for ((n = 0; n < $nrRefs; n++)); do read ref; echo "ref $ref"; test -e "$ref"; done
-      done < refs
-    '';
-
-  foo."bar.runtimeGraph" = mkDerivation {
-    name = "dependencies";
-    builder = builtins.toFile "build-graph-builder" "${printRefs}";
-    exportReferencesGraph = ["refs" (import ./dependencies.nix)];
-  };
-
-  foo."bar.buildGraph" = mkDerivation {
-    name = "dependencies";
-    builder = builtins.toFile "build-graph-builder" "${printRefs}";
-    exportReferencesGraph = ["refs" (import ./dependencies.nix).drvPath];
-  };
-
-}
diff --git a/third_party/nix/tests/export-graph.sh b/third_party/nix/tests/export-graph.sh
deleted file mode 100644
index a6fd690544..0000000000
--- a/third_party/nix/tests/export-graph.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-source common.sh
-
-clearStore
-clearProfiles
-
-checkRef() {
-    nix-store -q --references $TEST_ROOT/result | grep -q "$1" || fail "missing reference $1"
-}
-
-# Test the export of the runtime dependency graph.
-
-outPath=$(nix-build ./export-graph.nix -A 'foo."bar.runtimeGraph"' -o $TEST_ROOT/result)
-
-test $(nix-store -q --references $TEST_ROOT/result | wc -l) = 2 || fail "bad nr of references"
-
-checkRef input-2
-for i in $(cat $outPath); do checkRef $i; done
-
-# Test the export of the build-time dependency graph.
-
-nix-store --gc # should force rebuild of input-1
-
-outPath=$(nix-build ./export-graph.nix -A 'foo."bar.buildGraph"' -o $TEST_ROOT/result)
-
-checkRef input-1
-checkRef input-1.drv
-checkRef input-2
-checkRef input-2.drv
-
-for i in $(cat $outPath); do checkRef $i; done
diff --git a/third_party/nix/tests/export.sh b/third_party/nix/tests/export.sh
deleted file mode 100644
index 2238539bcc..0000000000
--- a/third_party/nix/tests/export.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-source common.sh
-
-clearStore
-
-outPath=$(nix-build dependencies.nix --no-out-link)
-
-nix-store --export $outPath > $TEST_ROOT/exp
-
-nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all
-
-if nix-store --export $outPath >/dev/full ; then
-    echo "exporting to a bad file descriptor should fail"
-    exit 1
-fi
-
-
-clearStore
-
-if nix-store --import < $TEST_ROOT/exp; then
-    echo "importing a non-closure should fail"
-    exit 1
-fi
-
-
-clearStore
-
-nix-store --import < $TEST_ROOT/exp_all
-
-nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all2
-
-
-clearStore
-
-# Regression test: the derivers in exp_all2 are empty, which shouldn't
-# cause a failure.
-nix-store --import < $TEST_ROOT/exp_all2
diff --git a/third_party/nix/tests/fetchGit.sh b/third_party/nix/tests/fetchGit.sh
deleted file mode 100644
index 4c46bdf046..0000000000
--- a/third_party/nix/tests/fetchGit.sh
+++ /dev/null
@@ -1,141 +0,0 @@
-source common.sh
-
-if [[ -z $(type -p git) ]]; then
-    echo "Git not installed; skipping Git tests"
-    exit 99
-fi
-
-clearStore
-
-repo=$TEST_ROOT/git
-
-rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix/gitv2
-
-git init $repo
-git -C $repo config user.email "foobar@example.com"
-git -C $repo config user.name "Foobar"
-
-echo utrecht > $repo/hello
-touch $repo/.gitignore
-git -C $repo add hello .gitignore
-git -C $repo commit -m 'Bla1'
-rev1=$(git -C $repo rev-parse HEAD)
-
-echo world > $repo/hello
-git -C $repo commit -m 'Bla2' -a
-rev2=$(git -C $repo rev-parse HEAD)
-
-# Fetch the default branch.
-path=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
-[[ $(cat $path/hello) = world ]]
-
-# In pure eval mode, fetchGit without a revision should fail.
-[[ $(nix eval --raw "(builtins.readFile (fetchGit file://$repo + \"/hello\"))") = world ]]
-(! nix eval --pure-eval --raw "(builtins.readFile (fetchGit file://$repo + \"/hello\"))")
-
-# Fetch using an explicit revision hash.
-path2=$(nix eval --raw "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath")
-[[ $path = $path2 ]]
-
-# In pure eval mode, fetchGit with a revision should succeed.
-[[ $(nix eval --pure-eval --raw "(builtins.readFile (fetchGit { url = file://$repo; rev = \"$rev2\"; } + \"/hello\"))") = world ]]
-
-# Fetch again. This should be cached.
-mv $repo ${repo}-tmp
-path2=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
-[[ $path = $path2 ]]
-
-[[ $(nix eval "(builtins.fetchGit file://$repo).revCount") = 2 ]]
-[[ $(nix eval --raw "(builtins.fetchGit file://$repo).rev") = $rev2 ]]
-
-# But with TTL 0, it should fail.
-(! nix eval --tarball-ttl 0 "(builtins.fetchGit file://$repo)" -vvvvv)
-
-# Fetching with a explicit hash should succeed.
-path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath")
-[[ $path = $path2 ]]
-
-path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchGit { url = file://$repo; rev = \"$rev1\"; }).outPath")
-[[ $(cat $path2/hello) = utrecht ]]
-
-mv ${repo}-tmp $repo
-
-# Using a clean working tree should produce the same result.
-path2=$(nix eval --raw "(builtins.fetchGit $repo).outPath")
-[[ $path = $path2 ]]
-
-# Using an unclean tree should yield the tracked but uncommitted changes.
-mkdir $repo/dir1 $repo/dir2
-echo foo > $repo/dir1/foo
-echo bar > $repo/bar
-echo bar > $repo/dir2/bar
-git -C $repo add dir1/foo
-git -C $repo rm hello
-
-path2=$(nix eval --raw "(builtins.fetchGit $repo).outPath")
-[ ! -e $path2/hello ]
-[ ! -e $path2/bar ]
-[ ! -e $path2/dir2/bar ]
-[ ! -e $path2/.git ]
-[[ $(cat $path2/dir1/foo) = foo ]]
-
-[[ $(nix eval --raw "(builtins.fetchGit $repo).rev") = 0000000000000000000000000000000000000000 ]]
-
-# ... unless we're using an explicit ref or rev.
-path3=$(nix eval --raw "(builtins.fetchGit { url = $repo; ref = \"master\"; }).outPath")
-[[ $path = $path3 ]]
-
-path3=$(nix eval --raw "(builtins.fetchGit { url = $repo; rev = \"$rev2\"; }).outPath")
-[[ $path = $path3 ]]
-
-# Committing should not affect the store path.
-git -C $repo commit -m 'Bla3' -a
-
-path4=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchGit file://$repo).outPath")
-[[ $path2 = $path4 ]]
-
-# tarball-ttl should be ignored if we specify a rev
-echo delft > $repo/hello
-git -C $repo add hello
-git -C $repo commit -m 'Bla4'
-rev3=$(git -C $repo rev-parse HEAD)
-nix eval --tarball-ttl 3600 "(builtins.fetchGit { url = $repo; rev = \"$rev3\"; })" >/dev/null
-
-# Update 'path' to reflect latest master
-path=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
-
-# Check behavior when non-master branch is used
-git -C $repo checkout $rev2 -b dev
-echo dev > $repo/hello
-
-# File URI uses 'master' unless specified otherwise
-path2=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
-[[ $path = $path2 ]]
-
-# Using local path with branch other than 'master' should work when clean or dirty
-path3=$(nix eval --raw "(builtins.fetchGit $repo).outPath")
-# (check dirty-tree handling was used)
-[[ $(nix eval --raw "(builtins.fetchGit $repo).rev") = 0000000000000000000000000000000000000000 ]]
-
-# Committing shouldn't change store path, or switch to using 'master'
-git -C $repo commit -m 'Bla5' -a
-path4=$(nix eval --raw "(builtins.fetchGit $repo).outPath")
-[[ $(cat $path4/hello) = dev ]]
-[[ $path3 = $path4 ]]
-
-# Confirm same as 'dev' branch
-path5=$(nix eval --raw "(builtins.fetchGit { url = $repo; ref = \"dev\"; }).outPath")
-[[ $path3 = $path5 ]]
-
-
-# Nuke the cache
-rm -rf $TEST_HOME/.cache/nix/gitv2
-
-# Try again, but without 'git' on PATH
-NIX=$(command -v nix)
-# This should fail
-(! PATH= $NIX eval --raw "(builtins.fetchGit { url = $repo; ref = \"dev\"; }).outPath" )
-
-# Try again, with 'git' available.  This should work.
-path5=$(nix eval --raw "(builtins.fetchGit { url = $repo; ref = \"dev\"; }).outPath")
-[[ $path3 = $path5 ]]
diff --git a/third_party/nix/tests/fetchMercurial.sh b/third_party/nix/tests/fetchMercurial.sh
deleted file mode 100644
index 4088dbd397..0000000000
--- a/third_party/nix/tests/fetchMercurial.sh
+++ /dev/null
@@ -1,93 +0,0 @@
-source common.sh
-
-if [[ -z $(type -p hg) ]]; then
-    echo "Mercurial not installed; skipping Mercurial tests"
-    exit 99
-fi
-
-clearStore
-
-repo=$TEST_ROOT/hg
-
-rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix/hg
-
-hg init $repo
-echo '[ui]' >> $repo/.hg/hgrc
-echo 'username = Foobar <foobar@example.org>' >> $repo/.hg/hgrc
-
-echo utrecht > $repo/hello
-touch $repo/.hgignore
-hg add --cwd $repo hello .hgignore
-hg commit --cwd $repo -m 'Bla1'
-rev1=$(hg log --cwd $repo -r tip --template '{node}')
-
-echo world > $repo/hello
-hg commit --cwd $repo -m 'Bla2'
-rev2=$(hg log --cwd $repo -r tip --template '{node}')
-
-# Fetch the default branch.
-path=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath")
-[[ $(cat $path/hello) = world ]]
-
-# In pure eval mode, fetchGit without a revision should fail.
-[[ $(nix eval --raw "(builtins.readFile (fetchMercurial file://$repo + \"/hello\"))") = world ]]
-(! nix eval --pure-eval --raw "(builtins.readFile (fetchMercurial file://$repo + \"/hello\"))")
-
-# Fetch using an explicit revision hash.
-path2=$(nix eval --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath")
-[[ $path = $path2 ]]
-
-# In pure eval mode, fetchGit with a revision should succeed.
-[[ $(nix eval --pure-eval --raw "(builtins.readFile (fetchMercurial { url = file://$repo; rev = \"$rev2\"; } + \"/hello\"))") = world ]]
-
-# Fetch again. This should be cached.
-mv $repo ${repo}-tmp
-path2=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath")
-[[ $path = $path2 ]]
-
-[[ $(nix eval --raw "(builtins.fetchMercurial file://$repo).branch") = default ]]
-[[ $(nix eval "(builtins.fetchMercurial file://$repo).revCount") = 1 ]]
-[[ $(nix eval --raw "(builtins.fetchMercurial file://$repo).rev") = $rev2 ]]
-
-# But with TTL 0, it should fail.
-(! nix eval --tarball-ttl 0 "(builtins.fetchMercurial file://$repo)")
-
-# Fetching with a explicit hash should succeed.
-path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath")
-[[ $path = $path2 ]]
-
-path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev1\"; }).outPath")
-[[ $(cat $path2/hello) = utrecht ]]
-
-mv ${repo}-tmp $repo
-
-# Using a clean working tree should produce the same result.
-path2=$(nix eval --raw "(builtins.fetchMercurial $repo).outPath")
-[[ $path = $path2 ]]
-
-# Using an unclean tree should yield the tracked but uncommitted changes.
-mkdir $repo/dir1 $repo/dir2
-echo foo > $repo/dir1/foo
-echo bar > $repo/bar
-echo bar > $repo/dir2/bar
-hg add --cwd $repo dir1/foo
-hg rm --cwd $repo hello
-
-path2=$(nix eval --raw "(builtins.fetchMercurial $repo).outPath")
-[ ! -e $path2/hello ]
-[ ! -e $path2/bar ]
-[ ! -e $path2/dir2/bar ]
-[ ! -e $path2/.hg ]
-[[ $(cat $path2/dir1/foo) = foo ]]
-
-[[ $(nix eval --raw "(builtins.fetchMercurial $repo).rev") = 0000000000000000000000000000000000000000 ]]
-
-# ... unless we're using an explicit rev.
-path3=$(nix eval --raw "(builtins.fetchMercurial { url = $repo; rev = \"default\"; }).outPath")
-[[ $path = $path3 ]]
-
-# Committing should not affect the store path.
-hg commit --cwd $repo -m 'Bla3'
-
-path4=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchMercurial file://$repo).outPath")
-[[ $path2 = $path4 ]]
diff --git a/third_party/nix/tests/fetchurl.sh b/third_party/nix/tests/fetchurl.sh
deleted file mode 100644
index 7319ced2b5..0000000000
--- a/third_party/nix/tests/fetchurl.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-source common.sh
-
-clearStore
-
-# Test fetching a flat file.
-hash=$(nix-hash --flat --type sha256 ./fetchurl.sh)
-
-outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha256 $hash --no-out-link --hashed-mirrors '')
-
-cmp $outPath fetchurl.sh
-
-# Now using a base-64 hash.
-clearStore
-
-hash=$(nix hash-file --type sha512 --base64 ./fetchurl.sh)
-
-outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha512 $hash --no-out-link --hashed-mirrors '')
-
-cmp $outPath fetchurl.sh
-
-# Now using an SRI hash.
-clearStore
-
-hash=$(nix hash-file ./fetchurl.sh)
-
-[[ $hash =~ ^sha256- ]]
-
-outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr hash $hash --no-out-link --hashed-mirrors '')
-
-cmp $outPath fetchurl.sh
-
-# Test the hashed mirror feature.
-clearStore
-
-hash=$(nix hash-file --type sha512 --base64 ./fetchurl.sh)
-hash32=$(nix hash-file --type sha512 --base16 ./fetchurl.sh)
-
-mirror=$TMPDIR/hashed-mirror
-rm -rf $mirror
-mkdir -p $mirror/sha512
-ln -s $(pwd)/fetchurl.sh $mirror/sha512/$hash32
-
-outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr sha512 $hash --no-out-link --hashed-mirrors "file://$mirror")
-
-# Test hashed mirrors with an SRI hash.
-nix-build '<nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr hash $(nix to-sri --type sha512 $hash) \
-          --argstr name bla --no-out-link --hashed-mirrors "file://$mirror"
-
-# Test unpacking a NAR.
-rm -rf $TEST_ROOT/archive
-mkdir -p $TEST_ROOT/archive
-cp ./fetchurl.sh $TEST_ROOT/archive
-chmod +x $TEST_ROOT/archive/fetchurl.sh
-ln -s foo $TEST_ROOT/archive/symlink
-nar=$TEST_ROOT/archive.nar
-nix-store --dump $TEST_ROOT/archive > $nar
-
-hash=$(nix-hash --flat --type sha256 $nar)
-
-outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$nar --argstr sha256 $hash \
-          --arg unpack true --argstr name xyzzy --no-out-link)
-
-echo $outPath | grep -q 'xyzzy'
-
-test -x $outPath/fetchurl.sh
-test -L $outPath/symlink
-
-nix-store --delete $outPath
-
-# Test unpacking a compressed NAR.
-narxz=$TEST_ROOT/archive.nar.xz
-rm -f $narxz
-xz --keep $nar
-outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$narxz --argstr sha256 $hash \
-          --arg unpack true --argstr name xyzzy --no-out-link)
-
-test -x $outPath/fetchurl.sh
-test -L $outPath/symlink
diff --git a/third_party/nix/tests/filter-source.nix b/third_party/nix/tests/filter-source.nix
deleted file mode 100644
index 9071636394..0000000000
--- a/third_party/nix/tests/filter-source.nix
+++ /dev/null
@@ -1,12 +0,0 @@
-with import ./config.nix;
-
-mkDerivation {
-  name = "filter";
-  builder = builtins.toFile "builder" "ln -s $input $out";
-  input =
-    let filter = path: type:
-      type != "symlink"
-      && baseNameOf path != "foo"
-      && !((import ./lang/lib.nix).hasSuffix ".bak" (baseNameOf path));
-    in builtins.filterSource filter ((builtins.getEnv "TEST_ROOT") + "/filterin");
-}
diff --git a/third_party/nix/tests/filter-source.sh b/third_party/nix/tests/filter-source.sh
deleted file mode 100644
index 1f8dceee57..0000000000
--- a/third_party/nix/tests/filter-source.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-source common.sh
-
-rm -rf $TEST_ROOT/filterin
-mkdir $TEST_ROOT/filterin
-mkdir $TEST_ROOT/filterin/foo
-touch $TEST_ROOT/filterin/foo/bar
-touch $TEST_ROOT/filterin/xyzzy
-touch $TEST_ROOT/filterin/b
-touch $TEST_ROOT/filterin/bak
-touch $TEST_ROOT/filterin/bla.c.bak
-ln -s xyzzy $TEST_ROOT/filterin/link
-
-nix-build ./filter-source.nix -o $TEST_ROOT/filterout
-
-test ! -e $TEST_ROOT/filterout/foo/bar
-test -e $TEST_ROOT/filterout/xyzzy
-test -e $TEST_ROOT/filterout/bak
-test ! -e $TEST_ROOT/filterout/bla.c.bak
-test ! -L $TEST_ROOT/filterout/link
diff --git a/third_party/nix/tests/fixed.builder1.sh b/third_party/nix/tests/fixed.builder1.sh
deleted file mode 100644
index c41bb2b9a6..0000000000
--- a/third_party/nix/tests/fixed.builder1.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-if test "$IMPURE_VAR1" != "foo"; then exit 1; fi
-if test "$IMPURE_VAR2" != "bar"; then exit 1; fi
-echo "Hello World!" > $out
diff --git a/third_party/nix/tests/fixed.builder2.sh b/third_party/nix/tests/fixed.builder2.sh
deleted file mode 100644
index 31ea1579a5..0000000000
--- a/third_party/nix/tests/fixed.builder2.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-echo dummy: $dummy
-if test -n "$dummy"; then sleep 2; fi
-mkdir $out
-mkdir $out/bla
-echo "Hello World!" > $out/foo
-ln -s foo $out/bar
diff --git a/third_party/nix/tests/fixed.nix b/third_party/nix/tests/fixed.nix
deleted file mode 100644
index 76580ffa19..0000000000
--- a/third_party/nix/tests/fixed.nix
+++ /dev/null
@@ -1,50 +0,0 @@
-with import ./config.nix;
-
-rec {
-
-  f2 = dummy: builder: mode: algo: hash: mkDerivation {
-    name = "fixed";
-    inherit builder;
-    outputHashMode = mode;
-    outputHashAlgo = algo;
-    outputHash = hash;
-    inherit dummy;
-    impureEnvVars = ["IMPURE_VAR1" "IMPURE_VAR2"];
-  };
-
-  f = f2 "";
-
-  good = [
-    (f ./fixed.builder1.sh "flat" "md5" "8ddd8be4b179a529afa5f2ffae4b9858")
-    (f ./fixed.builder1.sh "flat" "sha1" "a0b65939670bc2c010f4d5d6a0b3e4e4590fb92b")
-    (f ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
-    (f ./fixed.builder2.sh "recursive" "sha1" "vw46m23bizj4n8afrc0fj19wrp7mj3c0")
-  ];
-
-  good2 = [
-    # Yes, this looks fscked up: builder2 doesn't have that result.
-    # But Nix sees that an output with the desired hash already
-    # exists, and will refrain from building it.
-    (f ./fixed.builder2.sh "flat" "md5" "8ddd8be4b179a529afa5f2ffae4b9858")
-  ];
-
-  sameAsAdd =
-    f ./fixed.builder2.sh "recursive" "sha256" "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik";
-
-  bad = [
-    (f ./fixed.builder1.sh "flat" "md5" "0ddd8be4b179a529afa5f2ffae4b9858")
-  ];
-
-  reallyBad = [
-    # Hash too short, and not base-32 either.
-    (f ./fixed.builder1.sh "flat" "md5" "ddd8be4b179a529afa5f2ffae4b9858")
-  ];
-
-  # Test for building two derivations in parallel that produce the
-  # same output path because they're fixed-output derivations.
-  parallelSame = [
-    (f2 "foo" ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
-    (f2 "bar" ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
-  ];
-
-}
diff --git a/third_party/nix/tests/fixed.sh b/third_party/nix/tests/fixed.sh
deleted file mode 100644
index 8f51403a70..0000000000
--- a/third_party/nix/tests/fixed.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-source common.sh
-
-clearStore
-
-export IMPURE_VAR1=foo
-export IMPURE_VAR2=bar
-
-path=$(nix-store -q $(nix-instantiate fixed.nix -A good.0))
-
-echo 'testing bad...'
-nix-build fixed.nix -A bad --no-out-link && fail "should fail"
-
-# Building with the bad hash should produce the "good" output path as
-# a side-effect.
-[[ -e $path ]]
-nix path-info --json $path | grep fixed:md5:2qk15sxzzjlnpjk9brn7j8ppcd
-
-echo 'testing good...'
-nix-build fixed.nix -A good --no-out-link
-
-echo 'testing good2...'
-nix-build fixed.nix -A good2 --no-out-link
-
-echo 'testing reallyBad...'
-nix-instantiate fixed.nix -A reallyBad && fail "should fail"
-
-# While we're at it, check attribute selection a bit more.
-echo 'testing attribute selection...'
-test $(nix-instantiate fixed.nix -A good.1 | wc -l) = 1
-
-# Test parallel builds of derivations that produce the same output.
-# Only one should run at the same time.
-echo 'testing parallelSame...'
-clearStore
-nix-build fixed.nix -A parallelSame --no-out-link -j2
-
-# Fixed-output derivations with a recursive SHA-256 hash should
-# produce the same path as "nix-store --add".
-echo 'testing sameAsAdd...'
-out=$(nix-build fixed.nix -A sameAsAdd --no-out-link)
-
-# This is what fixed.builder2 produces...
-rm -rf $TEST_ROOT/fixed
-mkdir $TEST_ROOT/fixed
-mkdir $TEST_ROOT/fixed/bla
-echo "Hello World!" > $TEST_ROOT/fixed/foo
-ln -s foo $TEST_ROOT/fixed/bar
-
-out2=$(nix-store --add $TEST_ROOT/fixed)
-[ "$out" = "$out2" ]
-
-out3=$(nix-store --add-fixed --recursive sha256 $TEST_ROOT/fixed)
-[ "$out" = "$out3" ]
-
-out4=$(nix-store --print-fixed-path --recursive sha256 "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik" fixed)
-[ "$out" = "$out4" ]
diff --git a/third_party/nix/tests/function-trace.sh b/third_party/nix/tests/function-trace.sh
deleted file mode 100755
index 182a4d5c28..0000000000
--- a/third_party/nix/tests/function-trace.sh
+++ /dev/null
@@ -1,85 +0,0 @@
-source common.sh
-
-set +x
-
-expect_trace() {
-    expr="$1"
-    expect="$2"
-    actual=$(
-        nix-instantiate \
-            --trace-function-calls \
-            --expr "$expr" 2>&1 \
-            | grep "function-trace" \
-            | sed -e 's/ [0-9]*$//'
-    );
-
-    echo -n "Tracing expression '$expr'"
-    set +e
-    msg=$(diff -swB \
-               <(echo "$expect") \
-               <(echo "$actual")
-    );
-    result=$?
-    set -e
-    if [ $result -eq 0 ]; then
-        echo " ok."
-    else
-        echo " failed. difference:"
-        echo "$msg"
-        return $result
-    fi
-}
-
-# failure inside a tryEval
-expect_trace 'builtins.tryEval (throw "example")' "
-function-trace entered undefined position at
-function-trace exited undefined position at
-function-trace entered (string):1:1 at
-function-trace entered (string):1:19 at
-function-trace exited (string):1:19 at
-function-trace exited (string):1:1 at
-"
-
-# Missing argument to a formal function
-expect_trace '({ x }: x) { }' "
-function-trace entered undefined position at
-function-trace exited undefined position at
-function-trace entered (string):1:1 at
-function-trace exited (string):1:1 at
-"
-
-# Too many arguments to a formal function
-expect_trace '({ x }: x) { x = "x"; y = "y"; }' "
-function-trace entered undefined position at
-function-trace exited undefined position at
-function-trace entered (string):1:1 at
-function-trace exited (string):1:1 at
-"
-
-# Not enough arguments to a lambda
-expect_trace '(x: y: x + y) 1' "
-function-trace entered undefined position at
-function-trace exited undefined position at
-function-trace entered (string):1:1 at
-function-trace exited (string):1:1 at
-"
-
-# Too many arguments to a lambda
-expect_trace '(x: x) 1 2' "
-function-trace entered undefined position at
-function-trace exited undefined position at
-function-trace entered (string):1:1 at
-function-trace exited (string):1:1 at
-function-trace entered (string):1:1 at
-function-trace exited (string):1:1 at
-"
-
-# Not a function
-expect_trace '1 2' "
-function-trace entered undefined position at
-function-trace exited undefined position at
-function-trace entered (string):1:1 at
-function-trace exited (string):1:1 at
-"
-
-set -e
diff --git a/third_party/nix/tests/gc-auto.sh b/third_party/nix/tests/gc-auto.sh
deleted file mode 100644
index de1e2cfe40..0000000000
--- a/third_party/nix/tests/gc-auto.sh
+++ /dev/null
@@ -1,70 +0,0 @@
-source common.sh
-
-clearStore
-
-garbage1=$(nix add-to-store --name garbage1 ./nar-access.sh)
-garbage2=$(nix add-to-store --name garbage2 ./nar-access.sh)
-garbage3=$(nix add-to-store --name garbage3 ./nar-access.sh)
-
-ls -l $garbage3
-POSIXLY_CORRECT=1 du $garbage3
-
-fake_free=$TEST_ROOT/fake-free
-export _NIX_TEST_FREE_SPACE_FILE=$fake_free
-echo 1100 > $fake_free
-
-expr=$(cat <<EOF
-with import ./config.nix; mkDerivation {
-  name = "gc-A";
-  buildCommand = ''
-    set -x
-    [[ \$(ls \$NIX_STORE/*-garbage? | wc -l) = 3 ]]
-    mkdir \$out
-    echo foo > \$out/bar
-    echo 1...
-    sleep 2
-    echo 200 > ${fake_free}.tmp1
-    mv ${fake_free}.tmp1 $fake_free
-    echo 2...
-    sleep 2
-    echo 3...
-    sleep 2
-    echo 4...
-    [[ \$(ls \$NIX_STORE/*-garbage? | wc -l) = 1 ]]
-  '';
-}
-EOF
-)
-
-expr2=$(cat <<EOF
-with import ./config.nix; mkDerivation {
-  name = "gc-B";
-  buildCommand = ''
-    set -x
-    mkdir \$out
-    echo foo > \$out/bar
-    echo 1...
-    sleep 2
-    echo 200 > ${fake_free}.tmp2
-    mv ${fake_free}.tmp2 $fake_free
-    echo 2...
-    sleep 2
-    echo 3...
-    sleep 2
-    echo 4...
-  '';
-}
-EOF
-)
-
-nix build -v -o $TEST_ROOT/result-A -L "($expr)" \
-    --min-free 1000 --max-free 2000 --min-free-check-interval 1 &
-pid=$!
-
-nix build -v -o $TEST_ROOT/result-B -L "($expr2)" \
-    --min-free 1000 --max-free 2000 --min-free-check-interval 1
-
-wait "$pid"
-
-[[ foo = $(cat $TEST_ROOT/result-A/bar) ]]
-[[ foo = $(cat $TEST_ROOT/result-B/bar) ]]
diff --git a/third_party/nix/tests/gc-concurrent.builder.sh b/third_party/nix/tests/gc-concurrent.builder.sh
deleted file mode 100644
index 0cd67df3ae..0000000000
--- a/third_party/nix/tests/gc-concurrent.builder.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-mkdir $out
-echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
-
-sleep 10
-
-# $out should not have been GC'ed while we were sleeping, but just in
-# case...
-mkdir -p $out
-
-# Check that the GC hasn't deleted the lock on our output.
-test -e "$out.lock"
-
-ln -s $input2 $out/input-2
diff --git a/third_party/nix/tests/gc-concurrent.nix b/third_party/nix/tests/gc-concurrent.nix
deleted file mode 100644
index c0595cc471..0000000000
--- a/third_party/nix/tests/gc-concurrent.nix
+++ /dev/null
@@ -1,27 +0,0 @@
-with import ./config.nix;
-
-rec {
-
-  input1 = mkDerivation {
-    name = "dependencies-input-1";
-    builder = ./dependencies.builder1.sh;
-  };
-
-  input2 = mkDerivation {
-    name = "dependencies-input-2";
-    builder = ./dependencies.builder2.sh;
-  };
-
-  test1 = mkDerivation {
-    name = "gc-concurrent";
-    builder = ./gc-concurrent.builder.sh;
-    inherit input1 input2;
-  };
-
-  test2 = mkDerivation {
-    name = "gc-concurrent2";
-    builder = ./gc-concurrent2.builder.sh;
-    inherit input1 input2;
-  };
-  
-}
diff --git a/third_party/nix/tests/gc-concurrent.sh b/third_party/nix/tests/gc-concurrent.sh
deleted file mode 100644
index d395930ca0..0000000000
--- a/third_party/nix/tests/gc-concurrent.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-source common.sh
-
-clearStore
-
-drvPath1=$(nix-instantiate gc-concurrent.nix -A test1)
-outPath1=$(nix-store -q $drvPath1)
-
-drvPath2=$(nix-instantiate gc-concurrent.nix -A test2)
-outPath2=$(nix-store -q $drvPath2)
-
-drvPath3=$(nix-instantiate simple.nix)
-outPath3=$(nix-store -r $drvPath3)
-
-(! test -e $outPath3.lock)
-touch $outPath3.lock
-
-rm -f "$NIX_STATE_DIR"/gcroots/foo*
-ln -s $drvPath2 "$NIX_STATE_DIR"/gcroots/foo
-ln -s $outPath3 "$NIX_STATE_DIR"/gcroots/foo2
-
-# Start build #1 in the background.  It starts immediately.
-nix-store -rvv "$drvPath1" &
-pid1=$!
-
-# Start build #2 in the background after 10 seconds.
-(sleep 10 && nix-store -rvv "$drvPath2") &
-pid2=$!
-
-# Run the garbage collector while the build is running.
-sleep 6
-nix-collect-garbage
-
-# Wait for build #1/#2 to finish.
-echo waiting for pid $pid1 to finish...
-wait $pid1
-echo waiting for pid $pid2 to finish...
-wait $pid2
-
-# Check that the root of build #1 and its dependencies haven't been
-# deleted.  The should not be deleted by the GC because they were
-# being built during the GC.
-cat $outPath1/foobar
-cat $outPath1/input-2/bar
-
-# Check that build #2 has succeeded.  It should succeed because the
-# derivation is a GC root.
-cat $outPath2/foobar
-
-rm -f "$NIX_STATE_DIR"/gcroots/foo*
-
-# The collector should have deleted lock files for paths that have
-# been built previously.
-(! test -e $outPath3.lock)
-
-# If we run the collector now, it should delete outPath1/2.
-nix-collect-garbage
-(! test -e $outPath1)
-(! test -e $outPath2)
diff --git a/third_party/nix/tests/gc-concurrent2.builder.sh b/third_party/nix/tests/gc-concurrent2.builder.sh
deleted file mode 100644
index 4bfb33103e..0000000000
--- a/third_party/nix/tests/gc-concurrent2.builder.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-mkdir $out
-echo $(cat $input1/foo)$(cat $input2/bar)xyzzy > $out/foobar
-
-# Check that the GC hasn't deleted the lock on our output.
-test -e "$out.lock"
-
-sleep 6
diff --git a/third_party/nix/tests/gc-runtime.nix b/third_party/nix/tests/gc-runtime.nix
deleted file mode 100644
index ee5980bdff..0000000000
--- a/third_party/nix/tests/gc-runtime.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-with import ./config.nix;
-
-mkDerivation {
-  name = "gc-runtime";
-  builder =
-    # Test inline source file definitions.
-    builtins.toFile "builder.sh" ''
-      mkdir $out
-
-      cat > $out/program <<EOF
-      #! ${shell}
-      sleep 10000
-      EOF
-
-      chmod +x $out/program
-    '';
-}
diff --git a/third_party/nix/tests/gc-runtime.sh b/third_party/nix/tests/gc-runtime.sh
deleted file mode 100644
index 4c5028005c..0000000000
--- a/third_party/nix/tests/gc-runtime.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-source common.sh
-
-case $system in
-    *linux*)
-        ;;
-    *)
-        exit 0;
-esac
-
-set -m # enable job control, needed for kill
-
-profiles="$NIX_STATE_DIR"/profiles
-rm -rf $profiles
-
-nix-env -p $profiles/test -f ./gc-runtime.nix -i gc-runtime
-
-outPath=$(nix-env -p $profiles/test -q --no-name --out-path gc-runtime)
-echo $outPath
-
-echo "backgrounding program..."
-$profiles/test/program &
-sleep 2 # hack - wait for the program to get started
-child=$!
-echo PID=$child
-
-nix-env -p $profiles/test -e gc-runtime
-nix-env -p $profiles/test --delete-generations old
-
-nix-store --gc
-
-kill -- -$child
-
-if ! test -e $outPath; then
-    echo "running program was garbage collected!"
-    exit 1
-fi
-
-exit 0
diff --git a/third_party/nix/tests/gc.sh b/third_party/nix/tests/gc.sh
deleted file mode 100644
index 8b4f8d2821..0000000000
--- a/third_party/nix/tests/gc.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-source common.sh
-
-drvPath=$(nix-instantiate dependencies.nix)
-outPath=$(nix-store -rvv "$drvPath")
-
-# Set a GC root.
-rm -f "$NIX_STATE_DIR"/gcroots/foo
-ln -sf $outPath "$NIX_STATE_DIR"/gcroots/foo
-
-[ "$(nix-store -q --roots $outPath)" = "$NIX_STATE_DIR/gcroots/foo -> $outPath" ]
-
-nix-store --gc --print-roots | grep $outPath
-nix-store --gc --print-live | grep $outPath
-nix-store --gc --print-dead | grep $drvPath
-if nix-store --gc --print-dead | grep $outPath; then false; fi
-
-nix-store --gc --print-dead
-
-inUse=$(readLink $outPath/input-2)
-if nix-store --delete $inUse; then false; fi
-test -e $inUse
-
-if nix-store --delete $outPath; then false; fi
-test -e $outPath
-
-nix-collect-garbage
-
-# Check that the root and its dependencies haven't been deleted.
-cat $outPath/foobar
-cat $outPath/input-2/bar
-
-# Check that the derivation has been GC'd.
-if test -e $drvPath; then false; fi
-
-rm "$NIX_STATE_DIR"/gcroots/foo
-
-nix-collect-garbage
-
-# Check that the output has been GC'd.
-if test -e $outPath/foobar; then false; fi
diff --git a/third_party/nix/tests/hash-check.nix b/third_party/nix/tests/hash-check.nix
deleted file mode 100644
index 4a8e9b8a8d..0000000000
--- a/third_party/nix/tests/hash-check.nix
+++ /dev/null
@@ -1,29 +0,0 @@
-let {
-
-  input1 = derivation {
-    name = "dependencies-input-1";
-    system = "i086-msdos";
-    builder = "/bar/sh";
-    args = ["-e" "-x" ./dummy];
-  };
-
-  input2 = derivation {
-    name = "dependencies-input-2";
-    system = "i086-msdos";
-    builder = "/bar/sh";
-    args = ["-e" "-x" ./dummy];
-    outputHashMode = "recursive";
-    outputHashAlgo = "md5";
-    outputHash = "ffffffffffffffffffffffffffffffff";
-  };
-
-  body = derivation {
-    name = "dependencies";
-    system = "i086-msdos";
-    builder = "/bar/sh";
-    args = ["-e" "-x" (./dummy  + "/FOOBAR/../.")];
-    input1 = input1 + "/.";
-    inherit input2;
-  };
-
-}
\ No newline at end of file
diff --git a/third_party/nix/tests/hash.sh b/third_party/nix/tests/hash.sh
deleted file mode 100644
index 4cfc979010..0000000000
--- a/third_party/nix/tests/hash.sh
+++ /dev/null
@@ -1,87 +0,0 @@
-source common.sh
-
-try () {
-    printf "%s" "$2" > $TEST_ROOT/vector
-    hash=$(nix hash-file --base16 $EXTRA --type "$1" $TEST_ROOT/vector)
-    if test "$hash" != "$3"; then
-        echo "hash $1, expected $3, got $hash"
-        exit 1
-    fi
-}
-
-try md5 "" "d41d8cd98f00b204e9800998ecf8427e"
-try md5 "a" "0cc175b9c0f1b6a831c399e269772661"
-try md5 "abc" "900150983cd24fb0d6963f7d28e17f72"
-try md5 "message digest" "f96b697d7cb7938d525a2f31aaf161d0"
-try md5 "abcdefghijklmnopqrstuvwxyz" "c3fcd3d76192e4007dfb496cca67e13b"
-try md5 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" "d174ab98d277d9f5a5611c2c9f419d9f"
-try md5 "12345678901234567890123456789012345678901234567890123456789012345678901234567890" "57edf4a22be3c955ac49da2e2107b67a"
-
-try sha1 "" "da39a3ee5e6b4b0d3255bfef95601890afd80709"
-try sha1 "abc" "a9993e364706816aba3e25717850c26c9cd0d89d"
-try sha1 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "84983e441c3bd26ebaae4aa1f95129e5e54670f1"
-
-try sha256 "" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
-try sha256 "abc" "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
-try sha256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
-
-try sha512 "" "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
-try sha512 "abc" "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
-try sha512 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"
-
-EXTRA=--base32
-try sha256 "abc" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"
-EXTRA=
-
-EXTRA=--sri
-try sha512 "" "sha512-z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg=="
-try sha512 "abc" "sha512-3a81oZNherrMQXNJriBBMRLm+k6JqX6iCp7u5ktV05ohkpkqJ0/BqDa6PCOj/uu9RU1EI2Q86A4qmslPpUyknw=="
-try sha512 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "sha512-IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="
-try sha256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "sha256-JI1qYdIGOLjlwCaTDD5gOaM85Flk/yFn9uzt1BnbBsE="
-
-try2 () {
-    hash=$(nix-hash --type "$1" $TEST_ROOT/hash-path)
-    if test "$hash" != "$2"; then
-        echo "hash $1, expected $2, got $hash"
-        exit 1
-    fi
-}
-
-rm -rf $TEST_ROOT/hash-path
-mkdir $TEST_ROOT/hash-path
-echo "Hello World" > $TEST_ROOT/hash-path/hello
-
-try2 md5 "ea9b55537dd4c7e104515b2ccfaf4100"
-
-# Execute bit matters.
-chmod +x $TEST_ROOT/hash-path/hello
-try2 md5 "20f3ffe011d4cfa7d72bfabef7882836"
-
-# Mtime and other bits don't.
-touch -r . $TEST_ROOT/hash-path/hello
-chmod 744 $TEST_ROOT/hash-path/hello
-try2 md5 "20f3ffe011d4cfa7d72bfabef7882836"
-
-# File type (e.g., symlink) does.
-rm $TEST_ROOT/hash-path/hello
-ln -s x $TEST_ROOT/hash-path/hello
-try2 md5 "f78b733a68f5edbdf9413899339eaa4a"
-
-# Conversion.
-try3() {
-    h64=$(nix to-base64 --type "$1" "$2")
-    [ "$h64" = "$4" ]
-    sri=$(nix to-sri --type "$1" "$2")
-    [ "$sri" = "$1-$4" ]
-    h32=$(nix-hash --type "$1" --to-base32 "$2")
-    [ "$h32" = "$3" ]
-    h16=$(nix-hash --type "$1" --to-base16 "$h32")
-    [ "$h16" = "$2" ]
-    h16=$(nix to-base16 --type "$1" "$h64")
-    [ "$h16" = "$2" ]
-    h16=$(nix to-base16 "$sri")
-    [ "$h16" = "$2" ]
-}
-try3 sha1 "800d59cfcd3c05e900cb4e214be48f6b886a08df" "vw46m23bizj4n8afrc0fj19wrp7mj3c0" "gA1Zz808BekAy04hS+SPa4hqCN8="
-try3 sha256 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="
-try3 sha512 "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0" "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="
diff --git a/third_party/nix/tests/import-derivation.nix b/third_party/nix/tests/import-derivation.nix
deleted file mode 100644
index 44fa9a45d7..0000000000
--- a/third_party/nix/tests/import-derivation.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-with import ./config.nix;
-
-let
-
-  bar = mkDerivation {
-    name = "bar";
-    builder = builtins.toFile "builder.sh"
-      ''
-        echo 'builtins.add 123 456' > $out
-      '';
-  };
-
-  value =
-    # Test that pathExists can check the existence of /nix/store paths
-    assert builtins.pathExists bar;
-    import bar;
-
-in
-
-mkDerivation {
-  name = "foo";
-  builder = builtins.toFile "builder.sh"
-    ''
-      echo -n FOO${toString value} > $out
-    '';
-}
diff --git a/third_party/nix/tests/import-derivation.sh b/third_party/nix/tests/import-derivation.sh
deleted file mode 100644
index 98d61ef49b..0000000000
--- a/third_party/nix/tests/import-derivation.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-source common.sh
-
-clearStore
-
-if nix-instantiate --readonly-mode ./import-derivation.nix; then
-    echo "read-only evaluation of an imported derivation unexpectedly failed"
-    exit 1
-fi
-
-outPath=$(nix-build ./import-derivation.nix --no-out-link)
-
-[ "$(cat $outPath)" = FOO579 ]
diff --git a/third_party/nix/tests/init.sh b/third_party/nix/tests/init.sh
deleted file mode 100644
index 19a12c1e2d..0000000000
--- a/third_party/nix/tests/init.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-source common.sh
-
-test -n "$TEST_ROOT"
-if test -d "$TEST_ROOT"; then
-    chmod -R u+w "$TEST_ROOT"
-    rm -rf "$TEST_ROOT"
-fi
-mkdir "$TEST_ROOT"
-
-mkdir "$NIX_STORE_DIR"
-mkdir "$NIX_LOCALSTATE_DIR"
-mkdir -p "$NIX_LOG_DIR"/drvs
-mkdir "$NIX_STATE_DIR"
-mkdir "$NIX_CONF_DIR"
-
-cat > "$NIX_CONF_DIR"/nix.conf <<EOF
-build-users-group =
-keep-derivations = false
-sandbox = false
-include nix.conf.extra
-EOF
-
-cat > "$NIX_CONF_DIR"/nix.conf.extra <<EOF
-fsync-metadata = false
-!include nix.conf.extra.not-there
-EOF
-
-# Initialise the database.
-nix-store --init
-
-# Did anything happen?
-test -e "$NIX_STATE_DIR"/db/db.sqlite
-
-echo 'Hello World' > ./dummy
diff --git a/third_party/nix/tests/install-darwin.sh b/third_party/nix/tests/install-darwin.sh
deleted file mode 100755
index 9933eba944..0000000000
--- a/third_party/nix/tests/install-darwin.sh
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/bin/sh
-
-set -eux
-
-cleanup() {
-    PLIST="/Library/LaunchDaemons/org.nixos.nix-daemon.plist"
-    if sudo launchctl list | grep -q nix-daemon; then
-        sudo launchctl unload "$PLIST"
-    fi
-
-    if [ -f "$PLIST" ]; then
-        sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
-    fi
-
-    profiles=(/etc/profile /etc/bashrc /etc/zshrc)
-    for profile in "${profiles[@]}"; do
-        if [ -f "${profile}.backup-before-nix" ]; then
-            sudo mv "${profile}.backup-before-nix" "${profile}"
-        fi
-    done
-
-    for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do
-        if [ -e "$file" ]; then
-            cat "$file" | grep -v nix-profile > "$file.next"
-            mv "$file.next" "$file"
-        fi
-    done
-
-    for i in $(seq 1 $(sysctl -n hw.ncpu)); do
-        sudo /usr/bin/dscl . -delete "/Users/nixbld$i" || true
-    done
-    sudo /usr/bin/dscl . -delete "/Groups/nixbld" || true
-
-    sudo rm -rf /etc/nix \
-         /nix \
-         /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels \
-         "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels"
-}
-
-verify() {
-    set +e
-    output=$(echo "nix-shell -p bash --run 'echo toow | rev'" | bash -l)
-    set -e
-
-    test "$output" = "woot"
-}
-
-scratch=$(mktemp -d -t tmp.XXXXXXXXXX)
-function finish {
-    rm -rf "$scratch"
-}
-trap finish EXIT
-
-# First setup Nix
-cleanup
-curl -o install https://nixos.org/nix/install
-yes | bash ./install
-verify
-
-
-(
-    set +e
-    (
-        echo "cd $(pwd)"
-        echo nix-build ./release.nix -A binaryTarball.x86_64-darwin
-    ) | bash -l
-    set -e
-    cp ./result/nix-*.tar.bz2 $scratch/nix.tar.bz2
-)
-
-(
-    cd $scratch
-    tar -xf ./nix.tar.bz2
-
-    cd nix-*
-
-    set -eux
-
-    cleanup
-
-    yes | ./install
-    verify
-    cleanup
-
-    echo -n "" | ./install
-    verify
-    cleanup
-
-    sudo mkdir -p /nix/store
-    sudo touch /nix/store/.silly-hint
-    echo -n "" | ALLOW_PREEXISTING_INSTALLATION=true ./install
-    verify
-    test -e /nix/store/.silly-hint
-
-    cleanup
-)
diff --git a/third_party/nix/tests/lang.sh b/third_party/nix/tests/lang.sh
deleted file mode 100644
index 151a713166..0000000000
--- a/third_party/nix/tests/lang.sh
+++ /dev/null
@@ -1,68 +0,0 @@
-export TEST_VAR=foo # for eval-okay-getenv.nix
-
-nix-instantiate --eval -E 'builtins.trace "Hello" 123' 2>&1 | grep -q Hello
-(! nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1 | grep -q Hello)
-nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" (throw "Foo")' 2>&1 | grep -q Hello
-
-set +x
-
-fail=0
-
-for i in lang/parse-fail-*.nix; do
-    echo "parsing $i (should fail)";
-    i=$(basename $i .nix)
-    if nix-instantiate --parse - < lang/$i.nix; then
-        echo "FAIL: $i shouldn't parse"
-        fail=1
-    fi
-done
-
-for i in lang/parse-okay-*.nix; do
-    echo "parsing $i (should succeed)";
-    i=$(basename $i .nix)
-    if ! nix-instantiate --parse - < lang/$i.nix > lang/$i.out; then
-        echo "FAIL: $i should parse"
-        fail=1
-    fi
-done
-
-for i in lang/eval-fail-*.nix; do
-    echo "evaluating $i (should fail)";
-    i=$(basename $i .nix)
-    if nix-instantiate --eval lang/$i.nix; then
-        echo "FAIL: $i shouldn't evaluate"
-        fail=1
-    fi
-done
-
-for i in lang/eval-okay-*.nix; do
-    echo "evaluating $i (should succeed)";
-    i=$(basename $i .nix)
-
-    if test -e lang/$i.exp; then
-        flags=
-        if test -e lang/$i.flags; then
-            flags=$(cat lang/$i.flags)
-        fi
-        if ! NIX_PATH=lang/dir3:lang/dir4 nix-instantiate $flags --eval --strict lang/$i.nix > lang/$i.out; then
-            echo "FAIL: $i should evaluate"
-            fail=1
-        elif ! diff lang/$i.out lang/$i.exp; then
-            echo "FAIL: evaluation result of $i not as expected"
-            fail=1
-        fi
-    fi
-
-    if test -e lang/$i.exp.xml; then
-        if ! nix-instantiate --eval --xml --no-location --strict \
-                lang/$i.nix > lang/$i.out.xml; then
-            echo "FAIL: $i should evaluate"
-            fail=1
-        elif ! cmp -s lang/$i.out.xml lang/$i.exp.xml; then
-            echo "FAIL: XML evaluation result of $i not as expected"
-            fail=1
-        fi
-    fi
-done
-
-exit $fail
diff --git a/third_party/nix/tests/linux-sandbox.sh b/third_party/nix/tests/linux-sandbox.sh
deleted file mode 100644
index 52967d07dd..0000000000
--- a/third_party/nix/tests/linux-sandbox.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-source common.sh
-
-clearStore
-
-if ! canUseSandbox; then exit; fi
-
-# Note: we need to bind-mount $SHELL into the chroot. Currently we
-# only support the case where $SHELL is in the Nix store, because
-# otherwise things get complicated (e.g. if it's in /bin, do we need
-# /lib as well?).
-if [[ ! $SHELL =~ /nix/store ]]; then exit; fi
-
-chmod -R u+w $TEST_ROOT/store0 || true
-rm -rf $TEST_ROOT/store0
-
-export NIX_STORE_DIR=/my/store
-export NIX_REMOTE=$TEST_ROOT/store0
-
-outPath=$(nix-build dependencies.nix --no-out-link --sandbox-paths /nix/store)
-
-[[ $outPath =~ /my/store/.*-dependencies ]]
-
-nix path-info -r $outPath | grep input-2
-
-nix ls-store -R -l $outPath | grep foobar
-
-nix cat-store $outPath/foobar | grep FOOBAR
-
-# Test --check without hash rewriting.
-nix-build dependencies.nix --no-out-link --check --sandbox-paths /nix/store
diff --git a/third_party/nix/tests/logging.sh b/third_party/nix/tests/logging.sh
deleted file mode 100644
index c894ad3ff0..0000000000
--- a/third_party/nix/tests/logging.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-source common.sh
-
-clearStore
-
-path=$(nix-build dependencies.nix --no-out-link)
-
-# Test nix-store -l.
-[ "$(nix-store -l $path)" = FOO ]
-
-# Test compressed logs.
-clearStore
-rm -rf $NIX_LOG_DIR
-(! nix-store -l $path)
-nix-build dependencies.nix --no-out-link --compress-build-log
-[ "$(nix-store -l $path)" = FOO ]
diff --git a/third_party/nix/tests/misc.sh b/third_party/nix/tests/misc.sh
deleted file mode 100644
index eda0164167..0000000000
--- a/third_party/nix/tests/misc.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-source common.sh
-
-# Tests miscellaneous commands.
-
-# Do all commands have help?
-#nix-env --help | grep -q install
-#nix-store --help | grep -q realise
-#nix-instantiate --help | grep -q eval
-#nix-hash --help | grep -q base32
-
-# Can we ask for the version number?
-nix-env --version | grep "$version"
-
-# Usage errors.
-nix-env --foo 2>&1 | grep "no operation"
-nix-env -q --foo 2>&1 | grep "unknown flag"
-
-# Eval Errors.
-nix-instantiate --eval -E 'let a = {} // a; in a.foo' 2>&1 | grep "infinite recursion encountered, at .*(string).*:1:15$"
diff --git a/third_party/nix/tests/multiple-outputs.nix b/third_party/nix/tests/multiple-outputs.nix
deleted file mode 100644
index 4a9010d186..0000000000
--- a/third_party/nix/tests/multiple-outputs.nix
+++ /dev/null
@@ -1,68 +0,0 @@
-with import ./config.nix;
-
-rec {
-
-  a = mkDerivation {
-    name = "multiple-outputs-a";
-    outputs = [ "first" "second" ];
-    builder = builtins.toFile "builder.sh"
-      ''
-        mkdir $first $second
-        test -z $all
-        echo "first" > $first/file
-        echo "second" > $second/file
-        ln -s $first $second/link
-      '';
-    helloString = "Hello, world!";
-  };
-
-  b = mkDerivation {
-    defaultOutput = assert a.second.helloString == "Hello, world!"; a;
-    firstOutput = assert a.outputName == "first"; a.first.first;
-    secondOutput = assert a.second.outputName == "second"; a.second.first.first.second.second.first.second;
-    allOutputs = a.all;
-    name = "multiple-outputs-b";
-    builder = builtins.toFile "builder.sh"
-      ''
-        mkdir $out
-        test "$firstOutput $secondOutput" = "$allOutputs"
-        test "$defaultOutput" = "$firstOutput"
-        test "$(cat $firstOutput/file)" = "first"
-        test "$(cat $secondOutput/file)" = "second"
-        echo "success" > $out/file
-      '';
-  };
-
-  c = mkDerivation {
-    name = "multiple-outputs-c";
-    drv = b.drvPath;
-    builder = builtins.toFile "builder.sh"
-      ''
-        mkdir $out
-        ln -s $drv $out/drv
-      '';
-  };
-
-  d = mkDerivation {
-    name = "multiple-outputs-d";
-    drv = builtins.unsafeDiscardOutputDependency b.drvPath;
-    builder = builtins.toFile "builder.sh"
-      ''
-        mkdir $out
-        echo $drv > $out/drv
-      '';
-  };
-
-  cyclic = (mkDerivation {
-    name = "cyclic-outputs";
-    outputs = [ "a" "b" "c" ];
-    builder = builtins.toFile "builder.sh"
-      ''
-        mkdir $a $b $c
-        echo $a > $b/foo
-        echo $b > $c/bar
-        echo $c > $a/baz
-      '';
-  }).a;
-
-}
diff --git a/third_party/nix/tests/multiple-outputs.sh b/third_party/nix/tests/multiple-outputs.sh
deleted file mode 100644
index bedbc39a4e..0000000000
--- a/third_party/nix/tests/multiple-outputs.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-source common.sh
-
-clearStore
-
-rm -f $TEST_ROOT/result*
-
-# Test whether read-only evaluation works when referring to the
-# β€˜drvPath’ attribute.
-echo "evaluating c..."
-#drvPath=$(nix-instantiate multiple-outputs.nix -A c --readonly-mode)
-
-# And check whether the resulting derivation explicitly depends on all
-# outputs.
-drvPath=$(nix-instantiate multiple-outputs.nix -A c)
-#[ "$drvPath" = "$drvPath2" ]
-grep -q 'multiple-outputs-a.drv",\["first","second"\]' $drvPath
-grep -q 'multiple-outputs-b.drv",\["out"\]' $drvPath
-
-# While we're at it, test the β€˜unsafeDiscardOutputDependency’ primop.
-outPath=$(nix-build multiple-outputs.nix -A d --no-out-link)
-drvPath=$(cat $outPath/drv)
-outPath=$(nix-store -q $drvPath)
-(! [ -e "$outPath" ])
-
-# Do a build of something that depends on a derivation with multiple
-# outputs.
-echo "building b..."
-outPath=$(nix-build multiple-outputs.nix -A b --no-out-link)
-echo "output path is $outPath"
-[ "$(cat "$outPath"/file)" = "success" ]
-
-# Test nix-build on a derivation with multiple outputs.
-outPath1=$(nix-build multiple-outputs.nix -A a -o $TEST_ROOT/result)
-[ -e $TEST_ROOT/result-first ]
-(! [ -e $TEST_ROOT/result-second ])
-nix-build multiple-outputs.nix -A a.all -o $TEST_ROOT/result
-[ "$(cat $TEST_ROOT/result-first/file)" = "first" ]
-[ "$(cat $TEST_ROOT/result-second/file)" = "second" ]
-[ "$(cat $TEST_ROOT/result-second/link/file)" = "first" ]
-hash1=$(nix-store -q --hash $TEST_ROOT/result-second)
-
-outPath2=$(nix-build $(nix-instantiate multiple-outputs.nix -A a) --no-out-link)
-[[ $outPath1 = $outPath2 ]]
-
-outPath2=$(nix-build $(nix-instantiate multiple-outputs.nix -A a.first) --no-out-link)
-[[ $outPath1 = $outPath2 ]]
-
-outPath2=$(nix-build $(nix-instantiate multiple-outputs.nix -A a.second) --no-out-link)
-[[ $(cat $outPath2/file) = second ]]
-
-[[ $(nix-build $(nix-instantiate multiple-outputs.nix -A a.all) --no-out-link | wc -l) -eq 2 ]]
-
-# Delete one of the outputs and rebuild it.  This will cause a hash
-# rewrite.
-nix-store --delete $TEST_ROOT/result-second --ignore-liveness
-nix-build multiple-outputs.nix -A a.all -o $TEST_ROOT/result
-[ "$(cat $TEST_ROOT/result-second/file)" = "second" ]
-[ "$(cat $TEST_ROOT/result-second/link/file)" = "first" ]
-hash2=$(nix-store -q --hash $TEST_ROOT/result-second)
-[ "$hash1" = "$hash2" ]
-
-# Make sure that nix-build works on derivations with multiple outputs.
-echo "building a.first..."
-nix-build multiple-outputs.nix -A a.first --no-out-link
-
-# Cyclic outputs should be rejected.
-echo "building cyclic..."
-if nix-build multiple-outputs.nix -A cyclic --no-out-link; then
-    echo "Cyclic outputs incorrectly accepted!"
-    exit 1
-fi
-
-echo "collecting garbage..."
-rm $TEST_ROOT/result*
-nix-store --gc --keep-derivations --keep-outputs
-nix-store --gc --print-roots
diff --git a/third_party/nix/tests/nar-access.nix b/third_party/nix/tests/nar-access.nix
deleted file mode 100644
index 0e2a7f7211..0000000000
--- a/third_party/nix/tests/nar-access.nix
+++ /dev/null
@@ -1,23 +0,0 @@
-with import ./config.nix;
-
-rec {
-    a = mkDerivation {
-        name = "nar-index-a";
-        builder = builtins.toFile "builder.sh"
-      ''
-        mkdir $out
-        mkdir $out/foo
-        touch $out/foo-x
-        touch $out/foo/bar
-        touch $out/foo/baz
-        touch $out/qux
-        mkdir $out/zyx
-
-        cat >$out/foo/data <<EOF
-        lasjdΓΆaxnasd
-asdom 12398
-Γ€"Β§Γ†αΊžΒ’Β«Β»β€alsd
-EOF
-      '';
-    };
-}
\ No newline at end of file
diff --git a/third_party/nix/tests/nar-access.sh b/third_party/nix/tests/nar-access.sh
deleted file mode 100644
index 553d6ca89d..0000000000
--- a/third_party/nix/tests/nar-access.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-source common.sh
-
-echo "building test path"
-storePath="$(nix-build nar-access.nix -A a --no-out-link)"
-
-cd "$TEST_ROOT"
-
-# Dump path to nar.
-narFile="$TEST_ROOT/path.nar"
-nix-store --dump $storePath > $narFile
-
-# Check that find and ls-nar match.
-( cd $storePath; find . | sort ) > files.find
-nix ls-nar -R -d $narFile "" | sort > files.ls-nar
-diff -u files.find files.ls-nar
-
-# Check that file contents of data match.
-nix cat-nar $narFile /foo/data > data.cat-nar
-diff -u data.cat-nar $storePath/foo/data
-
-# Check that file contents of baz match.
-nix cat-nar $narFile /foo/baz > baz.cat-nar
-diff -u baz.cat-nar $storePath/foo/baz
-
-nix cat-store $storePath/foo/baz > baz.cat-nar
-diff -u baz.cat-nar $storePath/foo/baz
-
-# Test --json.
-[[ $(nix ls-nar --json $narFile /) = '{"type":"directory","entries":{"foo":{},"foo-x":{},"qux":{},"zyx":{}}}' ]]
-[[ $(nix ls-nar --json -R $narFile /foo) = '{"type":"directory","entries":{"bar":{"type":"regular","size":0,"narOffset":368},"baz":{"type":"regular","size":0,"narOffset":552},"data":{"type":"regular","size":58,"narOffset":736}}}' ]]
-[[ $(nix ls-nar --json -R $narFile /foo/bar) = '{"type":"regular","size":0,"narOffset":368}' ]]
-[[ $(nix ls-store --json $storePath) = '{"type":"directory","entries":{"foo":{},"foo-x":{},"qux":{},"zyx":{}}}' ]]
-[[ $(nix ls-store --json -R $storePath/foo) = '{"type":"directory","entries":{"bar":{"type":"regular","size":0},"baz":{"type":"regular","size":0},"data":{"type":"regular","size":58}}}' ]]
-[[ $(nix ls-store --json -R $storePath/foo/bar) = '{"type":"regular","size":0}' ]]
-
-# Test missing files.
-nix ls-store --json -R $storePath/xyzzy 2>&1 | grep 'does not exist in NAR'
-nix ls-store $storePath/xyzzy 2>&1 | grep 'does not exist'
-
-# Test failure to dump.
-if nix-store --dump $storePath >/dev/full ; then
-    echo "dumping to /dev/full should fail"
-    exit -1
-fi
diff --git a/third_party/nix/tests/nix-build.sh b/third_party/nix/tests/nix-build.sh
deleted file mode 100644
index 3952648631..0000000000
--- a/third_party/nix/tests/nix-build.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-source common.sh
-
-clearStore
-
-outPath=$(nix-build dependencies.nix -o $TEST_ROOT/result)
-test "$(cat $TEST_ROOT/result/foobar)" = FOOBAR
-
-# The result should be retained by a GC.
-echo A
-target=$(readLink $TEST_ROOT/result)
-echo B
-echo target is $target
-nix-store --gc
-test -e $target/foobar
-
-# But now it should be gone.
-rm $TEST_ROOT/result
-nix-store --gc
-if test -e $target/foobar; then false; fi
-
-outPath2=$(nix-build $(nix-instantiate dependencies.nix) --no-out-link)
-[[ $outPath = $outPath2 ]]
-
-outPath2=$(nix-build $(nix-instantiate dependencies.nix)!out --no-out-link)
-[[ $outPath = $outPath2 ]]
diff --git a/third_party/nix/tests/nix-channel.sh b/third_party/nix/tests/nix-channel.sh
deleted file mode 100644
index 93f837befc..0000000000
--- a/third_party/nix/tests/nix-channel.sh
+++ /dev/null
@@ -1,59 +0,0 @@
-source common.sh
-
-clearProfiles
-
-rm -f $TEST_HOME/.nix-channels $TEST_HOME/.nix-profile
-
-# Test add/list/remove.
-nix-channel --add http://foo/bar xyzzy
-nix-channel --list | grep -q http://foo/bar
-nix-channel --remove xyzzy
-
-[ -e $TEST_HOME/.nix-channels ]
-[ "$(cat $TEST_HOME/.nix-channels)" = '' ]
-
-# Create a channel.
-rm -rf $TEST_ROOT/foo
-mkdir -p $TEST_ROOT/foo
-nix copy --to file://$TEST_ROOT/foo?compression="bzip2" $(nix-store -r $(nix-instantiate dependencies.nix))
-rm -rf $TEST_ROOT/nixexprs
-mkdir -p $TEST_ROOT/nixexprs
-cp config.nix dependencies.nix dependencies.builder*.sh $TEST_ROOT/nixexprs/
-ln -s dependencies.nix $TEST_ROOT/nixexprs/default.nix
-(cd $TEST_ROOT && tar cvf - nixexprs) | bzip2 > $TEST_ROOT/foo/nixexprs.tar.bz2
-
-# Test the update action.
-nix-channel --add file://$TEST_ROOT/foo
-nix-channel --update
-
-# Do a query.
-nix-env -qa \* --meta --xml --out-path > $TEST_ROOT/meta.xml
-if [ "$xmllint" != false ]; then
-    $xmllint --noout $TEST_ROOT/meta.xml || fail "malformed XML"
-fi
-grep -q 'meta.*description.*Random test package' $TEST_ROOT/meta.xml
-grep -q 'item.*attrPath="foo".*name="dependencies"' $TEST_ROOT/meta.xml
-
-# Do an install.
-nix-env -i dependencies
-[ -e $TEST_HOME/.nix-profile/foobar ]
-
-clearProfiles
-rm -f $TEST_HOME/.nix-channels
-
-# Test updating from a tarball
-nix-channel --add file://$TEST_ROOT/foo/nixexprs.tar.bz2 foo
-nix-channel --update
-
-# Do a query.
-nix-env -qa \* --meta --xml --out-path > $TEST_ROOT/meta.xml
-if [ "$xmllint" != false ]; then
-    $xmllint --noout $TEST_ROOT/meta.xml || fail "malformed XML"
-fi
-grep -q 'meta.*description.*Random test package' $TEST_ROOT/meta.xml
-grep -q 'item.*attrPath="foo".*name="dependencies"' $TEST_ROOT/meta.xml
-
-# Do an install.
-nix-env -i dependencies
-[ -e $TEST_HOME/.nix-profile/foobar ]
-
diff --git a/third_party/nix/tests/nix-copy-closure.nix b/third_party/nix/tests/nix-copy-closure.nix
deleted file mode 100644
index 0dc147fb34..0000000000
--- a/third_party/nix/tests/nix-copy-closure.nix
+++ /dev/null
@@ -1,64 +0,0 @@
-# Test β€˜nix-copy-closure’.
-
-{ nixpkgs, system, nix }:
-
-with import (nixpkgs + "/nixos/lib/testing.nix") { inherit system; };
-
-makeTest (let pkgA = pkgs.cowsay; pkgB = pkgs.wget; pkgC = pkgs.hello; in {
-
-  nodes =
-    { client =
-        { config, pkgs, ... }:
-        { virtualisation.writableStore = true;
-          virtualisation.pathsInNixDB = [ pkgA ];
-          nix.package = nix;
-          nix.binaryCaches = [ ];
-        };
-
-      server =
-        { config, pkgs, ... }:
-        { services.openssh.enable = true;
-          virtualisation.writableStore = true;
-          virtualisation.pathsInNixDB = [ pkgB pkgC ];
-          nix.package = nix;
-        };
-    };
-
-  testScript = { nodes }:
-    ''
-      startAll;
-
-      # Create an SSH key on the client.
-      my $key = `${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f key -N ""`;
-      $client->succeed("mkdir -m 700 /root/.ssh");
-      $client->copyFileFromHost("key", "/root/.ssh/id_ed25519");
-      $client->succeed("chmod 600 /root/.ssh/id_ed25519");
-
-      # Install the SSH key on the server.
-      $server->succeed("mkdir -m 700 /root/.ssh");
-      $server->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
-      $server->waitForUnit("sshd");
-      $client->waitForUnit("network.target");
-      $client->succeed("ssh -o StrictHostKeyChecking=no " . $server->name() . " 'echo hello world'");
-
-      # Copy the closure of package A from the client to the server.
-      $server->fail("nix-store --check-validity ${pkgA}");
-      $client->succeed("nix-copy-closure --to server --gzip ${pkgA} >&2");
-      $server->succeed("nix-store --check-validity ${pkgA}");
-
-      # Copy the closure of package B from the server to the client.
-      $client->fail("nix-store --check-validity ${pkgB}");
-      $client->succeed("nix-copy-closure --from server --gzip ${pkgB} >&2");
-      $client->succeed("nix-store --check-validity ${pkgB}");
-
-      # Copy the closure of package C via the SSH substituter.
-      $client->fail("nix-store -r ${pkgC}");
-      # FIXME
-      #$client->succeed(
-      #  "nix-store --option use-ssh-substituter true"
-      #  . " --option ssh-substituter-hosts root\@server"
-      #  . " -r ${pkgC} >&2");
-      #$client->succeed("nix-store --check-validity ${pkgC}");
-    '';
-
-})
diff --git a/third_party/nix/tests/nix-copy-ssh.sh b/third_party/nix/tests/nix-copy-ssh.sh
deleted file mode 100644
index eb801548d2..0000000000
--- a/third_party/nix/tests/nix-copy-ssh.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-source common.sh
-
-clearStore
-clearCache
-
-remoteRoot=$TEST_ROOT/store2
-chmod -R u+w "$remoteRoot" || true
-rm -rf "$remoteRoot"
-
-outPath=$(nix-build --no-out-link dependencies.nix)
-
-nix copy --to "ssh://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath
-
-[ -f $remoteRoot$outPath/foobar ]
-
-clearStore
-
-nix copy --no-check-sigs --from "ssh://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath
-
-[ -f $outPath/foobar ]
diff --git a/third_party/nix/tests/nix-profile.sh b/third_party/nix/tests/nix-profile.sh
deleted file mode 100644
index e2e0d10908..0000000000
--- a/third_party/nix/tests/nix-profile.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-source common.sh
-
-sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh
-
-user=$(whoami)
-rm -rf $TEST_HOME $TEST_ROOT/profile-var
-mkdir -p $TEST_HOME
-USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh; set"
-USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh" # test idempotency
diff --git a/third_party/nix/tests/nix-shell.sh b/third_party/nix/tests/nix-shell.sh
deleted file mode 100644
index ee502dddb9..0000000000
--- a/third_party/nix/tests/nix-shell.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-source common.sh
-
-clearStore
-
-# Test nix-shell -A
-export IMPURE_VAR=foo
-export SELECTED_IMPURE_VAR=baz
-export NIX_BUILD_SHELL=$SHELL
-output=$(nix-shell --pure shell.nix -A shellDrv --run \
-    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"')
-
-[ "$output" = " - foo - bar" ]
-
-# Test --keep
-output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR shell.nix -A shellDrv --run \
-    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"')
-
-[ "$output" = " - foo - bar - baz" ]
-
-# Test nix-shell on a .drv
-[[ $(nix-shell --pure $(nix-instantiate shell.nix -A shellDrv) --run \
-    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]]
-
-[[ $(nix-shell --pure $(nix-instantiate shell.nix -A shellDrv) --run \
-    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]]
-
-# Test nix-shell on a .drv symlink
-
-# Legacy: absolute path and .drv extension required
-nix-instantiate shell.nix -A shellDrv --indirect --add-root $TEST_ROOT/shell.drv
-[[ $(nix-shell --pure $TEST_ROOT/shell.drv --run \
-    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]]
-
-# New behaviour: just needs to resolve to a derivation in the store
-nix-instantiate shell.nix -A shellDrv --indirect --add-root $TEST_ROOT/shell
-[[ $(nix-shell --pure $TEST_ROOT/shell --run \
-    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]]
-
-# Test nix-shell -p
-output=$(NIX_PATH=nixpkgs=shell.nix nix-shell --pure -p foo bar --run 'echo "$(foo) $(bar)"')
-[ "$output" = "foo bar" ]
-
-# Test nix-shell shebang mode
-sed -e "s|@ENV_PROG@|$(type -p env)|" shell.shebang.sh > $TEST_ROOT/shell.shebang.sh
-chmod a+rx $TEST_ROOT/shell.shebang.sh
-
-output=$($TEST_ROOT/shell.shebang.sh abc def)
-[ "$output" = "foo bar abc def" ]
-
-# Test nix-shell shebang mode for ruby
-# This uses a fake interpreter that returns the arguments passed
-# This, in turn, verifies the `rc` script is valid and the `load()` script (given using `-e`) is as expected.
-sed -e "s|@SHELL_PROG@|$(type -p nix-shell)|" shell.shebang.rb > $TEST_ROOT/shell.shebang.rb
-chmod a+rx $TEST_ROOT/shell.shebang.rb
-
-output=$($TEST_ROOT/shell.shebang.rb abc ruby)
-[ "$output" = '-e load("'"$TEST_ROOT"'/shell.shebang.rb") -- abc ruby' ]
diff --git a/third_party/nix/tests/optimise-store.sh b/third_party/nix/tests/optimise-store.sh
deleted file mode 100644
index 61e3df2f9f..0000000000
--- a/third_party/nix/tests/optimise-store.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-source common.sh
-
-clearStore
-
-outPath1=$(echo 'with import ./config.nix; mkDerivation { name = "foo1"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store)
-outPath2=$(echo 'with import ./config.nix; mkDerivation { name = "foo2"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store)
-
-inode1="$(stat --format=%i $outPath1/foo)"
-inode2="$(stat --format=%i $outPath2/foo)"
-if [ "$inode1" != "$inode2" ]; then
-    echo "inodes do not match"
-    exit 1
-fi
-
-nlink="$(stat --format=%h $outPath1/foo)"
-if [ "$nlink" != 3 ]; then
-    echo "link count incorrect"
-    exit 1
-fi
-
-outPath3=$(echo 'with import ./config.nix; mkDerivation { name = "foo3"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link)
-
-inode3="$(stat --format=%i $outPath3/foo)"
-if [ "$inode1" = "$inode3" ]; then
-    echo "inodes match unexpectedly"
-    exit 1
-fi
-
-nix-store --optimise
-
-inode1="$(stat --format=%i $outPath1/foo)"
-inode3="$(stat --format=%i $outPath3/foo)"
-if [ "$inode1" != "$inode3" ]; then
-    echo "inodes do not match"
-    exit 1
-fi
-
-nix-store --gc
-
-if [ -n "$(ls $NIX_STORE_DIR/.links)" ]; then
-    echo ".links directory not empty after GC"
-    exit 1
-fi
diff --git a/third_party/nix/tests/parallel.builder.sh b/third_party/nix/tests/parallel.builder.sh
deleted file mode 100644
index d092bc5a6b..0000000000
--- a/third_party/nix/tests/parallel.builder.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-echo "DOING $text"
-
-
-# increase counter
-while ! ln -s x $shared.lock 2> /dev/null; do
-    sleep 1
-done
-test -f $shared.cur || echo 0 > $shared.cur
-test -f $shared.max || echo 0 > $shared.max
-new=$(($(cat $shared.cur) + 1))
-if test $new -gt $(cat $shared.max); then
-    echo $new > $shared.max
-fi
-echo $new > $shared.cur
-rm $shared.lock
-
-
-echo -n $(cat $inputs)$text > $out
-
-sleep $sleepTime
-
-
-# decrease counter
-while ! ln -s x $shared.lock 2> /dev/null; do
-    sleep 1
-done
-test -f $shared.cur || echo 0 > $shared.cur
-echo $(($(cat $shared.cur) - 1)) > $shared.cur
-rm $shared.lock
diff --git a/third_party/nix/tests/parallel.nix b/third_party/nix/tests/parallel.nix
deleted file mode 100644
index 23f142059f..0000000000
--- a/third_party/nix/tests/parallel.nix
+++ /dev/null
@@ -1,19 +0,0 @@
-{sleepTime ? 3}:
-
-with import ./config.nix;
-
-let
-
-  mkDrv = text: inputs: mkDerivation {
-    name = "parallel";
-    builder = ./parallel.builder.sh;
-    inherit text inputs shared sleepTime;
-  };
-
-  a = mkDrv "a" [];
-  b = mkDrv "b" [a];
-  c = mkDrv "c" [a];
-  d = mkDrv "d" [a];
-  e = mkDrv "e" [b c d];
-
-in e
diff --git a/third_party/nix/tests/parallel.sh b/third_party/nix/tests/parallel.sh
deleted file mode 100644
index 3b7bbe5a22..0000000000
--- a/third_party/nix/tests/parallel.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-source common.sh
-
-
-# First, test that -jN performs builds in parallel.
-echo "testing nix-build -j..."
-
-clearStore
-
-rm -f $_NIX_TEST_SHARED.cur $_NIX_TEST_SHARED.max
-
-outPath=$(nix-build -j10000 parallel.nix --no-out-link)
-
-echo "output path is $outPath"
-
-text=$(cat "$outPath")
-if test "$text" != "abacade"; then exit 1; fi
-
-if test "$(cat $_NIX_TEST_SHARED.cur)" != 0; then fail "wrong current process count"; fi
-if test "$(cat $_NIX_TEST_SHARED.max)" != 3; then fail "not enough parallelism"; fi
-
-
-# Second, test that parallel invocations of nix-build perform builds
-# in parallel, and don't block waiting on locks held by the others.
-echo "testing multiple nix-build -j1..."
-
-clearStore
-
-rm -f $_NIX_TEST_SHARED.cur $_NIX_TEST_SHARED.max
-
-drvPath=$(nix-instantiate parallel.nix --argstr sleepTime 15)
-
-cmd="nix-store -j1 -r $drvPath"
-
-$cmd &
-pid1=$!
-echo "pid 1 is $pid1"
-
-$cmd &
-pid2=$!
-echo "pid 2 is $pid2"
-
-$cmd &
-pid3=$!
-echo "pid 3 is $pid3"
-
-$cmd &
-pid4=$!
-echo "pid 4 is $pid4"
-
-wait $pid1 || fail "instance 1 failed: $?"
-wait $pid2 || fail "instance 2 failed: $?"
-wait $pid3 || fail "instance 3 failed: $?"
-wait $pid4 || fail "instance 4 failed: $?"
-
-if test "$(cat $_NIX_TEST_SHARED.cur)" != 0; then fail "wrong current process count"; fi
-if test "$(cat $_NIX_TEST_SHARED.max)" != 3; then fail "not enough parallelism"; fi
diff --git a/third_party/nix/tests/pass-as-file.sh b/third_party/nix/tests/pass-as-file.sh
deleted file mode 100644
index 2c0bc5031a..0000000000
--- a/third_party/nix/tests/pass-as-file.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-source common.sh
-
-clearStore
-
-outPath=$(nix-build --no-out-link -E "
-with import ./config.nix;
-
-mkDerivation {
-  name = \"pass-as-file\";
-  passAsFile = [ \"foo\" ];
-  foo = [ \"xyzzy\" ];
-  builder = builtins.toFile \"builder.sh\" ''
-    [ \"\$(basename \$fooPath)\" = .attr-1bp7cri8hplaz6hbz0v4f0nl44rl84q1sg25kgwqzipzd1mv89ic ]
-    [ \"\$(cat \$fooPath)\" = xyzzy ]
-    touch \$out
-  '';
-}
-")
diff --git a/third_party/nix/tests/placeholders.sh b/third_party/nix/tests/placeholders.sh
deleted file mode 100644
index cd1bb7bc2a..0000000000
--- a/third_party/nix/tests/placeholders.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-source common.sh
-
-clearStore
-
-nix-build --no-out-link -E '
-  with import ./config.nix;
-
-  mkDerivation {
-    name = "placeholders";
-    outputs = [ "out" "bin" "dev" ];
-    buildCommand = "
-      echo foo1 > $out
-      echo foo2 > $bin
-      echo foo3 > $dev
-      [[ $(cat ${placeholder "out"}) = foo1 ]]
-      [[ $(cat ${placeholder "bin"}) = foo2 ]]
-      [[ $(cat ${placeholder "dev"}) = foo3 ]]
-    ";
-  }
-'
diff --git a/third_party/nix/tests/post-hook.sh b/third_party/nix/tests/post-hook.sh
deleted file mode 100644
index a026572154..0000000000
--- a/third_party/nix/tests/post-hook.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-source common.sh
-
-clearStore
-
-export REMOTE_STORE=$TEST_ROOT/remote_store
-
-# Build the dependencies and push them to the remote store
-nix-build -o $TEST_ROOT/result dependencies.nix --post-build-hook $PWD/push-to-store.sh
-
-clearStore
-
-# Ensure that we the remote store contains both the runtime and buildtime
-# closure of what we've just built
-nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix
-nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix input1_drv
diff --git a/third_party/nix/tests/pure-eval.nix b/third_party/nix/tests/pure-eval.nix
deleted file mode 100644
index ed25b3d456..0000000000
--- a/third_party/nix/tests/pure-eval.nix
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  x = 123;
-}
diff --git a/third_party/nix/tests/pure-eval.sh b/third_party/nix/tests/pure-eval.sh
deleted file mode 100644
index 49c8564487..0000000000
--- a/third_party/nix/tests/pure-eval.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-source common.sh
-
-clearStore
-
-nix eval --pure-eval '(assert 1 + 2 == 3; true)'
-
-[[ $(nix eval '(builtins.readFile ./pure-eval.sh)') =~ clearStore ]]
-
-(! nix eval --pure-eval '(builtins.readFile ./pure-eval.sh)')
-
-(! nix eval --pure-eval '(builtins.currentTime)')
-(! nix eval --pure-eval '(builtins.currentSystem)')
-
-(! nix-instantiate --pure-eval ./simple.nix)
-
-[[ $(nix eval "((import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; })).x)") == 123 ]]
-(! nix eval --pure-eval "((import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; })).x)")
-nix eval --pure-eval "((import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; sha256 = \"$(nix hash-file pure-eval.nix --type sha256)\"; })).x)"
diff --git a/third_party/nix/tests/push-to-store.sh b/third_party/nix/tests/push-to-store.sh
deleted file mode 100755
index 6aadb916ba..0000000000
--- a/third_party/nix/tests/push-to-store.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-echo Pushing "$@" to "$REMOTE_STORE"
-printf "%s" "$OUT_PATHS" | xargs -d: nix copy --to "$REMOTE_STORE" --no-require-sigs
diff --git a/third_party/nix/tests/referrers.sh b/third_party/nix/tests/referrers.sh
deleted file mode 100644
index 8ab8e5ddfe..0000000000
--- a/third_party/nix/tests/referrers.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-source common.sh
-
-clearStore
-
-max=500
-
-reference=$NIX_STORE_DIR/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-touch $reference
-(echo $reference && echo && echo 0) | nix-store --register-validity 
-
-echo "making registration..."
-
-set +x
-for ((n = 0; n < $max; n++)); do
-    storePath=$NIX_STORE_DIR/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-$n
-    echo -n > $storePath
-    ref2=$NIX_STORE_DIR/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-$((n+1))
-    if test $((n+1)) = $max; then
-        ref2=$reference
-    fi
-    echo $storePath; echo; echo 2; echo $reference; echo $ref2
-done > $TEST_ROOT/reg_info
-set -x
-
-echo "registering..."
-
-nix-store --register-validity < $TEST_ROOT/reg_info
-
-echo "collecting garbage..."
-ln -sfn $reference "$NIX_STATE_DIR"/gcroots/ref
-nix-store --gc
-
-if [ -n "$(type -p sqlite3)" -a "$(sqlite3 $NIX_STATE_DIR/db/db.sqlite 'select count(*) from Refs')" -ne 0 ]; then
-    echo "referrers not cleaned up"
-    exit 1
-fi
diff --git a/third_party/nix/tests/remote-builds.nix b/third_party/nix/tests/remote-builds.nix
deleted file mode 100644
index b867f13b49..0000000000
--- a/third_party/nix/tests/remote-builds.nix
+++ /dev/null
@@ -1,108 +0,0 @@
-# Test Nix's remote build feature.
-
-{ nixpkgs, system, nix }:
-
-with import (nixpkgs + "/nixos/lib/testing.nix") { inherit system; };
-
-makeTest (
-
-let
-
-  # The configuration of the remote builders.
-  builder =
-    { config, pkgs, ... }:
-    { services.openssh.enable = true;
-      virtualisation.writableStore = true;
-      nix.package = nix;
-      nix.useSandbox = true;
-    };
-
-  # Trivial Nix expression to build remotely.
-  expr = config: nr: pkgs.writeText "expr.nix"
-    ''
-      let utils = builtins.storePath ${config.system.build.extraUtils}; in
-      derivation {
-        name = "hello-${toString nr}";
-        system = "i686-linux";
-        PATH = "''${utils}/bin";
-        builder = "''${utils}/bin/sh";
-        args = [ "-c" "if [ ${toString nr} = 5 ]; then echo FAIL; exit 1; fi; echo Hello; mkdir $out $foo; cat /proc/sys/kernel/hostname > $out/host; ln -s $out $foo/bar; sleep 10" ];
-        outputs = [ "out" "foo" ];
-      }
-    '';
-
-in
-
-{
-
-  nodes =
-    { builder1 = builder;
-      builder2 = builder;
-
-      client =
-        { config, pkgs, ... }:
-        { nix.maxJobs = 0; # force remote building
-          nix.distributedBuilds = true;
-          nix.buildMachines =
-            [ { hostName = "builder1";
-                sshUser = "root";
-                sshKey = "/root/.ssh/id_ed25519";
-                system = "i686-linux";
-                maxJobs = 1;
-              }
-              { hostName = "builder2";
-                sshUser = "root";
-                sshKey = "/root/.ssh/id_ed25519";
-                system = "i686-linux";
-                maxJobs = 1;
-              }
-            ];
-          virtualisation.writableStore = true;
-          virtualisation.pathsInNixDB = [ config.system.build.extraUtils ];
-          nix.package = nix;
-          nix.binaryCaches = [ ];
-          programs.ssh.extraConfig = "ConnectTimeout 30";
-        };
-    };
-
-  testScript = { nodes }:
-    ''
-      startAll;
-
-      # Create an SSH key on the client.
-      my $key = `${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f key -N ""`;
-      $client->succeed("mkdir -p -m 700 /root/.ssh");
-      $client->copyFileFromHost("key", "/root/.ssh/id_ed25519");
-      $client->succeed("chmod 600 /root/.ssh/id_ed25519");
-
-      # Install the SSH key on the builders.
-      $client->waitForUnit("network.target");
-      foreach my $builder ($builder1, $builder2) {
-          $builder->succeed("mkdir -p -m 700 /root/.ssh");
-          $builder->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
-          $builder->waitForUnit("sshd");
-          $client->succeed("ssh -o StrictHostKeyChecking=no " . $builder->name() . " 'echo hello world'");
-      }
-
-      # Perform a build and check that it was performed on the builder.
-      my $out = $client->succeed(
-        "nix-build ${expr nodes.client.config 1} 2> build-output",
-        "grep -q Hello build-output"
-      );
-      $builder1->succeed("test -e $out");
-
-      # And a parallel build.
-      my ($out1, $out2) = split /\s/,
-          $client->succeed('nix-store -r $(nix-instantiate ${expr nodes.client.config 2})\!out $(nix-instantiate ${expr nodes.client.config 3})\!out');
-      $builder1->succeed("test -e $out1 -o -e $out2");
-      $builder2->succeed("test -e $out1 -o -e $out2");
-
-      # And a failing build.
-      $client->fail("nix-build ${expr nodes.client.config 5}");
-
-      # Test whether the build hook automatically skips unavailable builders.
-      $builder1->block;
-      $client->succeed("nix-build ${expr nodes.client.config 4}");
-    '';
-
-})
diff --git a/third_party/nix/tests/remote-store.sh b/third_party/nix/tests/remote-store.sh
deleted file mode 100644
index 77437658ea..0000000000
--- a/third_party/nix/tests/remote-store.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-source common.sh
-
-clearStore
-
-startDaemon
-
-storeCleared=1 $SHELL ./user-envs.sh
-
-nix-store --dump-db > $TEST_ROOT/d1
-NIX_REMOTE= nix-store --dump-db > $TEST_ROOT/d2
-cmp $TEST_ROOT/d1 $TEST_ROOT/d2
-
-nix-store --gc --max-freed 1K
-
-killDaemon
-
-user=$(whoami)
-[ -e $NIX_STATE_DIR/gcroots/per-user/$user ]
-[ -e $NIX_STATE_DIR/profiles/per-user/$user ]
diff --git a/third_party/nix/tests/repair.sh b/third_party/nix/tests/repair.sh
deleted file mode 100644
index ec7ad5dcaf..0000000000
--- a/third_party/nix/tests/repair.sh
+++ /dev/null
@@ -1,77 +0,0 @@
-source common.sh
-
-clearStore
-
-path=$(nix-build dependencies.nix -o $TEST_ROOT/result)
-path2=$(nix-store -qR $path | grep input-2)
-
-nix-store --verify --check-contents -v
-
-hash=$(nix-hash $path2)
-
-# Corrupt a path and check whether nix-build --repair can fix it.
-chmod u+w $path2
-touch $path2/bad
-
-if nix-store --verify --check-contents -v; then
-    echo "nix-store --verify succeeded unexpectedly" >&2
-    exit 1
-fi
-
-# The path can be repaired by rebuilding the derivation.
-nix-store --verify --check-contents --repair
-
-nix-store --verify-path $path2
-
-# Re-corrupt and delete the deriver. Now --verify --repair should
-# not work.
-chmod u+w $path2
-touch $path2/bad
-
-nix-store --delete $(nix-store -qd $path2)
-
-if nix-store --verify --check-contents --repair; then
-    echo "nix-store --verify --repair succeeded unexpectedly" >&2
-    exit 1
-fi
-
-nix-build dependencies.nix -o $TEST_ROOT/result --repair
-
-if [ "$(nix-hash $path2)" != "$hash" -o -e $path2/bad ]; then
-    echo "path not repaired properly" >&2
-    exit 1
-fi
-
-# Corrupt a path that has a substitute and check whether nix-store
-# --verify can fix it.
-clearCache
-
-nix copy --to file://$cacheDir $path
-
-chmod u+w $path2
-rm -rf $path2
-
-nix-store --verify --check-contents --repair --substituters "file://$cacheDir" --no-require-sigs
-
-if [ "$(nix-hash $path2)" != "$hash" -o -e $path2/bad ]; then
-    echo "path not repaired properly" >&2
-    exit 1
-fi
-
-# Check --verify-path and --repair-path.
-nix-store --verify-path $path2
-
-chmod u+w $path2
-rm -rf $path2
-
-if nix-store --verify-path $path2; then
-    echo "nix-store --verify-path succeeded unexpectedly" >&2
-    exit 1
-fi
-
-nix-store --repair-path $path2 --substituters "file://$cacheDir" --no-require-sigs
-
-if [ "$(nix-hash $path2)" != "$hash" -o -e $path2/bad ]; then
-    echo "path not repaired properly" >&2
-    exit 1
-fi
diff --git a/third_party/nix/tests/restricted.nix b/third_party/nix/tests/restricted.nix
deleted file mode 100644
index e0ef584020..0000000000
--- a/third_party/nix/tests/restricted.nix
+++ /dev/null
@@ -1 +0,0 @@
-1 + 2
diff --git a/third_party/nix/tests/restricted.sh b/third_party/nix/tests/restricted.sh
deleted file mode 100644
index e02becc60e..0000000000
--- a/third_party/nix/tests/restricted.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-source common.sh
-
-clearStore
-
-nix-instantiate --restrict-eval --eval -E '1 + 2'
-(! nix-instantiate --restrict-eval ./restricted.nix)
-(! nix-instantiate --eval --restrict-eval <(echo '1 + 2'))
-nix-instantiate --restrict-eval ./simple.nix -I src=.
-nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh
-
-(! nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix')
-nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=..
-
-(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel')
-nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel' -I src=../src
-
-(! nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>')
-nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>' -I src=.
-
-p=$(nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)")
-cmp $p restricted.sh
-
-(! nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval)
-
-(! nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh/")
-
-nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh"
-
-(! nix eval --raw "(builtins.fetchurl https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
-(! nix eval --raw "(builtins.fetchTarball https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
-(! nix eval --raw "(fetchGit git://github.com/NixOS/patchelf.git)" --restrict-eval)
-
-ln -sfn $(pwd)/restricted.nix $TEST_ROOT/restricted.nix
-[[ $(nix-instantiate --eval $TEST_ROOT/restricted.nix) == 3 ]]
-(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix)
-(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT)
-(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I .)
-nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT -I .
-
-[[ $(nix eval --raw --restrict-eval -I . '(builtins.readFile "${import ./simple.nix}/hello")') == 'Hello World!' ]]
-
-# Check whether we can leak symlink information through directory traversal.
-traverseDir="$(pwd)/restricted-traverse-me"
-ln -sfn "$(pwd)/restricted-secret" "$(pwd)/restricted-innocent"
-mkdir -p "$traverseDir"
-goUp="..$(echo "$traverseDir" | sed -e 's,[^/]\+,..,g')"
-output="$(nix eval --raw --restrict-eval -I "$traverseDir" \
-    "(builtins.readFile \"$traverseDir/$goUp$(pwd)/restricted-innocent\")" \
-    2>&1 || :)"
-echo "$output" | grep "is forbidden"
-! echo "$output" | grep -F restricted-secret
diff --git a/third_party/nix/tests/run.nix b/third_party/nix/tests/run.nix
deleted file mode 100644
index 77dcbd2a9d..0000000000
--- a/third_party/nix/tests/run.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-with import ./config.nix;
-
-{
-  hello = mkDerivation {
-    name = "hello";
-    buildCommand =
-      ''
-        mkdir -p $out/bin
-        cat > $out/bin/hello <<EOF
-        #! ${shell}
-        who=\$1
-        echo "Hello \''${who:-World} from $out/bin/hello"
-        EOF
-        chmod +x $out/bin/hello
-      '';
-  };
-}
diff --git a/third_party/nix/tests/run.sh b/third_party/nix/tests/run.sh
deleted file mode 100644
index d1dbfd6bd4..0000000000
--- a/third_party/nix/tests/run.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-source common.sh
-
-clearStore
-clearCache
-
-nix run -f run.nix hello -c hello | grep 'Hello World'
-nix run -f run.nix hello -c hello NixOS | grep 'Hello NixOS'
-
-if ! canUseSandbox; then exit; fi
-
-chmod -R u+w $TEST_ROOT/store0 || true
-rm -rf $TEST_ROOT/store0
-
-clearStore
-
-path=$(nix eval --raw -f run.nix hello)
-
-# Note: we need the sandbox paths to ensure that the shell is
-# visible in the sandbox.
-nix run --sandbox-build-dir /build-tmp \
-    --sandbox-paths '/nix? /bin? /lib? /lib64? /usr?' \
-    --store $TEST_ROOT/store0 -f run.nix hello -c hello | grep 'Hello World'
-
-path2=$(nix run --sandbox-paths '/nix? /bin? /lib? /lib64? /usr?' --store $TEST_ROOT/store0 -f run.nix hello -c $SHELL -c 'type -p hello')
-
-[[ $path/bin/hello = $path2 ]]
-
-[[ -e $TEST_ROOT/store0/nix/store/$(basename $path)/bin/hello ]]
diff --git a/third_party/nix/tests/search.nix b/third_party/nix/tests/search.nix
deleted file mode 100644
index fea6e7a7a6..0000000000
--- a/third_party/nix/tests/search.nix
+++ /dev/null
@@ -1,25 +0,0 @@
-with import ./config.nix;
-
-{
-  hello = mkDerivation rec {
-    name = "hello-${version}";
-    version = "0.1";
-    buildCommand = "touch $out";
-    meta.description = "Empty file";
-  };
-  foo = mkDerivation rec {
-    name = "foo-5";
-    buildCommand = ''
-      mkdir -p $out
-      echo ${name} > $out/${name}
-    '';
-  };
-  bar = mkDerivation rec {
-    name = "bar-3";
-    buildCommand = ''
-      echo "Does not build successfully"
-      exit 1
-    '';
-    meta.description = "broken bar";
-  };
-}
diff --git a/third_party/nix/tests/search.sh b/third_party/nix/tests/search.sh
deleted file mode 100644
index 14da3127b0..0000000000
--- a/third_party/nix/tests/search.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-source common.sh
-
-clearStore
-clearCache
-
-# No packages
-(( $(NIX_PATH= nix search -u|wc -l) == 0 ))
-
-# Haven't updated cache, still nothing
-(( $(nix search -f search.nix hello|wc -l) == 0 ))
-(( $(nix search -f search.nix |wc -l) == 0 ))
-
-# Update cache, search should work
-(( $(nix search -f search.nix -u hello|wc -l) > 0 ))
-
-# Use cache
-(( $(nix search -f search.nix foo|wc -l) > 0 ))
-(( $(nix search foo|wc -l) > 0 ))
-
-# Test --no-cache works
-# No results from cache
-(( $(nix search --no-cache foo |wc -l) == 0 ))
-# Does find results from file pointed at
-(( $(nix search -f search.nix --no-cache foo |wc -l) > 0 ))
-
-# Check descriptions are searched
-(( $(nix search broken | wc -l) > 0 ))
-
-# Check search that matches nothing
-(( $(nix search nosuchpackageexists | wc -l) == 0 ))
-
-# Search for multiple arguments
-(( $(nix search hello empty | wc -l) == 3 ))
-
-# Multiple arguments will not exist
-(( $(nix search hello broken | wc -l) == 0 ))
-
-## Search expressions
-
-# Check that empty search string matches all
-nix search|grep -q foo
-nix search|grep -q bar
-nix search|grep -q hello
diff --git a/third_party/nix/tests/secure-drv-outputs.nix b/third_party/nix/tests/secure-drv-outputs.nix
deleted file mode 100644
index b4ac8ff531..0000000000
--- a/third_party/nix/tests/secure-drv-outputs.nix
+++ /dev/null
@@ -1,23 +0,0 @@
-with import ./config.nix;
-
-{
-
-  good = mkDerivation {
-    name = "good";
-    builder = builtins.toFile "builder"
-      ''
-        mkdir $out
-        echo > $out/good
-      '';
-  };
-
-  bad = mkDerivation {
-    name = "good";
-    builder = builtins.toFile "builder"
-      ''
-        mkdir $out
-        echo > $out/bad
-      '';
-  };
-
-}
diff --git a/third_party/nix/tests/secure-drv-outputs.sh b/third_party/nix/tests/secure-drv-outputs.sh
deleted file mode 100644
index 50a9c4428d..0000000000
--- a/third_party/nix/tests/secure-drv-outputs.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-# Test that users cannot register specially-crafted derivations that
-# produce output paths belonging to other derivations.  This could be
-# used to inject malware into the store.
-
-source common.sh
-
-clearStore
-
-startDaemon
-
-# Determine the output path of the "good" derivation.
-goodOut=$(nix-store -q $(nix-instantiate ./secure-drv-outputs.nix -A good))
-
-# Instantiate the "bad" derivation.
-badDrv=$(nix-instantiate ./secure-drv-outputs.nix -A bad)
-badOut=$(nix-store -q $badDrv)
-
-# Rewrite the bad derivation to produce the output path of the good
-# derivation.
-rm -f $TEST_ROOT/bad.drv
-sed -e "s|$badOut|$goodOut|g" < $badDrv > $TEST_ROOT/bad.drv
-
-# Add the manipulated derivation to the store and build it.  This
-# should fail.
-if badDrv2=$(nix-store --add $TEST_ROOT/bad.drv); then
-    nix-store -r "$badDrv2"
-fi
-
-# Now build the good derivation.
-goodOut2=$(nix-build ./secure-drv-outputs.nix -A good --no-out-link)
-test "$goodOut" = "$goodOut2"
-
-if ! test -e "$goodOut"/good; then
-    echo "Bad derivation stole the output path of the good derivation!"
-    exit 1
-fi
diff --git a/third_party/nix/tests/setuid.nix b/third_party/nix/tests/setuid.nix
deleted file mode 100644
index 77e83c8d6c..0000000000
--- a/third_party/nix/tests/setuid.nix
+++ /dev/null
@@ -1,108 +0,0 @@
-# Verify that Linux builds cannot create setuid or setgid binaries.
-
-{ nixpkgs, system, nix }:
-
-with import (nixpkgs + "/nixos/lib/testing.nix") { inherit system; };
-
-makeTest {
-
-  machine =
-    { config, lib, pkgs, ... }:
-    { virtualisation.writableStore = true;
-      nix.package = nix;
-      nix.binaryCaches = [ ];
-      nix.nixPath = [ "nixpkgs=${lib.cleanSource pkgs.path}" ];
-      virtualisation.pathsInNixDB = [ pkgs.stdenv pkgs.pkgsi686Linux.stdenv ];
-    };
-
-  testScript = { nodes }:
-    ''
-      startAll;
-
-      # Copying to /tmp should succeed.
-      $machine->succeed('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" {} "
-        mkdir -p $out
-        cp ${pkgs.coreutils}/bin/id /tmp/id
-      ")\' ');
-
-      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
-
-      $machine->succeed("rm /tmp/id");
-
-      # Creating a setuid binary should fail.
-      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" {} "
-        mkdir -p $out
-        cp ${pkgs.coreutils}/bin/id /tmp/id
-        chmod 4755 /tmp/id
-      ")\' ');
-
-      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
-
-      $machine->succeed("rm /tmp/id");
-
-      # Creating a setgid binary should fail.
-      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" {} "
-        mkdir -p $out
-        cp ${pkgs.coreutils}/bin/id /tmp/id
-        chmod 2755 /tmp/id
-      ")\' ');
-
-      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
-
-      $machine->succeed("rm /tmp/id");
-
-      # The checks should also work on 32-bit binaries.
-      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> { system = "i686-linux"; }; runCommand "foo" {} "
-        mkdir -p $out
-        cp ${pkgs.coreutils}/bin/id /tmp/id
-        chmod 2755 /tmp/id
-      ")\' ');
-
-      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
-
-      $machine->succeed("rm /tmp/id");
-
-      # The tests above use fchmodat(). Test chmod() as well.
-      $machine->succeed('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
-        mkdir -p $out
-        cp ${pkgs.coreutils}/bin/id /tmp/id
-        perl -e \"chmod 0666, qw(/tmp/id) or die\"
-      ")\' ');
-
-      $machine->succeed('[[ $(stat -c %a /tmp/id) = 666 ]]');
-
-      $machine->succeed("rm /tmp/id");
-
-      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
-        mkdir -p $out
-        cp ${pkgs.coreutils}/bin/id /tmp/id
-        perl -e \"chmod 04755, qw(/tmp/id) or die\"
-      ")\' ');
-
-      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
-
-      $machine->succeed("rm /tmp/id");
-
-      # And test fchmod().
-      $machine->succeed('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
-        mkdir -p $out
-        cp ${pkgs.coreutils}/bin/id /tmp/id
-        perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 01750, \\\$x or die\"
-      ")\' ');
-
-      $machine->succeed('[[ $(stat -c %a /tmp/id) = 1750 ]]');
-
-      $machine->succeed("rm /tmp/id");
-
-      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
-        mkdir -p $out
-        cp ${pkgs.coreutils}/bin/id /tmp/id
-        perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 04777, \\\$x or die\"
-      ")\' ');
-
-      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
-
-      $machine->succeed("rm /tmp/id");
-    '';
-
-}
diff --git a/third_party/nix/tests/shell.nix b/third_party/nix/tests/shell.nix
deleted file mode 100644
index eb39f9039a..0000000000
--- a/third_party/nix/tests/shell.nix
+++ /dev/null
@@ -1,56 +0,0 @@
-{ }:
-
-with import ./config.nix;
-
-let pkgs = rec {
-  setupSh = builtins.toFile "setup" ''
-    export VAR_FROM_STDENV_SETUP=foo
-    for pkg in $buildInputs; do
-      export PATH=$PATH:$pkg/bin
-    done
-  '';
-
-  stdenv = mkDerivation {
-    name = "stdenv";
-    buildCommand = ''
-      mkdir -p $out
-      ln -s ${setupSh} $out/setup
-    '';
-  };
-
-  shellDrv = mkDerivation {
-    name = "shellDrv";
-    builder = "/does/not/exist";
-    VAR_FROM_NIX = "bar";
-    inherit stdenv;
-  };
-
-  # Used by nix-shell -p
-  runCommand = name: args: buildCommand: mkDerivation (args // {
-    inherit name buildCommand stdenv;
-  });
-
-  foo = runCommand "foo" {} ''
-    mkdir -p $out/bin
-    echo 'echo foo' > $out/bin/foo
-    chmod a+rx $out/bin/foo
-    ln -s ${shell} $out/bin/bash
-  '';
-
-  bar = runCommand "bar" {} ''
-    mkdir -p $out/bin
-    echo 'echo bar' > $out/bin/bar
-    chmod a+rx $out/bin/bar
-  '';
-
-  bash = shell;
-
-  # ruby "interpreter" that outputs "$@"
-  ruby = runCommand "ruby" {} ''
-    mkdir -p $out/bin
-    echo 'printf -- "$*"' > $out/bin/ruby
-    chmod a+rx $out/bin/ruby
-  '';
-
-  inherit pkgs;
-}; in pkgs
diff --git a/third_party/nix/tests/shell.shebang.rb b/third_party/nix/tests/shell.shebang.rb
deleted file mode 100644
index ea67eb09c1..0000000000
--- a/third_party/nix/tests/shell.shebang.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-#! @SHELL_PROG@
-#! ruby
-#! nix-shell -I nixpkgs=shell.nix --no-substitute
-#! nix-shell --pure -p ruby -i ruby
-
-# Contents doesn't matter.
-abort("This shouldn't be executed.")
diff --git a/third_party/nix/tests/shell.shebang.sh b/third_party/nix/tests/shell.shebang.sh
deleted file mode 100755
index f7132043de..0000000000
--- a/third_party/nix/tests/shell.shebang.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#! @ENV_PROG@ nix-shell
-#! nix-shell -I nixpkgs=shell.nix --no-substitute
-#! nix-shell --pure -i bash -p foo bar
-echo "$(foo) $(bar) $@"
diff --git a/third_party/nix/tests/signing.sh b/third_party/nix/tests/signing.sh
deleted file mode 100644
index 9e29e3fbf0..0000000000
--- a/third_party/nix/tests/signing.sh
+++ /dev/null
@@ -1,105 +0,0 @@
-source common.sh
-
-clearStore
-clearCache
-
-nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1
-pk1=$(cat $TEST_ROOT/pk1)
-nix-store --generate-binary-cache-key cache2.example.org $TEST_ROOT/sk2 $TEST_ROOT/pk2
-pk2=$(cat $TEST_ROOT/pk2)
-
-# Build a path.
-outPath=$(nix-build dependencies.nix --no-out-link --secret-key-files "$TEST_ROOT/sk1 $TEST_ROOT/sk2")
-
-# Verify that the path got signed.
-info=$(nix path-info --json $outPath)
-[[ $info =~ '"ultimate":true' ]]
-[[ $info =~ 'cache1.example.org' ]]
-[[ $info =~ 'cache2.example.org' ]]
-
-# Test "nix verify".
-nix verify -r $outPath
-
-expect 2 nix verify -r $outPath --sigs-needed 1
-
-nix verify -r $outPath --sigs-needed 1 --trusted-public-keys $pk1
-
-expect 2 nix verify -r $outPath --sigs-needed 2 --trusted-public-keys $pk1
-
-nix verify -r $outPath --sigs-needed 2 --trusted-public-keys "$pk1 $pk2"
-
-nix verify --all --sigs-needed 2 --trusted-public-keys "$pk1 $pk2"
-
-# Build something unsigned.
-outPath2=$(nix-build simple.nix --no-out-link)
-
-nix verify -r $outPath
-
-# Verify that the path did not get signed but does have the ultimate bit.
-info=$(nix path-info --json $outPath2)
-[[ $info =~ '"ultimate":true' ]]
-(! [[ $info =~ 'signatures' ]])
-
-# Test "nix verify".
-nix verify -r $outPath2
-
-expect 2 nix verify -r $outPath2 --sigs-needed 1
-
-expect 2 nix verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1
-
-# Test "nix sign-paths".
-nix sign-paths --key-file $TEST_ROOT/sk1 $outPath2
-
-nix verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1
-
-# Build something content-addressed.
-outPathCA=$(IMPURE_VAR1=foo IMPURE_VAR2=bar nix-build ./fixed.nix -A good.0 --no-out-link)
-
-[[ $(nix path-info --json $outPathCA) =~ '"ca":"fixed:md5:' ]]
-
-# Content-addressed paths don't need signatures, so they verify
-# regardless of --sigs-needed.
-nix verify $outPathCA
-nix verify $outPathCA --sigs-needed 1000
-
-# Check that signing a content-addressed path doesn't overflow validSigs
-nix sign-paths --key-file $TEST_ROOT/sk1 $outPathCA
-nix verify -r $outPathCA --sigs-needed 1000 --trusted-public-keys $pk1
-
-# Copy to a binary cache.
-nix copy --to file://$cacheDir $outPath2
-
-# Verify that signatures got copied.
-info=$(nix path-info --store file://$cacheDir --json $outPath2)
-(! [[ $info =~ '"ultimate":true' ]])
-[[ $info =~ 'cache1.example.org' ]]
-(! [[ $info =~ 'cache2.example.org' ]])
-
-# Verify that adding a signature to a path in a binary cache works.
-nix sign-paths --store file://$cacheDir --key-file $TEST_ROOT/sk2 $outPath2
-info=$(nix path-info --store file://$cacheDir --json $outPath2)
-[[ $info =~ 'cache1.example.org' ]]
-[[ $info =~ 'cache2.example.org' ]]
-
-# Copying to a diverted store should fail due to a lack of valid signatures.
-chmod -R u+w $TEST_ROOT/store0 || true
-rm -rf $TEST_ROOT/store0
-(! nix copy --to $TEST_ROOT/store0 $outPath)
-
-# But succeed if we supply the public keys.
-nix copy --to $TEST_ROOT/store0 $outPath --trusted-public-keys $pk1
-
-expect 2 nix verify --store $TEST_ROOT/store0 -r $outPath
-
-nix verify --store $TEST_ROOT/store0 -r $outPath --trusted-public-keys $pk1
-nix verify --store $TEST_ROOT/store0 -r $outPath --sigs-needed 2 --trusted-public-keys "$pk1 $pk2"
-
-# It should also succeed if we disable signature checking.
-(! nix copy --to $TEST_ROOT/store0 $outPath2)
-nix copy --to $TEST_ROOT/store0?require-sigs=false $outPath2
-
-# But signatures should still get copied.
-nix verify --store $TEST_ROOT/store0 -r $outPath2 --trusted-public-keys $pk1
-
-# Content-addressed stuff can be copied without signatures.
-nix copy --to $TEST_ROOT/store0 $outPathCA
diff --git a/third_party/nix/tests/simple.builder.sh b/third_party/nix/tests/simple.builder.sh
deleted file mode 100644
index 569e8ca88c..0000000000
--- a/third_party/nix/tests/simple.builder.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-echo "PATH=$PATH"
-
-# Verify that the PATH is empty.
-if mkdir foo 2> /dev/null; then exit 1; fi
-
-# Set a PATH (!!! impure).
-export PATH=$goodPath
-
-mkdir $out
-
-echo "Hello World!" > $out/hello
\ No newline at end of file
diff --git a/third_party/nix/tests/simple.nix b/third_party/nix/tests/simple.nix
deleted file mode 100644
index 4223c0f23a..0000000000
--- a/third_party/nix/tests/simple.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-with import ./config.nix;
-
-mkDerivation {
-  name = "simple";
-  builder = ./simple.builder.sh;
-  PATH = "";
-  goodPath = path;
-}
diff --git a/third_party/nix/tests/simple.sh b/third_party/nix/tests/simple.sh
deleted file mode 100644
index 37631b648c..0000000000
--- a/third_party/nix/tests/simple.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-source common.sh
-
-drvPath=$(nix-instantiate simple.nix)
-
-test "$(nix-store -q --binding system "$drvPath")" = "$system"
-
-echo "derivation is $drvPath"
-
-outPath=$(nix-store -rvv "$drvPath")
-
-echo "output path is $outPath"
-
-text=$(cat "$outPath"/hello)
-if test "$text" != "Hello World!"; then exit 1; fi
-
-# Directed delete: $outPath is not reachable from a root, so it should
-# be deleteable.
-nix-store --delete $outPath
-if test -e $outPath/hello; then false; fi
-
-outPath="$(NIX_REMOTE=local?store=/foo\&real=$TEST_ROOT/real-store nix-instantiate --readonly-mode hash-check.nix)"
-if test "$outPath" != "/foo/lfy1s6ca46rm5r6w4gg9hc0axiakjcnm-dependencies.drv"; then
-    echo "hashDerivationModulo appears broken, got $outPath"
-    exit 1
-fi
diff --git a/third_party/nix/tests/structured-attrs.nix b/third_party/nix/tests/structured-attrs.nix
deleted file mode 100644
index 6c77a43913..0000000000
--- a/third_party/nix/tests/structured-attrs.nix
+++ /dev/null
@@ -1,66 +0,0 @@
-with import ./config.nix;
-
-let
-
-  dep = mkDerivation {
-    name = "dep";
-    buildCommand = ''
-      mkdir $out; echo bla > $out/bla
-    '';
-  };
-
-in
-
-mkDerivation {
-  name = "structured";
-
-  __structuredAttrs = true;
-
-  buildCommand = ''
-    set -x
-
-    [[ $int = 123456789 ]]
-    [[ -z $float ]]
-    [[ -n $boolTrue ]]
-    [[ -z $boolFalse ]]
-    [[ -n ''${hardening[format]} ]]
-    [[ -z ''${hardening[fortify]} ]]
-    [[ ''${#buildInputs[@]} = 7 ]]
-    [[ ''${buildInputs[2]} = c ]]
-    [[ -v nothing ]]
-    [[ -z $nothing ]]
-
-    mkdir ''${outputs[out]}
-    echo bar > $dest
-
-    json=$(cat .attrs.json)
-    [[ $json =~ '"narHash":"sha256:1r7yc43zqnzl5b0als5vnyp649gk17i37s7mj00xr8kc47rjcybk"' ]]
-    [[ $json =~ '"narSize":288' ]]
-    [[ $json =~ '"closureSize":288' ]]
-    [[ $json =~ '"references":[]' ]]
-  '';
-
-  buildInputs = [ "a" "b" "c" 123 "'" "\"" null ];
-
-  hardening.format = true;
-  hardening.fortify = false;
-
-  outer.inner = [ 1 2 3 ];
-
-  int = 123456789;
-
-  float = 123.456;
-
-  boolTrue = true;
-  boolFalse = false;
-
-  nothing = null;
-
-  dest = "${placeholder "out"}/foo";
-
-  "foo bar" = "BAD";
-  "1foobar" = "BAD";
-  "foo$" = "BAD";
-
-  exportReferencesGraph.refs = [ dep ];
-}
diff --git a/third_party/nix/tests/structured-attrs.sh b/third_party/nix/tests/structured-attrs.sh
deleted file mode 100644
index 9ba2672b68..0000000000
--- a/third_party/nix/tests/structured-attrs.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-source common.sh
-
-clearStore
-
-outPath=$(nix-build structured-attrs.nix --no-out-link)
-
-[[ $(cat $outPath/foo) = bar ]]
diff --git a/third_party/nix/tests/tarball.sh b/third_party/nix/tests/tarball.sh
deleted file mode 100644
index ba534c6261..0000000000
--- a/third_party/nix/tests/tarball.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-source common.sh
-
-clearStore
-
-rm -rf $TEST_HOME
-
-tarroot=$TEST_ROOT/tarball
-rm -rf $tarroot
-mkdir -p $tarroot
-cp dependencies.nix $tarroot/default.nix
-cp config.nix dependencies.builder*.sh $tarroot/
-
-tarball=$TEST_ROOT/tarball.tar.xz
-(cd $TEST_ROOT && tar c tarball) | xz > $tarball
-
-nix-env -f file://$tarball -qa --out-path | grep -q dependencies
-
-nix-build -o $TEST_ROOT/result file://$tarball
-
-nix-build -o $TEST_ROOT/result '<foo>' -I foo=file://$tarball
-
-nix-build -o $TEST_ROOT/result -E "import (fetchTarball file://$tarball)"
-
-nix-instantiate --eval -E '1 + 2' -I fnord=file://no-such-tarball.tar.xz
-nix-instantiate --eval -E 'with <fnord/xyzzy>; 1 + 2' -I fnord=file://no-such-tarball.tar.xz
-(! nix-instantiate --eval -E '<fnord/xyzzy> 1' -I fnord=file://no-such-tarball.tar.xz)
-
-nix-instantiate --eval -E '<fnord/config.nix>' -I fnord=file://no-such-tarball.tar.xz -I fnord=.
diff --git a/third_party/nix/tests/timeout.nix b/third_party/nix/tests/timeout.nix
deleted file mode 100644
index d0e949e314..0000000000
--- a/third_party/nix/tests/timeout.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-with import ./config.nix;
-
-{
-
-  infiniteLoop = mkDerivation {
-    name = "timeout";
-    buildCommand = ''
-      touch $out
-      echo "'timeout' builder entering an infinite loop"
-      while true ; do echo -n .; done
-    '';
-  };
-
-  silent = mkDerivation {
-    name = "silent";
-    buildCommand = ''
-      touch $out
-      sleep 60
-    '';
-  };
-
-  closeLog = mkDerivation {
-    name = "silent";
-    buildCommand = ''
-      touch $out
-      exec > /dev/null 2>&1
-      sleep 1000000000
-    '';
-  };
-
-}
diff --git a/third_party/nix/tests/timeout.sh b/third_party/nix/tests/timeout.sh
deleted file mode 100644
index eea9b5731d..0000000000
--- a/third_party/nix/tests/timeout.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-# Test the `--timeout' option.
-
-source common.sh
-
-
-set +e
-messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1)
-status=$?
-set -e
-
-if [ $status -ne 101 ]; then
-    echo "error: 'nix-store' exited with '$status'; should have exited 101"
-    exit 1
-fi
-
-if ! echo "$messages" | grep -q "timed out"; then
-    echo "error: build may have failed for reasons other than timeout; output:"
-    echo "$messages" >&2
-    exit 1
-fi
-
-if nix-build -Q timeout.nix -A infiniteLoop --max-build-log-size 100; then
-    echo "build should have failed"
-    exit 1
-fi
-
-if nix-build timeout.nix -A silent --max-silent-time 2; then
-    echo "build should have failed"
-    exit 1
-fi
-
-if nix-build timeout.nix -A closeLog; then
-    echo "build should have failed"
-    exit 1
-fi
-
-if nix build -f timeout.nix silent --max-silent-time 2; then
-    echo "build should have failed"
-    exit 1
-fi
diff --git a/third_party/nix/tests/user-envs.builder.sh b/third_party/nix/tests/user-envs.builder.sh
deleted file mode 100644
index 5fafa797f1..0000000000
--- a/third_party/nix/tests/user-envs.builder.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-mkdir $out
-mkdir $out/bin
-echo "#! $shell" > $out/bin/$progName
-echo "echo $name" >> $out/bin/$progName
-chmod +x $out/bin/$progName
diff --git a/third_party/nix/tests/user-envs.nix b/third_party/nix/tests/user-envs.nix
deleted file mode 100644
index 1aa410cc96..0000000000
--- a/third_party/nix/tests/user-envs.nix
+++ /dev/null
@@ -1,29 +0,0 @@
-# Some dummy arguments...
-{ foo ? "foo"
-}:
-
-with import ./config.nix;
-
-assert foo == "foo";
-
-let
-
-  makeDrv = name: progName: (mkDerivation {
-    inherit name progName system;
-    builder = ./user-envs.builder.sh;
-  } // {
-    meta = {
-      description = "A silly test package";
-    };
-  });
-
-in
-
-  [
-    (makeDrv "foo-1.0" "foo")
-    (makeDrv "foo-2.0pre1" "foo")
-    (makeDrv "bar-0.1" "bar")
-    (makeDrv "foo-2.0" "foo")
-    (makeDrv "bar-0.1.1" "bar")
-    (makeDrv "foo-0.1" "foo" // { meta.priority = 10; })
-  ]
diff --git a/third_party/nix/tests/user-envs.sh b/third_party/nix/tests/user-envs.sh
deleted file mode 100644
index aebf6a2a2b..0000000000
--- a/third_party/nix/tests/user-envs.sh
+++ /dev/null
@@ -1,181 +0,0 @@
-source common.sh
-
-if [ -z "$storeCleared" ]; then
-    clearStore
-fi
-
-clearProfiles
-
-# Query installed: should be empty.
-test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0
-
-mkdir -p $TEST_HOME
-nix-env --switch-profile $profiles/test
-
-# Query available: should contain several.
-test "$(nix-env -f ./user-envs.nix -qa '*' | wc -l)" -eq 6
-outPath10=$(nix-env -f ./user-envs.nix -qa --out-path --no-name '*' | grep foo-1.0)
-drvPath10=$(nix-env -f ./user-envs.nix -qa --drv-path --no-name '*' | grep foo-1.0)
-[ -n "$outPath10" -a -n "$drvPath10" ]
-
-# Query descriptions.
-nix-env -f ./user-envs.nix -qa '*' --description | grep -q silly
-rm -rf $HOME/.nix-defexpr
-ln -s $(pwd)/user-envs.nix $HOME/.nix-defexpr
-nix-env -qa '*' --description | grep -q silly
-
-# Query the system.
-nix-env -qa '*' --system | grep -q $system
-
-# Install "foo-1.0".
-nix-env -i foo-1.0
-
-# Query installed: should contain foo-1.0 now (which should be
-# executable).
-test "$(nix-env -q '*' | wc -l)" -eq 1
-nix-env -q '*' | grep -q foo-1.0
-test "$($profiles/test/bin/foo)" = "foo-1.0"
-
-# Test nix-env -qc to compare installed against available packages, and vice versa.
-nix-env -qc '*' | grep -q '< 2.0'
-nix-env -qac '*' | grep -q '> 1.0'
-
-# Test the -b flag to filter out source-only packages.
-[ "$(nix-env -qab | wc -l)" -eq 1 ]
-
-# Test the -s flag to get package status.
-nix-env -qas | grep -q 'IP-  foo-1.0'
-nix-env -qas | grep -q -- '---  bar-0.1'
-
-# Disable foo.
-nix-env --set-flag active false foo
-(! [ -e "$profiles/test/bin/foo" ])
-
-# Enable foo.
-nix-env --set-flag active true foo
-[ -e "$profiles/test/bin/foo" ]
-
-# Store the path of foo-1.0.
-outPath10_=$(nix-env -q --out-path --no-name '*' | grep foo-1.0)
-echo "foo-1.0 = $outPath10"
-[ "$outPath10" = "$outPath10_" ]
-
-# Install "foo-2.0pre1": should remove foo-1.0.
-nix-env -i foo-2.0pre1
-
-# Query installed: should contain foo-2.0pre1 now.
-test "$(nix-env -q '*' | wc -l)" -eq 1
-nix-env -q '*' | grep -q foo-2.0pre1
-test "$($profiles/test/bin/foo)" = "foo-2.0pre1"
-
-# Upgrade "foo": should install foo-2.0.
-NIX_PATH=nixpkgs=./user-envs.nix:$NIX_PATH nix-env -f '<nixpkgs>' -u foo
-
-# Query installed: should contain foo-2.0 now.
-test "$(nix-env -q '*' | wc -l)" -eq 1
-nix-env -q '*' | grep -q foo-2.0
-test "$($profiles/test/bin/foo)" = "foo-2.0"
-
-# Store the path of foo-2.0.
-outPath20=$(nix-env -q --out-path --no-name '*' | grep foo-2.0)
-test -n "$outPath20"
-
-# Install bar-0.1, uninstall foo.
-nix-env -i bar-0.1
-nix-env -e foo
-
-# Query installed: should only contain bar-0.1 now.
-if nix-env -q '*' | grep -q foo; then false; fi
-nix-env -q '*' | grep -q bar
-
-# Rollback: should bring "foo" back.
-oldGen="$(nix-store -q --resolve $profiles/test)"
-nix-env --rollback
-[ "$(nix-store -q --resolve $profiles/test)" != "$oldGen" ]
-nix-env -q '*' | grep -q foo-2.0
-nix-env -q '*' | grep -q bar
-
-# Rollback again: should remove "bar".
-nix-env --rollback
-nix-env -q '*' | grep -q foo-2.0
-if nix-env -q '*' | grep -q bar; then false; fi
-
-# Count generations.
-nix-env --list-generations
-test "$(nix-env --list-generations | wc -l)" -eq 7
-
-# Doing the same operation twice results in the same generation, which triggers
-# "lazy" behaviour and does not create a new symlink.
-
-nix-env -i foo
-nix-env -i foo
-
-# Count generations.
-nix-env --list-generations
-test "$(nix-env --list-generations | wc -l)" -eq 8
-
-# Switch to a specified generation.
-nix-env --switch-generation 7
-[ "$(nix-store -q --resolve $profiles/test)" = "$oldGen" ]
-
-# Install foo-1.0, now using its store path.
-nix-env -i "$outPath10"
-nix-env -q '*' | grep -q foo-1.0
-nix-store -qR $profiles/test | grep "$outPath10"
-nix-store -q --referrers-closure $profiles/test | grep "$(nix-store -q --resolve $profiles/test)"
-[ "$(nix-store -q --deriver "$outPath10")" = $drvPath10 ]
-
-# Uninstall foo-1.0, using a symlink to its store path.
-ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink
-nix-env -e $TEST_ROOT/symlink
-if nix-env -q '*' | grep -q foo; then false; fi
-(! nix-store -qR $profiles/test | grep "$outPath10")
-
-# Install foo-1.0, now using a symlink to its store path.
-nix-env -i $TEST_ROOT/symlink
-nix-env -q '*' | grep -q foo
-
-# Delete all old generations.
-nix-env --delete-generations old
-
-# Run the garbage collector.  This should get rid of foo-2.0 but not
-# foo-1.0.
-nix-collect-garbage
-test -e "$outPath10"
-(! [ -e "$outPath20" ])
-
-# Uninstall everything
-nix-env -e '*'
-test "$(nix-env -q '*' | wc -l)" -eq 0
-
-# Installing "foo" should only install the newest foo.
-nix-env -i foo
-test "$(nix-env -q '*' | grep foo- | wc -l)" -eq 1
-nix-env -q '*' | grep -q foo-2.0
-
-# On the other hand, this should install both (and should fail due to
-# a collision).
-nix-env -e '*'
-(! nix-env -i foo-1.0 foo-2.0)
-
-# Installing "*" should install one foo and one bar.
-nix-env -e '*'
-nix-env -i '*'
-test "$(nix-env -q '*' | wc -l)" -eq 2
-nix-env -q '*' | grep -q foo-2.0
-nix-env -q '*' | grep -q bar-0.1.1
-
-# Test priorities: foo-0.1 has a lower priority than foo-1.0, so it
-# should be possible to install both without a collision.  Also test
-# β€˜--set-flag priority’ to manually override the declared priorities.
-nix-env -e '*'
-nix-env -i foo-0.1 foo-1.0
-[ "$($profiles/test/bin/foo)" = "foo-1.0" ]
-nix-env --set-flag priority 1 foo-0.1
-[ "$($profiles/test/bin/foo)" = "foo-0.1" ]
-
-# Test nix-env --set.
-nix-env --set $outPath10
-[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ]
-nix-env --set $drvPath10
-[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ]
diff --git a/third_party/nixery/default.nix b/third_party/nixery/default.nix
deleted file mode 100644
index be3a9dfc1b..0000000000
--- a/third_party/nixery/default.nix
+++ /dev/null
@@ -1,18 +0,0 @@
-# Import the Nixery repository as-is, but pass our own package set
-# instead of the pin it has.
-{ depot, pkgs, ... }:
-
-let
-  inherit (depot.nix.readTree) drvTargets;
-
-  commit = "601cd998077f77f257ad1a40fa488add8464650f";
-  src = pkgs.fetchFromGitHub {
-    owner = "google";
-    repo = "nixery";
-    rev = commit;
-    sha256 = "195rz25y3hfxcmniysajzjg7g69qhz7w06lql8fn0dbcdcxsq6g4";
-  };
-in drvTargets (import src {
-  inherit pkgs;
-  commitHash = _: commit;
-})
diff --git a/third_party/nixpkgs/default.nix b/third_party/nixpkgs/default.nix
index 5afed93e4c..b79a963e5c 100644
--- a/third_party/nixpkgs/default.nix
+++ b/third_party/nixpkgs/default.nix
@@ -1,28 +1,41 @@
 # This file imports the pinned nixpkgs sets and applies relevant
 # modifications, such as our overlays.
 #
+# The actual source pinning happens via niv in //third_party/sources
+#
 # Note that the attribute exposed by this (third_party.nixpkgs) is
 # "special" in that the fixpoint used as readTree's config parameter
 # in //default.nix passes this attribute as the `pkgs` argument to all
 # readTree derivations.
 
-{ depot ? {}, externalArgs ? {}, depotOverlays ? true, ... }:
+{ depot ? { }
+, externalArgs ? { }
+, depotOverlays ? true
+, localSystem ? externalArgs.localSystem or builtins.currentSystem
+, crossSystem ? externalArgs.crossSystem or localSystem
+  # additional overlays to be applied.
+  # Useful when calling this file in a view exported from depot.
+, additionalOverlays ? [ ]
+, ...
+}:
 
 let
-  # This provides the sources of nixpkgs. We track both
-  # nixos-unstable, and the current stable channel of the latest NixOS
-  # release.
-
-  # Tracking nixos-unstable as of 2022-01-27.
-  unstableHashes = {
-    commit = "945ec499041db73043f745fad3b2a3a01e826081";
-    sha256 = "1ixv310sjw0r5vda4yfwp3snyha2i9h7aqygd43cyvdk2qsjk8pq";
-  };
+  # Arguments passed to both the stable nixpkgs and the main, unstable one.
+  # Includes everything but overlays which are only passed to unstable nixpkgs.
+  commonNixpkgsArgs = {
+    # allow users to inject their config into builds (e.g. to test CA derivations)
+    config =
+      (if externalArgs ? nixpkgsConfig then externalArgs.nixpkgsConfig else { })
+      // {
+        allowUnfree = true;
+        allowUnfreeRedistributable = true;
+        allowBroken = true;
+        # Forbids our meta.ci attribute
+        # https://github.com/NixOS/nixpkgs/pull/191171#issuecomment-1260650771
+        checkMeta = false;
+      };
 
-  # Tracking nixos-21.11 as of 2022-01-26.
-  stableHashes = {
-    commit = "b3d86c56c786ad9530f1400adbd4dfac3c42877b";
-    sha256 = "09nslcjdgwwb6j9alxrsnq1wvhifq1nmzl2w02l305j0wsmgdial";
+    inherit localSystem crossSystem;
   };
 
   # import the nixos-unstable package set, or optionally use the
@@ -30,50 +43,34 @@ let
   # argument. This is intended for use-cases where the depot is
   # bisected against nixpkgs to find the root cause of an issue in a
   # channel bump.
-  nixpkgsSrc = externalArgs.nixpkgsBisectPath or (fetchTarball {
-    url = "https://github.com/NixOS/nixpkgs/archive/${unstableHashes.commit}.tar.gz";
-    sha256 = unstableHashes.sha256;
-  });
-
-  stableNixpkgsSrc = fetchTarball {
-    url = "https://github.com/NixOS/nixpkgs/archive/${stableHashes.commit}.tar.gz";
-    sha256 = stableHashes.sha256;
-  };
+  nixpkgsSrc = externalArgs.nixpkgsBisectPath or depot.third_party.sources.nixpkgs;
 
   # Stable package set is imported, but not exposed, to overlay
   # required packages into the unstable set.
-  stableNixpkgs = import stableNixpkgsSrc {};
+  stableNixpkgs = import depot.third_party.sources.nixpkgs-stable commonNixpkgsArgs;
 
   # Overlay for packages that should come from the stable channel
   # instead (e.g. because something is broken in unstable).
-  stableOverlay = self: super: {
-    # Nothing picked from stable presently.
-  };
+  # Use `stableNixpkgs` from above.
+  stableOverlay = _unstableSelf: unstableSuper: { };
 
   # Overlay to expose the nixpkgs commits we are using to other Nix code.
   commitsOverlay = _: _: {
     nixpkgsCommits = {
-      unstable = unstableHashes.commit;
-      stable = stableHashes.commit;
+      unstable = depot.third_party.sources.nixpkgs.rev;
+      stable = depot.third_party.sources.nixpkgs-stable.rev;
     };
   };
-
-in import nixpkgsSrc {
-  # allow users to inject their config into builds (e.g. to test CA derivations)
-  config =
-    (if externalArgs ? nixpkgsConfig then externalArgs.nixpkgsConfig else { })
-    // {
-      allowUnfree = true;
-      allowBroken = true;
-    };
-
+in
+import nixpkgsSrc (commonNixpkgsArgs // {
   overlays = [
     commitsOverlay
     stableOverlay
   ] ++ (if depotOverlays then [
     depot.third_party.overlays.haskell
-    depot.third_party.overlays.emacs
     depot.third_party.overlays.tvl
     depot.third_party.overlays.ecl-static
-  ] else []);
-}
+    depot.third_party.overlays.dhall
+    (import depot.third_party.sources.rust-overlay)
+  ] else [ ] ++ additionalOverlays);
+})
diff --git a/third_party/overlays/dhall/OWNERS b/third_party/overlays/dhall/OWNERS
new file mode 100644
index 0000000000..a640227914
--- /dev/null
+++ b/third_party/overlays/dhall/OWNERS
@@ -0,0 +1 @@
+Profpatsch
diff --git a/third_party/overlays/dhall/default.nix b/third_party/overlays/dhall/default.nix
new file mode 100644
index 0000000000..4625035999
--- /dev/null
+++ b/third_party/overlays/dhall/default.nix
@@ -0,0 +1,30 @@
+{ ... }:
+
+self: super:
+
+let
+
+  # binary releases of dhall tools, since the build in nixpkgs is
+  # broken most of the time. The binaries are also fully static
+  # builds, instead of the half-static crap that nixpkgs produces.
+  easy-dhall-nix =
+    import
+      (builtins.fetchTarball {
+        url = "https://github.com/justinwoo/easy-dhall-nix/archive/dce9acbb99776a7f1344db4751d6080380f76f57.tar.gz";
+        sha256 = "0ckp6515gfvbxm08yyll87d9vg8sq2l21gwav2npzvwc3xz2lccf";
+      })
+      { pkgs = self; };
+in
+{
+  # ATTN: see the haskell overlay for some overrides we need.
+
+  # dhall = easy-dhall-nix.dhall-simple;
+  # dhall-nix = easy-dhall-nix.dhall-nix-simple;
+  dhall-bash = easy-dhall-nix.dhall-bash-simple;
+  dhall-docs = easy-dhall-nix.dhall-docs-simple;
+  dhall-json = easy-dhall-nix.dhall-json-simple;
+  dhall-lsp-server = easy-dhall-nix.dhall-lsp-simple;
+  # not yet in dhall-simple
+  # dhall-nixpkgs = easy-dhall-nix.dhall-nixpkgs-simple;
+  dhall-yaml = easy-dhall-nix.dhall-yaml-simple;
+}
diff --git a/third_party/overlays/ecl-static.nix b/third_party/overlays/ecl-static.nix
index 66579c33ab..d81075bdee 100644
--- a/third_party/overlays/ecl-static.nix
+++ b/third_party/overlays/ecl-static.nix
@@ -20,15 +20,6 @@ self: super:
   ecl-static = (super.pkgsMusl.ecl.override {
     inherit (self.pkgsStatic) gmp libffi boehmgc;
   }).overrideAttrs (drv: rec {
-    # version must not be changed as it indicates where to find the bundled libs,
-    # using ecl HEAD is necessary for us since it includes multiple fixes to do
-    # with bytecode compilation and allows to concatenate fasc files again.
-    src = self.fetchFromGitLab {
-      owner = "embeddable-common-lisp";
-      repo = "ecl";
-      rev = "1c989247c1b0bf1d38a76aec30b9ca5e41afe1e3";
-      sha256 = "0bzjqw6m1kk5z5b81yizic347k931msp5lf78x65dcw3fqfwv3xn";
-    };
     configureFlags = drv.configureFlags ++ [
       "--disable-shared"
       "--with-dffi=no" # will fail at runtime anyways if statically linked
diff --git a/third_party/overlays/emacs.nix b/third_party/overlays/emacs.nix
deleted file mode 100644
index 895c45a9bc..0000000000
--- a/third_party/overlays/emacs.nix
+++ /dev/null
@@ -1,11 +0,0 @@
-# Emacs overlay from https://github.com/nix-community/emacs-overlay
-{ ... }:
-
-let
-  # from 2022-01-04
-  commit = "a463c3bcbb04b4b744a259587081786ded8fd5b5";
-  src = builtins.fetchTarball {
-    url = "https://github.com/nix-community/emacs-overlay/archive/${commit}.tar.gz";
-    sha256 = "1b7rmshf1wc9wcml7jlzggdzilj644brk5m49fry6lv53vqmykjq";
-  };
-in import src
diff --git a/third_party/overlays/haskell/.skip-subtree b/third_party/overlays/haskell/.skip-subtree
new file mode 100644
index 0000000000..2a528eaa8a
--- /dev/null
+++ b/third_party/overlays/haskell/.skip-subtree
@@ -0,0 +1 @@
+extra-pkgs need to be callPackage-ed
diff --git a/third_party/overlays/haskell/OWNERS b/third_party/overlays/haskell/OWNERS
new file mode 100644
index 0000000000..5f87d2f271
--- /dev/null
+++ b/third_party/overlays/haskell/OWNERS
@@ -0,0 +1,2 @@
+Profpatsch
+sterni
diff --git a/third_party/overlays/haskell/default.nix b/third_party/overlays/haskell/default.nix
index 6e1ec2d0d1..dc1201ec43 100644
--- a/third_party/overlays/haskell/default.nix
+++ b/third_party/overlays/haskell/default.nix
@@ -7,12 +7,69 @@
 self: super: # overlay parameters for the nixpkgs overlay
 
 let
-  overrides = hsSelf: hsSuper: with super.haskell.lib; {
-    generic-arbitrary = appendPatch hsSuper.generic-arbitrary
-      [ ./patches/generic-arbitrary-export-garbitrary.patch ];
-  };
-in {
+  haskellLib = self.haskell.lib.compose;
+in
+{
   haskellPackages = super.haskellPackages.override {
-    inherit overrides;
+    overrides = hsSelf: hsSuper: {
+      punycode = haskellLib.appendPatch
+        (self.fetchpatch {
+          name = "punycode-mtl-2.3.patch";
+          url = "https://github.com/litherum/punycode/pull/5/commits/41e55c8b7cef14563e6d04a7190dbabff5a77886.patch";
+          sha256 = "03kgmy4z36jv16ffp5jrig2gr8ydc8cl1iscc7difisaq88mxvqc";
+        })
+        hsSuper.punycode;
+
+      # Build with deprecated ansi-wl-pprint is broken now, use HEAD which switched to
+      # prettyprinter
+      tmp-postgres = haskellLib.overrideSrc
+        {
+          version = "unstable-2023-08-08";
+          src = self.fetchFromGitHub {
+            owner = "jfischoff";
+            repo = "tmp-postgres";
+            rev = "7f2467a6d6d5f6db7eed59919a6773fe006cf22b";
+            sha256 = "0l1gdx5s8ximgawd3yzfy47pv5pgwqmjqp8hx5rbrq68vr04wkbl";
+          };
+        }
+        (hsSuper.tmp-postgres.override {
+          ansi-wl-pprint = hsSelf.prettyprinter;
+        });
+
+      ihp-hsx = lib.pipe hsSuper.ihp-hsx [
+        (haskellLib.overrideSrc {
+          version = "unstable-2023-03-28";
+          src = "${self.fetchFromGitHub {
+            owner = "digitallyinduced";
+            repo = "ihp";
+            rev = "ab4ecd05f4e7b6b3c4b74b82d39fc6c5cc48766b";
+            sha256 = "1fj5q9lygnmvqqv2fwqdj12sv63gkdfv5ha6fi190sv07dp9n9an";
+          }}/ihp-hsx";
+        })
+        haskellLib.doJailbreak
+      ];
+
+      pa-prelude = hsSelf.callPackage ./extra-pkgs/pa-prelude.nix { };
+      pa-error-tree = hsSelf.callPackage ./extra-pkgs/pa-error-tree-0.1.0.0.nix { };
+      pa-field-parser = hsSelf.callPackage ./extra-pkgs/pa-field-parser.nix { };
+      pa-label = hsSelf.callPackage ./extra-pkgs/pa-label.nix { };
+      pa-pretty = hsSelf.callPackage ./extra-pkgs/pa-pretty-0.1.1.0.nix { };
+      pa-json = hsSelf.callPackage ./extra-pkgs/pa-json.nix { };
+      pa-run-command = hsSelf.callPackage ./extra-pkgs/pa-run-command-0.1.0.0.nix { };
+    };
+  };
+
+  haskell = lib.recursiveUpdate super.haskell {
+    packages.ghc8107 = super.haskell.packages.ghc8107.override {
+      overrides = hsSelf: hsSuper: {
+        # TODO(sterni): TODO(grfn): patch xanthous to work with random-fu 0.3.*,
+        # so we can use GHC 9.0.2 and benefit from upstream binary cache.
+        random-fu = hsSelf.callPackage ./extra-pkgs/random-fu-0.2.nix { };
+        rvar = hsSelf.callPackage ./extra-pkgs/rvar-0.2.nix { };
+
+        # TODO(grfn): port to brick 1.4 (EventM gains an additional type argument in 1.0)
+        brick = hsSelf.callPackage ./extra-pkgs/brick-0.73.nix { };
+      };
+    };
   };
 }
diff --git a/third_party/overlays/haskell/extra-pkgs/brick-0.73.nix b/third_party/overlays/haskell/extra-pkgs/brick-0.73.nix
new file mode 100644
index 0000000000..c5e2883c75
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/brick-0.73.nix
@@ -0,0 +1,70 @@
+{ mkDerivation
+, base
+, bytestring
+, config-ini
+, containers
+, contravariant
+, data-clist
+, deepseq
+, directory
+, dlist
+, exceptions
+, filepath
+, lib
+, microlens
+, microlens-mtl
+, microlens-th
+, QuickCheck
+, stm
+, template-haskell
+, text
+, text-zipper
+, transformers
+, unix
+, vector
+, vty
+, word-wrap
+}:
+mkDerivation {
+  pname = "brick";
+  version = "0.73";
+  sha256 = "741c8d0717f0ab5addd5d3acc88cb36d645a0c73907bde509b2fd9d9bc02039c";
+  isLibrary = true;
+  isExecutable = true;
+  libraryHaskellDepends = [
+    base
+    bytestring
+    config-ini
+    containers
+    contravariant
+    data-clist
+    deepseq
+    directory
+    dlist
+    exceptions
+    filepath
+    microlens
+    microlens-mtl
+    microlens-th
+    stm
+    template-haskell
+    text
+    text-zipper
+    transformers
+    unix
+    vector
+    vty
+    word-wrap
+  ];
+  testHaskellDepends = [
+    base
+    containers
+    microlens
+    QuickCheck
+    vector
+    vty
+  ];
+  homepage = "https://github.com/jtdaugherty/brick/";
+  description = "A declarative terminal user interface library";
+  license = lib.licenses.bsd3;
+}
diff --git a/third_party/overlays/haskell/extra-pkgs/pa-error-tree-0.1.0.0.nix b/third_party/overlays/haskell/extra-pkgs/pa-error-tree-0.1.0.0.nix
new file mode 100644
index 0000000000..a38cd4efaa
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/pa-error-tree-0.1.0.0.nix
@@ -0,0 +1,10 @@
+{ mkDerivation, base, containers, lib, pa-prelude }:
+mkDerivation {
+  pname = "pa-error-tree";
+  version = "0.1.0.0";
+  sha256 = "f82d3d905e8d9f0d31c81f31c424b9a95c65a8925517ccac92134f410cf8d639";
+  libraryHaskellDepends = [ base containers pa-prelude ];
+  homepage = "https://github.com/possehl-analytics/pa-hackage";
+  description = "Collect a tree of errors and pretty-print";
+  license = lib.licenses.bsd3;
+}
diff --git a/third_party/overlays/haskell/extra-pkgs/pa-field-parser.nix b/third_party/overlays/haskell/extra-pkgs/pa-field-parser.nix
new file mode 100644
index 0000000000..a3c146ee09
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/pa-field-parser.nix
@@ -0,0 +1,39 @@
+{ mkDerivation
+, aeson
+, aeson-better-errors
+, attoparsec
+, base
+, case-insensitive
+, containers
+, lib
+, pa-error-tree
+, pa-prelude
+, scientific
+, semigroupoids
+, template-haskell
+, text
+, time
+}:
+mkDerivation {
+  pname = "pa-field-parser";
+  version = "0.3.0.0";
+  sha256 = "528c2b6bf5ad6454861b059c7eb6924f4c32bcb5b8faa4c2389d9ddfd92fcd57";
+  libraryHaskellDepends = [
+    aeson
+    aeson-better-errors
+    attoparsec
+    base
+    case-insensitive
+    containers
+    pa-error-tree
+    pa-prelude
+    scientific
+    semigroupoids
+    template-haskell
+    text
+    time
+  ];
+  homepage = "https://github.com/possehl-analytics/pa-hackage";
+  description = "β€œVertical” parsing of values";
+  license = lib.licenses.bsd3;
+}
diff --git a/third_party/overlays/haskell/extra-pkgs/pa-json.nix b/third_party/overlays/haskell/extra-pkgs/pa-json.nix
new file mode 100644
index 0000000000..8ce838b22c
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/pa-json.nix
@@ -0,0 +1,43 @@
+{ mkDerivation
+, aeson
+, aeson-better-errors
+, aeson-pretty
+, base
+, base64-bytestring
+, bytestring
+, containers
+, lib
+, pa-error-tree
+, pa-field-parser
+, pa-label
+, pa-prelude
+, scientific
+, text
+, time
+, vector
+}:
+mkDerivation {
+  pname = "pa-json";
+  version = "0.3.0.0";
+  sha256 = "45e79765e57e21400f3f3b1e86094473fac61d298618d7e34f6cad4988d8923b";
+  libraryHaskellDepends = [
+    aeson
+    aeson-better-errors
+    aeson-pretty
+    base
+    base64-bytestring
+    bytestring
+    containers
+    pa-error-tree
+    pa-field-parser
+    pa-label
+    pa-prelude
+    scientific
+    text
+    time
+    vector
+  ];
+  homepage = "https://github.com/possehl-analytics/pa-hackage";
+  description = "Our JSON parsers/encoders";
+  license = lib.licenses.bsd3;
+}
diff --git a/third_party/overlays/haskell/extra-pkgs/pa-label.nix b/third_party/overlays/haskell/extra-pkgs/pa-label.nix
new file mode 100644
index 0000000000..7cfa257c81
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/pa-label.nix
@@ -0,0 +1,10 @@
+{ mkDerivation, base, lib }:
+mkDerivation {
+  pname = "pa-label";
+  version = "0.1.1.0";
+  sha256 = "b40183900c045641c0632ed8e53a326c0c0e9c2806568613c03b3131d9016183";
+  libraryHaskellDepends = [ base ];
+  homepage = "https://github.com/possehl-analytics/pa-hackage";
+  description = "Labels, and labelled tuples and enums (GHC >9.2)";
+  license = lib.licenses.bsd3;
+}
diff --git a/third_party/overlays/haskell/extra-pkgs/pa-prelude.nix b/third_party/overlays/haskell/extra-pkgs/pa-prelude.nix
new file mode 100644
index 0000000000..17e1996ab6
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/pa-prelude.nix
@@ -0,0 +1,43 @@
+{ mkDerivation
+, base
+, bytestring
+, containers
+, error
+, exceptions
+, lib
+, mtl
+, profunctors
+, PyF
+, scientific
+, semigroupoids
+, template-haskell
+, text
+, these
+, validation-selective
+, vector
+}:
+mkDerivation {
+  pname = "pa-prelude";
+  version = "0.2.0.0";
+  sha256 = "68015f7c19e9c618fc04e2516baccfce52af24efb9ca1480162c9ea0aef7f301";
+  libraryHaskellDepends = [
+    base
+    bytestring
+    containers
+    error
+    exceptions
+    mtl
+    profunctors
+    PyF
+    scientific
+    semigroupoids
+    template-haskell
+    text
+    these
+    validation-selective
+    vector
+  ];
+  homepage = "https://github.com/possehl-analytics/pa-hackage";
+  description = "The Possehl Analytics Prelude";
+  license = lib.licenses.bsd3;
+}
diff --git a/third_party/overlays/haskell/extra-pkgs/pa-pretty-0.1.1.0.nix b/third_party/overlays/haskell/extra-pkgs/pa-pretty-0.1.1.0.nix
new file mode 100644
index 0000000000..d6dadef849
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/pa-pretty-0.1.1.0.nix
@@ -0,0 +1,29 @@
+{ mkDerivation
+, aeson
+, aeson-pretty
+, ansi-terminal
+, base
+, hscolour
+, lib
+, nicify-lib
+, pa-prelude
+, text
+}:
+mkDerivation {
+  pname = "pa-pretty";
+  version = "0.1.1.0";
+  sha256 = "da925a7cf2ac49c5769d7ebd08c2599b537efe45b3d506bf4d7c8673633ef6c9";
+  libraryHaskellDepends = [
+    aeson
+    aeson-pretty
+    ansi-terminal
+    base
+    hscolour
+    nicify-lib
+    pa-prelude
+    text
+  ];
+  homepage = "https://github.com/possehl-analytics/pa-hackage";
+  description = "Some pretty-printing helpers";
+  license = lib.licenses.bsd3;
+}
diff --git a/third_party/overlays/haskell/extra-pkgs/pa-run-command-0.1.0.0.nix b/third_party/overlays/haskell/extra-pkgs/pa-run-command-0.1.0.0.nix
new file mode 100644
index 0000000000..b12eb5efbf
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/pa-run-command-0.1.0.0.nix
@@ -0,0 +1,25 @@
+{ mkDerivation
+, base
+, bytestring
+, lib
+, monad-logger
+, pa-prelude
+, text
+, typed-process
+}:
+mkDerivation {
+  pname = "pa-run-command";
+  version = "0.1.0.0";
+  sha256 = "37837e0cddedc9b615063f0357115739c53b5dcb8af82ce86a95a3a5c88c29a3";
+  libraryHaskellDepends = [
+    base
+    bytestring
+    monad-logger
+    pa-prelude
+    text
+    typed-process
+  ];
+  homepage = "https://github.com/possehl-analytics/pa-hackage";
+  description = "Helper functions for spawning subprocesses";
+  license = lib.licenses.bsd3;
+}
diff --git a/third_party/overlays/haskell/extra-pkgs/random-fu-0.2.nix b/third_party/overlays/haskell/extra-pkgs/random-fu-0.2.nix
new file mode 100644
index 0000000000..1626eca7be
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/random-fu-0.2.nix
@@ -0,0 +1,41 @@
+{ mkDerivation
+, base
+, erf
+, lib
+, math-functions
+, monad-loops
+, mtl
+, random
+, random-shuffle
+, random-source
+, rvar
+, syb
+, template-haskell
+, transformers
+, vector
+}:
+mkDerivation {
+  pname = "random-fu";
+  version = "0.2.7.7";
+  sha256 = "8466bcfb5290bdc30a571c91e1eb526c419ea9773bc118996778b516cfc665ca";
+  revision = "1";
+  editedCabalFile = "16nhymfriygqr2by9v72vdzv93v6vhd9z07pgaji4zvv66jikv82";
+  libraryHaskellDepends = [
+    base
+    erf
+    math-functions
+    monad-loops
+    mtl
+    random
+    random-shuffle
+    random-source
+    rvar
+    syb
+    template-haskell
+    transformers
+    vector
+  ];
+  homepage = "https://github.com/mokus0/random-fu";
+  description = "Random number generation";
+  license = lib.licenses.publicDomain;
+}
diff --git a/third_party/overlays/haskell/extra-pkgs/rvar-0.2.nix b/third_party/overlays/haskell/extra-pkgs/rvar-0.2.nix
new file mode 100644
index 0000000000..c00f5a1a8d
--- /dev/null
+++ b/third_party/overlays/haskell/extra-pkgs/rvar-0.2.nix
@@ -0,0 +1,25 @@
+{ mkDerivation
+, base
+, lib
+, MonadPrompt
+, mtl
+, random-source
+, transformers
+}:
+mkDerivation {
+  pname = "rvar";
+  version = "0.2.0.6";
+  sha256 = "01e18875ffde43f9591a8acd9f60c9c51704a026e51c1a6797faecd1c7ae8cd3";
+  revision = "1";
+  editedCabalFile = "1jn9ivlj3k65n8d9sfsp882m5lvni1ah79mk0cvkz91pgywvkiyq";
+  libraryHaskellDepends = [
+    base
+    MonadPrompt
+    mtl
+    random-source
+    transformers
+  ];
+  homepage = "https://github.com/mokus0/random-fu";
+  description = "Random Variables";
+  license = lib.licenses.publicDomain;
+}
diff --git a/third_party/overlays/patches/.skip-tree b/third_party/overlays/patches/.skip-tree
new file mode 100644
index 0000000000..86eae51a6d
--- /dev/null
+++ b/third_party/overlays/patches/.skip-tree
@@ -0,0 +1 @@
+No readTree-compatible files.
diff --git a/third_party/overlays/patches/0001-configure-ac-version.patch b/third_party/overlays/patches/0001-configure-ac-version.patch
new file mode 100644
index 0000000000..fa2575cb93
--- /dev/null
+++ b/third_party/overlays/patches/0001-configure-ac-version.patch
@@ -0,0 +1,13 @@
+diff --git a/configure.ac b/configure.ac
+index e861e42..018c19c 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -26,7 +26,7 @@
+ #;**********************************************************************;
+ 
+ AC_INIT([tpm2-pkcs11],
+-  [m4_esyscmd_s([git describe --tags --always --dirty])],
++  [git-@VERSION@],
+   [https://github.com/tpm2-software/tpm2-pkcs11/issues],
+   [],
+   [https://github.com/tpm2-software/tpm2-pkcs11])
diff --git a/third_party/overlays/patches/buf-tests-dont-use-file-transport.patch b/third_party/overlays/patches/buf-tests-dont-use-file-transport.patch
new file mode 100644
index 0000000000..34be80eb36
--- /dev/null
+++ b/third_party/overlays/patches/buf-tests-dont-use-file-transport.patch
@@ -0,0 +1,64 @@
+commit e9219b88de5ed37af337ee2d2e71e7ec7c0aad1b
+Author: Robbert van Ginkel <rvanginkel@buf.build>
+Date:   Thu Oct 20 16:43:28 2022 -0400
+
+    Fix git unit test by using fake git server rather than file:// (#1518)
+    
+    More recent versions of git fix a CVE by disabling some usage of the
+    `file://` transport, see
+    https://github.blog/2022-10-18-git-security-vulnerabilities-announced/#cve-2022-39253.
+    We were using this transport in tests.
+    
+    Instead, use https://git-scm.com/docs/git-http-backend to serve up this
+    repository locally so we don't have to use the file protocol. This
+    should be a more accurate tests, since we mostly expect submodules to
+    come from servers.
+
+diff --git a/.golangci.yml b/.golangci.yml
+index 318d1171..865e03e7 100644
+--- a/.golangci.yml
++++ b/.golangci.yml
+@@ -136,3 +136,8 @@ issues:
+     - linters:
+         - containedctx
+       path: private/bufpkg/bufmodule/bufmoduleprotocompile
++      # We should be able to use net/http/cgi in a unit test, in addition the CVE mentions only versions of go < 1.6.3 are affected.
++    - linters:
++        - gosec
++      path: private/pkg/git/git_test.go
++      text: "G504:"
+diff --git a/private/pkg/git/git_test.go b/private/pkg/git/git_test.go
+index 7b77b6cd..7132054e 100644
+--- a/private/pkg/git/git_test.go
++++ b/private/pkg/git/git_test.go
+@@ -17,6 +17,8 @@ package git
+ import (
+ 	"context"
+ 	"errors"
++	"net/http/cgi"
++	"net/http/httptest"
+ 	"os"
+ 	"os/exec"
+ 	"path/filepath"
+@@ -213,6 +215,21 @@ func createGitDirs(
+ 	runCommand(ctx, t, container, runner, "git", "-C", submodulePath, "add", "test.proto")
+ 	runCommand(ctx, t, container, runner, "git", "-C", submodulePath, "commit", "-m", "commit 0")
+ 
++	gitExecPath, err := command.RunStdout(ctx, container, runner, "git", "--exec-path")
++	require.NoError(t, err)
++	t.Log(filepath.Join(string(gitExecPath), "git-http-backend"))
++	// https://git-scm.com/docs/git-http-backend#_description
++	f, err := os.Create(filepath.Join(submodulePath, ".git", "git-daemon-export-ok"))
++	require.NoError(t, err)
++	require.NoError(t, f.Close())
++	server := httptest.NewServer(&cgi.Handler{
++		Path: filepath.Join(strings.TrimSpace(string(gitExecPath)), "git-http-backend"),
++		Dir:  submodulePath,
++		Env:  []string{"GIT_PROJECT_ROOT=" + submodulePath},
++	})
++	t.Cleanup(server.Close)
++	submodulePath = server.URL
++
+ 	originPath := filepath.Join(tmpDir, "origin")
+ 	require.NoError(t, os.MkdirAll(originPath, 0777))
+ 	runCommand(ctx, t, container, runner, "git", "-C", originPath, "init")
diff --git a/third_party/overlays/patches/cbtemulator-uds.patch b/third_party/overlays/patches/cbtemulator-uds.patch
new file mode 100644
index 0000000000..a19255306f
--- /dev/null
+++ b/third_party/overlays/patches/cbtemulator-uds.patch
@@ -0,0 +1,140 @@
+commit 1397e10225d8c6fd079a86fccd58fb5d0f4200bc
+Author: Florian Klink <flokli@flokli.de>
+Date:   Fri Mar 29 10:06:34 2024 +0100
+
+    feat(bigtable/emulator): allow listening on Unix Domain Sockets
+    
+    cbtemulator listening on unix domain sockets is much easier than trying
+    to allocate free TCP ports, especially if many cbtemulators are run at
+    the same time in integration tests.
+    
+    This adds an additional flag, address, which has priority if it's set,
+    rather than host:port.
+    
+    `NewServer` already takes a `laddr string`, so we simply check for it to
+    contain slashes, and if so, listen on unix, rather than TCP.
+
+diff --git a/bigtable/bttest/inmem.go b/bigtable/bttest/inmem.go
+index 556abc2a85..33e4bf2667 100644
+--- a/bttest/inmem.go
++++ b/bttest/inmem.go
+@@ -40,6 +40,7 @@ import (
+ 	"math"
+ 	"math/rand"
+ 	"net"
++	"os"
+ 	"regexp"
+ 	"sort"
+ 	"strings"
+@@ -106,7 +107,15 @@ type server struct {
+ // The Server will be listening for gRPC connections, without TLS,
+ // on the provided address. The resolved address is named by the Addr field.
+ func NewServer(laddr string, opt ...grpc.ServerOption) (*Server, error) {
+-	l, err := net.Listen("tcp", laddr)
++	var l net.Listener
++	var err error
++
++	// If the address contains slashes, listen on a unix domain socket instead.
++	if strings.Contains(laddr, "/") {
++		l, err = net.Listen("unix", laddr)
++	} else {
++		l, err = net.Listen("tcp", laddr)
++	}
+ 	if err != nil {
+ 		return nil, err
+ 	}
+diff --git a/bigtable/cmd/emulator/cbtemulator.go b/bigtable/cmd/emulator/cbtemulator.go
+index 144c09ffb1..deaf69b717 100644
+--- a/cmd/emulator/cbtemulator.go
++++ b/cmd/emulator/cbtemulator.go
+@@ -27,8 +27,9 @@ import (
+ )
+ 
+ var (
+-	host = flag.String("host", "localhost", "the address to bind to on the local machine")
+-	port = flag.Int("port", 9000, "the port number to bind to on the local machine")
++	host    = flag.String("host", "localhost", "the address to bind to on the local machine")
++	port    = flag.Int("port", 9000, "the port number to bind to on the local machine")
++	address = flag.String("address", "", "address:port number or unix socket path to listen on. Has priority over host/port")
+ )
+ 
+ const (
+@@ -42,7 +43,15 @@ func main() {
+ 		grpc.MaxRecvMsgSize(maxMsgSize),
+ 		grpc.MaxSendMsgSize(maxMsgSize),
+ 	}
+-	srv, err := bttest.NewServer(fmt.Sprintf("%s:%d", *host, *port), opts...)
++
++	var laddr string
++	if *address != "" {
++		laddr = *address
++	} else {
++		laddr = fmt.Sprintf("%s:%d", *host, *port)
++	}
++
++	srv, err := bttest.NewServer(laddr, opts...)
+ 	if err != nil {
+ 		log.Fatalf("failed to start emulator: %v", err)
+ 	}
+commit ce16f843d6c93159d86b3807c6d9ff66e43aac67
+Author: Florian Klink <flokli@flokli.de>
+Date:   Fri Mar 29 11:53:15 2024 +0100
+
+    feat(bigtable): clean up unix socket on close
+    
+    Call srv.Close when receiving an interrupt, and delete the unix domain
+    socket in that function.
+
+diff --git a/bigtable/bttest/inmem.go b/bigtable/bttest/inmem.go
+index 33e4bf2667..0dc96024b1 100644
+--- a/bttest/inmem.go
++++ b/bttest/inmem.go
+@@ -148,6 +148,11 @@ func (s *Server) Close() {
+ 
+ 	s.srv.Stop()
+ 	s.l.Close()
++
++	// clean up unix socket
++	if strings.Contains(s.Addr, "/") {
++		_ = os.Remove(s.Addr)
++	}
+ }
+ 
+ func (s *server) CreateTable(ctx context.Context, req *btapb.CreateTableRequest) (*btapb.Table, error) {
+diff --git a/bigtable/cmd/emulator/cbtemulator.go b/bigtable/cmd/emulator/cbtemulator.go
+index deaf69b717..5a9e8f7a8c 100644
+--- a/cmd/emulator/cbtemulator.go
++++ b/cmd/emulator/cbtemulator.go
+@@ -18,9 +18,12 @@ cbtemulator launches the in-memory Cloud Bigtable server on the given address.
+ package main
+ 
+ import (
++	"context"
+ 	"flag"
+ 	"fmt"
+ 	"log"
++	"os"
++	"os/signal"
+ 
+ 	"cloud.google.com/go/bigtable/bttest"
+ 	"google.golang.org/grpc"
+@@ -51,11 +54,18 @@ func main() {
+ 		laddr = fmt.Sprintf("%s:%d", *host, *port)
+ 	}
+ 
++	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
++	defer stop()
++
+ 	srv, err := bttest.NewServer(laddr, opts...)
+ 	if err != nil {
+ 		log.Fatalf("failed to start emulator: %v", err)
+ 	}
+ 
+ 	fmt.Printf("Cloud Bigtable emulator running on %s\n", srv.Addr)
+-	select {}
++	select {
++	case <-ctx.Done():
++		srv.Close()
++		stop()
++	}
+ }
diff --git a/third_party/overlays/patches/clickhouse-support-reading-arrow-LargeListArray.patch b/third_party/overlays/patches/clickhouse-support-reading-arrow-LargeListArray.patch
new file mode 100644
index 0000000000..9e79aa7267
--- /dev/null
+++ b/third_party/overlays/patches/clickhouse-support-reading-arrow-LargeListArray.patch
@@ -0,0 +1,106 @@
+From cdea2e8ad98995202ce81c9c030f2ae64d73b05a Mon Sep 17 00:00:00 2001
+From: edef <edef@edef.eu>
+Date: Mon, 30 Oct 2023 08:08:10 +0000
+Subject: [PATCH] Support reading arrow::LargeListArray
+
+---
+ .../Formats/Impl/ArrowColumnToCHColumn.cpp    | 33 +++++++++++++++----
+ 1 file changed, 26 insertions(+), 7 deletions(-)
+
+diff --git a/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp b/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp
+index 6f9d49498f2..b93846cd4eb 100644
+--- a/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp
++++ b/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp
+@@ -436,6 +436,22 @@ static ColumnPtr readByteMapFromArrowColumn(std::shared_ptr<arrow::ChunkedArray>
+     return nullmap_column;
+ }
+ 
++template <typename T>
++struct ArrowOffsetArray;
++
++template <>
++struct ArrowOffsetArray<arrow::ListArray>
++{
++    using type = arrow::Int32Array;
++};
++
++template <>
++struct ArrowOffsetArray<arrow::LargeListArray>
++{
++    using type = arrow::Int64Array;
++};
++
++template <typename ArrowListArray>
+ static ColumnPtr readOffsetsFromArrowListColumn(std::shared_ptr<arrow::ChunkedArray> & arrow_column)
+ {
+     auto offsets_column = ColumnUInt64::create();
+@@ -444,9 +460,9 @@ static ColumnPtr readOffsetsFromArrowListColumn(std::shared_ptr<arrow::ChunkedAr
+ 
+     for (int chunk_i = 0, num_chunks = arrow_column->num_chunks(); chunk_i < num_chunks; ++chunk_i)
+     {
+-        arrow::ListArray & list_chunk = dynamic_cast<arrow::ListArray &>(*(arrow_column->chunk(chunk_i)));
++        ArrowListArray & list_chunk = dynamic_cast<ArrowListArray &>(*(arrow_column->chunk(chunk_i)));
+         auto arrow_offsets_array = list_chunk.offsets();
+-        auto & arrow_offsets = dynamic_cast<arrow::Int32Array &>(*arrow_offsets_array);
++        auto & arrow_offsets = dynamic_cast<ArrowOffsetArray<ArrowListArray>::type &>(*arrow_offsets_array);
+ 
+         /*
+          * CH uses element size as "offsets", while arrow uses actual offsets as offsets.
+@@ -602,13 +618,14 @@ static ColumnPtr readColumnWithIndexesData(std::shared_ptr<arrow::ChunkedArray>
+     }
+ }
+ 
++template <typename ArrowListArray>
+ static std::shared_ptr<arrow::ChunkedArray> getNestedArrowColumn(std::shared_ptr<arrow::ChunkedArray> & arrow_column)
+ {
+     arrow::ArrayVector array_vector;
+     array_vector.reserve(arrow_column->num_chunks());
+     for (int chunk_i = 0, num_chunks = arrow_column->num_chunks(); chunk_i < num_chunks; ++chunk_i)
+     {
+-        arrow::ListArray & list_chunk = dynamic_cast<arrow::ListArray &>(*(arrow_column->chunk(chunk_i)));
++        ArrowListArray & list_chunk = dynamic_cast<ArrowListArray &>(*(arrow_column->chunk(chunk_i)));
+ 
+         /*
+          * It seems like arrow::ListArray::values() (nested column data) might or might not be shared across chunks.
+@@ -819,12 +836,12 @@ static ColumnWithTypeAndName readColumnFromArrowColumn(
+                     key_type_hint = map_type_hint->getKeyType();
+                 }
+             }
+-            auto arrow_nested_column = getNestedArrowColumn(arrow_column);
++            auto arrow_nested_column = getNestedArrowColumn<arrow::ListArray>(arrow_column);
+             auto nested_column = readColumnFromArrowColumn(arrow_nested_column, column_name, format_name, false, dictionary_infos, allow_null_type, skip_columns_with_unsupported_types, skipped, date_time_overflow_behavior, nested_type_hint, true);
+             if (skipped)
+                 return {};
+ 
+-            auto offsets_column = readOffsetsFromArrowListColumn(arrow_column);
++            auto offsets_column = readOffsetsFromArrowListColumn<arrow::ListArray>(arrow_column);
+ 
+             const auto * tuple_column = assert_cast<const ColumnTuple *>(nested_column.column.get());
+             const auto * tuple_type = assert_cast<const DataTypeTuple *>(nested_column.type.get());
+@@ -846,7 +863,9 @@ static ColumnWithTypeAndName readColumnFromArrowColumn(
+             return {std::move(map_column), std::move(map_type), column_name};
+         }
+         case arrow::Type::LIST:
++        case arrow::Type::LARGE_LIST:
+         {
++            bool is_large = arrow_column->type()->id() == arrow::Type::LARGE_LIST;
+             DataTypePtr nested_type_hint;
+             if (type_hint)
+             {
+@@ -854,11 +873,11 @@ static ColumnWithTypeAndName readColumnFromArrowColumn(
+                 if (array_type_hint)
+                     nested_type_hint = array_type_hint->getNestedType();
+             }
+-            auto arrow_nested_column = getNestedArrowColumn(arrow_column);
++            auto arrow_nested_column = is_large ? getNestedArrowColumn<arrow::LargeListArray>(arrow_column) : getNestedArrowColumn<arrow::ListArray>(arrow_column);
+             auto nested_column = readColumnFromArrowColumn(arrow_nested_column, column_name, format_name, false, dictionary_infos, allow_null_type, skip_columns_with_unsupported_types, skipped, date_time_overflow_behavior, nested_type_hint);
+             if (skipped)
+                 return {};
+-            auto offsets_column = readOffsetsFromArrowListColumn(arrow_column);
++            auto offsets_column = is_large ? readOffsetsFromArrowListColumn<arrow::LargeListArray>(arrow_column) : readOffsetsFromArrowListColumn<arrow::ListArray>(arrow_column);
+             auto array_column = ColumnArray::create(nested_column.column, offsets_column);
+             auto array_type = std::make_shared<DataTypeArray>(nested_column.type);
+             return {std::move(array_column), std::move(array_type), column_name};
+-- 
+2.42.0
+
diff --git a/third_party/overlays/patches/crate2nix-run-tests-in-build-source.patch b/third_party/overlays/patches/crate2nix-run-tests-in-build-source.patch
new file mode 100644
index 0000000000..52793270e6
--- /dev/null
+++ b/third_party/overlays/patches/crate2nix-run-tests-in-build-source.patch
@@ -0,0 +1,69 @@
+From 7cf084f73f7d15fe0538a625182fa7179c083b3d Mon Sep 17 00:00:00 2001
+From: Raito Bezarius <masterancpp@gmail.com>
+Date: Tue, 16 Jan 2024 02:10:48 +0100
+Subject: [PATCH] fix(template): run tests in `/build/source` instead `/build`
+
+Previously, the source tree was located inline in `/build` during tests, this was a mistake
+because the crates more than often are built in `/build/source` as per the `sourceRoot` system.
+
+This can cause issues with test binaries hardcoding `/build/source/...` as their choice for doing things,
+causing them to be confused in the test phase which is relocated without rewriting the paths inside test binaries.
+
+We fix that by relocating ourselves in the right hierarchy.
+
+This is a "simple" fix in the sense that more edge cases could exist but they are hard to reason about
+because they would be crates using custom `sourceRoot`, i.e. having `crate.sourceRoot` set and then it becomes
+a bit hard to reproduce the hierarchy, you need to analyze whether the path is absolute or relative,
+
+If it's relative, you can just reuse it and reproduce that specific hierarchy.
+If it's absolute, you need to cut the "absolute" meaningless part, e.g. `$NIX_BUILD_TOP/` and proceed like
+it's a relative path IMHO.
+---
+ crate2nix/Cargo.nix                                  | 10 ++++++++++
+ crate2nix/templates/nix/crate2nix/default.nix        | 10 ++++++++++
+
+diff --git a/Cargo.nix b/Cargo.nix
+index 6ef7a49..172ff34 100644
+--- a/Cargo.nix
++++ b/Cargo.nix
+@@ -2889,6 +2889,16 @@ rec {
+           # recreate a file hierarchy as when running tests with cargo
+ 
+           # the source for test data
++          # It's necessary to locate the source in $NIX_BUILD_TOP/source/
++          # instead of $NIX_BUILD_TOP/
++          # because we compiled those test binaries in the former and not the latter.
++          # So all paths will expect source tree to be there and not in the build top directly.
++          # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
++          # TODO(raitobezarius): I believe there could be more edge cases if `crate.sourceRoot`
++          # do exist but it's very hard to reason about them, so let's wait until the first bug report.
++          mkdir -p source/
++          cd source/
++
+           ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+ 
+           # build outputs
+diff --git a/crate2nix/templates/nix/crate2nix/default.nix b/crate2nix/templates/nix/crate2nix/default.nix
+index e4fc2e9..dfb14c4 100644
+--- a/templates/nix/crate2nix/default.nix
++++ b/templates/nix/crate2nix/default.nix
+@@ -135,6 +135,16 @@ rec {
+           # recreate a file hierarchy as when running tests with cargo
+ 
+           # the source for test data
++          # It's necessary to locate the source in $NIX_BUILD_TOP/source/
++          # instead of $NIX_BUILD_TOP/
++          # because we compiled those test binaries in the former and not the latter.
++          # So all paths will expect source tree to be there and not in the build top directly.
++          # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
++          # TODO(raitobezarius): I believe there could be more edge cases if `crate.sourceRoot`
++          # do exist but it's very hard to reason about them, so let's wait until the first bug report.
++          mkdir -p source/
++          cd source/
++
+           ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+ 
+           # build outputs
+-- 
+2.43.0
+
diff --git a/third_party/overlays/patches/crate2nix-tests-debug.patch b/third_party/overlays/patches/crate2nix-tests-debug.patch
new file mode 100644
index 0000000000..384178c805
--- /dev/null
+++ b/third_party/overlays/patches/crate2nix-tests-debug.patch
@@ -0,0 +1,12 @@
+diff --git a/templates/nix/crate2nix/default.nix b/templates/nix/crate2nix/default.nix
+index 4eefda8..d064118 100644
+--- a/templates/nix/crate2nix/default.nix
++++ b/templates/nix/crate2nix/default.nix
+@@ -111,6 +111,7 @@ rec {
+             (
+               _: {
+                 buildTests = true;
++                release = false;
+               }
+             );
+           # If the user hasn't set any pre/post commands, we don't want to
diff --git a/third_party/overlays/patches/evans-add-support-for-unix-domain-sockets.patch b/third_party/overlays/patches/evans-add-support-for-unix-domain-sockets.patch
new file mode 100644
index 0000000000..c66528f538
--- /dev/null
+++ b/third_party/overlays/patches/evans-add-support-for-unix-domain-sockets.patch
@@ -0,0 +1,39 @@
+From 55d7e7af7c56f678eb817059417241bb61ee5181 Mon Sep 17 00:00:00 2001
+From: Florian Klink <flokli@flokli.de>
+Date: Sun, 8 Oct 2023 11:00:27 +0200
+Subject: [PATCH] add support for unix domain sockets
+
+grpc.NewClient already supports connecting to unix domain sockets, and
+accepts a string anyways.
+
+As a quick fix, detect the `address` starting with `unix://` and don't
+add the port.
+
+In the long term, we might want to deprecate `host` and `port` cmdline
+args in favor of a single `address` arg.
+---
+ mode/common.go | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/mode/common.go b/mode/common.go
+index dfc7839..55f1e36 100644
+--- a/mode/common.go
++++ b/mode/common.go
+@@ -13,7 +13,13 @@ import (
+ )
+ 
+ func newGRPCClient(cfg *config.Config) (grpc.Client, error) {
+-	addr := fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port)
++	addr := cfg.Server.Host
++
++	// as long as the address doesn't start with unix, also add the port.
++	if !strings.HasPrefix(cfg.Server.Host, "unix://") {
++		addr = fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port)
++	}
++
+ 	if cfg.Request.Web {
+ 		//TODO: remove second arg
+ 		return grpc.NewWebClient(addr, cfg.Server.Reflection, false, "", "", "", grpc.Headers(cfg.Request.Header)), nil
+-- 
+2.42.0
+
diff --git a/third_party/overlays/patches/tpm2-pkcs11-190-dbupgrade.patch b/third_party/overlays/patches/tpm2-pkcs11-190-dbupgrade.patch
new file mode 100644
index 0000000000..f831c11a80
--- /dev/null
+++ b/third_party/overlays/patches/tpm2-pkcs11-190-dbupgrade.patch
@@ -0,0 +1,29 @@
+From 987323794148a6ff5ce3d02eef8cfeb46bee1761 Mon Sep 17 00:00:00 2001
+From: Anton <tracefinder@gmail.com>
+Date: Tue, 7 Nov 2023 12:02:15 +0300
+Subject: [PATCH] Skip null attribute during DB update
+
+Signed-off-by: Anton <tracefinder@gmail.com>
+---
+ src/lib/db.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/src/lib/db.c b/src/lib/db.c
+index b4bbd1bf..74c5a7b4 100644
+--- a/src/lib/db.c
++++ b/src/lib/db.c
+@@ -2169,9 +2169,11 @@ static CK_RV dbup_handler_from_7_to_8(sqlite3 *updb) {
+ 
+         /* for each tobject */
+         CK_ATTRIBUTE_PTR a = attr_get_attribute_by_type(tobj->attrs, CKA_ALLOWED_MECHANISMS);
+-        CK_BYTE type = type_from_ptr(a->pValue, a->ulValueLen);
+-        if (type != TYPE_BYTE_INT_SEQ) {
+-            rv = _db_update_tobject_attrs(updb, tobj->id, tobj->attrs);
++        if (a) {
++            CK_BYTE type = type_from_ptr(a->pValue, a->ulValueLen);
++            if (type != TYPE_BYTE_INT_SEQ) {
++                rv = _db_update_tobject_attrs(updb, tobj->id, tobj->attrs);
++            }
+         }
+ 
+         tobject_free(tobj);
diff --git a/third_party/overlays/tvl.nix b/third_party/overlays/tvl.nix
index 7105dc59c2..b54e899b88 100644
--- a/third_party/overlays/tvl.nix
+++ b/third_party/overlays/tvl.nix
@@ -1,26 +1,46 @@
 # This overlay is used to make TVL-specific modifications in the
 # nixpkgs tree, where required.
-{ depot, ... }:
+{ lib, depot, localSystem, ... }:
 
-self: super: {
-  # Rollback Nix to a stable version (2.3) while there is lots of
-  # random ecosystem breakage with the newer versions.
-  nix = super.nix_2_3;
-
-  clang-tools_11 = self.clang-tools.override {
-    llvmPackages = self.llvmPackages_11;
+self: super:
+depot.nix.readTree.drvTargets {
+  nix_2_3 = (super.nix_2_3.override {
+    # flaky tests, long painful build, see https://github.com/NixOS/nixpkgs/pull/266443
+    withAWS = false;
+  });
+  nix = self.nix_2_3 // {
+    # avoid duplicate pipeline step
+    meta = self.nix_2_3.meta or { } // {
+      ci = self.nix_2_3.meta.ci or { } // {
+        skip = true;
+      };
+    };
   };
+  nix_latest = super.nix.override ({
+    # flaky tests, long painful build, see https://github.com/NixOS/nixpkgs/pull/266443
+    withAWS = false;
+  });
+
+  # To match telega in emacs-overlay or wherever
+  tdlib = super.tdlib.overrideAttrs (_: {
+    version = "1.8.24";
+    src = self.fetchFromGitHub {
+      owner = "tdlib";
+      repo = "td";
+      rev = "d79bd4b69403868897496da39b773ab25c69f6af";
+      sha256 = "0bc5akzw12qwj45rzqkrhw65qlrn9q8pzmvc5aiqv4bvhkb1ghl0";
+    };
+  });
 
-  # stdenv which uses clang, lld and libc++; full is a slight exaggeration,
-  # we for example don't use LLVM's libunwind
-  fullLlvm11Stdenv = self.overrideCC self.stdenv
-    (self.llvmPackages_11.libcxxStdenv.cc.override {
-      inherit (self.llvmPackages_11) bintools;
-    });
+  home-manager = super.home-manager.overrideAttrs (_: {
+    src = depot.third_party.sources.home-manager;
+    version = "git-"
+      + builtins.substring 0 7 depot.third_party.sources.home-manager.rev;
+  });
 
   # Add our Emacs packages to the fixpoint
   emacsPackagesFor = emacs: (
-    (super.emacsPackagesFor emacs).overrideScope' (eself: esuper: {
+    (super.emacsPackagesFor emacs).overrideScope (eself: esuper: {
       tvlPackages = depot.tools.emacs-pkgs // depot.third_party.emacs;
 
       # Use the notmuch from nixpkgs instead of from the Emacs
@@ -28,14 +48,32 @@ self: super: {
       notmuch = super.notmuch.emacs;
 
       # Build EXWM with the depot sources instead.
-      exwm = esuper.exwm.overrideAttrs(_: {
-        src = depot.path.origSrc + "/third_party/exwm";
+      depotExwm = eself.callPackage depot.third_party.exwm.override { };
+
+      # Workaround for magit checking the git version at load time
+      magit = esuper.magit.overrideAttrs (_: {
+        propagatedNativeBuildInputs = [
+          self.git
+        ];
       });
+
+      # Pin xelb to a newer one until the new maintainers do a release.
+      xelb = eself.trivialBuild {
+        pname = "xelb";
+        version = "0.19-dev"; # invented version, last actual release was 0.18
+
+        src = self.fetchFromGitHub {
+          owner = "emacs-exwm";
+          repo = "xelb";
+          rev = "86089eba2de6c818bfa2fac075cb7ad876262798";
+          sha256 = "1mmlrd2zpcwiv8gh10y7lrpflnbmsycdascrxjr3bfcwa8yx7901";
+        };
+      };
     })
   );
 
   # dottime support for notmuch
-  notmuch = super.notmuch.overrideAttrs(old: {
+  notmuch = super.notmuch.overrideAttrs (old: {
     passthru = old.passthru // {
       patches = old.patches ++ [ ./patches/notmuch-dottime.patch ];
     };
@@ -43,10 +81,43 @@ self: super: {
 
   # nix-serve does not work with nix 2.4
   # https://github.com/edolstra/nix-serve/issues/28
-  nix-serve = super.nix-serve.override { nix = super.nix_2_3; };
+  nix-serve = super.nix-serve.override { nix = self.nix_2_3; };
 
   # Avoid builds of mkShell derivations in CI.
-  mkShell = super.lib.makeOverridable(args: (super.mkShell args) // {
-    meta.ci.skip = true;
+  mkShell = super.lib.makeOverridable (args: (super.mkShell args).overrideAttrs (_: {
+    passthru = {
+      meta.ci.skip = true;
+    };
+  }));
+
+  # https://github.com/googleapis/google-cloud-go/pull/9665
+  cbtemulator = super.cbtemulator.overrideAttrs (old: {
+    patches = old.patches or [ ] ++ [
+      ./patches/cbtemulator-uds.patch
+    ];
+  });
+
+  crate2nix = super.crate2nix.overrideAttrs (old: {
+    patches = old.patches or [ ] ++ [
+      # https://github.com/nix-community/crate2nix/pull/301
+      ./patches/crate2nix-tests-debug.patch
+    ];
+  });
+
+  evans = super.evans.overrideAttrs (old: {
+    patches = old.patches or [ ] ++ [
+      # add support for unix domain sockets
+      # https://github.com/ktr0731/evans/pull/680
+      ./patches/evans-add-support-for-unix-domain-sockets.patch
+    ];
+  });
+
+  # Imports a patch that fixes usage of this package on versions
+  # >=1.9. The patch has been proposed upstream, but so far with no
+  # reactions from the maintainer:
+  #
+  # https://github.com/tpm2-software/tpm2-pkcs11/pull/849
+  tpm2-pkcs11 = super.tpm2-pkcs11.overrideAttrs (old: {
+    patches = (old.patches or [ ]) ++ [ ./patches/tpm2-pkcs11-190-dbupgrade.patch ];
   });
 }
diff --git a/third_party/prometheus-fail2ban-exporter/default.nix b/third_party/prometheus-fail2ban-exporter/default.nix
index bed5bd6301..42ba0a14db 100644
--- a/third_party/prometheus-fail2ban-exporter/default.nix
+++ b/third_party/prometheus-fail2ban-exporter/default.nix
@@ -7,10 +7,11 @@ let
   };
 
   python = pkgs.python3.withPackages (p: [
-    p.prometheus_client
+    p.prometheus-client
   ]);
 
-in pkgs.writeShellScriptBin "prometheus-fail2ban-exporter" ''
+in
+pkgs.writeShellScriptBin "prometheus-fail2ban-exporter" ''
   set -eo pipefail
 
   exec "${python}/bin/python" "${script}"
diff --git a/third_party/protobuf/default.nix b/third_party/protobuf/default.nix
deleted file mode 100644
index b30ff9de91..0000000000
--- a/third_party/protobuf/default.nix
+++ /dev/null
@@ -1,12 +0,0 @@
-# Pin protobuf to version 3.12, with LLVM.
-{ depot, pkgs, ... }:
-
-pkgs.callPackage "${pkgs.path}/pkgs/development/libraries/protobuf/generic-v3.nix" {
-  version = "3.12.2";
-  sha256 = "1lp368aa206vpic9fmax4k6llnmf28plfvkkm4vqhgphmjqykvl2";
-  stdenv = pkgs.fullLlvm11Stdenv;
-  buildPackages = {
-    inherit (pkgs) which;
-    stdenv = pkgs.fullLlvm11Stdenv;
-  };
-}
diff --git a/third_party/public-inbox/0001-feat-always-set-the-List-ID-header-even-in-watch.patch b/third_party/public-inbox/0001-feat-always-set-the-List-ID-header-even-in-watch.patch
new file mode 100644
index 0000000000..bff2d4c200
--- /dev/null
+++ b/third_party/public-inbox/0001-feat-always-set-the-List-ID-header-even-in-watch.patch
@@ -0,0 +1,30 @@
+From 1719e904acf19499209b16a8a008f55390a7b5e2 Mon Sep 17 00:00:00 2001
+From: Vincent Ambo <mail@tazj.in>
+Date: Sun, 29 Jan 2023 13:36:12 +0300
+Subject: [PATCH] feat: always set the List-ID header even in -watch
+
+Without bothering to figure out exactly how this code path is usually
+triggered, always set a list ID when ingesting new emails in
+public-inbox-watch.
+---
+ lib/PublicInbox/Watch.pm | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/lib/PublicInbox/Watch.pm b/lib/PublicInbox/Watch.pm
+index 3f6fe21..147971c 100644
+--- a/lib/PublicInbox/Watch.pm
++++ b/lib/PublicInbox/Watch.pm
+@@ -188,6 +188,10 @@ sub _remove_spam {
+ sub import_eml ($$$) {
+ 	my ($self, $ibx, $eml) = @_;
+ 
++        # TVL-specific: always set the list-id header, regardless of
++        # any of the other logic below.
++        PublicInbox::MDA->set_list_headers($eml, $ibx);
++
+ 	# any header match means it's eligible for the inbox:
+ 	if (my $watch_hdrs = $ibx->{-watchheaders}) {
+ 		my $ok;
+-- 
+2.39.0
+
diff --git a/third_party/public-inbox/default.nix b/third_party/public-inbox/default.nix
new file mode 100644
index 0000000000..1a4b196f94
--- /dev/null
+++ b/third_party/public-inbox/default.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+pkgs.public-inbox.overrideAttrs (old: {
+  patches = (old.patches or [ ]) ++ [
+    ./0001-feat-always-set-the-List-ID-header-even-in-watch.patch
+  ];
+
+  doCheck = false; # too slow, and nixpkgs already runs them
+})
diff --git a/third_party/python/broadlink/default.nix b/third_party/python/broadlink/default.nix
index e316d83d1d..b1dcf30081 100644
--- a/third_party/python/broadlink/default.nix
+++ b/third_party/python/broadlink/default.nix
@@ -7,7 +7,8 @@
 let
   inherit (pkgs) fetchFromGitHub;
   inherit (pkgs.python3Packages) buildPythonPackage cryptography;
-in buildPythonPackage (lib.fix (self: {
+in
+buildPythonPackage (lib.fix (self: {
   pname = "python-broadlink";
   version = "0.13.2";
   src = ./.;
diff --git a/third_party/rapidcheck/default.nix b/third_party/rapidcheck/default.nix
deleted file mode 100644
index ec8c01694d..0000000000
--- a/third_party/rapidcheck/default.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-{ pkgs, ... }:
-
-(pkgs.callPackage "${pkgs.path}/pkgs/development/libraries/rapidcheck" {
-  stdenv = pkgs.fullLlvm11Stdenv;
-}).overrideAttrs (attrs: rec {
-  # follows the versioning scheme of nixpkgs, since rapidcheck does not
-  # provide versioned releases
-  version = "unstable-2020-05-04";
-
-  src = pkgs.fetchFromGitHub {
-    owner = "emil-e";
-    repo = "rapidcheck";
-    rev = "7bc7d302191a4f3d0bf005692677126136e02f60";
-    sha256 = "0khawy2n007yk97ls2qqpna4ly09v6rb6hw72nm16kzk3zbyzh17";
-  };
-
-  cmakeFlags = [
-    "-DRC_ENABLE_GTEST=ON"
-    "-DRC_ENABLE_GMOCK=ON"
-  ];
-})
diff --git a/third_party/re2/default.nix b/third_party/re2/default.nix
deleted file mode 100644
index 62bce9e2ee..0000000000
--- a/third_party/re2/default.nix
+++ /dev/null
@@ -1,5 +0,0 @@
-{ pkgs, ... }:
-
-pkgs.re2.override {
-  stdenv = pkgs.fullLlvm11Stdenv;
-}
diff --git a/third_party/rust-crates/OWNERS b/third_party/rust-crates/OWNERS
index e978e46150..d2bb7704b6 100644
--- a/third_party/rust-crates/OWNERS
+++ b/third_party/rust-crates/OWNERS
@@ -1,5 +1,3 @@
-inherited: true
-owners:
-  - sterni
-  - Profpatsch
-  - zseri
+fogti
+sterni
+Profpatsch
diff --git a/third_party/rust-crates/default.nix b/third_party/rust-crates/default.nix
index b912a9bde3..697e47cdde 100644
--- a/third_party/rust-crates/default.nix
+++ b/third_party/rust-crates/default.nix
@@ -1,23 +1,24 @@
 { depot, pkgs, ... }:
 
 # TVL tool rust crate dependencies, where tools like carnix are not used.
-# Intended for manual updates, which keeps us honest with what we pull into our closure.
+# Intended for manual updates, which makes sure we never actually update.
 
 let
-  buildRustCrate = attrs@{
-    edition ? "2018",
-    pname,
-    crateName ? pname,
-    ...
-  }: pkgs.buildRustCrate (attrs // {
-    inherit
-      crateName
-      edition
-      ;
-   });
+  inherit (pkgs) fetchpatch;
+
+  buildRustCrate =
+    attrs@{ edition ? "2018"
+    , pname
+    , crateName ? pname
+    , ...
+    }: pkgs.buildRustCrate (attrs // {
+      inherit
+        crateName
+        edition
+        ;
+    });
 in
-
-rec {
+depot.nix.readTree.drvTargets rec{
   cfg-if = buildRustCrate {
     pname = "cfg-if";
     version = "1.0.0";
@@ -39,18 +40,18 @@ rec {
 
   regex-syntax = buildRustCrate {
     pname = "regex-syntax";
-    version = "0.6.22";
+    version = "0.6.25";
     edition = "2015";
-    sha256 = "0r00n2dgyixacl1sczqp18gxf0xh7x272hcdp62412lypba2gqyg";
+    sha256 = "0i211p26m97ii169g0f4gf2a99r8an4xc1fdqj0sf5wpn17qhs29";
   };
 
   regex = buildRustCrate {
     pname = "regex";
-    version = "1.4.3";
+    version = "1.5.5";
     features = [ "std" ];
     dependencies = [ regex-syntax ];
-    edition = "2015";
-    sha256 = "0w0b4bh0ng20lf5y8raaxmxj46ikjqpgwy1iggzpby9lhv9vydkp";
+    edition = "2018";
+    sha256 = "0i7yrxsvxpx682vdbkvj7j4w3a3z2c1qwmaa795mm9a9prx4yzjk";
   };
 
   libloading = buildRustCrate {
@@ -75,15 +76,15 @@ rec {
 
   libc = buildRustCrate {
     pname = "libc";
-    version = "0.2.82";
+    version = "0.2.153";
     edition = "2015";
-    sha256 = "02zgn6c0xwh331hky417lbr29kmvrw3ylxs8822syyhjfjqszvsx";
+    sha256 = "1xz1nz9k0vrv7lbir7ma0q4ii9cp3c0s9fbxp6268film2wrxs19";
   };
 
   bitflags = buildRustCrate {
     pname = "bitflags";
-    version = "1.2.1";
-    sha256 = "0b77awhpn7yaqjjibm69ginfn996azx5vkzfjj39g3wbsqs7mkxg";
+    version = "2.4.2";
+    sha256 = "1p370m8qh3clk33rqmyglcphlsq0gpf69j22d61fy4kkmrfn8hbd";
   };
 
   inotify-sys = buildRustCrate {
@@ -95,9 +96,18 @@ rec {
 
   inotify = buildRustCrate {
     pname = "inotify";
-    version = "0.9.2";
+    version = "0.10.2";
+    patches = [
+      # Unreleased compat patch for bitflags >= 2
+      (fetchpatch {
+        name = "inotify-bitflags-2.patch";
+        url = "https://github.com/hannobraun/inotify-rs/commit/f4765593894ef0b36d39739cf3349485ca88b1ce.patch";
+        sha256 = "107r9jai0jdr0hybsvbjyjn23vyk2lp1l1pmznb7jp38my0grh4b";
+        excludes = [ "Cargo.toml" ];
+      })
+    ];
     dependencies = [ bitflags libc inotify-sys ];
-    sha256 = "0fcknyvknglwwk1pdzdlb4m0ry2dym1yx8r5prf2v00pxnjk0hv2";
+    sha256 = "0lqwk7yf6bzc2jzj5iji2p3f29zdpllqd207vgg7jswmg2gqnlqc";
   };
 
   httparse = buildRustCrate {
@@ -108,7 +118,7 @@ rec {
   };
 
   version-check = buildRustCrate {
-    pname = "version-check";
+    pname = "version_check";
     version = "0.9.2";
     edition = "2015";
     sha256 = "1vwvc1mzwv8ana9jv8z933p2xzgj1533qwwl5zr8mi89azyhq21v";
@@ -168,11 +178,11 @@ rec {
 
   chrono = buildRustCrate {
     pname = "chrono";
-    version = "0.4.19";
-    edition = "2015";
+    version = "0.4.22";
+    edition = "2018";
     dependencies = [ num-traits num-integer ];
     features = [ "alloc" "std" ];
-    sha256 = "0cjf5dnfbk99607vz6n5r6bhwykcypq5psihvk845sxrhnzadsar";
+    sha256 = "01vbn93ba1q2afq10qis41j847damk5ifgn1all337mcscl345fn";
   };
 
   imap-proto = buildRustCrate {
@@ -205,9 +215,9 @@ rec {
 
   epoll = buildRustCrate {
     pname = "epoll";
-    version = "4.3.1";
+    version = "4.3.3";
     dependencies = [ bitflags libc ];
-    sha256 = "0dgmgdmrfbjkpxn1w3xmmwsm2a623a9qdwn90s8yl78n4a36kbh9";
+    sha256 = "1wc8dsd0dhqgskmkwd82fzqsy2hg0wm3833jxhzxkrwcip25yr3a";
   };
 
   serde = buildRustCrate {
@@ -279,14 +289,12 @@ rec {
   pkg-config = buildRustCrate {
     pname = "pkg-config";
     version = "0.3.19";
-    crateName = "pkg_config";
     sha256 = "1kd047p8jv6mhmfzddjvfa2nwkfrb3l1wml6lfm51n1cr06cc9lz";
   };
 
   libz-sys = buildRustCrate {
     pname = "libz-sys";
     version = "1.1.2";
-    crateName = "libz-sys";
     sha256 = "1y7v6bkwr4b6yaf951p1ns7mx47b29ziwdd5wziaic14gs1gwq30";
     buildDependencies = [
       cc
@@ -296,9 +304,8 @@ rec {
 
   libgit2-sys = buildRustCrate {
     pname = "libgit2-sys";
-    version = "0.12.26+1.3.0";
-    crateName = "libgit2_sys";
-    sha256 = "15zg0yy7lk7464yf9i1kxh4gaxdyb8m96ayb7vkjgmz1s2rgq7s2";
+    version = "0.16.2+1.7.2";
+    sha256 = "0bs446idbmg8s13jvb0ck6qmrskcdn2mp3d4mn9ggxbmiw4ryd3g";
     dependencies = [
       libc
       libz-sys
@@ -313,20 +320,19 @@ rec {
       cc
       pkg-config
     ];
+    env.LIBGIT2_NO_VENDOR = "1";
   };
 
   matches = buildRustCrate {
     pname = "matches";
     version = "0.1.8";
-    crateName = "matches";
     sha256 = "03hl636fg6xggy0a26200xs74amk3k9n0908rga2szn68agyz3cv";
     libPath = "lib.rs";
   };
 
   percent-encoding = buildRustCrate {
-    pname = "percent_encoding";
+    pname = "percent-encoding";
     version = "2.1.0";
-    crateName = "percent_encoding";
     sha256 = "0i838f2nr81585ckmfymf8l1x1vdmx6n8xqvli0lgcy60yl2axy3";
     libPath = "lib.rs";
   };
@@ -334,7 +340,6 @@ rec {
   form_urlencoded = buildRustCrate {
     pname = "form_urlencoded";
     version = "1.0.1";
-    crateName = "form_urlencoded";
     sha256 = "0rhv2hfrzk2smdh27walkm66zlvccnnwrbd47fmf8jh6m420dhj8";
     dependencies = [
       matches
@@ -345,14 +350,12 @@ rec {
   tinyvec_macros = buildRustCrate {
     pname = "tinyvec_macros";
     version = "0.1.0";
-    crateName = "tinyvec-macros";
     sha256 = "0aim73hyq5g8b2hs9gjq2sv0xm4xzfbwp5fdyg1frljqzkapq682";
   };
 
   tinyvec = buildRustCrate {
     pname = "tinyvec";
     version = "1.2.0";
-    crateName = "tinyvec";
     sha256 = "1c95nma20kiyrjwfsk7hzd5ir6yy4bm63fmfbfb4dm9ahnlvdp3y";
     features = [ "alloc" ];
     dependencies = [
@@ -363,7 +366,6 @@ rec {
   unicode-normalization = buildRustCrate {
     pname = "unicode-normalization";
     version = "0.1.17";
-    crateName = "unicode_normalization";
     sha256 = "0w4s0avzlf7pzcclhhih93aap613398sshm6jrxcwq0f9lhis11c";
     dependencies = [
       tinyvec
@@ -373,7 +375,6 @@ rec {
   unicode-bidi = buildRustCrate {
     pname = "unicode-bidi";
     version = "0.3.5";
-    crateName = "unicode_bidi";
     sha256 = "193jzlxj1dfcms2381lyd45zh4ywlicj9lzcfpid1zbkmfarymkz";
     dependencies = [
       matches
@@ -383,7 +384,6 @@ rec {
   idna = buildRustCrate {
     pname = "idna";
     version = "0.2.3";
-    crateName = "idna";
     sha256 = "0hwypd0fpym9lmd4bbqpwyr5lhrlvmvzhi1vy9asc5wxwkzrh299";
     dependencies = [
       matches
@@ -395,7 +395,6 @@ rec {
   url = buildRustCrate {
     pname = "url";
     version = "2.2.1";
-    crateName = "url";
     sha256 = "1ci1djafh83qhpzbmxnr9w5gcrjs3ghf8rrxdy4vklqyji6fvn5v";
     dependencies = [
       form_urlencoded
@@ -409,9 +408,8 @@ rec {
   git2 = buildRustCrate {
     pname = "git2";
     edition = "2018";
-    version = "0.13.25";
-    crateName = "git2";
-    sha256 = "181mw4kxsqrwpib9kf25fykc48wxhjla37vzis4j0b0w0yhyaqi3";
+    version = "0.18.1";
+    sha256 = "1d1wm8cn37svyxgvzfapwilkkc9d2x7fcrgciwn8b2pv9aqz102k";
     dependencies = [
       bitflags
       libc
diff --git a/third_party/rustsec-advisory-db/default.nix b/third_party/rustsec-advisory-db/default.nix
index 3d3b387be8..e0ea2b080a 100644
--- a/third_party/rustsec-advisory-db/default.nix
+++ b/third_party/rustsec-advisory-db/default.nix
@@ -1,24 +1,19 @@
 # RustSec's advisory db for crates
-#
-# Update using:
-#
-#   nix-prefetch-git --quiet --url https://github.com/RustSec/advisory-db.git > third_party/rustsec-advisory-db/pin.json
-#
-# TODO(Profpatsch): automatically update in regular intervals
-{ pkgs, ... }:
+{ pkgs, depot, ... }:
 
 let
-  pin = builtins.fromJSON (builtins.readFile ./pin.json);
-
-  date = builtins.head (builtins.split "T" pin.date);
+  inherit (depot.third_party.sources) rustsec-advisory-db;
 in
 
 pkgs.fetchFromGitHub {
-  name = "advisory-db-${date}";
-  owner = "RustSec";
-  repo = "advisory-db";
-  inherit (pin)
-    rev
+  inherit (rustsec-advisory-db)
+    owner
+    repo
     sha256
-  ;
+    rev
+    ;
+
+  passthru = {
+    inherit (rustsec-advisory-db) rev;
+  };
 }
diff --git a/third_party/rustsec-advisory-db/pin.json b/third_party/rustsec-advisory-db/pin.json
deleted file mode 100644
index 1155625cff..0000000000
--- a/third_party/rustsec-advisory-db/pin.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "url": "https://github.com/RustSec/advisory-db.git",
-  "rev": "d29205a680bb8b3a22eaba6e9b2a5a6580274af0",
-  "date": "2021-10-08T18:17:22+02:00",
-  "path": "/nix/store/nm8nwgdyrs6mi9dydf6vylc833i3alnn-advisory-db",
-  "sha256": "0h08kfn2878k5l0qdsxikakrjbqbn6fb8f95zxpqfh5hqzn7mb6b",
-  "fetchLFS": false,
-  "fetchSubmodules": false,
-  "deepClone": false,
-  "leaveDotGit": false
-}
diff --git a/third_party/smtprelay/default.nix b/third_party/smtprelay/default.nix
index b2d730a667..1a68245e92 100644
--- a/third_party/smtprelay/default.nix
+++ b/third_party/smtprelay/default.nix
@@ -4,7 +4,7 @@
 pkgs.buildGoModule rec {
   pname = "smtprelay";
   version = "1.7.0";
-  vendorSha256 = "00nb81hdg5pv5l0q7w5lv08dv4v72vml7jha351frani0gpg27pn";
+  vendorHash = "sha256:00nb81hdg5pv5l0q7w5lv08dv4v72vml7jha351frani0gpg27pn";
 
   src = pkgs.fetchFromGitHub {
     owner = "decke";
diff --git a/third_party/sources/default.nix b/third_party/sources/default.nix
new file mode 100644
index 0000000000..5894c92079
--- /dev/null
+++ b/third_party/sources/default.nix
@@ -0,0 +1,151 @@
+# This file has been generated by Niv.
+_:
+let
+
+  #
+  # The fetchers. fetch_<type> fetches specs of type <type>.
+  #
+
+  fetch_file = pkgs: spec:
+    if spec.builtin or true then
+      builtins_fetchurl { inherit (spec) url sha256; }
+    else
+      pkgs.fetchurl { inherit (spec) url sha256; };
+
+  fetch_tarball = pkgs: name: spec:
+    let
+      ok = str: ! builtins.isNull (builtins.match "[a-zA-Z0-9+-._?=]" str);
+      # sanitize the name, though nix will still fail if name starts with period
+      name' = stringAsChars (x: if ! ok x then "-" else x) "${name}-src";
+    in
+    if spec.builtin or true then
+      builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
+    else
+      pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
+
+  fetch_git = spec:
+    builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; };
+
+  fetch_local = spec: spec.path;
+
+  fetch_builtin-tarball = name: throw
+    ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
+        $ niv modify ${name} -a type=tarball -a builtin=true'';
+
+  fetch_builtin-url = name: throw
+    ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
+        $ niv modify ${name} -a type=file -a builtin=true'';
+
+  #
+  # Various helpers
+  #
+
+  # The set of packages used when specs are fetched using non-builtins.
+  mkPkgs = sources:
+    let
+      sourcesNixpkgs =
+        import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { };
+      hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
+      hasThisAsNixpkgsPath = <nixpkgs> == ./.;
+    in
+    if builtins.hasAttr "nixpkgs" sources
+    then sourcesNixpkgs
+    else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
+      import <nixpkgs> { }
+    else
+      abort
+        ''
+          Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
+          add a package called "nixpkgs" to your sources.json.
+        '';
+
+  # The actual fetching function.
+  fetch = pkgs: name: spec:
+
+    if ! builtins.hasAttr "type" spec then
+      abort "ERROR: niv spec ${name} does not have a 'type' attribute"
+    else if spec.type == "file" then fetch_file pkgs spec
+    else if spec.type == "tarball" then fetch_tarball pkgs name spec
+    else if spec.type == "git" then fetch_git spec
+    else if spec.type == "local" then fetch_local spec
+    else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
+    else if spec.type == "builtin-url" then fetch_builtin-url name
+    else
+      abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
+
+  # If the environment variable NIV_OVERRIDE_${name} is set, then use
+  # the path directly as opposed to the fetched source.
+  replace = name: drv:
+    let
+      saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
+      ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
+    in
+    if ersatz == "" then drv else ersatz;
+
+  # Ports of functions for older nix versions
+
+  # a Nix version of mapAttrs if the built-in doesn't exist
+  mapAttrs = builtins.mapAttrs or (
+    f: set: with builtins;
+    listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
+  );
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
+  range = first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1);
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
+  stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
+  stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
+  concatStrings = builtins.concatStringsSep "";
+
+  # fetchTarball version that is compatible between all the versions of Nix
+  builtins_fetchTarball = { url, name, sha256 }@attrs:
+    let
+      inherit (builtins) lessThan nixVersion fetchTarball;
+    in
+    if lessThan nixVersion "1.12" then
+      fetchTarball { inherit name url; }
+    else
+      fetchTarball attrs;
+
+  # fetchurl version that is compatible between all the versions of Nix
+  builtins_fetchurl = { url, sha256 }@attrs:
+    let
+      inherit (builtins) lessThan nixVersion fetchurl;
+    in
+    if lessThan nixVersion "1.12" then
+      fetchurl { inherit url; }
+    else
+      fetchurl attrs;
+
+  # Create the final "sources" from the config
+  mkSources = config:
+    mapAttrs
+      (
+        name: spec:
+          if builtins.hasAttr "outPath" spec
+          then
+            abort
+              "The values in sources.json should not have an 'outPath' attribute"
+          else
+            spec // { outPath = replace name (fetch config.pkgs name spec); }
+      )
+      config.sources;
+
+  # The "config" used by the fetchers
+  mkConfig =
+    { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
+    , sources ? if isNull sourcesFile then { } else builtins.fromJSON (builtins.readFile sourcesFile)
+    , pkgs ? mkPkgs sources
+    }: rec {
+      # The sources, i.e. the attribute set of spec name to spec
+      inherit sources;
+
+      # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
+      inherit pkgs;
+    };
+
+in
+mkSources (mkConfig { }) // { __functor = _: settings: mkSources (mkConfig settings); }
diff --git a/third_party/sources/sources.json b/third_party/sources/sources.json
new file mode 100644
index 0000000000..109451ff51
--- /dev/null
+++ b/third_party/sources/sources.json
@@ -0,0 +1,110 @@
+{
+    "agenix": {
+        "branch": "main",
+        "description": "age-encrypted secrets for NixOS",
+        "homepage": "https://matrix.to/#/#agenix:nixos.org",
+        "owner": "ryantm",
+        "repo": "agenix",
+        "rev": "0.15.0",
+        "sha256": "01dhrghwa7zw93cybvx4gnrskqk97b004nfxgsys0736823956la",
+        "type": "tarball",
+        "url": "https://github.com/ryantm/agenix/archive/0.15.0.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "home-manager": {
+        "branch": "master",
+        "description": "Manage a user environment using Nix  [maintainer=@rycee] ",
+        "homepage": "https://nix-community.github.io/home-manager/",
+        "owner": "nix-community",
+        "repo": "home-manager",
+        "rev": "c1609d584a6b5e9e6a02010f51bd368cb4782f8e",
+        "sha256": "112r86p3iah1xahwlp82yd3gvh10wkf271za5h7v3jsqv08c6gkr",
+        "type": "tarball",
+        "url": "https://github.com/nix-community/home-manager/archive/c1609d584a6b5e9e6a02010f51bd368cb4782f8e.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "impermanence": {
+        "branch": "master",
+        "description": "Modules to help you handle persistent state on systems with ephemeral root storage [maintainer=@talyz]",
+        "homepage": "",
+        "owner": "nix-community",
+        "repo": "impermanence",
+        "rev": "a33ef102a02ce77d3e39c25197664b7a636f9c30",
+        "sha256": "1mig6ns8l5iynsm6pfbnx2b9hmr592s1kqbw6gq1n25czdlcniam",
+        "type": "tarball",
+        "url": "https://github.com/nix-community/impermanence/archive/a33ef102a02ce77d3e39c25197664b7a636f9c30.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "naersk": {
+        "branch": "master",
+        "description": "Build rust crates in Nix. No configuration, no code generation, no IFD. Sandbox friendly. [maintainer: @Patryk27]",
+        "homepage": "",
+        "owner": "nmattia",
+        "repo": "naersk",
+        "rev": "c5037590290c6c7dae2e42e7da1e247e54ed2d49",
+        "sha256": "1ql5ziwfrpmc8cxhgflmdy2z06z4dsdfzjwb2vv9bag6a2chrvq8",
+        "type": "tarball",
+        "url": "https://github.com/nmattia/naersk/archive/c5037590290c6c7dae2e42e7da1e247e54ed2d49.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "napalm": {
+        "branch": "master",
+        "description": "Support for building npm packages in Nix and lightweight npm registry [maintainer @nmattia]",
+        "homepage": "",
+        "owner": "nix-community",
+        "repo": "napalm",
+        "rev": "edcb26c266ca37c9521f6a97f33234633cbec186",
+        "sha256": "0ai1ax380nnpz0mbgbc5vdzafyjilcmdj7kgv087x2vagpprb4yy",
+        "type": "tarball",
+        "url": "https://github.com/nix-community/napalm/archive/edcb26c266ca37c9521f6a97f33234633cbec186.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "nixpkgs": {
+        "branch": "nixos-unstable",
+        "description": "Nix Packages collection",
+        "homepage": "",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "7bb2ccd8cdc44c91edba16c48d2c8f331fb3d856",
+        "sha256": "0ijqx995jw9i16f28whyjdll9b0nydmyl4n91bci2cgryxms7f8f",
+        "type": "tarball",
+        "url": "https://github.com/NixOS/nixpkgs/archive/7bb2ccd8cdc44c91edba16c48d2c8f331fb3d856.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "nixpkgs-stable": {
+        "branch": "nixos-23.11",
+        "description": "Nix Packages collection",
+        "homepage": "",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "dd37924974b9202f8226ed5d74a252a9785aedf8",
+        "sha256": "1nxd4dqci8rs94a7cypx30axgj778p2wydkx16q298n29crkflbw",
+        "type": "tarball",
+        "url": "https://github.com/NixOS/nixpkgs/archive/dd37924974b9202f8226ed5d74a252a9785aedf8.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "rust-overlay": {
+        "branch": "master",
+        "description": "Pure and reproducible nix overlay of binary distributed rust toolchains",
+        "homepage": "",
+        "owner": "oxalica",
+        "repo": "rust-overlay",
+        "rev": "2a42c742ab04b61d9b2f1edf392842cf9f27ebfd",
+        "sha256": "1wpkca75ysb2ssycc0dshd1m76q8iqhzrrbr6xmfmkkcj1p333nk",
+        "type": "tarball",
+        "url": "https://github.com/oxalica/rust-overlay/archive/2a42c742ab04b61d9b2f1edf392842cf9f27ebfd.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "rustsec-advisory-db": {
+        "branch": "main",
+        "description": "Security advisory database for Rust crates published through crates.io",
+        "homepage": "https://rustsec.org",
+        "owner": "RustSec",
+        "repo": "advisory-db",
+        "rev": "35e7459a331d3e0c585e56dabd03006b9b354088",
+        "sha256": "1j8c0vzwg6b9lxmdy2a40pvwsy2kncv455spbjbxsj10p2vmy5fl",
+        "type": "tarball",
+        "url": "https://github.com/RustSec/advisory-db/archive/35e7459a331d3e0c585e56dabd03006b9b354088.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    }
+}
diff --git a/third_party/terraform-provider-glesys/default.nix b/third_party/terraform-provider-glesys/default.nix
index 51582fd609..2eea7e1905 100644
--- a/third_party/terraform-provider-glesys/default.nix
+++ b/third_party/terraform-provider-glesys/default.nix
@@ -4,14 +4,15 @@
 { pkgs, ... }:
 
 pkgs.terraform-providers.mkProvider rec {
-  version = "0.3.1";
+  version = "0.9.0";
+  spdx = "MPL-2.0";
 
   owner = "glesys";
   repo = "terraform-provider-glesys";
   rev = "v${version}";
-  sha256 = "1rcwzf31gdxjywkcnlq1nxv4y8fcrc2z2xrp73q61mglv01bqq8m";
+  hash = "sha256:0n2wb1gl0agc9agqlmhg4mh9kyfhw4zvrryyl8wfxlp1hkr0wz9y";
 
-  vendorSha256 = "0g5g69absf0vmin0ff0anrxcgfq0bzx4iz3qci90p9xkvyph4nlw";
+  vendorHash = "sha256:13wdx7q5rsyjrm6cn030m5hgcvx0m17dhr16wmbfv71pmsszfdjm";
 
   # This provider is not officially published in the TF registry, so
   # we're giving it a fake source here.
diff --git a/tools/checks/default.nix b/tools/checks/default.nix
new file mode 100644
index 0000000000..89e4b809bc
--- /dev/null
+++ b/tools/checks/default.nix
@@ -0,0 +1,38 @@
+# Utilities for CI checks that work with the readTree-based CI.
+{ pkgs, ... }:
+
+let
+  inherit (pkgs.lib.strings) sanitizeDerivationName;
+in
+{
+  # Utility for verifying Terraform configuration.
+  #
+  # Expects to be passed a pre-configured Terraform derivation and a
+  # source path, and will do a dummy-initialisation and config
+  # validation inside of that Terraform configuration.
+  validateTerraform =
+    {
+      # Environment name to use (inconsequential, only for drv name)
+      name ? "main"
+    , # Terraform package to use. Should be pre-configured with the
+      # correct providers.
+      terraform ? pkgs.terraform
+    , # Source path for Terraform configuration. Be careful about
+      # relative imports. Use the 'subDir' parameter to optionally cd
+      # into a subdirectory of source, e.g. if there is a flat structure
+      # with modules.
+      src
+    , # Sub-directory of $src from which to run the check. Useful in
+      # case of relative Terraform imports from a code tree
+      subDir ? "."
+    , # Environment variables to pass to Terraform. Necessary in case of
+      # dummy environment variables that need to be set.
+      env ? { }
+    }:
+    pkgs.runCommand "tf-validate-${sanitizeDerivationName name}" env ''
+      cp -r ${src}/* . && chmod -R u+w .
+      cd ${subDir}
+      ${terraform}/bin/terraform init -upgrade -backend=false -input=false
+      ${terraform}/bin/terraform validate | tee $out
+    '';
+}
diff --git a/tools/cheddar/Cargo.lock b/tools/cheddar/Cargo.lock
index 0635209a1d..41632ea159 100644
--- a/tools/cheddar/Cargo.lock
+++ b/tools/cheddar/Cargo.lock
@@ -16,39 +16,105 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.18"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "ansi_term"
-version = "0.11.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
 dependencies = [
  "winapi",
 ]
 
 [[package]]
-name = "arrayref"
-version = "0.3.6"
+name = "anstream"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
+checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
 
 [[package]]
-name = "arrayvec"
-version = "0.5.2"
+name = "anstyle"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
 
 [[package]]
-name = "ascii"
+name = "anstyle-parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
+name = "ascii"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
 
 [[package]]
 name = "atty"
@@ -56,22 +122,28 @@ version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 dependencies = [
- "hermit-abi",
+ "hermit-abi 0.1.19",
  "libc",
  "winapi",
 ]
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "base64"
-version = "0.13.0"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
 
 [[package]]
 name = "bincode"
@@ -83,61 +155,60 @@ dependencies = [
 ]
 
 [[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
 name = "bitflags"
 version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
-name = "blake2b_simd"
-version = "0.5.11"
+name = "bitflags"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
-dependencies = [
- "arrayref",
- "arrayvec",
- "constant_time_eq",
-]
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
 
 [[package]]
 name = "block-buffer"
-version = "0.7.3"
+version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
 dependencies = [
- "block-padding",
- "byte-tools",
- "byteorder",
  "generic-array",
 ]
 
 [[package]]
-name = "block-padding"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
-dependencies = [
- "byte-tools",
-]
-
-[[package]]
-name = "brotli-sys"
-version = "0.3.2"
+name = "brotli"
+version = "3.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd"
+checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f"
 dependencies = [
- "cc",
- "libc",
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
 ]
 
 [[package]]
-name = "brotli2"
-version = "0.3.2"
+name = "brotli-decompressor"
+version = "2.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e"
+checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
 dependencies = [
- "brotli-sys",
- "libc",
+ "alloc-no-stdlib",
+ "alloc-stdlib",
 ]
 
 [[package]]
@@ -151,22 +222,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "byte-tools"
-version = "0.3.1"
+name = "bumpalo"
+version = "3.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
-
-[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
 
 [[package]]
 name = "cc"
-version = "1.0.71"
+version = "1.0.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
+checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "cfg-if"
@@ -178,7 +246,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 name = "cheddar"
 version = "0.2.0"
 dependencies = [
- "clap",
+ "clap 2.34.0",
  "comrak",
  "lazy_static",
  "regex",
@@ -190,110 +258,165 @@ dependencies = [
 
 [[package]]
 name = "chrono"
-version = "0.4.19"
+version = "0.4.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
 dependencies = [
- "libc",
- "num-integer",
+ "android-tzdata",
+ "iana-time-zone",
  "num-traits",
- "time 0.1.43",
- "winapi",
+ "windows-targets",
 ]
 
 [[package]]
 name = "chunked_transfer"
-version = "1.4.0"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
+checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a"
 
 [[package]]
 name = "clap"
-version = "2.33.3"
+version = "2.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
 dependencies = [
  "ansi_term",
  "atty",
- "bitflags",
- "strsim",
+ "bitflags 1.3.2",
+ "strsim 0.8.0",
  "textwrap",
  "unicode-width",
  "vec_map",
 ]
 
 [[package]]
+name = "clap"
+version = "4.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim 0.10.0",
+ "terminal_size",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
 name = "comrak"
-version = "0.10.1"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b423acba50d5016684beaf643f9991e622633a4c858be6885653071c2da2b0c6"
+checksum = "c11e55664fcff7f4d37cc2adf3a1996913692f037312f4ab0909047fdd2bf962"
 dependencies = [
- "clap",
+ "clap 4.4.8",
  "entities",
- "lazy_static",
+ "memchr",
+ "once_cell",
  "pest",
  "pest_derive",
  "regex",
  "shell-words",
- "twoway 0.2.2",
+ "syntect",
  "typed-arena",
  "unicode_categories",
  "xdg",
 ]
 
 [[package]]
-name = "constant_time_eq"
-version = "0.1.5"
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
+checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "crc32fast"
-version = "1.2.1"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
-name = "crossbeam-utils"
-version = "0.8.5"
+name = "crypto-common"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
 dependencies = [
- "cfg-if",
- "lazy_static",
+ "generic-array",
+ "typenum",
 ]
 
 [[package]]
 name = "deflate"
-version = "0.9.1"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f95bf05dffba6e6cce8dfbb30def788154949ccd9aed761b472119c21e01c70"
+checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f"
 dependencies = [
  "adler32",
  "gzip-header",
 ]
 
 [[package]]
-name = "digest"
-version = "0.8.1"
+name = "deranged"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
 dependencies = [
- "generic-array",
+ "powerfmt",
 ]
 
 [[package]]
-name = "dirs"
-version = "1.0.5"
+name = "digest"
+version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 dependencies = [
- "libc",
- "redox_users",
- "winapi",
+ "block-buffer",
+ "crypto-common",
 ]
 
 [[package]]
@@ -303,32 +426,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
 
 [[package]]
-name = "fake-simd"
-version = "0.1.2"
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fancy-regex"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
+dependencies = [
+ "bit-set",
+ "regex",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
 
 [[package]]
 name = "filetime"
-version = "0.2.15"
+version = "0.2.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
+checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall 0.2.10",
- "winapi",
+ "redox_syscall 0.3.5",
+ "windows-sys",
 ]
 
 [[package]]
 name = "flate2"
-version = "1.0.22"
+version = "1.0.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
 dependencies = [
- "cfg-if",
  "crc32fast",
- "libc",
  "miniz_oxide",
 ]
 
@@ -340,59 +487,54 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "form_urlencoded"
-version = "1.0.1"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
 dependencies = [
- "matches",
  "percent-encoding",
 ]
 
 [[package]]
 name = "generic-array"
-version = "0.12.4"
+version = "0.14.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
 dependencies = [
  "typenum",
+ "version_check",
 ]
 
 [[package]]
 name = "getrandom"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.3"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi 0.10.2+wasi-snapshot-preview1",
+ "wasi",
 ]
 
 [[package]]
 name = "gzip-header"
-version = "0.3.0"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0131feb3d3bb2a5a238d8a4d09f6353b7ebfdc52e77bccbf4ea6eaa751dde639"
+checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2"
 dependencies = [
  "crc32fast",
 ]
 
 [[package]]
 name = "hashbrown"
-version = "0.11.2"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
 name = "hermit-abi"
@@ -404,55 +546,92 @@ dependencies = [
 ]
 
 [[package]]
+name = "hermit-abi"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
+
+[[package]]
 name = "httparse"
-version = "1.5.1"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
 
 [[package]]
 name = "idna"
-version = "0.2.3"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
+checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
 dependencies = [
- "matches",
  "unicode-bidi",
  "unicode-normalization",
 ]
 
 [[package]]
 name = "indexmap"
-version = "1.7.0"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
 dependencies = [
- "autocfg",
+ "equivalent",
  "hashbrown",
 ]
 
 [[package]]
 name = "itoa"
-version = "0.4.8"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
 [[package]]
-name = "lazy_static"
-version = "1.4.0"
+name = "js-sys"
+version = "0.3.65"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+dependencies = [
+ "wasm-bindgen",
+]
 
 [[package]]
-name = "lazycell"
-version = "1.3.0"
+name = "lazy_static"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.103"
+version = "0.2.150"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
 
 [[package]]
 name = "line-wrap"
@@ -465,48 +644,39 @@ dependencies = [
 
 [[package]]
 name = "linked-hash-map"
-version = "0.5.4"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
 
 [[package]]
-name = "log"
-version = "0.4.14"
+name = "linux-raw-sys"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
-dependencies = [
- "cfg-if",
-]
+checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
 
 [[package]]
-name = "maplit"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
-
-[[package]]
-name = "matches"
-version = "0.1.9"
+name = "log"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
 [[package]]
 name = "memchr"
-version = "2.4.1"
+version = "2.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
 
 [[package]]
 name = "mime"
-version = "0.3.16"
+version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
 [[package]]
 name = "mime_guess"
-version = "2.0.3"
+version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
 dependencies = [
  "mime",
  "unicase",
@@ -514,12 +684,11 @@ dependencies = [
 
 [[package]]
 name = "miniz_oxide"
-version = "0.4.4"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
 dependencies = [
  "adler",
- "autocfg",
 ]
 
 [[package]]
@@ -537,86 +706,87 @@ dependencies = [
  "rand",
  "safemem",
  "tempfile",
- "twoway 0.1.8",
+ "twoway",
 ]
 
 [[package]]
-name = "num-integer"
-version = "0.1.44"
+name = "num-traits"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
 dependencies = [
  "autocfg",
- "num-traits",
 ]
 
 [[package]]
-name = "num-traits"
-version = "0.2.14"
+name = "num_cpus"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "autocfg",
+ "hermit-abi 0.3.3",
+ "libc",
 ]
 
 [[package]]
-name = "num_cpus"
-version = "1.13.0"
+name = "num_threads"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
 dependencies = [
- "hermit-abi",
  "libc",
 ]
 
 [[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
 name = "onig"
-version = "6.3.0"
+version = "6.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b17403cf40f61e3ee059e3e90b7fc0a2953297168d4379b160f80d18fed848a4"
+checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f"
 dependencies = [
- "bitflags",
- "lazy_static",
+ "bitflags 1.3.2",
  "libc",
+ "once_cell",
  "onig_sys",
 ]
 
 [[package]]
 name = "onig_sys"
-version = "69.7.1"
+version = "69.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dd3eee045c84695b53b20255bb7317063df090b68e18bfac0abb6c39cf7f33e"
+checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7"
 dependencies = [
  "cc",
  "pkg-config",
 ]
 
 [[package]]
-name = "opaque-debug"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
-
-[[package]]
 name = "percent-encoding"
-version = "2.1.0"
+version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
 
 [[package]]
 name = "pest"
-version = "2.1.3"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5"
 dependencies = [
+ "memchr",
+ "thiserror",
  "ucd-trie",
 ]
 
 [[package]]
 name = "pest_derive"
-version = "2.1.0"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
+checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2"
 dependencies = [
  "pest",
  "pest_generator",
@@ -624,9 +794,9 @@ dependencies = [
 
 [[package]]
 name = "pest_generator"
-version = "2.1.3"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
+checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227"
 dependencies = [
  "pest",
  "pest_meta",
@@ -637,48 +807,54 @@ dependencies = [
 
 [[package]]
 name = "pest_meta"
-version = "2.1.3"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
+checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6"
 dependencies = [
- "maplit",
+ "once_cell",
  "pest",
- "sha-1",
+ "sha2",
 ]
 
 [[package]]
 name = "pkg-config"
-version = "0.3.20"
+version = "0.3.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
 
 [[package]]
 name = "plist"
-version = "1.2.1"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a38d026d73eeaf2ade76309d0c65db5a35ecf649e3cec428db316243ea9d6711"
+checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef"
 dependencies = [
- "base64",
- "chrono",
+ "base64 0.21.5",
  "indexmap",
  "line-wrap",
+ "quick-xml",
  "serde",
- "xml-rs",
+ "time",
 ]
 
 [[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
 name = "ppv-lite86"
-version = "0.2.10"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.29"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
 dependencies = [
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -688,24 +864,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 
 [[package]]
+name = "quick-xml"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "quote"
-version = "1.0.10"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "rand"
-version = "0.8.4"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
  "rand_chacha",
  "rand_core",
- "rand_hc",
 ]
 
 [[package]]
@@ -720,116 +904,108 @@ dependencies = [
 
 [[package]]
 name = "rand_core"
-version = "0.6.3"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
- "getrandom 0.2.3",
+ "getrandom",
 ]
 
 [[package]]
-name = "rand_hc"
-version = "0.3.1"
+name = "redox_syscall"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
 dependencies = [
- "rand_core",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "redox_syscall"
-version = "0.1.57"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
-
-[[package]]
-name = "redox_syscall"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
-name = "redox_users"
-version = "0.3.5"
+name = "regex"
+version = "1.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
 dependencies = [
- "getrandom 0.1.16",
- "redox_syscall 0.1.57",
- "rust-argon2",
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax 0.8.2",
 ]
 
 [[package]]
-name = "regex"
-version = "1.5.4"
+name = "regex-automata"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-syntax",
+ "regex-syntax 0.8.2",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.25"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
 
 [[package]]
-name = "remove_dir_all"
-version = "0.5.3"
+name = "regex-syntax"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
 
 [[package]]
 name = "rouille"
-version = "3.3.1"
+version = "3.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc1bcf3b32bd9ef568402e750404c369ff172a6a34597c858f8ccf5f3bed013"
+checksum = "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921"
 dependencies = [
- "base64",
- "brotli2",
+ "base64 0.13.1",
+ "brotli",
  "chrono",
  "deflate",
  "filetime",
  "multipart",
- "num_cpus",
  "percent-encoding",
  "rand",
  "serde",
  "serde_derive",
  "serde_json",
- "sha1",
+ "sha1_smol",
  "threadpool",
- "time 0.3.3",
+ "time",
  "tiny_http",
  "url",
 ]
 
 [[package]]
-name = "rust-argon2"
-version = "0.8.3"
+name = "rustix"
+version = "0.38.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
 dependencies = [
- "base64",
- "blake2b_simd",
- "constant_time_eq",
- "crossbeam-utils",
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
 ]
 
 [[package]]
 name = "ryu"
-version = "1.0.5"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
 
 [[package]]
 name = "safemem"
@@ -848,18 +1024,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.130"
+version = "1.0.192"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
+checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.130"
+version = "1.0.192"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
+checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -868,9 +1044,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.68"
+version = "1.0.108"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
 dependencies = [
  "itoa",
  "ryu",
@@ -878,28 +1054,27 @@ dependencies = [
 ]
 
 [[package]]
-name = "sha-1"
-version = "0.8.2"
+name = "sha1_smol"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
-dependencies = [
- "block-buffer",
- "digest",
- "fake-simd",
- "opaque-debug",
-]
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
 
 [[package]]
-name = "sha1"
-version = "0.6.0"
+name = "sha2"
+version = "0.10.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
 
 [[package]]
 name = "shell-words"
-version = "1.0.0"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074"
+checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
 
 [[package]]
 name = "strsim"
@@ -908,50 +1083,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 
 [[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
 name = "syn"
-version = "1.0.80"
+version = "2.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
 dependencies = [
  "proc-macro2",
  "quote",
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
 name = "syntect"
-version = "4.6.0"
+version = "5.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b20815bbe80ee0be06e6957450a841185fcf690fe0178f14d77a05ce2caa031"
+checksum = "e02b4b303bf8d08bfeb0445cba5068a3d306b6baece1d5582171a9bf49188f91"
 dependencies = [
  "bincode",
- "bitflags",
+ "bitflags 1.3.2",
+ "fancy-regex",
  "flate2",
  "fnv",
- "lazy_static",
- "lazycell",
+ "once_cell",
  "onig",
  "plist",
- "regex-syntax",
+ "regex-syntax 0.7.5",
  "serde",
- "serde_derive",
  "serde_json",
+ "thiserror",
  "walkdir",
  "yaml-rust",
 ]
 
 [[package]]
 name = "tempfile"
-version = "3.2.0"
+version = "3.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
 dependencies = [
  "cfg-if",
- "libc",
- "rand",
- "redox_syscall 0.2.10",
- "remove_dir_all",
- "winapi",
+ "fastrand",
+ "redox_syscall 0.4.1",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "terminal_size"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
+dependencies = [
+ "rustix",
+ "windows-sys",
 ]
 
 [[package]]
@@ -964,6 +1154,26 @@ dependencies = [
 ]
 
 [[package]]
+name = "thiserror"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "threadpool"
 version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -974,50 +1184,61 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.1.43"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
+checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
 dependencies = [
+ "deranged",
+ "itoa",
  "libc",
- "winapi",
+ "num_threads",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
 ]
 
 [[package]]
-name = "time"
-version = "0.3.3"
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cde1cf55178e0293453ba2cca0d5f8392a922e52aa958aee9c28ed02becc6d03"
+checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
 dependencies = [
- "libc",
+ "time-core",
 ]
 
 [[package]]
 name = "tiny_http"
-version = "0.8.2"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce51b50006056f590c9b7c3808c3bd70f0d1101666629713866c227d6e58d39"
+checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
 dependencies = [
  "ascii",
- "chrono",
  "chunked_transfer",
+ "httpdate",
  "log",
- "url",
 ]
 
 [[package]]
 name = "tinyvec"
-version = "1.5.0"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
 dependencies = [
  "tinyvec_macros",
 ]
 
 [[package]]
 name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "twoway"
@@ -1029,74 +1250,58 @@ dependencies = [
 ]
 
 [[package]]
-name = "twoway"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c57ffb460d7c24cd6eda43694110189030a3d1dfe418416d9468fd1c1d290b47"
-dependencies = [
- "memchr",
- "unchecked-index",
-]
-
-[[package]]
 name = "typed-arena"
-version = "1.7.0"
+version = "2.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
+checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
 
 [[package]]
 name = "typenum"
-version = "1.14.0"
+version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
 [[package]]
 name = "ucd-trie"
-version = "0.1.3"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
-
-[[package]]
-name = "unchecked-index"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c"
+checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
 
 [[package]]
 name = "unicase"
-version = "2.6.0"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
 dependencies = [
  "version_check",
 ]
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.7"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.19"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
 dependencies = [
  "tinyvec",
 ]
 
 [[package]]
 name = "unicode-width"
-version = "0.1.9"
+version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
 
 [[package]]
 name = "unicode_categories"
@@ -1106,17 +1311,22 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
 
 [[package]]
 name = "url"
-version = "2.2.2"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
+checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
 dependencies = [
  "form_urlencoded",
  "idna",
- "matches",
  "percent-encoding",
 ]
 
 [[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
 name = "vec_map"
 version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1124,32 +1334,79 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 
 [[package]]
 name = "version_check"
-version = "0.9.3"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "walkdir"
-version = "2.3.2"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
 dependencies = [
  "same-file",
- "winapi",
  "winapi-util",
 ]
 
 [[package]]
 name = "wasi"
-version = "0.9.0+wasi-snapshot-preview1"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
-name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
+name = "wasm-bindgen"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
 
 [[package]]
 name = "winapi"
@@ -1169,9 +1426,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
 dependencies = [
  "winapi",
 ]
@@ -1183,19 +1440,85 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
-name = "xdg"
-version = "2.3.0"
+name = "windows-core"
+version = "0.51.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de4cfc7dc9727713f386aadce9496f1ed64ea368d9f1f813a54d0f98f8741286"
+checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
 dependencies = [
- "dirs",
+ "windows-targets",
 ]
 
 [[package]]
-name = "xml-rs"
-version = "0.8.4"
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "xdg"
+version = "2.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
+checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
 
 [[package]]
 name = "yaml-rust"
diff --git a/tools/cheddar/Cargo.toml b/tools/cheddar/Cargo.toml
index 6cc8163c73..d911b7c446 100644
--- a/tools/cheddar/Cargo.toml
+++ b/tools/cheddar/Cargo.toml
@@ -6,12 +6,12 @@ edition = "2018"
 
 [dependencies]
 clap = "2.33"
-comrak = "0.10"
+comrak = "0.15"
 lazy_static = "1.4"
-rouille = "3.0"
-syntect = "4.5.0"
+rouille = "3.6"
+syntect = "5.0"
 serde_json = "1.0"
-regex = "1.4"
+regex = "1.7"
 
 [dependencies.serde]
 version = "1.0"
diff --git a/tools/cheddar/build.rs b/tools/cheddar/build.rs
index b63b2e3378..f70818d801 100644
--- a/tools/cheddar/build.rs
+++ b/tools/cheddar/build.rs
@@ -28,14 +28,19 @@ fn main() {
 
     // Otherwise ask Nix to build it and inject the result.
     let output = Command::new("nix-build")
-        .arg("-A").arg("third_party.bat_syntaxes")
+        .arg("-A")
+        .arg("third_party.bat_syntaxes")
         // ... assuming cheddar is at //tools/cheddar ...
         .arg("../..")
         .output()
         .expect(ERROR_MESSAGE);
 
     if !output.status.success() {
-        eprintln!("{}\nNix output: {}", ERROR_MESSAGE, String::from_utf8_lossy(&output.stderr));
+        eprintln!(
+            "{}\nNix output: {}",
+            ERROR_MESSAGE,
+            String::from_utf8_lossy(&output.stderr)
+        );
         return;
     }
 
diff --git a/tools/cheddar/default.nix b/tools/cheddar/default.nix
index c8d7ba5ffe..d29f75dc3c 100644
--- a/tools/cheddar/default.nix
+++ b/tools/cheddar/default.nix
@@ -3,10 +3,22 @@
 depot.third_party.naersk.buildPackage {
   src = ./.;
   doDoc = false;
+  doCheck = true;
 
   override = x: {
     # Use our custom bat syntax set, which is everything from upstream,
     # plus additional languages we care about.
     BAT_SYNTAXES = "${depot.third_party.bat_syntaxes}";
   };
+
+  passthru = {
+    # Wrapper for cgit which can't be told to pass arguments to a filter
+    about-filter = pkgs.writeShellScriptBin "cheddar-about" ''
+      exec ${depot.tools.cheddar}/bin/cheddar --about-filter $@
+    '';
+  };
+
+  meta.ci.targets = [
+    "about-filter"
+  ];
 }
diff --git a/tools/cheddar/src/bin/cheddar.rs b/tools/cheddar/src/bin/cheddar.rs
index 58ef32a1b4..73017a223d 100644
--- a/tools/cheddar/src/bin/cheddar.rs
+++ b/tools/cheddar/src/bin/cheddar.rs
@@ -5,14 +5,13 @@
 //! 2. As a long-running HTTP server that handles rendering requests
 //!    (matching the SourceGraph protocol).
 use clap::{App, Arg};
-use rouille::Response;
-use rouille::{router, try_or_400};
+use rouille::{router, try_or_400, Response};
 use serde::Deserialize;
 use serde_json::json;
 use std::collections::HashMap;
 use std::io;
 
-use cheddar::{THEMES, format_code, format_markdown};
+use cheddar::{format_code, format_markdown, THEMES};
 
 // Server endpoint for rendering the syntax of source code. This
 // replaces the 'syntect_server' component of Sourcegraph.
@@ -49,7 +48,7 @@ fn markdown_endpoint(request: &rouille::Request) -> rouille::Response {
 
     for text in texts.values_mut() {
         let mut buf: Vec<u8> = Vec::new();
-        format_markdown(&mut text.as_bytes(), &mut buf);
+        format_markdown(&mut text.as_bytes(), &mut buf, true);
         *text = String::from_utf8_lossy(&buf).to_string();
     }
 
@@ -91,6 +90,12 @@ fn main() {
                 .takes_value(false),
         )
         .arg(
+            Arg::with_name("no-tagfilter")
+                .help("Disable HTML tag filter")
+                .long("no-tagfilter")
+                .takes_value(false),
+        )
+        .arg(
             Arg::with_name("sourcegraph-server")
                 .help("Run as a Sourcegraph compatible web-server")
                 .long("sourcegraph-server")
@@ -123,7 +128,11 @@ fn main() {
     let mut out_handle = stdout.lock();
 
     if matches.is_present("about-filter") && filename.ends_with(".md") {
-        format_markdown(&mut in_handle, &mut out_handle);
+        format_markdown(
+            &mut in_handle,
+            &mut out_handle,
+            !matches.is_present("no-tagfilter"),
+        );
     } else {
         format_code(
             &THEMES.themes["InspiredGitHub"],
diff --git a/tools/cheddar/src/lib.rs b/tools/cheddar/src/lib.rs
index da0eace175..be8bc7f82f 100644
--- a/tools/cheddar/src/lib.rs
+++ b/tools/cheddar/src/lib.rs
@@ -8,13 +8,11 @@ use lazy_static::lazy_static;
 use regex::Regex;
 use std::cell::RefCell;
 use std::collections::HashMap;
-use std::env;
 use std::ffi::OsStr;
-use std::io;
-use std::io::BufRead;
-use std::io::Write;
+use std::io::{BufRead, Write};
 use std::path::Path;
-use syntect::dumps::from_binary;
+use std::{env, io};
+use syntect::dumps::from_uncompressed_data;
 use syntect::easy::HighlightLines;
 use syntect::highlighting::{Theme, ThemeSet};
 use syntect::parsing::{SyntaxReference, SyntaxSet};
@@ -35,7 +33,9 @@ lazy_static! {
     // Note that the syntax set is included from the path pointed to
     // by the BAT_SYNTAXES environment variable at compile time. This
     // variable is populated by Nix and points to TVL's syntax set.
-    static ref SYNTAXES: SyntaxSet = from_binary(include_bytes!(env!("BAT_SYNTAXES")));
+    static ref SYNTAXES: SyntaxSet = from_uncompressed_data(include_bytes!(env!("BAT_SYNTAXES")))
+            .expect("failed to deserialise SyntaxSet");
+
     pub static ref THEMES: ThemeSet = ThemeSet::load_defaults();
 
     // Configure Comrak's Markdown rendering with all the bells &
@@ -155,8 +155,11 @@ fn highlight_code_block(code_block: &NodeCodeBlock) -> NodeValue {
         let mut buf = BLOCK_PRE.to_string();
 
         for line in LinesWithEndings::from(&code) {
-            let regions = hl.highlight(line, &SYNTAXES);
-            append_highlighted_html_for_styled_line(&regions[..], IncludeBackground::No, &mut buf);
+            let regions = hl
+                .highlight_line(line, &SYNTAXES)
+                .expect("highlight_line failed");
+            append_highlighted_html_for_styled_line(&regions[..], IncludeBackground::No, &mut buf)
+                .expect("appending HTML failed");
         }
 
         buf.push_str("</pre>");
@@ -230,6 +233,7 @@ fn format_callout_paragraph(callout: Callout) -> NodeValue {
 pub fn format_markdown_with_shortlinks<R: BufRead, W: Write>(
     reader: &mut R,
     writer: &mut W,
+    tagfilter: bool,
     shortlinks: &[Shortlink],
 ) {
     let document = {
@@ -241,7 +245,13 @@ pub fn format_markdown_with_shortlinks<R: BufRead, W: Write>(
     };
 
     let arena = Arena::new();
-    let root = parse_document(&arena, &linkify_shortlinks(document, shortlinks), &MD_OPTS);
+
+    let mut opts = MD_OPTS.clone();
+    if !tagfilter {
+        opts.extension.tagfilter = false;
+    }
+
+    let root = parse_document(&arena, &linkify_shortlinks(document, shortlinks), &opts);
 
     // This node must exist with a lifetime greater than that of the parsed AST
     // in case that callouts are encountered (otherwise insertion into the tree
@@ -277,11 +287,11 @@ pub fn format_markdown_with_shortlinks<R: BufRead, W: Write>(
         }
     });
 
-    format_html(root, &MD_OPTS, writer).expect("Markdown rendering failed");
+    format_html(root, &opts, writer).expect("Markdown rendering failed");
 }
 
-pub fn format_markdown<R: BufRead, W: Write>(reader: &mut R, writer: &mut W) {
-    format_markdown_with_shortlinks(reader, writer, &TVL_LINKS)
+pub fn format_markdown<R: BufRead, W: Write>(reader: &mut R, writer: &mut W, tagfilter: bool) {
+    format_markdown_with_shortlinks(reader, writer, tagfilter, &TVL_LINKS)
 }
 
 fn find_syntax_for_file(filename: &str) -> &'static SyntaxReference {
@@ -319,13 +329,16 @@ pub fn format_code<R: BufRead, W: Write>(
     // newlines to be efficient, and those are stripped in the lines
     // iterator.
     while should_continue(&read_result) {
-        let regions = hl.highlight(&linebuf, &SYNTAXES);
+        let regions = hl
+            .highlight_line(&linebuf, &SYNTAXES)
+            .expect("highlight_line failed");
 
         append_highlighted_html_for_styled_line(
             &regions[..],
             IncludeBackground::IfDifferent(bg),
             &mut outbuf,
-        );
+        )
+        .expect("appending highlighted HTML failed");
 
         // immediately output the current state to avoid keeping
         // things in memory
diff --git a/tools/cheddar/src/tests.rs b/tools/cheddar/src/tests.rs
index c82bba6767..0550acd35c 100644
--- a/tools/cheddar/src/tests.rs
+++ b/tools/cheddar/src/tests.rs
@@ -6,7 +6,7 @@ use std::io::BufReader;
 fn expect_markdown(input: &str, expected: &str) {
     let mut input_buf = BufReader::new(input.trim().as_bytes());
     let mut out_buf: Vec<u8> = vec![];
-    format_markdown(&mut input_buf, &mut out_buf);
+    format_markdown(&mut input_buf, &mut out_buf, true);
 
     let out_string = String::from_utf8(out_buf).expect("output should be UTF8");
     assert_eq!(out_string.trim(), expected.trim());
@@ -103,3 +103,8 @@ fn highlights_multiple_shortlinks() {
 fn ignores_invalid_shortlinks() {
     expect_markdown("b/abc is not a real bug", "<p>b/abc is not a real bug</p>");
 }
+
+#[test]
+fn syntax_set_loaded() {
+    assert!(SYNTAXES.syntaxes().len() > 0)
+}
diff --git a/tools/crate2nix-generate.nix b/tools/crate2nix-generate.nix
new file mode 100644
index 0000000000..a627588ae3
--- /dev/null
+++ b/tools/crate2nix-generate.nix
@@ -0,0 +1,8 @@
+{ pkgs, depot, ... }:
+
+# Run crate2nix generate in the current working directory, then
+# format the generated file with depotfmt.
+pkgs.writeShellScriptBin "crate2nix-generate" ''
+  ${pkgs.crate2nix}/bin/crate2nix generate --all-features
+  ${depot.tools.depotfmt}/bin/depotfmt Cargo.nix
+''
diff --git a/tools/crfo-approve.nix b/tools/crfo-approve.nix
new file mode 100644
index 0000000000..d4cff9e1b2
--- /dev/null
+++ b/tools/crfo-approve.nix
@@ -0,0 +1,52 @@
+# Helper script to run a CRFO approval using depot-interventions.
+#
+# Use as 'crfo-approve $CL_ID $PATCHSET $REAL_USER $ON_BEHALF_OF'.
+#
+# Set credential in GERRIT_TOKEN envvar.
+{ pkgs, ... }:
+
+pkgs.writeShellScriptBin "crfo-approve" ''
+  set -ueo pipefail
+
+  if (($# != 4)) || [[ -z ''${GERRIT_TOKEN-} ]]; then
+    cat >&2 <<'EOF'
+  crfo-approve - Helper script to CRFO approve a TVL CL
+
+  Requires membership in depot-interventions to work.
+
+  Gerrit HTTP credential must be set in GERRIT_TOKEN envvar.
+
+  Usage:
+    crfo-approve $CL_ID $PATCHSET $REAL_USER $ON_BEHALF_OF
+  EOF
+    exit 1
+  fi
+
+  export PATH="${pkgs.lib.makeBinPath [ pkgs.httpie pkgs.jq ]}:''${PATH}"
+
+  readonly CL_ID="''${1}"
+  readonly PATCHSET="''${2}"
+  readonly REAL_USER="''${3}"
+  readonly TOKEN="''${GERRIT_TOKEN}"
+  readonly ON_BEHALF_OF="''${4}"
+  readonly URL="https://cl.tvl.fyi/a/changes/''${CL_ID}/revisions/''${PATCHSET}/review"
+
+  # First we need to find the account ID for the user
+  ACC_RESPONSE=$(http --check-status 'https://cl.tvl.fyi/accounts/' "q==name:''${ON_BEHALF_OF}" | tail -n +2)
+  ACC_LENGTH=$(echo "''${ACC_RESPONSE}" | jq 'length')
+
+  if [[ ''${ACC_LENGTH} -ne 1 ]]; then
+      echo "Did not find a unique account ID for ''${ON_BEHALF_OF}"
+      exit 1
+  fi
+
+  ACC_ID=$(jq -n --argjson response "''${ACC_RESPONSE}" '$response[0]._account_id')
+  echo "using account ID ''${ACC_ID} for ''${ON_BEHALF_OF}"
+
+  http --check-status -a "''${REAL_USER}:''${TOKEN}" POST "''${URL}" \
+    message="CRFO on behalf of ''${ON_BEHALF_OF}" \
+    'labels[Code-Review]=+2' \
+    on_behalf_of="''${ACC_ID}" \
+    "add_to_attention_set[0][user]=''${ACC_ID}" \
+    "add_to_attention_set[0][reason]=CRFO approval through depot-interventions"
+''
diff --git a/tools/depot-build.nix b/tools/depot-build.nix
deleted file mode 100644
index 62b4c7fc44..0000000000
--- a/tools/depot-build.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-# Utility script for building any arbitrary depot path in its folder.
-{ pkgs, ... }:
-
-pkgs.writeShellScriptBin "depot-build" ''
-  TARGET=$(git rev-parse --show-prefix | sed 's|/$||')
-  echo "Building //$TARGET"
-  nix-build -A $(echo $TARGET | sed 's|/|.|g') $(${pkgs.git}/bin/git rev-parse --show-toplevel)
-''
diff --git a/tools/depot-deps.nix b/tools/depot-deps.nix
new file mode 100644
index 0000000000..c2f1cd302c
--- /dev/null
+++ b/tools/depot-deps.nix
@@ -0,0 +1,35 @@
+# Shell derivation to invoke //nix/lazy-deps with the dependencies
+# that should be lazily made available in depot.
+{ pkgs, depot, ... }:
+
+depot.nix.lazy-deps {
+  age-keygen.attr = "third_party.nixpkgs.age";
+  age.attr = "third_party.nixpkgs.age";
+  depotfmt.attr = "tools.depotfmt";
+  fetch-depot-inbox.attr = "tools.fetch-depot-inbox";
+  git-r.attr = "tools.git-r";
+  gerrit-update.attr = "tools.gerrit-update";
+  gerrit.attr = "tools.gerrit-cli";
+  hash-password.attr = "tools.hash-password";
+  josh-filter.attr = "third_party.nixpkgs.josh";
+  mg.attr = "tools.magrathea";
+  nint.attr = "nix.nint";
+  niv.attr = "third_party.nixpkgs.niv";
+  rebuild-system.attr = "ops.nixos.rebuild-system";
+  rink.attr = "third_party.nixpkgs.rink";
+
+  tf-buildkite = {
+    attr = "ops.buildkite.terraform";
+    cmd = "terraform";
+  };
+
+  tf-glesys = {
+    attr = "ops.glesys.terraform";
+    cmd = "terraform";
+  };
+
+  tf-keycloak = {
+    attr = "ops.keycloak.terraform";
+    cmd = "terraform";
+  };
+}
diff --git a/tools/depot-nixpkgs-update.nix b/tools/depot-nixpkgs-update.nix
deleted file mode 100644
index 6557b2f25a..0000000000
--- a/tools/depot-nixpkgs-update.nix
+++ /dev/null
@@ -1,44 +0,0 @@
-{ pkgs, depot, ... }:
-
-let
-  inherit (depot.nix)
-    getBins
-    ;
-
-  stableRelease = "21.11";
-
-  channelsUrl = "https://channels.nixos.org";
-  archiveUrl = "https://github.com/NixOS/nixpkgs/archive/";
-
-  bins = getBins pkgs.nix [ "nix-prefetch-url" ]
-    //   getBins pkgs.curl [ "curl" ]
-    ;
-
-in
-
-pkgs.writers.writeDashBin "depot-nixpkgs-update" ''
-  set -e
-
-  printSet() {
-    setname="$1"
-    shift
-    channel="$1"
-    shift
-
-    commit="$(${bins.curl} -L "${channelsUrl}/$channel/git-revision")"
-    date="$(curl -i -L "${channelsUrl}/$channel/git-revision" \
-      | grep ^last-modified \
-      | sed 's/^last-modified: \(.\+\)\r/\1/')"
-    hash="$(${bins.nix-prefetch-url} --unpack --type sha256 "${archiveUrl}/$commit.tar.gz")"
-
-    printf '%s\n' "
-    # Tracking $channel as of $(date --rfc-3339=date --date="$date").
-    $setname = {
-      commit = \"$commit\";
-      sha256 = \"$hash\";
-    };"
-  }
-
-  printSet unstableHashes nixos-unstable
-  printSet stableHashes nixos-${stableRelease}
-''
diff --git a/tools/depot-scanner/OWNERS b/tools/depot-scanner/OWNERS
deleted file mode 100644
index cefacea4d0..0000000000
--- a/tools/depot-scanner/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherit: true
-owners:
- - riking
diff --git a/tools/depot-scanner/default.nix b/tools/depot-scanner/default.nix
deleted file mode 100644
index d18034cff0..0000000000
--- a/tools/depot-scanner/default.nix
+++ /dev/null
@@ -1,16 +0,0 @@
-{ depot, pkgs, ...}:
-
-let
-  localProto = depot.nix.buildGo.grpc {
-    name = "code.tvl.fyi/tools/depot-scanner/proto";
-    proto = ./depot_scanner.proto;
-  };
-in depot.nix.buildGo.program {
-  name = "depot-scanner";
-  srcs = [
-    ./main.go
-  ];
-  deps = [
-    localProto
-  ];
-} // { inherit localProto; }
diff --git a/tools/depot-scanner/depot_scanner.proto b/tools/depot-scanner/depot_scanner.proto
deleted file mode 100644
index 5249daebf4..0000000000
--- a/tools/depot-scanner/depot_scanner.proto
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2020 TVL
-// SPDX-License-Identifier: MIT
-
-syntax = "proto3";
-package tvl.tools.depot_scanner;
-option go_package = "code.tvl.fyi/tools/depot-scanner/proto";
-
-enum PathType {
-  UNKNOWN = 0;
-  DEPOT = 1;
-  STORE = 2;
-  CORE = 3;
-}
-
-message ScanRequest {
-  // Which revision of the depot
-  string revision = 1;
-  string attr = 2;
-  // Optionally, the attr to evaluate can be provided as a path to a folder or a
-  // .nix file.  This is used by the HTTP service.
-  string attrAsPath = 3;
-}
-
-message ScanResponse {
-  repeated string depotPath = 1;
-  repeated string nixStorePath = 2;
-  repeated string corePkgsPath = 4;
-  repeated string otherPath = 3;
-
-  bytes derivation = 5;
-}
-
-message ArchiveRequest {
-  repeated string depotPath = 1;
-}
-
-message ArchiveChunk {
-  bytes chunk = 1;
-}
-
-service DepotScanService {
-  rpc Scan(ScanRequest) returns (ScanResponse);
-
-  rpc MakeArchive(ArchiveRequest) returns (stream ArchiveChunk);
-}
-
diff --git a/tools/depot-scanner/go.mod b/tools/depot-scanner/go.mod
deleted file mode 100644
index bdd22fc1ef..0000000000
--- a/tools/depot-scanner/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module code.tvl.fyi/tools/depot-scanner
-
-go 1.14
diff --git a/tools/depot-scanner/main.go b/tools/depot-scanner/main.go
deleted file mode 100644
index 9171587be2..0000000000
--- a/tools/depot-scanner/main.go
+++ /dev/null
@@ -1,227 +0,0 @@
-package main
-
-import (
-	"bufio"
-	"flag"
-	"fmt"
-	"io"
-	"os"
-	"os/exec"
-	"strings"
-
-	pb "code.tvl.fyi/tools/depot-scanner/proto"
-)
-
-var nixInstantiatePath = flag.String("nix-bin", "/run/current-system/sw/bin/nix-instantiate", "path to nix-instantiate")
-var depotRoot = flag.String("depot", envOr("DEPOT_ROOT", "/depot/"), "path to tvl.fyi depot at current canon")
-var nixStoreRoot = flag.String("store-path", "/nix/store/", "prefix for all valid nix store paths")
-
-var modeFlag = flag.String("mode", modeArchive, "operation mode. valid values: tar, print")
-var onlyFlag = flag.String("only", "", "only enable the listed output types, comma separated. valid values: DEPOT, STORE, CORE, UNKNOWN")
-var relativeFlag = flag.Bool("relpath", false, "when printing paths, print them relative to the root of their path type")
-
-const (
-	modeArchive = "tar"
-	modePrint   = "print"
-)
-
-const (
-	// String that identifies a path as belonging to nix corepkgs.
-	corePkgsString = "/share/nix/corepkgs/"
-
-	depotTraceString = "trace: depot-scan: "
-)
-
-type fileScanType int
-
-const (
-	unknownPath fileScanType = iota
-	depotPath
-	nixStorePath
-	corePkgsPath
-)
-
-func launchNix(attr string) (*exec.Cmd, io.ReadCloser, io.ReadCloser, error) {
-	cmd := exec.Command(*nixInstantiatePath, "--trace-file-access", "-A", attr)
-	stdout, err := cmd.StdoutPipe()
-	if err != nil {
-		return nil, nil, nil, err
-	}
-	stderr, err := cmd.StderrPipe()
-	if err != nil {
-		stdout.Close()
-		return nil, nil, nil, err
-	}
-
-	err = cmd.Start()
-	if err != nil {
-		stdout.Close()
-		stderr.Close()
-		return nil, nil, nil, err
-	}
-
-	return cmd, stdout, stderr, nil
-}
-
-func categorizePath(path string) fileScanType {
-	if strings.HasPrefix(path, *nixStoreRoot) {
-		if strings.Contains(path, corePkgsString) {
-			return corePkgsPath
-		}
-		return nixStorePath
-	} else if strings.HasPrefix(path, *depotRoot) {
-		return depotPath
-	} else if strings.Contains(path, corePkgsString) {
-		return corePkgsPath
-	}
-	return unknownPath
-}
-
-func addPath(path string, out map[fileScanType]map[string]struct{}) {
-	cat := categorizePath(path)
-	if out[cat] == nil {
-		out[cat] = make(map[string]struct{})
-	}
-
-	out[cat][path] = struct{}{}
-}
-
-func consumeOutput(stdout, stderr io.ReadCloser) (map[fileScanType]map[string]struct{}, string, error) {
-	result := make(map[fileScanType]map[string]struct{})
-
-	scanner := bufio.NewScanner(stderr)
-	for scanner.Scan() {
-		line := scanner.Text()
-		if strings.HasPrefix(line, depotTraceString) {
-			addPath(strings.TrimPrefix(line, depotTraceString), result)
-		} else {
-			// print remaining stderr output of nix-instantiate
-			// to prevent silent swallowing of possible important
-			// error messages (e.g. about command line interface changes)
-			fmt.Fprintf(os.Stderr, "nix-inst> %s\n", line)
-		}
-	}
-	if scanner.Err() != nil {
-		return nil, "", scanner.Err()
-	}
-
-	// Get derivation path
-	derivPath := ""
-	scanner = bufio.NewScanner(stdout)
-	for scanner.Scan() {
-		line := scanner.Text()
-		if strings.HasPrefix(line, *nixStoreRoot) {
-			derivPath = line
-			// consume the rest of the output
-		}
-	}
-	if scanner.Err() != nil {
-		return nil, "", scanner.Err()
-	}
-
-	return result, derivPath, nil
-}
-
-func main() {
-	flag.Parse()
-
-	checkDepotRoot()
-
-	enabledPathTypes := make(map[pb.PathType]bool, 4)
-	if len(*onlyFlag) > 0 {
-		enabledOutputs := strings.Split(*onlyFlag, ",")
-		for _, v := range enabledOutputs {
-			i, ok := pb.PathType_value[strings.ToUpper(v)]
-			if !ok {
-				fmt.Fprintln(os.Stderr, "warning: unrecognized PathType name: ", v)
-				continue
-			}
-			enabledPathTypes[pb.PathType(i)] = true
-		}
-	} else {
-		// Default
-		enabledPathTypes = map[pb.PathType]bool{
-			pb.PathType_UNKNOWN: true,
-			pb.PathType_DEPOT:   true,
-			pb.PathType_STORE:   true,
-			pb.PathType_CORE:    true,
-		}
-	}
-
-	cmd, stdout, stderr, err := launchNix(flag.Arg(0))
-	if err != nil {
-		panic(fmt.Errorf("could not launch nix: %w", err))
-	}
-	results, derivPath, err := consumeOutput(stdout, stderr)
-	if err != nil {
-		err2 := cmd.Wait()
-		if err2 != nil {
-			panic(fmt.Errorf("nix-instantiate failed: %w\nadditionally, while reading output: %w", err2, err))
-		}
-		panic(fmt.Errorf("problem reading nix output: %w", err))
-	}
-	err = cmd.Wait()
-	if err != nil {
-		panic(fmt.Errorf("nix-instantiate failed: %w", err))
-	}
-
-	_ = derivPath
-
-	if *modeFlag == "print" {
-		if enabledPathTypes[pb.PathType_STORE] {
-			for k, _ := range results[nixStorePath] {
-				if *relativeFlag {
-					k = strings.TrimPrefix(k, *nixStoreRoot)
-					k = strings.TrimPrefix(k, "/")
-				}
-				fmt.Println(k)
-			}
-		}
-		if enabledPathTypes[pb.PathType_DEPOT] {
-			for k, _ := range results[depotPath] {
-				if *relativeFlag {
-					k = strings.TrimPrefix(k, *depotRoot)
-					k = strings.TrimPrefix(k, "/")
-				}
-				fmt.Println(k)
-			}
-		}
-		if enabledPathTypes[pb.PathType_CORE] {
-			for k, _ := range results[corePkgsPath] {
-				// TODO relativeFlag
-				fmt.Println(k)
-			}
-		}
-		if enabledPathTypes[pb.PathType_UNKNOWN] {
-			for k, _ := range results[unknownPath] {
-				fmt.Println(k)
-			}
-		}
-	} else {
-		panic("unimplemented")
-	}
-}
-
-func envOr(envVar, def string) string {
-	v := os.Getenv(envVar)
-	if v == "" {
-		return def
-	}
-	return v
-}
-
-func checkDepotRoot() {
-	if *depotRoot == "" {
-		fmt.Fprintln(os.Stderr, "error: DEPOT_ROOT / -depot not set")
-		os.Exit(2)
-	}
-	_, err := os.Stat(*depotRoot)
-	if os.IsNotExist(err) {
-		fmt.Fprintf(os.Stderr, "error: %q does not exist\ndid you forget to set DEPOT_ROOT / --depot ?\n", *depotRoot)
-		os.Exit(1)
-	} else if err != nil {
-		fmt.Fprintf(os.Stderr, "error: could not stat %q: %v\n", *depotRoot, err)
-		os.Exit(1)
-	}
-
-}
diff --git a/tools/depotfmt.nix b/tools/depotfmt.nix
index 8c8e99c089..706b7c05a5 100644
--- a/tools/depotfmt.nix
+++ b/tools/depotfmt.nix
@@ -11,13 +11,27 @@ let
 
   config = pkgs.writeText "depot-treefmt-config" ''
     [formatter.go]
-    command = "${pkgs.go}/bin/gofmt"
+    command = "${depot.nix.buildGo.go}/bin/gofmt"
     options = [ "-w" ]
     includes = ["*.go"]
 
     [formatter.tf]
     command = "${terraformat}"
     includes = [ "*.tf" ]
+
+    [formatter.nix]
+    command = "${pkgs.nixpkgs-fmt}/bin/nixpkgs-fmt"
+    includes = [ "*.nix" ]
+    excludes = [
+      "tvix/eval/src/tests/nix_tests/*",
+    ]
+
+    [formatter.rust]
+    command = "${pkgs.rustfmt}/bin/rustfmt"
+    includes = [ "*.rs" ]
+    excludes = [
+      "users/tazjin/*",
+    ]
   '';
 
   # helper tool for formatting the depot interactively
@@ -30,11 +44,13 @@ let
   # wrapper script for running formatting checks in CI
   check = pkgs.writeShellScript "depotfmt-check" ''
     ${pkgs.treefmt}/bin/treefmt \
+      --clear-cache \
       --fail-on-change \
       --config-file ${config} \
       --tree-root .
   '';
-in depotfmt.overrideAttrs(_: {
+in
+depotfmt.overrideAttrs (_: {
   passthru.meta.ci.extraSteps.check = {
     label = "depot formatting check";
     command = check;
diff --git a/tools/emacs-pkgs/FSF_OWNERS b/tools/emacs-pkgs/FSF_OWNERS
new file mode 100644
index 0000000000..32a278ca74
--- /dev/null
+++ b/tools/emacs-pkgs/FSF_OWNERS
@@ -0,0 +1,6 @@
+# Users with approval powers for code that requires FSF copyright
+# assignment. Users added here should have FSF paperwork on file, and
+# should - if changes to a covered project are made - verify that the
+# committers also have done the paperwork.
+
+tazjin
diff --git a/tools/emacs-pkgs/buildEmacsPackage.nix b/tools/emacs-pkgs/buildEmacsPackage.nix
index 160c062613..990b53b763 100644
--- a/tools/emacs-pkgs/buildEmacsPackage.nix
+++ b/tools/emacs-pkgs/buildEmacsPackage.nix
@@ -16,19 +16,23 @@
 
 buildArgs:
 
-pkgs.callPackage({ emacsPackages }:
+pkgs.callPackage
+  ({ emacsPackages }:
 
-let
-  # Select external dependencies from the emacsPackages set
-  externalDeps = (buildArgs.externalRequires or (_: [])) emacsPackages;
+  let
+    # Select external dependencies from the emacsPackages set
+    externalDeps = (buildArgs.externalRequires or (_: [ ])) emacsPackages;
 
-  # Override emacsPackages for depot-internal packages
-  internalDeps = map (p: p.override { inherit emacsPackages; })
-                     (buildArgs.internalRequires or []);
+    # Override emacsPackages for depot-internal packages
+    internalDeps = map (p: p.override { inherit emacsPackages; })
+      (buildArgs.internalRequires or [ ]);
 
-  trivialBuildArgs = builtins.removeAttrs buildArgs [
-    "externalRequires" "internalRequires"
-  ] // {
-    packageRequires = externalDeps ++ internalDeps;
-  };
-in emacsPackages.trivialBuild trivialBuildArgs) {}
+    trivialBuildArgs = builtins.removeAttrs buildArgs [
+      "externalRequires"
+      "internalRequires"
+    ] // {
+      packageRequires = externalDeps ++ internalDeps;
+    };
+  in
+  emacsPackages.trivialBuild trivialBuildArgs)
+{ }
diff --git a/tools/emacs-pkgs/notable/OWNERS b/tools/emacs-pkgs/notable/OWNERS
index f7da62ecf7..45c9222313 100644
--- a/tools/emacs-pkgs/notable/OWNERS
+++ b/tools/emacs-pkgs/notable/OWNERS
@@ -1,2 +1 @@
-owners:
-  - tazjin
+tazjin
diff --git a/tools/emacs-pkgs/notable/default.nix b/tools/emacs-pkgs/notable/default.nix
index 8c6935fe88..f57b1c66ae 100644
--- a/tools/emacs-pkgs/notable/default.nix
+++ b/tools/emacs-pkgs/notable/default.nix
@@ -6,7 +6,9 @@ depot.tools.emacs-pkgs.buildEmacsPackage rec {
   src = ./notable.el;
 
   externalRequires = epkgs: with epkgs; [
-    f ht s
+    f
+    ht
+    s
   ];
 
   internalRequires = [
diff --git a/tools/emacs-pkgs/passively/OWNERS b/tools/emacs-pkgs/passively/OWNERS
index 56853aed59..45c9222313 100644
--- a/tools/emacs-pkgs/passively/OWNERS
+++ b/tools/emacs-pkgs/passively/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - tazjin
+tazjin
diff --git a/tools/emacs-pkgs/passively/README.md b/tools/emacs-pkgs/passively/README.md
index 052c496b32..a5ac0d5a40 100644
--- a/tools/emacs-pkgs/passively/README.md
+++ b/tools/emacs-pkgs/passively/README.md
@@ -68,7 +68,7 @@ can clone passively like this:
 
 Passively depends on `ht.el`.
 
-Feel free to contribute patches by emailing them to `depot@tazj.in`
+Feel free to contribute patches by emailing them to `depot@tvl.su`.
 
 ## Use-cases
 
diff --git a/tools/emacs-pkgs/term-switcher/term-switcher.el b/tools/emacs-pkgs/term-switcher/term-switcher.el
index 0055f87fd6..c141a5e9cc 100644
--- a/tools/emacs-pkgs/term-switcher/term-switcher.el
+++ b/tools/emacs-pkgs/term-switcher/term-switcher.el
@@ -1,19 +1,20 @@
 ;;; term-switcher.el --- Easily switch between open vterms
 ;;
-;; Copyright (C) 2019 Google Inc.
+;; Copyright (C) 2019-2020 Google Inc.
+;; Copyright (C) 2021-2023 The TVL Authors
 ;;
-;; Author: Vincent Ambo <tazjin@google.com>
+;; Author: Vincent Ambo <tazjin@tvl.su>
 ;; Version: 1.1
-;; Package-Requires: (dash ivy s vterm)
+;; Package-Requires: (ivy s vterm)
 ;;
 ;;; Commentary:
 ;;
 ;; This package adds a function that lets users quickly switch between
 ;; different open vterms via ivy.
 
-(require 'dash)
 (require 'ivy)
 (require 's)
+(require 'seq)
 (require 'vterm)
 
 (defgroup term-switcher nil
@@ -26,14 +27,18 @@
   :type '(string)
   :group 'term-switcher)
 
-(defun ts/open-or-create-vterm (buffer-name)
-  "Switch to the buffer with BUFFER-NAME or create a new vterm
-  buffer."
-  (if (equal "New vterm" buffer-name)
-      (vterm)
-    (if-let ((buffer (get-buffer buffer-name)))
-        (switch-to-buffer buffer)
-      (error "Could not find vterm buffer: %s" buffer-name))))
+(defun ts/create-vterm ()
+  "Launch vterm, but don't open semi-broken vterms over TRAMP."
+  (if (file-remote-p default-directory)
+      (let ((default-directory "~"))
+        (vterm))
+    (vterm)))
+
+(defun ts/open-or-create-vterm (buffer)
+  "Switch to the terminal in BUFFER, or create a new one if buffer is nil."
+  (if buffer
+      (switch-to-buffer buffer)
+    (ts/create-vterm)))
 
 (defun ts/is-vterm-buffer (buffer)
   "Determine whether BUFFER runs a vterm."
@@ -43,15 +48,16 @@
   "Switch to an existing vterm buffer or create a new one."
 
   (interactive)
-  (let ((terms (-map #'buffer-name
-                     (-filter #'ts/is-vterm-buffer (buffer-list)))))
+  (let ((terms (seq-map (lambda (b) (cons (buffer-name b) b))
+                        (seq-filter #'ts/is-vterm-buffer (buffer-list)))))
     (if terms
         (ivy-read "Switch to vterm: "
-                  (cons "New vterm" terms)
+                  (cons "New vterm" (seq-map #'car terms))
                   :caller 'ts/switch-to-terminal
                   :preselect (s-concat "^" term-switcher-buffer-prefix)
                   :require-match t
-                  :action #'ts/open-or-create-vterm)
-      (vterm))))
+                  :action (lambda (match)
+                            (ts/open-or-create-vterm (cdr (assoc match terms)))))
+      (ts/create-vterm))))
 
 (provide 'term-switcher)
diff --git a/tools/emacs-pkgs/treecrumbs/OWNERS b/tools/emacs-pkgs/treecrumbs/OWNERS
new file mode 100644
index 0000000000..6049a23634
--- /dev/null
+++ b/tools/emacs-pkgs/treecrumbs/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+file:/tools/emacs-pkgs/FSF_OWNERS
diff --git a/tools/emacs-pkgs/treecrumbs/default.nix b/tools/emacs-pkgs/treecrumbs/default.nix
new file mode 100644
index 0000000000..8895baab9a
--- /dev/null
+++ b/tools/emacs-pkgs/treecrumbs/default.nix
@@ -0,0 +1,7 @@
+{ depot, ... }:
+
+depot.tools.emacs-pkgs.buildEmacsPackage {
+  pname = "treecrumbs";
+  version = "1.0";
+  src = ./treecrumbs.el;
+}
diff --git a/tools/emacs-pkgs/treecrumbs/treecrumbs.el b/tools/emacs-pkgs/treecrumbs/treecrumbs.el
new file mode 100644
index 0000000000..cd49324ad7
--- /dev/null
+++ b/tools/emacs-pkgs/treecrumbs/treecrumbs.el
@@ -0,0 +1,202 @@
+;; treecrumbs.el --- Fast, tree-sitter based breadcrumbs  -*- lexical-binding: t; -*-
+;;
+;; Copyright (C) Free Software Foundation, Inc.
+;; SPDX-License-Identifier: GPL-3.0-or-later
+;;
+;; Author: Vincent Ambo <tazjin@tvl.su>
+;; Created: 2024-03-08
+;; Version: 1.0
+;; Keywords: convenience
+;; Package-Requires: ((emacs "29.1"))
+;; URL: https://code.tvl.fyi/tree/tools/emacs-pkgs/treecrumbs
+;;
+;; This file is not (yet) part of GNU Emacs.
+
+;;; Commentary:
+
+;; This package provides a tree-sitter based implementation of "breadcrumbs",
+;; that is indicators displaying where in the semantic structure of a document
+;; the point is currently located.
+;;
+;; Imagine a large YAML-document where the names of the parent keys are far out
+;; of view: Treecrumbs can quickly display the hierarchy of keys (e.g. `foo < []
+;; < baz') and help figure out where point is.
+;;
+;; Treecrumbs only works if a tree-sitter parser for the target language is
+;; available in the buffer, and the language is supported in the
+;; `treecrumbs-languages'. Adding a new language is not difficult, and patches
+;; for this are welcome.
+;;
+;; To active treecrumbs, enable `treecrumbs-mode'. This buffer-local minor mode
+;; adds the crumbs to the buffer's `header-line-format'. Alternatively, users
+;; can also use the `treecrumbs-line-segment' either in their own header-line,
+;; tab-line or mode-line configuration.
+
+;;; Code:
+
+(require 'seq)
+(require 'treesit)
+
+(defvar treecrumbs-languages nil
+  "Describes the tree-sitter language grammars supported by
+treecrumbs, and how the breadcrumbs for their node types are
+generated.
+
+Alist of symbols representing tree-sitter languages (e.g. `yaml')
+to another alist (the \"node type list\") describing how
+different node types should be displayed in the crumbs.
+
+See `define-treecrumbs-language' for more details on how to add a
+language.")
+
+(defmacro define-treecrumbs-language (lang &rest clauses)
+  "Defines a new language for use in treecrumbs. LANG should be a
+symbol representing the language as understood by treesit (e.g.
+`yaml').
+
+Each of CLAUSES is a cons cell mapping the name of a tree
+node (in string format) to one of either:
+
+1. a static string, which will become the breadcrumb verbatim
+
+2. a tree-sitter query (in S-expression syntax) which must capture
+   exactly one argument named `@key' that will become the
+   breadcrumb (e.g. the name of a function, the key in a map, ...)
+
+Treecrumbs will only consider node types that are mentioned in
+CLAUSES. All other nodes are ignored when constructing the
+crumbs.
+
+The defined languages are stored in `treecrumbs-languages'."
+
+  (declare (indent 1))
+  (let ((compiled
+         (seq-map (lambda (clause)
+                    (if (stringp (cdr clause))
+                        `(cons ,(car clause) ,(cdr clause))
+                      `(cons ,(car clause)
+                             (treesit-query-compile ',lang ',(cdr clause)))))
+                  clauses)))
+    `(setf (alist-get ',lang treecrumbs-languages nil nil #'equal) (list ,@compiled))))
+
+(define-treecrumbs-language yaml
+  ;; In YAML documents, crumbs are generated from the keys of maps, and from
+  ;; elements of arrays. "block"-nodes are standard YAML syntax, "flow"-nodes
+  ;; are inline JSON-ish syntax.
+  ("block_mapping_pair" . ((block_mapping_pair key: (_) @key)))
+  ("block_sequence" . "[]")
+
+  ;; TODO: Why can this query not match on to (flow_pair)?
+  ("flow_pair" . ((_) key: (_) @key))
+  ("flow_sequence" . "[]"))
+
+(define-treecrumbs-language json
+  ;; In JSON documents, crumbs are generated from key names and array fields.
+  ("pair" . ((pair key: (string (string_content) @key))))
+  ("array" . "[]"))
+
+(define-treecrumbs-language toml
+  ;; TOML has sections, key names and arrays. Sections are the only
+  ;; relevant difference to YAML. Nested keys are not parsed, and just
+  ;; displayed as-is.
+  ("table" . ((table (_) @key)) )
+  ;; TODO: query cannot match on pair in inline_table, hence matching
+  ;; directly on keys
+  ("pair" . ([(dotted_key)
+              (quoted_key)
+              (bare_key)]))
+  ("array" . "[]"))
+
+(define-treecrumbs-language cpp
+  ;; In C++ files, crumbs are generated from namespaces and
+  ;; identifier declarations.
+  ("namespace_definition" . ([(namespace_definition
+                               name: (namespace_identifier) @key)
+                              (namespace_definition
+                               "namespace" @key
+                               !name)]))
+
+  ("function_definition" . ((function_definition
+                             declarator:
+                             (function_declarator
+                              declarator: (_) @key))))
+
+  ("class_specifier" . ((class_specifier
+                         name: (type_identifier) @key)))
+
+  ("struct_specifier" . ((struct_specifier
+                          name: (type_identifier) @key)))
+
+  ("field_declaration" . ((field_declaration
+                           declarator: (_) @key)))
+
+  ("init_declarator" . ((init_declarator
+                         declarator: (_) @key))))
+
+(defvar-local treecrumbs--current-crumbs nil
+  "Current crumbs to display in the header line. Only updated when
+the node under point changes.")
+
+(defun treecrumbs--crumbs-for (node)
+  "Construct the crumbs for the given NODE, if its language is
+supported in `treecrumbs-languages'. This functions return value
+is undefined, it directly updates the buffer-local
+`treecrumbs--current-crumbs'."
+  (let ((lang (cdr (assoc (treesit-node-language node) treecrumbs-languages))))
+    (unless lang
+      (user-error "No supported treecrumbs language at point!"))
+
+    (setq-local treecrumbs--current-crumbs "")
+    (treesit-parent-while
+     node
+     (lambda (parent)
+       (when-let ((query (cdr (assoc (treesit-node-type parent) lang))))
+
+         (setq-local treecrumbs--current-crumbs
+                     (concat treecrumbs--current-crumbs
+                             (if (string-empty-p treecrumbs--current-crumbs) ""
+                               " < ")
+
+                             (if (stringp query)
+                                 query
+                               (substring-no-properties
+                                (treesit-node-text (cdar (treesit-query-capture parent query))))))))
+       t))))
+
+
+(defvar-local treecrumbs--last-node nil
+  "Caches the node that was last seen at point.")
+
+(defun treecrumbs-at-point ()
+  "Returns the treecrumbs at point as a string, if point is on a
+node in a language supported in `treecrumbs-languages'.
+
+The last known crumbs in a given buffer are cached, and only if
+the node under point changes are they updated."
+  (let ((node (treesit-node-at (point))))
+    (when (or (not treecrumbs--current-crumbs)
+              (not (equal treecrumbs--last-node node)))
+      (setq-local treecrumbs--last-node node)
+      (treecrumbs--crumbs-for node)))
+
+  treecrumbs--current-crumbs)
+
+(defvar treecrumbs-line-segment
+  '(:eval (treecrumbs-at-point))
+
+  "Treecrumbs segment for use in the header-line or mode-line.")
+
+;;;###autoload
+(define-minor-mode treecrumbs-mode
+  "Display header line hints about current position in structure."
+  :init-value nil
+  :lighter " Crumbs"
+  (if treecrumbs-mode
+      (if (treesit-parser-list)
+          (push treecrumbs-line-segment header-line-format)
+        (user-error "Treecrumbs mode works only in tree-sitter based buffers!"))
+    (setq header-line-format
+          (delq treecrumbs-line-segment header-line-format))))
+
+(provide 'treecrumbs)
+;;; treecrumbs.el ends here
diff --git a/tools/emacs-pkgs/tvl/OWNERS b/tools/emacs-pkgs/tvl/OWNERS
index ce7e0e37ee..b381c4e660 100644
--- a/tools/emacs-pkgs/tvl/OWNERS
+++ b/tools/emacs-pkgs/tvl/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - grfn
+aspen
diff --git a/tools/emacs-pkgs/tvl/tvl.el b/tools/emacs-pkgs/tvl/tvl.el
index 500ffa1653..8db718a835 100644
--- a/tools/emacs-pkgs/tvl/tvl.el
+++ b/tools/emacs-pkgs/tvl/tvl.el
@@ -104,6 +104,19 @@ passes. This is potentially dangerous, use with care."
   #'magit-push ["r"]
   (list "P" "push & rubberstamp to Gerrit" #'magit-gerrit-rubberstamp))
 
+(transient-define-suffix magit-gerrit-push-private ()
+  "Push a private change to Gerrit."
+  (interactive)
+  (magit-push-refspecs tvl-gerrit-remote
+                       (tvl--gerrit-ref tvl-target-branch
+                                        '("private"
+                                          "publish-comments"))
+                       nil))
+
+(transient-append-suffix
+  #'magit-push ["r"]
+  (list "Q" "push private change to Gerrit" #'magit-gerrit-push-private))
+
 (defvar magit-cl-history nil)
 (defun magit-read-cl (prompt remote)
   (let* ((refs (prog2 (message "Determining available refs...")
@@ -186,7 +199,7 @@ passes. This is potentially dangerous, use with care."
   (magit-status-setup-buffer tvl-depot-path))
 
 (eval-after-load 'sly
-  '(defun tvl-sly-from-depot (attribute)
+  '(defun tvl-sly-from-depot (attribute only-deps)
      "Start a Sly REPL configured with a Lisp matching a derivation
      from the depot.
 
@@ -194,12 +207,21 @@ passes. This is potentially dangerous, use with care."
      asynchronously. The build output is included in the error
      thrown on build failures."
 
-     (interactive "sAttribute: ")
+     ;; TODO(sterni): this function asumes that we are using SBCL
+     ;;               - for determining the resulting wrapper's location
+     ;;               - for creating the dep-only wrapper
+
+     (interactive (list (read-string "Attribute: ")
+                        (yes-or-no-p "Only include dependencies? ")))
      (lexical-let* ((outbuf (get-buffer-create (format "*depot-out/%s*" attribute)))
                     (errbuf (get-buffer-create (format "*depot-errors/%s*" attribute)))
-                    (expression (format "(import <depot> {}).%s.repl" attribute))
+                    (attr-display (if only-deps attribute (format "dependencies of %s" attribute)))
+                    (expression (if only-deps
+                                    (format "let d = import <depot> {}; in d.nix.buildLisp.sbcl.lispWith d.%s.lispDeps"
+                                            attribute)
+                                    (format "(import <depot> {}).%s.repl" attribute)))
                     (command (list "nix-build" "--no-out-link" "-I" (format "depot=%s" tvl-depot-path) "-E" expression)))
-       (message "Acquiring Lisp for <depot>.%s" attribute)
+       (message "Acquiring Lisp for <depot>.%s" attr-display)
        (make-process :name (format "depot-nix-build/%s" attribute)
                      :buffer outbuf
                      :stderr errbuf
@@ -211,10 +233,10 @@ passes. This is potentially dangerous, use with care."
                              ("finished\n"
                               (let* ((outpath (s-trim (with-current-buffer outbuf (buffer-string))))
                                      (lisp-path (s-concat outpath "/bin/sbcl")))
-                                (message "Acquired Lisp for <depot>.%s at %s" attribute lisp-path)
+                                (message "Acquired Lisp for <depot>.%s at %s" attr-display lisp-path)
                                 (sly lisp-path)))
                              (_ (with-current-buffer errbuf
-                                  (error "Failed to build '%s':\n%s" attribute (buffer-string)))))
+                                  (error "Failed to build %s:\nTried building '%s':\n%s" attr-display expression (buffer-string)))))
                          (kill-buffer outbuf)
                          (kill-buffer errbuf)))))))
 
diff --git a/tools/eprintf.nix b/tools/eprintf.nix
index eeacca4c8c..933d73ea71 100644
--- a/tools/eprintf.nix
+++ b/tools/eprintf.nix
@@ -3,7 +3,13 @@
 let
   bins = depot.nix.getBins pkgs.coreutils [ "printf" ];
 
-# printf(1), but redirect to stderr
-in depot.nix.writeExecline "eprintf" {} [
-  "fdmove" "-c" "1" "2" bins.printf "$@"
+  # printf(1), but redirect to stderr
+in
+depot.nix.writeExecline "eprintf" { } [
+  "fdmove"
+  "-c"
+  "1"
+  "2"
+  bins.printf
+  "$@"
 ]
diff --git a/tools/fetch-depot-inbox.nix b/tools/fetch-depot-inbox.nix
new file mode 100644
index 0000000000..e14ddf20e0
--- /dev/null
+++ b/tools/fetch-depot-inbox.nix
@@ -0,0 +1,49 @@
+# Wrapper script that uses offlineimap to fetch the depot inbox from
+# inbox.tvl.su.
+#
+# Run with the desired output directory as the only argument.
+#
+# Alternatively, users can browse the inbox on https://inbox.tvl.su
+# and interact with public-inbox in any other supported way (IMAP,
+# NNTP, git, etc.).
+{ pkgs, depot, ... }:
+
+let
+  config = pkgs.writeText "offlineimaprc" ''
+    [general]
+    accounts = depot
+
+    [Account depot]
+    localrepository = Local
+    remoterepository = Remote
+
+    [Repository Local]
+    type = Maildir
+    # localfolders set by CLI
+
+    [Repository Remote]
+    type = IMAP
+    ssl = yes
+    sslcacertfile = /etc/ssl/certs/ca-bundle.crt
+    remotehost = inbox.tvl.su
+    remoteuser = anonymous
+    remotepass = anonymous
+  '';
+in
+pkgs.writeShellScriptBin "fetch-depot-inbox" ''
+  readonly MAILDIR=''${1}
+
+  if [ -z "''${MAILDIR}" ]; then
+    echo "[inbox] must specify target maildir as the first argument!" >&2
+    exit 1
+  fi
+
+  if [ ! -d "''${MAILDIR}" ]; then
+    echo "[inbox] specified maildir must exist and be a directory!" >&2
+    exit 1
+  fi
+
+  echo "[inbox] Synchronising TVL depot inbox into ''${MAILDIR}"
+  ${pkgs.offlineimap}/bin/offlineimap -c ${config} \
+    -k "Repository_Local:localfolders=''${MAILDIR}"
+''
diff --git a/tools/git-r.nix b/tools/git-r.nix
new file mode 100644
index 0000000000..dbda330082
--- /dev/null
+++ b/tools/git-r.nix
@@ -0,0 +1,138 @@
+# Git subcommand loaded into the depot direnv via //tools/depot-deps that can
+# display the r/number for (a) given commit(s) in depot. The r/number is a
+# monotonically increasing number assigned to each commit which correspond to
+# refs/r/* as created by `//ops/pipelines/static-pipeline.yaml`. They can also
+# be used as TVL shortlinks and are supported by //web/atward.
+{ pkgs, lib, ... }:
+
+pkgs.writeTextFile {
+  name = "git-r";
+  destination = "/bin/git-r";
+  executable = true;
+  text = ''
+      set -euo pipefail
+
+      PROG_NAME="$0"
+
+      CANON_BRANCH="canon"
+      CANON_REMOTE="$(git config "branch.$CANON_BRANCH.remote" || echo "origin")"
+      CANON_HEAD="$CANON_REMOTE/$CANON_BRANCH"
+
+      usage() {
+        cat <<EOF
+    Usage: git r [-h | --usage] [<git commit> ...]
+
+      Display the r/number for the given git commit(s). If none is given,
+      HEAD is used as a default. The r/number is a monotonically increasing
+      number assigned to each commit on the $CANON_BRANCH branch in depot
+      equivalent  to the revcount ignoring merged in branches (using
+      git-rev-list(1) internally).
+
+      The r/numbers displayed by \`git r\` correspond to refs created by CI
+      in depot, so they can be used as monotonically increasing commit
+      identifiers that can be used instead of a commit hash. To have
+      \`refs/r/*\` available locally (which is not necessary for the operation
+      of \`git r\`), you may have to enable fetching them like this:
+
+          git config --add remote.origin.fetch '+refs/r/*:refs/r/*'
+
+      They are created the next time you run `git fetch origin`.
+
+    EOF
+        exit "''${1:-0}"
+      }
+
+      eprintf() {
+        printf "$@" 1>&2
+      }
+
+      revs=()
+
+      if [[ $# -le 0 ]]; then
+        revs+=("HEAD")
+      fi
+
+      for arg in "$@"; do
+        # No flags supported at the moment
+        case "$arg" in
+          # --help is mapped to `man git-r` by git(1)
+          # TODO(sterni): git-r man page
+          -h | --usage)
+            usage
+            ;;
+          -*)
+            eprintf 'error: unknown flag %s\n' "$PROG_NAME" "$arg"
+            usage 100 1>&2
+            ;;
+          *)
+            revs+=("$arg")
+            ;;
+        esac
+      done
+
+      for rev in "''${revs[@]}"; do
+        # Make sure $rev is well formed
+        git rev-parse "$rev" -- > /dev/null
+
+        if git merge-base --is-ancestor "$rev" "$CANON_HEAD"; then
+          printf 'r/'
+          git rev-list --count --first-parent "$rev"
+        else
+          eprintf 'error: refusing to calculate r/number: %s is not an ancestor of %s\n' \
+            "$rev" "$CANON_HEAD" 1>&2
+          exit 100
+        fi
+      done
+  '';
+
+  # Test case, assumes that it is executed in a checkout of depot
+  meta.ci.extraSteps.matches-refs = {
+    needsOutput = true;
+    label = "Verify `git r` output matches refs/r/*";
+    command = pkgs.writeShellScript "git-r-matches-refs" ''
+      set -euo pipefail
+
+      export PATH="${lib.makeBinPath [ pkgs.git pkgs.findutils ]}"
+      revs=("origin/canon" "origin/canon~1" "93a746aaaa092ffc3e7eb37e1df30bfd3a28435f")
+
+      failed=false
+
+      # assert_eq DESCRIPTION EXPECTED GIVEN
+      assert_eq() {
+        desc="$1"
+        exp="$2"
+        given="$3"
+
+        if [[ "$exp" != "$given" ]]; then
+          failed=true
+          printf 'error: case "%s" failed\n\texp:\t%s\n\tgot:\t%s\n' "$desc" "$exp" "$given" 1>&2
+        fi
+      }
+
+      git fetch origin '+refs/r/*:refs/r/*'
+
+      for rev in "''${revs[@]}"; do
+        assert_eq \
+          "r/number ref for $rev points at that rev" \
+          "$(git rev-parse "$rev")" \
+          "$(git rev-parse "$(./result/bin/git-r "$rev")")"
+      done
+
+      for rev in "''${revs[@]}"; do
+        assert_eq \
+          "r/number for matches ref pointing at $rev" \
+          "$(git for-each-ref --points-at="$rev" --format="%(refname:short)" 'refs/r/*')" \
+          "$(./result/bin/git-r "$rev")"
+      done
+
+      assert_eq \
+        "Passing multiple revs to git r works as expected" \
+        "$(git rev-parse "''${revs[@]}")" \
+        "$(./result/bin/git-r "''${revs[@]}" | xargs git rev-parse)"
+
+      if $failed; then
+        exit 1
+      fi
+    '';
+  };
+}
diff --git a/tools/hash-password.nix b/tools/hash-password.nix
index 9893d52178..583f1210bd 100644
--- a/tools/hash-password.nix
+++ b/tools/hash-password.nix
@@ -1,7 +1,17 @@
 # Utility for invoking slappasswd with the correct options for
 # creating an ARGON2 password hash.
+#
+# Users should generally use https://signup.tvl.fyi instead.
 { pkgs, ... }:
 
-pkgs.writeShellScriptBin "hash-password" ''
-  ${pkgs.openldap}/bin/slappasswd -o module-load=pw-argon2 -h '{ARGON2}'
-''
+let
+  script = pkgs.writeShellScriptBin "hash-password" ''
+    ${pkgs.openldap}/bin/slappasswd -o module-load=argon2 -h '{ARGON2}' "$@"
+  '';
+in
+script.overrideAttrs (old: {
+  doCheck = true;
+  checkPhase = ''
+    ${pkgs.stdenv.shell} $out/bin/hash-password -s example-password > /dev/null
+  '';
+})
diff --git a/tools/magrathea/default.nix b/tools/magrathea/default.nix
index fa0a5d89a1..5e8019852a 100644
--- a/tools/magrathea/default.nix
+++ b/tools/magrathea/default.nix
@@ -3,21 +3,37 @@
 # it is a tool for working with monorepos in the style of tvl's depot
 { pkgs, ... }:
 
-pkgs.stdenv.mkDerivation {
+let
+  inherit (pkgs)
+    stdenv
+    chicken
+    chickenPackages
+    makeWrapper
+    git
+    nix
+    lib
+    ;
+
+in
+stdenv.mkDerivation {
   name = "magrathea";
   src = ./.;
   dontInstall = true;
 
-  nativeBuildInputs = [ pkgs.chicken ];
-  buildInputs = with pkgs.chickenPackages.chickenEggs; [
+  nativeBuildInputs = [ chicken makeWrapper ];
+  buildInputs = with chickenPackages.chickenEggs; [
     matchable
     srfi-13
   ];
 
-  propagatedBuildInputs = [ pkgs.git ];
+  propagatedBuildInputs = [ git ];
 
   buildPhase = ''
     mkdir -p $out/bin
-    csc -o $out/bin/mg -static ${./mg.scm}
+    csc -o $out/bin/mg -host -static ${./mg.scm}
+  '';
+
+  fixupPhase = ''
+    wrapProgram $out/bin/mg --prefix PATH ${lib.makeBinPath [ nix ]}
   '';
 }
diff --git a/tools/magrathea/mg.scm b/tools/magrathea/mg.scm
index e5b0cb07d4..0418a94f0f 100644
--- a/tools/magrathea/mg.scm
+++ b/tools/magrathea/mg.scm
@@ -11,6 +11,8 @@
         (chicken format)
         (chicken irregex)
         (chicken port)
+        (chicken file)
+        (chicken file posix)
         (chicken process)
         (chicken process-context)
         (chicken string)
@@ -19,6 +21,8 @@
 
 (define usage #<<USAGE
 usage: mg <command> [<target>]
+       mg run [<target>] [-- <arguments>]
+       mg shell [<target>] [<command>]
 
 target:
   a target specification with meaning inside of the repository. can
@@ -37,6 +41,8 @@ commands:
   build - build a target
   shell - enter a shell with the target's build dependencies
   path  - print source folder for the target
+  repl  - start a nix repl in the repository root
+  run   - build a target and execute its output
 
 file all feedback on b.tvl.fyi
 USAGE
@@ -169,9 +175,8 @@ USAGE
       (begin
         (set! mg--repository-root
               (or (get-environment-variable "MG_ROOT")
-                  (string-chomp
-                   (call-with-input-pipe "git rev-parse --show-toplevel"
-                                         (lambda (p) (read-string #f p))))))
+                  (call-with-input-pipe "git rev-parse --show-toplevel"
+                                        (lambda (p) (read-chomping p)))))
         mg--repository-root)))
 
 ;; determine the current path relative to the root of the repository
@@ -212,36 +217,136 @@ USAGE
          [('error . message) (mg-error message)]
          [_ value]))
 
-(define (execute-build t)
-  (let ((expr (nix-expr-for t)))
-    (printf "[mg] building target ~A~%" t)
-    (process-execute "nix-build" (list "-E" expr "--show-trace"))))
+(define-record build-args target passthru unknown)
+(define (execute-build args)
+  (let ((expr (nix-expr-for (build-args-target args))))
+    (fprintf (current-error-port) "[mg] building target ~A~%" (build-args-target args))
+    (process-execute "nix-build" (append (list "-E" expr "--show-trace")
+                                         (or (build-args-passthru args) '())))))
 
-(define (build args)
+;; split the arguments used for builds into target/unknown args/nix
+;; args, where the latter occur after '--'
+(define (parse-build-args acc args)
   (match args
-         ;; simplest case: plain mg build with no target spec -> build
-         ;; the current folder's main target.
-         [() (execute-build (empty-target))]
-
-         ;; single argument should be a target spec
-         [(arg) (execute-build
-                 (guarantee-success (parse-target arg)))]
-
-         [other (print "not yet implemented")]))
-
-(define (execute-shell t)
-  (let ((expr (nix-expr-for t))
-        (user-shell (or (get-environment-variable "SHELL") "bash")))
-    (printf "[mg] entering shell for ~A~%" t)
+         ;; no arguments remaining, return accumulator as is
+         [() acc]
+
+         ;; next argument is '--' separator, split off passthru and
+         ;; return
+         [("--" . passthru)
+          (begin
+            (build-args-passthru-set! acc passthru)
+            acc)]
+
+         [(arg . rest)
+          ;; set target if not already known (and if the first
+          ;; argument does not look like an accidental unknown
+          ;; parameter)
+          (if (and (not (build-args-target acc))
+                   (not (substring=? "-" arg)))
+              (begin
+                (build-args-target-set! acc (guarantee-success (parse-target arg)))
+                (parse-build-args acc rest))
+
+              ;; otherwise, collect unknown arguments
+              (begin
+                (build-args-unknown-set! acc (append (or (build-args-unknown acc) '())
+                                                     (list arg)))
+                (parse-build-args acc rest)))]))
+
+;; parse the passed build args, applying sanity checks and defaulting
+;; the target if necessary, then execute the build
+(define (build args)
+  (let ((parsed (parse-build-args (make-build-args #f #f #f) args)))
+    ;; fail if there are unknown arguments present
+    (when (build-args-unknown parsed)
+      (let ((unknown (string-intersperse (build-args-unknown parsed))))
+        (mg-error (sprintf "unknown arguments: ~a
+
+if you meant to pass these arguments to nix, please separate them with
+'--' like so:
+
+  mg build ~a -- ~a"
+                        unknown
+                        (or (build-args-target parsed) "")
+                        unknown))))
+
+    ;; default the target to the current folder's main target
+    (unless (build-args-target parsed)
+      (build-args-target-set! parsed (empty-target)))
+
+    (execute-build parsed)))
+
+(define (execute-shell target #!optional command)
+  (if command
+      (fprintf (current-error-port) "[mg] executing ~A in shell for ~A~%"
+               command
+               target)
+      (fprintf (current-error-port) "[mg] entering shell for ~A~%" target))
+  (let ((expr (nix-expr-for target))
+        (command (or command
+                     (get-environment-variable "SHELL")
+                     "bash")))
     (process-execute "nix-shell"
-                     (list "-E" expr "--command" user-shell))))
+                     (list "-E" expr "--command" command))))
 
 (define (shell args)
   (match args
          [() (execute-shell (empty-target))]
-         [(arg) (execute-shell
-                 (guarantee-success (parse-target arg)))]
-         [other (print "not yet implemented")]))
+         [(target . args) (apply
+                           execute-shell
+                           (guarantee-success (parse-target target))
+                           args)]))
+
+(define (repl args)
+  (process-execute "nix" (append (list "repl" "--show-trace" (repository-root)) args)))
+
+(define (read-chomping pipe)
+  (let ((s (read-string #f pipe)))
+    (if (eq? s #!eof) "" (string-chomp s))))
+
+(define (execute-run t #!optional cmd-args)
+  (fprintf (current-error-port) "[mg] building target ~A~%" t)
+  (let* ((expr (nix-expr-for t))
+         (out
+          (receive (pipe _ pid)
+              ;; TODO(sterni): temporary gc root
+              (process "nix-build" (list "-E" expr "--no-out-link"))
+            (let ((stdout (read-chomping pipe)))
+              (receive (_ _ status)
+                  (process-wait pid)
+                (when (not (eq? status 0))
+                  (mg-error (format "Couldn't build target ~A" t)))
+                stdout)))))
+
+    (fprintf (current-error-port) "[mg] running target ~A~%" t)
+    (process-execute
+     ;; If the output is a file, we assume it's an executable Γ  la writeExecline,
+     ;; otherwise we look in the bin subdirectory and pick the only executable.
+     ;; Handling multiple executables is not possible at the moment, the choice
+     ;; could be made via a command line flag in the future.
+     (if (regular-file? out)
+         out
+         (let* ((dir-path (string-append out "/bin"))
+                (dir-contents (if (directory-exists? dir-path)
+                                  (directory dir-path #f)
+                                  '())))
+           (case (length dir-contents)
+             ((0) (mg-error "no executables in build output")
+                  (exit 1))
+             ((1) (string-append dir-path "/" (car dir-contents)))
+             (else (mg-error "more than one executable in build output")
+                   (exit 1)))))
+     cmd-args)))
+
+(define (run args)
+  (match args
+         [() (execute-run (empty-target))]
+         [("--" . rest) (execute-run (empty-target) rest)]
+         [(target) (execute-run (guarantee-success (parse-target target)))]
+         [(target . ("--" . rest)) (execute-run (guarantee-success (parse-target target)) rest)]
+         ;; TODO(sterni): flag for selecting binary name
+         [_ (mg-error "usage: mg run [<target>] [-- <arguments>] (hint: use \"--\" to separate the `mg run [<target>]` invocation from the arguments you're passing to the built executable)")]))
 
 (define (path args)
   (match args
@@ -262,6 +367,8 @@ USAGE
          [("build" . _) (build (cdr args))]
          [("shell" . _) (shell (cdr args))]
          [("path" . _) (path (cdr args))]
+         [("repl" . _) (repl (cdr args))]
+         [("run" . _) (run (cdr args))]
          [other (begin (print "unknown command: mg " args)
                        (print usage))]))
 
diff --git a/tools/nixery/.gitignore b/tools/nixery/.gitignore
new file mode 100644
index 0000000000..578eea3923
--- /dev/null
+++ b/tools/nixery/.gitignore
@@ -0,0 +1,12 @@
+result
+result-*
+.envrc
+debug/
+
+# Just to be sure, since we're occasionally handling test keys:
+*.pem
+*.p12
+*.json
+
+# Created by the integration test
+var-cache-nixery
diff --git a/tools/nixery/.skip-subtree b/tools/nixery/.skip-subtree
new file mode 100644
index 0000000000..4948dd56eb
--- /dev/null
+++ b/tools/nixery/.skip-subtree
@@ -0,0 +1 @@
+Imported subtree is not yet fully readTree-compatible.
diff --git a/tools/nixery/LICENSE b/tools/nixery/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/tools/nixery/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/tools/nixery/README.md b/tools/nixery/README.md
new file mode 100644
index 0000000000..a879d030b8
--- /dev/null
+++ b/tools/nixery/README.md
@@ -0,0 +1,156 @@
+<div align="center">
+  <img src="https://nixery.dev/nixery-logo.png">
+</div>
+
+-----------------
+
+[![Build status](https://badge.buildkite.com/016bff4b8ae2704a3bbbb0a250784e6692007c582983b6dea7.svg?branch=refs/heads/canon)](https://buildkite.com/tvl/depot)
+
+**Nixery** is a Docker-compatible container registry that is capable of
+transparently building and serving container images using [Nix][].
+
+Images are built on-demand based on the *image name*. Every package that the
+user intends to include in the image is specified as a path component of the
+image name.
+
+The path components refer to top-level keys in `nixpkgs` and are used to build a
+container image using a [layering strategy][] that optimises for caching popular
+and/or large dependencies.
+
+A public instance as well as additional documentation is available at
+[nixery.dev][public].
+
+You can watch the NixCon 2019 [talk about
+Nixery](https://www.youtube.com/watch?v=pOI9H4oeXqA) for more information about
+the project and its use-cases.
+
+The canonical location of the Nixery source code is
+[`//tools/nixery`][depot-link] in the [TVL](https://tvl.fyi)
+monorepository. If cloning the entire repository is not desirable, the
+Nixery subtree can be cloned like this:
+
+    git clone https://code.tvl.fyi/depot.git:/tools/nixery.git
+
+The subtree is infrequently mirrored to `tazjin/nixery` on Github.
+
+## Demo
+
+Click the image to see an example in which an image containing an interactive
+shell and GNU `hello` is downloaded.
+
+[![asciicast](https://asciinema.org/a/262583.png)](https://asciinema.org/a/262583?autoplay=1)
+
+To try it yourself, head to [nixery.dev][public]!
+
+The special meta-package `shell` provides an image base with many core
+components (such as `bash` and `coreutils`) that users commonly expect in
+interactive images.
+
+## Feature overview
+
+* Serve container images on-demand using image names as content specifications
+
+  Specify package names as path components and Nixery will create images, using
+  the most efficient caching strategy it can to share data between different
+  images.
+
+* Use private package sets from various sources
+
+  In addition to building images from the publicly available Nix/NixOS channels,
+  a private Nixery instance can be configured to serve images built from a
+  package set hosted in a custom git repository or filesystem path.
+
+  When using this feature with custom git repositories, Nixery will forward the
+  specified image tags as git references.
+
+  For example, if a company used a custom repository overlaying their packages
+  on the Nix package set, images could be built from a git tag `release-v2`:
+
+  `docker pull nixery.thecompany.website/custom-service:release-v2`
+
+* Efficient serving of image layers from Google Cloud Storage
+
+  After building an image, Nixery stores all of its layers in a GCS bucket and
+  forwards requests to retrieve layers to the bucket. This enables efficient
+  serving of layers, as well as sharing of image layers between redundant
+  instances.
+
+## Configuration
+
+Nixery supports the following configuration options, provided via environment
+variables:
+
+* `PORT`: HTTP port on which Nixery should listen
+* `NIXERY_CHANNEL`: The name of a Nix/NixOS channel to use for building
+* `NIXERY_PKGS_REPO`: URL of a git repository containing a package set (uses
+  locally configured SSH/git credentials)
+* `NIXERY_PKGS_PATH`: A local filesystem path containing a Nix package set to
+  use for building
+* `NIXERY_STORAGE_BACKEND`: The type of backend storage to use, currently
+  supported values are `gcs` (Google Cloud Storage) and `filesystem`.
+
+  For each of these additional backend configuration is necessary, see the
+  [storage section](#storage) for details.
+* `NIX_TIMEOUT`: Number of seconds that any Nix builder is allowed to run
+  (defaults to 60)
+* `NIX_POPULARITY_URL`: URL to a file containing popularity data for
+  the package set (see `popcount/`)
+
+If the `GOOGLE_APPLICATION_CREDENTIALS` environment variable is set to a service
+account key, Nixery will also use this key to create [signed URLs][] for layers
+in the storage bucket. This makes it possible to serve layers from a bucket
+without having to make them publicly available.
+
+In case the `GOOGLE_APPLICATION_CREDENTIALS` environment variable is not set, a
+redirect to storage.googleapis.com is issued, which means the underlying bucket
+objects need to be publicly accessible.
+
+### Storage
+
+Nixery supports multiple different storage backends in which its build cache and
+image layers are kept, and from which they are served.
+
+Currently the available storage backends are Google Cloud Storage and the local
+file system.
+
+In the GCS case, images are served by redirecting clients to the storage bucket.
+Layers stored on the filesystem are served straight from the local disk.
+
+These extra configuration variables must be set to configure storage backends:
+
+* `GCS_BUCKET`: Name of the Google Cloud Storage bucket to use (**required** for
+  `gcs`)
+* `GOOGLE_APPLICATION_CREDENTIALS`: Path to a GCP service account JSON key
+  (**optional** for `gcs`)
+* `STORAGE_PATH`: Path to a folder in which to store and from which to serve
+  data (**required** for `filesystem`)
+
+### Background
+
+The project started out inspired by the [buildLayeredImage][] blog post with the
+intention of becoming a Kubernetes controller that can serve declarative image
+specifications specified in CRDs as container images. The design for this was
+outlined in [a public gist][gist].
+
+## Roadmap
+
+### Kubernetes integration
+
+It should be trivial to deploy Nixery inside of a Kubernetes cluster with
+correct caching behaviour, addressing and so on.
+
+See [issue #4](https://github.com/tazjin/nixery/issues/4).
+
+### Nix-native builder
+
+The image building and layering functionality of Nixery will be extracted into a
+separate Nix function, which will make it possible to build images directly in
+Nix builds.
+
+[Nix]: https://nixos.org/
+[layering strategy]: https://tazj.in/blog/nixery-layers
+[gist]: https://gist.github.com/tazjin/08f3d37073b3590aacac424303e6f745
+[buildLayeredImage]: https://grahamc.com/blog/nix-and-layered-docker-images
+[public]: https://nixery.dev
+[depot-link]: https://cs.tvl.fyi/depot/-/tree/tools/nixery
+[gcs]: https://cloud.google.com/storage/
diff --git a/tools/nixery/builder/archive.go b/tools/nixery/builder/archive.go
new file mode 100644
index 0000000000..8763e4cb85
--- /dev/null
+++ b/tools/nixery/builder/archive.go
@@ -0,0 +1,104 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+package builder
+
+// This file implements logic for walking through a directory and creating a
+// tarball of it.
+//
+// The tarball is written straight to the supplied reader, which makes it
+// possible to create an image layer from the specified store paths, hash it and
+// upload it in one reading pass.
+import (
+	"archive/tar"
+	"compress/gzip"
+	"crypto/sha256"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+
+	"github.com/google/nixery/layers"
+)
+
+// Create a new compressed tarball from each of the paths in the list
+// and write it to the supplied writer.
+//
+// The uncompressed tarball is hashed because image manifests must
+// contain both the hashes of compressed and uncompressed layers.
+func packStorePaths(l *layers.Layer, w io.Writer) (string, error) {
+	shasum := sha256.New()
+	gz := gzip.NewWriter(w)
+	multi := io.MultiWriter(shasum, gz)
+	t := tar.NewWriter(multi)
+
+	for _, path := range l.Contents {
+		err := filepath.Walk(path, tarStorePath(t))
+		if err != nil {
+			return "", err
+		}
+	}
+
+	if err := t.Close(); err != nil {
+		return "", err
+	}
+
+	if err := gz.Close(); err != nil {
+		return "", err
+	}
+
+	return fmt.Sprintf("sha256:%x", shasum.Sum([]byte{})), nil
+}
+
+func tarStorePath(w *tar.Writer) filepath.WalkFunc {
+	return func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+
+		// If the entry is not a symlink or regular file, skip it.
+		if info.Mode()&os.ModeSymlink == 0 && !info.Mode().IsRegular() {
+			return nil
+		}
+
+		// the symlink target is read if this entry is a symlink, as it
+		// is required when creating the file header
+		var link string
+		if info.Mode()&os.ModeSymlink != 0 {
+			link, err = os.Readlink(path)
+			if err != nil {
+				return err
+			}
+		}
+
+		header, err := tar.FileInfoHeader(info, link)
+		if err != nil {
+			return err
+		}
+
+		// The name retrieved from os.FileInfo only contains the file's
+		// basename, but the full path is required within the layer
+		// tarball.
+		header.Name = path
+		if err = w.WriteHeader(header); err != nil {
+			return err
+		}
+
+		// At this point, return if no file content needs to be written
+		if !info.Mode().IsRegular() {
+			return nil
+		}
+
+		f, err := os.Open(path)
+		if err != nil {
+			return err
+		}
+
+		if _, err := io.Copy(w, f); err != nil {
+			return err
+		}
+
+		f.Close()
+
+		return nil
+	}
+}
diff --git a/tools/nixery/builder/builder.go b/tools/nixery/builder/builder.go
new file mode 100644
index 0000000000..7f0bd7fffd
--- /dev/null
+++ b/tools/nixery/builder/builder.go
@@ -0,0 +1,527 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// Package builder implements the logic for assembling container
+// images. It shells out to Nix to retrieve all required Nix-packages
+// and assemble the symlink layer and then creates the required
+// tarballs in-process.
+package builder
+
+import (
+	"bufio"
+	"bytes"
+	"compress/gzip"
+	"context"
+	"crypto/sha256"
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"sort"
+	"strings"
+
+	"github.com/google/nixery/config"
+	"github.com/google/nixery/layers"
+	"github.com/google/nixery/manifest"
+	"github.com/google/nixery/storage"
+	"github.com/im7mortal/kmutex"
+	log "github.com/sirupsen/logrus"
+)
+
+// The maximum number of layers in an image is 125. To allow for
+// extensibility, the actual number of layers Nixery is "allowed" to
+// use up is set at a lower point.
+const LayerBudget int = 94
+
+// State holds the runtime state that is carried around in Nixery and
+// passed to builder functions.
+type State struct {
+	Storage     storage.Backend
+	Cache       *LocalCache
+	Cfg         config.Config
+	Pop         layers.Popularity
+	UploadMutex *kmutex.Kmutex
+}
+
+// Architecture represents the possible CPU architectures for which
+// container images can be built.
+//
+// The default architecture is amd64, but support for ARM platforms is
+// available within nixpkgs and can be toggled via meta-packages.
+type Architecture struct {
+	// Name of the system tuple to pass to Nix
+	nixSystem string
+
+	// Name of the architecture as used in the OCI manifests
+	imageArch string
+}
+
+var amd64 = Architecture{"x86_64-linux", "amd64"}
+var arm64 = Architecture{"aarch64-linux", "arm64"}
+
+// Image represents the information necessary for building a container image.
+// This can be either a list of package names (corresponding to keys in the
+// nixpkgs set) or a Nix expression that results in a *list* of derivations.
+type Image struct {
+	Name string
+	Tag  string
+
+	// Names of packages to include in the image. These must correspond
+	// directly to top-level names of Nix packages in the nixpkgs tree.
+	Packages []string
+
+	// Architecture for which to build the image. Nixery defaults
+	// this to amd64 if not specified via meta-packages.
+	Arch *Architecture
+}
+
+// BuildResult represents the data returned from the server to the
+// HTTP handlers. Error information is propagated straight from Nix
+// for errors inside of the build that should be fed back to the
+// client (such as missing packages).
+type BuildResult struct {
+	Error    string          `json:"error"`
+	Pkgs     []string        `json:"pkgs"`
+	Manifest json.RawMessage `json:"manifest"`
+}
+
+// ImageFromName parses an image name into the corresponding structure which can
+// be used to invoke Nix.
+//
+// It will expand convenience names under the hood (see the `convenienceNames`
+// function below) and append packages that are always included (cacert, iana-etc).
+//
+// Once assembled the image structure uses a sorted representation of
+// the name. This is to avoid unnecessarily cache-busting images if
+// only the order of requested packages has changed.
+func ImageFromName(name string, tag string) Image {
+	pkgs := strings.Split(name, "/")
+	arch, expanded := metaPackages(pkgs)
+	expanded = append(expanded, "cacert", "iana-etc")
+
+	sort.Strings(pkgs)
+	sort.Strings(expanded)
+
+	return Image{
+		Name:     strings.Join(pkgs, "/"),
+		Tag:      tag,
+		Packages: expanded,
+		Arch:     arch,
+	}
+}
+
+// ImageResult represents the output of calling the Nix derivation
+// responsible for preparing an image.
+type ImageResult struct {
+	// These fields are populated in case of an error
+	Error string   `json:"error"`
+	Pkgs  []string `json:"pkgs"`
+
+	// These fields are populated in case of success
+	Graph        layers.RuntimeGraph `json:"runtimeGraph"`
+	SymlinkLayer struct {
+		Size    int    `json:"size"`
+		TarHash string `json:"tarHash"`
+		Path    string `json:"path"`
+	} `json:"symlinkLayer"`
+}
+
+// metaPackages expands package names defined by Nixery which either
+// include sets of packages or trigger certain image-building
+// behaviour.
+//
+// Meta-packages must be specified as the first packages in an image
+// name.
+//
+// Currently defined meta-packages are:
+//
+// * `shell`: Includes bash, coreutils and other common command-line tools
+// * `arm64`: Causes Nixery to build images for the ARM64 architecture
+func metaPackages(packages []string) (*Architecture, []string) {
+	arch := &amd64
+
+	var metapkgs []string
+	lastMeta := 0
+	for idx, p := range packages {
+		if p == "shell" || p == "arm64" {
+			metapkgs = append(metapkgs, p)
+			lastMeta = idx + 1
+		} else {
+			break
+		}
+	}
+
+	// Chop off the meta-packages from the front of the package
+	// list
+	packages = packages[lastMeta:]
+
+	for _, p := range metapkgs {
+		switch p {
+		case "shell":
+			packages = append(packages, "bashInteractive", "coreutils", "moreutils", "nano")
+		case "arm64":
+			arch = &arm64
+		}
+	}
+
+	return arch, packages
+}
+
+// logNix logs each output line from Nix. It runs in a goroutine per
+// output channel that should be live-logged.
+func logNix(image, cmd string, r io.ReadCloser) {
+	scanner := bufio.NewScanner(r)
+	for scanner.Scan() {
+		log.WithFields(log.Fields{
+			"image": image,
+			"cmd":   cmd,
+		}).Info("[nix] " + scanner.Text())
+	}
+}
+
+func callNix(program, image string, args []string) ([]byte, error) {
+	cmd := exec.Command(program, args...)
+
+	outpipe, err := cmd.StdoutPipe()
+	if err != nil {
+		return nil, err
+	}
+
+	errpipe, err := cmd.StderrPipe()
+	if err != nil {
+		return nil, err
+	}
+	go logNix(image, program, errpipe)
+
+	if err = cmd.Start(); err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"image": image,
+			"cmd":   program,
+		}).Error("error invoking Nix")
+
+		return nil, err
+	}
+
+	log.WithFields(log.Fields{
+		"cmd":   program,
+		"image": image,
+	}).Info("invoked Nix build")
+
+	stdout, _ := ioutil.ReadAll(outpipe)
+
+	if err = cmd.Wait(); err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"image":  image,
+			"cmd":    program,
+			"stdout": stdout,
+		}).Info("failed to invoke Nix")
+
+		return nil, err
+	}
+
+	resultFile := strings.TrimSpace(string(stdout))
+	buildOutput, err := ioutil.ReadFile(resultFile)
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"image": image,
+			"file":  resultFile,
+		}).Info("failed to read Nix result file")
+
+		return nil, err
+	}
+
+	return buildOutput, nil
+}
+
+// Call out to Nix and request metadata for the image to be built. All
+// required store paths for the image will be realised, but layers
+// will not yet be created from them.
+//
+// This function is only invoked if the manifest is not found in any
+// cache.
+func prepareImage(s *State, image *Image) (*ImageResult, error) {
+	packages, err := json.Marshal(image.Packages)
+	if err != nil {
+		return nil, err
+	}
+
+	srcType, srcArgs := s.Cfg.Pkgs.Render(image.Tag)
+
+	args := []string{
+		"--timeout", s.Cfg.Timeout,
+		"--argstr", "packages", string(packages),
+		"--argstr", "srcType", srcType,
+		"--argstr", "srcArgs", srcArgs,
+		"--argstr", "system", image.Arch.nixSystem,
+	}
+
+	output, err := callNix("nixery-prepare-image", image.Name, args)
+	if err != nil {
+		// granular error logging is performed in callNix already
+		return nil, err
+	}
+
+	log.WithFields(log.Fields{
+		"image": image.Name,
+		"tag":   image.Tag,
+	}).Info("finished image preparation via Nix")
+
+	var result ImageResult
+	err = json.Unmarshal(output, &result)
+	if err != nil {
+		return nil, err
+	}
+
+	return &result, nil
+}
+
+// Groups layers and checks whether they are present in the cache
+// already, otherwise calls out to Nix to assemble layers.
+//
+// Newly built layers are uploaded to the bucket. Cache entries are
+// added only after successful uploads, which guarantees that entries
+// retrieved from the cache are present in the bucket.
+func prepareLayers(ctx context.Context, s *State, image *Image, result *ImageResult) ([]manifest.Entry, error) {
+	grouped := layers.GroupLayers(&result.Graph, &s.Pop, LayerBudget)
+
+	var entries []manifest.Entry
+
+	// Splits the layers into those which are already present in
+	// the cache, and those that are missing.
+	//
+	// Missing layers are built and uploaded to the storage
+	// bucket.
+	for _, l := range grouped {
+		lh := l.Hash()
+
+		// While packing store paths, the SHA sum of
+		// the uncompressed layer is computed and
+		// written to `tarhash`.
+		//
+		// TODO(tazjin): Refactor this to make the
+		// flow of data cleaner.
+		lw := func(w io.Writer) (string, error) {
+			tarhash, err := packStorePaths(&l, w)
+			if err != nil {
+				return "", err
+			}
+
+			var pkgs []string
+			for _, p := range l.Contents {
+				pkgs = append(pkgs, layers.PackageFromPath(p))
+			}
+
+			log.WithFields(log.Fields{
+				"layer":    lh,
+				"packages": pkgs,
+				"tarhash":  tarhash,
+			}).Info("created image layer")
+
+			return tarhash, err
+		}
+
+		entry, err := uploadHashLayer(ctx, s, lh, l.MergeRating, lw)
+		if err != nil {
+			return nil, err
+		}
+
+		entries = append(entries, *entry)
+	}
+
+	// Symlink layer (built in the first Nix build) needs to be
+	// included here manually:
+	slkey := result.SymlinkLayer.TarHash
+	entry, err := uploadHashLayer(ctx, s, slkey, 0, func(w io.Writer) (string, error) {
+		f, err := os.Open(result.SymlinkLayer.Path)
+		if err != nil {
+			log.WithError(err).WithFields(log.Fields{
+				"image": image.Name,
+				"tag":   image.Tag,
+				"layer": slkey,
+			}).Error("failed to open symlink layer")
+
+			return "", err
+		}
+		defer f.Close()
+
+		gz := gzip.NewWriter(w)
+		_, err = io.Copy(gz, f)
+		if err != nil {
+			log.WithError(err).WithFields(log.Fields{
+				"image": image.Name,
+				"tag":   image.Tag,
+				"layer": slkey,
+			}).Error("failed to upload symlink layer")
+
+			return "", err
+		}
+
+		return "sha256:" + slkey, gz.Close()
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	entries = append(entries, *entry)
+
+	return entries, nil
+}
+
+// layerWriter is the type for functions that can write a layer to the
+// multiwriter used for uploading & hashing.
+//
+// This type exists to avoid duplication between the handling of
+// symlink layers and store path layers.
+type layerWriter func(w io.Writer) (string, error)
+
+// byteCounter is a special io.Writer that counts all bytes written to
+// it and does nothing else.
+//
+// This is required because the ad-hoc writing of tarballs leaves no
+// single place to count the final tarball size otherwise.
+type byteCounter struct {
+	count int64
+}
+
+func (b *byteCounter) Write(p []byte) (n int, err error) {
+	b.count += int64(len(p))
+	return len(p), nil
+}
+
+// Upload a layer tarball to the storage bucket, while hashing it at
+// the same time. The supplied function is expected to provide the
+// layer data to the writer.
+//
+// The initial upload is performed in a 'staging' folder, as the
+// SHA256-hash is not yet available when the upload is initiated.
+//
+// After a successful upload, the file is moved to its final location
+// in the bucket and the build cache is populated.
+//
+// The return value is the layer's SHA256 hash, which is used in the
+// image manifest.
+func uploadHashLayer(ctx context.Context, s *State, key string, mrating uint64, lw layerWriter) (*manifest.Entry, error) {
+	s.UploadMutex.Lock(key)
+	defer s.UploadMutex.Unlock(key)
+
+	if entry, cached := layerFromCache(ctx, s, key); cached {
+		return entry, nil
+	}
+
+	path := "staging/" + key
+	var tarhash string
+	sha256sum, size, err := s.Storage.Persist(ctx, path, manifest.LayerType, func(sw io.Writer) (string, int64, error) {
+		// Sets up a "multiwriter" that simultaneously runs both hash
+		// algorithms and uploads to the storage backend.
+		shasum := sha256.New()
+		counter := &byteCounter{}
+		multi := io.MultiWriter(sw, shasum, counter)
+
+		var err error
+		tarhash, err = lw(multi)
+		sha256sum := fmt.Sprintf("%x", shasum.Sum([]byte{}))
+
+		return sha256sum, counter.count, err
+	})
+
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"layer":   key,
+			"backend": s.Storage.Name(),
+		}).Error("failed to create and store layer")
+
+		return nil, err
+	}
+
+	// Hashes are now known and the object is in the bucket, what
+	// remains is to move it to the correct location and cache it.
+	err = s.Storage.Move(ctx, "staging/"+key, "layers/"+sha256sum)
+	if err != nil {
+		log.WithError(err).WithField("layer", key).
+			Error("failed to move layer from staging")
+
+		return nil, err
+	}
+
+	log.WithFields(log.Fields{
+		"layer":  key,
+		"sha256": sha256sum,
+		"size":   size,
+	}).Info("created and persisted layer")
+
+	entry := manifest.Entry{
+		Digest:      "sha256:" + sha256sum,
+		Size:        size,
+		TarHash:     tarhash,
+		MergeRating: mrating,
+	}
+
+	cacheLayer(ctx, s, key, entry)
+
+	return &entry, nil
+}
+
+func BuildImage(ctx context.Context, s *State, image *Image) (*BuildResult, error) {
+	key := s.Cfg.Pkgs.CacheKey(image.Packages, image.Tag)
+	if key != "" {
+		if m, c := manifestFromCache(ctx, s, key); c {
+			return &BuildResult{
+				Manifest: m,
+			}, nil
+		}
+	}
+
+	imageResult, err := prepareImage(s, image)
+	if err != nil {
+		return nil, err
+	}
+
+	if imageResult.Error != "" {
+		return &BuildResult{
+			Error: imageResult.Error,
+			Pkgs:  imageResult.Pkgs,
+		}, nil
+	}
+
+	layers, err := prepareLayers(ctx, s, image, imageResult)
+	if err != nil {
+		return nil, err
+	}
+
+	// If the requested packages include a shell,
+	// set cmd accordingly.
+	cmd := ""
+	for _, pkg := range image.Packages {
+		if pkg == "bashInteractive" {
+			cmd = "bash"
+		}
+	}
+	m, c := manifest.Manifest(image.Arch.imageArch, layers, cmd)
+
+	lw := func(w io.Writer) (string, error) {
+		r := bytes.NewReader(c.Config)
+		_, err := io.Copy(w, r)
+		return "", err
+	}
+
+	if _, err = uploadHashLayer(ctx, s, c.SHA256, 0, lw); err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"image": image.Name,
+			"tag":   image.Tag,
+		}).Error("failed to upload config")
+
+		return nil, err
+	}
+
+	if key != "" {
+		go cacheManifest(ctx, s, key, m)
+	}
+
+	result := BuildResult{
+		Manifest: m,
+	}
+	return &result, nil
+}
diff --git a/tools/nixery/builder/builder_test.go b/tools/nixery/builder/builder_test.go
new file mode 100644
index 0000000000..507f3eb15a
--- /dev/null
+++ b/tools/nixery/builder/builder_test.go
@@ -0,0 +1,112 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+package builder
+
+import (
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"testing"
+)
+
+var ignoreArch = cmpopts.IgnoreFields(Image{}, "Arch")
+
+func TestImageFromNameSimple(t *testing.T) {
+	image := ImageFromName("hello", "latest")
+	expected := Image{
+		Name: "hello",
+		Tag:  "latest",
+		Packages: []string{
+			"cacert",
+			"hello",
+			"iana-etc",
+		},
+	}
+
+	if diff := cmp.Diff(expected, image, ignoreArch); diff != "" {
+		t.Fatalf("Image(\"hello\", \"latest\") mismatch:\n%s", diff)
+	}
+}
+
+func TestImageFromNameMultiple(t *testing.T) {
+	image := ImageFromName("hello/git/htop", "latest")
+	expected := Image{
+		Name: "git/hello/htop",
+		Tag:  "latest",
+		Packages: []string{
+			"cacert",
+			"git",
+			"hello",
+			"htop",
+			"iana-etc",
+		},
+	}
+
+	if diff := cmp.Diff(expected, image, ignoreArch); diff != "" {
+		t.Fatalf("Image(\"hello/git/htop\", \"latest\") mismatch:\n%s", diff)
+	}
+}
+
+func TestImageFromNameShell(t *testing.T) {
+	image := ImageFromName("shell", "latest")
+	expected := Image{
+		Name: "shell",
+		Tag:  "latest",
+		Packages: []string{
+			"bashInteractive",
+			"cacert",
+			"coreutils",
+			"iana-etc",
+			"moreutils",
+			"nano",
+		},
+	}
+
+	if diff := cmp.Diff(expected, image, ignoreArch); diff != "" {
+		t.Fatalf("Image(\"shell\", \"latest\") mismatch:\n%s", diff)
+	}
+}
+
+func TestImageFromNameShellMultiple(t *testing.T) {
+	image := ImageFromName("shell/htop", "latest")
+	expected := Image{
+		Name: "htop/shell",
+		Tag:  "latest",
+		Packages: []string{
+			"bashInteractive",
+			"cacert",
+			"coreutils",
+			"htop",
+			"iana-etc",
+			"moreutils",
+			"nano",
+		},
+	}
+
+	if diff := cmp.Diff(expected, image, ignoreArch); diff != "" {
+		t.Fatalf("Image(\"shell/htop\", \"latest\") mismatch:\n%s", diff)
+	}
+}
+
+func TestImageFromNameShellArm64(t *testing.T) {
+	image := ImageFromName("shell/arm64", "latest")
+	expected := Image{
+		Name: "arm64/shell",
+		Tag:  "latest",
+		Packages: []string{
+			"bashInteractive",
+			"cacert",
+			"coreutils",
+			"iana-etc",
+			"moreutils",
+			"nano",
+		},
+	}
+
+	if diff := cmp.Diff(expected, image, ignoreArch); diff != "" {
+		t.Fatalf("Image(\"shell/arm64\", \"latest\") mismatch:\n%s", diff)
+	}
+
+	if image.Arch.imageArch != "arm64" {
+		t.Fatal("Image(\"shell/arm64\"): Expected arch arm64")
+	}
+}
diff --git a/tools/nixery/builder/cache.go b/tools/nixery/builder/cache.go
new file mode 100644
index 0000000000..9e4283c0e5
--- /dev/null
+++ b/tools/nixery/builder/cache.go
@@ -0,0 +1,225 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+package builder
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"io"
+	"io/ioutil"
+	"os"
+	"sync"
+
+	"github.com/google/nixery/manifest"
+	log "github.com/sirupsen/logrus"
+)
+
+// LocalCache implements the structure used for local caching of
+// manifests and layer uploads.
+type LocalCache struct {
+	// Manifest cache
+	mmtx sync.RWMutex
+	mdir string
+
+	// Layer cache
+	lmtx   sync.RWMutex
+	lcache map[string]manifest.Entry
+}
+
+// Creates an in-memory cache and ensures that the local file path for
+// manifest caching exists.
+func NewCache() (LocalCache, error) {
+	path := os.TempDir() + "/nixery"
+	err := os.MkdirAll(path, 0755)
+	if err != nil {
+		return LocalCache{}, err
+	}
+
+	return LocalCache{
+		mdir:   path + "/",
+		lcache: make(map[string]manifest.Entry),
+	}, nil
+}
+
+// Retrieve a cached manifest if the build is cacheable and it exists.
+func (c *LocalCache) manifestFromLocalCache(key string) (json.RawMessage, bool) {
+	c.mmtx.RLock()
+	defer c.mmtx.RUnlock()
+
+	f, err := os.Open(c.mdir + key)
+	if err != nil {
+		// This is a debug log statement because failure to
+		// read the manifest key is currently expected if it
+		// is not cached.
+		log.WithError(err).WithField("manifest", key).
+			Debug("failed to read manifest from local cache")
+
+		return nil, false
+	}
+	defer f.Close()
+
+	m, err := ioutil.ReadAll(f)
+	if err != nil {
+		log.WithError(err).WithField("manifest", key).
+			Error("failed to read manifest from local cache")
+
+		return nil, false
+	}
+
+	return json.RawMessage(m), true
+}
+
+// Adds the result of a manifest build to the local cache, if the
+// manifest is considered cacheable.
+//
+// Manifests can be quite large and are cached on disk instead of in
+// memory.
+func (c *LocalCache) localCacheManifest(key string, m json.RawMessage) {
+	c.mmtx.Lock()
+	defer c.mmtx.Unlock()
+
+	err := ioutil.WriteFile(c.mdir+key, []byte(m), 0644)
+	if err != nil {
+		log.WithError(err).WithField("manifest", key).
+			Error("failed to locally cache manifest")
+	}
+}
+
+// Retrieve a layer build from the local cache.
+func (c *LocalCache) layerFromLocalCache(key string) (*manifest.Entry, bool) {
+	c.lmtx.RLock()
+	e, ok := c.lcache[key]
+	c.lmtx.RUnlock()
+
+	return &e, ok
+}
+
+// Add a layer build result to the local cache.
+func (c *LocalCache) localCacheLayer(key string, e manifest.Entry) {
+	c.lmtx.Lock()
+	c.lcache[key] = e
+	c.lmtx.Unlock()
+}
+
+// Retrieve a manifest from the cache(s). First the local cache is
+// checked, then the storage backend.
+func manifestFromCache(ctx context.Context, s *State, key string) (json.RawMessage, bool) {
+	if m, cached := s.Cache.manifestFromLocalCache(key); cached {
+		return m, true
+	}
+
+	r, err := s.Storage.Fetch(ctx, "manifests/"+key)
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"manifest": key,
+			"backend":  s.Storage.Name(),
+		}).Error("failed to fetch manifest from cache")
+
+		return nil, false
+	}
+	defer r.Close()
+
+	m, err := ioutil.ReadAll(r)
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"manifest": key,
+			"backend":  s.Storage.Name(),
+		}).Error("failed to read cached manifest from storage backend")
+
+		return nil, false
+	}
+
+	go s.Cache.localCacheManifest(key, m)
+	log.WithField("manifest", key).Info("retrieved manifest from GCS")
+
+	return json.RawMessage(m), true
+}
+
+// Add a manifest to the bucket & local caches
+func cacheManifest(ctx context.Context, s *State, key string, m json.RawMessage) {
+	go s.Cache.localCacheManifest(key, m)
+
+	path := "manifests/" + key
+	_, size, err := s.Storage.Persist(ctx, path, manifest.ManifestType, func(w io.Writer) (string, int64, error) {
+		size, err := io.Copy(w, bytes.NewReader([]byte(m)))
+		return "", size, err
+	})
+
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"manifest": key,
+			"backend":  s.Storage.Name(),
+		}).Error("failed to cache manifest to storage backend")
+
+		return
+	}
+
+	log.WithFields(log.Fields{
+		"manifest": key,
+		"size":     size,
+		"backend":  s.Storage.Name(),
+	}).Info("cached manifest to storage backend")
+}
+
+// Retrieve a layer build from the cache, first checking the local
+// cache followed by the bucket cache.
+func layerFromCache(ctx context.Context, s *State, key string) (*manifest.Entry, bool) {
+	if entry, cached := s.Cache.layerFromLocalCache(key); cached {
+		return entry, true
+	}
+
+	r, err := s.Storage.Fetch(ctx, "builds/"+key)
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"layer":   key,
+			"backend": s.Storage.Name(),
+		}).Debug("failed to retrieve cached layer from storage backend")
+
+		return nil, false
+	}
+	defer r.Close()
+
+	jb := bytes.NewBuffer([]byte{})
+	_, err = io.Copy(jb, r)
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"layer":   key,
+			"backend": s.Storage.Name(),
+		}).Error("failed to read cached layer from storage backend")
+
+		return nil, false
+	}
+
+	var entry manifest.Entry
+	err = json.Unmarshal(jb.Bytes(), &entry)
+	if err != nil {
+		log.WithError(err).WithField("layer", key).
+			Error("failed to unmarshal cached layer")
+
+		return nil, false
+	}
+
+	go s.Cache.localCacheLayer(key, entry)
+	return &entry, true
+}
+
+func cacheLayer(ctx context.Context, s *State, key string, entry manifest.Entry) {
+	s.Cache.localCacheLayer(key, entry)
+
+	j, _ := json.Marshal(&entry)
+	path := "builds/" + key
+	_, _, err := s.Storage.Persist(ctx, path, "", func(w io.Writer) (string, int64, error) {
+		size, err := io.Copy(w, bytes.NewReader(j))
+		return "", size, err
+	})
+
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"layer":   key,
+			"backend": s.Storage.Name(),
+		}).Error("failed to cache layer")
+	}
+
+	return
+}
diff --git a/tools/nixery/cmd/server/main.go b/tools/nixery/cmd/server/main.go
new file mode 100644
index 0000000000..24aec6391c
--- /dev/null
+++ b/tools/nixery/cmd/server/main.go
@@ -0,0 +1,283 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// The nixery server implements a container registry that transparently builds
+// container images based on Nix derivations.
+//
+// The Nix derivation used for image creation is responsible for creating
+// objects that are compatible with the registry API. The targeted registry
+// protocol is currently Docker's.
+//
+// When an image is requested, the required contents are parsed out of the
+// request and a Nix-build is initiated that eventually responds with the
+// manifest as well as information linking each layer digest to a local
+// filesystem path.
+package main
+
+import (
+	"context"
+	"crypto/sha256"
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"regexp"
+
+	"github.com/google/nixery/builder"
+	"github.com/google/nixery/config"
+	"github.com/google/nixery/layers"
+	"github.com/google/nixery/logs"
+	mf "github.com/google/nixery/manifest"
+	"github.com/google/nixery/storage"
+	"github.com/im7mortal/kmutex"
+	log "github.com/sirupsen/logrus"
+)
+
+// ManifestMediaType is the Content-Type used for the manifest itself. This
+// corresponds to the "Image Manifest V2, Schema 2" described on this page:
+//
+// https://docs.docker.com/registry/spec/manifest-v2-2/
+const manifestMediaType string = "application/vnd.docker.distribution.manifest.v2+json"
+
+// This variable will be initialised during the build process and set
+// to the hash of the entire Nixery source tree.
+var version string = "devel"
+
+// Regexes matching the V2 Registry API routes. This only includes the
+// routes required for serving images, since pushing and other such
+// functionality is not available.
+var (
+	manifestRegex = regexp.MustCompile(`^/v2/([\w|\-|\.|\_|\/]+)/manifests/([\w|\-|\.|\_]+)$`)
+	blobRegex     = regexp.MustCompile(`^/v2/([\w|\-|\.|\_|\/]+)/(blobs|manifests)/sha256:(\w+)$`)
+)
+
+// Downloads the popularity information for the package set from the
+// URL specified in Nixery's configuration.
+func downloadPopularity(url string) (layers.Popularity, error) {
+	resp, err := http.Get(url)
+	if err != nil {
+		return nil, err
+	}
+
+	if resp.StatusCode != 200 {
+		return nil, fmt.Errorf("popularity download from '%s' returned status: %s\n", url, resp.Status)
+	}
+
+	j, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var pop layers.Popularity
+	err = json.Unmarshal(j, &pop)
+	if err != nil {
+		return nil, err
+	}
+
+	return pop, nil
+}
+
+// Error format corresponding to the registry protocol V2 specification. This
+// allows feeding back errors to clients in a way that can be presented to
+// users.
+type registryError struct {
+	Code    string `json:"code"`
+	Message string `json:"message"`
+}
+
+type registryErrors struct {
+	Errors []registryError `json:"errors"`
+}
+
+func writeError(w http.ResponseWriter, status int, code, message string) {
+	err := registryErrors{
+		Errors: []registryError{
+			{code, message},
+		},
+	}
+	json, _ := json.Marshal(err)
+
+	w.WriteHeader(status)
+	w.Header().Add("Content-Type", "application/json")
+	w.Write(json)
+}
+
+type registryHandler struct {
+	state *builder.State
+}
+
+// Serve a manifest by tag, building it via Nix and populating caches
+// if necessary.
+func (h *registryHandler) serveManifestTag(w http.ResponseWriter, r *http.Request, name string, tag string) {
+	log.WithFields(log.Fields{
+		"image": name,
+		"tag":   tag,
+	}).Info("requesting image manifest")
+
+	image := builder.ImageFromName(name, tag)
+	buildResult, err := builder.BuildImage(r.Context(), h.state, &image)
+
+	if err != nil {
+		writeError(w, 500, "UNKNOWN", "image build failure")
+
+		log.WithError(err).WithFields(log.Fields{
+			"image": name,
+			"tag":   tag,
+		}).Error("failed to build image manifest")
+
+		return
+	}
+
+	// Some error types have special handling, which is applied
+	// here.
+	if buildResult.Error == "not_found" {
+		s := fmt.Sprintf("Could not find Nix packages: %v", buildResult.Pkgs)
+		writeError(w, 404, "MANIFEST_UNKNOWN", s)
+
+		log.WithFields(log.Fields{
+			"image":    name,
+			"tag":      tag,
+			"packages": buildResult.Pkgs,
+		}).Warn("could not find Nix packages")
+
+		return
+	}
+
+	// This marshaling error is ignored because we know that this
+	// field represents valid JSON data.
+	manifest, _ := json.Marshal(buildResult.Manifest)
+	w.Header().Add("Content-Type", manifestMediaType)
+
+	// The manifest needs to be persisted to the blob storage (to become
+	// available for clients that fetch manifests by their hash, e.g.
+	// containerd) and served to the client.
+	//
+	// Since we have no stable key to address this manifest (it may be
+	// uncacheable, yet still addressable by blob) we need to separate
+	// out the hashing, uploading and serving phases. The latter is
+	// especially important as clients may start to fetch it by digest
+	// as soon as they see a response.
+	sha256sum := fmt.Sprintf("%x", sha256.Sum256(manifest))
+	path := "layers/" + sha256sum
+	ctx := context.TODO()
+
+	_, _, err = h.state.Storage.Persist(ctx, path, mf.ManifestType, func(sw io.Writer) (string, int64, error) {
+		// We already know the hash, so no additional hash needs to be
+		// constructed here.
+		written, err := sw.Write(manifest)
+		return sha256sum, int64(written), err
+	})
+
+	if err != nil {
+		writeError(w, 500, "MANIFEST_UPLOAD", "could not upload manifest to blob store")
+
+		log.WithError(err).WithFields(log.Fields{
+			"image": name,
+			"tag":   tag,
+		}).Error("could not upload manifest")
+
+		return
+	}
+
+	w.Write(manifest)
+}
+
+// serveBlob serves a blob from storage by digest
+func (h *registryHandler) serveBlob(w http.ResponseWriter, r *http.Request, blobType, digest string) {
+	storage := h.state.Storage
+	err := storage.Serve(digest, r, w)
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"type":    blobType,
+			"digest":  digest,
+			"backend": storage.Name(),
+		}).Error("failed to serve blob from storage backend")
+	}
+}
+
+// ServeHTTP dispatches HTTP requests to the matching handlers.
+func (h *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	// Acknowledge that we speak V2 with an empty response
+	if r.RequestURI == "/v2/" {
+		return
+	}
+
+	// Build & serve a manifest by tag
+	manifestMatches := manifestRegex.FindStringSubmatch(r.RequestURI)
+	if len(manifestMatches) == 3 {
+		h.serveManifestTag(w, r, manifestMatches[1], manifestMatches[2])
+		return
+	}
+
+	// Serve a blob by digest
+	layerMatches := blobRegex.FindStringSubmatch(r.RequestURI)
+	if len(layerMatches) == 4 {
+		h.serveBlob(w, r, layerMatches[2], layerMatches[3])
+		return
+	}
+
+	log.WithField("uri", r.RequestURI).Info("unsupported registry route")
+
+	w.WriteHeader(404)
+}
+
+func main() {
+	logs.Init(version)
+	cfg, err := config.FromEnv()
+	if err != nil {
+		log.WithError(err).Fatal("failed to load configuration")
+	}
+
+	var s storage.Backend
+
+	switch cfg.Backend {
+	case config.GCS:
+		s, err = storage.NewGCSBackend()
+	case config.FileSystem:
+		s, err = storage.NewFSBackend()
+	}
+	if err != nil {
+		log.WithError(err).Fatal("failed to initialise storage backend")
+	}
+
+	log.WithField("backend", s.Name()).Info("initialised storage backend")
+
+	cache, err := builder.NewCache()
+	if err != nil {
+		log.WithError(err).Fatal("failed to instantiate build cache")
+	}
+
+	var pop layers.Popularity
+	if cfg.PopUrl != "" {
+		pop, err = downloadPopularity(cfg.PopUrl)
+		if err != nil {
+			log.WithError(err).WithField("popURL", cfg.PopUrl).
+				Fatal("failed to fetch popularity information")
+		}
+	}
+
+	state := builder.State{
+		Cache:       &cache,
+		Cfg:         cfg,
+		Pop:         pop,
+		Storage:     s,
+		UploadMutex: kmutex.New(),
+	}
+
+	log.WithFields(log.Fields{
+		"version": version,
+		"port":    cfg.Port,
+	}).Info("starting Nixery")
+
+	// All /v2/ requests belong to the registry handler.
+	http.Handle("/v2/", &registryHandler{
+		state: &state,
+	})
+
+	// All other roots are served by the static file server.
+	webDir := http.Dir(cfg.WebDir)
+	http.Handle("/", http.FileServer(webDir))
+
+	log.Fatal(http.ListenAndServe(":"+cfg.Port, nil))
+}
diff --git a/tools/nixery/config/config.go b/tools/nixery/config/config.go
new file mode 100644
index 0000000000..73ff5c8356
--- /dev/null
+++ b/tools/nixery/config/config.go
@@ -0,0 +1,73 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// Package config implements structures to store Nixery's configuration at
+// runtime as well as the logic for instantiating this configuration from the
+// environment.
+package config
+
+import (
+	"os"
+
+	log "github.com/sirupsen/logrus"
+)
+
+func getConfig(key, desc, def string) string {
+	value := os.Getenv(key)
+	if value == "" && def == "" {
+		log.WithFields(log.Fields{
+			"option":      key,
+			"description": desc,
+		}).Fatal("missing required configuration envvar")
+	} else if value == "" {
+		return def
+	}
+
+	return value
+}
+
+// Backend represents the possible storage backend types
+type Backend int
+
+const (
+	GCS = iota
+	FileSystem
+)
+
+// Config holds the Nixery configuration options.
+type Config struct {
+	Port    string    // Port on which to launch HTTP server
+	Pkgs    PkgSource // Source for Nix package set
+	Timeout string    // Timeout for a single Nix builder (seconds)
+	WebDir  string    // Directory with static web assets
+	PopUrl  string    // URL to the Nix package popularity count
+	Backend Backend   // Storage backend to use for Nixery
+}
+
+func FromEnv() (Config, error) {
+	pkgs, err := pkgSourceFromEnv()
+	if err != nil {
+		return Config{}, err
+	}
+
+	var b Backend
+	switch os.Getenv("NIXERY_STORAGE_BACKEND") {
+	case "gcs":
+		b = GCS
+	case "filesystem":
+		b = FileSystem
+	default:
+		log.WithField("values", []string{
+			"gcs",
+		}).Fatal("NIXERY_STORAGE_BACKEND must be set to a supported value (gcs or filesystem)")
+	}
+
+	return Config{
+		Port:    getConfig("PORT", "HTTP port", ""),
+		Pkgs:    pkgs,
+		Timeout: getConfig("NIX_TIMEOUT", "Nix builder timeout", "60"),
+		WebDir:  getConfig("WEB_DIR", "Static web file dir", ""),
+		PopUrl:  os.Getenv("NIX_POPULARITY_URL"),
+		Backend: b,
+	}, nil
+}
diff --git a/tools/nixery/config/pkgsource.go b/tools/nixery/config/pkgsource.go
new file mode 100644
index 0000000000..c7508a4d3a
--- /dev/null
+++ b/tools/nixery/config/pkgsource.go
@@ -0,0 +1,148 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+package config
+
+import (
+	"crypto/sha1"
+	"encoding/json"
+	"fmt"
+	"os"
+	"regexp"
+	"strings"
+
+	log "github.com/sirupsen/logrus"
+)
+
+// PkgSource represents the source from which the Nix package set used
+// by Nixery is imported. Users configure the source by setting one of
+// the supported environment variables.
+type PkgSource interface {
+	// Convert the package source into the representation required
+	// for calling Nix.
+	Render(tag string) (string, string)
+
+	// Create a key by which builds for this source and image
+	// combination can be cached.
+	//
+	// The empty string means that this value is not cacheable due
+	// to the package source being a moving target (such as a
+	// channel).
+	CacheKey(pkgs []string, tag string) string
+}
+
+type GitSource struct {
+	repository string
+}
+
+// Regex to determine whether a git reference is a commit hash or
+// something else (branch/tag).
+//
+// Used to check whether a git reference is cacheable, and to pass the
+// correct git structure to Nix.
+//
+// Note: If a user creates a branch or tag with the name of a commit
+// and references it intentionally, this heuristic will fail.
+var commitRegex = regexp.MustCompile(`^[0-9a-f]{40}$`)
+
+func (g *GitSource) Render(tag string) (string, string) {
+	args := map[string]string{
+		"url": g.repository,
+	}
+
+	// The 'git' source requires a tag to be present. If the user
+	// has not specified one, it is assumed that the default
+	// 'master' branch should be used.
+	if tag == "latest" || tag == "" {
+		tag = "master"
+	}
+
+	if commitRegex.MatchString(tag) {
+		args["rev"] = tag
+	} else {
+		args["ref"] = tag
+	}
+
+	j, _ := json.Marshal(args)
+
+	return "git", string(j)
+}
+
+func (g *GitSource) CacheKey(pkgs []string, tag string) string {
+	// Only full commit hashes can be used for caching, as
+	// everything else is potentially a moving target.
+	if !commitRegex.MatchString(tag) {
+		return ""
+	}
+
+	unhashed := strings.Join(pkgs, "") + tag
+	hashed := fmt.Sprintf("%x", sha1.Sum([]byte(unhashed)))
+
+	return hashed
+}
+
+type NixChannel struct {
+	channel string
+}
+
+func (n *NixChannel) Render(tag string) (string, string) {
+	return "nixpkgs", n.channel
+}
+
+func (n *NixChannel) CacheKey(pkgs []string, tag string) string {
+	// Since Nix channels are downloaded from the nixpkgs-channels
+	// Github, users can specify full commit hashes as the
+	// "channel", in which case builds are cacheable.
+	if !commitRegex.MatchString(n.channel) {
+		return ""
+	}
+
+	unhashed := strings.Join(pkgs, "") + n.channel
+	hashed := fmt.Sprintf("%x", sha1.Sum([]byte(unhashed)))
+
+	return hashed
+}
+
+type PkgsPath struct {
+	path string
+}
+
+func (p *PkgsPath) Render(tag string) (string, string) {
+	return "path", p.path
+}
+
+func (p *PkgsPath) CacheKey(pkgs []string, tag string) string {
+	// Path-based builds are not currently cacheable because we
+	// have no local hash of the package folder's state easily
+	// available.
+	return ""
+}
+
+// Retrieve a package source from the environment. If no source is
+// specified, the Nix code will default to a recent NixOS channel.
+func pkgSourceFromEnv() (PkgSource, error) {
+	if channel := os.Getenv("NIXERY_CHANNEL"); channel != "" {
+		log.WithField("channel", channel).Info("using Nix package set from Nix channel or commit")
+
+		return &NixChannel{
+			channel: channel,
+		}, nil
+	}
+
+	if git := os.Getenv("NIXERY_PKGS_REPO"); git != "" {
+		log.WithField("repo", git).Info("using Nix package set from git repository")
+
+		return &GitSource{
+			repository: git,
+		}, nil
+	}
+
+	if path := os.Getenv("NIXERY_PKGS_PATH"); path != "" {
+		log.WithField("path", path).Info("using Nix package set at local path")
+
+		return &PkgsPath{
+			path: path,
+		}, nil
+	}
+
+	return nil, fmt.Errorf("no valid package source has been specified")
+}
diff --git a/tools/nixery/default.nix b/tools/nixery/default.nix
new file mode 100644
index 0000000000..91eabca960
--- /dev/null
+++ b/tools/nixery/default.nix
@@ -0,0 +1,129 @@
+# Copyright 2022 The TVL Contributors
+# SPDX-License-Identifier: Apache-2.0
+
+# This function header aims to provide compatibility between builds of
+# Nixery taking place inside/outside of the TVL depot.
+#
+# In the future, Nixery will transition to using //nix/buildGo for its
+# build system and this will need some major adaptations to support
+# that.
+{ depot ? { nix.readTree.drvTargets = x: x; }
+, pkgs ? import <nixpkgs> { }
+, preLaunch ? ""
+, extraPackages ? [ ]
+, maxLayers ? 20
+, commitHash ? null
+, ...
+}@args:
+
+with pkgs;
+
+let
+  inherit (pkgs) buildGoModule lib;
+
+  # Avoid extracting this from git until we have a way to plumb
+  # through revision numbers.
+  nixery-commit-hash = "depot";
+in
+depot.nix.readTree.drvTargets rec {
+  # Implementation of the Nix image building logic
+  nixery-prepare-image = import ./prepare-image { inherit pkgs; };
+
+  # Include the Nixery website into the Nix store, unless its being
+  # overridden to something else. Nixery will serve this as its front
+  # page when visited from a browser.
+  nixery-web = ./web;
+
+  nixery-popcount = callPackage ./popcount { };
+
+  # Build Nixery's Go code, resulting in the binaries used for various
+  # bits of functionality.
+  #
+  # The server binary is wrapped to ensure that required environment
+  # variables are set at runtime.
+  nixery = buildGoModule rec {
+    name = "nixery";
+    src = ./.;
+    doCheck = true;
+
+    # Needs to be updated after every modification of go.mod/go.sum
+    vendorHash = "sha256-io9NCeZmjCZPLmII3ajXIsBWbT40XiW8ncXOuUDabbo=";
+
+    ldflags = [
+      "-s"
+      "-w"
+      "-X"
+      "main.version=${nixery-commit-hash}"
+    ];
+
+    nativeBuildInputs = [ makeWrapper ];
+    postInstall = ''
+      wrapProgram $out/bin/server \
+        --set-default WEB_DIR "${nixery-web}" \
+        --prefix PATH : ${nixery-prepare-image}/bin
+    '';
+
+    # Nixery is mirrored to Github at tazjin/nixery; this is
+    # automatically updated from CI for canon builds.
+    passthru.meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+      filter = ":/tools/nixery";
+      remote = "git@github.com:tazjin/nixery.git";
+      ref = "refs/heads/master";
+    };
+  };
+
+  # Wrapper script for the wrapper script (meta!) which configures
+  # the container environment appropriately.
+  #
+  # Most importantly, sandboxing is disabled to avoid privilege
+  # issues in containers.
+  nixery-launch-script = writeShellScriptBin "nixery" ''
+    set -e
+    export PATH=${coreutils}/bin:$PATH
+    export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
+    mkdir -p /tmp
+
+    # Create the build user/group required by Nix
+    echo 'nixbld:x:30000:nixbld' >> /etc/group
+    echo 'nixbld:x:30000:30000:nixbld:/tmp:/bin/bash' >> /etc/passwd
+    echo 'root:x:0:0:root:/root:/bin/bash' >> /etc/passwd
+    echo 'root:x:0:' >> /etc/group
+
+    # Disable sandboxing to avoid running into privilege issues
+    mkdir -p /etc/nix
+    echo 'sandbox = false' >> /etc/nix/nix.conf
+
+    # In some cases users building their own image might want to
+    # customise something on the inside (e.g. set up an environment
+    # for keys or whatever).
+    #
+    # This can be achieved by setting a 'preLaunch' script.
+    ${preLaunch}
+
+    exec ${nixery}/bin/server
+  '';
+
+  # Container image containing Nixery and Nix itself. This image can
+  # be run on Kubernetes, published on AppEngine or whatever else is
+  # desired.
+  nixery-image = dockerTools.buildLayeredImage {
+    name = "nixery";
+    config.Cmd = [ "${nixery-launch-script}/bin/nixery" ];
+
+    inherit maxLayers;
+    contents = [
+      bashInteractive
+      cacert
+      coreutils
+      git
+      gnutar
+      gzip
+      iana-etc
+      nix
+      nixery-prepare-image
+      nixery-launch-script
+      openssh
+      zlib
+    ] ++ extraPackages;
+  };
+}
diff --git a/tools/nixery/go.mod b/tools/nixery/go.mod
new file mode 100644
index 0000000000..9e896ffb40
--- /dev/null
+++ b/tools/nixery/go.mod
@@ -0,0 +1,14 @@
+module github.com/google/nixery
+
+go 1.15
+
+require (
+	cloud.google.com/go/storage v1.22.1
+	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+	github.com/google/go-cmp v0.5.8
+	github.com/im7mortal/kmutex v1.0.1 // indirect
+	github.com/pkg/xattr v0.4.7
+	github.com/sirupsen/logrus v1.8.1
+	golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401
+	gonum.org/v1/gonum v0.11.0
+)
diff --git a/tools/nixery/go.sum b/tools/nixery/go.sum
new file mode 100644
index 0000000000..5b6054fb60
--- /dev/null
+++ b/tools/nixery/go.sum
@@ -0,0 +1,708 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
+cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
+cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
+cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
+cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
+cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
+cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
+cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
+cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
+cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
+cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
+cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
+cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y=
+cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
+cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
+cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
+cloud.google.com/go/compute v1.6.0 h1:XdQIN5mdPTSBVwSIVDuY5e8ZzVAccsHvD3qTEz4zIps=
+cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc=
+cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+cloud.google.com/go/storage v1.22.1 h1:F6IlQJZrZM++apn9V5/VfS3gbTUYg98PS3EMQAzqtfg=
+cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
+git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
+github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
+github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
+github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
+github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
+github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
+github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
+github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
+github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
+github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
+github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
+github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
+github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
+github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
+github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
+github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
+github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
+github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
+github.com/googleapis/gax-go/v2 v2.3.0 h1:nRJtk3y8Fm770D42QV6T90ZnvFZyk7agSo3Q+Z9p3WI=
+github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
+github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA=
+github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/im7mortal/kmutex v1.0.1 h1:zAACzjwD+OEknDqnLdvRa/BhzFM872EBwKijviGLc9Q=
+github.com/im7mortal/kmutex v1.0.1/go.mod h1:f71c/Ugk/+58OHRAgvgzPP3QEiWGUjK13fd8ozfKWdo=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
+github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
+github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
+github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/xattr v0.4.7 h1:XoA3KzmFvyPlH4RwX5eMcgtzcaGBaSvgt3IoFQfbrmQ=
+github.com/pkg/xattr v0.4.7/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
+github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
+github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
+go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
+golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
+golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
+golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=
+golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 h1:zwrSfklXn0gxyLRX/aR+q6cgHbV/ItVyzbPlbA+dkAw=
+golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
+golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
+golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
+gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
+gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
+gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E=
+gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA=
+gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
+gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
+gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
+gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
+google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
+google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
+google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
+google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
+google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
+google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
+google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
+google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
+google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
+google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
+google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
+google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
+google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
+google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
+google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE=
+google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
+google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
+google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
+google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335 h1:2D0OT6tPVdrQTOnVe1VQjfJPTED6EZ7fdJ/f6Db6OsY=
+google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
+google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8=
+google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
+google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/tools/nixery/layers/layers.go b/tools/nixery/layers/layers.go
new file mode 100644
index 0000000000..7251c61a84
--- /dev/null
+++ b/tools/nixery/layers/layers.go
@@ -0,0 +1,354 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// This package reads an export reference graph (i.e. a graph representing the
+// runtime dependencies of a set of derivations) created by Nix and groups it in
+// a way that is likely to match the grouping for other derivation sets with
+// overlapping dependencies.
+//
+// This is used to determine which derivations to include in which layers of a
+// container image.
+//
+// # Inputs
+//
+//   - a graph of Nix runtime dependencies, generated via exportReferenceGraph
+//   - popularity values of each package in the Nix package set (in the form of a
+//     direct reference count)
+//   - a maximum number of layers to allocate for the image (the "layer budget")
+//
+// # Algorithm
+//
+// It works by first creating a (directed) dependency tree:
+//
+// img (root node)
+// β”‚
+// β”œβ”€β”€β”€> A ─────┐
+// β”‚            v
+// β”œβ”€β”€β”€> B ───> E
+// β”‚            ^
+// β”œβ”€β”€β”€> C β”€β”€β”€β”€β”€β”˜
+// β”‚     β”‚
+// β”‚     v
+// └───> D ───> F
+//
+//	β”‚
+//	└────> G
+//
+// Each node (i.e. package) is then visited to determine how important
+// it is to separate this node into its own layer, specifically:
+//
+//  1. Is the node within a certain threshold percentile of absolute
+//     popularity within all of nixpkgs? (e.g. `glibc`, `openssl`)
+//
+// 2. Is the node's runtime closure above a threshold size? (e.g. 100MB)
+//
+// In either case, a bit is flipped for this node representing each
+// condition and an edge to it is inserted directly from the image
+// root, if it does not already exist.
+//
+// For the rest of the example we assume 'G' is above the threshold
+// size and 'E' is popular.
+//
+// This tree is then transformed into a dominator tree:
+//
+// img
+// β”‚
+// β”œβ”€β”€β”€> A
+// β”œβ”€β”€β”€> B
+// β”œβ”€β”€β”€> C
+// β”œβ”€β”€β”€> E
+// β”œβ”€β”€β”€> D ───> F
+// └───> G
+//
+// Specifically this means that the paths to A, B, C, E, G, and D
+// always pass through the root (i.e. are dominated by it), whilst F
+// is dominated by D (all paths go through it).
+//
+// The top-level subtrees are considered as the initially selected
+// layers.
+//
+// If the list of layers fits within the layer budget, it is returned.
+//
+// Otherwise, a merge rating is calculated for each layer. This is the
+// product of the layer's total size and its root node's popularity.
+//
+// Layers are then merged in ascending order of merge ratings until
+// they fit into the layer budget.
+//
+// # Threshold values
+//
+// Threshold values for the partitioning conditions mentioned above
+// have not yet been determined, but we will make a good first guess
+// based on gut feeling and proceed to measure their impact on cache
+// hits/misses.
+//
+// # Example
+//
+// Using the logic described above as well as the example presented in
+// the introduction, this program would create the following layer
+// groupings (assuming no additional partitioning):
+//
+// Layer budget: 1
+// Layers: { A, B, C, D, E, F, G }
+//
+// Layer budget: 2
+// Layers: { G }, { A, B, C, D, E, F }
+//
+// Layer budget: 3
+// Layers: { G }, { E }, { A, B, C, D, F }
+//
+// Layer budget: 4
+// Layers: { G }, { E }, { D, F }, { A, B, C }
+//
+// ...
+//
+// Layer budget: 10
+// Layers: { E }, { D, F }, { A }, { B }, { C }
+package layers
+
+import (
+	"crypto/sha1"
+	"fmt"
+	"regexp"
+	"sort"
+	"strings"
+
+	log "github.com/sirupsen/logrus"
+	"gonum.org/v1/gonum/graph/flow"
+	"gonum.org/v1/gonum/graph/simple"
+)
+
+// runtimeGraph represents structured information from Nix about the runtime
+// dependencies of a derivation.
+//
+// This is generated in Nix by using the exportReferencesGraph feature.
+type RuntimeGraph struct {
+	References struct {
+		Graph []string `json:"graph"`
+	} `json:"exportReferencesGraph"`
+
+	Graph []struct {
+		Size uint64   `json:"closureSize"`
+		Path string   `json:"path"`
+		Refs []string `json:"references"`
+	} `json:"graph"`
+}
+
+// Popularity data for each Nix package that was calculated in advance.
+//
+// Popularity is a number from 1-100 that represents the
+// popularity percentile in which this package resides inside
+// of the nixpkgs tree.
+type Popularity = map[string]int
+
+// Layer represents the data returned for each layer that Nix should
+// build for the container image.
+type Layer struct {
+	Contents    []string `json:"contents"`
+	MergeRating uint64
+}
+
+// Hash the contents of a layer to create a deterministic identifier that can be
+// used for caching.
+func (l *Layer) Hash() string {
+	sum := sha1.Sum([]byte(strings.Join(l.Contents, ":")))
+	return fmt.Sprintf("%x", sum)
+}
+
+func (a Layer) merge(b Layer) Layer {
+	a.Contents = append(a.Contents, b.Contents...)
+	a.MergeRating += b.MergeRating
+	return a
+}
+
+// closure as pointed to by the graph nodes.
+type closure struct {
+	GraphID    int64
+	Path       string
+	Size       uint64
+	Refs       []string
+	Popularity int
+}
+
+func (c *closure) ID() int64 {
+	return c.GraphID
+}
+
+var nixRegexp = regexp.MustCompile(`^/nix/store/[a-z0-9]+-`)
+
+// PackageFromPath returns the name of a Nix package based on its
+// output store path.
+func PackageFromPath(path string) string {
+	return nixRegexp.ReplaceAllString(path, "")
+}
+
+// DOTID provides a human-readable package name. The name stems from
+// the dot format used by GraphViz, into which the dependency graph
+// can be rendered.
+func (c *closure) DOTID() string {
+	return PackageFromPath(c.Path)
+}
+
+// bigOrPopular checks whether this closure should be considered for
+// separation into its own layer, even if it would otherwise only
+// appear in a subtree of the dominator tree.
+func (c *closure) bigOrPopular() bool {
+	const sizeThreshold = 100 * 1000000 // 100MB
+
+	if c.Size > sizeThreshold {
+		return true
+	}
+
+	// Threshold value is picked arbitrarily right now. The reason
+	// for this is that some packages (such as `cacert`) have very
+	// few direct dependencies, but are required by pretty much
+	// everything.
+	if c.Popularity >= 100 {
+		return true
+	}
+
+	return false
+}
+
+func insertEdges(graph *simple.DirectedGraph, cmap *map[string]*closure, node *closure) {
+	// Big or popular nodes get a separate edge from the top to
+	// flag them for their own layer.
+	if node.bigOrPopular() && !graph.HasEdgeFromTo(0, node.ID()) {
+		edge := graph.NewEdge(graph.Node(0), node)
+		graph.SetEdge(edge)
+	}
+
+	for _, c := range node.Refs {
+		// Nix adds a self reference to each node, which
+		// should not be inserted.
+		if c != node.Path {
+			edge := graph.NewEdge(node, (*cmap)[c])
+			graph.SetEdge(edge)
+		}
+	}
+}
+
+// Create a graph structure from the references supplied by Nix.
+func buildGraph(refs *RuntimeGraph, pop *Popularity) *simple.DirectedGraph {
+	cmap := make(map[string]*closure)
+	graph := simple.NewDirectedGraph()
+
+	// Insert all closures into the graph, as well as a fake root
+	// closure which serves as the top of the tree.
+	//
+	// A map from store paths to IDs is kept to actually insert
+	// edges below.
+	root := &closure{
+		GraphID: 0,
+		Path:    "image_root",
+	}
+	graph.AddNode(root)
+
+	for idx, c := range refs.Graph {
+		node := &closure{
+			GraphID: int64(idx + 1), // inc because of root node
+			Path:    c.Path,
+			Size:    c.Size,
+			Refs:    c.Refs,
+		}
+
+		// The packages `nss-cacert` and `iana-etc` are added
+		// by Nixery to *every single image* and should have a
+		// very high popularity.
+		//
+		// Other popularity values are populated from the data
+		// set assembled by Nixery's popcount.
+		id := node.DOTID()
+		if strings.HasPrefix(id, "nss-cacert") || strings.HasPrefix(id, "iana-etc") {
+			// glibc has ~300k references, these packages need *more*
+			node.Popularity = 500000
+		} else if p, ok := (*pop)[id]; ok {
+			node.Popularity = p
+		} else {
+			node.Popularity = 1
+		}
+
+		graph.AddNode(node)
+		cmap[c.Path] = node
+	}
+
+	// Insert the top-level closures with edges from the root
+	// node, then insert all edges for each closure.
+	for _, p := range refs.References.Graph {
+		edge := graph.NewEdge(root, cmap[p])
+		graph.SetEdge(edge)
+	}
+
+	for _, c := range cmap {
+		insertEdges(graph, &cmap, c)
+	}
+
+	return graph
+}
+
+// Extracts a subgraph starting at the specified root from the
+// dominator tree. The subgraph is converted into a flat list of
+// layers, each containing the store paths and merge rating.
+func groupLayer(dt *flow.DominatorTree, root *closure) Layer {
+	size := root.Size
+	contents := []string{root.Path}
+	children := dt.DominatedBy(root.ID())
+
+	// This iteration does not use 'range' because the list being
+	// iterated is modified during the iteration (yes, I'm sorry).
+	for i := 0; i < len(children); i++ {
+		child := children[i].(*closure)
+		size += child.Size
+		contents = append(contents, child.Path)
+		children = append(children, dt.DominatedBy(child.ID())...)
+	}
+
+	// Contents are sorted to ensure that hashing is consistent
+	sort.Strings(contents)
+
+	return Layer{
+		Contents:    contents,
+		MergeRating: uint64(root.Popularity) * size,
+	}
+}
+
+// Calculate the dominator tree of the entire package set and group
+// each top-level subtree into a layer.
+//
+// Layers are merged together until they fit into the layer budget,
+// based on their merge rating.
+func dominate(budget int, graph *simple.DirectedGraph) []Layer {
+	dt := flow.Dominators(graph.Node(0), graph)
+
+	var layers []Layer
+	for _, n := range dt.DominatedBy(dt.Root().ID()) {
+		layers = append(layers, groupLayer(&dt, n.(*closure)))
+	}
+
+	sort.Slice(layers, func(i, j int) bool {
+		return layers[i].MergeRating < layers[j].MergeRating
+	})
+
+	if len(layers) > budget {
+		log.WithFields(log.Fields{
+			"layers": len(layers),
+			"budget": budget,
+		}).Info("ideal image exceeds layer budget")
+	}
+
+	for len(layers) > budget {
+		merged := layers[0].merge(layers[1])
+		layers[1] = merged
+		layers = layers[1:]
+	}
+
+	return layers
+}
+
+// groupLayers applies the algorithm described above the its input and returns a
+// list of layers, each consisting of a list of Nix store paths that it should
+// contain.
+func GroupLayers(refs *RuntimeGraph, pop *Popularity, budget int) []Layer {
+	graph := buildGraph(refs, pop)
+	return dominate(budget, graph)
+}
diff --git a/tools/nixery/logs/logs.go b/tools/nixery/logs/logs.go
new file mode 100644
index 0000000000..06adc701ef
--- /dev/null
+++ b/tools/nixery/logs/logs.go
@@ -0,0 +1,108 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+package logs
+
+// This file configures different log formatters via logrus. The
+// standard formatter uses a structured JSON format that is compatible
+// with Stackdriver Error Reporting.
+//
+// https://cloud.google.com/error-reporting/docs/formatting-error-messages
+
+import (
+	"bytes"
+	"encoding/json"
+	log "github.com/sirupsen/logrus"
+)
+
+type stackdriverFormatter struct{}
+
+type serviceContext struct {
+	Service string `json:"service"`
+	Version string `json:"version"`
+}
+
+type reportLocation struct {
+	FilePath     string `json:"filePath"`
+	LineNumber   int    `json:"lineNumber"`
+	FunctionName string `json:"functionName"`
+}
+
+var nixeryContext = serviceContext{
+	Service: "nixery",
+}
+
+// isError determines whether an entry should be logged as an error
+// (i.e. with attached `context`).
+//
+// This requires the caller information to be present on the log
+// entry, as stacktraces are not available currently.
+func isError(e *log.Entry) bool {
+	l := e.Level
+	return (l == log.ErrorLevel || l == log.FatalLevel || l == log.PanicLevel) &&
+		e.HasCaller()
+}
+
+// logSeverity formats the entry's severity into a format compatible
+// with Stackdriver Logging.
+//
+// The two formats that are being mapped do not have an equivalent set
+// of severities/levels, so the mapping is somewhat arbitrary for a
+// handful of them.
+//
+// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
+func logSeverity(l log.Level) string {
+	switch l {
+	case log.TraceLevel:
+		return "DEBUG"
+	case log.DebugLevel:
+		return "DEBUG"
+	case log.InfoLevel:
+		return "INFO"
+	case log.WarnLevel:
+		return "WARNING"
+	case log.ErrorLevel:
+		return "ERROR"
+	case log.FatalLevel:
+		return "CRITICAL"
+	case log.PanicLevel:
+		return "EMERGENCY"
+	default:
+		return "DEFAULT"
+	}
+}
+
+func (f stackdriverFormatter) Format(e *log.Entry) ([]byte, error) {
+	msg := e.Data
+	msg["serviceContext"] = &nixeryContext
+	msg["message"] = &e.Message
+	msg["eventTime"] = &e.Time
+	msg["severity"] = logSeverity(e.Level)
+
+	if e, ok := msg[log.ErrorKey]; ok {
+		if err, isError := e.(error); isError {
+			msg[log.ErrorKey] = err.Error()
+		} else {
+			delete(msg, log.ErrorKey)
+		}
+	}
+
+	if isError(e) {
+		loc := reportLocation{
+			FilePath:     e.Caller.File,
+			LineNumber:   e.Caller.Line,
+			FunctionName: e.Caller.Function,
+		}
+		msg["context"] = &loc
+	}
+
+	b := new(bytes.Buffer)
+	err := json.NewEncoder(b).Encode(&msg)
+
+	return b.Bytes(), err
+}
+
+func Init(version string) {
+	nixeryContext.Version = version
+	log.SetReportCaller(true)
+	log.SetFormatter(stackdriverFormatter{})
+}
diff --git a/tools/nixery/manifest/manifest.go b/tools/nixery/manifest/manifest.go
new file mode 100644
index 0000000000..5638b576eb
--- /dev/null
+++ b/tools/nixery/manifest/manifest.go
@@ -0,0 +1,135 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// Package image implements logic for creating the image metadata
+// (such as the image manifest and configuration).
+package manifest
+
+import (
+	"crypto/sha256"
+	"encoding/json"
+	"fmt"
+	"sort"
+)
+
+const (
+	// manifest constants
+	schemaVersion = 2
+
+	// media types
+	ManifestType = "application/vnd.docker.distribution.manifest.v2+json"
+	LayerType    = "application/vnd.docker.image.rootfs.diff.tar.gzip"
+	configType   = "application/vnd.docker.container.image.v1+json"
+
+	// image config constants
+	os     = "linux"
+	fsType = "layers"
+)
+
+type Entry struct {
+	MediaType string `json:"mediaType,omitempty"`
+	Size      int64  `json:"size"`
+	Digest    string `json:"digest"`
+
+	// These fields are internal to Nixery and not part of the
+	// serialised entry.
+	MergeRating uint64 `json:"-"`
+	TarHash     string `json:",omitempty"`
+}
+
+type manifest struct {
+	SchemaVersion int     `json:"schemaVersion"`
+	MediaType     string  `json:"mediaType"`
+	Config        Entry   `json:"config"`
+	Layers        []Entry `json:"layers"`
+}
+
+type imageConfig struct {
+	Architecture string `json:"architecture"`
+	OS           string `json:"os"`
+
+	RootFS struct {
+		FSType  string   `json:"type"`
+		DiffIDs []string `json:"diff_ids"`
+	} `json:"rootfs"`
+
+	Config struct {
+		Cmd []string `json:",omitempty"`
+		Env []string `json:",omitempty"`
+	} `json:"config"`
+}
+
+// ConfigLayer represents the configuration layer to be included in
+// the manifest, containing its JSON-serialised content and SHA256
+// hash.
+type ConfigLayer struct {
+	Config []byte
+	SHA256 string
+}
+
+// imageConfig creates an image configuration with the values set to
+// the constant defaults.
+//
+// Outside of this module the image configuration is treated as an
+// opaque blob and it is thus returned as an already serialised byte
+// array and its SHA256-hash.
+func configLayer(arch string, hashes []string, cmd string) ConfigLayer {
+	c := imageConfig{}
+	c.Architecture = arch
+	c.OS = os
+	c.RootFS.FSType = fsType
+	c.RootFS.DiffIDs = hashes
+	if cmd != "" {
+		c.Config.Cmd = []string{cmd}
+	}
+	c.Config.Env = []string{"SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt"}
+
+	j, _ := json.Marshal(c)
+
+	return ConfigLayer{
+		Config: j,
+		SHA256: fmt.Sprintf("%x", sha256.Sum256(j)),
+	}
+}
+
+// Manifest creates an image manifest from the specified layer entries
+// and returns its JSON-serialised form as well as the configuration
+// layer.
+//
+// Callers do not need to set the media type for the layer entries.
+func Manifest(arch string, layers []Entry, cmd string) (json.RawMessage, ConfigLayer) {
+	// Sort layers by their merge rating, from highest to lowest.
+	// This makes it likely for a contiguous chain of shared image
+	// layers to appear at the beginning of a layer.
+	//
+	// Due to moby/moby#38446 Docker considers the order of layers
+	// when deciding which layers to download again.
+	sort.Slice(layers, func(i, j int) bool {
+		return layers[i].MergeRating > layers[j].MergeRating
+	})
+
+	hashes := make([]string, len(layers))
+	for i, l := range layers {
+		hashes[i] = l.TarHash
+		l.MediaType = LayerType
+		l.TarHash = ""
+		layers[i] = l
+	}
+
+	c := configLayer(arch, hashes, cmd)
+
+	m := manifest{
+		SchemaVersion: schemaVersion,
+		MediaType:     ManifestType,
+		Config: Entry{
+			MediaType: configType,
+			Size:      int64(len(c.Config)),
+			Digest:    "sha256:" + c.SHA256,
+		},
+		Layers: layers,
+	}
+
+	j, _ := json.Marshal(m)
+
+	return json.RawMessage(j), c
+}
diff --git a/tools/nixery/popcount/README.md b/tools/nixery/popcount/README.md
new file mode 100644
index 0000000000..3e56f99d57
--- /dev/null
+++ b/tools/nixery/popcount/README.md
@@ -0,0 +1,39 @@
+popcount
+========
+
+This script is used to count the popularity for each package in `nixpkgs`, by
+determining how many other packages depend on it.
+
+It skips over all packages that fail to build, are not cached or are unfree -
+but these omissions do not meaningfully affect the statistics.
+
+It currently does not evaluate nested attribute sets (such as
+`haskellPackages`).
+
+## Usage
+
+1. Generate a list of all top-level attributes in `nixpkgs`:
+
+   ```shell
+   nix eval '(with builtins; toJSON (attrNames (import <nixpkgs> {})))' | jq -r | jq > all-top-level.json
+   ```
+
+2. Run `./popcount > all-runtime-deps.txt`
+
+3. Collect and count the results with the following magic incantation:
+
+   ```shell
+   cat all-runtime-deps.txt \
+     | sed -r 's|/nix/store/[a-z0-9]+-||g' \
+     | sort \
+     | uniq -c \
+     | sort -n -r \
+     | awk '{ print "{\"" $2 "\":" $1 "}"}' \
+     | jq -c -s '. | add | with_entries(select(.value > 1))' \
+     > your-output-file
+   ```
+
+   In essence, this will trim Nix's store paths and hashes from the output,
+   count the occurrences of each package and return the output as JSON. All
+   packages that have no references other than themselves are removed from the
+   output.
diff --git a/tools/nixery/popcount/default.nix b/tools/nixery/popcount/default.nix
new file mode 100644
index 0000000000..4b16768e4e
--- /dev/null
+++ b/tools/nixery/popcount/default.nix
@@ -0,0 +1,13 @@
+# Copyright 2022 The TVL Contributors
+# SPDX-License-Identifier: Apache-2.0
+
+{ buildGoPackage }:
+
+buildGoPackage {
+  name = "nixery-popcount";
+
+  src = ./.;
+
+  goPackagePath = "github.com/google/nixery/popcount";
+  doCheck = true;
+}
diff --git a/tools/nixery/popcount/popcount.go b/tools/nixery/popcount/popcount.go
new file mode 100644
index 0000000000..b83ac3ed1a
--- /dev/null
+++ b/tools/nixery/popcount/popcount.go
@@ -0,0 +1,280 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// Popcount fetches popularity information for each store path in a
+// given Nix channel from the upstream binary cache.
+//
+// It does this simply by inspecting the narinfo files, rather than
+// attempting to deal with instantiation of the binary cache.
+//
+// This is *significantly* faster than attempting to realise the whole
+// channel and then calling `nix path-info` on it.
+//
+// TODO(tazjin): Persist intermediate results (references for each
+// store path) to speed up subsequent runs.
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"os/exec"
+	"regexp"
+	"strings"
+)
+
+var client http.Client
+var pathexp = regexp.MustCompile("/nix/store/([a-z0-9]{32})-(.*)$")
+var refsexp = regexp.MustCompile("(?m:^References: (.*)$)")
+var refexp = regexp.MustCompile("^([a-z0-9]{32})-(.*)$")
+
+type meta struct {
+	name   string
+	url    string
+	commit string
+}
+
+type item struct {
+	name string
+	hash string
+}
+
+func failOn(err error, msg string) {
+	if err != nil {
+		log.Fatalf("%s: %s", msg, err)
+	}
+}
+
+func channelMetadata(channel string) meta {
+	// This needs an HTTP client that does not follow redirects
+	// because the channel URL is used explicitly for other
+	// downloads.
+	c := http.Client{
+		CheckRedirect: func(req *http.Request, via []*http.Request) error {
+			return http.ErrUseLastResponse
+		},
+	}
+
+	resp, err := c.Get(fmt.Sprintf("https://channels.nixos.org/%s", channel))
+	failOn(err, "failed to retrieve channel metadata")
+
+	loc, err := resp.Location()
+	failOn(err, "no redirect location given for channel")
+
+	// TODO(tazjin): These redirects are currently served as 301s, but
+	// should (and used to) be 302s. Check if/when this is fixed and
+	// update accordingly.
+	if !(resp.StatusCode == 301 || resp.StatusCode == 302) {
+		log.Fatalf("Expected redirect for channel, but received '%s'\n", resp.Status)
+	}
+
+	commitResp, err := c.Get(fmt.Sprintf("%s/git-revision", loc.String()))
+	failOn(err, "failed to retrieve commit for channel")
+
+	defer commitResp.Body.Close()
+	commit, err := ioutil.ReadAll(commitResp.Body)
+	failOn(err, "failed to read commit from response")
+	if commitResp.StatusCode != 200 {
+		log.Fatalf("non-success status code when fetching commit: %s (%v)", string(commit), commitResp.StatusCode)
+	}
+
+	return meta{
+		name:   channel,
+		url:    loc.String(),
+		commit: string(commit),
+	}
+}
+
+func downloadStorePaths(c *meta) []string {
+	resp, err := client.Get(fmt.Sprintf("%s/store-paths.xz", c.url))
+	failOn(err, "failed to download store-paths.xz")
+	defer resp.Body.Close()
+
+	cmd := exec.Command("xzcat")
+	stdin, err := cmd.StdinPipe()
+	failOn(err, "failed to open xzcat stdin")
+	stdout, err := cmd.StdoutPipe()
+	failOn(err, "failed to open xzcat stdout")
+	defer stdout.Close()
+
+	go func() {
+		defer stdin.Close()
+		io.Copy(stdin, resp.Body)
+	}()
+
+	err = cmd.Start()
+	failOn(err, "failed to start xzcat")
+
+	paths, err := ioutil.ReadAll(stdout)
+	failOn(err, "failed to read uncompressed store paths")
+
+	err = cmd.Wait()
+	failOn(err, "xzcat failed to decompress")
+
+	return strings.Split(string(paths), "\n")
+}
+
+func storePathToItem(path string) *item {
+	res := pathexp.FindStringSubmatch(path)
+	if len(res) != 3 {
+		return nil
+	}
+
+	return &item{
+		hash: res[1],
+		name: res[2],
+	}
+}
+
+func narInfoToRefs(narinfo string) []string {
+	all := refsexp.FindAllStringSubmatch(narinfo, 1)
+
+	if len(all) != 1 {
+		log.Fatalf("failed to parse narinfo:\n%s\nfound: %v\n", narinfo, all[0])
+	}
+
+	if len(all[0]) != 2 {
+		// no references found
+		return []string{}
+	}
+
+	refs := strings.Split(all[0][1], " ")
+	for i, s := range refs {
+		if s == "" {
+			continue
+		}
+
+		res := refexp.FindStringSubmatch(s)
+		refs[i] = res[2]
+	}
+
+	return refs
+}
+
+func fetchNarInfo(i *item) (string, error) {
+	file, err := ioutil.ReadFile("popcache/" + i.hash)
+	if err == nil {
+		return string(file), nil
+	}
+
+	resp, err := client.Get(fmt.Sprintf("https://cache.nixos.org/%s.narinfo", i.hash))
+	if err != nil {
+		return "", err
+	}
+
+	defer resp.Body.Close()
+
+	narinfo, err := ioutil.ReadAll(resp.Body)
+
+	// best-effort write the file to the cache
+	ioutil.WriteFile("popcache/"+i.hash, narinfo, 0644)
+
+	return string(narinfo), err
+}
+
+// downloader starts a worker that takes care of downloading narinfos
+// for all paths received from the queue.
+//
+// If there is no data remaining in the queue, the downloader exits
+// and informs the finaliser queue about having exited.
+func downloader(queue chan *item, narinfos chan string, downloaders chan struct{}) {
+	for i := range queue {
+		ni, err := fetchNarInfo(i)
+		if err != nil {
+			log.Printf("couldn't fetch narinfo for %s: %s\n", i.name, err)
+			continue
+
+		}
+		narinfos <- ni
+	}
+	downloaders <- struct{}{}
+}
+
+// finaliser counts the number of downloaders that have exited and
+// closes the narinfos queue to signal to the counters that no more
+// elements will arrive.
+func finaliser(count int, downloaders chan struct{}, narinfos chan string) {
+	for range downloaders {
+		count--
+		if count == 0 {
+			close(downloaders)
+			close(narinfos)
+			break
+		}
+	}
+}
+
+func main() {
+	if len(os.Args) == 1 {
+		log.Fatalf("Nix channel must be specified as first argument")
+	}
+
+	err := os.MkdirAll("popcache", 0755)
+	if err != nil {
+		log.Fatalf("Failed to create 'popcache' directory in current folder: %s\n", err)
+	}
+
+	count := 42 // concurrent downloader count
+	channel := os.Args[1]
+	log.Printf("Fetching metadata for channel '%s'\n", channel)
+
+	meta := channelMetadata(channel)
+	log.Printf("Pinned channel '%s' to commit '%s'\n", meta.name, meta.commit)
+
+	paths := downloadStorePaths(&meta)
+	log.Printf("Fetching references for %d store paths\n", len(paths))
+
+	// Download paths concurrently and receive their narinfos into
+	// a channel. Data is collated centrally into a map and
+	// serialised at the /very/ end.
+	downloadQueue := make(chan *item, len(paths))
+	for _, p := range paths {
+		if i := storePathToItem(p); i != nil {
+			downloadQueue <- i
+		}
+	}
+	close(downloadQueue)
+
+	// Set up a task tracking channel for parsing & counting
+	// narinfos, as well as a coordination channel for signaling
+	// that all downloads have finished
+	narinfos := make(chan string, 50)
+	downloaders := make(chan struct{}, count)
+	for i := 0; i < count; i++ {
+		go downloader(downloadQueue, narinfos, downloaders)
+	}
+
+	go finaliser(count, downloaders, narinfos)
+
+	counts := make(map[string]int)
+	for ni := range narinfos {
+		refs := narInfoToRefs(ni)
+		for _, ref := range refs {
+			if ref == "" {
+				continue
+			}
+
+			counts[ref] += 1
+		}
+	}
+
+	// Remove all self-references (i.e. packages not referenced by anyone else)
+	for k, v := range counts {
+		if v == 1 {
+			delete(counts, k)
+		}
+	}
+
+	bytes, _ := json.Marshal(counts)
+	outfile := fmt.Sprintf("popularity-%s-%s.json", meta.name, meta.commit)
+	err = ioutil.WriteFile(outfile, bytes, 0644)
+	if err != nil {
+		log.Fatalf("Failed to write output to '%s': %s\n", outfile, err)
+	}
+
+	log.Printf("Wrote output to '%s'\n", outfile)
+}
diff --git a/tools/nixery/prepare-image/default.nix b/tools/nixery/prepare-image/default.nix
new file mode 100644
index 0000000000..efd9ed3404
--- /dev/null
+++ b/tools/nixery/prepare-image/default.nix
@@ -0,0 +1,18 @@
+# Copyright 2022 The TVL Contributors
+# SPDX-License-Identifier: Apache-2.0
+
+# This file builds a wrapper script called by Nixery to ask for the
+# content information for a given image.
+#
+# The purpose of using a wrapper script is to ensure that the paths to
+# all required Nix files are set correctly at runtime.
+
+{ pkgs ? import <nixpkgs> { } }:
+
+pkgs.writeShellScriptBin "nixery-prepare-image" ''
+  exec ${pkgs.nix}/bin/nix-build \
+    --show-trace \
+    --no-out-link "$@" \
+    --argstr loadPkgs ${./load-pkgs.nix} \
+    ${./prepare-image.nix}
+''
diff --git a/tools/nixery/prepare-image/load-pkgs.nix b/tools/nixery/prepare-image/load-pkgs.nix
new file mode 100644
index 0000000000..7f8ab5479d
--- /dev/null
+++ b/tools/nixery/prepare-image/load-pkgs.nix
@@ -0,0 +1,36 @@
+# Copyright 2022 The TVL Contributors
+# SPDX-License-Identifier: Apache-2.0
+
+# Load a Nix package set from one of the supported source types
+# (nixpkgs, git, path).
+{ srcType, srcArgs, importArgs ? { } }:
+
+with builtins;
+let
+  # If a nixpkgs channel is requested, it is retrieved from Github (as
+  # a tarball) and imported.
+  fetchImportChannel = channel:
+    let
+      url =
+        "https://github.com/NixOS/nixpkgs/archive/${channel}.tar.gz";
+    in
+    import (fetchTarball url) importArgs;
+
+  # If a git repository is requested, it is retrieved via
+  # builtins.fetchGit which defaults to the git configuration of the
+  # outside environment. This means that user-configured SSH
+  # credentials etc. are going to work as expected.
+  fetchImportGit = spec: import (fetchGit spec) importArgs;
+
+  # No special handling is used for paths, so users are expected to pass one
+  # that will work natively with Nix.
+  importPath = path: import (toPath path) importArgs;
+in
+if srcType == "nixpkgs" then
+  fetchImportChannel srcArgs
+else if srcType == "git" then
+  fetchImportGit (fromJSON srcArgs)
+else if srcType == "path" then
+  importPath srcArgs
+else
+  throw ("Invalid package set source specification: ${srcType} (${srcArgs})")
diff --git a/tools/nixery/prepare-image/prepare-image.nix b/tools/nixery/prepare-image/prepare-image.nix
new file mode 100644
index 0000000000..28022fe42f
--- /dev/null
+++ b/tools/nixery/prepare-image/prepare-image.nix
@@ -0,0 +1,198 @@
+# Copyright 2022 The TVL Contributors
+# SPDX-License-Identifier: Apache-2.0
+
+# This file contains a derivation that outputs structured information
+# about the runtime dependencies of an image with a given set of
+# packages. This is used by Nixery to determine the layer grouping and
+# assemble each layer.
+#
+# In addition it creates and outputs a meta-layer with the symlink
+# structure required for using the image together with the individual
+# package layers.
+
+{
+  # Description of the package set to be used (will be loaded by load-pkgs.nix)
+  srcType ? "nixpkgs"
+, srcArgs ? "nixos-unstable"
+, system ? "x86_64-linux"
+, importArgs ? { }
+, # Path to load-pkgs.nix
+  loadPkgs ? ./load-pkgs.nix
+, # Packages to install by name (which must refer to top-level attributes of
+  # nixpkgs). This is passed in as a JSON-array in string form.
+  packages ? "[]"
+}:
+
+let
+  inherit (builtins)
+    foldl'
+    fromJSON
+    hasAttr
+    length
+    match
+    readFile
+    toFile
+    toJSON;
+
+  # Package set to use for sourcing utilities
+  nativePkgs = import loadPkgs { inherit srcType srcArgs importArgs; };
+  inherit (nativePkgs) coreutils jq openssl lib runCommand writeText symlinkJoin;
+
+  # Package set to use for packages to be included in the image. This
+  # package set is imported with the system set to the target
+  # architecture.
+  pkgs = import loadPkgs {
+    inherit srcType srcArgs;
+    importArgs = importArgs // {
+      inherit system;
+    };
+  };
+
+  # deepFetch traverses the top-level Nix package set to retrieve an item via a
+  # path specified in string form.
+  #
+  # For top-level items, the name of the key yields the result directly. Nested
+  # items are fetched by using dot-syntax, as in Nix itself.
+  #
+  # Due to a restriction of the registry API specification it is not possible to
+  # pass uppercase characters in an image name, however the Nix package set
+  # makes use of camelCasing repeatedly (for example for `haskellPackages`).
+  #
+  # To work around this, if no value is found on the top-level a second lookup
+  # is done on the package set using lowercase-names. This is not done for
+  # nested sets, as they often have keys that only differ in case.
+  #
+  # For example, `deepFetch pkgs "xorg.xev"` retrieves `pkgs.xorg.xev` and
+  # `deepFetch haskellpackages.stylish-haskell` retrieves
+  # `haskellPackages.stylish-haskell`.
+  deepFetch = with lib; s: n:
+    let
+      path = splitString "." n;
+      err = { error = "not_found"; pkg = n; };
+      # The most efficient way I've found to do a lookup against
+      # case-differing versions of an attribute is to first construct a
+      # mapping of all lowercased attribute names to their differently cased
+      # equivalents.
+      #
+      # This map is then used for a second lookup if the top-level
+      # (case-sensitive) one does not yield a result.
+      hasUpper = str: (match ".*[A-Z].*" str) != null;
+      allUpperKeys = filter hasUpper (attrNames s);
+      lowercased = listToAttrs (map
+        (k: {
+          name = toLower k;
+          value = k;
+        })
+        allUpperKeys);
+      caseAmendedPath = map (v: if hasAttr v lowercased then lowercased."${v}" else v) path;
+      fetchLower = attrByPath caseAmendedPath err s;
+    in
+    attrByPath path fetchLower s;
+
+  # Workaround for a workaround in nixpkgs: Unquoted language
+  # identifiers can not start with numbers in Nix, but some package
+  # names start with numbers (such as `1password`).
+  #
+  # In nixpkgs convention, these identifiers are prefixed with
+  # underscores (e.g. `_1password`), however this is not accepted by
+  # the Docker registry protocol.
+  #
+  # To make this work, we detect these kinds of packages and add the
+  # missing underscore.
+  needsUnderscore = pkg: (builtins.match "^[0-9].*" pkg) != null;
+  normalisedPackages = map (p: if needsUnderscore p then "_${p}" else p) (fromJSON packages);
+
+  # allContents contains all packages successfully retrieved by name
+  # from the package set, as well as any errors encountered while
+  # attempting to fetch a package.
+  #
+  # Accumulated error information is returned back to the server.
+  allContents =
+    # Folds over the results of 'deepFetch' on all requested packages to
+    # separate them into errors and content. This allows the program to
+    # terminate early and return only the errors if any are encountered.
+    let
+      splitter = attrs: res:
+        if hasAttr "error" res
+        then attrs // { errors = attrs.errors ++ [ res ]; }
+        else attrs // { contents = attrs.contents ++ [ res ]; };
+      init = { contents = [ ]; errors = [ ]; };
+      fetched = (map (deepFetch pkgs) normalisedPackages);
+    in
+    foldl' splitter init fetched;
+
+  # Contains the export references graph of all retrieved packages,
+  # which has information about all runtime dependencies of the image.
+  #
+  # This is used by Nixery to group closures into image layers.
+  runtimeGraph = runCommand "runtime-graph.json"
+    {
+      __structuredAttrs = true;
+      exportReferencesGraph.graph = allContents.contents;
+      PATH = "${coreutils}/bin";
+      builder = toFile "builder" ''
+        . .attrs.sh
+        cp .attrs.json ''${outputs[out]}
+      '';
+    } "";
+
+  # Create a symlink forest into all top-level store paths of the
+  # image contents.
+  contentsEnv = symlinkJoin {
+    name = "bulk-layers";
+    paths = allContents.contents;
+
+    # Provide a few essentials that many programs expect:
+    # - a /tmp directory,
+    # - a /usr/bin/env for shell scripts that require it.
+    #
+    # Note that in images that do not actually contain `coreutils`,
+    # /usr/bin/env will be a dangling symlink.
+    #
+    # TODO(tazjin): Don't link /usr/bin/env if coreutils is not included.
+    postBuild = ''
+      mkdir -p $out/tmp
+      mkdir -p $out/usr/bin
+      ln -s ${coreutils}/bin/env $out/usr/bin/env
+    '';
+  };
+
+  # Image layer that contains the symlink forest created above. This
+  # must be included in the image to ensure that the filesystem has a
+  # useful layout at runtime.
+  symlinkLayer = runCommand "symlink-layer.tar" { } ''
+    cp -r ${contentsEnv}/ ./layer
+    tar --transform='s|^\./||' -C layer --sort=name --mtime="@$SOURCE_DATE_EPOCH" --owner=0 --group=0 -cf $out .
+  '';
+
+  # Metadata about the symlink layer which is required for serving it.
+  # Two different hashes are computed for different usages (inclusion
+  # in manifest vs. content-checking in the layer cache).
+  symlinkLayerMeta = fromJSON (builtins.unsafeDiscardStringContext (readFile (runCommand "symlink-layer-meta.json"
+    {
+      buildInputs = [ coreutils jq openssl ];
+    } ''
+    tarHash=$(sha256sum ${symlinkLayer} | cut -d ' ' -f1)
+    layerSize=$(stat --printf '%s' ${symlinkLayer})
+
+    jq -n -c --arg tarHash $tarHash --arg size $layerSize --arg path ${symlinkLayer} \
+      '{ size: ($size | tonumber), tarHash: $tarHash, path: $path }' >> $out
+  '')));
+
+  # Final output structure returned to Nixery if the build succeeded
+  buildOutput = {
+    runtimeGraph = fromJSON (builtins.unsafeDiscardStringContext (readFile runtimeGraph));
+    symlinkLayer = symlinkLayerMeta;
+  };
+
+  # Output structure returned if errors occured during the build. Currently the
+  # only error type that is returned in a structured way is 'not_found'.
+  errorOutput = {
+    error = "not_found";
+    pkgs = map (err: err.pkg) allContents.errors;
+  };
+in
+writeText "build-output.json" (if (length allContents.errors) == 0
+then toJSON buildOutput
+else toJSON errorOutput
+)
diff --git a/tools/nixery/scripts/integration-test.sh b/tools/nixery/scripts/integration-test.sh
new file mode 100755
index 0000000000..9d06e96ba2
--- /dev/null
+++ b/tools/nixery/scripts/integration-test.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+set -eou pipefail
+
+# This integration test makes sure that the container image built
+# for Nixery itself runs fine in Docker, and that images pulled
+# from it work in Docker.
+
+IMG=$(docker load -q -i "$(nix-build -A nixery-image)" | awk '{ print $3 }')
+echo "Loaded Nixery image as ${IMG}"
+
+# Run the built nixery docker image in the background, but keep printing its
+# output as it occurs.
+# We can't just mount a tmpfs to /var/cache/nixery, as tmpfs doesn't support
+# user xattrs.
+# So create a temporary directory in the current working directory, and hope
+# it's backed by something supporting user xattrs.
+# We'll notice it isn't if nixery starts complaining about not able to set
+# xattrs anyway.
+if [ -d var-cache-nixery ]; then rm -Rf var-cache-nixery; fi
+mkdir var-cache-nixery
+docker run --privileged --rm -p 8080:8080 --name nixery \
+  -e PORT=8080 \
+  --mount "type=bind,source=${PWD}/var-cache-nixery,target=/var/cache/nixery" \
+  -e NIXERY_CHANNEL=nixos-unstable \
+  -e NIXERY_STORAGE_BACKEND=filesystem \
+  -e STORAGE_PATH=/var/cache/nixery \
+  "${IMG}" &
+
+# Give the container ~20 seconds to come up
+set +e
+attempts=0
+echo -n "Waiting for Nixery to start ..."
+until curl --fail --silent "http://localhost:8080/v2/"; do
+  [[ attempts -eq 30 ]] && echo "Nixery container failed to start!" && exit 1
+  ((attempts++))
+  echo -n "."
+  sleep 1
+done
+set -e
+
+# Pull and run an image of the current CPU architecture
+case $(uname -m) in
+  x86_64)
+    docker run --rm localhost:8080/hello hello
+    ;;
+  aarch64)
+    docker run --rm localhost:8080/arm64/hello hello
+    ;;
+esac
+
+# Pull an image of the opposite CPU architecture (but without running it)
+case $(uname -m) in
+x86_64)
+  docker pull localhost:8080/arm64/hello
+  ;;
+aarch64)
+  docker pull localhost:8080/hello
+  ;;
+esac
diff --git a/tools/nixery/shell.nix b/tools/nixery/shell.nix
new file mode 100644
index 0000000000..b91094722c
--- /dev/null
+++ b/tools/nixery/shell.nix
@@ -0,0 +1,13 @@
+# Copyright 2022 The TVL Contributors
+# SPDX-License-Identifier: Apache-2.0
+
+# Configures a shell environment that builds required local packages to
+# run Nixery.
+{ pkgs ? import <nixpkgs> { } }:
+
+let nixery = import ./default.nix { inherit pkgs; };
+in pkgs.stdenv.mkDerivation {
+  name = "nixery-dev-shell";
+
+  buildInputs = with pkgs; [ jq nixery.nixery-prepare-image ];
+}
diff --git a/tools/nixery/storage/filesystem.go b/tools/nixery/storage/filesystem.go
new file mode 100644
index 0000000000..3df4420f0f
--- /dev/null
+++ b/tools/nixery/storage/filesystem.go
@@ -0,0 +1,99 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// Filesystem storage backend for Nixery.
+package storage
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"path"
+
+	"github.com/pkg/xattr"
+	log "github.com/sirupsen/logrus"
+)
+
+type FSBackend struct {
+	path string
+}
+
+func NewFSBackend() (*FSBackend, error) {
+	p := os.Getenv("STORAGE_PATH")
+	if p == "" {
+		return nil, fmt.Errorf("STORAGE_PATH must be set for filesystem storage")
+	}
+
+	p = path.Clean(p)
+	err := os.MkdirAll(p, 0755)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create storage dir: %s", err)
+	}
+
+	return &FSBackend{p}, nil
+}
+
+func (b *FSBackend) Name() string {
+	return fmt.Sprintf("Filesystem (%s)", b.path)
+}
+
+func (b *FSBackend) Persist(ctx context.Context, key, contentType string, f Persister) (string, int64, error) {
+	full := path.Join(b.path, key)
+	dir := path.Dir(full)
+	err := os.MkdirAll(dir, 0755)
+	if err != nil {
+		log.WithError(err).WithField("path", dir).Error("failed to create storage directory")
+		return "", 0, err
+	}
+
+	file, err := os.OpenFile(full, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
+	if err != nil {
+		log.WithError(err).WithField("file", full).Error("failed to write file")
+		return "", 0, err
+	}
+	defer file.Close()
+
+	err = xattr.Set(full, "user.mime_type", []byte(contentType))
+	if err != nil {
+		log.WithError(err).WithField("file", full).Error("failed to store file type in xattrs")
+		return "", 0, err
+	}
+
+	return f(file)
+}
+
+func (b *FSBackend) Fetch(ctx context.Context, key string) (io.ReadCloser, error) {
+	full := path.Join(b.path, key)
+	return os.Open(full)
+}
+
+func (b *FSBackend) Move(ctx context.Context, old, new string) error {
+	newpath := path.Join(b.path, new)
+	err := os.MkdirAll(path.Dir(newpath), 0755)
+	if err != nil {
+		return err
+	}
+
+	return os.Rename(path.Join(b.path, old), newpath)
+}
+
+func (b *FSBackend) Serve(digest string, r *http.Request, w http.ResponseWriter) error {
+	p := path.Join(b.path, "layers", digest)
+
+	log.WithFields(log.Fields{
+		"digest": digest,
+		"path":   p,
+	}).Info("serving blob from filesystem")
+
+	contentType, err := xattr.Get(p, "user.mime_type")
+	if err != nil {
+		log.WithError(err).WithField("file", p).Error("failed to read file type from xattrs")
+		return err
+	}
+	w.Header().Add("Content-Type", string(contentType))
+
+	http.ServeFile(w, r, p)
+	return nil
+}
diff --git a/tools/nixery/storage/gcs.go b/tools/nixery/storage/gcs.go
new file mode 100644
index 0000000000..752c6bbd82
--- /dev/null
+++ b/tools/nixery/storage/gcs.go
@@ -0,0 +1,231 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// Google Cloud Storage backend for Nixery.
+package storage
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"os"
+	"time"
+
+	"cloud.google.com/go/storage"
+	log "github.com/sirupsen/logrus"
+	"golang.org/x/oauth2/google"
+)
+
+// HTTP client to use for direct calls to APIs that are not part of the SDK
+var client = &http.Client{}
+
+// API scope needed for renaming objects in GCS
+const gcsScope = "https://www.googleapis.com/auth/devstorage.read_write"
+
+type GCSBackend struct {
+	bucket  string
+	handle  *storage.BucketHandle
+	signing *storage.SignedURLOptions
+}
+
+// Constructs a new GCS bucket backend based on the configured
+// environment variables.
+func NewGCSBackend() (*GCSBackend, error) {
+	bucket := os.Getenv("GCS_BUCKET")
+	if bucket == "" {
+		return nil, fmt.Errorf("GCS_BUCKET must be configured for GCS usage")
+	}
+
+	ctx := context.Background()
+	client, err := storage.NewClient(ctx)
+	if err != nil {
+		log.WithError(err).Fatal("failed to set up Cloud Storage client")
+	}
+
+	handle := client.Bucket(bucket)
+
+	if _, err := handle.Attrs(ctx); err != nil {
+		log.WithError(err).WithField("bucket", bucket).Error("could not access configured bucket")
+		return nil, err
+	}
+
+	signing, err := signingOptsFromEnv()
+	if err != nil {
+		log.WithError(err).Error("failed to configure GCS bucket signing")
+		return nil, err
+	}
+
+	return &GCSBackend{
+		bucket:  bucket,
+		handle:  handle,
+		signing: signing,
+	}, nil
+}
+
+func (b *GCSBackend) Name() string {
+	return "Google Cloud Storage (" + b.bucket + ")"
+}
+
+func (b *GCSBackend) Persist(ctx context.Context, path, contentType string, f Persister) (string, int64, error) {
+	obj := b.handle.Object(path)
+	w := obj.NewWriter(ctx)
+
+	hash, size, err := f(w)
+	if err != nil {
+		log.WithError(err).WithField("path", path).Error("failed to write to GCS")
+		return hash, size, err
+	}
+
+	err = w.Close()
+	if err != nil {
+		log.WithError(err).WithField("path", path).Error("failed to complete GCS upload")
+		return hash, size, err
+	}
+
+	// GCS natively supports content types for objects, which will be
+	// used when serving them back.
+	if contentType != "" {
+		_, err = obj.Update(ctx, storage.ObjectAttrsToUpdate{
+			ContentType: contentType,
+		})
+
+		if err != nil {
+			log.WithError(err).WithField("path", path).Error("failed to update object attrs")
+			return hash, size, err
+		}
+	}
+
+	return hash, size, nil
+}
+
+func (b *GCSBackend) Fetch(ctx context.Context, path string) (io.ReadCloser, error) {
+	obj := b.handle.Object(path)
+
+	// Probe whether the file exists before trying to fetch it
+	_, err := obj.Attrs(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	return obj.NewReader(ctx)
+}
+
+// renameObject renames an object in the specified Cloud Storage
+// bucket.
+//
+// The Go API for Cloud Storage does not support renaming objects, but
+// the HTTP API does. The code below makes the relevant call manually.
+func (b *GCSBackend) Move(ctx context.Context, old, new string) error {
+	creds, err := google.FindDefaultCredentials(ctx, gcsScope)
+	if err != nil {
+		return err
+	}
+
+	token, err := creds.TokenSource.Token()
+	if err != nil {
+		return err
+	}
+
+	// as per https://cloud.google.com/storage/docs/renaming-copying-moving-objects#rename
+	url := fmt.Sprintf(
+		"https://www.googleapis.com/storage/v1/b/%s/o/%s/rewriteTo/b/%s/o/%s",
+		url.PathEscape(b.bucket), url.PathEscape(old),
+		url.PathEscape(b.bucket), url.PathEscape(new),
+	)
+
+	req, err := http.NewRequest("POST", url, nil)
+	req.Header.Add("Authorization", "Bearer "+token.AccessToken)
+	_, err = client.Do(req)
+	if err != nil {
+		return err
+	}
+
+	// It seems that 'rewriteTo' copies objects instead of
+	// renaming/moving them, hence a deletion call afterwards is
+	// required.
+	if err = b.handle.Object(old).Delete(ctx); err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"new": new,
+			"old": old,
+		}).Warn("failed to delete renamed object")
+
+		// this error should not break renaming and is not returned
+	}
+
+	return nil
+}
+
+func (b *GCSBackend) Serve(digest string, r *http.Request, w http.ResponseWriter) error {
+	url, err := b.constructLayerUrl(digest)
+	if err != nil {
+		log.WithError(err).WithFields(log.Fields{
+			"digest": digest,
+			"bucket": b.bucket,
+		}).Error("failed to sign GCS URL")
+
+		return err
+	}
+
+	log.WithField("digest", digest).Info("redirecting blob request to GCS bucket")
+
+	w.Header().Set("Location", url)
+	w.WriteHeader(303)
+	return nil
+}
+
+// Configure GCS URL signing in the presence of a service account key
+// (toggled if the user has set GOOGLE_APPLICATION_CREDENTIALS).
+func signingOptsFromEnv() (*storage.SignedURLOptions, error) {
+	path := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")
+	if path == "" {
+		// No credentials configured -> no URL signing
+		return nil, nil
+	}
+
+	key, err := ioutil.ReadFile(path)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read service account key: %s", err)
+	}
+
+	conf, err := google.JWTConfigFromJSON(key)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse service account key: %s", err)
+	}
+
+	log.WithField("account", conf.Email).Info("GCS URL signing enabled")
+
+	return &storage.SignedURLOptions{
+		Scheme:         storage.SigningSchemeV4,
+		GoogleAccessID: conf.Email,
+		PrivateKey:     conf.PrivateKey,
+		Method:         "GET",
+	}, nil
+}
+
+// layerRedirect constructs the public URL of the layer object in the Cloud
+// Storage bucket, signs it and redirects the user there.
+//
+// Signing the URL allows unauthenticated clients to retrieve objects from the
+// bucket.
+//
+// In case signing is not configured, a redirect to storage.googleapis.com is
+// issued, which means the underlying bucket objects need to be publicly
+// accessible.
+//
+// The Docker client is known to follow redirects, but this might not be true
+// for all other registry clients.
+func (b *GCSBackend) constructLayerUrl(digest string) (string, error) {
+	log.WithField("layer", digest).Info("redirecting layer request to bucket")
+	object := "layers/" + digest
+
+	if b.signing != nil {
+		opts := *b.signing
+		opts.Expires = time.Now().Add(5 * time.Minute)
+		return storage.SignedURL(b.bucket, object, &opts)
+	} else {
+		return ("https://storage.googleapis.com/" + b.bucket + "/" + object), nil
+	}
+}
diff --git a/tools/nixery/storage/storage.go b/tools/nixery/storage/storage.go
new file mode 100644
index 0000000000..5500d61640
--- /dev/null
+++ b/tools/nixery/storage/storage.go
@@ -0,0 +1,40 @@
+// Copyright 2022 The TVL Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// Package storage implements an interface that can be implemented by
+// storage backends, such as Google Cloud Storage or the local
+// filesystem.
+package storage
+
+import (
+	"context"
+	"io"
+	"net/http"
+)
+
+type Persister = func(io.Writer) (string, int64, error)
+
+type Backend interface {
+	// Name returns the name of the storage backend, for use in
+	// log messages and such.
+	Name() string
+
+	// Persist provides a user-supplied function with a writer
+	// that stores data in the storage backend.
+	//
+	// It needs to return the SHA256 hash of the data written as
+	// well as the total number of bytes, as those are required
+	// for the image manifest.
+	Persist(ctx context.Context, path, contentType string, f Persister) (string, int64, error)
+
+	// Fetch retrieves data from the storage backend.
+	Fetch(ctx context.Context, path string) (io.ReadCloser, error)
+
+	// Move renames a path inside the storage backend. This is
+	// used for staging uploads while calculating their hashes.
+	Move(ctx context.Context, old, new string) error
+
+	// Serve provides a handler function to serve HTTP requests
+	// for objects in the storage backend.
+	Serve(digest string, r *http.Request, w http.ResponseWriter) error
+}
diff --git a/tools/nixery/web/index.html b/tools/nixery/web/index.html
new file mode 100644
index 0000000000..354c4913b2
--- /dev/null
+++ b/tools/nixery/web/index.html
@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <meta name="description" content="The Virus Lounge">
+  <link rel="stylesheet" type="text/css" href="https://static.tvl.fyi/latest/tvl.css" media="all">
+  <link rel="icon" type="image/webp" href="/favicon.webp">
+  <title>Nixery</title>
+</head>
+<body class="light">
+  <img src="./nixery-logo.png" alt="Nixery">
+  <hr>
+
+  <p>
+    Welcome to this instance of Nixery, an ad-hoc container image registry that provides
+    packages from the <a href="https://nixos.org/nix">Nix</a> package manager.
+  </p>
+
+  <p>
+    You can pull container images from this registry
+    at <code><span class="registry-hostname">nixery.dev</span></code> by appending any
+    packages that you need in the URL, separated by slashes.
+  </p>
+
+  <noscript>
+    <p class="cheddar-callout cheddar-tip">
+      <strong>NOTE:</strong> When pulling from a private Nixery instance,
+      replace <code>nixery.dev</code> in the above examples with your registry address.
+    </p>
+  </noscript>
+
+  <h2><a href="#demo" aria-hidden="true" class="anchor" id="demo"></a>Demo</h2>
+
+  <noscript>
+    <p>
+      The interactive demo needs Javascript to run, but you can just read the Usage
+      instructions below instead
+    </p>
+  </noscript>
+
+  <script src="https://asciinema.org/a/262583.js" id="asciicast-262583" async data-autoplay="true" data-loop="true"></script>
+
+  <h2><a href="#usage" aria-hidden="true" class="anchor" id="usage"></a>Usage</h2>
+
+  <p>
+    These usage examples assume that you use Docker, but should not be much different for
+    other OCI-compatible platforms.
+  </p>
+
+  <p>
+    Pull an image from this registry, separating each package you want included by a
+    slash:
+  </p>
+
+  <pre style="background-color:#f6f8fa;padding:16px;"><span style="color:#323232;">docker pull <span class="registry-hostname">nixery.dev</span>/shell/git/htop</span></pre>
+
+  <p>
+    This gives you an image with <code>git</code>, <code>htop</code> and an interactively
+    configured shell. You could run it like this:
+  </p>
+
+  <pre style="background-color:#f6f8fa;padding:16px;"><span style="color:#323232;">docker run -ti <span class="registry-hostname">nixery.dev</span>/shell/git/htop bash</span></pre>
+
+  <p>
+    Each path segment corresponds either to a key in the Nix package set, or a
+    meta-package that automatically expands to several other packages.
+  </p>
+
+  <p>
+    Meta-packages <strong>must</strong> be the first path component if they are used.
+    Currently there are only two meta-packages:
+  </p>
+
+  <ul>
+    <li>
+      <p>
+        <code>shell</code>, which provides a <code>bash</code>-shell with interactive
+        configuration and standard tools like <code>coreutils</code></p>
+    </li>
+    <li>
+      <p><code>arm64</code>, which provides ARM64 binaries</p>
+    </li>
+  </ul>
+
+  <h2><a href="#faq" aria-hidden="true" class="anchor" id="faq"></a>FAQ</h2>
+
+  <h3>
+    <a href="#how-does-this-work" aria-hidden="true" class="anchor" id="how-does-this-work"></a>
+    How does this work?
+  </h3>
+
+  <p>
+    The short version is that we use the Nix package manager and an optimised
+    <a href="https://tazj.in/blog/nixery-layers">layering strategy</a>.
+  </p>
+
+  <p>
+    Check out <a href="https://www.youtube.com/watch?v=pOI9H4oeXqA">the Nixery talk</a>
+    from NixCon 2019 for more information.
+  </p>
+
+  <h3>
+    <a href="#should-i-depend-on-nixerydev-in-production" aria-hidden="true" class="anchor" id="should-i-depend-on-nixerydev-in-production"></a>
+    Should I depend on <code>nixery.dev</code> in production?
+  </h3>
+
+  <p>
+    While we appreciate the enthusiasm, if you would like to use Nixery in your production
+    project we recommend setting up a private instance. The public Nixery
+    at <code>nixery.dev</code> is run on a best-effort basis and we make no guarantees
+    about availability.
+  </p>
+
+  <h3>
+    <a href="#who-made-this" aria-hidden="true" class="anchor" id="who-made-this"></a>
+    Who made this?
+  </h3>
+
+  <p>
+    Nixery was written by <a href="https://tazj.in">tazjin</a>, originally at Google.
+    These days Nixery is maintained by <a href="https://tvl.su">TVL</a>.
+  </p>
+  <p>
+    Nixery would not be possible without the many people that have contributed to Nix and
+    nixpkgs over time, maybe you could become one of them?
+  </p>
+
+  <h3>
+    <a href="#where-is-the-source-code-for-this" aria-hidden="true" class="anchor" id="where-is-the-source-code-for-this"></a>
+    Where is the source code for this?
+  </h3>
+
+  <p>
+    Nixery lives in the <a href="https://cs.tvl.fyi/depot/-/tree/tools/nixery">TVL
+      monorepo</a>. All development happens there and follows
+      the <a href="https://cs.tvl.fyi/depot/-/blob/docs/CONTRIBUTING.md">TVL contribution
+      guidelines</a>.
+  </p>
+
+  <p>
+    We <em>mirror</em> the source code <a href="https://github.com/tazjin/nixery">to
+    Github</a> but do not guarantee that anyone will look at PRs or issues there.
+  </p>
+
+  <hr>
+  <footer>
+    <p class="footer">
+      <a class="uncoloured-link" href="https://at.tvl.fyi/?q=//tools/nixery">code</a>
+      |
+      <a class="uncoloured-link" href="https://cl.tvl.fyi/q/file:%2522%255Etools/nixery/.*%2522">reviews</a>
+      |
+      <a class="uncoloured-link" href="https://b.tvl.fyi/">bugs</a>
+    </p>
+    <p class="lod">ΰ² _ΰ² </p>
+  </footer>
+
+  <script>
+    /* Replace the hostnames above with the one at which this page runs. */
+    let hostname = window.location.hostname;
+    if (hostname != '') {
+        for (span of document.getElementsByClassName("registry-hostname")) {
+            span.textContent = hostname;
+        }
+    }
+  </script>
+</body>
diff --git a/tools/nixery/web/nixery-logo.png b/tools/nixery/web/nixery-logo.png
new file mode 100644
index 0000000000..fcf77df3d6
--- /dev/null
+++ b/tools/nixery/web/nixery-logo.png
Binary files differdiff --git a/tools/nsfv-setup/default.nix b/tools/nsfv-setup/default.nix
index 98dcc61b7b..1e353e3269 100644
--- a/tools/nsfv-setup/default.nix
+++ b/tools/nsfv-setup/default.nix
@@ -15,7 +15,8 @@
 let
   inherit (pkgs) ripgrep pulseaudio;
   inherit (depot.third_party) nsfv;
-in pkgs.writeShellScriptBin "nsfv-setup" ''
+in
+pkgs.writeShellScriptBin "nsfv-setup" ''
   export PATH="${ripgrep}/bin:${pulseaudio}/bin:$PATH"
 
   if pacmd list-sinks | rg librnnoise_ladspa.so >/dev/null; then
diff --git a/tools/releases/default.nix b/tools/releases/default.nix
new file mode 100644
index 0000000000..0df07bbc9c
--- /dev/null
+++ b/tools/releases/default.nix
@@ -0,0 +1,37 @@
+# Definitions for simple release mechanisms from depot.
+{ depot, lib, pkgs, ... }:
+
+let
+  inherit (lib.strings) makeBinPath sanitizeDerivationName;
+in
+{
+  # Use a josh filter to push a certain subset of canon to another git
+  # repository.
+  #
+  # This expects, of course, that the remote repository has granted
+  # push access to the CI SSH key.
+  filteredGitPush = { filter, remote, ref ? "refs/heads/canon" }: {
+    label = ":git: push '${filter}' to external git repository";
+    branches = [ "refs/heads/canon" ];
+    phase = "release";
+
+    command = pkgs.writeShellScript "${sanitizeDerivationName filter}-push" ''
+      set -e
+      export PATH="${makeBinPath [ pkgs.git pkgs.josh ]}:$PATH"
+
+      echo 'Filtering depot through ${filter}'
+      josh-filter '${filter}'
+
+      echo 'Fetching remote to check if a push is needed'
+      git fetch '${remote}' '${ref}'
+
+      if git merge-base --is-ancestor FILTERED_HEAD FETCH_HEAD; then
+        echo 'Commit already present, nothing to push.'
+        exit 0
+      fi
+
+      echo 'Pushing filtered repository to ${remote}:${ref}'
+      git push '${remote}' 'FILTERED_HEAD:${ref}'
+    '';
+  };
+}
diff --git a/tools/rust-crates-advisory/OWNERS b/tools/rust-crates-advisory/OWNERS
index a742d0d22b..5f87d2f271 100644
--- a/tools/rust-crates-advisory/OWNERS
+++ b/tools/rust-crates-advisory/OWNERS
@@ -1,3 +1,2 @@
-inherited: true
-owners:
-  - Profpatsch
+Profpatsch
+sterni
diff --git a/tools/rust-crates-advisory/check-security-advisory.rs b/tools/rust-crates-advisory/check-security-advisory.rs
deleted file mode 100644
index 3fd9bc2dd9..0000000000
--- a/tools/rust-crates-advisory/check-security-advisory.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-extern crate semver;
-extern crate toml;
-
-use std::io::Write;
-
-/// reads a security advisory of the form
-/// https://github.com/RustSec/advisory-db/blob/a24932e220dfa9be8b0b501210fef8a0bc7ef43e/EXAMPLE_ADVISORY.md
-/// and a crate version number,
-/// and returns 0 if the crate version is patched
-/// and returns 1 if the crate version is *not* patched
-///
-/// If PRINT_ADVISORY is set, the advisory is printed if it matches.
-
-fn main() {
-    let mut args = std::env::args_os();
-    let file = args.nth(1).expect("security advisory md file is $1");
-    let crate_version =
-        args.nth(0).expect("crate version is $2")
-        .into_string().expect("crate version string not utf8")
-        ;
-    let crate_version = semver::Version::parse(&crate_version).expect(&format!("this is not a semver version: {}", &crate_version));
-    let filename = file.to_string_lossy();
-
-    let content = std::fs::read(&file).expect(&format!("could not read {}", filename));
-    let content =
-        std::str::from_utf8(&content).expect(&format!("file {} was not encoded as utf-8", filename));
-    let content = content.trim_start();
-
-    let toml_start = content
-        .strip_prefix("```toml").expect(&format!("file did not start with ```toml: {}", filename));
-    let toml_end_index = toml_start.find("```").expect(&format!("the toml section did not end, no `` found: {}", filename));
-    let toml = &toml_start[..toml_end_index];
-    let toml : toml::Value = toml::de::from_slice(toml.as_bytes()).expect(&format!("could not parse toml: {}", filename));
-
-    let versions = toml
-        .as_table().expect(&format!("the toml is not a table: {}", filename))
-        .get("versions").expect(&format!("the toml does not contain the versions field: {}", filename))
-        .as_table().expect(&format!("the toml versions field must be a table: {}", filename));
-
-    let unaffected = match versions.get("unaffected") {
-        Some(u) => u
-            .as_array().expect(&format!("the toml versions.unaffected field must be a list of semvers: {}", filename))
-            .iter()
-            .map(|v| semver::VersionReq::parse(v.as_str().expect(&format!("the version field {} is not a string", v))).expect(&format!("the version field {} is not a valid semver VersionReq", v)))
-            .collect(),
-        None => vec![]
-    };
-
-    let mut patched : Vec<semver::VersionReq> = versions.get("patched").expect(&format!("the toml versions.patched field must exist: {}", filename))
-        .as_array().expect(&format!("the toml versions.patched field must be a list of semvers: {}", filename))
-        .iter()
-        .map(|v| semver::VersionReq::parse(v.as_str().expect(&format!("the version field {} is not a string", v))).expect(&format!("the version field {} is not a valid semver VersionReq", v)))
-        .collect();
-
-    patched.extend_from_slice(&unaffected[..]);
-    let is_patched_or_unaffected = patched.iter().any(|req| req.matches(&crate_version));
-
-    if is_patched_or_unaffected {
-        std::process::exit(0);
-    } else {
-        if std::env::var_os("PRINT_ADVISORY").is_some() {
-            write!(std::io::stderr(), "Advisory {} matched!\n{}\n", filename, content).unwrap();
-        }
-        std::process::exit(1);
-    }
-
-}
diff --git a/tools/rust-crates-advisory/default.nix b/tools/rust-crates-advisory/default.nix
index c0cd4dc03e..da7eb75447 100644
--- a/tools/rust-crates-advisory/default.nix
+++ b/tools/rust-crates-advisory/default.nix
@@ -3,88 +3,119 @@
 let
 
   bins =
-       depot.nix.getBins pkgs.s6-portable-utils [ "s6-ln" "s6-cat" "s6-echo" "s6-mkdir" "s6-test" "s6-touch" ]
-    // depot.nix.getBins pkgs.lr [ "lr" ]
-    ;
-
-  crate-advisories = "${depot.third_party.rustsec-advisory-db}/crates";
+    depot.nix.getBins pkgs.cargo-audit [ "cargo-audit" ]
+    // depot.nix.getBins pkgs.jq [ "jq" ]
+    // depot.nix.getBins pkgs.findutils [ "find" ]
+    // depot.nix.getBins pkgs.gnused [ "sed" ]
+  ;
 
   our-crates = lib.filter (v: v ? outPath)
     (builtins.attrValues depot.third_party.rust-crates);
 
-  check-security-advisory = depot.nix.writers.rustSimple {
-    name = "parse-security-advisory";
-    dependencies = [
-      depot.third_party.rust-crates.toml
-      depot.third_party.rust-crates.semver
-    ];
-  } (builtins.readFile ./check-security-advisory.rs);
-
-  # $1 is the directory with advisories for crate $2 with version $3
-  check-crate-advisory = depot.nix.writeExecline "check-crate-advisory" { readNArgs = 3; } [
-    "pipeline" [ bins.lr "-0" "-t" "depth == 1" "$1" ]
-    "forstdin" "-0" "-Eo" "0" "advisory"
-    "if" [ depot.tools.eprintf "advisory %s\n" "$advisory" ]
-    check-security-advisory "$advisory" "$3"
-  ];
-
-  # Run through everything in the `crate-advisories` repository
-  # and check whether we can parse all the advisories without crashing.
-  test-parsing-all-security-advisories = depot.nix.runExecline "check-all-our-crates" {} [
-    "pipeline" [ bins.lr "-0" "-t" "depth == 1" crate-advisories ]
-    "if" [
-      # this will succeed as long as check-crate-advisory doesn’t `panic!()` (status 101)
-      "forstdin" "-0" "-E" "-x" "101" "crate_advisories"
-      check-crate-advisory "$crate_advisories" "foo" "0.0.0"
-    ]
-    "importas" "out" "out"
-    bins.s6-touch "$out"
-  ];
-
-
-  check-all-our-crates = depot.nix.runExecline "check-all-our-crates" {
-    stdin = lib.concatStrings
-      (map
-        (crate:
-          depot.nix.netstring.fromString
-            ( depot.nix.netstring.fromString crate.crateName
-            + depot.nix.netstring.fromString crate.version ))
-        our-crates);
-  } [
-    "if" [
-      "forstdin" "-o" "0" "-Ed" "" "crateNetstring"
-      "multidefine" "-d" "" "$crateNetstring" [ "crate" "crate_version" ]
-      "if" [ depot.tools.eprintf "checking %s, version %s\n" "$crate" "$crate_version" ]
-
-      "ifthenelse" [ bins.s6-test "-d" "${crate-advisories}/\${crate}" ]
-          [ # also print the full advisory text if it matches
-            "export" "PRINT_ADVISORY" "1"
-            check-crate-advisory "${crate-advisories}/\${crate}" "$crate" "$crate_version"
-          ]
-        [ depot.tools.eprintf "No advisories found for crate %s\n" "$crate" ]
-        "importas" "-ui" "ret" "?"
-        # put a marker in ./failed to read at the end
-        "ifelse" [ bins.s6-test "$ret" "-eq" "1" ]
-          [ bins.s6-touch "./failed" ]
-        "if" [ depot.tools.eprintf "\n" ]
-        "exit" "$ret"
-    ]
-    "ifelse" [ bins.s6-test "-f" "./failed" ]
-      [ "if" [ depot.tools.eprintf "Error: Found active advisories!" ]
-        "exit" "1"
-      ]
-    "importas" "out" "out"
-    bins.s6-touch "$out"
-  ];
-
-in depot.nix.readTree.drvTargets {
-
-  check-all-our-crates =
-    depot.nix.drvSeqL
-      [ test-parsing-all-security-advisories ]
-      check-all-our-crates;
+  our-crates-lock-file = pkgs.writeText "our-crates-Cargo.lock"
+    (lib.concatMapStrings
+      (crate: ''
+        [[package]]
+        name = "${crate.crateName}"
+        version = "${crate.version}"
+        source = "registry+https://github.com/rust-lang/crates.io-index"
+
+      '')
+      our-crates);
+
+  lock-file-report = pkgs.writers.writeBash "lock-file-report" ''
+    set -u
+
+    if test "$#" -lt 2; then
+      echo "Usage: $0 IDENTIFIER LOCKFILE [CHECKLIST [MAINTAINERS]]" >&2
+      echo 2>&1
+      echo "  IDENTIFIER  Unique string describing the lock file" >&2
+      echo "  LOCKFILE    Path to Cargo.lock file" >&2
+      echo "  CHECKLIST   Whether to use GHFM checklists in the output (true or false)" >&2
+      echo "  MAINTAINERS List of @names to cc in case of advisories" >&2
+      exit 100
+    fi
+
+    "${bins.cargo-audit}" audit --json --no-fetch \
+      --db "${depot.third_party.rustsec-advisory-db}" \
+      --file "$2" \
+    | "${bins.jq}" --raw-output --join-output \
+      --from-file "${./format-audit-result.jq}" \
+      --arg maintainers "''${4:-}" \
+      --argjson checklist "''${3:-false}" \
+      --arg attr "$1"
+
+    exit "''${PIPESTATUS[0]}" # inherit exit code from cargo-audit
+  '';
+
+  tree-lock-file-report = pkgs.writers.writeBash "tree-lock-file-report" ''
+    set -euo pipefail
+    status=0
+
+    root="''${1:-.}"
+
+    # Find prints the found lockfiles as <DEPOT ROOT>\t<LOCKFILE DIR>\t<LOCKFILE PATH>\0
+    while IFS=$'\t' read -r -d $'\0' entryPoint dir lockFile; do
+      label="$(printf '%s' "$dir" | "${bins.sed}" "s|^$entryPoint|/|")"
+      "${lock-file-report}" "$label" "$lockFile" || status=1
+    done < <("${bins.find}" "$root" -type f -name Cargo.lock -printf '%H\t%h\t%p\0' )
 
+    exit $status
+  '';
+
+  depot-rust-crates-advisory-report = pkgs.writers.writeBash "depot-advisory-report" ''
+    set -eu
+    status=0
+
+    "${lock-file-report}" "//third_party/rust-crates" "${our-crates-lock-file}" || status=1
+    "${tree-lock-file-report}" || status=1
+
+    exit $status
+  '';
+
+  buildkiteReportStep =
+    { command
+    , context ? null
+    , style ? "warning"
+    }:
+    let
+      commandName = depot.nix.utils.storePathName (builtins.head command);
+    in
+
+    pkgs.writers.writeBash "buildkite-report-${commandName}" ''
+      set -uo pipefail
+
+      report="$(${lib.escapeShellArgs command})"
+
+      if test $? -ne 0; then
+         printf "%s" "$report" | \
+         buildkite-agent annotate ${
+           lib.escapeShellArgs ([
+             "--style"
+             style
+           ] ++ lib.optionals (context != null) [
+             "--context"
+             context
+           ])
+         }
+      fi
+    '';
+
+in
+depot.nix.readTree.drvTargets {
   inherit
-    check-crate-advisory
+    lock-file-report
     ;
+
+  tree-lock-file-report = tree-lock-file-report // {
+    meta.ci.extraSteps.run = {
+      label = "Check all crates used in depot for advisories";
+      alwaysRun = true;
+      command = buildkiteReportStep {
+        command = [ depot-rust-crates-advisory-report ];
+        style = "warning";
+        context = "depot-crate-advisories";
+      };
+    };
+  };
 }
diff --git a/tools/rust-crates-advisory/format-audit-result.jq b/tools/rust-crates-advisory/format-audit-result.jq
new file mode 100644
index 0000000000..d42ff6e55c
--- /dev/null
+++ b/tools/rust-crates-advisory/format-audit-result.jq
@@ -0,0 +1,75 @@
+# This is a jq script to format the JSON output of cargo-audit into a short
+# markdown report for humans. It is used by //users/sterni/nixpkgs-crate-holes
+# and //tools/rust-crates-advisory:check-all-our-lock-files which will provide
+# you with example invocations.
+#
+# It needs the following arguments passed to it:
+#
+# - maintainers: Either the empty string or a list of maintainers to @mention
+#   for the current lock file.
+# - attr: An attribute name (or otherwise unique identifier) to associate the
+#   report for the current lock file with.
+# - checklist: If true, the markdown report will use GHFM checklists for the
+#   report, allowing to tick of attributes as taken care of.
+
+# Link to human-readable advisory info for a given vulnerability
+def link:
+  [ "https://rustsec.org/advisories/", .advisory.id, ".html" ] | add;
+
+# Format a list of version constraints
+def version_list:
+  [ .[] | "`" + . + "`" ] | join("; ");
+
+# show paths to fixing this vulnerability:
+#
+# - if there are patched releases, show them (the version we are using presumably
+#   predates the vulnerability discovery, so we likely want to upgrade to a
+#   patched release).
+# - if there are no patched releases, show the unaffected versions (in case we
+#   want to downgrade).
+# - otherwise we state that no unaffected versions are available at this time.
+#
+# This logic should be useful, but is slightly dumber than cargo-audit's
+# suggestion when using the non-JSON output.
+def patched:
+  if .versions.patched == [] then
+    if .versions.unaffected != [] then
+       "unaffected: " + (.versions.unaffected | version_list)
+    else
+      "no unaffected version available"
+    end
+  else
+    "patched: " + (.versions.patched | version_list)
+  end;
+
+# if the vulnerability has aliases (like CVE-*) emit them in parens
+def aliases:
+  if .advisory.aliases == [] then
+    ""
+  else
+    [ " (", (.advisory.aliases | join(", ")), ")" ] | add
+  end;
+
+# each vulnerability is rendered as a (normal) sublist item
+def format_vulnerability:
+  [ "  - "
+  , .package.name, " ", .package.version, ": "
+  , "[", .advisory.id, "](", link, ")"
+  , aliases
+  , ", ", patched
+  , "\n"
+  ] | add;
+
+# be quiet if no found vulnerabilities, otherwise render a GHFM checklist item
+if .vulnerabilities.found | not then
+  ""
+else
+  ([ "-", if $checklist then " [ ] " else " " end
+   , "`", $attr, "`: "
+   , (.vulnerabilities.count | tostring)
+   , " advisories for Cargo.lock"
+   , if $maintainers != "" then " (cc " + $maintainers + ")" else "" end
+   , "\n"
+   ] + (.vulnerabilities.list | map(format_vulnerability))
+  ) | add
+end
diff --git a/tools/tvlc/OWNERS b/tools/tvlc/OWNERS
deleted file mode 100644
index 9e7830ab21..0000000000
--- a/tools/tvlc/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherited: true
-owners:
- - riking
diff --git a/tools/tvlc/common.sh b/tools/tvlc/common.sh
deleted file mode 100644
index fe7605857f..0000000000
--- a/tools/tvlc/common.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-
-set -eu
-set -o pipefail
-
-source path-scripts
-
-XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
-tvlc_root="$XDG_DATA_HOME/tvlc"
-
-nice_checkout_root=
-if [ -f "$tvlc_root"/nice_checkout_root ]; then
-  nice_checkout_root="$(cat "$tvlc_root"/nice_checkout_root)"
-fi
-nice_checkout_root="${nice_checkout_root:-$HOME/tvlc}"
-
-depot_root=
-if [ -f "$tvlc_root/depot_root" ]; then
-  depot_root="$(cat "$tvlc_root/depot_root")"
-fi
-if [ -d /depot ]; then
-  # don't require config on tvl nixos servers
-  depot_root="${depot_root:-/depot}"
-fi
-if [ -n "$depot_root" ]; then
-  export DEPOT_ROOT="$depot_root"
-fi
-
-if [ ! -d "$tvlc_root" ]; then
-  echo "tvlc: setup required"
-  echo "please run 'tvlc setup' from the depot root"
-  exit 1
-fi
diff --git a/tools/tvlc/default.nix b/tools/tvlc/default.nix
deleted file mode 100644
index f40f30a44e..0000000000
--- a/tools/tvlc/default.nix
+++ /dev/null
@@ -1,50 +0,0 @@
-{ pkgs, depot, ... }:
-
-let
-  pathScripts = pkgs.writeShellScript "imports" ''
-    export tvix_instantiate="${depot.third_party.nix}/bin/nix-instantiate"
-    export depot_scanner="${depot.tools.depot-scanner}/bin/depot-scanner"
-  '';
-
-  # setup: git rev-parse --show-toplevel > $tvlc_root/depot_root
-  # setup: mkdir $tvlc_root/clients
-  # setup: echo 1 > $tvlc_root/next_clientid
-
-  commonsh = pkgs.stdenv.mkDerivation {
-    name = "common.sh";
-    src = ./common.sh;
-    doCheck = true;
-    unpackPhase = "true";
-    buildPhase = ''
-      substitute ${./common.sh} $out --replace path-scripts ${pathScripts}
-    '';
-    checkPhase = ''
-      ${pkgs.shellcheck}/bin/shellcheck $out ${pathScripts} && echo "SHELLCHECK OK"
-    '';
-    installPhase = ''
-      chmod +x $out
-    '';
-  };
-
-  tvlcNew = pkgs.stdenv.mkDerivation {
-    name = "tvlc-new";
-    src = ./tvlc-new;
-    doCheck = true;
-
-    unpackPhase = "true";
-    buildPhase = ''
-      substitute ${./tvlc-new} $out --replace common.sh ${commonsh}
-    '';
-    checkPhase = ''
-      ${pkgs.shellcheck}/bin/shellcheck $out ${commonsh} ${pathScripts} && echo "SHELLCHECK OK"
-    '';
-    installPhase = ''
-      chmod +x $out
-    '';
-  };
-
-in {
-  inherit pathScripts;
-  inherit commonsh;
-  inherit tvlcNew;
-}
diff --git a/tools/tvlc/tvlc-new b/tools/tvlc/tvlc-new
deleted file mode 100755
index 4ef0df5d33..0000000000
--- a/tools/tvlc/tvlc-new
+++ /dev/null
@@ -1,103 +0,0 @@
-#!/bin/bash
-
-source common.sh
-
-set -eu
-set -o pipefail
-
-function usage() {
-  echo "tvlc new [-n|--name CLIENTNAME] [derivation...]"
-  echo ""
-  cat <<EOF
-  The 'new' command creates a new git sparse checkout with the given name, and
-  contents needed to build the Nix derivation(s) specified on the command line.
-
-  Options:
-    -n/--name client-name: Sets the git branch and nice checkout name for the
-	workspace. If the option is not provided, the name will be based on the
-	first non-option command-line argument.
-    --branch branch-name: Sets the git branch name only.
-EOF
-}
-
-checkout_name=
-branch_name=
-
-options=$(getopt -o 'n:' --long debug --long name: -- "$@")
-eval set -- "$options"
-while true; do
-  case "$1" in
-  -h)
-    usage
-    exit 0
-    ;;
-  -v)
-    version
-    exit 0
-    ;;
-  -n|--name)
-    shift
-    checkout_name="$1"
-    if [ -z "$branch_name" ]; then
-      branch_name=tvlc-"$1"
-    fi
-    ;;
-  --branch)
-    shift
-    branch_name="$1"
-    ;;
-  --)
-    shift
-    break
-    ;;
-  esac
-  shift
-done
-
-if [ $# -eq 0 ]; then
-  echo "error: workspace name, target derivations required"
-  exit 1
-fi
-
-if [ -z "$checkout_name" ]; then
-  # TODO(riking): deduce
-  echo "error: workspace name (-n) required"
-  exit 1
-fi
-
-if [ -d "$nice_checkout_root/$checkout_name" ]; then
-  echo "error: checkout $checkout_name already exists"
-  # nb: shellescape checkout_name because we expect the user to copy-paste it
-  # shellcheck disable=SC1003
-  echo "consider deleting it with tvlc remove '${checkout_name/'/\'}'"
-  exit 1
-fi
-if [ -f "$DEPOT_ROOT/.git/refs/heads/$branch_name" ]; then
-  echo "error: branch $branch_name already exists in git"
-  # shellcheck disable=SC1003
-  echo "consider deleting it with cd $DEPOT_ROOT; git branch -d '${checkout_name/'/\'}'"
-  exit 1
-fi
-
-# The big one: call into Nix to figure out what paths the desired derivations depend on.
-readarray -t includedPaths < <("$depot_scanner" --mode 'print' --only 'DEPOT' --relpath --depot "$DEPOT_ROOT" --nix-bin "$tvix_instantiate" "$@")
-
-# bash math
-checkout_id=$(("$(cat "$tvlc_root/next_clientid")"))
-next_checkout_id=$(("$checkout_id"+1))
-echo "$next_checkout_id" > "$tvlc_root/next_clientid"
-
-checkout_dir="$tvlc_root/clients/$checkout_id"
-mkdir "$checkout_dir"
-cd "$DEPOT_ROOT"
-git worktree add --no-checkout -b "$branch_name" "$checkout_dir"
-# BUG: git not creating the /info/ subdir
-mkdir "$DEPOT_ROOT/.git/worktrees/$checkout_id/info"
-
-cd "$checkout_dir"
-git sparse-checkout init --cone
-git sparse-checkout set "${includedPaths[@]}"
-
-ln -s "$checkout_dir" "$nice_checkout_root"/"$checkout_name"
-
-echo "$nice_checkout_root/$checkout_name"
diff --git a/tvix/.envrc b/tvix/.envrc
deleted file mode 100644
index ea1ec94e43..0000000000
--- a/tvix/.envrc
+++ /dev/null
@@ -1,10 +0,0 @@
-source_env ../.envrc
-
-if type lorri &>/dev/null; then
-    echo "direnv: using lorri from PATH ($(type -p lorri))"
-    eval "$(lorri direnv)"
-else
-    # fall back to using direnv's builtin nix support
-    # to prevent bootstrapping problems.
-    use nix
-fi
diff --git a/tvix/.gitignore b/tvix/.gitignore
index f807dfa42c..e047e8af40 100644
--- a/tvix/.gitignore
+++ b/tvix/.gitignore
@@ -1,3 +1,6 @@
 /target
 /result-*
 /result
+target
+
+/*.sled
diff --git a/tvix/.vscode/extensions.json b/tvix/.vscode/extensions.json
index dd7012c107..0740550041 100644
--- a/tvix/.vscode/extensions.json
+++ b/tvix/.vscode/extensions.json
@@ -1,6 +1,6 @@
 {
     "recommendations": [
-        "matklad.rust-analyzer"
+        "rust-lang.rust-analyzer"
     ],
     "unwantedRecommendations": [
         "rust-lang.rust"
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock
index 15d97837e6..334b69b7f5 100644
--- a/tvix/Cargo.lock
+++ b/tvix/Cargo.lock
@@ -3,21 +3,461 @@
 version = 3
 
 [[package]]
-name = "atty"
-version = "0.2.14"
+name = "addr2line"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
 dependencies = [
- "hermit-abi",
  "libc",
- "winapi",
 ]
 
 [[package]]
+name = "anes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+
+[[package]]
+name = "anstream"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
+
+[[package]]
+name = "arc-swap"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
+
+[[package]]
+name = "arrayref"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+
+[[package]]
+name = "async-channel"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3"
+dependencies = [
+ "concurrent-queue",
+ "event-listener 5.2.0",
+ "event-listener-strategy 0.5.0",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-compression"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c"
+dependencies = [
+ "bzip2",
+ "flate2",
+ "futures-core",
+ "memchr",
+ "pin-project-lite",
+ "tokio",
+ "xz2",
+]
+
+[[package]]
+name = "async-io"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884"
+dependencies = [
+ "async-lock 3.3.0",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix",
+ "slab",
+ "tracing",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
+dependencies = [
+ "event-listener 2.5.3",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b"
+dependencies = [
+ "event-listener 4.0.3",
+ "event-listener-strategy 0.4.0",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8"
+dependencies = [
+ "async-channel",
+ "async-io",
+ "async-lock 3.3.0",
+ "async-signal",
+ "blocking",
+ "cfg-if",
+ "event-listener 5.2.0",
+ "futures-lite",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "async-recursion"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "async-signal"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5"
+dependencies = [
+ "async-io",
+ "async-lock 2.8.0",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "async-task"
+version = "4.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
+
+[[package]]
+name = "async-tempfile"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b37d4bb113c47e4f263d4b0221912ff5aa840a51bc9b7b47b024e1cf1926fd9b"
+dependencies = [
+ "tokio",
+ "uuid",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "axum"
+version = "0.6.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
+dependencies = [
+ "async-trait",
+ "axum-core 0.3.4",
+ "bitflags 1.3.2",
+ "bytes",
+ "futures-util",
+ "http 0.2.11",
+ "http-body 0.4.6",
+ "hyper 0.14.28",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "sync_wrapper",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e"
+dependencies = [
+ "async-trait",
+ "axum-core 0.4.3",
+ "bytes",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "http-body-util",
+ "hyper 1.2.0",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http 0.2.11",
+ "http-body 0.4.6",
+ "mime",
+ "rustversion",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bigtable_rs"
+version = "0.2.9"
+source = "git+https://github.com/flokli/bigtable_rs?rev=0af404741dfc40eb9fa99cf4d4140a09c5c20df7#0af404741dfc40eb9fa99cf4d4140a09c5c20df7"
+dependencies = [
+ "gcp_auth",
+ "http 0.2.11",
+ "log",
+ "prost 0.12.3",
+ "prost-build",
+ "prost-types",
+ "prost-wkt",
+ "prost-wkt-build",
+ "prost-wkt-types",
+ "serde",
+ "serde_with",
+ "thiserror",
+ "tokio",
+ "tonic 0.11.0",
+ "tonic-build",
+ "tower",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
 
 [[package]]
 name = "bitflags"
@@ -26,31 +466,982 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
+name = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[package]]
+name = "bitmaps"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403"
+
+[[package]]
+name = "blake3"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if",
+ "constant_time_eq",
+ "digest",
+ "rayon",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "blocking"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118"
+dependencies = [
+ "async-channel",
+ "async-lock 3.3.0",
+ "async-task",
+ "fastrand",
+ "futures-io",
+ "futures-lite",
+ "piper",
+ "tracing",
+]
+
+[[package]]
+name = "bstr"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
+dependencies = [
+ "memchr",
+ "regex-automata 0.4.3",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "bzip2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
+dependencies = [
+ "bzip2-sys",
+ "libc",
+]
+
+[[package]]
+name = "bzip2-sys"
+version = "0.1.11+1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "caps"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b"
+dependencies = [
+ "libc",
+ "thiserror",
+]
+
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "jobserver",
+ "libc",
+]
+
+[[package]]
 name = "cfg-if"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
+name = "chrono"
+version = "0.4.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "ciborium"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
+[[package]]
 name = "clap"
-version = "3.0.5"
+version = "4.4.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6f34b09b9ee8c7c7b400fe2f8df39cafc9538b03d6ba7f4ae13e4cb90bfbb7d"
+checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
 dependencies = [
- "atty",
- "bitflags",
- "indexmap",
- "os_str_bytes",
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
  "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
+name = "clipboard-win"
+version = "4.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
+dependencies = [
+ "error-code",
+ "str-buf",
+ "winapi",
+]
+
+[[package]]
+name = "codemap"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24"
+
+[[package]]
+name = "codemap-diagnostic"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc20770be05b566a963bf91505e60412c4a2d016d1ef95c5512823bb085a8122"
+dependencies = [
+ "codemap",
  "termcolor",
- "textwrap",
+]
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "concurrent-queue"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "constant_time_eq"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "count-write"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced507ab50aa0123e2c54db8b5f44fdfee04b1c93744d69e924307945fe57a85"
+
+[[package]]
+name = "countme"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "criterion"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
+dependencies = [
+ "anes",
+ "cast",
+ "ciborium",
+ "clap",
+ "criterion-plot",
+ "is-terminal",
+ "itertools 0.10.5",
+ "num-traits",
+ "once_cell",
+ "oorandom",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
+dependencies = [
+ "cast",
+ "itertools 0.10.5",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "platforms",
+ "rustc_version",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "darling"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
+
+[[package]]
+name = "der"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+ "serde",
+]
+
+[[package]]
+name = "diff"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "dirs"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "doc-comment"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+
+[[package]]
+name = "document-features"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "endian-type"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
+
+[[package]]
+name = "enum-primitive-derive"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c"
+dependencies = [
+ "num-traits",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "erased-serde"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "error-code"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
+dependencies = [
+ "libc",
+ "str-buf",
+]
+
+[[package]]
+name = "event-listener"
+version = "2.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+
+[[package]]
+name = "event-listener"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3"
+dependencies = [
+ "event-listener 4.0.3",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291"
+dependencies = [
+ "event-listener 5.2.0",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "fastcdc"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a71061d097bfa9a5a4d2efdec57990d9a88745020b365191d37e48541a1628f2"
+dependencies = [
+ "async-stream",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+
+[[package]]
+name = "fd-lock"
+version = "3.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
+dependencies = [
+ "cfg-if",
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7"
+
+[[package]]
+name = "filetime"
+version = "0.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.4.1",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "flate2"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "fs2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "fuse-backend-rs"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e5a63a89f40ec26a0a1434e89de3f4ee939a920eae15d641053ee09ee6ed44b"
+dependencies = [
+ "arc-swap",
+ "bitflags 1.3.2",
+ "caps",
+ "core-foundation-sys",
+ "lazy_static",
+ "libc",
+ "log",
+ "mio",
+ "nix 0.24.3",
+ "vhost",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-lite"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-timer"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "gcp_auth"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de2c71ea685b88a1aa50e9fb66fe0e1cb29d755f58cca41fb8c91ef604d4f4d4"
+dependencies = [
+ "async-trait",
+ "base64",
+ "chrono",
+ "home",
+ "hyper 0.14.28",
+ "hyper-rustls",
+ "ring",
+ "rustls 0.21.10",
+ "rustls-pemfile 1.0.4",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "tracing-futures",
+ "url",
+ "which 5.0.0",
+]
+
+[[package]]
+name = "genawaiter"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0"
+dependencies = [
+ "genawaiter-macro",
+]
+
+[[package]]
+name = "genawaiter-macro"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc"
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
 ]
 
 [[package]]
 name = "getrandom"
-version = "0.2.3"
+version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
 dependencies = [
  "cfg-if",
  "libc",
@@ -58,67 +1449,1409 @@ dependencies = [
 ]
 
 [[package]]
+name = "gimli"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "h2"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http 0.2.11",
+ "indexmap 2.1.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http 1.1.0",
+ "indexmap 2.1.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "half"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
+
+[[package]]
 name = "hashbrown"
-version = "0.11.2"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hex-literal"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
+
+[[package]]
+name = "home"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
 dependencies = [
- "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "http"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
+dependencies = [
+ "bytes",
+ "http 0.2.11",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
+dependencies = [
+ "bytes",
+ "http 1.1.0",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "hyper"
+version = "0.14.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2 0.3.24",
+ "http 0.2.11",
+ "http-body 0.4.6",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2 0.4.3",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
+dependencies = [
+ "futures-util",
+ "http 0.2.11",
+ "hyper 0.14.28",
+ "rustls 0.21.10",
+ "rustls-native-certs 0.6.3",
+ "tokio",
+ "tokio-rustls 0.24.1",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
+dependencies = [
+ "hyper 0.14.28",
+ "pin-project-lite",
+ "tokio",
+ "tokio-io-timeout",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.0",
+ "hyper 1.2.0",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "imbl"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978d142c8028edf52095703af2fad11d6f611af1246685725d6b850634647085"
+dependencies = [
+ "bitmaps",
+ "imbl-sized-chunks",
+ "proptest",
+ "rand_core",
+ "rand_xoshiro",
+ "serde",
+ "version_check",
+]
+
+[[package]]
+name = "imbl-sized-chunks"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "144006fb58ed787dcae3f54575ff4349755b00ccc99f4b4873860b654be1ed63"
+dependencies = [
+ "bitmaps",
 ]
 
 [[package]]
 name = "indexmap"
-version = "1.7.0"
+version = "1.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
  "autocfg",
- "hashbrown",
+ "hashbrown 0.12.3",
+ "serde",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.3",
+ "serde",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "inventory"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767"
+
+[[package]]
+name = "ipnet"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+
+[[package]]
+name = "is-terminal"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
+dependencies = [
+ "hermit-abi",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
+[[package]]
+name = "jobserver"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lexical-core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46"
+dependencies = [
+ "lexical-parse-float",
+ "lexical-parse-integer",
+ "lexical-util",
+ "lexical-write-float",
+ "lexical-write-integer",
+]
+
+[[package]]
+name = "lexical-parse-float"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f"
+dependencies = [
+ "lexical-parse-integer",
+ "lexical-util",
+ "static_assertions",
+]
+
+[[package]]
+name = "lexical-parse-integer"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9"
+dependencies = [
+ "lexical-util",
+ "static_assertions",
+]
+
+[[package]]
+name = "lexical-util"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc"
+dependencies = [
+ "static_assertions",
+]
+
+[[package]]
+name = "lexical-write-float"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862"
+dependencies = [
+ "lexical-util",
+ "lexical-write-integer",
+ "static_assertions",
+]
+
+[[package]]
+name = "lexical-write-integer"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446"
+dependencies = [
+ "lexical-util",
+ "static_assertions",
 ]
 
 [[package]]
 name = "libc"
-version = "0.2.112"
+version = "0.2.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "libredox"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
+dependencies = [
+ "bitflags 2.4.2",
+ "libc",
+ "redox_syscall 0.4.1",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
+[[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "lzma-sys"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "magic"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a200ae03df8c3dce7a963f6eeaac8feb41bf9001cb7e5ab22e3205aec2f0373d"
+dependencies = [
+ "bitflags 2.4.2",
+ "libc",
+ "magic-sys",
+ "thiserror",
+]
+
+[[package]]
+name = "magic-sys"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
+checksum = "eff86ae08895140d628119d407d568f3b657145ee8c265878064f717534bb3bc"
+dependencies = [
+ "libc",
+ "vcpkg",
+]
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
+[[package]]
+name = "md-5"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
+dependencies = [
+ "cfg-if",
+ "digest",
+]
 
 [[package]]
 name = "memchr"
-version = "2.4.1"
+version = "2.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "multimap"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+
+[[package]]
+name = "nibble_vec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "nix"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+ "memoffset 0.6.5",
+]
+
+[[package]]
+name = "nix"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
+dependencies = [
+ "autocfg",
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "nix"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
+dependencies = [
+ "bitflags 2.4.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "nix-compat"
+version = "0.1.0"
+dependencies = [
+ "bitflags 2.4.2",
+ "bstr",
+ "criterion",
+ "data-encoding",
+ "ed25519",
+ "ed25519-dalek",
+ "enum-primitive-derive",
+ "futures",
+ "glob",
+ "hex-literal",
+ "lazy_static",
+ "nom",
+ "num-traits",
+ "pin-project-lite",
+ "pretty_assertions",
+ "rstest",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror",
+ "tokio",
+ "tokio-test",
+ "zstd",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "nom8"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "object_store"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8718f8b65fdf67a45108d1548347d4af7d71fb81ce727bbf9e3b2535e079db3"
+dependencies = [
+ "async-trait",
+ "base64",
+ "bytes",
+ "chrono",
+ "futures",
+ "humantime",
+ "hyper 0.14.28",
+ "itertools 0.12.0",
+ "md-5",
+ "parking_lot 0.12.1",
+ "percent-encoding",
+ "quick-xml",
+ "rand",
+ "reqwest",
+ "ring",
+ "rustls-pemfile 2.1.0",
+ "serde",
+ "serde_json",
+ "snafu",
+ "tokio",
+ "tracing",
+ "url",
+ "walkdir",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "oorandom"
+version = "11.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "opentelemetry"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "indexmap 2.1.0",
+ "js-sys",
+ "once_cell",
+ "pin-project-lite",
+ "thiserror",
+ "urlencoding",
+]
+
+[[package]]
+name = "opentelemetry-otlp"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f24cda83b20ed2433c68241f918d0f6fdec8b1d43b7a9590ab4420c5095ca930"
+dependencies = [
+ "async-trait",
+ "futures-core",
+ "http 0.2.11",
+ "opentelemetry",
+ "opentelemetry-proto",
+ "opentelemetry-semantic-conventions",
+ "opentelemetry_sdk",
+ "prost 0.11.9",
+ "thiserror",
+ "tokio",
+ "tonic 0.9.2",
+]
+
+[[package]]
+name = "opentelemetry-proto"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2e155ce5cc812ea3d1dffbd1539aed653de4bf4882d60e6e04dcf0901d674e1"
+dependencies = [
+ "opentelemetry",
+ "opentelemetry_sdk",
+ "prost 0.11.9",
+ "tonic 0.9.2",
+]
+
+[[package]]
+name = "opentelemetry-semantic-conventions"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84"
+dependencies = [
+ "opentelemetry",
+]
+
+[[package]]
+name = "opentelemetry_sdk"
+version = "0.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4"
+dependencies = [
+ "async-trait",
+ "crossbeam-channel",
+ "futures-channel",
+ "futures-executor",
+ "futures-util",
+ "glob",
+ "once_cell",
+ "opentelemetry",
+ "ordered-float",
+ "percent-encoding",
+ "rand",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "ordered-float"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e"
+dependencies = [
+ "num-traits",
+]
 
 [[package]]
 name = "os_str_bytes"
-version = "6.0.0"
+version = "6.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core 0.8.6",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.9",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall 0.2.16",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.4.1",
+ "smallvec",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "path-clean"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "petgraph"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
+dependencies = [
+ "fixedbitset",
+ "indexmap 2.1.0",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "piper"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4"
+dependencies = [
+ "atomic-waker",
+ "fastrand",
+ "futures-io",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
+
+[[package]]
+name = "platforms"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
+
+[[package]]
+name = "plotters"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
+name = "polling"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30054e72317ab98eddd8561db0f6524df3367636884b7b21b703e4b280a84a14"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "pin-project-lite",
+ "rustix",
+ "tracing",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
 name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "pretty_assertions"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
+dependencies = [
+ "diff",
+ "yansi",
+]
+
+[[package]]
+name = "prettyplease"
 version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proptest"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf"
+dependencies = [
+ "bit-set",
+ "bit-vec",
+ "bitflags 2.4.2",
+ "lazy_static",
+ "num-traits",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax 0.8.2",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "prost"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
+dependencies = [
+ "bytes",
+ "prost-derive 0.11.9",
+]
+
+[[package]]
+name = "prost"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a"
+dependencies = [
+ "bytes",
+ "prost-derive 0.12.3",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2"
+dependencies = [
+ "bytes",
+ "heck",
+ "itertools 0.11.0",
+ "log",
+ "multimap",
+ "once_cell",
+ "petgraph",
+ "prettyplease",
+ "prost 0.12.3",
+ "prost-types",
+ "pulldown-cmark",
+ "pulldown-cmark-to-cmark",
+ "regex",
+ "syn 2.0.48",
+ "tempfile",
+ "which 4.4.2",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
+dependencies = [
+ "anyhow",
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
+dependencies = [
+ "anyhow",
+ "itertools 0.11.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e"
+dependencies = [
+ "prost 0.12.3",
+]
+
+[[package]]
+name = "prost-wkt"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d8ef9c3f0f1dab910d2b7e2c24a8e4322e122eba6d7a1921eeebcebbc046c40"
+dependencies = [
+ "chrono",
+ "inventory",
+ "prost 0.12.3",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "typetag",
+]
+
+[[package]]
+name = "prost-wkt-build"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b31cae9a54ca84fee1504740a82eebf2479532905e106f63ca0c3bc8d780321"
+dependencies = [
+ "heck",
+ "prost 0.12.3",
+ "prost-build",
+ "prost-types",
+ "quote",
+]
+
+[[package]]
+name = "prost-wkt-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435be4a8704091b4c5fb1d79799de7f2dbff53af05edf29385237f8cf7ab37ee"
+dependencies = [
+ "chrono",
+ "prost 0.12.3",
+ "prost-build",
+ "prost-types",
+ "prost-wkt",
+ "prost-wkt-build",
+ "regex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+]
+
+[[package]]
+name = "pulldown-cmark"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
+dependencies = [
+ "bitflags 2.4.2",
+ "memchr",
+ "unicase",
+]
+
+[[package]]
+name = "pulldown-cmark-to-cmark"
+version = "10.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0194e6e1966c23cc5fd988714f85b18d548d773e81965413555d96569931833d"
+dependencies = [
+ "pulldown-cmark",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quick-xml"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "radix_trie"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
+dependencies = [
+ "endian-type",
+ "nibble_vec",
+]
 
 [[package]]
 name = "rand"
-version = "0.8.4"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
  "rand_chacha",
  "rand_core",
- "rand_hc",
 ]
 
 [[package]]
@@ -133,88 +2866,1985 @@ dependencies = [
 
 [[package]]
 name = "rand_core"
-version = "0.6.3"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
  "getrandom",
 ]
 
 [[package]]
-name = "rand_hc"
-version = "0.3.1"
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_xoshiro"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
 dependencies = [
  "rand_core",
 ]
 
 [[package]]
+name = "rayon"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
 name = "redox_syscall"
-version = "0.2.10"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
-name = "remove_dir_all"
-version = "0.5.3"
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.3",
+ "regex-syntax 0.8.2",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.2",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "relative-path"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc"
+
+[[package]]
+name = "reqwest"
+version = "0.11.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
+dependencies = [
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2 0.3.24",
+ "http 0.2.11",
+ "http-body 0.4.6",
+ "hyper 0.14.28",
+ "hyper-rustls",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls 0.21.10",
+ "rustls-native-certs 0.6.3",
+ "rustls-pemfile 1.0.4",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "system-configuration",
+ "tokio",
+ "tokio-rustls 0.24.1",
+ "tokio-util",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+ "winreg",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
+dependencies = [
+ "cc",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rnix"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb35cedbeb70e0ccabef2a31bcff0aebd114f19566086300b8f42c725fc2cb5f"
+dependencies = [
+ "rowan",
+]
+
+[[package]]
+name = "rowan"
+version = "0.15.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
+dependencies = [
+ "countme",
+ "hashbrown 0.14.3",
+ "memoffset 0.9.0",
+ "rustc-hash",
+ "text-size",
+]
+
+[[package]]
+name = "rstest"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5316d2a1479eeef1ea21e7f9ddc67c191d497abc8fc3ba2467857abbb68330"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "rstest_macros",
+ "rustc_version",
+]
+
+[[package]]
+name = "rstest_macros"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04a9df72cc1f67020b0d63ad9bfe4a323e459ea7eb68e03bd9824db49f9a4c25"
+dependencies = [
+ "cfg-if",
+ "glob",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "relative-path",
+ "rustc_version",
+ "syn 2.0.48",
+ "unicode-ident",
+]
+
+[[package]]
+name = "rstest_reuse"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88530b681abe67924d42cca181d070e3ac20e0740569441a9e35a7cedd2b34a4"
+dependencies = [
+ "quote",
+ "rand",
+ "rustc_version",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
+dependencies = [
+ "bitflags 2.4.2",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.21.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-webpki 0.101.7",
+ "sct",
+]
+
+[[package]]
+name = "rustls"
+version = "0.22.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki 0.102.2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile 1.0.4",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile 2.1.0",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
+dependencies = [
+ "base64",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c333bb734fcdedcea57de1602543590f545f127dc8b533324318fd492c5c70b"
+dependencies = [
+ "base64",
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.101.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
+]
+
+[[package]]
+name = "rustyline"
+version = "10.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+checksum = "c1e83c32c3f3c33b08496e0d1df9ea8c64d39adb8eb36a1ebb1440c690697aef"
 dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "clipboard-win",
+ "dirs-next",
+ "fd-lock",
+ "libc",
+ "log",
+ "memchr",
+ "nix 0.25.1",
+ "radix_trie",
+ "scopeguard",
+ "unicode-segmentation",
+ "unicode-width",
+ "utf8parse",
  "winapi",
 ]
 
 [[package]]
+name = "ryu"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "sct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
+
+[[package]]
+name = "serde"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.111"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_qs"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c"
+dependencies = [
+ "percent-encoding",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_with"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a"
+dependencies = [
+ "base64",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.1.0",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "sled"
+version = "0.34.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
+dependencies = [
+ "crc32fast",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+ "fs2",
+ "fxhash",
+ "libc",
+ "log",
+ "parking_lot 0.11.2",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
+
+[[package]]
+name = "smol_str"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "snafu"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6"
+dependencies = [
+ "doc-comment",
+ "snafu-derive",
+]
+
+[[package]]
+name = "snafu-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "str-buf"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
+
+[[package]]
 name = "strsim"
 version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
+name = "structmeta"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "104842d6278bf64aa9d2f182ba4bde31e8aec7a131d29b7f444bb9b344a09e2a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "structmeta-derive",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "structmeta-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24420be405b590e2d746d83b01f09af673270cf80e9b003a5fa7b651c58c7d93"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "system-configuration"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "tabwriter"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a327282c4f64f6dc37e3bba4c2b6842cc3a992f204fa58d917696a89f691e5f6"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
 name = "tempfile"
-version = "3.2.0"
+version = "3.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
 dependencies = [
  "cfg-if",
- "libc",
- "rand",
- "redox_syscall",
- "remove_dir_all",
- "winapi",
+ "fastrand",
+ "redox_syscall 0.4.1",
+ "rustix",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "termcolor"
-version = "1.1.2"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
 dependencies = [
  "winapi-util",
 ]
 
 [[package]]
-name = "textwrap"
-version = "0.14.2"
+name = "test-strategy"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62d6408d1406657be2f9d1701fbae379331d30d2f6e92050710edb0d34eeb480"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "structmeta",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "text-size"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
+
+[[package]]
+name = "thiserror"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "time"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.35.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-io-timeout"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
+dependencies = [
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-listener"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96367e127b4cf47b92592a5154a563435fe28fe3fccf25917d4a34ee59c87303"
+dependencies = [
+ "axum 0.7.4",
+ "document-features",
+ "futures-core",
+ "futures-util",
+ "nix 0.26.4",
+ "pin-project",
+ "socket2",
+ "tokio",
+ "tokio-util",
+ "tonic 0.11.0",
+ "tracing",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
 
 [[package]]
-name = "tvix"
+name = "tokio-retry"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f"
+dependencies = [
+ "pin-project",
+ "rand",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
+dependencies = [
+ "rustls 0.21.10",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
+dependencies = [
+ "rustls 0.22.2",
+ "rustls-pki-types",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-tar"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5714c010ca3e5c27114c1cdeb9d14641ace49874aa5626d7149e47aedace75"
+dependencies = [
+ "filetime",
+ "futures-core",
+ "libc",
+ "redox_syscall 0.3.5",
+ "tokio",
+ "tokio-stream",
+ "xattr",
+]
+
+[[package]]
+name = "tokio-test"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719"
+dependencies = [
+ "async-stream",
+ "bytes",
+ "futures-core",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "toml"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b"
+dependencies = [
+ "indexmap 1.9.3",
+ "nom8",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+]
+
+[[package]]
+name = "tonic"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
+dependencies = [
+ "async-trait",
+ "axum 0.6.20",
+ "base64",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "h2 0.3.24",
+ "http 0.2.11",
+ "http-body 0.4.6",
+ "hyper 0.14.28",
+ "hyper-timeout",
+ "percent-encoding",
+ "pin-project",
+ "prost 0.11.9",
+ "tokio",
+ "tokio-stream",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tonic"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13"
+dependencies = [
+ "async-stream",
+ "async-trait",
+ "axum 0.6.20",
+ "base64",
+ "bytes",
+ "h2 0.3.24",
+ "http 0.2.11",
+ "http-body 0.4.6",
+ "hyper 0.14.28",
+ "hyper-timeout",
+ "percent-encoding",
+ "pin-project",
+ "prost 0.12.3",
+ "rustls-native-certs 0.7.0",
+ "rustls-pemfile 2.1.0",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls 0.25.0",
+ "tokio-stream",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tonic-build"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2"
+dependencies = [
+ "prettyplease",
+ "proc-macro2",
+ "prost-build",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "tonic-reflection"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "548c227bd5c0fae5925812c4ec6c66ffcfced23ea370cb823f4d18f0fc1cb6a7"
+dependencies = [
+ "prost 0.12.3",
+ "prost-types",
+ "tokio",
+ "tokio-stream",
+ "tonic 0.11.0",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "indexmap 1.9.3",
+ "pin-project",
+ "pin-project-lite",
+ "rand",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-futures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
+dependencies = [
+ "pin-project",
+ "tracing",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-opentelemetry"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596"
+dependencies = [
+ "js-sys",
+ "once_cell",
+ "opentelemetry",
+ "opentelemetry_sdk",
+ "smallvec",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-subscriber",
+ "web-time",
+]
+
+[[package]]
+name = "tracing-serde"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "serde",
+ "serde_json",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-serde",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "tvix-build"
+version = "0.1.0"
+dependencies = [
+ "bytes",
+ "clap",
+ "itertools 0.12.0",
+ "prost 0.12.3",
+ "prost-build",
+ "rstest",
+ "thiserror",
+ "tokio",
+ "tokio-listener",
+ "tonic 0.11.0",
+ "tonic-build",
+ "tonic-reflection",
+ "tracing",
+ "tracing-subscriber",
+ "tvix-castore",
+ "url",
+]
+
+[[package]]
+name = "tvix-castore"
+version = "0.1.0"
+dependencies = [
+ "async-process",
+ "async-stream",
+ "async-tempfile",
+ "bigtable_rs",
+ "blake3",
+ "bstr",
+ "bytes",
+ "data-encoding",
+ "digest",
+ "fastcdc",
+ "fuse-backend-rs",
+ "futures",
+ "hex-literal",
+ "lazy_static",
+ "libc",
+ "object_store",
+ "parking_lot 0.12.1",
+ "petgraph",
+ "pin-project-lite",
+ "prost 0.12.3",
+ "prost-build",
+ "rstest",
+ "rstest_reuse",
+ "serde",
+ "serde_qs",
+ "serde_with",
+ "sled",
+ "tempfile",
+ "thiserror",
+ "tokio",
+ "tokio-retry",
+ "tokio-stream",
+ "tokio-tar",
+ "tokio-util",
+ "tonic 0.11.0",
+ "tonic-build",
+ "tonic-reflection",
+ "tower",
+ "tracing",
+ "url",
+ "vhost",
+ "vhost-user-backend",
+ "virtio-bindings 0.2.2",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+ "walkdir",
+ "xattr",
+ "zstd",
+]
+
+[[package]]
+name = "tvix-cli"
+version = "0.1.0"
+dependencies = [
+ "bytes",
+ "clap",
+ "dirs",
+ "nix-compat",
+ "rustyline",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "tracing-subscriber",
+ "tvix-build",
+ "tvix-castore",
+ "tvix-eval",
+ "tvix-glue",
+ "tvix-store",
+ "wu-manber",
+]
+
+[[package]]
+name = "tvix-eval"
+version = "0.1.0"
+dependencies = [
+ "bstr",
+ "bytes",
+ "codemap",
+ "codemap-diagnostic",
+ "criterion",
+ "data-encoding",
+ "dirs",
+ "genawaiter",
+ "imbl",
+ "itertools 0.12.0",
+ "lazy_static",
+ "lexical-core",
+ "md-5",
+ "os_str_bytes",
+ "path-clean",
+ "pretty_assertions",
+ "proptest",
+ "regex",
+ "rnix",
+ "rowan",
+ "rstest",
+ "serde",
+ "serde_json",
+ "sha1",
+ "sha2",
+ "smol_str",
+ "tabwriter",
+ "tempfile",
+ "test-strategy",
+ "toml",
+ "tvix-eval-builtin-macros",
+ "xml-rs",
+]
+
+[[package]]
+name = "tvix-eval-builtin-macros"
+version = "0.0.1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "tvix-eval",
+]
+
+[[package]]
+name = "tvix-glue"
 version = "0.1.0"
 dependencies = [
+ "async-compression",
+ "async-recursion",
+ "bstr",
+ "bytes",
+ "criterion",
+ "data-encoding",
+ "futures",
+ "hex-literal",
+ "lazy_static",
+ "magic",
+ "md-5",
+ "nix 0.27.1",
+ "nix-compat",
+ "pin-project",
+ "pretty_assertions",
+ "reqwest",
+ "rstest",
+ "serde",
+ "serde_json",
+ "sha1",
+ "sha2",
+ "tempfile",
+ "thiserror",
+ "tokio",
+ "tokio-tar",
+ "tokio-util",
+ "tracing",
+ "tvix-build",
+ "tvix-castore",
+ "tvix-eval",
+ "tvix-store",
+ "url",
+ "walkdir",
+ "wu-manber",
+]
+
+[[package]]
+name = "tvix-serde"
+version = "0.1.0"
+dependencies = [
+ "bstr",
+ "serde",
+ "tvix-eval",
+]
+
+[[package]]
+name = "tvix-store"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-process",
+ "async-recursion",
+ "async-stream",
+ "bigtable_rs",
+ "blake3",
+ "bstr",
+ "bytes",
  "clap",
+ "count-write",
+ "data-encoding",
+ "futures",
+ "lazy_static",
+ "nix-compat",
+ "opentelemetry",
+ "opentelemetry-otlp",
+ "opentelemetry_sdk",
+ "pin-project-lite",
+ "prost 0.12.3",
+ "prost-build",
+ "reqwest",
+ "rstest",
+ "rstest_reuse",
+ "serde",
+ "serde_json",
+ "serde_qs",
+ "serde_with",
+ "sha2",
+ "sled",
  "tempfile",
+ "thiserror",
+ "tokio",
+ "tokio-listener",
+ "tokio-retry",
+ "tokio-stream",
+ "tokio-util",
+ "tonic 0.11.0",
+ "tonic-build",
+ "tonic-reflection",
+ "tower",
+ "tracing",
+ "tracing-opentelemetry",
+ "tracing-subscriber",
+ "tvix-castore",
+ "url",
+ "walkdir",
+ "xz2",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "typetag"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "661d18414ec032a49ece2d56eee03636e43c4e8d577047ab334c0ba892e29aaf"
+dependencies = [
+ "erased-serde",
+ "inventory",
+ "once_cell",
+ "serde",
+ "typetag-impl",
+]
+
+[[package]]
+name = "typetag-impl"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac73887f47b9312552aa90ef477927ff014d63d1920ca8037c6c1951eab64bb1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
+[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "url"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "urlencoding"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "uuid"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "vhost"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6769e8dbf5276b4376439fbf36bb880d203bf614bf7ef444198edc24b5a9f35"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vhost-user-backend"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f237b91db4ac339d639fb43398b52d785fa51e3c7760ac9425148863c1f4303"
+dependencies = [
+ "libc",
+ "log",
+ "vhost",
+ "virtio-bindings 0.1.0",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "virtio-bindings"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ff512178285488516ed85f15b5d0113a7cdb89e9e8a760b269ae4f02b84bd6b"
+
+[[package]]
+name = "virtio-bindings"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "878bcb1b2812a10c30d53b0ed054999de3d98f25ece91fc173973f9c57aaae86"
+
+[[package]]
+name = "virtio-queue"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ba81e2bcc21c0d2fc5e6683e79367e26ad219197423a498df801d79d5ba77bd"
+dependencies = [
+ "log",
+ "virtio-bindings 0.1.0",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vm-memory"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c"
+dependencies = [
+ "arc-swap",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "vmm-sys-util"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48b7b084231214f7427041e4220d77dfe726897a6d41fddee450696e66ff2a29"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+]
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
 ]
 
 [[package]]
 name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
+
+[[package]]
+name = "wasm-streams"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+]
+
+[[package]]
+name = "which"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.48.0",
+]
 
 [[package]]
 name = "winapi"
@@ -234,9 +4864,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
 dependencies = [
  "winapi",
 ]
@@ -246,3 +4876,225 @@ name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
+[[package]]
+name = "winreg"
+version = "0.50.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wu-manber"
+version = "0.1.0"
+source = "git+https://github.com/tvlfyi/wu-manber.git#0d5b22bea136659f7de60b102a7030e0daaa503d"
+
+[[package]]
+name = "xattr"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
+dependencies = [
+ "libc",
+ "linux-raw-sys",
+ "rustix",
+]
+
+[[package]]
+name = "xml-rs"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
+
+[[package]]
+name = "xz2"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
+dependencies = [
+ "lzma-sys",
+]
+
+[[package]]
+name = "yansi"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+
+[[package]]
+name = "zstd"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.9+zstd.1.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix
new file mode 100644
index 0000000000..715d08d345
--- /dev/null
+++ b/tvix/Cargo.nix
@@ -0,0 +1,17645 @@
+# This file was @generated by crate2nix 0.14.0 with the command:
+#   "generate" "--all-features"
+# See https://github.com/kolloch/crate2nix for more info.
+
+{ nixpkgs ? <nixpkgs>
+, pkgs ? import nixpkgs { config = { }; }
+, lib ? pkgs.lib
+, stdenv ? pkgs.stdenv
+, buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate
+  # This is used as the `crateOverrides` argument for `buildRustCrate`.
+, defaultCrateOverrides ? pkgs.defaultCrateOverrides
+  # The features to enable for the root_crate or the workspace_members.
+, rootFeatures ? [ "default" ]
+  # If true, throw errors instead of issueing deprecation warnings.
+, strictDeprecation ? false
+  # Used for conditional compilation based on CPU feature detection.
+, targetFeatures ? [ ]
+  # Whether to perform release builds: longer compile times, faster binaries.
+, release ? true
+  # Additional crate2nix configuration if it exists.
+, crateConfig ? if builtins.pathExists ./crate-config.nix
+  then pkgs.callPackage ./crate-config.nix { }
+  else { }
+}:
+
+rec {
+  #
+  # "public" attributes that we attempt to keep stable with new versions of crate2nix.
+  #
+
+
+  # Refer your crate build derivation by name here.
+  # You can override the features with
+  # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }.
+  workspaceMembers = {
+    "nix-compat" = rec {
+      packageId = "nix-compat";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "nix-compat";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+    "tvix-build" = rec {
+      packageId = "tvix-build";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-build";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+    "tvix-castore" = rec {
+      packageId = "tvix-castore";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-castore";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+    "tvix-cli" = rec {
+      packageId = "tvix-cli";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-cli";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+    "tvix-eval" = rec {
+      packageId = "tvix-eval";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-eval";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+    "tvix-eval-builtin-macros" = rec {
+      packageId = "tvix-eval-builtin-macros";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-eval-builtin-macros";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+    "tvix-glue" = rec {
+      packageId = "tvix-glue";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-glue";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+    "tvix-serde" = rec {
+      packageId = "tvix-serde";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-serde";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+    "tvix-store" = rec {
+      packageId = "tvix-store";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-store";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+  };
+
+  # A derivation that joins the outputs of all workspace members together.
+  allWorkspaceMembers = pkgs.symlinkJoin {
+    name = "all-workspace-members";
+    paths =
+      let members = builtins.attrValues workspaceMembers;
+      in builtins.map (m: m.build) members;
+  };
+
+  #
+  # "internal" ("private") attributes that may change in every new version of crate2nix.
+  #
+
+  internal = rec {
+    # Build and dependency information for crates.
+    # Many of the fields are passed one-to-one to buildRustCrate.
+    #
+    # Noteworthy:
+    # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate.
+    #   but with additional information which is used during dependency/feature resolution.
+    # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging.
+    # * `devDependencies` as of now not used by `buildRustCrate` but used to
+    #   inject test dependencies into the build
+
+    crates = {
+      "addr2line" = rec {
+        crateName = "addr2line";
+        version = "0.21.0";
+        edition = "2018";
+        sha256 = "1jx0k3iwyqr8klqbzk6kjvr496yd94aspis10vwsj5wy7gib4c4a";
+        dependencies = [
+          {
+            name = "gimli";
+            packageId = "gimli";
+            usesDefaultFeatures = false;
+            features = [ "read" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "rustc-demangle" "cpp_demangle" "std-object" "fallible-iterator" "smallvec" "memmap2" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "memmap2" = [ "dep:memmap2" ];
+          "object" = [ "dep:object" ];
+          "rustc-demangle" = [ "dep:rustc-demangle" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "gimli/rustc-dep-of-std" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "gimli/std" ];
+          "std-object" = [ "std" "object" "object/std" "object/compression" "gimli/endian-reader" ];
+        };
+      };
+      "adler" = rec {
+        crateName = "adler";
+        version = "1.0.2";
+        edition = "2015";
+        sha256 = "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj";
+        authors = [
+          "Jonas Schievink <jonasschievink@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "aho-corasick" = rec {
+        crateName = "aho-corasick";
+        version = "1.1.2";
+        edition = "2021";
+        sha256 = "1w510wnixvlgimkx1zjbvlxh6xps2vjgfqgwf5a6adlbjp5rv5mj";
+        libName = "aho_corasick";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf-literal" ];
+          "logging" = [ "dep:log" ];
+          "perf-literal" = [ "dep:memchr" ];
+          "std" = [ "memchr?/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "perf-literal" "std" ];
+      };
+      "android-tzdata" = rec {
+        crateName = "android-tzdata";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        authors = [
+          "RumovZ"
+        ];
+
+      };
+      "android_system_properties" = rec {
+        crateName = "android_system_properties";
+        version = "0.1.5";
+        edition = "2018";
+        sha256 = "04b3wrz12837j7mdczqd95b732gw5q7q66cv4yn4646lvccp57l1";
+        authors = [
+          "Nicolas Silva <nical@fastmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "anes" = rec {
+        crateName = "anes";
+        version = "0.1.6";
+        edition = "2018";
+        sha256 = "16bj1ww1xkwzbckk32j2pnbn5vk6wgsl3q4p3j9551xbcarwnijb";
+        authors = [
+          "Robert Vojta <rvojta@me.com>"
+        ];
+        features = {
+          "bitflags" = [ "dep:bitflags" ];
+          "parser" = [ "bitflags" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "anstream" = rec {
+        crateName = "anstream";
+        version = "0.6.11";
+        edition = "2021";
+        sha256 = "19dndamalavhjwp4i74k8hdijcixb7gsfa6ycwyc1r8xn6y1wbkf";
+        dependencies = [
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "anstyle-parse";
+            packageId = "anstyle-parse";
+          }
+          {
+            name = "anstyle-query";
+            packageId = "anstyle-query";
+            optional = true;
+          }
+          {
+            name = "anstyle-wincon";
+            packageId = "anstyle-wincon";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "colorchoice";
+            packageId = "colorchoice";
+          }
+          {
+            name = "utf8parse";
+            packageId = "utf8parse";
+          }
+        ];
+        features = {
+          "auto" = [ "dep:anstyle-query" ];
+          "default" = [ "auto" "wincon" ];
+          "wincon" = [ "dep:anstyle-wincon" ];
+        };
+        resolvedDefaultFeatures = [ "auto" "default" "wincon" ];
+      };
+      "anstyle" = rec {
+        crateName = "anstyle";
+        version = "1.0.4";
+        edition = "2021";
+        sha256 = "11yxw02b6parn29s757z96rgiqbn8qy0fk9a3p3bhczm85dhfybh";
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "anstyle-parse" = rec {
+        crateName = "anstyle-parse";
+        version = "0.2.3";
+        edition = "2021";
+        sha256 = "134jhzrz89labrdwxxnjxqjdg06qvaflj1wkfnmyapwyldfwcnn7";
+        dependencies = [
+          {
+            name = "utf8parse";
+            packageId = "utf8parse";
+            optional = true;
+          }
+        ];
+        features = {
+          "core" = [ "dep:arrayvec" ];
+          "default" = [ "utf8" ];
+          "utf8" = [ "dep:utf8parse" ];
+        };
+        resolvedDefaultFeatures = [ "default" "utf8" ];
+      };
+      "anstyle-query" = rec {
+        crateName = "anstyle-query";
+        version = "1.0.2";
+        edition = "2021";
+        sha256 = "0j3na4b1nma39g4x7cwvj009awxckjf3z2vkwhldgka44hqj72g2";
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_System_Console" "Win32_Foundation" ];
+          }
+        ];
+
+      };
+      "anstyle-wincon" = rec {
+        crateName = "anstyle-wincon";
+        version = "3.0.2";
+        edition = "2021";
+        sha256 = "19v0fv400bmp4niqpzxnhg83vz12mmqv7l2l8vi80qcdxj0lpm8w";
+        dependencies = [
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_System_Console" "Win32_Foundation" ];
+          }
+        ];
+
+      };
+      "anyhow" = rec {
+        crateName = "anyhow";
+        version = "1.0.79";
+        edition = "2018";
+        sha256 = "1ji5irqiwr8yprgqj8zvnli7zd7fz9kzaiddq44jnrl2l289h3h8";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "arc-swap" = rec {
+        crateName = "arc-swap";
+        version = "1.6.0";
+        edition = "2018";
+        sha256 = "19n9j146bpxs9phyh48gmlh9jjsdijr9p9br04qms0g9ypfsvp5x";
+        authors = [
+          "Michal 'vorner' Vaner <vorner@vorner.cz>"
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "arrayref" = rec {
+        crateName = "arrayref";
+        version = "0.3.7";
+        edition = "2015";
+        sha256 = "0ia5ndyxqkzdymqr4ls53jdmajf09adjimg5kvw65kkprg930jbb";
+        authors = [
+          "David Roundy <roundyd@physics.oregonstate.edu>"
+        ];
+
+      };
+      "arrayvec" = rec {
+        crateName = "arrayvec";
+        version = "0.7.4";
+        edition = "2018";
+        sha256 = "04b7n722jij0v3fnm3qk072d5ysc2q30rl9fz33zpfhzah30mlwn";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+      };
+      "async-channel" = rec {
+        crateName = "async-channel";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "1hzhkbrlmgbrrwb1d5aba5f03p42s6z80g5p38s127c27nj470pj";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "concurrent-queue";
+            packageId = "concurrent-queue";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "event-listener";
+            packageId = "event-listener 5.2.0";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "event-listener-strategy";
+            packageId = "event-listener-strategy 0.5.0";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "concurrent-queue/std" "event-listener/std" "event-listener-strategy/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "async-compression" = rec {
+        crateName = "async-compression";
+        version = "0.4.6";
+        edition = "2018";
+        sha256 = "0b6874q56g1cx8ivs9j89d757rsh9kyrrwlp1852094jjrmg85m1";
+        authors = [
+          "Wim Looman <wim@nemo157.com>"
+          "Allen Bui <fairingrey@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bzip2";
+            packageId = "bzip2";
+            optional = true;
+          }
+          {
+            name = "flate2";
+            packageId = "flate2";
+            optional = true;
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "xz2";
+            packageId = "xz2";
+            optional = true;
+          }
+        ];
+        features = {
+          "all" = [ "all-implementations" "all-algorithms" ];
+          "all-algorithms" = [ "brotli" "bzip2" "deflate" "gzip" "lzma" "xz" "zlib" "zstd" "deflate64" ];
+          "all-implementations" = [ "futures-io" "tokio" ];
+          "brotli" = [ "dep:brotli" ];
+          "bzip2" = [ "dep:bzip2" ];
+          "deflate" = [ "flate2" ];
+          "deflate64" = [ "dep:deflate64" ];
+          "flate2" = [ "dep:flate2" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "gzip" = [ "flate2" ];
+          "libzstd" = [ "dep:libzstd" ];
+          "lzma" = [ "xz2" ];
+          "tokio" = [ "dep:tokio" ];
+          "xz" = [ "xz2" ];
+          "xz2" = [ "dep:xz2" ];
+          "zlib" = [ "flate2" ];
+          "zstd" = [ "libzstd" "zstd-safe" ];
+          "zstd-safe" = [ "dep:zstd-safe" ];
+          "zstdmt" = [ "zstd" "zstd-safe/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "bzip2" "flate2" "gzip" "tokio" "xz" "xz2" ];
+      };
+      "async-io" = rec {
+        crateName = "async-io";
+        version = "2.3.2";
+        edition = "2021";
+        sha256 = "110847w0ycfhklm3i928avd28x7lf9amblr2wjngi8ngk7sv1k6w";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-lock";
+            packageId = "async-lock 3.3.0";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "concurrent-queue";
+            packageId = "concurrent-queue";
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-lite";
+            packageId = "futures-lite";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "parking";
+            packageId = "parking";
+          }
+          {
+            name = "polling";
+            packageId = "polling";
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
+            features = [ "fs" "net" "std" ];
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" ];
+          }
+        ];
+
+      };
+      "async-lock 2.8.0" = rec {
+        crateName = "async-lock";
+        version = "2.8.0";
+        edition = "2018";
+        sha256 = "0asq5xdzgp3d5m82y5rg7a0k9q0g95jy6mgc7ivl334x7qlp4wi8";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "event-listener";
+            packageId = "event-listener 2.5.3";
+          }
+        ];
+
+      };
+      "async-lock 3.3.0" = rec {
+        crateName = "async-lock";
+        version = "3.3.0";
+        edition = "2021";
+        sha256 = "0yxflkfw46rad4lv86f59b5z555dlfmg1riz1n8830rgi0qb8d6h";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "event-listener";
+            packageId = "event-listener 4.0.3";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "event-listener-strategy";
+            packageId = "event-listener-strategy 0.4.0";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "event-listener/std" "event-listener-strategy/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "async-process" = rec {
+        crateName = "async-process";
+        version = "2.1.0";
+        edition = "2021";
+        sha256 = "1j0cfac9p3kq5dclfzlz6jv5l29kwflh9nvr3ivmdg8ih3v3q7j5";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-channel";
+            packageId = "async-channel";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "async-io";
+            packageId = "async-io";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "async-lock";
+            packageId = "async-lock 3.3.0";
+          }
+          {
+            name = "async-signal";
+            packageId = "async-signal";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "blocking";
+            packageId = "blocking";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "event-listener";
+            packageId = "event-listener 5.2.0";
+          }
+          {
+            name = "futures-lite";
+            packageId = "futures-lite";
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+            features = [ "std" "fs" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_System_Threading" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "async-io";
+            packageId = "async-io";
+          }
+        ];
+
+      };
+      "async-recursion" = rec {
+        crateName = "async-recursion";
+        version = "1.0.5";
+        edition = "2018";
+        sha256 = "1l2vlgyaa9a2dd0y1vbqyppzsvpdr1y4rar4gn1qi68pl5dmmmaz";
+        procMacro = true;
+        authors = [
+          "Robert Usher <266585+dcchut@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            usesDefaultFeatures = false;
+            features = [ "full" "parsing" "printing" "proc-macro" "clone-impls" ];
+          }
+        ];
+
+      };
+      "async-signal" = rec {
+        crateName = "async-signal";
+        version = "0.2.5";
+        edition = "2018";
+        sha256 = "1i9466hiqghhmljjnn83a8vnxi8z013xga03f59c89d2cl7xjiwy";
+        authors = [
+          "John Nunley <dev@notgull.net>"
+        ];
+        dependencies = [
+          {
+            name = "async-io";
+            packageId = "async-io";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "async-lock";
+            packageId = "async-lock 2.8.0";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "atomic-waker";
+            packageId = "atomic-waker";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+            features = [ "process" "std" ];
+          }
+          {
+            name = "signal-hook-registry";
+            packageId = "signal-hook-registry";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_System_Console" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "async-io";
+            packageId = "async-io";
+          }
+        ];
+
+      };
+      "async-stream" = rec {
+        crateName = "async-stream";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream-impl";
+            packageId = "async-stream-impl";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "async-stream-impl" = rec {
+        crateName = "async-stream-impl";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
+        procMacro = true;
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "async-task" = rec {
+        crateName = "async-task";
+        version = "4.7.0";
+        edition = "2018";
+        sha256 = "16975vx6aqy5yf16fs9xz5vx1zq8mwkzfmykvcilc1j7b6c6xczv";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "async-tempfile" = rec {
+        crateName = "async-tempfile";
+        version = "0.4.0";
+        edition = "2021";
+        sha256 = "16zx4qcwzq94n13pp6xwa4589apm5y8j20jb7lk4yzn42fqlnzdk";
+        authors = [
+          "Markus Mayer"
+        ];
+        dependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" ];
+          }
+          {
+            name = "uuid";
+            packageId = "uuid";
+            optional = true;
+            features = [ "v4" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt-multi-thread" "macros" ];
+          }
+        ];
+        features = {
+          "default" = [ "uuid" ];
+          "uuid" = [ "dep:uuid" ];
+        };
+        resolvedDefaultFeatures = [ "default" "uuid" ];
+      };
+      "async-trait" = rec {
+        crateName = "async-trait";
+        version = "0.1.77";
+        edition = "2021";
+        sha256 = "1adf1jh2yg39rkpmqjqyr9xyd6849p0d95425i6imgbhx0syx069";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "atomic-waker" = rec {
+        crateName = "atomic-waker";
+        version = "1.1.2";
+        edition = "2018";
+        sha256 = "1h5av1lw56m0jf0fd3bchxq8a30xv0b4wv8s4zkp4s0i7mfvs18m";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+          "Contributors to futures-rs"
+        ];
+        features = {
+          "portable-atomic" = [ "dep:portable-atomic" ];
+        };
+      };
+      "autocfg" = rec {
+        crateName = "autocfg";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "1ylp3cb47ylzabimazvbz9ms6ap784zhb6syaz6c1jqpmcmq0s6l";
+        authors = [
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+
+      };
+      "axum 0.6.20" = rec {
+        crateName = "axum";
+        version = "0.6.20";
+        edition = "2021";
+        sha256 = "1gynqkg3dcy1zd7il69h8a3zax86v6qq5zpawqyn87mr6979x0iv";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "axum-core";
+            packageId = "axum-core 0.3.4";
+          }
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 0.4.6";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            features = [ "stream" ];
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "matchit";
+            packageId = "matchit";
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "mime";
+            packageId = "mime";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "sync_wrapper";
+            packageId = "sync_wrapper";
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            usesDefaultFeatures = false;
+            features = [ "util" ];
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            rename = "tower";
+            features = [ "util" "timeout" "limit" "load-shed" "steer" "filter" ];
+          }
+        ];
+        features = {
+          "__private_docs" = [ "tower/full" "dep:tower-http" ];
+          "default" = [ "form" "http1" "json" "matched-path" "original-uri" "query" "tokio" "tower-log" ];
+          "form" = [ "dep:serde_urlencoded" ];
+          "headers" = [ "dep:headers" ];
+          "http1" = [ "hyper/http1" ];
+          "http2" = [ "hyper/http2" ];
+          "json" = [ "dep:serde_json" "dep:serde_path_to_error" ];
+          "macros" = [ "dep:axum-macros" ];
+          "multipart" = [ "dep:multer" ];
+          "query" = [ "dep:serde_urlencoded" ];
+          "tokio" = [ "dep:tokio" "hyper/server" "hyper/tcp" "hyper/runtime" "tower/make" ];
+          "tower-log" = [ "tower/log" ];
+          "tracing" = [ "dep:tracing" "axum-core/tracing" ];
+          "ws" = [ "tokio" "dep:tokio-tungstenite" "dep:sha1" "dep:base64" ];
+        };
+      };
+      "axum 0.7.4" = rec {
+        crateName = "axum";
+        version = "0.7.4";
+        edition = "2021";
+        sha256 = "17kv7v8m981cqmfbv5m538fzxhw51l9bajv06kfddi7njarb8dhj";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "axum-core";
+            packageId = "axum-core 0.4.3";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "http";
+            packageId = "http 1.1.0";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 1.0.0";
+          }
+          {
+            name = "http-body-util";
+            packageId = "http-body-util";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 1.2.0";
+            optional = true;
+          }
+          {
+            name = "hyper-util";
+            packageId = "hyper-util";
+            optional = true;
+            features = [ "tokio" "server" "server-auto" ];
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "matchit";
+            packageId = "matchit";
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "mime";
+            packageId = "mime";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+            features = [ "raw_value" ];
+          }
+          {
+            name = "serde_path_to_error";
+            packageId = "serde_path_to_error";
+            optional = true;
+          }
+          {
+            name = "serde_urlencoded";
+            packageId = "serde_urlencoded";
+            optional = true;
+          }
+          {
+            name = "sync_wrapper";
+            packageId = "sync_wrapper";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            rename = "tokio";
+            optional = true;
+            features = [ "time" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            usesDefaultFeatures = false;
+            features = [ "util" ];
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            rename = "tokio";
+            features = [ "macros" "rt" "rt-multi-thread" "net" "test-util" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            rename = "tower";
+            features = [ "util" "timeout" "limit" "load-shed" "steer" "filter" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+        ];
+        features = {
+          "__private_docs" = [ "tower/full" "dep:tower-http" ];
+          "default" = [ "form" "http1" "json" "matched-path" "original-uri" "query" "tokio" "tower-log" "tracing" ];
+          "form" = [ "dep:serde_urlencoded" ];
+          "http1" = [ "dep:hyper" "hyper?/http1" ];
+          "http2" = [ "dep:hyper" "hyper?/http2" ];
+          "json" = [ "dep:serde_json" "dep:serde_path_to_error" ];
+          "macros" = [ "dep:axum-macros" ];
+          "multipart" = [ "dep:multer" ];
+          "query" = [ "dep:serde_urlencoded" ];
+          "tokio" = [ "dep:hyper-util" "dep:tokio" "tokio/net" "tokio/rt" "tower/make" "tokio/macros" ];
+          "tower-log" = [ "tower/log" ];
+          "tracing" = [ "dep:tracing" "axum-core/tracing" ];
+          "ws" = [ "dep:hyper" "tokio" "dep:tokio-tungstenite" "dep:sha1" "dep:base64" ];
+        };
+        resolvedDefaultFeatures = [ "default" "form" "http1" "json" "matched-path" "original-uri" "query" "tokio" "tower-log" "tracing" ];
+      };
+      "axum-core 0.3.4" = rec {
+        crateName = "axum-core";
+        version = "0.3.4";
+        edition = "2021";
+        sha256 = "0b1d9nkqb8znaba4qqzxzc968qwj4ybn4vgpyz9lz4a7l9vsb7vm";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 0.4.6";
+          }
+          {
+            name = "mime";
+            packageId = "mime";
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+        ];
+        features = {
+          "__private_docs" = [ "dep:tower-http" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+      };
+      "axum-core 0.4.3" = rec {
+        crateName = "axum-core";
+        version = "0.4.3";
+        edition = "2021";
+        sha256 = "1qx28wg4j6qdcdrisqwyaavlzc0zvbsrcwa99zf9456lfbyn6p51";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "http";
+            packageId = "http 1.1.0";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 1.0.0";
+          }
+          {
+            name = "http-body-util";
+            packageId = "http-body-util";
+          }
+          {
+            name = "mime";
+            packageId = "mime";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "sync_wrapper";
+            packageId = "sync_wrapper";
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+        ];
+        features = {
+          "__private_docs" = [ "dep:tower-http" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "tracing" ];
+      };
+      "backtrace" = rec {
+        crateName = "backtrace";
+        version = "0.3.69";
+        edition = "2018";
+        sha256 = "0dsq23dhw4pfndkx2nsa1ml2g31idm7ss7ljxp8d57avygivg290";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "addr2line";
+            packageId = "addr2line";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "object";
+            packageId = "object";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+            features = [ "read_core" "elf" "macho" "pe" "unaligned" "archive" ];
+          }
+          {
+            name = "rustc-demangle";
+            packageId = "rustc-demangle";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "std" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "serialize-rustc" = [ "rustc-serialize" ];
+          "serialize-serde" = [ "serde" ];
+          "verify-winapi" = [ "winapi/dbghelp" "winapi/handleapi" "winapi/libloaderapi" "winapi/memoryapi" "winapi/minwindef" "winapi/processthreadsapi" "winapi/synchapi" "winapi/tlhelp32" "winapi/winbase" "winapi/winnt" ];
+          "winapi" = [ "dep:winapi" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64" = rec {
+        crateName = "base64";
+        version = "0.21.7";
+        edition = "2018";
+        sha256 = "0rw52yvsk75kar9wgqfwgb414kvil1gn7mqkrhn9zf1537mpsacx";
+        authors = [
+          "Alice Maz <alice@alicemaz.com>"
+          "Marshall Pierce <marshall@mpierce.org>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "base64ct" = rec {
+        crateName = "base64ct";
+        version = "1.6.0";
+        edition = "2021";
+        sha256 = "0nvdba4jb8aikv60az40x2w1y96sjdq8z3yp09rwzmkhiwv1lg4c";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "bigtable_rs" = rec {
+        crateName = "bigtable_rs";
+        version = "0.2.9";
+        edition = "2021";
+        workspace_member = null;
+        src = pkgs.fetchgit {
+          url = "https://github.com/flokli/bigtable_rs";
+          rev = "0af404741dfc40eb9fa99cf4d4140a09c5c20df7";
+          sha256 = "1njjam1lx2xlnm7a41lga8601vmjgqz0fvc77x24gd04pc7avxll";
+        };
+        authors = [
+          "Fuyang Liu <liufuyang@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "gcp_auth";
+            packageId = "gcp_auth";
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+          }
+          {
+            name = "prost-types";
+            packageId = "prost-types";
+          }
+          {
+            name = "prost-wkt";
+            packageId = "prost-wkt";
+          }
+          {
+            name = "prost-wkt-types";
+            packageId = "prost-wkt-types";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_with";
+            packageId = "serde_with";
+            features = [ "base64" ];
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt-multi-thread" ];
+          }
+          {
+            name = "tonic";
+            packageId = "tonic 0.11.0";
+            features = [ "tls" "transport" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "prost-build";
+            packageId = "prost-build";
+          }
+          {
+            name = "prost-wkt-build";
+            packageId = "prost-wkt-build";
+          }
+          {
+            name = "tonic-build";
+            packageId = "tonic-build";
+            features = [ "cleanup-markdown" ];
+          }
+        ];
+
+      };
+      "bit-set" = rec {
+        crateName = "bit-set";
+        version = "0.5.3";
+        edition = "2015";
+        sha256 = "1wcm9vxi00ma4rcxkl3pzzjli6ihrpn9cfdi0c5b4cvga2mxs007";
+        authors = [
+          "Alexis Beingessner <a.beingessner@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bit-vec";
+            packageId = "bit-vec";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "bit-vec/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "bit-vec" = rec {
+        crateName = "bit-vec";
+        version = "0.6.3";
+        edition = "2015";
+        sha256 = "1ywqjnv60cdh1slhz67psnp422md6jdliji6alq0gmly2xm9p7rl";
+        authors = [
+          "Alexis Beingessner <a.beingessner@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde_no_std" = [ "serde/alloc" ];
+          "serde_std" = [ "std" "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "bitflags 1.3.2" = rec {
+        crateName = "bitflags";
+        version = "1.3.2";
+        edition = "2018";
+        sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bitflags 2.4.2" = rec {
+        crateName = "bitflags";
+        version = "2.4.2";
+        edition = "2021";
+        sha256 = "1pqd142hyqlzr7p9djxq2ff0jx07a2sb2xp9lhw69cbf80s0jmzd";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "bitmaps" = rec {
+        crateName = "bitmaps";
+        version = "3.2.0";
+        edition = "2021";
+        sha256 = "00ql08pm4l9hizkldyy54v0pk96g7zg8x6i72c2vkcq0iawl4dkh";
+        authors = [
+          "Bodil Stokke <bodil@bodil.org>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "blake3" = rec {
+        crateName = "blake3";
+        version = "1.5.0";
+        edition = "2021";
+        sha256 = "11ysh12zcqq6xkjxh5cbrmnwzalprm3z552i5ff7wm5za9hz0c82";
+        authors = [
+          "Jack O'Connor <oconnor663@gmail.com>"
+          "Samuel Neves"
+        ];
+        dependencies = [
+          {
+            name = "arrayref";
+            packageId = "arrayref";
+          }
+          {
+            name = "arrayvec";
+            packageId = "arrayvec";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "constant_time_eq";
+            packageId = "constant_time_eq";
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+            optional = true;
+            features = [ "mac" ];
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "digest" = [ "dep:digest" ];
+          "mmap" = [ "std" "dep:memmap2" ];
+          "rayon" = [ "dep:rayon" "std" ];
+          "serde" = [ "dep:serde" ];
+          "traits-preview" = [ "digest" ];
+          "zeroize" = [ "dep:zeroize" "arrayvec/zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "default" "digest" "rayon" "std" "traits-preview" ];
+      };
+      "block-buffer" = rec {
+        crateName = "block-buffer";
+        version = "0.10.4";
+        edition = "2018";
+        sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+
+      };
+      "blocking" = rec {
+        crateName = "blocking";
+        version = "1.5.1";
+        edition = "2018";
+        sha256 = "064i3d6b8ln34fgdw49nmx9m36bwi3r3nv8c9xhcrpf4ilz92dva";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-channel";
+            packageId = "async-channel";
+          }
+          {
+            name = "async-lock";
+            packageId = "async-lock 3.3.0";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "async-task";
+            packageId = "async-task";
+          }
+          {
+            name = "fastrand";
+            packageId = "fastrand";
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-lite";
+            packageId = "futures-lite";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "piper";
+            packageId = "piper";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-lite";
+            packageId = "futures-lite";
+          }
+        ];
+
+      };
+      "bstr" = rec {
+        crateName = "bstr";
+        version = "1.9.0";
+        edition = "2021";
+        sha256 = "1p6hzf3wqwwynv6w4pn17jg21amfafph9kb5sfvf1idlli8h13y4";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata 0.4.3";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "dfa-search" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "memchr/alloc" "serde?/alloc" ];
+          "default" = [ "std" "unicode" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" "memchr/std" "serde?/std" ];
+          "unicode" = [ "dep:regex-automata" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "serde" "std" "unicode" ];
+      };
+      "bumpalo" = rec {
+        crateName = "bumpalo";
+        version = "3.14.0";
+        edition = "2021";
+        sha256 = "1v4arnv9kwk54v5d0qqpv4vyw2sgr660nk0w3apzixi1cm3yfc3z";
+        authors = [
+          "Nick Fitzgerald <fitzgen@gmail.com>"
+        ];
+        features = {
+          "allocator-api2" = [ "dep:allocator-api2" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "byteorder" = rec {
+        crateName = "byteorder";
+        version = "1.5.0";
+        edition = "2021";
+        sha256 = "0jzncxyf404mwqdbspihyzpkndfgda450l0893pz5xj685cg5l0z";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "bytes" = rec {
+        crateName = "bytes";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "bzip2" = rec {
+        crateName = "bzip2";
+        version = "0.4.4";
+        edition = "2015";
+        sha256 = "1y27wgqkx3k2jmh4k26vra2kqjq1qc1asww8hac3cv1zxyk1dcdx";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "bzip2-sys";
+            packageId = "bzip2-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "futures" = [ "dep:futures" ];
+          "static" = [ "bzip2-sys/static" ];
+          "tokio" = [ "tokio-io" "futures" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+        };
+      };
+      "bzip2-sys" = rec {
+        crateName = "bzip2-sys";
+        version = "0.1.11+1.0.8";
+        edition = "2015";
+        links = "bzip2";
+        sha256 = "1p2crnv8d8gpz5c2vlvzl0j55i3yqg5bi0kwsl1531x77xgraskk";
+        libName = "bzip2_sys";
+        libPath = "lib.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+        ];
+        features = { };
+      };
+      "caps" = rec {
+        crateName = "caps";
+        version = "0.5.5";
+        edition = "2018";
+        sha256 = "02vk0w48rncgvfmj2mz2kpzvdgc14z225451w7lvvkwvaansl2qr";
+        authors = [
+          "Luca Bruno <lucab@lucabruno.net>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "serde_support" = [ "serde" ];
+        };
+      };
+      "cast" = rec {
+        crateName = "cast";
+        version = "0.3.0";
+        edition = "2018";
+        sha256 = "1dbyngbyz2qkk0jn2sxil8vrz3rnpcj142y184p9l4nbl9radcip";
+        authors = [
+          "Jorge Aparicio <jorge@japaric.io>"
+        ];
+        features = { };
+      };
+      "cc" = rec {
+        crateName = "cc";
+        version = "1.0.83";
+        edition = "2018";
+        crateBin = [ ];
+        sha256 = "1l643zidlb5iy1dskc5ggqs4wqa29a02f44piczqc8zcnsq4y5zi";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "jobserver";
+            packageId = "jobserver";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        features = {
+          "jobserver" = [ "dep:jobserver" ];
+          "parallel" = [ "jobserver" ];
+        };
+        resolvedDefaultFeatures = [ "jobserver" "parallel" ];
+      };
+      "cfg-if" = rec {
+        crateName = "cfg-if";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "chrono" = rec {
+        crateName = "chrono";
+        version = "0.4.34";
+        edition = "2021";
+        sha256 = "12zk0ja924f55va2fs0qj34xaygq46fy92blmc7qkmcj9dj1bh2v";
+        dependencies = [
+          {
+            name = "android-tzdata";
+            packageId = "android-tzdata";
+            optional = true;
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "iana-time-zone";
+            packageId = "iana-time-zone";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+            features = [ "fallback" ];
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            optional = true;
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!(("emscripten" == target."os" or null) || ("wasi" == target."os" or null))));
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            optional = true;
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!(("emscripten" == target."os" or null) || ("wasi" == target."os" or null))));
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "android-tzdata" = [ "dep:android-tzdata" ];
+          "arbitrary" = [ "dep:arbitrary" ];
+          "clock" = [ "winapi" "iana-time-zone" "android-tzdata" "now" ];
+          "default" = [ "clock" "std" "oldtime" "wasmbind" ];
+          "iana-time-zone" = [ "dep:iana-time-zone" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "now" = [ "std" ];
+          "pure-rust-locales" = [ "dep:pure-rust-locales" ];
+          "rkyv" = [ "dep:rkyv" "rkyv/size_32" ];
+          "rkyv-16" = [ "dep:rkyv" "rkyv?/size_16" ];
+          "rkyv-32" = [ "dep:rkyv" "rkyv?/size_32" ];
+          "rkyv-64" = [ "dep:rkyv" "rkyv?/size_64" ];
+          "rkyv-validation" = [ "rkyv?/validation" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "unstable-locales" = [ "pure-rust-locales" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+          "wasmbind" = [ "wasm-bindgen" "js-sys" ];
+          "winapi" = [ "windows-targets" ];
+          "windows-targets" = [ "dep:windows-targets" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "android-tzdata" "clock" "default" "iana-time-zone" "js-sys" "now" "oldtime" "serde" "std" "wasm-bindgen" "wasmbind" "winapi" "windows-targets" ];
+      };
+      "ciborium" = rec {
+        crateName = "ciborium";
+        version = "0.2.1";
+        edition = "2021";
+        sha256 = "09p9gr3jxys51v0fzwsmxym2p7pcz9mhng2xib74lnlfqzv93zgg";
+        authors = [
+          "Nathaniel McCallum <npmccallum@profian.com>"
+        ];
+        dependencies = [
+          {
+            name = "ciborium-io";
+            packageId = "ciborium-io";
+            features = [ "alloc" ];
+          }
+          {
+            name = "ciborium-ll";
+            packageId = "ciborium-ll";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+            features = [ "alloc" "derive" ];
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "ciborium-io/std" "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "ciborium-io" = rec {
+        crateName = "ciborium-io";
+        version = "0.2.1";
+        edition = "2021";
+        sha256 = "0mi6ci27lpz3azksxrvgzl9jc4a3dfr20pjx7y2nkcrjalbikyfd";
+        authors = [
+          "Nathaniel McCallum <npmccallum@profian.com>"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "ciborium-ll" = rec {
+        crateName = "ciborium-ll";
+        version = "0.2.1";
+        edition = "2021";
+        sha256 = "0az2vabamfk75m74ylgf6nzqgqgma5yf25bc1ripfg09ri7a5yny";
+        authors = [
+          "Nathaniel McCallum <npmccallum@profian.com>"
+        ];
+        dependencies = [
+          {
+            name = "ciborium-io";
+            packageId = "ciborium-io";
+          }
+          {
+            name = "half";
+            packageId = "half";
+          }
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+      };
+      "clap" = rec {
+        crateName = "clap";
+        version = "4.4.18";
+        edition = "2021";
+        crateBin = [ ];
+        sha256 = "0p46h346y8nval6gwzh27if3icbi9dwl95fg5ir36ihrqip8smqy";
+        dependencies = [
+          {
+            name = "clap_builder";
+            packageId = "clap_builder";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "clap_derive";
+            packageId = "clap_derive";
+            optional = true;
+          }
+        ];
+        features = {
+          "cargo" = [ "clap_builder/cargo" ];
+          "color" = [ "clap_builder/color" ];
+          "debug" = [ "clap_builder/debug" "clap_derive?/debug" ];
+          "default" = [ "std" "color" "help" "usage" "error-context" "suggestions" ];
+          "deprecated" = [ "clap_builder/deprecated" "clap_derive?/deprecated" ];
+          "derive" = [ "dep:clap_derive" ];
+          "env" = [ "clap_builder/env" ];
+          "error-context" = [ "clap_builder/error-context" ];
+          "help" = [ "clap_builder/help" ];
+          "std" = [ "clap_builder/std" ];
+          "string" = [ "clap_builder/string" ];
+          "suggestions" = [ "clap_builder/suggestions" ];
+          "unicode" = [ "clap_builder/unicode" ];
+          "unstable-doc" = [ "clap_builder/unstable-doc" "derive" ];
+          "unstable-styles" = [ "clap_builder/unstable-styles" ];
+          "unstable-v5" = [ "clap_builder/unstable-v5" "clap_derive?/unstable-v5" "deprecated" ];
+          "usage" = [ "clap_builder/usage" ];
+          "wrap_help" = [ "clap_builder/wrap_help" ];
+        };
+        resolvedDefaultFeatures = [ "color" "default" "derive" "env" "error-context" "help" "std" "suggestions" "usage" ];
+      };
+      "clap_builder" = rec {
+        crateName = "clap_builder";
+        version = "4.4.18";
+        edition = "2021";
+        sha256 = "1iyif47075caa4x1p3ygk18b07lb4xl4k48w4c061i2hxi0dzx2d";
+        dependencies = [
+          {
+            name = "anstream";
+            packageId = "anstream";
+            optional = true;
+          }
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "clap_lex";
+            packageId = "clap_lex";
+          }
+          {
+            name = "strsim";
+            packageId = "strsim";
+            optional = true;
+          }
+        ];
+        features = {
+          "color" = [ "dep:anstream" ];
+          "debug" = [ "dep:backtrace" ];
+          "default" = [ "std" "color" "help" "usage" "error-context" "suggestions" ];
+          "std" = [ "anstyle/std" ];
+          "suggestions" = [ "dep:strsim" "error-context" ];
+          "unicode" = [ "dep:unicode-width" "dep:unicase" ];
+          "unstable-doc" = [ "cargo" "wrap_help" "env" "unicode" "string" ];
+          "unstable-styles" = [ "color" ];
+          "unstable-v5" = [ "deprecated" ];
+          "wrap_help" = [ "help" "dep:terminal_size" ];
+        };
+        resolvedDefaultFeatures = [ "color" "env" "error-context" "help" "std" "suggestions" "usage" ];
+      };
+      "clap_derive" = rec {
+        crateName = "clap_derive";
+        version = "4.4.7";
+        edition = "2021";
+        sha256 = "0hk4hcxl56qwqsf4hmf7c0gr19r9fbxk0ah2bgkr36pmmaph966g";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "raw-deprecated" = [ "deprecated" ];
+          "unstable-v5" = [ "deprecated" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "clap_lex" = rec {
+        crateName = "clap_lex";
+        version = "0.6.0";
+        edition = "2021";
+        sha256 = "1l8bragdvim7mva9flvd159dskn2bdkpl0jqrr41wnjfn8pcfbvh";
+
+      };
+      "clipboard-win" = rec {
+        crateName = "clipboard-win";
+        version = "4.5.0";
+        edition = "2018";
+        sha256 = "0qh3rypkf1lazniq4nr04hxsck0d55rigb5sjvpvgnap4dyc54bi";
+        authors = [
+          "Douman <douman@gmx.se>"
+        ];
+        dependencies = [
+          {
+            name = "error-code";
+            packageId = "error-code";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "str-buf";
+            packageId = "str-buf";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."windows" or false);
+            features = [ "basetsd" "shellapi" "winbase" "winuser" "winerror" "stringapiset" "errhandlingapi" "synchapi" ];
+          }
+        ];
+        features = {
+          "std" = [ "error-code/std" ];
+        };
+      };
+      "codemap" = rec {
+        crateName = "codemap";
+        version = "0.1.3";
+        edition = "2015";
+        sha256 = "091azkslwkcijj3lp9ymb084y9a0wm4fkil7m613ja68r2snkrxr";
+        authors = [
+          "Kevin Mehall <km@kevinmehall.net>"
+        ];
+
+      };
+      "codemap-diagnostic" = rec {
+        crateName = "codemap-diagnostic";
+        version = "0.1.2";
+        edition = "2015";
+        sha256 = "08l1b84bn8r8a72rbvyi2v8a5i0j0kk0a5gr7fb6lmjvw05pf86c";
+        authors = [
+          "Kevin Mehall <km@kevinmehall.net>"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "codemap";
+            packageId = "codemap";
+          }
+          {
+            name = "termcolor";
+            packageId = "termcolor";
+          }
+        ];
+
+      };
+      "colorchoice" = rec {
+        crateName = "colorchoice";
+        version = "1.0.0";
+        edition = "2021";
+        sha256 = "1ix7w85kwvyybwi2jdkl3yva2r2bvdcc3ka2grjfzfgrapqimgxc";
+
+      };
+      "concurrent-queue" = rec {
+        crateName = "concurrent-queue";
+        version = "2.4.0";
+        edition = "2018";
+        sha256 = "0qvk23ynj311adb4z7v89wk3bs65blps4n24q8rgl23vjk6lhq6i";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+          "Taiki Endo <te316e89@gmail.com>"
+          "John Nunley <jtnunley01@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "loom" = [ "dep:loom" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "const-oid" = rec {
+        crateName = "const-oid";
+        version = "0.9.6";
+        edition = "2021";
+        sha256 = "1y0jnqaq7p2wvspnx7qj76m7hjcqpz73qzvr9l2p9n2s51vr6if2";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+        };
+      };
+      "constant_time_eq" = rec {
+        crateName = "constant_time_eq";
+        version = "0.3.0";
+        edition = "2021";
+        sha256 = "1hl0y8frzlhpr58rh8rlg4bm53ax09ikj2i5fk7gpyphvhq4s57p";
+        authors = [
+          "Cesar Eduardo Barros <cesarb@cesarb.eti.br>"
+        ];
+        features = { };
+      };
+      "core-foundation" = rec {
+        crateName = "core-foundation";
+        version = "0.9.4";
+        edition = "2018";
+        sha256 = "13zvbbj07yk3b61b8fhwfzhy35535a583irf23vlcg59j7h9bqci";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "chrono" = [ "dep:chrono" ];
+          "default" = [ "link" ];
+          "link" = [ "core-foundation-sys/link" ];
+          "mac_os_10_7_support" = [ "core-foundation-sys/mac_os_10_7_support" ];
+          "mac_os_10_8_features" = [ "core-foundation-sys/mac_os_10_8_features" ];
+          "uuid" = [ "dep:uuid" ];
+          "with-chrono" = [ "chrono" ];
+          "with-uuid" = [ "uuid" ];
+        };
+        resolvedDefaultFeatures = [ "default" "link" ];
+      };
+      "core-foundation-sys" = rec {
+        crateName = "core-foundation-sys";
+        version = "0.8.6";
+        edition = "2018";
+        sha256 = "13w6sdf06r0hn7bx2b45zxsg1mm2phz34jikm6xc5qrbr6djpsh6";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "default" = [ "link" ];
+        };
+        resolvedDefaultFeatures = [ "default" "link" ];
+      };
+      "count-write" = rec {
+        crateName = "count-write";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "11bswmgr81s3jagdci1pr6qh9vnz9zsbbf2dqpi260daa2mhgmff";
+        authors = [
+          "SOFe <sofe2038@gmail.com>"
+        ];
+        features = {
+          "futures" = [ "futures-io-preview" ];
+          "futures-io-preview" = [ "dep:futures-io-preview" ];
+          "tokio" = [ "tokio-io" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+        };
+      };
+      "countme" = rec {
+        crateName = "countme";
+        version = "3.0.1";
+        edition = "2018";
+        sha256 = "0dn62hhvgmwyxslh14r4nlbvz8h50cp5mnn1qhqsw63vs7yva13p";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        features = {
+          "dashmap" = [ "dep:dashmap" ];
+          "enable" = [ "dashmap" "once_cell" "rustc-hash" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "print_at_exit" = [ "enable" ];
+          "rustc-hash" = [ "dep:rustc-hash" ];
+        };
+      };
+      "cpufeatures" = rec {
+        crateName = "cpufeatures";
+        version = "0.2.12";
+        edition = "2018";
+        sha256 = "012m7rrak4girqlii3jnqwrr73gv1i980q4wra5yyyhvzwk5xzjk";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-linux-android");
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("apple" == target."vendor" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("loongarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+        ];
+
+      };
+      "crc32fast" = rec {
+        crateName = "crc32fast";
+        version = "1.3.2";
+        edition = "2015";
+        sha256 = "03c8f29yx293yf43xar946xbls1g60c207m9drf8ilqhr25vsh5m";
+        authors = [
+          "Sam Rijs <srijs@airpost.net>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "criterion" = rec {
+        crateName = "criterion";
+        version = "0.5.1";
+        edition = "2018";
+        sha256 = "0bv9ipygam3z8kk6k771gh9zi0j0lb9ir0xi1pc075ljg80jvcgj";
+        authors = [
+          "Jorge Aparicio <japaricious@gmail.com>"
+          "Brook Heisler <brookheisler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "anes";
+            packageId = "anes";
+          }
+          {
+            name = "cast";
+            packageId = "cast";
+          }
+          {
+            name = "ciborium";
+            packageId = "ciborium";
+          }
+          {
+            name = "clap";
+            packageId = "clap";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "criterion-plot";
+            packageId = "criterion-plot";
+          }
+          {
+            name = "is-terminal";
+            packageId = "is-terminal";
+          }
+          {
+            name = "itertools";
+            packageId = "itertools 0.10.5";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "oorandom";
+            packageId = "oorandom";
+          }
+          {
+            name = "plotters";
+            packageId = "plotters";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "svg_backend" "area_series" "line_series" ];
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+            optional = true;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tinytemplate";
+            packageId = "tinytemplate";
+          }
+          {
+            name = "walkdir";
+            packageId = "walkdir";
+          }
+        ];
+        features = {
+          "async" = [ "futures" ];
+          "async-std" = [ "dep:async-std" ];
+          "async_futures" = [ "futures/executor" "async" ];
+          "async_smol" = [ "smol" "async" ];
+          "async_std" = [ "async-std" "async" ];
+          "async_tokio" = [ "tokio" "async" ];
+          "csv" = [ "dep:csv" ];
+          "csv_output" = [ "csv" ];
+          "default" = [ "rayon" "plotters" "cargo_bench_support" ];
+          "futures" = [ "dep:futures" ];
+          "plotters" = [ "dep:plotters" ];
+          "rayon" = [ "dep:rayon" ];
+          "smol" = [ "dep:smol" ];
+          "stable" = [ "csv_output" "html_reports" "async_futures" "async_smol" "async_tokio" "async_std" ];
+          "tokio" = [ "dep:tokio" ];
+        };
+        resolvedDefaultFeatures = [ "cargo_bench_support" "default" "html_reports" "plotters" "rayon" ];
+      };
+      "criterion-plot" = rec {
+        crateName = "criterion-plot";
+        version = "0.5.0";
+        edition = "2018";
+        sha256 = "1c866xkjqqhzg4cjvg01f8w6xc1j3j7s58rdksl52skq89iq4l3b";
+        authors = [
+          "Jorge Aparicio <japaricious@gmail.com>"
+          "Brook Heisler <brookheisler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cast";
+            packageId = "cast";
+          }
+          {
+            name = "itertools";
+            packageId = "itertools 0.10.5";
+          }
+        ];
+
+      };
+      "crossbeam-channel" = rec {
+        crateName = "crossbeam-channel";
+        version = "0.5.11";
+        edition = "2021";
+        sha256 = "16v48qdflpw3hgdik70bhsj7hympna79q7ci47rw0mlgnxsw2v8p";
+        dependencies = [
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crossbeam-deque" = rec {
+        crateName = "crossbeam-deque";
+        version = "0.8.5";
+        edition = "2021";
+        sha256 = "03bp38ljx4wj6vvy4fbhx41q8f585zyqix6pncz1mkz93z08qgv1";
+        dependencies = [
+          {
+            name = "crossbeam-epoch";
+            packageId = "crossbeam-epoch";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "crossbeam-epoch/std" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crossbeam-epoch" = rec {
+        crateName = "crossbeam-epoch";
+        version = "0.9.18";
+        edition = "2021";
+        sha256 = "03j2np8llwf376m3fxqx859mgp9f83hj1w34153c7a9c7i5ar0jv";
+        dependencies = [
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "loom" = [ "loom-crate" "crossbeam-utils/loom" ];
+          "loom-crate" = [ "dep:loom-crate" ];
+          "nightly" = [ "crossbeam-utils/nightly" ];
+          "std" = [ "alloc" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "crossbeam-utils" = rec {
+        crateName = "crossbeam-utils";
+        version = "0.8.19";
+        edition = "2021";
+        sha256 = "0iakrb1b8fjqrag7wphl94d10irhbh2fw1g444xslsywqyn3p3i4";
+        features = {
+          "default" = [ "std" ];
+          "loom" = [ "dep:loom" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crypto-common" = rec {
+        crateName = "crypto-common";
+        version = "0.1.6";
+        edition = "2018";
+        sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+            features = [ "more_lengths" ];
+          }
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        features = {
+          "getrandom" = [ "rand_core/getrandom" ];
+          "rand_core" = [ "dep:rand_core" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "curve25519-dalek" = rec {
+        crateName = "curve25519-dalek";
+        version = "4.1.1";
+        edition = "2021";
+        sha256 = "0p7ns5917k6369gajrsbfj24llc5zfm635yh3abla7sb5rm8r6z8";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: ("x86_64" == target."arch" or null);
+          }
+          {
+            name = "curve25519-dalek-derive";
+            packageId = "curve25519-dalek-derive";
+            target = { target, features }: ((!("fiat" == target."curve25519_dalek_backend" or null)) && (!("serial" == target."curve25519_dalek_backend" or null)) && ("x86_64" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "fiat-crypto";
+            packageId = "fiat-crypto";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("fiat" == target."curve25519_dalek_backend" or null);
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "platforms";
+            packageId = "platforms";
+          }
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "default" = [ "alloc" "precomputed-tables" "zeroize" ];
+          "digest" = [ "dep:digest" ];
+          "ff" = [ "dep:ff" ];
+          "group" = [ "dep:group" "rand_core" ];
+          "group-bits" = [ "group" "ff/bits" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "digest" "precomputed-tables" "zeroize" ];
+      };
+      "curve25519-dalek-derive" = rec {
+        crateName = "curve25519-dalek-derive";
+        version = "0.1.1";
+        edition = "2021";
+        sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "darling" = rec {
+        crateName = "darling";
+        version = "0.20.8";
+        edition = "2018";
+        sha256 = "14a38qsi9104kvk1z11rqj0bnz1866dyhnvgvbgzz17d2g6nzqsl";
+        authors = [
+          "Ted Driggs <ted.driggs@outlook.com>"
+        ];
+        dependencies = [
+          {
+            name = "darling_core";
+            packageId = "darling_core";
+          }
+          {
+            name = "darling_macro";
+            packageId = "darling_macro";
+          }
+        ];
+        features = {
+          "default" = [ "suggestions" ];
+          "diagnostics" = [ "darling_core/diagnostics" ];
+          "suggestions" = [ "darling_core/suggestions" ];
+        };
+        resolvedDefaultFeatures = [ "default" "suggestions" ];
+      };
+      "darling_core" = rec {
+        crateName = "darling_core";
+        version = "0.20.8";
+        edition = "2018";
+        sha256 = "03x7s149p06xfwcq0lgkk4yxh6jf7jckny18nzp1yyk87b1g2b4w";
+        authors = [
+          "Ted Driggs <ted.driggs@outlook.com>"
+        ];
+        dependencies = [
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "ident_case";
+            packageId = "ident_case";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "strsim";
+            packageId = "strsim";
+            optional = true;
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" "extra-traits" ];
+          }
+        ];
+        features = {
+          "strsim" = [ "dep:strsim" ];
+          "suggestions" = [ "strsim" ];
+        };
+        resolvedDefaultFeatures = [ "strsim" "suggestions" ];
+      };
+      "darling_macro" = rec {
+        crateName = "darling_macro";
+        version = "0.20.8";
+        edition = "2018";
+        sha256 = "0gwkz0cjfy3fgcc1zmm7azzhj5qpja34s0cklcria4l38sjyss56";
+        procMacro = true;
+        authors = [
+          "Ted Driggs <ted.driggs@outlook.com>"
+        ];
+        dependencies = [
+          {
+            name = "darling_core";
+            packageId = "darling_core";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+          }
+        ];
+
+      };
+      "data-encoding" = rec {
+        crateName = "data-encoding";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "1rcbnwfmfxhlshzbn3r7srm3azqha3mn33yxyqxkzz2wpqcjm5ky";
+        authors = [
+          "Julien Cretin <git@ia0.eu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "der" = rec {
+        crateName = "der";
+        version = "0.7.8";
+        edition = "2021";
+        sha256 = "070bwiyr80800h31c5zd96ckkgagfjgnrrdmz3dzg2lccsd3dypz";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "const-oid";
+            packageId = "const-oid";
+            optional = true;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "arbitrary" = [ "dep:arbitrary" "const-oid?/arbitrary" "std" ];
+          "bytes" = [ "dep:bytes" "alloc" ];
+          "derive" = [ "dep:der_derive" ];
+          "flagset" = [ "dep:flagset" ];
+          "oid" = [ "dep:const-oid" ];
+          "pem" = [ "dep:pem-rfc7468" "alloc" "zeroize" ];
+          "std" = [ "alloc" ];
+          "time" = [ "dep:time" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "oid" "std" "zeroize" ];
+      };
+      "deranged" = rec {
+        crateName = "deranged";
+        version = "0.3.11";
+        edition = "2021";
+        sha256 = "1d1ibqqnr5qdrpw8rclwrf1myn3wf0dygl04idf4j2s49ah6yaxl";
+        authors = [
+          "Jacob Pratt <jacob@jhpratt.dev>"
+        ];
+        dependencies = [
+          {
+            name = "powerfmt";
+            packageId = "powerfmt";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "num" = [ "dep:num-traits" ];
+          "powerfmt" = [ "dep:powerfmt" ];
+          "quickcheck" = [ "dep:quickcheck" "alloc" ];
+          "rand" = [ "dep:rand" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "powerfmt" "serde" "std" ];
+      };
+      "diff" = rec {
+        crateName = "diff";
+        version = "0.1.13";
+        edition = "2015";
+        sha256 = "1j0nzjxci2zqx63hdcihkp0a4dkdmzxd7my4m7zk6cjyfy34j9an";
+        authors = [
+          "Utkarsh Kukreti <utkarshkukreti@gmail.com>"
+        ];
+
+      };
+      "digest" = rec {
+        crateName = "digest";
+        version = "0.10.7";
+        edition = "2018";
+        sha256 = "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer";
+            optional = true;
+          }
+          {
+            name = "crypto-common";
+            packageId = "crypto-common";
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "block-buffer" = [ "dep:block-buffer" ];
+          "const-oid" = [ "dep:const-oid" ];
+          "core-api" = [ "block-buffer" ];
+          "default" = [ "core-api" ];
+          "dev" = [ "blobby" ];
+          "mac" = [ "subtle" ];
+          "oid" = [ "const-oid" ];
+          "rand_core" = [ "crypto-common/rand_core" ];
+          "std" = [ "alloc" "crypto-common/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "block-buffer" "core-api" "default" "mac" "std" "subtle" ];
+      };
+      "dirs" = rec {
+        crateName = "dirs";
+        version = "4.0.0";
+        edition = "2015";
+        sha256 = "0n8020zl4f0frfnzvgb9agvk4a14i1kjz4daqnxkgslndwmaffna";
+        authors = [
+          "Simon Ochsenreither <simon@ochsenreither.de>"
+        ];
+        dependencies = [
+          {
+            name = "dirs-sys";
+            packageId = "dirs-sys";
+          }
+        ];
+
+      };
+      "dirs-next" = rec {
+        crateName = "dirs-next";
+        version = "2.0.0";
+        edition = "2018";
+        sha256 = "1q9kr151h9681wwp6is18750ssghz6j9j7qm7qi1ngcwy7mzi35r";
+        authors = [
+          "The @xdg-rs members"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "dirs-sys-next";
+            packageId = "dirs-sys-next";
+          }
+        ];
+
+      };
+      "dirs-sys" = rec {
+        crateName = "dirs-sys";
+        version = "0.3.7";
+        edition = "2015";
+        sha256 = "19md1cnkazham8a6kh22v12d8hh3raqahfk6yb043vrjr68is78v";
+        authors = [
+          "Simon Ochsenreither <simon@ochsenreither.de>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_users";
+            packageId = "redox_users";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "knownfolders" "objbase" "shlobj" "winbase" "winerror" ];
+          }
+        ];
+
+      };
+      "dirs-sys-next" = rec {
+        crateName = "dirs-sys-next";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "0kavhavdxv4phzj4l0psvh55hszwnr0rcz8sxbvx20pyqi2a3gaf";
+        authors = [
+          "The @xdg-rs members"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_users";
+            packageId = "redox_users";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "knownfolders" "objbase" "shlobj" "winbase" "winerror" ];
+          }
+        ];
+
+      };
+      "doc-comment" = rec {
+        crateName = "doc-comment";
+        version = "0.3.3";
+        edition = "2015";
+        sha256 = "043sprsf3wl926zmck1bm7gw0jq50mb76lkpk49vasfr6ax1p97y";
+        libName = "doc_comment";
+        authors = [
+          "Guillaume Gomez <guillaume1.gomez@gmail.com>"
+        ];
+        features = { };
+      };
+      "document-features" = rec {
+        crateName = "document-features";
+        version = "0.2.8";
+        edition = "2018";
+        sha256 = "15cvgxqngxslgllz15m8aban6wqfgsi6nlhr0g25yfsnd6nq4lpg";
+        procMacro = true;
+        libPath = "lib.rs";
+        authors = [
+          "Slint Developers <info@slint-ui.com>"
+        ];
+        dependencies = [
+          {
+            name = "litrs";
+            packageId = "litrs";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "ed25519" = rec {
+        crateName = "ed25519";
+        version = "2.2.3";
+        edition = "2021";
+        sha256 = "0lydzdf26zbn82g7xfczcac9d7mzm3qgx934ijjrd5hjpjx32m8i";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "pkcs8";
+            packageId = "pkcs8";
+            optional = true;
+          }
+          {
+            name = "signature";
+            packageId = "signature";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "pkcs8?/alloc" ];
+          "default" = [ "std" ];
+          "pem" = [ "alloc" "pkcs8/pem" ];
+          "pkcs8" = [ "dep:pkcs8" ];
+          "serde" = [ "dep:serde" ];
+          "serde_bytes" = [ "serde" "dep:serde_bytes" ];
+          "std" = [ "pkcs8?/std" "signature/std" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "ed25519-dalek" = rec {
+        crateName = "ed25519-dalek";
+        version = "2.1.0";
+        edition = "2021";
+        sha256 = "1h13qm789m9gdjl6jazss80hqi8ll37m0afwcnw23zcbqjp8wqhz";
+        authors = [
+          "isis lovecruft <isis@patternsinthevoid.net>"
+          "Tony Arcieri <bascule@gmail.com>"
+          "Michael Rosenberg <michael@mrosenberg.pub>"
+        ];
+        dependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" ];
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" "rand_core" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "curve25519-dalek/alloc" "ed25519/alloc" "serde?/alloc" "zeroize/alloc" ];
+          "asm" = [ "sha2/asm" ];
+          "batch" = [ "alloc" "merlin" "rand_core" ];
+          "default" = [ "fast" "std" "zeroize" ];
+          "digest" = [ "signature/digest" ];
+          "fast" = [ "curve25519-dalek/precomputed-tables" ];
+          "legacy_compatibility" = [ "curve25519-dalek/legacy_compatibility" ];
+          "merlin" = [ "dep:merlin" ];
+          "pem" = [ "alloc" "ed25519/pem" "pkcs8" ];
+          "pkcs8" = [ "ed25519/pkcs8" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" "ed25519/serde" ];
+          "signature" = [ "dep:signature" ];
+          "std" = [ "alloc" "ed25519/std" "serde?/std" "sha2/std" ];
+          "zeroize" = [ "dep:zeroize" "curve25519-dalek/zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fast" "std" "zeroize" ];
+      };
+      "either" = rec {
+        crateName = "either";
+        version = "1.9.0";
+        edition = "2018";
+        sha256 = "01qy3anr7jal5lpc20791vxrw0nl6vksb5j7x56q2fycgcyy8sm2";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "use_std" ];
+      };
+      "encoding_rs" = rec {
+        crateName = "encoding_rs";
+        version = "0.8.33";
+        edition = "2018";
+        sha256 = "1qa5k4a0ipdrxq4xg9amms9r9pnnfn7nfh2i9m3mw0ka563b6s3j";
+        authors = [
+          "Henri Sivonen <hsivonen@hsivonen.fi>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "default" = [ "alloc" ];
+          "fast-legacy-encode" = [ "fast-hangul-encode" "fast-hanja-encode" "fast-kanji-encode" "fast-gb-hanzi-encode" "fast-big5-hanzi-encode" ];
+          "packed_simd" = [ "dep:packed_simd" ];
+          "serde" = [ "dep:serde" ];
+          "simd-accel" = [ "packed_simd" "packed_simd/into_bits" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" ];
+      };
+      "endian-type" = rec {
+        crateName = "endian-type";
+        version = "0.1.2";
+        edition = "2015";
+        sha256 = "0bbh88zaig1jfqrm7w3gx0pz81kw2jakk3055vbgapw3dmk08ky3";
+        authors = [
+          "Lolirofle <lolipopple@hotmail.com>"
+        ];
+
+      };
+      "enum-primitive-derive" = rec {
+        crateName = "enum-primitive-derive";
+        version = "0.3.0";
+        edition = "2018";
+        sha256 = "0k6wcf58h5kh64yq5nfq71va53kaya0kzxwsjwbgwm2n2zd9axxs";
+        procMacro = true;
+        authors = [
+          "Doug Goldstein <cardoe@cardoe.com>"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+          }
+        ];
+
+      };
+      "equivalent" = rec {
+        crateName = "equivalent";
+        version = "1.0.1";
+        edition = "2015";
+        sha256 = "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl";
+
+      };
+      "erased-serde" = rec {
+        crateName = "erased-serde";
+        version = "0.4.4";
+        edition = "2021";
+        sha256 = "1lx0si6iljzmfpblhn4b0ip3kw2yv4vjyca0riqz3ix311q80wrb";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" ];
+          "default" = [ "std" ];
+          "std" = [ "alloc" "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "errno" = rec {
+        crateName = "errno";
+        version = "0.3.8";
+        edition = "2018";
+        sha256 = "0ia28ylfsp36i27g1qih875cyyy4by2grf80ki8vhgh6vinf8n52";
+        authors = [
+          "Chris Wong <lambda.fairy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_System_Diagnostics_Debug" ];
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "libc/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "error-code" = rec {
+        crateName = "error-code";
+        version = "2.3.1";
+        edition = "2018";
+        sha256 = "08baxlf8qz01lgjsdbfhs193r9y1nlc566s5xvzyf4dzwy8qkwb4";
+        authors = [
+          "Douman <douman@gmx.se>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "str-buf";
+            packageId = "str-buf";
+          }
+        ];
+        features = { };
+      };
+      "event-listener 2.5.3" = rec {
+        crateName = "event-listener";
+        version = "2.5.3";
+        edition = "2018";
+        sha256 = "1q4w3pndc518crld6zsqvvpy9lkzwahp2zgza9kbzmmqh9gif1h2";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+
+      };
+      "event-listener 4.0.3" = rec {
+        crateName = "event-listener";
+        version = "4.0.3";
+        edition = "2021";
+        sha256 = "0vk4smw1vf871vi76af1zn7w69jg3zmpjddpby2qq91bkg21bck7";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "concurrent-queue";
+            packageId = "concurrent-queue";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "parking";
+            packageId = "parking";
+            optional = true;
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "parking" = [ "dep:parking" ];
+          "portable-atomic" = [ "portable-atomic-util" "portable_atomic_crate" ];
+          "portable-atomic-util" = [ "dep:portable-atomic-util" ];
+          "portable_atomic_crate" = [ "dep:portable_atomic_crate" ];
+          "std" = [ "concurrent-queue/std" "parking" ];
+        };
+        resolvedDefaultFeatures = [ "parking" "std" ];
+      };
+      "event-listener 5.2.0" = rec {
+        crateName = "event-listener";
+        version = "5.2.0";
+        edition = "2021";
+        sha256 = "14fcnjgpfl22645nhc3hzkdq3a1v0srqacc3kfassg7sjj8vhprb";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "concurrent-queue";
+            packageId = "concurrent-queue";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "parking";
+            packageId = "parking";
+            optional = true;
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "parking" = [ "dep:parking" ];
+          "portable-atomic" = [ "portable-atomic-util" "portable_atomic_crate" ];
+          "portable-atomic-util" = [ "dep:portable-atomic-util" ];
+          "portable_atomic_crate" = [ "dep:portable_atomic_crate" ];
+          "std" = [ "concurrent-queue/std" "parking" ];
+        };
+        resolvedDefaultFeatures = [ "default" "parking" "std" ];
+      };
+      "event-listener-strategy 0.4.0" = rec {
+        crateName = "event-listener-strategy";
+        version = "0.4.0";
+        edition = "2018";
+        sha256 = "1lwprdjqp2ibbxhgm9khw7s7y7k4xiqj5i5yprqiks6mnrq4v3lm";
+        authors = [
+          "John Nunley <dev@notgull.net>"
+        ];
+        dependencies = [
+          {
+            name = "event-listener";
+            packageId = "event-listener 4.0.3";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "event-listener/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "event-listener-strategy 0.5.0" = rec {
+        crateName = "event-listener-strategy";
+        version = "0.5.0";
+        edition = "2021";
+        sha256 = "148jflvjrq0zrr3dx3srv88jksj1klm4amy3b9fifjdpm75azvgy";
+        authors = [
+          "John Nunley <dev@notgull.net>"
+        ];
+        dependencies = [
+          {
+            name = "event-listener";
+            packageId = "event-listener 5.2.0";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "event-listener/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "fastcdc" = rec {
+        crateName = "fastcdc";
+        version = "3.1.0";
+        edition = "2018";
+        sha256 = "1wi82qd58j3ysf8m2dhb092qga6rj1wwbppgsajabadzjz862457";
+        authors = [
+          "Nathan Fiedler <nathanfiedler@fastmail.fm>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+            optional = true;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "io-util" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "io-util" "rt" "rt-multi-thread" "macros" ];
+          }
+        ];
+        features = {
+          "async-stream" = [ "dep:async-stream" ];
+          "futures" = [ "dep:futures" ];
+          "tokio" = [ "dep:tokio" "tokio-stream" "async-stream" ];
+          "tokio-stream" = [ "dep:tokio-stream" ];
+        };
+        resolvedDefaultFeatures = [ "async-stream" "default" "tokio" "tokio-stream" ];
+      };
+      "fastrand" = rec {
+        crateName = "fastrand";
+        version = "2.0.1";
+        edition = "2018";
+        sha256 = "19flpv5zbzpf0rk4x77z4zf25in0brg8l7m304d3yrf47qvwxjr5";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "getrandom" = [ "dep:getrandom" ];
+          "js" = [ "std" "getrandom" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "fd-lock" = rec {
+        crateName = "fd-lock";
+        version = "3.0.13";
+        edition = "2018";
+        sha256 = "1df1jdncda67g65hrnmd2zsl7q5hdn8cm84chdalxndsx7akw0zg";
+        authors = [
+          "Yoshua Wuyts <yoshuawuyts@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            target = { target, features }: (target."unix" or false);
+            features = [ "fs" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Storage_FileSystem" "Win32_System_IO" ];
+          }
+        ];
+
+      };
+      "fiat-crypto" = rec {
+        crateName = "fiat-crypto";
+        version = "0.2.5";
+        edition = "2018";
+        sha256 = "1dxn0g50pv0ppal779vi7k40fr55pbhkyv4in7i13pgl4sn3wmr7";
+        authors = [
+          "Fiat Crypto library authors <jgross@mit.edu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+      };
+      "filetime" = rec {
+        crateName = "filetime";
+        version = "0.2.23";
+        edition = "2018";
+        sha256 = "1za0sbq7fqidk8aaq9v7m9ms0sv8mmi49g6p5cphpan819q4gr0y";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.4.1";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Storage_FileSystem" ];
+          }
+        ];
+
+      };
+      "fixedbitset" = rec {
+        crateName = "fixedbitset";
+        version = "0.4.2";
+        edition = "2015";
+        sha256 = "101v41amgv5n9h4hcghvrbfk5vrncx1jwm35rn5szv4rk55i7rqc";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "flate2" = rec {
+        crateName = "flate2";
+        version = "1.0.28";
+        edition = "2018";
+        sha256 = "03llhsh4gqdirnfxxb9g2w9n0721dyn4yjir3pz7z4vjaxb3yc26";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Josh Triplett <josh@joshtriplett.org>"
+        ];
+        dependencies = [
+          {
+            name = "crc32fast";
+            packageId = "crc32fast";
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "with-alloc" ];
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!("emscripten" == target."os" or null)));
+            features = [ "with-alloc" ];
+          }
+        ];
+        features = {
+          "any_zlib" = [ "any_impl" ];
+          "cloudflare-zlib-sys" = [ "dep:cloudflare-zlib-sys" ];
+          "cloudflare_zlib" = [ "any_zlib" "cloudflare-zlib-sys" ];
+          "default" = [ "rust_backend" ];
+          "libz-ng-sys" = [ "dep:libz-ng-sys" ];
+          "libz-sys" = [ "dep:libz-sys" ];
+          "miniz-sys" = [ "rust_backend" ];
+          "miniz_oxide" = [ "dep:miniz_oxide" ];
+          "rust_backend" = [ "miniz_oxide" "any_impl" ];
+          "zlib" = [ "any_zlib" "libz-sys" ];
+          "zlib-default" = [ "any_zlib" "libz-sys/default" ];
+          "zlib-ng" = [ "any_zlib" "libz-ng-sys" ];
+          "zlib-ng-compat" = [ "zlib" "libz-sys/zlib-ng" ];
+        };
+        resolvedDefaultFeatures = [ "any_impl" "default" "miniz_oxide" "rust_backend" ];
+      };
+      "fnv" = rec {
+        crateName = "fnv";
+        version = "1.0.7";
+        edition = "2015";
+        sha256 = "1hc2mcqha06aibcaza94vbi81j6pr9a1bbxrxjfhc91zin8yr7iz";
+        libPath = "lib.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "form_urlencoded" = rec {
+        crateName = "form_urlencoded";
+        version = "1.2.1";
+        edition = "2018";
+        sha256 = "0milh8x7nl4f450s3ddhg57a3flcv6yq8hlkyk6fyr3mcb128dp1";
+        authors = [
+          "The rust-url developers"
+        ];
+        dependencies = [
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "percent-encoding/alloc" ];
+          "default" = [ "std" ];
+          "std" = [ "alloc" "percent-encoding/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "fs2" = rec {
+        crateName = "fs2";
+        version = "0.4.3";
+        edition = "2015";
+        sha256 = "04v2hwk7035c088f19mfl5b1lz84gnvv2hv6m935n0hmirszqr4m";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "handleapi" "processthreadsapi" "winerror" "fileapi" "winbase" "std" ];
+          }
+        ];
+
+      };
+      "fuse-backend-rs" = rec {
+        crateName = "fuse-backend-rs";
+        version = "0.11.0";
+        edition = "2018";
+        sha256 = "0jyldvp0kvjk21j5vqga42lkksaf7zg8jkj3l6h2dv20kyl66nif";
+        authors = [
+          "Liu Bo <bo.liu@linux.alibaba.com>"
+          "Liu Jiang <gerry@linux.alibaba.com>"
+          "Peng Tao <bergwolf@hyper.sh>"
+        ];
+        dependencies = [
+          {
+            name = "arc-swap";
+            packageId = "arc-swap";
+          }
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "caps";
+            packageId = "caps";
+            optional = true;
+            target = { target, features }: ("linux" == target."os" or null);
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            optional = true;
+            target = { target, features }: ("macos" == target."os" or null);
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            features = [ "os-poll" "os-ext" ];
+          }
+          {
+            name = "nix";
+            packageId = "nix 0.24.3";
+          }
+          {
+            name = "vhost";
+            packageId = "vhost";
+            optional = true;
+            features = [ "vhost-user-slave" ];
+          }
+          {
+            name = "virtio-queue";
+            packageId = "virtio-queue";
+            optional = true;
+          }
+          {
+            name = "vm-memory";
+            packageId = "vm-memory";
+            features = [ "backend-mmap" ];
+          }
+          {
+            name = "vmm-sys-util";
+            packageId = "vmm-sys-util";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "vm-memory";
+            packageId = "vm-memory";
+            features = [ "backend-mmap" "backend-bitmap" ];
+          }
+          {
+            name = "vmm-sys-util";
+            packageId = "vmm-sys-util";
+          }
+        ];
+        features = {
+          "async-io" = [ "async-trait" "tokio-uring" "tokio/fs" "tokio/net" "tokio/sync" "tokio/rt" "tokio/macros" "io-uring" ];
+          "async-trait" = [ "dep:async-trait" ];
+          "caps" = [ "dep:caps" ];
+          "core-foundation-sys" = [ "dep:core-foundation-sys" ];
+          "dbs-snapshot" = [ "dep:dbs-snapshot" ];
+          "default" = [ "fusedev" ];
+          "fusedev" = [ "vmm-sys-util" "caps" "core-foundation-sys" ];
+          "io-uring" = [ "dep:io-uring" ];
+          "persist" = [ "dbs-snapshot" "versionize" "versionize_derive" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-uring" = [ "dep:tokio-uring" ];
+          "versionize" = [ "dep:versionize" ];
+          "versionize_derive" = [ "dep:versionize_derive" ];
+          "vhost" = [ "dep:vhost" ];
+          "vhost-user-fs" = [ "virtiofs" "vhost" "caps" ];
+          "virtio-queue" = [ "dep:virtio-queue" ];
+          "virtiofs" = [ "virtio-queue" "caps" "vmm-sys-util" ];
+          "vmm-sys-util" = [ "dep:vmm-sys-util" ];
+        };
+        resolvedDefaultFeatures = [ "caps" "core-foundation-sys" "default" "fusedev" "vhost" "vhost-user-fs" "virtio-queue" "virtiofs" "vmm-sys-util" ];
+      };
+      "futures" = rec {
+        crateName = "futures";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1c04g14bccmprwsvx2j9m2blhwrynq7vhl151lsvcv4gi0b6jp34";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-executor";
+            packageId = "futures-executor";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" "futures-sink/alloc" "futures-channel/alloc" "futures-util/alloc" ];
+          "async-await" = [ "futures-util/async-await" "futures-util/async-await-macro" ];
+          "bilock" = [ "futures-util/bilock" ];
+          "compat" = [ "std" "futures-util/compat" ];
+          "default" = [ "std" "async-await" "executor" ];
+          "executor" = [ "std" "futures-executor/std" ];
+          "futures-executor" = [ "dep:futures-executor" ];
+          "io-compat" = [ "compat" "futures-util/io-compat" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "futures-io/std" "futures-sink/std" "futures-util/std" "futures-util/io" "futures-util/channel" ];
+          "thread-pool" = [ "executor" "futures-executor/thread-pool" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" "futures-channel/unstable" "futures-io/unstable" "futures-util/unstable" ];
+          "write-all-vectored" = [ "futures-util/write-all-vectored" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "default" "executor" "futures-executor" "std" ];
+      };
+      "futures-channel" = rec {
+        crateName = "futures-channel";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" ];
+          "default" = [ "std" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "sink" = [ "futures-sink" ];
+          "std" = [ "alloc" "futures-core/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "futures-sink" "sink" "std" ];
+      };
+      "futures-core" = rec {
+        crateName = "futures-core";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        features = {
+          "default" = [ "std" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-executor" = rec {
+        crateName = "futures-executor";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "07dh08gs9vfll2h36kq32q9xd86xm6lyl9xikmmwlkqnmrrgqxm5";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "std" = [ "futures-core/std" "futures-task/std" "futures-util/std" ];
+          "thread-pool" = [ "std" "num_cpus" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "futures-io" = rec {
+        crateName = "futures-io";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1hgh25isvsr4ybibywhr4dpys8mjnscw4wfxxwca70cn1gi26im4";
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "futures-lite" = rec {
+        crateName = "futures-lite";
+        version = "2.3.0";
+        edition = "2021";
+        sha256 = "19gk4my8zhfym6gwnpdjiyv2hw8cc098skkbkhryjdaf0yspwljj";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+          "Contributors to futures-rs"
+        ];
+        dependencies = [
+          {
+            name = "fastrand";
+            packageId = "fastrand";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            optional = true;
+          }
+          {
+            name = "parking";
+            packageId = "parking";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+        features = {
+          "default" = [ "race" "std" ];
+          "fastrand" = [ "dep:fastrand" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "memchr" = [ "dep:memchr" ];
+          "parking" = [ "dep:parking" ];
+          "race" = [ "fastrand" ];
+          "std" = [ "alloc" "fastrand/std" "futures-io" "parking" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fastrand" "futures-io" "parking" "race" "std" ];
+      };
+      "futures-macro" = rec {
+        crateName = "futures-macro";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "futures-sink" = rec {
+        crateName = "futures-sink";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-task" = rec {
+        crateName = "futures-task";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "futures-timer" = rec {
+        crateName = "futures-timer";
+        version = "3.0.2";
+        edition = "2018";
+        sha256 = "0b5v7lk9838ix6jdcrainsyrh7xrf24pwm61dp13907qkn806jz6";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "gloo-timers" = [ "dep:gloo-timers" ];
+          "send_wrapper" = [ "dep:send_wrapper" ];
+          "wasm-bindgen" = [ "gloo-timers" "send_wrapper" ];
+        };
+      };
+      "futures-util" = rec {
+        crateName = "futures-util";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-macro";
+            packageId = "futures-macro";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "pin-utils";
+            packageId = "pin-utils";
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" ];
+          "async-await-macro" = [ "async-await" "futures-macro" ];
+          "channel" = [ "std" "futures-channel" ];
+          "compat" = [ "std" "futures_01" ];
+          "default" = [ "std" "async-await" "async-await-macro" ];
+          "futures-channel" = [ "dep:futures-channel" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-macro" = [ "dep:futures-macro" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "futures_01" = [ "dep:futures_01" ];
+          "io" = [ "std" "futures-io" "memchr" ];
+          "io-compat" = [ "io" "compat" "tokio-io" ];
+          "memchr" = [ "dep:memchr" ];
+          "portable-atomic" = [ "futures-core/portable-atomic" ];
+          "sink" = [ "futures-sink" ];
+          "slab" = [ "dep:slab" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "slab" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" ];
+          "write-all-vectored" = [ "io" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "async-await-macro" "channel" "default" "futures-channel" "futures-io" "futures-macro" "futures-sink" "io" "memchr" "sink" "slab" "std" ];
+      };
+      "fxhash" = rec {
+        crateName = "fxhash";
+        version = "0.2.1";
+        edition = "2015";
+        sha256 = "037mb9ichariqi45xm6mz0b11pa92gj38ba0409z3iz239sns6y3";
+        libPath = "lib.rs";
+        authors = [
+          "cbreeden <github@u.breeden.cc>"
+        ];
+        dependencies = [
+          {
+            name = "byteorder";
+            packageId = "byteorder";
+          }
+        ];
+
+      };
+      "gcp_auth" = rec {
+        crateName = "gcp_auth";
+        version = "0.10.0";
+        edition = "2021";
+        sha256 = "1m7lsh2gc7n9p0gs9k2qbxsrvchw1vz6dyz9a2ma322vd3m72b6y";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "base64";
+            packageId = "base64";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            features = [ "serde" ];
+          }
+          {
+            name = "home";
+            packageId = "home";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            features = [ "client" "runtime" "http2" ];
+          }
+          {
+            name = "hyper-rustls";
+            packageId = "hyper-rustls";
+            usesDefaultFeatures = false;
+            features = [ "tokio-runtime" "http1" "http2" ];
+          }
+          {
+            name = "ring";
+            packageId = "ring";
+          }
+          {
+            name = "rustls";
+            packageId = "rustls 0.21.10";
+          }
+          {
+            name = "rustls-pemfile";
+            packageId = "rustls-pemfile 1.0.4";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" "rc" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "sync" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "tracing-futures";
+            packageId = "tracing-futures";
+          }
+          {
+            name = "url";
+            packageId = "url";
+          }
+          {
+            name = "which";
+            packageId = "which 5.0.0";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "parking_lot" "rt-multi-thread" ];
+          }
+        ];
+        features = {
+          "default" = [ "hyper-rustls/rustls-native-certs" ];
+          "webpki-roots" = [ "hyper-rustls/webpki-roots" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "genawaiter" = rec {
+        crateName = "genawaiter";
+        version = "0.99.1";
+        edition = "2018";
+        sha256 = "1861a6vy9lc9a8lbw496m9j9jcjcn9nf7rkm6jqkkpnb3cvd0sy8";
+        authors = [
+          "John Simon <john@whatisaph.one>"
+        ];
+        dependencies = [
+          {
+            name = "genawaiter-macro";
+            packageId = "genawaiter-macro";
+          }
+        ];
+        features = {
+          "default" = [ "proc_macro" ];
+          "futures-core" = [ "dep:futures-core" ];
+          "futures03" = [ "futures-core" ];
+          "genawaiter-proc-macro" = [ "dep:genawaiter-proc-macro" ];
+          "proc-macro-hack" = [ "dep:proc-macro-hack" ];
+          "proc_macro" = [ "genawaiter-proc-macro" "proc-macro-hack" "genawaiter-macro/proc_macro" ];
+        };
+      };
+      "genawaiter-macro" = rec {
+        crateName = "genawaiter-macro";
+        version = "0.99.1";
+        edition = "2018";
+        sha256 = "1g6zmr88fk48f1ksz9ik1i2mwjsiam9s4p9aybhvs2zwzphxychb";
+        authors = [
+          "Devin R <devin.ragotzy@gmail.com>"
+        ];
+        features = { };
+      };
+      "generic-array" = rec {
+        crateName = "generic-array";
+        version = "0.14.7";
+        edition = "2015";
+        sha256 = "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45";
+        libName = "generic_array";
+        authors = [
+          "BartΕ‚omiej KamiΕ„ski <fizyk20@gmail.com>"
+          "Aaron Trent <novacrazy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "more_lengths" ];
+      };
+      "getrandom" = rec {
+        crateName = "getrandom";
+        version = "0.2.12";
+        edition = "2018";
+        sha256 = "1d8jb9bv38nkwlqqdjcav6gxckgwc9g30pm3qq506rvncpm9400r";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "js" = [ "wasm-bindgen" "js-sys" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "libc/rustc-dep-of-std" "wasi/rustc-dep-of-std" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "gimli" = rec {
+        crateName = "gimli";
+        version = "0.28.1";
+        edition = "2018";
+        sha256 = "0lv23wc8rxvmjia3mcxc6hj9vkqnv1bqq0h8nzjcgf71mrxx6wa2";
+        features = {
+          "default" = [ "read-all" "write" ];
+          "endian-reader" = [ "read" "dep:stable_deref_trait" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "read" = [ "read-core" ];
+          "read-all" = [ "read" "std" "fallible-iterator" "endian-reader" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" ];
+          "std" = [ "fallible-iterator?/std" "stable_deref_trait?/std" ];
+          "write" = [ "dep:indexmap" ];
+        };
+        resolvedDefaultFeatures = [ "read" "read-core" ];
+      };
+      "glob" = rec {
+        crateName = "glob";
+        version = "0.3.1";
+        edition = "2015";
+        sha256 = "16zca52nglanv23q5qrwd5jinw3d3as5ylya6y1pbx47vkxvrynj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+
+      };
+      "h2 0.3.24" = rec {
+        crateName = "h2";
+        version = "0.3.24";
+        edition = "2018";
+        sha256 = "1jf9488b66nayxzp3iw3b2rb64y49hdbbywnv9wfwrsv14i48b5v";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap 2.1.0";
+            features = [ "std" ];
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            features = [ "codec" "io" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt-multi-thread" "macros" "sync" "net" ];
+          }
+        ];
+        features = { };
+      };
+      "h2 0.4.3" = rec {
+        crateName = "h2";
+        version = "0.4.3";
+        edition = "2021";
+        sha256 = "1m4rj76zl77jany6p10k4mm1cqwsrlc1dmgmxwp3jy7kwk92vvji";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http 1.1.0";
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap 2.1.0";
+            features = [ "std" ];
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            features = [ "codec" "io" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt-multi-thread" "macros" "sync" "net" ];
+          }
+        ];
+        features = { };
+      };
+      "half" = rec {
+        crateName = "half";
+        version = "1.8.2";
+        edition = "2018";
+        sha256 = "1mqbmx2m9qd4lslkb42fzgldsklhv9c4bxsc8j82r80d8m24mfza";
+        authors = [
+          "Kathryn Long <squeeself@gmail.com>"
+        ];
+        features = {
+          "bytemuck" = [ "dep:bytemuck" ];
+          "num-traits" = [ "dep:num-traits" ];
+          "serde" = [ "dep:serde" ];
+          "serialize" = [ "serde" ];
+          "std" = [ "alloc" ];
+          "zerocopy" = [ "dep:zerocopy" ];
+        };
+      };
+      "hashbrown 0.12.3" = rec {
+        crateName = "hashbrown";
+        version = "0.12.3";
+        edition = "2021";
+        sha256 = "1268ka4750pyg2pbgsr43f0289l5zah4arir2k4igx5a8c6fg7la";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "ahash-compile-time-rng" = [ "ahash/compile-time-rng" ];
+          "alloc" = [ "dep:alloc" ];
+          "bumpalo" = [ "dep:bumpalo" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "ahash" "inline-more" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "raw" ];
+      };
+      "hashbrown 0.14.3" = rec {
+        crateName = "hashbrown";
+        version = "0.14.3";
+        edition = "2021";
+        sha256 = "012nywlg0lj9kwanh69my5x67vjlfmzfi9a0rq4qvis2j8fil3r9";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "alloc" = [ "dep:alloc" ];
+          "allocator-api2" = [ "dep:allocator-api2" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "ahash" "inline-more" "allocator-api2" ];
+          "equivalent" = [ "dep:equivalent" ];
+          "nightly" = [ "allocator-api2?/nightly" "bumpalo/allocator_api" ];
+          "rayon" = [ "dep:rayon" ];
+          "rkyv" = [ "dep:rkyv" ];
+          "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "inline-more" "raw" ];
+      };
+      "heck" = rec {
+        crateName = "heck";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1a7mqsnycv5z4z5vnv1k34548jzmc0ajic7c1j8jsaspnhw5ql4m";
+        authors = [
+          "Without Boats <woboats@gmail.com>"
+        ];
+        features = {
+          "unicode" = [ "unicode-segmentation" ];
+          "unicode-segmentation" = [ "dep:unicode-segmentation" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "hermit-abi" = rec {
+        crateName = "hermit-abi";
+        version = "0.3.4";
+        edition = "2021";
+        sha256 = "07v5vbwb9kx0yxgdpx15h38ynpzhaqx5ncriryipypi5707hwgax";
+        authors = [
+          "Stefan Lankes"
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins/rustc-dep-of-std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "hex" = rec {
+        crateName = "hex";
+        version = "0.4.3";
+        edition = "2018";
+        sha256 = "0w1a4davm1lgzpamwnba907aysmlrnygbqmfis2mqjx5m552a93z";
+        authors = [
+          "KokaKiwi <kokakiwi@kokakiwi.net>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "hex-literal" = rec {
+        crateName = "hex-literal";
+        version = "0.4.1";
+        edition = "2021";
+        sha256 = "0iny5inkixsdr41pm2vkqh3fl66752z5j5c0cdxw16yl9ryjdqkg";
+        authors = [
+          "RustCrypto Developers"
+        ];
+
+      };
+      "home" = rec {
+        crateName = "home";
+        version = "0.5.9";
+        edition = "2021";
+        sha256 = "19grxyg35rqfd802pcc9ys1q3lafzlcjcv2pl2s5q8xpyr5kblg3";
+        authors = [
+          "Brian Anderson <andersrb@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_UI_Shell" "Win32_System_Com" ];
+          }
+        ];
+
+      };
+      "http 0.2.11" = rec {
+        crateName = "http";
+        version = "0.2.11";
+        edition = "2018";
+        sha256 = "1fwz3mhh86h5kfnr5767jlx9agpdggclq7xsqx930fflzakb2iw9";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+        ];
+
+      };
+      "http 1.1.0" = rec {
+        crateName = "http";
+        version = "1.1.0";
+        edition = "2018";
+        sha256 = "0n426lmcxas6h75c2cp25m933pswlrfjz10v91vc62vib2sdvf91";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "http-body 0.4.6" = rec {
+        crateName = "http-body";
+        version = "0.4.6";
+        edition = "2018";
+        sha256 = "1lmyjfk6bqk6k9gkn1dxq770sb78pqbqshga241hr5p995bb5skw";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "http-body 1.0.0" = rec {
+        crateName = "http-body";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "0hyn8n3iadrbwq8y0p1rl1275s4nm49bllw5wji29g4aa3dqbb0w";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "http";
+            packageId = "http 1.1.0";
+          }
+        ];
+
+      };
+      "http-body-util" = rec {
+        crateName = "http-body-util";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "07agldas2qgcfc05ckiarlmf9vzragbda823nqhrqrc6mjrghx84";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "http";
+            packageId = "http 1.1.0";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 1.0.0";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "httparse" = rec {
+        crateName = "httparse";
+        version = "1.8.0";
+        edition = "2018";
+        sha256 = "010rrfahm1jss3p022fqf3j3jmm72vhn4iqhykahb9ynpaag75yq";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "httpdate" = rec {
+        crateName = "httpdate";
+        version = "1.0.3";
+        edition = "2021";
+        sha256 = "1aa9rd2sac0zhjqh24c9xvir96g188zldkx0hr6dnnlx5904cfyz";
+        authors = [
+          "Pyfisch <pyfisch@posteo.org>"
+        ];
+
+      };
+      "humantime" = rec {
+        crateName = "humantime";
+        version = "2.1.0";
+        edition = "2018";
+        sha256 = "1r55pfkkf5v0ji1x6izrjwdq9v6sc7bv99xj6srywcar37xmnfls";
+        authors = [
+          "Paul Colomiets <paul@colomiets.name>"
+        ];
+
+      };
+      "hyper 0.14.28" = rec {
+        crateName = "hyper";
+        version = "0.14.28";
+        edition = "2018";
+        sha256 = "107gkvqx4h9bl17d602zkm2dgpfq86l2dr36yzfsi8l3xcsy35mz";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "h2";
+            packageId = "h2 0.3.24";
+            optional = true;
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 0.4.6";
+          }
+          {
+            name = "httparse";
+            packageId = "httparse";
+          }
+          {
+            name = "httpdate";
+            packageId = "httpdate";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            features = [ "all" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "want";
+            packageId = "want";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "macros" "io-std" "io-util" "rt" "rt-multi-thread" "sync" "time" "test-util" ];
+          }
+        ];
+        features = {
+          "ffi" = [ "libc" ];
+          "full" = [ "client" "http1" "http2" "server" "stream" "runtime" ];
+          "h2" = [ "dep:h2" ];
+          "http2" = [ "h2" ];
+          "libc" = [ "dep:libc" ];
+          "runtime" = [ "tcp" "tokio/rt" "tokio/time" ];
+          "socket2" = [ "dep:socket2" ];
+          "tcp" = [ "socket2" "tokio/net" "tokio/rt" "tokio/time" ];
+        };
+        resolvedDefaultFeatures = [ "client" "default" "full" "h2" "http1" "http2" "runtime" "server" "socket2" "stream" "tcp" ];
+      };
+      "hyper 1.2.0" = rec {
+        crateName = "hyper";
+        version = "1.2.0";
+        edition = "2021";
+        sha256 = "0fi6k7hz5fmdph0a5r8hw50d7h2n9zxkizmafcmb65f67bblhr8q";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            optional = true;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "h2";
+            packageId = "h2 0.4.3";
+            optional = true;
+          }
+          {
+            name = "http";
+            packageId = "http 1.1.0";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 1.0.0";
+          }
+          {
+            name = "httparse";
+            packageId = "httparse";
+            optional = true;
+          }
+          {
+            name = "httpdate";
+            packageId = "httpdate";
+            optional = true;
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+            optional = true;
+            features = [ "const_generics" "const_new" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            features = [ "sink" ];
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "macros" "net" "io-std" "io-util" "rt" "rt-multi-thread" "sync" "time" "test-util" ];
+          }
+        ];
+        features = {
+          "client" = [ "dep:want" "dep:pin-project-lite" "dep:smallvec" ];
+          "ffi" = [ "dep:libc" "dep:http-body-util" ];
+          "full" = [ "client" "http1" "http2" "server" ];
+          "http1" = [ "dep:futures-channel" "dep:futures-util" "dep:httparse" "dep:itoa" ];
+          "http2" = [ "dep:futures-channel" "dep:futures-util" "dep:h2" ];
+          "server" = [ "dep:httpdate" "dep:pin-project-lite" "dep:smallvec" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "default" "http1" "http2" "server" ];
+      };
+      "hyper-rustls" = rec {
+        crateName = "hyper-rustls";
+        version = "0.24.2";
+        edition = "2021";
+        sha256 = "1475j4a2nczz4aajzzsq3hpwg1zacmzbqg393a14j80ff8izsgpc";
+        dependencies = [
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            usesDefaultFeatures = false;
+            features = [ "client" ];
+          }
+          {
+            name = "rustls";
+            packageId = "rustls 0.21.10";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rustls-native-certs";
+            packageId = "rustls-native-certs 0.6.3";
+            optional = true;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "tokio-rustls";
+            packageId = "tokio-rustls 0.24.1";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            features = [ "full" ];
+          }
+          {
+            name = "rustls";
+            packageId = "rustls 0.21.10";
+            usesDefaultFeatures = false;
+            features = [ "tls12" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-std" "macros" "net" "rt-multi-thread" ];
+          }
+        ];
+        features = {
+          "acceptor" = [ "hyper/server" "tokio-runtime" ];
+          "default" = [ "native-tokio" "http1" "tls12" "logging" "acceptor" ];
+          "http1" = [ "hyper/http1" ];
+          "http2" = [ "hyper/http2" ];
+          "log" = [ "dep:log" ];
+          "logging" = [ "log" "tokio-rustls/logging" "rustls/logging" ];
+          "native-tokio" = [ "tokio-runtime" "rustls-native-certs" ];
+          "rustls-native-certs" = [ "dep:rustls-native-certs" ];
+          "tls12" = [ "tokio-rustls/tls12" "rustls/tls12" ];
+          "tokio-runtime" = [ "hyper/runtime" ];
+          "webpki-roots" = [ "dep:webpki-roots" ];
+          "webpki-tokio" = [ "tokio-runtime" "webpki-roots" ];
+        };
+        resolvedDefaultFeatures = [ "http1" "http2" "rustls-native-certs" "tokio-runtime" ];
+      };
+      "hyper-timeout" = rec {
+        crateName = "hyper-timeout";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1c8k3g8k2yh1gxvsx9p7amkimgxhl9kafwpj7jyf8ywc5r45ifdv";
+        authors = [
+          "Herman J. Radtke III <herman@hermanradtke.com>"
+        ];
+        dependencies = [
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            features = [ "client" ];
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "tokio-io-timeout";
+            packageId = "tokio-io-timeout";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            features = [ "client" "http1" "tcp" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-std" "io-util" "macros" ];
+          }
+        ];
+
+      };
+      "hyper-util" = rec {
+        crateName = "hyper-util";
+        version = "0.1.3";
+        edition = "2021";
+        sha256 = "1akngan7j0n2n0wd25c6952mvqbkj9gp1lcwzyxjc0d37l8yyf6a";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http 1.1.0";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 1.0.0";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 1.2.0";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            features = [ "all" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "net" "rt" "time" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 1.2.0";
+            features = [ "full" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "test-util" ];
+          }
+        ];
+        features = {
+          "client" = [ "hyper/client" "dep:tracing" "dep:futures-channel" "dep:tower" "dep:tower-service" ];
+          "client-legacy" = [ "client" ];
+          "full" = [ "client" "client-legacy" "server" "server-auto" "service" "http1" "http2" "tokio" ];
+          "http1" = [ "hyper/http1" ];
+          "http2" = [ "hyper/http2" ];
+          "server" = [ "hyper/server" ];
+          "server-auto" = [ "server" "http1" "http2" ];
+          "service" = [ "dep:tower" "dep:tower-service" ];
+          "tokio" = [ "dep:tokio" "dep:socket2" ];
+        };
+        resolvedDefaultFeatures = [ "default" "http1" "http2" "server" "server-auto" "tokio" ];
+      };
+      "iana-time-zone" = rec {
+        crateName = "iana-time-zone";
+        version = "0.1.60";
+        edition = "2018";
+        sha256 = "0hdid5xz3jznm04lysjm3vi93h3c523w0hcc3xba47jl3ddbpzz7";
+        authors = [
+          "Andrew Straw <strawman@astraw.com>"
+          "RenΓ© Kijewski <rene.kijewski@fu-berlin.de>"
+          "Ryan Lopopolo <rjl@hyperbo.la>"
+        ];
+        dependencies = [
+          {
+            name = "android_system_properties";
+            packageId = "android_system_properties";
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "iana-time-zone-haiku";
+            packageId = "iana-time-zone-haiku";
+            target = { target, features }: ("haiku" == target."os" or null);
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "windows-core";
+            packageId = "windows-core";
+            target = { target, features }: ("windows" == target."os" or null);
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "fallback" ];
+      };
+      "iana-time-zone-haiku" = rec {
+        crateName = "iana-time-zone-haiku";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        authors = [
+          "RenΓ© Kijewski <crates.io@k6i.de>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "ident_case" = rec {
+        crateName = "ident_case";
+        version = "1.0.1";
+        edition = "2015";
+        sha256 = "0fac21q6pwns8gh1hz3nbq15j8fi441ncl6w4vlnd1cmc55kiq5r";
+        authors = [
+          "Ted Driggs <ted.driggs@outlook.com>"
+        ];
+
+      };
+      "idna" = rec {
+        crateName = "idna";
+        version = "0.5.0";
+        edition = "2018";
+        sha256 = "1xhjrcjqq0l5bpzvdgylvpkgk94panxgsirzhjnnqfdgc4a9nkb3";
+        authors = [
+          "The rust-url developers"
+        ];
+        dependencies = [
+          {
+            name = "unicode-bidi";
+            packageId = "unicode-bidi";
+            usesDefaultFeatures = false;
+            features = [ "hardcoded-data" ];
+          }
+          {
+            name = "unicode-normalization";
+            packageId = "unicode-normalization";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" "unicode-bidi/std" "unicode-normalization/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "imbl" = rec {
+        crateName = "imbl";
+        version = "2.0.3";
+        edition = "2018";
+        sha256 = "11bhchs0d1bbbmr8ari4y4d62vqxs7xg4fkhjlhgbv98h0n193cp";
+        authors = [
+          "Bodil Stokke <bodil@bodil.org>"
+          "Joe Neeman <joeneeman@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitmaps";
+            packageId = "bitmaps";
+          }
+          {
+            name = "imbl-sized-chunks";
+            packageId = "imbl-sized-chunks";
+          }
+          {
+            name = "proptest";
+            packageId = "proptest";
+            optional = true;
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+          {
+            name = "rand_xoshiro";
+            packageId = "rand_xoshiro";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "proptest";
+            packageId = "proptest";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "proptest" = [ "dep:proptest" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" ];
+          "refpool" = [ "dep:refpool" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "proptest" "serde" ];
+      };
+      "imbl-sized-chunks" = rec {
+        crateName = "imbl-sized-chunks";
+        version = "0.1.2";
+        edition = "2021";
+        sha256 = "0qzdw55na2w6fd44p7y9rh05nxa98gzpaigmwg57sy7db3xhch0l";
+        authors = [
+          "Bodil Stokke <bodil@bodil.org>"
+          "Joe Neeman <joeneeman@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitmaps";
+            packageId = "bitmaps";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "array-ops" = [ "dep:array-ops" ];
+          "default" = [ "std" ];
+          "refpool" = [ "dep:refpool" ];
+          "ringbuffer" = [ "array-ops" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "indexmap 1.9.3" = rec {
+        crateName = "indexmap";
+        version = "1.9.3";
+        edition = "2021";
+        sha256 = "16dxmy7yvk51wvnih3a3im6fp5lmx0wx76i03n06wyak6cwhw1xx";
+        dependencies = [
+          {
+            name = "hashbrown";
+            packageId = "hashbrown 0.12.3";
+            usesDefaultFeatures = false;
+            features = [ "raw" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-rayon" = [ "dep:rustc-rayon" ];
+          "serde" = [ "dep:serde" ];
+          "serde-1" = [ "serde" ];
+        };
+        resolvedDefaultFeatures = [ "serde" "serde-1" "std" ];
+      };
+      "indexmap 2.1.0" = rec {
+        crateName = "indexmap";
+        version = "2.1.0";
+        edition = "2021";
+        sha256 = "07rxrqmryr1xfnmhrjlz8ic6jw28v6h5cig3ws2c9d0wifhy2c6m";
+        dependencies = [
+          {
+            name = "equivalent";
+            packageId = "equivalent";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown 0.14.3";
+            usesDefaultFeatures = false;
+            features = [ "raw" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-rayon" = [ "dep:rustc-rayon" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "serde" "std" ];
+      };
+      "instant" = rec {
+        crateName = "instant";
+        version = "0.1.12";
+        edition = "2018";
+        sha256 = "0b2bx5qdlwayriidhrag8vhy10kdfimfhmb3jnjmsz2h9j1bwnvs";
+        authors = [
+          "sebcrozet <developer@crozet.re>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "js-sys" = [ "dep:js-sys" ];
+          "stdweb" = [ "dep:stdweb" ];
+          "wasm-bindgen" = [ "js-sys" "wasm-bindgen_rs" "web-sys" ];
+          "wasm-bindgen_rs" = [ "dep:wasm-bindgen_rs" ];
+          "web-sys" = [ "dep:web-sys" ];
+        };
+      };
+      "inventory" = rec {
+        crateName = "inventory";
+        version = "0.3.15";
+        edition = "2021";
+        sha256 = "0rspmi9qxz9hkajg4dx5hhwmcd3n3qw107hl3050hrs1izbd6n7r";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "ipnet" = rec {
+        crateName = "ipnet";
+        version = "2.9.0";
+        edition = "2018";
+        sha256 = "1hzrcysgwf0knf83ahb3535hrkw63mil88iqc6kjaryfblrqylcg";
+        authors = [
+          "Kris Price <kris@krisprice.nz>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "heapless" = [ "dep:heapless" ];
+          "json" = [ "serde" "schemars" ];
+          "schemars" = [ "dep:schemars" ];
+          "ser_as_str" = [ "heapless" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "is-terminal" = rec {
+        crateName = "is-terminal";
+        version = "0.4.10";
+        edition = "2018";
+        sha256 = "0m9la3f7cs77y85nkbcjsxkb7k861fc6bdhahyfidgh7gljh1b8b";
+        authors = [
+          "softprops <d.tangren@gmail.com>"
+          "Dan Gohman <dev@sunfishcode.online>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            target = { target, features }: (!((target."windows" or false) || ("hermit" == target."os" or null) || ("unknown" == target."os" or null)));
+            features = [ "termios" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Storage_FileSystem" "Win32_System_Console" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rustix";
+            packageId = "rustix";
+            target = { target, features }: (!((target."windows" or false) || ("hermit" == target."os" or null) || ("unknown" == target."os" or null)));
+            features = [ "stdio" ];
+          }
+        ];
+
+      };
+      "itertools 0.10.5" = rec {
+        crateName = "itertools";
+        version = "0.10.5";
+        edition = "2018";
+        sha256 = "0ww45h7nxx5kj6z2y6chlskxd1igvs4j507anr6dzg99x1h25zdh";
+        authors = [
+          "bluss"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "use_std" ];
+          "use_std" = [ "use_alloc" "either/use_std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "use_alloc" "use_std" ];
+      };
+      "itertools 0.11.0" = rec {
+        crateName = "itertools";
+        version = "0.11.0";
+        edition = "2018";
+        sha256 = "0mzyqcc59azx9g5cg6fs8k529gvh4463smmka6jvzs3cd2jp7hdi";
+        authors = [
+          "bluss"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "use_std" ];
+          "use_std" = [ "use_alloc" "either/use_std" ];
+        };
+        resolvedDefaultFeatures = [ "use_alloc" ];
+      };
+      "itertools 0.12.0" = rec {
+        crateName = "itertools";
+        version = "0.12.0";
+        edition = "2018";
+        sha256 = "1c07gzdlc6a1c8p8jrvvw3gs52bss3y58cs2s21d9i978l36pnr5";
+        authors = [
+          "bluss"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "use_std" ];
+          "use_std" = [ "use_alloc" "either/use_std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "use_alloc" "use_std" ];
+      };
+      "itoa" = rec {
+        crateName = "itoa";
+        version = "1.0.10";
+        edition = "2018";
+        sha256 = "0k7xjfki7mnv6yzjrbnbnjllg86acmbnk4izz2jmm1hx2wd6v95i";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "jobserver" = rec {
+        crateName = "jobserver";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "0z9w6vfqwbr6hfk9yaw7kydlh6f7k39xdlszxlh39in4acwzcdwc";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+
+      };
+      "js-sys" = rec {
+        crateName = "js-sys";
+        version = "0.3.67";
+        edition = "2018";
+        sha256 = "1lar78p13w781b4zf44a0sk26i461fczbdrhpan6kjav4gqkc7cs";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+        ];
+
+      };
+      "lazy_static" = rec {
+        crateName = "lazy_static";
+        version = "1.4.0";
+        edition = "2015";
+        sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2";
+        authors = [
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        features = {
+          "spin" = [ "dep:spin" ];
+          "spin_no_std" = [ "spin" ];
+        };
+      };
+      "lexical-core" = rec {
+        crateName = "lexical-core";
+        version = "0.8.5";
+        edition = "2018";
+        sha256 = "0ihf0x3vrk25fq3bv9q35m0xax0wmvwkh0j0pjm2yk4ddvh5vpic";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lexical-parse-float";
+            packageId = "lexical-parse-float";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "lexical-parse-integer";
+            packageId = "lexical-parse-integer";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "lexical-util";
+            packageId = "lexical-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "lexical-write-float";
+            packageId = "lexical-write-float";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "lexical-write-integer";
+            packageId = "lexical-write-integer";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "compact" = [ "lexical-write-integer/compact" "lexical-write-float/compact" "lexical-parse-integer/compact" "lexical-parse-float/compact" ];
+          "default" = [ "std" "write-integers" "write-floats" "parse-integers" "parse-floats" ];
+          "f128" = [ "lexical-util/f128" "lexical-parse-float/f128" "lexical-write-float/f128" ];
+          "f16" = [ "lexical-util/f16" "lexical-parse-float/f16" "lexical-write-float/f16" ];
+          "format" = [ "lexical-util/format" "lexical-parse-integer/format" "lexical-parse-float/format" "lexical-write-integer/format" "lexical-write-float/format" ];
+          "lexical-parse-float" = [ "dep:lexical-parse-float" ];
+          "lexical-parse-integer" = [ "dep:lexical-parse-integer" ];
+          "lexical-write-float" = [ "dep:lexical-write-float" ];
+          "lexical-write-integer" = [ "dep:lexical-write-integer" ];
+          "lint" = [ "lexical-util/lint" "lexical-write-integer/lint" "lexical-write-float/lint" "lexical-parse-integer/lint" "lexical-parse-float/lint" ];
+          "nightly" = [ "lexical-write-integer/nightly" "lexical-write-float/nightly" "lexical-parse-integer/nightly" "lexical-parse-float/nightly" ];
+          "parse-floats" = [ "lexical-parse-float" "parse" "floats" ];
+          "parse-integers" = [ "lexical-parse-integer" "parse" "integers" ];
+          "power-of-two" = [ "lexical-util/power-of-two" "lexical-write-integer/power-of-two" "lexical-write-float/power-of-two" "lexical-parse-integer/power-of-two" "lexical-parse-float/power-of-two" ];
+          "radix" = [ "lexical-util/radix" "lexical-write-integer/radix" "lexical-write-float/radix" "lexical-parse-integer/radix" "lexical-parse-float/radix" ];
+          "safe" = [ "lexical-write-integer/safe" "lexical-write-float/safe" "lexical-parse-integer/safe" "lexical-parse-float/safe" ];
+          "std" = [ "lexical-util/std" "lexical-write-integer/std" "lexical-write-float/std" "lexical-parse-integer/std" "lexical-parse-float/std" ];
+          "write-floats" = [ "lexical-write-float" "write" "floats" ];
+          "write-integers" = [ "lexical-write-integer" "write" "integers" ];
+        };
+        resolvedDefaultFeatures = [ "default" "floats" "format" "integers" "lexical-parse-float" "lexical-parse-integer" "lexical-write-float" "lexical-write-integer" "parse" "parse-floats" "parse-integers" "std" "write" "write-floats" "write-integers" ];
+      };
+      "lexical-parse-float" = rec {
+        crateName = "lexical-parse-float";
+        version = "0.8.5";
+        edition = "2018";
+        sha256 = "0py0gp8hlzcrlvjqmqlpl2v1as65iiqxq2xsabxvhc01pmg3lfv8";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lexical-parse-integer";
+            packageId = "lexical-parse-integer";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "lexical-util";
+            packageId = "lexical-util";
+            usesDefaultFeatures = false;
+            features = [ "parse-floats" ];
+          }
+          {
+            name = "static_assertions";
+            packageId = "static_assertions";
+          }
+        ];
+        features = {
+          "compact" = [ "lexical-util/compact" "lexical-parse-integer/compact" ];
+          "default" = [ "std" ];
+          "f128" = [ "lexical-util/f128" ];
+          "f16" = [ "lexical-util/f16" ];
+          "format" = [ "lexical-util/format" "lexical-parse-integer/format" ];
+          "lint" = [ "lexical-util/lint" "lexical-parse-integer/lint" ];
+          "nightly" = [ "lexical-parse-integer/nightly" ];
+          "power-of-two" = [ "lexical-util/power-of-two" "lexical-parse-integer/power-of-two" ];
+          "radix" = [ "lexical-util/radix" "lexical-parse-integer/radix" "power-of-two" ];
+          "safe" = [ "lexical-parse-integer/safe" ];
+          "std" = [ "lexical-util/std" "lexical-parse-integer/std" ];
+        };
+        resolvedDefaultFeatures = [ "format" "std" ];
+      };
+      "lexical-parse-integer" = rec {
+        crateName = "lexical-parse-integer";
+        version = "0.8.6";
+        edition = "2018";
+        sha256 = "1sayji3mpvb2xsjq56qcq3whfz8px9a6fxk5v7v15hyhbr4982bd";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lexical-util";
+            packageId = "lexical-util";
+            usesDefaultFeatures = false;
+            features = [ "parse-integers" ];
+          }
+          {
+            name = "static_assertions";
+            packageId = "static_assertions";
+          }
+        ];
+        features = {
+          "compact" = [ "lexical-util/compact" ];
+          "default" = [ "std" ];
+          "format" = [ "lexical-util/format" ];
+          "lint" = [ "lexical-util/lint" ];
+          "power-of-two" = [ "lexical-util/power-of-two" ];
+          "radix" = [ "lexical-util/radix" "power-of-two" ];
+          "std" = [ "lexical-util/std" ];
+        };
+        resolvedDefaultFeatures = [ "format" "std" ];
+      };
+      "lexical-util" = rec {
+        crateName = "lexical-util";
+        version = "0.8.5";
+        edition = "2018";
+        sha256 = "1z73qkv7yxhsbc4aiginn1dqmsj8jarkrdlyxc88g2gz2vzvjmaj";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "static_assertions";
+            packageId = "static_assertions";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "f128" = [ "floats" ];
+          "f16" = [ "floats" ];
+          "parse-floats" = [ "parse" "floats" ];
+          "parse-integers" = [ "parse" "integers" ];
+          "radix" = [ "power-of-two" ];
+          "write-floats" = [ "write" "floats" ];
+          "write-integers" = [ "write" "integers" ];
+        };
+        resolvedDefaultFeatures = [ "floats" "format" "integers" "parse" "parse-floats" "parse-integers" "std" "write" "write-floats" "write-integers" ];
+      };
+      "lexical-write-float" = rec {
+        crateName = "lexical-write-float";
+        version = "0.8.5";
+        edition = "2018";
+        sha256 = "0qk825l0csvnksh9sywb51996cjc2bylq6rxjaiha7sqqjhvmjmc";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lexical-util";
+            packageId = "lexical-util";
+            usesDefaultFeatures = false;
+            features = [ "write-floats" ];
+          }
+          {
+            name = "lexical-write-integer";
+            packageId = "lexical-write-integer";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "static_assertions";
+            packageId = "static_assertions";
+          }
+        ];
+        features = {
+          "compact" = [ "lexical-util/compact" "lexical-write-integer/compact" ];
+          "default" = [ "std" ];
+          "f128" = [ "lexical-util/f128" ];
+          "f16" = [ "lexical-util/f16" ];
+          "format" = [ "lexical-util/format" ];
+          "lint" = [ "lexical-util/lint" "lexical-write-integer/lint" ];
+          "nightly" = [ "lexical-write-integer/nightly" ];
+          "power-of-two" = [ "lexical-util/power-of-two" "lexical-write-integer/power-of-two" ];
+          "radix" = [ "lexical-util/radix" "lexical-write-integer/radix" "power-of-two" ];
+          "safe" = [ "lexical-write-integer/safe" ];
+          "std" = [ "lexical-util/std" "lexical-write-integer/std" ];
+        };
+        resolvedDefaultFeatures = [ "format" "std" ];
+      };
+      "lexical-write-integer" = rec {
+        crateName = "lexical-write-integer";
+        version = "0.8.5";
+        edition = "2018";
+        sha256 = "0ii4hmvqrg6pd4j9y1pkhkp0nw2wpivjzmljh6v6ca22yk8z7dp1";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lexical-util";
+            packageId = "lexical-util";
+            usesDefaultFeatures = false;
+            features = [ "write-integers" ];
+          }
+          {
+            name = "static_assertions";
+            packageId = "static_assertions";
+          }
+        ];
+        features = {
+          "compact" = [ "lexical-util/compact" ];
+          "default" = [ "std" ];
+          "format" = [ "lexical-util/format" ];
+          "lint" = [ "lexical-util/lint" ];
+          "power-of-two" = [ "lexical-util/power-of-two" ];
+          "radix" = [ "lexical-util/radix" "power-of-two" ];
+          "std" = [ "lexical-util/std" ];
+        };
+        resolvedDefaultFeatures = [ "format" "std" ];
+      };
+      "libc" = rec {
+        crateName = "libc";
+        version = "0.2.152";
+        edition = "2015";
+        sha256 = "1rsnma7hnw22w7jh9yqg43slddvfbnfzrvm3s7s4kinbj1jvzqqk";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ];
+          "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "extra_traits" "std" ];
+      };
+      "libm" = rec {
+        crateName = "libm";
+        version = "0.2.8";
+        edition = "2018";
+        sha256 = "0n4hk1rs8pzw8hdfmwn96c4568s93kfxqgcqswr7sajd2diaihjf";
+        authors = [
+          "Jorge Aparicio <jorge@japaric.io>"
+        ];
+        features = {
+          "musl-reference-tests" = [ "rand" ];
+          "rand" = [ "dep:rand" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "libredox" = rec {
+        crateName = "libredox";
+        version = "0.0.1";
+        edition = "2021";
+        sha256 = "1s2fh4ikpp9xl0lsl01pi0n8pw1q9s3ld452vd8qh1v63v537j45";
+        authors = [
+          "4lDO2 <4lDO2@protonmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.4.1";
+          }
+        ];
+        features = {
+          "default" = [ "scheme" "call" ];
+          "scheme" = [ "call" ];
+        };
+        resolvedDefaultFeatures = [ "call" ];
+      };
+      "linux-raw-sys" = rec {
+        crateName = "linux-raw-sys";
+        version = "0.4.13";
+        edition = "2021";
+        sha256 = "172k2c6422gsc914ig8rh99mb9yc7siw6ikc3d9xw1k7vx0s3k81";
+        authors = [
+          "Dan Gohman <dev@sunfishcode.online>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" "general" "errno" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "no_std" ];
+        };
+        resolvedDefaultFeatures = [ "elf" "errno" "general" "if_ether" "ioctl" "net" "netlink" "no_std" "prctl" "std" "xdp" ];
+      };
+      "litrs" = rec {
+        crateName = "litrs";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "19cssch9gc0x2snd9089nvwzz79zx6nzsi3icffpx25p4hck1kml";
+        authors = [
+          "Lukas Kalbertodt <lukas.kalbertodt@gmail.com>"
+        ];
+        features = {
+          "check_suffix" = [ "unicode-xid" ];
+          "default" = [ "proc-macro2" ];
+          "proc-macro2" = [ "dep:proc-macro2" ];
+          "unicode-xid" = [ "dep:unicode-xid" ];
+        };
+      };
+      "lock_api" = rec {
+        crateName = "lock_api";
+        version = "0.4.11";
+        edition = "2018";
+        sha256 = "0iggx0h4jx63xm35861106af3jkxq06fpqhpkhgw0axi2n38y5iw";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "atomic_usize" ];
+          "owning_ref" = [ "dep:owning_ref" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "atomic_usize" "default" ];
+      };
+      "log" = rec {
+        crateName = "log";
+        version = "0.4.20";
+        edition = "2015";
+        sha256 = "13rf7wphnwd61vazpxr7fiycin6cb1g8fmvgqg18i464p0y1drmm";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "kv_unstable" = [ "value-bag" ];
+          "kv_unstable_serde" = [ "kv_unstable_std" "value-bag/serde" "serde" ];
+          "kv_unstable_std" = [ "std" "kv_unstable" "value-bag/error" ];
+          "kv_unstable_sval" = [ "kv_unstable" "value-bag/sval" "sval" "sval_ref" ];
+          "serde" = [ "dep:serde" ];
+          "sval" = [ "dep:sval" ];
+          "sval_ref" = [ "dep:sval_ref" ];
+          "value-bag" = [ "dep:value-bag" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "lzma-sys" = rec {
+        crateName = "lzma-sys";
+        version = "0.1.20";
+        edition = "2018";
+        links = "lzma";
+        sha256 = "09sxp20waxyglgn3cjz8qjkspb3ryz2fwx4rigkwvrk46ymh9njz";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+        ];
+        features = { };
+      };
+      "magic" = rec {
+        crateName = "magic";
+        version = "0.16.2";
+        edition = "2018";
+        sha256 = "0g9py31aw19j5sr5lznb068byhgbiynflvizjrxcwgccvw1sw052";
+        authors = [
+          "Daniel Micay <danielmicay@gmail.com>"
+          "Petar Radoőević <petar@wunki.org>"
+          "lilydjwg <lilydjwg@gmail.com>"
+          "Jeff Belgum <belgum@bastille.io>"
+          "Onur Aslan <onur@onur.im>"
+          "robo9k <robo9k@symlink.io>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "magic-sys";
+            packageId = "magic-sys";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+
+      };
+      "magic-sys" = rec {
+        crateName = "magic-sys";
+        version = "0.3.0";
+        edition = "2015";
+        links = "magic";
+        sha256 = "1g5k9d9igxv4h23nbhp8bqa5gdpkd3ahgm0rh5i0s54mi3h6my7g";
+        authors = [
+          "robo9k <robo9k@symlink.io>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "vcpkg";
+            packageId = "vcpkg";
+          }
+        ];
+        features = {
+          "default" = [ "v5-38" ];
+          "v5-05" = [ "v5-04" ];
+          "v5-10" = [ "v5-05" ];
+          "v5-13" = [ "v5-10" ];
+          "v5-20" = [ "v5-13" ];
+          "v5-21" = [ "v5-20" ];
+          "v5-22" = [ "v5-21" ];
+          "v5-23" = [ "v5-22" ];
+          "v5-25" = [ "v5-23" ];
+          "v5-27" = [ "v5-25" ];
+          "v5-32" = [ "v5-27" ];
+          "v5-35" = [ "v5-32" ];
+          "v5-38" = [ "v5-35" ];
+          "v5-40" = [ "v5-38" ];
+        };
+        resolvedDefaultFeatures = [ "default" "v5-04" "v5-05" "v5-10" "v5-13" "v5-20" "v5-21" "v5-22" "v5-23" "v5-25" "v5-27" "v5-32" "v5-35" "v5-38" ];
+      };
+      "matchers" = rec {
+        crateName = "matchers";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "0n2mbk7lg2vf962c8xwzdq96yrc9i0p8dbmm4wa1nnkcp1dhfqw2";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+        ];
+        dependencies = [
+          {
+            name = "regex-automata";
+            packageId = "regex-automata 0.1.10";
+          }
+        ];
+
+      };
+      "matchit" = rec {
+        crateName = "matchit";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "156bgdmmlv4crib31qhgg49nsjk88dxkdqp80ha2pk2rk6n6ax0f";
+        authors = [
+          "Ibraheem Ahmed <ibraheem@ibraheem.ca>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "md-5" = rec {
+        crateName = "md-5";
+        version = "0.10.6";
+        edition = "2018";
+        sha256 = "1kvq5rnpm4fzwmyv5nmnxygdhhb2369888a06gdc9pxyrzh7x7nq";
+        libName = "md5";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "md5-asm" ];
+          "default" = [ "std" ];
+          "md5-asm" = [ "dep:md5-asm" ];
+          "oid" = [ "digest/oid" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "memchr" = rec {
+        crateName = "memchr";
+        version = "2.7.1";
+        edition = "2021";
+        sha256 = "0jf1kicqa4vs9lyzj4v4y1p90q0dh87hvhsdd5xvhnp527sw8gaj";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+          "bluss"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "logging" = [ "dep:log" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "std" = [ "alloc" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "memoffset 0.6.5" = rec {
+        crateName = "memoffset";
+        version = "0.6.5";
+        edition = "2015";
+        sha256 = "1kkrzll58a3ayn5zdyy9i1f1v3mx0xgl29x0chq614zazba638ss";
+        authors = [
+          "Gilad Naaman <gilad.naaman@gmail.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "memoffset 0.9.0" = rec {
+        crateName = "memoffset";
+        version = "0.9.0";
+        edition = "2015";
+        sha256 = "0v20ihhdzkfw1jx00a7zjpk2dcp5qjq6lz302nyqamd9c4f4nqss";
+        authors = [
+          "Gilad Naaman <gilad.naaman@gmail.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "mime" = rec {
+        crateName = "mime";
+        version = "0.3.17";
+        edition = "2015";
+        sha256 = "16hkibgvb9klh0w0jk5crr5xv90l3wlf77ggymzjmvl1818vnxv8";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+
+      };
+      "minimal-lexical" = rec {
+        crateName = "minimal-lexical";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "miniz_oxide" = rec {
+        crateName = "miniz_oxide";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1ivl3rbbdm53bzscrd01g60l46lz5krl270487d8lhjvwl5hx0g7";
+        authors = [
+          "Frommi <daniil.liferenko@gmail.com>"
+          "oyvindln <oyvindln@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "adler";
+            packageId = "adler";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "with-alloc" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "adler/rustc-dep-of-std" ];
+          "simd" = [ "simd-adler32" ];
+          "simd-adler32" = [ "dep:simd-adler32" ];
+        };
+        resolvedDefaultFeatures = [ "with-alloc" ];
+      };
+      "mio" = rec {
+        crateName = "mio";
+        version = "0.8.10";
+        edition = "2018";
+        sha256 = "02gyaxvaia9zzi4drrw59k9s0j6pa5d1y2kv7iplwjipdqlhngcg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_Storage_FileSystem" "Win32_System_IO" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = {
+          "default" = [ "log" ];
+          "log" = [ "dep:log" ];
+          "os-ext" = [ "os-poll" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_Security" ];
+        };
+        resolvedDefaultFeatures = [ "default" "log" "net" "os-ext" "os-poll" ];
+      };
+      "multimap" = rec {
+        crateName = "multimap";
+        version = "0.8.3";
+        edition = "2015";
+        sha256 = "0sicyz4n500vdhgcxn4g8jz97cp1ijir1rnbgph3pmx9ckz4dkp5";
+        authors = [
+          "HΓ₯var NΓΈvik <havar.novik@gmail.com>"
+        ];
+        features = {
+          "default" = [ "serde_impl" ];
+          "serde" = [ "dep:serde" ];
+          "serde_impl" = [ "serde" ];
+        };
+      };
+      "nibble_vec" = rec {
+        crateName = "nibble_vec";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "0hsdp3s724s30hkqz74ky6sqnadhp2xwcj1n1hzy4vzkz4yxi9bp";
+        authors = [
+          "Michael Sproul <micsproul@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+          }
+        ];
+
+      };
+      "nix 0.24.3" = rec {
+        crateName = "nix";
+        version = "0.24.3";
+        edition = "2018";
+        sha256 = "0sc0yzdl51b49bqd9l9cmimp1sw1hxb8iyv4d35ww6d7m5rfjlps";
+        authors = [
+          "The nix-rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            features = [ "extra_traits" ];
+          }
+          {
+            name = "memoffset";
+            packageId = "memoffset 0.6.5";
+            optional = true;
+            target = { target, features }: (!("redox" == target."os" or null));
+          }
+        ];
+        features = {
+          "default" = [ "acct" "aio" "dir" "env" "event" "feature" "fs" "hostname" "inotify" "ioctl" "kmod" "mman" "mount" "mqueue" "net" "personality" "poll" "process" "pthread" "ptrace" "quota" "reboot" "resource" "sched" "signal" "socket" "term" "time" "ucontext" "uio" "user" "zerocopy" ];
+          "dir" = [ "fs" ];
+          "memoffset" = [ "dep:memoffset" ];
+          "mount" = [ "uio" ];
+          "mqueue" = [ "fs" ];
+          "net" = [ "socket" ];
+          "ptrace" = [ "process" ];
+          "sched" = [ "process" ];
+          "signal" = [ "process" ];
+          "socket" = [ "memoffset" ];
+          "ucontext" = [ "signal" ];
+          "user" = [ "feature" ];
+          "zerocopy" = [ "fs" "uio" ];
+        };
+        resolvedDefaultFeatures = [ "acct" "aio" "default" "dir" "env" "event" "feature" "fs" "hostname" "inotify" "ioctl" "kmod" "memoffset" "mman" "mount" "mqueue" "net" "personality" "poll" "process" "pthread" "ptrace" "quota" "reboot" "resource" "sched" "signal" "socket" "term" "time" "ucontext" "uio" "user" "zerocopy" ];
+      };
+      "nix 0.25.1" = rec {
+        crateName = "nix";
+        version = "0.25.1";
+        edition = "2018";
+        sha256 = "1r4vyp5g1lxzpig31bkrhxdf2bggb4nvk405x5gngzfvwxqgyipk";
+        authors = [
+          "The nix-rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            features = [ "extra_traits" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "aio" = [ "pin-utils" ];
+          "default" = [ "acct" "aio" "dir" "env" "event" "feature" "fs" "hostname" "inotify" "ioctl" "kmod" "mman" "mount" "mqueue" "net" "personality" "poll" "process" "pthread" "ptrace" "quota" "reboot" "resource" "sched" "signal" "socket" "term" "time" "ucontext" "uio" "user" "zerocopy" ];
+          "dir" = [ "fs" ];
+          "memoffset" = [ "dep:memoffset" ];
+          "mount" = [ "uio" ];
+          "mqueue" = [ "fs" ];
+          "net" = [ "socket" ];
+          "pin-utils" = [ "dep:pin-utils" ];
+          "ptrace" = [ "process" ];
+          "sched" = [ "process" ];
+          "signal" = [ "process" ];
+          "socket" = [ "memoffset" ];
+          "ucontext" = [ "signal" ];
+          "user" = [ "feature" ];
+          "zerocopy" = [ "fs" "uio" ];
+        };
+        resolvedDefaultFeatures = [ "fs" "ioctl" "poll" "process" "signal" "term" ];
+      };
+      "nix 0.26.4" = rec {
+        crateName = "nix";
+        version = "0.26.4";
+        edition = "2018";
+        sha256 = "06xgl4ybb8pvjrbmc3xggbgk3kbs1j0c4c0nzdfrmpbgrkrym2sr";
+        authors = [
+          "The nix-rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            features = [ "extra_traits" ];
+          }
+        ];
+        features = {
+          "aio" = [ "pin-utils" ];
+          "default" = [ "acct" "aio" "dir" "env" "event" "feature" "fs" "hostname" "inotify" "ioctl" "kmod" "mman" "mount" "mqueue" "net" "personality" "poll" "process" "pthread" "ptrace" "quota" "reboot" "resource" "sched" "signal" "socket" "term" "time" "ucontext" "uio" "user" "zerocopy" ];
+          "dir" = [ "fs" ];
+          "memoffset" = [ "dep:memoffset" ];
+          "mount" = [ "uio" ];
+          "mqueue" = [ "fs" ];
+          "net" = [ "socket" ];
+          "pin-utils" = [ "dep:pin-utils" ];
+          "ptrace" = [ "process" ];
+          "sched" = [ "process" ];
+          "signal" = [ "process" ];
+          "socket" = [ "memoffset" ];
+          "ucontext" = [ "signal" ];
+          "user" = [ "feature" ];
+          "zerocopy" = [ "fs" "uio" ];
+        };
+        resolvedDefaultFeatures = [ "feature" "fs" "user" ];
+      };
+      "nix 0.27.1" = rec {
+        crateName = "nix";
+        version = "0.27.1";
+        edition = "2021";
+        sha256 = "0ly0kkmij5f0sqz35lx9czlbk6zpihb7yh1bsy4irzwfd2f4xc1f";
+        authors = [
+          "The nix-rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            features = [ "extra_traits" ];
+          }
+        ];
+        features = {
+          "aio" = [ "pin-utils" ];
+          "dir" = [ "fs" ];
+          "memoffset" = [ "dep:memoffset" ];
+          "mount" = [ "uio" ];
+          "mqueue" = [ "fs" ];
+          "net" = [ "socket" ];
+          "pin-utils" = [ "dep:pin-utils" ];
+          "ptrace" = [ "process" ];
+          "sched" = [ "process" ];
+          "signal" = [ "process" ];
+          "socket" = [ "memoffset" ];
+          "ucontext" = [ "signal" ];
+          "user" = [ "feature" ];
+          "zerocopy" = [ "fs" "uio" ];
+        };
+        resolvedDefaultFeatures = [ "default" "fs" ];
+      };
+      "nix-compat" = rec {
+        crateName = "nix-compat";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "drvfmt";
+            path = "src/bin/drvfmt.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./nix-compat; }
+          else ./nix-compat;
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+            features = [ "alloc" "unicode" "serde" ];
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+          }
+          {
+            name = "ed25519-dalek";
+            packageId = "ed25519-dalek";
+          }
+          {
+            name = "enum-primitive-derive";
+            packageId = "enum-primitive-derive";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "nom";
+            packageId = "nom";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "io-util" "macros" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "criterion";
+            packageId = "criterion";
+            features = [ "html_reports" ];
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            usesDefaultFeatures = false;
+            features = [ "executor" ];
+          }
+          {
+            name = "hex-literal";
+            packageId = "hex-literal";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "pretty_assertions";
+            packageId = "pretty_assertions";
+          }
+          {
+            name = "rstest";
+            packageId = "rstest";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tokio-test";
+            packageId = "tokio-test";
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+          }
+        ];
+        features = {
+          "async" = [ "tokio" ];
+          "default" = [ "async" "wire" ];
+          "pin-project-lite" = [ "dep:pin-project-lite" ];
+          "tokio" = [ "dep:tokio" ];
+          "wire" = [ "tokio" "pin-project-lite" ];
+        };
+        resolvedDefaultFeatures = [ "async" "default" "pin-project-lite" "tokio" "wire" ];
+      };
+      "nom" = rec {
+        crateName = "nom";
+        version = "7.1.3";
+        edition = "2018";
+        sha256 = "0jha9901wxam390jcf5pfa0qqfrgh8li787jx2ip0yk5b8y9hwyj";
+        authors = [
+          "contact@geoffroycouprie.com"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "minimal-lexical";
+            packageId = "minimal-lexical";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" "memchr/std" "minimal-lexical/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "nom8" = rec {
+        crateName = "nom8";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "1y6jzabxyrl05vxnh63r66ac2fh0symg5fnynxm4ii3zkif580df";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" "memchr/std" ];
+          "unstable-doc" = [ "alloc" "std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "nu-ansi-term" = rec {
+        crateName = "nu-ansi-term";
+        version = "0.46.0";
+        edition = "2018";
+        sha256 = "115sywxh53p190lyw97alm14nc004qj5jm5lvdj608z84rbida3p";
+        authors = [
+          "ogham@bsago.me"
+          "Ryan Scheel (Havvy) <ryan.havvy@gmail.com>"
+          "Josh Triplett <josh@joshtriplett.org>"
+          "The Nushell Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "overload";
+            packageId = "overload";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: ("windows" == target."os" or null);
+            features = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "processenv" ];
+          }
+        ];
+        features = {
+          "derive_serde_style" = [ "serde" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "num-conv" = rec {
+        crateName = "num-conv";
+        version = "0.1.0";
+        edition = "2021";
+        sha256 = "1ndiyg82q73783jq18isi71a7mjh56wxrk52rlvyx0mi5z9ibmai";
+        authors = [
+          "Jacob Pratt <jacob@jhpratt.dev>"
+        ];
+
+      };
+      "num-traits" = rec {
+        crateName = "num-traits";
+        version = "0.2.18";
+        edition = "2018";
+        sha256 = "0yjib8p2p9kzmaz48xwhs69w5dh1wipph9jgnillzd2x33jz03fs";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "libm";
+            packageId = "libm";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "libm" = [ "dep:libm" ];
+        };
+        resolvedDefaultFeatures = [ "default" "libm" "std" ];
+      };
+      "num_cpus" = rec {
+        crateName = "num_cpus";
+        version = "1.16.0";
+        edition = "2015";
+        sha256 = "0hra6ihpnh06dvfvz9ipscys0xfqa9ca9hzp384d5m02ssvgqqa1";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(target."windows" or false));
+          }
+        ];
+
+      };
+      "object" = rec {
+        crateName = "object";
+        version = "0.32.2";
+        edition = "2018";
+        sha256 = "0hc4cjwyngiy6k51hlzrlsxgv5z25vv7c2cp0ky1lckfic0259m6";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "all" = [ "read" "write" "std" "compression" "wasm" ];
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "compression" = [ "dep:flate2" "dep:ruzstd" "std" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "read" "compression" ];
+          "doc" = [ "read_core" "write_std" "std" "compression" "archive" "coff" "elf" "macho" "pe" "wasm" "xcoff" ];
+          "pe" = [ "coff" ];
+          "read" = [ "read_core" "archive" "coff" "elf" "macho" "pe" "xcoff" "unaligned" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "alloc" "memchr/rustc-dep-of-std" ];
+          "std" = [ "memchr/std" ];
+          "unstable-all" = [ "all" "unstable" ];
+          "wasm" = [ "dep:wasmparser" ];
+          "write" = [ "write_std" "coff" "elf" "macho" "pe" "xcoff" ];
+          "write_core" = [ "dep:crc32fast" "dep:indexmap" "dep:hashbrown" ];
+          "write_std" = [ "write_core" "std" "indexmap?/std" "crc32fast?/std" ];
+        };
+        resolvedDefaultFeatures = [ "archive" "coff" "elf" "macho" "pe" "read_core" "unaligned" ];
+      };
+      "object_store" = rec {
+        crateName = "object_store";
+        version = "0.9.1";
+        edition = "2021";
+        sha256 = "1cwx0xg57cp3z6xjgrqwp0gxgxsagls4h5cd212pmxpxcn5qywdq";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "base64";
+            packageId = "base64";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "clock" ];
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "humantime";
+            packageId = "humantime";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "itertools";
+            packageId = "itertools 0.12.0";
+          }
+          {
+            name = "md-5";
+            packageId = "md-5";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot 0.12.1";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "quick-xml";
+            packageId = "quick-xml";
+            optional = true;
+            features = [ "serialize" "overlapped-lists" ];
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" "std_rng" ];
+          }
+          {
+            name = "reqwest";
+            packageId = "reqwest";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "rustls-tls-native-roots" ];
+          }
+          {
+            name = "ring";
+            packageId = "ring";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "rustls-pemfile";
+            packageId = "rustls-pemfile 2.1.0";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "snafu";
+            packageId = "snafu";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" "macros" "rt" "time" "io-util" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "url";
+            packageId = "url";
+          }
+          {
+            name = "walkdir";
+            packageId = "walkdir";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            features = [ "server" ];
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+          }
+        ];
+        features = {
+          "aws" = [ "cloud" "md-5" ];
+          "azure" = [ "cloud" ];
+          "base64" = [ "dep:base64" ];
+          "cloud" = [ "serde" "serde_json" "quick-xml" "hyper" "reqwest" "reqwest/json" "reqwest/stream" "chrono/serde" "base64" "rand" "ring" ];
+          "gcp" = [ "cloud" "rustls-pemfile" ];
+          "http" = [ "cloud" ];
+          "hyper" = [ "dep:hyper" ];
+          "md-5" = [ "dep:md-5" ];
+          "quick-xml" = [ "dep:quick-xml" ];
+          "rand" = [ "dep:rand" ];
+          "reqwest" = [ "dep:reqwest" ];
+          "ring" = [ "dep:ring" ];
+          "rustls-pemfile" = [ "dep:rustls-pemfile" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "tls-webpki-roots" = [ "reqwest?/rustls-tls-webpki-roots" ];
+        };
+        resolvedDefaultFeatures = [ "aws" "azure" "base64" "cloud" "gcp" "http" "hyper" "md-5" "quick-xml" "rand" "reqwest" "ring" "rustls-pemfile" "serde" "serde_json" ];
+      };
+      "once_cell" = rec {
+        crateName = "once_cell";
+        version = "1.19.0";
+        edition = "2021";
+        sha256 = "14kvw7px5z96dk4dwdm1r9cqhhy2cyj1l5n5b29mynbb8yr15nrz";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        features = {
+          "alloc" = [ "race" ];
+          "atomic-polyfill" = [ "critical-section" ];
+          "critical-section" = [ "dep:critical-section" "portable-atomic" ];
+          "default" = [ "std" ];
+          "parking_lot" = [ "dep:parking_lot_core" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "race" "std" ];
+      };
+      "oorandom" = rec {
+        crateName = "oorandom";
+        version = "11.1.3";
+        edition = "2018";
+        sha256 = "0xdm4vd89aiwnrk1xjwzklnchjqvib4klcihlc2bsd4x50mbrc8a";
+        authors = [
+          "Simon Heath <icefox@dreamquest.io>"
+        ];
+
+      };
+      "openssl-probe" = rec {
+        crateName = "openssl-probe";
+        version = "0.1.5";
+        edition = "2015";
+        sha256 = "1kq18qm48rvkwgcggfkqq6pm948190czqc94d6bm2sir5hq1l0gz";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+
+      };
+      "opentelemetry" = rec {
+        crateName = "opentelemetry";
+        version = "0.21.0";
+        edition = "2021";
+        sha256 = "12jfmyx8k9q2sjlx4wp76ddzaf94i7lnkliv1c9mj164bnd36chy";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap 2.1.0";
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!("wasi" == target."os" or null)));
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "urlencoding";
+            packageId = "urlencoding";
+          }
+        ];
+        features = {
+          "default" = [ "trace" ];
+          "logs_level_enabled" = [ "logs" ];
+          "pin-project-lite" = [ "dep:pin-project-lite" ];
+          "testing" = [ "trace" "metrics" ];
+          "trace" = [ "pin-project-lite" ];
+        };
+        resolvedDefaultFeatures = [ "default" "metrics" "pin-project-lite" "trace" ];
+      };
+      "opentelemetry-otlp" = rec {
+        crateName = "opentelemetry-otlp";
+        version = "0.14.0";
+        edition = "2021";
+        sha256 = "0c59bh4wa824mf89ayivsjqwipkg1y6r27r4d0y47lhfna1xlk7j";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+            optional = true;
+          }
+          {
+            name = "opentelemetry";
+            packageId = "opentelemetry";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "opentelemetry-proto";
+            packageId = "opentelemetry-proto";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "opentelemetry-semantic-conventions";
+            packageId = "opentelemetry-semantic-conventions";
+          }
+          {
+            name = "opentelemetry_sdk";
+            packageId = "opentelemetry_sdk";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.11.9";
+            optional = true;
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "sync" "rt" ];
+          }
+          {
+            name = "tonic";
+            packageId = "tonic 0.9.2";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "rt-multi-thread" ];
+          }
+        ];
+        features = {
+          "default" = [ "grpc-tonic" "trace" ];
+          "grpc-sys" = [ "grpcio" "opentelemetry-proto/gen-grpcio" ];
+          "grpc-tonic" = [ "tonic" "prost" "http" "tokio" "opentelemetry-proto/gen-tonic" ];
+          "grpcio" = [ "dep:grpcio" ];
+          "gzip-tonic" = [ "tonic/gzip" ];
+          "http" = [ "dep:http" ];
+          "http-proto" = [ "prost" "opentelemetry-http" "opentelemetry-proto/gen-tonic-messages" "http" "trace" "metrics" ];
+          "integration-testing" = [ "tonic" "prost" "tokio/full" "trace" ];
+          "logs" = [ "opentelemetry/logs" "opentelemetry_sdk/logs" "opentelemetry-proto/logs" ];
+          "metrics" = [ "opentelemetry/metrics" "opentelemetry_sdk/metrics" "opentelemetry-proto/metrics" ];
+          "openssl" = [ "grpcio/openssl" ];
+          "openssl-vendored" = [ "grpcio/openssl-vendored" ];
+          "opentelemetry-http" = [ "dep:opentelemetry-http" ];
+          "prost" = [ "dep:prost" ];
+          "reqwest" = [ "dep:reqwest" ];
+          "reqwest-blocking-client" = [ "reqwest/blocking" "opentelemetry-http/reqwest" ];
+          "reqwest-client" = [ "reqwest" "opentelemetry-http/reqwest" ];
+          "reqwest-rustls" = [ "reqwest" "reqwest/rustls-tls-native-roots" ];
+          "serde" = [ "dep:serde" ];
+          "serialize" = [ "serde" ];
+          "surf" = [ "dep:surf" ];
+          "surf-client" = [ "surf" "opentelemetry-http/surf" ];
+          "tls" = [ "tonic/tls" ];
+          "tls-roots" = [ "tls" "tonic/tls-roots" ];
+          "tokio" = [ "dep:tokio" ];
+          "tonic" = [ "dep:tonic" ];
+          "trace" = [ "opentelemetry/trace" "opentelemetry_sdk/trace" "opentelemetry-proto/trace" ];
+        };
+        resolvedDefaultFeatures = [ "default" "grpc-tonic" "http" "prost" "tokio" "tonic" "trace" ];
+      };
+      "opentelemetry-proto" = rec {
+        crateName = "opentelemetry-proto";
+        version = "0.4.0";
+        edition = "2021";
+        sha256 = "1qblsq0hkksdw3k60bc8yi5xwlynmqwibggz3lyyl4n8bk75bqd2";
+        dependencies = [
+          {
+            name = "opentelemetry";
+            packageId = "opentelemetry";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "opentelemetry_sdk";
+            packageId = "opentelemetry_sdk";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.11.9";
+            optional = true;
+          }
+          {
+            name = "tonic";
+            packageId = "tonic 0.9.2";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "codegen" "prost" ];
+          }
+        ];
+        features = {
+          "full" = [ "gen-tonic" "gen-grpcio" "trace" "logs" "metrics" "zpages" "with-serde" ];
+          "gen-grpcio" = [ "grpcio" "prost" ];
+          "gen-tonic" = [ "gen-tonic-messages" "tonic/transport" ];
+          "gen-tonic-messages" = [ "tonic" "prost" ];
+          "grpcio" = [ "dep:grpcio" ];
+          "logs" = [ "opentelemetry/logs" "opentelemetry_sdk/logs" ];
+          "metrics" = [ "opentelemetry/metrics" "opentelemetry_sdk/metrics" ];
+          "prost" = [ "dep:prost" ];
+          "serde" = [ "dep:serde" ];
+          "tonic" = [ "dep:tonic" ];
+          "trace" = [ "opentelemetry/trace" "opentelemetry_sdk/trace" ];
+          "with-serde" = [ "serde" ];
+          "zpages" = [ "trace" ];
+        };
+        resolvedDefaultFeatures = [ "gen-tonic" "gen-tonic-messages" "prost" "tonic" "trace" ];
+      };
+      "opentelemetry-semantic-conventions" = rec {
+        crateName = "opentelemetry-semantic-conventions";
+        version = "0.13.0";
+        edition = "2021";
+        sha256 = "115wbgk840dklyhpg3lwp4x1m643qd7f0vkz8hmfz0pry4g4yxzm";
+        dependencies = [
+          {
+            name = "opentelemetry";
+            packageId = "opentelemetry";
+            usesDefaultFeatures = false;
+          }
+        ];
+
+      };
+      "opentelemetry_sdk" = rec {
+        crateName = "opentelemetry_sdk";
+        version = "0.21.2";
+        edition = "2021";
+        sha256 = "1r7gw2j2n800rd0vdnga32yhlfmc3c4y0sadcr97licam74aw5ig";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+            optional = true;
+          }
+          {
+            name = "crossbeam-channel";
+            packageId = "crossbeam-channel";
+            optional = true;
+          }
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+          }
+          {
+            name = "futures-executor";
+            packageId = "futures-executor";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "std" "sink" "async-await-macro" ];
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+            optional = true;
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "opentelemetry";
+            packageId = "opentelemetry";
+          }
+          {
+            name = "ordered-float";
+            packageId = "ordered-float";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+            optional = true;
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" "std_rng" ];
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "rt" "time" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+            optional = true;
+          }
+        ];
+        features = {
+          "async-std" = [ "dep:async-std" ];
+          "async-trait" = [ "dep:async-trait" ];
+          "crossbeam-channel" = [ "dep:crossbeam-channel" ];
+          "default" = [ "trace" ];
+          "glob" = [ "dep:glob" ];
+          "http" = [ "dep:http" ];
+          "jaeger_remote_sampler" = [ "trace" "opentelemetry-http" "http" "serde" "serde_json" "url" ];
+          "logs" = [ "opentelemetry/logs" "crossbeam-channel" "async-trait" "serde_json" ];
+          "logs_level_enabled" = [ "logs" "opentelemetry/logs_level_enabled" ];
+          "metrics" = [ "opentelemetry/metrics" "glob" "async-trait" ];
+          "opentelemetry-http" = [ "dep:opentelemetry-http" ];
+          "percent-encoding" = [ "dep:percent-encoding" ];
+          "rand" = [ "dep:rand" ];
+          "rt-async-std" = [ "async-std" ];
+          "rt-tokio" = [ "tokio" "tokio-stream" ];
+          "rt-tokio-current-thread" = [ "tokio" "tokio-stream" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "testing" = [ "opentelemetry/testing" "trace" "metrics" "logs" "rt-async-std" "rt-tokio" "rt-tokio-current-thread" "tokio/macros" "tokio/rt-multi-thread" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-stream" = [ "dep:tokio-stream" ];
+          "trace" = [ "opentelemetry/trace" "crossbeam-channel" "rand" "async-trait" "percent-encoding" ];
+          "url" = [ "dep:url" ];
+        };
+        resolvedDefaultFeatures = [ "async-trait" "crossbeam-channel" "default" "glob" "metrics" "percent-encoding" "rand" "rt-tokio" "tokio" "tokio-stream" "trace" ];
+      };
+      "ordered-float" = rec {
+        crateName = "ordered-float";
+        version = "4.2.0";
+        edition = "2021";
+        sha256 = "0kjqcvvbcsibbx3hnj7ag06bd9gv2zfi5ja6rgyh2kbxbh3zfvd7";
+        authors = [
+          "Jonathan Reem <jonathan.reem@gmail.com>"
+          "Matt Brubeck <mbrubeck@limpet.net>"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "borsh" = [ "dep:borsh" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "default" = [ "std" ];
+          "proptest" = [ "dep:proptest" ];
+          "rand" = [ "dep:rand" ];
+          "randtest" = [ "rand/std" "rand/std_rng" ];
+          "rkyv" = [ "rkyv_32" ];
+          "rkyv_16" = [ "dep:rkyv" "rkyv?/size_16" ];
+          "rkyv_32" = [ "dep:rkyv" "rkyv?/size_32" ];
+          "rkyv_64" = [ "dep:rkyv" "rkyv?/size_64" ];
+          "rkyv_ck" = [ "rkyv?/validation" ];
+          "schemars" = [ "dep:schemars" ];
+          "serde" = [ "dep:serde" "rand?/serde1" ];
+          "speedy" = [ "dep:speedy" ];
+          "std" = [ "num-traits/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "os_str_bytes" = rec {
+        crateName = "os_str_bytes";
+        version = "6.6.1";
+        edition = "2021";
+        sha256 = "1885z1x4sm86v5p41ggrl49m58rbzzhd1kj72x46yy53p62msdg2";
+        authors = [
+          "dylni"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+          }
+        ];
+        features = {
+          "checked_conversions" = [ "conversions" ];
+          "default" = [ "memchr" "raw_os_str" ];
+          "memchr" = [ "dep:memchr" ];
+          "print_bytes" = [ "dep:print_bytes" ];
+          "uniquote" = [ "dep:uniquote" ];
+        };
+        resolvedDefaultFeatures = [ "conversions" "default" "memchr" "raw_os_str" ];
+      };
+      "overload" = rec {
+        crateName = "overload";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "0fdgbaqwknillagy1xq7xfgv60qdbk010diwl7s1p0qx7hb16n5i";
+        authors = [
+          "Daniel Salvadori <danaugrs@gmail.com>"
+        ];
+
+      };
+      "parking" = rec {
+        crateName = "parking";
+        version = "2.2.0";
+        edition = "2018";
+        sha256 = "1blwbkq6im1hfxp5wlbr475mw98rsyc0bbr2d5n16m38z253p0dv";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+          "The Rust Project Developers"
+        ];
+        features = {
+          "loom" = [ "dep:loom" ];
+        };
+      };
+      "parking_lot 0.11.2" = rec {
+        crateName = "parking_lot";
+        version = "0.11.2";
+        edition = "2018";
+        sha256 = "16gzf41bxmm10x82bla8d6wfppy9ym3fxsmdjyvn61m66s0bf5vx";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "instant";
+            packageId = "instant";
+          }
+          {
+            name = "lock_api";
+            packageId = "lock_api";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core 0.8.6";
+          }
+        ];
+        features = {
+          "arc_lock" = [ "lock_api/arc_lock" ];
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+          "stdweb" = [ "instant/stdweb" ];
+          "wasm-bindgen" = [ "instant/wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot 0.12.1" = rec {
+        crateName = "parking_lot";
+        version = "0.12.1";
+        edition = "2018";
+        sha256 = "13r2xk7mnxfc5g0g6dkdxqdqad99j7s7z8zhzz4npw5r0g0v4hip";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lock_api";
+            packageId = "lock_api";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core 0.9.9";
+          }
+        ];
+        features = {
+          "arc_lock" = [ "lock_api/arc_lock" ];
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot_core 0.8.6" = rec {
+        crateName = "parking_lot_core";
+        version = "0.8.6";
+        edition = "2018";
+        sha256 = "1p2nfcbr0b9lm9rglgm28k6mwyjwgm4knipsmqbgqaxdy3kcz8k0";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "instant";
+            packageId = "instant";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.2.16";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "winnt" "ntstatus" "minwindef" "winerror" "winbase" "errhandlingapi" "handleapi" ];
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "parking_lot_core 0.9.9" = rec {
+        crateName = "parking_lot_core";
+        version = "0.9.9";
+        edition = "2018";
+        sha256 = "13h0imw1aq86wj28gxkblhkzx6z1gk8q18n0v76qmmj6cliajhjc";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.4.1";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "path-clean" = rec {
+        crateName = "path-clean";
+        version = "0.1.0";
+        edition = "2015";
+        sha256 = "1pcgqxw0mgg3ha5hi5xkjhyjf488bw5rw1g3qlr9awbq4szh3fpc";
+        authors = [
+          "Dan Reeves <hey@danreev.es>"
+        ];
+
+      };
+      "percent-encoding" = rec {
+        crateName = "percent-encoding";
+        version = "2.3.1";
+        edition = "2018";
+        sha256 = "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573";
+        authors = [
+          "The rust-url developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "petgraph" = rec {
+        crateName = "petgraph";
+        version = "0.6.4";
+        edition = "2018";
+        sha256 = "1ac6wfq5f5pzcv0nvzzfgjbwg2kwslpnzsw5wcmxlscfcb9azlz1";
+        authors = [
+          "bluss"
+          "mitchmindtree"
+        ];
+        dependencies = [
+          {
+            name = "fixedbitset";
+            packageId = "fixedbitset";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap 2.1.0";
+          }
+        ];
+        features = {
+          "all" = [ "unstable" "quickcheck" "matrix_graph" "stable_graph" "graphmap" ];
+          "default" = [ "graphmap" "stable_graph" "matrix_graph" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "serde" = [ "dep:serde" ];
+          "serde-1" = [ "serde" "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+          "unstable" = [ "generate" ];
+        };
+        resolvedDefaultFeatures = [ "default" "graphmap" "matrix_graph" "stable_graph" ];
+      };
+      "pin-project" = rec {
+        crateName = "pin-project";
+        version = "1.1.3";
+        edition = "2021";
+        sha256 = "08k4cpy8q3j93qqgnrbzkcgpn7g0a88l4a9nm33kyghpdhffv97x";
+        dependencies = [
+          {
+            name = "pin-project-internal";
+            packageId = "pin-project-internal";
+          }
+        ];
+
+      };
+      "pin-project-internal" = rec {
+        crateName = "pin-project-internal";
+        version = "1.1.3";
+        edition = "2021";
+        sha256 = "01a4l3vb84brv9v7wl71chzxra2kynm6yvcjca66xv3ij6fgsna3";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "pin-project-lite" = rec {
+        crateName = "pin-project-lite";
+        version = "0.2.13";
+        edition = "2018";
+        sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+
+      };
+      "pin-utils" = rec {
+        crateName = "pin-utils";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        authors = [
+          "Josef Brandl <mail@josefbrandl.de>"
+        ];
+
+      };
+      "piper" = rec {
+        crateName = "piper";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "1m45fkdq7q5l9mv3b0ra10qwm0kb67rjp2q8y91958gbqjqk33b6";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+          "John Nunley <dev@notgull.net>"
+        ];
+        dependencies = [
+          {
+            name = "atomic-waker";
+            packageId = "atomic-waker";
+          }
+          {
+            name = "fastrand";
+            packageId = "fastrand";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "portable-atomic" = [ "atomic-waker/portable-atomic" "portable_atomic_crate" "portable-atomic-util" ];
+          "portable-atomic-util" = [ "dep:portable-atomic-util" ];
+          "portable_atomic_crate" = [ "dep:portable_atomic_crate" ];
+          "std" = [ "fastrand/std" "futures-io" ];
+        };
+        resolvedDefaultFeatures = [ "default" "futures-io" "std" ];
+      };
+      "pkcs8" = rec {
+        crateName = "pkcs8";
+        version = "0.10.2";
+        edition = "2021";
+        sha256 = "1dx7w21gvn07azszgqd3ryjhyphsrjrmq5mmz1fbxkj5g0vv4l7r";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+          {
+            name = "spki";
+            packageId = "spki";
+          }
+        ];
+        features = {
+          "3des" = [ "encryption" "pkcs5/3des" ];
+          "alloc" = [ "der/alloc" "der/zeroize" "spki/alloc" ];
+          "des-insecure" = [ "encryption" "pkcs5/des-insecure" ];
+          "encryption" = [ "alloc" "pkcs5/alloc" "pkcs5/pbes2" "rand_core" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "pem" = [ "alloc" "der/pem" "spki/pem" ];
+          "pkcs5" = [ "dep:pkcs5" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "sha1-insecure" = [ "encryption" "pkcs5/sha1-insecure" ];
+          "std" = [ "alloc" "der/std" "spki/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "pkg-config" = rec {
+        crateName = "pkg-config";
+        version = "0.3.29";
+        edition = "2015";
+        sha256 = "1jy6158v1316khkpmq2sjj1vgbnbnw51wffx7p0k0l9h9vlys019";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+
+      };
+      "platforms" = rec {
+        crateName = "platforms";
+        version = "3.3.0";
+        edition = "2018";
+        sha256 = "0k7q6pigmnvgpfasvssb12m2pv3pc94zrhrfg9by3h3wmhyfqvb2";
+        authors = [
+          "Tony Arcieri <bascule@gmail.com>"
+          "Sergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "plotters" = rec {
+        crateName = "plotters";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "0igxq58bx96gz58pqls6g3h80plf17rfl3b6bi6xvjnp02x29hnj";
+        authors = [
+          "Hao Hou <haohou302@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "plotters-backend";
+            packageId = "plotters-backend";
+          }
+          {
+            name = "plotters-svg";
+            packageId = "plotters-svg";
+            optional = true;
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!("wasi" == target."os" or null)));
+          }
+          {
+            name = "web-sys";
+            packageId = "web-sys";
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!("wasi" == target."os" or null)));
+            features = [ "Document" "DomRect" "Element" "HtmlElement" "Node" "Window" "HtmlCanvasElement" "CanvasRenderingContext2d" ];
+          }
+        ];
+        features = {
+          "ab_glyph" = [ "dep:ab_glyph" "once_cell" ];
+          "all_elements" = [ "errorbar" "candlestick" "boxplot" "histogram" ];
+          "all_series" = [ "area_series" "line_series" "point_series" "surface_series" ];
+          "bitmap_backend" = [ "plotters-bitmap" ];
+          "bitmap_encoder" = [ "plotters-bitmap/image_encoder" ];
+          "bitmap_gif" = [ "plotters-bitmap/gif_backend" ];
+          "chrono" = [ "dep:chrono" ];
+          "datetime" = [ "chrono" ];
+          "default" = [ "bitmap_backend" "bitmap_encoder" "bitmap_gif" "svg_backend" "chrono" "ttf" "image" "deprecated_items" "all_series" "all_elements" "full_palette" "colormaps" ];
+          "evcxr" = [ "svg_backend" ];
+          "evcxr_bitmap" = [ "evcxr" "bitmap_backend" "plotters-svg/bitmap_encoder" ];
+          "font-kit" = [ "dep:font-kit" ];
+          "fontconfig-dlopen" = [ "font-kit/source-fontconfig-dlopen" ];
+          "image" = [ "dep:image" ];
+          "lazy_static" = [ "dep:lazy_static" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "pathfinder_geometry" = [ "dep:pathfinder_geometry" ];
+          "plotters-bitmap" = [ "dep:plotters-bitmap" ];
+          "plotters-svg" = [ "dep:plotters-svg" ];
+          "svg_backend" = [ "plotters-svg" ];
+          "ttf" = [ "font-kit" "ttf-parser" "lazy_static" "pathfinder_geometry" ];
+          "ttf-parser" = [ "dep:ttf-parser" ];
+        };
+        resolvedDefaultFeatures = [ "area_series" "line_series" "plotters-svg" "svg_backend" ];
+      };
+      "plotters-backend" = rec {
+        crateName = "plotters-backend";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "02cn98gsj2i1bwrfsymifmyas1wn2gibdm9mk8w82x9s9n5n4xly";
+        authors = [
+          "Hao Hou <haohou302@gmail.com>"
+        ];
+
+      };
+      "plotters-svg" = rec {
+        crateName = "plotters-svg";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "1axbw82frs5di4drbyzihr5j35wpy2a75hp3f49p186cjfcd7xiq";
+        authors = [
+          "Hao Hou <haohou302@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "plotters-backend";
+            packageId = "plotters-backend";
+          }
+        ];
+        features = {
+          "bitmap_encoder" = [ "image" ];
+          "image" = [ "dep:image" ];
+        };
+      };
+      "polling" = rec {
+        crateName = "polling";
+        version = "3.4.0";
+        edition = "2021";
+        sha256 = "052am20b5r03nwhpnjw86rv3dwsdabvb07anv3fqxfbs65r4w19h";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+          "John Nunley <dev@notgull.net>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "concurrent-queue";
+            packageId = "concurrent-queue";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((target."unix" or false) || ("fuchsia" == target."os" or null) || ("vxworks" == target."os" or null));
+            features = [ "event" "fs" "pipe" "process" "std" "time" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Wdk_Foundation" "Wdk_Storage_FileSystem" "Win32_Foundation" "Win32_Networking_WinSock" "Win32_Security" "Win32_Storage_FileSystem" "Win32_System_IO" "Win32_System_LibraryLoader" "Win32_System_Threading" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+
+      };
+      "powerfmt" = rec {
+        crateName = "powerfmt";
+        version = "0.2.0";
+        edition = "2021";
+        sha256 = "14ckj2xdpkhv3h6l5sdmb9f1d57z8hbfpdldjc2vl5givq2y77j3";
+        authors = [
+          "Jacob Pratt <jacob@jhpratt.dev>"
+        ];
+        features = {
+          "default" = [ "std" "macros" ];
+          "macros" = [ "dep:powerfmt-macros" ];
+          "std" = [ "alloc" ];
+        };
+      };
+      "ppv-lite86" = rec {
+        crateName = "ppv-lite86";
+        version = "0.2.17";
+        edition = "2018";
+        sha256 = "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v";
+        authors = [
+          "The CryptoCorrosion Contributors"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "simd" "std" ];
+      };
+      "pretty_assertions" = rec {
+        crateName = "pretty_assertions";
+        version = "1.4.0";
+        edition = "2018";
+        sha256 = "0rmsnqlpmpfjp5gyi31xgc48kdhc1kqn246bnc494nwadhdfwz5g";
+        authors = [
+          "Colin Kiegel <kiegel@gmx.de>"
+          "Florent Fayolle <florent.fayolle69@gmail.com>"
+          "Tom Milligan <code@tommilligan.net>"
+        ];
+        dependencies = [
+          {
+            name = "diff";
+            packageId = "diff";
+          }
+          {
+            name = "yansi";
+            packageId = "yansi";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "prettyplease" = rec {
+        crateName = "prettyplease";
+        version = "0.2.16";
+        edition = "2021";
+        links = "prettyplease02";
+        sha256 = "1dfbq98rkq86l9g8w1l81bdvrz4spcfl48929n0pyz79clhzc754";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            usesDefaultFeatures = false;
+            features = [ "full" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            usesDefaultFeatures = false;
+            features = [ "parsing" ];
+          }
+        ];
+        features = {
+          "verbatim" = [ "syn/parsing" ];
+        };
+      };
+      "proc-macro2" = rec {
+        crateName = "proc-macro2";
+        version = "1.0.76";
+        edition = "2021";
+        sha256 = "136cp0fgl6rg5ljm3b1xpc0bn0lyvagzzmxvbxgk5hxml36mdz4m";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "proptest" = rec {
+        crateName = "proptest";
+        version = "1.4.0";
+        edition = "2018";
+        sha256 = "1gzmw40pgmwzb7x6jsyr88z5w151snv5rp1g0dlcp1iw3h9pdd1i";
+        authors = [
+          "Jason Lingle"
+        ];
+        dependencies = [
+          {
+            name = "bit-set";
+            packageId = "bit-set";
+            optional = true;
+          }
+          {
+            name = "bit-vec";
+            packageId = "bit-vec";
+            optional = true;
+          }
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+            optional = true;
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+            features = [ "libm" ];
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "rand_chacha";
+            packageId = "rand_chacha";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand_xorshift";
+            packageId = "rand_xorshift";
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax 0.8.2";
+            optional = true;
+          }
+          {
+            name = "rusty-fork";
+            packageId = "rusty-fork";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+            optional = true;
+          }
+          {
+            name = "unarray";
+            packageId = "unarray";
+          }
+        ];
+        features = {
+          "bit-set" = [ "dep:bit-set" "dep:bit-vec" ];
+          "default" = [ "std" "fork" "timeout" "bit-set" ];
+          "default-code-coverage" = [ "std" "fork" "timeout" "bit-set" ];
+          "fork" = [ "std" "rusty-fork" "tempfile" ];
+          "hardware-rng" = [ "x86" ];
+          "lazy_static" = [ "dep:lazy_static" ];
+          "regex-syntax" = [ "dep:regex-syntax" ];
+          "rusty-fork" = [ "dep:rusty-fork" ];
+          "std" = [ "rand/std" "lazy_static" "regex-syntax" "num-traits/std" ];
+          "tempfile" = [ "dep:tempfile" ];
+          "timeout" = [ "fork" "rusty-fork/timeout" ];
+          "x86" = [ "dep:x86" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "bit-set" "default" "fork" "lazy_static" "regex-syntax" "rusty-fork" "std" "tempfile" "timeout" ];
+      };
+      "prost 0.11.9" = rec {
+        crateName = "prost";
+        version = "0.11.9";
+        edition = "2021";
+        sha256 = "1kc1hva2h894hc0zf6r4r8fsxfpazf7xn5rj3jya9sbrsyhym0hb";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "prost-derive";
+            packageId = "prost-derive 0.11.9";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "prost-derive" "std" ];
+          "prost-derive" = [ "dep:prost-derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "prost-derive" "std" ];
+      };
+      "prost 0.12.3" = rec {
+        crateName = "prost";
+        version = "0.12.3";
+        edition = "2021";
+        sha256 = "0jmrhlb4jkiylz72xb14vlkfbmlq0jwv7j20ini9harhvaf2hv0l";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "prost-derive";
+            packageId = "prost-derive 0.12.3";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "prost-derive" "std" ];
+          "prost-derive" = [ "dep:prost-derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "prost-derive" "std" ];
+      };
+      "prost-build" = rec {
+        crateName = "prost-build";
+        version = "0.12.3";
+        edition = "2021";
+        sha256 = "1lp2l1l65l163yggk9nw5mjb2fqwzz12693af5phn1v0abih4pn5";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "itertools";
+            packageId = "itertools 0.11.0";
+            usesDefaultFeatures = false;
+            features = [ "use_alloc" ];
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "multimap";
+            packageId = "multimap";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "petgraph";
+            packageId = "petgraph";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "prettyplease";
+            packageId = "prettyplease";
+            optional = true;
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "prost-types";
+            packageId = "prost-types";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "pulldown-cmark";
+            packageId = "pulldown-cmark";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "pulldown-cmark-to-cmark";
+            packageId = "pulldown-cmark-to-cmark";
+            optional = true;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            usesDefaultFeatures = false;
+            features = [ "std" "unicode-bool" ];
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            optional = true;
+            features = [ "full" ];
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+          }
+          {
+            name = "which";
+            packageId = "which 4.4.2";
+          }
+        ];
+        features = {
+          "cleanup-markdown" = [ "pulldown-cmark" "pulldown-cmark-to-cmark" ];
+          "default" = [ "format" ];
+          "format" = [ "prettyplease" "syn" ];
+          "prettyplease" = [ "dep:prettyplease" ];
+          "pulldown-cmark" = [ "dep:pulldown-cmark" ];
+          "pulldown-cmark-to-cmark" = [ "dep:pulldown-cmark-to-cmark" ];
+          "syn" = [ "dep:syn" ];
+        };
+        resolvedDefaultFeatures = [ "cleanup-markdown" "default" "format" "prettyplease" "pulldown-cmark" "pulldown-cmark-to-cmark" "syn" ];
+      };
+      "prost-derive 0.11.9" = rec {
+        crateName = "prost-derive";
+        version = "0.11.9";
+        edition = "2021";
+        sha256 = "1d3mw2s2jba1f7wcjmjd6ha2a255p2rmynxhm1nysv9w1z8xilp5";
+        procMacro = true;
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "anyhow";
+            packageId = "anyhow";
+          }
+          {
+            name = "itertools";
+            packageId = "itertools 0.10.5";
+            usesDefaultFeatures = false;
+            features = [ "use_alloc" ];
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "extra-traits" ];
+          }
+        ];
+
+      };
+      "prost-derive 0.12.3" = rec {
+        crateName = "prost-derive";
+        version = "0.12.3";
+        edition = "2021";
+        sha256 = "03l4yf6pdjvc4sgbvln2srq1avzm1ai86zni4hhqxvqxvnhwkdpg";
+        procMacro = true;
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "anyhow";
+            packageId = "anyhow";
+          }
+          {
+            name = "itertools";
+            packageId = "itertools 0.11.0";
+            usesDefaultFeatures = false;
+            features = [ "use_alloc" ];
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "extra-traits" ];
+          }
+        ];
+
+      };
+      "prost-types" = rec {
+        crateName = "prost-types";
+        version = "0.12.3";
+        edition = "2021";
+        sha256 = "03j73llzljdxv9cdxp4m3vb9j3gh4y24rkbx48k3rx6wkvsrhf0r";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+            usesDefaultFeatures = false;
+            features = [ "prost-derive" ];
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "prost/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "prost-wkt" = rec {
+        crateName = "prost-wkt";
+        version = "0.5.0";
+        edition = "2021";
+        sha256 = "0h3c0jyfpg7f3s9a3mx6xcif28j3ir5c5qmps88bknpiy31zk3jd";
+        authors = [
+          "fdeantoni <fdeantoni@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "serde" ];
+          }
+          {
+            name = "inventory";
+            packageId = "inventory";
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "typetag";
+            packageId = "typetag";
+          }
+        ];
+
+      };
+      "prost-wkt-build" = rec {
+        crateName = "prost-wkt-build";
+        version = "0.5.0";
+        edition = "2021";
+        sha256 = "0883g26vrhx07kv0dq85559pj95zxs10lx042pp4za2clplwlcav";
+        authors = [
+          "fdeantoni <fdeantoni@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+          }
+          {
+            name = "prost-build";
+            packageId = "prost-build";
+          }
+          {
+            name = "prost-types";
+            packageId = "prost-types";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+        ];
+
+      };
+      "prost-wkt-types" = rec {
+        crateName = "prost-wkt-types";
+        version = "0.5.0";
+        edition = "2021";
+        sha256 = "1vipmgvqqzr3hn9z5v85mx9zznzjwyfpjy8xzg2v94a0f2lf8ns3";
+        authors = [
+          "fdeantoni <fdeantoni@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "serde" ];
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+          }
+          {
+            name = "prost-wkt";
+            packageId = "prost-wkt";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+          }
+          {
+            name = "prost-build";
+            packageId = "prost-build";
+          }
+          {
+            name = "prost-types";
+            packageId = "prost-types";
+          }
+          {
+            name = "prost-wkt-build";
+            packageId = "prost-wkt-build";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "protobuf-src" = [ "dep:protobuf-src" ];
+          "protox" = [ "dep:protox" ];
+          "vendored-protoc" = [ "protobuf-src" ];
+          "vendored-protox" = [ "protox" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "pulldown-cmark" = rec {
+        crateName = "pulldown-cmark";
+        version = "0.9.6";
+        edition = "2021";
+        crateBin = [ ];
+        sha256 = "0av876a31qvqhy7gzdg134zn4s10smlyi744mz9vrllkf906n82p";
+        authors = [
+          "Raph Levien <raph.levien@gmail.com>"
+          "Marcus Klaas de Vries <mail@marcusklaas.nl>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "unicase";
+            packageId = "unicase";
+          }
+        ];
+        features = {
+          "default" = [ "getopts" ];
+          "getopts" = [ "dep:getopts" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "pulldown-cmark-to-cmark" = rec {
+        crateName = "pulldown-cmark-to-cmark";
+        version = "10.0.4";
+        edition = "2018";
+        sha256 = "0gc366cmd5jxal9m95l17rvqsm4dn62lywc8v5gwq8vcjvhyd501";
+        authors = [
+          "Sebastian Thiel <byronimo@gmail.com>"
+          "Dylan Owen <dyltotheo@gmail.com>"
+          "Alessandro Ogier <alessandro.ogier@gmail.com>"
+          "Zixian Cai <2891235+caizixian@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "pulldown-cmark";
+            packageId = "pulldown-cmark";
+            usesDefaultFeatures = false;
+          }
+        ];
+
+      };
+      "quick-error" = rec {
+        crateName = "quick-error";
+        version = "1.2.3";
+        edition = "2015";
+        sha256 = "1q6za3v78hsspisc197bg3g7rpc989qycy8ypr8ap8igv10ikl51";
+        authors = [
+          "Paul Colomiets <paul@colomiets.name>"
+          "Colin Kiegel <kiegel@gmx.de>"
+        ];
+
+      };
+      "quick-xml" = rec {
+        crateName = "quick-xml";
+        version = "0.31.0";
+        edition = "2021";
+        sha256 = "0cravqanylzh5cq2v6hzlfqgxcid5nrp2snnb3pf4m0and2a610h";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "async-tokio" = [ "tokio" ];
+          "document-features" = [ "dep:document-features" ];
+          "encoding" = [ "encoding_rs" ];
+          "encoding_rs" = [ "dep:encoding_rs" ];
+          "serde" = [ "dep:serde" ];
+          "serde-types" = [ "serde/derive" ];
+          "serialize" = [ "serde" ];
+          "tokio" = [ "dep:tokio" ];
+        };
+        resolvedDefaultFeatures = [ "default" "overlapped-lists" "serde" "serialize" ];
+      };
+      "quote" = rec {
+        crateName = "quote";
+        version = "1.0.35";
+        edition = "2018";
+        sha256 = "1vv8r2ncaz4pqdr78x7f138ka595sp2ncr1sa2plm4zxbsmwj7i9";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "radix_trie" = rec {
+        crateName = "radix_trie";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "1zaq3im5ss03w91ij11cj97vvzc5y1f3064d9pi2ysnwziww2sf0";
+        authors = [
+          "Michael Sproul <micsproul@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "endian-type";
+            packageId = "endian-type";
+          }
+          {
+            name = "nibble_vec";
+            packageId = "nibble_vec";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "rand" = rec {
+        crateName = "rand";
+        version = "0.8.5";
+        edition = "2018";
+        sha256 = "013l6931nn7gkc23jz5mm3qdhf93jjf0fg64nz2lp4i51qd8vbrl";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "rand_chacha";
+            packageId = "rand_chacha";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "alloc" = [ "rand_core/alloc" ];
+          "default" = [ "std" "std_rng" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "libc" = [ "dep:libc" ];
+          "log" = [ "dep:log" ];
+          "packed_simd" = [ "dep:packed_simd" ];
+          "rand_chacha" = [ "dep:rand_chacha" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" "rand_core/serde1" ];
+          "simd_support" = [ "packed_simd" ];
+          "std" = [ "rand_core/std" "rand_chacha/std" "alloc" "getrandom" "libc" ];
+          "std_rng" = [ "rand_chacha" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "getrandom" "libc" "rand_chacha" "small_rng" "std" "std_rng" ];
+      };
+      "rand_chacha" = rec {
+        crateName = "rand_chacha";
+        version = "0.3.1";
+        edition = "2018";
+        sha256 = "123x2adin558xbhvqb8w4f6syjsdkmqff8cxwhmjacpsl1ihmhg6";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+          "The CryptoCorrosion Contributors"
+        ];
+        dependencies = [
+          {
+            name = "ppv-lite86";
+            packageId = "ppv-lite86";
+            usesDefaultFeatures = false;
+            features = [ "simd" ];
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "ppv-lite86/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "rand_core" = rec {
+        crateName = "rand_core";
+        version = "0.6.4";
+        edition = "2018";
+        sha256 = "0b4j2v4cb5krak1pv6kakv4sz6xcwbrmy2zckc32hsigbrwy82zc";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+        ];
+        features = {
+          "getrandom" = [ "dep:getrandom" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "alloc" "getrandom" "getrandom/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "getrandom" "std" ];
+      };
+      "rand_xorshift" = rec {
+        crateName = "rand_xorshift";
+        version = "0.3.0";
+        edition = "2018";
+        sha256 = "13vcag7gmqspzyabfl1gr9ykvxd2142q2agrj8dkyjmfqmgg4nyj";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+        };
+      };
+      "rand_xoshiro" = rec {
+        crateName = "rand_xoshiro";
+        version = "0.6.0";
+        edition = "2018";
+        sha256 = "1ajsic84rzwz5qr0mzlay8vi17swqi684bqvwqyiim3flfrcv5vg";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+        };
+      };
+      "rayon" = rec {
+        crateName = "rayon";
+        version = "1.8.1";
+        edition = "2021";
+        sha256 = "0lg0488xwpj5jsfz2gfczcrpclbjl8221mj5vdrhg8bp3883fwps";
+        authors = [
+          "Niko Matsakis <niko@alum.mit.edu>"
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon-core";
+            packageId = "rayon-core";
+          }
+        ];
+        features = {
+          "web_spin_lock" = [ "dep:wasm_sync" "rayon-core/web_spin_lock" ];
+        };
+      };
+      "rayon-core" = rec {
+        crateName = "rayon-core";
+        version = "1.12.1";
+        edition = "2021";
+        links = "rayon-core";
+        sha256 = "1qpwim68ai5h0j7axa8ai8z0payaawv3id0lrgkqmapx7lx8fr8l";
+        authors = [
+          "Niko Matsakis <niko@alum.mit.edu>"
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-deque";
+            packageId = "crossbeam-deque";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+        ];
+        features = {
+          "web_spin_lock" = [ "dep:wasm_sync" ];
+        };
+      };
+      "redox_syscall 0.2.16" = rec {
+        crateName = "redox_syscall";
+        version = "0.2.16";
+        edition = "2018";
+        sha256 = "16jicm96kjyzm802cxdd1k9jmcph0db1a4lhslcnhjsvhp0mhnpv";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+
+      };
+      "redox_syscall 0.3.5" = rec {
+        crateName = "redox_syscall";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "0acgiy2lc1m2vr8cr33l5s7k9wzby8dybyab1a9p753hcbr68xjn";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+        features = {
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "bitflags/rustc-dep-of-std" ];
+        };
+      };
+      "redox_syscall 0.4.1" = rec {
+        crateName = "redox_syscall";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1aiifyz5dnybfvkk4cdab9p2kmphag1yad6iknc7aszlxxldf8j7";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+        features = {
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "bitflags/rustc-dep-of-std" ];
+        };
+      };
+      "redox_users" = rec {
+        crateName = "redox_users";
+        version = "0.4.4";
+        edition = "2021";
+        sha256 = "1d1c7dhbb62sh8jrq9dhvqcyxqsh3wg8qknsi94iwq3r0wh7k151";
+        authors = [
+          "Jose Narvaez <goyox86@gmail.com>"
+          "Wesley Hershberger <mggmugginsmc@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            features = [ "std" ];
+          }
+          {
+            name = "libredox";
+            packageId = "libredox";
+            usesDefaultFeatures = false;
+            features = [ "call" ];
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        features = {
+          "auth" = [ "rust-argon2" "zeroize" ];
+          "default" = [ "auth" ];
+          "rust-argon2" = [ "dep:rust-argon2" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+      };
+      "regex" = rec {
+        crateName = "regex";
+        version = "1.10.2";
+        edition = "2021";
+        sha256 = "0hxkd814n4irind8im5c9am221ri6bprx49nc7yxv02ykhd9a2rq";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata 0.4.3";
+            usesDefaultFeatures = false;
+            features = [ "alloc" "syntax" "meta" "nfa-pikevm" ];
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax 0.8.2";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf" "unicode" "regex-syntax/default" ];
+          "logging" = [ "aho-corasick?/logging" "memchr?/logging" "regex-automata/logging" ];
+          "perf" = [ "perf-cache" "perf-dfa" "perf-onepass" "perf-backtrack" "perf-inline" "perf-literal" ];
+          "perf-backtrack" = [ "regex-automata/nfa-backtrack" ];
+          "perf-dfa" = [ "regex-automata/hybrid" ];
+          "perf-dfa-full" = [ "regex-automata/dfa-build" "regex-automata/dfa-search" ];
+          "perf-inline" = [ "regex-automata/perf-inline" ];
+          "perf-literal" = [ "dep:aho-corasick" "dep:memchr" "regex-automata/perf-literal" ];
+          "perf-onepass" = [ "regex-automata/dfa-onepass" ];
+          "std" = [ "aho-corasick?/std" "memchr?/std" "regex-automata/std" "regex-syntax/std" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "regex-automata/unicode" "regex-syntax/unicode" ];
+          "unicode-age" = [ "regex-automata/unicode-age" "regex-syntax/unicode-age" ];
+          "unicode-bool" = [ "regex-automata/unicode-bool" "regex-syntax/unicode-bool" ];
+          "unicode-case" = [ "regex-automata/unicode-case" "regex-syntax/unicode-case" ];
+          "unicode-gencat" = [ "regex-automata/unicode-gencat" "regex-syntax/unicode-gencat" ];
+          "unicode-perl" = [ "regex-automata/unicode-perl" "regex-automata/unicode-word-boundary" "regex-syntax/unicode-perl" ];
+          "unicode-script" = [ "regex-automata/unicode-script" "regex-syntax/unicode-script" ];
+          "unicode-segment" = [ "regex-automata/unicode-segment" "regex-syntax/unicode-segment" ];
+          "unstable" = [ "pattern" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "perf" "perf-backtrack" "perf-cache" "perf-dfa" "perf-inline" "perf-literal" "perf-onepass" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "regex-automata 0.1.10" = rec {
+        crateName = "regex-automata";
+        version = "0.1.10";
+        edition = "2015";
+        sha256 = "0ci1hvbzhrfby5fdpf4ganhf7kla58acad9i1ff1p34dzdrhs8vc";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax 0.6.29";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "fst" = [ "dep:fst" ];
+          "regex-syntax" = [ "dep:regex-syntax" ];
+          "std" = [ "regex-syntax" ];
+          "transducer" = [ "std" "fst" ];
+        };
+        resolvedDefaultFeatures = [ "default" "regex-syntax" "std" ];
+      };
+      "regex-automata 0.4.3" = rec {
+        crateName = "regex-automata";
+        version = "0.4.3";
+        edition = "2021";
+        sha256 = "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax 0.8.2";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "syntax" "perf" "unicode" "meta" "nfa" "dfa" "hybrid" ];
+          "dfa" = [ "dfa-build" "dfa-search" "dfa-onepass" ];
+          "dfa-build" = [ "nfa-thompson" "dfa-search" ];
+          "dfa-onepass" = [ "nfa-thompson" ];
+          "hybrid" = [ "alloc" "nfa-thompson" ];
+          "internal-instrument" = [ "internal-instrument-pikevm" ];
+          "internal-instrument-pikevm" = [ "logging" "std" ];
+          "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ];
+          "meta" = [ "syntax" "nfa-pikevm" ];
+          "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ];
+          "nfa-backtrack" = [ "nfa-thompson" ];
+          "nfa-pikevm" = [ "nfa-thompson" ];
+          "nfa-thompson" = [ "alloc" ];
+          "perf" = [ "perf-inline" "perf-literal" ];
+          "perf-literal" = [ "perf-literal-substring" "perf-literal-multisubstring" ];
+          "perf-literal-multisubstring" = [ "std" "dep:aho-corasick" ];
+          "perf-literal-substring" = [ "aho-corasick?/perf-literal" "dep:memchr" ];
+          "std" = [ "regex-syntax?/std" "memchr?/std" "aho-corasick?/std" "alloc" ];
+          "syntax" = [ "dep:regex-syntax" "alloc" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" "regex-syntax?/unicode" ];
+          "unicode-age" = [ "regex-syntax?/unicode-age" ];
+          "unicode-bool" = [ "regex-syntax?/unicode-bool" ];
+          "unicode-case" = [ "regex-syntax?/unicode-case" ];
+          "unicode-gencat" = [ "regex-syntax?/unicode-gencat" ];
+          "unicode-perl" = [ "regex-syntax?/unicode-perl" ];
+          "unicode-script" = [ "regex-syntax?/unicode-script" ];
+          "unicode-segment" = [ "regex-syntax?/unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "dfa-onepass" "dfa-search" "hybrid" "meta" "nfa-backtrack" "nfa-pikevm" "nfa-thompson" "perf-inline" "perf-literal" "perf-literal-multisubstring" "perf-literal-substring" "std" "syntax" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" ];
+      };
+      "regex-syntax 0.6.29" = rec {
+        crateName = "regex-syntax";
+        version = "0.6.29";
+        edition = "2018";
+        sha256 = "1qgj49vm6y3zn1hi09x91jvgkl2b1fiaq402skj83280ggfwcqpi";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "unicode" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "default" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "regex-syntax 0.8.2" = rec {
+        crateName = "regex-syntax";
+        version = "0.8.2";
+        edition = "2021";
+        sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" "unicode" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "relative-path" = rec {
+        crateName = "relative-path";
+        version = "1.9.2";
+        edition = "2021";
+        sha256 = "1g0gc604zwm73gvpcicn8si25j9j5agqz50r0x1bkmgx6f7mi678";
+        authors = [
+          "John-John Tedro <udoprog@tedro.se>"
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "reqwest" = rec {
+        crateName = "reqwest";
+        version = "0.11.23";
+        edition = "2018";
+        sha256 = "0hgvzb7r46656r9vqhl5qk1kbr2xzjb91yr2cb321160ka6sxc9p";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "encoding_rs";
+            packageId = "encoding_rs";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "h2";
+            packageId = "h2 0.3.24";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 0.4.6";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+            features = [ "tcp" "http1" "http2" "client" "runtime" ];
+          }
+          {
+            name = "hyper-rustls";
+            packageId = "hyper-rustls";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "ipnet";
+            packageId = "ipnet";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "log";
+            packageId = "log";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "mime";
+            packageId = "mime";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "rustls";
+            packageId = "rustls 0.21.10";
+            optional = true;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+            features = [ "dangerous_configuration" ];
+          }
+          {
+            name = "rustls-native-certs";
+            packageId = "rustls-native-certs 0.6.3";
+            optional = true;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "rustls-pemfile";
+            packageId = "rustls-pemfile 1.0.4";
+            optional = true;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "serde_urlencoded";
+            packageId = "serde_urlencoded";
+          }
+          {
+            name = "system-configuration";
+            packageId = "system-configuration";
+            target = { target, features }: ("macos" == target."os" or null);
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+            features = [ "net" "time" ];
+          }
+          {
+            name = "tokio-rustls";
+            packageId = "tokio-rustls 0.24.1";
+            optional = true;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+            features = [ "codec" "io" ];
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "url";
+            packageId = "url";
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "wasm-bindgen-futures";
+            packageId = "wasm-bindgen-futures";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "wasm-streams";
+            packageId = "wasm-streams";
+            optional = true;
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "web-sys";
+            packageId = "web-sys";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+            features = [ "AbortController" "AbortSignal" "Headers" "Request" "RequestInit" "RequestMode" "Response" "Window" "FormData" "Blob" "BlobPropertyBag" "ServiceWorkerGlobalScope" "RequestCredentials" "File" "ReadableStream" ];
+          }
+          {
+            name = "winreg";
+            packageId = "winreg";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+            features = [ "tcp" "stream" "http1" "http2" "client" "server" "runtime" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+            features = [ "derive" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!("wasm32" == target."arch" or null));
+            features = [ "macros" "rt-multi-thread" ];
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+            features = [ "serde-serialize" ];
+          }
+        ];
+        features = {
+          "__rustls" = [ "hyper-rustls" "tokio-rustls" "rustls" "__tls" "rustls-pemfile" ];
+          "async-compression" = [ "dep:async-compression" ];
+          "blocking" = [ "futures-util/io" "tokio/rt-multi-thread" "tokio/sync" ];
+          "brotli" = [ "async-compression" "async-compression/brotli" "tokio-util" ];
+          "cookie_crate" = [ "dep:cookie_crate" ];
+          "cookie_store" = [ "dep:cookie_store" ];
+          "cookies" = [ "cookie_crate" "cookie_store" ];
+          "default" = [ "default-tls" ];
+          "default-tls" = [ "hyper-tls" "native-tls-crate" "__tls" "tokio-native-tls" ];
+          "deflate" = [ "async-compression" "async-compression/zlib" "tokio-util" ];
+          "futures-channel" = [ "dep:futures-channel" ];
+          "gzip" = [ "async-compression" "async-compression/gzip" "tokio-util" ];
+          "h3" = [ "dep:h3" ];
+          "h3-quinn" = [ "dep:h3-quinn" ];
+          "http3" = [ "rustls-tls-manual-roots" "h3" "h3-quinn" "quinn" "futures-channel" ];
+          "hyper-rustls" = [ "dep:hyper-rustls" ];
+          "hyper-tls" = [ "dep:hyper-tls" ];
+          "json" = [ "serde_json" ];
+          "mime_guess" = [ "dep:mime_guess" ];
+          "multipart" = [ "mime_guess" ];
+          "native-tls" = [ "default-tls" ];
+          "native-tls-alpn" = [ "native-tls" "native-tls-crate/alpn" ];
+          "native-tls-crate" = [ "dep:native-tls-crate" ];
+          "native-tls-vendored" = [ "native-tls" "native-tls-crate/vendored" ];
+          "quinn" = [ "dep:quinn" ];
+          "rustls" = [ "dep:rustls" ];
+          "rustls-native-certs" = [ "dep:rustls-native-certs" ];
+          "rustls-pemfile" = [ "dep:rustls-pemfile" ];
+          "rustls-tls" = [ "rustls-tls-webpki-roots" ];
+          "rustls-tls-manual-roots" = [ "__rustls" ];
+          "rustls-tls-native-roots" = [ "rustls-native-certs" "__rustls" ];
+          "rustls-tls-webpki-roots" = [ "webpki-roots" "__rustls" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "socks" = [ "tokio-socks" ];
+          "stream" = [ "tokio/fs" "tokio-util" "wasm-streams" ];
+          "tokio-native-tls" = [ "dep:tokio-native-tls" ];
+          "tokio-rustls" = [ "dep:tokio-rustls" ];
+          "tokio-socks" = [ "dep:tokio-socks" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "trust-dns" = [ "trust-dns-resolver" ];
+          "trust-dns-resolver" = [ "dep:trust-dns-resolver" ];
+          "wasm-streams" = [ "dep:wasm-streams" ];
+          "webpki-roots" = [ "dep:webpki-roots" ];
+        };
+        resolvedDefaultFeatures = [ "__rustls" "__tls" "hyper-rustls" "json" "rustls" "rustls-native-certs" "rustls-pemfile" "rustls-tls-native-roots" "serde_json" "stream" "tokio-rustls" "tokio-util" "wasm-streams" ];
+      };
+      "ring" = rec {
+        crateName = "ring";
+        version = "0.17.7";
+        edition = "2021";
+        links = "ring_core_0_17_7";
+        sha256 = "0x5vvsp2424vll571xx085qf4hzljmwpz4x8n9l0j1c3akb67338";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((("android" == target."os" or null) || ("linux" == target."os" or null)) && (("aarch64" == target."arch" or null) || ("arm" == target."arch" or null)));
+          }
+          {
+            name = "spin";
+            packageId = "spin";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("arm" == target."arch" or null) || ("x86" == target."arch" or null) || ("x86_64" == target."arch" or null));
+            features = [ "once" ];
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("windows" == target."os" or null));
+            features = [ "Win32_Foundation" "Win32_System_Threading" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((target."unix" or false) || (target."windows" or false) || ("wasi" == target."os" or null));
+          }
+        ];
+        features = {
+          "default" = [ "alloc" "dev_urandom_fallback" ];
+          "std" = [ "alloc" ];
+          "wasm32_unknown_unknown_js" = [ "getrandom/js" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "dev_urandom_fallback" "std" ];
+      };
+      "rnix" = rec {
+        crateName = "rnix";
+        version = "0.11.0";
+        edition = "2021";
+        sha256 = "0pybq9gp4b7lp0066236jpqi9lgb1bzvqc9axymwrq3hxgdwwddv";
+        authors = [
+          "jD91mZM2 <me@krake.one>"
+        ];
+        dependencies = [
+          {
+            name = "rowan";
+            packageId = "rowan";
+          }
+        ];
+
+      };
+      "rowan" = rec {
+        crateName = "rowan";
+        version = "0.15.15";
+        edition = "2021";
+        sha256 = "0j9b340gsyf2h7v1q9xb4mqyqp4qbyzlbk1r9zn2mzyclyl8z99j";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "countme";
+            packageId = "countme";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown 0.14.3";
+            usesDefaultFeatures = false;
+            features = [ "inline-more" ];
+          }
+          {
+            name = "memoffset";
+            packageId = "memoffset 0.9.0";
+          }
+          {
+            name = "rustc-hash";
+            packageId = "rustc-hash";
+          }
+          {
+            name = "text-size";
+            packageId = "text-size";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" "text-size/serde" ];
+        };
+      };
+      "rstest" = rec {
+        crateName = "rstest";
+        version = "0.19.0";
+        edition = "2021";
+        sha256 = "0c43nsxpm1b74jxc73xwg94is6bwqvfzkrr1xbqyx7j7l791clwx";
+        authors = [
+          "Michele d'Amico <michele.damico@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "futures-timer";
+            packageId = "futures-timer";
+            optional = true;
+          }
+          {
+            name = "rstest_macros";
+            packageId = "rstest_macros";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "async-timeout" = [ "dep:futures" "dep:futures-timer" "rstest_macros/async-timeout" ];
+          "default" = [ "async-timeout" ];
+        };
+        resolvedDefaultFeatures = [ "async-timeout" "default" ];
+      };
+      "rstest_macros" = rec {
+        crateName = "rstest_macros";
+        version = "0.19.0";
+        edition = "2021";
+        sha256 = "09ackagv8kc2v4xy0s7blyg4agij9bz9pbb31l5h4rqzrirdza84";
+        procMacro = true;
+        authors = [
+          "Michele d'Amico <michele.damico@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "relative-path";
+            packageId = "relative-path";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" "parsing" "extra-traits" "visit" "visit-mut" ];
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "default" = [ "async-timeout" ];
+        };
+        resolvedDefaultFeatures = [ "async-timeout" ];
+      };
+      "rstest_reuse" = rec {
+        crateName = "rstest_reuse";
+        version = "0.6.0";
+        edition = "2021";
+        sha256 = "191l5gfwx9rmkqd48s85fkh21b73f38838fc896r4rxy39l0nlw8";
+        procMacro = true;
+        authors = [
+          "Michele d'Amico <michele.damico@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" "extra-traits" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+
+      };
+      "rustc-demangle" = rec {
+        crateName = "rustc-demangle";
+        version = "0.1.23";
+        edition = "2015";
+        sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "rustc-hash" = rec {
+        crateName = "rustc-hash";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "1qkc5khrmv5pqi5l5ca9p5nl5hs742cagrndhbrlk3dhlrx3zm08";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "rustc_version" = rec {
+        crateName = "rustc_version";
+        version = "0.4.0";
+        edition = "2018";
+        sha256 = "0rpk9rcdk405xhbmgclsh4pai0svn49x35aggl4nhbkd4a2zb85z";
+        authors = [
+          "Dirkjan Ochtman <dirkjan@ochtman.nl>"
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "semver";
+            packageId = "semver";
+          }
+        ];
+
+      };
+      "rustix" = rec {
+        crateName = "rustix";
+        version = "0.38.30";
+        edition = "2021";
+        sha256 = "1jkb6bzrj2w9ffy35aw4q04mqk1yxqw35fz80x0c4cxgi9c988rj";
+        authors = [
+          "Dan Gohman <dev@sunfishcode.online>"
+          "Jakub Konka <kubkon@jakubkonka.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."windows" or false)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+            features = [ "extra_traits" ];
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."windows" or false)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+            features = [ "extra_traits" ];
+          }
+          {
+            name = "linux-raw-sys";
+            packageId = "linux-raw-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((("android" == target."os" or null) || ("linux" == target."os" or null)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+            features = [ "general" "ioctl" "no_std" ];
+          }
+          {
+            name = "linux-raw-sys";
+            packageId = "linux-raw-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+            features = [ "general" "errno" "ioctl" "no_std" "elf" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_NetworkManagement_IpHelper" "Win32_System_Threading" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "all-apis" = [ "event" "fs" "io_uring" "mm" "mount" "net" "param" "pipe" "process" "procfs" "pty" "rand" "runtime" "shm" "stdio" "system" "termios" "thread" "time" ];
+          "default" = [ "std" "use-libc-auxv" ];
+          "io_uring" = [ "event" "fs" "net" "linux-raw-sys/io_uring" ];
+          "itoa" = [ "dep:itoa" ];
+          "libc" = [ "dep:libc" ];
+          "libc_errno" = [ "dep:libc_errno" ];
+          "linux_latest" = [ "linux_4_11" ];
+          "net" = [ "linux-raw-sys/net" "linux-raw-sys/netlink" "linux-raw-sys/if_ether" "linux-raw-sys/xdp" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "param" = [ "fs" ];
+          "process" = [ "linux-raw-sys/prctl" ];
+          "procfs" = [ "once_cell" "itoa" "fs" ];
+          "pty" = [ "itoa" "fs" ];
+          "runtime" = [ "linux-raw-sys/prctl" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" "linux-raw-sys/rustc-dep-of-std" "bitflags/rustc-dep-of-std" "compiler_builtins?/rustc-dep-of-std" ];
+          "shm" = [ "fs" ];
+          "std" = [ "bitflags/std" "alloc" "libc?/std" "libc_errno?/std" ];
+          "system" = [ "linux-raw-sys/system" ];
+          "thread" = [ "linux-raw-sys/prctl" ];
+          "use-libc" = [ "libc_errno" "libc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "event" "fs" "net" "pipe" "process" "std" "termios" "time" "use-libc-auxv" ];
+      };
+      "rustls 0.21.10" = rec {
+        crateName = "rustls";
+        version = "0.21.10";
+        edition = "2021";
+        sha256 = "1fmpzk3axnhkd99saqkvraifdfms4pkyi56lkihf8n877j0sdmgr";
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "ring";
+            packageId = "ring";
+          }
+          {
+            name = "rustls-webpki";
+            packageId = "rustls-webpki 0.101.7";
+            rename = "webpki";
+            features = [ "alloc" "std" ];
+          }
+          {
+            name = "sct";
+            packageId = "sct";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "default" = [ "logging" "tls12" ];
+          "log" = [ "dep:log" ];
+          "logging" = [ "log" ];
+          "read_buf" = [ "rustversion" ];
+          "rustversion" = [ "dep:rustversion" ];
+        };
+        resolvedDefaultFeatures = [ "dangerous_configuration" "default" "log" "logging" "tls12" ];
+      };
+      "rustls 0.22.2" = rec {
+        crateName = "rustls";
+        version = "0.22.2";
+        edition = "2021";
+        sha256 = "0hcxyhq6ynvws9v5b2h81s1nwmijmya7a3vyyyhsy1wqpmb9jz78";
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "ring";
+            packageId = "ring";
+            optional = true;
+          }
+          {
+            name = "rustls-pki-types";
+            packageId = "rustls-pki-types";
+            rename = "pki-types";
+            features = [ "std" ];
+          }
+          {
+            name = "rustls-webpki";
+            packageId = "rustls-webpki 0.102.2";
+            rename = "webpki";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "aws_lc_rs" = [ "dep:aws-lc-rs" "webpki/aws_lc_rs" ];
+          "default" = [ "logging" "ring" "tls12" ];
+          "log" = [ "dep:log" ];
+          "logging" = [ "log" ];
+          "read_buf" = [ "rustversion" ];
+          "ring" = [ "dep:ring" "webpki/ring" ];
+          "rustversion" = [ "dep:rustversion" ];
+        };
+        resolvedDefaultFeatures = [ "log" "logging" "ring" "tls12" ];
+      };
+      "rustls-native-certs 0.6.3" = rec {
+        crateName = "rustls-native-certs";
+        version = "0.6.3";
+        edition = "2021";
+        sha256 = "007zind70rd5rfsrkdcfm8vn09j8sg02phg9334kark6rdscxam9";
+        dependencies = [
+          {
+            name = "openssl-probe";
+            packageId = "openssl-probe";
+            target = { target, features }: ((target."unix" or false) && (!("macos" == target."os" or null)));
+          }
+          {
+            name = "rustls-pemfile";
+            packageId = "rustls-pemfile 1.0.4";
+          }
+          {
+            name = "schannel";
+            packageId = "schannel";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "security-framework";
+            packageId = "security-framework";
+            target = { target, features }: ("macos" == target."os" or null);
+          }
+        ];
+
+      };
+      "rustls-native-certs 0.7.0" = rec {
+        crateName = "rustls-native-certs";
+        version = "0.7.0";
+        edition = "2021";
+        sha256 = "14ip15dcr6fmjzi12lla9cpln7mmkdid4a7wsp344v4kz9gbh7wg";
+        dependencies = [
+          {
+            name = "openssl-probe";
+            packageId = "openssl-probe";
+            target = { target, features }: ((target."unix" or false) && (!("macos" == target."os" or null)));
+          }
+          {
+            name = "rustls-pemfile";
+            packageId = "rustls-pemfile 2.1.0";
+          }
+          {
+            name = "rustls-pki-types";
+            packageId = "rustls-pki-types";
+            rename = "pki-types";
+          }
+          {
+            name = "schannel";
+            packageId = "schannel";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "security-framework";
+            packageId = "security-framework";
+            target = { target, features }: ("macos" == target."os" or null);
+          }
+        ];
+
+      };
+      "rustls-pemfile 1.0.4" = rec {
+        crateName = "rustls-pemfile";
+        version = "1.0.4";
+        edition = "2018";
+        sha256 = "1324n5bcns0rnw6vywr5agff3rwfvzphi7rmbyzwnv6glkhclx0w";
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64";
+          }
+        ];
+
+      };
+      "rustls-pemfile 2.1.0" = rec {
+        crateName = "rustls-pemfile";
+        version = "2.1.0";
+        edition = "2018";
+        sha256 = "02y7qn9d93ri4hrm72yw4zqlbxch6ma045nyazmdrppw6jvkncrw";
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "rustls-pki-types";
+            packageId = "rustls-pki-types";
+            rename = "pki-types";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "base64/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "rustls-pki-types" = rec {
+        crateName = "rustls-pki-types";
+        version = "1.3.1";
+        edition = "2021";
+        sha256 = "1a0g7453h07701vyxjj05gv903a0shi43mf7hl3cdd08hsr6gpjy";
+        features = {
+          "default" = [ "alloc" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "rustls-webpki 0.101.7" = rec {
+        crateName = "rustls-webpki";
+        version = "0.101.7";
+        edition = "2021";
+        sha256 = "0rapfhpkqp75552i8r0y7f4vq7csb4k7gjjans0df73sxv8paqlb";
+        libName = "webpki";
+        dependencies = [
+          {
+            name = "ring";
+            packageId = "ring";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted";
+          }
+        ];
+        features = {
+          "alloc" = [ "ring/alloc" ];
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "rustls-webpki 0.102.2" = rec {
+        crateName = "rustls-webpki";
+        version = "0.102.2";
+        edition = "2021";
+        sha256 = "041ncshpw8wsvi8p74a3yw9c0r17lhyk1yjsxyrbkv8bfii0maps";
+        libName = "webpki";
+        dependencies = [
+          {
+            name = "ring";
+            packageId = "ring";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rustls-pki-types";
+            packageId = "rustls-pki-types";
+            rename = "pki-types";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted";
+          }
+        ];
+        features = {
+          "alloc" = [ "ring?/alloc" "pki-types/alloc" ];
+          "aws_lc_rs" = [ "dep:aws-lc-rs" ];
+          "default" = [ "std" "ring" ];
+          "ring" = [ "dep:ring" ];
+          "std" = [ "alloc" "pki-types/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "ring" "std" ];
+      };
+      "rustversion" = rec {
+        crateName = "rustversion";
+        version = "1.0.14";
+        edition = "2018";
+        sha256 = "1x1pz1yynk5xzzrazk2svmidj69jhz89dz5vrc28sixl20x1iz3z";
+        procMacro = true;
+        build = "build/build.rs";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "rusty-fork" = rec {
+        crateName = "rusty-fork";
+        version = "0.3.0";
+        edition = "2018";
+        sha256 = "0kxwq5c480gg6q0j3bg4zzyfh2kwmc3v2ba94jw8ncjc8mpcqgfb";
+        authors = [
+          "Jason Lingle"
+        ];
+        dependencies = [
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "quick-error";
+            packageId = "quick-error";
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+          }
+          {
+            name = "wait-timeout";
+            packageId = "wait-timeout";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "timeout" ];
+          "timeout" = [ "wait-timeout" ];
+          "wait-timeout" = [ "dep:wait-timeout" ];
+        };
+        resolvedDefaultFeatures = [ "timeout" "wait-timeout" ];
+      };
+      "rustyline" = rec {
+        crateName = "rustyline";
+        version = "10.1.1";
+        edition = "2018";
+        sha256 = "1vvsd68cch0lpcg6mcwfvfdd6r4cxbwis3bf9443phzkqcr3rs61";
+        authors = [
+          "Katsu Kawakami <kkawa1570@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "clipboard-win";
+            packageId = "clipboard-win";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "dirs-next";
+            packageId = "dirs-next";
+            optional = true;
+          }
+          {
+            name = "fd-lock";
+            packageId = "fd-lock";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "nix";
+            packageId = "nix 0.25.1";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+            features = [ "fs" "ioctl" "poll" "signal" "term" ];
+          }
+          {
+            name = "radix_trie";
+            packageId = "radix_trie";
+            optional = true;
+          }
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "unicode-segmentation";
+            packageId = "unicode-segmentation";
+          }
+          {
+            name = "unicode-width";
+            packageId = "unicode-width";
+          }
+          {
+            name = "utf8parse";
+            packageId = "utf8parse";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "consoleapi" "handleapi" "synchapi" "minwindef" "processenv" "std" "winbase" "wincon" "winuser" ];
+          }
+        ];
+        features = {
+          "case_insensitive_history_search" = [ "regex" ];
+          "custom-bindings" = [ "radix_trie" ];
+          "default" = [ "custom-bindings" "with-dirs" ];
+          "dirs-next" = [ "dep:dirs-next" ];
+          "radix_trie" = [ "dep:radix_trie" ];
+          "regex" = [ "dep:regex" ];
+          "signal-hook" = [ "dep:signal-hook" ];
+          "skim" = [ "dep:skim" ];
+          "with-dirs" = [ "dirs-next" ];
+          "with-fuzzy" = [ "skim" ];
+        };
+        resolvedDefaultFeatures = [ "custom-bindings" "default" "dirs-next" "radix_trie" "with-dirs" ];
+      };
+      "ryu" = rec {
+        crateName = "ryu";
+        version = "1.0.16";
+        edition = "2018";
+        sha256 = "0k7b90xr48ag5bzmfjp82rljasw2fx28xr3bg1lrpx7b5sljm3gr";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "same-file" = rec {
+        crateName = "same-file";
+        version = "1.0.6";
+        edition = "2018";
+        sha256 = "00h5j1w87dmhnvbv9l8bic3y7xxsnjmssvifw2ayvgx9mb1ivz4k";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-util";
+            packageId = "winapi-util";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+
+      };
+      "schannel" = rec {
+        crateName = "schannel";
+        version = "0.1.23";
+        edition = "2018";
+        sha256 = "0d1m156bsjrws6xzzr1wyfyih9i22mb2csb5pc5kmkrvci2ibjgv";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Steffen Butzer <steffen.butzer@outlook.com>"
+        ];
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            features = [ "Win32_Foundation" "Win32_Security_Cryptography" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_System_Memory" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            features = [ "Win32_System_SystemInformation" "Win32_System_Time" ];
+          }
+        ];
+
+      };
+      "scopeguard" = rec {
+        crateName = "scopeguard";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "0jcz9sd47zlsgcnm1hdw0664krxwb5gczlif4qngj2aif8vky54l";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "use_std" ];
+      };
+      "sct" = rec {
+        crateName = "sct";
+        version = "0.7.1";
+        edition = "2021";
+        sha256 = "056lmi2xkzdg1dbai6ha3n57s18cbip4pnmpdhyljli3m99n216s";
+        authors = [
+          "Joseph Birr-Pixton <jpixton@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ring";
+            packageId = "ring";
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted";
+          }
+        ];
+
+      };
+      "security-framework" = rec {
+        crateName = "security-framework";
+        version = "2.9.2";
+        edition = "2021";
+        sha256 = "1pplxk15s5yxvi2m1sz5xfmjibp96cscdcl432w9jzbk0frlzdh5";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Kornel <kornel@geekhood.net>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "core-foundation";
+            packageId = "core-foundation";
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "security-framework-sys";
+            packageId = "security-framework-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "OSX_10_10" = [ "OSX_10_9" "security-framework-sys/OSX_10_10" ];
+          "OSX_10_11" = [ "OSX_10_10" "security-framework-sys/OSX_10_11" ];
+          "OSX_10_12" = [ "OSX_10_11" "security-framework-sys/OSX_10_12" ];
+          "OSX_10_13" = [ "OSX_10_12" "security-framework-sys/OSX_10_13" "alpn" "session-tickets" "serial-number-bigint" ];
+          "OSX_10_14" = [ "OSX_10_13" "security-framework-sys/OSX_10_14" ];
+          "OSX_10_15" = [ "OSX_10_14" "security-framework-sys/OSX_10_15" ];
+          "OSX_10_9" = [ "security-framework-sys/OSX_10_9" ];
+          "default" = [ "OSX_10_9" ];
+          "log" = [ "dep:log" ];
+          "serial-number-bigint" = [ "dep:num-bigint" ];
+        };
+        resolvedDefaultFeatures = [ "OSX_10_9" "default" ];
+      };
+      "security-framework-sys" = rec {
+        crateName = "security-framework-sys";
+        version = "2.9.1";
+        edition = "2021";
+        sha256 = "0yhciwlsy9dh0ps1gw3197kvyqx1bvc4knrhiznhid6kax196cp9";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Kornel <kornel@geekhood.net>"
+        ];
+        dependencies = [
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "OSX_10_10" = [ "OSX_10_9" ];
+          "OSX_10_11" = [ "OSX_10_10" ];
+          "OSX_10_12" = [ "OSX_10_11" ];
+          "OSX_10_13" = [ "OSX_10_12" ];
+          "OSX_10_14" = [ "OSX_10_13" ];
+          "OSX_10_15" = [ "OSX_10_14" ];
+          "default" = [ "OSX_10_9" ];
+        };
+        resolvedDefaultFeatures = [ "OSX_10_9" ];
+      };
+      "semver" = rec {
+        crateName = "semver";
+        version = "1.0.21";
+        edition = "2018";
+        sha256 = "1c49snqlfcx93xym1cgwx8zcspmyyxm37xa2fyfgjx1vhalxfzmr";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "serde" = rec {
+        crateName = "serde";
+        version = "1.0.197";
+        edition = "2018";
+        sha256 = "1qjcxqd3p4yh5cmmax9q4ics1zy34j5ij32cvjj5dc5rw5rwic9z";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            optional = true;
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "derive" "rc" "serde_derive" "std" ];
+      };
+      "serde_derive" = rec {
+        crateName = "serde_derive";
+        version = "1.0.197";
+        edition = "2015";
+        sha256 = "02v1x0sdv8qy06lpr6by4ar1n3jz3hmab15cgimpzhgd895v7c3y";
+        procMacro = true;
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            usesDefaultFeatures = false;
+            features = [ "clone-impls" "derive" "parsing" "printing" "proc-macro" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "serde_json" = rec {
+        crateName = "serde_json";
+        version = "1.0.111";
+        edition = "2021";
+        sha256 = "1x441azvvdy6x8am4bvkxhswhzw5cr8ml0cqspnihvri8bx4cvhp";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" ];
+          "default" = [ "std" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "preserve_order" = [ "indexmap" "std" ];
+          "std" = [ "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "raw_value" "std" ];
+      };
+      "serde_path_to_error" = rec {
+        crateName = "serde_path_to_error";
+        version = "0.1.16";
+        edition = "2021";
+        sha256 = "19hlz2359l37ifirskpcds7sxg0gzpqvfilibs7whdys0128i6dg";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+        ];
+
+      };
+      "serde_qs" = rec {
+        crateName = "serde_qs";
+        version = "0.12.0";
+        edition = "2018";
+        sha256 = "031kgpxbqkkxnql0k7sd80lyp98x7jc92311chrkc7k5d1as6c84";
+        authors = [
+          "Sam Scott <sam@osohq.com>"
+        ];
+        dependencies = [
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        features = {
+          "actix-web2" = [ "dep:actix-web2" ];
+          "actix-web3" = [ "dep:actix-web3" ];
+          "actix-web4" = [ "dep:actix-web4" ];
+          "actix2" = [ "actix-web2" "futures" ];
+          "actix3" = [ "actix-web3" "futures" ];
+          "actix4" = [ "actix-web4" "futures" ];
+          "axum" = [ "axum-framework" "futures" ];
+          "axum-framework" = [ "dep:axum-framework" ];
+          "futures" = [ "dep:futures" ];
+          "tracing" = [ "dep:tracing" ];
+          "warp" = [ "futures" "tracing" "warp-framework" ];
+          "warp-framework" = [ "dep:warp-framework" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "serde_spanned" = rec {
+        crateName = "serde_spanned";
+        version = "0.6.5";
+        edition = "2021";
+        sha256 = "1hgh6s3jjwyzhfk3xwb6pnnr1misq9nflwq0f026jafi37s24dpb";
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "serde" ];
+      };
+      "serde_urlencoded" = rec {
+        crateName = "serde_urlencoded";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1zgklbdaysj3230xivihs30qi5vkhigg323a9m62k8jwf4a1qjfk";
+        authors = [
+          "Anthony Ramine <n.oxyde@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "form_urlencoded";
+            packageId = "form_urlencoded";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+        ];
+
+      };
+      "serde_with" = rec {
+        crateName = "serde_with";
+        version = "3.7.0";
+        edition = "2021";
+        sha256 = "16jn72cij27fxjafcsma1z5p587xkk8wqhp2yv98zy5vc7iv107f";
+        authors = [
+          "Jonas Bushart"
+          "Marcin KaΕΊmierczak"
+        ];
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            rename = "chrono_0_4";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "serde" ];
+          }
+          {
+            name = "hex";
+            packageId = "hex";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap 1.9.3";
+            rename = "indexmap_1";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "serde-1" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap 2.1.0";
+            rename = "indexmap_2";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "serde" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde_with_macros";
+            packageId = "serde_with_macros";
+            optional = true;
+          }
+          {
+            name = "time";
+            packageId = "time";
+            rename = "time_0_3";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            features = [ "preserve_order" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" "base64?/alloc" "chrono_0_4?/alloc" "hex?/alloc" "serde_json?/alloc" "time_0_3?/alloc" ];
+          "base64" = [ "dep:base64" "alloc" ];
+          "chrono" = [ "chrono_0_4" ];
+          "chrono_0_4" = [ "dep:chrono_0_4" ];
+          "default" = [ "std" "macros" ];
+          "guide" = [ "dep:doc-comment" "dep:document-features" "macros" "std" ];
+          "hashbrown_0_14" = [ "dep:hashbrown_0_14" "alloc" ];
+          "hex" = [ "dep:hex" "alloc" ];
+          "indexmap" = [ "indexmap_1" ];
+          "indexmap_1" = [ "dep:indexmap_1" "alloc" ];
+          "indexmap_2" = [ "dep:indexmap_2" "alloc" ];
+          "json" = [ "dep:serde_json" "alloc" ];
+          "macros" = [ "dep:serde_with_macros" ];
+          "schemars_0_8" = [ "dep:schemars_0_8" "std" "serde_with_macros?/schemars_0_8" ];
+          "std" = [ "alloc" "serde/std" "chrono_0_4?/clock" "chrono_0_4?/std" "indexmap_1?/std" "indexmap_2?/std" "time_0_3?/serde-well-known" "time_0_3?/std" ];
+          "time_0_3" = [ "dep:time_0_3" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "base64" "default" "macros" "std" ];
+      };
+      "serde_with_macros" = rec {
+        crateName = "serde_with_macros";
+        version = "3.7.0";
+        edition = "2021";
+        sha256 = "0mbnika5bw1mvgnl50rs7wfzj7dwxzgwqxnq6656694j38bdqqb5";
+        procMacro = true;
+        authors = [
+          "Jonas Bushart"
+        ];
+        dependencies = [
+          {
+            name = "darling";
+            packageId = "darling";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "extra-traits" "full" "parsing" ];
+          }
+        ];
+        features = { };
+      };
+      "sha1" = rec {
+        crateName = "sha1";
+        version = "0.10.6";
+        edition = "2018";
+        sha256 = "1fnnxlfg08xhkmwf2ahv634as30l1i3xhlhkvxflmasi5nd85gz3";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86" == target."arch" or null) || ("x86_64" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha1-asm" ];
+          "default" = [ "std" ];
+          "oid" = [ "digest/oid" ];
+          "sha1-asm" = [ "dep:sha1-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sha2" = rec {
+        crateName = "sha2";
+        version = "0.10.8";
+        edition = "2018";
+        sha256 = "1j1x78zk9il95w9iv46dh9wm73r6xrgj32y6lzzw7bxws9dbfgbr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha2-asm" ];
+          "asm-aarch64" = [ "asm" ];
+          "default" = [ "std" ];
+          "oid" = [ "digest/oid" ];
+          "sha2-asm" = [ "dep:sha2-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sharded-slab" = rec {
+        crateName = "sharded-slab";
+        version = "0.1.7";
+        edition = "2018";
+        sha256 = "1xipjr4nqsgw34k7a2cgj9zaasl2ds6jwn89886kww93d32a637l";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+        ];
+        dependencies = [
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+        ];
+        features = {
+          "loom" = [ "dep:loom" ];
+        };
+      };
+      "signal-hook-registry" = rec {
+        crateName = "signal-hook-registry";
+        version = "1.4.1";
+        edition = "2015";
+        sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        authors = [
+          "Michal 'vorner' Vaner <vorner@vorner.cz>"
+          "Masaki Hara <ackie.h.gmai@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "signature" = rec {
+        crateName = "signature";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "1pi9hd5vqfr3q3k49k37z06p7gs5si0in32qia4mmr1dancr6m3p";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "derive" = [ "dep:derive" ];
+          "digest" = [ "dep:digest" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "std" = [ "alloc" "rand_core?/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "slab" = rec {
+        crateName = "slab";
+        version = "0.4.9";
+        edition = "2018";
+        sha256 = "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sled" = rec {
+        crateName = "sled";
+        version = "0.34.7";
+        edition = "2018";
+        sha256 = "0dcr2s7cylj5mb33ci3kpx7fz797jwvysnl5airrir9cgirv95kz";
+        authors = [
+          "Tyler Neely <t@jujit.su>"
+        ];
+        dependencies = [
+          {
+            name = "crc32fast";
+            packageId = "crc32fast";
+          }
+          {
+            name = "crossbeam-epoch";
+            packageId = "crossbeam-epoch";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+          {
+            name = "fs2";
+            packageId = "fs2";
+            target = { target, features }: (("linux" == target."os" or null) || ("macos" == target."os" or null) || ("windows" == target."os" or null));
+          }
+          {
+            name = "fxhash";
+            packageId = "fxhash";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot 0.11.2";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "color-backtrace" = [ "dep:color-backtrace" ];
+          "compression" = [ "zstd" ];
+          "default" = [ "no_metrics" ];
+          "io_uring" = [ "rio" ];
+          "no_logs" = [ "log/max_level_off" ];
+          "pretty_backtrace" = [ "color-backtrace" ];
+          "rio" = [ "dep:rio" ];
+          "testing" = [ "event_log" "lock_free_delays" "compression" "failpoints" "backtrace" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "default" "no_metrics" ];
+      };
+      "smallvec" = rec {
+        crateName = "smallvec";
+        version = "1.13.1";
+        edition = "2018";
+        sha256 = "1mzk9j117pn3k1gabys0b7nz8cdjsx5xc6q7fwnm8r0an62d7v76";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "const_new" = [ "const_generics" ];
+          "drain_keep_rest" = [ "drain_filter" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "const_generics" "const_new" ];
+      };
+      "smol_str" = rec {
+        crateName = "smol_str";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "0jca0hyrwnv428q5gxhn2s8jsvrrkyrb0fyla9x37056mmimb176";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "serde?/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "snafu" = rec {
+        crateName = "snafu";
+        version = "0.7.5";
+        edition = "2018";
+        sha256 = "1mj2j2gfbf8mm1hr02zrbrqrh2zp01f61xgkx0lpln2w0ankgpp4";
+        authors = [
+          "Jake Goulding <jake.goulding@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "doc-comment";
+            packageId = "doc-comment";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "snafu-derive";
+            packageId = "snafu-derive";
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "backtraces" = [ "std" "backtrace" ];
+          "backtraces-impl-backtrace-crate" = [ "backtraces" ];
+          "default" = [ "std" "rust_1_46" ];
+          "futures" = [ "futures-core-crate" "pin-project" ];
+          "futures-core-crate" = [ "dep:futures-core-crate" ];
+          "futures-crate" = [ "dep:futures-crate" ];
+          "internal-dev-dependencies" = [ "futures-crate" ];
+          "pin-project" = [ "dep:pin-project" ];
+          "rust_1_39" = [ "snafu-derive/rust_1_39" ];
+          "rust_1_46" = [ "rust_1_39" "snafu-derive/rust_1_46" ];
+          "rust_1_61" = [ "rust_1_46" "snafu-derive/rust_1_61" ];
+          "unstable-backtraces-impl-std" = [ "backtraces-impl-std" "snafu-derive/unstable-backtraces-impl-std" ];
+          "unstable-provider-api" = [ "snafu-derive/unstable-provider-api" ];
+        };
+        resolvedDefaultFeatures = [ "default" "rust_1_39" "rust_1_46" "std" ];
+      };
+      "snafu-derive" = rec {
+        crateName = "snafu-derive";
+        version = "0.7.5";
+        edition = "2018";
+        sha256 = "1gzy9rzggs090zf7hfvgp4lm1glrmg9qzh796686jnq7bxk7j04r";
+        procMacro = true;
+        authors = [
+          "Jake Goulding <jake.goulding@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "full" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "rust_1_39" "rust_1_46" ];
+      };
+      "socket2" = rec {
+        crateName = "socket2";
+        version = "0.5.5";
+        edition = "2021";
+        sha256 = "1sgq315f1njky114ip7wcy83qlphv9qclprfjwvxcpfblmcsqpvv";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "all" ];
+      };
+      "spin" = rec {
+        crateName = "spin";
+        version = "0.9.8";
+        edition = "2015";
+        sha256 = "0rvam5r0p3a6qhc18scqpvpgb3ckzyqxpgdfyjnghh8ja7byi039";
+        authors = [
+          "Mathijs van de Nes <git@mathijs.vd-nes.nl>"
+          "John Ericson <git@JohnEricson.me>"
+          "Joshua Barretto <joshua.s.barretto@gmail.com>"
+        ];
+        features = {
+          "barrier" = [ "mutex" ];
+          "default" = [ "lock_api" "mutex" "spin_mutex" "rwlock" "once" "lazy" "barrier" ];
+          "fair_mutex" = [ "mutex" ];
+          "lazy" = [ "once" ];
+          "lock_api" = [ "lock_api_crate" ];
+          "lock_api_crate" = [ "dep:lock_api_crate" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "portable_atomic" = [ "portable-atomic" ];
+          "spin_mutex" = [ "mutex" ];
+          "ticket_mutex" = [ "mutex" ];
+          "use_ticket_mutex" = [ "mutex" "ticket_mutex" ];
+        };
+        resolvedDefaultFeatures = [ "once" ];
+      };
+      "spki" = rec {
+        crateName = "spki";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "17fj8k5fmx4w9mp27l970clrh5qa7r5sjdvbsln987xhb34dc7nr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "base64ct";
+            packageId = "base64ct";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "base64ct?/alloc" "der/alloc" ];
+          "arbitrary" = [ "std" "dep:arbitrary" "der/arbitrary" ];
+          "base64" = [ "dep:base64ct" ];
+          "fingerprint" = [ "sha2" ];
+          "pem" = [ "alloc" "der/pem" ];
+          "sha2" = [ "dep:sha2" ];
+          "std" = [ "der/std" "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "static_assertions" = rec {
+        crateName = "static_assertions";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "0gsl6xmw10gvn3zs1rv99laj5ig7ylffnh71f9l34js4nr4r7sx2";
+        authors = [
+          "Nikolai Vazquez"
+        ];
+        features = { };
+      };
+      "str-buf" = rec {
+        crateName = "str-buf";
+        version = "1.0.6";
+        edition = "2018";
+        sha256 = "1l7q4nha7wpsr0970bfqm773vhmpwr9l6rr8r4gwgrh46wvdh24y";
+        authors = [
+          "Douman <douman@gmx.se>"
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "strsim" = rec {
+        crateName = "strsim";
+        version = "0.10.0";
+        edition = "2015";
+        sha256 = "08s69r4rcrahwnickvi0kq49z524ci50capybln83mg6b473qivk";
+        authors = [
+          "Danny Guo <danny@dannyguo.com>"
+        ];
+
+      };
+      "structmeta" = rec {
+        crateName = "structmeta";
+        version = "0.1.6";
+        edition = "2021";
+        sha256 = "0alyl12b7fab8izrpliil73sxs1ivr5vm0pisallmxlb4zb44j0h";
+        authors = [
+          "frozenlib"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "structmeta-derive";
+            packageId = "structmeta-derive";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "extra-traits" "full" ];
+          }
+        ];
+
+      };
+      "structmeta-derive" = rec {
+        crateName = "structmeta-derive";
+        version = "0.1.6";
+        edition = "2021";
+        sha256 = "14vxik2m3dm7bwx016qfz062fwznkbq02fyq8vby545m0pj0nhi4";
+        procMacro = true;
+        authors = [
+          "frozenlib"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "extra-traits" ];
+          }
+        ];
+
+      };
+      "subtle" = rec {
+        crateName = "subtle";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "1g2yjs7gffgmdvkkq0wrrh0pxds3q0dv6dhkw9cdpbib656xdkc1";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        features = {
+          "default" = [ "std" "i128" ];
+        };
+      };
+      "syn 1.0.109" = rec {
+        crateName = "syn";
+        version = "1.0.109";
+        edition = "2018";
+        sha256 = "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit" "visit-mut" ];
+      };
+      "syn 2.0.48" = rec {
+        crateName = "syn";
+        version = "2.0.48";
+        edition = "2021";
+        sha256 = "0gqgfygmrxmp8q32lia9p294kdd501ybn6kn2h4gqza0irik2d8g";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit" "visit-mut" ];
+      };
+      "sync_wrapper" = rec {
+        crateName = "sync_wrapper";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "0q01lyj0gr9a93n10nxsn8lwbzq97jqd6b768x17c8f7v7gccir0";
+        authors = [
+          "Actyx AG <developer@actyx.io>"
+        ];
+        features = {
+          "futures" = [ "futures-core" ];
+          "futures-core" = [ "dep:futures-core" ];
+        };
+      };
+      "system-configuration" = rec {
+        crateName = "system-configuration";
+        version = "0.5.1";
+        edition = "2021";
+        sha256 = "1rz0r30xn7fiyqay2dvzfy56cvaa3km74hnbz2d72p97bkf3lfms";
+        authors = [
+          "Mullvad VPN"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "core-foundation";
+            packageId = "core-foundation";
+          }
+          {
+            name = "system-configuration-sys";
+            packageId = "system-configuration-sys";
+          }
+        ];
+
+      };
+      "system-configuration-sys" = rec {
+        crateName = "system-configuration-sys";
+        version = "0.5.0";
+        edition = "2021";
+        sha256 = "1jckxvdr37bay3i9v52izgy52dg690x5xfg3hd394sv2xf4b2px7";
+        authors = [
+          "Mullvad VPN"
+        ];
+        dependencies = [
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "tabwriter" = rec {
+        crateName = "tabwriter";
+        version = "1.4.0";
+        edition = "2021";
+        sha256 = "1xp5j7v8jsk92zcmiyh4ya9akhrchjvc595vwcvxrxk49wn2h9x3";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "unicode-width";
+            packageId = "unicode-width";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "tempfile" = rec {
+        crateName = "tempfile";
+        version = "3.9.0";
+        edition = "2018";
+        sha256 = "1ypkl7rvv57n16q28psxpb61rnyhmfaif12ascdnsyljm90l3kh1";
+        authors = [
+          "Steven Allen <steven@stebalien.com>"
+          "The Rust Project Developers"
+          "Ashley Mannix <ashleymannix@live.com.au>"
+          "Jason White <me@jasonwhite.io>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "fastrand";
+            packageId = "fastrand";
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.4.1";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            target = { target, features }: ((target."unix" or false) || ("wasi" == target."os" or null));
+            features = [ "fs" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Storage_FileSystem" "Win32_Foundation" ];
+          }
+        ];
+        features = { };
+      };
+      "termcolor" = rec {
+        crateName = "termcolor";
+        version = "1.4.1";
+        edition = "2018";
+        sha256 = "0mappjh3fj3p2nmrg4y7qv94rchwi9mzmgmfflr8p2awdj7lyy86";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-util";
+            packageId = "winapi-util";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+
+      };
+      "test-strategy" = rec {
+        crateName = "test-strategy";
+        version = "0.2.1";
+        edition = "2021";
+        sha256 = "105lxqs0vnqff5821sgns8q1scvrwfx1yw6iz7i7nr862j6l1mk2";
+        procMacro = true;
+        authors = [
+          "frozenlib"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "structmeta";
+            packageId = "structmeta";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "visit" "full" ];
+          }
+        ];
+
+      };
+      "text-size" = rec {
+        crateName = "text-size";
+        version = "1.1.1";
+        edition = "2018";
+        sha256 = "0cwjbkl7w3xc8mnkhg1nwij6p5y2qkcfldgss8ddnawvhf3s32pi";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+          "Christopher Durham (CAD97) <cad97@cad97.com>"
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "thiserror" = rec {
+        crateName = "thiserror";
+        version = "1.0.56";
+        edition = "2021";
+        sha256 = "1b9hnzngjan4d89zjs16i01bcpcnvdwklyh73lj16xk28p37hhym";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "thiserror-impl";
+            packageId = "thiserror-impl";
+          }
+        ];
+
+      };
+      "thiserror-impl" = rec {
+        crateName = "thiserror-impl";
+        version = "1.0.56";
+        edition = "2021";
+        sha256 = "0w9ldp8fa574ilz4dn7y7scpcq66vdjy59qal8qdpwsh7faal3zs";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+          }
+        ];
+
+      };
+      "thread_local" = rec {
+        crateName = "thread_local";
+        version = "1.1.7";
+        edition = "2021";
+        sha256 = "0lp19jdgvp5m4l60cgxdnl00yw1hlqy8gcywg9bddwng9h36zp9z";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+        ];
+        features = { };
+      };
+      "time" = rec {
+        crateName = "time";
+        version = "0.3.34";
+        edition = "2021";
+        sha256 = "0jc7wgprzqjhzd0nqkbmdlnjwyddnswmjw86ni2vq55v45jqn968";
+        authors = [
+          "Jacob Pratt <open-source@jhpratt.dev>"
+          "Time contributors"
+        ];
+        dependencies = [
+          {
+            name = "deranged";
+            packageId = "deranged";
+            usesDefaultFeatures = false;
+            features = [ "powerfmt" ];
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+            optional = true;
+          }
+          {
+            name = "num-conv";
+            packageId = "num-conv";
+          }
+          {
+            name = "powerfmt";
+            packageId = "powerfmt";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "time-core";
+            packageId = "time-core";
+          }
+          {
+            name = "time-macros";
+            packageId = "time-macros";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "num-conv";
+            packageId = "num-conv";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+            features = [ "derive" ];
+          }
+          {
+            name = "time-macros";
+            packageId = "time-macros";
+          }
+        ];
+        features = {
+          "alloc" = [ "serde?/alloc" ];
+          "default" = [ "std" ];
+          "formatting" = [ "dep:itoa" "std" "time-macros?/formatting" ];
+          "large-dates" = [ "time-macros?/large-dates" ];
+          "local-offset" = [ "std" "dep:libc" "dep:num_threads" ];
+          "macros" = [ "dep:time-macros" ];
+          "parsing" = [ "time-macros?/parsing" ];
+          "quickcheck" = [ "dep:quickcheck" "alloc" "deranged/quickcheck" ];
+          "rand" = [ "dep:rand" "deranged/rand" ];
+          "serde" = [ "dep:serde" "time-macros?/serde" "deranged/serde" ];
+          "serde-human-readable" = [ "serde" "formatting" "parsing" ];
+          "serde-well-known" = [ "serde" "formatting" "parsing" ];
+          "std" = [ "alloc" "deranged/std" ];
+          "wasm-bindgen" = [ "dep:js-sys" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "formatting" "parsing" "serde" "serde-well-known" "std" ];
+      };
+      "time-core" = rec {
+        crateName = "time-core";
+        version = "0.1.2";
+        edition = "2021";
+        sha256 = "1wx3qizcihw6z151hywfzzyd1y5dl804ydyxci6qm07vbakpr4pg";
+        authors = [
+          "Jacob Pratt <open-source@jhpratt.dev>"
+          "Time contributors"
+        ];
+
+      };
+      "time-macros" = rec {
+        crateName = "time-macros";
+        version = "0.2.17";
+        edition = "2021";
+        sha256 = "0x3pahhk2751c6kqqq9dk6lz0gydbnxr44q01wpjlrz687ps78vv";
+        procMacro = true;
+        authors = [
+          "Jacob Pratt <open-source@jhpratt.dev>"
+          "Time contributors"
+        ];
+        dependencies = [
+          {
+            name = "num-conv";
+            packageId = "num-conv";
+          }
+          {
+            name = "time-core";
+            packageId = "time-core";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "formatting" "parsing" "serde" ];
+      };
+      "tinytemplate" = rec {
+        crateName = "tinytemplate";
+        version = "1.2.1";
+        edition = "2015";
+        sha256 = "1g5n77cqkdh9hy75zdb01adxn45mkh9y40wdr7l68xpz35gnnkdy";
+        authors = [
+          "Brook Heisler <brookheisler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+        ];
+
+      };
+      "tinyvec" = rec {
+        crateName = "tinyvec";
+        version = "1.6.0";
+        edition = "2018";
+        sha256 = "0l6bl2h62a5m44jdnpn7lmj14rd44via8180i7121fvm73mmrk47";
+        authors = [
+          "Lokathor <zefria@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "tinyvec_macros";
+            packageId = "tinyvec_macros";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc" = [ "tinyvec_macros" ];
+          "arbitrary" = [ "dep:arbitrary" ];
+          "real_blackbox" = [ "criterion/real_blackbox" ];
+          "rustc_1_55" = [ "rustc_1_40" ];
+          "rustc_1_57" = [ "rustc_1_55" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "tinyvec_macros" = [ "dep:tinyvec_macros" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "tinyvec_macros" ];
+      };
+      "tinyvec_macros" = rec {
+        crateName = "tinyvec_macros";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "081gag86208sc3y6sdkshgw3vysm5d34p431dzw0bshz66ncng0z";
+        authors = [
+          "Soveu <marx.tomasz@gmail.com>"
+        ];
+
+      };
+      "tokio" = rec {
+        crateName = "tokio";
+        version = "1.35.1";
+        edition = "2021";
+        sha256 = "01613rkziqp812a288ga65aqygs254wgajdi57v8brivjkx4x6y8";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            target = { target, features }: (target."tokio_taskdump" or false);
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "signal-hook-registry";
+            packageId = "signal-hook-registry";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+            features = [ "all" ];
+          }
+          {
+            name = "tokio-macros";
+            packageId = "tokio-macros";
+            optional = true;
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Security_Authorization" ];
+          }
+        ];
+        features = {
+          "bytes" = [ "dep:bytes" ];
+          "full" = [ "fs" "io-util" "io-std" "macros" "net" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "sync" "time" ];
+          "io-util" = [ "bytes" ];
+          "libc" = [ "dep:libc" ];
+          "macros" = [ "tokio-macros" ];
+          "mio" = [ "dep:mio" ];
+          "net" = [ "libc" "mio/os-poll" "mio/os-ext" "mio/net" "socket2" "windows-sys/Win32_Foundation" "windows-sys/Win32_Security" "windows-sys/Win32_Storage_FileSystem" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_System_SystemServices" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "process" = [ "bytes" "libc" "mio/os-poll" "mio/os-ext" "mio/net" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Threading" "windows-sys/Win32_System_WindowsProgramming" ];
+          "rt-multi-thread" = [ "num_cpus" "rt" ];
+          "signal" = [ "libc" "mio/os-poll" "mio/net" "mio/os-ext" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Console" ];
+          "signal-hook-registry" = [ "dep:signal-hook-registry" ];
+          "socket2" = [ "dep:socket2" ];
+          "test-util" = [ "rt" "sync" "time" ];
+          "tokio-macros" = [ "dep:tokio-macros" ];
+          "tracing" = [ "dep:tracing" ];
+          "windows-sys" = [ "dep:windows-sys" ];
+        };
+        resolvedDefaultFeatures = [ "bytes" "default" "fs" "io-std" "io-util" "libc" "macros" "mio" "net" "num_cpus" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "test-util" "time" "tokio-macros" "windows-sys" ];
+      };
+      "tokio-io-timeout" = rec {
+        crateName = "tokio-io-timeout";
+        version = "1.2.0";
+        edition = "2018";
+        sha256 = "1gx84f92q1491vj4pkn81j8pz1s3pgwnbrsdhfsa2556mli41drh";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "time" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-listener" = rec {
+        crateName = "tokio-listener";
+        version = "0.3.2";
+        edition = "2021";
+        sha256 = "00vkr1cywd2agn8jbkzwwf7y4ps3cfjm8l9ab697px2cgc97wdln";
+        dependencies = [
+          {
+            name = "axum";
+            packageId = "axum 0.7.4";
+            rename = "axum07";
+          }
+          {
+            name = "document-features";
+            packageId = "document-features";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+          }
+          {
+            name = "nix";
+            packageId = "nix 0.26.4";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+            features = [ "user" "fs" ];
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            features = [ "all" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "net" "io-std" "time" "sync" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            optional = true;
+            features = [ "net" "codec" ];
+          }
+          {
+            name = "tonic";
+            packageId = "tonic 0.11.0";
+            rename = "tonic";
+            optional = true;
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "rt" "io-util" ];
+          }
+        ];
+        features = {
+          "axum07" = [ "dep:hyper1" "dep:hyper-util" "dep:futures-util" "dep:tower-service" "dep:tower" ];
+          "clap" = [ "dep:clap" ];
+          "default" = [ "user_facing_default" "tokio-util" ];
+          "hyper014" = [ "dep:hyper014" ];
+          "inetd" = [ "dep:futures-util" ];
+          "nix" = [ "dep:nix" ];
+          "serde" = [ "dep:serde" "serde_with" ];
+          "serde_with" = [ "dep:serde_with" ];
+          "socket2" = [ "dep:socket2" ];
+          "socket_options" = [ "socket2" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "tonic010" = [ "dep:tonic_010" ];
+          "tonic011" = [ "dep:tonic" ];
+          "unix_path_tools" = [ "nix" ];
+          "user_facing_default" = [ "inetd" "unix" "unix_path_tools" "sd_listen" "socket_options" ];
+        };
+        resolvedDefaultFeatures = [ "default" "inetd" "nix" "sd_listen" "socket2" "socket_options" "tokio-util" "tonic011" "unix" "unix_path_tools" "user_facing_default" ];
+      };
+      "tokio-macros" = rec {
+        crateName = "tokio-macros";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-retry" = rec {
+        crateName = "tokio-retry";
+        version = "0.3.0";
+        edition = "2018";
+        sha256 = "0kr1hnm5dmb9gfkby88yg2xj8g6x4i4gipva0c8ca3xyxhvfnmvz";
+        authors = [
+          "Sam Rijs <srijs@airpost.net>"
+        ];
+        dependencies = [
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "time" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-rustls 0.24.1" = rec {
+        crateName = "tokio-rustls";
+        version = "0.24.1";
+        edition = "2018";
+        sha256 = "10bhibg57mqir7xjhb2xmf24xgfpx6fzpyw720a4ih8a737jg0y2";
+        dependencies = [
+          {
+            name = "rustls";
+            packageId = "rustls 0.21.10";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "dangerous_configuration" = [ "rustls/dangerous_configuration" ];
+          "default" = [ "logging" "tls12" ];
+          "logging" = [ "rustls/logging" ];
+          "secret_extraction" = [ "rustls/secret_extraction" ];
+          "tls12" = [ "rustls/tls12" ];
+        };
+        resolvedDefaultFeatures = [ "default" "logging" "tls12" ];
+      };
+      "tokio-rustls 0.25.0" = rec {
+        crateName = "tokio-rustls";
+        version = "0.25.0";
+        edition = "2021";
+        sha256 = "03w6d5aqqf084rmcmrsyq5grhydl53blaiqcl0i2yfnv187hqpkp";
+        dependencies = [
+          {
+            name = "rustls";
+            packageId = "rustls 0.22.2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rustls-pki-types";
+            packageId = "rustls-pki-types";
+            rename = "pki-types";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "default" = [ "logging" "tls12" "ring" ];
+          "logging" = [ "rustls/logging" ];
+          "ring" = [ "rustls/ring" ];
+          "tls12" = [ "rustls/tls12" ];
+        };
+        resolvedDefaultFeatures = [ "default" "logging" "ring" "tls12" ];
+      };
+      "tokio-stream" = rec {
+        crateName = "tokio-stream";
+        version = "0.1.14";
+        edition = "2021";
+        sha256 = "0hi8hcwavh5sdi1ivc9qc4yvyr32f153c212dpd7sb366y6rhz1r";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" "test-util" ];
+          }
+        ];
+        features = {
+          "default" = [ "time" ];
+          "fs" = [ "tokio/fs" ];
+          "full" = [ "time" "net" "io-util" "fs" "sync" "signal" ];
+          "io-util" = [ "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "signal" = [ "tokio/signal" ];
+          "sync" = [ "tokio/sync" "tokio-util" ];
+          "time" = [ "tokio/time" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+        };
+        resolvedDefaultFeatures = [ "default" "fs" "net" "time" ];
+      };
+      "tokio-tar" = rec {
+        crateName = "tokio-tar";
+        version = "0.3.1";
+        edition = "2018";
+        sha256 = "0xffvap4g7hlswk5daklk3jaqha6s6wxw72c24kmqgna23018mwx";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "dignifiedquire <me@dignifiequire.com>"
+          "Artem Vorotnikov <artem@vorotnikov.me>"
+          "Aiden McClelland <me@drbonez.dev>"
+        ];
+        dependencies = [
+          {
+            name = "filetime";
+            packageId = "filetime";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.3.5";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "io-util" "rt" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+          }
+          {
+            name = "xattr";
+            packageId = "xattr";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "default" = [ "xattr" ];
+          "xattr" = [ "dep:xattr" ];
+        };
+        resolvedDefaultFeatures = [ "default" "xattr" ];
+      };
+      "tokio-test" = rec {
+        crateName = "tokio-test";
+        version = "0.4.3";
+        edition = "2021";
+        sha256 = "06fplzcc2ymahfzykd2ickw2qn7g3lz47bll00865s1spnx3r6z8";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt" "sync" "time" "test-util" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-util" = rec {
+        crateName = "tokio-util";
+        version = "0.7.10";
+        edition = "2021";
+        sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            optional = true;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "__docs_rs" = [ "futures-util" ];
+          "codec" = [ "tracing" ];
+          "compat" = [ "futures-io" ];
+          "full" = [ "codec" "compat" "io-util" "time" "net" "rt" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hashbrown" = [ "dep:hashbrown" ];
+          "io-util" = [ "io" "tokio/rt" "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "rt" = [ "tokio/rt" "tokio/sync" "futures-util" "hashbrown" ];
+          "slab" = [ "dep:slab" ];
+          "time" = [ "tokio/time" "slab" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "codec" "compat" "default" "futures-io" "io" "io-util" "net" "tracing" ];
+      };
+      "toml" = rec {
+        crateName = "toml";
+        version = "0.6.0";
+        edition = "2021";
+        sha256 = "05zjz69wjymp9yrgccg5vhvxpf855rgn23vl1yvri4nwwj8difag";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_spanned";
+            packageId = "serde_spanned";
+            features = [ "serde" ];
+          }
+          {
+            name = "toml_datetime";
+            packageId = "toml_datetime";
+            features = [ "serde" ];
+          }
+          {
+            name = "toml_edit";
+            packageId = "toml_edit";
+            optional = true;
+            features = [ "serde" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "default" = [ "parse" "display" ];
+          "display" = [ "dep:toml_edit" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "parse" = [ "dep:toml_edit" ];
+          "preserve_order" = [ "indexmap" ];
+        };
+        resolvedDefaultFeatures = [ "default" "display" "parse" ];
+      };
+      "toml_datetime" = rec {
+        crateName = "toml_datetime";
+        version = "0.5.1";
+        edition = "2021";
+        sha256 = "1xcw3kyklh3s2gxp65ma26rgkl7505la4xx1r55kfgcfmikz8ls5";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "serde" ];
+      };
+      "toml_edit" = rec {
+        crateName = "toml_edit";
+        version = "0.18.1";
+        edition = "2021";
+        sha256 = "0ax1bwzd4xclpids3b69nd1nxqi3x3qa4ymz51jbrp6hsy6rvian";
+        authors = [
+          "Andronik Ordian <write@reusable.software>"
+          "Ed Page <eopage@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "indexmap";
+            packageId = "indexmap 1.9.3";
+          }
+          {
+            name = "nom8";
+            packageId = "nom8";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+          }
+          {
+            name = "serde_spanned";
+            packageId = "serde_spanned";
+            optional = true;
+            features = [ "serde" ];
+          }
+          {
+            name = "toml_datetime";
+            packageId = "toml_datetime";
+          }
+        ];
+        features = {
+          "easy" = [ "serde" ];
+          "perf" = [ "dep:kstring" ];
+          "serde" = [ "dep:serde" "toml_datetime/serde" "dep:serde_spanned" ];
+        };
+        resolvedDefaultFeatures = [ "default" "serde" ];
+      };
+      "tonic 0.11.0" = rec {
+        crateName = "tonic";
+        version = "0.11.0";
+        edition = "2021";
+        sha256 = "04qsr527i256i3dk9dp1g2jr42q7yl91y5h06rvd9ycy9rxfpi3n";
+        authors = [
+          "Lucio Franco <luciofranco14@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+            optional = true;
+          }
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+            optional = true;
+          }
+          {
+            name = "axum";
+            packageId = "axum 0.6.20";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "base64";
+            packageId = "base64";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "h2";
+            packageId = "h2 0.3.24";
+            optional = true;
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 0.4.6";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            optional = true;
+            features = [ "full" ];
+          }
+          {
+            name = "hyper-timeout";
+            packageId = "hyper-timeout";
+            optional = true;
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "rustls-native-certs";
+            packageId = "rustls-native-certs 0.7.0";
+            optional = true;
+          }
+          {
+            name = "rustls-pemfile";
+            packageId = "rustls-pemfile 2.1.0";
+            optional = true;
+          }
+          {
+            name = "rustls-pki-types";
+            packageId = "rustls-pki-types";
+            optional = true;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "tokio-rustls";
+            packageId = "tokio-rustls 0.25.0";
+            optional = true;
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "balance" "buffer" "discover" "limit" "load" "make" "timeout" "util" ];
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt" "macros" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "codegen" = [ "dep:async-trait" ];
+          "default" = [ "transport" "codegen" "prost" ];
+          "gzip" = [ "dep:flate2" ];
+          "prost" = [ "dep:prost" ];
+          "tls" = [ "dep:rustls-pki-types" "dep:rustls-pemfile" "transport" "dep:tokio-rustls" "tokio/rt" "tokio/macros" ];
+          "tls-roots" = [ "tls-roots-common" "dep:rustls-native-certs" ];
+          "tls-roots-common" = [ "tls" ];
+          "tls-webpki-roots" = [ "tls-roots-common" "dep:webpki-roots" ];
+          "transport" = [ "dep:async-stream" "dep:axum" "channel" "dep:h2" "dep:hyper" "tokio/net" "tokio/time" "dep:tower" "dep:hyper-timeout" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "channel" "codegen" "default" "prost" "tls" "tls-roots" "tls-roots-common" "transport" ];
+      };
+      "tonic 0.9.2" = rec {
+        crateName = "tonic";
+        version = "0.9.2";
+        edition = "2021";
+        sha256 = "0nlx35lvah5hdcp6lg1d6dlprq0zz8ijj6f727szfcv479m6d0ih";
+        authors = [
+          "Lucio Franco <luciofranco14@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+            optional = true;
+          }
+          {
+            name = "axum";
+            packageId = "axum 0.6.20";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "base64";
+            packageId = "base64";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "h2";
+            packageId = "h2 0.3.24";
+            optional = true;
+          }
+          {
+            name = "http";
+            packageId = "http 0.2.11";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body 0.4.6";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper 0.14.28";
+            optional = true;
+            features = [ "full" ];
+          }
+          {
+            name = "hyper-timeout";
+            packageId = "hyper-timeout";
+            optional = true;
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.11.9";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "net" "time" "macros" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "balance" "buffer" "discover" "limit" "load" "make" "timeout" "util" ];
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt" "macros" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "channel" = [ "dep:h2" "dep:hyper" "dep:tokio" "dep:tower" "dep:hyper-timeout" ];
+          "codegen" = [ "dep:async-trait" ];
+          "default" = [ "transport" "codegen" "prost" ];
+          "gzip" = [ "dep:flate2" ];
+          "prost" = [ "dep:prost" ];
+          "tls" = [ "dep:rustls-pemfile" "transport" "dep:tokio-rustls" "dep:async-stream" ];
+          "tls-roots" = [ "tls-roots-common" "dep:rustls-native-certs" ];
+          "tls-roots-common" = [ "tls" ];
+          "tls-webpki-roots" = [ "tls-roots-common" "dep:webpki-roots" ];
+          "transport" = [ "dep:axum" "channel" ];
+        };
+        resolvedDefaultFeatures = [ "channel" "codegen" "default" "prost" "transport" ];
+      };
+      "tonic-build" = rec {
+        crateName = "tonic-build";
+        version = "0.11.0";
+        edition = "2021";
+        sha256 = "1hm99ckaw0pzq8h22bdjy6gpbg06kpvs0f73nj60f456f3fzckmy";
+        authors = [
+          "Lucio Franco <luciofranco14@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "prettyplease";
+            packageId = "prettyplease";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "prost-build";
+            packageId = "prost-build";
+            optional = true;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+          }
+        ];
+        features = {
+          "cleanup-markdown" = [ "prost" "prost-build/cleanup-markdown" ];
+          "default" = [ "transport" "prost" ];
+          "prost" = [ "prost-build" ];
+          "prost-build" = [ "dep:prost-build" ];
+        };
+        resolvedDefaultFeatures = [ "cleanup-markdown" "default" "prost" "prost-build" "transport" ];
+      };
+      "tonic-reflection" = rec {
+        crateName = "tonic-reflection";
+        version = "0.11.0";
+        edition = "2021";
+        sha256 = "19xn3kyg062d7y1cnw537v9cxkzzcrnfri0jb29fbyn0smxj532l";
+        authors = [
+          "James Nugent <james@jen20.com>"
+          "Samani G. Gikandi <samani@gojulas.com>"
+        ];
+        dependencies = [
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+          }
+          {
+            name = "prost-types";
+            packageId = "prost-types";
+            optional = true;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "sync" "rt" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+            optional = true;
+            features = [ "net" ];
+          }
+          {
+            name = "tonic";
+            packageId = "tonic 0.11.0";
+            usesDefaultFeatures = false;
+            features = [ "codegen" "prost" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tonic";
+            packageId = "tonic 0.11.0";
+            usesDefaultFeatures = false;
+            features = [ "transport" ];
+          }
+        ];
+        features = {
+          "default" = [ "server" ];
+          "prost-types" = [ "dep:prost-types" ];
+          "server" = [ "prost-types" "dep:tokio" "dep:tokio-stream" ];
+        };
+        resolvedDefaultFeatures = [ "default" "prost-types" "server" ];
+      };
+      "tower" = rec {
+        crateName = "tower";
+        version = "0.4.13";
+        edition = "2018";
+        sha256 = "073wncyqav4sak1p755hf6vl66njgfc1z1g1di9rxx3cvvh9pymq";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            optional = true;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap 1.9.3";
+            optional = true;
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            optional = true;
+            features = [ "small_rng" ];
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+            optional = true;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "sync" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "sync" "test-util" "rt-multi-thread" ];
+          }
+        ];
+        features = {
+          "__common" = [ "futures-core" "pin-project-lite" ];
+          "balance" = [ "discover" "load" "ready-cache" "make" "rand" "slab" ];
+          "buffer" = [ "__common" "tokio/sync" "tokio/rt" "tokio-util" "tracing" ];
+          "default" = [ "log" ];
+          "discover" = [ "__common" ];
+          "filter" = [ "__common" "futures-util" ];
+          "full" = [ "balance" "buffer" "discover" "filter" "hedge" "limit" "load" "load-shed" "make" "ready-cache" "reconnect" "retry" "spawn-ready" "steer" "timeout" "util" ];
+          "futures-core" = [ "dep:futures-core" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hdrhistogram" = [ "dep:hdrhistogram" ];
+          "hedge" = [ "util" "filter" "futures-util" "hdrhistogram" "tokio/time" "tracing" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "limit" = [ "__common" "tokio/time" "tokio/sync" "tokio-util" "tracing" ];
+          "load" = [ "__common" "tokio/time" "tracing" ];
+          "load-shed" = [ "__common" ];
+          "log" = [ "tracing/log" ];
+          "make" = [ "futures-util" "pin-project-lite" "tokio/io-std" ];
+          "pin-project" = [ "dep:pin-project" ];
+          "pin-project-lite" = [ "dep:pin-project-lite" ];
+          "rand" = [ "dep:rand" ];
+          "ready-cache" = [ "futures-core" "futures-util" "indexmap" "tokio/sync" "tracing" "pin-project-lite" ];
+          "reconnect" = [ "make" "tokio/io-std" "tracing" ];
+          "retry" = [ "__common" "tokio/time" ];
+          "slab" = [ "dep:slab" ];
+          "spawn-ready" = [ "__common" "futures-util" "tokio/sync" "tokio/rt" "util" "tracing" ];
+          "timeout" = [ "pin-project-lite" "tokio/time" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-stream" = [ "dep:tokio-stream" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "tracing" = [ "dep:tracing" ];
+          "util" = [ "__common" "futures-util" "pin-project" ];
+        };
+        resolvedDefaultFeatures = [ "__common" "balance" "buffer" "default" "discover" "futures-core" "futures-util" "indexmap" "limit" "load" "log" "make" "pin-project" "pin-project-lite" "rand" "ready-cache" "slab" "timeout" "tokio" "tokio-util" "tracing" "util" ];
+      };
+      "tower-layer" = rec {
+        crateName = "tower-layer";
+        version = "0.3.2";
+        edition = "2018";
+        sha256 = "1l7i17k9vlssrdg4s3b0ia5jjkmmxsvv8s9y9ih0jfi8ssz8s362";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+
+      };
+      "tower-service" = rec {
+        crateName = "tower-service";
+        version = "0.3.2";
+        edition = "2018";
+        sha256 = "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+
+      };
+      "tracing" = rec {
+        crateName = "tracing";
+        version = "0.1.40";
+        edition = "2018";
+        sha256 = "1vv48dac9zgj9650pg2b4d0j3w6f3x9gbggf43scq5hrlysklln3";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tracing-attributes";
+            packageId = "tracing-attributes";
+            optional = true;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "attributes" = [ "tracing-attributes" ];
+          "default" = [ "std" "attributes" ];
+          "log" = [ "dep:log" ];
+          "log-always" = [ "log" ];
+          "std" = [ "tracing-core/std" ];
+          "tracing-attributes" = [ "dep:tracing-attributes" ];
+          "valuable" = [ "tracing-core/valuable" ];
+        };
+        resolvedDefaultFeatures = [ "attributes" "default" "log" "max_level_trace" "release_max_level_info" "std" "tracing-attributes" ];
+      };
+      "tracing-attributes" = rec {
+        crateName = "tracing-attributes";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <dbarsky@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            usesDefaultFeatures = false;
+            features = [ "full" "parsing" "printing" "visit-mut" "clone-impls" "extra-traits" "proc-macro" ];
+          }
+        ];
+        features = { };
+      };
+      "tracing-core" = rec {
+        crateName = "tracing-core";
+        version = "0.1.32";
+        edition = "2018";
+        sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+          }
+          {
+            name = "valuable";
+            packageId = "valuable";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."tracing_unstable" or false);
+          }
+        ];
+        features = {
+          "default" = [ "std" "valuable/std" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "std" = [ "once_cell" ];
+          "valuable" = [ "dep:valuable" ];
+        };
+        resolvedDefaultFeatures = [ "default" "once_cell" "std" "valuable" ];
+      };
+      "tracing-futures" = rec {
+        crateName = "tracing-futures";
+        version = "0.2.5";
+        edition = "2018";
+        sha256 = "1wimg0iwa2ldq7xv98lvivvf3q9ykfminig8r1bs0ig22np9bl4p";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+            optional = true;
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std-future" "std" ];
+          "futures" = [ "dep:futures" ];
+          "futures-01" = [ "futures_01" "std" ];
+          "futures-03" = [ "std-future" "futures" "futures-task" "std" ];
+          "futures-task" = [ "dep:futures-task" ];
+          "futures_01" = [ "dep:futures_01" ];
+          "pin-project" = [ "dep:pin-project" ];
+          "std" = [ "tracing/std" ];
+          "std-future" = [ "pin-project" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-executor" = [ "dep:tokio-executor" ];
+        };
+        resolvedDefaultFeatures = [ "default" "pin-project" "std" "std-future" ];
+      };
+      "tracing-log" = rec {
+        crateName = "tracing-log";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "1hs77z026k730ij1a9dhahzrl0s073gfa2hm5p0fbl0b80gmz1gf";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+          }
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "default" = [ "log-tracer" "std" ];
+          "interest-cache" = [ "lru" "ahash" ];
+          "lru" = [ "dep:lru" ];
+          "std" = [ "log/std" ];
+        };
+        resolvedDefaultFeatures = [ "log-tracer" "std" ];
+      };
+      "tracing-opentelemetry" = rec {
+        crateName = "tracing-opentelemetry";
+        version = "0.22.0";
+        edition = "2018";
+        sha256 = "15jmwmbp6wz15bx20bfsmabx53wmlnd7wvzwz9hvkrq7aifc4yn6";
+        authors = [
+          "Julian Tescher <julian@tescher.me>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!("wasi" == target."os" or null)));
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "opentelemetry";
+            packageId = "opentelemetry";
+            usesDefaultFeatures = false;
+            features = [ "trace" ];
+          }
+          {
+            name = "opentelemetry_sdk";
+            packageId = "opentelemetry_sdk";
+            usesDefaultFeatures = false;
+            features = [ "trace" ];
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+            optional = true;
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+          }
+          {
+            name = "tracing-log";
+            packageId = "tracing-log";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tracing-subscriber";
+            packageId = "tracing-subscriber";
+            usesDefaultFeatures = false;
+            features = [ "registry" "std" ];
+          }
+          {
+            name = "web-time";
+            packageId = "web-time";
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!("wasi" == target."os" or null)));
+          }
+        ];
+        devDependencies = [
+          {
+            name = "opentelemetry";
+            packageId = "opentelemetry";
+            features = [ "trace" "metrics" ];
+          }
+          {
+            name = "opentelemetry_sdk";
+            packageId = "opentelemetry_sdk";
+            usesDefaultFeatures = false;
+            features = [ "trace" "rt-tokio" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" "attributes" ];
+          }
+          {
+            name = "tracing-subscriber";
+            packageId = "tracing-subscriber";
+            usesDefaultFeatures = false;
+            features = [ "registry" "std" "fmt" ];
+          }
+        ];
+        features = {
+          "async-trait" = [ "dep:async-trait" ];
+          "default" = [ "tracing-log" "metrics" ];
+          "metrics" = [ "opentelemetry/metrics" "opentelemetry_sdk/metrics" "smallvec" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "thiserror" = [ "dep:thiserror" ];
+          "tracing-log" = [ "dep:tracing-log" ];
+        };
+        resolvedDefaultFeatures = [ "default" "metrics" "smallvec" "tracing-log" ];
+      };
+      "tracing-serde" = rec {
+        crateName = "tracing-serde";
+        version = "0.1.3";
+        edition = "2018";
+        sha256 = "1qfr0va69djvxqvjrx4vqq7p6myy414lx4w1f6amcn0hfwqj2sxw";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+          }
+        ];
+        features = {
+          "valuable" = [ "valuable_crate" "valuable-serde" "tracing-core/valuable" ];
+          "valuable-serde" = [ "dep:valuable-serde" ];
+          "valuable_crate" = [ "dep:valuable_crate" ];
+        };
+      };
+      "tracing-subscriber" = rec {
+        crateName = "tracing-subscriber";
+        version = "0.3.18";
+        edition = "2018";
+        sha256 = "12vs1bwk4kig1l2qqjbbn2nm5amwiqmkcmnznylzmnfvjy6083xd";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <me@davidbarsky.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "matchers";
+            packageId = "matchers";
+            optional = true;
+          }
+          {
+            name = "nu-ansi-term";
+            packageId = "nu-ansi-term";
+            optional = true;
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" "unicode-case" "unicode-perl" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+          }
+          {
+            name = "sharded-slab";
+            packageId = "sharded-slab";
+            optional = true;
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+            optional = true;
+          }
+          {
+            name = "thread_local";
+            packageId = "thread_local";
+            optional = true;
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tracing-log";
+            packageId = "tracing-log";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "log-tracer" "std" ];
+          }
+          {
+            name = "tracing-serde";
+            packageId = "tracing-serde";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "regex";
+            packageId = "regex";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "tracing-log";
+            packageId = "tracing-log";
+          }
+        ];
+        features = {
+          "ansi" = [ "fmt" "nu-ansi-term" ];
+          "chrono" = [ "dep:chrono" ];
+          "default" = [ "smallvec" "fmt" "ansi" "tracing-log" "std" ];
+          "env-filter" = [ "matchers" "regex" "once_cell" "tracing" "std" "thread_local" ];
+          "fmt" = [ "registry" "std" ];
+          "json" = [ "tracing-serde" "serde" "serde_json" ];
+          "local-time" = [ "time/local-offset" ];
+          "matchers" = [ "dep:matchers" ];
+          "nu-ansi-term" = [ "dep:nu-ansi-term" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "regex" = [ "dep:regex" ];
+          "registry" = [ "sharded-slab" "thread_local" "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "sharded-slab" = [ "dep:sharded-slab" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "alloc" "tracing-core/std" ];
+          "thread_local" = [ "dep:thread_local" ];
+          "time" = [ "dep:time" ];
+          "tracing" = [ "dep:tracing" ];
+          "tracing-log" = [ "dep:tracing-log" ];
+          "tracing-serde" = [ "dep:tracing-serde" ];
+          "valuable" = [ "tracing-core/valuable" "valuable_crate" "valuable-serde" "tracing-serde/valuable" ];
+          "valuable-serde" = [ "dep:valuable-serde" ];
+          "valuable_crate" = [ "dep:valuable_crate" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "ansi" "default" "env-filter" "fmt" "json" "matchers" "nu-ansi-term" "once_cell" "regex" "registry" "serde" "serde_json" "sharded-slab" "smallvec" "std" "thread_local" "tracing" "tracing-log" "tracing-serde" ];
+      };
+      "try-lock" = rec {
+        crateName = "try-lock";
+        version = "0.2.5";
+        edition = "2015";
+        sha256 = "0jqijrrvm1pyq34zn1jmy2vihd4jcrjlvsh4alkjahhssjnsn8g4";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+
+      };
+      "tvix-build" = rec {
+        crateName = "tvix-build";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "tvix-build";
+            path = "src/bin/tvix-build.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./build; }
+          else ./build;
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "clap";
+            packageId = "clap";
+            features = [ "derive" "env" ];
+          }
+          {
+            name = "itertools";
+            packageId = "itertools 0.12.0";
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "tokio-listener";
+            packageId = "tokio-listener";
+            features = [ "tonic011" ];
+          }
+          {
+            name = "tonic";
+            packageId = "tonic 0.11.0";
+            features = [ "tls" "tls-roots" ];
+          }
+          {
+            name = "tonic-reflection";
+            packageId = "tonic-reflection";
+            optional = true;
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "tracing-subscriber";
+            packageId = "tracing-subscriber";
+            features = [ "json" ];
+          }
+          {
+            name = "tvix-castore";
+            packageId = "tvix-castore";
+          }
+          {
+            name = "url";
+            packageId = "url";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "prost-build";
+            packageId = "prost-build";
+          }
+          {
+            name = "tonic-build";
+            packageId = "tonic-build";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rstest";
+            packageId = "rstest";
+          }
+        ];
+        features = {
+          "tonic-reflection" = [ "dep:tonic-reflection" ];
+        };
+        resolvedDefaultFeatures = [ "default" "tonic-reflection" ];
+      };
+      "tvix-castore" = rec {
+        crateName = "tvix-castore";
+        version = "0.1.0";
+        edition = "2021";
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./castore; }
+          else ./castore;
+        dependencies = [
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+          }
+          {
+            name = "async-tempfile";
+            packageId = "async-tempfile";
+          }
+          {
+            name = "bigtable_rs";
+            packageId = "bigtable_rs";
+            optional = true;
+          }
+          {
+            name = "blake3";
+            packageId = "blake3";
+            features = [ "rayon" "std" "traits-preview" ];
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+          {
+            name = "fastcdc";
+            packageId = "fastcdc";
+            features = [ "tokio" ];
+          }
+          {
+            name = "fuse-backend-rs";
+            packageId = "fuse-backend-rs";
+            optional = true;
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+          }
+          {
+            name = "object_store";
+            packageId = "object_store";
+            features = [ "http" ];
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot 0.12.1";
+          }
+          {
+            name = "petgraph";
+            packageId = "petgraph";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_qs";
+            packageId = "serde_qs";
+          }
+          {
+            name = "serde_with";
+            packageId = "serde_with";
+          }
+          {
+            name = "sled";
+            packageId = "sled";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "macros" "net" "rt" "rt-multi-thread" "signal" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+            features = [ "fs" "net" ];
+          }
+          {
+            name = "tokio-tar";
+            packageId = "tokio-tar";
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            features = [ "io" "io-util" ];
+          }
+          {
+            name = "tonic";
+            packageId = "tonic 0.11.0";
+          }
+          {
+            name = "tonic-reflection";
+            packageId = "tonic-reflection";
+            optional = true;
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "url";
+            packageId = "url";
+          }
+          {
+            name = "vhost";
+            packageId = "vhost";
+            optional = true;
+          }
+          {
+            name = "vhost-user-backend";
+            packageId = "vhost-user-backend";
+            optional = true;
+          }
+          {
+            name = "virtio-bindings";
+            packageId = "virtio-bindings 0.2.2";
+            optional = true;
+          }
+          {
+            name = "virtio-queue";
+            packageId = "virtio-queue";
+            optional = true;
+          }
+          {
+            name = "vm-memory";
+            packageId = "vm-memory";
+            optional = true;
+          }
+          {
+            name = "vmm-sys-util";
+            packageId = "vmm-sys-util";
+            optional = true;
+          }
+          {
+            name = "walkdir";
+            packageId = "walkdir";
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "prost-build";
+            packageId = "prost-build";
+          }
+          {
+            name = "tonic-build";
+            packageId = "tonic-build";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "async-process";
+            packageId = "async-process";
+          }
+          {
+            name = "hex-literal";
+            packageId = "hex-literal";
+          }
+          {
+            name = "rstest";
+            packageId = "rstest";
+          }
+          {
+            name = "rstest_reuse";
+            packageId = "rstest_reuse";
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+          }
+          {
+            name = "tokio-retry";
+            packageId = "tokio-retry";
+          }
+          {
+            name = "xattr";
+            packageId = "xattr";
+          }
+        ];
+        features = {
+          "cloud" = [ "dep:bigtable_rs" "object_store/aws" "object_store/azure" "object_store/gcp" ];
+          "fs" = [ "dep:libc" "dep:fuse-backend-rs" ];
+          "fuse" = [ "fs" ];
+          "tonic-reflection" = [ "dep:tonic-reflection" ];
+          "virtiofs" = [ "fs" "dep:vhost" "dep:vhost-user-backend" "dep:virtio-queue" "dep:vm-memory" "dep:vmm-sys-util" "dep:virtio-bindings" "fuse-backend-rs?/vhost-user-fs" "fuse-backend-rs?/virtiofs" ];
+        };
+        resolvedDefaultFeatures = [ "cloud" "default" "fs" "fuse" "integration" "tonic-reflection" "virtiofs" ];
+      };
+      "tvix-cli" = rec {
+        crateName = "tvix-cli";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "tvix";
+            path = "src/main.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./cli; }
+          else ./cli;
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "clap";
+            packageId = "clap";
+            features = [ "derive" "env" ];
+          }
+          {
+            name = "dirs";
+            packageId = "dirs";
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+          }
+          {
+            name = "rustyline";
+            packageId = "rustyline";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            features = [ "max_level_trace" "release_max_level_info" ];
+          }
+          {
+            name = "tracing-subscriber";
+            packageId = "tracing-subscriber";
+            features = [ "json" ];
+          }
+          {
+            name = "tvix-build";
+            packageId = "tvix-build";
+          }
+          {
+            name = "tvix-castore";
+            packageId = "tvix-castore";
+          }
+          {
+            name = "tvix-eval";
+            packageId = "tvix-eval";
+          }
+          {
+            name = "tvix-glue";
+            packageId = "tvix-glue";
+          }
+          {
+            name = "tvix-store";
+            packageId = "tvix-store";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "wu-manber";
+            packageId = "wu-manber";
+          }
+        ];
+
+      };
+      "tvix-eval" = rec {
+        crateName = "tvix-eval";
+        version = "0.1.0";
+        edition = "2021";
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./eval; }
+          else ./eval;
+        libName = "tvix_eval";
+        dependencies = [
+          {
+            name = "bstr";
+            packageId = "bstr";
+            features = [ "serde" ];
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "codemap";
+            packageId = "codemap";
+          }
+          {
+            name = "codemap-diagnostic";
+            packageId = "codemap-diagnostic";
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "dirs";
+            packageId = "dirs";
+          }
+          {
+            name = "genawaiter";
+            packageId = "genawaiter";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "imbl";
+            packageId = "imbl";
+            features = [ "serde" ];
+          }
+          {
+            name = "itertools";
+            packageId = "itertools 0.12.0";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "lexical-core";
+            packageId = "lexical-core";
+            features = [ "format" "parse-floats" ];
+          }
+          {
+            name = "md-5";
+            packageId = "md-5";
+          }
+          {
+            name = "os_str_bytes";
+            packageId = "os_str_bytes";
+            features = [ "conversions" ];
+          }
+          {
+            name = "path-clean";
+            packageId = "path-clean";
+          }
+          {
+            name = "proptest";
+            packageId = "proptest";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" "alloc" "tempfile" ];
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "rnix";
+            packageId = "rnix";
+          }
+          {
+            name = "rowan";
+            packageId = "rowan";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "rc" "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sha1";
+            packageId = "sha1";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+          }
+          {
+            name = "smol_str";
+            packageId = "smol_str";
+          }
+          {
+            name = "tabwriter";
+            packageId = "tabwriter";
+          }
+          {
+            name = "test-strategy";
+            packageId = "test-strategy";
+            optional = true;
+          }
+          {
+            name = "toml";
+            packageId = "toml";
+          }
+          {
+            name = "tvix-eval-builtin-macros";
+            packageId = "tvix-eval-builtin-macros";
+            rename = "builtin-macros";
+          }
+          {
+            name = "xml-rs";
+            packageId = "xml-rs";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "criterion";
+            packageId = "criterion";
+          }
+          {
+            name = "itertools";
+            packageId = "itertools 0.12.0";
+          }
+          {
+            name = "pretty_assertions";
+            packageId = "pretty_assertions";
+          }
+          {
+            name = "rstest";
+            packageId = "rstest";
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+          }
+        ];
+        features = {
+          "arbitrary" = [ "proptest" "test-strategy" "imbl/proptest" ];
+          "default" = [ "impure" "arbitrary" "nix_tests" ];
+          "proptest" = [ "dep:proptest" ];
+          "test-strategy" = [ "dep:test-strategy" ];
+        };
+        resolvedDefaultFeatures = [ "arbitrary" "default" "impure" "nix_tests" "proptest" "test-strategy" ];
+      };
+      "tvix-eval-builtin-macros" = rec {
+        crateName = "tvix-eval-builtin-macros";
+        version = "0.0.1";
+        edition = "2021";
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./eval/builtin-macros; }
+          else ./eval/builtin-macros;
+        procMacro = true;
+        authors = [
+          "Griffin Smith <root@gws.fyi>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "full" "parsing" "printing" "visit" "visit-mut" "extra-traits" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tvix-eval";
+            packageId = "tvix-eval";
+          }
+        ];
+
+      };
+      "tvix-glue" = rec {
+        crateName = "tvix-glue";
+        version = "0.1.0";
+        edition = "2021";
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./glue; }
+          else ./glue;
+        dependencies = [
+          {
+            name = "async-compression";
+            packageId = "async-compression";
+            features = [ "tokio" "gzip" "bzip2" "xz" ];
+          }
+          {
+            name = "async-recursion";
+            packageId = "async-recursion";
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "magic";
+            packageId = "magic";
+          }
+          {
+            name = "md-5";
+            packageId = "md-5";
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+          }
+          {
+            name = "reqwest";
+            packageId = "reqwest";
+            usesDefaultFeatures = false;
+            features = [ "rustls-tls-native-roots" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sha1";
+            packageId = "sha1";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "tokio-tar";
+            packageId = "tokio-tar";
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            features = [ "io" "io-util" "compat" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "tvix-build";
+            packageId = "tvix-build";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tvix-castore";
+            packageId = "tvix-castore";
+          }
+          {
+            name = "tvix-eval";
+            packageId = "tvix-eval";
+          }
+          {
+            name = "tvix-store";
+            packageId = "tvix-store";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "url";
+            packageId = "url";
+          }
+          {
+            name = "walkdir";
+            packageId = "walkdir";
+          }
+          {
+            name = "wu-manber";
+            packageId = "wu-manber";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "criterion";
+            packageId = "criterion";
+            features = [ "html_reports" ];
+          }
+          {
+            name = "hex-literal";
+            packageId = "hex-literal";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "nix";
+            packageId = "nix 0.27.1";
+            features = [ "fs" ];
+          }
+          {
+            name = "pretty_assertions";
+            packageId = "pretty_assertions";
+          }
+          {
+            name = "rstest";
+            packageId = "rstest";
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+          }
+        ];
+        features = {
+          "default" = [ "nix_tests" ];
+        };
+        resolvedDefaultFeatures = [ "default" "nix_tests" ];
+      };
+      "tvix-serde" = rec {
+        crateName = "tvix-serde";
+        version = "0.1.0";
+        edition = "2021";
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./serde; }
+          else ./serde;
+        dependencies = [
+          {
+            name = "bstr";
+            packageId = "bstr";
+            features = [ "serde" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "tvix-eval";
+            packageId = "tvix-eval";
+          }
+        ];
+
+      };
+      "tvix-store" = rec {
+        crateName = "tvix-store";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "tvix-store";
+            path = "src/bin/tvix-store.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./store; }
+          else ./store;
+        dependencies = [
+          {
+            name = "anyhow";
+            packageId = "anyhow";
+          }
+          {
+            name = "async-recursion";
+            packageId = "async-recursion";
+          }
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+          }
+          {
+            name = "bigtable_rs";
+            packageId = "bigtable_rs";
+            optional = true;
+          }
+          {
+            name = "blake3";
+            packageId = "blake3";
+            features = [ "rayon" "std" ];
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "clap";
+            packageId = "clap";
+            features = [ "derive" "env" ];
+          }
+          {
+            name = "count-write";
+            packageId = "count-write";
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+            features = [ "async" ];
+          }
+          {
+            name = "opentelemetry";
+            packageId = "opentelemetry";
+            optional = true;
+          }
+          {
+            name = "opentelemetry-otlp";
+            packageId = "opentelemetry-otlp";
+            optional = true;
+          }
+          {
+            name = "opentelemetry_sdk";
+            packageId = "opentelemetry_sdk";
+            optional = true;
+            features = [ "rt-tokio" ];
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "prost";
+            packageId = "prost 0.12.3";
+          }
+          {
+            name = "reqwest";
+            packageId = "reqwest";
+            usesDefaultFeatures = false;
+            features = [ "rustls-tls-native-roots" "stream" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "serde_qs";
+            packageId = "serde_qs";
+          }
+          {
+            name = "serde_with";
+            packageId = "serde_with";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+          }
+          {
+            name = "sled";
+            packageId = "sled";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "macros" "net" "rt" "rt-multi-thread" "signal" ];
+          }
+          {
+            name = "tokio-listener";
+            packageId = "tokio-listener";
+            features = [ "tonic011" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+            features = [ "fs" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            features = [ "io" "io-util" "compat" ];
+          }
+          {
+            name = "tonic";
+            packageId = "tonic 0.11.0";
+            features = [ "tls" "tls-roots" ];
+          }
+          {
+            name = "tonic-reflection";
+            packageId = "tonic-reflection";
+            optional = true;
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "tracing-opentelemetry";
+            packageId = "tracing-opentelemetry";
+          }
+          {
+            name = "tracing-subscriber";
+            packageId = "tracing-subscriber";
+            features = [ "env-filter" "json" ];
+          }
+          {
+            name = "tvix-castore";
+            packageId = "tvix-castore";
+          }
+          {
+            name = "url";
+            packageId = "url";
+          }
+          {
+            name = "walkdir";
+            packageId = "walkdir";
+          }
+          {
+            name = "xz2";
+            packageId = "xz2";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "prost-build";
+            packageId = "prost-build";
+          }
+          {
+            name = "tonic-build";
+            packageId = "tonic-build";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "async-process";
+            packageId = "async-process";
+          }
+          {
+            name = "rstest";
+            packageId = "rstest";
+          }
+          {
+            name = "rstest_reuse";
+            packageId = "rstest_reuse";
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+          }
+          {
+            name = "tokio-retry";
+            packageId = "tokio-retry";
+          }
+        ];
+        features = {
+          "cloud" = [ "dep:bigtable_rs" "tvix-castore/cloud" ];
+          "default" = [ "cloud" "fuse" "otlp" "tonic-reflection" ];
+          "fuse" = [ "tvix-castore/fuse" ];
+          "otlp" = [ "dep:opentelemetry" "dep:opentelemetry-otlp" "dep:opentelemetry_sdk" ];
+          "tonic-reflection" = [ "dep:tonic-reflection" "tvix-castore/tonic-reflection" ];
+          "virtiofs" = [ "tvix-castore/virtiofs" ];
+        };
+        resolvedDefaultFeatures = [ "cloud" "default" "fuse" "integration" "otlp" "tonic-reflection" "virtiofs" ];
+      };
+      "typenum" = rec {
+        crateName = "typenum";
+        version = "1.17.0";
+        edition = "2018";
+        sha256 = "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2";
+        build = "build/main.rs";
+        authors = [
+          "Paho Lurie-Gregg <paho@paholg.com>"
+          "Andre Bogus <bogusandre@gmail.com>"
+        ];
+        features = {
+          "scale-info" = [ "dep:scale-info" ];
+          "scale_info" = [ "scale-info/derive" ];
+        };
+      };
+      "typetag" = rec {
+        crateName = "typetag";
+        version = "0.2.16";
+        edition = "2021";
+        sha256 = "1bwswa9ah2sc6fmlfw2pim73rr1n6vhfwmidrsga8cn09r0ih7b6";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "erased-serde";
+            packageId = "erased-serde";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "inventory";
+            packageId = "inventory";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+            features = [ "alloc" "derive" ];
+          }
+          {
+            name = "typetag-impl";
+            packageId = "typetag-impl";
+          }
+        ];
+
+      };
+      "typetag-impl" = rec {
+        crateName = "typetag-impl";
+        version = "0.2.16";
+        edition = "2021";
+        sha256 = "1cabnvm526bcgh1sh34js5ils0gz4xwlgvwhm992acdr8xzqhwxc";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "unarray" = rec {
+        crateName = "unarray";
+        version = "0.1.4";
+        edition = "2018";
+        sha256 = "154smf048k84prsdgh09nkm2n0w0336v84jd4zikyn6v6jrqbspa";
+
+      };
+      "unicase" = rec {
+        crateName = "unicase";
+        version = "2.7.0";
+        edition = "2015";
+        sha256 = "12gd74j79f94k4clxpf06l99wiv4p30wjr0qm04ihqk9zgdd9lpp";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = { };
+      };
+      "unicode-bidi" = rec {
+        crateName = "unicode-bidi";
+        version = "0.3.15";
+        edition = "2018";
+        sha256 = "0xcdxm7h0ydyprwpcbh436rbs6s6lph7f3gr527lzgv6lw053y88";
+        libName = "unicode_bidi";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "default" = [ "std" "hardcoded-data" ];
+          "flame" = [ "dep:flame" ];
+          "flame_it" = [ "flame" "flamer" ];
+          "flamer" = [ "dep:flamer" ];
+          "serde" = [ "dep:serde" ];
+          "with_serde" = [ "serde" ];
+        };
+        resolvedDefaultFeatures = [ "hardcoded-data" "std" ];
+      };
+      "unicode-ident" = rec {
+        crateName = "unicode-ident";
+        version = "1.0.12";
+        edition = "2018";
+        sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "unicode-normalization" = rec {
+        crateName = "unicode-normalization";
+        version = "0.1.22";
+        edition = "2018";
+        sha256 = "08d95g7b1irc578b2iyhzv4xhsa4pfvwsqxcl9lbcpabzkq16msw";
+        authors = [
+          "kwantam <kwantam@gmail.com>"
+          "Manish Goregaokar <manishsmail@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "tinyvec";
+            packageId = "tinyvec";
+            features = [ "alloc" ];
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "unicode-segmentation" = rec {
+        crateName = "unicode-segmentation";
+        version = "1.10.1";
+        edition = "2018";
+        sha256 = "0dky2hm5k51xy11hc3nk85p533rvghd462b6i0c532b7hl4j9mhx";
+        authors = [
+          "kwantam <kwantam@gmail.com>"
+          "Manish Goregaokar <manishsmail@gmail.com>"
+        ];
+        features = { };
+      };
+      "unicode-width" = rec {
+        crateName = "unicode-width";
+        version = "0.1.11";
+        edition = "2015";
+        sha256 = "11ds4ydhg8g7l06rlmh712q41qsrd0j0h00n1jm74kww3kqk65z5";
+        authors = [
+          "kwantam <kwantam@gmail.com>"
+          "Manish Goregaokar <manishsmail@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "std" "core" "compiler_builtins" ];
+          "std" = [ "dep:std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "untrusted" = rec {
+        crateName = "untrusted";
+        version = "0.9.0";
+        edition = "2018";
+        sha256 = "1ha7ib98vkc538x0z60gfn0fc5whqdd85mb87dvisdcaifi6vjwf";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+
+      };
+      "url" = rec {
+        crateName = "url";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "0cs65961miawncdg2z20171w0vqrmraswv2ihdpd8lxp7cp31rii";
+        authors = [
+          "The rust-url developers"
+        ];
+        dependencies = [
+          {
+            name = "form_urlencoded";
+            packageId = "form_urlencoded";
+          }
+          {
+            name = "idna";
+            packageId = "idna";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "urlencoding" = rec {
+        crateName = "urlencoding";
+        version = "2.1.3";
+        edition = "2021";
+        sha256 = "1nj99jp37k47n0hvaz5fvz7z6jd0sb4ppvfy3nphr1zbnyixpy6s";
+        authors = [
+          "Kornel <kornel@geekhood.net>"
+          "Bertram Truong <b@bertramtruong.com>"
+        ];
+
+      };
+      "utf8parse" = rec {
+        crateName = "utf8parse";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "02ip1a0az0qmc2786vxk2nqwsgcwf17d3a38fkf0q7hrmwh9c6vi";
+        authors = [
+          "Joe Wilm <joe@jwilm.com>"
+          "Christian Duerr <contact@christianduerr.com>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "uuid" = rec {
+        crateName = "uuid";
+        version = "1.7.0";
+        edition = "2018";
+        sha256 = "0aivp5ys7sg2izlj2sn6rr8p43vdcwg64naj8n0kqbd15iqcj37h";
+        authors = [
+          "Ashley Mannix<ashleymannix@live.com.au>"
+          "Christopher Armstrong"
+          "Dylan DPC<dylan.dpc@gmail.com>"
+          "Hunar Roop Kahlon<hunar.roop@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "atomic" = [ "dep:atomic" ];
+          "borsh" = [ "dep:borsh" "dep:borsh-derive" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "default" = [ "std" ];
+          "fast-rng" = [ "rng" "dep:rand" ];
+          "js" = [ "dep:wasm-bindgen" "getrandom?/js" ];
+          "macro-diagnostics" = [ "dep:uuid-macro-internal" ];
+          "md5" = [ "dep:md-5" ];
+          "rng" = [ "dep:getrandom" ];
+          "serde" = [ "dep:serde" ];
+          "sha1" = [ "dep:sha1_smol" ];
+          "slog" = [ "dep:slog" ];
+          "v1" = [ "atomic" ];
+          "v3" = [ "md5" ];
+          "v4" = [ "rng" ];
+          "v5" = [ "sha1" ];
+          "v6" = [ "atomic" ];
+          "v7" = [ "atomic" "rng" ];
+          "zerocopy" = [ "dep:zerocopy" ];
+        };
+        resolvedDefaultFeatures = [ "default" "rng" "std" "v4" ];
+      };
+      "valuable" = rec {
+        crateName = "valuable";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "0v9gp3nkjbl30z0fd56d8mx7w1csk86wwjhfjhr400wh9mfpw2w3";
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "valuable-derive" ];
+          "std" = [ "alloc" ];
+          "valuable-derive" = [ "dep:valuable-derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "vcpkg" = rec {
+        crateName = "vcpkg";
+        version = "0.2.15";
+        edition = "2015";
+        sha256 = "09i4nf5y8lig6xgj3f7fyrvzd3nlaw4znrihw8psidvv5yk4xkdc";
+        authors = [
+          "Jim McGrath <jimmc2@gmail.com>"
+        ];
+
+      };
+      "version_check" = rec {
+        crateName = "version_check";
+        version = "0.9.4";
+        edition = "2015";
+        sha256 = "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9";
+        authors = [
+          "Sergio Benitez <sb@sergio.bz>"
+        ];
+
+      };
+      "vhost" = rec {
+        crateName = "vhost";
+        version = "0.6.1";
+        edition = "2018";
+        sha256 = "0dczb95w5vcq852fzxsbc6zh7ll0p1mz7yrrchvv8xjjpy6rwxm6";
+        authors = [
+          "Liu Jiang <gerry@linux.alibaba.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "vm-memory";
+            packageId = "vm-memory";
+          }
+          {
+            name = "vmm-sys-util";
+            packageId = "vmm-sys-util";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "vm-memory";
+            packageId = "vm-memory";
+            features = [ "backend-mmap" ];
+          }
+        ];
+        features = {
+          "vhost-net" = [ "vhost-kern" ];
+          "vhost-user-master" = [ "vhost-user" ];
+          "vhost-user-slave" = [ "vhost-user" ];
+          "vhost-vdpa" = [ "vhost-kern" ];
+        };
+        resolvedDefaultFeatures = [ "default" "vhost-user" "vhost-user-slave" ];
+      };
+      "vhost-user-backend" = rec {
+        crateName = "vhost-user-backend";
+        version = "0.8.0";
+        edition = "2018";
+        sha256 = "00s33wy8cj2i8b4hlxn7wd8zm1fpaa5kjhzv77b3khsavf8pn8wz";
+        authors = [
+          "The Cloud Hypervisor Authors"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "vhost";
+            packageId = "vhost";
+            features = [ "vhost-user-slave" ];
+          }
+          {
+            name = "virtio-bindings";
+            packageId = "virtio-bindings 0.1.0";
+          }
+          {
+            name = "virtio-queue";
+            packageId = "virtio-queue";
+          }
+          {
+            name = "vm-memory";
+            packageId = "vm-memory";
+            features = [ "backend-mmap" "backend-atomic" ];
+          }
+          {
+            name = "vmm-sys-util";
+            packageId = "vmm-sys-util";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "vhost";
+            packageId = "vhost";
+            features = [ "vhost-user-master" "vhost-user-slave" ];
+          }
+          {
+            name = "vm-memory";
+            packageId = "vm-memory";
+            features = [ "backend-mmap" "backend-atomic" "backend-bitmap" ];
+          }
+        ];
+
+      };
+      "virtio-bindings 0.1.0" = rec {
+        crateName = "virtio-bindings";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "0sxxhhmz1r4s4q5pd2lykswcv9qk05fmpwc5xlb8aj45h8bi5x9z";
+        authors = [
+          "Sergio Lopez <slp@redhat.com>"
+        ];
+        features = { };
+      };
+      "virtio-bindings 0.2.2" = rec {
+        crateName = "virtio-bindings";
+        version = "0.2.2";
+        edition = "2021";
+        sha256 = "11mfm9brqgwpfg0izsgc4n7xkqwxk5ad03ivslq0r88j50dwp2w7";
+        authors = [
+          "Sergio Lopez <slp@redhat.com>"
+        ];
+        features = { };
+      };
+      "virtio-queue" = rec {
+        crateName = "virtio-queue";
+        version = "0.7.1";
+        edition = "2021";
+        sha256 = "1gbppbapj7c0vyca88vl34cx4sp2cy9yg0v6bvyd5h11rhmixa1v";
+        authors = [
+          "The Chromium OS Authors"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "virtio-bindings";
+            packageId = "virtio-bindings 0.1.0";
+          }
+          {
+            name = "vm-memory";
+            packageId = "vm-memory";
+          }
+          {
+            name = "vmm-sys-util";
+            packageId = "vmm-sys-util";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "vm-memory";
+            packageId = "vm-memory";
+            features = [ "backend-mmap" "backend-atomic" ];
+          }
+        ];
+        features = { };
+      };
+      "vm-memory" = rec {
+        crateName = "vm-memory";
+        version = "0.10.0";
+        edition = "2021";
+        sha256 = "0z423a8i4s3addq4yjad4ar5l6qwarjwdn94lismbd0mcqv712k8";
+        authors = [
+          "Liu Jiang <gerry@linux.alibaba.com>"
+        ];
+        dependencies = [
+          {
+            name = "arc-swap";
+            packageId = "arc-swap";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "errhandlingapi" "sysinfoapi" ];
+          }
+        ];
+        features = {
+          "arc-swap" = [ "dep:arc-swap" ];
+          "backend-atomic" = [ "arc-swap" ];
+        };
+        resolvedDefaultFeatures = [ "arc-swap" "backend-atomic" "backend-mmap" "default" ];
+      };
+      "vmm-sys-util" = rec {
+        crateName = "vmm-sys-util";
+        version = "0.11.2";
+        edition = "2021";
+        sha256 = "0a9azxk6wsahwkggshbdga4jdryzfw6j5r21f11gf50j4f2b1ds8";
+        authors = [
+          "Intel Virtualization Team <vmm-maintainers@intel.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+            target = { target, features }: (("linux" == target."os" or null) || ("android" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+          "with-serde" = [ "serde" "serde_derive" ];
+        };
+      };
+      "wait-timeout" = rec {
+        crateName = "wait-timeout";
+        version = "0.2.0";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "1xpkk0j5l9pfmjfh1pi0i89invlavfrd9av5xp0zhxgb29dhy84z";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+
+      };
+      "walkdir" = rec {
+        crateName = "walkdir";
+        version = "2.4.0";
+        edition = "2018";
+        sha256 = "1vjl9fmfc4v8k9ald23qrpcbyb8dl1ynyq8d516cm537r1yqa7fp";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "same-file";
+            packageId = "same-file";
+          }
+          {
+            name = "winapi-util";
+            packageId = "winapi-util";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+
+      };
+      "want" = rec {
+        crateName = "want";
+        version = "0.3.1";
+        edition = "2018";
+        sha256 = "03hbfrnvqqdchb5kgxyavb9jabwza0dmh2vw5kg0dq8rxl57d9xz";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "try-lock";
+            packageId = "try-lock";
+          }
+        ];
+
+      };
+      "wasi" = rec {
+        crateName = "wasi";
+        version = "0.11.0+wasi-snapshot-preview1";
+        edition = "2018";
+        sha256 = "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw";
+        authors = [
+          "The Cranelift Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "rustc-std-workspace-alloc" ];
+          "rustc-std-workspace-alloc" = [ "dep:rustc-std-workspace-alloc" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "wasm-bindgen" = rec {
+        crateName = "wasm-bindgen";
+        version = "0.2.90";
+        edition = "2018";
+        sha256 = "01jlal3mynqwvqx4acrdnr9bvsdczaz2sy8lmmzmqh81lab348mi";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "wasm-bindgen-macro";
+            packageId = "wasm-bindgen-macro";
+          }
+        ];
+        features = {
+          "default" = [ "spans" "std" ];
+          "enable-interning" = [ "std" ];
+          "gg-alloc" = [ "wasm-bindgen-test/gg-alloc" ];
+          "serde" = [ "dep:serde" ];
+          "serde-serialize" = [ "serde" "serde_json" "std" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "spans" = [ "wasm-bindgen-macro/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro/strict-macro" ];
+          "xxx_debug_only_print_generated_code" = [ "wasm-bindgen-macro/xxx_debug_only_print_generated_code" ];
+        };
+        resolvedDefaultFeatures = [ "default" "spans" "std" ];
+      };
+      "wasm-bindgen-backend" = rec {
+        crateName = "wasm-bindgen-backend";
+        version = "0.2.90";
+        edition = "2018";
+        sha256 = "1kcxml9762zjdrn0h0n0qxfg1n7z1f577jcc5yimi3a0cddr7p7w";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "bumpalo";
+            packageId = "bumpalo";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-futures" = rec {
+        crateName = "wasm-bindgen-futures";
+        version = "0.4.40";
+        edition = "2018";
+        sha256 = "0qf4bzlinyg0s4b38fhzdi1cqdd7rgrywqdjr3ngmgc6xcm07qmx";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+          {
+            name = "web-sys";
+            packageId = "web-sys";
+            target = { target, features }: (builtins.elem "atomics" targetFeatures);
+            features = [ "MessageEvent" "Worker" ];
+          }
+        ];
+        features = {
+          "futures-core" = [ "dep:futures-core" ];
+          "futures-core-03-stream" = [ "futures-core" ];
+        };
+      };
+      "wasm-bindgen-macro" = rec {
+        crateName = "wasm-bindgen-macro";
+        version = "0.2.90";
+        edition = "2018";
+        sha256 = "16d980bql7y5krfqlmcr8mk1q4mrm0rmb0a99j92im5jc62j6k1y";
+        procMacro = true;
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "wasm-bindgen-macro-support";
+            packageId = "wasm-bindgen-macro-support";
+          }
+        ];
+        features = {
+          "spans" = [ "wasm-bindgen-macro-support/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro-support/strict-macro" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro-support" = rec {
+        crateName = "wasm-bindgen-macro-support";
+        version = "0.2.90";
+        edition = "2018";
+        sha256 = "19r5bsyjw0fvim7dsj8pbwrq8v0ggh845lhfasgavhbdh2vapqds";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "visit" "full" ];
+          }
+          {
+            name = "wasm-bindgen-backend";
+            packageId = "wasm-bindgen-backend";
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+          "spans" = [ "wasm-bindgen-backend/spans" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-shared" = rec {
+        crateName = "wasm-bindgen-shared";
+        version = "0.2.90";
+        edition = "2018";
+        links = "wasm_bindgen";
+        sha256 = "0av0m0shdg1jxhf66ymjbq03m0qb7ypm297glndm7mri3hxl34ad";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+
+      };
+      "wasm-streams" = rec {
+        crateName = "wasm-streams";
+        version = "0.3.0";
+        edition = "2021";
+        sha256 = "1iqa4kmhbsjj8k4q15i1x0x4p3xda0dhbg7zw51mydr4g129sq5l";
+        type = [ "cdylib" "rlib" ];
+        authors = [
+          "Mattias Buelens <mattias@buelens.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            features = [ "io" "sink" ];
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+          {
+            name = "wasm-bindgen-futures";
+            packageId = "wasm-bindgen-futures";
+          }
+          {
+            name = "web-sys";
+            packageId = "web-sys";
+            features = [ "AbortSignal" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "web-sys";
+            packageId = "web-sys";
+            features = [ "console" "AbortSignal" "Response" "ReadableStream" "Window" ];
+          }
+        ];
+
+      };
+      "web-sys" = rec {
+        crateName = "web-sys";
+        version = "0.3.67";
+        edition = "2018";
+        sha256 = "1vfjjj3i49gy8bh8znnqhak1hx7xj9c2a3jzc0wpmgp0nqrj7kaq";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+        ];
+        features = {
+          "AbortSignal" = [ "EventTarget" ];
+          "AnalyserNode" = [ "AudioNode" "EventTarget" ];
+          "Animation" = [ "EventTarget" ];
+          "AnimationEvent" = [ "Event" ];
+          "AnimationPlaybackEvent" = [ "Event" ];
+          "Attr" = [ "EventTarget" "Node" ];
+          "AudioBufferSourceNode" = [ "AudioNode" "AudioScheduledSourceNode" "EventTarget" ];
+          "AudioContext" = [ "BaseAudioContext" "EventTarget" ];
+          "AudioDestinationNode" = [ "AudioNode" "EventTarget" ];
+          "AudioNode" = [ "EventTarget" ];
+          "AudioProcessingEvent" = [ "Event" ];
+          "AudioScheduledSourceNode" = [ "AudioNode" "EventTarget" ];
+          "AudioStreamTrack" = [ "EventTarget" "MediaStreamTrack" ];
+          "AudioTrackList" = [ "EventTarget" ];
+          "AudioWorklet" = [ "Worklet" ];
+          "AudioWorkletGlobalScope" = [ "WorkletGlobalScope" ];
+          "AudioWorkletNode" = [ "AudioNode" "EventTarget" ];
+          "AuthenticatorAssertionResponse" = [ "AuthenticatorResponse" ];
+          "AuthenticatorAttestationResponse" = [ "AuthenticatorResponse" ];
+          "BaseAudioContext" = [ "EventTarget" ];
+          "BatteryManager" = [ "EventTarget" ];
+          "BeforeUnloadEvent" = [ "Event" ];
+          "BiquadFilterNode" = [ "AudioNode" "EventTarget" ];
+          "BlobEvent" = [ "Event" ];
+          "Bluetooth" = [ "EventTarget" ];
+          "BluetoothAdvertisingEvent" = [ "Event" ];
+          "BluetoothDevice" = [ "EventTarget" ];
+          "BluetoothPermissionResult" = [ "EventTarget" "PermissionStatus" ];
+          "BluetoothRemoteGattCharacteristic" = [ "EventTarget" ];
+          "BluetoothRemoteGattService" = [ "EventTarget" ];
+          "BroadcastChannel" = [ "EventTarget" ];
+          "CanvasCaptureMediaStream" = [ "EventTarget" "MediaStream" ];
+          "CanvasCaptureMediaStreamTrack" = [ "EventTarget" "MediaStreamTrack" ];
+          "CdataSection" = [ "CharacterData" "EventTarget" "Node" "Text" ];
+          "ChannelMergerNode" = [ "AudioNode" "EventTarget" ];
+          "ChannelSplitterNode" = [ "AudioNode" "EventTarget" ];
+          "CharacterData" = [ "EventTarget" "Node" ];
+          "ChromeWorker" = [ "EventTarget" "Worker" ];
+          "Clipboard" = [ "EventTarget" ];
+          "ClipboardEvent" = [ "Event" ];
+          "CloseEvent" = [ "Event" ];
+          "Comment" = [ "CharacterData" "EventTarget" "Node" ];
+          "CompositionEvent" = [ "Event" "UiEvent" ];
+          "ConstantSourceNode" = [ "AudioNode" "AudioScheduledSourceNode" "EventTarget" ];
+          "ConvolverNode" = [ "AudioNode" "EventTarget" ];
+          "CssAnimation" = [ "Animation" "EventTarget" ];
+          "CssConditionRule" = [ "CssGroupingRule" "CssRule" ];
+          "CssCounterStyleRule" = [ "CssRule" ];
+          "CssFontFaceRule" = [ "CssRule" ];
+          "CssFontFeatureValuesRule" = [ "CssRule" ];
+          "CssGroupingRule" = [ "CssRule" ];
+          "CssImportRule" = [ "CssRule" ];
+          "CssKeyframeRule" = [ "CssRule" ];
+          "CssKeyframesRule" = [ "CssRule" ];
+          "CssMediaRule" = [ "CssConditionRule" "CssGroupingRule" "CssRule" ];
+          "CssNamespaceRule" = [ "CssRule" ];
+          "CssPageRule" = [ "CssRule" ];
+          "CssStyleRule" = [ "CssRule" ];
+          "CssStyleSheet" = [ "StyleSheet" ];
+          "CssSupportsRule" = [ "CssConditionRule" "CssGroupingRule" "CssRule" ];
+          "CssTransition" = [ "Animation" "EventTarget" ];
+          "CustomEvent" = [ "Event" ];
+          "DedicatedWorkerGlobalScope" = [ "EventTarget" "WorkerGlobalScope" ];
+          "DelayNode" = [ "AudioNode" "EventTarget" ];
+          "DeviceLightEvent" = [ "Event" ];
+          "DeviceMotionEvent" = [ "Event" ];
+          "DeviceOrientationEvent" = [ "Event" ];
+          "DeviceProximityEvent" = [ "Event" ];
+          "Document" = [ "EventTarget" "Node" ];
+          "DocumentFragment" = [ "EventTarget" "Node" ];
+          "DocumentTimeline" = [ "AnimationTimeline" ];
+          "DocumentType" = [ "EventTarget" "Node" ];
+          "DomMatrix" = [ "DomMatrixReadOnly" ];
+          "DomPoint" = [ "DomPointReadOnly" ];
+          "DomRect" = [ "DomRectReadOnly" ];
+          "DomRequest" = [ "EventTarget" ];
+          "DragEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "DynamicsCompressorNode" = [ "AudioNode" "EventTarget" ];
+          "Element" = [ "EventTarget" "Node" ];
+          "ErrorEvent" = [ "Event" ];
+          "EventSource" = [ "EventTarget" ];
+          "ExtendableEvent" = [ "Event" ];
+          "ExtendableMessageEvent" = [ "Event" "ExtendableEvent" ];
+          "FetchEvent" = [ "Event" "ExtendableEvent" ];
+          "FetchObserver" = [ "EventTarget" ];
+          "File" = [ "Blob" ];
+          "FileReader" = [ "EventTarget" ];
+          "FileSystemDirectoryEntry" = [ "FileSystemEntry" ];
+          "FileSystemDirectoryHandle" = [ "FileSystemHandle" ];
+          "FileSystemFileEntry" = [ "FileSystemEntry" ];
+          "FileSystemFileHandle" = [ "FileSystemHandle" ];
+          "FileSystemWritableFileStream" = [ "WritableStream" ];
+          "FocusEvent" = [ "Event" "UiEvent" ];
+          "FontFaceSet" = [ "EventTarget" ];
+          "FontFaceSetLoadEvent" = [ "Event" ];
+          "GainNode" = [ "AudioNode" "EventTarget" ];
+          "GamepadAxisMoveEvent" = [ "Event" "GamepadEvent" ];
+          "GamepadButtonEvent" = [ "Event" "GamepadEvent" ];
+          "GamepadEvent" = [ "Event" ];
+          "GpuDevice" = [ "EventTarget" ];
+          "GpuInternalError" = [ "GpuError" ];
+          "GpuOutOfMemoryError" = [ "GpuError" ];
+          "GpuPipelineError" = [ "DomException" ];
+          "GpuUncapturedErrorEvent" = [ "Event" ];
+          "GpuValidationError" = [ "GpuError" ];
+          "HashChangeEvent" = [ "Event" ];
+          "Hid" = [ "EventTarget" ];
+          "HidConnectionEvent" = [ "Event" ];
+          "HidDevice" = [ "EventTarget" ];
+          "HidInputReportEvent" = [ "Event" ];
+          "HtmlAnchorElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlAreaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlAudioElement" = [ "Element" "EventTarget" "HtmlElement" "HtmlMediaElement" "Node" ];
+          "HtmlBaseElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlBodyElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlBrElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlButtonElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlCanvasElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDataElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDataListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDetailsElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDialogElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDirectoryElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDivElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDocument" = [ "Document" "EventTarget" "Node" ];
+          "HtmlElement" = [ "Element" "EventTarget" "Node" ];
+          "HtmlEmbedElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFieldSetElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFontElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFormControlsCollection" = [ "HtmlCollection" ];
+          "HtmlFormElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFrameElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFrameSetElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHeadElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHeadingElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHrElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHtmlElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlIFrameElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlImageElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlInputElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLabelElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLegendElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLiElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLinkElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMapElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMediaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMenuElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMenuItemElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMetaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMeterElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlModElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlObjectElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOptGroupElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOptionElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOptionsCollection" = [ "HtmlCollection" ];
+          "HtmlOutputElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlParagraphElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlParamElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlPictureElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlPreElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlProgressElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlQuoteElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlScriptElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSelectElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSlotElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSourceElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSpanElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlStyleElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableCaptionElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableCellElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableColElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableRowElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableSectionElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTemplateElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTextAreaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTimeElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTitleElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTrackElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlUListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlUnknownElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlVideoElement" = [ "Element" "EventTarget" "HtmlElement" "HtmlMediaElement" "Node" ];
+          "IdbCursorWithValue" = [ "IdbCursor" ];
+          "IdbDatabase" = [ "EventTarget" ];
+          "IdbFileHandle" = [ "EventTarget" ];
+          "IdbFileRequest" = [ "DomRequest" "EventTarget" ];
+          "IdbLocaleAwareKeyRange" = [ "IdbKeyRange" ];
+          "IdbMutableFile" = [ "EventTarget" ];
+          "IdbOpenDbRequest" = [ "EventTarget" "IdbRequest" ];
+          "IdbRequest" = [ "EventTarget" ];
+          "IdbTransaction" = [ "EventTarget" ];
+          "IdbVersionChangeEvent" = [ "Event" ];
+          "IirFilterNode" = [ "AudioNode" "EventTarget" ];
+          "ImageCaptureErrorEvent" = [ "Event" ];
+          "ImageTrack" = [ "EventTarget" ];
+          "InputEvent" = [ "Event" "UiEvent" ];
+          "KeyboardEvent" = [ "Event" "UiEvent" ];
+          "KeyframeEffect" = [ "AnimationEffect" ];
+          "LocalMediaStream" = [ "EventTarget" "MediaStream" ];
+          "MediaDevices" = [ "EventTarget" ];
+          "MediaElementAudioSourceNode" = [ "AudioNode" "EventTarget" ];
+          "MediaEncryptedEvent" = [ "Event" ];
+          "MediaKeyError" = [ "Event" ];
+          "MediaKeyMessageEvent" = [ "Event" ];
+          "MediaKeySession" = [ "EventTarget" ];
+          "MediaQueryList" = [ "EventTarget" ];
+          "MediaQueryListEvent" = [ "Event" ];
+          "MediaRecorder" = [ "EventTarget" ];
+          "MediaRecorderErrorEvent" = [ "Event" ];
+          "MediaSource" = [ "EventTarget" ];
+          "MediaStream" = [ "EventTarget" ];
+          "MediaStreamAudioDestinationNode" = [ "AudioNode" "EventTarget" ];
+          "MediaStreamAudioSourceNode" = [ "AudioNode" "EventTarget" ];
+          "MediaStreamEvent" = [ "Event" ];
+          "MediaStreamTrack" = [ "EventTarget" ];
+          "MediaStreamTrackEvent" = [ "Event" ];
+          "MediaStreamTrackGenerator" = [ "EventTarget" "MediaStreamTrack" ];
+          "MessageEvent" = [ "Event" ];
+          "MessagePort" = [ "EventTarget" ];
+          "MidiAccess" = [ "EventTarget" ];
+          "MidiConnectionEvent" = [ "Event" ];
+          "MidiInput" = [ "EventTarget" "MidiPort" ];
+          "MidiMessageEvent" = [ "Event" ];
+          "MidiOutput" = [ "EventTarget" "MidiPort" ];
+          "MidiPort" = [ "EventTarget" ];
+          "MouseEvent" = [ "Event" "UiEvent" ];
+          "MouseScrollEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "MutationEvent" = [ "Event" ];
+          "NetworkInformation" = [ "EventTarget" ];
+          "Node" = [ "EventTarget" ];
+          "Notification" = [ "EventTarget" ];
+          "NotificationEvent" = [ "Event" "ExtendableEvent" ];
+          "OfflineAudioCompletionEvent" = [ "Event" ];
+          "OfflineAudioContext" = [ "BaseAudioContext" "EventTarget" ];
+          "OfflineResourceList" = [ "EventTarget" ];
+          "OffscreenCanvas" = [ "EventTarget" ];
+          "OscillatorNode" = [ "AudioNode" "AudioScheduledSourceNode" "EventTarget" ];
+          "PageTransitionEvent" = [ "Event" ];
+          "PaintWorkletGlobalScope" = [ "WorkletGlobalScope" ];
+          "PannerNode" = [ "AudioNode" "EventTarget" ];
+          "PaymentMethodChangeEvent" = [ "Event" "PaymentRequestUpdateEvent" ];
+          "PaymentRequestUpdateEvent" = [ "Event" ];
+          "Performance" = [ "EventTarget" ];
+          "PerformanceMark" = [ "PerformanceEntry" ];
+          "PerformanceMeasure" = [ "PerformanceEntry" ];
+          "PerformanceNavigationTiming" = [ "PerformanceEntry" "PerformanceResourceTiming" ];
+          "PerformanceResourceTiming" = [ "PerformanceEntry" ];
+          "PermissionStatus" = [ "EventTarget" ];
+          "PointerEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "PopStateEvent" = [ "Event" ];
+          "PopupBlockedEvent" = [ "Event" ];
+          "PresentationAvailability" = [ "EventTarget" ];
+          "PresentationConnection" = [ "EventTarget" ];
+          "PresentationConnectionAvailableEvent" = [ "Event" ];
+          "PresentationConnectionCloseEvent" = [ "Event" ];
+          "PresentationConnectionList" = [ "EventTarget" ];
+          "PresentationRequest" = [ "EventTarget" ];
+          "ProcessingInstruction" = [ "CharacterData" "EventTarget" "Node" ];
+          "ProgressEvent" = [ "Event" ];
+          "PromiseRejectionEvent" = [ "Event" ];
+          "PublicKeyCredential" = [ "Credential" ];
+          "PushEvent" = [ "Event" "ExtendableEvent" ];
+          "RadioNodeList" = [ "NodeList" ];
+          "RtcDataChannel" = [ "EventTarget" ];
+          "RtcDataChannelEvent" = [ "Event" ];
+          "RtcPeerConnection" = [ "EventTarget" ];
+          "RtcPeerConnectionIceEvent" = [ "Event" ];
+          "RtcTrackEvent" = [ "Event" ];
+          "RtcdtmfSender" = [ "EventTarget" ];
+          "RtcdtmfToneChangeEvent" = [ "Event" ];
+          "Screen" = [ "EventTarget" ];
+          "ScreenOrientation" = [ "EventTarget" ];
+          "ScriptProcessorNode" = [ "AudioNode" "EventTarget" ];
+          "ScrollAreaEvent" = [ "Event" "UiEvent" ];
+          "SecurityPolicyViolationEvent" = [ "Event" ];
+          "Serial" = [ "EventTarget" ];
+          "SerialPort" = [ "EventTarget" ];
+          "ServiceWorker" = [ "EventTarget" ];
+          "ServiceWorkerContainer" = [ "EventTarget" ];
+          "ServiceWorkerGlobalScope" = [ "EventTarget" "WorkerGlobalScope" ];
+          "ServiceWorkerRegistration" = [ "EventTarget" ];
+          "ShadowRoot" = [ "DocumentFragment" "EventTarget" "Node" ];
+          "SharedWorker" = [ "EventTarget" ];
+          "SharedWorkerGlobalScope" = [ "EventTarget" "WorkerGlobalScope" ];
+          "SourceBuffer" = [ "EventTarget" ];
+          "SourceBufferList" = [ "EventTarget" ];
+          "SpeechRecognition" = [ "EventTarget" ];
+          "SpeechRecognitionError" = [ "Event" ];
+          "SpeechRecognitionEvent" = [ "Event" ];
+          "SpeechSynthesis" = [ "EventTarget" ];
+          "SpeechSynthesisErrorEvent" = [ "Event" "SpeechSynthesisEvent" ];
+          "SpeechSynthesisEvent" = [ "Event" ];
+          "SpeechSynthesisUtterance" = [ "EventTarget" ];
+          "StereoPannerNode" = [ "AudioNode" "EventTarget" ];
+          "StorageEvent" = [ "Event" ];
+          "SubmitEvent" = [ "Event" ];
+          "SvgAnimateElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgAnimateMotionElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgAnimateTransformElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgAnimationElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgCircleElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgClipPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgComponentTransferFunctionElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgDefsElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgDescElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgElement" = [ "Element" "EventTarget" "Node" ];
+          "SvgEllipseElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgFilterElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgForeignObjectElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgGeometryElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgGradientElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgGraphicsElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgImageElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgLineElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgLinearGradientElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGradientElement" ];
+          "SvgMarkerElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgMaskElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgMetadataElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgPathSegArcAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegArcRel" = [ "SvgPathSeg" ];
+          "SvgPathSegClosePath" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicRel" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicSmoothAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicSmoothRel" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticRel" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticSmoothAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticSmoothRel" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoHorizontalAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoHorizontalRel" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoRel" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoVerticalAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoVerticalRel" = [ "SvgPathSeg" ];
+          "SvgPathSegMovetoAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegMovetoRel" = [ "SvgPathSeg" ];
+          "SvgPatternElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgPolygonElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgPolylineElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgRadialGradientElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGradientElement" ];
+          "SvgRectElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgScriptElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgSetElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgStopElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgStyleElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgSwitchElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgSymbolElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgTextContentElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgTextElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" "SvgTextPositioningElement" ];
+          "SvgTextPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" ];
+          "SvgTextPositioningElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" ];
+          "SvgTitleElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgUseElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgViewElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgaElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgfeBlendElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeColorMatrixElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeComponentTransferElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeCompositeElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeConvolveMatrixElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDiffuseLightingElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDisplacementMapElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDistantLightElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDropShadowElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeFloodElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeFuncAElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeFuncBElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeFuncGElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeFuncRElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeGaussianBlurElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeImageElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeMergeElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeMergeNodeElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeMorphologyElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeOffsetElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfePointLightElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeSpecularLightingElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeSpotLightElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeTileElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeTurbulenceElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvggElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgmPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgsvgElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgtSpanElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" "SvgTextPositioningElement" ];
+          "TaskController" = [ "AbortController" ];
+          "TaskPriorityChangeEvent" = [ "Event" ];
+          "TaskSignal" = [ "AbortSignal" "EventTarget" ];
+          "TcpServerSocket" = [ "EventTarget" ];
+          "TcpServerSocketEvent" = [ "Event" ];
+          "TcpSocket" = [ "EventTarget" ];
+          "TcpSocketErrorEvent" = [ "Event" ];
+          "TcpSocketEvent" = [ "Event" ];
+          "Text" = [ "CharacterData" "EventTarget" "Node" ];
+          "TextTrack" = [ "EventTarget" ];
+          "TextTrackCue" = [ "EventTarget" ];
+          "TextTrackList" = [ "EventTarget" ];
+          "TimeEvent" = [ "Event" ];
+          "TouchEvent" = [ "Event" "UiEvent" ];
+          "TrackEvent" = [ "Event" ];
+          "TransitionEvent" = [ "Event" ];
+          "UiEvent" = [ "Event" ];
+          "Usb" = [ "EventTarget" ];
+          "UsbConnectionEvent" = [ "Event" ];
+          "UsbPermissionResult" = [ "EventTarget" "PermissionStatus" ];
+          "UserProximityEvent" = [ "Event" ];
+          "ValueEvent" = [ "Event" ];
+          "VideoStreamTrack" = [ "EventTarget" "MediaStreamTrack" ];
+          "VideoTrackList" = [ "EventTarget" ];
+          "VrDisplay" = [ "EventTarget" ];
+          "VttCue" = [ "EventTarget" "TextTrackCue" ];
+          "WakeLockSentinel" = [ "EventTarget" ];
+          "WaveShaperNode" = [ "AudioNode" "EventTarget" ];
+          "WebGlContextEvent" = [ "Event" ];
+          "WebKitCssMatrix" = [ "DomMatrix" "DomMatrixReadOnly" ];
+          "WebSocket" = [ "EventTarget" ];
+          "WebTransportError" = [ "DomException" ];
+          "WebTransportReceiveStream" = [ "ReadableStream" ];
+          "WebTransportSendStream" = [ "WritableStream" ];
+          "WheelEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "Window" = [ "EventTarget" ];
+          "WindowClient" = [ "Client" ];
+          "Worker" = [ "EventTarget" ];
+          "WorkerDebuggerGlobalScope" = [ "EventTarget" ];
+          "WorkerGlobalScope" = [ "EventTarget" ];
+          "XmlDocument" = [ "Document" "EventTarget" "Node" ];
+          "XmlHttpRequest" = [ "EventTarget" "XmlHttpRequestEventTarget" ];
+          "XmlHttpRequestEventTarget" = [ "EventTarget" ];
+          "XmlHttpRequestUpload" = [ "EventTarget" "XmlHttpRequestEventTarget" ];
+          "XrBoundedReferenceSpace" = [ "EventTarget" "XrReferenceSpace" "XrSpace" ];
+          "XrInputSourceEvent" = [ "Event" ];
+          "XrInputSourcesChangeEvent" = [ "Event" ];
+          "XrJointPose" = [ "XrPose" ];
+          "XrJointSpace" = [ "EventTarget" "XrSpace" ];
+          "XrLayer" = [ "EventTarget" ];
+          "XrPermissionStatus" = [ "EventTarget" "PermissionStatus" ];
+          "XrReferenceSpace" = [ "EventTarget" "XrSpace" ];
+          "XrReferenceSpaceEvent" = [ "Event" ];
+          "XrSession" = [ "EventTarget" ];
+          "XrSessionEvent" = [ "Event" ];
+          "XrSpace" = [ "EventTarget" ];
+          "XrSystem" = [ "EventTarget" ];
+          "XrViewerPose" = [ "XrPose" ];
+          "XrWebGlLayer" = [ "EventTarget" "XrLayer" ];
+        };
+        resolvedDefaultFeatures = [ "AbortController" "AbortSignal" "Blob" "BlobPropertyBag" "CanvasRenderingContext2d" "Document" "DomRect" "DomRectReadOnly" "Element" "Event" "EventTarget" "File" "FormData" "Headers" "HtmlCanvasElement" "HtmlElement" "MessageEvent" "Node" "ReadableStream" "Request" "RequestCredentials" "RequestInit" "RequestMode" "Response" "ServiceWorkerGlobalScope" "Window" "Worker" "WorkerGlobalScope" ];
+      };
+      "web-time" = rec {
+        crateName = "web-time";
+        version = "0.2.4";
+        edition = "2021";
+        sha256 = "1q6gk0nkwbfz30g1pz8g52mq00zjx7m5im36k3474aw73jdh8c5a";
+        dependencies = [
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: ((builtins.elem "wasm" target."family") && (!(("emscripten" == target."os" or null) || ("wasi" == target."os" or null))));
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((builtins.elem "wasm" target."family") && (!(("emscripten" == target."os" or null) || ("wasi" == target."os" or null))));
+          }
+        ];
+
+      };
+      "which 4.4.2" = rec {
+        crateName = "which";
+        version = "4.4.2";
+        edition = "2021";
+        sha256 = "1ixzmx3svsv5hbdvd8vdhd3qwvf6ns8jdpif1wmwsy10k90j9fl7";
+        authors = [
+          "Harry Fei <tiziyuanfang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "home";
+            packageId = "home";
+            target = { target, features }: ((target."windows" or false) || (target."unix" or false) || ("redox" == target."os" or null));
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
+            features = [ "fs" "std" ];
+          }
+        ];
+        features = {
+          "regex" = [ "dep:regex" ];
+        };
+      };
+      "which 5.0.0" = rec {
+        crateName = "which";
+        version = "5.0.0";
+        edition = "2021";
+        sha256 = "053fpbczryyn8lcbpkvwl8v2rzld0pr30r5lh1cxv87kjs2ymwwv";
+        authors = [
+          "Harry Fei <tiziyuanfang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "home";
+            packageId = "home";
+            target = { target, features }: ((target."windows" or false) || (target."unix" or false) || ("redox" == target."os" or null));
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
+            features = [ "fs" "std" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Storage_FileSystem" "Win32_Foundation" ];
+          }
+        ];
+        features = {
+          "regex" = [ "dep:regex" ];
+        };
+      };
+      "winapi" = rec {
+        crateName = "winapi";
+        version = "0.3.9";
+        edition = "2015";
+        sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-i686-pc-windows-gnu";
+            packageId = "winapi-i686-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+          }
+          {
+            name = "winapi-x86_64-pc-windows-gnu";
+            packageId = "winapi-x86_64-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+          }
+        ];
+        features = {
+          "debug" = [ "impl-debug" ];
+        };
+        resolvedDefaultFeatures = [ "basetsd" "consoleapi" "errhandlingapi" "fileapi" "handleapi" "knownfolders" "minwindef" "ntstatus" "objbase" "processenv" "processthreadsapi" "shellapi" "shlobj" "std" "stringapiset" "synchapi" "sysinfoapi" "winbase" "wincon" "winerror" "winnt" "winuser" ];
+      };
+      "winapi-i686-pc-windows-gnu" = rec {
+        crateName = "winapi-i686-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi-util" = rec {
+        crateName = "winapi-util";
+        version = "0.1.6";
+        edition = "2021";
+        sha256 = "15i5lm39wd44004i9d5qspry2cynkrpvwzghr6s2c3dsk28nz7pj";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "std" "consoleapi" "errhandlingapi" "fileapi" "minwindef" "processenv" "sysinfoapi" "winbase" "wincon" "winerror" "winnt" ];
+          }
+        ];
+
+      };
+      "winapi-x86_64-pc-windows-gnu" = rec {
+        crateName = "winapi-x86_64-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "windows-core" = rec {
+        crateName = "windows-core";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1nc3qv7sy24x0nlnb32f7alzpd6f72l4p24vl65vydbyil669ark";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "windows-sys 0.48.0" = rec {
+        crateName = "windows-sys";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+          }
+        ];
+        features = {
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_Pipes" "Win32_System_Registry" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_Time" "Win32_System_WindowsProgramming" "default" ];
+      };
+      "windows-sys 0.52.0" = rec {
+        crateName = "windows-sys";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+          }
+        ];
+        features = {
+          "Wdk_Foundation" = [ "Wdk" ];
+          "Wdk_Graphics" = [ "Wdk" ];
+          "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ];
+          "Wdk_Storage" = [ "Wdk" ];
+          "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ];
+          "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ];
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_IO" = [ "Wdk_System" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Wdk_System_Registry" = [ "Wdk_System" ];
+          "Wdk_System_SystemInformation" = [ "Wdk_System" ];
+          "Wdk_System_SystemServices" = [ "Wdk_System" ];
+          "Wdk_System_Threading" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_Nvme" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_Variant" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Wdk" "Wdk_Foundation" "Wdk_Storage" "Wdk_Storage_FileSystem" "Win32" "Win32_Foundation" "Win32_NetworkManagement" "Win32_NetworkManagement_IpHelper" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_Security_Cryptography" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Com" "Win32_System_Console" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_LibraryLoader" "Win32_System_Memory" "Win32_System_Threading" "Win32_System_WindowsProgramming" "Win32_UI" "Win32_UI_Shell" "default" ];
+      };
+      "windows-targets 0.48.5" = rec {
+        crateName = "windows-targets";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.48.5";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows-targets 0.52.0" = rec {
+        crateName = "windows-targets";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1kg7a27ynzw8zz3krdgy6w5gbqcji27j1sz4p7xk2j5j8082064a";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.52.0";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.52.0";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.52.0";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.52.0";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.52.0";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.52.0";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.52.0";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.48.5" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.52.0" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1shmn1kbdc0bpphcxz0vlph96bxz0h1jlmh93s9agf2dbpin8xyb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.48.5" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.52.0" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1vvmy1ypvzdvxn9yf0b8ygfl85gl2gpcyvsvqppsmlpisil07amv";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.48.5" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.52.0" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "04zkglz4p3pjsns5gbz85v4s5aw102raz4spj4b0lmm33z5kg1m2";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.48.5" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.52.0" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "16kvmbvx0vr0zbgnaz6nsks9ycvfh5xp05bjrhq65kj623iyirgz";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.48.5" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.52.0" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1zdy4qn178sil5sdm63lm7f0kkcjg6gvdwmcprd2yjmwn8ns6vrx";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.48.5" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.52.0" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "17lllq4l2k1lqgcnw1cccphxp9vs7inq99kjlm2lfl9zklg7wr8s";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.48.5" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.52.0" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "012wfq37f18c09ij5m6rniw7xxn5fcvrxbqd0wd8vgnl3hfn9yfz";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "winreg" = rec {
+        crateName = "winreg";
+        version = "0.50.0";
+        edition = "2018";
+        sha256 = "1cddmp929k882mdh6i9f2as848f13qqna6czwsqzkh1pqnr5fkjj";
+        authors = [
+          "Igor Shaula <gentoo90@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            features = [ "Win32_Foundation" "Win32_System_Time" "Win32_System_Registry" "Win32_Security" "Win32_Storage_FileSystem" "Win32_System_Diagnostics_Debug" ];
+          }
+        ];
+        features = {
+          "chrono" = [ "dep:chrono" ];
+          "serde" = [ "dep:serde" ];
+          "serialization-serde" = [ "transactions" "serde" ];
+        };
+      };
+      "wu-manber" = rec {
+        crateName = "wu-manber";
+        version = "0.1.0";
+        edition = "2015";
+        workspace_member = null;
+        src = pkgs.fetchgit {
+          url = "https://github.com/tvlfyi/wu-manber.git";
+          rev = "0d5b22bea136659f7de60b102a7030e0daaa503d";
+          sha256 = "1zhk83lbq99xzyjwphv2qrb8f8qgfqwa5bbbvyzm0z0bljsjv0pd";
+        };
+        authors = [
+          "Joe Neeman <joeneeman@gmail.com>"
+        ];
+
+      };
+      "xattr" = rec {
+        crateName = "xattr";
+        version = "1.3.1";
+        edition = "2021";
+        sha256 = "0kqxm36w89vc6qcpn6pizlhgjgzq138sx4hdhbv2g6wk4ld4za4d";
+        authors = [
+          "Steven Allen <steven@stebalien.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("freebsd" == target."os" or null) || ("netbsd" == target."os" or null));
+          }
+          {
+            name = "linux-raw-sys";
+            packageId = "linux-raw-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("linux" == target."os" or null);
+            features = [ "std" ];
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
+            features = [ "fs" "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
+            features = [ "net" ];
+          }
+        ];
+        features = {
+          "default" = [ "unsupported" ];
+        };
+        resolvedDefaultFeatures = [ "default" "unsupported" ];
+      };
+      "xml-rs" = rec {
+        crateName = "xml-rs";
+        version = "0.8.19";
+        edition = "2021";
+        crateBin = [ ];
+        sha256 = "0nnpvk3fv32hgh7vs9gbg2swmzxx5yz73f4b7rak7q39q2x9rjqg";
+        libName = "xml";
+        authors = [
+          "Vladimir Matveev <vmatveev@citrine.cc>"
+        ];
+
+      };
+      "xz2" = rec {
+        crateName = "xz2";
+        version = "0.1.7";
+        edition = "2018";
+        sha256 = "1qk7nzpblizvayyq4xzi4b0zacmmbqr6vb9fc0v1avyp17f4931q";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "lzma-sys";
+            packageId = "lzma-sys";
+          }
+        ];
+        features = {
+          "futures" = [ "dep:futures" ];
+          "static" = [ "lzma-sys/static" ];
+          "tokio" = [ "tokio-io" "futures" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+        };
+      };
+      "yansi" = rec {
+        crateName = "yansi";
+        version = "0.5.1";
+        edition = "2015";
+        sha256 = "1v4qljgzh73knr7291cgwrf56zrvhmpn837n5n5pypzq1kciq109";
+        authors = [
+          "Sergio Benitez <sb@sergio.bz>"
+        ];
+
+      };
+      "zeroize" = rec {
+        crateName = "zeroize";
+        version = "1.7.0";
+        edition = "2021";
+        sha256 = "0bfvby7k9pdp6623p98yz2irqnamcyzpn7zh20nqmdn68b0lwnsj";
+        authors = [
+          "The RustCrypto Project Developers"
+        ];
+        features = {
+          "default" = [ "alloc" ];
+          "derive" = [ "zeroize_derive" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "zeroize_derive" = [ "dep:zeroize_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" ];
+      };
+      "zstd" = rec {
+        crateName = "zstd";
+        version = "0.13.0";
+        edition = "2018";
+        sha256 = "0401q54s9r35x2i7m1kwppgkj79g0pb6xz3xpby7qlkdb44k7yxz";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "zstd-safe";
+            packageId = "zstd-safe";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        features = {
+          "arrays" = [ "zstd-safe/arrays" ];
+          "bindgen" = [ "zstd-safe/bindgen" ];
+          "debug" = [ "zstd-safe/debug" ];
+          "default" = [ "legacy" "arrays" "zdict_builder" ];
+          "experimental" = [ "zstd-safe/experimental" ];
+          "fat-lto" = [ "zstd-safe/fat-lto" ];
+          "legacy" = [ "zstd-safe/legacy" ];
+          "no_asm" = [ "zstd-safe/no_asm" ];
+          "pkg-config" = [ "zstd-safe/pkg-config" ];
+          "thin" = [ "zstd-safe/thin" ];
+          "thin-lto" = [ "zstd-safe/thin-lto" ];
+          "zdict_builder" = [ "zstd-safe/zdict_builder" ];
+          "zstdmt" = [ "zstd-safe/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "default" "legacy" "zdict_builder" ];
+      };
+      "zstd-safe" = rec {
+        crateName = "zstd-safe";
+        version = "7.0.0";
+        edition = "2018";
+        sha256 = "0gpav2lcibrpmyslmjkcn3w0w64qif3jjljd2h8lr4p249s7qx23";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "zstd-sys";
+            packageId = "zstd-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "bindgen" = [ "zstd-sys/bindgen" ];
+          "debug" = [ "zstd-sys/debug" ];
+          "default" = [ "legacy" "arrays" "zdict_builder" ];
+          "experimental" = [ "zstd-sys/experimental" ];
+          "fat-lto" = [ "zstd-sys/fat-lto" ];
+          "legacy" = [ "zstd-sys/legacy" ];
+          "no_asm" = [ "zstd-sys/no_asm" ];
+          "pkg-config" = [ "zstd-sys/pkg-config" ];
+          "std" = [ "zstd-sys/std" ];
+          "thin" = [ "zstd-sys/thin" ];
+          "thin-lto" = [ "zstd-sys/thin-lto" ];
+          "zdict_builder" = [ "zstd-sys/zdict_builder" ];
+          "zstdmt" = [ "zstd-sys/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "legacy" "std" "zdict_builder" ];
+      };
+      "zstd-sys" = rec {
+        crateName = "zstd-sys";
+        version = "2.0.9+zstd.1.5.5";
+        edition = "2018";
+        links = "zstd";
+        sha256 = "0mk6a2367swdi22zg03lcackpnvgq96d7120awd4i83lm2lfy5ly";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            features = [ "parallel" ];
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+        ];
+        features = {
+          "bindgen" = [ "dep:bindgen" ];
+          "default" = [ "legacy" "zdict_builder" ];
+        };
+        resolvedDefaultFeatures = [ "legacy" "std" "zdict_builder" ];
+      };
+    };
+
+    #
+    # crate2nix/default.nix (excerpt start)
+    #
+
+    /* Target (platform) data for conditional dependencies.
+      This corresponds roughly to what buildRustCrate is setting.
+    */
+    makeDefaultTarget = platform: {
+      unix = platform.isUnix;
+      windows = platform.isWindows;
+      fuchsia = true;
+      test = false;
+
+      /* We are choosing an arbitrary rust version to grab `lib` from,
+      which is unfortunate, but `lib` has been version-agnostic the
+      whole time so this is good enough for now.
+      */
+      os = pkgs.rust.lib.toTargetOs platform;
+      arch = pkgs.rust.lib.toTargetArch platform;
+      family = pkgs.rust.lib.toTargetFamily platform;
+      vendor = pkgs.rust.lib.toTargetVendor platform;
+      env = "gnu";
+      endian =
+        if platform.parsed.cpu.significantByte.name == "littleEndian"
+        then "little" else "big";
+      pointer_width = toString platform.parsed.cpu.bits;
+      debug_assertions = false;
+    };
+
+    /* Filters common temp files and build files. */
+    # TODO(pkolloch): Substitute with gitignore filter
+    sourceFilter = name: type:
+      let
+        baseName = builtins.baseNameOf (builtins.toString name);
+      in
+        ! (
+          # Filter out git
+          baseName == ".gitignore"
+          || (type == "directory" && baseName == ".git")
+
+          # Filter out build results
+          || (
+            type == "directory" && (
+              baseName == "target"
+              || baseName == "_site"
+              || baseName == ".sass-cache"
+              || baseName == ".jekyll-metadata"
+              || baseName == "build-artifacts"
+            )
+          )
+
+          # Filter out nix-build result symlinks
+          || (
+            type == "symlink" && lib.hasPrefix "result" baseName
+          )
+
+          # Filter out IDE config
+          || (
+            type == "directory" && (
+              baseName == ".idea" || baseName == ".vscode"
+            )
+          ) || lib.hasSuffix ".iml" baseName
+
+          # Filter out nix build files
+          || baseName == "Cargo.nix"
+
+          # Filter out editor backup / swap files.
+          || lib.hasSuffix "~" baseName
+          || builtins.match "^\\.sw[a-z]$$" baseName != null
+          || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null
+          || lib.hasSuffix ".tmp" baseName
+          || lib.hasSuffix ".bak" baseName
+          || baseName == "tests.nix"
+        );
+
+    /* Returns a crate which depends on successful test execution
+      of crate given as the second argument.
+
+      testCrateFlags: list of flags to pass to the test exectuable
+      testInputs: list of packages that should be available during test execution
+    */
+    crateWithTest = { crate, testCrate, testCrateFlags, testInputs, testPreRun, testPostRun }:
+      assert builtins.typeOf testCrateFlags == "list";
+      assert builtins.typeOf testInputs == "list";
+      assert builtins.typeOf testPreRun == "string";
+      assert builtins.typeOf testPostRun == "string";
+      let
+        # override the `crate` so that it will build and execute tests instead of
+        # building the actual lib and bin targets We just have to pass `--test`
+        # to rustc and it will do the right thing.  We execute the tests and copy
+        # their log and the test executables to $out for later inspection.
+        test =
+          let
+            drv = testCrate.override
+              (
+                _: {
+                  buildTests = true;
+                  release = false;
+                }
+              );
+            # If the user hasn't set any pre/post commands, we don't want to
+            # insert empty lines. This means that any existing users of crate2nix
+            # don't get a spurious rebuild unless they set these explicitly.
+            testCommand = pkgs.lib.concatStringsSep "\n"
+              (pkgs.lib.filter (s: s != "") [
+                testPreRun
+                "$f $testCrateFlags 2>&1 | tee -a $out"
+                testPostRun
+              ]);
+          in
+          pkgs.runCommand "run-tests-${testCrate.name}"
+            {
+              inherit testCrateFlags;
+              buildInputs = testInputs;
+            } ''
+            set -e
+
+            export RUST_BACKTRACE=1
+
+            # recreate a file hierarchy as when running tests with cargo
+
+            # the source for test data
+            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
+            # instead of $NIX_BUILD_TOP/
+            # because we compiled those test binaries in the former and not the latter.
+            # So all paths will expect source tree to be there and not in the build top directly.
+            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
+            # NOTE: There could be edge cases if `crate.sourceRoot` does exist but
+            # it's very hard to reason about them.
+            # Open a bug if you run into this!
+            mkdir -p source/
+            cd source/
+
+            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+
+            # build outputs
+            testRoot=target/debug
+            mkdir -p $testRoot
+
+            # executables of the crate
+            # we copy to prevent std::env::current_exe() to resolve to a store location
+            for i in ${crate}/bin/*; do
+              cp "$i" "$testRoot"
+            done
+            chmod +w -R .
+
+            # test harness executables are suffixed with a hash, like cargo does
+            # this allows to prevent name collision with the main
+            # executables of the crate
+            hash=$(basename $out)
+            for file in ${drv}/tests/*; do
+              f=$testRoot/$(basename $file)-$hash
+              cp $file $f
+              ${testCommand}
+            done
+          '';
+      in
+      pkgs.runCommand "${crate.name}-linked"
+        {
+          inherit (crate) outputs crateName;
+          passthru = (crate.passthru or { }) // {
+            inherit test;
+          };
+        }
+        (lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+          echo tested by ${test}
+        '' + ''
+          ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs}
+        '');
+
+    /* A restricted overridable version of builtRustCratesWithFeatures. */
+    buildRustCrateWithFeatures =
+      { packageId
+      , features ? rootFeatures
+      , crateOverrides ? defaultCrateOverrides
+      , buildRustCrateForPkgsFunc ? null
+      , runTests ? false
+      , testCrateFlags ? [ ]
+      , testInputs ? [ ]
+        # Any command to run immediatelly before a test is executed.
+      , testPreRun ? ""
+        # Any command run immediatelly after a test is executed.
+      , testPostRun ? ""
+      }:
+      lib.makeOverridable
+        (
+          { features
+          , crateOverrides
+          , runTests
+          , testCrateFlags
+          , testInputs
+          , testPreRun
+          , testPostRun
+          }:
+          let
+            buildRustCrateForPkgsFuncOverriden =
+              if buildRustCrateForPkgsFunc != null
+              then buildRustCrateForPkgsFunc
+              else
+                (
+                  if crateOverrides == pkgs.defaultCrateOverrides
+                  then buildRustCrateForPkgs
+                  else
+                    pkgs: (buildRustCrateForPkgs pkgs).override {
+                      defaultCrateOverrides = crateOverrides;
+                    }
+                );
+            builtRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = false;
+            };
+            builtTestRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = true;
+            };
+            drv = builtRustCrates.crates.${packageId};
+            testDrv = builtTestRustCrates.crates.${packageId};
+            derivation =
+              if runTests then
+                crateWithTest
+                  {
+                    crate = drv;
+                    testCrate = testDrv;
+                    inherit testCrateFlags testInputs testPreRun testPostRun;
+                  }
+              else drv;
+          in
+          derivation
+        )
+        { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun; };
+
+    /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc
+      for the corresponding crate.
+    */
+    builtRustCratesWithFeatures =
+      { packageId
+      , features
+      , crateConfigs ? crates
+      , buildRustCrateForPkgsFunc
+      , runTests
+      , makeTarget ? makeDefaultTarget
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isList features);
+        assert (builtins.isAttrs (makeTarget stdenv.hostPlatform));
+        assert (builtins.isBool runTests);
+        let
+          rootPackageId = packageId;
+          mergedFeatures = mergePackageFeatures
+            (
+              args // {
+                inherit rootPackageId;
+                target = makeTarget stdenv.hostPlatform // { test = runTests; };
+              }
+            );
+          # Memoize built packages so that reappearing packages are only built once.
+          builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs;
+          mkBuiltByPackageIdByPkgs = pkgs:
+            let
+              self = {
+                crates = lib.mapAttrs (packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId) crateConfigs;
+                target = makeTarget pkgs.stdenv.hostPlatform;
+                build = mkBuiltByPackageIdByPkgs pkgs.buildPackages;
+              };
+            in
+            self;
+          buildByPackageIdForPkgsImpl = self: pkgs: packageId:
+            let
+              features = mergedFeatures."${packageId}" or [ ];
+              crateConfig' = crateConfigs."${packageId}";
+              crateConfig =
+                builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ];
+              devDependencies =
+                lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig'.devDependencies or [ ]);
+              dependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self) target;
+                  buildByPackageId = depPackageId:
+                    # proc_macro crates must be compiled for the build architecture
+                    if crateConfigs.${depPackageId}.procMacro or false
+                    then self.build.crates.${depPackageId}
+                    else self.crates.${depPackageId};
+                  dependencies =
+                    (crateConfig.dependencies or [ ])
+                    ++ devDependencies;
+                };
+              buildDependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self.build) target;
+                  buildByPackageId = depPackageId:
+                    self.build.crates.${depPackageId};
+                  dependencies = crateConfig.buildDependencies or [ ];
+                };
+              dependenciesWithRenames =
+                let
+                  buildDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self) target;
+                    dependencies = crateConfig.dependencies or [ ] ++ devDependencies;
+                  };
+                  hostDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self.build) target;
+                    dependencies = crateConfig.buildDependencies or [ ];
+                  };
+                in
+                lib.filter (d: d ? "rename") (hostDeps ++ buildDeps);
+              # Crate renames have the form:
+              #
+              # {
+              #    crate_name = [
+              #       { version = "1.2.3"; rename = "crate_name01"; }
+              #    ];
+              #    # ...
+              # }
+              crateRenames =
+                let
+                  grouped =
+                    lib.groupBy
+                      (dependency: dependency.name)
+                      dependenciesWithRenames;
+                  versionAndRename = dep:
+                    let
+                      package = crateConfigs."${dep.packageId}";
+                    in
+                    { inherit (dep) rename; inherit (package) version; };
+                in
+                lib.mapAttrs (name: builtins.map versionAndRename) grouped;
+            in
+            buildRustCrateForPkgsFunc pkgs
+              (
+                crateConfig // {
+                  # https://github.com/NixOS/nixpkgs/issues/218712
+                  dontStrip = stdenv.hostPlatform.isDarwin;
+                  src = crateConfig.src or (
+                    pkgs.fetchurl rec {
+                      name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz";
+                      # https://www.pietroalbini.org/blog/downloading-crates-io/
+                      # Not rate-limited, CDN URL.
+                      url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate";
+                      sha256 =
+                        assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}");
+                        crateConfig.sha256;
+                    }
+                  );
+                  extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}";
+                  inherit features dependencies buildDependencies crateRenames release;
+                }
+              );
+        in
+        builtByPackageIdByPkgs;
+
+    /* Returns the actual derivations for the given dependencies. */
+    dependencyDerivations =
+      { buildByPackageId
+      , features
+      , dependencies
+      , target
+      }:
+        assert (builtins.isList features);
+        assert (builtins.isList dependencies);
+        assert (builtins.isAttrs target);
+        let
+          enabledDependencies = filterEnabledDependencies {
+            inherit dependencies features target;
+          };
+          depDerivation = dependency: buildByPackageId dependency.packageId;
+        in
+        map depDerivation enabledDependencies;
+
+    /* Returns a sanitized version of val with all values substituted that cannot
+      be serialized as JSON.
+    */
+    sanitizeForJson = val:
+      if builtins.isAttrs val
+      then lib.mapAttrs (n: sanitizeForJson) val
+      else if builtins.isList val
+      then builtins.map sanitizeForJson val
+      else if builtins.isFunction val
+      then "function"
+      else val;
+
+    /* Returns various tools to debug a crate. */
+    debugCrate = { packageId, target ? makeDefaultTarget stdenv.hostPlatform }:
+      assert (builtins.isString packageId);
+      let
+        debug = rec {
+          # The built tree as passed to buildRustCrate.
+          buildTree = buildRustCrateWithFeatures {
+            buildRustCrateForPkgsFunc = _: lib.id;
+            inherit packageId;
+          };
+          sanitizedBuildTree = sanitizeForJson buildTree;
+          dependencyTree = sanitizeForJson
+            (
+              buildRustCrateWithFeatures {
+                buildRustCrateForPkgsFunc = _: crate: {
+                  "01_crateName" = crate.crateName or false;
+                  "02_features" = crate.features or [ ];
+                  "03_dependencies" = crate.dependencies or [ ];
+                };
+                inherit packageId;
+              }
+            );
+          mergedPackageFeatures = mergePackageFeatures {
+            features = rootFeatures;
+            inherit packageId target;
+          };
+          diffedDefaultPackageFeatures = diffDefaultPackageFeatures {
+            inherit packageId target;
+          };
+        };
+      in
+      { internal = debug; };
+
+    /* Returns differences between cargo default features and crate2nix default
+      features.
+
+      This is useful for verifying the feature resolution in crate2nix.
+    */
+    diffDefaultPackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , target
+      }:
+        assert (builtins.isAttrs crateConfigs);
+        let
+          prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; });
+          mergedFeatures =
+            prefixValues
+              "crate2nix"
+              (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; });
+          configs = prefixValues "cargo" crateConfigs;
+          combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ];
+          onlyInCargo =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined);
+          onlyInCrate2Nix =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined);
+          differentFeatures = lib.filterAttrs
+            (
+              n: v:
+                (v ? "crate2nix")
+                && (v ? "cargo")
+                && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ])
+            )
+            combined;
+        in
+        builtins.toJSON {
+          inherit onlyInCargo onlyInCrate2Nix differentFeatures;
+        };
+
+    /* Returns an attrset mapping packageId to the list of enabled features.
+
+      If multiple paths to a dependency enable different features, the
+      corresponding feature sets are merged. Features in rust are additive.
+    */
+    mergePackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , rootPackageId ? packageId
+      , features ? rootFeatures
+      , dependencyPath ? [ crates.${packageId}.crateName ]
+      , featuresByPackageId ? { }
+      , target
+        # Adds devDependencies to the crate with rootPackageId.
+      , runTests ? false
+      , ...
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isString rootPackageId);
+        assert (builtins.isList features);
+        assert (builtins.isList dependencyPath);
+        assert (builtins.isAttrs featuresByPackageId);
+        assert (builtins.isAttrs target);
+        assert (builtins.isBool runTests);
+        let
+          crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
+          expandedFeatures = expandFeatures (crateConfig.features or { }) features;
+          enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
+          depWithResolvedFeatures = dependency:
+            let
+              inherit (dependency) packageId;
+              features = dependencyFeatures enabledFeatures dependency;
+            in
+            { inherit packageId features; };
+          resolveDependencies = cache: path: dependencies:
+            assert (builtins.isAttrs cache);
+            assert (builtins.isList dependencies);
+            let
+              enabledDependencies = filterEnabledDependencies {
+                inherit dependencies target;
+                features = enabledFeatures;
+              };
+              directDependencies = map depWithResolvedFeatures enabledDependencies;
+              foldOverCache = op: lib.foldl op cache directDependencies;
+            in
+            foldOverCache
+              (
+                cache: { packageId, features }:
+                  let
+                    cacheFeatures = cache.${packageId} or [ ];
+                    combinedFeatures = sortedUnique (cacheFeatures ++ features);
+                  in
+                  if cache ? ${packageId} && cache.${packageId} == combinedFeatures
+                  then cache
+                  else
+                    mergePackageFeatures {
+                      features = combinedFeatures;
+                      featuresByPackageId = cache;
+                      inherit crateConfigs packageId target runTests rootPackageId;
+                    }
+              );
+          cacheWithSelf =
+            let
+              cacheFeatures = featuresByPackageId.${packageId} or [ ];
+              combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
+            in
+            featuresByPackageId // {
+              "${packageId}" = combinedFeatures;
+            };
+          cacheWithDependencies =
+            resolveDependencies cacheWithSelf "dep"
+              (
+                crateConfig.dependencies or [ ]
+                ++ lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig.devDependencies or [ ])
+              );
+          cacheWithAll =
+            resolveDependencies
+              cacheWithDependencies "build"
+              (crateConfig.buildDependencies or [ ]);
+        in
+        cacheWithAll;
+
+    /* Returns the enabled dependencies given the enabled features. */
+    filterEnabledDependencies = { dependencies, features, target }:
+      assert (builtins.isList dependencies);
+      assert (builtins.isList features);
+      assert (builtins.isAttrs target);
+
+      lib.filter
+        (
+          dep:
+          let
+            targetFunc = dep.target or (features: true);
+          in
+          targetFunc { inherit features target; }
+          && (
+            !(dep.optional or false)
+            || builtins.any (doesFeatureEnableDependency dep) features
+          )
+        )
+        dependencies;
+
+    /* Returns whether the given feature should enable the given dependency. */
+    doesFeatureEnableDependency = dependency: feature:
+      let
+        name = dependency.rename or dependency.name;
+        prefix = "${name}/";
+        len = builtins.stringLength prefix;
+        startsWithPrefix = builtins.substring 0 len feature == prefix;
+      in
+      feature == name || feature == "dep:" + name || startsWithPrefix;
+
+    /* Returns the expanded features for the given inputFeatures by applying the
+      rules in featureMap.
+
+      featureMap is an attribute set which maps feature names to lists of further
+      feature names to enable in case this feature is selected.
+    */
+    expandFeatures = featureMap: inputFeatures:
+      assert (builtins.isAttrs featureMap);
+      assert (builtins.isList inputFeatures);
+      let
+        expandFeaturesNoCycle = oldSeen: inputFeatures:
+          if inputFeatures != [ ]
+          then
+            let
+              # The feature we're currently expanding.
+              feature = builtins.head inputFeatures;
+              # All the features we've seen/expanded so far, including the one
+              # we're currently processing.
+              seen = oldSeen // { ${feature} = 1; };
+              # Expand the feature but be careful to not re-introduce a feature
+              # that we've already seen: this can easily cause a cycle, see issue
+              # #209.
+              enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]);
+            in
+            [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
+          # No more features left, nothing to expand to.
+          else [ ];
+        outFeatures = expandFeaturesNoCycle { } inputFeatures;
+      in
+      sortedUnique outFeatures;
+
+    /* This function adds optional dependencies as features if they are enabled
+      indirectly by dependency features. This function mimics Cargo's behavior
+      described in a note at:
+      https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
+    */
+    enableFeatures = dependencies: features:
+      assert (builtins.isList features);
+      assert (builtins.isList dependencies);
+      let
+        additionalFeatures = lib.concatMap
+          (
+            dependency:
+              assert (builtins.isAttrs dependency);
+              let
+                enabled = builtins.any (doesFeatureEnableDependency dependency) features;
+              in
+              if (dependency.optional or false) && enabled
+              then [ (dependency.rename or dependency.name) ]
+              else [ ]
+          )
+          dependencies;
+      in
+      sortedUnique (features ++ additionalFeatures);
+
+    /*
+      Returns the actual features for the given dependency.
+
+      features: The features of the crate that refers this dependency.
+    */
+    dependencyFeatures = features: dependency:
+      assert (builtins.isList features);
+      assert (builtins.isAttrs dependency);
+      let
+        defaultOrNil =
+          if dependency.usesDefaultFeatures or true
+          then [ "default" ]
+          else [ ];
+        explicitFeatures = dependency.features or [ ];
+        additionalDependencyFeatures =
+          let
+            name = dependency.rename or dependency.name;
+            stripPrefixMatch = prefix: s:
+              if lib.hasPrefix prefix s
+              then lib.removePrefix prefix s
+              else null;
+            extractFeature = feature: lib.findFirst
+              (f: f != null)
+              null
+              (map (prefix: stripPrefixMatch prefix feature) [
+                (name + "/")
+                (name + "?/")
+              ]);
+            dependencyFeatures = lib.filter (f: f != null) (map extractFeature features);
+          in
+          dependencyFeatures;
+      in
+      defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures;
+
+    /* Sorts and removes duplicates from a list of strings. */
+    sortedUnique = features:
+      assert (builtins.isList features);
+      assert (builtins.all builtins.isString features);
+      let
+        outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features;
+        outFeaturesUnique = builtins.attrNames outFeaturesSet;
+      in
+      builtins.sort (a: b: a < b) outFeaturesUnique;
+
+    deprecationWarning = message: value:
+      if strictDeprecation
+      then builtins.throw "strictDeprecation enabled, aborting: ${message}"
+      else builtins.trace message value;
+
+    #
+    # crate2nix/default.nix (excerpt end)
+    #
+  };
+}
+
diff --git a/tvix/Cargo.toml b/tvix/Cargo.toml
index 8b0fbd846a..6cd19831dc 100644
--- a/tvix/Cargo.toml
+++ b/tvix/Cargo.toml
@@ -1,14 +1,38 @@
-[package]
-name = "tvix"
-version = "0.1.0"
-edition = "2021"
+# This Cargo file is a workspace configuration as per
+# https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html
+#
+# We add this file to get a coherent set of dependencies across Tvix
+# crates by sharing a Cargo.lock. This is necessary because of the
+# currently limited support for Rust builds in Nix.
+#
+# Note that this explicitly does *not* mean that //tvix should be
+# considered "one project": This is simply a workaround for a
+# technical limitation and it should be our aim to remove this
+# workspace file and make the subprojects independent.
+#
+# Note also that CI targets for actual projects should *not* be tied
+# to //tvix, but to its subprojects. A potential file at
+# //tvix/default.nix should likely *not* expose anything other than
+# extra steps or other auxiliary targets.
 
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[workspace]
+resolver = "2"
 
-[dependencies.clap]
-version = "3.0.5"
-[dependencies.tempfile]
-version = "3.2.0"
+members = [
+  "build",
+  "castore",
+  "cli",
+  "eval",
+  "eval/builtin-macros",
+  "glue",
+  "nix-compat",
+  "serde",
+  "store",
+]
 
-[[bin]]
-name = "nix-store"
+# Add a profile to all targets that enables release optimisations, but
+# retains debug symbols. This is great for use with
+# benchmarking/profiling tools.
+[profile.release-with-debug]
+inherits = "release"
+debug = true
diff --git a/tvix/OWNERS b/tvix/OWNERS
index 3005e4f6d3..a0b1dd8d77 100644
--- a/tvix/OWNERS
+++ b/tvix/OWNERS
@@ -1,5 +1,7 @@
-inherited: false
-owners:
-  - adisbladis
-  - flokli
-  - tazjin
+set noparent
+
+adisbladis
+flokli
+aspen
+sterni
+tazjin
diff --git a/tvix/README.md b/tvix/README.md
index 9569cedf33..bf96afa4ba 100644
--- a/tvix/README.md
+++ b/tvix/README.md
@@ -1,10 +1,109 @@
-Tvix
-====
+<div align="center">
+  <img src="https://tvix.dev/logo.webp">
+</div>
 
-For more information about Tvix, contact one of the project owners. We
-are interested in people who would like to help us review designs,
-brainstorm and describe requirements that we may not yet have
-considered.
+-----------------
+
+Tvix is a new implementation of the Nix language and package manager. See the
+[announcement post][post-1] for information about the background of this
+project.
+
+Tvix is developed by [TVL][tvl] in our monorepo, the `depot`, at
+[//tvix][tvix-src]. Code reviews take place on [Gerrit][tvix-gerrit], bugs are
+filed in [our issue tracker][b].
+
+For more information about Tvix, feel free to reach out. We are interested in
+people who would like to help us review designs, brainstorm and describe
+requirements that we may not yet have considered.
+
+Most of the discussion around development happens in our dedicated IRC channel,
+[`#tvix-dev`][tvix-dev-irc] on [hackint][],
+which is also reachable [via XMPP][hackint-xmpp]
+at [`#tvix-dev@irc.hackint.org`][tvix-dev-xmpp] (sic!)
+and [via Matrix][hackint-matrix] at [`#tvix-dev:hackint.org`][tvix-dev-matrix].
+
+There's also the IRC channel of the [wider TVL community][tvl-getting-in-touch],
+less on-topic, or our [mailing list][].
+
+Contributions to Tvix follow the TVL [review flow][review-docs] and
+[contribution guidelines][contributing].
+
+[post-1]: https://tvl.fyi/blog/rewriting-nix
+[tvl]: https://tvl.fyi
+[tvix-src]: https://cs.tvl.fyi/depot/-/tree/tvix/
+[tvix-gerrit]: https://cl.tvl.fyi/q/path:%255Etvix.*
+[b]: https://b.tvl.fyi
+[tvl-getting-in-touch]: https://tvl.fyi/#getting-in-touch
+[mailing list]: https://inbox.tvl.su
+[review-docs]: https://code.tvl.fyi/about/docs/REVIEWS.md
+[contributing]: https://code.tvl.fyi/about/docs/CONTRIBUTING.md
+[tvix-dev-irc]: ircs://irc.hackint.org:6697/#tvix-dev
+[hackint]: https://hackint.org/
+[hackint-xmpp]: https://hackint.org/transport/xmpp
+[tvix-dev-xmpp]: xmpp:#tvix-dev@irc.hackint.org?join
+[hackint-matrix]: https://hackint.org/transport/matrix
+[tvix-dev-matrix]: https://matrix.to/#/#tvix-dev:hackint.org
+[tvix-dev-webchat]: https://webirc.hackint.org/#ircs://irc.hackint.org/#tvix-dev
+
+WARNING: Tvix is not ready for use in production. None of our current APIs
+should be considered stable in any way.
+
+WARNING: Any other instances of this project or repository are
+[`josh`-mirrors][josh]. We do not accept code contributions or issues outside of
+the tooling and communication methods outlined above.
+
+[josh]: https://github.com/josh-project/josh
+
+## Components
+
+This folder contains the following components:
+
+* `//tvix/castore` - subtree storage/transfer in a content-addressed fashion
+* `//tvix/cli` - preliminary REPL & CLI implementation for Tvix
+* `//tvix/eval` - an implementation of the Nix programming language
+* `//tvix/nar-bridge`
+  * `nar-bridge-http`: A HTTP webserver providing a Nix HTTP Binary Cache interface in front of a tvix-store
+* `//tvix/nix-compat` - a Rust library for compatibility with C++ Nix, features like encodings and hashing schemes and formats
+* `//tvix/serde` - a Rust library for using the Nix language for app configuration
+* `//tvix/store` - a "filesystem" linking Nix store paths and metadata with the content-addressed layer
+
+Some additional folders with auxiliary things exist and can be explored at your
+leisure.
+
+## Building the CLI
+
+The CLI can also be built with standard Rust tooling (i.e. `cargo build`),
+as long as you are in a shell with the right dependencies.
+
+ - If you cloned the full monorepo, it can be provided by
+   `mg shell //tvix:shell`.
+ - If you cloned the `tvix` workspace only
+   (`git clone https://code.tvl.fyi/depot.git:workspace=views/tvix.git`),
+   `nix-shell` provides it.
+
+If you're in the TVL monorepo, you can also run `mg build //tvix/cli`
+(or `mg build` from inside that folder) for a more incremental build.
+
+Please follow the depot-wide instructions on how to get `mg` and use the depot
+tooling.
+
+### Compatibility
+**Important note:** We only use and test Nix builds of our software
+against Nix 2.3. There are a variety of bugs and subtle problems in
+newer Nix versions which we do not have the bandwidth to address,
+builds in newer Nix versions may or may not work.
+
+## Rust projects, crate2nix
+
+Some parts of Tvix are written in Rust. To simplify the dependency
+management on the Nix side of these builds, we use `crate2nix` in a
+single Rust workspace in `//tvix` to maintain the Nix build
+configuration.
+
+When making changes to Cargo dependency configuration in any of the
+Rust projects under `//tvix`, be sure to run
+`mg run //tools:crate2nix-generate` in `//tvix` itself and commit the changes
+to the generated `Cargo.nix` file. This only applies to the full TVL checkout.
 
 ## License structure
 
diff --git a/tvix/boot/README.md b/tvix/boot/README.md
new file mode 100644
index 0000000000..13a4855060
--- /dev/null
+++ b/tvix/boot/README.md
@@ -0,0 +1,136 @@
+# tvix/boot
+
+This directory provides tooling to boot VMs with /nix/store provided by
+virtiofs.
+
+In the `tests/` subdirectory, there's some integration tests.
+
+## //tvix/boot:runVM
+A script spinning up a `tvix-store virtiofs` daemon, then starting a cloud-
+hypervisor VM.
+
+The cloud-hypervisor VM is using a (semi-)minimal kernel image with virtiofs
+support, and a custom initrd (using u-root). It supports various command line
+options, to be able to do VM tests, act as an interactive shell or exec a binary
+from a closure.
+
+It supports the following env vars:
+ - `CH_NUM_CPUS=1` controls the number of CPUs available to the VM
+ - `CH_MEM_SIZE=512M` controls the memory availabe to the VM
+ - `CH_CMDLINE=` controls the kernel cmdline (which can be used to control the
+   boot)
+
+### Usage
+First, ensure you have `tvix-store` in `$PATH`, as that's what `run-tvix-vm`
+expects:
+
+Assuming you ran `cargo build --profile=release-with-debug` before, and are in
+the `tvix` directory:
+
+```
+export PATH=$PATH:$PWD/target/release-with-debug
+```
+
+Secondly, configure tvix to use the local backend:
+
+```
+export BLOB_SERVICE_ADDR=sled://$PWD/blobs.sled
+export DIRECTORY_SERVICE_ADDR=sled://$PWD/directories.sled
+export PATH_INFO_SERVICE_ADDR=sled://$PWD/pathinfo.sled
+```
+
+Potentially copy some data into tvix-store (via nar-bridge):
+
+```
+mg run //tvix:store -- daemon &
+$(mg build //tvix:nar-bridge)/bin/nar-bridge-http &
+rm -Rf ~/.cache/nix; nix copy --to http://localhost:9000\?compression\=none $(mg build //third_party/nixpkgs:hello)
+pkill nar-bridge-http; pkill tvix-store
+```
+
+#### Interactive shell
+Run the VM like this:
+
+```
+CH_CMDLINE=tvix.shell mg run //tvix/boot:runVM --
+```
+
+You'll get dropped into an interactive shell, from which you can do things with
+the store:
+
+```
+  ______      _         ____      _ __
+ /_  __/   __(_)  __   /  _/___  (_) /_
+  / / | | / / / |/_/   / // __ \/ / __/
+ / /  | |/ / />  <   _/ // / / / / /_
+/_/   |___/_/_/|_|  /___/_/ /_/_/\__/
+
+/# ls -la /nix/store/
+dr-xr-xr-x root 0 0   Jan  1 00:00 .
+dr-xr-xr-x root 0 989 Jan  1 00:00 aw2fw9ag10wr9pf0qk4nk5sxi0q0bn56-glibc-2.37-8
+dr-xr-xr-x root 0 3   Jan  1 00:00 jbwb8d8l28lg9z0xzl784wyb9vlbwss6-xgcc-12.3.0-libgcc
+dr-xr-xr-x root 0 82  Jan  1 00:00 k8ivghpggjrq1n49xp8sj116i4sh8lia-libidn2-2.3.4
+dr-xr-xr-x root 0 141 Jan  1 00:00 mdi7lvrn2mx7rfzv3fdq3v5yw8swiks6-hello-2.12.1
+dr-xr-xr-x root 0 5   Jan  1 00:00 s2gi8pfjszy6rq3ydx0z1vwbbskw994i-libunistring-1.1
+```
+
+Once you exit the shell, the VM will power off itself.
+
+#### Execute a specific binary
+Run the VM like this:
+
+```
+hello_cmd=$(mg build //third_party/nixpkgs:hello)/bin/hello
+CH_CMDLINE=tvix.run=$hello_cmd mg run //tvix/boot:runVM --
+```
+
+Observe it executing the file (and closure) from the tvix-store:
+
+```
+[    0.277486] Run /init as init process
+  ______      _         ____      _ __
+ /_  __/   __(_)  __   /  _/___  (_) /_
+  / / | | / / / |/_/   / // __ \/ / __/
+ / /  | |/ / />  <   _/ // / / / / /_
+/_/   |___/_/_/|_|  /___/_/ /_/_/\__/
+
+Hello, world!
+2023/09/24 21:10:19 Nothing left to be done, powering off.
+[    0.299122] ACPI: PM: Preparing to enter system sleep state S5
+[    0.299422] reboot: Power down
+```
+
+#### Execute a NixOS system closure
+It's also possible to invoke a system closure. To do this, tvix-init honors the
+init= cmdline option, and will switch_root to it.
+
+
+```
+CH_CMDLINE=init=/nix/store/…-nixos-system-…/init mg run //tvix/boot:runVM --
+```
+
+```
+  ______      _         ____      _ __
+ /_  __/   __(_)  __   /  _/___  (_) /_
+  / / | | / / / |/_/   / // __ \/ / __/
+ / /  | |/ / />  <   _/ // / / / / /_
+/_/   |___/_/_/|_|  /___/_/ /_/_/\__/
+
+2023/09/24 21:16:43 switch_root: moving mounts
+2023/09/24 21:16:43 switch_root: Skipping "/run" as the dir does not exist
+2023/09/24 21:16:43 switch_root: Changing directory
+2023/09/24 21:16:43 switch_root: Moving /
+2023/09/24 21:16:43 switch_root: Changing root!
+2023/09/24 21:16:43 switch_root: Deleting old /
+2023/09/24 21:16:43 switch_root: executing init
+
+<<< NixOS Stage 2 >>>
+
+[    0.322096] booting system configuration /nix/store/g657sdxinpqfcdv0162zmb8vv9b5c4c5-nixos-system-client-23.11.git.82102fc37da
+running activation script...
+setting up /etc...
+starting systemd...
+[    0.980740] systemd[1]: systemd 253.6 running in system mode (+PAM +AUDIT -SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD +BPF_FRAMEWORK -XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified)
+```
+
+This effectively replaces the NixOS Stage 1 entirely.
diff --git a/tvix/boot/default.nix b/tvix/boot/default.nix
new file mode 100644
index 0000000000..85995ffbf2
--- /dev/null
+++ b/tvix/boot/default.nix
@@ -0,0 +1,113 @@
+{ depot, pkgs, ... }:
+
+rec {
+  # A binary that sets up /nix/store from virtiofs, lists all store paths, and
+  # powers off the machine.
+  tvix-init = depot.nix.buildGo.program {
+    name = "tvix-init";
+    srcs = [
+      ./tvix-init.go
+    ];
+  };
+
+  # A kernel with virtiofs support baked in
+  # TODO: make a smaller kernel, we don't need a gazillion filesystems and
+  # device drivers in it.
+  kernel = pkgs.buildLinux ({ } // {
+    inherit (pkgs.linuxPackages_latest.kernel) src version modDirVersion;
+    autoModules = false;
+    kernelPreferBuiltin = true;
+    ignoreConfigErrors = true;
+    kernelPatches = [ ];
+    structuredExtraConfig = with pkgs.lib.kernel; {
+      FUSE_FS = option yes;
+      DAX_DRIVER = option yes;
+      DAX = option yes;
+      FS_DAX = option yes;
+      VIRTIO_FS = option yes;
+      VIRTIO = option yes;
+      ZONE_DEVICE = option yes;
+    };
+  });
+
+  # A build framework for minimal initrds
+  uroot = pkgs.buildGoModule rec {
+    pname = "u-root";
+    version = "0.14.0";
+    src = pkgs.fetchFromGitHub {
+      owner = "u-root";
+      repo = "u-root";
+      rev = "v${version}";
+      hash = "sha256-8zA3pHf45MdUcq/MA/mf0KCTxB1viHieU/oigYwIPgo=";
+    };
+    vendorHash = null;
+
+    doCheck = false; # Some tests invoke /bin/bash
+  };
+
+  # Use u-root to build a initrd with our tvix-init inside.
+  initrd = pkgs.stdenv.mkDerivation {
+    name = "initrd.cpio";
+    nativeBuildInputs = [ pkgs.go ];
+    # https://github.com/u-root/u-root/issues/2466
+    buildCommand = ''
+      mkdir -p /tmp/go/src/github.com/u-root/
+      cp -R ${uroot.src} /tmp/go/src/github.com/u-root/u-root
+      cd /tmp/go/src/github.com/u-root/u-root
+      chmod +w .
+      cp ${tvix-init}/bin/tvix-init tvix-init
+
+      export HOME=$(mktemp -d)
+      export GOROOT="$(go env GOROOT)"
+
+      GO111MODULE=off GOPATH=/tmp/go GOPROXY=off ${uroot}/bin/u-root -files ./tvix-init -initcmd "/tvix-init" -o $out
+    '';
+  };
+
+  # Start a `tvix-store` virtiofs daemon from $PATH, then a cloud-hypervisor
+  # pointed to it.
+  # Supports the following env vars (and defaults)
+  # CH_NUM_CPUS=2
+  # CH_MEM_SIZE=512M
+  # CH_CMDLINE=""
+  runVM = pkgs.writers.writeBashBin "run-tvix-vm" ''
+    tempdir=$(mktemp -d)
+
+    cleanup() {
+      kill $virtiofsd_pid
+      if [[ -n ''${work_dir-} ]]; then
+        chmod -R u+rw "$tempdir"
+        rm -rf "$tempdir"
+      fi
+    }
+    trap cleanup EXIT
+
+    # Spin up the virtiofs daemon
+    tvix-store --otlp=false virtiofs -l $tempdir/tvix.sock &
+    virtiofsd_pid=$!
+
+    # Wait for the socket to exist.
+    until [ -e $tempdir/tvix.sock ]; do sleep 0.1; done
+
+    CH_NUM_CPUS="''${CH_NUM_CPUS:-2}"
+    CH_MEM_SIZE="''${CH_MEM_SIZE:-512M}"
+    CH_CMDLINE="''${CH_CMDLINE:-}"
+
+    # spin up cloud_hypervisor
+    ${pkgs.cloud-hypervisor}/bin/cloud-hypervisor \
+     --cpus boot=$CH_NUM_CPU \
+     --memory mergeable=on,shared=on,size=$CH_MEM_SIZE \
+     --console null \
+     --serial tty \
+     --kernel ${kernel.dev}/vmlinux \
+     --initramfs ${initrd} \
+     --cmdline "console=ttyS0 $CH_CMDLINE" \
+     --fs tag=tvix,socket=$tempdir/tvix.sock,num_queues=''${CH_NUM_CPU},queue_size=512
+  '';
+
+  meta.ci.targets = [
+    "initrd"
+    "kernel"
+    "runVM"
+  ];
+}
diff --git a/tvix/boot/tests/default.nix b/tvix/boot/tests/default.nix
new file mode 100644
index 0000000000..d16dba79f1
--- /dev/null
+++ b/tvix/boot/tests/default.nix
@@ -0,0 +1,138 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  # Seed a tvix-store with the tvix docs, then start a VM, ask it to list all
+  # files in /nix/store, and ensure the store path is present, which acts as a
+  # nice smoketest.
+  mkBootTest =
+    { blobServiceAddr ? "memory://"
+    , directoryServiceAddr ? "memory://"
+    , pathInfoServiceAddr ? "memory://"
+
+
+      # The path to import.
+    , path
+
+      # Whether the path should be imported as a closure.
+      # If false, importPathName must be specified.
+    , isClosure ? false
+    , importPathName ? null
+
+      # The cmdline to pass to the VM.
+      # Defaults to tvix.find, which lists all files in the store.
+    , vmCmdline ? "tvix.find"
+      # The string we expect to find in the VM output.
+      # Defaults the value of `path` (the store path we upload).
+    , assertVMOutput ? path
+    }:
+
+      assert isClosure -> importPathName == null;
+      assert (!isClosure) -> importPathName != null;
+
+      pkgs.stdenv.mkDerivation {
+        name = "run-vm";
+
+        __structuredAttrs = true;
+        exportReferencesGraph.closure = [ path ];
+
+        nativeBuildInputs = [
+          depot.tvix.store
+          depot.tvix.boot.runVM
+        ];
+        buildCommand = ''
+          touch $out
+
+          # Start the tvix daemon, listening on a unix socket.
+          BLOB_SERVICE_ADDR=${blobServiceAddr} \
+            DIRECTORY_SERVICE_ADDR=${directoryServiceAddr} \
+            PATH_INFO_SERVICE_ADDR=${pathInfoServiceAddr} \
+            tvix-store \
+              --otlp=false \
+              daemon -l $PWD/tvix-store.sock &
+
+          # Wait for the socket to be created.
+          while [ ! -e $PWD/tvix-store.sock ]; do sleep 1; done
+
+          # Export env vars so that subsequent tvix-store commands will talk to
+          # our tvix-store daemon over the unix socket.
+          export BLOB_SERVICE_ADDR=grpc+unix://$PWD/tvix-store.sock
+          export DIRECTORY_SERVICE_ADDR=grpc+unix://$PWD/tvix-store.sock
+          export PATH_INFO_SERVICE_ADDR=grpc+unix://$PWD/tvix-store.sock
+        '' + lib.optionalString (!isClosure) ''
+          echo "Importing ${path} into tvix-store with name ${importPathName}…"
+          cp -R ${path} ${importPathName}
+          outpath=$(tvix-store import ${importPathName})
+
+          echo "imported to $outpath"
+        '' + lib.optionalString (isClosure) ''
+          echo "Copying closure ${path}…"
+          # This picks up the `closure` key in `$NIX_ATTRS_JSON_FILE` automatically.
+          tvix-store --otlp=false copy
+        '' + ''
+          # Invoke a VM using tvix as the backing store, ensure the outpath appears in its listing.
+          echo "Starting VM…"
+
+          CH_CMDLINE="${vmCmdline}" run-tvix-vm 2>&1 | tee output.txt
+          grep "${assertVMOutput}" output.txt
+        '';
+        requiredSystemFeatures = [ "kvm" ];
+      };
+
+  systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
+
+  testSystem = systemFor ({ modulesPath, pkgs, ... }: {
+    # Set some options necessary to evaluate.
+    boot.loader.systemd-boot.enable = true;
+    # TODO: figure out how to disable this without causing eval to fail
+    fileSystems."/" = {
+      device = "/dev/root";
+      fsType = "tmpfs";
+    };
+
+    services.getty.helpLine = "Onwards and upwards.";
+    systemd.services.do-shutdown = {
+      after = [ "getty.target" ];
+      description = "Shut down again";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.Type = "oneshot";
+      script = "/run/current-system/sw/bin/systemctl poweroff --when=+10s";
+    };
+
+    # Don't warn about stateVersion.
+    system.stateVersion = "24.05";
+  });
+
+in
+depot.nix.readTree.drvTargets
+{
+  docs-memory = (mkBootTest {
+    path = ../../docs;
+    importPathName = "docs";
+  });
+  docs-sled = (mkBootTest {
+    blobServiceAddr = "sled://$PWD/blobs.sled";
+    directoryServiceAddr = "sled://$PWD/directories.sled";
+    pathInfoServiceAddr = "sled://$PWD/pathinfo.sled";
+    path = ../../docs;
+    importPathName = "docs";
+  });
+  docs-objectstore-local = (mkBootTest {
+    blobServiceAddr = "objectstore+file://$PWD/blobs";
+    path = ../../docs;
+    importPathName = "docs";
+  });
+
+  closure-tvix = (mkBootTest {
+    blobServiceAddr = "objectstore+file://$PWD/blobs";
+    path = depot.tvix.store;
+    isClosure = true;
+  });
+
+  closure-nixos = (mkBootTest {
+    blobServiceAddr = "objectstore+file://$PWD/blobs";
+    path = testSystem;
+    isClosure = true;
+    vmCmdline = "init=${testSystem}/init panic=-1"; # reboot immediately on panic
+    assertVMOutput = "Onwards and upwards.";
+  });
+}
diff --git a/tvix/boot/tvix-init.go b/tvix/boot/tvix-init.go
new file mode 100644
index 0000000000..97a24bab35
--- /dev/null
+++ b/tvix/boot/tvix-init.go
@@ -0,0 +1,138 @@
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"strings"
+	"syscall"
+)
+
+// run the given command, connecting std{in,err,out} with the OS one.
+func run(args ...string) error {
+	cmd := exec.Command(args[0], args[1:]...)
+	cmd.Stdin = os.Stdin
+	cmd.Stderr = os.Stderr
+	cmd.Stdout = os.Stdout
+
+	return cmd.Run()
+}
+
+// parse the cmdline, return a map[string]string.
+func parseCmdline(cmdline string) map[string]string {
+	line := strings.TrimSuffix(cmdline, "\n")
+	fields := strings.Fields(line)
+	out := make(map[string]string, 0)
+
+	for _, arg := range fields {
+		kv := strings.SplitN(arg, "=", 2)
+		switch len(kv) {
+		case 1:
+			out[kv[0]] = ""
+		case 2:
+			out[kv[0]] = kv[1]
+		}
+	}
+
+	return out
+}
+
+// mounts the nix store from the virtiofs tag to the given destination,
+// creating the destination if it doesn't exist already.
+func mountTvixStore(dest string) error {
+	if err := os.MkdirAll(dest, os.ModePerm); err != nil {
+		return fmt.Errorf("unable to mkdir dest: %w", err)
+	}
+	if err := run("mount", "-t", "virtiofs", "tvix", dest, "-o", "ro"); err != nil {
+		return fmt.Errorf("unable to run mount: %w", err)
+	}
+
+	return nil
+}
+
+func main() {
+	fmt.Print(`
+  ______      _         ____      _ __
+ /_  __/   __(_)  __   /  _/___  (_) /_
+  / / | | / / / |/_/   / // __ \/ / __/
+ / /  | |/ / />  <   _/ // / / / / /_
+/_/   |___/_/_/|_|  /___/_/ /_/_/\__/
+
+`)
+
+	// Set PATH to "/bbin", so we can find the u-root tools
+	os.Setenv("PATH", "/bbin")
+
+	if err := run("mount", "-t", "proc", "none", "/proc"); err != nil {
+		log.Printf("Failed to mount /proc: %v\n", err)
+	}
+	if err := run("mount", "-t", "sysfs", "none", "/sys"); err != nil {
+		log.Printf("Failed to mount /sys: %v\n", err)
+	}
+	if err := run("mount", "-t", "devtmpfs", "devtmpfs", "/dev"); err != nil {
+		log.Printf("Failed to mount /dev: %v\n", err)
+	}
+
+	cmdline, err := os.ReadFile("/proc/cmdline")
+	if err != nil {
+		log.Printf("Failed to read cmdline: %s\n", err)
+	}
+	cmdlineFields := parseCmdline(string(cmdline))
+
+	if _, ok := cmdlineFields["tvix.find"]; ok {
+		// If tvix.find is set, invoke find /nix/store
+		if err := mountTvixStore("/nix/store"); err != nil {
+			log.Printf("Failed to mount tvix store: %v\n", err)
+		}
+
+		if err := run("find", "/nix/store"); err != nil {
+			log.Printf("Failed to run find command: %s\n", err)
+		}
+	} else if _, ok := cmdlineFields["tvix.shell"]; ok {
+		// If tvix.shell is set, mount the nix store to /nix/store directly,
+		// then invoke the elvish shell
+		if err := mountTvixStore("/nix/store"); err != nil {
+			log.Printf("Failed to mount tvix store: %v\n", err)
+		}
+
+		if err := run("elvish"); err != nil {
+			log.Printf("Failed to run shell: %s\n", err)
+		}
+	} else if v, ok := cmdlineFields["tvix.run"]; ok {
+		// If tvix.run is set, mount the nix store to /nix/store directly,
+		// then invoke the command.
+		if err := mountTvixStore("/nix/store"); err != nil {
+			log.Printf("Failed to mount tvix store: %v\n", err)
+		}
+
+		if err := run(v); err != nil {
+			log.Printf("Failed to run command: %s\n", err)
+		}
+	} else if v, ok := cmdlineFields["init"]; ok {
+		// If init is set, invoke the binary specified (with switch_root),
+		// and prepare /fs beforehand as well.
+		os.Mkdir("/fs", os.ModePerm)
+		if err := run("mount", "-t", "tmpfs", "none", "/fs"); err != nil {
+			log.Fatalf("Failed to mount /fs tmpfs: %s\n", err)
+		}
+
+		// Mount /fs/nix/store
+		if err := mountTvixStore("/fs/nix/store"); err != nil {
+			log.Fatalf("Failed to mount tvix store: %v\n", err)
+		}
+
+		// Invoke switch_root, which will take care of moving /proc, /sys and /dev.
+		if err := syscall.Exec("/bbin/switch_root", []string{"switch_root", "/fs", v}, []string{}); err != nil {
+			log.Printf("Failed to switch root: %s\n", err)
+		}
+	} else {
+		log.Printf("No command detected, not knowing what to do!")
+	}
+
+	// This is only reached in the non switch_root case.
+	log.Printf("Nothing left to be done, powering off.")
+	if err := run("poweroff"); err != nil {
+		log.Printf("Failed to run poweroff command: %v\n", err)
+	}
+}
diff --git a/tvix/build-go/LICENSE b/tvix/build-go/LICENSE
new file mode 100644
index 0000000000..2034ada6fd
--- /dev/null
+++ b/tvix/build-go/LICENSE
@@ -0,0 +1,21 @@
+Copyright Β© The Tvix Authors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+β€œSoftware”), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/tvix/build-go/README.md b/tvix/build-go/README.md
new file mode 100644
index 0000000000..19edfced01
--- /dev/null
+++ b/tvix/build-go/README.md
@@ -0,0 +1,10 @@
+# build-go
+
+This directory contains generated golang bindings, both for the `tvix-build`
+data models, as well as the gRPC bindings.
+
+They are generated with `mg run //tvix/build-go:regenerate`.
+These files end with `.pb.go`, and are ensured to be up to date by a CI check.
+
+Additionally, code useful when interacting with these data structures
+(ending just with `.go`) is provided.
diff --git a/tvix/build-go/build.pb.go b/tvix/build-go/build.pb.go
new file mode 100644
index 0000000000..9c6bd5f248
--- /dev/null
+++ b/tvix/build-go/build.pb.go
@@ -0,0 +1,670 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        (unknown)
+// source: tvix/build/protos/build.proto
+
+package buildv1
+
+import (
+	castore_go "code.tvl.fyi/tvix/castore-go"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// A BuildRequest describes the request of something to be run on the builder.
+// It is distinct from an actual [Build] that has already happened, or might be
+// currently ongoing.
+//
+// A BuildRequest can be seen as a more normalized version of a Derivation
+// (parsed from A-Term), "writing out" some of the Nix-internal details about
+// how e.g. environment variables in the build are set.
+//
+// Nix has some impurities when building a Derivation, for example the --cores option
+// ends up as an environment variable in the build, that's not part of the ATerm.
+//
+// As of now, we serialize this into the BuildRequest, so builders can stay dumb.
+// This might change in the future.
+//
+// There's also a big difference when it comes to how inputs are modelled:
+//   - Nix only uses store path (strings) to describe the inputs.
+//     As store paths can be input-addressed, a certain store path can contain
+//     different contents (as not all store paths are binary reproducible).
+//     This requires that for every input-addressed input, the builder has access
+//     to either the input's deriver (and needs to build it) or else a trusted
+//     source for the built input.
+//     to upload input-addressed paths, requiring the trusted users concept.
+//   - tvix-build records a list of tvix.castore.v1.Node as inputs.
+//     These map from the store path base name to their contents, relieving the
+//     builder from having to "trust" any input-addressed paths, contrary to Nix.
+//
+// While this approach gives a better hermeticity, it has one downside:
+// A BuildRequest can only be sent once the contents of all its inputs are known.
+//
+// As of now, we're okay to accept this, but it prevents uploading an
+// entirely-non-IFD subgraph of BuildRequests eagerly.
+//
+// FUTUREWORK: We might be introducing another way to refer to inputs, to
+// support "send all BuildRequest for a nixpkgs eval to a remote builder and put
+// the laptop to sleep" usecases later.
+type BuildRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The list of all root nodes that should be visible in `inputs_dir` at the
+	// time of the build.
+	// As root nodes are content-addressed, no additional signatures are needed
+	// to substitute / make these available in the build environment.
+	// Inputs MUST be sorted by their names.
+	Inputs []*castore_go.Node `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"`
+	// The command (and its args) executed as the build script.
+	// In the case of a Nix derivation, this is usually
+	// ["/path/to/some-bash/bin/bash", "-e", "/path/to/some/builder.sh"].
+	CommandArgs []string `protobuf:"bytes,2,rep,name=command_args,json=commandArgs,proto3" json:"command_args,omitempty"`
+	// The working dir of the command, relative to the build root.
+	// "build", in the case of Nix.
+	// This MUST be a clean relative path, without any ".", "..", or superfluous
+	// slashes.
+	WorkingDir string `protobuf:"bytes,3,opt,name=working_dir,json=workingDir,proto3" json:"working_dir,omitempty"`
+	// A list of "scratch" paths, relative to the build root.
+	// These will be write-able during the build.
+	// [build, nix/store] in the case of Nix.
+	// These MUST be clean relative paths, without any ".", "..", or superfluous
+	// slashes, and sorted.
+	ScratchPaths []string `protobuf:"bytes,4,rep,name=scratch_paths,json=scratchPaths,proto3" json:"scratch_paths,omitempty"`
+	// The path where the castore input nodes will be located at,
+	// "nix/store" in case of Nix.
+	// Builds might also write into here (Nix builds do that).
+	// This MUST be a clean relative path, without any ".", "..", or superfluous
+	// slashes.
+	InputsDir string `protobuf:"bytes,5,opt,name=inputs_dir,json=inputsDir,proto3" json:"inputs_dir,omitempty"`
+	// The list of output paths the build is expected to produce,
+	// relative to the root.
+	// If the path is not produced, the build is considered to have failed.
+	// These MUST be clean relative paths, without any ".", "..", or superfluous
+	// slashes, and sorted.
+	Outputs []string `protobuf:"bytes,6,rep,name=outputs,proto3" json:"outputs,omitempty"`
+	// The list of environment variables and their values that should be set
+	// inside the build environment.
+	// This includes both environment vars set inside the derivation, as well as
+	// more "ephemeral" ones like NIX_BUILD_CORES, controlled by the `--cores`
+	// CLI option of `nix-build`.
+	// For now, we consume this as an option when turning a Derivation into a BuildRequest,
+	// similar to how Nix has a `--cores` option.
+	// We don't want to bleed these very nix-specific sandbox impl details into
+	// (dumber) builders if we don't have to.
+	// Environment variables are sorted by their keys.
+	EnvironmentVars []*BuildRequest_EnvVar `protobuf:"bytes,7,rep,name=environment_vars,json=environmentVars,proto3" json:"environment_vars,omitempty"`
+	// A set of constraints that need to be satisfied on a build host before a
+	// Build can be started.
+	Constraints *BuildRequest_BuildConstraints `protobuf:"bytes,8,opt,name=constraints,proto3" json:"constraints,omitempty"`
+	// Additional (small) files and their contents that should be placed into the
+	// build environment, but outside inputs_dir.
+	// Used for passAsFile and structuredAttrs in Nix.
+	AdditionalFiles []*BuildRequest_AdditionalFile `protobuf:"bytes,9,rep,name=additional_files,json=additionalFiles,proto3" json:"additional_files,omitempty"`
+}
+
+func (x *BuildRequest) Reset() {
+	*x = BuildRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_build_protos_build_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildRequest) ProtoMessage() {}
+
+func (x *BuildRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_build_protos_build_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildRequest.ProtoReflect.Descriptor instead.
+func (*BuildRequest) Descriptor() ([]byte, []int) {
+	return file_tvix_build_protos_build_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *BuildRequest) GetInputs() []*castore_go.Node {
+	if x != nil {
+		return x.Inputs
+	}
+	return nil
+}
+
+func (x *BuildRequest) GetCommandArgs() []string {
+	if x != nil {
+		return x.CommandArgs
+	}
+	return nil
+}
+
+func (x *BuildRequest) GetWorkingDir() string {
+	if x != nil {
+		return x.WorkingDir
+	}
+	return ""
+}
+
+func (x *BuildRequest) GetScratchPaths() []string {
+	if x != nil {
+		return x.ScratchPaths
+	}
+	return nil
+}
+
+func (x *BuildRequest) GetInputsDir() string {
+	if x != nil {
+		return x.InputsDir
+	}
+	return ""
+}
+
+func (x *BuildRequest) GetOutputs() []string {
+	if x != nil {
+		return x.Outputs
+	}
+	return nil
+}
+
+func (x *BuildRequest) GetEnvironmentVars() []*BuildRequest_EnvVar {
+	if x != nil {
+		return x.EnvironmentVars
+	}
+	return nil
+}
+
+func (x *BuildRequest) GetConstraints() *BuildRequest_BuildConstraints {
+	if x != nil {
+		return x.Constraints
+	}
+	return nil
+}
+
+func (x *BuildRequest) GetAdditionalFiles() []*BuildRequest_AdditionalFile {
+	if x != nil {
+		return x.AdditionalFiles
+	}
+	return nil
+}
+
+// A Build is (one possible) outcome of executing a [BuildRequest].
+type Build struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The orginal build request producing the build.
+	BuildRequest *BuildRequest `protobuf:"bytes,1,opt,name=build_request,json=buildRequest,proto3" json:"build_request,omitempty"` // <- TODO: define hashing scheme for BuildRequest, refer to it by hash?
+	// The outputs that were produced after successfully building.
+	// They are sorted by their names.
+	Outputs []*castore_go.Node `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"`
+}
+
+func (x *Build) Reset() {
+	*x = Build{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_build_protos_build_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Build) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Build) ProtoMessage() {}
+
+func (x *Build) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_build_protos_build_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Build.ProtoReflect.Descriptor instead.
+func (*Build) Descriptor() ([]byte, []int) {
+	return file_tvix_build_protos_build_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Build) GetBuildRequest() *BuildRequest {
+	if x != nil {
+		return x.BuildRequest
+	}
+	return nil
+}
+
+func (x *Build) GetOutputs() []*castore_go.Node {
+	if x != nil {
+		return x.Outputs
+	}
+	return nil
+}
+
+type BuildRequest_EnvVar struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// name of the environment variable. Must not contain =.
+	Key   string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+	Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (x *BuildRequest_EnvVar) Reset() {
+	*x = BuildRequest_EnvVar{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_build_protos_build_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildRequest_EnvVar) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildRequest_EnvVar) ProtoMessage() {}
+
+func (x *BuildRequest_EnvVar) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_build_protos_build_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildRequest_EnvVar.ProtoReflect.Descriptor instead.
+func (*BuildRequest_EnvVar) Descriptor() ([]byte, []int) {
+	return file_tvix_build_protos_build_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *BuildRequest_EnvVar) GetKey() string {
+	if x != nil {
+		return x.Key
+	}
+	return ""
+}
+
+func (x *BuildRequest_EnvVar) GetValue() []byte {
+	if x != nil {
+		return x.Value
+	}
+	return nil
+}
+
+// BuildConstraints represents certain conditions that must be fulfilled
+// inside the build environment to be able to build this.
+// Constraints can be things like required architecture and minimum amount of memory.
+// The required input paths are *not* represented in here, because it
+// wouldn't be hermetic enough - see the comment around inputs too.
+type BuildRequest_BuildConstraints struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The system that's needed to execute the build.
+	// Must not be empty.
+	System string `protobuf:"bytes,1,opt,name=system,proto3" json:"system,omitempty"`
+	// The amount of memory required to be available for the build, in bytes.
+	MinMemory uint64 `protobuf:"varint,2,opt,name=min_memory,json=minMemory,proto3" json:"min_memory,omitempty"`
+	// A list of (absolute) paths that need to be available in the build
+	// environment, like `/dev/kvm`.
+	// This is distinct from the castore nodes in inputs.
+	// TODO: check if these should be individual constraints instead.
+	// These MUST be clean absolute paths, without any ".", "..", or superfluous
+	// slashes, and sorted.
+	AvailableRoPaths []string `protobuf:"bytes,3,rep,name=available_ro_paths,json=availableRoPaths,proto3" json:"available_ro_paths,omitempty"`
+	// Whether the build should be able to access the network,
+	NetworkAccess bool `protobuf:"varint,4,opt,name=network_access,json=networkAccess,proto3" json:"network_access,omitempty"`
+	// Whether to provide a /bin/sh inside the build environment, usually a static bash.
+	ProvideBinSh bool `protobuf:"varint,5,opt,name=provide_bin_sh,json=provideBinSh,proto3" json:"provide_bin_sh,omitempty"`
+}
+
+func (x *BuildRequest_BuildConstraints) Reset() {
+	*x = BuildRequest_BuildConstraints{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_build_protos_build_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildRequest_BuildConstraints) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildRequest_BuildConstraints) ProtoMessage() {}
+
+func (x *BuildRequest_BuildConstraints) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_build_protos_build_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildRequest_BuildConstraints.ProtoReflect.Descriptor instead.
+func (*BuildRequest_BuildConstraints) Descriptor() ([]byte, []int) {
+	return file_tvix_build_protos_build_proto_rawDescGZIP(), []int{0, 1}
+}
+
+func (x *BuildRequest_BuildConstraints) GetSystem() string {
+	if x != nil {
+		return x.System
+	}
+	return ""
+}
+
+func (x *BuildRequest_BuildConstraints) GetMinMemory() uint64 {
+	if x != nil {
+		return x.MinMemory
+	}
+	return 0
+}
+
+func (x *BuildRequest_BuildConstraints) GetAvailableRoPaths() []string {
+	if x != nil {
+		return x.AvailableRoPaths
+	}
+	return nil
+}
+
+func (x *BuildRequest_BuildConstraints) GetNetworkAccess() bool {
+	if x != nil {
+		return x.NetworkAccess
+	}
+	return false
+}
+
+func (x *BuildRequest_BuildConstraints) GetProvideBinSh() bool {
+	if x != nil {
+		return x.ProvideBinSh
+	}
+	return false
+}
+
+type BuildRequest_AdditionalFile struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Path     string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
+	Contents []byte `protobuf:"bytes,2,opt,name=contents,proto3" json:"contents,omitempty"`
+}
+
+func (x *BuildRequest_AdditionalFile) Reset() {
+	*x = BuildRequest_AdditionalFile{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_build_protos_build_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildRequest_AdditionalFile) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildRequest_AdditionalFile) ProtoMessage() {}
+
+func (x *BuildRequest_AdditionalFile) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_build_protos_build_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildRequest_AdditionalFile.ProtoReflect.Descriptor instead.
+func (*BuildRequest_AdditionalFile) Descriptor() ([]byte, []int) {
+	return file_tvix_build_protos_build_proto_rawDescGZIP(), []int{0, 2}
+}
+
+func (x *BuildRequest_AdditionalFile) GetPath() string {
+	if x != nil {
+		return x.Path
+	}
+	return ""
+}
+
+func (x *BuildRequest_AdditionalFile) GetContents() []byte {
+	if x != nil {
+		return x.Contents
+	}
+	return nil
+}
+
+var File_tvix_build_protos_build_proto protoreflect.FileDescriptor
+
+var file_tvix_build_protos_build_proto_rawDesc = []byte{
+	0x0a, 0x1d, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x73, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+	0x0d, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x1a, 0x21,
+	0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x73, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x22, 0x90, 0x06, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
+	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74,
+	0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x72, 0x67,
+	0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
+	0x41, 0x72, 0x67, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f,
+	0x64, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x69,
+	0x6e, 0x67, 0x44, 0x69, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68,
+	0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x63,
+	0x72, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e,
+	0x70, 0x75, 0x74, 0x73, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+	0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x44, 0x69, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x75, 0x74,
+	0x70, 0x75, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70,
+	0x75, 0x74, 0x73, 0x12, 0x4d, 0x0a, 0x10, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65,
+	0x6e, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e,
+	0x74, 0x76, 0x69, 0x78, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75,
+	0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x6e, 0x76, 0x56, 0x61,
+	0x72, 0x52, 0x0f, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61,
+	0x72, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74,
+	0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72,
+	0x61, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e,
+	0x74, 0x73, 0x12, 0x55, 0x0a, 0x10, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
+	0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74,
+	0x76, 0x69, 0x78, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69,
+	0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69,
+	0x6f, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69,
+	0x6f, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x1a, 0x30, 0x0a, 0x06, 0x45, 0x6e, 0x76,
+	0x56, 0x61, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0xc4, 0x01, 0x0a, 0x10,
+	0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73,
+	0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f,
+	0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x69,
+	0x6e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x76, 0x61, 0x69, 0x6c,
+	0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f,
+	0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+	0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e,
+	0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x24, 0x0a, 0x0e,
+	0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x5f, 0x73, 0x68, 0x18, 0x05,
+	0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x42, 0x69, 0x6e,
+	0x53, 0x68, 0x1a, 0x40, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
+	0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x6e, 0x74, 0x73, 0x22, 0x7a, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x40, 0x0a,
+	0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x62, 0x75, 0x69, 0x6c,
+	0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x2f, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x15, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e,
+	0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73,
+	0x42, 0x24, 0x5a, 0x22, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e, 0x66, 0x79, 0x69,
+	0x2f, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2d, 0x67, 0x6f, 0x3b, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_tvix_build_protos_build_proto_rawDescOnce sync.Once
+	file_tvix_build_protos_build_proto_rawDescData = file_tvix_build_protos_build_proto_rawDesc
+)
+
+func file_tvix_build_protos_build_proto_rawDescGZIP() []byte {
+	file_tvix_build_protos_build_proto_rawDescOnce.Do(func() {
+		file_tvix_build_protos_build_proto_rawDescData = protoimpl.X.CompressGZIP(file_tvix_build_protos_build_proto_rawDescData)
+	})
+	return file_tvix_build_protos_build_proto_rawDescData
+}
+
+var file_tvix_build_protos_build_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_tvix_build_protos_build_proto_goTypes = []interface{}{
+	(*BuildRequest)(nil),                  // 0: tvix.build.v1.BuildRequest
+	(*Build)(nil),                         // 1: tvix.build.v1.Build
+	(*BuildRequest_EnvVar)(nil),           // 2: tvix.build.v1.BuildRequest.EnvVar
+	(*BuildRequest_BuildConstraints)(nil), // 3: tvix.build.v1.BuildRequest.BuildConstraints
+	(*BuildRequest_AdditionalFile)(nil),   // 4: tvix.build.v1.BuildRequest.AdditionalFile
+	(*castore_go.Node)(nil),               // 5: tvix.castore.v1.Node
+}
+var file_tvix_build_protos_build_proto_depIdxs = []int32{
+	5, // 0: tvix.build.v1.BuildRequest.inputs:type_name -> tvix.castore.v1.Node
+	2, // 1: tvix.build.v1.BuildRequest.environment_vars:type_name -> tvix.build.v1.BuildRequest.EnvVar
+	3, // 2: tvix.build.v1.BuildRequest.constraints:type_name -> tvix.build.v1.BuildRequest.BuildConstraints
+	4, // 3: tvix.build.v1.BuildRequest.additional_files:type_name -> tvix.build.v1.BuildRequest.AdditionalFile
+	0, // 4: tvix.build.v1.Build.build_request:type_name -> tvix.build.v1.BuildRequest
+	5, // 5: tvix.build.v1.Build.outputs:type_name -> tvix.castore.v1.Node
+	6, // [6:6] is the sub-list for method output_type
+	6, // [6:6] is the sub-list for method input_type
+	6, // [6:6] is the sub-list for extension type_name
+	6, // [6:6] is the sub-list for extension extendee
+	0, // [0:6] is the sub-list for field type_name
+}
+
+func init() { file_tvix_build_protos_build_proto_init() }
+func file_tvix_build_protos_build_proto_init() {
+	if File_tvix_build_protos_build_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_tvix_build_protos_build_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_build_protos_build_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Build); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_build_protos_build_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildRequest_EnvVar); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_build_protos_build_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildRequest_BuildConstraints); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_build_protos_build_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildRequest_AdditionalFile); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_tvix_build_protos_build_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   5,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_tvix_build_protos_build_proto_goTypes,
+		DependencyIndexes: file_tvix_build_protos_build_proto_depIdxs,
+		MessageInfos:      file_tvix_build_protos_build_proto_msgTypes,
+	}.Build()
+	File_tvix_build_protos_build_proto = out.File
+	file_tvix_build_protos_build_proto_rawDesc = nil
+	file_tvix_build_protos_build_proto_goTypes = nil
+	file_tvix_build_protos_build_proto_depIdxs = nil
+}
diff --git a/tvix/build-go/default.nix b/tvix/build-go/default.nix
new file mode 100644
index 0000000000..62cd01b0f9
--- /dev/null
+++ b/tvix/build-go/default.nix
@@ -0,0 +1,31 @@
+{ depot, pkgs, ... }:
+
+let
+  regenerate = pkgs.writeShellScript "regenerate" ''
+    (cd $(git rev-parse --show-toplevel)/tvix/build-go && rm *.pb.go && cp ${depot.tvix.build.protos.go-bindings}/*.pb.go . && chmod +w *.pb.go)
+  '';
+in
+(pkgs.buildGoModule {
+  name = "build-go";
+  src = depot.third_party.gitignoreSource ./.;
+  vendorHash = "sha256-BprOPkgyT1F6TNToCN2uSHlkCXMdmv/QK+lTvA6O/rM=";
+}).overrideAttrs (_: {
+  meta.ci.extraSteps = {
+    check = {
+      label = ":water_buffalo: ensure generated protobuf files match";
+      needsOutput = true;
+      command = pkgs.writeShellScript "pb-go-check" ''
+        ${regenerate}
+        if [[ -n "$(git status --porcelain -unormal)" ]]; then
+            echo "-----------------------------"
+            echo ".pb.go files need to be updated, mg run //tvix/build-go/regenerate"
+            echo "-----------------------------"
+            git status -unormal
+            exit 1
+        fi
+      '';
+      alwaysRun = true;
+    };
+  };
+  passthru.regenerate = regenerate;
+})
diff --git a/tvix/build-go/go.mod b/tvix/build-go/go.mod
new file mode 100644
index 0000000000..1454b5cada
--- /dev/null
+++ b/tvix/build-go/go.mod
@@ -0,0 +1,19 @@
+module code.tvl.fyi/tvix/build-go
+
+go 1.19
+
+require (
+	code.tvl.fyi/tvix/castore-go v0.0.0-20231105151352-990d6ba2175e
+	google.golang.org/grpc v1.51.0
+	google.golang.org/protobuf v1.31.0
+)
+
+require (
+	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
+	golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
+	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
+	golang.org/x/text v0.4.0 // indirect
+	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
+	lukechampine.com/blake3 v1.1.7 // indirect
+)
diff --git a/tvix/build-go/go.sum b/tvix/build-go/go.sum
new file mode 100644
index 0000000000..cd64b9966b
--- /dev/null
+++ b/tvix/build-go/go.sum
@@ -0,0 +1,88 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+code.tvl.fyi/tvix/castore-go v0.0.0-20231105151352-990d6ba2175e h1:Nj+anfyEYeEdhnIo2BG/N1ZwQl1IvI7AH3TbNDLwUOA=
+code.tvl.fyi/tvix/castore-go v0.0.0-20231105151352-990d6ba2175e/go.mod h1:+vKbozsa04yy2TWh3kUVU568jaza3Hf0p1jAEoMoCwA=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
+google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
+lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
diff --git a/tvix/build-go/rpc_build.pb.go b/tvix/build-go/rpc_build.pb.go
new file mode 100644
index 0000000000..aae0cd9d47
--- /dev/null
+++ b/tvix/build-go/rpc_build.pb.go
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        (unknown)
+// source: tvix/build/protos/rpc_build.proto
+
+package buildv1
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+var File_tvix_build_protos_rpc_build_proto protoreflect.FileDescriptor
+
+var file_tvix_build_protos_rpc_build_proto_rawDesc = []byte{
+	0x0a, 0x21, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e,
+	0x76, 0x31, 0x1a, 0x1d, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x32, 0x4c, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x12, 0x3c, 0x0a, 0x07, 0x44, 0x6f, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x1b, 0x2e, 0x74,
+	0x76, 0x69, 0x78, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69,
+	0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x76, 0x69, 0x78,
+	0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x42,
+	0x24, 0x5a, 0x22, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e, 0x66, 0x79, 0x69, 0x2f,
+	0x74, 0x76, 0x69, 0x78, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2d, 0x67, 0x6f, 0x3b, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var file_tvix_build_protos_rpc_build_proto_goTypes = []interface{}{
+	(*BuildRequest)(nil), // 0: tvix.build.v1.BuildRequest
+	(*Build)(nil),        // 1: tvix.build.v1.Build
+}
+var file_tvix_build_protos_rpc_build_proto_depIdxs = []int32{
+	0, // 0: tvix.build.v1.BuildService.DoBuild:input_type -> tvix.build.v1.BuildRequest
+	1, // 1: tvix.build.v1.BuildService.DoBuild:output_type -> tvix.build.v1.Build
+	1, // [1:2] is the sub-list for method output_type
+	0, // [0:1] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_tvix_build_protos_rpc_build_proto_init() }
+func file_tvix_build_protos_rpc_build_proto_init() {
+	if File_tvix_build_protos_rpc_build_proto != nil {
+		return
+	}
+	file_tvix_build_protos_build_proto_init()
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_tvix_build_protos_rpc_build_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   0,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_tvix_build_protos_rpc_build_proto_goTypes,
+		DependencyIndexes: file_tvix_build_protos_rpc_build_proto_depIdxs,
+	}.Build()
+	File_tvix_build_protos_rpc_build_proto = out.File
+	file_tvix_build_protos_rpc_build_proto_rawDesc = nil
+	file_tvix_build_protos_rpc_build_proto_goTypes = nil
+	file_tvix_build_protos_rpc_build_proto_depIdxs = nil
+}
diff --git a/tvix/build-go/rpc_build_grpc.pb.go b/tvix/build-go/rpc_build_grpc.pb.go
new file mode 100644
index 0000000000..0ef5855982
--- /dev/null
+++ b/tvix/build-go/rpc_build_grpc.pb.go
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.3.0
+// - protoc             (unknown)
+// source: tvix/build/protos/rpc_build.proto
+
+package buildv1
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+const (
+	BuildService_DoBuild_FullMethodName = "/tvix.build.v1.BuildService/DoBuild"
+)
+
+// BuildServiceClient is the client API for BuildService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type BuildServiceClient interface {
+	DoBuild(ctx context.Context, in *BuildRequest, opts ...grpc.CallOption) (*Build, error)
+}
+
+type buildServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewBuildServiceClient(cc grpc.ClientConnInterface) BuildServiceClient {
+	return &buildServiceClient{cc}
+}
+
+func (c *buildServiceClient) DoBuild(ctx context.Context, in *BuildRequest, opts ...grpc.CallOption) (*Build, error) {
+	out := new(Build)
+	err := c.cc.Invoke(ctx, BuildService_DoBuild_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// BuildServiceServer is the server API for BuildService service.
+// All implementations must embed UnimplementedBuildServiceServer
+// for forward compatibility
+type BuildServiceServer interface {
+	DoBuild(context.Context, *BuildRequest) (*Build, error)
+	mustEmbedUnimplementedBuildServiceServer()
+}
+
+// UnimplementedBuildServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedBuildServiceServer struct {
+}
+
+func (UnimplementedBuildServiceServer) DoBuild(context.Context, *BuildRequest) (*Build, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method DoBuild not implemented")
+}
+func (UnimplementedBuildServiceServer) mustEmbedUnimplementedBuildServiceServer() {}
+
+// UnsafeBuildServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to BuildServiceServer will
+// result in compilation errors.
+type UnsafeBuildServiceServer interface {
+	mustEmbedUnimplementedBuildServiceServer()
+}
+
+func RegisterBuildServiceServer(s grpc.ServiceRegistrar, srv BuildServiceServer) {
+	s.RegisterService(&BuildService_ServiceDesc, srv)
+}
+
+func _BuildService_DoBuild_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(BuildRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BuildServiceServer).DoBuild(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: BuildService_DoBuild_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BuildServiceServer).DoBuild(ctx, req.(*BuildRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// BuildService_ServiceDesc is the grpc.ServiceDesc for BuildService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var BuildService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "tvix.build.v1.BuildService",
+	HandlerType: (*BuildServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "DoBuild",
+			Handler:    _BuildService_DoBuild_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "tvix/build/protos/rpc_build.proto",
+}
diff --git a/tvix/build/Cargo.toml b/tvix/build/Cargo.toml
new file mode 100644
index 0000000000..626fd35d77
--- /dev/null
+++ b/tvix/build/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "tvix-build"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+bytes = "1.4.0"
+clap = { version = "4.0", features = ["derive", "env"] }
+itertools = "0.12.0"
+prost = "0.12.1"
+thiserror = "1.0.56"
+tokio = { version = "1.32.0" }
+tokio-listener = { version = "0.3.2", features = [ "tonic011" ] }
+tonic = { version = "0.11.0", features = ["tls", "tls-roots"] }
+tvix-castore = { path = "../castore" }
+tracing = "0.1.37"
+tracing-subscriber = { version = "0.3.16", features = ["json"] }
+url = "2.4.0"
+
+[dependencies.tonic-reflection]
+optional = true
+version = "0.11.0"
+
+[build-dependencies]
+prost-build = "0.12.1"
+tonic-build = "0.11.0"
+
+[features]
+default = []
+tonic-reflection = ["dep:tonic-reflection"]
+
+[dev-dependencies]
+rstest = "0.19.0"
diff --git a/tvix/build/build.rs b/tvix/build/build.rs
new file mode 100644
index 0000000000..c3518ea877
--- /dev/null
+++ b/tvix/build/build.rs
@@ -0,0 +1,38 @@
+use std::io::Result;
+
+fn main() -> Result<()> {
+    #[allow(unused_mut)]
+    let mut builder = tonic_build::configure();
+
+    #[cfg(feature = "tonic-reflection")]
+    {
+        let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
+        let descriptor_path = out_dir.join("tvix.build.v1.bin");
+
+        builder = builder.file_descriptor_set_path(descriptor_path);
+    };
+
+    // https://github.com/hyperium/tonic/issues/908
+    let mut config = prost_build::Config::new();
+    config.bytes(["."]);
+    config.extern_path(".tvix.castore.v1", "::tvix_castore::proto");
+
+    builder
+        .build_server(true)
+        .build_client(true)
+        .emit_rerun_if_changed(false)
+        .compile_with_config(
+            config,
+            &[
+                "tvix/build/protos/build.proto",
+                "tvix/build/protos/rpc_build.proto",
+            ],
+            // If we are in running `cargo build` manually, using `../..` works fine,
+            // but in case we run inside a nix build, we need to instead point PROTO_ROOT
+            // to a sparseTree containing that structure.
+            &[match std::env::var_os("PROTO_ROOT") {
+                Some(proto_root) => proto_root.to_str().unwrap().to_owned(),
+                None => "../..".to_string(),
+            }],
+        )
+}
diff --git a/tvix/build/default.nix b/tvix/build/default.nix
new file mode 100644
index 0000000000..a2a3bea0c5
--- /dev/null
+++ b/tvix/build/default.nix
@@ -0,0 +1,5 @@
+{ depot, pkgs, ... }:
+
+depot.tvix.crates.workspaceMembers.tvix-build.build.override {
+  runTests = true;
+}
diff --git a/tvix/build/protos/LICENSE b/tvix/build/protos/LICENSE
new file mode 100644
index 0000000000..2034ada6fd
--- /dev/null
+++ b/tvix/build/protos/LICENSE
@@ -0,0 +1,21 @@
+Copyright Β© The Tvix Authors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+β€œSoftware”), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/tvix/build/protos/build.proto b/tvix/build/protos/build.proto
new file mode 100644
index 0000000000..f1f6bf0b05
--- /dev/null
+++ b/tvix/build/protos/build.proto
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+syntax = "proto3";
+
+package tvix.build.v1;
+
+import "tvix/castore/protos/castore.proto";
+
+option go_package = "code.tvl.fyi/tvix/build-go;buildv1";
+
+// A BuildRequest describes the request of something to be run on the builder.
+// It is distinct from an actual [Build] that has already happened, or might be
+// currently ongoing.
+//
+// A BuildRequest can be seen as a more normalized version of a Derivation
+// (parsed from A-Term), "writing out" some of the Nix-internal details about
+// how e.g. environment variables in the build are set.
+//
+// Nix has some impurities when building a Derivation, for example the --cores option
+// ends up as an environment variable in the build, that's not part of the ATerm.
+//
+// As of now, we serialize this into the BuildRequest, so builders can stay dumb.
+// This might change in the future.
+//
+// There's also a big difference when it comes to how inputs are modelled:
+//  - Nix only uses store path (strings) to describe the inputs.
+//    As store paths can be input-addressed, a certain store path can contain
+//    different contents (as not all store paths are binary reproducible).
+//    This requires that for every input-addressed input, the builder has access
+//    to either the input's deriver (and needs to build it) or else a trusted
+//    source for the built input.
+//    to upload input-addressed paths, requiring the trusted users concept.
+//  - tvix-build records a list of tvix.castore.v1.Node as inputs.
+//    These map from the store path base name to their contents, relieving the
+//    builder from having to "trust" any input-addressed paths, contrary to Nix.
+//
+// While this approach gives a better hermeticity, it has one downside:
+// A BuildRequest can only be sent once the contents of all its inputs are known.
+//
+// As of now, we're okay to accept this, but it prevents uploading an
+// entirely-non-IFD subgraph of BuildRequests eagerly.
+//
+// FUTUREWORK: We might be introducing another way to refer to inputs, to
+// support "send all BuildRequest for a nixpkgs eval to a remote builder and put
+// the laptop to sleep" usecases later.
+message BuildRequest {
+  // The list of all root nodes that should be visible in `inputs_dir` at the
+  // time of the build.
+  // As root nodes are content-addressed, no additional signatures are needed
+  // to substitute / make these available in the build environment.
+  // Inputs MUST be sorted by their names.
+  repeated tvix.castore.v1.Node inputs = 1;
+
+  // The command (and its args) executed as the build script.
+  // In the case of a Nix derivation, this is usually
+  // ["/path/to/some-bash/bin/bash", "-e", "/path/to/some/builder.sh"].
+  repeated string command_args = 2;
+
+  // The working dir of the command, relative to the build root.
+  // "build", in the case of Nix.
+  // This MUST be a clean relative path, without any ".", "..", or superfluous
+  // slashes.
+  string working_dir = 3;
+
+  // A list of "scratch" paths, relative to the build root.
+  // These will be write-able during the build.
+  // [build, nix/store] in the case of Nix.
+  // These MUST be clean relative paths, without any ".", "..", or superfluous
+  // slashes, and sorted.
+  repeated string scratch_paths = 4;
+
+  // The path where the castore input nodes will be located at,
+  // "nix/store" in case of Nix.
+  // Builds might also write into here (Nix builds do that).
+  // This MUST be a clean relative path, without any ".", "..", or superfluous
+  // slashes.
+  string inputs_dir = 5;
+
+  // The list of output paths the build is expected to produce,
+  // relative to the root.
+  // If the path is not produced, the build is considered to have failed.
+  // These MUST be clean relative paths, without any ".", "..", or superfluous
+  // slashes, and sorted.
+  repeated string outputs = 6;
+
+  // The list of environment variables and their values that should be set
+  // inside the build environment.
+  // This includes both environment vars set inside the derivation, as well as
+  // more "ephemeral" ones like NIX_BUILD_CORES, controlled by the `--cores`
+  // CLI option of `nix-build`.
+  // For now, we consume this as an option when turning a Derivation into a BuildRequest,
+  // similar to how Nix has a `--cores` option.
+  // We don't want to bleed these very nix-specific sandbox impl details into
+  // (dumber) builders if we don't have to.
+  // Environment variables are sorted by their keys.
+  repeated EnvVar environment_vars = 7;
+
+  message EnvVar {
+    // name of the environment variable. Must not contain =.
+    string key = 1;
+    bytes value = 2;
+  }
+
+  // A set of constraints that need to be satisfied on a build host before a
+  // Build can be started.
+  BuildConstraints constraints = 8;
+
+  // BuildConstraints represents certain conditions that must be fulfilled
+  // inside the build environment to be able to build this.
+  // Constraints can be things like required architecture and minimum amount of memory.
+  // The required input paths are *not* represented in here, because it
+  // wouldn't be hermetic enough - see the comment around inputs too.
+  message BuildConstraints {
+    // The system that's needed to execute the build.
+    // Must not be empty.
+    string system = 1;
+
+    // The amount of memory required to be available for the build, in bytes.
+    uint64 min_memory = 2;
+
+    // A list of (absolute) paths that need to be available in the build
+    // environment, like `/dev/kvm`.
+    // This is distinct from the castore nodes in inputs.
+    // TODO: check if these should be individual constraints instead.
+    // These MUST be clean absolute paths, without any ".", "..", or superfluous
+    // slashes, and sorted.
+    repeated string available_ro_paths = 3;
+
+    // Whether the build should be able to access the network,
+    bool network_access = 4;
+
+    // Whether to provide a /bin/sh inside the build environment, usually a static bash.
+    bool provide_bin_sh = 5;
+  }
+
+  // Additional (small) files and their contents that should be placed into the
+  // build environment, but outside inputs_dir.
+  // Used for passAsFile and structuredAttrs in Nix.
+  repeated AdditionalFile additional_files = 9;
+
+  message AdditionalFile {
+    string path = 1;
+    bytes contents = 2;
+  }
+
+  // TODO: allow describing something like "preferLocal", to influence composition?
+}
+
+// A Build is (one possible) outcome of executing a [BuildRequest].
+message Build {
+  // The orginal build request producing the build.
+  BuildRequest build_request = 1; // <- TODO: define hashing scheme for BuildRequest, refer to it by hash?
+
+  // The outputs that were produced after successfully building.
+  // They are sorted by their names.
+  repeated tvix.castore.v1.Node outputs = 2;
+
+  // TODO: where did this run, how long, logs, …
+}
+
+/// TODO: check remarkable notes on constraints again
+/// TODO: https://github.com/adisbladis/go-nix/commit/603df5db86ab97ba29f6f94d74f4e51642c56834
diff --git a/tvix/build/protos/default.nix b/tvix/build/protos/default.nix
new file mode 100644
index 0000000000..790655ac75
--- /dev/null
+++ b/tvix/build/protos/default.nix
@@ -0,0 +1,56 @@
+{ depot, pkgs, ... }:
+let
+  protos = depot.nix.sparseTree {
+    name = "build-protos";
+    root = depot.path.origSrc;
+    paths = [
+      # We need to include castore.proto (only), as it's referred.
+      ../../castore/protos/castore.proto
+      ./build.proto
+      ./rpc_build.proto
+      ../../../buf.yaml
+      ../../../buf.gen.yaml
+    ];
+  };
+in
+depot.nix.readTree.drvTargets {
+  inherit protos;
+
+  # Lints and ensures formatting of the proto files.
+  check = pkgs.stdenv.mkDerivation {
+    name = "proto-check";
+    src = protos;
+
+    nativeBuildInputs = [
+      pkgs.buf
+    ];
+
+    buildPhase = ''
+      export HOME=$TMPDIR
+      buf lint
+      buf format -d --exit-code
+      touch $out
+    '';
+  };
+
+  # Produces the golang bindings.
+  go-bindings = pkgs.stdenv.mkDerivation {
+    name = "go-bindings";
+
+    src = protos;
+
+    nativeBuildInputs = [
+      pkgs.buf
+      pkgs.protoc-gen-go
+      pkgs.protoc-gen-go-grpc
+    ];
+
+    buildPhase = ''
+      export HOME=$TMPDIR
+      buf generate
+
+      mkdir -p $out
+      cp tvix/build/protos/*.pb.go $out/
+    '';
+  };
+}
diff --git a/tvix/build/protos/rpc_build.proto b/tvix/build/protos/rpc_build.proto
new file mode 100644
index 0000000000..73eebf78fe
--- /dev/null
+++ b/tvix/build/protos/rpc_build.proto
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+syntax = "proto3";
+
+package tvix.build.v1;
+
+import "tvix/build/protos/build.proto";
+
+option go_package = "code.tvl.fyi/tvix/build-go;buildv1";
+
+service BuildService {
+  rpc DoBuild(BuildRequest) returns (Build);
+}
diff --git a/tvix/build/src/bin/tvix-build.rs b/tvix/build/src/bin/tvix-build.rs
new file mode 100644
index 0000000000..ed36c8933c
--- /dev/null
+++ b/tvix/build/src/bin/tvix-build.rs
@@ -0,0 +1,132 @@
+use std::sync::Arc;
+
+use clap::Parser;
+use clap::Subcommand;
+use tokio_listener::Listener;
+use tokio_listener::SystemOptions;
+use tokio_listener::UserOptions;
+use tonic::{self, transport::Server};
+use tracing::{info, Level};
+use tracing_subscriber::prelude::*;
+use tvix_build::{
+    buildservice,
+    proto::{build_service_server::BuildServiceServer, GRPCBuildServiceWrapper},
+};
+use tvix_castore::blobservice;
+use tvix_castore::directoryservice;
+
+#[cfg(feature = "tonic-reflection")]
+use tvix_build::proto::FILE_DESCRIPTOR_SET;
+#[cfg(feature = "tonic-reflection")]
+use tvix_castore::proto::FILE_DESCRIPTOR_SET as CASTORE_FILE_DESCRIPTOR_SET;
+
+#[derive(Parser)]
+#[command(author, version, about, long_about = None)]
+struct Cli {
+    /// Whether to log in JSON
+    #[arg(long)]
+    json: bool,
+
+    #[arg(long)]
+    log_level: Option<Level>,
+
+    #[command(subcommand)]
+    command: Commands,
+}
+#[derive(Subcommand)]
+enum Commands {
+    /// Runs the tvix-build daemon.
+    Daemon {
+        #[arg(long, short = 'l')]
+        listen_address: Option<String>,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        blob_service_addr: String,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        directory_service_addr: String,
+
+        #[arg(long, env, default_value = "dummy://")]
+        build_service_addr: String,
+    },
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let cli = Cli::parse();
+
+    // configure log settings
+    let level = cli.log_level.unwrap_or(Level::INFO);
+
+    let subscriber = tracing_subscriber::registry()
+        .with(
+            cli.json.then_some(
+                tracing_subscriber::fmt::Layer::new()
+                    .with_writer(std::io::stderr.with_max_level(level))
+                    .json(),
+            ),
+        )
+        .with(
+            (!cli.json).then_some(
+                tracing_subscriber::fmt::Layer::new()
+                    .with_writer(std::io::stderr.with_max_level(level))
+                    .pretty(),
+            ),
+        );
+
+    tracing::subscriber::set_global_default(subscriber).expect("Unable to set global subscriber");
+
+    match cli.command {
+        Commands::Daemon {
+            listen_address,
+            blob_service_addr,
+            directory_service_addr,
+            build_service_addr,
+        } => {
+            // initialize stores
+            let blob_service = blobservice::from_addr(&blob_service_addr).await?;
+            let directory_service = directoryservice::from_addr(&directory_service_addr).await?;
+
+            let build_service = buildservice::from_addr(
+                &build_service_addr,
+                Arc::from(blob_service),
+                Arc::from(directory_service),
+            )
+            .await?;
+
+            let listen_address = listen_address
+                .unwrap_or_else(|| "[::]:8000".to_string())
+                .parse()
+                .unwrap();
+
+            let mut server = Server::builder();
+
+            #[allow(unused_mut)]
+            let mut router = server.add_service(BuildServiceServer::new(
+                GRPCBuildServiceWrapper::new(build_service),
+            ));
+
+            #[cfg(feature = "tonic-reflection")]
+            {
+                let reflection_svc = tonic_reflection::server::Builder::configure()
+                    .register_encoded_file_descriptor_set(CASTORE_FILE_DESCRIPTOR_SET)
+                    .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
+                    .build()?;
+                router = router.add_service(reflection_svc);
+            }
+
+            info!(listen_address=%listen_address, "listening");
+
+            let listener = Listener::bind(
+                &listen_address,
+                &SystemOptions::default(),
+                &UserOptions::default(),
+            )
+            .await?;
+
+            router.serve_with_incoming(listener).await?;
+        }
+    }
+
+    Ok(())
+}
diff --git a/tvix/build/src/buildservice/dummy.rs b/tvix/build/src/buildservice/dummy.rs
new file mode 100644
index 0000000000..d20444755e
--- /dev/null
+++ b/tvix/build/src/buildservice/dummy.rs
@@ -0,0 +1,19 @@
+use tonic::async_trait;
+use tracing::instrument;
+
+use super::BuildService;
+use crate::proto::{Build, BuildRequest};
+
+#[derive(Default)]
+pub struct DummyBuildService {}
+
+#[async_trait]
+impl BuildService for DummyBuildService {
+    #[instrument(skip(self), ret, err)]
+    async fn do_build(&self, _request: BuildRequest) -> std::io::Result<Build> {
+        Err(std::io::Error::new(
+            std::io::ErrorKind::Other,
+            "builds are not supported with DummyBuildService",
+        ))
+    }
+}
diff --git a/tvix/build/src/buildservice/from_addr.rs b/tvix/build/src/buildservice/from_addr.rs
new file mode 100644
index 0000000000..cc5403edef
--- /dev/null
+++ b/tvix/build/src/buildservice/from_addr.rs
@@ -0,0 +1,90 @@
+use super::{grpc::GRPCBuildService, BuildService, DummyBuildService};
+use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
+use url::Url;
+
+/// Constructs a new instance of a [BuildService] from an URI.
+///
+/// The following schemes are supported by the following services:
+/// - `dummy://` ([DummyBuildService])
+/// - `grpc+*://` ([GRPCBuildService])
+///
+/// As some of these [BuildService] need to talk to a [BlobService] and
+/// [DirectoryService], these also need to be passed in.
+pub async fn from_addr<BS, DS>(
+    uri: &str,
+    _blob_service: BS,
+    _directory_service: DS,
+) -> std::io::Result<Box<dyn BuildService>>
+where
+    BS: AsRef<dyn BlobService> + Send + Sync + Clone + 'static,
+    DS: AsRef<dyn DirectoryService> + Send + Sync + Clone + 'static,
+{
+    let url = Url::parse(uri)
+        .map_err(|e| std::io::Error::other(format!("unable to parse url: {}", e)))?;
+
+    Ok(match url.scheme() {
+        // dummy doesn't care about parameters.
+        "dummy" => Box::<DummyBuildService>::default(),
+        scheme => {
+            if scheme.starts_with("grpc+") {
+                let client = crate::proto::build_service_client::BuildServiceClient::new(
+                    tvix_castore::tonic::channel_from_url(&url)
+                        .await
+                        .map_err(std::io::Error::other)?,
+                );
+                // FUTUREWORK: also allow responding to {blob,directory}_service
+                // requests from the remote BuildService?
+                Box::new(GRPCBuildService::from_client(client))
+            } else {
+                Err(std::io::Error::other(format!(
+                    "unknown scheme: {}",
+                    url.scheme()
+                )))?
+            }
+        }
+    })
+}
+
+#[cfg(test)]
+mod tests {
+    use std::sync::Arc;
+
+    use super::from_addr;
+    use rstest::rstest;
+    use tvix_castore::{
+        blobservice::{BlobService, MemoryBlobService},
+        directoryservice::{DirectoryService, MemoryDirectoryService},
+    };
+
+    #[rstest]
+    /// This uses an unsupported scheme.
+    #[case::unsupported_scheme("http://foo.example/test", false)]
+    /// This configures dummy
+    #[case::valid_dummy("dummy://", true)]
+    /// Correct scheme to connect to a unix socket.
+    #[case::grpc_valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
+    /// Correct scheme for unix socket, but setting a host too, which is invalid.
+    #[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
+    /// Correct scheme to connect to localhost, with port 12345
+    #[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
+    /// Correct scheme to connect to localhost over http, but with additional path, which is invalid.
+    #[case::grpc_invalid_host_and_path("grpc+http://localhost/some-path", false)]
+    #[tokio::test]
+    async fn test_from_addr(#[case] uri_str: &str, #[case] exp_succeed: bool) {
+        let blob_service: Arc<dyn BlobService> = Arc::from(MemoryBlobService::default());
+        let directory_service: Arc<dyn DirectoryService> =
+            Arc::from(MemoryDirectoryService::default());
+
+        let resp = from_addr(uri_str, blob_service, directory_service).await;
+
+        if exp_succeed {
+            resp.expect("should succeed");
+        } else {
+            assert!(resp.is_err(), "should fail");
+        }
+    }
+}
diff --git a/tvix/build/src/buildservice/grpc.rs b/tvix/build/src/buildservice/grpc.rs
new file mode 100644
index 0000000000..9d22d8397a
--- /dev/null
+++ b/tvix/build/src/buildservice/grpc.rs
@@ -0,0 +1,28 @@
+use tonic::{async_trait, transport::Channel};
+
+use crate::proto::{build_service_client::BuildServiceClient, Build, BuildRequest};
+
+use super::BuildService;
+
+pub struct GRPCBuildService {
+    client: BuildServiceClient<Channel>,
+}
+
+impl GRPCBuildService {
+    #[allow(dead_code)]
+    pub fn from_client(client: BuildServiceClient<Channel>) -> Self {
+        Self { client }
+    }
+}
+
+#[async_trait]
+impl BuildService for GRPCBuildService {
+    async fn do_build(&self, request: BuildRequest) -> std::io::Result<Build> {
+        let mut client = self.client.clone();
+        client
+            .do_build(request)
+            .await
+            .map(|resp| resp.into_inner())
+            .map_err(std::io::Error::other)
+    }
+}
diff --git a/tvix/build/src/buildservice/mod.rs b/tvix/build/src/buildservice/mod.rs
new file mode 100644
index 0000000000..a61d782919
--- /dev/null
+++ b/tvix/build/src/buildservice/mod.rs
@@ -0,0 +1,16 @@
+use tonic::async_trait;
+
+use crate::proto::{Build, BuildRequest};
+
+mod dummy;
+mod from_addr;
+mod grpc;
+
+pub use dummy::DummyBuildService;
+pub use from_addr::from_addr;
+
+#[async_trait]
+pub trait BuildService: Send + Sync {
+    /// TODO: document
+    async fn do_build(&self, request: BuildRequest) -> std::io::Result<Build>;
+}
diff --git a/tvix/build/src/lib.rs b/tvix/build/src/lib.rs
new file mode 100644
index 0000000000..b173657e43
--- /dev/null
+++ b/tvix/build/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod buildservice;
+pub mod proto;
diff --git a/tvix/build/src/proto/grpc_buildservice_wrapper.rs b/tvix/build/src/proto/grpc_buildservice_wrapper.rs
new file mode 100644
index 0000000000..024f075de9
--- /dev/null
+++ b/tvix/build/src/proto/grpc_buildservice_wrapper.rs
@@ -0,0 +1,35 @@
+use crate::buildservice::BuildService;
+use std::ops::Deref;
+use tonic::async_trait;
+
+use super::{Build, BuildRequest};
+
+/// Implements the gRPC server trait ([crate::proto::build_service_server::BuildService]
+/// for anything implementing [BuildService].
+pub struct GRPCBuildServiceWrapper<BUILD> {
+    inner: BUILD,
+}
+
+impl<BUILD> GRPCBuildServiceWrapper<BUILD> {
+    pub fn new(build_service: BUILD) -> Self {
+        Self {
+            inner: build_service,
+        }
+    }
+}
+
+#[async_trait]
+impl<BUILD> crate::proto::build_service_server::BuildService for GRPCBuildServiceWrapper<BUILD>
+where
+    BUILD: Deref<Target = dyn BuildService> + Send + Sync + 'static,
+{
+    async fn do_build(
+        &self,
+        request: tonic::Request<BuildRequest>,
+    ) -> Result<tonic::Response<Build>, tonic::Status> {
+        match self.inner.do_build(request.into_inner()).await {
+            Ok(resp) => Ok(tonic::Response::new(resp)),
+            Err(e) => Err(tonic::Status::internal(e.to_string())),
+        }
+    }
+}
diff --git a/tvix/build/src/proto/mod.rs b/tvix/build/src/proto/mod.rs
new file mode 100644
index 0000000000..e359b5b5b7
--- /dev/null
+++ b/tvix/build/src/proto/mod.rs
@@ -0,0 +1,264 @@
+use std::path::{Path, PathBuf};
+
+use itertools::Itertools;
+use tvix_castore::proto::{NamedNode, ValidateNodeError};
+
+mod grpc_buildservice_wrapper;
+
+pub use grpc_buildservice_wrapper::GRPCBuildServiceWrapper;
+
+tonic::include_proto!("tvix.build.v1");
+
+#[cfg(feature = "tonic-reflection")]
+/// Compiled file descriptors for implementing [gRPC
+/// reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) with e.g.
+/// [`tonic_reflection`](https://docs.rs/tonic-reflection).
+pub const FILE_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("tvix.build.v1");
+
+/// Errors that occur during the validation of [BuildRequest] messages.
+#[derive(Debug, thiserror::Error)]
+pub enum ValidateBuildRequestError {
+    #[error("invalid input node at position {0}: {1}")]
+    InvalidInputNode(usize, ValidateNodeError),
+
+    #[error("input nodes are not sorted by name")]
+    InputNodesNotSorted,
+
+    #[error("invalid working_dir")]
+    InvalidWorkingDir,
+
+    #[error("scratch_paths not sorted")]
+    ScratchPathsNotSorted,
+
+    #[error("invalid scratch path at position {0}")]
+    InvalidScratchPath(usize),
+
+    #[error("invalid inputs_dir")]
+    InvalidInputsDir,
+
+    #[error("invalid output path at position {0}")]
+    InvalidOutputPath(usize),
+
+    #[error("outputs not sorted")]
+    OutputsNotSorted,
+
+    #[error("invalid environment variable at position {0}")]
+    InvalidEnvVar(usize),
+
+    #[error("EnvVar not sorted by their keys")]
+    EnvVarNotSorted,
+
+    #[error("invalid build constraints: {0}")]
+    InvalidBuildConstraints(ValidateBuildConstraintsError),
+
+    #[error("invalid additional file path at position: {0}")]
+    InvalidAdditionalFilePath(usize),
+
+    #[error("additional_files not sorted")]
+    AdditionalFilesNotSorted,
+}
+
+/// Checks a path to be without any '..' components, and clean (no superfluous
+/// slashes).
+fn is_clean_path<P: AsRef<Path>>(p: P) -> bool {
+    let p = p.as_ref();
+
+    // Look at all components, bail in case of ".", ".." and empty normal
+    // segments (superfluous slashes)
+    // We still need to assemble a cleaned PathBuf, and compare the OsString
+    // later, as .components() already does do some normalization before
+    // yielding.
+    let mut cleaned_p = PathBuf::new();
+    for component in p.components() {
+        match component {
+            std::path::Component::Prefix(_) => {}
+            std::path::Component::RootDir => {}
+            std::path::Component::CurDir => return false,
+            std::path::Component::ParentDir => return false,
+            std::path::Component::Normal(a) => {
+                if a.is_empty() {
+                    return false;
+                }
+            }
+        }
+        cleaned_p.push(component);
+    }
+
+    // if cleaned_p looks like p, we're good.
+    if cleaned_p.as_os_str() != p.as_os_str() {
+        return false;
+    }
+
+    true
+}
+
+fn is_clean_relative_path<P: AsRef<Path>>(p: P) -> bool {
+    if p.as_ref().is_absolute() {
+        return false;
+    }
+
+    is_clean_path(p)
+}
+
+fn is_clean_absolute_path<P: AsRef<Path>>(p: P) -> bool {
+    if !p.as_ref().is_absolute() {
+        return false;
+    }
+
+    is_clean_path(p)
+}
+
+/// Checks if a given list is sorted.
+fn is_sorted<I>(data: I) -> bool
+where
+    I: Iterator,
+    I::Item: Ord + Clone,
+{
+    data.tuple_windows().all(|(a, b)| a <= b)
+}
+
+impl BuildRequest {
+    /// Ensures the build request is valid.
+    /// This means, all input nodes need to be valid, paths in lists need to be sorted,
+    /// and all restrictions around paths themselves (relative, clean, …) need
+    // to be fulfilled.
+    pub fn validate(&self) -> Result<(), ValidateBuildRequestError> {
+        // validate all input nodes
+        for (i, n) in self.inputs.iter().enumerate() {
+            // ensure the input node itself is valid
+            n.validate()
+                .map_err(|e| ValidateBuildRequestError::InvalidInputNode(i, e))?;
+        }
+
+        // now we can look at the names, and make sure they're sorted.
+        if !is_sorted(
+            self.inputs
+                .iter()
+                .map(|e| e.node.as_ref().unwrap().get_name()),
+        ) {
+            Err(ValidateBuildRequestError::InputNodesNotSorted)?
+        }
+
+        // validate working_dir
+        if !is_clean_relative_path(&self.working_dir) {
+            Err(ValidateBuildRequestError::InvalidWorkingDir)?;
+        }
+
+        // validate scratch paths
+        for (i, p) in self.scratch_paths.iter().enumerate() {
+            if !is_clean_relative_path(p) {
+                Err(ValidateBuildRequestError::InvalidScratchPath(i))?
+            }
+        }
+        if !is_sorted(self.scratch_paths.iter().map(|e| e.as_bytes())) {
+            Err(ValidateBuildRequestError::ScratchPathsNotSorted)?;
+        }
+
+        // validate inputs_dir
+        if !is_clean_relative_path(&self.inputs_dir) {
+            Err(ValidateBuildRequestError::InvalidInputsDir)?;
+        }
+
+        // validate outputs
+        for (i, p) in self.outputs.iter().enumerate() {
+            if !is_clean_relative_path(p) {
+                Err(ValidateBuildRequestError::InvalidOutputPath(i))?
+            }
+        }
+        if !is_sorted(self.outputs.iter().map(|e| e.as_bytes())) {
+            Err(ValidateBuildRequestError::OutputsNotSorted)?;
+        }
+
+        // validate environment_vars.
+        for (i, e) in self.environment_vars.iter().enumerate() {
+            if e.key.is_empty() || e.key.contains('=') {
+                Err(ValidateBuildRequestError::InvalidEnvVar(i))?
+            }
+        }
+        if !is_sorted(self.environment_vars.iter().map(|e| e.key.as_bytes())) {
+            Err(ValidateBuildRequestError::EnvVarNotSorted)?;
+        }
+
+        // validate build constraints
+        if let Some(constraints) = self.constraints.as_ref() {
+            constraints
+                .validate()
+                .map_err(ValidateBuildRequestError::InvalidBuildConstraints)?;
+        }
+
+        // validate additional_files
+        for (i, additional_file) in self.additional_files.iter().enumerate() {
+            if !is_clean_relative_path(&additional_file.path) {
+                Err(ValidateBuildRequestError::InvalidAdditionalFilePath(i))?
+            }
+        }
+        if !is_sorted(self.additional_files.iter().map(|e| e.path.as_bytes())) {
+            Err(ValidateBuildRequestError::AdditionalFilesNotSorted)?;
+        }
+
+        Ok(())
+    }
+}
+
+/// Errors that occur during the validation of
+/// [build_request::BuildConstraints] messages.
+#[derive(Debug, thiserror::Error)]
+pub enum ValidateBuildConstraintsError {
+    #[error("invalid system")]
+    InvalidSystem,
+
+    #[error("invalid available_ro_paths at position {0}")]
+    InvalidAvailableRoPaths(usize),
+
+    #[error("available_ro_paths not sorted")]
+    AvailableRoPathsNotSorted,
+}
+
+impl build_request::BuildConstraints {
+    pub fn validate(&self) -> Result<(), ValidateBuildConstraintsError> {
+        // validate system
+        if self.system.is_empty() {
+            Err(ValidateBuildConstraintsError::InvalidSystem)?;
+        }
+        // validate available_ro_paths
+        for (i, p) in self.available_ro_paths.iter().enumerate() {
+            if !is_clean_absolute_path(p) {
+                Err(ValidateBuildConstraintsError::InvalidAvailableRoPaths(i))?
+            }
+        }
+        if !is_sorted(self.available_ro_paths.iter().map(|e| e.as_bytes())) {
+            Err(ValidateBuildConstraintsError::AvailableRoPathsNotSorted)?;
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{is_clean_path, is_clean_relative_path};
+    use rstest::rstest;
+
+    #[rstest]
+    #[case::fail_trailing_slash("foo/bar/", false)]
+    #[case::fail_dotdot("foo/../bar", false)]
+    #[case::fail_singledot("foo/./bar", false)]
+    #[case::fail_unnecessary_slashes("foo//bar", false)]
+    #[case::fail_absolute_unnecessary_slashes("//foo/bar", false)]
+    #[case::ok_empty("", true)]
+    #[case::ok_relative("foo/bar", true)]
+    #[case::ok_absolute("/", true)]
+    #[case::ok_absolute2("/foo/bar", true)]
+    fn test_is_clean_path(#[case] s: &str, #[case] expected: bool) {
+        assert_eq!(is_clean_path(s), expected);
+    }
+
+    #[rstest]
+    #[case::fail_absolute("/", false)]
+    #[case::ok_relative("foo/bar", true)]
+    fn test_is_clean_relative_path(#[case] s: &str, #[case] expected: bool) {
+        assert_eq!(is_clean_relative_path(s), expected);
+    }
+
+    // TODO: add tests for BuildRequest validation itself
+}
diff --git a/tvix/buildkite.yml b/tvix/buildkite.yml
new file mode 100644
index 0000000000..2a24ca4264
--- /dev/null
+++ b/tvix/buildkite.yml
@@ -0,0 +1,10 @@
+# Build pipeline for the filtered //views/tvix workspace of depot. This
+# pipeline is triggered by each build of canon.
+#
+# Pipeline status is visible on https://buildkite.com/tvl/tvix
+
+steps:
+  - label: ":crab: cargo build"
+    command: |
+      nix-shell --run "cargo build && cargo test"
+    timeout_in_minutes: 10
diff --git a/tvix/castore-go/LICENSE b/tvix/castore-go/LICENSE
new file mode 100644
index 0000000000..2034ada6fd
--- /dev/null
+++ b/tvix/castore-go/LICENSE
@@ -0,0 +1,21 @@
+Copyright Β© The Tvix Authors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+β€œSoftware”), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/tvix/castore-go/README.md b/tvix/castore-go/README.md
new file mode 100644
index 0000000000..c7a2205ae8
--- /dev/null
+++ b/tvix/castore-go/README.md
@@ -0,0 +1,10 @@
+# castore-go
+
+This directory contains generated golang bindings, both for the `tvix-castore`
+data models, as well as the gRPC bindings.
+
+They are generated with `mg run //tvix:castore-go:regenerate`.
+These files end with `.pb.go`, and are ensured to be up to date by a CI check.
+
+Additionally, code useful when interacting with these data structures
+(ending just with `.go`) is provided.
diff --git a/tvix/castore-go/castore.go b/tvix/castore-go/castore.go
new file mode 100644
index 0000000000..40aeaf1911
--- /dev/null
+++ b/tvix/castore-go/castore.go
@@ -0,0 +1,212 @@
+package castorev1
+
+import (
+	"bytes"
+	"encoding/base64"
+	"fmt"
+
+	"google.golang.org/protobuf/proto"
+	"lukechampine.com/blake3"
+)
+
+// The size of a directory is calculated by summing up the numbers of
+// `directories`, `files` and `symlinks`, and for each directory, its size
+// field.
+func (d *Directory) Size() uint64 {
+	var size uint64
+	size = uint64(len(d.Files) + len(d.Symlinks))
+	for _, d := range d.Directories {
+		size += 1 + d.Size
+	}
+	return size
+}
+
+func (d *Directory) Digest() ([]byte, error) {
+	b, err := proto.MarshalOptions{
+		Deterministic: true,
+	}.Marshal(d)
+
+	if err != nil {
+		return nil, fmt.Errorf("error while marshalling directory: %w", err)
+	}
+
+	h := blake3.New(32, nil)
+
+	_, err = h.Write(b)
+	if err != nil {
+		return nil, fmt.Errorf("error writing to hasher: %w", err)
+	}
+
+	return h.Sum(nil), nil
+}
+
+// isValidName checks a name for validity.
+// We disallow slashes, null bytes, '.', '..' and the empty string.
+// Depending on the context, a *Node message with an empty string as name is
+// allowed, but they don't occur inside a Directory message.
+func isValidName(n []byte) bool {
+	if len(n) == 0 || bytes.Equal(n, []byte("..")) || bytes.Equal(n, []byte{'.'}) || bytes.Contains(n, []byte{'\x00'}) || bytes.Contains(n, []byte{'/'}) {
+		return false
+	}
+	return true
+}
+
+// Validate ensures a DirectoryNode has a valid name and correct digest len.
+func (n *DirectoryNode) Validate() error {
+	if len(n.Digest) != 32 {
+		return fmt.Errorf("invalid digest length for %s, expected %d, got %d", n.Name, 32, len(n.Digest))
+	}
+
+	if !isValidName(n.Name) {
+		return fmt.Errorf("invalid node name: %s", n.Name)
+	}
+
+	return nil
+}
+
+// Validate ensures a FileNode has a valid name and correct digest len.
+func (n *FileNode) Validate() error {
+	if len(n.Digest) != 32 {
+		return fmt.Errorf("invalid digest length for %s, expected %d, got %d", n.Name, 32, len(n.Digest))
+	}
+
+	if !isValidName(n.Name) {
+		return fmt.Errorf("invalid node name: %s", n.Name)
+	}
+
+	return nil
+}
+
+// Validate ensures a SymlinkNode has a valid name and target.
+func (n *SymlinkNode) Validate() error {
+	if len(n.Target) == 0 || bytes.Contains(n.Target, []byte{0}) {
+		return fmt.Errorf("invalid symlink target: %s", n.Target)
+	}
+
+	if !isValidName(n.Name) {
+		return fmt.Errorf("invalid node name: %s", n.Name)
+	}
+
+	return nil
+}
+
+// Validate ensures a node is valid, by dispatching to the per-type validation functions.
+func (n *Node) Validate() error {
+	if node := n.GetDirectory(); node != nil {
+		if err := node.Validate(); err != nil {
+			return fmt.Errorf("SymlinkNode failed validation: %w", err)
+		}
+	} else if node := n.GetFile(); node != nil {
+		if err := node.Validate(); err != nil {
+			return fmt.Errorf("FileNode failed validation: %w", err)
+		}
+	} else if node := n.GetSymlink(); node != nil {
+		if err := node.Validate(); err != nil {
+			return fmt.Errorf("SymlinkNode failed validation: %w", err)
+		}
+
+	} else {
+		// this would only happen if we introduced a new type
+		return fmt.Errorf("no specific node found")
+	}
+
+	return nil
+}
+
+// Validate thecks the Directory message for invalid data, such as:
+// - violations of name restrictions
+// - invalid digest lengths
+// - not properly sorted lists
+// - duplicate names in the three lists
+func (d *Directory) Validate() error {
+	// seenNames contains all seen names so far.
+	// We populate this to ensure node names are unique across all three lists.
+	seenNames := make(map[string]interface{})
+
+	// We also track the last seen name in each of the three lists,
+	// to ensure nodes are sorted by their names.
+	var lastDirectoryName, lastFileName, lastSymlinkName []byte
+
+	// helper function to only insert in sorted order.
+	// used with the three lists above.
+	// Note this consumes a *pointer to* a string,  as it mutates it.
+	insertIfGt := func(lastName *[]byte, name []byte) error {
+		// update if it's greater than the previous name
+		if bytes.Compare(name, *lastName) == 1 {
+			*lastName = name
+			return nil
+		} else {
+			return fmt.Errorf("%v is not in sorted order", name)
+		}
+	}
+
+	// insertOnce inserts into seenNames if the key doesn't exist yet.
+	insertOnce := func(name []byte) error {
+		encoded := base64.StdEncoding.EncodeToString(name)
+		if _, found := seenNames[encoded]; found {
+			return fmt.Errorf("duplicate name: %v", string(name))
+		}
+		seenNames[encoded] = nil
+		return nil
+	}
+
+	// Loop over all Directories, Files and Symlinks individually,
+	// check them for validity, then check for sorting in the current list, and
+	// uniqueness across all three lists.
+	for _, directoryNode := range d.Directories {
+		directoryName := directoryNode.GetName()
+
+		if err := directoryNode.Validate(); err != nil {
+			return fmt.Errorf("DirectoryNode %s failed validation: %w", directoryName, err)
+		}
+
+		// ensure names are sorted
+		if err := insertIfGt(&lastDirectoryName, directoryName); err != nil {
+			return err
+		}
+
+		// add to seenNames
+		if err := insertOnce(directoryName); err != nil {
+			return err
+		}
+
+	}
+
+	for _, fileNode := range d.Files {
+		fileName := fileNode.GetName()
+
+		if err := fileNode.Validate(); err != nil {
+			return fmt.Errorf("FileNode %s failed validation: %w", fileName, err)
+		}
+
+		// ensure names are sorted
+		if err := insertIfGt(&lastFileName, fileName); err != nil {
+			return err
+		}
+
+		// add to seenNames
+		if err := insertOnce(fileName); err != nil {
+			return err
+		}
+	}
+
+	for _, symlinkNode := range d.Symlinks {
+		symlinkName := symlinkNode.GetName()
+
+		if err := symlinkNode.Validate(); err != nil {
+			return fmt.Errorf("SymlinkNode %s failed validation: %w", symlinkName, err)
+		}
+
+		// ensure names are sorted
+		if err := insertIfGt(&lastSymlinkName, symlinkName); err != nil {
+			return err
+		}
+
+		// add to seenNames
+		if err := insertOnce(symlinkName); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/tvix/castore-go/castore.pb.go b/tvix/castore-go/castore.pb.go
new file mode 100644
index 0000000000..464f1d4a41
--- /dev/null
+++ b/tvix/castore-go/castore.pb.go
@@ -0,0 +1,580 @@
+// SPDX-FileCopyrightText: edef <edef@unfathomable.blue>
+// SPDX-License-Identifier: OSL-3.0 OR MIT OR Apache-2.0
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        (unknown)
+// source: tvix/castore/protos/castore.proto
+
+package castorev1
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// A Directory can contain Directory, File or Symlink nodes.
+// Each of these nodes have a name attribute, which is the basename in that
+// directory and node type specific attributes.
+// The name attribute:
+//   - MUST not contain slashes or null bytes
+//   - MUST not be '.' or '..'
+//   - MUST be unique across all three lists
+//
+// Elements in each list need to be lexicographically ordered by the name
+// attribute.
+type Directory struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Directories []*DirectoryNode `protobuf:"bytes,1,rep,name=directories,proto3" json:"directories,omitempty"`
+	Files       []*FileNode      `protobuf:"bytes,2,rep,name=files,proto3" json:"files,omitempty"`
+	Symlinks    []*SymlinkNode   `protobuf:"bytes,3,rep,name=symlinks,proto3" json:"symlinks,omitempty"`
+}
+
+func (x *Directory) Reset() {
+	*x = Directory{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_castore_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Directory) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Directory) ProtoMessage() {}
+
+func (x *Directory) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_castore_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Directory.ProtoReflect.Descriptor instead.
+func (*Directory) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Directory) GetDirectories() []*DirectoryNode {
+	if x != nil {
+		return x.Directories
+	}
+	return nil
+}
+
+func (x *Directory) GetFiles() []*FileNode {
+	if x != nil {
+		return x.Files
+	}
+	return nil
+}
+
+func (x *Directory) GetSymlinks() []*SymlinkNode {
+	if x != nil {
+		return x.Symlinks
+	}
+	return nil
+}
+
+// A DirectoryNode represents a directory in a Directory.
+type DirectoryNode struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The (base)name of the directory
+	Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	// The blake3 hash of a Directory message, serialized in protobuf canonical form.
+	Digest []byte `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"`
+	// Number of child elements in the Directory referred to by `digest`.
+	// Calculated by summing up the numbers of `directories`, `files` and
+	// `symlinks`, and for each directory, its size field. Used for inode number
+	// calculation.
+	// This field is precisely as verifiable as any other Merkle tree edge.
+	// Resolve `digest`, and you can compute it incrementally. Resolve the entire
+	// tree, and you can fully compute it from scratch.
+	// A credulous implementation won't reject an excessive size, but this is
+	// harmless: you'll have some ordinals without nodes. Undersizing is obvious
+	// and easy to reject: you won't have an ordinal for some nodes.
+	Size uint64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
+}
+
+func (x *DirectoryNode) Reset() {
+	*x = DirectoryNode{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_castore_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DirectoryNode) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DirectoryNode) ProtoMessage() {}
+
+func (x *DirectoryNode) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_castore_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DirectoryNode.ProtoReflect.Descriptor instead.
+func (*DirectoryNode) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *DirectoryNode) GetName() []byte {
+	if x != nil {
+		return x.Name
+	}
+	return nil
+}
+
+func (x *DirectoryNode) GetDigest() []byte {
+	if x != nil {
+		return x.Digest
+	}
+	return nil
+}
+
+func (x *DirectoryNode) GetSize() uint64 {
+	if x != nil {
+		return x.Size
+	}
+	return 0
+}
+
+// A FileNode represents a regular or executable file in a Directory.
+type FileNode struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The (base)name of the file
+	Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	// The blake3 digest of the file contents
+	Digest []byte `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"`
+	// The file content size
+	Size uint64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
+	// Whether the file is executable
+	Executable bool `protobuf:"varint,4,opt,name=executable,proto3" json:"executable,omitempty"`
+}
+
+func (x *FileNode) Reset() {
+	*x = FileNode{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_castore_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FileNode) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FileNode) ProtoMessage() {}
+
+func (x *FileNode) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_castore_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use FileNode.ProtoReflect.Descriptor instead.
+func (*FileNode) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *FileNode) GetName() []byte {
+	if x != nil {
+		return x.Name
+	}
+	return nil
+}
+
+func (x *FileNode) GetDigest() []byte {
+	if x != nil {
+		return x.Digest
+	}
+	return nil
+}
+
+func (x *FileNode) GetSize() uint64 {
+	if x != nil {
+		return x.Size
+	}
+	return 0
+}
+
+func (x *FileNode) GetExecutable() bool {
+	if x != nil {
+		return x.Executable
+	}
+	return false
+}
+
+// A SymlinkNode represents a symbolic link in a Directory.
+type SymlinkNode struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The (base)name of the symlink
+	Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	// The target of the symlink.
+	Target []byte `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
+}
+
+func (x *SymlinkNode) Reset() {
+	*x = SymlinkNode{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_castore_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SymlinkNode) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SymlinkNode) ProtoMessage() {}
+
+func (x *SymlinkNode) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_castore_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SymlinkNode.ProtoReflect.Descriptor instead.
+func (*SymlinkNode) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *SymlinkNode) GetName() []byte {
+	if x != nil {
+		return x.Name
+	}
+	return nil
+}
+
+func (x *SymlinkNode) GetTarget() []byte {
+	if x != nil {
+		return x.Target
+	}
+	return nil
+}
+
+// A Node is either a DirectoryNode, FileNode or SymlinkNode.
+type Node struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to Node:
+	//
+	//	*Node_Directory
+	//	*Node_File
+	//	*Node_Symlink
+	Node isNode_Node `protobuf_oneof:"node"`
+}
+
+func (x *Node) Reset() {
+	*x = Node{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_castore_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Node) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Node) ProtoMessage() {}
+
+func (x *Node) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_castore_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Node.ProtoReflect.Descriptor instead.
+func (*Node) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{4}
+}
+
+func (m *Node) GetNode() isNode_Node {
+	if m != nil {
+		return m.Node
+	}
+	return nil
+}
+
+func (x *Node) GetDirectory() *DirectoryNode {
+	if x, ok := x.GetNode().(*Node_Directory); ok {
+		return x.Directory
+	}
+	return nil
+}
+
+func (x *Node) GetFile() *FileNode {
+	if x, ok := x.GetNode().(*Node_File); ok {
+		return x.File
+	}
+	return nil
+}
+
+func (x *Node) GetSymlink() *SymlinkNode {
+	if x, ok := x.GetNode().(*Node_Symlink); ok {
+		return x.Symlink
+	}
+	return nil
+}
+
+type isNode_Node interface {
+	isNode_Node()
+}
+
+type Node_Directory struct {
+	Directory *DirectoryNode `protobuf:"bytes,1,opt,name=directory,proto3,oneof"`
+}
+
+type Node_File struct {
+	File *FileNode `protobuf:"bytes,2,opt,name=file,proto3,oneof"`
+}
+
+type Node_Symlink struct {
+	Symlink *SymlinkNode `protobuf:"bytes,3,opt,name=symlink,proto3,oneof"`
+}
+
+func (*Node_Directory) isNode_Node() {}
+
+func (*Node_File) isNode_Node() {}
+
+func (*Node_Symlink) isNode_Node() {}
+
+var File_tvix_castore_protos_castore_proto protoreflect.FileDescriptor
+
+var file_tvix_castore_protos_castore_proto_rawDesc = []byte{
+	0x0a, 0x21, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
+	0x65, 0x2e, 0x76, 0x31, 0x22, 0xb8, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f,
+	0x72, 0x79, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65,
+	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63,
+	0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74,
+	0x6f, 0x72, 0x79, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f,
+	0x72, 0x69, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f,
+	0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05,
+	0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b,
+	0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63,
+	0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e,
+	0x6b, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x22,
+	0x4f, 0x0a, 0x0d, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x4e, 0x6f, 0x64, 0x65,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
+	0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65,
+	0x22, 0x6a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
+	0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1e, 0x0a, 0x0a,
+	0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x39, 0x0a, 0x0b,
+	0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+	0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
+	0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xb9, 0x01, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65,
+	0x12, 0x3e, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f,
+	0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x4e,
+	0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79,
+	0x12, 0x2f, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
+	0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31,
+	0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x04, 0x66, 0x69, 0x6c,
+	0x65, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
+	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x64, 0x65,
+	0x48, 0x00, 0x52, 0x07, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x42, 0x06, 0x0a, 0x04, 0x6e,
+	0x6f, 0x64, 0x65, 0x42, 0x28, 0x5a, 0x26, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e,
+	0x66, 0x79, 0x69, 0x2f, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65,
+	0x2d, 0x67, 0x6f, 0x3b, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_tvix_castore_protos_castore_proto_rawDescOnce sync.Once
+	file_tvix_castore_protos_castore_proto_rawDescData = file_tvix_castore_protos_castore_proto_rawDesc
+)
+
+func file_tvix_castore_protos_castore_proto_rawDescGZIP() []byte {
+	file_tvix_castore_protos_castore_proto_rawDescOnce.Do(func() {
+		file_tvix_castore_protos_castore_proto_rawDescData = protoimpl.X.CompressGZIP(file_tvix_castore_protos_castore_proto_rawDescData)
+	})
+	return file_tvix_castore_protos_castore_proto_rawDescData
+}
+
+var file_tvix_castore_protos_castore_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_tvix_castore_protos_castore_proto_goTypes = []interface{}{
+	(*Directory)(nil),     // 0: tvix.castore.v1.Directory
+	(*DirectoryNode)(nil), // 1: tvix.castore.v1.DirectoryNode
+	(*FileNode)(nil),      // 2: tvix.castore.v1.FileNode
+	(*SymlinkNode)(nil),   // 3: tvix.castore.v1.SymlinkNode
+	(*Node)(nil),          // 4: tvix.castore.v1.Node
+}
+var file_tvix_castore_protos_castore_proto_depIdxs = []int32{
+	1, // 0: tvix.castore.v1.Directory.directories:type_name -> tvix.castore.v1.DirectoryNode
+	2, // 1: tvix.castore.v1.Directory.files:type_name -> tvix.castore.v1.FileNode
+	3, // 2: tvix.castore.v1.Directory.symlinks:type_name -> tvix.castore.v1.SymlinkNode
+	1, // 3: tvix.castore.v1.Node.directory:type_name -> tvix.castore.v1.DirectoryNode
+	2, // 4: tvix.castore.v1.Node.file:type_name -> tvix.castore.v1.FileNode
+	3, // 5: tvix.castore.v1.Node.symlink:type_name -> tvix.castore.v1.SymlinkNode
+	6, // [6:6] is the sub-list for method output_type
+	6, // [6:6] is the sub-list for method input_type
+	6, // [6:6] is the sub-list for extension type_name
+	6, // [6:6] is the sub-list for extension extendee
+	0, // [0:6] is the sub-list for field type_name
+}
+
+func init() { file_tvix_castore_protos_castore_proto_init() }
+func file_tvix_castore_protos_castore_proto_init() {
+	if File_tvix_castore_protos_castore_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_tvix_castore_protos_castore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Directory); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_castore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DirectoryNode); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_castore_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FileNode); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_castore_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SymlinkNode); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_castore_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Node); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_tvix_castore_protos_castore_proto_msgTypes[4].OneofWrappers = []interface{}{
+		(*Node_Directory)(nil),
+		(*Node_File)(nil),
+		(*Node_Symlink)(nil),
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_tvix_castore_protos_castore_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   5,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_tvix_castore_protos_castore_proto_goTypes,
+		DependencyIndexes: file_tvix_castore_protos_castore_proto_depIdxs,
+		MessageInfos:      file_tvix_castore_protos_castore_proto_msgTypes,
+	}.Build()
+	File_tvix_castore_protos_castore_proto = out.File
+	file_tvix_castore_protos_castore_proto_rawDesc = nil
+	file_tvix_castore_protos_castore_proto_goTypes = nil
+	file_tvix_castore_protos_castore_proto_depIdxs = nil
+}
diff --git a/tvix/castore-go/castore_test.go b/tvix/castore-go/castore_test.go
new file mode 100644
index 0000000000..c237442f4e
--- /dev/null
+++ b/tvix/castore-go/castore_test.go
@@ -0,0 +1,298 @@
+package castorev1_test
+
+import (
+	"testing"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	"github.com/stretchr/testify/assert"
+)
+
+var (
+	dummyDigest = []byte{
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00,
+	}
+)
+
+func TestDirectorySize(t *testing.T) {
+	t.Run("empty", func(t *testing.T) {
+		d := castorev1pb.Directory{
+			Directories: []*castorev1pb.DirectoryNode{},
+			Files:       []*castorev1pb.FileNode{},
+			Symlinks:    []*castorev1pb.SymlinkNode{},
+		}
+
+		assert.Equal(t, uint64(0), d.Size())
+	})
+
+	t.Run("containing single empty directory", func(t *testing.T) {
+		d := castorev1pb.Directory{
+			Directories: []*castorev1pb.DirectoryNode{{
+				Name:   []byte([]byte("foo")),
+				Digest: dummyDigest,
+				Size:   0,
+			}},
+			Files:    []*castorev1pb.FileNode{},
+			Symlinks: []*castorev1pb.SymlinkNode{},
+		}
+
+		assert.Equal(t, uint64(1), d.Size())
+	})
+
+	t.Run("containing single non-empty directory", func(t *testing.T) {
+		d := castorev1pb.Directory{
+			Directories: []*castorev1pb.DirectoryNode{{
+				Name:   []byte("foo"),
+				Digest: dummyDigest,
+				Size:   4,
+			}},
+			Files:    []*castorev1pb.FileNode{},
+			Symlinks: []*castorev1pb.SymlinkNode{},
+		}
+
+		assert.Equal(t, uint64(5), d.Size())
+	})
+
+	t.Run("containing single file", func(t *testing.T) {
+		d := castorev1pb.Directory{
+			Directories: []*castorev1pb.DirectoryNode{},
+			Files: []*castorev1pb.FileNode{{
+				Name:       []byte("foo"),
+				Digest:     dummyDigest,
+				Size:       42,
+				Executable: false,
+			}},
+			Symlinks: []*castorev1pb.SymlinkNode{},
+		}
+
+		assert.Equal(t, uint64(1), d.Size())
+	})
+
+	t.Run("containing single symlink", func(t *testing.T) {
+		d := castorev1pb.Directory{
+			Directories: []*castorev1pb.DirectoryNode{},
+			Files:       []*castorev1pb.FileNode{},
+			Symlinks: []*castorev1pb.SymlinkNode{{
+				Name:   []byte("foo"),
+				Target: []byte("bar"),
+			}},
+		}
+
+		assert.Equal(t, uint64(1), d.Size())
+	})
+
+}
+func TestDirectoryDigest(t *testing.T) {
+	d := castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{},
+		Files:       []*castorev1pb.FileNode{},
+		Symlinks:    []*castorev1pb.SymlinkNode{},
+	}
+
+	dgst, err := d.Digest()
+	assert.NoError(t, err, "calling Digest() on a directory shouldn't error")
+	assert.Equal(t, []byte{
+		0xaf, 0x13, 0x49, 0xb9, 0xf5, 0xf9, 0xa1, 0xa6, 0xa0, 0x40, 0x4d, 0xea, 0x36, 0xdc,
+		0xc9, 0x49, 0x9b, 0xcb, 0x25, 0xc9, 0xad, 0xc1, 0x12, 0xb7, 0xcc, 0x9a, 0x93, 0xca,
+		0xe4, 0x1f, 0x32, 0x62,
+	}, dgst)
+}
+
+func TestDirectoryValidate(t *testing.T) {
+	t.Run("empty", func(t *testing.T) {
+		d := castorev1pb.Directory{
+			Directories: []*castorev1pb.DirectoryNode{},
+			Files:       []*castorev1pb.FileNode{},
+			Symlinks:    []*castorev1pb.SymlinkNode{},
+		}
+
+		assert.NoError(t, d.Validate())
+	})
+
+	t.Run("invalid names", func(t *testing.T) {
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{{
+					Name:   []byte{},
+					Digest: dummyDigest,
+					Size:   42,
+				}},
+				Files:    []*castorev1pb.FileNode{},
+				Symlinks: []*castorev1pb.SymlinkNode{},
+			}
+
+			assert.ErrorContains(t, d.Validate(), "invalid node name")
+		}
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{{
+					Name:   []byte("."),
+					Digest: dummyDigest,
+					Size:   42,
+				}},
+				Files:    []*castorev1pb.FileNode{},
+				Symlinks: []*castorev1pb.SymlinkNode{},
+			}
+
+			assert.ErrorContains(t, d.Validate(), "invalid node name")
+		}
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{},
+				Files: []*castorev1pb.FileNode{{
+					Name:       []byte(".."),
+					Digest:     dummyDigest,
+					Size:       42,
+					Executable: false,
+				}},
+				Symlinks: []*castorev1pb.SymlinkNode{},
+			}
+
+			assert.ErrorContains(t, d.Validate(), "invalid node name")
+		}
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{},
+				Files:       []*castorev1pb.FileNode{},
+				Symlinks: []*castorev1pb.SymlinkNode{{
+					Name:   []byte("\x00"),
+					Target: []byte("foo"),
+				}},
+			}
+
+			assert.ErrorContains(t, d.Validate(), "invalid node name")
+		}
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{},
+				Files:       []*castorev1pb.FileNode{},
+				Symlinks: []*castorev1pb.SymlinkNode{{
+					Name:   []byte("foo/bar"),
+					Target: []byte("foo"),
+				}},
+			}
+
+			assert.ErrorContains(t, d.Validate(), "invalid node name")
+		}
+	})
+
+	t.Run("invalid digest", func(t *testing.T) {
+		d := castorev1pb.Directory{
+			Directories: []*castorev1pb.DirectoryNode{{
+				Name:   []byte("foo"),
+				Digest: nil,
+				Size:   42,
+			}},
+			Files:    []*castorev1pb.FileNode{},
+			Symlinks: []*castorev1pb.SymlinkNode{},
+		}
+
+		assert.ErrorContains(t, d.Validate(), "invalid digest length")
+	})
+
+	t.Run("invalid symlink targets", func(t *testing.T) {
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{},
+				Files:       []*castorev1pb.FileNode{},
+				Symlinks: []*castorev1pb.SymlinkNode{{
+					Name:   []byte("foo"),
+					Target: []byte{},
+				}},
+			}
+
+			assert.ErrorContains(t, d.Validate(), "invalid symlink target")
+		}
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{},
+				Files:       []*castorev1pb.FileNode{},
+				Symlinks: []*castorev1pb.SymlinkNode{{
+					Name:   []byte("foo"),
+					Target: []byte{0x66, 0x6f, 0x6f, 0},
+				}},
+			}
+
+			assert.ErrorContains(t, d.Validate(), "invalid symlink target")
+		}
+	})
+
+	t.Run("sorting", func(t *testing.T) {
+		// "b" comes before "a", bad.
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{{
+					Name:   []byte("b"),
+					Digest: dummyDigest,
+					Size:   42,
+				}, {
+					Name:   []byte("a"),
+					Digest: dummyDigest,
+					Size:   42,
+				}},
+				Files:    []*castorev1pb.FileNode{},
+				Symlinks: []*castorev1pb.SymlinkNode{},
+			}
+			assert.ErrorContains(t, d.Validate(), "is not in sorted order")
+		}
+
+		// "a" exists twice, bad.
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{{
+					Name:   []byte("a"),
+					Digest: dummyDigest,
+					Size:   42,
+				}},
+				Files: []*castorev1pb.FileNode{{
+					Name:       []byte("a"),
+					Digest:     dummyDigest,
+					Size:       42,
+					Executable: false,
+				}},
+				Symlinks: []*castorev1pb.SymlinkNode{},
+			}
+			assert.ErrorContains(t, d.Validate(), "duplicate name")
+		}
+
+		// "a" comes before "b", all good.
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{{
+					Name:   []byte("a"),
+					Digest: dummyDigest,
+					Size:   42,
+				}, {
+					Name:   []byte("b"),
+					Digest: dummyDigest,
+					Size:   42,
+				}},
+				Files:    []*castorev1pb.FileNode{},
+				Symlinks: []*castorev1pb.SymlinkNode{},
+			}
+			assert.NoError(t, d.Validate(), "shouldn't error")
+		}
+
+		// [b, c] and [a] are both properly sorted.
+		{
+			d := castorev1pb.Directory{
+				Directories: []*castorev1pb.DirectoryNode{{
+					Name:   []byte("b"),
+					Digest: dummyDigest,
+					Size:   42,
+				}, {
+					Name:   []byte("c"),
+					Digest: dummyDigest,
+					Size:   42,
+				}},
+				Files: []*castorev1pb.FileNode{},
+				Symlinks: []*castorev1pb.SymlinkNode{{
+					Name:   []byte("a"),
+					Target: []byte("foo"),
+				}},
+			}
+			assert.NoError(t, d.Validate(), "shouldn't error")
+		}
+	})
+}
diff --git a/tvix/castore-go/default.nix b/tvix/castore-go/default.nix
new file mode 100644
index 0000000000..6999e90ccd
--- /dev/null
+++ b/tvix/castore-go/default.nix
@@ -0,0 +1,31 @@
+{ depot, pkgs, ... }:
+
+let
+  regenerate = pkgs.writeShellScript "regenerate" ''
+    (cd $(git rev-parse --show-toplevel)/tvix/castore-go && rm *.pb.go && cp ${depot.tvix.castore.protos.go-bindings}/*.pb.go . && chmod +w *.pb.go)
+  '';
+in
+(pkgs.buildGoModule {
+  name = "castore-go";
+  src = depot.third_party.gitignoreSource ./.;
+  vendorHash = "sha256-ZNtSSW+oCxMsBtURSrea9/GyUHDagtGefM+Ii+VkgCA=";
+}).overrideAttrs (_: {
+  meta.ci.extraSteps = {
+    check = {
+      label = ":water_buffalo: ensure generated protobuf files match";
+      needsOutput = true;
+      command = pkgs.writeShellScript "pb-go-check" ''
+        ${regenerate}
+        if [[ -n "$(git status --porcelain -unormal)" ]]; then
+            echo "-----------------------------"
+            echo ".pb.go files need to be updated, mg run //tvix/castore-go/regenerate"
+            echo "-----------------------------"
+            git status -unormal
+            exit 1
+        fi
+      '';
+      alwaysRun = true;
+    };
+  };
+  passthru.regenerate = regenerate;
+})
diff --git a/tvix/castore-go/go.mod b/tvix/castore-go/go.mod
new file mode 100644
index 0000000000..94310dd2aa
--- /dev/null
+++ b/tvix/castore-go/go.mod
@@ -0,0 +1,22 @@
+module code.tvl.fyi/tvix/castore-go
+
+go 1.19
+
+require (
+	github.com/stretchr/testify v1.8.1
+	google.golang.org/grpc v1.51.0
+	google.golang.org/protobuf v1.31.0
+	lukechampine.com/blake3 v1.1.7
+)
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
+	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
+	golang.org/x/text v0.4.0 // indirect
+	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/tvix/castore-go/go.sum b/tvix/castore-go/go.sum
new file mode 100644
index 0000000000..535b8e32f0
--- /dev/null
+++ b/tvix/castore-go/go.sum
@@ -0,0 +1,99 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
+google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
+lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
diff --git a/tvix/castore-go/rename_node.go b/tvix/castore-go/rename_node.go
new file mode 100644
index 0000000000..80537b16d3
--- /dev/null
+++ b/tvix/castore-go/rename_node.go
@@ -0,0 +1,38 @@
+package castorev1
+
+// RenamedNode returns a node with a new name.
+func RenamedNode(node *Node, name string) *Node {
+	if directoryNode := node.GetDirectory(); directoryNode != nil {
+		return &Node{
+			Node: &Node_Directory{
+				Directory: &DirectoryNode{
+					Name:   []byte(name),
+					Digest: directoryNode.GetDigest(),
+					Size:   directoryNode.GetSize(),
+				},
+			},
+		}
+	} else if fileNode := node.GetFile(); fileNode != nil {
+		return &Node{
+			Node: &Node_File{
+				File: &FileNode{
+					Name:       []byte(name),
+					Digest:     fileNode.GetDigest(),
+					Size:       fileNode.GetSize(),
+					Executable: fileNode.GetExecutable(),
+				},
+			},
+		}
+	} else if symlinkNode := node.GetSymlink(); symlinkNode != nil {
+		return &Node{
+			Node: &Node_Symlink{
+				Symlink: &SymlinkNode{
+					Name:   []byte(name),
+					Target: symlinkNode.GetTarget(),
+				},
+			},
+		}
+	} else {
+		panic("unreachable")
+	}
+}
diff --git a/tvix/castore-go/rpc_blobstore.pb.go b/tvix/castore-go/rpc_blobstore.pb.go
new file mode 100644
index 0000000000..3607a65bbe
--- /dev/null
+++ b/tvix/castore-go/rpc_blobstore.pb.go
@@ -0,0 +1,538 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        (unknown)
+// source: tvix/castore/protos/rpc_blobstore.proto
+
+package castorev1
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type StatBlobRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The blake3 digest of the blob requested
+	Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
+	// Whether the server should reply with a list of more granular chunks.
+	SendChunks bool `protobuf:"varint,2,opt,name=send_chunks,json=sendChunks,proto3" json:"send_chunks,omitempty"`
+	// Whether the server should reply with a bao.
+	SendBao bool `protobuf:"varint,3,opt,name=send_bao,json=sendBao,proto3" json:"send_bao,omitempty"`
+}
+
+func (x *StatBlobRequest) Reset() {
+	*x = StatBlobRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *StatBlobRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*StatBlobRequest) ProtoMessage() {}
+
+func (x *StatBlobRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use StatBlobRequest.ProtoReflect.Descriptor instead.
+func (*StatBlobRequest) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *StatBlobRequest) GetDigest() []byte {
+	if x != nil {
+		return x.Digest
+	}
+	return nil
+}
+
+func (x *StatBlobRequest) GetSendChunks() bool {
+	if x != nil {
+		return x.SendChunks
+	}
+	return false
+}
+
+func (x *StatBlobRequest) GetSendBao() bool {
+	if x != nil {
+		return x.SendBao
+	}
+	return false
+}
+
+type StatBlobResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// If `send_chunks` was set to true, this MAY contain a list of more
+	// granular chunks, which then may be read individually via the `Read`
+	// method.
+	Chunks []*StatBlobResponse_ChunkMeta `protobuf:"bytes,2,rep,name=chunks,proto3" json:"chunks,omitempty"`
+	// If `send_bao` was set to true, this MAY contain a outboard bao.
+	// The exact format and message types here will still be fleshed out.
+	Bao []byte `protobuf:"bytes,3,opt,name=bao,proto3" json:"bao,omitempty"`
+}
+
+func (x *StatBlobResponse) Reset() {
+	*x = StatBlobResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *StatBlobResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*StatBlobResponse) ProtoMessage() {}
+
+func (x *StatBlobResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use StatBlobResponse.ProtoReflect.Descriptor instead.
+func (*StatBlobResponse) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *StatBlobResponse) GetChunks() []*StatBlobResponse_ChunkMeta {
+	if x != nil {
+		return x.Chunks
+	}
+	return nil
+}
+
+func (x *StatBlobResponse) GetBao() []byte {
+	if x != nil {
+		return x.Bao
+	}
+	return nil
+}
+
+type ReadBlobRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The blake3 digest of the blob or chunk requested
+	Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
+}
+
+func (x *ReadBlobRequest) Reset() {
+	*x = ReadBlobRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReadBlobRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReadBlobRequest) ProtoMessage() {}
+
+func (x *ReadBlobRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReadBlobRequest.ProtoReflect.Descriptor instead.
+func (*ReadBlobRequest) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *ReadBlobRequest) GetDigest() []byte {
+	if x != nil {
+		return x.Digest
+	}
+	return nil
+}
+
+// This represents some bytes of a blob.
+// Blobs are sent in smaller chunks to keep message sizes manageable.
+type BlobChunk struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
+}
+
+func (x *BlobChunk) Reset() {
+	*x = BlobChunk{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BlobChunk) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BlobChunk) ProtoMessage() {}
+
+func (x *BlobChunk) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BlobChunk.ProtoReflect.Descriptor instead.
+func (*BlobChunk) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *BlobChunk) GetData() []byte {
+	if x != nil {
+		return x.Data
+	}
+	return nil
+}
+
+type PutBlobResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The blake3 digest of the data that was sent.
+	Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
+}
+
+func (x *PutBlobResponse) Reset() {
+	*x = PutBlobResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PutBlobResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PutBlobResponse) ProtoMessage() {}
+
+func (x *PutBlobResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PutBlobResponse.ProtoReflect.Descriptor instead.
+func (*PutBlobResponse) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *PutBlobResponse) GetDigest() []byte {
+	if x != nil {
+		return x.Digest
+	}
+	return nil
+}
+
+type StatBlobResponse_ChunkMeta struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Digest of that specific chunk
+	Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
+	// Length of that chunk, in bytes.
+	Size uint64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
+}
+
+func (x *StatBlobResponse_ChunkMeta) Reset() {
+	*x = StatBlobResponse_ChunkMeta{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *StatBlobResponse_ChunkMeta) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*StatBlobResponse_ChunkMeta) ProtoMessage() {}
+
+func (x *StatBlobResponse_ChunkMeta) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use StatBlobResponse_ChunkMeta.ProtoReflect.Descriptor instead.
+func (*StatBlobResponse_ChunkMeta) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{1, 0}
+}
+
+func (x *StatBlobResponse_ChunkMeta) GetDigest() []byte {
+	if x != nil {
+		return x.Digest
+	}
+	return nil
+}
+
+func (x *StatBlobResponse_ChunkMeta) GetSize() uint64 {
+	if x != nil {
+		return x.Size
+	}
+	return 0
+}
+
+var File_tvix_castore_protos_rpc_blobstore_proto protoreflect.FileDescriptor
+
+var file_tvix_castore_protos_rpc_blobstore_proto_rawDesc = []byte{
+	0x0a, 0x27, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x74,
+	0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x74, 0x76, 0x69, 0x78, 0x2e,
+	0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x22, 0x65, 0x0a, 0x0f, 0x53, 0x74,
+	0x61, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a,
+	0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64,
+	0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x63, 0x68,
+	0x75, 0x6e, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64,
+	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x62,
+	0x61, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x65, 0x6e, 0x64, 0x42, 0x61,
+	0x6f, 0x22, 0xa2, 0x01, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73,
+	0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61,
+	0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x42, 0x6c, 0x6f,
+	0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d,
+	0x65, 0x74, 0x61, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x62,
+	0x61, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x62, 0x61, 0x6f, 0x1a, 0x37, 0x0a,
+	0x09, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69,
+	0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65,
+	0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x29, 0x0a, 0x0f, 0x52, 0x65, 0x61, 0x64, 0x42, 0x6c,
+	0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67,
+	0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73,
+	0x74, 0x22, 0x1f, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x12,
+	0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61,
+	0x74, 0x61, 0x22, 0x29, 0x0a, 0x0f, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x32, 0xe9, 0x01,
+	0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4b, 0x0a,
+	0x04, 0x53, 0x74, 0x61, 0x74, 0x12, 0x20, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73,
+	0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x42, 0x6c, 0x6f, 0x62,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63,
+	0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x42, 0x6c,
+	0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x52, 0x65,
+	0x61, 0x64, 0x12, 0x20, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
+	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74,
+	0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b,
+	0x30, 0x01, 0x12, 0x45, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x1a, 0x2e, 0x74, 0x76, 0x69, 0x78,
+	0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x62,
+	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x20, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73,
+	0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x42, 0x28, 0x5a, 0x26, 0x63, 0x6f, 0x64,
+	0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e, 0x66, 0x79, 0x69, 0x2f, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63,
+	0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2d, 0x67, 0x6f, 0x3b, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
+	0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_tvix_castore_protos_rpc_blobstore_proto_rawDescOnce sync.Once
+	file_tvix_castore_protos_rpc_blobstore_proto_rawDescData = file_tvix_castore_protos_rpc_blobstore_proto_rawDesc
+)
+
+func file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP() []byte {
+	file_tvix_castore_protos_rpc_blobstore_proto_rawDescOnce.Do(func() {
+		file_tvix_castore_protos_rpc_blobstore_proto_rawDescData = protoimpl.X.CompressGZIP(file_tvix_castore_protos_rpc_blobstore_proto_rawDescData)
+	})
+	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescData
+}
+
+var file_tvix_castore_protos_rpc_blobstore_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
+var file_tvix_castore_protos_rpc_blobstore_proto_goTypes = []interface{}{
+	(*StatBlobRequest)(nil),            // 0: tvix.castore.v1.StatBlobRequest
+	(*StatBlobResponse)(nil),           // 1: tvix.castore.v1.StatBlobResponse
+	(*ReadBlobRequest)(nil),            // 2: tvix.castore.v1.ReadBlobRequest
+	(*BlobChunk)(nil),                  // 3: tvix.castore.v1.BlobChunk
+	(*PutBlobResponse)(nil),            // 4: tvix.castore.v1.PutBlobResponse
+	(*StatBlobResponse_ChunkMeta)(nil), // 5: tvix.castore.v1.StatBlobResponse.ChunkMeta
+}
+var file_tvix_castore_protos_rpc_blobstore_proto_depIdxs = []int32{
+	5, // 0: tvix.castore.v1.StatBlobResponse.chunks:type_name -> tvix.castore.v1.StatBlobResponse.ChunkMeta
+	0, // 1: tvix.castore.v1.BlobService.Stat:input_type -> tvix.castore.v1.StatBlobRequest
+	2, // 2: tvix.castore.v1.BlobService.Read:input_type -> tvix.castore.v1.ReadBlobRequest
+	3, // 3: tvix.castore.v1.BlobService.Put:input_type -> tvix.castore.v1.BlobChunk
+	1, // 4: tvix.castore.v1.BlobService.Stat:output_type -> tvix.castore.v1.StatBlobResponse
+	3, // 5: tvix.castore.v1.BlobService.Read:output_type -> tvix.castore.v1.BlobChunk
+	4, // 6: tvix.castore.v1.BlobService.Put:output_type -> tvix.castore.v1.PutBlobResponse
+	4, // [4:7] is the sub-list for method output_type
+	1, // [1:4] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_tvix_castore_protos_rpc_blobstore_proto_init() }
+func file_tvix_castore_protos_rpc_blobstore_proto_init() {
+	if File_tvix_castore_protos_rpc_blobstore_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*StatBlobRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*StatBlobResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReadBlobRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BlobChunk); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PutBlobResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*StatBlobResponse_ChunkMeta); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_tvix_castore_protos_rpc_blobstore_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   6,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_tvix_castore_protos_rpc_blobstore_proto_goTypes,
+		DependencyIndexes: file_tvix_castore_protos_rpc_blobstore_proto_depIdxs,
+		MessageInfos:      file_tvix_castore_protos_rpc_blobstore_proto_msgTypes,
+	}.Build()
+	File_tvix_castore_protos_rpc_blobstore_proto = out.File
+	file_tvix_castore_protos_rpc_blobstore_proto_rawDesc = nil
+	file_tvix_castore_protos_rpc_blobstore_proto_goTypes = nil
+	file_tvix_castore_protos_rpc_blobstore_proto_depIdxs = nil
+}
diff --git a/tvix/castore-go/rpc_blobstore_grpc.pb.go b/tvix/castore-go/rpc_blobstore_grpc.pb.go
new file mode 100644
index 0000000000..d63a6cae96
--- /dev/null
+++ b/tvix/castore-go/rpc_blobstore_grpc.pb.go
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.3.0
+// - protoc             (unknown)
+// source: tvix/castore/protos/rpc_blobstore.proto
+
+package castorev1
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+const (
+	BlobService_Stat_FullMethodName = "/tvix.castore.v1.BlobService/Stat"
+	BlobService_Read_FullMethodName = "/tvix.castore.v1.BlobService/Read"
+	BlobService_Put_FullMethodName  = "/tvix.castore.v1.BlobService/Put"
+)
+
+// BlobServiceClient is the client API for BlobService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type BlobServiceClient interface {
+	// Stat can be used to check for the existence of a blob, as well as
+	// gathering more data about it, like more granular chunking information
+	// or baos.
+	// Server implementations are not required to provide more granular chunking
+	// information, especially if the digest specified in `StatBlobRequest` is
+	// already a chunk of a blob.
+	Stat(ctx context.Context, in *StatBlobRequest, opts ...grpc.CallOption) (*StatBlobResponse, error)
+	// Read allows reading (all) data of a blob/chunk by the BLAKE3 digest of
+	// its contents.
+	// If the backend communicated more granular chunks in the `Stat` request,
+	// this can also be used to read chunks.
+	// This request returns a stream of BlobChunk, which is just a container for
+	// a stream of bytes.
+	// The server may decide on whatever chunking it may seem fit as a size for
+	// the individual BlobChunk sent in the response stream, this is mostly to
+	// keep individual messages at a manageable size.
+	Read(ctx context.Context, in *ReadBlobRequest, opts ...grpc.CallOption) (BlobService_ReadClient, error)
+	// Put uploads a Blob, by reading a stream of bytes.
+	//
+	// The way the data is chunked up in individual BlobChunk messages sent in
+	// the stream has no effect on how the server ends up chunking blobs up, if
+	// it does at all.
+	Put(ctx context.Context, opts ...grpc.CallOption) (BlobService_PutClient, error)
+}
+
+type blobServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewBlobServiceClient(cc grpc.ClientConnInterface) BlobServiceClient {
+	return &blobServiceClient{cc}
+}
+
+func (c *blobServiceClient) Stat(ctx context.Context, in *StatBlobRequest, opts ...grpc.CallOption) (*StatBlobResponse, error) {
+	out := new(StatBlobResponse)
+	err := c.cc.Invoke(ctx, BlobService_Stat_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *blobServiceClient) Read(ctx context.Context, in *ReadBlobRequest, opts ...grpc.CallOption) (BlobService_ReadClient, error) {
+	stream, err := c.cc.NewStream(ctx, &BlobService_ServiceDesc.Streams[0], BlobService_Read_FullMethodName, opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &blobServiceReadClient{stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+type BlobService_ReadClient interface {
+	Recv() (*BlobChunk, error)
+	grpc.ClientStream
+}
+
+type blobServiceReadClient struct {
+	grpc.ClientStream
+}
+
+func (x *blobServiceReadClient) Recv() (*BlobChunk, error) {
+	m := new(BlobChunk)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+func (c *blobServiceClient) Put(ctx context.Context, opts ...grpc.CallOption) (BlobService_PutClient, error) {
+	stream, err := c.cc.NewStream(ctx, &BlobService_ServiceDesc.Streams[1], BlobService_Put_FullMethodName, opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &blobServicePutClient{stream}
+	return x, nil
+}
+
+type BlobService_PutClient interface {
+	Send(*BlobChunk) error
+	CloseAndRecv() (*PutBlobResponse, error)
+	grpc.ClientStream
+}
+
+type blobServicePutClient struct {
+	grpc.ClientStream
+}
+
+func (x *blobServicePutClient) Send(m *BlobChunk) error {
+	return x.ClientStream.SendMsg(m)
+}
+
+func (x *blobServicePutClient) CloseAndRecv() (*PutBlobResponse, error) {
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	m := new(PutBlobResponse)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// BlobServiceServer is the server API for BlobService service.
+// All implementations must embed UnimplementedBlobServiceServer
+// for forward compatibility
+type BlobServiceServer interface {
+	// Stat can be used to check for the existence of a blob, as well as
+	// gathering more data about it, like more granular chunking information
+	// or baos.
+	// Server implementations are not required to provide more granular chunking
+	// information, especially if the digest specified in `StatBlobRequest` is
+	// already a chunk of a blob.
+	Stat(context.Context, *StatBlobRequest) (*StatBlobResponse, error)
+	// Read allows reading (all) data of a blob/chunk by the BLAKE3 digest of
+	// its contents.
+	// If the backend communicated more granular chunks in the `Stat` request,
+	// this can also be used to read chunks.
+	// This request returns a stream of BlobChunk, which is just a container for
+	// a stream of bytes.
+	// The server may decide on whatever chunking it may seem fit as a size for
+	// the individual BlobChunk sent in the response stream, this is mostly to
+	// keep individual messages at a manageable size.
+	Read(*ReadBlobRequest, BlobService_ReadServer) error
+	// Put uploads a Blob, by reading a stream of bytes.
+	//
+	// The way the data is chunked up in individual BlobChunk messages sent in
+	// the stream has no effect on how the server ends up chunking blobs up, if
+	// it does at all.
+	Put(BlobService_PutServer) error
+	mustEmbedUnimplementedBlobServiceServer()
+}
+
+// UnimplementedBlobServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedBlobServiceServer struct {
+}
+
+func (UnimplementedBlobServiceServer) Stat(context.Context, *StatBlobRequest) (*StatBlobResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Stat not implemented")
+}
+func (UnimplementedBlobServiceServer) Read(*ReadBlobRequest, BlobService_ReadServer) error {
+	return status.Errorf(codes.Unimplemented, "method Read not implemented")
+}
+func (UnimplementedBlobServiceServer) Put(BlobService_PutServer) error {
+	return status.Errorf(codes.Unimplemented, "method Put not implemented")
+}
+func (UnimplementedBlobServiceServer) mustEmbedUnimplementedBlobServiceServer() {}
+
+// UnsafeBlobServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to BlobServiceServer will
+// result in compilation errors.
+type UnsafeBlobServiceServer interface {
+	mustEmbedUnimplementedBlobServiceServer()
+}
+
+func RegisterBlobServiceServer(s grpc.ServiceRegistrar, srv BlobServiceServer) {
+	s.RegisterService(&BlobService_ServiceDesc, srv)
+}
+
+func _BlobService_Stat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(StatBlobRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BlobServiceServer).Stat(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: BlobService_Stat_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BlobServiceServer).Stat(ctx, req.(*StatBlobRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _BlobService_Read_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(ReadBlobRequest)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(BlobServiceServer).Read(m, &blobServiceReadServer{stream})
+}
+
+type BlobService_ReadServer interface {
+	Send(*BlobChunk) error
+	grpc.ServerStream
+}
+
+type blobServiceReadServer struct {
+	grpc.ServerStream
+}
+
+func (x *blobServiceReadServer) Send(m *BlobChunk) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+func _BlobService_Put_Handler(srv interface{}, stream grpc.ServerStream) error {
+	return srv.(BlobServiceServer).Put(&blobServicePutServer{stream})
+}
+
+type BlobService_PutServer interface {
+	SendAndClose(*PutBlobResponse) error
+	Recv() (*BlobChunk, error)
+	grpc.ServerStream
+}
+
+type blobServicePutServer struct {
+	grpc.ServerStream
+}
+
+func (x *blobServicePutServer) SendAndClose(m *PutBlobResponse) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+func (x *blobServicePutServer) Recv() (*BlobChunk, error) {
+	m := new(BlobChunk)
+	if err := x.ServerStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// BlobService_ServiceDesc is the grpc.ServiceDesc for BlobService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var BlobService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "tvix.castore.v1.BlobService",
+	HandlerType: (*BlobServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Stat",
+			Handler:    _BlobService_Stat_Handler,
+		},
+	},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "Read",
+			Handler:       _BlobService_Read_Handler,
+			ServerStreams: true,
+		},
+		{
+			StreamName:    "Put",
+			Handler:       _BlobService_Put_Handler,
+			ClientStreams: true,
+		},
+	},
+	Metadata: "tvix/castore/protos/rpc_blobstore.proto",
+}
diff --git a/tvix/castore-go/rpc_directory.pb.go b/tvix/castore-go/rpc_directory.pb.go
new file mode 100644
index 0000000000..78c4a243e3
--- /dev/null
+++ b/tvix/castore-go/rpc_directory.pb.go
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        (unknown)
+// source: tvix/castore/protos/rpc_directory.proto
+
+package castorev1
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type GetDirectoryRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to ByWhat:
+	//
+	//	*GetDirectoryRequest_Digest
+	ByWhat isGetDirectoryRequest_ByWhat `protobuf_oneof:"by_what"`
+	// If set to true, recursively resolve all child Directory messages.
+	// Directory messages SHOULD be streamed in a recursive breadth-first walk,
+	// but other orders are also fine, as long as Directory messages are only
+	// sent after they are referred to from previously sent Directory messages.
+	Recursive bool `protobuf:"varint,2,opt,name=recursive,proto3" json:"recursive,omitempty"`
+}
+
+func (x *GetDirectoryRequest) Reset() {
+	*x = GetDirectoryRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_rpc_directory_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetDirectoryRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetDirectoryRequest) ProtoMessage() {}
+
+func (x *GetDirectoryRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_rpc_directory_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetDirectoryRequest.ProtoReflect.Descriptor instead.
+func (*GetDirectoryRequest) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_rpc_directory_proto_rawDescGZIP(), []int{0}
+}
+
+func (m *GetDirectoryRequest) GetByWhat() isGetDirectoryRequest_ByWhat {
+	if m != nil {
+		return m.ByWhat
+	}
+	return nil
+}
+
+func (x *GetDirectoryRequest) GetDigest() []byte {
+	if x, ok := x.GetByWhat().(*GetDirectoryRequest_Digest); ok {
+		return x.Digest
+	}
+	return nil
+}
+
+func (x *GetDirectoryRequest) GetRecursive() bool {
+	if x != nil {
+		return x.Recursive
+	}
+	return false
+}
+
+type isGetDirectoryRequest_ByWhat interface {
+	isGetDirectoryRequest_ByWhat()
+}
+
+type GetDirectoryRequest_Digest struct {
+	// The blake3 hash of the (root) Directory message, serialized in
+	// protobuf canonical form.
+	// Keep in mind this can be a subtree of another root.
+	Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3,oneof"`
+}
+
+func (*GetDirectoryRequest_Digest) isGetDirectoryRequest_ByWhat() {}
+
+type PutDirectoryResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	RootDigest []byte `protobuf:"bytes,1,opt,name=root_digest,json=rootDigest,proto3" json:"root_digest,omitempty"`
+}
+
+func (x *PutDirectoryResponse) Reset() {
+	*x = PutDirectoryResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_castore_protos_rpc_directory_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PutDirectoryResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PutDirectoryResponse) ProtoMessage() {}
+
+func (x *PutDirectoryResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_castore_protos_rpc_directory_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PutDirectoryResponse.ProtoReflect.Descriptor instead.
+func (*PutDirectoryResponse) Descriptor() ([]byte, []int) {
+	return file_tvix_castore_protos_rpc_directory_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *PutDirectoryResponse) GetRootDigest() []byte {
+	if x != nil {
+		return x.RootDigest
+	}
+	return nil
+}
+
+var File_tvix_castore_protos_rpc_directory_proto protoreflect.FileDescriptor
+
+var file_tvix_castore_protos_rpc_directory_proto_rawDesc = []byte{
+	0x0a, 0x27, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
+	0x6f, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x74, 0x76, 0x69, 0x78, 0x2e,
+	0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x21, 0x74, 0x76, 0x69, 0x78,
+	0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
+	0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x58, 0x0a,
+	0x13, 0x47, 0x65, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x1c,
+	0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x42, 0x09, 0x0a, 0x07,
+	0x62, 0x79, 0x5f, 0x77, 0x68, 0x61, 0x74, 0x22, 0x37, 0x0a, 0x14, 0x50, 0x75, 0x74, 0x44, 0x69,
+	0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+	0x1f, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74,
+	0x32, 0xa9, 0x01, 0x0a, 0x10, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65,
+	0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x24, 0x2e, 0x74,
+	0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47,
+	0x65, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
+	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x01,
+	0x12, 0x4a, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x1a, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63,
+	0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74,
+	0x6f, 0x72, 0x79, 0x1a, 0x25, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f,
+	0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f,
+	0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x42, 0x28, 0x5a, 0x26,
+	0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e, 0x66, 0x79, 0x69, 0x2f, 0x74, 0x76, 0x69,
+	0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2d, 0x67, 0x6f, 0x3b, 0x63, 0x61, 0x73,
+	0x74, 0x6f, 0x72, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_tvix_castore_protos_rpc_directory_proto_rawDescOnce sync.Once
+	file_tvix_castore_protos_rpc_directory_proto_rawDescData = file_tvix_castore_protos_rpc_directory_proto_rawDesc
+)
+
+func file_tvix_castore_protos_rpc_directory_proto_rawDescGZIP() []byte {
+	file_tvix_castore_protos_rpc_directory_proto_rawDescOnce.Do(func() {
+		file_tvix_castore_protos_rpc_directory_proto_rawDescData = protoimpl.X.CompressGZIP(file_tvix_castore_protos_rpc_directory_proto_rawDescData)
+	})
+	return file_tvix_castore_protos_rpc_directory_proto_rawDescData
+}
+
+var file_tvix_castore_protos_rpc_directory_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_tvix_castore_protos_rpc_directory_proto_goTypes = []interface{}{
+	(*GetDirectoryRequest)(nil),  // 0: tvix.castore.v1.GetDirectoryRequest
+	(*PutDirectoryResponse)(nil), // 1: tvix.castore.v1.PutDirectoryResponse
+	(*Directory)(nil),            // 2: tvix.castore.v1.Directory
+}
+var file_tvix_castore_protos_rpc_directory_proto_depIdxs = []int32{
+	0, // 0: tvix.castore.v1.DirectoryService.Get:input_type -> tvix.castore.v1.GetDirectoryRequest
+	2, // 1: tvix.castore.v1.DirectoryService.Put:input_type -> tvix.castore.v1.Directory
+	2, // 2: tvix.castore.v1.DirectoryService.Get:output_type -> tvix.castore.v1.Directory
+	1, // 3: tvix.castore.v1.DirectoryService.Put:output_type -> tvix.castore.v1.PutDirectoryResponse
+	2, // [2:4] is the sub-list for method output_type
+	0, // [0:2] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_tvix_castore_protos_rpc_directory_proto_init() }
+func file_tvix_castore_protos_rpc_directory_proto_init() {
+	if File_tvix_castore_protos_rpc_directory_proto != nil {
+		return
+	}
+	file_tvix_castore_protos_castore_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_tvix_castore_protos_rpc_directory_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetDirectoryRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_castore_protos_rpc_directory_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PutDirectoryResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_tvix_castore_protos_rpc_directory_proto_msgTypes[0].OneofWrappers = []interface{}{
+		(*GetDirectoryRequest_Digest)(nil),
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_tvix_castore_protos_rpc_directory_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_tvix_castore_protos_rpc_directory_proto_goTypes,
+		DependencyIndexes: file_tvix_castore_protos_rpc_directory_proto_depIdxs,
+		MessageInfos:      file_tvix_castore_protos_rpc_directory_proto_msgTypes,
+	}.Build()
+	File_tvix_castore_protos_rpc_directory_proto = out.File
+	file_tvix_castore_protos_rpc_directory_proto_rawDesc = nil
+	file_tvix_castore_protos_rpc_directory_proto_goTypes = nil
+	file_tvix_castore_protos_rpc_directory_proto_depIdxs = nil
+}
diff --git a/tvix/castore-go/rpc_directory_grpc.pb.go b/tvix/castore-go/rpc_directory_grpc.pb.go
new file mode 100644
index 0000000000..98789fef83
--- /dev/null
+++ b/tvix/castore-go/rpc_directory_grpc.pb.go
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.3.0
+// - protoc             (unknown)
+// source: tvix/castore/protos/rpc_directory.proto
+
+package castorev1
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+const (
+	DirectoryService_Get_FullMethodName = "/tvix.castore.v1.DirectoryService/Get"
+	DirectoryService_Put_FullMethodName = "/tvix.castore.v1.DirectoryService/Put"
+)
+
+// DirectoryServiceClient is the client API for DirectoryService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type DirectoryServiceClient interface {
+	// Get retrieves a stream of Directory messages, by using the lookup
+	// parameters in GetDirectoryRequest.
+	// Keep in mind multiple DirectoryNodes in different parts of the graph might
+	// have the same digest if they have the same underlying contents,
+	// so sending subsequent ones can be omitted.
+	//
+	// It is okay for certain implementations to only allow retrieval of
+	// Directory digests that are at the "root", aka the last element that's
+	// sent in a Put. This makes sense for implementations bundling closures of
+	// directories together in batches.
+	Get(ctx context.Context, in *GetDirectoryRequest, opts ...grpc.CallOption) (DirectoryService_GetClient, error)
+	// Put uploads a graph of Directory messages.
+	// Individual Directory messages need to be send in an order walking up
+	// from the leaves to the root - a Directory message can only refer to
+	// Directory messages previously sent in the same stream.
+	// Keep in mind multiple DirectoryNodes in different parts of the graph might
+	// have the same digest if they have the same underlying contents,
+	// so sending subsequent ones can be omitted.
+	// We might add a separate method, allowing to send partial graphs at a later
+	// time, if requiring to send the full graph turns out to be a problem.
+	Put(ctx context.Context, opts ...grpc.CallOption) (DirectoryService_PutClient, error)
+}
+
+type directoryServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewDirectoryServiceClient(cc grpc.ClientConnInterface) DirectoryServiceClient {
+	return &directoryServiceClient{cc}
+}
+
+func (c *directoryServiceClient) Get(ctx context.Context, in *GetDirectoryRequest, opts ...grpc.CallOption) (DirectoryService_GetClient, error) {
+	stream, err := c.cc.NewStream(ctx, &DirectoryService_ServiceDesc.Streams[0], DirectoryService_Get_FullMethodName, opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &directoryServiceGetClient{stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+type DirectoryService_GetClient interface {
+	Recv() (*Directory, error)
+	grpc.ClientStream
+}
+
+type directoryServiceGetClient struct {
+	grpc.ClientStream
+}
+
+func (x *directoryServiceGetClient) Recv() (*Directory, error) {
+	m := new(Directory)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+func (c *directoryServiceClient) Put(ctx context.Context, opts ...grpc.CallOption) (DirectoryService_PutClient, error) {
+	stream, err := c.cc.NewStream(ctx, &DirectoryService_ServiceDesc.Streams[1], DirectoryService_Put_FullMethodName, opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &directoryServicePutClient{stream}
+	return x, nil
+}
+
+type DirectoryService_PutClient interface {
+	Send(*Directory) error
+	CloseAndRecv() (*PutDirectoryResponse, error)
+	grpc.ClientStream
+}
+
+type directoryServicePutClient struct {
+	grpc.ClientStream
+}
+
+func (x *directoryServicePutClient) Send(m *Directory) error {
+	return x.ClientStream.SendMsg(m)
+}
+
+func (x *directoryServicePutClient) CloseAndRecv() (*PutDirectoryResponse, error) {
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	m := new(PutDirectoryResponse)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// DirectoryServiceServer is the server API for DirectoryService service.
+// All implementations must embed UnimplementedDirectoryServiceServer
+// for forward compatibility
+type DirectoryServiceServer interface {
+	// Get retrieves a stream of Directory messages, by using the lookup
+	// parameters in GetDirectoryRequest.
+	// Keep in mind multiple DirectoryNodes in different parts of the graph might
+	// have the same digest if they have the same underlying contents,
+	// so sending subsequent ones can be omitted.
+	//
+	// It is okay for certain implementations to only allow retrieval of
+	// Directory digests that are at the "root", aka the last element that's
+	// sent in a Put. This makes sense for implementations bundling closures of
+	// directories together in batches.
+	Get(*GetDirectoryRequest, DirectoryService_GetServer) error
+	// Put uploads a graph of Directory messages.
+	// Individual Directory messages need to be send in an order walking up
+	// from the leaves to the root - a Directory message can only refer to
+	// Directory messages previously sent in the same stream.
+	// Keep in mind multiple DirectoryNodes in different parts of the graph might
+	// have the same digest if they have the same underlying contents,
+	// so sending subsequent ones can be omitted.
+	// We might add a separate method, allowing to send partial graphs at a later
+	// time, if requiring to send the full graph turns out to be a problem.
+	Put(DirectoryService_PutServer) error
+	mustEmbedUnimplementedDirectoryServiceServer()
+}
+
+// UnimplementedDirectoryServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedDirectoryServiceServer struct {
+}
+
+func (UnimplementedDirectoryServiceServer) Get(*GetDirectoryRequest, DirectoryService_GetServer) error {
+	return status.Errorf(codes.Unimplemented, "method Get not implemented")
+}
+func (UnimplementedDirectoryServiceServer) Put(DirectoryService_PutServer) error {
+	return status.Errorf(codes.Unimplemented, "method Put not implemented")
+}
+func (UnimplementedDirectoryServiceServer) mustEmbedUnimplementedDirectoryServiceServer() {}
+
+// UnsafeDirectoryServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to DirectoryServiceServer will
+// result in compilation errors.
+type UnsafeDirectoryServiceServer interface {
+	mustEmbedUnimplementedDirectoryServiceServer()
+}
+
+func RegisterDirectoryServiceServer(s grpc.ServiceRegistrar, srv DirectoryServiceServer) {
+	s.RegisterService(&DirectoryService_ServiceDesc, srv)
+}
+
+func _DirectoryService_Get_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(GetDirectoryRequest)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(DirectoryServiceServer).Get(m, &directoryServiceGetServer{stream})
+}
+
+type DirectoryService_GetServer interface {
+	Send(*Directory) error
+	grpc.ServerStream
+}
+
+type directoryServiceGetServer struct {
+	grpc.ServerStream
+}
+
+func (x *directoryServiceGetServer) Send(m *Directory) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+func _DirectoryService_Put_Handler(srv interface{}, stream grpc.ServerStream) error {
+	return srv.(DirectoryServiceServer).Put(&directoryServicePutServer{stream})
+}
+
+type DirectoryService_PutServer interface {
+	SendAndClose(*PutDirectoryResponse) error
+	Recv() (*Directory, error)
+	grpc.ServerStream
+}
+
+type directoryServicePutServer struct {
+	grpc.ServerStream
+}
+
+func (x *directoryServicePutServer) SendAndClose(m *PutDirectoryResponse) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+func (x *directoryServicePutServer) Recv() (*Directory, error) {
+	m := new(Directory)
+	if err := x.ServerStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// DirectoryService_ServiceDesc is the grpc.ServiceDesc for DirectoryService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var DirectoryService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "tvix.castore.v1.DirectoryService",
+	HandlerType: (*DirectoryServiceServer)(nil),
+	Methods:     []grpc.MethodDesc{},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "Get",
+			Handler:       _DirectoryService_Get_Handler,
+			ServerStreams: true,
+		},
+		{
+			StreamName:    "Put",
+			Handler:       _DirectoryService_Put_Handler,
+			ClientStreams: true,
+		},
+	},
+	Metadata: "tvix/castore/protos/rpc_directory.proto",
+}
diff --git a/tvix/castore/Cargo.toml b/tvix/castore/Cargo.toml
new file mode 100644
index 0000000000..2797ef08f2
--- /dev/null
+++ b/tvix/castore/Cargo.toml
@@ -0,0 +1,118 @@
+[package]
+name = "tvix-castore"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+async-stream = "0.3.5"
+async-tempfile = "0.4.0"
+blake3 = { version = "1.3.1", features = ["rayon", "std", "traits-preview"] }
+bstr = "1.6.0"
+bytes = "1.4.0"
+data-encoding = "2.3.3"
+digest = "0.10.7"
+fastcdc = { version = "3.1.0", features = ["tokio"] }
+futures = "0.3.30"
+lazy_static = "1.4.0"
+object_store = { version = "0.9.1", features = ["http"] }
+parking_lot = "0.12.1"
+pin-project-lite = "0.2.13"
+prost = "0.12.1"
+sled = { version = "0.34.7" }
+thiserror = "1.0.38"
+tokio-stream = { version = "0.1.14", features = ["fs", "net"] }
+tokio-util = { version = "0.7.9", features = ["io", "io-util"] }
+tokio-tar = "0.3.1"
+tokio = { version = "1.32.0", features = ["fs", "macros", "net", "rt", "rt-multi-thread", "signal"] }
+tonic = "0.11.0"
+tower = "0.4.13"
+tracing = "0.1.37"
+url = "2.4.0"
+walkdir = "2.4.0"
+zstd = "0.13.0"
+serde = { version = "1.0.197", features = [ "derive" ] }
+serde_with = "3.7.0"
+serde_qs = "0.12.0"
+petgraph = "0.6.4"
+
+[dependencies.bigtable_rs]
+optional = true
+# https://github.com/liufuyang/bigtable_rs/pull/72
+git = "https://github.com/flokli/bigtable_rs"
+rev = "0af404741dfc40eb9fa99cf4d4140a09c5c20df7"
+
+[dependencies.fuse-backend-rs]
+optional = true
+version = "0.11.0"
+
+[dependencies.libc]
+optional = true
+version = "0.2.144"
+
+[dependencies.tonic-reflection]
+optional = true
+version = "0.11.0"
+
+[dependencies.vhost]
+optional = true
+version = "0.6"
+
+[dependencies.vhost-user-backend]
+optional = true
+version = "0.8"
+
+[dependencies.virtio-queue]
+optional = true
+version = "0.7"
+
+[dependencies.vm-memory]
+optional = true
+version = "0.10"
+
+[dependencies.vmm-sys-util]
+optional = true
+version = "0.11"
+
+[dependencies.virtio-bindings]
+optional = true
+version = "0.2.1"
+
+[build-dependencies]
+prost-build = "0.12.1"
+tonic-build = "0.11.0"
+
+[dev-dependencies]
+async-process = "2.1.0"
+rstest = "0.19.0"
+tempfile = "3.3.0"
+tokio-retry = "0.3.0"
+hex-literal = "0.4.1"
+rstest_reuse = "0.6.0"
+xattr = "1.3.1"
+
+[features]
+default = []
+cloud = [
+  "dep:bigtable_rs",
+  "object_store/aws",
+  "object_store/azure",
+  "object_store/gcp",
+]
+fs = ["dep:libc", "dep:fuse-backend-rs"]
+virtiofs = [
+  "fs",
+  "dep:vhost",
+  "dep:vhost-user-backend",
+  "dep:virtio-queue",
+  "dep:vm-memory",
+  "dep:vmm-sys-util",
+  "dep:virtio-bindings",
+  "fuse-backend-rs?/vhost-user-fs", # impl FsCacheReqHandler for SlaveFsCacheReq
+  "fuse-backend-rs?/virtiofs",
+]
+fuse = ["fs"]
+tonic-reflection = ["dep:tonic-reflection"]
+# Whether to run the integration tests.
+# Requires the following packages in $PATH:
+# cbtemulator, google-cloud-bigtable-tool
+integration = []
diff --git a/tvix/castore/build.rs b/tvix/castore/build.rs
new file mode 100644
index 0000000000..089c093e71
--- /dev/null
+++ b/tvix/castore/build.rs
@@ -0,0 +1,39 @@
+use std::io::Result;
+
+fn main() -> Result<()> {
+    #[allow(unused_mut)]
+    let mut builder = tonic_build::configure();
+
+    #[cfg(feature = "tonic-reflection")]
+    {
+        let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
+        let descriptor_path = out_dir.join("tvix.castore.v1.bin");
+
+        builder = builder.file_descriptor_set_path(descriptor_path);
+    };
+
+    // https://github.com/hyperium/tonic/issues/908
+    let mut config = prost_build::Config::new();
+    config.bytes(["."]);
+    config.type_attribute(".", "#[derive(Eq, Hash)]");
+
+    builder
+        .build_server(true)
+        .build_client(true)
+        .emit_rerun_if_changed(false)
+        .compile_with_config(
+            config,
+            &[
+                "tvix/castore/protos/castore.proto",
+                "tvix/castore/protos/rpc_blobstore.proto",
+                "tvix/castore/protos/rpc_directory.proto",
+            ],
+            // If we are in running `cargo build` manually, using `../..` works fine,
+            // but in case we run inside a nix build, we need to instead point PROTO_ROOT
+            // to a sparseTree containing that structure.
+            &[match std::env::var_os("PROTO_ROOT") {
+                Some(proto_root) => proto_root.to_str().unwrap().to_owned(),
+                None => "../..".to_string(),
+            }],
+        )
+}
diff --git a/tvix/castore/default.nix b/tvix/castore/default.nix
new file mode 100644
index 0000000000..641d883760
--- /dev/null
+++ b/tvix/castore/default.nix
@@ -0,0 +1,23 @@
+{ depot, pkgs, ... }:
+
+(depot.tvix.crates.workspaceMembers.tvix-castore.build.override {
+  runTests = true;
+  testPreRun = ''
+    export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt;
+  '';
+
+  # enable some optional features.
+  features = [ "default" "cloud" ];
+}).overrideAttrs (_: {
+  meta.ci.targets = [ "integration-tests" ];
+  passthru.integration-tests = depot.tvix.crates.workspaceMembers.tvix-castore.build.override {
+    runTests = true;
+    testPreRun = ''
+      export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt;
+      export PATH="$PATH:${pkgs.lib.makeBinPath [pkgs.cbtemulator pkgs.google-cloud-bigtable-tool]}"
+    '';
+
+    # enable some optional features.
+    features = [ "default" "cloud" "integration" ];
+  };
+})
diff --git a/tvix/castore/docs/blobstore-chunking.md b/tvix/castore/docs/blobstore-chunking.md
new file mode 100644
index 0000000000..49bbe69275
--- /dev/null
+++ b/tvix/castore/docs/blobstore-chunking.md
@@ -0,0 +1,147 @@
+# BlobStore: Chunking & Verified Streaming
+
+`tvix-castore`'s BlobStore is a content-addressed storage system, using [blake3]
+as hash function.
+
+Returned data is fetched by using the digest as lookup key, and can be verified
+to be correct by feeding the received data through the hash function and
+ensuring it matches the digest initially used for the lookup.
+
+This means, data can be downloaded by any untrusted third-party as well, as the
+received data is validated to match the digest it was originally requested with.
+
+However, for larger blobs of data, having to download the entire blob at once is
+wasteful, if we only care about a part of the blob. Think about mounting a
+seekable data structure, like loop-mounting an .iso file, or doing partial reads
+in a large Parquet file, a column-oriented data format.
+
+> We want to have the possibility to *seek* into a larger file.
+
+This however shouldn't compromise on data integrity properties - we should not
+need to trust a peer we're downloading from to be "honest" about the partial
+data we're reading. We should be able to verify smaller reads.
+
+Especially when substituting from an untrusted third-party, we want to be able
+to detect quickly if that third-party is sending us wrong data, and terminate
+the connection early.
+
+## Chunking
+In content-addressed systems, this problem has historically been solved by
+breaking larger blobs into smaller chunks, which can be fetched individually,
+and making a hash of *this listing* the blob digest/identifier.
+
+ - BitTorrent for example breaks files up into smaller chunks, and maintains
+   a list of sha1 digests for each of these chunks. Magnet links contain a
+   digest over this listing as an identifier. (See [bittorrent-v2][here for
+   more details]).
+   With the identifier, a client can fetch the entire list, and then recursively
+   "unpack the graph" of nodes, until it ends up with a list of individual small
+   chunks, which can be fetched individually.
+ - Similarly, IPFS with its IPLD model builds up a Merkle DAG, and uses the
+   digest of the root node as an identitier.
+
+These approaches solve the problem of being able to fetch smaller chunks in a
+trusted fashion. They can also do some deduplication, in case there's the same
+leaf nodes same leaf nodes in multiple places.
+
+However, they also have a big disadvantage. The chunking parameters, and the
+"topology" of the graph structure itself "bleed" into the root hash of the
+entire data structure itself.
+
+Depending on the chunking parameters used, there's different representations for
+the same data, causing less data sharing/reuse in the overall system, in terms of how
+many chunks need to be downloaded vs. are already available locally, as well as
+how compact data is stored on-disk.
+
+This can be workarounded by agreeing on only a single way of chunking, but it's
+not pretty and misses a lot of deduplication potential.
+
+### Chunking in Tvix' Blobstore
+tvix-castore's BlobStore uses a hybrid approach to eliminate some of the
+disadvantages, while still being content-addressed internally, with the
+highlighted benefits.
+
+It uses [blake3] as hash function, and the blake3 digest of **the raw data
+itself** as an identifier (rather than some application-specific Merkle DAG that
+also embeds some chunking information).
+
+BLAKE3 is a tree hash where all left nodes fully populated, contrary to
+conventional serial hash functions. To be able to validate the hash of a node,
+one only needs the hash of the (2) children [^1], if any.
+
+This means one only needs to the root digest to validate a constructions, and these
+constructions can be sent [separately][bao-spec].
+
+This relieves us from the need of having to encode more granular chunking into
+our data model / identifier upfront, but can make this a mostly a transport/
+storage concern.
+
+For the some more description on the (remote) protocol, check
+`./blobstore-protocol.md`.
+
+#### Logical vs. physical chunking
+
+Due to the properties of the BLAKE3 hash function, we have logical blocks of
+1KiB, but this doesn't necessarily imply we need to restrict ourselves to these
+chunk sizes w.r.t. what "physical chunks" are sent over the wire between peers,
+or are stored on-disk.
+
+The only thing we need to be able to read and verify an arbitrary byte range is
+having the covering range of aligned 1K blocks, and a construction from the root
+digest to the 1K block.
+
+Note the intermediate hash tree can be further trimmed, [omitting][bao-tree]
+lower parts of the tree while still providing verified streaming - at the cost
+of having to fetch larger covering ranges of aligned blocks.
+
+Let's pick an example. We identify each KiB by a number here for illustrational
+purposes.
+
+Assuming we omit the last two layers of the hash tree, we end up with logical
+4KiB leaf chunks (`bao_shift` of `2`).
+
+For a blob of 14 KiB total size, we could fetch logical blocks `[0..=3]`,
+`[4..=7]`, `[8..=11]` and `[12..=13]` in an authenticated fashion:
+
+`[ 0 1 2 3 ] [ 4 5 6 7 ] [ 8 9 10 11 ] [ 12 13 ]`
+
+Assuming the server now informs us about the following physical chunking:
+
+```
+[ 0 1 ] [ 2 3 4 5 ] [ 6 ] [ 7 8 ] [ 9 10 11 12 13 14 15 ]`
+```
+
+If our application now wants to arbitrarily read from 0 until 4 (inclusive):
+
+```
+[ 0 1 ] [ 2 3 4 5 ] [ 6 ] [ 7 8 ] [ 9 10 11 12 13 14 15 ]
+ |-------------|
+
+```
+
+…we need to fetch physical chunks `[ 0 1 ]`, `[ 2 3 4 5 ]` and `[ 6 ] [ 7 8 ]`.
+
+
+`[ 0 1 ]` and `[ 2 3 4 5 ]` are obvious, they contain the data we're
+interested in.
+
+We however also need to fetch the physical chunks `[ 6 ]` and `[ 7 8 ]`, so we
+can assemble `[ 4 5 6 7 ]` to verify both logical chunks:
+
+```
+[ 0 1 ] [ 2 3 4 5 ] [ 6 ] [ 7 8 ] [ 9 10 11 12 13 14 15 ]
+^       ^           ^     ^
+|----4KiB----|------4KiB-----|
+```
+
+Each physical chunk fetched can be validated to have the blake3 digest that was
+communicated upfront, and can be stored in a client-side cache/storage, so
+subsequent / other requests for the same data will be fast(er).
+
+---
+
+[^1]: and the surrounding context, aka position inside the whole blob, which is available while verifying the tree
+[bittorrent-v2]: https://blog.libtorrent.org/2020/09/bittorrent-v2/
+[blake3]: https://github.com/BLAKE3-team/BLAKE3
+[bao-spec]: https://github.com/oconnor663/bao/blob/master/docs/spec.md
+[bao-tree]: https://github.com/n0-computer/bao-tree
diff --git a/tvix/castore/docs/blobstore-protocol.md b/tvix/castore/docs/blobstore-protocol.md
new file mode 100644
index 0000000000..048cafc3d8
--- /dev/null
+++ b/tvix/castore/docs/blobstore-protocol.md
@@ -0,0 +1,104 @@
+# BlobStore: Protocol / Composition
+
+This documents describes the protocol that BlobStore uses to substitute blobs
+other ("remote") BlobStores.
+
+How to come up with the blake3 digest of the blob to fetch is left to another
+layer in the stack.
+
+To put this into the context of Tvix as a Nix alternative, a blob represents an
+individual file inside a StorePath.
+In the Tvix Data Model, this is accomplished by having a `FileNode` (either the
+`root_node` in a `PathInfo` message, or a individual file inside a `Directory`
+message) encode a BLAKE3 digest.
+
+However, the whole infrastructure can be applied for other usecases requiring
+exchange/storage or access into data of which the blake3 digest is known.
+
+## Protocol and Interfaces
+As an RPC protocol, BlobStore currently uses gRPC.
+
+On the Rust side of things, every blob service implements the
+[`BlobService`](../src/blobservice/mod.rs) async trait, which isn't
+gRPC-specific.
+
+This `BlobService` trait provides functionality to check for existence of Blobs,
+read from blobs, and write new blobs.
+It also provides a method to ask for more granular chunks if they are available.
+
+In addition to some in-memory, on-disk and (soon) object-storage-based
+implementations, we also have a `BlobService` implementation that talks to a
+gRPC server, as well as a gRPC server wrapper component, which provides a gRPC
+service for anything implementing the `BlobService` trait.
+
+This makes it very easy to talk to a remote `BlobService`, which does not even
+need to be written in the same language, as long it speaks the same gRPC
+protocol.
+
+It also puts very little requirements on someone implementing a new
+`BlobService`, and how its internal storage or chunking algorithm looks like.
+
+The gRPC protocol is documented in `../protos/rpc_blobstore.proto`.
+Contrary to the `BlobService` trait, it does not have any options for seeking/
+ranging, as it's more desirable to provide this through chunking (see also
+`./blobstore-chunking.md`).
+
+## Composition
+Different `BlobStore` are supposed to be "composed"/"layered" to express
+caching, multiple local and remote sources.
+
+The fronting interface can be the same, it'd just be multiple "tiers" that can
+respond to requests, depending on where the data resides. [^1]
+
+This makes it very simple for consumers, as they don't need to be aware of the
+entire substitutor config.
+
+The flexibility of this doesn't need to be exposed to the user in the default
+case; in most cases we should be fine with some form of on-disk storage and a
+bunch of substituters with different priorities.
+
+### gRPC Clients
+Clients are encouraged to always read blobs in a chunked fashion (asking for a
+list of chunks for a blob via `BlobService.Stat()`, then fetching chunks via
+`BlobService.Read()` as needed), instead of directly reading the entire blob via
+`BlobService.Read()`.
+
+In a composition setting, this provides opportunity for caching, and avoids
+downloading some chunks if they're already present locally (for example, because
+they were already downloaded by reading from a similar blob earlier).
+
+It also removes the need for seeking to be a part of the gRPC protocol
+alltogether, as chunks are supposed to be "reasonably small" [^2].
+
+There's some further optimization potential, a `BlobService.Stat()` request
+could tell the server it's happy with very small blobs just being inlined in
+an additional additional field in the response, which would allow clients to
+populate their local chunk store in a single roundtrip.
+
+## Verified Streaming
+As already described in `./docs/blobstore-chunking.md`, the physical chunk
+information sent in a `BlobService.Stat()` response is still sufficient to fetch
+in an authenticated fashion.
+
+The exact protocol and formats are still a bit in flux, but here's some notes:
+
+ - `BlobService.Stat()` request gets a `send_bao` field (bool), signalling a
+   [BAO][bao-spec] should be sent. Could also be `bao_shift` integer, signalling
+   how detailed (down to the leaf chunks) it should go.
+   The exact format (and request fields) still need to be defined, edef has some
+   ideas around omitting some intermediate hash nodes over the wire and
+   recomputing them, reducing size by another ~50% over [bao-tree].
+ - `BlobService.Stat()` response gets some bao-related fields (`bao_shift`
+   field, signalling the actual format/shift level the server replies with, the
+   actual bao, and maybe some format specifier).
+   It would be nice to also be compatible with the baos used by [iroh], so we
+   can provide an implementation using it too.
+
+---
+
+[^1]: We might want to have some backchannel, so it becomes possible to provide
+      feedback to the user that something is downloaded.
+[^2]: Something between 512K-4M, TBD.
+[bao-spec]: https://github.com/oconnor663/bao/blob/master/docs/spec.md
+[bao-tree]: https://github.com/n0-computer/bao-tree
+[iroh]: https://github.com/n0-computer/iroh
diff --git a/tvix/castore/docs/data-model.md b/tvix/castore/docs/data-model.md
new file mode 100644
index 0000000000..2df6761aae
--- /dev/null
+++ b/tvix/castore/docs/data-model.md
@@ -0,0 +1,50 @@
+# Data model
+
+This provides some more notes on the fields used in castore.proto.
+
+See `//tvix/store/docs/api.md` for the full context.
+
+## Directory message
+`Directory` messages use the blake3 hash of their canonical protobuf
+serialization as its identifier.
+
+A `Directory` message contains three lists, `directories`, `files` and
+`symlinks`, holding `DirectoryNode`, `FileNode` and `SymlinkNode` messages
+respectively. They describe all the direct child elements that are contained in
+a directory.
+
+All three message types have a `name` field, specifying the (base)name of the
+element (which MUST not contain slashes or null bytes, and MUST not be '.' or '..').
+For reproducibility reasons, the lists MUST be sorted by that name and also
+MUST be unique across all three lists.
+
+In addition to the `name` field, the various *Node messages have the following
+fields:
+
+## DirectoryNode
+A `DirectoryNode` message represents a child directory.
+
+It has a `digest` field, which points to the identifier of another `Directory`
+message, making a `Directory` a merkle tree (or strictly speaking, a graph, as
+two elements pointing to a child directory with the same contents would point
+to the same `Directory` message.
+
+There's also a `size` field, containing the (total) number of all child
+elements in the referenced `Directory`, which helps for inode calculation.
+
+## FileNode
+A `FileNode` message represents a child (regular) file.
+
+Its `digest` field contains the blake3 hash of the file contents. It can be
+looked up in the `BlobService`.
+
+The `size` field contains the size of the blob the `digest` field refers to.
+
+The `executable` field specifies whether the file should be marked as
+executable or not.
+
+## SymlinkNode
+A `SymlinkNode` message represents a child symlink.
+
+In addition to the `name` field, the only additional field is the `target`,
+which is a string containing the target of the symlink.
diff --git a/tvix/castore/docs/why-not-git-trees.md b/tvix/castore/docs/why-not-git-trees.md
new file mode 100644
index 0000000000..fd46252cf5
--- /dev/null
+++ b/tvix/castore/docs/why-not-git-trees.md
@@ -0,0 +1,57 @@
+## Why not git tree objects?
+
+We've been experimenting with (some variations of) the git tree and object
+format, and ultimately decided against using it as an internal format, and
+instead adapted the one documented in the other documents here.
+
+While the tvix-store API protocol shares some similarities with the format used
+in git for trees and objects, the git one has shown some significant
+disadvantages:
+
+### The binary encoding itself
+
+#### trees
+The git tree object format is a very binary, error-prone and
+"made-to-be-read-and-written-from-C" format.
+
+Tree objects are a combination of null-terminated strings, and fields of known
+length. References to other tree objects use the literal sha1 hash of another
+tree object in this encoding.
+Extensions of the format/changes are very hard to do right, because parsers are
+not aware they might be parsing something different.
+
+The tvix-store protocol uses a canonical protobuf serialization, and uses
+the [blake3][blake3] hash of that serialization to point to other `Directory`
+messages.
+It's both compact and with a wide range of libraries for encoders and decoders
+in many programming languages.
+The choice of protobuf makes it easy to add new fields, and make old clients
+aware of some unknown fields being detected [^adding-fields].
+
+#### blob
+On disk, git blob objects start with a "blob" prefix, then the size of the
+payload, and then the data itself. The hash of a blob is the literal sha1sum
+over all of this - which makes it something very git specific to request for.
+
+tvix-store simply uses the [blake3][blake3] hash of the literal contents
+when referring to a file/blob, which makes it very easy to ask other data
+sources for the same data, as no git-specific payload is included in the hash.
+This also plays very well together with things like [iroh][iroh-discussion],
+which plans to provide a way to substitute (large)blobs by their blake3 hash
+over the IPFS network.
+
+In addition to that, [blake3][blake3] makes it possible to do
+[verified streaming][bao], as already described in other parts of the
+documentation.
+
+The git tree object format uses sha1 both for references to other trees and
+hashes of blobs, which isn't really a hash function to fundamentally base
+everything on in 2023.
+The [migration to sha256][git-sha256] also has been dead for some years now,
+and it's unclear how a "blake3" version of this would even look like.
+
+[bao]: https://github.com/oconnor663/bao
+[blake3]: https://github.com/BLAKE3-team/BLAKE3
+[git-sha256]: https://git-scm.com/docs/hash-function-transition/
+[iroh-discussion]: https://github.com/n0-computer/iroh/discussions/707#discussioncomment-5070197
+[^adding-fields]: Obviously, adding new fields will change hashes, but it's something that's easy to detect.
\ No newline at end of file
diff --git a/tvix/castore/protos/LICENSE b/tvix/castore/protos/LICENSE
new file mode 100644
index 0000000000..2034ada6fd
--- /dev/null
+++ b/tvix/castore/protos/LICENSE
@@ -0,0 +1,21 @@
+Copyright Β© The Tvix Authors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+β€œSoftware”), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/tvix/castore/protos/castore.proto b/tvix/castore/protos/castore.proto
new file mode 100644
index 0000000000..1ef4044045
--- /dev/null
+++ b/tvix/castore/protos/castore.proto
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: edef <edef@unfathomable.blue>
+// SPDX-License-Identifier: OSL-3.0 OR MIT OR Apache-2.0
+
+syntax = "proto3";
+
+package tvix.castore.v1;
+
+option go_package = "code.tvl.fyi/tvix/castore-go;castorev1";
+
+// A Directory can contain Directory, File or Symlink nodes.
+// Each of these nodes have a name attribute, which is the basename in that
+// directory and node type specific attributes.
+// The name attribute:
+//  - MUST not contain slashes or null bytes
+//  - MUST not be '.' or '..'
+//  - MUST be unique across all three lists
+// Elements in each list need to be lexicographically ordered by the name
+// attribute.
+message Directory {
+  repeated DirectoryNode directories = 1;
+  repeated FileNode files = 2;
+  repeated SymlinkNode symlinks = 3;
+}
+
+// A DirectoryNode represents a directory in a Directory.
+message DirectoryNode {
+  // The (base)name of the directory
+  bytes name = 1;
+  // The blake3 hash of a Directory message, serialized in protobuf canonical form.
+  bytes digest = 2;
+  // Number of child elements in the Directory referred to by `digest`.
+  // Calculated by summing up the numbers of `directories`, `files` and
+  // `symlinks`, and for each directory, its size field. Used for inode number
+  // calculation.
+  // This field is precisely as verifiable as any other Merkle tree edge.
+  // Resolve `digest`, and you can compute it incrementally. Resolve the entire
+  // tree, and you can fully compute it from scratch.
+  // A credulous implementation won't reject an excessive size, but this is
+  // harmless: you'll have some ordinals without nodes. Undersizing is obvious
+  // and easy to reject: you won't have an ordinal for some nodes.
+  uint64 size = 3;
+}
+
+// A FileNode represents a regular or executable file in a Directory.
+message FileNode {
+  // The (base)name of the file
+  bytes name = 1;
+  // The blake3 digest of the file contents
+  bytes digest = 2;
+  // The file content size
+  uint64 size = 3;
+  // Whether the file is executable
+  bool executable = 4;
+}
+
+// A SymlinkNode represents a symbolic link in a Directory.
+message SymlinkNode {
+  // The (base)name of the symlink
+  bytes name = 1;
+  // The target of the symlink.
+  bytes target = 2;
+}
+
+// A Node is either a DirectoryNode, FileNode or SymlinkNode.
+message Node {
+  oneof node {
+    DirectoryNode directory = 1;
+    FileNode file = 2;
+    SymlinkNode symlink = 3;
+  }
+}
diff --git a/tvix/castore/protos/default.nix b/tvix/castore/protos/default.nix
new file mode 100644
index 0000000000..feef55690f
--- /dev/null
+++ b/tvix/castore/protos/default.nix
@@ -0,0 +1,54 @@
+{ depot, pkgs, ... }:
+let
+  protos = depot.nix.sparseTree {
+    name = "castore-protos";
+    root = depot.path.origSrc;
+    paths = [
+      ./castore.proto
+      ./rpc_blobstore.proto
+      ./rpc_directory.proto
+      ../../../buf.yaml
+      ../../../buf.gen.yaml
+    ];
+  };
+in
+depot.nix.readTree.drvTargets {
+  inherit protos;
+
+  # Lints and ensures formatting of the proto files.
+  check = pkgs.stdenv.mkDerivation {
+    name = "proto-check";
+    src = protos;
+
+    nativeBuildInputs = [
+      pkgs.buf
+    ];
+
+    buildPhase = ''
+      export HOME=$TMPDIR
+      buf lint
+      buf format -d --exit-code
+      touch $out
+    '';
+  };
+
+  # Produces the golang bindings.
+  go-bindings = pkgs.stdenv.mkDerivation {
+    name = "go-bindings";
+    src = protos;
+
+    nativeBuildInputs = [
+      pkgs.buf
+      pkgs.protoc-gen-go
+      pkgs.protoc-gen-go-grpc
+    ];
+
+    buildPhase = ''
+      export HOME=$TMPDIR
+      buf generate
+
+      mkdir -p $out
+      cp tvix/castore/protos/*.pb.go $out/
+    '';
+  };
+}
diff --git a/tvix/castore/protos/rpc_blobstore.proto b/tvix/castore/protos/rpc_blobstore.proto
new file mode 100644
index 0000000000..eebe39ace7
--- /dev/null
+++ b/tvix/castore/protos/rpc_blobstore.proto
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+syntax = "proto3";
+
+package tvix.castore.v1;
+
+option go_package = "code.tvl.fyi/tvix/castore-go;castorev1";
+
+// BlobService allows reading (or uploading) content-addressed blobs of data.
+// BLAKE3 is used as a hashing function for the data. Uploading a blob will
+// return the BLAKE3 digest of it, and that's the identifier used to Read/Stat
+// them too.
+service BlobService {
+  // Stat can be used to check for the existence of a blob, as well as
+  // gathering more data about it, like more granular chunking information
+  // or baos.
+  // Server implementations are not required to provide more granular chunking
+  // information, especially if the digest specified in `StatBlobRequest` is
+  // already a chunk of a blob.
+  rpc Stat(StatBlobRequest) returns (StatBlobResponse);
+
+  // Read allows reading (all) data of a blob/chunk by the BLAKE3 digest of
+  // its contents.
+  // If the backend communicated more granular chunks in the `Stat` request,
+  // this can also be used to read chunks.
+  // This request returns a stream of BlobChunk, which is just a container for
+  // a stream of bytes.
+  // The server may decide on whatever chunking it may seem fit as a size for
+  // the individual BlobChunk sent in the response stream, this is mostly to
+  // keep individual messages at a manageable size.
+  rpc Read(ReadBlobRequest) returns (stream BlobChunk);
+
+  // Put uploads a Blob, by reading a stream of bytes.
+  //
+  // The way the data is chunked up in individual BlobChunk messages sent in
+  // the stream has no effect on how the server ends up chunking blobs up, if
+  // it does at all.
+  rpc Put(stream BlobChunk) returns (PutBlobResponse);
+}
+
+message StatBlobRequest {
+  // The blake3 digest of the blob requested
+  bytes digest = 1;
+
+  // Whether the server should reply with a list of more granular chunks.
+  bool send_chunks = 2;
+
+  // Whether the server should reply with a bao.
+  bool send_bao = 3;
+}
+
+message StatBlobResponse {
+  // If `send_chunks` was set to true, this MAY contain a list of more
+  // granular chunks, which then may be read individually via the `Read`
+  // method.
+  repeated ChunkMeta chunks = 2;
+
+  message ChunkMeta {
+    // Digest of that specific chunk
+    bytes digest = 1;
+
+    // Length of that chunk, in bytes.
+    uint64 size = 2;
+  }
+
+  // If `send_bao` was set to true, this MAY contain a outboard bao.
+  // The exact format and message types here will still be fleshed out.
+  bytes bao = 3;
+}
+
+message ReadBlobRequest {
+  // The blake3 digest of the blob or chunk requested
+  bytes digest = 1;
+}
+
+// This represents some bytes of a blob.
+// Blobs are sent in smaller chunks to keep message sizes manageable.
+message BlobChunk {
+  bytes data = 1;
+}
+
+message PutBlobResponse {
+  // The blake3 digest of the data that was sent.
+  bytes digest = 1;
+}
diff --git a/tvix/castore/protos/rpc_directory.proto b/tvix/castore/protos/rpc_directory.proto
new file mode 100644
index 0000000000..f4f41c433a
--- /dev/null
+++ b/tvix/castore/protos/rpc_directory.proto
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+syntax = "proto3";
+
+package tvix.castore.v1;
+
+import "tvix/castore/protos/castore.proto";
+
+option go_package = "code.tvl.fyi/tvix/castore-go;castorev1";
+
+service DirectoryService {
+  // Get retrieves a stream of Directory messages, by using the lookup
+  // parameters in GetDirectoryRequest.
+  // Keep in mind multiple DirectoryNodes in different parts of the graph might
+  // have the same digest if they have the same underlying contents,
+  // so sending subsequent ones can be omitted.
+  //
+  // It is okay for certain implementations to only allow retrieval of
+  // Directory digests that are at the "root", aka the last element that's
+  // sent in a Put. This makes sense for implementations bundling closures of
+  // directories together in batches.
+  rpc Get(GetDirectoryRequest) returns (stream Directory);
+
+  // Put uploads a graph of Directory messages.
+  // Individual Directory messages need to be send in an order walking up
+  // from the leaves to the root - a Directory message can only refer to
+  // Directory messages previously sent in the same stream.
+  // Keep in mind multiple DirectoryNodes in different parts of the graph might
+  // have the same digest if they have the same underlying contents,
+  // so sending subsequent ones can be omitted.
+  // We might add a separate method, allowing to send partial graphs at a later
+  // time, if requiring to send the full graph turns out to be a problem.
+  rpc Put(stream Directory) returns (PutDirectoryResponse);
+}
+
+message GetDirectoryRequest {
+  oneof by_what {
+    // The blake3 hash of the (root) Directory message, serialized in
+    // protobuf canonical form.
+    // Keep in mind this can be a subtree of another root.
+    bytes digest = 1;
+  }
+
+  // If set to true, recursively resolve all child Directory messages.
+  // Directory messages SHOULD be streamed in a recursive breadth-first walk,
+  // but other orders are also fine, as long as Directory messages are only
+  // sent after they are referred to from previously sent Directory messages.
+  bool recursive = 2;
+}
+
+message PutDirectoryResponse {
+  bytes root_digest = 1;
+}
diff --git a/tvix/castore/src/blobservice/chunked_reader.rs b/tvix/castore/src/blobservice/chunked_reader.rs
new file mode 100644
index 0000000000..6e8355874b
--- /dev/null
+++ b/tvix/castore/src/blobservice/chunked_reader.rs
@@ -0,0 +1,496 @@
+use futures::{ready, TryStreamExt};
+use pin_project_lite::pin_project;
+use tokio::io::{AsyncRead, AsyncSeekExt};
+use tokio_stream::StreamExt;
+use tokio_util::io::{ReaderStream, StreamReader};
+use tracing::{instrument, trace, warn};
+
+use crate::B3Digest;
+use std::{cmp::Ordering, pin::Pin};
+
+use super::{BlobReader, BlobService};
+
+pin_project! {
+    /// ChunkedReader provides a chunk-aware [BlobReader], so allows reading and
+    /// seeking into a blob.
+    /// It internally holds a [ChunkedBlob], which is storing chunk information
+    /// able to emit a reader seeked to a specific position whenever we need to seek.
+    pub struct ChunkedReader<BS> {
+        chunked_blob: ChunkedBlob<BS>,
+
+        #[pin]
+        r: Box<dyn AsyncRead + Unpin + Send>,
+
+        pos: u64,
+    }
+}
+
+impl<BS> ChunkedReader<BS>
+where
+    BS: AsRef<dyn BlobService> + Clone + 'static + Send,
+{
+    /// Construct a new [ChunkedReader], by retrieving a list of chunks (their
+    /// blake3 digests and chunk sizes)
+    pub fn from_chunks(chunks_it: impl Iterator<Item = (B3Digest, u64)>, blob_service: BS) -> Self {
+        let chunked_blob = ChunkedBlob::from_iter(chunks_it, blob_service);
+        let r = chunked_blob.reader_skipped_offset(0);
+
+        Self {
+            chunked_blob,
+            r,
+            pos: 0,
+        }
+    }
+}
+
+/// ChunkedReader implements BlobReader.
+impl<BS> BlobReader for ChunkedReader<BS> where BS: Send + Clone + 'static + AsRef<dyn BlobService> {}
+
+impl<BS> tokio::io::AsyncRead for ChunkedReader<BS>
+where
+    BS: AsRef<dyn BlobService> + Clone + 'static,
+{
+    fn poll_read(
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+        buf: &mut tokio::io::ReadBuf<'_>,
+    ) -> std::task::Poll<std::io::Result<()>> {
+        // The amount of data read can be determined by the increase
+        // in the length of the slice returned by `ReadBuf::filled`.
+        let filled_before = buf.filled().len();
+
+        let this = self.project();
+
+        ready!(this.r.poll_read(cx, buf))?;
+        let bytes_read = buf.filled().len() - filled_before;
+        *this.pos += bytes_read as u64;
+
+        Ok(()).into()
+    }
+}
+
+impl<BS> tokio::io::AsyncSeek for ChunkedReader<BS>
+where
+    BS: AsRef<dyn BlobService> + Clone + Send + 'static,
+{
+    #[instrument(skip(self), err(Debug))]
+    fn start_seek(self: Pin<&mut Self>, position: std::io::SeekFrom) -> std::io::Result<()> {
+        let total_len = self.chunked_blob.blob_length();
+        let mut this = self.project();
+
+        let absolute_offset: u64 = match position {
+            std::io::SeekFrom::Start(from_start) => from_start,
+            std::io::SeekFrom::End(from_end) => {
+                // note from_end is i64, not u64, so this is usually negative.
+                total_len.checked_add_signed(from_end).ok_or_else(|| {
+                    std::io::Error::new(
+                        std::io::ErrorKind::InvalidInput,
+                        "over/underflow while seeking",
+                    )
+                })?
+            }
+            std::io::SeekFrom::Current(from_current) => {
+                // note from_end is i64, not u64, so this can be positive or negative.
+                (*this.pos)
+                    .checked_add_signed(from_current)
+                    .ok_or_else(|| {
+                        std::io::Error::new(
+                            std::io::ErrorKind::InvalidInput,
+                            "over/underflow while seeking",
+                        )
+                    })?
+            }
+        };
+
+        // check if the position actually did change.
+        if absolute_offset != *this.pos {
+            // ensure the new position still is inside the file.
+            if absolute_offset > total_len {
+                Err(std::io::Error::new(
+                    std::io::ErrorKind::InvalidInput,
+                    "seeked beyond EOF",
+                ))?
+            }
+
+            // Update the position and the internal reader.
+            *this.pos = absolute_offset;
+
+            // FUTUREWORK: if we can seek forward, avoid re-assembling.
+            // At least if it's still in the same chunk?
+            *this.r = this.chunked_blob.reader_skipped_offset(absolute_offset);
+        }
+
+        Ok(())
+    }
+
+    fn poll_complete(
+        self: Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<std::io::Result<u64>> {
+        std::task::Poll::Ready(Ok(self.pos))
+    }
+}
+
+/// Holds a list of blake3 digest for individual chunks (and their sizes).
+/// Is able to construct a Reader that seeked to a certain offset, which
+/// is useful to construct a BlobReader (that implements AsyncSeek).
+/// - the current chunk index, and a Custor<Vec<u8>> holding the data of that chunk.
+struct ChunkedBlob<BS> {
+    blob_service: BS,
+    chunks: Vec<(u64, u64, B3Digest)>,
+}
+
+impl<BS> ChunkedBlob<BS>
+where
+    BS: AsRef<dyn BlobService> + Clone + 'static + Send,
+{
+    /// Constructs [Self] from a list of blake3 digests of chunks and their
+    /// sizes, and a reference to a blob service.
+    /// Initializing it with an empty list is disallowed.
+    fn from_iter(chunks_it: impl Iterator<Item = (B3Digest, u64)>, blob_service: BS) -> Self {
+        let mut chunks = Vec::new();
+        let mut offset: u64 = 0;
+
+        for (chunk_digest, chunk_size) in chunks_it {
+            chunks.push((offset, chunk_size, chunk_digest));
+            offset += chunk_size;
+        }
+
+        assert!(
+            !chunks.is_empty(),
+            "Chunks must be provided, don't use this for blobs without chunks"
+        );
+
+        Self {
+            blob_service,
+            chunks,
+        }
+    }
+
+    /// Returns the length of the blob.
+    fn blob_length(&self) -> u64 {
+        self.chunks
+            .last()
+            .map(|(chunk_offset, chunk_size, _)| chunk_offset + chunk_size)
+            .unwrap_or(0)
+    }
+
+    /// For a given position pos, return the chunk containing the data.
+    /// In case this would range outside the blob, None is returned.
+    #[instrument(level = "trace", skip(self), ret)]
+    fn get_chunk_idx_for_position(&self, pos: u64) -> Option<usize> {
+        // FUTUREWORK: benchmark when to use linear search, binary_search and BTreeSet
+        self.chunks
+            .binary_search_by(|(chunk_start_pos, chunk_size, _)| {
+                if chunk_start_pos + chunk_size <= pos {
+                    Ordering::Less
+                } else if *chunk_start_pos > pos {
+                    Ordering::Greater
+                } else {
+                    Ordering::Equal
+                }
+            })
+            .ok()
+    }
+
+    /// Returns a stream of bytes of the data in that blob.
+    /// It internally assembles a stream reading from each chunk (skipping over
+    /// chunks containing irrelevant data).
+    /// From the first relevant chunk, the irrelevant bytes are skipped too.
+    /// The returned boxed thing does not implement AsyncSeek on its own, but
+    /// ChunkedReader does.
+    #[instrument(level = "trace", skip(self))]
+    fn reader_skipped_offset(&self, offset: u64) -> Box<dyn tokio::io::AsyncRead + Send + Unpin> {
+        if offset == self.blob_length() {
+            return Box::new(std::io::Cursor::new(vec![]));
+        }
+        // construct a stream of all chunks starting with the given offset
+        let start_chunk_idx = self
+            .get_chunk_idx_for_position(offset)
+            .expect("outside of blob");
+        // It's ok to panic here, we can only reach this by seeking, and seeking should already reject out-of-file seeking.
+
+        let skip_first_chunk_bytes = (offset - self.chunks[start_chunk_idx].0) as usize;
+
+        let blob_service = self.blob_service.clone();
+        let chunks: Vec<_> = self.chunks[start_chunk_idx..].to_vec();
+        let readers_stream = tokio_stream::iter(chunks.into_iter().enumerate()).map(
+            move |(nth_chunk, (_chunk_start_offset, chunk_size, chunk_digest))| {
+                let chunk_digest = chunk_digest.to_owned();
+                let blob_service = blob_service.clone();
+                async move {
+                    trace!(chunk_size=%chunk_size, chunk_digest=%chunk_digest, "open_read on chunk in stream");
+                    let mut blob_reader = blob_service
+                        .as_ref()
+                        .open_read(&chunk_digest.to_owned())
+                        .await?
+                        .ok_or_else(|| {
+                            warn!(chunk.digest = %chunk_digest, "chunk not found");
+                            std::io::Error::new(std::io::ErrorKind::NotFound, "chunk not found")
+                        })?;
+
+                    // iff this is the first chunk in the stream, skip by skip_first_chunk_bytes
+                    if nth_chunk == 0 && skip_first_chunk_bytes > 0 {
+                        blob_reader
+                            .seek(std::io::SeekFrom::Start(skip_first_chunk_bytes as u64))
+                            .await?;
+                    }
+                    Ok::<_, std::io::Error>(blob_reader)
+                }
+            },
+        );
+
+        // convert the stream of readers to a stream of streams of byte chunks
+        let bytes_streams = readers_stream.then(|elem| async { elem.await.map(ReaderStream::new) });
+
+        // flatten into one stream of byte chunks
+        let bytes_stream = bytes_streams.try_flatten();
+
+        // convert into AsyncRead
+        Box::new(StreamReader::new(Box::pin(bytes_stream)))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use std::{io::SeekFrom, sync::Arc};
+
+    use crate::{
+        blobservice::{chunked_reader::ChunkedReader, BlobService, MemoryBlobService},
+        B3Digest,
+    };
+    use hex_literal::hex;
+    use lazy_static::lazy_static;
+    use tokio::io::{AsyncReadExt, AsyncSeekExt};
+
+    const CHUNK_1: [u8; 2] = hex!("0001");
+    const CHUNK_2: [u8; 4] = hex!("02030405");
+    const CHUNK_3: [u8; 1] = hex!("06");
+    const CHUNK_4: [u8; 2] = hex!("0708");
+    const CHUNK_5: [u8; 7] = hex!("090a0b0c0d0e0f");
+
+    lazy_static! {
+        // `[ 0 1 ] [ 2 3 4 5 ] [ 6 ] [ 7 8 ] [ 9 10 11 12 13 14 15 ]`
+        pub static ref CHUNK_1_DIGEST: B3Digest = blake3::hash(&CHUNK_1).as_bytes().into();
+        pub static ref CHUNK_2_DIGEST: B3Digest = blake3::hash(&CHUNK_2).as_bytes().into();
+        pub static ref CHUNK_3_DIGEST: B3Digest = blake3::hash(&CHUNK_3).as_bytes().into();
+        pub static ref CHUNK_4_DIGEST: B3Digest = blake3::hash(&CHUNK_4).as_bytes().into();
+        pub static ref CHUNK_5_DIGEST: B3Digest = blake3::hash(&CHUNK_5).as_bytes().into();
+        pub static ref BLOB_1_LIST: [(B3Digest, u64); 5] = [
+            (CHUNK_1_DIGEST.clone(), 2),
+            (CHUNK_2_DIGEST.clone(), 4),
+            (CHUNK_3_DIGEST.clone(), 1),
+            (CHUNK_4_DIGEST.clone(), 2),
+            (CHUNK_5_DIGEST.clone(), 7),
+        ];
+    }
+
+    use super::ChunkedBlob;
+
+    /// ensure the start offsets are properly calculated.
+    #[test]
+    fn from_iter() {
+        let cb = ChunkedBlob::from_iter(
+            BLOB_1_LIST.clone().into_iter(),
+            Arc::new(MemoryBlobService::default()) as Arc<dyn BlobService>,
+        );
+
+        assert_eq!(
+            cb.chunks,
+            Vec::from_iter([
+                (0, 2, CHUNK_1_DIGEST.clone()),
+                (2, 4, CHUNK_2_DIGEST.clone()),
+                (6, 1, CHUNK_3_DIGEST.clone()),
+                (7, 2, CHUNK_4_DIGEST.clone()),
+                (9, 7, CHUNK_5_DIGEST.clone()),
+            ])
+        );
+    }
+
+    /// ensure ChunkedBlob can't be used with an empty list of chunks
+    #[test]
+    #[should_panic]
+    fn from_iter_empty() {
+        ChunkedBlob::from_iter(
+            [].into_iter(),
+            Arc::new(MemoryBlobService::default()) as Arc<dyn BlobService>,
+        );
+    }
+
+    /// ensure the right chunk is selected
+    #[test]
+    fn chunk_idx_for_position() {
+        let cb = ChunkedBlob::from_iter(
+            BLOB_1_LIST.clone().into_iter(),
+            Arc::new(MemoryBlobService::default()) as Arc<dyn BlobService>,
+        );
+
+        assert_eq!(Some(0), cb.get_chunk_idx_for_position(0), "start of blob");
+
+        assert_eq!(
+            Some(0),
+            cb.get_chunk_idx_for_position(1),
+            "middle of first chunk"
+        );
+        assert_eq!(
+            Some(1),
+            cb.get_chunk_idx_for_position(2),
+            "beginning of second chunk"
+        );
+
+        assert_eq!(
+            Some(4),
+            cb.get_chunk_idx_for_position(15),
+            "right before the end of the blob"
+        );
+        assert_eq!(
+            None,
+            cb.get_chunk_idx_for_position(16),
+            "right outside the blob"
+        );
+        assert_eq!(
+            None,
+            cb.get_chunk_idx_for_position(100),
+            "way outside the blob"
+        );
+    }
+
+    /// returns a blobservice with all chunks in BLOB_1 present.
+    async fn gen_blobservice_blob1() -> Arc<dyn BlobService> {
+        let blob_service = Arc::new(MemoryBlobService::default()) as Arc<dyn BlobService>;
+
+        // seed blob service with all chunks
+        for blob_contents in [
+            CHUNK_1.to_vec(),
+            CHUNK_2.to_vec(),
+            CHUNK_3.to_vec(),
+            CHUNK_4.to_vec(),
+            CHUNK_5.to_vec(),
+        ] {
+            let mut bw = blob_service.open_write().await;
+            tokio::io::copy(&mut std::io::Cursor::new(blob_contents), &mut bw)
+                .await
+                .expect("writing blob");
+            bw.close().await.expect("close blobwriter");
+        }
+
+        blob_service
+    }
+
+    #[tokio::test]
+    async fn test_read() {
+        let blob_service = gen_blobservice_blob1().await;
+        let mut chunked_reader =
+            ChunkedReader::from_chunks(BLOB_1_LIST.clone().into_iter(), blob_service);
+
+        // read all data
+        let mut buf = Vec::new();
+        tokio::io::copy(&mut chunked_reader, &mut buf)
+            .await
+            .expect("copy");
+
+        assert_eq!(
+            hex!("000102030405060708090a0b0c0d0e0f").to_vec(),
+            buf,
+            "read data must match"
+        );
+    }
+
+    #[tokio::test]
+    async fn test_seek() {
+        let blob_service = gen_blobservice_blob1().await;
+        let mut chunked_reader =
+            ChunkedReader::from_chunks(BLOB_1_LIST.clone().into_iter(), blob_service);
+
+        // seek to the end
+        // expect to read 0 bytes
+        {
+            chunked_reader
+                .seek(SeekFrom::End(0))
+                .await
+                .expect("seek to end");
+
+            let mut buf = Vec::new();
+            chunked_reader
+                .read_to_end(&mut buf)
+                .await
+                .expect("read to end");
+
+            assert_eq!(hex!("").to_vec(), buf);
+        }
+
+        // seek one bytes before the end
+        {
+            chunked_reader.seek(SeekFrom::End(-1)).await.expect("seek");
+
+            let mut buf = Vec::new();
+            chunked_reader
+                .read_to_end(&mut buf)
+                .await
+                .expect("read to end");
+
+            assert_eq!(hex!("0f").to_vec(), buf);
+        }
+
+        // seek back three bytes, but using relative positioning
+        // read two bytes
+        {
+            chunked_reader
+                .seek(SeekFrom::Current(-3))
+                .await
+                .expect("seek");
+
+            let mut buf = [0b0; 2];
+            chunked_reader
+                .read_exact(&mut buf)
+                .await
+                .expect("read exact");
+
+            assert_eq!(hex!("0d0e"), buf);
+        }
+    }
+
+    // seeds a blob service with only the first two chunks, reads a bit in the
+    // front (which succeeds), but then tries to seek past and read more (which
+    // should fail).
+    #[tokio::test]
+    async fn test_read_missing_chunks() {
+        let blob_service = Arc::new(MemoryBlobService::default()) as Arc<dyn BlobService>;
+
+        for blob_contents in [CHUNK_1.to_vec(), CHUNK_2.to_vec()] {
+            let mut bw = blob_service.open_write().await;
+            tokio::io::copy(&mut std::io::Cursor::new(blob_contents), &mut bw)
+                .await
+                .expect("writing blob");
+
+            bw.close().await.expect("close blobwriter");
+        }
+
+        let mut chunked_reader =
+            ChunkedReader::from_chunks(BLOB_1_LIST.clone().into_iter(), blob_service);
+
+        // read a bit from the front (5 bytes out of 6 available)
+        let mut buf = [0b0; 5];
+        chunked_reader
+            .read_exact(&mut buf)
+            .await
+            .expect("read exact");
+
+        assert_eq!(hex!("0001020304"), buf);
+
+        // seek 2 bytes forward, into an area where we don't have chunks
+        chunked_reader
+            .seek(SeekFrom::Current(2))
+            .await
+            .expect("seek");
+
+        let mut buf = Vec::new();
+        chunked_reader
+            .read_to_end(&mut buf)
+            .await
+            .expect_err("must fail");
+
+        // FUTUREWORK: check semantics on errorkinds. Should this be InvalidData
+        // or NotFound?
+    }
+}
diff --git a/tvix/castore/src/blobservice/combinator.rs b/tvix/castore/src/blobservice/combinator.rs
new file mode 100644
index 0000000000..067eff96f4
--- /dev/null
+++ b/tvix/castore/src/blobservice/combinator.rs
@@ -0,0 +1,132 @@
+use futures::{StreamExt, TryStreamExt};
+use tokio_util::io::{ReaderStream, StreamReader};
+use tonic::async_trait;
+use tracing::{instrument, warn};
+
+use crate::B3Digest;
+
+use super::{naive_seeker::NaiveSeeker, BlobReader, BlobService, BlobWriter};
+
+/// Combinator for a BlobService, using a "local" and "remote" blobservice.
+/// Requests are tried in (and returned from) the local store first, only if
+/// things are not present there, the remote BlobService is queried.
+/// In case the local blobservice doesn't have the blob, we ask the remote
+/// blobservice for chunks, and try to read each of these chunks from the local
+/// blobservice again, before falling back to the remote one.
+/// The remote BlobService is never written to.
+pub struct CombinedBlobService<BL, BR> {
+    local: BL,
+    remote: BR,
+}
+
+impl<BL, BR> Clone for CombinedBlobService<BL, BR>
+where
+    BL: Clone,
+    BR: Clone,
+{
+    fn clone(&self) -> Self {
+        Self {
+            local: self.local.clone(),
+            remote: self.remote.clone(),
+        }
+    }
+}
+
+#[async_trait]
+impl<BL, BR> BlobService for CombinedBlobService<BL, BR>
+where
+    BL: AsRef<dyn BlobService> + Clone + Send + Sync + 'static,
+    BR: AsRef<dyn BlobService> + Clone + Send + Sync + 'static,
+{
+    #[instrument(skip(self, digest), fields(blob.digest=%digest))]
+    async fn has(&self, digest: &B3Digest) -> std::io::Result<bool> {
+        Ok(self.local.as_ref().has(digest).await? || self.remote.as_ref().has(digest).await?)
+    }
+
+    #[instrument(skip(self, digest), fields(blob.digest=%digest), err)]
+    async fn open_read(&self, digest: &B3Digest) -> std::io::Result<Option<Box<dyn BlobReader>>> {
+        if self.local.as_ref().has(digest).await? {
+            // local store has the blob, so we can assume it also has all chunks.
+            self.local.as_ref().open_read(digest).await
+        } else {
+            // Local store doesn't have the blob.
+            // Ask the remote one for the list of chunks,
+            // and create a chunked reader that uses self.open_read() for
+            // individual chunks. There's a chance we already have some chunks
+            // locally, meaning we don't need to fetch them all from the remote
+            // BlobService.
+            match self.remote.as_ref().chunks(digest).await? {
+                // blob doesn't exist on the remote side either, nothing we can do.
+                None => Ok(None),
+                Some(remote_chunks) => {
+                    // if there's no more granular chunks, or the remote
+                    // blobservice doesn't support chunks, read the blob from
+                    // the remote blobservice directly.
+                    if remote_chunks.is_empty() {
+                        return self.remote.as_ref().open_read(digest).await;
+                    }
+                    // otherwise, a chunked reader, which will always try the
+                    // local backend first.
+
+                    // map Vec<ChunkMeta> to Vec<(B3Digest, u64)>
+                    let chunks: Vec<(B3Digest, u64)> = remote_chunks
+                        .into_iter()
+                        .map(|chunk_meta| {
+                            (
+                                B3Digest::try_from(chunk_meta.digest)
+                                    .expect("invalid chunk digest"),
+                                chunk_meta.size,
+                            )
+                        })
+                        .collect();
+
+                    Ok(Some(make_chunked_reader(self.clone(), chunks)))
+                }
+            }
+        }
+    }
+
+    #[instrument(skip_all)]
+    async fn open_write(&self) -> Box<dyn BlobWriter> {
+        // direct writes to the local one.
+        self.local.as_ref().open_write().await
+    }
+}
+
+fn make_chunked_reader<BS>(
+    // This must consume, as we can't retain references to blob_service,
+    // as it'd add a lifetime to BlobReader in general, which will get
+    // problematic in TvixStoreFs, which is using async move closures and cloning.
+    blob_service: BS,
+    // A list of b3 digests for individual chunks, and their sizes.
+    chunks: Vec<(B3Digest, u64)>,
+) -> Box<dyn BlobReader>
+where
+    BS: BlobService + Clone + 'static,
+{
+    // TODO: offset, verified streaming
+
+    // construct readers for each chunk
+    let blob_service = blob_service.clone();
+    let readers_stream = tokio_stream::iter(chunks).map(move |(digest, _)| {
+        let d = digest.to_owned();
+        let blob_service = blob_service.clone();
+        async move {
+            blob_service.open_read(&d.to_owned()).await?.ok_or_else(|| {
+                warn!(chunk.digest = %digest, "chunk not found");
+                std::io::Error::new(std::io::ErrorKind::NotFound, "chunk not found")
+            })
+        }
+    });
+
+    // convert the stream of readers to a stream of streams of byte chunks
+    let bytes_streams = readers_stream.then(|elem| async { elem.await.map(ReaderStream::new) });
+
+    // flatten into one stream of byte chunks
+    let bytes_stream = bytes_streams.try_flatten();
+
+    // convert into AsyncRead
+    let blob_reader = StreamReader::new(bytes_stream);
+
+    Box::new(NaiveSeeker::new(Box::pin(blob_reader)))
+}
diff --git a/tvix/castore/src/blobservice/from_addr.rs b/tvix/castore/src/blobservice/from_addr.rs
new file mode 100644
index 0000000000..3e3f943e59
--- /dev/null
+++ b/tvix/castore/src/blobservice/from_addr.rs
@@ -0,0 +1,152 @@
+use url::Url;
+
+use crate::{proto::blob_service_client::BlobServiceClient, Error};
+
+use super::{
+    BlobService, GRPCBlobService, MemoryBlobService, ObjectStoreBlobService, SledBlobService,
+};
+
+/// Constructs a new instance of a [BlobService] from an URI.
+///
+/// The following schemes are supported by the following services:
+/// - `memory://` ([MemoryBlobService])
+/// - `sled://` ([SledBlobService])
+/// - `grpc+*://` ([GRPCBlobService])
+/// - `objectstore+*://` ([ObjectStoreBlobService])
+///
+/// See their `from_url` methods for more details about their syntax.
+pub async fn from_addr(uri: &str) -> Result<Box<dyn BlobService>, crate::Error> {
+    let url = Url::parse(uri)
+        .map_err(|e| crate::Error::StorageError(format!("unable to parse url: {}", e)))?;
+
+    let blob_service: Box<dyn BlobService> = match url.scheme() {
+        "memory" => {
+            // memory doesn't support host or path in the URL.
+            if url.has_host() || !url.path().is_empty() {
+                return Err(Error::StorageError("invalid url".to_string()));
+            }
+            Box::<MemoryBlobService>::default()
+        }
+        "sled" => {
+            // sled doesn't support host, and a path can be provided (otherwise
+            // it'll live in memory only).
+            if url.has_host() {
+                return Err(Error::StorageError("no host allowed".to_string()));
+            }
+
+            if url.path() == "/" {
+                return Err(Error::StorageError(
+                    "cowardly refusing to open / with sled".to_string(),
+                ));
+            }
+
+            // TODO: expose other parameters as URL parameters?
+
+            Box::new(if url.path().is_empty() {
+                SledBlobService::new_temporary().map_err(|e| Error::StorageError(e.to_string()))?
+            } else {
+                SledBlobService::new(url.path()).map_err(|e| Error::StorageError(e.to_string()))?
+            })
+        }
+        scheme if scheme.starts_with("grpc+") => {
+            // schemes starting with grpc+ go to the GRPCPathInfoService.
+            //   That's normally grpc+unix for unix sockets, and grpc+http(s) for the HTTP counterparts.
+            // - In the case of unix sockets, there must be a path, but may not be a host.
+            // - In the case of non-unix sockets, there must be a host, but no path.
+            // Constructing the channel is handled by tvix_castore::channel::from_url.
+            let client = BlobServiceClient::new(crate::tonic::channel_from_url(&url).await?);
+            Box::new(GRPCBlobService::from_client(client))
+        }
+        scheme if scheme.starts_with("objectstore+") => {
+            // We need to convert the URL to string, strip the prefix there, and then
+            // parse it back as url, as Url::set_scheme() rejects some of the transitions we want to do.
+            let trimmed_url = {
+                let s = url.to_string();
+                Url::parse(s.strip_prefix("objectstore+").unwrap()).unwrap()
+            };
+            Box::new(
+                ObjectStoreBlobService::parse_url(&trimmed_url)
+                    .map_err(|e| Error::StorageError(e.to_string()))?,
+            )
+        }
+        scheme => {
+            return Err(crate::Error::StorageError(format!(
+                "unknown scheme: {}",
+                scheme
+            )))
+        }
+    };
+
+    Ok(blob_service)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::from_addr;
+    use lazy_static::lazy_static;
+    use rstest::rstest;
+    use tempfile::TempDir;
+
+    lazy_static! {
+        static ref TMPDIR_SLED_1: TempDir = TempDir::new().unwrap();
+        static ref TMPDIR_SLED_2: TempDir = TempDir::new().unwrap();
+    }
+
+    #[rstest]
+    /// This uses an unsupported scheme.
+    #[case::unsupported_scheme("http://foo.example/test", false)]
+    /// This configures sled in temporary mode.
+    #[case::sled_temporary("sled://", true)]
+    /// This configures sled with /, which should fail.
+    #[case::sled_invalid_root("sled:///", false)]
+    /// This configures sled with a host, not path, which should fail.
+    #[case::sled_invalid_host("sled://foo.example", false)]
+    /// This configures sled with a valid path path, which should succeed.
+    #[case::sled_valid_path(&format!("sled://{}", &TMPDIR_SLED_1.path().to_str().unwrap()), true)]
+    /// This configures sled with a host, and a valid path path, which should fail.
+    #[case::sled_invalid_host_with_valid_path(&format!("sled://foo.example{}", &TMPDIR_SLED_2.path().to_str().unwrap()), false)]
+    /// This correctly sets the scheme, and doesn't set a path.
+    #[case::memory_valid("memory://", true)]
+    /// This sets a memory url host to `foo`
+    #[case::memory_invalid_host("memory://foo", false)]
+    /// This sets a memory url path to "/", which is invalid.
+    #[case::memory_invalid_root_path("memory:///", false)]
+    /// This sets a memory url path to "/foo", which is invalid.
+    #[case::memory_invalid_root_path_foo("memory:///foo", false)]
+    /// Correct scheme to connect to a unix socket.
+    #[case::grpc_valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
+    /// Correct scheme for unix socket, but setting a host too, which is invalid.
+    #[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
+    /// Correct scheme to connect to localhost, with port 12345
+    #[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
+    /// Correct scheme to connect to localhost over http, but with additional path, which is invalid.
+    #[case::grpc_invalid_has_path("grpc+http://localhost/some-path", false)]
+    /// An example for object store (InMemory)
+    #[case::objectstore_valid_memory("objectstore+memory:///", true)]
+    /// An example for object store (LocalFileSystem)
+    #[case::objectstore_valid_file("objectstore+file:///foo/bar", true)]
+    // An example for object store (HTTP / WebDAV)
+    #[case::objectstore_valid_http_url("objectstore+https://localhost:8080/some-path", true)]
+    /// An example for object store (S3)
+    #[cfg_attr(
+        feature = "cloud",
+        case::objectstore_valid_s3_url("objectstore+s3://bucket/path", true)
+    )]
+    /// An example for object store (GCS)
+    #[cfg_attr(
+        feature = "cloud",
+        case::objectstore_valid_gcs_url("objectstore+gs://bucket/path", true)
+    )]
+    #[tokio::test]
+    async fn test_from_addr_tokio(#[case] uri_str: &str, #[case] exp_succeed: bool) {
+        if exp_succeed {
+            from_addr(uri_str).await.expect("should succeed");
+        } else {
+            assert!(from_addr(uri_str).await.is_err(), "should fail");
+        }
+    }
+}
diff --git a/tvix/castore/src/blobservice/grpc.rs b/tvix/castore/src/blobservice/grpc.rs
new file mode 100644
index 0000000000..5663cd3838
--- /dev/null
+++ b/tvix/castore/src/blobservice/grpc.rs
@@ -0,0 +1,349 @@
+use super::{BlobReader, BlobService, BlobWriter, ChunkedReader};
+use crate::{
+    proto::{self, stat_blob_response::ChunkMeta},
+    B3Digest,
+};
+use futures::sink::SinkExt;
+use std::{
+    io::{self, Cursor},
+    pin::pin,
+    sync::Arc,
+    task::Poll,
+};
+use tokio::io::AsyncWriteExt;
+use tokio::task::JoinHandle;
+use tokio_stream::{wrappers::ReceiverStream, StreamExt};
+use tokio_util::{
+    io::{CopyToBytes, SinkWriter},
+    sync::PollSender,
+};
+use tonic::{async_trait, transport::Channel, Code, Status};
+use tracing::instrument;
+
+/// Connects to a (remote) tvix-store BlobService over gRPC.
+#[derive(Clone)]
+pub struct GRPCBlobService {
+    /// The internal reference to a gRPC client.
+    /// Cloning it is cheap, and it internally handles concurrent requests.
+    grpc_client: proto::blob_service_client::BlobServiceClient<Channel>,
+}
+
+impl GRPCBlobService {
+    /// construct a [GRPCBlobService] from a [proto::blob_service_client::BlobServiceClient].
+    /// panics if called outside the context of a tokio runtime.
+    pub fn from_client(
+        grpc_client: proto::blob_service_client::BlobServiceClient<Channel>,
+    ) -> Self {
+        Self { grpc_client }
+    }
+}
+
+#[async_trait]
+impl BlobService for GRPCBlobService {
+    #[instrument(skip(self, digest), fields(blob.digest=%digest))]
+    async fn has(&self, digest: &B3Digest) -> io::Result<bool> {
+        let mut grpc_client = self.grpc_client.clone();
+        let resp = grpc_client
+            .stat(proto::StatBlobRequest {
+                digest: digest.clone().into(),
+                ..Default::default()
+            })
+            .await;
+
+        match resp {
+            Ok(_blob_meta) => Ok(true),
+            Err(e) if e.code() == Code::NotFound => Ok(false),
+            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
+        }
+    }
+
+    #[instrument(skip(self, digest), fields(blob.digest=%digest), err)]
+    async fn open_read(&self, digest: &B3Digest) -> io::Result<Option<Box<dyn BlobReader>>> {
+        // First try to get a list of chunks. In case there's only one chunk returned,
+        // buffer its data into a Vec, otherwise use a ChunkedReader.
+        // We previously used NaiveSeeker here, but userland likes to seek backwards too often,
+        // and without store composition this will get very noisy.
+        // FUTUREWORK: use CombinedBlobService and store composition.
+        match self.chunks(digest).await {
+            Ok(None) => Ok(None),
+            Ok(Some(chunks)) => {
+                if chunks.is_empty() || chunks.len() == 1 {
+                    // No more granular chunking info, treat this as an individual chunk.
+                    // Get a stream of [proto::BlobChunk], or return an error if the blob
+                    // doesn't exist.
+                    return match self
+                        .grpc_client
+                        .clone()
+                        .read(proto::ReadBlobRequest {
+                            digest: digest.clone().into(),
+                        })
+                        .await
+                    {
+                        Ok(stream) => {
+                            let data_stream = stream.into_inner().map(|e| {
+                                e.map(|c| c.data)
+                                    .map_err(|s| std::io::Error::new(io::ErrorKind::InvalidData, s))
+                            });
+
+                            // Use StreamReader::new to convert to an AsyncRead.
+                            let mut data_reader = tokio_util::io::StreamReader::new(data_stream);
+
+                            let mut buf = Vec::new();
+                            // TODO: only do this up to a certain limit.
+                            tokio::io::copy(&mut data_reader, &mut buf).await?;
+
+                            Ok(Some(Box::new(Cursor::new(buf))))
+                        }
+                        Err(e) if e.code() == Code::NotFound => Ok(None),
+                        Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
+                    };
+                }
+
+                // The chunked case. Let ChunkedReader do individual reads.
+                // TODO: we should store the chunking data in some local cache,
+                // so `ChunkedReader` doesn't call `self.chunks` *again* for every chunk.
+                // Think about how store composition will fix this.
+                let chunked_reader = ChunkedReader::from_chunks(
+                    chunks.into_iter().map(|chunk| {
+                        (
+                            chunk.digest.try_into().expect("invalid b3 digest"),
+                            chunk.size,
+                        )
+                    }),
+                    Arc::new(self.clone()) as Arc<dyn BlobService>,
+                );
+                Ok(Some(Box::new(chunked_reader)))
+            }
+            Err(e) => Err(e)?,
+        }
+    }
+
+    /// Returns a BlobWriter, that'll internally wrap each write in a
+    /// [proto::BlobChunk], which is send to the gRPC server.
+    #[instrument(skip_all)]
+    async fn open_write(&self) -> Box<dyn BlobWriter> {
+        // set up an mpsc channel passing around Bytes.
+        let (tx, rx) = tokio::sync::mpsc::channel::<bytes::Bytes>(10);
+
+        // bytes arriving on the RX side are wrapped inside a
+        // [proto::BlobChunk], and a [ReceiverStream] is constructed.
+        let blobchunk_stream = ReceiverStream::new(rx).map(|x| proto::BlobChunk { data: x });
+
+        // spawn the gRPC put request, which will read from blobchunk_stream.
+        let task = tokio::spawn({
+            let mut grpc_client = self.grpc_client.clone();
+            async move { Ok::<_, Status>(grpc_client.put(blobchunk_stream).await?.into_inner()) }
+        });
+
+        // The tx part of the channel is converted to a sink of byte chunks.
+        let sink = PollSender::new(tx)
+            .sink_map_err(|e| std::io::Error::new(std::io::ErrorKind::BrokenPipe, e));
+
+        // … which is turned into an [tokio::io::AsyncWrite].
+        let writer = SinkWriter::new(CopyToBytes::new(sink));
+
+        Box::new(GRPCBlobWriter {
+            task_and_writer: Some((task, writer)),
+            digest: None,
+        })
+    }
+
+    #[instrument(skip(self, digest), fields(blob.digest=%digest), err)]
+    async fn chunks(&self, digest: &B3Digest) -> io::Result<Option<Vec<ChunkMeta>>> {
+        let resp = self
+            .grpc_client
+            .clone()
+            .stat(proto::StatBlobRequest {
+                digest: digest.clone().into(),
+                send_chunks: true,
+                ..Default::default()
+            })
+            .await;
+
+        match resp {
+            Err(e) if e.code() == Code::NotFound => Ok(None),
+            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
+            Ok(resp) => {
+                let resp = resp.into_inner();
+
+                resp.validate()
+                    .map_err(|e| std::io::Error::new(io::ErrorKind::InvalidData, e))?;
+
+                Ok(Some(resp.chunks))
+            }
+        }
+    }
+}
+
+pub struct GRPCBlobWriter<W: tokio::io::AsyncWrite> {
+    /// The task containing the put request, and the inner writer, if we're still writing.
+    task_and_writer: Option<(JoinHandle<Result<proto::PutBlobResponse, Status>>, W)>,
+
+    /// The digest that has been returned, if we successfully closed.
+    digest: Option<B3Digest>,
+}
+
+#[async_trait]
+impl<W: tokio::io::AsyncWrite + Send + Sync + Unpin + 'static> BlobWriter for GRPCBlobWriter<W> {
+    async fn close(&mut self) -> io::Result<B3Digest> {
+        if self.task_and_writer.is_none() {
+            // if we're already closed, return the b3 digest, which must exist.
+            // If it doesn't, we already closed and failed once, and didn't handle the error.
+            match &self.digest {
+                Some(digest) => Ok(digest.clone()),
+                None => Err(io::Error::new(io::ErrorKind::BrokenPipe, "already closed")),
+            }
+        } else {
+            let (task, mut writer) = self.task_and_writer.take().unwrap();
+
+            // invoke shutdown, so the inner writer closes its internal tx side of
+            // the channel.
+            writer.shutdown().await?;
+
+            // block on the RPC call to return.
+            // This ensures all chunks are sent out, and have been received by the
+            // backend.
+
+            match task.await? {
+                Ok(resp) => {
+                    // return the digest from the response, and store it in self.digest for subsequent closes.
+                    let digest_len = resp.digest.len();
+                    let digest: B3Digest = resp.digest.try_into().map_err(|_| {
+                        io::Error::new(
+                            io::ErrorKind::Other,
+                            format!("invalid root digest length {} in response", digest_len),
+                        )
+                    })?;
+                    self.digest = Some(digest.clone());
+                    Ok(digest)
+                }
+                Err(e) => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
+            }
+        }
+    }
+}
+
+impl<W: tokio::io::AsyncWrite + Unpin> tokio::io::AsyncWrite for GRPCBlobWriter<W> {
+    fn poll_write(
+        mut self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+        buf: &[u8],
+    ) -> std::task::Poll<Result<usize, io::Error>> {
+        match &mut self.task_and_writer {
+            None => Poll::Ready(Err(io::Error::new(
+                io::ErrorKind::NotConnected,
+                "already closed",
+            ))),
+            Some((_, ref mut writer)) => {
+                let pinned_writer = pin!(writer);
+                pinned_writer.poll_write(cx, buf)
+            }
+        }
+    }
+
+    fn poll_flush(
+        mut self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Result<(), io::Error>> {
+        match &mut self.task_and_writer {
+            None => Poll::Ready(Err(io::Error::new(
+                io::ErrorKind::NotConnected,
+                "already closed",
+            ))),
+            Some((_, ref mut writer)) => {
+                let pinned_writer = pin!(writer);
+                pinned_writer.poll_flush(cx)
+            }
+        }
+    }
+
+    fn poll_shutdown(
+        self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Result<(), io::Error>> {
+        // TODO(raitobezarius): this might not be a graceful shutdown of the
+        // channel inside the gRPC connection.
+        Poll::Ready(Ok(()))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::time::Duration;
+
+    use tempfile::TempDir;
+    use tokio::net::UnixListener;
+    use tokio_retry::strategy::ExponentialBackoff;
+    use tokio_retry::Retry;
+    use tokio_stream::wrappers::UnixListenerStream;
+
+    use crate::blobservice::MemoryBlobService;
+    use crate::fixtures;
+    use crate::proto::blob_service_client::BlobServiceClient;
+    use crate::proto::GRPCBlobServiceWrapper;
+
+    use super::BlobService;
+    use super::GRPCBlobService;
+
+    /// This ensures connecting via gRPC works as expected.
+    #[tokio::test]
+    async fn test_valid_unix_path_ping_pong() {
+        let tmpdir = TempDir::new().unwrap();
+        let socket_path = tmpdir.path().join("daemon");
+
+        let path_clone = socket_path.clone();
+
+        // Spin up a server
+        tokio::spawn(async {
+            let uds = UnixListener::bind(path_clone).unwrap();
+            let uds_stream = UnixListenerStream::new(uds);
+
+            // spin up a new server
+            let mut server = tonic::transport::Server::builder();
+            let router =
+                server.add_service(crate::proto::blob_service_server::BlobServiceServer::new(
+                    GRPCBlobServiceWrapper::new(
+                        Box::<MemoryBlobService>::default() as Box<dyn BlobService>
+                    ),
+                ));
+            router.serve_with_incoming(uds_stream).await
+        });
+
+        // wait for the socket to be created
+        Retry::spawn(
+            ExponentialBackoff::from_millis(20).max_delay(Duration::from_secs(10)),
+            || async {
+                if socket_path.exists() {
+                    Ok(())
+                } else {
+                    Err(())
+                }
+            },
+        )
+        .await
+        .expect("failed to wait for socket");
+
+        // prepare a client
+        let grpc_client = {
+            let url = url::Url::parse(&format!(
+                "grpc+unix://{}?wait-connect=1",
+                socket_path.display()
+            ))
+            .expect("must parse");
+            let client = BlobServiceClient::new(
+                crate::tonic::channel_from_url(&url)
+                    .await
+                    .expect("must succeed"),
+            );
+
+            GRPCBlobService::from_client(client)
+        };
+
+        let has = grpc_client
+            .has(&fixtures::BLOB_A_DIGEST)
+            .await
+            .expect("must not be err");
+
+        assert!(!has);
+    }
+}
diff --git a/tvix/castore/src/blobservice/memory.rs b/tvix/castore/src/blobservice/memory.rs
new file mode 100644
index 0000000000..25eec334de
--- /dev/null
+++ b/tvix/castore/src/blobservice/memory.rs
@@ -0,0 +1,137 @@
+use std::io::{self, Cursor, Write};
+use std::task::Poll;
+use std::{
+    collections::HashMap,
+    sync::{Arc, RwLock},
+};
+use tonic::async_trait;
+use tracing::instrument;
+
+use super::{BlobReader, BlobService, BlobWriter};
+use crate::B3Digest;
+
+#[derive(Clone, Default)]
+pub struct MemoryBlobService {
+    db: Arc<RwLock<HashMap<B3Digest, Vec<u8>>>>,
+}
+
+#[async_trait]
+impl BlobService for MemoryBlobService {
+    #[instrument(skip_all, ret, err, fields(blob.digest=%digest))]
+    async fn has(&self, digest: &B3Digest) -> io::Result<bool> {
+        let db = self.db.read().unwrap();
+        Ok(db.contains_key(digest))
+    }
+
+    #[instrument(skip_all, err, fields(blob.digest=%digest))]
+    async fn open_read(&self, digest: &B3Digest) -> io::Result<Option<Box<dyn BlobReader>>> {
+        let db = self.db.read().unwrap();
+
+        match db.get(digest).map(|x| Cursor::new(x.clone())) {
+            Some(result) => Ok(Some(Box::new(result))),
+            None => Ok(None),
+        }
+    }
+
+    #[instrument(skip_all)]
+    async fn open_write(&self) -> Box<dyn BlobWriter> {
+        Box::new(MemoryBlobWriter::new(self.db.clone()))
+    }
+}
+
+pub struct MemoryBlobWriter {
+    db: Arc<RwLock<HashMap<B3Digest, Vec<u8>>>>,
+
+    /// Contains the buffer Vec and hasher, or None if already closed
+    writers: Option<(Vec<u8>, blake3::Hasher)>,
+
+    /// The digest that has been returned, if we successfully closed.
+    digest: Option<B3Digest>,
+}
+
+impl MemoryBlobWriter {
+    fn new(db: Arc<RwLock<HashMap<B3Digest, Vec<u8>>>>) -> Self {
+        Self {
+            db,
+            writers: Some((Vec::new(), blake3::Hasher::new())),
+            digest: None,
+        }
+    }
+}
+impl tokio::io::AsyncWrite for MemoryBlobWriter {
+    fn poll_write(
+        mut self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+        b: &[u8],
+    ) -> std::task::Poll<Result<usize, io::Error>> {
+        Poll::Ready(match &mut self.writers {
+            None => Err(io::Error::new(
+                io::ErrorKind::NotConnected,
+                "already closed",
+            )),
+            Some((ref mut buf, ref mut hasher)) => {
+                let bytes_written = buf.write(b)?;
+                hasher.write(&b[..bytes_written])
+            }
+        })
+    }
+
+    fn poll_flush(
+        self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Result<(), io::Error>> {
+        Poll::Ready(match self.writers {
+            None => Err(io::Error::new(
+                io::ErrorKind::NotConnected,
+                "already closed",
+            )),
+            Some(_) => Ok(()),
+        })
+    }
+
+    fn poll_shutdown(
+        self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Result<(), io::Error>> {
+        // shutdown is "instantaneous", we only write to memory.
+        Poll::Ready(Ok(()))
+    }
+}
+
+#[async_trait]
+impl BlobWriter for MemoryBlobWriter {
+    async fn close(&mut self) -> io::Result<B3Digest> {
+        if self.writers.is_none() {
+            match &self.digest {
+                Some(digest) => Ok(digest.clone()),
+                None => Err(io::Error::new(io::ErrorKind::BrokenPipe, "already closed")),
+            }
+        } else {
+            let (buf, hasher) = self.writers.take().unwrap();
+
+            // We know self.hasher is doing blake3 hashing, so this won't fail.
+            let digest: B3Digest = hasher.finalize().as_bytes().into();
+
+            // Only insert if the blob doesn't already exist.
+            let db = self.db.read().map_err(|e| {
+                io::Error::new(io::ErrorKind::BrokenPipe, format!("RwLock poisoned: {}", e))
+            })?;
+            if !db.contains_key(&digest) {
+                // drop the read lock, so we can open for writing.
+                drop(db);
+
+                // open the database for writing.
+                let mut db = self.db.write().map_err(|e| {
+                    io::Error::new(io::ErrorKind::BrokenPipe, format!("RwLock poisoned: {}", e))
+                })?;
+
+                // and put buf in there. This will move buf out.
+                db.insert(digest.clone(), buf);
+            }
+
+            self.digest = Some(digest.clone());
+
+            Ok(digest)
+        }
+    }
+}
diff --git a/tvix/castore/src/blobservice/mod.rs b/tvix/castore/src/blobservice/mod.rs
new file mode 100644
index 0000000000..4ba56a4af7
--- /dev/null
+++ b/tvix/castore/src/blobservice/mod.rs
@@ -0,0 +1,105 @@
+use std::io;
+use tonic::async_trait;
+
+use crate::proto::stat_blob_response::ChunkMeta;
+use crate::B3Digest;
+
+mod chunked_reader;
+mod combinator;
+mod from_addr;
+mod grpc;
+mod memory;
+mod naive_seeker;
+mod object_store;
+mod sled;
+
+#[cfg(test)]
+pub mod tests;
+
+pub use self::chunked_reader::ChunkedReader;
+pub use self::combinator::CombinedBlobService;
+pub use self::from_addr::from_addr;
+pub use self::grpc::GRPCBlobService;
+pub use self::memory::MemoryBlobService;
+pub use self::object_store::ObjectStoreBlobService;
+pub use self::sled::SledBlobService;
+
+/// The base trait all BlobService services need to implement.
+/// It provides functions to check whether a given blob exists,
+/// a way to read (and seek) a blob, and a method to create a blobwriter handle,
+/// which will implement a writer interface, and also provides a close funtion,
+/// to finalize a blob and get its digest.
+#[async_trait]
+pub trait BlobService: Send + Sync {
+    /// Check if the service has the blob, by its content hash.
+    /// On implementations returning chunks, this must also work for chunks.
+    async fn has(&self, digest: &B3Digest) -> io::Result<bool>;
+
+    /// Request a blob from the store, by its content hash.
+    /// On implementations returning chunks, this must also work for chunks.
+    async fn open_read(&self, digest: &B3Digest) -> io::Result<Option<Box<dyn BlobReader>>>;
+
+    /// Insert a new blob into the store. Returns a [BlobWriter], which
+    /// implements [tokio::io::AsyncWrite] and a [BlobWriter::close] to finalize
+    /// the blob and get its digest.
+    async fn open_write(&self) -> Box<dyn BlobWriter>;
+
+    /// Return a list of chunks for a given blob.
+    /// There's a distinction between returning Ok(None) and Ok(Some(vec![])).
+    /// The former return value is sent in case the blob is not present at all,
+    /// while the second one is sent in case there's no more granular chunks (or
+    /// the backend does not support chunking).
+    /// A default implementation checking for existence and then returning it
+    /// does not have more granular chunks available is provided.
+    async fn chunks(&self, digest: &B3Digest) -> io::Result<Option<Vec<ChunkMeta>>> {
+        if !self.has(digest).await? {
+            return Ok(None);
+        }
+        // default implementation, signalling the backend does not have more
+        // granular chunks available.
+        Ok(Some(vec![]))
+    }
+}
+
+#[async_trait]
+impl<A> BlobService for A
+where
+    A: AsRef<dyn BlobService> + Send + Sync,
+{
+    async fn has(&self, digest: &B3Digest) -> io::Result<bool> {
+        self.as_ref().has(digest).await
+    }
+
+    async fn open_read(&self, digest: &B3Digest) -> io::Result<Option<Box<dyn BlobReader>>> {
+        self.as_ref().open_read(digest).await
+    }
+
+    async fn open_write(&self) -> Box<dyn BlobWriter> {
+        self.as_ref().open_write().await
+    }
+
+    async fn chunks(&self, digest: &B3Digest) -> io::Result<Option<Vec<ChunkMeta>>> {
+        self.as_ref().chunks(digest).await
+    }
+}
+
+/// A [tokio::io::AsyncWrite] that the user needs to close() afterwards for persist.
+/// On success, it returns the digest of the written blob.
+#[async_trait]
+pub trait BlobWriter: tokio::io::AsyncWrite + Send + Unpin {
+    /// Signal there's no more data to be written, and return the digest of the
+    /// contents written.
+    ///
+    /// Closing a already-closed BlobWriter is a no-op.
+    async fn close(&mut self) -> io::Result<B3Digest>;
+}
+
+/// BlobReader is a [tokio::io::AsyncRead] that also allows seeking.
+pub trait BlobReader: tokio::io::AsyncRead + tokio::io::AsyncSeek + Send + Unpin + 'static {}
+
+/// A [`io::Cursor<Vec<u8>>`] can be used as a BlobReader.
+impl BlobReader for io::Cursor<&'static [u8]> {}
+impl BlobReader for io::Cursor<&'static [u8; 0]> {}
+impl BlobReader for io::Cursor<Vec<u8>> {}
+impl BlobReader for io::Cursor<bytes::Bytes> {}
+impl BlobReader for tokio::fs::File {}
diff --git a/tvix/castore/src/blobservice/naive_seeker.rs b/tvix/castore/src/blobservice/naive_seeker.rs
new file mode 100644
index 0000000000..f5a5307150
--- /dev/null
+++ b/tvix/castore/src/blobservice/naive_seeker.rs
@@ -0,0 +1,265 @@
+use super::BlobReader;
+use futures::ready;
+use pin_project_lite::pin_project;
+use std::io;
+use std::task::Poll;
+use tokio::io::AsyncRead;
+use tracing::{debug, instrument, trace, warn};
+
+pin_project! {
+    /// This implements [tokio::io::AsyncSeek] for and [tokio::io::AsyncRead] by
+    /// simply skipping over some bytes, keeping track of the position.
+    /// It fails whenever you try to seek backwards.
+    ///
+    /// ## Pinning concerns:
+    ///
+    /// [NaiveSeeker] is itself pinned by callers, and we do not need to concern
+    /// ourselves regarding that.
+    ///
+    /// Though, its fields as per
+    /// <https://doc.rust-lang.org/std/pin/#pinning-is-not-structural-for-field>
+    /// can be pinned or unpinned.
+    ///
+    /// So we need to go over each field and choose our policy carefully.
+    ///
+    /// The obvious cases are the bookkeeping integers we keep in the structure,
+    /// those are private and not shared to anyone, we never build a
+    /// `Pin<&mut X>` out of them at any point, therefore, we can safely never
+    /// mark them as pinned. Of course, it is expected that no developer here
+    /// attempt to `pin!(self.pos)` to pin them because it makes no sense. If
+    /// they have to become pinned, they should be marked `#[pin]` and we need
+    /// to discuss it.
+    ///
+    /// So the bookkeeping integers are in the right state with respect to their
+    /// pinning status. The projection should offer direct access.
+    ///
+    /// On the `r` field, i.e. a `BufReader<R>`, given that
+    /// <https://docs.rs/tokio/latest/tokio/io/struct.BufReader.html#impl-Unpin-for-BufReader%3CR%3E>
+    /// is available, even a `Pin<&mut BufReader<R>>` can be safely moved.
+    ///
+    /// The only care we should have regards the internal reader itself, i.e.
+    /// the `R` instance, see that Tokio decided to `#[pin]` it too:
+    /// <https://docs.rs/tokio/latest/src/tokio/io/util/buf_reader.rs.html#29>
+    ///
+    /// In general, there's no `Unpin` instance for `R: tokio::io::AsyncRead`
+    /// (see <https://docs.rs/tokio/latest/tokio/io/trait.AsyncRead.html>).
+    ///
+    /// Therefore, we could keep it unpinned and pin it in every call site
+    /// whenever we need to call `poll_*` which can be confusing to the non-
+    /// expert developer and we have a fair share amount of situations where the
+    /// [BufReader] instance is naked, i.e. in its `&mut BufReader<R>`
+    /// form, this is annoying because it could lead to expose the naked `R`
+    /// internal instance somehow and would produce a risk of making it move
+    /// unexpectedly.
+    ///
+    /// We choose the path of the least resistance as we have no reason to have
+    /// access to the raw `BufReader<R>` instance, we just `#[pin]` it too and
+    /// enjoy its `poll_*` safe APIs and push the unpinning concerns to the
+    /// internal implementations themselves, which studied the question longer
+    /// than us.
+    pub struct NaiveSeeker<R: tokio::io::AsyncRead> {
+        #[pin]
+        r: tokio::io::BufReader<R>,
+        pos: u64,
+        bytes_to_skip: u64,
+    }
+}
+
+/// The buffer size used to discard data.
+const DISCARD_BUF_SIZE: usize = 4096;
+
+impl<R: tokio::io::AsyncRead> NaiveSeeker<R> {
+    pub fn new(r: R) -> Self {
+        NaiveSeeker {
+            r: tokio::io::BufReader::new(r),
+            pos: 0,
+            bytes_to_skip: 0,
+        }
+    }
+}
+
+impl<R: tokio::io::AsyncRead> tokio::io::AsyncRead for NaiveSeeker<R> {
+    #[instrument(level = "trace", skip_all)]
+    fn poll_read(
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+        buf: &mut tokio::io::ReadBuf<'_>,
+    ) -> Poll<std::io::Result<()>> {
+        // The amount of data read can be determined by the increase
+        // in the length of the slice returned by `ReadBuf::filled`.
+        let filled_before = buf.filled().len();
+
+        let this = self.project();
+        ready!(this.r.poll_read(cx, buf))?;
+
+        let bytes_read = buf.filled().len() - filled_before;
+        *this.pos += bytes_read as u64;
+
+        trace!(bytes_read = bytes_read, new_pos = this.pos, "poll_read");
+
+        Ok(()).into()
+    }
+}
+
+impl<R: tokio::io::AsyncRead> tokio::io::AsyncBufRead for NaiveSeeker<R> {
+    fn poll_fill_buf(
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> Poll<io::Result<&[u8]>> {
+        self.project().r.poll_fill_buf(cx)
+    }
+
+    #[instrument(level = "trace", skip(self))]
+    fn consume(self: std::pin::Pin<&mut Self>, amt: usize) {
+        let this = self.project();
+        this.r.consume(amt);
+        *this.pos += amt as u64;
+
+        trace!(new_pos = this.pos, "consume");
+    }
+}
+
+impl<R: tokio::io::AsyncRead> tokio::io::AsyncSeek for NaiveSeeker<R> {
+    #[instrument(level="trace", skip(self), fields(inner_pos=%self.pos), err(Debug))]
+    fn start_seek(
+        self: std::pin::Pin<&mut Self>,
+        position: std::io::SeekFrom,
+    ) -> std::io::Result<()> {
+        let absolute_offset: u64 = match position {
+            io::SeekFrom::Start(start_offset) => {
+                if start_offset < self.pos {
+                    return Err(io::Error::new(
+                        io::ErrorKind::Unsupported,
+                        format!("can't seek backwards ({} -> {})", self.pos, start_offset),
+                    ));
+                } else {
+                    start_offset
+                }
+            }
+            // we don't know the total size, can't support this.
+            io::SeekFrom::End(_end_offset) => {
+                return Err(io::Error::new(
+                    io::ErrorKind::Unsupported,
+                    "can't seek from end",
+                ));
+            }
+            io::SeekFrom::Current(relative_offset) => {
+                if relative_offset < 0 {
+                    return Err(io::Error::new(
+                        io::ErrorKind::Unsupported,
+                        "can't seek backwards relative to current position",
+                    ));
+                } else {
+                    self.pos + relative_offset as u64
+                }
+            }
+        };
+
+        // we already know absolute_offset is >= self.pos
+        debug_assert!(
+            absolute_offset >= self.pos,
+            "absolute_offset {} must be >= self.pos {}",
+            absolute_offset,
+            self.pos
+        );
+
+        // calculate bytes to skip
+        let this = self.project();
+        *this.bytes_to_skip = absolute_offset - *this.pos;
+
+        debug!(bytes_to_skip = *this.bytes_to_skip, "seek");
+
+        Ok(())
+    }
+
+    #[instrument(skip_all)]
+    fn poll_complete(
+        mut self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> Poll<std::io::Result<u64>> {
+        if self.bytes_to_skip == 0 {
+            // return the new position (from the start of the stream)
+            return Poll::Ready(Ok(self.pos));
+        }
+
+        // discard some bytes, until pos is where we want it to be.
+        // We create a buffer that we'll discard later on.
+        let mut discard_buf = [0; DISCARD_BUF_SIZE];
+
+        // Loop until we've reached the desired seek position. This is done by issuing repeated
+        // `poll_read` calls.
+        // If the data is not available yet, we will yield back to the executor
+        // and wait to be polled again.
+        loop {
+            if self.bytes_to_skip == 0 {
+                return Poll::Ready(Ok(self.pos));
+            }
+
+            // calculate the length we want to skip at most, which is either a max
+            // buffer size, or the number of remaining bytes to read, whatever is
+            // smaller.
+            let bytes_to_skip_now = std::cmp::min(self.bytes_to_skip as usize, discard_buf.len());
+            let mut discard_buf = tokio::io::ReadBuf::new(&mut discard_buf[..bytes_to_skip_now]);
+
+            ready!(self.as_mut().poll_read(cx, &mut discard_buf))?;
+            let bytes_skipped = discard_buf.filled().len();
+
+            if bytes_skipped == 0 {
+                return Poll::Ready(Err(io::Error::new(
+                    io::ErrorKind::UnexpectedEof,
+                    "got EOF while trying to skip bytes",
+                )));
+            }
+            // decrement bytes to skip. The poll_read call already updated self.pos.
+            *self.as_mut().project().bytes_to_skip -= bytes_skipped as u64;
+        }
+    }
+}
+
+impl<R: tokio::io::AsyncRead + Send + Unpin + 'static> BlobReader for NaiveSeeker<R> {}
+
+#[cfg(test)]
+mod tests {
+    use super::{NaiveSeeker, DISCARD_BUF_SIZE};
+    use std::io::{Cursor, SeekFrom};
+    use tokio::io::{AsyncReadExt, AsyncSeekExt};
+
+    /// This seek requires multiple `poll_read` as we use a multiples of
+    /// DISCARD_BUF_SIZE when doing the seek.
+    /// This ensures we don't hang indefinitely.
+    #[tokio::test]
+    async fn seek() {
+        let buf = vec![0u8; DISCARD_BUF_SIZE * 4];
+        let reader = Cursor::new(&buf);
+        let mut seeker = NaiveSeeker::new(reader);
+        seeker.seek(SeekFrom::Start(4000)).await.unwrap();
+    }
+
+    #[tokio::test]
+    async fn seek_read() {
+        let mut buf = vec![0u8; DISCARD_BUF_SIZE * 2];
+        buf.extend_from_slice(&[1u8; DISCARD_BUF_SIZE * 2]);
+        buf.extend_from_slice(&[2u8; DISCARD_BUF_SIZE * 2]);
+
+        let reader = Cursor::new(&buf);
+        let mut seeker = NaiveSeeker::new(reader);
+
+        let mut read_buf = vec![0u8; DISCARD_BUF_SIZE];
+        seeker.read_exact(&mut read_buf).await.expect("must read");
+        assert_eq!(read_buf.as_slice(), &[0u8; DISCARD_BUF_SIZE]);
+
+        seeker
+            .seek(SeekFrom::Current(DISCARD_BUF_SIZE as i64))
+            .await
+            .expect("must seek");
+        seeker.read_exact(&mut read_buf).await.expect("must read");
+        assert_eq!(read_buf.as_slice(), &[1u8; DISCARD_BUF_SIZE]);
+
+        seeker
+            .seek(SeekFrom::Start(2 * 2 * DISCARD_BUF_SIZE as u64))
+            .await
+            .expect("must seek");
+        seeker.read_exact(&mut read_buf).await.expect("must read");
+        assert_eq!(read_buf.as_slice(), &[2u8; DISCARD_BUF_SIZE]);
+    }
+}
diff --git a/tvix/castore/src/blobservice/object_store.rs b/tvix/castore/src/blobservice/object_store.rs
new file mode 100644
index 0000000000..d2d0a288a5
--- /dev/null
+++ b/tvix/castore/src/blobservice/object_store.rs
@@ -0,0 +1,546 @@
+use std::{
+    io::{self, Cursor},
+    pin::pin,
+    sync::Arc,
+    task::Poll,
+};
+
+use data_encoding::HEXLOWER;
+use fastcdc::v2020::AsyncStreamCDC;
+use futures::Future;
+use object_store::{path::Path, ObjectStore};
+use pin_project_lite::pin_project;
+use prost::Message;
+use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
+use tokio_stream::StreamExt;
+use tonic::async_trait;
+use tracing::{debug, instrument, trace, Level};
+use url::Url;
+
+use crate::{
+    proto::{stat_blob_response::ChunkMeta, StatBlobResponse},
+    B3Digest, B3HashingReader,
+};
+
+use super::{BlobReader, BlobService, BlobWriter, ChunkedReader};
+
+#[derive(Clone)]
+pub struct ObjectStoreBlobService {
+    object_store: Arc<dyn ObjectStore>,
+    base_path: Path,
+
+    /// Average chunk size for FastCDC, in bytes.
+    /// min value is half, max value double of that number.
+    avg_chunk_size: u32,
+}
+
+/// Uses any object storage supported by the [object_store] crate to provide a
+/// tvix-castore [BlobService].
+///
+/// # Data format
+/// Data is organized in "blobs" and "chunks".
+/// Blobs don't hold the actual data, but instead contain a list of more
+/// granular chunks that assemble to the contents requested.
+/// This allows clients to seek, and not download chunks they already have
+/// locally, as it's referred to from other files.
+/// Check `rpc_blobstore` and more general BlobStore docs on that.
+///
+/// ## Blobs
+/// Stored at `${base_path}/blobs/b3/$digest_key`. They contains the serialized
+/// StatBlobResponse for the blob with the digest.
+///
+/// ## Chunks
+/// Chunks are stored at `${base_path}/chunks/b3/$digest_key`. They contain
+/// the literal contents of the chunk, but are zstd-compressed.
+///
+/// ## Digest key sharding
+/// The blake3 digest encoded in lower hex, and sharded after the second
+/// character.
+/// The blob for "Hello World" is stored at
+/// `${base_path}/blobs/b3/41/41f8394111eb713a22165c46c90ab8f0fd9399c92028fd6d288944b23ff5bf76`.
+///
+/// This reduces the number of files in the same directory, which would be a
+/// problem at least when using [object_store::local::LocalFileSystem].
+///
+/// # Future changes
+/// There's no guarantees about this being a final format yet.
+/// Once object_store gets support for additional metadata / content-types,
+/// we can eliminate some requests (small blobs only consisting of a single
+/// chunk can be stored as-is, without the blob index file).
+/// It also allows signalling any compression of chunks in the content-type.
+/// Migration *should* be possible by simply adding the right content-types to
+/// all keys stored so far, but no promises ;-)
+impl ObjectStoreBlobService {
+    /// Constructs a new [ObjectStoreBlobService] from a [Url] supported by
+    /// [object_store].
+    /// Any path suffix becomes the base path of the object store.
+    /// additional options, the same as in [object_store::parse_url_opts] can
+    /// be passed.
+    pub fn parse_url_opts<I, K, V>(url: &Url, options: I) -> Result<Self, object_store::Error>
+    where
+        I: IntoIterator<Item = (K, V)>,
+        K: AsRef<str>,
+        V: Into<String>,
+    {
+        let (object_store, path) = object_store::parse_url_opts(url, options)?;
+
+        Ok(Self {
+            object_store: Arc::new(object_store),
+            base_path: path,
+            avg_chunk_size: 256 * 1024,
+        })
+    }
+
+    /// Like [Self::parse_url_opts], except without the options.
+    pub fn parse_url(url: &Url) -> Result<Self, object_store::Error> {
+        Self::parse_url_opts(url, Vec::<(String, String)>::new())
+    }
+}
+
+#[instrument(level=Level::TRACE, skip_all,fields(base_path=%base_path,blob.digest=%digest),ret(Display))]
+fn derive_blob_path(base_path: &Path, digest: &B3Digest) -> Path {
+    base_path
+        .child("blobs")
+        .child("b3")
+        .child(HEXLOWER.encode(&digest.as_slice()[..2]))
+        .child(HEXLOWER.encode(digest.as_slice()))
+}
+
+#[instrument(level=Level::TRACE, skip_all,fields(base_path=%base_path,chunk.digest=%digest),ret(Display))]
+fn derive_chunk_path(base_path: &Path, digest: &B3Digest) -> Path {
+    base_path
+        .child("chunks")
+        .child("b3")
+        .child(HEXLOWER.encode(&digest.as_slice()[..2]))
+        .child(HEXLOWER.encode(digest.as_slice()))
+}
+
+#[async_trait]
+impl BlobService for ObjectStoreBlobService {
+    #[instrument(skip_all, ret, err, fields(blob.digest=%digest))]
+    async fn has(&self, digest: &B3Digest) -> io::Result<bool> {
+        // TODO: clarify if this should work for chunks or not, and explicitly
+        // document in the proto docs.
+        let p = derive_blob_path(&self.base_path, digest);
+
+        match self.object_store.head(&p).await {
+            Ok(_) => Ok(true),
+            Err(object_store::Error::NotFound { .. }) => {
+                let p = derive_chunk_path(&self.base_path, digest);
+                match self.object_store.head(&p).await {
+                    Ok(_) => Ok(true),
+                    Err(object_store::Error::NotFound { .. }) => Ok(false),
+                    Err(e) => Err(e)?,
+                }
+            }
+            Err(e) => Err(e)?,
+        }
+    }
+
+    #[instrument(skip_all, err, fields(blob.digest=%digest))]
+    async fn open_read(&self, digest: &B3Digest) -> io::Result<Option<Box<dyn BlobReader>>> {
+        // handle reading the empty blob.
+        if digest.as_slice() == blake3::hash(b"").as_bytes() {
+            return Ok(Some(Box::new(Cursor::new(b"")) as Box<dyn BlobReader>));
+        }
+        match self
+            .object_store
+            .get(&derive_chunk_path(&self.base_path, digest))
+            .await
+        {
+            Ok(res) => {
+                // handle reading blobs that are small enough to fit inside a single chunk:
+                // fetch the entire chunk into memory, decompress, ensure the b3 digest matches,
+                // and return a io::Cursor over that data.
+                // FUTUREWORK: use zstd::bulk to prevent decompression bombs
+
+                let chunk_raw_bytes = res.bytes().await?;
+                let chunk_contents = zstd::stream::decode_all(Cursor::new(chunk_raw_bytes))?;
+
+                if *digest != blake3::hash(&chunk_contents).as_bytes().into() {
+                    Err(io::Error::other("chunk contents invalid"))?;
+                }
+
+                Ok(Some(Box::new(Cursor::new(chunk_contents))))
+            }
+            Err(object_store::Error::NotFound { .. }) => {
+                // NOTE: For public-facing things, we would want to stop here.
+                // Clients should fetch granularly, so they can make use of
+                // chunks they have locally.
+                // However, if this is used directly, without any caches, do the
+                // assembly here.
+                // This is subject to change, once we have store composition.
+                // TODO: make this configurable, and/or clarify behaviour for
+                // the gRPC server surface (explicitly document behaviour in the
+                // proto docs)
+                if let Some(chunks) = self.chunks(digest).await? {
+                    let chunked_reader = ChunkedReader::from_chunks(
+                        chunks.into_iter().map(|chunk| {
+                            (
+                                chunk.digest.try_into().expect("invalid b3 digest"),
+                                chunk.size,
+                            )
+                        }),
+                        Arc::new(self.clone()) as Arc<dyn BlobService>,
+                    );
+
+                    Ok(Some(Box::new(chunked_reader)))
+                } else {
+                    // This is neither a chunk nor a blob, return None.
+                    Ok(None)
+                }
+            }
+            Err(e) => Err(e.into()),
+        }
+    }
+
+    #[instrument(skip_all)]
+    async fn open_write(&self) -> Box<dyn BlobWriter> {
+        // ObjectStoreBlobWriter implements AsyncWrite, but all the chunking
+        // needs an AsyncRead, so we create a pipe here.
+        // In its `AsyncWrite` implementation, `ObjectStoreBlobWriter` delegates
+        // writes to w. It periodically polls the future that's reading from the
+        // other side.
+        let (w, r) = tokio::io::duplex(self.avg_chunk_size as usize * 10);
+
+        Box::new(ObjectStoreBlobWriter {
+            writer: Some(w),
+            fut: Some(Box::pin(chunk_and_upload(
+                r,
+                self.object_store.clone(),
+                self.base_path.clone(),
+                self.avg_chunk_size / 2,
+                self.avg_chunk_size,
+                self.avg_chunk_size * 2,
+            ))),
+            fut_output: None,
+        })
+    }
+
+    #[instrument(skip_all, err, fields(blob.digest=%digest))]
+    async fn chunks(&self, digest: &B3Digest) -> io::Result<Option<Vec<ChunkMeta>>> {
+        match self
+            .object_store
+            .get(&derive_blob_path(&self.base_path, digest))
+            .await
+        {
+            Ok(get_result) => {
+                // fetch the data at the blob path
+                let blob_data = get_result.bytes().await?;
+                // parse into StatBlobResponse
+                let stat_blob_response: StatBlobResponse = StatBlobResponse::decode(blob_data)?;
+
+                debug!(
+                    chunk.count = stat_blob_response.chunks.len(),
+                    blob.size = stat_blob_response
+                        .chunks
+                        .iter()
+                        .map(|x| x.size)
+                        .sum::<u64>(),
+                    "found more granular chunks"
+                );
+
+                Ok(Some(stat_blob_response.chunks))
+            }
+            Err(object_store::Error::NotFound { .. }) => {
+                // If there's only a chunk, we must return the empty vec here, rather than None.
+                match self
+                    .object_store
+                    .head(&derive_chunk_path(&self.base_path, digest))
+                    .await
+                {
+                    Ok(_) => {
+                        // present, but no more chunks available
+                        debug!("found a single chunk");
+                        Ok(Some(vec![]))
+                    }
+                    Err(object_store::Error::NotFound { .. }) => {
+                        // Neither blob nor single chunk found
+                        debug!("not found");
+                        Ok(None)
+                    }
+                    // error checking for chunk
+                    Err(e) => Err(e.into()),
+                }
+            }
+            // error checking for blob
+            Err(err) => Err(err.into()),
+        }
+    }
+}
+
+/// Reads blob contents from a AsyncRead, chunks and uploads them.
+/// On success, returns a [StatBlobResponse] pointing to the individual chunks.
+#[instrument(skip_all, fields(base_path=%base_path, min_chunk_size, avg_chunk_size, max_chunk_size), err)]
+async fn chunk_and_upload<R: AsyncRead + Unpin>(
+    r: R,
+    object_store: Arc<dyn ObjectStore>,
+    base_path: Path,
+    min_chunk_size: u32,
+    avg_chunk_size: u32,
+    max_chunk_size: u32,
+) -> io::Result<B3Digest> {
+    // wrap reader with something calculating the blake3 hash of all data read.
+    let mut b3_r = B3HashingReader::from(r);
+    // set up a fastcdc chunker
+    let mut chunker =
+        AsyncStreamCDC::new(&mut b3_r, min_chunk_size, avg_chunk_size, max_chunk_size);
+
+    /// This really should just belong into the closure at
+    /// `chunker.as_stream().then(|_| { … })``, but if we try to, rustc spits
+    /// higher-ranked lifetime errors at us.
+    async fn fastcdc_chunk_uploader(
+        resp: Result<fastcdc::v2020::ChunkData, fastcdc::v2020::Error>,
+        base_path: Path,
+        object_store: Arc<dyn ObjectStore>,
+    ) -> std::io::Result<ChunkMeta> {
+        let chunk_data = resp?;
+        let chunk_digest: B3Digest = blake3::hash(&chunk_data.data).as_bytes().into();
+        let chunk_path = derive_chunk_path(&base_path, &chunk_digest);
+
+        upload_chunk(object_store, chunk_digest, chunk_path, chunk_data.data).await
+    }
+
+    // Use the fastcdc chunker to produce a stream of chunks, and upload these
+    // that don't exist to the backend.
+    let chunks = chunker
+        .as_stream()
+        .then(|resp| fastcdc_chunk_uploader(resp, base_path.clone(), object_store.clone()))
+        .collect::<io::Result<Vec<ChunkMeta>>>()
+        .await?;
+
+    let stat_blob_response = StatBlobResponse {
+        chunks,
+        bao: "".into(), // still todo
+    };
+
+    // check for Blob, if it doesn't exist, persist.
+    let blob_digest: B3Digest = b3_r.digest().into();
+    let blob_path = derive_blob_path(&base_path, &blob_digest);
+
+    match object_store.head(&blob_path).await {
+        // blob already exists, nothing to do
+        Ok(_) => {
+            trace!(
+                blob.digest = %blob_digest,
+                blob.path = %blob_path,
+                "blob already exists on backend"
+            );
+        }
+        // chunk does not yet exist, upload first
+        Err(object_store::Error::NotFound { .. }) => {
+            debug!(
+                blob.digest = %blob_digest,
+                blob.path = %blob_path,
+                "uploading blob"
+            );
+            object_store
+                .put(&blob_path, stat_blob_response.encode_to_vec().into())
+                .await?;
+        }
+        Err(err) => {
+            // other error
+            Err(err)?
+        }
+    }
+
+    Ok(blob_digest)
+}
+
+/// upload chunk if it doesn't exist yet.
+#[instrument(skip_all, fields(chunk.digest = %chunk_digest, chunk.size = chunk_data.len(), chunk.path = %chunk_path), err)]
+async fn upload_chunk(
+    object_store: Arc<dyn ObjectStore>,
+    chunk_digest: B3Digest,
+    chunk_path: Path,
+    chunk_data: Vec<u8>,
+) -> std::io::Result<ChunkMeta> {
+    let chunk_size = chunk_data.len();
+    match object_store.head(&chunk_path).await {
+        // chunk already exists, nothing to do
+        Ok(_) => {
+            debug!("chunk already exists");
+        }
+
+        // chunk does not yet exist, compress and upload.
+        Err(object_store::Error::NotFound { .. }) => {
+            let chunk_data_compressed =
+                zstd::encode_all(Cursor::new(chunk_data), zstd::DEFAULT_COMPRESSION_LEVEL)?;
+
+            debug!(chunk.compressed_size=%chunk_data_compressed.len(), "uploading chunk");
+
+            object_store
+                .as_ref()
+                .put(&chunk_path, chunk_data_compressed.into())
+                .await?;
+        }
+        // other error
+        Err(err) => Err(err)?,
+    }
+
+    Ok(ChunkMeta {
+        digest: chunk_digest.into(),
+        size: chunk_size as u64,
+    })
+}
+
+pin_project! {
+    /// Takes care of blob uploads.
+    /// All writes are relayed to self.writer, and we continuously poll the
+    /// future (which will internally read from the other side of the pipe and
+    /// upload chunks).
+    /// Our BlobWriter::close() needs to drop self.writer, so the other side
+    /// will read EOF and can finalize the blob.
+    /// The future should then resolve and return the blob digest.
+    pub struct ObjectStoreBlobWriter<W, Fut>
+    where
+        W: AsyncWrite,
+        Fut: Future,
+    {
+        #[pin]
+        writer: Option<W>,
+
+        #[pin]
+        fut: Option<Fut>,
+
+        fut_output: Option<io::Result<B3Digest>>
+    }
+}
+
+impl<W, Fut> tokio::io::AsyncWrite for ObjectStoreBlobWriter<W, Fut>
+where
+    W: AsyncWrite + Send + Unpin,
+    Fut: Future,
+{
+    fn poll_write(
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+        buf: &[u8],
+    ) -> std::task::Poll<Result<usize, io::Error>> {
+        let this = self.project();
+        // poll the future.
+        let fut = this.fut.as_pin_mut().expect("not future");
+        let fut_p = fut.poll(cx);
+        // if it's ready, the only way this could have happened is that the
+        // upload failed, because we're only closing `self.writer` after all
+        // writes happened.
+        if fut_p.is_ready() {
+            return Poll::Ready(Err(io::Error::other("upload failed")));
+        }
+
+        // write to the underlying writer
+        this.writer
+            .as_pin_mut()
+            .expect("writer must be some")
+            .poll_write(cx, buf)
+    }
+
+    fn poll_flush(
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Result<(), io::Error>> {
+        let this = self.project();
+        // poll the future.
+        let fut = this.fut.as_pin_mut().expect("not future");
+        let fut_p = fut.poll(cx);
+        // if it's ready, the only way this could have happened is that the
+        // upload failed, because we're only closing `self.writer` after all
+        // writes happened.
+        if fut_p.is_ready() {
+            return Poll::Ready(Err(io::Error::other("upload failed")));
+        }
+
+        // Call poll_flush on the writer
+        this.writer
+            .as_pin_mut()
+            .expect("writer must be some")
+            .poll_flush(cx)
+    }
+
+    fn poll_shutdown(
+        self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Result<(), io::Error>> {
+        // There's nothing to do on shutdown. We might have written some chunks
+        // that are nowhere else referenced, but cleaning them up here would be racy.
+        std::task::Poll::Ready(Ok(()))
+    }
+}
+
+#[async_trait]
+impl<W, Fut> BlobWriter for ObjectStoreBlobWriter<W, Fut>
+where
+    W: AsyncWrite + Send + Unpin,
+    Fut: Future<Output = io::Result<B3Digest>> + Send + Unpin,
+{
+    async fn close(&mut self) -> io::Result<B3Digest> {
+        match self.writer.take() {
+            Some(mut writer) => {
+                // shut down the writer, so the other side will read EOF.
+                writer.shutdown().await?;
+
+                // take out the future.
+                let fut = self.fut.take().expect("fut must be some");
+                // await it.
+                let resp = pin!(fut).await;
+
+                match resp.as_ref() {
+                    // In the case of an Ok value, we store it in self.fut_output,
+                    // so future calls to close can return that.
+                    Ok(b3_digest) => {
+                        self.fut_output = Some(Ok(b3_digest.clone()));
+                    }
+                    Err(e) => {
+                        // for the error type, we need to cheat a bit, as
+                        // they're not clone-able.
+                        // Simply store a sloppy clone, with the same ErrorKind and message there.
+                        self.fut_output = Some(Err(std::io::Error::new(e.kind(), e.to_string())))
+                    }
+                }
+                resp
+            }
+            None => {
+                // called a second time, return self.fut_output.
+                match self.fut_output.as_ref().unwrap() {
+                    Ok(ref b3_digest) => Ok(b3_digest.clone()),
+                    Err(e) => Err(std::io::Error::new(e.kind(), e.to_string())),
+                }
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::chunk_and_upload;
+    use crate::{
+        blobservice::{BlobService, ObjectStoreBlobService},
+        fixtures::{BLOB_A, BLOB_A_DIGEST},
+    };
+    use std::{io::Cursor, sync::Arc};
+    use url::Url;
+
+    /// Tests chunk_and_upload directly, bypassing the BlobWriter at open_write().
+    #[tokio::test]
+    async fn test_chunk_and_upload() {
+        let blobsvc = Arc::new(
+            ObjectStoreBlobService::parse_url(&Url::parse("memory:///").unwrap()).unwrap(),
+        );
+
+        let blob_digest = chunk_and_upload(
+            &mut Cursor::new(BLOB_A.to_vec()),
+            blobsvc.object_store.clone(),
+            object_store::path::Path::from("/"),
+            1024 / 2,
+            1024,
+            1024 * 2,
+        )
+        .await
+        .expect("chunk_and_upload succeeds");
+
+        assert_eq!(BLOB_A_DIGEST.clone(), blob_digest);
+
+        // Now we should have the blob
+        assert!(blobsvc.has(&BLOB_A_DIGEST).await.unwrap());
+    }
+}
diff --git a/tvix/castore/src/blobservice/sled.rs b/tvix/castore/src/blobservice/sled.rs
new file mode 100644
index 0000000000..3dd4bff7bc
--- /dev/null
+++ b/tvix/castore/src/blobservice/sled.rs
@@ -0,0 +1,150 @@
+use super::{BlobReader, BlobService, BlobWriter};
+use crate::{B3Digest, Error};
+use std::{
+    io::{self, Cursor, Write},
+    path::Path,
+    task::Poll,
+};
+use tonic::async_trait;
+use tracing::instrument;
+
+#[derive(Clone)]
+pub struct SledBlobService {
+    db: sled::Db,
+}
+
+impl SledBlobService {
+    pub fn new<P: AsRef<Path>>(p: P) -> Result<Self, sled::Error> {
+        let config = sled::Config::default()
+            .use_compression(false) // is a required parameter
+            .path(p);
+        let db = config.open()?;
+
+        Ok(Self { db })
+    }
+
+    pub fn new_temporary() -> Result<Self, sled::Error> {
+        let config = sled::Config::default().temporary(true);
+        let db = config.open()?;
+
+        Ok(Self { db })
+    }
+}
+
+#[async_trait]
+impl BlobService for SledBlobService {
+    #[instrument(skip(self), fields(blob.digest=%digest))]
+    async fn has(&self, digest: &B3Digest) -> io::Result<bool> {
+        match self.db.contains_key(digest.as_slice()) {
+            Ok(has) => Ok(has),
+            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
+        }
+    }
+
+    #[instrument(skip(self), fields(blob.digest=%digest))]
+    async fn open_read(&self, digest: &B3Digest) -> io::Result<Option<Box<dyn BlobReader>>> {
+        match self.db.get(digest.as_slice()) {
+            Ok(None) => Ok(None),
+            Ok(Some(data)) => Ok(Some(Box::new(Cursor::new(data[..].to_vec())))),
+            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
+        }
+    }
+
+    #[instrument(skip(self))]
+    async fn open_write(&self) -> Box<dyn BlobWriter> {
+        Box::new(SledBlobWriter::new(self.db.clone()))
+    }
+}
+
+pub struct SledBlobWriter {
+    db: sled::Db,
+
+    /// Contains the buffer Vec and hasher, or None if already closed
+    writers: Option<(Vec<u8>, blake3::Hasher)>,
+
+    /// The digest that has been returned, if we successfully closed.
+    digest: Option<B3Digest>,
+}
+
+impl SledBlobWriter {
+    pub fn new(db: sled::Db) -> Self {
+        Self {
+            db,
+            writers: Some((Vec::new(), blake3::Hasher::new())),
+            digest: None,
+        }
+    }
+}
+
+impl tokio::io::AsyncWrite for SledBlobWriter {
+    fn poll_write(
+        mut self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+        b: &[u8],
+    ) -> std::task::Poll<Result<usize, io::Error>> {
+        Poll::Ready(match &mut self.writers {
+            None => Err(io::Error::new(
+                io::ErrorKind::NotConnected,
+                "already closed",
+            )),
+            Some((ref mut buf, ref mut hasher)) => {
+                let bytes_written = buf.write(b)?;
+                hasher.write(&b[..bytes_written])
+            }
+        })
+    }
+
+    fn poll_flush(
+        mut self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Result<(), io::Error>> {
+        Poll::Ready(match &mut self.writers {
+            None => Err(io::Error::new(
+                io::ErrorKind::NotConnected,
+                "already closed",
+            )),
+            Some(_) => Ok(()),
+        })
+    }
+
+    fn poll_shutdown(
+        self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Result<(), io::Error>> {
+        // shutdown is "instantaneous", we only write to a Vec<u8> as buffer.
+        Poll::Ready(Ok(()))
+    }
+}
+
+#[async_trait]
+impl BlobWriter for SledBlobWriter {
+    async fn close(&mut self) -> io::Result<B3Digest> {
+        if self.writers.is_none() {
+            match &self.digest {
+                Some(digest) => Ok(digest.clone()),
+                None => Err(io::Error::new(
+                    io::ErrorKind::NotConnected,
+                    "already closed",
+                )),
+            }
+        } else {
+            let (buf, hasher) = self.writers.take().unwrap();
+
+            let digest: B3Digest = hasher.finalize().as_bytes().into();
+
+            // Only insert if the blob doesn't already exist.
+            if !self.db.contains_key(digest.as_slice()).map_err(|e| {
+                Error::StorageError(format!("Unable to check if we have blob {}: {}", digest, e))
+            })? {
+                // put buf in there. This will move buf out.
+                self.db
+                    .insert(digest.as_slice(), buf)
+                    .map_err(|e| Error::StorageError(format!("unable to insert blob: {}", e)))?;
+            }
+
+            self.digest = Some(digest.clone());
+
+            Ok(digest)
+        }
+    }
+}
diff --git a/tvix/castore/src/blobservice/tests/mod.rs b/tvix/castore/src/blobservice/tests/mod.rs
new file mode 100644
index 0000000000..30c4e97634
--- /dev/null
+++ b/tvix/castore/src/blobservice/tests/mod.rs
@@ -0,0 +1,254 @@
+//! This contains test scenarios that a given [BlobService] needs to pass.
+//! We use [rstest] and [rstest_reuse] to provide all services we want to test
+//! against, and then apply this template to all test functions.
+
+use rstest::*;
+use rstest_reuse::{self, *};
+use std::io;
+use tokio::io::AsyncReadExt;
+use tokio::io::AsyncSeekExt;
+
+use super::BlobService;
+use crate::blobservice;
+use crate::fixtures::BLOB_A;
+use crate::fixtures::BLOB_A_DIGEST;
+use crate::fixtures::BLOB_B;
+use crate::fixtures::BLOB_B_DIGEST;
+
+mod utils;
+use self::utils::make_grpc_blob_service_client;
+
+/// This produces a template, which will be applied to all individual test functions.
+/// See https://github.com/la10736/rstest/issues/130#issuecomment-968864832
+#[template]
+#[rstest]
+#[case::grpc(make_grpc_blob_service_client().await)]
+#[case::memory(blobservice::from_addr("memory://").await.unwrap())]
+#[case::objectstore_memory(blobservice::from_addr("objectstore+memory://").await.unwrap())]
+#[case::sled(blobservice::from_addr("sled://").await.unwrap())]
+pub fn blob_services(#[case] blob_service: impl BlobService) {}
+
+/// Using [BlobService::has] on a non-existing blob should return false.
+#[apply(blob_services)]
+#[tokio::test]
+async fn has_nonexistent_false(blob_service: impl BlobService) {
+    assert!(!blob_service
+        .has(&BLOB_A_DIGEST)
+        .await
+        .expect("must not fail"));
+}
+
+/// Using [BlobService::chunks] on a non-existing blob should return Ok(None)
+#[apply(blob_services)]
+#[tokio::test]
+async fn chunks_nonexistent_false(blob_service: impl BlobService) {
+    assert!(blob_service
+        .chunks(&BLOB_A_DIGEST)
+        .await
+        .expect("must be ok")
+        .is_none());
+}
+
+// TODO: do tests with `chunks`
+
+/// Trying to read a non-existing blob should return a None instead of a reader.
+#[apply(blob_services)]
+#[tokio::test]
+async fn not_found_read(blob_service: impl BlobService) {
+    assert!(blob_service
+        .open_read(&BLOB_A_DIGEST)
+        .await
+        .expect("must not fail")
+        .is_none())
+}
+
+/// Put a blob in the store, check has, get it back.
+#[apply(blob_services)]
+// #[case::small(&fixtures::BLOB_A, &fixtures::BLOB_A_DIGEST)]
+// #[case::big(&fixtures::BLOB_B, &fixtures::BLOB_B_DIGEST)]
+#[tokio::test]
+async fn put_has_get(blob_service: impl BlobService) {
+    // TODO: figure out how to instantiate this with BLOB_A and BLOB_B, as two separate cases
+    for (blob_contents, blob_digest) in &[
+        (&*BLOB_A, BLOB_A_DIGEST.clone()),
+        (&*BLOB_B, BLOB_B_DIGEST.clone()),
+    ] {
+        let mut w = blob_service.open_write().await;
+
+        let l = tokio::io::copy(&mut io::Cursor::new(blob_contents), &mut w)
+            .await
+            .expect("copy must succeed");
+        assert_eq!(
+            blob_contents.len(),
+            l as usize,
+            "written bytes must match blob length"
+        );
+
+        let digest = w.close().await.expect("close must succeed");
+
+        assert_eq!(*blob_digest, digest, "returned digest must be correct");
+
+        assert!(
+            blob_service.has(blob_digest).await.expect("must not fail"),
+            "blob service should now have the blob"
+        );
+
+        let mut r = blob_service
+            .open_read(blob_digest)
+            .await
+            .expect("open_read must succeed")
+            .expect("must be some");
+
+        let mut buf: Vec<u8> = Vec::new();
+        let mut pinned_reader = std::pin::pin!(r);
+        let l = tokio::io::copy(&mut pinned_reader, &mut buf)
+            .await
+            .expect("copy must succeed");
+
+        assert_eq!(
+            blob_contents.len(),
+            l as usize,
+            "read bytes must match blob length"
+        );
+
+        assert_eq!(&blob_contents[..], &buf, "read blob contents must match");
+    }
+}
+
+/// Put a blob in the store, and seek inside it a bit.
+#[apply(blob_services)]
+#[tokio::test]
+async fn put_seek(blob_service: impl BlobService) {
+    let mut w = blob_service.open_write().await;
+
+    tokio::io::copy(&mut io::Cursor::new(&BLOB_B.to_vec()), &mut w)
+        .await
+        .expect("copy must succeed");
+    w.close().await.expect("close must succeed");
+
+    // open a blob for reading
+    let mut r = blob_service
+        .open_read(&BLOB_B_DIGEST)
+        .await
+        .expect("open_read must succeed")
+        .expect("must be some");
+
+    let mut pos: u64 = 0;
+
+    // read the first 10 bytes, they must match the data in the fixture.
+    {
+        let mut buf = [0; 10];
+        r.read_exact(&mut buf).await.expect("must succeed");
+
+        assert_eq!(
+            &BLOB_B[pos as usize..pos as usize + buf.len()],
+            buf,
+            "expected first 10 bytes to match"
+        );
+
+        pos += buf.len() as u64;
+    }
+    // seek by 0 bytes, using SeekFrom::Start.
+    let p = r
+        .seek(io::SeekFrom::Start(pos))
+        .await
+        .expect("must not fail");
+    assert_eq!(pos, p);
+
+    // read the next 10 bytes, they must match the data in the fixture.
+    {
+        let mut buf = [0; 10];
+        r.read_exact(&mut buf).await.expect("must succeed");
+
+        assert_eq!(
+            &BLOB_B[pos as usize..pos as usize + buf.len()],
+            buf,
+            "expected data to match"
+        );
+
+        pos += buf.len() as u64;
+    }
+
+    // seek by 5 bytes, using SeekFrom::Start.
+    let p = r
+        .seek(io::SeekFrom::Start(pos + 5))
+        .await
+        .expect("must not fail");
+    pos += 5;
+    assert_eq!(pos, p);
+
+    // read the next 10 bytes, they must match the data in the fixture.
+    {
+        let mut buf = [0; 10];
+        r.read_exact(&mut buf).await.expect("must succeed");
+
+        assert_eq!(
+            &BLOB_B[pos as usize..pos as usize + buf.len()],
+            buf,
+            "expected data to match"
+        );
+
+        pos += buf.len() as u64;
+    }
+
+    // seek by 12345 bytes, using SeekFrom::
+    let p = r
+        .seek(io::SeekFrom::Current(12345))
+        .await
+        .expect("must not fail");
+    pos += 12345;
+    assert_eq!(pos, p);
+
+    // read the next 10 bytes, they must match the data in the fixture.
+    {
+        let mut buf = [0; 10];
+        r.read_exact(&mut buf).await.expect("must succeed");
+
+        assert_eq!(
+            &BLOB_B[pos as usize..pos as usize + buf.len()],
+            buf,
+            "expected data to match"
+        );
+
+        #[allow(unused_assignments)]
+        {
+            pos += buf.len() as u64;
+        }
+    }
+
+    // seeking to the end is okay…
+    let p = r
+        .seek(io::SeekFrom::Start(BLOB_B.len() as u64))
+        .await
+        .expect("must not fail");
+    pos = BLOB_B.len() as u64;
+    assert_eq!(pos, p);
+
+    {
+        // but it returns no more data.
+        let mut buf: Vec<u8> = Vec::new();
+        r.read_to_end(&mut buf).await.expect("must not fail");
+        assert!(buf.is_empty(), "expected no more data to be read");
+    }
+
+    // seeking past the end…
+    // should either be ok, but then return 0 bytes.
+    // this matches the behaviour or a Cursor<Vec<u8>>.
+    if let Ok(_pos) = r.seek(io::SeekFrom::Start(BLOB_B.len() as u64 + 1)).await {
+        let mut buf: Vec<u8> = Vec::new();
+        r.read_to_end(&mut buf).await.expect("must not fail");
+        assert!(buf.is_empty(), "expected no more data to be read");
+    }
+    // or not be okay.
+
+    // TODO: this is only broken for the gRPC version
+    // We expect seeking backwards or relative to the end to fail.
+    // r.seek(io::SeekFrom::Current(-1))
+    //     .expect_err("SeekFrom::Current(-1) expected to fail");
+
+    // r.seek(io::SeekFrom::Start(pos - 1))
+    //     .expect_err("SeekFrom::Start(pos-1) expected to fail");
+
+    // r.seek(io::SeekFrom::End(0))
+    //     .expect_err("SeekFrom::End(_) expected to fail");
+}
diff --git a/tvix/castore/src/blobservice/tests/utils.rs b/tvix/castore/src/blobservice/tests/utils.rs
new file mode 100644
index 0000000000..706c4b5e43
--- /dev/null
+++ b/tvix/castore/src/blobservice/tests/utils.rs
@@ -0,0 +1,41 @@
+use crate::blobservice::{BlobService, MemoryBlobService};
+use crate::proto::blob_service_client::BlobServiceClient;
+use crate::proto::GRPCBlobServiceWrapper;
+use crate::{blobservice::GRPCBlobService, proto::blob_service_server::BlobServiceServer};
+use tonic::transport::{Endpoint, Server, Uri};
+
+/// Constructs and returns a gRPC BlobService.
+/// The server part is a [MemoryBlobService], exposed via the
+/// [GRPCBlobServiceWrapper], and connected through a DuplexStream
+pub async fn make_grpc_blob_service_client() -> Box<dyn BlobService> {
+    let (left, right) = tokio::io::duplex(64);
+
+    // spin up a server, which will only connect once, to the left side.
+    tokio::spawn(async {
+        let blob_service = Box::<MemoryBlobService>::default() as Box<dyn BlobService>;
+
+        // spin up a new DirectoryService
+        let mut server = Server::builder();
+        let router = server.add_service(BlobServiceServer::new(GRPCBlobServiceWrapper::new(
+            blob_service,
+        )));
+
+        router
+            .serve_with_incoming(tokio_stream::once(Ok::<_, std::io::Error>(left)))
+            .await
+    });
+
+    // Create a client, connecting to the right side. The URI is unused.
+    let mut maybe_right = Some(right);
+
+    Box::new(GRPCBlobService::from_client(BlobServiceClient::new(
+        Endpoint::try_from("http://[::]:50051")
+            .unwrap()
+            .connect_with_connector(tower::service_fn(move |_: Uri| {
+                let right = maybe_right.take().unwrap();
+                async move { Ok::<_, std::io::Error>(right) }
+            }))
+            .await
+            .unwrap(),
+    )))
+}
diff --git a/tvix/castore/src/digests.rs b/tvix/castore/src/digests.rs
new file mode 100644
index 0000000000..2311c95c4d
--- /dev/null
+++ b/tvix/castore/src/digests.rs
@@ -0,0 +1,86 @@
+use bytes::Bytes;
+use data_encoding::BASE64;
+use thiserror::Error;
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct B3Digest(Bytes);
+
+// TODO: allow converting these errors to crate::Error
+#[derive(Error, Debug)]
+pub enum Error {
+    #[error("invalid digest length: {0}")]
+    InvalidDigestLen(usize),
+}
+
+pub const B3_LEN: usize = 32;
+
+impl B3Digest {
+    pub fn as_slice(&self) -> &[u8] {
+        &self.0[..]
+    }
+}
+
+impl From<B3Digest> for bytes::Bytes {
+    fn from(val: B3Digest) -> Self {
+        val.0
+    }
+}
+
+impl From<digest::Output<blake3::Hasher>> for B3Digest {
+    fn from(value: digest::Output<blake3::Hasher>) -> Self {
+        let v = Into::<[u8; B3_LEN]>::into(value);
+        Self(Bytes::copy_from_slice(&v))
+    }
+}
+
+impl TryFrom<Vec<u8>> for B3Digest {
+    type Error = Error;
+
+    // constructs a [B3Digest] from a [Vec<u8>].
+    // Returns an error if the digest has the wrong length.
+    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
+        if value.len() != B3_LEN {
+            Err(Error::InvalidDigestLen(value.len()))
+        } else {
+            Ok(Self(value.into()))
+        }
+    }
+}
+
+impl TryFrom<bytes::Bytes> for B3Digest {
+    type Error = Error;
+
+    // constructs a [B3Digest] from a [bytes::Bytes].
+    // Returns an error if the digest has the wrong length.
+    fn try_from(value: bytes::Bytes) -> Result<Self, Self::Error> {
+        if value.len() != B3_LEN {
+            Err(Error::InvalidDigestLen(value.len()))
+        } else {
+            Ok(Self(value))
+        }
+    }
+}
+
+impl From<&[u8; B3_LEN]> for B3Digest {
+    fn from(value: &[u8; B3_LEN]) -> Self {
+        Self(value.to_vec().into())
+    }
+}
+
+impl Clone for B3Digest {
+    fn clone(&self) -> Self {
+        Self(self.0.to_owned())
+    }
+}
+
+impl std::fmt::Display for B3Digest {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "b3:{}", BASE64.encode(&self.0))
+    }
+}
+
+impl std::fmt::Debug for B3Digest {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "b3:{}", BASE64.encode(&self.0))
+    }
+}
diff --git a/tvix/castore/src/directoryservice/bigtable.rs b/tvix/castore/src/directoryservice/bigtable.rs
new file mode 100644
index 0000000000..0fdb24628f
--- /dev/null
+++ b/tvix/castore/src/directoryservice/bigtable.rs
@@ -0,0 +1,357 @@
+use bigtable_rs::{bigtable, google::bigtable::v2 as bigtable_v2};
+use bytes::Bytes;
+use data_encoding::HEXLOWER;
+use futures::stream::BoxStream;
+use prost::Message;
+use serde::{Deserialize, Serialize};
+use serde_with::{serde_as, DurationSeconds};
+use tonic::async_trait;
+use tracing::{instrument, trace, warn};
+
+use super::{utils::traverse_directory, DirectoryPutter, DirectoryService, SimplePutter};
+use crate::{proto, B3Digest, Error};
+
+/// There should not be more than 10 MiB in a single cell.
+/// https://cloud.google.com/bigtable/docs/schema-design#cells
+const CELL_SIZE_LIMIT: u64 = 10 * 1024 * 1024;
+
+/// Provides a [DirectoryService] implementation using
+/// [Bigtable](https://cloud.google.com/bigtable/docs/)
+/// as an underlying K/V store.
+///
+/// # Data format
+/// We use Bigtable as a plain K/V store.
+/// The row key is the digest of the directory, in hexlower.
+/// Inside the row, we currently have a single column/cell, again using the
+/// hexlower directory digest.
+/// Its value is the Directory message, serialized in canonical protobuf.
+/// We currently only populate this column.
+///
+/// In the future, we might want to introduce "bucketing", essentially storing
+/// all directories inserted via `put_multiple_start` in a batched form.
+/// This will prevent looking up intermediate Directories, which are not
+/// directly at the root, so rely on store composition.
+#[derive(Clone)]
+pub struct BigtableDirectoryService {
+    client: bigtable::BigTable,
+    params: BigtableParameters,
+
+    #[cfg(test)]
+    #[allow(dead_code)]
+    /// Holds the temporary directory containing the unix socket, and the
+    /// spawned emulator process.
+    emulator: std::sync::Arc<(tempfile::TempDir, async_process::Child)>,
+}
+
+/// Represents configuration of [BigtableDirectoryService].
+/// This currently conflates both connect parameters and data model/client
+/// behaviour parameters.
+#[serde_as]
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub struct BigtableParameters {
+    project_id: String,
+    instance_name: String,
+    #[serde(default)]
+    is_read_only: bool,
+    #[serde(default = "default_channel_size")]
+    channel_size: usize,
+
+    #[serde_as(as = "Option<DurationSeconds<String>>")]
+    #[serde(default = "default_timeout")]
+    timeout: Option<std::time::Duration>,
+    table_name: String,
+    family_name: String,
+
+    #[serde(default = "default_app_profile_id")]
+    app_profile_id: String,
+}
+
+fn default_app_profile_id() -> String {
+    "default".to_owned()
+}
+
+fn default_channel_size() -> usize {
+    4
+}
+
+fn default_timeout() -> Option<std::time::Duration> {
+    Some(std::time::Duration::from_secs(4))
+}
+
+impl BigtableDirectoryService {
+    #[cfg(not(test))]
+    pub async fn connect(params: BigtableParameters) -> Result<Self, bigtable::Error> {
+        let connection = bigtable::BigTableConnection::new(
+            &params.project_id,
+            &params.instance_name,
+            params.is_read_only,
+            params.channel_size,
+            params.timeout,
+        )
+        .await?;
+
+        Ok(Self {
+            client: connection.client(),
+            params,
+        })
+    }
+
+    #[cfg(test)]
+    pub async fn connect(params: BigtableParameters) -> Result<Self, bigtable::Error> {
+        use std::time::Duration;
+
+        use async_process::{Command, Stdio};
+        use tempfile::TempDir;
+        use tokio_retry::{strategy::ExponentialBackoff, Retry};
+
+        let tmpdir = TempDir::new().unwrap();
+
+        let socket_path = tmpdir.path().join("cbtemulator.sock");
+
+        let emulator_process = Command::new("cbtemulator")
+            .arg("-address")
+            .arg(socket_path.clone())
+            .stderr(Stdio::piped())
+            .stdout(Stdio::piped())
+            .kill_on_drop(true)
+            .spawn()
+            .expect("failed to spawn emulator");
+
+        Retry::spawn(
+            ExponentialBackoff::from_millis(20)
+                .max_delay(Duration::from_secs(1))
+                .take(3),
+            || async {
+                if socket_path.exists() {
+                    Ok(())
+                } else {
+                    Err(())
+                }
+            },
+        )
+        .await
+        .expect("failed to wait for socket");
+
+        // populate the emulator
+        for cmd in &[
+            vec!["createtable", &params.table_name],
+            vec!["createfamily", &params.table_name, &params.family_name],
+        ] {
+            Command::new("cbt")
+                .args({
+                    let mut args = vec![
+                        "-instance",
+                        &params.instance_name,
+                        "-project",
+                        &params.project_id,
+                    ];
+                    args.extend_from_slice(cmd);
+                    args
+                })
+                .env(
+                    "BIGTABLE_EMULATOR_HOST",
+                    format!("unix://{}", socket_path.to_string_lossy()),
+                )
+                .output()
+                .await
+                .expect("failed to run cbt setup command");
+        }
+
+        let connection = bigtable_rs::bigtable::BigTableConnection::new_with_emulator(
+            &format!("unix://{}", socket_path.to_string_lossy()),
+            &params.project_id,
+            &params.instance_name,
+            params.is_read_only,
+            params.timeout,
+        )?;
+
+        Ok(Self {
+            client: connection.client(),
+            params,
+            emulator: (tmpdir, emulator_process).into(),
+        })
+    }
+}
+
+/// Derives the row/column key for a given blake3 digest.
+/// We use hexlower encoding, also because it can't be misinterpreted as RE2.
+fn derive_directory_key(digest: &B3Digest) -> String {
+    HEXLOWER.encode(digest.as_slice())
+}
+
+#[async_trait]
+impl DirectoryService for BigtableDirectoryService {
+    #[instrument(skip(self, digest), err, fields(directory.digest = %digest))]
+    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+        let mut client = self.client.clone();
+        let directory_key = derive_directory_key(digest);
+
+        let request = bigtable_v2::ReadRowsRequest {
+            app_profile_id: self.params.app_profile_id.to_string(),
+            table_name: client.get_full_table_name(&self.params.table_name),
+            rows_limit: 1,
+            rows: Some(bigtable_v2::RowSet {
+                row_keys: vec![directory_key.clone().into()],
+                row_ranges: vec![],
+            }),
+            // Filter selected family name, and column qualifier matching our digest.
+            // This is to ensure we don't fail once we start bucketing.
+            filter: Some(bigtable_v2::RowFilter {
+                filter: Some(bigtable_v2::row_filter::Filter::Chain(
+                    bigtable_v2::row_filter::Chain {
+                        filters: vec![
+                            bigtable_v2::RowFilter {
+                                filter: Some(
+                                    bigtable_v2::row_filter::Filter::FamilyNameRegexFilter(
+                                        self.params.family_name.to_string(),
+                                    ),
+                                ),
+                            },
+                            bigtable_v2::RowFilter {
+                                filter: Some(
+                                    bigtable_v2::row_filter::Filter::ColumnQualifierRegexFilter(
+                                        directory_key.clone().into(),
+                                    ),
+                                ),
+                            },
+                        ],
+                    },
+                )),
+            }),
+            ..Default::default()
+        };
+
+        let mut response = client
+            .read_rows(request)
+            .await
+            .map_err(|e| Error::StorageError(format!("unable to read rows: {}", e)))?;
+
+        if response.len() != 1 {
+            if response.len() > 1 {
+                // This shouldn't happen, we limit number of rows to 1
+                return Err(Error::StorageError(
+                    "got more than one row from bigtable".into(),
+                ));
+            }
+            // else, this is simply a "not found".
+            return Ok(None);
+        }
+
+        let (row_key, mut row_cells) = response.pop().unwrap();
+        if row_key != directory_key.as_bytes() {
+            // This shouldn't happen, we requested this row key.
+            return Err(Error::StorageError(
+                "got wrong row key from bigtable".into(),
+            ));
+        }
+
+        let row_cell = row_cells
+            .pop()
+            .ok_or_else(|| Error::StorageError("found no cells".into()))?;
+
+        // Ensure there's only one cell (so no more left after the pop())
+        // This shouldn't happen, We filter out other cells in our query.
+        if !row_cells.is_empty() {
+            return Err(Error::StorageError(
+                "more than one cell returned from bigtable".into(),
+            ));
+        }
+
+        // We also require the qualifier to be correct in the filter above,
+        // so this shouldn't happen.
+        if directory_key.as_bytes() != row_cell.qualifier {
+            return Err(Error::StorageError("unexpected cell qualifier".into()));
+        }
+
+        // For the data in that cell, ensure the digest matches what's requested, before parsing.
+        let got_digest = B3Digest::from(blake3::hash(&row_cell.value).as_bytes());
+        if got_digest != *digest {
+            return Err(Error::StorageError(format!(
+                "invalid digest: {}",
+                got_digest
+            )));
+        }
+
+        // Try to parse the value into a Directory message.
+        let directory = proto::Directory::decode(Bytes::from(row_cell.value))
+            .map_err(|e| Error::StorageError(format!("unable to decode directory proto: {}", e)))?;
+
+        // validate the Directory.
+        directory
+            .validate()
+            .map_err(|e| Error::StorageError(format!("invalid Directory message: {}", e)))?;
+
+        Ok(Some(directory))
+    }
+
+    #[instrument(skip(self, directory), err, fields(directory.digest = %directory.digest()))]
+    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
+        let directory_digest = directory.digest();
+        let mut client = self.client.clone();
+        let directory_key = derive_directory_key(&directory_digest);
+
+        // Ensure the directory we're trying to upload passes validation
+        directory
+            .validate()
+            .map_err(|e| Error::InvalidRequest(format!("directory is invalid: {}", e)))?;
+
+        let data = directory.encode_to_vec();
+        if data.len() as u64 > CELL_SIZE_LIMIT {
+            return Err(Error::StorageError(
+                "Directory exceeds cell limit on Bigtable".into(),
+            ));
+        }
+
+        let resp = client
+            .check_and_mutate_row(bigtable_v2::CheckAndMutateRowRequest {
+                table_name: client.get_full_table_name(&self.params.table_name),
+                app_profile_id: self.params.app_profile_id.to_string(),
+                row_key: directory_key.clone().into(),
+                predicate_filter: Some(bigtable_v2::RowFilter {
+                    filter: Some(bigtable_v2::row_filter::Filter::ColumnQualifierRegexFilter(
+                        directory_key.clone().into(),
+                    )),
+                }),
+                // If the column was already found, do nothing.
+                true_mutations: vec![],
+                // Else, do the insert.
+                false_mutations: vec![
+                    // https://cloud.google.com/bigtable/docs/writes
+                    bigtable_v2::Mutation {
+                        mutation: Some(bigtable_v2::mutation::Mutation::SetCell(
+                            bigtable_v2::mutation::SetCell {
+                                family_name: self.params.family_name.to_string(),
+                                column_qualifier: directory_key.clone().into(),
+                                timestamp_micros: -1, // use server time to fill timestamp
+                                value: data,
+                            },
+                        )),
+                    },
+                ],
+            })
+            .await
+            .map_err(|e| Error::StorageError(format!("unable to mutate rows: {}", e)))?;
+
+        if resp.predicate_matched {
+            trace!("already existed")
+        }
+
+        Ok(directory_digest)
+    }
+
+    #[instrument(skip_all, fields(directory.digest = %root_directory_digest))]
+    fn get_recursive(
+        &self,
+        root_directory_digest: &B3Digest,
+    ) -> BoxStream<Result<proto::Directory, Error>> {
+        traverse_directory(self.clone(), root_directory_digest)
+    }
+
+    #[instrument(skip_all)]
+    fn put_multiple_start(&self) -> Box<(dyn DirectoryPutter + 'static)>
+    where
+        Self: Clone,
+    {
+        Box::new(SimplePutter::new(self.clone()))
+    }
+}
diff --git a/tvix/castore/src/directoryservice/closure_validator.rs b/tvix/castore/src/directoryservice/closure_validator.rs
new file mode 100644
index 0000000000..183928a86f
--- /dev/null
+++ b/tvix/castore/src/directoryservice/closure_validator.rs
@@ -0,0 +1,268 @@
+use std::collections::{HashMap, HashSet};
+
+use bstr::ByteSlice;
+
+use petgraph::{
+    graph::{DiGraph, NodeIndex},
+    visit::Bfs,
+};
+use tracing::instrument;
+
+use crate::{
+    proto::{self, Directory},
+    B3Digest, Error,
+};
+
+/// This can be used to validate a Directory closure (DAG of connected
+/// Directories), and their insertion order.
+///
+/// Directories need to be inserted (via `add`), in an order from the leaves to
+/// the root (DFS Post-Order).
+/// During insertion, We validate as much as we can at that time:
+///
+///  - individual validation of Directory messages
+///  - validation of insertion order (no upload of not-yet-known Directories)
+///  - validation of size fields of referred Directories
+///
+/// Internally it keeps all received Directories in a directed graph,
+/// with node weights being the Directories and edges pointing to child
+/// directories.
+///
+/// Once all Directories have been inserted, a finalize function can be
+/// called to get a (deduplicated and) validated list of directories, in
+/// insertion order.
+/// During finalize, a check for graph connectivity is performed too, to ensure
+/// there's no disconnected components, and only one root.
+#[derive(Default)]
+pub struct ClosureValidator {
+    // A directed graph, using Directory as node weight, without edge weights.
+    // Edges point from parents to children.
+    graph: DiGraph<Directory, ()>,
+
+    // A lookup table from directory digest to node index.
+    digest_to_node_ix: HashMap<B3Digest, NodeIndex>,
+
+    /// Keeps track of the last-inserted directory graph node index.
+    /// On a correct insert, this will be the root node, from which the DFS post
+    /// order traversal will start from.
+    last_directory_ix: Option<NodeIndex>,
+}
+
+impl ClosureValidator {
+    /// Insert a new Directory into the closure.
+    /// Perform individual Directory validation, validation of insertion order
+    /// and size fields.
+    #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest(), directory.size=%directory.size()), err)]
+    pub fn add(&mut self, directory: proto::Directory) -> Result<(), Error> {
+        let digest = directory.digest();
+
+        // If we already saw this node previously, it's already validated and in the graph.
+        if self.digest_to_node_ix.contains_key(&digest) {
+            return Ok(());
+        }
+
+        // Do some general validation
+        directory
+            .validate()
+            .map_err(|e| Error::InvalidRequest(e.to_string()))?;
+
+        // Ensure the directory only refers to directories which we already accepted.
+        // We lookup their node indices and add them to a HashSet.
+        let mut child_ixs = HashSet::new();
+        for dir in &directory.directories {
+            let child_digest = B3Digest::try_from(dir.digest.to_owned()).unwrap(); // validated
+
+            // Ensure the digest has already been seen
+            let child_ix = *self.digest_to_node_ix.get(&child_digest).ok_or_else(|| {
+                Error::InvalidRequest(format!(
+                    "'{}' refers to unseen child dir: {}",
+                    dir.name.as_bstr(),
+                    &child_digest
+                ))
+            })?;
+
+            // Ensure the size specified in the child node matches the directory size itself.
+            let recorded_child_size = self
+                .graph
+                .node_weight(child_ix)
+                .expect("node not found")
+                .size();
+
+            // Ensure the size specified in the child node matches our records.
+            if dir.size != recorded_child_size {
+                return Err(Error::InvalidRequest(format!(
+                    "'{}' has wrong size, specified {}, recorded {}",
+                    dir.name.as_bstr(),
+                    dir.size,
+                    recorded_child_size
+                )));
+            }
+
+            child_ixs.insert(child_ix);
+        }
+
+        // Insert node into the graph, and add edges to all children.
+        let node_ix = self.graph.add_node(directory);
+        for child_ix in child_ixs {
+            self.graph.add_edge(node_ix, child_ix, ());
+        }
+
+        // Record the mapping from digest to node_ix in our lookup table.
+        self.digest_to_node_ix.insert(digest, node_ix);
+
+        // Update last_directory_ix.
+        self.last_directory_ix = Some(node_ix);
+
+        Ok(())
+    }
+
+    /// Ensure that all inserted Directories are connected, then return a
+    /// (deduplicated) and validated list of directories, in from-leaves-to-root
+    /// order.
+    /// In case no elements have been inserted, returns an empty list.
+    #[instrument(level = "trace", skip_all, err)]
+    pub(crate) fn finalize(self) -> Result<Vec<Directory>, Error> {
+        // If no nodes were inserted, an empty list is returned.
+        let last_directory_ix = if let Some(x) = self.last_directory_ix {
+            x
+        } else {
+            return Ok(vec![]);
+        };
+
+        // do a BFS traversal of the graph, starting with the root node to get
+        // (the count of) all nodes reachable from there.
+        let mut traversal = Bfs::new(&self.graph, last_directory_ix);
+
+        let mut visited_directory_count = 0;
+        #[cfg(debug_assertions)]
+        let mut visited_directory_ixs = HashSet::new();
+        #[cfg_attr(not(debug_assertions), allow(unused))]
+        while let Some(directory_ix) = traversal.next(&self.graph) {
+            #[cfg(debug_assertions)]
+            visited_directory_ixs.insert(directory_ix);
+
+            visited_directory_count += 1;
+        }
+
+        // If the number of nodes collected equals the total number of nodes in
+        // the graph, we know all nodes are connected.
+        if visited_directory_count != self.graph.node_count() {
+            // more or less exhaustive error reporting.
+            #[cfg(debug_assertions)]
+            {
+                let all_directory_ixs: HashSet<_> = self.graph.node_indices().collect();
+
+                let unvisited_directories: HashSet<_> = all_directory_ixs
+                    .difference(&visited_directory_ixs)
+                    .map(|ix| self.graph.node_weight(*ix).expect("node not found"))
+                    .collect();
+
+                return Err(Error::InvalidRequest(format!(
+                    "found {} disconnected directories: {:?}",
+                    self.graph.node_count() - visited_directory_ixs.len(),
+                    unvisited_directories
+                )));
+            }
+            #[cfg(not(debug_assertions))]
+            {
+                return Err(Error::InvalidRequest(format!(
+                    "found {} disconnected directories",
+                    self.graph.node_count() - visited_directory_count
+                )));
+            }
+        }
+
+        // Dissolve the graph, returning the nodes as a Vec.
+        // As the graph was populated in a valid DFS PostOrder, we can return
+        // nodes in that same order.
+        let (nodes, _edges) = self.graph.into_nodes_edges();
+        Ok(nodes.into_iter().map(|x| x.weight).collect())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        fixtures::{DIRECTORY_A, DIRECTORY_B, DIRECTORY_C},
+        proto::{self, Directory},
+    };
+    use lazy_static::lazy_static;
+    use rstest::rstest;
+
+    lazy_static! {
+        pub static ref BROKEN_DIRECTORY : Directory = Directory {
+            symlinks: vec![proto::SymlinkNode {
+                name: "".into(), // invalid name!
+                target: "doesntmatter".into(),
+            }],
+            ..Default::default()
+        };
+
+        pub static ref BROKEN_PARENT_DIRECTORY: Directory = Directory {
+            directories: vec![proto::DirectoryNode {
+                name: "foo".into(),
+                digest: DIRECTORY_A.digest().into(),
+                size: DIRECTORY_A.size() + 42, // wrong!
+            }],
+            ..Default::default()
+        };
+    }
+
+    use super::ClosureValidator;
+
+    #[rstest]
+    /// Uploading an empty directory should succeed.
+    #[case::empty_directory(&[&*DIRECTORY_A], false, Some(vec![&*DIRECTORY_A]))]
+    /// Uploading A, then B (referring to A) should succeed.
+    #[case::simple_closure(&[&*DIRECTORY_A, &*DIRECTORY_B], false, Some(vec![&*DIRECTORY_A, &*DIRECTORY_B]))]
+    /// Uploading A, then A, then C (referring to A twice) should succeed.
+    /// We pretend to be a dumb client not deduping directories.
+    #[case::same_child(&[&*DIRECTORY_A, &*DIRECTORY_A, &*DIRECTORY_C], false, Some(vec![&*DIRECTORY_A, &*DIRECTORY_C]))]
+    /// Uploading A, then C (referring to A twice) should succeed.
+    #[case::same_child_dedup(&[&*DIRECTORY_A, &*DIRECTORY_C], false, Some(vec![&*DIRECTORY_A, &*DIRECTORY_C]))]
+    /// Uploading A, then C (referring to A twice), then B (itself referring to A) should fail during close,
+    /// as B itself would be left unconnected.
+    #[case::unconnected_node(&[&*DIRECTORY_A, &*DIRECTORY_C, &*DIRECTORY_B], false, None)]
+    /// Uploading B (referring to A) should fail immediately, because A was never uploaded.
+    #[case::dangling_pointer(&[&*DIRECTORY_B], true, None)]
+    /// Uploading a directory failing validation should fail immediately.
+    #[case::failing_validation(&[&*BROKEN_DIRECTORY], true, None)]
+    /// Uploading a directory which refers to another Directory with a wrong size should fail.
+    #[case::wrong_size_in_parent(&[&*DIRECTORY_A, &*BROKEN_PARENT_DIRECTORY], true, None)]
+    fn test_uploads(
+        #[case] directories_to_upload: &[&Directory],
+        #[case] exp_fail_upload_last: bool,
+        #[case] exp_finalize: Option<Vec<&Directory>>, // Some(_) if finalize successful, None if not.
+    ) {
+        let mut dcv = ClosureValidator::default();
+        let len_directories_to_upload = directories_to_upload.len();
+
+        for (i, d) in directories_to_upload.iter().enumerate() {
+            let resp = dcv.add((*d).clone());
+            if i == len_directories_to_upload - 1 && exp_fail_upload_last {
+                assert!(resp.is_err(), "expect last put to fail");
+
+                // We don't really care anymore what finalize() would return, as
+                // the add() failed.
+                return;
+            } else {
+                assert!(resp.is_ok(), "expect put to succeed");
+            }
+        }
+
+        // everything was uploaded successfully. Test finalize().
+        let resp = dcv.finalize();
+
+        match exp_finalize {
+            Some(directories) => {
+                assert_eq!(
+                    Vec::from_iter(directories.iter().map(|e| (*e).to_owned())),
+                    resp.expect("drain should succeed")
+                );
+            }
+            None => {
+                resp.expect_err("drain should fail");
+            }
+        }
+    }
+}
diff --git a/tvix/castore/src/directoryservice/from_addr.rs b/tvix/castore/src/directoryservice/from_addr.rs
new file mode 100644
index 0000000000..ae51df6376
--- /dev/null
+++ b/tvix/castore/src/directoryservice/from_addr.rs
@@ -0,0 +1,174 @@
+use url::Url;
+
+use crate::{proto::directory_service_client::DirectoryServiceClient, Error};
+
+use super::{DirectoryService, GRPCDirectoryService, MemoryDirectoryService, SledDirectoryService};
+
+/// Constructs a new instance of a [DirectoryService] from an URI.
+///
+/// The following URIs are supported:
+/// - `memory:`
+///   Uses a in-memory implementation.
+/// - `sled:`
+///   Uses a in-memory sled implementation.
+/// - `sled:///absolute/path/to/somewhere`
+///   Uses sled, using a path on the disk for persistency. Can be only opened
+///   from one process at the same time.
+/// - `grpc+unix:///absolute/path/to/somewhere`
+///   Connects to a local tvix-store gRPC service via Unix socket.
+/// - `grpc+http://host:port`, `grpc+https://host:port`
+///    Connects to a (remote) tvix-store gRPC service.
+pub async fn from_addr(uri: &str) -> Result<Box<dyn DirectoryService>, crate::Error> {
+    #[allow(unused_mut)]
+    let mut url = Url::parse(uri)
+        .map_err(|e| crate::Error::StorageError(format!("unable to parse url: {}", e)))?;
+
+    let directory_service: Box<dyn DirectoryService> = match url.scheme() {
+        "memory" => {
+            // memory doesn't support host or path in the URL.
+            if url.has_host() || !url.path().is_empty() {
+                return Err(Error::StorageError("invalid url".to_string()));
+            }
+            Box::<MemoryDirectoryService>::default()
+        }
+        "sled" => {
+            // sled doesn't support host, and a path can be provided (otherwise
+            // it'll live in memory only).
+            if url.has_host() {
+                return Err(Error::StorageError("no host allowed".to_string()));
+            }
+
+            if url.path() == "/" {
+                return Err(Error::StorageError(
+                    "cowardly refusing to open / with sled".to_string(),
+                ));
+            }
+
+            // TODO: expose compression and other parameters as URL parameters?
+
+            Box::new(if url.path().is_empty() {
+                SledDirectoryService::new_temporary()
+                    .map_err(|e| Error::StorageError(e.to_string()))?
+            } else {
+                SledDirectoryService::new(url.path())
+                    .map_err(|e| Error::StorageError(e.to_string()))?
+            })
+        }
+        scheme if scheme.starts_with("grpc+") => {
+            // schemes starting with grpc+ go to the GRPCPathInfoService.
+            //   That's normally grpc+unix for unix sockets, and grpc+http(s) for the HTTP counterparts.
+            // - In the case of unix sockets, there must be a path, but may not be a host.
+            // - In the case of non-unix sockets, there must be a host, but no path.
+            // Constructing the channel is handled by tvix_castore::channel::from_url.
+            let client = DirectoryServiceClient::new(crate::tonic::channel_from_url(&url).await?);
+            Box::new(GRPCDirectoryService::from_client(client))
+        }
+        #[cfg(feature = "cloud")]
+        "bigtable" => {
+            use super::bigtable::BigtableParameters;
+            use super::BigtableDirectoryService;
+
+            // parse the instance name from the hostname.
+            let instance_name = url
+                .host_str()
+                .ok_or_else(|| Error::StorageError("instance name missing".into()))?
+                .to_string();
+
+            // … but add it to the query string now, so we just need to parse that.
+            url.query_pairs_mut()
+                .append_pair("instance_name", &instance_name);
+
+            let params: BigtableParameters = serde_qs::from_str(url.query().unwrap_or_default())
+                .map_err(|e| Error::InvalidRequest(format!("failed to parse parameters: {}", e)))?;
+
+            Box::new(
+                BigtableDirectoryService::connect(params)
+                    .await
+                    .map_err(|e| Error::StorageError(e.to_string()))?,
+            )
+        }
+        _ => {
+            return Err(crate::Error::StorageError(format!(
+                "unknown scheme: {}",
+                url.scheme()
+            )))
+        }
+    };
+    Ok(directory_service)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::from_addr;
+    use lazy_static::lazy_static;
+    use rstest::rstest;
+    use tempfile::TempDir;
+
+    lazy_static! {
+        static ref TMPDIR_SLED_1: TempDir = TempDir::new().unwrap();
+        static ref TMPDIR_SLED_2: TempDir = TempDir::new().unwrap();
+    }
+
+    #[rstest]
+    /// This uses an unsupported scheme.
+    #[case::unsupported_scheme("http://foo.example/test", false)]
+    /// This configures sled in temporary mode.
+    #[case::sled_valid_temporary("sled://", true)]
+    /// This configures sled with /, which should fail.
+    #[case::sled_invalid_root("sled:///", false)]
+    /// This configures sled with a host, not path, which should fail.
+    #[case::sled_invalid_host("sled://foo.example", false)]
+    /// This configures sled with a valid path path, which should succeed.
+    #[case::sled_valid_path(&format!("sled://{}", &TMPDIR_SLED_1.path().to_str().unwrap()), true)]
+    /// This configures sled with a host, and a valid path path, which should fail.
+    #[case::sled_invalid_host_with_valid_path(&format!("sled://foo.example{}", &TMPDIR_SLED_2.path().to_str().unwrap()), false)]
+    /// This correctly sets the scheme, and doesn't set a path.
+    #[case::memory_valid("memory://", true)]
+    /// This sets a memory url host to `foo`
+    #[case::memory_invalid_host("memory://foo", false)]
+    /// This sets a memory url path to "/", which is invalid.
+    #[case::memory_invalid_root_path("memory:///", false)]
+    /// This sets a memory url path to "/foo", which is invalid.
+    #[case::memory_invalid_root_path_foo("memory:///foo", false)]
+    /// Correct scheme to connect to a unix socket.
+    #[case::grpc_valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
+    /// Correct scheme for unix socket, but setting a host too, which is invalid.
+    #[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
+    /// Correct scheme to connect to localhost, with port 12345
+    #[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
+    /// Correct scheme to connect to localhost over http, but with additional path, which is invalid.
+    #[case::grpc_invalid_host_and_path("grpc+http://localhost/some-path", false)]
+    /// A valid example for Bigtable
+    #[cfg_attr(
+        all(feature = "cloud", feature = "integration"),
+        case::bigtable_valid_url(
+            "bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1",
+            true
+        )
+    )]
+    /// A valid example for Bigtable, specifying a custom channel size and timeout
+    #[cfg_attr(
+        all(feature = "cloud", feature = "integration"),
+        case::bigtable_valid_url(
+            "bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1&channel_size=10&timeout=10",
+            true
+        )
+    )]
+    /// A invalid Bigtable example (missing fields)
+    #[cfg_attr(
+        all(feature = "cloud", feature = "integration"),
+        case::bigtable_invalid_url("bigtable://instance-1", false)
+    )]
+    #[tokio::test]
+    async fn test_from_addr_tokio(#[case] uri_str: &str, #[case] exp_succeed: bool) {
+        if exp_succeed {
+            from_addr(uri_str).await.expect("should succeed");
+        } else {
+            assert!(from_addr(uri_str).await.is_err(), "should fail");
+        }
+    }
+}
diff --git a/tvix/castore/src/directoryservice/grpc.rs b/tvix/castore/src/directoryservice/grpc.rs
new file mode 100644
index 0000000000..7402fe1b56
--- /dev/null
+++ b/tvix/castore/src/directoryservice/grpc.rs
@@ -0,0 +1,345 @@
+use std::collections::HashSet;
+
+use super::{DirectoryPutter, DirectoryService};
+use crate::proto::{self, get_directory_request::ByWhat};
+use crate::{B3Digest, Error};
+use async_stream::try_stream;
+use futures::stream::BoxStream;
+use tokio::spawn;
+use tokio::sync::mpsc::UnboundedSender;
+use tokio::task::JoinHandle;
+use tokio_stream::wrappers::UnboundedReceiverStream;
+use tonic::async_trait;
+use tonic::Code;
+use tonic::{transport::Channel, Status};
+use tracing::{instrument, warn};
+
+/// Connects to a (remote) tvix-store DirectoryService over gRPC.
+#[derive(Clone)]
+pub struct GRPCDirectoryService {
+    /// The internal reference to a gRPC client.
+    /// Cloning it is cheap, and it internally handles concurrent requests.
+    grpc_client: proto::directory_service_client::DirectoryServiceClient<Channel>,
+}
+
+impl GRPCDirectoryService {
+    /// construct a [GRPCDirectoryService] from a [proto::directory_service_client::DirectoryServiceClient].
+    /// panics if called outside the context of a tokio runtime.
+    pub fn from_client(
+        grpc_client: proto::directory_service_client::DirectoryServiceClient<Channel>,
+    ) -> Self {
+        Self { grpc_client }
+    }
+}
+
+#[async_trait]
+impl DirectoryService for GRPCDirectoryService {
+    #[instrument(level = "trace", skip_all, fields(directory.digest = %digest))]
+    async fn get(
+        &self,
+        digest: &B3Digest,
+    ) -> Result<Option<crate::proto::Directory>, crate::Error> {
+        // Get a new handle to the gRPC client, and copy the digest.
+        let mut grpc_client = self.grpc_client.clone();
+        let digest_cpy = digest.clone();
+        let message = async move {
+            let mut s = grpc_client
+                .get(proto::GetDirectoryRequest {
+                    recursive: false,
+                    by_what: Some(ByWhat::Digest(digest_cpy.into())),
+                })
+                .await?
+                .into_inner();
+
+            // Retrieve the first message only, then close the stream (we set recursive to false)
+            s.message().await
+        };
+
+        let digest = digest.clone();
+        match message.await {
+            Ok(Some(directory)) => {
+                // Validate the retrieved Directory indeed has the
+                // digest we expect it to have, to detect corruptions.
+                let actual_digest = directory.digest();
+                if actual_digest != digest {
+                    Err(crate::Error::StorageError(format!(
+                        "requested directory with digest {}, but got {}",
+                        digest, actual_digest
+                    )))
+                } else if let Err(e) = directory.validate() {
+                    // Validate the Directory itself is valid.
+                    warn!("directory failed validation: {}", e.to_string());
+                    Err(crate::Error::StorageError(format!(
+                        "directory {} failed validation: {}",
+                        digest, e,
+                    )))
+                } else {
+                    Ok(Some(directory))
+                }
+            }
+            Ok(None) => Ok(None),
+            Err(e) if e.code() == Code::NotFound => Ok(None),
+            Err(e) => Err(crate::Error::StorageError(e.to_string())),
+        }
+    }
+
+    #[instrument(level = "trace", skip_all, fields(directory.digest = %directory.digest()))]
+    async fn put(&self, directory: crate::proto::Directory) -> Result<B3Digest, crate::Error> {
+        let resp = self
+            .grpc_client
+            .clone()
+            .put(tokio_stream::once(directory))
+            .await;
+
+        match resp {
+            Ok(put_directory_resp) => Ok(put_directory_resp
+                .into_inner()
+                .root_digest
+                .try_into()
+                .map_err(|_| {
+                    Error::StorageError("invalid root digest length in response".to_string())
+                })?),
+            Err(e) => Err(crate::Error::StorageError(e.to_string())),
+        }
+    }
+
+    #[instrument(level = "trace", skip_all, fields(directory.digest = %root_directory_digest))]
+    fn get_recursive(
+        &self,
+        root_directory_digest: &B3Digest,
+    ) -> BoxStream<Result<proto::Directory, Error>> {
+        let mut grpc_client = self.grpc_client.clone();
+        let root_directory_digest = root_directory_digest.clone();
+
+        let stream = try_stream! {
+            let mut stream = grpc_client
+                .get(proto::GetDirectoryRequest {
+                    recursive: true,
+                    by_what: Some(ByWhat::Digest(root_directory_digest.clone().into())),
+                })
+                .await
+                .map_err(|e| crate::Error::StorageError(e.to_string()))?
+                .into_inner();
+
+            // The Directory digests we received so far
+            let mut received_directory_digests: HashSet<B3Digest> = HashSet::new();
+            // The Directory digests we're still expecting to get sent.
+            let mut expected_directory_digests: HashSet<B3Digest> = HashSet::from([root_directory_digest]);
+
+            loop {
+                match stream.message().await {
+                    Ok(Some(directory)) => {
+                        // validate the directory itself.
+                        if let Err(e) = directory.validate() {
+                            Err(crate::Error::StorageError(format!(
+                                "directory {} failed validation: {}",
+                                directory.digest(),
+                                e,
+                            )))?;
+                        }
+                        // validate we actually expected that directory, and move it from expected to received.
+                        let directory_digest = directory.digest();
+                        let was_expected = expected_directory_digests.remove(&directory_digest);
+                        if !was_expected {
+                            // FUTUREWORK: dumb clients might send the same stuff twice.
+                            // as a fallback, we might want to tolerate receiving
+                            // it if it's in received_directory_digests (as that
+                            // means it once was in expected_directory_digests)
+                            Err(crate::Error::StorageError(format!(
+                                "received unexpected directory {}",
+                                directory_digest
+                            )))?;
+                        }
+                        received_directory_digests.insert(directory_digest);
+
+                        // register all children in expected_directory_digests.
+                        for child_directory in &directory.directories {
+                            // We ran validate() above, so we know these digests must be correct.
+                            let child_directory_digest =
+                                child_directory.digest.clone().try_into().unwrap();
+
+                            expected_directory_digests
+                                .insert(child_directory_digest);
+                        }
+
+                        yield directory;
+                    },
+                    Ok(None) => {
+                        // If we were still expecting something, that's an error.
+                        if !expected_directory_digests.is_empty() {
+                            Err(crate::Error::StorageError(format!(
+                                "still expected {} directories, but got premature end of stream",
+                                expected_directory_digests.len(),
+                            )))?
+                        } else {
+                            return
+                        }
+                    },
+                    Err(e) => {
+                        Err(crate::Error::StorageError(e.to_string()))?;
+                    },
+                }
+            }
+        };
+
+        Box::pin(stream)
+    }
+
+    #[instrument(skip_all)]
+    fn put_multiple_start(&self) -> Box<(dyn DirectoryPutter + 'static)>
+    where
+        Self: Clone,
+    {
+        let mut grpc_client = self.grpc_client.clone();
+
+        let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
+
+        let task: JoinHandle<Result<proto::PutDirectoryResponse, Status>> = spawn(async move {
+            let s = grpc_client
+                .put(UnboundedReceiverStream::new(rx))
+                .await?
+                .into_inner();
+
+            Ok(s)
+        });
+
+        Box::new(GRPCPutter {
+            rq: Some((task, tx)),
+        })
+    }
+}
+
+/// Allows uploading multiple Directory messages in the same gRPC stream.
+pub struct GRPCPutter {
+    /// Data about the current request - a handle to the task, and the tx part
+    /// of the channel.
+    /// The tx part of the pipe is used to send [proto::Directory] to the ongoing request.
+    /// The task will yield a [proto::PutDirectoryResponse] once the stream is closed.
+    #[allow(clippy::type_complexity)] // lol
+    rq: Option<(
+        JoinHandle<Result<proto::PutDirectoryResponse, Status>>,
+        UnboundedSender<proto::Directory>,
+    )>,
+}
+
+#[async_trait]
+impl DirectoryPutter for GRPCPutter {
+    #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)]
+    async fn put(&mut self, directory: proto::Directory) -> Result<(), crate::Error> {
+        match self.rq {
+            // If we're not already closed, send the directory to directory_sender.
+            Some((_, ref directory_sender)) => {
+                if directory_sender.send(directory).is_err() {
+                    // If the channel has been prematurely closed, invoke close (so we can peek at the error code)
+                    // That error code is much more helpful, because it
+                    // contains the error message from the server.
+                    self.close().await?;
+                }
+                Ok(())
+            }
+            // If self.close() was already called, we can't put again.
+            None => Err(Error::StorageError(
+                "DirectoryPutter already closed".to_string(),
+            )),
+        }
+    }
+
+    /// Closes the stream for sending, and returns the value.
+    #[instrument(level = "trace", skip_all, ret, err)]
+    async fn close(&mut self) -> Result<B3Digest, crate::Error> {
+        // get self.rq, and replace it with None.
+        // This ensures we can only close it once.
+        match std::mem::take(&mut self.rq) {
+            None => Err(Error::StorageError("already closed".to_string())),
+            Some((task, directory_sender)) => {
+                // close directory_sender, so blocking on task will finish.
+                drop(directory_sender);
+
+                let root_digest = task
+                    .await?
+                    .map_err(|e| Error::StorageError(e.to_string()))?
+                    .root_digest;
+
+                root_digest.try_into().map_err(|_| {
+                    Error::StorageError("invalid root digest length in response".to_string())
+                })
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::time::Duration;
+    use tempfile::TempDir;
+    use tokio::net::UnixListener;
+    use tokio_retry::{strategy::ExponentialBackoff, Retry};
+    use tokio_stream::wrappers::UnixListenerStream;
+
+    use crate::{
+        directoryservice::{DirectoryService, GRPCDirectoryService, MemoryDirectoryService},
+        fixtures,
+        proto::{directory_service_client::DirectoryServiceClient, GRPCDirectoryServiceWrapper},
+    };
+
+    /// This ensures connecting via gRPC works as expected.
+    #[tokio::test]
+    async fn test_valid_unix_path_ping_pong() {
+        let tmpdir = TempDir::new().unwrap();
+        let socket_path = tmpdir.path().join("daemon");
+
+        let path_clone = socket_path.clone();
+
+        // Spin up a server
+        tokio::spawn(async {
+            let uds = UnixListener::bind(path_clone).unwrap();
+            let uds_stream = UnixListenerStream::new(uds);
+
+            // spin up a new server
+            let mut server = tonic::transport::Server::builder();
+            let router = server.add_service(
+                crate::proto::directory_service_server::DirectoryServiceServer::new(
+                    GRPCDirectoryServiceWrapper::new(
+                        Box::<MemoryDirectoryService>::default() as Box<dyn DirectoryService>
+                    ),
+                ),
+            );
+            router.serve_with_incoming(uds_stream).await
+        });
+
+        // wait for the socket to be created
+        Retry::spawn(
+            ExponentialBackoff::from_millis(20).max_delay(Duration::from_secs(10)),
+            || async {
+                if socket_path.exists() {
+                    Ok(())
+                } else {
+                    Err(())
+                }
+            },
+        )
+        .await
+        .expect("failed to wait for socket");
+
+        // prepare a client
+        let grpc_client = {
+            let url = url::Url::parse(&format!(
+                "grpc+unix://{}?wait-connect=1",
+                socket_path.display()
+            ))
+            .expect("must parse");
+            let client = DirectoryServiceClient::new(
+                crate::tonic::channel_from_url(&url)
+                    .await
+                    .expect("must succeed"),
+            );
+            GRPCDirectoryService::from_client(client)
+        };
+
+        assert!(grpc_client
+            .get(&fixtures::DIRECTORY_A.digest())
+            .await
+            .expect("must not fail")
+            .is_none())
+    }
+}
diff --git a/tvix/castore/src/directoryservice/memory.rs b/tvix/castore/src/directoryservice/memory.rs
new file mode 100644
index 0000000000..2cbbbd1b16
--- /dev/null
+++ b/tvix/castore/src/directoryservice/memory.rs
@@ -0,0 +1,86 @@
+use crate::{proto, B3Digest, Error};
+use futures::stream::BoxStream;
+use std::collections::HashMap;
+use std::sync::{Arc, RwLock};
+use tonic::async_trait;
+use tracing::{instrument, warn};
+
+use super::utils::traverse_directory;
+use super::{DirectoryPutter, DirectoryService, SimplePutter};
+
+#[derive(Clone, Default)]
+pub struct MemoryDirectoryService {
+    db: Arc<RwLock<HashMap<B3Digest, proto::Directory>>>,
+}
+
+#[async_trait]
+impl DirectoryService for MemoryDirectoryService {
+    #[instrument(skip(self, digest), fields(directory.digest = %digest))]
+    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+        let db = self.db.read()?;
+
+        match db.get(digest) {
+            // The directory was not found, return
+            None => Ok(None),
+
+            // The directory was found, try to parse the data as Directory message
+            Some(directory) => {
+                // Validate the retrieved Directory indeed has the
+                // digest we expect it to have, to detect corruptions.
+                let actual_digest = directory.digest();
+                if actual_digest != *digest {
+                    return Err(Error::StorageError(format!(
+                        "requested directory with digest {}, but got {}",
+                        digest, actual_digest
+                    )));
+                }
+
+                // Validate the Directory itself is valid.
+                if let Err(e) = directory.validate() {
+                    warn!("directory failed validation: {}", e.to_string());
+                    return Err(Error::StorageError(format!(
+                        "directory {} failed validation: {}",
+                        actual_digest, e,
+                    )));
+                }
+
+                Ok(Some(directory.clone()))
+            }
+        }
+    }
+
+    #[instrument(skip(self, directory), fields(directory.digest = %directory.digest()))]
+    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
+        let digest = directory.digest();
+
+        // validate the directory itself.
+        if let Err(e) = directory.validate() {
+            return Err(Error::InvalidRequest(format!(
+                "directory {} failed validation: {}",
+                digest, e,
+            )));
+        }
+
+        // store it
+        let mut db = self.db.write()?;
+        db.insert(digest.clone(), directory);
+
+        Ok(digest)
+    }
+
+    #[instrument(skip_all, fields(directory.digest = %root_directory_digest))]
+    fn get_recursive(
+        &self,
+        root_directory_digest: &B3Digest,
+    ) -> BoxStream<Result<proto::Directory, Error>> {
+        traverse_directory(self.clone(), root_directory_digest)
+    }
+
+    #[instrument(skip_all)]
+    fn put_multiple_start(&self) -> Box<(dyn DirectoryPutter + 'static)>
+    where
+        Self: Clone,
+    {
+        Box::new(SimplePutter::new(self.clone()))
+    }
+}
diff --git a/tvix/castore/src/directoryservice/mod.rs b/tvix/castore/src/directoryservice/mod.rs
new file mode 100644
index 0000000000..cf6bea39d8
--- /dev/null
+++ b/tvix/castore/src/directoryservice/mod.rs
@@ -0,0 +1,122 @@
+use crate::{proto, B3Digest, Error};
+use futures::stream::BoxStream;
+use tonic::async_trait;
+
+mod closure_validator;
+mod from_addr;
+mod grpc;
+mod memory;
+mod simple_putter;
+mod sled;
+#[cfg(test)]
+pub mod tests;
+mod traverse;
+mod utils;
+
+pub use self::closure_validator::ClosureValidator;
+pub use self::from_addr::from_addr;
+pub use self::grpc::GRPCDirectoryService;
+pub use self::memory::MemoryDirectoryService;
+pub use self::simple_putter::SimplePutter;
+pub use self::sled::SledDirectoryService;
+pub use self::traverse::descend_to;
+pub use self::utils::traverse_directory;
+
+#[cfg(feature = "cloud")]
+mod bigtable;
+
+#[cfg(feature = "cloud")]
+pub use self::bigtable::BigtableDirectoryService;
+
+/// The base trait all Directory services need to implement.
+/// This is a simple get and put of [crate::proto::Directory], returning their
+/// digest.
+#[async_trait]
+pub trait DirectoryService: Send + Sync {
+    /// Looks up a single Directory message by its digest.
+    /// The returned Directory message *must* be valid.
+    /// In case the directory is not found, Ok(None) is returned.
+    ///
+    /// It is okay for certain implementations to only allow retrieval of
+    /// Directory digests that are at the "root", aka the last element that's
+    /// sent to a DirectoryPutter. This makes sense for implementations bundling
+    /// closures of directories together in batches.
+    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error>;
+    /// Uploads a single Directory message, and returns the calculated
+    /// digest, or an error. An error *must* also be returned if the message is
+    /// not valid.
+    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error>;
+
+    /// Looks up a closure of [proto::Directory].
+    /// Ideally this would be a `impl Stream<Item = Result<proto::Directory, Error>>`,
+    /// and we'd be able to add a default implementation for it here, but
+    /// we can't have that yet.
+    ///
+    /// This returns a pinned, boxed stream. The pinning allows for it to be polled easily,
+    /// and the box allows different underlying stream implementations to be returned since
+    /// Rust doesn't support this as a generic in traits yet. This is the same thing that
+    /// [async_trait] generates, but for streams instead of futures.
+    ///
+    /// The individually returned Directory messages *must* be valid.
+    /// Directories are sent in an order from the root to the leaves, so that
+    /// the receiving side can validate each message to be a connected to the root
+    /// that has initially been requested.
+    fn get_recursive(
+        &self,
+        root_directory_digest: &B3Digest,
+    ) -> BoxStream<Result<proto::Directory, Error>>;
+
+    /// Allows persisting a closure of [proto::Directory], which is a graph of
+    /// connected Directory messages.
+    fn put_multiple_start(&self) -> Box<dyn DirectoryPutter>;
+}
+
+#[async_trait]
+impl<A> DirectoryService for A
+where
+    A: AsRef<dyn DirectoryService> + Send + Sync,
+{
+    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+        self.as_ref().get(digest).await
+    }
+
+    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
+        self.as_ref().put(directory).await
+    }
+
+    fn get_recursive(
+        &self,
+        root_directory_digest: &B3Digest,
+    ) -> BoxStream<Result<proto::Directory, Error>> {
+        self.as_ref().get_recursive(root_directory_digest)
+    }
+
+    fn put_multiple_start(&self) -> Box<dyn DirectoryPutter> {
+        self.as_ref().put_multiple_start()
+    }
+}
+
+/// Provides a handle to put a closure of connected [proto::Directory] elements.
+///
+/// The consumer can periodically call [DirectoryPutter::put], starting from the
+/// leaves. Once the root is reached, [DirectoryPutter::close] can be called to
+/// retrieve the root digest (or an error).
+///
+/// DirectoryPutters might be created without a single [DirectoryPutter::put],
+/// and then dropped without calling [DirectoryPutter::close],
+/// for example when ingesting a path that ends up not pointing to a directory,
+/// but a single file or symlink.
+#[async_trait]
+pub trait DirectoryPutter: Send {
+    /// Put a individual [proto::Directory] into the store.
+    /// Error semantics and behaviour is up to the specific implementation of
+    /// this trait.
+    /// Due to bursting, the returned error might refer to an object previously
+    /// sent via `put`.
+    async fn put(&mut self, directory: proto::Directory) -> Result<(), Error>;
+
+    /// Close the stream, and wait for any errors.
+    /// If there's been any invalid Directory message uploaded, and error *must*
+    /// be returned.
+    async fn close(&mut self) -> Result<B3Digest, Error>;
+}
diff --git a/tvix/castore/src/directoryservice/simple_putter.rs b/tvix/castore/src/directoryservice/simple_putter.rs
new file mode 100644
index 0000000000..25617ebcac
--- /dev/null
+++ b/tvix/castore/src/directoryservice/simple_putter.rs
@@ -0,0 +1,75 @@
+use super::ClosureValidator;
+use super::DirectoryPutter;
+use super::DirectoryService;
+use crate::proto;
+use crate::B3Digest;
+use crate::Error;
+use tonic::async_trait;
+use tracing::instrument;
+use tracing::warn;
+
+/// This is an implementation of DirectoryPutter that simply
+/// inserts individual Directory messages one by one, on close, after
+/// they successfully validated.
+pub struct SimplePutter<DS: DirectoryService> {
+    directory_service: DS,
+
+    directory_validator: Option<ClosureValidator>,
+}
+
+impl<DS: DirectoryService> SimplePutter<DS> {
+    pub fn new(directory_service: DS) -> Self {
+        Self {
+            directory_service,
+            directory_validator: Some(Default::default()),
+        }
+    }
+}
+
+#[async_trait]
+impl<DS: DirectoryService + 'static> DirectoryPutter for SimplePutter<DS> {
+    #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)]
+    async fn put(&mut self, directory: proto::Directory) -> Result<(), Error> {
+        match self.directory_validator {
+            None => return Err(Error::StorageError("already closed".to_string())),
+            Some(ref mut validator) => {
+                validator.add(directory)?;
+            }
+        }
+
+        Ok(())
+    }
+
+    #[instrument(level = "trace", skip_all, ret, err)]
+    async fn close(&mut self) -> Result<B3Digest, Error> {
+        match self.directory_validator.take() {
+            None => Err(Error::InvalidRequest("already closed".to_string())),
+            Some(validator) => {
+                // retrieve the validated directories.
+                let directories = validator.finalize()?;
+
+                // Get the root digest, which is at the end (cf. insertion order)
+                let root_digest = directories
+                    .last()
+                    .ok_or_else(|| Error::InvalidRequest("got no directories".to_string()))?
+                    .digest();
+
+                // call an individual put for each directory and await the insertion.
+                for directory in directories {
+                    let exp_digest = directory.digest();
+                    let actual_digest = self.directory_service.put(directory).await?;
+
+                    // ensure the digest the backend told us matches our expectations.
+                    if exp_digest != actual_digest {
+                        warn!(directory.digest_expected=%exp_digest, directory.digest_actual=%actual_digest, "unexpected digest");
+                        return Err(Error::StorageError(
+                            "got unexpected digest from backend during put".into(),
+                        ));
+                    }
+                }
+
+                Ok(root_digest)
+            }
+        }
+    }
+}
diff --git a/tvix/castore/src/directoryservice/sled.rs b/tvix/castore/src/directoryservice/sled.rs
new file mode 100644
index 0000000000..e4a4c2bbed
--- /dev/null
+++ b/tvix/castore/src/directoryservice/sled.rs
@@ -0,0 +1,168 @@
+use crate::proto::Directory;
+use crate::{proto, B3Digest, Error};
+use futures::stream::BoxStream;
+use prost::Message;
+use std::ops::Deref;
+use std::path::Path;
+use tonic::async_trait;
+use tracing::{instrument, warn};
+
+use super::utils::traverse_directory;
+use super::{ClosureValidator, DirectoryPutter, DirectoryService};
+
+#[derive(Clone)]
+pub struct SledDirectoryService {
+    db: sled::Db,
+}
+
+impl SledDirectoryService {
+    pub fn new<P: AsRef<Path>>(p: P) -> Result<Self, sled::Error> {
+        let config = sled::Config::default()
+            .use_compression(false) // is a required parameter
+            .path(p);
+        let db = config.open()?;
+
+        Ok(Self { db })
+    }
+
+    pub fn new_temporary() -> Result<Self, sled::Error> {
+        let config = sled::Config::default().temporary(true);
+        let db = config.open()?;
+
+        Ok(Self { db })
+    }
+}
+
+#[async_trait]
+impl DirectoryService for SledDirectoryService {
+    #[instrument(skip(self, digest), fields(directory.digest = %digest))]
+    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+        match self.db.get(digest.as_slice()) {
+            // The directory was not found, return
+            Ok(None) => Ok(None),
+
+            // The directory was found, try to parse the data as Directory message
+            Ok(Some(data)) => match Directory::decode(&*data) {
+                Ok(directory) => {
+                    // Validate the retrieved Directory indeed has the
+                    // digest we expect it to have, to detect corruptions.
+                    let actual_digest = directory.digest();
+                    if actual_digest != *digest {
+                        return Err(Error::StorageError(format!(
+                            "requested directory with digest {}, but got {}",
+                            digest, actual_digest
+                        )));
+                    }
+
+                    // Validate the Directory itself is valid.
+                    if let Err(e) = directory.validate() {
+                        warn!("directory failed validation: {}", e.to_string());
+                        return Err(Error::StorageError(format!(
+                            "directory {} failed validation: {}",
+                            actual_digest, e,
+                        )));
+                    }
+
+                    Ok(Some(directory))
+                }
+                Err(e) => {
+                    warn!("unable to parse directory {}: {}", digest, e);
+                    Err(Error::StorageError(e.to_string()))
+                }
+            },
+            // some storage error?
+            Err(e) => Err(Error::StorageError(e.to_string())),
+        }
+    }
+
+    #[instrument(skip(self, directory), fields(directory.digest = %directory.digest()))]
+    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
+        let digest = directory.digest();
+
+        // validate the directory itself.
+        if let Err(e) = directory.validate() {
+            return Err(Error::InvalidRequest(format!(
+                "directory {} failed validation: {}",
+                digest, e,
+            )));
+        }
+        // store it
+        let result = self.db.insert(digest.as_slice(), directory.encode_to_vec());
+        if let Err(e) = result {
+            return Err(Error::StorageError(e.to_string()));
+        }
+        Ok(digest)
+    }
+
+    #[instrument(skip_all, fields(directory.digest = %root_directory_digest))]
+    fn get_recursive(
+        &self,
+        root_directory_digest: &B3Digest,
+    ) -> BoxStream<Result<proto::Directory, Error>> {
+        traverse_directory(self.clone(), root_directory_digest)
+    }
+
+    #[instrument(skip_all)]
+    fn put_multiple_start(&self) -> Box<(dyn DirectoryPutter + 'static)>
+    where
+        Self: Clone,
+    {
+        Box::new(SledDirectoryPutter {
+            tree: self.db.deref().clone(),
+            directory_validator: Some(Default::default()),
+        })
+    }
+}
+
+/// Buffers Directory messages to be uploaded and inserts them in a batch
+/// transaction on close.
+pub struct SledDirectoryPutter {
+    tree: sled::Tree,
+
+    /// The directories (inside the directory validator) that we insert later,
+    /// or None, if they were already inserted.
+    directory_validator: Option<ClosureValidator>,
+}
+
+#[async_trait]
+impl DirectoryPutter for SledDirectoryPutter {
+    #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)]
+    async fn put(&mut self, directory: proto::Directory) -> Result<(), Error> {
+        match self.directory_validator {
+            None => return Err(Error::StorageError("already closed".to_string())),
+            Some(ref mut validator) => {
+                validator.add(directory)?;
+            }
+        }
+
+        Ok(())
+    }
+
+    #[instrument(level = "trace", skip_all, ret, err)]
+    async fn close(&mut self) -> Result<B3Digest, Error> {
+        match self.directory_validator.take() {
+            None => Err(Error::InvalidRequest("already closed".to_string())),
+            Some(validator) => {
+                // retrieve the validated directories.
+                let directories = validator.finalize()?;
+
+                // Get the root digest, which is at the end (cf. insertion order)
+                let root_digest = directories
+                    .last()
+                    .ok_or_else(|| Error::InvalidRequest("got no directories".to_string()))?
+                    .digest();
+
+                let mut batch = sled::Batch::default();
+                for directory in directories {
+                    batch.insert(directory.digest().as_slice(), directory.encode_to_vec());
+                }
+
+                self.tree
+                    .apply_batch(batch)
+                    .map_err(|e| Error::StorageError(format!("unable to apply batch: {}", e)))?;
+
+                Ok(root_digest)
+            }
+        }
+    }
+}
diff --git a/tvix/castore/src/directoryservice/tests/mod.rs b/tvix/castore/src/directoryservice/tests/mod.rs
new file mode 100644
index 0000000000..1b40d9feb0
--- /dev/null
+++ b/tvix/castore/src/directoryservice/tests/mod.rs
@@ -0,0 +1,226 @@
+//! This contains test scenarios that a given [DirectoryService] needs to pass.
+//! We use [rstest] and [rstest_reuse] to provide all services we want to test
+//! against, and then apply this template to all test functions.
+
+use futures::StreamExt;
+use rstest::*;
+use rstest_reuse::{self, *};
+
+use super::DirectoryService;
+use crate::directoryservice;
+use crate::{
+    fixtures::{DIRECTORY_A, DIRECTORY_B, DIRECTORY_C},
+    proto::{self, Directory},
+};
+
+mod utils;
+use self::utils::make_grpc_directory_service_client;
+
+// TODO: add tests doing individual puts of a closure, then doing a get_recursive
+// (and figure out semantics if necessary)
+
+/// This produces a template, which will be applied to all individual test functions.
+/// See https://github.com/la10736/rstest/issues/130#issuecomment-968864832
+#[template]
+#[rstest]
+#[case::grpc(make_grpc_directory_service_client().await)]
+#[case::memory(directoryservice::from_addr("memory://").await.unwrap())]
+#[case::sled(directoryservice::from_addr("sled://").await.unwrap())]
+#[cfg_attr(all(feature = "cloud", feature = "integration"), case::bigtable(directoryservice::from_addr("bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1").await.unwrap()))]
+pub fn directory_services(#[case] directory_service: impl DirectoryService) {}
+
+/// Ensures asking for a directory that doesn't exist returns a Ok(None).
+#[apply(directory_services)]
+#[tokio::test]
+async fn test_non_exist(directory_service: impl DirectoryService) {
+    let resp = directory_service.get(&DIRECTORY_A.digest()).await;
+    assert!(resp.unwrap().is_none())
+}
+
+/// Putting a single directory into the store, and then getting it out both via
+/// `.get[_recursive]` should work.
+#[apply(directory_services)]
+#[tokio::test]
+async fn put_get(directory_service: impl DirectoryService) {
+    // Insert a Directory.
+    let digest = directory_service.put(DIRECTORY_A.clone()).await.unwrap();
+    assert_eq!(DIRECTORY_A.digest(), digest, "returned digest must match");
+
+    // single get
+    assert_eq!(
+        Some(DIRECTORY_A.clone()),
+        directory_service.get(&DIRECTORY_A.digest()).await.unwrap()
+    );
+
+    // recursive get
+    assert_eq!(
+        vec![Ok(DIRECTORY_A.clone())],
+        directory_service
+            .get_recursive(&DIRECTORY_A.digest())
+            .collect::<Vec<_>>()
+            .await
+    );
+}
+
+/// Putting a directory closure should work, and it should be possible to get
+/// back the root node both via .get[_recursive]. We don't check `.get` for the
+/// leaf node is possible, as it's Ok for stores to not support that.
+#[apply(directory_services)]
+#[tokio::test]
+async fn put_get_multiple_success(directory_service: impl DirectoryService) {
+    // Insert a Directory closure.
+    let mut handle = directory_service.put_multiple_start();
+    handle.put(DIRECTORY_A.clone()).await.unwrap();
+    handle.put(DIRECTORY_C.clone()).await.unwrap();
+    let root_digest = handle.close().await.unwrap();
+    assert_eq!(
+        DIRECTORY_C.digest(),
+        root_digest,
+        "root digest should match"
+    );
+
+    // Get the root node.
+    assert_eq!(
+        Some(DIRECTORY_C.clone()),
+        directory_service.get(&DIRECTORY_C.digest()).await.unwrap()
+    );
+
+    // Get the closure. Ensure it's sent from the root to the leaves.
+    assert_eq!(
+        vec![Ok(DIRECTORY_C.clone()), Ok(DIRECTORY_A.clone())],
+        directory_service
+            .get_recursive(&DIRECTORY_C.digest())
+            .collect::<Vec<_>>()
+            .await
+    )
+}
+
+/// Puts a directory closure, but simulates a dumb client not deduplicating
+/// its list. Ensure we still only get back a deduplicated list.
+#[apply(directory_services)]
+#[tokio::test]
+async fn put_get_multiple_dedup(directory_service: impl DirectoryService) {
+    // Insert a Directory closure.
+    let mut handle = directory_service.put_multiple_start();
+    handle.put(DIRECTORY_A.clone()).await.unwrap();
+    handle.put(DIRECTORY_A.clone()).await.unwrap();
+    handle.put(DIRECTORY_C.clone()).await.unwrap();
+    let root_digest = handle.close().await.unwrap();
+    assert_eq!(
+        DIRECTORY_C.digest(),
+        root_digest,
+        "root digest should match"
+    );
+
+    // Ensure the returned closure only contains `DIRECTORY_A` once.
+    assert_eq!(
+        vec![Ok(DIRECTORY_C.clone()), Ok(DIRECTORY_A.clone())],
+        directory_service
+            .get_recursive(&DIRECTORY_C.digest())
+            .collect::<Vec<_>>()
+            .await
+    )
+}
+
+/// Uploading A, then C (referring to A twice), then B (itself referring to A) should fail during close,
+/// as B itself would be left unconnected.
+#[apply(directory_services)]
+#[tokio::test]
+async fn upload_reject_unconnected(directory_service: impl DirectoryService) {
+    let mut handle = directory_service.put_multiple_start();
+
+    handle.put(DIRECTORY_A.clone()).await.unwrap();
+    handle.put(DIRECTORY_C.clone()).await.unwrap();
+    handle.put(DIRECTORY_B.clone()).await.unwrap();
+
+    assert!(
+        handle.close().await.is_err(),
+        "closing handle should fail, as B would be left unconnected"
+    );
+}
+
+/// Uploading a directory that refers to another directory not yet uploaded
+/// should fail.
+#[apply(directory_services)]
+#[tokio::test]
+async fn upload_reject_dangling_pointer(directory_service: impl DirectoryService) {
+    let mut handle = directory_service.put_multiple_start();
+
+    // We insert DIRECTORY_A on its own, to ensure the check runs for the
+    // individual put_multiple session, not across the global DirectoryService
+    // contents.
+    directory_service.put(DIRECTORY_A.clone()).await.unwrap();
+
+    // DIRECTORY_B refers to DIRECTORY_A, which is not uploaded with this handle.
+    if handle.put(DIRECTORY_B.clone()).await.is_ok() {
+        assert!(
+            handle.close().await.is_err(),
+            "when succeeding put, close must fail"
+        )
+    }
+}
+
+/// Try uploading a Directory failing its internal validation, ensure it gets
+/// rejected.
+#[apply(directory_services)]
+#[tokio::test]
+async fn upload_reject_failing_validation(directory_service: impl DirectoryService) {
+    let broken_directory = Directory {
+        symlinks: vec![proto::SymlinkNode {
+            name: "".into(), // wrong!
+            target: "doesntmatter".into(),
+        }],
+        ..Default::default()
+    };
+    assert!(broken_directory.validate().is_err());
+
+    // Try to upload via single upload.
+    assert!(
+        directory_service
+            .put(broken_directory.clone())
+            .await
+            .is_err(),
+        "single upload must fail"
+    );
+
+    // Try to upload via put_multiple. We're a bit more permissive here, the
+    // intermediate .put() might succeed, due to client-side bursting (in the
+    // case of gRPC), but then the close MUST fail.
+    let mut handle = directory_service.put_multiple_start();
+    if handle.put(broken_directory).await.is_ok() {
+        assert!(
+            handle.close().await.is_err(),
+            "when succeeding put, close must fail"
+        )
+    }
+}
+
+/// Try uploading a Directory that refers to a previously-uploaded directory.
+/// Both pass their isolated validation, but the size field in the parent is wrong.
+/// This should be rejected.
+#[apply(directory_services)]
+#[tokio::test]
+async fn upload_reject_wrong_size(directory_service: impl DirectoryService) {
+    let wrong_parent_directory = Directory {
+        directories: vec![proto::DirectoryNode {
+            name: "foo".into(),
+            digest: DIRECTORY_A.digest().into(),
+            size: DIRECTORY_A.size() + 42, // wrong!
+        }],
+        ..Default::default()
+    };
+
+    // Make sure isolated validation itself is ok
+    assert!(wrong_parent_directory.validate().is_ok());
+
+    // Now upload both. Ensure it either fails during the second put, or during
+    // the close.
+    let mut handle = directory_service.put_multiple_start();
+    handle.put(DIRECTORY_A.clone()).await.unwrap();
+    if handle.put(wrong_parent_directory).await.is_ok() {
+        assert!(
+            handle.close().await.is_err(),
+            "when second put succeeds, close must fail"
+        )
+    }
+}
diff --git a/tvix/castore/src/directoryservice/tests/utils.rs b/tvix/castore/src/directoryservice/tests/utils.rs
new file mode 100644
index 0000000000..0f706695ee
--- /dev/null
+++ b/tvix/castore/src/directoryservice/tests/utils.rs
@@ -0,0 +1,46 @@
+use crate::directoryservice::{DirectoryService, GRPCDirectoryService};
+use crate::proto::directory_service_client::DirectoryServiceClient;
+use crate::proto::GRPCDirectoryServiceWrapper;
+use crate::{
+    directoryservice::MemoryDirectoryService,
+    proto::directory_service_server::DirectoryServiceServer,
+};
+
+use tonic::transport::{Endpoint, Server, Uri};
+
+/// Constructs and returns a gRPC DirectoryService.
+/// The server part is a [MemoryDirectoryService], exposed via the
+/// [GRPCDirectoryServiceWrapper], and connected through a DuplexStream.
+pub async fn make_grpc_directory_service_client() -> Box<dyn DirectoryService> {
+    let (left, right) = tokio::io::duplex(64);
+
+    // spin up a server, which will only connect once, to the left side.
+    tokio::spawn(async {
+        let directory_service =
+            Box::<MemoryDirectoryService>::default() as Box<dyn DirectoryService>;
+
+        let mut server = Server::builder();
+        let router = server.add_service(DirectoryServiceServer::new(
+            GRPCDirectoryServiceWrapper::new(directory_service),
+        ));
+
+        router
+            .serve_with_incoming(tokio_stream::once(Ok::<_, std::io::Error>(left)))
+            .await
+    });
+
+    // Create a client, connecting to the right side. The URI is unused.
+    let mut maybe_right = Some(right);
+    Box::new(GRPCDirectoryService::from_client(
+        DirectoryServiceClient::new(
+            Endpoint::try_from("http://[::]:50051")
+                .unwrap()
+                .connect_with_connector(tower::service_fn(move |_: Uri| {
+                    let right = maybe_right.take().unwrap();
+                    async move { Ok::<_, std::io::Error>(right) }
+                }))
+                .await
+                .unwrap(),
+        ),
+    ))
+}
diff --git a/tvix/castore/src/directoryservice/traverse.rs b/tvix/castore/src/directoryservice/traverse.rs
new file mode 100644
index 0000000000..8a668c868c
--- /dev/null
+++ b/tvix/castore/src/directoryservice/traverse.rs
@@ -0,0 +1,197 @@
+use super::DirectoryService;
+use crate::{proto::NamedNode, B3Digest, Error, Path};
+use tracing::{instrument, warn};
+
+/// This descends from a (root) node to the given (sub)path, returning the Node
+/// at that path, or none, if there's nothing at that path.
+#[instrument(skip(directory_service, path), fields(%path))]
+pub async fn descend_to<DS>(
+    directory_service: DS,
+    root_node: crate::proto::node::Node,
+    path: impl AsRef<Path> + std::fmt::Display,
+) -> Result<Option<crate::proto::node::Node>, Error>
+where
+    DS: AsRef<dyn DirectoryService>,
+{
+    let mut cur_node = root_node;
+    let mut it = path.as_ref().components();
+
+    loop {
+        match it.next() {
+            None => {
+                // the (remaining) path is empty, return the node we're current at.
+                return Ok(Some(cur_node));
+            }
+            Some(first_component) => {
+                match cur_node {
+                    crate::proto::node::Node::File(_) | crate::proto::node::Node::Symlink(_) => {
+                        // There's still some path left, but the current node is no directory.
+                        // This means the path doesn't exist, as we can't reach it.
+                        return Ok(None);
+                    }
+                    crate::proto::node::Node::Directory(directory_node) => {
+                        let digest: B3Digest = directory_node.digest.try_into().map_err(|_e| {
+                            Error::StorageError("invalid digest length".to_string())
+                        })?;
+
+                        // fetch the linked node from the directory_service
+                        match directory_service.as_ref().get(&digest).await? {
+                            // If we didn't get the directory node that's linked, that's a store inconsistency, bail out!
+                            None => {
+                                warn!("directory {} does not exist", digest);
+
+                                return Err(Error::StorageError(format!(
+                                    "directory {} does not exist",
+                                    digest
+                                )));
+                            }
+                            Some(directory) => {
+                                // look for first_component in the [Directory].
+                                // FUTUREWORK: as the nodes() iterator returns in a sorted fashion, we
+                                // could stop as soon as e.name is larger than the search string.
+                                let child_node =
+                                    directory.nodes().find(|n| n.get_name() == first_component);
+
+                                match child_node {
+                                    // child node not found means there's no such element inside the directory.
+                                    None => {
+                                        return Ok(None);
+                                    }
+                                    // child node found, return to top-of loop to find the next
+                                    // node in the path.
+                                    Some(child_node) => {
+                                        cur_node = child_node;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        directoryservice,
+        fixtures::{DIRECTORY_COMPLICATED, DIRECTORY_WITH_KEEP},
+        PathBuf,
+    };
+
+    use super::descend_to;
+
+    #[tokio::test]
+    async fn test_descend_to() {
+        let directory_service = directoryservice::from_addr("memory://").await.unwrap();
+
+        let mut handle = directory_service.put_multiple_start();
+        handle
+            .put(DIRECTORY_WITH_KEEP.clone())
+            .await
+            .expect("must succeed");
+        handle
+            .put(DIRECTORY_COMPLICATED.clone())
+            .await
+            .expect("must succeed");
+
+        handle.close().await.expect("must upload");
+
+        // construct the node for DIRECTORY_COMPLICATED
+        let node_directory_complicated =
+            crate::proto::node::Node::Directory(crate::proto::DirectoryNode {
+                name: "doesntmatter".into(),
+                digest: DIRECTORY_COMPLICATED.digest().into(),
+                size: DIRECTORY_COMPLICATED.size(),
+            });
+
+        // construct the node for DIRECTORY_COMPLICATED
+        let node_directory_with_keep = crate::proto::node::Node::Directory(
+            DIRECTORY_COMPLICATED.directories.first().unwrap().clone(),
+        );
+
+        // construct the node for the .keep file
+        let node_file_keep =
+            crate::proto::node::Node::File(DIRECTORY_WITH_KEEP.files.first().unwrap().clone());
+
+        // traversal to an empty subpath should return the root node.
+        {
+            let resp = descend_to(
+                &directory_service,
+                node_directory_complicated.clone(),
+                "".parse::<PathBuf>().unwrap(),
+            )
+            .await
+            .expect("must succeed");
+
+            assert_eq!(Some(node_directory_complicated.clone()), resp);
+        }
+
+        // traversal to `keep` should return the node for DIRECTORY_WITH_KEEP
+        {
+            let resp = descend_to(
+                &directory_service,
+                node_directory_complicated.clone(),
+                "keep".parse::<PathBuf>().unwrap(),
+            )
+            .await
+            .expect("must succeed");
+
+            assert_eq!(Some(node_directory_with_keep), resp);
+        }
+
+        // traversal to `keep/.keep` should return the node for the .keep file
+        {
+            let resp = descend_to(
+                &directory_service,
+                node_directory_complicated.clone(),
+                "keep/.keep".parse::<PathBuf>().unwrap(),
+            )
+            .await
+            .expect("must succeed");
+
+            assert_eq!(Some(node_file_keep.clone()), resp);
+        }
+
+        // traversal to `void` should return None (doesn't exist)
+        {
+            let resp = descend_to(
+                &directory_service,
+                node_directory_complicated.clone(),
+                "void".parse::<PathBuf>().unwrap(),
+            )
+            .await
+            .expect("must succeed");
+
+            assert_eq!(None, resp);
+        }
+
+        // traversal to `v/oid` should return None (doesn't exist)
+        {
+            let resp = descend_to(
+                &directory_service,
+                node_directory_complicated.clone(),
+                "v/oid".parse::<PathBuf>().unwrap(),
+            )
+            .await
+            .expect("must succeed");
+
+            assert_eq!(None, resp);
+        }
+
+        // traversal to `keep/.keep/404` should return None (the path can't be
+        // reached, as keep/.keep already is a file)
+        {
+            let resp = descend_to(
+                &directory_service,
+                node_directory_complicated.clone(),
+                "keep/.keep/foo".parse::<PathBuf>().unwrap(),
+            )
+            .await
+            .expect("must succeed");
+
+            assert_eq!(None, resp);
+        }
+    }
+}
diff --git a/tvix/castore/src/directoryservice/utils.rs b/tvix/castore/src/directoryservice/utils.rs
new file mode 100644
index 0000000000..01c521076c
--- /dev/null
+++ b/tvix/castore/src/directoryservice/utils.rs
@@ -0,0 +1,82 @@
+use super::DirectoryService;
+use crate::proto;
+use crate::B3Digest;
+use crate::Error;
+use async_stream::stream;
+use futures::stream::BoxStream;
+use std::collections::{HashSet, VecDeque};
+use tracing::warn;
+
+/// Traverses a [proto::Directory] from the root to the children.
+///
+/// This is mostly BFS, but directories are only returned once.
+pub fn traverse_directory<'a, DS: DirectoryService + 'static>(
+    directory_service: DS,
+    root_directory_digest: &B3Digest,
+) -> BoxStream<'a, Result<proto::Directory, Error>> {
+    // The list of all directories that still need to be traversed. The next
+    // element is picked from the front, new elements are enqueued at the
+    // back.
+    let mut worklist_directory_digests: VecDeque<B3Digest> =
+        VecDeque::from([root_directory_digest.clone()]);
+    // The list of directory digests already sent to the consumer.
+    // We omit sending the same directories multiple times.
+    let mut sent_directory_digests: HashSet<B3Digest> = HashSet::new();
+
+    let stream = stream! {
+        while let Some(current_directory_digest) = worklist_directory_digests.pop_front() {
+            match directory_service.get(&current_directory_digest).await {
+                // if it's not there, we have an inconsistent store!
+                Ok(None) => {
+                    warn!("directory {} does not exist", current_directory_digest);
+                    yield Err(Error::StorageError(format!(
+                        "directory {} does not exist",
+                        current_directory_digest
+                    )));
+                }
+                Err(e) => {
+                    warn!("failed to look up directory");
+                    yield Err(Error::StorageError(format!(
+                        "unable to look up directory {}: {}",
+                        current_directory_digest, e
+                    )));
+                }
+
+                // if we got it
+                Ok(Some(current_directory)) => {
+                    // validate, we don't want to send invalid directories.
+                    if let Err(e) = current_directory.validate() {
+                        warn!("directory failed validation: {}", e.to_string());
+                        yield Err(Error::StorageError(format!(
+                            "invalid directory: {}",
+                            current_directory_digest
+                        )));
+                    }
+
+                    // We're about to send this directory, so let's avoid sending it again if a
+                    // descendant has it.
+                    sent_directory_digests.insert(current_directory_digest);
+
+                    // enqueue all child directory digests to the work queue, as
+                    // long as they're not part of the worklist or already sent.
+                    // This panics if the digest looks invalid, it's supposed to be checked first.
+                    for child_directory_node in &current_directory.directories {
+                        // TODO: propagate error
+                        let child_digest: B3Digest = child_directory_node.digest.clone().try_into().unwrap();
+
+                        if worklist_directory_digests.contains(&child_digest)
+                            || sent_directory_digests.contains(&child_digest)
+                        {
+                            continue;
+                        }
+                        worklist_directory_digests.push_back(child_digest);
+                    }
+
+                    yield Ok(current_directory);
+                }
+            };
+        }
+    };
+
+    Box::pin(stream)
+}
diff --git a/tvix/castore/src/errors.rs b/tvix/castore/src/errors.rs
new file mode 100644
index 0000000000..e807a19b9e
--- /dev/null
+++ b/tvix/castore/src/errors.rs
@@ -0,0 +1,61 @@
+use std::sync::PoisonError;
+use thiserror::Error;
+use tokio::task::JoinError;
+use tonic::Status;
+
+/// Errors related to communication with the store.
+#[derive(Debug, Error, PartialEq)]
+pub enum Error {
+    #[error("invalid request: {0}")]
+    InvalidRequest(String),
+
+    #[error("internal storage error: {0}")]
+    StorageError(String),
+}
+
+impl<T> From<PoisonError<T>> for Error {
+    fn from(value: PoisonError<T>) -> Self {
+        Error::StorageError(value.to_string())
+    }
+}
+
+impl From<JoinError> for Error {
+    fn from(value: JoinError) -> Self {
+        Error::StorageError(value.to_string())
+    }
+}
+
+impl From<Error> for Status {
+    fn from(value: Error) -> Self {
+        match value {
+            Error::InvalidRequest(msg) => Status::invalid_argument(msg),
+            Error::StorageError(msg) => Status::data_loss(format!("storage error: {}", msg)),
+        }
+    }
+}
+
+impl From<crate::tonic::Error> for Error {
+    fn from(value: crate::tonic::Error) -> Self {
+        Self::StorageError(value.to_string())
+    }
+}
+
+impl From<std::io::Error> for Error {
+    fn from(value: std::io::Error) -> Self {
+        if value.kind() == std::io::ErrorKind::InvalidInput {
+            Error::InvalidRequest(value.to_string())
+        } else {
+            Error::StorageError(value.to_string())
+        }
+    }
+}
+
+// TODO: this should probably go somewhere else?
+impl From<Error> for std::io::Error {
+    fn from(value: Error) -> Self {
+        match value {
+            Error::InvalidRequest(msg) => Self::new(std::io::ErrorKind::InvalidInput, msg),
+            Error::StorageError(msg) => Self::new(std::io::ErrorKind::Other, msg),
+        }
+    }
+}
diff --git a/tvix/castore/src/fixtures.rs b/tvix/castore/src/fixtures.rs
new file mode 100644
index 0000000000..a206d9b7dd
--- /dev/null
+++ b/tvix/castore/src/fixtures.rs
@@ -0,0 +1,88 @@
+use crate::{
+    proto::{self, Directory, DirectoryNode, FileNode, SymlinkNode},
+    B3Digest,
+};
+use lazy_static::lazy_static;
+
+pub const HELLOWORLD_BLOB_CONTENTS: &[u8] = b"Hello World!";
+pub const EMPTY_BLOB_CONTENTS: &[u8] = b"";
+
+lazy_static! {
+    pub static ref DUMMY_DIGEST: B3Digest = {
+        let u = [0u8; 32];
+        (&u).into()
+    };
+    pub static ref DUMMY_DIGEST_2: B3Digest = {
+        let mut u = [0u8; 32];
+        u[0] = 0x10;
+        (&u).into()
+    };
+    pub static ref DUMMY_DATA_1: bytes::Bytes = vec![0x01, 0x02, 0x03].into();
+    pub static ref DUMMY_DATA_2: bytes::Bytes = vec![0x04, 0x05].into();
+
+    pub static ref HELLOWORLD_BLOB_DIGEST: B3Digest =
+        blake3::hash(HELLOWORLD_BLOB_CONTENTS).as_bytes().into();
+    pub static ref EMPTY_BLOB_DIGEST: B3Digest =
+        blake3::hash(EMPTY_BLOB_CONTENTS).as_bytes().into();
+
+    // 2 bytes
+    pub static ref BLOB_A: bytes::Bytes = vec![0x00, 0x01].into();
+    pub static ref BLOB_A_DIGEST: B3Digest = blake3::hash(&BLOB_A).as_bytes().into();
+
+    // 1MB
+    pub static ref BLOB_B: bytes::Bytes = (0..255).collect::<Vec<u8>>().repeat(4 * 1024).into();
+    pub static ref BLOB_B_DIGEST: B3Digest = blake3::hash(&BLOB_B).as_bytes().into();
+
+    // Directories
+    pub static ref DIRECTORY_WITH_KEEP: proto::Directory = proto::Directory {
+        directories: vec![],
+        files: vec![FileNode {
+            name: b".keep".to_vec().into(),
+            digest: EMPTY_BLOB_DIGEST.clone().into(),
+            size: 0,
+            executable: false,
+        }],
+        symlinks: vec![],
+    };
+    pub static ref DIRECTORY_COMPLICATED: proto::Directory = proto::Directory {
+        directories: vec![DirectoryNode {
+            name: b"keep".to_vec().into(),
+            digest: DIRECTORY_WITH_KEEP.digest().into(),
+            size: DIRECTORY_WITH_KEEP.size(),
+        }],
+        files: vec![FileNode {
+            name: b".keep".to_vec().into(),
+            digest: EMPTY_BLOB_DIGEST.clone().into(),
+            size: 0,
+            executable: false,
+        }],
+        symlinks: vec![SymlinkNode {
+            name: b"aa".to_vec().into(),
+            target: b"/nix/store/somewhereelse".to_vec().into(),
+        }],
+    };
+    pub static ref DIRECTORY_A: Directory = Directory::default();
+    pub static ref DIRECTORY_B: Directory = Directory {
+        directories: vec![DirectoryNode {
+            name: b"a".to_vec().into(),
+            digest: DIRECTORY_A.digest().into(),
+            size: DIRECTORY_A.size(),
+        }],
+        ..Default::default()
+    };
+    pub static ref DIRECTORY_C: Directory = Directory {
+        directories: vec![
+            DirectoryNode {
+                name: b"a".to_vec().into(),
+                digest: DIRECTORY_A.digest().into(),
+                size: DIRECTORY_A.size(),
+            },
+            DirectoryNode {
+                name: b"a'".to_vec().into(),
+                digest: DIRECTORY_A.digest().into(),
+                size: DIRECTORY_A.size(),
+            }
+        ],
+        ..Default::default()
+    };
+}
diff --git a/tvix/castore/src/fs/file_attr.rs b/tvix/castore/src/fs/file_attr.rs
new file mode 100644
index 0000000000..2e0e70e3cd
--- /dev/null
+++ b/tvix/castore/src/fs/file_attr.rs
@@ -0,0 +1,29 @@
+#![allow(clippy::unnecessary_cast)] // libc::S_IFDIR is u32 on Linux and u16 on MacOS
+
+use fuse_backend_rs::abi::fuse_abi::Attr;
+
+/// The [Attr] describing the root
+pub const ROOT_FILE_ATTR: Attr = Attr {
+    ino: fuse_backend_rs::api::filesystem::ROOT_ID,
+    size: 0,
+    blksize: 1024,
+    blocks: 0,
+    mode: libc::S_IFDIR as u32 | 0o555,
+    atime: 0,
+    mtime: 0,
+    ctime: 0,
+    atimensec: 0,
+    mtimensec: 0,
+    ctimensec: 0,
+    nlink: 0,
+    uid: 0,
+    gid: 0,
+    rdev: 0,
+    flags: 0,
+    #[cfg(target_os = "macos")]
+    crtime: 0,
+    #[cfg(target_os = "macos")]
+    crtimensec: 0,
+    #[cfg(target_os = "macos")]
+    padding: 0,
+};
diff --git a/tvix/castore/src/fs/fuse.rs b/tvix/castore/src/fs/fuse.rs
new file mode 100644
index 0000000000..cd50618ff5
--- /dev/null
+++ b/tvix/castore/src/fs/fuse.rs
@@ -0,0 +1,120 @@
+use std::{io, path::Path, sync::Arc, thread};
+
+use fuse_backend_rs::{api::filesystem::FileSystem, transport::FuseSession};
+use tracing::{error, instrument};
+
+struct FuseServer<FS>
+where
+    FS: FileSystem + Sync + Send,
+{
+    server: Arc<fuse_backend_rs::api::server::Server<Arc<FS>>>,
+    channel: fuse_backend_rs::transport::FuseChannel,
+}
+
+#[cfg(target_os = "macos")]
+const BADFD: libc::c_int = libc::EBADF;
+#[cfg(target_os = "linux")]
+const BADFD: libc::c_int = libc::EBADFD;
+
+impl<FS> FuseServer<FS>
+where
+    FS: FileSystem + Sync + Send,
+{
+    fn start(&mut self) -> io::Result<()> {
+        while let Some((reader, writer)) = self
+            .channel
+            .get_request()
+            .map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?
+        {
+            if let Err(e) = self
+                .server
+                .handle_message(reader, writer.into(), None, None)
+            {
+                match e {
+                    // This indicates the session has been shut down.
+                    fuse_backend_rs::Error::EncodeMessage(e) if e.raw_os_error() == Some(BADFD) => {
+                        break;
+                    }
+                    error => {
+                        error!(?error, "failed to handle fuse request");
+                        continue;
+                    }
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+pub struct FuseDaemon {
+    session: FuseSession,
+    threads: Vec<thread::JoinHandle<()>>,
+}
+
+impl FuseDaemon {
+    #[instrument(skip(fs, mountpoint), fields(mountpoint=?mountpoint), err)]
+    pub fn new<FS, P>(
+        fs: FS,
+        mountpoint: P,
+        threads: usize,
+        allow_other: bool,
+    ) -> Result<Self, io::Error>
+    where
+        FS: FileSystem + Sync + Send + 'static,
+        P: AsRef<Path> + std::fmt::Debug,
+    {
+        let server = Arc::new(fuse_backend_rs::api::server::Server::new(Arc::new(fs)));
+
+        let mut session = FuseSession::new(mountpoint.as_ref(), "tvix-store", "", true)
+            .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
+
+        #[cfg(target_os = "linux")]
+        session.set_allow_other(allow_other);
+        session
+            .mount()
+            .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
+        let mut join_handles = Vec::with_capacity(threads);
+        for _ in 0..threads {
+            let mut server = FuseServer {
+                server: server.clone(),
+                channel: session
+                    .new_channel()
+                    .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?,
+            };
+            let join_handle = thread::Builder::new()
+                .name("fuse_server".to_string())
+                .spawn(move || {
+                    let _ = server.start();
+                })?;
+            join_handles.push(join_handle);
+        }
+
+        Ok(FuseDaemon {
+            session,
+            threads: join_handles,
+        })
+    }
+
+    #[instrument(skip_all, err)]
+    pub fn unmount(&mut self) -> Result<(), io::Error> {
+        self.session
+            .umount()
+            .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
+
+        for thread in self.threads.drain(..) {
+            thread.join().map_err(|_| {
+                io::Error::new(io::ErrorKind::Other, "failed to join fuse server thread")
+            })?;
+        }
+
+        Ok(())
+    }
+}
+
+impl Drop for FuseDaemon {
+    fn drop(&mut self) {
+        if let Err(error) = self.unmount() {
+            error!(?error, "failed to unmont fuse filesystem")
+        }
+    }
+}
diff --git a/tvix/castore/src/fs/inode_tracker.rs b/tvix/castore/src/fs/inode_tracker.rs
new file mode 100644
index 0000000000..4a8283b6b1
--- /dev/null
+++ b/tvix/castore/src/fs/inode_tracker.rs
@@ -0,0 +1,207 @@
+use std::{collections::HashMap, sync::Arc};
+
+use super::inodes::{DirectoryInodeData, InodeData};
+use crate::B3Digest;
+
+/// InodeTracker keeps track of inodes, stores data being these inodes and deals
+/// with inode allocation.
+pub struct InodeTracker {
+    data: HashMap<u64, Arc<InodeData>>,
+
+    // lookup table for blobs by their B3Digest
+    blob_digest_to_inode: HashMap<B3Digest, u64>,
+
+    // lookup table for symlinks by their target
+    symlink_target_to_inode: HashMap<bytes::Bytes, u64>,
+
+    // lookup table for directories by their B3Digest.
+    // Note the corresponding directory may not be present in data yet.
+    directory_digest_to_inode: HashMap<B3Digest, u64>,
+
+    // the next inode to allocate
+    next_inode: u64,
+}
+
+impl Default for InodeTracker {
+    fn default() -> Self {
+        Self {
+            data: Default::default(),
+
+            blob_digest_to_inode: Default::default(),
+            symlink_target_to_inode: Default::default(),
+            directory_digest_to_inode: Default::default(),
+
+            next_inode: 2,
+        }
+    }
+}
+
+impl InodeTracker {
+    // Retrieves data for a given inode, if it exists.
+    pub fn get(&self, ino: u64) -> Option<Arc<InodeData>> {
+        self.data.get(&ino).cloned()
+    }
+
+    // Replaces data for a given inode.
+    // Panics if the inode doesn't already exist.
+    pub fn replace(&mut self, ino: u64, data: Arc<InodeData>) {
+        if self.data.insert(ino, data).is_none() {
+            panic!("replace called on unknown inode");
+        }
+    }
+
+    // Stores data and returns the inode for it.
+    // In case an inode has already been allocated for the same data, that inode
+    // is returned, otherwise a new one is allocated.
+    // In case data is a [InodeData::Directory], inodes for all items are looked
+    // up
+    pub fn put(&mut self, data: InodeData) -> u64 {
+        match data {
+            InodeData::Regular(ref digest, _, _) => {
+                match self.blob_digest_to_inode.get(digest) {
+                    Some(found_ino) => {
+                        // We already have it, return the inode.
+                        *found_ino
+                    }
+                    None => self.insert_and_increment(data),
+                }
+            }
+            InodeData::Symlink(ref target) => {
+                match self.symlink_target_to_inode.get(target) {
+                    Some(found_ino) => {
+                        // We already have it, return the inode.
+                        *found_ino
+                    }
+                    None => self.insert_and_increment(data),
+                }
+            }
+            InodeData::Directory(DirectoryInodeData::Sparse(ref digest, _size)) => {
+                // check the lookup table if the B3Digest is known.
+                match self.directory_digest_to_inode.get(digest) {
+                    Some(found_ino) => {
+                        // We already have it, return the inode.
+                        *found_ino
+                    }
+                    None => {
+                        // insert and return the inode
+                        self.insert_and_increment(data)
+                    }
+                }
+            }
+            // Inserting [DirectoryInodeData::Populated] doesn't normally happen,
+            // only via [replace].
+            InodeData::Directory(DirectoryInodeData::Populated(..)) => {
+                unreachable!("should never be called with DirectoryInodeData::Populated")
+            }
+        }
+    }
+
+    // Inserts the data and returns the inode it was stored at, while
+    // incrementing next_inode.
+    fn insert_and_increment(&mut self, data: InodeData) -> u64 {
+        let ino = self.next_inode;
+        // insert into lookup tables
+        match data {
+            InodeData::Regular(ref digest, _, _) => {
+                self.blob_digest_to_inode.insert(digest.clone(), ino);
+            }
+            InodeData::Symlink(ref target) => {
+                self.symlink_target_to_inode.insert(target.clone(), ino);
+            }
+            InodeData::Directory(DirectoryInodeData::Sparse(ref digest, _size)) => {
+                self.directory_digest_to_inode.insert(digest.clone(), ino);
+            }
+            // This is currently not used outside test fixtures.
+            // Usually a [DirectoryInodeData::Sparse] is inserted and later
+            // "upgraded" with more data.
+            // However, as a future optimization, a lookup for a PathInfo could trigger a
+            // [DirectoryService::get_recursive()] request that "forks into
+            // background" and prepopulates all Directories in a closure.
+            InodeData::Directory(DirectoryInodeData::Populated(ref digest, _)) => {
+                self.directory_digest_to_inode.insert(digest.clone(), ino);
+            }
+        }
+        // Insert data
+        self.data.insert(ino, Arc::new(data));
+
+        // increment inode counter and return old inode.
+        self.next_inode += 1;
+        ino
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::fixtures;
+
+    use super::InodeData;
+    use super::InodeTracker;
+
+    /// Getting something non-existent should be none
+    #[test]
+    fn get_nonexistent() {
+        let inode_tracker = InodeTracker::default();
+        assert!(inode_tracker.get(1).is_none());
+    }
+
+    /// Put of a regular file should allocate a uid, which should be the same when inserting again.
+    #[test]
+    fn put_regular() {
+        let mut inode_tracker = InodeTracker::default();
+        let f = InodeData::Regular(
+            fixtures::BLOB_A_DIGEST.clone(),
+            fixtures::BLOB_A.len() as u64,
+            false,
+        );
+
+        // put it in
+        let ino = inode_tracker.put(f.clone());
+
+        // a get should return the right data
+        let data = inode_tracker.get(ino).expect("must be some");
+        match *data {
+            InodeData::Regular(ref digest, _, _) => {
+                assert_eq!(&fixtures::BLOB_A_DIGEST.clone(), digest);
+            }
+            InodeData::Symlink(_) | InodeData::Directory(..) => panic!("wrong type"),
+        }
+
+        // another put should return the same ino
+        assert_eq!(ino, inode_tracker.put(f));
+
+        // inserting another file should return a different ino
+        assert_ne!(
+            ino,
+            inode_tracker.put(InodeData::Regular(
+                fixtures::BLOB_B_DIGEST.clone(),
+                fixtures::BLOB_B.len() as u64,
+                false,
+            ))
+        );
+    }
+
+    // Put of a symlink should allocate a uid, which should be the same when inserting again
+    #[test]
+    fn put_symlink() {
+        let mut inode_tracker = InodeTracker::default();
+        let f = InodeData::Symlink("target".into());
+
+        // put it in
+        let ino = inode_tracker.put(f.clone());
+
+        // a get should return the right data
+        let data = inode_tracker.get(ino).expect("must be some");
+        match *data {
+            InodeData::Symlink(ref target) => {
+                assert_eq!(b"target".to_vec(), *target);
+            }
+            InodeData::Regular(..) | InodeData::Directory(..) => panic!("wrong type"),
+        }
+
+        // another put should return the same ino
+        assert_eq!(ino, inode_tracker.put(f));
+
+        // inserting another file should return a different ino
+        assert_ne!(ino, inode_tracker.put(InodeData::Symlink("target2".into())));
+    }
+}
diff --git a/tvix/castore/src/fs/inodes.rs b/tvix/castore/src/fs/inodes.rs
new file mode 100644
index 0000000000..bdd4595434
--- /dev/null
+++ b/tvix/castore/src/fs/inodes.rs
@@ -0,0 +1,96 @@
+//! This module contains all the data structures used to track information
+//! about inodes, which present tvix-castore nodes in a filesystem.
+use std::time::Duration;
+
+use bytes::Bytes;
+
+use crate::proto as castorepb;
+use crate::B3Digest;
+
+#[derive(Clone, Debug)]
+pub enum InodeData {
+    Regular(B3Digest, u64, bool),  // digest, size, executable
+    Symlink(bytes::Bytes),         // target
+    Directory(DirectoryInodeData), // either [DirectoryInodeData:Sparse] or [DirectoryInodeData:Populated]
+}
+
+/// This encodes the two different states of [InodeData::Directory].
+/// Either the data still is sparse (we only saw a [castorepb::DirectoryNode],
+/// but didn't fetch the [castorepb::Directory] struct yet, or we processed a
+/// lookup and did fetch the data.
+#[derive(Clone, Debug)]
+pub enum DirectoryInodeData {
+    Sparse(B3Digest, u64),                                  // digest, size
+    Populated(B3Digest, Vec<(u64, castorepb::node::Node)>), // [(child_inode, node)]
+}
+
+impl InodeData {
+    /// Constructs a new InodeData by consuming a [Node].
+    /// It splits off the orginal name, so it can be used later.
+    pub fn from_node(node: castorepb::node::Node) -> (Self, Bytes) {
+        match node {
+            castorepb::node::Node::Directory(n) => (
+                Self::Directory(DirectoryInodeData::Sparse(
+                    n.digest.try_into().unwrap(),
+                    n.size,
+                )),
+                n.name,
+            ),
+            castorepb::node::Node::File(n) => (
+                Self::Regular(n.digest.try_into().unwrap(), n.size, n.executable),
+                n.name,
+            ),
+            castorepb::node::Node::Symlink(n) => (Self::Symlink(n.target), n.name),
+        }
+    }
+
+    pub fn as_fuse_file_attr(&self, inode: u64) -> fuse_backend_rs::abi::fuse_abi::Attr {
+        fuse_backend_rs::abi::fuse_abi::Attr {
+            ino: inode,
+            // FUTUREWORK: play with this numbers, as it affects read sizes for client applications.
+            blocks: 1024,
+            size: match self {
+                InodeData::Regular(_, size, _) => *size,
+                InodeData::Symlink(target) => target.len() as u64,
+                InodeData::Directory(DirectoryInodeData::Sparse(_, size)) => *size,
+                InodeData::Directory(DirectoryInodeData::Populated(_, ref children)) => {
+                    children.len() as u64
+                }
+            },
+            mode: self.as_fuse_type() | self.mode(),
+            ..Default::default()
+        }
+    }
+
+    fn mode(&self) -> u32 {
+        match self {
+            InodeData::Regular(_, _, false) | InodeData::Symlink(_) => 0o444,
+            InodeData::Regular(_, _, true) | InodeData::Directory(_) => 0o555,
+        }
+    }
+
+    pub fn as_fuse_entry(&self, inode: u64) -> fuse_backend_rs::api::filesystem::Entry {
+        fuse_backend_rs::api::filesystem::Entry {
+            inode,
+            attr: self.as_fuse_file_attr(inode).into(),
+            attr_timeout: Duration::MAX,
+            entry_timeout: Duration::MAX,
+            ..Default::default()
+        }
+    }
+
+    /// Returns the u32 fuse type
+    pub fn as_fuse_type(&self) -> u32 {
+        #[allow(clippy::let_and_return)]
+        let ty = match self {
+            InodeData::Regular(_, _, _) => libc::S_IFREG,
+            InodeData::Symlink(_) => libc::S_IFLNK,
+            InodeData::Directory(_) => libc::S_IFDIR,
+        };
+        // libc::S_IFDIR is u32 on Linux and u16 on MacOS
+        #[cfg(target_os = "macos")]
+        let ty = ty as u32;
+
+        ty
+    }
+}
diff --git a/tvix/castore/src/fs/mod.rs b/tvix/castore/src/fs/mod.rs
new file mode 100644
index 0000000000..826523131f
--- /dev/null
+++ b/tvix/castore/src/fs/mod.rs
@@ -0,0 +1,877 @@
+mod file_attr;
+mod inode_tracker;
+mod inodes;
+mod root_nodes;
+
+#[cfg(feature = "fuse")]
+pub mod fuse;
+
+#[cfg(feature = "virtiofs")]
+pub mod virtiofs;
+
+#[cfg(test)]
+mod tests;
+
+pub use self::root_nodes::RootNodes;
+use self::{
+    file_attr::ROOT_FILE_ATTR,
+    inode_tracker::InodeTracker,
+    inodes::{DirectoryInodeData, InodeData},
+};
+use crate::proto as castorepb;
+use crate::{
+    blobservice::{BlobReader, BlobService},
+    directoryservice::DirectoryService,
+    proto::{node::Node, NamedNode},
+    B3Digest,
+};
+use bstr::ByteVec;
+use bytes::Bytes;
+use fuse_backend_rs::abi::fuse_abi::{stat64, OpenOptions};
+use fuse_backend_rs::api::filesystem::{
+    Context, FileSystem, FsOptions, GetxattrReply, ListxattrReply, ROOT_ID,
+};
+use futures::StreamExt;
+use parking_lot::RwLock;
+use std::sync::Mutex;
+use std::{
+    collections::HashMap,
+    io,
+    sync::atomic::AtomicU64,
+    sync::{atomic::Ordering, Arc},
+    time::Duration,
+};
+use std::{ffi::CStr, io::Cursor};
+use tokio::{
+    io::{AsyncReadExt, AsyncSeekExt},
+    sync::mpsc,
+};
+use tracing::{debug, error, instrument, warn, Span};
+
+/// This implements a read-only FUSE filesystem for a tvix-store
+/// with the passed [BlobService], [DirectoryService] and [RootNodes].
+///
+/// Linux uses inodes in filesystems. When implementing FUSE, most calls are
+/// *for* a given inode.
+///
+/// This means, we need to have a stable mapping of inode numbers to the
+/// corresponding store nodes.
+///
+/// We internally delegate all inode allocation and state keeping to the
+/// inode tracker.
+/// We store a mapping from currently "explored" names in the root to their
+/// inode.
+///
+/// There's some places where inodes are allocated / data inserted into
+/// the inode tracker, if not allocated before already:
+///  - Processing a `lookup` request, either in the mount root, or somewhere
+///    deeper.
+///  - Processing a `readdir` request
+///
+///  Things pointing to the same contents get the same inodes, irrespective of
+///  their own location.
+///  This means:
+///  - Symlinks with the same target will get the same inode.
+///  - Regular/executable files with the same contents will get the same inode
+///  - Directories with the same contents will get the same inode.
+///
+/// Due to the above being valid across the whole store, and considering the
+/// merkle structure is a DAG, not a tree, this also means we can't do "bucketed
+/// allocation", aka reserve Directory.size inodes for each directory node we
+/// explore.
+/// Tests for this live in the tvix-store crate.
+pub struct TvixStoreFs<BS, DS, RN> {
+    blob_service: BS,
+    directory_service: DS,
+    root_nodes_provider: RN,
+
+    /// Whether to (try) listing elements in the root.
+    list_root: bool,
+
+    /// Whether to expose blob and directory digests as extended attributes.
+    show_xattr: bool,
+
+    /// This maps a given basename in the root to the inode we allocated for the node.
+    root_nodes: RwLock<HashMap<Bytes, u64>>,
+
+    /// This keeps track of inodes and data alongside them.
+    inode_tracker: RwLock<InodeTracker>,
+
+    // FUTUREWORK: have a generic container type for dir/file handles and handle
+    // allocation.
+    /// Maps from the handle returned from an opendir to
+    /// This holds all opendir handles (for the root inode)
+    /// They point to the rx part of the channel producing the listing.
+    #[allow(clippy::type_complexity)]
+    dir_handles: RwLock<
+        HashMap<
+            u64,
+            (
+                Span,
+                Arc<Mutex<mpsc::Receiver<(usize, Result<Node, crate::Error>)>>>,
+            ),
+        >,
+    >,
+
+    next_dir_handle: AtomicU64,
+
+    /// This holds all open file handles
+    #[allow(clippy::type_complexity)]
+    file_handles: RwLock<HashMap<u64, (Span, Arc<Mutex<Box<dyn BlobReader>>>)>>,
+
+    next_file_handle: AtomicU64,
+
+    tokio_handle: tokio::runtime::Handle,
+}
+
+impl<BS, DS, RN> TvixStoreFs<BS, DS, RN>
+where
+    BS: AsRef<dyn BlobService> + Clone + Send,
+    DS: AsRef<dyn DirectoryService> + Clone + Send + 'static,
+    RN: RootNodes + Clone + 'static,
+{
+    pub fn new(
+        blob_service: BS,
+        directory_service: DS,
+        root_nodes_provider: RN,
+        list_root: bool,
+        show_xattr: bool,
+    ) -> Self {
+        Self {
+            blob_service,
+            directory_service,
+            root_nodes_provider,
+
+            list_root,
+            show_xattr,
+
+            root_nodes: RwLock::new(HashMap::default()),
+            inode_tracker: RwLock::new(Default::default()),
+
+            dir_handles: RwLock::new(Default::default()),
+            next_dir_handle: AtomicU64::new(1),
+
+            file_handles: RwLock::new(Default::default()),
+            next_file_handle: AtomicU64::new(1),
+            tokio_handle: tokio::runtime::Handle::current(),
+        }
+    }
+
+    /// Retrieves the inode for a given root node basename, if present.
+    /// This obtains a read lock on self.root_nodes.
+    fn get_inode_for_root_name(&self, name: &[u8]) -> Option<u64> {
+        self.root_nodes.read().get(name).cloned()
+    }
+
+    /// For a given inode, look up the given directory behind it (from
+    /// self.inode_tracker), and return its children.
+    /// The inode_tracker MUST know about this inode already, and it MUST point
+    /// to a [InodeData::Directory].
+    /// It is ok if it's a [DirectoryInodeData::Sparse] - in that case, a lookup
+    /// in self.directory_service is performed, and self.inode_tracker is updated with the
+    /// [DirectoryInodeData::Populated].
+    #[instrument(skip(self), err)]
+    fn get_directory_children(&self, ino: u64) -> io::Result<(B3Digest, Vec<(u64, Node)>)> {
+        let data = self.inode_tracker.read().get(ino).unwrap();
+        match *data {
+            // if it's populated already, return children.
+            InodeData::Directory(DirectoryInodeData::Populated(
+                ref parent_digest,
+                ref children,
+            )) => Ok((parent_digest.clone(), children.clone())),
+            // if it's sparse, fetch data using directory_service, populate child nodes
+            // and update it in [self.inode_tracker].
+            InodeData::Directory(DirectoryInodeData::Sparse(ref parent_digest, _)) => {
+                let directory = self
+                    .tokio_handle
+                    .block_on({
+                        let directory_service = self.directory_service.clone();
+                        let parent_digest = parent_digest.to_owned();
+                        async move { directory_service.as_ref().get(&parent_digest).await }
+                    })?
+                    .ok_or_else(|| {
+                        warn!(directory.digest=%parent_digest, "directory not found");
+                        // If the Directory can't be found, this is a hole, bail out.
+                        io::Error::from_raw_os_error(libc::EIO)
+                    })?;
+
+                // Turn the retrieved directory into a InodeData::Directory(DirectoryInodeData::Populated(..)),
+                // allocating inodes for the children on the way.
+                // FUTUREWORK: there's a bunch of cloning going on here, which we can probably avoid.
+                let children = {
+                    let mut inode_tracker = self.inode_tracker.write();
+
+                    let children: Vec<(u64, castorepb::node::Node)> = directory
+                        .nodes()
+                        .map(|child_node| {
+                            let (inode_data, _) = InodeData::from_node(child_node.clone());
+
+                            let child_ino = inode_tracker.put(inode_data);
+                            (child_ino, child_node)
+                        })
+                        .collect();
+
+                    // replace.
+                    inode_tracker.replace(
+                        ino,
+                        Arc::new(InodeData::Directory(DirectoryInodeData::Populated(
+                            parent_digest.clone(),
+                            children.clone(),
+                        ))),
+                    );
+
+                    children
+                };
+
+                Ok((parent_digest.clone(), children))
+            }
+            // if the parent inode was not a directory, this doesn't make sense
+            InodeData::Regular(..) | InodeData::Symlink(_) => {
+                Err(io::Error::from_raw_os_error(libc::ENOTDIR))
+            }
+        }
+    }
+
+    /// This will turn a lookup request for a name in the root to a ino and
+    /// [InodeData].
+    /// It will peek in [self.root_nodes], and then either look it up from
+    /// [self.inode_tracker],
+    /// or otherwise fetch from [self.root_nodes], and then insert into
+    /// [self.inode_tracker].
+    /// In the case the name can't be found, a libc::ENOENT is returned.
+    fn name_in_root_to_ino_and_data(
+        &self,
+        name: &std::ffi::CStr,
+    ) -> io::Result<(u64, Arc<InodeData>)> {
+        // Look up the inode for that root node.
+        // If there's one, [self.inode_tracker] MUST also contain the data,
+        // which we can then return.
+        if let Some(inode) = self.get_inode_for_root_name(name.to_bytes()) {
+            return Ok((
+                inode,
+                self.inode_tracker
+                    .read()
+                    .get(inode)
+                    .expect("must exist")
+                    .to_owned(),
+            ));
+        }
+
+        // We don't have it yet, look it up in [self.root_nodes].
+        match self.tokio_handle.block_on({
+            let root_nodes_provider = self.root_nodes_provider.clone();
+            async move { root_nodes_provider.get_by_basename(name.to_bytes()).await }
+        }) {
+            // if there was an error looking up the root node, propagate up an IO error.
+            Err(_e) => Err(io::Error::from_raw_os_error(libc::EIO)),
+            // the root node doesn't exist, so the file doesn't exist.
+            Ok(None) => Err(io::Error::from_raw_os_error(libc::ENOENT)),
+            // The root node does exist
+            Ok(Some(root_node)) => {
+                // The name must match what's passed in the lookup, otherwise this is also a ENOENT.
+                if root_node.get_name() != name.to_bytes() {
+                    debug!(root_node.name=?root_node.get_name(), found_node.name=%name.to_string_lossy(), "node name mismatch");
+                    return Err(io::Error::from_raw_os_error(libc::ENOENT));
+                }
+
+                // Let's check if someone else beat us to updating the inode tracker and
+                // root_nodes map. This avoids locking inode_tracker for writing.
+                if let Some(ino) = self.root_nodes.read().get(name.to_bytes()) {
+                    return Ok((
+                        *ino,
+                        self.inode_tracker.read().get(*ino).expect("must exist"),
+                    ));
+                }
+
+                // Only in case it doesn't, lock [self.root_nodes] and
+                // [self.inode_tracker] for writing.
+                let mut root_nodes = self.root_nodes.write();
+                let mut inode_tracker = self.inode_tracker.write();
+
+                // insert the (sparse) inode data and register in
+                // self.root_nodes.
+                let (inode_data, name) = InodeData::from_node(root_node);
+                let ino = inode_tracker.put(inode_data.clone());
+                root_nodes.insert(name, ino);
+
+                Ok((ino, Arc::new(inode_data)))
+            }
+        }
+    }
+}
+
+/// Buffer size of the channel providing nodes in the mount root
+const ROOT_NODES_BUFFER_SIZE: usize = 16;
+
+const XATTR_NAME_DIRECTORY_DIGEST: &[u8] = b"user.tvix.castore.directory.digest";
+const XATTR_NAME_BLOB_DIGEST: &[u8] = b"user.tvix.castore.blob.digest";
+
+impl<BS, DS, RN> FileSystem for TvixStoreFs<BS, DS, RN>
+where
+    BS: AsRef<dyn BlobService> + Clone + Send + 'static,
+    DS: AsRef<dyn DirectoryService> + Send + Clone + 'static,
+    RN: RootNodes + Clone + 'static,
+{
+    type Handle = u64;
+    type Inode = u64;
+
+    fn init(&self, _capable: FsOptions) -> io::Result<FsOptions> {
+        Ok(FsOptions::empty())
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode))]
+    fn getattr(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        _handle: Option<Self::Handle>,
+    ) -> io::Result<(stat64, Duration)> {
+        if inode == ROOT_ID {
+            return Ok((ROOT_FILE_ATTR.into(), Duration::MAX));
+        }
+
+        match self.inode_tracker.read().get(inode) {
+            None => Err(io::Error::from_raw_os_error(libc::ENOENT)),
+            Some(inode_data) => {
+                debug!(inode_data = ?inode_data, "found node");
+                Ok((inode_data.as_fuse_file_attr(inode).into(), Duration::MAX))
+            }
+        }
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.parent_inode = parent, rq.name = ?name))]
+    fn lookup(
+        &self,
+        _ctx: &Context,
+        parent: Self::Inode,
+        name: &std::ffi::CStr,
+    ) -> io::Result<fuse_backend_rs::api::filesystem::Entry> {
+        debug!("lookup");
+
+        // This goes from a parent inode to a node.
+        // - If the parent is [ROOT_ID], we need to check
+        //   [self.root_nodes] (fetching from a [RootNode] provider if needed)
+        // - Otherwise, lookup the parent in [self.inode_tracker] (which must be
+        //   a [InodeData::Directory]), and find the child with that name.
+        if parent == ROOT_ID {
+            let (ino, inode_data) = self.name_in_root_to_ino_and_data(name)?;
+
+            debug!(inode_data=?&inode_data, ino=ino, "Some");
+            return Ok(inode_data.as_fuse_entry(ino));
+        }
+        // This is the "lookup for "a" inside inode 42.
+        // We already know that inode 42 must be a directory.
+        let (parent_digest, children) = self.get_directory_children(parent)?;
+
+        Span::current().record("directory.digest", parent_digest.to_string());
+        // Search for that name in the list of children and return the FileAttrs.
+
+        // in the children, find the one with the desired name.
+        if let Some((child_ino, _)) = children.iter().find(|e| e.1.get_name() == name.to_bytes()) {
+            // lookup the child [InodeData] in [self.inode_tracker].
+            // We know the inodes for children have already been allocated.
+            let child_inode_data = self.inode_tracker.read().get(*child_ino).unwrap();
+
+            // Reply with the file attributes for the child.
+            // For child directories, we still have all data we need to reply.
+            Ok(child_inode_data.as_fuse_entry(*child_ino))
+        } else {
+            // Child not found, return ENOENT.
+            Err(io::Error::from_raw_os_error(libc::ENOENT))
+        }
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode))]
+    fn opendir(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        _flags: u32,
+    ) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
+        // In case opendir on the root is called, we provide the handle, as re-entering that listing is expensive.
+        // For all other directory inodes we just let readdir take care of it.
+        if inode == ROOT_ID {
+            if !self.list_root {
+                return Err(io::Error::from_raw_os_error(libc::EPERM)); // same error code as ipfs/kubo
+            }
+
+            let root_nodes_provider = self.root_nodes_provider.clone();
+            let (tx, rx) = mpsc::channel(ROOT_NODES_BUFFER_SIZE);
+
+            // This task will run in the background immediately and will exit
+            // after the stream ends or if we no longer want any more entries.
+            self.tokio_handle.spawn(async move {
+                let mut stream = root_nodes_provider.list().enumerate();
+                while let Some(node) = stream.next().await {
+                    if tx.send(node).await.is_err() {
+                        // If we get a send error, it means the sync code
+                        // doesn't want any more entries.
+                        break;
+                    }
+                }
+            });
+
+            // Put the rx part into [self.dir_handles].
+            // TODO: this will overflow after 2**64 operations,
+            // which is fine for now.
+            // See https://cl.tvl.fyi/c/depot/+/8834/comment/a6684ce0_d72469d1
+            // for the discussion on alternatives.
+            let dh = self.next_dir_handle.fetch_add(1, Ordering::SeqCst);
+
+            self.dir_handles
+                .write()
+                .insert(dh, (Span::current(), Arc::new(Mutex::new(rx))));
+
+            return Ok((
+                Some(dh),
+                fuse_backend_rs::api::filesystem::OpenOptions::empty(), // TODO: non-seekable
+            ));
+        }
+
+        Ok((None, OpenOptions::empty()))
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode, rq.handle = handle, rq.offset = offset), parent = self.dir_handles.read().get(&handle).and_then(|x| x.0.id()))]
+    fn readdir(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        handle: Self::Handle,
+        _size: u32,
+        offset: u64,
+        add_entry: &mut dyn FnMut(fuse_backend_rs::api::filesystem::DirEntry) -> io::Result<usize>,
+    ) -> io::Result<()> {
+        debug!("readdir");
+
+        if inode == ROOT_ID {
+            if !self.list_root {
+                return Err(io::Error::from_raw_os_error(libc::EPERM)); // same error code as ipfs/kubo
+            }
+
+            // get the handle from [self.dir_handles]
+            let (_span, rx) = match self.dir_handles.read().get(&handle) {
+                Some(rx) => rx.clone(),
+                None => {
+                    warn!("dir handle {} unknown", handle);
+                    return Err(io::Error::from_raw_os_error(libc::EIO));
+                }
+            };
+
+            let mut rx = rx
+                .lock()
+                .map_err(|_| crate::Error::StorageError("mutex poisoned".into()))?;
+
+            while let Some((i, n)) = rx.blocking_recv() {
+                let root_node = n.map_err(|e| {
+                    warn!("failed to retrieve root node: {}", e);
+                    io::Error::from_raw_os_error(libc::EIO)
+                })?;
+
+                let (inode_data, name) = InodeData::from_node(root_node);
+
+                // obtain the inode, or allocate a new one.
+                let ino = self.get_inode_for_root_name(&name).unwrap_or_else(|| {
+                    // insert the (sparse) inode data and register in
+                    // self.root_nodes.
+                    let ino = self.inode_tracker.write().put(inode_data.clone());
+                    self.root_nodes.write().insert(name.clone(), ino);
+                    ino
+                });
+
+                let written = add_entry(fuse_backend_rs::api::filesystem::DirEntry {
+                    ino,
+                    offset: offset + (i as u64) + 1,
+                    type_: inode_data.as_fuse_type(),
+                    name: &name,
+                })?;
+                // If the buffer is full, add_entry will return `Ok(0)`.
+                if written == 0 {
+                    break;
+                }
+            }
+            return Ok(());
+        }
+
+        // Non root-node case: lookup the children, or return an error if it's not a directory.
+        let (parent_digest, children) = self.get_directory_children(inode)?;
+        Span::current().record("directory.digest", parent_digest.to_string());
+
+        for (i, (ino, child_node)) in children.into_iter().skip(offset as usize).enumerate() {
+            let (inode_data, name) = InodeData::from_node(child_node);
+
+            // the second parameter will become the "offset" parameter on the next call.
+            let written = add_entry(fuse_backend_rs::api::filesystem::DirEntry {
+                ino,
+                offset: offset + (i as u64) + 1,
+                type_: inode_data.as_fuse_type(),
+                name: &name,
+            })?;
+            // If the buffer is full, add_entry will return `Ok(0)`.
+            if written == 0 {
+                break;
+            }
+        }
+
+        Ok(())
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode, rq.handle = handle), parent = self.dir_handles.read().get(&handle).and_then(|x| x.0.id()))]
+    fn readdirplus(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        handle: Self::Handle,
+        _size: u32,
+        offset: u64,
+        add_entry: &mut dyn FnMut(
+            fuse_backend_rs::api::filesystem::DirEntry,
+            fuse_backend_rs::api::filesystem::Entry,
+        ) -> io::Result<usize>,
+    ) -> io::Result<()> {
+        debug!("readdirplus");
+
+        if inode == ROOT_ID {
+            if !self.list_root {
+                return Err(io::Error::from_raw_os_error(libc::EPERM)); // same error code as ipfs/kubo
+            }
+
+            // get the handle from [self.dir_handles]
+            let (_span, rx) = match self.dir_handles.read().get(&handle) {
+                Some(rx) => rx.clone(),
+                None => {
+                    warn!("dir handle {} unknown", handle);
+                    return Err(io::Error::from_raw_os_error(libc::EIO));
+                }
+            };
+
+            let mut rx = rx
+                .lock()
+                .map_err(|_| crate::Error::StorageError("mutex poisoned".into()))?;
+
+            while let Some((i, n)) = rx.blocking_recv() {
+                let root_node = n.map_err(|e| {
+                    warn!("failed to retrieve root node: {}", e);
+                    io::Error::from_raw_os_error(libc::EPERM)
+                })?;
+
+                let (inode_data, name) = InodeData::from_node(root_node);
+
+                // obtain the inode, or allocate a new one.
+                let ino = self.get_inode_for_root_name(&name).unwrap_or_else(|| {
+                    // insert the (sparse) inode data and register in
+                    // self.root_nodes.
+                    let ino = self.inode_tracker.write().put(inode_data.clone());
+                    self.root_nodes.write().insert(name.clone(), ino);
+                    ino
+                });
+
+                let written = add_entry(
+                    fuse_backend_rs::api::filesystem::DirEntry {
+                        ino,
+                        offset: offset + (i as u64) + 1,
+                        type_: inode_data.as_fuse_type(),
+                        name: &name,
+                    },
+                    inode_data.as_fuse_entry(ino),
+                )?;
+                // If the buffer is full, add_entry will return `Ok(0)`.
+                if written == 0 {
+                    break;
+                }
+            }
+            return Ok(());
+        }
+
+        // Non root-node case: lookup the children, or return an error if it's not a directory.
+        let (parent_digest, children) = self.get_directory_children(inode)?;
+        Span::current().record("directory.digest", parent_digest.to_string());
+
+        for (i, (ino, child_node)) in children.into_iter().skip(offset as usize).enumerate() {
+            let (inode_data, name) = InodeData::from_node(child_node);
+
+            // the second parameter will become the "offset" parameter on the next call.
+            let written = add_entry(
+                fuse_backend_rs::api::filesystem::DirEntry {
+                    ino,
+                    offset: offset + (i as u64) + 1,
+                    type_: inode_data.as_fuse_type(),
+                    name: &name,
+                },
+                inode_data.as_fuse_entry(ino),
+            )?;
+            // If the buffer is full, add_entry will return `Ok(0)`.
+            if written == 0 {
+                break;
+            }
+        }
+
+        Ok(())
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode, rq.handle = handle), parent = self.dir_handles.read().get(&handle).and_then(|x| x.0.id()))]
+    fn releasedir(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        _flags: u32,
+        handle: Self::Handle,
+    ) -> io::Result<()> {
+        if inode == ROOT_ID {
+            // drop the rx part of the channel.
+            match self.dir_handles.write().remove(&handle) {
+                // drop it, which will close it.
+                Some(rx) => drop(rx),
+                None => {
+                    warn!("dir handle not found");
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode))]
+    fn open(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        _flags: u32,
+        _fuse_flags: u32,
+    ) -> io::Result<(
+        Option<Self::Handle>,
+        fuse_backend_rs::api::filesystem::OpenOptions,
+    )> {
+        if inode == ROOT_ID {
+            return Err(io::Error::from_raw_os_error(libc::ENOSYS));
+        }
+
+        // lookup the inode
+        match *self.inode_tracker.read().get(inode).unwrap() {
+            // read is invalid on non-files.
+            InodeData::Directory(..) | InodeData::Symlink(_) => {
+                warn!("is directory");
+                Err(io::Error::from_raw_os_error(libc::EISDIR))
+            }
+            InodeData::Regular(ref blob_digest, _blob_size, _) => {
+                Span::current().record("blob.digest", blob_digest.to_string());
+
+                match self.tokio_handle.block_on({
+                    let blob_service = self.blob_service.clone();
+                    let blob_digest = blob_digest.clone();
+                    async move { blob_service.as_ref().open_read(&blob_digest).await }
+                }) {
+                    Ok(None) => {
+                        warn!("blob not found");
+                        Err(io::Error::from_raw_os_error(libc::EIO))
+                    }
+                    Err(e) => {
+                        warn!(e=?e, "error opening blob");
+                        Err(io::Error::from_raw_os_error(libc::EIO))
+                    }
+                    Ok(Some(blob_reader)) => {
+                        // get a new file handle
+                        // TODO: this will overflow after 2**64 operations,
+                        // which is fine for now.
+                        // See https://cl.tvl.fyi/c/depot/+/8834/comment/a6684ce0_d72469d1
+                        // for the discussion on alternatives.
+                        let fh = self.next_file_handle.fetch_add(1, Ordering::SeqCst);
+
+                        self.file_handles
+                            .write()
+                            .insert(fh, (Span::current(), Arc::new(Mutex::new(blob_reader))));
+
+                        Ok((
+                            Some(fh),
+                            fuse_backend_rs::api::filesystem::OpenOptions::empty(),
+                        ))
+                    }
+                }
+            }
+        }
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode, rq.handle = handle), parent = self.file_handles.read().get(&handle).and_then(|x| x.0.id()))]
+    fn release(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        _flags: u32,
+        handle: Self::Handle,
+        _flush: bool,
+        _flock_release: bool,
+        _lock_owner: Option<u64>,
+    ) -> io::Result<()> {
+        match self.file_handles.write().remove(&handle) {
+            // drop the blob reader, which will close it.
+            Some(blob_reader) => drop(blob_reader),
+            None => {
+                // These might already be dropped if a read error occured.
+                warn!("file handle not found");
+            }
+        }
+
+        Ok(())
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode, rq.handle = handle, rq.offset = offset, rq.size = size), parent = self.file_handles.read().get(&handle).and_then(|x| x.0.id()))]
+    fn read(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        handle: Self::Handle,
+        w: &mut dyn fuse_backend_rs::api::filesystem::ZeroCopyWriter,
+        size: u32,
+        offset: u64,
+        _lock_owner: Option<u64>,
+        _flags: u32,
+    ) -> io::Result<usize> {
+        debug!("read");
+
+        // We need to take out the blob reader from self.file_handles, so we can
+        // interact with it in the separate task.
+        // On success, we pass it back out of the task, so we can put it back in self.file_handles.
+        let (_span, blob_reader) = self
+            .file_handles
+            .read()
+            .get(&handle)
+            .ok_or_else(|| {
+                warn!("file handle {} unknown", handle);
+                io::Error::from_raw_os_error(libc::EIO)
+            })
+            .cloned()?;
+
+        let mut blob_reader = blob_reader
+            .lock()
+            .map_err(|_| crate::Error::StorageError("mutex poisoned".into()))?;
+
+        let buf = self.tokio_handle.block_on(async move {
+            // seek to the offset specified, which is relative to the start of the file.
+            let pos = blob_reader
+                .seek(io::SeekFrom::Start(offset))
+                .await
+                .map_err(|e| {
+                    warn!("failed to seek to offset {}: {}", offset, e);
+                    io::Error::from_raw_os_error(libc::EIO)
+                })?;
+
+            debug_assert_eq!(offset, pos);
+
+            // As written in the fuse docs, read should send exactly the number
+            // of bytes requested except on EOF or error.
+
+            let mut buf: Vec<u8> = Vec::with_capacity(size as usize);
+
+            // copy things from the internal buffer into buf to fill it till up until size
+            tokio::io::copy(&mut blob_reader.as_mut().take(size as u64), &mut buf).await?;
+
+            Ok::<_, std::io::Error>(buf)
+        })?;
+
+        // We cannot use w.write() here, we're required to call write multiple
+        // times until we wrote the entirety of the buffer (which is `size`, except on EOF).
+        let buf_len = buf.len();
+        let bytes_written = io::copy(&mut Cursor::new(buf), w)?;
+        if bytes_written != buf_len as u64 {
+            error!(bytes_written=%bytes_written, "unable to write all of buf to kernel");
+            return Err(io::Error::from_raw_os_error(libc::EIO));
+        }
+
+        Ok(bytes_written as usize)
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode))]
+    fn readlink(&self, _ctx: &Context, inode: Self::Inode) -> io::Result<Vec<u8>> {
+        if inode == ROOT_ID {
+            return Err(io::Error::from_raw_os_error(libc::ENOSYS));
+        }
+
+        // lookup the inode
+        match *self.inode_tracker.read().get(inode).unwrap() {
+            InodeData::Directory(..) | InodeData::Regular(..) => {
+                Err(io::Error::from_raw_os_error(libc::EINVAL))
+            }
+            InodeData::Symlink(ref target) => Ok(target.to_vec()),
+        }
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode, name=?name))]
+    fn getxattr(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        name: &CStr,
+        size: u32,
+    ) -> io::Result<GetxattrReply> {
+        if !self.show_xattr {
+            return Err(io::Error::from_raw_os_error(libc::ENOSYS));
+        }
+
+        // Peek at the inode requested, and construct the response.
+        let digest_str = match *self
+            .inode_tracker
+            .read()
+            .get(inode)
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::ENODATA))?
+        {
+            InodeData::Directory(DirectoryInodeData::Sparse(ref digest, _))
+            | InodeData::Directory(DirectoryInodeData::Populated(ref digest, _))
+                if name.to_bytes() == XATTR_NAME_DIRECTORY_DIGEST =>
+            {
+                digest.to_string()
+            }
+            InodeData::Regular(ref digest, _, _) if name.to_bytes() == XATTR_NAME_BLOB_DIGEST => {
+                digest.to_string()
+            }
+            _ => {
+                return Err(io::Error::from_raw_os_error(libc::ENODATA));
+            }
+        };
+
+        if size == 0 {
+            Ok(GetxattrReply::Count(digest_str.len() as u32))
+        } else if size < digest_str.len() as u32 {
+            Err(io::Error::from_raw_os_error(libc::ERANGE))
+        } else {
+            Ok(GetxattrReply::Value(digest_str.into_bytes()))
+        }
+    }
+
+    #[tracing::instrument(skip_all, fields(rq.inode = inode))]
+    fn listxattr(
+        &self,
+        _ctx: &Context,
+        inode: Self::Inode,
+        size: u32,
+    ) -> io::Result<ListxattrReply> {
+        if !self.show_xattr {
+            return Err(io::Error::from_raw_os_error(libc::ENOSYS));
+        }
+
+        // determine the (\0-terminated list) to of xattr keys present, depending on the type of the inode.
+        let xattrs_names = {
+            let mut out = Vec::new();
+            if let Some(inode_data) = self.inode_tracker.read().get(inode) {
+                match *inode_data {
+                    InodeData::Directory(_) => {
+                        out.extend_from_slice(XATTR_NAME_DIRECTORY_DIGEST);
+                        out.push_byte(b'\x00');
+                    }
+                    InodeData::Regular(..) => {
+                        out.extend_from_slice(XATTR_NAME_BLOB_DIGEST);
+                        out.push_byte(b'\x00');
+                    }
+                    _ => {}
+                }
+            }
+            out
+        };
+
+        if size == 0 {
+            Ok(ListxattrReply::Count(xattrs_names.len() as u32))
+        } else if size < xattrs_names.len() as u32 {
+            Err(io::Error::from_raw_os_error(libc::ERANGE))
+        } else {
+            Ok(ListxattrReply::Names(xattrs_names.to_vec()))
+        }
+    }
+}
diff --git a/tvix/castore/src/fs/root_nodes.rs b/tvix/castore/src/fs/root_nodes.rs
new file mode 100644
index 0000000000..6609e049a1
--- /dev/null
+++ b/tvix/castore/src/fs/root_nodes.rs
@@ -0,0 +1,37 @@
+use std::collections::BTreeMap;
+
+use crate::{proto::node::Node, Error};
+use bytes::Bytes;
+use futures::stream::BoxStream;
+use tonic::async_trait;
+
+/// Provides an interface for looking up root nodes  in tvix-castore by given
+/// a lookup key (usually the basename), and optionally allow a listing.
+#[async_trait]
+pub trait RootNodes: Send + Sync {
+    /// Looks up a root CA node based on the basename of the node in the root
+    /// directory of the filesystem.
+    async fn get_by_basename(&self, name: &[u8]) -> Result<Option<Node>, Error>;
+
+    /// Lists all root CA nodes in the filesystem. An error can be returned
+    /// in case listing is not allowed
+    fn list(&self) -> BoxStream<Result<Node, Error>>;
+}
+
+#[async_trait]
+/// Implements RootNodes for something deref'ing to a BTreeMap of Nodes, where
+/// the key is the node name.
+impl<T> RootNodes for T
+where
+    T: AsRef<BTreeMap<Bytes, Node>> + Send + Sync,
+{
+    async fn get_by_basename(&self, name: &[u8]) -> Result<Option<Node>, Error> {
+        Ok(self.as_ref().get(name).cloned())
+    }
+
+    fn list(&self) -> BoxStream<Result<Node, Error>> {
+        Box::pin(tokio_stream::iter(
+            self.as_ref().iter().map(|(_, v)| Ok(v.clone())),
+        ))
+    }
+}
diff --git a/tvix/castore/src/fs/tests.rs b/tvix/castore/src/fs/tests.rs
new file mode 100644
index 0000000000..d6eeb8a411
--- /dev/null
+++ b/tvix/castore/src/fs/tests.rs
@@ -0,0 +1,1244 @@
+use bstr::ByteSlice;
+use bytes::Bytes;
+use std::{
+    collections::BTreeMap,
+    ffi::{OsStr, OsString},
+    io::{self, Cursor},
+    os::unix::{ffi::OsStrExt, fs::MetadataExt},
+    path::Path,
+    sync::Arc,
+};
+use tempfile::TempDir;
+use tokio_stream::{wrappers::ReadDirStream, StreamExt};
+
+use super::{fuse::FuseDaemon, TvixStoreFs};
+use crate::proto as castorepb;
+use crate::proto::node::Node;
+use crate::{
+    blobservice::{BlobService, MemoryBlobService},
+    directoryservice::{DirectoryService, MemoryDirectoryService},
+    fixtures,
+};
+
+const BLOB_A_NAME: &str = "00000000000000000000000000000000-test";
+const BLOB_B_NAME: &str = "55555555555555555555555555555555-test";
+const HELLOWORLD_BLOB_NAME: &str = "66666666666666666666666666666666-test";
+const SYMLINK_NAME: &str = "11111111111111111111111111111111-test";
+const SYMLINK_NAME2: &str = "44444444444444444444444444444444-test";
+const DIRECTORY_WITH_KEEP_NAME: &str = "22222222222222222222222222222222-test";
+const DIRECTORY_COMPLICATED_NAME: &str = "33333333333333333333333333333333-test";
+
+fn gen_svcs() -> (Arc<dyn BlobService>, Arc<dyn DirectoryService>) {
+    (
+        Arc::new(MemoryBlobService::default()) as Arc<dyn BlobService>,
+        Arc::new(MemoryDirectoryService::default()) as Arc<dyn DirectoryService>,
+    )
+}
+
+fn do_mount<P: AsRef<Path>, BS, DS>(
+    blob_service: BS,
+    directory_service: DS,
+    root_nodes: BTreeMap<bytes::Bytes, Node>,
+    mountpoint: P,
+    list_root: bool,
+    show_xattr: bool,
+) -> io::Result<FuseDaemon>
+where
+    BS: AsRef<dyn BlobService> + Send + Sync + Clone + 'static,
+    DS: AsRef<dyn DirectoryService> + Send + Sync + Clone + 'static,
+{
+    let fs = TvixStoreFs::new(
+        blob_service,
+        directory_service,
+        Arc::new(root_nodes),
+        list_root,
+        show_xattr,
+    );
+    FuseDaemon::new(Arc::new(fs), mountpoint.as_ref(), 4, false)
+}
+
+async fn populate_blob_a(
+    blob_service: &Arc<dyn BlobService>,
+    root_nodes: &mut BTreeMap<Bytes, Node>,
+) {
+    let mut bw = blob_service.open_write().await;
+    tokio::io::copy(&mut Cursor::new(fixtures::BLOB_A.to_vec()), &mut bw)
+        .await
+        .expect("must succeed uploading");
+    bw.close().await.expect("must succeed closing");
+
+    root_nodes.insert(
+        BLOB_A_NAME.into(),
+        Node::File(castorepb::FileNode {
+            name: BLOB_A_NAME.into(),
+            digest: fixtures::BLOB_A_DIGEST.clone().into(),
+            size: fixtures::BLOB_A.len() as u64,
+            executable: false,
+        }),
+    );
+}
+
+async fn populate_blob_b(
+    blob_service: &Arc<dyn BlobService>,
+    root_nodes: &mut BTreeMap<Bytes, Node>,
+) {
+    let mut bw = blob_service.open_write().await;
+    tokio::io::copy(&mut Cursor::new(fixtures::BLOB_B.to_vec()), &mut bw)
+        .await
+        .expect("must succeed uploading");
+    bw.close().await.expect("must succeed closing");
+
+    root_nodes.insert(
+        BLOB_B_NAME.into(),
+        Node::File(castorepb::FileNode {
+            name: BLOB_B_NAME.into(),
+            digest: fixtures::BLOB_B_DIGEST.clone().into(),
+            size: fixtures::BLOB_B.len() as u64,
+            executable: false,
+        }),
+    );
+}
+
+/// adds a blob containing helloworld and marks it as executable
+async fn populate_blob_helloworld(
+    blob_service: &Arc<dyn BlobService>,
+    root_nodes: &mut BTreeMap<Bytes, Node>,
+) {
+    let mut bw = blob_service.open_write().await;
+    tokio::io::copy(
+        &mut Cursor::new(fixtures::HELLOWORLD_BLOB_CONTENTS.to_vec()),
+        &mut bw,
+    )
+    .await
+    .expect("must succeed uploading");
+    bw.close().await.expect("must succeed closing");
+
+    root_nodes.insert(
+        HELLOWORLD_BLOB_NAME.into(),
+        Node::File(castorepb::FileNode {
+            name: HELLOWORLD_BLOB_NAME.into(),
+            digest: fixtures::HELLOWORLD_BLOB_DIGEST.clone().into(),
+            size: fixtures::HELLOWORLD_BLOB_CONTENTS.len() as u64,
+            executable: true,
+        }),
+    );
+}
+
+async fn populate_symlink(root_nodes: &mut BTreeMap<Bytes, Node>) {
+    root_nodes.insert(
+        SYMLINK_NAME.into(),
+        Node::Symlink(castorepb::SymlinkNode {
+            name: SYMLINK_NAME.into(),
+            target: BLOB_A_NAME.into(),
+        }),
+    );
+}
+
+/// This writes a symlink pointing to /nix/store/somewhereelse,
+/// which is the same symlink target as "aa" inside DIRECTORY_COMPLICATED.
+async fn populate_symlink2(root_nodes: &mut BTreeMap<Bytes, Node>) {
+    root_nodes.insert(
+        SYMLINK_NAME2.into(),
+        Node::Symlink(castorepb::SymlinkNode {
+            name: SYMLINK_NAME2.into(),
+            target: "/nix/store/somewhereelse".into(),
+        }),
+    );
+}
+
+async fn populate_directory_with_keep(
+    blob_service: &Arc<dyn BlobService>,
+    directory_service: &Arc<dyn DirectoryService>,
+    root_nodes: &mut BTreeMap<Bytes, Node>,
+) {
+    // upload empty blob
+    let mut bw = blob_service.open_write().await;
+    assert_eq!(
+        fixtures::EMPTY_BLOB_DIGEST.as_slice(),
+        bw.close().await.expect("must succeed closing").as_slice(),
+    );
+
+    // upload directory
+    directory_service
+        .put(fixtures::DIRECTORY_WITH_KEEP.clone())
+        .await
+        .expect("must succeed uploading");
+
+    root_nodes.insert(
+        DIRECTORY_WITH_KEEP_NAME.into(),
+        castorepb::node::Node::Directory(castorepb::DirectoryNode {
+            name: DIRECTORY_WITH_KEEP_NAME.into(),
+            digest: fixtures::DIRECTORY_WITH_KEEP.digest().into(),
+            size: fixtures::DIRECTORY_WITH_KEEP.size(),
+        }),
+    );
+}
+
+/// Create a root node for DIRECTORY_WITH_KEEP, but don't upload the Directory
+/// itself.
+async fn populate_directorynode_without_directory(root_nodes: &mut BTreeMap<Bytes, Node>) {
+    root_nodes.insert(
+        DIRECTORY_WITH_KEEP_NAME.into(),
+        castorepb::node::Node::Directory(castorepb::DirectoryNode {
+            name: DIRECTORY_WITH_KEEP_NAME.into(),
+            digest: fixtures::DIRECTORY_WITH_KEEP.digest().into(),
+            size: fixtures::DIRECTORY_WITH_KEEP.size(),
+        }),
+    );
+}
+
+/// Insert BLOB_A, but don't provide the blob .keep is pointing to.
+async fn populate_filenode_without_blob(root_nodes: &mut BTreeMap<Bytes, Node>) {
+    root_nodes.insert(
+        BLOB_A_NAME.into(),
+        Node::File(castorepb::FileNode {
+            name: BLOB_A_NAME.into(),
+            digest: fixtures::BLOB_A_DIGEST.clone().into(),
+            size: fixtures::BLOB_A.len() as u64,
+            executable: false,
+        }),
+    );
+}
+
+async fn populate_directory_complicated(
+    blob_service: &Arc<dyn BlobService>,
+    directory_service: &Arc<dyn DirectoryService>,
+    root_nodes: &mut BTreeMap<Bytes, Node>,
+) {
+    // upload empty blob
+    let mut bw = blob_service.open_write().await;
+    assert_eq!(
+        fixtures::EMPTY_BLOB_DIGEST.as_slice(),
+        bw.close().await.expect("must succeed closing").as_slice(),
+    );
+
+    // upload inner directory
+    directory_service
+        .put(fixtures::DIRECTORY_WITH_KEEP.clone())
+        .await
+        .expect("must succeed uploading");
+
+    // upload parent directory
+    directory_service
+        .put(fixtures::DIRECTORY_COMPLICATED.clone())
+        .await
+        .expect("must succeed uploading");
+
+    root_nodes.insert(
+        DIRECTORY_COMPLICATED_NAME.into(),
+        Node::Directory(castorepb::DirectoryNode {
+            name: DIRECTORY_COMPLICATED_NAME.into(),
+            digest: fixtures::DIRECTORY_COMPLICATED.digest().into(),
+            size: fixtures::DIRECTORY_COMPLICATED.size(),
+        }),
+    );
+}
+
+/// Ensure mounting itself doesn't fail
+#[tokio::test]
+async fn mount() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        BTreeMap::default(),
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    fuse_daemon.unmount().expect("unmount");
+}
+/// Ensure listing the root isn't allowed
+#[tokio::test]
+async fn root() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        BTreeMap::default(),
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    {
+        // read_dir fails (as opendir fails).
+        let err = tokio::fs::read_dir(tmpdir).await.expect_err("must fail");
+        assert_eq!(std::io::ErrorKind::PermissionDenied, err.kind());
+    }
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Ensure listing the root is allowed if configured explicitly
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn root_with_listing() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_blob_a(&blob_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        true, /* allow listing */
+        false,
+    )
+    .expect("must succeed");
+
+    {
+        // read_dir succeeds, but getting the first element will fail.
+        let mut it = ReadDirStream::new(tokio::fs::read_dir(tmpdir).await.expect("must succeed"));
+
+        let e = it
+            .next()
+            .await
+            .expect("must be some")
+            .expect("must succeed");
+
+        let metadata = e.metadata().await.expect("must succeed");
+        assert!(metadata.is_file());
+        assert!(metadata.permissions().readonly());
+        assert_eq!(fixtures::BLOB_A.len() as u64, metadata.len());
+    }
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Ensure we can stat a file at the root
+#[tokio::test]
+async fn stat_file_at_root() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_blob_a(&blob_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(BLOB_A_NAME);
+
+    // peek at the file metadata
+    let metadata = tokio::fs::metadata(p).await.expect("must succeed");
+
+    assert!(metadata.is_file());
+    assert!(metadata.permissions().readonly());
+    assert_eq!(fixtures::BLOB_A.len() as u64, metadata.len());
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Ensure we can read a file at the root
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn read_file_at_root() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_blob_a(&blob_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(BLOB_A_NAME);
+
+    // read the file contents
+    let data = tokio::fs::read(p).await.expect("must succeed");
+
+    // ensure size and contents match
+    assert_eq!(fixtures::BLOB_A.len(), data.len());
+    assert_eq!(fixtures::BLOB_A.to_vec(), data);
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Ensure we can read a large file at the root
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn read_large_file_at_root() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_blob_b(&blob_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(BLOB_B_NAME);
+    {
+        // peek at the file metadata
+        let metadata = tokio::fs::metadata(&p).await.expect("must succeed");
+
+        assert!(metadata.is_file());
+        assert!(metadata.permissions().readonly());
+        assert_eq!(fixtures::BLOB_B.len() as u64, metadata.len());
+    }
+
+    // read the file contents
+    let data = tokio::fs::read(p).await.expect("must succeed");
+
+    // ensure size and contents match
+    assert_eq!(fixtures::BLOB_B.len(), data.len());
+    assert_eq!(fixtures::BLOB_B.to_vec(), data);
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Read the target of a symlink
+#[tokio::test]
+async fn symlink_readlink() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_symlink(&mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(SYMLINK_NAME);
+
+    let target = tokio::fs::read_link(&p).await.expect("must succeed");
+    assert_eq!(BLOB_A_NAME, target.to_str().unwrap());
+
+    // peek at the file metadata, which follows symlinks.
+    // this must fail, as we didn't populate the target.
+    let e = tokio::fs::metadata(&p).await.expect_err("must fail");
+    assert_eq!(std::io::ErrorKind::NotFound, e.kind());
+
+    // peeking at the file metadata without following symlinks will succeed.
+    let metadata = tokio::fs::symlink_metadata(&p).await.expect("must succeed");
+    assert!(metadata.is_symlink());
+
+    // reading from the symlink (which follows) will fail, because the target doesn't exist.
+    let e = tokio::fs::read(p).await.expect_err("must fail");
+    assert_eq!(std::io::ErrorKind::NotFound, e.kind());
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Read and stat a regular file through a symlink pointing to it.
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn read_stat_through_symlink() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_blob_a(&blob_service, &mut root_nodes).await;
+    populate_symlink(&mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p_symlink = tmpdir.path().join(SYMLINK_NAME);
+    let p_blob = tmpdir.path().join(SYMLINK_NAME);
+
+    // peek at the file metadata, which follows symlinks.
+    // this must now return the same metadata as when statting at the target directly.
+    let metadata_symlink = tokio::fs::metadata(&p_symlink).await.expect("must succeed");
+    let metadata_blob = tokio::fs::metadata(&p_blob).await.expect("must succeed");
+    assert_eq!(metadata_blob.file_type(), metadata_symlink.file_type());
+    assert_eq!(metadata_blob.len(), metadata_symlink.len());
+
+    // reading from the symlink (which follows) will return the same data as if
+    // we were reading from the file directly.
+    assert_eq!(
+        tokio::fs::read(p_blob).await.expect("must succeed"),
+        tokio::fs::read(p_symlink).await.expect("must succeed"),
+    );
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Read a directory in the root, and validate some attributes.
+#[tokio::test]
+async fn read_stat_directory() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directory_with_keep(&blob_service, &directory_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(DIRECTORY_WITH_KEEP_NAME);
+
+    // peek at the metadata of the directory
+    let metadata = tokio::fs::metadata(p).await.expect("must succeed");
+    assert!(metadata.is_dir());
+    assert!(metadata.permissions().readonly());
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Read a directory and file in the root, and ensure the xattrs expose blob or
+/// directory digests.
+#[tokio::test]
+async fn xattr() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directory_with_keep(&blob_service, &directory_service, &mut root_nodes).await;
+    populate_blob_a(&blob_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        true, /* support xattr */
+    )
+    .expect("must succeed");
+
+    // peek at the directory
+    {
+        let p = tmpdir.path().join(DIRECTORY_WITH_KEEP_NAME);
+
+        let xattr_names: Vec<OsString> = xattr::list(&p).expect("must succeed").collect();
+        // There should be 1 key, XATTR_NAME_DIRECTORY_DIGEST.
+        assert_eq!(1, xattr_names.len(), "there should be 1 xattr name");
+        assert_eq!(
+            super::XATTR_NAME_DIRECTORY_DIGEST,
+            xattr_names.first().unwrap().as_encoded_bytes()
+        );
+
+        // The key should equal to the string-formatted b3 digest.
+        let val = xattr::get(&p, OsStr::from_bytes(super::XATTR_NAME_DIRECTORY_DIGEST))
+            .expect("must succeed")
+            .expect("must be some");
+        assert_eq!(
+            fixtures::DIRECTORY_WITH_KEEP
+                .digest()
+                .to_string()
+                .as_bytes()
+                .as_bstr(),
+            val.as_bstr()
+        );
+
+        // Reading another xattr key is gonna return None.
+        let val = xattr::get(&p, OsStr::from_bytes(b"user.cheesecake")).expect("must succeed");
+        assert_eq!(None, val);
+    }
+    // peek at the file
+    {
+        let p = tmpdir.path().join(BLOB_A_NAME);
+
+        let xattr_names: Vec<OsString> = xattr::list(&p).expect("must succeed").collect();
+        // There should be 1 key, XATTR_NAME_BLOB_DIGEST.
+        assert_eq!(1, xattr_names.len(), "there should be 1 xattr name");
+        assert_eq!(
+            super::XATTR_NAME_BLOB_DIGEST,
+            xattr_names.first().unwrap().as_encoded_bytes()
+        );
+
+        // The key should equal to the string-formatted b3 digest.
+        let val = xattr::get(&p, OsStr::from_bytes(super::XATTR_NAME_BLOB_DIGEST))
+            .expect("must succeed")
+            .expect("must be some");
+        assert_eq!(
+            fixtures::BLOB_A_DIGEST.to_string().as_bytes().as_bstr(),
+            val.as_bstr()
+        );
+
+        // Reading another xattr key is gonna return None.
+        let val = xattr::get(&p, OsStr::from_bytes(b"user.cheesecake")).expect("must succeed");
+        assert_eq!(None, val);
+    }
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+/// Read a blob inside a directory. This ensures we successfully populate directory data.
+async fn read_blob_inside_dir() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directory_with_keep(&blob_service, &directory_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(DIRECTORY_WITH_KEEP_NAME).join(".keep");
+
+    // peek at metadata.
+    let metadata = tokio::fs::metadata(&p).await.expect("must succeed");
+    assert!(metadata.is_file());
+    assert!(metadata.permissions().readonly());
+
+    // read from it
+    let data = tokio::fs::read(&p).await.expect("must succeed");
+    assert_eq!(fixtures::EMPTY_BLOB_CONTENTS.to_vec(), data);
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+/// Read a blob inside a directory inside a directory. This ensures we properly
+/// populate directories as we traverse down the structure.
+async fn read_blob_deep_inside_dir() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directory_complicated(&blob_service, &directory_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir
+        .path()
+        .join(DIRECTORY_COMPLICATED_NAME)
+        .join("keep")
+        .join(".keep");
+
+    // peek at metadata.
+    let metadata = tokio::fs::metadata(&p).await.expect("must succeed");
+    assert!(metadata.is_file());
+    assert!(metadata.permissions().readonly());
+
+    // read from it
+    let data = tokio::fs::read(&p).await.expect("must succeed");
+    assert_eq!(fixtures::EMPTY_BLOB_CONTENTS.to_vec(), data);
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Ensure readdir works.
+#[tokio::test]
+async fn readdir() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directory_complicated(&blob_service, &directory_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(DIRECTORY_COMPLICATED_NAME);
+
+    {
+        // read_dir should succeed. Collect all elements
+        let elements: Vec<_> =
+            ReadDirStream::new(tokio::fs::read_dir(p).await.expect("must succeed"))
+                .map(|e| e.expect("must not be err"))
+                .collect()
+                .await;
+
+        assert_eq!(3, elements.len(), "number of elements should be 3"); // rust skips . and ..
+
+        // We explicitly look at specific positions here, because we always emit
+        // them ordered.
+
+        // ".keep", 0 byte file.
+        let e = &elements[0];
+        assert_eq!(".keep", e.file_name());
+        assert!(e.file_type().await.expect("must succeed").is_file());
+        assert_eq!(0, e.metadata().await.expect("must succeed").len());
+
+        // "aa", symlink.
+        let e = &elements[1];
+        assert_eq!("aa", e.file_name());
+        assert!(e.file_type().await.expect("must succeed").is_symlink());
+
+        // "keep", directory
+        let e = &elements[2];
+        assert_eq!("keep", e.file_name());
+        assert!(e.file_type().await.expect("must succeed").is_dir());
+    }
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+#[tokio::test]
+/// Do a readdir deeper inside a directory, without doing readdir or stat in the parent directory.
+async fn readdir_deep() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directory_complicated(&blob_service, &directory_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(DIRECTORY_COMPLICATED_NAME).join("keep");
+
+    {
+        // read_dir should succeed. Collect all elements
+        let elements: Vec<_> =
+            ReadDirStream::new(tokio::fs::read_dir(p).await.expect("must succeed"))
+                .map(|e| e.expect("must not be err"))
+                .collect()
+                .await;
+
+        assert_eq!(1, elements.len(), "number of elements should be 1"); // rust skips . and ..
+
+        // ".keep", 0 byte file.
+        let e = &elements[0];
+        assert_eq!(".keep", e.file_name());
+        assert!(e.file_type().await.expect("must succeed").is_file());
+        assert_eq!(0, e.metadata().await.expect("must succeed").len());
+    }
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Check attributes match how they show up in /nix/store normally.
+#[tokio::test]
+async fn check_attributes() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_blob_a(&blob_service, &mut root_nodes).await;
+    populate_directory_with_keep(&blob_service, &directory_service, &mut root_nodes).await;
+    populate_symlink(&mut root_nodes).await;
+    populate_blob_helloworld(&blob_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p_file = tmpdir.path().join(BLOB_A_NAME);
+    let p_directory = tmpdir.path().join(DIRECTORY_WITH_KEEP_NAME);
+    let p_symlink = tmpdir.path().join(SYMLINK_NAME);
+    let p_executable_file = tmpdir.path().join(HELLOWORLD_BLOB_NAME);
+
+    // peek at metadata. We use symlink_metadata to ensure we don't traverse a symlink by accident.
+    let metadata_file = tokio::fs::symlink_metadata(&p_file)
+        .await
+        .expect("must succeed");
+    let metadata_executable_file = tokio::fs::symlink_metadata(&p_executable_file)
+        .await
+        .expect("must succeed");
+    let metadata_directory = tokio::fs::symlink_metadata(&p_directory)
+        .await
+        .expect("must succeed");
+    let metadata_symlink = tokio::fs::symlink_metadata(&p_symlink)
+        .await
+        .expect("must succeed");
+
+    // modes should match. We & with 0o777 to remove any higher bits.
+    assert_eq!(0o444, metadata_file.mode() & 0o777);
+    assert_eq!(0o555, metadata_executable_file.mode() & 0o777);
+    assert_eq!(0o555, metadata_directory.mode() & 0o777);
+    assert_eq!(0o444, metadata_symlink.mode() & 0o777);
+
+    // files should have the correct filesize
+    assert_eq!(fixtures::BLOB_A.len() as u64, metadata_file.len());
+    // directories should have their "size" as filesize
+    assert_eq!(
+        { fixtures::DIRECTORY_WITH_KEEP.size() },
+        metadata_directory.size()
+    );
+
+    for metadata in &[&metadata_file, &metadata_directory, &metadata_symlink] {
+        // uid and gid should be 0.
+        assert_eq!(0, metadata.uid());
+        assert_eq!(0, metadata.gid());
+
+        // all times should be set to the unix epoch.
+        assert_eq!(0, metadata.atime());
+        assert_eq!(0, metadata.mtime());
+        assert_eq!(0, metadata.ctime());
+        // crtime seems MacOS only
+    }
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+#[tokio::test]
+/// Ensure we allocate the same inodes for the same directory contents.
+/// $DIRECTORY_COMPLICATED_NAME/keep contains the same data as $DIRECTORY_WITH_KEEP.
+async fn compare_inodes_directories() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directory_with_keep(&blob_service, &directory_service, &mut root_nodes).await;
+    populate_directory_complicated(&blob_service, &directory_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p_dir_with_keep = tmpdir.path().join(DIRECTORY_WITH_KEEP_NAME);
+    let p_sibling_dir = tmpdir.path().join(DIRECTORY_COMPLICATED_NAME).join("keep");
+
+    // peek at metadata.
+    assert_eq!(
+        tokio::fs::metadata(p_dir_with_keep)
+            .await
+            .expect("must succeed")
+            .ino(),
+        tokio::fs::metadata(p_sibling_dir)
+            .await
+            .expect("must succeed")
+            .ino()
+    );
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Ensure we allocate the same inodes for the same directory contents.
+/// $DIRECTORY_COMPLICATED_NAME/keep/,keep contains the same data as $DIRECTORY_COMPLICATED_NAME/.keep
+#[tokio::test]
+async fn compare_inodes_files() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directory_complicated(&blob_service, &directory_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p_keep1 = tmpdir.path().join(DIRECTORY_COMPLICATED_NAME).join(".keep");
+    let p_keep2 = tmpdir
+        .path()
+        .join(DIRECTORY_COMPLICATED_NAME)
+        .join("keep")
+        .join(".keep");
+
+    // peek at metadata.
+    assert_eq!(
+        tokio::fs::metadata(p_keep1)
+            .await
+            .expect("must succeed")
+            .ino(),
+        tokio::fs::metadata(p_keep2)
+            .await
+            .expect("must succeed")
+            .ino()
+    );
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Ensure we allocate the same inode for symlinks pointing to the same targets.
+/// $DIRECTORY_COMPLICATED_NAME/aa points to the same target as SYMLINK_NAME2.
+#[tokio::test]
+async fn compare_inodes_symlinks() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directory_complicated(&blob_service, &directory_service, &mut root_nodes).await;
+    populate_symlink2(&mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p1 = tmpdir.path().join(DIRECTORY_COMPLICATED_NAME).join("aa");
+    let p2 = tmpdir.path().join(SYMLINK_NAME2);
+
+    // peek at metadata.
+    assert_eq!(
+        tokio::fs::symlink_metadata(p1)
+            .await
+            .expect("must succeed")
+            .ino(),
+        tokio::fs::symlink_metadata(p2)
+            .await
+            .expect("must succeed")
+            .ino()
+    );
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Check we match paths exactly.
+#[tokio::test]
+async fn read_wrong_paths_in_root() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_blob_a(&blob_service, &mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    // wrong name
+    assert!(
+        tokio::fs::metadata(tmpdir.path().join("00000000000000000000000000000000-tes"))
+            .await
+            .is_err()
+    );
+
+    // invalid hash
+    assert!(
+        tokio::fs::metadata(tmpdir.path().join("0000000000000000000000000000000-test"))
+            .await
+            .is_err()
+    );
+
+    // right name, must exist
+    assert!(
+        tokio::fs::metadata(tmpdir.path().join("00000000000000000000000000000000-test"))
+            .await
+            .is_ok()
+    );
+
+    // now wrong name with right hash still may not exist
+    assert!(
+        tokio::fs::metadata(tmpdir.path().join("00000000000000000000000000000000-tes"))
+            .await
+            .is_err()
+    );
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+/// Make sure writes are not allowed
+#[tokio::test]
+async fn disallow_writes() {
+    // https://plume.benboeckel.net/~/JustAnotherBlog/skipping-tests-in-rust
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let root_nodes = BTreeMap::default();
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(BLOB_A_NAME);
+    let e = tokio::fs::File::create(p).await.expect_err("must fail");
+
+    assert_eq!(Some(libc::EROFS), e.raw_os_error());
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+#[tokio::test]
+/// Ensure we get an IO error if the directory service does not have the Directory object.
+async fn missing_directory() {
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_directorynode_without_directory(&mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(DIRECTORY_WITH_KEEP_NAME);
+
+    {
+        // `stat` on the path should succeed, because it doesn't trigger the directory request.
+        tokio::fs::metadata(&p).await.expect("must succeed");
+
+        // However, calling either `readdir` or `stat` on a child should fail with an IO error.
+        // It fails when trying to pull the first entry, because we don't implement opendir separately
+        ReadDirStream::new(tokio::fs::read_dir(&p).await.unwrap())
+            .next()
+            .await
+            .expect("must be some")
+            .expect_err("must be err");
+
+        // rust currently sets e.kind() to Uncategorized, which isn't very
+        // helpful, so we don't look at the error more closely than that..
+        tokio::fs::metadata(p.join(".keep"))
+            .await
+            .expect_err("must fail");
+    }
+
+    fuse_daemon.unmount().expect("unmount");
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+/// Ensure we get an IO error if the blob service does not have the blob
+async fn missing_blob() {
+    if !std::path::Path::new("/dev/fuse").exists() {
+        eprintln!("skipping test");
+        return;
+    }
+    let tmpdir = TempDir::new().unwrap();
+
+    let (blob_service, directory_service) = gen_svcs();
+    let mut root_nodes = BTreeMap::default();
+
+    populate_filenode_without_blob(&mut root_nodes).await;
+
+    let mut fuse_daemon = do_mount(
+        blob_service,
+        directory_service,
+        root_nodes,
+        tmpdir.path(),
+        false,
+        false,
+    )
+    .expect("must succeed");
+
+    let p = tmpdir.path().join(BLOB_A_NAME);
+
+    {
+        // `stat` on the blob should succeed, because it doesn't trigger a request to the blob service.
+        tokio::fs::metadata(&p).await.expect("must succeed");
+
+        // However, calling read on the blob should fail.
+        // rust currently sets e.kind() to Uncategorized, which isn't very
+        // helpful, so we don't look at the error more closely than that..
+        tokio::fs::read(p).await.expect_err("must fail");
+    }
+
+    fuse_daemon.unmount().expect("unmount");
+}
diff --git a/tvix/castore/src/fs/virtiofs.rs b/tvix/castore/src/fs/virtiofs.rs
new file mode 100644
index 0000000000..d63e2f2bdd
--- /dev/null
+++ b/tvix/castore/src/fs/virtiofs.rs
@@ -0,0 +1,238 @@
+use std::{
+    convert, error, fmt, io,
+    ops::Deref,
+    path::Path,
+    sync::{Arc, MutexGuard, RwLock},
+};
+
+use fuse_backend_rs::{
+    api::{filesystem::FileSystem, server::Server},
+    transport::{FsCacheReqHandler, Reader, VirtioFsWriter},
+};
+use tracing::error;
+use vhost::vhost_user::{
+    Listener, SlaveFsCacheReq, VhostUserProtocolFeatures, VhostUserVirtioFeatures,
+};
+use vhost_user_backend::{VhostUserBackendMut, VhostUserDaemon, VringMutex, VringState, VringT};
+use virtio_bindings::bindings::virtio_ring::{
+    VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC,
+};
+use virtio_queue::QueueT;
+use vm_memory::{GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap};
+use vmm_sys_util::epoll::EventSet;
+
+const VIRTIO_F_VERSION_1: u32 = 32;
+const NUM_QUEUES: usize = 2;
+const QUEUE_SIZE: usize = 1024;
+
+#[derive(Debug)]
+enum Error {
+    /// Failed to handle non-input event.
+    HandleEventNotEpollIn,
+    /// Failed to handle unknown event.
+    HandleEventUnknownEvent,
+    /// Invalid descriptor chain.
+    InvalidDescriptorChain,
+    /// Failed to handle filesystem requests.
+    #[allow(dead_code)]
+    HandleRequests(fuse_backend_rs::Error),
+    /// Failed to construct new vhost user daemon.
+    NewDaemon,
+    /// Failed to start the vhost user daemon.
+    StartDaemon,
+    /// Failed to wait for the vhost user daemon.
+    WaitDaemon,
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "vhost_user_fs_error: {self:?}")
+    }
+}
+
+impl error::Error for Error {}
+
+impl convert::From<Error> for io::Error {
+    fn from(e: Error) -> Self {
+        io::Error::new(io::ErrorKind::Other, e)
+    }
+}
+
+struct VhostUserFsBackend<FS>
+where
+    FS: FileSystem + Send + Sync,
+{
+    server: Arc<Server<Arc<FS>>>,
+    event_idx: bool,
+    guest_mem: GuestMemoryAtomic<GuestMemoryMmap>,
+    cache_req: Option<SlaveFsCacheReq>,
+}
+
+impl<FS> VhostUserFsBackend<FS>
+where
+    FS: FileSystem + Send + Sync,
+{
+    fn process_queue(&mut self, vring: &mut MutexGuard<VringState>) -> std::io::Result<bool> {
+        let mut used_descs = false;
+
+        while let Some(desc_chain) = vring
+            .get_queue_mut()
+            .pop_descriptor_chain(self.guest_mem.memory())
+        {
+            let memory = desc_chain.memory();
+            let reader = Reader::from_descriptor_chain(memory, desc_chain.clone())
+                .map_err(|_| Error::InvalidDescriptorChain)?;
+            let writer = VirtioFsWriter::new(memory, desc_chain.clone())
+                .map_err(|_| Error::InvalidDescriptorChain)?;
+
+            self.server
+                .handle_message(
+                    reader,
+                    writer.into(),
+                    self.cache_req
+                        .as_mut()
+                        .map(|req| req as &mut dyn FsCacheReqHandler),
+                    None,
+                )
+                .map_err(Error::HandleRequests)?;
+
+            // TODO: Is len 0 correct?
+            if let Err(error) = vring
+                .get_queue_mut()
+                .add_used(memory, desc_chain.head_index(), 0)
+            {
+                error!(?error, "failed to add desc back to ring");
+            }
+
+            // TODO: What happens if we error out before here?
+            used_descs = true;
+        }
+
+        let needs_notification = if self.event_idx {
+            match vring
+                .get_queue_mut()
+                .needs_notification(self.guest_mem.memory().deref())
+            {
+                Ok(needs_notification) => needs_notification,
+                Err(error) => {
+                    error!(?error, "failed to check if queue needs notification");
+                    true
+                }
+            }
+        } else {
+            true
+        };
+
+        if needs_notification {
+            if let Err(error) = vring.signal_used_queue() {
+                error!(?error, "failed to signal used queue");
+            }
+        }
+
+        Ok(used_descs)
+    }
+}
+
+impl<FS> VhostUserBackendMut<VringMutex> for VhostUserFsBackend<FS>
+where
+    FS: FileSystem + Send + Sync,
+{
+    fn num_queues(&self) -> usize {
+        NUM_QUEUES
+    }
+
+    fn max_queue_size(&self) -> usize {
+        QUEUE_SIZE
+    }
+
+    fn features(&self) -> u64 {
+        1 << VIRTIO_F_VERSION_1
+            | 1 << VIRTIO_RING_F_INDIRECT_DESC
+            | 1 << VIRTIO_RING_F_EVENT_IDX
+            | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
+    }
+
+    fn protocol_features(&self) -> VhostUserProtocolFeatures {
+        VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::SLAVE_REQ
+    }
+
+    fn set_event_idx(&mut self, enabled: bool) {
+        self.event_idx = enabled;
+    }
+
+    fn update_memory(&mut self, _mem: GuestMemoryAtomic<GuestMemoryMmap>) -> std::io::Result<()> {
+        // This is what most the vhost user implementations do...
+        Ok(())
+    }
+
+    fn set_slave_req_fd(&mut self, cache_req: SlaveFsCacheReq) {
+        self.cache_req = Some(cache_req);
+    }
+
+    fn handle_event(
+        &mut self,
+        device_event: u16,
+        evset: vmm_sys_util::epoll::EventSet,
+        vrings: &[VringMutex],
+        _thread_id: usize,
+    ) -> std::io::Result<bool> {
+        if evset != EventSet::IN {
+            return Err(Error::HandleEventNotEpollIn.into());
+        }
+
+        let mut queue = match device_event {
+            // High priority queue
+            0 => vrings[0].get_mut(),
+            // Regurlar priority queue
+            1 => vrings[1].get_mut(),
+            _ => {
+                return Err(Error::HandleEventUnknownEvent.into());
+            }
+        };
+
+        if self.event_idx {
+            loop {
+                queue
+                    .get_queue_mut()
+                    .enable_notification(self.guest_mem.memory().deref())
+                    .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
+                if !self.process_queue(&mut queue)? {
+                    break;
+                }
+            }
+        } else {
+            self.process_queue(&mut queue)?;
+        }
+
+        Ok(false)
+    }
+}
+
+pub fn start_virtiofs_daemon<FS, P>(fs: FS, socket: P) -> io::Result<()>
+where
+    FS: FileSystem + Send + Sync + 'static,
+    P: AsRef<Path>,
+{
+    let guest_mem = GuestMemoryAtomic::new(GuestMemoryMmap::new());
+
+    let server = Arc::new(fuse_backend_rs::api::server::Server::new(Arc::new(fs)));
+
+    let backend = Arc::new(RwLock::new(VhostUserFsBackend {
+        server,
+        guest_mem: guest_mem.clone(),
+        event_idx: false,
+        cache_req: None,
+    }));
+
+    let listener = Listener::new(socket, true).unwrap();
+
+    let mut fs_daemon =
+        VhostUserDaemon::new(String::from("vhost-user-fs-tvix-store"), backend, guest_mem)
+            .map_err(|_| Error::NewDaemon)?;
+
+    fs_daemon.start(listener).map_err(|_| Error::StartDaemon)?;
+
+    fs_daemon.wait().map_err(|_| Error::WaitDaemon)?;
+
+    Ok(())
+}
diff --git a/tvix/castore/src/hashing_reader.rs b/tvix/castore/src/hashing_reader.rs
new file mode 100644
index 0000000000..7d78cae587
--- /dev/null
+++ b/tvix/castore/src/hashing_reader.rs
@@ -0,0 +1,89 @@
+use pin_project_lite::pin_project;
+use tokio::io::AsyncRead;
+
+pin_project! {
+    /// Wraps an existing AsyncRead, and allows querying for the digest of all
+    /// data read "through" it.
+    /// The hash function is configurable by type parameter.
+    pub struct HashingReader<R, H>
+    where
+        R: AsyncRead,
+        H: digest::Digest,
+    {
+        #[pin]
+        inner: R,
+        hasher: H,
+    }
+}
+
+pub type B3HashingReader<R> = HashingReader<R, blake3::Hasher>;
+
+impl<R, H> HashingReader<R, H>
+where
+    R: AsyncRead,
+    H: digest::Digest,
+{
+    pub fn from(r: R) -> Self {
+        Self {
+            inner: r,
+            hasher: H::new(),
+        }
+    }
+
+    /// Return the digest.
+    pub fn digest(self) -> digest::Output<H> {
+        self.hasher.finalize()
+    }
+}
+
+impl<R, H> tokio::io::AsyncRead for HashingReader<R, H>
+where
+    R: AsyncRead,
+    H: digest::Digest,
+{
+    fn poll_read(
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+        buf: &mut tokio::io::ReadBuf<'_>,
+    ) -> std::task::Poll<std::io::Result<()>> {
+        let buf_filled_len_before = buf.filled().len();
+
+        let this = self.project();
+        let ret = this.inner.poll_read(cx, buf);
+
+        // write everything new filled into the hasher.
+        this.hasher.update(&buf.filled()[buf_filled_len_before..]);
+
+        ret
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::io::Cursor;
+
+    use rstest::rstest;
+
+    use crate::fixtures::BLOB_A;
+    use crate::fixtures::BLOB_A_DIGEST;
+    use crate::fixtures::BLOB_B;
+    use crate::fixtures::BLOB_B_DIGEST;
+    use crate::fixtures::EMPTY_BLOB_DIGEST;
+    use crate::{B3Digest, B3HashingReader};
+
+    #[rstest]
+    #[case::blob_a(&BLOB_A, &BLOB_A_DIGEST)]
+    #[case::blob_b(&BLOB_B, &BLOB_B_DIGEST)]
+    #[case::empty_blob(&[], &EMPTY_BLOB_DIGEST)]
+    #[tokio::test]
+    async fn test_b3_hashing_reader(#[case] data: &[u8], #[case] b3_digest: &B3Digest) {
+        let r = Cursor::new(data);
+        let mut hr = B3HashingReader::from(r);
+
+        tokio::io::copy(&mut hr, &mut tokio::io::sink())
+            .await
+            .expect("read must succeed");
+
+        assert_eq!(*b3_digest, hr.digest().into());
+    }
+}
diff --git a/tvix/castore/src/import/archive.rs b/tvix/castore/src/import/archive.rs
new file mode 100644
index 0000000000..fb8ef9a50b
--- /dev/null
+++ b/tvix/castore/src/import/archive.rs
@@ -0,0 +1,458 @@
+//! Imports from an archive (tarballs)
+
+use std::collections::HashMap;
+use std::io::{Cursor, Write};
+use std::sync::Arc;
+
+use petgraph::graph::{DiGraph, NodeIndex};
+use petgraph::visit::{DfsPostOrder, EdgeRef};
+use petgraph::Direction;
+use tokio::io::AsyncRead;
+use tokio::sync::Semaphore;
+use tokio::task::JoinSet;
+use tokio_stream::StreamExt;
+use tokio_tar::Archive;
+use tokio_util::io::InspectReader;
+use tracing::{instrument, warn, Level};
+
+use crate::blobservice::BlobService;
+use crate::directoryservice::DirectoryService;
+use crate::import::{ingest_entries, IngestionEntry, IngestionError};
+use crate::proto::node::Node;
+use crate::B3Digest;
+
+type TarPathBuf = std::path::PathBuf;
+
+/// Files smaller than this threshold, in bytes, are uploaded to the [BlobService] in the
+/// background.
+///
+/// This is a u32 since we acquire a weighted semaphore using the size of the blob.
+/// [Semaphore::acquire_many_owned] takes a u32, so we need to ensure the size of
+/// the blob can be represented using a u32 and will not cause an overflow.
+const CONCURRENT_BLOB_UPLOAD_THRESHOLD: u32 = 1024 * 1024;
+
+/// The maximum amount of bytes allowed to be buffered in memory to perform async blob uploads.
+const MAX_TARBALL_BUFFER_SIZE: usize = 128 * 1024 * 1024;
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    #[error("unable to construct stream of entries: {0}")]
+    Entries(std::io::Error),
+
+    #[error("unable to read next entry: {0}")]
+    NextEntry(std::io::Error),
+
+    #[error("unable to read path for entry: {0}")]
+    PathRead(std::io::Error),
+
+    #[error("unable to convert path {0} for entry: {1}")]
+    PathConvert(TarPathBuf, std::io::Error),
+
+    #[error("unable to read size field for {0}: {1}")]
+    Size(TarPathBuf, std::io::Error),
+
+    #[error("unable to read mode field for {0}: {1}")]
+    Mode(TarPathBuf, std::io::Error),
+
+    #[error("unable to read link name field for {0}: {1}")]
+    LinkName(TarPathBuf, std::io::Error),
+
+    #[error("unable to read blob contents for {0}: {1}")]
+    BlobRead(TarPathBuf, std::io::Error),
+
+    // FUTUREWORK: proper error for blob finalize
+    #[error("unable to finalize blob {0}: {1}")]
+    BlobFinalize(TarPathBuf, std::io::Error),
+
+    #[error("unsupported tar entry {0} type: {1:?}")]
+    EntryType(TarPathBuf, tokio_tar::EntryType),
+
+    #[error("symlink missing target {0}")]
+    MissingSymlinkTarget(TarPathBuf),
+
+    #[error("unexpected number of top level directory entries")]
+    UnexpectedNumberOfTopLevelEntries,
+}
+
+/// Ingests elements from the given tar [`Archive`] into a the passed [`BlobService`] and
+/// [`DirectoryService`].
+#[instrument(skip_all, ret(level = Level::TRACE), err)]
+pub async fn ingest_archive<BS, DS, R>(
+    blob_service: BS,
+    directory_service: DS,
+    mut archive: Archive<R>,
+) -> Result<Node, IngestionError<Error>>
+where
+    BS: BlobService + Clone + 'static,
+    DS: AsRef<dyn DirectoryService>,
+    R: AsyncRead + Unpin,
+{
+    // Since tarballs can have entries in any arbitrary order, we need to
+    // buffer all of the directory metadata so we can reorder directory
+    // contents and entries to meet the requires of the castore.
+
+    // In the first phase, collect up all the regular files and symlinks.
+    let mut nodes = IngestionEntryGraph::new();
+
+    let semaphore = Arc::new(Semaphore::new(MAX_TARBALL_BUFFER_SIZE));
+    let mut async_blob_uploads: JoinSet<Result<(), Error>> = JoinSet::new();
+
+    let mut entries_iter = archive.entries().map_err(Error::Entries)?;
+    while let Some(mut entry) = entries_iter.try_next().await.map_err(Error::NextEntry)? {
+        let tar_path: TarPathBuf = entry.path().map_err(Error::PathRead)?.into();
+
+        // construct a castore PathBuf, which we use in the produced IngestionEntry.
+        let path = crate::path::PathBuf::from_host_path(tar_path.as_path(), true)
+            .map_err(|e| Error::PathConvert(tar_path.clone(), e))?;
+
+        let header = entry.header();
+        let entry = match header.entry_type() {
+            tokio_tar::EntryType::Regular
+            | tokio_tar::EntryType::GNUSparse
+            | tokio_tar::EntryType::Continuous => {
+                let header_size = header
+                    .size()
+                    .map_err(|e| Error::Size(tar_path.clone(), e))?;
+
+                // If the blob is small enough, read it off the wire, compute the digest,
+                // and upload it to the [BlobService] in the background.
+                let (size, digest) = if header_size <= CONCURRENT_BLOB_UPLOAD_THRESHOLD as u64 {
+                    let mut buffer = Vec::with_capacity(header_size as usize);
+                    let mut hasher = blake3::Hasher::new();
+                    let mut reader = InspectReader::new(&mut entry, |bytes| {
+                        hasher.write_all(bytes).unwrap();
+                    });
+
+                    // Ensure that we don't buffer into memory until we've acquired a permit.
+                    // This prevents consuming too much memory when performing concurrent
+                    // blob uploads.
+                    let permit = semaphore
+                        .clone()
+                        // This cast is safe because ensure the header_size is less than
+                        // CONCURRENT_BLOB_UPLOAD_THRESHOLD which is a u32.
+                        .acquire_many_owned(header_size as u32)
+                        .await
+                        .unwrap();
+                    let size = tokio::io::copy(&mut reader, &mut buffer)
+                        .await
+                        .map_err(|e| Error::Size(tar_path.clone(), e))?;
+
+                    let digest: B3Digest = hasher.finalize().as_bytes().into();
+
+                    {
+                        let blob_service = blob_service.clone();
+                        let digest = digest.clone();
+                        async_blob_uploads.spawn({
+                            let tar_path = tar_path.clone();
+                            async move {
+                                let mut writer = blob_service.open_write().await;
+
+                                tokio::io::copy(&mut Cursor::new(buffer), &mut writer)
+                                    .await
+                                    .map_err(|e| Error::BlobRead(tar_path.clone(), e))?;
+
+                                let blob_digest = writer
+                                    .close()
+                                    .await
+                                    .map_err(|e| Error::BlobFinalize(tar_path, e))?;
+
+                                assert_eq!(digest, blob_digest, "Tvix bug: blob digest mismatch");
+
+                                // Make sure we hold the permit until we finish writing the blob
+                                // to the [BlobService].
+                                drop(permit);
+                                Ok(())
+                            }
+                        });
+                    }
+
+                    (size, digest)
+                } else {
+                    let mut writer = blob_service.open_write().await;
+
+                    let size = tokio::io::copy(&mut entry, &mut writer)
+                        .await
+                        .map_err(|e| Error::BlobRead(tar_path.clone(), e))?;
+
+                    let digest = writer
+                        .close()
+                        .await
+                        .map_err(|e| Error::BlobFinalize(tar_path.clone(), e))?;
+
+                    (size, digest)
+                };
+
+                let executable = entry
+                    .header()
+                    .mode()
+                    .map_err(|e| Error::Mode(tar_path, e))?
+                    & 64
+                    != 0;
+
+                IngestionEntry::Regular {
+                    path,
+                    size,
+                    executable,
+                    digest,
+                }
+            }
+            tokio_tar::EntryType::Symlink => IngestionEntry::Symlink {
+                target: entry
+                    .link_name()
+                    .map_err(|e| Error::LinkName(tar_path.clone(), e))?
+                    .ok_or_else(|| Error::MissingSymlinkTarget(tar_path.clone()))?
+                    .into_owned()
+                    .into_os_string()
+                    .into_encoded_bytes(),
+                path,
+            },
+            // Push a bogus directory marker so we can make sure this directoy gets
+            // created. We don't know the digest and size until after reading the full
+            // tarball.
+            tokio_tar::EntryType::Directory => IngestionEntry::Dir { path },
+
+            tokio_tar::EntryType::XGlobalHeader | tokio_tar::EntryType::XHeader => continue,
+
+            entry_type => return Err(Error::EntryType(tar_path, entry_type).into()),
+        };
+
+        nodes.add(entry)?;
+    }
+
+    while let Some(result) = async_blob_uploads.join_next().await {
+        result.expect("task panicked")?;
+    }
+
+    let root_node = ingest_entries(
+        directory_service,
+        futures::stream::iter(nodes.finalize()?.into_iter().map(Ok)),
+    )
+    .await?;
+
+    Ok(root_node)
+}
+
+/// Keep track of the directory structure of a file tree being ingested. This is used
+/// for ingestion sources which do not provide any ordering or uniqueness guarantees
+/// like tarballs.
+///
+/// If we ingest multiple entries with the same paths and both entries are not directories,
+/// the newer entry will replace the latter entry, disconnecting the old node's children
+/// from the graph.
+///
+/// Once all nodes are ingested a call to [IngestionEntryGraph::finalize] will return
+/// a list of entries compute by performaing a DFS post order traversal of the graph
+/// from the top-level directory entry.
+///
+/// This expects the directory structure to contain a single top-level directory entry.
+/// An error is returned if this is not the case and ingestion will fail.
+struct IngestionEntryGraph {
+    graph: DiGraph<IngestionEntry, ()>,
+    path_to_index: HashMap<crate::path::PathBuf, NodeIndex>,
+    root_node: Option<NodeIndex>,
+}
+
+impl Default for IngestionEntryGraph {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl IngestionEntryGraph {
+    /// Creates a new ingestion entry graph.
+    pub fn new() -> Self {
+        IngestionEntryGraph {
+            graph: DiGraph::new(),
+            path_to_index: HashMap::new(),
+            root_node: None,
+        }
+    }
+
+    /// Adds a new entry to the graph. Parent directories are automatically inserted.
+    /// If a node exists in the graph with the same name as the new entry and both the old
+    /// and new nodes are not directories, the node is replaced and is disconnected from its
+    /// children.
+    pub fn add(&mut self, entry: IngestionEntry) -> Result<NodeIndex, Error> {
+        let path = entry.path().to_owned();
+
+        let index = match self.path_to_index.get(entry.path()) {
+            Some(&index) => {
+                // If either the old entry or new entry are not directories, we'll replace the old
+                // entry.
+                if !entry.is_dir() || !self.get_node(index).is_dir() {
+                    self.replace_node(index, entry);
+                }
+
+                index
+            }
+            None => self.graph.add_node(entry),
+        };
+
+        // A path with 1 component is the root node
+        if path.components().count() == 1 {
+            // We expect archives to contain a single root node, if there is another root node
+            // entry with a different path name, this is unsupported.
+            if let Some(root_node) = self.root_node {
+                if self.get_node(root_node).path() != &path {
+                    return Err(Error::UnexpectedNumberOfTopLevelEntries);
+                }
+            }
+
+            self.root_node = Some(index)
+        } else if let Some(parent_path) = path.parent() {
+            // Recursively add the parent node until it hits the root node.
+            let parent_index = self.add(IngestionEntry::Dir {
+                path: parent_path.to_owned(),
+            })?;
+
+            // Insert an edge from the parent directory to the child entry.
+            self.graph.add_edge(parent_index, index, ());
+        }
+
+        self.path_to_index.insert(path, index);
+
+        Ok(index)
+    }
+
+    /// Traverses the graph in DFS post order and collects the entries into a [Vec<IngestionEntry>].
+    ///
+    /// Unreachable parts of the graph are not included in the result.
+    pub fn finalize(self) -> Result<Vec<IngestionEntry>, Error> {
+        // There must be a root node.
+        let Some(root_node_index) = self.root_node else {
+            return Err(Error::UnexpectedNumberOfTopLevelEntries);
+        };
+
+        // The root node must be a directory.
+        if !self.get_node(root_node_index).is_dir() {
+            return Err(Error::UnexpectedNumberOfTopLevelEntries);
+        }
+
+        let mut traversal = DfsPostOrder::new(&self.graph, root_node_index);
+        let mut nodes = Vec::with_capacity(self.graph.node_count());
+        while let Some(node_index) = traversal.next(&self.graph) {
+            nodes.push(self.get_node(node_index).clone());
+        }
+
+        Ok(nodes)
+    }
+
+    /// Replaces the node with the specified entry. The node's children are disconnected.
+    ///
+    /// This should never be called if both the old and new nodes are directories.
+    fn replace_node(&mut self, index: NodeIndex, new_entry: IngestionEntry) {
+        let entry = self
+            .graph
+            .node_weight_mut(index)
+            .expect("Tvix bug: missing node entry");
+
+        debug_assert!(!(entry.is_dir() && new_entry.is_dir()));
+
+        // Replace the node itself.
+        warn!(
+            "saw duplicate entry in archive at path {:?}. old: {:?} new: {:?}",
+            entry.path(),
+            &entry,
+            &new_entry
+        );
+        *entry = new_entry;
+
+        // Remove any outgoing edges to disconnect the old node's children.
+        let edges = self
+            .graph
+            .edges_directed(index, Direction::Outgoing)
+            .map(|edge| edge.id())
+            .collect::<Vec<_>>();
+        for edge in edges {
+            self.graph.remove_edge(edge);
+        }
+    }
+
+    fn get_node(&self, index: NodeIndex) -> &IngestionEntry {
+        self.graph
+            .node_weight(index)
+            .expect("Tvix bug: missing node entry")
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::import::IngestionEntry;
+    use crate::B3Digest;
+
+    use super::{Error, IngestionEntryGraph};
+
+    use lazy_static::lazy_static;
+    use rstest::rstest;
+
+    lazy_static! {
+        pub static ref EMPTY_DIGEST: B3Digest = blake3::hash(&[]).as_bytes().into();
+        pub static ref DIR_A: IngestionEntry = IngestionEntry::Dir {
+            path: "a".parse().unwrap()
+        };
+        pub static ref DIR_B: IngestionEntry = IngestionEntry::Dir {
+            path: "b".parse().unwrap()
+        };
+        pub static ref DIR_A_B: IngestionEntry = IngestionEntry::Dir {
+            path: "a/b".parse().unwrap()
+        };
+        pub static ref FILE_A: IngestionEntry = IngestionEntry::Regular {
+            path: "a".parse().unwrap(),
+            size: 0,
+            executable: false,
+            digest: EMPTY_DIGEST.clone(),
+        };
+        pub static ref FILE_A_B: IngestionEntry = IngestionEntry::Regular {
+            path: "a/b".parse().unwrap(),
+            size: 0,
+            executable: false,
+            digest: EMPTY_DIGEST.clone(),
+        };
+        pub static ref FILE_A_B_C: IngestionEntry = IngestionEntry::Regular {
+            path: "a/b/c".parse().unwrap(),
+            size: 0,
+            executable: false,
+            digest: EMPTY_DIGEST.clone(),
+        };
+    }
+
+    #[rstest]
+    #[case::implicit_directories(&[&*FILE_A_B_C], &[&*FILE_A_B_C, &*DIR_A_B, &*DIR_A])]
+    #[case::explicit_directories(&[&*DIR_A, &*DIR_A_B, &*FILE_A_B_C], &[&*FILE_A_B_C, &*DIR_A_B, &*DIR_A])]
+    #[case::inaccesible_tree(&[&*DIR_A, &*DIR_A_B, &*FILE_A_B], &[&*FILE_A_B, &*DIR_A])]
+    fn node_ingestion_success(
+        #[case] in_entries: &[&IngestionEntry],
+        #[case] exp_entries: &[&IngestionEntry],
+    ) {
+        let mut nodes = IngestionEntryGraph::new();
+
+        for entry in in_entries {
+            nodes.add((*entry).clone()).expect("failed to add entry");
+        }
+
+        let entries = nodes.finalize().expect("invalid entries");
+
+        let exp_entries: Vec<IngestionEntry> =
+            exp_entries.iter().map(|entry| (*entry).clone()).collect();
+
+        assert_eq!(entries, exp_entries);
+    }
+
+    #[rstest]
+    #[case::no_top_level_entries(&[], Error::UnexpectedNumberOfTopLevelEntries)]
+    #[case::multiple_top_level_dirs(&[&*DIR_A, &*DIR_B], Error::UnexpectedNumberOfTopLevelEntries)]
+    #[case::top_level_file_entry(&[&*FILE_A], Error::UnexpectedNumberOfTopLevelEntries)]
+    fn node_ingestion_error(#[case] in_entries: &[&IngestionEntry], #[case] exp_error: Error) {
+        let mut nodes = IngestionEntryGraph::new();
+
+        let result = (|| {
+            for entry in in_entries {
+                nodes.add((*entry).clone())?;
+            }
+            nodes.finalize()
+        })();
+
+        let error = result.expect_err("expected error");
+        assert_eq!(error.to_string(), exp_error.to_string());
+    }
+}
diff --git a/tvix/castore/src/import/error.rs b/tvix/castore/src/import/error.rs
new file mode 100644
index 0000000000..e3fba617e0
--- /dev/null
+++ b/tvix/castore/src/import/error.rs
@@ -0,0 +1,20 @@
+use super::PathBuf;
+
+use crate::Error as CastoreError;
+
+/// Represents all error types that emitted by ingest_entries.
+/// It can represent errors uploading individual Directories and finalizing
+/// the upload.
+/// It also contains a generic error kind that'll carry ingestion-method
+/// specific errors.
+#[derive(Debug, thiserror::Error)]
+pub enum IngestionError<E: std::fmt::Display> {
+    #[error("error from producer: {0}")]
+    Producer(#[from] E),
+
+    #[error("failed to upload directory at {0}: {1}")]
+    UploadDirectoryError(PathBuf, CastoreError),
+
+    #[error("failed to finalize directory upload: {0}")]
+    FinalizeDirectoryUpload(CastoreError),
+}
diff --git a/tvix/castore/src/import/fs.rs b/tvix/castore/src/import/fs.rs
new file mode 100644
index 0000000000..b8cfac86f8
--- /dev/null
+++ b/tvix/castore/src/import/fs.rs
@@ -0,0 +1,185 @@
+//! Import from a real filesystem.
+
+use futures::stream::BoxStream;
+use futures::StreamExt;
+use std::fs::FileType;
+use std::os::unix::ffi::OsStringExt;
+use std::os::unix::fs::MetadataExt;
+use std::os::unix::fs::PermissionsExt;
+use tracing::instrument;
+use walkdir::DirEntry;
+use walkdir::WalkDir;
+
+use crate::blobservice::BlobService;
+use crate::directoryservice::DirectoryService;
+use crate::proto::node::Node;
+use crate::B3Digest;
+
+use super::ingest_entries;
+use super::IngestionEntry;
+use super::IngestionError;
+
+/// Ingests the contents at a given path into the tvix store, interacting with a [BlobService] and
+/// [DirectoryService]. It returns the root node or an error.
+///
+/// It does not follow symlinks at the root, they will be ingested as actual symlinks.
+///
+/// This function will walk the filesystem using `walkdir` and will consume
+/// `O(#number of entries)` space.
+#[instrument(skip(blob_service, directory_service), fields(path), err)]
+pub async fn ingest_path<BS, DS, P>(
+    blob_service: BS,
+    directory_service: DS,
+    path: P,
+) -> Result<Node, IngestionError<Error>>
+where
+    P: AsRef<std::path::Path> + std::fmt::Debug,
+    BS: BlobService + Clone,
+    DS: AsRef<dyn DirectoryService>,
+{
+    let iter = WalkDir::new(path.as_ref())
+        .follow_links(false)
+        .follow_root_links(false)
+        .contents_first(true)
+        .into_iter();
+
+    let entries = dir_entries_to_ingestion_stream(blob_service, iter, path.as_ref());
+    ingest_entries(directory_service, entries).await
+}
+
+/// Converts an iterator of [walkdir::DirEntry]s into a stream of ingestion entries.
+/// This can then be fed into [ingest_entries] to ingest all the entries into the castore.
+///
+/// The produced stream is buffered, so uploads can happen concurrently.
+///
+/// The root is the [Path] in the filesystem that is being ingested into the castore.
+pub fn dir_entries_to_ingestion_stream<'a, BS, I>(
+    blob_service: BS,
+    iter: I,
+    root: &'a std::path::Path,
+) -> BoxStream<'a, Result<IngestionEntry, Error>>
+where
+    BS: BlobService + Clone + 'a,
+    I: Iterator<Item = Result<DirEntry, walkdir::Error>> + Send + 'a,
+{
+    let prefix = root.parent().unwrap_or_else(|| std::path::Path::new(""));
+
+    Box::pin(
+        futures::stream::iter(iter)
+            .map(move |x| {
+                let blob_service = blob_service.clone();
+                async move {
+                    match x {
+                        Ok(dir_entry) => {
+                            dir_entry_to_ingestion_entry(blob_service, &dir_entry, prefix).await
+                        }
+                        Err(e) => Err(Error::Stat(
+                            prefix.to_path_buf(),
+                            e.into_io_error().expect("walkdir err must be some"),
+                        )),
+                    }
+                }
+            })
+            .buffered(50),
+    )
+}
+
+/// Converts a [walkdir::DirEntry] into an [IngestionEntry], uploading blobs to the
+/// provided [BlobService].
+///
+/// The prefix path is stripped from the path of each entry. This is usually the parent path
+/// of the path being ingested so that the last element of the stream only has one component.
+pub async fn dir_entry_to_ingestion_entry<BS>(
+    blob_service: BS,
+    entry: &DirEntry,
+    prefix: &std::path::Path,
+) -> Result<IngestionEntry, Error>
+where
+    BS: BlobService,
+{
+    let file_type = entry.file_type();
+
+    let fs_path = entry
+        .path()
+        .strip_prefix(prefix)
+        .expect("Tvix bug: failed to strip root path prefix");
+
+    // convert to castore PathBuf
+    let path = crate::path::PathBuf::from_host_path(fs_path, false)
+        .unwrap_or_else(|e| panic!("Tvix bug: walkdir direntry cannot be parsed: {}", e));
+
+    if file_type.is_dir() {
+        Ok(IngestionEntry::Dir { path })
+    } else if file_type.is_symlink() {
+        let target = std::fs::read_link(entry.path())
+            .map_err(|e| Error::Stat(entry.path().to_path_buf(), e))?
+            .into_os_string()
+            .into_vec();
+
+        Ok(IngestionEntry::Symlink { path, target })
+    } else if file_type.is_file() {
+        let metadata = entry
+            .metadata()
+            .map_err(|e| Error::Stat(entry.path().to_path_buf(), e.into()))?;
+
+        let digest = upload_blob(blob_service, entry.path().to_path_buf()).await?;
+
+        Ok(IngestionEntry::Regular {
+            path,
+            size: metadata.size(),
+            // If it's executable by the user, it'll become executable.
+            // This matches nix's dump() function behaviour.
+            executable: metadata.permissions().mode() & 64 != 0,
+            digest,
+        })
+    } else {
+        return Err(Error::FileType(fs_path.to_path_buf(), file_type));
+    }
+}
+
+/// Uploads the file at the provided [Path] the the [BlobService].
+#[instrument(skip(blob_service), fields(path), err)]
+async fn upload_blob<BS>(
+    blob_service: BS,
+    path: impl AsRef<std::path::Path>,
+) -> Result<B3Digest, Error>
+where
+    BS: BlobService,
+{
+    let mut file = match tokio::fs::File::open(path.as_ref()).await {
+        Ok(file) => file,
+        Err(e) => return Err(Error::BlobRead(path.as_ref().to_path_buf(), e)),
+    };
+
+    let mut writer = blob_service.open_write().await;
+
+    if let Err(e) = tokio::io::copy(&mut file, &mut writer).await {
+        return Err(Error::BlobRead(path.as_ref().to_path_buf(), e));
+    };
+
+    let digest = writer
+        .close()
+        .await
+        .map_err(|e| Error::BlobFinalize(path.as_ref().to_path_buf(), e))?;
+
+    Ok(digest)
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    #[error("unsupported file type at {0}: {1:?}")]
+    FileType(std::path::PathBuf, FileType),
+
+    #[error("unable to stat {0}: {1}")]
+    Stat(std::path::PathBuf, std::io::Error),
+
+    #[error("unable to open {0}: {1}")]
+    Open(std::path::PathBuf, std::io::Error),
+
+    #[error("unable to read {0}: {1}")]
+    BlobRead(std::path::PathBuf, std::io::Error),
+
+    // TODO: proper error for blob finalize
+    #[error("unable to finalize blob {0}: {1}")]
+    BlobFinalize(std::path::PathBuf, std::io::Error),
+}
diff --git a/tvix/castore/src/import/mod.rs b/tvix/castore/src/import/mod.rs
new file mode 100644
index 0000000000..53ebc2b339
--- /dev/null
+++ b/tvix/castore/src/import/mod.rs
@@ -0,0 +1,197 @@
+//! The main library function here is [ingest_entries], receiving a stream of
+//! [IngestionEntry].
+//!
+//! Specific implementations, such as ingesting from the filesystem, live in
+//! child modules.
+
+use crate::directoryservice::DirectoryPutter;
+use crate::directoryservice::DirectoryService;
+use crate::path::PathBuf;
+use crate::proto::node::Node;
+use crate::proto::Directory;
+use crate::proto::DirectoryNode;
+use crate::proto::FileNode;
+use crate::proto::SymlinkNode;
+use crate::B3Digest;
+use futures::{Stream, StreamExt};
+
+use tracing::Level;
+
+use std::collections::HashMap;
+use tracing::instrument;
+
+mod error;
+pub use error::IngestionError;
+
+pub mod archive;
+pub mod fs;
+
+/// Ingests [IngestionEntry] from the given stream into a the passed [DirectoryService].
+/// On success, returns the root [Node].
+///
+/// The stream must have the following invariants:
+/// - All children entries must come before their parents.
+/// - The last entry must be the root node which must have a single path component.
+/// - Every entry should have a unique path, and only consist of normal components.
+///   This means, no windows path prefixes, absolute paths, `.` or `..`.
+/// - All referenced directories must have an associated directory entry in the stream.
+///   This means if there is a file entry for `foo/bar`, there must also be a `foo` directory
+///   entry.
+///
+/// Internally we maintain a [HashMap] of [PathBuf] to partially populated [Directory] at that
+/// path. Once we receive an [IngestionEntry] for the directory itself, we remove it from the
+/// map and upload it to the [DirectoryService] through a lazily created [DirectoryPutter].
+///
+/// On success, returns the root node.
+#[instrument(skip_all, ret(level = Level::TRACE), err)]
+pub async fn ingest_entries<DS, S, E>(
+    directory_service: DS,
+    mut entries: S,
+) -> Result<Node, IngestionError<E>>
+where
+    DS: AsRef<dyn DirectoryService>,
+    S: Stream<Item = Result<IngestionEntry, E>> + Send + std::marker::Unpin,
+    E: std::error::Error,
+{
+    // For a given path, this holds the [Directory] structs as they are populated.
+    let mut directories: HashMap<PathBuf, Directory> = HashMap::default();
+    let mut maybe_directory_putter: Option<Box<dyn DirectoryPutter>> = None;
+
+    let root_node = loop {
+        let mut entry = entries
+            .next()
+            .await
+            // The last entry of the stream must have 1 path component, after which
+            // we break the loop manually.
+            .expect("Tvix bug: unexpected end of stream")?;
+
+        let name = entry
+            .path()
+            .file_name()
+            // If this is the root node, it will have an empty name.
+            .unwrap_or_default()
+            .to_owned()
+            .into();
+
+        let node = match &mut entry {
+            IngestionEntry::Dir { .. } => {
+                // If the entry is a directory, we traversed all its children (and
+                // populated it in `directories`).
+                // If we don't have it in there, it's an empty directory.
+                let directory = directories
+                    .remove(entry.path())
+                    // In that case, it contained no children
+                    .unwrap_or_default();
+
+                let directory_size = directory.size();
+                let directory_digest = directory.digest();
+
+                // Use the directory_putter to upload the directory.
+                // If we don't have one yet (as that's the first one to upload),
+                // initialize the putter.
+                maybe_directory_putter
+                    .get_or_insert_with(|| directory_service.as_ref().put_multiple_start())
+                    .put(directory)
+                    .await
+                    .map_err(|e| {
+                        IngestionError::UploadDirectoryError(entry.path().to_owned(), e)
+                    })?;
+
+                Node::Directory(DirectoryNode {
+                    name,
+                    digest: directory_digest.into(),
+                    size: directory_size,
+                })
+            }
+            IngestionEntry::Symlink { ref target, .. } => Node::Symlink(SymlinkNode {
+                name,
+                target: target.to_owned().into(),
+            }),
+            IngestionEntry::Regular {
+                size,
+                executable,
+                digest,
+                ..
+            } => Node::File(FileNode {
+                name,
+                digest: digest.to_owned().into(),
+                size: *size,
+                executable: *executable,
+            }),
+        };
+
+        if entry.path().components().count() == 1 {
+            break node;
+        }
+
+        // record node in parent directory, creating a new [Directory] if not there yet.
+        directories
+            .entry(entry.path().parent().unwrap().to_owned())
+            .or_default()
+            .add(node);
+    };
+
+    assert!(
+        directories.is_empty(),
+        "Tvix bug: left over directories after processing ingestion stream"
+    );
+
+    // if there were directories uploaded, make sure we flush the putter, so
+    // they're all persisted to the backend.
+    if let Some(mut directory_putter) = maybe_directory_putter {
+        #[cfg_attr(not(debug_assertions), allow(unused))]
+        let root_directory_digest = directory_putter
+            .close()
+            .await
+            .map_err(|e| IngestionError::FinalizeDirectoryUpload(e))?;
+
+        #[cfg(debug_assertions)]
+        {
+            if let Node::Directory(directory_node) = &root_node {
+                debug_assert_eq!(
+                    root_directory_digest,
+                    directory_node
+                        .digest
+                        .to_vec()
+                        .try_into()
+                        .expect("invalid digest len")
+                )
+            } else {
+                unreachable!("Tvix bug: directory putter initialized but no root directory node");
+            }
+        }
+    };
+
+    Ok(root_node)
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum IngestionEntry {
+    Regular {
+        path: PathBuf,
+        size: u64,
+        executable: bool,
+        digest: B3Digest,
+    },
+    Symlink {
+        path: PathBuf,
+        target: Vec<u8>,
+    },
+    Dir {
+        path: PathBuf,
+    },
+}
+
+impl IngestionEntry {
+    fn path(&self) -> &PathBuf {
+        match self {
+            IngestionEntry::Regular { path, .. } => path,
+            IngestionEntry::Symlink { path, .. } => path,
+            IngestionEntry::Dir { path } => path,
+        }
+    }
+
+    fn is_dir(&self) -> bool {
+        matches!(self, IngestionEntry::Dir { .. })
+    }
+}
diff --git a/tvix/castore/src/lib.rs b/tvix/castore/src/lib.rs
new file mode 100644
index 0000000000..bdc533a8c5
--- /dev/null
+++ b/tvix/castore/src/lib.rs
@@ -0,0 +1,30 @@
+mod digests;
+mod errors;
+mod hashing_reader;
+
+pub mod blobservice;
+pub mod directoryservice;
+pub mod fixtures;
+
+#[cfg(feature = "fs")]
+pub mod fs;
+
+mod path;
+pub use path::{Path, PathBuf};
+
+pub mod import;
+pub mod proto;
+pub mod tonic;
+
+pub use digests::{B3Digest, B3_LEN};
+pub use errors::Error;
+pub use hashing_reader::{B3HashingReader, HashingReader};
+
+#[cfg(test)]
+mod tests;
+
+// That's what the rstest_reuse README asks us do, and fails about being unable
+// to find rstest_reuse in crate root.
+#[cfg(test)]
+#[allow(clippy::single_component_path_imports)]
+use rstest_reuse;
diff --git a/tvix/castore/src/path.rs b/tvix/castore/src/path.rs
new file mode 100644
index 0000000000..fcc2bd01fb
--- /dev/null
+++ b/tvix/castore/src/path.rs
@@ -0,0 +1,446 @@
+//! Contains data structures to deal with Paths in the tvix-castore model.
+
+use std::{
+    borrow::Borrow,
+    fmt::{self, Debug, Display},
+    mem,
+    ops::Deref,
+    str::FromStr,
+};
+
+use bstr::ByteSlice;
+
+use crate::proto::validate_node_name;
+
+/// Represents a Path in the castore model.
+/// These are always relative, and platform-independent, which distinguishes
+/// them from the ones provided in the standard library.
+#[derive(Eq, Hash, PartialEq)]
+#[repr(transparent)] // SAFETY: Representation has to match [u8]
+pub struct Path {
+    // As node names in the castore model cannot contain slashes,
+    // we use them as component separators here.
+    inner: [u8],
+}
+
+#[allow(dead_code)]
+impl Path {
+    // SAFETY: The empty path is valid.
+    pub const ROOT: &'static Path = unsafe { Path::from_bytes_unchecked(&[]) };
+
+    /// Convert a byte slice to a path, without checking validity.
+    const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Path {
+        // SAFETY: &[u8] and &Path have the same representation.
+        unsafe { mem::transmute(bytes) }
+    }
+
+    fn from_bytes(bytes: &[u8]) -> Option<&Path> {
+        if !bytes.is_empty() {
+            // Ensure all components are valid castore node names.
+            for component in bytes.split_str(b"/") {
+                validate_node_name(component).ok()?;
+            }
+        }
+
+        // SAFETY: We have verified that the path contains no empty components.
+        Some(unsafe { Path::from_bytes_unchecked(bytes) })
+    }
+
+    pub fn into_boxed_bytes(self: Box<Path>) -> Box<[u8]> {
+        // SAFETY: Box<Path> and Box<[u8]> have the same representation.
+        unsafe { mem::transmute(self) }
+    }
+
+    /// Returns the path without its final component, if there is one.
+    ///
+    /// Note that the parent of a bare file name is [Path::ROOT].
+    /// [Path::ROOT] is the only path without a parent.
+    pub fn parent(&self) -> Option<&Path> {
+        // The root does not have a parent.
+        if self.inner.is_empty() {
+            return None;
+        }
+
+        Some(
+            if let Some((parent, _file_name)) = self.inner.rsplit_once_str(b"/") {
+                // SAFETY: The parent of a valid Path is a valid Path.
+                unsafe { Path::from_bytes_unchecked(parent) }
+            } else {
+                // The parent of a bare file name is the root.
+                Path::ROOT
+            },
+        )
+    }
+
+    /// Creates a PathBuf with `name` adjoined to self.
+    pub fn try_join(&self, name: &[u8]) -> Result<PathBuf, std::io::Error> {
+        let mut v = PathBuf::with_capacity(self.inner.len() + name.len() + 1);
+        v.inner.extend_from_slice(&self.inner);
+        v.try_push(name)?;
+
+        Ok(v)
+    }
+
+    /// Produces an iterator over the components of the path, which are
+    /// individual byte slices.
+    /// In case the path is empty, an empty iterator is returned.
+    pub fn components(&self) -> impl Iterator<Item = &[u8]> {
+        let mut iter = self.inner.split_str(&b"/");
+
+        // We don't want to return an empty element, consume it if it's the only one.
+        if self.inner.is_empty() {
+            let _ = iter.next();
+        }
+
+        iter
+    }
+
+    /// Returns the final component of the Path, if there is one.
+    pub fn file_name(&self) -> Option<&[u8]> {
+        self.components().last()
+    }
+
+    pub fn as_bytes(&self) -> &[u8] {
+        &self.inner
+    }
+}
+
+impl Debug for Path {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Debug::fmt(self.inner.as_bstr(), f)
+    }
+}
+
+impl Display for Path {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Display::fmt(self.inner.as_bstr(), f)
+    }
+}
+
+impl AsRef<Path> for Path {
+    fn as_ref(&self) -> &Path {
+        self
+    }
+}
+
+/// Represents a owned PathBuf in the castore model.
+/// These are always relative, and platform-independent, which distinguishes
+/// them from the ones provided in the standard library.
+#[derive(Clone, Default, Eq, Hash, PartialEq)]
+pub struct PathBuf {
+    inner: Vec<u8>,
+}
+
+impl Deref for PathBuf {
+    type Target = Path;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: PathBuf always contains a valid Path.
+        unsafe { Path::from_bytes_unchecked(&self.inner) }
+    }
+}
+
+impl AsRef<Path> for PathBuf {
+    fn as_ref(&self) -> &Path {
+        self
+    }
+}
+
+impl ToOwned for Path {
+    type Owned = PathBuf;
+
+    fn to_owned(&self) -> Self::Owned {
+        PathBuf {
+            inner: self.inner.to_owned(),
+        }
+    }
+}
+
+impl Borrow<Path> for PathBuf {
+    fn borrow(&self) -> &Path {
+        self
+    }
+}
+
+impl From<Box<Path>> for PathBuf {
+    fn from(value: Box<Path>) -> Self {
+        // SAFETY: Box<Path> is always a valid path.
+        unsafe { PathBuf::from_bytes_unchecked(value.into_boxed_bytes().into_vec()) }
+    }
+}
+
+impl From<&Path> for PathBuf {
+    fn from(value: &Path) -> Self {
+        value.to_owned()
+    }
+}
+
+impl FromStr for PathBuf {
+    type Err = std::io::Error;
+
+    fn from_str(s: &str) -> Result<PathBuf, Self::Err> {
+        Ok(Path::from_bytes(s.as_bytes())
+            .ok_or(std::io::ErrorKind::InvalidData)?
+            .to_owned())
+    }
+}
+
+impl Debug for PathBuf {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Debug::fmt(&**self, f)
+    }
+}
+
+impl Display for PathBuf {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Display::fmt(&**self, f)
+    }
+}
+
+impl PathBuf {
+    pub fn new() -> PathBuf {
+        Self::default()
+    }
+
+    pub fn with_capacity(capacity: usize) -> PathBuf {
+        // SAFETY: The empty path is a valid path.
+        Self {
+            inner: Vec::with_capacity(capacity),
+        }
+    }
+
+    /// Adjoins `name` to self.
+    pub fn try_push(&mut self, name: &[u8]) -> Result<(), std::io::Error> {
+        validate_node_name(name).map_err(|_| std::io::ErrorKind::InvalidData)?;
+
+        if !self.inner.is_empty() {
+            self.inner.push(b'/');
+        }
+
+        self.inner.extend_from_slice(name);
+
+        Ok(())
+    }
+
+    /// Convert a byte vector to a PathBuf, without checking validity.
+    unsafe fn from_bytes_unchecked(bytes: Vec<u8>) -> PathBuf {
+        PathBuf { inner: bytes }
+    }
+
+    /// Convert from a [&std::path::Path] to [Self].
+    ///
+    /// - Self uses `/` as path separator.
+    /// - Absolute paths are always rejected, are are these with custom prefixes.
+    /// - Repeated separators are deduplicated.
+    /// - Occurrences of `.` are normalized away.
+    /// - A trailing slash is normalized away.
+    ///
+    /// A `canonicalize_dotdot` boolean controls whether `..` will get
+    /// canonicalized if possible, or should return an error.
+    ///
+    /// For more exotic paths, this conversion might produce different results
+    /// on different platforms, due to different underlying byte
+    /// representations, which is why it's restricted to unix for now.
+    #[cfg(unix)]
+    pub fn from_host_path(
+        host_path: &std::path::Path,
+        canonicalize_dotdot: bool,
+    ) -> Result<Self, std::io::Error> {
+        let mut p = PathBuf::with_capacity(host_path.as_os_str().len());
+
+        for component in host_path.components() {
+            match component {
+                std::path::Component::Prefix(_) | std::path::Component::RootDir => {
+                    return Err(std::io::Error::new(
+                        std::io::ErrorKind::InvalidData,
+                        "found disallowed prefix or rootdir",
+                    ))
+                }
+                std::path::Component::CurDir => continue, // ignore
+                std::path::Component::ParentDir => {
+                    if canonicalize_dotdot {
+                        // Try popping the last element from the path being constructed.
+                        // FUTUREWORK: pop method?
+                        p = p
+                            .parent()
+                            .ok_or_else(|| {
+                                std::io::Error::new(
+                                    std::io::ErrorKind::InvalidData,
+                                    "found .. going too far up",
+                                )
+                            })?
+                            .to_owned();
+                    } else {
+                        return Err(std::io::Error::new(
+                            std::io::ErrorKind::InvalidData,
+                            "found disallowed ..",
+                        ));
+                    }
+                }
+                std::path::Component::Normal(s) => {
+                    // append the new component to the path being constructed.
+                    p.try_push(s.as_encoded_bytes()).map_err(|_| {
+                        std::io::Error::new(
+                            std::io::ErrorKind::InvalidData,
+                            "encountered invalid node in sub_path component",
+                        )
+                    })?
+                }
+            }
+        }
+
+        Ok(p)
+    }
+
+    pub fn into_boxed_path(self) -> Box<Path> {
+        // SAFETY: Box<[u8]> and Box<Path> have the same representation,
+        // and PathBuf always contains a valid Path.
+        unsafe { mem::transmute(self.inner.into_boxed_slice()) }
+    }
+
+    pub fn into_bytes(self) -> Vec<u8> {
+        self.inner
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::{Path, PathBuf};
+    use bstr::ByteSlice;
+    use rstest::rstest;
+
+    // TODO: add some manual tests including invalid UTF-8 (hard to express
+    // with rstest)
+
+    #[rstest]
+    #[case::empty("", 0)]
+    #[case("a", 1)]
+    #[case("a/b", 2)]
+    #[case("a/b/c", 3)]
+    // add two slightly more cursed variants.
+    // Technically nothing prevents us from representing this with castore,
+    // but maybe we want to disallow constructing paths like this as it's a
+    // bad idea.
+    #[case::cursed("C:\\a/b", 2)]
+    #[case::cursed("\\\\tvix-store", 1)]
+    pub fn from_str(#[case] s: &str, #[case] num_components: usize) {
+        let p: PathBuf = s.parse().expect("must parse");
+
+        assert_eq!(s.as_bytes(), p.as_bytes(), "inner bytes mismatch");
+        assert_eq!(
+            num_components,
+            p.components().count(),
+            "number of components mismatch"
+        );
+    }
+
+    #[rstest]
+    #[case::absolute("/a/b")]
+    #[case::two_forward_slashes_start("//a/b")]
+    #[case::two_forward_slashes_middle("a/b//c/d")]
+    #[case::trailing_slash("a/b/")]
+    #[case::dot(".")]
+    #[case::dotdot("..")]
+    #[case::dot_start("./a")]
+    #[case::dotdot_start("../a")]
+    #[case::dot_middle("a/./b")]
+    #[case::dotdot_middle("a/../b")]
+    #[case::dot_end("a/b/.")]
+    #[case::dotdot_end("a/b/..")]
+    #[case::null("fo\0o")]
+    pub fn from_str_fail(#[case] s: &str) {
+        s.parse::<PathBuf>().expect_err("must fail");
+    }
+
+    #[rstest]
+    #[case("foo", "")]
+    #[case("foo/bar", "foo")]
+    #[case("foo2/bar2", "foo2")]
+    #[case("foo/bar/baz", "foo/bar")]
+    pub fn parent(#[case] p: PathBuf, #[case] exp_parent: PathBuf) {
+        assert_eq!(Some(&*exp_parent), p.parent());
+    }
+
+    #[rstest]
+    pub fn no_parent() {
+        assert!(Path::ROOT.parent().is_none());
+    }
+
+    #[rstest]
+    #[case("a", "b", "a/b")]
+    #[case("a", "b", "a/b")]
+    pub fn join_push(#[case] mut p: PathBuf, #[case] name: &str, #[case] exp_p: PathBuf) {
+        assert_eq!(exp_p, p.try_join(name.as_bytes()).expect("join failed"));
+        p.try_push(name.as_bytes()).expect("push failed");
+        assert_eq!(exp_p, p);
+    }
+
+    #[rstest]
+    #[case("a", "/")]
+    #[case("a", "")]
+    #[case("a", "b/c")]
+    #[case("", "/")]
+    #[case("", "")]
+    #[case("", "b/c")]
+    #[case("", ".")]
+    #[case("", "..")]
+    pub fn join_push_fail(#[case] mut p: PathBuf, #[case] name: &str) {
+        p.try_join(name.as_bytes())
+            .expect_err("join succeeded unexpectedly");
+        p.try_push(name.as_bytes())
+            .expect_err("push succeeded unexpectedly");
+    }
+
+    #[rstest]
+    #[case::empty("", vec![])]
+    #[case("a", vec!["a"])]
+    #[case("a/b", vec!["a", "b"])]
+    #[case("a/b/c", vec!["a","b", "c"])]
+    pub fn components(#[case] p: PathBuf, #[case] exp_components: Vec<&str>) {
+        assert_eq!(
+            exp_components,
+            p.components()
+                .map(|x| x.to_str().unwrap())
+                .collect::<Vec<_>>()
+        );
+    }
+
+    #[rstest]
+    #[case::empty("", "", false)]
+    #[case::path("a", "a", false)]
+    #[case::path2("a/b", "a/b", false)]
+    #[case::double_slash_middle("a//b", "a/b", false)]
+    #[case::dot(".", "", false)]
+    #[case::dot_start("./a/b", "a/b", false)]
+    #[case::dot_middle("a/./b", "a/b", false)]
+    #[case::dot_end("a/b/.", "a/b", false)]
+    #[case::trailing_slash("a/b/", "a/b", false)]
+    #[case::dotdot_canonicalize("a/..", "", true)]
+    #[case::dotdot_canonicalize2("a/../b", "b", true)]
+    #[cfg_attr(unix, case::faux_prefix("\\\\nix-store", "\\\\nix-store", false))]
+    #[cfg_attr(unix, case::faux_letter("C:\\foo.txt", "C:\\foo.txt", false))]
+    pub fn from_host_path(
+        #[case] host_path: std::path::PathBuf,
+        #[case] exp_path: PathBuf,
+        #[case] canonicalize_dotdot: bool,
+    ) {
+        let p = PathBuf::from_host_path(&host_path, canonicalize_dotdot).expect("must succeed");
+
+        assert_eq!(exp_path, p);
+    }
+
+    #[rstest]
+    #[case::absolute("/", false)]
+    #[case::dotdot_root("..", false)]
+    #[case::dotdot_root_canonicalize("..", true)]
+    #[case::dotdot_root_no_canonicalize("a/..", false)]
+    #[case::invalid_name("foo/bar\0", false)]
+    // #[cfg_attr(windows, case::prefix("\\\\nix-store", false))]
+    // #[cfg_attr(windows, case::letter("C:\\foo.txt", false))]
+    pub fn from_host_path_fail(
+        #[case] host_path: std::path::PathBuf,
+        #[case] canonicalize_dotdot: bool,
+    ) {
+        PathBuf::from_host_path(&host_path, canonicalize_dotdot).expect_err("must fail");
+    }
+}
diff --git a/tvix/castore/src/proto/grpc_blobservice_wrapper.rs b/tvix/castore/src/proto/grpc_blobservice_wrapper.rs
new file mode 100644
index 0000000000..41bd0698ec
--- /dev/null
+++ b/tvix/castore/src/proto/grpc_blobservice_wrapper.rs
@@ -0,0 +1,175 @@
+use crate::blobservice::BlobService;
+use core::pin::pin;
+use data_encoding::BASE64;
+use futures::{stream::BoxStream, TryFutureExt};
+use std::{
+    collections::VecDeque,
+    ops::{Deref, DerefMut},
+};
+use tokio_stream::StreamExt;
+use tokio_util::io::ReaderStream;
+use tonic::{async_trait, Request, Response, Status, Streaming};
+use tracing::{instrument, warn};
+
+pub struct GRPCBlobServiceWrapper<T> {
+    blob_service: T,
+}
+
+impl<T> GRPCBlobServiceWrapper<T> {
+    pub fn new(blob_service: T) -> Self {
+        Self { blob_service }
+    }
+}
+
+// This is necessary because bytes::BytesMut comes up with
+// a default 64 bytes capacity that cannot be changed
+// easily if you assume a bytes::BufMut trait implementation
+// Therefore, we override the Default implementation here
+// TODO(raitobezarius?): upstream me properly
+struct BytesMutWithDefaultCapacity<const N: usize> {
+    inner: bytes::BytesMut,
+}
+
+impl<const N: usize> Deref for BytesMutWithDefaultCapacity<N> {
+    type Target = bytes::BytesMut;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl<const N: usize> DerefMut for BytesMutWithDefaultCapacity<N> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.inner
+    }
+}
+
+impl<const N: usize> Default for BytesMutWithDefaultCapacity<N> {
+    fn default() -> Self {
+        BytesMutWithDefaultCapacity {
+            inner: bytes::BytesMut::with_capacity(N),
+        }
+    }
+}
+
+impl<const N: usize> bytes::Buf for BytesMutWithDefaultCapacity<N> {
+    fn remaining(&self) -> usize {
+        self.inner.remaining()
+    }
+
+    fn chunk(&self) -> &[u8] {
+        self.inner.chunk()
+    }
+
+    fn advance(&mut self, cnt: usize) {
+        self.inner.advance(cnt);
+    }
+}
+
+unsafe impl<const N: usize> bytes::BufMut for BytesMutWithDefaultCapacity<N> {
+    fn remaining_mut(&self) -> usize {
+        self.inner.remaining_mut()
+    }
+
+    unsafe fn advance_mut(&mut self, cnt: usize) {
+        self.inner.advance_mut(cnt);
+    }
+
+    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
+        self.inner.chunk_mut()
+    }
+}
+
+#[async_trait]
+impl<T> super::blob_service_server::BlobService for GRPCBlobServiceWrapper<T>
+where
+    T: Deref<Target = dyn BlobService> + Send + Sync + 'static,
+{
+    // https://github.com/tokio-rs/tokio/issues/2723#issuecomment-1534723933
+    type ReadStream = BoxStream<'static, Result<super::BlobChunk, Status>>;
+
+    #[instrument(skip_all, fields(blob.digest=format!("b3:{}", BASE64.encode(&request.get_ref().digest))))]
+    async fn stat(
+        &self,
+        request: Request<super::StatBlobRequest>,
+    ) -> Result<Response<super::StatBlobResponse>, Status> {
+        let rq = request.into_inner();
+        let req_digest = rq
+            .digest
+            .try_into()
+            .map_err(|_e| Status::invalid_argument("invalid digest length"))?;
+
+        match self.blob_service.chunks(&req_digest).await {
+            Ok(None) => Err(Status::not_found(format!("blob {} not found", &req_digest))),
+            Ok(Some(chunk_metas)) => Ok(Response::new(super::StatBlobResponse {
+                chunks: chunk_metas,
+                ..Default::default()
+            })),
+            Err(e) => {
+                warn!(err=%e, "failed to request chunks");
+                Err(e.into())
+            }
+        }
+    }
+
+    #[instrument(skip_all, fields(blob.digest=format!("b3:{}", BASE64.encode(&request.get_ref().digest))))]
+    async fn read(
+        &self,
+        request: Request<super::ReadBlobRequest>,
+    ) -> Result<Response<Self::ReadStream>, Status> {
+        let rq = request.into_inner();
+
+        let req_digest = rq
+            .digest
+            .try_into()
+            .map_err(|_e| Status::invalid_argument("invalid digest length"))?;
+
+        match self.blob_service.open_read(&req_digest).await {
+            Ok(Some(r)) => {
+                let chunks_stream =
+                    ReaderStream::new(r).map(|chunk| Ok(super::BlobChunk { data: chunk? }));
+                Ok(Response::new(Box::pin(chunks_stream)))
+            }
+            Ok(None) => Err(Status::not_found(format!("blob {} not found", &req_digest))),
+            Err(e) => {
+                warn!(err=%e, "failed to call open_read");
+                Err(e.into())
+            }
+        }
+    }
+
+    #[instrument(skip_all)]
+    async fn put(
+        &self,
+        request: Request<Streaming<super::BlobChunk>>,
+    ) -> Result<Response<super::PutBlobResponse>, Status> {
+        let req_inner = request.into_inner();
+
+        let data_stream = req_inner.map(|x| {
+            x.map(|x| VecDeque::from(x.data.to_vec()))
+                .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))
+        });
+
+        let mut data_reader = tokio_util::io::StreamReader::new(data_stream);
+
+        let mut blob_writer = pin!(self.blob_service.open_write().await);
+
+        tokio::io::copy(&mut data_reader, &mut blob_writer)
+            .await
+            .map_err(|e| {
+                warn!("error copying: {}", e);
+                Status::internal("error copying")
+            })?;
+
+        let digest = blob_writer
+            .close()
+            .map_err(|e| {
+                warn!("error closing stream: {}", e);
+                Status::internal("error closing stream")
+            })
+            .await?;
+
+        Ok(Response::new(super::PutBlobResponse {
+            digest: digest.into(),
+        }))
+    }
+}
diff --git a/tvix/castore/src/proto/grpc_directoryservice_wrapper.rs b/tvix/castore/src/proto/grpc_directoryservice_wrapper.rs
new file mode 100644
index 0000000000..7d741a3f07
--- /dev/null
+++ b/tvix/castore/src/proto/grpc_directoryservice_wrapper.rs
@@ -0,0 +1,114 @@
+use crate::directoryservice::ClosureValidator;
+use crate::proto;
+use crate::{directoryservice::DirectoryService, B3Digest};
+use futures::StreamExt;
+use std::ops::Deref;
+use tokio::sync::mpsc::channel;
+use tokio_stream::wrappers::ReceiverStream;
+use tonic::{async_trait, Request, Response, Status, Streaming};
+use tracing::{debug, instrument, warn};
+
+pub struct GRPCDirectoryServiceWrapper<T> {
+    directory_service: T,
+}
+
+impl<T> GRPCDirectoryServiceWrapper<T> {
+    pub fn new(directory_service: T) -> Self {
+        Self { directory_service }
+    }
+}
+
+#[async_trait]
+impl<T> proto::directory_service_server::DirectoryService for GRPCDirectoryServiceWrapper<T>
+where
+    T: Deref<Target = dyn DirectoryService> + Send + Sync + 'static,
+{
+    type GetStream = ReceiverStream<tonic::Result<proto::Directory, Status>>;
+
+    #[instrument(skip_all)]
+    async fn get(
+        &self,
+        request: Request<proto::GetDirectoryRequest>,
+    ) -> Result<Response<Self::GetStream>, Status> {
+        let (tx, rx) = channel(5);
+
+        let req_inner = request.into_inner();
+
+        // look at the digest in the request and put it in the top of the queue.
+        match &req_inner.by_what {
+            None => return Err(Status::invalid_argument("by_what needs to be specified")),
+            Some(proto::get_directory_request::ByWhat::Digest(ref digest)) => {
+                let digest: B3Digest = digest
+                    .clone()
+                    .try_into()
+                    .map_err(|_e| Status::invalid_argument("invalid digest length"))?;
+
+                if !req_inner.recursive {
+                    let e: Result<proto::Directory, Status> = match self
+                        .directory_service
+                        .get(&digest)
+                        .await
+                    {
+                        Ok(Some(directory)) => Ok(directory),
+                        Ok(None) => {
+                            Err(Status::not_found(format!("directory {} not found", digest)))
+                        }
+                        Err(e) => {
+                            warn!(err = %e, directory.digest=%digest, "failed to get directory");
+                            Err(e.into())
+                        }
+                    };
+
+                    if tx.send(e).await.is_err() {
+                        debug!("receiver dropped");
+                    }
+                } else {
+                    // If recursive was requested, traverse via get_recursive.
+                    let mut directories_it = self.directory_service.get_recursive(&digest);
+
+                    while let Some(e) = directories_it.next().await {
+                        // map err in res from Error to Status
+                        let res = e.map_err(|e| Status::internal(e.to_string()));
+                        if tx.send(res).await.is_err() {
+                            debug!("receiver dropped");
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        let receiver_stream = ReceiverStream::new(rx);
+        Ok(Response::new(receiver_stream))
+    }
+
+    #[instrument(skip_all)]
+    async fn put(
+        &self,
+        request: Request<Streaming<proto::Directory>>,
+    ) -> Result<Response<proto::PutDirectoryResponse>, Status> {
+        let mut req_inner = request.into_inner();
+
+        // We put all Directory messages we receive into ClosureValidator first.
+        let mut validator = ClosureValidator::default();
+        while let Some(directory) = req_inner.message().await? {
+            validator.add(directory)?;
+        }
+
+        // drain, which validates connectivity too.
+        let directories = validator.finalize()?;
+
+        let mut directory_putter = self.directory_service.put_multiple_start();
+        for directory in directories {
+            directory_putter.put(directory).await?;
+        }
+
+        // Properly close the directory putter. Peek at last_directory_digest
+        // and return it, or propagate errors.
+        let last_directory_dgst = directory_putter.close().await?;
+
+        Ok(Response::new(proto::PutDirectoryResponse {
+            root_digest: last_directory_dgst.into(),
+        }))
+    }
+}
diff --git a/tvix/castore/src/proto/mod.rs b/tvix/castore/src/proto/mod.rs
new file mode 100644
index 0000000000..5374e3ae5a
--- /dev/null
+++ b/tvix/castore/src/proto/mod.rs
@@ -0,0 +1,471 @@
+#![allow(non_snake_case)]
+// https://github.com/hyperium/tonic/issues/1056
+use bstr::ByteSlice;
+use std::{collections::HashSet, iter::Peekable, str};
+
+use prost::Message;
+
+mod grpc_blobservice_wrapper;
+mod grpc_directoryservice_wrapper;
+
+pub use grpc_blobservice_wrapper::GRPCBlobServiceWrapper;
+pub use grpc_directoryservice_wrapper::GRPCDirectoryServiceWrapper;
+
+use crate::{B3Digest, B3_LEN};
+
+tonic::include_proto!("tvix.castore.v1");
+
+#[cfg(feature = "tonic-reflection")]
+/// Compiled file descriptors for implementing [gRPC
+/// reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) with e.g.
+/// [`tonic_reflection`](https://docs.rs/tonic-reflection).
+pub const FILE_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("tvix.castore.v1");
+
+#[cfg(test)]
+mod tests;
+
+/// Errors that can occur during the validation of [Directory] messages.
+#[derive(Debug, PartialEq, Eq, thiserror::Error)]
+pub enum ValidateDirectoryError {
+    /// Elements are not in sorted order
+    #[error("{:?} is not sorted", .0.as_bstr())]
+    WrongSorting(Vec<u8>),
+    /// Multiple elements with the same name encountered
+    #[error("{:?} is a duplicate name", .0.as_bstr())]
+    DuplicateName(Vec<u8>),
+    /// Invalid node
+    #[error("invalid node with name {:?}: {:?}", .0.as_bstr(), .1.to_string())]
+    InvalidNode(Vec<u8>, ValidateNodeError),
+    #[error("Total size exceeds u32::MAX")]
+    SizeOverflow,
+}
+
+/// Errors that occur during Node validation
+#[derive(Debug, PartialEq, Eq, thiserror::Error)]
+pub enum ValidateNodeError {
+    #[error("No node set")]
+    NoNodeSet,
+    /// Invalid digest length encountered
+    #[error("Invalid Digest length: {0}")]
+    InvalidDigestLen(usize),
+    /// Invalid name encountered
+    #[error("Invalid name: {}", .0.as_bstr())]
+    InvalidName(Vec<u8>),
+    /// Invalid symlink target
+    #[error("Invalid symlink target: {}", .0.as_bstr())]
+    InvalidSymlinkTarget(Vec<u8>),
+}
+
+/// Errors that occur during StatBlobResponse validation
+#[derive(Debug, PartialEq, Eq, thiserror::Error)]
+pub enum ValidateStatBlobResponseError {
+    /// Invalid digest length encountered
+    #[error("Invalid digest length {0} for chunk #{1}")]
+    InvalidDigestLen(usize, usize),
+}
+
+/// Checks a Node name for validity as an intermediate node.
+/// We disallow slashes, null bytes, '.', '..' and the empty string.
+pub(crate) fn validate_node_name(name: &[u8]) -> Result<(), ValidateNodeError> {
+    if name.is_empty()
+        || name == b".."
+        || name == b"."
+        || name.contains(&0x00)
+        || name.contains(&b'/')
+    {
+        Err(ValidateNodeError::InvalidName(name.to_owned()))
+    } else {
+        Ok(())
+    }
+}
+
+/// NamedNode is implemented for [FileNode], [DirectoryNode] and [SymlinkNode]
+/// and [node::Node], so we can ask all of them for the name easily.
+pub trait NamedNode {
+    fn get_name(&self) -> &[u8];
+}
+
+impl NamedNode for &FileNode {
+    fn get_name(&self) -> &[u8] {
+        &self.name
+    }
+}
+
+impl NamedNode for &DirectoryNode {
+    fn get_name(&self) -> &[u8] {
+        &self.name
+    }
+}
+
+impl NamedNode for &SymlinkNode {
+    fn get_name(&self) -> &[u8] {
+        &self.name
+    }
+}
+
+impl NamedNode for node::Node {
+    fn get_name(&self) -> &[u8] {
+        match self {
+            node::Node::File(node_file) => &node_file.name,
+            node::Node::Directory(node_directory) => &node_directory.name,
+            node::Node::Symlink(node_symlink) => &node_symlink.name,
+        }
+    }
+}
+
+impl Node {
+    /// Ensures the node has a valid enum kind (is Some), and passes its
+    // per-enum validation.
+    pub fn validate(&self) -> Result<(), ValidateNodeError> {
+        if let Some(node) = self.node.as_ref() {
+            node.validate()
+        } else {
+            Err(ValidateNodeError::NoNodeSet)
+        }
+    }
+}
+
+impl node::Node {
+    /// Returns the node with a new name.
+    pub fn rename(self, name: bytes::Bytes) -> Self {
+        match self {
+            node::Node::Directory(n) => node::Node::Directory(DirectoryNode { name, ..n }),
+            node::Node::File(n) => node::Node::File(FileNode { name, ..n }),
+            node::Node::Symlink(n) => node::Node::Symlink(SymlinkNode { name, ..n }),
+        }
+    }
+
+    /// Ensures the node has a valid name, and checks the type-specific fields too.
+    pub fn validate(&self) -> Result<(), ValidateNodeError> {
+        match self {
+            // for a directory root node, ensure the digest has the appropriate size.
+            node::Node::Directory(directory_node) => {
+                if directory_node.digest.len() != B3_LEN {
+                    Err(ValidateNodeError::InvalidDigestLen(
+                        directory_node.digest.len(),
+                    ))?;
+                }
+                validate_node_name(&directory_node.name)
+            }
+            // for a file root node, ensure the digest has the appropriate size.
+            node::Node::File(file_node) => {
+                if file_node.digest.len() != B3_LEN {
+                    Err(ValidateNodeError::InvalidDigestLen(file_node.digest.len()))?;
+                }
+                validate_node_name(&file_node.name)
+            }
+            // ensure the symlink target is not empty and doesn't contain null bytes.
+            node::Node::Symlink(symlink_node) => {
+                if symlink_node.target.is_empty() || symlink_node.target.contains(&b'\0') {
+                    Err(ValidateNodeError::InvalidSymlinkTarget(
+                        symlink_node.target.to_vec(),
+                    ))?;
+                }
+                validate_node_name(&symlink_node.name)
+            }
+        }
+    }
+}
+
+impl PartialOrd for node::Node {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for node::Node {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.get_name().cmp(other.get_name())
+    }
+}
+
+impl PartialOrd for FileNode {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for FileNode {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.get_name().cmp(other.get_name())
+    }
+}
+
+impl PartialOrd for SymlinkNode {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for SymlinkNode {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.get_name().cmp(other.get_name())
+    }
+}
+
+impl PartialOrd for DirectoryNode {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for DirectoryNode {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.get_name().cmp(other.get_name())
+    }
+}
+
+/// Accepts a name, and a mutable reference to the previous name.
+/// If the passed name is larger than the previous one, the reference is updated.
+/// If it's not, an error is returned.
+fn update_if_lt_prev<'n>(
+    prev_name: &mut &'n [u8],
+    name: &'n [u8],
+) -> Result<(), ValidateDirectoryError> {
+    if *name < **prev_name {
+        return Err(ValidateDirectoryError::WrongSorting(name.to_vec()));
+    }
+    *prev_name = name;
+    Ok(())
+}
+
+/// Inserts the given name into a HashSet if it's not already in there.
+/// If it is, an error is returned.
+fn insert_once<'n>(
+    seen_names: &mut HashSet<&'n [u8]>,
+    name: &'n [u8],
+) -> Result<(), ValidateDirectoryError> {
+    if seen_names.get(name).is_some() {
+        return Err(ValidateDirectoryError::DuplicateName(name.to_vec()));
+    }
+    seen_names.insert(name);
+    Ok(())
+}
+
+fn checked_sum(iter: impl IntoIterator<Item = u64>) -> Option<u64> {
+    iter.into_iter().try_fold(0u64, |acc, i| acc.checked_add(i))
+}
+
+impl Directory {
+    /// The size of a directory is the number of all regular and symlink elements,
+    /// the number of directory elements, and their size fields.
+    pub fn size(&self) -> u64 {
+        if cfg!(debug_assertions) {
+            self.size_checked()
+                .expect("Directory::size exceeds u64::MAX")
+        } else {
+            self.size_checked().unwrap_or(u64::MAX)
+        }
+    }
+
+    fn size_checked(&self) -> Option<u64> {
+        checked_sum([
+            self.files.len().try_into().ok()?,
+            self.symlinks.len().try_into().ok()?,
+            self.directories.len().try_into().ok()?,
+            checked_sum(self.directories.iter().map(|e| e.size))?,
+        ])
+    }
+
+    /// Calculates the digest of a Directory, which is the blake3 hash of a
+    /// Directory protobuf message, serialized in protobuf canonical form.
+    pub fn digest(&self) -> B3Digest {
+        let mut hasher = blake3::Hasher::new();
+
+        hasher
+            .update(&self.encode_to_vec())
+            .finalize()
+            .as_bytes()
+            .into()
+    }
+
+    /// validate checks the directory for invalid data, such as:
+    /// - violations of name restrictions
+    /// - invalid digest lengths
+    /// - not properly sorted lists
+    /// - duplicate names in the three lists
+    pub fn validate(&self) -> Result<(), ValidateDirectoryError> {
+        let mut seen_names: HashSet<&[u8]> = HashSet::new();
+
+        let mut last_directory_name: &[u8] = b"";
+        let mut last_file_name: &[u8] = b"";
+        let mut last_symlink_name: &[u8] = b"";
+
+        // check directories
+        for directory_node in &self.directories {
+            node::Node::Directory(directory_node.clone())
+                .validate()
+                .map_err(|e| {
+                    ValidateDirectoryError::InvalidNode(directory_node.name.to_vec(), e)
+                })?;
+
+            update_if_lt_prev(&mut last_directory_name, &directory_node.name)?;
+            insert_once(&mut seen_names, &directory_node.name)?;
+        }
+
+        // check files
+        for file_node in &self.files {
+            node::Node::File(file_node.clone())
+                .validate()
+                .map_err(|e| ValidateDirectoryError::InvalidNode(file_node.name.to_vec(), e))?;
+
+            update_if_lt_prev(&mut last_file_name, &file_node.name)?;
+            insert_once(&mut seen_names, &file_node.name)?;
+        }
+
+        // check symlinks
+        for symlink_node in &self.symlinks {
+            node::Node::Symlink(symlink_node.clone())
+                .validate()
+                .map_err(|e| ValidateDirectoryError::InvalidNode(symlink_node.name.to_vec(), e))?;
+
+            update_if_lt_prev(&mut last_symlink_name, &symlink_node.name)?;
+            insert_once(&mut seen_names, &symlink_node.name)?;
+        }
+
+        self.size_checked()
+            .ok_or(ValidateDirectoryError::SizeOverflow)?;
+
+        Ok(())
+    }
+
+    /// Allows iterating over all three nodes ([DirectoryNode], [FileNode],
+    /// [SymlinkNode]) in an ordered fashion, as long as the individual lists
+    /// are sorted (which can be checked by the [Directory::validate]).
+    pub fn nodes(&self) -> DirectoryNodesIterator {
+        return DirectoryNodesIterator {
+            i_directories: self.directories.iter().peekable(),
+            i_files: self.files.iter().peekable(),
+            i_symlinks: self.symlinks.iter().peekable(),
+        };
+    }
+
+    /// Adds the specified [node::Node] to the [Directory], preserving sorted entries.
+    /// This assumes the [Directory] to be sorted prior to adding the node.
+    ///
+    /// Inserting an element that already exists with the same name in the directory is not
+    /// supported.
+    pub fn add(&mut self, node: node::Node) {
+        debug_assert!(
+            !self.files.iter().any(|x| x.get_name() == node.get_name()),
+            "name already exists in files"
+        );
+        debug_assert!(
+            !self
+                .directories
+                .iter()
+                .any(|x| x.get_name() == node.get_name()),
+            "name already exists in directories"
+        );
+        debug_assert!(
+            !self
+                .symlinks
+                .iter()
+                .any(|x| x.get_name() == node.get_name()),
+            "name already exists in symlinks"
+        );
+
+        match node {
+            node::Node::File(node) => {
+                let pos = self
+                    .files
+                    .binary_search(&node)
+                    .expect_err("Tvix bug: dir entry with name already exists");
+                self.files.insert(pos, node);
+            }
+            node::Node::Directory(node) => {
+                let pos = self
+                    .directories
+                    .binary_search(&node)
+                    .expect_err("Tvix bug: dir entry with name already exists");
+                self.directories.insert(pos, node);
+            }
+            node::Node::Symlink(node) => {
+                let pos = self
+                    .symlinks
+                    .binary_search(&node)
+                    .expect_err("Tvix bug: dir entry with name already exists");
+                self.symlinks.insert(pos, node);
+            }
+        }
+    }
+}
+
+impl StatBlobResponse {
+    /// Validates a StatBlobResponse. All chunks must have valid blake3 digests.
+    /// It is allowed to send an empty list, if no more granular chunking is
+    /// available.
+    pub fn validate(&self) -> Result<(), ValidateStatBlobResponseError> {
+        for (i, chunk) in self.chunks.iter().enumerate() {
+            if chunk.digest.len() != blake3::KEY_LEN {
+                return Err(ValidateStatBlobResponseError::InvalidDigestLen(
+                    chunk.digest.len(),
+                    i,
+                ));
+            }
+        }
+        Ok(())
+    }
+}
+
+/// Struct to hold the state of an iterator over all nodes of a Directory.
+///
+/// Internally, this keeps peekable Iterators over all three lists of a
+/// Directory message.
+pub struct DirectoryNodesIterator<'a> {
+    // directory: &Directory,
+    i_directories: Peekable<std::slice::Iter<'a, DirectoryNode>>,
+    i_files: Peekable<std::slice::Iter<'a, FileNode>>,
+    i_symlinks: Peekable<std::slice::Iter<'a, SymlinkNode>>,
+}
+
+/// looks at two elements implementing NamedNode, and returns true if "left
+/// is smaller / comes first".
+///
+/// Some(_) is preferred over None.
+fn left_name_lt_right<A: NamedNode, B: NamedNode>(left: Option<&A>, right: Option<&B>) -> bool {
+    match left {
+        // if left is None, right always wins
+        None => false,
+        Some(left_inner) => {
+            // left is Some.
+            match right {
+                // left is Some, right is None - left wins.
+                None => true,
+                Some(right_inner) => {
+                    // both are Some - compare the name.
+                    return left_inner.get_name() < right_inner.get_name();
+                }
+            }
+        }
+    }
+}
+
+impl Iterator for DirectoryNodesIterator<'_> {
+    type Item = node::Node;
+
+    // next returns the next node in the Directory.
+    // we peek at all three internal iterators, and pick the one with the
+    // smallest name, to ensure lexicographical ordering.
+    // The individual lists are already known to be sorted.
+    fn next(&mut self) -> Option<Self::Item> {
+        if left_name_lt_right(self.i_directories.peek(), self.i_files.peek()) {
+            // i_directories is still in the game, compare with symlinks
+            if left_name_lt_right(self.i_directories.peek(), self.i_symlinks.peek()) {
+                self.i_directories
+                    .next()
+                    .cloned()
+                    .map(node::Node::Directory)
+            } else {
+                self.i_symlinks.next().cloned().map(node::Node::Symlink)
+            }
+        } else {
+            // i_files is still in the game, compare with symlinks
+            if left_name_lt_right(self.i_files.peek(), self.i_symlinks.peek()) {
+                self.i_files.next().cloned().map(node::Node::File)
+            } else {
+                self.i_symlinks.next().cloned().map(node::Node::Symlink)
+            }
+        }
+    }
+}
diff --git a/tvix/castore/src/proto/tests/directory.rs b/tvix/castore/src/proto/tests/directory.rs
new file mode 100644
index 0000000000..81b73a048d
--- /dev/null
+++ b/tvix/castore/src/proto/tests/directory.rs
@@ -0,0 +1,452 @@
+use crate::proto::{
+    node, Directory, DirectoryNode, FileNode, SymlinkNode, ValidateDirectoryError,
+    ValidateNodeError,
+};
+
+use hex_literal::hex;
+
+const DUMMY_DIGEST: [u8; 32] = [0; 32];
+
+#[test]
+fn size() {
+    {
+        let d = Directory::default();
+        assert_eq!(d.size(), 0);
+    }
+    {
+        let d = Directory {
+            directories: vec![DirectoryNode {
+                name: "foo".into(),
+                digest: DUMMY_DIGEST.to_vec().into(),
+                size: 0,
+            }],
+            ..Default::default()
+        };
+        assert_eq!(d.size(), 1);
+    }
+    {
+        let d = Directory {
+            directories: vec![DirectoryNode {
+                name: "foo".into(),
+                digest: DUMMY_DIGEST.to_vec().into(),
+                size: 4,
+            }],
+            ..Default::default()
+        };
+        assert_eq!(d.size(), 5);
+    }
+    {
+        let d = Directory {
+            files: vec![FileNode {
+                name: "foo".into(),
+                digest: DUMMY_DIGEST.to_vec().into(),
+                size: 42,
+                executable: false,
+            }],
+            ..Default::default()
+        };
+        assert_eq!(d.size(), 1);
+    }
+    {
+        let d = Directory {
+            symlinks: vec![SymlinkNode {
+                name: "foo".into(),
+                target: "bar".into(),
+            }],
+            ..Default::default()
+        };
+        assert_eq!(d.size(), 1);
+    }
+}
+
+#[test]
+#[cfg_attr(not(debug_assertions), ignore)]
+#[should_panic = "Directory::size exceeds u64::MAX"]
+fn size_unchecked_panic() {
+    let d = Directory {
+        directories: vec![DirectoryNode {
+            name: "foo".into(),
+            digest: DUMMY_DIGEST.to_vec().into(),
+            size: u64::MAX,
+        }],
+        ..Default::default()
+    };
+
+    d.size();
+}
+
+#[test]
+#[cfg_attr(debug_assertions, ignore)]
+fn size_unchecked_saturate() {
+    let d = Directory {
+        directories: vec![DirectoryNode {
+            name: "foo".into(),
+            digest: DUMMY_DIGEST.to_vec().into(),
+            size: u64::MAX,
+        }],
+        ..Default::default()
+    };
+
+    assert_eq!(d.size(), u64::MAX);
+}
+
+#[test]
+fn size_checked() {
+    // We don't test the overflow cases that rely purely on immediate
+    // child count, since that would take an absurd amount of memory.
+    {
+        let d = Directory {
+            directories: vec![DirectoryNode {
+                name: "foo".into(),
+                digest: DUMMY_DIGEST.to_vec().into(),
+                size: u64::MAX - 1,
+            }],
+            ..Default::default()
+        };
+        assert_eq!(d.size_checked(), Some(u64::MAX));
+    }
+    {
+        let d = Directory {
+            directories: vec![DirectoryNode {
+                name: "foo".into(),
+                digest: DUMMY_DIGEST.to_vec().into(),
+                size: u64::MAX,
+            }],
+            ..Default::default()
+        };
+        assert_eq!(d.size_checked(), None);
+    }
+    {
+        let d = Directory {
+            directories: vec![
+                DirectoryNode {
+                    name: "foo".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: u64::MAX / 2,
+                },
+                DirectoryNode {
+                    name: "foo".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: u64::MAX / 2,
+                },
+            ],
+            ..Default::default()
+        };
+        assert_eq!(d.size_checked(), None);
+    }
+}
+
+#[test]
+fn digest() {
+    let d = Directory::default();
+
+    assert_eq!(
+        d.digest(),
+        (&hex!("af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262")).into()
+    )
+}
+
+#[test]
+fn validate_empty() {
+    let d = Directory::default();
+    assert_eq!(d.validate(), Ok(()));
+}
+
+#[test]
+fn validate_invalid_names() {
+    {
+        let d = Directory {
+            directories: vec![DirectoryNode {
+                name: "".into(),
+                digest: DUMMY_DIGEST.to_vec().into(),
+                size: 42,
+            }],
+            ..Default::default()
+        };
+        match d.validate().expect_err("must fail") {
+            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
+                assert_eq!(n, b"")
+            }
+            _ => panic!("unexpected error"),
+        };
+    }
+
+    {
+        let d = Directory {
+            directories: vec![DirectoryNode {
+                name: ".".into(),
+                digest: DUMMY_DIGEST.to_vec().into(),
+                size: 42,
+            }],
+            ..Default::default()
+        };
+        match d.validate().expect_err("must fail") {
+            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
+                assert_eq!(n, b".")
+            }
+            _ => panic!("unexpected error"),
+        };
+    }
+
+    {
+        let d = Directory {
+            files: vec![FileNode {
+                name: "..".into(),
+                digest: DUMMY_DIGEST.to_vec().into(),
+                size: 42,
+                executable: false,
+            }],
+            ..Default::default()
+        };
+        match d.validate().expect_err("must fail") {
+            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
+                assert_eq!(n, b"..")
+            }
+            _ => panic!("unexpected error"),
+        };
+    }
+
+    {
+        let d = Directory {
+            symlinks: vec![SymlinkNode {
+                name: "\x00".into(),
+                target: "foo".into(),
+            }],
+            ..Default::default()
+        };
+        match d.validate().expect_err("must fail") {
+            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
+                assert_eq!(n, b"\x00")
+            }
+            _ => panic!("unexpected error"),
+        };
+    }
+
+    {
+        let d = Directory {
+            symlinks: vec![SymlinkNode {
+                name: "foo/bar".into(),
+                target: "foo".into(),
+            }],
+            ..Default::default()
+        };
+        match d.validate().expect_err("must fail") {
+            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
+                assert_eq!(n, b"foo/bar")
+            }
+            _ => panic!("unexpected error"),
+        };
+    }
+}
+
+#[test]
+fn validate_invalid_digest() {
+    let d = Directory {
+        directories: vec![DirectoryNode {
+            name: "foo".into(),
+            digest: vec![0x00, 0x42].into(), // invalid length
+            size: 42,
+        }],
+        ..Default::default()
+    };
+    match d.validate().expect_err("must fail") {
+        ValidateDirectoryError::InvalidNode(_, ValidateNodeError::InvalidDigestLen(n)) => {
+            assert_eq!(n, 2)
+        }
+        _ => panic!("unexpected error"),
+    }
+}
+
+#[test]
+fn validate_sorting() {
+    // "b" comes before "a", bad.
+    {
+        let d = Directory {
+            directories: vec![
+                DirectoryNode {
+                    name: "b".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: 42,
+                },
+                DirectoryNode {
+                    name: "a".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: 42,
+                },
+            ],
+            ..Default::default()
+        };
+        match d.validate().expect_err("must fail") {
+            ValidateDirectoryError::WrongSorting(s) => {
+                assert_eq!(s, b"a");
+            }
+            _ => panic!("unexpected error"),
+        }
+    }
+
+    // "a" exists twice, bad.
+    {
+        let d = Directory {
+            directories: vec![
+                DirectoryNode {
+                    name: "a".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: 42,
+                },
+                DirectoryNode {
+                    name: "a".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: 42,
+                },
+            ],
+            ..Default::default()
+        };
+        match d.validate().expect_err("must fail") {
+            ValidateDirectoryError::DuplicateName(s) => {
+                assert_eq!(s, b"a");
+            }
+            _ => panic!("unexpected error"),
+        }
+    }
+
+    // "a" comes before "b", all good.
+    {
+        let d = Directory {
+            directories: vec![
+                DirectoryNode {
+                    name: "a".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: 42,
+                },
+                DirectoryNode {
+                    name: "b".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: 42,
+                },
+            ],
+            ..Default::default()
+        };
+
+        d.validate().expect("validate shouldn't error");
+    }
+
+    // [b, c] and [a] are both properly sorted.
+    {
+        let d = Directory {
+            directories: vec![
+                DirectoryNode {
+                    name: "b".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: 42,
+                },
+                DirectoryNode {
+                    name: "c".into(),
+                    digest: DUMMY_DIGEST.to_vec().into(),
+                    size: 42,
+                },
+            ],
+            symlinks: vec![SymlinkNode {
+                name: "a".into(),
+                target: "foo".into(),
+            }],
+            ..Default::default()
+        };
+
+        d.validate().expect("validate shouldn't error");
+    }
+}
+
+#[test]
+fn validate_overflow() {
+    let d = Directory {
+        directories: vec![DirectoryNode {
+            name: "foo".into(),
+            digest: DUMMY_DIGEST.to_vec().into(),
+            size: u64::MAX,
+        }],
+        ..Default::default()
+    };
+
+    match d.validate().expect_err("must fail") {
+        ValidateDirectoryError::SizeOverflow => {}
+        _ => panic!("unexpected error"),
+    }
+}
+
+#[test]
+fn add_nodes_to_directory() {
+    let mut d = Directory {
+        ..Default::default()
+    };
+
+    d.add(node::Node::Directory(DirectoryNode {
+        name: "b".into(),
+        digest: DUMMY_DIGEST.to_vec().into(),
+        size: 1,
+    }));
+    d.add(node::Node::Directory(DirectoryNode {
+        name: "a".into(),
+        digest: DUMMY_DIGEST.to_vec().into(),
+        size: 1,
+    }));
+    d.add(node::Node::Directory(DirectoryNode {
+        name: "z".into(),
+        digest: DUMMY_DIGEST.to_vec().into(),
+        size: 1,
+    }));
+
+    d.add(node::Node::File(FileNode {
+        name: "f".into(),
+        digest: DUMMY_DIGEST.to_vec().into(),
+        size: 1,
+        executable: true,
+    }));
+    d.add(node::Node::File(FileNode {
+        name: "c".into(),
+        digest: DUMMY_DIGEST.to_vec().into(),
+        size: 1,
+        executable: true,
+    }));
+    d.add(node::Node::File(FileNode {
+        name: "g".into(),
+        digest: DUMMY_DIGEST.to_vec().into(),
+        size: 1,
+        executable: true,
+    }));
+
+    d.add(node::Node::Symlink(SymlinkNode {
+        name: "t".into(),
+        target: "a".into(),
+    }));
+    d.add(node::Node::Symlink(SymlinkNode {
+        name: "o".into(),
+        target: "a".into(),
+    }));
+    d.add(node::Node::Symlink(SymlinkNode {
+        name: "e".into(),
+        target: "a".into(),
+    }));
+
+    d.validate().expect("directory should be valid");
+}
+
+#[test]
+#[cfg_attr(not(debug_assertions), ignore)]
+#[should_panic = "name already exists in directories"]
+fn add_duplicate_node_to_directory_panics() {
+    let mut d = Directory {
+        ..Default::default()
+    };
+
+    d.add(node::Node::Directory(DirectoryNode {
+        name: "a".into(),
+        digest: DUMMY_DIGEST.to_vec().into(),
+        size: 1,
+    }));
+    d.add(node::Node::File(FileNode {
+        name: "a".into(),
+        digest: DUMMY_DIGEST.to_vec().into(),
+        size: 1,
+        executable: true,
+    }));
+}
diff --git a/tvix/castore/src/proto/tests/directory_nodes_iterator.rs b/tvix/castore/src/proto/tests/directory_nodes_iterator.rs
new file mode 100644
index 0000000000..68f147a332
--- /dev/null
+++ b/tvix/castore/src/proto/tests/directory_nodes_iterator.rs
@@ -0,0 +1,78 @@
+use crate::proto::Directory;
+use crate::proto::DirectoryNode;
+use crate::proto::FileNode;
+use crate::proto::NamedNode;
+use crate::proto::SymlinkNode;
+
+#[test]
+fn iterator() {
+    let d = Directory {
+        directories: vec![
+            DirectoryNode {
+                name: "c".into(),
+                ..DirectoryNode::default()
+            },
+            DirectoryNode {
+                name: "d".into(),
+                ..DirectoryNode::default()
+            },
+            DirectoryNode {
+                name: "h".into(),
+                ..DirectoryNode::default()
+            },
+            DirectoryNode {
+                name: "l".into(),
+                ..DirectoryNode::default()
+            },
+        ],
+        files: vec![
+            FileNode {
+                name: "b".into(),
+                ..FileNode::default()
+            },
+            FileNode {
+                name: "e".into(),
+                ..FileNode::default()
+            },
+            FileNode {
+                name: "g".into(),
+                ..FileNode::default()
+            },
+            FileNode {
+                name: "j".into(),
+                ..FileNode::default()
+            },
+        ],
+        symlinks: vec![
+            SymlinkNode {
+                name: "a".into(),
+                ..SymlinkNode::default()
+            },
+            SymlinkNode {
+                name: "f".into(),
+                ..SymlinkNode::default()
+            },
+            SymlinkNode {
+                name: "i".into(),
+                ..SymlinkNode::default()
+            },
+            SymlinkNode {
+                name: "k".into(),
+                ..SymlinkNode::default()
+            },
+        ],
+    };
+
+    // We keep this strings here and convert to string to make the comparison
+    // less messy.
+    let mut node_names: Vec<String> = vec![];
+
+    for node in d.nodes() {
+        node_names.push(String::from_utf8(node.get_name().to_vec()).unwrap());
+    }
+
+    assert_eq!(
+        vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"],
+        node_names
+    );
+}
diff --git a/tvix/castore/src/proto/tests/mod.rs b/tvix/castore/src/proto/tests/mod.rs
new file mode 100644
index 0000000000..8d903bacb6
--- /dev/null
+++ b/tvix/castore/src/proto/tests/mod.rs
@@ -0,0 +1,2 @@
+mod directory;
+mod directory_nodes_iterator;
diff --git a/tvix/castore/src/tests/import.rs b/tvix/castore/src/tests/import.rs
new file mode 100644
index 0000000000..8b3bd5ce0f
--- /dev/null
+++ b/tvix/castore/src/tests/import.rs
@@ -0,0 +1,129 @@
+use crate::blobservice::{self, BlobService};
+use crate::directoryservice;
+use crate::fixtures::*;
+use crate::import::fs::ingest_path;
+use crate::proto;
+
+use std::sync::Arc;
+use tempfile::TempDir;
+
+#[cfg(target_family = "unix")]
+use std::os::unix::ffi::OsStrExt;
+
+#[cfg(target_family = "unix")]
+#[tokio::test]
+async fn symlink() {
+    let blob_service = blobservice::from_addr("memory://").await.unwrap();
+    let directory_service = directoryservice::from_addr("memory://").await.unwrap();
+
+    let tmpdir = TempDir::new().unwrap();
+
+    std::fs::create_dir_all(&tmpdir).unwrap();
+    std::os::unix::fs::symlink(
+        "/nix/store/somewhereelse",
+        tmpdir.path().join("doesntmatter"),
+    )
+    .unwrap();
+
+    let root_node = ingest_path(
+        Arc::from(blob_service),
+        directory_service,
+        tmpdir.path().join("doesntmatter"),
+    )
+    .await
+    .expect("must succeed");
+
+    assert_eq!(
+        proto::node::Node::Symlink(proto::SymlinkNode {
+            name: "doesntmatter".into(),
+            target: "/nix/store/somewhereelse".into(),
+        }),
+        root_node,
+    )
+}
+
+#[tokio::test]
+async fn single_file() {
+    let blob_service =
+        Arc::from(blobservice::from_addr("memory://").await.unwrap()) as Arc<dyn BlobService>;
+    let directory_service = directoryservice::from_addr("memory://").await.unwrap();
+
+    let tmpdir = TempDir::new().unwrap();
+
+    std::fs::write(tmpdir.path().join("root"), HELLOWORLD_BLOB_CONTENTS).unwrap();
+
+    let root_node = ingest_path(
+        blob_service.clone(),
+        directory_service,
+        tmpdir.path().join("root"),
+    )
+    .await
+    .expect("must succeed");
+
+    assert_eq!(
+        proto::node::Node::File(proto::FileNode {
+            name: "root".into(),
+            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+            size: HELLOWORLD_BLOB_CONTENTS.len() as u64,
+            executable: false,
+        }),
+        root_node,
+    );
+
+    // ensure the blob has been uploaded
+    assert!(blob_service.has(&HELLOWORLD_BLOB_DIGEST).await.unwrap());
+}
+
+#[cfg(target_family = "unix")]
+#[tokio::test]
+async fn complicated() {
+    let blob_service =
+        Arc::from(blobservice::from_addr("memory://").await.unwrap()) as Arc<dyn BlobService>;
+    let directory_service = directoryservice::from_addr("memory://").await.unwrap();
+
+    let tmpdir = TempDir::new().unwrap();
+
+    // File ``.keep`
+    std::fs::write(tmpdir.path().join(".keep"), vec![]).unwrap();
+    // Symlink `aa`
+    std::os::unix::fs::symlink("/nix/store/somewhereelse", tmpdir.path().join("aa")).unwrap();
+    // Directory `keep`
+    std::fs::create_dir(tmpdir.path().join("keep")).unwrap();
+    // File ``keep/.keep`
+    std::fs::write(tmpdir.path().join("keep").join(".keep"), vec![]).unwrap();
+
+    let root_node = ingest_path(blob_service.clone(), &directory_service, tmpdir.path())
+        .await
+        .expect("must succeed");
+
+    // ensure root_node matched expectations
+    assert_eq!(
+        proto::node::Node::Directory(proto::DirectoryNode {
+            name: tmpdir
+                .path()
+                .file_name()
+                .unwrap()
+                .as_bytes()
+                .to_owned()
+                .into(),
+            digest: DIRECTORY_COMPLICATED.digest().into(),
+            size: DIRECTORY_COMPLICATED.size(),
+        }),
+        root_node,
+    );
+
+    // ensure DIRECTORY_WITH_KEEP and DIRECTORY_COMPLICATED have been uploaded
+    assert!(directory_service
+        .get(&DIRECTORY_WITH_KEEP.digest())
+        .await
+        .unwrap()
+        .is_some());
+    assert!(directory_service
+        .get(&DIRECTORY_COMPLICATED.digest())
+        .await
+        .unwrap()
+        .is_some());
+
+    // ensure EMPTY_BLOB_CONTENTS has been uploaded
+    assert!(blob_service.has(&EMPTY_BLOB_DIGEST).await.unwrap());
+}
diff --git a/tvix/castore/src/tests/mod.rs b/tvix/castore/src/tests/mod.rs
new file mode 100644
index 0000000000..d016f3e0aa
--- /dev/null
+++ b/tvix/castore/src/tests/mod.rs
@@ -0,0 +1 @@
+mod import;
diff --git a/tvix/castore/src/tonic.rs b/tvix/castore/src/tonic.rs
new file mode 100644
index 0000000000..4b65d6b028
--- /dev/null
+++ b/tvix/castore/src/tonic.rs
@@ -0,0 +1,122 @@
+use tokio::net::UnixStream;
+use tonic::transport::{Channel, Endpoint};
+
+fn url_wants_wait_connect(url: &url::Url) -> bool {
+    url.query_pairs()
+        .filter(|(k, v)| k == "wait-connect" && v == "1")
+        .count()
+        > 0
+}
+
+/// Turn a [url::Url] to a [Channel] if it can be parsed successfully.
+/// It supports the following schemes (and URLs):
+///  - `grpc+http://[::1]:8000`, connecting over unencrypted HTTP/2 (h2c)
+///  - `grpc+https://[::1]:8000`, connecting over encrypted HTTP/2
+///  - `grpc+unix:/path/to/socket`, connecting to a unix domain socket
+///
+/// All URLs support adding `wait-connect=1` as a URL parameter, in which case
+/// the connection is established lazily.
+pub async fn channel_from_url(url: &url::Url) -> Result<Channel, self::Error> {
+    match url.scheme() {
+        "grpc+unix" => {
+            if url.host_str().is_some() {
+                return Err(Error::HostSetForUnixSocket());
+            }
+
+            let connector = tower::service_fn({
+                let url = url.clone();
+                move |_: tonic::transport::Uri| UnixStream::connect(url.path().to_string().clone())
+            });
+
+            // the URL doesn't matter
+            let endpoint = Endpoint::from_static("http://[::]:50051");
+            if url_wants_wait_connect(url) {
+                Ok(endpoint.connect_with_connector(connector).await?)
+            } else {
+                Ok(endpoint.connect_with_connector_lazy(connector))
+            }
+        }
+        _ => {
+            // ensure path is empty, not supported with gRPC.
+            if !url.path().is_empty() {
+                return Err(Error::PathMayNotBeSet());
+            }
+
+            // Stringify the URL and remove the grpc+ prefix.
+            // We can't use `url.set_scheme(rest)`, as it disallows
+            // setting something http(s) that previously wasn't.
+            let unprefixed_url_str = match url.to_string().strip_prefix("grpc+") {
+                None => return Err(Error::MissingGRPCPrefix()),
+                Some(url_str) => url_str.to_owned(),
+            };
+
+            // Use the regular tonic transport::Endpoint logic, but unprefixed_url_str,
+            // as tonic doesn't know about grpc+http[s].
+            let endpoint = Endpoint::try_from(unprefixed_url_str)?;
+            if url_wants_wait_connect(url) {
+                Ok(endpoint.connect().await?)
+            } else {
+                Ok(endpoint.connect_lazy())
+            }
+        }
+    }
+}
+
+/// Errors occuring when trying to connect to a backend
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    #[error("grpc+ prefix is missing from URL")]
+    MissingGRPCPrefix(),
+
+    #[error("host may not be set for unix domain sockets")]
+    HostSetForUnixSocket(),
+
+    #[error("path may not be set")]
+    PathMayNotBeSet(),
+
+    #[error("transport error: {0}")]
+    TransportError(tonic::transport::Error),
+}
+
+impl From<tonic::transport::Error> for Error {
+    fn from(value: tonic::transport::Error) -> Self {
+        Self::TransportError(value)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::channel_from_url;
+    use rstest::rstest;
+    use url::Url;
+
+    #[rstest]
+    /// Correct scheme to connect to a unix socket.
+    #[case::valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
+    /// Connecting with wait-connect set to 0 succeeds, as that's the default.
+    #[case::valid_unix_socket_wait_connect_0("grpc+unix:///path/to/somewhere?wait-connect=0", true)]
+    /// Connecting with wait-connect set to 1 fails, as the path doesn't exist.
+    #[case::valid_unix_socket_wait_connect_1(
+        "grpc+unix:///path/to/somewhere?wait-connect=1",
+        false
+    )]
+    /// Correct scheme for unix socket, but setting a host too, which is invalid.
+    #[case::invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
+    /// Correct scheme to connect to localhost, with port 12345
+    #[case::valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::valid_http_host_without_port("grpc+http://localhost", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::valid_https_host_without_port("grpc+https://localhost", true)]
+    /// Correct scheme to connect to localhost over http, but with additional path, which is invalid.
+    #[case::invalid_host_and_path("grpc+http://localhost/some-path", false)]
+    /// Connecting with wait-connect set to 0 succeeds, as that's the default.
+    #[case::valid_host_wait_connect_0("grpc+http://localhost?wait-connect=0", true)]
+    /// Connecting with wait-connect set to 1 fails, as the host doesn't exist.
+    #[case::valid_host_wait_connect_1_fails("grpc+http://nonexist.invalid?wait-connect=1", false)]
+    #[tokio::test]
+    async fn test_from_addr_tokio(#[case] uri_str: &str, #[case] is_ok: bool) {
+        let url = Url::parse(uri_str).expect("must parse");
+        assert_eq!(channel_from_url(&url).await.is_ok(), is_ok)
+    }
+}
diff --git a/tvix/cli/Cargo.toml b/tvix/cli/Cargo.toml
new file mode 100644
index 0000000000..ce8d361771
--- /dev/null
+++ b/tvix/cli/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "tvix-cli"
+version = "0.1.0"
+edition = "2021"
+
+[[bin]]
+name = "tvix"
+path = "src/main.rs"
+
+[dependencies]
+nix-compat = { path = "../nix-compat" }
+tvix-build = { path = "../build" }
+tvix-castore = { path = "../castore" }
+tvix-store = { path = "../store", default-features = false, features = []}
+tvix-eval = { path = "../eval" }
+tvix-glue = { path = "../glue" }
+bytes = "1.4.0"
+clap = { version = "4.0", features = ["derive", "env"] }
+dirs = "4.0.0"
+rustyline = "10.0.0"
+thiserror = "1.0.38"
+tokio = "1.28.0"
+tracing = { version = "0.1.37", features = ["max_level_trace", "release_max_level_info"] }
+tracing-subscriber = { version = "0.3.16", features = ["json"] }
+
+[dependencies.wu-manber]
+git = "https://github.com/tvlfyi/wu-manber.git"
diff --git a/tvix/cli/default.nix b/tvix/cli/default.nix
new file mode 100644
index 0000000000..62e93cc213
--- /dev/null
+++ b/tvix/cli/default.nix
@@ -0,0 +1,95 @@
+{ depot, pkgs, lib, ... }:
+
+(depot.tvix.crates.workspaceMembers.tvix-cli.build.override {
+  runTests = true;
+}).overrideAttrs (finalAttrs: previousAttrs:
+
+let
+  tvix-cli = finalAttrs.finalPackage;
+
+  benchmark-gnutime-format-string =
+    description:
+    "Benchmark: " +
+    (builtins.toJSON {
+      "${description}" = {
+        kbytes = "%M";
+        system = "%S";
+        user = "%U";
+      };
+    });
+
+  # You can run the benchmark with a simple `nix run`, like:
+  #
+  #  nix-build -A tvix.cli.meta.ci.extraSteps.benchmark-nixpkgs-cross-hello-outpath
+  #
+  # TODO(amjoseph): store these results someplace more durable, like git trailers
+  #
+  mkExprBenchmark = { expr, description }:
+    let name = "tvix-cli-benchmark-${description}"; in
+    (pkgs.runCommand name { } ''
+      export SSL_CERT_FILE=${pkgs.cacert.out}/etc/ssl/certs/ca-bundle.crt
+      ${lib.escapeShellArgs [
+        "${pkgs.time}/bin/time"
+        "--format" "${benchmark-gnutime-format-string description}"
+        "${tvix-cli}/bin/tvix"
+        "--no-warnings"
+        "-E" expr
+      ]}
+      touch $out
+    '');
+
+  mkNixpkgsBenchmark = attrpath:
+    mkExprBenchmark {
+      description = builtins.replaceStrings [ ".drv" ] [ "-drv" ] attrpath;
+      expr = "(import ${pkgs.path} {}).${attrpath}";
+    };
+
+  # Constructs a Derivation invoking tvix-cli inside a build, ensures the
+  # calculated tvix output path matches what's passed in externally.
+  mkNixpkgsEvalTest = attrpath: expectedPath:
+    let
+      name = "tvix-eval-test-${builtins.replaceStrings [".drv"] ["-drv"] attrpath}";
+    in
+    (pkgs.runCommand name { } ''
+      export SSL_CERT_FILE=${pkgs.cacert.out}/etc/ssl/certs/ca-bundle.crt
+      TVIX_OUTPUT=$(${tvix-cli}/bin/tvix -E '(import ${pkgs.path} {}).${attrpath}')
+      EXPECTED='${/* the verbatim expected Tvix output: */ "=> \"${builtins.unsafeDiscardStringContext expectedPath}\" :: string"}'
+
+      echo "Tvix output: ''${TVIX_OUTPUT}"
+      if [ "$TVIX_OUTPUT" != "$EXPECTED" ]; then
+        echo "Correct would have been ''${EXPECTED}"
+        exit 1
+      fi
+
+      echo "Output was correct."
+      touch $out
+    '');
+
+
+  benchmarks = {
+    benchmark-hello = (mkNixpkgsBenchmark "hello.outPath");
+    benchmark-cross-hello = (mkNixpkgsBenchmark "pkgsCross.aarch64-multiplatform.hello.outPath");
+    benchmark-firefox = (mkNixpkgsBenchmark "firefox.outPath");
+    benchmark-cross-firefox = (mkNixpkgsBenchmark "pkgsCross.aarch64-multiplatform.firefox.outPath");
+    # Example used for benchmarking LightSpan::Delayed in commit bf286a54bc2ac5eeb78c3d5c5ae66e9af24d74d4
+    benchmark-nixpkgs-attrnames = (mkExprBenchmark { expr = "builtins.length (builtins.attrNames (import ${pkgs.path} {}))"; description = "nixpkgs-attrnames"; });
+  };
+
+  evalTests = {
+    eval-nixpkgs-stdenv-drvpath = (mkNixpkgsEvalTest "stdenv.drvPath" pkgs.stdenv.drvPath);
+    eval-nixpkgs-stdenv-outpath = (mkNixpkgsEvalTest "stdenv.outPath" pkgs.stdenv.outPath);
+    eval-nixpkgs-hello-outpath = (mkNixpkgsEvalTest "hello.outPath" pkgs.hello.outPath);
+    eval-nixpkgs-firefox-outpath = (mkNixpkgsEvalTest "firefox.outPath" pkgs.firefox.outPath);
+    eval-nixpkgs-firefox-drvpath = (mkNixpkgsEvalTest "firefox.drvPath" pkgs.firefox.drvPath);
+    eval-nixpkgs-cross-stdenv-outpath = (mkNixpkgsEvalTest "pkgsCross.aarch64-multiplatform.stdenv.outPath" pkgs.pkgsCross.aarch64-multiplatform.stdenv.outPath);
+    eval-nixpkgs-cross-hello-outpath = (mkNixpkgsEvalTest "pkgsCross.aarch64-multiplatform.hello.outPath" pkgs.pkgsCross.aarch64-multiplatform.hello.outPath);
+  };
+in
+{
+  meta = {
+    ci.targets = (builtins.attrNames benchmarks) ++ (builtins.attrNames evalTests);
+  };
+
+  # Expose benchmarks and evalTests as standard CI targets.
+  passthru = benchmarks // evalTests;
+})
diff --git a/tvix/cli/src/main.rs b/tvix/cli/src/main.rs
new file mode 100644
index 0000000000..5635f446b9
--- /dev/null
+++ b/tvix/cli/src/main.rs
@@ -0,0 +1,337 @@
+use clap::Parser;
+use rustyline::{error::ReadlineError, Editor};
+use std::rc::Rc;
+use std::{fs, path::PathBuf};
+use tracing::Level;
+use tracing_subscriber::fmt::writer::MakeWriterExt;
+use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
+use tracing_subscriber::{EnvFilter, Layer};
+use tvix_build::buildservice;
+use tvix_eval::builtins::impure_builtins;
+use tvix_eval::observer::{DisassemblingObserver, TracingObserver};
+use tvix_eval::{EvalIO, Value};
+use tvix_glue::builtins::add_fetcher_builtins;
+use tvix_glue::builtins::add_import_builtins;
+use tvix_glue::tvix_io::TvixIO;
+use tvix_glue::tvix_store_io::TvixStoreIO;
+use tvix_glue::{builtins::add_derivation_builtins, configure_nix_path};
+
+#[derive(Parser)]
+struct Args {
+    #[arg(long)]
+    log_level: Option<Level>,
+
+    /// Path to a script to evaluate
+    script: Option<PathBuf>,
+
+    #[clap(long, short = 'E')]
+    expr: Option<String>,
+
+    /// Dump the raw AST to stdout before interpreting
+    #[clap(long, env = "TVIX_DISPLAY_AST")]
+    display_ast: bool,
+
+    /// Dump the bytecode to stdout before evaluating
+    #[clap(long, env = "TVIX_DUMP_BYTECODE")]
+    dump_bytecode: bool,
+
+    /// Trace the runtime of the VM
+    #[clap(long, env = "TVIX_TRACE_RUNTIME")]
+    trace_runtime: bool,
+
+    /// Capture the time (relative to the start time of evaluation) of all events traced with
+    /// `--trace-runtime`
+    #[clap(long, env = "TVIX_TRACE_RUNTIME_TIMING", requires("trace_runtime"))]
+    trace_runtime_timing: bool,
+
+    /// Only compile, but do not execute code. This will make Tvix act
+    /// sort of like a linter.
+    #[clap(long)]
+    compile_only: bool,
+
+    /// Don't print warnings.
+    #[clap(long)]
+    no_warnings: bool,
+
+    /// A colon-separated list of directories to use to resolve `<...>`-style paths
+    #[clap(long, short = 'I', env = "NIX_PATH")]
+    nix_search_path: Option<String>,
+
+    /// Print "raw" (unquoted) output.
+    #[clap(long)]
+    raw: bool,
+
+    /// Strictly evaluate values, traversing them and forcing e.g.
+    /// elements of lists and attribute sets before printing the
+    /// return value.
+    #[clap(long)]
+    strict: bool,
+
+    #[arg(long, env, default_value = "memory://")]
+    blob_service_addr: String,
+
+    #[arg(long, env, default_value = "memory://")]
+    directory_service_addr: String,
+
+    #[arg(long, env, default_value = "memory://")]
+    path_info_service_addr: String,
+
+    #[arg(long, env, default_value = "dummy://")]
+    build_service_addr: String,
+}
+
+/// Interprets the given code snippet, printing out warnings, errors
+/// and the result itself. The return value indicates whether
+/// evaluation succeeded.
+fn interpret(code: &str, path: Option<PathBuf>, args: &Args, explain: bool) -> bool {
+    let tokio_runtime = tokio::runtime::Runtime::new().expect("failed to setup tokio runtime");
+
+    let (blob_service, directory_service, path_info_service) = tokio_runtime
+        .block_on({
+            let blob_service_addr = args.blob_service_addr.clone();
+            let directory_service_addr = args.directory_service_addr.clone();
+            let path_info_service_addr = args.path_info_service_addr.clone();
+            async move {
+                tvix_store::utils::construct_services(
+                    blob_service_addr,
+                    directory_service_addr,
+                    path_info_service_addr,
+                )
+                .await
+            }
+        })
+        .expect("unable to setup {blob|directory|pathinfo}service before interpreter setup");
+
+    let build_service = tokio_runtime
+        .block_on({
+            let blob_service = blob_service.clone();
+            let directory_service = directory_service.clone();
+            async move {
+                buildservice::from_addr(
+                    &args.build_service_addr,
+                    blob_service.clone(),
+                    directory_service.clone(),
+                )
+                .await
+            }
+        })
+        .expect("unable to setup buildservice before interpreter setup");
+
+    let tvix_store_io = Rc::new(TvixStoreIO::new(
+        blob_service.clone(),
+        directory_service.clone(),
+        path_info_service.into(),
+        build_service.into(),
+        tokio_runtime.handle().clone(),
+    ));
+
+    let mut eval = tvix_eval::Evaluation::new(
+        Box::new(TvixIO::new(tvix_store_io.clone() as Rc<dyn EvalIO>)) as Box<dyn EvalIO>,
+        true,
+    );
+    eval.strict = args.strict;
+    eval.builtins.extend(impure_builtins());
+    add_derivation_builtins(&mut eval, Rc::clone(&tvix_store_io));
+    add_fetcher_builtins(&mut eval, Rc::clone(&tvix_store_io));
+    add_import_builtins(&mut eval, tvix_store_io);
+    configure_nix_path(&mut eval, &args.nix_search_path);
+
+    let source_map = eval.source_map();
+    let result = {
+        let mut compiler_observer =
+            DisassemblingObserver::new(source_map.clone(), std::io::stderr());
+        if args.dump_bytecode {
+            eval.compiler_observer = Some(&mut compiler_observer);
+        }
+
+        let mut runtime_observer = TracingObserver::new(std::io::stderr());
+        if args.trace_runtime {
+            if args.trace_runtime_timing {
+                runtime_observer.enable_timing()
+            }
+            eval.runtime_observer = Some(&mut runtime_observer);
+        }
+
+        eval.evaluate(code, path)
+    };
+
+    if args.display_ast {
+        if let Some(ref expr) = result.expr {
+            eprintln!("AST: {}", tvix_eval::pretty_print_expr(expr));
+        }
+    }
+
+    for error in &result.errors {
+        error.fancy_format_stderr();
+    }
+
+    if !args.no_warnings {
+        for warning in &result.warnings {
+            warning.fancy_format_stderr(&source_map);
+        }
+    }
+
+    if let Some(value) = result.value.as_ref() {
+        if explain {
+            println!("=> {}", value.explain());
+        } else {
+            println_result(value, args.raw);
+        }
+    }
+
+    // inform the caller about any errors
+    result.errors.is_empty()
+}
+
+/// Interpret the given code snippet, but only run the Tvix compiler
+/// on it and return errors and warnings.
+fn lint(code: &str, path: Option<PathBuf>, args: &Args) -> bool {
+    let mut eval = tvix_eval::Evaluation::new_impure();
+    eval.strict = args.strict;
+
+    let source_map = eval.source_map();
+
+    let mut compiler_observer = DisassemblingObserver::new(source_map.clone(), std::io::stderr());
+
+    if args.dump_bytecode {
+        eval.compiler_observer = Some(&mut compiler_observer);
+    }
+
+    if args.trace_runtime {
+        eprintln!("warning: --trace-runtime has no effect with --compile-only!");
+    }
+
+    let result = eval.compile_only(code, path);
+
+    if args.display_ast {
+        if let Some(ref expr) = result.expr {
+            eprintln!("AST: {}", tvix_eval::pretty_print_expr(expr));
+        }
+    }
+
+    for error in &result.errors {
+        error.fancy_format_stderr();
+    }
+
+    for warning in &result.warnings {
+        warning.fancy_format_stderr(&source_map);
+    }
+
+    // inform the caller about any errors
+    result.errors.is_empty()
+}
+
+fn main() {
+    let args = Args::parse();
+
+    // configure log settings
+    let level = args.log_level.unwrap_or(Level::INFO);
+
+    let subscriber = tracing_subscriber::registry().with(
+        tracing_subscriber::fmt::Layer::new()
+            .with_writer(std::io::stderr.with_max_level(level))
+            .compact()
+            .with_filter(
+                EnvFilter::builder()
+                    .with_default_directive(level.into())
+                    .from_env()
+                    .expect("invalid RUST_LOG"),
+            ),
+    );
+    subscriber
+        .try_init()
+        .expect("unable to set up tracing subscriber");
+
+    if let Some(file) = &args.script {
+        run_file(file.clone(), &args)
+    } else if let Some(expr) = &args.expr {
+        if !interpret(expr, None, &args, false) {
+            std::process::exit(1);
+        }
+    } else {
+        run_prompt(&args)
+    }
+}
+
+fn run_file(mut path: PathBuf, args: &Args) {
+    if path.is_dir() {
+        path.push("default.nix");
+    }
+    let contents = fs::read_to_string(&path).expect("failed to read the input file");
+
+    let success = if args.compile_only {
+        lint(&contents, Some(path), args)
+    } else {
+        interpret(&contents, Some(path), args, false)
+    };
+
+    if !success {
+        std::process::exit(1);
+    }
+}
+
+fn println_result(result: &Value, raw: bool) {
+    if raw {
+        println!("{}", result.to_contextful_str().unwrap())
+    } else {
+        println!("=> {} :: {}", result, result.type_of())
+    }
+}
+
+fn state_dir() -> Option<PathBuf> {
+    let mut path = dirs::data_dir();
+    if let Some(p) = path.as_mut() {
+        p.push("tvix")
+    }
+    path
+}
+
+fn run_prompt(args: &Args) {
+    let mut rl = Editor::<()>::new().expect("should be able to launch rustyline");
+
+    if args.compile_only {
+        eprintln!("warning: `--compile-only` has no effect on REPL usage!");
+    }
+
+    let history_path = match state_dir() {
+        // Attempt to set up these paths, but do not hard fail if it
+        // doesn't work.
+        Some(mut path) => {
+            let _ = std::fs::create_dir_all(&path);
+            path.push("history.txt");
+            let _ = rl.load_history(&path);
+            Some(path)
+        }
+
+        None => None,
+    };
+
+    loop {
+        let readline = rl.readline("tvix-repl> ");
+        match readline {
+            Ok(line) => {
+                if line.is_empty() {
+                    continue;
+                }
+
+                rl.add_history_entry(&line);
+
+                if let Some(without_prefix) = line.strip_prefix(":d ") {
+                    interpret(without_prefix, None, args, true);
+                } else {
+                    interpret(&line, None, args, false);
+                }
+            }
+            Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break,
+
+            Err(err) => {
+                eprintln!("error: {}", err);
+                break;
+            }
+        }
+    }
+
+    if let Some(path) = history_path {
+        rl.save_history(&path).unwrap();
+    }
+}
diff --git a/tvix/clippy.toml b/tvix/clippy.toml
new file mode 100644
index 0000000000..be7709684c
--- /dev/null
+++ b/tvix/clippy.toml
@@ -0,0 +1,6 @@
+# prevents a false-positive lint on our types containing bytes::Bytes
+# https://rust-lang.github.io/rust-clippy/master/index.html#/mutable_key_type
+ignore-interior-mutability = [
+  "bytes::Bytes",
+  "tvix_castore::digests::B3Digest"
+]
diff --git a/tvix/crate-hashes.json b/tvix/crate-hashes.json
new file mode 100644
index 0000000000..2c1e740cb9
--- /dev/null
+++ b/tvix/crate-hashes.json
@@ -0,0 +1,4 @@
+{
+  "git+https://github.com/flokli/bigtable_rs?rev=0af404741dfc40eb9fa99cf4d4140a09c5c20df7#0.2.9": "1njjam1lx2xlnm7a41lga8601vmjgqz0fvc77x24gd04pc7avxll",
+  "git+https://github.com/tvlfyi/wu-manber.git#wu-manber@0.1.0": "1zhk83lbq99xzyjwphv2qrb8f8qgfqwa5bbbvyzm0z0bljsjv0pd"
+}
\ No newline at end of file
diff --git a/tvix/default.nix b/tvix/default.nix
index 82cd87cda4..f562cf37de 100644
--- a/tvix/default.nix
+++ b/tvix/default.nix
@@ -1,2 +1,238 @@
-{...}:
-{}
+# Nix helpers for projects under //tvix
+{ pkgs, lib, depot, ... }:
+
+let
+  # crate override for crates that need protobuf
+  protobufDep = prev: (prev.nativeBuildInputs or [ ]) ++ [ pkgs.buildPackages.protobuf ];
+  iconvDarwinDep = lib.optional pkgs.stdenv.isDarwin pkgs.libiconv;
+
+  # On Darwin, some crates producing binaries need to be able to link against security.
+  darwinDeps = lib.optionals pkgs.stdenv.isDarwin (with pkgs.buildPackages.darwin.apple_sdk.frameworks; [
+    Security
+    SystemConfiguration
+  ]);
+
+  # Load the crate2nix crate tree.
+  crates = import ./Cargo.nix {
+    inherit pkgs;
+    nixpkgs = pkgs.path;
+
+    # Hack to fix Darwin build
+    # See https://github.com/NixOS/nixpkgs/issues/218712
+    buildRustCrateForPkgs = pkgs:
+      if pkgs.stdenv.isDarwin then
+        let
+          buildRustCrate = pkgs.buildRustCrate;
+          buildRustCrate_ = args: buildRustCrate args // { dontStrip = true; };
+          override = o: args: buildRustCrate.override o (args // { dontStrip = true; });
+        in
+        pkgs.makeOverridable override { }
+      else pkgs.buildRustCrate;
+
+    defaultCrateOverrides = pkgs.defaultCrateOverrides // {
+      zstd-sys = prev: {
+        nativeBuildInputs = prev.nativeBuildInputs or [ ];
+        buildInputs = prev.buildInputs or [ ] ++ iconvDarwinDep;
+      };
+
+      opentelemetry-proto = prev: {
+        nativeBuildInputs = protobufDep prev;
+      };
+
+      prost-build = prev: {
+        nativeBuildInputs = protobufDep prev;
+      };
+
+      prost-wkt-types = prev: {
+        nativeBuildInputs = protobufDep prev;
+      };
+
+      tonic-reflection = prev: {
+        nativeBuildInputs = protobufDep prev;
+      };
+
+      tvix-build = prev: {
+        PROTO_ROOT = depot.tvix.build.protos.protos;
+        nativeBuildInputs = protobufDep prev;
+        buildInputs = darwinDeps;
+      };
+
+      tvix-castore = prev: {
+        PROTO_ROOT = depot.tvix.castore.protos.protos;
+        nativeBuildInputs = protobufDep prev;
+      };
+
+      tvix-cli = prev: {
+        buildInputs = prev.buildInputs or [ ] ++ darwinDeps;
+      };
+
+      tvix-store = prev: {
+        PROTO_ROOT = depot.tvix.store.protos.protos;
+        nativeBuildInputs = protobufDep prev;
+        # fuse-backend-rs uses DiskArbitration framework to handle mount/unmount on Darwin
+        buildInputs = prev.buildInputs or [ ]
+          ++ darwinDeps
+          ++ lib.optional pkgs.stdenv.isDarwin pkgs.buildPackages.darwin.apple_sdk.frameworks.DiskArbitration;
+      };
+    };
+  };
+
+  # Cargo dependencies to be used with nixpkgs rustPlatform functions.
+  cargoDeps = pkgs.rustPlatform.importCargoLock {
+    lockFile = ./Cargo.lock;
+    # Extract the hashes from `crates` / Cargo.nix, we already get them from cargo2nix.
+    # This returns an attribute set containing "${crateName}-${version}" as key,
+    # and the outputHash as value.
+    outputHashes = builtins.listToAttrs
+      (map
+        (crateName:
+          (lib.nameValuePair "${crateName}-${crates.internal.crates.${crateName}.version}" crates.internal.crates.${crateName}.src.outputHash)
+        ) [
+        "bigtable_rs"
+        "wu-manber"
+      ]);
+  };
+
+  # The cleaned sources.
+  src = depot.third_party.gitignoreSource ./.;
+
+  # Target containing *all* tvix proto files.
+  # Useful for workspace-wide cargo invocations (doc, clippy)
+  protos = pkgs.symlinkJoin {
+    name = "tvix-all-protos";
+    paths = [
+      depot.tvix.build.protos.protos
+      depot.tvix.castore.protos.protos
+      depot.tvix.store.protos.protos
+    ];
+  };
+
+in
+{
+  inherit crates protos;
+
+  # Run crate2nix generate, ensure the output doesn't differ afterwards
+  # (and doesn't fail).
+  #
+  # Currently this re-downloads every crate every time
+  # crate2nix-check (but not crate2nix) is built.
+  # TODO(amjoseph): be less wasteful with bandwidth.
+  #
+  crate2nix-check =
+    let
+      outputHashAlgo = "sha256";
+    in
+    pkgs.stdenv.mkDerivation {
+      inherit src;
+
+      # Important: we include the hash of the Cargo.lock file and
+      # Cargo.nix file in the derivation name.  This forces the FOD
+      # to be rebuilt/reverified whenever either of them changes.
+      name = "tvix-crate2nix-check-" +
+        (builtins.substring 0 8 (builtins.hashFile "sha256" ./Cargo.lock)) +
+        "-" +
+        (builtins.substring 0 8 (builtins.hashFile "sha256" ./Cargo.nix));
+
+      nativeBuildInputs = with pkgs; [ git cacert cargo ];
+      buildPhase = ''
+        export CARGO_HOME=$(mktemp -d)
+
+        # The following command can be omitted, in which case
+        # crate2nix-generate will run it automatically, but won't show the
+        # output, which makes it look like the build is somehow "stuck" for a
+        # minute or two.
+        cargo metadata > /dev/null
+
+        # running this command counteracts depotfmt brokenness
+        git init
+
+        ${depot.tools.crate2nix-generate}/bin/crate2nix-generate
+
+        # technically unnecessary, but provides more-helpful output in case of error
+        diff -ur Cargo.nix ${src}/Cargo.nix
+
+        # the FOD hash will check that the (re-)generated Cargo.nix matches the committed Cargo.nix
+        cp Cargo.nix $out
+      '';
+
+      # This is an FOD in order to allow `cargo` to perform network access.
+      outputHashMode = "flat";
+      inherit outputHashAlgo;
+      outputHash = builtins.hashFile outputHashAlgo ./Cargo.nix;
+      env.SSL_CERT_FILE = "${pkgs.cacert.out}/etc/ssl/certs/ca-bundle.crt";
+    };
+
+  # Provide the Tvix logo in both .webp and .png format.
+  logo = pkgs.runCommand "logo"
+    {
+      nativeBuildInputs = [ pkgs.imagemagick ];
+    } ''
+    mkdir -p $out
+    cp ${./logo.webp} $out/logo.webp
+    convert $out/logo.webp $out/logo.png
+  '';
+
+  # Provide a shell for the combined dependencies of all Tvix Rust
+  # projects. Note that as this is manually maintained it may be
+  # lacking something, but it is required for some people's workflows.
+  #
+  # This shell can be entered with e.g. `mg shell //tvix:shell`.
+  # This is a separate file, so it can be used individually in the tvix josh
+  # workspace too.
+  shell = (import ./shell.nix { inherit pkgs; });
+
+  # Build the Rust documentation for publishing on docs.tvix.dev.
+  rust-docs = pkgs.stdenv.mkDerivation {
+    inherit cargoDeps src;
+    name = "tvix-rust-docs";
+    PROTO_ROOT = protos;
+
+    nativeBuildInputs = with pkgs; [
+      cargo
+      pkg-config
+      protobuf
+      rustc
+      rustPlatform.cargoSetupHook
+    ];
+
+    buildInputs = [
+      pkgs.fuse
+    ] ++ iconvDarwinDep;
+
+    buildPhase = ''
+      cargo doc --document-private-items
+      mv target/doc $out
+    '';
+  };
+
+  # Run cargo clippy. We run it with -Dwarnings, so warnings cause a nonzero
+  # exit code.
+  clippy = pkgs.stdenv.mkDerivation {
+    inherit cargoDeps src;
+    name = "tvix-clippy";
+    PROTO_ROOT = protos;
+
+    buildInputs = [
+      pkgs.fuse
+    ];
+    nativeBuildInputs = with pkgs; [
+      cargo
+      clippy
+      pkg-config
+      protobuf
+      rustc
+      rustPlatform.cargoSetupHook
+    ];
+
+    # Allow blocks_in_conditions due to false positives with #[tracing::instrument(…)]:
+    # https://github.com/rust-lang/rust-clippy/issues/12281
+    buildPhase = "cargo clippy --tests --all-features --benches --examples -- -Dwarnings -A clippy::blocks_in_conditions | tee $out";
+  };
+
+  meta.ci.targets = [
+    "clippy"
+    "crate2nix-check"
+    "shell"
+    "rust-docs"
+  ];
+}
diff --git a/tvix/docs/.gitignore b/tvix/docs/.gitignore
index 77699ee8a3..8117055463 100644
--- a/tvix/docs/.gitignore
+++ b/tvix/docs/.gitignore
@@ -1,2 +1,2 @@
-*.svg
-*.html
+book
+.mdbook-plantuml-cache/
diff --git a/tvix/docs/Makefile b/tvix/docs/Makefile
deleted file mode 100644
index ba9e2bdef6..0000000000
--- a/tvix/docs/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-all: build
-
-puml:
-	plantuml *.puml -tsvg
-
-html:
-	pandoc *.md -f markdown --self-contained -t html -s -o tvix.html --csl=${CSL}
-
-build: puml html
-
-clean:
-	rm -f *.tex *.pdf *.png *.svg
diff --git a/tvix/docs/book.toml b/tvix/docs/book.toml
new file mode 100644
index 0000000000..7318a90233
--- /dev/null
+++ b/tvix/docs/book.toml
@@ -0,0 +1,11 @@
+[book]
+authors = ["The Tvix Authors"]
+language = "en"
+multilingual = false
+src = "src"
+title = "Tvix Docs"
+
+[preprocessor.plantuml]
+# override the /usr/bin/plantuml default
+plantuml-cmd = "plantuml"
+use-data-uris = true
diff --git a/tvix/docs/component-flow.puml b/tvix/docs/component-flow.puml
deleted file mode 100644
index 3bcddbe746..0000000000
--- a/tvix/docs/component-flow.puml
+++ /dev/null
@@ -1,74 +0,0 @@
-@startuml
-
-title Tvix build flow
-
-actor User
-participant CLI
-participant "Coordinator" as Coord
-participant "Evaluator" as Eval
-database Store
-participant "Builder" as Build
-
-note over CLI,Eval
-    Typically runs locally on the invoking machine
-end note
-/ note over Store, Build
-    Can be either local or remote
-end note
-
-User-->CLI: User initiates build of `hello` (analogous to `nix-build -f '<nixpkgs>' -A hello`)
-
-CLI-->Coord: CLI invokes coordinator
-
-Coord-->Eval: Sends message to start evaluation of `<nixpkgs>` (path lookup) with attribute `hello`
-note right: The paths to the evaluator are local file system paths
-
-Coord<--Eval: Yields derivations to be built
-note right
-    Immediately starts streaming derivations as they are instantiated across
-    the dependency graph so they can be built while the evaluation is still running.
-
-    There are two types of build requests: One for regular "fire and forget" builds
-    and another for IFD (import from derivation).
-
-    These are distinct because IFD needs to be fed back into the evaluator for
-    further processing while a regular build does not.
-end note
-
-loop while has more derivations
-
-    Coord-->Store: Check if desired paths are in store
-    alt Store has path
-        Coord<--Store: Success response
-    else Store does not have path
-        Coord-->Build: Request derivation to be built
-        note left
-            The build request optionally includes a desired store.
-            If a builder is aware of how to push to the store it will do so
-            directly when the build is finished.
-
-            If the store is not known by the builder results will be streamed
-            back to the coordinator for store addition.
-        end note
-
-        alt Build failure
-            Coord<--Build: Fail response
-            note left: It's up to the coordinator whether to exit on build failure
-        else Build success
-            alt Known store
-                Build-->Store: Push outputs to store
-                Build<--Coord: Send success & pushed response
-            else Unknown store
-                Build<--Coord: Send success & not pushed response
-                Coord<--Build: Stream build outputs
-                Coord-->Store: Push outputs to store
-            end
-        end
-
-    end
-end
-
-CLI<--Coord: Respond success/fail
-User<--CLI: Exit success/fail
-
-@enduml
diff --git a/tvix/docs/components.md b/tvix/docs/components.md
deleted file mode 100644
index 19e7baa3ec..0000000000
--- a/tvix/docs/components.md
+++ /dev/null
@@ -1,114 +0,0 @@
----
-title: "Tvix - Architecture & data flow"
-numbersections: true
-author:
-- adisbladis
-- flokli
-- tazjin
-email:
-- adis@blad.is
-- mail@tazj.in
-lang: en-GB
-classoption:
-- twocolumn
-header-includes:
-- \usepackage{caption, graphicx, tikz, aeguill, pdflscape}
----
-
-# Background
-
-We intend for Tvix tooling to be more decoupled than the existing,
-monolithic Nix implementation. In practice, we expect to gain several
-benefits from this, such as:
-
-- Ability to use different builders
-- Ability to use different store implementations
-- No monopolisation of the implementation, allowing users to replace
-  components that they are unhappy with (up to and including the
-  language evaluator)
-- Less hidden intra-dependencies between tools due to explicit RPC/IPC
-  boundaries
-
-Communication between different components of the system will use
-gRPC. The rest of this document outlines the components.
-
-# Components
-
-## Coordinator
-
-*Purpose:* The coordinator (in the simplest case, the Tvix CLI tool)
-oversees the flow of a build process and delegates tasks to the right
-subcomponents. For example, if a user runs the equivalent of
-`nix-build` in a folder containing a `default.nix` file, the
-coordinator will invoke the evaluator, pass the resulting derivations
-to the builder and coordinate any necessary store interactions (for
-substitution and other purposes).
-
-While many users are likely to use the CLI tool as their primary
-method of interacting with Tvix, it is not unlikely that alternative
-coordinators (e.g. for a distributed, "Nix-native" CI system) would be
-implemented. To facilitate this, we are considering implementing the
-coordinator on top of a state-machine model that would make it
-possible to reuse the FSM logic without tying it to any particular
-kind of application.
-
-## Evaluator
-
-*Purpose:* Eval takes care of evaluating Nix code. In a typical build
-flow it would be responsible for producing derivations. It can also be
-used as a standalone tool, for example, in use-cases where Nix is used
-to generate configuration without any build or store involvement.
-
-*Requirements:* For now, it will run on the machine invoking the build
-command itself. We give it filesystem access to handle things like
-imports or `builtins.readFile`.
-
-In the future, we might abstract away raw filesystem access by
-allowing the evaluator to request files from the coordinator (which
-will query the store for it). This might get messy, and the benefits
-are questionable. We might be okay with running the evaluator with
-filesystem access for now and can extend the interface if the need
-arises.
-
-## Builder
-
-*Purpose:* A builder receives derivations from the coordinator and
-builds them.
-
-By making builder a standardised interface it's possible to make the
-sandboxing mechanism used by the build process pluggable.
-
-Nix is currently using a hard-coded
-[libseccomp](https://github.com/seccomp/libseccomp) based sandboxing
-mechanism and another one based on
-[sandboxd](https://www.unix.com/man-page/mojave/8/sandboxd/) on macOS.
-These are only separated by [compiler preprocessor
-macros](https://gcc.gnu.org/onlinedocs/cpp/Ifdef.html) within the same
-source files despite having very little in common with each other.
-
-This makes experimentation with alternative backends difficult and
-porting Nix to other platforms harder than it has to be. We want to
-write a new Linux builder which uses
-[OCI](https://github.com/opencontainers/runtime-spec), the current
-dominant Linux containerisation technology, by default.
-
-With a well-defined builder abstraction, it's also easy to imagine
-other backends such as a Kubernetes-based one in the future.
-
-## Store
-
-*Purpose:* Store takes care of storing build results. It provides a
-unified interface to get file paths and upload new ones.
-
-Most likely, we will end up with multiple implementations of store, a
-few possible ones that come to mind are:
-
-- Local
-- SSH
-- GCP
-- S3
-- Ceph
-
-# Figures
-
-![component flow](./component-flow.svg)
diff --git a/tvix/docs/default.nix b/tvix/docs/default.nix
index 4b09f8d2da..9fc2f76576 100644
--- a/tvix/docs/default.nix
+++ b/tvix/docs/default.nix
@@ -1,46 +1,23 @@
 { pkgs, lib, ... }:
 
-let
-
-  tl = pkgs.texlive.combine {
-    inherit (pkgs.texlive) scheme-medium wrapfig ulem capt-of
-    titlesec preprint enumitem paralist ctex environ svg
-    beamer trimspaces zhnumber changepage framed pdfpages
-    fvextra minted upquote ifplatform xstring;
-  };
-
-  csl = pkgs.fetchurl {
-    name = "numeric.csl";
-    url = "https://gist.githubusercontent.com/bwiernik/8c6f39cf51ceb3a03107/raw/1d75c2d62113ffbba6ed03a47ad99bde86934f2b/APA%2520Numeric";
-    sha256 = "1yfhhnhbzvhrv93baz98frmgsx5y442nzhb0l956l4j35fb0cc3h";
-  };
-
-in pkgs.stdenv.mkDerivation {
-  pname = "tvix-doc";
+pkgs.stdenv.mkDerivation {
+  pname = "tvix-docs";
   version = "0.1";
 
-  outputs = [ "out" "svg" ];
+  outputs = [ "out" ];
 
   src = lib.cleanSource ./.;
 
-  CSL = csl;
-
   nativeBuildInputs = [
-    pkgs.pandoc
+    pkgs.mdbook
+    pkgs.mdbook-plantuml
     pkgs.plantuml
-    tl
   ];
 
-  installPhase = ''
-    runHook preInstall
-
-    mkdir -p $out
-    cp -v *.html $out/
-
-    mkdir -p $svg
-    cp -v *.svg $svg/
-
-    runHook postSubmit
+  # plantuml wants to create ./.mdbook-plantuml-cache, which fails as $src is r/o.
+  # copy all sources elsewhere to workaround.
+  buildCommand = ''
+    cp -R $src/. .
+    mdbook build -d $out
   '';
-
 }
diff --git a/tvix/docs/src/SUMMARY.md b/tvix/docs/src/SUMMARY.md
new file mode 100644
index 0000000000..b0e47a0011
--- /dev/null
+++ b/tvix/docs/src/SUMMARY.md
@@ -0,0 +1,10 @@
+# Summary
+
+# Tvix
+- [Architecture & data flow](./architecture.md)
+- [TODOs](./TODO.md)
+
+# Nix
+- [Specification of the Nix Language](./language-spec.md)
+- [Nix language version history](./lang-version.md)
+- [Value Pointer Equality](./value-pointer-equality.md)
diff --git a/tvix/docs/src/TODO.md b/tvix/docs/src/TODO.md
new file mode 100644
index 0000000000..6644bb6bac
--- /dev/null
+++ b/tvix/docs/src/TODO.md
@@ -0,0 +1,129 @@
+# TODO
+
+This contains a rough collection of ideas on the TODO list, trying to keep track
+of it somewhere.
+
+Of course, there's no guarantee these things will get addressed, but it helps
+dumping the backlog somewhere.
+
+Feel free to add new ideas. Before picking something, ask in `#tvix-dev` to make
+sure noone is working on this, or has some specific design in mind already.
+
+## Cleanups
+ - move some of the rstest cases in `tvix-glue` to the `.nix`/`.exp` mechanism.
+   - Parts requiring test fixtures need some special convention.
+     Some of these also cannot be checked into the repo, like the import tests
+     adding special files to test filtering.
+ - add `nix_oracle` mechanism from `tvix-eval` to `tvix-glue`.
+
+## Fixes towards correctness
+ - `builtins.toXML` is missing string context. See b/398.
+ - `builtins.toXML` self-closing tags need to be configurable in a more granular
+   fashion, requires third-party crate support. See b/399.
+ - `rnix` only supports string source files, but `NixString` uses bytes (and Nix
+   source code might be no valid UTF-8).
+
+## Documentation
+Extend the other pages in here. Some ideas on what should be tackled:
+ - Document what Tvix is, and what it is not yet. What it is now, what it is not
+   (yet), explaining some of the architectural choices (castore, more hermetic
+   `Build` repr), while still being compatible. Explain how it's possible to
+   plug in other frontends, and use `tvix-{[ca]store,build}` without Nixlang even.
+   And how `nix-compat` is a useful crate for all sorts of formats and data
+   types of Nix.
+ - Update the Architecture diagram to model the current state of things.
+   There's no gRPC between Coordinator and Evaluator.
+ - Add a dedicated section/page explaining the separation between tvix-glue and
+   tvix-eval, and how more annoying builtins get injected into tvix-eval through
+   tvix-glue.
+   Maybe restructure to only explain the component structure potentially
+   crossing process boundaries (those with gRPC), and make the rest more crate
+   and trait-focused?
+ - Restructure docs on castore vs store, this seems to be duplicated a bit and
+   is probably still not too clear.
+ - Describe store composition(s) in more detail. There's some notes on granular
+   fetching which probably can be repurposed.
+ - Absorb the rest of //tvix/website into this.
+
+## Features
+
+### CLI
+ - `nix repl` can set variables and effectively mutates a global scope. We
+  should update the existing / add another repl that allows the same. We don't
+  want to mutate the evaluator, but should construct a new one, passing in the
+  root scope returned from the previous evaluation.
+
+### Fetchers
+Some more fetcher-related builtins need work:
+ - `fetchGit`
+ - `fetchTree` (hairy, seems there's no proper spec and the URL syntax seems
+   subject to change/underdocumented)
+
+### Convert builtins:fetchurl to Fetches
+We need to convert `builtins:fetchurl`-style calls to `builtins.derivation` to
+fetches, not Derivations (tracked in `KnownPaths`).
+
+### Derivation -> Build
+While we have some support for `structuredAttrs` and `fetchClosure` (at least
+enough to calculate output hashes, aka produce identical ATerm), the code
+populating the `Build` struct doesn't exist it yet.
+
+Similarly, we also don't properly populate the build environment for
+`fetchClosure` yet. (Note there already is `ExportedPathInfo`, so once
+`structuredAttrs` is there this should be easy.
+
+### Builders
+Once builds are proven to work with real-world builds, and the corner cases
+there are ruled out, adding other types of builders might be interesting.
+
+ - bwrap
+ - gVisor
+ - Cloud Hypervisor (using similar technique as `//tvix//boot`).
+
+Long-term, we want to extend traits and gRPC protocol to expose more telemetry,
+logs etc, but this is something requiring a lot of designing.
+
+### Store composition
+ - Combinators: list-by-priority, first-come-first-serve, cache
+ - How do describe hierarchies. URL format too one-dimensional, but we might get
+   quite far with a similar "substituters" concept that Nix uses, to construct
+   the composed stores.
+### Store Config
+   There's already serde for some store options (bigtable uses `serde_qs`).
+   We might also have common options global over all backends, like chunking
+   parameters for chunking blobservices. Think where this would fit in.
+ - Rework the URL syntax for object_store. We should support the default s3/gcs
+   URLs at least.
+
+### BlobService
+ - On the trait side, currently there's no way to distinguish reading a
+   known-chunk vs blob, so we might be calling `.chunks()` unnecessarily often.
+   At least for the `object_store` backend, this might be a problem.
+ - While `object_store` recently got support for `Content-Type`
+   (https://github.com/apache/arrow-rs/pull/5650), there's no support on the
+   local filesystem yet. We'd need to add support to this (through xattrs).
+
+### DirectoryService
+ - Add an `object_store` variant, storing a Directory *closure* keyed by the
+   root `Directory` digest. This won't allow indexing intermediate Directory
+   nodes, but once we have `DirectoryService` composition, it shouldn't be an
+   issue.
+ - [redb](https://www.redb.org/) backend
+
+### PathInfoService
+ - [redb](https://www.redb.org/) backend
+ - sqlite backend (different schema than the Nix one, we need the root nodes data!)
+
+### Nix-compat
+- Async NAR reader (@edef?)
+
+### Nix Daemon protocol
+- Some work ongoing on the worker operation parsing. Partially blocked on the async NAR reader.
+
+### O11Y
+ - gRPC trace propagation (cl/10532)
+ - `tracing-tracy` (cl/10952)
+ - `[tracing-]indicatif` for progress/log reporting (floklis stash)
+ - unification into `tvix-tracing` crate, currently a lot of boilerplate
+   in `tvix-store` CLI entrypoint, and half of the boilerplate copied over to
+   `tvix-cli`.
diff --git a/tvix/docs/src/architecture.md b/tvix/docs/src/architecture.md
new file mode 100644
index 0000000000..5e0aa95f1a
--- /dev/null
+++ b/tvix/docs/src/architecture.md
@@ -0,0 +1,147 @@
+# Tvix - Architecture & data flow
+
+## Background
+
+We intend for Tvix tooling to be more decoupled than the existing,
+monolithic Nix implementation. In practice, we expect to gain several
+benefits from this, such as:
+
+- Ability to use different builders
+- Ability to use different store implementations
+- No monopolisation of the implementation, allowing users to replace
+  components that they are unhappy with (up to and including the
+  language evaluator)
+- Less hidden intra-dependencies between tools due to explicit RPC/IPC
+  boundaries
+
+Communication between different components of the system will use
+gRPC. The rest of this document outlines the components.
+
+## Components
+
+### Coordinator
+
+*Purpose:* The coordinator (in the simplest case, the Tvix CLI tool)
+oversees the flow of a build process and delegates tasks to the right
+subcomponents. For example, if a user runs the equivalent of
+`nix-build` in a folder containing a `default.nix` file, the
+coordinator will invoke the evaluator, pass the resulting derivations
+to the builder and coordinate any necessary store interactions (for
+substitution and other purposes).
+
+While many users are likely to use the CLI tool as their primary
+method of interacting with Tvix, it is not unlikely that alternative
+coordinators (e.g. for a distributed, "Nix-native" CI system) would be
+implemented. To facilitate this, we are considering implementing the
+coordinator on top of a state-machine model that would make it
+possible to reuse the FSM logic without tying it to any particular
+kind of application.
+
+### Evaluator
+
+*Purpose:* Eval takes care of evaluating Nix code. In a typical build
+flow it would be responsible for producing derivations. It can also be
+used as a standalone tool, for example, in use-cases where Nix is used
+to generate configuration without any build or store involvement.
+
+*Requirements:* For now, it will run on the machine invoking the build
+command itself. We give it filesystem access to handle things like
+imports or `builtins.readFile`.
+
+To support IFD, the Evaluator also needs access to store paths. This
+could be implemented by having the coordinator provide an interface to retrieve
+files from a store path, or by ensuring a "realized version of the store" is
+accessible by the evaluator (this could be a FUSE filesystem, or the "real"
+/nix/store on disk.
+
+We might be okay with running the evaluator with filesystem access for now and
+can extend the interface if the need arises.
+
+### Builder
+
+*Purpose:* A builder receives derivations from the coordinator and
+builds them.
+
+By making builder a standardised interface it's possible to make the
+sandboxing mechanism used by the build process pluggable.
+
+Nix is currently using a hard-coded
+[libseccomp](https://github.com/seccomp/libseccomp) based sandboxing
+mechanism and another one based on
+[sandboxd](https://www.unix.com/man-page/mojave/8/sandboxd/) on macOS.
+These are only separated by [compiler preprocessor
+macros](https://gcc.gnu.org/onlinedocs/cpp/Ifdef.html) within the same
+source files despite having very little in common with each other.
+
+This makes experimentation with alternative backends difficult and
+porting Nix to other platforms harder than it has to be. We want to
+write a new Linux builder which uses
+[OCI](https://github.com/opencontainers/runtime-spec), the current
+dominant Linux containerisation technology, by default.
+
+With a well-defined builder abstraction, it's also easy to imagine
+other backends such as a Kubernetes-based one in the future.
+
+The environment in which builds happen is currently very Nix-specific. We might
+want to avoid having to maintain all the intricacies of a Nix-specific
+sandboxing environment in every builder, and instead only provide a more
+generic interface, receiving build requests (and have the coordinator translate
+derivations to that format). [^1]
+
+To build, the builder needs to be able to mount all build inputs into the build
+environment. For this, it needs the store to expose a filesystem interface.
+
+### Store
+
+*Purpose:* Store takes care of storing build results. It provides a
+unified interface to get store paths and upload new ones, as well as querying
+for the existence of a store path and its metadata (references, signatures, …).
+
+Tvix natively uses an improved store protocol. Instead of transferring around
+NAR files, which don't provide an index and don't allow seekable access, a
+concept similar to git tree hashing is used.
+
+This allows more granular substitution, chunk reusage and parallel download of
+individual files, reducing bandwidth usage.
+As these chunks are content-addressed, it opens up the potential for
+peer-to-peer trustless substitution of most of the data, as long as we sign the
+root of the index.
+
+Tvix still keeps the old-style signatures, NAR hashes and NAR size around. In
+the case of NAR hash / NAR size, this data is strictly required in some cases.
+The old-style signatures are valuable for communication with existing
+implementations.
+
+Old-style binary caches (like cache.nixos.org) can still be exposed via the new
+interface, by doing on-the-fly (re)chunking/ingestion.
+
+Most likely, there will be multiple implementations of store, some storing
+things locally, some exposing a "remote view".
+
+A few possible ones that come to mind are:
+
+- Local store
+- SFTP/ GCP / S3 / HTTP
+- NAR/NARInfo protocol: HTTP, S3
+
+A remote Tvix store can be connected by simply connecting to its gRPC
+interface, possibly using SSH tunneling, but there doesn't need to be an
+additional "wire format" like the Nix `ssh(+ng)://` protocol.
+
+Settling on one interface allows composition of stores, meaning it becomes
+possible to express substitution from remote caches as a proxy layer.
+
+It'd also be possible to write a FUSE implementation on top of the RPC
+interface, exposing a lazily-substituting /nix/store mountpoint. Using this in
+remote build context dramatically reduces the amount of data transferred to a
+builder, as only the files really accessed during the build are substituted.
+
+## Figures
+
+```plantuml,format=svg
+{{#include figures/component-flow.puml}}
+```
+
+[^1]: There have already been some discussions in the Nix community, to switch
+  to REAPI:
+  https://discourse.nixos.org/t/a-proposal-for-replacing-the-nix-worker-protocol/20926/22
diff --git a/tvix/docs/src/figures/component-flow.puml b/tvix/docs/src/figures/component-flow.puml
new file mode 100644
index 0000000000..5b6d79b823
--- /dev/null
+++ b/tvix/docs/src/figures/component-flow.puml
@@ -0,0 +1,60 @@
+@startuml
+
+title Tvix build flow
+
+actor User
+participant CLI
+participant "Coordinator" as Coord
+participant "Evaluator" as Eval
+database Store
+participant "Builder" as Build
+
+note over CLI,Eval
+    Typically runs locally on the invoking machine
+end note
+/ note over Store, Build
+    Can be either local or remote
+end note
+
+User-->CLI: User initiates build of `hello` (analogous to `nix-build -f '<nixpkgs>' -A hello`)
+
+CLI-->Coord: CLI invokes coordinator
+
+Coord-->Eval: Sends message to start evaluation of `<nixpkgs>` (path lookup) with attribute `hello`
+note right: The paths to the evaluator are local file system paths
+
+Coord<--Eval: Yields derivations to be built
+note right
+    Immediately starts streaming derivations as they are instantiated across
+    the dependency graph so they can be built while the evaluation is still running.
+
+    There are two types of build requests: One for regular "fire and forget" builds,
+    and another for IFD (import from derivation).
+
+    These are distinct because IFD needs to be fed back into the evaluator for
+    further processing while a regular build does not.
+end note
+
+loop while has more derivations
+
+    Coord-->Store: Check if desired paths are in store
+    alt Store has path
+        Coord<--Store: Success response
+    else Store does not have path
+        Coord-->Build: Request derivation to be built
+
+        alt Build failure
+            Coord<--Build: Fail response
+            note left: It's up to the coordinator whether to exit on build failure
+        else Build success
+            Build-->Store: Push outputs to store
+            Build<--Coord: Send success & pushed response
+        end
+
+    end
+end
+
+CLI<--Coord: Respond success/fail
+User<--CLI: Exit success/fail
+
+@enduml
diff --git a/tvix/docs/src/lang-version.md b/tvix/docs/src/lang-version.md
new file mode 100644
index 0000000000..c288274c91
--- /dev/null
+++ b/tvix/docs/src/lang-version.md
@@ -0,0 +1,62 @@
+# Nix language version history
+
+The Nix language (β€œNix”) has its own versioning mechanism independent from its
+most popular implementation (β€œC++ Nix”): `builtins.langVersion`. It has been
+increased whenever the language has changed syntactically or semantically in a
+way that would not be introspectable otherwise. In particular, this does not
+include addition (or removal) of `builtins`, as this can be introspected using
+standard attribute set operations.
+
+Changes to `builtins.langVersion` are best found by viewing the git history of
+C++ Nix using `git log -G 'mkInt\\(v, [0-9]\\)'` for `builtins.langVersion` < 7.
+After that point `git log -G 'v\\.mkInt\\([0-9]+\\)'` should work. To reduce the
+amount of false positives, specify the version number you are interested in
+explicitly.
+
+## 1
+
+The first version of the Nix language is its state at the point when
+`builtins.langVersion` was added in [8b8ee53] which was first released
+as part of C++ Nix 1.2.
+
+## 2
+
+Nix version 2 changed the behavior of `builtins.storePath`: It would now [try to
+substitute the given path if missing][storePath-substitute], instead of creating
+an evaluation failure. `builtins.langVersion` was increased in [e36229d].
+
+## 3
+
+Nix version 3 changed the behavior of the `==` behavior. Strings would now be
+considered [equal even if they had differing string context][equal-no-ctx].
+
+## 4
+
+Nix version 4 [added the float type][float] to the language.
+
+## 5
+
+The [increase of `builtins.langVersion` to 5][langVersion-5] did not signify a
+language change, but added support for structured attributes to the Nix daemon.
+Eelco Dolstra writes as to what changed:
+
+> The structured attributes support. Unfortunately that's not so much a language
+> change as a build.cc (i.e. daemon) change, but we don't really have a way to
+> express that...
+
+Maybe `builtins.nixVersion` (which was added in version 1) should have been
+used instead. In any case, the [only `langVersion` check][nixpkgs-langVersion-5]
+in nixpkgs verifies a lower bound of 5.
+
+## 6
+
+Nix version 6 added support for [comparing two lists][list-comparison].
+
+[8b8ee53]: https://github.com/nixos/nix/commit/8b8ee53bc73769bb25d967ba259dabc9b23e2e6f
+[storePath-substitute]: https://github.com/nixos/nix/commit/22d665019a3770148929b7504c73bcdbe025ec12
+[e36229d]: https://github.com/nixos/nix/commit/e36229d27f9ab508e0abf1892f3e8c263d2f8c58
+[equal-no-ctx]: https://github.com/nixos/nix/commit/ee7fe64c0ac00f2be11604a2a6509eb86dc19f0a
+[float]: https://github.com/nixos/nix/commit/14ebde52893263930cdcde1406cc91cc5c42556f
+[langVersion-5]: https://github.com/nixos/nix/commit/8191992c83bf4387b03c5fdaba818dc2b520462d
+[list-comparison]: https://github.com/nixos/nix/commit/09471d2680292af48b2788108de56a8da755d661
+[nixpkgs-langVersion-5]: https://github.com/NixOS/nixpkgs/blob/d7ac3423d321b8b145ccdd1aed9dfdb280f5e391/pkgs/build-support/closure-info.nix#L11
diff --git a/tvix/docs/language-spec.md b/tvix/docs/src/language-spec.md
index a714374933..0ff1dc491e 100644
--- a/tvix/docs/language-spec.md
+++ b/tvix/docs/src/language-spec.md
@@ -1,15 +1,4 @@
----
-title: "Specification of the Nix language"
-numbersections: true
-author:
-- tazjin
-email:
-- tazjin@tvl.su
-lang: en-GB
----
-
-The Nix Language
-================
+# Specification of the Nix Language
 
 WARNING: This document is a work in progress. Please keep an eye on
 [`topic:nix-spec`](https://cl.tvl.fyi/q/topic:nix-spec) for ongoing
diff --git a/tvix/docs/src/value-pointer-equality.md b/tvix/docs/src/value-pointer-equality.md
new file mode 100644
index 0000000000..d84efcb50c
--- /dev/null
+++ b/tvix/docs/src/value-pointer-equality.md
@@ -0,0 +1,338 @@
+# Value Pointer Equality in Nix
+
+## Introduction
+
+It is a piece of semi-obscure Nix trivia that while functions are generally not
+comparable, they can be compared in certain situations. This is actually quite an
+important fact, as it is essential for the evaluation of nixpkgs: The attribute sets
+used to represent platforms in nixpkgs, like `stdenv.buildPlatform`, contain functions,
+such as `stdenv.buildPlatform.canExecute`. When writing cross logic, one invariably
+ends up writing expressions that compare these sets, e.g. `stdenv.buildPlatform !=
+stdenv.hostPlatform`. Since attribute set equality is the equality of their attribute
+names and values, we also end up comparing the functions within them.  We can summarize
+the relevant part of this behavior for platform comparisons in the following (true)
+Nix expressions:
+
+* `stdenv.hostPlatform.canExecute != stdenv.hostPlatform.canExecute`
+* `stdenv.hostPlatform == stdenv.hostPlatform`
+
+This fact is commonly referred to as pointer equality of functions (or function pointer
+equality) which is not an entirely accurate name, as we'll see. This account of the
+behavior states that, while functions are incomparable in general, they are comparable
+insofar, as they occupy the same spot in an attribute set.
+
+However, [a maybe lesser known trick][puck-issue] is to write a function such as the
+following to allow comparing functions:
+
+```nix
+let
+  pointerEqual = lhs: rhs: { x = lhs; } == { x = rhs; };
+
+  f = name: "Hello, my name is ${name}";
+  g = name: "Hello, my name is ${name}";
+in
+[
+  (pointerEqual f f) # => true
+  (pointerEqual f g) # => false
+]
+```
+
+Here, clearly, the function is not contained at the same position in one and the same
+attribute set, but at the same position in two entirely different attribute sets. We can
+also see that we are not comparing the functions themselves (e.g. their AST), but
+rather if they are the same individual value (i.e. pointer equal).
+
+To figure out the _actual_ semantics, we'll first have a look at how value (pointer) equality
+works in C++ Nix, the only production ready Nix implementation currently available.
+
+## Nix (Pointer) Equality in C++ Nix
+
+TIP: The summary presented here is up-to-date as of 2023-06-27 and was tested
+with Nix 2.3, 2.11 and 2.15.
+
+### `EvalState::eqValues` and `ExprOpEq::eval`
+
+The function implementing equality in C++ Nix is `EvalState::eqValues` which starts with
+[the following bit of code][eqValues-pointer-eq]:
+
+```cpp
+bool EvalState::eqValues(Value & v1, Value & v2)
+{
+    forceValue(v1);
+    forceValue(v2);
+
+    /* !!! Hack to support some old broken code that relies on pointer
+       equality tests between sets.  (Specifically, builderDefs calls
+       uniqList on a list of sets.)  Will remove this eventually. */
+    if (&v1 == &v2) return true;
+```
+
+So this immediately looks more like pointer equality of arbitrary *values* instead of functions. In fact
+there is [no special code facilitating function equality][eqValues-function-eq]:
+
+```cpp
+        /* Functions are incomparable. */
+        case nFunction:
+            return false;
+```
+
+So one takeaway of this is that pointer equality is neither dependent on functions nor attribute sets.
+In fact, we can also write our `pointerEqual` function as:
+
+```nix
+lhs: rhs: [ lhs ] == [ rhs ]
+```
+
+It's interesting that `EvalState::eqValues` forces the left and right-hand value before trying pointer
+equality. It explains that `let x = throw ""; in x == x` does not evaluate successfully, but it is puzzling why
+`let f = x: x; in f == f` does not return `true`. In fact, why do we need to wrap the values in a list or
+attribute set at all for our `pointerEqual` function to work?
+
+The answer lies in [the code that evaluates `ExprOpEq`][ExprOpEq],
+i.e. an expression involving the `==` operator:
+
+```cpp
+void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
+{
+    Value v1; e1->eval(state, env, v1);
+    Value v2; e2->eval(state, env, v2);
+    v.mkBool(state.eqValues(v1, v2));
+}
+```
+
+As you can see, two _distinct_ `Value` structs are created, so they can never be pointer equal even
+if the `union` inside points to the same bit of memory. We can thus understand what actually happens
+when we check the equality of an attribute set (or list), by looking at the following expression:
+
+```nix
+let
+  x = { name = throw "nameless"; };
+in
+
+x == x # => causes an evaluation error
+```
+
+Because `x` can't be pointer equal, as it'll end up in the distinct structs `v1` and `v2`, it needs to be compared
+by value. For this reason, the `name` attribute will be forced and an evaluation error caused.
+If we rewrite the expression to use…
+
+```nix
+{ inherit x; } == { inherit x; } # => true
+```
+
+…, it'll work: The two attribute sets are compared by value, but their `x` attribute turns out to be pointer
+equal _after_ forcing it. This does not throw, since forcing an attribute set does not force its attributes'
+values (as forcing a list doesn't force its elements).
+
+As we have seen, pointer equality can not only be used to compare function values, but also other
+otherwise incomparable values, such as lists and attribute sets that would cause an evaluation
+error if they were forced recursively. We can even switch out the `throw` for an `abort`. The limitation is
+of course that we need to use a value that behaves differently depending on whether it is forced
+β€œnormally” (think `builtins.seq`) or recursively (think `builtins.deepSeq`), so thunks will generally be
+evaluated before pointer equality can kick into effect.
+
+### Other Comparisons
+
+The `!=` operator uses `EvalState::eqValues` internally as well, so it behaves exactly as `!(a == b)`.
+
+The `>`, `<`, `>=` and `<=` operators all desugar to [CompareValues][]
+eventually which generally looks at the value type before comparing. It does,
+however, rely on `EvalState::eqValues` for list comparisons
+([introduced in Nix 2.5][nix-2.5-changelog]), so it is possible to compare lists
+with e.g. functions in them, as long as they are equal by pointer:
+
+```nix
+let
+  f = x: x + 42;
+in
+
+[
+  ([ f 2 ] > [ f 1 ]) # => true
+  ([ f 2 ] > [ (x: x) 1]) # => error: cannot compare a function with a function
+  ([ f ] > [ f ]) # => false
+]
+```
+
+Finally, since `builtins.elem` relies on `EvalState::eqValues`, you can check for
+a function by pointer equality:
+
+```nix
+let
+  f = x: f x;
+in
+builtins.elem f [ f 2 3 ] # => true
+```
+
+### Pointer Equality Preserving Nix Operations
+
+We have seen that pointer equality is established by comparing the memory
+location of two C++ `Value` structs. But how does this _representation_ relate
+to Nix values _themselves_ (in the sense of a platonic ideal if you will)? In
+Nix, values have no identity (ignoring `unsafeGetAttrPos`) or memory location.
+
+Since Nix is purely functional, values can't be mutated, so they need to be
+copied frequently. With Nix being garbage collected, there is no strong
+expectation when a copy is made, we probably just hope it is done as seldomly as
+possible to save on memory. With pointer equality leaking the memory location of
+the `Value` structs to an extent, it is now suddenly our business to know
+exactly _when_ a copy of a value is made.
+
+Evaluation in C++ Nix mainly proceeds along the following [two
+functions][eval-maybeThunk].
+
+```cpp
+struct Expr
+{
+    /* … */
+    virtual void eval(EvalState & state, Env & env, Value & v);
+    virtual Value * maybeThunk(EvalState & state, Env & env);
+    /* … */
+};
+```
+
+As you can see, `Expr::eval` always takes a reference to a struct _allocated by
+the caller_ to place the evaluation result in. Anything that is processed using
+`Expr::eval` will be a copy of the `Value` struct even if the value before and
+after are the same.
+
+`Expr::maybeThunk`, on the other hand, returns a pointer to a `Value` which may
+already exist or be newly allocated. So, if evaluation passes through `maybeThunk`,
+Nix values _can_ retain their pointer equality. Since Nix is lazy, a lot of
+evaluation needs to be thunked and pass through `maybeThunk`β€”knowing under what
+circumstances `maybeThunk` will return a pointer to an already existing `Value`
+struct thus means knowing the circumstances under which pointer equality of a
+Nix value will be preserved in C++ Nix.
+
+The [default case][maybeThunk-default] of `Expr::maybeThunk` allocates a new
+`Value` which holds the delayed computation of the `Expr` as a thunk:
+
+```cpp
+
+Value * Expr::maybeThunk(EvalState & state, Env & env)
+{
+    Value * v = state.allocValue();
+    mkThunk(*v, env, this);
+    return v;
+}
+```
+
+Consequently, only special cased expressions could preserve pointer equality.
+These are `ExprInt`, `ExprFloat`, `ExprString`, `ExprPath`β€”all of which relate
+to creating new valuesβ€”and [finally, `ExprVar`][maybeThunk-ExprVar]:
+
+```cpp
+Value * ExprVar::maybeThunk(EvalState & state, Env & env)
+{
+    Value * v = state.lookupVar(&env, *this, true);
+    /* The value might not be initialised in the environment yet.
+       In that case, ignore it. */
+    if (v) { state.nrAvoided++; return v; }
+    return Expr::maybeThunk(state, env);
+}
+```
+
+Here we may actually return an already existing `Value` struct. Consequently,
+accessing a value from the scope is the only thing you can do with a value in
+C++ Nix that preserves its pointer equality, as the following example shows:
+For example, using the select operator to get a value from an attribute set
+or even passing a value trough the identity function invalidates its pointer
+equality to itself (or rather, its former self).
+
+```nix
+let
+  pointerEqual = a: b: [ a ] == [ b ];
+  id = x: x;
+
+  f = _: null;
+  x = { inherit f; };
+  y = { inherit f; };
+in
+
+[
+  (pointerEqual f f)      # => true
+
+  (pointerEqual f (id f)) # => false
+
+  (pointerEqual x.f y.f)  # => false
+  (pointerEqual x.f x.f)  # => false
+
+  (pointerEqual x x)      # => true
+  (pointerEqual x y)      # => true
+]
+```
+
+In the last two cases, the example also shows that there is another way to
+preserve pointer equality: Storing a value in an attribute set (or list)
+preserves its pointer equality even if the structure holding it is modified in
+some way (as long as the value we care about is left untouched). The catch is,
+of course, that there is no way to get the value out of the structure while
+preserving pointer equality (which requires using the select operator or a call
+to `builtins.elemAt`).
+
+We initially illustrated the issue of pointer equality using the following
+true expressions:
+
+* `stdenv.hostPlatform.canExecute != stdenv.hostPlatform.canExecute`
+* `stdenv.hostPlatform == stdenv.hostPlatform`
+
+We can now add a third one, illustrating that pointer equality is invalidated
+by select operations:
+
+* `[ stdenv.hostPlatform.canExecute ] != [ stdenv.hostPlatform.canExecute ]`
+
+To summarize, pointer equality is established on the memory location of the
+`Value` struct in C++ Nix. Except for simple values (`int`, `bool`, …),
+the `Value` struct only consists of a pointer to the actual representation
+of the value (attribute set, list, function, …) and is thus cheap to copy.
+In practice, this happens when a value passes through the evaluation of
+almost any Nix expression. Only in the select cases described above
+a value preserves its pointer equality despite being unchanged by an
+expression. We can call this behavior *exterior pointer equality*.
+
+## Summary
+
+When comparing two Nix values, we must force both of them (non-recursively!), but are
+allowed to short-circuit the comparison based on pointer equality, i.e. if they are at
+the same exact value in memory, they are deemed equal immediately. This is completely
+independent of what type of value they are. If they are not pointer equal, they are
+(recursively) compared by value as expected.
+
+However, when evaluating the Nix expression `a == b`, we *must* invoke our implementation's
+value equality function in a way that `a` and `b` themselves can never be deemed pointer equal.
+Any values we encounter while recursing during the equality check must be compared by
+pointer as described above, though.
+
+## Stability of the Feature
+
+Keen readers will have noticed the following comment in the C++ Nix source code,
+indicating that pointer comparison may be removed in the future.
+
+```cpp
+    /* !!! Hack to support some old broken code that relies on pointer
+       equality tests between sets.  (Specifically, builderDefs calls
+       uniqList on a list of sets.)  Will remove this eventually. */
+```
+
+Now, I can't speak for the upstream C++ Nix developers, but sure can speculate.
+As already pointed out, this feature is currently needed for evaluating nixpkgs.
+While its use could realistically be eliminated (only bothersome spot is probably
+the `emulator` function, but that should also be doable), removing the feature
+would seriously compromise C++ Nix's ability to evaluate historical nixpkgs
+revision which is arguably a strength of the system.
+
+Another indication that it is likely here to stay is that it has already
+[outlived builderDefs][], even though
+it was (apparently) reintroduced just for this use case. More research into
+the history of this feature would still be prudent, especially the reason for
+its original introduction (maybe performance?).
+
+[puck-issue]: https://github.com/NixOS/nix/issues/3371
+[eqValues-pointer-eq]: https://github.com/NixOS/nix/blob/3c618c43c6044eda184df235c193877529e951cb/src/libexpr/eval.cc#L2401-L2404
+[eqValues-function-eq]: https://github.com/NixOS/nix/blob/3c618c43c6044eda184df235c193877529e951cb/src/libexpr/eval.cc#L2458-L2460
+[ExprOpEq]: https://github.com/NixOS/nix/blob/3c618c43c6044eda184df235c193877529e951cb/src/libexpr/eval.cc#L1822-L1827
+[outlived builderDefs]: https://github.com/NixOS/nixpkgs/issues/4210
+[CompareValues]: https://github.com/NixOS/nix/blob/3c618c43c6044eda184df235c193877529e951cb/src/libexpr/primops.cc#L569-L610
+[nix-2.5-changelog]: https://nixos.org/manual/nix/stable/release-notes/rl-2.5.html
+[eval-maybeThunk]: https://github.com/NixOS/nix/blob/3c618c43c6044eda184df235c193877529e951cb/src/libexpr/nixexpr.hh#L161-L162
+[maybeThunk-default]: https://github.com/NixOS/nix/blob/8e770dac9f68162cfbb368e53f928df491babff3/src/libexpr/eval.cc#L1076-L1081
+[maybeThunk-ExprVar]: https://github.com/NixOS/nix/blob/8e770dac9f68162cfbb368e53f928df491babff3/src/libexpr/eval.cc#L1084-L1091
diff --git a/tvix/docs/theme/highlight.js b/tvix/docs/theme/highlight.js
new file mode 100644
index 0000000000..86fb5af97a
--- /dev/null
+++ b/tvix/docs/theme/highlight.js
@@ -0,0 +1,590 @@
+/*!
+  Highlight.js v11.9.0 (git: b7ec4bfafc)
+  (c) 2006-2023 undefined and other contributors
+  License: BSD-3-Clause
+ */
+var hljs=function(){"use strict";function e(t){
+return t instanceof Map?t.clear=t.delete=t.set=()=>{
+throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
+throw Error("set is read-only")
+}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{
+const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i)
+})),t}class t{constructor(e){
+void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}
+ignoreMatch(){this.isMatchIgnored=!0}}function n(e){
+return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;")
+}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
+;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope
+;class o{constructor(e,t){
+this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
+this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{
+if(e.startsWith("language:"))return e.replace("language:","language-")
+;if(e.includes(".")){const n=e.split(".")
+;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")
+}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}
+closeNode(e){s(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
+this.buffer+=`<span class="${e}">`}}const r=(e={})=>{const t={children:[]}
+;return Object.assign(t,e),t};class a{constructor(){
+this.rootNode=r(),this.stack=[this.rootNode]}get top(){
+return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
+this.top.children.push(e)}openNode(e){const t=r({scope:e})
+;this.add(t),this.stack.push(t)}closeNode(){
+if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
+for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
+walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
+return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
+t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
+"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
+a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e}
+addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){
+this.closeNode()}__addSublanguage(e,t){const n=e.root
+;t&&(n.scope="language:"+t),this.add(n)}toHTML(){
+return new o(this,this.options).value()}finalize(){
+return this.closeAllNodes(),!0}}function l(e){
+return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")}
+function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")}
+function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{
+const t=e[e.length-1]
+;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}
+})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"}
+function p(e){return RegExp(e.toString()+"|").exec("").length-1}
+const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./
+;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n
+;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break}
+s+=i.substring(0,e.index),
+i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],
+"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)}
+const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",_="\\b(0b[01]+)",O={
+begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'",
+illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n",
+contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t,
+contains:[]},n);s.contains.push({scope:"doctag",
+begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",
+end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0})
+;const o=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/)
+;return s.contains.push({begin:h(/[ ]+/,"(",o,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s
+},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({
+__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{
+scope:"number",begin:_,relevance:0},BINARY_NUMBER_RE:_,COMMENT:N,
+C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number",
+begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{
+"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
+t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E,
+MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0},
+NUMBER_MODE:{scope:"number",begin:w,relevance:0},NUMBER_RE:w,
+PHRASAL_WORDS_MODE:{
+begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
+},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/,
+end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]},
+RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
+SHEBANG:(e={})=>{const t=/^#![ ]*\//
+;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t,
+end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},
+TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x,
+UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){
+"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){
+void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){
+t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
+e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
+void 0===e.relevance&&(e.relevance=0))}function L(e,t){
+Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){
+if(e.match){
+if(e.begin||e.end)throw Error("begin & end are not supported with match")
+;e.begin=e.match,delete e.match}}function P(e,t){
+void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return
+;if(e.starts)throw Error("beforeMatch cannot be used with starts")
+;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t]
+})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={
+relevance:0,contains:[Object.assign(n,{endsParent:!0})]
+},e.relevance=0,delete n.beforeMatch
+},H=["of","and","for","in","not","or","if","then","parent","list","value"],C="keyword"
+;function $(e,t,n=C){const i=Object.create(null)
+;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{
+Object.assign(i,$(e[n],t,n))})),i;function s(e,n){
+t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
+;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){
+return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const z={},W=e=>{
+console.error(e)},X=(e,...t)=>{console.log("WARN: "+e,...t)},G=(e,t)=>{
+z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0)
+},K=Error();function F(e,t,{key:n}){let i=0;const s=e[n],o={},r={}
+;for(let e=1;e<=t.length;e++)r[e+i]=s[e],o[e+i]=!0,i+=p(t[e-1])
+;e[n]=r,e[n]._emit=o,e[n]._multi=!0}function Z(e){(e=>{
+e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,
+delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={
+_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope
+}),(e=>{if(Array.isArray(e.begin)){
+if(e.skip||e.excludeBegin||e.returnBegin)throw W("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),
+K
+;if("object"!=typeof e.beginScope||null===e.beginScope)throw W("beginScope must be object"),
+K;F(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{
+if(Array.isArray(e.end)){
+if(e.skip||e.excludeEnd||e.returnEnd)throw W("skip, excludeEnd, returnEnd not compatible with endScope: {}"),
+K
+;if("object"!=typeof e.endScope||null===e.endScope)throw W("endScope must be object"),
+K;F(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function V(e){
+function t(t,n){
+return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":""))
+}class n{constructor(){
+this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
+addRule(e,t){
+t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
+this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null)
+;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|"
+}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex
+;const t=this.matcherRe.exec(e);if(!t)return null
+;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n]
+;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){
+this.rules=[],this.multiRegexes=[],
+this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
+if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
+;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
+t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
+return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
+this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
+const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
+;let n=t.exec(e)
+;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
+const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
+return n&&(this.regexIndex+=n.position+1,
+this.regexIndex===this.count&&this.considerAll()),n}}
+if(e.compilerExtensions||(e.compilerExtensions=[]),
+e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language.  See documentation.")
+;return e.classNameAliases=i(e.classNameAliases||{}),function n(o,r){const a=o
+;if(o.isCompiled)return a
+;[I,B,Z,D].forEach((e=>e(o,r))),e.compilerExtensions.forEach((e=>e(o,r))),
+o.__beforeBegin=null,[T,L,P].forEach((e=>e(o,r))),o.isCompiled=!0;let c=null
+;return"object"==typeof o.keywords&&o.keywords.$pattern&&(o.keywords=Object.assign({},o.keywords),
+c=o.keywords.$pattern,
+delete o.keywords.$pattern),c=c||/\w+/,o.keywords&&(o.keywords=$(o.keywords,e.case_insensitive)),
+a.keywordPatternRe=t(c,!0),
+r&&(o.begin||(o.begin=/\B|\b/),a.beginRe=t(a.begin),o.end||o.endsWithParent||(o.end=/\B|\b/),
+o.end&&(a.endRe=t(a.end)),
+a.terminatorEnd=l(a.end)||"",o.endsWithParent&&r.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+r.terminatorEnd)),
+o.illegal&&(a.illegalRe=t(o.illegal)),
+o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{
+variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?i(e,{
+starts:e.starts?i(e.starts):null
+}):Object.isFrozen(e)?i(e):e))("self"===e?o:e)))),o.contains.forEach((e=>{n(e,a)
+})),o.starts&&n(o.starts,r),a.matcher=(e=>{const t=new s
+;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
+}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
+}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){
+return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{
+constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}}
+const Y=n,Q=i,ee=Symbol("nomatch"),te=n=>{
+const i=Object.create(null),s=Object.create(null),o=[];let r=!0
+;const a="Could not find the language '{}', did you forget to load/include a language module?",l={
+disableAutodetect:!0,name:"Plain text",contains:[]};let p={
+ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,
+languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
+cssSelector:"pre code",languages:null,__emitter:c};function b(e){
+return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s=""
+;"object"==typeof t?(i=e,
+n=t.ignoreIllegals,s=t.language):(G("10.7.0","highlight(lang, code, ...args) has been deprecated."),
+G("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),
+s=e,i=t),void 0===n&&(n=!0);const o={code:i,language:s};N("before:highlight",o)
+;const r=o.result?o.result:E(o.language,o.code,n)
+;return r.code=o.code,N("after:highlight",r),r}function E(e,n,s,o){
+const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R)
+;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n=""
+;for(;t;){n+=R.substring(e,t.index)
+;const s=_.case_insensitive?t[0].toLowerCase():t[0],o=(i=s,N.keywords[i]);if(o){
+const[e,i]=o
+;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{
+const n=_.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0]
+;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i
+;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{
+if(""===R)return;let e=null;if("string"==typeof N.subLanguage){
+if(!i[N.subLanguage])return void M.addText(R)
+;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top
+}else e=x(R,N.subLanguage.length?N.subLanguage:null)
+;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language)
+})():l(),R=""}function u(e,t){
+""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1
+;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue}
+const i=_.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}}
+function h(e,t){
+return e.scope&&"string"==typeof e.scope&&M.openNode(_.classNameAliases[e.scope]||e.scope),
+e.beginScope&&(e.beginScope._wrap?(u(R,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),
+R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{
+value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t)
+;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e)
+;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){
+for(;e.endsParent&&e.parent;)e=e.parent;return e}}
+if(e.endsWithParent)return f(e.parent,n,i)}function b(e){
+return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){
+const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return ee;const o=N
+;N.endScope&&N.endScope._wrap?(g(),
+u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(),
+d(N.endScope,e)):o.skip?R+=t:(o.returnEnd||o.excludeEnd||(R+=t),
+g(),o.excludeEnd&&(R=t));do{
+N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent
+}while(N!==s.parent);return s.starts&&h(s.starts,e),o.returnEnd?0:t.length}
+let w={};function y(i,o){const a=o&&o[0];if(R+=i,null==a)return g(),0
+;if("begin"===w.type&&"end"===o.type&&w.index===o.index&&""===a){
+if(R+=n.slice(o.index,o.index+1),!r){const t=Error(`0 width match regex (${e})`)
+;throw t.languageName=e,t.badRule=w.rule,t}return 1}
+if(w=o,"begin"===o.type)return(e=>{
+const n=e[0],i=e.rule,s=new t(i),o=[i.__beforeBegin,i["on:begin"]]
+;for(const t of o)if(t&&(t(e,s),s.isMatchIgnored))return b(n)
+;return i.skip?R+=n:(i.excludeBegin&&(R+=n),
+g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(o)
+;if("illegal"===o.type&&!s){
+const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"<unnamed>")+'"')
+;throw e.mode=N,e}if("end"===o.type){const e=m(o);if(e!==ee)return e}
+if("illegal"===o.type&&""===a)return 1
+;if(I>1e5&&I>3*o.index)throw Error("potential infinite loop, way more iterations than matches")
+;return R+=a,a.length}const _=O(e)
+;if(!_)throw W(a.replace("{}",e)),Error('Unknown language: "'+e+'"')
+;const v=V(_);let k="",N=o||v;const S={},M=new p.__emitter(p);(()=>{const e=[]
+;for(let t=N;t!==_;t=t.parent)t.scope&&e.unshift(t.scope)
+;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{
+if(_.__emitTokens)_.__emitTokens(n,M);else{for(N.matcher.considerAll();;){
+I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A
+;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(A,e.index),e)
+;A=e.index+t}y(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e,
+value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){
+if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n),
+illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A,
+context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(r)return{
+language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N}
+;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{
+const t={value:Y(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)}
+;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1)))
+;s.unshift(n);const o=s.sort(((e,t)=>{
+if(e.relevance!==t.relevance)return t.relevance-e.relevance
+;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1
+;if(O(t.language).supersetOf===e.language)return-1}return 0})),[r,a]=o,c=r
+;return c.secondBest=a,c}function w(e){let t=null;const n=(e=>{
+let t=e.className+" ";t+=e.parentNode?e.parentNode.className:""
+;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1])
+;return t||(X(a.replace("{}",n[1])),
+X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}
+return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return
+;if(N("before:highlightElement",{el:e,language:n
+}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e)
+;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),
+console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),
+console.warn("The element with unescaped HTML:"),
+console.warn(e)),p.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML)
+;t=e;const i=t.textContent,o=n?m(i,{language:n,ignoreIllegals:!0}):x(i)
+;e.innerHTML=o.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n
+;e.classList.add("hljs"),e.classList.add("language-"+i)
+})(e,n,o.language),e.result={language:o.language,re:o.relevance,
+relevance:o.relevance},o.secondBest&&(e.secondBest={
+language:o.secondBest.language,relevance:o.secondBest.relevance
+}),N("after:highlightElement",{el:e,result:o,text:i})}let y=!1;function _(){
+"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(w):y=!0
+}function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}
+function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
+s[e.toLowerCase()]=t}))}function k(e){const t=O(e)
+;return t&&!t.disableAutodetect}function N(e,t){const n=e;o.forEach((e=>{
+e[n]&&e[n](t)}))}
+"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{
+y&&_()}),!1),Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:_,
+highlightElement:w,
+highlightBlock:e=>(G("10.7.0","highlightBlock will be removed entirely in v12.0"),
+G("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{p=Q(p,e)},
+initHighlighting:()=>{
+_(),G("10.6.0","initHighlighting() deprecated.  Use highlightAll() now.")},
+initHighlightingOnLoad:()=>{
+_(),G("10.6.0","initHighlightingOnLoad() deprecated.  Use highlightAll() now.")
+},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){
+if(W("Language definition for '{}' could not be registered.".replace("{}",e)),
+!r)throw t;W(t),s=l}
+s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{
+languageName:e})},unregisterLanguage:e=>{delete i[e]
+;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
+listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v,
+autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{
+e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
+e["before:highlightBlock"](Object.assign({block:t.el},t))
+}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
+e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),o.push(e)},
+removePlugin:e=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}),n.debugMode=()=>{
+r=!1},n.safeMode=()=>{r=!0},n.versionString="11.9.0",n.regex={concat:h,
+lookahead:g,either:f,optional:d,anyNumberOfTimes:u}
+;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n
+},ne=te({});return ne.newInstance=()=>te({}),ne}()
+;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `bash` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const s=e.regex,t={},n={begin:/\$\{/,
+end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]};Object.assign(t,{
+className:"variable",variants:[{
+begin:s.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},n]});const a={
+className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]
+},i=e.inherit(e.COMMENT(),{match:[/(^|\s)/,/#.*$/],scope:{2:"comment"}}),c={
+begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,
+end:/(\w+)/,className:"string"})]}},o={className:"string",begin:/"/,end:/"/,
+contains:[e.BACKSLASH_ESCAPE,t,a]};a.contains.push(o);const r={begin:/\$?\(\(/,
+end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t]
+},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10
+}),m={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,
+contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{
+name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/,
+keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","function","select"],
+literal:["true","false"],
+built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"]
+},contains:[l,e.SHEBANG(),m,r,i,c,{match:/(\/[a-z._-]+)+/},o,{match:/\\"/},{
+className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}})()
+;hljs.registerLanguage("bash",e)})();/*! `c` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const n=e.regex,t=e.COMMENT("//","$",{
+contains:[{begin:/\\\n/}]
+}),s="decltype\\(auto\\)",a="[a-zA-Z_]\\w*::",r="("+s+"|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",i={
+className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{
+match:/\batomic_[a-z]{3,6}\b/}]},l={className:"string",variants:[{
+begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{
+begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",
+end:"'",illegal:"."},e.END_SAME_AS_BEGIN({
+begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={
+className:"number",variants:[{begin:"\\b(0b[01']+)"},{
+begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"
+},{
+begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"
+}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{
+keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"
+},contains:[{begin:/\\\n/,relevance:0},e.inherit(l,{className:"string"}),{
+className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={
+className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0
+},g=n.optional(a)+e.IDENT_RE+"\\s*\\(",u={
+keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"],
+type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"],
+literal:"true false NULL",
+built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr"
+},m=[c,i,t,e.C_BLOCK_COMMENT_MODE,o,l],_={variants:[{begin:/=/,end:/;/},{
+begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],
+keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u,
+contains:m.concat(["self"]),relevance:0}]),relevance:0},p={
+begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,
+keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:s,keywords:u,relevance:0},{
+begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})],
+relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,
+keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,l,o,i,{begin:/\(/,
+end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,l,o,i]
+}]},i,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u,
+disableAutodetect:!0,illegal:"</",contains:[].concat(_,p,m,[c,{
+begin:e.IDENT_RE+"::",keywords:u},{className:"class",
+beginKeywords:"enum class struct union",end:/[{;:<>=]/,contains:[{
+beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c,
+strings:l,keywords:u}}}})();hljs.registerLanguage("c",e)})();/*! `cpp` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const t=e.regex,a=e.COMMENT("//","$",{
+contains:[{begin:/\\\n/}]
+}),n="decltype\\(auto\\)",r="[a-zA-Z_]\\w*::",i="(?!struct)("+n+"|"+t.optional(r)+"[a-zA-Z_]\\w*"+t.optional("<[^<>]+>")+")",s={
+className:"type",begin:"\\b[a-z\\d_]*_t\\b"},c={className:"string",variants:[{
+begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{
+begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",
+end:"'",illegal:"."},e.END_SAME_AS_BEGIN({
+begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={
+className:"number",variants:[{
+begin:"[+-]?(?:(?:[0-9](?:'?[0-9])*\\.(?:[0-9](?:'?[0-9])*)?|\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)"
+},{
+begin:"[+-]?\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)"
+}],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{
+keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"
+},contains:[{begin:/\\\n/,relevance:0},e.inherit(c,{className:"string"}),{
+className:"string",begin:/<.*?>/},a,e.C_BLOCK_COMMENT_MODE]},u={
+className:"title",begin:t.optional(r)+e.IDENT_RE,relevance:0
+},d=t.optional(r)+e.IDENT_RE+"\\s*\\(",p={
+type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],
+keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"],
+literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"],
+_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"]
+},_={className:"function.dispatch",relevance:0,keywords:{
+_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"]
+},
+begin:t.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,t.lookahead(/(<[^<>]+>|)\s*\(/))
+},m=[_,l,s,a,e.C_BLOCK_COMMENT_MODE,o,c],f={variants:[{begin:/=/,end:/;/},{
+begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],
+keywords:p,contains:m.concat([{begin:/\(/,end:/\)/,keywords:p,
+contains:m.concat(["self"]),relevance:0}]),relevance:0},g={className:"function",
+begin:"("+i+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,
+keywords:p,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:n,keywords:p,relevance:0},{
+begin:d,returnBegin:!0,contains:[u],relevance:0},{begin:/::/,relevance:0},{
+begin:/:/,endsWithParent:!0,contains:[c,o]},{relevance:0,match:/,/},{
+className:"params",begin:/\(/,end:/\)/,keywords:p,relevance:0,
+contains:[a,e.C_BLOCK_COMMENT_MODE,c,o,s,{begin:/\(/,end:/\)/,keywords:p,
+relevance:0,contains:["self",a,e.C_BLOCK_COMMENT_MODE,c,o,s]}]
+},s,a,e.C_BLOCK_COMMENT_MODE,l]};return{name:"C++",
+aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:p,illegal:"</",
+classNameAliases:{"function.dispatch":"built_in"},
+contains:[].concat(f,g,_,m,[l,{
+begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array|tuple|optional|variant|function)\\s*<(?!<)",
+end:">",keywords:p,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:p},{
+match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],
+className:{1:"keyword",3:"title.class"}}])}}})();hljs.registerLanguage("cpp",e)
+})();/*! `diff` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const a=e.regex;return{name:"Diff",
+aliases:["patch"],contains:[{className:"meta",relevance:10,
+match:a.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)
+},{className:"comment",variants:[{
+begin:a.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),
+end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{
+className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,
+end:/$/}]}}})();hljs.registerLanguage("diff",e)})();/*! `go` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const n={
+keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"],
+type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"],
+literal:["true","false","iota","nil"],
+built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"]
+};return{name:"Go",aliases:["golang"],keywords:n,illegal:"</",
+contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"string",
+variants:[e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{begin:"`",end:"`"}]},{
+className:"number",variants:[{begin:e.C_NUMBER_RE+"[i]",relevance:1
+},e.C_NUMBER_MODE]},{begin:/:=/},{className:"function",beginKeywords:"func",
+end:"\\s*(\\{|$)",excludeEnd:!0,contains:[e.TITLE_MODE,{className:"params",
+begin:/\(/,end:/\)/,endsParent:!0,keywords:n,illegal:/["']/}]}]}}})()
+;hljs.registerLanguage("go",e)})();/*! `ini` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const n=e.regex,a={className:"number",
+relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{begin:e.NUMBER_RE}]
+},s=e.COMMENT();s.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const i={
+className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)\}/
+}]},t={className:"literal",begin:/\bon|off|true|false|yes|no\b/},r={
+className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",
+end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'
+},{begin:"'",end:"'"}]},l={begin:/\[/,end:/\]/,contains:[s,t,i,r,a,"self"],
+relevance:0},c=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{
+name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,
+contains:[s,{className:"section",begin:/\[+/,end:/\]+/},{
+begin:n.concat(c,"(\\s*\\.\\s*",c,")*",n.lookahead(/\s*=\s*[^#\s]/)),
+className:"attr",starts:{end:/$/,contains:[s,l,t,i,r,a]}}]}}})()
+;hljs.registerLanguage("ini",e)})();/*! `json` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],n={
+scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",keywords:{
+literal:a},contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,
+relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0
+},e.QUOTE_STRING_MODE,n,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],
+illegal:"\\S"}}})();hljs.registerLanguage("json",e)})();/*! `markdown` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/,
+end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/,
+relevance:0},{
+begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
+relevance:2},{
+begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/),
+relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{
+begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/
+},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,
+returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",
+excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",
+end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[],
+variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}]
+},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{
+begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[]
+}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c)
+;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g)
+})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{
+className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{
+begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",
+contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",
+end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g,
+end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{
+begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{
+begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",
+contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{
+begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{
+className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{
+className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})()
+;hljs.registerLanguage("markdown",e)})();/*! `nix` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const n={
+keyword:["rec","with","let","in","inherit","assert","if","else","then"],
+literal:["true","false","or","and","null"],
+built_in:["import","abort","baseNameOf","dirOf","isNull","builtins","map","removeAttrs","throw","toString","derivation"]
+},s={className:"subst",begin:/\$\{/,end:/\}/,keywords:n},a={className:"string",
+contains:[{className:"char.escape",begin:/''\$/},s],variants:[{begin:"''",
+end:"''"},{begin:'"',end:'"'}]
+},i=[e.NUMBER_MODE,e.HASH_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,{
+begin:/[a-zA-Z0-9-_]+(\s*=)/,returnBegin:!0,relevance:0,contains:[{
+className:"attr",begin:/\S+/,relevance:.2}]}];return s.contains=i,{name:"Nix",
+aliases:["nixos"],keywords:n,contains:i}}})();hljs.registerLanguage("nix",e)
+})();/*! `protobuf` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const s={
+match:[/(message|enum|service)\s+/,e.IDENT_RE],scope:{1:"keyword",
+2:"title.class"}};return{name:"Protocol Buffers",aliases:["proto"],keywords:{
+keyword:["package","import","option","optional","required","repeated","group","oneof"],
+type:["double","float","int32","int64","uint32","uint64","sint32","sint64","fixed32","fixed64","sfixed32","sfixed64","bool","string","bytes"],
+literal:["true","false"]},
+contains:[e.QUOTE_STRING_MODE,e.NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{
+className:"function",beginKeywords:"rpc",end:/[{;]/,excludeEnd:!0,
+keywords:"rpc returns"},{begin:/^\s*[A-Z_]+(?=\s*=[^\n]+;$)/}]}}})()
+;hljs.registerLanguage("protobuf",e)})();/*! `rust` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const t=e.regex,n={
+className:"title.function.invoke",relevance:0,
+begin:t.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,t.lookahead(/\s*\(/))
+},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],s=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"]
+;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:s,
+keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"],
+literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:"</",
+contains:[e.C_LINE_COMMENT_MODE,e.COMMENT("/\\*","\\*/",{contains:["self"]
+}),e.inherit(e.QUOTE_STRING_MODE,{begin:/b?"/,illegal:null}),{
+className:"string",variants:[{begin:/b?r(#*)"(.|\n)*?"\1(?!#)/},{
+begin:/b?'\\?(x\w{2}|u\w{4}|U\w{8}|.)'/}]},{className:"symbol",
+begin:/'[a-zA-Z_][a-zA-Z0-9_]*/},{className:"number",variants:[{
+begin:"\\b0b([01_]+)"+a},{begin:"\\b0o([0-7_]+)"+a},{
+begin:"\\b0x([A-Fa-f0-9_]+)"+a},{
+begin:"\\b(\\d[\\d_]*(\\.[0-9_]+)?([eE][+-]?[0-9_]+)?)"+a}],relevance:0},{
+begin:[/fn/,/\s+/,e.UNDERSCORE_IDENT_RE],className:{1:"keyword",
+3:"title.function"}},{className:"meta",begin:"#!?\\[",end:"\\]",contains:[{
+className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE]}]},{
+begin:[/let/,/\s+/,/(?:mut\s+)?/,e.UNDERSCORE_IDENT_RE],className:{1:"keyword",
+3:"keyword",4:"variable"}},{
+begin:[/for/,/\s+/,e.UNDERSCORE_IDENT_RE,/\s+/,/in/],className:{1:"keyword",
+3:"variable",5:"keyword"}},{begin:[/type/,/\s+/,e.UNDERSCORE_IDENT_RE],
+className:{1:"keyword",3:"title.class"}},{
+begin:[/(?:trait|enum|struct|union|impl|for)/,/\s+/,e.UNDERSCORE_IDENT_RE],
+className:{1:"keyword",3:"title.class"}},{begin:e.IDENT_RE+"::",keywords:{
+keyword:"Self",built_in:i,type:s}},{className:"punctuation",begin:"->"},n]}}})()
+;hljs.registerLanguage("rust",e)})();/*! `shell` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var s=(()=>{"use strict";return s=>({name:"Shell Session",
+aliases:["console","shellsession"],contains:[{className:"meta.prompt",
+begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/,
+subLanguage:"bash"}}]})})();hljs.registerLanguage("shell",s)})();/*! `xml` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{
+const a=e.regex,n=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),s={
+className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},t={begin:/\s/,
+contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]
+},i=e.inherit(t,{begin:/\(/,end:/\)/}),c=e.inherit(e.APOS_STRING_MODE,{
+className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),r={
+endsWithParent:!0,illegal:/</,relevance:0,contains:[{className:"attr",
+begin:/[\p{L}0-9._:-]+/u,relevance:0},{begin:/=\s*/,relevance:0,contains:[{
+className:"string",endsParent:!0,variants:[{begin:/"/,end:/"/,contains:[s]},{
+begin:/'/,end:/'/,contains:[s]},{begin:/[^\s"'=<>`]+/}]}]}]};return{
+name:"HTML, XML",
+aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],
+case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin:/<![a-z]/,
+end:/>/,relevance:10,contains:[t,l,c,i,{begin:/\[/,end:/\]/,contains:[{
+className:"meta",begin:/<![a-z]/,end:/>/,contains:[t,i,l,c]}]}]
+},e.COMMENT(/<!--/,/-->/,{relevance:10}),{begin:/<!\[CDATA\[/,end:/\]\]>/,
+relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,
+relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",
+begin:/<style(?=\s|>)/,end:/>/,keywords:{name:"style"},contains:[r],starts:{
+end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",
+begin:/<script(?=\s|>)/,end:/>/,keywords:{name:"script"},contains:[r],starts:{
+end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{
+className:"tag",begin:/<>|<\/>/},{className:"tag",
+begin:a.concat(/</,a.lookahead(a.concat(n,a.either(/\/>/,/>/,/\s/)))),
+end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:r}]},{
+className:"tag",begin:a.concat(/<\//,a.lookahead(a.concat(n,/>/))),contains:[{
+className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}
+})();hljs.registerLanguage("xml",e)})();/*! `yaml` grammar compiled for Highlight.js 11.9.0 */
+(()=>{var e=(()=>{"use strict";return e=>{
+const n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={
+className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/
+},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",
+variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{
+variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={
+end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/,
+end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",
+contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{
+begin:/\w[\w :()\./-]*:(?=[ \t]|$)/},{begin:/"\w[\w :()\./-]*":(?=[ \t]|$)/},{
+begin:/'\w[\w :()\./-]*':(?=[ \t]|$)/}]},{className:"meta",begin:"^---\\s*$",
+relevance:10},{className:"string",
+begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{
+begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,
+relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",
+begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a
+},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",
+begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",
+relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{
+className:"number",
+begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"
+},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],r=[...b]
+;return r.pop(),r.push(i),l.contains=r,{name:"YAML",case_insensitive:!0,
+aliases:["yml"],contains:b}}})();hljs.registerLanguage("yaml",e)})();
\ No newline at end of file
diff --git a/tvix/eval/.skip-subtree b/tvix/eval/.skip-subtree
new file mode 100644
index 0000000000..05f9fc116f
--- /dev/null
+++ b/tvix/eval/.skip-subtree
@@ -0,0 +1,2 @@
+Do not traverse further, readTree will encounter Nix language tests
+and such and fail.
diff --git a/tvix/eval/Cargo.toml b/tvix/eval/Cargo.toml
new file mode 100644
index 0000000000..677ce6ab85
--- /dev/null
+++ b/tvix/eval/Cargo.toml
@@ -0,0 +1,61 @@
+[package]
+name = "tvix-eval"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "tvix_eval"
+
+[dependencies]
+builtin-macros = { path = "./builtin-macros", package = "tvix-eval-builtin-macros" }
+bytes = "1.4.0"
+bstr = { version = "1.8.0", features = ["serde"] }
+codemap = "0.1.3"
+codemap-diagnostic = "0.1.1"
+dirs = "4.0.0"
+genawaiter = { version = "0.99.1", default_features = false }
+imbl = { version = "2.0", features = [ "serde" ] }
+itertools = "0.12.0"
+lazy_static = "1.4.0"
+lexical-core = { version = "0.8.5", features = ["format", "parse-floats"] }
+os_str_bytes = { version = "6.3", features = ["conversions"] }
+path-clean = "0.1"
+proptest = { version = "1.3.0", default_features = false, features = ["std", "alloc", "tempfile"], optional = true }
+regex = "1.6.0"
+rnix = "0.11.0"
+rowan = "*" # pinned by rnix
+serde = { version = "1.0", features = [ "rc", "derive" ] }
+serde_json = "1.0"
+smol_str = "0.2.0"
+tabwriter = "1.2"
+test-strategy = { version = "0.2.1", optional = true }
+toml = "0.6.0"
+xml-rs = "0.8.4"
+sha2 = "0.10.8"
+sha1 = "0.10.6"
+md-5 = "0.10.6"
+data-encoding = "2.5.0"
+
+[dev-dependencies]
+criterion = "0.5"
+itertools = "0.12.0"
+pretty_assertions = "1.2.1"
+rstest = "0.19.0"
+tempfile = "3.3.0"
+
+[features]
+default = ["impure", "arbitrary", "nix_tests"]
+
+# Enables running the Nix language test suite from the original C++
+# Nix implementation (at version 2.3) against Tvix.
+nix_tests = []
+
+# Enables operations in the VM which depend on the ability to perform I/O
+impure = []
+
+# Enables Arbitrary impls for internal types (required to run tests)
+arbitrary = ["proptest", "test-strategy", "imbl/proptest"]
+
+[[bench]]
+name = "eval"
+harness = false
diff --git a/tvix/eval/README.md b/tvix/eval/README.md
new file mode 100644
index 0000000000..02e2100f59
--- /dev/null
+++ b/tvix/eval/README.md
@@ -0,0 +1,92 @@
+Tvix Evaluator
+==============
+
+This project implements an interpreter for the Nix programming
+language. You can experiment with an online version of the evaluator:
+[tvixbolt][].
+
+The interpreter aims to be compatible with `nixpkgs`, on the
+foundation of Nix 2.3.
+
+**Important note:** The evaluator is not yet feature-complete, and
+while the core mechanisms (compiler, runtime, ...) have stabilised
+somewhat, a lot of components are still changing rapidly.
+
+Please contact [TVL](https://tvl.fyi) with any questions you might
+have.
+
+## Building tvix-eval
+
+Please check the `README.md` one level up for instructions on how to build this.
+
+The evaluator itself can also be built with standard Rust tooling (i.e. `cargo
+build`).
+
+If you would like to clone **only** the evaluator and build it
+directly with Rust tooling, you can do:
+
+```bash
+git clone https://code.tvl.fyi/depot.git:/tvix/eval.git tvix-eval
+
+cd tvix-eval && cargo build
+```
+
+## Tests
+
+Tvix currently has three language test suites for tvix-eval:
+
+* `nix_tests` and `tvix_tests` are based on the same mechanism
+  borrowed from the C++ Nix implementation. They consist of
+  Nix files as well as expected output (if applicable).
+  The test cases are split into four categories:
+  `eval-okay` (evaluates successfully with the expected output),
+  `eval-fail` (fails to evaluate, no expected output),
+  `parse-okay` (expression parses successfully, no expected output)
+  and `parse-fail` (expression fails to parse, no expected output).
+  Tvix currently ignores the last two types of test cases, since
+  it doesn't implement its own parser.
+
+  Both test suites have a `notyetpassing` directory. All test cases
+  in here test behavior that is not yet supported by Tvix. They are
+  considered to be expected failures, so you can't forget to move
+  them into the test suite proper when fixing the incompatibility.
+
+  Additionally, separate targets in the depot pipeline, under
+  `//tvix/verify-lang-tests`, check both test suites (including
+  `notyetpassing` directories) against
+  C++ Nix 2.3 and the default C++ Nix version in nixpkgs.
+  This way we can prevent accidentally introducing test cases
+  for behavior that C++ Nix doesn't exhibit.
+
+  * `nix_tests` has the test cases from C++ Nix's language test
+    suite and is sporadically updated by manually syncing the
+    directories. The `notyetpassing` directory shows how far
+    it is until we pass it completely.
+
+  * `tvix_tests` contains test cases written by the Tvix contributors.
+    Some more or less duplicate test cases contained in `nix_tests`,
+    but many cover relevant behavior that isn't by `nix_tests`.
+    Consequently, it'd be nice to eventually merge the two test
+    suites into a jointly maintained, common Nix language test suite.
+
+    It also has a `notyetpassing` directory for missing behavior
+    that is discovered while working on Tvix and isn't covered by the
+    `nix_tests` suite.
+
+* `nix_oracle` can evaluate Nix expressions in Tvix and compare the
+  result against C++ Nix (2.3) directly. Eventually it should gain
+  the ability to property test generated Nix expressions.
+  An additional feature is that it can evaluate expressions without
+  `--strict`, so thunking behavior can be verified more easily.
+
+## rnix-parser
+
+Tvix is written in memory of jD91mZM2, the author of [rnix-parser][]
+who sadly [passed away][rip].
+
+Tvix makes heavy use of rnix-parser in its bytecode compiler. The
+parser is now maintained by Nix community members.
+
+[rnix-parser]: https://github.com/nix-community/rnix-parser
+[rip]: https://www.redox-os.org/news/open-source-mental-health/
+[tvixbolt]: https://bolt.tvix.dev/
diff --git a/tvix/eval/benches/eval.rs b/tvix/eval/benches/eval.rs
new file mode 100644
index 0000000000..57d4eb71b5
--- /dev/null
+++ b/tvix/eval/benches/eval.rs
@@ -0,0 +1,36 @@
+use criterion::{black_box, criterion_group, criterion_main, Criterion};
+use itertools::Itertools;
+
+fn interpret(code: &str) {
+    tvix_eval::Evaluation::new_pure().evaluate(code, None);
+}
+
+fn eval_literals(c: &mut Criterion) {
+    c.bench_function("int", |b| {
+        b.iter(|| {
+            interpret(black_box("42"));
+        })
+    });
+}
+
+fn eval_merge_attrs(c: &mut Criterion) {
+    c.bench_function("merge small attrs", |b| {
+        b.iter(|| {
+            interpret(black_box("{ a = 1; b = 2; } // { c = 3; }"));
+        })
+    });
+
+    c.bench_function("merge large attrs with small attrs", |b| {
+        let large_attrs = format!(
+            "{{{}}}",
+            (0..10000).map(|n| format!("a{n} = {n};")).join(" ")
+        );
+        let expr = format!("{large_attrs} // {{ c = 3; }}");
+        b.iter(move || {
+            interpret(black_box(&expr));
+        })
+    });
+}
+
+criterion_group!(benches, eval_literals, eval_merge_attrs);
+criterion_main!(benches);
diff --git a/tvix/eval/build.rs b/tvix/eval/build.rs
new file mode 100644
index 0000000000..a9c9a78b06
--- /dev/null
+++ b/tvix/eval/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    println!(
+        "cargo:rustc-env=TVIX_CURRENT_SYSTEM={}",
+        &env::var("TARGET").unwrap()
+    );
+    println!("cargo:rerun-if-changed-env=TARGET")
+}
diff --git a/tvix/eval/builtin-macros/.gitignore b/tvix/eval/builtin-macros/.gitignore
new file mode 100644
index 0000000000..eb5a316cbd
--- /dev/null
+++ b/tvix/eval/builtin-macros/.gitignore
@@ -0,0 +1 @@
+target
diff --git a/tvix/eval/builtin-macros/Cargo.toml b/tvix/eval/builtin-macros/Cargo.toml
new file mode 100644
index 0000000000..3a35ea12a0
--- /dev/null
+++ b/tvix/eval/builtin-macros/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "tvix-eval-builtin-macros"
+version = "0.0.1"
+authors = [ "Griffin Smith <root@gws.fyi>" ]
+edition = "2021"
+
+[dependencies]
+syn = { version = "1.0.57", features = ["full", "parsing", "printing", "visit", "visit-mut", "extra-traits"] }
+quote = "1.0.8"
+proc-macro2 = "1"
+
+[lib]
+proc-macro = true
+
+[dev-dependencies]
+tvix-eval = { path = "../" }
diff --git a/tvix/eval/builtin-macros/src/lib.rs b/tvix/eval/builtin-macros/src/lib.rs
new file mode 100644
index 0000000000..5cc9807f54
--- /dev/null
+++ b/tvix/eval/builtin-macros/src/lib.rs
@@ -0,0 +1,357 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::{quote, quote_spanned, ToTokens};
+use syn::parse::Parse;
+use syn::spanned::Spanned;
+use syn::{
+    parse2, parse_macro_input, parse_quote, parse_quote_spanned, Attribute, FnArg, Ident, Item,
+    ItemMod, LitStr, Meta, Pat, PatIdent, PatType, Token, Type,
+};
+
+/// Description of a single argument passed to a builtin
+struct BuiltinArgument {
+    /// The name of the argument, to be used in docstrings and error messages
+    name: Ident,
+
+    /// Type of the argument.
+    ty: Box<Type>,
+
+    /// Whether the argument should be forced before the underlying builtin
+    /// function is called.
+    strict: bool,
+
+    /// Propagate catchable values as values to the function, rather than short-circuit returning
+    /// them if encountered
+    catch: bool,
+
+    /// Span at which the argument was defined.
+    span: Span,
+}
+
+fn extract_docstring(attrs: &[Attribute]) -> Option<String> {
+    // Rust docstrings are transparently written pre-macro expansion into an attribute that looks
+    // like:
+    //
+    // #[doc = "docstring here"]
+    //
+    // Multi-line docstrings yield multiple attributes in order, which we assemble into a single
+    // string below.
+
+    #[allow(dead_code)]
+    #[derive(Debug)]
+    struct Docstring {
+        eq: Token![=],
+        doc: LitStr,
+    }
+
+    impl Parse for Docstring {
+        fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+            Ok(Self {
+                eq: input.parse()?,
+                doc: input.parse()?,
+            })
+        }
+    }
+
+    attrs
+        .iter()
+        .filter(|attr| attr.path.get_ident().into_iter().any(|id| id == "doc"))
+        .filter_map(|attr| parse2::<Docstring>(attr.tokens.clone()).ok())
+        .map(|docstring| docstring.doc.value())
+        .reduce(|mut fst, snd| {
+            if snd.is_empty() {
+                // An empty string represents a spacing newline that was added in the
+                // original doc comment.
+                fst.push_str("\n\n");
+            } else {
+                fst.push_str(&snd);
+            }
+
+            fst
+        })
+}
+
+/// Parse arguments to the `builtins` macro itself, such as `#[builtins(state = Rc<State>)]`.
+fn parse_module_args(args: TokenStream) -> Option<Type> {
+    if args.is_empty() {
+        return None;
+    }
+
+    let meta: Meta = syn::parse(args).expect("could not parse arguments to `builtins`-attribute");
+    let name_value = match meta {
+        Meta::NameValue(nv) => nv,
+        _ => panic!("arguments to `builtins`-attribute must be of the form `name = value`"),
+    };
+
+    if *name_value.path.get_ident().unwrap() != "state" {
+        return None;
+    }
+
+    if let syn::Lit::Str(type_name) = name_value.lit {
+        let state_type: Type =
+            syn::parse_str(&type_name.value()).expect("failed to parse builtins state type");
+        return Some(state_type);
+    }
+
+    panic!("state attribute must be a quoted Rust type");
+}
+
+/// Mark the annotated module as a module for defining Nix builtins.
+///
+/// An optional type definition may be specified as an argument (e.g. `#[builtins(Rc<State>)]`),
+/// which will add a parameter to the `builtins` function of that type which is passed to each
+/// builtin upon instantiation. Using this, builtins that close over some external state can be
+/// written.
+///
+/// The type of each function is rewritten to receive a `Vec<Value>`, containing each `Value`
+/// argument that the function receives. The body of functions is accordingly rewritten to "unwrap"
+/// values from this vector and bind them to the correct names, so unless a static error occurs this
+/// transformation is mostly invisible to users of the macro.
+///
+/// A function `fn builtins() -> Vec<Builtin>` will be defined within the annotated module,
+/// returning a list of [`tvix_eval::Builtin`] for each function annotated with the `#[builtin]`
+/// attribute within the module. If a `state` type is specified, the `builtins` function will take a
+/// value of that type.
+///
+/// Each invocation of the `#[builtin]` annotation within the module should be passed a string
+/// literal for the name of the builtin.
+///
+/// # Examples
+/// ```ignore
+/// # use tvix_eval;
+/// # use tvix_eval_builtin_macros::builtins;
+///
+/// #[builtins]
+/// mod builtins {
+///     use tvix_eval::{GenCo, ErrorKind, Value};
+///
+///     #[builtin("identity")]
+///     pub async fn builtin_identity(co: GenCo, x: Value) -> Result<Value, ErrorKind> {
+///         Ok(x)
+///     }
+///
+///     // Builtins can request their argument not be forced before being called by annotating the
+///     // argument with the `#[lazy]` attribute
+///
+///     #[builtin("tryEval")]
+///     pub async fn builtin_try_eval(co: GenCo, #[lazy] x: Value) -> Result<Value, ErrorKind> {
+///         todo!()
+///     }
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn builtins(args: TokenStream, item: TokenStream) -> TokenStream {
+    let mut module = parse_macro_input!(item as ItemMod);
+
+    // parse the optional state type, which users might want to pass to builtins
+    let state_type = parse_module_args(args);
+
+    let (_, items) = match &mut module.content {
+        Some(content) => content,
+        None => {
+            return (quote_spanned!(module.span() =>
+                compile_error!("Builtin modules must be defined in-line")
+            ))
+            .into();
+        }
+    };
+
+    let mut builtins = vec![];
+    for item in items.iter_mut() {
+        if let Item::Fn(f) = item {
+            if let Some(builtin_attr_pos) = f
+                .attrs
+                .iter()
+                .position(|attr| attr.path.get_ident().iter().any(|id| *id == "builtin"))
+            {
+                let builtin_attr = f.attrs.remove(builtin_attr_pos);
+                let name: LitStr = match builtin_attr.parse_args() {
+                    Ok(args) => args,
+                    Err(err) => return err.into_compile_error().into(),
+                };
+
+                if f.sig.inputs.len() <= 1 {
+                    return (quote_spanned!(
+                        f.sig.inputs.span() =>
+                            compile_error!("Builtin functions must take at least two arguments")
+                    ))
+                    .into();
+                }
+
+                // Inspect the first argument to determine if this function is
+                // taking the state parameter.
+                // TODO(tazjin): add a test in //tvix/eval that covers this
+                let mut captures_state = false;
+                if let FnArg::Typed(PatType { pat, .. }) = &f.sig.inputs[0] {
+                    if let Pat::Ident(PatIdent { ident, .. }) = pat.as_ref() {
+                        if *ident == "state" {
+                            if state_type.is_none() {
+                                panic!("builtin captures a `state` argument, but no state type was defined");
+                            }
+
+                            captures_state = true;
+                        }
+                    }
+                }
+
+                let mut rewritten_args = std::mem::take(&mut f.sig.inputs)
+                    .into_iter()
+                    .collect::<Vec<_>>();
+
+                // Split out the value arguments from the static arguments.
+                let split_idx = if captures_state { 2 } else { 1 };
+                let value_args = rewritten_args.split_off(split_idx);
+
+                let builtin_arguments = value_args
+                    .into_iter()
+                    .map(|arg| {
+                        let span = arg.span();
+                        let mut strict = true;
+                        let mut catch = false;
+                        let (name, ty) = match arg {
+                            FnArg::Receiver(_) => {
+                                return Err(quote_spanned!(span => {
+                                    compile_error!("unexpected receiver argument in builtin")
+                                }))
+                            }
+                            FnArg::Typed(PatType {
+                                mut attrs, pat, ty, ..
+                            }) => {
+                                attrs.retain(|attr| {
+                                    attr.path.get_ident().into_iter().any(|id| {
+                                        if id == "lazy" {
+                                            strict = false;
+                                            false
+                                        } else if id == "catch" {
+                                            catch = true;
+                                            false
+                                        } else {
+                                            true
+                                        }
+                                    })
+                                });
+                                match pat.as_ref() {
+                                    Pat::Ident(PatIdent { ident, .. }) => {
+                                        (ident.clone(), ty.clone())
+                                    }
+                                    _ => panic!("ignored value parameters must be named, e.g. `_x` and not just `_`"),
+                                }
+                            }
+                        };
+
+                        if catch && !strict {
+                            return Err(quote_spanned!(span => {
+                                compile_error!("Cannot mix both lazy and catch on the same argument")
+                            }));
+                        }
+
+                        Ok(BuiltinArgument {
+                            strict,
+                            catch,
+                            span,
+                            name,
+                            ty,
+                        })
+                    })
+                    .collect::<Result<Vec<BuiltinArgument>, _>>();
+
+                let builtin_arguments = match builtin_arguments {
+                    Err(err) => return err.into(),
+
+                    // reverse argument order, as they are popped from the stack
+                    // slice in opposite order
+                    Ok(args) => args,
+                };
+
+                // Rewrite the argument to the actual function to take a
+                // `Vec<Value>`, which is then destructured into the
+                // user-defined values in the function header.
+                let sig_span = f.sig.span();
+                rewritten_args.push(parse_quote_spanned!(sig_span=> mut values: Vec<Value>));
+                f.sig.inputs = rewritten_args.into_iter().collect();
+
+                // Rewrite the body of the function to do said argument forcing.
+                //
+                // This is done by creating a new block for each of the
+                // arguments that evaluates it, and wraps the inner block.
+                for arg in &builtin_arguments {
+                    let block = &f.block;
+                    let ty = &arg.ty;
+                    let ident = &arg.name;
+
+                    if arg.strict {
+                        if arg.catch {
+                            f.block = Box::new(parse_quote_spanned! {arg.span=> {
+                                let #ident: #ty = tvix_eval::generators::request_force(&co, values.pop()
+                                  .expect("Tvix bug: builtin called with incorrect number of arguments")).await;
+                                #block
+                            }});
+                        } else {
+                            f.block = Box::new(parse_quote_spanned! {arg.span=> {
+                                let #ident: #ty = tvix_eval::generators::request_force(&co, values.pop()
+                                  .expect("Tvix bug: builtin called with incorrect number of arguments")).await;
+                                if #ident.is_catchable() {
+                                    return Ok(#ident);
+                                }
+                                #block
+                            }});
+                        }
+                    } else {
+                        f.block = Box::new(parse_quote_spanned! {arg.span=> {
+                            let #ident: #ty = values.pop()
+                              .expect("Tvix bug: builtin called with incorrect number of arguments");
+
+                            #block
+                        }})
+                    }
+                }
+
+                let fn_name = f.sig.ident.clone();
+                let arg_count = builtin_arguments.len();
+                let docstring = match extract_docstring(&f.attrs) {
+                    Some(docs) => quote!(Some(#docs)),
+                    None => quote!(None),
+                };
+
+                if captures_state {
+                    builtins.push(quote_spanned! { builtin_attr.span() => {
+                        let inner_state = state.clone();
+                        tvix_eval::Builtin::new(
+                            #name,
+                            #docstring,
+                            #arg_count,
+                            move |values| Gen::new(|co| tvix_eval::generators::pin_generator(#fn_name(inner_state.clone(), co, values))),
+                        )
+                    }});
+                } else {
+                    builtins.push(quote_spanned! { builtin_attr.span() => {
+                        tvix_eval::Builtin::new(
+                            #name,
+                            #docstring,
+                            #arg_count,
+                            |values| Gen::new(|co| tvix_eval::generators::pin_generator(#fn_name(co, values))),
+                        )
+                    }});
+                }
+            }
+        }
+    }
+
+    if let Some(state_type) = state_type {
+        items.push(parse_quote! {
+            pub fn builtins(state: #state_type) -> Vec<(&'static str, Value)> {
+                vec![#(#builtins),*].into_iter().map(|b| (b.name(), Value::Builtin(b))).collect()
+            }
+        });
+    } else {
+        items.push(parse_quote! {
+            pub fn builtins() -> Vec<(&'static str, Value)> {
+                vec![#(#builtins),*].into_iter().map(|b| (b.name(), Value::Builtin(b))).collect()
+            }
+        });
+    }
+
+    module.into_token_stream().into()
+}
diff --git a/tvix/eval/builtin-macros/tests/tests.rs b/tvix/eval/builtin-macros/tests/tests.rs
new file mode 100644
index 0000000000..288b6670e1
--- /dev/null
+++ b/tvix/eval/builtin-macros/tests/tests.rs
@@ -0,0 +1,45 @@
+pub use tvix_eval::{Builtin, Value};
+use tvix_eval_builtin_macros::builtins;
+
+#[builtins]
+mod builtins {
+    use tvix_eval::generators::{Gen, GenCo};
+    use tvix_eval::{ErrorKind, Value};
+
+    /// Test docstring.
+    ///
+    /// It has multiple lines!
+    #[builtin("identity")]
+    pub async fn builtin_identity(co: GenCo, x: Value) -> Result<Value, ErrorKind> {
+        Ok(x)
+    }
+
+    #[builtin("tryEval")]
+    pub async fn builtin_try_eval(_co: GenCo, #[lazy] _x: Value) -> Result<Value, ErrorKind> {
+        unimplemented!("builtin is never called")
+    }
+}
+
+#[test]
+fn builtins() {
+    let builtins = builtins::builtins();
+    assert_eq!(builtins.len(), 2);
+
+    let (_, identity) = builtins
+        .iter()
+        .find(|(name, _)| *name == "identity")
+        .unwrap();
+
+    match identity {
+        Value::Builtin(identity) => assert_eq!(
+            identity.documentation(),
+            Some(
+                r#" Test docstring.
+
+ It has multiple lines!"#
+            )
+        ),
+
+        _ => panic!("builtin was not a builtin"),
+    }
+}
diff --git a/tvix/eval/default.nix b/tvix/eval/default.nix
new file mode 100644
index 0000000000..91661291f7
--- /dev/null
+++ b/tvix/eval/default.nix
@@ -0,0 +1,9 @@
+# TODO: find a way to build the benchmarks via crate2nix
+{ depot, pkgs, ... }:
+
+depot.tvix.crates.workspaceMembers.tvix-eval.build.override {
+  runTests = true;
+
+  # Make C++ Nix available, to compare eval results against.
+  testInputs = [ pkgs.nix ];
+}
diff --git a/tvix/eval/docs/abandoned/thread-local-vm.md b/tvix/eval/docs/abandoned/thread-local-vm.md
new file mode 100644
index 0000000000..c6a2d5e07e
--- /dev/null
+++ b/tvix/eval/docs/abandoned/thread-local-vm.md
@@ -0,0 +1,233 @@
+# We can't have nice things because IFD
+
+The thread-local VM work below was ultimately not merged because it
+was decided that it would be harmful for `tvix::eval::Value` to
+implement `Eq`, `Hash`, or any of the other `std` traits.
+
+Implementing `std` traits on `Value` was deemed harmful because IFD
+can cause arbitrary amounts of compilation to occur, including
+network transactions with builders.  Obviously it would be
+unexpected and error-prone to have a `PartialEq::eq()` which does
+something like this.  This problem does not manifest within the
+"nixpkgs compatibility only" scope, or in any undeprecated language
+feature other than IFD.  Although IFD is outside the "nixpkgs
+compatibility scope", it [has been added to the TVL compatibility
+scope](https://cl.tvl.fyi/c/depot/+/7193/comment/3418997b_0dbd0b65/).
+
+This was the sole reason for not merging.
+
+The explanation below may be useful in case future circumstances
+affect the relevance of the reasoning above.
+
+The implementation can be found in these CLs:
+
+- [refactor(tvix/eval): remove lifetime parameter from VM<'o>](https://cl.tvl.fyi/c/depot/+/7194)
+- [feat(tvix/eval): [FOUNDLING] thread-local VM](https://cl.tvl.fyi/c/depot/+/7195)
+- [feat(tvix/eval): [FOUNDLING] VM::vm_xxx convenience methods](https://cl.tvl.fyi/c/depot/+/7196)
+- [refactor(tvix/eval): [FOUNDLING]: drop explicit `&mut vm` parameter](https://cl.tvl.fyi/c/depot/+/7197)
+
+# Thread-local storage for tvix::eval::vm::VM
+
+## The problem
+
+`Value::force()` takes a `&mut VM` argument, since forcing a value
+requires executing opcodes.  This means that `Value::nix_eq()` too
+must take a `&mut VM`, since any sensible definition of equality
+will have to force thunks.
+
+Unfortunately Rust's `PartialEq::eq()` function does not accept any
+additional arguments like this, so `Value` cannot implement
+`PartialEq`.  Worse, structs which *contain* `Value`s can't
+implement `PartialEq` either.  This means `Value`, and anything
+containing it, cannot be the key for a `BTreeMap` or `HashMap`.  We
+can't even insert `Value`s into a `HashSet`!
+
+There are other situations like this that don't involve `PartialEq`,
+but it's the most glaring one.  The main problem is that you need a
+`VM` in order to force thunks, and thunks can be anywhere in a
+`Value`.
+
+## Solving the problem with thread-locals
+
+We could avoid threading the `&mut VM` through the entire codebase
+by making it a thread-local.
+
+To do this without a performance hit, we need to use LLVM
+thread-locals, which are the same cost as references to `static`s
+but load relative to
+[`llvm.threadlocal.address`][threadlocal-intrinsic] instead of
+relative to the data segment.  Unfortunately `#[thread_local]` [is
+unstable][thread-local-unstable] and [unsafe in
+general][thread-local-unsafe] for most of the cases where we would
+want to use it.  There is one [exception][tls-const-init], however:
+if a `!thread_local()` has a `const` initializer, the compiler will
+insert a `#[thread_local]`; this special case is both safe and
+stable.
+
+The difficult decision is what the type of the thread-local should
+be.  Since you can't get a mutable reference to a `thread_local!()`
+it will have to be some interior-mutability-bestowing wrapper around
+our current `struct VM`.  Here are the choices:
+
+### `RefCell<VM>`
+
+This is the obvious first choice, since it lets you borrow a
+`RefMut<Target=VM>`.  The problem here is that we want to keep the
+codebase written such that all the functions in `impl VM` still take
+a `&mut self`.  This means that there will be an active mutable
+borrow for the duration of `VM::call_builtin()`.  So if we implement
+`PartialEq` by having `eq()` attempt a second mutable borrow from
+the thread-local storage, it will fail since there is already an
+active borrow.
+
+The problem here is that you can't "unborrow" a `RefMut` except by
+dropping it.  There's no way around this.
+
+#### Problem: Uglification
+
+The only solution here is to rewrite all the functions in `impl VM`
+so they don't take any kind of `self` argument, and then have them
+do a short-lived `.borrow_mut()` from the thread-local `RefCell`
+*separately, each time* they want to modify one of the fields of
+`VM` (currently `frames`, `stack`, `with_stack`, `warnings`).  This
+means that if you had a code sequence like this:
+
+```
+impl VM {
+  fn foo(&mut self, ...) {
+    ...
+    self.frame().ip += 1;
+    self.some_other_method();
+    self.frame().ip += 1;
+```
+
+You would need to add *two separate `borrow_mut()`s*, one for each
+of the `self.frame().ip+=1` statements.  You can't just do one big
+`borrow_mut()` because `some_other_method()` will call
+`borrow_mut()` and panic.
+
+#### Problem: Performance
+
+The `RefCell<VM>` approach also has a fairly huge performance hit,
+because every single modification to any part of `VM` will require a
+reference count increment/decrement, and a conditional branch based
+on the check (which will never fail) that the `RefCell` isn't
+already mutably borrowed.  It will also impede a lot of rustc's
+optimizations.
+
+### `Cell<VM>`
+
+This is a non-starter because it means that in order to mutate any
+field of `VM`, you have to move the entire `struct VM` out of the
+`Cell`, mutate it, and move it back in.
+
+### `Cell<Box<VM>>`
+
+Now we're getting warmer.  Here, we can move the `Box<VM>` out of
+the cell with a single pointer-sized memory access.
+
+We don't want to do the "uglification" described in the previous
+section.  We are very fortunate that, sometime in mid-2019, the Rust
+dieties [decreed by fiat][fiat-decree] that `&Cell<T>` and `&mut T`
+are bit-for-bit identical, and even gave us mortals safe wrappers
+[`from_mut()`][from_mut] and [`get_mut()`][get_mut] around
+`mem::transmute()`.
+
+So now, when a `VM` method (which takes `&mut self`) calls out to
+some external code (like a builtin), instead of passing the `&mut
+self` to the external code it can call `Cell::from_mut(&mut self)`,
+and then `Cell::swap()` that into the thread-local storage cell for
+the duration of the external code.  After the external code returns,
+it can `Cell::swap()` it back.  This whole dance gets wrapped in a
+lexical block, and the borrow checker sees that the `&Cell<Box<VM>>`
+returned by `Cell::from_mut()` lives only until the end of the
+lexical block, *so we get the `&mut self` back after the close-brace
+for that block*.  NLL FTW.  This sounds like a lot of work, but it
+should compile down to two pointer-sized loads and two pointer-sized
+stores, and it is incurred basically only for `OpBuiltin`.
+
+This all works, with only two issues:
+
+1. `vm.rs` needs to be very careful to do the thread-local cell swap
+   dance before calling anything that might call `PartialEq::eq()`
+   (or any other method that expects to be able to pull the `VM` out
+   of thread-local storage).  There is no compile-time check that we
+   did the dance in all the right places.  If we forget to do the
+   dance somewhere we'll get a runtime panic from `Option::expect()`
+   (see next section).
+
+2. Since we need to call `Cell::from_mut()` on a `Box<VM>` rather
+   than a bare `VM`, we still need to rewrite all of `vm.rs` so that
+   every function takes a `&mut Box<VM>` instead of a `&mut self`.
+   This creates a huge amount of "noise" in the code.
+
+Fortunately, it turns out that nearly all the "noise" that arises
+from the second point can be eliminated by taking advantage of
+[deref coercions][deref-coercions]!  This was the last "shoe to
+drop".
+
+There is still the issue of having to be careful about calls from
+`vm.rs` to things outside that file, but it's manageable.
+
+### `Cell<Option<Box<VM>>>`
+
+In order to get the "safe and stable `#[thread_local]`"
+[exception][tls-const-init] we need a `const` initializer, which
+means we need to be able to put something into the `Cell` that isn't
+a `VM`.  So the type needs to be `Cell<Option<Box<VM>>>`.
+
+Recall that you can't turn an `Option<&T>` into an `&Option<T>`.
+The latter type has the "is this a `Some` or `None`" bit immediately
+adjacent to the bits representing `T`.  So if I hand you a `t:&T`
+and you wrap it as `Some(t)`, those bits aren't adjacent in memory.
+This means that all the VM methods need to operate on an
+`Option<Box<VM>>` -- we can't just wrap a `Some()` around `&mut
+self` "at the last minute" before inserting it into the thread-local
+storage cell.  Fortunately deref coercions save the day here too --
+the coercion is inferred through both layers (`Box` and `Option`) of
+wrapper, so there is no additional noise in the code.
+
+Note that Rust is clever and can find some sequence of bits that
+aren't a valid `T`, so `sizeof(Option<T>)==sizeof(T)`.  And in fact,
+`Box<T>` is one of these cases (and this is guaranteed).  So the
+`Option` has no overhead.
+
+# Closing thoughts, language-level support
+
+This would have been easier with language-level support.
+
+## What wouldn't help
+
+Although it [it was decreed][fiat-decree] that `Cell<T>` and `&mut
+T` are interchangeable, a `LocalKey<Cell<T>>` isn't quite the same
+thing as a `Cell<T>`, so it wouldn't be safe for the standard
+library to contain something like this:
+
+```
+impl<T> LocalKey<Cell<T>> {
+  fn get_mut(&self) -> &mut T {
+    unsafe {
+      // ... mem::transmute() voodoo goes here ...
+```
+
+The problem here is that you can call `LocalKey<Cell<T>>::get_mut()` twice and
+end up with two `&mut T`s that point to the same thing (mutable aliasing) which
+results in undefined behavior.
+
+## What would help
+
+The ideal solution is for Rust to let you call arbitrary methods
+`T::foo(&mut self...)` on a `LocalKey<Cell<T>>`.  This way you can
+have one (and only one) `&mut T` at any syntactical point in your
+program -- the `&mut self`.
+
+
+[tls-const-init]: https://github.com/rust-lang/rust/pull/90774
+[thread-local-unstable]: https://github.com/rust-lang/rust/issues/29594
+[thread-local-unsafe-generally]: https://github.com/rust-lang/rust/issues/54366
+[fiat-decree]: https://github.com/rust-lang/rust/issues/43038
+[from_mut]: https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#method.from_mut
+[get_mut]: https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#method.get_mut
+[thread-local-unsafe]: [https://github.com/rust-lang/rust/issues/54366]
+[deref-coercions]: https://doc.rust-lang.org/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods
+[threadlocal-intrinsic]: https://llvm.org/docs/LangRef.html#llvm-threadlocal-address-intrinsic
diff --git a/tvix/eval/docs/bindings.md b/tvix/eval/docs/bindings.md
new file mode 100644
index 0000000000..2b062cb13d
--- /dev/null
+++ b/tvix/eval/docs/bindings.md
@@ -0,0 +1,133 @@
+Compilation of bindings
+=======================
+
+Compilation of Nix bindings is one of the most mind-bending parts of Nix
+evaluation. The implementation of just the compilation is currently almost 1000
+lines of code, excluding the various insane test cases we dreamt up for it.
+
+## What is a binding?
+
+In short, any attribute set or `let`-expression. Tvix currently does not treat
+formals in function parameters (e.g. `{ name ? "fred" }: ...`) the same as these
+bindings.
+
+They have two very difficult features:
+
+1. Keys can mutually refer to each other in `rec` sets or `let`-bindings,
+   including out of definition order.
+2. Attribute sets can be nested, and parts of one attribute set can be defined
+   in multiple separate bindings.
+
+Tvix resolves as much of this logic statically (i.e. at compile-time) as
+possible, but the procedure is quite complicated.
+
+## High-level concept
+
+The idea behind the way we compile bindings is to fully resolve nesting
+statically, and use the usual mechanisms (i.e. recursion/thunking/value
+capturing) for resolving dynamic values.
+
+This is done by compiling bindings in several phases:
+
+1. An initial compilation phase *only* for plain inherit statements (i.e.
+   `inherit name;`), *not* for namespaced inherits (i.e. `inherit (from)
+   name;`).
+
+2. A declaration-only phase, in which we use the compiler's scope tracking logic
+   to calculate the physical runtime stack indices (further referred to as
+   "stack slots" or just "slots") that all values will end up in.
+
+   In this phase, whenever we encounter a nested attribute set, it is merged
+   into a custom data structure that acts like a synthetic AST node.
+
+   This can be imagined similar to a rewrite like this:
+
+   ```nix
+   # initial code:
+   {
+       a.b = 1;
+       a.c = 2;
+   }
+
+   # rewritten form:
+   {
+       a = {
+           b = 1;
+           c = 2;
+       };
+   }
+   ```
+
+   The rewrite applies to attribute sets and `let`-bindings alike.
+
+   At the end of this phase, we know the stack slots of all namespaces for
+   inheriting from, all values inherited from them, and all values (and
+   optionall keys) of bindings at the current level.
+
+   Only statically known keys are actually merged, so any dynamic keys that
+   conflict will lead to a "key already defined" error at runtime.
+
+3. A compilation phase, in which all values (and, when necessary, keys) are
+   actually compiled. In this phase the custom data structure used for merging
+   is encountered when compiling values.
+
+   As this data structure acts like an AST node, the process begins recursively
+   for each nested attribute set.
+
+At the end of this process we have bytecode that leaves the required values (and
+optionally keys) on the stack. In the case of attribute sets, a final operation
+is emitted that constructs the actual attribute set structure at runtime. For
+`let`-bindings a final operation is emitted that removes these locals from the
+stack when the scope ends.
+
+## Moving parts
+
+WARNING: This documents the *current* implementation. If you only care about the
+conceptual aspects, see above.
+
+There's a few types involved:
+
+* `PeekableAttrs`: peekable iterator over an attribute path (e.g. `a.b.c`)
+* `BindingsKind`: enum defining the kind of bindings (attrs/recattrs/let)
+* `AttributeSet`: struct holding the bindings kind, the AST nodes with inherits
+  (both namespaced and not), and an internal representation of bindings
+  (essentially a vector of tuples of the peekable attrs and the expression to
+  compile for the value).
+* `Binding`: enum describing the kind of binding (namespaced inherit, attribute
+  set, plain binding of *any other value type*)
+* `KeySlot`: enum describing the location in which a key slot is placed at
+  runtime (nowhere, statically known value in a slot, dynamic value in a slot)
+* `TrackedBinding`: struct representing statically known information about a
+  single binding (its key slot, value slot and `Binding`)
+* `TrackedBindings`: vector of tracked bindings, which implements logic for
+  merging attribute sets together
+
+And quite a few methods on `Compiler`:
+
+* `compile_bindings`: entry point for compiling anything that looks like a
+  binding, this calls out to the functions below.
+* `compile_plain_inherits`: takes all inherits of a bindings node and compiles
+  the ones that are trivial to compile (i.e. just plain inherits without a
+  namespace). The `rnix` parser does not represent namespaced/plain inherits in
+  different nodes, so this function also aggregates the namespaced inherits and
+  returns them for further use
+* `declare_namespaced_inherits`: passes over all namespaced inherits and
+  declares them on the locals stack, as well as inserts them into the provided
+  `TrackedBindings`
+* `declare_bindings`: declares all regular key/value bindings in a bindings
+  scope, but without actually compiling their keys or values.
+
+  There's a lot of heavy lifting going on here:
+
+  1. It invokes the various pieces of logic responsible for merging nested
+     attribute sets together, creating intermediate data structures in the value
+     slots of bindings that can be recursively processed the same way.
+  2. It decides on the key slots of expressions based on the kind of bindings,
+     and the type of expression providing the key.
+* `bind_values`: runs the actual compilation of values. Notably this function is
+  responsible for recursively compiling merged attribute sets when it encounters
+  a `Binding::Set` (on which it invokes `compile_bindings` itself).
+
+In addition to these several methods (such as `compile_attr_set`,
+`compile_let_in`, ...) invoke the binding-kind specific logic and then call out
+to the functions above.
diff --git a/tvix/eval/docs/build-references.md b/tvix/eval/docs/build-references.md
new file mode 100644
index 0000000000..badcea1155
--- /dev/null
+++ b/tvix/eval/docs/build-references.md
@@ -0,0 +1,254 @@
+Build references in derivations
+===============================
+
+This document describes how build references are calculated in Tvix. Build
+references are used to determine which store paths should be available to a
+builder during the execution of a build (i.e. the full build closure of a
+derivation).
+
+## String contexts in C++ Nix
+
+In C++ Nix, each string value in the evaluator carries an optional so-called
+"string context".
+
+These contexts are themselves a list of strings that take one of the following
+formats:
+
+1. `!<output_name>!<drv_path>`
+
+   This format describes a build reference to a specific output of a derivation.
+
+2. `=<drv_path>`
+
+   This format is used for a special case where a derivation attribute directly
+   refers to a derivation path (e.g. by accessing `.drvPath` on a derivation).
+
+   Note: In C++ Nix this case is quite special and actually requires a
+   store-database query during evaluation.
+
+3. `<path>` - a non-descript store path input, usually a plain source file (e.g.
+   from something like `src = ./.` or `src = ./foo.txt`).
+
+   In the case of `unsafeDiscardOutputDependency` this is used to pass a raw
+   derivation file, but *not* pull in its outputs.
+
+Lets introduce names for these (in the same order) to make them easier to
+reference below:
+
+```rust
+enum BuildReference {
+    /// !<output_name>!<drv_path>
+    SingleOutput(OutputName, DrvPath),
+
+    /// =<drv_path>
+    DrvClosure(DrvPath),
+
+    /// <path>
+    Path(StorePath),
+}
+```
+
+String contexts are, broadly speaking, created whenever a string is the result
+of a computation (e.g. string interpolation) that used a *computed* path or
+derivation in any way.
+
+Note: This explicitly does *not* include simply writing a literal string
+containing a store path (whether valid or not). That is only permitted through
+the `storePath` builtin.
+
+## Derivation inputs
+
+Based on the data above, the fields `inputDrvs` and `inputSrcs` of derivations
+are populated in `builtins.derivationStrict` (the function which
+`builtins.derivation`, which isn't actually a builtin, wraps).
+
+`inputDrvs` is represented by a map of derivation paths to the set of their
+outputs that were referenced by the context.
+
+TODO: What happens if the set is empty? Somebody claimed this means all outputs.
+
+`inputSrcs` is represented by a set of paths.
+
+These are populated by the above references as follows:
+
+* `SingleOutput` entries are merged into `inputDrvs`
+* `Path` entries are inserted into `inputSrcs`
+* `DrvClosure` leads to a special store computation (`computeFSClosure`), which
+  finds all paths referenced by the derivation and then inserts all of them into
+  the fields as above (derivations with _all_ their outputs)
+
+This is then serialised in the derivation and passed down the pipe.
+
+## Builtins interfacing with contexts
+
+C++ Nix has several builtins that interface directly with string contexts:
+
+* `unsafeDiscardStringContext`: throws away a string's string context (if
+  present)
+* `hasContext`: returns `true`/`false` depending on whether the string has
+  context
+* `unsafeDiscardOutputDependency`: drops dependencies on the *outputs* of a
+  `.drv` in the context, passing only the literal `.drv` itself
+
+  Note: This is only used for special test-cases in nixpkgs, and deprecated Nix
+  commands like `nix-push`.
+* `getContext`: returns the string context in serialised form as a Nix attribute
+  set
+* `appendContext`: adds a given string context to the string in the same format
+  as returned by `getContext`
+
+Most of the string manipulation operations will propagate the context to the
+result based on their parameters' contexts.
+
+## Placeholders
+
+C++ Nix has `builtins.placeholder`, which given the name of an output (e.g.
+`out`) creates a hashed string representation of that output name. If that
+string is used anywhere in input attributes, the builder will replace it with
+the actual name of the corresponding output of the current derivation.
+
+C++ Nix does not use contexts for this, it blindly creates a rewrite map of
+these placeholder strings to the names of all outputs, and runs the output
+replacement logic on all environment variables it creates, attribute files it
+passes etc.
+
+## Tvix & string contexts
+
+In the past, Tvix did not track string contexts in its evaluator at all, see
+the historical section for more information about that.
+
+Tvix tracks string contexts in every `NixString` structure via a
+`HashSet<BuildReference>` and offers an API to combine the references while
+keeping the exact internal structure of that data private.
+
+## Historical attempt: Persistent reference tracking
+
+We were investigating implementing a system which allows us to drop string
+contexts in favour of reference scanning derivation attributes.
+
+This means that instead of maintaining and passing around a string context data
+structure in eval, we maintain a data structure of *known paths* from the same
+evaluation elsewhere in Tvix, and scan each derivation attribute against this
+set of known paths when instantiating derivations.
+
+We believed we could take the stance that the system of string contexts as
+implemented in C++ Nix is likely an implementation detail that should not be
+leaking to the language surface as it does now.
+
+### Tracking "known paths"
+
+Every time a Tvix evaluation does something that causes a store interaction, a
+"known path" is created. On the language surface, this is the result of one of:
+
+1. Path literals (e.g. `src = ./.`).
+2. Calls to `builtins.derivationStrict` yielding a derivation and its output
+   paths.
+3. Calls to `builtins.path`.
+
+Whenever one of these occurs, some metadata that persists for the duration of
+one evaluation should be created in Nix. This metadata needs to be available in
+`builtins.derivationStrict`, and should be able to respond to these queries:
+
+1. What is the set of all known paths? (used for e.g. instantiating an
+   Aho-Corasick type string searcher)
+2. What is the _type_ of a path? (derivation path, derivation output, source
+   file)
+3. What are the outputs of a derivation?
+4. What is the derivation of an output?
+
+These queries will need to be asked of the metadata when populating the
+derivation fields.
+
+Note: Depending on how we implement `builtins.placeholder`, it might be useful
+to track created placeholders in this metadata, too.
+
+### Context builtins
+
+Context-reading builtins can be implemented in Tvix by adding `hasContext` and
+`getContext` with the appropriate reference-scanning logic. However, we should
+evaluate how these are used in nixpkgs and whether their uses can be removed.
+
+Context-mutating builtins can be implemented by tracking their effects in the
+value representation of Tvix, however we should consider not doing this at all.
+
+`unsafeDiscardOutputDependency` should probably never be used and we should warn
+or error on it.
+
+`unsafeDiscardStringContext` is often used as a workaround for avoiding IFD in
+inconvenient places (e.g. in the TVL depot pipeline generation). This is
+unnecessary in Tvix. We should evaluate which other uses exist, and act on them
+appropriately.
+
+The initial danger with diverging here is that we might cause derivation hash
+discrepancies between Tvix and C++ Nix, which can make initial comparisons of
+derivations generated by the two systems difficult. If this occurs we need to
+discuss how to approach it, but initially we will implement the mutating
+builtins as no-ops.
+
+### Why this did not work for us?
+
+Nix has a feature to perform environmental checks of your derivation, e.g.
+"these derivation outputs should not be referenced in this derivation", this was
+introduced in Nix 2.2 by
+https://github.com/NixOS/nix/commit/3cd15c5b1f5a8e6de87d5b7e8cc2f1326b420c88.
+
+Unfortunately, this feature introduced a very unfortunate and critical bug: all
+usage of this feature with contextful strings will actually force the
+derivation to depend at least at build time on those specific paths, see
+https://github.com/NixOS/nix/issues/4629.
+
+For example, if you wanted to `disallowedReferences` to a package and you used a
+derivation as a path, you would actually register that derivation as a input
+derivation of that derivation.
+
+This bug is still unfixed in Nix and it seems that fixing it would require
+introducing different ways to evaluate Nix derivations to preserve the
+output path calculation for Nix expressions so far.
+
+All of this would be fine if the bug behavior was uniform in the sense that no
+one tried to force-workaround it. Since Nixpkgs 23.05, due to
+https://github.com/NixOS/nixpkgs/pull/211783 this is not true anymore.
+
+If you let nixpkgs be the disjoint union of bootstrapping derivations $A$ and
+`stdenv.mkDerivation`-built derivations $B$.
+
+$A$ suffers from the bug and $B$ doesn't by the forced usage of
+`unsafeDiscardStringContext` on those special checking fields.
+
+This means that to build hash-compatible $A$ **and** $B$, we need to
+distinguish $A$ and $B$. A lot of hacks could be imagined to support this
+problem.
+
+Let's assume we have a solution to that problem, it means that we are able to
+detect implicitly when a set of specific fields are
+`unsafeDiscardStringContext`-ed.
+
+Thus, we could use that same trick to implement `unsafeDiscardStringContext`
+entirely for all fields actually.
+
+Now, to implement `unsafeDiscardStringContext` in the persistent reference
+tracking model, you will need to store a disallowed list of strings that should
+not trigger a reference when we are scanning a derivation parameters.
+
+But assume you have something like:
+
+```nix
+derivation {
+   buildInputs = [
+     stdenv.cc
+   ];
+
+   disallowedReferences = [ stdenv.cc ];
+}
+```
+
+If you unregister naively the `stdenv.cc` reference, it will silence the fact
+that it is part of the `buildInputs`, so you will observe that Nix will fail
+the derivation during environmental check, but Tvix would silently force remove
+that reference.
+
+Until proven otherwise, it seems highly difficult to have the fine-grained
+information to prevent reference tracking of those specific fields. It is not a
+failure of the persistent reference tracking, it is an unresolved critical bug
+of Nix that only nixpkgs really workarounded for `stdenv.mkDerivation`-based
+derivations.
diff --git a/tvix/eval/docs/builtins.md b/tvix/eval/docs/builtins.md
new file mode 100644
index 0000000000..dba4c48c65
--- /dev/null
+++ b/tvix/eval/docs/builtins.md
@@ -0,0 +1,138 @@
+Nix builtins
+============
+
+Nix has a lot of built-in functions, some of which are accessible in
+the global scope, and some of which are only accessible through the
+global `builtins` attribute set.
+
+This document is an attempt to track all of these builtins, but
+without documenting their functionality.
+
+See also https://nixos.org/manual/nix/stable/expressions/builtins.html
+
+The `impl` column indicates implementation status in tvix:
+- implemented: "" (empty cell)
+- not yet implemented, but not blocked: `todo`
+- not yet implemented, but blocked by other prerequisites:
+  - `store`: awaiting eval<->store api(s)
+  - `context`: awaiting support for string contexts
+
+| name                          | global | arity | pure  | impl    |
+|-------------------------------|--------|-------|-------|---------|
+| abort                         | true   | 1     |       |         |
+| add                           | false  | 2     | true  |         |
+| addErrorContext               | false  | ?     |       | context |
+| all                           | false  | 2     | true  |         |
+| any                           | false  | 2     | true  |         |
+| appendContext                 | false  | ?     |       |         |
+| attrNames                     | false  | 1     | true  |         |
+| attrValues                    | false  |       | true  |         |
+| baseNameOf                    | true   |       |       |         |
+| bitAnd                        | false  |       |       |         |
+| bitOr                         | false  |       |       |         |
+| bitXor                        | false  |       |       |         |
+| builtins                      | true   |       |       |         |
+| catAttrs                      | false  |       |       |         |
+| compareVersions               | false  |       |       |         |
+| concatLists                   | false  |       |       |         |
+| concatMap                     | false  |       |       |         |
+| concatStringsSep              | false  |       |       |         |
+| currentSystem                 | false  |       |       |         |
+| currentTime                   | false  |       | false |         |
+| deepSeq                       | false  |       |       |         |
+| derivation                    | true   |       |       | store   |
+| derivationStrict              | true   |       |       | store   |
+| dirOf                         | true   |       |       |         |
+| div                           | false  |       |       |         |
+| elem                          | false  |       |       |         |
+| elemAt                        | false  |       |       |         |
+| false                         | true   |       |       |         |
+| fetchGit                      | true   |       |       | store   |
+| fetchMercurial                | true   |       |       | store   |
+| fetchTarball                  | true   |       |       | store   |
+| fetchurl                      | false  |       |       | store   |
+| filter                        | false  |       |       |         |
+| filterSource                  | false  |       |       | store   |
+| findFile                      | false  |       | false | todo    |
+| foldl'                        | false  |       |       |         |
+| fromJSON                      | false  |       |       |         |
+| fromTOML                      | true   |       |       |         |
+| functionArgs                  | false  |       |       |         |
+| genList                       | false  |       |       |         |
+| genericClosure                | false  |       |       | todo    |
+| getAttr                       | false  |       |       |         |
+| getContext                    | false  |       |       |         |
+| getEnv                        | false  |       | false |         |
+| hasAttr                       | false  |       |       |         |
+| hasContext                    | false  |       |       |         |
+| hashFile                      | false  |       | false |         |
+| hashString                    | false  |       |       |         |
+| head                          | false  |       |       |         |
+| import                        | true   |       |       |         |
+| intersectAttrs                | false  |       |       |         |
+| isAttrs                       | false  |       |       |         |
+| isBool                        | false  |       |       |         |
+| isFloat                       | false  |       |       |         |
+| isFunction                    | false  |       |       |         |
+| isInt                         | false  |       |       |         |
+| isList                        | false  |       |       |         |
+| isNull                        | true   |       |       |         |
+| isPath                        | false  |       |       |         |
+| isString                      | false  |       |       |         |
+| langVersion                   | false  |       |       |         |
+| length                        | false  |       |       |         |
+| lessThan                      | false  |       |       |         |
+| listToAttrs                   | false  |       |       |         |
+| map                           | true   |       |       |         |
+| mapAttrs                      | false  |       |       |         |
+| match                         | false  |       |       |         |
+| mul                           | false  |       |       |         |
+| nixPath                       | false  |       |       | todo    |
+| nixVersion                    | false  |       |       | todo    |
+| null                          | true   |       |       |         |
+| parseDrvName                  | false  |       |       |         |
+| partition                     | false  |       |       |         |
+| path                          | false  |       | sometimes | store |
+| pathExists                    | false  |       | false |         |
+| placeholder                   | true   |       |       | context |
+| readDir                       | false  |       | false |         |
+| readFile                      | false  |       | false |         |
+| removeAttrs                   | true   |       |       |         |
+| replaceStrings                | false  |       |       |         |
+| scopedImport                  | true   |       |       |         |
+| seq                           | false  |       |       |         |
+| sort                          | false  |       |       |         |
+| split                         | false  |       |       |         |
+| splitVersion                  | false  |       |       |         |
+| storeDir                      | false  |       |       | store   |
+| storePath                     | false  |       |       | store   |
+| stringLength                  | false  |       |       |         |
+| sub                           | false  |       |       |         |
+| substring                     | false  |       |       |         |
+| tail                          | false  |       |       |         |
+| throw                         | true   |       |       |         |
+| toFile                        | false  |       |       | store   |
+| toJSON                        | false  |       |       |         |
+| toPath                        | false  |       |       |         |
+| toString                      | true   |       |       |         |
+| toXML                         | true   |       |       |         |
+| trace                         | false  |       |       |         |
+| true                          | true   |       |       |         |
+| tryEval                       | false  |       |       |         |
+| typeOf                        | false  |       |       |         |
+| unsafeDiscardOutputDependency | false  |       |       |         |
+| unsafeDiscardStringContext    | false  |       |       |         |
+| unsafeGetAttrPos              | false  |       |       | todo    |
+| valueSize                     | false  |       |       | todo    |
+
+## Added after C++ Nix 2.3 (without Flakes enabled)
+
+| name          | global | arity | pure  | impl  |
+|---------------|--------|-------|-------|-------|
+| break         | false  | 1     |       | todo  |
+| ceil          | false  | 1     | true  |       |
+| fetchTree     | true   | 1     |       | todo  |
+| floor         | false  | 1     | true  |       |
+| groupBy       | false  | 2     | true  |       |
+| traceVerbose  | false  | 2     |       | todo  |
+| zipAttrsWith  | false  | 2     | true  | todo  |
diff --git a/tvix/eval/docs/catchable-errors.md b/tvix/eval/docs/catchable-errors.md
new file mode 100644
index 0000000000..ce320a9217
--- /dev/null
+++ b/tvix/eval/docs/catchable-errors.md
@@ -0,0 +1,131 @@
+# (Possible) Implementation(s) of Catchable Errors for `builtins.tryEval`
+
+## Terminology
+
+Talking about β€œcatchable errors” in Nix in general is a bit precarious since
+there is no properly established terminology. Also, the existing terms are less
+than apt. The reason for this lies in the fact that catchable errors (or
+whatever you want to call them) don't properly _exist_ in the language: While
+Nix's `builtins.tryEval` is (originally) based on the C++ exception system,
+it specifically lacks the ability of such systems to have an exception _value_
+whilst handling it. Consequently, these errors don't have an obvious name
+as they never appear _in_ the Nix language. They just have to be named in the
+respective Nix implementation:
+
+- In C++ Nix the only term for such errors is `AssertionError` which is the
+  name of the (C++) exception used in the implementation internally. This
+  term isn't great, though, as `AssertionError`s can not only be generated
+  using `assert`, but also using `throw` and failed `NIX_PATH` resolutions.
+  Were this terminology to be used in documentation addressing Nix language
+  users, it would probably only serve confusion.
+
+- Tvix currently (as of r/7573) uses the term catchable errors. This term
+  relates to nothing in the language as such: Errors are not caught, we rather
+  try to evaluate an expression. Catching also sort of implies that a value
+  representation of the error is attainable (like in an exception system) which
+  is untrue.
+
+In light of this I (sterni) would like to suggest β€œtryable errors” as an
+alternative term going forward which isn't inaccurate and relates to terms
+already established by language internal naming.
+
+However, this document will continue using the term catchable error until the
+naming is adjusted in Tvix itself.
+
+## Implementation
+
+Below we discuss different implementation approaches in Tvix in order to arrive
+at a proposal for the new one. The historical discussion is intended as a basis
+for discussing the proposal: Are we committing to an old or current mistake? Are
+we solving all problems that cropped up or were solved at any given point in
+time?
+
+### Original
+
+The original implementation of `tryEval` in cl/6924 was quite straightforward:
+It would simply interrupt the propagation of a potential catchable error to the
+top level (which usually happened using the `?` operator) in the builtin and
+construct the appropriate representation of an unsuccessful evaluation if the
+error was deemed catchable. It had, however, multiple problems:
+
+- The VM was originally written without `tryEval` in mind, i.e. it largely
+  assumed that an error would always cause execution to be terminated. This
+  problem was later solved (cl/6940).
+- Thunks could not be `tryEval`-ed multiple times (b/281). This was another
+  consequence of VM architecture at the time: Thunks would be blackholed
+  before evaluation was started and the error could occur. Due to the
+  interaction of the generator-based VM code and `Value::force` the part
+  of the code altering the thunk state would never be informed about the
+  evaluation result in case of a failure, so the thunk would remain
+  blackholed leading to a crash if the same thunk was `tryEval`-ed or
+  forced again. To solve this issue, amjoseph completely overhauled
+  the implementation.
+
+One key point about this implementation is that it is based on the assumption
+that catchable errors can only be generated in thunks, i.e. expressions causing
+them are never evaluated strictly. This can be illustrated using C++ Nix:
+
+```console
+> nix-instantiate --eval -E '[ (assert false; true) (builtins.throw "") <nixpkgs> ]'
+[ <CODE> <CODE> <CODE> ]
+```
+
+If this wasn't the case, the VM could encounter the error in a situation where
+the error would not have needed to pass through the `tryEval` builtin, causing
+evaluation to abort.
+
+### Present
+
+The current system (mostly implemented in cl/9289) uses a very different
+approach: Instead of relying on the thunk boundary, catchable errors are no
+longer errors, but special values. They are created at the relevant points (e.g.
+`builtins.throw`) and propagated whenever they are encountered by VM ops or
+builtins. Finally, they either encounter `builtins.tryEval` (and are converted to
+an ordinary value again) or the top level where they become a normal error again.
+
+The problems with this mostly stem from the confusion between values and errors
+that it necessitates:
+
+- In most circumstances, catchable errors end up being errors again, as `tryEval`
+  is not used a lot. So `throw`s usually end up causing evaluation to abort.
+  Consequently, not only `Value::Catchable` is necessary, but also a corresponding
+  error variant that is _only_ created if a catchable value remains at the end of
+  evaluation. A requirement that was missed until cl/10991 (!) which illustrate
+  how strange that architecture is. A consequence of this is that catchable
+  errors have no location information at all.
+- `Value::Catchable` is similar to other internal values in Tvix, but is much
+  more problematic. Aside from thunks, internal values only exist for a brief
+  amount of time on the stack and it is very clear what parts of the VM or
+  builtins need to handle them. This means that the rest of the implementation
+  need to consider them, keeping the complexity caused by the internal value
+  low. `Value::Catchable`, on the other hand, may exist anywhere and be passed
+  to any VM op or builtin, so it needs to be correctly propagated _everywhere_.
+  This causes a lot of noise in the code as well as a big potential for bugs.
+  Essentially, catchable errors require as much attention by the Tvix developer
+  as laziness. This doesn't really correlate to the importance of the two
+  features to the Nix language.
+
+### Future?
+
+The core assumption of the original solution does offer a path forward: After
+cl/9289 we should be in a better position to introspect an error occurring from
+within the VM code, but we need a better way of storing such an error to prevent
+another b/281. If catchable errors can only be generated in thunks, we can just
+use the thunk representation for this. This would mean that `Thunk::force_`
+would need to check if evaluation was successful and (in case of failure)
+change the thunk representation
+
+- either to the original `ThunkRepr::Suspended` which would be simple, but of
+  course mean duplicated evaluation work in some expressions. In fact, this
+  would probably leave a lot of easy performance on the table for use cases we
+  would like to support, e.g. tree walkers for nixpkgs.
+- or to a new `ThunkRepr` variant that stores the kind of the error and all
+  necessary location info so stack traces can work properly. This of course
+  reintroduces some of the difficulty of having two kinds of errors, but it is
+  hopefully less problematic, as the thunk boundary (i.e. `Thunk::force`) is
+  where errors would usually occur.
+
+Besides the question whether this proposal can actually be implemented, another
+consideration is whether the underlying assumption will hold in the future, i.e.
+can we implement optimizations for thunk elimination in a way that thunks that
+generate catchable errors are never eliminated?
diff --git a/tvix/eval/docs/known-optimisation-potential.md b/tvix/eval/docs/known-optimisation-potential.md
new file mode 100644
index 0000000000..0ab185fe1b
--- /dev/null
+++ b/tvix/eval/docs/known-optimisation-potential.md
@@ -0,0 +1,162 @@
+Known Optimisation Potential
+============================
+
+There are several areas of the Tvix evaluator code base where
+potentially large performance gains can be achieved through
+optimisations that we are already aware of.
+
+The shape of most optimisations is that of moving more work into the
+compiler to simplify the runtime execution of Nix code. This leads, in
+some cases, to drastically higher complexity in both the compiler
+itself and in invariants that need to be guaranteed between the
+runtime and the compiler.
+
+For this reason, and because we lack the infrastructure to adequately
+track their impact (WIP), we have not yet implemented these
+optimisations, but note the most important ones here.
+
+* Use "open upvalues" [hard]
+
+  Right now, Tvix will immediately close over all upvalues that are
+  created and clone them into the `Closure::upvalues` array.
+
+  Instead of doing this, we can statically determine most locals that
+  are closed over *and escape their scope* (similar to how the
+  `compiler::scope::Scope` struct currently tracks whether locals are
+  used at all).
+
+  If we implement the machinery to track this, we can implement some
+  upvalues at runtime by simply sticking stack indices in the upvalue
+  array and only copy the values where we know that they escape.
+
+* Avoid `with` value duplication [easy]
+
+  If a `with` makes use of a local identifier in a scope that can not
+  close before the with (e.g. not across `LambdaCtx` boundaries), we
+  can avoid the allocation of the phantom value and duplication of the
+  `NixAttrs` value on the stack. In this case we simply push the stack
+  index of the known local.
+
+* Multiple attribute selection [medium]
+
+  An instruction could be introduced that avoids repeatedly pushing an
+  attribute set to/from the stack if multiple keys are being selected
+  from it. This occurs, for example, when inheriting from an attribute
+  set or when binding function formals.
+
+* Split closure/function representation [easy]
+
+  Functions have fewer fields that need to be populated at runtime and
+  can directly use the `value::function::Lambda` representation where
+  possible.
+
+* Apply `compiler::optimise_select` to other set operations [medium]
+
+  In addition to selects, statically known attribute resolution could
+  also be used for things like `?` or `with`. The latter might be a
+  little more complicated but is worth investigating.
+
+* Inline fully applied builtins with equivalent operators [medium]
+
+  Some `builtins` have equivalent operators, e.g. `builtins.sub`
+  corresponds to the `-` operator, `builtins.hasAttr` to the `?`
+  operator etc. These operators additionally compile to a primitive
+  VM opcode, so they should be just as cheap (if not cheaper) as
+  a builtin application.
+
+  In case the compiler encounters a fully applied builtin (i.e.
+  no currying is occurring) and the `builtins` global is unshadowed,
+  it could compile the equivalent operator bytecode instead: For
+  example, `builtins.sub 20 22` would be compiled as `20 - 22`.
+  This would ensure that equivalent `builtins` can also benefit
+  from special optimisations we may implement for certain operators
+  (in the absence of currying). E.g. we could optimise access
+  to the `builtins` attribute set which a call to
+  `builtins.getAttr "foo" builtins` should also profit from.
+
+* Avoid nested `VM::run` calls [hard]
+
+  Currently when encountering Nix-native callables (thunks, closures)
+  the VM's run loop will nest and return the value of the nested call
+  frame one level up. This makes the Rust call stack almost mirror the
+  Nix call stack, which is usually undesirable.
+
+  It is possible to detect situations where this is avoidable and
+  instead set up the VM in such a way that it continues and produces
+  the desired result in the same run loop, but this is kind of tricky
+  to get right - especially while other parts are still in flux.
+
+  For details consult the commit with Gerrit change ID
+  `I96828ab6a628136e0bac1bf03555faa4e6b74ece`, in which the initial
+  attempt at doing this was reverted.
+
+* Avoid thunks if only identifier closing is required [medium]
+
+  Some constructs, like `with`, mostly do not change runtime behaviour
+  if thunked. However, they are wrapped in thunks to ensure that
+  deferred identifiers are resolved correctly.
+
+  This can be avoided, as we statically analyse the scope and should
+  be able to tell whether any such logic was required.
+
+* Intern literals [easy]
+
+  Currently, the compiler emits a separate entry in the constant
+  table for each literal.  So the program `1 + 1 + 1` will have
+  three entries in its `Chunk::constants` instead of only one.
+
+* Do some list and attribute set operations in place [hard]
+
+  Algorithms that can not do a lot of work inside `builtins` like `map`,
+  `filter` or `foldl'` usually perform terribly if they use data structures like
+  lists and attribute sets.
+
+  `builtins` can do work in place on a copy of a `Value`, but naΓ―vely expressed
+  recursive algorithms will usually use `//` and `++` to do a single change to a
+  `Value` at a time, requiring a full copy of the data structure each time.
+  It would be a big improvement if we could do some of these operations in place
+  without requiring a new copy.
+
+  There are probably two approaches: We could determine statically if a value is
+  reachable from elsewhere and emit a special in place instruction if not. An
+  easier alternative is probably to rely on reference counting at runtime: If no
+  other reference to a value exists, we can extend the list or update the
+  attribute set in place.
+
+  An **alternative** to this is using [persistent data
+  structures](https://en.wikipedia.org/wiki/Persistent_data_structure) or at the
+  very least [immutable data structures](https://docs.rs/im/latest/im/) that can
+  be copied more efficiently than the stock structures we are using at the
+  moment.
+
+* Skip finalising unfinalised thunks or non-thunks instead of crashing [easy]
+
+  Currently `OpFinalise` crashes the VM if it is called on values that don't
+  need to be finalised. This helps catching miscompilations where `OpFinalise`
+  operates on the wrong `StackIdx`. In the case of function argument patterns,
+  however, this means extra VM stack and instruction overhead for dynamically
+  determining if finalisation is necessary or not. This wouldn't be necessary
+  if `OpFinalise` would just noop on any values that don't need to be finalised
+  (anymore).
+
+* Phantom binding for from expression of inherits [easy]
+
+  The from expression of an inherit is reevaluated for each inherit. This can
+  be demonstrated using the following Nix expression which, counter-intuitively,
+  will print β€œplonk” twice.
+
+  ```nix
+  let
+    inherit (builtins.trace "plonk" { a = null; b = null; }) a b;
+  in
+  builtins.seq a (builtins.seq b null)
+  ```
+
+  In most Nix code, the from expression is just an identifier, so it is not
+  terribly inefficient, but in some cases a more expensive expression may
+  be used. We should create a phantom binding for the from expression that
+  is reused in the inherits, so only a single thunk is created for the from
+  expression.
+
+  Since we discovered this, C++ Nix has implemented a similar optimization:
+  <https://github.com/NixOS/nix/pull/9847>.
diff --git a/tvix/eval/docs/language-issues.md b/tvix/eval/docs/language-issues.md
new file mode 100644
index 0000000000..152e6594a1
--- /dev/null
+++ b/tvix/eval/docs/language-issues.md
@@ -0,0 +1,46 @@
+# Nix language issues
+
+In the absence of a language standard, what Nix (the language) is, is prescribed
+by the behavior of the C++ Nix implementation. Still, there are reasons not to
+accept some behavior:
+
+* Tvix aims for nixpkgs compatibility only. This means we can ignore behavior in
+  edge cases nixpkgs doesn't trigger as well as obscure features it doesn't use
+  (e.g. `__overrides`).
+* Some behavior of the Nix evaluator seems to be unintentional or an
+  implementation detail leaking out into language behavior.
+
+Especially in the latter case, it makes sense to raise the respective issue and
+maybe to get rid of the behavior in all implementations for good. Below is an
+(incomplete) list of such issues:
+
+* [Behaviour of nested attribute sets depends on definition order][i7111]
+* [Partially constructed attribute sets are observable during dynamic attr names construction][i7012]
+* [Nix parsers merges multiple attribute set literals for the same key incorrectly depending on definition order][i7115]
+
+On the other hand, there is behavior that seems to violate one's expectation
+about the language at first, but has good enough reasons from an implementor's
+perspective to keep them:
+
+* Dynamic keys are forbidden in `let` and `inherit`. This makes sure that we
+  only need to do runtime identifier lookups for `with`. More dynamic (i.e.
+  runtime) lookups would make the scoping system even more complicated as well
+  as hurt performance.
+* Dynamic attributes of `rec` sets are not added to its scope. This makes sense
+  for the same reason.
+* Dynamic and nested attributes in attribute sets don't get merged. This is a
+  tricky one, but avoids doing runtime (recursive) merges of attribute sets.
+  Instead all necessary merging can be inferred statically, i.e. the C++ Nix
+  implementation already merges at parse time, making nested attribute keys
+  syntactic sugar effectively.
+
+Other behavior is just odd, surprising or underdocumented:
+
+* `builtins.foldl'` doesn't force the initial accumulator (but all other
+  intermediate accumulator values), differing from e.g. Haskell, see
+  the [relevant PR discussion][p7158].
+
+[i7111]: https://github.com/NixOS/nix/issues/7111
+[i7012]: https://github.com/NixOS/nix/issues/7012
+[i7115]: https://github.com/NixOS/nix/issues/7115
+[p7158]: https://github.com/NixOS/nix/pull/7158
diff --git a/tvix/eval/docs/opcodes-attrsets.md b/tvix/eval/docs/opcodes-attrsets.md
new file mode 100644
index 0000000000..7026f3319d
--- /dev/null
+++ b/tvix/eval/docs/opcodes-attrsets.md
@@ -0,0 +1,122 @@
+# attrset-opcodes
+
+The problem with attrset literals is twofold:
+
+1. The keys of attribute sets may be dynamically evaluated.
+
+   Access:
+
+   ```nix
+   let
+     k = "foo";
+     attrs = { /* etc. */ };
+   in attrs."${k}"
+   ```
+
+   Literal:
+   ```nix
+   let
+     k = "foo";
+   in {
+     "${k}" = 42;
+   }
+   ```
+
+   The problem with this is that the attribute set key is not known at
+   compile time, and needs to be dynamically evaluated by the VM as an
+   expression.
+
+   For the most part this should be pretty simple, assuming a
+   theoretical instruction set:
+
+   ```
+   0000  OP_CONSTANT(0) # key "foo"
+   0001  OP_CONSTANT(1) # value 42
+   0002  OP_ATTR_SET(1) # construct attrset from 2 stack values
+   ```
+
+   The operation pushing the key needs to be replaced with one that
+   leaves a single value (the key) on the stack, i.e. the code for the
+   expression, e.g.:
+
+   ```
+   0000..000n <operations leaving a string value on the stack>
+   000n+1     OP_CONSTANT(1) # value 42
+   000n+2     OP_ATTR_SET(1) # construct attrset from 2 stack values
+   ```
+
+   This is fairly easy to do by simply recursing in the compiler when
+   the key expression is encountered.
+
+2. The keys of attribute sets may be nested.
+
+   This is the non-trivial part of dealing with attribute set
+   literals. Specifically, the nesting can be arbitrarily deep and the
+   AST does not guarantee that related set keys are located
+   adjacently.
+
+   Furthermore, this frequently occurs in practice in Nix. We need a
+   bytecode representation that makes it possible to construct nested
+   attribute sets at runtime.
+
+   Proposal: AttrPath values
+
+   If we can leave a value representing an attribute path on the
+   stack, we can offload the construction of nested attribute sets to
+   the `OpAttrSet` operation.
+
+   Under the hood, OpAttrSet in practice constructs a `Map<NixString,
+   Value>` attribute set in most cases. This means it expects to pop
+   the value of the key of the stack, but is otherwise free to do
+   whatever it wants with the underlying map.
+
+   In a simple example, we could have code like this:
+
+   ```nix
+   {
+     a.b = 15;
+   }
+   ```
+
+   This would be compiled to a new `OpAttrPath` instruction that
+   constructs and pushes an attribute path from a given number of
+   fragments (which are popped off the stack).
+
+   For example,
+
+   ```
+   0000 OP_CONSTANT(0)  # key "a"
+   0001 OP_CONSTANT(1)  # key "b"
+   0002 OP_ATTR_PATH(2) # construct attrpath from 2 fragments
+   0003 OP_CONSTANT(2)  # value 42
+   0004 OP_ATTRS(1)     # construct attrset from one pair
+   ```
+
+   Right before `0004` the stack would be left like this:
+
+   [ AttrPath[a,b], 42 ]
+
+   Inside of the `OP_ATTRS` instruction we could then begin
+   construction of the map and insert the nested attribute sets as
+   required, as well as validate that there are no duplicate keys.
+
+3. Both of these cases can occur simultaneously, but this is not a
+   problem as the opcodes combine perfectly fine, e.g.:
+
+   ```nix
+   let
+     k = "a";
+   in {
+     "${k}".b = 42;
+   }
+   ```
+
+   results in
+
+   ```
+   0000..000n <operations leaving a string value on the stack>
+   000n+1     OP_CONSTANT(1)  # key "b"
+   000n+2     OP_ATTR_PATH(2) # construct attrpath from 2 fragments
+   000n+3     OP_CONSTANT(2)  # value 42
+   000n+4     OP_ATTR_SET(1)  # construct attrset from 2 stack values
+   ```
diff --git a/tvix/eval/docs/recursive-attrs.md b/tvix/eval/docs/recursive-attrs.md
new file mode 100644
index 0000000000..c30cfd33e6
--- /dev/null
+++ b/tvix/eval/docs/recursive-attrs.md
@@ -0,0 +1,68 @@
+Recursive attribute sets
+========================
+
+The construction behaviour of recursive attribute sets is very
+specific, and a bit peculiar.
+
+In essence, there are multiple "phases" of scoping that take place
+during attribute set construction:
+
+1. Every inherited value without an explicit source is inherited only
+   from the **outer** scope in which the attribute set is enclosed.
+
+2. A new scope is opened in which all recursive keys are evaluated.
+   This only considers **statically known keys**, attributes can
+   **not** recurse into dynamic keys in `self`!
+
+   For example, this code is invalid in C++ Nix:
+
+   ```
+   nix-repl> rec { ${"a"+""} = 2; b = a * 10; }
+   error: undefined variable 'a' at (string):1:26
+   ```
+
+3. Finally, a third scope is opened in which dynamic keys are
+   evaluated.
+
+This behaviour, while possibly a bit strange and unexpected, actually
+simplifies the implementation of recursive attribute sets in Tvix as
+well.
+
+Essentially, a recursive attribute set like this:
+
+```nix
+rec {
+  inherit a;
+  b = a * 10;
+  ${"c" + ""} = b * 2;
+}
+```
+
+Can be compiled like the following expression:
+
+```nix
+let
+  inherit a;
+in let
+  b = a * 10;
+  in {
+    inherit a b;
+    ${"c" + ""} = b * 2;
+  }
+```
+
+Completely deferring the resolution of recursive identifiers to the
+existing handling of recursive scopes (i.e. deferred access) in let
+bindings.
+
+In practice, we can further specialise this and compile each scope
+directly into the form expected by `OpAttrs` (that is, leaving
+attribute names on the stack) before each value's position.
+
+C++ Nix's Implementation
+------------------------
+
+* [`ExprAttrs`](https://github.com/NixOS/nix/blob/2097c30b08af19a9b42705fbc07463bea60dfb5b/src/libexpr/nixexpr.hh#L241-L268)
+  (AST representation of attribute sets)
+* [`ExprAttrs::eval`](https://github.com/NixOS/nix/blob/075bf6e5565aff9fba0ea02f3333c82adf4dccee/src/libexpr/eval.cc#L1333-L1414)
+* [`addAttr`](https://github.com/NixOS/nix/blob/master/src/libexpr/parser.y#L98-L156) (`ExprAttrs` construction in the parser)
diff --git a/tvix/eval/docs/vm-loop.md b/tvix/eval/docs/vm-loop.md
new file mode 100644
index 0000000000..6266d34709
--- /dev/null
+++ b/tvix/eval/docs/vm-loop.md
@@ -0,0 +1,315 @@
+tvix-eval VM loop
+=================
+
+This document describes the new tvix-eval VM execution loop implemented in the
+chain focusing around cl/8104.
+
+## Background
+
+The VM loop implemented in Tvix prior to cl/8104 had several functions:
+
+1. Advancing the instruction pointer for a chunk of Tvix bytecode and
+   executing instructions in a loop until a result was yielded.
+
+2. Tracking Nix call frames as functions/thunks were entered/exited.
+
+3. Catching trampoline requests returned from instructions to force suspended
+   thunks without increasing stack size *where possible*.
+
+4. Handling trampolines through an inner trampoline loop, switching between a
+   code execution mode and execution of subsequent trampolines.
+
+This implementation of the trampoline logic was added on to the existing VM,
+which previously always recursed for thunk forcing. There are some cases (for
+example values that need to be forced *inside* of the execution of a builtin)
+where trampolines could not previously be used, and the VM recursed anyways.
+
+As a result of this trampoline logic being added "on top" of the existing VM
+loop the code became quite difficult to understand. This led to several bugs,
+for example: b/251, b/246, b/245, and b/238.
+
+These bugs were tricky to deal with, as we had to try and make the VM do
+things that are somewhat difficult to fit into its model. We could of course
+keep extending the trampoline logic to accommodate all sorts of concepts (such
+as finalisers), but that seems like it does not solve the root problem.
+
+## New VM loop
+
+In cl/8104, a unified new solution is implemented with which the VM is capable
+of evaluating everything without increasing the call stack size.
+
+This is done by introducing a new frame stack in the VM, on which execution
+frames are enqueued that are either:
+
+1. A bytecode frame, consisting of Tvix bytecode that evaluates compiled Nix
+   code.
+2. A generator frame, consisting of some VM logic implemented in pure Rust
+   code that can be *suspended* when it hits a point where the VM would
+   previously need to recurse.
+
+We do this by making use of the `async` *keyword* in Rust, but notably
+*without* introducing asynchronous I/O or concurrency in tvix-eval (the
+complexity of which is currently undesirable for us).
+
+Specifically, when writing a Rust function that uses the `async` keyword, such
+as:
+
+```rust
+async fn some_builtin(input: Value) -> Result<Value, ErrorKind> {
+  let mut out = NixList::new();
+
+  for element in input.to_list()? {
+    let result = do_something_that_requires_the_vm(element).await;
+    out.push(result);
+  }
+
+  Ok(out)
+}
+```
+
+The compiler actually generates a state-machine under-the-hood which allows
+the execution of that function to be *suspended* whenever it hits an `await`.
+
+We use the [`genawaiter`][] crate that gives us a data structure and simple
+interface for getting instances of these state machines that can be stored in
+a struct (in our case, a *generator frame*).
+
+The execution of the VM then becomes the execution of an *outer loop*, which
+is responsible for selecting the next generator frame to execute, and two
+*inner loops*, which drive the execution of a bytecode frame or generator
+frame forward until it either yields a value or asks to be suspended in favour
+of another frame.
+
+All "communication" between frames happens solely through values left on the
+stack: Whenever a frame of either type runs to completion, it is expected to
+leave a *single* value on the stack. It follows that the whole VM, upon
+completion of the last (or initial, depending on your perspective) frame
+yields its result as the return value.
+
+The core of the VM restructuring is cl/8104, unfortunately one of the largest
+single commit changes we've had to make yet, as it touches pretty much all
+areas of tvix-eval. The introduction of the generators and the
+message/response system we built to request something from the VM, suspend a
+generator, and wait for the return is in cl/8148.
+
+The next sections describe in detail how the three different loops work.
+
+### Outer VM loop
+
+The outer VM loop is responsible for selecting the next frame to run, and
+dispatching it correctly to inner loops, as well as determining when to shut
+down the VM and return the final result.
+
+```
+                          ╭──────────────────β•
+                 ╭───────── match frame kind β”œβ”€β”€β”€β”€β”€β”€β•
+                 β”‚        ╰──────────────────╯      β”‚
+                 β”‚                                  β”‚
+    ┏━━━━━━━━━━━━┷━━━━━┓                ╭───────────┴───────────β•
+───►┃ frame_stack.pop()┃                β–Ό                       β–Ό
+    ┗━━━━━━━━━━━━━━━━━━┛       ┏━━━━━━━━━━━━━━━━┓      ┏━━━━━━━━━━━━━━━━━┓
+                 β–²             ┃ bytecode frame ┃      ┃ generator frame ┃
+                 β”‚             ┗━━━━━━━━┯━━━━━━━┛      ┗━━━━━━━━┯━━━━━━━━┛
+                 β”‚[yes, cont.]          β”‚                       β”‚
+                 β”‚                      β–Ό                       β–Ό
+    ┏━━━━━━━━┓   β”‚             ╔════════════════╗      ╔═════════════════╗
+◄───┨ return ┃   β”‚             β•‘ inner bytecode β•‘      β•‘ inner generator β•‘
+    ┗━━━━━━━━┛   β”‚             β•‘      loop      β•‘      β•‘      loop       β•‘
+        β–²        β”‚             β•šβ•β•β•β•β•β•β•β•β•€β•β•β•β•β•β•β•β•      β•šβ•β•β•β•β•β•β•β•β•€β•β•β•β•β•β•β•β•β•
+        β”‚   ╭────┴─────╠               β”‚                       β”‚
+        β”‚   β”‚ has next β”‚                ╰───────────┬───────────╯
+   [no] ╰────  frame?  β”‚                            β”‚
+            ╰────┬─────╯                            β–Ό
+                 β”‚                         ┏━━━━━━━━━━━━━━━━━┓
+                 β”‚                         ┃ frame completed ┃
+                 ╰─────────────────────────┨  or suspended   ┃
+                                           ┗━━━━━━━━━━━━━━━━━┛
+```
+
+Initially, the VM always pops a frame from the frame stack and then inspects
+the type of frame it found. As a consequence the next frame to execute is
+always the frame at the top of the stack, and setting up a VM initially for
+code execution is done by leaving a bytecode frame with the code to execute on
+the stack and passing control to the outer loop.
+
+Control is dispatched to either of the inner loops (depending on the type of
+frame) and the cycle continues once they return.
+
+When an inner loop returns, it has either finished its execution (and left its
+result value on the *value stack*), or its frame has requested to be
+suspended.
+
+Frames request suspension by re-enqueueing *themselves* through VM helper
+methods, and then leaving the frame they want to run *on top* of themselves in
+the frame stack before yielding control back to the outer loop.
+
+The inner control loops inform the outer loops about whether the frame has
+been *completed* or *suspended* by returning a boolean.
+
+### Inner bytecode loop
+
+The inner bytecode loop drives the execution of some Tvix bytecode by
+continously looking at the next instruction to execute, and dispatching to the
+instruction handler.
+
+```
+   ┏━━━━━━━━━━━━━┓
+◄──┨ return true ┃
+   ┗━━━━━━━━━━━━━┛
+          β–²
+     ╔════╧═════╗
+     β•‘ OpReturn β•‘
+     β•šβ•β•β•β•β•β•β•β•β•β•β•
+          β–²
+          ╰──┬────────────────────────────β•
+             β”‚                            β–Ό
+             β”‚                 ╔═════════════════════╗
+    ┏━━━━━━━━┷━━━━━┓           β•‘ execute instruction β•‘
+───►┃ inspect next ┃           β•šβ•β•β•β•β•β•β•β•β•β•β•€β•β•β•β•β•β•β•β•β•β•β•
+    ┃  instruction ┃                      β”‚
+    ┗━━━━━━━━━━━━━━┛                      β”‚
+             β–²                      ╭─────┴─────β•
+             ╰─────────────────────── suspends? β”‚
+                       [no]         ╰─────┬─────╯
+                                          β”‚
+                                          β”‚
+   ┏━━━━━━━━━━━━━━┓                       β”‚
+◄──┨ return false ┃───────────────────────╯
+   ┗━━━━━━━━━━━━━━┛              [yes]
+```
+
+With this refactoring, the compiler now emits a special `OpReturn` instruction
+at the end of bytecode chunks. This is a signal to the runtime that the chunk
+has completed and that its current value should be returned, without having to
+perform instruction pointer arithmetic.
+
+When `OpReturn` is encountered, the inner bytecode loop returns control to the
+outer loop and informs it (by returning `true`) that the bytecode frame has
+completed.
+
+Any other instruction may also request a suspension of the bytecode frame (for
+example, instructions that need to force a value). In this case the inner loop
+is responsible for setting up the frame stack correctly, and returning `false`
+to inform the outer loop of the suspension
+
+### Inner generator loop
+
+The inner generator loop is responsible for driving the execution of a
+generator frame by continously calling [`Gen::resume`][] until it requests a
+suspension (as a result of which control is returned to the outer loop), or
+until the generator is done and yields a value.
+
+```
+   ┏━━━━━━━━━━━━━┓
+◄──┨ return true ┃ ◄───────────────────β•
+   ┗━━━━━━━━━━━━━┛                     β”‚
+                                       β”‚
+                               [Done]  β”‚
+                    ╭──────────────────┴─────────β•
+                    β”‚ inspect generator response │◄────────────β•
+                    ╰──────────────────┬─────────╯             β”‚
+                            [yielded]  β”‚              ┏━━━━━━━━┷━━━━━━━━┓
+                                       β”‚              ┃ gen.resume(msg) ┃◄──
+                                       β–Ό              ┗━━━━━━━━━━━━━━━━━┛
+                                 ╭────────────╠               β–²
+                                 β”‚ same-frame β”‚                β”‚
+                                 β”‚  request?  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•―
+                                 ╰─────┬──────╯      [yes]
+   ┏━━━━━━━━━━━━━━┓                    β”‚
+◄──┨ return false ┃ ◄──────────────────╯
+   ┗━━━━━━━━━━━━━━┛                [no]
+```
+
+On each execution of a generator frame, `resume_with` is called with a
+[`VMResponse`][] (i.e. a message *from* the VM *to* the generator). For a newly
+created generator, the initial message is just `Empty`.
+
+A generator may then respond by signaling that it has finished execution
+(`Done`), in which case the inner generator loop returns control to the outer
+loop and informs it that this generator is done (by returning `true`).
+
+A generator may also respond by signaling that it needs some data from the VM.
+This is implemented through a request-response pattern, in which the generator
+returns a `Yielded` message containing a [`VMRequest`][]. These requests can be
+very simple ("Tell me the current store path") or more complex ("Call this Nix
+function with these values").
+
+Requests are divided into two classes: Same-frame requests (requests that can be
+responded to *without* returning control to the outer loop, i.e. without
+executing a *different* frame), and multi-frame generator requests. Based on the
+type of request, the inner generator loop will either handle it right away and
+send the response in a new `resume_with` call, or return `false` to the outer
+generator loop after setting up the frame stack.
+
+Most of this logic is implemented in cl/8148.
+
+[`Gen::resume`]: https://docs.rs/genawaiter/0.99.1/genawaiter/rc/struct.Gen.html#method.resume_with
+[`VMRequest`]: https://cs.tvl.fyi/depot@2696839770c1ccb62929ff2575a633c07f5c9593/-/blob/tvix/eval/src/vm/generators.rs?L44
+[`VMResponse`]: https://cs.tvl.fyi/depot@2696839770c1ccb62929ff2575a633c07f5c9593/-/blob/tvix/eval/src/vm/generators.rs?L169
+
+## Advantages & Disadvantages of the approach
+
+This approach has several advantages:
+
+* The execution model is much simpler than before, making it fairly
+  straightforward to build up a mental model of what the VM does.
+
+* All "out of band requests" inside the VM are handled through the same
+  abstraction (generators).
+
+* Implementation is not difficult, albeit a little verbose in some cases (we
+  can argue about whether or not to introduce macros for simplifying it).
+
+* Several parts of the VM execution are now much easier to document,
+  potentially letting us onboard tvix-eval contributors faster.
+
+* The linear VM execution itself is much easier to trace now, with for example
+  the `RuntimeObserver` (and by extension `tvixbolt`) giving much clearer
+  output now.
+
+But it also comes with some disadvantages:
+
+* Even though we "only" use the `async` keyword without a full async-I/O
+  runtime, we still encounter many of the drawbacks of the fragmented Rust
+  async ecosystem.
+
+  The biggest issue with this is that parts of the standard library become
+  unavailable to us, for example the built-in `Vec::sort_by` can no longer be
+  used for sorting in Nix because our comparators themselves are `async`.
+
+  This led us to having to implement some logic on our own, as the design of
+  `async` in Rust even makes it difficult to provide usecase-generic
+  implementations of concepts like sorting.
+
+* We need to allocate quite a few new structures on the heap in order to drive
+  generators, as generators involve storing `Future` types (with unknown
+  sizes) inside of structs.
+
+  In initial testing this seems to make no significant difference in
+  performance (our performance in an actual nixpkgs-eval is still bottlenecked
+  by I/O concerns and reference scanning), but is something to keep in mind
+  later on when we start optimising more after the low-hanging fruits have
+  been reaped.
+
+## Alternatives considered
+
+1. Tacking on more functionality onto the existing VM loop
+   implementation to accomodate problems as they show up. This is not
+   preferred as the code is already getting messy.
+
+2. Making tvix-eval a fully `async` project, pulling in something like Tokio
+   or `async-std` as a runtime. This is not preferred due to the massively
+   increased complexity of those solutions, and all the known issues of fully
+   buying in to the async ecosystem.
+
+   tvix-eval fundamentally should work for use-cases besides building Nix
+   packages (e.g. for `//tvix/serde`), and its profile should be as slim as
+   possible.
+
+3. Convincing the Rust developers that Rust needs a way to guarantee
+   constant-stack-depth tail calls through something like a `tailcall`
+   keyword.
+
+4. ... ?
+
+[`genawaiter`]: https://docs.rs/genawaiter/
diff --git a/tvix/eval/proptest-regressions/value/mod.txt b/tvix/eval/proptest-regressions/value/mod.txt
new file mode 100644
index 0000000000..05b01b4c76
--- /dev/null
+++ b/tvix/eval/proptest-regressions/value/mod.txt
@@ -0,0 +1,10 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc 241ec68db9f684f4280d4c7907f7105e7b746df433fbb5cbd6bf45323a7f3be0 # shrinks to input = _ReflexiveArgs { x: List(NixList([List(NixList([Path("𑁯")]))])) }
+cc b6ab5fb25f5280f39d2372e951544d8cc9e3fcd5da83351266a0a01161e12dd7 # shrinks to input = _ReflexiveArgs { x: Attrs(NixAttrs(KV { name: Path("𐎸-{\u{a81}lq9Z"), value: Bool(false) })) }
+cc 3656053e7a8dbe1c01dd68a8e06840fb6e693dde942717a7c18173876d9c2cce # shrinks to input = _SymmetricArgs { x: List(NixList([Path("\u{1daa2}:.H💞୑\\πŸ•΄7iuπ‹T𝓕\\%i:"), Integer(-7435423970896550032), Float(1.2123587650724335e-5), Integer(5314432620816586712), Integer(-8316092768376026052), Integer(-7632521684027819842), String(NixString(Smol("ᰏα₯€\\\\Θ·f8=\u{2003}\"π‘’ο­ˆA/%οΏ½bQffl<ΰ±―ΘΊ\u{1b3a}`T{"))), Bool(true), String(NixString(Smol("Ⱥힳ\"<\\`tZ/�ࡘ💦Ⱥ=x𑇬"))), Null, Bool(false), String(NixString(Smol(";%'࿎.ΰ³ŠπŸ‰‘π‘ŒŠ/πŸ•΄3Ja"))), Null, String(NixString(Smol("vNᛦ=\\`𝐓P\\"))), Bool(true), Null, String(NixString(Smol("\"zΰͺΎΓ«\u{11cb6}6%yκŸ½πšΏΎπŸ‘–`!"))), Integer(1513983844724992869), Bool(true), Float(-8.036903674864022e114), Path("G࿐�/α †β‚«%πˆ‡P"), Bool(false), Bool(true), Null, Attrs(NixAttrs(Empty)), Float(-6.394835856260315e-46), Null, Path("G?πŸ­™O<🟰π­π‘€³οΏ½*ά₯πžΉ‰`?$/j1=pπ‘™•h\u{e0147}\u{1cf3b}"), Bool(false), Bool(false), Path("$"), Float(7.76801238087078e-309), Integer(-4304837936532390878), Attrs(NixAttrs(Im({NixString(Smol("")): Float(-1.5117468628684961e-307), NixString(Smol("#=B\"o~Ρ¨\"Ѩ𐠅T")): String(NixString(Smol("Kΰ¦…&NVκ©‹ΰ ”'𝄏<"))), NixString(Heap("&Β₯sଲ\"\\=π‘–ΊQ𚿾VTຐ[ﬓ%")): Integer(-9079075359788064855), NixString(Smol("'``{:5π‘šž=lπ‘£Ώ")): Bool(true), NixString(Smol("*π–š")): Bool(false), NixString(Heap(":*𐖛𑡨%C'Ρ¨")): Null, NixString(Smol("?Υ/ΰ·΄")): Bool(true), NixString(Smol("Wπ‘Œ")): Float(-1.606122565666547e-309), NixString(Smol("`𐺭Β₯\u{9d7}π–Ώ‘1Yπ–€›>")): Float(0.0), NixString(Heap("{&]\\πž…‚πž……&7ΰ¬Έ")): Path("\"*o$ΰ¬‡πŸ›’πžΈ€οΏ½πŸ‰οΏ½πŸƒ?##bο·…|aΒ₯ΰΏ”α“Š\u{bd7}𑆍W?Pπ‘ŠŒπ‘©°"), NixString(Smol("ΘΊ\u{10a3a}a/πŸ•΄'\u{b3e}𐦨Sᅩ$1kw\u{cd5}B)\u{e01b7}1:R@")): Path("-*κ£’=#\\πŸ„£ο­˜π‘΄ƒ\\α€–%3\u{1e00e}{YബL\'GE<|aΘΊ:\u{1daa8}π―{ಯ𝑠\\"), NixString(Smol("מ'%:")): Integer(-5866590459020557377), NixString(Heap("ΰ­ͺ3\u{1a79}-l\u{bd7}Ξ<ন<")): Float(1.4001769654461214e-61), NixString(Heap("ΰ™πžΉ”π‘Š")): Path("*%q<%=LU.TჍw𭴟[Ρ¨π‘Œ­πžˆ?Ì?X%ο¬ΎΒ₯πž²₯πžΉ’ΰ±š"), NixString(Heap("ΰŸΡ¨ο¬Ύ&'πŒΊπ«ˆ”.=\u{5af}\u{10a39}β‚Ό\\G")): String(NixString(Smol("β‚™.\u{10f83}<x/Ⱥ𝼐$=𑡧{jE𞹺/f*𐒯ഒ𖿣﹂:ᨧ𐞴Β₯`%"))), NixString(Smol("𐠈𐠁`\u{b57}κ›ͺ`@$y")): Float(1.2295541964458574e-308), NixString(Smol("π«`:𐠂$4%\u{ac5}π•―πŸ•΄")): String(NixString(Heap("='/𑍐<πŸ•΄\"=Iα­»9π™\u{1e01b}"))), NixString(Heap("π‘Œ({?kG:οΊ‘π‘Œ΅0π’šq\"ঐ")): String(NixString(Heap(":ਕ"))), NixString(Heap("π‘ŒπžΈ‘?π‘™“,?πŸ©’7\":'..'<𐀏𑓒Ეdπ‘ŠˆαΏ π–½™3'&πŸ€ͺπž…")): Path("."), NixString(Smol("π‘’¬π‘΅‘?Kα²ΎWΘΊ`οΏ”0{<ኘzEΓƒ\"û𝀕$πŸž€π‹―οΏ½.\"𐕾\u{1daad}γˆƒ\\𐀿ʊs")): Integer(2276487328720525493), NixString(Heap("𝔾\u{11c9f}<Ξ”]Tπž‘πž΅πžΉ‰οΏ½")): Bool(false), NixString(Heap("πŸ•΄ΘΊ\u{f82}/%*𛅐᠐Ѩ$πŸ’• *οΏ½)𝓁𑃀𝒦𐝂â𑀕Ѩh")): Path("πŸ•΄")}))), String(NixString(Smol("&m𝈢%&+\u{ecd}:&Β₯\u{11d3d}Β°%'.πžΉ™2\u{10eac}-࢈\u{11369}𞹰Β₯π’‘΄'xѨ𞹝.π‘Ό–V"))), Path(""), Path("π‘«’%J\u{11ca3}"), Integer(5535573838430566518), String(NixString(Heap("βΏ΅π›…πŸ€°Z$,\\/v\\⏇\u{a02}ΰ³π•?\u{11ef4}%&/οΏ½|\"<ΘΊ&cUΓ›αΌœα₯£π Όπ˜΄„"))), Float(-1.0028921870468647e243), Path(".j*π‘£±ΓœMπ‘ˆ…I?MvZy:π„‚οΏ½πžΉ‹%?Ⴧ%"), Null, String(NixString(Smol("νŸ˜πŸ•΄π’΄π’‹$\u{afd}ΰ¨ƒΓœΡ¨`\u{11d44}E\\;\"?𬛕$"))), String(NixString(Heap("/)!.PπŸ‡ͺΰ¨Ύ'β𐰣א=tៜβm:\u{1773}\"Ⴧsα‰ͺ+HNk"))), Float(-3.7874900882792316e-77), String(NixString(Heap("ΘΊ<𐺰L:πŸ­π‘†πž…€Ρ¨π‘¬„a.m𐀼V\"π‹ŠA𝄀\u{1e131}﹝"))), Path("aᨩ�?"), Float(6.493959567338054e87), Null, Null, Float(1.13707995848205e-115), Integer(-4231403127163468251), Float(-0.0), Float(-1.1096242386070431e-45), Integer(-5080222544395825040), Integer(2218353666908906569), Bool(false), Bool(true), Null, Float(-334324631469448.56), String(NixString(Heap("j%Ρ¨Γ‘Ρ¨ΰ¨­\"᠖𐔅𛲂"))), Null, Float(5.823165830825334e-224), Path("&πžΉ‹π–­–:$\\π‘‚»&ኊ(πžΉ‹LH{κŸ“@=\\nΰͺ²&lyໃd"), String(NixString(Smol(""))), Bool(false), Float(1.0892031562808535e81), Null, Integer(-3110788464663743166), Bool(false), Null, Null])), y: List(NixList([Integer(7749573686807634185), Float(0.0), Attrs(NixAttrs(Im({NixString(Heap("")): Null, NixString(Smol("*<Y")): Bool(true), NixString(Heap("*\u{eb5}*:κ’ΆΡ¨&mπŸ•΄πŸ›«:_\u{ecb}1$pk!\u{1183a}b*:")): String(NixString(Heap("πŸ•΄πž»°π›ƒπžΉ›π±¬’ΘΊ=*]\u{1bad}t\u{11d40}ΰΆ‚πž“€κ§‘\"ΘΊ\"\u{9d7}𬇨$/Γ›\"zz*ఏ"))), NixString(Smol("?�𐄀&TbY&<'ᾍ?βΆ±ε·₯=%Β₯Ρ¨:<Ρ¨")): Null, NixString(Smol("Yπ£΅πŸ’…$Γ¨π•Šf&5κ¬’NπŸ•΄πŸ‰z�𐺰Mꕁ")): Integer(7378053691404749008), NixString(Heap("]\u{1712}&πŸ•΄{π›²—ΰ°­")): Path("`κ¬ͺα‹ΘΊΡ¨πžΉ΄:ΘΊκ•Žwpπ–©‚αƒ‡π‘œΆΰ¦²2/?Β₯\"DLΒ₯?οΏ½\'πžΉ‡π›…’p=ΘΊ"), NixString(Heap("`𝋀\"m\\.π ΌπŸ•΄π–ΊŠ?")): Null, NixString(Heap("\u{a71}ΰ‘·6lꙧ{οΏ½ΰ©`&οΏ½GןQ$(")): String(NixString(Heap("f\"<\"X!"))), NixString(Heap("αͺ cκͺ™\\ΰ¨²ΰ πŸ•΄?ΘΊ\u{11c97}<πŸ•΄Β«Γ™\u{10eff}𐒣:<Γ­N%y\\π™κ©…R\"=e𐺭")): Float(0.0), NixString(Smol("π™MჍ:%ΰŠ\u{dd6}$Γ™?,)Z/π‘Œ‰?Qo?=\u{b01}cΘΊ,\\*")): Integer(-750568219128686012), NixString(Smol("π–«΅π‘Ά¨ΰ²­\u{10a06}\\C_=πŸ•΄\u{10eac}೨喝W]\u{fb3}ΘΊ8")): Path("vLeড়ys..Ρ¨οΏ½π‘ˆ“!ଳ&y<ΰ»‚Β§{EUβ΄­κ©—U*\u{1e132}"), NixString(Smol("πž’€TAC\\*2πŸ•΄>%Ρ¨π‘©P?G\u{a0}fπ—»*vΒ₯Uq\u{b62}")): Float(3.882048884582667e95), NixString(Smol("𞸱𐏍2$ml.?.*U𫊴κ­ͺ<~gへ𑾰")): Null, NixString(Smol("πŸ•΄%`α­„N{3?kπ‘“—%:/Dπ‘€˜οΏ›P^9=Z៦")): Null, NixString(Smol("𫘦*ΰΊ’'7οΉ KΡ¨jπ”Šq\u{bc0}bΰͺ²π€Ώ%πŸ•΄οΏ½πž ³οΏ½L&Β₯")): Null}))), Path("A\u{a3c}lαͺ…q.ΘΊA&"), Integer(4907301083808036161), Bool(false), Null, Bool(true), Attrs(NixAttrs(Empty)), Bool(false), String(NixString(Smol("α§ΉH$Tΰ΅§πž…=πžΉŸπŸ•΄{[y"))), Path("\u{1da43}$οΏ½uῚÈΒ₯οΏ½Β₯\'\u{b82}ΰΆ½\u{11d3a}ΘΊ:πŸ†¨`𝍨1%`=ꝡ\u{11d41}\\X:*ΰΎˆαŠ‹πžΈ―"), Integer(-8507829082635299848), Integer(-3606086848261110558), Float(2.2176784412249313e-278), Bool(true), Float(-1.1853240079167073e253), Path("=𐠕ߕ*πŸ•΄ΘΊΘΊ:ΘΊ%LπŸ’‡Γ”\\`α½™%πŸ’–α‹€ ΰ΅Ώ\\πŸ•΄Iቫ=~;\"ȺΌ"), Null, Attrs(NixAttrs(Im({NixString(Heap("")): Float(-7.81298889375788e-309), NixString(Smol("%>=κ¨œα‡²ΰΊπ–­œ\u{1a60}Q(/X㈌\"*{γ„Ÿ`Β¨=&'")): Bool(true), NixString(Heap("%n𛄲Y'𞹧𞺧")): Bool(true), NixString(Smol("&/.ΘΊVοΏ½οΈ’\u{afb}'πžΉ”~\\Oᬽ�")): Float(8.59582436625354e-309), NixString(Heap("&y")): Path(".<\'κ’Ώ.W&Β₯οΏ½"), NixString(Smol("'1\u{11d91}Β₯O-𑨩ȺrὝΒ₯:Wෳቍqπ‘ΌŒ@^π‘Šœ?")): Bool(true), NixString(Smol("*Eπ‘–¬κ¦ŠΒΉπ›…€ΰ³‘/'𝐔jπ–πžΈΉ7)γ…š")): Integer(3181119108337466410), NixString(Smol("*rΒ₯[.Βͺ\u{10a3a}\u{1e132}EBβ΅°πžΉ—")): String(NixString(Heap("{πŸŸ°π–«§πŸ’š/𐍰'𞸁ࡻπŸƒͺ�𛲃?s&2𑴉"))), NixString(Smol("*\u{1e08f}SΓΌπŸ•΄N'EΖͺ")): Bool(false), NixString(Smol("/=`ΘΊ")): Float(-1.8155608059955555e-303), NixString(Heap("/ΰ©RE=L,/*\"'π‘ŠΏ=οΏ½+οΏ½πŸ‘¨α’’π–ΰ¨Ήπ‘±ŸΡ¨k᧧\"\"R")): Integer(-1311391009295683341), NixString(Heap("?%$'<<-23αͺ•^ቝȺj\\𐴆")): Null, NixString(Heap("?&?⑁ಡ\u{bd7}π‘“˜\u{112e9}ᝑl9p`")): Integer(-5524232499716374878), NixString(Smol("?Χ΄{j\"8ΰ’ΏπžΉ”")): Integer(1965352054196388057), NixString(Smol("@π¬–—{0.:?")): Null, NixString(Heap("C\"$π–‚\"/𐬗IyοΏΌU_π‘΄ˆ/N=\u{ac7}𐬉:ꬁ<κŸ“<.えG𖦐I/α €")): Float(-2.6565358047869407e-299), NixString(Heap("G`🟑y𖾝𐣴`#+'<")): Float(-1.643412216185574e-73), NixString(Smol("O𞹀៷.?π’‘²")): Integer(8639041881884941660), NixString(Smol("R<Xπš’Β₯=ጚ,.ဦ/?{\u{b57}꯴ೝ/Β₯mπŸ•΄αƒžπŸŸ°fπ’‘±X_.")): Null, NixString(Heap("Rο€—ΰͺοΏ½$f")): Null, NixString(Heap("ZbΘΊα½–")): Integer(300189569873494072), NixString(Smol("\\9rπ’’π©ˆΒ₯Ð𐼚\\?Ρ¨{$")): Integer(-5531416284385072043), NixString(Smol("`j$�ⓧ\u{16b34}'β·†t\\𞠏|πŸ’’'%&𑂕𐖼/$\u{ac7}")): String(NixString(Heap("1=Β‘<πž΅αŸ‘πŸ’œΓβ„§p\\4𐨗𐀿=ශ`.[<\u{dd6}."))), NixString(Smol("`α₯³")): Bool(true), NixString(Heap("`𐨕``α‰“π‘œΏ$*Dα€±`:/}πŸ•΄N'π˜…Ίΰ΅Ž7")): Path("/"), NixString(Heap("fΰ·³\\ßϐ*𞸭'%𑀁$jΰͺΎ=:Ρ¨t{\"0ί’/ΰ²π†πžΉ›iπ—Ή±'πŸ•΄")): Bool(false), NixString(Smol("n6&<𞟀'JBΒ¦x🩀vα‰š\u{1e008}Ρ¨Β₯𐖛jπŸ•΄bΒ₯𐼙'\u{c00}a")): Path("\u{110c2}p:Ρ¨\u{1a58}βΆ°O<?πžΈ‘π–™"), NixString(Smol("ntU𯀑�^πŸͺ«'%&ΰ¨°/")): Bool(false), NixString(Smol("o")): Path("Β₯οΏ½\'/Γ¬RVπ„š"), NixString(Smol("t\u{2df0}b.π ˆΒΉπ°ƒ¦*π–­ž:πŸ»π›…€7πž’Όπ‘Š©XL\\ਐ.N")): Null, NixString(Heap("yg'Β«πŸ‘“")): Path("/`]𞻰`\u{1cf0c}Γ‹\u{c55}<bΘΊοΏ½!"), NixString(Heap("yπŸ•΄π‘€‘/ΰΆ½$💱\\~\u{aaec}`ΰΊ„")): String(NixString(Smol(":bπŸ­‘π‘…Ÿΰ±˜ΰΆ­'οΏ½:hiⷊ*{*/κ™Ÿ"))), NixString(Smol("{?^nπ‘΄‰πŸ©»αŸ΅o<ΰ²z-λ—¨")): Bool(false), NixString(Smol("Γ°ΰ­­ΰ£?π‘…’\\<<%?<=$[<dπ‘‹Ά\\w𐖔u<")): Attrs(NixAttrs(KV { name: Path("ΰ¬›πŸ•΄R`𱑍=\u{2028}?Β€π”˜οΏΌαΏw41A𑃰π‘₯™<&:/"), value: Integer(-4007996343681736077) })), NixString(Smol("ΓΉ5 \u{c4a}α")): List(NixList([Integer(3829726756090354238), Bool(false), Integer(-7605748774039015772), Integer(-2904585304126015516), Float(1.668751782763388e125), Path("αŒ“π‘Χ΄π‘–$~ΰ·Β₯"), Float(0.00010226480079820994), String(NixString(Heap("yΒ₯C\u{c62}3"))), Integer(4954265069162436553), String(NixString(Heap("f𝔗/^%}₧Ѩαͺˆ\u{aa43}$π†”π˜΄„π‘€€N\u{c47}π‘ƒΉπ‘§„βΏΉπžΉ»"))), Bool(false), Float(7.883608538511117e36), Path("*ΰͺ·ΰΆ‰"), Integer(8893667840985960833), Null, String(NixString(Heap("Ρ¨|f2\u{11300}ΘΊ\u{11374}πŸ•΄ΰΏ˜\\e$ᒊRπ‘ŒΰΉ”αƒ‡$οΏ½'`\u{1e028}πŸ•΄"))), Path("<Uc?πžΉ‘\"πŸ•΄α₯³:/ꬍ=κ«\\:ο¬Ί&&jq\u{11d41}<_οΏ½%(῝Ⱥ�"), Float(-3.7947462255873233e189), Integer(1963485019377075037), Null, Integer(2642884952152033378), String(NixString(Heap("=\\Ρ¨qদ)%@οΏ½NHπ‘Ό„β΄­.α‹€*ΘΊ$&\u{d01}`ΰ¦­I𑩧h\u{1da9e}vπ‘€°/wl"))), Float(-3.1057935562909707e-153), Path("ΓŠΘΊπ¬—$?\'$α‹€%J`_𞹀"), String(NixString(Smol(":`'ຣ𐅔':π‘΄―\"R&r2h5\\οΏ½\\ਲ�<\u{11c3c}Β₯{Ⴭ!\":𝕆<*"))), Path("`%οΏ½ΘΊΰ ½Β₯3ΘΊ?r&οΏ½πŸ•΄n<π­°ΘΊΘΊα›ž$ΘΊ\u{a41}$ο­±ΰΆ΄%\u{1b6b}.π–Ώ°πŸ›±?d"), String(NixString(Smol("ΰΆΈπ‘Œ°d𞹺B𞹩&𑣄$ꬃO(ಝ{οΏ½/Β₯"))), Null, Path(""), Null, Integer(-5596806430051718833), String(NixString(Heap("Ρ¨.<\\?π‘΄ Β₯<ΰͺ=~𯀉𑀉`v\\Hf\u{ac5}LαΎ‘&.πž₯ŸπŸ•΄5A\\'Β₯"))), Integer(-4563843222966965028), Integer(-1260016740228553697), Path("π‘ΌŠW𐄑ຄ<u\u{11357}e"), Float(-1.4738886746660203e-287), Float(-2.1710863308744702e271), Integer(-4463138123798208283), Null, Integer(7334938770111854006), String(NixString(Smol("&<\\π–ΊŽCKΰ½›="))), Null, Float(3.6654773986616826e238), Path("Β₯\"=ᝯ𐒭$ΰ{.𐒫8ujx"), Bool(true), Path("𐩼ᨅ�\\ΰ}oΡ¨π–ŒY$οΏ½/z𑇧/`%ΒΌπ–­˜π‘ƒ¦:α₯²$-"), Integer(4610198342416185998), Integer(-8760902751118060791), Path("HΓ­{𖬩~\u{733}{𝒹\':πž…€έΌ:π‘£˜Aΰœpπ‘¦§πž¦K=Z*"), String(NixString(Smol("\\\"\\O=𝆩𐝋0πŸ•΄\">.πŸŸ‡/𝔇`Β₯β·’"))), String(NixString(Smol(".𝼩𑡒"))), Path("&cαΌΎV&🈫WΘΊ2{:Uΰ”iπ’’¨οΏ½$\\Ρ¨β·‰<+πž₯‘οΈΎοΏ½πŸ’…π„¦"), Bool(false), String(NixString(Smol("?H\"ᝨᛧDπŸ•΄e"))), Bool(false), Null, Path("~"), String(NixString(Heap("*<π„˜ΰ°¬u;.ππŸ›΅πžΈΉg\\mF%[LgG.𐭸𐫃*ε€±πŸ•΄`"))), String(NixString(Smol("`Γ³Β₯!0Ρ¨W.ଠಏퟞ\\ਫ਼?🫳"))), Integer(5647514456840216227), Null, Bool(true), Bool(false), Integer(-7154144835313791397), Path("\\=πŸ•΄οΏ½α£²*πžΉ·π›²•cκˆπŸ«£CȺÏ𑀉נּ/$ਜ.\u{dd6}*%আ`𐄿yκ‘Ά"), String(NixString(Smol("`2Β₯/οΏ½κ«€X\"Lα±½"))), Float(-1.5486826515105273e-100), Bool(false), Path("\\A6πΌ₯^]<πŸ’–"), Null, Path("G`6𱑎%\u{1e08f}α³°"), Float(0.0), Float(-5.1289125333715925e299), Integer(-2181421333849729760), Bool(false), Null, Float(1.8473914799193903e206), Float(-0.0), Integer(-1376655844349042067), Integer(-5430097094598507290)])), NixString(Heap("ΝΏΒ₯%h:?=$πŸŸ™p\u{1cf24}*π‘΄ ΘΊ]Xb")): Path("]l\'*𑇑\u{1e08f}*𝄍&,Γ·ncΰ·΄GΒ₯,πŸ•΄π‘Œ+`?"), NixString(Smol("\u{85b}/")): String(NixString(Smol("%ΘΊ{𑀉pOπ‘±€$d/Γ±PF\"="))), NixString(Smol("\u{a51}H𞹾$`𐒑:οΏ½Β₯𐝑{𐺙౾�i${ಇTGοΏ½οΏ½Β₯{`Γ’ή„^")): Path("<𛁀Ehπ‘±…"), NixString(Heap("ΰͺ•\"1ਐȺ")): String(NixString(Smol("πžΉ‰Γ•I$𑁔﹫xpnὝ{`RgX.&]ଘ"))), NixString(Smol("ΰ™+Χ—πΌŒΰ:=R0D\u{afe}Γ°<%π–­΄?/CT%ΘΊo=?πž₯”𐴷\"")): Null, NixString(Heap("\u{e4d}/Ð\u{11d3c}m6ΰ±¨ΰΏ’ΰ©žKΒ₯u\\πͺΰ­‹")): String(NixString(Smol("𐋴+Z\\𞸻kמּﲿ𐀨tn>αΏ“/>3Ρ¨<E{𐧆!"))), NixString(Heap("αŒ’.ퟴ%\u{1e016}πŸ•΄π‘¨αΌ’=~\\:7𐦜")): Integer(7492187363855822507), NixString(Smol("β±…/𞹯=\\ꬆ^α°’.Fπ’Ώ€π’Ύ–ΘΊlȺÐ")): String(NixString(Smol("πžΉ—R"))), NixString(Heap("βΆ€B2.$\u{10a05}𖫦&*\";y$ΒΈπ›²’π‘Š²UπŸ•΄Γ›\\πŸ•΄πžΉ‚E಄,ΰ­¨")): Float(4.8766603500240926e-73), NixString(Smol("κ¬\\'\"A\\\\\"R.")): Bool(false), NixString(Heap("οͺš$%`ΘΊ")): Float(-6.1502027459326004e57), NixString(Heap("οΏœπ›²…</\"\\:οΏ½=hπ‘΅₯V=\u{c4a}")): Path(""), NixString(Heap("οΏ½I𐔋\u{1bc9d}\u{1e029}π›…Ήπ‘»¨πŠΎIΒ₯?Ρ¨Ρ¨:�È\\'𞟾'Ρ¨")): Float(4.528490506607037e180), NixString(Heap("πͺqՎ4𝒦?F𐙍?")): String(NixString(Heap("`πŸ‚»πΊ­` π‘Ššΰ΅½/βΆ»πŸ›ΆfΘΊ(f𐖻Έ{αͺπ‘Œ«Z%π‘κ¦˜π΄&zd৑𑼩"))), NixString(Heap("π’“ΈΰΆ‚#.Ѩ㈞�i")): Path("$/"), NixString(Smol("𖀔𐖔%π–­–\u{1bc9e}")): Float(-2.70849802708656e-257), NixString(Heap("π–Ύ–:y'π«dmvਫ਼`*QR𐏐::P=\\BπŸ•΄\"cπ’“•eαŽΎΞ‘")): Path("\\Γ”A"), NixString(Heap("πž„'π–­•:π–½­π–˜\"{ΘΊ.\"⺈??ΰ§ŽπŸ‘ΰ¬Šπ¦©½πŸ•΄πž£w.:ΰ‘°")): Null, NixString(Heap("πžΊ–ΘΊΰ³‡π₯’βΉ‚Ρ¨@ΰͺ‹πžΉ<ΘΊ6ⴏ𑽗ΰΆ=L𑍐M.<ꭚ*J\"@~𝁌$\\]")): Float(-2.5300443460528325e91), NixString(Smol("πŸ•΄7πͺ»Žπ‘ƒ¦π‘€+ῴᎣ\\ౝ?ΰŸ\"\\ꜭ")): Integer(8622149561196801422)}))), Path("I<:🫒:.π‘‹›ΰ―‹\'βΆ£[𑆔%)ΰŽM!1<ΰ―‚-J>/`$🠁<\\u*ল"), String(NixString(Smol("M|s?ଏ\\"))), Float(-0.0), Integer(6467180586052157790), Bool(false), Bool(true), Float(8.564068787661153e-156), Float(6.773183212257874e294), Integer(4333417029772452811), List(NixList([String(NixString(Smol("/%\u{9d7}π–Ž\u{b43}𑰅𝋓ᝠi`\u{a02}aѨ𐒒>β΄­ΘΊπ‘ŒƒC፧Â𐭰>G\"α™ππ ˆ&$"))), Integer(2000343086436224127), Integer(3499236969186180442), Integer(4699855887288445431), String(NixString(Heap("ΰ™π›π–Ώ£Y𘑌`.π’‘³πž‹€R7$@`")))])), Bool(false), String(NixString(Heap("*πŸ•΄,/𐀬tykπ’‘°\u{f90}"))), Integer(5929691397747217334), String(NixString(Smol(".=𝒒.E⢇⁃ΰ©\u{fe04}π›…•CαŸ°πŸ’α½™`{.gΒ₯Β₯"))), Path("\\*J(\'%\u{1a68}k\':β·‹?/%&"), Bool(false), Float(3.7904416693932316e-70), String(NixString(Heap("/ΘΊc$𐠼<�⾹ഉ "))), Integer(3823980300672166035), Null, Null, Bool(true), String(NixString(Heap("$�𐖔aοΏ‹ΰ·€w-=$πŸ•΄$𞹟xΡ¨bπŸ«‚,mΰΊ„"))), Float(-5.5969604383718855e-279), Path("Ἓቇ !\'𐍈π‘™₯&ಐz"), Bool(true), Integer(429169896063360948), Float(8.239424415661606e-193), Path(""), Attrs(NixAttrs(KV { name: Null, value: Float(-3.5244218644363005e64) })), Float(2.1261149106688998e-250), Float(2322171.9185311636), Integer(5934552133431813912), Integer(5774025761810842546), Float(7.97420158066399e225), Integer(4350620466621982631), Attrs(NixAttrs(Empty)), Integer(-6698369106426730093), Bool(false), Null, Null, Float(-5.41368837946135e190), Null, Path("\u{1112b}`Β₯𐀇=hπ›…•`/?qG%GΘΊ\u{cd5}𝔼.𞊠\'\'."), Null, Bool(true), Float(-1.0226054851755721e-231), String(NixString(Heap("\"$ΰΆ½%𐴴*s\"D:ᘯᜩ9π‘Œ—"))), Integer(-713882901472215672), Path("/{π‡˜π‘’₯*ﬧH`ΰ¨ΆΓ­$πž²αΏ„Z`πŸ«³π“Š―vg]YΰšΘΊSπžΉ’π‘Ό±Γ³3")])) }
+cc b0bf56ae751ef47cd6a2fc751b278f1246d61497fbf3f7235fe586d830df8ebd # shrinks to input = _TransitiveArgs { x: Attrs(NixAttrs(Im({NixString(Heap("")): Bool(false), NixString(Heap("\"𑃷{+πŸ•΄πΉ·πŒ‰πž₯ž'πŸ―ƒβΆ’vPw")): Bool(false), NixString(Heap("#z1j B\u{9d7}BUQΓ‰\"𞹎%-𑡣Შ")): Path("𐆠azπ―\u{c56}οΏ½γ„˜&πž„•ΰ¬¨z[zdΰοΏ½%𐕛*ΘΊDα‹»{𞊭꠱:."), NixString(Smol("&?")): Float(-1.47353330827237e-166), NixString(Smol("&👹B$Ρ¨K-/<4JvΡ¨ΘΊn?\u{11369}𐠈D-%ΰ­‡/=ΰ¨Ήπ›‚”")): Float(-1.2013756386823606e-129), NixString(Heap("'")): Float(-0.0), NixString(Heap(".`%+α©£\"HÎ&\"𐊸A%ἚOπŸ…‚<𞺦Β₯ைEAh.β₯˜?𑩦")): Integer(-5195668090573806811), NixString(Heap("4l\"π–«­Β₯r&𐑈{\u{11c9b}&οͺ«ΰͺ²π€·π‘Š£Γ‚\"'π“„‹{`?¬𝔔")): Attrs(NixAttrs(Im({NixString(Heap("\"!`=Wj㆐ᬒ\\Γ¨\u{1183a}T\u{11046}ꬕ&ΘΊΘΊ")): Float(-8.138682035627315e228), NixString(Smol("$Eπ‘±Όΰƒ:\"ΰ―†BπŸ.οΏ½πŸ‘ΎπŸ•΄:π‘‡π›²œ")): String(NixString(Smol("Qf1𝜻A\\L'?U"))), NixString(Heap("$kΰΈͺ𐀽ΰšπ Έ<`\u{f37}5α‰˜\u{cc6}G\"1ΰΊ₯Γ•π‘™“αΌ‘ο¬Ύt/𖭒𝓨₃ÑѨ𑀨aπ–Ώ°Ρ¨A")): Integer(-8744311468850207194), NixString(Smol("%'?Xl(ΰ‘».C+Tπ’Ώπ˜΄ˆL-π‘Œ³\\\u{1cd1}bK>SA")): Path("\\πŸ•΄ΰͺΡ¨πΉΌ-οΉ”Γπ˜΄‚:?{\u{1e02a}Β₯\u{c46}#𚿾?K"), NixString(Heap("%eπŸ•΄v𐒕&:שׂΰ€")): String(NixString(Heap(""))), NixString(Smol("%Β₯\u{1d167} |M")): String(NixString(Heap("π’ŒΆ$O🁹/𑑝ò"))), NixString(Heap("&=?\u{ec9}ΘΊuγ„π‘œΏΡ¨/�𝼦\"$π‘€·T{?βΆΎ,π‘‹²9ΘΊοΏ½")): Null, NixString(Smol("&π‘Œ–πŽ€?`F𑍍gΒ₯`\"ΘΊπ–Ώ’ΰΊ₯𝼦L{\u{b01}Ρ¨O*.&K%🫳\"πŸ•΄f𑆓Β₯/")): Null, NixString(Smol("'Pπ–†π½€π“²π”ŠSβΆ„α°ŽE\\kF𑦣`�ఎG&/K*")): Path("α ¦lΡ¨κ¬πžΈ·:\u{1344f}α‹€ME.&\u{aff}𐧣ᑋbπ¦Ÿα‰˜α„¨"), NixString(Heap("'ΰ±»πŸ•΄_𐠷𑍑!')?&πŸ•΄.\\{{𐾡�*>sj\u{9e3}ΰ°¨`Ρ¨Β€")): Bool(true), NixString(Smol("+ས&1:᳇P%{r?Ρ¨/d`𐠷\u{1ac5}>'💧")): Bool(true), NixString(Smol("3\\Ѩ=&ΰΆ΄ΰ±π‘ˆ‹πŸ’±\"/<.&πŸ•΄/ΰ« α₯¬οΏ£|&Γ‚$'")): Bool(false), NixString(Heap("6|α‹…`Υͺy𐠃*")): Bool(false), NixString(Smol("8=᠊<ΰΆ©TRΰͺ³(Q𑝆=ΰ·¨\\?ΝΏ{>n&")): Null, NixString(Heap(":6zꬑ\\ΰ₯ž\u{1cf15}Χ°πΌ¦πŸ›©Lv<?\u{10a3a}*π‘Œ‚wೝ𝓃u%C.R%$ΰ·ͺl")): Bool(true), NixString(Smol("<VπŸ›·VΘΊπ©ˆπ“ˆ€ΰ±&β‚”Ρ¨")): Integer(-2521102349508766000), NixString(Heap("=/eοΏ½S=dπ–ͺ\\bα˜”8$οΏΌZ'")): Float(1.7754451890487876e-308), NixString(Smol("Dπ‘Š“αŽ΅%")): String(NixString(Heap("π‘ŒΓ«;ΰΆ½"))), NixString(Heap("E</")): Float(1.74088780910557e-309), NixString(Heap("FΰΌ€.ΰ΅·π’€–>ΒΉπΘΊ$M\\\u{10a3f}ಎπ’Ώαƒ5α‹“6{πŸ•΄:")): Null, NixString(Heap("GCΰͺ•\\Β₯{4πŸ•΄?ΰ πŸ•΄=πŸ•΄π–­'?π₯%ΘΊ\u{10f48}οΏ½kኲ:%Β₯")): Path("𝉅w𞹟`αΏΌ`Ρ¨h/\u{11d3d}\u{65e}j/πŸ‰₯\\&π›…₯אּѨ𑧒\"Ρ¨kΰ»„\\a~π‘š©-(:"), NixString(Smol("K|*`\u{9e2}Ρ¨π– ‰%𑦺=u</ৌβ•κ¬‹\u{a01}*6𐩒Pα°Ώπž…Ž'πŸ‰")): Bool(false), NixString(Smol("MSᅳ")): Integer(-8585013260819116073), NixString(Smol("P&κ©ΰ‘žπŸ© &π‘ˆ³:β².Ψ²")): String(NixString(Heap("G]Ρ¨{D"))), NixString(Heap("Rπ’žj:𝄁{𑍝ﹰ\u{309a}5Ρ¨yべkπŸŸ°π‘Šˆπ±Ώ:π‘‘‘'πŸ‰₯ⷊ{ΰ± <=&:α¦—*_K")): Float(1.215573811363203e25), NixString(Heap("Xoy_ZπŸ•΄πž…ˆβ·ˆβ΅\"έ­)<π‘½Ž]π’’΄")): Float(-2.0196288842285875e215), NixString(Heap("[y𞹨/")): Path("(p\'/.ΘΊΰ‘Ή?πŸ„Žα‰ŒmοΏ½>>%z~{`%4Ρ¨ΘΊπ‘«Ž"), NixString(Smol("[𝒻W𑬀\")a𛄲𑰄&βΆΉ.\":π–‚")): Path("\u{a51}<:κ«§γˆ˜πŸ•΄π‘’»gΘΊπ‘ŒƒDπŸ¦π«“"), NixString(Heap("^?𔕃{\"κ’­π₯{πŸ’Ώo𝼨\u{b01}Ρ¨Γ΅")): Bool(true), NixString(Smol("`.\u{1e005}&ΘΊπŸ•΄=%𑩦৩\u{a3c}{ⷜ:FοΏ½h'\":α½›ΰ Έ")): Integer(958752561685496671), NixString(Smol("`𑀓(b𝔡3=πž•<\u{f93}π‡žπ–­Ά$πŸ•΄Β₯.:&?=oΰ°‹N\u{9c3}")): Float(-1.2016436878109123e-90), NixString(Heap("`πŸ‚£`𐀒.ΓΌIΒ₯::.$)𐨡/\\π’‘šZὍ𖹕eπ’Ÿπ― ‘Yຈ|πŸ€™ΰ‘ž")): Path("/οΏœΘΊπ’½αƒ<πŸ‰€u)πŸ•΄ο¬Ύ/ΰ¨"), NixString(Smol("dΒλŒ“7M𑍐_β€½sxβΆ‹π–©ˆXβΆ­]β·“?`oπž₯Ÿ\"@,γ„›πž²”ΰ³ πŸ•΄j#y{'")): Path("ቝgπ‘…‡α‹€πŸ•΄πŒΌοΏ½t=:.|*]οΏ½πŸ•΄,Ρ¨*ᝑㄧΒ₯nΘΊ"), NixString(Heap("eα‰Šΰ΄ŽΘΊοΉ°ΒΎ0𐣰/ΰ…πŸ „r~=𐞌_ΰ°½")): Integer(-4346221131161118847), NixString(Smol("eπ ͺα‰˜<$=Q\u{ecc}𚿷*}ଐ$/𐂒Y?y\u{11d3d}fଐ𐋹\u{20ed}H\\οΏ½Γ»'g<L{")): Integer(1302899345904266282), NixString(Heap("fJπŸ‰€!6$Fπ‘°€πŽC&ΰ±šπž“€T\"\u{d81}Β«Υπ˜΄ƒ.𑰍Â﬩*")): Float(-9.550597053049143e239), NixString(Heap("jοΌ\"πŸ•΄$%'Gૌ3?\u{302d}Β₯")): Bool(false), NixString(Heap("u~ΘΊΒ₯𝣀YNπ‚œ Β₯' `r𐿄/\u{b57}")): Integer(3809742241627978303), NixString(Smol("z%cοͺ—ΓΌπŸ•΄$`.F*`Ѩଏ<\"π£……'<3p=r:Y\u{1a60}঑:/]𐾁")): String(NixString(Smol("\\*EοΏ­{:πŸ•΄"))), NixString(Heap("{<B9-Yπ‘£ŠN61Ρ¨H.Β‘\\κ¬'ΓƒkΓ΄")): Path("=π‘₯˜^*Β₯K𚿾ΰͺΈ*Ðᝰf[:P𐀅*<β»‘<\"8"), NixString(Smol("{ΰΊ„Β±π‘Ό‰Ρ¨`βΆπšΏΊΓ%𞹑`!/")): Integer(-7455997128197210401), NixString(Smol("|πŸ•΄ΰ·š0\u{1e131}πŸ‘‘=ΰ©›_`𝔗.ΰ«‹1γ„ˆπΉ£\u{1773}κ­†β΅―\u{1c2e}ࠨ𐧔੫=\u{b42}1\u{bd7}οΏ½/οΏ½Β₯$")): Null, NixString(Heap("Β₯⡰ᅬ<*'ZΒ₯\"α½›$α₯–οΏ’πšΏ±\"κ₯³Γ’π–Ώ±πž“•Ρ¨9MÌΒ₯u{ୈ<οΉ¨")): Float(6.4374351060383694e243), NixString(Smol("Β₯πŸ•΄=r`z&.<V<.Ⱥ๏\"")): Bool(true), NixString(Smol("Ê9𝐐/Jkα‘ΆUl\"πŸ•΄{i")): Path("z/Β₯&Wzκ‘­`?Ρ¨\\🟰þ၈$ΰ “"), NixString(Heap("Γ™π‘₯˜c\"οΏ½\u{8ce}π‘€Έπ†₯ΰ³?~")): Bool(true), NixString(Heap("Γ£U&𐔐/")): Bool(false), NixString(Heap("Ρ¨Ρ¨?<'?ΰΏŽΘΊΥ‰β·Š$\u{fe09}κž™")): String(NixString(Smol("Ό/`jπŸ•΄πŸ•΄πž‚πŸ•΄\u{ce3}ਲ$π˜Ÿ›ΒΊΧ°?πžΈJ𝍣Q\"}πŸ•΄qπ§Έΰ’Ί\u{a48}"))), NixString(Heap("ΰ‘©κ―³%^ౝ")): String(NixString(Smol("{%"))), NixString(Heap("\u{b57}πŸͺƒβ·šΘΊMC?Β₯\u{11c9a}+:*<B𐖰y~/.1&$𐺭hΰ¦°\"\"")): Path("α‰•πŒ«=(|R{ei\u{1a5b}Z!2\u{c3c}+<rΓ­:\"f"), NixString(Heap("ΰ™ΒΈ4ᨔ\u{17cb}ਜ'ΰ²£Β₯z&=='cπœ½«ΰ‘ οΉ³κ ²π¨–ΰ¦<Β₯πŸ•΄π‘™«π–·")): Integer(-2404377207820357643), NixString(Heap("α‰˜πŸ•΄{ΘΊL'.")): Null, NixString(Smol("ኸ\"𖦆d\\&𐞷CπŸ €k1\u{eb1}KV%πŸͺΏ\u{1cf35}>:3𐑾P.\"Ρ¨.ΰ»†αŠ*'")): Bool(true), NixString(Heap("ኸ8")): String(NixString(Smol("οΏ½?IaπŸ€›*\u{10d27}UΒ₯𐖙9ΰ«€γ‡ΊIW:%&G+?"))), NixString(Heap("α±°αΏ™:ΰ™α€„.:*𐺰𝄿勉Ѩ&Β₯𐧾7&`AΰΊ‚Ρ¨π‘—’&Β₯π›…•/πŸ•΄Ρ¨")): Float(-0.0), NixString(Heap("ꝏs𞊑<g=𝕃ᅭRΘΊοΏ½uπ›±œ")): Bool(false), NixString(Heap("κ‘©$𰻍🯷@a\\")): Null, NixString(Smol("π­»πžΈ§Γƒ\\Ⱥ𞸒𘌧%h+|@κ–¦*\"~π‘„£")): Bool(false), NixString(Smol("π‘‘πžŸ£Ρ¨<''Β₯Ρ¨<ΰΊ­αͺ¦\"α΅π›„²πž€Ύαžo")): Float(1.540295382708916e-173), NixString(Heap("\u{1145e}p\u{ac1}e\u{10a05}𝼩#ΰ¨*/?πŸ‘—\u{c4d}ΝΌα²ΏκŸ‘")): Integer(-4194082747744625061), NixString(Smol("π‘˜πžŸ¨:Ρ¨?^ cπŸƒŠtΰΉ†.ΘΊ9 𔑳&αŸ΅Χ―πŸ¬”\u{11f01}π¬’ͺ*ο·²ΘΊ`π‘Ό†n")): Path("7οΏ½$Ì\u{1e00c}𯀁2&𐝀<"), NixString(Smol("π‘š†")): String(NixString(Smol("𝒒\u{11727};ΰ•\u{c56}P'π„ŽπŸͺ¬*ⷍ\u{1e08f}59/π‘¨™π€Ώΰ·œZ$Jΰ·³*πžΊ“"))), NixString(Heap("π‘΄‹Β₯#=%")): Integer(1639298533614063138), NixString(Heap("\u{11d3d}π‘ŠŒo:}=ਫ਼$ΰ»“οΏ‹Ρ¨π‘Œ…6πŸ«„οΏ“πŸ›΄")): Integer(4745566200697725742), NixString(Heap("\u{16af4}πž²‘πŸ‘•:ΰ³ˆπ’©$𝄀\u{c3e}ΰ‘¨")): Float(2.8652739787522095e-21), NixString(Heap("𖽩$,Β'w5ή€*π–Ώ£H'6Β₯ΰ©€")): Path("+κ‘•:/f"), NixString(Heap("πž‹Ώ@%\u{20d6}Γ·MqȺÏπŸͺ§πžŸ£&𝼆Ⱥn2?ᦽ")): Path("n𞺣Ꮝ*𐀀FU.T"), NixString(Smol("πŸ•΄/ΘΊαœ„&{.\u{e0109}]𐣨|ຆ᱓𐃯πŸ‚₯𐺭𑀬@$r{ΘΊβ€ΏπŸ•΄γ€§\u{c3e}'X")): Null, NixString(Smol("πŸ•΄<V𖬑u\u{bd7}ໆC`xῴ𐄂f.M^ਐ𑼂;%.𐠈Β₯ΰ»”κ˜„α‹€Ρ¨ΰͺ‘")): Null, NixString(Heap("πŸ•΄Β₯\u{10a38}/{\\-?πŸ•΄'Ýὓ𛱱`XuαΎΊπŠŽΰ …|πžΉ’π†—π‘Œ²")): Bool(true)}))), NixString(Heap("9;G'.%𑴆")): String(NixString(Heap("πŸ•΄8?$=πžΊ’%V*\u{9bc}π‘Š„ΰ­œJbΡ¨=ΰͺ³=𑛉\u{a81}Γ‰;οΏ…α²ΏN=ਫ਼:οΏ½`𐁙"))), NixString(Smol(":Ρ¨\\Lꝅ5𐀀'uꦙ~*ο₯“J")): List(NixList([String(NixString(Smol(".ΰ‘ž\\P?*<<\"𐖄9&𘚯%Ѩю1G\\౭𒑃&πžΉ›=9:πž₯@("))), Path("<𝒗ঽΒ₯<\u{fa7}ୌD={=ଡ଼\"ΘΊ\u{bd7}ΰͺ$\'οΏ½Β₯𞹼Eπ¦™πŸ‘“t`\u{11357}"), Float(-3.099020840259708e-255), Path("𞟫αͺ“h𖭁")])), NixString(Heap("<:^XπžΉ‡πžΉ·\\\u{f7e}\u{1713}^ⴝQ`.|Gπ‚œπžΉπ‘¬π…{x𑍇סּ\"`πŸ‰€πššπ°³³ΘΊ")): Integer(6011461020988750685), NixString(Smol("<{ο­„%#Β₯πŸ•΄d:οΏ½,y")): Float(-2.901654271651391e201), NixString(Heap("=/I\"πŸ§{z&aοΏ½<?`LΒ₯πž‹Ώ``.|")): String(NixString(Smol("=𖫉m\"ΘΊhC_"))), NixString(Smol("=L=X'aΰ¨Ώ9.")): Path("<&]Mΰ¦­"), NixString(Smol("Eο­€:b$?Jα½™s𝒒O?κ₯ Ρ¨{\\ΰ¨Έπ•Žπ’Ύ`𐀒")): Float(3.6870021161947895e-16), NixString(Heap("\\οΏ½' Γ¬rᩆ#ΰΊ₯BΒ₯𐖻l%π›…₯D3{/.4OΒ₯\\.{𐼓^π’Ύ¬")): Path("/Γ­Rβ€ΊdπŸ•΄-K\u{ac8}"), NixString(Heap("\\\u{1e028}೫I/Ν½4")): Float(-8.628193252502098e81), NixString(Heap("a𐠈/K33/&𝐡Β₯𐼰.y/🫒\\𐧨𞟹")): String(NixString(Heap("𞹑W<᱂a=α‘žΰ²‡οΏ½%&π‘΄ˆΰ’³Β₯π‘Šˆ"))), NixString(Smol("eDοΏ½Β₯πŒ‹x<`B𞣍H\\`Γ¨Z𐐅αŸ₯{\\?T*")): Bool(false), NixString(Heap("oP𝒬(Ρ¨Jΰ¨ˆπ›€ΊΡ¨\"?T=O𛄲sοΏ½πŸ•΄LK🝈ከ⢩+\u{1344f}hu?E")): Bool(false), NixString(Heap("vNκŸ“ΘΊM]")): Float(-2.9637346989743125e232), NixString(Smol("{!Β₯\\\u{a47}\u{10a38}.E4Ρ¨;=R\u{a48}<=/\\&స𐒩N'?.9π‘—Š\u{7ec}ΰ¨Ά:𐫃")): Null, NixString(Smol("~+!$M.π‘―ΰ±š\\>.Β₯π–₯<Q'ΰ»™0.kQ{πžΉΎΰΌΊπ‘Ό‰sEὕ")): Float(9.45765137266644e-294), NixString(Smol("Β₯;Z$*&+οΏ½&\"πŸ‰†`ኲ")): Null, NixString(Heap("Γ£F&<` \u{64c}-:Ρ¨*οΏ½")): Integer(-7654340132753689736), NixString(Heap("ΘΊ`\u{a81}ΘΊπžΊ₯\u{11727}<m\u{a51}{$`\u{11d3d}ΰͺΆR/E")): Bool(true), NixString(Heap("Ρ¨%gὋ 4𑢠𑀕Y{Q<")): String(NixString(Heap("π›ƒ›κŸκ©€π‘ŠˆRoR<ৌ:4`βΆ₯(ΰ³³>8*π‘ŒŸ/π›…§<}&κ’©α ™\""))), NixString(Heap("ѨನQπ‘ ƒ%=iπ˜“¦ΰ·\u{1939}𞟭7;οΊΈπΌ—κŸ˜W")): Bool(true), NixString(Smol("ਬͿ𝒩./πŸͺ‘Ѩ𝁾ໆ+ΰ°«J{𑰏*\u{cd6}ΐѨ'APΘΊ*")): Path("Q%︹֏Ὓ<$κŸ–$ᦣ𐖻Ѩ\u{c3c}MΡ¨"), NixString(Heap("ଢ଼")): List(NixList([Path("?&οΏ½=\u{fe0a}z\"ΰͺ΅πŸŸ°}ৱα¦₯𐕅\"𞹭MⷝDJΰΊ„"), String(NixString(Smol("𐺱`πž±ΈΘΊπŸœ›?ୈ&Ꞓ=jΒ₯`ΰ΅·@ਐෂ't=7)κ¬–ΰ°¨2Dκͺ©lπ£αŒ“=W"))), Path("`ΰ²²=π‘˜€eΰͺ²7𖽧Ù&αœ‰:.ﺴ𐳂\'𞹎nπŸ•΄π“Šš>π““ΰ―ˆ:\'{`o"), Null, Bool(true), Float(-1.424807473222878e-261), String(NixString(Heap("π‘€…wπ‘ŒαŠΈΒ‘ΰ¨Š/𞺩Ⱥ\\π›…€πž’Š?\""))), Null, Bool(true), Float(-4.714901519361897e-299), Null, Integer(2676153683650725840), Null, Integer(3879649205909941200), Bool(false), Integer(-7874695792262285476), String(NixString(Heap("Wα₯³β‘*\\%ᜦ.βκ«žQπžΉ”L|ௌѨdU=*Ρ¨\u{20dc}"))), Null, String(NixString(Smol("Uπ‘Žπ–Ί—$πžΉ‘πž…π”―ο΄<)&π‘Œ«\\{\u{1acd}wΡ¨!πžΈ€π–­“ΒΊ"))), Integer(802198132362652319), Path(":\u{b4d}Fᝊ:w:κ―΅"), Integer(-8241314039419932440), Null, Bool(true), Float(-0.0), Float(0.0), Integer(-3815417798906879402), Path("\"Dΰ·†ΰ«Œΰ·ͺ\u{b01}ૐ%Mβ„–"), Null, Path("ab`𐒨\\{Γ·ΰ―Άπ–Œ$=Γ«"), Bool(true), Null, Float(2.607265033718189e-240), String(NixString(Smol("𐖀𑀷=6$`:0Ρ¨E\"Ρ¨\"π—ΎαŠ΄}.πž ΈπžΊ’ί”π‘΅‘"))), Integer(340535348291582986), String(NixString(Heap("α ‘%*&tπ':\\\"κ©‚ΰΊ₯'\"𞟳|J\\\\Vπ‘§ˆπ›‡μ·­π¬$\u{eb4}"))), Null, Float(-0.0), Float(2.533509218227985e-50), Integer(2424692299527350019), Integer(8550372276678005182), Integer(2463774675297034756), Float(-1.5273858905127126e203), String(NixString(Smol("𐕐𐐀ùI"))), String(NixString(Heap("?:πŸ•΄π‘΄žοΉ²Ρ¨Γ‹π«¬Zf{𝋍{{ΰΏ€&\"Γ”:<CJN?"))), Integer(-916756719790576181), Float(5.300552697992164e116), Path("𞹝�.?𝌼uଢ଼%~"), Bool(true), Float(-4.423451855615858e107), String(NixString(Smol("αΏ»ο±Β₯8gH^α›¨κŸ–jRπ’\"Sπ–©‘πžΈ»πŸͺ"))), Integer(8503745651802746605), Integer(8360793923494146338), Bool(false), Path("π„ž3Β¨/π‘’ΈmΒΊ~𑍋haπ«$⹎\u{dd6}*ᲬѨク?𝔔¿"), Null, Float(-1.116670032902463e-188)])), NixString(Smol("ഌ⾞Vπ‘„Ύ7π‘š…Dπž…Ž$\"")): String(NixString(Heap("Β€<ꑀὝΒ₯πžΈ€πžΈ‡π‘΄„οΏ½β·aπ‘ŸO\"f&π˜΄‡`*<z𑨧ဗ"))), NixString(Smol("α‹„SπΎ†κŸ“\")αŠ›7G<oΡ¨πŸ•΄?𛄲×𑿕ᩣb。𖩒w")): Float(-2.1782297015402654e-308), NixString(Heap("ᩌਲ਼Ⱥﹲ<'=Φ‚ΰΊ₯ο¬ΎοΏ½p")): Path("𐀽𐠄"), NixString(Smol("\u{1a7b}$ΰΌ„Xΰ­ˆΓ‚`7\"$*=ΰ‘žαŸ£π€Έ.tw")): String(NixString(Smol("ο¬–<j%π‘ŠŠkૐmΘΊrαŽ‡π’„ΉΓ«ΰ¦².y*ါCο½BΒ½"))), NixString(Smol("αΏΈ:𝍷💰8g>\u{fe0d}RΡ¨κ₯«^αΏ·&ຈ/'π‘Œ…gው=s𝂀'οΏ½π‘›‚πž΄~π‘…?π‘ͺ")): Float(1.6480784640549027e-202), NixString(Smol("β΄­8πŸ•΄VπžΉ™2π‘Ά¦ΰΆ‚=")): String(NixString(Heap("ΰ¨“πžΉ—Ρ¨π–­¨π“ΌΒ₯..αΎΆ`oU{β‚–B\\Β©:/Ρ¨I𝆍.&πŸ•΄:ΰΆΈπžΉ‰ο¬*v"))), NixString(Smol("ο¬ΉN\"@𐦽Ë`𑇑ȺC{Β₯b$ΰ²­ΰ³±p$hS[μ’΅&")): Float(0.0), NixString(Smol("ﺡ🟰\"nj𞺑o&*Oz{ΘΊ\"/\\፨\u{8dd}^v´𞹺`")): Path("π‘‡ž.πŸ•‚d"), NixString(Smol("�𞸹מּz?_R'α₯²<<Β₯/*οΉ›ΘΊοΏ½Rπ‘—“π‘™₯π‘œ„ΰ³±a'π€ž7")): Float(1.6499558125107454e233), NixString(Heap("πŠΰ΄ŽπŸŸ©ο¬”V?Ѩ𝔒𖭑\u{1bc9d}πŒ…π‘€ΈΪ½πžΉΊV.π•ŠBβΈ›*")): Null, NixString(Heap("𐞒u1%<πŸ•΄Γ’}π›±·$Ω­bπ›²ŸΰΊœvοͺͺ\"𝕃\\$ΘΊ<ΒΊο­ƒ%ଓ")): Null, NixString(Heap("πžΆπƒ*$πŸ‚­eΰ·ƒ\u{dd6};𞁝'α©―")): String(NixString(Heap("'&ⷘᰎ\"𐠸π‘ˆͺ"))), NixString(Heap("\u{10a05}/")): String(NixString(Heap("γ„΄αŠ²YΒ₯'π‘›…αΏ¬\"π”•‘π–•ο­€πŸ‡½$l.=$πŸ €π›°œ-𝒒R{$'$^"))), NixString(Heap("\u{11300}πŸƒ†+$οΏ½β‚œj\"ჇA*r&π‘₯π‘Š™W\u{10a0d}yR<\\ΘΊΧ³πž„½C&?")): Integer(6886651566886381061), NixString(Heap("π‘ŒπŸ•΄`'\\GΒ₯<꬀βΆM%𑡠꒦ૉS\u{9d7}Ⴭࢹ8πŸ•΄*[Γ–")): Float(2.2707656538220278e250), NixString(Smol("π˜΄€'>$Β¦C/`M*πž»°Β’κ©™α –οΏ½*πŸ•΄:&Γ¨")): String(NixString(Smol("'<ড়ᰦ"))), NixString(Smol("𞊭")): Attrs(NixAttrs(Empty)), NixString(Heap("πž₯–πŸ’™=ཏ=οΏ½%𛲁$ziΒ₯&꧗𫝒૱G𐧠𝓾$/𐼍𫟨Yhβ·•")): String(NixString(Smol("Us:)?𘘼"))), NixString(Smol("πžΉͺ*π‘Œ³`f𝐋ଐ𝕆j")): String(NixString(Heap("π–­Ώ5^Zq𐨗𑄿π‘₯„ί€π‘œ±2ঐ"))), NixString(Smol("π±ΎΌ\u{1e4ef}Γ΅<\u{a0}{ΓΆ\\$οΏ½αΏ·q")): Null}))), y: Attrs(NixAttrs(KV { name: String(NixString(Smol("�𛲑ો\\?Γ‰'{<?W2𫳫p%"))), value: Integer(134481456438872098) })), z: Attrs(NixAttrs(Empty)) }
diff --git a/tvix/eval/src/builtins/hash.rs b/tvix/eval/src/builtins/hash.rs
new file mode 100644
index 0000000000..d0145f1e7d
--- /dev/null
+++ b/tvix/eval/src/builtins/hash.rs
@@ -0,0 +1,29 @@
+use bstr::ByteSlice;
+use data_encoding::HEXLOWER;
+use md5::Md5;
+use sha1::Sha1;
+use sha2::{digest::Output, Digest, Sha256, Sha512};
+
+use crate::ErrorKind;
+
+/// Reads through all data from the passed reader, and returns the resulting [Digest].
+/// The exact hash function used is left generic over all [Digest].
+fn hash<D: Digest + std::io::Write>(mut r: impl std::io::Read) -> Result<Output<D>, ErrorKind> {
+    let mut hasher = D::new();
+    std::io::copy(&mut r, &mut hasher)?;
+    Ok(hasher.finalize())
+}
+
+/// For a given algo "string" and reader for data, calculate the digest
+/// and return it as a hexlower encoded [String].
+pub fn hash_nix_string(algo: impl AsRef<[u8]>, s: impl std::io::Read) -> Result<String, ErrorKind> {
+    match algo.as_ref() {
+        b"md5" => Ok(HEXLOWER.encode(hash::<Md5>(s)?.as_bstr())),
+        b"sha1" => Ok(HEXLOWER.encode(hash::<Sha1>(s)?.as_bstr())),
+        b"sha256" => Ok(HEXLOWER.encode(hash::<Sha256>(s)?.as_bstr())),
+        b"sha512" => Ok(HEXLOWER.encode(hash::<Sha512>(s)?.as_bstr())),
+        _ => Err(ErrorKind::UnknownHashType(
+            algo.as_ref().as_bstr().to_string(),
+        )),
+    }
+}
diff --git a/tvix/eval/src/builtins/impure.rs b/tvix/eval/src/builtins/impure.rs
new file mode 100644
index 0000000000..c82b910f5f
--- /dev/null
+++ b/tvix/eval/src/builtins/impure.rs
@@ -0,0 +1,110 @@
+use builtin_macros::builtins;
+use genawaiter::rc::Gen;
+
+use std::{
+    env,
+    time::{SystemTime, UNIX_EPOCH},
+};
+
+use crate::{
+    self as tvix_eval,
+    errors::ErrorKind,
+    io::FileType,
+    value::NixAttrs,
+    vm::generators::{self, GenCo},
+    NixString, Value,
+};
+
+#[builtins]
+mod impure_builtins {
+    use std::ffi::OsStr;
+    use std::os::unix::ffi::OsStrExt;
+
+    use super::*;
+    use crate::builtins::{coerce_value_to_path, hash::hash_nix_string};
+
+    #[builtin("getEnv")]
+    async fn builtin_get_env(co: GenCo, var: Value) -> Result<Value, ErrorKind> {
+        Ok(env::var(OsStr::from_bytes(&var.to_str()?))
+            .unwrap_or_else(|_| "".into())
+            .into())
+    }
+
+    #[builtin("hashFile")]
+    async fn builtin_hash_file(co: GenCo, algo: Value, path: Value) -> Result<Value, ErrorKind> {
+        let path = match coerce_value_to_path(&co, path).await? {
+            Err(cek) => return Ok(Value::from(cek)),
+            Ok(p) => p,
+        };
+        let r = generators::request_open_file(&co, path).await;
+        hash_nix_string(algo.to_str()?, r).map(Value::from)
+    }
+
+    #[builtin("pathExists")]
+    async fn builtin_path_exists(co: GenCo, path: Value) -> Result<Value, ErrorKind> {
+        match coerce_value_to_path(&co, path).await? {
+            Err(cek) => Ok(Value::from(cek)),
+            Ok(path) => Ok(generators::request_path_exists(&co, path).await),
+        }
+    }
+
+    #[builtin("readDir")]
+    async fn builtin_read_dir(co: GenCo, path: Value) -> Result<Value, ErrorKind> {
+        match coerce_value_to_path(&co, path).await? {
+            Err(cek) => Ok(Value::from(cek)),
+            Ok(path) => {
+                let dir = generators::request_read_dir(&co, path).await;
+                let res = dir.into_iter().map(|(name, ftype)| {
+                    (
+                        // TODO: propagate Vec<u8> or bytes::Bytes into NixString.
+                        NixString::from(
+                            String::from_utf8(name.to_vec()).expect("parsing file name as string"),
+                        ),
+                        Value::from(match ftype {
+                            FileType::Directory => "directory",
+                            FileType::Regular => "regular",
+                            FileType::Symlink => "symlink",
+                            FileType::Unknown => "unknown",
+                        }),
+                    )
+                });
+
+                Ok(Value::attrs(NixAttrs::from_iter(res)))
+            }
+        }
+    }
+
+    #[builtin("readFile")]
+    async fn builtin_read_file(co: GenCo, path: Value) -> Result<Value, ErrorKind> {
+        match coerce_value_to_path(&co, path).await? {
+            Err(cek) => Ok(Value::from(cek)),
+            Ok(path) => {
+                let mut buf = Vec::new();
+                generators::request_open_file(&co, path)
+                    .await
+                    .read_to_end(&mut buf)?;
+                Ok(Value::from(buf))
+            }
+        }
+    }
+}
+
+/// Return all impure builtins, that is all builtins which may perform I/O
+/// outside of the VM and so cannot be used in all contexts (e.g. WASM).
+pub fn impure_builtins() -> Vec<(&'static str, Value)> {
+    let mut result = impure_builtins::builtins();
+
+    // currentTime pins the time at which evaluation was started
+    {
+        let seconds = match SystemTime::now().duration_since(UNIX_EPOCH) {
+            Ok(dur) => dur.as_secs() as i64,
+
+            // This case is hit if the system time is *before* epoch.
+            Err(err) => -(err.duration().as_secs() as i64),
+        };
+
+        result.push(("currentTime", Value::Integer(seconds)));
+    }
+
+    result
+}
diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs
new file mode 100644
index 0000000000..04a0b3dd33
--- /dev/null
+++ b/tvix/eval/src/builtins/mod.rs
@@ -0,0 +1,1728 @@
+//! This module implements the builtins exposed in the Nix language.
+//!
+//! See //tvix/eval/docs/builtins.md for a some context on the
+//! available builtins in Nix.
+
+use bstr::{ByteSlice, ByteVec};
+use builtin_macros::builtins;
+use genawaiter::rc::Gen;
+use imbl::OrdMap;
+use regex::Regex;
+use std::cmp::{self, Ordering};
+use std::collections::VecDeque;
+use std::collections::{BTreeMap, HashSet};
+use std::path::PathBuf;
+
+use crate::arithmetic_op;
+use crate::value::PointerEquality;
+use crate::vm::generators::{self, GenCo};
+use crate::warnings::WarningKind;
+use crate::{
+    self as tvix_eval,
+    builtins::hash::hash_nix_string,
+    errors::{CatchableErrorKind, ErrorKind},
+    value::{CoercionKind, NixAttrs, NixList, NixString, Thunk, Value},
+};
+
+use self::versions::{VersionPart, VersionPartsIter};
+
+mod hash;
+mod to_xml;
+mod versions;
+
+#[cfg(test)]
+pub use to_xml::value_to_xml;
+
+#[cfg(feature = "impure")]
+mod impure;
+
+#[cfg(feature = "impure")]
+pub use impure::impure_builtins;
+
+// we set TVIX_CURRENT_SYSTEM in build.rs
+pub const CURRENT_PLATFORM: &str = env!("TVIX_CURRENT_SYSTEM");
+
+/// Coerce a Nix Value to a plain path, e.g. in order to access the
+/// file it points to via either `builtins.toPath` or an impure
+/// builtin. This coercion can _never_ be performed in a Nix program
+/// without using builtins (i.e. the trick `path: /. + path` to
+/// convert from a string to a path wouldn't hit this code).
+///
+/// This operation doesn't import a Nix path value into the store.
+pub async fn coerce_value_to_path(
+    co: &GenCo,
+    v: Value,
+) -> Result<Result<PathBuf, CatchableErrorKind>, ErrorKind> {
+    let value = generators::request_force(co, v).await;
+    if let Value::Path(p) = value {
+        return Ok(Ok(*p));
+    }
+
+    match generators::request_string_coerce(
+        co,
+        value,
+        CoercionKind {
+            strong: false,
+            import_paths: false,
+        },
+    )
+    .await
+    {
+        Ok(vs) => {
+            let path = vs.to_path()?.to_owned();
+            if path.is_absolute() {
+                Ok(Ok(path))
+            } else {
+                Err(ErrorKind::NotAnAbsolutePath(path))
+            }
+        }
+        Err(cek) => Ok(Err(cek)),
+    }
+}
+
+#[builtins]
+mod pure_builtins {
+    use std::ffi::OsString;
+
+    use bstr::{BString, ByteSlice, B};
+    use imbl::Vector;
+    use itertools::Itertools;
+    use os_str_bytes::OsStringBytes;
+
+    use crate::{value::PointerEquality, AddContext, NixContext, NixContextElement};
+
+    use super::*;
+
+    macro_rules! try_value {
+        ($value:expr) => {{
+            let val = $value;
+            if val.is_catchable() {
+                return Ok(val);
+            }
+            val
+        }};
+    }
+
+    #[builtin("abort")]
+    async fn builtin_abort(co: GenCo, message: Value) -> Result<Value, ErrorKind> {
+        // TODO(sterni): coerces to string
+        // Although `abort` does not make use of any context,
+        // we must still accept contextful strings as parameters.
+        // If `to_str` was used, this would err out with an unexpected type error.
+        // Therefore, we explicitly accept contextful strings and ignore their contexts.
+        Err(ErrorKind::Abort(message.to_contextful_str()?.to_string()))
+    }
+
+    #[builtin("add")]
+    async fn builtin_add(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        arithmetic_op!(&x, &y, +)
+    }
+
+    #[builtin("all")]
+    async fn builtin_all(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
+        for value in list.to_list()?.into_iter() {
+            let pred_result = generators::request_call_with(&co, pred.clone(), [value]).await;
+            let pred_result = try_value!(generators::request_force(&co, pred_result).await);
+
+            if !pred_result.as_bool()? {
+                return Ok(Value::Bool(false));
+            }
+        }
+
+        Ok(Value::Bool(true))
+    }
+
+    #[builtin("any")]
+    async fn builtin_any(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
+        for value in list.to_list()?.into_iter() {
+            let pred_result = generators::request_call_with(&co, pred.clone(), [value]).await;
+            let pred_result = try_value!(generators::request_force(&co, pred_result).await);
+
+            if pred_result.as_bool()? {
+                return Ok(Value::Bool(true));
+            }
+        }
+
+        Ok(Value::Bool(false))
+    }
+
+    #[builtin("attrNames")]
+    async fn builtin_attr_names(co: GenCo, set: Value) -> Result<Value, ErrorKind> {
+        let xs = set.to_attrs()?;
+        let mut output = Vec::with_capacity(xs.len());
+
+        for (key, _val) in xs.iter() {
+            output.push(Value::from(key.clone()));
+        }
+
+        Ok(Value::List(NixList::construct(output.len(), output)))
+    }
+
+    #[builtin("attrValues")]
+    async fn builtin_attr_values(co: GenCo, set: Value) -> Result<Value, ErrorKind> {
+        let xs = set.to_attrs()?;
+        let mut output = Vec::with_capacity(xs.len());
+
+        for (_key, val) in xs.iter() {
+            output.push(val.clone());
+        }
+
+        Ok(Value::List(NixList::construct(output.len(), output)))
+    }
+
+    #[builtin("baseNameOf")]
+    async fn builtin_base_name_of(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
+        let span = generators::request_span(&co).await;
+        let s = s
+            .coerce_to_string(
+                co,
+                CoercionKind {
+                    strong: false,
+                    import_paths: false,
+                },
+                span,
+            )
+            .await?
+            .to_contextful_str()?;
+
+        let mut bs = (**s).to_owned();
+        if let Some(last_slash) = bs.rfind_char('/') {
+            bs = bs[(last_slash + 1)..].into();
+        }
+        Ok(NixString::new_inherit_context_from(&s, bs).into())
+    }
+
+    #[builtin("bitAnd")]
+    async fn builtin_bit_and(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        Ok(Value::Integer(x.as_int()? & y.as_int()?))
+    }
+
+    #[builtin("bitOr")]
+    async fn builtin_bit_or(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        Ok(Value::Integer(x.as_int()? | y.as_int()?))
+    }
+
+    #[builtin("bitXor")]
+    async fn builtin_bit_xor(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        Ok(Value::Integer(x.as_int()? ^ y.as_int()?))
+    }
+
+    #[builtin("catAttrs")]
+    async fn builtin_cat_attrs(co: GenCo, key: Value, list: Value) -> Result<Value, ErrorKind> {
+        let key = key.to_str()?;
+        let list = list.to_list()?;
+        let mut output = vec![];
+
+        for item in list.into_iter() {
+            let set = generators::request_force(&co, item).await.to_attrs()?;
+
+            if let Some(value) = set.select(&key) {
+                output.push(value.clone());
+            }
+        }
+
+        Ok(Value::List(NixList::construct(output.len(), output)))
+    }
+
+    #[builtin("ceil")]
+    async fn builtin_ceil(co: GenCo, double: Value) -> Result<Value, ErrorKind> {
+        Ok(Value::Integer(double.as_float()?.ceil() as i64))
+    }
+
+    #[builtin("compareVersions")]
+    async fn builtin_compare_versions(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        let s1 = x.to_str()?;
+        let s1 = VersionPartsIter::new_for_cmp((&s1).into());
+        let s2 = y.to_str()?;
+        let s2 = VersionPartsIter::new_for_cmp((&s2).into());
+
+        match s1.cmp(s2) {
+            std::cmp::Ordering::Less => Ok(Value::Integer(-1)),
+            std::cmp::Ordering::Equal => Ok(Value::Integer(0)),
+            std::cmp::Ordering::Greater => Ok(Value::Integer(1)),
+        }
+    }
+
+    #[builtin("concatLists")]
+    async fn builtin_concat_lists(co: GenCo, lists: Value) -> Result<Value, ErrorKind> {
+        let mut out = imbl::Vector::new();
+
+        for value in lists.to_list()? {
+            let list = try_value!(generators::request_force(&co, value).await).to_list()?;
+            out.extend(list.into_iter());
+        }
+
+        Ok(Value::List(out.into()))
+    }
+
+    #[builtin("concatMap")]
+    async fn builtin_concat_map(co: GenCo, f: Value, list: Value) -> Result<Value, ErrorKind> {
+        let list = list.to_list()?;
+        let mut res = imbl::Vector::new();
+        for val in list {
+            let out = generators::request_call_with(&co, f.clone(), [val]).await;
+            let out = try_value!(generators::request_force(&co, out).await);
+            res.extend(out.to_list()?);
+        }
+        Ok(Value::List(res.into()))
+    }
+
+    #[builtin("concatStringsSep")]
+    async fn builtin_concat_strings_sep(
+        co: GenCo,
+        separator: Value,
+        list: Value,
+    ) -> Result<Value, ErrorKind> {
+        let mut separator = separator.to_contextful_str()?;
+        let mut context = NixContext::new();
+        if let Some(sep_context) = separator.context_mut() {
+            context = context.join(sep_context);
+        }
+        let list = list.to_list()?;
+        let mut res = BString::default();
+        for (i, val) in list.into_iter().enumerate() {
+            if i != 0 {
+                res.push_str(&separator);
+            }
+            match generators::request_string_coerce(
+                &co,
+                val,
+                CoercionKind {
+                    strong: false,
+                    import_paths: true,
+                },
+            )
+            .await
+            {
+                Ok(mut s) => {
+                    res.push_str(&s);
+                    if let Some(ref mut other_context) = s.context_mut() {
+                        // It is safe to consume the other context here
+                        // because the `list` and `separator` are originally
+                        // moved, here.
+                        // We are not going to use them again
+                        // because the result here is a string.
+                        context = context.join(other_context);
+                    }
+                }
+                Err(c) => return Ok(Value::Catchable(Box::new(c))),
+            }
+        }
+        // FIXME: pass immediately the string res.
+        Ok(NixString::new_context_from(context, res).into())
+    }
+
+    #[builtin("deepSeq")]
+    async fn builtin_deep_seq(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        generators::request_deep_force(&co, x).await;
+        Ok(y)
+    }
+
+    #[builtin("div")]
+    async fn builtin_div(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        arithmetic_op!(&x, &y, /)
+    }
+
+    #[builtin("dirOf")]
+    async fn builtin_dir_of(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
+        let is_path = s.is_path();
+        let span = generators::request_span(&co).await;
+        let str = s
+            .coerce_to_string(
+                co,
+                CoercionKind {
+                    strong: false,
+                    import_paths: false,
+                },
+                span,
+            )
+            .await?
+            .to_contextful_str()?;
+        let result = str
+            .rfind_char('/')
+            .map(|last_slash| {
+                let x = &str[..last_slash];
+                if x.is_empty() {
+                    B("/")
+                } else {
+                    x
+                }
+            })
+            .unwrap_or(b".");
+        if is_path {
+            Ok(Value::Path(Box::new(PathBuf::from(
+                OsString::assert_from_raw_vec(result.to_owned()),
+            ))))
+        } else {
+            Ok(Value::from(NixString::new_inherit_context_from(
+                &str, result,
+            )))
+        }
+    }
+
+    #[builtin("elem")]
+    async fn builtin_elem(co: GenCo, x: Value, xs: Value) -> Result<Value, ErrorKind> {
+        for val in xs.to_list()? {
+            match generators::check_equality(&co, x.clone(), val, PointerEquality::AllowAll).await?
+            {
+                Ok(true) => return Ok(true.into()),
+                Ok(false) => continue,
+                Err(cek) => return Ok(Value::from(cek)),
+            }
+        }
+        Ok(false.into())
+    }
+
+    #[builtin("elemAt")]
+    async fn builtin_elem_at(co: GenCo, xs: Value, i: Value) -> Result<Value, ErrorKind> {
+        let xs = xs.to_list()?;
+        let i = i.as_int()?;
+        if i < 0 {
+            Err(ErrorKind::IndexOutOfBounds { index: i })
+        } else {
+            match xs.get(i as usize) {
+                Some(x) => Ok(x.clone()),
+                None => Err(ErrorKind::IndexOutOfBounds { index: i }),
+            }
+        }
+    }
+
+    #[builtin("filter")]
+    async fn builtin_filter(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
+        let list: NixList = list.to_list()?;
+        let mut out = imbl::Vector::new();
+
+        for value in list {
+            let result = generators::request_call_with(&co, pred.clone(), [value.clone()]).await;
+            let verdict = try_value!(generators::request_force(&co, result).await);
+            if verdict.as_bool()? {
+                out.push_back(value);
+            }
+        }
+
+        Ok(Value::List(out.into()))
+    }
+
+    #[builtin("floor")]
+    async fn builtin_floor(co: GenCo, double: Value) -> Result<Value, ErrorKind> {
+        Ok(Value::Integer(double.as_float()?.floor() as i64))
+    }
+
+    #[builtin("foldl'")]
+    async fn builtin_foldl(
+        co: GenCo,
+        op: Value,
+        #[lazy] nul: Value,
+        list: Value,
+    ) -> Result<Value, ErrorKind> {
+        let mut nul = nul;
+        let list = list.to_list()?;
+        for val in list {
+            // Every call of `op` is forced immediately, but `nul` is not, see
+            // https://github.com/NixOS/nix/blob/940e9eb8/src/libexpr/primops.cc#L3069-L3070C36
+            // and our tests for foldl'.
+            nul = generators::request_call_with(&co, op.clone(), [nul, val]).await;
+            nul = generators::request_force(&co, nul).await;
+            if let c @ Value::Catchable(_) = nul {
+                return Ok(c);
+            }
+        }
+
+        Ok(nul)
+    }
+
+    #[builtin("functionArgs")]
+    async fn builtin_function_args(co: GenCo, f: Value) -> Result<Value, ErrorKind> {
+        let lambda = &f.as_closure()?.lambda();
+        let formals = if let Some(formals) = &lambda.formals {
+            formals
+        } else {
+            return Ok(Value::attrs(NixAttrs::empty()));
+        };
+        Ok(Value::attrs(NixAttrs::from_iter(
+            formals.arguments.iter().map(|(k, v)| (k.clone(), (*v))),
+        )))
+    }
+
+    #[builtin("fromJSON")]
+    async fn builtin_from_json(co: GenCo, json: Value) -> Result<Value, ErrorKind> {
+        let json_str = json.to_str()?;
+
+        serde_json::from_slice(&json_str).map_err(|err| err.into())
+    }
+
+    #[builtin("toJSON")]
+    async fn builtin_to_json(co: GenCo, val: Value) -> Result<Value, ErrorKind> {
+        match val.into_contextful_json(&co).await? {
+            Err(cek) => Ok(Value::from(cek)),
+            Ok((json_value, ctx)) => {
+                let json_str = serde_json::to_string(&json_value)?;
+                Ok(NixString::new_context_from(ctx, json_str).into())
+            }
+        }
+    }
+
+    #[builtin("fromTOML")]
+    async fn builtin_from_toml(co: GenCo, toml: Value) -> Result<Value, ErrorKind> {
+        let toml_str = toml.to_str()?;
+
+        toml::from_str(toml_str.to_str()?).map_err(|err| err.into())
+    }
+
+    #[builtin("filterSource")]
+    #[allow(non_snake_case)]
+    async fn builtin_filterSource(_co: GenCo, #[lazy] _e: Value) -> Result<Value, ErrorKind> {
+        // TODO: implement for nixpkgs compatibility
+        Ok(Value::from(CatchableErrorKind::UnimplementedFeature(
+            "filterSource".into(),
+        )))
+    }
+
+    #[builtin("genericClosure")]
+    async fn builtin_generic_closure(co: GenCo, input: Value) -> Result<Value, ErrorKind> {
+        let attrs = input.to_attrs()?;
+
+        // The work set is maintained as a VecDeque because new items
+        // are popped from the front.
+        let mut work_set: VecDeque<Value> =
+            generators::request_force(&co, attrs.select_required("startSet")?.clone())
+                .await
+                .to_list()?
+                .into_iter()
+                .collect();
+
+        let operator = attrs.select_required("operator")?;
+
+        let mut res = imbl::Vector::new();
+        let mut done_keys: Vec<Value> = vec![];
+
+        while let Some(val) = work_set.pop_front() {
+            let val = generators::request_force(&co, val).await;
+            let attrs = val.to_attrs()?;
+            let key = attrs.select_required("key")?;
+
+            let value_missing = bgc_insert_key(&co, key.clone(), &mut done_keys).await?;
+
+            if let Err(cek) = value_missing {
+                return Ok(Value::Catchable(Box::new(cek)));
+            }
+
+            if let Ok(false) = value_missing {
+                continue;
+            }
+
+            res.push_back(val.clone());
+
+            let op_result = generators::request_force(
+                &co,
+                generators::request_call_with(&co, operator.clone(), [val]).await,
+            )
+            .await;
+
+            work_set.extend(op_result.to_list()?.into_iter());
+        }
+
+        Ok(Value::List(NixList::from(res)))
+    }
+
+    #[builtin("genList")]
+    async fn builtin_gen_list(
+        co: GenCo,
+        // Nix 2.3 doesn't propagate failures here
+        #[lazy] generator: Value,
+        length: Value,
+    ) -> Result<Value, ErrorKind> {
+        let mut out = imbl::Vector::<Value>::new();
+        let len = length.as_int()?;
+        // the best span we can get…
+        let span = generators::request_span(&co).await;
+
+        for i in 0..len {
+            let val = Value::Thunk(Thunk::new_suspended_call(
+                generator.clone(),
+                i.into(),
+                span.clone(),
+            ));
+            out.push_back(val);
+        }
+
+        Ok(Value::List(out.into()))
+    }
+
+    #[builtin("getAttr")]
+    async fn builtin_get_attr(co: GenCo, key: Value, set: Value) -> Result<Value, ErrorKind> {
+        let k = key.to_str()?;
+        let xs = set.to_attrs()?;
+
+        match xs.select(&k) {
+            Some(x) => Ok(x.clone()),
+            None => Err(ErrorKind::AttributeNotFound {
+                name: k.to_string(),
+            }),
+        }
+    }
+
+    #[builtin("groupBy")]
+    async fn builtin_group_by(co: GenCo, f: Value, list: Value) -> Result<Value, ErrorKind> {
+        let mut res: BTreeMap<NixString, imbl::Vector<Value>> = BTreeMap::new();
+        for val in list.to_list()? {
+            let key = try_value!(
+                generators::request_force(
+                    &co,
+                    generators::request_call_with(&co, f.clone(), [val.clone()]).await,
+                )
+                .await
+            )
+            .to_str()?;
+
+            res.entry(key).or_default().push_back(val);
+        }
+        Ok(Value::attrs(NixAttrs::from_iter(
+            res.into_iter()
+                .map(|(k, v)| (k, Value::List(NixList::from(v)))),
+        )))
+    }
+
+    #[builtin("hasAttr")]
+    async fn builtin_has_attr(co: GenCo, key: Value, set: Value) -> Result<Value, ErrorKind> {
+        let k = key.to_str()?;
+        let xs = set.to_attrs()?;
+
+        Ok(Value::Bool(xs.contains(&k)))
+    }
+
+    #[builtin("hasContext")]
+    #[allow(non_snake_case)]
+    async fn builtin_hasContext(co: GenCo, e: Value) -> Result<Value, ErrorKind> {
+        if e.is_catchable() {
+            return Ok(e);
+        }
+
+        let v = e.to_contextful_str()?;
+        Ok(Value::Bool(v.has_context()))
+    }
+
+    #[builtin("getContext")]
+    #[allow(non_snake_case)]
+    async fn builtin_getContext(co: GenCo, e: Value) -> Result<Value, ErrorKind> {
+        if e.is_catchable() {
+            return Ok(e);
+        }
+
+        // also forces the value
+        let span = generators::request_span(&co).await;
+        let v = e
+            .coerce_to_string(
+                co,
+                CoercionKind {
+                    strong: true,
+                    import_paths: true,
+                },
+                span,
+            )
+            .await?;
+        let s = v.to_contextful_str()?;
+
+        let groups = s
+            .iter_context()
+            .flat_map(|context| context.iter())
+            // Do not think `group_by` works here.
+            // `group_by` works on consecutive elements of the iterator.
+            // Due to how `HashSet` works (ordering is not guaranteed),
+            // this can become a source of non-determinism if you `group_by` naively.
+            // I know I did.
+            .into_grouping_map_by(|ctx_element| match ctx_element {
+                NixContextElement::Plain(spath) => spath,
+                NixContextElement::Single { derivation, .. } => derivation,
+                NixContextElement::Derivation(drv_path) => drv_path,
+            })
+            .collect::<Vec<_>>();
+
+        let elements = groups
+            .into_iter()
+            .map(|(key, group)| {
+                let mut outputs: Vector<NixString> = Vector::new();
+                let mut is_path = false;
+                let mut all_outputs = false;
+
+                for ctx_element in group {
+                    match ctx_element {
+                        NixContextElement::Plain(spath) => {
+                            debug_assert!(spath == key, "Unexpected group containing mixed keys, expected: {:?}, encountered {:?}", key, spath);
+                            is_path = true;
+                        }
+
+                        NixContextElement::Single { name, derivation } => {
+                            debug_assert!(derivation == key, "Unexpected group containing mixed keys, expected: {:?}, encountered {:?}", key, derivation);
+                            outputs.push_back(name.clone().into());
+                        }
+
+                        NixContextElement::Derivation(drv_path) => {
+                            debug_assert!(drv_path == key, "Unexpected group containing mixed keys, expected: {:?}, encountered {:?}", key, drv_path);
+                            all_outputs = true;
+                        }
+                    }
+                }
+
+                // FIXME(raitobezarius): is there a better way to construct an attribute set
+                // conditionally?
+                let mut vec_attrs: Vec<(&str, Value)> = Vec::new();
+
+                if is_path {
+                    vec_attrs.push(("path", true.into()));
+                }
+
+                if all_outputs {
+                    vec_attrs.push(("allOutputs", true.into()));
+                }
+
+                if !outputs.is_empty() {
+                    outputs.sort();
+                    vec_attrs.push(("outputs", Value::List(outputs
+                                .into_iter()
+                                .map(|s| s.into())
+                                .collect::<Vector<Value>>()
+                                .into()
+                    )));
+                }
+
+                (key.clone(), Value::attrs(NixAttrs::from_iter(vec_attrs.into_iter())))
+            });
+
+        Ok(Value::attrs(NixAttrs::from_iter(elements)))
+    }
+
+    #[builtin("appendContext")]
+    #[allow(non_snake_case)]
+    async fn builtin_appendContext(
+        co: GenCo,
+        origin: Value,
+        added_context: Value,
+    ) -> Result<Value, ErrorKind> {
+        // `appendContext` is a "grow" context function.
+        // It cannot remove a context element, neither replace a piece of its contents.
+        //
+        // Growing context is always a safe operation, there's no loss of dependency tracking
+        // information.
+        //
+        // This is why this operation is not prefixed by `unsafe` and is deemed *safe*.
+        // Nonetheless, it is possible to craft nonsensical context elements referring
+        // to inexistent derivations, output paths or output names.
+        //
+        // In Nix, those nonsensical context elements are partially mitigated by checking
+        // that various parameters are indeed syntatically valid store paths in the context, i.e.
+        // starting with the same prefix as `builtins.storeDir`, or ending with `.drv`.
+        // In addition, if writing to the store is possible (evaluator not in read-only mode), Nix
+        // will realize some paths and ensures they are present in the store.
+        //
+        // In this implementation, we do none of that, no syntax checks, no realization.
+        // The next `TODO` are the checks that Nix implements.
+        let mut ctx_elements: HashSet<NixContextElement> = HashSet::new();
+        let span = generators::request_span(&co).await;
+        let origin = origin
+            .coerce_to_string(
+                co,
+                CoercionKind {
+                    strong: true,
+                    import_paths: true,
+                },
+                span,
+            )
+            .await?;
+        let mut origin = origin.to_contextful_str()?;
+
+        let added_context = added_context.to_attrs()?;
+        for (context_key, context_element) in added_context.into_iter() {
+            // Invariant checks:
+            // - TODO: context_key must be a syntactically valid store path.
+            // - Perform a deep force `context_element`.
+            let context_element = context_element.to_attrs()?;
+            if let Some(path) = context_element.select("path") {
+                if path.as_bool()? {
+                    ctx_elements.insert(NixContextElement::Plain(context_key.to_string()));
+                }
+            }
+            if let Some(all_outputs) = context_element.select("allOutputs") {
+                if all_outputs.as_bool()? {
+                    // TODO: check if `context_key` is a derivation path.
+                    // This may require realization.
+                    ctx_elements.insert(NixContextElement::Derivation(context_key.to_string()));
+                }
+            }
+            if let Some(some_outputs) = context_element.select("outputs") {
+                let some_outputs = some_outputs.to_list()?;
+                // TODO: check if `context_key` is a derivation path.
+                // This may require realization.
+                for output in some_outputs.into_iter() {
+                    let output = output.to_str()?;
+                    ctx_elements.insert(NixContextElement::Single {
+                        derivation: context_key.to_string(),
+                        name: output.to_string(),
+                    });
+                }
+            }
+        }
+
+        if let Some(origin_ctx) = origin.context_mut() {
+            // FUTUREWORK(performance): avoid this clone
+            // and extend in-place.
+            *origin_ctx = origin_ctx.clone().join(&mut ctx_elements.into());
+        }
+
+        Ok(origin.into())
+    }
+
+    #[builtin("hashString")]
+    async fn builtin_hash_string(co: GenCo, algo: Value, s: Value) -> Result<Value, ErrorKind> {
+        hash_nix_string(algo.to_str()?, std::io::Cursor::new(s.to_str()?)).map(Value::from)
+    }
+
+    #[builtin("head")]
+    async fn builtin_head(co: GenCo, list: Value) -> Result<Value, ErrorKind> {
+        if list.is_catchable() {
+            return Ok(list);
+        }
+
+        match list.to_list()?.get(0) {
+            Some(x) => Ok(x.clone()),
+            None => Err(ErrorKind::IndexOutOfBounds { index: 0 }),
+        }
+    }
+
+    #[builtin("intersectAttrs")]
+    async fn builtin_intersect_attrs(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        if x.is_catchable() {
+            return Ok(x);
+        }
+        if y.is_catchable() {
+            return Ok(y);
+        }
+        let left_set = x.to_attrs()?;
+        if left_set.is_empty() {
+            return Ok(Value::attrs(NixAttrs::empty()));
+        }
+        let mut left_keys = left_set.keys();
+
+        let right_set = y.to_attrs()?;
+        if right_set.is_empty() {
+            return Ok(Value::attrs(NixAttrs::empty()));
+        }
+        let mut right_keys = right_set.keys();
+
+        let mut out: OrdMap<NixString, Value> = OrdMap::new();
+
+        // Both iterators have at least one entry
+        let mut left = left_keys.next().unwrap();
+        let mut right = right_keys.next().unwrap();
+
+        // Calculate the intersection of the attribute sets by simultaneously
+        // advancing two key iterators, and inserting into the result set from
+        // the right side when the keys match. Iteration over Nix attribute sets
+        // is in sorted lexicographical order, so we can advance either iterator
+        // until it "catches up" with its counterpart.
+        //
+        // Only when keys match are the key and value clones actually allocated.
+        //
+        // We opted for this implementation over simpler ones because of the
+        // heavy use of this function in nixpkgs.
+        loop {
+            if left == right {
+                // We know that the key exists in the set, and can
+                // skip the check instructions.
+                unsafe {
+                    out.insert(
+                        right.clone(),
+                        right_set.select(right).unwrap_unchecked().clone(),
+                    );
+                }
+
+                left = match left_keys.next() {
+                    Some(x) => x,
+                    None => break,
+                };
+
+                right = match right_keys.next() {
+                    Some(x) => x,
+                    None => break,
+                };
+
+                continue;
+            }
+
+            if left < right {
+                left = match left_keys.next() {
+                    Some(x) => x,
+                    None => break,
+                };
+                continue;
+            }
+
+            if right < left {
+                right = match right_keys.next() {
+                    Some(x) => x,
+                    None => break,
+                };
+                continue;
+            }
+        }
+
+        Ok(Value::attrs(out.into()))
+    }
+
+    #[builtin("isAttrs")]
+    async fn builtin_is_attrs(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        // TODO(edef): make this beautiful
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        Ok(Value::Bool(matches!(value, Value::Attrs(_))))
+    }
+
+    #[builtin("isBool")]
+    async fn builtin_is_bool(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        Ok(Value::Bool(matches!(value, Value::Bool(_))))
+    }
+
+    #[builtin("isFloat")]
+    async fn builtin_is_float(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        Ok(Value::Bool(matches!(value, Value::Float(_))))
+    }
+
+    #[builtin("isFunction")]
+    async fn builtin_is_function(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        Ok(Value::Bool(matches!(
+            value,
+            Value::Closure(_) | Value::Builtin(_)
+        )))
+    }
+
+    #[builtin("isInt")]
+    async fn builtin_is_int(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        Ok(Value::Bool(matches!(value, Value::Integer(_))))
+    }
+
+    #[builtin("isList")]
+    async fn builtin_is_list(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        Ok(Value::Bool(matches!(value, Value::List(_))))
+    }
+
+    #[builtin("isNull")]
+    async fn builtin_is_null(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        Ok(Value::Bool(matches!(value, Value::Null)))
+    }
+
+    #[builtin("isPath")]
+    async fn builtin_is_path(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        Ok(Value::Bool(matches!(value, Value::Path(_))))
+    }
+
+    #[builtin("isString")]
+    async fn builtin_is_string(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        Ok(Value::Bool(matches!(value, Value::String(_))))
+    }
+
+    #[builtin("length")]
+    async fn builtin_length(co: GenCo, list: Value) -> Result<Value, ErrorKind> {
+        if list.is_catchable() {
+            return Ok(list);
+        }
+        Ok(Value::Integer(list.to_list()?.len() as i64))
+    }
+
+    #[builtin("lessThan")]
+    async fn builtin_less_than(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        let span = generators::request_span(&co).await;
+        match x.nix_cmp_ordering(y, co, span).await? {
+            Err(cek) => Ok(Value::from(cek)),
+            Ok(Ordering::Less) => Ok(Value::Bool(true)),
+            Ok(_) => Ok(Value::Bool(false)),
+        }
+    }
+
+    #[builtin("listToAttrs")]
+    async fn builtin_list_to_attrs(co: GenCo, list: Value) -> Result<Value, ErrorKind> {
+        let list = list.to_list()?;
+        let mut map = BTreeMap::new();
+        for val in list {
+            let attrs = try_value!(generators::request_force(&co, val).await).to_attrs()?;
+            let name = try_value!(
+                generators::request_force(&co, attrs.select_required("name")?.clone()).await
+            )
+            .to_str()?;
+            let value = attrs.select_required("value")?.clone();
+            // Map entries earlier in the list take precedence over entries later in the list
+            map.entry(name).or_insert(value);
+        }
+        Ok(Value::attrs(NixAttrs::from_iter(map.into_iter())))
+    }
+
+    #[builtin("map")]
+    async fn builtin_map(co: GenCo, #[lazy] f: Value, list: Value) -> Result<Value, ErrorKind> {
+        let mut out = imbl::Vector::<Value>::new();
+
+        // the best span we can get…
+        let span = generators::request_span(&co).await;
+
+        for val in list.to_list()? {
+            let result = Value::Thunk(Thunk::new_suspended_call(f.clone(), val, span.clone()));
+            out.push_back(result)
+        }
+
+        Ok(Value::List(out.into()))
+    }
+
+    #[builtin("mapAttrs")]
+    async fn builtin_map_attrs(
+        co: GenCo,
+        #[lazy] f: Value,
+        attrs: Value,
+    ) -> Result<Value, ErrorKind> {
+        let attrs = attrs.to_attrs()?;
+        let mut out = imbl::OrdMap::new();
+
+        // the best span we can get…
+        let span = generators::request_span(&co).await;
+
+        for (key, value) in attrs.into_iter() {
+            let result = Value::Thunk(Thunk::new_suspended_call(
+                f.clone(),
+                key.clone().into(),
+                span.clone(),
+            ));
+            let result = Value::Thunk(Thunk::new_suspended_call(result, value, span.clone()));
+
+            out.insert(key, result);
+        }
+
+        Ok(Value::attrs(out.into()))
+    }
+
+    #[builtin("match")]
+    async fn builtin_match(co: GenCo, regex: Value, str: Value) -> Result<Value, ErrorKind> {
+        let s = str;
+        if s.is_catchable() {
+            return Ok(s);
+        }
+        let s = s.to_contextful_str()?;
+        let re = regex;
+        if re.is_catchable() {
+            return Ok(re);
+        }
+        let re = re.to_str()?;
+        let re: Regex = Regex::new(&format!("^{}$", re.to_str()?)).unwrap();
+        match re.captures(s.to_str()?) {
+            Some(caps) => Ok(Value::List(
+                caps.iter()
+                    .skip(1)
+                    .map(|grp| {
+                        // Surprisingly, Nix does not propagate
+                        // the original context here.
+                        // Though, it accepts contextful strings as an argument.
+                        // An example of such behaviors in nixpkgs
+                        // can be observed in make-initrd.nix when it comes
+                        // to compressors which are matched over their full command
+                        // and then a compressor name will be extracted from that.
+                        grp.map(|g| Value::from(g.as_str())).unwrap_or(Value::Null)
+                    })
+                    .collect::<imbl::Vector<Value>>()
+                    .into(),
+            )),
+            None => Ok(Value::Null),
+        }
+    }
+
+    #[builtin("mul")]
+    async fn builtin_mul(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        arithmetic_op!(&x, &y, *)
+    }
+
+    #[builtin("parseDrvName")]
+    async fn builtin_parse_drv_name(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
+        if s.is_catchable() {
+            return Ok(s);
+        }
+
+        // This replicates cppnix's (mis?)handling of codepoints
+        // above U+007f following 0x2d ('-')
+        let s = s.to_str()?;
+        let slice: &[u8] = s.as_ref();
+        let (name, dash_and_version) = slice.split_at(
+            slice
+                .windows(2)
+                .enumerate()
+                .find_map(|x| match x {
+                    (idx, [b'-', c1]) if !c1.is_ascii_alphabetic() => Some(idx),
+                    _ => None,
+                })
+                .unwrap_or(slice.len()),
+        );
+        let version = dash_and_version
+            .split_first()
+            .map(|x| core::str::from_utf8(x.1))
+            .unwrap_or(Ok(""))?;
+        Ok(Value::attrs(NixAttrs::from_iter(
+            [("name", core::str::from_utf8(name)?), ("version", version)].into_iter(),
+        )))
+    }
+
+    #[builtin("partition")]
+    async fn builtin_partition(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
+        let mut right: imbl::Vector<Value> = Default::default();
+        let mut wrong: imbl::Vector<Value> = Default::default();
+
+        let list: NixList = list.to_list()?;
+        for elem in list {
+            let result = generators::request_call_with(&co, pred.clone(), [elem.clone()]).await;
+
+            if try_value!(generators::request_force(&co, result).await).as_bool()? {
+                right.push_back(elem);
+            } else {
+                wrong.push_back(elem);
+            };
+        }
+
+        let res = [
+            ("right", Value::List(NixList::from(right))),
+            ("wrong", Value::List(NixList::from(wrong))),
+        ];
+
+        Ok(Value::attrs(NixAttrs::from_iter(res.into_iter())))
+    }
+
+    #[builtin("removeAttrs")]
+    async fn builtin_remove_attrs(
+        co: GenCo,
+        attrs: Value,
+        keys: Value,
+    ) -> Result<Value, ErrorKind> {
+        let attrs = attrs.to_attrs()?;
+        let keys = keys
+            .to_list()?
+            .into_iter()
+            .map(|v| v.to_str())
+            .collect::<Result<HashSet<_>, _>>()?;
+        let res = attrs.iter().filter_map(|(k, v)| {
+            if !keys.contains(k) {
+                Some((k.clone(), v.clone()))
+            } else {
+                None
+            }
+        });
+        Ok(Value::attrs(NixAttrs::from_iter(res)))
+    }
+
+    #[builtin("replaceStrings")]
+    async fn builtin_replace_strings(
+        co: GenCo,
+        from: Value,
+        to: Value,
+        s: Value,
+    ) -> Result<Value, ErrorKind> {
+        let from = from.to_list()?;
+        for val in &from {
+            try_value!(generators::request_force(&co, val.clone()).await);
+        }
+
+        let to = to.to_list()?;
+        for val in &to {
+            try_value!(generators::request_force(&co, val.clone()).await);
+        }
+
+        let mut string = s.to_contextful_str()?;
+
+        let mut res = BString::default();
+
+        let mut i: usize = 0;
+        let mut empty_string_replace = false;
+        let mut context = NixContext::new();
+
+        if let Some(string_context) = string.context_mut() {
+            context = context.join(string_context);
+        }
+
+        // This can't be implemented using Rust's string.replace() as
+        // well as a map because we need to handle errors with results
+        // as well as "reset" the iterator to zero for the replacement
+        // everytime there's a successful match.
+        // Also, Rust's string.replace allocates a new string
+        // on every call which is not preferable.
+        'outer: while i < string.len() {
+            // Try a match in all the from strings
+            for elem in std::iter::zip(from.iter(), to.iter()) {
+                let from = elem.0.to_contextful_str()?;
+                let mut to = elem.1.to_contextful_str()?;
+
+                if i + from.len() > string.len() {
+                    continue;
+                }
+
+                // We already applied a from->to with an empty from
+                // transformation.
+                // Let's skip it so that we don't loop infinitely
+                if empty_string_replace && from.is_empty() {
+                    continue;
+                }
+
+                // if we match the `from` string, let's replace
+                if string[i..i + from.len()] == *from {
+                    res.push_str(&to);
+                    i += from.len();
+                    if let Some(to_ctx) = to.context_mut() {
+                        context = context.join(to_ctx);
+                    }
+
+                    // remember if we applied the empty from->to
+                    empty_string_replace = from.is_empty();
+
+                    continue 'outer;
+                }
+            }
+
+            // If we don't match any `from`, we simply add a character
+            res.push_str(&string[i..i + 1]);
+            i += 1;
+
+            // Since we didn't apply anything transformation,
+            // we reset the empty string replacement
+            empty_string_replace = false;
+        }
+
+        // Special case when the string is empty or at the string's end
+        // and one of the from is also empty
+        for elem in std::iter::zip(from.iter(), to.iter()) {
+            let from = elem.0.to_contextful_str()?;
+            // We mutate `to` by consuming its context
+            // if we perform a successful replacement.
+            // Therefore, it's fine if `to` was mutate and we reuse it here.
+            // We don't need to merge again the context, it's already in the right state.
+            let mut to = elem.1.to_contextful_str()?;
+
+            if from.is_empty() {
+                res.push_str(&to);
+                if let Some(to_ctx) = to.context_mut() {
+                    context = context.join(to_ctx);
+                }
+                break;
+            }
+        }
+
+        Ok(Value::from(NixString::new_context_from(context, res)))
+    }
+
+    #[builtin("seq")]
+    async fn builtin_seq(co: GenCo, _x: Value, y: Value) -> Result<Value, ErrorKind> {
+        // The builtin calling infra has already forced both args for us, so
+        // we just return the second and ignore the first
+        Ok(y)
+    }
+
+    #[builtin("split")]
+    async fn builtin_split(co: GenCo, regex: Value, str: Value) -> Result<Value, ErrorKind> {
+        if str.is_catchable() {
+            return Ok(str);
+        }
+
+        if regex.is_catchable() {
+            return Ok(regex);
+        }
+
+        let s = str.to_contextful_str()?;
+        let text = s.to_str()?;
+        let re = regex.to_str()?;
+        let re = Regex::new(re.to_str()?).unwrap();
+        let mut capture_locations = re.capture_locations();
+        let num_captures = capture_locations.len();
+        let mut ret = imbl::Vector::new();
+        let mut pos = 0;
+
+        while let Some(thematch) = re.captures_read_at(&mut capture_locations, text, pos) {
+            // push the unmatched characters preceding the match
+            ret.push_back(Value::from(NixString::new_inherit_context_from(
+                &s,
+                &text[pos..thematch.start()],
+            )));
+
+            // Push a list with one element for each capture
+            // group in the regex, containing the characters
+            // matched by that capture group, or null if no match.
+            // We skip capture 0; it represents the whole match.
+            let v: imbl::Vector<Value> = (1..num_captures)
+                .map(|i| capture_locations.get(i))
+                .map(|o| {
+                    o.map(|(start, end)| {
+                        // Here, a surprising thing happens: we silently discard the original
+                        // context. This is as intended, Nix does the same.
+                        Value::from(&text[start..end])
+                    })
+                    .unwrap_or(Value::Null)
+                })
+                .collect();
+            ret.push_back(Value::List(NixList::from(v)));
+            pos = thematch.end();
+        }
+
+        // push the unmatched characters following the last match
+        // Here, a surprising thing happens: we silently discard the original
+        // context. This is as intended, Nix does the same.
+        ret.push_back(Value::from(&text[pos..]));
+
+        Ok(Value::List(NixList::from(ret)))
+    }
+
+    #[builtin("sort")]
+    async fn builtin_sort(co: GenCo, comparator: Value, list: Value) -> Result<Value, ErrorKind> {
+        let list = list.to_list()?;
+        let mut len = list.len();
+        let mut data = list.into_inner();
+
+        // Asynchronous sorting algorithm in which the comparator can make use of
+        // VM requests (required as `builtins.sort` uses comparators written in
+        // Nix).
+        //
+        // This is a simple, optimised bubble sort implementation. The choice of
+        // algorithm is constrained by the comparator in Nix not being able to
+        // yield equality, and us being unable to use the standard library
+        // implementation of sorting (which is a lot longer, but a lot more
+        // efficient) here.
+        // TODO(amjoseph): Investigate potential impl in Nix code, or Tvix bytecode.
+        loop {
+            let mut new_len = 0;
+            for i in 1..len {
+                if try_value!(
+                    generators::request_force(
+                        &co,
+                        generators::request_call_with(
+                            &co,
+                            comparator.clone(),
+                            [data[i].clone(), data[i - 1].clone()],
+                        )
+                        .await,
+                    )
+                    .await
+                )
+                .as_bool()
+                .context("evaluating comparator in `builtins.sort`")?
+                {
+                    data.swap(i, i - 1);
+                    new_len = i;
+                }
+            }
+
+            if new_len == 0 {
+                break;
+            }
+
+            len = new_len;
+        }
+
+        Ok(Value::List(data.into()))
+    }
+
+    #[builtin("splitVersion")]
+    async fn builtin_split_version(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
+        if s.is_catchable() {
+            return Ok(s);
+        }
+        let s = s.to_str()?;
+        let s = VersionPartsIter::new((&s).into());
+
+        let parts = s
+            .map(|s| {
+                Value::from(match s {
+                    VersionPart::Number(n) => n,
+                    VersionPart::Word(w) => w,
+                })
+            })
+            .collect::<Vec<Value>>();
+        Ok(Value::List(NixList::construct(parts.len(), parts)))
+    }
+
+    #[builtin("stringLength")]
+    async fn builtin_string_length(co: GenCo, #[lazy] s: Value) -> Result<Value, ErrorKind> {
+        // also forces the value
+        let span = generators::request_span(&co).await;
+        let s = s
+            .coerce_to_string(
+                co,
+                CoercionKind {
+                    strong: false,
+                    import_paths: true,
+                },
+                span,
+            )
+            .await?;
+
+        if s.is_catchable() {
+            return Ok(s);
+        }
+
+        Ok(Value::Integer(s.to_contextful_str()?.len() as i64))
+    }
+
+    #[builtin("sub")]
+    async fn builtin_sub(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
+        arithmetic_op!(&x, &y, -)
+    }
+
+    #[builtin("substring")]
+    async fn builtin_substring(
+        co: GenCo,
+        start: Value,
+        len: Value,
+        s: Value,
+    ) -> Result<Value, ErrorKind> {
+        let beg = start.as_int()?;
+        let len = len.as_int()?;
+        let span = generators::request_span(&co).await;
+        let x = s
+            .coerce_to_string(
+                co,
+                CoercionKind {
+                    strong: false,
+                    import_paths: true,
+                },
+                span,
+            )
+            .await?;
+        if x.is_catchable() {
+            return Ok(x);
+        }
+        let x = x.to_contextful_str()?;
+
+        if beg < 0 {
+            return Err(ErrorKind::IndexOutOfBounds { index: beg });
+        }
+        let beg = beg as usize;
+
+        // Nix doesn't assert that the length argument is
+        // non-negative when the starting index is GTE the
+        // string's length.
+        if beg >= x.len() {
+            return Ok(Value::from(NixString::new_inherit_context_from(
+                &x,
+                BString::default(),
+            )));
+        }
+
+        let end = if len < 0 {
+            x.len()
+        } else {
+            cmp::min(beg + (len as usize), x.len())
+        };
+
+        Ok(Value::from(NixString::new_inherit_context_from(
+            &x,
+            &x[beg..end],
+        )))
+    }
+
+    #[builtin("tail")]
+    async fn builtin_tail(co: GenCo, list: Value) -> Result<Value, ErrorKind> {
+        if list.is_catchable() {
+            return Ok(list);
+        }
+
+        let xs = list.to_list()?;
+
+        if xs.is_empty() {
+            Err(ErrorKind::TailEmptyList)
+        } else {
+            let output = xs.into_iter().skip(1).collect::<Vec<_>>();
+            Ok(Value::List(NixList::construct(output.len(), output)))
+        }
+    }
+
+    #[builtin("throw")]
+    async fn builtin_throw(co: GenCo, message: Value) -> Result<Value, ErrorKind> {
+        // If it's already some error, let's propagate it immediately.
+        if message.is_catchable() {
+            return Ok(message);
+        }
+        // TODO(sterni): coerces to string
+        // We do not care about the context here explicitly.
+        Ok(Value::from(CatchableErrorKind::Throw(
+            message.to_contextful_str()?.to_string().into(),
+        )))
+    }
+
+    #[builtin("toString")]
+    async fn builtin_to_string(co: GenCo, #[lazy] x: Value) -> Result<Value, ErrorKind> {
+        // TODO(edef): please fix me w.r.t. to catchability.
+        // coerce_to_string forces for us
+        // FIXME: should `coerce_to_string` preserve context?
+        // it does for now.
+        let span = generators::request_span(&co).await;
+        x.coerce_to_string(
+            co,
+            CoercionKind {
+                strong: true,
+                import_paths: false,
+            },
+            span,
+        )
+        .await
+    }
+
+    #[builtin("toXML")]
+    async fn builtin_to_xml(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+        let value = generators::request_deep_force(&co, value).await;
+        if value.is_catchable() {
+            return Ok(value);
+        }
+
+        let mut buf: Vec<u8> = vec![];
+        to_xml::value_to_xml(&mut buf, &value)?;
+        Ok(String::from_utf8(buf)?.into())
+    }
+
+    #[builtin("placeholder")]
+    async fn builtin_placeholder(co: GenCo, #[lazy] _x: Value) -> Result<Value, ErrorKind> {
+        generators::emit_warning_kind(&co, WarningKind::NotImplemented("builtins.placeholder"))
+            .await;
+        Ok("<builtins.placeholder-is-not-implemented-in-tvix-yet>".into())
+    }
+
+    #[builtin("trace")]
+    async fn builtin_trace(co: GenCo, message: Value, value: Value) -> Result<Value, ErrorKind> {
+        // TODO(grfn): `trace` should be pluggable and capturable, probably via a method on
+        // the VM
+        eprintln!("trace: {} :: {}", message, message.type_of());
+        Ok(value)
+    }
+
+    #[builtin("toPath")]
+    async fn builtin_to_path(co: GenCo, s: Value) -> Result<Value, ErrorKind> {
+        if s.is_catchable() {
+            return Ok(s);
+        }
+
+        match coerce_value_to_path(&co, s).await? {
+            Err(cek) => Ok(Value::from(cek)),
+            Ok(path) => {
+                let path: Value = crate::value::canon_path(path).into();
+                let span = generators::request_span(&co).await;
+                Ok(path
+                    .coerce_to_string(
+                        co,
+                        CoercionKind {
+                            strong: false,
+                            import_paths: false,
+                        },
+                        span,
+                    )
+                    .await?)
+            }
+        }
+    }
+
+    #[builtin("tryEval")]
+    async fn builtin_try_eval(co: GenCo, #[lazy] e: Value) -> Result<Value, ErrorKind> {
+        let res = match generators::request_try_force(&co, e).await {
+            Value::Catchable(_) => [("value", false.into()), ("success", false.into())],
+            value => [("value", value), ("success", true.into())],
+        };
+
+        Ok(Value::attrs(NixAttrs::from_iter(res.into_iter())))
+    }
+
+    #[builtin("typeOf")]
+    async fn builtin_type_of(co: GenCo, x: Value) -> Result<Value, ErrorKind> {
+        if x.is_catchable() {
+            return Ok(x);
+        }
+
+        Ok(Value::from(x.type_of()))
+    }
+}
+
+/// Internal helper function for genericClosure, determining whether a
+/// value has been seen before.
+async fn bgc_insert_key(
+    co: &GenCo,
+    key: Value,
+    done: &mut Vec<Value>,
+) -> Result<Result<bool, CatchableErrorKind>, ErrorKind> {
+    for existing in done.iter() {
+        match generators::check_equality(
+            co,
+            existing.clone(),
+            key.clone(),
+            // TODO(tazjin): not actually sure which semantics apply here
+            PointerEquality::ForbidAll,
+        )
+        .await?
+        {
+            Ok(true) => return Ok(Ok(false)),
+            Ok(false) => (),
+            Err(cek) => return Ok(Err(cek)),
+        }
+    }
+
+    done.push(key);
+    Ok(Ok(true))
+}
+
+/// The set of standard pure builtins in Nix, mostly concerned with
+/// data structure manipulation (string, attrs, list, etc. functions).
+pub fn pure_builtins() -> Vec<(&'static str, Value)> {
+    let mut result = pure_builtins::builtins();
+
+    // Pure-value builtins
+    result.push(("nixVersion", Value::from("2.3-compat-tvix-0.1")));
+    result.push(("langVersion", Value::Integer(6)));
+    result.push(("null", Value::Null));
+    result.push(("true", Value::Bool(true)));
+    result.push(("false", Value::Bool(false)));
+
+    result.push((
+        "currentSystem",
+        crate::systems::llvm_triple_to_nix_double(CURRENT_PLATFORM).into(),
+    ));
+
+    // TODO: implement for nixpkgs compatibility
+    result.push((
+        "__curPos",
+        Value::from(CatchableErrorKind::UnimplementedFeature("__curPos".into())),
+    ));
+
+    result
+}
+
+#[builtins]
+mod placeholder_builtins {
+    use super::*;
+
+    #[builtin("unsafeDiscardStringContext")]
+    async fn builtin_unsafe_discard_string_context(
+        co: GenCo,
+        s: Value,
+    ) -> Result<Value, ErrorKind> {
+        let span = generators::request_span(&co).await;
+        let mut v = s
+            .coerce_to_string(
+                co,
+                // It's weak because
+                // lists, integers, floats and null are not
+                // accepted as parameters.
+                CoercionKind {
+                    strong: false,
+                    import_paths: true,
+                },
+                span,
+            )
+            .await?
+            .to_contextful_str()?;
+        v.clear_context();
+        Ok(Value::from(v))
+    }
+
+    #[builtin("unsafeDiscardOutputDependency")]
+    async fn builtin_unsafe_discard_output_dependency(
+        co: GenCo,
+        s: Value,
+    ) -> Result<Value, ErrorKind> {
+        let span = generators::request_span(&co).await;
+        let mut v = s
+            .coerce_to_string(
+                co,
+                // It's weak because
+                // lists, integers, floats and null are not
+                // accepted as parameters.
+                CoercionKind {
+                    strong: false,
+                    import_paths: true,
+                },
+                span,
+            )
+            .await?
+            .to_contextful_str()?;
+
+        // If there's any context, we will swap any ... by a path one.
+        if let Some(ctx) = v.context_mut() {
+            let new_context: tvix_eval::NixContext = ctx
+                .iter()
+                .map(|elem| match elem {
+                    // FUTUREWORK(performance): ideally, we should either:
+                    // (a) do interior mutation of the existing context.
+                    // (b) let the structural sharing make those clones cheap.
+                    crate::NixContextElement::Derivation(drv_path) => {
+                        crate::NixContextElement::Plain(drv_path.to_string())
+                    }
+                    elem => elem.clone(),
+                })
+                .collect::<HashSet<_>>()
+                .into();
+
+            *ctx = new_context;
+        }
+
+        Ok(Value::from(v))
+    }
+
+    #[builtin("addErrorContext")]
+    async fn builtin_add_error_context(
+        co: GenCo,
+        #[lazy] _context: Value,
+        #[lazy] val: Value,
+    ) -> Result<Value, ErrorKind> {
+        generators::emit_warning_kind(&co, WarningKind::NotImplemented("builtins.addErrorContext"))
+            .await;
+        Ok(val)
+    }
+
+    #[builtin("unsafeGetAttrPos")]
+    async fn builtin_unsafe_get_attr_pos(
+        co: GenCo,
+        _name: Value,
+        _attrset: Value,
+    ) -> Result<Value, ErrorKind> {
+        generators::emit_warning_kind(
+            &co,
+            WarningKind::NotImplemented("builtins.unsafeGetAttrsPos"),
+        )
+        .await;
+        let res = [
+            ("line", 42.into()),
+            ("column", 42.into()),
+            ("file", Value::String("/deep/thought".into())),
+        ];
+        Ok(Value::attrs(NixAttrs::from_iter(res.into_iter())))
+    }
+}
+
+pub fn placeholders() -> Vec<(&'static str, Value)> {
+    placeholder_builtins::builtins()
+}
diff --git a/tvix/eval/src/builtins/to_xml.rs b/tvix/eval/src/builtins/to_xml.rs
new file mode 100644
index 0000000000..bb12cebfc9
--- /dev/null
+++ b/tvix/eval/src/builtins/to_xml.rs
@@ -0,0 +1,154 @@
+//! This module implements `builtins.toXML`, which is a serialisation
+//! of value information as well as internal tvix state that several
+//! things in nixpkgs rely on.
+
+use bstr::ByteSlice;
+use std::{io::Write, rc::Rc};
+use xml::writer::events::XmlEvent;
+use xml::writer::EmitterConfig;
+use xml::writer::EventWriter;
+
+use crate::{ErrorKind, Value};
+
+/// Recursively serialise a value to XML. The value *must* have been
+/// deep-forced before being passed to this function.
+pub fn value_to_xml<W: Write>(mut writer: W, value: &Value) -> Result<(), ErrorKind> {
+    let config = EmitterConfig {
+        perform_indent: true,
+        pad_self_closing: true,
+
+        // Nix uses single-quotes *only* in the document declaration,
+        // so we need to write it manually.
+        write_document_declaration: false,
+        ..Default::default()
+    };
+
+    // Write a literal document declaration, using C++-Nix-style
+    // single quotes.
+    writeln!(writer, "<?xml version='1.0' encoding='utf-8'?>")?;
+
+    let mut writer = EventWriter::new_with_config(writer, config);
+
+    writer.write(XmlEvent::start_element("expr"))?;
+    value_variant_to_xml(&mut writer, value)?;
+    writer.write(XmlEvent::end_element())?;
+
+    // Unwrap the writer to add the final newline that C++ Nix adds.
+    writeln!(writer.into_inner())?;
+
+    Ok(())
+}
+
+fn write_typed_value<W: Write, V: ToString>(
+    w: &mut EventWriter<W>,
+    name: &str,
+    value: V,
+) -> Result<(), ErrorKind> {
+    w.write(XmlEvent::start_element(name).attr("value", &value.to_string()))?;
+    w.write(XmlEvent::end_element())?;
+    Ok(())
+}
+
+fn value_variant_to_xml<W: Write>(w: &mut EventWriter<W>, value: &Value) -> Result<(), ErrorKind> {
+    match value {
+        Value::Thunk(t) => return value_variant_to_xml(w, &t.value()),
+
+        Value::Null => {
+            w.write(XmlEvent::start_element("null"))?;
+            w.write(XmlEvent::end_element())
+        }
+
+        Value::Bool(b) => return write_typed_value(w, "bool", b),
+        Value::Integer(i) => return write_typed_value(w, "int", i),
+        Value::Float(f) => return write_typed_value(w, "float", f),
+        Value::String(s) => return write_typed_value(w, "string", s.to_str()?),
+        Value::Path(p) => return write_typed_value(w, "path", p.to_string_lossy()),
+
+        Value::List(list) => {
+            w.write(XmlEvent::start_element("list"))?;
+
+            for elem in list.into_iter() {
+                value_variant_to_xml(w, elem)?;
+            }
+
+            w.write(XmlEvent::end_element())
+        }
+
+        Value::Attrs(attrs) => {
+            w.write(XmlEvent::start_element("attrs"))?;
+
+            for elem in attrs.iter() {
+                w.write(XmlEvent::start_element("attr").attr("name", &elem.0.to_str_lossy()))?;
+                value_variant_to_xml(w, elem.1)?;
+                w.write(XmlEvent::end_element())?;
+            }
+
+            w.write(XmlEvent::end_element())
+        }
+
+        Value::Closure(c) => {
+            w.write(XmlEvent::start_element("function"))?;
+
+            match &c.lambda.formals {
+                Some(formals) => {
+                    let mut attrspat = XmlEvent::start_element("attrspat");
+                    if formals.ellipsis {
+                        attrspat = attrspat.attr("ellipsis", "1");
+                    }
+                    if let Some(ref name) = &formals.name {
+                        attrspat = attrspat.attr("name", name.as_str());
+                    }
+
+                    w.write(attrspat)?;
+
+                    for arg in formals.arguments.iter() {
+                        w.write(
+                            XmlEvent::start_element("attr").attr("name", &arg.0.to_str_lossy()),
+                        )?;
+                        w.write(XmlEvent::end_element())?;
+                    }
+
+                    w.write(XmlEvent::end_element())?;
+                }
+                None => {
+                    // TODO(tazjin): tvix does not currently persist function
+                    // argument names anywhere (whereas we do for formals, as
+                    // that is required for other runtime behaviour). Because of
+                    // this the implementation here is fake, always returning
+                    // the same argument name.
+                    //
+                    // If we don't want to persist the data, we can re-parse the
+                    // AST from the spans of the lambda's bytecode and figure it
+                    // out that way, but it needs some investigating.
+                    w.write(XmlEvent::start_element("varpat").attr("name", /* fake: */ "x"))?;
+                    w.write(XmlEvent::end_element())?;
+                }
+            }
+
+            w.write(XmlEvent::end_element())
+        }
+
+        Value::Builtin(_) => {
+            w.write(XmlEvent::start_element("unevaluated"))?;
+            w.write(XmlEvent::end_element())
+        }
+
+        Value::AttrNotFound
+        | Value::Blueprint(_)
+        | Value::DeferredUpvalue(_)
+        | Value::UnresolvedPath(_)
+        | Value::Json(..)
+        | Value::FinaliseRequest(_) => {
+            return Err(ErrorKind::TvixBug {
+                msg: "internal value variant encountered in builtins.toXML",
+                metadata: Some(Rc::new(value.clone())),
+            })
+        }
+
+        Value::Catchable(_) => {
+            panic!("tvix bug: value_to_xml() called on a value which had not been deep-forced")
+        }
+    }?;
+
+    Ok(())
+}
diff --git a/tvix/eval/src/builtins/versions.rs b/tvix/eval/src/builtins/versions.rs
new file mode 100644
index 0000000000..6de5121424
--- /dev/null
+++ b/tvix/eval/src/builtins/versions.rs
@@ -0,0 +1,163 @@
+use std::cmp::Ordering;
+use std::iter::{once, Chain, Once};
+use std::ops::RangeInclusive;
+
+use bstr::{BStr, ByteSlice, B};
+
+/// Version strings can be broken up into Parts.
+/// One Part represents either a string of digits or characters.
+/// '.' and '_' represent deviders between parts and are not included in any part.
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum VersionPart<'a> {
+    Word(&'a BStr),
+    Number(&'a BStr),
+}
+
+impl PartialOrd for VersionPart<'_> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for VersionPart<'_> {
+    fn cmp(&self, other: &Self) -> Ordering {
+        match (self, other) {
+            (VersionPart::Number(s1), VersionPart::Number(s2)) => {
+                // Note: C++ Nix uses `int`, but probably doesn't make a difference
+                // We trust that the splitting was done correctly and parsing will work
+                let n1: u64 = s1.to_str_lossy().parse().unwrap();
+                let n2: u64 = s2.to_str_lossy().parse().unwrap();
+                n1.cmp(&n2)
+            }
+
+            // `pre` looses unless the other part is also a `pre`
+            (VersionPart::Word(x), VersionPart::Word(y)) if *x == B("pre") && *y == B("pre") => {
+                Ordering::Equal
+            }
+            (VersionPart::Word(x), _) if *x == B("pre") => Ordering::Less,
+            (_, VersionPart::Word(y)) if *y == B("pre") => Ordering::Greater,
+
+            // Number wins against Word
+            (VersionPart::Number(_), VersionPart::Word(_)) => Ordering::Greater,
+            (VersionPart::Word(_), VersionPart::Number(_)) => Ordering::Less,
+
+            (VersionPart::Word(w1), VersionPart::Word(w2)) => w1.cmp(w2),
+        }
+    }
+}
+
+/// Type used to hold information about a VersionPart during creation
+enum InternalPart {
+    Number { range: RangeInclusive<usize> },
+    Word { range: RangeInclusive<usize> },
+    Break,
+}
+
+/// An iterator which yields the parts of a version string.
+///
+/// This can then be directly used to compare two versions
+pub struct VersionPartsIter<'a> {
+    cached_part: InternalPart,
+    iter: bstr::CharIndices<'a>,
+    version: &'a BStr,
+}
+
+impl<'a> VersionPartsIter<'a> {
+    pub fn new(version: &'a BStr) -> Self {
+        Self {
+            cached_part: InternalPart::Break,
+            iter: version.char_indices(),
+            version,
+        }
+    }
+
+    /// Create an iterator that yields all version parts followed by an additional
+    /// `VersionPart::Word("")` part (i.e. you can think of this as
+    /// `builtins.splitVersion version ++ [ "" ]`). This is necessary, because
+    /// Nix's `compareVersions` is not entirely lexicographical: If we have two
+    /// equal versions, but one is longer, the longer one is only considered
+    /// greater if the first additional part of the longer version is not `pre`,
+    /// e.g. `2.3 > 2.3pre`. It is otherwise lexicographical, so peculiar behavior
+    /// like `2.3 < 2.3.0pre` ensues. Luckily for us, this means that we can
+    /// lexicographically compare two version strings, _if_ we append an extra
+    /// component to both versions.
+    pub fn new_for_cmp(version: &'a BStr) -> Chain<Self, Once<VersionPart>> {
+        Self::new(version).chain(once(VersionPart::Word("".into())))
+    }
+}
+
+impl<'a> Iterator for VersionPartsIter<'a> {
+    type Item = VersionPart<'a>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let char = self.iter.next();
+
+        if char.is_none() {
+            let cached_part = std::mem::replace(&mut self.cached_part, InternalPart::Break);
+            match cached_part {
+                InternalPart::Break => return None,
+                InternalPart::Number { range } => {
+                    return Some(VersionPart::Number(&self.version[range]))
+                }
+                InternalPart::Word { range } => {
+                    return Some(VersionPart::Word(&self.version[range]))
+                }
+            }
+        }
+
+        let (start, end, char) = char.unwrap();
+        match char {
+            // Divider encountered
+            '.' | '-' => {
+                let cached_part = std::mem::replace(&mut self.cached_part, InternalPart::Break);
+                match cached_part {
+                    InternalPart::Number { range } => {
+                        Some(VersionPart::Number(&self.version[range]))
+                    }
+                    InternalPart::Word { range } => Some(VersionPart::Word(&self.version[range])),
+                    InternalPart::Break => self.next(),
+                }
+            }
+
+            // digit encountered
+            _ if char.is_ascii_digit() => {
+                let cached_part = std::mem::replace(
+                    &mut self.cached_part,
+                    InternalPart::Number {
+                        range: start..=(end - 1),
+                    },
+                );
+                match cached_part {
+                    InternalPart::Number { range } => {
+                        self.cached_part = InternalPart::Number {
+                            range: *range.start()..=*range.end() + 1,
+                        };
+                        self.next()
+                    }
+                    InternalPart::Word { range } => Some(VersionPart::Word(&self.version[range])),
+                    InternalPart::Break => self.next(),
+                }
+            }
+
+            // char encountered
+            _ => {
+                let mut cached_part = InternalPart::Word {
+                    range: start..=(end - 1),
+                };
+                std::mem::swap(&mut cached_part, &mut self.cached_part);
+                match cached_part {
+                    InternalPart::Word { range } => {
+                        self.cached_part = InternalPart::Word {
+                            range: *range.start()..=*range.end() + char.len_utf8(),
+                        };
+                        self.next()
+                    }
+                    InternalPart::Number { range } => {
+                        Some(VersionPart::Number(&self.version[range]))
+                    }
+                    InternalPart::Break => self.next(),
+                }
+            }
+        }
+    }
+}
diff --git a/tvix/eval/src/chunk.rs b/tvix/eval/src/chunk.rs
new file mode 100644
index 0000000000..f1a35a6ce1
--- /dev/null
+++ b/tvix/eval/src/chunk.rs
@@ -0,0 +1,289 @@
+use std::io::Write;
+use std::ops::{Index, IndexMut};
+
+use crate::opcode::{CodeIdx, ConstantIdx, OpCode};
+use crate::value::Value;
+use crate::SourceCode;
+
+/// Represents a source location from which one or more operations
+/// were compiled.
+///
+/// The span itself is an index into a [codemap::CodeMap], and the
+/// structure tracks the number of operations that were yielded from
+/// the same span.
+///
+/// At error reporting time, it becomes possible to either just fetch
+/// the textual representation of that span from the codemap, or to
+/// even re-parse the AST using rnix to create more semantically
+/// interesting errors.
+#[derive(Clone, Debug, PartialEq)]
+struct SourceSpan {
+    /// Span into the [codemap::CodeMap].
+    span: codemap::Span,
+
+    /// Index of the first operation covered by this span.
+    start: usize,
+}
+
+/// A chunk is a representation of a sequence of bytecode
+/// instructions, associated constants and additional metadata as
+/// emitted by the compiler.
+#[derive(Debug, Default)]
+pub struct Chunk {
+    pub code: Vec<OpCode>,
+    pub constants: Vec<Value>,
+    spans: Vec<SourceSpan>,
+}
+
+impl Index<ConstantIdx> for Chunk {
+    type Output = Value;
+
+    fn index(&self, index: ConstantIdx) -> &Self::Output {
+        &self.constants[index.0]
+    }
+}
+
+impl Index<CodeIdx> for Chunk {
+    type Output = OpCode;
+
+    fn index(&self, index: CodeIdx) -> &Self::Output {
+        &self.code[index.0]
+    }
+}
+
+impl IndexMut<CodeIdx> for Chunk {
+    fn index_mut(&mut self, index: CodeIdx) -> &mut Self::Output {
+        &mut self.code[index.0]
+    }
+}
+
+impl Chunk {
+    pub fn push_op(&mut self, data: OpCode, span: codemap::Span) -> CodeIdx {
+        let idx = self.code.len();
+        self.code.push(data);
+        self.push_span(span, idx);
+        CodeIdx(idx)
+    }
+
+    /// Get the first span of a chunk, no questions asked.
+    pub fn first_span(&self) -> codemap::Span {
+        self.spans[0].span
+    }
+
+    /// Return a reference to the last op in the chunk, if any
+    pub fn last_op(&self) -> Option<&OpCode> {
+        self.code.last()
+    }
+
+    /// Pop the last operation from the chunk and clean up its tracked
+    /// span. Used when the compiler backtracks.
+    pub fn pop_op(&mut self) {
+        // Simply drop the last op.
+        self.code.pop();
+
+        if let Some(span) = self.spans.last() {
+            // If the last span started at this op, drop it.
+            if span.start == self.code.len() {
+                self.spans.pop();
+            }
+        }
+    }
+
+    pub fn push_constant(&mut self, data: Value) -> ConstantIdx {
+        let idx = self.constants.len();
+        self.constants.push(data);
+        ConstantIdx(idx)
+    }
+
+    /// Return a reference to the constant at the given [`ConstantIdx`]
+    pub fn get_constant(&self, constant: ConstantIdx) -> Option<&Value> {
+        self.constants.get(constant.0)
+    }
+
+    // Span tracking implementation
+
+    fn push_span(&mut self, span: codemap::Span, start: usize) {
+        match self.spans.last_mut() {
+            // We do not need to insert the same span again, as this
+            // instruction was compiled from the same span as the last
+            // one.
+            Some(last) if last.span == span => {}
+
+            // In all other cases, this is a new source span.
+            _ => self.spans.push(SourceSpan { span, start }),
+        }
+    }
+
+    /// Retrieve the [codemap::Span] from which the instruction at
+    /// `offset` was compiled.
+    pub fn get_span(&self, offset: CodeIdx) -> codemap::Span {
+        let position = self
+            .spans
+            .binary_search_by(|span| span.start.cmp(&offset.0));
+
+        let span = match position {
+            Ok(index) => &self.spans[index],
+            Err(index) => {
+                if index == 0 {
+                    &self.spans[0]
+                } else {
+                    &self.spans[index - 1]
+                }
+            }
+        };
+
+        span.span
+    }
+
+    /// Write the disassembler representation of the operation at
+    /// `idx` to the specified writer.
+    pub fn disassemble_op<W: Write>(
+        &self,
+        writer: &mut W,
+        source: &SourceCode,
+        width: usize,
+        idx: CodeIdx,
+    ) -> Result<(), std::io::Error> {
+        write!(writer, "{:#width$x}\t ", idx.0, width = width)?;
+
+        // Print continuation character if the previous operation was at
+        // the same line, otherwise print the line.
+        let line = source.get_line(self.get_span(idx));
+        if idx.0 > 0 && source.get_line(self.get_span(CodeIdx(idx.0 - 1))) == line {
+            write!(writer, "   |\t")?;
+        } else {
+            write!(writer, "{:4}\t", line)?;
+        }
+
+        match self[idx] {
+            OpCode::OpConstant(idx) => {
+                let val_str = match &self[idx] {
+                    Value::Thunk(t) => t.debug_repr(),
+                    Value::Closure(c) => format!("closure({:p})", c.lambda),
+                    val => format!("{}", val),
+                };
+
+                writeln!(writer, "OpConstant({}@{})", val_str, idx.0)
+            }
+            op => writeln!(writer, "{:?}", op),
+        }?;
+
+        Ok(())
+    }
+
+    /// Extend this chunk with the content of another, moving out of the other
+    /// in the process.
+    ///
+    /// This is used by the compiler when it detects that it unnecessarily
+    /// thunked a nested expression.
+    pub fn extend(&mut self, other: Self) {
+        // Some operations need to be modified in certain ways before being
+        // valid as part of the new chunk.
+        let const_count = self.constants.len();
+        for (idx, op) in other.code.iter().enumerate() {
+            let span = other.get_span(CodeIdx(idx));
+            match op {
+                // As the constants shift, the index needs to be moved relatively.
+                OpCode::OpConstant(ConstantIdx(idx)) => {
+                    self.push_op(OpCode::OpConstant(ConstantIdx(idx + const_count)), span)
+                }
+
+                // Other operations either operate on relative offsets, or no
+                // offsets, and are safe to keep as-is.
+                _ => self.push_op(*op, span),
+            };
+        }
+
+        self.constants.extend(other.constants);
+        self.spans.extend(other.spans);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test_utils::dummy_span;
+
+    // Note: These tests are about the functionality of the `Chunk` type, the
+    // opcodes used below do *not* represent valid, executable Tvix code (and
+    // don't need to).
+
+    #[test]
+    fn push_op() {
+        let mut chunk = Chunk::default();
+        chunk.push_op(OpCode::OpAdd, dummy_span());
+        assert_eq!(chunk.code.last().unwrap(), &OpCode::OpAdd);
+    }
+
+    #[test]
+    fn extend_empty() {
+        let mut chunk = Chunk::default();
+        chunk.push_op(OpCode::OpAdd, dummy_span());
+
+        let other = Chunk::default();
+        chunk.extend(other);
+
+        assert_eq!(
+            chunk.code,
+            vec![OpCode::OpAdd],
+            "code should not have changed"
+        );
+    }
+
+    #[test]
+    fn extend_simple() {
+        let span = dummy_span();
+        let mut chunk = Chunk::default();
+        chunk.push_op(OpCode::OpAdd, span);
+
+        let mut other = Chunk::default();
+        other.push_op(OpCode::OpSub, span);
+        other.push_op(OpCode::OpMul, span);
+
+        let expected_code = vec![OpCode::OpAdd, OpCode::OpSub, OpCode::OpMul];
+
+        chunk.extend(other);
+
+        assert_eq!(chunk.code, expected_code, "code should have been extended");
+    }
+
+    #[test]
+    fn extend_with_constant() {
+        let span = dummy_span();
+        let mut chunk = Chunk::default();
+        chunk.push_op(OpCode::OpAdd, span);
+        let cidx = chunk.push_constant(Value::Integer(0));
+        assert_eq!(
+            cidx.0, 0,
+            "first constant in main chunk should have index 0"
+        );
+        chunk.push_op(OpCode::OpConstant(cidx), span);
+
+        let mut other = Chunk::default();
+        other.push_op(OpCode::OpSub, span);
+        let other_cidx = other.push_constant(Value::Integer(1));
+        assert_eq!(
+            other_cidx.0, 0,
+            "first constant in other chunk should have index 0"
+        );
+        other.push_op(OpCode::OpConstant(other_cidx), span);
+
+        chunk.extend(other);
+
+        let expected_code = vec![
+            OpCode::OpAdd,
+            OpCode::OpConstant(ConstantIdx(0)),
+            OpCode::OpSub,
+            OpCode::OpConstant(ConstantIdx(1)), // <- note: this was rewritten
+        ];
+
+        assert_eq!(
+            chunk.code, expected_code,
+            "code should have been extended and rewritten"
+        );
+
+        assert_eq!(chunk.constants.len(), 2);
+        assert!(matches!(chunk.constants[0], Value::Integer(0)));
+        assert!(matches!(chunk.constants[1], Value::Integer(1)));
+    }
+}
diff --git a/tvix/eval/src/compiler/bindings.rs b/tvix/eval/src/compiler/bindings.rs
new file mode 100644
index 0000000000..634cc54022
--- /dev/null
+++ b/tvix/eval/src/compiler/bindings.rs
@@ -0,0 +1,826 @@
+//! This module implements compiler logic related to name/value binding
+//! definitions (that is, attribute sets and let-expressions).
+//!
+//! In the case of recursive scopes these cases share almost all of their
+//! (fairly complex) logic.
+
+use std::iter::Peekable;
+
+use rnix::ast::HasEntry;
+use rowan::ast::AstChildren;
+
+use super::*;
+
+type PeekableAttrs = Peekable<AstChildren<ast::Attr>>;
+
+/// What kind of bindings scope is being compiled?
+#[derive(Clone, Copy, PartialEq)]
+enum BindingsKind {
+    /// Standard `let ... in ...`-expression.
+    LetIn,
+
+    /// Non-recursive attribute set.
+    Attrs,
+
+    /// Recursive attribute set.
+    RecAttrs,
+}
+
+impl BindingsKind {
+    fn is_attrs(&self) -> bool {
+        matches!(self, BindingsKind::Attrs | BindingsKind::RecAttrs)
+    }
+}
+
+// Internal representation of an attribute set used for merging sets, or
+// inserting nested keys.
+#[derive(Clone)]
+struct AttributeSet {
+    /// Original span at which this set was first encountered.
+    span: Span,
+
+    /// Tracks the kind of set (rec or not).
+    kind: BindingsKind,
+
+    /// All inherited entries
+    inherits: Vec<ast::Inherit>,
+
+    /// All internal entries
+    entries: Vec<(Span, PeekableAttrs, ast::Expr)>,
+}
+
+impl ToSpan for AttributeSet {
+    fn span_for(&self, _: &codemap::File) -> Span {
+        self.span
+    }
+}
+
+impl AttributeSet {
+    fn from_ast(c: &Compiler, node: &ast::AttrSet) -> Self {
+        AttributeSet {
+            span: c.span_for(node),
+
+            // Kind of the attrs depends on the first time it is
+            // encountered. We actually believe this to be a Nix
+            // bug: https://github.com/NixOS/nix/issues/7111
+            kind: if node.rec_token().is_some() {
+                BindingsKind::RecAttrs
+            } else {
+                BindingsKind::Attrs
+            },
+
+            inherits: ast::HasEntry::inherits(node).collect(),
+
+            entries: ast::HasEntry::attrpath_values(node)
+                .map(|entry| {
+                    let span = c.span_for(&entry);
+                    (
+                        span,
+                        entry.attrpath().unwrap().attrs().peekable(),
+                        entry.value().unwrap(),
+                    )
+                })
+                .collect(),
+        }
+    }
+}
+
+// Data structures to track the bindings observed in the second pass, and
+// forward the information needed to compile their value.
+enum Binding {
+    InheritFrom {
+        namespace: ast::Expr,
+        name: SmolStr,
+        span: Span,
+    },
+
+    Plain {
+        expr: ast::Expr,
+    },
+
+    Set(AttributeSet),
+}
+
+impl Binding {
+    /// Merge the provided value into the current binding, or emit an
+    /// error if this turns out to be impossible.
+    fn merge(
+        &mut self,
+        c: &mut Compiler,
+        span: Span,
+        mut remaining_path: PeekableAttrs,
+        value: ast::Expr,
+    ) {
+        match self {
+            Binding::InheritFrom { name, ref span, .. } => {
+                c.emit_error(span, ErrorKind::UnmergeableInherit { name: name.clone() })
+            }
+
+            // If the value is not yet a nested binding, flip the representation
+            // and recurse.
+            Binding::Plain { expr } => match expr {
+                ast::Expr::AttrSet(existing) => {
+                    let nested = AttributeSet::from_ast(c, existing);
+                    *self = Binding::Set(nested);
+                    self.merge(c, span, remaining_path, value);
+                }
+
+                _ => c.emit_error(&value, ErrorKind::UnmergeableValue),
+            },
+
+            // If the value is nested further, it is simply inserted into the
+            // bindings with its full path and resolved recursively further
+            // down.
+            Binding::Set(existing) if remaining_path.peek().is_some() => {
+                existing.entries.push((span, remaining_path, value))
+            }
+
+            Binding::Set(existing) => {
+                if let ast::Expr::AttrSet(new) = value {
+                    existing.inherits.extend(ast::HasEntry::inherits(&new));
+                    existing
+                        .entries
+                        .extend(ast::HasEntry::attrpath_values(&new).map(|entry| {
+                            let span = c.span_for(&entry);
+                            (
+                                span,
+                                entry.attrpath().unwrap().attrs().peekable(),
+                                entry.value().unwrap(),
+                            )
+                        }));
+                } else {
+                    // This branch is unreachable because in cases where the
+                    // path is empty (i.e. there is no further nesting), the
+                    // previous try_merge function already verified that the
+                    // expression is an attribute set.
+
+                    // TODO(tazjin): Consider making this branch live by
+                    // shuffling that check around and emitting a static error
+                    // here instead of a runtime error.
+                    unreachable!()
+                }
+            }
+        }
+    }
+}
+
+enum KeySlot {
+    /// There is no key slot (`let`-expressions do not emit their key).
+    None { name: SmolStr },
+
+    /// The key is statically known and has a slot.
+    Static { slot: LocalIdx, name: SmolStr },
+
+    /// The key is dynamic, i.e. only known at runtime, and must be compiled
+    /// into its slot.
+    Dynamic { slot: LocalIdx, attr: ast::Attr },
+}
+
+struct TrackedBinding {
+    key_slot: KeySlot,
+    value_slot: LocalIdx,
+    binding: Binding,
+}
+
+impl TrackedBinding {
+    /// Does this binding match the given key?
+    ///
+    /// Used to determine which binding to merge another one into.
+    fn matches(&self, key: &str) -> bool {
+        match &self.key_slot {
+            KeySlot::None { name } => name == key,
+            KeySlot::Static { name, .. } => name == key,
+            KeySlot::Dynamic { .. } => false,
+        }
+    }
+}
+
+struct TrackedBindings {
+    bindings: Vec<TrackedBinding>,
+}
+
+impl TrackedBindings {
+    fn new() -> Self {
+        TrackedBindings { bindings: vec![] }
+    }
+
+    /// Attempt to merge an entry into an existing matching binding, assuming
+    /// that the provided binding is mergable (i.e. either a nested key or an
+    /// attribute set literal).
+    ///
+    /// Returns true if the binding was merged, false if it needs to be compiled
+    /// separately as a new binding.
+    fn try_merge(
+        &mut self,
+        c: &mut Compiler,
+        span: Span,
+        name: &ast::Attr,
+        mut remaining_path: PeekableAttrs,
+        value: ast::Expr,
+    ) -> bool {
+        // If the path has no more entries, and if the entry is not an
+        // attribute set literal, the entry can not be merged.
+        if remaining_path.peek().is_none() && !matches!(value, ast::Expr::AttrSet(_)) {
+            return false;
+        }
+
+        // If the first element of the path is not statically known, the entry
+        // can not be merged.
+        let name = match expr_static_attr_str(name) {
+            Some(name) => name,
+            None => return false,
+        };
+
+        // If there is no existing binding with this key, the entry can not be
+        // merged.
+        // TODO: benchmark whether using a map or something is useful over the
+        // `find` here
+        let binding = match self.bindings.iter_mut().find(|b| b.matches(&name)) {
+            Some(b) => b,
+            None => return false,
+        };
+
+        // No more excuses ... the binding can be merged!
+        binding.binding.merge(c, span, remaining_path, value);
+
+        true
+    }
+
+    /// Add a completely new binding to the tracked bindings.
+    fn track_new(&mut self, key_slot: KeySlot, value_slot: LocalIdx, binding: Binding) {
+        self.bindings.push(TrackedBinding {
+            key_slot,
+            value_slot,
+            binding,
+        });
+    }
+}
+
+/// Wrapper around the `ast::HasEntry` trait as that trait can not be
+/// implemented for custom types.
+trait HasEntryProxy {
+    fn inherits(&self) -> Box<dyn Iterator<Item = ast::Inherit>>;
+
+    fn attributes<'a>(
+        &self,
+        file: &'a codemap::File,
+    ) -> Box<dyn Iterator<Item = (Span, PeekableAttrs, ast::Expr)> + 'a>;
+}
+
+impl<N: HasEntry> HasEntryProxy for N {
+    fn inherits(&self) -> Box<dyn Iterator<Item = ast::Inherit>> {
+        Box::new(ast::HasEntry::inherits(self))
+    }
+
+    fn attributes<'a>(
+        &self,
+        file: &'a codemap::File,
+    ) -> Box<dyn Iterator<Item = (Span, PeekableAttrs, ast::Expr)> + 'a> {
+        Box::new(ast::HasEntry::attrpath_values(self).map(move |entry| {
+            (
+                entry.span_for(file),
+                entry.attrpath().unwrap().attrs().peekable(),
+                entry.value().unwrap(),
+            )
+        }))
+    }
+}
+
+impl HasEntryProxy for AttributeSet {
+    fn inherits(&self) -> Box<dyn Iterator<Item = ast::Inherit>> {
+        Box::new(self.inherits.clone().into_iter())
+    }
+
+    fn attributes<'a>(
+        &self,
+        _: &'a codemap::File,
+    ) -> Box<dyn Iterator<Item = (Span, PeekableAttrs, ast::Expr)> + 'a> {
+        Box::new(self.entries.clone().into_iter())
+    }
+}
+
+/// AST-traversing functions related to bindings.
+impl Compiler<'_, '_> {
+    /// Compile all inherits of a node with entries that do *not* have a
+    /// namespace to inherit from, and return the remaining ones that do.
+    fn compile_plain_inherits<N>(
+        &mut self,
+        slot: LocalIdx,
+        kind: BindingsKind,
+        count: &mut usize,
+        node: &N,
+    ) -> Vec<(ast::Expr, SmolStr, Span)>
+    where
+        N: ToSpan + HasEntryProxy,
+    {
+        // Pass over all inherits, resolving only those without namespaces.
+        // Since they always resolve in a higher scope, we can just compile and
+        // declare them immediately.
+        //
+        // Inherits with namespaces are returned to the caller.
+        let mut inherit_froms: Vec<(ast::Expr, SmolStr, Span)> = vec![];
+
+        for inherit in node.inherits() {
+            if inherit.attrs().peekable().peek().is_none() {
+                self.emit_warning(&inherit, WarningKind::EmptyInherit);
+                continue;
+            }
+
+            match inherit.from() {
+                // Within a `let` binding, inheriting from the outer scope is a
+                // no-op *if* there are no dynamic bindings.
+                None if !kind.is_attrs() && !self.has_dynamic_ancestor() => {
+                    self.emit_warning(&inherit, WarningKind::UselessInherit);
+                    continue;
+                }
+
+                None => {
+                    for attr in inherit.attrs() {
+                        let name = match expr_static_attr_str(&attr) {
+                            Some(name) => name,
+                            None => {
+                                self.emit_error(&attr, ErrorKind::DynamicKeyInScope("inherit"));
+                                continue;
+                            }
+                        };
+
+                        // If the identifier resolves statically in a `let`, it
+                        // has precedence over dynamic bindings, and the inherit
+                        // is useless.
+                        if kind == BindingsKind::LetIn
+                            && matches!(
+                                self.scope_mut().resolve_local(&name),
+                                LocalPosition::Known(_)
+                            )
+                        {
+                            self.emit_warning(&attr, WarningKind::UselessInherit);
+                            continue;
+                        }
+
+                        *count += 1;
+
+                        // Place key on the stack when compiling attribute sets.
+                        if kind.is_attrs() {
+                            self.emit_constant(name.as_str().into(), &attr);
+                            let span = self.span_for(&attr);
+                            self.scope_mut().declare_phantom(span, true);
+                        }
+
+                        // Place the value on the stack. Note that because plain
+                        // inherits are always in the outer scope, the slot of
+                        // *this* scope itself is used.
+                        self.compile_identifier_access(slot, &name, &attr);
+
+                        // In non-recursive attribute sets, the key slot must be
+                        // a phantom (i.e. the identifier can not be resolved in
+                        // this scope).
+                        let idx = if kind == BindingsKind::Attrs {
+                            let span = self.span_for(&attr);
+                            self.scope_mut().declare_phantom(span, false)
+                        } else {
+                            self.declare_local(&attr, name)
+                        };
+
+                        self.scope_mut().mark_initialised(idx);
+                    }
+                }
+
+                Some(from) => {
+                    for attr in inherit.attrs() {
+                        let name = match expr_static_attr_str(&attr) {
+                            Some(name) => name,
+                            None => {
+                                self.emit_error(&attr, ErrorKind::DynamicKeyInScope("inherit"));
+                                continue;
+                            }
+                        };
+
+                        *count += 1;
+                        inherit_froms.push((from.expr().unwrap(), name, self.span_for(&attr)));
+                    }
+                }
+            }
+        }
+
+        inherit_froms
+    }
+
+    /// Declare all namespaced inherits, that is inherits which are inheriting
+    /// values from an attribute set.
+    ///
+    /// This only ensures that the locals stack is aware of the inherits, it
+    /// does not yet emit bytecode that places them on the stack. This is up to
+    /// the owner of the `bindings` vector, which this function will populate.
+    fn declare_namespaced_inherits(
+        &mut self,
+        kind: BindingsKind,
+        inherit_froms: Vec<(ast::Expr, SmolStr, Span)>,
+        bindings: &mut TrackedBindings,
+    ) {
+        for (from, name, span) in inherit_froms {
+            let key_slot = if kind.is_attrs() {
+                // In an attribute set, the keys themselves are placed on the
+                // stack but their stack slot is inaccessible (it is only
+                // consumed by `OpAttrs`).
+                KeySlot::Static {
+                    slot: self.scope_mut().declare_phantom(span, false),
+                    name: name.clone(),
+                }
+            } else {
+                KeySlot::None { name: name.clone() }
+            };
+
+            let value_slot = match kind {
+                // In recursive scopes, the value needs to be accessible on the
+                // stack.
+                BindingsKind::LetIn | BindingsKind::RecAttrs => {
+                    self.declare_local(&span, name.clone())
+                }
+
+                // In non-recursive attribute sets, the value is inaccessible
+                // (only consumed by `OpAttrs`).
+                BindingsKind::Attrs => self.scope_mut().declare_phantom(span, false),
+            };
+
+            bindings.track_new(
+                key_slot,
+                value_slot,
+                Binding::InheritFrom {
+                    namespace: from,
+                    name,
+                    span,
+                },
+            );
+        }
+    }
+
+    /// Declare all regular bindings (i.e. `key = value;`) in a bindings scope,
+    /// but do not yet compile their values.
+    fn declare_bindings<N>(
+        &mut self,
+        kind: BindingsKind,
+        count: &mut usize,
+        bindings: &mut TrackedBindings,
+        node: &N,
+    ) where
+        N: ToSpan + HasEntryProxy,
+    {
+        for (span, mut path, value) in node.attributes(self.file) {
+            let key = path.next().unwrap();
+
+            if bindings.try_merge(self, span, &key, path.clone(), value.clone()) {
+                // Binding is nested, or already exists and was merged, move on.
+                continue;
+            }
+
+            *count += 1;
+
+            let key_span = self.span_for(&key);
+            let key_slot = match expr_static_attr_str(&key) {
+                Some(name) if kind.is_attrs() => KeySlot::Static {
+                    name,
+                    slot: self.scope_mut().declare_phantom(key_span, false),
+                },
+
+                Some(name) => KeySlot::None { name },
+
+                None if kind.is_attrs() => KeySlot::Dynamic {
+                    attr: key,
+                    slot: self.scope_mut().declare_phantom(key_span, false),
+                },
+
+                None => {
+                    self.emit_error(&key, ErrorKind::DynamicKeyInScope("let-expression"));
+                    continue;
+                }
+            };
+
+            let value_slot = match kind {
+                BindingsKind::LetIn | BindingsKind::RecAttrs => match &key_slot {
+                    // In recursive scopes, the value needs to be accessible on the
+                    // stack if it is statically known
+                    KeySlot::None { name } | KeySlot::Static { name, .. } => {
+                        self.declare_local(&key_span, name.as_str())
+                    }
+
+                    // Dynamic values are never resolvable (as their names are
+                    // of course only known at runtime).
+                    //
+                    // Note: This branch is unreachable in `let`-expressions.
+                    KeySlot::Dynamic { .. } => self.scope_mut().declare_phantom(key_span, false),
+                },
+
+                // In non-recursive attribute sets, the value is inaccessible
+                // (only consumed by `OpAttrs`).
+                BindingsKind::Attrs => self.scope_mut().declare_phantom(key_span, false),
+            };
+
+            let binding = if path.peek().is_some() {
+                Binding::Set(AttributeSet {
+                    span,
+                    kind: BindingsKind::Attrs,
+                    inherits: vec![],
+                    entries: vec![(span, path, value)],
+                })
+            } else {
+                Binding::Plain { expr: value }
+            };
+
+            bindings.track_new(key_slot, value_slot, binding);
+        }
+    }
+
+    /// Compile attribute set literals into equivalent bytecode.
+    ///
+    /// This is complicated by a number of features specific to Nix attribute
+    /// sets, most importantly:
+    ///
+    /// 1. Keys can be dynamically constructed through interpolation.
+    /// 2. Keys can refer to nested attribute sets.
+    /// 3. Attribute sets can (optionally) be recursive.
+    pub(super) fn compile_attr_set(&mut self, slot: LocalIdx, node: &ast::AttrSet) {
+        // Open a scope to track the positions of the temporaries used by the
+        // `OpAttrs` instruction.
+        self.scope_mut().begin_scope();
+
+        let kind = if node.rec_token().is_some() {
+            BindingsKind::RecAttrs
+        } else {
+            BindingsKind::Attrs
+        };
+
+        self.compile_bindings(slot, kind, node);
+
+        // Remove the temporary scope, but do not emit any additional cleanup
+        // (OpAttrs consumes all of these locals).
+        self.scope_mut().end_scope();
+    }
+
+    /// Actually binds all tracked bindings by emitting the bytecode that places
+    /// them in their stack slots.
+    fn bind_values(&mut self, bindings: TrackedBindings) {
+        let mut value_indices: Vec<LocalIdx> = vec![];
+
+        for binding in bindings.bindings.into_iter() {
+            value_indices.push(binding.value_slot);
+
+            match binding.key_slot {
+                KeySlot::None { .. } => {} // nothing to do here
+
+                KeySlot::Static { slot, name } => {
+                    let span = self.scope()[slot].span;
+                    self.emit_constant(name.as_str().into(), &span);
+                    self.scope_mut().mark_initialised(slot);
+                }
+
+                KeySlot::Dynamic { slot, attr } => {
+                    self.compile_attr(slot, &attr);
+                    self.scope_mut().mark_initialised(slot);
+                }
+            }
+
+            match binding.binding {
+                // This entry is an inherit (from) expr. The value is placed on
+                // the stack by selecting an attribute.
+                Binding::InheritFrom {
+                    namespace,
+                    name,
+                    span,
+                } => {
+                    // Create a thunk wrapping value (which may be one as well)
+                    // to avoid forcing the from expr too early.
+                    self.thunk(binding.value_slot, &namespace, |c, s| {
+                        c.compile(s, namespace.clone());
+                        c.emit_force(&namespace);
+
+                        c.emit_constant(name.as_str().into(), &span);
+                        c.push_op(OpCode::OpAttrsSelect, &span);
+                    })
+                }
+
+                // Binding is "just" a plain expression that needs to be
+                // compiled.
+                Binding::Plain { expr } => self.compile(binding.value_slot, expr),
+
+                // Binding is a merged or nested attribute set, and needs to be
+                // recursively compiled as another binding.
+                Binding::Set(set) => self.thunk(binding.value_slot, &set, |c, _| {
+                    c.scope_mut().begin_scope();
+                    c.compile_bindings(binding.value_slot, set.kind, &set);
+                    c.scope_mut().end_scope();
+                }),
+            }
+
+            // Any code after this point will observe the value in the right
+            // stack slot, so mark it as initialised.
+            self.scope_mut().mark_initialised(binding.value_slot);
+        }
+
+        // Final pass to emit finaliser instructions if necessary.
+        for idx in value_indices {
+            if self.scope()[idx].needs_finaliser {
+                let stack_idx = self.scope().stack_index(idx);
+                let span = self.scope()[idx].span;
+                self.push_op(OpCode::OpFinalise(stack_idx), &span);
+            }
+        }
+    }
+
+    fn compile_bindings<N>(&mut self, slot: LocalIdx, kind: BindingsKind, node: &N)
+    where
+        N: ToSpan + HasEntryProxy,
+    {
+        let mut count = 0;
+        self.scope_mut().begin_scope();
+
+        // Vector to track all observed bindings.
+        let mut bindings = TrackedBindings::new();
+
+        let inherit_froms = self.compile_plain_inherits(slot, kind, &mut count, node);
+        self.declare_namespaced_inherits(kind, inherit_froms, &mut bindings);
+        self.declare_bindings(kind, &mut count, &mut bindings, node);
+
+        // Check if we can bail out on empty bindings
+        if count == 0 {
+            // still need an attrset to exist, but it is empty.
+            if kind.is_attrs() {
+                self.emit_constant(Value::Attrs(Box::new(NixAttrs::empty())), node);
+                return;
+            }
+
+            self.emit_warning(node, WarningKind::EmptyLet);
+            return;
+        }
+
+        // Actually bind values and ensure they are on the stack.
+        self.bind_values(bindings);
+
+        if kind.is_attrs() {
+            self.push_op(OpCode::OpAttrs(Count(count)), node);
+        }
+
+        if count == 0 {
+            self.unthunk();
+        }
+    }
+
+    /// Compile a standard `let ...; in ...` expression.
+    ///
+    /// Unless in a non-standard scope, the encountered values are simply pushed
+    /// on the stack and their indices noted in the entries vector.
+    pub(super) fn compile_let_in(&mut self, slot: LocalIdx, node: &ast::LetIn) {
+        self.compile_bindings(slot, BindingsKind::LetIn, node);
+
+        // Deal with the body, then clean up the locals afterwards.
+        self.compile(slot, node.body().unwrap());
+        self.cleanup_scope(node);
+    }
+
+    pub(super) fn compile_legacy_let(&mut self, slot: LocalIdx, node: &ast::LegacyLet) {
+        self.emit_warning(node, WarningKind::DeprecatedLegacyLet);
+        self.scope_mut().begin_scope();
+        self.compile_bindings(slot, BindingsKind::RecAttrs, node);
+
+        // Remove the temporary scope, but do not emit any additional cleanup
+        // (OpAttrs consumes all of these locals).
+        self.scope_mut().end_scope();
+
+        self.emit_constant("body".into(), node);
+        self.push_op(OpCode::OpAttrsSelect, node);
+    }
+
+    /// Is the given identifier defined *by the user* in any current scope?
+    pub(super) fn is_user_defined(&mut self, ident: &str) -> bool {
+        matches!(
+            self.scope_mut().resolve_local(ident),
+            LocalPosition::Known(_) | LocalPosition::Recursive(_)
+        )
+    }
+
+    /// Resolve and compile access to an identifier in the scope.
+    fn compile_identifier_access<N: ToSpan + Clone>(
+        &mut self,
+        slot: LocalIdx,
+        ident: &str,
+        node: &N,
+    ) {
+        match self.scope_mut().resolve_local(ident) {
+            LocalPosition::Unknown => {
+                // Are we possibly dealing with an upvalue?
+                if let Some(idx) = self.resolve_upvalue(self.contexts.len() - 1, ident, node) {
+                    self.push_op(OpCode::OpGetUpvalue(idx), node);
+                    return;
+                }
+
+                // Globals are the "upmost upvalues": they behave
+                // exactly like a `let ... in` prepended to the
+                // program's text, and the global scope is nothing
+                // more than the parent scope of the root scope.
+                if let Some(global) = self.globals.get(ident) {
+                    self.emit_constant(global.clone(), &self.span_for(node));
+                    return;
+                }
+
+                // If there is a non-empty `with`-stack (or a parent context
+                // with one), emit a runtime dynamic resolution instruction.
+                //
+                // Since it is possible for users to e.g. assign a variable to a
+                // dynamic resolution without actually using it, this operation
+                // is wrapped in an extra thunk.
+                if self.has_dynamic_ancestor() {
+                    self.thunk(slot, node, |c, _| {
+                        c.context_mut().captures_with_stack = true;
+                        c.emit_constant(ident.into(), node);
+                        c.push_op(OpCode::OpResolveWith, node);
+                    });
+                    return;
+                }
+
+                // Otherwise, this variable is missing.
+                self.emit_error(node, ErrorKind::UnknownStaticVariable);
+            }
+
+            LocalPosition::Known(idx) => {
+                let stack_idx = self.scope().stack_index(idx);
+                self.push_op(OpCode::OpGetLocal(stack_idx), node);
+            }
+
+            // This identifier is referring to a value from the same scope which
+            // is not yet defined. This identifier access must be thunked.
+            LocalPosition::Recursive(idx) => self.thunk(slot, node, move |compiler, _| {
+                let upvalue_idx = compiler.add_upvalue(
+                    compiler.contexts.len() - 1,
+                    node,
+                    UpvalueKind::Local(idx),
+                );
+                compiler.push_op(OpCode::OpGetUpvalue(upvalue_idx), node);
+            }),
+        };
+    }
+
+    pub(super) fn compile_ident(&mut self, slot: LocalIdx, node: &ast::Ident) {
+        let ident = node.ident_token().unwrap();
+        self.compile_identifier_access(slot, ident.text(), node);
+    }
+}
+
+/// Private compiler helpers related to bindings.
+impl Compiler<'_, '_> {
+    fn resolve_upvalue<N: ToSpan>(
+        &mut self,
+        ctx_idx: usize,
+        name: &str,
+        node: &N,
+    ) -> Option<UpvalueIdx> {
+        if ctx_idx == 0 {
+            // There can not be any upvalue at the outermost context.
+            return None;
+        }
+
+        // Determine whether the upvalue is a local in the enclosing context.
+        match self.contexts[ctx_idx - 1].scope.resolve_local(name) {
+            // recursive upvalues are dealt with the same way as standard known
+            // ones, as thunks and closures are guaranteed to be placed on the
+            // stack (i.e. in the right position) *during* their runtime
+            // construction
+            LocalPosition::Known(idx) | LocalPosition::Recursive(idx) => {
+                return Some(self.add_upvalue(ctx_idx, node, UpvalueKind::Local(idx)))
+            }
+
+            LocalPosition::Unknown => { /* continue below */ }
+        };
+
+        // If the upvalue comes from even further up, we need to recurse to make
+        // sure that the upvalues are created at each level.
+        if let Some(idx) = self.resolve_upvalue(ctx_idx - 1, name, node) {
+            return Some(self.add_upvalue(ctx_idx, node, UpvalueKind::Upvalue(idx)));
+        }
+
+        None
+    }
+
+    fn add_upvalue<N: ToSpan>(
+        &mut self,
+        ctx_idx: usize,
+        node: &N,
+        kind: UpvalueKind,
+    ) -> UpvalueIdx {
+        // If there is already an upvalue closing over the specified index,
+        // retrieve that instead.
+        for (idx, existing) in self.contexts[ctx_idx].scope.upvalues.iter().enumerate() {
+            if existing.kind == kind {
+                return UpvalueIdx(idx);
+            }
+        }
+
+        let span = self.span_for(node);
+        self.contexts[ctx_idx]
+            .scope
+            .upvalues
+            .push(Upvalue { kind, span });
+
+        let idx = UpvalueIdx(self.contexts[ctx_idx].lambda.upvalue_count);
+        self.contexts[ctx_idx].lambda.upvalue_count += 1;
+        idx
+    }
+}
diff --git a/tvix/eval/src/compiler/import.rs b/tvix/eval/src/compiler/import.rs
new file mode 100644
index 0000000000..9036eec817
--- /dev/null
+++ b/tvix/eval/src/compiler/import.rs
@@ -0,0 +1,120 @@
+//! This module implements the Nix language's `import` feature, which
+//! is exposed as a builtin in the Nix language.
+//!
+//! This is not a typical builtin, as it needs access to internal
+//! compiler and VM state (such as the [`crate::SourceCode`]
+//! instance, or observers).
+
+use super::GlobalsMap;
+use genawaiter::rc::Gen;
+use std::rc::Weak;
+
+use crate::{
+    builtins::coerce_value_to_path,
+    generators::pin_generator,
+    observer::NoOpObserver,
+    value::{Builtin, Thunk},
+    vm::generators::{self, GenCo},
+    ErrorKind, SourceCode, Value,
+};
+
+async fn import_impl(
+    co: GenCo,
+    globals: Weak<GlobalsMap>,
+    source: SourceCode,
+    mut args: Vec<Value>,
+) -> Result<Value, ErrorKind> {
+    // TODO(sterni): canon_path()?
+    let mut path = match coerce_value_to_path(&co, args.pop().unwrap()).await? {
+        Err(cek) => return Ok(Value::Catchable(Box::new(cek))),
+        Ok(path) => path,
+    };
+
+    if path.is_dir() {
+        path.push("default.nix");
+    }
+
+    if let Some(cached) = generators::request_import_cache_lookup(&co, path.clone()).await {
+        return Ok(cached);
+    }
+
+    let mut reader = generators::request_open_file(&co, path.clone()).await;
+    // We read to a String instead of a Vec<u8> because rnix only supports
+    // string source files.
+    let mut contents = String::new();
+    reader.read_to_string(&mut contents)?;
+
+    let parsed = rnix::ast::Root::parse(&contents);
+    let errors = parsed.errors();
+    let file = source.add_file(path.to_string_lossy().to_string(), contents.to_owned());
+
+    if !errors.is_empty() {
+        return Err(ErrorKind::ImportParseError {
+            path,
+            file,
+            errors: errors.to_vec(),
+        });
+    }
+
+    let result = crate::compiler::compile(
+        &parsed.tree().expr().unwrap(),
+        Some(path.clone()),
+        // The VM must ensure that a strong reference to the globals outlives
+        // any self-references (which are weak) embedded within the globals. If
+        // the expect() below panics, it means that did not happen.
+        globals
+            .upgrade()
+            .expect("globals dropped while still in use"),
+        &source,
+        &file,
+        &mut NoOpObserver::default(),
+    )
+    .map_err(|err| ErrorKind::ImportCompilerError {
+        path: path.clone(),
+        errors: vec![err],
+    })?;
+
+    if !result.errors.is_empty() {
+        return Err(ErrorKind::ImportCompilerError {
+            path,
+            errors: result.errors,
+        });
+    }
+
+    for warning in result.warnings {
+        generators::emit_warning(&co, warning).await;
+    }
+
+    // Compilation succeeded, we can construct a thunk from whatever it spat
+    // out and return that.
+    let res = Value::Thunk(Thunk::new_suspended(
+        result.lambda,
+        generators::request_span(&co).await,
+    ));
+
+    generators::request_import_cache_put(&co, path, res.clone()).await;
+
+    Ok(res)
+}
+
+/// Constructs the `import` builtin. This builtin is special in that
+/// it needs to capture the [crate::SourceCode] structure to correctly
+/// track source code locations while invoking a compiler.
+// TODO: need to be able to pass through a CompilationObserver, too.
+// TODO: can the `SourceCode` come from the compiler?
+pub(super) fn builtins_import(globals: &Weak<GlobalsMap>, source: SourceCode) -> Builtin {
+    // This (very cheap, once-per-compiler-startup) clone exists
+    // solely in order to keep the borrow checker happy.  It
+    // resolves the tension between the requirements of
+    // Rc::new_cyclic() and Builtin::new()
+    let globals = globals.clone();
+
+    Builtin::new(
+        "import",
+        Some("Import the given file and return the Nix value it evaluates to"),
+        1,
+        move |args| {
+            Gen::new(|co| pin_generator(import_impl(co, globals.clone(), source.clone(), args)))
+        },
+    )
+}
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
new file mode 100644
index 0000000000..60c55dda27
--- /dev/null
+++ b/tvix/eval/src/compiler/mod.rs
@@ -0,0 +1,1684 @@
+//! This module implements a compiler for compiling the rnix AST
+//! representation to Tvix bytecode.
+//!
+//! A note on `unwrap()`: This module contains a lot of calls to
+//! `unwrap()` or `expect(...)` on data structures returned by `rnix`.
+//! The reason for this is that rnix uses the same data structures to
+//! represent broken and correct ASTs, so all typed AST variants have
+//! the ability to represent an incorrect node.
+//!
+//! However, at the time that the AST is passed to the compiler we
+//! have verified that `rnix` considers the code to be correct, so all
+//! variants are fulfilled. In cases where the invariant is guaranteed
+//! by the code in this module, `debug_assert!` has been used to catch
+//! mistakes early during development.
+
+mod bindings;
+mod import;
+mod optimiser;
+mod scope;
+
+use codemap::Span;
+use rnix::ast::{self, AstToken};
+use smol_str::SmolStr;
+use std::collections::{BTreeMap, HashMap};
+use std::path::{Path, PathBuf};
+use std::rc::{Rc, Weak};
+
+use crate::chunk::Chunk;
+use crate::errors::{CatchableErrorKind, Error, ErrorKind, EvalResult};
+use crate::observer::CompilerObserver;
+use crate::opcode::{CodeIdx, ConstantIdx, Count, JumpOffset, OpCode, UpvalueIdx};
+use crate::spans::LightSpan;
+use crate::spans::ToSpan;
+use crate::value::{Closure, Formals, Lambda, NixAttrs, Thunk, Value};
+use crate::warnings::{EvalWarning, WarningKind};
+use crate::CoercionKind;
+use crate::SourceCode;
+
+use self::scope::{LocalIdx, LocalPosition, Scope, Upvalue, UpvalueKind};
+
+/// Represents the result of compiling a piece of Nix code. If
+/// compilation was successful, the resulting bytecode can be passed
+/// to the VM.
+pub struct CompilationOutput {
+    pub lambda: Rc<Lambda>,
+    pub warnings: Vec<EvalWarning>,
+    pub errors: Vec<Error>,
+
+    // This field must outlive the rc::Weak reference which breaks the
+    // builtins -> import -> builtins reference cycle. For this
+    // reason, it must be passed to the VM.
+    pub globals: Rc<GlobalsMap>,
+}
+
+/// Represents the lambda currently being compiled.
+struct LambdaCtx {
+    lambda: Lambda,
+    scope: Scope,
+    captures_with_stack: bool,
+    unthunk: bool,
+}
+
+impl LambdaCtx {
+    fn new() -> Self {
+        LambdaCtx {
+            lambda: Lambda::default(),
+            scope: Default::default(),
+            captures_with_stack: false,
+            unthunk: false,
+        }
+    }
+
+    fn inherit(&self) -> Self {
+        LambdaCtx {
+            lambda: Lambda::default(),
+            scope: self.scope.inherit(),
+            captures_with_stack: false,
+            unthunk: false,
+        }
+    }
+}
+
+/// When compiling functions with an argument attribute set destructuring pattern,
+/// we need to do multiple passes over the declared formal arguments when setting
+/// up their local bindings (similarly to `let … in` expressions and recursive
+/// attribute sets. For this purpose, this struct is used to represent the two
+/// kinds of formal arguments:
+///
+/// - `TrackedFormal::NoDefault` is always required and causes an evaluation error
+///   if the corresponding attribute is missing in a function call.
+/// - `TrackedFormal::WithDefault` may be missing in the passed attribute setβ€”
+///   in which case a `default_expr` will be evaluated and placed in the formal
+///   argument's local variable slot.
+enum TrackedFormal {
+    NoDefault {
+        local_idx: LocalIdx,
+        pattern_entry: ast::PatEntry,
+    },
+    WithDefault {
+        local_idx: LocalIdx,
+        /// Extra phantom local used for coordinating runtime dispatching not observable to
+        /// the language user. Detailed description in `compile_param_pattern()`.
+        finalise_request_idx: LocalIdx,
+        default_expr: ast::Expr,
+        pattern_entry: ast::PatEntry,
+    },
+}
+
+impl TrackedFormal {
+    fn pattern_entry(&self) -> &ast::PatEntry {
+        match self {
+            TrackedFormal::NoDefault { pattern_entry, .. } => pattern_entry,
+            TrackedFormal::WithDefault { pattern_entry, .. } => pattern_entry,
+        }
+    }
+    fn local_idx(&self) -> LocalIdx {
+        match self {
+            TrackedFormal::NoDefault { local_idx, .. } => *local_idx,
+            TrackedFormal::WithDefault { local_idx, .. } => *local_idx,
+        }
+    }
+}
+
+/// The map of globally available functions and other values that
+/// should implicitly be resolvable in the global scope.
+pub(crate) type GlobalsMap = HashMap<&'static str, Value>;
+
+/// Set of builtins that (if they exist) should be made available in
+/// the global scope, meaning that they can be accessed not just
+/// through `builtins.<name>`, but directly as `<name>`. This is not
+/// configurable, it is based on what Nix 2.3 exposed.
+const GLOBAL_BUILTINS: &[&str] = &[
+    "abort",
+    "baseNameOf",
+    "derivation",
+    "derivationStrict",
+    "dirOf",
+    "fetchGit",
+    "fetchMercurial",
+    "fetchTarball",
+    "fromTOML",
+    "import",
+    "isNull",
+    "map",
+    "placeholder",
+    "removeAttrs",
+    "scopedImport",
+    "throw",
+    "toString",
+    "__curPos",
+];
+
+pub struct Compiler<'source, 'observer> {
+    contexts: Vec<LambdaCtx>,
+    warnings: Vec<EvalWarning>,
+    errors: Vec<Error>,
+    root_dir: PathBuf,
+
+    /// Carries all known global tokens; the full set of which is
+    /// created when the compiler is invoked.
+    ///
+    /// Each global has an associated token, which when encountered as
+    /// an identifier is resolved against the scope poisoning logic,
+    /// and a function that should emit code for the token.
+    globals: Rc<GlobalsMap>,
+
+    /// Reference to the struct holding all of the source code, which
+    /// is used for error creation.
+    source: &'source SourceCode,
+
+    /// File reference in the source map for the current file, which
+    /// is used for creating spans.
+    file: &'source codemap::File,
+
+    /// Carry an observer for the compilation process, which is called
+    /// whenever a chunk is emitted.
+    observer: &'observer mut dyn CompilerObserver,
+
+    /// Carry a count of nested scopes which have requested the
+    /// compiler not to emit anything. This used for compiling dead
+    /// code branches to catch errors & warnings in them.
+    dead_scope: usize,
+}
+
+impl Compiler<'_, '_> {
+    pub(super) fn span_for<S: ToSpan>(&self, to_span: &S) -> Span {
+        to_span.span_for(self.file)
+    }
+}
+
+/// Compiler construction
+impl<'source, 'observer> Compiler<'source, 'observer> {
+    pub(crate) fn new(
+        location: Option<PathBuf>,
+        globals: Rc<GlobalsMap>,
+        source: &'source SourceCode,
+        file: &'source codemap::File,
+        observer: &'observer mut dyn CompilerObserver,
+    ) -> EvalResult<Self> {
+        let mut root_dir = match location {
+            Some(dir) if cfg!(target_arch = "wasm32") || dir.is_absolute() => Ok(dir),
+            _ => {
+                let current_dir = std::env::current_dir().map_err(|e| {
+                    Error::new(
+                        ErrorKind::RelativePathResolution(format!(
+                            "could not determine current directory: {}",
+                            e
+                        )),
+                        file.span,
+                        source.clone(),
+                    )
+                })?;
+                if let Some(dir) = location {
+                    Ok(current_dir.join(dir))
+                } else {
+                    Ok(current_dir)
+                }
+            }
+        }?;
+
+        // If the path passed from the caller points to a file, the
+        // filename itself needs to be truncated as this must point to a
+        // directory.
+        if root_dir.is_file() {
+            root_dir.pop();
+        }
+
+        #[cfg(not(target_arch = "wasm32"))]
+        debug_assert!(root_dir.is_absolute());
+
+        Ok(Self {
+            root_dir,
+            source,
+            file,
+            observer,
+            globals,
+            contexts: vec![LambdaCtx::new()],
+            warnings: vec![],
+            errors: vec![],
+            dead_scope: 0,
+        })
+    }
+}
+
+// Helper functions for emitting code and metadata to the internal
+// structures of the compiler.
+impl Compiler<'_, '_> {
+    fn context(&self) -> &LambdaCtx {
+        &self.contexts[self.contexts.len() - 1]
+    }
+
+    fn context_mut(&mut self) -> &mut LambdaCtx {
+        let idx = self.contexts.len() - 1;
+        &mut self.contexts[idx]
+    }
+
+    fn chunk(&mut self) -> &mut Chunk {
+        &mut self.context_mut().lambda.chunk
+    }
+
+    fn scope(&self) -> &Scope {
+        &self.context().scope
+    }
+
+    fn scope_mut(&mut self) -> &mut Scope {
+        &mut self.context_mut().scope
+    }
+
+    /// Push a single instruction to the current bytecode chunk and
+    /// track the source span from which it was compiled.
+    fn push_op<T: ToSpan>(&mut self, data: OpCode, node: &T) -> CodeIdx {
+        if self.dead_scope > 0 {
+            return CodeIdx(0);
+        }
+
+        let span = self.span_for(node);
+        self.chunk().push_op(data, span)
+    }
+
+    /// Emit a single constant to the current bytecode chunk and track
+    /// the source span from which it was compiled.
+    pub(super) fn emit_constant<T: ToSpan>(&mut self, value: Value, node: &T) {
+        if self.dead_scope > 0 {
+            return;
+        }
+
+        let idx = self.chunk().push_constant(value);
+        self.push_op(OpCode::OpConstant(idx), node);
+    }
+}
+
+// Actual code-emitting AST traversal methods.
+impl Compiler<'_, '_> {
+    fn compile(&mut self, slot: LocalIdx, expr: ast::Expr) {
+        let expr = optimiser::optimise_expr(self, slot, expr);
+
+        match &expr {
+            ast::Expr::Literal(literal) => self.compile_literal(literal),
+            ast::Expr::Path(path) => self.compile_path(slot, path),
+            ast::Expr::Str(s) => self.compile_str(slot, s),
+
+            ast::Expr::UnaryOp(op) => self.thunk(slot, op, move |c, s| c.compile_unary_op(s, op)),
+
+            ast::Expr::BinOp(binop) => {
+                self.thunk(slot, binop, move |c, s| c.compile_binop(s, binop))
+            }
+
+            ast::Expr::HasAttr(has_attr) => {
+                self.thunk(slot, has_attr, move |c, s| c.compile_has_attr(s, has_attr))
+            }
+
+            ast::Expr::List(list) => self.thunk(slot, list, move |c, s| c.compile_list(s, list)),
+
+            ast::Expr::AttrSet(attrs) => {
+                self.thunk(slot, attrs, move |c, s| c.compile_attr_set(s, attrs))
+            }
+
+            ast::Expr::Select(select) => {
+                self.thunk(slot, select, move |c, s| c.compile_select(s, select))
+            }
+
+            ast::Expr::Assert(assert) => {
+                self.thunk(slot, assert, move |c, s| c.compile_assert(s, assert))
+            }
+            ast::Expr::IfElse(if_else) => {
+                self.thunk(slot, if_else, move |c, s| c.compile_if_else(s, if_else))
+            }
+
+            ast::Expr::LetIn(let_in) => {
+                self.thunk(slot, let_in, move |c, s| c.compile_let_in(s, let_in))
+            }
+
+            ast::Expr::Ident(ident) => self.compile_ident(slot, ident),
+            ast::Expr::With(with) => self.thunk(slot, with, |c, s| c.compile_with(s, with)),
+            ast::Expr::Lambda(lambda) => self.thunk(slot, lambda, move |c, s| {
+                c.compile_lambda_or_thunk(false, s, lambda, |c, s| c.compile_lambda(s, lambda))
+            }),
+            ast::Expr::Apply(apply) => {
+                self.thunk(slot, apply, move |c, s| c.compile_apply(s, apply))
+            }
+
+            // Parenthesized expressions are simply unwrapped, leaving
+            // their value on the stack.
+            ast::Expr::Paren(paren) => self.compile(slot, paren.expr().unwrap()),
+
+            ast::Expr::LegacyLet(legacy_let) => self.thunk(slot, legacy_let, move |c, s| {
+                c.compile_legacy_let(s, legacy_let)
+            }),
+
+            ast::Expr::Root(_) => unreachable!("there cannot be more than one root"),
+            ast::Expr::Error(_) => unreachable!("compile is only called on validated trees"),
+        }
+    }
+
+    /// Compiles an expression, but does not emit any code for it as
+    /// it is considered dead. This will still catch errors and
+    /// warnings in that expression.
+    ///
+    /// A warning about the that code being dead is assumed to already be
+    /// emitted by the caller of this.
+    fn compile_dead_code(&mut self, slot: LocalIdx, node: ast::Expr) {
+        self.dead_scope += 1;
+        self.compile(slot, node);
+        self.dead_scope -= 1;
+    }
+
+    fn compile_literal(&mut self, node: &ast::Literal) {
+        let value = match node.kind() {
+            ast::LiteralKind::Float(f) => Value::Float(f.value().unwrap()),
+            ast::LiteralKind::Integer(i) => match i.value() {
+                Ok(v) => Value::Integer(v),
+                Err(err) => return self.emit_error(node, err.into()),
+            },
+
+            ast::LiteralKind::Uri(u) => {
+                self.emit_warning(node, WarningKind::DeprecatedLiteralURL);
+                Value::from(u.syntax().text())
+            }
+        };
+
+        self.emit_constant(value, node);
+    }
+
+    fn compile_path(&mut self, slot: LocalIdx, node: &ast::Path) {
+        // TODO(tazjin): placeholder implementation while waiting for
+        // https://github.com/nix-community/rnix-parser/pull/96
+
+        let raw_path = node.to_string();
+        let path = if raw_path.starts_with('/') {
+            Path::new(&raw_path).to_owned()
+        } else if raw_path.starts_with('~') {
+            // We assume that home paths start with ~/ or fail to parse
+            // TODO: this should be checked using a parse-fail test.
+            debug_assert!(raw_path.len() > 2 && raw_path.starts_with("~/"));
+
+            let home_relative_path = &raw_path[2..(raw_path.len())];
+            self.emit_constant(
+                Value::UnresolvedPath(Box::new(home_relative_path.into())),
+                node,
+            );
+            self.push_op(OpCode::OpResolveHomePath, node);
+            return;
+        } else if raw_path.starts_with('<') {
+            // TODO: decide what to do with findFile
+            if raw_path.len() == 2 {
+                return self.emit_constant(
+                    Value::Catchable(Box::new(CatchableErrorKind::NixPathResolution(
+                        "Empty <> path not allowed".into(),
+                    ))),
+                    node,
+                );
+            }
+            let path = &raw_path[1..(raw_path.len() - 1)];
+            // Make a thunk to resolve the path (without using `findFile`, at least for now?)
+            return self.thunk(slot, node, move |c, _| {
+                c.emit_constant(Value::UnresolvedPath(Box::new(path.into())), node);
+                c.push_op(OpCode::OpFindFile, node);
+            });
+        } else {
+            let mut buf = self.root_dir.clone();
+            buf.push(&raw_path);
+            buf
+        };
+
+        // TODO: Use https://github.com/rust-lang/rfcs/issues/2208
+        // once it is available
+        let value = Value::Path(Box::new(crate::value::canon_path(path)));
+        self.emit_constant(value, node);
+    }
+
+    /// Helper that compiles the given string parts strictly. The caller
+    /// (`compile_str`) needs to figure out if the result of compiling this
+    /// needs to be thunked or not.
+    fn compile_str_parts(
+        &mut self,
+        slot: LocalIdx,
+        parent_node: &ast::Str,
+        parts: Vec<ast::InterpolPart<String>>,
+    ) {
+        // The string parts are produced in literal order, however
+        // they need to be reversed on the stack in order to
+        // efficiently create the real string in case of
+        // interpolation.
+        for part in parts.iter().rev() {
+            match part {
+                // Interpolated expressions are compiled as normal and
+                // dealt with by the VM before being assembled into
+                // the final string. We need to coerce them here,
+                // so OpInterpolate definitely has a string to consume.
+                ast::InterpolPart::Interpolation(ipol) => {
+                    self.compile(slot, ipol.expr().unwrap());
+                    // implicitly forces as well
+                    self.push_op(
+                        OpCode::OpCoerceToString(CoercionKind {
+                            strong: false,
+                            import_paths: true,
+                        }),
+                        ipol,
+                    );
+                }
+
+                ast::InterpolPart::Literal(lit) => {
+                    self.emit_constant(Value::from(lit.as_str()), parent_node);
+                }
+            }
+        }
+
+        if parts.len() != 1 {
+            self.push_op(OpCode::OpInterpolate(Count(parts.len())), parent_node);
+        }
+    }
+
+    fn compile_str(&mut self, slot: LocalIdx, node: &ast::Str) {
+        let parts = node.normalized_parts();
+
+        // We need to thunk string expressions if they are the result of
+        // interpolation. A string that only consists of a single part (`"${foo}"`)
+        // can't desugar to the enclosed expression (`foo`) because we need to
+        // coerce the result to a string value. This would require forcing the
+        // value of the inner expression, so we need to wrap it in another thunk.
+        if parts.len() != 1 || matches!(&parts[0], ast::InterpolPart::Interpolation(_)) {
+            self.thunk(slot, node, move |c, s| {
+                c.compile_str_parts(s, node, parts);
+            });
+        } else {
+            self.compile_str_parts(slot, node, parts);
+        }
+    }
+
+    fn compile_unary_op(&mut self, slot: LocalIdx, op: &ast::UnaryOp) {
+        self.compile(slot, op.expr().unwrap());
+        self.emit_force(op);
+
+        let opcode = match op.operator().unwrap() {
+            ast::UnaryOpKind::Invert => OpCode::OpInvert,
+            ast::UnaryOpKind::Negate => OpCode::OpNegate,
+        };
+
+        self.push_op(opcode, op);
+    }
+
+    fn compile_binop(&mut self, slot: LocalIdx, op: &ast::BinOp) {
+        use ast::BinOpKind;
+
+        // Short-circuiting and other strange operators, which are
+        // under the same node type as NODE_BIN_OP, but need to be
+        // handled separately (i.e. before compiling the expressions
+        // used for standard binary operators).
+
+        match op.operator().unwrap() {
+            BinOpKind::And => return self.compile_and(slot, op),
+            BinOpKind::Or => return self.compile_or(slot, op),
+            BinOpKind::Implication => return self.compile_implication(slot, op),
+            _ => {}
+        };
+
+        // For all other operators, the two values need to be left on
+        // the stack in the correct order before pushing the
+        // instruction for the operation itself.
+        self.compile(slot, op.lhs().unwrap());
+        self.emit_force(&op.lhs().unwrap());
+
+        self.compile(slot, op.rhs().unwrap());
+        self.emit_force(&op.rhs().unwrap());
+
+        match op.operator().unwrap() {
+            BinOpKind::Add => self.push_op(OpCode::OpAdd, op),
+            BinOpKind::Sub => self.push_op(OpCode::OpSub, op),
+            BinOpKind::Mul => self.push_op(OpCode::OpMul, op),
+            BinOpKind::Div => self.push_op(OpCode::OpDiv, op),
+            BinOpKind::Update => self.push_op(OpCode::OpAttrsUpdate, op),
+            BinOpKind::Equal => self.push_op(OpCode::OpEqual, op),
+            BinOpKind::Less => self.push_op(OpCode::OpLess, op),
+            BinOpKind::LessOrEq => self.push_op(OpCode::OpLessOrEq, op),
+            BinOpKind::More => self.push_op(OpCode::OpMore, op),
+            BinOpKind::MoreOrEq => self.push_op(OpCode::OpMoreOrEq, op),
+            BinOpKind::Concat => self.push_op(OpCode::OpConcat, op),
+
+            BinOpKind::NotEqual => {
+                self.push_op(OpCode::OpEqual, op);
+                self.push_op(OpCode::OpInvert, op)
+            }
+
+            // Handled by separate branch above.
+            BinOpKind::And | BinOpKind::Implication | BinOpKind::Or => {
+                unreachable!()
+            }
+        };
+    }
+
+    fn compile_and(&mut self, slot: LocalIdx, node: &ast::BinOp) {
+        debug_assert!(
+            matches!(node.operator(), Some(ast::BinOpKind::And)),
+            "compile_and called with wrong operator kind: {:?}",
+            node.operator(),
+        );
+
+        // Leave left-hand side value on the stack.
+        self.compile(slot, node.lhs().unwrap());
+        self.emit_force(&node.lhs().unwrap());
+
+        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), node);
+        // If this value is false, jump over the right-hand side - the
+        // whole expression is false.
+        let end_idx = self.push_op(OpCode::OpJumpIfFalse(JumpOffset(0)), node);
+
+        // Otherwise, remove the previous value and leave the
+        // right-hand side on the stack. Its result is now the value
+        // of the whole expression.
+        self.push_op(OpCode::OpPop, node);
+        self.compile(slot, node.rhs().unwrap());
+        self.emit_force(&node.rhs().unwrap());
+
+        self.patch_jump(end_idx);
+        self.push_op(OpCode::OpAssertBool, node);
+        self.patch_jump(throw_idx);
+    }
+
+    fn compile_or(&mut self, slot: LocalIdx, node: &ast::BinOp) {
+        debug_assert!(
+            matches!(node.operator(), Some(ast::BinOpKind::Or)),
+            "compile_or called with wrong operator kind: {:?}",
+            node.operator(),
+        );
+
+        // Leave left-hand side value on the stack
+        self.compile(slot, node.lhs().unwrap());
+        self.emit_force(&node.lhs().unwrap());
+
+        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), node);
+        // Opposite of above: If this value is **true**, we can
+        // short-circuit the right-hand side.
+        let end_idx = self.push_op(OpCode::OpJumpIfTrue(JumpOffset(0)), node);
+        self.push_op(OpCode::OpPop, node);
+        self.compile(slot, node.rhs().unwrap());
+        self.emit_force(&node.rhs().unwrap());
+
+        self.patch_jump(end_idx);
+        self.push_op(OpCode::OpAssertBool, node);
+        self.patch_jump(throw_idx);
+    }
+
+    fn compile_implication(&mut self, slot: LocalIdx, node: &ast::BinOp) {
+        debug_assert!(
+            matches!(node.operator(), Some(ast::BinOpKind::Implication)),
+            "compile_implication called with wrong operator kind: {:?}",
+            node.operator(),
+        );
+
+        // Leave left-hand side value on the stack and invert it.
+        self.compile(slot, node.lhs().unwrap());
+        self.emit_force(&node.lhs().unwrap());
+        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), node);
+        self.push_op(OpCode::OpInvert, node);
+
+        // Exactly as `||` (because `a -> b` = `!a || b`).
+        let end_idx = self.push_op(OpCode::OpJumpIfTrue(JumpOffset(0)), node);
+        self.push_op(OpCode::OpPop, node);
+        self.compile(slot, node.rhs().unwrap());
+        self.emit_force(&node.rhs().unwrap());
+
+        self.patch_jump(end_idx);
+        self.push_op(OpCode::OpAssertBool, node);
+        self.patch_jump(throw_idx);
+    }
+
+    /// Compile list literals into equivalent bytecode. List
+    /// construction is fairly simple, consisting of pushing code for
+    /// each literal element and an instruction with the element
+    /// count.
+    ///
+    /// The VM, after evaluating the code for each element, simply
+    /// constructs the list from the given number of elements.
+    fn compile_list(&mut self, slot: LocalIdx, node: &ast::List) {
+        let mut count = 0;
+
+        // Open a temporary scope to correctly account for stack items
+        // that exist during the construction.
+        self.scope_mut().begin_scope();
+
+        for item in node.items() {
+            // Start tracing new stack slots from the second list
+            // element onwards. The first list element is located in
+            // the stack slot of the list itself.
+            let item_slot = match count {
+                0 => slot,
+                _ => {
+                    let item_span = self.span_for(&item);
+                    self.scope_mut().declare_phantom(item_span, false)
+                }
+            };
+
+            count += 1;
+            self.compile(item_slot, item);
+            self.scope_mut().mark_initialised(item_slot);
+        }
+
+        if count == 0 {
+            self.unthunk();
+        }
+
+        self.push_op(OpCode::OpList(Count(count)), node);
+        self.scope_mut().end_scope();
+    }
+
+    fn compile_attr(&mut self, slot: LocalIdx, node: &ast::Attr) {
+        match node {
+            ast::Attr::Dynamic(dynamic) => {
+                self.compile(slot, dynamic.expr().unwrap());
+                self.emit_force(&dynamic.expr().unwrap());
+            }
+
+            ast::Attr::Str(s) => {
+                self.compile_str(slot, s);
+                self.emit_force(s);
+            }
+
+            ast::Attr::Ident(ident) => self.emit_literal_ident(ident),
+        }
+    }
+
+    fn compile_has_attr(&mut self, slot: LocalIdx, node: &ast::HasAttr) {
+        // Put the attribute set on the stack.
+        self.compile(slot, node.expr().unwrap());
+        self.emit_force(node);
+
+        // Push all path fragments with an operation for fetching the
+        // next nested element, for all fragments except the last one.
+        for (count, fragment) in node.attrpath().unwrap().attrs().enumerate() {
+            if count > 0 {
+                self.push_op(OpCode::OpAttrsTrySelect, &fragment);
+                self.emit_force(&fragment);
+            }
+
+            self.compile_attr(slot, &fragment);
+        }
+
+        // After the last fragment, emit the actual instruction that
+        // leaves a boolean on the stack.
+        self.push_op(OpCode::OpHasAttr, node);
+    }
+
+    /// When compiling select or select_or expressions, an optimisation is
+    /// possible of compiling the set emitted a constant attribute set by
+    /// immediately replacing it with the actual value.
+    ///
+    /// We take care not to emit an error here, as that would interfere with
+    /// thunking behaviour (there can be perfectly valid Nix code that accesses
+    /// a statically known attribute set that is lacking a key, because that
+    /// thunk is never evaluated). If anything is missing, just inform the
+    /// caller that the optimisation did not take place and move on. We may want
+    /// to emit warnings here in the future.
+    fn optimise_select(&mut self, path: &ast::Attrpath) -> bool {
+        // If compiling the set emitted a constant attribute set, the
+        // associated constant can immediately be replaced with the
+        // actual value.
+        //
+        // We take care not to emit an error here, as that would
+        // interfere with thunking behaviour (there can be perfectly
+        // valid Nix code that accesses a statically known attribute
+        // set that is lacking a key, because that thunk is never
+        // evaluated). If anything is missing, just move on. We may
+        // want to emit warnings here in the future.
+        if let Some(OpCode::OpConstant(ConstantIdx(idx))) = self.chunk().code.last().cloned() {
+            let constant = &mut self.chunk().constants[idx];
+            if let Value::Attrs(attrs) = constant {
+                let mut path_iter = path.attrs();
+
+                // Only do this optimisation if there is a *single*
+                // element in the attribute path. It is extremely
+                // unlikely that we'd have a static nested set.
+                if let (Some(attr), None) = (path_iter.next(), path_iter.next()) {
+                    // Only do this optimisation for statically known attrs.
+                    if let Some(ident) = expr_static_attr_str(&attr) {
+                        if let Some(selected_value) = attrs.select(ident.as_bytes()) {
+                            *constant = selected_value.clone();
+
+                            // If this worked, we can unthunk the current thunk.
+                            self.unthunk();
+
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+
+        false
+    }
+
+    fn compile_select(&mut self, slot: LocalIdx, node: &ast::Select) {
+        let set = node.expr().unwrap();
+        let path = node.attrpath().unwrap();
+
+        if node.or_token().is_some() {
+            return self.compile_select_or(slot, set, path, node.default_expr().unwrap());
+        }
+
+        // Push the set onto the stack
+        self.compile(slot, set.clone());
+        if self.optimise_select(&path) {
+            return;
+        }
+
+        // Compile each key fragment and emit access instructions.
+        //
+        // TODO: multi-select instruction to avoid re-pushing attrs on
+        // nested selects.
+        for fragment in path.attrs() {
+            // Force the current set value.
+            self.emit_force(&set);
+
+            self.compile_attr(slot, &fragment);
+            self.push_op(OpCode::OpAttrsSelect, &fragment);
+        }
+    }
+
+    /// Compile an `or` expression into a chunk of conditional jumps.
+    ///
+    /// If at any point during attribute set traversal a key is
+    /// missing, the `OpAttrOrNotFound` instruction will leave a
+    /// special sentinel value on the stack.
+    ///
+    /// After each access, a conditional jump evaluates the top of the
+    /// stack and short-circuits to the default value if it sees the
+    /// sentinel.
+    ///
+    /// Code like `{ a.b = 1; }.a.c or 42` yields this bytecode and
+    /// runtime stack:
+    ///
+    /// ```notrust
+    ///            Bytecode                     Runtime stack
+    ///  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
+    ///  β”‚    ...                     β”‚   β”‚ ...                     β”‚
+    ///  β”‚ 5  OP_ATTRS(1)             β”‚ β†’ β”‚ 5  [ { a.b = 1; }     ] β”‚
+    ///  β”‚ 6  OP_CONSTANT("a")        β”‚ β†’ β”‚ 6  [ { a.b = 1; } "a" ] β”‚
+    ///  β”‚ 7  OP_ATTR_OR_NOT_FOUND    β”‚ β†’ β”‚ 7  [ { b = 1; }       ] β”‚
+    ///  β”‚ 8  JUMP_IF_NOT_FOUND(13)   β”‚ β†’ β”‚ 8  [ { b = 1; }       ] β”‚
+    ///  β”‚ 9  OP_CONSTANT("C")        β”‚ β†’ β”‚ 9  [ { b = 1; } "c"   ] β”‚
+    ///  β”‚ 10 OP_ATTR_OR_NOT_FOUND    β”‚ β†’ β”‚ 10 [ NOT_FOUND        ] β”‚
+    ///  β”‚ 11 JUMP_IF_NOT_FOUND(13)   β”‚ β†’ β”‚ 11 [                  ] β”‚
+    ///  β”‚ 12 JUMP(14)                β”‚   β”‚ ..     jumped over      β”‚
+    ///  β”‚ 13 CONSTANT(42)            β”‚ β†’ β”‚ 12 [ 42 ]               β”‚
+    ///  β”‚ 14 ...                     β”‚   β”‚ ..   ....               β”‚
+    ///  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
+    /// ```
+    fn compile_select_or(
+        &mut self,
+        slot: LocalIdx,
+        set: ast::Expr,
+        path: ast::Attrpath,
+        default: ast::Expr,
+    ) {
+        self.compile(slot, set);
+        if self.optimise_select(&path) {
+            return;
+        }
+
+        let mut jumps = vec![];
+
+        for fragment in path.attrs() {
+            self.emit_force(&fragment);
+            self.compile_attr(slot, &fragment.clone());
+            self.push_op(OpCode::OpAttrsTrySelect, &fragment);
+            jumps.push(self.push_op(OpCode::OpJumpIfNotFound(JumpOffset(0)), &fragment));
+        }
+
+        let final_jump = self.push_op(OpCode::OpJump(JumpOffset(0)), &path);
+
+        for jump in jumps {
+            self.patch_jump(jump);
+        }
+
+        // Compile the default value expression and patch the final
+        // jump to point *beyond* it.
+        self.compile(slot, default);
+        self.patch_jump(final_jump);
+    }
+
+    /// Compile `assert` expressions using jumping instructions in the VM.
+    ///
+    /// ```notrust
+    ///                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
+    ///                        β”‚ 0  [ conditional ]  β”‚
+    ///                        β”‚ 1   JUMP_IF_FALSE  →┼─┐
+    ///                        β”‚ 2  [  main body  ]  β”‚ β”‚ Jump to else body if
+    ///                       β”Œβ”Όβ”€3─←     JUMP        β”‚ β”‚ condition is false.
+    ///  Jump over else body  β”‚β”‚ 4   OP_ASSERT_FAIL β†β”Όβ”€β”˜
+    ///  if condition is true.└┼─5─→     ...         β”‚
+    ///                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
+    /// ```
+    fn compile_assert(&mut self, slot: LocalIdx, node: &ast::Assert) {
+        // Compile the assertion condition to leave its value on the stack.
+        self.compile(slot, node.condition().unwrap());
+        self.emit_force(&node.condition().unwrap());
+        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), node);
+        let then_idx = self.push_op(OpCode::OpJumpIfFalse(JumpOffset(0)), node);
+
+        self.push_op(OpCode::OpPop, node);
+        self.compile(slot, node.body().unwrap());
+
+        let else_idx = self.push_op(OpCode::OpJump(JumpOffset(0)), node);
+
+        self.patch_jump(then_idx);
+        self.push_op(OpCode::OpPop, node);
+        self.push_op(OpCode::OpAssertFail, &node.condition().unwrap());
+
+        self.patch_jump(else_idx);
+        self.patch_jump(throw_idx);
+    }
+
+    /// Compile conditional expressions using jumping instructions in the VM.
+    ///
+    /// ```notrust
+    ///                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
+    ///                        β”‚ 0  [ conditional ] β”‚
+    ///                        β”‚ 1   JUMP_IF_FALSE →┼─┐
+    ///                        β”‚ 2  [  main body  ] β”‚ β”‚ Jump to else body if
+    ///                       β”Œβ”Όβ”€3─←     JUMP       β”‚ β”‚ condition is false.
+    ///  Jump over else body  β”‚β”‚ 4  [  else body  ]β†β”Όβ”€β”˜
+    ///  if condition is true.└┼─5─→     ...        β”‚
+    ///                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
+    /// ```
+    fn compile_if_else(&mut self, slot: LocalIdx, node: &ast::IfElse) {
+        self.compile(slot, node.condition().unwrap());
+        self.emit_force(&node.condition().unwrap());
+
+        let throw_idx = self.push_op(
+            OpCode::OpJumpIfCatchable(JumpOffset(0)),
+            &node.condition().unwrap(),
+        );
+        let then_idx = self.push_op(
+            OpCode::OpJumpIfFalse(JumpOffset(0)),
+            &node.condition().unwrap(),
+        );
+
+        self.push_op(OpCode::OpPop, node); // discard condition value
+        self.compile(slot, node.body().unwrap());
+
+        let else_idx = self.push_op(OpCode::OpJump(JumpOffset(0)), node);
+
+        self.patch_jump(then_idx); // patch jump *to* else_body
+        self.push_op(OpCode::OpPop, node); // discard condition value
+        self.compile(slot, node.else_body().unwrap());
+
+        self.patch_jump(else_idx); // patch jump *over* else body
+        self.patch_jump(throw_idx); // patch jump *over* else body
+    }
+
+    /// Compile `with` expressions by emitting instructions that
+    /// pop/remove the indices of attribute sets that are implicitly
+    /// in scope through `with` on the "with-stack".
+    fn compile_with(&mut self, slot: LocalIdx, node: &ast::With) {
+        self.scope_mut().begin_scope();
+        // TODO: Detect if the namespace is just an identifier, and
+        // resolve that directly (thus avoiding duplication on the
+        // stack).
+        self.compile(slot, node.namespace().unwrap());
+
+        let span = self.span_for(&node.namespace().unwrap());
+
+        // The attribute set from which `with` inherits values
+        // occupies a slot on the stack, but this stack slot is not
+        // directly accessible. As it must be accounted for to
+        // calculate correct offsets, what we call a "phantom" local
+        // is declared here.
+        let local_idx = self.scope_mut().declare_phantom(span, true);
+        let with_idx = self.scope().stack_index(local_idx);
+
+        self.scope_mut().push_with();
+
+        self.push_op(OpCode::OpPushWith(with_idx), &node.namespace().unwrap());
+
+        self.compile(slot, node.body().unwrap());
+
+        self.push_op(OpCode::OpPopWith, node);
+        self.scope_mut().pop_with();
+        self.cleanup_scope(node);
+    }
+
+    /// Compiles pattern function arguments, such as `{ a, b }: ...`.
+    ///
+    /// These patterns are treated as a special case of locals binding
+    /// where the attribute set itself is placed on the first stack
+    /// slot of the call frame (either as a phantom, or named in case
+    /// of an `@` binding), and the function call sets up the rest of
+    /// the stack as if the parameters were rewritten into a `let`
+    /// binding.
+    ///
+    /// For example:
+    ///
+    /// ```nix
+    /// ({ a, b ? 2, c ? a * b, ... }@args: <body>)  { a = 10; }
+    /// ```
+    ///
+    /// would be compiled similarly to a binding such as
+    ///
+    /// ```nix
+    /// let args = { a = 10; };
+    /// in let a = args.a;
+    ///        b = args.a or 2;
+    ///        c = args.c or a * b;
+    ///    in <body>
+    /// ```
+    ///
+    /// However, there are two properties of pattern function arguments that can
+    /// not be compiled by desugaring in this way:
+    ///
+    /// 1. Bindings have to fail if too many arguments are provided. This is
+    ///    done by emitting a special instruction that checks the set of keys
+    ///    from a constant containing the expected keys.
+    /// 2. Formal arguments with a default expression are (as an optimization and
+    ///    because it is simpler) not wrapped in another thunk, instead compiled
+    ///    and accessed separately. This means that the default expression may
+    ///    never make it into the local's stack slot if the argument is provided
+    ///    by the caller. We need to take this into account and skip any
+    ///    operations specific to the expression like thunk finalisation in such
+    ///    cases.
+    fn compile_param_pattern(&mut self, pattern: &ast::Pattern) -> (Formals, CodeIdx) {
+        let span = self.span_for(pattern);
+
+        let (set_idx, pat_bind_name) = match pattern.pat_bind() {
+            Some(name) => {
+                let pat_bind_name = name.ident().unwrap().to_string();
+                (
+                    self.declare_local(&name, pat_bind_name.clone()),
+                    Some(pat_bind_name),
+                )
+            }
+            None => (self.scope_mut().declare_phantom(span, true), None),
+        };
+
+        // At call time, the attribute set is already at the top of the stack.
+        self.scope_mut().mark_initialised(set_idx);
+        self.emit_force(pattern);
+        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), pattern);
+        // Evaluation fails on a type error, even if the argument(s) are unused.
+        self.push_op(OpCode::OpAssertAttrs, pattern);
+
+        let ellipsis = pattern.ellipsis_token().is_some();
+        if !ellipsis {
+            self.push_op(OpCode::OpValidateClosedFormals, pattern);
+        }
+
+        // Similar to `let ... in ...`, we now do multiple passes over
+        // the bindings to first declare them, then populate them, and
+        // then finalise any necessary recursion into the scope.
+        let mut entries: Vec<TrackedFormal> = vec![];
+        let mut arguments = BTreeMap::default();
+
+        for entry in pattern.pat_entries() {
+            let ident = entry.ident().unwrap();
+            let idx = self.declare_local(&ident, ident.to_string());
+
+            arguments.insert(ident.into(), entry.default().is_some());
+
+            if let Some(default_expr) = entry.default() {
+                entries.push(TrackedFormal::WithDefault {
+                    local_idx: idx,
+                    // This phantom is used to track at runtime (!) whether we need to
+                    // finalise the local's stack slot or not. The relevant instructions are
+                    // emitted in the second pass where the mechanism is explained as well.
+                    finalise_request_idx: {
+                        let span = self.span_for(&default_expr);
+                        self.scope_mut().declare_phantom(span, false)
+                    },
+                    default_expr,
+                    pattern_entry: entry,
+                });
+            } else {
+                entries.push(TrackedFormal::NoDefault {
+                    local_idx: idx,
+                    pattern_entry: entry,
+                });
+            }
+        }
+
+        // For each of the bindings, push the set on the stack and
+        // attempt to select from it.
+        let stack_idx = self.scope().stack_index(set_idx);
+        for tracked_formal in entries.iter() {
+            self.push_op(OpCode::OpGetLocal(stack_idx), pattern);
+            self.emit_literal_ident(&tracked_formal.pattern_entry().ident().unwrap());
+
+            let idx = tracked_formal.local_idx();
+
+            // Use the same mechanism as `compile_select_or` if a
+            // default value was provided, or simply select otherwise.
+            match tracked_formal {
+                TrackedFormal::WithDefault {
+                    default_expr,
+                    pattern_entry,
+                    ..
+                } => {
+                    // The tricky bit about compiling a formal argument with a default value
+                    // is that the default may be a thunk that may depend on the value of
+                    // other formal arguments, i.e. may need to be finalised. This
+                    // finalisation can only happen if we are actually using the default
+                    // valueβ€”otherwise OpFinalise will crash on an already finalised (or
+                    // non-thunk) value.
+                    //
+                    // Thus we use an additional local to track whether we wound up
+                    // defaulting or not. `FinaliseRequest(false)` indicates that we should
+                    // not finalise, as we did not default.
+                    //
+                    // We are being wasteful with VM stack space in case of default
+                    // expressions that don't end up needing to be finalised. Unfortunately
+                    // we only know better after compiling the default expression, so
+                    // avoiding unnecessary locals would mean we'd need to modify the chunk
+                    // after the fact.
+                    self.push_op(OpCode::OpAttrsTrySelect, &pattern_entry.ident().unwrap());
+                    let jump_to_default =
+                        self.push_op(OpCode::OpJumpIfNotFound(JumpOffset(0)), default_expr);
+
+                    self.emit_constant(Value::FinaliseRequest(false), default_expr);
+
+                    let jump_over_default =
+                        self.push_op(OpCode::OpJump(JumpOffset(0)), default_expr);
+
+                    self.patch_jump(jump_to_default);
+
+                    // Does not need to thunked since compile() already does so when necessary
+                    self.compile(idx, default_expr.clone());
+
+                    self.emit_constant(Value::FinaliseRequest(true), default_expr);
+
+                    self.patch_jump(jump_over_default);
+                }
+                TrackedFormal::NoDefault { pattern_entry, .. } => {
+                    self.push_op(OpCode::OpAttrsSelect, &pattern_entry.ident().unwrap());
+                }
+            }
+
+            self.scope_mut().mark_initialised(idx);
+            if let TrackedFormal::WithDefault {
+                finalise_request_idx,
+                ..
+            } = tracked_formal
+            {
+                self.scope_mut().mark_initialised(*finalise_request_idx);
+            }
+        }
+
+        for tracked_formal in entries.iter() {
+            if self.scope()[tracked_formal.local_idx()].needs_finaliser {
+                let stack_idx = self.scope().stack_index(tracked_formal.local_idx());
+                match tracked_formal {
+                    TrackedFormal::NoDefault { .. } =>
+                        panic!("Tvix bug: local for pattern formal needs finaliser, but has no default expr"),
+                    TrackedFormal::WithDefault { finalise_request_idx, .. } => {
+                        let finalise_request_stack_idx = self.scope().stack_index(*finalise_request_idx);
+
+                        // TODO(sterni): better spans
+                        self.push_op(
+                            OpCode::OpGetLocal(finalise_request_stack_idx),
+                            pattern
+                        );
+                        let jump_over_finalise =
+                            self.push_op(
+                                OpCode::OpJumpIfNoFinaliseRequest(
+                                    JumpOffset(0)),
+                                pattern
+                            );
+                        self.push_op(
+                            OpCode::OpFinalise(stack_idx),
+                            pattern,
+                        );
+                        self.patch_jump(jump_over_finalise);
+                        // Get rid of finaliser request value on the stack
+                        self.push_op(OpCode::OpPop, pattern);
+                    }
+                }
+            }
+        }
+
+        (
+            (Formals {
+                arguments,
+                ellipsis,
+                span,
+                name: pat_bind_name,
+            }),
+            throw_idx,
+        )
+    }
+
+    fn compile_lambda(&mut self, slot: LocalIdx, node: &ast::Lambda) -> Option<CodeIdx> {
+        // Compile the function itself, recording its formal arguments (if any)
+        // for later use
+        let formals = match node.param().unwrap() {
+            ast::Param::Pattern(pat) => Some(self.compile_param_pattern(&pat)),
+
+            ast::Param::IdentParam(param) => {
+                let name = param
+                    .ident()
+                    .unwrap()
+                    .ident_token()
+                    .unwrap()
+                    .text()
+                    .to_string();
+
+                let idx = self.declare_local(&param, &name);
+                self.scope_mut().mark_initialised(idx);
+                None
+            }
+        };
+
+        self.compile(slot, node.body().unwrap());
+        if let Some((formals, throw_idx)) = formals {
+            self.context_mut().lambda.formals = Some(formals);
+            Some(throw_idx)
+        } else {
+            self.context_mut().lambda.formals = None;
+            None
+        }
+    }
+
+    fn thunk<N, F>(&mut self, outer_slot: LocalIdx, node: &N, content: F)
+    where
+        N: ToSpan,
+        F: FnOnce(&mut Compiler, LocalIdx),
+    {
+        self.compile_lambda_or_thunk(true, outer_slot, node, |comp, idx| {
+            content(comp, idx);
+            None
+        })
+    }
+
+    /// Mark the current thunk as redundant, i.e. possible to merge directly
+    /// into its parent lambda context without affecting runtime behaviour.
+    fn unthunk(&mut self) {
+        self.context_mut().unthunk = true;
+    }
+
+    /// Compile an expression into a runtime closure or thunk
+    fn compile_lambda_or_thunk<N, F>(
+        &mut self,
+        is_suspended_thunk: bool,
+        outer_slot: LocalIdx,
+        node: &N,
+        content: F,
+    ) where
+        N: ToSpan,
+        F: FnOnce(&mut Compiler, LocalIdx) -> Option<CodeIdx>,
+    {
+        let name = self.scope()[outer_slot].name();
+        self.new_context();
+
+        // Set the (optional) name of the current slot on the lambda that is
+        // being compiled.
+        self.context_mut().lambda.name = name;
+
+        let span = self.span_for(node);
+        let slot = self.scope_mut().declare_phantom(span, false);
+        self.scope_mut().begin_scope();
+
+        let throw_idx = content(self, slot);
+        self.cleanup_scope(node);
+        if let Some(throw_idx) = throw_idx {
+            self.patch_jump(throw_idx);
+        }
+
+        // TODO: determine and insert enclosing name, if available.
+
+        // Pop the lambda context back off, and emit the finished
+        // lambda as a constant.
+        let mut compiled = self.contexts.pop().unwrap();
+
+        // The compiler might have decided to unthunk, i.e. raise the compiled
+        // code to the parent context. In that case we do so and return right
+        // away.
+        if compiled.unthunk && is_suspended_thunk {
+            self.chunk().extend(compiled.lambda.chunk);
+            return;
+        }
+
+        // Emit an instruction to inform the VM that the chunk has ended.
+        compiled
+            .lambda
+            .chunk
+            .push_op(OpCode::OpReturn, self.span_for(node));
+
+        // Capturing the with stack counts as an upvalue, as it is
+        // emitted as an upvalue data instruction.
+        if compiled.captures_with_stack {
+            compiled.lambda.upvalue_count += 1;
+        }
+
+        let lambda = Rc::new(compiled.lambda);
+        if is_suspended_thunk {
+            self.observer.observe_compiled_thunk(&lambda);
+        } else {
+            self.observer.observe_compiled_lambda(&lambda);
+        }
+
+        // If no upvalues are captured, emit directly and move on.
+        if lambda.upvalue_count == 0 {
+            self.emit_constant(
+                if is_suspended_thunk {
+                    Value::Thunk(Thunk::new_suspended(lambda, LightSpan::new_actual(span)))
+                } else {
+                    Value::Closure(Rc::new(Closure::new(lambda)))
+                },
+                node,
+            );
+            return;
+        }
+
+        // Otherwise, we need to emit the variable number of
+        // operands that allow the runtime to close over the
+        // upvalues and leave a blueprint in the constant index from
+        // which the result can be constructed.
+        let blueprint_idx = self.chunk().push_constant(Value::Blueprint(lambda));
+
+        let code_idx = self.push_op(
+            if is_suspended_thunk {
+                OpCode::OpThunkSuspended(blueprint_idx)
+            } else {
+                OpCode::OpThunkClosure(blueprint_idx)
+            },
+            node,
+        );
+
+        self.emit_upvalue_data(
+            outer_slot,
+            node,
+            compiled.scope.upvalues,
+            compiled.captures_with_stack,
+        );
+
+        if !is_suspended_thunk && !self.scope()[outer_slot].needs_finaliser {
+            if !self.scope()[outer_slot].must_thunk {
+                // The closure has upvalues, but is not recursive.  Therefore no thunk is required,
+                // which saves us the overhead of Rc<RefCell<>>
+                self.chunk()[code_idx] = OpCode::OpClosure(blueprint_idx);
+            } else {
+                // This case occurs when a closure has upvalue-references to itself but does not need a
+                // finaliser.  Since no OpFinalise will be emitted later on we synthesize one here.
+                // It is needed here only to set [`Closure::is_finalised`] which is used for sanity checks.
+                #[cfg(debug_assertions)]
+                self.push_op(
+                    OpCode::OpFinalise(self.scope().stack_index(outer_slot)),
+                    &self.span_for(node),
+                );
+            }
+        }
+    }
+
+    fn compile_apply(&mut self, slot: LocalIdx, node: &ast::Apply) {
+        // To call a function, we leave its arguments on the stack,
+        // followed by the function expression itself, and then emit a
+        // call instruction. This way, the stack is perfectly laid out
+        // to enter the function call straight away.
+        self.compile(slot, node.argument().unwrap());
+        self.compile(slot, node.lambda().unwrap());
+        self.emit_force(&node.lambda().unwrap());
+        self.push_op(OpCode::OpCall, node);
+    }
+
+    /// Emit the data instructions that the runtime needs to correctly
+    /// assemble the upvalues struct.
+    fn emit_upvalue_data<T: ToSpan>(
+        &mut self,
+        slot: LocalIdx,
+        node: &T,
+        upvalues: Vec<Upvalue>,
+        capture_with: bool,
+    ) {
+        for upvalue in upvalues {
+            match upvalue.kind {
+                UpvalueKind::Local(idx) => {
+                    let target = &self.scope()[idx];
+                    let stack_idx = self.scope().stack_index(idx);
+
+                    // If the target is not yet initialised, we need to defer
+                    // the local access
+                    if !target.initialised {
+                        self.push_op(OpCode::DataDeferredLocal(stack_idx), &upvalue.span);
+                        self.scope_mut().mark_needs_finaliser(slot);
+                    } else {
+                        // a self-reference
+                        if slot == idx {
+                            self.scope_mut().mark_must_thunk(slot);
+                        }
+                        self.push_op(OpCode::DataStackIdx(stack_idx), &upvalue.span);
+                    }
+                }
+
+                UpvalueKind::Upvalue(idx) => {
+                    self.push_op(OpCode::DataUpvalueIdx(idx), &upvalue.span);
+                }
+            };
+        }
+
+        if capture_with {
+            // TODO(tazjin): probably better to emit span for the ident that caused this
+            self.push_op(OpCode::DataCaptureWith, node);
+        }
+    }
+
+    /// Emit the literal string value of an identifier. Required for
+    /// several operations related to attribute sets, where
+    /// identifiers are used as string keys.
+    fn emit_literal_ident(&mut self, ident: &ast::Ident) {
+        self.emit_constant(Value::String(ident.clone().into()), ident);
+    }
+
+    /// Patch the jump instruction at the given index, setting its
+    /// jump offset from the placeholder to the current code position.
+    ///
+    /// This is required because the actual target offset of jumps is
+    /// not known at the time when the jump operation itself is
+    /// emitted.
+    fn patch_jump(&mut self, idx: CodeIdx) {
+        let offset = JumpOffset(self.chunk().code.len() - 1 - idx.0);
+
+        match &mut self.chunk().code[idx.0] {
+            OpCode::OpJump(n)
+            | OpCode::OpJumpIfFalse(n)
+            | OpCode::OpJumpIfTrue(n)
+            | OpCode::OpJumpIfCatchable(n)
+            | OpCode::OpJumpIfNotFound(n)
+            | OpCode::OpJumpIfNoFinaliseRequest(n) => {
+                *n = offset;
+            }
+
+            op => panic!("attempted to patch unsupported op: {:?}", op),
+        }
+    }
+
+    /// Decrease scope depth of the current function and emit
+    /// instructions to clean up the stack at runtime.
+    fn cleanup_scope<N: ToSpan>(&mut self, node: &N) {
+        // When ending a scope, all corresponding locals need to be
+        // removed, but the value of the body needs to remain on the
+        // stack. This is implemented by a separate instruction.
+        let (popcount, unused_spans) = self.scope_mut().end_scope();
+
+        for span in &unused_spans {
+            self.emit_warning(span, WarningKind::UnusedBinding);
+        }
+
+        if popcount > 0 {
+            self.push_op(OpCode::OpCloseScope(Count(popcount)), node);
+        }
+    }
+
+    /// Open a new lambda context within which to compile a function,
+    /// closure or thunk.
+    fn new_context(&mut self) {
+        self.contexts.push(self.context().inherit());
+    }
+
+    /// Declare a local variable known in the scope that is being
+    /// compiled by pushing it to the locals. This is used to
+    /// determine the stack offset of variables.
+    fn declare_local<S: Into<String>, N: ToSpan>(&mut self, node: &N, name: S) -> LocalIdx {
+        let name = name.into();
+        let depth = self.scope().scope_depth();
+
+        // Do this little dance to turn name:&'a str into the same
+        // string with &'static lifetime, as required by WarningKind
+        if let Some((global_ident, _)) = self.globals.get_key_value(name.as_str()) {
+            self.emit_warning(node, WarningKind::ShadowedGlobal(global_ident));
+        }
+
+        let span = self.span_for(node);
+        let (idx, shadowed) = self.scope_mut().declare_local(name, span);
+
+        if let Some(shadow_idx) = shadowed {
+            let other = &self.scope()[shadow_idx];
+            if other.depth == depth {
+                self.emit_error(node, ErrorKind::VariableAlreadyDefined(other.span));
+            }
+        }
+
+        idx
+    }
+
+    /// Determine whether the current lambda context has any ancestors
+    /// that use dynamic scope resolution, and mark contexts as
+    /// needing to capture their enclosing `with`-stack in their
+    /// upvalues.
+    fn has_dynamic_ancestor(&mut self) -> bool {
+        let mut ancestor_has_with = false;
+
+        for ctx in self.contexts.iter_mut() {
+            if ancestor_has_with {
+                // If the ancestor has an active with stack, mark this
+                // lambda context as needing to capture it.
+                ctx.captures_with_stack = true;
+            } else {
+                // otherwise, check this context and move on
+                ancestor_has_with = ctx.scope.has_with();
+            }
+        }
+
+        ancestor_has_with
+    }
+
+    fn emit_force<N: ToSpan>(&mut self, node: &N) {
+        if let Some(&OpCode::OpConstant(c)) = self.chunk().last_op() {
+            if !self.chunk().get_constant(c).unwrap().is_thunk() {
+                // Optimization: Don't emit a force op for non-thunk constants, since they don't
+                // need one!
+                // TODO: this is probably doable for more ops (?)
+                return;
+            }
+        }
+
+        self.push_op(OpCode::OpForce, node);
+    }
+
+    fn emit_warning<N: ToSpan>(&mut self, node: &N, kind: WarningKind) {
+        let span = self.span_for(node);
+        self.warnings.push(EvalWarning { kind, span })
+    }
+
+    fn emit_error<N: ToSpan>(&mut self, node: &N, kind: ErrorKind) {
+        let span = self.span_for(node);
+        self.errors
+            .push(Error::new(kind, span, self.source.clone()))
+    }
+}
+
+/// Convert a non-dynamic string expression to a string if possible.
+fn expr_static_str(node: &ast::Str) -> Option<SmolStr> {
+    let mut parts = node.normalized_parts();
+
+    if parts.len() != 1 {
+        return None;
+    }
+
+    if let Some(ast::InterpolPart::Literal(lit)) = parts.pop() {
+        return Some(SmolStr::new(lit));
+    }
+
+    None
+}
+
+/// Convert the provided `ast::Attr` into a statically known string if
+/// possible.
+fn expr_static_attr_str(node: &ast::Attr) -> Option<SmolStr> {
+    match node {
+        ast::Attr::Ident(ident) => Some(ident.ident_token().unwrap().text().into()),
+        ast::Attr::Str(s) => expr_static_str(s),
+
+        // The dynamic node type is just a wrapper. C++ Nix does not care
+        // about the dynamic wrapper when determining whether the node
+        // itself is dynamic, it depends solely on the expression inside
+        // (i.e. `let ${"a"} = 1; in a` is valid).
+        ast::Attr::Dynamic(ref dynamic) => match dynamic.expr().unwrap() {
+            ast::Expr::Str(s) => expr_static_str(&s),
+            _ => None,
+        },
+    }
+}
+
+/// Create a delayed source-only builtin compilation, for a builtin
+/// which is written in Nix code.
+///
+/// **Important:** tvix *panics* if a builtin with invalid source code
+/// is supplied. This is because there is no user-friendly way to
+/// thread the errors out of this function right now.
+fn compile_src_builtin(
+    name: &'static str,
+    code: &str,
+    source: SourceCode,
+    weak: &Weak<GlobalsMap>,
+) -> Value {
+    use std::fmt::Write;
+
+    let parsed = rnix::ast::Root::parse(code);
+
+    if !parsed.errors().is_empty() {
+        let mut out = format!("BUG: code for source-builtin '{}' had parser errors", name);
+        for error in parsed.errors() {
+            writeln!(out, "{}", error).unwrap();
+        }
+
+        panic!("{}", out);
+    }
+
+    let file = source.add_file(format!("<src-builtins/{}.nix>", name), code.to_string());
+    let weak = weak.clone();
+
+    Value::Thunk(Thunk::new_suspended_native(Box::new(move || {
+        let result = compile(
+            &parsed.tree().expr().unwrap(),
+            None,
+            weak.upgrade().unwrap(),
+            &source,
+            &file,
+            &mut crate::observer::NoOpObserver {},
+        )
+        .map_err(|e| ErrorKind::NativeError {
+            gen_type: "derivation",
+            err: Box::new(e),
+        })?;
+
+        if !result.errors.is_empty() {
+            return Err(ErrorKind::ImportCompilerError {
+                path: format!("src-builtins/{}.nix", name).into(),
+                errors: result.errors,
+            });
+        }
+
+        Ok(Value::Thunk(Thunk::new_suspended(
+            result.lambda,
+            LightSpan::Actual { span: file.span },
+        )))
+    })))
+}
+
+/// Prepare the full set of globals available in evaluated code. These
+/// are constructed from the set of builtins supplied by the caller,
+/// which are made available globally under the `builtins` identifier.
+///
+/// A subset of builtins (specified by [`GLOBAL_BUILTINS`]) is
+/// available globally *iff* they are set.
+///
+/// Optionally adds the `import` feature if desired by the caller.
+pub fn prepare_globals(
+    builtins: Vec<(&'static str, Value)>,
+    src_builtins: Vec<(&'static str, &'static str)>,
+    source: SourceCode,
+    enable_import: bool,
+) -> Rc<GlobalsMap> {
+    Rc::new_cyclic(Box::new(move |weak: &Weak<GlobalsMap>| {
+        // First step is to construct the builtins themselves as
+        // `NixAttrs`.
+        let mut builtins: GlobalsMap = HashMap::from_iter(builtins);
+
+        // At this point, optionally insert `import` if enabled. To
+        // "tie the knot" of `import` needing the full set of globals
+        // to instantiate its compiler, the `Weak` reference is passed
+        // here.
+        if enable_import {
+            let import = Value::Builtin(import::builtins_import(weak, source.clone()));
+            builtins.insert("import", import);
+        }
+
+        // Next, the actual map of globals which the compiler will use
+        // to resolve identifiers is constructed.
+        let mut globals: GlobalsMap = HashMap::new();
+
+        // builtins contain themselves (`builtins.builtins`), which we
+        // can resolve by manually constructing a suspended thunk that
+        // dereferences the same weak pointer as above.
+        let weak_globals = weak.clone();
+        builtins.insert(
+            "builtins",
+            Value::Thunk(Thunk::new_suspended_native(Box::new(move || {
+                Ok(weak_globals
+                    .upgrade()
+                    .unwrap()
+                    .get("builtins")
+                    .cloned()
+                    .unwrap())
+            }))),
+        );
+
+        // Insert top-level static value builtins.
+        globals.insert("true", Value::Bool(true));
+        globals.insert("false", Value::Bool(false));
+        globals.insert("null", Value::Null);
+
+        // If "source builtins" were supplied, compile them and insert
+        // them.
+        builtins.extend(src_builtins.into_iter().map(move |(name, code)| {
+            let compiled = compile_src_builtin(name, code, source.clone(), weak);
+            (name, compiled)
+        }));
+
+        // Construct the actual `builtins` attribute set and insert it
+        // in the global scope.
+        globals.insert(
+            "builtins",
+            Value::attrs(NixAttrs::from_iter(builtins.clone())),
+        );
+
+        // Finally, the builtins that should be globally available are
+        // "elevated" to the outer scope.
+        for global in GLOBAL_BUILTINS {
+            if let Some(builtin) = builtins.get(global).cloned() {
+                globals.insert(global, builtin);
+            }
+        }
+
+        globals
+    }))
+}
+
+pub fn compile(
+    expr: &ast::Expr,
+    location: Option<PathBuf>,
+    globals: Rc<GlobalsMap>,
+    source: &SourceCode,
+    file: &codemap::File,
+    observer: &mut dyn CompilerObserver,
+) -> EvalResult<CompilationOutput> {
+    let mut c = Compiler::new(location, globals.clone(), source, file, observer)?;
+
+    let root_span = c.span_for(expr);
+    let root_slot = c.scope_mut().declare_phantom(root_span, false);
+    c.compile(root_slot, expr.clone());
+
+    // The final operation of any top-level Nix program must always be
+    // `OpForce`. A thunk should not be returned to the user in an
+    // unevaluated state (though in practice, a value *containing* a
+    // thunk might be returned).
+    c.emit_force(expr);
+    c.push_op(OpCode::OpReturn, &root_span);
+
+    let lambda = Rc::new(c.contexts.pop().unwrap().lambda);
+    c.observer.observe_compiled_toplevel(&lambda);
+
+    Ok(CompilationOutput {
+        lambda,
+        warnings: c.warnings,
+        errors: c.errors,
+        globals,
+    })
+}
diff --git a/tvix/eval/src/compiler/optimiser.rs b/tvix/eval/src/compiler/optimiser.rs
new file mode 100644
index 0000000000..48960d355c
--- /dev/null
+++ b/tvix/eval/src/compiler/optimiser.rs
@@ -0,0 +1,125 @@
+//! Helper functions for extending the compiler with more linter-like
+//! functionality while compiling (i.e. smarter warnings).
+
+use super::*;
+
+use ast::Expr;
+
+/// Optimise the given expression where possible.
+pub(super) fn optimise_expr(c: &mut Compiler, slot: LocalIdx, expr: ast::Expr) -> ast::Expr {
+    match expr {
+        Expr::BinOp(_) => optimise_bin_op(c, slot, expr),
+        _ => expr,
+    }
+}
+
+enum LitBool {
+    Expr(Expr),
+    True(Expr),
+    False(Expr),
+}
+
+/// Is this a literal boolean, or something else?
+fn is_lit_bool(expr: ast::Expr) -> LitBool {
+    if let ast::Expr::Ident(ident) = &expr {
+        match ident.ident_token().unwrap().text() {
+            "true" => LitBool::True(expr),
+            "false" => LitBool::False(expr),
+            _ => LitBool::Expr(expr),
+        }
+    } else {
+        LitBool::Expr(expr)
+    }
+}
+
+/// Detect useless binary operations (i.e. useless bool comparisons).
+fn optimise_bin_op(c: &mut Compiler, slot: LocalIdx, expr: ast::Expr) -> ast::Expr {
+    use ast::BinOpKind;
+
+    // bail out of this check if the user has overridden either `true`
+    // or `false` identifiers. Note that they will have received a
+    // separate warning about this for shadowing the global(s).
+    if c.is_user_defined("true") || c.is_user_defined("false") {
+        return expr;
+    }
+
+    if let Expr::BinOp(op) = &expr {
+        let lhs = is_lit_bool(op.lhs().unwrap());
+        let rhs = is_lit_bool(op.rhs().unwrap());
+
+        match (op.operator().unwrap(), lhs, rhs) {
+            // useless `false` arm in `||` expression
+            (BinOpKind::Or, LitBool::False(f), LitBool::Expr(other))
+            | (BinOpKind::Or, LitBool::Expr(other), LitBool::False(f)) => {
+                c.emit_warning(
+                    &f,
+                    WarningKind::UselessBoolOperation(
+                        "this `false` has no effect on the result of the comparison",
+                    ),
+                );
+
+                return other;
+            }
+
+            // useless `true` arm in `&&` expression
+            (BinOpKind::And, LitBool::True(t), LitBool::Expr(other))
+            | (BinOpKind::And, LitBool::Expr(other), LitBool::True(t)) => {
+                c.emit_warning(
+                    &t,
+                    WarningKind::UselessBoolOperation(
+                        "this `true` has no effect on the result of the comparison",
+                    ),
+                );
+
+                return other;
+            }
+
+            // useless `||` expression (one arm is `true`), return
+            // `true` directly (and warn about dead code on the right)
+            (BinOpKind::Or, LitBool::True(t), LitBool::Expr(other)) => {
+                c.emit_warning(
+                    op,
+                    WarningKind::UselessBoolOperation("this expression is always true"),
+                );
+
+                c.compile_dead_code(slot, other);
+
+                return t;
+            }
+
+            (BinOpKind::Or, _, LitBool::True(t)) | (BinOpKind::Or, LitBool::True(t), _) => {
+                c.emit_warning(
+                    op,
+                    WarningKind::UselessBoolOperation("this expression is always true"),
+                );
+
+                return t;
+            }
+
+            // useless `&&` expression (one arm is `false), same as above
+            (BinOpKind::And, LitBool::False(f), LitBool::Expr(other)) => {
+                c.emit_warning(
+                    op,
+                    WarningKind::UselessBoolOperation("this expression is always false"),
+                );
+
+                c.compile_dead_code(slot, other);
+
+                return f;
+            }
+
+            (BinOpKind::And, _, LitBool::False(f)) | (BinOpKind::Or, LitBool::False(f), _) => {
+                c.emit_warning(
+                    op,
+                    WarningKind::UselessBoolOperation("this expression is always false"),
+                );
+
+                return f;
+            }
+
+            _ => { /* nothing to optimise */ }
+        }
+    }
+
+    expr
+}
diff --git a/tvix/eval/src/compiler/scope.rs b/tvix/eval/src/compiler/scope.rs
new file mode 100644
index 0000000000..892727c107
--- /dev/null
+++ b/tvix/eval/src/compiler/scope.rs
@@ -0,0 +1,378 @@
+//! This module implements the scope-tracking logic of the Tvix
+//! compiler.
+//!
+//! Scoping in Nix is fairly complicated, there are features like
+//! mutually recursive bindings, `with`, upvalue capturing, and so
+//! on that introduce a fair bit of complexity.
+//!
+//! Tvix attempts to do as much of the heavy lifting of this at
+//! compile time, and leave the runtime to mostly deal with known
+//! stack indices. To do this, the compiler simulates where locals
+//! will be at runtime using the data structures implemented here.
+
+use std::{
+    collections::{hash_map, HashMap},
+    ops::Index,
+};
+
+use smol_str::SmolStr;
+
+use crate::opcode::{StackIdx, UpvalueIdx};
+
+#[derive(Debug)]
+enum LocalName {
+    /// Normally declared local with a statically known name.
+    Ident(String),
+
+    /// Phantom stack value (e.g. attribute set used for `with`) that
+    /// must be accounted for to calculate correct stack offsets.
+    Phantom,
+}
+
+/// Represents a single local already known to the compiler.
+#[derive(Debug)]
+pub struct Local {
+    /// Identifier of this local. This is always a statically known
+    /// value (Nix does not allow dynamic identifier names in locals),
+    /// or a "phantom" value not accessible by users.
+    name: LocalName,
+
+    /// Source span at which this local was declared.
+    pub span: codemap::Span,
+
+    /// Scope depth of this local.
+    pub depth: usize,
+
+    /// Is this local initialised?
+    pub initialised: bool,
+
+    /// Is this local known to have been used at all?
+    pub used: bool,
+
+    /// Does this local need to be finalised after the enclosing scope
+    /// is completely constructed?
+    pub needs_finaliser: bool,
+
+    /// Does this local's upvalues contain a reference to itself?
+    pub must_thunk: bool,
+}
+
+impl Local {
+    /// Retrieve the name of the given local (if available).
+    pub fn name(&self) -> Option<SmolStr> {
+        match &self.name {
+            LocalName::Phantom => None,
+            LocalName::Ident(name) => Some(SmolStr::new(name)),
+        }
+    }
+
+    /// Is this local intentionally ignored? (i.e. name starts with `_`)
+    pub fn is_ignored(&self) -> bool {
+        match &self.name {
+            LocalName::Ident(name) => name.starts_with('_'),
+            LocalName::Phantom => false,
+        }
+    }
+}
+
+/// Represents the current position of an identifier as resolved in a scope.
+pub enum LocalPosition {
+    /// Local is not known in this scope.
+    Unknown,
+
+    /// Local is known at the given local index.
+    Known(LocalIdx),
+
+    /// Local is known, but is being accessed recursively within its
+    /// own initialisation. Depending on context, this is either an
+    /// error or forcing a closure/thunk.
+    Recursive(LocalIdx),
+}
+
+/// Represents the different ways in which upvalues can be captured in
+/// closures or thunks.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum UpvalueKind {
+    /// This upvalue captures a local from the stack.
+    Local(LocalIdx),
+
+    /// This upvalue captures an enclosing upvalue.
+    Upvalue(UpvalueIdx),
+}
+
+#[derive(Clone, Debug)]
+pub struct Upvalue {
+    pub kind: UpvalueKind,
+    pub span: codemap::Span,
+}
+
+/// The index of a local in the scope's local array at compile time.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
+pub struct LocalIdx(usize);
+
+/// Helper struct for indexing over `Scope::locals` by name.
+#[derive(Debug)]
+enum ByName {
+    Single(LocalIdx),
+    Shadowed(Vec<LocalIdx>),
+}
+
+impl ByName {
+    /// Add an additional index for this name.
+    fn add_idx(&mut self, new: LocalIdx) {
+        match self {
+            ByName::Shadowed(indices) => indices.push(new),
+            ByName::Single(idx) => {
+                *self = ByName::Shadowed(vec![*idx, new]);
+            }
+        }
+    }
+
+    /// Remove the most recent index for this name, unless it is a
+    /// single. Returns `true` if an entry was removed.
+    fn remove_idx(&mut self) -> bool {
+        match self {
+            ByName::Single(_) => false,
+            ByName::Shadowed(indices) => match indices[..] {
+                [fst, _snd] => {
+                    *self = ByName::Single(fst);
+                    true
+                }
+                _ => {
+                    indices.pop();
+                    true
+                }
+            },
+        }
+    }
+
+    /// Return the most recent index.
+    pub fn index(&self) -> LocalIdx {
+        match self {
+            ByName::Single(idx) => *idx,
+            ByName::Shadowed(vec) => *vec.last().unwrap(),
+        }
+    }
+}
+
+/// Represents a scope known during compilation, which can be resolved
+/// directly to stack indices.
+#[derive(Debug, Default)]
+pub struct Scope {
+    locals: Vec<Local>,
+    pub upvalues: Vec<Upvalue>,
+
+    /// Secondary by-name index over locals.
+    by_name: HashMap<String, ByName>,
+
+    /// How many scopes "deep" are these locals?
+    scope_depth: usize,
+
+    /// Current size of the `with`-stack at runtime.
+    with_stack_size: usize,
+}
+
+impl Index<LocalIdx> for Scope {
+    type Output = Local;
+
+    fn index(&self, index: LocalIdx) -> &Self::Output {
+        &self.locals[index.0]
+    }
+}
+
+impl Scope {
+    /// Inherit scope details from a parent scope (required for
+    /// correctly nesting scopes in lambdas and thunks when special
+    /// scope features like dynamic resolution are present).
+    pub fn inherit(&self) -> Self {
+        Self {
+            scope_depth: self.scope_depth + 1,
+            with_stack_size: self.with_stack_size,
+            ..Default::default()
+        }
+    }
+
+    /// Increase the `with`-stack size of this scope.
+    pub fn push_with(&mut self) {
+        self.with_stack_size += 1;
+    }
+
+    /// Decrease the `with`-stack size of this scope.
+    pub fn pop_with(&mut self) {
+        self.with_stack_size -= 1;
+    }
+
+    /// Does this scope currently require dynamic runtime resolution
+    /// of identifiers that could not be found?
+    pub fn has_with(&self) -> bool {
+        self.with_stack_size > 0
+    }
+
+    /// Resolve the stack index of a statically known local.
+    pub fn resolve_local(&mut self, name: &str) -> LocalPosition {
+        if let Some(by_name) = self.by_name.get(name) {
+            let idx = by_name.index();
+            let local = self
+                .locals
+                .get_mut(idx.0)
+                .expect("invalid compiler state: indexed local missing");
+
+            local.used = true;
+
+            // This local is still being initialised, meaning that
+            // we know its final runtime stack position, but it is
+            // not yet on the stack.
+            if !local.initialised {
+                return LocalPosition::Recursive(idx);
+            }
+
+            return LocalPosition::Known(idx);
+        }
+
+        LocalPosition::Unknown
+    }
+
+    /// Declare a local variable that occupies a stack slot and should
+    /// be accounted for, but is not directly accessible by users
+    /// (e.g. attribute sets used for `with`).
+    pub fn declare_phantom(&mut self, span: codemap::Span, initialised: bool) -> LocalIdx {
+        let idx = self.locals.len();
+        self.locals.push(Local {
+            initialised,
+            span,
+            name: LocalName::Phantom,
+            depth: self.scope_depth,
+            needs_finaliser: false,
+            must_thunk: false,
+            used: true,
+        });
+
+        LocalIdx(idx)
+    }
+
+    /// Declare an uninitialised, named local variable.
+    ///
+    /// Returns the `LocalIdx` of the new local, and optionally the
+    /// index of a previous local shadowed by this one.
+    pub fn declare_local(
+        &mut self,
+        name: String,
+        span: codemap::Span,
+    ) -> (LocalIdx, Option<LocalIdx>) {
+        let idx = LocalIdx(self.locals.len());
+        self.locals.push(Local {
+            name: LocalName::Ident(name.clone()),
+            span,
+            depth: self.scope_depth,
+            initialised: false,
+            needs_finaliser: false,
+            must_thunk: false,
+            used: false,
+        });
+
+        let mut shadowed = None;
+        match self.by_name.entry(name) {
+            hash_map::Entry::Occupied(mut entry) => {
+                let existing = entry.get_mut();
+                shadowed = Some(existing.index());
+                existing.add_idx(idx);
+            }
+            hash_map::Entry::Vacant(entry) => {
+                entry.insert(ByName::Single(idx));
+            }
+        }
+
+        (idx, shadowed)
+    }
+
+    /// Mark local as initialised after compiling its expression.
+    pub fn mark_initialised(&mut self, idx: LocalIdx) {
+        self.locals[idx.0].initialised = true;
+    }
+
+    /// Mark local as needing a finaliser.
+    pub fn mark_needs_finaliser(&mut self, idx: LocalIdx) {
+        self.locals[idx.0].needs_finaliser = true;
+    }
+
+    /// Mark local as must be wrapped in a thunk.  This happens if
+    /// the local has a reference to itself in its upvalues.
+    pub fn mark_must_thunk(&mut self, idx: LocalIdx) {
+        self.locals[idx.0].must_thunk = true;
+    }
+
+    /// Compute the runtime stack index for a given local by
+    /// accounting for uninitialised variables at scopes below this
+    /// one.
+    pub fn stack_index(&self, idx: LocalIdx) -> StackIdx {
+        let uninitialised_count = self.locals[..(idx.0)]
+            .iter()
+            .filter(|l| !l.initialised && self[idx].depth > l.depth)
+            .count();
+
+        StackIdx(idx.0 - uninitialised_count)
+    }
+
+    /// Increase the current scope depth (e.g. within a new bindings
+    /// block, or `with`-scope).
+    pub fn begin_scope(&mut self) {
+        self.scope_depth += 1;
+    }
+
+    /// Decrease the scope depth and remove all locals still tracked
+    /// for the current scope.
+    ///
+    /// Returns the count of locals that were dropped while marked as
+    /// initialised (used by the compiler to determine whether to emit
+    /// scope cleanup operations), as well as the spans of the
+    /// definitions of unused locals (used by the compiler to emit
+    /// unused binding warnings).
+    pub fn end_scope(&mut self) -> (usize, Vec<codemap::Span>) {
+        debug_assert!(self.scope_depth != 0, "can not end top scope");
+
+        let mut pops = 0;
+        let mut unused_spans = vec![];
+
+        // TL;DR - iterate from the back while things belonging to the
+        // ended scope still exist.
+        while self.locals.last().unwrap().depth == self.scope_depth {
+            if let Some(local) = self.locals.pop() {
+                // pop the local from the stack if it was actually
+                // initialised
+                if local.initialised {
+                    pops += 1;
+                }
+
+                // analyse whether the local was accessed during its
+                // lifetime, and emit a warning otherwise (unless the
+                // user explicitly chose to ignore it by prefixing the
+                // identifier with `_`)
+                if !local.used && !local.is_ignored() {
+                    unused_spans.push(local.span);
+                }
+
+                // remove the by-name index if this was a named local
+                if let LocalName::Ident(name) = local.name {
+                    if let hash_map::Entry::Occupied(mut entry) = self.by_name.entry(name) {
+                        // If no removal occured through `remove_idx`
+                        // (i.e. there was no shadowing going on),
+                        // nuke the whole entry.
+                        if !entry.get_mut().remove_idx() {
+                            entry.remove();
+                        }
+                    }
+                }
+            }
+        }
+
+        self.scope_depth -= 1;
+
+        (pops, unused_spans)
+    }
+
+    /// Access the current scope depth.
+    pub fn scope_depth(&self) -> usize {
+        self.scope_depth
+    }
+}
diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs
new file mode 100644
index 0000000000..652252dadf
--- /dev/null
+++ b/tvix/eval/src/errors.rs
@@ -0,0 +1,1109 @@
+use std::error;
+use std::io;
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::str::Utf8Error;
+use std::string::FromUtf8Error;
+use std::sync::Arc;
+use std::{fmt::Debug, fmt::Display, num::ParseIntError};
+
+use codemap::{File, Span};
+use codemap_diagnostic::{ColorConfig, Diagnostic, Emitter, Level, SpanLabel, SpanStyle};
+use smol_str::SmolStr;
+use xml::writer::Error as XmlError;
+
+use crate::spans::ToSpan;
+use crate::value::{CoercionKind, NixString};
+use crate::{SourceCode, Value};
+
+/// "CatchableErrorKind" errors -- those which can be detected by
+/// `builtins.tryEval`.
+///
+/// Note: this type is deliberately *not* incorporated as a variant
+/// of ErrorKind, because then Result<Value,ErrorKind> would have
+/// redundant representations for catchable errors, which would make
+/// it too easy to handle errors incorrectly:
+///
+///   - Ok(Value::Catchable(cek))
+///   - Err(ErrorKind::ThisVariantDoesNotExist(cek))
+///
+/// Because CatchableErrorKind is not a variant of ErrorKind, you
+/// will often see functions which return a type like:
+///
+///   Result<Result<T,CatchableErrorKind>,ErrorKind>
+///
+/// ... where T is any type other than Value.  This is unfortunate,
+/// because Rust's magic `?`-syntax does not work on nested Result
+/// values like this.
+// TODO(amjoseph): investigate result<T,Either<CatchableErrorKind,ErrorKind>>
+#[derive(Clone, Debug)]
+pub enum CatchableErrorKind {
+    Throw(Box<str>),
+    AssertionFailed,
+    UnimplementedFeature(Box<str>),
+    /// Resolving a user-supplied angle brackets path literal failed in some way.
+    NixPathResolution(Box<str>),
+}
+
+impl Display for CatchableErrorKind {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            CatchableErrorKind::Throw(s) => write!(f, "error thrown: {}", s),
+            CatchableErrorKind::AssertionFailed => write!(f, "assertion failed"),
+            CatchableErrorKind::UnimplementedFeature(s) => {
+                write!(f, "feature {} is not implemented yet", s)
+            }
+            CatchableErrorKind::NixPathResolution(s) => {
+                write!(f, "Nix path entry could not be resolved: {}", s)
+            }
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum ErrorKind {
+    /// These are user-generated errors through builtins.
+    Abort(String),
+
+    DivisionByZero,
+
+    DuplicateAttrsKey {
+        key: String,
+    },
+
+    /// Attempted to specify an invalid key type (e.g. integer) in a
+    /// dynamic attribute name.
+    InvalidAttributeName(Value),
+
+    AttributeNotFound {
+        name: String,
+    },
+
+    /// Attempted to index into a list beyond its boundaries.
+    IndexOutOfBounds {
+        index: i64,
+    },
+
+    /// Attempted to call `builtins.tail` on an empty list.
+    TailEmptyList,
+
+    TypeError {
+        expected: &'static str,
+        actual: &'static str,
+    },
+
+    Incomparable {
+        lhs: &'static str,
+        rhs: &'static str,
+    },
+
+    /// Resolving a user-supplied relative or home-relative path literal failed in some way.
+    RelativePathResolution(String),
+
+    /// Dynamic keys are not allowed in some scopes.
+    DynamicKeyInScope(&'static str),
+
+    /// Unknown variable in statically known scope.
+    UnknownStaticVariable,
+
+    /// Unknown variable in dynamic scope (with, rec, ...).
+    UnknownDynamicVariable(String),
+
+    /// User is defining the same variable twice at the same depth.
+    VariableAlreadyDefined(Span),
+
+    /// Attempt to call something that is not callable.
+    NotCallable(&'static str),
+
+    /// Infinite recursion encountered while forcing thunks.
+    InfiniteRecursion {
+        first_force: Span,
+        suspended_at: Option<Span>,
+        content_span: Option<Span>,
+    },
+
+    ParseErrors(Vec<rnix::parser::ParseError>),
+
+    /// An error occured while executing some native code (e.g. a
+    /// builtin), and needs to be chained up.
+    NativeError {
+        gen_type: &'static str,
+        err: Box<Error>,
+    },
+
+    /// An error occured while executing Tvix bytecode, but needs to
+    /// be chained up.
+    BytecodeError(Box<Error>),
+
+    /// Given type can't be coerced to a string in the respective context
+    NotCoercibleToString {
+        from: &'static str,
+        kind: CoercionKind,
+    },
+
+    /// The given string doesn't represent an absolute path
+    NotAnAbsolutePath(PathBuf),
+
+    /// An error occurred when parsing an integer
+    ParseIntError(ParseIntError),
+
+    // Errors specific to nested attribute sets and merges thereof.
+    /// Nested attributes can not be merged with an inherited value.
+    UnmergeableInherit {
+        name: SmolStr,
+    },
+
+    /// Nested attributes can not be merged with values that are not
+    /// literal attribute sets.
+    UnmergeableValue,
+
+    /// Parse errors occured while importing a file.
+    ImportParseError {
+        path: PathBuf,
+        file: Arc<File>,
+        errors: Vec<rnix::parser::ParseError>,
+    },
+
+    /// Compilation errors occured while importing a file.
+    ImportCompilerError {
+        path: PathBuf,
+        errors: Vec<Error>,
+    },
+
+    /// I/O errors
+    IO {
+        path: Option<PathBuf>,
+        error: Rc<io::Error>,
+    },
+
+    /// Errors parsing JSON, or serializing as JSON.
+    JsonError(String),
+
+    /// Nix value that can not be serialised to JSON.
+    NotSerialisableToJson(&'static str),
+
+    /// Errors converting TOML to a value
+    FromTomlError(String),
+
+    /// An unexpected argument was supplied to a function that takes formal parameters
+    UnexpectedArgument {
+        arg: NixString,
+        formals_span: Span,
+    },
+
+    /// Invalid UTF-8 was encoutered somewhere
+    Utf8,
+
+    /// Errors while serialising to XML.
+    Xml(Rc<XmlError>),
+
+    /// Variant for errors that bubble up to eval from other Tvix
+    /// components.
+    TvixError(Rc<dyn error::Error>),
+
+    /// Variant for code paths that are known bugs in Tvix (usually
+    /// issues with the compiler/VM interaction).
+    TvixBug {
+        msg: &'static str,
+        metadata: Option<Rc<dyn Debug>>,
+    },
+
+    /// Tvix internal warning for features triggered by users that are
+    /// not actually implemented yet, and without which eval can not
+    /// proceed.
+    NotImplemented(&'static str),
+
+    /// Internal variant which should disappear during error construction.
+    WithContext {
+        context: String,
+        underlying: Box<ErrorKind>,
+    },
+
+    /// Unexpected context string
+    UnexpectedContext,
+
+    /// Top-level evaluation result was a catchable Nix error, and
+    /// should fail the evaluation.
+    ///
+    /// This variant **must** only be used at the top-level of
+    /// tvix-eval when returning a result to the user, never inside of
+    /// eval code.
+    CatchableError(CatchableErrorKind),
+
+    /// Invalid hash type specified, must be one of "md5", "sha1", "sha256"
+    /// or "sha512"
+    UnknownHashType(String),
+}
+
+impl error::Error for Error {
+    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+        match &self.kind {
+            ErrorKind::NativeError { err, .. } | ErrorKind::BytecodeError(err) => err.source(),
+            ErrorKind::ParseErrors(err) => err.first().map(|e| e as &dyn error::Error),
+            ErrorKind::ParseIntError(err) => Some(err),
+            ErrorKind::ImportParseError { errors, .. } => {
+                errors.first().map(|e| e as &dyn error::Error)
+            }
+            ErrorKind::ImportCompilerError { errors, .. } => {
+                errors.first().map(|e| e as &dyn error::Error)
+            }
+            ErrorKind::IO { error, .. } => Some(error.as_ref()),
+            ErrorKind::Xml(error) => Some(error.as_ref()),
+            ErrorKind::TvixError(error) => Some(error.as_ref()),
+            _ => None,
+        }
+    }
+}
+
+impl From<ParseIntError> for ErrorKind {
+    fn from(e: ParseIntError) -> Self {
+        Self::ParseIntError(e)
+    }
+}
+
+impl From<Utf8Error> for ErrorKind {
+    fn from(_: Utf8Error) -> Self {
+        Self::NotImplemented("FromUtf8Error not handled: https://b.tvl.fyi/issues/189")
+    }
+}
+
+impl From<FromUtf8Error> for ErrorKind {
+    fn from(_: FromUtf8Error) -> Self {
+        Self::NotImplemented("FromUtf8Error not handled: https://b.tvl.fyi/issues/189")
+    }
+}
+
+impl From<bstr::Utf8Error> for ErrorKind {
+    fn from(_: bstr::Utf8Error) -> Self {
+        Self::Utf8
+    }
+}
+
+impl From<bstr::FromUtf8Error> for ErrorKind {
+    fn from(_value: bstr::FromUtf8Error) -> Self {
+        Self::Utf8
+    }
+}
+
+impl From<XmlError> for ErrorKind {
+    fn from(err: XmlError) -> Self {
+        Self::Xml(Rc::new(err))
+    }
+}
+
+impl From<io::Error> for ErrorKind {
+    fn from(e: io::Error) -> Self {
+        ErrorKind::IO {
+            path: None,
+            error: Rc::new(e),
+        }
+    }
+}
+
+impl From<serde_json::Error> for ErrorKind {
+    fn from(err: serde_json::Error) -> Self {
+        // Can't just put the `serde_json::Error` in the ErrorKind since it doesn't impl `Clone`
+        Self::JsonError(err.to_string())
+    }
+}
+
+impl From<toml::de::Error> for ErrorKind {
+    fn from(err: toml::de::Error) -> Self {
+        Self::FromTomlError(format!("error in TOML serialization: {err}"))
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct Error {
+    pub kind: ErrorKind,
+    pub span: Span,
+    pub contexts: Vec<String>,
+    pub source: SourceCode,
+}
+
+impl Error {
+    pub fn new(mut kind: ErrorKind, span: Span, source: SourceCode) -> Self {
+        let mut contexts = vec![];
+        while let ErrorKind::WithContext {
+            context,
+            underlying,
+        } = kind
+        {
+            kind = *underlying;
+            contexts.push(context);
+        }
+
+        Error {
+            kind,
+            span,
+            contexts,
+            source,
+        }
+    }
+}
+
+impl Display for ErrorKind {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match &self {
+            ErrorKind::Abort(msg) => write!(f, "evaluation aborted: {}", msg),
+
+            ErrorKind::DivisionByZero => write!(f, "division by zero"),
+
+            ErrorKind::DuplicateAttrsKey { key } => {
+                write!(f, "attribute key '{}' already defined", key)
+            }
+
+            ErrorKind::InvalidAttributeName(val) => write!(
+                f,
+                "found attribute name '{}' of type '{}', but attribute names must be strings",
+                val,
+                val.type_of()
+            ),
+
+            ErrorKind::AttributeNotFound { name } => write!(
+                f,
+                "attribute with name '{}' could not be found in the set",
+                name
+            ),
+
+            ErrorKind::IndexOutOfBounds { index } => {
+                write!(f, "list index '{}' is out of bounds", index)
+            }
+
+            ErrorKind::TailEmptyList => write!(f, "'tail' called on an empty list"),
+
+            ErrorKind::TypeError { expected, actual } => write!(
+                f,
+                "expected value of type '{}', but found a '{}'",
+                expected, actual
+            ),
+
+            ErrorKind::Incomparable { lhs, rhs } => {
+                write!(f, "can not compare a {} with a {}", lhs, rhs)
+            }
+
+            ErrorKind::RelativePathResolution(err) => {
+                write!(f, "could not resolve path: {}", err)
+            }
+
+            ErrorKind::DynamicKeyInScope(scope) => {
+                write!(f, "dynamically evaluated keys are not allowed in {}", scope)
+            }
+
+            ErrorKind::UnknownStaticVariable => write!(f, "variable not found"),
+
+            ErrorKind::UnknownDynamicVariable(name) => write!(
+                f,
+                r#"variable '{}' could not be found
+
+Note that this occured within a `with`-expression. The problem may be related
+to a missing value in the attribute set(s) included via `with`."#,
+                name
+            ),
+
+            ErrorKind::VariableAlreadyDefined(_) => write!(f, "variable has already been defined"),
+
+            ErrorKind::NotCallable(other_type) => {
+                write!(
+                    f,
+                    "only functions and builtins can be called, but this is a '{}'",
+                    other_type
+                )
+            }
+
+            ErrorKind::InfiniteRecursion { .. } => write!(f, "infinite recursion encountered"),
+
+            // Errors themselves ignored here & handled in Self::spans instead
+            ErrorKind::ParseErrors(_) => write!(f, "failed to parse Nix code:"),
+
+            ErrorKind::NativeError { gen_type, .. } => {
+                write!(f, "while evaluating this as native code ({})", gen_type)
+            }
+
+            ErrorKind::BytecodeError(_) => write!(f, "while evaluating this Nix code"),
+
+            ErrorKind::NotCoercibleToString { kind, from } => {
+                let kindly = if kind.strong { "strongly" } else { "weakly" };
+
+                let hint = if *from == "set" {
+                    ", missing a `__toString` or `outPath` attribute"
+                } else {
+                    ""
+                };
+
+                write!(f, "cannot ({kindly}) coerce {from} to a string{hint}")
+            }
+
+            ErrorKind::NotAnAbsolutePath(given) => {
+                write!(
+                    f,
+                    "string '{}' does not represent an absolute path",
+                    given.to_string_lossy()
+                )
+            }
+
+            ErrorKind::ParseIntError(err) => {
+                write!(f, "invalid integer: {}", err)
+            }
+
+            ErrorKind::UnmergeableInherit { name } => {
+                write!(
+                    f,
+                    "cannot merge a nested attribute set into the inherited entry '{}'",
+                    name
+                )
+            }
+
+            ErrorKind::UnmergeableValue => {
+                write!(
+                    f,
+                    "nested attribute sets or keys can only be merged with literal attribute sets"
+                )
+            }
+
+            // Errors themselves ignored here & handled in Self::spans instead
+            ErrorKind::ImportParseError { path, .. } => {
+                write!(
+                    f,
+                    "parse errors occured while importing '{}'",
+                    path.to_string_lossy()
+                )
+            }
+
+            ErrorKind::ImportCompilerError { path, .. } => {
+                writeln!(
+                    f,
+                    "compiler errors occured while importing '{}'",
+                    path.to_string_lossy()
+                )
+            }
+
+            ErrorKind::IO { path, error } => {
+                write!(f, "I/O error: ")?;
+                if let Some(path) = path {
+                    write!(f, "{}: ", path.display())?;
+                }
+                write!(f, "{error}")
+            }
+
+            ErrorKind::JsonError(msg) => {
+                write!(f, "Error converting JSON to a Nix value or back: {msg}")
+            }
+
+            ErrorKind::NotSerialisableToJson(_type) => {
+                write!(f, "a {} cannot be converted to JSON", _type)
+            }
+
+            ErrorKind::FromTomlError(msg) => {
+                write!(f, "Error converting TOML to a Nix value: {msg}")
+            }
+
+            ErrorKind::UnexpectedArgument { arg, .. } => {
+                write!(f, "Unexpected argument `{arg}` supplied to function",)
+            }
+
+            ErrorKind::Utf8 => {
+                write!(f, "Invalid UTF-8 in string")
+            }
+
+            ErrorKind::Xml(error) => write!(f, "failed to serialise to XML: {error}"),
+
+            ErrorKind::TvixError(inner_error) => {
+                write!(f, "{inner_error}")
+            }
+
+            ErrorKind::TvixBug { msg, metadata } => {
+                write!(f, "Tvix bug: {}", msg)?;
+
+                if let Some(metadata) = metadata {
+                    write!(f, "; metadata: {:?}", metadata)?;
+                }
+
+                Ok(())
+            }
+
+            ErrorKind::NotImplemented(feature) => {
+                write!(f, "feature not yet implemented in Tvix: {}", feature)
+            }
+
+            ErrorKind::WithContext { .. } => {
+                panic!("internal ErrorKind::WithContext variant leaked")
+            }
+
+            ErrorKind::UnexpectedContext => {
+                write!(f, "unexpected context string")
+            }
+
+            ErrorKind::CatchableError(inner) => {
+                write!(f, "{}", inner)
+            }
+
+            ErrorKind::UnknownHashType(hash_type) => {
+                write!(f, "unknown hash type '{}'", hash_type)
+            }
+        }
+    }
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.kind)
+    }
+}
+
+pub type EvalResult<T> = Result<T, Error>;
+
+/// Human-readable names for rnix syntaxes.
+fn name_for_syntax(syntax: &rnix::SyntaxKind) -> &'static str {
+    match syntax {
+        rnix::SyntaxKind::TOKEN_COMMENT => "a comment",
+        rnix::SyntaxKind::TOKEN_WHITESPACE => "whitespace",
+        rnix::SyntaxKind::TOKEN_ASSERT => "`assert`-keyword",
+        rnix::SyntaxKind::TOKEN_ELSE => "`else`-keyword",
+        rnix::SyntaxKind::TOKEN_IN => "`in`-keyword",
+        rnix::SyntaxKind::TOKEN_IF => "`if`-keyword",
+        rnix::SyntaxKind::TOKEN_INHERIT => "`inherit`-keyword",
+        rnix::SyntaxKind::TOKEN_LET => "`let`-keyword",
+        rnix::SyntaxKind::TOKEN_OR => "`or`-keyword",
+        rnix::SyntaxKind::TOKEN_REC => "`rec`-keyword",
+        rnix::SyntaxKind::TOKEN_THEN => "`then`-keyword",
+        rnix::SyntaxKind::TOKEN_WITH => "`with`-keyword",
+        rnix::SyntaxKind::TOKEN_L_BRACE => "{",
+        rnix::SyntaxKind::TOKEN_R_BRACE => "}",
+        rnix::SyntaxKind::TOKEN_L_BRACK => "[",
+        rnix::SyntaxKind::TOKEN_R_BRACK => "]",
+        rnix::SyntaxKind::TOKEN_ASSIGN => "=",
+        rnix::SyntaxKind::TOKEN_AT => "@",
+        rnix::SyntaxKind::TOKEN_COLON => ":",
+        rnix::SyntaxKind::TOKEN_COMMA => "`,`",
+        rnix::SyntaxKind::TOKEN_DOT => ".",
+        rnix::SyntaxKind::TOKEN_ELLIPSIS => "...",
+        rnix::SyntaxKind::TOKEN_QUESTION => "?",
+        rnix::SyntaxKind::TOKEN_SEMICOLON => ";",
+        rnix::SyntaxKind::TOKEN_L_PAREN => "(",
+        rnix::SyntaxKind::TOKEN_R_PAREN => ")",
+        rnix::SyntaxKind::TOKEN_CONCAT => "++",
+        rnix::SyntaxKind::TOKEN_INVERT => "!",
+        rnix::SyntaxKind::TOKEN_UPDATE => "//",
+        rnix::SyntaxKind::TOKEN_ADD => "+",
+        rnix::SyntaxKind::TOKEN_SUB => "-",
+        rnix::SyntaxKind::TOKEN_MUL => "*",
+        rnix::SyntaxKind::TOKEN_DIV => "/",
+        rnix::SyntaxKind::TOKEN_AND_AND => "&&",
+        rnix::SyntaxKind::TOKEN_EQUAL => "==",
+        rnix::SyntaxKind::TOKEN_IMPLICATION => "->",
+        rnix::SyntaxKind::TOKEN_LESS => "<",
+        rnix::SyntaxKind::TOKEN_LESS_OR_EQ => "<=",
+        rnix::SyntaxKind::TOKEN_MORE => ">",
+        rnix::SyntaxKind::TOKEN_MORE_OR_EQ => ">=",
+        rnix::SyntaxKind::TOKEN_NOT_EQUAL => "!=",
+        rnix::SyntaxKind::TOKEN_OR_OR => "||",
+        rnix::SyntaxKind::TOKEN_FLOAT => "a float",
+        rnix::SyntaxKind::TOKEN_IDENT => "an identifier",
+        rnix::SyntaxKind::TOKEN_INTEGER => "an integer",
+        rnix::SyntaxKind::TOKEN_INTERPOL_END => "}",
+        rnix::SyntaxKind::TOKEN_INTERPOL_START => "${",
+        rnix::SyntaxKind::TOKEN_PATH => "a path",
+        rnix::SyntaxKind::TOKEN_URI => "a literal URI",
+        rnix::SyntaxKind::TOKEN_STRING_CONTENT => "content of a string",
+        rnix::SyntaxKind::TOKEN_STRING_END => "\"",
+        rnix::SyntaxKind::TOKEN_STRING_START => "\"",
+
+        rnix::SyntaxKind::NODE_APPLY => "a function application",
+        rnix::SyntaxKind::NODE_ASSERT => "an assertion",
+        rnix::SyntaxKind::NODE_ATTRPATH => "an attribute path",
+        rnix::SyntaxKind::NODE_DYNAMIC => "a dynamic identifier",
+
+        rnix::SyntaxKind::NODE_IDENT => "an identifier",
+        rnix::SyntaxKind::NODE_IF_ELSE => "an `if`-expression",
+        rnix::SyntaxKind::NODE_SELECT => "a `select`-expression",
+        rnix::SyntaxKind::NODE_INHERIT => "inherited values",
+        rnix::SyntaxKind::NODE_INHERIT_FROM => "inherited values",
+        rnix::SyntaxKind::NODE_STRING => "a string",
+        rnix::SyntaxKind::NODE_INTERPOL => "an interpolation",
+        rnix::SyntaxKind::NODE_LAMBDA => "a function",
+        rnix::SyntaxKind::NODE_IDENT_PARAM => "a function parameter",
+        rnix::SyntaxKind::NODE_LEGACY_LET => "a legacy `let`-expression",
+        rnix::SyntaxKind::NODE_LET_IN => "a `let`-expression",
+        rnix::SyntaxKind::NODE_LIST => "a list",
+        rnix::SyntaxKind::NODE_BIN_OP => "a binary operator",
+        rnix::SyntaxKind::NODE_PAREN => "a parenthesised expression",
+        rnix::SyntaxKind::NODE_PATTERN => "a function argument pattern",
+        rnix::SyntaxKind::NODE_PAT_BIND => "an argument pattern binding",
+        rnix::SyntaxKind::NODE_PAT_ENTRY => "an argument pattern entry",
+        rnix::SyntaxKind::NODE_ROOT => "a Nix expression",
+        rnix::SyntaxKind::NODE_ATTR_SET => "an attribute set",
+        rnix::SyntaxKind::NODE_ATTRPATH_VALUE => "an attribute set entry",
+        rnix::SyntaxKind::NODE_UNARY_OP => "a unary operator",
+        rnix::SyntaxKind::NODE_LITERAL => "a literal value",
+        rnix::SyntaxKind::NODE_WITH => "a `with`-expression",
+        rnix::SyntaxKind::NODE_PATH => "a path",
+        rnix::SyntaxKind::NODE_HAS_ATTR => "`?`-operator",
+
+        // TODO(tazjin): unsure what these variants are, lets crash!
+        rnix::SyntaxKind::NODE_ERROR => todo!("NODE_ERROR found, tell tazjin!"),
+        rnix::SyntaxKind::TOKEN_ERROR => todo!("TOKEN_ERROR found, tell tazjin!"),
+        _ => todo!(),
+    }
+}
+
+/// Construct the string representation for a list of expected parser tokens.
+fn expected_syntax(one_of: &[rnix::SyntaxKind]) -> String {
+    match one_of.len() {
+        0 => "nothing".into(),
+        1 => format!("'{}'", name_for_syntax(&one_of[0])),
+        _ => {
+            let mut out: String = "one of: ".into();
+            let end = one_of.len() - 1;
+
+            for (idx, item) in one_of.iter().enumerate() {
+                if idx != 0 {
+                    out.push_str(", ");
+                } else if idx == end {
+                    out.push_str(", or ");
+                };
+
+                out.push_str(name_for_syntax(item));
+            }
+
+            out
+        }
+    }
+}
+
+/// Process a list of parse errors into a set of span labels, annotating parse
+/// errors.
+fn spans_for_parse_errors(file: &File, errors: &[rnix::parser::ParseError]) -> Vec<SpanLabel> {
+    // rnix has a tendency to emit some identical errors more than once, but
+    // they do not enhance the user experience necessarily, so we filter them
+    // out
+    let mut had_eof = false;
+
+    errors
+        .iter()
+        .enumerate()
+        .filter_map(|(idx, err)| {
+            let (span, label): (Span, String) = match err {
+                rnix::parser::ParseError::Unexpected(range) => (
+                    range.span_for(file),
+                    "found an unexpected syntax element here".into(),
+                ),
+
+                rnix::parser::ParseError::UnexpectedExtra(range) => (
+                    range.span_for(file),
+                    "found unexpected extra elements at the root of the expression".into(),
+                ),
+
+                rnix::parser::ParseError::UnexpectedWanted(found, range, wanted) => {
+                    let span = range.span_for(file);
+                    (
+                        span,
+                        format!(
+                            "found '{}', but expected {}",
+                            name_for_syntax(found),
+                            expected_syntax(wanted),
+                        ),
+                    )
+                }
+
+                rnix::parser::ParseError::UnexpectedEOF => {
+                    if had_eof {
+                        return None;
+                    }
+
+                    had_eof = true;
+
+                    (
+                        file.span,
+                        "code ended unexpectedly while the parser still expected more".into(),
+                    )
+                }
+
+                rnix::parser::ParseError::UnexpectedEOFWanted(wanted) => {
+                    had_eof = true;
+
+                    (
+                        file.span,
+                        format!(
+                            "code ended unexpectedly, but wanted {}",
+                            expected_syntax(wanted)
+                        ),
+                    )
+                }
+
+                rnix::parser::ParseError::DuplicatedArgs(range, name) => (
+                    range.span_for(file),
+                    format!(
+                        "the function argument pattern '{}' was bound more than once",
+                        name
+                    ),
+                ),
+
+                rnix::parser::ParseError::RecursionLimitExceeded => (
+                    file.span,
+                    "this code exceeds the parser's recursion limit, please report a Tvix bug"
+                        .to_string(),
+                ),
+
+                // TODO: can rnix even still throw this? it's semantic!
+                rnix::parser::ParseError::UnexpectedDoubleBind(range) => (
+                    range.span_for(file),
+                    "this pattern was bound more than once".into(),
+                ),
+
+                // The error enum is marked as `#[non_exhaustive]` in rnix,
+                // which disables the compiler error for missing a variant. This
+                // feature makes it possible for users to miss critical updates
+                // of enum variants for a more exciting runtime experience.
+                new => todo!("new parse error variant: {}", new),
+            };
+
+            Some(SpanLabel {
+                span,
+                label: Some(label),
+                style: if idx == 0 {
+                    SpanStyle::Primary
+                } else {
+                    SpanStyle::Secondary
+                },
+            })
+        })
+        .collect()
+}
+
+impl Error {
+    pub fn fancy_format_str(&self) -> String {
+        let mut out = vec![];
+        Emitter::vec(&mut out, Some(&*self.source.codemap())).emit(&self.diagnostics());
+        String::from_utf8_lossy(&out).to_string()
+    }
+
+    /// Render a fancy, human-readable output of this error and print
+    /// it to stderr.
+    pub fn fancy_format_stderr(&self) {
+        Emitter::stderr(ColorConfig::Auto, Some(&*self.source.codemap())).emit(&self.diagnostics());
+    }
+
+    /// Create the optional span label displayed as an annotation on
+    /// the underlined span of the error.
+    fn span_label(&self) -> Option<String> {
+        let label = match &self.kind {
+            ErrorKind::DuplicateAttrsKey { .. } => "in this attribute set",
+            ErrorKind::InvalidAttributeName(_) => "in this attribute set",
+            ErrorKind::RelativePathResolution(_) => "in this path literal",
+            ErrorKind::UnexpectedArgument { .. } => "in this function call",
+            ErrorKind::UnexpectedContext => "in this string",
+
+            // The spans for some errors don't have any more descriptive stuff
+            // in them, or we don't utilise it yet.
+            ErrorKind::Abort(_)
+            | ErrorKind::AttributeNotFound { .. }
+            | ErrorKind::IndexOutOfBounds { .. }
+            | ErrorKind::TailEmptyList
+            | ErrorKind::TypeError { .. }
+            | ErrorKind::Incomparable { .. }
+            | ErrorKind::DivisionByZero
+            | ErrorKind::DynamicKeyInScope(_)
+            | ErrorKind::UnknownStaticVariable
+            | ErrorKind::UnknownDynamicVariable(_)
+            | ErrorKind::VariableAlreadyDefined(_)
+            | ErrorKind::NotCallable(_)
+            | ErrorKind::InfiniteRecursion { .. }
+            | ErrorKind::ParseErrors(_)
+            | ErrorKind::NativeError { .. }
+            | ErrorKind::BytecodeError(_)
+            | ErrorKind::NotCoercibleToString { .. }
+            | ErrorKind::NotAnAbsolutePath(_)
+            | ErrorKind::ParseIntError(_)
+            | ErrorKind::UnmergeableInherit { .. }
+            | ErrorKind::UnmergeableValue
+            | ErrorKind::ImportParseError { .. }
+            | ErrorKind::ImportCompilerError { .. }
+            | ErrorKind::IO { .. }
+            | ErrorKind::JsonError(_)
+            | ErrorKind::NotSerialisableToJson(_)
+            | ErrorKind::FromTomlError(_)
+            | ErrorKind::Xml(_)
+            | ErrorKind::Utf8
+            | ErrorKind::TvixError(_)
+            | ErrorKind::TvixBug { .. }
+            | ErrorKind::NotImplemented(_)
+            | ErrorKind::WithContext { .. }
+            | ErrorKind::UnknownHashType(_)
+            | ErrorKind::CatchableError(_) => return None,
+        };
+
+        Some(label.into())
+    }
+
+    /// Return the unique error code for this variant which can be
+    /// used to refer users to documentation.
+    fn code(&self) -> &'static str {
+        match self.kind {
+            ErrorKind::CatchableError(CatchableErrorKind::Throw(_)) => "E001",
+            ErrorKind::Abort(_) => "E002",
+            ErrorKind::CatchableError(CatchableErrorKind::AssertionFailed) => "E003",
+            ErrorKind::InvalidAttributeName { .. } => "E004",
+            ErrorKind::AttributeNotFound { .. } => "E005",
+            ErrorKind::TypeError { .. } => "E006",
+            ErrorKind::Incomparable { .. } => "E007",
+            ErrorKind::CatchableError(CatchableErrorKind::NixPathResolution(_)) => "E008",
+            ErrorKind::DynamicKeyInScope(_) => "E009",
+            ErrorKind::UnknownStaticVariable => "E010",
+            ErrorKind::UnknownDynamicVariable(_) => "E011",
+            ErrorKind::VariableAlreadyDefined(_) => "E012",
+            ErrorKind::NotCallable(_) => "E013",
+            ErrorKind::InfiniteRecursion { .. } => "E014",
+            ErrorKind::ParseErrors(_) => "E015",
+            ErrorKind::DuplicateAttrsKey { .. } => "E016",
+            ErrorKind::NotCoercibleToString { .. } => "E018",
+            ErrorKind::IndexOutOfBounds { .. } => "E019",
+            ErrorKind::NotAnAbsolutePath(_) => "E020",
+            ErrorKind::ParseIntError(_) => "E021",
+            ErrorKind::TailEmptyList { .. } => "E023",
+            ErrorKind::UnmergeableInherit { .. } => "E024",
+            ErrorKind::UnmergeableValue => "E025",
+            ErrorKind::ImportParseError { .. } => "E027",
+            ErrorKind::ImportCompilerError { .. } => "E028",
+            ErrorKind::IO { .. } => "E029",
+            ErrorKind::JsonError { .. } => "E030",
+            ErrorKind::UnexpectedArgument { .. } => "E031",
+            ErrorKind::RelativePathResolution(_) => "E032",
+            ErrorKind::DivisionByZero => "E033",
+            ErrorKind::Xml(_) => "E034",
+            ErrorKind::FromTomlError(_) => "E035",
+            ErrorKind::NotSerialisableToJson(_) => "E036",
+            ErrorKind::UnexpectedContext => "E037",
+            ErrorKind::Utf8 => "E038",
+            ErrorKind::UnknownHashType(_) => "E039",
+
+            // Special error code for errors from other Tvix
+            // components. We may want to introduce a code namespacing
+            // system to have these errors pass codes through.
+            ErrorKind::TvixError(_) => "E997",
+
+            // Special error code that is not part of the normal
+            // ordering.
+            ErrorKind::TvixBug { .. } => "E998",
+
+            // Placeholder error while Tvix is under construction.
+            ErrorKind::CatchableError(CatchableErrorKind::UnimplementedFeature(_))
+            | ErrorKind::NotImplemented(_) => "E999",
+
+            // Chained errors should yield the code of the innermost
+            // error.
+            ErrorKind::NativeError { ref err, .. } | ErrorKind::BytecodeError(ref err) => {
+                err.code()
+            }
+
+            ErrorKind::WithContext { .. } => {
+                panic!("internal ErrorKind::WithContext variant leaked")
+            }
+        }
+    }
+
+    fn spans(&self) -> Vec<SpanLabel> {
+        let mut spans = match &self.kind {
+            ErrorKind::ImportParseError { errors, file, .. } => {
+                spans_for_parse_errors(file, errors)
+            }
+
+            ErrorKind::ParseErrors(errors) => {
+                let file = self.source.get_file(self.span);
+                spans_for_parse_errors(&file, errors)
+            }
+
+            ErrorKind::UnexpectedArgument { formals_span, .. } => {
+                vec![
+                    SpanLabel {
+                        label: self.span_label(),
+                        span: self.span,
+                        style: SpanStyle::Primary,
+                    },
+                    SpanLabel {
+                        label: Some("the accepted arguments".into()),
+                        span: *formals_span,
+                        style: SpanStyle::Secondary,
+                    },
+                ]
+            }
+
+            ErrorKind::InfiniteRecursion {
+                first_force,
+                suspended_at,
+                content_span,
+            } => {
+                let mut spans = vec![];
+
+                if let Some(content_span) = content_span {
+                    spans.push(SpanLabel {
+                        label: Some("this lazily-evaluated code".into()),
+                        span: *content_span,
+                        style: SpanStyle::Secondary,
+                    })
+                }
+
+                if let Some(suspended_at) = suspended_at {
+                    spans.push(SpanLabel {
+                        label: Some("which was instantiated here".into()),
+                        span: *suspended_at,
+                        style: SpanStyle::Secondary,
+                    })
+                }
+
+                spans.push(SpanLabel {
+                    label: Some("was first requested to be evaluated here".into()),
+                    span: *first_force,
+                    style: SpanStyle::Secondary,
+                });
+
+                spans.push(SpanLabel {
+                    label: Some("but then requested again here during its own evaluation".into()),
+                    span: self.span,
+                    style: SpanStyle::Primary,
+                });
+
+                spans
+            }
+
+            // All other errors pretty much have the same shape.
+            _ => {
+                vec![SpanLabel {
+                    label: self.span_label(),
+                    span: self.span,
+                    style: SpanStyle::Primary,
+                }]
+            }
+        };
+
+        for ctx in &self.contexts {
+            spans.push(SpanLabel {
+                label: Some(format!("while {}", ctx)),
+                span: self.span,
+                style: SpanStyle::Secondary,
+            });
+        }
+
+        spans
+    }
+
+    /// Create the primary diagnostic for a given error.
+    fn diagnostic(&self) -> Diagnostic {
+        Diagnostic {
+            level: Level::Error,
+            message: self.to_string(),
+            spans: self.spans(),
+            code: Some(self.code().into()),
+        }
+    }
+
+    /// Return the primary diagnostic and all further associated diagnostics (if
+    /// any) of an error.
+    fn diagnostics(&self) -> Vec<Diagnostic> {
+        match &self.kind {
+            ErrorKind::ImportCompilerError { errors, .. } => {
+                let mut out = vec![self.diagnostic()];
+                out.extend(errors.iter().map(|e| e.diagnostic()));
+                out
+            }
+
+            // When encountering either of these error kinds, we are dealing
+            // with the top of an error chain.
+            //
+            // An error chain creates a list of diagnostics which provide trace
+            // information.
+            //
+            // We don't know how deep this chain is, so we avoid recursing in
+            // this function while unrolling the chain.
+            ErrorKind::NativeError { err: next, .. } | ErrorKind::BytecodeError(next) => {
+                // Accumulated diagnostics to return.
+                let mut diagnostics: Vec<Diagnostic> = vec![];
+
+                // The next (inner) error to add to the diagnostics, after this
+                // one.
+                let mut next = *next.clone();
+
+                // Diagnostic message for *this* error.
+                let mut this_message = self.to_string();
+
+                // Primary span for *this* error.
+                let mut this_span = self.span;
+
+                // Diagnostic spans for *this* error.
+                let mut this_spans = self.spans();
+
+                loop {
+                    if is_new_span(
+                        this_span,
+                        diagnostics.last().and_then(|last| last.spans.last()),
+                    ) {
+                        diagnostics.push(Diagnostic {
+                            level: Level::Note,
+                            message: this_message,
+                            spans: this_spans,
+                            code: None, // only the top-level error has one
+                        });
+                    }
+
+                    this_message = next.to_string();
+                    this_span = next.span;
+                    this_spans = next.spans();
+
+                    match next.kind {
+                        ErrorKind::NativeError { err: inner, .. }
+                        | ErrorKind::BytecodeError(inner) => {
+                            next = *inner;
+                            continue;
+                        }
+                        _ => {
+                            diagnostics.extend(next.diagnostics());
+                            break;
+                        }
+                    }
+                }
+
+                diagnostics
+            }
+
+            _ => vec![self.diagnostic()],
+        }
+    }
+}
+
+// Check if this error is in a different span from its immediate ancestor.
+fn is_new_span(this_span: Span, parent: Option<&SpanLabel>) -> bool {
+    match parent {
+        None => true,
+        Some(parent) => parent.span != this_span,
+    }
+}
+
+// Convenience methods to add context on other types.
+pub trait AddContext {
+    /// Add context to the error-carrying type.
+    fn context<S: Into<String>>(self, ctx: S) -> Self;
+}
+
+impl AddContext for ErrorKind {
+    fn context<S: Into<String>>(self, ctx: S) -> Self {
+        ErrorKind::WithContext {
+            context: ctx.into(),
+            underlying: Box::new(self),
+        }
+    }
+}
+
+impl<T> AddContext for Result<T, ErrorKind> {
+    fn context<S: Into<String>>(self, ctx: S) -> Self {
+        self.map_err(|kind| kind.context(ctx))
+    }
+}
+
+impl<T> AddContext for Result<T, Error> {
+    fn context<S: Into<String>>(self, ctx: S) -> Self {
+        self.map_err(|err| Error {
+            kind: err.kind.context(ctx),
+            ..err
+        })
+    }
+}
diff --git a/tvix/eval/src/io.rs b/tvix/eval/src/io.rs
new file mode 100644
index 0000000000..f775077af8
--- /dev/null
+++ b/tvix/eval/src/io.rs
@@ -0,0 +1,164 @@
+//! Interface for injecting I/O-related functionality into tvix-eval.
+//!
+//! The Nix language contains several builtins (e.g. `builtins.readDir`), as
+//! well as language feature (e.g. string-"coercion" of paths) that interact
+//! with the filesystem.
+//!
+//! The language evaluator implemented by this crate does not depend on any
+//! particular filesystem interaction model. Instead, this module provides a
+//! trait that can be implemented by tvix-eval callers to provide the
+//! functionality they desire.
+//!
+//! In theory this can be used to implement "mocked" filesystem interactions, or
+//! interaction with remote filesystems, etc.
+//!
+//! In the context of Nix builds, callers also use this interface to determine
+//! how store paths are opened and so on.
+
+use std::{
+    fs::File,
+    io,
+    path::{Path, PathBuf},
+};
+
+#[cfg(target_family = "unix")]
+use std::os::unix::ffi::OsStringExt;
+
+/// Types of files as represented by `builtins.readDir` in Nix.
+#[derive(Debug)]
+pub enum FileType {
+    Directory,
+    Regular,
+    Symlink,
+    Unknown,
+}
+
+/// Represents all possible filesystem interactions that exist in the Nix
+/// language, and that need to be executed somehow.
+///
+/// This trait is specifically *only* concerned with what is visible on the
+/// level of the language. All internal implementation details are not part of
+/// this trait.
+pub trait EvalIO {
+    /// Verify whether the file at the specified path exists.
+    ///
+    /// This is used for the following language evaluation cases:
+    ///
+    /// * checking whether a file added to the `NIX_PATH` actually exists when
+    ///   it is referenced in `<...>` brackets.
+    /// * `builtins.pathExists :: path -> bool`
+    fn path_exists(&self, path: &Path) -> io::Result<bool>;
+
+    /// Open the file at the specified path to a `io::Read`.
+    fn open(&self, path: &Path) -> io::Result<Box<dyn io::Read>>;
+
+    /// Read the directory at the specified path and return the names
+    /// of its entries associated with their [`FileType`].
+    ///
+    /// This is used for the following language evaluation cases:
+    ///
+    /// * `builtins.readDir :: path -> attrs<filename, filetype>`
+    fn read_dir(&self, path: &Path) -> io::Result<Vec<(bytes::Bytes, FileType)>>;
+
+    /// Import the given path. What this means depends on the implementation,
+    /// for example for a `std::io`-based implementation this might be a no-op,
+    /// while for a Tvix store this might be a copy of the given files to the
+    /// store.
+    ///
+    /// This is used for the following language evaluation cases:
+    ///
+    /// * string coercion of path literals (e.g. `/foo/bar`), which are expected
+    ///   to return a path
+    /// * `builtins.toJSON` on a path literal, also expected to return a path
+    fn import_path(&self, path: &Path) -> io::Result<PathBuf>;
+
+    /// Returns the root of the store directory, if such a thing
+    /// exists in the evaluation context.
+    ///
+    /// This is used for the following language evaluation cases:
+    ///
+    /// * `builtins.storeDir :: string`
+    fn store_dir(&self) -> Option<String> {
+        None
+    }
+}
+
+/// Implementation of [`EvalIO`] that simply uses the equivalent
+/// standard library functions, i.e. does local file-IO.
+#[cfg(feature = "impure")]
+pub struct StdIO;
+
+// TODO: we might want to make this whole impl to be target_family = "unix".
+#[cfg(feature = "impure")]
+impl EvalIO for StdIO {
+    fn path_exists(&self, path: &Path) -> io::Result<bool> {
+        path.try_exists()
+    }
+
+    fn open(&self, path: &Path) -> io::Result<Box<dyn io::Read>> {
+        Ok(Box::new(File::open(path)?))
+    }
+
+    fn read_dir(&self, path: &Path) -> io::Result<Vec<(bytes::Bytes, FileType)>> {
+        let mut result = vec![];
+
+        for entry in path.read_dir()? {
+            let entry = entry?;
+            let file_type = entry.metadata()?.file_type();
+
+            let val = if file_type.is_dir() {
+                FileType::Directory
+            } else if file_type.is_file() {
+                FileType::Regular
+            } else if file_type.is_symlink() {
+                FileType::Symlink
+            } else {
+                FileType::Unknown
+            };
+
+            result.push((entry.file_name().into_vec().into(), val))
+        }
+
+        Ok(result)
+    }
+
+    // this is a no-op for `std::io`, as the user can already refer to
+    // the path directly
+    fn import_path(&self, path: &Path) -> io::Result<PathBuf> {
+        Ok(path.to_path_buf())
+    }
+}
+
+/// Dummy implementation of [`EvalIO`], can be used in contexts where
+/// IO is not available but code should "pretend" that it is.
+pub struct DummyIO;
+
+impl EvalIO for DummyIO {
+    fn path_exists(&self, _: &Path) -> io::Result<bool> {
+        Err(io::Error::new(
+            io::ErrorKind::Unsupported,
+            "I/O methods are not implemented in DummyIO",
+        ))
+    }
+
+    fn open(&self, _: &Path) -> io::Result<Box<dyn io::Read>> {
+        Err(io::Error::new(
+            io::ErrorKind::Unsupported,
+            "I/O methods are not implemented in DummyIO",
+        ))
+    }
+
+    fn read_dir(&self, _: &Path) -> io::Result<Vec<(bytes::Bytes, FileType)>> {
+        Err(io::Error::new(
+            io::ErrorKind::Unsupported,
+            "I/O methods are not implemented in DummyIO",
+        ))
+    }
+
+    fn import_path(&self, _: &Path) -> io::Result<PathBuf> {
+        Err(io::Error::new(
+            io::ErrorKind::Unsupported,
+            "I/O methods are not implemented in DummyIO",
+        ))
+    }
+}
diff --git a/tvix/eval/src/lib.rs b/tvix/eval/src/lib.rs
new file mode 100644
index 0000000000..845964cb7e
--- /dev/null
+++ b/tvix/eval/src/lib.rs
@@ -0,0 +1,394 @@
+//! `tvix-eval` implements the evaluation of the Nix programming language in
+//! Tvix.
+//!
+//! It is designed to allow users to use Nix as a versatile language for
+//! different use-cases.
+//!
+//! This module exports the high-level functions and types needed for evaluating
+//! Nix code and interacting with the language's data structures.
+//!
+//! Nix has several language features that make use of impurities (such as
+//! reading from the NIX_PATH environment variable, or interacting with files).
+//! These features are optional and the API of this crate exposes functionality
+//! for controlling how they work.
+
+pub mod builtins;
+mod chunk;
+mod compiler;
+mod errors;
+mod io;
+pub mod observer;
+mod opcode;
+mod pretty_ast;
+mod source;
+mod spans;
+mod systems;
+mod upvalues;
+mod value;
+mod vm;
+mod warnings;
+
+mod nix_search_path;
+#[cfg(test)]
+mod properties;
+#[cfg(test)]
+mod test_utils;
+#[cfg(test)]
+mod tests;
+
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::str::FromStr;
+use std::sync::Arc;
+
+use crate::compiler::GlobalsMap;
+use crate::observer::{CompilerObserver, RuntimeObserver};
+use crate::value::Lambda;
+use crate::vm::run_lambda;
+
+// Re-export the public interface used by other crates.
+pub use crate::compiler::{compile, prepare_globals, CompilationOutput};
+pub use crate::errors::{AddContext, CatchableErrorKind, Error, ErrorKind, EvalResult};
+pub use crate::io::{DummyIO, EvalIO, FileType};
+pub use crate::pretty_ast::pretty_print_expr;
+pub use crate::source::SourceCode;
+pub use crate::value::{NixContext, NixContextElement};
+pub use crate::vm::generators;
+pub use crate::warnings::{EvalWarning, WarningKind};
+pub use builtin_macros;
+
+pub use crate::value::{Builtin, CoercionKind, NixAttrs, NixList, NixString, Value};
+
+#[cfg(feature = "impure")]
+pub use crate::io::StdIO;
+
+/// An `Evaluation` represents how a piece of Nix code is evaluated. It can be
+/// instantiated and configured directly, or it can be accessed through the
+/// various simplified helper methods available below.
+///
+/// Public fields are intended to be set by the caller. Setting all
+/// fields is optional.
+pub struct Evaluation<'co, 'ro, IO> {
+    /// Source code map used for error reporting.
+    source_map: SourceCode,
+
+    /// Set of all builtins that should be available during the
+    /// evaluation.
+    ///
+    /// This defaults to all pure builtins. Users might want to add
+    /// the set of impure builtins, or other custom builtins.
+    pub builtins: Vec<(&'static str, Value)>,
+
+    /// Set of builtins that are implemented in Nix itself and should
+    /// be compiled and inserted in the builtins set.
+    pub src_builtins: Vec<(&'static str, &'static str)>,
+
+    /// Implementation of file-IO to use during evaluation, e.g. for
+    /// impure builtins.
+    ///
+    /// Defaults to [`DummyIO`] if not set explicitly.
+    pub io_handle: IO,
+
+    /// Determines whether the `import` builtin should be made
+    /// available. Note that this depends on the `io_handle` being
+    /// able to read the files specified as arguments to `import`.
+    pub enable_import: bool,
+
+    /// Determines whether the returned value should be strictly
+    /// evaluated, that is whether its list and attribute set elements
+    /// should be forced recursively.
+    pub strict: bool,
+
+    /// (optional) Nix search path, e.g. the value of `NIX_PATH` used
+    /// for resolving items on the search path (such as `<nixpkgs>`).
+    pub nix_path: Option<String>,
+
+    /// (optional) compiler observer for reporting on compilation
+    /// details, like the emitted bytecode.
+    pub compiler_observer: Option<&'co mut dyn CompilerObserver>,
+
+    /// (optional) runtime observer, for reporting on execution steps
+    /// of Nix code.
+    pub runtime_observer: Option<&'ro mut dyn RuntimeObserver>,
+}
+
+/// Result of evaluating a piece of Nix code. If evaluation succeeded, a value
+/// will be present (and potentially some warnings!). If evaluation failed,
+/// errors will be present.
+#[derive(Debug, Default)]
+pub struct EvaluationResult {
+    /// Nix value that the code evaluated to.
+    pub value: Option<Value>,
+
+    /// Errors that occured during evaluation (if any).
+    pub errors: Vec<Error>,
+
+    /// Warnings that occured during evaluation. Warnings are not critical, but
+    /// should be addressed either to modernise code or improve performance.
+    pub warnings: Vec<EvalWarning>,
+
+    /// AST node that was parsed from the code (on success only).
+    pub expr: Option<rnix::ast::Expr>,
+}
+
+impl<'co, 'ro, IO> Evaluation<'co, 'ro, IO>
+where
+    IO: AsRef<dyn EvalIO> + 'static,
+{
+    /// Initialize an `Evaluation`.
+    pub fn new(io_handle: IO, enable_import: bool) -> Self {
+        let mut builtins = builtins::pure_builtins();
+        builtins.extend(builtins::placeholders()); // these are temporary
+
+        Self {
+            source_map: SourceCode::default(),
+            enable_import,
+            io_handle,
+            builtins,
+            src_builtins: vec![],
+            strict: false,
+            nix_path: None,
+            compiler_observer: None,
+            runtime_observer: None,
+        }
+    }
+}
+
+impl<'co, 'ro> Evaluation<'co, 'ro, Box<dyn EvalIO>> {
+    /// Initialize an `Evaluation`, without the import statement available, and
+    /// all IO operations stubbed out.
+    pub fn new_pure() -> Self {
+        Self::new(Box::new(DummyIO) as Box<dyn EvalIO>, false)
+    }
+
+    #[cfg(feature = "impure")]
+    /// Configure an `Evaluation` to have impure features available
+    /// with the given I/O implementation.
+    ///
+    /// If no I/O implementation is supplied, [`StdIO`] is used by
+    /// default.
+    pub fn enable_impure(&mut self, io: Option<Box<dyn EvalIO>>) {
+        self.io_handle = io.unwrap_or_else(|| Box::new(StdIO) as Box<dyn EvalIO>);
+        self.enable_import = true;
+        self.builtins.extend(builtins::impure_builtins());
+
+        // Make `NIX_PATH` resolutions work by default, unless the
+        // user already overrode this with something else.
+        if self.nix_path.is_none() {
+            self.nix_path = std::env::var("NIX_PATH").ok();
+        }
+    }
+
+    #[cfg(feature = "impure")]
+    /// Initialise an `Evaluation`, with all impure features turned on by default.
+    pub fn new_impure() -> Self {
+        let mut eval = Self::new_pure();
+        eval.enable_impure(None);
+        eval
+    }
+}
+
+impl<'co, 'ro, IO> Evaluation<'co, 'ro, IO>
+where
+    IO: AsRef<dyn EvalIO> + 'static,
+{
+    /// Clone the reference to the contained source code map. This is used after
+    /// an evaluation for pretty error printing.
+    pub fn source_map(&self) -> SourceCode {
+        self.source_map.clone()
+    }
+
+    /// Only compile the provided source code, at an optional location of the
+    /// source code (i.e. path to the file it was read from; used for error
+    /// reporting, and for resolving relative paths in impure functions)
+    /// This does not *run* the code, it only provides analysis (errors and
+    /// warnings) of the compiler.
+    pub fn compile_only(
+        mut self,
+        code: impl AsRef<str>,
+        location: Option<PathBuf>,
+    ) -> EvaluationResult {
+        let mut result = EvaluationResult::default();
+        let source = self.source_map();
+
+        let location_str = location
+            .as_ref()
+            .map(|p| p.to_string_lossy().to_string())
+            .unwrap_or_else(|| "[code]".into());
+
+        let file = source.add_file(location_str, code.as_ref().to_string());
+
+        let mut noop_observer = observer::NoOpObserver::default();
+        let compiler_observer = self.compiler_observer.take().unwrap_or(&mut noop_observer);
+
+        parse_compile_internal(
+            &mut result,
+            code.as_ref(),
+            file,
+            location,
+            source,
+            self.builtins,
+            self.src_builtins,
+            self.enable_import,
+            compiler_observer,
+        );
+
+        result
+    }
+
+    /// Evaluate the provided source code, at an optional location of the source
+    /// code (i.e. path to the file it was read from; used for error reporting,
+    /// and for resolving relative paths in impure functions)
+    pub fn evaluate(
+        mut self,
+        code: impl AsRef<str>,
+        location: Option<PathBuf>,
+    ) -> EvaluationResult {
+        let mut result = EvaluationResult::default();
+        let source = self.source_map();
+
+        let location_str = location
+            .as_ref()
+            .map(|p| p.to_string_lossy().to_string())
+            .unwrap_or_else(|| "[code]".into());
+
+        let file = source.add_file(location_str, code.as_ref().to_string());
+
+        let mut noop_observer = observer::NoOpObserver::default();
+        let compiler_observer = self.compiler_observer.take().unwrap_or(&mut noop_observer);
+
+        // Insert a storeDir builtin *iff* a store directory is present.
+        if let Some(store_dir) = self.io_handle.as_ref().store_dir() {
+            self.builtins.push(("storeDir", store_dir.into()));
+        }
+
+        let (lambda, globals) = match parse_compile_internal(
+            &mut result,
+            code.as_ref(),
+            file.clone(),
+            location,
+            source.clone(),
+            self.builtins,
+            self.src_builtins,
+            self.enable_import,
+            compiler_observer,
+        ) {
+            None => return result,
+            Some(cr) => cr,
+        };
+
+        // If bytecode was returned, there were no errors and the
+        // code is safe to execute.
+
+        let nix_path = self
+            .nix_path
+            .as_ref()
+            .and_then(|s| match nix_search_path::NixSearchPath::from_str(s) {
+                Ok(path) => Some(path),
+                Err(err) => {
+                    result.warnings.push(EvalWarning {
+                        kind: WarningKind::InvalidNixPath(err.to_string()),
+                        span: file.span,
+                    });
+                    None
+                }
+            })
+            .unwrap_or_default();
+
+        let runtime_observer = self.runtime_observer.take().unwrap_or(&mut noop_observer);
+
+        let vm_result = run_lambda(
+            nix_path,
+            self.io_handle,
+            runtime_observer,
+            source.clone(),
+            globals,
+            lambda,
+            self.strict,
+        );
+
+        match vm_result {
+            Ok(mut runtime_result) => {
+                result.warnings.append(&mut runtime_result.warnings);
+                if let Value::Catchable(inner) = runtime_result.value {
+                    result.errors.push(Error::new(
+                        ErrorKind::CatchableError(*inner),
+                        file.span,
+                        source,
+                    ));
+                    return result;
+                }
+
+                result.value = Some(runtime_result.value);
+            }
+            Err(err) => {
+                result.errors.push(err);
+            }
+        }
+
+        result
+    }
+}
+
+/// Internal helper function for common parsing & compilation logic
+/// between the public functions.
+#[allow(clippy::too_many_arguments)] // internal API, no point making an indirection type
+fn parse_compile_internal(
+    result: &mut EvaluationResult,
+    code: &str,
+    file: Arc<codemap::File>,
+    location: Option<PathBuf>,
+    source: SourceCode,
+    builtins: Vec<(&'static str, Value)>,
+    src_builtins: Vec<(&'static str, &'static str)>,
+    enable_import: bool,
+    compiler_observer: &mut dyn CompilerObserver,
+) -> Option<(Rc<Lambda>, Rc<GlobalsMap>)> {
+    let parsed = rnix::ast::Root::parse(code);
+    let parse_errors = parsed.errors();
+
+    if !parse_errors.is_empty() {
+        result.errors.push(Error::new(
+            ErrorKind::ParseErrors(parse_errors.to_vec()),
+            file.span,
+            source,
+        ));
+        return None;
+    }
+
+    // At this point we know that the code is free of parse errors and
+    // we can continue to compile it. The expression is persisted in
+    // the result, in case the caller needs it for something.
+    result.expr = parsed.tree().expr();
+
+    let builtins =
+        crate::compiler::prepare_globals(builtins, src_builtins, source.clone(), enable_import);
+
+    let compiler_result = match compiler::compile(
+        result.expr.as_ref().unwrap(),
+        location,
+        builtins,
+        &source,
+        &file,
+        compiler_observer,
+    ) {
+        Ok(result) => result,
+        Err(err) => {
+            result.errors.push(err);
+            return None;
+        }
+    };
+
+    result.warnings = compiler_result.warnings;
+    result.errors.extend(compiler_result.errors);
+
+    // Short-circuit if errors exist at this point (do not pass broken
+    // bytecode to the runtime).
+    if !result.errors.is_empty() {
+        return None;
+    }
+
+    // Return the lambda (for execution) and the globals map (to
+    // ensure the invariant that the globals outlive the runtime).
+    Some((compiler_result.lambda, compiler_result.globals))
+}
diff --git a/tvix/eval/src/nix_search_path.rs b/tvix/eval/src/nix_search_path.rs
new file mode 100644
index 0000000000..566ca12238
--- /dev/null
+++ b/tvix/eval/src/nix_search_path.rs
@@ -0,0 +1,256 @@
+use path_clean::PathClean;
+use std::convert::Infallible;
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
+
+use crate::errors::{CatchableErrorKind, ErrorKind};
+use crate::EvalIO;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+enum NixSearchPathEntry {
+    /// Resolve subdirectories of this path within `<...>` brackets. This
+    /// corresponds to bare paths within the `NIX_PATH` environment variable
+    ///
+    /// For example, with `NixSearchPathEntry::Path("/example")` and the following
+    /// directory structure:
+    ///
+    /// ```notrust
+    /// example
+    /// └── subdir
+    ///     └── grandchild
+    /// ```
+    ///
+    /// A Nix path literal `<subdir>` would resolve to `/example/subdir`, and a
+    /// Nix path literal `<subdir/grandchild>` would resolve to
+    /// `/example/subdir/grandchild`
+    Path(PathBuf),
+
+    /// Resolve paths starting with `prefix` as subdirectories of `path`. This
+    /// corresponds to `prefix=path` within the `NIX_PATH` environment variable.
+    ///
+    /// For example, with `NixSearchPathEntry::Prefix { prefix: "prefix", path:
+    /// "/example" }` and the following directory structure:
+    ///
+    /// ```notrust
+    /// example
+    /// └── subdir
+    ///     └── grandchild
+    /// ```
+    ///
+    /// A Nix path literal `<prefix/subdir>` would resolve to `/example/subdir`,
+    /// and a Nix path literal `<prefix/subdir/grandchild>` would resolve to
+    /// `/example/subdir/grandchild`
+    Prefix { prefix: PathBuf, path: PathBuf },
+}
+
+fn canonicalise(path: PathBuf) -> Result<PathBuf, ErrorKind> {
+    let absolute = if path.is_absolute() {
+        path
+    } else {
+        // TODO(tazjin): probably panics in wasm?
+        std::env::current_dir()
+            .map_err(|e| ErrorKind::IO {
+                path: Some(path.clone()),
+                error: e.into(),
+            })?
+            .join(path)
+    }
+    .clean();
+
+    Ok(absolute)
+}
+
+impl NixSearchPathEntry {
+    /// Determine whether this path entry matches the given lookup path.
+    ///
+    /// For bare paths, an entry is considered to match if a matching
+    /// file exists under it.
+    ///
+    /// For prefixed path, an entry matches if the prefix does.
+    // TODO(tazjin): verify these rules in the C++ impl, seems fishy.
+    fn resolve<IO>(&self, io: IO, lookup_path: &Path) -> Result<Option<PathBuf>, ErrorKind>
+    where
+        IO: AsRef<dyn EvalIO>,
+    {
+        let path = match self {
+            NixSearchPathEntry::Path(parent) => canonicalise(parent.join(lookup_path))?,
+
+            NixSearchPathEntry::Prefix { prefix, path } => {
+                if let Ok(child_path) = lookup_path.strip_prefix(prefix) {
+                    canonicalise(path.join(child_path))?
+                } else {
+                    return Ok(None);
+                }
+            }
+        };
+
+        if io.as_ref().path_exists(&path).map_err(|e| ErrorKind::IO {
+            path: Some(path.clone()),
+            error: e.into(),
+        })? {
+            Ok(Some(path))
+        } else {
+            Ok(None)
+        }
+    }
+}
+
+impl FromStr for NixSearchPathEntry {
+    type Err = Infallible;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s.split_once('=') {
+            Some((prefix, path)) => Ok(Self::Prefix {
+                prefix: prefix.into(),
+                path: path.into(),
+            }),
+            None => Ok(Self::Path(s.into())),
+        }
+    }
+}
+
+/// Struct implementing the format and path resolution rules of the `NIX_PATH`
+/// environment variable.
+///
+/// This struct can be constructed by parsing a string using the [`FromStr`]
+/// impl, or via [`str::parse`]. Nix `<...>` paths can then be resolved using
+/// [`NixSearchPath::resolve`].
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct NixSearchPath {
+    entries: Vec<NixSearchPathEntry>,
+}
+
+impl NixSearchPath {
+    /// Attempt to resolve the given `path` within this [`NixSearchPath`] using the
+    /// path resolution rules for `<...>`-style paths
+    pub fn resolve<P, IO>(
+        &self,
+        io: IO,
+        path: P,
+    ) -> Result<Result<PathBuf, CatchableErrorKind>, ErrorKind>
+    where
+        P: AsRef<Path>,
+        IO: AsRef<dyn EvalIO>,
+    {
+        let path = path.as_ref();
+        for entry in &self.entries {
+            if let Some(p) = entry.resolve(&io, path)? {
+                return Ok(Ok(p));
+            }
+        }
+        Ok(Err(CatchableErrorKind::NixPathResolution(
+            format!(
+                "path '{}' was not found in the Nix search path",
+                path.display()
+            )
+            .into_boxed_str(),
+        )))
+    }
+}
+
+impl FromStr for NixSearchPath {
+    type Err = Infallible;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let entries = s
+            .split(':')
+            .map(|s| s.parse())
+            .collect::<Result<Vec<_>, _>>()?;
+        Ok(NixSearchPath { entries })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    mod parse {
+        use super::*;
+
+        #[test]
+        fn bare_paths() {
+            assert_eq!(
+                NixSearchPath::from_str("/foo/bar:/baz").unwrap(),
+                NixSearchPath {
+                    entries: vec![
+                        NixSearchPathEntry::Path("/foo/bar".into()),
+                        NixSearchPathEntry::Path("/baz".into())
+                    ],
+                }
+            );
+        }
+
+        #[test]
+        fn mixed_prefix_and_paths() {
+            assert_eq!(
+                NixSearchPath::from_str("nixpkgs=/my/nixpkgs:/etc/nixos").unwrap(),
+                NixSearchPath {
+                    entries: vec![
+                        NixSearchPathEntry::Prefix {
+                            prefix: "nixpkgs".into(),
+                            path: "/my/nixpkgs".into()
+                        },
+                        NixSearchPathEntry::Path("/etc/nixos".into())
+                    ],
+                }
+            );
+        }
+    }
+
+    mod resolve {
+        use crate::StdIO;
+        use path_clean::PathClean;
+        use std::env::current_dir;
+
+        use super::*;
+
+        #[test]
+        fn simple_dir() {
+            let nix_search_path = NixSearchPath::from_str("./.").unwrap();
+            let io = Box::new(StdIO {}) as Box<dyn EvalIO>;
+            let res = nix_search_path.resolve(&io, "src").unwrap();
+            assert_eq!(
+                res.unwrap().to_path_buf(),
+                current_dir().unwrap().join("src").clean()
+            );
+        }
+
+        #[test]
+        fn failed_resolution() {
+            let nix_search_path = NixSearchPath::from_str("./.").unwrap();
+            let io = Box::new(StdIO {}) as Box<dyn EvalIO>;
+            let err = nix_search_path.resolve(&io, "nope").unwrap();
+            assert!(
+                matches!(err, Err(CatchableErrorKind::NixPathResolution(..))),
+                "err = {err:?}"
+            );
+        }
+
+        #[test]
+        fn second_in_path() {
+            let nix_search_path = NixSearchPath::from_str("./.:/").unwrap();
+            let io = Box::new(StdIO {}) as Box<dyn EvalIO>;
+            let res = nix_search_path.resolve(&io, "etc").unwrap();
+            assert_eq!(res.unwrap().to_path_buf(), Path::new("/etc"));
+        }
+
+        #[test]
+        fn prefix() {
+            let nix_search_path = NixSearchPath::from_str("/:tvix=.").unwrap();
+            let io = Box::new(StdIO {}) as Box<dyn EvalIO>;
+            let res = nix_search_path.resolve(&io, "tvix/src").unwrap();
+            assert_eq!(
+                res.unwrap().to_path_buf(),
+                current_dir().unwrap().join("src").clean()
+            );
+        }
+
+        #[test]
+        fn matching_prefix() {
+            let nix_search_path = NixSearchPath::from_str("/:tvix=.").unwrap();
+            let io = Box::new(StdIO {}) as Box<dyn EvalIO>;
+            let res = nix_search_path.resolve(&io, "tvix").unwrap();
+            assert_eq!(res.unwrap().to_path_buf(), current_dir().unwrap().clean());
+        }
+    }
+}
diff --git a/tvix/eval/src/observer.rs b/tvix/eval/src/observer.rs
new file mode 100644
index 0000000000..f5de399315
--- /dev/null
+++ b/tvix/eval/src/observer.rs
@@ -0,0 +1,318 @@
+//! Implements traits for things that wish to observe internal state
+//! changes of tvix-eval.
+//!
+//! This can be used to gain insights from compilation, to trace the
+//! runtime, and so on.
+//!
+//! All methods are optional, that is, observers can implement only
+/// what they are interested in observing.
+use std::io::Write;
+use std::rc::Rc;
+use std::time::Instant;
+use tabwriter::TabWriter;
+
+use crate::chunk::Chunk;
+use crate::generators::VMRequest;
+use crate::opcode::{CodeIdx, OpCode};
+use crate::value::Lambda;
+use crate::SourceCode;
+use crate::Value;
+
+/// Implemented by types that wish to observe internal happenings of
+/// the Tvix compiler.
+pub trait CompilerObserver {
+    /// Called when the compiler finishes compilation of the top-level
+    /// of an expression (usually the root Nix expression of a file).
+    fn observe_compiled_toplevel(&mut self, _: &Rc<Lambda>) {}
+
+    /// Called when the compiler finishes compilation of a
+    /// user-defined function.
+    ///
+    /// Note that in Nix there are only single argument functions, so
+    /// in an expression like `a: b: c: ...` this method will be
+    /// called three times.
+    fn observe_compiled_lambda(&mut self, _: &Rc<Lambda>) {}
+
+    /// Called when the compiler finishes compilation of a thunk.
+    fn observe_compiled_thunk(&mut self, _: &Rc<Lambda>) {}
+}
+
+/// Implemented by types that wish to observe internal happenings of
+/// the Tvix virtual machine at runtime.
+pub trait RuntimeObserver {
+    /// Called when the runtime enters a new call frame.
+    fn observe_enter_call_frame(&mut self, _arg_count: usize, _: &Rc<Lambda>, _call_depth: usize) {}
+
+    /// Called when the runtime exits a call frame.
+    fn observe_exit_call_frame(&mut self, _frame_at: usize, _stack: &[Value]) {}
+
+    /// Called when the runtime suspends a call frame.
+    fn observe_suspend_call_frame(&mut self, _frame_at: usize, _stack: &[Value]) {}
+
+    /// Called when the runtime enters a generator frame.
+    fn observe_enter_generator(&mut self, _frame_at: usize, _name: &str, _stack: &[Value]) {}
+
+    /// Called when the runtime exits a generator frame.
+    fn observe_exit_generator(&mut self, _frame_at: usize, _name: &str, _stack: &[Value]) {}
+
+    /// Called when the runtime suspends a generator frame.
+    fn observe_suspend_generator(&mut self, _frame_at: usize, _name: &str, _stack: &[Value]) {}
+
+    /// Called when a generator requests an action from the VM.
+    fn observe_generator_request(&mut self, _name: &str, _msg: &VMRequest) {}
+
+    /// Called when the runtime replaces the current call frame for a
+    /// tail call.
+    fn observe_tail_call(&mut self, _frame_at: usize, _: &Rc<Lambda>) {}
+
+    /// Called when the runtime enters a builtin.
+    fn observe_enter_builtin(&mut self, _name: &'static str) {}
+
+    /// Called when the runtime exits a builtin.
+    fn observe_exit_builtin(&mut self, _name: &'static str, _stack: &[Value]) {}
+
+    /// Called when the runtime *begins* executing an instruction. The
+    /// provided stack is the state at the beginning of the operation.
+    fn observe_execute_op(&mut self, _ip: CodeIdx, _: &OpCode, _: &[Value]) {}
+}
+
+#[derive(Default)]
+pub struct NoOpObserver {}
+
+impl CompilerObserver for NoOpObserver {}
+impl RuntimeObserver for NoOpObserver {}
+
+/// An observer that prints disassembled chunk information to its
+/// internal writer whenwever the compiler emits a toplevel function,
+/// closure or thunk.
+pub struct DisassemblingObserver<W: Write> {
+    source: SourceCode,
+    writer: TabWriter<W>,
+}
+
+impl<W: Write> DisassemblingObserver<W> {
+    pub fn new(source: SourceCode, writer: W) -> Self {
+        Self {
+            source,
+            writer: TabWriter::new(writer),
+        }
+    }
+
+    fn lambda_header(&mut self, kind: &str, lambda: &Rc<Lambda>) {
+        let _ = writeln!(
+            &mut self.writer,
+            "=== compiled {} @ {:p} ({} ops) ===",
+            kind,
+            *lambda,
+            lambda.chunk.code.len()
+        );
+    }
+
+    fn disassemble_chunk(&mut self, chunk: &Chunk) {
+        // calculate width of the widest address in the chunk
+        let width = format!("{:#x}", chunk.code.len() - 1).len();
+
+        for (idx, _) in chunk.code.iter().enumerate() {
+            let _ = chunk.disassemble_op(&mut self.writer, &self.source, width, CodeIdx(idx));
+        }
+    }
+}
+
+impl<W: Write> CompilerObserver for DisassemblingObserver<W> {
+    fn observe_compiled_toplevel(&mut self, lambda: &Rc<Lambda>) {
+        self.lambda_header("toplevel", lambda);
+        self.disassemble_chunk(&lambda.chunk);
+        let _ = self.writer.flush();
+    }
+
+    fn observe_compiled_lambda(&mut self, lambda: &Rc<Lambda>) {
+        self.lambda_header("lambda", lambda);
+        self.disassemble_chunk(&lambda.chunk);
+        let _ = self.writer.flush();
+    }
+
+    fn observe_compiled_thunk(&mut self, lambda: &Rc<Lambda>) {
+        self.lambda_header("thunk", lambda);
+        self.disassemble_chunk(&lambda.chunk);
+        let _ = self.writer.flush();
+    }
+}
+
+/// An observer that collects a textual representation of an entire
+/// runtime execution.
+pub struct TracingObserver<W: Write> {
+    // If timing is enabled, contains the timestamp of the last-emitted trace event
+    last_event: Option<Instant>,
+    writer: TabWriter<W>,
+}
+
+impl<W: Write> TracingObserver<W> {
+    pub fn new(writer: W) -> Self {
+        Self {
+            last_event: None,
+            writer: TabWriter::new(writer),
+        }
+    }
+
+    /// Write the time of each runtime event, relative to when this method is called
+    pub fn enable_timing(&mut self) {
+        self.last_event = Some(Instant::now());
+    }
+
+    fn maybe_write_time(&mut self) {
+        if let Some(last_event) = &mut self.last_event {
+            let _ = write!(&mut self.writer, "+{}ns\t", last_event.elapsed().as_nanos());
+            *last_event = Instant::now();
+        }
+    }
+
+    fn write_value(&mut self, val: &Value) {
+        let _ = match val {
+            // Potentially large types which we only want to print
+            // the type of (and avoid recursing).
+            Value::List(l) => write!(&mut self.writer, "list[{}] ", l.len()),
+            Value::Attrs(a) => write!(&mut self.writer, "attrs[{}] ", a.len()),
+            Value::Thunk(t) if t.is_evaluated() => {
+                self.write_value(&t.value());
+                Ok(())
+            }
+
+            // For other value types, defer to the standard value printer.
+            _ => write!(&mut self.writer, "{} ", val),
+        };
+    }
+
+    fn write_stack(&mut self, stack: &[Value]) {
+        let _ = write!(&mut self.writer, "[ ");
+
+        // Print out a maximum of 6 values from the top of the stack,
+        // before abbreviating it to `...`.
+        for (i, val) in stack.iter().rev().enumerate() {
+            if i == 6 {
+                let _ = write!(&mut self.writer, "...");
+                break;
+            }
+
+            self.write_value(val);
+        }
+
+        let _ = writeln!(&mut self.writer, "]");
+    }
+}
+
+impl<W: Write> RuntimeObserver for TracingObserver<W> {
+    fn observe_enter_call_frame(
+        &mut self,
+        arg_count: usize,
+        lambda: &Rc<Lambda>,
+        call_depth: usize,
+    ) {
+        self.maybe_write_time();
+
+        let _ = write!(&mut self.writer, "=== entering ");
+
+        let _ = if arg_count == 0 {
+            write!(&mut self.writer, "thunk ")
+        } else {
+            write!(&mut self.writer, "closure ")
+        };
+
+        if let Some(name) = &lambda.name {
+            let _ = write!(&mut self.writer, "'{}' ", name);
+        }
+
+        let _ = writeln!(
+            &mut self.writer,
+            "in frame[{}] @ {:p} ===",
+            call_depth, *lambda
+        );
+    }
+
+    /// Called when the runtime exits a call frame.
+    fn observe_exit_call_frame(&mut self, frame_at: usize, stack: &[Value]) {
+        self.maybe_write_time();
+        let _ = write!(&mut self.writer, "=== exiting frame {} ===\t ", frame_at);
+        self.write_stack(stack);
+    }
+
+    fn observe_suspend_call_frame(&mut self, frame_at: usize, stack: &[Value]) {
+        self.maybe_write_time();
+        let _ = write!(&mut self.writer, "=== suspending frame {} ===\t", frame_at);
+
+        self.write_stack(stack);
+    }
+
+    fn observe_enter_generator(&mut self, frame_at: usize, name: &str, stack: &[Value]) {
+        self.maybe_write_time();
+        let _ = write!(
+            &mut self.writer,
+            "=== entering generator frame '{}' [{}] ===\t",
+            name, frame_at,
+        );
+
+        self.write_stack(stack);
+    }
+
+    fn observe_exit_generator(&mut self, frame_at: usize, name: &str, stack: &[Value]) {
+        self.maybe_write_time();
+        let _ = write!(
+            &mut self.writer,
+            "=== exiting generator '{}' [{}] ===\t",
+            name, frame_at
+        );
+
+        self.write_stack(stack);
+    }
+
+    fn observe_suspend_generator(&mut self, frame_at: usize, name: &str, stack: &[Value]) {
+        self.maybe_write_time();
+        let _ = write!(
+            &mut self.writer,
+            "=== suspending generator '{}' [{}] ===\t",
+            name, frame_at
+        );
+
+        self.write_stack(stack);
+    }
+
+    fn observe_generator_request(&mut self, name: &str, msg: &VMRequest) {
+        self.maybe_write_time();
+        let _ = writeln!(
+            &mut self.writer,
+            "=== generator '{}' requested {} ===",
+            name, msg
+        );
+    }
+
+    fn observe_enter_builtin(&mut self, name: &'static str) {
+        self.maybe_write_time();
+        let _ = writeln!(&mut self.writer, "=== entering builtin {} ===", name);
+    }
+
+    fn observe_exit_builtin(&mut self, name: &'static str, stack: &[Value]) {
+        self.maybe_write_time();
+        let _ = write!(&mut self.writer, "=== exiting builtin {} ===\t", name);
+        self.write_stack(stack);
+    }
+
+    fn observe_tail_call(&mut self, frame_at: usize, lambda: &Rc<Lambda>) {
+        self.maybe_write_time();
+        let _ = writeln!(
+            &mut self.writer,
+            "=== tail-calling {:p} in frame[{}] ===",
+            *lambda, frame_at
+        );
+    }
+
+    fn observe_execute_op(&mut self, ip: CodeIdx, op: &OpCode, stack: &[Value]) {
+        self.maybe_write_time();
+        let _ = write!(&mut self.writer, "{:04} {:?}\t", ip.0, op);
+        self.write_stack(stack);
+    }
+}
+
+impl<W: Write> Drop for TracingObserver<W> {
+    fn drop(&mut self) {
+        let _ = self.writer.flush();
+    }
+}
diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs
new file mode 100644
index 0000000000..f89c1c12e7
--- /dev/null
+++ b/tvix/eval/src/opcode.rs
@@ -0,0 +1,284 @@
+//! This module implements the instruction set running on the abstract
+//! machine implemented by tvix.
+
+use std::ops::{AddAssign, Sub};
+
+/// Index of a constant in the current code chunk.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct ConstantIdx(pub usize);
+
+/// Index of an instruction in the current code chunk.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug)]
+pub struct CodeIdx(pub usize);
+
+impl AddAssign<usize> for CodeIdx {
+    fn add_assign(&mut self, rhs: usize) {
+        *self = CodeIdx(self.0 + rhs)
+    }
+}
+
+impl Sub<usize> for CodeIdx {
+    type Output = Self;
+
+    fn sub(self, rhs: usize) -> Self::Output {
+        CodeIdx(self.0 - rhs)
+    }
+}
+
+/// Index of a value in the runtime stack.  This is an offset
+/// *relative to* the VM value stack_base of the CallFrame
+/// containing the opcode which contains this StackIdx.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
+pub struct StackIdx(pub usize);
+
+/// Index of an upvalue within a closure's bound-variable upvalue
+/// list.  This is an absolute index into the Upvalues of the
+/// CallFrame containing the opcode which contains this UpvalueIdx.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct UpvalueIdx(pub usize);
+
+/// Offset by which an instruction pointer should change in a jump.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct JumpOffset(pub usize);
+
+/// Provided count for an instruction (could represent e.g. a number
+/// of elements).
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct Count(pub usize);
+
+/// All variants of this enum carry a bounded amount of data to
+/// ensure that no heap allocations are needed for an Opcode.
+///
+/// In documentation comments, stack positions are referred to by
+/// indices written in `{}` as such, where required:
+///
+/// ```notrust
+///                             --- top of the stack
+///                            /
+///                           v
+///       [ ... | 3 | 2 | 1 | 0 ]
+///                   ^
+///                  /
+/// 2 values deep ---
+/// ```
+///
+/// Unless otherwise specified, operations leave their result at the
+/// top of the stack.
+#[warn(variant_size_differences)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum OpCode {
+    /// Push a constant onto the stack.
+    OpConstant(ConstantIdx),
+
+    // Unary operators
+    /// Discard a value from the stack.
+    OpPop,
+
+    /// Invert the boolean at the top of the stack.
+    OpInvert,
+
+    // Binary operators
+    /// Invert the sign of the number at the top of the stack.
+    OpNegate,
+
+    /// Sum up the two numbers at the top of the stack.
+    OpAdd,
+
+    /// Subtract the number at {1} from the number at {2}.
+    OpSub,
+
+    /// Multiply the two numbers at the top of the stack.
+    OpMul,
+
+    /// Divide the two numbers at the top of the stack.
+    OpDiv,
+
+    // Comparison operators
+    /// Check the two values at the top of the stack for Nix-equality.
+    OpEqual,
+
+    /// Check whether the value at {2} is less than {1}.
+    OpLess,
+
+    /// Check whether the value at {2} is less than or equal to {1}.
+    OpLessOrEq,
+
+    /// Check whether the value at {2} is greater than {1}.
+    OpMore,
+
+    /// Check whether the value at {2} is greater than or equal to {1}.
+    OpMoreOrEq,
+
+    // Logical operators & generic jumps
+    /// Jump forward in the bytecode specified by the number of
+    /// instructions in its usize operand.
+    OpJump(JumpOffset),
+
+    /// Jump forward in the bytecode specified by the number of
+    /// instructions in its usize operand, *if* the value at the top
+    /// of the stack is `true`.
+    OpJumpIfTrue(JumpOffset),
+
+    /// Jump forward in the bytecode specified by the number of
+    /// instructions in its usize operand, *if* the value at the top
+    /// of the stack is `false`.
+    OpJumpIfFalse(JumpOffset),
+
+    /// Pop one stack item and jump forward in the bytecode
+    /// specified by the number of instructions in its usize
+    /// operand, *if* the value at the top of the stack is a
+    /// Value::Catchable.
+    OpJumpIfCatchable(JumpOffset),
+
+    /// Jump forward in the bytecode specified by the number of
+    /// instructions in its usize operand, *if* the value at the top
+    /// of the stack is the internal value representing a missing
+    /// attribute set key.
+    OpJumpIfNotFound(JumpOffset),
+
+    /// Jump forward in the bytecode specified by the number of
+    /// instructions in its usize operand, *if* the value at the top
+    /// of the stack is *not* the internal value requesting a
+    /// stack value finalisation.
+    OpJumpIfNoFinaliseRequest(JumpOffset),
+
+    // Attribute sets
+    /// Construct an attribute set from the given number of key-value pairs on the top of the stack
+    ///
+    /// Note that this takes the count of *pairs*, not the number of *stack values* - the actual
+    /// number of values popped off the stack will be twice the argument to this op
+    OpAttrs(Count),
+    /// Merge the attribute set at {2} into the attribute set at {1},
+    /// and leave the new set at the top of the stack.
+    OpAttrsUpdate,
+
+    /// Select the attribute with the name at {1} from the set at {2}.
+    OpAttrsSelect,
+
+    /// Select the attribute with the name at {1} from the set at {2}, but leave
+    /// a `Value::AttrNotFound` in the stack instead of failing if it is
+    /// missing.
+    OpAttrsTrySelect,
+
+    /// Check for the presence of the attribute with the name at {1} in the set
+    /// at {2}.
+    OpHasAttr,
+
+    /// Throw an error if the attribute set at the top of the stack has any attributes
+    /// other than those listed in the formals of the current lambda
+    ///
+    /// Panics if the current frame is not a lambda with formals
+    OpValidateClosedFormals,
+
+    // `with`-handling
+    /// Push a value onto the runtime `with`-stack to enable dynamic identifier
+    /// resolution. The absolute stack index of the value is supplied as a usize
+    /// operand.
+    OpPushWith(StackIdx),
+
+    /// Pop the last runtime `with`-stack element.
+    OpPopWith,
+
+    /// Dynamically resolve an identifier with the name at {1} from the runtime
+    /// `with`-stack.
+    OpResolveWith,
+
+    // Lists
+    /// Construct a list from the given number of values at the top of the
+    /// stack.
+    OpList(Count),
+
+    /// Concatenate the lists at {2} and {1}.
+    OpConcat,
+
+    // Strings
+    /// Interpolate the given number of string fragments into a single string.
+    OpInterpolate(Count),
+
+    /// Force the Value on the stack and coerce it to a string
+    OpCoerceToString(crate::CoercionKind),
+
+    // Paths
+    /// Attempt to resolve the Value on the stack using the configured [`NixSearchPath`][]
+    ///
+    /// [`NixSearchPath`]: crate::nix_search_path::NixSearchPath
+    OpFindFile,
+
+    /// Attempt to resolve a path literal relative to the home dir
+    OpResolveHomePath,
+
+    // Type assertion operators
+    /// Assert that the value at {1} is a boolean, and fail with a runtime error
+    /// otherwise.
+    OpAssertBool,
+    OpAssertAttrs,
+
+    /// Access local identifiers with statically known positions.
+    OpGetLocal(StackIdx),
+
+    /// Close scopes while leaving their expression value around.
+    OpCloseScope(Count), // number of locals to pop
+
+    /// Return an error indicating that an `assert` failed
+    OpAssertFail,
+
+    // Lambdas & closures
+    /// Call the value at {1} in a new VM callframe
+    OpCall,
+
+    /// Retrieve the upvalue at the given index from the closure or thunk
+    /// currently under evaluation.
+    OpGetUpvalue(UpvalueIdx),
+
+    /// Construct a closure which has upvalues but no self-references
+    OpClosure(ConstantIdx),
+
+    /// Construct a closure which has self-references (direct or via upvalues)
+    OpThunkClosure(ConstantIdx),
+
+    /// Construct a suspended thunk, used to delay a computation for laziness.
+    OpThunkSuspended(ConstantIdx),
+
+    /// Force the value at {1} until it is a `Thunk::Evaluated`.
+    OpForce,
+
+    /// Finalise initialisation of the upvalues of the value in the given stack
+    /// index (which must be a Value::Thunk) after the scope is fully bound.
+    OpFinalise(StackIdx),
+
+    /// Final instruction emitted in a chunk. Does not have an
+    /// inherent effect, but can simplify VM logic as a marker in some
+    /// cases.
+    ///
+    /// Can be thought of as "returning" the value to the parent
+    /// frame, hence the name.
+    OpReturn,
+
+    // [`OpClosure`], [`OpThunkSuspended`], and [`OpThunkClosure`] have a
+    // variable number of arguments to the instruction, which is
+    // represented here by making their data part of the opcodes.
+    // Each of these two opcodes has a `ConstantIdx`, which must
+    // reference a `Value::Blueprint(Lambda)`.  The `upvalue_count`
+    // field in that `Lambda` indicates the number of arguments it
+    // takes, and the opcode must be followed by exactly this number
+    // of `Data*` opcodes.  The VM skips over these by advancing the
+    // instruction pointer.
+    //
+    // It is illegal for a `Data*` opcode to appear anywhere else.
+    /// Populate a static upvalue by copying from the stack immediately.
+    DataStackIdx(StackIdx),
+    /// Populate a static upvalue of a thunk by copying it the stack, but do
+    /// when the thunk is finalised (by OpFinalise) rather than immediately.
+    DataDeferredLocal(StackIdx),
+    /// Populate a static upvalue by copying it from the upvalues of an
+    /// enclosing scope.
+    DataUpvalueIdx(UpvalueIdx),
+    /// Populate dynamic upvalues by saving a copy of the with-stack.
+    DataCaptureWith,
+}
diff --git a/tvix/eval/src/pretty_ast.rs b/tvix/eval/src/pretty_ast.rs
new file mode 100644
index 0000000000..5ac115e21c
--- /dev/null
+++ b/tvix/eval/src/pretty_ast.rs
@@ -0,0 +1,468 @@
+//! Pretty-printed format for the rnix AST representation.
+//!
+//! The AST is serialised into a JSON structure that can then be
+//! printed in either minimised or well-formatted style.
+
+use rnix::ast::{self, AstToken, HasEntry};
+use serde::{ser::SerializeMap, Serialize, Serializer};
+
+pub fn pretty_print_expr(expr: &ast::Expr) -> String {
+    serde_json::ser::to_string_pretty(&SerializeAST(expr))
+        .expect("serializing AST should always succeed")
+}
+
+#[repr(transparent)]
+struct SerializeAST<S>(S);
+
+impl<'a> Serialize for SerializeAST<&'a ast::Apply> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(3))?;
+        map.serialize_entry("kind", "apply")?;
+        map.serialize_entry("fn", &SerializeAST(&self.0.lambda().unwrap()))?;
+        map.serialize_entry("arg", &SerializeAST(&self.0.argument().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Assert> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(3))?;
+        map.serialize_entry("kind", "assert")?;
+        map.serialize_entry("condition", &SerializeAST(&self.0.condition().unwrap()))?;
+        map.serialize_entry("body", &SerializeAST(&self.0.body().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Error> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "error")?;
+        map.serialize_entry("node", &self.0.to_string())?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::IfElse> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(4))?;
+        map.serialize_entry("kind", "if_else")?;
+        map.serialize_entry("condition", &SerializeAST(&self.0.condition().unwrap()))?;
+        map.serialize_entry("then_body", &SerializeAST(&self.0.body().unwrap()))?;
+        map.serialize_entry("else_body", &SerializeAST(&self.0.else_body().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Select> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let size = match self.0.default_expr() {
+            Some(_) => 4,
+            None => 3,
+        };
+
+        let mut map = serializer.serialize_map(Some(size))?;
+        map.serialize_entry("kind", "select")?;
+        map.serialize_entry("set", &SerializeAST(&self.0.expr().unwrap()))?;
+        map.serialize_entry("path", &SerializeAST(self.0.attrpath().unwrap()))?;
+
+        if let Some(default) = self.0.default_expr() {
+            map.serialize_entry("default", &SerializeAST(&default))?;
+        }
+
+        map.end()
+    }
+}
+
+impl Serialize for SerializeAST<ast::InterpolPart<String>> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        match &self.0 {
+            ast::InterpolPart::Literal(s) => Serialize::serialize(s, serializer),
+            ast::InterpolPart::Interpolation(node) => {
+                Serialize::serialize(&SerializeAST(&node.expr().unwrap()), serializer)
+            }
+        }
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Str> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "string")?;
+
+        map.serialize_entry(
+            "parts",
+            &self
+                .0
+                .normalized_parts()
+                .into_iter()
+                .map(SerializeAST)
+                .collect::<Vec<_>>(),
+        )?;
+
+        map.end()
+    }
+}
+
+impl Serialize for SerializeAST<ast::InterpolPart<ast::PathContent>> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        match &self.0 {
+            ast::InterpolPart::Literal(p) => Serialize::serialize(p.syntax().text(), serializer),
+            ast::InterpolPart::Interpolation(node) => {
+                Serialize::serialize(&SerializeAST(&node.expr().unwrap()), serializer)
+            }
+        }
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Path> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "path")?;
+
+        map.serialize_entry(
+            "parts",
+            &self.0.parts().map(SerializeAST).collect::<Vec<_>>(),
+        )?;
+
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Literal> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "literal")?;
+
+        match self.0.kind() {
+            ast::LiteralKind::Float(val) => map.serialize_entry("float", &val.value().unwrap()),
+            ast::LiteralKind::Integer(val) => map.serialize_entry("int", &val.value().unwrap()),
+            ast::LiteralKind::Uri(val) => map.serialize_entry("uri", val.syntax().text()),
+        }?;
+
+        map.end()
+    }
+}
+
+impl Serialize for SerializeAST<ast::PatEntry> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(None)?;
+        map.serialize_entry("ident", &SerializeAST(&self.0.ident().unwrap()))?;
+
+        if let Some(default) = self.0.default() {
+            map.serialize_entry("default", &SerializeAST(&default))?;
+        }
+
+        map.end()
+    }
+}
+
+impl Serialize for SerializeAST<ast::Param> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        match &self.0 {
+            ast::Param::Pattern(pat) => {
+                let mut map = serializer.serialize_map(None)?;
+                map.serialize_entry("kind", "formals")?;
+
+                map.serialize_entry(
+                    "entries",
+                    &pat.pat_entries().map(SerializeAST).collect::<Vec<_>>(),
+                )?;
+
+                if let Some(bind) = pat.pat_bind() {
+                    map.serialize_entry("bind", &SerializeAST(&bind.ident().unwrap()))?;
+                }
+
+                map.serialize_entry("ellipsis", &pat.ellipsis_token().is_some())?;
+
+                map.end()
+            }
+
+            ast::Param::IdentParam(node) => {
+                Serialize::serialize(&SerializeAST(&node.ident().unwrap()), serializer)
+            }
+        }
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Lambda> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(3))?;
+        map.serialize_entry("kind", "lambda")?;
+        map.serialize_entry("param", &SerializeAST(self.0.param().unwrap()))?;
+        map.serialize_entry("body", &SerializeAST(self.0.body().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::LegacyLet> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(3))?;
+        map.serialize_entry("kind", "legacy_let")?;
+
+        map.serialize_entry(
+            "entries",
+            &self
+                .0
+                .attrpath_values()
+                .map(SerializeAST)
+                .collect::<Vec<_>>(),
+        )?;
+
+        map.serialize_entry(
+            "inherits",
+            &self.0.inherits().map(SerializeAST).collect::<Vec<_>>(),
+        )?;
+
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::LetIn> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(3))?;
+        map.serialize_entry("kind", "let")?;
+
+        map.serialize_entry(
+            "entries",
+            &self
+                .0
+                .attrpath_values()
+                .map(SerializeAST)
+                .collect::<Vec<_>>(),
+        )?;
+
+        map.serialize_entry(
+            "inherits",
+            &self.0.inherits().map(SerializeAST).collect::<Vec<_>>(),
+        )?;
+
+        map.serialize_entry("body", &SerializeAST(&self.0.body().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::List> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let list = self.0.items().map(SerializeAST).collect::<Vec<_>>();
+
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "list")?;
+        map.serialize_entry("items", &list)?;
+
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::BinOp> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(4))?;
+        map.serialize_entry("kind", "binary_op")?;
+
+        map.serialize_entry(
+            "operator",
+            match self.0.operator().unwrap() {
+                ast::BinOpKind::Concat => "concat",
+                ast::BinOpKind::Update => "update",
+                ast::BinOpKind::Add => "add",
+                ast::BinOpKind::Sub => "sub",
+                ast::BinOpKind::Mul => "mul",
+                ast::BinOpKind::Div => "div",
+                ast::BinOpKind::And => "and",
+                ast::BinOpKind::Equal => "equal",
+                ast::BinOpKind::Implication => "implication",
+                ast::BinOpKind::Less => "less",
+                ast::BinOpKind::LessOrEq => "less_or_eq",
+                ast::BinOpKind::More => "more",
+                ast::BinOpKind::MoreOrEq => "more_or_eq",
+                ast::BinOpKind::NotEqual => "not_equal",
+                ast::BinOpKind::Or => "or",
+            },
+        )?;
+
+        map.serialize_entry("lhs", &SerializeAST(&self.0.lhs().unwrap()))?;
+        map.serialize_entry("rhs", &SerializeAST(&self.0.rhs().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Paren> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "paren")?;
+        map.serialize_entry("expr", &SerializeAST(&self.0.expr().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Root> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "root")?;
+        map.serialize_entry("expr", &SerializeAST(&self.0.expr().unwrap()))?;
+        map.end()
+    }
+}
+
+impl Serialize for SerializeAST<ast::AttrpathValue> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("name", &SerializeAST(self.0.attrpath().unwrap()))?;
+        map.serialize_entry("value", &SerializeAST(self.0.value().unwrap()))?;
+        map.end()
+    }
+}
+
+impl Serialize for SerializeAST<ast::Inherit> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(None)?;
+
+        if let Some(from) = self.0.from() {
+            map.serialize_entry("namespace", &SerializeAST(&from.expr().unwrap()))?;
+        }
+
+        map.serialize_entry(
+            "names",
+            &self.0.attrs().map(SerializeAST).collect::<Vec<_>>(),
+        )?;
+
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::AttrSet> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(None)?;
+        map.serialize_entry("kind", "attrset")?;
+        map.serialize_entry("recursive", &self.0.rec_token().is_some())?;
+
+        map.serialize_entry(
+            "entries",
+            &self
+                .0
+                .attrpath_values()
+                .map(SerializeAST)
+                .collect::<Vec<_>>(),
+        )?;
+
+        map.serialize_entry(
+            "inherits",
+            &self.0.inherits().map(SerializeAST).collect::<Vec<_>>(),
+        )?;
+
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::UnaryOp> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(3))?;
+        map.serialize_entry("kind", "unary_op")?;
+
+        map.serialize_entry(
+            "operator",
+            match self.0.operator().unwrap() {
+                ast::UnaryOpKind::Invert => "invert",
+                ast::UnaryOpKind::Negate => "negate",
+            },
+        )?;
+
+        map.serialize_entry("expr", &SerializeAST(&self.0.expr().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Ident> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "ident")?;
+        map.serialize_entry("ident", self.0.ident_token().unwrap().text())?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::With> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(3))?;
+        map.serialize_entry("kind", "with")?;
+        map.serialize_entry("with", &SerializeAST(&self.0.namespace().unwrap()))?;
+        map.serialize_entry("body", &SerializeAST(&self.0.body().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Dynamic> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "dynamic")?;
+        map.serialize_entry("expr", &SerializeAST(&self.0.expr().unwrap()))?;
+        map.end()
+    }
+}
+
+impl Serialize for SerializeAST<ast::Attr> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        match &self.0 {
+            ast::Attr::Ident(ident) => Serialize::serialize(&SerializeAST(ident), serializer),
+            ast::Attr::Dynamic(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Attr::Str(node) => Serialize::serialize(&SerializeAST(node), serializer),
+        }
+    }
+}
+
+impl Serialize for SerializeAST<ast::Attrpath> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(2))?;
+        map.serialize_entry("kind", "attrpath")?;
+
+        map.serialize_entry(
+            "path",
+            &self.0.attrs().map(SerializeAST).collect::<Vec<_>>(),
+        )?;
+
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::HasAttr> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut map = serializer.serialize_map(Some(3))?;
+        map.serialize_entry("kind", "has_attr")?;
+        map.serialize_entry("expr", &SerializeAST(&self.0.expr().unwrap()))?;
+        map.serialize_entry("attrpath", &SerializeAST(self.0.attrpath().unwrap()))?;
+        map.end()
+    }
+}
+
+impl<'a> Serialize for SerializeAST<&'a ast::Expr> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        match self.0 {
+            ast::Expr::Apply(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Assert(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Error(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::IfElse(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Select(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Str(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Path(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Literal(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Lambda(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::LegacyLet(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::LetIn(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::List(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::BinOp(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Paren(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Root(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::AttrSet(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::UnaryOp(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::Ident(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::With(node) => Serialize::serialize(&SerializeAST(node), serializer),
+            ast::Expr::HasAttr(node) => Serialize::serialize(&SerializeAST(node), serializer),
+        }
+    }
+}
+
+impl Serialize for SerializeAST<ast::Expr> {
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializeAST(&self.0).serialize(serializer)
+    }
+}
diff --git a/tvix/eval/src/properties.rs b/tvix/eval/src/properties.rs
new file mode 100644
index 0000000000..45c1cdfce9
--- /dev/null
+++ b/tvix/eval/src/properties.rs
@@ -0,0 +1,164 @@
+//! Macros that generate proptest test suites checking laws of stdlib traits
+
+/// Generate a suite of tests to check the laws of the [`Eq`] impl for the given type
+macro_rules! eq_laws {
+    ($ty: ty) => {
+        eq_laws!(
+            #[strategy(::proptest::arbitrary::any::<$ty>())]
+            $ty,
+            Default::default()
+        );
+    };
+    ($ty: ty, $config: expr) => {
+        eq_laws!(
+            #[strategy(::proptest::arbitrary::any::<$ty>())]
+            $ty,
+            $config
+        );
+    };
+    (#[$meta: meta] $ty: ty, $config: expr) => {
+        #[allow(clippy::eq_op)]
+        mod eq {
+            use test_strategy::proptest;
+
+            use super::*;
+
+            #[proptest($config)]
+            fn reflexive(#[$meta] x: $ty) {
+                assert!(x == x);
+            }
+
+            #[proptest($config)]
+            fn symmetric(#[$meta] x: $ty, #[$meta] y: $ty) {
+                assert_eq!(x == y, y == x);
+            }
+
+            #[proptest($config)]
+            fn transitive(#[$meta] x: $ty, #[$meta] y: $ty, #[$meta] z: $ty) {
+                if x == y && y == z {
+                    assert!(x == z);
+                }
+            }
+        }
+    };
+}
+
+/// Generate a suite of tests to check the laws of the [`Ord`] impl for the given type
+macro_rules! ord_laws {
+    ($ty: ty) => {
+        ord_laws!(
+            #[strategy(::proptest::arbitrary::any::<$ty>())]
+            $ty,
+            Default::default()
+        );
+    };
+    ($ty: ty, $config: expr) => {
+        ord_laws!(
+            #[strategy(::proptest::arbitrary::any::<$ty>())]
+            $ty,
+            $config
+        );
+    };
+    (#[$meta: meta] $ty: ty, $config: expr) => {
+        mod ord {
+            use test_strategy::proptest;
+
+            use super::*;
+
+            #[proptest($config)]
+            fn partial_cmp_matches_cmp(#[$meta] x: $ty, #[$meta] y: $ty) {
+                assert_eq!(x.partial_cmp(&y), Some(x.cmp(&y)));
+            }
+
+            #[proptest($config)]
+            fn dual(#[$meta] x: $ty, #[$meta] y: $ty) {
+                if x < y {
+                    assert!(y > x);
+                }
+                if y < x {
+                    assert!(x > y);
+                }
+            }
+
+            #[proptest($config)]
+            fn le_transitive(#[$meta] x: $ty, #[$meta] y: $ty, #[$meta] z: $ty) {
+                if x < y && y < z {
+                    assert!(x < z)
+                }
+            }
+
+            #[proptest($config)]
+            fn gt_transitive(#[$meta] x: $ty, #[$meta] y: $ty, #[$meta] z: $ty) {
+                if x > y && y > z {
+                    assert!(x > z)
+                }
+            }
+
+            #[proptest($config)]
+            fn trichotomy(#[$meta] x: $ty, #[$meta] y: $ty) {
+                let less = x < y;
+                let greater = x > y;
+                let eq = x == y;
+
+                if less {
+                    assert!(!greater);
+                    assert!(!eq);
+                }
+
+                if greater {
+                    assert!(!less);
+                    assert!(!eq);
+                }
+
+                if eq {
+                    assert!(!less);
+                    assert!(!greater);
+                }
+            }
+        }
+    };
+}
+
+/// Generate a test to check the laws of the [`Hash`] impl for the given type
+macro_rules! hash_laws {
+    ($ty: ty) => {
+        hash_laws!(
+            #[strategy(::proptest::arbitrary::any::<$ty>())]
+            $ty,
+            Default::default()
+        );
+    };
+    ($ty: ty, $config: expr) => {
+        hash_laws!(
+            #[strategy(::proptest::arbitrary::any::<$ty>())]
+            $ty,
+            $config
+        );
+    };
+    (#[$meta: meta] $ty: ty, $config: expr) => {
+        mod hash {
+            use test_strategy::proptest;
+
+            use super::*;
+
+            #[proptest($config)]
+            fn matches_eq(#[$meta] x: $ty, #[$meta] y: $ty) {
+                let hash = |x: &$ty| {
+                    use std::hash::Hasher;
+
+                    let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
+                    x.hash(&mut hasher);
+                    hasher.finish()
+                };
+
+                if x == y {
+                    assert_eq!(hash(&x), hash(&y));
+                }
+            }
+        }
+    };
+}
+
+pub(crate) use eq_laws;
+pub(crate) use hash_laws;
+pub(crate) use ord_laws;
diff --git a/tvix/eval/src/source.rs b/tvix/eval/src/source.rs
new file mode 100644
index 0000000000..5a7f10abb8
--- /dev/null
+++ b/tvix/eval/src/source.rs
@@ -0,0 +1,65 @@
+//! This module contains utilities for dealing with the codemap that
+//! needs to be carried across different compiler instantiations in an
+//! evaluation.
+//!
+//! The data type `SourceCode` should be carried through all relevant
+//! places instead of copying the codemap structures directly.
+
+use std::{
+    cell::{Ref, RefCell, RefMut},
+    rc::Rc,
+    sync::Arc,
+};
+
+use codemap::{CodeMap, Span};
+
+/// Tracks all source code in a Tvix evaluation for accurate error
+/// reporting.
+#[derive(Clone, Debug)]
+pub struct SourceCode(Rc<RefCell<CodeMap>>);
+
+impl SourceCode {
+    /// Access a read-only reference to the codemap.
+    pub fn codemap(&self) -> Ref<CodeMap> {
+        self.0.borrow()
+    }
+
+    /// Access a writable reference to the codemap.
+    fn codemap_mut(&self) -> RefMut<CodeMap> {
+        self.0.borrow_mut()
+    }
+
+    /// Add a file to the codemap. The returned Arc is managed by the
+    /// codemap internally and can be used like a normal reference.
+    pub fn add_file(&self, name: String, code: String) -> Arc<codemap::File> {
+        self.codemap_mut().add_file(name, code)
+    }
+
+    /// Retrieve the line number of the given span. If it spans
+    /// multiple lines, the first line will be returned.
+    pub fn get_line(&self, span: Span) -> usize {
+        // lines are 0-indexed in the codemap, but users probably want
+        // real line numbers
+        self.codemap().look_up_span(span).begin.line + 1
+    }
+
+    /// Returns the literal source slice of the given span.
+    pub fn source_slice(&self, span: Span) -> Ref<str> {
+        Ref::map(self.codemap(), |c| {
+            c.find_file(span.low()).source_slice(span)
+        })
+    }
+
+    /// Returns the reference to the file structure that a given span
+    /// is in.
+    pub fn get_file(&self, span: Span) -> Arc<codemap::File> {
+        self.codemap().look_up_span(span).file
+    }
+}
+
+impl Default for SourceCode {
+    /// Create a new SourceCode instance.
+    fn default() -> Self {
+        Self(Rc::new(RefCell::new(CodeMap::new())))
+    }
+}
diff --git a/tvix/eval/src/spans.rs b/tvix/eval/src/spans.rs
new file mode 100644
index 0000000000..f422093b0d
--- /dev/null
+++ b/tvix/eval/src/spans.rs
@@ -0,0 +1,109 @@
+//! Utilities for dealing with span tracking in the compiler and in
+//! error reporting.
+
+use codemap::{File, Span};
+use rnix::ast;
+use rowan::ast::AstNode;
+
+/// Helper struct to carry information required for making a span, but
+/// without actually performing the (expensive) span lookup.
+///
+/// This is used for tracking spans across thunk boundaries, as they
+/// are frequently instantiated but spans are only used in error or
+/// warning cases.
+#[derive(Clone, Debug)]
+pub enum LightSpan {
+    /// The span has already been computed and can just be used right
+    /// away.
+    Actual { span: Span },
+}
+
+impl LightSpan {
+    pub fn new_actual(span: Span) -> Self {
+        Self::Actual { span }
+    }
+
+    pub fn span(&self) -> Span {
+        match self {
+            LightSpan::Actual { span } => *span,
+        }
+    }
+}
+
+impl From<Span> for LightSpan {
+    fn from(span: Span) -> Self {
+        LightSpan::Actual { span }
+    }
+}
+
+/// Trait implemented by all types from which we can retrieve a span.
+pub trait ToSpan {
+    fn span_for(&self, file: &File) -> Span;
+}
+
+impl ToSpan for Span {
+    fn span_for(&self, _: &File) -> Span {
+        *self
+    }
+}
+
+impl ToSpan for rnix::TextRange {
+    fn span_for(&self, file: &File) -> Span {
+        file.span
+            .subspan(u32::from(self.start()) as u64, u32::from(self.end()) as u64)
+    }
+}
+
+impl ToSpan for rnix::SyntaxToken {
+    fn span_for(&self, file: &File) -> Span {
+        self.text_range().span_for(file)
+    }
+}
+
+impl ToSpan for rnix::SyntaxNode {
+    fn span_for(&self, file: &File) -> Span {
+        self.text_range().span_for(file)
+    }
+}
+
+/// Generates a `ToSpan` implementation for a type implementing
+/// `rowan::AstNode`. This is impossible to do as a blanket
+/// implementation because `rustc` forbids these implementations for
+/// traits from third-party crates due to a belief that semantic
+/// versioning truly could work (it doesn't).
+macro_rules! expr_to_span {
+    ( $type:path ) => {
+        impl ToSpan for $type {
+            fn span_for(&self, file: &File) -> Span {
+                self.syntax().span_for(file)
+            }
+        }
+    };
+}
+
+expr_to_span!(ast::Expr);
+expr_to_span!(ast::Apply);
+expr_to_span!(ast::Assert);
+expr_to_span!(ast::Attr);
+expr_to_span!(ast::AttrSet);
+expr_to_span!(ast::Attrpath);
+expr_to_span!(ast::AttrpathValue);
+expr_to_span!(ast::BinOp);
+expr_to_span!(ast::HasAttr);
+expr_to_span!(ast::Ident);
+expr_to_span!(ast::IdentParam);
+expr_to_span!(ast::IfElse);
+expr_to_span!(ast::Inherit);
+expr_to_span!(ast::Interpol);
+expr_to_span!(ast::Lambda);
+expr_to_span!(ast::LegacyLet);
+expr_to_span!(ast::LetIn);
+expr_to_span!(ast::List);
+expr_to_span!(ast::Literal);
+expr_to_span!(ast::PatBind);
+expr_to_span!(ast::Path);
+expr_to_span!(ast::Pattern);
+expr_to_span!(ast::Select);
+expr_to_span!(ast::Str);
+expr_to_span!(ast::UnaryOp);
+expr_to_span!(ast::With);
diff --git a/tvix/eval/src/systems.rs b/tvix/eval/src/systems.rs
new file mode 100644
index 0000000000..16386cb9e0
--- /dev/null
+++ b/tvix/eval/src/systems.rs
@@ -0,0 +1,351 @@
+/// true iff the argument is recognized by cppnix as the second
+/// coordinate of a "nix double"
+fn is_second_coordinate(x: &str) -> bool {
+    matches!(x, "linux" | "darwin" | "netbsd" | "openbsd" | "freebsd")
+}
+
+/// This function takes an llvm triple (which may have three or four
+/// components, separated by dashes) and returns the "best"
+/// approximation as a nix double, where "best" is currently defined
+/// as "however cppnix handles it".
+pub fn llvm_triple_to_nix_double(llvm_triple: &str) -> String {
+    let parts: Vec<&str> = llvm_triple.split('-').collect();
+    let cpu = match parts[0] {
+        "armv6" => "armv6l", // cppnix appends an "l" to armv6
+        "armv7" => "armv7l", // cppnix appends an "l" to armv7
+        x => match x.as_bytes() {
+            [b'i', _, b'8', b'6'] => "i686", // cppnix glob-matches against i*86
+            _ => x,
+        },
+    };
+    let os = match parts[1..] {
+        [_vendor, kernel, _environment] if is_second_coordinate(kernel) => kernel,
+        [_vendor, kernel] if is_second_coordinate(kernel) => kernel,
+        [kernel, _environment] if is_second_coordinate(kernel) => kernel,
+
+        // Rustc uses wasm32-unknown-unknown, which is rejected by
+        // config.sub, for wasm-in-the-browser environments.  Rustc
+        // should be using wasm32-unknown-none, which config.sub
+        // accepts.  Hopefully the rustc people will change their
+        // triple before stabilising this triple.  In the meantime,
+        // we fix it here in order to unbreak tvixbolt.
+        //
+        // https://doc.rust-lang.org/beta/nightly-rustc/rustc_target/spec/wasm32_unknown_unknown/index.html
+        ["unknown", "unknown"] if cpu == "wasm32" => "none",
+
+        _ => panic!("unrecognized triple {llvm_triple}"),
+    };
+    format!("{cpu}-{os}")
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    #[test]
+    fn test_systems() {
+        assert_eq!(
+            llvm_triple_to_nix_double("aarch64-unknown-linux-gnu"),
+            "aarch64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("i686-unknown-linux-gnu"),
+            "i686-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("x86_64-apple-darwin"),
+            "x86_64-darwin"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("x86_64-unknown-linux-gnu"),
+            "x86_64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("aarch64-apple-darwin"),
+            "aarch64-darwin"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("aarch64-unknown-linux-musl"),
+            "aarch64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("arm-unknown-linux-gnueabi"),
+            "arm-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("arm-unknown-linux-gnueabihf"),
+            "arm-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv7-unknown-linux-gnueabihf"),
+            "armv7l-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips-unknown-linux-gnu"),
+            "mips-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips64-unknown-linux-gnuabi64"),
+            "mips64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips64-unknown-linux-gnuabin32"),
+            "mips64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips64el-unknown-linux-gnuabi64"),
+            "mips64el-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips64el-unknown-linux-gnuabin32"),
+            "mips64el-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mipsel-unknown-linux-gnu"),
+            "mipsel-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("powerpc-unknown-linux-gnu"),
+            "powerpc-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("powerpc64-unknown-linux-gnu"),
+            "powerpc64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("powerpc64le-unknown-linux-gnu"),
+            "powerpc64le-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("s390x-unknown-linux-gnu"),
+            "s390x-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("x86_64-unknown-linux-musl"),
+            "x86_64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("x86_64-unknown-netbsd"),
+            "x86_64-netbsd"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("aarch64-linux-android"),
+            "aarch64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("arm-linux-androideabi"),
+            "arm-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("arm-unknown-linux-musleabi"),
+            "arm-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("arm-unknown-linux-musleabihf"),
+            "arm-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv5te-unknown-linux-gnueabi"),
+            "armv5te-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv5te-unknown-linux-musleabi"),
+            "armv5te-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv7-linux-androideabi"),
+            "armv7l-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv7-unknown-linux-gnueabi"),
+            "armv7l-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv7-unknown-linux-musleabi"),
+            "armv7l-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv7-unknown-linux-musleabihf"),
+            "armv7l-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("i586-unknown-linux-gnu"),
+            "i686-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("i586-unknown-linux-musl"),
+            "i686-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("i686-linux-android"),
+            "i686-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("i686-unknown-linux-musl"),
+            "i686-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips-unknown-linux-musl"),
+            "mips-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips64-unknown-linux-muslabi64"),
+            "mips64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips64el-unknown-linux-muslabi64"),
+            "mips64el-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mipsel-unknown-linux-musl"),
+            "mipsel-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("sparc64-unknown-linux-gnu"),
+            "sparc64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("thumbv7neon-linux-androideabi"),
+            "thumbv7neon-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("thumbv7neon-unknown-linux-gnueabihf"),
+            "thumbv7neon-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("x86_64-linux-android"),
+            "x86_64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("x86_64-unknown-linux-gnux32"),
+            "x86_64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("aarch64-unknown-linux-gnu_ilp32"),
+            "aarch64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("aarch64-unknown-netbsd"),
+            "aarch64-netbsd"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("aarch64_be-unknown-linux-gnu_ilp32"),
+            "aarch64_be-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("aarch64_be-unknown-linux-gnu"),
+            "aarch64_be-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armeb-unknown-linux-gnueabi"),
+            "armeb-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv4t-unknown-linux-gnueabi"),
+            "armv4t-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv6-unknown-netbsd-eabihf"),
+            "armv6l-netbsd"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv7-unknown-linux-uclibceabi"),
+            "armv7l-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv7-unknown-linux-uclibceabihf"),
+            "armv7l-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("armv7-unknown-netbsd-eabihf"),
+            "armv7l-netbsd"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("hexagon-unknown-linux-musl"),
+            "hexagon-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("i686-unknown-netbsd"),
+            "i686-netbsd"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("m68k-unknown-linux-gnu"),
+            "m68k-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips-unknown-linux-uclibc"),
+            "mips-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mips64-openwrt-linux-musl"),
+            "mips64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mipsel-unknown-linux-uclibc"),
+            "mipsel-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mipsisa32r6-unknown-linux-gnu"),
+            "mipsisa32r6-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mipsisa32r6el-unknown-linux-gnu"),
+            "mipsisa32r6el-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mipsisa64r6-unknown-linux-gnuabi64"),
+            "mipsisa64r6-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("mipsisa64r6el-unknown-linux-gnuabi64"),
+            "mipsisa64r6el-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("powerpc-unknown-linux-gnuspe"),
+            "powerpc-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("powerpc-unknown-linux-musl"),
+            "powerpc-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("powerpc-unknown-netbsd"),
+            "powerpc-netbsd"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("powerpc64-unknown-linux-musl"),
+            "powerpc64-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("powerpc64le-unknown-linux-musl"),
+            "powerpc64le-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("riscv32gc-unknown-linux-gnu"),
+            "riscv32gc-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("riscv32gc-unknown-linux-musl"),
+            "riscv32gc-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("riscv64gc-unknown-linux-musl"),
+            "riscv64gc-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("s390x-unknown-linux-musl"),
+            "s390x-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("sparc-unknown-linux-gnu"),
+            "sparc-linux"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("sparc64-unknown-netbsd"),
+            "sparc64-netbsd"
+        );
+        assert_eq!(
+            llvm_triple_to_nix_double("thumbv7neon-unknown-linux-musleabihf"),
+            "thumbv7neon-linux"
+        );
+    }
+}
diff --git a/tvix/eval/src/test_utils.rs b/tvix/eval/src/test_utils.rs
new file mode 100644
index 0000000000..a7d1c3f968
--- /dev/null
+++ b/tvix/eval/src/test_utils.rs
@@ -0,0 +1,8 @@
+use codemap::CodeMap;
+
+/// Create a dummy [`codemap::Span`] for use in tests
+pub(crate) fn dummy_span() -> codemap::Span {
+    let mut codemap = CodeMap::new();
+    let file = codemap.add_file("<dummy>".to_owned(), "<dummy>".to_owned());
+    file.span
+}
diff --git a/tvix/eval/src/tests/mod.rs b/tvix/eval/src/tests/mod.rs
new file mode 100644
index 0000000000..5a7708e298
--- /dev/null
+++ b/tvix/eval/src/tests/mod.rs
@@ -0,0 +1,203 @@
+use crate::{value::Value, EvalIO};
+use builtin_macros::builtins;
+use pretty_assertions::assert_eq;
+use rstest::rstest;
+use std::path::PathBuf;
+
+/// Module for one-off tests which do not follow the rest of the
+/// test layout.
+mod one_offs;
+
+#[builtins]
+mod mock_builtins {
+    //! Builtins which are required by language tests, but should not
+    //! actually exist in //tvix/eval.
+    use crate as tvix_eval;
+    use crate::generators::GenCo;
+    use crate::*;
+    use genawaiter::rc::Gen;
+
+    #[builtin("derivation")]
+    async fn builtin_derivation(co: GenCo, input: Value) -> Result<Value, ErrorKind> {
+        let input = input.to_attrs()?;
+        let attrs = input.update(NixAttrs::from_iter(
+            [
+                (
+                    "outPath",
+                    "/nix/store/00000000000000000000000000000000-mock",
+                ),
+                (
+                    "drvPath",
+                    "/nix/store/00000000000000000000000000000000-mock.drv",
+                ),
+                ("type", "derivation"),
+            ]
+            .into_iter(),
+        ));
+
+        Ok(Value::Attrs(Box::new(attrs)))
+    }
+}
+
+fn eval_test(code_path: PathBuf, expect_success: bool) {
+    std::env::set_var("TEST_VAR", "foo"); // for eval-okay-getenv.nix
+
+    eprintln!("path: {}", code_path.display());
+    assert_eq!(
+        code_path.extension().unwrap(),
+        "nix",
+        "test files always end in .nix"
+    );
+
+    let code = std::fs::read_to_string(&code_path).expect("should be able to read test code");
+
+    let mut eval = crate::Evaluation::new_impure();
+    eval.strict = true;
+    eval.builtins.extend(mock_builtins::builtins());
+
+    let result = eval.evaluate(code, Some(code_path.clone()));
+    let failed = match result.value {
+        Some(Value::Catchable(_)) => true,
+        _ => !result.errors.is_empty(),
+    };
+    if expect_success && failed {
+        panic!(
+            "{}: evaluation of eval-okay test should succeed, but failed with {:?}",
+            code_path.display(),
+            result.errors,
+        );
+    }
+
+    if !expect_success && failed {
+        return;
+    }
+    // !expect_success can also mean the output differs, so don't panic if the
+    // evaluation didn't fail.
+
+    let value = result.value.unwrap();
+    let result_str = value.to_string();
+
+    let exp_path = code_path.with_extension("exp");
+    if exp_path.exists() {
+        // If there's an .exp file provided alongside, compare it with the
+        // output of the NixValue .to_string() method.
+        let exp_str = std::fs::read_to_string(&exp_path).expect("unable to read .exp file");
+
+        if expect_success {
+            assert_eq!(
+                result_str,
+                exp_str.trim(),
+                "{}: result value representation (left) must match expectation (right)",
+                code_path.display()
+            );
+        } else {
+            assert_ne!(
+                result_str,
+                exp_str.trim(),
+                "{}: test passed unexpectedly!  consider moving it out of notyetpassing",
+                code_path.display()
+            );
+
+            // Early return here, we don't compare .xml outputs if this is a !
+            // expect_success test.
+            return;
+        }
+    }
+
+    let exp_xml_path = code_path.with_extension("exp.xml");
+    if exp_xml_path.exists() {
+        // If there's an XML file provided alongside, compare it with the
+        // output produced when serializing the Value as XML.
+        let exp_xml_str = std::fs::read_to_string(exp_xml_path).expect("unable to read .xml file");
+
+        let mut xml_actual_buf = Vec::new();
+        crate::builtins::value_to_xml(&mut xml_actual_buf, &value).expect("value_to_xml failed");
+
+        assert_eq!(
+            String::from_utf8(xml_actual_buf).expect("to_xml produced invalid utf-8"),
+            exp_xml_str,
+            "{}: result value representation (left) must match expectation (right)",
+            code_path.display()
+        );
+    }
+}
+
+// identity-* tests contain Nix code snippets which should evaluate to
+// themselves exactly (i.e. literals).
+#[rstest]
+fn identity(#[files("src/tests/tvix_tests/identity-*.nix")] code_path: PathBuf) {
+    let code = std::fs::read_to_string(code_path).expect("should be able to read test code");
+
+    let mut eval = crate::Evaluation::new(Box::new(crate::StdIO) as Box<dyn EvalIO>, false);
+    eval.strict = true;
+
+    let result = eval.evaluate(&code, None);
+    assert!(
+        result.errors.is_empty(),
+        "evaluation of identity test failed: {:?}",
+        result.errors
+    );
+
+    let result_str = result.value.unwrap().to_string();
+
+    assert_eq!(
+        result_str,
+        code.trim(),
+        "result value representation (left) must match expectation (right)"
+    )
+}
+
+// eval-okay-* tests contain a snippet of Nix code, and an expectation
+// of the produced string output of the evaluator.
+//
+// These evaluations are always supposed to succeed, i.e. all snippets
+// are guaranteed to be valid Nix code.
+#[rstest]
+fn eval_okay(#[files("src/tests/tvix_tests/eval-okay-*.nix")] code_path: PathBuf) {
+    eval_test(code_path, true)
+}
+
+// eval-okay-* tests from the original Nix test suite.
+#[cfg(feature = "nix_tests")]
+#[rstest]
+fn nix_eval_okay(#[files("src/tests/nix_tests/eval-okay-*.nix")] code_path: PathBuf) {
+    eval_test(code_path, true)
+}
+
+// eval-okay-* tests from the original Nix test suite which do not yet pass for tvix
+//
+// Eventually there will be none of these left, and this function
+// will disappear :)
+//
+// Please don't submit failing tests unless they're in
+// notyetpassing; this makes the test suite much more useful for
+// regression testing, since there should always be zero non-ignored
+// failing tests.
+#[rstest]
+fn nix_eval_okay_currently_failing(
+    #[files("src/tests/nix_tests/notyetpassing/eval-okay-*.nix")] code_path: PathBuf,
+) {
+    eval_test(code_path, false)
+}
+
+#[rstest]
+fn eval_okay_currently_failing(
+    #[files("src/tests/tvix_tests/notyetpassing/eval-okay-*.nix")] code_path: PathBuf,
+) {
+    eval_test(code_path, false)
+}
+
+// eval-fail-* tests contain a snippet of Nix code, which is
+// expected to fail evaluation.  The exact type of failure
+// (assertion, parse error, etc) is not currently checked.
+#[rstest]
+fn eval_fail(#[files("src/tests/tvix_tests/eval-fail-*.nix")] code_path: PathBuf) {
+    eval_test(code_path, false)
+}
+
+// eval-fail-* tests from the original Nix test suite.
+#[cfg(feature = "nix_tests")]
+#[rstest]
+fn nix_eval_fail(#[files("src/tests/nix_tests/eval-fail-*.nix")] code_path: PathBuf) {
+    eval_test(code_path, false)
+}
diff --git a/tvix/eval/src/tests/nix_tests/README.md b/tvix/eval/src/tests/nix_tests/README.md
new file mode 100644
index 0000000000..357f3547da
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/README.md
@@ -0,0 +1,8 @@
+These test definitions are taken from the Nix 2.3 code base, they can
+be found upstream at:
+
+  https://github.com/NixOS/nix/tree/2.3.16/tests/lang
+
+These tests follow the licensing directions of Nix 2.3 itself:
+
+  https://github.com/NixOS/nix/blob/2.3.16/COPYING
diff --git a/third_party/nix/src/tests/lang/binary-data b/tvix/eval/src/tests/nix_tests/binary-data
index 06d7405020..06d7405020 100644
--- a/third_party/nix/src/tests/lang/binary-data
+++ b/tvix/eval/src/tests/nix_tests/binary-data
Binary files differdiff --git a/third_party/nix/src/tests/lang/data b/tvix/eval/src/tests/nix_tests/data
index 257cc5642c..257cc5642c 100644
--- a/third_party/nix/src/tests/lang/data
+++ b/tvix/eval/src/tests/nix_tests/data
diff --git a/third_party/nix/src/tests/lang/dir1/a.nix b/tvix/eval/src/tests/nix_tests/dir1/a.nix
index 231f150c57..231f150c57 100644
--- a/third_party/nix/src/tests/lang/dir1/a.nix
+++ b/tvix/eval/src/tests/nix_tests/dir1/a.nix
diff --git a/third_party/nix/src/tests/lang/dir2/a.nix b/tvix/eval/src/tests/nix_tests/dir2/a.nix
index 170df520ab..170df520ab 100644
--- a/third_party/nix/src/tests/lang/dir2/a.nix
+++ b/tvix/eval/src/tests/nix_tests/dir2/a.nix
diff --git a/third_party/nix/src/tests/lang/dir2/b.nix b/tvix/eval/src/tests/nix_tests/dir2/b.nix
index 19010cc35c..19010cc35c 100644
--- a/third_party/nix/src/tests/lang/dir2/b.nix
+++ b/tvix/eval/src/tests/nix_tests/dir2/b.nix
diff --git a/third_party/nix/src/tests/lang/dir3/a.nix b/tvix/eval/src/tests/nix_tests/dir3/a.nix
index 170df520ab..170df520ab 100644
--- a/third_party/nix/src/tests/lang/dir3/a.nix
+++ b/tvix/eval/src/tests/nix_tests/dir3/a.nix
diff --git a/third_party/nix/src/tests/lang/dir3/b.nix b/tvix/eval/src/tests/nix_tests/dir3/b.nix
index 170df520ab..170df520ab 100644
--- a/third_party/nix/src/tests/lang/dir3/b.nix
+++ b/tvix/eval/src/tests/nix_tests/dir3/b.nix
diff --git a/third_party/nix/src/tests/lang/dir3/c.nix b/tvix/eval/src/tests/nix_tests/dir3/c.nix
index cdf158597e..cdf158597e 100644
--- a/third_party/nix/src/tests/lang/dir3/c.nix
+++ b/tvix/eval/src/tests/nix_tests/dir3/c.nix
diff --git a/third_party/nix/src/tests/lang/dir4/a.nix b/tvix/eval/src/tests/nix_tests/dir4/a.nix
index 170df520ab..170df520ab 100644
--- a/third_party/nix/src/tests/lang/dir4/a.nix
+++ b/tvix/eval/src/tests/nix_tests/dir4/a.nix
diff --git a/third_party/nix/src/tests/lang/dir4/c.nix b/tvix/eval/src/tests/nix_tests/dir4/c.nix
index 170df520ab..170df520ab 100644
--- a/third_party/nix/src/tests/lang/dir4/c.nix
+++ b/tvix/eval/src/tests/nix_tests/dir4/c.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-abort.nix b/tvix/eval/src/tests/nix_tests/eval-fail-abort.nix
index 75c51bceb5..75c51bceb5 100644
--- a/third_party/nix/src/tests/lang/eval-fail-abort.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-abort.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-assert.nix b/tvix/eval/src/tests/nix_tests/eval-fail-assert.nix
index 3b7a1e8bf0..3b7a1e8bf0 100644
--- a/third_party/nix/src/tests/lang/eval-fail-assert.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-assert.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-bad-antiquote-1.nix b/tvix/eval/src/tests/nix_tests/eval-fail-bad-antiquote-1.nix
index ffe9c983c2..ffe9c983c2 100644
--- a/third_party/nix/src/tests/lang/eval-fail-bad-antiquote-1.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-bad-antiquote-1.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-bad-antiquote-3.nix b/tvix/eval/src/tests/nix_tests/eval-fail-bad-antiquote-3.nix
index 65b9d4f505..65b9d4f505 100644
--- a/third_party/nix/src/tests/lang/eval-fail-bad-antiquote-3.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-bad-antiquote-3.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-blackhole.nix b/tvix/eval/src/tests/nix_tests/eval-fail-blackhole.nix
index 81133b511c..81133b511c 100644
--- a/third_party/nix/src/tests/lang/eval-fail-blackhole.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-blackhole.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-deepseq.nix b/tvix/eval/src/tests/nix_tests/eval-fail-deepseq.nix
index 9baa49b063..9baa49b063 100644
--- a/third_party/nix/src/tests/lang/eval-fail-deepseq.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-deepseq.nix
diff --git a/tvix/eval/src/tests/nix_tests/eval-fail-foldlStrict-strict-op-application.nix b/tvix/eval/src/tests/nix_tests/eval-fail-foldlStrict-strict-op-application.nix
new file mode 100644
index 0000000000..1620cc76ee
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-foldlStrict-strict-op-application.nix
@@ -0,0 +1,5 @@
+# Tests that the result of applying op is forced even if the value is never used
+builtins.foldl'
+  (_: f: f null)
+  null
+  [ (_: throw "Not the final value, but is still forced!") (_: 23) ]
diff --git a/third_party/nix/src/tests/lang/eval-fail-hashfile-missing.nix b/tvix/eval/src/tests/nix_tests/eval-fail-hashfile-missing.nix
index ce098b8238..ce098b8238 100644
--- a/third_party/nix/src/tests/lang/eval-fail-hashfile-missing.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-hashfile-missing.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-missing-arg.nix b/tvix/eval/src/tests/nix_tests/eval-fail-missing-arg.nix
index c4be9797c5..c4be9797c5 100644
--- a/third_party/nix/src/tests/lang/eval-fail-missing-arg.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-missing-arg.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-path-slash.nix b/tvix/eval/src/tests/nix_tests/eval-fail-path-slash.nix
index 8c2e104c78..8c2e104c78 100644
--- a/third_party/nix/src/tests/lang/parse-fail-path-slash.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-path-slash.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-remove.nix b/tvix/eval/src/tests/nix_tests/eval-fail-remove.nix
index 539e0eb0a6..539e0eb0a6 100644
--- a/third_party/nix/src/tests/lang/eval-fail-remove.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-remove.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-seq.nix b/tvix/eval/src/tests/nix_tests/eval-fail-seq.nix
index cddbbfd326..cddbbfd326 100644
--- a/third_party/nix/src/tests/lang/eval-fail-seq.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-seq.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-substring.nix b/tvix/eval/src/tests/nix_tests/eval-fail-substring.nix
index f37c2bc0a1..f37c2bc0a1 100644
--- a/third_party/nix/src/tests/lang/eval-fail-substring.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-substring.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-to-path.nix b/tvix/eval/src/tests/nix_tests/eval-fail-to-path.nix
index 5e322bc313..5e322bc313 100644
--- a/third_party/nix/src/tests/lang/eval-fail-to-path.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-fail-to-path.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-any-all.exp b/tvix/eval/src/tests/nix_tests/eval-okay-any-all.exp
index eb273f45b2..eb273f45b2 100644
--- a/third_party/nix/src/tests/lang/eval-okay-any-all.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-any-all.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-any-all.nix b/tvix/eval/src/tests/nix_tests/eval-okay-any-all.nix
index a3f26ea2aa..a3f26ea2aa 100644
--- a/third_party/nix/src/tests/lang/eval-okay-any-all.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-any-all.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-arithmetic.exp b/tvix/eval/src/tests/nix_tests/eval-okay-arithmetic.exp
index 5c54d10b7b..5c54d10b7b 100644
--- a/third_party/nix/src/tests/lang/eval-okay-arithmetic.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-arithmetic.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-arithmetic.nix b/tvix/eval/src/tests/nix_tests/eval-okay-arithmetic.nix
index 7e9e6a0b66..7e9e6a0b66 100644
--- a/third_party/nix/src/tests/lang/eval-okay-arithmetic.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-arithmetic.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrnames.exp b/tvix/eval/src/tests/nix_tests/eval-okay-attrnames.exp
index b4aa387e07..b4aa387e07 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrnames.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrnames.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrnames.nix b/tvix/eval/src/tests/nix_tests/eval-okay-attrnames.nix
index e5b26e9f2e..e5b26e9f2e 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrnames.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrnames.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs.exp b/tvix/eval/src/tests/nix_tests/eval-okay-attrs.exp
index 45b0f829eb..45b0f829eb 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs.nix b/tvix/eval/src/tests/nix_tests/eval-okay-attrs.nix
index 810b31a5da..810b31a5da 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs2.exp b/tvix/eval/src/tests/nix_tests/eval-okay-attrs2.exp
index 45b0f829eb..45b0f829eb 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs2.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs2.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs2.nix b/tvix/eval/src/tests/nix_tests/eval-okay-attrs2.nix
index 9e06b83ac1..9e06b83ac1 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs2.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs2.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs3.exp b/tvix/eval/src/tests/nix_tests/eval-okay-attrs3.exp
index 19de4fdf79..19de4fdf79 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs3.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs3.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs3.nix b/tvix/eval/src/tests/nix_tests/eval-okay-attrs3.nix
index f29de11fe6..f29de11fe6 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs3.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs3.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs4.exp b/tvix/eval/src/tests/nix_tests/eval-okay-attrs4.exp
index 1851731442..1851731442 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs4.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs4.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs4.nix b/tvix/eval/src/tests/nix_tests/eval-okay-attrs4.nix
index 43ec81210f..43ec81210f 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs4.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs4.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs5.exp b/tvix/eval/src/tests/nix_tests/eval-okay-attrs5.exp
index ce0430d780..ce0430d780 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs5.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs5.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-attrs5.nix b/tvix/eval/src/tests/nix_tests/eval-okay-attrs5.nix
index a4584cd3b3..a4584cd3b3 100644
--- a/third_party/nix/src/tests/lang/eval-okay-attrs5.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-attrs5.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-backslash-newline-1.exp b/tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-1.exp
index 3e754364cc..3e754364cc 100644
--- a/third_party/nix/src/tests/lang/eval-okay-backslash-newline-1.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-1.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-backslash-newline-1.nix b/tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-1.nix
index 7fef3dddd4..7fef3dddd4 100644
--- a/third_party/nix/src/tests/lang/eval-okay-backslash-newline-1.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-1.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-backslash-newline-2.exp b/tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-2.exp
index 3e754364cc..3e754364cc 100644
--- a/third_party/nix/src/tests/lang/eval-okay-backslash-newline-2.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-2.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-backslash-newline-2.nix b/tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-2.nix
index 35ddf495c6..35ddf495c6 100644
--- a/third_party/nix/src/tests/lang/eval-okay-backslash-newline-2.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-backslash-newline-2.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-builtins-add.exp b/tvix/eval/src/tests/nix_tests/eval-okay-builtins-add.exp
index 0350b518a7..0350b518a7 100644
--- a/third_party/nix/src/tests/lang/eval-okay-builtins-add.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-builtins-add.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-builtins-add.nix b/tvix/eval/src/tests/nix_tests/eval-okay-builtins-add.nix
index c841816222..c841816222 100644
--- a/third_party/nix/src/tests/lang/eval-okay-builtins-add.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-builtins-add.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-builtins.exp b/tvix/eval/src/tests/nix_tests/eval-okay-builtins.exp
index 0661686d61..0661686d61 100644
--- a/third_party/nix/src/tests/lang/eval-okay-builtins.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-builtins.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-builtins.nix b/tvix/eval/src/tests/nix_tests/eval-okay-builtins.nix
index e9d65e88a8..e9d65e88a8 100644
--- a/third_party/nix/src/tests/lang/eval-okay-builtins.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-builtins.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-callable-attrs.exp b/tvix/eval/src/tests/nix_tests/eval-okay-callable-attrs.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-callable-attrs.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-callable-attrs.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-callable-attrs.nix b/tvix/eval/src/tests/nix_tests/eval-okay-callable-attrs.nix
index 310a030df0..310a030df0 100644
--- a/third_party/nix/src/tests/lang/eval-okay-callable-attrs.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-callable-attrs.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-catattrs.exp b/tvix/eval/src/tests/nix_tests/eval-okay-catattrs.exp
index b4a1e66d6b..b4a1e66d6b 100644
--- a/third_party/nix/src/tests/lang/eval-okay-catattrs.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-catattrs.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-catattrs.nix b/tvix/eval/src/tests/nix_tests/eval-okay-catattrs.nix
index 2c3dc10da5..2c3dc10da5 100644
--- a/third_party/nix/src/tests/lang/eval-okay-catattrs.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-catattrs.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-closure.exp b/tvix/eval/src/tests/nix_tests/eval-okay-closure.exp
index e7dbf97816..e7dbf97816 100644
--- a/third_party/nix/src/tests/lang/eval-okay-closure.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-closure.exp
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-closure.exp.xml b/tvix/eval/src/tests/nix_tests/eval-okay-closure.exp.xml
new file mode 100644
index 0000000000..dffc03a998
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-closure.exp.xml
@@ -0,0 +1,343 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <list>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-13" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-12" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-11" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-9" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-8" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-7" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-5" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-4" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-3" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="-1" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="0" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="1" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="2" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="4" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="5" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="6" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="8" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="9" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="10" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="13" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="14" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="15" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="17" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="18" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="19" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="22" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="23" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="26" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="27" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="28" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="31" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="32" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="35" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="36" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="40" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="41" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="44" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="45" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="49" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="53" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="54" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="58" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="62" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="67" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="71" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="80" />
+      </attr>
+    </attrs>
+  </list>
+</expr>
diff --git a/third_party/nix/src/tests/lang/eval-okay-closure.nix b/tvix/eval/src/tests/nix_tests/eval-okay-closure.nix
index cccd4dc357..cccd4dc357 100644
--- a/third_party/nix/src/tests/lang/eval-okay-closure.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-closure.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-comments.exp b/tvix/eval/src/tests/nix_tests/eval-okay-comments.exp
index 7182dc2f9b..7182dc2f9b 100644
--- a/third_party/nix/src/tests/lang/eval-okay-comments.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-comments.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-comments.nix b/tvix/eval/src/tests/nix_tests/eval-okay-comments.nix
index cb2cce2180..cb2cce2180 100644
--- a/third_party/nix/src/tests/lang/eval-okay-comments.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-comments.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-concat.exp b/tvix/eval/src/tests/nix_tests/eval-okay-concat.exp
index bb4bbd5774..bb4bbd5774 100644
--- a/third_party/nix/src/tests/lang/eval-okay-concat.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-concat.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-concat.nix b/tvix/eval/src/tests/nix_tests/eval-okay-concat.nix
index d158a9bf05..d158a9bf05 100644
--- a/third_party/nix/src/tests/lang/eval-okay-concat.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-concat.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-concatmap.exp b/tvix/eval/src/tests/nix_tests/eval-okay-concatmap.exp
index 3b8be7739d..3b8be7739d 100644
--- a/third_party/nix/src/tests/lang/eval-okay-concatmap.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-concatmap.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-concatmap.nix b/tvix/eval/src/tests/nix_tests/eval-okay-concatmap.nix
index 97da5d37a4..97da5d37a4 100644
--- a/third_party/nix/src/tests/lang/eval-okay-concatmap.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-concatmap.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-concatstringssep.exp b/tvix/eval/src/tests/nix_tests/eval-okay-concatstringssep.exp
index 93987647ff..93987647ff 100644
--- a/third_party/nix/src/tests/lang/eval-okay-concatstringssep.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-concatstringssep.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-concatstringssep.nix b/tvix/eval/src/tests/nix_tests/eval-okay-concatstringssep.nix
index adc4c41bd5..adc4c41bd5 100644
--- a/third_party/nix/src/tests/lang/eval-okay-concatstringssep.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-concatstringssep.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-deepseq.exp b/tvix/eval/src/tests/nix_tests/eval-okay-deepseq.exp
index 8d38505c16..8d38505c16 100644
--- a/third_party/nix/src/tests/lang/eval-okay-deepseq.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-deepseq.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-deepseq.nix b/tvix/eval/src/tests/nix_tests/eval-okay-deepseq.nix
index 53aa4b1dc2..53aa4b1dc2 100644
--- a/third_party/nix/src/tests/lang/eval-okay-deepseq.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-deepseq.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-delayed-with-inherit.exp b/tvix/eval/src/tests/nix_tests/eval-okay-delayed-with-inherit.exp
index eaacb55c1a..eaacb55c1a 100644
--- a/third_party/nix/src/tests/lang/eval-okay-delayed-with-inherit.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-delayed-with-inherit.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-delayed-with-inherit.nix b/tvix/eval/src/tests/nix_tests/eval-okay-delayed-with-inherit.nix
index 84b388c271..84b388c271 100644
--- a/third_party/nix/src/tests/lang/eval-okay-delayed-with-inherit.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-delayed-with-inherit.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-2.exp b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-2.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-2.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-2.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-2.nix b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-2.nix
index 6d57bf8549..6d57bf8549 100644
--- a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-2.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-2.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-bare.exp b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-bare.exp
index df8750afc0..df8750afc0 100644
--- a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-bare.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-bare.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-bare.nix b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-bare.nix
index 0dbe15e638..0dbe15e638 100644
--- a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs-bare.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs-bare.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs.exp b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs.exp
index df8750afc0..df8750afc0 100644
--- a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs.nix b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs.nix
index ee02ac7e65..ee02ac7e65 100644
--- a/third_party/nix/src/tests/lang/eval-okay-dynamic-attrs.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-dynamic-attrs.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-elem.exp b/tvix/eval/src/tests/nix_tests/eval-okay-elem.exp
index 3cf6c0e962..3cf6c0e962 100644
--- a/third_party/nix/src/tests/lang/eval-okay-elem.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-elem.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-elem.nix b/tvix/eval/src/tests/nix_tests/eval-okay-elem.nix
index 71ea7a4ed0..71ea7a4ed0 100644
--- a/third_party/nix/src/tests/lang/eval-okay-elem.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-elem.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-empty-args.exp b/tvix/eval/src/tests/nix_tests/eval-okay-empty-args.exp
index cb5537d5d7..cb5537d5d7 100644
--- a/third_party/nix/src/tests/lang/eval-okay-empty-args.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-empty-args.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-empty-args.nix b/tvix/eval/src/tests/nix_tests/eval-okay-empty-args.nix
index 78c133afdd..78c133afdd 100644
--- a/third_party/nix/src/tests/lang/eval-okay-empty-args.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-empty-args.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-eq.exp b/tvix/eval/src/tests/nix_tests/eval-okay-eq.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-eq.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-eq.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-eq.nix b/tvix/eval/src/tests/nix_tests/eval-okay-eq.nix
index 73d200b381..73d200b381 100644
--- a/third_party/nix/src/tests/lang/eval-okay-eq.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-eq.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-filter.exp b/tvix/eval/src/tests/nix_tests/eval-okay-filter.exp
index 355d51c27d..355d51c27d 100644
--- a/third_party/nix/src/tests/lang/eval-okay-filter.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-filter.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-filter.nix b/tvix/eval/src/tests/nix_tests/eval-okay-filter.nix
index 85109b0d0e..85109b0d0e 100644
--- a/third_party/nix/src/tests/lang/eval-okay-filter.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-filter.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-flatten.exp b/tvix/eval/src/tests/nix_tests/eval-okay-flatten.exp
index b979b2b8b9..b979b2b8b9 100644
--- a/third_party/nix/src/tests/lang/eval-okay-flatten.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-flatten.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-flatten.nix b/tvix/eval/src/tests/nix_tests/eval-okay-flatten.nix
index fe911e9683..fe911e9683 100644
--- a/third_party/nix/src/tests/lang/eval-okay-flatten.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-flatten.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-float.exp b/tvix/eval/src/tests/nix_tests/eval-okay-float.exp
index 3c50a8adce..3c50a8adce 100644
--- a/third_party/nix/src/tests/lang/eval-okay-float.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-float.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-float.nix b/tvix/eval/src/tests/nix_tests/eval-okay-float.nix
index b2702c7b16..b2702c7b16 100644
--- a/third_party/nix/src/tests/lang/eval-okay-float.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-float.nix
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-elements.exp b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-elements.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-elements.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-elements.nix b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-elements.nix
new file mode 100644
index 0000000000..c666e07f3a
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-elements.nix
@@ -0,0 +1,9 @@
+# Tests that the rhs argument of op is not forced unconditionally
+let
+  lst = builtins.foldl'
+    (acc: x: acc ++ [ x ])
+    [ ]
+    [ 42 (throw "this shouldn't be evaluated") ];
+in
+
+builtins.head lst
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.exp b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.nix b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.nix
new file mode 100644
index 0000000000..abcd5366ab
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.nix
@@ -0,0 +1,6 @@
+# Checks that the nul value for the accumulator is not forced unconditionally.
+# Some languages provide a foldl' that is strict in this argument, but Nix does not.
+builtins.foldl'
+  (_: x: x)
+  (throw "This is never forced")
+  [ "but the results of applying op are" 42 ]
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict.exp b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict.exp
new file mode 100644
index 0000000000..837e12b406
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict.exp
@@ -0,0 +1 @@
+500500
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict.nix b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict.nix
new file mode 100644
index 0000000000..3b87188d24
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-foldlStrict.nix
@@ -0,0 +1,3 @@
+with import ./lib.nix;
+
+builtins.foldl' (x: y: x + y) 0 (range 1 1000)
diff --git a/third_party/nix/src/tests/lang/eval-okay-fromTOML.exp b/tvix/eval/src/tests/nix_tests/eval-okay-fromTOML.exp
index d0dd3af2c8..d0dd3af2c8 100644
--- a/third_party/nix/src/tests/lang/eval-okay-fromTOML.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-fromTOML.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-fromTOML.nix b/tvix/eval/src/tests/nix_tests/eval-okay-fromTOML.nix
index 9639326899..9639326899 100644
--- a/third_party/nix/src/tests/lang/eval-okay-fromTOML.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-fromTOML.nix
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-fromjson-escapes.exp b/tvix/eval/src/tests/nix_tests/eval-okay-fromjson-escapes.exp
new file mode 100644
index 0000000000..add5505a82
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-fromjson-escapes.exp
@@ -0,0 +1 @@
+"quote \" reverse solidus \\ solidus / backspace  formfeed  newline \n carriage return \r horizontal tab \t 1 char unicode encoded backspace  1 char unicode encoded e with accent Γ© 2 char unicode encoded s with caron Ε‘ 3 char unicode encoded rightwards arrow β†’"
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-fromjson-escapes.nix b/tvix/eval/src/tests/nix_tests/eval-okay-fromjson-escapes.nix
new file mode 100644
index 0000000000..f007135077
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-fromjson-escapes.nix
@@ -0,0 +1,3 @@
+# This string contains all supported escapes in a JSON string, per json.org
+# \b and \f are not supported by Nix
+builtins.fromJSON ''"quote \" reverse solidus \\ solidus \/ backspace \b formfeed \f newline \n carriage return \r horizontal tab \t 1 char unicode encoded backspace \u0008 1 char unicode encoded e with accent \u00e9 2 char unicode encoded s with caron \u0161 3 char unicode encoded rightwards arrow \u2192"''
diff --git a/third_party/nix/src/tests/lang/eval-okay-fromjson.exp b/tvix/eval/src/tests/nix_tests/eval-okay-fromjson.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-fromjson.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-fromjson.exp
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-fromjson.nix b/tvix/eval/src/tests/nix_tests/eval-okay-fromjson.nix
new file mode 100644
index 0000000000..e1c0f86cc4
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-fromjson.nix
@@ -0,0 +1,35 @@
+builtins.fromJSON
+  ''
+    {
+      "Video": {
+          "Title":  "The Penguin Chronicles",
+          "Width":  1920,
+          "Height": 1080,
+          "EmbeddedData": [3.14159, 23493,null, true  ,false, -10],
+          "Thumb": {
+              "Url":    "http://www.example.com/video/5678931",
+              "Width":  200,
+              "Height": 250
+          },
+          "Subtitle" : false,
+          "Latitude":  46.2051,
+          "Longitude": 6.0723
+        }
+    }
+  ''
+==
+  { Video =
+    { Title = "The Penguin Chronicles";
+      Width = 1920;
+      Height = 1080;
+      EmbeddedData = [ 3.14159 23493 null true false (0-10) ];
+      Thumb =
+        { Url = "http://www.example.com/video/5678931";
+          Width = 200;
+          Height = 250;
+        };
+      Subtitle = false;
+      Latitude = 46.2051;
+      Longitude = 6.0723;
+    };
+  }
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-functionargs.exp b/tvix/eval/src/tests/nix_tests/eval-okay-functionargs.exp
new file mode 100644
index 0000000000..c1c9f8ffaf
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-functionargs.exp
@@ -0,0 +1 @@
+[ "stdenv" "fetchurl" "aterm-stdenv" "aterm-stdenv2" "libX11" "libXv" "mplayer-stdenv2.libXv-libX11" "mplayer-stdenv2.libXv-libX11_2" "nix-stdenv-aterm-stdenv" "nix-stdenv2-aterm2-stdenv2" ]
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-functionargs.exp.xml b/tvix/eval/src/tests/nix_tests/eval-okay-functionargs.exp.xml
new file mode 100644
index 0000000000..651f54c363
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-functionargs.exp.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <list>
+    <string value="stdenv" />
+    <string value="fetchurl" />
+    <string value="aterm-stdenv" />
+    <string value="aterm-stdenv2" />
+    <string value="libX11" />
+    <string value="libXv" />
+    <string value="mplayer-stdenv2.libXv-libX11" />
+    <string value="mplayer-stdenv2.libXv-libX11_2" />
+    <string value="nix-stdenv-aterm-stdenv" />
+    <string value="nix-stdenv2-aterm2-stdenv2" />
+  </list>
+</expr>
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-functionargs.nix b/tvix/eval/src/tests/nix_tests/eval-okay-functionargs.nix
new file mode 100644
index 0000000000..68dca62ee1
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-functionargs.nix
@@ -0,0 +1,80 @@
+let
+
+  stdenvFun = { }: { name = "stdenv"; };
+  stdenv2Fun = { }: { name = "stdenv2"; };
+  fetchurlFun = { stdenv }: assert stdenv.name == "stdenv"; { name = "fetchurl"; };
+  atermFun = { stdenv, fetchurl }: { name = "aterm-${stdenv.name}"; };
+  aterm2Fun = { stdenv, fetchurl }: { name = "aterm2-${stdenv.name}"; };
+  nixFun = { stdenv, fetchurl, aterm }: { name = "nix-${stdenv.name}-${aterm.name}"; };
+  
+  mplayerFun =
+    { stdenv, fetchurl, enableX11 ? false, xorg ? null, enableFoo ? true, foo ? null  }:
+    assert stdenv.name == "stdenv2";
+    assert enableX11 -> xorg.libXv.name == "libXv";
+    assert enableFoo -> foo != null;
+    { name = "mplayer-${stdenv.name}.${xorg.libXv.name}-${xorg.libX11.name}"; };
+
+  makeOverridable = f: origArgs: f origArgs //
+    { override = newArgs:
+        makeOverridable f (origArgs // (if builtins.isFunction newArgs then newArgs origArgs else newArgs));
+    };
+    
+  callPackage_ = pkgs: f: args:
+    makeOverridable f ((builtins.intersectAttrs (builtins.functionArgs f) pkgs) // args);
+
+  allPackages =
+    { overrides ? (pkgs: pkgsPrev: { }) }:
+    let
+      callPackage = callPackage_ pkgs;
+      pkgs = pkgsStd // (overrides pkgs pkgsStd);
+      pkgsStd = {
+        inherit pkgs;
+        stdenv = callPackage stdenvFun { };
+        stdenv2 = callPackage stdenv2Fun { };
+        fetchurl = callPackage fetchurlFun { };
+        aterm = callPackage atermFun { };
+        xorg = callPackage xorgFun { };
+        mplayer = callPackage mplayerFun { stdenv = pkgs.stdenv2; enableFoo = false; };
+        nix = callPackage nixFun { };
+      };
+    in pkgs;
+
+  libX11Fun = { stdenv, fetchurl }: { name = "libX11"; };
+  libX11_2Fun = { stdenv, fetchurl }: { name = "libX11_2"; };
+  libXvFun = { stdenv, fetchurl, libX11 }: { name = "libXv"; };
+  
+  xorgFun =
+    { pkgs }:
+    let callPackage = callPackage_ (pkgs // pkgs.xorg); in
+    {
+      libX11 = callPackage libX11Fun { };
+      libXv = callPackage libXvFun { };
+    };
+
+in
+
+let
+
+  pkgs = allPackages { };
+  
+  pkgs2 = allPackages {
+    overrides = pkgs: pkgsPrev: {
+      stdenv = pkgs.stdenv2;
+      nix = pkgsPrev.nix.override { aterm = aterm2Fun { inherit (pkgs) stdenv fetchurl; }; };
+      xorg = pkgsPrev.xorg // { libX11 = libX11_2Fun { inherit (pkgs) stdenv fetchurl; }; };
+    };
+  };
+  
+in
+
+  [ pkgs.stdenv.name
+    pkgs.fetchurl.name
+    pkgs.aterm.name
+    pkgs2.aterm.name
+    pkgs.xorg.libX11.name
+    pkgs.xorg.libXv.name
+    pkgs.mplayer.name
+    pkgs2.mplayer.name
+    pkgs.nix.name
+    pkgs2.nix.name
+  ]
diff --git a/third_party/nix/src/tests/lang/eval-okay-getenv.exp b/tvix/eval/src/tests/nix_tests/eval-okay-getenv.exp
index 14e24d4190..14e24d4190 100644
--- a/third_party/nix/src/tests/lang/eval-okay-getenv.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-getenv.exp
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-getenv.nix b/tvix/eval/src/tests/nix_tests/eval-okay-getenv.nix
new file mode 100644
index 0000000000..4cfec5f553
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-getenv.nix
@@ -0,0 +1 @@
+builtins.getEnv "TEST_VAR" + (if builtins.getEnv "NO_SUCH_VAR" == "" then "bar" else "bla")
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp
new file mode 100644
index 0000000000..bfca5652a5
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp
@@ -0,0 +1 @@
+{ "1" = [ 9 ]; "2" = [ 8 ]; "3" = [ 13 29 ]; "4" = [ 3 4 10 11 17 18 ]; "5" = [ 0 23 26 28 ]; "6" = [ 1 12 21 27 30 ]; "7" = [ 7 22 ]; "8" = [ 14 ]; "9" = [ 19 ]; b = [ 16 25 ]; c = [ 24 ]; d = [ 2 ]; e = [ 5 6 15 31 ]; f = [ 20 ]; }
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix
new file mode 100644
index 0000000000..862d89dbd6
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix
@@ -0,0 +1,5 @@
+with import ./lib.nix;
+
+builtins.groupBy (n:
+  builtins.substring 0 1 (builtins.hashString "sha256" (toString n))
+) (range 0 31)
diff --git a/third_party/nix/src/tests/lang/eval-okay-hash.exp b/tvix/eval/src/tests/nix_tests/eval-okay-hash.exp
index e69de29bb2..e69de29bb2 100644
--- a/third_party/nix/src/tests/lang/eval-okay-hash.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-hash.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-hashfile.exp b/tvix/eval/src/tests/nix_tests/eval-okay-hashfile.exp
index ff1e8293ef..ff1e8293ef 100644
--- a/third_party/nix/src/tests/lang/eval-okay-hashfile.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-hashfile.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-hashfile.nix b/tvix/eval/src/tests/nix_tests/eval-okay-hashfile.nix
index aff5a18568..aff5a18568 100644
--- a/third_party/nix/src/tests/lang/eval-okay-hashfile.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-hashfile.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-hashstring.exp b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.exp
index d720a082dd..d720a082dd 100644
--- a/third_party/nix/src/tests/lang/eval-okay-hashstring.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-hashstring.nix b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.nix
index b0f62b245c..b0f62b245c 100644
--- a/third_party/nix/src/tests/lang/eval-okay-hashstring.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-if.exp b/tvix/eval/src/tests/nix_tests/eval-okay-if.exp
index 00750edc07..00750edc07 100644
--- a/third_party/nix/src/tests/lang/eval-okay-if.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-if.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-if.nix b/tvix/eval/src/tests/nix_tests/eval-okay-if.nix
index 23e4c74d50..23e4c74d50 100644
--- a/third_party/nix/src/tests/lang/eval-okay-if.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-if.nix
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-ind-string.exp b/tvix/eval/src/tests/nix_tests/eval-okay-ind-string.exp
new file mode 100644
index 0000000000..7862331fa5
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-ind-string.exp
@@ -0,0 +1 @@
+"This is an indented multi-line string\nliteral.  An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed.  Thus,\nin this case four spaces will be\nstripped from each line, even though\n  THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n  followed by a newline, it's stripped, but\n  that's not the case here. Two spaces are\n  stripped because of the \"  \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n  The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', \${.\n   Tabs are not interpreted as whitespace (since we can't guess\n   what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n   space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored.  But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n  Similarly you can force an indentation level,\n  in this case to 2 spaces.  This works because the anti-quote\n  is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n  rm -f /var/run/opengl-driver\n  ln -sf 123 /var/run/opengl-driver\n\n  rm -f /var/log/slim.log\n   \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf  \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin         \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/          # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: \${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\ncut -d $'\\t' -f 1\nending dollar $$\n"
diff --git a/third_party/nix/src/tests/lang/eval-okay-ind-string.nix b/tvix/eval/src/tests/nix_tests/eval-okay-ind-string.nix
index 1669dc0648..95d59b5083 100644
--- a/third_party/nix/src/tests/lang/eval-okay-ind-string.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-ind-string.nix
@@ -110,7 +110,7 @@ let
     And finally to interpret \n etc. as in a string: ''\n, ''\r, ''\t.
   '';
 
-  # Regression test: antiquotation in '${x}' should work, but didn't.
+  # Regression test: string interpolation in '${x}' should work, but didn't.
   s15 = let x = "bla"; in ''
     foo
     '${x}'
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-intersectAttrs.exp b/tvix/eval/src/tests/nix_tests/eval-okay-intersectAttrs.exp
new file mode 100644
index 0000000000..50445bc0ee
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-intersectAttrs.exp
@@ -0,0 +1 @@
+[ { } { a = 1; } { a = 1; } { a = "a"; } { m = 1; } { m = "m"; } { n = 1; } { n = "n"; } { n = 1; p = 2; } { n = "n"; p = "p"; } { n = 1; p = 2; } { n = "n"; p = "p"; } { a = "a"; b = "b"; c = "c"; d = "d"; e = "e"; f = "f"; g = "g"; h = "h"; i = "i"; j = "j"; k = "k"; l = "l"; m = "m"; n = "n"; o = "o"; p = "p"; q = "q"; r = "r"; s = "s"; t = "t"; u = "u"; v = "v"; w = "w"; x = "x"; y = "y"; z = "z"; } true ]
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-intersectAttrs.nix b/tvix/eval/src/tests/nix_tests/eval-okay-intersectAttrs.nix
new file mode 100644
index 0000000000..39d49938cc
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-intersectAttrs.nix
@@ -0,0 +1,50 @@
+let
+  alphabet =
+  { a = "a";
+    b = "b";
+    c = "c";
+    d = "d";
+    e = "e";
+    f = "f";
+    g = "g";
+    h = "h";
+    i = "i";
+    j = "j";
+    k = "k";
+    l = "l";
+    m = "m";
+    n = "n";
+    o = "o";
+    p = "p";
+    q = "q";
+    r = "r";
+    s = "s";
+    t = "t";
+    u = "u";
+    v = "v";
+    w = "w";
+    x = "x";
+    y = "y";
+    z = "z";
+  };
+  foo = {
+    inherit (alphabet) f o b a r z q u x;
+    aa = throw "aa";
+  };
+  alphabetFail = builtins.mapAttrs throw alphabet;
+in
+[ (builtins.intersectAttrs { a = abort "l1"; } { b = abort "r1"; })
+  (builtins.intersectAttrs { a = abort "l2"; } { a = 1; })
+  (builtins.intersectAttrs alphabetFail { a = 1; })
+  (builtins.intersectAttrs  { a = abort "laa"; } alphabet)
+  (builtins.intersectAttrs alphabetFail { m = 1; })
+  (builtins.intersectAttrs  { m = abort "lam"; } alphabet)
+  (builtins.intersectAttrs alphabetFail { n = 1; })
+  (builtins.intersectAttrs  { n = abort "lan"; } alphabet)
+  (builtins.intersectAttrs alphabetFail { n = 1; p = 2; })
+  (builtins.intersectAttrs  { n = abort "lan2"; p = abort "lap"; } alphabet)
+  (builtins.intersectAttrs alphabetFail { n = 1; p = 2; })
+  (builtins.intersectAttrs  { n = abort "lan2"; p = abort "lap"; } alphabet)
+  (builtins.intersectAttrs alphabetFail alphabet)
+  (builtins.intersectAttrs alphabet foo == builtins.intersectAttrs foo alphabet)
+]
diff --git a/third_party/nix/src/tests/lang/eval-okay-let.exp b/tvix/eval/src/tests/nix_tests/eval-okay-let.exp
index 14e24d4190..14e24d4190 100644
--- a/third_party/nix/src/tests/lang/eval-okay-let.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-let.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-let.nix b/tvix/eval/src/tests/nix_tests/eval-okay-let.nix
index fe118c5282..fe118c5282 100644
--- a/third_party/nix/src/tests/lang/eval-okay-let.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-let.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-list.exp b/tvix/eval/src/tests/nix_tests/eval-okay-list.exp
index f784f26d83..f784f26d83 100644
--- a/third_party/nix/src/tests/lang/eval-okay-list.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-list.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-list.nix b/tvix/eval/src/tests/nix_tests/eval-okay-list.nix
index d433bcf908..d433bcf908 100644
--- a/third_party/nix/src/tests/lang/eval-okay-list.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-list.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-listtoattrs.exp b/tvix/eval/src/tests/nix_tests/eval-okay-listtoattrs.exp
index 74abef7bc6..74abef7bc6 100644
--- a/third_party/nix/src/tests/lang/eval-okay-listtoattrs.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-listtoattrs.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-listtoattrs.nix b/tvix/eval/src/tests/nix_tests/eval-okay-listtoattrs.nix
index 4186e029b5..4186e029b5 100644
--- a/third_party/nix/src/tests/lang/eval-okay-listtoattrs.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-listtoattrs.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-logic.exp b/tvix/eval/src/tests/nix_tests/eval-okay-logic.exp
index d00491fd7e..d00491fd7e 100644
--- a/third_party/nix/src/tests/lang/eval-okay-logic.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-logic.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-logic.nix b/tvix/eval/src/tests/nix_tests/eval-okay-logic.nix
index fbb1279440..fbb1279440 100644
--- a/third_party/nix/src/tests/lang/eval-okay-logic.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-logic.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-map.exp b/tvix/eval/src/tests/nix_tests/eval-okay-map.exp
index dbb64f717b..dbb64f717b 100644
--- a/third_party/nix/src/tests/lang/eval-okay-map.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-map.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-map.nix b/tvix/eval/src/tests/nix_tests/eval-okay-map.nix
index a76c1d8114..a76c1d8114 100644
--- a/third_party/nix/src/tests/lang/eval-okay-map.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-map.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-mapattrs.exp b/tvix/eval/src/tests/nix_tests/eval-okay-mapattrs.exp
index 3f113f17ba..3f113f17ba 100644
--- a/third_party/nix/src/tests/lang/eval-okay-mapattrs.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-mapattrs.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-mapattrs.nix b/tvix/eval/src/tests/nix_tests/eval-okay-mapattrs.nix
index f075b6275e..f075b6275e 100644
--- a/third_party/nix/src/tests/lang/eval-okay-mapattrs.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-mapattrs.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-nested-with.exp b/tvix/eval/src/tests/nix_tests/eval-okay-nested-with.exp
index 0cfbf08886..0cfbf08886 100644
--- a/third_party/nix/src/tests/lang/eval-okay-nested-with.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-nested-with.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-nested-with.nix b/tvix/eval/src/tests/nix_tests/eval-okay-nested-with.nix
index ba9d79aa79..ba9d79aa79 100644
--- a/third_party/nix/src/tests/lang/eval-okay-nested-with.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-nested-with.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-new-let.exp b/tvix/eval/src/tests/nix_tests/eval-okay-new-let.exp
index f98b388071..f98b388071 100644
--- a/third_party/nix/src/tests/lang/eval-okay-new-let.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-new-let.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-new-let.nix b/tvix/eval/src/tests/nix_tests/eval-okay-new-let.nix
index 7381231415..7381231415 100644
--- a/third_party/nix/src/tests/lang/eval-okay-new-let.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-new-let.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-null-dynamic-attrs.exp b/tvix/eval/src/tests/nix_tests/eval-okay-null-dynamic-attrs.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-null-dynamic-attrs.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-null-dynamic-attrs.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-null-dynamic-attrs.nix b/tvix/eval/src/tests/nix_tests/eval-okay-null-dynamic-attrs.nix
index b060c0bc98..b060c0bc98 100644
--- a/third_party/nix/src/tests/lang/eval-okay-null-dynamic-attrs.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-null-dynamic-attrs.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-partition.exp b/tvix/eval/src/tests/nix_tests/eval-okay-partition.exp
index cd8b8b020c..cd8b8b020c 100644
--- a/third_party/nix/src/tests/lang/eval-okay-partition.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-partition.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-partition.nix b/tvix/eval/src/tests/nix_tests/eval-okay-partition.nix
index 846d2ce494..846d2ce494 100644
--- a/third_party/nix/src/tests/lang/eval-okay-partition.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-partition.nix
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-path.exp b/tvix/eval/src/tests/nix_tests/eval-okay-path.exp
new file mode 100644
index 0000000000..3ce7f82830
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-path.exp
@@ -0,0 +1 @@
+"/nix/store/ya937r4ydw0l6kayq8jkyqaips9c75jm-output"
diff --git a/third_party/nix/src/tests/lang/eval-okay-pathexists.exp b/tvix/eval/src/tests/nix_tests/eval-okay-pathexists.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-pathexists.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-pathexists.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-pathexists.nix b/tvix/eval/src/tests/nix_tests/eval-okay-pathexists.nix
index 50c28ee0cd..50c28ee0cd 100644
--- a/third_party/nix/src/tests/lang/eval-okay-pathexists.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-pathexists.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-patterns.exp b/tvix/eval/src/tests/nix_tests/eval-okay-patterns.exp
index a4304010fe..a4304010fe 100644
--- a/third_party/nix/src/tests/lang/eval-okay-patterns.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-patterns.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-patterns.nix b/tvix/eval/src/tests/nix_tests/eval-okay-patterns.nix
index 96fd25a015..96fd25a015 100644
--- a/third_party/nix/src/tests/lang/eval-okay-patterns.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-patterns.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-readfile.exp b/tvix/eval/src/tests/nix_tests/eval-okay-readfile.exp
index a2c87d0c43..a2c87d0c43 100644
--- a/third_party/nix/src/tests/lang/eval-okay-readfile.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-readfile.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-readfile.nix b/tvix/eval/src/tests/nix_tests/eval-okay-readfile.nix
index 82f7cb1743..82f7cb1743 100644
--- a/third_party/nix/src/tests/lang/eval-okay-readfile.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-readfile.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-redefine-builtin.exp b/tvix/eval/src/tests/nix_tests/eval-okay-redefine-builtin.exp
index c508d5366f..c508d5366f 100644
--- a/third_party/nix/src/tests/lang/eval-okay-redefine-builtin.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-redefine-builtin.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-redefine-builtin.nix b/tvix/eval/src/tests/nix_tests/eval-okay-redefine-builtin.nix
index df9fc3f37d..df9fc3f37d 100644
--- a/third_party/nix/src/tests/lang/eval-okay-redefine-builtin.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-redefine-builtin.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-regex-match.exp b/tvix/eval/src/tests/nix_tests/eval-okay-regex-match.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-regex-match.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-regex-match.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-regex-match.nix b/tvix/eval/src/tests/nix_tests/eval-okay-regex-match.nix
index 273e259071..273e259071 100644
--- a/third_party/nix/src/tests/lang/eval-okay-regex-match.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-regex-match.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-regex-split.exp b/tvix/eval/src/tests/nix_tests/eval-okay-regex-split.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-regex-split.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-regex-split.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-regex-split.nix b/tvix/eval/src/tests/nix_tests/eval-okay-regex-split.nix
index 0073e05778..0073e05778 100644
--- a/third_party/nix/src/tests/lang/eval-okay-regex-split.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-regex-split.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-1.exp b/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220122.exp
index 00750edc07..00750edc07 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-1.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220122.exp
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220122.nix b/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220122.nix
new file mode 100644
index 0000000000..694e9a13b7
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220122.nix
@@ -0,0 +1 @@
+((_: _) 1) + ((__: __) 2)
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220125.exp b/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220125.exp
new file mode 100644
index 0000000000..00750edc07
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220125.exp
@@ -0,0 +1 @@
+3
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220125.nix b/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220125.nix
new file mode 100644
index 0000000000..4855023739
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-regression-20220125.nix
@@ -0,0 +1,2 @@
+((__curPosFoo: __curPosFoo) 1) + ((__curPosBar: __curPosBar) 2)
+
diff --git a/third_party/nix/src/tests/lang/eval-okay-remove.exp b/tvix/eval/src/tests/nix_tests/eval-okay-remove.exp
index 8d38505c16..8d38505c16 100644
--- a/third_party/nix/src/tests/lang/eval-okay-remove.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-remove.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-remove.nix b/tvix/eval/src/tests/nix_tests/eval-okay-remove.nix
index 4ad5ba897f..4ad5ba897f 100644
--- a/third_party/nix/src/tests/lang/eval-okay-remove.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-remove.nix
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-scope-1.exp b/tvix/eval/src/tests/nix_tests/eval-okay-scope-1.exp
new file mode 100644
index 0000000000..00750edc07
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-1.exp
@@ -0,0 +1 @@
+3
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-1.nix b/tvix/eval/src/tests/nix_tests/eval-okay-scope-1.nix
index fa38a7174e..fa38a7174e 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-1.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-1.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-2.exp b/tvix/eval/src/tests/nix_tests/eval-okay-scope-2.exp
index d00491fd7e..d00491fd7e 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-2.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-2.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-2.nix b/tvix/eval/src/tests/nix_tests/eval-okay-scope-2.nix
index eb8b02bc49..eb8b02bc49 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-2.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-2.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-3.exp b/tvix/eval/src/tests/nix_tests/eval-okay-scope-3.exp
index b8626c4cff..b8626c4cff 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-3.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-3.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-3.nix b/tvix/eval/src/tests/nix_tests/eval-okay-scope-3.nix
index 10d6bc04d8..10d6bc04d8 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-3.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-3.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-4.exp b/tvix/eval/src/tests/nix_tests/eval-okay-scope-4.exp
index 00ff03a46c..00ff03a46c 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-4.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-4.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-4.nix b/tvix/eval/src/tests/nix_tests/eval-okay-scope-4.nix
index dc8243bc85..dc8243bc85 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-4.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-4.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-6.exp b/tvix/eval/src/tests/nix_tests/eval-okay-scope-6.exp
index 00ff03a46c..00ff03a46c 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-6.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-6.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-6.nix b/tvix/eval/src/tests/nix_tests/eval-okay-scope-6.nix
index 0995d4e7e7..0995d4e7e7 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-6.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-6.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-7.exp b/tvix/eval/src/tests/nix_tests/eval-okay-scope-7.exp
index d00491fd7e..d00491fd7e 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-7.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-7.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-scope-7.nix b/tvix/eval/src/tests/nix_tests/eval-okay-scope-7.nix
index 4da02968f6..4da02968f6 100644
--- a/third_party/nix/src/tests/lang/eval-okay-scope-7.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-scope-7.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-seq.exp b/tvix/eval/src/tests/nix_tests/eval-okay-seq.exp
index 0cfbf08886..0cfbf08886 100644
--- a/third_party/nix/src/tests/lang/eval-okay-seq.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-seq.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-seq.nix b/tvix/eval/src/tests/nix_tests/eval-okay-seq.nix
index 0a9a21c03b..0a9a21c03b 100644
--- a/third_party/nix/src/tests/lang/eval-okay-seq.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-seq.nix
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-sort.exp b/tvix/eval/src/tests/nix_tests/eval-okay-sort.exp
new file mode 100644
index 0000000000..899119e20e
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-sort.exp
@@ -0,0 +1 @@
+[ [ 42 77 147 249 483 526 ] [ 526 483 249 147 77 42 ] [ "bar" "fnord" "foo" "xyzzy" ] [ { key = 1; value = "foo"; } { key = 1; value = "fnord"; } { key = 2; value = "bar"; } ] [ [ ] [ ] [ 1 ] [ 1 4 ] [ 1 5 ] [ 1 6 ] [ 2 ] [ 2 3 ] [ 3 ] [ 3 ] ] ]
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-sort.nix b/tvix/eval/src/tests/nix_tests/eval-okay-sort.nix
new file mode 100644
index 0000000000..50aa78e403
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-sort.nix
@@ -0,0 +1,20 @@
+with builtins;
+
+[ (sort lessThan [ 483 249 526 147 42 77 ])
+  (sort (x: y: y < x) [ 483 249 526 147 42 77 ])
+  (sort lessThan [ "foo" "bar" "xyzzy" "fnord" ])
+  (sort (x: y: x.key < y.key)
+    [ { key = 1; value = "foo"; } { key = 2; value = "bar"; } { key = 1; value = "fnord"; } ])
+  (sort lessThan [
+    [ 1 6 ]
+    [ ]
+    [ 2 3 ]
+    [ 3 ]
+    [ 1 5 ]
+    [ 2 ]
+    [ 1 ]
+    [ ]
+    [ 1 4 ]
+    [ 3 ]
+  ])
+]
diff --git a/third_party/nix/src/tests/lang/eval-okay-splitversion.exp b/tvix/eval/src/tests/nix_tests/eval-okay-splitversion.exp
index 153ceb8186..153ceb8186 100644
--- a/third_party/nix/src/tests/lang/eval-okay-splitversion.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-splitversion.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-splitversion.nix b/tvix/eval/src/tests/nix_tests/eval-okay-splitversion.nix
index 9e5c99d2e7..9e5c99d2e7 100644
--- a/third_party/nix/src/tests/lang/eval-okay-splitversion.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-splitversion.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-string.exp b/tvix/eval/src/tests/nix_tests/eval-okay-string.exp
index 63f650f73a..63f650f73a 100644
--- a/third_party/nix/src/tests/lang/eval-okay-string.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-string.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-string.nix b/tvix/eval/src/tests/nix_tests/eval-okay-string.nix
index 47cc989ad4..47cc989ad4 100644
--- a/third_party/nix/src/tests/lang/eval-okay-string.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-string.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-strings-as-attrs-names.exp b/tvix/eval/src/tests/nix_tests/eval-okay-strings-as-attrs-names.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-strings-as-attrs-names.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-strings-as-attrs-names.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-strings-as-attrs-names.nix b/tvix/eval/src/tests/nix_tests/eval-okay-strings-as-attrs-names.nix
index 5e40928dbe..5e40928dbe 100644
--- a/third_party/nix/src/tests/lang/eval-okay-strings-as-attrs-names.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-strings-as-attrs-names.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-substring.exp b/tvix/eval/src/tests/nix_tests/eval-okay-substring.exp
index 6aace04b0f..6aace04b0f 100644
--- a/third_party/nix/src/tests/lang/eval-okay-substring.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-substring.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-substring.nix b/tvix/eval/src/tests/nix_tests/eval-okay-substring.nix
index 424af00d9b..424af00d9b 100644
--- a/third_party/nix/src/tests/lang/eval-okay-substring.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-substring.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-tail-call-1.exp-disabled b/tvix/eval/src/tests/nix_tests/eval-okay-tail-call-1.exp
index f7393e847d..f7393e847d 100644
--- a/third_party/nix/src/tests/lang/eval-okay-tail-call-1.exp-disabled
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-tail-call-1.exp
diff --git a/third_party/nix/src/tests/lang/disabled/eval-okay-tail-call-1.nix b/tvix/eval/src/tests/nix_tests/eval-okay-tail-call-1.nix
index a3962ce3fd..a3962ce3fd 100644
--- a/third_party/nix/src/tests/lang/disabled/eval-okay-tail-call-1.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-tail-call-1.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-tojson.exp b/tvix/eval/src/tests/nix_tests/eval-okay-tojson.exp
index e92aae3235..e92aae3235 100644
--- a/third_party/nix/src/tests/lang/eval-okay-tojson.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-tojson.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-tojson.nix b/tvix/eval/src/tests/nix_tests/eval-okay-tojson.nix
index ce67943bea..ce67943bea 100644
--- a/third_party/nix/src/tests/lang/eval-okay-tojson.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-tojson.nix
diff --git a/third_party/nix/src/tests/lang/evalstore-okay-toxml.exp b/tvix/eval/src/tests/nix_tests/eval-okay-toxml.exp
index 828220890e..828220890e 100644
--- a/third_party/nix/src/tests/lang/evalstore-okay-toxml.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-toxml.exp
diff --git a/third_party/nix/src/tests/lang/evalstore-okay-toxml.nix b/tvix/eval/src/tests/nix_tests/eval-okay-toxml.nix
index 068c97a6c1..068c97a6c1 100644
--- a/third_party/nix/src/tests/lang/evalstore-okay-toxml.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-toxml.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-toxml2.exp b/tvix/eval/src/tests/nix_tests/eval-okay-toxml2.exp
index 634a841eb1..634a841eb1 100644
--- a/third_party/nix/src/tests/lang/eval-okay-toxml2.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-toxml2.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-toxml2.nix b/tvix/eval/src/tests/nix_tests/eval-okay-toxml2.nix
index ff1791b30e..ff1791b30e 100644
--- a/third_party/nix/src/tests/lang/eval-okay-toxml2.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-toxml2.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-tryeval.exp b/tvix/eval/src/tests/nix_tests/eval-okay-tryeval.exp
index 2b2e6fa711..2b2e6fa711 100644
--- a/third_party/nix/src/tests/lang/eval-okay-tryeval.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-tryeval.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-tryeval.nix b/tvix/eval/src/tests/nix_tests/eval-okay-tryeval.nix
index 629bc440a8..629bc440a8 100644
--- a/third_party/nix/src/tests/lang/eval-okay-tryeval.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-tryeval.nix
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-types.exp b/tvix/eval/src/tests/nix_tests/eval-okay-types.exp
new file mode 100644
index 0000000000..92a1532993
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-types.exp
@@ -0,0 +1 @@
+[ true false true false true false true false true true true true true true true true true true true false true true true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]
diff --git a/third_party/nix/src/tests/lang/eval-okay-types.nix b/tvix/eval/src/tests/nix_tests/eval-okay-types.nix
index cc51d8cb7a..9b58be5d1d 100644
--- a/third_party/nix/src/tests/lang/eval-okay-types.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-types.nix
@@ -3,7 +3,6 @@ with builtins;
 [ (isNull null)
   (isNull (x: x))
   (isFunction (x: x))
-  (isFunction functionArgs)
   (isFunction "fnord")
   (isString ("foo" + "bar"))
   (isString [ "x" ])
diff --git a/third_party/nix/src/tests/lang/eval-okay-versions.exp b/tvix/eval/src/tests/nix_tests/eval-okay-versions.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/eval-okay-versions.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-versions.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-versions.nix b/tvix/eval/src/tests/nix_tests/eval-okay-versions.nix
index e63c36586b..e9111f5f43 100644
--- a/third_party/nix/src/tests/lang/eval-okay-versions.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-versions.nix
@@ -4,6 +4,7 @@ let
   name2 = "hello";
   name3 = "915resolution-0.5.2";
   name4 = "xf86-video-i810-1.7.4";
+  name5 = "name-that-ends-with-dash--1.0";
 
   eq = 0;
   lt = builtins.sub 0 1;
@@ -23,6 +24,8 @@ let
     ((builtins.parseDrvName name3).version == "0.5.2")
     ((builtins.parseDrvName name4).name == "xf86-video-i810")
     ((builtins.parseDrvName name4).version == "1.7.4")
+    ((builtins.parseDrvName name5).name == "name-that-ends-with-dash")
+    ((builtins.parseDrvName name5).version == "-1.0")
     (versionTest "1.0" "2.3" lt)
     (versionTest "2.1" "2.3" lt)
     (versionTest "2.3" "2.3" eq)
diff --git a/third_party/nix/src/tests/lang/eval-okay-with.exp b/tvix/eval/src/tests/nix_tests/eval-okay-with.exp
index 378c8dc804..378c8dc804 100644
--- a/third_party/nix/src/tests/lang/eval-okay-with.exp
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-with.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-with.nix b/tvix/eval/src/tests/nix_tests/eval-okay-with.nix
index 033e8d3aba..033e8d3aba 100644
--- a/third_party/nix/src/tests/lang/eval-okay-with.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-with.nix
diff --git a/tvix/eval/src/tests/nix_tests/eval-okay-xml.exp.xml b/tvix/eval/src/tests/nix_tests/eval-okay-xml.exp.xml
new file mode 100644
index 0000000000..20099326cc
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-xml.exp.xml
@@ -0,0 +1,52 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <attrs>
+    <attr name="a">
+      <string value="foo" />
+    </attr>
+    <attr name="at">
+      <function>
+        <attrspat name="args">
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="b">
+      <string value="bar" />
+    </attr>
+    <attr name="c">
+      <string value="foobar" />
+    </attr>
+    <attr name="ellipsis">
+      <function>
+        <attrspat ellipsis="1">
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="f">
+      <function>
+        <attrspat>
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="id">
+      <function>
+        <varpat name="x" />
+      </function>
+    </attr>
+    <attr name="x">
+      <int value="123" />
+    </attr>
+    <attr name="y">
+      <float value="567.89" />
+    </attr>
+  </attrs>
+</expr>
diff --git a/third_party/nix/src/tests/lang/disabled/eval-okay-xml.nix b/tvix/eval/src/tests/nix_tests/eval-okay-xml.nix
index 9ee9f8a0b4..9ee9f8a0b4 100644
--- a/third_party/nix/src/tests/lang/disabled/eval-okay-xml.nix
+++ b/tvix/eval/src/tests/nix_tests/eval-okay-xml.nix
diff --git a/third_party/nix/src/tests/lang/imported.nix b/tvix/eval/src/tests/nix_tests/imported.nix
index fb39ee4efa..fb39ee4efa 100644
--- a/third_party/nix/src/tests/lang/imported.nix
+++ b/tvix/eval/src/tests/nix_tests/imported.nix
diff --git a/third_party/nix/src/tests/lang/imported2.nix b/tvix/eval/src/tests/nix_tests/imported2.nix
index 6d0a2992b7..6d0a2992b7 100644
--- a/third_party/nix/src/tests/lang/imported2.nix
+++ b/tvix/eval/src/tests/nix_tests/imported2.nix
diff --git a/third_party/nix/src/tests/lang/lib.nix b/tvix/eval/src/tests/nix_tests/lib.nix
index 028a538314..028a538314 100644
--- a/third_party/nix/src/tests/lang/lib.nix
+++ b/tvix/eval/src/tests/nix_tests/lib.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-bad-antiquote-2.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-bad-antiquote-2.nix
index 3745235ce9..3745235ce9 100644
--- a/third_party/nix/src/tests/lang/eval-fail-bad-antiquote-2.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-bad-antiquote-2.nix
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-fromTOML-timestamps.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-fromTOML-timestamps.nix
new file mode 100644
index 0000000000..74cff9470a
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-fromTOML-timestamps.nix
@@ -0,0 +1,130 @@
+builtins.fromTOML ''
+  key = "value"
+  bare_key = "value"
+  bare-key = "value"
+  1234 = "value"
+
+  "127.0.0.1" = "value"
+  "character encoding" = "value"
+  "ʎǝʞ" = "value"
+  'key2' = "value"
+  'quoted "value"' = "value"
+
+  name = "Orange"
+
+  physical.color = "orange"
+  physical.shape = "round"
+  site."google.com" = true
+
+  # This is legal according to the spec, but cpptoml doesn't handle it.
+  #a.b.c = 1
+  #a.d = 2
+
+  str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
+
+  int1 = +99
+  int2 = 42
+  int3 = 0
+  int4 = -17
+  int5 = 1_000
+  int6 = 5_349_221
+  int7 = 1_2_3_4_5
+
+  hex1 = 0xDEADBEEF
+  hex2 = 0xdeadbeef
+  hex3 = 0xdead_beef
+
+  oct1 = 0o01234567
+  oct2 = 0o755
+
+  bin1 = 0b11010110
+
+  flt1 = +1.0
+  flt2 = 3.1415
+  flt3 = -0.01
+  flt4 = 5e+22
+  flt5 = 1e6
+  flt6 = -2E-2
+  flt7 = 6.626e-34
+  flt8 = 9_224_617.445_991_228_313
+
+  bool1 = true
+  bool2 = false
+
+  odt1 = 1979-05-27T07:32:00Z
+  odt2 = 1979-05-27T00:32:00-07:00
+  odt3 = 1979-05-27T00:32:00.999999-07:00
+  odt4 = 1979-05-27 07:32:00Z
+  ldt1 = 1979-05-27T07:32:00
+  ldt2 = 1979-05-27T00:32:00.999999
+  ld1 = 1979-05-27
+  lt1 = 07:32:00
+  lt2 = 00:32:00.999999
+
+  arr1 = [ 1, 2, 3 ]
+  arr2 = [ "red", "yellow", "green" ]
+  arr3 = [ [ 1, 2 ], [3, 4, 5] ]
+  arr4 = [ "all", 'strings', """are the same""", ''''type'''']
+  arr5 = [ [ 1, 2 ], ["a", "b", "c"] ]
+
+  arr7 = [
+    1, 2, 3
+  ]
+
+  arr8 = [
+    1,
+    2, # this is ok
+  ]
+
+  [table-1]
+  key1 = "some string"
+  key2 = 123
+
+
+  [table-2]
+  key1 = "another string"
+  key2 = 456
+
+  [dog."tater.man"]
+  type.name = "pug"
+
+  [a.b.c]
+  [ d.e.f ]
+  [ g .  h  . i ]
+  [ j . "ʞ" . 'l' ]
+  [x.y.z.w]
+
+  name = { first = "Tom", last = "Preston-Werner" }
+  point = { x = 1, y = 2 }
+  animal = { type.name = "pug" }
+
+  [[products]]
+  name = "Hammer"
+  sku = 738594937
+
+  [[products]]
+
+  [[products]]
+  name = "Nail"
+  sku = 284758393
+  color = "gray"
+
+  [[fruit]]
+    name = "apple"
+
+    [fruit.physical]
+      color = "red"
+      shape = "round"
+
+    [[fruit.variety]]
+      name = "red delicious"
+
+    [[fruit.variety]]
+      name = "granny smith"
+
+  [[fruit]]
+    name = "banana"
+
+    [[fruit.variety]]
+      name = "plantain"
+''
diff --git a/third_party/nix/src/tests/lang/eval-fail-antiquoted-path.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-nonexist-path.nix
index f2f08107b5..f2f08107b5 100644
--- a/third_party/nix/src/tests/lang/eval-fail-antiquoted-path.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-nonexist-path.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-scope-5.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-scope-5.nix
index f89a65a99b..f89a65a99b 100644
--- a/third_party/nix/src/tests/lang/eval-fail-scope-5.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-scope-5.nix
diff --git a/third_party/nix/src/tests/lang/eval-fail-undeclared-arg.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-undeclared-arg.nix
index cafdf16362..cafdf16362 100644
--- a/third_party/nix/src/tests/lang/eval-fail-undeclared-arg.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-fail-undeclared-arg.nix
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-attrs6.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-attrs6.exp
new file mode 100644
index 0000000000..b46938032e
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-attrs6.exp
@@ -0,0 +1 @@
+{ __overrides = { bar = "qux"; }; bar = "qux"; foo = "bar"; }
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-attrs6.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-attrs6.nix
new file mode 100644
index 0000000000..2e5c85483b
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-attrs6.nix
@@ -0,0 +1,4 @@
+rec {
+  "${"foo"}" = "bar";
+   __overrides = { bar = "qux"; };
+}
diff --git a/third_party/nix/src/tests/lang/evalstore-okay-autoargs.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.exp
index 7a8391786a..7a8391786a 100644
--- a/third_party/nix/src/tests/lang/evalstore-okay-autoargs.exp
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.exp
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.flags b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.flags
new file mode 100644
index 0000000000..217c7a5ae2
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.flags
@@ -0,0 +1 @@
+--arg lib import(nix_tests/lib.nix) --argstr xyzzy xyzzy! -A result
diff --git a/third_party/nix/src/tests/lang/evalargs-okay-autoargs.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.nix
index 815f51b1d6..815f51b1d6 100644
--- a/third_party/nix/src/tests/lang/evalargs-okay-autoargs.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-autoargs.nix
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context-introspection.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context-introspection.exp
new file mode 100644
index 0000000000..03b400cc88
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context-introspection.exp
@@ -0,0 +1 @@
+[ true true true true true true ]
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context-introspection.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context-introspection.nix
new file mode 100644
index 0000000000..50a78d946e
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context-introspection.nix
@@ -0,0 +1,41 @@
+let
+  drv = derivation {
+    name = "fail";
+    builder = "/bin/false";
+    system = "x86_64-linux";
+    outputs = [ "out" "foo" ];
+  };
+
+  path = "${./eval-okay-context-introspection.nix}";
+
+  desired-context = {
+    "${builtins.unsafeDiscardStringContext path}" = {
+      path = true;
+    };
+    "${builtins.unsafeDiscardStringContext drv.drvPath}" = {
+      outputs = [ "foo" "out" ];
+      allOutputs = true;
+    };
+  };
+
+  combo-path = "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
+  legit-context = builtins.getContext combo-path;
+
+  reconstructed-path = builtins.appendContext
+    (builtins.unsafeDiscardStringContext combo-path)
+    desired-context;
+
+  # Eta rule for strings with context.
+  etaRule = str:
+    str == builtins.appendContext
+      (builtins.unsafeDiscardStringContext str)
+      (builtins.getContext str);
+
+in [
+  (legit-context == desired-context)
+  (reconstructed-path == combo-path)
+  (etaRule "foo")
+  (etaRule drv.drvPath)
+  (etaRule drv.foo.outPath)
+  (etaRule (builtins.unsafeDiscardOutputDependency drv.drvPath))
+]
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context.exp
new file mode 100644
index 0000000000..2f535bdbc4
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context.exp
@@ -0,0 +1 @@
+"foo eval-okay-context.nix bar"
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context.nix
new file mode 100644
index 0000000000..7b9531cfe9
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-context.nix
@@ -0,0 +1,6 @@
+let s = "foo ${builtins.substring 33 100 (baseNameOf "${./eval-okay-context.nix}")} bar";
+in
+  if s != "foo eval-okay-context.nix bar"
+  then abort "context not discarded"
+  else builtins.unsafeDiscardStringContext s
+
diff --git a/third_party/nix/src/tests/lang/eval-okay-curpos.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-curpos.exp
index 65fd65b4d0..65fd65b4d0 100644
--- a/third_party/nix/src/tests/lang/eval-okay-curpos.exp
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-curpos.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-curpos.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-curpos.nix
index b79553df0b..b79553df0b 100644
--- a/third_party/nix/src/tests/lang/eval-okay-curpos.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-curpos.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-delayed-with.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-delayed-with.exp
index 8e7c61ab8e..8e7c61ab8e 100644
--- a/third_party/nix/src/tests/lang/eval-okay-delayed-with.exp
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-delayed-with.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-delayed-with.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-delayed-with.nix
index 3fb023e1cd..3fb023e1cd 100644
--- a/third_party/nix/src/tests/lang/eval-okay-delayed-with.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-delayed-with.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-eq-derivations.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-eq-derivations.exp
index ec04aab6ae..ec04aab6ae 100644
--- a/third_party/nix/src/tests/lang/eval-okay-eq-derivations.exp
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-eq-derivations.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-eq-derivations.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-eq-derivations.nix
index d526cb4a21..d526cb4a21 100644
--- a/third_party/nix/src/tests/lang/eval-okay-eq-derivations.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-eq-derivations.nix
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-floor-ceil.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-floor-ceil.exp
new file mode 100644
index 0000000000..81f80420b9
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-floor-ceil.exp
@@ -0,0 +1 @@
+"23;24;23;23"
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-floor-ceil.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-floor-ceil.nix
new file mode 100644
index 0000000000..d76a0d86ea
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-floor-ceil.nix
@@ -0,0 +1,9 @@
+with import ./lib.nix;
+
+let
+  n1 = builtins.floor 23.5;
+  n2 = builtins.ceil 23.5;
+  n3 = builtins.floor 23;
+  n4 = builtins.ceil 23;
+in
+  builtins.concatStringsSep ";" (map toString [ n1 n2 n3 n4 ])
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.exp
new file mode 100644
index 0000000000..08b3c69a6c
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.exp
@@ -0,0 +1 @@
+{ "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; ld1 = { _type = "timestamp"; value = "1979-05-27"; }; ldt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00"; }; ldt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999"; }; lt1 = { _type = "timestamp"; value = "07:32:00"; }; lt2 = { _type = "timestamp"; value = "00:32:00.999999"; }; name = "Orange"; oct1 = 342391; oct2 = 493; odt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00-07:00"; }; odt3 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999-07:00"; }; odt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; }
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.flags b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.flags
new file mode 100644
index 0000000000..9ed39dc6ba
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.flags
@@ -0,0 +1 @@
+--extra-experimental-features parse-toml-timestamps
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.nix
new file mode 100644
index 0000000000..74cff9470a
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-fromTOML-timestamps.nix
@@ -0,0 +1,130 @@
+builtins.fromTOML ''
+  key = "value"
+  bare_key = "value"
+  bare-key = "value"
+  1234 = "value"
+
+  "127.0.0.1" = "value"
+  "character encoding" = "value"
+  "ʎǝʞ" = "value"
+  'key2' = "value"
+  'quoted "value"' = "value"
+
+  name = "Orange"
+
+  physical.color = "orange"
+  physical.shape = "round"
+  site."google.com" = true
+
+  # This is legal according to the spec, but cpptoml doesn't handle it.
+  #a.b.c = 1
+  #a.d = 2
+
+  str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
+
+  int1 = +99
+  int2 = 42
+  int3 = 0
+  int4 = -17
+  int5 = 1_000
+  int6 = 5_349_221
+  int7 = 1_2_3_4_5
+
+  hex1 = 0xDEADBEEF
+  hex2 = 0xdeadbeef
+  hex3 = 0xdead_beef
+
+  oct1 = 0o01234567
+  oct2 = 0o755
+
+  bin1 = 0b11010110
+
+  flt1 = +1.0
+  flt2 = 3.1415
+  flt3 = -0.01
+  flt4 = 5e+22
+  flt5 = 1e6
+  flt6 = -2E-2
+  flt7 = 6.626e-34
+  flt8 = 9_224_617.445_991_228_313
+
+  bool1 = true
+  bool2 = false
+
+  odt1 = 1979-05-27T07:32:00Z
+  odt2 = 1979-05-27T00:32:00-07:00
+  odt3 = 1979-05-27T00:32:00.999999-07:00
+  odt4 = 1979-05-27 07:32:00Z
+  ldt1 = 1979-05-27T07:32:00
+  ldt2 = 1979-05-27T00:32:00.999999
+  ld1 = 1979-05-27
+  lt1 = 07:32:00
+  lt2 = 00:32:00.999999
+
+  arr1 = [ 1, 2, 3 ]
+  arr2 = [ "red", "yellow", "green" ]
+  arr3 = [ [ 1, 2 ], [3, 4, 5] ]
+  arr4 = [ "all", 'strings', """are the same""", ''''type'''']
+  arr5 = [ [ 1, 2 ], ["a", "b", "c"] ]
+
+  arr7 = [
+    1, 2, 3
+  ]
+
+  arr8 = [
+    1,
+    2, # this is ok
+  ]
+
+  [table-1]
+  key1 = "some string"
+  key2 = 123
+
+
+  [table-2]
+  key1 = "another string"
+  key2 = 456
+
+  [dog."tater.man"]
+  type.name = "pug"
+
+  [a.b.c]
+  [ d.e.f ]
+  [ g .  h  . i ]
+  [ j . "ʞ" . 'l' ]
+  [x.y.z.w]
+
+  name = { first = "Tom", last = "Preston-Werner" }
+  point = { x = 1, y = 2 }
+  animal = { type.name = "pug" }
+
+  [[products]]
+  name = "Hammer"
+  sku = 738594937
+
+  [[products]]
+
+  [[products]]
+  name = "Nail"
+  sku = 284758393
+  color = "gray"
+
+  [[fruit]]
+    name = "apple"
+
+    [fruit.physical]
+      color = "red"
+      shape = "round"
+
+    [[fruit.variety]]
+      name = "red delicious"
+
+    [[fruit.variety]]
+      name = "granny smith"
+
+  [[fruit]]
+    name = "banana"
+
+    [[fruit.variety]]
+      name = "plantain"
+''
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-functionargs.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-functionargs.exp
new file mode 100644
index 0000000000..7f9ac40e81
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-functionargs.exp
@@ -0,0 +1 @@
+{ column = 11; file = "eval-okay-getattrpos-functionargs.nix"; line = 2; }
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-functionargs.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-functionargs.nix
new file mode 100644
index 0000000000..11d6bb0e3a
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-functionargs.nix
@@ -0,0 +1,4 @@
+let
+  fun = { foo }: {};
+  pos = builtins.unsafeGetAttrPos "foo" (builtins.functionArgs fun);
+in { inherit (pos) column line; file = baseNameOf pos.file; }
diff --git a/third_party/nix/src/tests/lang/eval-okay-getattrpos-undefined.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-undefined.exp
index 19765bd501..19765bd501 100644
--- a/third_party/nix/src/tests/lang/eval-okay-getattrpos-undefined.exp
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-undefined.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-getattrpos-undefined.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-undefined.nix
index 14dd38f773..14dd38f773 100644
--- a/third_party/nix/src/tests/lang/eval-okay-getattrpos-undefined.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos-undefined.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-getattrpos.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos.exp
index 469249bbc6..469249bbc6 100644
--- a/third_party/nix/src/tests/lang/eval-okay-getattrpos.exp
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos.exp
diff --git a/third_party/nix/src/tests/lang/eval-okay-getattrpos.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos.nix
index ca6b079615..ca6b079615 100644
--- a/third_party/nix/src/tests/lang/eval-okay-getattrpos.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-getattrpos.nix
diff --git a/third_party/nix/src/tests/lang/eval-okay-import.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-import.exp
index c508125b55..c508125b55 100644
--- a/third_party/nix/src/tests/lang/eval-okay-import.exp
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-import.exp
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-import.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-import.nix
new file mode 100644
index 0000000000..76213a9541
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-import.nix
@@ -0,0 +1,11 @@
+let
+
+  overrides = {
+    import = fn: scopedImport overrides fn;
+
+    scopedImport = attrs: fn: scopedImport (overrides // attrs) fn;
+
+    builtins = builtins // overrides;
+  } // import ./../lib.nix;
+
+in scopedImport overrides ./../imported.nix
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-overrides.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-overrides.exp
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-overrides.exp
@@ -0,0 +1 @@
+2
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-overrides.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-overrides.nix
new file mode 100644
index 0000000000..358742b36e
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-overrides.nix
@@ -0,0 +1,9 @@
+let
+
+  overrides = { a = 2; };
+
+in (rec {
+  __overrides = overrides;
+  x = a;
+  a = 1;
+}).x
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path-antiquotation.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path-antiquotation.exp
new file mode 100644
index 0000000000..5b8ea02438
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path-antiquotation.exp
@@ -0,0 +1 @@
+{ absolute = /foo; expr = /pwd/lang/foo/bar; home = /fake-home/foo; notfirst = /pwd/lang/bar/foo; simple = /pwd/lang/foo; slashes = /foo/bar; surrounded = /pwd/lang/a-foo-b; }
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path-antiquotation.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path-antiquotation.nix
new file mode 100644
index 0000000000..497d7c1c75
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path-antiquotation.nix
@@ -0,0 +1,12 @@
+let
+  foo = "foo";
+in
+{
+  simple = ./${foo};
+  surrounded = ./a-${foo}-b;
+  absolute = /${foo};
+  expr = ./${foo + "/bar"};
+  home = ~/${foo};
+  notfirst = ./bar/${foo};
+  slashes = /${foo}/${"bar"};
+}
diff --git a/third_party/nix/src/tests/lang/disabled/eval-okay-path.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path.nix
index e67168cf3e..e67168cf3e 100644
--- a/third_party/nix/src/tests/lang/disabled/eval-okay-path.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-path.nix
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readDir.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readDir.exp
new file mode 100644
index 0000000000..6413f6d4f9
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readDir.exp
@@ -0,0 +1 @@
+{ bar = "regular"; foo = "directory"; ldir = "symlink"; linked = "symlink"; }
diff --git a/third_party/nix/src/tests/lang/eval-okay-readDir.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readDir.nix.disabled
index a7ec9292aa..a7ec9292aa 100644
--- a/third_party/nix/src/tests/lang/eval-okay-readDir.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readDir.nix.disabled
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readFileType.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readFileType.exp
new file mode 100644
index 0000000000..6413f6d4f9
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readFileType.exp
@@ -0,0 +1 @@
+{ bar = "regular"; foo = "directory"; ldir = "symlink"; linked = "symlink"; }
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readFileType.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readFileType.nix
new file mode 100644
index 0000000000..174fb6c3a0
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-readFileType.nix
@@ -0,0 +1,6 @@
+{
+  bar    = builtins.readFileType ./readDir/bar;
+  foo    = builtins.readFileType ./readDir/foo;
+  linked = builtins.readFileType ./readDir/linked;
+  ldir   = builtins.readFileType ./readDir/ldir;
+}
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-replacestrings.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-replacestrings.exp
new file mode 100644
index 0000000000..eac67c5fed
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-replacestrings.exp
@@ -0,0 +1 @@
+[ "faabar" "fbar" "fubar" "faboor" "fubar" "XaXbXcX" "X" "a_b" "fubar" ]
diff --git a/third_party/nix/src/tests/lang/eval-okay-replacestrings.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-replacestrings.nix
index bd8031fc00..a803e65199 100644
--- a/third_party/nix/src/tests/lang/eval-okay-replacestrings.nix
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-replacestrings.nix
@@ -8,4 +8,5 @@ with builtins;
   (replaceStrings [""] ["X"] "abc")
   (replaceStrings [""] ["X"] "")
   (replaceStrings ["-"] ["_"] "a-b")
+  (replaceStrings ["oo" "XX"] ["u" (throw "unreachable")] "foobar")
 ]
diff --git a/third_party/nix/src/tests/lang/disabled/eval-okay-search-path.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.exp
index 4519bc406d..4519bc406d 100644
--- a/third_party/nix/src/tests/lang/disabled/eval-okay-search-path.exp
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.exp
diff --git a/third_party/nix/src/tests/lang/disabled/eval-okay-search-path.flags b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.flags
index a28e682100..a28e682100 100644
--- a/third_party/nix/src/tests/lang/disabled/eval-okay-search-path.flags
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.flags
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.nix
new file mode 100644
index 0000000000..6fe33decc0
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-search-path.nix
@@ -0,0 +1,10 @@
+with import ./lib.nix;
+with builtins;
+
+assert isFunction (import <nix/fetchurl.nix>);
+
+assert length __nixPath == 5;
+assert length (filter (x: baseNameOf x.path == "dir4") __nixPath) == 1;
+
+import <a.nix> + import <b.nix> + import <c.nix> + import <dir5/c.nix>
+  + (let __nixPath = [ { path = ./dir2; } { path = ./dir1; } ]; in import <a.nix>)
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-zipAttrsWith.exp b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-zipAttrsWith.exp
new file mode 100644
index 0000000000..9c0b15d22b
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-zipAttrsWith.exp
@@ -0,0 +1 @@
+{ "0" = { n = "0"; v = [ 5 23 29 ]; }; "1" = { n = "1"; v = [ 7 30 ]; }; "2" = { n = "2"; v = [ 18 ]; }; "4" = { n = "4"; v = [ 10 ]; }; "5" = { n = "5"; v = [ 15 25 26 31 ]; }; "6" = { n = "6"; v = [ 3 14 ]; }; "7" = { n = "7"; v = [ 12 ]; }; "8" = { n = "8"; v = [ 2 6 8 9 ]; }; "9" = { n = "9"; v = [ 0 16 ]; }; a = { n = "a"; v = [ 17 21 22 27 ]; }; c = { n = "c"; v = [ 11 24 ]; }; d = { n = "d"; v = [ 4 13 28 ]; }; e = { n = "e"; v = [ 20 ]; }; f = { n = "f"; v = [ 1 19 ]; }; }
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-zipAttrsWith.nix b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-zipAttrsWith.nix
new file mode 100644
index 0000000000..e5d4cdccb7
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-zipAttrsWith.nix
@@ -0,0 +1,9 @@
+with import ./../lib.nix;
+
+let
+  str = builtins.hashString "sha256" "test";
+in
+builtins.zipAttrsWith
+  (n: v: { inherit n v; })
+  (map (n: { ${builtins.substring n 1 str} = n; })
+    (range 0 31))
diff --git a/third_party/nix/src/tests/lang/readDir/bar b/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/bar
index e69de29bb2..e69de29bb2 100644
--- a/third_party/nix/src/tests/lang/readDir/bar
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/bar
diff --git a/third_party/nix/src/tests/lang/readDir/foo/git-hates-directories b/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/foo/git-hates-directories
index e69de29bb2..e69de29bb2 100644
--- a/third_party/nix/src/tests/lang/readDir/foo/git-hates-directories
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/foo/git-hates-directories
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/ldir b/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/ldir
new file mode 120000
index 0000000000..1910281566
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/ldir
@@ -0,0 +1 @@
+foo
\ No newline at end of file
diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/linked b/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/linked
new file mode 120000
index 0000000000..c503f86a0c
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/notyetpassing/readDir/linked
@@ -0,0 +1 @@
+foo/git-hates-directories
\ No newline at end of file
diff --git a/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-1.nix b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-1.nix
new file mode 100644
index 0000000000..2c02317d2a
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-1.nix
@@ -0,0 +1,4 @@
+{ x = 123;
+  y = 456;
+  x = 789;
+}
diff --git a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-2.nix b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-2.nix
index 864d9865e0..864d9865e0 100644
--- a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-2.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-2.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-3.nix b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-3.nix
index 114d19779f..114d19779f 100644
--- a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-3.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-3.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-4.nix b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-4.nix
index 77417432b3..77417432b3 100644
--- a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-4.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-4.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-7.nix b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-7.nix
index bbc3eb08c0..bbc3eb08c0 100644
--- a/third_party/nix/src/tests/lang/parse-fail-dup-attrs-7.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-dup-attrs-7.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-dup-formals.nix b/tvix/eval/src/tests/nix_tests/parse-fail-dup-formals.nix
index a0edd91a96..a0edd91a96 100644
--- a/third_party/nix/src/tests/lang/parse-fail-dup-formals.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-dup-formals.nix
diff --git a/tvix/eval/src/tests/nix_tests/parse-fail-eof-in-string.nix b/tvix/eval/src/tests/nix_tests/parse-fail-eof-in-string.nix
new file mode 100644
index 0000000000..19775d2ec8
--- /dev/null
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-eof-in-string.nix
@@ -0,0 +1,3 @@
+# https://github.com/NixOS/nix/issues/6562
+# Note that this file must not end with a newline.
+a 1"$
\ No newline at end of file
diff --git a/third_party/nix/src/tests/lang/parse-fail-mixed-nested-attrs1.nix b/tvix/eval/src/tests/nix_tests/parse-fail-mixed-nested-attrs1.nix
index 11e40e66fd..11e40e66fd 100644
--- a/third_party/nix/src/tests/lang/parse-fail-mixed-nested-attrs1.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-mixed-nested-attrs1.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-mixed-nested-attrs2.nix b/tvix/eval/src/tests/nix_tests/parse-fail-mixed-nested-attrs2.nix
index 17da82e5f0..17da82e5f0 100644
--- a/third_party/nix/src/tests/lang/parse-fail-mixed-nested-attrs2.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-mixed-nested-attrs2.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-patterns-1.nix b/tvix/eval/src/tests/nix_tests/parse-fail-patterns-1.nix
index 7b40616417..7b40616417 100644
--- a/third_party/nix/src/tests/lang/parse-fail-patterns-1.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-patterns-1.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-regression-20060610.nix b/tvix/eval/src/tests/nix_tests/parse-fail-regression-20060610.nix
index b1934f7e1e..b1934f7e1e 100644
--- a/third_party/nix/src/tests/lang/parse-fail-regression-20060610.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-regression-20060610.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-uft8.nix b/tvix/eval/src/tests/nix_tests/parse-fail-uft8.nix
index 34948d48ae..34948d48ae 100644
--- a/third_party/nix/src/tests/lang/parse-fail-uft8.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-uft8.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-undef-var-2.nix b/tvix/eval/src/tests/nix_tests/parse-fail-undef-var-2.nix
index c10a52b1ea..c10a52b1ea 100644
--- a/third_party/nix/src/tests/lang/parse-fail-undef-var-2.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-undef-var-2.nix
diff --git a/third_party/nix/src/tests/lang/parse-fail-undef-var.nix b/tvix/eval/src/tests/nix_tests/parse-fail-undef-var.nix
index 7b63008110..7b63008110 100644
--- a/third_party/nix/src/tests/lang/parse-fail-undef-var.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-fail-undef-var.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-1.nix b/tvix/eval/src/tests/nix_tests/parse-okay-1.nix
index 23a58ed109..23a58ed109 100644
--- a/third_party/nix/src/tests/lang/parse-okay-1.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-1.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-crlf.nix b/tvix/eval/src/tests/nix_tests/parse-okay-crlf.nix
index 21518d4c6d..21518d4c6d 100644
--- a/third_party/nix/src/tests/lang/parse-okay-crlf.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-crlf.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-dup-attrs-5.nix b/tvix/eval/src/tests/nix_tests/parse-okay-dup-attrs-5.nix
index f4b9efd0c5..f4b9efd0c5 100644
--- a/third_party/nix/src/tests/lang/parse-okay-dup-attrs-5.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-dup-attrs-5.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-dup-attrs-6.nix b/tvix/eval/src/tests/nix_tests/parse-okay-dup-attrs-6.nix
index ae6d7a7693..ae6d7a7693 100644
--- a/third_party/nix/src/tests/lang/parse-okay-dup-attrs-6.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-dup-attrs-6.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-mixed-nested-attrs-1.nix b/tvix/eval/src/tests/nix_tests/parse-okay-mixed-nested-attrs-1.nix
index fd1001c8ca..fd1001c8ca 100644
--- a/third_party/nix/src/tests/lang/parse-okay-mixed-nested-attrs-1.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-mixed-nested-attrs-1.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-mixed-nested-attrs-2.nix b/tvix/eval/src/tests/nix_tests/parse-okay-mixed-nested-attrs-2.nix
index ad066b6803..ad066b6803 100644
--- a/third_party/nix/src/tests/lang/parse-okay-mixed-nested-attrs-2.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-mixed-nested-attrs-2.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-mixed-nested-attrs-3.nix b/tvix/eval/src/tests/nix_tests/parse-okay-mixed-nested-attrs-3.nix
index 45a33e4803..45a33e4803 100644
--- a/third_party/nix/src/tests/lang/parse-okay-mixed-nested-attrs-3.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-mixed-nested-attrs-3.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-regression-20041027.nix b/tvix/eval/src/tests/nix_tests/parse-okay-regression-20041027.nix
index ae2e256eea..ae2e256eea 100644
--- a/third_party/nix/src/tests/lang/parse-okay-regression-20041027.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-regression-20041027.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-regression-751.nix b/tvix/eval/src/tests/nix_tests/parse-okay-regression-751.nix
index 05c78b3016..05c78b3016 100644
--- a/third_party/nix/src/tests/lang/parse-okay-regression-751.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-regression-751.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-subversion.nix b/tvix/eval/src/tests/nix_tests/parse-okay-subversion.nix
index 356272815d..356272815d 100644
--- a/third_party/nix/src/tests/lang/parse-okay-subversion.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-subversion.nix
diff --git a/third_party/nix/src/tests/lang/parse-okay-url.nix b/tvix/eval/src/tests/nix_tests/parse-okay-url.nix
index fce3b13ee6..08de27d0a4 100644
--- a/third_party/nix/src/tests/lang/parse-okay-url.nix
+++ b/tvix/eval/src/tests/nix_tests/parse-okay-url.nix
@@ -3,5 +3,6 @@
   http://www2.mplayerhq.hu/MPlayer/releases/fonts/font-arial-iso-8859-1.tar.bz2
   http://losser.st-lab.cs.uu.nl/~armijn/.nix/gcc-3.3.4-static-nix.tar.gz
   http://fpdownload.macromedia.com/get/shockwave/flash/english/linux/7.0r25/install_flash_player_7_linux.tar.gz
+  https://ftp5.gwdg.de/pub/linux/archlinux/extra/os/x86_64/unzip-6.0-14-x86_64.pkg.tar.zst
   ftp://ftp.gtk.org/pub/gtk/v1.2/gtk+-1.2.10.tar.gz
 ]
diff --git a/tvix/eval/src/tests/one_offs.rs b/tvix/eval/src/tests/one_offs.rs
new file mode 100644
index 0000000000..565d1dd48f
--- /dev/null
+++ b/tvix/eval/src/tests/one_offs.rs
@@ -0,0 +1,36 @@
+use crate::*;
+
+#[test]
+fn test_source_builtin() {
+    // Test an evaluation with a source-only builtin. The test ensures
+    // that the artificially constructed thunking is correct.
+
+    let mut eval = Evaluation::new_impure();
+    eval.src_builtins.push(("testSourceBuiltin", "42"));
+
+    let result = eval.evaluate("builtins.testSourceBuiltin", None);
+    assert!(
+        result.errors.is_empty(),
+        "evaluation failed: {:?}",
+        result.errors
+    );
+
+    let value = result.value.unwrap();
+    assert!(
+        matches!(value, Value::Integer(42)),
+        "expected the integer 42, but got {}",
+        value,
+    );
+}
+
+#[test]
+fn skip_broken_bytecode() {
+    let result = Evaluation::new_pure().evaluate(/* code = */ "x", None);
+
+    assert_eq!(result.errors.len(), 1);
+
+    assert!(matches!(
+        result.errors[0].kind,
+        ErrorKind::UnknownStaticVariable
+    ));
+}
diff --git a/tvix/eval/src/tests/tvix_tests/README.md b/tvix/eval/src/tests/tvix_tests/README.md
new file mode 100644
index 0000000000..b493aa81f1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/README.md
@@ -0,0 +1,19 @@
+These tests are "native" to Tvix and exist in addition to the Nix test
+suite.
+
+All of these are straightforward code snippets which are expected to
+produce a certain result.
+
+# `identity-*` tests
+
+Files named `identity-*.nix` contain code that is supposed to produce
+itself exactly after evaluation.
+
+These are useful for testing literals.
+
+# `eval-okay-*` tests
+
+Files named `eval-okay-*.nix` contain code which is supposed to
+evaluate to the output in the corresponding `eval-okay-*.exp` file.
+
+This convention is taken from the original Nix test suite.
diff --git a/tvix/eval/src/tests/tvix_tests/directory/default.nix b/tvix/eval/src/tests/tvix_tests/directory/default.nix
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/directory/default.nix
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-substring-negative-start.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-substring-negative-start.nix
new file mode 100644
index 0000000000..bc7a16ded8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-substring-negative-start.nix
@@ -0,0 +1,3 @@
+# Negative start is illegal, but negative length works, see
+# eval-okay-builtins-substring-negative-length.nix
+builtins.substring (-1) 1 "Wiggly Donkers"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-thunk-error.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-thunk-error.nix
new file mode 100644
index 0000000000..6df79d13f4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-thunk-error.nix
@@ -0,0 +1 @@
+builtins.genList (_: { }.foo) 1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-tojson-tostring-notcallable.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-tojson-tostring-notcallable.nix
new file mode 100644
index 0000000000..345b76fde0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-tojson-tostring-notcallable.nix
@@ -0,0 +1,5 @@
+# attribute sets with a non-callable `__toString` can not be
+# serialised to JSON.
+builtins.toJSON {
+  __toString = 42;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-tojson-tostring-strong.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-tojson-tostring-strong.nix
new file mode 100644
index 0000000000..d1c72dc678
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-builtins-tojson-tostring-strong.nix
@@ -0,0 +1,6 @@
+# String coercions when using builtins.toJSON on an attribute set with
+# a `__toString` attribute should be weak.
+builtins.toJSON {
+  __toString = self: self.x;
+  x = 42;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-closed-formals.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-closed-formals.nix
new file mode 100644
index 0000000000..a0cd20c470
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-closed-formals.nix
@@ -0,0 +1 @@
+({ x }: x) { x = 1; y = 2; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-deep-forced-thunk-error.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-deep-forced-thunk-error.nix
new file mode 100644
index 0000000000..b7a7583022
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-deep-forced-thunk-error.nix
@@ -0,0 +1 @@
+[ (throw "error!") ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-deepseq.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-deepseq.nix
new file mode 100644
index 0000000000..9baa49b063
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-deepseq.nix
@@ -0,0 +1 @@
+builtins.deepSeq { x = abort "foo"; } 456
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-division-by-zero-float.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-division-by-zero-float.nix
new file mode 100644
index 0000000000..82dd687321
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-division-by-zero-float.nix
@@ -0,0 +1 @@
+1.0 / 0.0
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-division-by-zero-int.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-division-by-zero-int.nix
new file mode 100644
index 0000000000..72dca4d5e4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-division-by-zero-int.nix
@@ -0,0 +1 @@
+1 / 0
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-foldlStrict-strict-op-application.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-foldlStrict-strict-op-application.nix
new file mode 100644
index 0000000000..adc029b2f2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-foldlStrict-strict-op-application.nix
@@ -0,0 +1,4 @@
+builtins.foldl'
+  (_: f: f null)
+  (throw "This doesn't explode")
+  [ (_: throw "Not the final value, but is still forced!") (_: 23) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-force-before-value-pointer-equality.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-force-before-value-pointer-equality.nix
new file mode 100644
index 0000000000..a2182a508f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-force-before-value-pointer-equality.nix
@@ -0,0 +1,5 @@
+let
+  x = throw "I have been forced";
+in
+
+x == x
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-function-formals-typecheck.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-function-formals-typecheck.nix
new file mode 100644
index 0000000000..0108f958bf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-function-formals-typecheck.nix
@@ -0,0 +1,2 @@
+# A function with formal set arguments forces its argument set and verifies its type.
+({ ... }@args: 42) [ ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-getEnv-coercion.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-getEnv-coercion.nix
new file mode 100644
index 0000000000..fe48a5690c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-getEnv-coercion.nix
@@ -0,0 +1 @@
+builtins.getEnv { var = "PATH"; __toString = self: self.var; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-infinite-recursion.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-infinite-recursion.nix
new file mode 100644
index 0000000000..5e4fd3789c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-infinite-recursion.nix
@@ -0,0 +1 @@
+let x = x; in x
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-outer-value-never-pointer-equal.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-outer-value-never-pointer-equal.nix
new file mode 100644
index 0000000000..a8c3cedf61
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-outer-value-never-pointer-equal.nix
@@ -0,0 +1,7 @@
+# For an explanation of this behavior see //tvix/docs/value-pointer-equality.md
+let
+  x = { foo = throw "foo"; };
+in
+
+# while `builtins.seq x null` would succeed, this fails!
+x == x
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-parsedrvname-coerce.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-parsedrvname-coerce.nix
new file mode 100644
index 0000000000..a1218de3fe
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-parsedrvname-coerce.nix
@@ -0,0 +1 @@
+builtins.parseDrvName { outPath = "lol"; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-remove.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-remove.nix
new file mode 100644
index 0000000000..93dd8ccd45
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-remove.nix
@@ -0,0 +1,5 @@
+let {
+attrs = { x = 123; y = 456; };
+
+body = (removeAttrs attrs [ "x" ]).x;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-seq.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-seq.nix
new file mode 100644
index 0000000000..cddbbfd326
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-seq.nix
@@ -0,0 +1 @@
+builtins.seq (abort "foo") 2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-fail-throw-abort-cannot-be-caught.nix b/tvix/eval/src/tests/tvix_tests/eval-fail-throw-abort-cannot-be-caught.nix
new file mode 100644
index 0000000000..10781cb4ea
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-fail-throw-abort-cannot-be-caught.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.throw (builtins.abort "abc"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-abort-throw-can-be-caught.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-abort-throw-can-be-caught.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-abort-throw-can-be-caught.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-abort-throw-can-be-caught.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-abort-throw-can-be-caught.nix
new file mode 100644
index 0000000000..aebeca1dad
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-abort-throw-can-be-caught.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.abort (builtins.throw "abc"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-access-strange-identifier.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-access-strange-identifier.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-access-strange-identifier.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-access-strange-identifier.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-access-strange-identifier.nix
new file mode 100644
index 0000000000..433f53dc56
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-access-strange-identifier.nix
@@ -0,0 +1,9 @@
+let
+  # There is no syntax for accessing this identifier in an ordinary
+  # way.
+  "foo bar" = 42;
+in
+({
+  # but we *can* inherit it back out
+  inherit "foo bar";
+})."foo bar"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.exp
new file mode 100644
index 0000000000..94ba9a881a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.exp
@@ -0,0 +1 @@
+[ /bin /binbar /binbar /binbar /binbar /bin/bar /bin/bin ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.nix
new file mode 100644
index 0000000000..462f670882
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.nix
@@ -0,0 +1,9 @@
+[
+  (/bin + "/")
+  (/bin + "bar")
+  (let name = "bar"; in /bin + name)
+  (let name = "bar"; in /bin + "${name}")
+  (let name = "bar"; in /bin + "/" + "${name}")
+  (let name = "bar"; in /bin + "/${name}")
+  (/bin + /bin)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-float.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-float.exp
new file mode 100644
index 0000000000..08ef6079f8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-float.exp
@@ -0,0 +1 @@
+{ add = 37.34; div = 1.05714; mul = 105.154; sub = 14.35; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-float.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-float.nix
new file mode 100644
index 0000000000..9d12aee061
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-float.nix
@@ -0,0 +1,6 @@
+{
+  add = 12.34 + 25.0;
+  sub = 20.05 - 5.7;
+  mul = 28.42 * 3.70;
+  div = 18.5 / 17.5;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-int.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-int.exp
new file mode 100644
index 0000000000..a5711e8bfe
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-int.exp
@@ -0,0 +1 @@
+{ add = 20; div = 3; mul = 8; sub = 15; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-int.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-int.nix
new file mode 100644
index 0000000000..c53790db09
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-arithmetic-int.nix
@@ -0,0 +1,6 @@
+{
+  add = 15 + 5;
+  sub = 20 - 5;
+  mul = 4 * 2;
+  div = 9 / 3;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-assert-thunk-condition.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-assert-thunk-condition.exp
new file mode 100644
index 0000000000..aabe6ec390
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-assert-thunk-condition.exp
@@ -0,0 +1 @@
+21
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-assert-thunk-condition.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-assert-thunk-condition.nix
new file mode 100644
index 0000000000..ac65f5814d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-assert-thunk-condition.nix
@@ -0,0 +1,7 @@
+let
+  condition = x: y: x < y;
+in
+
+# The function application here will become a thunk which verifies that
+  # assert forces the condition expression correctly.
+assert condition 21 42; 21
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attempt-to-call-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-attempt-to-call-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attempt-to-call-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attempt-to-call-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-attempt-to-call-catchable.nix
new file mode 100644
index 0000000000..f4ef72a88b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attempt-to-call-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (throw "fred" 5)).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attr-key-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-attr-key-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attr-key-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attr-key-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-attr-key-catchable.nix
new file mode 100644
index 0000000000..b9d835bcc5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attr-key-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval { "${builtins.throw "a"}" = "b"; }).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-inherit-literal.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-inherit-literal.exp
new file mode 100644
index 0000000000..60d3b2f4a4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-inherit-literal.exp
@@ -0,0 +1 @@
+15
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-inherit-literal.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-inherit-literal.nix
new file mode 100644
index 0000000000..9ecb4e9880
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-inherit-literal.nix
@@ -0,0 +1,2 @@
+# the 'from' part of an `inherit` can be any expression.
+{ inherit ({ a = 15; }) a; }.a
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-simple-inherit.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-simple-inherit.exp
new file mode 100644
index 0000000000..a779fce51a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-simple-inherit.exp
@@ -0,0 +1 @@
+{ a = 1; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-simple-inherit.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-simple-inherit.nix
new file mode 100644
index 0000000000..68880bcfd8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-simple-inherit.nix
@@ -0,0 +1,4 @@
+let
+  a = 1;
+in
+{ inherit a; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-lhs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-lhs.exp
new file mode 100644
index 0000000000..fedf8f25a6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-lhs.exp
@@ -0,0 +1 @@
+{ a = "ok"; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-lhs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-lhs.nix
new file mode 100644
index 0000000000..973170cdd5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-lhs.nix
@@ -0,0 +1 @@
+{ } // { a = "ok"; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-rhs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-rhs.exp
new file mode 100644
index 0000000000..fedf8f25a6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-rhs.exp
@@ -0,0 +1 @@
+{ a = "ok"; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-rhs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-rhs.nix
new file mode 100644
index 0000000000..f51b88e93a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-empty-rhs.nix
@@ -0,0 +1 @@
+{ a = "ok"; } // { }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-kv-lhs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-kv-lhs.exp
new file mode 100644
index 0000000000..c2234a47e2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-kv-lhs.exp
@@ -0,0 +1 @@
+{ name = "foo"; other = 42; value = "bar"; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-kv-lhs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-kv-lhs.nix
new file mode 100644
index 0000000000..6f71684902
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update-kv-lhs.nix
@@ -0,0 +1 @@
+{ name = "foo"; value = "bar"; } // { other = 42; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update.exp
new file mode 100644
index 0000000000..57f4d541bd
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update.exp
@@ -0,0 +1 @@
+{ a = 15; b = "works"; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update.nix
new file mode 100644
index 0000000000..735602fe02
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-attrs-update.nix
@@ -0,0 +1 @@
+{ a = 15; } // { b = "works"; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof-propagate-catchables.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof-propagate-catchables.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof-propagate-catchables.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof-propagate-catchables.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof-propagate-catchables.nix
new file mode 100644
index 0000000000..240736b12a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof-propagate-catchables.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.baseNameOf (throw "jill"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof.exp
new file mode 100644
index 0000000000..60a773f4af
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof.exp
@@ -0,0 +1 @@
+[ "bar" "foo" "" "bar" "." "" "" "" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof.nix
new file mode 100644
index 0000000000..bc59613f54
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-basenameof.nix
@@ -0,0 +1,10 @@
+[
+  (builtins.baseNameOf /foo/bar)
+  (builtins.baseNameOf "foo")
+  (builtins.baseNameOf "foo///")
+  (builtins.baseNameOf "foo/bar")
+  (builtins.baseNameOf "./.")
+  (builtins.baseNameOf "")
+  (builtins.baseNameOf /.)
+  (builtins.toString (builtins.baseNameOf /.))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-add.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-add.exp
new file mode 100644
index 0000000000..c3ac813de6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-add.exp
@@ -0,0 +1 @@
+[ 18 18.9 18.9 19.1 19 42 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-add.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-add.nix
new file mode 100644
index 0000000000..b04b1d1fa6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-add.nix
@@ -0,0 +1,8 @@
+[
+  (builtins.add 7 11)
+  (builtins.add 7.9 11)
+  (builtins.add 7 11.9)
+  (builtins.add 7.2 11.9)
+  (builtins.add 7.1 11.9)
+  (builtins.add (builtins.add 21 10) 11)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all-propagate-catchable.exp
new file mode 100644
index 0000000000..48e341f4d8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all-propagate-catchable.nix
new file mode 100644
index 0000000000..8902e27c45
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all-propagate-catchable.nix
@@ -0,0 +1 @@
+map (e: (builtins.tryEval e).success) [ (builtins.all (builtins.throw "a") [ "" ]) (builtins.all (x: true) (builtins.throw "b")) (builtins.all (_: builtins.throw "x") [ "" ]) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all.exp
new file mode 100644
index 0000000000..82ca7e6b6d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all.exp
@@ -0,0 +1 @@
+[ true true false false false false true true false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all.nix
new file mode 100644
index 0000000000..12d62632dd
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-all.nix
@@ -0,0 +1,15 @@
+[
+  (builtins.all (x: x) [ ])
+  (builtins.all (x: x) [ true true true ])
+  (builtins.all (x: x) [ false false false ])
+  (builtins.all (x: x) [ true true false ])
+  (builtins.all (x: x) [ false true true ])
+
+  # evaluation should short-circuit
+  (builtins.all (x: x) [ true false (builtins.abort "should be unreachable") ])
+
+  # arbitrary functions supported
+  (builtins.all (x: x * 2 == 42) [ ])
+  (builtins.all (x: x * 2 == 42) [ 21 21 21 ])
+  (builtins.all (x: x * 2 == 42) [ 1 2 3 ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any-propagate-catchable.exp
new file mode 100644
index 0000000000..48e341f4d8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any-propagate-catchable.nix
new file mode 100644
index 0000000000..8db5c0c6dc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any-propagate-catchable.nix
@@ -0,0 +1 @@
+map (e: (builtins.tryEval e).success) [ (builtins.any (builtins.throw "a") [ "" ]) (builtins.any (x: true) (builtins.throw "b")) (builtins.any (_: builtins.throw "a") [ "" ]) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any.exp
new file mode 100644
index 0000000000..d6846ac3f7
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any.exp
@@ -0,0 +1 @@
+[ false true false true true true false true false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any.nix
new file mode 100644
index 0000000000..2c659f130b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-any.nix
@@ -0,0 +1,15 @@
+[
+  (builtins.any (x: x) [ ])
+  (builtins.any (x: x) [ true true true ])
+  (builtins.any (x: x) [ false false false ])
+  (builtins.any (x: x) [ true true false ])
+  (builtins.any (x: x) [ false true true ])
+
+  # evaluation should short-circuit
+  (builtins.any (x: x) [ false true (builtins.abort "should be unreachable") ])
+
+  # arbitrary functions supported
+  (builtins.any (x: x * 2 == 42) [ ])
+  (builtins.any (x: x * 2 == 42) [ 7 21 42 ])
+  (builtins.any (x: x * 2 == 42) [ 1 2 3 ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrnames.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrnames.exp
new file mode 100644
index 0000000000..6521066a8e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrnames.exp
@@ -0,0 +1 @@
+[ [ ] [ "bar" "baz" "foo" ] [ "Baz" "Foo" "bar" ] [ "Eric Idle" "Graham Chapman" "John Cleese" "Michael Palin" "Terry Gilliam" "Terry Jones" ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrnames.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrnames.nix
new file mode 100644
index 0000000000..82240a0647
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrnames.nix
@@ -0,0 +1,13 @@
+[
+  (builtins.attrNames { })
+  (builtins.attrNames { foo = 1; bar = 2; baz = 3; })
+  (builtins.attrNames { Foo = 1; bar = 2; Baz = 3; })
+  (builtins.attrNames {
+    "Graham Chapman" = true;
+    "John Cleese" = true;
+    "Terry Gilliam" = true;
+    "Eric Idle" = true;
+    "Terry Jones" = true;
+    "Michael Palin" = true;
+  })
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues-propagate-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues-propagate-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues-propagate-catchable.nix
new file mode 100644
index 0000000000..b8c15c8748
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues-propagate-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.attrValues (builtins.throw "a"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues.exp
new file mode 100644
index 0000000000..35c3697720
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues.exp
@@ -0,0 +1 @@
+[ [ ] [ 2 3 1 ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues.nix
new file mode 100644
index 0000000000..ce6c5c3816
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-attrvalues.nix
@@ -0,0 +1,4 @@
+[
+  (builtins.attrValues { })
+  (builtins.attrValues { foo = 1; bar = 2; baz = 3; })
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitand.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitand.exp
new file mode 100644
index 0000000000..30b348853e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitand.exp
@@ -0,0 +1 @@
+[ 0 0 0 1 8 8 8 8 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitand.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitand.nix
new file mode 100644
index 0000000000..af40005ed9
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitand.nix
@@ -0,0 +1,10 @@
+[
+  (builtins.bitAnd 0 0)
+  (builtins.bitAnd 0 1)
+  (builtins.bitAnd 1 0)
+  (builtins.bitAnd 1 1)
+  (builtins.bitAnd 8 8)
+  (builtins.bitAnd 8 (builtins.add 4 4))
+  (builtins.bitAnd (builtins.add 4 4) 8)
+  (builtins.bitAnd (builtins.add 4 4) (builtins.add 4 4))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitor.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitor.exp
new file mode 100644
index 0000000000..2556b4183c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitor.exp
@@ -0,0 +1 @@
+[ 0 1 1 1 8 8 8 8 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitor.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitor.nix
new file mode 100644
index 0000000000..9c28f6d7ac
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitor.nix
@@ -0,0 +1,10 @@
+[
+  (builtins.bitOr 0 0)
+  (builtins.bitOr 1 0)
+  (builtins.bitOr 0 1)
+  (builtins.bitOr 1 1)
+  (builtins.bitOr 8 8)
+  (builtins.bitOr 8 (builtins.add 4 4))
+  (builtins.bitOr (builtins.add 4 4) 8)
+  (builtins.bitOr (builtins.add 4 4) (builtins.add 4 4))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitxor.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitxor.exp
new file mode 100644
index 0000000000..457157d459
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitxor.exp
@@ -0,0 +1 @@
+[ 0 1 1 0 8 8 0 0 0 0 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitxor.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitxor.nix
new file mode 100644
index 0000000000..80e363fb07
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-bitxor.nix
@@ -0,0 +1,12 @@
+[
+  (builtins.bitXor 0 0)
+  (builtins.bitXor 1 0)
+  (builtins.bitXor 0 1)
+  (builtins.bitXor 1 1)
+  (builtins.bitXor 8 0)
+  (builtins.bitXor 0 8)
+  (builtins.bitXor 8 8)
+  (builtins.bitXor 8 (builtins.add 4 4))
+  (builtins.bitXor (builtins.add 4 4) 8)
+  (builtins.bitXor (builtins.add 4 4) (builtins.add 4 4))
+]
diff --git a/third_party/nix/src/tests/lang/evalstore-okay-context-introspection.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.exp
index 27ba77ddaf..27ba77ddaf 100644
--- a/third_party/nix/src/tests/lang/evalstore-okay-context-introspection.exp
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.exp
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.nix
new file mode 100644
index 0000000000..434ccf8049
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.nix
@@ -0,0 +1 @@
+[ builtins ] == [ builtins.builtins ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catAttrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catAttrs.exp
new file mode 100644
index 0000000000..f8c0b2de5f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catAttrs.exp
@@ -0,0 +1 @@
+[ 21 "+" 21 "=" 42 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catAttrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catAttrs.nix
new file mode 100644
index 0000000000..edac76d446
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catAttrs.nix
@@ -0,0 +1,10 @@
+builtins.catAttrs "foo" [
+  { foo = 21; }
+  { bar = 23; foo = "+"; }
+  { }
+  { bar = 12; }
+  { foo = 21 + 0; }
+  { foo = "="; }
+  ({ bar = 13; } // { baz = 89; })
+  { foo = 42; bar = 33; }
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catattrs-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catattrs-propagate-catchable.exp
new file mode 100644
index 0000000000..c3bb809c9f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catattrs-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catattrs-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catattrs-propagate-catchable.nix
new file mode 100644
index 0000000000..5385591f77
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-catattrs-propagate-catchable.nix
@@ -0,0 +1 @@
+map (e: (builtins.tryEval e).success) [ (builtins.catAttrs "a" (builtins.throw "b")) (builtins.catAttrs (builtins.throw "a") { a = 1; }) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-compareVersions.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-compareVersions.exp
new file mode 100644
index 0000000000..e69498c3e1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-compareVersions.exp
@@ -0,0 +1 @@
+[ 0 -1 -1 0 0 0 1 1 -1 1 -1 1 -1 -1 -1 -1 0 1 -1 -1 1 -1 -1 0 1 1 1 1 -1 -1 -1 -1 -1 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-compareVersions.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-compareVersions.nix
new file mode 100644
index 0000000000..40a90b5070
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-compareVersions.nix
@@ -0,0 +1,46 @@
+let
+  cmp = a: b:
+    let
+      ord1 = builtins.compareVersions a b;
+      ord2 = builtins.compareVersions b a;
+    in
+    assert ord1 == -ord2; ord1;
+in
+
+[
+  (cmp "1.2.3" "1.2.3")
+  (cmp "1.2.2" "1.2.3")
+  (cmp "1.2.3" "1.2.40")
+  (cmp "1.2.3" ".1.2.3")
+  (cmp "1.2.3" "1..2.3")
+  (cmp "1.2.3" "1.2.3.")
+  (cmp "1.2.3" "1.2")
+  (cmp "1.2.3" "1.2.a")
+  (cmp "1a.b" "1a.2")
+  (cmp "1" "")
+  (cmp "1.0" "1.0.0")
+  (cmp "2.3" "2.3pre")
+  (cmp "2.3" "2.3.0pre")
+  (cmp "2.3pre" "2.3.0pre")
+  (cmp "2.3" "2.3prepre")
+  (cmp "2.3pre" "2.3prepre")
+  (cmp "2.3prepre" "2.3prepre")
+  # check that the plain word comparison (via Ord) behaves the same
+  (cmp "foo" "bar")
+  (cmp "FoO" "fOo")
+  (cmp "foo" "fooo")
+  (cmp "foopre" "foo")
+  # Subset of test cases from eval-okay-versions.nix shipped by C++ Nix
+  (cmp "1.0" "2.3")
+  (cmp "2.1" "2.3")
+  (cmp "2.3" "2.3")
+  (cmp "2.5" "2.3")
+  (cmp "3.1" "2.3")
+  (cmp "2.3.1" "2.3")
+  (cmp "2.3.1" "2.3a")
+  (cmp "2.3pre1" "2.3")
+  (cmp "2.3pre3" "2.3pre12")
+  (cmp "2.3a" "2.3c")
+  (cmp "2.3pre1" "2.3c")
+  (cmp "2.3pre1" "2.3q")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists-propagate-catchable.exp
new file mode 100644
index 0000000000..c82ddd8a80
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false true ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists-propagate-catchable.nix
new file mode 100644
index 0000000000..8071daf7fb
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists-propagate-catchable.nix
@@ -0,0 +1 @@
+map (e: (builtins.tryEval e).success) [ (builtins.concatLists (builtins.throw "a")) (builtins.concatLists [ [ ] (builtins.throw "a") ]) (builtins.concatLists [ [ ] [ ] [ builtins.throw "a" ] ]) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists.exp
new file mode 100644
index 0000000000..64ae529ac2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists.exp
@@ -0,0 +1 @@
+[ [ ] [ 1 2 3 4 5 6 ] [ [ 1 ] [ 2 ] [ 3 ] ] [ 1 2 3 4 5 6 ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists.nix
new file mode 100644
index 0000000000..19ef5eba11
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-lists.nix
@@ -0,0 +1,6 @@
+[
+  (builtins.concatLists [ ])
+  (builtins.concatLists [ [ 1 2 ] [ 3 4 ] [ 5 6 ] ])
+  (builtins.concatLists [ [ [ 1 ] [ 2 ] ] [ [ 3 ] ] [ ] ])
+  (builtins.concatLists [ [ 1 2 ] [ ] [ 3 4 ] [ ] [ 5 6 ] ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-map-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-map-propagate-catchable.exp
new file mode 100644
index 0000000000..48e341f4d8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-map-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-map-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-map-propagate-catchable.nix
new file mode 100644
index 0000000000..740f0d3fbc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-map-propagate-catchable.nix
@@ -0,0 +1 @@
+map (e: (builtins.tryEval e).success) [ (builtins.concatMap (builtins.throw "a") [ "" ]) (builtins.concatMap (_: builtins.throw "x") [ "" ]) (builtins.concatMap (_: [ ]) (builtins.throw "a")) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-strings-sep-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-strings-sep-propagate-catchable.exp
new file mode 100644
index 0000000000..48e341f4d8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-strings-sep-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-strings-sep-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-strings-sep-propagate-catchable.nix
new file mode 100644
index 0000000000..ce5ce4170f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-concat-strings-sep-propagate-catchable.nix
@@ -0,0 +1 @@
+map (e: (builtins.tryEval e).success) [ (builtins.concatStringsSep (builtins.throw "a") [ "" ]) (builtins.concatStringsSep "," (builtins.throw "a")) (builtins.concatStringsSep "," [ "a" (builtins.throw "a") ]) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-div.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-div.exp
new file mode 100644
index 0000000000..44154ba6a6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-div.exp
@@ -0,0 +1 @@
+[ 3 7 0 1 0 0.5 0.5 0.5 42 1 1 -1 -1 -1 1 1 -1 -1 -1 1 1 -1 -1 -1 -74711 -74711 -74711 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-div.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-div.nix
new file mode 100644
index 0000000000..dc6ce27815
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-div.nix
@@ -0,0 +1,34 @@
+[
+  (builtins.div 9 3)
+  (builtins.div 7 1)
+  (builtins.div 3 9)
+  (builtins.div 4 4)
+  (builtins.div 1 2)
+  (builtins.div 1.0 2)
+  (builtins.div 1 2.0)
+  (builtins.div 1.0 2.0)
+  (builtins.div (builtins.div 84 4) 0.5)
+
+  # builtins.div should truncate towards 0
+  (builtins.div 3 2)
+  (builtins.div (-3) (-2))
+  (builtins.div (-3) 2)
+  (builtins.div 3 (-2))
+  (-(builtins.div 3 2))
+
+  (builtins.div 4 3)
+  (builtins.div (-4) (-3))
+  (builtins.div (-4) 3)
+  (builtins.div 4 (-3))
+  (-(builtins.div 4 3))
+
+  (builtins.div 5 3)
+  (builtins.div (-5) (-3))
+  (builtins.div (-5) 3)
+  (builtins.div 5 (-3))
+  (-(builtins.div 5 3))
+
+  (builtins.div 2147812578 (-28748))
+  (builtins.div (-2147812578) 28748)
+  (-(builtins.div 2147812578 28748))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemAt-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemAt-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemAt-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemAt-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemAt-catchable.nix
new file mode 100644
index 0000000000..97be4b013c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemAt-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.elemAt (throw "fred") 0)).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemat.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemat.exp
new file mode 100644
index 0000000000..3701c9d75f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemat.exp
@@ -0,0 +1 @@
+[ "foo" "bar" "baz" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemat.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemat.nix
new file mode 100644
index 0000000000..762adeebbf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-elemat.nix
@@ -0,0 +1,5 @@
+[
+  (builtins.elemAt [ "foo" "bar" "baz" ] 0)
+  (builtins.elemAt [ "foo" "bar" "baz" ] 1)
+  (builtins.elemAt [ "foo" "bar" "baz" ] 2)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-catchable.nix
new file mode 100644
index 0000000000..98d90b01bb
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.filter (_: throw "fred") [ 3 ])).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-propagate-catchable.exp
new file mode 100644
index 0000000000..48e341f4d8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-propagate-catchable.nix
new file mode 100644
index 0000000000..715a0ce34f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter-propagate-catchable.nix
@@ -0,0 +1 @@
+map (e: (builtins.tryEval e).success) [ (builtins.filter (builtins.throw "a") [ "" ]) (builtins.filter (x: true) (builtins.throw "b")) (builtins.filter (_: builtins.throw "x") [ "" ]) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter.exp
new file mode 100644
index 0000000000..fb94ebaa49
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter.exp
@@ -0,0 +1 @@
+[ [ 1 2 3 4 5 ] [ ] [ 2 2 2 ] [ [ 1 2 ] [ 3 4 ] ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter.nix
new file mode 100644
index 0000000000..b621fdb43e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-filter.nix
@@ -0,0 +1,13 @@
+[
+  (builtins.filter (_: true) [ 1 2 3 4 5 ])
+  (builtins.filter (_: false) [ 1 2 3 4 5 ])
+  (builtins.filter (x: x == 2) [ 1 2 1 2 1 2 ])
+
+  (builtins.filter (x: (builtins.length x) > 0) [
+    [ ]
+    [ 1 2 ]
+    [ ]
+    [ ]
+    [ 3 4 ]
+  ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-foldl-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-foldl-propagate-catchable.exp
new file mode 100644
index 0000000000..7bf6c63466
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-foldl-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false false true true ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-foldl-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-foldl-propagate-catchable.nix
new file mode 100644
index 0000000000..c11a21ce1e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-foldl-propagate-catchable.nix
@@ -0,0 +1,8 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.foldl' (builtins.throw "a") { } [{ } { } { }])
+  (builtins.foldl' (x: y: x // y) { } (builtins.throw "b"))
+  (builtins.foldl' (_: _: builtins.throw "x") { } [{ }])
+  (builtins.foldl' (x: y: x // y) (builtins.throw "x") [{ }])
+  (builtins.foldl' (x: y: x // y) { } [{ } { a = builtins.throw "z"; } { }])
+  (builtins.foldl' (x: y: x // y) { } [{ } { b = 3; a = builtins.throw "u"; } { }])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-from-json-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-from-json-propagate-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-from-json-propagate-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-from-json-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-from-json-propagate-catchable.nix
new file mode 100644
index 0000000000..aa973c1352
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-from-json-propagate-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.fromJSON (builtins.throw "a"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-function-args-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-function-args-propagate-catchable.exp
new file mode 100644
index 0000000000..bd52bff2fa
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-function-args-propagate-catchable.exp
@@ -0,0 +1 @@
+[ true false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-function-args-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-function-args-propagate-catchable.nix
new file mode 100644
index 0000000000..ca3e6772f2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-function-args-propagate-catchable.nix
@@ -0,0 +1,4 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.functionArgs (_: builtins.throw "a"))
+  (builtins.functionArgs (builtins.throw "b"))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-gen-list-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-gen-list-propagate-catchable.exp
new file mode 100644
index 0000000000..652df3d4da
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-gen-list-propagate-catchable.exp
@@ -0,0 +1 @@
+[ true false true ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-gen-list-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-gen-list-propagate-catchable.nix
new file mode 100644
index 0000000000..3d4739966e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-gen-list-propagate-catchable.nix
@@ -0,0 +1,5 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.genList (builtins.throw "a") 10)
+  (builtins.genList (i: "") (builtins.throw "b"))
+  (builtins.genList (i: builtins.throw "x") 5)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genList-function-strictness.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genList-function-strictness.exp
new file mode 100644
index 0000000000..06712ebc33
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genList-function-strictness.exp
@@ -0,0 +1 @@
+[ <LAMBDA> 0 1 2 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genList-function-strictness.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genList-function-strictness.nix
new file mode 100644
index 0000000000..e161e3b4af
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genList-function-strictness.nix
@@ -0,0 +1,8 @@
+let
+  self =
+    let
+      l = builtins.genList (builtins.head self) 3;
+    in
+    [ (x: x) ] ++ l;
+in
+self
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-pointer-equality.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-pointer-equality.exp
new file mode 100644
index 0000000000..87977137a5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-pointer-equality.exp
@@ -0,0 +1 @@
+[ { key = [ { foo = <LAMBDA>; } ]; val = null; } ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-pointer-equality.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-pointer-equality.nix
new file mode 100644
index 0000000000..f6ca340c09
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-pointer-equality.nix
@@ -0,0 +1,15 @@
+let
+  foo = x: x;
+in
+
+# key needs to be a list since it uses comparison, not equality checks:
+  # lists are comparable in Nix if all non-comparable items in them are equal (e.g.
+  # functions, attribute sets).
+builtins.genericClosure {
+  startSet = [
+    { key = [{ inherit foo; }]; val = null; }
+  ];
+  operator = { val, ... }: if val != null then [ ] else [
+    { key = [{ inherit foo; }]; val = throw "no pointer equality? πŸ₯ΊπŸ‘‰πŸ‘ˆ"; }
+  ];
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-propagate-catchable.exp
new file mode 100644
index 0000000000..6c89e78fc4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-propagate-catchable.exp
@@ -0,0 +1 @@
+{ success = false; value = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-propagate-catchable.nix
new file mode 100644
index 0000000000..1dfc0bb04f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-genericClosure-propagate-catchable.nix
@@ -0,0 +1 @@
+builtins.tryEval (builtins.genericClosure { operator = (_: [{ key = throw "lol"; }]); startSet = [{ key = "lol"; }]; })
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getAttr-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getAttr-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getAttr-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getAttr-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getAttr-catchable.nix
new file mode 100644
index 0000000000..ef4a042ffb
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getAttr-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.getAttr (throw "fred") "bob")).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getContext-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getContext-propagate-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getContext-propagate-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getContext-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getContext-propagate-catchable.nix
new file mode 100644
index 0000000000..70521665ca
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getContext-propagate-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.getContext (builtins.throw "a"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getattr.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getattr.exp
new file mode 100644
index 0000000000..89fa6c6810
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getattr.exp
@@ -0,0 +1 @@
+[ 1 2 3 { bar = { baz = 3; }; } ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getattr.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getattr.nix
new file mode 100644
index 0000000000..87a2adbcd3
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-getattr.nix
@@ -0,0 +1,6 @@
+[
+  (builtins.getAttr "foo" { foo = 1; bar = 2; baz = 3; })
+  (builtins.getAttr "bar" { foo = 1; bar = 2; baz = 3; })
+  (builtins.getAttr "baz" { foo = 1; bar = 2; baz = 3; })
+  (builtins.getAttr "foo" { foo = { bar = { baz = 3; }; }; })
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-group-by-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-group-by-propagate-catchable.exp
new file mode 100644
index 0000000000..48e341f4d8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-group-by-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-group-by-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-group-by-propagate-catchable.nix
new file mode 100644
index 0000000000..182601abb1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-group-by-propagate-catchable.nix
@@ -0,0 +1,5 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.groupBy (builtins.throw "a") [ "" ])
+  (builtins.groupBy (x: true) (builtins.throw "b"))
+  (builtins.groupBy (_: builtins.throw "x") [ "" ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-groupby-thunk.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-groupby-thunk.exp
new file mode 100644
index 0000000000..94649819ca
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-groupby-thunk.exp
@@ -0,0 +1 @@
+{ fred = [ { x = "fred"; y = "fred"; } ]; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-groupby-thunk.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-groupby-thunk.nix
new file mode 100644
index 0000000000..eaf48045f2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-groupby-thunk.nix
@@ -0,0 +1,5 @@
+builtins.groupBy
+  (v: v.x)
+  [ (rec { y = x; x = "fred"; }) ]
+
+
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasContext-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasContext-propagate-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasContext-propagate-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasContext-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasContext-propagate-catchable.nix
new file mode 100644
index 0000000000..0c02a82730
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasContext-propagate-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.hasContext (builtins.throw "a"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasattr.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasattr.exp
new file mode 100644
index 0000000000..541fe347cb
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasattr.exp
@@ -0,0 +1 @@
+[ true true true false false true false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasattr.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasattr.nix
new file mode 100644
index 0000000000..fb786b4f09
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hasattr.nix
@@ -0,0 +1,9 @@
+[
+  (builtins.hasAttr "foo" { foo = 1; bar = 2; baz = 3; })
+  (builtins.hasAttr "bar" { foo = 1; bar = 2; baz = 3; })
+  (builtins.hasAttr "baz" { foo = 1; bar = 2; baz = 3; })
+  (builtins.hasAttr "FOO" { foo = 1; bar = 2; baz = 3; })
+  (builtins.hasAttr "foo" { })
+  (builtins.hasAttr ("f" + "o" + "o") { foo = 1; })
+  (builtins.hasAttr ("b" + "a" + "r") { foo = 1; })
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp
new file mode 100644
index 0000000000..e00b80e561
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp
@@ -0,0 +1 @@
+[ "8a0614b4eaa4cffb7515ec101847e198" "8bd218cf61321d8aa05b3602b99f90d2d8cef3d6" "80ac06d74cb6c5d14af718ce8c3c1255969a1a595b76a3cf92354a95331a879a" "0edac513b6b0454705b553deda4c9b055da0939d26d2f73548862817ebeac5378cf64ff7a752ce1a0590a736735d3bbd9e8a7f04d93617cdf514313f5ab5baa4" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix
new file mode 100644
index 0000000000..aed723d367
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix
@@ -0,0 +1,6 @@
+[
+  (builtins.hashString "md5" "tvix")
+  (builtins.hashString "sha1" "tvix")
+  (builtins.hashString "sha256" "tvix")
+  (builtins.hashString "sha512" "tvix")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head-propagate-catchable.exp
new file mode 100644
index 0000000000..c3bb809c9f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head-propagate-catchable.nix
new file mode 100644
index 0000000000..0e69f2f6fc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head-propagate-catchable.nix
@@ -0,0 +1,4 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.head (builtins.throw "a"))
+  (builtins.head [ (builtins.throw "a") ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.exp
new file mode 100644
index 0000000000..afe288459f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.exp
@@ -0,0 +1 @@
+[ "foo" 1 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.nix
new file mode 100644
index 0000000000..1741a7aac4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.nix
@@ -0,0 +1,4 @@
+[
+  (builtins.head [ "foo" ])
+  (builtins.head [ 1 2 3 ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-isType-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-isType-propagate-catchable.exp
new file mode 100644
index 0000000000..cefd8652b4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-isType-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false false false false false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-isType-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-isType-propagate-catchable.nix
new file mode 100644
index 0000000000..28cf2351f6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-isType-propagate-catchable.nix
@@ -0,0 +1,14 @@
+let
+  isTypeFns = [
+    builtins.isAttrs
+    builtins.isBool
+    builtins.isFloat
+    builtins.isFunction
+    builtins.isInt
+    builtins.isList
+    builtins.isNull
+    builtins.isPath
+    builtins.isString
+  ];
+in
+map (fn: (builtins.tryEval (fn (builtins.throw "is type"))).success) isTypeFns
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length-catchable.nix
new file mode 100644
index 0000000000..037a4911ac
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.length (throw "fred"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length.exp
new file mode 100644
index 0000000000..e80eb6ef14
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length.exp
@@ -0,0 +1 @@
+[ 0 1 3 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length.nix
new file mode 100644
index 0000000000..6af6915f97
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-length.nix
@@ -0,0 +1,5 @@
+[
+  (builtins.length [ ])
+  (builtins.length [ 1 ])
+  (builtins.length [ "one" "two" "three" ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.exp
new file mode 100644
index 0000000000..31f4598bb5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.exp
@@ -0,0 +1 @@
+[ true true true true false false false false true true true true false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.nix
new file mode 100644
index 0000000000..cd2d0c209c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-lessThan.nix
@@ -0,0 +1,15 @@
+[
+  (builtins.lessThan 2 3)
+  (builtins.lessThan 2.0 3)
+  (builtins.lessThan 2 3.0)
+  (builtins.lessThan 2.0 3.0)
+  (builtins.lessThan 3 2)
+  (builtins.lessThan 3.0 2)
+  (builtins.lessThan 3 2.0)
+  (builtins.lessThan 3.0 2.0)
+  (builtins.lessThan 10 (builtins.add 9 2))
+  (builtins.lessThan (builtins.add 9 1) 11)
+  (builtins.lessThan (builtins.add 9 1) (builtins.add 9 2))
+  (builtins.lessThan "a" "b")
+  (builtins.lessThan "b" "a")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-list-to-attrs-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-list-to-attrs-propagate-catchable.exp
new file mode 100644
index 0000000000..5ee59ced83
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-list-to-attrs-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false true true false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-list-to-attrs-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-list-to-attrs-propagate-catchable.nix
new file mode 100644
index 0000000000..91b9f889bb
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-list-to-attrs-propagate-catchable.nix
@@ -0,0 +1,7 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.listToAttrs [{ name = builtins.throw "a"; value = "b"; }])
+  (builtins.listToAttrs [{ name = "a"; value = builtins.throw "b"; }])
+  (builtins.listToAttrs [{ name = "a"; value = "b"; } { name = "c"; value = builtins.throw "d"; }])
+  (builtins.listToAttrs [{ name = "a"; value = "b"; } (builtins.throw "e")])
+  (builtins.listToAttrs (builtins.throw "f"))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-function-strictness.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-function-strictness.exp
new file mode 100644
index 0000000000..050c2c4de5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-function-strictness.exp
@@ -0,0 +1 @@
+[ <LAMBDA> 2 "." 18 "https://github.com/NixOS/nix/issues/9779" "-.-" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-function-strictness.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-function-strictness.nix
new file mode 100644
index 0000000000..932d3d0eae
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-function-strictness.nix
@@ -0,0 +1,8 @@
+let
+  self =
+    let
+      l = builtins.map (builtins.head self) [ 2 "." 18 https://github.com/NixOS/nix/issues/9779 "-.-" ];
+    in
+    [ (x: x) ] ++ l;
+in
+self
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-propagate-catchable.exp
new file mode 100644
index 0000000000..652df3d4da
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-propagate-catchable.exp
@@ -0,0 +1 @@
+[ true false true ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-propagate-catchable.nix
new file mode 100644
index 0000000000..3ebb006c3f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map-propagate-catchable.nix
@@ -0,0 +1,5 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.map (builtins.throw "a") [ "" ])
+  (builtins.map (x: true) (builtins.throw "b"))
+  (builtins.map (_: builtins.throw "x") [ "" ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.exp
new file mode 100644
index 0000000000..6cf5304032
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.exp
@@ -0,0 +1 @@
+[ [ 1 2 3 4 5 ] [ 2 4 6 8 10 ] [ 2 4 6 8 10 ] [ 2 4 6 8 10 ] [ 1 2 3 4 5 ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.nix
new file mode 100644
index 0000000000..71b351fd55
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-map.nix
@@ -0,0 +1,19 @@
+[
+  # identity function
+  (builtins.map (x: x) [ 1 2 3 4 5 ])
+
+  # double stuff
+  (builtins.map (x: x * 2) [ 1 2 3 4 5 ])
+
+  # same but with a closure this time
+  (
+    let n = 2;
+    in builtins.map (x: x * n) [ 1 2 3 4 5 ]
+  )
+
+  # same, but with a builtin
+  (builtins.map (builtins.mul 2) [ 1 2 3 4 5 ])
+
+  # from global scope
+  (map (x: x) [ 1 2 3 4 5 ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mapAttrs-function-strictness.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mapAttrs-function-strictness.exp
new file mode 100644
index 0000000000..7e70748ffd
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mapAttrs-function-strictness.exp
@@ -0,0 +1 @@
+{ a = 1; b = 2; f = <LAMBDA>; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mapAttrs-function-strictness.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mapAttrs-function-strictness.nix
new file mode 100644
index 0000000000..2946d6de17
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mapAttrs-function-strictness.nix
@@ -0,0 +1,8 @@
+let
+  self =
+    let
+      s = builtins.mapAttrs self.f { a = 1; b = 2; };
+    in
+    { f = _: x: x; } // s;
+in
+self
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-match-propagate-catchables.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-match-propagate-catchables.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-match-propagate-catchables.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-match-propagate-catchables.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-match-propagate-catchables.nix
new file mode 100644
index 0000000000..8d00994b60
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-match-propagate-catchables.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.match (throw "foo") ".*")).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mul.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mul.exp
new file mode 100644
index 0000000000..e3e0f03a8a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mul.exp
@@ -0,0 +1 @@
+[ 36 0 0 14 42 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mul.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mul.nix
new file mode 100644
index 0000000000..2a8d6c4214
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-mul.nix
@@ -0,0 +1,7 @@
+[
+  (builtins.mul 4 9)
+  (builtins.mul 0 7)
+  (builtins.mul 7 0)
+  (builtins.mul 7 2)
+  (builtins.mul (builtins.mul 4 0.5) 21)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-parse-drv-name-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-parse-drv-name-propagate-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-parse-drv-name-propagate-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-parse-drv-name-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-parse-drv-name-propagate-catchable.nix
new file mode 100644
index 0000000000..c718727e74
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-parse-drv-name-propagate-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.parseDrvName (builtins.throw "a"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition-propagate-catchable.exp
new file mode 100644
index 0000000000..48e341f4d8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition-propagate-catchable.nix
new file mode 100644
index 0000000000..1960b1790f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition-propagate-catchable.nix
@@ -0,0 +1,5 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.partition (builtins.throw "a") [ "" ])
+  (builtins.partition (x: true) (builtins.throw "b"))
+  (builtins.partition (_: builtins.throw "x") [ "" ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition.exp
new file mode 100644
index 0000000000..d2390db4f5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition.exp
@@ -0,0 +1 @@
+[ { right = [ 1 2 3 4 5 ]; wrong = [ ]; } { right = [ ]; wrong = [ 1 2 3 4 5 ]; } { right = [ 2 ]; wrong = [ 1 3 4 5 ]; } { right = [ [ 1 2 ] [ 3 4 ] ]; wrong = [ [ 1 ] [ 2 ] [ 3 ] ]; } ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition.nix
new file mode 100644
index 0000000000..44022a9e0c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-partition.nix
@@ -0,0 +1,13 @@
+[
+  (builtins.partition (_: true) [ 1 2 3 4 5 ])
+  (builtins.partition (_: false) [ 1 2 3 4 5 ])
+  (builtins.partition (x: x == 2) [ 1 2 3 4 5 ])
+
+  (builtins.partition (x: (builtins.length x) > 1) [
+    [ 1 ]
+    [ 1 2 ]
+    [ 2 ]
+    [ 3 ]
+    [ 3 4 ]
+  ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-remove-attrs-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-remove-attrs-propagate-catchable.exp
new file mode 100644
index 0000000000..1e950b5aa2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-remove-attrs-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false true false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-remove-attrs-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-remove-attrs-propagate-catchable.nix
new file mode 100644
index 0000000000..8ca8a414aa
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-remove-attrs-propagate-catchable.nix
@@ -0,0 +1,6 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.removeAttrs (builtins.throw "a") [ "a" ])
+  (builtins.removeAttrs { a = { }; } (builtins.throw "b"))
+  (builtins.removeAttrs { a = builtins.throw "b"; } [ "a" ])
+  (builtins.removeAttrs { "${builtins.throw "c"}" = "b"; } [ "c" ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replace-strings-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replace-strings-propagate-catchable.exp
new file mode 100644
index 0000000000..cefd8652b4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replace-strings-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false false false false false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replace-strings-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replace-strings-propagate-catchable.nix
new file mode 100644
index 0000000000..ad9734ba9a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replace-strings-propagate-catchable.nix
@@ -0,0 +1,16 @@
+map (e: (builtins.tryEval e).success) [
+  # This one may be hard to read for non-experts.
+  # Replace strings is a special built-in compared to others in the sense
+  # it might attempt to lazily evaluate things upon successful replacements,
+  # so it would not be surprising that some of the non-replacements which could throw
+  # could be ignored by laziness. It is not the case though.
+  (builtins.replaceStrings [ "a" (builtins.throw "b") ] [ "c" "d" ] "ab")
+  (builtins.replaceStrings [ "a" (builtins.throw "b") ] [ "c" "d" ] "a")
+  (builtins.replaceStrings [ "a" "b" ] [ "c" (builtins.throw "d") ] "a")
+  (builtins.replaceStrings [ "a" "b" ] [ "c" (builtins.throw "d") ] "ab")
+  (builtins.replaceStrings [ "" ] [ (builtins.throw "d") ] "ab")
+  (builtins.replaceStrings [ "a" "" ] [ "b" (builtins.throw "d") ] "ab")
+  (builtins.replaceStrings (builtins.throw "z") [ ] "ab")
+  (builtins.replaceStrings [ ] (builtins.throw "z") "ab")
+  (builtins.replaceStrings [ ] [ ] (builtins.throw "z"))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replaceStrings.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replaceStrings.exp
new file mode 100644
index 0000000000..9f20496c7a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replaceStrings.exp
@@ -0,0 +1 @@
+[ "fabir" "a" "1a1" "ABC" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replaceStrings.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replaceStrings.nix
new file mode 100644
index 0000000000..eea3f87a2f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-replaceStrings.nix
@@ -0,0 +1,6 @@
+[
+  (builtins.replaceStrings [ "oo" "a" ] [ "a" "i" ] "foobar")
+  (builtins.replaceStrings [ "o" ] [ "a" ] "a")
+  (builtins.replaceStrings [ "" "" ] [ "1" "2" ] "a")
+  (builtins.replaceStrings [ "a" "b" "c" ] [ "A" "B" "C" ] "abc")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sort-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sort-propagate-catchable.exp
new file mode 100644
index 0000000000..1e950b5aa2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sort-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false true false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sort-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sort-propagate-catchable.nix
new file mode 100644
index 0000000000..66ca4b98ed
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sort-propagate-catchable.nix
@@ -0,0 +1,6 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.sort (builtins.throw "a") [ "" ])
+  (builtins.sort (x: y: true) (builtins.throw "b"))
+  (builtins.sort (_: _: builtins.throw "x") [ "" ])
+  (builtins.sort (_: _: builtins.throw "x") [ "" "" ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-split-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-split-propagate-catchable.exp
new file mode 100644
index 0000000000..c3bb809c9f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-split-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-split-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-split-propagate-catchable.nix
new file mode 100644
index 0000000000..1c86e9d6f8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-split-propagate-catchable.nix
@@ -0,0 +1,4 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.split (builtins.throw "regex") "abc")
+  (builtins.split "[^/]" (builtins.throw "string"))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitVersion.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitVersion.exp
new file mode 100644
index 0000000000..222a0093f5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitVersion.exp
@@ -0,0 +1 @@
+[ [ "1" "2" "3" ] [ "2" "3" "16" ] [ "22" "11" "pre" "408963" "823" "e" "2" "c" "9" "b" "0" "a" "0" ] [ "9" "4" "1" "rc" "1" ] [ "9" "4" "0" "20220721" ] [ "0" "1" "alpha" ] [ "unstable" "2022" "09" "20" ] [ "30" "pre" "9" ] [ "0" "pre+date=" "2021" "11" "30" ] [ "1" "2" "0" "_pre" "23" ] [ "0" "1" "0" "pre" "71" "_" "170" "f" "840" ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitVersion.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitVersion.nix
new file mode 100644
index 0000000000..4083e86714
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitVersion.nix
@@ -0,0 +1,13 @@
+[
+  (builtins.splitVersion "1.2.3")
+  (builtins.splitVersion "2.3.16")
+  (builtins.splitVersion "22.11pre408963.823e2c9b0a0")
+  (builtins.splitVersion "9.4.1-rc1")
+  (builtins.splitVersion "9.4.0.20220721")
+  (builtins.splitVersion "0.1-alpha")
+  (builtins.splitVersion "unstable-2022-09-20")
+  (builtins.splitVersion "30.pre9")
+  (builtins.splitVersion "0.pre+date=2021-11-30")
+  (builtins.splitVersion "1.2.0_pre23")
+  (builtins.splitVersion "0.1.0pre71_170f840")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitversion-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitversion-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitversion-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitversion-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitversion-catchable.nix
new file mode 100644
index 0000000000..0668ec7de2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-splitversion-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.splitVersion (throw "fred"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length-propagate-catchable.exp
new file mode 100644
index 0000000000..d181ebcff6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length-propagate-catchable.nix
new file mode 100644
index 0000000000..0904acd114
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length-propagate-catchable.nix
@@ -0,0 +1,4 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.stringLength (builtins.throw "a"))
+  # FIXME(raitobezarius): test coercions too.
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length.exp
new file mode 100644
index 0000000000..b019be4bfd
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length.exp
@@ -0,0 +1 @@
+[ 3 "hello" 9 4 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length.nix
new file mode 100644
index 0000000000..b7d51db3c5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-string-length.nix
@@ -0,0 +1,10 @@
+[
+  (builtins.stringLength "foo")
+  (let s = "hello"; in (builtins.substring 0 (builtins.stringLength s) s))
+  (builtins.stringLength ("foo" + "${"bar" + "baz"}"))
+
+  # feel free to delete this test case at any time, it's just to show: This is a
+  # thing at the moment. We may want to break compatibility with this aspect of
+  # the C++ Nix implementation at any time.
+  (builtins.stringLength "πŸ˜€")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sub.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sub.exp
new file mode 100644
index 0000000000..51842eccfa
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sub.exp
@@ -0,0 +1 @@
+[ -4 -3.1 -4.9 -4.7 -4 42 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sub.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sub.nix
new file mode 100644
index 0000000000..2929c4dddd
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-sub.nix
@@ -0,0 +1,8 @@
+[
+  (builtins.sub 7 11)
+  (builtins.sub 7.9 11)
+  (builtins.sub 7 11.9)
+  (builtins.sub 7.2 11.9)
+  (builtins.sub 7.9 11.9)
+  (builtins.sub (builtins.sub 123 23) 58)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-coerce.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-coerce.exp
new file mode 100644
index 0000000000..192548e949
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-coerce.exp
@@ -0,0 +1 @@
+"42"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-coerce.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-coerce.nix
new file mode 100644
index 0000000000..626ae1d1be
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-coerce.nix
@@ -0,0 +1,5 @@
+# builtins.substring uses string coercion internally
+
+builtins.substring 0 2 {
+  __toString = _: "4200";
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-negative-length.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-negative-length.exp
new file mode 100644
index 0000000000..e614d49940
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-negative-length.exp
@@ -0,0 +1 @@
+[ "SIP dial" "Lounge" " Lounge" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-negative-length.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-negative-length.nix
new file mode 100644
index 0000000000..062e2c0581
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring-negative-length.nix
@@ -0,0 +1,5 @@
+[
+  (builtins.substring 0 (-1) "SIP dial")
+  (builtins.substring 13 (-1) "Nichtraucher Lounge")
+  (builtins.substring 12 (-2) "Nichtraucher Lounge")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring.exp
new file mode 100644
index 0000000000..1682760228
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring.exp
@@ -0,0 +1 @@
+[ "tes" "testing" "" "estin" "ting" "" "" "" "" "est" "est" "est" "est" "est" "est" "" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring.nix
new file mode 100644
index 0000000000..f4ee82e273
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-substring.nix
@@ -0,0 +1,18 @@
+[
+  (builtins.substring 0 3 "testing")
+  (builtins.substring 0 300 "testing")
+  (builtins.substring 3 0 "testing")
+  (builtins.substring 1 5 "testing")
+  (builtins.substring 3 5 "testing")
+  (builtins.substring 300 300 "testing")
+  (builtins.substring 301 300 "testing")
+  (builtins.substring 0 0 "")
+  (builtins.substring 0 1 "")
+  (builtins.substring (builtins.add 0 1) 3 "testing")
+  (builtins.substring 1 (builtins.add 3 0) "testing")
+  (builtins.substring (builtins.add 0 1) (builtins.add 3 0) "testing")
+  (builtins.substring (builtins.add 0 1) (builtins.add 3 0) "testing")
+  (builtins.substring (builtins.add 0 1) (builtins.add 3 0) ("test" + "ing"))
+  (builtins.substring (builtins.add 0 1) (builtins.add 3 0) ("test" + "ing"))
+  (builtins.substring 300 (-10) "testing")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail-propagate-catchable.exp
new file mode 100644
index 0000000000..d4cd584d22
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false true true true ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail-propagate-catchable.nix
new file mode 100644
index 0000000000..b4e461e12b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail-propagate-catchable.nix
@@ -0,0 +1,6 @@
+map (e: (builtins.tryEval e).success) [
+  (builtins.tail (builtins.throw "a"))
+  (builtins.tail [ (builtins.throw "a") ])
+  (builtins.tail [ (builtins.throw "a") "a" ])
+  (builtins.tail [ (builtins.throw "a") (builtins.throw "a") ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail.exp
new file mode 100644
index 0000000000..b9e3aa1ef7
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail.exp
@@ -0,0 +1 @@
+[ [ ] [ 2 3 ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail.nix
new file mode 100644
index 0000000000..2be9496a98
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tail.nix
@@ -0,0 +1,4 @@
+[
+  (builtins.tail [ "foo" ])
+  (builtins.tail [ 1 2 3 ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-thunked-function-calls.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-thunked-function-calls.exp
new file mode 100644
index 0000000000..3d4204d5a8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-thunked-function-calls.exp
@@ -0,0 +1 @@
+[ 2 [ "Hans" "James" "Joachim" ] 2 [ "Clawdia" "Mynheer" ] 981 3 2 2 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-thunked-function-calls.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-thunked-function-calls.nix
new file mode 100644
index 0000000000..3a1c0b9821
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-thunked-function-calls.nix
@@ -0,0 +1,31 @@
+[
+  # This is independent of builtins
+  (builtins.length [ (builtins.throw "Ferge") (builtins.throw "Wehsal") ])
+  (builtins.attrNames {
+    Hans = throw "Castorp";
+    Joachim = throw "Ziemßen";
+    James = "Tienappel";
+  })
+
+  (builtins.length (builtins.map builtins.throw [ "Settembrini" "Naphta" ]))
+
+  (builtins.attrNames (builtins.mapAttrs builtins.throw {
+    Clawdia = "Chauchat";
+    Mynheer = "Peeperkorn";
+  }))
+
+  (builtins.length (builtins.genList (builtins.add "Marusja") 981))
+  (builtins.length (builtins.genList builtins.throw 3))
+
+  # These are hard to get wrong since the outer layer needs to be forced anyways
+  (builtins.length (builtins.genericClosure {
+    startSet = [
+      { key = 1; initial = true; }
+    ];
+    operator = { key, initial, ... }:
+      if initial
+      then [{ key = key - 1; initial = false; value = throw "lol"; }]
+      else [ ];
+  }))
+  (builtins.length (builtins.concatMap (m: [ m (builtins.throw m) ]) [ "Marusja" ]))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-json-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-json-propagate-catchable.exp
new file mode 100644
index 0000000000..ca00e3c049
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-json-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-json-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-json-propagate-catchable.nix
new file mode 100644
index 0000000000..8ae5e48e97
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-json-propagate-catchable.nix
@@ -0,0 +1,14 @@
+map (e: (builtins.tryEval (builtins.toJSON e)).success) [
+  (builtins.throw "a")
+  {
+    a = builtins.throw "attribute a";
+  }
+  {
+    a.b.c.d.e.f.g.h.i = builtins.throw "deep i";
+  }
+  {
+    x = 32;
+    y = builtins.throw "second argument";
+  }
+  # FIXME(raitobezarius): we would like to test coercions, i.e. `toFile` and `derivation` containing throwables.
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-path-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-path-propagate-catchable.exp
new file mode 100644
index 0000000000..c3bb809c9f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-path-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-path-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-path-propagate-catchable.nix
new file mode 100644
index 0000000000..808fb8c46e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-path-propagate-catchable.nix
@@ -0,0 +1,5 @@
+map (e: (builtins.tryEval (builtins.toPath e)).success) [
+  (builtins.throw "a")
+  (./xyz + (builtins.throw "p"))
+  # FIXME: test derivations and files.
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-string-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-string-propagate-catchable.exp
new file mode 100644
index 0000000000..ca00e3c049
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-string-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-string-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-string-propagate-catchable.nix
new file mode 100644
index 0000000000..7b19ff53c3
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-string-propagate-catchable.nix
@@ -0,0 +1,7 @@
+map (e: (builtins.tryEval (builtins.toString e)).success) [
+  (builtins.throw "a")
+  [ (builtins.throw "a") ]
+  [ "abc" (builtins.throw "a") ]
+  "abc${builtins.throw "c"}"
+  # FIXME: test derivations and files.
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-xml-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-xml-propagate-catchable.exp
new file mode 100644
index 0000000000..366f7adf0d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-xml-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false false false false true false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-xml-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-xml-propagate-catchable.nix
new file mode 100644
index 0000000000..80d9e688be
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-to-xml-propagate-catchable.nix
@@ -0,0 +1,15 @@
+map (e: (builtins.tryEval (builtins.toXML e)).success) [
+  (builtins.throw "a")
+  [ (builtins.throw "a") ]
+  [ "abc" (builtins.throw "a") ]
+  "abc${builtins.throw "c"}"
+  (_: builtins.throw "d")
+  {
+    u = builtins.throw "x";
+    v = "a";
+  }
+  {
+    u.i.w.x.z = builtins.throw "n";
+  }
+  # FIXME: test derivations and files.
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-toString.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-toString.exp
new file mode 100644
index 0000000000..cd5a6c0d54
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-toString.exp
@@ -0,0 +1 @@
+[ "" " " " /deep/thought" " 2  3" " flat" "1" "4.200000" "" "" "1" "foo" "/etc" "Hello World" "Hello World" "1" "out" "2" "    /deep/thought  2  3  flat 1 4.200000   1 foo /etc Hello World Hello World 1 out 2" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-toString.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-toString.nix
new file mode 100644
index 0000000000..eb8011158f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-toString.nix
@@ -0,0 +1,28 @@
+let
+  toStringableSet = {
+    __toString = self: self.content;
+    content = "Hello World";
+  };
+
+  toStringExamples = [
+    null
+    [ null false ]
+    [ null /deep/thought ]
+    [ [ null 2 ] null 3 ]
+    [ false "flat" ]
+    1
+    4.2
+    null
+    false
+    true
+    "foo"
+    /etc
+    toStringableSet
+    { __toString = _: toStringableSet; }
+    { __toString = _: true; }
+    { outPath = "out"; }
+    { outPath = { outPath = { __toString = _: 2; }; }; }
+  ];
+in
+
+(builtins.map toString toStringExamples) ++ [ (toString toStringExamples) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-literals.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-literals.exp
new file mode 100644
index 0000000000..0a274c201f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-literals.exp
@@ -0,0 +1 @@
+"[42,\"hello\",13.37,[],[1,2,3],{},{\"name\":\"foo\",\"value\":42},{\"foo\":42}]"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-literals.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-literals.nix
new file mode 100644
index 0000000000..12e8c03b17
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-literals.nix
@@ -0,0 +1,11 @@
+# tests serialisation of literal data
+builtins.toJSON [
+  42
+  "hello"
+  13.37
+  [ ]
+  [ 1 2 3 ]
+  { }
+  { name = "foo"; value = 42; }
+  { foo = 42; }
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath-nested.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath-nested.exp
new file mode 100644
index 0000000000..69667de5a1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath-nested.exp
@@ -0,0 +1 @@
+"{\"a\":40,\"b\":2}"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath-nested.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath-nested.nix
new file mode 100644
index 0000000000..70755c8c6d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath-nested.nix
@@ -0,0 +1,8 @@
+# Attribute sets with an `outPath` can contain _any_ serialisable
+# value in that field.
+builtins.toJSON {
+  outPath = {
+    a = 40;
+    b = 2;
+  };
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath.exp
new file mode 100644
index 0000000000..82dd081798
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath.exp
@@ -0,0 +1 @@
+"\"/nix/store/jzka5ndnygkkfjfvpqwjipqp75lhz138-emacs-28.2\""
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath.nix
new file mode 100644
index 0000000000..7f9d95ac60
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-outpath.nix
@@ -0,0 +1,5 @@
+# Attribute sets with an `outPath` have that outPath itself serialised
+# to string.
+builtins.toJSON {
+  outPath = "/nix/store/jzka5ndnygkkfjfvpqwjipqp75lhz138-emacs-28.2";
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-thunks.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-thunks.exp
new file mode 100644
index 0000000000..9ccd94224b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-thunks.exp
@@ -0,0 +1 @@
+"[42,42,\"42\"]"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-thunks.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-thunks.nix
new file mode 100644
index 0000000000..16234ab451
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-thunks.nix
@@ -0,0 +1,9 @@
+let
+  a = b * 2;
+  b = 21;
+in
+builtins.toJSON [
+  a
+  ((n: n * 2) 21)
+  (builtins.toJSON a)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-tostring.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-tostring.exp
new file mode 100644
index 0000000000..2661fd257b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-tostring.exp
@@ -0,0 +1 @@
+"\"it's 42\""
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-tostring.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-tostring.nix
new file mode 100644
index 0000000000..ec6f8d947c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-tojson-tostring.nix
@@ -0,0 +1,8 @@
+# Attribute sets with a `__toString` attribute JSON-serialise with a
+# string coercion of the function call result.
+
+builtins.toJSON {
+  __toString = self: "it's " + (builtins.toString (self.x * self.y));
+  x = 21;
+  y = 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of-propagate-catchable.exp
new file mode 100644
index 0000000000..41f22d3ee4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false true true false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of-propagate-catchable.nix
new file mode 100644
index 0000000000..4b70609bbf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of-propagate-catchable.nix
@@ -0,0 +1,9 @@
+map (e: (builtins.tryEval (builtins.typeOf e)).success) [
+  (builtins.throw "a")
+  {
+    a = builtins.throw "b";
+  }
+  [ (builtins.throw "c") ]
+  (./xyz + (builtins.throw "p"))
+  # FIXME: test derivations and files.
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of.exp
new file mode 100644
index 0000000000..1ea054fc2d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of.exp
@@ -0,0 +1 @@
+[ "null" "bool" "bool" "int" "int" "float" "string" "string" "set" "set" "list" "lambda" "path" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of.nix
new file mode 100644
index 0000000000..fa42c6008e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-of.nix
@@ -0,0 +1,22 @@
+let
+  fix = f: let x = f x; in x;
+in
+
+fix (self:
+[
+  (builtins.typeOf null)
+  (builtins.typeOf true)
+  (builtins.typeOf (true && false))
+  (builtins.typeOf 12)
+  (builtins.typeOf (builtins.add 21 21))
+  (builtins.typeOf 1.2)
+  (builtins.typeOf "foo")
+  (builtins.typeOf "${"foo" + "bar"}baz")
+  (builtins.typeOf { })
+  # (builtins.typeOf { foo.bar = 32; }.foo) # TODO: re-enable when nested keys are done
+  (builtins.typeOf ({ name = "foo"; value = 13; } // { name = "bar"; }))
+  (builtins.typeOf self)
+  (builtins.typeOf fix)
+  (builtins.typeOf /nix/store)
+]
+)
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-predicates.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-predicates.exp
new file mode 100644
index 0000000000..724c1f9c34
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-predicates.exp
@@ -0,0 +1 @@
+[ true true false true true false true true false true true false true true false true true false true true false true true false true true true false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-predicates.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-predicates.nix
new file mode 100644
index 0000000000..e67b219159
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-type-predicates.nix
@@ -0,0 +1,34 @@
+let
+  # apply is thunked, so we can create a thunked value using the identity function
+  thunk = x: x;
+in
+[
+  (builtins.isAttrs { bar = throw "baz"; })
+  (builtins.isAttrs (thunk { foo = 13; }))
+  (builtins.isAttrs (thunk 123))
+  (builtins.isBool true)
+  (builtins.isBool (thunk false))
+  (builtins.isBool (thunk "lol"))
+  (builtins.isFloat 1.2)
+  (builtins.isFloat (thunk (1 * 1.0)))
+  (builtins.isFloat 1)
+  (builtins.isFunction thunk)
+  (builtins.isFunction (thunk thunk))
+  (builtins.isFunction { })
+  (builtins.isInt 1)
+  (builtins.isInt (thunk 42))
+  (builtins.isInt 1.0)
+  (builtins.isList [ (throw "oh no") (abort "it's over") ])
+  (builtins.isList (thunk [ 21 21 ]))
+  (builtins.isList (thunk { }))
+  (builtins.isNull null)
+  (builtins.isNull (thunk null))
+  (builtins.isNull 42)
+  (builtins.isPath ./relative)
+  (builtins.isPath (thunk /absolute))
+  (builtins.isPath "/not/a/path")
+  (builtins.isString "simple")
+  (builtins.isString "${{ outPath = "coerced"; }}")
+  (builtins.isString "hello ${"interpolation"}")
+  (builtins.isString true)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-unsafe-discard-string-context-propagate-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-unsafe-discard-string-context-propagate-catchable.exp
new file mode 100644
index 0000000000..d181ebcff6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-unsafe-discard-string-context-propagate-catchable.exp
@@ -0,0 +1 @@
+[ false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-unsafe-discard-string-context-propagate-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-unsafe-discard-string-context-propagate-catchable.nix
new file mode 100644
index 0000000000..8ef5f35a17
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-unsafe-discard-string-context-propagate-catchable.nix
@@ -0,0 +1,4 @@
+map (e: (builtins.tryEval (builtins.unsafeDiscardStringContext e)).success) [
+  (builtins.throw "a")
+  # FIXME: test derivations with throwables.
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-double-throw.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-double-throw.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-double-throw.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-double-throw.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-double-throw.nix
new file mode 100644
index 0000000000..7c2132b502
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-double-throw.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.throw (builtins.throw "a"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-attrNames.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-attrNames.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-attrNames.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-attrNames.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-attrNames.nix
new file mode 100644
index 0000000000..75531d56a3
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-attrNames.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.attrNames (throw "fred"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-inequality.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-inequality.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-inequality.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-inequality.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-inequality.nix
new file mode 100644
index 0000000000..93836bd8fe
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-inequality.nix
@@ -0,0 +1 @@
+(builtins.tryEval (throw "bob" != 3)).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-intersectattrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-intersectattrs.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-intersectattrs.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-intersectattrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-intersectattrs.nix
new file mode 100644
index 0000000000..a06a383342
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-intersectattrs.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.intersectAttrs (throw "fred") { })).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-string-interpolation.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-string-interpolation.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-string-interpolation.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-string-interpolation.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-string-interpolation.nix
new file mode 100644
index 0000000000..7a0cf16709
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-string-interpolation.nix
@@ -0,0 +1 @@
+(builtins.tryEval ("${toString 3}  ${throw "bob"}")).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-update-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-update-attrs.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-update-attrs.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-update-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-update-attrs.nix
new file mode 100644
index 0000000000..38a9169034
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-in-update-attrs.nix
@@ -0,0 +1 @@
+(builtins.tryEval (throw "bob" // { })).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-passed-to-function-with-formals.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-passed-to-function-with-formals.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-passed-to-function-with-formals.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-passed-to-function-with-formals.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-passed-to-function-with-formals.nix
new file mode 100644
index 0000000000..df6726db76
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-catchable-passed-to-function-with-formals.nix
@@ -0,0 +1 @@
+(builtins.tryEval (({ fred }: "bob") (throw "3"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-ceil.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-ceil.exp
new file mode 100644
index 0000000000..dffbbe59f0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-ceil.exp
@@ -0,0 +1 @@
+[ 4 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-ceil.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-ceil.nix
new file mode 100644
index 0000000000..5835bf829b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-ceil.nix
@@ -0,0 +1 @@
+[ (builtins.ceil 3.4) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-closure-pointer-compare.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-pointer-compare.exp
new file mode 100644
index 0000000000..c3bb809c9f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-pointer-compare.exp
@@ -0,0 +1 @@
+[ false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-closure-pointer-compare.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-pointer-compare.nix
new file mode 100644
index 0000000000..639191be5d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-pointer-compare.nix
@@ -0,0 +1,14 @@
+# For an explanation of this behavior see //tvix/docs/value-pointer-equality.md
+let
+  g = x:
+    owo: "th" + x;
+in
+[
+  (
+    { q = g "ia"; } == { q = g ("i" + "a"); }
+  )
+
+  (
+    [ (g "ia") ] == [ (g ("i" + "a")) ]
+  )
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-closure-self.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-self.exp
new file mode 100644
index 0000000000..be54b4b4e3
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-self.exp
@@ -0,0 +1 @@
+"done"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-closure-self.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-self.nix
new file mode 100644
index 0000000000..7be6660009
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-self.nix
@@ -0,0 +1,5 @@
+let
+  # self-recursive function should be able to close over itself
+  f = n: if n <= 0 then "done" else f (n - 1);
+in
+f 10
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-closure-with-shadowing.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-with-shadowing.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-with-shadowing.exp
@@ -0,0 +1 @@
+1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-closure-with-shadowing.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-with-shadowing.nix
new file mode 100644
index 0000000000..2c4de65e76
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-closure-with-shadowing.nix
@@ -0,0 +1,14 @@
+# If a closure closes over a variable that is statically known *and*
+# available dynamically through `with`, the statically known one must
+# have precedence.
+
+let
+  # introduce statically known `a` (this should be the result)
+  a = 1;
+in
+
+# introduce some closure depth to force both kinds of upvalue
+  # resolution, and introduce a dynamically known `a` within the
+  # closures
+let f = b: with { a = 2; }; c: a + b + c;
+in f 0 0
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.exp
new file mode 100644
index 0000000000..95a0e7378b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.exp
@@ -0,0 +1 @@
+{ eq = false; ge = false; gt = false; le = false; lt = false; ne = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.nix
new file mode 100644
index 0000000000..2b511f56ee
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-false.nix
@@ -0,0 +1,8 @@
+{
+  eq = 6.9 == 4.2;
+  ne = 4.2 != 4.2;
+  lt = 2.5 < 1.5;
+  le = 2.5 <= 1.5;
+  gt = 1.5 > 2.5;
+  ge = 1.5 >= 2.5;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.exp
new file mode 100644
index 0000000000..9160829dde
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.exp
@@ -0,0 +1 @@
+{ eq = true; ge = true; gt = true; le = true; lt = true; ne = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.nix
new file mode 100644
index 0000000000..c505a85b1f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-float-true.nix
@@ -0,0 +1,8 @@
+{
+  eq = 4.2 == 4.2;
+  ne = 6.9 != 4.2;
+  lt = 1.5 < 2.5;
+  le = 2.5 <= 2.5;
+  gt = 2.3 > 1.2;
+  ge = 2.3 >= 2.3;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.exp
new file mode 100644
index 0000000000..95a0e7378b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.exp
@@ -0,0 +1 @@
+{ eq = false; ge = false; gt = false; le = false; lt = false; ne = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.nix
new file mode 100644
index 0000000000..7d6b30419f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-false.nix
@@ -0,0 +1,8 @@
+{
+  eq = 69 == 42;
+  ne = 42 != 42;
+  lt = 2 < 1;
+  le = 2 <= 1;
+  gt = 1 > 2;
+  ge = 1 >= 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.exp
new file mode 100644
index 0000000000..9160829dde
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.exp
@@ -0,0 +1 @@
+{ eq = true; ge = true; gt = true; le = true; lt = true; ne = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.nix
new file mode 100644
index 0000000000..0bf474e53f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-int-true.nix
@@ -0,0 +1,8 @@
+{
+  eq = 42 == 42;
+  ne = 69 != 42;
+  lt = 1 < 2;
+  le = 2 <= 2;
+  gt = 2 > 1;
+  ge = 2 >= 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.exp
new file mode 100644
index 0000000000..95a0e7378b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.exp
@@ -0,0 +1 @@
+{ eq = false; ge = false; gt = false; le = false; lt = false; ne = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.nix
new file mode 100644
index 0000000000..61b206c033
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-false.nix
@@ -0,0 +1,8 @@
+{
+  eq = 6.9 == 4;
+  ne = 4.0 != 4;
+  lt = 2.5 < 1;
+  le = 2 <= 1.5;
+  gt = 1 > 1.1;
+  ge = 1.5 >= 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.exp
new file mode 100644
index 0000000000..9160829dde
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.exp
@@ -0,0 +1 @@
+{ eq = true; ge = true; gt = true; le = true; lt = true; ne = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.nix
new file mode 100644
index 0000000000..ad77074710
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-num-true.nix
@@ -0,0 +1,8 @@
+{
+  eq = 42.0 == 42;
+  ne = 6.9 != 4;
+  lt = 1.5 < 2;
+  le = 2.0 <= 2.0;
+  gt = 1.1 > 1;
+  ge = 2.3 >= 2.3;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.exp
new file mode 100644
index 0000000000..95a0e7378b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.exp
@@ -0,0 +1 @@
+{ eq = false; ge = false; gt = false; le = false; lt = false; ne = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.nix
new file mode 100644
index 0000000000..b5773a21d3
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-false.nix
@@ -0,0 +1,8 @@
+{
+  eq = "test" == "not test";
+  ne = "test" != "test";
+  lt = "bcd" < "abc";
+  le = "bcd" <= "abc";
+  gt = "abc" > "bcd";
+  ge = "abc" >= "bcd";
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.exp
new file mode 100644
index 0000000000..9160829dde
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.exp
@@ -0,0 +1 @@
+{ eq = true; ge = true; gt = true; le = true; lt = true; ne = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.nix
new file mode 100644
index 0000000000..172d2237e9
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-cmp-str-true.nix
@@ -0,0 +1,8 @@
+{
+  eq = "test" == "test";
+  ne = "test" != "not test";
+  lt = "abc" < "bcd";
+  le = "bcd" <= "bcd";
+  gt = "bcd" > "abc";
+  ge = "bcd" >= "bcd";
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-coerce-opadd.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-coerce-opadd.exp
new file mode 100644
index 0000000000..d874518a37
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-coerce-opadd.exp
@@ -0,0 +1 @@
+[ "lordnikon" "zerocool" /tmp/31337h4x0r "fooblah" "blahfoo" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-coerce-opadd.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-coerce-opadd.nix
new file mode 100644
index 0000000000..e79e521f8a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-coerce-opadd.nix
@@ -0,0 +1,7 @@
+[
+  ({ __toString = _: "lord"; } + "nikon")
+  ("zero" + { __toString = _: "cool"; })
+  (/tmp/31337 + "h4x0r")
+  ("foo" + { outPath = "blah"; })
+  ({ outPath = "blah"; } + "foo")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-compare-lists.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-compare-lists.exp
new file mode 100644
index 0000000000..3b7fd39819
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-compare-lists.exp
@@ -0,0 +1 @@
+[ false true true true false true false false false true false false false true true ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-compare-lists.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-compare-lists.nix
new file mode 100644
index 0000000000..1837f4d820
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-compare-lists.nix
@@ -0,0 +1,17 @@
+[
+  ([ 1 2 ] < [ 1 ])
+  ([ 1 2 ] < [ 2 3 ])
+  ([ 1 2 ] < [ 2 ])
+  ([ 1 2 ] < [ 1 2 3 ])
+  ([ 3 4 ] < [ 1 ])
+  ([ 1 2 ] > [ 1 ])
+  ([ 1 2 ] > [ 2 3 ])
+  ([ 1 2 ] > [ 2 ])
+  ([ 1 2 ] > [ 1 2 3 ])
+  ([ 3 4 ] > [ 1 ])
+  ([ 1 2 ] <= [ 1 ])
+  ([ 1 2 ] >= [ 2 3 ])
+  ([ 1 2 ] >= [ 2 ])
+  ([ 1 2 ] <= [ 1 2 3 ])
+  ([ 3 4 ] >= [ 1 ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-compare-ordering-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-compare-ordering-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-compare-ordering-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-compare-ordering-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-compare-ordering-catchable.nix
new file mode 100644
index 0000000000..9000160e57
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-compare-ordering-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval ((throw "x") < 3)).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-concat-lists.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-concat-lists.exp
new file mode 100644
index 0000000000..3bed31f76e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-concat-lists.exp
@@ -0,0 +1 @@
+[ 1 2 3 4 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-concat-lists.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-concat-lists.nix
new file mode 100644
index 0000000000..de332cd29f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-concat-lists.nix
@@ -0,0 +1 @@
+[ 1 2 ] ++ [ 3 4 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-concat-strings.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-concat-strings.exp
new file mode 100644
index 0000000000..cd4bc1ab64
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-concat-strings.exp
@@ -0,0 +1 @@
+"hello world"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-concat-strings.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-concat-strings.nix
new file mode 100644
index 0000000000..1fc7089299
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-concat-strings.nix
@@ -0,0 +1 @@
+"hello " + "world"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-concatmap.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-concatmap.exp
new file mode 100644
index 0000000000..14d804aa22
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-concatmap.exp
@@ -0,0 +1 @@
+[ "a" "z" "b" "z" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-concatmap.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-concatmap.nix
new file mode 100644
index 0000000000..cff39b05e6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-concatmap.nix
@@ -0,0 +1 @@
+(builtins.concatMap (x: [ x ] ++ [ "z" ]) [ "a" "b" ])
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-concatstringssep.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-concatstringssep.exp
new file mode 100644
index 0000000000..93987647ff
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-concatstringssep.exp
@@ -0,0 +1 @@
+[ "" "foobarxyzzy" "foo, bar, xyzzy" "foo" "" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-concatstringssep.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-concatstringssep.nix
new file mode 100644
index 0000000000..cd94ca99b4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-concatstringssep.nix
@@ -0,0 +1,9 @@
+with builtins;
+
+[
+  (concatStringsSep "" [ ])
+  (concatStringsSep "" [ "foo" "bar" "xyzzy" ])
+  (concatStringsSep ", " [ "foo" "bar" "xyzzy" ])
+  (concatStringsSep ", " [ "foo" ])
+  (concatStringsSep ", " [ ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-contains-nested-non-set.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-contains-nested-non-set.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-contains-nested-non-set.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-contains-nested-non-set.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-contains-nested-non-set.nix
new file mode 100644
index 0000000000..361ba91445
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-contains-nested-non-set.nix
@@ -0,0 +1,3 @@
+# ? operator should work even if encountering a non-set value on the
+# walk
+{ a.b = 42; } ? a.b.c
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-contains-non-set.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-contains-non-set.exp
new file mode 100644
index 0000000000..ca00e3c049
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-contains-non-set.exp
@@ -0,0 +1 @@
+[ false false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-contains-non-set.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-contains-non-set.nix
new file mode 100644
index 0000000000..c086759f45
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-contains-non-set.nix
@@ -0,0 +1,3 @@
+# Nix allows using the ? operator on non-set types, in which case it
+# should always return false.
+[ (123 ? key) ("foo" ? key) (null ? key) ([ "key" ] ? key) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-attrs.exp
new file mode 100644
index 0000000000..7cf54d9596
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-attrs.exp
@@ -0,0 +1 @@
+{ a = { b = { c = { d = { e = { f = { g = "deep!"; }; }; }; }; }; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-attrs.nix
new file mode 100644
index 0000000000..91649d0c6d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-attrs.nix
@@ -0,0 +1 @@
+{ a.b.c.d.e.f.g = "deep!"; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with-closure.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with-closure.exp
new file mode 100644
index 0000000000..3bed31f76e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with-closure.exp
@@ -0,0 +1 @@
+[ 1 2 3 4 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with-closure.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with-closure.nix
new file mode 100644
index 0000000000..f65e8ee537
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with-closure.nix
@@ -0,0 +1,21 @@
+# This convoluted test constructs a situation in which dynamically
+# resolved upvalues refer `with` blocks introduced at different lambda
+# context boundaries, i.e. the access to a, b in the innermost closure
+# must be threaded through upvalues in several levels.
+
+(_:
+with { a = 1; b = 1; };
+
+_:
+with { b = 2; c = 2; };
+
+_:
+with { c = 3; d = 3; };
+
+_:
+with { d = 4; };
+
+[ a b c d ]) null
+  null
+  null
+  null
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with.exp
new file mode 100644
index 0000000000..3bed31f76e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with.exp
@@ -0,0 +1 @@
+[ 1 2 3 4 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with.nix
new file mode 100644
index 0000000000..7f1128b670
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deeply-nested-with.nix
@@ -0,0 +1,6 @@
+with { a = 1; b = 1; };
+with { b = 2; c = 2; };
+with { c = 3; d = 3; };
+with { d = 4; };
+
+[ a b c d ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deepseq.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-deepseq.exp
new file mode 100644
index 0000000000..8d38505c16
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deepseq.exp
@@ -0,0 +1 @@
+456
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deepseq.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-deepseq.nix
new file mode 100644
index 0000000000..53aa4b1dc2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deepseq.nix
@@ -0,0 +1 @@
+builtins.deepSeq (let as = { x = 123; y = as; }; in as) 456
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-unary-formals.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-unary-formals.exp
new file mode 100644
index 0000000000..5993db7ccc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-unary-formals.exp
@@ -0,0 +1 @@
+[ false -2 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-unary-formals.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-unary-formals.nix
new file mode 100644
index 0000000000..1fbb3e853a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-unary-formals.nix
@@ -0,0 +1,6 @@
+# Application of unary operators on deferred formals arguments (via
+# defaulting), see also b/255.
+[
+  (({ b ? !a, a }: b) { a = true; })
+  (({ b ? -a, a }: b) { a = 2; })
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-with.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-with.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-with.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-with.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-with.nix
new file mode 100644
index 0000000000..ccafdf74ce
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-deferred-with.nix
@@ -0,0 +1,9 @@
+# Tests using `with` on a set that does not yet exist on the stack.
+
+let
+  result = with set; value;
+  set = {
+    value = 42;
+  };
+in
+result
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-dirof.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-dirof.exp
new file mode 100644
index 0000000000..ff464e4c30
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-dirof.exp
@@ -0,0 +1 @@
+[ /foo "." "foo//" "foo" "." "." / "/" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-dirof.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-dirof.nix
new file mode 100644
index 0000000000..13cf473205
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-dirof.nix
@@ -0,0 +1,10 @@
+[
+  (builtins.dirOf /foo/bar)
+  (builtins.dirOf "foo")
+  (builtins.dirOf "foo///")
+  (builtins.dirOf "foo/bar")
+  (builtins.dirOf "./.")
+  (builtins.dirOf "")
+  (builtins.dirOf /.)
+  (builtins.toString (builtins.dirOf /.))
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-elem.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-elem.exp
new file mode 100644
index 0000000000..3cf6c0e962
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-elem.exp
@@ -0,0 +1 @@
+[ true false 30 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-elem.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-elem.nix
new file mode 100644
index 0000000000..71ea7a4ed0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-elem.nix
@@ -0,0 +1,6 @@
+with import ./lib.nix;
+
+let xs = range 10 40; in
+
+[ (builtins.elem 23 xs) (builtins.elem 42 xs) (builtins.elemAt xs 20) ]
+
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-empty-rec-inherit.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-empty-rec-inherit.exp
new file mode 100644
index 0000000000..ffcd4415b0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-empty-rec-inherit.exp
@@ -0,0 +1 @@
+{ }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-empty-rec-inherit.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-empty-rec-inherit.nix
new file mode 100644
index 0000000000..a1181431de
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-empty-rec-inherit.nix
@@ -0,0 +1 @@
+rec { inherit; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-eq-float.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-float.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-float.exp
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-eq-float.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-float.nix
new file mode 100644
index 0000000000..398f4a9dfc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-float.nix
@@ -0,0 +1 @@
+4.2 == 4.2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-eq-int.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-int.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-int.exp
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-eq-int.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-int.nix
new file mode 100644
index 0000000000..dc52ba112a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-int.nix
@@ -0,0 +1 @@
+42 == 42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-eq-nested-list.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-nested-list.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-nested-list.exp
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-eq-nested-list.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-nested-list.nix
new file mode 100644
index 0000000000..cc39e2415f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-eq-nested-list.nix
@@ -0,0 +1 @@
+[ [ "f" "" ] ] == [ [ "f" "" ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-equality-tolerate-catchable-in-type-field.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-equality-tolerate-catchable-in-type-field.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-equality-tolerate-catchable-in-type-field.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-equality-tolerate-catchable-in-type-field.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-equality-tolerate-catchable-in-type-field.nix
new file mode 100644
index 0000000000..6bd018b68d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-equality-tolerate-catchable-in-type-field.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.elem { type = rec { x = throw "fred"; }.x; } [{ type = 3; }])).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-escape-string-correct-char-boundaries.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-escape-string-correct-char-boundaries.exp
new file mode 100644
index 0000000000..d889063f9a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-escape-string-correct-char-boundaries.exp
@@ -0,0 +1 @@
+"πŸ’­(\":thonking:\")"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-escape-string-correct-char-boundaries.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-escape-string-correct-char-boundaries.nix
new file mode 100644
index 0000000000..49f4b62731
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-escape-string-correct-char-boundaries.nix
@@ -0,0 +1,6 @@
+# Regression test for a bug where tvix would crash in nix_escape_string
+# because it counted the string position by unicode code point count,
+# but then used it as a byte index for slicing. Consequently, it would
+# try slicing πŸ’­ in half, thinking the first element to be escaped was
+# at byte index 2 (i.e. the quote).
+"πŸ’­(\":thonking:\")"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.exp
new file mode 100644
index 0000000000..aa98a082a8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.exp
@@ -0,0 +1 @@
+{ "3" = 3; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.nix
new file mode 100644
index 0000000000..aa98a082a8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.nix
@@ -0,0 +1 @@
+{ "3" = 3; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-fib.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-fib.exp
new file mode 100644
index 0000000000..8643cf6deb
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-fib.exp
@@ -0,0 +1 @@
+89
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-fib.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-fib.nix
new file mode 100644
index 0000000000..04cb52e033
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-fib.nix
@@ -0,0 +1,9 @@
+let
+  fib' = i: n: m:
+    if i == 0
+    then n
+    else fib' (i - 1) m (n + m);
+
+  fib = n: fib' n 1 1;
+in
+fib 10
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-fix.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-fix.exp
new file mode 100644
index 0000000000..c158154351
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-fix.exp
@@ -0,0 +1 @@
+{ a = 1; b = 21; c = 42; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-fix.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-fix.nix
new file mode 100644
index 0000000000..6069950194
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-fix.nix
@@ -0,0 +1,8 @@
+let
+  fix = f: let x = f x; in x;
+in
+fix (self: {
+  a = 1;
+  b = self.a + 20;
+  c = self.b * 2;
+})
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-float-repr.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-float-repr.exp
new file mode 100644
index 0000000000..c55d2be717
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-float-repr.exp
@@ -0,0 +1 @@
+1.23457
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-float-repr.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-float-repr.nix
new file mode 100644
index 0000000000..447bd5af7f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-float-repr.nix
@@ -0,0 +1,2 @@
+# Floats are displayed with a maximum of 5 digits
+1.23456789
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-floor.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-floor.exp
new file mode 100644
index 0000000000..6f98a7f48f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-floor.exp
@@ -0,0 +1 @@
+[ 3 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-floor.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-floor.nix
new file mode 100644
index 0000000000..c6b79c91a1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-floor.nix
@@ -0,0 +1 @@
+[ (builtins.floor 3.4) ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-elements.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-elements.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-elements.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-elements.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-elements.nix
new file mode 100644
index 0000000000..fc4129a254
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-elements.nix
@@ -0,0 +1,8 @@
+let
+  lst = builtins.foldl'
+    (acc: x: acc ++ [ x ])
+    [ ]
+    [ 42 (throw "this shouldn't be evaluated") ];
+in
+
+builtins.head lst
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.nix
new file mode 100644
index 0000000000..59fd29b552
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict-lazy-initial-accumulator.nix
@@ -0,0 +1,4 @@
+builtins.foldl'
+  (_: x: x)
+  (throw "This is never forced")
+  [ "but the results of applying op are" 42 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict.exp
new file mode 100644
index 0000000000..8d683a20fa
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict.exp
@@ -0,0 +1 @@
+[ 6 [ 0 1 2 3 ] 2 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict.nix
new file mode 100644
index 0000000000..aadf5e1121
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-foldlStrict.nix
@@ -0,0 +1,5 @@
+[
+  (builtins.foldl' builtins.add 0 [ 1 2 3 ])
+  (builtins.foldl' (l1: l2: l1 ++ l2) [ 0 ] [ [ 1 ] [ 2 3 ] ])
+  (builtins.foldl' (x: y: if x == 0 then y else x * y) 0 [ 1 2 ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-formals-miscompilation-b-261-regression.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-formals-miscompilation-b-261-regression.exp
new file mode 100644
index 0000000000..721a052bcc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-formals-miscompilation-b-261-regression.exp
@@ -0,0 +1 @@
+[ true null ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-formals-miscompilation-b-261-regression.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-formals-miscompilation-b-261-regression.nix
new file mode 100644
index 0000000000..772fa6f386
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-formals-miscompilation-b-261-regression.nix
@@ -0,0 +1,20 @@
+# This is a regression test for https://b.tvl.fyi/261.
+#
+# The bug occurred when Tvix would unconditionally finalise the stack slot of
+# `finalise` (as its default expression needs a finaliser): Finalising an
+# manually provided, already forced thunk would cause the VM to crash.
+let
+  thunk = x: x;
+  bomb = thunk true;
+  f =
+    { finalise ? later == null
+    , later ? null
+    }:
+    [ finalise later ];
+in
+
+# Note that the crash did not occur if the offending expression was the rhs
+  # argument to `builtins.seq`, hence we need to put the assert in between.
+assert builtins.seq bomb true;
+
+f { finalise = bomb; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson-escapes.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson-escapes.exp
new file mode 100644
index 0000000000..add5505a82
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson-escapes.exp
@@ -0,0 +1 @@
+"quote \" reverse solidus \\ solidus / backspace  formfeed  newline \n carriage return \r horizontal tab \t 1 char unicode encoded backspace  1 char unicode encoded e with accent Γ© 2 char unicode encoded s with caron Ε‘ 3 char unicode encoded rightwards arrow β†’"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson-escapes.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson-escapes.nix
new file mode 100644
index 0000000000..f007135077
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson-escapes.nix
@@ -0,0 +1,3 @@
+# This string contains all supported escapes in a JSON string, per json.org
+# \b and \f are not supported by Nix
+builtins.fromJSON ''"quote \" reverse solidus \\ solidus \/ backspace \b formfeed \f newline \n carriage return \r horizontal tab \t 1 char unicode encoded backspace \u0008 1 char unicode encoded e with accent \u00e9 2 char unicode encoded s with caron \u0161 3 char unicode encoded rightwards arrow \u2192"''
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson.exp
new file mode 100644
index 0000000000..24aa21d78f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson.exp
@@ -0,0 +1 @@
+[ { Image = { Animated = false; Height = 600; IDs = [ 116 943 234 38793 true false null -100 ]; Latitude = 37.7668; Longitude = -122.396; Thumbnail = { Height = 125; Url = "http://www.example.com/image/481989943"; Width = 100; }; Title = "View from 15th Floor"; Width = 800; }; } { name = "a"; value = "b"; } [ 1 2 3 4 ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson.nix
new file mode 100644
index 0000000000..1083919af8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-fromjson.nix
@@ -0,0 +1,24 @@
+[
+  # RFC 7159, section 13.
+  (builtins.fromJSON
+    ''
+      {
+        "Image": {
+            "Width":  800,
+            "Height": 600,
+            "Title":  "View from 15th Floor",
+            "Thumbnail": {
+                "Url":    "http://www.example.com/image/481989943",
+                "Height": 125,
+                "Width":  100
+            },
+            "Animated" : false,
+            "IDs": [116, 943, 234, 38793, true  ,false,null, -100],
+            "Latitude":  37.7668,
+            "Longitude": -122.396
+        }
+      }
+    '')
+  (builtins.fromJSON ''{"name": "a", "value": "b"}'')
+  (builtins.fromJSON "[ 1, 2, 3, 4 ]")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-functionargs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-functionargs.exp
new file mode 100644
index 0000000000..c1c9f8ffaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-functionargs.exp
@@ -0,0 +1 @@
+[ "stdenv" "fetchurl" "aterm-stdenv" "aterm-stdenv2" "libX11" "libXv" "mplayer-stdenv2.libXv-libX11" "mplayer-stdenv2.libXv-libX11_2" "nix-stdenv-aterm-stdenv" "nix-stdenv2-aterm2-stdenv2" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-functionargs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-functionargs.nix
new file mode 100644
index 0000000000..6db04c562c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-functionargs.nix
@@ -0,0 +1,83 @@
+let
+
+  stdenvFun = {}: { name = "stdenv"; };
+  stdenv2Fun = {}: { name = "stdenv2"; };
+  fetchurlFun = { stdenv }: assert stdenv.name == "stdenv"; { name = "fetchurl"; };
+  atermFun = { stdenv, fetchurl }: { name = "aterm-${stdenv.name}"; };
+  aterm2Fun = { stdenv, fetchurl }: { name = "aterm2-${stdenv.name}"; };
+  nixFun = { stdenv, fetchurl, aterm }: { name = "nix-${stdenv.name}-${aterm.name}"; };
+
+  mplayerFun =
+    { stdenv, fetchurl, enableX11 ? false, xorg ? null, enableFoo ? true, foo ? null }:
+      assert stdenv.name == "stdenv2";
+      assert enableX11 -> xorg.libXv.name == "libXv";
+      assert enableFoo -> foo != null;
+      { name = "mplayer-${stdenv.name}.${xorg.libXv.name}-${xorg.libX11.name}"; };
+
+  makeOverridable = f: origArgs: f origArgs //
+    {
+      override = newArgs:
+        makeOverridable f (origArgs // (if builtins.isFunction newArgs then newArgs origArgs else newArgs));
+    };
+
+  callPackage_ = pkgs: f: args:
+    makeOverridable f ((builtins.intersectAttrs (builtins.functionArgs f) pkgs) // args);
+
+  allPackages =
+    { overrides ? (pkgs: pkgsPrev: { }) }:
+    let
+      callPackage = callPackage_ pkgs;
+      pkgs = pkgsStd // (overrides pkgs pkgsStd);
+      pkgsStd = {
+        inherit pkgs;
+        stdenv = callPackage stdenvFun { };
+        stdenv2 = callPackage stdenv2Fun { };
+        fetchurl = callPackage fetchurlFun { };
+        aterm = callPackage atermFun { };
+        xorg = callPackage xorgFun { };
+        mplayer = callPackage mplayerFun { stdenv = pkgs.stdenv2; enableFoo = false; };
+        nix = callPackage nixFun { };
+      };
+    in
+    pkgs;
+
+  libX11Fun = { stdenv, fetchurl }: { name = "libX11"; };
+  libX11_2Fun = { stdenv, fetchurl }: { name = "libX11_2"; };
+  libXvFun = { stdenv, fetchurl, libX11 }: { name = "libXv"; };
+
+  xorgFun =
+    { pkgs }:
+    let callPackage = callPackage_ (pkgs // pkgs.xorg); in
+    {
+      libX11 = callPackage libX11Fun { };
+      libXv = callPackage libXvFun { };
+    };
+
+in
+
+let
+
+  pkgs = allPackages { };
+
+  pkgs2 = allPackages {
+    overrides = pkgs: pkgsPrev: {
+      stdenv = pkgs.stdenv2;
+      nix = pkgsPrev.nix.override { aterm = aterm2Fun { inherit (pkgs) stdenv fetchurl; }; };
+      xorg = pkgsPrev.xorg // { libX11 = libX11_2Fun { inherit (pkgs) stdenv fetchurl; }; };
+    };
+  };
+
+in
+
+[
+  pkgs.stdenv.name
+  pkgs.fetchurl.name
+  pkgs.aterm.name
+  pkgs2.aterm.name
+  pkgs.xorg.libX11.name
+  pkgs.xorg.libXv.name
+  pkgs.mplayer.name
+  pkgs2.mplayer.name
+  pkgs.nix.name
+  pkgs2.nix.name
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-functor-call.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-functor-call.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-functor-call.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-functor-call.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-functor-call.nix
new file mode 100644
index 0000000000..80ae345d83
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-functor-call.nix
@@ -0,0 +1 @@
+{ x = 21; __functor = self: y: self.x * y; } 2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-genlist.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-genlist.exp
new file mode 100644
index 0000000000..cd4ca34f14
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-genlist.exp
@@ -0,0 +1 @@
+[ 0 1 4 9 16 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-genlist.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-genlist.nix
new file mode 100644
index 0000000000..2c4dfba203
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-genlist.nix
@@ -0,0 +1 @@
+builtins.genList (x: x * x) 5
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-hasattr-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-hasattr-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-hasattr-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-hasattr-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-hasattr-catchable.nix
new file mode 100644
index 0000000000..ba85d6b776
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-hasattr-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval ((throw "fred") ? bob)).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-identifier-formatting.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-identifier-formatting.exp
new file mode 100644
index 0000000000..9800c675fc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-identifier-formatting.exp
@@ -0,0 +1 @@
+{ "'quoted'" = false; "-20Β°" = false; "2normal" = false; "45 44 43-'3 2 1" = false; "9front" = false; Very2Normal = true; VeryNormal = true; _'12 = true; "_'12.5" = false; __internal = true; _internal = true; abort = true; "assert" = false; "attr.path" = false; "else" = false; false = true; foldl' = true; "if" = false; "in" = false; "inherit" = false; "let" = false; normal = true; normal2 = true; null = true; or = true; "rec" = false; "then" = false; throw = true; true = true; "with" = false; x = true; x' = true; x'' = true; "πŸ˜€" = false; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-identifier-formatting.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-identifier-formatting.nix
new file mode 100644
index 0000000000..58af3d6d16
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-identifier-formatting.nix
@@ -0,0 +1,42 @@
+# Note: the attribute values in this set aren't just dummies!  They
+# are booleans which indicate whether or not the corresponding
+# attrname is valid without quotification.
+{
+  __internal = true;
+  _internal = true;
+  normal = true;
+  VeryNormal = true;
+  normal2 = true;
+  Very2Normal = true;
+  _'12 = true;
+  foldl' = true;
+  x = true;
+  x' = true;
+  x'' = true;
+
+  true = true;
+  false = true;
+  null = true;
+  or = true;
+  "assert" = false;
+  throw = true;
+  abort = true;
+
+  "9front" = false;
+  "2normal" = false;
+  "-20Β°" = false;
+  "45 44 43-'3 2 1" = false;
+  "attr.path" = false;
+  "'quoted'" = false;
+  "_'12.5" = false;
+  "πŸ˜€" = false;
+
+  "if" = false;
+  "then" = false;
+  "else" = false;
+  "with" = false;
+  "let" = false;
+  "in" = false;
+  "rec" = false;
+  "inherit" = false;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-import-display.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-import-display.exp
new file mode 100644
index 0000000000..15d838950e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-import-display.exp
@@ -0,0 +1 @@
+<PRIMOP>
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-import-display.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-import-display.nix
new file mode 100644
index 0000000000..411f3cd6ef
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-import-display.nix
@@ -0,0 +1,2 @@
+# In C++ Nix 2.3 this used to be <PRIMOP-APP>
+import
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-import.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-import.exp
new file mode 100644
index 0000000000..5ba7f64d78
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-import.exp
@@ -0,0 +1 @@
+[ 42 42 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-import.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-import.nix
new file mode 100644
index 0000000000..49cd244f06
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-import.nix
@@ -0,0 +1,4 @@
+[
+  (import ./directory)
+  (import ./directory/default.nix)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-inherit-string-ident.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-inherit-string-ident.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-inherit-string-ident.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-inherit-string-ident.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-inherit-string-ident.nix
new file mode 100644
index 0000000000..75794d3337
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-inherit-string-ident.nix
@@ -0,0 +1,8 @@
+# identifiers in inherits can be string-like expressions
+
+let
+  set = {
+    inherit ({ value = 42; }) "value";
+  };
+in
+set.value
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals-deferred.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals-deferred.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals-deferred.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals-deferred.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals-deferred.nix
new file mode 100644
index 0000000000..5c6702120f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals-deferred.nix
@@ -0,0 +1,3 @@
+# Tests formals which have internal default values that must be deferred.
+
+({ optional ? defaultValue, defaultValue }: optional) { defaultValue = 42; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals.nix
new file mode 100644
index 0000000000..c6dd5e9d54
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-internal-formals.nix
@@ -0,0 +1,3 @@
+# Tests formals which have internal default values.
+
+({ defaultValue, optional ? defaultValue }: optional) { defaultValue = 42; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-intersectattrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-intersectattrs.exp
new file mode 100644
index 0000000000..25001b211f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-intersectattrs.exp
@@ -0,0 +1 @@
+{ a = 100; b = 200; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-intersectattrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-intersectattrs.nix
new file mode 100644
index 0000000000..f02d963226
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-intersectattrs.nix
@@ -0,0 +1,3 @@
+builtins.intersectAttrs
+{ a = 1; b = 2; c = 3; }
+{ a = 100; b = 200; d = 5; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lambda-identity.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-lambda-identity.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lambda-identity.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lambda-identity.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-lambda-identity.nix
new file mode 100644
index 0000000000..f2ee49df80
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lambda-identity.nix
@@ -0,0 +1,2 @@
+# Identity function is the simplest possible function.
+(x: x) 42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding-closure.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding-closure.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding-closure.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding-closure.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding-closure.nix
new file mode 100644
index 0000000000..4312ec9a52
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding-closure.nix
@@ -0,0 +1,5 @@
+let
+  f = n: n + a;
+  a = 2;
+in
+f 40
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding.nix
new file mode 100644
index 0000000000..6b6875cf1a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-late-binding.nix
@@ -0,0 +1,5 @@
+let
+  a = b;
+  b = 42;
+in
+a
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-assert.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-assert.exp
new file mode 100644
index 0000000000..48082f72f0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-assert.exp
@@ -0,0 +1 @@
+12
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-assert.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-assert.nix
new file mode 100644
index 0000000000..5a36964976
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-assert.nix
@@ -0,0 +1,8 @@
+assert true;
+
+let
+  x = assert false; 13;
+  y = 12;
+in
+
+{ inherit x y; }.y
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-equality.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-equality.exp
new file mode 100644
index 0000000000..1c70d1bcf1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-equality.exp
@@ -0,0 +1 @@
+[ true true false true true ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-equality.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-equality.nix
new file mode 100644
index 0000000000..92363245f8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-equality.nix
@@ -0,0 +1,16 @@
+let
+  attrs1 = { x = 1 + 2; };
+  attrs2 = { x = 2 + 1; };
+  list1 = [ (1 + 2) ];
+  list2 = [ (2 + 1) ];
+  list3 = [ (2 + 2) ];
+  list4 = [ (2 + 2) ];
+  list5 = [ (2 + 2) ];
+in
+[
+  (attrs1 == attrs2)
+  (list1 == list2)
+  (list3 == list2)
+  (list4 == [ 4 ])
+  ([ 4 ] == list5)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with-nested.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with-nested.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with-nested.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with-nested.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with-nested.nix
new file mode 100644
index 0000000000..22ac14b3f1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with-nested.nix
@@ -0,0 +1,5 @@
+# The 'namespace' of a with should only be evaluated if an identifier
+# from it is actually accessed.
+
+with (abort "should not be evaluated");
+let a = dynamic; in 42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with.nix
new file mode 100644
index 0000000000..8b1a0191dc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-lazy-with.nix
@@ -0,0 +1,6 @@
+# The 'namespace' of a with should only be evaluated if an identifier
+# from it is actually accessed.
+
+with (abort "should not be evaluated");
+
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-fix.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-fix.exp
new file mode 100644
index 0000000000..5d2955ffd5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-fix.exp
@@ -0,0 +1 @@
+{ one = 42; two = 42; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-fix.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-fix.nix
new file mode 100644
index 0000000000..a411b1c4a4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-fix.nix
@@ -0,0 +1,9 @@
+let {
+a = 21;
+b = body.one;
+
+body = {
+  one = a * 2;
+  two = b;
+};
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-in-with.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-in-with.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-in-with.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-in-with.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-in-with.nix
new file mode 100644
index 0000000000..7d95efa5c3
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let-in-with.nix
@@ -0,0 +1 @@
+with { }; let { body = 42; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let.nix
new file mode 100644
index 0000000000..faabe25457
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-legacy-let.nix
@@ -0,0 +1,4 @@
+let {
+a = 21;
+body = a * 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-identifiers.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-let-identifiers.exp
new file mode 100644
index 0000000000..5776134d0e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-identifiers.exp
@@ -0,0 +1 @@
+[ 1 2 3 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-identifiers.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-let-identifiers.nix
new file mode 100644
index 0000000000..ce588be069
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-identifiers.nix
@@ -0,0 +1,6 @@
+let
+  a = 1;
+  "b" = 2;
+  ${"c"} = 3;
+in
+[ a b c ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit-from-later-bound.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit-from-later-bound.exp
new file mode 100644
index 0000000000..409940768f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit-from-later-bound.exp
@@ -0,0 +1 @@
+23
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit-from-later-bound.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit-from-later-bound.nix
new file mode 100644
index 0000000000..21196f48bc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit-from-later-bound.nix
@@ -0,0 +1,13 @@
+let
+  inherit (c) d;
+  inherit (a) b c;
+
+  a = {
+    b = 20;
+    c = {
+      d = 3;
+    };
+  };
+in
+
+b + d
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.exp
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.exp
@@ -0,0 +1 @@
+2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.nix
new file mode 100644
index 0000000000..3aa7c0f8d2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.nix
@@ -0,0 +1,13 @@
+let
+  set = {
+    a = 1;
+  };
+in
+let
+  set2 = {
+    b = 1;
+  };
+  inherit (set) a;
+  inherit (set2) b;
+in
+a + b
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-sibling-access.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-let-sibling-access.exp
new file mode 100644
index 0000000000..00750edc07
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-sibling-access.exp
@@ -0,0 +1 @@
+3
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-sibling-access.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-let-sibling-access.nix
new file mode 100644
index 0000000000..faad81a213
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-sibling-access.nix
@@ -0,0 +1,6 @@
+let
+  a = 1;
+  b = 2;
+  c = a + b;
+in
+c
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit-mixed.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit-mixed.exp
new file mode 100644
index 0000000000..3bed31f76e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit-mixed.exp
@@ -0,0 +1 @@
+[ 1 2 3 4 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit-mixed.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit-mixed.nix
new file mode 100644
index 0000000000..30981099cb
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit-mixed.nix
@@ -0,0 +1,20 @@
+# This test mixes different ways of creating bindings in a let … in expression
+# to make sure that the compiler initialises the locals in the same order as
+# they are declared.
+
+let
+  d = 4;
+in
+
+# Trick to allow useless inherits in the following let
+with { _unused = null; };
+
+let
+  set = { b = 2; };
+  a = 1;
+  inherit (set) b;
+  c = 3;
+  inherit d;
+in
+
+[ a b c d ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit.exp
@@ -0,0 +1 @@
+1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit.nix
new file mode 100644
index 0000000000..3d1c46b10b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-useful-plain-inherit.nix
@@ -0,0 +1,9 @@
+with { a = 1; };
+
+let
+  inherit a;
+in
+
+with { a = 2; };
+
+a
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-list-comparison.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-list-comparison.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-list-comparison.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-list-comparison.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-list-comparison.nix
new file mode 100644
index 0000000000..7796fe4cbb
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-list-comparison.nix
@@ -0,0 +1 @@
+[ 1 2 ] > [ ((rec{ x = 1; }).x) 2 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-listtoattrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-listtoattrs.exp
new file mode 100644
index 0000000000..74abef7bc6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-listtoattrs.exp
@@ -0,0 +1 @@
+"AAbar"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-listtoattrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-listtoattrs.nix
new file mode 100644
index 0000000000..551db72cb0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-listtoattrs.nix
@@ -0,0 +1,16 @@
+with builtins;
+let
+  fold = op: nul: list:
+    if list == [ ]
+    then nul
+    else op (head list) (fold op nul (tail list));
+  concat =
+    fold (x: y: x + y) "";
+  asi = name: value: { inherit name value; };
+  list = [ (asi "a" "A") (asi "b" "B") ];
+  a = builtins.listToAttrs list;
+  b = builtins.listToAttrs (list ++ list);
+  r = builtins.listToAttrs [ (asi "result" [ a b ]) (asi "throw" (throw "this should not be thrown")) ];
+  x = builtins.listToAttrs [ (asi "foo" "bar") (asi "foo" "bla") ];
+in
+concat (map (x: x.a) r.result) + x.foo
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-logical-and-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-logical-and-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-logical-and-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-logical-and-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-logical-and-catchable.nix
new file mode 100644
index 0000000000..dd2a9baa75
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-logical-and-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval ((throw "fred") && (throw "jill"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-logical-or-catchable.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-logical-or-catchable.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-logical-or-catchable.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-logical-or-catchable.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-logical-or-catchable.nix
new file mode 100644
index 0000000000..3adccfa441
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-logical-or-catchable.nix
@@ -0,0 +1 @@
+(builtins.tryEval ((throw "fred") || (throw "jill"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-manual-rec.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-manual-rec.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-manual-rec.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-manual-rec.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-manual-rec.nix
new file mode 100644
index 0000000000..23459e384a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-manual-rec.nix
@@ -0,0 +1,10 @@
+# Manual desugaring of something similar to `rec`, to test lower level
+# recursion primitives.
+
+let
+  set = with set; {
+    a = 21;
+    b = a * 2;
+  };
+in
+set.b
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-attrs.exp
new file mode 100644
index 0000000000..911ab51de5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-attrs.exp
@@ -0,0 +1 @@
+{ set = { a = 1; b = 2; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-attrs.nix
new file mode 100644
index 0000000000..78b28909a2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-attrs.nix
@@ -0,0 +1,9 @@
+{
+  set = {
+    a = 1;
+  };
+
+  set = {
+    b = 2;
+  };
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-rec-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-rec-attrs.exp
new file mode 100644
index 0000000000..768eaae61c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-rec-attrs.exp
@@ -0,0 +1 @@
+{ set = { a = 21; b = 42; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-rec-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-rec-attrs.nix
new file mode 100644
index 0000000000..cea4cb1b4f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-merge-nested-rec-attrs.nix
@@ -0,0 +1,12 @@
+{
+  set = rec {
+    a = 21;
+  };
+
+  set = {
+    # Fun fact: This might be the only case in Nix where a lexical
+    # resolution of an identifier can only be resolved by looking at
+    # *siblings* in the AST.
+    b = 2 * a;
+  };
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-multiline-string.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-multiline-string.exp
new file mode 100644
index 0000000000..9839e480b7
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-multiline-string.exp
@@ -0,0 +1 @@
+"hello\nworld"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-multiline-string.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-multiline-string.nix
new file mode 100644
index 0000000000..84beb22ed5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-multiline-string.nix
@@ -0,0 +1,2 @@
+''hello
+world''
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-multiple-nested-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-multiple-nested-attrs.exp
new file mode 100644
index 0000000000..b5c707cf46
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-multiple-nested-attrs.exp
@@ -0,0 +1 @@
+{ a = { b = 15; }; b = { c = "test"; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-multiple-nested-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-multiple-nested-attrs.nix
new file mode 100644
index 0000000000..5d611930ca
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-multiple-nested-attrs.nix
@@ -0,0 +1 @@
+{ a.b = 15; b.c = "test"; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-mutually-recursive-let-binding.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-mutually-recursive-let-binding.exp
new file mode 100644
index 0000000000..edca9baca9
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-mutually-recursive-let-binding.exp
@@ -0,0 +1 @@
+{ a = 1; b = 2; c = 3; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-mutually-recursive-let-binding.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-mutually-recursive-let-binding.nix
new file mode 100644
index 0000000000..1b3feda432
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-mutually-recursive-let-binding.nix
@@ -0,0 +1,14 @@
+let
+  a = {
+    a = 3;
+    b = b.b;
+  };
+
+  b = {
+    a = a.a - 2;
+    b = 2;
+    c = a.c or 3;
+  };
+in
+
+a // b
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-ne-int.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-ne-int.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-ne-int.exp
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-ne-int.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-ne-int.nix
new file mode 100644
index 0000000000..e06b571a28
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-ne-int.nix
@@ -0,0 +1 @@
+42 != 69
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-ne-string.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-ne-string.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-ne-string.exp
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-ne-string.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-ne-string.nix
new file mode 100644
index 0000000000..a83471e500
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-ne-string.nix
@@ -0,0 +1 @@
+"this" != "that"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-assertions.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-assertions.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-assertions.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-assertions.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-assertions.nix
new file mode 100644
index 0000000000..b0397e268e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-assertions.nix
@@ -0,0 +1 @@
+(builtins.tryEval (assert (assert false; true); true)).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-closure.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-closure.exp
new file mode 100644
index 0000000000..b6a7d89c68
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-closure.exp
@@ -0,0 +1 @@
+16
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-closure.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-closure.nix
new file mode 100644
index 0000000000..97bff7f077
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-closure.nix
@@ -0,0 +1 @@
+(a: b: c: d: a + b + c + d) 1 3 5 7
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-deferred-upvalue.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-deferred-upvalue.exp
new file mode 100644
index 0000000000..209e3ef4b6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-deferred-upvalue.exp
@@ -0,0 +1 @@
+20
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-deferred-upvalue.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-deferred-upvalue.nix
new file mode 100644
index 0000000000..3fa1d3ed05
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-deferred-upvalue.nix
@@ -0,0 +1,10 @@
+let
+  doubler = n: outer n;
+  outer =
+    let
+      inner = n: a * n;
+      a = 2;
+    in
+    inner;
+in
+doubler 10
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-has-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-has-attrs.exp
new file mode 100644
index 0000000000..d2c1c04da3
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-has-attrs.exp
@@ -0,0 +1 @@
+[ true true true true true true true false false false false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-has-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-has-attrs.nix
new file mode 100644
index 0000000000..47dcec7a95
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-has-attrs.nix
@@ -0,0 +1,26 @@
+let
+  set = {
+    a.b.c = 123;
+    foo = {
+      bar = 23;
+    };
+    baz = 1;
+  };
+
+  tes = "random value";
+in
+
+[
+  (set ? a)
+  (set ? a.b)
+  (set ? a.b.c)
+  (set ? foo)
+  (set ? foo.bar)
+  (set.foo ? bar)
+  (set ? baz)
+  (set ? x)
+  (set ? x.y.z)
+  (tes ? bar)
+  (tes ? x.y.z)
+  (null ? null)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-let.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-let.exp
new file mode 100644
index 0000000000..6db47b033e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-let.exp
@@ -0,0 +1 @@
+{ a = { b = 42; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-let.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-let.nix
new file mode 100644
index 0000000000..c99ac748e6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-let.nix
@@ -0,0 +1,5 @@
+let
+  inner = 21;
+  set.a.b = inner * 2;
+in
+set
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-rec.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-rec.exp
new file mode 100644
index 0000000000..77eb325dde
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-rec.exp
@@ -0,0 +1 @@
+{ a = { b = { c = 42; }; }; outer = 21; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-rec.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-rec.nix
new file mode 100644
index 0000000000..797d11108f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-keys-rec.nix
@@ -0,0 +1,4 @@
+rec {
+  outer = 21;
+  a.b.c = outer * 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let-slots.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let-slots.exp
new file mode 100644
index 0000000000..e45ef1da2f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let-slots.exp
@@ -0,0 +1 @@
+[ 1 2 3 4 5 6 7 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let-slots.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let-slots.nix
new file mode 100644
index 0000000000..eec5940875
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let-slots.nix
@@ -0,0 +1,22 @@
+# This test deals with a tricky edge-case around scopes, where the
+# stack slot accounting must correctly account for the position at
+# which the body of a let expression is being initialised when
+# resolving upvalues.
+
+let
+  a = 1;
+  b = 2;
+  outer =
+    let
+      c = 3;
+      d = 4;
+      inner =
+        let
+          e = 5;
+          f = 6;
+        in
+        g: [ a b c d e f g ];
+    in
+    inner;
+in
+outer 7
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let.exp
new file mode 100644
index 0000000000..7f8f011eb7
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let.exp
@@ -0,0 +1 @@
+7
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let.nix
new file mode 100644
index 0000000000..f40c04b139
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-let.nix
@@ -0,0 +1,10 @@
+let
+  a =
+    let
+      b = 1;
+      c = 2;
+    in
+    b + c;
+  b = 4;
+in
+a + b
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-poisoning.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-poisoning.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-poisoning.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-poisoning.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-poisoning.nix
new file mode 100644
index 0000000000..0fd22a671c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-poisoning.nix
@@ -0,0 +1,5 @@
+let
+  null = 1;
+  f = n: n + null;
+in
+f 41
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-set-thunks.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-set-thunks.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-set-thunks.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-set-thunks.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-set-thunks.nix
new file mode 100644
index 0000000000..f3ad829354
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-set-thunks.nix
@@ -0,0 +1,5 @@
+({
+  x = {
+    y = 42;
+  };
+}).x.y
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-siblings.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-siblings.exp
new file mode 100644
index 0000000000..d757cae1f5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-siblings.exp
@@ -0,0 +1 @@
+{ outer = 42; sibling = 42; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-siblings.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-siblings.nix
new file mode 100644
index 0000000000..31111d8081
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-siblings.nix
@@ -0,0 +1,7 @@
+rec {
+  outer =
+    let inner = sibling;
+    in inner;
+
+  sibling = 42;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-thunks.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-thunks.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-thunks.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-thunks.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-thunks.nix
new file mode 100644
index 0000000000..2519221e97
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-thunks.nix
@@ -0,0 +1,8 @@
+# If a thunk yields another thunk, OpForce should keep forcing until
+# there is a value.
+let
+  a = b;
+  b = c;
+  c = 42;
+in
+a
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-with.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-with.exp
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-with.exp
@@ -0,0 +1 @@
+2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nested-with.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-with.nix
new file mode 100644
index 0000000000..fa832b2099
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nested-with.nix
@@ -0,0 +1,5 @@
+let
+  set1 = { a = 1; };
+  set2 = { a = 2; };
+in
+with set1; with set2; a
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nix-version-cmp.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-nix-version-cmp.exp
new file mode 100644
index 0000000000..3a2e3f4984
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nix-version-cmp.exp
@@ -0,0 +1 @@
+-1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-nix-version-cmp.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-nix-version-cmp.nix
new file mode 100644
index 0000000000..6f35305612
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-nix-version-cmp.nix
@@ -0,0 +1,5 @@
+# nixpkgs checks against the `builtins.nixVersion` and fails if it
+# doesn't like what it sees. To work around this we have a "user-agent
+# style" version (see cl/6858) that ensures compatibility.
+
+builtins.compareVersions "2.3" builtins.nixVersion
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-observable-eval-cache.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-observable-eval-cache.exp
new file mode 100644
index 0000000000..aaa53b6025
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-observable-eval-cache.exp
@@ -0,0 +1 @@
+[ true true false false true ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-observable-eval-cache.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-observable-eval-cache.nix
new file mode 100644
index 0000000000..24003d0637
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-observable-eval-cache.nix
@@ -0,0 +1,7 @@
+[
+  (import ./observable-eval-cache1.nix == import ./observable-eval-cache1.nix)
+  (import ./observable-eval-cache1.nix == import ./observable-eval-cache2.nix)
+  (import ./observable-eval-cache1.nix == import ./observable-eval-cache3.nix)
+  (import ./observable-eval-cache2.nix == import ./observable-eval-cache3.nix)
+  (import ./observable-eval-cache3.nix == import ./observable-eval-cache3.nix)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-observe-infinite-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-observe-infinite-attrs.exp
new file mode 100644
index 0000000000..bbb332a5ee
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-observe-infinite-attrs.exp
@@ -0,0 +1 @@
+[ "x" "y" ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-observe-infinite-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-observe-infinite-attrs.nix
new file mode 100644
index 0000000000..684c88f800
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-observe-infinite-attrs.nix
@@ -0,0 +1,4 @@
+# The below attribute set is infinitely large, but we should be able
+# to observe it as long as we don't access its entire value.
+
+let as = { x = 123; y = as; }; in builtins.attrNames as.y.y
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-optimised-bools.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-optimised-bools.exp
new file mode 100644
index 0000000000..9d9185fcd1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-optimised-bools.exp
@@ -0,0 +1 @@
+[ true true false false true true false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-optimised-bools.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-optimised-bools.nix
new file mode 100644
index 0000000000..650d7f028d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-optimised-bools.nix
@@ -0,0 +1,21 @@
+let
+  makeTrue = _: true;
+  makeFalse = _: false;
+in
+[
+  # useless `false`
+  (false || makeTrue null) # true
+  (makeTrue null || false) # true
+
+  # useless `true`
+  (true && makeFalse null) # false
+  (makeFalse null && true) # false
+
+  # useless `||`
+  (true || makeFalse null) # true
+  (makeFalse null || true) # true
+
+  # useless `&&`
+  (false && makeTrue null) # false
+  (makeTrue null && false) # false
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-default.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-default.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-default.exp
@@ -0,0 +1 @@
+1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-default.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-default.nix
new file mode 100644
index 0000000000..444f270af6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-default.nix
@@ -0,0 +1 @@
+{ b = 1; }.b or 2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested-default.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested-default.exp
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested-default.exp
@@ -0,0 +1 @@
+2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested-default.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested-default.nix
new file mode 100644
index 0000000000..ceffd0697b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested-default.nix
@@ -0,0 +1 @@
+{ a.b = 1; }.a.c or 2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested.exp
@@ -0,0 +1 @@
+1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested.nix
new file mode 100644
index 0000000000..1a76594546
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-nested.nix
@@ -0,0 +1 @@
+{ a.b = 1; }.a.b or 2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-non-set.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-non-set.exp
new file mode 100644
index 0000000000..a833e32892
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-non-set.exp
@@ -0,0 +1 @@
+"works fine"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-non-set.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-non-set.nix
new file mode 100644
index 0000000000..fd09bfee64
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator-non-set.nix
@@ -0,0 +1,2 @@
+# `or` operator should keep working if it encounters a non-set type.
+{ a.b = 42; }.a.b.c or "works fine"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator.exp
@@ -0,0 +1 @@
+1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator.nix
new file mode 100644
index 0000000000..ce1e6e67c2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-or-operator.nix
@@ -0,0 +1 @@
+{ a = 1; }.a or 2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-overlapping-nested-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-overlapping-nested-attrs.exp
new file mode 100644
index 0000000000..2483a27183
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-overlapping-nested-attrs.exp
@@ -0,0 +1 @@
+{ a = { b = 15; c = "test"; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-overlapping-nested-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-overlapping-nested-attrs.nix
new file mode 100644
index 0000000000..4154ff9da2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-overlapping-nested-attrs.nix
@@ -0,0 +1,4 @@
+{
+  a.b = 15;
+  a.c = "test";
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-parsedrvname.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-parsedrvname.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-parsedrvname.exp
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-parsedrvname.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-parsedrvname.nix
new file mode 100644
index 0000000000..5997c99b47
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-parsedrvname.nix
@@ -0,0 +1,11 @@
+# the first dash followed by a non-alphabetic character separates
+# the "name" from the "version"
+
+assert builtins.parseDrvName "ripgrep-1.2" == { name = "ripgrep"; version = "1.2"; };
+assert builtins.parseDrvName "rip-grep-1.2" == { name = "rip-grep"; version = "1.2"; };
+assert builtins.parseDrvName "7zip_archiver-0.2" == { name = "7zip_archiver"; version = "0.2"; };
+assert builtins.parseDrvName "gcc-1-2" == { name = "gcc"; version = "1-2"; };
+assert builtins.parseDrvName "bash--1-2" == { name = "bash"; version = "-1-2"; };
+assert builtins.parseDrvName "xvidtune-?1-2" == { name = "xvidtune"; version = "?1-2"; };
+
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-pathexists.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-pathexists.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-pathexists.exp
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-pathexists.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-pathexists.nix
new file mode 100644
index 0000000000..c9eedb44ff
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-pathexists.nix
@@ -0,0 +1,2 @@
+builtins.pathExists ./lib.nix
+  && !builtins.pathExists ./bla.nix
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-poisoned-scopes.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-poisoned-scopes.exp
new file mode 100644
index 0000000000..5776134d0e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-poisoned-scopes.exp
@@ -0,0 +1 @@
+[ 1 2 3 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-poisoned-scopes.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-poisoned-scopes.nix
new file mode 100644
index 0000000000..81f03d9e2b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-poisoned-scopes.nix
@@ -0,0 +1,6 @@
+let
+  true = 1;
+  false = 2;
+  null = 3;
+in
+[ true false null ]
diff --git a/third_party/nix/src/tests/lang/eval-okay-readDir.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.exp
index bf8d2c14ea..bf8d2c14ea 100644
--- a/third_party/nix/src/tests/lang/eval-okay-readDir.exp
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.exp
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix.disabled b/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix.disabled
new file mode 100644
index 0000000000..a7ec9292aa
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix.disabled
@@ -0,0 +1 @@
+builtins.readDir ./readDir
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-readfile.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-readfile.exp
new file mode 100644
index 0000000000..a2c87d0c43
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-readfile.exp
@@ -0,0 +1 @@
+"builtins.readFile ./eval-okay-readfile.nix\n"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-readfile.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-readfile.nix
new file mode 100644
index 0000000000..82f7cb1743
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-readfile.nix
@@ -0,0 +1 @@
+builtins.readFile ./eval-okay-readfile.nix
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.exp
new file mode 100644
index 0000000000..ac8d062a69
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.exp
@@ -0,0 +1 @@
+{ barbaz = 42; foobar = 42; val = 21; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.nix
new file mode 100644
index 0000000000..8d7a8cef8e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.nix
@@ -0,0 +1,5 @@
+rec {
+  val = 21;
+  ${"foo" + "bar"} = 42;
+  ${"bar" + "baz"} = val * 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-rec-nested-access.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-nested-access.exp
new file mode 100644
index 0000000000..a1dca9bb68
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-nested-access.exp
@@ -0,0 +1 @@
+{ a = { b = 1; c = 2; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-rec-nested-access.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-nested-access.nix
new file mode 100644
index 0000000000..7d037c6b37
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-nested-access.nix
@@ -0,0 +1,4 @@
+rec {
+  a.b = 1;
+  a.c = a.b * 2;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-recursive-attrs-all-features.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-recursive-attrs-all-features.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-recursive-attrs-all-features.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-recursive-attrs-all-features.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-recursive-attrs-all-features.nix
new file mode 100644
index 0000000000..a234705b5e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-recursive-attrs-all-features.nix
@@ -0,0 +1,13 @@
+let a = 1;
+in
+(rec {
+  inherit a;
+
+  b = {
+    c = a + 20;
+  };
+
+  inherit (b) c;
+
+  d = c * 2;
+}).d
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-regex-match.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-regex-match.exp
new file mode 100644
index 0000000000..9501035391
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-regex-match.exp
@@ -0,0 +1 @@
+[ true true false true true true true false false true false [ "foobar" ] [ "FOO" ] [ "/path/to/" "/path/to" "foobar" "nix" ] [ null null "foobar" "cc" ] ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-regex-match.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-regex-match.nix
new file mode 100644
index 0000000000..f774e00a21
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-regex-match.nix
@@ -0,0 +1,29 @@
+with builtins;
+
+let
+
+  matches = pat: s: match pat s != null;
+
+  splitFN = match "((.*)/)?([^/]*)\\.(nix|cc)";
+
+in
+
+[
+  (matches "foobar" "foobar")
+  (matches "fo*" "f")
+  (matches "fo+" "f")
+  (matches "fo*" "fo")
+  (matches "fo*" "foo")
+  (matches "fo+" "foo")
+  (matches "fo{1,2}" "foo")
+  (matches "fo{1,2}" "fooo")
+  (matches "fo*" "foobar")
+  (matches "[[:space:]]+([^[:space:]]+)[[:space:]]+" "  foo   ")
+  (matches "[[:space:]]+([[:upper:]]+)[[:space:]]+" "  foo   ")
+
+  (match "(.*)\\.nix" "foobar.nix")
+  (match "[[:space:]]+([[:upper:]]+)[[:space:]]+" "  FOO   ")
+
+  (splitFN "/path/to/foobar.nix")
+  (splitFN "foobar.cc")
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-remove.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-remove.exp
new file mode 100644
index 0000000000..8d38505c16
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-remove.exp
@@ -0,0 +1 @@
+456
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-remove.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-remove.nix
new file mode 100644
index 0000000000..62c5aa1fd4
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-remove.nix
@@ -0,0 +1,5 @@
+let {
+attrs = { x = 123; y = 456; };
+
+body = (removeAttrs attrs [ "x" ]).y;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-repeated-list-to-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-repeated-list-to-attrs.exp
new file mode 100644
index 0000000000..b4a1e66d6b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-repeated-list-to-attrs.exp
@@ -0,0 +1 @@
+[ 1 2 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-repeated-list-to-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-repeated-list-to-attrs.nix
new file mode 100644
index 0000000000..ed819d76c7
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-repeated-list-to-attrs.nix
@@ -0,0 +1,14 @@
+# Ensure that builtins.listToAttrs returns the first instance of a key.
+
+let
+  inherit (builtins) foldl' listToAttrs;
+
+  input = [{ name = "result"; value = 1; } { name = "result"; value = 2; }];
+
+  # foldl-based version of listToAttrs with the _opposite_ behaviour.
+  listToAttrs' = list: foldl' (acc: elem: acc // { ${elem.name} = elem.value; }) { } list;
+in
+[
+  (listToAttrs input).result
+  (listToAttrs' input).result
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-seq.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-seq.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-seq.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-seq.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-seq.nix
new file mode 100644
index 0000000000..fd0806c199
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-seq.nix
@@ -0,0 +1 @@
+(builtins.seq 1 2) + (builtins.seq [ (throw "list") ] 20) + (builtins.seq { boing = throw "set"; } 20)
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-closure.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-closure.exp
new file mode 100644
index 0000000000..7f8f011eb7
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-closure.exp
@@ -0,0 +1 @@
+7
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-closure.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-closure.nix
new file mode 100644
index 0000000000..56445454fe
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-closure.nix
@@ -0,0 +1 @@
+(a: b: a + b) 2 5
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-interpol.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-interpol.exp
new file mode 100644
index 0000000000..cd4bc1ab64
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-interpol.exp
@@ -0,0 +1 @@
+"hello world"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-interpol.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-interpol.nix
new file mode 100644
index 0000000000..125b0859ac
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-interpol.nix
@@ -0,0 +1 @@
+"hello ${"world"}"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-let.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-let.exp
new file mode 100644
index 0000000000..00750edc07
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-let.exp
@@ -0,0 +1 @@
+3
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-let.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-let.nix
new file mode 100644
index 0000000000..b4da0f824a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-let.nix
@@ -0,0 +1,5 @@
+let
+  a = 1;
+  b = 2;
+in
+a + b
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-nested-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-nested-attrs.exp
new file mode 100644
index 0000000000..6db47b033e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-nested-attrs.exp
@@ -0,0 +1 @@
+{ a = { b = 42; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-nested-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-nested-attrs.nix
new file mode 100644
index 0000000000..a97394d165
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-nested-attrs.nix
@@ -0,0 +1 @@
+{ a.b = 42; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-recursive-attrs.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-recursive-attrs.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-recursive-attrs.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-recursive-attrs.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-recursive-attrs.nix
new file mode 100644
index 0000000000..c86ff80383
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-recursive-attrs.nix
@@ -0,0 +1,4 @@
+(rec {
+  a = 21;
+  b = a * 2;
+}).b
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-with.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-with.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-with.exp
@@ -0,0 +1 @@
+1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-simple-with.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-with.nix
new file mode 100644
index 0000000000..3d375be4f9
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-simple-with.nix
@@ -0,0 +1,6 @@
+let
+  set = {
+    a = 1;
+  };
+in
+with set; a
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-stable-sort.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-stable-sort.exp
new file mode 100644
index 0000000000..9d78376214
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-stable-sort.exp
@@ -0,0 +1 @@
+[ { index = 7; key = 0; } { index = 0; key = 1; } { index = 13; key = 1; } { index = 1; key = 2; } { index = 3; key = 2; } { index = 4; key = 2; } { index = 5; key = 2; } { index = 12; key = 2; } { index = 14; key = 2; } { index = 2; key = 3; } { index = 11; key = 3; } { index = 15; key = 3; } { index = 10; key = 4; } { index = 6; key = 5; } { index = 8; key = 5; } { index = 9; key = 5; } { index = 16; key = 22; } ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-stable-sort.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-stable-sort.nix
new file mode 100644
index 0000000000..9969e0a294
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-stable-sort.nix
@@ -0,0 +1,7 @@
+let
+  keys = [ 1 2 3 2 2 2 5 0 5 5 4 3 2 1 2 3 22 ];
+in
+
+builtins.sort
+  (a: b: a.key < b.key)
+  (builtins.genList (index: { inherit index; key = builtins.elemAt keys index; }) (builtins.length keys))
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-substring-propagate-catchables.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-substring-propagate-catchables.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-substring-propagate-catchables.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-substring-propagate-catchables.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-substring-propagate-catchables.nix
new file mode 100644
index 0000000000..78d5dda38e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-substring-propagate-catchables.nix
@@ -0,0 +1 @@
+(builtins.tryEval (builtins.substring 0 4 (throw "jill"))).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-default-args.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-default-args.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-default-args.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-default-args.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-default-args.nix
new file mode 100644
index 0000000000..0523cf864c
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-default-args.nix
@@ -0,0 +1 @@
+(builtins.tryEval (({ foo ? throw "up" }: if foo then 1 else 2) { })).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-implications.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-implications.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-implications.exp
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-implications.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-implications.nix
new file mode 100644
index 0000000000..126738d883
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-test-catchables-in-implications.nix
@@ -0,0 +1 @@
+(builtins.tryEval (({ foo ? throw "up" }: foo -> true) { })).success
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-functor.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-functor.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-functor.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-functor.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-functor.nix
new file mode 100644
index 0000000000..568a5c5413
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-functor.nix
@@ -0,0 +1,8 @@
+let
+  __functor = f;
+  f = self: x: self.out * x;
+in
+{
+  inherit __functor;
+  out = 21;
+} 2
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-if.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-if.exp
new file mode 100644
index 0000000000..ffcd4415b0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-if.exp
@@ -0,0 +1 @@
+{ }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-if.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-if.nix
new file mode 100644
index 0000000000..3810ebe784
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-if.nix
@@ -0,0 +1,8 @@
+let
+  a = { };
+in
+let
+  c = if builtins.isFunction a then a b else a;
+  b = { };
+in
+c
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.exp
new file mode 100644
index 0000000000..fc2f21e930
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.exp
@@ -0,0 +1 @@
+"strict literal"
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.nix
new file mode 100644
index 0000000000..bd3555bb24
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.nix
@@ -0,0 +1,7 @@
+let
+  final = { text = "strict literal"; inherit x y; };
+  x = "lazy ${throw "interpolation"}";
+  y = "${throw "also lazy!"}";
+in
+
+final.text
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-with.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-with.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-with.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-with.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-with.nix
new file mode 100644
index 0000000000..799408b2e6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-with.nix
@@ -0,0 +1,8 @@
+# Creates a `with` across multiple thunk boundaries.
+
+let
+  set = {
+    a = with { b = 42; }; b;
+  };
+in
+set.a
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.exp
new file mode 100644
index 0000000000..edca9baca9
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.exp
@@ -0,0 +1 @@
+{ a = 1; b = 2; c = 3; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.nix
new file mode 100644
index 0000000000..5f25f80671
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.nix
@@ -0,0 +1,11 @@
+# A simple expression with upvalue resolution beyond the target stack
+# index of the root expression.
+
+let
+  a = 1;
+  b = 2;
+  c = 3;
+in
+{
+  inherit a b c;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval-thunk-twice.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval-thunk-twice.exp
new file mode 100644
index 0000000000..b5ba0757c1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval-thunk-twice.exp
@@ -0,0 +1 @@
+[ { success = false; value = false; } { success = false; value = false; } ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval-thunk-twice.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval-thunk-twice.nix
new file mode 100644
index 0000000000..1749643f82
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval-thunk-twice.nix
@@ -0,0 +1 @@
+let x = throw "lol"; in builtins.map (f: f x) [ builtins.tryEval builtins.tryEval ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval.exp
new file mode 100644
index 0000000000..8b6ed7dbac
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval.exp
@@ -0,0 +1 @@
+{ v = false; w = { success = false; value = false; }; x = { success = true; value = "x"; }; y = { success = false; value = false; }; z = { success = false; value = false; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval.nix
new file mode 100644
index 0000000000..e2357c7987
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-tryeval.nix
@@ -0,0 +1,7 @@
+{
+  v = (builtins.tryEval (toString <oink>)).value;
+  w = builtins.tryEval <nope>;
+  x = builtins.tryEval "x";
+  y = builtins.tryEval (assert false; "y");
+  z = builtins.tryEval (throw "bla");
+}
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-unpoison-scope.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-unpoison-scope.exp
new file mode 100644
index 0000000000..5462431496
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-unpoison-scope.exp
@@ -0,0 +1 @@
+[ true false null 1 2 3 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-unpoison-scope.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-unpoison-scope.nix
new file mode 100644
index 0000000000..539735a8ef
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-unpoison-scope.nix
@@ -0,0 +1,10 @@
+let
+  poisoned =
+    let
+      true = 1;
+      false = 2;
+      null = 3;
+    in
+    [ true false null ];
+in
+[ true false null ] ++ poisoned
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-useless-inherit-with.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-useless-inherit-with.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-useless-inherit-with.exp
@@ -0,0 +1 @@
+1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-useless-inherit-with.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-useless-inherit-with.nix
new file mode 100644
index 0000000000..dd768c1aca
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-useless-inherit-with.nix
@@ -0,0 +1,16 @@
+# Normally using an `inherit` without a source attribute set within a
+# `let` is a no-op, *unless* there is a with in-scope that might
+# provide the value.
+
+# Provide a dynamic `x` identifier in the scope.
+with ({ x = 1; });
+
+# inherit this `x` as a static identifier
+let inherit x;
+
+  # Provide another dynamic `x` identifier
+in
+with ({ x = 3; });
+
+# Inherited static identifier should have precedence
+x
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-value-display.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-value-display.exp
new file mode 100644
index 0000000000..c7e3fc6503
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-value-display.exp
@@ -0,0 +1 @@
+[ null true false 42 42 "foo\t\nbar" /home/arthur [ 1 2 3 ] <LAMBDA> <PRIMOP> <PRIMOP-APP> { hello = "world"; } ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-value-display.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-value-display.nix
new file mode 100644
index 0000000000..d34ed1697e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-value-display.nix
@@ -0,0 +1,16 @@
+# Sanity check of how values are rendered by tvix vs. nix-instantiate(1).
+# Ensures that we can use this test suite to compare against C++ Nix.
+[
+  null
+  true
+  false
+  42
+  42.0
+  "foo\t\nbar"
+  /home/arthur
+  [ 1 2 3 ]
+  (x: x)
+  builtins.add
+  (builtins.substring 0 1)
+  { hello = "world"; }
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-compare.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-compare.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-compare.exp
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-compare.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-compare.nix
new file mode 100644
index 0000000000..c2ca913af2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-compare.nix
@@ -0,0 +1,6 @@
+# For an explanation of this behavior see //tvix/docs/value-pointer-equality.md
+let
+  f = owo: "thia";
+in
+
+[ f 42 ] > [ f 21 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-equality.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-equality.exp
new file mode 100644
index 0000000000..aec30f297a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-equality.exp
@@ -0,0 +1 @@
+[ true true true true false false false true true true true true true true true true false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-equality.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-equality.nix
new file mode 100644
index 0000000000..b5cfbeb12e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-value-pointer-equality.nix
@@ -0,0 +1,46 @@
+# For an explanation of this behavior see //tvix/docs/value-pointer-equality.md
+let
+  # Some incomparable values
+  f = MC: "Boing";
+  t = [ (throw "is a little blue man") ];
+  a = { "with" = abort "headphones and a big smile."; };
+
+  # Aliases
+  f' = f;
+  t' = t;
+  a' = a;
+
+  peq1 = a: b: [ a ] == [ b ];
+  peq2 = a: b: { x = a; } == { x = b; };
+in
+
+[
+  # pointer equality of functions
+  (peq1 f f)
+  (peq2 f f)
+  (peq1 f f')
+  (peq2 f f')
+
+  # encapsulation is necessary for pointer equality
+  (f == f)
+  (f == f')
+  # works with !=
+  ([ f ] != [ f' ])
+
+  # thunks that fail to evaluated wrapped in sets/lists
+  (peq1 t t)
+  (peq2 t t)
+  (peq1 a a)
+  (peq2 a a)
+  (peq1 t t')
+  (peq2 t t')
+  (peq1 a' a)
+  (peq2 a' a)
+
+  # function equality with builtins.elem
+  (builtins.elem f [ 21 f 42 ])
+
+  # pointer inequality
+  (peq1 f (x: x))
+  (peq2 (x: x) f)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-with-closure.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-with-closure.exp
new file mode 100644
index 0000000000..fa8f08cb6f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-with-closure.exp
@@ -0,0 +1 @@
+150
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-with-closure.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-with-closure.nix
new file mode 100644
index 0000000000..7e2f7c073b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-with-closure.nix
@@ -0,0 +1,5 @@
+# Upvalues from `with` require special runtime handling. Do they work?
+let
+  f = with { a = 15; }; n: n * a;
+in
+f 10
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-dynamic-key.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-dynamic-key.exp
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-dynamic-key.exp
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-dynamic-key.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-dynamic-key.nix
new file mode 100644
index 0000000000..bf221746c0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-dynamic-key.nix
@@ -0,0 +1,13 @@
+# Tests correct tracking of stack indices within construction of an
+# attribute set. Dynamic keys can be any expression, so something that
+# is extremely sensitive to stack offsets (like `with`) can be tricky.
+
+let
+  set1 = { key = "b"; };
+  set2 = {
+    a = 20;
+    ${with set1; key} = 20;
+    ${with { key = "c"; }; key} = 2;
+  };
+in
+set2.a + set2.b + set2.c
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-list.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-list.exp
new file mode 100644
index 0000000000..5776134d0e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-list.exp
@@ -0,0 +1 @@
+[ 1 2 3 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-list.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-list.nix
new file mode 100644
index 0000000000..3e85cbee45
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-with-in-list.nix
@@ -0,0 +1,14 @@
+# This code causes a situation where a list element causes an
+# additional phantom value to temporarily be placed on the locals
+# stack, which must be correctly accounted for by the compiler.
+
+let
+  set = {
+    value = 2;
+  };
+in
+[
+  1
+  (with set; value)
+  3
+]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-xml.exp.xml b/tvix/eval/src/tests/tvix_tests/eval-okay-xml.exp.xml
new file mode 100644
index 0000000000..1521bcc97a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-xml.exp.xml
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <attrs>
+    <attr name="attrspat">
+      <function>
+        <attrspat name="args">
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="attrspat-ellipsis">
+      <function>
+        <attrspat ellipsis="1" name="args">
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="noattrspat">
+      <function>
+        <attrspat>
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="noattrspat-ellipsis">
+      <function>
+        <attrspat ellipsis="1">
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+  </attrs>
+</expr>
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-xml.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-xml.nix
new file mode 100644
index 0000000000..3cc5acf430
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-xml.nix
@@ -0,0 +1,7 @@
+{
+  attrspat = args@{ x, y, z }: x;
+  attrspat-ellipsis = args@{ x, y, z, ... }: x;
+
+  noattrspat = { x, y, z }: x;
+  noattrspat-ellipsis = { x, y, z, ... }: x;
+}
diff --git a/tvix/eval/src/tests/tvix_tests/identity-bool-false.nix b/tvix/eval/src/tests/tvix_tests/identity-bool-false.nix
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-bool-false.nix
@@ -0,0 +1 @@
+false
diff --git a/tvix/eval/src/tests/tvix_tests/identity-bool-true.nix b/tvix/eval/src/tests/tvix_tests/identity-bool-true.nix
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-bool-true.nix
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/identity-dollar-escape.nix b/tvix/eval/src/tests/tvix_tests/identity-dollar-escape.nix
new file mode 100644
index 0000000000..08951d7637
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-dollar-escape.nix
@@ -0,0 +1 @@
+"\${foobar}"
diff --git a/tvix/eval/src/tests/tvix_tests/identity-empty-attrs.nix b/tvix/eval/src/tests/tvix_tests/identity-empty-attrs.nix
new file mode 100644
index 0000000000..ffcd4415b0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-empty-attrs.nix
@@ -0,0 +1 @@
+{ }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-empty-list.nix b/tvix/eval/src/tests/tvix_tests/identity-empty-list.nix
new file mode 100644
index 0000000000..1e3ec7217a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-empty-list.nix
@@ -0,0 +1 @@
+[ ]
diff --git a/tvix/eval/src/tests/tvix_tests/identity-flat-attrs.nix b/tvix/eval/src/tests/tvix_tests/identity-flat-attrs.nix
new file mode 100644
index 0000000000..e7c2ae18a6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-flat-attrs.nix
@@ -0,0 +1 @@
+{ a = 15; b = "string"; c = null; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-float.nix b/tvix/eval/src/tests/tvix_tests/identity-float.nix
new file mode 100644
index 0000000000..bf77d54968
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-float.nix
@@ -0,0 +1 @@
+4.2
diff --git a/tvix/eval/src/tests/tvix_tests/identity-heterogeneous-list.nix b/tvix/eval/src/tests/tvix_tests/identity-heterogeneous-list.nix
new file mode 100644
index 0000000000..87f7fb0d06
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-heterogeneous-list.nix
@@ -0,0 +1 @@
+[ 1 2.1 "three" null ]
diff --git a/tvix/eval/src/tests/tvix_tests/identity-homogeneous-float-list.nix b/tvix/eval/src/tests/tvix_tests/identity-homogeneous-float-list.nix
new file mode 100644
index 0000000000..48e6655fb1
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-homogeneous-float-list.nix
@@ -0,0 +1 @@
+[ 4.2 6.9 13.37 ]
diff --git a/tvix/eval/src/tests/tvix_tests/identity-homogeneous-int-list.nix b/tvix/eval/src/tests/tvix_tests/identity-homogeneous-int-list.nix
new file mode 100644
index 0000000000..d23a5c3814
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-homogeneous-int-list.nix
@@ -0,0 +1 @@
+[ 0 1 2 3 4 5 7 8 9 ]
diff --git a/tvix/eval/src/tests/tvix_tests/identity-homogeneous-string-list.nix b/tvix/eval/src/tests/tvix_tests/identity-homogeneous-string-list.nix
new file mode 100644
index 0000000000..d78a54e5b0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-homogeneous-string-list.nix
@@ -0,0 +1 @@
+[ "string" "list" ]
diff --git a/tvix/eval/src/tests/tvix_tests/identity-int.nix b/tvix/eval/src/tests/tvix_tests/identity-int.nix
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-int.nix
@@ -0,0 +1 @@
+42
diff --git a/tvix/eval/src/tests/tvix_tests/identity-kv-attrs.nix b/tvix/eval/src/tests/tvix_tests/identity-kv-attrs.nix
new file mode 100644
index 0000000000..f1398b8d05
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-kv-attrs.nix
@@ -0,0 +1 @@
+{ name = "foo"; value = 12; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-nested-attrs.nix b/tvix/eval/src/tests/tvix_tests/identity-nested-attrs.nix
new file mode 100644
index 0000000000..6a139452ef
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-nested-attrs.nix
@@ -0,0 +1 @@
+{ a = { b = null; }; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-null.nix b/tvix/eval/src/tests/tvix_tests/identity-null.nix
new file mode 100644
index 0000000000..19765bd501
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-null.nix
@@ -0,0 +1 @@
+null
diff --git a/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-assert.nix b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-assert.nix
new file mode 100644
index 0000000000..575b1af588
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-assert.nix
@@ -0,0 +1 @@
+{ "assert" = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-else.nix b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-else.nix
new file mode 100644
index 0000000000..7601f14b32
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-else.nix
@@ -0,0 +1 @@
+{ "else" = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-if.nix b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-if.nix
new file mode 100644
index 0000000000..1c391fc9a3
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-if.nix
@@ -0,0 +1 @@
+{ "if" = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-in.nix b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-in.nix
new file mode 100644
index 0000000000..b4f238651d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-in.nix
@@ -0,0 +1 @@
+{ "in" = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-inherit.nix b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-inherit.nix
new file mode 100644
index 0000000000..e62ed32b04
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-inherit.nix
@@ -0,0 +1 @@
+{ "inherit" = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-let.nix b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-let.nix
new file mode 100644
index 0000000000..196ec7cc88
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-let.nix
@@ -0,0 +1 @@
+{ "let" = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-rec.nix b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-rec.nix
new file mode 100644
index 0000000000..d2c4f93a2a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-rec.nix
@@ -0,0 +1 @@
+{ "rec" = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-then.nix b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-then.nix
new file mode 100644
index 0000000000..f2af8d6970
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-then.nix
@@ -0,0 +1 @@
+{ "then" = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-with.nix b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-with.nix
new file mode 100644
index 0000000000..cbcfa970c2
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-quoted-attrname-with.nix
@@ -0,0 +1 @@
+{ "with" = true; }
diff --git a/tvix/eval/src/tests/tvix_tests/identity-signed-float.nix b/tvix/eval/src/tests/tvix_tests/identity-signed-float.nix
new file mode 100644
index 0000000000..50c9d06aa5
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-signed-float.nix
@@ -0,0 +1 @@
+-4.2
diff --git a/tvix/eval/src/tests/tvix_tests/identity-signed-int.nix b/tvix/eval/src/tests/tvix_tests/identity-signed-int.nix
new file mode 100644
index 0000000000..6a0e60d48b
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-signed-int.nix
@@ -0,0 +1 @@
+-42
diff --git a/tvix/eval/src/tests/tvix_tests/identity-string.nix b/tvix/eval/src/tests/tvix_tests/identity-string.nix
new file mode 100644
index 0000000000..d71ddbcf82
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/identity-string.nix
@@ -0,0 +1 @@
+"test string"
diff --git a/tvix/eval/src/tests/tvix_tests/lib.nix b/tvix/eval/src/tests/tvix_tests/lib.nix
new file mode 100644
index 0000000000..ab509bc85f
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/lib.nix
@@ -0,0 +1,64 @@
+with builtins;
+
+rec {
+
+  fold = op: nul: list:
+    if list == [ ]
+    then nul
+    else op (head list) (fold op nul (tail list));
+
+  concat =
+    fold (x: y: x + y) "";
+
+  and = fold (x: y: x && y) true;
+
+  flatten = x:
+    if isList x
+    then fold (x: y: (flatten x) ++ y) [ ] x
+    else [ x ];
+
+  sum = foldl' (x: y: add x y) 0;
+
+  hasSuffix = ext: fileName:
+    let
+      lenFileName = stringLength fileName;
+      lenExt = stringLength ext;
+    in
+    !(lessThan lenFileName lenExt) &&
+    substring (sub lenFileName lenExt) lenFileName fileName == ext;
+
+  # Split a list at the given position.
+  splitAt = pos: list:
+    if pos == 0 then { first = [ ]; second = list; } else
+    if list == [ ] then { first = [ ]; second = [ ]; } else
+    let res = splitAt (sub pos 1) (tail list);
+    in { first = [ (head list) ] ++ res.first; second = res.second; };
+
+  # Stable merge sort.
+  sortBy = comp: list:
+    if lessThan 1 (length list)
+    then
+      let
+        split = splitAt (div (length list) 2) list;
+        first = sortBy comp split.first;
+        second = sortBy comp split.second;
+      in
+      mergeLists comp first second
+    else list;
+
+  mergeLists = comp: list1: list2:
+    if list1 == [ ] then list2 else
+    if list2 == [ ] then list1 else
+    if comp (head list2) (head list1) then [ (head list2) ] ++ mergeLists comp list1 (tail list2) else
+    [ (head list1) ] ++ mergeLists comp (tail list1) list2;
+
+  id = x: x;
+
+  const = x: y: x;
+
+  range = first: last:
+    if first > last
+    then [ ]
+    else genList (n: first + n) (last - first + 1);
+
+}
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-fail-builtins-genericClosure-uncomparable-keys.nix b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-fail-builtins-genericClosure-uncomparable-keys.nix
new file mode 100644
index 0000000000..d4e93e1f28
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-fail-builtins-genericClosure-uncomparable-keys.nix
@@ -0,0 +1,9 @@
+# Attribute sets can't be compared, only checked for equality
+builtins.genericClosure {
+  startSet = [
+    { key = { foo = 21; }; }
+  ];
+  operator = _: [
+    { key = { bar = 21; }; }
+  ];
+}
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-fail-builtins-genericClosure-uncomparable-keys2.nix b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-fail-builtins-genericClosure-uncomparable-keys2.nix
new file mode 100644
index 0000000000..0589a3ab59
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-fail-builtins-genericClosure-uncomparable-keys2.nix
@@ -0,0 +1,12 @@
+let
+  id = x: x;
+in
+
+builtins.genericClosure {
+  startSet = [{ key = id; first = true; }];
+  operator =
+    { first, ... }:
+    if first then [
+      { key = id; first = false; }
+    ] else [ ];
+}
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-builtins-set-pointer-equality.exp b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-builtins-set-pointer-equality.exp
new file mode 100644
index 0000000000..097eb2033a
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-builtins-set-pointer-equality.exp
@@ -0,0 +1 @@
+[ true true true true true true true true true true ]
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-builtins-set-pointer-equality.nix b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-builtins-set-pointer-equality.nix
new file mode 100644
index 0000000000..aa2a0a1e19
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-builtins-set-pointer-equality.nix
@@ -0,0 +1,25 @@
+let
+  alias = builtins;
+in
+
+[
+  (builtins == builtins)
+  (alias == builtins)
+  (builtins == builtins.builtins)
+  (builtins.builtins == builtins.builtins)
+  (builtins.builtins == builtins.builtins.builtins)
+  (alias == alias)
+  (alias == builtins.builtins)
+  ([ builtins ] == [ builtins ])
+
+  # Surprisingly the following expressions don't work. They are
+  # here for documentation purposes and covered only
+  # by eval-okay-select-pointer-inequality.nix. Reasoning is that
+  # we may not want / be able to replicate this behavior at all.
+  #   ([ builtins.add ] == [ builtins.add ])
+  #   ({ inherit (builtins) import; } == { inherit (builtins) import; })
+
+  # These expressions work as expected, however:
+  (let x = { inherit (builtins) add; }; in x == x)
+  (let inherit (builtins) add; in [ add ] == [ add ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-cycle-display-cpp-nix-2.13.exp b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-cycle-display-cpp-nix-2.13.exp
new file mode 100644
index 0000000000..9c44023f02
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-cycle-display-cpp-nix-2.13.exp
@@ -0,0 +1 @@
+[ { car = 42; cdr = Β«repeatedΒ»; } [ Β«repeatedΒ» Β«repeatedΒ» Β«repeatedΒ» ] { val = 42; wal = Β«repeatedΒ»; xal = Β«repeatedΒ»; } { tail1 = Β«repeatedΒ»; tail2 = Β«repeatedΒ»; val = 42; } { tail1 = Β«repeatedΒ»; tail2 = Β«repeatedΒ»; val = 21; } ]
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-cycle-display-cpp-nix-2.13.nix b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-cycle-display-cpp-nix-2.13.nix
new file mode 100644
index 0000000000..ac849a58fe
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-cycle-display-cpp-nix-2.13.nix
@@ -0,0 +1,34 @@
+let
+  linkedList = {
+    car = 42;
+    cdr = linkedList;
+  };
+
+  list = [
+    linkedList
+    linkedList
+    linkedList
+  ];
+
+  set = {
+    val = 42;
+    wal = set;
+    xal = set;
+  };
+
+  multiTail = {
+    val = 42;
+    tail1 = multiTail;
+    tail2 = multiTail;
+  };
+in
+
+[
+  linkedList
+  list
+  set
+
+  # In C++ Nix 2.3 these would be displayed differently
+  multiTail
+  (let multiTail = { val = 21; tail1 = multiTail; tail2 = multiTail; }; in multiTail)
+]
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-minimal-2.3-builtins.exp b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-minimal-2.3-builtins.exp
new file mode 100644
index 0000000000..967fc858bc
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-minimal-2.3-builtins.exp
@@ -0,0 +1 @@
+[ "abort" "add" "addErrorContext" "all" "any" "appendContext" "attrNames" "attrValues" "baseNameOf" "bitAnd" "bitOr" "bitXor" "builtins" "catAttrs" "compareVersions" "concatLists" "concatMap" "concatStringsSep" "currentSystem" "currentTime" "deepSeq" "derivation" "derivationStrict" "dirOf" "div" "elem" "elemAt" "false" "fetchGit" "fetchMercurial" "fetchTarball" "fetchurl" "filter" "filterSource" "findFile" "foldl'" "fromJSON" "fromTOML" "functionArgs" "genList" "genericClosure" "getAttr" "getContext" "getEnv" "hasAttr" "hasContext" "hashFile" "hashString" "head" "import" "intersectAttrs" "isAttrs" "isBool" "isFloat" "isFunction" "isInt" "isList" "isNull" "isPath" "isString" "langVersion" "length" "lessThan" "listToAttrs" "map" "mapAttrs" "match" "mul" "nixPath" "nixVersion" "null" "parseDrvName" "partition" "path" "pathExists" "placeholder" "readDir" "readFile" "removeAttrs" "replaceStrings" "scopedImport" "seq" "sort" "split" "splitVersion" "storeDir" "storePath" "stringLength" "sub" "substring" "tail" "throw" "toFile" "toJSON" "toPath" "toString" "toXML" "trace" "true" "tryEval" "typeOf" "unsafeDiscardOutputDependency" "unsafeDiscardStringContext" "unsafeGetAttrPos" ]
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-minimal-2.3-builtins.nix b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-minimal-2.3-builtins.nix
new file mode 100644
index 0000000000..4480daecd9
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-minimal-2.3-builtins.nix
@@ -0,0 +1,122 @@
+# This tests verifies that the Nix implementation evaluating this has at least
+# all the builtins given in `minimalBuiltins`. We don't test a precise list of
+# builtins since we accept that there will always be difference between the
+# builtins sets of Tvix, C++ Nix 2.3 and newer C++ Nix versions, as new builtins
+# are added.
+#
+# Tvix also may choose never to implement some builtins if they are only useful
+# for flakes or perform well enough via the shims nixpkgs usually provides.
+
+let
+  # C++ Nix 2.3 builtins except valueSize which is removed in later versions
+  minimalBuiltins = [
+    "abort"
+    "add"
+    "addErrorContext"
+    "all"
+    "any"
+    "appendContext"
+    "attrNames"
+    "attrValues"
+    "baseNameOf"
+    "bitAnd"
+    "bitOr"
+    "bitXor"
+    "builtins"
+    "catAttrs"
+    "compareVersions"
+    "concatLists"
+    "concatMap"
+    "concatStringsSep"
+    "currentSystem"
+    "currentTime"
+    "deepSeq"
+    "derivation"
+    "derivationStrict"
+    "dirOf"
+    "div"
+    "elem"
+    "elemAt"
+    "false"
+    "fetchGit"
+    "fetchMercurial"
+    "fetchTarball"
+    "fetchurl"
+    "filter"
+    "filterSource"
+    "findFile"
+    "foldl'"
+    "fromJSON"
+    "fromTOML"
+    "functionArgs"
+    "genList"
+    "genericClosure"
+    "getAttr"
+    "getContext"
+    "getEnv"
+    "hasAttr"
+    "hasContext"
+    "hashFile"
+    "hashString"
+    "head"
+    "import"
+    "intersectAttrs"
+    "isAttrs"
+    "isBool"
+    "isFloat"
+    "isFunction"
+    "isInt"
+    "isList"
+    "isNull"
+    "isPath"
+    "isString"
+    "langVersion"
+    "length"
+    "lessThan"
+    "listToAttrs"
+    "map"
+    "mapAttrs"
+    "match"
+    "mul"
+    "nixPath"
+    "nixVersion"
+    "null"
+    "parseDrvName"
+    "partition"
+    "path"
+    "pathExists"
+    "placeholder"
+    "readDir"
+    "readFile"
+    "removeAttrs"
+    "replaceStrings"
+    "scopedImport"
+    "seq"
+    "sort"
+    "split"
+    "splitVersion"
+    "storeDir"
+    "storePath"
+    "stringLength"
+    "sub"
+    "substring"
+    "tail"
+    "throw"
+    "toFile"
+    "toJSON"
+    "toPath"
+    "toString"
+    "toXML"
+    "trace"
+    "true"
+    "tryEval"
+    "typeOf"
+    "unsafeDiscardOutputDependency"
+    "unsafeDiscardStringContext"
+    "unsafeGetAttrPos"
+  ];
+
+  intersectLists = as: bs: builtins.filter (a: builtins.elem a bs) as;
+in
+
+intersectLists minimalBuiltins (builtins.attrNames builtins)
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-non-identifier-pointer-inequality.exp b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-non-identifier-pointer-inequality.exp
new file mode 100644
index 0000000000..69fd1d0847
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-non-identifier-pointer-inequality.exp
@@ -0,0 +1 @@
+[ false false false false false true false false ]
diff --git a/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-non-identifier-pointer-inequality.nix b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-non-identifier-pointer-inequality.nix
new file mode 100644
index 0000000000..821aa47a0d
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/notyetpassing/eval-okay-non-identifier-pointer-inequality.nix
@@ -0,0 +1,28 @@
+# C++ Nix frequently creates copies of Value structs when evaluating
+# a variety of expressions. As a result, pointer equality doesn't
+# work for many (all?) expressions that go beyond simple identifier
+# access from the scope: Even if the inner representation of the
+# value still has the same memory location, C++ Nix has created
+# a copy of the struct that holds the pointer to this memory.
+# Since pointer equality is established via the location of
+# the latter, not the former, the values are no longer equal
+# by pointer.
+let
+  foo = { bar = x: x; };
+
+  id = x: x;
+in
+
+[
+  ({ inherit (foo) bar; } == { inherit (foo) bar; })
+  ([ foo.bar ] == [ foo.bar ])
+
+  ([ builtins.add ] == [ builtins.add ])
+  ({ inherit (builtins) import; } == { inherit (builtins) import; })
+
+  ([ (id id) ] == [ (id id) ])
+  ([ id ] == [ id ])
+
+  (with foo; [ bar ] == [ bar ])
+  (with builtins; [ add ] == [ add ])
+]
diff --git a/tvix/eval/src/tests/tvix_tests/observable-eval-cache1.nix b/tvix/eval/src/tests/tvix_tests/observable-eval-cache1.nix
new file mode 100644
index 0000000000..b5f3f59a79
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/observable-eval-cache1.nix
@@ -0,0 +1 @@
+let id = x: x; in { inherit id; }
diff --git a/tvix/eval/src/tests/tvix_tests/observable-eval-cache2.nix b/tvix/eval/src/tests/tvix_tests/observable-eval-cache2.nix
new file mode 120000
index 0000000000..7f69c0eb47
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/observable-eval-cache2.nix
@@ -0,0 +1 @@
+observable-eval-cache1.nix
\ No newline at end of file
diff --git a/tvix/eval/src/tests/tvix_tests/observable-eval-cache3.nix b/tvix/eval/src/tests/tvix_tests/observable-eval-cache3.nix
new file mode 100644
index 0000000000..b5f3f59a79
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/observable-eval-cache3.nix
@@ -0,0 +1 @@
+let id = x: x; in { inherit id; }
diff --git a/users/grfn/system/home/.skip-subtree b/tvix/eval/src/tests/tvix_tests/readDir/bar
index e69de29bb2..e69de29bb2 100644
--- a/users/grfn/system/home/.skip-subtree
+++ b/tvix/eval/src/tests/tvix_tests/readDir/bar
diff --git a/users/grfn/system/system/.skip-subtree b/tvix/eval/src/tests/tvix_tests/readDir/foo/.keep
index e69de29bb2..e69de29bb2 100644
--- a/users/grfn/system/system/.skip-subtree
+++ b/tvix/eval/src/tests/tvix_tests/readDir/foo/.keep
diff --git a/tvix/eval/src/upvalues.rs b/tvix/eval/src/upvalues.rs
new file mode 100644
index 0000000000..687d6850cc
--- /dev/null
+++ b/tvix/eval/src/upvalues.rs
@@ -0,0 +1,86 @@
+//! This module encapsulates some logic for upvalue handling, which is
+//! relevant to both thunks (delayed computations for lazy-evaluation)
+//! as well as closures (lambdas that capture variables from the
+//! surrounding scope).
+//!
+//! The upvalues of a scope are whatever data are needed at runtime
+//! in order to resolve each free variable in the scope to a value.
+//! "Upvalue" is a term taken from Lua.
+
+use std::ops::Index;
+
+use crate::{opcode::UpvalueIdx, Value};
+
+/// Structure for carrying upvalues of an UpvalueCarrier.  The
+/// implementation of this struct encapsulates the logic for
+/// capturing and accessing upvalues.
+///
+/// Nix's `with` cannot be used to shadow an enclosing binding --
+/// like Rust's `use xyz::*` construct, but unlike Javascript's
+/// `with (xyz)`.  This means that Nix has two kinds of identifiers,
+/// which can be distinguished at compile time:
+///
+/// - Static identifiers, which are bound in some enclosing scope by
+///   `let`, `name:` or `{name}:`
+/// - Dynamic identifiers, which are not bound in any enclosing
+///   scope
+#[derive(Clone, Debug)]
+pub struct Upvalues {
+    /// The upvalues of static identifiers.  Each static identifier
+    /// is assigned an integer identifier at compile time, which is
+    /// an index into this Vec.
+    static_upvalues: Vec<Value>,
+
+    /// The upvalues of dynamic identifiers, if any exist.  This
+    /// consists of the value passed to each enclosing `with val;`,
+    /// from outermost to innermost.
+    with_stack: Option<Vec<Value>>,
+}
+
+impl Upvalues {
+    pub fn with_capacity(count: usize) -> Self {
+        Upvalues {
+            static_upvalues: Vec::with_capacity(count),
+            with_stack: None,
+        }
+    }
+
+    /// Push an upvalue at the end of the upvalue list.
+    pub fn push(&mut self, value: Value) {
+        self.static_upvalues.push(value);
+    }
+
+    /// Set the captured with stack.
+    pub fn set_with_stack(&mut self, with_stack: Vec<Value>) {
+        self.with_stack = Some(with_stack);
+    }
+
+    pub fn with_stack(&self) -> Option<&Vec<Value>> {
+        self.with_stack.as_ref()
+    }
+
+    pub fn with_stack_len(&self) -> usize {
+        match &self.with_stack {
+            None => 0,
+            Some(stack) => stack.len(),
+        }
+    }
+
+    /// Resolve deferred upvalues from the provided stack slice,
+    /// mutating them in the internal upvalue slots.
+    pub fn resolve_deferred_upvalues(&mut self, stack: &[Value]) {
+        for upvalue in self.static_upvalues.iter_mut() {
+            if let Value::DeferredUpvalue(update_from_idx) = upvalue {
+                *upvalue = stack[update_from_idx.0].clone();
+            }
+        }
+    }
+}
+
+impl Index<UpvalueIdx> for Upvalues {
+    type Output = Value;
+
+    fn index(&self, index: UpvalueIdx) -> &Self::Output {
+        &self.static_upvalues[index.0]
+    }
+}
diff --git a/tvix/eval/src/value/arbitrary.rs b/tvix/eval/src/value/arbitrary.rs
new file mode 100644
index 0000000000..bf53f4fcb2
--- /dev/null
+++ b/tvix/eval/src/value/arbitrary.rs
@@ -0,0 +1,106 @@
+//! Support for configurable generation of arbitrary nix values
+
+use imbl::proptest::{ord_map, vector};
+use proptest::{prelude::*, strategy::BoxedStrategy};
+use std::ffi::OsString;
+
+use super::{attrs::AttrsRep, NixAttrs, NixList, NixString, Value};
+
+#[derive(Clone)]
+pub enum Parameters {
+    Strategy(BoxedStrategy<Value>),
+    Parameters {
+        generate_internal_values: bool,
+        generate_functions: bool,
+        generate_nested: bool,
+    },
+}
+
+impl Default for Parameters {
+    fn default() -> Self {
+        Self::Parameters {
+            generate_internal_values: false,
+            generate_functions: false,
+            generate_nested: true,
+        }
+    }
+}
+
+impl Arbitrary for NixAttrs {
+    type Parameters = Parameters;
+    type Strategy = BoxedStrategy<Self>;
+
+    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
+        prop_oneof![
+            // Empty attrs representation
+            Just(Self(AttrsRep::Empty)),
+            // KV representation (name/value pairs)
+            (
+                any_with::<Value>(args.clone()),
+                any_with::<Value>(args.clone())
+            )
+                .prop_map(|(name, value)| Self(AttrsRep::KV { name, value })),
+            // Map representation
+            ord_map(NixString::arbitrary(), Value::arbitrary_with(args), 0..100)
+                .prop_map(|map| Self(AttrsRep::Im(map)))
+        ]
+        .boxed()
+    }
+}
+
+impl Arbitrary for NixList {
+    type Parameters = <Value as Arbitrary>::Parameters;
+    type Strategy = BoxedStrategy<Self>;
+
+    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
+        vector(<Value as Arbitrary>::arbitrary_with(args), 0..100)
+            .prop_map(NixList::from)
+            .boxed()
+    }
+}
+
+impl Arbitrary for Value {
+    type Parameters = Parameters;
+    type Strategy = BoxedStrategy<Self>;
+
+    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
+        match args {
+            Parameters::Strategy(s) => s,
+            Parameters::Parameters {
+                generate_internal_values,
+                generate_functions,
+                generate_nested,
+            } => {
+                if generate_internal_values || generate_functions {
+                    todo!("Generating internal values and functions not implemented yet")
+                } else if generate_nested {
+                    non_internal_value().boxed()
+                } else {
+                    leaf_value().boxed()
+                }
+            }
+        }
+    }
+}
+
+fn leaf_value() -> impl Strategy<Value = Value> {
+    use Value::*;
+
+    prop_oneof![
+        Just(Null),
+        any::<bool>().prop_map(Bool),
+        any::<i64>().prop_map(Integer),
+        any::<f64>().prop_map(Float),
+        any::<NixString>().prop_map(String),
+        any::<OsString>().prop_map(|s| Path(Box::new(s.into()))),
+    ]
+}
+
+fn non_internal_value() -> impl Strategy<Value = Value> {
+    leaf_value().prop_recursive(3, 5, 5, |inner| {
+        prop_oneof![
+            NixAttrs::arbitrary_with(Parameters::Strategy(inner.clone())).prop_map(Value::attrs),
+            any_with::<NixList>(Parameters::Strategy(inner)).prop_map(Value::List)
+        ]
+    })
+}
diff --git a/tvix/eval/src/value/attrs.rs b/tvix/eval/src/value/attrs.rs
new file mode 100644
index 0000000000..33259c8058
--- /dev/null
+++ b/tvix/eval/src/value/attrs.rs
@@ -0,0 +1,621 @@
+//! This module implements Nix attribute sets. They have flexible
+//! backing implementations, as they are used in very versatile
+//! use-cases that are all exposed the same way in the language
+//! surface.
+//!
+//! Due to this, construction and management of attribute sets has
+//! some peculiarities that are encapsulated within this module.
+use std::borrow::Borrow;
+use std::iter::FromIterator;
+
+use bstr::BStr;
+use imbl::{ordmap, OrdMap};
+use lazy_static::lazy_static;
+use serde::de::{Deserializer, Error, Visitor};
+use serde::Deserialize;
+
+use super::string::NixString;
+use super::thunk::ThunkSet;
+use super::TotalDisplay;
+use super::Value;
+use crate::errors::ErrorKind;
+use crate::CatchableErrorKind;
+
+lazy_static! {
+    static ref NAME_S: NixString = "name".into();
+    static ref NAME_REF: &'static NixString = &NAME_S;
+    static ref VALUE_S: NixString = "value".into();
+    static ref VALUE_REF: &'static NixString = &VALUE_S;
+}
+
+#[cfg(test)]
+mod tests;
+
+#[derive(Clone, Debug, Deserialize, Default)]
+pub(super) enum AttrsRep {
+    #[default]
+    Empty,
+
+    Im(OrdMap<NixString, Value>),
+
+    /// Warning: this represents a **two**-attribute attrset, with
+    /// attribute names "name" and "value", like `{name="foo";
+    /// value="bar";}`, *not* `{foo="bar";}`!
+    KV {
+        name: Value,
+        value: Value,
+    },
+}
+
+impl AttrsRep {
+    /// Retrieve reference to a mutable map inside of an attrs,
+    /// optionally changing the representation if required.
+    fn map_mut(&mut self) -> &mut OrdMap<NixString, Value> {
+        match self {
+            AttrsRep::Im(m) => m,
+
+            AttrsRep::Empty => {
+                *self = AttrsRep::Im(OrdMap::new());
+                self.map_mut()
+            }
+
+            AttrsRep::KV { name, value } => {
+                *self = AttrsRep::Im(ordmap! {
+                    NAME_S.clone() => name.clone(),
+                    VALUE_S.clone() => value.clone()
+                });
+
+                self.map_mut()
+            }
+        }
+    }
+
+    fn select(&self, key: &BStr) -> Option<&Value> {
+        match self {
+            AttrsRep::Empty => None,
+
+            AttrsRep::KV { name, value } => match &**key {
+                b"name" => Some(name),
+                b"value" => Some(value),
+                _ => None,
+            },
+
+            AttrsRep::Im(map) => map.get(key),
+        }
+    }
+
+    fn contains(&self, key: &BStr) -> bool {
+        match self {
+            AttrsRep::Empty => false,
+            AttrsRep::KV { .. } => key == "name" || key == "value",
+            AttrsRep::Im(map) => map.contains_key(key),
+        }
+    }
+}
+
+#[repr(transparent)]
+#[derive(Clone, Debug, Default)]
+pub struct NixAttrs(pub(super) AttrsRep);
+
+impl From<OrdMap<NixString, Value>> for NixAttrs {
+    fn from(map: OrdMap<NixString, Value>) -> Self {
+        NixAttrs(AttrsRep::Im(map))
+    }
+}
+
+impl<K, V> FromIterator<(K, V)> for NixAttrs
+where
+    NixString: From<K>,
+    Value: From<V>,
+{
+    fn from_iter<T>(iter: T) -> NixAttrs
+    where
+        T: IntoIterator<Item = (K, V)>,
+    {
+        NixAttrs(AttrsRep::Im(iter.into_iter().collect()))
+    }
+}
+
+impl TotalDisplay for NixAttrs {
+    fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
+        f.write_str("{ ")?;
+
+        match &self.0 {
+            AttrsRep::KV { name, value } => {
+                f.write_str("name = ")?;
+                name.total_fmt(f, set)?;
+                f.write_str("; ")?;
+
+                f.write_str("value = ")?;
+                value.total_fmt(f, set)?;
+                f.write_str("; ")?;
+            }
+
+            AttrsRep::Im(map) => {
+                for (name, value) in map {
+                    write!(f, "{} = ", name.ident_str())?;
+                    value.total_fmt(f, set)?;
+                    f.write_str("; ")?;
+                }
+            }
+
+            AttrsRep::Empty => { /* no values to print! */ }
+        }
+
+        f.write_str("}")
+    }
+}
+
+impl<'de> Deserialize<'de> for NixAttrs {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct MapVisitor;
+
+        impl<'de> Visitor<'de> for MapVisitor {
+            type Value = NixAttrs;
+
+            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+                formatter.write_str("a valid Nix attribute set")
+            }
+
+            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
+            where
+                A: serde::de::MapAccess<'de>,
+            {
+                let mut stack_array = Vec::with_capacity(map.size_hint().unwrap_or(0) * 2);
+
+                while let Some((key, value)) = map.next_entry()? {
+                    stack_array.push(key);
+                    stack_array.push(value);
+                }
+
+                Ok(NixAttrs::construct(stack_array.len() / 2, stack_array)
+                    .map_err(A::Error::custom)?
+                    .expect("Catchable values are unreachable here"))
+            }
+        }
+
+        deserializer.deserialize_map(MapVisitor)
+    }
+}
+
+impl NixAttrs {
+    pub fn empty() -> Self {
+        Self(AttrsRep::Empty)
+    }
+
+    /// Compare two attribute sets by pointer equality. Only makes
+    /// sense for some attribute set reprsentations, i.e. returning
+    /// `false` does not mean that the two attribute sets do not have
+    /// equal *content*.
+    pub fn ptr_eq(&self, other: &Self) -> bool {
+        match (&self.0, &other.0) {
+            (AttrsRep::Im(lhs), AttrsRep::Im(rhs)) => lhs.ptr_eq(rhs),
+            _ => false,
+        }
+    }
+
+    /// Return an attribute set containing the merge of the two
+    /// provided sets. Keys from the `other` set have precedence.
+    pub fn update(self, other: Self) -> Self {
+        // Short-circuit on some optimal cases:
+        match (&self.0, &other.0) {
+            (AttrsRep::Empty, AttrsRep::Empty) => return self,
+            (AttrsRep::Empty, _) => return other,
+            (_, AttrsRep::Empty) => return self,
+            (AttrsRep::KV { .. }, AttrsRep::KV { .. }) => return other,
+
+            // Explicitly handle all branches instead of falling
+            // through, to ensure that we get at least some compiler
+            // errors if variants are modified.
+            (AttrsRep::Im(_), AttrsRep::Im(_))
+            | (AttrsRep::Im(_), AttrsRep::KV { .. })
+            | (AttrsRep::KV { .. }, AttrsRep::Im(_)) => {}
+        };
+
+        // Slightly more advanced, but still optimised updates
+        match (self.0, other.0) {
+            (AttrsRep::Im(mut m), AttrsRep::KV { name, value }) => {
+                m.insert(NAME_S.clone(), name);
+                m.insert(VALUE_S.clone(), value);
+                NixAttrs(AttrsRep::Im(m))
+            }
+
+            (AttrsRep::KV { name, value }, AttrsRep::Im(mut m)) => {
+                match m.entry(NAME_S.clone()) {
+                    imbl::ordmap::Entry::Vacant(e) => {
+                        e.insert(name);
+                    }
+
+                    imbl::ordmap::Entry::Occupied(_) => { /* name from `m` has precedence */ }
+                };
+
+                match m.entry(VALUE_S.clone()) {
+                    imbl::ordmap::Entry::Vacant(e) => {
+                        e.insert(value);
+                    }
+
+                    imbl::ordmap::Entry::Occupied(_) => { /* value from `m` has precedence */ }
+                };
+
+                NixAttrs(AttrsRep::Im(m))
+            }
+
+            // Plain merge of maps.
+            (AttrsRep::Im(m1), AttrsRep::Im(m2)) => NixAttrs(AttrsRep::Im(m2.union(m1))),
+
+            // Cases handled above by the borrowing match:
+            _ => unreachable!(),
+        }
+    }
+
+    /// Return the number of key-value entries in an attrset.
+    pub fn len(&self) -> usize {
+        match &self.0 {
+            AttrsRep::Im(map) => map.len(),
+            AttrsRep::Empty => 0,
+            AttrsRep::KV { .. } => 2,
+        }
+    }
+
+    pub fn is_empty(&self) -> bool {
+        match &self.0 {
+            AttrsRep::Im(map) => map.is_empty(),
+            AttrsRep::Empty => true,
+            AttrsRep::KV { .. } => false,
+        }
+    }
+
+    /// Select a value from an attribute set by key.
+    pub fn select<K>(&self, key: &K) -> Option<&Value>
+    where
+        K: Borrow<BStr> + ?Sized,
+    {
+        self.0.select(key.borrow())
+    }
+
+    /// Select a required value from an attribute set by key, return
+    /// an `AttributeNotFound` error if it is missing.
+    pub fn select_required<K>(&self, key: &K) -> Result<&Value, ErrorKind>
+    where
+        K: Borrow<BStr> + ?Sized,
+    {
+        self.select(key)
+            .ok_or_else(|| ErrorKind::AttributeNotFound {
+                name: key.borrow().to_string(),
+            })
+    }
+
+    pub fn contains<'a, K: 'a>(&self, key: K) -> bool
+    where
+        &'a BStr: From<K>,
+    {
+        self.0.contains(key.into())
+    }
+
+    /// Construct an iterator over all the key-value pairs in the attribute set.
+    #[allow(clippy::needless_lifetimes)]
+    pub fn iter<'a>(&'a self) -> Iter<KeyValue<'a>> {
+        Iter(match &self.0 {
+            AttrsRep::Im(map) => KeyValue::Im(map.iter()),
+            AttrsRep::Empty => KeyValue::Empty,
+
+            AttrsRep::KV {
+                ref name,
+                ref value,
+            } => KeyValue::KV {
+                name,
+                value,
+                at: IterKV::default(),
+            },
+        })
+    }
+
+    /// Same as iter(), but marks call sites which rely on the
+    /// iteration being lexicographic.
+    pub fn iter_sorted(&self) -> Iter<KeyValue<'_>> {
+        self.iter()
+    }
+
+    /// Same as [IntoIterator::into_iter], but marks call sites which rely on the
+    /// iteration being lexicographic.
+    pub fn into_iter_sorted(self) -> OwnedAttrsIterator {
+        self.into_iter()
+    }
+
+    /// Construct an iterator over all the keys of the attribute set
+    pub fn keys(&self) -> Keys {
+        Keys(match &self.0 {
+            AttrsRep::Empty => KeysInner::Empty,
+            AttrsRep::Im(m) => KeysInner::Im(m.keys()),
+            AttrsRep::KV { .. } => KeysInner::KV(IterKV::default()),
+        })
+    }
+
+    /// Implement construction logic of an attribute set, to encapsulate
+    /// logic about attribute set optimisations inside of this module.
+    pub fn construct(
+        count: usize,
+        mut stack_slice: Vec<Value>,
+    ) -> Result<Result<Self, CatchableErrorKind>, ErrorKind> {
+        debug_assert!(
+            stack_slice.len() == count * 2,
+            "construct_attrs called with count == {}, but slice.len() == {}",
+            count,
+            stack_slice.len(),
+        );
+
+        // Optimisation: Empty attribute set
+        if count == 0 {
+            return Ok(Ok(NixAttrs(AttrsRep::Empty)));
+        }
+
+        // Optimisation: KV pattern
+        if count == 2 {
+            if let Some(kv) = attempt_optimise_kv(&mut stack_slice) {
+                return Ok(Ok(kv));
+            }
+        }
+
+        let mut attrs = NixAttrs(AttrsRep::Im(OrdMap::new()));
+
+        for _ in 0..count {
+            let value = stack_slice.pop().unwrap();
+            let key = stack_slice.pop().unwrap();
+
+            match key {
+                Value::String(ks) => set_attr(&mut attrs, ks, value)?,
+
+                Value::Null => {
+                    // This is in fact valid, but leads to the value
+                    // being ignored and nothing being set, i.e. `{
+                    // ${null} = 1; } => { }`.
+                    continue;
+                }
+
+                Value::Catchable(err) => return Ok(Err(*err)),
+
+                other => return Err(ErrorKind::InvalidAttributeName(other)),
+            }
+        }
+
+        Ok(Ok(attrs))
+    }
+
+    /// Construct an optimized "KV"-style attribute set given the value for the
+    /// `"name"` key, and the value for the `"value"` key
+    pub(crate) fn from_kv(name: Value, value: Value) -> Self {
+        NixAttrs(AttrsRep::KV { name, value })
+    }
+}
+
+impl IntoIterator for NixAttrs {
+    type Item = (NixString, Value);
+    type IntoIter = OwnedAttrsIterator;
+
+    fn into_iter(self) -> Self::IntoIter {
+        match self.0 {
+            AttrsRep::Empty => OwnedAttrsIterator(IntoIterRepr::Empty),
+            AttrsRep::KV { name, value } => OwnedAttrsIterator(IntoIterRepr::Finite(
+                vec![(NAME_REF.clone(), name), (VALUE_REF.clone(), value)].into_iter(),
+            )),
+            AttrsRep::Im(map) => OwnedAttrsIterator(IntoIterRepr::Im(map.into_iter())),
+        }
+    }
+}
+
+/// In Nix, name/value attribute pairs are frequently constructed from
+/// literals. This particular case should avoid allocation of a map,
+/// additional heap values etc. and use the optimised `KV` variant
+/// instead.
+///
+/// ```norust
+/// `slice` is the top of the stack from which the attrset is being
+/// constructed, e.g.
+///
+///   slice: [ "value" 5 "name" "foo" ]
+///   index:   0       1 2      3
+///   stack:   3       2 1      0
+/// ```
+fn attempt_optimise_kv(slice: &mut [Value]) -> Option<NixAttrs> {
+    let (name_idx, value_idx) = {
+        match (&slice[2], &slice[0]) {
+            (Value::String(s1), Value::String(s2)) if (*s1 == *NAME_S && *s2 == *VALUE_S) => (3, 1),
+            (Value::String(s1), Value::String(s2)) if (*s1 == *VALUE_S && *s2 == *NAME_S) => (1, 3),
+
+            // Technically this branch lets type errors pass,
+            // but they will be caught during normal attribute
+            // set construction instead.
+            _ => return None,
+        }
+    };
+
+    Some(NixAttrs::from_kv(
+        slice[name_idx].clone(),
+        slice[value_idx].clone(),
+    ))
+}
+
+/// Set an attribute on an in-construction attribute set, while
+/// checking against duplicate keys.
+fn set_attr(attrs: &mut NixAttrs, key: NixString, value: Value) -> Result<(), ErrorKind> {
+    match attrs.0.map_mut().entry(key) {
+        imbl::ordmap::Entry::Occupied(entry) => Err(ErrorKind::DuplicateAttrsKey {
+            key: entry.key().to_string(),
+        }),
+
+        imbl::ordmap::Entry::Vacant(entry) => {
+            entry.insert(value);
+            Ok(())
+        }
+    }
+}
+
+/// Internal helper type to track the iteration status of an iterator
+/// over the name/value representation.
+#[derive(Debug, Default)]
+pub enum IterKV {
+    #[default]
+    Name,
+    Value,
+    Done,
+}
+
+impl IterKV {
+    fn next(&mut self) {
+        match *self {
+            Self::Name => *self = Self::Value,
+            Self::Value => *self = Self::Done,
+            Self::Done => {}
+        }
+    }
+}
+
+/// Iterator representation over the keys *and* values of an attribute
+/// set.
+pub enum KeyValue<'a> {
+    Empty,
+
+    KV {
+        name: &'a Value,
+        value: &'a Value,
+        at: IterKV,
+    },
+
+    Im(imbl::ordmap::Iter<'a, NixString, Value>),
+}
+
+/// Iterator over a Nix attribute set.
+// This wrapper type exists to make the inner "raw" iterator
+// inaccessible.
+#[repr(transparent)]
+pub struct Iter<T>(T);
+
+impl<'a> Iterator for Iter<KeyValue<'a>> {
+    type Item = (&'a NixString, &'a Value);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match &mut self.0 {
+            KeyValue::Im(inner) => inner.next(),
+            KeyValue::Empty => None,
+
+            KeyValue::KV { name, value, at } => match at {
+                IterKV::Name => {
+                    at.next();
+                    Some((&NAME_REF, name))
+                }
+
+                IterKV::Value => {
+                    at.next();
+                    Some((&VALUE_REF, value))
+                }
+
+                IterKV::Done => None,
+            },
+        }
+    }
+}
+
+impl<'a> ExactSizeIterator for Iter<KeyValue<'a>> {
+    fn len(&self) -> usize {
+        match &self.0 {
+            KeyValue::Empty => 0,
+            KeyValue::KV { .. } => 2,
+            KeyValue::Im(inner) => inner.len(),
+        }
+    }
+}
+
+enum KeysInner<'a> {
+    Empty,
+    KV(IterKV),
+    Im(imbl::ordmap::Keys<'a, NixString, Value>),
+}
+
+pub struct Keys<'a>(KeysInner<'a>);
+
+impl<'a> Iterator for Keys<'a> {
+    type Item = &'a NixString;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match &mut self.0 {
+            KeysInner::Empty => None,
+            KeysInner::KV(at @ IterKV::Name) => {
+                at.next();
+                Some(&NAME_REF)
+            }
+            KeysInner::KV(at @ IterKV::Value) => {
+                at.next();
+                Some(&VALUE_REF)
+            }
+            KeysInner::KV(IterKV::Done) => None,
+            KeysInner::Im(m) => m.next(),
+        }
+    }
+}
+
+impl<'a> IntoIterator for &'a NixAttrs {
+    type Item = (&'a NixString, &'a Value);
+
+    type IntoIter = Iter<KeyValue<'a>>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.iter()
+    }
+}
+
+impl<'a> ExactSizeIterator for Keys<'a> {
+    fn len(&self) -> usize {
+        match &self.0 {
+            KeysInner::Empty => 0,
+            KeysInner::KV(_) => 2,
+            KeysInner::Im(m) => m.len(),
+        }
+    }
+}
+
+/// Internal representation of an owning attrset iterator
+pub enum IntoIterRepr {
+    Empty,
+    Finite(std::vec::IntoIter<(NixString, Value)>),
+    Im(imbl::ordmap::ConsumingIter<(NixString, Value)>),
+}
+
+/// Wrapper type which hides the internal implementation details from
+/// users.
+#[repr(transparent)]
+pub struct OwnedAttrsIterator(IntoIterRepr);
+
+impl Iterator for OwnedAttrsIterator {
+    type Item = (NixString, Value);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match &mut self.0 {
+            IntoIterRepr::Empty => None,
+            IntoIterRepr::Finite(inner) => inner.next(),
+            IntoIterRepr::Im(inner) => inner.next(),
+        }
+    }
+}
+
+impl ExactSizeIterator for OwnedAttrsIterator {
+    fn len(&self) -> usize {
+        match &self.0 {
+            IntoIterRepr::Empty => 0,
+            IntoIterRepr::Finite(inner) => inner.len(),
+            IntoIterRepr::Im(inner) => inner.len(),
+        }
+    }
+}
+
+impl DoubleEndedIterator for OwnedAttrsIterator {
+    fn next_back(&mut self) -> Option<Self::Item> {
+        match &mut self.0 {
+            IntoIterRepr::Empty => None,
+            IntoIterRepr::Finite(inner) => inner.next_back(),
+            IntoIterRepr::Im(inner) => inner.next_back(),
+        }
+    }
+}
diff --git a/tvix/eval/src/value/attrs/tests.rs b/tvix/eval/src/value/attrs/tests.rs
new file mode 100644
index 0000000000..534b78a00d
--- /dev/null
+++ b/tvix/eval/src/value/attrs/tests.rs
@@ -0,0 +1,106 @@
+use bstr::B;
+
+use super::*;
+
+#[test]
+fn test_empty_attrs() {
+    let attrs = NixAttrs::construct(0, vec![])
+        .expect("empty attr construction should succeed")
+        .unwrap();
+
+    assert!(
+        matches!(attrs, NixAttrs(AttrsRep::Empty)),
+        "empty attribute set should use optimised representation"
+    );
+}
+
+#[test]
+fn test_simple_attrs() {
+    let attrs = NixAttrs::construct(1, vec![Value::from("key"), Value::from("value")])
+        .expect("simple attr construction should succeed")
+        .unwrap();
+
+    assert!(
+        matches!(attrs, NixAttrs(AttrsRep::Im(_))),
+        "simple attribute set should use map representation",
+    )
+}
+
+#[test]
+fn test_kv_attrs() {
+    let name_val = Value::from("name");
+    let value_val = Value::from("value");
+    let meaning_val = Value::from("meaning");
+    let forty_two_val = Value::Integer(42);
+
+    let kv_attrs = NixAttrs::construct(
+        2,
+        vec![
+            value_val,
+            forty_two_val.clone(),
+            name_val,
+            meaning_val.clone(),
+        ],
+    )
+    .expect("constructing K/V pair attrs should succeed")
+    .unwrap();
+
+    match kv_attrs {
+        NixAttrs(AttrsRep::KV { name, value })
+            if name.to_str().unwrap() == meaning_val.to_str().unwrap()
+                || value.to_str().unwrap() == forty_two_val.to_str().unwrap() => {}
+
+        _ => panic!(
+            "K/V attribute set should use optimised representation, but got {:?}",
+            kv_attrs
+        ),
+    }
+}
+
+#[test]
+fn test_empty_attrs_iter() {
+    let attrs = NixAttrs::construct(0, vec![]).unwrap().unwrap();
+    assert!(attrs.iter().next().is_none());
+}
+
+#[test]
+fn test_kv_attrs_iter() {
+    let name_val = Value::from("name");
+    let value_val = Value::from("value");
+    let meaning_val = Value::from("meaning");
+    let forty_two_val = Value::Integer(42);
+
+    let kv_attrs = NixAttrs::construct(
+        2,
+        vec![
+            value_val,
+            forty_two_val.clone(),
+            name_val,
+            meaning_val.clone(),
+        ],
+    )
+    .expect("constructing K/V pair attrs should succeed")
+    .unwrap();
+
+    let mut iter = kv_attrs.iter().collect::<Vec<_>>().into_iter();
+    let (k, v) = iter.next().unwrap();
+    assert!(k == *NAME_REF);
+    assert!(v.to_str().unwrap() == meaning_val.to_str().unwrap());
+    let (k, v) = iter.next().unwrap();
+    assert!(k == *VALUE_REF);
+    assert!(v.as_int().unwrap() == forty_two_val.as_int().unwrap());
+    assert!(iter.next().is_none());
+}
+
+#[test]
+fn test_map_attrs_iter() {
+    let attrs = NixAttrs::construct(1, vec![Value::from("key"), Value::from("value")])
+        .expect("simple attr construction should succeed")
+        .unwrap();
+
+    let mut iter = attrs.iter().collect::<Vec<_>>().into_iter();
+    let (k, v) = iter.next().unwrap();
+    assert!(k == &NixString::from("key"));
+    assert_eq!(v.to_str().unwrap(), B("value"));
+    assert!(iter.next().is_none());
+}
diff --git a/tvix/eval/src/value/builtin.rs b/tvix/eval/src/value/builtin.rs
new file mode 100644
index 0000000000..346f06cb77
--- /dev/null
+++ b/tvix/eval/src/value/builtin.rs
@@ -0,0 +1,137 @@
+//! This module implements the runtime representation of a Nix
+//! builtin.
+//!
+//! Builtins are directly backed by Rust code operating on Nix values.
+
+use crate::vm::generators::Generator;
+
+use super::Value;
+
+use std::{
+    fmt::{Debug, Display},
+    rc::Rc,
+};
+
+/// Trait for closure types of builtins.
+///
+/// Builtins are expected to yield a generator which can be run by the VM to
+/// produce the final value.
+///
+/// Implementors should use the builtins-macros to create these functions
+/// instead of handling the argument-passing logic manually.
+pub trait BuiltinGen: Fn(Vec<Value>) -> Generator {}
+impl<F: Fn(Vec<Value>) -> Generator> BuiltinGen for F {}
+
+#[derive(Clone)]
+pub struct BuiltinRepr {
+    name: &'static str,
+    /// Optional documentation for the builtin.
+    documentation: Option<&'static str>,
+    arg_count: usize,
+
+    func: Rc<dyn BuiltinGen>,
+
+    /// Partially applied function arguments.
+    partials: Vec<Value>,
+}
+
+pub enum BuiltinResult {
+    /// Builtin was not ready to be called (arguments missing) and remains
+    /// partially applied.
+    Partial(Builtin),
+
+    /// Builtin was called and constructed a generator that the VM must run.
+    Called(&'static str, Generator),
+}
+
+/// Represents a single built-in function which directly executes Rust
+/// code that operates on a Nix value.
+///
+/// Builtins are the only functions in Nix that have varying arities
+/// (for example, `hasAttr` has an arity of 2, but `isAttrs` an arity
+/// of 1). To facilitate this generically, builtins expect to be
+/// called with a vector of Nix values corresponding to their
+/// arguments in order.
+///
+/// Partially applied builtins act similar to closures in that they
+/// "capture" the partially applied arguments, and are treated
+/// specially when printing their representation etc.
+#[derive(Clone)]
+pub struct Builtin(Box<BuiltinRepr>);
+
+impl From<BuiltinRepr> for Builtin {
+    fn from(value: BuiltinRepr) -> Self {
+        Builtin(Box::new(value))
+    }
+}
+
+impl Builtin {
+    pub fn new<F: BuiltinGen + 'static>(
+        name: &'static str,
+        documentation: Option<&'static str>,
+        arg_count: usize,
+        func: F,
+    ) -> Self {
+        BuiltinRepr {
+            name,
+            documentation,
+            arg_count,
+            func: Rc::new(func),
+            partials: vec![],
+        }
+        .into()
+    }
+
+    pub fn name(&self) -> &'static str {
+        self.0.name
+    }
+
+    pub fn documentation(&self) -> Option<&'static str> {
+        self.0.documentation
+    }
+
+    /// Apply an additional argument to the builtin.
+    /// After this, [`Builtin::call`] *must* be called, otherwise it may leave
+    /// the builtin in an incorrect state.
+    pub fn apply_arg(&mut self, arg: Value) {
+        self.0.partials.push(arg);
+
+        debug_assert!(
+            self.0.partials.len() <= self.0.arg_count,
+            "Tvix bug: pushed too many arguments to builtin"
+        );
+    }
+
+    /// Attempt to call a builtin, which will produce a generator if it is fully
+    /// applied or return the builtin if it is partially applied.
+    pub fn call(self) -> BuiltinResult {
+        if self.0.partials.len() == self.0.arg_count {
+            BuiltinResult::Called(self.0.name, (self.0.func)(self.0.partials))
+        } else {
+            BuiltinResult::Partial(self)
+        }
+    }
+}
+
+impl Debug for Builtin {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "builtin[{}]", self.0.name)
+    }
+}
+
+impl Display for Builtin {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        if !self.0.partials.is_empty() {
+            f.write_str("<PRIMOP-APP>")
+        } else {
+            f.write_str("<PRIMOP>")
+        }
+    }
+}
+
+/// Builtins are uniquely identified by their name
+impl PartialEq for Builtin {
+    fn eq(&self, other: &Self) -> bool {
+        self.0.name == other.0.name
+    }
+}
diff --git a/tvix/eval/src/value/function.rs b/tvix/eval/src/value/function.rs
new file mode 100644
index 0000000000..7592e3d641
--- /dev/null
+++ b/tvix/eval/src/value/function.rs
@@ -0,0 +1,112 @@
+//! This module implements the runtime representation of functions.
+use std::{collections::BTreeMap, hash::Hash, rc::Rc};
+
+use codemap::Span;
+use smol_str::SmolStr;
+
+use crate::{chunk::Chunk, upvalues::Upvalues};
+
+use super::NixString;
+
+#[derive(Clone, Debug, PartialEq)]
+pub(crate) struct Formals {
+    /// Map from argument name, to whether that argument is required
+    pub(crate) arguments: BTreeMap<NixString, bool>,
+
+    /// Do the formals of this function accept extra arguments
+    pub(crate) ellipsis: bool,
+
+    /// The span of the formals themselves, to use to emit errors
+    pub(crate) span: Span,
+
+    /// Optionally tracks a name for all function arguments (args@ style).
+    /// Used by toXML.
+    pub(crate) name: Option<String>,
+}
+
+impl Formals {
+    /// Returns true if the given arg is a valid argument to these formals.
+    ///
+    /// This is true if it is either listed in the list of arguments, or the formals have an
+    /// ellipsis
+    pub(crate) fn contains<Q>(&self, arg: &Q) -> bool
+    where
+        Q: ?Sized + Hash + Ord + Eq,
+        NixString: std::borrow::Borrow<Q>,
+    {
+        self.ellipsis || self.arguments.contains_key(arg)
+    }
+}
+
+/// The opcodes for a thunk or closure, plus the number of
+/// non-executable opcodes which are allowed after an OpThunkClosure or
+/// OpThunkSuspended referencing it.  At runtime `Lambda` is usually wrapped
+/// in `Rc` to avoid copying the `Chunk` it holds (which can be
+/// quite large).
+///
+/// In order to correctly reproduce cppnix's "pointer equality"
+/// semantics it is important that we never clone a Lambda --
+/// use `Rc<Lambda>::clone()` instead.  This struct deliberately
+/// does not `derive(Clone)` in order to prevent this from being
+/// done accidentally.
+///
+#[derive(/* do not add Clone here */ Debug, Default)]
+pub struct Lambda {
+    pub(crate) chunk: Chunk,
+
+    /// Name of the function (equivalent to the name of the
+    /// identifier (e.g. a value in a let-expression or an attribute
+    /// set entry) it is located in).
+    pub(crate) name: Option<SmolStr>,
+
+    /// Number of upvalues which the code in this Lambda closes
+    /// over, and which need to be initialised at
+    /// runtime.  Information about the variables is emitted using
+    /// data-carrying opcodes (see [`crate::opcode::OpCode::DataStackIdx`]).
+    pub(crate) upvalue_count: usize,
+    pub(crate) formals: Option<Formals>,
+}
+
+impl Lambda {
+    pub fn chunk(&mut self) -> &mut Chunk {
+        &mut self.chunk
+    }
+}
+
+///
+/// In order to correctly reproduce cppnix's "pointer equality"
+/// semantics it is important that we never clone a Lambda --
+/// use `Rc<Lambda>::clone()` instead.  This struct deliberately
+/// does not `derive(Clone)` in order to prevent this from being
+/// done accidentally.
+///
+#[derive(/* do not add Clone here */ Debug)]
+pub struct Closure {
+    pub lambda: Rc<Lambda>,
+    pub upvalues: Rc<Upvalues>,
+}
+
+impl Closure {
+    pub fn new(lambda: Rc<Lambda>) -> Self {
+        Self::new_with_upvalues(
+            Rc::new(Upvalues::with_capacity(lambda.upvalue_count)),
+            lambda,
+        )
+    }
+
+    pub fn new_with_upvalues(upvalues: Rc<Upvalues>, lambda: Rc<Lambda>) -> Self {
+        Closure { upvalues, lambda }
+    }
+
+    pub fn chunk(&self) -> &Chunk {
+        &self.lambda.chunk
+    }
+
+    pub fn lambda(&self) -> Rc<Lambda> {
+        self.lambda.clone()
+    }
+
+    pub fn upvalues(&self) -> Rc<Upvalues> {
+        self.upvalues.clone()
+    }
+}
diff --git a/tvix/eval/src/value/json.rs b/tvix/eval/src/value/json.rs
new file mode 100644
index 0000000000..c48e9c1f4e
--- /dev/null
+++ b/tvix/eval/src/value/json.rs
@@ -0,0 +1,154 @@
+/// Implementation of Value serialisation *to* JSON.
+///
+/// This can not be implemented through standard serde-derive methods,
+/// as there is internal Nix logic that must happen within the
+/// serialisation methods.
+use super::{CoercionKind, Value};
+use crate::errors::{CatchableErrorKind, ErrorKind};
+use crate::generators::{self, GenCo};
+use crate::NixContext;
+
+use bstr::ByteSlice;
+use serde_json::value::to_value;
+use serde_json::Value as Json; // name clash with *our* `Value`
+use serde_json::{Map, Number};
+
+impl Value {
+    /// Transforms the structure into a JSON
+    /// and accumulate all encountered context in the second's element
+    /// of the return type.
+    pub async fn into_contextful_json(
+        self,
+        co: &GenCo,
+    ) -> Result<Result<(Json, NixContext), CatchableErrorKind>, ErrorKind> {
+        let self_forced = generators::request_force(co, self).await;
+        let mut context = NixContext::new();
+
+        let value = match self_forced {
+            Value::Null => Json::Null,
+            Value::Bool(b) => Json::Bool(b),
+            Value::Integer(i) => Json::Number(Number::from(i)),
+            Value::Float(f) => to_value(f)?,
+            Value::String(s) => {
+                context.mimic(&s);
+
+                Json::String(s.to_str()?.to_owned())
+            }
+
+            Value::Path(p) => {
+                let imported = generators::request_path_import(co, *p).await;
+                let path = imported.to_string_lossy().to_string();
+                context = context.append(crate::NixContextElement::Plain(path.clone()));
+                Json::String(path)
+            }
+
+            Value::List(l) => {
+                let mut out = vec![];
+
+                for val in l.into_iter() {
+                    match generators::request_to_json(co, val).await {
+                        Ok((v, mut ctx)) => {
+                            context = context.join(&mut ctx);
+                            out.push(v)
+                        }
+                        Err(cek) => return Ok(Err(cek)),
+                    }
+                }
+
+                Json::Array(out)
+            }
+
+            Value::Attrs(attrs) => {
+                // Attribute sets with a callable `__toString` attribute
+                // serialise to the string-coerced version of the result of
+                // calling that.
+                if attrs.select("__toString").is_some() {
+                    let span = generators::request_span(co).await;
+                    match Value::Attrs(attrs)
+                        .coerce_to_string_(
+                            co,
+                            CoercionKind {
+                                strong: false,
+                                import_paths: false,
+                            },
+                            span,
+                        )
+                        .await?
+                    {
+                        Value::Catchable(cek) => return Ok(Err(*cek)),
+                        Value::String(s) => {
+                            // We need a fresh context here because `__toString` will discard
+                            // everything.
+                            let mut fresh = NixContext::new();
+                            fresh.mimic(&s);
+
+                            return Ok(Ok((Json::String(s.to_str()?.to_owned()), fresh)));
+                        }
+                        _ => panic!("Value::coerce_to_string_() returned a non-string!"),
+                    }
+                }
+
+                // Attribute sets with an `outPath` attribute
+                // serialise to a JSON serialisation of that inner
+                // value (regardless of what it is!).
+                if let Some(out_path) = attrs.select("outPath") {
+                    return Ok(generators::request_to_json(co, out_path.clone()).await);
+                }
+
+                let mut out = Map::with_capacity(attrs.len());
+                for (name, value) in attrs.into_iter_sorted() {
+                    out.insert(
+                        name.to_str()?.to_owned(),
+                        match generators::request_to_json(co, value).await {
+                            Ok((v, mut ctx)) => {
+                                context = context.join(&mut ctx);
+                                v
+                            }
+                            Err(cek) => return Ok(Err(cek)),
+                        },
+                    );
+                }
+
+                Json::Object(out)
+            }
+
+            Value::Catchable(c) => return Ok(Err(*c)),
+
+            val @ Value::Closure(_)
+            | val @ Value::Thunk(_)
+            | val @ Value::Builtin(_)
+            | val @ Value::AttrNotFound
+            | val @ Value::Blueprint(_)
+            | val @ Value::DeferredUpvalue(_)
+            | val @ Value::UnresolvedPath(_)
+            | val @ Value::Json(..)
+            | val @ Value::FinaliseRequest(_) => {
+                return Err(ErrorKind::NotSerialisableToJson(val.type_of()))
+            }
+        };
+
+        Ok(Ok((value, context)))
+    }
+
+    /// Generator version of the above, which wraps responses in
+    /// [`Value::Json`].
+    pub(crate) async fn into_contextful_json_generator(
+        self,
+        co: GenCo,
+    ) -> Result<Value, ErrorKind> {
+        match self.into_contextful_json(&co).await? {
+            Err(cek) => Ok(Value::from(cek)),
+            Ok((json, ctx)) => Ok(Value::Json(Box::new((json, ctx)))),
+        }
+    }
+
+    /// Transforms the structure into a JSON
+    /// All the accumulated context is ignored, use [`into_contextful_json`]
+    /// to obtain the resulting context of the JSON object.
+    pub async fn into_json(
+        self,
+        co: &GenCo,
+    ) -> Result<Result<Json, CatchableErrorKind>, ErrorKind> {
+        Ok(self.into_contextful_json(co).await?.map(|(json, _)| json))
+    }
+}
diff --git a/tvix/eval/src/value/list.rs b/tvix/eval/src/value/list.rs
new file mode 100644
index 0000000000..2b8b3de28d
--- /dev/null
+++ b/tvix/eval/src/value/list.rs
@@ -0,0 +1,102 @@
+//! This module implements Nix lists.
+use std::ops::Index;
+use std::rc::Rc;
+
+use imbl::{vector, Vector};
+
+use serde::Deserialize;
+
+use super::thunk::ThunkSet;
+use super::TotalDisplay;
+use super::Value;
+
+#[repr(transparent)]
+#[derive(Clone, Debug, Deserialize)]
+pub struct NixList(Rc<Vector<Value>>);
+
+impl TotalDisplay for NixList {
+    fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
+        f.write_str("[ ")?;
+
+        for v in self {
+            v.total_fmt(f, set)?;
+            f.write_str(" ")?;
+        }
+
+        f.write_str("]")
+    }
+}
+
+impl From<Vector<Value>> for NixList {
+    fn from(vs: Vector<Value>) -> Self {
+        Self(Rc::new(vs))
+    }
+}
+
+impl NixList {
+    pub fn len(&self) -> usize {
+        self.0.len()
+    }
+
+    pub fn get(&self, i: usize) -> Option<&Value> {
+        self.0.get(i)
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+
+    pub fn construct(count: usize, stack_slice: Vec<Value>) -> Self {
+        debug_assert!(
+            count == stack_slice.len(),
+            "NixList::construct called with count == {}, but slice.len() == {}",
+            count,
+            stack_slice.len(),
+        );
+
+        NixList(Rc::new(Vector::from_iter(stack_slice)))
+    }
+
+    pub fn iter(&self) -> vector::Iter<Value> {
+        self.0.iter()
+    }
+
+    pub fn ptr_eq(&self, other: &Self) -> bool {
+        Rc::ptr_eq(&self.0, &other.0)
+    }
+
+    pub fn into_inner(self) -> Vector<Value> {
+        Rc::try_unwrap(self.0).unwrap_or_else(|rc| (*rc).clone())
+    }
+
+    #[deprecated(note = "callers should avoid constructing from Vec")]
+    pub fn from_vec(vs: Vec<Value>) -> Self {
+        Self(Rc::new(Vector::from_iter(vs)))
+    }
+}
+
+impl IntoIterator for NixList {
+    type Item = Value;
+    type IntoIter = imbl::vector::ConsumingIter<Value>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.into_inner().into_iter()
+    }
+}
+
+impl<'a> IntoIterator for &'a NixList {
+    type Item = &'a Value;
+    type IntoIter = imbl::vector::Iter<'a, Value>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.0.iter()
+    }
+}
+
+impl Index<usize> for NixList {
+    type Output = Value;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self.0[index]
+    }
+}
diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs
new file mode 100644
index 0000000000..c171c9a04e
--- /dev/null
+++ b/tvix/eval/src/value/mod.rs
@@ -0,0 +1,1073 @@
+//! This module implements the backing representation of runtime
+//! values in the Nix language.
+use std::cmp::Ordering;
+use std::fmt::Display;
+use std::num::{NonZeroI32, NonZeroUsize};
+use std::path::PathBuf;
+use std::rc::Rc;
+
+use bstr::{BString, ByteVec};
+use lexical_core::format::CXX_LITERAL;
+use serde::Deserialize;
+
+#[cfg(feature = "arbitrary")]
+mod arbitrary;
+mod attrs;
+mod builtin;
+mod function;
+mod json;
+mod list;
+mod path;
+mod string;
+mod thunk;
+
+use crate::errors::{CatchableErrorKind, ErrorKind};
+use crate::opcode::StackIdx;
+use crate::spans::LightSpan;
+use crate::vm::generators::{self, GenCo};
+use crate::AddContext;
+pub use attrs::NixAttrs;
+pub use builtin::{Builtin, BuiltinResult};
+pub(crate) use function::Formals;
+pub use function::{Closure, Lambda};
+pub use list::NixList;
+pub use path::canon_path;
+pub use string::{NixContext, NixContextElement, NixString};
+pub use thunk::Thunk;
+
+pub use self::thunk::ThunkSet;
+
+use lazy_static::lazy_static;
+
+#[warn(variant_size_differences)]
+#[derive(Clone, Debug, Deserialize)]
+#[serde(untagged)]
+pub enum Value {
+    Null,
+    Bool(bool),
+    Integer(i64),
+    Float(f64),
+    String(NixString),
+
+    #[serde(skip)]
+    Path(Box<PathBuf>),
+    Attrs(Box<NixAttrs>),
+    List(NixList),
+
+    #[serde(skip)]
+    Closure(Rc<Closure>), // must use Rc<Closure> here in order to get proper pointer equality
+
+    #[serde(skip)]
+    Builtin(Builtin),
+
+    // Internal values that, while they technically exist at runtime,
+    // are never returned to or created directly by users.
+    #[serde(skip_deserializing)]
+    Thunk(Thunk),
+
+    // See [`compiler::compile_select_or()`] for explanation
+    #[serde(skip)]
+    AttrNotFound,
+
+    // this can only occur in Chunk::Constants and nowhere else
+    #[serde(skip)]
+    Blueprint(Rc<Lambda>),
+
+    #[serde(skip)]
+    DeferredUpvalue(StackIdx),
+    #[serde(skip)]
+    UnresolvedPath(Box<PathBuf>),
+    #[serde(skip)]
+    Json(Box<(serde_json::Value, NixContext)>),
+
+    #[serde(skip)]
+    FinaliseRequest(bool),
+
+    #[serde(skip)]
+    // TODO(tazjin): why is this in a Box?
+    Catchable(Box<CatchableErrorKind>),
+}
+
+impl From<CatchableErrorKind> for Value {
+    #[inline]
+    fn from(c: CatchableErrorKind) -> Value {
+        Value::Catchable(Box::new(c))
+    }
+}
+
+impl<V> From<Result<V, CatchableErrorKind>> for Value
+where
+    Value: From<V>,
+{
+    #[inline]
+    fn from(v: Result<V, CatchableErrorKind>) -> Value {
+        match v {
+            Ok(v) => v.into(),
+            Err(e) => Value::Catchable(Box::new(e)),
+        }
+    }
+}
+
+lazy_static! {
+    static ref WRITE_FLOAT_OPTIONS: lexical_core::WriteFloatOptions =
+        lexical_core::WriteFloatOptionsBuilder::new()
+            .trim_floats(true)
+            .round_mode(lexical_core::write_float_options::RoundMode::Round)
+            .positive_exponent_break(Some(NonZeroI32::new(5).unwrap()))
+            .max_significant_digits(Some(NonZeroUsize::new(6).unwrap()))
+            .build()
+            .unwrap();
+}
+
+// Helper macros to generate the to_*/as_* macros while accounting for
+// thunks.
+
+/// Generate an `as_*` method returning a reference to the expected
+/// type, or a type error. This only works for types that implement
+/// `Copy`, as returning a reference to an inner thunk value is not
+/// possible.
+
+/// Generate an `as_*/to_*` accessor method that returns either the
+/// expected type, or a type error.
+macro_rules! gen_cast {
+    ( $name:ident, $type:ty, $expected:expr, $variant:pat, $result:expr ) => {
+        pub fn $name(&self) -> Result<$type, ErrorKind> {
+            match self {
+                $variant => Ok($result),
+                Value::Thunk(thunk) => Self::$name(&thunk.value()),
+                other => Err(type_error($expected, &other)),
+            }
+        }
+    };
+}
+
+/// Generate an `as_*_mut/to_*_mut` accessor method that returns either the
+/// expected type, or a type error.
+macro_rules! gen_cast_mut {
+    ( $name:ident, $type:ty, $expected:expr, $variant:ident) => {
+        pub fn $name(&mut self) -> Result<&mut $type, ErrorKind> {
+            match self {
+                Value::$variant(x) => Ok(x),
+                other => Err(type_error($expected, &other)),
+            }
+        }
+    };
+}
+
+/// Generate an `is_*` type-checking method.
+macro_rules! gen_is {
+    ( $name:ident, $variant:pat ) => {
+        pub fn $name(&self) -> bool {
+            match self {
+                $variant => true,
+                Value::Thunk(thunk) => Self::$name(&thunk.value()),
+                _ => false,
+            }
+        }
+    };
+}
+
+/// Describes what input types are allowed when coercing a `Value` to a string
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct CoercionKind {
+    /// If false only coerce already "stringly" types like strings and paths, but
+    /// also coerce sets that have a `__toString` attribute. In Tvix, this is
+    /// usually called a weak coercion. Equivalent to passing `false` as the
+    /// `coerceMore` argument of `EvalState::coerceToString` in C++ Nix.
+    ///
+    /// If true coerce all value types included by a weak coercion, but also
+    /// coerce `null`, booleans, integers, floats and lists of coercible types.
+    /// Consequently, we call this a strong coercion. Equivalent to passing
+    /// `true` as `coerceMore` in C++ Nix.
+    pub strong: bool,
+
+    /// If `import_paths` is `true`, paths are imported into the store and their
+    /// store path is the result of the coercion (equivalent to the
+    /// `copyToStore` argument of `EvalState::coerceToString` in C++ Nix).
+    pub import_paths: bool,
+}
+
+impl<T> From<T> for Value
+where
+    T: Into<NixString>,
+{
+    fn from(t: T) -> Self {
+        Self::String(t.into())
+    }
+}
+
+/// Constructors
+impl Value {
+    /// Construct a [`Value::Attrs`] from a [`NixAttrs`].
+    pub fn attrs(attrs: NixAttrs) -> Self {
+        Self::Attrs(Box::new(attrs))
+    }
+}
+
+/// Controls what kind of by-pointer equality comparison is allowed.
+///
+/// See `//tvix/docs/value-pointer-equality.md` for details.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum PointerEquality {
+    /// Pointer equality not allowed at all.
+    ForbidAll,
+
+    /// Pointer equality comparisons only allowed for nested values.
+    AllowNested,
+
+    /// Pointer equality comparisons are allowed in all contexts.
+    AllowAll,
+}
+
+impl Value {
+    /// Deeply forces a value, traversing e.g. lists and attribute sets and forcing
+    /// their contents, too.
+    ///
+    /// This is a generator function.
+    pub(super) async fn deep_force(self, co: GenCo, span: LightSpan) -> Result<Value, ErrorKind> {
+        if let Some(v) = Self::deep_force_(self.clone(), co, span).await? {
+            Ok(v)
+        } else {
+            Ok(self)
+        }
+    }
+
+    /// Returns Some(v) or None to indicate the returned value is myself
+    async fn deep_force_(
+        myself: Value,
+        co: GenCo,
+        span: LightSpan,
+    ) -> Result<Option<Value>, ErrorKind> {
+        // This is a stack of values which still remain to be forced.
+        let mut vals = vec![myself];
+
+        let mut thunk_set: ThunkSet = Default::default();
+
+        loop {
+            let v = if let Some(v) = vals.pop() {
+                v
+            } else {
+                return Ok(None);
+            };
+
+            // Get rid of any top-level thunks, and bail out of self-recursive
+            // thunks.
+            let value = if let Value::Thunk(t) = &v {
+                if !thunk_set.insert(t) {
+                    continue;
+                }
+                Thunk::force_(t.clone(), &co, span.clone()).await?
+            } else {
+                v
+            };
+
+            match value {
+                // Short-circuit on already evaluated values, or fail on internal values.
+                Value::Null
+                | Value::Bool(_)
+                | Value::Integer(_)
+                | Value::Float(_)
+                | Value::String(_)
+                | Value::Path(_)
+                | Value::Closure(_)
+                | Value::Builtin(_) => continue,
+
+                Value::List(list) => {
+                    for val in list.into_iter().rev() {
+                        vals.push(val);
+                    }
+                    continue;
+                }
+
+                Value::Attrs(attrs) => {
+                    for (_, val) in attrs.into_iter().rev() {
+                        vals.push(val);
+                    }
+                    continue;
+                }
+
+                Value::Thunk(_) => panic!("Tvix bug: force_value() returned a thunk"),
+
+                Value::Catchable(_) => return Ok(Some(value)),
+
+                Value::AttrNotFound
+                | Value::Blueprint(_)
+                | Value::DeferredUpvalue(_)
+                | Value::UnresolvedPath(_)
+                | Value::Json(..)
+                | Value::FinaliseRequest(_) => panic!(
+                    "Tvix bug: internal value left on stack: {}",
+                    value.type_of()
+                ),
+            }
+        }
+    }
+
+    pub async fn coerce_to_string(
+        self,
+        co: GenCo,
+        kind: CoercionKind,
+        span: LightSpan,
+    ) -> Result<Value, ErrorKind> {
+        self.coerce_to_string_(&co, kind, span).await
+    }
+
+    /// Coerce a `Value` to a string. See `CoercionKind` for a rundown of what
+    /// input types are accepted under what circumstances.
+    pub async fn coerce_to_string_(
+        self,
+        co: &GenCo,
+        kind: CoercionKind,
+        span: LightSpan,
+    ) -> Result<Value, ErrorKind> {
+        let mut result = BString::default();
+        let mut vals = vec![self];
+        // Track if we are coercing the first value of a list to correctly emit
+        // separating white spaces.
+        let mut is_list_head = None;
+        // FIXME(raitobezarius): as per https://b.tvl.fyi/issues/364
+        // we might be interested into more powerful context-related coercion kinds.
+        let mut context: NixContext = NixContext::new();
+
+        loop {
+            let value = if let Some(v) = vals.pop() {
+                v.force(co, span.clone()).await?
+            } else {
+                return Ok(Value::String(NixString::new_context_from(context, result)));
+            };
+            let coerced: Result<BString, _> = match (value, kind) {
+                // coercions that are always done
+                (Value::String(mut s), _) => {
+                    if let Some(ctx) = s.context_mut() {
+                        context = context.join(ctx);
+                    }
+                    Ok((*s).into())
+                }
+
+                // TODO(sterni): Think about proper encoding handling here. This needs
+                // general consideration anyways, since one current discrepancy between
+                // C++ Nix and Tvix is that the former's strings are arbitrary byte
+                // sequences without NUL bytes, whereas Tvix only allows valid
+                // Unicode. See also b/189.
+                (
+                    Value::Path(p),
+                    CoercionKind {
+                        import_paths: true, ..
+                    },
+                ) => {
+                    let imported = generators::request_path_import(co, *p).await;
+                    // When we import a path from the evaluator, we must attach
+                    // its original path as its context.
+                    context = context.append(NixContextElement::Plain(
+                        imported.to_string_lossy().to_string(),
+                    ));
+                    Ok(imported.into_os_string().into_encoded_bytes().into())
+                }
+                (
+                    Value::Path(p),
+                    CoercionKind {
+                        import_paths: false,
+                        ..
+                    },
+                ) => Ok(p.into_os_string().into_encoded_bytes().into()),
+
+                // Attribute sets can be converted to strings if they either have an
+                // `__toString` attribute which holds a function that receives the
+                // set itself or an `outPath` attribute which should be a string.
+                // `__toString` is preferred.
+                (Value::Attrs(attrs), kind) => {
+                    if let Some(to_string) = attrs.select("__toString") {
+                        let callable = to_string.clone().force(co, span.clone()).await?;
+
+                        // Leave the attribute set on the stack as an argument
+                        // to the function call.
+                        generators::request_stack_push(co, Value::Attrs(attrs.clone())).await;
+
+                        // Call the callable ...
+                        let result = generators::request_call(co, callable).await;
+
+                        // Recurse on the result, as attribute set coercion
+                        // actually works recursively, e.g. you can even return
+                        // /another/ set with a __toString attr.
+                        vals.push(result);
+                        continue;
+                    } else if let Some(out_path) = attrs.select("outPath") {
+                        vals.push(out_path.clone());
+                        continue;
+                    } else {
+                        return Err(ErrorKind::NotCoercibleToString { from: "set", kind });
+                    }
+                }
+
+                // strong coercions
+                (Value::Null, CoercionKind { strong: true, .. })
+                | (Value::Bool(false), CoercionKind { strong: true, .. }) => Ok("".into()),
+                (Value::Bool(true), CoercionKind { strong: true, .. }) => Ok("1".into()),
+
+                (Value::Integer(i), CoercionKind { strong: true, .. }) => Ok(format!("{i}").into()),
+                (Value::Float(f), CoercionKind { strong: true, .. }) => {
+                    // contrary to normal Display, coercing a float to a string will
+                    // result in unconditional 6 decimal places
+                    Ok(format!("{:.6}", f).into())
+                }
+
+                // Lists are coerced by coercing their elements and interspersing spaces
+                (Value::List(list), CoercionKind { strong: true, .. }) => {
+                    for elem in list.into_iter().rev() {
+                        vals.push(elem);
+                    }
+                    // In case we are coercing a list within a list we don't want
+                    // to touch this. Since the algorithm is nonrecursive, the
+                    // space would not have been created yet (due to continue).
+                    if is_list_head.is_none() {
+                        is_list_head = Some(true);
+                    }
+                    continue;
+                }
+
+                (Value::Thunk(_), _) => panic!("Tvix bug: force returned unforced thunk"),
+
+                val @ (Value::Closure(_), _)
+                | val @ (Value::Builtin(_), _)
+                | val @ (Value::Null, _)
+                | val @ (Value::Bool(_), _)
+                | val @ (Value::Integer(_), _)
+                | val @ (Value::Float(_), _)
+                | val @ (Value::List(_), _) => Err(ErrorKind::NotCoercibleToString {
+                    from: val.0.type_of(),
+                    kind,
+                }),
+
+                (c @ Value::Catchable(_), _) => return Ok(c),
+
+                (Value::AttrNotFound, _)
+                | (Value::Blueprint(_), _)
+                | (Value::DeferredUpvalue(_), _)
+                | (Value::UnresolvedPath(_), _)
+                | (Value::Json(..), _)
+                | (Value::FinaliseRequest(_), _) => {
+                    panic!("tvix bug: .coerce_to_string() called on internal value")
+                }
+            };
+
+            if let Some(head) = is_list_head {
+                if !head {
+                    result.push(b' ');
+                } else {
+                    is_list_head = Some(false);
+                }
+            }
+
+            result.push_str(&coerced?);
+        }
+    }
+
+    pub(crate) async fn nix_eq_owned_genco(
+        self,
+        other: Value,
+        co: GenCo,
+        ptr_eq: PointerEquality,
+        span: LightSpan,
+    ) -> Result<Value, ErrorKind> {
+        self.nix_eq(other, &co, ptr_eq, span).await
+    }
+
+    /// Compare two Nix values for equality, forcing nested parts of the structure
+    /// as needed.
+    ///
+    /// This comparison needs to be invoked for nested values (e.g. in lists and
+    /// attribute sets) as well, which is done by suspending and asking the VM to
+    /// perform the nested comparison.
+    ///
+    /// The `top_level` parameter controls whether this invocation is the top-level
+    /// comparison, or a nested value comparison. See
+    /// `//tvix/docs/value-pointer-equality.md`
+    pub(crate) async fn nix_eq(
+        self,
+        other: Value,
+        co: &GenCo,
+        ptr_eq: PointerEquality,
+        span: LightSpan,
+    ) -> Result<Value, ErrorKind> {
+        // this is a stack of ((v1,v2),peq) triples to be compared;
+        // after each triple is popped off of the stack, v1 is
+        // compared to v2 using peq-mode PointerEquality
+        let mut vals = vec![((self, other), ptr_eq)];
+
+        loop {
+            let ((a, b), ptr_eq) = if let Some(abp) = vals.pop() {
+                abp
+            } else {
+                // stack is empty, so comparison has succeeded
+                return Ok(Value::Bool(true));
+            };
+            let a = match a {
+                Value::Thunk(thunk) => {
+                    // If both values are thunks, and thunk comparisons are allowed by
+                    // pointer, do that and move on.
+                    if ptr_eq == PointerEquality::AllowAll {
+                        if let Value::Thunk(t1) = &b {
+                            if t1.ptr_eq(&thunk) {
+                                continue;
+                            }
+                        }
+                    };
+
+                    Thunk::force_(thunk, co, span.clone()).await?
+                }
+
+                _ => a,
+            };
+
+            let b = b.force(co, span.clone()).await?;
+
+            debug_assert!(!matches!(a, Value::Thunk(_)));
+            debug_assert!(!matches!(b, Value::Thunk(_)));
+
+            let result = match (a, b) {
+                // Trivial comparisons
+                (c @ Value::Catchable(_), _) => return Ok(c),
+                (_, c @ Value::Catchable(_)) => return Ok(c),
+                (Value::Null, Value::Null) => true,
+                (Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
+                (Value::String(s1), Value::String(s2)) => s1 == s2,
+                (Value::Path(p1), Value::Path(p2)) => p1 == p2,
+
+                // Numerical comparisons (they work between float & int)
+                (Value::Integer(i1), Value::Integer(i2)) => i1 == i2,
+                (Value::Integer(i), Value::Float(f)) => i as f64 == f,
+                (Value::Float(f1), Value::Float(f2)) => f1 == f2,
+                (Value::Float(f), Value::Integer(i)) => i as f64 == f,
+
+                // List comparisons
+                (Value::List(l1), Value::List(l2)) => {
+                    if ptr_eq >= PointerEquality::AllowNested && l1.ptr_eq(&l2) {
+                        continue;
+                    }
+
+                    if l1.len() != l2.len() {
+                        return Ok(Value::Bool(false));
+                    }
+
+                    vals.extend(l1.into_iter().rev().zip(l2.into_iter().rev()).zip(
+                        std::iter::repeat(std::cmp::max(ptr_eq, PointerEquality::AllowNested)),
+                    ));
+                    continue;
+                }
+
+                (_, Value::List(_)) | (Value::List(_), _) => return Ok(Value::Bool(false)),
+
+                // Attribute set comparisons
+                (Value::Attrs(a1), Value::Attrs(a2)) => {
+                    if ptr_eq >= PointerEquality::AllowNested && a1.ptr_eq(&a2) {
+                        continue;
+                    }
+
+                    // Special-case for derivation comparisons: If both attribute sets
+                    // have `type = derivation`, compare them by `outPath`.
+                    #[allow(clippy::single_match)] // might need more match arms later
+                    match (a1.select("type"), a2.select("type")) {
+                        (Some(v1), Some(v2)) => {
+                            let s1 = v1.clone().force(co, span.clone()).await?;
+                            if s1.is_catchable() {
+                                return Ok(s1);
+                            }
+                            let s2 = v2.clone().force(co, span.clone()).await?;
+                            if s2.is_catchable() {
+                                return Ok(s2);
+                            }
+                            let s1 = s1.to_str();
+                            let s2 = s2.to_str();
+
+                            if let (Ok(s1), Ok(s2)) = (s1, s2) {
+                                if s1 == "derivation" && s2 == "derivation" {
+                                    // TODO(tazjin): are the outPaths really required,
+                                    // or should it fall through?
+                                    let out1 = a1
+                                        .select_required("outPath")
+                                        .context("comparing derivations")?
+                                        .clone();
+
+                                    let out2 = a2
+                                        .select_required("outPath")
+                                        .context("comparing derivations")?
+                                        .clone();
+
+                                    let out1 = out1.clone().force(co, span.clone()).await?;
+                                    let out2 = out2.clone().force(co, span.clone()).await?;
+
+                                    if out1.is_catchable() {
+                                        return Ok(out1);
+                                    }
+
+                                    if out2.is_catchable() {
+                                        return Ok(out2);
+                                    }
+
+                                    let result =
+                                        out1.to_contextful_str()? == out2.to_contextful_str()?;
+                                    if !result {
+                                        return Ok(Value::Bool(false));
+                                    } else {
+                                        continue;
+                                    }
+                                }
+                            }
+                        }
+                        _ => {}
+                    };
+
+                    if a1.len() != a2.len() {
+                        return Ok(Value::Bool(false));
+                    }
+
+                    // note that it is important to be careful here with the
+                    // order we push the keys and values in order to properly
+                    // compare attrsets containing `throw` elements.
+                    let iter1 = a1.into_iter_sorted().rev();
+                    let iter2 = a2.into_iter_sorted().rev();
+                    for ((k1, v1), (k2, v2)) in iter1.zip(iter2) {
+                        vals.push((
+                            (v1, v2),
+                            std::cmp::max(ptr_eq, PointerEquality::AllowNested),
+                        ));
+                        vals.push((
+                            (k1.into(), k2.into()),
+                            std::cmp::max(ptr_eq, PointerEquality::AllowNested),
+                        ));
+                    }
+                    continue;
+                }
+
+                (Value::Attrs(_), _) | (_, Value::Attrs(_)) => return Ok(Value::Bool(false)),
+
+                (Value::Closure(c1), Value::Closure(c2))
+                    if ptr_eq >= PointerEquality::AllowNested =>
+                {
+                    if Rc::ptr_eq(&c1, &c2) {
+                        continue;
+                    } else {
+                        return Ok(Value::Bool(false));
+                    }
+                }
+
+                // Everything else is either incomparable (e.g. internal types) or
+                // false.
+                _ => return Ok(Value::Bool(false)),
+            };
+            if !result {
+                return Ok(Value::Bool(false));
+            }
+        }
+    }
+
+    pub fn type_of(&self) -> &'static str {
+        match self {
+            Value::Null => "null",
+            Value::Bool(_) => "bool",
+            Value::Integer(_) => "int",
+            Value::Float(_) => "float",
+            Value::String(_) => "string",
+            Value::Path(_) => "path",
+            Value::Attrs(_) => "set",
+            Value::List(_) => "list",
+            Value::Closure(_) | Value::Builtin(_) => "lambda",
+
+            // Internal types. Note: These are only elaborated here
+            // because it makes debugging easier. If a user ever sees
+            // any of these strings, it's a bug.
+            Value::Thunk(_) => "internal[thunk]",
+            Value::AttrNotFound => "internal[attr_not_found]",
+            Value::Blueprint(_) => "internal[blueprint]",
+            Value::DeferredUpvalue(_) => "internal[deferred_upvalue]",
+            Value::UnresolvedPath(_) => "internal[unresolved_path]",
+            Value::Json(..) => "internal[json]",
+            Value::FinaliseRequest(_) => "internal[finaliser_sentinel]",
+            Value::Catchable(_) => "internal[catchable]",
+        }
+    }
+
+    gen_cast!(as_bool, bool, "bool", Value::Bool(b), *b);
+    gen_cast!(as_int, i64, "int", Value::Integer(x), *x);
+    gen_cast!(as_float, f64, "float", Value::Float(x), *x);
+
+    /// Cast the current value into a **context-less** string.
+    /// If you wanted to cast it into a potentially contextful string,
+    /// you have to explicitly use `to_contextful_str`.
+    /// Contextful strings are special, they should not be obtained
+    /// everytime you want a string.
+    pub fn to_str(&self) -> Result<NixString, ErrorKind> {
+        match self {
+            Value::String(s) if !s.has_context() => Ok((*s).clone()),
+            Value::Thunk(thunk) => Self::to_str(&thunk.value()),
+            other => Err(type_error("contextless strings", other)),
+        }
+    }
+
+    gen_cast!(
+        to_contextful_str,
+        NixString,
+        "contextful string",
+        Value::String(s),
+        (*s).clone()
+    );
+    gen_cast!(to_path, Box<PathBuf>, "path", Value::Path(p), p.clone());
+    gen_cast!(to_attrs, Box<NixAttrs>, "set", Value::Attrs(a), a.clone());
+    gen_cast!(to_list, NixList, "list", Value::List(l), l.clone());
+    gen_cast!(
+        as_closure,
+        Rc<Closure>,
+        "lambda",
+        Value::Closure(c),
+        c.clone()
+    );
+
+    gen_cast_mut!(as_list_mut, NixList, "list", List);
+
+    gen_is!(is_path, Value::Path(_));
+    gen_is!(is_number, Value::Integer(_) | Value::Float(_));
+    gen_is!(is_bool, Value::Bool(_));
+    gen_is!(is_attrs, Value::Attrs(_));
+    gen_is!(is_catchable, Value::Catchable(_));
+
+    /// Returns `true` if the value is a [`Thunk`].
+    ///
+    /// [`Thunk`]: Value::Thunk
+    pub fn is_thunk(&self) -> bool {
+        matches!(self, Self::Thunk(..))
+    }
+
+    /// Compare `self` against other using (fallible) Nix ordering semantics.
+    ///
+    /// The function is intended to be used from within other generator
+    /// functions or `gen!` blocks.
+    pub async fn nix_cmp_ordering(
+        self,
+        other: Self,
+        co: GenCo,
+        span: LightSpan,
+    ) -> Result<Result<Ordering, CatchableErrorKind>, ErrorKind> {
+        Self::nix_cmp_ordering_(self, other, co, span).await
+    }
+
+    async fn nix_cmp_ordering_(
+        myself: Self,
+        other: Self,
+        co: GenCo,
+        span: LightSpan,
+    ) -> Result<Result<Ordering, CatchableErrorKind>, ErrorKind> {
+        // this is a stack of ((v1,v2),peq) triples to be compared;
+        // after each triple is popped off of the stack, v1 is
+        // compared to v2 using peq-mode PointerEquality
+        let mut vals = vec![((myself, other), PointerEquality::ForbidAll)];
+
+        loop {
+            let ((mut a, mut b), ptr_eq) = if let Some(abp) = vals.pop() {
+                abp
+            } else {
+                // stack is empty, so they are equal
+                return Ok(Ok(Ordering::Equal));
+            };
+            if ptr_eq == PointerEquality::AllowAll {
+                if a.clone()
+                    .nix_eq(b.clone(), &co, PointerEquality::AllowAll, span.clone())
+                    .await?
+                    .as_bool()?
+                {
+                    continue;
+                }
+                a = a.force(&co, span.clone()).await?;
+                b = b.force(&co, span.clone()).await?;
+            }
+            let result = match (a, b) {
+                (Value::Catchable(c), _) => return Ok(Err(*c)),
+                (_, Value::Catchable(c)) => return Ok(Err(*c)),
+                // same types
+                (Value::Integer(i1), Value::Integer(i2)) => i1.cmp(&i2),
+                (Value::Float(f1), Value::Float(f2)) => f1.total_cmp(&f2),
+                (Value::String(s1), Value::String(s2)) => s1.cmp(&s2),
+                (Value::List(l1), Value::List(l2)) => {
+                    let max = l1.len().max(l2.len());
+                    for j in 0..max {
+                        let i = max - 1 - j;
+                        if i >= l2.len() {
+                            vals.push(((1.into(), 0.into()), PointerEquality::ForbidAll));
+                        } else if i >= l1.len() {
+                            vals.push(((0.into(), 1.into()), PointerEquality::ForbidAll));
+                        } else {
+                            vals.push(((l1[i].clone(), l2[i].clone()), PointerEquality::AllowAll));
+                        }
+                    }
+                    continue;
+                }
+
+                // different types
+                (Value::Integer(i1), Value::Float(f2)) => (i1 as f64).total_cmp(&f2),
+                (Value::Float(f1), Value::Integer(i2)) => f1.total_cmp(&(i2 as f64)),
+
+                // unsupported types
+                (lhs, rhs) => {
+                    return Err(ErrorKind::Incomparable {
+                        lhs: lhs.type_of(),
+                        rhs: rhs.type_of(),
+                    })
+                }
+            };
+            if result != Ordering::Equal {
+                return Ok(Ok(result));
+            }
+        }
+    }
+
+    // TODO(amjoseph): de-asyncify this (when called directly by the VM)
+    pub async fn force(self, co: &GenCo, span: LightSpan) -> Result<Value, ErrorKind> {
+        if let Value::Thunk(thunk) = self {
+            // TODO(amjoseph): use #[tailcall::mutual]
+            return Thunk::force_(thunk, co, span).await;
+        }
+        Ok(self)
+    }
+
+    // need two flavors, because async
+    pub async fn force_owned_genco(self, co: GenCo, span: LightSpan) -> Result<Value, ErrorKind> {
+        if let Value::Thunk(thunk) = self {
+            // TODO(amjoseph): use #[tailcall::mutual]
+            return Thunk::force_(thunk, &co, span).await;
+        }
+        Ok(self)
+    }
+
+    /// Explain a value in a human-readable way, e.g. by presenting
+    /// the docstrings of functions if present.
+    pub fn explain(&self) -> String {
+        match self {
+            Value::Null => "the 'null' value".into(),
+            Value::Bool(b) => format!("the boolean value '{}'", b),
+            Value::Integer(i) => format!("the integer '{}'", i),
+            Value::Float(f) => format!("the float '{}'", f),
+            Value::String(s) if s.has_context() => format!("the contextful string '{}'", s),
+            Value::String(s) => format!("the contextless string '{}'", s),
+            Value::Path(p) => format!("the path '{}'", p.to_string_lossy()),
+            Value::Attrs(attrs) => format!("a {}-item attribute set", attrs.len()),
+            Value::List(list) => format!("a {}-item list", list.len()),
+
+            Value::Closure(f) => {
+                if let Some(name) = &f.lambda.name {
+                    format!("the user-defined Nix function '{}'", name)
+                } else {
+                    "a user-defined Nix function".to_string()
+                }
+            }
+
+            Value::Builtin(b) => {
+                let mut out = format!("the builtin function '{}'", b.name());
+                if let Some(docs) = b.documentation() {
+                    out.push_str("\n\n");
+                    out.push_str(docs);
+                }
+                out
+            }
+
+            // TODO: handle suspended thunks with a different explanation instead of panicking
+            Value::Thunk(t) => t.value().explain(),
+
+            Value::Catchable(_) => "a catchable failure".into(),
+
+            Value::AttrNotFound
+            | Value::Blueprint(_)
+            | Value::DeferredUpvalue(_)
+            | Value::UnresolvedPath(_)
+            | Value::Json(..)
+            | Value::FinaliseRequest(_) => "an internal Tvix evaluator value".into(),
+        }
+    }
+}
+
+trait TotalDisplay {
+    fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result;
+}
+
+impl Display for Value {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.total_fmt(f, &mut Default::default())
+    }
+}
+
+/// Emulates the C++-Nix style formatting of floats, which diverges
+/// significantly from Rust's native float formatting.
+fn total_fmt_float<F: std::fmt::Write>(num: f64, mut f: F) -> std::fmt::Result {
+    let mut buf = [b'0'; lexical_core::BUFFER_SIZE];
+    let mut s = lexical_core::write_with_options::<f64, { CXX_LITERAL }>(
+        num,
+        &mut buf,
+        &WRITE_FLOAT_OPTIONS,
+    );
+
+    // apply some postprocessing on the buffer. If scientific
+    // notation is used (we see an `e`), and the next character is
+    // a digit, add the missing `+` sign.)
+    let mut new_s = Vec::with_capacity(s.len());
+
+    if s.contains(&b'e') {
+        for (i, c) in s.iter().enumerate() {
+            // encountered `e`
+            if c == &b'e' {
+                // next character is a digit (so no negative exponent)
+                if s.len() > i && s[i + 1].is_ascii_digit() {
+                    // copy everything from the start up to (including) the e
+                    new_s.extend_from_slice(&s[0..=i]);
+                    // add the missing '+'
+                    new_s.push(b'+');
+                    // check for the remaining characters.
+                    // If it's only one, we need to prepend a trailing zero
+                    if s.len() == i + 2 {
+                        new_s.push(b'0');
+                    }
+                    new_s.extend_from_slice(&s[i + 1..]);
+                    break;
+                }
+            }
+        }
+
+        // if we modified the scientific notation, flip the reference
+        if !new_s.is_empty() {
+            s = &mut new_s
+        }
+    } else if s.contains(&b'.') {
+        // else, if this is not scientific notation, and there's a
+        // decimal point, make sure we really drop trailing zeroes.
+        // In some cases, lexical_core doesn't.
+        for (i, c) in s.iter().enumerate() {
+            // at `.``
+            if c == &b'.' {
+                // trim zeroes from the right side.
+                let frac = String::from_utf8_lossy(&s[i + 1..]);
+                let frac_no_trailing_zeroes = frac.trim_end_matches('0');
+
+                if frac.len() != frac_no_trailing_zeroes.len() {
+                    // we managed to strip something, construct new_s
+                    if frac_no_trailing_zeroes.is_empty() {
+                        // if frac_no_trailing_zeroes is empty, the fractional part was all zeroes, so we can drop the decimal point as well
+                        new_s.extend_from_slice(&s[0..=i - 1]);
+                    } else {
+                        // else, assemble the rest of the string
+                        new_s.extend_from_slice(&s[0..=i]);
+                        new_s.extend_from_slice(frac_no_trailing_zeroes.as_bytes());
+                    }
+
+                    // flip the reference
+                    s = &mut new_s;
+                    break;
+                }
+            }
+        }
+    }
+
+    write!(f, "{}", String::from_utf8_lossy(s))
+}
+
+impl TotalDisplay for Value {
+    fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
+        match self {
+            Value::Null => f.write_str("null"),
+            Value::Bool(true) => f.write_str("true"),
+            Value::Bool(false) => f.write_str("false"),
+            Value::Integer(num) => write!(f, "{}", num),
+            Value::String(s) => s.fmt(f),
+            Value::Path(p) => p.display().fmt(f),
+            Value::Attrs(attrs) => attrs.total_fmt(f, set),
+            Value::List(list) => list.total_fmt(f, set),
+            // TODO: fancy REPL display with position
+            Value::Closure(_) => f.write_str("<LAMBDA>"),
+            Value::Builtin(builtin) => builtin.fmt(f),
+
+            // Nix prints floats with a maximum precision of 5 digits
+            // only. Except when it decides to use scientific notation
+            // (with a + after the `e`, and zero-padded to 0 digits)
+            Value::Float(num) => total_fmt_float(*num, f),
+
+            // internal types
+            Value::AttrNotFound => f.write_str("internal[not found]"),
+            Value::Blueprint(_) => f.write_str("internal[blueprint]"),
+            Value::DeferredUpvalue(_) => f.write_str("internal[deferred_upvalue]"),
+            Value::UnresolvedPath(_) => f.write_str("internal[unresolved_path]"),
+            Value::Json(..) => f.write_str("internal[json]"),
+            Value::FinaliseRequest(_) => f.write_str("internal[finaliser_sentinel]"),
+
+            // Delegate thunk display to the type, as it must handle
+            // the case of already evaluated or cyclic thunks.
+            Value::Thunk(t) => t.total_fmt(f, set),
+            Value::Catchable(_) => panic!("total_fmt() called on a CatchableErrorKind"),
+        }
+    }
+}
+
+impl From<bool> for Value {
+    fn from(b: bool) -> Self {
+        Value::Bool(b)
+    }
+}
+
+impl From<i64> for Value {
+    fn from(i: i64) -> Self {
+        Self::Integer(i)
+    }
+}
+
+impl From<f64> for Value {
+    fn from(i: f64) -> Self {
+        Self::Float(i)
+    }
+}
+
+impl From<PathBuf> for Value {
+    fn from(path: PathBuf) -> Self {
+        Self::Path(Box::new(path))
+    }
+}
+
+fn type_error(expected: &'static str, actual: &Value) -> ErrorKind {
+    ErrorKind::TypeError {
+        expected,
+        actual: actual.type_of(),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::mem::size_of;
+
+    #[test]
+    fn size() {
+        assert_eq!(size_of::<Value>(), 16);
+    }
+
+    mod floats {
+        use crate::value::total_fmt_float;
+
+        #[test]
+        fn format_float() {
+            let ff = [
+                (0f64, "0"),
+                (1.0f64, "1"),
+                (-0.01, "-0.01"),
+                (5e+22, "5e+22"),
+                (1e6, "1e+06"),
+                (-2E-2, "-0.02"),
+                (6.626e-34, "6.626e-34"),
+                (9_224_617.445_991_227, "9.22462e+06"),
+            ];
+            for (n, expected) in ff.iter() {
+                let mut buf = String::new();
+                let res = total_fmt_float(*n, &mut buf);
+                assert!(res.is_ok());
+                assert_eq!(
+                    expected, &buf,
+                    "{} should be formatted as {}, but got {}",
+                    n, expected, &buf
+                );
+            }
+        }
+    }
+}
diff --git a/tvix/eval/src/value/path.rs b/tvix/eval/src/value/path.rs
new file mode 100644
index 0000000000..ad526a8746
--- /dev/null
+++ b/tvix/eval/src/value/path.rs
@@ -0,0 +1,14 @@
+use path_clean::PathClean;
+use std::path::PathBuf;
+
+/// This function should match the behavior of canonPath() in
+/// src/libutil/util.cc of cppnix.  Currently it does not match that
+/// behavior; it uses the `path_clean` library which is based on the
+/// Go standard library
+///
+/// TODO: make this match the behavior of cppnix
+/// TODO: write tests for this
+
+pub fn canon_path(path: PathBuf) -> PathBuf {
+    path.clean()
+}
diff --git a/tvix/eval/src/value/string.rs b/tvix/eval/src/value/string.rs
new file mode 100644
index 0000000000..ceb43f1ea5
--- /dev/null
+++ b/tvix/eval/src/value/string.rs
@@ -0,0 +1,873 @@
+//! This module implements Nix language strings.
+//!
+//! Nix language strings never need to be modified on the language
+//! level, allowing us to shave off some memory overhead and only
+//! paying the cost when creating new strings.
+use bstr::{BStr, BString, ByteSlice, Chars};
+use rnix::ast;
+use std::alloc::{alloc, dealloc, handle_alloc_error, Layout};
+use std::borrow::{Borrow, Cow};
+use std::collections::HashSet;
+use std::ffi::c_void;
+use std::fmt::{self, Debug, Display};
+use std::hash::Hash;
+use std::ops::Deref;
+use std::ptr::{self, NonNull};
+use std::slice;
+
+use serde::de::{Deserializer, Visitor};
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, Serialize, Hash, PartialEq, Eq)]
+pub enum NixContextElement {
+    /// A plain store path (e.g. source files copied to the store)
+    Plain(String),
+
+    /// Single output of a derivation, represented by its name and its derivation path.
+    Single { name: String, derivation: String },
+
+    /// A reference to a complete derivation
+    /// including its source and its binary closure.
+    /// It is used for the `drvPath` attribute context.
+    /// The referred string is the store path to
+    /// the derivation path.
+    Derivation(String),
+}
+
+/// Nix context strings representation in Tvix. This tracks a set of different kinds of string
+/// dependencies that we can come across during manipulation of our language primitives, mostly
+/// strings. There's some simple algebra of context strings and how they propagate w.r.t. primitive
+/// operations, e.g. concatenation, interpolation and other string operations.
+#[repr(transparent)]
+#[derive(Clone, Debug, Serialize, Default)]
+pub struct NixContext(HashSet<NixContextElement>);
+
+impl From<NixContextElement> for NixContext {
+    fn from(value: NixContextElement) -> Self {
+        Self([value].into())
+    }
+}
+
+impl From<HashSet<NixContextElement>> for NixContext {
+    fn from(value: HashSet<NixContextElement>) -> Self {
+        Self(value)
+    }
+}
+
+impl NixContext {
+    /// Creates an empty context that can be populated
+    /// and passed to form a contextful [NixString], albeit
+    /// if the context is concretly empty, the resulting [NixString]
+    /// will be contextless.
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// For internal consumers, we let people observe
+    /// if the [NixContext] is actually empty or not
+    /// to decide whether they want to skip the allocation
+    /// of a full blown [HashSet].
+    pub(crate) fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+
+    /// Consumes a new [NixContextElement] and add it if not already
+    /// present in this context.
+    pub fn append(mut self, other: NixContextElement) -> Self {
+        self.0.insert(other);
+        self
+    }
+
+    /// Consumes both ends of the join into a new NixContent
+    /// containing the union of elements of both ends.
+    pub fn join(mut self, other: &mut NixContext) -> Self {
+        let other_set = std::mem::take(&mut other.0);
+        let mut set: HashSet<NixContextElement> = std::mem::take(&mut self.0);
+        set.extend(other_set);
+        Self(set)
+    }
+
+    /// Copies from another [NixString] its context strings
+    /// in this context.
+    pub fn mimic(&mut self, other: &NixString) {
+        if let Some(context) = other.context() {
+            self.0.extend(context.iter().cloned());
+        }
+    }
+
+    /// Iterates over "plain" context elements, e.g. sources imported
+    /// in the store without more information, i.e. `toFile` or coerced imported paths.
+    /// It yields paths to the store.
+    pub fn iter_plain(&self) -> impl Iterator<Item = &str> {
+        self.iter().filter_map(|elt| {
+            if let NixContextElement::Plain(s) = elt {
+                Some(s.as_str())
+            } else {
+                None
+            }
+        })
+    }
+
+    /// Iterates over "full derivations" context elements, e.g. something
+    /// referring to their `drvPath`, i.e. their full sources and binary closure.
+    /// It yields derivation paths.
+    pub fn iter_derivation(&self) -> impl Iterator<Item = &str> {
+        self.iter().filter_map(|elt| {
+            if let NixContextElement::Derivation(s) = elt {
+                Some(s.as_str())
+            } else {
+                None
+            }
+        })
+    }
+
+    /// Iterates over "single" context elements, e.g. single derived paths,
+    /// or also known as the single output of a given derivation.
+    /// The first element of the tuple is the output name
+    /// and the second element is the derivation path.
+    pub fn iter_single_outputs(&self) -> impl Iterator<Item = (&str, &str)> {
+        self.iter().filter_map(|elt| {
+            if let NixContextElement::Single { name, derivation } = elt {
+                Some((name.as_str(), derivation.as_str()))
+            } else {
+                None
+            }
+        })
+    }
+
+    /// Iterates over any element of the context.
+    pub fn iter(&self) -> impl Iterator<Item = &NixContextElement> {
+        self.0.iter()
+    }
+
+    /// Produces a list of owned references to this current context,
+    /// no matter its type.
+    pub fn to_owned_references(self) -> Vec<String> {
+        self.0
+            .into_iter()
+            .map(|ctx| match ctx {
+                NixContextElement::Derivation(drv_path) => drv_path,
+                NixContextElement::Plain(store_path) => store_path,
+                NixContextElement::Single { derivation, .. } => derivation,
+            })
+            .collect()
+    }
+}
+
+/// This type is never instantiated, but serves to document the memory layout of the actual heap
+/// allocation for Nix strings.
+#[allow(dead_code)]
+struct NixStringInner {
+    /// The string context, if any.  Note that this is boxed to take advantage of the null pointer
+    /// niche, otherwise this field ends up being very large:
+    ///
+    /// ```notrust
+    /// >> std::mem::size_of::<Option<HashSet<String>>>()
+    /// 48
+    ///
+    /// >> std::mem::size_of::<Option<Box<HashSet<String>>>>()
+    /// 8
+    /// ```
+    context: Option<Box<NixContext>>,
+    /// The length of the data, stored *inline in the allocation*
+    length: usize,
+    /// The actual data for the string itself. Will always be `length` bytes long
+    data: [u8],
+}
+
+#[allow(clippy::zst_offset)]
+impl NixStringInner {
+    /// Construct a [`Layout`] for a nix string allocation with the given length.
+    ///
+    /// Returns a tuple of:
+    /// 1. The layout itself.
+    /// 2. The offset of [`Self::length`] within the allocation, assuming the allocation starts at 0
+    /// 3. The offset of the data array within the allocation, assuming the allocation starts at 0
+    fn layout(len: usize) -> (Layout, usize, usize) {
+        let layout = Layout::new::<Option<Box<NixContext>>>();
+        let (layout, len_offset) = layout.extend(Layout::new::<usize>()).unwrap();
+        let (layout, data_offset) = layout.extend(Layout::array::<u8>(len).unwrap()).unwrap();
+        (layout, len_offset, data_offset)
+    }
+
+    /// Returns the [`Layout`] for an *already-allocated* nix string, loading the length from the
+    /// pointer.
+    ///
+    /// Returns a tuple of:
+    /// 1. The layout itself.
+    /// 2. The offset of [`Self::length`] within the allocation, assuming the allocation starts at 0
+    /// 3. The offset of the data array within the allocation, assuming the allocation starts at 0
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called on a pointer that has been properly initialized with
+    /// [`Self::alloc`]. The data buffer may not necessarily be initialized
+    unsafe fn layout_of(this: NonNull<c_void>) -> (Layout, usize, usize) {
+        let layout = Layout::new::<Option<Box<NixContext>>>();
+        let (_, len_offset) = layout.extend(Layout::new::<usize>()).unwrap();
+        // SAFETY: Layouts are linear, so even though we haven't involved data at all yet, we know
+        // the len_offset is a valid offset into the second field of the allocation
+        let len = *(this.as_ptr().add(len_offset) as *const usize);
+        Self::layout(len)
+    }
+
+    /// Allocate an *uninitialized* nix string with the given length. Writes the length to the
+    /// length value in the pointer, but leaves both context and data uninitialized
+    ///
+    /// This function is safe to call (as constructing pointers of any sort of validity is always
+    /// safe in Rust) but it is unsafe to use the resulting pointer to do anything other than
+    ///
+    /// 1. Read the length
+    /// 2. Write the context
+    /// 3. Write the data
+    ///
+    /// until the string is fully initialized
+    fn alloc(len: usize) -> NonNull<c_void> {
+        let (layout, len_offset, _data_offset) = Self::layout(len);
+        debug_assert_ne!(layout.size(), 0);
+        unsafe {
+            // SAFETY: Layout has non-zero size, since the layout of the context and the
+            // layout of the len both have non-zero size
+            let ptr = alloc(layout);
+
+            if let Some(this) = NonNull::new(ptr as *mut _) {
+                // SAFETY: We've allocated with a layout that causes the len_offset to be in-bounds
+                // and writeable, and if the allocation succeeded it won't wrap
+                ((this.as_ptr() as *mut u8).add(len_offset) as *mut usize).write(len);
+                debug_assert_eq!(Self::len(this), len);
+                this
+            } else {
+                handle_alloc_error(layout);
+            }
+        }
+    }
+
+    /// Deallocate the Nix string at the given pointer
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called with a pointer that has been properly initialized with
+    /// [`Self::alloc`]
+    unsafe fn dealloc(this: NonNull<c_void>) {
+        let (layout, _, _) = Self::layout_of(this);
+        // SAFETY: okay because of the safety guarantees of this method
+        dealloc(this.as_ptr() as *mut u8, layout)
+    }
+
+    /// Return the length of the Nix string at the given pointer
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called with a pointer that has been properly initialized with
+    /// [`Self::alloc`]
+    unsafe fn len(this: NonNull<c_void>) -> usize {
+        let (_, len_offset, _) = Self::layout_of(this);
+        // SAFETY: As long as the safety guarantees of this method are upheld, we've allocated with
+        // a layout that causes the len_offset to be in-bounds and writeable, and if the allocation
+        // succeeded it won't wrap
+        *(this.as_ptr().add(len_offset) as *const usize)
+    }
+
+    /// Return a pointer to the context value within the given Nix string pointer
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called with a pointer that has been properly initialized with
+    /// [`Self::alloc`]
+    unsafe fn context_ptr(this: NonNull<c_void>) -> *mut Option<Box<NixContext>> {
+        // SAFETY: The context is the first field in the layout of the allocation
+        this.as_ptr() as *mut Option<Box<NixContext>>
+    }
+
+    /// Construct a shared reference to the context value within the given Nix string pointer
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called with a pointer that has been properly initialized with
+    /// [`Self::alloc`], and where the context has been properly initialized (by writing to the
+    /// pointer returned from [`Self::context_ptr`]).
+    ///
+    /// Also, all the normal Rust rules about pointer-to-reference conversion apply. See
+    /// [`NonNull::as_ref`] for more.
+    unsafe fn context_ref<'a>(this: NonNull<c_void>) -> &'a Option<Box<NixContext>> {
+        Self::context_ptr(this).as_ref().unwrap()
+    }
+
+    /// Construct a mutable reference to the context value within the given Nix string pointer
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called with a pointer that has been properly initialized with
+    /// [`Self::alloc`], and where the context has been properly initialized (by writing to the
+    /// pointer returned from [`Self::context_ptr`]).
+    ///
+    /// Also, all the normal Rust rules about pointer-to-reference conversion apply. See
+    /// [`NonNull::as_mut`] for more.
+    unsafe fn context_mut<'a>(this: NonNull<c_void>) -> &'a mut Option<Box<NixContext>> {
+        Self::context_ptr(this).as_mut().unwrap()
+    }
+
+    /// Return a pointer to the data array within the given Nix string pointer
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called with a pointer that has been properly initialized with
+    /// [`Self::alloc`]
+    unsafe fn data_ptr(this: NonNull<c_void>) -> *mut u8 {
+        let (_, _, data_offset) = Self::layout_of(this);
+        // SAFETY: data is the third field in the layout of the allocation
+        this.as_ptr().add(data_offset) as *mut u8
+    }
+
+    /// Construct a shared reference to the data slice within the given Nix string pointer
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called with a pointer that has been properly initialized with
+    /// [`Self::alloc`], and where the data array has been properly initialized (by writing to the
+    /// pointer returned from [`Self::data_ptr`]).
+    ///
+    /// Also, all the normal Rust rules about pointer-to-reference conversion apply. See
+    /// [`slice::from_raw_parts`] for more.
+    unsafe fn data_slice<'a>(this: NonNull<c_void>) -> &'a [u8] {
+        let len = Self::len(this);
+        let data = Self::data_ptr(this);
+        slice::from_raw_parts(data, len)
+    }
+
+    /// Construct a mutable reference to the data slice within the given Nix string pointer
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called with a pointer that has been properly initialized with
+    /// [`Self::alloc`], and where the data array has been properly initialized (by writing to the
+    /// pointer returned from [`Self::data_ptr`]).
+    ///
+    /// Also, all the normal Rust rules about pointer-to-reference conversion apply. See
+    /// [`slice::from_raw_parts_mut`] for more.
+    #[allow(dead_code)]
+    unsafe fn data_slice_mut<'a>(this: NonNull<c_void>) -> &'a mut [u8] {
+        let len = Self::len(this);
+        let data = Self::data_ptr(this);
+        slice::from_raw_parts_mut(data, len)
+    }
+
+    /// Clone the Nix string pointed to by this pointer, and return a pointer to a new Nix string
+    /// containing the same data and context.
+    ///
+    /// # Safety
+    ///
+    /// This function must only be called with a pointer that has been properly initialized with
+    /// [`Self::alloc`], and where the context has been properly initialized (by writing to the
+    /// pointer returned from [`Self::context_ptr`]), and the data array has been properly
+    /// initialized (by writing to the pointer returned from [`Self::data_ptr`]).
+    unsafe fn clone(this: NonNull<c_void>) -> NonNull<c_void> {
+        let (layout, _, _) = Self::layout_of(this);
+        let ptr = alloc(layout);
+        if let Some(new) = NonNull::new(ptr as *mut _) {
+            ptr::copy_nonoverlapping(this.as_ptr(), new.as_ptr(), layout.size());
+            Self::context_ptr(new).write(Self::context_ref(this).clone());
+            new
+        } else {
+            handle_alloc_error(layout);
+        }
+    }
+}
+
+/// Nix string values
+///
+/// # Internals
+///
+/// For performance reasons (to keep allocations small, and to avoid indirections), [`NixString`] is
+/// represented as a single *thin* pointer to a packed data structure containing the
+/// [context][NixContext] and the string data itself (which is a raw byte array, to match the Nix
+/// string semantics that allow any array of bytes to be represented by a string).
+
+/// This memory representation is documented in [`NixStringInner`], but since Rust prefers to deal
+/// with slices via *fat pointers* (pointers that include the length in the *pointer*, not in the
+/// heap allocation), we have to do mostly manual layout management and allocation for this
+/// representation. See the documentation for the methods of [`NixStringInner`] for more information
+pub struct NixString(NonNull<c_void>);
+
+unsafe impl Send for NixString {}
+unsafe impl Sync for NixString {}
+
+impl Drop for NixString {
+    fn drop(&mut self) {
+        // SAFETY: There's no way to construct a NixString that doesn't leave the allocation correct
+        // according to the rules of dealloc
+        unsafe {
+            NixStringInner::dealloc(self.0);
+        }
+    }
+}
+
+impl Clone for NixString {
+    fn clone(&self) -> Self {
+        // SAFETY: There's no way to construct a NixString that doesn't leave the allocation correct
+        // according to the rules of clone
+        unsafe { Self(NixStringInner::clone(self.0)) }
+    }
+}
+
+impl Debug for NixString {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if let Some(ctx) = self.context() {
+            f.debug_struct("NixString")
+                .field("context", ctx)
+                .field("data", &self.as_bstr())
+                .finish()
+        } else {
+            write!(f, "{:?}", self.as_bstr())
+        }
+    }
+}
+
+impl PartialEq for NixString {
+    fn eq(&self, other: &Self) -> bool {
+        self.as_bstr() == other.as_bstr()
+    }
+}
+
+impl Eq for NixString {}
+
+impl PartialEq<&[u8]> for NixString {
+    fn eq(&self, other: &&[u8]) -> bool {
+        **self == **other
+    }
+}
+
+impl PartialEq<&str> for NixString {
+    fn eq(&self, other: &&str) -> bool {
+        **self == other.as_bytes()
+    }
+}
+
+impl PartialOrd for NixString {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for NixString {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.as_bstr().cmp(other.as_bstr())
+    }
+}
+
+impl From<Box<BStr>> for NixString {
+    fn from(value: Box<BStr>) -> Self {
+        Self::new(&value, None)
+    }
+}
+
+impl From<BString> for NixString {
+    fn from(value: BString) -> Self {
+        Self::new(&value, None)
+    }
+}
+
+impl From<&BStr> for NixString {
+    fn from(value: &BStr) -> Self {
+        value.to_owned().into()
+    }
+}
+
+impl From<&[u8]> for NixString {
+    fn from(value: &[u8]) -> Self {
+        Self::from(value.to_owned())
+    }
+}
+
+impl From<Vec<u8>> for NixString {
+    fn from(value: Vec<u8>) -> Self {
+        value.into_boxed_slice().into()
+    }
+}
+
+impl From<Box<[u8]>> for NixString {
+    fn from(value: Box<[u8]>) -> Self {
+        Self::new(&value, None)
+    }
+}
+
+impl From<&str> for NixString {
+    fn from(s: &str) -> Self {
+        s.as_bytes().into()
+    }
+}
+
+impl From<String> for NixString {
+    fn from(s: String) -> Self {
+        s.into_bytes().into()
+    }
+}
+
+impl<T> From<(T, Option<Box<NixContext>>)> for NixString
+where
+    NixString: From<T>,
+{
+    fn from((s, ctx): (T, Option<Box<NixContext>>)) -> Self {
+        Self::new(NixString::from(s).as_ref(), ctx)
+    }
+}
+
+impl From<Box<str>> for NixString {
+    fn from(s: Box<str>) -> Self {
+        s.into_boxed_bytes().into()
+    }
+}
+
+impl From<ast::Ident> for NixString {
+    fn from(ident: ast::Ident) -> Self {
+        ident.ident_token().unwrap().text().into()
+    }
+}
+
+impl<'a> From<&'a NixString> for &'a BStr {
+    fn from(s: &'a NixString) -> Self {
+        s.as_bstr()
+    }
+}
+
+// No impl From<NixString> for String, that one quotes.
+
+impl From<NixString> for BString {
+    fn from(s: NixString) -> Self {
+        s.as_bstr().to_owned()
+    }
+}
+
+impl AsRef<[u8]> for NixString {
+    fn as_ref(&self) -> &[u8] {
+        self.as_bytes()
+    }
+}
+
+impl Borrow<BStr> for NixString {
+    fn borrow(&self) -> &BStr {
+        self.as_bstr()
+    }
+}
+
+impl Hash for NixString {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.as_bstr().hash(state)
+    }
+}
+
+impl<'de> Deserialize<'de> for NixString {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct StringVisitor;
+
+        impl<'de> Visitor<'de> for StringVisitor {
+            type Value = NixString;
+
+            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+                formatter.write_str("a valid Nix string")
+            }
+
+            fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                Ok(v.into())
+            }
+
+            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                Ok(v.into())
+            }
+        }
+
+        deserializer.deserialize_string(StringVisitor)
+    }
+}
+
+impl Deref for NixString {
+    type Target = BStr;
+
+    fn deref(&self) -> &Self::Target {
+        self.as_bstr()
+    }
+}
+
+#[cfg(feature = "arbitrary")]
+mod arbitrary {
+    use super::*;
+    use proptest::prelude::{any_with, Arbitrary};
+    use proptest::strategy::{BoxedStrategy, Strategy};
+
+    impl Arbitrary for NixString {
+        type Parameters = <String as Arbitrary>::Parameters;
+
+        type Strategy = BoxedStrategy<Self>;
+
+        fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
+            any_with::<String>(args).prop_map(Self::from).boxed()
+        }
+    }
+}
+
+impl NixString {
+    fn new(contents: &[u8], context: Option<Box<NixContext>>) -> Self {
+        // SAFETY: We're always fully initializing a NixString here:
+        //
+        // 1. NixStringInner::alloc sets up the len for us
+        // 2. We set the context, using ptr::write to make sure that the uninitialized memory isn't
+        //    read or dropped
+        // 3. We set the data, using copy_from_nonoverlapping to make sure that the uninitialized
+        //    memory isn't read or dropped
+        //
+        // Only *then* can we construct a NixString
+        unsafe {
+            let inner = NixStringInner::alloc(contents.len());
+            NixStringInner::context_ptr(inner).write(context);
+            NixStringInner::data_ptr(inner)
+                .copy_from_nonoverlapping(contents.as_ptr(), contents.len());
+            Self(inner)
+        }
+    }
+
+    pub fn new_inherit_context_from<T>(other: &NixString, new_contents: T) -> Self
+    where
+        NixString: From<T>,
+    {
+        Self::new(
+            Self::from(new_contents).as_ref(),
+            other.context().map(|c| Box::new(c.clone())),
+        )
+    }
+
+    pub fn new_context_from<T>(context: NixContext, contents: T) -> Self
+    where
+        NixString: From<T>,
+    {
+        Self::new(
+            Self::from(contents).as_ref(),
+            if context.is_empty() {
+                None
+            } else {
+                Some(Box::new(context))
+            },
+        )
+    }
+
+    pub fn as_bstr(&self) -> &BStr {
+        BStr::new(self.as_bytes())
+    }
+
+    pub fn as_bytes(&self) -> &[u8] {
+        // SAFETY: There's no way to construct an uninitialized NixString (see the SAFETY comment in
+        // `new`)
+        unsafe { NixStringInner::data_slice(self.0) }
+    }
+
+    pub fn into_bstring(self) -> BString {
+        self.as_bstr().to_owned()
+    }
+
+    /// Return a displayable representation of the string as an
+    /// identifier.
+    ///
+    /// This is used when printing out strings used as e.g. attribute
+    /// set keys, as those are only escaped in the presence of special
+    /// characters.
+    pub fn ident_str(&self) -> Cow<str> {
+        let escaped = match self.to_str_lossy() {
+            Cow::Borrowed(s) => nix_escape_string(s),
+            Cow::Owned(s) => nix_escape_string(&s).into_owned().into(),
+        };
+        match escaped {
+            // A borrowed string is unchanged and can be returned as
+            // is.
+            Cow::Borrowed(_) => {
+                if is_valid_nix_identifier(&escaped) && !is_keyword(&escaped) {
+                    escaped
+                } else {
+                    Cow::Owned(format!("\"{}\"", escaped))
+                }
+            }
+
+            // An owned string has escapes, and needs the outer quotes
+            // for display.
+            Cow::Owned(s) => Cow::Owned(format!("\"{}\"", s)),
+        }
+    }
+
+    pub fn concat(&self, other: &Self) -> Self {
+        let mut s = self.to_vec();
+        s.extend(&(***other));
+
+        let context = [self.context(), other.context()]
+            .into_iter()
+            .flatten()
+            .fold(NixContext::new(), |acc_ctx, new_ctx| {
+                acc_ctx.join(&mut new_ctx.clone())
+            });
+        Self::new_context_from(context, s)
+    }
+
+    pub(crate) fn context(&self) -> Option<&NixContext> {
+        // SAFETY: There's no way to construct an uninitialized or invalid NixString (see the SAFETY
+        // comment in `new`).
+        //
+        // Also, we're using the same lifetime and mutability as self, to fit the
+        // pointer-to-reference conversion rules
+        unsafe { NixStringInner::context_ref(self.0).as_deref() }
+    }
+
+    pub(crate) fn context_mut(&mut self) -> Option<&mut NixContext> {
+        // SAFETY: There's no way to construct an uninitialized or invalid NixString (see the SAFETY
+        // comment in `new`).
+        //
+        // Also, we're using the same lifetime and mutability as self, to fit the
+        // pointer-to-reference conversion rules
+        unsafe { NixStringInner::context_mut(self.0).as_deref_mut() }
+    }
+
+    pub fn iter_context(&self) -> impl Iterator<Item = &NixContext> {
+        self.context().into_iter()
+    }
+
+    pub fn iter_plain(&self) -> impl Iterator<Item = &str> {
+        self.iter_context().flat_map(|context| context.iter_plain())
+    }
+
+    pub fn iter_derivation(&self) -> impl Iterator<Item = &str> {
+        return self
+            .iter_context()
+            .flat_map(|context| context.iter_derivation());
+    }
+
+    pub fn iter_single_outputs(&self) -> impl Iterator<Item = (&str, &str)> {
+        return self
+            .iter_context()
+            .flat_map(|context| context.iter_single_outputs());
+    }
+
+    /// Returns whether this Nix string possess a context or not.
+    pub fn has_context(&self) -> bool {
+        self.context().is_some()
+    }
+
+    /// This clears the context of that string, losing
+    /// all dependency tracking information.
+    pub fn clear_context(&mut self) {
+        // SAFETY: There's no way to construct an uninitialized or invalid NixString (see the SAFETY
+        // comment in `new`).
+        *unsafe { NixStringInner::context_mut(self.0) } = None;
+    }
+
+    pub fn chars(&self) -> Chars<'_> {
+        self.as_bstr().chars()
+    }
+}
+
+fn nix_escape_char(ch: char, next: Option<&char>) -> Option<&'static str> {
+    match (ch, next) {
+        ('\\', _) => Some("\\\\"),
+        ('"', _) => Some("\\\""),
+        ('\n', _) => Some("\\n"),
+        ('\t', _) => Some("\\t"),
+        ('\r', _) => Some("\\r"),
+        ('$', Some('{')) => Some("\\$"),
+        _ => None,
+    }
+}
+
+/// Return true if this string is a keyword -- character strings
+/// which lexically match the "identifier" production but are not
+/// parsed as identifiers.  See also cppnix commit
+/// b72bc4a972fe568744d98b89d63adcd504cb586c.
+fn is_keyword(s: &str) -> bool {
+    matches!(
+        s,
+        "if" | "then" | "else" | "assert" | "with" | "let" | "in" | "rec" | "inherit"
+    )
+}
+
+/// Return true if this string can be used as an identifier in Nix.
+fn is_valid_nix_identifier(s: &str) -> bool {
+    // adapted from rnix-parser's tokenizer.rs
+    let mut chars = s.chars();
+    match chars.next() {
+        Some('a'..='z' | 'A'..='Z' | '_') => (),
+        _ => return false,
+    }
+    for c in chars {
+        match c {
+            'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '\'' => (),
+            _ => return false,
+        }
+    }
+    true
+}
+
+/// Escape a Nix string for display, as most user-visible representation
+/// are escaped strings.
+///
+/// Note that this does not add the outer pair of surrounding quotes.
+fn nix_escape_string(input: &str) -> Cow<str> {
+    let mut iter = input.char_indices().peekable();
+
+    while let Some((i, c)) = iter.next() {
+        if let Some(esc) = nix_escape_char(c, iter.peek().map(|(_, c)| c)) {
+            let mut escaped = String::with_capacity(input.len());
+            escaped.push_str(&input[..i]);
+            escaped.push_str(esc);
+
+            // In theory we calculate how many bytes it takes to represent `esc`
+            // in UTF-8 and use that for the offset. It is, however, safe to
+            // assume that to be 1, as all characters that can be escaped in a
+            // Nix string are ASCII.
+            let mut inner_iter = input[i + 1..].chars().peekable();
+            while let Some(c) = inner_iter.next() {
+                match nix_escape_char(c, inner_iter.peek()) {
+                    Some(esc) => escaped.push_str(esc),
+                    None => escaped.push(c),
+                }
+            }
+
+            return Cow::Owned(escaped);
+        }
+    }
+
+    Cow::Borrowed(input)
+}
+
+impl Display for NixString {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str("\"")?;
+        f.write_str(&nix_escape_string(&self.to_str_lossy()))?;
+        f.write_str("\"")
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use test_strategy::proptest;
+
+    use super::*;
+
+    use crate::properties::{eq_laws, hash_laws, ord_laws};
+
+    #[test]
+    fn size() {
+        assert_eq!(std::mem::size_of::<NixString>(), 8);
+    }
+
+    #[proptest]
+    fn clone_strings(s: NixString) {
+        drop(s.clone())
+    }
+
+    eq_laws!(NixString);
+    hash_laws!(NixString);
+    ord_laws!(NixString);
+}
diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs
new file mode 100644
index 0000000000..a67537f945
--- /dev/null
+++ b/tvix/eval/src/value/thunk.rs
@@ -0,0 +1,434 @@
+//! This module implements the runtime representation of Thunks.
+//!
+//! Thunks are a special kind of Nix value, similar to a 0-argument
+//! closure that yields some value. Thunks are used to implement the
+//! lazy evaluation behaviour of Nix:
+//!
+//! Whenever the compiler determines that an expression should be
+//! evaluated lazily, it creates a thunk instead of compiling the
+//! expression value directly. At any point in the runtime where the
+//! actual value of a thunk is required, it is "forced", meaning that
+//! the encompassing computation takes place and the thunk takes on
+//! its new value.
+//!
+//! Thunks have interior mutability to be able to memoise their
+//! computation. Once a thunk is evaluated, its internal
+//! representation becomes the result of the expression. It is legal
+//! for the runtime to replace a thunk object directly with its value
+//! object, but when forcing a thunk, the runtime *must* mutate the
+//! memoisable slot.
+
+use std::{
+    cell::{Ref, RefCell, RefMut},
+    collections::HashSet,
+    fmt::Debug,
+    rc::Rc,
+};
+
+use crate::{
+    errors::ErrorKind,
+    opcode::OpCode,
+    spans::LightSpan,
+    upvalues::Upvalues,
+    value::Closure,
+    vm::generators::{self, GenCo},
+    Value,
+};
+
+use super::{Lambda, TotalDisplay};
+use codemap::Span;
+
+/// Internal representation of a suspended native thunk.
+struct SuspendedNative(Box<dyn Fn() -> Result<Value, ErrorKind>>);
+
+impl Debug for SuspendedNative {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "SuspendedNative({:p})", self.0)
+    }
+}
+
+/// Internal representation of the different states of a thunk.
+///
+/// Upvalues must be finalised before leaving the initial state
+/// (Suspended or RecursiveClosure).  The [`value()`] function may
+/// not be called until the thunk is in the final state (Evaluated).
+#[derive(Debug)]
+enum ThunkRepr {
+    /// Thunk is closed over some values, suspended and awaiting
+    /// execution.
+    Suspended {
+        lambda: Rc<Lambda>,
+        upvalues: Rc<Upvalues>,
+        light_span: LightSpan,
+    },
+
+    /// Thunk is a suspended native computation.
+    Native(SuspendedNative),
+
+    /// Thunk currently under-evaluation; encountering a blackhole
+    /// value means that infinite recursion has occured.
+    Blackhole {
+        /// Span at which the thunk was first forced.
+        forced_at: LightSpan,
+
+        /// Span at which the thunk was originally suspended.
+        suspended_at: Option<LightSpan>,
+
+        /// Span of the first instruction of the actual code inside
+        /// the thunk.
+        content_span: Option<Span>,
+    },
+
+    // TODO(amjoseph): consider changing `Value` to `Rc<Value>` to avoid
+    // expensive clone()s in Thunk::force().
+    /// Fully evaluated thunk.
+    Evaluated(Value),
+}
+
+impl ThunkRepr {
+    fn debug_repr(&self) -> String {
+        match self {
+            ThunkRepr::Evaluated(v) => format!("thunk(val|{})", v),
+            ThunkRepr::Blackhole { .. } => "thunk(blackhole)".to_string(),
+            ThunkRepr::Native(_) => "thunk(native)".to_string(),
+            ThunkRepr::Suspended { lambda, .. } => format!("thunk({:p})", *lambda),
+        }
+    }
+
+    /// Return the Value within a fully-evaluated ThunkRepr; panics
+    /// if the thunk is not fully-evaluated.
+    fn expect(self) -> Value {
+        match self {
+            ThunkRepr::Evaluated(value) => value,
+            ThunkRepr::Blackhole { .. } => panic!("Thunk::expect() called on a black-holed thunk"),
+            ThunkRepr::Suspended { .. } | ThunkRepr::Native(_) => {
+                panic!("Thunk::expect() called on a suspended thunk")
+            }
+        }
+    }
+
+    fn expect_ref(&self) -> &Value {
+        match self {
+            ThunkRepr::Evaluated(value) => value,
+            ThunkRepr::Blackhole { .. } => panic!("Thunk::expect() called on a black-holed thunk"),
+            ThunkRepr::Suspended { .. } | ThunkRepr::Native(_) => {
+                panic!("Thunk::expect() called on a suspended thunk")
+            }
+        }
+    }
+
+    pub fn is_forced(&self) -> bool {
+        match self {
+            ThunkRepr::Evaluated(Value::Thunk(_)) => false,
+            ThunkRepr::Evaluated(_) => true,
+            _ => false,
+        }
+    }
+}
+
+/// A thunk is created for any value which requires non-strict
+/// evaluation due to self-reference or lazy semantics (or both).
+/// Every reference cycle involving `Value`s will contain at least
+/// one `Thunk`.
+#[derive(Clone, Debug)]
+pub struct Thunk(Rc<RefCell<ThunkRepr>>);
+
+impl Thunk {
+    pub fn new_closure(lambda: Rc<Lambda>) -> Self {
+        Thunk(Rc::new(RefCell::new(ThunkRepr::Evaluated(Value::Closure(
+            Rc::new(Closure {
+                upvalues: Rc::new(Upvalues::with_capacity(lambda.upvalue_count)),
+                lambda: lambda.clone(),
+            }),
+        )))))
+    }
+
+    pub fn new_suspended(lambda: Rc<Lambda>, light_span: LightSpan) -> Self {
+        Thunk(Rc::new(RefCell::new(ThunkRepr::Suspended {
+            upvalues: Rc::new(Upvalues::with_capacity(lambda.upvalue_count)),
+            lambda: lambda.clone(),
+            light_span,
+        })))
+    }
+
+    pub fn new_suspended_native(native: Box<dyn Fn() -> Result<Value, ErrorKind>>) -> Self {
+        Thunk(Rc::new(RefCell::new(ThunkRepr::Native(SuspendedNative(
+            native,
+        )))))
+    }
+
+    /// Helper function to create a [`Thunk`] that calls a function given as the
+    /// [`Value`] `callee` with the argument `arg` when it is forced. This is
+    /// particularly useful in builtin implementations if the result of calling
+    /// a function does not need to be forced immediately, because e.g. it is
+    /// stored in an attribute set.
+    pub fn new_suspended_call(callee: Value, arg: Value, light_span: LightSpan) -> Self {
+        let mut lambda = Lambda::default();
+        let span = light_span.span();
+
+        let arg_idx = lambda.chunk().push_constant(arg);
+        let f_idx = lambda.chunk().push_constant(callee);
+
+        // This is basically a recreation of compile_apply():
+        // We need to push the argument onto the stack and then the function.
+        // The function (not the argument) needs to be forced before calling.
+        lambda.chunk.push_op(OpCode::OpConstant(arg_idx), span);
+        lambda.chunk().push_op(OpCode::OpConstant(f_idx), span);
+        lambda.chunk.push_op(OpCode::OpForce, span);
+        lambda.chunk.push_op(OpCode::OpCall, span);
+
+        // Inform the VM that the chunk has ended
+        lambda.chunk.push_op(OpCode::OpReturn, span);
+
+        Thunk(Rc::new(RefCell::new(ThunkRepr::Suspended {
+            upvalues: Rc::new(Upvalues::with_capacity(0)),
+            lambda: Rc::new(lambda),
+            light_span,
+        })))
+    }
+
+    fn prepare_blackhole(&self, forced_at: LightSpan) -> ThunkRepr {
+        match &*self.0.borrow() {
+            ThunkRepr::Suspended {
+                light_span, lambda, ..
+            } => ThunkRepr::Blackhole {
+                forced_at,
+                suspended_at: Some(light_span.clone()),
+                content_span: Some(lambda.chunk.first_span()),
+            },
+
+            _ => ThunkRepr::Blackhole {
+                forced_at,
+                suspended_at: None,
+                content_span: None,
+            },
+        }
+    }
+
+    pub async fn force(myself: Thunk, co: GenCo, span: LightSpan) -> Result<Value, ErrorKind> {
+        Self::force_(myself, &co, span).await
+    }
+    pub async fn force_(
+        mut myself: Thunk,
+        co: &GenCo,
+        span: LightSpan,
+    ) -> Result<Value, ErrorKind> {
+        // This vector of "thunks which point to the thunk-being-forced", to
+        // be updated along with it, is necessary in order to write this
+        // function in iterative (and later, mutual-tail-call) form.
+        let mut also_update: Vec<Rc<RefCell<ThunkRepr>>> = vec![];
+
+        loop {
+            // If the current thunk is already fully evaluated, return its evaluated
+            // value. The VM will continue running the code that landed us here.
+            if myself.is_forced() {
+                let val = myself.unwrap_or_clone();
+                for other_thunk in also_update.into_iter() {
+                    other_thunk.replace(ThunkRepr::Evaluated(val.clone()));
+                }
+                return Ok(val);
+            }
+
+            // Begin evaluation of this thunk by marking it as a blackhole, meaning
+            // that any other forcing frame encountering this thunk before its
+            // evaluation is completed detected an evaluation cycle.
+            let inner = myself.0.replace(myself.prepare_blackhole(span.clone()));
+
+            match inner {
+                // If there was already a blackhole in the thunk, this is an
+                // evaluation cycle.
+                ThunkRepr::Blackhole {
+                    forced_at,
+                    suspended_at,
+                    content_span,
+                } => {
+                    return Err(ErrorKind::InfiniteRecursion {
+                        first_force: forced_at.span(),
+                        suspended_at: suspended_at.map(|s| s.span()),
+                        content_span,
+                    })
+                }
+
+                // If there is a native function stored in the thunk, evaluate it
+                // and replace this thunk's representation with the result.
+                ThunkRepr::Native(native) => {
+                    let value = native.0()?;
+                    myself.0.replace(ThunkRepr::Evaluated(value));
+                    continue;
+                }
+
+                // When encountering a suspended thunk, request that the VM enters
+                // it and produces the result.
+                ThunkRepr::Suspended {
+                    lambda,
+                    upvalues,
+                    light_span,
+                } => {
+                    // TODO(amjoseph): use #[tailcall::mutual] here.  This can
+                    // be turned into a tailcall to vm::execute_bytecode() by
+                    // passing `also_update` to it.
+                    let value =
+                        generators::request_enter_lambda(co, lambda, upvalues, light_span).await;
+                    myself.0.replace(ThunkRepr::Evaluated(value));
+                    continue;
+                }
+
+                // nested thunks -- try to flatten before forcing
+                ThunkRepr::Evaluated(Value::Thunk(inner_thunk)) => {
+                    match Rc::try_unwrap(inner_thunk.0) {
+                        Ok(refcell) => {
+                            // we are the only reference to the inner thunk,
+                            // so steal it
+                            myself.0.replace(refcell.into_inner());
+                            continue;
+                        }
+                        Err(rc) => {
+                            let inner_thunk = Thunk(rc);
+                            if inner_thunk.is_forced() {
+                                // tail call to force the inner thunk; note that
+                                // this means the outer thunk remains unforced
+                                // even after calling force() on it; however the
+                                // next time it is forced we will be one
+                                // thunk-forcing closer to it being
+                                // fully-evaluated.
+                                myself
+                                    .0
+                                    .replace(ThunkRepr::Evaluated(inner_thunk.value().clone()));
+                                continue;
+                            }
+                            also_update.push(myself.0.clone());
+                            myself = inner_thunk;
+                            continue;
+                        }
+                    }
+                }
+
+                ThunkRepr::Evaluated(val) => {
+                    return Ok(val);
+                }
+            }
+        }
+    }
+
+    pub fn finalise(&self, stack: &[Value]) {
+        self.upvalues_mut().resolve_deferred_upvalues(stack);
+    }
+
+    pub fn is_evaluated(&self) -> bool {
+        matches!(*self.0.borrow(), ThunkRepr::Evaluated(_))
+    }
+
+    pub fn is_suspended(&self) -> bool {
+        matches!(
+            *self.0.borrow(),
+            ThunkRepr::Suspended { .. } | ThunkRepr::Native(_)
+        )
+    }
+
+    /// Returns true if forcing this thunk will not change it.
+    pub fn is_forced(&self) -> bool {
+        self.0.borrow().is_forced()
+    }
+
+    /// Returns a reference to the inner evaluated value of a thunk.
+    /// It is an error to call this on a thunk that has not been
+    /// forced, or is not otherwise known to be fully evaluated.
+    // Note: Due to the interior mutability of thunks this is
+    // difficult to represent in the type system without impacting the
+    // API too much.
+    pub fn value(&self) -> Ref<Value> {
+        Ref::map(self.0.borrow(), |thunk| match thunk {
+            ThunkRepr::Evaluated(value) => value,
+            ThunkRepr::Blackhole { .. } => panic!("Thunk::value called on a black-holed thunk"),
+            ThunkRepr::Suspended { .. } | ThunkRepr::Native(_) => {
+                panic!("Thunk::value called on a suspended thunk")
+            }
+        })
+    }
+
+    /// Returns the inner evaluated value of a thunk, cloning it if
+    /// the Rc has more than one strong reference.  It is an error
+    /// to call this on a thunk that has not been forced, or is not
+    /// otherwise known to be fully evaluated.
+    fn unwrap_or_clone(self) -> Value {
+        match Rc::try_unwrap(self.0) {
+            Ok(refcell) => refcell.into_inner().expect(),
+            Err(rc) => Ref::map(rc.borrow(), |thunkrepr| thunkrepr.expect_ref()).clone(),
+        }
+    }
+
+    pub fn upvalues(&self) -> Ref<'_, Upvalues> {
+        Ref::map(self.0.borrow(), |thunk| match thunk {
+            ThunkRepr::Suspended { upvalues, .. } => upvalues.as_ref(),
+            ThunkRepr::Evaluated(Value::Closure(c)) => &c.upvalues,
+            _ => panic!("upvalues() on non-suspended thunk"),
+        })
+    }
+
+    pub fn upvalues_mut(&self) -> RefMut<'_, Upvalues> {
+        RefMut::map(self.0.borrow_mut(), |thunk| match thunk {
+            ThunkRepr::Suspended { upvalues, .. } => Rc::get_mut(upvalues).unwrap(),
+            ThunkRepr::Evaluated(Value::Closure(c)) => Rc::get_mut(
+                &mut Rc::get_mut(c).unwrap().upvalues,
+            )
+            .expect(
+                "upvalues_mut() was called on a thunk which already had multiple references to it",
+            ),
+            thunk => panic!("upvalues() on non-suspended thunk: {thunk:?}"),
+        })
+    }
+
+    /// Do not use this without first reading and understanding
+    /// `tvix/docs/value-pointer-equality.md`.
+    pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
+        if Rc::ptr_eq(&self.0, &other.0) {
+            return true;
+        }
+        match &*self.0.borrow() {
+            ThunkRepr::Evaluated(Value::Closure(c1)) => match &*other.0.borrow() {
+                ThunkRepr::Evaluated(Value::Closure(c2)) => Rc::ptr_eq(c1, c2),
+                _ => false,
+            },
+            _ => false,
+        }
+    }
+
+    /// Helper function to format thunks in observer output.
+    pub(crate) fn debug_repr(&self) -> String {
+        self.0.borrow().debug_repr()
+    }
+}
+
+impl TotalDisplay for Thunk {
+    fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
+        if !set.insert(self) {
+            return f.write_str("<CYCLE>");
+        }
+
+        match &*self.0.borrow() {
+            ThunkRepr::Evaluated(v) => v.total_fmt(f, set),
+            ThunkRepr::Suspended { .. } | ThunkRepr::Native(_) => f.write_str("<CODE>"),
+            other => write!(f, "internal[{}]", other.debug_repr()),
+        }
+    }
+}
+
+/// A wrapper type for tracking which thunks have already been seen
+/// in a context. This is necessary for printing and deeply forcing
+/// cyclic non-diverging data structures like `rec { f = [ f ]; }`.
+/// This is separate from the ThunkRepr::Blackhole mechanism, which
+/// detects diverging data structures like `(rec { f = f; }).f`.
+///
+/// The inner `HashSet` is not available on the outside, as it would be
+/// potentially unsafe to interact with the pointers in the set.
+#[derive(Default)]
+pub struct ThunkSet(HashSet<*const ThunkRepr>);
+
+impl ThunkSet {
+    /// Check whether the given thunk has already been seen. Will mark the thunk
+    /// as seen otherwise.
+    pub fn insert(&mut self, thunk: &Thunk) -> bool {
+        let ptr: *const ThunkRepr = thunk.0.as_ptr();
+        self.0.insert(ptr)
+    }
+}
diff --git a/tvix/eval/src/vm/generators.rs b/tvix/eval/src/vm/generators.rs
new file mode 100644
index 0000000000..79de688692
--- /dev/null
+++ b/tvix/eval/src/vm/generators.rs
@@ -0,0 +1,809 @@
+//! This module implements generator logic for the VM. Generators are functions
+//! used during evaluation which can suspend their execution during their
+//! control flow, and request that the VM do something.
+//!
+//! This is used to keep the VM's stack size constant even when evaluating
+//! deeply nested recursive data structures.
+//!
+//! We implement generators using the [`genawaiter`] crate.
+
+use core::pin::Pin;
+use genawaiter::rc::Co;
+pub use genawaiter::rc::Gen;
+use std::fmt::Display;
+use std::future::Future;
+
+use crate::value::PointerEquality;
+use crate::warnings::{EvalWarning, WarningKind};
+use crate::FileType;
+use crate::NixString;
+
+use super::*;
+
+// -- Implementation of generic generator logic.
+
+/// States that a generator can be in while being driven by the VM.
+pub(crate) enum GeneratorState {
+    /// Normal execution of the generator.
+    Running,
+
+    /// Generator is awaiting the result of a forced value.
+    AwaitingValue,
+}
+
+/// Messages that can be sent from generators *to* the VM. In most
+/// cases, the VM will suspend the generator when receiving a message
+/// and enter some other frame to process the request.
+///
+/// Responses are returned to generators via the [`GeneratorResponse`] type.
+pub enum VMRequest {
+    /// Request that the VM forces this value. This message is first sent to the
+    /// VM with the unforced value, then returned to the generator with the
+    /// forced result.
+    ForceValue(Value),
+
+    /// Request that the VM deep-forces the value.
+    DeepForceValue(Value),
+
+    /// Request the value at the given index from the VM's with-stack, in forced
+    /// state.
+    ///
+    /// The value is returned in the `ForceValue` message.
+    WithValue(usize),
+
+    /// Request the value at the given index from the *captured* with-stack, in
+    /// forced state.
+    CapturedWithValue(usize),
+
+    /// Request that the two values be compared for Nix equality. The result is
+    /// returned in the `ForceValue` message.
+    NixEquality(Box<(Value, Value)>, PointerEquality),
+
+    /// Push the given value to the VM's stack. This is used to prepare the
+    /// stack for requesting a function call from the VM.
+    ///
+    /// The VM does not respond to this request, so the next message received is
+    /// `Empty`.
+    StackPush(Value),
+
+    /// Pop a value from the stack and return it to the generator.
+    StackPop,
+
+    /// Request that the VM coerces this value to a string.
+    StringCoerce(Value, CoercionKind),
+
+    /// Request that the VM calls the given value, with arguments already
+    /// prepared on the stack. Value must already be forced.
+    Call(Value),
+
+    /// Request a call frame entering the given lambda immediately. This can be
+    /// used to force thunks.
+    EnterLambda {
+        lambda: Rc<Lambda>,
+        upvalues: Rc<Upvalues>,
+        light_span: LightSpan,
+    },
+
+    /// Emit a runtime warning (already containing a span) through the VM.
+    EmitWarning(EvalWarning),
+
+    /// Emit a runtime warning through the VM. The span of the current generator
+    /// is used for the final warning.
+    EmitWarningKind(WarningKind),
+
+    /// Request a lookup in the VM's import cache, which tracks the
+    /// thunks yielded by previously imported files.
+    ImportCacheLookup(PathBuf),
+
+    /// Provide the VM with an imported value for a given path, which
+    /// it can populate its input cache with.
+    ImportCachePut(PathBuf, Value),
+
+    /// Request that the VM imports the given path through its I/O interface.
+    PathImport(PathBuf),
+
+    /// Request that the VM opens the specified file and provides a reader.
+    OpenFile(PathBuf),
+
+    /// Request that the VM checks whether the given path exists.
+    PathExists(PathBuf),
+
+    /// Request that the VM reads the given path.
+    ReadDir(PathBuf),
+
+    /// Request a reasonable span from the VM.
+    Span,
+
+    /// Request evaluation of `builtins.tryEval` from the VM. See
+    /// [`VM::catch_result`] for an explanation of how this works.
+    TryForce(Value),
+
+    /// Request serialisation of a value to JSON, according to the
+    /// slightly odd Nix evaluation rules.
+    ToJson(Value),
+}
+
+/// Human-readable representation of a generator message, used by observers.
+impl Display for VMRequest {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            VMRequest::ForceValue(v) => write!(f, "force_value({})", v.type_of()),
+            VMRequest::DeepForceValue(v) => {
+                write!(f, "deep_force_value({})", v.type_of())
+            }
+            VMRequest::WithValue(_) => write!(f, "with_value"),
+            VMRequest::CapturedWithValue(_) => write!(f, "captured_with_value"),
+            VMRequest::NixEquality(values, ptr_eq) => {
+                write!(
+                    f,
+                    "nix_eq({}, {}, PointerEquality::{:?})",
+                    values.0.type_of(),
+                    values.1.type_of(),
+                    ptr_eq
+                )
+            }
+            VMRequest::StackPush(v) => write!(f, "stack_push({})", v.type_of()),
+            VMRequest::StackPop => write!(f, "stack_pop"),
+            VMRequest::StringCoerce(
+                v,
+                CoercionKind {
+                    strong,
+                    import_paths,
+                },
+            ) => write!(
+                f,
+                "{}_{}importing_string_coerce({})",
+                if *strong { "strong" } else { "weak" },
+                if *import_paths { "" } else { "non_" },
+                v.type_of()
+            ),
+            VMRequest::Call(v) => write!(f, "call({})", v),
+            VMRequest::EnterLambda { lambda, .. } => {
+                write!(f, "enter_lambda({:p})", *lambda)
+            }
+            VMRequest::EmitWarning(_) => write!(f, "emit_warning"),
+            VMRequest::EmitWarningKind(_) => write!(f, "emit_warning_kind"),
+            VMRequest::ImportCacheLookup(p) => {
+                write!(f, "import_cache_lookup({})", p.to_string_lossy())
+            }
+            VMRequest::ImportCachePut(p, _) => {
+                write!(f, "import_cache_put({})", p.to_string_lossy())
+            }
+            VMRequest::PathImport(p) => write!(f, "path_import({})", p.to_string_lossy()),
+            VMRequest::OpenFile(p) => {
+                write!(f, "open_file({})", p.to_string_lossy())
+            }
+            VMRequest::PathExists(p) => write!(f, "path_exists({})", p.to_string_lossy()),
+            VMRequest::ReadDir(p) => write!(f, "read_dir({})", p.to_string_lossy()),
+            VMRequest::Span => write!(f, "span"),
+            VMRequest::TryForce(v) => write!(f, "try_force({})", v.type_of()),
+            VMRequest::ToJson(v) => write!(f, "to_json({})", v.type_of()),
+        }
+    }
+}
+
+/// Responses returned to generators *from* the VM.
+pub enum VMResponse {
+    /// Empty message. Passed to the generator as the first message,
+    /// or when return values were optional.
+    Empty,
+
+    /// Value produced by the VM and returned to the generator.
+    Value(Value),
+
+    /// Path produced by the VM in response to some IO operation.
+    Path(PathBuf),
+
+    /// VM response with the contents of a directory.
+    Directory(Vec<(bytes::Bytes, FileType)>),
+
+    /// VM response with a span to use at the current point.
+    Span(LightSpan),
+
+    /// [std::io::Reader] produced by the VM in response to some IO operation.
+    Reader(Box<dyn std::io::Read>),
+}
+
+impl Display for VMResponse {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            VMResponse::Empty => write!(f, "empty"),
+            VMResponse::Value(v) => write!(f, "value({})", v),
+            VMResponse::Path(p) => write!(f, "path({})", p.to_string_lossy()),
+            VMResponse::Directory(d) => write!(f, "dir(len = {})", d.len()),
+            VMResponse::Span(_) => write!(f, "span"),
+            VMResponse::Reader(_) => write!(f, "reader"),
+        }
+    }
+}
+
+pub(crate) type Generator =
+    Gen<VMRequest, VMResponse, Pin<Box<dyn Future<Output = Result<Value, ErrorKind>>>>>;
+
+/// Helper function to provide type annotations which are otherwise difficult to
+/// infer.
+pub fn pin_generator(
+    f: impl Future<Output = Result<Value, ErrorKind>> + 'static,
+) -> Pin<Box<dyn Future<Output = Result<Value, ErrorKind>>>> {
+    Box::pin(f)
+}
+
+impl<'o, IO> VM<'o, IO>
+where
+    IO: AsRef<dyn EvalIO> + 'static,
+{
+    /// Helper function to re-enqueue the current generator while it
+    /// is awaiting a value.
+    fn reenqueue_generator(&mut self, name: &'static str, span: LightSpan, generator: Generator) {
+        self.frames.push(Frame::Generator {
+            name,
+            generator,
+            span,
+            state: GeneratorState::AwaitingValue,
+        });
+    }
+
+    /// Helper function to enqueue a new generator.
+    pub(super) fn enqueue_generator<F, G>(&mut self, name: &'static str, span: LightSpan, gen: G)
+    where
+        F: Future<Output = Result<Value, ErrorKind>> + 'static,
+        G: FnOnce(GenCo) -> F,
+    {
+        self.frames.push(Frame::Generator {
+            name,
+            span,
+            state: GeneratorState::Running,
+            generator: Gen::new(|co| pin_generator(gen(co))),
+        });
+    }
+
+    /// Run a generator frame until it yields to the outer control loop, or runs
+    /// to completion.
+    ///
+    /// The return value indicates whether the generator has completed (true),
+    /// or was suspended (false).
+    pub(crate) fn run_generator(
+        &mut self,
+        name: &'static str,
+        span: LightSpan,
+        frame_id: usize,
+        state: GeneratorState,
+        mut generator: Generator,
+        initial_message: Option<VMResponse>,
+    ) -> EvalResult<bool> {
+        // Determine what to send to the generator based on its state.
+        let mut message = match (initial_message, state) {
+            (Some(msg), _) => msg,
+            (_, GeneratorState::Running) => VMResponse::Empty,
+
+            // If control returned here, and the generator is
+            // awaiting a value, send it the top of the stack.
+            (_, GeneratorState::AwaitingValue) => VMResponse::Value(self.stack_pop()),
+        };
+
+        loop {
+            match generator.resume_with(message) {
+                // If the generator yields, it contains an instruction
+                // for what the VM should do.
+                genawaiter::GeneratorState::Yielded(request) => {
+                    self.observer.observe_generator_request(name, &request);
+
+                    match request {
+                        VMRequest::StackPush(value) => {
+                            self.stack.push(value);
+                            message = VMResponse::Empty;
+                        }
+
+                        VMRequest::StackPop => {
+                            message = VMResponse::Value(self.stack_pop());
+                        }
+
+                        // Generator has requested a force, which means that
+                        // this function prepares the frame stack and yields
+                        // back to the outer VM loop.
+                        VMRequest::ForceValue(value) => {
+                            self.reenqueue_generator(name, span.clone(), generator);
+                            self.enqueue_generator("force", span.clone(), |co| {
+                                value.force_owned_genco(co, span)
+                            });
+                            return Ok(false);
+                        }
+
+                        // Generator has requested a deep-force.
+                        VMRequest::DeepForceValue(value) => {
+                            self.reenqueue_generator(name, span.clone(), generator);
+                            self.enqueue_generator("deep_force", span.clone(), |co| {
+                                value.deep_force(co, span)
+                            });
+                            return Ok(false);
+                        }
+
+                        // Generator has requested a value from the with-stack.
+                        // Logic is similar to `ForceValue`, except with the
+                        // value being taken from that stack.
+                        VMRequest::WithValue(idx) => {
+                            self.reenqueue_generator(name, span.clone(), generator);
+
+                            let value = self.stack[self.with_stack[idx]].clone();
+                            self.enqueue_generator("force", span.clone(), |co| {
+                                value.force_owned_genco(co, span)
+                            });
+
+                            return Ok(false);
+                        }
+
+                        // Generator has requested a value from the *captured*
+                        // with-stack. Logic is same as above, except for the
+                        // value being from that stack.
+                        VMRequest::CapturedWithValue(idx) => {
+                            self.reenqueue_generator(name, span.clone(), generator);
+
+                            let call_frame = self.last_call_frame()
+                                .expect("Tvix bug: generator requested captured with-value, but there is no call frame");
+
+                            let value = call_frame.upvalues.with_stack().unwrap()[idx].clone();
+                            self.enqueue_generator("force", span.clone(), |co| {
+                                value.force_owned_genco(co, span)
+                            });
+
+                            return Ok(false);
+                        }
+
+                        VMRequest::NixEquality(values, ptr_eq) => {
+                            let values = *values;
+                            self.reenqueue_generator(name, span.clone(), generator);
+                            self.enqueue_generator("nix_eq", span.clone(), |co| {
+                                values.0.nix_eq_owned_genco(values.1, co, ptr_eq, span)
+                            });
+                            return Ok(false);
+                        }
+
+                        VMRequest::StringCoerce(val, kind) => {
+                            self.reenqueue_generator(name, span.clone(), generator);
+                            self.enqueue_generator("coerce_to_string", span.clone(), |co| {
+                                val.coerce_to_string(co, kind, span)
+                            });
+                            return Ok(false);
+                        }
+
+                        VMRequest::Call(callable) => {
+                            self.reenqueue_generator(name, span.clone(), generator);
+                            self.call_value(span, None, callable)?;
+                            return Ok(false);
+                        }
+
+                        VMRequest::EnterLambda {
+                            lambda,
+                            upvalues,
+                            light_span,
+                        } => {
+                            self.reenqueue_generator(name, span, generator);
+
+                            self.frames.push(Frame::CallFrame {
+                                span: light_span,
+                                call_frame: CallFrame {
+                                    lambda,
+                                    upvalues,
+                                    ip: CodeIdx(0),
+                                    stack_offset: self.stack.len(),
+                                },
+                            });
+
+                            return Ok(false);
+                        }
+
+                        VMRequest::EmitWarning(warning) => {
+                            self.push_warning(warning);
+                            message = VMResponse::Empty;
+                        }
+
+                        VMRequest::EmitWarningKind(kind) => {
+                            self.emit_warning(kind);
+                            message = VMResponse::Empty;
+                        }
+
+                        VMRequest::ImportCacheLookup(path) => {
+                            if let Some(cached) = self.import_cache.get(path) {
+                                message = VMResponse::Value(cached.clone());
+                            } else {
+                                message = VMResponse::Empty;
+                            }
+                        }
+
+                        VMRequest::ImportCachePut(path, value) => {
+                            self.import_cache.insert(path, value);
+                            message = VMResponse::Empty;
+                        }
+
+                        VMRequest::PathImport(path) => {
+                            let imported = self
+                                .io_handle
+                                .as_ref()
+                                .import_path(&path)
+                                .map_err(|e| ErrorKind::IO {
+                                    path: Some(path),
+                                    error: e.into(),
+                                })
+                                .with_span(&span, self)?;
+
+                            message = VMResponse::Path(imported);
+                        }
+
+                        VMRequest::OpenFile(path) => {
+                            let reader = self
+                                .io_handle
+                                .as_ref()
+                                .open(&path)
+                                .map_err(|e| ErrorKind::IO {
+                                    path: Some(path),
+                                    error: e.into(),
+                                })
+                                .with_span(&span, self)?;
+
+                            message = VMResponse::Reader(reader)
+                        }
+
+                        VMRequest::PathExists(path) => {
+                            let exists = self
+                                .io_handle
+                                .as_ref()
+                                .path_exists(&path)
+                                .map_err(|e| ErrorKind::IO {
+                                    path: Some(path),
+                                    error: e.into(),
+                                })
+                                .map(Value::Bool)
+                                .with_span(&span, self)?;
+
+                            message = VMResponse::Value(exists);
+                        }
+
+                        VMRequest::ReadDir(path) => {
+                            let dir = self
+                                .io_handle
+                                .as_ref()
+                                .read_dir(&path)
+                                .map_err(|e| ErrorKind::IO {
+                                    path: Some(path),
+                                    error: e.into(),
+                                })
+                                .with_span(&span, self)?;
+                            message = VMResponse::Directory(dir);
+                        }
+
+                        VMRequest::Span => {
+                            message = VMResponse::Span(self.reasonable_light_span());
+                        }
+
+                        VMRequest::TryForce(value) => {
+                            self.try_eval_frames.push(frame_id);
+                            self.reenqueue_generator(name, span.clone(), generator);
+
+                            debug_assert!(
+                                self.frames.len() == frame_id + 1,
+                                "generator should be reenqueued with the same frame ID"
+                            );
+
+                            self.enqueue_generator("force", span.clone(), |co| {
+                                value.force_owned_genco(co, span)
+                            });
+                            return Ok(false);
+                        }
+
+                        VMRequest::ToJson(value) => {
+                            self.reenqueue_generator(name, span.clone(), generator);
+                            self.enqueue_generator("to_json", span, |co| {
+                                value.into_contextful_json_generator(co)
+                            });
+                            return Ok(false);
+                        }
+                    }
+                }
+
+                // Generator has completed, and its result value should
+                // be left on the stack.
+                genawaiter::GeneratorState::Complete(result) => {
+                    let value = result.with_span(&span, self)?;
+                    self.stack.push(value);
+                    return Ok(true);
+                }
+            }
+        }
+    }
+}
+
+pub type GenCo = Co<VMRequest, VMResponse>;
+
+// -- Implementation of concrete generator use-cases.
+
+/// Request that the VM place the given value on its stack.
+pub async fn request_stack_push(co: &GenCo, val: Value) {
+    match co.yield_(VMRequest::StackPush(val)).await {
+        VMResponse::Empty => {}
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Request that the VM pop a value from the stack and return it to the
+/// generator.
+pub async fn request_stack_pop(co: &GenCo) -> Value {
+    match co.yield_(VMRequest::StackPop).await {
+        VMResponse::Value(value) => value,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Force any value and return the evaluated result from the VM.
+pub async fn request_force(co: &GenCo, val: Value) -> Value {
+    if let Value::Thunk(_) = val {
+        match co.yield_(VMRequest::ForceValue(val)).await {
+            VMResponse::Value(value) => value,
+            msg => panic!(
+                "Tvix bug: VM responded with incorrect generator message: {}",
+                msg
+            ),
+        }
+    } else {
+        val
+    }
+}
+
+/// Force a value
+pub(crate) async fn request_try_force(co: &GenCo, val: Value) -> Value {
+    if let Value::Thunk(_) = val {
+        match co.yield_(VMRequest::TryForce(val)).await {
+            VMResponse::Value(value) => value,
+            msg => panic!(
+                "Tvix bug: VM responded with incorrect generator message: {}",
+                msg
+            ),
+        }
+    } else {
+        val
+    }
+}
+
+/// Call the given value as a callable. The argument(s) must already be prepared
+/// on the stack.
+pub async fn request_call(co: &GenCo, val: Value) -> Value {
+    let val = request_force(co, val).await;
+    match co.yield_(VMRequest::Call(val)).await {
+        VMResponse::Value(value) => value,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Helper function to call the given value with the provided list of arguments.
+/// This uses the StackPush and Call messages under the hood.
+pub async fn request_call_with<I>(co: &GenCo, mut callable: Value, args: I) -> Value
+where
+    I: IntoIterator<Item = Value>,
+    I::IntoIter: DoubleEndedIterator,
+{
+    let mut num_args = 0_usize;
+    for arg in args.into_iter().rev() {
+        num_args += 1;
+        request_stack_push(co, arg).await;
+    }
+
+    debug_assert!(num_args > 0, "call_with called with an empty list of args");
+
+    while num_args > 0 {
+        callable = request_call(co, callable).await;
+        num_args -= 1;
+    }
+
+    callable
+}
+
+pub async fn request_string_coerce(
+    co: &GenCo,
+    val: Value,
+    kind: CoercionKind,
+) -> Result<NixString, CatchableErrorKind> {
+    match val {
+        Value::String(s) => Ok(s),
+        _ => match co.yield_(VMRequest::StringCoerce(val, kind)).await {
+            VMResponse::Value(Value::Catchable(c)) => Err(*c),
+            VMResponse::Value(value) => Ok(value
+                .to_contextful_str()
+                .expect("coerce_to_string always returns a string")),
+            msg => panic!(
+                "Tvix bug: VM responded with incorrect generator message: {}",
+                msg
+            ),
+        },
+    }
+}
+
+/// Deep-force any value and return the evaluated result from the VM.
+pub async fn request_deep_force(co: &GenCo, val: Value) -> Value {
+    match co.yield_(VMRequest::DeepForceValue(val)).await {
+        VMResponse::Value(value) => value,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Ask the VM to compare two values for equality.
+pub(crate) async fn check_equality(
+    co: &GenCo,
+    a: Value,
+    b: Value,
+    ptr_eq: PointerEquality,
+) -> Result<Result<bool, CatchableErrorKind>, ErrorKind> {
+    match co
+        .yield_(VMRequest::NixEquality(Box::new((a, b)), ptr_eq))
+        .await
+    {
+        VMResponse::Value(Value::Bool(b)) => Ok(Ok(b)),
+        VMResponse::Value(Value::Catchable(cek)) => Ok(Err(*cek)),
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Emit a fully constructed runtime warning.
+pub(crate) async fn emit_warning(co: &GenCo, warning: EvalWarning) {
+    match co.yield_(VMRequest::EmitWarning(warning)).await {
+        VMResponse::Empty => {}
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Emit a runtime warning with the span of the current generator.
+pub async fn emit_warning_kind(co: &GenCo, kind: WarningKind) {
+    match co.yield_(VMRequest::EmitWarningKind(kind)).await {
+        VMResponse::Empty => {}
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Request that the VM enter the given lambda.
+pub(crate) async fn request_enter_lambda(
+    co: &GenCo,
+    lambda: Rc<Lambda>,
+    upvalues: Rc<Upvalues>,
+    light_span: LightSpan,
+) -> Value {
+    let msg = VMRequest::EnterLambda {
+        lambda,
+        upvalues,
+        light_span,
+    };
+
+    match co.yield_(msg).await {
+        VMResponse::Value(value) => value,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Request a lookup in the VM's import cache.
+pub(crate) async fn request_import_cache_lookup(co: &GenCo, path: PathBuf) -> Option<Value> {
+    match co.yield_(VMRequest::ImportCacheLookup(path)).await {
+        VMResponse::Value(value) => Some(value),
+        VMResponse::Empty => None,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Request that the VM populate its input cache for the given path.
+pub(crate) async fn request_import_cache_put(co: &GenCo, path: PathBuf, value: Value) {
+    match co.yield_(VMRequest::ImportCachePut(path, value)).await {
+        VMResponse::Empty => {}
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Request that the VM import the given path.
+pub(crate) async fn request_path_import(co: &GenCo, path: PathBuf) -> PathBuf {
+    match co.yield_(VMRequest::PathImport(path)).await {
+        VMResponse::Path(path) => path,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Request that the VM open a [std::io::Read] for the specified file.
+pub async fn request_open_file(co: &GenCo, path: PathBuf) -> Box<dyn std::io::Read> {
+    match co.yield_(VMRequest::OpenFile(path)).await {
+        VMResponse::Reader(value) => value,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+pub(crate) async fn request_path_exists(co: &GenCo, path: PathBuf) -> Value {
+    match co.yield_(VMRequest::PathExists(path)).await {
+        VMResponse::Value(value) => value,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+pub(crate) async fn request_read_dir(co: &GenCo, path: PathBuf) -> Vec<(bytes::Bytes, FileType)> {
+    match co.yield_(VMRequest::ReadDir(path)).await {
+        VMResponse::Directory(dir) => dir,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+pub(crate) async fn request_span(co: &GenCo) -> LightSpan {
+    match co.yield_(VMRequest::Span).await {
+        VMResponse::Span(span) => span,
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+pub(crate) async fn request_to_json(
+    co: &GenCo,
+    value: Value,
+) -> Result<(serde_json::Value, NixContext), CatchableErrorKind> {
+    match co.yield_(VMRequest::ToJson(value)).await {
+        VMResponse::Value(Value::Json(json_with_ctx)) => Ok(*json_with_ctx),
+        VMResponse::Value(Value::Catchable(cek)) => Err(*cek),
+        msg => panic!(
+            "Tvix bug: VM responded with incorrect generator message: {}",
+            msg
+        ),
+    }
+}
+
+/// Call the given value as if it was an attribute set containing a functor. The
+/// arguments must already be prepared on the stack when a generator frame from
+/// this function is invoked.
+///
+pub(crate) async fn call_functor(co: GenCo, value: Value) -> Result<Value, ErrorKind> {
+    let attrs = value.to_attrs()?;
+
+    match attrs.select("__functor") {
+        None => Err(ErrorKind::NotCallable("set without `__functor_` attribute")),
+        Some(functor) => {
+            // The functor receives the set itself as its first argument and
+            // needs to be called with it.
+            let functor = request_force(&co, functor.clone()).await;
+            let primed = request_call_with(&co, functor, [value]).await;
+            Ok(request_call(&co, primed).await)
+        }
+    }
+}
diff --git a/tvix/eval/src/vm/macros.rs b/tvix/eval/src/vm/macros.rs
new file mode 100644
index 0000000000..d8a09706ab
--- /dev/null
+++ b/tvix/eval/src/vm/macros.rs
@@ -0,0 +1,93 @@
+/// This module provides macros which are used in the implementation
+/// of the VM for the implementation of repetitive operations.
+
+/// This macro simplifies the implementation of arithmetic operations,
+/// correctly handling the behaviour on different pairings of number
+/// types.
+#[macro_export]
+macro_rules! arithmetic_op {
+    ( $self:ident, $op:tt ) => {{ // TODO: remove
+        let b = $self.pop();
+        let a = $self.pop();
+        let result = fallible!($self, arithmetic_op!(&a, &b, $op));
+        $self.push(result);
+    }};
+
+    ( $a:expr, $b:expr, $op:tt ) => {{
+        match ($a, $b) {
+            (Value::Integer(i1), Value::Integer(i2)) => Ok(Value::Integer(i1 $op i2)),
+            (Value::Float(f1), Value::Float(f2)) => Ok(Value::Float(f1 $op f2)),
+            (Value::Integer(i1), Value::Float(f2)) => Ok(Value::Float(*i1 as f64 $op f2)),
+            (Value::Float(f1), Value::Integer(i2)) => Ok(Value::Float(f1 $op *i2 as f64)),
+
+            (v1, v2) => Err(ErrorKind::TypeError {
+                expected: "number (either int or float)",
+                actual: if v1.is_number() {
+                    v2.type_of()
+                } else {
+                    v1.type_of()
+                },
+            }),
+        }
+    }};
+}
+
+/// This macro simplifies the implementation of comparison operations.
+#[macro_export]
+macro_rules! cmp_op {
+    ( $vm:ident, $frame:ident, $span:ident, $op:tt ) => {{
+        lifted_pop! {
+            $vm(b, a) => {
+                async fn compare(a: Value, b: Value, co: GenCo) -> Result<Value, ErrorKind> {
+                    let a = generators::request_force(&co, a).await;
+                    let b = generators::request_force(&co, b).await;
+                    let span = generators::request_span(&co).await;
+                    let ordering = a.nix_cmp_ordering(b, co, span).await?;
+                    match ordering {
+                        Err(cek) => Ok(Value::from(cek)),
+                        Ok(ordering) => Ok(Value::Bool(cmp_op!(@order $op ordering))),
+                    }
+                }
+
+                let gen_span = $frame.current_light_span();
+                $vm.push_call_frame($span, $frame);
+                $vm.enqueue_generator("compare", gen_span, |co| compare(a, b, co));
+                return Ok(false);
+            }
+        }
+    }};
+
+    (@order < $ordering:expr) => {
+        $ordering == Ordering::Less
+    };
+
+    (@order > $ordering:expr) => {
+        $ordering == Ordering::Greater
+    };
+
+    (@order <= $ordering:expr) => {
+        matches!($ordering, Ordering::Equal | Ordering::Less)
+    };
+
+    (@order >= $ordering:expr) => {
+        matches!($ordering, Ordering::Equal | Ordering::Greater)
+    };
+}
+
+#[macro_export]
+macro_rules! lifted_pop {
+    ($vm:ident ($($bind:ident),+) => $body:expr) => {
+        {
+            $(
+                let $bind = $vm.stack_pop();
+            )+
+            $(
+                if $bind.is_catchable() {
+                    $vm.stack.push($bind);
+                    continue;
+                }
+            )+
+            $body
+        }
+    }
+}
diff --git a/tvix/eval/src/vm/mod.rs b/tvix/eval/src/vm/mod.rs
new file mode 100644
index 0000000000..5c244cc3ca
--- /dev/null
+++ b/tvix/eval/src/vm/mod.rs
@@ -0,0 +1,1368 @@
+//! This module implements the abstract/virtual machine that runs Tvix
+//! bytecode.
+//!
+//! The operation of the VM is facilitated by the [`Frame`] type,
+//! which controls the current execution state of the VM and is
+//! processed within the VM's operating loop.
+//!
+//! A [`VM`] is used by instantiating it with an initial [`Frame`],
+//! then triggering its execution and waiting for the VM to return or
+//! yield an error.
+
+pub mod generators;
+mod macros;
+
+use bstr::{BString, ByteSlice, ByteVec};
+use codemap::Span;
+use serde_json::json;
+use std::{cmp::Ordering, collections::HashMap, ops::DerefMut, path::PathBuf, rc::Rc};
+
+use crate::{
+    arithmetic_op,
+    chunk::Chunk,
+    cmp_op,
+    compiler::GlobalsMap,
+    errors::{CatchableErrorKind, Error, ErrorKind, EvalResult},
+    io::EvalIO,
+    lifted_pop,
+    nix_search_path::NixSearchPath,
+    observer::RuntimeObserver,
+    opcode::{CodeIdx, Count, JumpOffset, OpCode, StackIdx, UpvalueIdx},
+    spans::LightSpan,
+    upvalues::Upvalues,
+    value::{
+        Builtin, BuiltinResult, Closure, CoercionKind, Lambda, NixAttrs, NixContext, NixList,
+        PointerEquality, Thunk, Value,
+    },
+    vm::generators::GenCo,
+    warnings::{EvalWarning, WarningKind},
+    NixString, SourceCode,
+};
+
+use generators::{call_functor, Generator, GeneratorState};
+
+use self::generators::{VMRequest, VMResponse};
+
+/// Internal helper trait for taking a span from a variety of types, to make use
+/// of `WithSpan` (defined below) more ergonomic at call sites.
+trait GetSpan {
+    fn get_span(self) -> Span;
+}
+
+impl<'o, IO> GetSpan for &VM<'o, IO> {
+    fn get_span(self) -> Span {
+        self.reasonable_span.span()
+    }
+}
+
+impl GetSpan for &CallFrame {
+    fn get_span(self) -> Span {
+        self.current_span()
+    }
+}
+
+impl GetSpan for &LightSpan {
+    fn get_span(self) -> Span {
+        self.span()
+    }
+}
+
+impl GetSpan for Span {
+    fn get_span(self) -> Span {
+        self
+    }
+}
+
+/// Internal helper trait for ergonomically converting from a `Result<T,
+/// ErrorKind>` to a `Result<T, Error>` using the current span of a call frame,
+/// and chaining the VM's frame stack around it for printing a cause chain.
+trait WithSpan<T, S: GetSpan, IO> {
+    fn with_span(self, top_span: S, vm: &VM<IO>) -> Result<T, Error>;
+}
+
+impl<T, S: GetSpan, IO> WithSpan<T, S, IO> for Result<T, ErrorKind> {
+    fn with_span(self, top_span: S, vm: &VM<IO>) -> Result<T, Error> {
+        match self {
+            Ok(something) => Ok(something),
+            Err(kind) => {
+                let mut error = Error::new(kind, top_span.get_span(), vm.source.clone());
+
+                // Wrap the top-level error in chaining errors for each element
+                // of the frame stack.
+                for frame in vm.frames.iter().rev() {
+                    match frame {
+                        Frame::CallFrame { span, .. } => {
+                            error = Error::new(
+                                ErrorKind::BytecodeError(Box::new(error)),
+                                span.span(),
+                                vm.source.clone(),
+                            );
+                        }
+                        Frame::Generator { name, span, .. } => {
+                            error = Error::new(
+                                ErrorKind::NativeError {
+                                    err: Box::new(error),
+                                    gen_type: name,
+                                },
+                                span.span(),
+                                vm.source.clone(),
+                            );
+                        }
+                    }
+                }
+
+                Err(error)
+            }
+        }
+    }
+}
+
+struct CallFrame {
+    /// The lambda currently being executed.
+    lambda: Rc<Lambda>,
+
+    /// Optional captured upvalues of this frame (if a thunk or
+    /// closure if being evaluated).
+    upvalues: Rc<Upvalues>,
+
+    /// Instruction pointer to the instruction currently being
+    /// executed.
+    ip: CodeIdx,
+
+    /// Stack offset, i.e. the frames "view" into the VM's full stack.
+    stack_offset: usize,
+}
+
+impl CallFrame {
+    /// Retrieve an upvalue from this frame at the given index.
+    fn upvalue(&self, idx: UpvalueIdx) -> &Value {
+        &self.upvalues[idx]
+    }
+
+    /// Borrow the chunk of this frame's lambda.
+    fn chunk(&self) -> &Chunk {
+        &self.lambda.chunk
+    }
+
+    /// Increment this frame's instruction pointer and return the operation that
+    /// the pointer moved past.
+    fn inc_ip(&mut self) -> OpCode {
+        let op = self.chunk()[self.ip];
+        self.ip += 1;
+        op
+    }
+
+    /// Construct an error result from the given ErrorKind and the source span
+    /// of the current instruction.
+    pub fn error<T, IO>(&self, vm: &VM<IO>, kind: ErrorKind) -> Result<T, Error> {
+        Err(kind).with_span(self, vm)
+    }
+
+    /// Returns the current span. This is potentially expensive and should only
+    /// be used when actually constructing an error or warning.
+    pub fn current_span(&self) -> Span {
+        self.chunk().get_span(self.ip - 1)
+    }
+
+    /// Returns the information needed to calculate the current span,
+    /// but without performing that calculation.
+    // TODO: why pub?
+    pub(crate) fn current_light_span(&self) -> LightSpan {
+        LightSpan::new_actual(self.current_span())
+    }
+}
+
+/// A frame represents an execution state of the VM. The VM has a stack of
+/// frames representing the nesting of execution inside of the VM, and operates
+/// on the frame at the top.
+///
+/// When a frame has been fully executed, it is removed from the VM's frame
+/// stack and expected to leave a result [`Value`] on the top of the stack.
+enum Frame {
+    /// CallFrame represents the execution of Tvix bytecode within a thunk,
+    /// function or closure.
+    CallFrame {
+        /// The call frame itself, separated out into another type to pass it
+        /// around easily.
+        call_frame: CallFrame,
+
+        /// Span from which the call frame was launched.
+        span: LightSpan,
+    },
+
+    /// Generator represents a frame that can yield further
+    /// instructions to the VM while its execution is being driven.
+    ///
+    /// A generator is essentially an asynchronous function that can
+    /// be suspended while waiting for the VM to do something (e.g.
+    /// thunk forcing), and resume at the same point.
+    Generator {
+        /// human-readable description of the generator,
+        name: &'static str,
+
+        /// Span from which the generator was launched.
+        span: LightSpan,
+
+        state: GeneratorState,
+
+        /// Generator itself, which can be resumed with `.resume()`.
+        generator: Generator,
+    },
+}
+
+impl Frame {
+    pub fn span(&self) -> LightSpan {
+        match self {
+            Frame::CallFrame { span, .. } | Frame::Generator { span, .. } => span.clone(),
+        }
+    }
+}
+
+#[derive(Default)]
+struct ImportCache(HashMap<PathBuf, Value>);
+
+/// The `ImportCache` holds the `Value` resulting from `import`ing a certain
+/// file, so that the same file doesn't need to be re-evaluated multiple times.
+/// Currently the real path of the imported file (determined using
+/// [`std::fs::canonicalize()`], not to be confused with our
+/// [`crate::value::canon_path()`]) is used to identify the file,
+/// just like C++ Nix does.
+///
+/// Errors while determining the real path are currently just ignored, since we
+/// pass around some fake paths like `/__corepkgs__/fetchurl.nix`.
+///
+/// In the future, we could use something more sophisticated, like file hashes.
+/// However, a consideration is that the eval cache is observable via impurities
+/// like pointer equality and `builtins.trace`.
+impl ImportCache {
+    fn get(&self, path: PathBuf) -> Option<&Value> {
+        let path = match std::fs::canonicalize(path.as_path()).map_err(ErrorKind::from) {
+            Ok(path) => path,
+            Err(_) => path,
+        };
+        self.0.get(&path)
+    }
+
+    fn insert(&mut self, path: PathBuf, value: Value) -> Option<Value> {
+        self.0.insert(
+            match std::fs::canonicalize(path.as_path()).map_err(ErrorKind::from) {
+                Ok(path) => path,
+                Err(_) => path,
+            },
+            value,
+        )
+    }
+}
+
+struct VM<'o, IO> {
+    /// VM's frame stack, representing the execution contexts the VM is working
+    /// through. Elements are usually pushed when functions are called, or
+    /// thunks are being forced.
+    frames: Vec<Frame>,
+
+    /// The VM's top-level value stack. Within this stack, each code-executing
+    /// frame holds a "view" of the stack representing the slice of the
+    /// top-level stack that is relevant to its operation. This is done to avoid
+    /// allocating a new `Vec` for each frame's stack.
+    pub(crate) stack: Vec<Value>,
+
+    /// Stack indices (absolute indexes into `stack`) of attribute
+    /// sets from which variables should be dynamically resolved
+    /// (`with`).
+    with_stack: Vec<usize>,
+
+    /// Runtime warnings collected during evaluation.
+    warnings: Vec<EvalWarning>,
+
+    /// Import cache, mapping absolute file paths to the value that
+    /// they compile to. Note that this reuses thunks, too!
+    // TODO: should probably be based on a file hash
+    pub import_cache: ImportCache,
+
+    /// Data structure holding all source code evaluated in this VM,
+    /// used for pretty error reporting.
+    source: SourceCode,
+
+    /// Parsed Nix search path, which is used to resolve `<...>`
+    /// references.
+    nix_search_path: NixSearchPath,
+
+    /// Implementation of I/O operations used for impure builtins and
+    /// features like `import`.
+    io_handle: IO,
+
+    /// Runtime observer which can print traces of runtime operations.
+    observer: &'o mut dyn RuntimeObserver,
+
+    /// Strong reference to the globals, guaranteeing that they are
+    /// kept alive for the duration of evaluation.
+    ///
+    /// This is important because recursive builtins (specifically
+    /// `import`) hold a weak reference to the builtins, while the
+    /// original strong reference is held by the compiler which does
+    /// not exist anymore at runtime.
+    #[allow(dead_code)]
+    globals: Rc<GlobalsMap>,
+
+    /// A reasonably applicable span that can be used for errors in each
+    /// execution situation.
+    ///
+    /// The VM should update this whenever control flow changes take place (i.e.
+    /// entering or exiting a frame to yield control somewhere).
+    reasonable_span: LightSpan,
+
+    /// This field is responsible for handling `builtins.tryEval`. When that
+    /// builtin is encountered, it sends a special message to the VM which
+    /// pushes the frame index that requested to be informed of catchable
+    /// errors in this field.
+    ///
+    /// The frame stack is then laid out like this:
+    ///
+    /// ```notrust
+    /// β”Œβ”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
+    /// β”‚ 0β”‚ `Result`-producing frame β”‚
+    /// β”œβ”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
+    /// β”‚-1β”‚ `builtins.tryEval` frame β”‚
+    /// β”œβ”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
+    /// β”‚..β”‚ ... other frames ...     β”‚
+    /// β””β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
+    /// ```
+    ///
+    /// Control is yielded to the outer VM loop, which evaluates the next frame
+    /// and returns the result itself to the `builtins.tryEval` frame.
+    try_eval_frames: Vec<usize>,
+}
+
+impl<'o, IO> VM<'o, IO>
+where
+    IO: AsRef<dyn EvalIO> + 'static,
+{
+    pub fn new(
+        nix_search_path: NixSearchPath,
+        io_handle: IO,
+        observer: &'o mut dyn RuntimeObserver,
+        source: SourceCode,
+        globals: Rc<GlobalsMap>,
+        reasonable_span: LightSpan,
+    ) -> Self {
+        Self {
+            nix_search_path,
+            io_handle,
+            observer,
+            globals,
+            reasonable_span,
+            source,
+            frames: vec![],
+            stack: vec![],
+            with_stack: vec![],
+            warnings: vec![],
+            import_cache: Default::default(),
+            try_eval_frames: vec![],
+        }
+    }
+
+    /// Push a call frame onto the frame stack.
+    fn push_call_frame(&mut self, span: LightSpan, call_frame: CallFrame) {
+        self.frames.push(Frame::CallFrame { span, call_frame })
+    }
+
+    /// Run the VM's primary (outer) execution loop, continuing execution based
+    /// on the current frame at the top of the frame stack.
+    fn execute(mut self) -> EvalResult<RuntimeResult> {
+        while let Some(frame) = self.frames.pop() {
+            self.reasonable_span = frame.span();
+            let frame_id = self.frames.len();
+
+            match frame {
+                Frame::CallFrame { call_frame, span } => {
+                    self.observer
+                        .observe_enter_call_frame(0, &call_frame.lambda, frame_id);
+
+                    match self.execute_bytecode(span, call_frame) {
+                        Ok(true) => self.observer.observe_exit_call_frame(frame_id, &self.stack),
+                        Ok(false) => self
+                            .observer
+                            .observe_suspend_call_frame(frame_id, &self.stack),
+
+                        Err(err) => return Err(err),
+                    };
+                }
+
+                // Handle generator frames, which can request thunk forcing
+                // during their execution.
+                Frame::Generator {
+                    name,
+                    span,
+                    state,
+                    generator,
+                } => {
+                    self.observer
+                        .observe_enter_generator(frame_id, name, &self.stack);
+
+                    match self.run_generator(name, span, frame_id, state, generator, None) {
+                        Ok(true) => {
+                            self.observer
+                                .observe_exit_generator(frame_id, name, &self.stack)
+                        }
+                        Ok(false) => {
+                            self.observer
+                                .observe_suspend_generator(frame_id, name, &self.stack)
+                        }
+
+                        Err(err) => return Err(err),
+                    };
+                }
+            }
+        }
+
+        // Once no more frames are present, return the stack's top value as the
+        // result.
+        let value = self
+            .stack
+            .pop()
+            .expect("tvix bug: runtime stack empty after execution");
+        Ok(RuntimeResult {
+            value,
+            warnings: self.warnings,
+        })
+    }
+
+    /// Run the VM's inner execution loop, processing Tvix bytecode from a
+    /// chunk. This function returns if:
+    ///
+    /// 1. The code has run to the end, and has left a value on the top of the
+    ///    stack. In this case, the frame is not returned to the frame stack.
+    ///
+    /// 2. The code encounters a generator, in which case the frame in its
+    /// current state is pushed back on the stack, and the generator is left on
+    /// top of it for the outer loop to execute.
+    ///
+    /// 3. An error is encountered.
+    ///
+    /// This function *must* ensure that it leaves the frame stack in the
+    /// correct order, especially when re-enqueuing a frame to execute.
+    ///
+    /// The return value indicates whether the bytecode has been executed to
+    /// completion, or whether it has been suspended in favour of a generator.
+    fn execute_bytecode(&mut self, span: LightSpan, mut frame: CallFrame) -> EvalResult<bool> {
+        loop {
+            let op = frame.inc_ip();
+            self.observer.observe_execute_op(frame.ip, &op, &self.stack);
+
+            match op {
+                OpCode::OpThunkSuspended(idx) | OpCode::OpThunkClosure(idx) => {
+                    let blueprint = match &frame.chunk()[idx] {
+                        Value::Blueprint(lambda) => lambda.clone(),
+                        _ => panic!("compiler bug: non-blueprint in blueprint slot"),
+                    };
+
+                    let upvalue_count = blueprint.upvalue_count;
+                    let thunk = if matches!(op, OpCode::OpThunkClosure(_)) {
+                        debug_assert!(
+                            upvalue_count > 0,
+                            "OpThunkClosure should not be called for plain lambdas"
+                        );
+                        Thunk::new_closure(blueprint)
+                    } else {
+                        Thunk::new_suspended(blueprint, frame.current_light_span())
+                    };
+                    let upvalues = thunk.upvalues_mut();
+                    self.stack.push(Value::Thunk(thunk.clone()));
+
+                    // From this point on we internally mutate the
+                    // upvalues. The closure (if `is_closure`) is
+                    // already in its stack slot, which means that it
+                    // can capture itself as an upvalue for
+                    // self-recursion.
+                    self.populate_upvalues(&mut frame, upvalue_count, upvalues)?;
+                }
+
+                OpCode::OpForce => {
+                    if let Some(Value::Thunk(_)) = self.stack.last() {
+                        let thunk = match self.stack_pop() {
+                            Value::Thunk(t) => t,
+                            _ => unreachable!(),
+                        };
+
+                        let gen_span = frame.current_light_span();
+
+                        self.push_call_frame(span, frame);
+                        self.enqueue_generator("force", gen_span.clone(), |co| {
+                            Thunk::force(thunk, co, gen_span)
+                        });
+
+                        return Ok(false);
+                    }
+                }
+
+                OpCode::OpGetUpvalue(upv_idx) => {
+                    let value = frame.upvalue(upv_idx).clone();
+                    self.stack.push(value);
+                }
+
+                // Discard the current frame.
+                OpCode::OpReturn => {
+                    // TODO(amjoseph): I think this should assert `==` rather
+                    // than `<=` but it fails with the stricter condition.
+                    debug_assert!(self.stack.len() - 1 <= frame.stack_offset);
+                    return Ok(true);
+                }
+
+                OpCode::OpConstant(idx) => {
+                    let c = frame.chunk()[idx].clone();
+                    self.stack.push(c);
+                }
+
+                OpCode::OpCall => {
+                    let callable = self.stack_pop();
+                    self.call_value(frame.current_light_span(), Some((span, frame)), callable)?;
+
+                    // exit this loop and let the outer loop enter the new call
+                    return Ok(true);
+                }
+
+                // Remove the given number of elements from the stack,
+                // but retain the top value.
+                OpCode::OpCloseScope(Count(count)) => {
+                    // Immediately move the top value into the right
+                    // position.
+                    let target_idx = self.stack.len() - 1 - count;
+                    self.stack[target_idx] = self.stack_pop();
+
+                    // Then drop the remaining values.
+                    for _ in 0..(count - 1) {
+                        self.stack.pop();
+                    }
+                }
+
+                OpCode::OpClosure(idx) => {
+                    let blueprint = match &frame.chunk()[idx] {
+                        Value::Blueprint(lambda) => lambda.clone(),
+                        _ => panic!("compiler bug: non-blueprint in blueprint slot"),
+                    };
+
+                    let upvalue_count = blueprint.upvalue_count;
+                    debug_assert!(
+                        upvalue_count > 0,
+                        "OpClosure should not be called for plain lambdas"
+                    );
+
+                    let mut upvalues = Upvalues::with_capacity(blueprint.upvalue_count);
+                    self.populate_upvalues(&mut frame, upvalue_count, &mut upvalues)?;
+                    self.stack
+                        .push(Value::Closure(Rc::new(Closure::new_with_upvalues(
+                            Rc::new(upvalues),
+                            blueprint,
+                        ))));
+                }
+
+                OpCode::OpAttrsSelect => lifted_pop! {
+                    self(key, attrs) => {
+                        let key = key.to_str().with_span(&frame, self)?;
+                        let attrs = attrs.to_attrs().with_span(&frame, self)?;
+
+                        match attrs.select(&key) {
+                            Some(value) => self.stack.push(value.clone()),
+
+                            None => {
+                                return frame.error(
+                                    self,
+                                    ErrorKind::AttributeNotFound {
+                                        name: key.to_str_lossy().into_owned()
+                                    },
+                                );
+                            }
+                        }
+                    }
+                },
+
+                OpCode::OpJumpIfFalse(JumpOffset(offset)) => {
+                    debug_assert!(offset != 0);
+                    if !self.stack_peek(0).as_bool().with_span(&frame, self)? {
+                        frame.ip += offset;
+                    }
+                }
+
+                OpCode::OpJumpIfCatchable(JumpOffset(offset)) => {
+                    debug_assert!(offset != 0);
+                    if self.stack_peek(0).is_catchable() {
+                        frame.ip += offset;
+                    }
+                }
+
+                OpCode::OpJumpIfNoFinaliseRequest(JumpOffset(offset)) => {
+                    debug_assert!(offset != 0);
+                    match self.stack_peek(0) {
+                        Value::FinaliseRequest(finalise) => {
+                            if !finalise {
+                                frame.ip += offset;
+                            }
+                        },
+                        val => panic!("Tvix bug: OpJumIfNoFinaliseRequest: expected FinaliseRequest, but got {}", val.type_of()),
+                    }
+                }
+
+                OpCode::OpPop => {
+                    self.stack.pop();
+                }
+
+                OpCode::OpAttrsTrySelect => {
+                    let key = self.stack_pop().to_str().with_span(&frame, self)?;
+                    let value = match self.stack_pop() {
+                        Value::Attrs(attrs) => match attrs.select(&key) {
+                            Some(value) => value.clone(),
+                            None => Value::AttrNotFound,
+                        },
+
+                        _ => Value::AttrNotFound,
+                    };
+
+                    self.stack.push(value);
+                }
+
+                OpCode::OpGetLocal(StackIdx(local_idx)) => {
+                    let idx = frame.stack_offset + local_idx;
+                    self.stack.push(self.stack[idx].clone());
+                }
+
+                OpCode::OpJumpIfNotFound(JumpOffset(offset)) => {
+                    debug_assert!(offset != 0);
+                    if matches!(self.stack_peek(0), Value::AttrNotFound) {
+                        self.stack_pop();
+                        frame.ip += offset;
+                    }
+                }
+
+                OpCode::OpJump(JumpOffset(offset)) => {
+                    debug_assert!(offset != 0);
+                    frame.ip += offset;
+                }
+
+                OpCode::OpEqual => lifted_pop! {
+                    self(b, a) => {
+                        let gen_span = frame.current_light_span();
+                        self.push_call_frame(span, frame);
+                        self.enqueue_generator("nix_eq", gen_span.clone(), |co| {
+                            a.nix_eq_owned_genco(b, co, PointerEquality::ForbidAll, gen_span)
+                        });
+                        return Ok(false);
+                    }
+                },
+
+                // These assertion operations error out if the stack
+                // top is not of the expected type. This is necessary
+                // to implement some specific behaviours of Nix
+                // exactly.
+                OpCode::OpAssertBool => {
+                    let val = self.stack_peek(0);
+                    // TODO(edef): propagate this into is_bool, since bottom values *are* values of any type
+                    if !val.is_catchable() && !val.is_bool() {
+                        return frame.error(
+                            self,
+                            ErrorKind::TypeError {
+                                expected: "bool",
+                                actual: val.type_of(),
+                            },
+                        );
+                    }
+                }
+
+                OpCode::OpAssertAttrs => {
+                    let val = self.stack_peek(0);
+                    // TODO(edef): propagate this into is_attrs, since bottom values *are* values of any type
+                    if !val.is_catchable() && !val.is_attrs() {
+                        return frame.error(
+                            self,
+                            ErrorKind::TypeError {
+                                expected: "set",
+                                actual: val.type_of(),
+                            },
+                        );
+                    }
+                }
+
+                OpCode::OpAttrs(Count(count)) => self.run_attrset(&frame, count)?,
+
+                OpCode::OpAttrsUpdate => lifted_pop! {
+                    self(rhs, lhs) => {
+                        let rhs = rhs.to_attrs().with_span(&frame, self)?;
+                        let lhs = lhs.to_attrs().with_span(&frame, self)?;
+                        self.stack.push(Value::attrs(lhs.update(*rhs)))
+                    }
+                },
+
+                OpCode::OpInvert => lifted_pop! {
+                    self(v) => {
+                        let v = v.as_bool().with_span(&frame, self)?;
+                        self.stack.push(Value::Bool(!v));
+                    }
+                },
+
+                OpCode::OpList(Count(count)) => {
+                    let list =
+                        NixList::construct(count, self.stack.split_off(self.stack.len() - count));
+
+                    self.stack.push(Value::List(list));
+                }
+
+                OpCode::OpJumpIfTrue(JumpOffset(offset)) => {
+                    debug_assert!(offset != 0);
+                    if self.stack_peek(0).as_bool().with_span(&frame, self)? {
+                        frame.ip += offset;
+                    }
+                }
+
+                OpCode::OpHasAttr => lifted_pop! {
+                    self(key, attrs) => {
+                        let key = key.to_str().with_span(&frame, self)?;
+                        let result = match attrs {
+                            Value::Attrs(attrs) => attrs.contains(&key),
+
+                            // Nix allows use of `?` on non-set types, but
+                            // always returns false in those cases.
+                            _ => false,
+                        };
+
+                        self.stack.push(Value::Bool(result));
+                    }
+                },
+
+                OpCode::OpConcat => lifted_pop! {
+                    self(rhs, lhs) => {
+                        let rhs = rhs.to_list().with_span(&frame, self)?.into_inner();
+                        let lhs = lhs.to_list().with_span(&frame, self)?.into_inner();
+                        self.stack.push(Value::List(NixList::from(lhs + rhs)))
+                    }
+                },
+
+                OpCode::OpResolveWith => {
+                    let ident = self.stack_pop().to_str().with_span(&frame, self)?;
+
+                    // Re-enqueue this frame.
+                    let op_span = frame.current_light_span();
+                    self.push_call_frame(span, frame);
+
+                    // Construct a generator frame doing the lookup in constant
+                    // stack space.
+                    let with_stack_len = self.with_stack.len();
+                    let closed_with_stack_len = self
+                        .last_call_frame()
+                        .map(|frame| frame.upvalues.with_stack_len())
+                        .unwrap_or(0);
+
+                    self.enqueue_generator("resolve_with", op_span, |co| {
+                        resolve_with(
+                            co,
+                            ident.as_bstr().to_owned(),
+                            with_stack_len,
+                            closed_with_stack_len,
+                        )
+                    });
+
+                    return Ok(false);
+                }
+
+                OpCode::OpFinalise(StackIdx(idx)) => match &self.stack[frame.stack_offset + idx] {
+                    Value::Closure(_) => panic!("attempted to finalise a closure"),
+                    Value::Thunk(thunk) => thunk.finalise(&self.stack[frame.stack_offset..]),
+                    _ => panic!("attempted to finalise a non-thunk"),
+                },
+
+                OpCode::OpCoerceToString(kind) => {
+                    let value = self.stack_pop();
+                    let gen_span = frame.current_light_span();
+                    self.push_call_frame(span, frame);
+
+                    self.enqueue_generator("coerce_to_string", gen_span.clone(), |co| {
+                        value.coerce_to_string(co, kind, gen_span)
+                    });
+
+                    return Ok(false);
+                }
+
+                OpCode::OpInterpolate(Count(count)) => self.run_interpolate(&frame, count)?,
+
+                OpCode::OpValidateClosedFormals => {
+                    let formals = frame.lambda.formals.as_ref().expect(
+                        "OpValidateClosedFormals called within the frame of a lambda without formals",
+                    );
+
+                    let peeked = self.stack_peek(0);
+                    if peeked.is_catchable() {
+                        continue;
+                    }
+
+                    let args = peeked.to_attrs().with_span(&frame, self)?;
+                    for arg in args.keys() {
+                        if !formals.contains(arg) {
+                            return frame.error(
+                                self,
+                                ErrorKind::UnexpectedArgument {
+                                    arg: arg.clone(),
+                                    formals_span: formals.span,
+                                },
+                            );
+                        }
+                    }
+                }
+
+                OpCode::OpAdd => lifted_pop! {
+                    self(b, a) => {
+                        let gen_span = frame.current_light_span();
+                        self.push_call_frame(span, frame);
+
+                        // OpAdd can add not just numbers, but also string-like
+                        // things, which requires more VM logic. This operation is
+                        // evaluated in a generator frame.
+                        self.enqueue_generator("add_values", gen_span, |co| add_values(co, a, b));
+                        return Ok(false);
+                    }
+                },
+
+                OpCode::OpSub => lifted_pop! {
+                    self(b, a) => {
+                        let result = arithmetic_op!(&a, &b, -).with_span(&frame, self)?;
+                        self.stack.push(result);
+                    }
+                },
+
+                OpCode::OpMul => lifted_pop! {
+                    self(b, a) => {
+                        let result = arithmetic_op!(&a, &b, *).with_span(&frame, self)?;
+                        self.stack.push(result);
+                    }
+                },
+
+                OpCode::OpDiv => lifted_pop! {
+                    self(b, a) => {
+                        match b {
+                            Value::Integer(0) => return frame.error(self, ErrorKind::DivisionByZero),
+                            Value::Float(b) if b == 0.0_f64 => {
+                                return frame.error(self, ErrorKind::DivisionByZero)
+                            }
+                            _ => {}
+                        };
+
+                        let result = arithmetic_op!(&a, &b, /).with_span(&frame, self)?;
+                        self.stack.push(result);
+                    }
+                },
+
+                OpCode::OpNegate => match self.stack_pop() {
+                    Value::Integer(i) => self.stack.push(Value::Integer(-i)),
+                    Value::Float(f) => self.stack.push(Value::Float(-f)),
+                    Value::Catchable(cex) => self.stack.push(Value::Catchable(cex)),
+                    v => {
+                        return frame.error(
+                            self,
+                            ErrorKind::TypeError {
+                                expected: "number (either int or float)",
+                                actual: v.type_of(),
+                            },
+                        );
+                    }
+                },
+
+                OpCode::OpLess => cmp_op!(self, frame, span, <),
+                OpCode::OpLessOrEq => cmp_op!(self, frame, span, <=),
+                OpCode::OpMore => cmp_op!(self, frame, span, >),
+                OpCode::OpMoreOrEq => cmp_op!(self, frame, span, >=),
+
+                OpCode::OpFindFile => match self.stack_pop() {
+                    Value::UnresolvedPath(path) => {
+                        let resolved = self
+                            .nix_search_path
+                            .resolve(&self.io_handle, *path)
+                            .with_span(&frame, self)?;
+                        self.stack.push(resolved.into());
+                    }
+
+                    _ => panic!("tvix compiler bug: OpFindFile called on non-UnresolvedPath"),
+                },
+
+                OpCode::OpResolveHomePath => match self.stack_pop() {
+                    Value::UnresolvedPath(path) => {
+                        match dirs::home_dir() {
+                            None => {
+                                return frame.error(
+                                    self,
+                                    ErrorKind::RelativePathResolution(
+                                        "failed to determine home directory".into(),
+                                    ),
+                                );
+                            }
+                            Some(mut buf) => {
+                                buf.push(*path);
+                                self.stack.push(buf.into());
+                            }
+                        };
+                    }
+
+                    _ => {
+                        panic!("tvix compiler bug: OpResolveHomePath called on non-UnresolvedPath")
+                    }
+                },
+
+                OpCode::OpPushWith(StackIdx(idx)) => self.with_stack.push(frame.stack_offset + idx),
+
+                OpCode::OpPopWith => {
+                    self.with_stack.pop();
+                }
+
+                OpCode::OpAssertFail => {
+                    self.stack
+                        .push(Value::from(CatchableErrorKind::AssertionFailed));
+                }
+
+                // Data-carrying operands should never be executed,
+                // that is a critical error in the VM/compiler.
+                OpCode::DataStackIdx(_)
+                | OpCode::DataDeferredLocal(_)
+                | OpCode::DataUpvalueIdx(_)
+                | OpCode::DataCaptureWith => {
+                    panic!("Tvix bug: attempted to execute data-carrying operand")
+                }
+            }
+        }
+    }
+}
+
+/// Implementation of helper functions for the runtime logic above.
+impl<'o, IO> VM<'o, IO>
+where
+    IO: AsRef<dyn EvalIO> + 'static,
+{
+    pub(crate) fn stack_pop(&mut self) -> Value {
+        self.stack.pop().expect("runtime stack empty")
+    }
+
+    fn stack_peek(&self, offset: usize) -> &Value {
+        &self.stack[self.stack.len() - 1 - offset]
+    }
+
+    fn run_attrset(&mut self, frame: &CallFrame, count: usize) -> EvalResult<()> {
+        let attrs = NixAttrs::construct(count, self.stack.split_off(self.stack.len() - count * 2))
+            .with_span(frame, self)?
+            .map(Value::attrs)
+            .into();
+
+        self.stack.push(attrs);
+        Ok(())
+    }
+
+    /// Access the last call frame present in the frame stack.
+    fn last_call_frame(&self) -> Option<&CallFrame> {
+        for frame in self.frames.iter().rev() {
+            if let Frame::CallFrame { call_frame, .. } = frame {
+                return Some(call_frame);
+            }
+        }
+
+        None
+    }
+
+    /// Push an already constructed warning.
+    pub fn push_warning(&mut self, warning: EvalWarning) {
+        self.warnings.push(warning);
+    }
+
+    /// Emit a warning with the given WarningKind and the source span
+    /// of the current instruction.
+    pub fn emit_warning(&mut self, kind: WarningKind) {
+        self.push_warning(EvalWarning {
+            kind,
+            span: self.get_span(),
+        });
+    }
+
+    /// Interpolate string fragments by popping the specified number of
+    /// fragments of the stack, evaluating them to strings, and pushing
+    /// the concatenated result string back on the stack.
+    fn run_interpolate(&mut self, frame: &CallFrame, count: usize) -> EvalResult<()> {
+        let mut out = BString::default();
+        // Interpolation propagates the context and union them.
+        let mut context: NixContext = NixContext::new();
+
+        for i in 0..count {
+            let val = self.stack_pop();
+            if val.is_catchable() {
+                for _ in (i + 1)..count {
+                    self.stack.pop();
+                }
+                self.stack.push(val);
+                return Ok(());
+            }
+            let mut nix_string = val.to_contextful_str().with_span(frame, self)?;
+            out.push_str(nix_string.as_bstr());
+            if let Some(nix_string_ctx) = nix_string.context_mut() {
+                context = context.join(nix_string_ctx);
+            }
+        }
+
+        self.stack
+            .push(Value::String(NixString::new_context_from(context, out)));
+        Ok(())
+    }
+
+    /// Returns a reasonable light span for the current situation that the VM is
+    /// in.
+    pub fn reasonable_light_span(&self) -> LightSpan {
+        self.reasonable_span.clone()
+    }
+
+    /// Apply an argument from the stack to a builtin, and attempt to call it.
+    ///
+    /// All calls are tail-calls in Tvix, as every function application is a
+    /// separate thunk and OpCall is thus the last result in the thunk.
+    ///
+    /// Due to this, once control flow exits this function, the generator will
+    /// automatically be run by the VM.
+    fn call_builtin(&mut self, span: LightSpan, mut builtin: Builtin) -> EvalResult<()> {
+        let builtin_name = builtin.name();
+        self.observer.observe_enter_builtin(builtin_name);
+
+        builtin.apply_arg(self.stack_pop());
+
+        match builtin.call() {
+            // Partially applied builtin is just pushed back on the stack.
+            BuiltinResult::Partial(partial) => self.stack.push(Value::Builtin(partial)),
+
+            // Builtin is fully applied and the generator needs to be run by the VM.
+            BuiltinResult::Called(name, generator) => self.frames.push(Frame::Generator {
+                generator,
+                span,
+                name,
+                state: GeneratorState::Running,
+            }),
+        }
+
+        Ok(())
+    }
+
+    fn call_value(
+        &mut self,
+        span: LightSpan,
+        parent: Option<(LightSpan, CallFrame)>,
+        callable: Value,
+    ) -> EvalResult<()> {
+        match callable {
+            Value::Builtin(builtin) => self.call_builtin(span, builtin),
+            Value::Thunk(thunk) => self.call_value(span, parent, thunk.value().clone()),
+
+            Value::Closure(closure) => {
+                let lambda = closure.lambda();
+                self.observer.observe_tail_call(self.frames.len(), &lambda);
+
+                // The stack offset is always `stack.len() - arg_count`, and
+                // since this branch handles native Nix functions (which always
+                // take only a single argument and are curried), the offset is
+                // `stack_len - 1`.
+                let stack_offset = self.stack.len() - 1;
+
+                // Reenqueue the parent frame, which should only have
+                // `OpReturn` left. Not throwing it away leads to more
+                // useful error traces.
+                if let Some((parent_span, parent_frame)) = parent {
+                    self.push_call_frame(parent_span, parent_frame);
+                }
+
+                self.push_call_frame(
+                    span,
+                    CallFrame {
+                        lambda,
+                        upvalues: closure.upvalues(),
+                        ip: CodeIdx(0),
+                        stack_offset,
+                    },
+                );
+
+                Ok(())
+            }
+
+            // Attribute sets with a __functor attribute are callable.
+            val @ Value::Attrs(_) => {
+                if let Some((parent_span, parent_frame)) = parent {
+                    self.push_call_frame(parent_span, parent_frame);
+                }
+
+                self.enqueue_generator("__functor call", span, |co| call_functor(co, val));
+                Ok(())
+            }
+
+            val @ Value::Catchable(_) => {
+                // the argument that we tried to apply a catchable to
+                self.stack.pop();
+                // applying a `throw` to anything is still a `throw`, so we just
+                // push it back on the stack.
+                self.stack.push(val);
+                Ok(())
+            }
+
+            v => Err(ErrorKind::NotCallable(v.type_of())).with_span(&span, self),
+        }
+    }
+
+    /// Populate the upvalue fields of a thunk or closure under construction.
+    fn populate_upvalues(
+        &mut self,
+        frame: &mut CallFrame,
+        count: usize,
+        mut upvalues: impl DerefMut<Target = Upvalues>,
+    ) -> EvalResult<()> {
+        for _ in 0..count {
+            match frame.inc_ip() {
+                OpCode::DataStackIdx(StackIdx(stack_idx)) => {
+                    let idx = frame.stack_offset + stack_idx;
+
+                    let val = match self.stack.get(idx) {
+                        Some(val) => val.clone(),
+                        None => {
+                            return frame.error(
+                                self,
+                                ErrorKind::TvixBug {
+                                    msg: "upvalue to be captured was missing on stack",
+                                    metadata: Some(Rc::new(json!({
+                                        "ip": format!("{:#x}", frame.ip.0 - 1),
+                                        "stack_idx(relative)": stack_idx,
+                                        "stack_idx(absolute)": idx,
+                                    }))),
+                                },
+                            );
+                        }
+                    };
+
+                    upvalues.deref_mut().push(val);
+                }
+
+                OpCode::DataUpvalueIdx(upv_idx) => {
+                    upvalues.deref_mut().push(frame.upvalue(upv_idx).clone());
+                }
+
+                OpCode::DataDeferredLocal(idx) => {
+                    upvalues.deref_mut().push(Value::DeferredUpvalue(idx));
+                }
+
+                OpCode::DataCaptureWith => {
+                    // Start the captured with_stack off of the
+                    // current call frame's captured with_stack, ...
+                    let mut captured_with_stack = frame
+                        .upvalues
+                        .with_stack()
+                        .cloned()
+                        // ... or make an empty one if there isn't one already.
+                        .unwrap_or_else(|| Vec::with_capacity(self.with_stack.len()));
+
+                    for idx in &self.with_stack {
+                        captured_with_stack.push(self.stack[*idx].clone());
+                    }
+
+                    upvalues.deref_mut().set_with_stack(captured_with_stack);
+                }
+
+                _ => panic!("compiler error: missing closure operand"),
+            }
+        }
+
+        Ok(())
+    }
+}
+
+// TODO(amjoseph): de-asyncify this
+/// Resolve a dynamically bound identifier (through `with`) by looking
+/// for matching values in the with-stacks carried at runtime.
+async fn resolve_with(
+    co: GenCo,
+    ident: BString,
+    vm_with_len: usize,
+    upvalue_with_len: usize,
+) -> Result<Value, ErrorKind> {
+    /// Fetch and force a value on the with-stack from the VM.
+    async fn fetch_forced_with(co: &GenCo, idx: usize) -> Value {
+        match co.yield_(VMRequest::WithValue(idx)).await {
+            VMResponse::Value(value) => value,
+            msg => panic!(
+                "Tvix bug: VM responded with incorrect generator message: {}",
+                msg
+            ),
+        }
+    }
+
+    /// Fetch and force a value on the *captured* with-stack from the VM.
+    async fn fetch_captured_with(co: &GenCo, idx: usize) -> Value {
+        match co.yield_(VMRequest::CapturedWithValue(idx)).await {
+            VMResponse::Value(value) => value,
+            msg => panic!(
+                "Tvix bug: VM responded with incorrect generator message: {}",
+                msg
+            ),
+        }
+    }
+
+    for with_stack_idx in (0..vm_with_len).rev() {
+        // TODO(tazjin): is this branch still live with the current with-thunking?
+        let with = fetch_forced_with(&co, with_stack_idx).await;
+
+        if with.is_catchable() {
+            return Ok(with);
+        }
+
+        match with.to_attrs()?.select(&ident) {
+            None => continue,
+            Some(val) => return Ok(val.clone()),
+        }
+    }
+
+    for upvalue_with_idx in (0..upvalue_with_len).rev() {
+        let with = fetch_captured_with(&co, upvalue_with_idx).await;
+
+        if with.is_catchable() {
+            return Ok(with);
+        }
+
+        match with.to_attrs()?.select(&ident) {
+            None => continue,
+            Some(val) => return Ok(val.clone()),
+        }
+    }
+
+    Err(ErrorKind::UnknownDynamicVariable(ident.to_string()))
+}
+
+// TODO(amjoseph): de-asyncify this
+async fn add_values(co: GenCo, a: Value, b: Value) -> Result<Value, ErrorKind> {
+    // What we try to do is solely determined by the type of the first value!
+    let result = match (a, b) {
+        (Value::Path(p), v) => {
+            let mut path = p.into_os_string();
+            match generators::request_string_coerce(
+                &co,
+                v,
+                CoercionKind {
+                    strong: false,
+
+                    // Concatenating a Path with something else results in a
+                    // Path, so we don't need to import any paths (paths
+                    // imported by Nix always exist as a string, unless
+                    // converted by the user). In C++ Nix they even may not
+                    // contain any string context, the resulting error of such a
+                    // case can not be replicated by us.
+                    import_paths: false,
+                    // FIXME(raitobezarius): per https://b.tvl.fyi/issues/364, this is a usecase
+                    // for having a `reject_context: true` option here. This didn't occur yet in
+                    // nixpkgs during my evaluations, therefore, I skipped it.
+                },
+            )
+            .await
+            {
+                Ok(vs) => {
+                    path.push(vs.to_os_str()?);
+                    crate::value::canon_path(PathBuf::from(path)).into()
+                }
+                Err(c) => Value::Catchable(Box::new(c)),
+            }
+        }
+        (Value::String(s1), Value::String(s2)) => Value::String(s1.concat(&s2)),
+        (Value::String(s1), v) => generators::request_string_coerce(
+            &co,
+            v,
+            CoercionKind {
+                strong: false,
+                // Behaves the same as string interpolation
+                import_paths: true,
+            },
+        )
+        .await
+        .map(|s2| Value::String(s1.concat(&s2)))
+        .into(),
+        (a @ Value::Integer(_), b) | (a @ Value::Float(_), b) => arithmetic_op!(&a, &b, +)?,
+        (a, b) => {
+            let r1 = generators::request_string_coerce(
+                &co,
+                a,
+                CoercionKind {
+                    strong: false,
+                    import_paths: false,
+                },
+            )
+            .await;
+            let r2 = generators::request_string_coerce(
+                &co,
+                b,
+                CoercionKind {
+                    strong: false,
+                    import_paths: false,
+                },
+            )
+            .await;
+            match (r1, r2) {
+                (Ok(s1), Ok(s2)) => Value::String(s1.concat(&s2)),
+                (Err(c), _) => return Ok(Value::from(c)),
+                (_, Err(c)) => return Ok(Value::from(c)),
+            }
+        }
+    };
+
+    Ok(result)
+}
+
+/// The result of a VM's runtime evaluation.
+pub struct RuntimeResult {
+    pub value: Value,
+    pub warnings: Vec<EvalWarning>,
+}
+
+// TODO(amjoseph): de-asyncify this
+/// Generator that retrieves the final value from the stack, and deep-forces it
+/// before returning.
+async fn final_deep_force(co: GenCo) -> Result<Value, ErrorKind> {
+    let value = generators::request_stack_pop(&co).await;
+    Ok(generators::request_deep_force(&co, value).await)
+}
+
+pub fn run_lambda<IO>(
+    nix_search_path: NixSearchPath,
+    io_handle: IO,
+    observer: &mut dyn RuntimeObserver,
+    source: SourceCode,
+    globals: Rc<GlobalsMap>,
+    lambda: Rc<Lambda>,
+    strict: bool,
+) -> EvalResult<RuntimeResult>
+where
+    IO: AsRef<dyn EvalIO> + 'static,
+{
+    // Retain the top-level span of the expression in this lambda, as
+    // synthetic "calls" in deep_force will otherwise not have a span
+    // to fall back to.
+    //
+    // We exploit the fact that the compiler emits a final instruction
+    // with the span of the entire file for top-level expressions.
+    let root_span = lambda.chunk.get_span(CodeIdx(lambda.chunk.code.len() - 1));
+
+    let mut vm = VM::new(
+        nix_search_path,
+        io_handle,
+        observer,
+        source,
+        globals,
+        root_span.into(),
+    );
+
+    // When evaluating strictly, synthesise a frame that will instruct
+    // the VM to deep-force the final value before returning it.
+    if strict {
+        vm.enqueue_generator("final_deep_force", root_span.into(), final_deep_force);
+    }
+
+    vm.frames.push(Frame::CallFrame {
+        span: root_span.into(),
+        call_frame: CallFrame {
+            lambda,
+            upvalues: Rc::new(Upvalues::with_capacity(0)),
+            ip: CodeIdx(0),
+            stack_offset: 0,
+        },
+    });
+
+    vm.execute()
+}
diff --git a/tvix/eval/src/warnings.rs b/tvix/eval/src/warnings.rs
new file mode 100644
index 0000000000..f537aa913e
--- /dev/null
+++ b/tvix/eval/src/warnings.rs
@@ -0,0 +1,152 @@
+//! Implements warnings that are emitted in cases where code passed to
+//! Tvix exhibits problems that the user could address.
+
+use codemap_diagnostic::{ColorConfig, Diagnostic, Emitter, Level, SpanLabel, SpanStyle};
+
+use crate::SourceCode;
+
+#[derive(Debug)]
+pub enum WarningKind {
+    DeprecatedLiteralURL,
+    UselessInherit,
+    UnusedBinding,
+    ShadowedGlobal(&'static str),
+    DeprecatedLegacyLet,
+    InvalidNixPath(String),
+    UselessBoolOperation(&'static str),
+    DeadCode,
+    EmptyInherit,
+    EmptyLet,
+    ShadowedOutput(String),
+    SRIHashWrongPadding,
+
+    /// Tvix internal warning for features triggered by users that are
+    /// not actually implemented yet, but do not cause runtime failures.
+    NotImplemented(&'static str),
+}
+
+#[derive(Debug)]
+pub struct EvalWarning {
+    pub kind: WarningKind,
+    pub span: codemap::Span,
+}
+
+impl EvalWarning {
+    /// Render a fancy, human-readable output of this warning and
+    /// return it as a String. Note that this version of the output
+    /// does not include any colours or font styles.
+    pub fn fancy_format_str(&self, source: &SourceCode) -> String {
+        let mut out = vec![];
+        Emitter::vec(&mut out, Some(&*source.codemap())).emit(&[self.diagnostic(source)]);
+        String::from_utf8_lossy(&out).to_string()
+    }
+
+    /// Render a fancy, human-readable output of this warning and
+    /// print it to stderr. If rendered in a terminal that supports
+    /// colours and font styles, the output will include those.
+    pub fn fancy_format_stderr(&self, source: &SourceCode) {
+        Emitter::stderr(ColorConfig::Auto, Some(&*source.codemap()))
+            .emit(&[self.diagnostic(source)]);
+    }
+
+    /// Create the optional span label displayed as an annotation on
+    /// the underlined span of the warning.
+    fn span_label(&self) -> Option<String> {
+        match self.kind {
+            WarningKind::UnusedBinding | WarningKind::ShadowedGlobal(_) => {
+                Some("variable declared here".into())
+            }
+            _ => None,
+        }
+    }
+
+    /// Create the primary warning message displayed to users for a
+    /// warning.
+    fn message(&self, source: &SourceCode) -> String {
+        match self.kind {
+            WarningKind::DeprecatedLiteralURL => {
+                "URL literal syntax is deprecated, use a quoted string instead".to_string()
+            }
+
+            WarningKind::UselessInherit => {
+                "inherit does nothing (this variable already exists with the same value)"
+                    .to_string()
+            }
+
+            WarningKind::UnusedBinding => {
+                format!(
+                    "variable '{}' is declared, but never used:",
+                    source.source_slice(self.span)
+                )
+            }
+
+            WarningKind::ShadowedGlobal(name) => {
+                format!("declared variable '{}' shadows a built-in global!", name)
+            }
+
+            WarningKind::DeprecatedLegacyLet => {
+                "legacy `let` syntax used, please rewrite this as `let .. in ...`".to_string()
+            }
+
+            WarningKind::InvalidNixPath(ref err) => {
+                format!("invalid NIX_PATH resulted in a parse error: {}", err)
+            }
+
+            WarningKind::UselessBoolOperation(msg) => {
+                format!("useless operation on boolean: {}", msg)
+            }
+
+            WarningKind::DeadCode => "this code will never be executed".to_string(),
+
+            WarningKind::EmptyInherit => "this `inherit` statement is empty".to_string(),
+
+            WarningKind::EmptyLet => "this `let`-expression contains no bindings".to_string(),
+
+            WarningKind::ShadowedOutput(ref out) => format!(
+                "this derivation's environment shadows the output name {}",
+                out
+            ),
+            WarningKind::SRIHashWrongPadding => "SRI hash has wrong padding".to_string(),
+
+            WarningKind::NotImplemented(what) => {
+                format!("feature not yet implemented in tvix: {}", what)
+            }
+        }
+    }
+
+    /// Return the unique warning code for this variant which can be
+    /// used to refer users to documentation.
+    fn code(&self) -> &'static str {
+        match self.kind {
+            WarningKind::DeprecatedLiteralURL => "W001",
+            WarningKind::UselessInherit => "W002",
+            WarningKind::UnusedBinding => "W003",
+            WarningKind::ShadowedGlobal(_) => "W004",
+            WarningKind::DeprecatedLegacyLet => "W005",
+            WarningKind::InvalidNixPath(_) => "W006",
+            WarningKind::UselessBoolOperation(_) => "W007",
+            WarningKind::DeadCode => "W008",
+            WarningKind::EmptyInherit => "W009",
+            WarningKind::EmptyLet => "W010",
+            WarningKind::ShadowedOutput(_) => "W011",
+            WarningKind::SRIHashWrongPadding => "W012",
+
+            WarningKind::NotImplemented(_) => "W999",
+        }
+    }
+
+    fn diagnostic(&self, source: &SourceCode) -> Diagnostic {
+        let span_label = SpanLabel {
+            label: self.span_label(),
+            span: self.span,
+            style: SpanStyle::Primary,
+        };
+
+        Diagnostic {
+            level: Level::Warning,
+            message: self.message(source),
+            spans: vec![span_label],
+            code: Some(self.code().into()),
+        }
+    }
+}
diff --git a/tvix/eval/tests/nix_oracle.rs b/tvix/eval/tests/nix_oracle.rs
new file mode 100644
index 0000000000..5a5cc0a822
--- /dev/null
+++ b/tvix/eval/tests/nix_oracle.rs
@@ -0,0 +1,171 @@
+//! Tests which use upstream nix as an oracle to test evaluation against
+
+use std::{env, path::PathBuf, process::Command};
+
+use pretty_assertions::assert_eq;
+
+fn nix_binary_path() -> PathBuf {
+    env::var("NIX_INSTANTIATE_BINARY_PATH")
+        .unwrap_or_else(|_| "nix-instantiate".to_owned())
+        .into()
+}
+
+#[derive(Clone, Copy)]
+enum Strictness {
+    Lazy,
+    Strict,
+}
+
+fn nix_eval(expr: &str, strictness: Strictness) -> String {
+    let store_dir = tempfile::tempdir().unwrap();
+
+    let mut args = match strictness {
+        Strictness::Lazy => vec![],
+        Strictness::Strict => vec!["--strict"],
+    };
+    args.extend_from_slice(&["--eval", "-E"]);
+
+    let output = Command::new(nix_binary_path())
+        .args(&args[..])
+        .arg(format!("({expr})"))
+        .env(
+            "NIX_REMOTE",
+            format!(
+                "local?root={}",
+                store_dir
+                    .path()
+                    .canonicalize()
+                    .expect("valid path")
+                    .display()
+            ),
+        )
+        .output()
+        .unwrap();
+    if !output.status.success() {
+        panic!(
+            "nix eval {expr} failed!\n    stdout: {}\n    stderr: {}",
+            String::from_utf8_lossy(&output.stdout),
+            String::from_utf8_lossy(&output.stderr)
+        )
+    }
+
+    String::from_utf8(output.stdout).unwrap()
+}
+
+/// Compare the evaluation of the given nix expression in nix (using the
+/// `NIX_INSTANTIATE_BINARY_PATH` env var to resolve the `nix-instantiate` binary) and tvix, and
+/// assert that the result is identical
+#[track_caller]
+fn compare_eval(expr: &str, strictness: Strictness) {
+    let nix_result = nix_eval(expr, strictness);
+    let mut eval = tvix_eval::Evaluation::new_pure();
+    eval.strict = matches!(strictness, Strictness::Strict);
+    eval.io_handle = Box::new(tvix_eval::StdIO);
+
+    let tvix_result = eval
+        .evaluate(expr, None)
+        .value
+        .expect("tvix evaluation should succeed")
+        .to_string();
+
+    assert_eq!(nix_result.trim(), tvix_result);
+}
+
+/// Generate a suite of tests which call [`compare_eval`] on expressions, checking that nix and tvix
+/// return identical results.
+macro_rules! compare_eval_tests {
+    ($strictness:expr, {}) => {};
+    ($strictness:expr, {$(#[$meta:meta])* $test_name: ident($expr: expr); $($rest:tt)*}) => {
+        #[test]
+        $(#[$meta])*
+        fn $test_name() {
+            compare_eval($expr, $strictness);
+        }
+
+        compare_eval_tests!($strictness, { $($rest)* });
+    }
+}
+
+macro_rules! compare_strict_eval_tests {
+    ($($tests:tt)*) => {
+        compare_eval_tests!(Strictness::Strict, { $($tests)* });
+    }
+}
+
+macro_rules! compare_lazy_eval_tests {
+    ($($tests:tt)*) => {
+        compare_eval_tests!(Strictness::Lazy, { $($tests)* });
+    }
+}
+
+compare_strict_eval_tests! {
+    literal_int("1");
+    add_ints("1 + 1");
+    add_lists("[1 2] ++ [3 4]");
+    add_paths(r#"[
+        (./. + "/")
+        (./foo + "bar")
+        (let name = "bar"; in ./foo + name)
+        (let name = "bar"; in ./foo + "${name}")
+        (let name = "bar"; in ./foo + "/" + "${name}")
+        (let name = "bar"; in ./foo + "/${name}")
+        (./. + ./.)
+    ]"#);
+}
+
+// TODO(sterni): tvix_tests should gain support for something similar in the future,
+// but this requires messing with the path naming which would break compat with
+// C++ Nix's test suite
+compare_lazy_eval_tests! {
+    // Wrap every expression type supported by [Compiler::compile] in a list
+    // with lazy evaluation enabled, so we can check it being thunked or not
+    // against C++ Nix.
+    unthunked_literals_in_list("[ https://tvl.fyi 1 1.2 ]");
+    unthunked_path_in_list("[ ./nix_oracle.rs ]");
+    unthunked_string_literal_in_list("[ \":thonking:\" ]");
+    thunked_unary_ops_in_list("[ (!true) (-1) ]");
+    thunked_bin_ops_in_list(r#"
+      let
+        # Necessary to fool the optimiser for && and ||
+        true' = true;
+        false' = false;
+      in
+      [
+        (true' && false')
+        (true' || false')
+        (false -> true)
+        (40 + 2)
+        (43 - 1)
+        (21 * 2)
+        (126 / 3)
+        ({ } // { bar = null; })
+        (12 == 13)
+        (3 < 2)
+        (4 > 2)
+        (23 >= 42)
+        (33 <= 22)
+        ([ ] ++ [ ])
+        (42 != null)
+      ]
+    "#);
+    thunked_has_attrs_in_list("[ ({ } ? foo) ]");
+    thunked_list_in_list("[ [ 1 2 3 ] ]");
+    thunked_attr_set_in_list("[ { foo = null; } ]");
+    thunked_select_in_list("[ ({ foo = null; }.bar) ]");
+    thunked_assert_in_list("[ (assert false; 12) ]");
+    thunked_if_in_list("[ (if false then 13 else 12) ]");
+    thunked_let_in_list("[ (let foo = 12; in foo) ]");
+    thunked_with_in_list("[ (with { foo = 13; }; fooo) ]");
+    unthunked_identifier_in_list("let foo = 12; in [ foo ]");
+    thunked_lambda_in_list("[ (x: x) ]");
+    thunked_function_application_in_list("[ (builtins.add 1 2) ]");
+    thunked_legacy_let_in_list("[ (let { foo = 12; body = foo; }) ]");
+    unthunked_relative_path("[ ./foo ]");
+    unthunked_home_relative_path("[ ~/foo ]");
+    unthunked_absolute_path("[ /foo ]");
+
+    unthunked_formals_fallback_literal("({ foo ? 12 }: [ foo ]) { }");
+    unthunked_formals_fallback_string_literal("({ foo ? \"wiggly\" }: [ foo ]) { }");
+    thunked_formals_fallback_application("({ foo ? builtins.add 1 2 }: [ foo ]) { }");
+    thunked_formals_fallback_name_resolution_literal("({ foo ? bar, bar ? 12 }: [ foo ]) { }");
+}
diff --git a/tvix/glue/Cargo.toml b/tvix/glue/Cargo.toml
new file mode 100644
index 0000000000..f929d720a0
--- /dev/null
+++ b/tvix/glue/Cargo.toml
@@ -0,0 +1,57 @@
+[package]
+name = "tvix-glue"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+async-recursion = "1.0.5"
+bstr = "1.6.0"
+bytes = "1.4.0"
+data-encoding = "2.3.3"
+futures = "0.3.30"
+magic = "0.16.2"
+nix-compat = { path = "../nix-compat" }
+pin-project = "1.1"
+reqwest = { version = "0.11.22", features = ["rustls-tls-native-roots"], default-features = false }
+tvix-build = { path = "../build", default-features = false, features = []}
+tvix-eval = { path = "../eval" }
+tvix-castore = { path = "../castore" }
+tvix-store = { path = "../store", default-features = false, features = []}
+tracing = "0.1.37"
+tokio = "1.28.0"
+tokio-tar = "0.3.1"
+tokio-util = { version = "0.7.9", features = ["io", "io-util", "compat"] }
+thiserror = "1.0.38"
+serde = "1.0.195"
+serde_json = "1.0"
+sha2 = "0.10.8"
+sha1 = "0.10.6"
+md-5 = "0.10.6"
+url = "2.4.0"
+walkdir = "2.4.0"
+
+[dependencies.async-compression]
+version = "0.4.6"
+features = ["tokio", "gzip", "bzip2", "xz"]
+
+[dependencies.wu-manber]
+git = "https://github.com/tvlfyi/wu-manber.git"
+
+[dev-dependencies]
+criterion = { version = "0.5", features = ["html_reports"] }
+hex-literal = "0.4.1"
+lazy_static = "1.4.0"
+nix = { version = "0.27.1", features = [ "fs" ] }
+pretty_assertions = "1.4.0"
+rstest = "0.19.0"
+tempfile = "3.8.1"
+
+[features]
+default = ["nix_tests"]
+# Enables running the Nix language test suite from the original C++
+# Nix implementation (at version 2.3) against Tvix.
+nix_tests = []
+
+[[bench]]
+name = "eval"
+harness = false
diff --git a/tvix/glue/benches/eval.rs b/tvix/glue/benches/eval.rs
new file mode 100644
index 0000000000..dfb4fabe44
--- /dev/null
+++ b/tvix/glue/benches/eval.rs
@@ -0,0 +1,77 @@
+use criterion::{black_box, criterion_group, criterion_main, Criterion};
+use lazy_static::lazy_static;
+use std::{env, rc::Rc, sync::Arc, time::Duration};
+use tvix_build::buildservice::DummyBuildService;
+use tvix_castore::{
+    blobservice::{BlobService, MemoryBlobService},
+    directoryservice::{DirectoryService, MemoryDirectoryService},
+};
+use tvix_eval::{builtins::impure_builtins, EvalIO};
+use tvix_glue::{
+    builtins::{add_derivation_builtins, add_fetcher_builtins, add_import_builtins},
+    configure_nix_path,
+    tvix_io::TvixIO,
+    tvix_store_io::TvixStoreIO,
+};
+use tvix_store::pathinfoservice::{MemoryPathInfoService, PathInfoService};
+
+lazy_static! {
+    static ref BLOB_SERVICE: Arc<dyn BlobService> = Arc::new(MemoryBlobService::default());
+    static ref DIRECTORY_SERVICE: Arc<dyn DirectoryService> =
+        Arc::new(MemoryDirectoryService::default());
+    static ref PATH_INFO_SERVICE: Arc<dyn PathInfoService> = Arc::new(MemoryPathInfoService::new(
+        BLOB_SERVICE.clone(),
+        DIRECTORY_SERVICE.clone(),
+    ));
+    static ref TOKIO_RUNTIME: tokio::runtime::Runtime = tokio::runtime::Runtime::new().unwrap();
+}
+
+fn interpret(code: &str) {
+    // TODO: this is a bit annoying.
+    // It'd be nice if we could set this up once and then run evaluate() with a
+    // piece of code. b/262
+
+    // We assemble a complete store in memory.
+    let tvix_store_io = Rc::new(TvixStoreIO::new(
+        BLOB_SERVICE.clone(),
+        DIRECTORY_SERVICE.clone(),
+        PATH_INFO_SERVICE.clone(),
+        Arc::<DummyBuildService>::default(),
+        TOKIO_RUNTIME.handle().clone(),
+    ));
+
+    let mut eval = tvix_eval::Evaluation::new(
+        Box::new(TvixIO::new(tvix_store_io.clone() as Rc<dyn EvalIO>)) as Box<dyn EvalIO>,
+        true,
+    );
+
+    eval.builtins.extend(impure_builtins());
+    add_derivation_builtins(&mut eval, Rc::clone(&tvix_store_io));
+    add_fetcher_builtins(&mut eval, Rc::clone(&tvix_store_io));
+    add_import_builtins(&mut eval, tvix_store_io);
+    configure_nix_path(
+        &mut eval,
+        // The benchmark requires TVIX_BENCH_NIX_PATH to be set, so barf out
+        // early, rather than benchmarking tvix returning an error.
+        &Some(env::var("TVIX_BENCH_NIX_PATH").expect("TVIX_BENCH_NIX_PATH must be set")),
+    );
+
+    let result = eval.evaluate(code, None);
+
+    assert!(result.errors.is_empty());
+}
+
+fn eval_nixpkgs(c: &mut Criterion) {
+    c.bench_function("hello outpath", |b| {
+        b.iter(|| {
+            interpret(black_box("(import <nixpkgs> {}).hello.outPath"));
+        })
+    });
+}
+
+criterion_group!(
+    name = benches;
+    config = Criterion::default().measurement_time(Duration::from_secs(30)).sample_size(10);
+    targets = eval_nixpkgs
+);
+criterion_main!(benches);
diff --git a/tvix/glue/default.nix b/tvix/glue/default.nix
new file mode 100644
index 0000000000..08f5c2228d
--- /dev/null
+++ b/tvix/glue/default.nix
@@ -0,0 +1,8 @@
+{ depot, pkgs, ... }:
+
+(depot.tvix.crates.workspaceMembers.tvix-glue.build.override {
+  runTests = true;
+  testPreRun = ''
+    export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt;
+  '';
+})
diff --git a/tvix/glue/src/.skip-subtree b/tvix/glue/src/.skip-subtree
new file mode 100644
index 0000000000..a16a2afe1f
--- /dev/null
+++ b/tvix/glue/src/.skip-subtree
@@ -0,0 +1 @@
+Because of the derivation.nix file ...
diff --git a/tvix/glue/src/builtins/derivation.nix b/tvix/glue/src/builtins/derivation.nix
new file mode 100644
index 0000000000..9355cc3a96
--- /dev/null
+++ b/tvix/glue/src/builtins/derivation.nix
@@ -0,0 +1,36 @@
+# LGPL-2.1-or-later
+#
+# taken from: https://github.com/NixOS/nix/blob/master/src/libexpr/primops/derivation.nix
+#
+# TODO: rewrite in native Rust code
+
+/* This is the implementation of the β€˜derivation’ builtin function.
+   It's actually a wrapper around the β€˜derivationStrict’ primop. */
+
+drvAttrs @ { outputs ? [ "out" ], ... }:
+
+let
+
+  strict = derivationStrict drvAttrs;
+
+  commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) //
+    {
+      all = map (x: x.value) outputsList;
+      inherit drvAttrs;
+    };
+
+  outputToAttrListElement = outputName:
+    {
+      name = outputName;
+      value = commonAttrs // {
+        outPath = builtins.getAttr outputName strict;
+        drvPath = strict.drvPath;
+        type = "derivation";
+        inherit outputName;
+      };
+    };
+
+  outputsList = map outputToAttrListElement outputs;
+
+in
+(builtins.head outputsList).value
diff --git a/tvix/glue/src/builtins/derivation.rs b/tvix/glue/src/builtins/derivation.rs
new file mode 100644
index 0000000000..a7742ae40a
--- /dev/null
+++ b/tvix/glue/src/builtins/derivation.rs
@@ -0,0 +1,552 @@
+//! Implements `builtins.derivation`, the core of what makes Nix build packages.
+use crate::builtins::DerivationError;
+use crate::known_paths::KnownPaths;
+use crate::tvix_store_io::TvixStoreIO;
+use bstr::BString;
+use nix_compat::derivation::{Derivation, Output};
+use nix_compat::nixhash;
+use nix_compat::store_path::{StorePath, StorePathRef};
+use std::collections::{btree_map, BTreeSet};
+use std::rc::Rc;
+use tvix_eval::builtin_macros::builtins;
+use tvix_eval::generators::{self, emit_warning_kind, GenCo};
+use tvix_eval::{
+    AddContext, ErrorKind, NixAttrs, NixContext, NixContextElement, Value, WarningKind,
+};
+
+// Constants used for strangely named fields in derivation inputs.
+const STRUCTURED_ATTRS: &str = "__structuredAttrs";
+const IGNORE_NULLS: &str = "__ignoreNulls";
+
+/// Populate the inputs of a derivation from the build references
+/// found when scanning the derivation's parameters and extracting their contexts.
+fn populate_inputs(drv: &mut Derivation, full_context: NixContext, known_paths: &KnownPaths) {
+    for element in full_context.iter() {
+        match element {
+            NixContextElement::Plain(source) => {
+                let sp = StorePathRef::from_absolute_path(source.as_bytes())
+                    .expect("invalid store path")
+                    .to_owned();
+                drv.input_sources.insert(sp);
+            }
+
+            NixContextElement::Single {
+                name,
+                derivation: derivation_str,
+            } => {
+                // TODO: b/264
+                // We assume derivations to be passed validated, so ignoring rest
+                // and expecting parsing is ok.
+                let (derivation, _rest) =
+                    StorePath::from_absolute_path_full(derivation_str).expect("valid store path");
+
+                #[cfg(debug_assertions)]
+                assert!(
+                    _rest.iter().next().is_none(),
+                    "Extra path not empty for {}",
+                    derivation_str
+                );
+
+                match drv.input_derivations.entry(derivation.clone()) {
+                    btree_map::Entry::Vacant(entry) => {
+                        entry.insert(BTreeSet::from([name.clone()]));
+                    }
+
+                    btree_map::Entry::Occupied(mut entry) => {
+                        entry.get_mut().insert(name.clone());
+                    }
+                }
+            }
+
+            NixContextElement::Derivation(drv_path) => {
+                let (derivation, _rest) =
+                    StorePath::from_absolute_path_full(drv_path).expect("valid store path");
+
+                #[cfg(debug_assertions)]
+                assert!(
+                    _rest.iter().next().is_none(),
+                    "Extra path not empty for {}",
+                    drv_path
+                );
+
+                // We need to know all the outputs *names* of that derivation.
+                let output_names = known_paths
+                    .get_drv_by_drvpath(&derivation)
+                    .expect("no known derivation associated to that derivation path")
+                    .outputs
+                    .keys();
+
+                // FUTUREWORK(performance): ideally, we should be able to clone
+                // cheaply those outputs rather than duplicate them all around.
+                match drv.input_derivations.entry(derivation.clone()) {
+                    btree_map::Entry::Vacant(entry) => {
+                        entry.insert(output_names.cloned().collect());
+                    }
+
+                    btree_map::Entry::Occupied(mut entry) => {
+                        entry.get_mut().extend(output_names.cloned());
+                    }
+                }
+
+                drv.input_sources.insert(derivation);
+            }
+        }
+    }
+}
+
+/// Populate the output configuration of a derivation based on the
+/// parameters passed to the call, configuring a fixed-output derivation output
+/// if necessary.
+///
+/// This function handles all possible combinations of the
+/// parameters, including invalid ones.
+///
+/// Due to the support for SRI hashes, and how these are passed along to
+/// builtins.derivation, outputHash and outputHashAlgo can have values which
+/// need to be further modified before constructing the Derivation struct.
+///
+/// If outputHashAlgo is an SRI hash, outputHashAlgo must either be an empty
+/// string, or the hash algorithm as specified in the (single) SRI (entry).
+/// SRI strings with multiple hash algorithms are not supported.
+///
+/// In case an SRI string was used, the (single) fixed output is populated
+/// with the hash algo name, and the hash digest is populated with the
+/// (lowercase) hex encoding of the digest.
+///
+/// These values are only rewritten for the outputs, not what's passed to env.
+///
+/// The return value may optionally contain a warning.
+fn handle_fixed_output(
+    drv: &mut Derivation,
+    hash_str: Option<String>,      // in nix: outputHash
+    hash_algo_str: Option<String>, // in nix: outputHashAlgo
+    hash_mode_str: Option<String>, // in nix: outputHashmode
+) -> Result<Option<WarningKind>, ErrorKind> {
+    // If outputHash is provided, ensure hash_algo_str is compatible.
+    // If outputHash is not provided, do nothing.
+    if let Some(hash_str) = hash_str {
+        // treat an empty algo as None
+        let hash_algo_str = match hash_algo_str {
+            Some(s) if s.is_empty() => None,
+            Some(s) => Some(s),
+            None => None,
+        };
+
+        // construct a NixHash.
+        let nixhash = nixhash::from_str(&hash_str, hash_algo_str.as_deref())
+            .map_err(DerivationError::InvalidOutputHash)?;
+        let algo = nixhash.algo();
+
+        // construct the fixed output.
+        drv.outputs.insert(
+            "out".to_string(),
+            Output {
+                path: None,
+                ca_hash: match hash_mode_str.as_deref() {
+                    None | Some("flat") => Some(nixhash::CAHash::Flat(nixhash)),
+                    Some("recursive") => Some(nixhash::CAHash::Nar(nixhash)),
+                    Some(other) => {
+                        return Err(DerivationError::InvalidOutputHashMode(other.to_string()))?
+                    }
+                },
+            },
+        );
+
+        // Peek at hash_str once more.
+        // If it was a SRI hash, but is not using the correct length, this means
+        // the padding was wrong. Emit a warning in that case.
+        let sri_prefix = format!("{}-", algo);
+        if let Some(rest) = hash_str.strip_prefix(&sri_prefix) {
+            if data_encoding::BASE64.encode_len(algo.digest_length()) != rest.len() {
+                return Ok(Some(WarningKind::SRIHashWrongPadding));
+            }
+        }
+    }
+    Ok(None)
+}
+
+#[builtins(state = "Rc<TvixStoreIO>")]
+pub(crate) mod derivation_builtins {
+    use std::collections::BTreeMap;
+
+    use crate::builtins::utils::{select_string, strong_importing_coerce_to_string};
+
+    use super::*;
+    use bstr::ByteSlice;
+    use nix_compat::store_path::hash_placeholder;
+    use tvix_eval::generators::Gen;
+    use tvix_eval::{NixContext, NixContextElement, NixString};
+
+    #[builtin("placeholder")]
+    async fn builtin_placeholder(co: GenCo, input: Value) -> Result<Value, ErrorKind> {
+        if input.is_catchable() {
+            return Ok(input);
+        }
+
+        let placeholder = hash_placeholder(
+            input
+                .to_str()
+                .context("looking at output name in builtins.placeholder")?
+                .to_str()?,
+        );
+
+        Ok(placeholder.into())
+    }
+
+    /// Strictly construct a Nix derivation from the supplied arguments.
+    ///
+    /// This is considered an internal function, users usually want to
+    /// use the higher-level `builtins.derivation` instead.
+    #[builtin("derivationStrict")]
+    async fn builtin_derivation_strict(
+        state: Rc<TvixStoreIO>,
+        co: GenCo,
+        input: Value,
+    ) -> Result<Value, ErrorKind> {
+        if input.is_catchable() {
+            return Ok(input);
+        }
+
+        let input = input.to_attrs()?;
+        let name = generators::request_force(&co, input.select_required("name")?.clone()).await;
+
+        if name.is_catchable() {
+            return Ok(name);
+        }
+
+        let name = name.to_str().context("determining derivation name")?;
+        if name.is_empty() {
+            return Err(ErrorKind::Abort("derivation has empty name".to_string()));
+        }
+        let name = name.to_str()?;
+
+        let mut drv = Derivation::default();
+        drv.outputs.insert("out".to_string(), Default::default());
+        let mut input_context = NixContext::new();
+
+        /// Inserts a key and value into the drv.environment BTreeMap, and fails if the
+        /// key did already exist before.
+        fn insert_env(
+            drv: &mut Derivation,
+            k: &str, /* TODO: non-utf8 env keys */
+            v: BString,
+        ) -> Result<(), DerivationError> {
+            if drv.environment.insert(k.into(), v).is_some() {
+                return Err(DerivationError::DuplicateEnvVar(k.into()));
+            }
+            Ok(())
+        }
+
+        // Check whether null attributes should be ignored or passed through.
+        let ignore_nulls = match input.select(IGNORE_NULLS) {
+            Some(b) => generators::request_force(&co, b.clone()).await.as_bool()?,
+            None => false,
+        };
+
+        // peek at the STRUCTURED_ATTRS argument.
+        // If it's set and true, provide a BTreeMap that gets populated while looking at the arguments.
+        // We need it to be a BTreeMap, so iteration order of keys is reproducible.
+        let mut structured_attrs: Option<BTreeMap<String, serde_json::Value>> =
+            match input.select(STRUCTURED_ATTRS) {
+                Some(b) => generators::request_force(&co, b.clone())
+                    .await
+                    .as_bool()?
+                    .then_some(Default::default()),
+                None => None,
+            };
+
+        // Look at the arguments passed to builtins.derivationStrict.
+        // Some set special fields in the Derivation struct, some change
+        // behaviour of other functionality.
+        for (arg_name, arg_value) in input.clone().into_iter_sorted() {
+            let arg_name = arg_name.to_str()?;
+            // force the current value.
+            let value = generators::request_force(&co, arg_value).await;
+
+            // filter out nulls if ignore_nulls is set.
+            if ignore_nulls && matches!(value, Value::Null) {
+                continue;
+            }
+
+            match arg_name {
+                // Command line arguments to the builder.
+                // These are only set in drv.arguments.
+                "args" => {
+                    for arg in value.to_list()? {
+                        match strong_importing_coerce_to_string(&co, arg).await {
+                            Err(cek) => return Ok(Value::from(cek)),
+                            Ok(s) => {
+                                input_context.mimic(&s);
+                                drv.arguments.push(s.to_str()?.to_owned())
+                            }
+                        }
+                    }
+                }
+
+                // If outputs is set, remove the original default `out` output,
+                // and replace it with the list of outputs.
+                "outputs" => {
+                    let outputs = value
+                        .to_list()
+                        .context("looking at the `outputs` parameter of the derivation")?;
+
+                    // Remove the original default `out` output.
+                    drv.outputs.clear();
+
+                    let mut output_names = vec![];
+
+                    for output in outputs {
+                        let output_name = generators::request_force(&co, output)
+                            .await
+                            .to_str()
+                            .context("determining output name")?;
+
+                        input_context.mimic(&output_name);
+
+                        // Populate drv.outputs
+                        if drv
+                            .outputs
+                            .insert(output_name.to_str()?.to_owned(), Default::default())
+                            .is_some()
+                        {
+                            Err(DerivationError::DuplicateOutput(
+                                output_name.to_str_lossy().into_owned(),
+                            ))?
+                        }
+                        output_names.push(output_name.to_str()?.to_owned());
+                    }
+
+                    match structured_attrs.as_mut() {
+                        // add outputs to the json itself (as a list of strings)
+                        Some(structured_attrs) => {
+                            structured_attrs.insert(arg_name.into(), output_names.into());
+                        }
+                        // add drv.environment["outputs"] as a space-separated list
+                        None => {
+                            insert_env(&mut drv, arg_name, output_names.join(" ").into())?;
+                        }
+                    }
+                    // drv.environment[$output_name] is added after the loop,
+                    // with whatever is in drv.outputs[$output_name].
+                }
+
+                // handle builder and system.
+                "builder" | "system" => {
+                    match strong_importing_coerce_to_string(&co, value).await {
+                        Err(cek) => return Ok(Value::from(cek)),
+                        Ok(val_str) => {
+                            input_context.mimic(&val_str);
+
+                            if arg_name == "builder" {
+                                drv.builder = val_str.to_str()?.to_owned();
+                            } else {
+                                drv.system = val_str.to_str()?.to_owned();
+                            }
+
+                            // Either populate drv.environment or structured_attrs.
+                            if let Some(ref mut structured_attrs) = structured_attrs {
+                                // No need to check for dups, we only iterate over every attribute name once
+                                structured_attrs.insert(
+                                    arg_name.to_owned(),
+                                    val_str.to_str()?.to_owned().into(),
+                                );
+                            } else {
+                                insert_env(&mut drv, arg_name, val_str.as_bytes().into())?;
+                            }
+                        }
+                    }
+                }
+
+                // Don't add STRUCTURED_ATTRS if enabled.
+                STRUCTURED_ATTRS if structured_attrs.is_some() => continue,
+                // IGNORE_NULLS is always skipped, even if it's not set to true.
+                IGNORE_NULLS => continue,
+
+                // all other args.
+                _ => {
+                    // In SA case, force and add to structured attrs.
+                    // In non-SA case, coerce to string and add to env.
+                    if let Some(ref mut structured_attrs) = structured_attrs {
+                        let val = generators::request_force(&co, value).await;
+                        if val.is_catchable() {
+                            return Ok(val);
+                        }
+
+                        let (val_json, mut context) = match val.into_contextful_json(&co).await? {
+                            Ok(v) => v,
+                            Err(cek) => return Ok(Value::from(cek)),
+                        };
+
+                        input_context = input_context.join(&mut context);
+
+                        // No need to check for dups, we only iterate over every attribute name once
+                        structured_attrs.insert(arg_name.to_owned(), val_json);
+                    } else {
+                        match strong_importing_coerce_to_string(&co, value).await {
+                            Err(cek) => return Ok(Value::from(cek)),
+                            Ok(val_str) => {
+                                input_context.mimic(&val_str);
+
+                                insert_env(&mut drv, arg_name, val_str.as_bytes().into())?;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        // end of per-argument loop
+
+        // Configure fixed-output derivations if required.
+        {
+            let output_hash = match select_string(&co, &input, "outputHash")
+                .await
+                .context("evaluating the `outputHash` parameter")?
+            {
+                Err(cek) => return Ok(Value::from(cek)),
+                Ok(s) => s,
+            };
+            let output_hash_algo = match select_string(&co, &input, "outputHashAlgo")
+                .await
+                .context("evaluating the `outputHashAlgo` parameter")?
+            {
+                Err(cek) => return Ok(Value::from(cek)),
+                Ok(s) => s,
+            };
+            let output_hash_mode = match select_string(&co, &input, "outputHashMode")
+                .await
+                .context("evaluating the `outputHashMode` parameter")?
+            {
+                Err(cek) => return Ok(Value::from(cek)),
+                Ok(s) => s,
+            };
+
+            if let Some(warning) =
+                handle_fixed_output(&mut drv, output_hash, output_hash_algo, output_hash_mode)?
+            {
+                emit_warning_kind(&co, warning).await;
+            }
+        }
+
+        // Each output name needs to exist in the environment, at this
+        // point initialised as an empty string, as the ATerm serialization of that is later
+        // used for the output path calculation (which will also update output
+        // paths post-calculation, both in drv.environment and drv.outputs)
+        for output in drv.outputs.keys() {
+            if drv
+                .environment
+                .insert(output.to_string(), String::new().into())
+                .is_some()
+            {
+                emit_warning_kind(&co, WarningKind::ShadowedOutput(output.to_string())).await;
+            }
+        }
+
+        if let Some(structured_attrs) = structured_attrs {
+            // configure __json
+            drv.environment.insert(
+                "__json".to_string(),
+                BString::from(serde_json::to_string(&structured_attrs)?),
+            );
+        }
+
+        let mut known_paths = state.as_ref().known_paths.borrow_mut();
+        populate_inputs(&mut drv, input_context, &known_paths);
+
+        // At this point, derivation fields are fully populated from
+        // eval data structures.
+        drv.validate(false)
+            .map_err(DerivationError::InvalidDerivation)?;
+
+        // Calculate the hash_derivation_modulo for the current derivation..
+        debug_assert!(
+            drv.outputs.values().all(|output| { output.path.is_none() }),
+            "outputs should still be unset"
+        );
+
+        // Mutate the Derivation struct and set output paths
+        drv.calculate_output_paths(
+            name,
+            // This one is still intermediate (so not added to known_paths),
+            // as the outputs are still unset.
+            &drv.hash_derivation_modulo(|drv_path| {
+                *known_paths
+                    .get_hash_derivation_modulo(&drv_path.to_owned())
+                    .unwrap_or_else(|| panic!("{} not found", drv_path))
+            }),
+        )
+        .map_err(DerivationError::InvalidDerivation)?;
+
+        let drv_path = drv
+            .calculate_derivation_path(name)
+            .map_err(DerivationError::InvalidDerivation)?;
+
+        // Assemble the attrset to return from this builtin.
+        let out = Value::Attrs(Box::new(NixAttrs::from_iter(
+            drv.outputs
+                .iter()
+                .map(|(name, output)| {
+                    (
+                        name.clone(),
+                        NixString::new_context_from(
+                            NixContextElement::Single {
+                                name: name.clone(),
+                                derivation: drv_path.to_absolute_path(),
+                            }
+                            .into(),
+                            output.path.as_ref().unwrap().to_absolute_path(),
+                        ),
+                    )
+                })
+                .chain(std::iter::once((
+                    "drvPath".to_owned(),
+                    NixString::new_context_from(
+                        NixContextElement::Derivation(drv_path.to_absolute_path()).into(),
+                        drv_path.to_absolute_path(),
+                    ),
+                ))),
+        )));
+
+        // Register the Derivation in known_paths.
+        known_paths.add_derivation(drv_path, drv);
+
+        Ok(out)
+    }
+
+    #[builtin("toFile")]
+    async fn builtin_to_file(co: GenCo, name: Value, content: Value) -> Result<Value, ErrorKind> {
+        if name.is_catchable() {
+            return Ok(name);
+        }
+
+        if content.is_catchable() {
+            return Ok(content);
+        }
+
+        let name = name
+            .to_str()
+            .context("evaluating the `name` parameter of builtins.toFile")?;
+        let content = content
+            .to_contextful_str()
+            .context("evaluating the `content` parameter of builtins.toFile")?;
+
+        if content.iter_derivation().count() > 0 || content.iter_single_outputs().count() > 0 {
+            return Err(ErrorKind::UnexpectedContext);
+        }
+
+        let path =
+            nix_compat::store_path::build_text_path(name.to_str()?, &content, content.iter_plain())
+                .map_err(|_e| {
+                    nix_compat::derivation::DerivationError::InvalidOutputName(
+                        name.to_str_lossy().into_owned(),
+                    )
+                })
+                .map_err(DerivationError::InvalidDerivation)?
+                .to_absolute_path();
+
+        let context: NixContext = NixContextElement::Plain(path.clone()).into();
+
+        // TODO: actually persist the file in the store at that path ...
+
+        Ok(Value::from(NixString::new_context_from(context, path)))
+    }
+}
diff --git a/tvix/glue/src/builtins/errors.rs b/tvix/glue/src/builtins/errors.rs
new file mode 100644
index 0000000000..f6d5745c56
--- /dev/null
+++ b/tvix/glue/src/builtins/errors.rs
@@ -0,0 +1,76 @@
+//! Contains errors that can occur during evaluation of builtins in this crate
+use nix_compat::{
+    nixhash::{self, NixHash},
+    store_path::BuildStorePathError,
+};
+use reqwest::Url;
+use std::rc::Rc;
+use thiserror::Error;
+use tvix_castore::import;
+
+/// Errors related to derivation construction
+#[derive(Debug, Error)]
+pub enum DerivationError {
+    #[error("an output with the name '{0}' is already defined")]
+    DuplicateOutput(String),
+    #[error("fixed-output derivations can only have the default `out`-output")]
+    ConflictingOutputTypes,
+    #[error("the environment variable '{0}' has already been set in this derivation")]
+    DuplicateEnvVar(String),
+    #[error("invalid derivation parameters: {0}")]
+    InvalidDerivation(#[from] nix_compat::derivation::DerivationError),
+    #[error("invalid output hash: {0}")]
+    InvalidOutputHash(#[from] nixhash::Error),
+    #[error("invalid output hash mode: '{0}', only 'recursive' and 'flat` are supported")]
+    InvalidOutputHashMode(String),
+}
+
+impl From<DerivationError> for tvix_eval::ErrorKind {
+    fn from(err: DerivationError) -> Self {
+        tvix_eval::ErrorKind::TvixError(Rc::new(err))
+    }
+}
+
+#[derive(Debug, Error)]
+pub enum FetcherError {
+    #[error("hash mismatch in file downloaded from {url}:\n  wanted: {wanted}\n     got: {got}")]
+    HashMismatch {
+        url: Url,
+        wanted: NixHash,
+        got: NixHash,
+    },
+
+    #[error("Invalid hash type '{0}' for fetcher")]
+    InvalidHashType(&'static str),
+
+    #[error("Unable to parse URL: {0}")]
+    InvalidUrl(#[from] url::ParseError),
+
+    #[error(transparent)]
+    Http(#[from] reqwest::Error),
+
+    #[error(transparent)]
+    Io(#[from] std::io::Error),
+
+    #[error(transparent)]
+    Import(#[from] tvix_castore::import::IngestionError<import::archive::Error>),
+
+    #[error("Error calculating store path for fetcher output: {0}")]
+    StorePath(#[from] BuildStorePathError),
+}
+
+/// Errors related to `builtins.path` and `builtins.filterSource`,
+/// a.k.a. "importing" builtins.
+#[derive(Debug, Error)]
+pub enum ImportError {
+    #[error("non-file '{0}' cannot be imported in 'flat' mode")]
+    FlatImportOfNonFile(String),
+    #[error("hash mismatch at ingestion of '{0}', expected: '{1}', got: '{2}'")]
+    HashMismatch(String, NixHash, NixHash),
+}
+
+impl From<ImportError> for tvix_eval::ErrorKind {
+    fn from(err: ImportError) -> Self {
+        tvix_eval::ErrorKind::TvixError(Rc::new(err))
+    }
+}
diff --git a/tvix/glue/src/builtins/fetchers.rs b/tvix/glue/src/builtins/fetchers.rs
new file mode 100644
index 0000000000..c7602c03e8
--- /dev/null
+++ b/tvix/glue/src/builtins/fetchers.rs
@@ -0,0 +1,186 @@
+//! Contains builtins that fetch paths from the Internet, or local filesystem.
+
+use super::utils::select_string;
+use crate::{
+    fetchers::{url_basename, Fetch},
+    tvix_store_io::TvixStoreIO,
+};
+use nix_compat::nixhash;
+use nix_compat::nixhash::NixHash;
+use std::rc::Rc;
+use tracing::info;
+use tvix_eval::builtin_macros::builtins;
+use tvix_eval::generators::Gen;
+use tvix_eval::generators::GenCo;
+use tvix_eval::{CatchableErrorKind, ErrorKind, Value};
+
+struct NixFetchArgs {
+    url_str: String,
+    name: Option<String>,
+    sha256: Option<[u8; 32]>,
+}
+
+// `fetchurl` and `fetchTarball` accept a single argument, which can either be the URL (as string),
+// or an attrset, where `url`, `sha256` and `name` keys are allowed.
+async fn extract_fetch_args(
+    co: &GenCo,
+    args: Value,
+) -> Result<Result<NixFetchArgs, CatchableErrorKind>, ErrorKind> {
+    if let Ok(url_str) = args.to_str() {
+        // Get the raw bytes, not the ToString repr.
+        let url_str =
+            String::from_utf8(url_str.as_bytes().to_vec()).map_err(|_| ErrorKind::Utf8)?;
+        return Ok(Ok(NixFetchArgs {
+            url_str,
+            name: None,
+            sha256: None,
+        }));
+    }
+
+    let attrs = args.to_attrs().map_err(|_| ErrorKind::TypeError {
+        expected: "attribute set or contextless string",
+        actual: args.type_of(),
+    })?;
+
+    let url_str = match select_string(co, &attrs, "url").await? {
+        Ok(s) => s.ok_or_else(|| ErrorKind::AttributeNotFound { name: "url".into() })?,
+        Err(cek) => return Ok(Err(cek)),
+    };
+    let name = match select_string(co, &attrs, "name").await? {
+        Ok(s) => s,
+        Err(cek) => return Ok(Err(cek)),
+    };
+    let sha256_str = match select_string(co, &attrs, "sha256").await? {
+        Ok(s) => s,
+        Err(cek) => return Ok(Err(cek)),
+    };
+
+    // TODO: disallow other attrset keys, to match Nix' behaviour.
+
+    // parse the sha256 string into a digest.
+    let sha256 = match sha256_str {
+        Some(sha256_str) => {
+            let nixhash = nixhash::from_str(&sha256_str, Some("sha256"))
+                // TODO: DerivationError::InvalidOutputHash should be moved to ErrorKind::InvalidHash and used here instead
+                .map_err(|e| ErrorKind::TvixError(Rc::new(e)))?;
+
+            Some(nixhash.digest_as_bytes().try_into().expect("is sha256"))
+        }
+        None => None,
+    };
+
+    Ok(Ok(NixFetchArgs {
+        url_str,
+        name,
+        sha256,
+    }))
+}
+
+#[allow(unused_variables)] // for the `state` arg, for now
+#[builtins(state = "Rc<TvixStoreIO>")]
+pub(crate) mod fetcher_builtins {
+    use crate::builtins::FetcherError;
+    use url::Url;
+
+    use super::*;
+
+    /// Consumes a fetch.
+    /// If there is enough info to calculate the store path without fetching,
+    /// queue the fetch to be fetched lazily, and return the store path.
+    /// If there's not enough info to calculate it, do the fetch now, and then
+    /// return the store path.
+    fn fetch_lazy(state: Rc<TvixStoreIO>, name: String, fetch: Fetch) -> Result<Value, ErrorKind> {
+        match fetch
+            .store_path(&name)
+            .map_err(|e| ErrorKind::TvixError(Rc::new(e)))?
+        {
+            Some(store_path) => {
+                // Move the fetch to KnownPaths, so it can be actually fetched later.
+                let sp = state
+                    .known_paths
+                    .borrow_mut()
+                    .add_fetch(fetch, &name)
+                    .expect("Tvix bug: should only fail if the store path cannot be calculated");
+
+                debug_assert_eq!(
+                    sp, store_path,
+                    "calculated store path by KnownPaths should match"
+                );
+
+                // Emit the calculated Store Path.
+                Ok(Value::Path(Box::new(store_path.to_absolute_path().into())))
+            }
+            None => {
+                // If we don't have enough info, do the fetch now.
+                info!(?fetch, "triggering required fetch");
+
+                let (store_path, _root_node) = state
+                    .tokio_handle
+                    .block_on(async { state.fetcher.ingest_and_persist(&name, fetch).await })
+                    .map_err(|e| ErrorKind::TvixError(Rc::new(e)))?;
+
+                Ok(Value::Path(Box::new(store_path.to_absolute_path().into())))
+            }
+        }
+    }
+
+    #[builtin("fetchurl")]
+    async fn builtin_fetchurl(
+        state: Rc<TvixStoreIO>,
+        co: GenCo,
+        args: Value,
+    ) -> Result<Value, ErrorKind> {
+        let args = match extract_fetch_args(&co, args).await? {
+            Ok(args) => args,
+            Err(cek) => return Ok(Value::from(cek)),
+        };
+
+        // Derive the name from the URL basename if not set explicitly.
+        let name = args
+            .name
+            .unwrap_or_else(|| url_basename(&args.url_str).to_owned());
+
+        // Parse the URL.
+        let url = Url::parse(&args.url_str)
+            .map_err(|e| ErrorKind::TvixError(Rc::new(FetcherError::InvalidUrl(e))))?;
+
+        fetch_lazy(
+            state,
+            name,
+            Fetch::URL(url, args.sha256.map(NixHash::Sha256)),
+        )
+    }
+
+    #[builtin("fetchTarball")]
+    async fn builtin_fetch_tarball(
+        state: Rc<TvixStoreIO>,
+        co: GenCo,
+        args: Value,
+    ) -> Result<Value, ErrorKind> {
+        let args = match extract_fetch_args(&co, args).await? {
+            Ok(args) => args,
+            Err(cek) => return Ok(Value::from(cek)),
+        };
+
+        // Name defaults to "source" if not set explicitly.
+        const DEFAULT_NAME_FETCH_TARBALL: &str = "source";
+        let name = args
+            .name
+            .unwrap_or_else(|| DEFAULT_NAME_FETCH_TARBALL.to_owned());
+
+        // Parse the URL.
+        let url = Url::parse(&args.url_str)
+            .map_err(|e| ErrorKind::TvixError(Rc::new(FetcherError::InvalidUrl(e))))?;
+
+        fetch_lazy(state, name, Fetch::Tarball(url, args.sha256))
+    }
+
+    #[builtin("fetchGit")]
+    async fn builtin_fetch_git(
+        state: Rc<TvixStoreIO>,
+        co: GenCo,
+        args: Value,
+    ) -> Result<Value, ErrorKind> {
+        Err(ErrorKind::NotImplemented("fetchGit"))
+    }
+}
diff --git a/tvix/glue/src/builtins/import.rs b/tvix/glue/src/builtins/import.rs
new file mode 100644
index 0000000000..219695b69f
--- /dev/null
+++ b/tvix/glue/src/builtins/import.rs
@@ -0,0 +1,287 @@
+//! Implements builtins used to import paths in the store.
+
+use crate::builtins::errors::ImportError;
+use std::path::Path;
+use tvix_castore::import::ingest_entries;
+use tvix_eval::{
+    builtin_macros::builtins,
+    generators::{self, GenCo},
+    ErrorKind, EvalIO, Value,
+};
+
+use std::rc::Rc;
+
+async fn filtered_ingest(
+    state: Rc<TvixStoreIO>,
+    co: GenCo,
+    path: &Path,
+    filter: Option<&Value>,
+) -> Result<tvix_castore::proto::node::Node, ErrorKind> {
+    let mut entries: Vec<walkdir::DirEntry> = vec![];
+    let mut it = walkdir::WalkDir::new(path)
+        .follow_links(false)
+        .follow_root_links(false)
+        .contents_first(false)
+        .into_iter();
+
+    // Skip root node.
+    entries.push(
+        it.next()
+            .ok_or_else(|| ErrorKind::IO {
+                path: Some(path.to_path_buf()),
+                error: std::io::Error::new(std::io::ErrorKind::NotFound, "No root node emitted")
+                    .into(),
+            })?
+            .map_err(|err| ErrorKind::IO {
+                path: Some(path.to_path_buf()),
+                error: std::io::Error::from(err).into(),
+            })?,
+    );
+
+    while let Some(entry) = it.next() {
+        // Entry could be a NotFound, if the root path specified does not exist.
+        let entry = entry.map_err(|err| ErrorKind::IO {
+            path: err.path().map(|p| p.to_path_buf()),
+            error: std::io::Error::from(err).into(),
+        })?;
+
+        // As per Nix documentation `:doc builtins.filterSource`.
+        let file_type = if entry.file_type().is_dir() {
+            "directory"
+        } else if entry.file_type().is_file() {
+            "regular"
+        } else if entry.file_type().is_symlink() {
+            "symlink"
+        } else {
+            "unknown"
+        };
+
+        let should_keep: bool = if let Some(filter) = filter {
+            generators::request_force(
+                &co,
+                generators::request_call_with(
+                    &co,
+                    filter.clone(),
+                    [
+                        Value::String(entry.path().as_os_str().as_encoded_bytes().into()),
+                        Value::String(file_type.into()),
+                    ],
+                )
+                .await,
+            )
+            .await
+            .as_bool()?
+        } else {
+            true
+        };
+
+        if !should_keep {
+            if file_type == "directory" {
+                it.skip_current_dir();
+            }
+            continue;
+        }
+
+        entries.push(entry);
+    }
+
+    let dir_entries = entries.into_iter().rev().map(Ok);
+
+    state.tokio_handle.block_on(async {
+        let entries = tvix_castore::import::fs::dir_entries_to_ingestion_stream(
+            &state.blob_service,
+            dir_entries,
+            path,
+        );
+        ingest_entries(&state.directory_service, entries)
+            .await
+            .map_err(|e| ErrorKind::IO {
+                path: Some(path.to_path_buf()),
+                error: Rc::new(std::io::Error::new(std::io::ErrorKind::Other, e)),
+            })
+    })
+}
+
+#[builtins(state = "Rc<TvixStoreIO>")]
+mod import_builtins {
+    use std::rc::Rc;
+
+    use super::*;
+
+    use nix_compat::nixhash::{CAHash, NixHash};
+    use tvix_eval::generators::Gen;
+    use tvix_eval::{generators::GenCo, ErrorKind, Value};
+    use tvix_eval::{NixContextElement, NixString};
+
+    use tvix_castore::B3Digest;
+
+    use crate::tvix_store_io::TvixStoreIO;
+
+    #[builtin("path")]
+    async fn builtin_path(
+        state: Rc<TvixStoreIO>,
+        co: GenCo,
+        args: Value,
+    ) -> Result<Value, ErrorKind> {
+        let args = args.to_attrs()?;
+        let path = args.select_required("path")?;
+        let path = generators::request_force(&co, path.clone())
+            .await
+            .to_path()?;
+        let name: String = if let Some(name) = args.select("name") {
+            generators::request_force(&co, name.clone())
+                .await
+                .to_str()?
+                .as_bstr()
+                .to_string()
+        } else {
+            tvix_store::import::path_to_name(&path)
+                .expect("Failed to derive the default name out of the path")
+                .to_string()
+        };
+        let filter = args.select("filter");
+        let recursive_ingestion = args
+            .select("recursive")
+            .map(|r| r.as_bool())
+            .transpose()?
+            .unwrap_or(true); // Yes, yes, Nix, by default, puts `recursive = true;`.
+        let expected_sha256 = args
+            .select("sha256")
+            .map(|h| {
+                h.to_str().and_then(|expected| {
+                    let expected = expected.into_bstring().to_string();
+                    // TODO: ensure that we fail if this is not a valid str.
+                    nix_compat::nixhash::from_str(&expected, None).map_err(|_err| {
+                        // TODO: a better error would be nice, we use
+                        // DerivationError::InvalidOutputHash usually for derivation construction.
+                        // This is not a derivation construction, should we move it outside and
+                        // generalize?
+                        ErrorKind::TypeError {
+                            expected: "sha256",
+                            actual: "not a sha256",
+                        }
+                    })
+                })
+            })
+            .transpose()?;
+
+        // FUTUREWORK(performance): this opens the file instead of using a stat-like
+        // system call to the file.
+        if !recursive_ingestion && state.open(path.as_ref()).is_err() {
+            Err(ImportError::FlatImportOfNonFile(
+                path.to_string_lossy().to_string(),
+            ))?;
+        }
+
+        let root_node = filtered_ingest(state.clone(), co, path.as_ref(), filter).await?;
+        let ca: CAHash = if recursive_ingestion {
+            CAHash::Nar(NixHash::Sha256(state.tokio_handle.block_on(async {
+                Ok::<_, tvix_eval::ErrorKind>(
+                    state
+                        .path_info_service
+                        .as_ref()
+                        .calculate_nar(&root_node)
+                        .await
+                        .map_err(|e| ErrorKind::TvixError(Rc::new(e)))?
+                        .1,
+                )
+            })?))
+        } else {
+            let digest: B3Digest = match root_node {
+                tvix_castore::proto::node::Node::File(ref fnode) => {
+                    // It's already validated.
+                    fnode.digest.clone().try_into().unwrap()
+                }
+                // We cannot hash anything else than file in flat import mode.
+                _ => {
+                    return Err(ImportError::FlatImportOfNonFile(
+                        path.to_string_lossy().to_string(),
+                    )
+                    .into())
+                }
+            };
+
+            // FUTUREWORK: avoid hashing again.
+            CAHash::Flat(NixHash::Sha256(
+                state
+                    .tokio_handle
+                    .block_on(async { state.blob_to_sha256_hash(digest).await })?,
+            ))
+        };
+
+        let obtained_hash = ca.hash().clone().into_owned();
+        let (path_info, _hash, output_path) = state.tokio_handle.block_on(async {
+            state
+                .node_to_path_info(name.as_ref(), path.as_ref(), ca, root_node)
+                .await
+        })?;
+
+        if let Some(expected_sha256) = expected_sha256 {
+            if obtained_hash != expected_sha256 {
+                Err(ImportError::HashMismatch(
+                    path.to_string_lossy().to_string(),
+                    expected_sha256,
+                    obtained_hash,
+                ))?;
+            }
+        }
+
+        let _: tvix_store::proto::PathInfo = state.tokio_handle.block_on(async {
+            // This is necessary to cause the coercion of the error type.
+            Ok::<_, std::io::Error>(state.path_info_service.as_ref().put(path_info).await?)
+        })?;
+
+        // We need to attach context to the final output path.
+        let outpath = output_path.to_absolute_path();
+
+        Ok(
+            NixString::new_context_from(NixContextElement::Plain(outpath.clone()).into(), outpath)
+                .into(),
+        )
+    }
+
+    #[builtin("filterSource")]
+    async fn builtin_filter_source(
+        state: Rc<TvixStoreIO>,
+        co: GenCo,
+        #[lazy] filter: Value,
+        path: Value,
+    ) -> Result<Value, ErrorKind> {
+        let p = path.to_path()?;
+        let root_node = filtered_ingest(Rc::clone(&state), co, &p, Some(&filter)).await?;
+        let name = tvix_store::import::path_to_name(&p)?;
+
+        let outpath = state
+            .tokio_handle
+            .block_on(async {
+                let (_, nar_sha256) = state
+                    .path_info_service
+                    .as_ref()
+                    .calculate_nar(&root_node)
+                    .await?;
+
+                state
+                    .register_node_in_path_info_service(
+                        name,
+                        &p,
+                        CAHash::Nar(NixHash::Sha256(nar_sha256)),
+                        root_node,
+                    )
+                    .await
+            })
+            .map_err(|err| ErrorKind::IO {
+                path: Some(p.to_path_buf()),
+                error: err.into(),
+            })?
+            .to_absolute_path();
+
+        Ok(
+            NixString::new_context_from(NixContextElement::Plain(outpath.clone()).into(), outpath)
+                .into(),
+        )
+    }
+}
+
+pub use import_builtins::builtins as import_builtins;
+
+use crate::tvix_store_io::TvixStoreIO;
diff --git a/tvix/glue/src/builtins/mod.rs b/tvix/glue/src/builtins/mod.rs
new file mode 100644
index 0000000000..0c7bcc880a
--- /dev/null
+++ b/tvix/glue/src/builtins/mod.rs
@@ -0,0 +1,797 @@
+//! Contains builtins that deal with the store or builder.
+
+use std::rc::Rc;
+
+use crate::tvix_store_io::TvixStoreIO;
+
+mod derivation;
+mod errors;
+mod fetchers;
+mod import;
+mod utils;
+
+pub use errors::{DerivationError, FetcherError, ImportError};
+
+/// Adds derivation-related builtins to the passed [tvix_eval::Evaluation].
+///
+/// These are `derivation` and `derivationStrict`.
+///
+/// As they need to interact with `known_paths`, we also need to pass in
+/// `known_paths`.
+pub fn add_derivation_builtins<IO>(eval: &mut tvix_eval::Evaluation<IO>, io: Rc<TvixStoreIO>) {
+    eval.builtins
+        .extend(derivation::derivation_builtins::builtins(Rc::clone(&io)));
+
+    // Add the actual `builtins.derivation` from compiled Nix code
+    eval.src_builtins
+        .push(("derivation", include_str!("derivation.nix")));
+}
+
+/// Adds fetcher builtins to the passed [tvix_eval::Evaluation]:
+///
+/// * `fetchurl`
+/// * `fetchTarball`
+/// * `fetchGit`
+pub fn add_fetcher_builtins<IO>(eval: &mut tvix_eval::Evaluation<IO>, io: Rc<TvixStoreIO>) {
+    eval.builtins
+        .extend(fetchers::fetcher_builtins::builtins(Rc::clone(&io)));
+}
+
+/// Adds import-related builtins to the passed [tvix_eval::Evaluation].
+///
+/// These are `filterSource` and `path`
+///
+/// As they need to interact with the store implementation, we pass [`TvixStoreIO`].
+pub fn add_import_builtins<IO>(eval: &mut tvix_eval::Evaluation<IO>, io: Rc<TvixStoreIO>) {
+    eval.builtins.extend(import::import_builtins(io));
+
+    // TODO(raitobezarius): evaluate expressing filterSource as Nix code using path (b/372)
+}
+
+#[cfg(test)]
+mod tests {
+    use std::{fs, rc::Rc, sync::Arc};
+
+    use crate::tvix_store_io::TvixStoreIO;
+
+    use super::{add_derivation_builtins, add_fetcher_builtins, add_import_builtins};
+    use nix_compat::store_path::hash_placeholder;
+    use rstest::rstest;
+    use tempfile::TempDir;
+    use tvix_build::buildservice::DummyBuildService;
+    use tvix_eval::{EvalIO, EvaluationResult};
+    use tvix_store::utils::construct_services;
+
+    /// evaluates a given nix expression and returns the result.
+    /// Takes care of setting up the evaluator so it knows about the
+    // `derivation` builtin.
+    fn eval(str: &str) -> EvaluationResult {
+        // We assemble a complete store in memory.
+        let runtime = tokio::runtime::Runtime::new().expect("Failed to build a Tokio runtime");
+        let (blob_service, directory_service, path_info_service) = runtime
+            .block_on(async { construct_services("memory://", "memory://", "memory://").await })
+            .expect("Failed to construct store services in memory");
+
+        let io = Rc::new(TvixStoreIO::new(
+            blob_service,
+            directory_service,
+            path_info_service.into(),
+            Arc::<DummyBuildService>::default(),
+            runtime.handle().clone(),
+        ));
+
+        let mut eval = tvix_eval::Evaluation::new(io.clone() as Rc<dyn EvalIO>, false);
+
+        add_derivation_builtins(&mut eval, Rc::clone(&io));
+        add_fetcher_builtins(&mut eval, Rc::clone(&io));
+        add_import_builtins(&mut eval, io);
+
+        // run the evaluation itself.
+        eval.evaluate(str, None)
+    }
+
+    #[test]
+    fn derivation() {
+        let result = eval(
+            r#"(derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux";}).outPath"#,
+        );
+
+        assert!(result.errors.is_empty(), "expect evaluation to succeed");
+        let value = result.value.expect("must be some");
+
+        match value {
+            tvix_eval::Value::String(s) => {
+                assert_eq!(*s, "/nix/store/xpcvxsx5sw4rbq666blz6sxqlmsqphmr-foo",);
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+    }
+
+    /// a derivation with an empty name is an error.
+    #[test]
+    fn derivation_empty_name_fail() {
+        let result = eval(
+            r#"(derivation { name = ""; builder = "/bin/sh"; system = "x86_64-linux";}).outPath"#,
+        );
+
+        assert!(!result.errors.is_empty(), "expect evaluation to fail");
+    }
+
+    /// construct some calls to builtins.derivation and compare produced output
+    /// paths.
+    #[rstest]
+    #[case::r_sha256(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha256"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath"#, "/nix/store/17wgs52s7kcamcyin4ja58njkf91ipq8-foo")]
+    #[case::r_sha256_other_name(r#"(builtins.derivation { name = "foo2"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha256"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath"#, "/nix/store/gi0p8vd635vpk1nq029cz3aa3jkhar5k-foo2")]
+    #[case::r_sha1(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha1"; outputHash = "sha1-VUCRC+16gU5lcrLYHlPSUyx0Y/Q="; }).outPath"#, "/nix/store/p5sammmhpa84ama7ymkbgwwzrilva24x-foo")]
+    #[case::r_md5(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "md5"; outputHash = "md5-07BzhNET7exJ6qYjitX/AA=="; }).outPath"#, "/nix/store/gmmxgpy1jrzs86r5y05wy6wiy2m15xgi-foo")]
+    #[case::r_sha512(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha512"; outputHash = "sha512-DPkYCnZKuoY6Z7bXLwkYvBMcZ3JkLLLc5aNPCnAvlHDdwr8SXBIZixmVwjPDS0r9NGxUojNMNQqUilG26LTmtg=="; }).outPath"#, "/nix/store/lfi2bfyyap88y45mfdwi4j99gkaxaj19-foo")]
+    #[case::r_sha256_base16(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha256"; outputHash = "4374173a8cbe88de152b609f96f46e958bcf65762017474eec5a05ec2bd61530"; }).outPath"#, "/nix/store/17wgs52s7kcamcyin4ja58njkf91ipq8-foo")]
+    #[case::r_sha256_nixbase32(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha256"; outputHash = "0c0msqmyq1asxi74f5r0frjwz2wmdvs9d7v05caxx25yihx1fx23"; }).outPath"#, "/nix/store/17wgs52s7kcamcyin4ja58njkf91ipq8-foo")]
+    #[case::r_sha256_base64(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha256"; outputHash = "Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath"#, "/nix/store/17wgs52s7kcamcyin4ja58njkf91ipq8-foo")]
+    #[case::r_sha256_base64_nopad(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha256"; outputHash = "sha256-fgIr3TyFGDAXP5+qoAaiMKDg/a1MlT6Fv/S/DaA24S8="; }).outPath"#, "/nix/store/xm1l9dx4zgycv9qdhcqqvji1z88z534b-foo")]
+    #[case::sha256(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "flat"; outputHashAlgo = "sha256"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath"#, "/nix/store/q4pkwkxdib797fhk22p0k3g1q32jmxvf-foo")]
+    #[case::sha256_other_name(r#"(builtins.derivation { name = "foo2"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "flat"; outputHashAlgo = "sha256"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath"#, "/nix/store/znw17xlmx9r6gw8izjkqxkl6s28sza4l-foo2")]
+    #[case::sha1(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "flat"; outputHashAlgo = "sha1"; outputHash = "sha1-VUCRC+16gU5lcrLYHlPSUyx0Y/Q="; }).outPath"#, "/nix/store/zgpnjjmga53d8srp8chh3m9fn7nnbdv6-foo")]
+    #[case::md5(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "flat"; outputHashAlgo = "md5"; outputHash = "md5-07BzhNET7exJ6qYjitX/AA=="; }).outPath"#, "/nix/store/jfhcwnq1852ccy9ad9nakybp2wadngnd-foo")]
+    #[case::sha512(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "flat"; outputHashAlgo = "sha512"; outputHash = "sha512-DPkYCnZKuoY6Z7bXLwkYvBMcZ3JkLLLc5aNPCnAvlHDdwr8SXBIZixmVwjPDS0r9NGxUojNMNQqUilG26LTmtg=="; }).outPath"#, "/nix/store/as736rr116ian9qzg457f96j52ki8bm3-foo")]
+    #[case::r_sha256_outputhashalgo_omitted(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath"#, "/nix/store/17wgs52s7kcamcyin4ja58njkf91ipq8-foo")]
+    #[case::r_sha256_outputhashalgo_and_outputhashmode_omitted(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath"#, "/nix/store/q4pkwkxdib797fhk22p0k3g1q32jmxvf-foo")]
+    #[case::outputhash_omitted(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; }).outPath"#, "/nix/store/xpcvxsx5sw4rbq666blz6sxqlmsqphmr-foo")]
+    #[case::multiple_outputs(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; outputs = ["foo" "bar"]; system = "x86_64-linux"; }).outPath"#, "/nix/store/hkwdinvz2jpzgnjy9lv34d2zxvclj4s3-foo-foo")]
+    #[case::args(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; args = ["--foo" "42" "--bar"]; system = "x86_64-linux"; }).outPath"#, "/nix/store/365gi78n2z7vwc1bvgb98k0a9cqfp6as-foo")]
+    #[case::full(r#"
+                   let
+                     bar = builtins.derivation {
+                       name = "bar";
+                       builder = ":";
+                       system = ":";
+                       outputHash = "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba";
+                       outputHashAlgo = "sha256";
+                       outputHashMode = "recursive";
+                     };
+                   in
+                   (builtins.derivation {
+                     name = "foo";
+                     builder = ":";
+                     system = ":";
+                     inherit bar;
+                   }).outPath
+        "#, "/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo")]
+    #[case::pass_as_file(r#"(builtins.derivation { "name" = "foo"; passAsFile = ["bar"]; bar = "baz"; system = ":"; builder = ":";}).outPath"#, "/nix/store/25gf0r1ikgmh4vchrn8qlc4fnqlsa5a1-foo")]
+    // __ignoreNulls = true, but nothing set to null
+    #[case::ignore_nulls_true_no_arg_drvpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = true; }).drvPath"#, "/nix/store/xa96w6d7fxrlkk60z1fmx2ffp2wzmbqx-foo.drv")]
+    #[case::ignore_nulls_true_no_arg_outpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = true; }).outPath"#, "/nix/store/pk2agn9za8r9bxsflgh1y7fyyrmwcqkn-foo")]
+    // __ignoreNulls = true, with a null arg, same paths
+    #[case::ignore_nulls_true_drvpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = true; ignoreme = null; }).drvPath"#, "/nix/store/xa96w6d7fxrlkk60z1fmx2ffp2wzmbqx-foo.drv")]
+    #[case::ignore_nulls_true_outpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = true; ignoreme = null; }).outPath"#, "/nix/store/pk2agn9za8r9bxsflgh1y7fyyrmwcqkn-foo")]
+    // __ignoreNulls = false
+    #[case::ignore_nulls_false_no_arg_drvpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; }).drvPath"#, "/nix/store/xa96w6d7fxrlkk60z1fmx2ffp2wzmbqx-foo.drv")]
+    #[case::ignore_nulls_false_no_arg_outpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; }).outPath"#, "/nix/store/pk2agn9za8r9bxsflgh1y7fyyrmwcqkn-foo")]
+    // __ignoreNulls = false, with a null arg
+    #[case::ignore_nulls_fales_arg_path_drvpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; foo = null; }).drvPath"#, "/nix/store/xwkwbajfiyhdqmksrbzm0s4g4ib8d4ms-foo.drv")]
+    #[case::ignore_nulls_fales_arg_path_outpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; foo = null; }).outPath"#, "/nix/store/2n2jqm6l7r2ahi19m58pl896ipx9cyx6-foo")]
+    // structured attrs set to false will render an empty string inside env
+    #[case::structured_attrs_false_drvpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = false; foo = "bar"; }).drvPath"#, "/nix/store/qs39krwr2lsw6ac910vqx4pnk6m63333-foo.drv")]
+    #[case::structured_attrs_false_outpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = false; foo = "bar"; }).outPath"#, "/nix/store/9yy3764rdip3fbm8ckaw4j9y7vh4d231-foo")]
+    // simple structured attrs
+    #[case::structured_attrs_simple_drvpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = true; foo = "bar"; }).drvPath"#, "/nix/store/k6rlb4k10cb9iay283037ml1nv3xma2f-foo.drv")]
+    #[case::structured_attrs_simple_outpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = true; foo = "bar"; }).outPath"#, "/nix/store/6lmv3hyha1g4cb426iwjyifd7nrdv1xn-foo")]
+    // structured attrs with outputsCheck
+    #[case::structured_attrs_output_checks_drvpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = true; foo = "bar"; outputChecks = {out = {maxClosureSize = 256 * 1024 * 1024; disallowedRequisites = [ "dev" ];};}; }).drvPath"#, "/nix/store/fx9qzpchh5wchchhy39bwsml978d6wp1-foo.drv")]
+    #[case::structured_attrs_output_checks_outpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = true; foo = "bar"; outputChecks = {out = {maxClosureSize = 256 * 1024 * 1024; disallowedRequisites = [ "dev" ];};}; }).outPath"#, "/nix/store/pcywah1nwym69rzqdvpp03sphfjgyw1l-foo")]
+    // structured attrs and __ignoreNulls. ignoreNulls is inactive (so foo ends up in __json, yet __ignoreNulls itself is not present.
+    #[case::structured_attrs_and_ignore_nulls_drvpath(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; foo = null; __structuredAttrs = true; }).drvPath"#, "/nix/store/rldskjdcwa3p7x5bqy3r217va1jsbjsc-foo.drv")]
+    // structured attrs, setting outputs.
+    #[case::structured_attrs_outputs_drvpath(r#"(builtins.derivation { name = "test"; system = "aarch64-linux"; builder = "/bin/sh"; __structuredAttrs = true; outputs = [ "out"]; }).drvPath"#, "/nix/store/6sgawp30zibsh525p7c948xxd22y2ngy-test.drv")]
+    fn test_outpath(#[case] code: &str, #[case] expected_path: &str) {
+        let value = eval(code).value.expect("must succeed");
+
+        match value {
+            tvix_eval::Value::String(s) => {
+                assert_eq!(*s, expected_path);
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+    }
+
+    /// construct some calls to builtins.derivation that should be rejected
+    #[rstest]
+    #[case::invalid_outputhash(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha256"; outputHash = "sha256-00"; }).outPath"#)]
+    #[case::sha1_and_sha256(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha1"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath"#)]
+    #[case::duplicate_output_names(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; outputs = ["foo" "foo"]; system = "x86_64-linux"; }).outPath"#)]
+    fn test_outpath_invalid(#[case] code: &str) {
+        let resp = eval(code);
+        assert!(resp.value.is_none(), "Value should be None");
+        assert!(
+            !resp.errors.is_empty(),
+            "There should have been some errors"
+        );
+    }
+
+    /// Construct two FODs with the same name, and same known output (but
+    /// slightly different recipe), ensure they have the same output hash.
+    #[test]
+    fn test_fod_outpath() {
+        let code = r#"
+          (builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath ==
+          (builtins.derivation { name = "foo"; builder = "/bin/aa"; system = "x86_64-linux"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath
+        "#;
+
+        let value = eval(code).value.expect("must succeed");
+        match value {
+            tvix_eval::Value::Bool(v) => {
+                assert!(v);
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+    }
+
+    /// Construct two FODs with the same name, and same known output (but
+    /// slightly different recipe), ensure they have the same output hash.
+    #[test]
+    fn test_fod_outpath_different_name() {
+        let code = r#"
+          (builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath ==
+          (builtins.derivation { name = "foo"; builder = "/bin/aa"; system = "x86_64-linux"; outputHash = "sha256-Q3QXOoy+iN4VK2CflvRulYvPZXYgF0dO7FoF7CvWFTA="; }).outPath
+        "#;
+
+        let value = eval(code).value.expect("must succeed");
+        match value {
+            tvix_eval::Value::Bool(v) => {
+                assert!(v);
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+    }
+
+    /// Construct two derivations with the same parameters except one of them lost a context string
+    /// for a dependency, causing the loss of an element in the `inputDrvs` derivation. Therefore,
+    /// making `outPath` different.
+    #[test]
+    fn test_unsafe_discard_string_context() {
+        let code = r#"
+        let
+            dep = builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; };
+        in
+          (builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; env = "${dep}"; }).outPath !=
+          (builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; env = "${builtins.unsafeDiscardStringContext dep}"; }).outPath
+        "#;
+
+        let value = eval(code).value.expect("must succeed");
+        match value {
+            tvix_eval::Value::Bool(v) => {
+                assert!(v);
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+    }
+
+    /// Construct an attribute set that coerces to a derivation and verify that the return type is
+    /// a string.
+    #[test]
+    fn test_unsafe_discard_string_context_of_coercible() {
+        let code = r#"
+        let
+            dep = builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; };
+            attr = { __toString = _: dep; };
+        in
+            builtins.typeOf (builtins.unsafeDiscardStringContext attr) == "string"
+        "#;
+
+        let value = eval(code).value.expect("must succeed");
+        match value {
+            tvix_eval::Value::Bool(v) => {
+                assert!(v);
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+    }
+
+    #[rstest]
+    #[case::input_in_args(r#"
+                   let
+                     bar = builtins.derivation {
+                       name = "bar";
+                       builder = ":";
+                       system = ":";
+                       outputHash = "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba";
+                       outputHashAlgo = "sha256";
+                       outputHashMode = "recursive";
+                     };
+                   in
+                   (builtins.derivation {
+                     name = "foo";
+                     builder = ":";
+                     args = [ "${bar}" ];
+                     system = ":";
+                   }).drvPath
+        "#, "/nix/store/50yl2gmmljyl0lzyrp1mcyhn53vhjhkd-foo.drv")]
+    fn test_inputs_derivation_from_context(#[case] code: &str, #[case] expected_drvpath: &str) {
+        let eval_result = eval(code);
+
+        let value = eval_result.value.expect("must succeed");
+
+        match value {
+            tvix_eval::Value::String(s) => {
+                assert_eq!(*s, expected_drvpath);
+            }
+
+            _ => panic!("unexpected value type: {:?}", value),
+        };
+    }
+
+    #[test]
+    fn builtins_placeholder_hashes() {
+        assert_eq!(
+            hash_placeholder("out").as_str(),
+            "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"
+        );
+
+        assert_eq!(
+            hash_placeholder("").as_str(),
+            "/171rf4jhx57xqz3p7swniwkig249cif71pa08p80mgaf0mqz5bmr"
+        );
+    }
+
+    /// constructs calls to builtins.derivation that should succeed, but produce warnings
+    #[rstest]
+    #[case::r_sha256_wrong_padding(r#"(builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; outputHashMode = "recursive"; outputHashAlgo = "sha256"; outputHash = "sha256-fgIr3TyFGDAXP5+qoAaiMKDg/a1MlT6Fv/S/DaA24S8===="; }).outPath"#, "/nix/store/xm1l9dx4zgycv9qdhcqqvji1z88z534b-foo")]
+    fn builtins_derivation_hash_wrong_padding_warn(
+        #[case] code: &str,
+        #[case] expected_path: &str,
+    ) {
+        let eval_result = eval(code);
+
+        let value = eval_result.value.expect("must succeed");
+
+        match value {
+            tvix_eval::Value::String(s) => {
+                assert_eq!(*s, expected_path);
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+
+        assert!(
+            !eval_result.warnings.is_empty(),
+            "warnings should not be empty"
+        );
+    }
+
+    /// Invokes `builtins.filterSource` on various carefully-crated subdirs, and
+    /// ensures the resulting store paths matches what Nix produces.
+    /// @fixtures is replaced to the fixtures directory.
+    #[rstest]
+    #[cfg(target_family = "unix")]
+    #[case::complicated_filter_nothing(
+        r#"(builtins.filterSource (p: t: true) @fixtures)"#,
+        "/nix/store/bqh6kd0x3vps2rzagzpl7qmbbgnx19cp-import_fixtures"
+    )]
+    #[case::complicated_filter_everything(
+        r#"(builtins.filterSource (p: t: false) @fixtures)"#,
+        "/nix/store/giq6czz24lpjg97xxcxk6rg950lcpib1-import_fixtures"
+    )]
+    #[case::simple_dir_with_one_file_filter_dirs(
+        r#"(builtins.filterSource (p: t: t != "directory") @fixtures/a_dir)"#,
+        "/nix/store/8vbqaxapywkvv1hacdja3pi075r14d43-a_dir"
+    )]
+    #[case::simple_dir_with_one_file_filter_files(
+        r#"(builtins.filterSource (p: t: t != "regular") @fixtures/a_dir)"#,
+        "/nix/store/zphlqc93s2iq4xm393l06hzf8hp85r4z-a_dir"
+    )]
+    #[case::simple_dir_with_one_file_filter_symlinks(
+        r#"(builtins.filterSource (p: t: t != "symlink") @fixtures/a_dir)"#,
+        "/nix/store/8vbqaxapywkvv1hacdja3pi075r14d43-a_dir"
+    )]
+    #[case::simple_dir_with_one_file_filter_nothing(
+        r#"(builtins.filterSource (p: t: true) @fixtures/a_dir)"#,
+        "/nix/store/8vbqaxapywkvv1hacdja3pi075r14d43-a_dir"
+    )]
+    #[case::simple_dir_with_one_file_filter_everything(
+        r#"(builtins.filterSource (p: t: false) @fixtures/a_dir)"#,
+        "/nix/store/zphlqc93s2iq4xm393l06hzf8hp85r4z-a_dir"
+    )]
+    #[case::simple_dir_with_one_dir_filter_dirs(
+        r#"builtins.filterSource (p: t: t != "directory") @fixtures/b_dir"#,
+        "/nix/store/xzsfzdgrxg93icaamjm8zq1jq6xvf2fz-b_dir"
+    )]
+    #[case::simple_dir_with_one_dir_filter_files(
+        r#"builtins.filterSource (p: t: t != "regular") @fixtures/b_dir"#,
+        "/nix/store/8rjx64mm7173xp60rahv7cl3ixfkv3rf-b_dir"
+    )]
+    #[case::simple_dir_with_one_dir_filter_symlinks(
+        r#"builtins.filterSource (p: t: t != "symlink") @fixtures/b_dir"#,
+        "/nix/store/8rjx64mm7173xp60rahv7cl3ixfkv3rf-b_dir"
+    )]
+    #[case::simple_dir_with_one_dir_filter_nothing(
+        r#"builtins.filterSource (p: t: true) @fixtures/b_dir"#,
+        "/nix/store/8rjx64mm7173xp60rahv7cl3ixfkv3rf-b_dir"
+    )]
+    #[case::simple_dir_with_one_dir_filter_everything(
+        r#"builtins.filterSource (p: t: false) @fixtures/b_dir"#,
+        "/nix/store/xzsfzdgrxg93icaamjm8zq1jq6xvf2fz-b_dir"
+    )]
+    #[case::simple_dir_with_one_symlink_to_file_filter_dirs(
+        r#"builtins.filterSource (p: t: t != "directory") @fixtures/c_dir"#,
+        "/nix/store/riigfmmzzrq65zqiffcjk5sbqr9c9h09-c_dir"
+    )]
+    #[case::simple_dir_with_one_symlink_to_file_filter_files(
+        r#"builtins.filterSource (p: t: t != "regular") @fixtures/c_dir"#,
+        "/nix/store/riigfmmzzrq65zqiffcjk5sbqr9c9h09-c_dir"
+    )]
+    #[case::simple_dir_with_one_symlink_to_file_filter_symlinks(
+        r#"builtins.filterSource (p: t: t != "symlink") @fixtures/c_dir"#,
+        "/nix/store/y5g1fz04vzjvf422q92qmv532axj5q26-c_dir"
+    )]
+    #[case::simple_dir_with_one_symlink_to_file_filter_nothing(
+        r#"builtins.filterSource (p: t: true) @fixtures/c_dir"#,
+        "/nix/store/riigfmmzzrq65zqiffcjk5sbqr9c9h09-c_dir"
+    )]
+    #[case::simple_dir_with_one_symlink_to_file_filter_everything(
+        r#"builtins.filterSource (p: t: false) @fixtures/c_dir"#,
+        "/nix/store/y5g1fz04vzjvf422q92qmv532axj5q26-c_dir"
+    )]
+    #[case::simple_dir_with_dangling_symlink_filter_dirs(
+        r#"builtins.filterSource (p: t: t != "directory") @fixtures/d_dir"#,
+        "/nix/store/f2d1aixwiqy4lbzrd040ala2s4m2z199-d_dir"
+    )]
+    #[case::simple_dir_with_dangling_symlink_filter_files(
+        r#"builtins.filterSource (p: t: t != "regular") @fixtures/d_dir"#,
+        "/nix/store/f2d1aixwiqy4lbzrd040ala2s4m2z199-d_dir"
+    )]
+    #[case::simple_dir_with_dangling_symlink_filter_symlinks(
+        r#"builtins.filterSource (p: t: t != "symlink") @fixtures/d_dir"#,
+        "/nix/store/7l371xax8kknhpska4wrmyll1mzlhzvl-d_dir"
+    )]
+    #[case::simple_dir_with_dangling_symlink_filter_nothing(
+        r#"builtins.filterSource (p: t: true) @fixtures/d_dir"#,
+        "/nix/store/f2d1aixwiqy4lbzrd040ala2s4m2z199-d_dir"
+    )]
+    #[case::simple_dir_with_dangling_symlink_filter_everything(
+        r#"builtins.filterSource (p: t: false) @fixtures/d_dir"#,
+        "/nix/store/7l371xax8kknhpska4wrmyll1mzlhzvl-d_dir"
+    )]
+    #[case::simple_symlinked_dir_with_one_file_filter_dirs(
+        r#"builtins.filterSource (p: t: t != "directory") @fixtures/symlink_to_a_dir"#,
+        "/nix/store/apmdprm8fwl2zrjpbyfcd99zrnhvf47q-symlink_to_a_dir"
+    )]
+    #[case::simple_symlinked_dir_with_one_file_filter_files(
+        r#"builtins.filterSource (p: t: t != "regular") @fixtures/symlink_to_a_dir"#,
+        "/nix/store/apmdprm8fwl2zrjpbyfcd99zrnhvf47q-symlink_to_a_dir"
+    )]
+    #[case::simple_symlinked_dir_with_one_file_filter_symlinks(
+        r#"builtins.filterSource (p: t: t != "symlink") @fixtures/symlink_to_a_dir"#,
+        "/nix/store/apmdprm8fwl2zrjpbyfcd99zrnhvf47q-symlink_to_a_dir"
+    )]
+    #[case::simple_symlinked_dir_with_one_file_filter_nothing(
+        r#"builtins.filterSource (p: t: true) @fixtures/symlink_to_a_dir"#,
+        "/nix/store/apmdprm8fwl2zrjpbyfcd99zrnhvf47q-symlink_to_a_dir"
+    )]
+    #[case::simple_symlinked_dir_with_one_file_filter_everything(
+        r#"builtins.filterSource (p: t: false) @fixtures/symlink_to_a_dir"#,
+        "/nix/store/apmdprm8fwl2zrjpbyfcd99zrnhvf47q-symlink_to_a_dir"
+    )]
+    fn builtins_filter_source_succeed(#[case] code: &str, #[case] expected_outpath: &str) {
+        // populate the fixtures dir
+        let temp = TempDir::new().expect("create temporary directory");
+        let p = temp.path().join("import_fixtures");
+
+        // create the fixtures directory.
+        // We produce them at runtime rather than shipping it inside the source
+        // tree, as git can't model certain things - like directories without any
+        // items.
+        {
+            fs::create_dir(&p).expect("creating import_fixtures");
+
+            // `/a_dir` contains an empty `a_file` file
+            fs::create_dir(p.join("a_dir")).expect("creating /a_dir");
+            fs::write(p.join("a_dir").join("a_file"), "").expect("creating /a_dir/a_file");
+
+            // `/a_file` is an empty file
+            fs::write(p.join("a_file"), "").expect("creating /a_file");
+
+            // `/b_dir` contains an empty "a_dir" directory
+            fs::create_dir_all(p.join("b_dir").join("a_dir")).expect("creating /b_dir/a_dir");
+
+            // `/c_dir` contains a `symlink_to_a_file` symlink, pointing to `../a_dir/a_file`.
+            fs::create_dir(p.join("c_dir")).expect("creating /c_dir");
+            std::os::unix::fs::symlink(
+                "../a_dir/a_file",
+                p.join("c_dir").join("symlink_to_a_file"),
+            )
+            .expect("creating /c_dir/symlink_to_a_file");
+
+            // `/d_dir` contains a `dangling_symlink`, pointing to `a_dir/a_file`,
+            // which does not exist.
+            fs::create_dir(p.join("d_dir")).expect("creating /d_dir");
+            std::os::unix::fs::symlink("a_dir/a_file", p.join("d_dir").join("dangling_symlink"))
+                .expect("creating /d_dir/dangling_symlink");
+
+            // `/symlink_to_a_dir` is a symlink to `a_dir`, which exists.
+            std::os::unix::fs::symlink("a_dir", p.join("symlink_to_a_dir"))
+                .expect("creating /symlink_to_a_dir");
+        }
+
+        // replace @fixtures with the temporary path containing the fixtures
+        let code_replaced = code.replace("@fixtures", &p.to_string_lossy());
+
+        let eval_result = eval(&code_replaced);
+
+        let value = eval_result.value.expect("must succeed");
+
+        match value {
+            tvix_eval::Value::String(s) => {
+                assert_eq!(expected_outpath, s.as_bstr());
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+
+        assert!(eval_result.errors.is_empty(), "errors should be empty");
+    }
+
+    // Space is an illegal character.
+    #[rstest]
+    #[case(
+        r#"(builtins.path { name = "valid-name"; path = @fixtures + "/te st"; recursive = true; })"#,
+        true
+    )]
+    // Space is still an illegal character.
+    #[case(
+        r#"(builtins.path { name = "invalid name"; path = @fixtures + "/te st"; recursive = true; })"#,
+        false
+    )]
+    fn builtins_path_recursive_rename(#[case] code: &str, #[case] success: bool) {
+        // populate the fixtures dir
+        let temp = TempDir::new().expect("create temporary directory");
+        let p = temp.path().join("import_fixtures");
+
+        // create the fixtures directory.
+        // We produce them at runtime rather than shipping it inside the source
+        // tree, as git can't model certain things - like directories without any
+        // items.
+        {
+            fs::create_dir(&p).expect("creating import_fixtures");
+            fs::write(p.join("te st"), "").expect("creating `/te st`");
+        }
+        // replace @fixtures with the temporary path containing the fixtures
+        let code_replaced = code.replace("@fixtures", &p.to_string_lossy());
+
+        let eval_result = eval(&code_replaced);
+
+        let value = eval_result.value;
+
+        if success {
+            match value.expect("expected successful evaluation on legal rename") {
+                tvix_eval::Value::String(s) => {
+                    assert_eq!(
+                        "/nix/store/nd5z11x7zjqqz44rkbhc6v7yifdkn659-valid-name",
+                        s.as_bstr()
+                    );
+                }
+                v => panic!("unexpected value type: {:?}", v),
+            }
+        } else {
+            assert!(value.is_none(), "unexpected success on illegal store paths");
+        }
+    }
+
+    // Space is an illegal character.
+    #[rstest]
+    #[case(
+        r#"(builtins.path { name = "valid-name"; path = @fixtures + "/te st"; recursive = false; })"#,
+        true
+    )]
+    // Space is still an illegal character.
+    #[case(
+        r#"(builtins.path { name = "invalid name"; path = @fixtures + "/te st"; recursive = false; })"#,
+        false
+    )]
+    // The non-recursive variant passes explicitly `recursive = false;`
+    fn builtins_path_nonrecursive_rename(#[case] code: &str, #[case] success: bool) {
+        // populate the fixtures dir
+        let temp = TempDir::new().expect("create temporary directory");
+        let p = temp.path().join("import_fixtures");
+
+        // create the fixtures directory.
+        // We produce them at runtime rather than shipping it inside the source
+        // tree, as git can't model certain things - like directories without any
+        // items.
+        {
+            fs::create_dir(&p).expect("creating import_fixtures");
+            fs::write(p.join("te st"), "").expect("creating `/te st`");
+        }
+        // replace @fixtures with the temporary path containing the fixtures
+        let code_replaced = code.replace("@fixtures", &p.to_string_lossy());
+
+        let eval_result = eval(&code_replaced);
+
+        let value = eval_result.value;
+
+        if success {
+            match value.expect("expected successful evaluation on legal rename") {
+                tvix_eval::Value::String(s) => {
+                    assert_eq!(
+                        "/nix/store/il2rmfbqgs37rshr8w7x64hd4d3b4bsa-valid-name",
+                        s.as_bstr()
+                    );
+                }
+                v => panic!("unexpected value type: {:?}", v),
+            }
+        } else {
+            assert!(value.is_none(), "unexpected success on illegal store paths");
+        }
+    }
+
+    #[rstest]
+    #[case(
+        r#"(builtins.path { name = "valid-name"; path = @fixtures + "/te st"; recursive = false; sha256 = "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="; })"#,
+        true
+    )]
+    #[case(
+        r#"(builtins.path { name = "valid-name"; path = @fixtures + "/te st"; recursive = true; sha256 = "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="; })"#,
+        false
+    )]
+    #[case(
+        r#"(builtins.path { name = "valid-name"; path = @fixtures + "/te st"; recursive = true; sha256 = "sha256-d6xi4mKdjkX2JFicDIv5niSzpyI0m/Hnm8GGAIU04kY="; })"#,
+        true
+    )]
+    #[case(
+        r#"(builtins.path { name = "valid-name"; path = @fixtures + "/te st"; recursive = false; sha256 = "sha256-d6xi4mKdjkX2JFicDIv5niSzpyI0m/Hnm8GGAIU04kY="; })"#,
+        false
+    )]
+    fn builtins_path_fod_locking(#[case] code: &str, #[case] exp_success: bool) {
+        // populate the fixtures dir
+        let temp = TempDir::new().expect("create temporary directory");
+        let p = temp.path().join("import_fixtures");
+
+        // create the fixtures directory.
+        // We produce them at runtime rather than shipping it inside the source
+        // tree, as git can't model certain things - like directories without any
+        // items.
+        {
+            fs::create_dir(&p).expect("creating import_fixtures");
+            fs::write(p.join("te st"), "").expect("creating `/te st`");
+        }
+        // replace @fixtures with the temporary path containing the fixtures
+        let code_replaced = code.replace("@fixtures", &p.to_string_lossy());
+
+        let eval_result = eval(&code_replaced);
+
+        let value = eval_result.value;
+
+        if exp_success {
+            assert!(
+                value.is_some(),
+                "expected successful evaluation on legal rename and valid FOD sha256"
+            );
+        } else {
+            assert!(value.is_none(), "unexpected success on invalid FOD sha256");
+        }
+    }
+
+    #[rstest]
+    #[case(
+        r#"(builtins.path { name = "valid-path"; path = @fixtures + "/te st dir"; filter = _: _: true; })"#,
+        "/nix/store/i28jmi4fwym4fw3flkrkp2mdxx50pdy0-valid-path"
+    )]
+    #[case(
+        r#"(builtins.path { name = "valid-path"; path = @fixtures + "/te st dir"; filter = _: _: false; })"#,
+        "/nix/store/pwza2ij9gk1fmzhbjnynmfv2mq2sgcap-valid-path"
+    )]
+    fn builtins_path_filter(#[case] code: &str, #[case] expected_outpath: &str) {
+        // populate the fixtures dir
+        let temp = TempDir::new().expect("create temporary directory");
+        let p = temp.path().join("import_fixtures");
+
+        // create the fixtures directory.
+        // We produce them at runtime rather than shipping it inside the source
+        // tree, as git can't model certain things - like directories without any
+        // items.
+        {
+            fs::create_dir(&p).expect("creating import_fixtures");
+            fs::create_dir(p.join("te st dir")).expect("creating `/te st dir`");
+            fs::write(p.join("te st dir").join("test"), "").expect("creating `/te st dir/test`");
+        }
+        // replace @fixtures with the temporary path containing the fixtures
+        let code_replaced = code.replace("@fixtures", &p.to_string_lossy());
+
+        let eval_result = eval(&code_replaced);
+
+        let value = eval_result.value.expect("must succeed");
+
+        match value {
+            tvix_eval::Value::String(s) => {
+                assert_eq!(expected_outpath, s.as_bstr());
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+
+        assert!(eval_result.errors.is_empty(), "errors should be empty");
+    }
+
+    // All tests filter out some unsupported (not representable in castore) nodes, confirming
+    // invalid, but filtered-out nodes don't prevent ingestion of a path.
+    #[rstest]
+    #[cfg(target_family = "unix")]
+    // There is a set of invalid filetypes.
+    // We write various filter functions filtering them out, but usually leaving
+    // some behind.
+    // In case there's still invalid filetypes left after the filtering, we
+    // expect the evaluation to fail.
+    #[case::fail_kept_unknowns(
+        r#"(builtins.filterSource (p: t: t == "unknown") @fixtures)"#,
+        false
+    )]
+    // We filter all invalid filetypes, so the evaluation has to succeed.
+    #[case::succeed_filter_unknowns(
+        r#"(builtins.filterSource (p: t: t != "unknown") @fixtures)"#,
+        true
+    )]
+    #[case::fail_kept_charnode(
+        r#"(builtins.filterSource (p: t: (builtins.baseNameOf p) != "a_charnode") @fixtures)"#,
+        false
+    )]
+    #[case::fail_kept_socket(
+        r#"(builtins.filterSource (p: t: (builtins.baseNameOf p) != "a_socket") @fixtures)"#,
+        false
+    )]
+    #[case::fail_kept_fifo(
+        r#"(builtins.filterSource (p: t: (builtins.baseNameOf p) != "a_fifo") @fixtures)"#,
+        false
+    )]
+    fn builtins_filter_source_unsupported_files(#[case] code: &str, #[case] exp_success: bool) {
+        use nix::errno::Errno;
+        use nix::sys::stat;
+        use nix::unistd;
+        use std::os::unix::net::UnixListener;
+        use tempfile::TempDir;
+
+        // We prepare a directory containing some unsupported file nodes:
+        // - character device
+        // - socket
+        // - FIFO
+        // and we run the evaluation inside that CWD.
+        //
+        // block devices cannot be tested because we don't have the right permissions.
+        let temp = TempDir::with_prefix("foo").expect("Failed to create a temporary directory");
+
+        // read, write, execute to the owner.
+        unistd::mkfifo(&temp.path().join("a_fifo"), stat::Mode::S_IRWXU)
+            .expect("Failed to create the FIFO");
+
+        UnixListener::bind(temp.path().join("a_socket")).expect("Failed to create the socket");
+
+        stat::mknod(
+            &temp.path().join("a_charnode"),
+            stat::SFlag::S_IFCHR,
+            stat::Mode::S_IRWXU,
+            0,
+        )
+        .inspect_err(|e| {
+            if *e == Errno::EPERM {
+                eprintln!(
+                    "\
+Missing permissions to create a character device node with mknod(2).
+Please run this test as root or set CAP_MKNOD."
+                );
+            }
+        })
+        .expect("Failed to create a character device node");
+
+        let code_replaced = code.replace("@fixtures", &temp.path().to_string_lossy());
+        let eval_result = eval(&code_replaced);
+
+        if exp_success {
+            assert!(
+                eval_result.value.is_some(),
+                "unexpected failure on a directory of unsupported file types but all filtered: {:?}",
+                eval_result.errors
+            );
+        } else {
+            assert!(
+                eval_result.value.is_none(),
+                "unexpected success on unsupported file type ingestion: {:?}",
+                eval_result.value
+            );
+        }
+    }
+}
diff --git a/tvix/glue/src/builtins/utils.rs b/tvix/glue/src/builtins/utils.rs
new file mode 100644
index 0000000000..586169beeb
--- /dev/null
+++ b/tvix/glue/src/builtins/utils.rs
@@ -0,0 +1,36 @@
+use bstr::ByteSlice;
+use tvix_eval::{
+    generators::{self, GenCo},
+    CatchableErrorKind, CoercionKind, ErrorKind, NixAttrs, NixString, Value,
+};
+
+pub(super) async fn strong_importing_coerce_to_string(
+    co: &GenCo,
+    val: Value,
+) -> Result<NixString, CatchableErrorKind> {
+    let val = generators::request_force(co, val).await;
+    generators::request_string_coerce(
+        co,
+        val,
+        CoercionKind {
+            strong: true,
+            import_paths: true,
+        },
+    )
+    .await
+}
+
+pub(super) async fn select_string(
+    co: &GenCo,
+    attrs: &NixAttrs,
+    key: &str,
+) -> Result<Result<Option<String>, CatchableErrorKind>, ErrorKind> {
+    if let Some(attr) = attrs.select(key) {
+        match strong_importing_coerce_to_string(co, attr.clone()).await {
+            Err(cek) => return Ok(Err(cek)),
+            Ok(str) => return Ok(Ok(Some(str.to_str()?.to_owned()))),
+        }
+    }
+
+    Ok(Ok(None))
+}
diff --git a/tvix/glue/src/fetchers/decompression.rs b/tvix/glue/src/fetchers/decompression.rs
new file mode 100644
index 0000000000..f96fa60e34
--- /dev/null
+++ b/tvix/glue/src/fetchers/decompression.rs
@@ -0,0 +1,222 @@
+#![allow(dead_code)] // TODO
+
+use std::{
+    io, mem,
+    pin::Pin,
+    task::{Context, Poll},
+};
+
+use async_compression::tokio::bufread::{BzDecoder, GzipDecoder, XzDecoder};
+use futures::ready;
+use pin_project::pin_project;
+use tokio::io::{AsyncBufRead, AsyncRead, BufReader, ReadBuf};
+
+const GZIP_MAGIC: [u8; 2] = [0x1f, 0x8b];
+const BZIP2_MAGIC: [u8; 3] = *b"BZh";
+const XZ_MAGIC: [u8; 6] = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00];
+const BYTES_NEEDED: usize = 6;
+
+#[derive(Debug, Clone, Copy)]
+enum Algorithm {
+    Gzip,
+    Bzip2,
+    Xz,
+}
+
+impl Algorithm {
+    fn from_magic(magic: &[u8]) -> Option<Self> {
+        if magic.starts_with(&GZIP_MAGIC) {
+            Some(Self::Gzip)
+        } else if magic.starts_with(&BZIP2_MAGIC) {
+            Some(Self::Bzip2)
+        } else if magic.starts_with(&XZ_MAGIC) {
+            Some(Self::Xz)
+        } else {
+            None
+        }
+    }
+}
+
+#[pin_project]
+struct WithPreexistingBuffer<R> {
+    buffer: Vec<u8>,
+    #[pin]
+    inner: R,
+}
+
+impl<R> AsyncRead for WithPreexistingBuffer<R>
+where
+    R: AsyncRead,
+{
+    fn poll_read(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &mut ReadBuf<'_>,
+    ) -> Poll<io::Result<()>> {
+        let this = self.project();
+        if !this.buffer.is_empty() {
+            // TODO: check if the buffer fits first
+            buf.put_slice(this.buffer);
+            this.buffer.clear();
+        }
+        this.inner.poll_read(cx, buf)
+    }
+}
+
+#[pin_project(project = DecompressedReaderInnerProj)]
+enum DecompressedReaderInner<R> {
+    Unknown {
+        buffer: Vec<u8>,
+        #[pin]
+        inner: Option<R>,
+    },
+    Gzip(#[pin] GzipDecoder<BufReader<WithPreexistingBuffer<R>>>),
+    Bzip2(#[pin] BzDecoder<BufReader<WithPreexistingBuffer<R>>>),
+    Xz(#[pin] XzDecoder<BufReader<WithPreexistingBuffer<R>>>),
+}
+
+impl<R> DecompressedReaderInner<R>
+where
+    R: AsyncBufRead,
+{
+    fn switch_to(&mut self, algorithm: Algorithm) {
+        let (buffer, inner) = match self {
+            DecompressedReaderInner::Unknown { buffer, inner } => {
+                (mem::take(buffer), inner.take().unwrap())
+            }
+            DecompressedReaderInner::Gzip(_)
+            | DecompressedReaderInner::Bzip2(_)
+            | DecompressedReaderInner::Xz(_) => unreachable!(),
+        };
+        let inner = BufReader::new(WithPreexistingBuffer { buffer, inner });
+
+        *self = match algorithm {
+            Algorithm::Gzip => Self::Gzip(GzipDecoder::new(inner)),
+            Algorithm::Bzip2 => Self::Bzip2(BzDecoder::new(inner)),
+            Algorithm::Xz => Self::Xz(XzDecoder::new(inner)),
+        }
+    }
+}
+
+impl<R> AsyncRead for DecompressedReaderInner<R>
+where
+    R: AsyncBufRead,
+{
+    fn poll_read(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &mut ReadBuf<'_>,
+    ) -> Poll<io::Result<()>> {
+        match self.project() {
+            DecompressedReaderInnerProj::Unknown { .. } => {
+                unreachable!("Can't call poll_read on Unknown")
+            }
+            DecompressedReaderInnerProj::Gzip(inner) => inner.poll_read(cx, buf),
+            DecompressedReaderInnerProj::Bzip2(inner) => inner.poll_read(cx, buf),
+            DecompressedReaderInnerProj::Xz(inner) => inner.poll_read(cx, buf),
+        }
+    }
+}
+
+#[pin_project]
+pub struct DecompressedReader<R> {
+    #[pin]
+    inner: DecompressedReaderInner<R>,
+    switch_to: Option<Algorithm>,
+}
+
+impl<R> DecompressedReader<R> {
+    pub fn new(inner: R) -> Self {
+        Self {
+            inner: DecompressedReaderInner::Unknown {
+                buffer: vec![0; BYTES_NEEDED],
+                inner: Some(inner),
+            },
+            switch_to: None,
+        }
+    }
+}
+
+impl<R> AsyncRead for DecompressedReader<R>
+where
+    R: AsyncBufRead + Unpin,
+{
+    fn poll_read(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &mut ReadBuf<'_>,
+    ) -> Poll<io::Result<()>> {
+        let mut this = self.project();
+        let (buffer, inner) = match this.inner.as_mut().project() {
+            DecompressedReaderInnerProj::Gzip(inner) => return inner.poll_read(cx, buf),
+            DecompressedReaderInnerProj::Bzip2(inner) => return inner.poll_read(cx, buf),
+            DecompressedReaderInnerProj::Xz(inner) => return inner.poll_read(cx, buf),
+            DecompressedReaderInnerProj::Unknown { buffer, inner } => (buffer, inner),
+        };
+
+        let mut our_buf = ReadBuf::new(buffer);
+        if let Err(e) = ready!(inner.as_pin_mut().unwrap().poll_read(cx, &mut our_buf)) {
+            return Poll::Ready(Err(e));
+        }
+
+        let data = our_buf.filled();
+        if data.len() >= BYTES_NEEDED {
+            if let Some(algorithm) = Algorithm::from_magic(data) {
+                this.inner.as_mut().switch_to(algorithm);
+            } else {
+                return Poll::Ready(Err(io::Error::new(
+                    io::ErrorKind::InvalidData,
+                    "tar data not gz, bzip2, or xz compressed",
+                )));
+            }
+            this.inner.poll_read(cx, buf)
+        } else {
+            cx.waker().wake_by_ref();
+            Poll::Pending
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::path::Path;
+
+    use async_compression::tokio::bufread::GzipEncoder;
+    use futures::TryStreamExt;
+    use rstest::rstest;
+    use tokio::io::{AsyncReadExt, BufReader};
+    use tokio_tar::Archive;
+
+    use super::*;
+
+    #[tokio::test]
+    async fn gzip() {
+        let data = b"abcdefghijk";
+        let mut enc = GzipEncoder::new(&data[..]);
+        let mut gzipped = vec![];
+        enc.read_to_end(&mut gzipped).await.unwrap();
+
+        let mut reader = DecompressedReader::new(BufReader::new(&gzipped[..]));
+        let mut round_tripped = vec![];
+        reader.read_to_end(&mut round_tripped).await.unwrap();
+
+        assert_eq!(data[..], round_tripped[..]);
+    }
+
+    #[rstest]
+    #[case::gzip(include_bytes!("../tests/blob.tar.gz"))]
+    #[case::bzip2(include_bytes!("../tests/blob.tar.bz2"))]
+    #[case::xz(include_bytes!("../tests/blob.tar.xz"))]
+    #[tokio::test]
+    async fn compressed_tar(#[case] data: &[u8]) {
+        let reader = DecompressedReader::new(BufReader::new(data));
+        let mut archive = Archive::new(reader);
+        let mut entries: Vec<_> = archive.entries().unwrap().try_collect().await.unwrap();
+
+        assert_eq!(entries.len(), 1);
+        assert_eq!(entries[0].path().unwrap().as_ref(), Path::new("empty"));
+        let mut data = String::new();
+        entries[0].read_to_string(&mut data).await.unwrap();
+        assert_eq!(data, "");
+    }
+}
diff --git a/tvix/glue/src/fetchers/mod.rs b/tvix/glue/src/fetchers/mod.rs
new file mode 100644
index 0000000000..9a884e51c4
--- /dev/null
+++ b/tvix/glue/src/fetchers/mod.rs
@@ -0,0 +1,445 @@
+use futures::TryStreamExt;
+use md5::Md5;
+use nix_compat::{
+    nixhash::{CAHash, HashAlgo, NixHash},
+    store_path::{build_ca_path, BuildStorePathError, StorePathRef},
+};
+use sha1::Sha1;
+use sha2::{digest::Output, Digest, Sha256, Sha512};
+use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite};
+use tokio_util::io::InspectReader;
+use tracing::warn;
+use tvix_castore::{
+    blobservice::BlobService,
+    directoryservice::DirectoryService,
+    proto::{node::Node, FileNode},
+};
+use tvix_store::{pathinfoservice::PathInfoService, proto::PathInfo};
+use url::Url;
+
+use crate::builtins::FetcherError;
+
+mod decompression;
+use decompression::DecompressedReader;
+
+/// Representing options for doing a fetch.
+#[derive(Clone, Eq, PartialEq)]
+pub enum Fetch {
+    /// Fetch a literal file from the given URL, with an optional expected
+    /// NixHash of it.
+    /// TODO: check if this is *always* sha256, and if so, make it [u8; 32].
+    URL(Url, Option<NixHash>),
+
+    /// Fetch a tarball from the given URL and unpack.
+    /// The file must be a tape archive (.tar), optionally compressed with gzip,
+    /// bzip2 or xz.
+    /// The top-level path component of the files in the tarball is removed,
+    /// so it is best if the tarball contains a single directory at top level.
+    /// Optionally, a sha256 digest can be provided to verify the unpacked
+    /// contents against.
+    Tarball(Url, Option<[u8; 32]>),
+
+    /// TODO
+    Git(),
+}
+
+// Drops potentially sensitive username and password from a URL.
+fn redact_url(url: &Url) -> Url {
+    let mut url = url.to_owned();
+    if !url.username().is_empty() {
+        let _ = url.set_username("redacted");
+    }
+
+    if url.password().is_some() {
+        let _ = url.set_password(Some("redacted"));
+    }
+
+    url
+}
+
+impl std::fmt::Debug for Fetch {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Fetch::URL(url, exp_hash) => {
+                let url = redact_url(url);
+                if let Some(exp_hash) = exp_hash {
+                    write!(f, "URL [url: {}, exp_hash: Some({})]", &url, exp_hash)
+                } else {
+                    write!(f, "URL [url: {}, exp_hash: None]", &url)
+                }
+            }
+            Fetch::Tarball(url, exp_digest) => {
+                let url = redact_url(url);
+                if let Some(exp_digest) = exp_digest {
+                    write!(
+                        f,
+                        "Tarball [url: {}, exp_hash: Some({})]",
+                        url,
+                        NixHash::Sha256(*exp_digest)
+                    )
+                } else {
+                    write!(f, "Tarball [url: {}, exp_hash: None]", url)
+                }
+            }
+            Fetch::Git() => todo!(),
+        }
+    }
+}
+
+impl Fetch {
+    /// If the [Fetch] contains an expected hash upfront, returns the resulting
+    /// store path.
+    /// This doesn't do any fetching.
+    pub fn store_path<'a>(
+        &self,
+        name: &'a str,
+    ) -> Result<Option<StorePathRef<'a>>, BuildStorePathError> {
+        let ca_hash = match self {
+            Fetch::URL(_, Some(nixhash)) => CAHash::Flat(nixhash.clone()),
+            Fetch::Tarball(_, Some(nar_sha256)) => CAHash::Nar(NixHash::Sha256(*nar_sha256)),
+            _ => return Ok(None),
+        };
+
+        // calculate the store path of this fetch
+        build_ca_path(name, &ca_hash, Vec::<String>::new(), false).map(Some)
+    }
+}
+
+/// Knows how to fetch a given [Fetch].
+pub struct Fetcher<BS, DS, PS> {
+    http_client: reqwest::Client,
+    blob_service: BS,
+    directory_service: DS,
+    path_info_service: PS,
+}
+
+impl<BS, DS, PS> Fetcher<BS, DS, PS> {
+    pub fn new(blob_service: BS, directory_service: DS, path_info_service: PS) -> Self {
+        Self {
+            http_client: reqwest::Client::new(),
+            blob_service,
+            directory_service,
+            path_info_service,
+        }
+    }
+
+    /// Constructs a HTTP request to the passed URL, and returns a AsyncReadBuf to it.
+    /// In case the URI uses the file:// scheme, use tokio::fs to open it.
+    async fn download(&self, url: Url) -> Result<Box<dyn AsyncBufRead + Unpin>, FetcherError> {
+        match url.scheme() {
+            "file" => {
+                let f = tokio::fs::File::open(url.to_file_path().map_err(|_| {
+                    // "Returns Err if the host is neither empty nor "localhost"
+                    // (except on Windows, where file: URLs may have a non-local host)"
+                    FetcherError::Io(std::io::Error::new(
+                        std::io::ErrorKind::Other,
+                        "invalid host for file:// scheme",
+                    ))
+                })?)
+                .await?;
+                Ok(Box::new(tokio::io::BufReader::new(f)))
+            }
+            _ => {
+                let resp = self.http_client.get(url).send().await?;
+                Ok(Box::new(tokio_util::io::StreamReader::new(
+                    resp.bytes_stream().map_err(|e| {
+                        let e = e.without_url();
+                        warn!(%e, "failed to get response body");
+                        std::io::Error::new(std::io::ErrorKind::BrokenPipe, e)
+                    }),
+                )))
+            }
+        }
+    }
+}
+
+/// Copies all data from the passed reader to the passed writer.
+/// Afterwards, it also returns the resulting [Digest], as well as the number of
+/// bytes copied.
+/// The exact hash function used is left generic over all [Digest].
+async fn hash<D: Digest + std::io::Write>(
+    mut r: impl AsyncRead + Unpin,
+    mut w: impl AsyncWrite + Unpin,
+) -> std::io::Result<(Output<D>, u64)> {
+    let mut hasher = D::new();
+    let bytes_copied = tokio::io::copy(
+        &mut InspectReader::new(&mut r, |d| hasher.write_all(d).unwrap()),
+        &mut w,
+    )
+    .await?;
+    Ok((hasher.finalize(), bytes_copied))
+}
+
+impl<BS, DS, PS> Fetcher<BS, DS, PS>
+where
+    BS: AsRef<(dyn BlobService + 'static)> + Clone + Send + Sync + 'static,
+    DS: AsRef<(dyn DirectoryService + 'static)>,
+    PS: PathInfoService,
+{
+    /// Ingest the data from a specified [Fetch].
+    /// On success, return the root node, a content digest and length.
+    /// Returns an error if there was a failure during fetching, or the contents
+    /// didn't match the previously communicated hash contained inside the FetchArgs.
+    pub async fn ingest(&self, fetch: Fetch) -> Result<(Node, CAHash, u64), FetcherError> {
+        match fetch {
+            Fetch::URL(url, exp_hash) => {
+                // Construct a AsyncRead reading from the data as its downloaded.
+                let mut r = self.download(url.clone()).await?;
+
+                // Construct a AsyncWrite to write into the BlobService.
+                let mut blob_writer = self.blob_service.open_write().await;
+
+                // Copy the contents from the download reader to the blob writer.
+                // Calculate the digest of the file received, depending on the
+                // communicated expected hash (or sha256 if none provided).
+                let (actual_hash, blob_size) = match exp_hash
+                    .as_ref()
+                    .map(NixHash::algo)
+                    .unwrap_or_else(|| HashAlgo::Sha256)
+                {
+                    HashAlgo::Sha256 => hash::<Sha256>(&mut r, &mut blob_writer).await.map(
+                        |(digest, bytes_written)| (NixHash::Sha256(digest.into()), bytes_written),
+                    )?,
+                    HashAlgo::Md5 => hash::<Md5>(&mut r, &mut blob_writer).await.map(
+                        |(digest, bytes_written)| (NixHash::Md5(digest.into()), bytes_written),
+                    )?,
+                    HashAlgo::Sha1 => hash::<Sha1>(&mut r, &mut blob_writer).await.map(
+                        |(digest, bytes_written)| (NixHash::Sha1(digest.into()), bytes_written),
+                    )?,
+                    HashAlgo::Sha512 => hash::<Sha512>(&mut r, &mut blob_writer).await.map(
+                        |(digest, bytes_written)| {
+                            (NixHash::Sha512(Box::new(digest.into())), bytes_written)
+                        },
+                    )?,
+                };
+
+                if let Some(exp_hash) = exp_hash {
+                    if exp_hash != actual_hash {
+                        return Err(FetcherError::HashMismatch {
+                            url,
+                            wanted: exp_hash,
+                            got: actual_hash,
+                        });
+                    }
+                }
+
+                // Construct and return the FileNode describing the downloaded contents.
+                Ok((
+                    Node::File(FileNode {
+                        name: vec![].into(),
+                        digest: blob_writer.close().await?.into(),
+                        size: blob_size,
+                        executable: false,
+                    }),
+                    CAHash::Flat(actual_hash),
+                    blob_size,
+                ))
+            }
+            Fetch::Tarball(url, exp_nar_sha256) => {
+                // Construct a AsyncRead reading from the data as its downloaded.
+                let r = self.download(url.clone()).await?;
+
+                // Pop compression.
+                let r = DecompressedReader::new(r);
+                // Open the archive.
+                let archive = tokio_tar::Archive::new(r);
+
+                // Ingest the archive, get the root node
+                let node = tvix_castore::import::archive::ingest_archive(
+                    self.blob_service.clone(),
+                    &self.directory_service,
+                    archive,
+                )
+                .await?;
+
+                // If an expected NAR sha256 was provided, compare with the one
+                // calculated from our root node.
+                // Even if no expected NAR sha256 has been provided, we need
+                // the actual one later.
+                let (nar_size, actual_nar_sha256) = self
+                    .path_info_service
+                    .calculate_nar(&node)
+                    .await
+                    .map_err(|e| {
+                        // convert the generic Store error to an IO error.
+                        FetcherError::Io(e.into())
+                    })?;
+
+                if let Some(exp_nar_sha256) = exp_nar_sha256 {
+                    if exp_nar_sha256 != actual_nar_sha256 {
+                        return Err(FetcherError::HashMismatch {
+                            url,
+                            wanted: NixHash::Sha256(exp_nar_sha256),
+                            got: NixHash::Sha256(actual_nar_sha256),
+                        });
+                    }
+                }
+
+                Ok((
+                    node,
+                    CAHash::Nar(NixHash::Sha256(actual_nar_sha256)),
+                    nar_size,
+                ))
+            }
+            Fetch::Git() => todo!(),
+        }
+    }
+
+    /// Ingests the data from a specified [Fetch], persists the returned node
+    /// in the PathInfoService, and returns the calculated StorePath, as well as
+    /// the root node pointing to the contents.
+    /// The root node can be used to descend into the data without doing the
+    /// lookup to the PathInfoService again.
+    pub async fn ingest_and_persist<'a>(
+        &self,
+        name: &'a str,
+        fetch: Fetch,
+    ) -> Result<(StorePathRef<'a>, Node), FetcherError> {
+        // Fetch file, return the (unnamed) (File)Node of its contents, ca hash and filesize.
+        let (node, ca_hash, size) = self.ingest(fetch).await?;
+
+        // Calculate the store path to return later, which is done with the ca_hash.
+        let store_path = build_ca_path(name, &ca_hash, Vec::<String>::new(), false)?;
+
+        // Rename the node name to match the Store Path.
+        let node = node.rename(store_path.to_string().into());
+
+        // If the resulting hash is not a CAHash::Nar, we also need to invoke
+        // `calculate_nar` to calculate this representation, as it's required in
+        // the [PathInfo].
+        let (nar_size, nar_sha256) = match &ca_hash {
+            CAHash::Flat(_nix_hash) => self
+                .path_info_service
+                .calculate_nar(&node)
+                .await
+                .map_err(|e| FetcherError::Io(e.into()))?,
+            CAHash::Nar(NixHash::Sha256(nar_sha256)) => (size, *nar_sha256),
+            CAHash::Nar(_) => unreachable!("Tvix bug: fetch returned non-sha256 CAHash::Nar"),
+            CAHash::Text(_) => unreachable!("Tvix bug: fetch returned CAHash::Text"),
+        };
+
+        // Construct the PathInfo and persist it.
+        let path_info = PathInfo {
+            node: Some(tvix_castore::proto::Node { node: Some(node) }),
+            references: vec![],
+            narinfo: Some(tvix_store::proto::NarInfo {
+                nar_size,
+                nar_sha256: nar_sha256.to_vec().into(),
+                signatures: vec![],
+                reference_names: vec![],
+                deriver: None,
+                ca: Some(ca_hash.into()),
+            }),
+        };
+
+        let path_info = self
+            .path_info_service
+            .put(path_info)
+            .await
+            .map_err(|e| FetcherError::Io(e.into()))?;
+
+        Ok((store_path, path_info.node.unwrap().node.unwrap()))
+    }
+}
+
+/// Attempts to mimic `nix::libutil::baseNameOf`
+pub(crate) fn url_basename(s: &str) -> &str {
+    if s.is_empty() {
+        return "";
+    }
+
+    let mut last = s.len() - 1;
+    if s.chars().nth(last).unwrap() == '/' && last > 0 {
+        last -= 1;
+    }
+
+    if last == 0 {
+        return "";
+    }
+
+    let pos = match s[..=last].rfind('/') {
+        Some(pos) => {
+            if pos == last - 1 {
+                0
+            } else {
+                pos
+            }
+        }
+        None => 0,
+    };
+
+    &s[(pos + 1)..=last]
+}
+
+#[cfg(test)]
+mod tests {
+    mod fetch {
+        use nix_compat::nixbase32;
+
+        use crate::fetchers::Fetch;
+
+        use super::super::*;
+
+        #[test]
+        fn fetchurl_store_path() {
+            let url = Url::parse("https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch").unwrap();
+            let exp_hash = NixHash::Sha256(
+                nixbase32::decode_fixed("0nawkl04sj7psw6ikzay7kydj3dhd0fkwghcsf5rzaw4bmp4kbax")
+                    .unwrap(),
+            );
+
+            let fetch = Fetch::URL(url, Some(exp_hash));
+            assert_eq!(
+                "06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch",
+                &fetch
+                    .store_path("notmuch-extract-patch")
+                    .unwrap()
+                    .unwrap()
+                    .to_string(),
+            )
+        }
+
+        #[test]
+        fn fetch_tarball_store_path() {
+            let url = Url::parse("https://github.com/NixOS/nixpkgs/archive/91050ea1e57e50388fa87a3302ba12d188ef723a.tar.gz").unwrap();
+            let exp_nixbase32 =
+                nixbase32::decode_fixed("1hf6cgaci1n186kkkjq106ryf8mmlq9vnwgfwh625wa8hfgdn4dm")
+                    .unwrap();
+            let fetch = Fetch::Tarball(url, Some(exp_nixbase32));
+
+            assert_eq!(
+                "7adgvk5zdfq4pwrhsm3n9lzypb12gw0g-source",
+                &fetch.store_path("source").unwrap().unwrap().to_string(),
+            )
+        }
+    }
+
+    mod url_basename {
+        use super::super::*;
+
+        #[test]
+        fn empty_path() {
+            assert_eq!(url_basename(""), "");
+        }
+
+        #[test]
+        fn path_on_root() {
+            assert_eq!(url_basename("/dir"), "dir");
+        }
+
+        #[test]
+        fn relative_path() {
+            assert_eq!(url_basename("dir/foo"), "foo");
+        }
+
+        #[test]
+        fn root_with_trailing_slash() {
+            assert_eq!(url_basename("/"), "");
+        }
+
+        #[test]
+        fn trailing_slash() {
+            assert_eq!(url_basename("/dir/"), "dir");
+        }
+    }
+}
diff --git a/tvix/glue/src/fetchurl.nix b/tvix/glue/src/fetchurl.nix
new file mode 100644
index 0000000000..3f182a5a31
--- /dev/null
+++ b/tvix/glue/src/fetchurl.nix
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: LGPL-2.1
+#
+# This file is vendored from C++ Nix, as it needs to be bundled with
+# an evaluator to be able to evaluate nixpkgs.
+#
+# Source: https://github.com/NixOS/nix/blob/2.3.16/corepkgs/fetchurl.nix
+
+{ system ? "" # obsolete
+, url
+, hash ? "" # an SRI hash
+
+  # Legacy hash specification
+, md5 ? ""
+, sha1 ? ""
+, sha256 ? ""
+, sha512 ? ""
+, outputHash ? if hash != "" then hash else if sha512 != "" then sha512 else if sha1 != "" then sha1 else if md5 != "" then md5 else sha256
+, outputHashAlgo ? if hash != "" then "" else if sha512 != "" then "sha512" else if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256"
+
+, executable ? false
+, unpack ? false
+, name ? baseNameOf (toString url)
+}:
+
+derivation {
+  builder = "builtin:fetchurl";
+
+  # New-style output content requirements.
+  inherit outputHashAlgo outputHash;
+  outputHashMode = if unpack || executable then "recursive" else "flat";
+
+  inherit name url executable unpack;
+
+  system = "builtin";
+
+  # No need to double the amount of network traffic
+  preferLocalBuild = true;
+
+  impureEnvVars = [
+    # We borrow these environment variables from the caller to allow
+    # easy proxy configuration.  This is impure, but a fixed-output
+    # derivation like fetchurl is allowed to do so since its result is
+    # by definition pure.
+    "http_proxy"
+    "https_proxy"
+    "ftp_proxy"
+    "all_proxy"
+    "no_proxy"
+  ];
+
+  # To make "nix-prefetch-url" work.
+  urls = [ url ];
+}
diff --git a/tvix/glue/src/known_paths.rs b/tvix/glue/src/known_paths.rs
new file mode 100644
index 0000000000..290c9d5b69
--- /dev/null
+++ b/tvix/glue/src/known_paths.rs
@@ -0,0 +1,289 @@
+//! This module implements logic required for persisting known paths
+//! during an evaluation.
+//!
+//! Tvix needs to be able to keep track of each Nix store path that it
+//! knows about during the scope of a single evaluation and its
+//! related builds.
+//!
+//! This data is required to find the derivation needed to actually trigger the
+//! build, if necessary.
+
+use nix_compat::{
+    derivation::Derivation,
+    store_path::{BuildStorePathError, StorePath, StorePathRef},
+};
+use std::collections::HashMap;
+
+use crate::fetchers::Fetch;
+
+/// Struct keeping track of all known Derivations in the current evaluation.
+/// This keeps both the Derivation struct, as well as the "Hash derivation
+/// modulo".
+#[derive(Debug, Default)]
+pub struct KnownPaths {
+    /// All known derivation or FOD hashes.
+    ///
+    /// Keys are derivation paths, values are a tuple of the "hash derivation
+    /// modulo" and the Derivation struct itself.
+    derivations: HashMap<StorePath, ([u8; 32], Derivation)>,
+
+    /// A map from output path to (one) drv path.
+    /// Note that in the case of FODs, multiple drvs can produce the same output
+    /// path. We use one of them.
+    outputs_to_drvpath: HashMap<StorePath, StorePath>,
+
+    /// A map from output path to fetches (and their names).
+    outputs_to_fetches: HashMap<StorePath, (String, Fetch)>,
+}
+
+impl KnownPaths {
+    /// Fetch the opaque "hash derivation modulo" for a given derivation path.
+    pub fn get_hash_derivation_modulo(&self, drv_path: &StorePath) -> Option<&[u8; 32]> {
+        self.derivations
+            .get(drv_path)
+            .map(|(hash_derivation_modulo, _derivation)| hash_derivation_modulo)
+    }
+
+    /// Return a reference to the Derivation for a given drv path.
+    pub fn get_drv_by_drvpath(&self, drv_path: &StorePath) -> Option<&Derivation> {
+        self.derivations
+            .get(drv_path)
+            .map(|(_hash_derivation_modulo, derivation)| derivation)
+    }
+
+    /// Return the drv path of the derivation producing the passed output path.
+    /// Note there can be multiple Derivations producing the same output path in
+    /// flight; this function will only return one of them.
+    pub fn get_drv_path_for_output_path(&self, output_path: &StorePath) -> Option<&StorePath> {
+        self.outputs_to_drvpath.get(output_path)
+    }
+
+    /// Insert a new [Derivation] into this struct.
+    /// The Derivation struct must pass validation, and its output paths need to
+    /// be fully calculated.
+    /// All input derivations this refers to must also be inserted to this
+    /// struct.
+    pub fn add_derivation(&mut self, drv_path: StorePath, drv: Derivation) {
+        // check input derivations to have been inserted.
+        #[cfg(debug_assertions)]
+        {
+            for input_drv_path in drv.input_derivations.keys() {
+                debug_assert!(self.derivations.contains_key(input_drv_path));
+            }
+        }
+
+        // compute the hash derivation modulo
+        let hash_derivation_modulo = drv.hash_derivation_modulo(|drv_path| {
+            self.get_hash_derivation_modulo(&drv_path.to_owned())
+                .unwrap_or_else(|| panic!("{} not found", drv_path))
+                .to_owned()
+        });
+
+        // For all output paths, update our lookup table.
+        // We only write into the lookup table once.
+        for output in drv.outputs.values() {
+            self.outputs_to_drvpath
+                .entry(output.path.as_ref().expect("missing store path").clone())
+                .or_insert(drv_path.to_owned());
+        }
+
+        // insert the derivation itself
+        #[allow(unused_variables)] // assertions on this only compiled in debug builds
+        let old = self
+            .derivations
+            .insert(drv_path.to_owned(), (hash_derivation_modulo, drv));
+
+        #[cfg(debug_assertions)]
+        {
+            if let Some(old) = old {
+                debug_assert!(
+                    old.0 == hash_derivation_modulo,
+                    "hash derivation modulo for a given derivation should always be calculated the same"
+                );
+            }
+        }
+    }
+
+    /// Insert a new [Fetch] into this struct, which *must* have an expected
+    /// hash (otherwise we wouldn't be able to calculate the store path).
+    /// Fetches without a known hash need to be fetched inside builtins.
+    pub fn add_fetch<'a>(
+        &mut self,
+        fetch: Fetch,
+        name: &'a str,
+    ) -> Result<StorePathRef<'a>, BuildStorePathError> {
+        let store_path = fetch
+            .store_path(name)?
+            .expect("Tvix bug: fetch must have an expected hash");
+        // insert the fetch.
+        self.outputs_to_fetches
+            .insert(store_path.to_owned(), (name.to_owned(), fetch));
+
+        Ok(store_path)
+    }
+
+    /// Return the name and fetch producing the passed output path.
+    /// Note there can also be (multiple) Derivations producing the same output path.
+    pub fn get_fetch_for_output_path(&self, output_path: &StorePath) -> Option<(String, Fetch)> {
+        self.outputs_to_fetches
+            .get(output_path)
+            .map(|(name, fetch)| (name.to_owned(), fetch.to_owned()))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use nix_compat::{derivation::Derivation, nixbase32, nixhash::NixHash, store_path::StorePath};
+    use url::Url;
+
+    use crate::fetchers::Fetch;
+
+    use super::KnownPaths;
+    use hex_literal::hex;
+    use lazy_static::lazy_static;
+
+    lazy_static! {
+        static ref BAR_DRV: Derivation = Derivation::from_aterm_bytes(include_bytes!(
+            "tests/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv"
+        ))
+        .expect("must parse");
+        static ref FOO_DRV: Derivation = Derivation::from_aterm_bytes(include_bytes!(
+            "tests/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv"
+        ))
+        .expect("must parse");
+        static ref BAR_DRV_PATH: StorePath =
+            StorePath::from_bytes(b"ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv").expect("must parse");
+        static ref FOO_DRV_PATH: StorePath =
+            StorePath::from_bytes(b"ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv").expect("must parse");
+        static ref BAR_OUT_PATH: StorePath =
+            StorePath::from_bytes(b"mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar").expect("must parse");
+        static ref FOO_OUT_PATH: StorePath =
+            StorePath::from_bytes(b"fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo").expect("must parse");
+
+        static ref FETCH_URL : Fetch = Fetch::URL(
+            Url::parse("https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch").unwrap(),
+            Some(NixHash::Sha256(nixbase32::decode_fixed("0nawkl04sj7psw6ikzay7kydj3dhd0fkwghcsf5rzaw4bmp4kbax").unwrap()))
+        );
+        static ref FETCH_URL_OUT_PATH: StorePath = StorePath::from_bytes(b"06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch").unwrap();
+
+        static ref FETCH_TARBALL : Fetch = Fetch::Tarball(
+            Url::parse("https://github.com/NixOS/nixpkgs/archive/91050ea1e57e50388fa87a3302ba12d188ef723a.tar.gz").unwrap(),
+            Some(nixbase32::decode_fixed("1hf6cgaci1n186kkkjq106ryf8mmlq9vnwgfwh625wa8hfgdn4dm").unwrap())
+        );
+        static ref FETCH_TARBALL_OUT_PATH: StorePath = StorePath::from_bytes(b"7adgvk5zdfq4pwrhsm3n9lzypb12gw0g-source").unwrap();
+    }
+
+    /// ensure we don't allow acdding a Derivation that depends on another,
+    /// not-yet-added Derivation.
+    #[test]
+    #[should_panic]
+    fn drv_reject_if_missing_input_drv() {
+        let mut known_paths = KnownPaths::default();
+
+        // FOO_DRV depends on BAR_DRV, which wasn't added.
+        known_paths.add_derivation(FOO_DRV_PATH.clone(), FOO_DRV.clone());
+    }
+
+    #[test]
+    fn drv_happy_path() {
+        let mut known_paths = KnownPaths::default();
+
+        // get_drv_by_drvpath should return None for non-existing Derivations,
+        // same as get_hash_derivation_modulo and get_drv_path_for_output_path
+        assert_eq!(None, known_paths.get_drv_by_drvpath(&BAR_DRV_PATH));
+        assert_eq!(None, known_paths.get_hash_derivation_modulo(&BAR_DRV_PATH));
+        assert_eq!(
+            None,
+            known_paths.get_drv_path_for_output_path(&BAR_OUT_PATH)
+        );
+
+        // Add BAR_DRV
+        known_paths.add_derivation(BAR_DRV_PATH.clone(), BAR_DRV.clone());
+
+        // We should get it back
+        assert_eq!(
+            Some(&BAR_DRV.clone()),
+            known_paths.get_drv_by_drvpath(&BAR_DRV_PATH)
+        );
+
+        // Test get_drv_path_for_output_path
+        assert_eq!(
+            Some(&BAR_DRV_PATH.clone()),
+            known_paths.get_drv_path_for_output_path(&BAR_OUT_PATH)
+        );
+
+        // It should be possible to get the hash derivation modulo.
+        assert_eq!(
+            Some(&hex!(
+                "c79aebd0ce3269393d4a1fde2cbd1d975d879b40f0bf40a48f550edc107fd5df"
+            )),
+            known_paths.get_hash_derivation_modulo(&BAR_DRV_PATH.clone())
+        );
+
+        // Now insert FOO_DRV too. It shouldn't panic, as BAR_DRV is already
+        // added.
+        known_paths.add_derivation(FOO_DRV_PATH.clone(), FOO_DRV.clone());
+
+        assert_eq!(
+            Some(&FOO_DRV.clone()),
+            known_paths.get_drv_by_drvpath(&FOO_DRV_PATH)
+        );
+        assert_eq!(
+            Some(&hex!(
+                "af030d36d63d3d7f56a71adaba26b36f5fa1f9847da5eed953ed62e18192762f"
+            )),
+            known_paths.get_hash_derivation_modulo(&FOO_DRV_PATH.clone())
+        );
+
+        // Test get_drv_path_for_output_path
+        assert_eq!(
+            Some(&FOO_DRV_PATH.clone()),
+            known_paths.get_drv_path_for_output_path(&FOO_OUT_PATH)
+        );
+    }
+
+    #[test]
+    fn fetch_happy_path() {
+        let mut known_paths = KnownPaths::default();
+
+        // get_fetch_for_output_path should return None for new fetches.
+        assert!(known_paths
+            .get_fetch_for_output_path(&FETCH_TARBALL_OUT_PATH)
+            .is_none());
+
+        // add_fetch should return the properly calculated store paths.
+        assert_eq!(
+            *FETCH_TARBALL_OUT_PATH,
+            known_paths
+                .add_fetch(FETCH_TARBALL.clone(), "source")
+                .unwrap()
+                .to_owned()
+        );
+
+        assert_eq!(
+            *FETCH_URL_OUT_PATH,
+            known_paths
+                .add_fetch(FETCH_URL.clone(), "notmuch-extract-patch")
+                .unwrap()
+                .to_owned()
+        );
+
+        // We should be able to get these fetches out, when asking for their out path.
+        let (got_name, got_fetch) = known_paths
+            .get_fetch_for_output_path(&FETCH_URL_OUT_PATH)
+            .expect("must be some");
+
+        assert_eq!("notmuch-extract-patch", got_name);
+        assert_eq!(FETCH_URL.clone(), got_fetch);
+
+        // … multiple times.
+        let (got_name, got_fetch) = known_paths
+            .get_fetch_for_output_path(&FETCH_URL_OUT_PATH)
+            .expect("must be some");
+
+        assert_eq!("notmuch-extract-patch", got_name);
+        assert_eq!(FETCH_URL.clone(), got_fetch);
+    }
+
+    // TODO: add test panicking about missing digest
+}
diff --git a/tvix/glue/src/lib.rs b/tvix/glue/src/lib.rs
new file mode 100644
index 0000000000..2e5a3be103
--- /dev/null
+++ b/tvix/glue/src/lib.rs
@@ -0,0 +1,23 @@
+pub mod builtins;
+pub mod fetchers;
+pub mod known_paths;
+pub mod refscan;
+pub mod tvix_build;
+pub mod tvix_io;
+pub mod tvix_store_io;
+
+#[cfg(test)]
+mod tests;
+
+/// Tell the Evaluator to resolve `<nix>` to the path `/__corepkgs__`,
+/// which has special handling in [tvix_io::TvixIO].
+/// This is used in nixpkgs to import `fetchurl.nix` from `<nix>`.
+pub fn configure_nix_path<IO>(
+    eval: &mut tvix_eval::Evaluation<IO>,
+    nix_search_path: &Option<String>,
+) {
+    eval.nix_path = nix_search_path
+        .as_ref()
+        .map(|p| format!("nix=/__corepkgs__:{}", p))
+        .or_else(|| Some("nix=/__corepkgs__".to_string()));
+}
diff --git a/tvix/glue/src/refscan.rs b/tvix/glue/src/refscan.rs
new file mode 100644
index 0000000000..0e0bb6c778
--- /dev/null
+++ b/tvix/glue/src/refscan.rs
@@ -0,0 +1,115 @@
+//! Simple scanner for non-overlapping, known references of Nix store paths in a
+//! given string.
+//!
+//! This is used for determining build references (see
+//! //tvix/eval/docs/build-references.md for more details).
+//!
+//! The scanner itself is using the Wu-Manber string-matching algorithm, using
+//! our fork of the `wu-mamber` crate.
+
+use std::collections::BTreeSet;
+use wu_manber::TwoByteWM;
+
+pub const STORE_PATH_LEN: usize = "/nix/store/00000000000000000000000000000000".len();
+
+/// Represents a "primed" reference scanner with an automaton that knows the set
+/// of store paths to scan for.
+pub struct ReferenceScanner<P: Ord + AsRef<[u8]>> {
+    candidates: Vec<P>,
+    searcher: Option<TwoByteWM>,
+    matches: Vec<usize>,
+}
+
+impl<P: Clone + Ord + AsRef<[u8]>> ReferenceScanner<P> {
+    /// Construct a new `ReferenceScanner` that knows how to scan for the given
+    /// candidate store paths.
+    pub fn new(candidates: Vec<P>) -> Self {
+        let searcher = if candidates.is_empty() {
+            None
+        } else {
+            Some(TwoByteWM::new(&candidates))
+        };
+
+        ReferenceScanner {
+            searcher,
+            candidates,
+            matches: Default::default(),
+        }
+    }
+
+    /// Scan the given str for all non-overlapping matches and collect them
+    /// in the scanner.
+    pub fn scan<S: AsRef<[u8]>>(&mut self, haystack: S) {
+        if haystack.as_ref().len() < STORE_PATH_LEN {
+            return;
+        }
+
+        if let Some(searcher) = &self.searcher {
+            for m in searcher.find(haystack) {
+                self.matches.push(m.pat_idx);
+            }
+        }
+    }
+
+    /// Finalise the reference scanner and return the resulting matches.
+    pub fn finalise(self) -> BTreeSet<P> {
+        self.matches
+            .into_iter()
+            .map(|idx| self.candidates[idx].clone())
+            .collect()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    // The actual derivation of `nixpkgs.hello`.
+    const HELLO_DRV: &str = r#"Derive([("out","/nix/store/33l4p0pn0mybmqzaxfkpppyh7vx1c74p-hello-2.12.1","","")],[("/nix/store/6z1jfnqqgyqr221zgbpm30v91yfj3r45-bash-5.1-p16.drv",["out"]),("/nix/store/ap9g09fxbicj836zm88d56dn3ff4clxl-stdenv-linux.drv",["out"]),("/nix/store/pf80kikyxr63wrw56k00i1kw6ba76qik-hello-2.12.1.tar.gz.drv",["out"])],["/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"],"x86_64-linux","/nix/store/4xw8n979xpivdc46a9ndcvyhwgif00hz-bash-5.1-p16/bin/bash",["-e","/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"],[("buildInputs",""),("builder","/nix/store/4xw8n979xpivdc46a9ndcvyhwgif00hz-bash-5.1-p16/bin/bash"),("cmakeFlags",""),("configureFlags",""),("depsBuildBuild",""),("depsBuildBuildPropagated",""),("depsBuildTarget",""),("depsBuildTargetPropagated",""),("depsHostHost",""),("depsHostHostPropagated",""),("depsTargetTarget",""),("depsTargetTargetPropagated",""),("doCheck","1"),("doInstallCheck",""),("mesonFlags",""),("name","hello-2.12.1"),("nativeBuildInputs",""),("out","/nix/store/33l4p0pn0mybmqzaxfkpppyh7vx1c74p-hello-2.12.1"),("outputs","out"),("patches",""),("pname","hello"),("propagatedBuildInputs",""),("propagatedNativeBuildInputs",""),("src","/nix/store/pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz"),("stdenv","/nix/store/cp65c8nk29qq5cl1wyy5qyw103cwmax7-stdenv-linux"),("strictDeps",""),("system","x86_64-linux"),("version","2.12.1")])"#;
+
+    #[test]
+    fn test_no_patterns() {
+        let mut scanner: ReferenceScanner<String> = ReferenceScanner::new(vec![]);
+
+        scanner.scan(HELLO_DRV);
+
+        let result = scanner.finalise();
+
+        assert_eq!(result.len(), 0);
+    }
+
+    #[test]
+    fn test_single_match() {
+        let mut scanner = ReferenceScanner::new(vec![
+            "/nix/store/4xw8n979xpivdc46a9ndcvyhwgif00hz-bash-5.1-p16".to_string(),
+        ]);
+        scanner.scan(HELLO_DRV);
+
+        let result = scanner.finalise();
+
+        assert_eq!(result.len(), 1);
+        assert!(result.contains("/nix/store/4xw8n979xpivdc46a9ndcvyhwgif00hz-bash-5.1-p16"));
+    }
+
+    #[test]
+    fn test_multiple_matches() {
+        let candidates = vec![
+            // these exist in the drv:
+            "/nix/store/33l4p0pn0mybmqzaxfkpppyh7vx1c74p-hello-2.12.1".to_string(),
+            "/nix/store/pf80kikyxr63wrw56k00i1kw6ba76qik-hello-2.12.1.tar.gz.drv".to_string(),
+            "/nix/store/cp65c8nk29qq5cl1wyy5qyw103cwmax7-stdenv-linux".to_string(),
+            // this doesn't:
+            "/nix/store/fn7zvafq26f0c8b17brs7s95s10ibfzs-emacs-28.2.drv".to_string(),
+        ];
+
+        let mut scanner = ReferenceScanner::new(candidates.clone());
+        scanner.scan(HELLO_DRV);
+
+        let result = scanner.finalise();
+        assert_eq!(result.len(), 3);
+
+        for c in candidates[..3].iter() {
+            assert!(result.contains(c));
+        }
+    }
+}
diff --git a/tvix/glue/src/tests/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv b/tvix/glue/src/tests/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv
new file mode 100644
index 0000000000..a4fea3c5f4
--- /dev/null
+++ b/tvix/glue/src/tests/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar","r:sha256","08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba")],[],[],":",":",[],[("builder",":"),("name","bar"),("out","/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar"),("outputHash","08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"),("outputHashAlgo","sha256"),("outputHashMode","recursive"),("system",":")])
\ No newline at end of file
diff --git a/tvix/glue/src/tests/blob.tar.bz2 b/tvix/glue/src/tests/blob.tar.bz2
new file mode 100644
index 0000000000..d74b913912
--- /dev/null
+++ b/tvix/glue/src/tests/blob.tar.bz2
Binary files differdiff --git a/tvix/glue/src/tests/blob.tar.gz b/tvix/glue/src/tests/blob.tar.gz
new file mode 100644
index 0000000000..c2bae55078
--- /dev/null
+++ b/tvix/glue/src/tests/blob.tar.gz
Binary files differdiff --git a/tvix/glue/src/tests/blob.tar.xz b/tvix/glue/src/tests/blob.tar.xz
new file mode 100644
index 0000000000..324a99d895
--- /dev/null
+++ b/tvix/glue/src/tests/blob.tar.xz
Binary files differdiff --git a/tvix/glue/src/tests/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv b/tvix/glue/src/tests/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv
new file mode 100644
index 0000000000..1699c2a75e
--- /dev/null
+++ b/tvix/glue/src/tests/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo","","")],[("/nix/store/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv",["out"])],[],":",":",[],[("bar","/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar"),("builder",":"),("name","foo"),("out","/nix/store/fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo"),("system",":")])
\ No newline at end of file
diff --git a/tvix/glue/src/tests/mod.rs b/tvix/glue/src/tests/mod.rs
new file mode 100644
index 0000000000..8e1572b6e3
--- /dev/null
+++ b/tvix/glue/src/tests/mod.rs
@@ -0,0 +1,153 @@
+use std::{rc::Rc, sync::Arc};
+
+use pretty_assertions::assert_eq;
+use std::path::PathBuf;
+use tvix_build::buildservice::DummyBuildService;
+use tvix_castore::{
+    blobservice::{BlobService, MemoryBlobService},
+    directoryservice::{DirectoryService, MemoryDirectoryService},
+};
+use tvix_eval::{EvalIO, Value};
+use tvix_store::pathinfoservice::{MemoryPathInfoService, PathInfoService};
+
+use rstest::rstest;
+
+use crate::{
+    builtins::{add_derivation_builtins, add_fetcher_builtins, add_import_builtins},
+    configure_nix_path,
+    tvix_io::TvixIO,
+    tvix_store_io::TvixStoreIO,
+};
+
+fn eval_test(code_path: PathBuf, expect_success: bool) {
+    assert_eq!(
+        code_path.extension().unwrap(),
+        "nix",
+        "test files always end in .nix"
+    );
+    let exp_path = code_path.with_extension("exp");
+    let exp_xml_path = code_path.with_extension("exp.xml");
+
+    let code = std::fs::read_to_string(&code_path).expect("should be able to read test code");
+
+    if exp_xml_path.exists() {
+        // We can't test them at the moment because we don't have XML output yet.
+        // Checking for success / failure only is a bit disingenious.
+        return;
+    }
+
+    let blob_service = Arc::new(MemoryBlobService::default()) as Arc<dyn BlobService>;
+    let directory_service =
+        Arc::new(MemoryDirectoryService::default()) as Arc<dyn DirectoryService>;
+    let path_info_service = Box::new(MemoryPathInfoService::new(
+        blob_service.clone(),
+        directory_service.clone(),
+    )) as Box<dyn PathInfoService>;
+    let tokio_runtime = tokio::runtime::Runtime::new().unwrap();
+
+    let tvix_store_io = Rc::new(TvixStoreIO::new(
+        blob_service,
+        directory_service,
+        path_info_service.into(),
+        Arc::new(DummyBuildService::default()),
+        tokio_runtime.handle().clone(),
+    ));
+    // Wrap with TvixIO, so <nix/fetchurl.nix can be imported.
+    let mut eval = tvix_eval::Evaluation::new(
+        Box::new(TvixIO::new(tvix_store_io.clone() as Rc<dyn EvalIO>)) as Box<dyn EvalIO>,
+        true,
+    );
+
+    eval.strict = true;
+    add_derivation_builtins(&mut eval, tvix_store_io.clone());
+    add_fetcher_builtins(&mut eval, tvix_store_io.clone());
+    add_import_builtins(&mut eval, tvix_store_io.clone());
+    configure_nix_path(&mut eval, &None);
+
+    let result = eval.evaluate(code, Some(code_path.clone()));
+    let failed = match result.value {
+        Some(Value::Catchable(_)) => true,
+        _ => !result.errors.is_empty(),
+    };
+    if expect_success && failed {
+        panic!(
+            "{}: evaluation of eval-okay test should succeed, but failed with {:?}",
+            code_path.display(),
+            result.errors,
+        );
+    }
+
+    if !expect_success && failed {
+        return;
+    }
+
+    let value = result.value.unwrap();
+    let result_str = value.to_string();
+
+    if let Ok(exp) = std::fs::read_to_string(exp_path) {
+        if expect_success {
+            assert_eq!(
+                result_str,
+                exp.trim(),
+                "{}: result value representation (left) must match expectation (right)",
+                code_path.display()
+            );
+        } else {
+            assert_ne!(
+                result_str,
+                exp.trim(),
+                "{}: test passed unexpectedly!  consider moving it out of notyetpassing",
+                code_path.display()
+            );
+        }
+    } else if expect_success {
+        panic!(
+            "{}: should be able to read test expectation",
+            code_path.display()
+        );
+    } else {
+        panic!(
+            "{}: test should have failed, but succeeded with output {}",
+            code_path.display(),
+            result_str
+        );
+    }
+}
+
+// eval-okay-* tests contain a snippet of Nix code, and an expectation
+// of the produced string output of the evaluator.
+//
+// These evaluations are always supposed to succeed, i.e. all snippets
+// are guaranteed to be valid Nix code.
+#[rstest]
+fn eval_okay(#[files("src/tests/tvix_tests/eval-okay-*.nix")] code_path: PathBuf) {
+    eval_test(code_path, true)
+}
+
+// eval-okay-* tests from the original Nix test suite.
+#[cfg(feature = "nix_tests")]
+#[rstest]
+fn nix_eval_okay(#[files("src/tests/nix_tests/eval-okay-*.nix")] code_path: PathBuf) {
+    eval_test(code_path, true)
+}
+
+// eval-okay-* tests from the original Nix test suite which do not yet pass for tvix
+//
+// Eventually there will be none of these left, and this function
+// will disappear :) Until then, to run these tests, use `cargo test
+// --features expected_failures`.
+//
+// Please don't submit failing tests unless they're in
+// notyetpassing; this makes the test suite much more useful for
+// regression testing, since there should always be zero non-ignored
+// failing tests.
+//
+// NOTE: There's no such test anymore. `rstest` does not handle empty directories, so, we
+// just comment it for now.
+//
+// #[rstest]
+// fn nix_eval_okay_currently_failing(
+//     #[files("src/tests/nix_tests/notyetpassing/eval-okay-*.nix")] code_path: PathBuf,
+// ) {
+//     eval_test(code_path, false)
+// }
diff --git a/tvix/glue/src/tests/nix_tests/eval-okay-context-introspection.exp b/tvix/glue/src/tests/nix_tests/eval-okay-context-introspection.exp
new file mode 100644
index 0000000000..03b400cc88
--- /dev/null
+++ b/tvix/glue/src/tests/nix_tests/eval-okay-context-introspection.exp
@@ -0,0 +1 @@
+[ true true true true true true ]
diff --git a/tvix/glue/src/tests/nix_tests/eval-okay-context-introspection.nix b/tvix/glue/src/tests/nix_tests/eval-okay-context-introspection.nix
new file mode 100644
index 0000000000..354376b895
--- /dev/null
+++ b/tvix/glue/src/tests/nix_tests/eval-okay-context-introspection.nix
@@ -0,0 +1,42 @@
+let
+  drv = derivation {
+    name = "fail";
+    builder = "/bin/false";
+    system = "x86_64-linux";
+    outputs = [ "out" "foo" ];
+  };
+
+  path = "${./eval-okay-context-introspection.nix}";
+
+  desired-context = {
+    "${builtins.unsafeDiscardStringContext path}" = {
+      path = true;
+    };
+    "${builtins.unsafeDiscardStringContext drv.drvPath}" = {
+      outputs = [ "foo" "out" ];
+      allOutputs = true;
+    };
+  };
+
+  combo-path = "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
+  legit-context = builtins.getContext combo-path;
+
+  reconstructed-path = builtins.appendContext
+    (builtins.unsafeDiscardStringContext combo-path)
+    desired-context;
+
+  # Eta rule for strings with context.
+  etaRule = str:
+    str == builtins.appendContext
+      (builtins.unsafeDiscardStringContext str)
+      (builtins.getContext str);
+
+in
+[
+  (legit-context == desired-context)
+  (reconstructed-path == combo-path)
+  (etaRule "foo")
+  (etaRule drv.drvPath)
+  (etaRule drv.foo.outPath)
+  (etaRule (builtins.unsafeDiscardOutputDependency drv.drvPath))
+]
diff --git a/tvix/glue/src/tests/nix_tests/eval-okay-context.exp b/tvix/glue/src/tests/nix_tests/eval-okay-context.exp
new file mode 100644
index 0000000000..2f535bdbc4
--- /dev/null
+++ b/tvix/glue/src/tests/nix_tests/eval-okay-context.exp
@@ -0,0 +1 @@
+"foo eval-okay-context.nix bar"
diff --git a/tvix/glue/src/tests/nix_tests/eval-okay-context.nix b/tvix/glue/src/tests/nix_tests/eval-okay-context.nix
new file mode 100644
index 0000000000..c873211862
--- /dev/null
+++ b/tvix/glue/src/tests/nix_tests/eval-okay-context.nix
@@ -0,0 +1,6 @@
+let s = "foo ${builtins.substring 33 100 (baseNameOf "${./eval-okay-context.nix}")} bar";
+in
+if s != "foo eval-okay-context.nix bar"
+then abort "context not discarded"
+else builtins.unsafeDiscardStringContext s
+
diff --git a/tvix/glue/src/tests/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv b/tvix/glue/src/tests/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv
new file mode 100644
index 0000000000..559e93ed0e
--- /dev/null
+++ b/tvix/glue/src/tests/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar","r:sha1","0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")],[],[],":",":",[],[("builder",":"),("name","bar"),("out","/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar"),("outputHash","0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"),("outputHashAlgo","sha1"),("outputHashMode","recursive"),("system",":")])
\ No newline at end of file
diff --git a/tvix/glue/src/tests/tvix_tests/eval-okay-context-introspection.exp b/tvix/glue/src/tests/tvix_tests/eval-okay-context-introspection.exp
new file mode 100644
index 0000000000..a136b0035e
--- /dev/null
+++ b/tvix/glue/src/tests/tvix_tests/eval-okay-context-introspection.exp
@@ -0,0 +1 @@
+[ true true true true true true true true true true true true true ]
diff --git a/tvix/glue/src/tests/tvix_tests/eval-okay-context-introspection.nix b/tvix/glue/src/tests/tvix_tests/eval-okay-context-introspection.nix
new file mode 100644
index 0000000000..ecd8ab0073
--- /dev/null
+++ b/tvix/glue/src/tests/tvix_tests/eval-okay-context-introspection.nix
@@ -0,0 +1,83 @@
+# Contrary to the Nix tests, this one does not make any use of `builtins.appendContext`
+# It's a weaker yet interesting test by abusing knowledge on how does our builtins
+# performs propagation.
+let
+  drv = derivation {
+    name = "fail";
+    builder = "/bin/false";
+    system = "x86_64-linux";
+    outputs = [ "out" "foo" ];
+  };
+
+  # `substr` propagates context, we truncate to an empty string and concatenate to the target
+  # to infect it with the context of `copied`.
+  appendContextFrom = copied: target: (builtins.substring 0 0 "${copied}") + "${target}";
+
+  # `split` discards (!!) contexts, we first split by `/` (there's at least one such `/` by
+  # virtue of `target` being a store path, i.e. starting with `$store_root/$derivation_name`)
+  # then, we reassemble the list into a proper string.
+  discardContext = target: builtins.concatStringsSep "" (builtins.split "(.*)" "${target}");
+
+  # Note that this should never return true for any attribute set.
+  hasContextInAttrKeys = attrs: builtins.any builtins.hasContext (builtins.attrNames attrs);
+
+  path = "${./eval-okay-context-introspection.nix}";
+
+  # This is a context-less attribute set, which should be exactly the same
+  # as `builtins.getContext combo-path`.
+  desired-context = {
+    "${builtins.unsafeDiscardStringContext path}" = {
+      path = true;
+    };
+    "${builtins.unsafeDiscardStringContext drv.drvPath}" = {
+      outputs = [ "foo" "out" ];
+      allOutputs = true;
+    };
+  };
+
+  combo-path = "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
+  legit-context = builtins.getContext combo-path;
+
+  reconstructed-path = appendContextFrom combo-path
+    (builtins.unsafeDiscardStringContext combo-path);
+
+  an-str = {
+    a = "${drv}";
+  };
+  an-list = {
+    b = [ drv ];
+  };
+
+  # Eta rule for strings with context.
+  etaRule = str:
+    str == appendContextFrom
+      str
+      (builtins.unsafeDiscardStringContext str);
+
+  etaRule' = str:
+    str == appendContextFrom
+      str
+      (discardContext str);
+
+in
+[
+  (!hasContextInAttrKeys desired-context)
+  (legit-context."${builtins.unsafeDiscardStringContext path}".path)
+  (legit-context."${builtins.unsafeDiscardStringContext drv.drvPath}".outputs == [ "foo" "out" ])
+  # `allOutputs` is present only on DrvClosure-style context string, i.e. the
+  # context string of a drvPath itself, not an outPath.
+  (!builtins.hasAttr "allOutputs" (builtins.getContext drv.outPath)."${builtins.unsafeDiscardStringContext drv.drvPath}")
+  (builtins.hasAttr "allOutputs" legit-context."${builtins.unsafeDiscardStringContext drv.drvPath}")
+  (builtins.hasAttr "allOutputs" (builtins.getContext drv.drvPath)."${builtins.unsafeDiscardStringContext drv.drvPath}")
+  (legit-context == desired-context) # FIXME(raitobezarius): this should not use `builtins.seq`, this is a consequence of excessive laziness of Tvix, I believe.
+  (reconstructed-path == combo-path)
+  # Those are too slow?
+  # (etaRule' "foo")
+  # (etaRule' combo-path)
+  (etaRule "foo")
+  (etaRule drv.drvPath)
+  (etaRule drv.foo.outPath)
+  # `toJSON` tests
+  (builtins.hasContext (builtins.toJSON an-str))
+  (builtins.hasContext (builtins.toJSON an-list))
+]
diff --git a/tvix/glue/src/tests/tvix_tests/eval-okay-context-propagation.exp b/tvix/glue/src/tests/tvix_tests/eval-okay-context-propagation.exp
new file mode 100644
index 0000000000..ff56f6ca18
--- /dev/null
+++ b/tvix/glue/src/tests/tvix_tests/eval-okay-context-propagation.exp
@@ -0,0 +1 @@
+[ true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true ]
diff --git a/tvix/glue/src/tests/tvix_tests/eval-okay-context-propagation.nix b/tvix/glue/src/tests/tvix_tests/eval-okay-context-propagation.nix
new file mode 100644
index 0000000000..41e7f207b9
--- /dev/null
+++ b/tvix/glue/src/tests/tvix_tests/eval-okay-context-propagation.nix
@@ -0,0 +1,119 @@
+# We test various propagation of contexts under other builtins here.
+let
+  drv = derivation {
+    name = "fail";
+    builder = "/bin/false";
+    system = "x86_64-linux";
+    outputs = [ "out" "foo" ];
+  };
+  other-drv = derivation {
+    name = "other-fail";
+    builder = "/bin/false";
+    system = "x86_64-linux";
+    outputs = [ "out" "bar" ];
+  };
+  a-path-drv = builtins.path {
+    name = "a-path-drv";
+    path = ./eval-okay-context-introspection.nix;
+  };
+  another-path-drv = builtins.filterSource (_: true) ./eval-okay-context-introspection.nix;
+
+  # `substr` propagates context, we truncate to an empty string and concatenate to the target
+  # to infect it with the context of `copied`.
+  appendContextFrom = copied: target: (builtins.substring 0 0 copied) + target;
+
+  path = "${./eval-okay-context-introspection.nix}";
+
+  combo-path = "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
+
+  mergeContext = a: b:
+    builtins.getContext a // builtins.getContext b;
+
+  preserveContext = origin: result:
+    builtins.getContext "${result}" == builtins.getContext "${origin}";
+
+  preserveContexts = origins: result:
+    let union = builtins.foldl' (x: y: x // y) { } (builtins.map (d: builtins.getContext "${d}") origins);
+    in
+    union == builtins.getContext "${result}";
+in
+[
+  # `toFile` should produce context.
+  (builtins.hasContext "${(builtins.toFile "myself" "${./eval-okay-context-introspection.nix}")}")
+  # `derivation` should produce context.
+  (builtins.hasContext "${drv}")
+  # `builtins.path` / `builtins.filterSource` should produce context.
+  (builtins.hasContext "${a-path-drv}")
+  (builtins.hasContext "${another-path-drv}")
+  # Low-level test to ensure that interpolation is working as expected.
+  (builtins.length (builtins.attrNames (builtins.getContext "${drv}${other-drv}")) == 2)
+  (builtins.getContext "${drv}${other-drv}" == mergeContext drv other-drv)
+  # Those three next tests are extremely related.
+  # To test interpolation, we need concatenation to be working and vice versa.
+  # In addition, we need `builtins.substring` empty string propagation to attach context
+  # in absence of `builtins.appendContext`.
+  # The previous test should ensure that we don't test vacuous truths.
+  # Substring preserves contexts.
+  (preserveContext combo-path (builtins.substring 0 0 combo-path)) # <- FIXME: broken
+  # Interpolation preserves contexts.
+  (preserveContext "${drv}${other-drv}" (appendContextFrom drv other-drv))
+  # Concatenation preserves contexts.
+  (preserveContext "${drv}${other-drv}" (drv + other-drv))
+  # Special case when Nix does not assert that the length argument is non-negative
+  # when the starting index is β‰₯Β than the string's length.
+  # FIXME: those three are broken too, NON DETERMINISTIC!!!
+  (preserveContext combo-path (builtins.substring 5 (-5) (builtins.substring 0 0 combo-path)))
+  (preserveContext combo-path (toString combo-path))
+  # No replacement should yield at least the same context.
+  (preserveContext combo-path (builtins.replaceStrings [ ] [ ] combo-path))
+  # This is an idempotent replacement, it should yield therefore to full preservation of the context.
+  (preserveContext "${drv}${drv}" (builtins.replaceStrings [ "${drv}" ] [ "${drv}" ] "${drv}"))
+  # There's no context here, so no context should appear from `drv`.
+  (preserveContext "abc" (builtins.replaceStrings [ "${drv}" ] [ "${drv}" ] "abc"))
+  # Context should appear by a successful replacement.
+  (preserveContext "${drv}" (builtins.replaceStrings [ "a" ] [ "${drv}" ] "a"))
+  # We test multiple successful replacements.
+  (preserveContexts [ drv other-drv ] (builtins.replaceStrings [ "a" "b" ] [ "${drv}" "${other-drv}" ] "ab"))
+  # We test *empty* string replacements.
+  (preserveContext "${drv}" (builtins.replaceStrings [ "" ] [ "${drv}" ] "abc"))
+  (preserveContext "${drv}" (builtins.replaceStrings [ "" ] [ "${drv}" ] ""))
+  # There should be no context in a parsed derivation name.
+  (!builtins.any builtins.hasContext (builtins.attrValues (builtins.parseDrvName "${drv.name}")))
+  # Nix does not propagate contexts for `match`.
+  (!builtins.any builtins.hasContext (builtins.match "(.*)" "${drv}"))
+  # `dirOf` preserves contexts of non-paths.
+  (preserveContext "${drv}" (builtins.dirOf "${drv}"))
+  (preserveContext "abc" (builtins.dirOf "abc"))
+  # `baseNameOf propagates context of argument
+  (preserveContext "${drv}" (builtins.baseNameOf drv))
+  (preserveContext "abc" (builtins.baseNameOf "abc"))
+  # `concatStringsSep` preserves contexts of both arguments.
+  (preserveContexts [ drv other-drv ] (builtins.concatStringsSep "${other-drv}" (map toString [ drv drv drv drv drv ])))
+  (preserveContext drv (builtins.concatStringsSep "|" (map toString [ drv drv drv drv drv ])))
+  (preserveContext other-drv (builtins.concatStringsSep "${other-drv}" [ "abc" "def" ]))
+  # `attrNames` will never ever produce context.
+  (preserveContext "abc" (toString (builtins.attrNames { a = { }; b = { }; c = { }; })))
+  # `toJSON` preserves context of its inputs.
+  (preserveContexts [ drv other-drv ] (builtins.toJSON {
+    a = [ drv ];
+    b = [ other-drv ];
+  }))
+  (preserveContexts [ drv other-drv ] (builtins.toJSON {
+    a.deep = [ drv ];
+    b = [ other-drv ];
+  }))
+  (preserveContexts [ drv other-drv ] (builtins.toJSON {
+    a = "${drv}";
+    b = [ other-drv ];
+  }))
+  (preserveContexts [ drv other-drv ] (builtins.toJSON {
+    a.deep = "${drv}";
+    b = [ other-drv ];
+  }))
+  (preserveContexts [ drv other-drv ] (builtins.toJSON {
+    a = "${drv} ${other-drv}";
+  }))
+  (preserveContexts [ drv other-drv ] (builtins.toJSON {
+    a.b.c.d.e.f = "${drv} ${other-drv}";
+  }))
+]
diff --git a/tvix/glue/src/tests/tvix_tests/eval-okay-fetchtarball.exp b/tvix/glue/src/tests/tvix_tests/eval-okay-fetchtarball.exp
new file mode 100644
index 0000000000..c7332c0503
--- /dev/null
+++ b/tvix/glue/src/tests/tvix_tests/eval-okay-fetchtarball.exp
@@ -0,0 +1 @@
+[ /nix/store/7adgvk5zdfq4pwrhsm3n9lzypb12gw0g-source /nix/store/7adgvk5zdfq4pwrhsm3n9lzypb12gw0g-source /nix/store/7adgvk5zdfq4pwrhsm3n9lzypb12gw0g-source /nix/store/7adgvk5zdfq4pwrhsm3n9lzypb12gw0g-source /nix/store/7adgvk5zdfq4pwrhsm3n9lzypb12gw0g-source /nix/store/md9dsn2zwa6aj7zzalvjwwwx82whcyva-some-name ]
diff --git a/tvix/glue/src/tests/tvix_tests/eval-okay-fetchtarball.nix b/tvix/glue/src/tests/tvix_tests/eval-okay-fetchtarball.nix
new file mode 100644
index 0000000000..e454f12444
--- /dev/null
+++ b/tvix/glue/src/tests/tvix_tests/eval-okay-fetchtarball.nix
@@ -0,0 +1,42 @@
+[
+  # (fetchTarball "url") cannot be tested, as that one has to fetch from the
+  # internet to calculate the path.
+
+  # with url and sha256
+  (builtins.fetchTarball {
+    url = "https://github.com/NixOS/nixpkgs/archive/91050ea1e57e50388fa87a3302ba12d188ef723a.tar.gz";
+    sha256 = "1hf6cgaci1n186kkkjq106ryf8mmlq9vnwgfwh625wa8hfgdn4dm";
+  })
+
+  # with url and sha256 (as SRI)
+  (builtins.fetchTarball {
+    url = "https://github.com/NixOS/nixpkgs/archive/91050ea1e57e50388fa87a3302ba12d188ef723a.tar.gz";
+    sha256 = "sha256-tRHbnoNI8SIM5O5xuxOmtSLnswEByzmnQcGGyNRjxsE=";
+  })
+
+  # with another url, it actually doesn't matter (no .gz prefix)
+  (builtins.fetchTarball {
+    url = "https://github.com/NixOS/nixpkgs/archive/91050ea1e57e50388fa87a3302ba12d188ef723a.tar";
+    sha256 = "sha256-tRHbnoNI8SIM5O5xuxOmtSLnswEByzmnQcGGyNRjxsE=";
+  })
+
+  # also with an entirely different url, it doesn't change
+  (builtins.fetchTarball {
+    url = "https://test.example/owo";
+    sha256 = "sha256-tRHbnoNI8SIM5O5xuxOmtSLnswEByzmnQcGGyNRjxsE=";
+  })
+
+  # … because `name` defaults to source, and that (and the sha256 affect the store path)
+  (builtins.fetchTarball {
+    name = "source";
+    url = "https://test.example/owo";
+    sha256 = "sha256-tRHbnoNI8SIM5O5xuxOmtSLnswEByzmnQcGGyNRjxsE=";
+  })
+
+  # … so changing name causes the hash to change.
+  (builtins.fetchTarball {
+    name = "some-name";
+    url = "https://test.example/owo";
+    sha256 = "sha256-tRHbnoNI8SIM5O5xuxOmtSLnswEByzmnQcGGyNRjxsE=";
+  })
+]
diff --git a/tvix/glue/src/tests/tvix_tests/eval-okay-fetchurl.exp b/tvix/glue/src/tests/tvix_tests/eval-okay-fetchurl.exp
new file mode 100644
index 0000000000..37a04d577c
--- /dev/null
+++ b/tvix/glue/src/tests/tvix_tests/eval-okay-fetchurl.exp
@@ -0,0 +1 @@
+[ /nix/store/y0r1p1cqmlvm0yqkz3gxvkc1p8kg2sz8-null /nix/store/06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch /nix/store/06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch /nix/store/06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch ]
diff --git a/tvix/glue/src/tests/tvix_tests/eval-okay-fetchurl.nix b/tvix/glue/src/tests/tvix_tests/eval-okay-fetchurl.nix
new file mode 100644
index 0000000000..8a39101525
--- /dev/null
+++ b/tvix/glue/src/tests/tvix_tests/eval-okay-fetchurl.nix
@@ -0,0 +1,25 @@
+[
+  # (fetchurl "url") needs to immediately fetch, but our options without
+  # internet access are fairly limited.
+  # TODO: populate some fixtures at a known location instead.
+  (builtins.fetchurl "file:///dev/null")
+
+  # fetchurl with url and sha256
+  (builtins.fetchurl {
+    url = "https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch";
+    sha256 = "0nawkl04sj7psw6ikzay7kydj3dhd0fkwghcsf5rzaw4bmp4kbax";
+  })
+
+  # fetchurl with url and sha256 (as SRI)
+  (builtins.fetchurl {
+    url = "https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch";
+    sha256 = "sha256-Xa1Jbl2Eq5+L0ww+Ph1osA3Z/Dxe/RkN1/dITQCdXFk=";
+  })
+
+  # fetchurl with another url, but same name
+  (builtins.fetchurl {
+    url = "https://test.example/owo";
+    name = "notmuch-extract-patch";
+    sha256 = "sha256-Xa1Jbl2Eq5+L0ww+Ph1osA3Z/Dxe/RkN1/dITQCdXFk=";
+  })
+]
diff --git a/tvix/glue/src/tvix_build.rs b/tvix/glue/src/tvix_build.rs
new file mode 100644
index 0000000000..e9eb1725ef
--- /dev/null
+++ b/tvix/glue/src/tvix_build.rs
@@ -0,0 +1,439 @@
+//! This module contains glue code translating from
+//! [nix_compat::derivation::Derivation] to [tvix_build::proto::BuildRequest].
+
+use std::collections::{BTreeMap, BTreeSet};
+
+use bytes::Bytes;
+use nix_compat::{derivation::Derivation, nixbase32};
+use sha2::{Digest, Sha256};
+use tvix_build::proto::{
+    build_request::{AdditionalFile, BuildConstraints, EnvVar},
+    BuildRequest,
+};
+use tvix_castore::proto::{self, node::Node};
+
+/// These are the environment variables that Nix sets in its sandbox for every
+/// build.
+const NIX_ENVIRONMENT_VARS: [(&str, &str); 12] = [
+    ("HOME", "/homeless-shelter"),
+    ("NIX_BUILD_CORES", "0"), // TODO: make this configurable?
+    ("NIX_BUILD_TOP", "/"),
+    ("NIX_LOG_FD", "2"),
+    ("NIX_STORE", "/nix/store"),
+    ("PATH", "/path-not-set"),
+    ("PWD", "/build"),
+    ("TEMP", "/build"),
+    ("TEMPDIR", "/build"),
+    ("TERM", "xterm-256color"),
+    ("TMP", "/build"),
+    ("TMPDIR", "/build"),
+];
+
+/// Takes a [Derivation] and turns it into a [BuildRequest].
+/// It assumes the Derivation has been validated.
+/// It needs two lookup functions:
+/// - one translating input sources to a castore node
+///   (`fn_input_sources_to_node`)
+/// - one translating a tuple of drv path and (a subset of their) output names to
+///   castore nodes of the selected outpus (`fn_input_drvs_to_output_nodes`).
+#[allow(clippy::mutable_key_type)]
+pub(crate) fn derivation_to_build_request(
+    derivation: &Derivation,
+    inputs: BTreeSet<Node>,
+) -> std::io::Result<BuildRequest> {
+    debug_assert!(derivation.validate(true).is_ok(), "drv must validate");
+
+    // produce command_args, which is builder and arguments in a Vec.
+    let mut command_args: Vec<String> = Vec::with_capacity(derivation.arguments.len() + 1);
+    command_args.push(derivation.builder.clone());
+    command_args.extend_from_slice(&derivation.arguments);
+
+    // produce output_paths, which is the absolute path of each output (sorted)
+    let mut output_paths: Vec<String> = derivation
+        .outputs
+        .values()
+        .map(|e| e.path_str()[1..].to_owned())
+        .collect();
+
+    // Sort the outputs. We can use sort_unstable, as these are unique strings.
+    output_paths.sort_unstable();
+
+    // Produce environment_vars and additional files.
+    // We use a BTreeMap while producing, and only realize the resulting Vec
+    // while populating BuildRequest, so we don't need to worry about ordering.
+    let mut environment_vars: BTreeMap<String, Bytes> = BTreeMap::new();
+    let mut additional_files: BTreeMap<String, Bytes> = BTreeMap::new();
+
+    // Start with some the ones that nix magically sets:
+    environment_vars.extend(
+        NIX_ENVIRONMENT_VARS
+            .iter()
+            .map(|(k, v)| (k.to_string(), Bytes::from_static(v.as_bytes()))),
+    );
+
+    // extend / overwrite with the keys set in the derivation environment itself.
+    // TODO: check if this order is correct, and environment vars set in the
+    // *Derivation actually* have priority.
+    environment_vars.extend(
+        derivation
+            .environment
+            .iter()
+            .map(|(k, v)| (k.clone(), Bytes::from(v.to_vec()))),
+    );
+
+    handle_pass_as_file(&mut environment_vars, &mut additional_files)?;
+
+    // TODO: handle __json (structured attrs, provide JSON file and source-able bash script)
+
+    // Produce constraints.
+    let constraints = Some(BuildConstraints {
+        system: derivation.system.clone(),
+        min_memory: 0,
+        available_ro_paths: vec![],
+        // in case this is a fixed-output derivation, allow network access.
+        network_access: derivation.outputs.len() == 1
+            && derivation
+                .outputs
+                .get("out")
+                .expect("invalid derivation")
+                .is_fixed(),
+        provide_bin_sh: true,
+    });
+
+    let build_request = BuildRequest {
+        command_args,
+        outputs: output_paths,
+
+        // Turn this into a sorted-by-key Vec<EnvVar>.
+        environment_vars: environment_vars
+            .into_iter()
+            .map(|(key, value)| EnvVar { key, value })
+            .collect(),
+        inputs: inputs
+            .into_iter()
+            .map(|n| proto::Node { node: Some(n) })
+            .collect(),
+        inputs_dir: nix_compat::store_path::STORE_DIR[1..].into(),
+        constraints,
+        working_dir: "build".into(),
+        scratch_paths: vec!["build".into(), "nix/store".into()],
+        additional_files: additional_files
+            .into_iter()
+            .map(|(path, contents)| AdditionalFile { path, contents })
+            .collect(),
+    };
+
+    debug_assert!(
+        build_request.validate().is_ok(),
+        "invalid BuildRequest: {}",
+        build_request.validate().unwrap_err()
+    );
+
+    Ok(build_request)
+}
+
+/// handle passAsFile, if set.
+/// For each env $x in that list, the original env is removed, and a $xPath
+/// environment var added instead, referring to a path inside the build with
+/// the contents from the original env var.
+fn handle_pass_as_file(
+    environment_vars: &mut BTreeMap<String, Bytes>,
+    additional_files: &mut BTreeMap<String, Bytes>,
+) -> std::io::Result<()> {
+    let pass_as_file = environment_vars.get("passAsFile").map(|v| {
+        // Convert pass_as_file to string.
+        // When it gets here, it contains a space-separated list of env var
+        // keys, which must be strings.
+        String::from_utf8(v.to_vec())
+    });
+
+    if let Some(pass_as_file) = pass_as_file {
+        let pass_as_file = pass_as_file.map_err(|_| {
+            std::io::Error::new(
+                std::io::ErrorKind::InvalidInput,
+                "passAsFile elements are no valid utf8 strings",
+            )
+        })?;
+
+        for x in pass_as_file.split(' ') {
+            match environment_vars.remove_entry(x) {
+                Some((k, contents)) => {
+                    let (new_k, path) = calculate_pass_as_file_env(&k);
+
+                    additional_files.insert(path[1..].to_string(), contents);
+                    environment_vars.insert(new_k, Bytes::from(path));
+                }
+                None => {
+                    return Err(std::io::Error::new(
+                        std::io::ErrorKind::InvalidData,
+                        "passAsFile refers to non-existent env key",
+                    ));
+                }
+            }
+        }
+    }
+
+    Ok(())
+}
+
+/// For a given key k in a derivation environment that's supposed to be passed as file,
+/// calculate the ${k}Path key and filepath value that it's being replaced with
+/// while preparing the build.
+/// The filepath is `/build/.attrs-${nixbase32(sha256(key))`.
+fn calculate_pass_as_file_env(k: &str) -> (String, String) {
+    (
+        format!("{}Path", k),
+        format!(
+            "/build/.attr-{}",
+            nixbase32::encode(&Sha256::new_with_prefix(k).finalize())
+        ),
+    )
+}
+
+#[cfg(test)]
+mod test {
+    use std::collections::BTreeSet;
+
+    use bytes::Bytes;
+    use nix_compat::derivation::Derivation;
+    use tvix_build::proto::{
+        build_request::{AdditionalFile, BuildConstraints, EnvVar},
+        BuildRequest,
+    };
+    use tvix_castore::{
+        fixtures::DUMMY_DIGEST,
+        proto::{self, node::Node, DirectoryNode},
+    };
+
+    use crate::tvix_build::NIX_ENVIRONMENT_VARS;
+
+    use super::derivation_to_build_request;
+    use lazy_static::lazy_static;
+
+    lazy_static! {
+        static ref INPUT_NODE_FOO: Node = Node::Directory(DirectoryNode {
+            name: Bytes::from("mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar"),
+            digest: DUMMY_DIGEST.clone().into(),
+            size: 42,
+        });
+    }
+
+    #[test]
+    fn test_derivation_to_build_request() {
+        let aterm_bytes = include_bytes!("tests/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv");
+
+        let derivation = Derivation::from_aterm_bytes(aterm_bytes).expect("must parse");
+
+        let build_request =
+            derivation_to_build_request(&derivation, BTreeSet::from([INPUT_NODE_FOO.clone()]))
+                .expect("must succeed");
+
+        let mut expected_environment_vars = vec![
+            EnvVar {
+                key: "bar".into(),
+                value: "/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar".into(),
+            },
+            EnvVar {
+                key: "builder".into(),
+                value: ":".into(),
+            },
+            EnvVar {
+                key: "name".into(),
+                value: "foo".into(),
+            },
+            EnvVar {
+                key: "out".into(),
+                value: "/nix/store/fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo".into(),
+            },
+            EnvVar {
+                key: "system".into(),
+                value: ":".into(),
+            },
+        ];
+
+        expected_environment_vars.extend(NIX_ENVIRONMENT_VARS.iter().map(|(k, v)| EnvVar {
+            key: k.to_string(),
+            value: Bytes::from_static(v.as_bytes()),
+        }));
+
+        expected_environment_vars.sort_unstable_by_key(|e| e.key.to_owned());
+
+        assert_eq!(
+            BuildRequest {
+                command_args: vec![":".into()],
+                outputs: vec!["nix/store/fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo".into()],
+                environment_vars: expected_environment_vars,
+                inputs: vec![proto::Node {
+                    node: Some(INPUT_NODE_FOO.clone())
+                }],
+                inputs_dir: "nix/store".into(),
+                constraints: Some(BuildConstraints {
+                    system: derivation.system.clone(),
+                    min_memory: 0,
+                    network_access: false,
+                    available_ro_paths: vec![],
+                    provide_bin_sh: true,
+                }),
+                additional_files: vec![],
+                working_dir: "build".into(),
+                scratch_paths: vec!["build".into(), "nix/store".into()],
+            },
+            build_request
+        );
+    }
+
+    #[test]
+    fn test_fod_to_build_request() {
+        let aterm_bytes = include_bytes!("tests/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv");
+
+        let derivation = Derivation::from_aterm_bytes(aterm_bytes).expect("must parse");
+
+        let build_request =
+            derivation_to_build_request(&derivation, BTreeSet::from([])).expect("must succeed");
+
+        let mut expected_environment_vars = vec![
+            EnvVar {
+                key: "builder".into(),
+                value: ":".into(),
+            },
+            EnvVar {
+                key: "name".into(),
+                value: "bar".into(),
+            },
+            EnvVar {
+                key: "out".into(),
+                value: "/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar".into(),
+            },
+            EnvVar {
+                key: "outputHash".into(),
+                value: "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba".into(),
+            },
+            EnvVar {
+                key: "outputHashAlgo".into(),
+                value: "sha256".into(),
+            },
+            EnvVar {
+                key: "outputHashMode".into(),
+                value: "recursive".into(),
+            },
+            EnvVar {
+                key: "system".into(),
+                value: ":".into(),
+            },
+        ];
+
+        expected_environment_vars.extend(NIX_ENVIRONMENT_VARS.iter().map(|(k, v)| EnvVar {
+            key: k.to_string(),
+            value: Bytes::from_static(v.as_bytes()),
+        }));
+
+        expected_environment_vars.sort_unstable_by_key(|e| e.key.to_owned());
+
+        assert_eq!(
+            BuildRequest {
+                command_args: vec![":".to_string()],
+                outputs: vec!["nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar".into()],
+                environment_vars: expected_environment_vars,
+                inputs: vec![],
+                inputs_dir: "nix/store".into(),
+                constraints: Some(BuildConstraints {
+                    system: derivation.system.clone(),
+                    min_memory: 0,
+                    network_access: true,
+                    available_ro_paths: vec![],
+                    provide_bin_sh: true,
+                }),
+                additional_files: vec![],
+                working_dir: "build".into(),
+                scratch_paths: vec!["build".into(), "nix/store".into()],
+            },
+            build_request
+        );
+    }
+
+    #[test]
+    fn test_pass_as_file() {
+        // (builtins.derivation { "name" = "foo"; passAsFile = ["bar" "baz"]; bar = "baz"; baz = "bar"; system = ":"; builder = ":";}).drvPath
+        let aterm_bytes = r#"Derive([("out","/nix/store/pp17lwra2jkx8rha15qabg2q3wij72lj-foo","","")],[],[],":",":",[],[("bar","baz"),("baz","bar"),("builder",":"),("name","foo"),("out","/nix/store/pp17lwra2jkx8rha15qabg2q3wij72lj-foo"),("passAsFile","bar baz"),("system",":")])"#.as_bytes();
+
+        let derivation = Derivation::from_aterm_bytes(aterm_bytes).expect("must parse");
+
+        let build_request =
+            derivation_to_build_request(&derivation, BTreeSet::from([])).expect("must succeed");
+
+        let mut expected_environment_vars = vec![
+            // Note how bar and baz are not present in the env anymore,
+            // but replaced with barPath, bazPath respectively.
+            EnvVar {
+                key: "barPath".into(),
+                value: "/build/.attr-1fcgpy7vc4ammr7s17j2xq88scswkgz23dqzc04g8sx5vcp2pppw".into(),
+            },
+            EnvVar {
+                key: "bazPath".into(),
+                value: "/build/.attr-15l04iksj1280dvhbzdq9ai3wlf8ac2188m9qv0gn81k9nba19ds".into(),
+            },
+            EnvVar {
+                key: "builder".into(),
+                value: ":".into(),
+            },
+            EnvVar {
+                key: "name".into(),
+                value: "foo".into(),
+            },
+            EnvVar {
+                key: "out".into(),
+                value: "/nix/store/pp17lwra2jkx8rha15qabg2q3wij72lj-foo".into(),
+            },
+            // passAsFile stays around
+            EnvVar {
+                key: "passAsFile".into(),
+                value: "bar baz".into(),
+            },
+            EnvVar {
+                key: "system".into(),
+                value: ":".into(),
+            },
+        ];
+
+        expected_environment_vars.extend(NIX_ENVIRONMENT_VARS.iter().map(|(k, v)| EnvVar {
+            key: k.to_string(),
+            value: Bytes::from_static(v.as_bytes()),
+        }));
+
+        expected_environment_vars.sort_unstable_by_key(|e| e.key.to_owned());
+
+        assert_eq!(
+            BuildRequest {
+                command_args: vec![":".to_string()],
+                outputs: vec!["nix/store/pp17lwra2jkx8rha15qabg2q3wij72lj-foo".into()],
+                environment_vars: expected_environment_vars,
+                inputs: vec![],
+                inputs_dir: "nix/store".into(),
+                constraints: Some(BuildConstraints {
+                    system: derivation.system.clone(),
+                    min_memory: 0,
+                    network_access: false,
+                    available_ro_paths: vec![],
+                    provide_bin_sh: true,
+                }),
+                additional_files: vec![
+                    // baz env
+                    AdditionalFile {
+                        path: "build/.attr-15l04iksj1280dvhbzdq9ai3wlf8ac2188m9qv0gn81k9nba19ds"
+                            .into(),
+                        contents: "bar".into()
+                    },
+                    // bar env
+                    AdditionalFile {
+                        path: "build/.attr-1fcgpy7vc4ammr7s17j2xq88scswkgz23dqzc04g8sx5vcp2pppw"
+                            .into(),
+                        contents: "baz".into(),
+                    },
+                ],
+                working_dir: "build".into(),
+                scratch_paths: vec!["build".into(), "nix/store".into()],
+            },
+            build_request
+        );
+    }
+}
diff --git a/tvix/glue/src/tvix_io.rs b/tvix/glue/src/tvix_io.rs
new file mode 100644
index 0000000000..0e5f23b990
--- /dev/null
+++ b/tvix/glue/src/tvix_io.rs
@@ -0,0 +1,66 @@
+//! This module implements a wrapper around tvix-eval's [EvalIO] type,
+//! adding functionality which is required by tvix-cli:
+//!
+//! 1. Marking plain paths known to the reference scanner.
+//! 2. Handling the C++ Nix `__corepkgs__`-hack for nixpkgs bootstrapping.
+//!
+//! All uses of [EvalIO] in tvix-cli must make use of this wrapper,
+//! otherwise fundamental features like nixpkgs bootstrapping and hash
+//! calculation will not work.
+
+use std::io::{self, Cursor};
+use std::path::{Path, PathBuf};
+use tvix_eval::{EvalIO, FileType};
+
+// TODO: Merge this together with TvixStoreIO?
+pub struct TvixIO<T> {
+    // Actual underlying [EvalIO] implementation.
+    actual: T,
+}
+
+impl<T> TvixIO<T> {
+    pub fn new(actual: T) -> Self {
+        Self { actual }
+    }
+}
+
+impl<T> EvalIO for TvixIO<T>
+where
+    T: AsRef<dyn EvalIO>,
+{
+    fn store_dir(&self) -> Option<String> {
+        self.actual.as_ref().store_dir()
+    }
+
+    fn import_path(&self, path: &Path) -> io::Result<PathBuf> {
+        self.actual.as_ref().import_path(path)
+    }
+
+    fn path_exists(&self, path: &Path) -> io::Result<bool> {
+        if path.starts_with("/__corepkgs__") {
+            return Ok(true);
+        }
+
+        self.actual.as_ref().path_exists(path)
+    }
+
+    fn open(&self, path: &Path) -> io::Result<Box<dyn io::Read>> {
+        // Bundled version of corepkgs/fetchurl.nix. The counterpart
+        // of this happens in [crate::configure_nix_path], where the `nix_path`
+        // of the evaluation has `nix=/__corepkgs__` added to it.
+        //
+        // This workaround is similar to what cppnix does for passing
+        // the path through.
+        //
+        // TODO: this comparison is bad we should use the sane path library.
+        if path.starts_with("/__corepkgs__/fetchurl.nix") {
+            return Ok(Box::new(Cursor::new(include_bytes!("fetchurl.nix"))));
+        }
+
+        self.actual.as_ref().open(path)
+    }
+
+    fn read_dir(&self, path: &Path) -> io::Result<Vec<(bytes::Bytes, FileType)>> {
+        self.actual.as_ref().read_dir(path)
+    }
+}
diff --git a/tvix/glue/src/tvix_store_io.rs b/tvix/glue/src/tvix_store_io.rs
new file mode 100644
index 0000000000..7daefffe82
--- /dev/null
+++ b/tvix/glue/src/tvix_store_io.rs
@@ -0,0 +1,717 @@
+//! This module provides an implementation of EvalIO talking to tvix-store.
+
+use async_recursion::async_recursion;
+use bytes::Bytes;
+use futures::{StreamExt, TryStreamExt};
+use nix_compat::nixhash::NixHash;
+use nix_compat::store_path::StorePathRef;
+use nix_compat::{nixhash::CAHash, store_path::StorePath};
+use sha2::{Digest, Sha256};
+use std::{
+    cell::RefCell,
+    collections::BTreeSet,
+    io,
+    path::{Path, PathBuf},
+    sync::Arc,
+};
+use tokio_util::io::SyncIoBridge;
+use tracing::{error, info, instrument, warn, Level};
+use tvix_build::buildservice::BuildService;
+use tvix_castore::proto::node::Node;
+use tvix_eval::{EvalIO, FileType, StdIO};
+use tvix_store::utils::AsyncIoBridge;
+
+use tvix_castore::{
+    blobservice::BlobService,
+    directoryservice::{self, DirectoryService},
+    proto::NamedNode,
+    B3Digest,
+};
+use tvix_store::{pathinfoservice::PathInfoService, proto::PathInfo};
+
+use crate::fetchers::Fetcher;
+use crate::known_paths::KnownPaths;
+use crate::tvix_build::derivation_to_build_request;
+
+/// Implements [EvalIO], asking given [PathInfoService], [DirectoryService]
+/// and [BlobService].
+///
+/// In case the given path does not exist in these stores, we ask StdIO.
+/// This is to both cover cases of syntactically valid store paths, that exist
+/// on the filesystem (still managed by Nix), as well as being able to read
+/// files outside store paths.
+///
+/// This structure is also directly used by the derivation builtins
+/// and tightly coupled to it.
+///
+/// In the future, we may revisit that coupling and figure out how to generalize this interface and
+/// hide this implementation detail of the glue itself so that glue can be used with more than one
+/// implementation of "Tvix Store IO" which does not necessarily bring the concept of blob service,
+/// directory service or path info service.
+pub struct TvixStoreIO {
+    // This is public so helper functions can interact with the stores directly.
+    pub(crate) blob_service: Arc<dyn BlobService>,
+    pub(crate) directory_service: Arc<dyn DirectoryService>,
+    pub(crate) path_info_service: Arc<dyn PathInfoService>,
+    std_io: StdIO,
+    #[allow(dead_code)]
+    build_service: Arc<dyn BuildService>,
+    pub(crate) tokio_handle: tokio::runtime::Handle,
+
+    pub(crate) fetcher:
+        Fetcher<Arc<dyn BlobService>, Arc<dyn DirectoryService>, Arc<dyn PathInfoService>>,
+
+    // Paths known how to produce, by building or fetching.
+    pub(crate) known_paths: RefCell<KnownPaths>,
+}
+
+impl TvixStoreIO {
+    pub fn new(
+        blob_service: Arc<dyn BlobService>,
+        directory_service: Arc<dyn DirectoryService>,
+        path_info_service: Arc<dyn PathInfoService>,
+        build_service: Arc<dyn BuildService>,
+        tokio_handle: tokio::runtime::Handle,
+    ) -> Self {
+        Self {
+            blob_service: blob_service.clone(),
+            directory_service: directory_service.clone(),
+            path_info_service: path_info_service.clone(),
+            std_io: StdIO {},
+            build_service,
+            tokio_handle,
+            fetcher: Fetcher::new(blob_service, directory_service, path_info_service),
+            known_paths: Default::default(),
+        }
+    }
+
+    /// for a given [StorePath] and additional [Path] inside the store path,
+    /// look up the [PathInfo], and if it exists, and then use
+    /// [directoryservice::descend_to] to return the
+    /// [Node] specified by `sub_path`.
+    ///
+    /// In case there is no PathInfo yet, this means we need to build it
+    /// (which currently is stubbed out still).
+    #[async_recursion(?Send)]
+    #[instrument(skip(self, store_path), fields(store_path=%store_path), ret(level = Level::TRACE), err)]
+    async fn store_path_to_node(
+        &self,
+        store_path: &StorePath,
+        sub_path: &Path,
+    ) -> io::Result<Option<Node>> {
+        // Find the root node for the store_path.
+        // It asks the PathInfoService first, but in case there was a Derivation
+        // produced that would build it, fall back to triggering the build.
+        // To populate the input nodes, it might recursively trigger builds of
+        // its dependencies too.
+        let root_node = match self
+            .path_info_service
+            .as_ref()
+            .get(*store_path.digest())
+            .await?
+        {
+            // if we have a PathInfo, we know there will be a root_node (due to validation)
+            Some(path_info) => path_info.node.expect("no node").node.expect("no node"),
+            // If there's no PathInfo found, this normally means we have to
+            // trigger the build (and insert into PathInfoService, after
+            // reference scanning).
+            // However, as Tvix is (currently) not managing /nix/store itself,
+            // we return Ok(None) to let std_io take over.
+            // While reading from store paths that are not known to Tvix during
+            // that evaluation clearly is an impurity, we still need to support
+            // it for things like <nixpkgs> pointing to a store path.
+            // In the future, these things will (need to) have PathInfo.
+            None => {
+                // The store path doesn't exist yet, so we need to fetch or build it.
+                // We check for fetches first, as we might have both native
+                // fetchers and FODs in KnownPaths, and prefer the former.
+
+                let maybe_fetch = self
+                    .known_paths
+                    .borrow()
+                    .get_fetch_for_output_path(store_path);
+
+                match maybe_fetch {
+                    Some((name, fetch)) => {
+                        info!(?fetch, "triggering lazy fetch");
+                        let (sp, root_node) = self
+                            .fetcher
+                            .ingest_and_persist(&name, fetch)
+                            .await
+                            .map_err(|e| {
+                            std::io::Error::new(std::io::ErrorKind::InvalidData, e)
+                        })?;
+
+                        debug_assert_eq!(
+                            sp.to_string(),
+                            store_path.to_string(),
+                            "store path returned from fetcher should match"
+                        );
+
+                        root_node
+                    }
+                    None => {
+                        // Look up the derivation for this output path.
+                        let (drv_path, drv) = {
+                            let known_paths = self.known_paths.borrow();
+                            match known_paths.get_drv_path_for_output_path(store_path) {
+                                Some(drv_path) => (
+                                    drv_path.to_owned(),
+                                    known_paths.get_drv_by_drvpath(drv_path).unwrap().to_owned(),
+                                ),
+                                None => {
+                                    warn!(store_path=%store_path, "no drv found");
+                                    // let StdIO take over
+                                    return Ok(None);
+                                }
+                            }
+                        };
+
+                        warn!("triggering build");
+
+                        // derivation_to_build_request needs castore nodes for all inputs.
+                        // Provide them, which means, here is where we recursively build
+                        // all dependencies.
+                        #[allow(clippy::mutable_key_type)]
+                        let input_nodes: BTreeSet<Node> =
+                            futures::stream::iter(drv.input_derivations.iter())
+                                .map(|(input_drv_path, output_names)| {
+                                    // look up the derivation object
+                                    let input_drv = {
+                                        let known_paths = self.known_paths.borrow();
+                                        known_paths
+                                            .get_drv_by_drvpath(input_drv_path)
+                                            .unwrap_or_else(|| {
+                                                panic!("{} not found", input_drv_path)
+                                            })
+                                            .to_owned()
+                                    };
+
+                                    // convert output names to actual paths
+                                    let output_paths: Vec<StorePath> = output_names
+                                        .iter()
+                                        .map(|output_name| {
+                                            input_drv
+                                                .outputs
+                                                .get(output_name)
+                                                .expect("missing output_name")
+                                                .path
+                                                .as_ref()
+                                                .expect("missing output path")
+                                                .clone()
+                                        })
+                                        .collect();
+                                    // For each output, ask for the castore node.
+                                    // We're in a per-derivation context, so if they're
+                                    // not built yet they'll all get built together.
+                                    // If they don't need to build, we can however still
+                                    // substitute all in parallel (if they don't need to
+                                    // be built) - so we turn this into a stream of streams.
+                                    // It's up to the builder to deduplicate same build requests.
+                                    futures::stream::iter(output_paths.into_iter()).map(
+                                        |output_path| async move {
+                                            let node = self
+                                                .store_path_to_node(&output_path, Path::new(""))
+                                                .await?;
+
+                                            if let Some(node) = node {
+                                                Ok(node)
+                                            } else {
+                                                Err(io::Error::other("no node produced"))
+                                            }
+                                        },
+                                    )
+                                })
+                                .flatten()
+                                .buffer_unordered(10) // TODO: make configurable
+                                .try_collect()
+                                .await?;
+
+                        // TODO: check if input sources are sufficiently dealth with,
+                        // I think yes, they must be imported into the store by other
+                        // operations, so dealt with in the Some(…) match arm
+
+                        // synthesize the build request.
+                        let build_request = derivation_to_build_request(&drv, input_nodes)?;
+
+                        // create a build
+                        let build_result = self
+                            .build_service
+                            .as_ref()
+                            .do_build(build_request)
+                            .await
+                            .map_err(|e| std::io::Error::new(io::ErrorKind::Other, e))?;
+
+                        // TODO: refscan?
+
+                        // For each output, insert a PathInfo.
+                        for output in &build_result.outputs {
+                            let root_node = output.node.as_ref().expect("invalid root node");
+
+                            // calculate the nar representation
+                            let (nar_size, nar_sha256) =
+                                self.path_info_service.calculate_nar(root_node).await?;
+
+                            // assemble the PathInfo to persist
+                            let path_info = PathInfo {
+                                node: Some(tvix_castore::proto::Node {
+                                    node: Some(root_node.clone()),
+                                }),
+                                references: vec![], // TODO: refscan
+                                narinfo: Some(tvix_store::proto::NarInfo {
+                                    nar_size,
+                                    nar_sha256: Bytes::from(nar_sha256.to_vec()),
+                                    signatures: vec![],
+                                    reference_names: vec![], // TODO: refscan
+                                    deriver: Some(tvix_store::proto::StorePath {
+                                        name: drv_path
+                                            .name()
+                                            .strip_suffix(".drv")
+                                            .expect("missing .drv suffix")
+                                            .to_string(),
+                                        digest: drv_path.digest().to_vec().into(),
+                                    }),
+                                    ca: drv.fod_digest().map(
+                                        |fod_digest| -> tvix_store::proto::nar_info::Ca {
+                                            (&CAHash::Nar(nix_compat::nixhash::NixHash::Sha256(
+                                                fod_digest,
+                                            )))
+                                                .into()
+                                        },
+                                    ),
+                                }),
+                            };
+
+                            self.path_info_service
+                                .put(path_info)
+                                .await
+                                .map_err(|e| std::io::Error::new(io::ErrorKind::Other, e))?;
+                        }
+
+                        // find the output for the store path requested
+                        build_result
+                            .outputs
+                            .into_iter()
+                            .find(|output_node| {
+                                output_node.node.as_ref().expect("invalid node").get_name()
+                                    == store_path.to_string().as_bytes()
+                            })
+                            .expect("build didn't produce the store path")
+                            .node
+                            .expect("invalid node")
+                    }
+                }
+            }
+        };
+
+        // now with the root_node and sub_path, descend to the node requested.
+        // We convert sub_path to the castore model here.
+        let sub_path = tvix_castore::PathBuf::from_host_path(sub_path, true)?;
+
+        directoryservice::descend_to(&self.directory_service, root_node, sub_path)
+            .await
+            .map_err(|e| std::io::Error::new(io::ErrorKind::Other, e))
+    }
+
+    pub(crate) async fn node_to_path_info(
+        &self,
+        name: &str,
+        path: &Path,
+        ca: CAHash,
+        root_node: Node,
+    ) -> io::Result<(PathInfo, NixHash, StorePath)> {
+        // Ask the PathInfoService for the NAR size and sha256
+        // We always need it no matter what is the actual hash mode
+        // because the path info construct a narinfo which *always*
+        // require a SHA256 of the NAR representation and the NAR size.
+        let (nar_size, nar_sha256) = self
+            .path_info_service
+            .as_ref()
+            .calculate_nar(&root_node)
+            .await?;
+
+        // Calculate the output path. This might still fail, as some names are illegal.
+        let output_path =
+            nix_compat::store_path::build_ca_path(name, &ca, Vec::<String>::new(), false).map_err(
+                |_| {
+                    std::io::Error::new(
+                        std::io::ErrorKind::InvalidData,
+                        format!("invalid name: {}", name),
+                    )
+                },
+            )?;
+
+        // assemble a new root_node with a name that is derived from the nar hash.
+        let root_node = root_node.rename(output_path.to_string().into_bytes().into());
+        tvix_store::import::log_node(&root_node, path);
+
+        let path_info =
+            tvix_store::import::derive_nar_ca_path_info(nar_size, nar_sha256, Some(ca), root_node);
+
+        Ok((
+            path_info,
+            NixHash::Sha256(nar_sha256),
+            output_path.to_owned(),
+        ))
+    }
+
+    pub(crate) async fn register_node_in_path_info_service(
+        &self,
+        name: &str,
+        path: &Path,
+        ca: CAHash,
+        root_node: Node,
+    ) -> io::Result<StorePath> {
+        let (path_info, _, output_path) = self.node_to_path_info(name, path, ca, root_node).await?;
+        let _path_info = self.path_info_service.as_ref().put(path_info).await?;
+
+        Ok(output_path)
+    }
+
+    /// Transforms a BLAKE-3 digest into a SHA256 digest
+    /// by re-hashing the whole file.
+    pub(crate) async fn blob_to_sha256_hash(&self, blob_digest: B3Digest) -> io::Result<[u8; 32]> {
+        let mut reader = self
+            .blob_service
+            .open_read(&blob_digest)
+            .await?
+            .ok_or_else(|| {
+                io::Error::new(
+                    io::ErrorKind::NotFound,
+                    format!("blob represented by digest: '{}' not found", blob_digest),
+                )
+            })?;
+        // It is fine to use `AsyncIoBridge` here because hashing is not actually I/O.
+        let mut hasher = AsyncIoBridge(Sha256::new());
+
+        tokio::io::copy(&mut reader, &mut hasher).await?;
+        Ok(hasher.0.finalize().into())
+    }
+
+    pub async fn store_path_exists<'a>(&'a self, store_path: StorePathRef<'a>) -> io::Result<bool> {
+        Ok(self
+            .path_info_service
+            .as_ref()
+            .get(*store_path.digest())
+            .await?
+            .is_some())
+    }
+}
+
+impl EvalIO for TvixStoreIO {
+    #[instrument(skip(self), ret(level = Level::TRACE), err)]
+    fn path_exists(&self, path: &Path) -> io::Result<bool> {
+        if let Ok((store_path, sub_path)) =
+            StorePath::from_absolute_path_full(&path.to_string_lossy())
+        {
+            if self
+                .tokio_handle
+                .block_on(async { self.store_path_to_node(&store_path, &sub_path).await })?
+                .is_some()
+            {
+                Ok(true)
+            } else {
+                // As tvix-store doesn't manage /nix/store on the filesystem,
+                // we still need to also ask self.std_io here.
+                self.std_io.path_exists(path)
+            }
+        } else {
+            // The store path is no store path, so do regular StdIO.
+            self.std_io.path_exists(path)
+        }
+    }
+
+    #[instrument(skip(self), err)]
+    fn open(&self, path: &Path) -> io::Result<Box<dyn io::Read>> {
+        if let Ok((store_path, sub_path)) =
+            StorePath::from_absolute_path_full(&path.to_string_lossy())
+        {
+            if let Some(node) = self
+                .tokio_handle
+                .block_on(async { self.store_path_to_node(&store_path, &sub_path).await })?
+            {
+                // depending on the node type, treat open differently
+                match node {
+                    Node::Directory(_) => {
+                        // This would normally be a io::ErrorKind::IsADirectory (still unstable)
+                        Err(io::Error::new(
+                            io::ErrorKind::Unsupported,
+                            format!("tried to open directory at {:?}", path),
+                        ))
+                    }
+                    Node::File(file_node) => {
+                        let digest: B3Digest =
+                            file_node.digest.clone().try_into().map_err(|_e| {
+                                error!(
+                                    file_node = ?file_node,
+                                    "invalid digest"
+                                );
+                                io::Error::new(
+                                    io::ErrorKind::InvalidData,
+                                    format!("invalid digest length in file node: {:?}", file_node),
+                                )
+                            })?;
+
+                        self.tokio_handle.block_on(async {
+                            let resp = self.blob_service.as_ref().open_read(&digest).await?;
+                            match resp {
+                                Some(blob_reader) => {
+                                    // The VM Response needs a sync [std::io::Reader].
+                                    Ok(Box::new(SyncIoBridge::new(blob_reader))
+                                        as Box<dyn io::Read>)
+                                }
+                                None => {
+                                    error!(
+                                        blob.digest = %digest,
+                                        "blob not found",
+                                    );
+                                    Err(io::Error::new(
+                                        io::ErrorKind::NotFound,
+                                        format!("blob {} not found", &digest),
+                                    ))
+                                }
+                            }
+                        })
+                    }
+                    Node::Symlink(_symlink_node) => Err(io::Error::new(
+                        io::ErrorKind::Unsupported,
+                        "open for symlinks is unsupported",
+                    ))?,
+                }
+            } else {
+                // As tvix-store doesn't manage /nix/store on the filesystem,
+                // we still need to also ask self.std_io here.
+                self.std_io.open(path)
+            }
+        } else {
+            // The store path is no store path, so do regular StdIO.
+            self.std_io.open(path)
+        }
+    }
+
+    #[instrument(skip(self), ret(level = Level::TRACE), err)]
+    fn read_dir(&self, path: &Path) -> io::Result<Vec<(bytes::Bytes, FileType)>> {
+        if let Ok((store_path, sub_path)) =
+            StorePath::from_absolute_path_full(&path.to_string_lossy())
+        {
+            if let Some(node) = self
+                .tokio_handle
+                .block_on(async { self.store_path_to_node(&store_path, &sub_path).await })?
+            {
+                match node {
+                    Node::Directory(directory_node) => {
+                        // fetch the Directory itself.
+                        let digest: B3Digest =
+                            directory_node.digest.clone().try_into().map_err(|_e| {
+                                io::Error::new(
+                                    io::ErrorKind::InvalidData,
+                                    format!(
+                                        "invalid digest length in directory node: {:?}",
+                                        directory_node
+                                    ),
+                                )
+                            })?;
+
+                        if let Some(directory) = self.tokio_handle.block_on(async {
+                            self.directory_service.as_ref().get(&digest).await
+                        })? {
+                            let mut children: Vec<(bytes::Bytes, FileType)> = Vec::new();
+                            for node in directory.nodes() {
+                                children.push(match node {
+                                    Node::Directory(e) => (e.name, FileType::Directory),
+                                    Node::File(e) => (e.name, FileType::Regular),
+                                    Node::Symlink(e) => (e.name, FileType::Symlink),
+                                })
+                            }
+                            Ok(children)
+                        } else {
+                            // If we didn't get the directory node that's linked, that's a store inconsistency!
+                            error!(
+                                directory.digest = %digest,
+                                path = ?path,
+                                "directory not found",
+                            );
+                            Err(io::Error::new(
+                                io::ErrorKind::NotFound,
+                                format!("directory {digest} does not exist"),
+                            ))?
+                        }
+                    }
+                    Node::File(_file_node) => {
+                        // This would normally be a io::ErrorKind::NotADirectory (still unstable)
+                        Err(io::Error::new(
+                            io::ErrorKind::Unsupported,
+                            "tried to readdir path {:?}, which is a file",
+                        ))?
+                    }
+                    Node::Symlink(_symlink_node) => Err(io::Error::new(
+                        io::ErrorKind::Unsupported,
+                        "read_dir for symlinks is unsupported",
+                    ))?,
+                }
+            } else {
+                self.std_io.read_dir(path)
+            }
+        } else {
+            self.std_io.read_dir(path)
+        }
+    }
+
+    #[instrument(skip(self), ret(level = Level::TRACE), err)]
+    fn import_path(&self, path: &Path) -> io::Result<PathBuf> {
+        let output_path = self.tokio_handle.block_on(async {
+            tvix_store::import::import_path_as_nar_ca(
+                path,
+                tvix_store::import::path_to_name(path)?,
+                &self.blob_service,
+                &self.directory_service,
+                &self.path_info_service,
+            )
+            .await
+        })?;
+
+        Ok(output_path.to_absolute_path().into())
+    }
+
+    #[instrument(skip(self), ret(level = Level::TRACE))]
+    fn store_dir(&self) -> Option<String> {
+        Some("/nix/store".to_string())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::{path::Path, rc::Rc, sync::Arc};
+
+    use bstr::ByteSlice;
+    use tempfile::TempDir;
+    use tvix_build::buildservice::DummyBuildService;
+    use tvix_castore::{
+        blobservice::{BlobService, MemoryBlobService},
+        directoryservice::{DirectoryService, MemoryDirectoryService},
+    };
+    use tvix_eval::{EvalIO, EvaluationResult};
+    use tvix_store::pathinfoservice::MemoryPathInfoService;
+
+    use super::TvixStoreIO;
+    use crate::builtins::{add_derivation_builtins, add_fetcher_builtins, add_import_builtins};
+
+    /// evaluates a given nix expression and returns the result.
+    /// Takes care of setting up the evaluator so it knows about the
+    // `derivation` builtin.
+    fn eval(str: &str) -> EvaluationResult {
+        let blob_service = Arc::new(MemoryBlobService::default()) as Arc<dyn BlobService>;
+        let directory_service =
+            Arc::new(MemoryDirectoryService::default()) as Arc<dyn DirectoryService>;
+        let path_info_service = Arc::new(MemoryPathInfoService::new(
+            blob_service.clone(),
+            directory_service.clone(),
+        ));
+
+        let runtime = tokio::runtime::Runtime::new().unwrap();
+
+        let io = Rc::new(TvixStoreIO::new(
+            blob_service.clone(),
+            directory_service.clone(),
+            path_info_service,
+            Arc::<DummyBuildService>::default(),
+            runtime.handle().clone(),
+        ));
+        let mut eval = tvix_eval::Evaluation::new(io.clone() as Rc<dyn EvalIO>, true);
+
+        add_derivation_builtins(&mut eval, io.clone());
+        add_fetcher_builtins(&mut eval, io.clone());
+        add_import_builtins(&mut eval, io);
+
+        // run the evaluation itself.
+        eval.evaluate(str, None)
+    }
+
+    /// Helper function that takes a &Path, and invokes a tvix evaluator coercing that path to a string
+    /// (via "${/this/path}"). The path can be both absolute or not.
+    /// It returns Option<String>, depending on whether the evaluation succeeded or not.
+    fn import_path_and_compare<P: AsRef<Path>>(p: P) -> Option<String> {
+        // Try to import the path using "${/tmp/path/to/test}".
+        // The format string looks funny, the {} passed to Nix needs to be
+        // escaped.
+        let code = format!(r#""${{{}}}""#, p.as_ref().display());
+        let result = eval(&code);
+
+        if !result.errors.is_empty() {
+            return None;
+        }
+
+        let value = result.value.expect("must be some");
+        match value {
+            tvix_eval::Value::String(s) => Some(s.to_str_lossy().into_owned()),
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+    }
+
+    /// Import a directory with a zero-sized ".keep" regular file.
+    /// Ensure it matches the (pre-recorded) store path that Nix would produce.
+    #[test]
+    fn import_directory() {
+        let tmpdir = TempDir::new().unwrap();
+
+        // create a directory named "test"
+        let src_path = tmpdir.path().join("test");
+        std::fs::create_dir(&src_path).unwrap();
+
+        // write a regular file `.keep`.
+        std::fs::write(src_path.join(".keep"), vec![]).unwrap();
+
+        // importing the path with .../test at the end.
+        assert_eq!(
+            Some("/nix/store/gq3xcv4xrj4yr64dflyr38acbibv3rm9-test".to_string()),
+            import_path_and_compare(&src_path)
+        );
+
+        // importing the path with .../test/. at the end.
+        assert_eq!(
+            Some("/nix/store/gq3xcv4xrj4yr64dflyr38acbibv3rm9-test".to_string()),
+            import_path_and_compare(src_path.join("."))
+        );
+    }
+
+    /// Import a file into the store. Nix uses the "recursive"/NAR-based hashing
+    /// scheme for these.
+    #[test]
+    fn import_file() {
+        let tmpdir = TempDir::new().unwrap();
+
+        // write a regular file `empty`.
+        std::fs::write(tmpdir.path().join("empty"), vec![]).unwrap();
+
+        assert_eq!(
+            Some("/nix/store/lx5i78a4izwk2qj1nq8rdc07y8zrwy90-empty".to_string()),
+            import_path_and_compare(tmpdir.path().join("empty"))
+        );
+
+        // write a regular file `hello.txt`.
+        std::fs::write(tmpdir.path().join("hello.txt"), b"Hello World!").unwrap();
+
+        assert_eq!(
+            Some("/nix/store/925f1jb1ajrypjbyq7rylwryqwizvhp0-hello.txt".to_string()),
+            import_path_and_compare(tmpdir.path().join("hello.txt"))
+        );
+    }
+
+    /// Invoke toString on a nonexisting file, and access the .file attribute.
+    /// This should not cause an error, because it shouldn't trigger an import,
+    /// and leave the path as-is.
+    #[test]
+    fn nonexisting_path_without_import() {
+        let result = eval("toString ({ line = 42; col = 42; file = /deep/thought; }.file)");
+
+        assert!(result.errors.is_empty(), "expect evaluation to succeed");
+        let value = result.value.expect("must be some");
+
+        match value {
+            tvix_eval::Value::String(s) => {
+                assert_eq!(*s, "/deep/thought");
+            }
+            _ => panic!("unexpected value type: {:?}", value),
+        }
+    }
+}
diff --git a/tvix/logo.webp b/tvix/logo.webp
new file mode 100644
index 0000000000..07bffc18b7
--- /dev/null
+++ b/tvix/logo.webp
Binary files differdiff --git a/tvix/nar-bridge/.gitignore b/tvix/nar-bridge/.gitignore
new file mode 100644
index 0000000000..d70e1f8120
--- /dev/null
+++ b/tvix/nar-bridge/.gitignore
@@ -0,0 +1,2 @@
+/nar-bridge-http
+/nar-bridge-pathinfo
diff --git a/tvix/nar-bridge/README.md b/tvix/nar-bridge/README.md
new file mode 100644
index 0000000000..b14ee7af7b
--- /dev/null
+++ b/tvix/nar-bridge/README.md
@@ -0,0 +1,7 @@
+# //tvix/nar-bridge
+
+This exposes a HTTP Binary cache interface (GET/HEAD/PUT requests) for a `tvix-
+store`.
+
+It can be used to configure a tvix-store as a substitutor for Nix, or to upload
+store paths from Nix via `nix copy` into a `tvix-store`.
diff --git a/tvix/nar-bridge/cmd/nar-bridge-http/main.go b/tvix/nar-bridge/cmd/nar-bridge-http/main.go
new file mode 100644
index 0000000000..171ea7f5bd
--- /dev/null
+++ b/tvix/nar-bridge/cmd/nar-bridge-http/main.go
@@ -0,0 +1,93 @@
+package main
+
+import (
+	"context"
+	"os"
+	"os/signal"
+	"runtime/debug"
+	"time"
+
+	"github.com/alecthomas/kong"
+
+	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials/insecure"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	narBridgeHttp "code.tvl.fyi/tvix/nar-bridge/pkg/http"
+	storev1pb "code.tvl.fyi/tvix/store-go"
+	log "github.com/sirupsen/logrus"
+)
+
+// `help:"Expose a tvix-store gRPC Interface as HTTP NAR/NARinfo"`
+var cli struct {
+	LogLevel        string `enum:"trace,debug,info,warn,error,fatal,panic" help:"The log level to log with" default:"info"`
+	ListenAddr      string `name:"listen-addr" help:"The address this service listens on" type:"string" default:"[::]:9000"`                    //nolint:lll
+	EnableAccessLog bool   `name:"access-log" help:"Enable access logging" type:"bool" default:"true" negatable:""`                             //nolint:lll
+	StoreAddr       string `name:"store-addr" help:"The address to the tvix-store RPC interface this will connect to" default:"localhost:8000"` //nolint:lll
+	EnableOtlp      bool   `name:"otlp" help:"Enable OpenTelemetry for logs, spans, and metrics" default:"true"`                                //nolint:lll
+}
+
+func main() {
+	_ = kong.Parse(&cli)
+
+	logLevel, err := log.ParseLevel(cli.LogLevel)
+	if err != nil {
+		log.Panic("invalid log level")
+		return
+	}
+	log.SetLevel(logLevel)
+
+	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
+	defer stop()
+
+	if cli.EnableOtlp {
+		buildInfo, ok := debug.ReadBuildInfo()
+		if !ok {
+			log.Fatal("failed to read build info")
+		}
+
+		shutdown, err := setupOpenTelemetry(ctx, "nar-bridge", buildInfo.Main.Version)
+		if err != nil {
+			log.WithError(err).Fatal("failed to setup OpenTelemetry")
+		}
+		defer shutdown(context.Background())
+	}
+
+	// connect to tvix-store
+	log.Debugf("Dialing to %v", cli.StoreAddr)
+	conn, err := grpc.DialContext(ctx, cli.StoreAddr,
+		grpc.WithTransportCredentials(insecure.NewCredentials()),
+		grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
+	)
+	if err != nil {
+		log.Fatalf("did not connect: %v", err)
+	}
+	defer conn.Close()
+
+	s := narBridgeHttp.New(
+		castorev1pb.NewDirectoryServiceClient(conn),
+		castorev1pb.NewBlobServiceClient(conn),
+		storev1pb.NewPathInfoServiceClient(conn),
+		cli.EnableAccessLog,
+		30,
+	)
+
+	log.Printf("Starting nar-bridge-http at %v", cli.ListenAddr)
+	go s.ListenAndServe(cli.ListenAddr)
+
+	// listen for the interrupt signal.
+	<-ctx.Done()
+
+	// Restore default behaviour on the interrupt signal
+	stop()
+	log.Info("Received Signal, shutting down, press Ctl+C again to force.")
+
+	timeoutCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+	defer cancel()
+
+	if err := s.Shutdown(timeoutCtx); err != nil {
+		log.WithError(err).Warn("failed to shutdown")
+		os.Exit(1)
+	}
+}
diff --git a/tvix/nar-bridge/cmd/nar-bridge-http/otel.go b/tvix/nar-bridge/cmd/nar-bridge-http/otel.go
new file mode 100644
index 0000000000..c446c6ec1a
--- /dev/null
+++ b/tvix/nar-bridge/cmd/nar-bridge-http/otel.go
@@ -0,0 +1,87 @@
+package main
+
+import (
+	"context"
+	"errors"
+
+	"go.opentelemetry.io/otel"
+	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
+	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
+	"go.opentelemetry.io/otel/propagation"
+	"go.opentelemetry.io/otel/sdk/metric"
+	"go.opentelemetry.io/otel/sdk/resource"
+	"go.opentelemetry.io/otel/sdk/trace"
+	semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
+)
+
+func setupOpenTelemetry(ctx context.Context, serviceName, serviceVersion string) (func(context.Context) error, error) {
+	var shutdownFuncs []func(context.Context) error
+	shutdown := func(ctx context.Context) error {
+		var err error
+		for _, fn := range shutdownFuncs {
+			err = errors.Join(err, fn(ctx))
+		}
+		shutdownFuncs = nil
+		return err
+	}
+
+	res, err := resource.Merge(
+		resource.Default(),
+		resource.NewWithAttributes(
+			semconv.SchemaURL,
+			semconv.ServiceName(serviceName),
+			semconv.ServiceVersion(serviceVersion),
+		),
+	)
+	if err != nil {
+		return nil, errors.Join(err, shutdown(ctx))
+	}
+
+	prop := propagation.NewCompositeTextMapPropagator(
+		propagation.TraceContext{},
+		propagation.Baggage{},
+	)
+	otel.SetTextMapPropagator(prop)
+
+	tracerProvider, err := newTraceProvider(ctx, res)
+	if err != nil {
+		return nil, errors.Join(err, shutdown(ctx))
+	}
+	shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown)
+	otel.SetTracerProvider(tracerProvider)
+
+	meterProvider, err := newMeterProvider(ctx, res)
+	if err != nil {
+		return nil, errors.Join(err, shutdown(ctx))
+	}
+	shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown)
+	otel.SetMeterProvider(meterProvider)
+
+	return shutdown, nil
+}
+
+func newTraceProvider(ctx context.Context, res *resource.Resource) (*trace.TracerProvider, error) {
+	traceExporter, err := otlptracegrpc.New(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	traceProvider := trace.NewTracerProvider(
+		trace.WithBatcher(traceExporter),
+		trace.WithResource(res),
+	)
+	return traceProvider, nil
+}
+
+func newMeterProvider(ctx context.Context, res *resource.Resource) (*metric.MeterProvider, error) {
+	metricExporter, err := otlpmetricgrpc.New(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	meterProvider := metric.NewMeterProvider(
+		metric.WithResource(res),
+		metric.WithReader(metric.NewPeriodicReader(metricExporter)),
+	)
+	return meterProvider, nil
+}
diff --git a/tvix/nar-bridge/default.nix b/tvix/nar-bridge/default.nix
new file mode 100644
index 0000000000..c0247f279f
--- /dev/null
+++ b/tvix/nar-bridge/default.nix
@@ -0,0 +1,10 @@
+# Target containing just the proto files.
+
+{ depot, pkgs, lib, ... }:
+
+pkgs.buildGoModule {
+  name = "nar-bridge";
+  src = depot.third_party.gitignoreSource ./.;
+
+  vendorHash = "sha256-7jugbC5sEGhppjiZgnoLP5A6kQSaHK9vE6cXVZBG22s=";
+}
diff --git a/tvix/nar-bridge/go.mod b/tvix/nar-bridge/go.mod
new file mode 100644
index 0000000000..deb6943e23
--- /dev/null
+++ b/tvix/nar-bridge/go.mod
@@ -0,0 +1,54 @@
+module code.tvl.fyi/tvix/nar-bridge
+
+require (
+	code.tvl.fyi/tvix/castore-go v0.0.0-20231105151352-990d6ba2175e
+	code.tvl.fyi/tvix/store-go v0.0.0-20231105203234-f2baad42494f
+	github.com/alecthomas/kong v0.7.1
+	github.com/go-chi/chi v1.5.4
+	github.com/go-chi/chi/v5 v5.0.7
+	github.com/google/go-cmp v0.6.0
+	github.com/multiformats/go-multihash v0.2.1
+	github.com/nix-community/go-nix v0.0.0-20231012070617-9b176785e54d
+	github.com/sirupsen/logrus v1.9.0
+	github.com/stretchr/testify v1.8.4
+	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0
+	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0
+	go.opentelemetry.io/otel v1.22.0
+	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.45.0
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0
+	go.opentelemetry.io/otel/sdk v1.22.0
+	go.opentelemetry.io/otel/sdk/metric v1.22.0
+	golang.org/x/sync v0.4.0
+	google.golang.org/grpc v1.60.1
+	google.golang.org/protobuf v1.32.0
+	lukechampine.com/blake3 v1.2.1
+)
+
+require (
+	github.com/cenkalti/backoff/v4 v4.2.1 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/felixge/httpsnoop v1.0.4 // indirect
+	github.com/go-logr/logr v1.4.1 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.5 // indirect
+	github.com/minio/sha256-simd v1.0.0 // indirect
+	github.com/mr-tron/base58 v1.2.0 // indirect
+	github.com/multiformats/go-varint v0.0.6 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	github.com/spaolacci/murmur3 v1.1.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect
+	go.opentelemetry.io/otel/metric v1.22.0 // indirect
+	go.opentelemetry.io/otel/trace v1.22.0 // indirect
+	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
+	golang.org/x/crypto v0.18.0 // indirect
+	golang.org/x/net v0.20.0 // indirect
+	golang.org/x/sys v0.16.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)
+
+go 1.19
diff --git a/tvix/nar-bridge/go.sum b/tvix/nar-bridge/go.sum
new file mode 100644
index 0000000000..39f77b9061
--- /dev/null
+++ b/tvix/nar-bridge/go.sum
@@ -0,0 +1,120 @@
+cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
+cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
+code.tvl.fyi/tvix/castore-go v0.0.0-20231105151352-990d6ba2175e h1:Nj+anfyEYeEdhnIo2BG/N1ZwQl1IvI7AH3TbNDLwUOA=
+code.tvl.fyi/tvix/castore-go v0.0.0-20231105151352-990d6ba2175e/go.mod h1:+vKbozsa04yy2TWh3kUVU568jaza3Hf0p1jAEoMoCwA=
+code.tvl.fyi/tvix/store-go v0.0.0-20231105203234-f2baad42494f h1:bN3K7oSu3IAHXqS3ETHUgpBPHF9+awKKBRLiM8/1tmI=
+code.tvl.fyi/tvix/store-go v0.0.0-20231105203234-f2baad42494f/go.mod h1:8jpfSC2rGi6VKaKOqqgmflPVSEpUawuRQFwQpQYCMiA=
+github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
+github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4=
+github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
+github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
+github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
+github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
+github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
+github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
+github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
+github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
+github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
+github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
+github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108=
+github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
+github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
+github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/nix-community/go-nix v0.0.0-20231012070617-9b176785e54d h1:kwc1ivTuStqa3iBC2M/ojWPor88+YeIbZGeD2SlMYZ0=
+github.com/nix-community/go-nix v0.0.0-20231012070617-9b176785e54d/go.mod h1:4ZJah5sYrUSsWXIOJIsQ6iVOQyLO+ffhWXU3gblcO+E=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
+github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
+github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw=
+go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
+go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.45.0 h1:tfil6di0PoNV7FZdsCS7A5izZoVVQ7AuXtyekbOpG/I=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.45.0/go.mod h1:AKFZIEPOnqB00P63bTjOiah4ZTaRzl1TKwUWpZdYUHI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ=
+go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
+go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
+go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
+go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
+go.opentelemetry.io/otel/sdk/metric v1.22.0 h1:ARrRetm1HCVxq0cbnaZQlfwODYJHo3gFL8Z3tSmHBcI=
+go.opentelemetry.io/otel/sdk/metric v1.22.0/go.mod h1:KjQGeMIDlBNEOo6HvjhxIec1p/69/kULDcp4gr0oLQQ=
+go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
+go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
+go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
+go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
+golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
+golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
+google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA=
+google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU=
+google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE=
+google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
+google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
+google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
+lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
diff --git a/tvix/nar-bridge/pkg/http/nar_get.go b/tvix/nar-bridge/pkg/http/nar_get.go
new file mode 100644
index 0000000000..75797f8da9
--- /dev/null
+++ b/tvix/nar-bridge/pkg/http/nar_get.go
@@ -0,0 +1,197 @@
+package http
+
+import (
+	"bytes"
+	"context"
+	"encoding/base64"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"io"
+	"io/fs"
+	"net/http"
+	"sync"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	storev1pb "code.tvl.fyi/tvix/store-go"
+	"github.com/go-chi/chi/v5"
+	nixhash "github.com/nix-community/go-nix/pkg/hash"
+	"github.com/nix-community/go-nix/pkg/nixbase32"
+	log "github.com/sirupsen/logrus"
+)
+
+const (
+	narUrl = "/nar/{narhash:^([" + nixbase32.Alphabet + "]{52})$}.nar"
+)
+
+func renderNar(
+	ctx context.Context,
+	log *log.Entry,
+	directoryServiceClient castorev1pb.DirectoryServiceClient,
+	blobServiceClient castorev1pb.BlobServiceClient,
+	narHashDbMu *sync.Mutex,
+	narHashDb map[string]*narData,
+	w io.Writer,
+	narHash *nixhash.Hash,
+	headOnly bool,
+) error {
+	// look in the lookup table
+	narHashDbMu.Lock()
+	narData, found := narHashDb[narHash.SRIString()]
+	narHashDbMu.Unlock()
+
+	rootNode := narData.rootNode
+
+	// if we didn't find anything, return 404.
+	if !found {
+		return fmt.Errorf("narHash not found: %w", fs.ErrNotExist)
+	}
+
+	// if this was only a head request, we're done.
+	if headOnly {
+		return nil
+	}
+
+	directories := make(map[string]*castorev1pb.Directory)
+
+	// If the root node is a directory, ask the directory service for all directories
+	if pathInfoDirectory := rootNode.GetDirectory(); pathInfoDirectory != nil {
+		rootDirectoryDigest := pathInfoDirectory.GetDigest()
+		log = log.WithField("root_directory", base64.StdEncoding.EncodeToString(rootDirectoryDigest))
+
+		directoryStream, err := directoryServiceClient.Get(ctx, &castorev1pb.GetDirectoryRequest{
+			ByWhat: &castorev1pb.GetDirectoryRequest_Digest{
+				Digest: rootDirectoryDigest,
+			},
+			Recursive: true,
+		})
+		if err != nil {
+			return fmt.Errorf("unable to query directory stream: %w", err)
+		}
+
+		// For now, we just stream all of these locally and put them into a hashmap,
+		// which is used in the lookup function below.
+		for {
+			directory, err := directoryStream.Recv()
+			if err != nil {
+				if err == io.EOF {
+					break
+				}
+				return fmt.Errorf("unable to receive from directory stream: %w", err)
+			}
+
+			// calculate directory digest
+			// TODO: do we need to do any more validation?
+			directoryDgst, err := directory.Digest()
+			if err != nil {
+				return fmt.Errorf("unable to calculate directory digest: %w", err)
+			}
+
+			log.WithField("directory", base64.StdEncoding.EncodeToString(directoryDgst)).Debug("received directory node")
+
+			directories[hex.EncodeToString(directoryDgst)] = directory
+		}
+
+	}
+
+	// render the NAR file
+	err := storev1pb.Export(
+		w,
+		rootNode,
+		func(directoryDigest []byte) (*castorev1pb.Directory, error) {
+			log.WithField("directory", base64.StdEncoding.EncodeToString(directoryDigest)).Debug("Get directory")
+			directoryRefStr := hex.EncodeToString(directoryDigest)
+			directory, found := directories[directoryRefStr]
+			if !found {
+				return nil, fmt.Errorf(
+					"directory with hash %v does not exist: %w",
+					directoryDigest,
+					fs.ErrNotExist,
+				)
+			}
+
+			return directory, nil
+		},
+		func(blobDigest []byte) (io.ReadCloser, error) {
+			log.WithField("blob", base64.StdEncoding.EncodeToString(blobDigest)).Debug("Get blob")
+			resp, err := blobServiceClient.Read(ctx, &castorev1pb.ReadBlobRequest{
+				Digest: blobDigest,
+			})
+			if err != nil {
+				return nil, fmt.Errorf("unable to get blob: %w", err)
+			}
+
+			// set up a pipe, let a goroutine write, return the reader.
+			pR, pW := io.Pipe()
+
+			go func() {
+				for {
+					chunk, err := resp.Recv()
+					if errors.Is(err, io.EOF) {
+						break
+					}
+					if err != nil {
+						pW.CloseWithError(fmt.Errorf("receiving chunk: %w", err))
+						return
+					}
+
+					// write the received chunk to the writer part of the pipe
+					if _, err := io.Copy(pW, bytes.NewReader(chunk.GetData())); err != nil {
+						log.WithError(err).Error("writing chunk to pipe")
+						pW.CloseWithError(fmt.Errorf("writing chunk to pipe: %w", err))
+						return
+					}
+				}
+				pW.Close()
+
+			}()
+
+			return io.NopCloser(pR), nil
+		},
+	)
+	if err != nil {
+		return fmt.Errorf("unable to export nar: %w", err)
+	}
+	return nil
+}
+
+func registerNarGet(s *Server) {
+	// produce a handler for rendering NAR files.
+	genNarHandler := func(isHead bool) func(w http.ResponseWriter, r *http.Request) {
+		return func(w http.ResponseWriter, r *http.Request) {
+			defer r.Body.Close()
+
+			ctx := r.Context()
+
+			// parse the narhash sent in the request URL
+			narHash, err := parseNarHashFromUrl(chi.URLParamFromCtx(ctx, "narhash"))
+			if err != nil {
+				log.WithError(err).WithField("url", r.URL).Error("unable to decode nar hash from url")
+				w.WriteHeader(http.StatusBadRequest)
+				_, err := w.Write([]byte("unable to decode nar hash from url"))
+				if err != nil {
+					log.WithError(err).Errorf("unable to write error message to client")
+				}
+
+				return
+			}
+
+			log := log.WithField("narhash_url", narHash.SRIString())
+
+			// TODO: inline more of that function here?
+			err = renderNar(ctx, log, s.directoryServiceClient, s.blobServiceClient, &s.narDbMu, s.narDb, w, narHash, isHead)
+			if err != nil {
+				if errors.Is(err, fs.ErrNotExist) {
+					w.WriteHeader(http.StatusNotFound)
+				} else {
+					log.WithError(err).Warn("unable to render nar")
+					w.WriteHeader(http.StatusInternalServerError)
+				}
+			}
+
+		}
+	}
+
+	s.handler.Head(narUrl, genNarHandler(true))
+	s.handler.Get(narUrl, genNarHandler(false))
+}
diff --git a/tvix/nar-bridge/pkg/http/nar_put.go b/tvix/nar-bridge/pkg/http/nar_put.go
new file mode 100644
index 0000000000..fdfa20f9c3
--- /dev/null
+++ b/tvix/nar-bridge/pkg/http/nar_put.go
@@ -0,0 +1,141 @@
+package http
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"net/http"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	"code.tvl.fyi/tvix/nar-bridge/pkg/importer"
+	"github.com/go-chi/chi/v5"
+	mh "github.com/multiformats/go-multihash/core"
+	nixhash "github.com/nix-community/go-nix/pkg/hash"
+	"github.com/sirupsen/logrus"
+	log "github.com/sirupsen/logrus"
+)
+
+func registerNarPut(s *Server) {
+	s.handler.Put(narUrl, func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+
+		ctx := r.Context()
+
+		// parse the narhash sent in the request URL
+		narHashFromUrl, err := parseNarHashFromUrl(chi.URLParamFromCtx(ctx, "narhash"))
+		if err != nil {
+			log.WithError(err).WithField("url", r.URL).Error("unable to decode nar hash from url")
+			w.WriteHeader(http.StatusBadRequest)
+			_, err := w.Write([]byte("unable to decode nar hash from url"))
+			if err != nil {
+				log.WithError(err).Error("unable to write error message to client")
+			}
+
+			return
+		}
+
+		log := log.WithField("narhash_url", narHashFromUrl.SRIString())
+
+		directoriesUploader := importer.NewDirectoriesUploader(ctx, s.directoryServiceClient)
+		defer directoriesUploader.Done() //nolint:errcheck
+
+		rootNode, narSize, narSha256, err := importer.Import(
+			ctx,
+			// buffer the body by 10MiB
+			bufio.NewReaderSize(r.Body, 10*1024*1024),
+			importer.GenBlobUploaderCb(ctx, s.blobServiceClient),
+			func(directory *castorev1pb.Directory) ([]byte, error) {
+				return directoriesUploader.Put(directory)
+			},
+		)
+
+		if err != nil {
+			log.Errorf("error during NAR import: %v", err)
+			w.WriteHeader(http.StatusInternalServerError)
+			_, err := w.Write([]byte(fmt.Sprintf("error during NAR import: %v", err)))
+			if err != nil {
+				log.WithError(err).Errorf("unable to write error message to client")
+			}
+
+			return
+		}
+
+		log.Debug("closing the stream")
+
+		// Close the directories uploader
+		directoriesPutResponse, err := directoriesUploader.Done()
+		if err != nil {
+			log.WithError(err).Error("error during directory upload")
+			w.WriteHeader(http.StatusBadRequest)
+			_, err := w.Write([]byte("error during directory upload"))
+			if err != nil {
+				log.WithError(err).Errorf("unable to write error message to client")
+			}
+
+			return
+		}
+		// If we uploaded directories (so directoriesPutResponse doesn't return null),
+		// the RootDigest field in directoriesPutResponse should match the digest
+		// returned in the PathInfo struct returned by the `Import` call.
+		// This check ensures the server-side came up with the same root hash.
+
+		if directoriesPutResponse != nil {
+			rootDigestPathInfo := rootNode.GetDirectory().GetDigest()
+			rootDigestDirectoriesPutResponse := directoriesPutResponse.GetRootDigest()
+
+			log := log.WithFields(logrus.Fields{
+				"root_digest_pathinfo":             rootDigestPathInfo,
+				"root_digest_directories_put_resp": rootDigestDirectoriesPutResponse,
+			})
+			if !bytes.Equal(rootDigestPathInfo, rootDigestDirectoriesPutResponse) {
+				log.Errorf("returned root digest doesn't match what's calculated")
+
+				w.WriteHeader(http.StatusBadRequest)
+				_, err := w.Write([]byte("error in root digest calculation"))
+				if err != nil {
+					log.WithError(err).Error("unable to write error message to client")
+				}
+
+				return
+			}
+		}
+
+		// Compare the nar hash specified in the URL with the one that has been
+		// calculated while processing the NAR file.
+		narHash, err := nixhash.FromHashTypeAndDigest(mh.SHA2_256, narSha256)
+		if err != nil {
+			panic("must parse nixbase32")
+		}
+
+		if !bytes.Equal(narHashFromUrl.Digest(), narHash.Digest()) {
+			log := log.WithFields(logrus.Fields{
+				"narhash_received_sha256": narHash.SRIString(),
+				"narsize":                 narSize,
+			})
+			log.Error("received bytes don't match narhash from URL")
+
+			w.WriteHeader(http.StatusBadRequest)
+			_, err := w.Write([]byte("received bytes don't match narHash specified in URL"))
+			if err != nil {
+				log.WithError(err).Errorf("unable to write error message to client")
+			}
+
+			return
+		}
+
+		// Insert the partial pathinfo structs into our lookup map,
+		// so requesting the NAR file will be possible.
+		// The same  might exist already, but it'll have the same contents (so
+		// replacing will be a no-op), except maybe the root node Name field value, which
+		// is safe to ignore (as not part of the NAR).
+		s.narDbMu.Lock()
+		s.narDb[narHash.SRIString()] = &narData{
+			rootNode: rootNode,
+			narSize:  narSize,
+		}
+		s.narDbMu.Unlock()
+
+		// Done!
+	})
+
+}
diff --git a/tvix/nar-bridge/pkg/http/narinfo.go b/tvix/nar-bridge/pkg/http/narinfo.go
new file mode 100644
index 0000000000..e5b99a9505
--- /dev/null
+++ b/tvix/nar-bridge/pkg/http/narinfo.go
@@ -0,0 +1,51 @@
+package http
+
+import (
+	"fmt"
+
+	storev1pb "code.tvl.fyi/tvix/store-go"
+	mh "github.com/multiformats/go-multihash/core"
+	nixhash "github.com/nix-community/go-nix/pkg/hash"
+
+	"github.com/nix-community/go-nix/pkg/narinfo"
+	"github.com/nix-community/go-nix/pkg/narinfo/signature"
+	"github.com/nix-community/go-nix/pkg/nixbase32"
+)
+
+// ToNixNarInfo converts the PathInfo to a narinfo.NarInfo.
+func ToNixNarInfo(p *storev1pb.PathInfo) (*narinfo.NarInfo, error) {
+	// ensure the PathInfo is valid, and extract the StorePath from the node in
+	// there.
+	storePath, err := p.Validate()
+	if err != nil {
+		return nil, fmt.Errorf("failed to validate PathInfo: %w", err)
+	}
+
+	// convert the signatures from storev1pb signatures to narinfo signatures
+	narinfoSignatures := make([]signature.Signature, len(p.GetNarinfo().GetSignatures()))
+	for i, pathInfoSignature := range p.GetNarinfo().GetSignatures() {
+		narinfoSignatures[i] = signature.Signature{
+			Name: pathInfoSignature.GetName(),
+			Data: pathInfoSignature.GetData(),
+		}
+	}
+
+	// produce nixhash for the narsha256.
+	narHash, err := nixhash.FromHashTypeAndDigest(
+		mh.SHA2_256,
+		p.GetNarinfo().GetNarSha256(),
+	)
+	if err != nil {
+		return nil, fmt.Errorf("invalid narsha256: %w", err)
+	}
+
+	return &narinfo.NarInfo{
+		StorePath:   storePath.Absolute(),
+		URL:         "nar/" + nixbase32.EncodeToString(narHash.Digest()) + ".nar",
+		Compression: "none",
+		NarHash:     narHash,
+		NarSize:     uint64(p.GetNarinfo().GetNarSize()),
+		References:  p.GetNarinfo().GetReferenceNames(),
+		Signatures:  narinfoSignatures,
+	}, nil
+}
diff --git a/tvix/nar-bridge/pkg/http/narinfo_get.go b/tvix/nar-bridge/pkg/http/narinfo_get.go
new file mode 100644
index 0000000000..d43cb58078
--- /dev/null
+++ b/tvix/nar-bridge/pkg/http/narinfo_get.go
@@ -0,0 +1,137 @@
+package http
+
+import (
+	"context"
+	"encoding/base64"
+	"errors"
+	"fmt"
+	"io"
+	"io/fs"
+	"net/http"
+	"strings"
+	"sync"
+
+	storev1pb "code.tvl.fyi/tvix/store-go"
+	"github.com/go-chi/chi/v5"
+	nixhash "github.com/nix-community/go-nix/pkg/hash"
+	"github.com/nix-community/go-nix/pkg/nixbase32"
+	log "github.com/sirupsen/logrus"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+// renderNarinfo writes narinfo contents to a passed io.Writer, or a returns a
+// (wrapped) io.ErrNoExist error if something doesn't exist.
+// if headOnly is set to true, only the existence is checked, but no content is
+// actually written.
+func renderNarinfo(
+	ctx context.Context,
+	log *log.Entry,
+	pathInfoServiceClient storev1pb.PathInfoServiceClient,
+	narHashToPathInfoMu *sync.Mutex,
+	narHashToPathInfo map[string]*narData,
+	outputHash []byte,
+	w io.Writer,
+	headOnly bool,
+) error {
+	pathInfo, err := pathInfoServiceClient.Get(ctx, &storev1pb.GetPathInfoRequest{
+		ByWhat: &storev1pb.GetPathInfoRequest_ByOutputHash{
+			ByOutputHash: outputHash,
+		},
+	})
+	if err != nil {
+		st, ok := status.FromError(err)
+		if ok {
+			if st.Code() == codes.NotFound {
+				return fmt.Errorf("output hash %v not found: %w", base64.StdEncoding.EncodeToString(outputHash), fs.ErrNotExist)
+			}
+			return fmt.Errorf("unable to get pathinfo, code %v: %w", st.Code(), err)
+		}
+
+		return fmt.Errorf("unable to get pathinfo: %w", err)
+	}
+
+	log = log.WithField("pathInfo", pathInfo)
+
+	if _, err := pathInfo.Validate(); err != nil {
+		log.WithError(err).Error("unable to validate PathInfo")
+
+		return fmt.Errorf("unable to validate PathInfo: %w", err)
+	}
+
+	if pathInfo.GetNarinfo() == nil {
+		log.Error("PathInfo doesn't contain Narinfo field")
+
+		return fmt.Errorf("PathInfo doesn't contain Narinfo field")
+	}
+
+	// extract the NARHash. This must succeed, as Validate() did succeed.
+	narHash, err := nixhash.FromHashTypeAndDigest(0x12, pathInfo.GetNarinfo().GetNarSha256())
+	if err != nil {
+		panic("must parse NarHash")
+	}
+
+	// add things to the lookup table, in case the same process didn't handle the NAR hash yet.
+	narHashToPathInfoMu.Lock()
+	narHashToPathInfo[narHash.SRIString()] = &narData{
+		rootNode: pathInfo.GetNode(),
+		narSize:  pathInfo.GetNarinfo().GetNarSize(),
+	}
+	narHashToPathInfoMu.Unlock()
+
+	if headOnly {
+		return nil
+	}
+
+	// convert the PathInfo to NARInfo.
+	narInfo, err := ToNixNarInfo(pathInfo)
+
+	// Write it out to the client.
+	_, err = io.Copy(w, strings.NewReader(narInfo.String()))
+	if err != nil {
+		return fmt.Errorf("unable to write narinfo to client: %w", err)
+	}
+
+	return nil
+}
+
+func registerNarinfoGet(s *Server) {
+	// GET/HEAD $outHash.narinfo looks up the PathInfo from the tvix-store,
+	// and, if it's a GET request, render a .narinfo file to the client.
+	// In both cases it will keep the PathInfo in the lookup map,
+	// so a subsequent GET/HEAD /nar/ $narhash.nar request can find it.
+	genNarinfoHandler := func(isHead bool) func(w http.ResponseWriter, r *http.Request) {
+		return func(w http.ResponseWriter, r *http.Request) {
+			defer r.Body.Close()
+
+			ctx := r.Context()
+			log := log.WithField("outputhash", chi.URLParamFromCtx(ctx, "outputhash"))
+
+			// parse the output hash sent in the request URL
+			outputHash, err := nixbase32.DecodeString(chi.URLParamFromCtx(ctx, "outputhash"))
+			if err != nil {
+				log.WithError(err).Error("unable to decode output hash from url")
+				w.WriteHeader(http.StatusBadRequest)
+				_, err := w.Write([]byte("unable to decode output hash from url"))
+				if err != nil {
+					log.WithError(err).Errorf("unable to write error message to client")
+				}
+
+				return
+			}
+
+			err = renderNarinfo(ctx, log, s.pathInfoServiceClient, &s.narDbMu, s.narDb, outputHash, w, isHead)
+			if err != nil {
+				if errors.Is(err, fs.ErrNotExist) {
+					w.WriteHeader(http.StatusNotFound)
+				} else {
+					log.WithError(err).Warn("unable to render narinfo")
+					w.WriteHeader(http.StatusInternalServerError)
+				}
+			}
+		}
+	}
+
+	s.handler.Get("/{outputhash:^["+nixbase32.Alphabet+"]{32}}.narinfo", genNarinfoHandler(false))
+	s.handler.Head("/{outputhash:^["+nixbase32.Alphabet+"]{32}}.narinfo", genNarinfoHandler(true))
+}
diff --git a/tvix/nar-bridge/pkg/http/narinfo_put.go b/tvix/nar-bridge/pkg/http/narinfo_put.go
new file mode 100644
index 0000000000..fd588bec86
--- /dev/null
+++ b/tvix/nar-bridge/pkg/http/narinfo_put.go
@@ -0,0 +1,103 @@
+package http
+
+import (
+	"net/http"
+
+	"code.tvl.fyi/tvix/nar-bridge/pkg/importer"
+	"github.com/go-chi/chi/v5"
+	"github.com/nix-community/go-nix/pkg/narinfo"
+	"github.com/nix-community/go-nix/pkg/nixbase32"
+	"github.com/sirupsen/logrus"
+	log "github.com/sirupsen/logrus"
+)
+
+func registerNarinfoPut(s *Server) {
+	s.handler.Put("/{outputhash:^["+nixbase32.Alphabet+"]{32}}.narinfo", func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+
+		ctx := r.Context()
+		log := log.WithField("outputhash", chi.URLParamFromCtx(ctx, "outputhash"))
+
+		// TODO: decide on merging behaviour.
+		// Maybe it's fine to add if contents are the same, but more sigs can be added?
+		// Right now, just replace a .narinfo for a path that already exists.
+
+		// read and parse the .narinfo file
+		narInfo, err := narinfo.Parse(r.Body)
+		if err != nil {
+			log.WithError(err).Error("unable to parse narinfo")
+			w.WriteHeader(http.StatusBadRequest)
+			_, err := w.Write([]byte("unable to parse narinfo"))
+			if err != nil {
+				log.WithError(err).Errorf("unable to write error message to client")
+			}
+
+			return
+		}
+
+		log = log.WithFields(logrus.Fields{
+			"narhash":     narInfo.NarHash.SRIString(),
+			"output_path": narInfo.StorePath,
+		})
+
+		// look up the narHash in our temporary map
+		s.narDbMu.Lock()
+		narData, found := s.narDb[narInfo.NarHash.SRIString()]
+		s.narDbMu.Unlock()
+		if !found {
+			log.Error("unable to find referred NAR")
+			w.WriteHeader(http.StatusBadRequest)
+			_, err := w.Write([]byte("unable to find referred NAR"))
+			if err != nil {
+				log.WithError(err).Errorf("unable to write error message to client")
+			}
+
+			return
+		}
+
+		rootNode := narData.rootNode
+
+		// compare fields with what we computed while receiving the NAR file
+
+		// NarSize needs to match
+		if narData.narSize != narInfo.NarSize {
+			log.Error("narsize mismatch")
+			w.WriteHeader(http.StatusBadRequest)
+			_, err := w.Write([]byte("unable to parse narinfo"))
+			if err != nil {
+				log.WithError(err).Errorf("unable to write error message to client")
+			}
+
+			return
+		}
+
+		pathInfo, err := importer.GenPathInfo(rootNode, narInfo)
+		if err != nil {
+			log.WithError(err).Error("unable to generate PathInfo")
+
+			w.WriteHeader(http.StatusInternalServerError)
+			_, err := w.Write([]byte("unable to generate PathInfo"))
+			if err != nil {
+				log.WithError(err).Errorf("unable to write error message to client")
+			}
+
+			return
+		}
+
+		log.WithField("pathInfo", pathInfo).Debug("inserted new pathInfo")
+
+		receivedPathInfo, err := s.pathInfoServiceClient.Put(ctx, pathInfo)
+		if err != nil {
+			log.WithError(err).Error("unable to upload pathinfo to service")
+			w.WriteHeader(http.StatusInternalServerError)
+			_, err := w.Write([]byte("unable to upload pathinfo to server"))
+			if err != nil {
+				log.WithError(err).Errorf("unable to write error message to client")
+			}
+
+			return
+		}
+
+		log.WithField("pathInfo", receivedPathInfo).Debug("got back PathInfo")
+	})
+}
diff --git a/tvix/nar-bridge/pkg/http/server.go b/tvix/nar-bridge/pkg/http/server.go
new file mode 100644
index 0000000000..fbcb20be18
--- /dev/null
+++ b/tvix/nar-bridge/pkg/http/server.go
@@ -0,0 +1,119 @@
+package http
+
+import (
+	"context"
+	"fmt"
+	"net"
+	"net/http"
+	"strings"
+	"sync"
+	"time"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	storev1pb "code.tvl.fyi/tvix/store-go"
+	"github.com/go-chi/chi/middleware"
+	"github.com/go-chi/chi/v5"
+	log "github.com/sirupsen/logrus"
+	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
+)
+
+type Server struct {
+	srv     *http.Server
+	handler chi.Router
+
+	directoryServiceClient castorev1pb.DirectoryServiceClient
+	blobServiceClient      castorev1pb.BlobServiceClient
+	pathInfoServiceClient  storev1pb.PathInfoServiceClient
+
+	// When uploading NAR files to a HTTP binary cache, the .nar
+	// files are uploaded before the .narinfo files.
+	// We need *both* to be able to fully construct a PathInfo object.
+	// Keep a in-memory map of narhash(es) (in SRI) to (unnamed) root node and nar
+	// size.
+	// This is necessary until we can ask a PathInfoService for a node with a given
+	// narSha256.
+	narDbMu sync.Mutex
+	narDb   map[string]*narData
+}
+
+type narData struct {
+	rootNode *castorev1pb.Node
+	narSize  uint64
+}
+
+func New(
+	directoryServiceClient castorev1pb.DirectoryServiceClient,
+	blobServiceClient castorev1pb.BlobServiceClient,
+	pathInfoServiceClient storev1pb.PathInfoServiceClient,
+	enableAccessLog bool,
+	priority int,
+) *Server {
+	r := chi.NewRouter()
+	r.Use(func(h http.Handler) http.Handler {
+		return otelhttp.NewHandler(h, "http.request")
+	})
+
+	if enableAccessLog {
+		r.Use(middleware.Logger)
+	}
+
+	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
+		_, err := w.Write([]byte("nar-bridge"))
+		if err != nil {
+			log.Errorf("Unable to write response: %v", err)
+		}
+	})
+
+	r.Get("/nix-cache-info", func(w http.ResponseWriter, r *http.Request) {
+		_, err := w.Write([]byte(fmt.Sprintf("StoreDir: /nix/store\nWantMassQuery: 1\nPriority: %d\n", priority)))
+		if err != nil {
+			log.Errorf("Unable to write response: %v", err)
+		}
+	})
+
+	s := &Server{
+		handler:                r,
+		directoryServiceClient: directoryServiceClient,
+		blobServiceClient:      blobServiceClient,
+		pathInfoServiceClient:  pathInfoServiceClient,
+		narDb:                  make(map[string]*narData),
+	}
+
+	registerNarPut(s)
+	registerNarinfoPut(s)
+
+	registerNarinfoGet(s)
+	registerNarGet(s)
+
+	return s
+}
+
+func (s *Server) Shutdown(ctx context.Context) error {
+	return s.srv.Shutdown(ctx)
+}
+
+// ListenAndServer starts the webserver, and waits for it being closed or
+// shutdown, after which it'll return ErrServerClosed.
+func (s *Server) ListenAndServe(addr string) error {
+	s.srv = &http.Server{
+		Handler:      s.handler,
+		ReadTimeout:  500 * time.Second,
+		WriteTimeout: 500 * time.Second,
+		IdleTimeout:  500 * time.Second,
+	}
+
+	var listener net.Listener
+	var err error
+
+	// check addr. If it contains slashes, assume it's a unix domain socket.
+	if strings.Contains(addr, "/") {
+		listener, err = net.Listen("unix", addr)
+	} else {
+		listener, err = net.Listen("tcp", addr)
+	}
+	if err != nil {
+		return fmt.Errorf("unable to listen on %v: %w", addr, err)
+	}
+
+	return s.srv.Serve(listener)
+}
diff --git a/tvix/nar-bridge/pkg/http/util.go b/tvix/nar-bridge/pkg/http/util.go
new file mode 100644
index 0000000000..60febea1f4
--- /dev/null
+++ b/tvix/nar-bridge/pkg/http/util.go
@@ -0,0 +1,24 @@
+package http
+
+import (
+	"fmt"
+	nixhash "github.com/nix-community/go-nix/pkg/hash"
+)
+
+// parseNarHashFromUrl parses a nixbase32 string representing a sha256 NarHash
+// and returns a nixhash.Hash when it was able to parse, or an error.
+func parseNarHashFromUrl(narHashFromUrl string) (*nixhash.Hash, error) {
+	// peek at the length. If it's 52 characters, assume sha256,
+	// if it's something else, this is an error.
+	l := len(narHashFromUrl)
+	if l != 52 {
+		return nil, fmt.Errorf("invalid length of narHash: %v", l)
+	}
+
+	nixHash, err := nixhash.ParseNixBase32("sha256:" + narHashFromUrl)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse nixbase32 hash: %w", err)
+	}
+
+	return nixHash, nil
+}
diff --git a/tvix/nar-bridge/pkg/importer/blob_upload.go b/tvix/nar-bridge/pkg/importer/blob_upload.go
new file mode 100644
index 0000000000..c1255dd3ad
--- /dev/null
+++ b/tvix/nar-bridge/pkg/importer/blob_upload.go
@@ -0,0 +1,71 @@
+package importer
+
+import (
+	"bufio"
+	"context"
+	"encoding/base64"
+	"errors"
+	"fmt"
+	"io"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	log "github.com/sirupsen/logrus"
+)
+
+// the size of individual BlobChunk we send when uploading to BlobService.
+const chunkSize = 1024 * 1024
+
+// this produces a callback function that can be used as blobCb for the
+// importer.Import function call.
+func GenBlobUploaderCb(ctx context.Context, blobServiceClient castorev1pb.BlobServiceClient) func(io.Reader) ([]byte, error) {
+	return func(blobReader io.Reader) ([]byte, error) {
+		// Ensure the blobReader is buffered to at least the chunk size.
+		blobReader = bufio.NewReaderSize(blobReader, chunkSize)
+
+		putter, err := blobServiceClient.Put(ctx)
+		if err != nil {
+			// return error to the importer
+			return nil, fmt.Errorf("error from blob service: %w", err)
+		}
+
+		blobSize := 0
+		chunk := make([]byte, chunkSize)
+
+		for {
+			n, err := blobReader.Read(chunk)
+			if err != nil && !errors.Is(err, io.EOF) {
+				return nil, fmt.Errorf("unable to read from blobreader: %w", err)
+			}
+
+			if n != 0 {
+				log.WithField("chunk_size", n).Debug("sending chunk")
+				blobSize += n
+
+				// send the blob chunk to the server. The err is only valid in the inner scope
+				if err := putter.Send(&castorev1pb.BlobChunk{
+					Data: chunk[:n],
+				}); err != nil {
+					return nil, fmt.Errorf("sending blob chunk: %w", err)
+				}
+			}
+
+			// if our read from blobReader returned an EOF, we're done reading
+			if errors.Is(err, io.EOF) {
+				break
+			}
+
+		}
+
+		resp, err := putter.CloseAndRecv()
+		if err != nil {
+			return nil, fmt.Errorf("close blob putter: %w", err)
+		}
+
+		log.WithFields(log.Fields{
+			"blob_digest": base64.StdEncoding.EncodeToString(resp.GetDigest()),
+			"blob_size":   blobSize,
+		}).Debug("uploaded blob")
+
+		return resp.GetDigest(), nil
+	}
+}
diff --git a/tvix/nar-bridge/pkg/importer/counting_writer.go b/tvix/nar-bridge/pkg/importer/counting_writer.go
new file mode 100644
index 0000000000..d003a4b11b
--- /dev/null
+++ b/tvix/nar-bridge/pkg/importer/counting_writer.go
@@ -0,0 +1,21 @@
+package importer
+
+import (
+	"io"
+)
+
+// CountingWriter implements io.Writer.
+var _ io.Writer = &CountingWriter{}
+
+type CountingWriter struct {
+	bytesWritten uint64
+}
+
+func (cw *CountingWriter) Write(p []byte) (n int, err error) {
+	cw.bytesWritten += uint64(len(p))
+	return len(p), nil
+}
+
+func (cw *CountingWriter) BytesWritten() uint64 {
+	return cw.bytesWritten
+}
diff --git a/tvix/nar-bridge/pkg/importer/directory_upload.go b/tvix/nar-bridge/pkg/importer/directory_upload.go
new file mode 100644
index 0000000000..117f442fa5
--- /dev/null
+++ b/tvix/nar-bridge/pkg/importer/directory_upload.go
@@ -0,0 +1,88 @@
+package importer
+
+import (
+	"bytes"
+	"context"
+	"encoding/base64"
+	"fmt"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	log "github.com/sirupsen/logrus"
+)
+
+// DirectoriesUploader opens a Put stream when it receives the first Put() call,
+// and then uses the opened stream for subsequent Put() calls.
+// When the uploading is finished, a call to Done() will close the stream and
+// return the root digest returned from the directoryServiceClient.
+type DirectoriesUploader struct {
+	ctx                       context.Context
+	directoryServiceClient    castorev1pb.DirectoryServiceClient
+	directoryServicePutStream castorev1pb.DirectoryService_PutClient
+	lastDirectoryDigest       []byte
+}
+
+func NewDirectoriesUploader(ctx context.Context, directoryServiceClient castorev1pb.DirectoryServiceClient) *DirectoriesUploader {
+	return &DirectoriesUploader{
+		ctx:                       ctx,
+		directoryServiceClient:    directoryServiceClient,
+		directoryServicePutStream: nil,
+	}
+}
+
+func (du *DirectoriesUploader) Put(directory *castorev1pb.Directory) ([]byte, error) {
+	directoryDigest, err := directory.Digest()
+	if err != nil {
+		return nil, fmt.Errorf("failed calculating directory digest: %w", err)
+	}
+
+	// Send the directory to the directory service
+	// If the stream hasn't been initialized yet, do it first
+	if du.directoryServicePutStream == nil {
+		directoryServicePutStream, err := du.directoryServiceClient.Put(du.ctx)
+		if err != nil {
+			return nil, fmt.Errorf("unable to initialize directory service put stream: %v", err)
+		}
+		du.directoryServicePutStream = directoryServicePutStream
+	}
+
+	// send the directory out
+	err = du.directoryServicePutStream.Send(directory)
+	if err != nil {
+		return nil, fmt.Errorf("error sending directory: %w", err)
+	}
+	log.WithField("digest", base64.StdEncoding.EncodeToString(directoryDigest)).Debug("uploaded directory")
+
+	// update lastDirectoryDigest
+	du.lastDirectoryDigest = directoryDigest
+
+	return directoryDigest, nil
+}
+
+// Done closes the stream and returns the response.
+// It returns null if closed for a second time.
+func (du *DirectoriesUploader) Done() (*castorev1pb.PutDirectoryResponse, error) {
+	// only close once, and only if we opened.
+	if du.directoryServicePutStream == nil {
+		return nil, nil
+	}
+
+	putDirectoryResponse, err := du.directoryServicePutStream.CloseAndRecv()
+	if err != nil {
+		return nil, fmt.Errorf("unable to close directory service put stream: %v", err)
+	}
+
+	// ensure the response contains the same digest as the one we have in lastDirectoryDigest.
+	// Otherwise, the backend came up with another digest than we, in which we return an error.
+	if !bytes.Equal(du.lastDirectoryDigest, putDirectoryResponse.RootDigest) {
+		return nil, fmt.Errorf(
+			"backend calculated different root digest as we, expected %s, actual %s",
+			base64.StdEncoding.EncodeToString(du.lastDirectoryDigest),
+			base64.StdEncoding.EncodeToString(putDirectoryResponse.RootDigest),
+		)
+	}
+
+	// clear directoryServicePutStream.
+	du.directoryServicePutStream = nil
+
+	return putDirectoryResponse, nil
+}
diff --git a/tvix/nar-bridge/pkg/importer/gen_pathinfo.go b/tvix/nar-bridge/pkg/importer/gen_pathinfo.go
new file mode 100644
index 0000000000..bdc298a9a3
--- /dev/null
+++ b/tvix/nar-bridge/pkg/importer/gen_pathinfo.go
@@ -0,0 +1,62 @@
+package importer
+
+import (
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	storev1pb "code.tvl.fyi/tvix/store-go"
+	"fmt"
+	"github.com/nix-community/go-nix/pkg/narinfo"
+	"github.com/nix-community/go-nix/pkg/storepath"
+)
+
+// GenPathInfo takes a rootNode and narInfo and assembles a PathInfo.
+// The rootNode is renamed to match the StorePath in the narInfo.
+func GenPathInfo(rootNode *castorev1pb.Node, narInfo *narinfo.NarInfo) (*storev1pb.PathInfo, error) {
+	// parse the storePath from the .narinfo
+	storePath, err := storepath.FromAbsolutePath(narInfo.StorePath)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse StorePath: %w", err)
+	}
+
+	// construct the references, by parsing ReferenceNames and extracting the digest
+	references := make([][]byte, len(narInfo.References))
+	for i, referenceStr := range narInfo.References {
+		// parse reference as store path
+		referenceStorePath, err := storepath.FromString(referenceStr)
+		if err != nil {
+			return nil, fmt.Errorf("unable to parse reference %s as storepath: %w", referenceStr, err)
+		}
+		references[i] = referenceStorePath.Digest
+	}
+
+	// construct the narInfo.Signatures[*] from pathInfo.Narinfo.Signatures[*]
+	narinfoSignatures := make([]*storev1pb.NARInfo_Signature, len(narInfo.Signatures))
+	for i, narinfoSig := range narInfo.Signatures {
+		narinfoSignatures[i] = &storev1pb.NARInfo_Signature{
+			Name: narinfoSig.Name,
+			Data: narinfoSig.Data,
+		}
+	}
+
+	// assemble the PathInfo.
+	pathInfo := &storev1pb.PathInfo{
+		// embed a new root node with the name set to the store path basename.
+		Node:       castorev1pb.RenamedNode(rootNode, storePath.String()),
+		References: references,
+		Narinfo: &storev1pb.NARInfo{
+			NarSize:        narInfo.NarSize,
+			NarSha256:      narInfo.FileHash.Digest(),
+			Signatures:     narinfoSignatures,
+			ReferenceNames: narInfo.References,
+		},
+	}
+
+	// run Validate on the PathInfo, more as an additional sanity check our code is sound,
+	// to make sure we populated everything properly, before returning it.
+	// Fail hard if we fail validation, this is a code error.
+	if _, err = pathInfo.Validate(); err != nil {
+		panic(fmt.Sprintf("PathInfo failed validation: %v", err))
+	}
+
+	return pathInfo, nil
+
+}
diff --git a/tvix/nar-bridge/pkg/importer/importer.go b/tvix/nar-bridge/pkg/importer/importer.go
new file mode 100644
index 0000000000..fce6c5f293
--- /dev/null
+++ b/tvix/nar-bridge/pkg/importer/importer.go
@@ -0,0 +1,303 @@
+package importer
+
+import (
+	"bytes"
+	"context"
+	"crypto/sha256"
+	"errors"
+	"fmt"
+	"io"
+	"path"
+	"strings"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	"github.com/nix-community/go-nix/pkg/nar"
+	"golang.org/x/sync/errgroup"
+	"lukechampine.com/blake3"
+)
+
+const (
+	// asyncUploadThreshold controls when a file is buffered into memory and uploaded
+	// asynchronously. Files must be smaller than the threshold to be uploaded asynchronously.
+	asyncUploadThreshold = 1024 * 1024 // 1 MiB
+	// maxAsyncUploadBufferBytes is the maximum number of async blob uploads allowed to be
+	// running concurrently at any given time for a simple import operation.
+	maxConcurrentAsyncUploads = 128
+)
+
+// An item on the directories stack
+type stackItem struct {
+	path      string
+	directory *castorev1pb.Directory
+}
+
+// Import reads a NAR from a reader, and returns a the root node,
+// NAR size and NAR sha256 digest.
+func Import(
+	// a context, to support cancellation
+	ctx context.Context,
+	// The reader the data is read from
+	r io.Reader,
+	// callback function called with each regular file content
+	blobCb func(fileReader io.Reader) ([]byte, error),
+	// callback function called with each finalized directory node
+	directoryCb func(directory *castorev1pb.Directory) ([]byte, error),
+) (*castorev1pb.Node, uint64, []byte, error) {
+	// We need to wrap the underlying reader a bit.
+	// - we want to keep track of the number of bytes read in total
+	// - we calculate the sha256 digest over all data read
+	// Express these two things in a MultiWriter, and give the NAR reader a
+	// TeeReader that writes to it.
+	narCountW := &CountingWriter{}
+	sha256W := sha256.New()
+	multiW := io.MultiWriter(narCountW, sha256W)
+	narReader, err := nar.NewReader(io.TeeReader(r, multiW))
+	if err != nil {
+		return nil, 0, nil, fmt.Errorf("failed to instantiate nar reader: %w", err)
+	}
+	defer narReader.Close()
+
+	// If we store a symlink or regular file at the root, these are not nil.
+	// If they are nil, we instead have a stackDirectory.
+	var rootSymlink *castorev1pb.SymlinkNode
+	var rootFile *castorev1pb.FileNode
+	var stackDirectory *castorev1pb.Directory
+
+	// Keep track of all asynch blob uploads so we can make sure they all succeed
+	// before returning.
+	var asyncBlobWg errgroup.Group
+	asyncBlobWg.SetLimit(maxConcurrentAsyncUploads)
+
+	var stack = []stackItem{}
+
+	// popFromStack is used when we transition to a different directory or
+	// drain the stack when we reach the end of the NAR.
+	// It adds the popped element to the element underneath if any,
+	// and passes it to the directoryCb callback.
+	// This function may only be called if the stack is not already empty.
+	popFromStack := func() error {
+		// Keep the top item, and "resize" the stack slice.
+		// This will only make the last element unaccessible, but chances are high
+		// we're re-using that space anyways.
+		toPop := stack[len(stack)-1]
+		stack = stack[:len(stack)-1]
+
+		// call the directoryCb
+		directoryDigest, err := directoryCb(toPop.directory)
+		if err != nil {
+			return fmt.Errorf("failed calling directoryCb: %w", err)
+		}
+
+		// if there's still a parent left on the stack, refer to it from there.
+		if len(stack) > 0 {
+			topOfStack := stack[len(stack)-1].directory
+			topOfStack.Directories = append(topOfStack.Directories, &castorev1pb.DirectoryNode{
+				Name:   []byte(path.Base(toPop.path)),
+				Digest: directoryDigest,
+				Size:   toPop.directory.Size(),
+			})
+		}
+		// Keep track that we have encounter at least one directory
+		stackDirectory = toPop.directory
+		return nil
+	}
+
+	getBasename := func(p string) string {
+		// extract the basename. In case of "/", replace with empty string.
+		basename := path.Base(p)
+		if basename == "/" {
+			basename = ""
+		}
+		return basename
+	}
+
+	for {
+		select {
+		case <-ctx.Done():
+			return nil, 0, nil, ctx.Err()
+		default:
+			// call narReader.Next() to get the next element
+			hdr, err := narReader.Next()
+
+			// If this returns an error, it's either EOF (when we're done reading from the NAR),
+			// or another error.
+			if err != nil {
+				// if this returns no EOF, bail out
+				if !errors.Is(err, io.EOF) {
+					return nil, 0, nil, fmt.Errorf("failed getting next nar element: %w", err)
+				}
+
+				// The NAR has been read all the way to the end…
+				// Make sure we close the nar reader, which might read some final trailers.
+				if err := narReader.Close(); err != nil {
+					return nil, 0, nil, fmt.Errorf("unable to close nar reader: %w", err)
+				}
+
+				// Check the stack. While it's not empty, we need to pop things off the stack.
+				for len(stack) > 0 {
+					err := popFromStack()
+					if err != nil {
+						return nil, 0, nil, fmt.Errorf("unable to pop from stack: %w", err)
+					}
+				}
+
+				// Wait for any pending blob uploads.
+				err := asyncBlobWg.Wait()
+				if err != nil {
+					return nil, 0, nil, fmt.Errorf("async blob upload: %w", err)
+				}
+
+				// Stack is empty.
+				// Now either root{File,Symlink,Directory} is not nil,
+				// and we can return the root node.
+				narSize := narCountW.BytesWritten()
+				narSha256 := sha256W.Sum(nil)
+
+				if rootFile != nil {
+					return &castorev1pb.Node{
+						Node: &castorev1pb.Node_File{
+							File: rootFile,
+						},
+					}, narSize, narSha256, nil
+				} else if rootSymlink != nil {
+					return &castorev1pb.Node{
+						Node: &castorev1pb.Node_Symlink{
+							Symlink: rootSymlink,
+						},
+					}, narSize, narSha256, nil
+				} else if stackDirectory != nil {
+					// calculate directory digest (i.e. after we received all its contents)
+					dgst, err := stackDirectory.Digest()
+					if err != nil {
+						return nil, 0, nil, fmt.Errorf("unable to calculate root directory digest: %w", err)
+					}
+
+					return &castorev1pb.Node{
+						Node: &castorev1pb.Node_Directory{
+							Directory: &castorev1pb.DirectoryNode{
+								Name:   []byte{},
+								Digest: dgst,
+								Size:   stackDirectory.Size(),
+							},
+						},
+					}, narSize, narSha256, nil
+				} else {
+					return nil, 0, nil, fmt.Errorf("no root set")
+				}
+			}
+
+			// Check for valid path transitions, pop from stack if needed
+			// The nar reader already gives us some guarantees about ordering and illegal transitions,
+			// So we really only need to check if the top-of-stack path is a prefix of the path,
+			// and if it's not, pop from the stack. We do this repeatedly until the top of the stack is
+			// the subdirectory the new entry is in, or we hit the root directory.
+
+			// We don't need to worry about the root node case, because we can only finish the root "/"
+			// If we're at the end of the NAR reader (covered by the EOF check)
+			for len(stack) > 1 && !strings.HasPrefix(hdr.Path, stack[len(stack)-1].path+"/") {
+				err := popFromStack()
+				if err != nil {
+					return nil, 0, nil, fmt.Errorf("unable to pop from stack: %w", err)
+				}
+			}
+
+			if hdr.Type == nar.TypeSymlink {
+				symlinkNode := &castorev1pb.SymlinkNode{
+					Name:   []byte(getBasename(hdr.Path)),
+					Target: []byte(hdr.LinkTarget),
+				}
+				if len(stack) > 0 {
+					topOfStack := stack[len(stack)-1].directory
+					topOfStack.Symlinks = append(topOfStack.Symlinks, symlinkNode)
+				} else {
+					rootSymlink = symlinkNode
+				}
+
+			}
+			if hdr.Type == nar.TypeRegular {
+				uploadBlob := func(r io.Reader) ([]byte, error) {
+					// wrap reader with a reader counting the number of bytes read
+					blobCountW := &CountingWriter{}
+					blobReader := io.TeeReader(r, blobCountW)
+
+					blobDigest, err := blobCb(blobReader)
+					if err != nil {
+						return nil, fmt.Errorf("failure from blobCb: %w", err)
+					}
+
+					// ensure blobCb did read all the way to the end.
+					// If it didn't, the blobCb function is wrong and we should bail out.
+					if blobCountW.BytesWritten() != uint64(hdr.Size) {
+						return nil, fmt.Errorf("blobCb did not read all: %d/%d bytes", blobCountW.BytesWritten(), hdr.Size)
+					}
+
+					return blobDigest, nil
+				}
+
+				h := blake3.New(32, nil)
+				blobReader := io.TeeReader(narReader, io.MultiWriter(h))
+				var blobDigest []byte
+
+				// If this file is small enough, read it off the wire immediately and
+				// upload to the blob service asynchronously. This helps reduce the
+				// RTT on blob uploads for NARs with many small files.
+				doAsync := hdr.Size < asyncUploadThreshold
+				if doAsync {
+					blobContents, err := io.ReadAll(blobReader)
+					if err != nil {
+						return nil, 0, nil, fmt.Errorf("read blob: %w", err)
+					}
+
+					blobDigest = h.Sum(nil)
+
+					asyncBlobWg.Go(func() error {
+						blobDigestFromCb, err := uploadBlob(bytes.NewReader(blobContents))
+						if err != nil {
+							return err
+						}
+
+						if !bytes.Equal(blobDigest, blobDigestFromCb) {
+							return fmt.Errorf("unexpected digest (got %x, expected %x)", blobDigestFromCb, blobDigest)
+						}
+
+						return nil
+					})
+				} else {
+					blobDigestFromCb, err := uploadBlob(blobReader)
+					if err != nil {
+						return nil, 0, nil, fmt.Errorf("upload blob: %w", err)
+					}
+
+					blobDigest = h.Sum(nil)
+					if !bytes.Equal(blobDigest, blobDigestFromCb) {
+						return nil, 0, nil, fmt.Errorf("unexpected digest (got %x, expected %x)", blobDigestFromCb, blobDigest)
+					}
+				}
+
+				fileNode := &castorev1pb.FileNode{
+					Name:       []byte(getBasename(hdr.Path)),
+					Digest:     blobDigest,
+					Size:       uint64(hdr.Size),
+					Executable: hdr.Executable,
+				}
+				if len(stack) > 0 {
+					topOfStack := stack[len(stack)-1].directory
+					topOfStack.Files = append(topOfStack.Files, fileNode)
+				} else {
+					rootFile = fileNode
+				}
+			}
+			if hdr.Type == nar.TypeDirectory {
+				directory := &castorev1pb.Directory{
+					Directories: []*castorev1pb.DirectoryNode{},
+					Files:       []*castorev1pb.FileNode{},
+					Symlinks:    []*castorev1pb.SymlinkNode{},
+				}
+				stack = append(stack, stackItem{
+					directory: directory,
+					path:      hdr.Path,
+				})
+			}
+		}
+	}
+}
diff --git a/tvix/nar-bridge/pkg/importer/importer_test.go b/tvix/nar-bridge/pkg/importer/importer_test.go
new file mode 100644
index 0000000000..8ff63b9257
--- /dev/null
+++ b/tvix/nar-bridge/pkg/importer/importer_test.go
@@ -0,0 +1,537 @@
+package importer_test
+
+import (
+	"bytes"
+	"context"
+	"errors"
+	"io"
+	"os"
+	"testing"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	"code.tvl.fyi/tvix/nar-bridge/pkg/importer"
+	"github.com/stretchr/testify/require"
+)
+
+func TestSymlink(t *testing.T) {
+	f, err := os.Open("../../testdata/symlink.nar")
+	require.NoError(t, err)
+
+	rootNode, narSize, narSha256, err := importer.Import(
+		context.Background(),
+		f,
+		func(blobReader io.Reader) ([]byte, error) {
+			panic("no file contents expected!")
+		}, func(directory *castorev1pb.Directory) ([]byte, error) {
+			panic("no directories expected!")
+		},
+	)
+	require.NoError(t, err)
+	require.Equal(t, &castorev1pb.Node{
+		Node: &castorev1pb.Node_Symlink{
+			Symlink: &castorev1pb.SymlinkNode{
+				Name:   []byte(""),
+				Target: []byte("/nix/store/somewhereelse"),
+			},
+		},
+	}, rootNode)
+	require.Equal(t, []byte{
+		0x09, 0x7d, 0x39, 0x7e, 0x9b, 0x58, 0x26, 0x38, 0x4e, 0xaa, 0x16, 0xc4, 0x57, 0x71, 0x5d, 0x1c, 0x1a, 0x51, 0x67, 0x03, 0x13, 0xea, 0xd0, 0xf5, 0x85, 0x66, 0xe0, 0xb2, 0x32, 0x53, 0x9c, 0xf1,
+	}, narSha256)
+	require.Equal(t, uint64(136), narSize)
+}
+
+func TestRegular(t *testing.T) {
+	f, err := os.Open("../../testdata/onebyteregular.nar")
+	require.NoError(t, err)
+
+	rootNode, narSize, narSha256, err := importer.Import(
+		context.Background(),
+		f,
+		func(blobReader io.Reader) ([]byte, error) {
+			contents, err := io.ReadAll(blobReader)
+			require.NoError(t, err, "reading blobReader should not error")
+			require.Equal(t, []byte{0x01}, contents, "contents read from blobReader should match expectations")
+			return mustBlobDigest(bytes.NewBuffer(contents)), nil
+		}, func(directory *castorev1pb.Directory) ([]byte, error) {
+			panic("no directories expected!")
+		},
+	)
+
+	// The blake3 digest of the 0x01 byte.
+	BLAKE3_DIGEST_0X01 := []byte{
+		0x48, 0xfc, 0x72, 0x1f, 0xbb, 0xc1, 0x72, 0xe0, 0x92, 0x5f, 0xa2, 0x7a, 0xf1, 0x67, 0x1d,
+		0xe2, 0x25, 0xba, 0x92, 0x71, 0x34, 0x80, 0x29, 0x98, 0xb1, 0x0a, 0x15, 0x68, 0xa1, 0x88,
+		0x65, 0x2b,
+	}
+
+	require.NoError(t, err)
+	require.Equal(t, &castorev1pb.Node{
+		Node: &castorev1pb.Node_File{
+			File: &castorev1pb.FileNode{
+				Name:       []byte(""),
+				Digest:     BLAKE3_DIGEST_0X01,
+				Size:       1,
+				Executable: false,
+			},
+		},
+	}, rootNode)
+	require.Equal(t, []byte{
+		0x73, 0x08, 0x50, 0xa8, 0x11, 0x25, 0x9d, 0xbf, 0x3a, 0x68, 0xdc, 0x2e, 0xe8, 0x7a, 0x79, 0xaa, 0x6c, 0xae, 0x9f, 0x71, 0x37, 0x5e, 0xdf, 0x39, 0x6f, 0x9d, 0x7a, 0x91, 0xfb, 0xe9, 0x13, 0x4d,
+	}, narSha256)
+	require.Equal(t, uint64(120), narSize)
+}
+
+func TestEmptyDirectory(t *testing.T) {
+	f, err := os.Open("../../testdata/emptydirectory.nar")
+	require.NoError(t, err)
+
+	expectedDirectory := &castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{},
+		Files:       []*castorev1pb.FileNode{},
+		Symlinks:    []*castorev1pb.SymlinkNode{},
+	}
+	rootNode, narSize, narSha256, err := importer.Import(
+		context.Background(),
+		f,
+		func(blobReader io.Reader) ([]byte, error) {
+			panic("no file contents expected!")
+		}, func(directory *castorev1pb.Directory) ([]byte, error) {
+			requireProtoEq(t, expectedDirectory, directory)
+			return mustDirectoryDigest(directory), nil
+		},
+	)
+	require.NoError(t, err)
+	require.Equal(t, &castorev1pb.Node{
+		Node: &castorev1pb.Node_Directory{
+			Directory: &castorev1pb.DirectoryNode{
+				Name:   []byte(""),
+				Digest: mustDirectoryDigest(expectedDirectory),
+				Size:   expectedDirectory.Size(),
+			},
+		},
+	}, rootNode)
+	require.Equal(t, []byte{
+		0xa5, 0x0a, 0x5a, 0xb6, 0xd9, 0x92, 0xf5, 0x59, 0x8e, 0xdd, 0x92, 0x10, 0x50, 0x59, 0xfa, 0xe9, 0xac, 0xfc, 0x19, 0x29, 0x81, 0xe0, 0x8b, 0xd8, 0x85, 0x34, 0xc2, 0x16, 0x7e, 0x92, 0x52, 0x6a,
+	}, narSha256)
+	require.Equal(t, uint64(96), narSize)
+}
+
+func TestFull(t *testing.T) {
+	f, err := os.Open("../../testdata/nar_1094wph9z4nwlgvsd53abfz8i117ykiv5dwnq9nnhz846s7xqd7d.nar")
+	require.NoError(t, err)
+
+	expectedDirectoryPaths := []string{
+		"/bin",
+		"/share/man/man1",
+		"/share/man/man5",
+		"/share/man/man8",
+		"/share/man",
+		"/share",
+		"/",
+	}
+	expectedDirectories := make(map[string]*castorev1pb.Directory, len(expectedDirectoryPaths))
+
+	// /bin is a leaf directory
+	expectedDirectories["/bin"] = &castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{},
+		Files: []*castorev1pb.FileNode{
+			{
+				Name: []byte("arp"),
+				Digest: []byte{
+					0xfb, 0xc4, 0x61, 0x4a, 0x29, 0x27, 0x11, 0xcb, 0xcc, 0xe4, 0x99, 0x81, 0x9c, 0xf0, 0xa9, 0x17, 0xf7, 0xd0, 0x91, 0xbe, 0xea, 0x08, 0xcb, 0x5b, 0xaa, 0x76, 0x76, 0xf5, 0xee, 0x4f, 0x82, 0xbb,
+				},
+				Size:       55288,
+				Executable: true,
+			},
+			{
+				Name: []byte("hostname"),
+				Digest: []byte{
+					0x9c, 0x6a, 0xe4, 0xb5, 0xe4, 0x6c, 0xb5, 0x67, 0x45, 0x0e, 0xaa, 0x2a, 0xd8, 0xdd, 0x9b, 0x38, 0xd7, 0xed, 0x01, 0x02, 0x84, 0xf7, 0x26, 0xe1, 0xc7, 0xf3, 0x1c, 0xeb, 0xaa, 0x8a, 0x01, 0x30,
+				},
+				Size:       17704,
+				Executable: true,
+			},
+			{
+				Name: []byte("ifconfig"),
+				Digest: []byte{
+					0x25, 0xbe, 0x3b, 0x1d, 0xf4, 0x1a, 0x45, 0x42, 0x79, 0x09, 0x2c, 0x2a, 0x83, 0xf0, 0x0b, 0xff, 0xe8, 0xc0, 0x9c, 0x26, 0x98, 0x70, 0x15, 0x4d, 0xa8, 0xca, 0x05, 0xfe, 0x92, 0x68, 0x35, 0x2e,
+				},
+				Size:       72576,
+				Executable: true,
+			},
+			{
+				Name: []byte("nameif"),
+				Digest: []byte{
+					0x8e, 0xaa, 0xc5, 0xdb, 0x71, 0x08, 0x8e, 0xe5, 0xe6, 0x30, 0x1f, 0x2c, 0x3a, 0xf2, 0x42, 0x39, 0x0c, 0x57, 0x15, 0xaf, 0x50, 0xaa, 0x1c, 0xdf, 0x84, 0x22, 0x08, 0x77, 0x03, 0x54, 0x62, 0xb1,
+				},
+				Size:       18776,
+				Executable: true,
+			},
+			{
+				Name: []byte("netstat"),
+				Digest: []byte{
+					0x13, 0x34, 0x7e, 0xdd, 0x2a, 0x9a, 0x17, 0x0b, 0x3f, 0xc7, 0x0a, 0xe4, 0x92, 0x89, 0x25, 0x9f, 0xaa, 0xb5, 0x05, 0x6b, 0x24, 0xa7, 0x91, 0xeb, 0xaf, 0xf9, 0xe9, 0x35, 0x56, 0xaa, 0x2f, 0xb2,
+				},
+				Size:       131784,
+				Executable: true,
+			},
+			{
+				Name: []byte("plipconfig"),
+				Digest: []byte{
+					0x19, 0x7c, 0x80, 0xdc, 0x81, 0xdc, 0xb4, 0xc0, 0x45, 0xe1, 0xf9, 0x76, 0x51, 0x4f, 0x50, 0xbf, 0xa4, 0x69, 0x51, 0x9a, 0xd4, 0xa9, 0xe7, 0xaa, 0xe7, 0x0d, 0x53, 0x32, 0xff, 0x28, 0x40, 0x60,
+				},
+				Size:       13160,
+				Executable: true,
+			},
+			{
+				Name: []byte("rarp"),
+				Digest: []byte{
+					0x08, 0x85, 0xb4, 0x85, 0x03, 0x2b, 0x3c, 0x7a, 0x3e, 0x24, 0x4c, 0xf8, 0xcc, 0x45, 0x01, 0x9e, 0x79, 0x43, 0x8c, 0x6f, 0x5e, 0x32, 0x46, 0x54, 0xb6, 0x68, 0x91, 0x8e, 0xa0, 0xcb, 0x6e, 0x0d,
+				},
+				Size:       30384,
+				Executable: true,
+			},
+			{
+				Name: []byte("route"),
+				Digest: []byte{
+					0x4d, 0x14, 0x20, 0x89, 0x9e, 0x76, 0xf4, 0xe2, 0x92, 0x53, 0xee, 0x9b, 0x78, 0x7d, 0x23, 0x80, 0x6c, 0xff, 0xe6, 0x33, 0xdc, 0x4a, 0x10, 0x29, 0x39, 0x02, 0xa0, 0x60, 0xff, 0xe2, 0xbb, 0xd7,
+				},
+				Size:       61928,
+				Executable: true,
+			},
+			{
+				Name: []byte("slattach"),
+				Digest: []byte{
+					0xfb, 0x25, 0xc3, 0x73, 0xb7, 0xb1, 0x0b, 0x25, 0xcd, 0x7b, 0x62, 0xf6, 0x71, 0x83, 0xfe, 0x36, 0x80, 0xf6, 0x48, 0xc3, 0xdb, 0xd8, 0x0c, 0xfe, 0xb8, 0xd3, 0xda, 0x32, 0x9b, 0x47, 0x4b, 0x05,
+				},
+				Size:       35672,
+				Executable: true,
+			},
+		},
+		Symlinks: []*castorev1pb.SymlinkNode{
+			{
+				Name:   []byte("dnsdomainname"),
+				Target: []byte("hostname"),
+			},
+			{
+				Name:   []byte("domainname"),
+				Target: []byte("hostname"),
+			},
+			{
+				Name:   []byte("nisdomainname"),
+				Target: []byte("hostname"),
+			},
+			{
+				Name:   []byte("ypdomainname"),
+				Target: []byte("hostname"),
+			},
+		},
+	}
+
+	// /share/man/man1 is a leaf directory.
+	// The parser traversed over /sbin, but only added it to / which is still on the stack.
+	expectedDirectories["/share/man/man1"] = &castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{},
+		Files: []*castorev1pb.FileNode{
+			{
+				Name: []byte("dnsdomainname.1.gz"),
+				Digest: []byte{
+					0x98, 0x8a, 0xbd, 0xfa, 0x64, 0xd5, 0xb9, 0x27, 0xfe, 0x37, 0x43, 0x56, 0xb3, 0x18, 0xc7, 0x2b, 0xcb, 0xe3, 0x17, 0x1c, 0x17, 0xf4, 0x17, 0xeb, 0x4a, 0xa4, 0x99, 0x64, 0x39, 0xca, 0x2d, 0xee,
+				},
+				Size:       40,
+				Executable: false,
+			},
+			{
+				Name: []byte("domainname.1.gz"),
+				Digest: []byte{
+					0x98, 0x8a, 0xbd, 0xfa, 0x64, 0xd5, 0xb9, 0x27, 0xfe, 0x37, 0x43, 0x56, 0xb3, 0x18, 0xc7, 0x2b, 0xcb, 0xe3, 0x17, 0x1c, 0x17, 0xf4, 0x17, 0xeb, 0x4a, 0xa4, 0x99, 0x64, 0x39, 0xca, 0x2d, 0xee,
+				},
+				Size:       40,
+				Executable: false,
+			},
+			{
+				Name: []byte("hostname.1.gz"),
+				Digest: []byte{
+					0xbf, 0x89, 0xe6, 0x28, 0x00, 0x24, 0x66, 0x79, 0x70, 0x04, 0x38, 0xd6, 0xdd, 0x9d, 0xf6, 0x0e, 0x0d, 0xee, 0x00, 0xf7, 0x64, 0x4f, 0x05, 0x08, 0x9d, 0xf0, 0x36, 0xde, 0x85, 0xf4, 0x75, 0xdb,
+				},
+				Size:       1660,
+				Executable: false,
+			},
+			{
+				Name: []byte("nisdomainname.1.gz"),
+				Digest: []byte{
+					0x98, 0x8a, 0xbd, 0xfa, 0x64, 0xd5, 0xb9, 0x27, 0xfe, 0x37, 0x43, 0x56, 0xb3, 0x18, 0xc7, 0x2b, 0xcb, 0xe3, 0x17, 0x1c, 0x17, 0xf4, 0x17, 0xeb, 0x4a, 0xa4, 0x99, 0x64, 0x39, 0xca, 0x2d, 0xee,
+				},
+				Size:       40,
+				Executable: false,
+			},
+			{
+				Name: []byte("ypdomainname.1.gz"),
+				Digest: []byte{
+					0x98, 0x8a, 0xbd, 0xfa, 0x64, 0xd5, 0xb9, 0x27, 0xfe, 0x37, 0x43, 0x56, 0xb3, 0x18, 0xc7, 0x2b, 0xcb, 0xe3, 0x17, 0x1c, 0x17, 0xf4, 0x17, 0xeb, 0x4a, 0xa4, 0x99, 0x64, 0x39, 0xca, 0x2d, 0xee,
+				},
+				Size:       40,
+				Executable: false,
+			},
+		},
+		Symlinks: []*castorev1pb.SymlinkNode{},
+	}
+
+	// /share/man/man5 is a leaf directory
+	expectedDirectories["/share/man/man5"] = &castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{},
+		Files: []*castorev1pb.FileNode{
+			{
+				Name: []byte("ethers.5.gz"),
+				Digest: []byte{
+					0x42, 0x63, 0x8c, 0xc4, 0x18, 0x93, 0xcf, 0x60, 0xd6, 0xff, 0x43, 0xbc, 0x16, 0xb4, 0xfd, 0x22, 0xd2, 0xf2, 0x05, 0x0b, 0x52, 0xdc, 0x6a, 0x6b, 0xff, 0x34, 0xe2, 0x6a, 0x38, 0x3a, 0x07, 0xe3,
+				},
+				Size:       563,
+				Executable: false,
+			},
+		},
+		Symlinks: []*castorev1pb.SymlinkNode{},
+	}
+
+	// /share/man/man8 is a leaf directory
+	expectedDirectories["/share/man/man8"] = &castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{},
+		Files: []*castorev1pb.FileNode{
+			{
+				Name: []byte("arp.8.gz"),
+				Digest: []byte{
+					0xf5, 0x35, 0x4e, 0xf5, 0xf6, 0x44, 0xf7, 0x52, 0x0f, 0x42, 0xa0, 0x26, 0x51, 0xd9, 0x89, 0xf9, 0x68, 0xf2, 0xef, 0xeb, 0xba, 0xe1, 0xf4, 0x55, 0x01, 0x57, 0x77, 0xb7, 0x68, 0x55, 0x92, 0xef,
+				},
+				Size:       2464,
+				Executable: false,
+			},
+			{
+				Name: []byte("ifconfig.8.gz"),
+				Digest: []byte{
+					0x18, 0x65, 0x25, 0x11, 0x32, 0xee, 0x77, 0x91, 0x35, 0x4c, 0x3c, 0x24, 0xdb, 0xaf, 0x66, 0xdb, 0xfc, 0x17, 0x7b, 0xba, 0xe1, 0x3d, 0x05, 0xd2, 0xca, 0x6e, 0x2c, 0xe4, 0xef, 0xb8, 0xa8, 0xbe,
+				},
+				Size:       3382,
+				Executable: false,
+			},
+			{
+				Name: []byte("nameif.8.gz"),
+				Digest: []byte{
+					0x73, 0xc1, 0x27, 0xe8, 0x3b, 0xa8, 0x49, 0xdc, 0x0e, 0xdf, 0x70, 0x5f, 0xaf, 0x06, 0x01, 0x2c, 0x62, 0xe9, 0x18, 0x67, 0x01, 0x94, 0x64, 0x26, 0xca, 0x95, 0x22, 0xc0, 0xdc, 0xe4, 0x42, 0xb6,
+				},
+				Size:       523,
+				Executable: false,
+			},
+			{
+				Name: []byte("netstat.8.gz"),
+				Digest: []byte{
+					0xc0, 0x86, 0x43, 0x4a, 0x43, 0x57, 0xaa, 0x84, 0xa7, 0x24, 0xa0, 0x7c, 0x65, 0x38, 0x46, 0x1c, 0xf2, 0x45, 0xa2, 0xef, 0x12, 0x44, 0x18, 0xba, 0x52, 0x56, 0xe9, 0x8e, 0x6a, 0x0f, 0x70, 0x63,
+				},
+				Size:       4284,
+				Executable: false,
+			},
+			{
+				Name: []byte("plipconfig.8.gz"),
+				Digest: []byte{
+					0x2a, 0xd9, 0x1d, 0xa8, 0x9e, 0x0d, 0x05, 0xd0, 0xb0, 0x49, 0xaa, 0x64, 0xba, 0x29, 0x28, 0xc6, 0x45, 0xe1, 0xbb, 0x5e, 0x72, 0x8d, 0x48, 0x7b, 0x09, 0x4f, 0x0a, 0x82, 0x1e, 0x26, 0x83, 0xab,
+				},
+				Size:       889,
+				Executable: false,
+			},
+			{
+				Name: []byte("rarp.8.gz"),
+				Digest: []byte{
+					0x3d, 0x51, 0xc1, 0xd0, 0x6a, 0x59, 0x1e, 0x6d, 0x9a, 0xf5, 0x06, 0xd2, 0xe7, 0x7d, 0x7d, 0xd0, 0x70, 0x3d, 0x84, 0x64, 0xc3, 0x7d, 0xfb, 0x10, 0x84, 0x3b, 0xe1, 0xa9, 0xdf, 0x46, 0xee, 0x9f,
+				},
+				Size:       1198,
+				Executable: false,
+			},
+			{
+				Name: []byte("route.8.gz"),
+				Digest: []byte{
+					0x2a, 0x5a, 0x4b, 0x4f, 0x91, 0xf2, 0x78, 0xe4, 0xa9, 0x25, 0xb2, 0x7f, 0xa7, 0x2a, 0xc0, 0x8a, 0x4a, 0x65, 0xc9, 0x5f, 0x07, 0xa0, 0x48, 0x44, 0xeb, 0x46, 0xf9, 0xc9, 0xe1, 0x17, 0x96, 0x21,
+				},
+				Size:       3525,
+				Executable: false,
+			},
+			{
+				Name: []byte("slattach.8.gz"),
+				Digest: []byte{
+					0x3f, 0x05, 0x6b, 0x20, 0xe1, 0xe4, 0xf0, 0xba, 0x16, 0x15, 0x66, 0x6b, 0x57, 0x96, 0xe9, 0x9d, 0x83, 0xa8, 0x20, 0xaf, 0x8a, 0xca, 0x16, 0x4d, 0xa2, 0x6d, 0x94, 0x8e, 0xca, 0x91, 0x8f, 0xd4,
+				},
+				Size:       1441,
+				Executable: false,
+			},
+		},
+		Symlinks: []*castorev1pb.SymlinkNode{},
+	}
+
+	// /share/man holds /share/man/man{1,5,8}.
+	expectedDirectories["/share/man"] = &castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{
+			{
+				Name:   []byte("man1"),
+				Digest: mustDirectoryDigest(expectedDirectories["/share/man/man1"]),
+				Size:   expectedDirectories["/share/man/man1"].Size(),
+			},
+			{
+				Name:   []byte("man5"),
+				Digest: mustDirectoryDigest(expectedDirectories["/share/man/man5"]),
+				Size:   expectedDirectories["/share/man/man5"].Size(),
+			},
+			{
+				Name:   []byte("man8"),
+				Digest: mustDirectoryDigest(expectedDirectories["/share/man/man8"]),
+				Size:   expectedDirectories["/share/man/man8"].Size(),
+			},
+		},
+		Files:    []*castorev1pb.FileNode{},
+		Symlinks: []*castorev1pb.SymlinkNode{},
+	}
+
+	// /share holds /share/man.
+	expectedDirectories["/share"] = &castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{
+			{
+				Name:   []byte("man"),
+				Digest: mustDirectoryDigest(expectedDirectories["/share/man"]),
+				Size:   expectedDirectories["/share/man"].Size(),
+			},
+		},
+		Files:    []*castorev1pb.FileNode{},
+		Symlinks: []*castorev1pb.SymlinkNode{},
+	}
+
+	// / holds /bin, /share, and a /sbin symlink.
+	expectedDirectories["/"] = &castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{
+			{
+				Name:   []byte("bin"),
+				Digest: mustDirectoryDigest(expectedDirectories["/bin"]),
+				Size:   expectedDirectories["/bin"].Size(),
+			},
+			{
+				Name:   []byte("share"),
+				Digest: mustDirectoryDigest(expectedDirectories["/share"]),
+				Size:   expectedDirectories["/share"].Size(),
+			},
+		},
+		Files: []*castorev1pb.FileNode{},
+		Symlinks: []*castorev1pb.SymlinkNode{
+			{
+				Name:   []byte("sbin"),
+				Target: []byte("bin"),
+			},
+		},
+	}
+	// assert we populated the two fixtures properly
+	require.Equal(t, len(expectedDirectoryPaths), len(expectedDirectories))
+
+	numDirectoriesReceived := 0
+
+	rootNode, narSize, narSha256, err := importer.Import(
+		context.Background(),
+		f,
+		func(blobReader io.Reader) ([]byte, error) {
+			// Don't really bother reading and comparing the contents here,
+			// We already verify the right digests are produced by comparing the
+			// directoryCb calls, and TestRegular ensures the reader works.
+			return mustBlobDigest(blobReader), nil
+		}, func(directory *castorev1pb.Directory) ([]byte, error) {
+			// use actualDirectoryOrder to look up the Directory object we expect at this specific invocation.
+			currentDirectoryPath := expectedDirectoryPaths[numDirectoriesReceived]
+
+			expectedDirectory, found := expectedDirectories[currentDirectoryPath]
+			require.True(t, found, "must find the current directory")
+
+			requireProtoEq(t, expectedDirectory, directory)
+
+			numDirectoriesReceived += 1
+			return mustDirectoryDigest(directory), nil
+		},
+	)
+	require.NoError(t, err)
+	require.Equal(t, &castorev1pb.Node{
+		Node: &castorev1pb.Node_Directory{
+			Directory: &castorev1pb.DirectoryNode{
+				Name:   []byte(""),
+				Digest: mustDirectoryDigest(expectedDirectories["/"]),
+				Size:   expectedDirectories["/"].Size(),
+			},
+		},
+	}, rootNode)
+	require.Equal(t, []byte{
+		0xc6, 0xe1, 0x55, 0xb3, 0x45, 0x6e, 0x30, 0xb7, 0x61, 0x22, 0x63, 0xec, 0x09, 0x50, 0x70, 0x81, 0x1c, 0xaf, 0x8a, 0xbf, 0xd5, 0x9f, 0xaa, 0x72, 0xab, 0x82, 0xa5, 0x92, 0xef, 0xde, 0xb2, 0x53,
+	}, narSha256)
+	require.Equal(t, uint64(464152), narSize)
+}
+
+// TestCallbackErrors ensures that errors returned from the callback function
+// bubble up to the importer process, and are not ignored.
+func TestCallbackErrors(t *testing.T) {
+	t.Run("callback blob", func(t *testing.T) {
+		// Pick an example NAR with a regular file.
+		f, err := os.Open("../../testdata/onebyteregular.nar")
+		require.NoError(t, err)
+
+		targetErr := errors.New("expected error")
+
+		_, _, _, err = importer.Import(
+			context.Background(),
+			f,
+			func(blobReader io.Reader) ([]byte, error) {
+				return nil, targetErr
+			}, func(directory *castorev1pb.Directory) ([]byte, error) {
+				panic("no directories expected!")
+			},
+		)
+		require.ErrorIs(t, err, targetErr)
+	})
+	t.Run("callback directory", func(t *testing.T) {
+		// Pick an example NAR with a directory node
+		f, err := os.Open("../../testdata/emptydirectory.nar")
+		require.NoError(t, err)
+
+		targetErr := errors.New("expected error")
+
+		_, _, _, err = importer.Import(
+			context.Background(),
+			f,
+			func(blobReader io.Reader) ([]byte, error) {
+				panic("no file contents expected!")
+			}, func(directory *castorev1pb.Directory) ([]byte, error) {
+				return nil, targetErr
+			},
+		)
+		require.ErrorIs(t, err, targetErr)
+	})
+}
+
+// TestPopDirectories is a regression test that ensures we handle the directory
+// stack properly.
+//
+// This test case looks like:
+//
+// / (dir)
+// /test (dir)
+// /test/tested (file)
+// /tested (file)
+//
+// We used to have a bug where the second `tested` file would appear as if
+// it was in the `/test` dir because it has that dir as a string prefix.
+func TestPopDirectories(t *testing.T) {
+	f, err := os.Open("../../testdata/popdirectories.nar")
+	require.NoError(t, err)
+	defer f.Close()
+
+	_, _, _, err = importer.Import(
+		context.Background(),
+		f,
+		func(blobReader io.Reader) ([]byte, error) { return mustBlobDigest(blobReader), nil },
+		func(directory *castorev1pb.Directory) ([]byte, error) {
+			require.NoError(t, directory.Validate(), "directory validation shouldn't error")
+			return mustDirectoryDigest(directory), nil
+		},
+	)
+	require.NoError(t, err)
+}
diff --git a/tvix/nar-bridge/pkg/importer/roundtrip_test.go b/tvix/nar-bridge/pkg/importer/roundtrip_test.go
new file mode 100644
index 0000000000..6d6fcb9ee2
--- /dev/null
+++ b/tvix/nar-bridge/pkg/importer/roundtrip_test.go
@@ -0,0 +1,85 @@
+package importer_test
+
+import (
+	"bytes"
+	"context"
+	"encoding/base64"
+	"fmt"
+	"io"
+	"os"
+	"sync"
+	"testing"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	"code.tvl.fyi/tvix/nar-bridge/pkg/importer"
+	storev1pb "code.tvl.fyi/tvix/store-go"
+	"github.com/stretchr/testify/require"
+)
+
+func TestRoundtrip(t *testing.T) {
+	// We pipe nar_1094wph9z4nwlgvsd53abfz8i117ykiv5dwnq9nnhz846s7xqd7d.nar to
+	// storev1pb.Export, and store all the file contents and directory objects
+	// received in two hashmaps.
+	// We then feed it to the writer, and test we come up with the same NAR file.
+
+	f, err := os.Open("../../testdata/nar_1094wph9z4nwlgvsd53abfz8i117ykiv5dwnq9nnhz846s7xqd7d.nar")
+	require.NoError(t, err)
+
+	narContents, err := io.ReadAll(f)
+	require.NoError(t, err)
+
+	var mu sync.Mutex
+	blobsMap := make(map[string][]byte, 0)
+	directoriesMap := make(map[string]*castorev1pb.Directory)
+
+	rootNode, _, _, err := importer.Import(
+		context.Background(),
+		bytes.NewBuffer(narContents),
+		func(blobReader io.Reader) ([]byte, error) {
+			// read in contents, we need to put it into filesMap later.
+			contents, err := io.ReadAll(blobReader)
+			require.NoError(t, err)
+
+			dgst := mustBlobDigest(bytes.NewReader(contents))
+
+			// put it in filesMap
+			mu.Lock()
+			blobsMap[base64.StdEncoding.EncodeToString(dgst)] = contents
+			mu.Unlock()
+
+			return dgst, nil
+		},
+		func(directory *castorev1pb.Directory) ([]byte, error) {
+			dgst := mustDirectoryDigest(directory)
+
+			directoriesMap[base64.StdEncoding.EncodeToString(dgst)] = directory
+			return dgst, nil
+		},
+	)
+
+	require.NoError(t, err)
+
+	// done populating everything, now actually test the export :-)
+	var narBuf bytes.Buffer
+	err = storev1pb.Export(
+		&narBuf,
+		rootNode,
+		func(directoryDgst []byte) (*castorev1pb.Directory, error) {
+			d, found := directoriesMap[base64.StdEncoding.EncodeToString(directoryDgst)]
+			if !found {
+				panic(fmt.Sprintf("directory %v not found", base64.StdEncoding.EncodeToString(directoryDgst)))
+			}
+			return d, nil
+		},
+		func(blobDgst []byte) (io.ReadCloser, error) {
+			blobContents, found := blobsMap[base64.StdEncoding.EncodeToString(blobDgst)]
+			if !found {
+				panic(fmt.Sprintf("blob      %v not found", base64.StdEncoding.EncodeToString(blobDgst)))
+			}
+			return io.NopCloser(bytes.NewReader(blobContents)), nil
+		},
+	)
+
+	require.NoError(t, err, "exporter shouldn't fail")
+	require.Equal(t, narContents, narBuf.Bytes())
+}
diff --git a/tvix/nar-bridge/pkg/importer/util_test.go b/tvix/nar-bridge/pkg/importer/util_test.go
new file mode 100644
index 0000000000..06353cf582
--- /dev/null
+++ b/tvix/nar-bridge/pkg/importer/util_test.go
@@ -0,0 +1,34 @@
+package importer_test
+
+import (
+	"io"
+	"testing"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	"github.com/google/go-cmp/cmp"
+	"google.golang.org/protobuf/testing/protocmp"
+	"lukechampine.com/blake3"
+)
+
+func requireProtoEq(t *testing.T, expected interface{}, actual interface{}) {
+	if diff := cmp.Diff(expected, actual, protocmp.Transform()); diff != "" {
+		t.Errorf("unexpected difference:\n%v", diff)
+	}
+}
+
+func mustDirectoryDigest(d *castorev1pb.Directory) []byte {
+	dgst, err := d.Digest()
+	if err != nil {
+		panic(err)
+	}
+	return dgst
+}
+
+func mustBlobDigest(r io.Reader) []byte {
+	hasher := blake3.New(32, nil)
+	_, err := io.Copy(hasher, r)
+	if err != nil {
+		panic(err)
+	}
+	return hasher.Sum([]byte{})
+}
diff --git a/tvix/nar-bridge/testdata/emptydirectory.nar b/tvix/nar-bridge/testdata/emptydirectory.nar
new file mode 100644
index 0000000000..baba558622
--- /dev/null
+++ b/tvix/nar-bridge/testdata/emptydirectory.nar
Binary files differdiff --git a/tvix/nar-bridge/testdata/nar_1094wph9z4nwlgvsd53abfz8i117ykiv5dwnq9nnhz846s7xqd7d.nar b/tvix/nar-bridge/testdata/nar_1094wph9z4nwlgvsd53abfz8i117ykiv5dwnq9nnhz846s7xqd7d.nar
new file mode 100644
index 0000000000..6cb0b16e5d
--- /dev/null
+++ b/tvix/nar-bridge/testdata/nar_1094wph9z4nwlgvsd53abfz8i117ykiv5dwnq9nnhz846s7xqd7d.nar
Binary files differdiff --git a/tvix/nar-bridge/testdata/onebyteexecutable.nar b/tvix/nar-bridge/testdata/onebyteexecutable.nar
new file mode 100644
index 0000000000..6868219666
--- /dev/null
+++ b/tvix/nar-bridge/testdata/onebyteexecutable.nar
Binary files differdiff --git a/tvix/nar-bridge/testdata/onebyteregular.nar b/tvix/nar-bridge/testdata/onebyteregular.nar
new file mode 100644
index 0000000000..b8c94932bf
--- /dev/null
+++ b/tvix/nar-bridge/testdata/onebyteregular.nar
Binary files differdiff --git a/tvix/nar-bridge/testdata/popdirectories.nar b/tvix/nar-bridge/testdata/popdirectories.nar
new file mode 100644
index 0000000000..74313aca52
--- /dev/null
+++ b/tvix/nar-bridge/testdata/popdirectories.nar
Binary files differdiff --git a/tvix/nar-bridge/testdata/symlink.nar b/tvix/nar-bridge/testdata/symlink.nar
new file mode 100644
index 0000000000..7990e4ad5b
--- /dev/null
+++ b/tvix/nar-bridge/testdata/symlink.nar
Binary files differdiff --git a/tvix/nix-compat/Cargo.toml b/tvix/nix-compat/Cargo.toml
new file mode 100644
index 0000000000..876ac3ecad
--- /dev/null
+++ b/tvix/nix-compat/Cargo.toml
@@ -0,0 +1,56 @@
+[package]
+name = "nix-compat"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+# async NAR writer
+async = ["tokio"]
+# code emitting low-level packets used in the daemon protocol.
+wire = ["tokio", "pin-project-lite"]
+
+# Enable all features by default.
+default = ["async", "wire"]
+
+[dependencies]
+bitflags = "2.4.1"
+bstr = { version = "1.6.0", features = ["alloc", "unicode", "serde"] }
+data-encoding = "2.3.3"
+ed25519 = "2.2.3"
+ed25519-dalek = "2.1.0"
+enum-primitive-derive = "0.3.0"
+glob = "0.3.0"
+nom = "7.1.3"
+num-traits = "0.2.18"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+sha2 = "0.10.6"
+thiserror = "1.0.38"
+
+[dependencies.tokio]
+optional = true
+version = "1.32.0"
+features = ["io-util", "macros"]
+
+[dependencies.pin-project-lite]
+optional = true
+version = "0.2.13"
+
+[dev-dependencies]
+criterion = { version = "0.5", features = ["html_reports"] }
+futures = { version = "0.3.30", default-features = false, features = ["executor"] }
+hex-literal = "0.4.1"
+lazy_static = "1.4.0"
+pretty_assertions = "1.4.0"
+rstest = "0.19.0"
+serde_json = "1.0"
+tokio-test = "0.4.3"
+zstd = "^0.13.0"
+
+[[bench]]
+name = "derivation_parse_aterm"
+harness = false
+
+[[bench]]
+name = "narinfo_parse"
+harness = false
diff --git a/tvix/nix-compat/benches/derivation_parse_aterm.rs b/tvix/nix-compat/benches/derivation_parse_aterm.rs
new file mode 100644
index 0000000000..4ace7d4480
--- /dev/null
+++ b/tvix/nix-compat/benches/derivation_parse_aterm.rs
@@ -0,0 +1,31 @@
+use std::path::Path;
+
+use criterion::{black_box, criterion_group, criterion_main, Criterion};
+use nix_compat::derivation::Derivation;
+
+const RESOURCES_PATHS: &str = "src/derivation/tests/derivation_tests/ok";
+
+fn bench_aterm_parser(c: &mut Criterion) {
+    for drv in [
+        "0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv",
+        "292w8yzv5nn7nhdpxcs8b7vby2p27s09-nested-json.drv",
+        "4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv",
+        "52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv",
+        "9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv",
+        "ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv",
+        "h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv",
+        "m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv",
+        "ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv",
+        "x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv",
+    ] {
+        let drv_path = Path::new(RESOURCES_PATHS).join(drv);
+        let drv_bytes = &std::fs::read(drv_path).unwrap();
+
+        c.bench_function(drv, |b| {
+            b.iter(|| Derivation::from_aterm_bytes(black_box(drv_bytes)))
+        });
+    }
+}
+
+criterion_group!(benches, bench_aterm_parser);
+criterion_main!(benches);
diff --git a/tvix/nix-compat/benches/narinfo_parse.rs b/tvix/nix-compat/benches/narinfo_parse.rs
new file mode 100644
index 0000000000..7ffd24d12b
--- /dev/null
+++ b/tvix/nix-compat/benches/narinfo_parse.rs
@@ -0,0 +1,69 @@
+use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
+use lazy_static::lazy_static;
+use nix_compat::narinfo::NarInfo;
+use std::{io, str};
+
+const SAMPLE: &str = r#"StorePath: /nix/store/1pajsq519irjy86vli20bgq1wr1q3pny-banking-0.3.0
+URL: nar/0rdn027rxqbl42bv9jxhsipgq2hwqdapvwmdzligmzdmz2p9vybs.nar.xz
+Compression: xz
+FileHash: sha256:0rdn027rxqbl42bv9jxhsipgq2hwqdapvwmdzligmzdmz2p9vybs
+FileSize: 92828
+NarHash: sha256:0cfnydzp132y69bh20dj76yfd6hc3qdyblbwr9hwn59vfmnb09m0
+NarSize: 173352
+References: 03d4ncyfh76mgs6sfayl8l6zzdhm219w-python3.9-mt-940-4.23.0 0rhbw783qcjxv3cqln1760i1lmz2yb67-gsettings-desktop-schemas-41.0 1dm9ndgg56ylawpcbdzkhl03fg6777rr-python3.9-six-1.16.0 1pajsq519irjy86vli20bgq1wr1q3pny-banking-0.3.0 2ccy5zc89zpc2aznqxgvzp4wm1bwj05n-bzip2-1.0.6.0.2-bin 32gy3pqk4n725lscdm622yzsg9np3xvs-python3.9-cryptography-36.0.0-dev 35chvqbr7vp9icdki0132fc6np09vrx5-python3.9-bleach-4.1.0 53abh5cz9zi4yh75lfzg99xqy0fdgj4i-python3.9-xmlschema-1.9.2 5p96sifyavb407mnharhyzlw6pn6km1b-glib-2.70.2-bin 6hil8z0zkqcgvaw1qwjyqa8qyaa1lm3k-python3.9-pycairo-1.20.1 803ffb21rv4af521pplb72zjm1ygm9kk-python3.9-pyparsing-2.4.7 al95l8psvmq5di3vdwa75n8w2m0sj2sy-gdk-pixbuf-2.42.6 b09371lq1jjrv43h8jpp82v23igndsn2-python3.9-fints-3.0.1 b53hk557pdk5mq4lv1zrh71a54qazbsm-python3.9-certifi-2021.10.08 bl0cwvwgch92cfsnli4dsah2gxgdickp-gtk+3-3.24.30 cfkq9wi7ypqk26c75dzic5v3nxlzyi58-python3.9-cryptography-36.0.0 cyhg57whqvrx7xf7fvn70dr5836y7zak-python3.9-sepaxml-2.4.1 d810g729g1c4lvp3nv1n3ah6cvpwg7by-cairo-1.16.0-dev dn4fwp0yx6nsa85cr20cwvdmg64xwmcy-python3-3.9.9 dzsj2n0nmq8nv6w0hvy5vb61kim3rzmd-pango-1.50.0 fs6rcnhbjvpxsyw5qiq0q7jx378fjrq7-python3.9-webencodings-0.5.1 g08sxarx191yh2dh0yk2j8icja54aksf-harfbuzz-3.1.2 glanz2lv7m6ak8pql0jcpr3izyp5cxm5-python3.9-pycparser-2.21 gpzx6h0dp5yhcvkfj68zs444ghll7dzm-python3.9-html5lib-1.1 gxyhqkpahahn4h8wbanzfhr1zkxbysid-expat-2.4.2-dev gy3pnc7bpff1h4ylhrivs4cjlvmxl0dk-python3.9-packaging-20.9 hhpqldw0552mf4mjdm2q7zqwy9hpfchd-libpng-apng-1.6.37-dev ig2bdwmplvs6dyg07fdyh006ha768jh1-python3.9-cffi-1.15.0 ij5rm5y6lmqzrwqd1zxckhbii3dg2nq5-glib-2.70.2-dev j5raylzz6fsafbgayyfaydadjl0x22s0-freetype-2.11.1-dev j6w2fbsl49jska4scyr860gz4df9biha-gobject-introspection-1.70.0 jfc99f1hrca6ih6h0n4ax431hjlx96j0-python3.9-brotli-1.0.9 kbazcxnki2qz514rl1plhsj3587hl8bb-python3.9-pysocks-1.7.1 kkljrrrj80fnz59qyfgnv6wvv0cbmpql-libhandy-1.5.0 l82il2lbp757c0smi81qmj4crlcmdz9s-python3.9-pygobject-3.42.0-dev m4zflhr10wz4frhgxqfi43rwvapki1pi-fontconfig-2.13.94-bin mbsc1c7mq15vgfzcdma9fglczih9ncfy-python3.9-chardet-4.0.0 mfvaaf4illpwrflg30cij5x4rncp9jin-python3.9-text-unidecode-1.3 msiv2nkdcaf4gvaf2cfnxcjm66j8mjxz-python3.9-elementpath-2.4.0 nmwapds8fcx22vd30d81va7a7a51ywwx-gettext-0.21 pbfraw351mksnkp2ni9c4rkc9cpp89iv-bash-5.1-p12 r8cbf18vrd54rb4psf3m4zlk5sd2jsv3-python3.9-pygobject-3.42.0 rig6npd9sd45ashf6fxcwgxzm7m4p0l3-python3.9-requests-2.26.0 ryj72ashr27gf4kh0ssgi3zpiv8fxw53-librsvg-2.52.4 s2jjq7rk5yrzlv9lyralzvpixg4p6jh3-atk-2.36.0 w1lsr2i37fr0mp1jya04nwa5nf5dxm2n-python3.9-setuptools-57.2.0 whfykra99ahs814l5hp3q5ps8rwzsf3s-python3.9-brotlicffi-1.0.9.2 wqdmghdvc4s95jgpp13fj5v3xar8mlks-python3.9-charset-normalizer-2.0.8 x1ha2nyji1px0iqknbyhdnvw4icw5h3i-python3.9-idna-3.3 z9560qb4ygbi0352m9pglwhi332cxb1f-python3.9-urllib3-1.26.7
+Deriver: 2ch8jx910qk6721mp4yqsmvdfgj5c8ir-banking-0.3.0.drv
+Sig: cache.nixos.org-1:xcL67rBZPcdVZudDLpLeddkBa0KaFTw5A0udnaa0axysjrQ6Nvd9p3BLZ4rhKgl52/cKiU3c6aq60L8+IcE5Dw==
+"#;
+
+lazy_static! {
+    static ref CASES: &'static [&'static str] = {
+        let data =
+            zstd::decode_all(io::Cursor::new(include_bytes!("../testdata/narinfo.zst"))).unwrap();
+        let data = str::from_utf8(Vec::leak(data)).unwrap();
+        Vec::leak(
+            data.split_inclusive("\n\n")
+                .map(|s| s.strip_suffix('\n').unwrap())
+                .collect::<Vec<_>>(),
+        )
+    };
+}
+
+pub fn parse(c: &mut Criterion) {
+    let mut g = c.benchmark_group("parse");
+
+    {
+        g.throughput(Throughput::Bytes(SAMPLE.len() as u64));
+        g.bench_with_input("single", SAMPLE, |b, data| {
+            b.iter(|| {
+                black_box(NarInfo::parse(black_box(data)).ok().unwrap());
+            });
+        });
+    }
+
+    {
+        for &case in *CASES {
+            NarInfo::parse(case).expect("should parse");
+        }
+
+        g.throughput(Throughput::Bytes(
+            CASES.iter().map(|s| s.len() as u64).sum(),
+        ));
+        g.bench_with_input("many", &*CASES, |b, data| {
+            let mut vec = vec![];
+            b.iter(|| {
+                vec.clear();
+                vec.extend(
+                    black_box(data)
+                        .iter()
+                        .map(|s| NarInfo::parse(s).ok().unwrap()),
+                );
+                black_box(&vec);
+            });
+        });
+    }
+
+    g.finish();
+}
+
+criterion_group!(benches, parse);
+criterion_main!(benches);
diff --git a/tvix/nix-compat/default.nix b/tvix/nix-compat/default.nix
new file mode 100644
index 0000000000..9df76e12fc
--- /dev/null
+++ b/tvix/nix-compat/default.nix
@@ -0,0 +1,7 @@
+{ depot, ... }:
+
+depot.tvix.crates.workspaceMembers.nix-compat.build.override {
+  runTests = true;
+  # make sure we also enable async here, so run the tests behind that feature flag.
+  features = [ "default" "async" "wire" ];
+}
diff --git a/tvix/nix-compat/src/aterm/escape.rs b/tvix/nix-compat/src/aterm/escape.rs
new file mode 100644
index 0000000000..80a85d2103
--- /dev/null
+++ b/tvix/nix-compat/src/aterm/escape.rs
@@ -0,0 +1,28 @@
+use bstr::ByteSlice;
+
+/// Escapes a byte sequence. Does not add surrounding quotes.
+pub fn escape_bytes<P: AsRef<[u8]>>(s: P) -> Vec<u8> {
+    let mut s: Vec<u8> = s.as_ref().to_vec();
+
+    s = s.replace(b"\\", b"\\\\");
+    s = s.replace(b"\n", b"\\n");
+    s = s.replace(b"\r", b"\\r");
+    s = s.replace(b"\t", b"\\t");
+    s = s.replace(b"\"", b"\\\"");
+
+    s
+}
+
+#[cfg(test)]
+mod tests {
+    use super::escape_bytes;
+    use rstest::rstest;
+
+    #[rstest]
+    #[case::empty(b"", b"")]
+    #[case::doublequote(b"\"", b"\\\"")]
+    #[case::colon(b":", b":")]
+    fn escape(#[case] input: &[u8], #[case] expected: &[u8]) {
+        assert_eq!(expected, escape_bytes(input))
+    }
+}
diff --git a/tvix/nix-compat/src/aterm/mod.rs b/tvix/nix-compat/src/aterm/mod.rs
new file mode 100644
index 0000000000..8806b6caf2
--- /dev/null
+++ b/tvix/nix-compat/src/aterm/mod.rs
@@ -0,0 +1,7 @@
+mod escape;
+mod parser;
+
+pub(crate) use escape::escape_bytes;
+pub(crate) use parser::parse_bstr_field;
+pub(crate) use parser::parse_str_list;
+pub(crate) use parser::parse_string_field;
diff --git a/tvix/nix-compat/src/aterm/parser.rs b/tvix/nix-compat/src/aterm/parser.rs
new file mode 100644
index 0000000000..a30cb40ab0
--- /dev/null
+++ b/tvix/nix-compat/src/aterm/parser.rs
@@ -0,0 +1,125 @@
+//! This module implements parsing code for some basic building blocks
+//! of the [ATerm][] format, which is used by C++ Nix to serialize Derivations.
+//!
+//! [ATerm]: http://program-transformation.org/Tools/ATermFormat.html
+use bstr::BString;
+use nom::branch::alt;
+use nom::bytes::complete::{escaped_transform, is_not, tag};
+use nom::character::complete::char as nomchar;
+use nom::combinator::{map, value};
+use nom::multi::separated_list0;
+use nom::sequence::delimited;
+use nom::IResult;
+
+/// Parse a bstr and undo any escaping.
+fn parse_escaped_bstr(i: &[u8]) -> IResult<&[u8], BString> {
+    escaped_transform(
+        is_not("\"\\"),
+        '\\',
+        alt((
+            value("\\".as_bytes(), nomchar('\\')),
+            value("\n".as_bytes(), nomchar('n')),
+            value("\t".as_bytes(), nomchar('t')),
+            value("\r".as_bytes(), nomchar('r')),
+            value("\"".as_bytes(), nomchar('\"')),
+        )),
+    )(i)
+    .map(|(i, v)| (i, BString::new(v)))
+}
+
+/// Parse a field in double quotes, undo any escaping, and return the unquoted
+/// and decoded `Vec<u8>`.
+pub(crate) fn parse_bstr_field(i: &[u8]) -> IResult<&[u8], BString> {
+    // inside double quotes…
+    delimited(
+        nomchar('\"'),
+        // There is
+        alt((
+            // …either is a bstr after unescaping
+            parse_escaped_bstr,
+            // …or an empty string.
+            map(tag(b""), |_| BString::default()),
+        )),
+        nomchar('\"'),
+    )(i)
+}
+
+/// Parse a field in double quotes, undo any escaping, and return the unquoted
+/// and decoded string, if it's a valid string. Or fail parsing if the bytes are
+/// no valid UTF-8.
+pub(crate) fn parse_string_field(i: &[u8]) -> IResult<&[u8], String> {
+    // inside double quotes…
+    delimited(
+        nomchar('\"'),
+        // There is
+        alt((
+            // either is a String after unescaping
+            nom::combinator::map_opt(parse_escaped_bstr, |escaped_bstr| {
+                String::from_utf8(escaped_bstr.into()).ok()
+            }),
+            // or an empty string.
+            map(tag(b""), |_| String::new()),
+        )),
+        nomchar('\"'),
+    )(i)
+}
+
+/// Parse a list of of string fields (enclosed in brackets)
+pub(crate) fn parse_str_list(i: &[u8]) -> IResult<&[u8], Vec<String>> {
+    // inside brackets
+    delimited(
+        nomchar('['),
+        separated_list0(nomchar(','), parse_string_field),
+        nomchar(']'),
+    )(i)
+}
+
+#[cfg(test)]
+mod tests {
+    use rstest::rstest;
+
+    #[rstest]
+    #[case::empty(br#""""#, b"", b"")]
+    #[case::hello_world(br#""Hello World""#, b"Hello World", b"")]
+    #[case::doublequote(br#""\"""#, br#"""#, b"")]
+    #[case::colon(br#"":""#, b":", b"")]
+    #[case::doublequote_rest(br#""\""Rest"#, br#"""#, b"Rest")]
+    fn test_parse_bstr_field(
+        #[case] input: &[u8],
+        #[case] expected: &[u8],
+        #[case] exp_rest: &[u8],
+    ) {
+        let (rest, parsed) = super::parse_bstr_field(input).expect("must parse");
+        assert_eq!(exp_rest, rest, "expected remainder");
+        assert_eq!(expected, parsed);
+    }
+
+    #[rstest]
+    #[case::empty(br#""""#, "", b"")]
+    #[case::hello_world(br#""Hello World""#, "Hello World", b"")]
+    #[case::doublequote(br#""\"""#, r#"""#, b"")]
+    #[case::colon(br#"":""#, ":", b"")]
+    #[case::doublequote_rest(br#""\""Rest"#, r#"""#, b"Rest")]
+    fn parse_string_field(#[case] input: &[u8], #[case] expected: &str, #[case] exp_rest: &[u8]) {
+        let (rest, parsed) = super::parse_string_field(input).expect("must parse");
+        assert_eq!(exp_rest, rest, "expected remainder");
+        assert_eq!(expected, &parsed);
+    }
+
+    #[test]
+    fn parse_string_field_invalid_encoding_fail() {
+        let input: Vec<_> = vec![b'"', 0xc5, 0xc4, 0xd6, b'"'];
+
+        super::parse_string_field(&input).expect_err("must fail");
+    }
+
+    #[rstest]
+    #[case::single_foo(br#"["foo"]"#, vec!["foo".to_string()], b"")]
+    #[case::empty_list(b"[]", vec![], b"")]
+    #[case::empty_list_with_rest(b"[]blub", vec![], b"blub")]
+    fn parse_list(#[case] input: &[u8], #[case] expected: Vec<String>, #[case] exp_rest: &[u8]) {
+        let (rest, parsed) = super::parse_str_list(input).expect("must parse");
+        assert_eq!(exp_rest, rest, "expected remainder");
+        assert_eq!(expected, parsed);
+    }
+}
diff --git a/tvix/nix-compat/src/bin/drvfmt.rs b/tvix/nix-compat/src/bin/drvfmt.rs
new file mode 100644
index 0000000000..ddc1f0389f
--- /dev/null
+++ b/tvix/nix-compat/src/bin/drvfmt.rs
@@ -0,0 +1,42 @@
+use std::{collections::BTreeMap, io::Read};
+
+use nix_compat::derivation::Derivation;
+use serde_json::json;
+
+/// construct a serde_json::Value from a Derivation.
+/// Some environment values can be non-valid UTF-8 strings.
+/// `serde_json` prints them out really unreadable.
+/// This is a tool to print A-Terms in a more readable fashion, so we brutally
+/// use the [std::string::ToString] implementation of [bstr::BString] to get
+/// a UTF-8 string (replacing invalid characters with the Unicode replacement
+/// codepoint).
+fn build_serde_json_value(drv: Derivation) -> serde_json::Value {
+    json!({
+        "args": drv.arguments,
+        "builder": drv.builder,
+        "env":   drv.environment.into_iter().map(|(k,v)| (k, v.to_string())).collect::<BTreeMap<String, String>>(),
+        "inputDrvs": drv.input_derivations,
+        "inputSrcs": drv.input_sources,
+        "outputs": drv.outputs,
+        "system": drv.system,
+    })
+}
+
+fn main() {
+    // read A-Term from stdin
+    let mut buf = Vec::new();
+    std::io::stdin()
+        .read_to_end(&mut buf)
+        .expect("failed to read from stdin");
+
+    match Derivation::from_aterm_bytes(&buf) {
+        Ok(drv) => {
+            println!(
+                "{}",
+                serde_json::to_string_pretty(&build_serde_json_value(drv))
+                    .expect("unable to serialize")
+            );
+        }
+        Err(e) => eprintln!("unable to parse derivation: {:#?}", e),
+    }
+}
diff --git a/tvix/nix-compat/src/derivation/errors.rs b/tvix/nix-compat/src/derivation/errors.rs
new file mode 100644
index 0000000000..452231f19d
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/errors.rs
@@ -0,0 +1,60 @@
+//! Contains [DerivationError], exported as [crate::derivation::DerivationError]
+use crate::store_path;
+use thiserror::Error;
+
+use super::CAHash;
+
+/// Errors that can occur during the validation of Derivation structs.
+#[derive(Debug, Error, PartialEq)]
+pub enum DerivationError {
+    // outputs
+    #[error("no outputs defined")]
+    NoOutputs(),
+    #[error("invalid output name: {0}")]
+    InvalidOutputName(String),
+    #[error("encountered fixed-output derivation, but more than 1 output in total")]
+    MoreThanOneOutputButFixed(),
+    #[error("invalid output name for fixed-output derivation: {0}")]
+    InvalidOutputNameForFixed(String),
+    #[error("unable to validate output {0}: {1}")]
+    InvalidOutput(String, OutputError),
+    #[error("unable to validate output {0}: {1}")]
+    InvalidOutputDerivationPath(String, store_path::BuildStorePathError),
+    // input derivation
+    #[error("unable to parse input derivation path {0}: {1}")]
+    InvalidInputDerivationPath(String, store_path::Error),
+    #[error("input derivation {0} doesn't end with .drv")]
+    InvalidInputDerivationPrefix(String),
+    #[error("input derivation {0} output names are empty")]
+    EmptyInputDerivationOutputNames(String),
+    #[error("input derivation {0} output name {1} is invalid")]
+    InvalidInputDerivationOutputName(String, String),
+
+    // input sources
+    #[error("unable to parse input sources path {0}: {1}")]
+    InvalidInputSourcesPath(String, store_path::Error),
+
+    // platform
+    #[error("invalid platform field: {0}")]
+    InvalidPlatform(String),
+
+    // builder
+    #[error("invalid builder field: {0}")]
+    InvalidBuilder(String),
+
+    // environment
+    #[error("invalid environment key {0}")]
+    InvalidEnvironmentKey(String),
+}
+
+/// Errors that can occur during the validation of a specific
+// [crate::derivation::Output] of a [crate::derivation::Derviation].
+#[derive(Debug, Error, PartialEq)]
+pub enum OutputError {
+    #[error("Invalid output path {0}: {1}")]
+    InvalidOutputPath(String, store_path::Error),
+    #[error("Missing output path")]
+    MissingOutputPath,
+    #[error("Invalid CAHash: {:?}", .0)]
+    InvalidCAHash(CAHash),
+}
diff --git a/tvix/nix-compat/src/derivation/mod.rs b/tvix/nix-compat/src/derivation/mod.rs
new file mode 100644
index 0000000000..6e12e3ea86
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/mod.rs
@@ -0,0 +1,305 @@
+use crate::store_path::{
+    self, build_ca_path, build_output_path, build_text_path, StorePath, StorePathRef,
+};
+use bstr::BString;
+use serde::{Deserialize, Serialize};
+use sha2::{Digest, Sha256};
+use std::collections::{BTreeMap, BTreeSet};
+use std::io;
+
+mod errors;
+mod output;
+mod parse_error;
+mod parser;
+mod validate;
+mod write;
+
+#[cfg(test)]
+mod tests;
+
+// Public API of the crate.
+pub use crate::nixhash::{CAHash, NixHash};
+pub use errors::{DerivationError, OutputError};
+pub use output::Output;
+
+use self::write::AtermWriteable;
+
+#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
+pub struct Derivation {
+    #[serde(rename = "args")]
+    pub arguments: Vec<String>,
+
+    pub builder: String,
+
+    #[serde(rename = "env")]
+    pub environment: BTreeMap<String, BString>,
+
+    /// Map from drv path to output names used from this derivation.
+    #[serde(rename = "inputDrvs")]
+    pub input_derivations: BTreeMap<StorePath, BTreeSet<String>>,
+
+    /// Plain store paths of additional inputs.
+    #[serde(rename = "inputSrcs")]
+    pub input_sources: BTreeSet<StorePath>,
+
+    /// Maps output names to Output.
+    pub outputs: BTreeMap<String, Output>,
+
+    pub system: String,
+}
+
+impl Derivation {
+    /// write the Derivation to the given [std::io::Write], in ATerm format.
+    ///
+    /// The only errors returns are these when writing to the passed writer.
+    pub fn serialize(&self, writer: &mut impl std::io::Write) -> Result<(), io::Error> {
+        self.serialize_with_replacements(writer, &self.input_derivations)
+    }
+
+    /// Like `serialize` but allow replacing the input_derivations for hash calculations.
+    fn serialize_with_replacements(
+        &self,
+        writer: &mut impl std::io::Write,
+        input_derivations: &BTreeMap<impl AtermWriteable, BTreeSet<String>>,
+    ) -> Result<(), io::Error> {
+        use write::*;
+
+        writer.write_all(write::DERIVATION_PREFIX.as_bytes())?;
+        write_char(writer, write::PAREN_OPEN)?;
+
+        write_outputs(writer, &self.outputs)?;
+        write_char(writer, COMMA)?;
+
+        write_input_derivations(writer, input_derivations)?;
+        write_char(writer, COMMA)?;
+
+        write_input_sources(writer, &self.input_sources)?;
+        write_char(writer, COMMA)?;
+
+        write_system(writer, &self.system)?;
+        write_char(writer, COMMA)?;
+
+        write_builder(writer, &self.builder)?;
+        write_char(writer, COMMA)?;
+
+        write_arguments(writer, &self.arguments)?;
+        write_char(writer, COMMA)?;
+
+        write_environment(writer, &self.environment)?;
+
+        write_char(writer, PAREN_CLOSE)?;
+
+        Ok(())
+    }
+
+    /// return the ATerm serialization.
+    pub fn to_aterm_bytes(&self) -> Vec<u8> {
+        self.to_aterm_bytes_with_replacements(&self.input_derivations)
+    }
+
+    /// Like `to_aterm_bytes`, but accept a different BTreeMap for input_derivations.
+    /// This is used to render the ATerm representation of a Derivation "modulo
+    /// fixed-output derivations".
+    fn to_aterm_bytes_with_replacements(
+        &self,
+        input_derivations: &BTreeMap<impl AtermWriteable, BTreeSet<String>>,
+    ) -> Vec<u8> {
+        let mut buffer: Vec<u8> = Vec::new();
+
+        // invoke serialize and write to the buffer.
+        // Note we only propagate errors writing to the writer in serialize,
+        // which won't panic for the string we write to.
+        self.serialize_with_replacements(&mut buffer, input_derivations)
+            .unwrap();
+
+        buffer
+    }
+
+    /// Parse an Derivation in ATerm serialization, and validate it passes our
+    /// set of validations.
+    pub fn from_aterm_bytes(b: &[u8]) -> Result<Derivation, parser::Error<&[u8]>> {
+        parser::parse(b)
+    }
+
+    /// Returns the drv path of a [Derivation] struct.
+    ///
+    /// The drv path is calculated by invoking [build_text_path], using
+    /// the `name` with a `.drv` suffix as name, all [Derivation::input_sources] and
+    /// keys of [Derivation::input_derivations] as references, and the ATerm string of
+    /// the [Derivation] as content.
+    pub fn calculate_derivation_path(&self, name: &str) -> Result<StorePath, DerivationError> {
+        // append .drv to the name
+        let name = &format!("{}.drv", name);
+
+        // collect the list of paths from input_sources and input_derivations
+        // into a (sorted, guaranteed by BTreeSet) list of references
+        let references: BTreeSet<String> = self
+            .input_sources
+            .iter()
+            .chain(self.input_derivations.keys())
+            .map(StorePath::to_absolute_path)
+            .collect();
+
+        build_text_path(name, self.to_aterm_bytes(), references)
+            .map(|s| s.to_owned())
+            .map_err(|_e| DerivationError::InvalidOutputName(name.to_string()))
+    }
+
+    /// Returns the FOD digest, if the derivation is fixed-output, or None if
+    /// it's not.
+    /// TODO: this is kinda the string from [build_ca_path] with a
+    /// [CAHash::Flat], what's fed to `build_store_path_from_fingerprint_parts`
+    /// (except the out_output.path being an empty string)
+    pub fn fod_digest(&self) -> Option<[u8; 32]> {
+        if self.outputs.len() != 1 {
+            return None;
+        }
+
+        let out_output = self.outputs.get("out")?;
+        let ca_hash = &out_output.ca_hash.as_ref()?;
+
+        Some(
+            Sha256::new_with_prefix(format!(
+                "fixed:out:{}{}:{}",
+                ca_kind_prefix(ca_hash),
+                ca_hash.hash().to_nix_hex_string(),
+                out_output
+                    .path
+                    .as_ref()
+                    .map(StorePath::to_absolute_path)
+                    .as_ref()
+                    .map(|s| s as &str)
+                    .unwrap_or(""),
+            ))
+            .finalize()
+            .into(),
+        )
+    }
+
+    /// Calculates the hash of a derivation modulo fixed-output subderivations.
+    ///
+    /// This is called `hashDerivationModulo` in nixcpp.
+    ///
+    /// It returns the sha256 digest of the derivation ATerm representation,
+    /// except that:
+    ///  -  any input derivation paths have beed replaced "by the result of a
+    ///     recursive call to this function" and that
+    ///  - for fixed-output derivations the special
+    ///    `fixed:out:${algo}:${digest}:${fodPath}` string is hashed instead of
+    ///    the A-Term.
+    ///
+    /// It's up to the caller of this function to provide a (infallible) lookup
+    /// function to query [hash_derivation_modulo] of direct input derivations,
+    /// by their [StorePathRef].
+    /// It will only be called in case the derivation is not a fixed-output
+    /// derivation.
+    pub fn hash_derivation_modulo<F>(&self, fn_lookup_hash_derivation_modulo: F) -> [u8; 32]
+    where
+        F: Fn(&StorePathRef) -> [u8; 32],
+    {
+        // Fixed-output derivations return a fixed hash.
+        // Non-Fixed-output derivations return the sha256 digest of the ATerm
+        // notation, but with all input_derivation paths replaced by a recursive
+        // call to this function.
+        // We call [fn_lookup_hash_derivation_modulo] rather than recursing
+        // ourselves, so callers can precompute this.
+        self.fod_digest().unwrap_or({
+            // For each input_derivation, look up the hash derivation modulo,
+            // and replace the derivation path in the aterm with it's HEXLOWER digest.
+            let aterm_bytes = self.to_aterm_bytes_with_replacements(&BTreeMap::from_iter(
+                self.input_derivations
+                    .iter()
+                    .map(|(drv_path, output_names)| {
+                        let hash = fn_lookup_hash_derivation_modulo(&drv_path.into());
+
+                        (hash, output_names.to_owned())
+                    }),
+            ));
+
+            // write the ATerm of that to the hash function and return its digest.
+            Sha256::new_with_prefix(aterm_bytes).finalize().into()
+        })
+    }
+
+    /// This calculates all output paths of a Derivation and updates the struct.
+    /// It requires the struct to be initially without output paths.
+    /// This means, self.outputs[$outputName].path needs to be an empty string,
+    /// and self.environment[$outputName] needs to be an empty string.
+    ///
+    /// Output path calculation requires knowledge of the
+    /// [hash_derivation_modulo], which (in case of non-fixed-output
+    /// derivations) also requires knowledge of the [hash_derivation_modulo] of
+    /// input derivations (recursively).
+    ///
+    /// To avoid recursing and doing unnecessary calculation, we simply
+    /// ask the caller of this function to provide the result of the
+    /// [hash_derivation_modulo] call of the current [Derivation],
+    /// and leave it up to them to calculate it when needed.
+    ///
+    /// On completion, `self.environment[$outputName]` and
+    /// `self.outputs[$outputName].path` are set to the calculated output path for all
+    /// outputs.
+    pub fn calculate_output_paths(
+        &mut self,
+        name: &str,
+        hash_derivation_modulo: &[u8; 32],
+    ) -> Result<(), DerivationError> {
+        // The fingerprint and hash differs per output
+        for (output_name, output) in self.outputs.iter_mut() {
+            // Assert that outputs are not yet populated, to avoid using this function wrongly.
+            // We don't also go over self.environment, but it's a sufficient
+            // footgun prevention mechanism.
+            assert!(output.path.is_none());
+
+            let path_name = output_path_name(name, output_name);
+
+            // For fixed output derivation we use [build_ca_path], otherwise we
+            // use [build_output_path] with [hash_derivation_modulo].
+            let abs_store_path = if let Some(ref hwm) = output.ca_hash {
+                build_ca_path(&path_name, hwm, Vec::<String>::new(), false).map_err(|e| {
+                    DerivationError::InvalidOutputDerivationPath(output_name.to_string(), e)
+                })?
+            } else {
+                build_output_path(hash_derivation_modulo, output_name, &path_name).map_err(|e| {
+                    DerivationError::InvalidOutputDerivationPath(
+                        output_name.to_string(),
+                        store_path::BuildStorePathError::InvalidStorePath(e),
+                    )
+                })?
+            };
+
+            output.path = Some(abs_store_path.to_owned());
+            self.environment.insert(
+                output_name.to_string(),
+                abs_store_path.to_absolute_path().into(),
+            );
+        }
+
+        Ok(())
+    }
+}
+
+/// Calculate the name part of the store path of a derivation [Output].
+///
+/// It's the name, and (if it's the non-out output), the output name
+/// after a `-`.
+fn output_path_name(derivation_name: &str, output_name: &str) -> String {
+    let mut output_path_name = derivation_name.to_string();
+    if output_name != "out" {
+        output_path_name.push('-');
+        output_path_name.push_str(output_name);
+    }
+    output_path_name
+}
+
+/// For a [CAHash], return the "prefix" used for NAR purposes.
+/// For [CAHash::Flat], this is an empty string, for [CAHash::Nar], it's "r:".
+/// Panics for other [CAHash] kinds, as they're not valid in a derivation
+/// context.
+fn ca_kind_prefix(ca_hash: &CAHash) -> &'static str {
+    match ca_hash {
+        CAHash::Flat(_) => "",
+        CAHash::Nar(_) => "r:",
+        _ => panic!("invalid ca hash in derivation context: {:?}", ca_hash),
+    }
+}
diff --git a/tvix/nix-compat/src/derivation/output.rs b/tvix/nix-compat/src/derivation/output.rs
new file mode 100644
index 0000000000..266617f587
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/output.rs
@@ -0,0 +1,189 @@
+use crate::nixhash::CAHash;
+use crate::store_path::StorePathRef;
+use crate::{derivation::OutputError, store_path::StorePath};
+use serde::de::Unexpected;
+use serde::{Deserialize, Serialize};
+use serde_json::Map;
+use std::borrow::Cow;
+
+/// References the derivation output.
+#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
+pub struct Output {
+    /// Store path of build result.
+    pub path: Option<StorePath>,
+
+    #[serde(flatten)]
+    pub ca_hash: Option<CAHash>, // we can only represent a subset here.
+}
+
+impl<'de> Deserialize<'de> for Output {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let fields = Map::deserialize(deserializer)?;
+        let path: &str = fields
+            .get("path")
+            .ok_or(serde::de::Error::missing_field(
+                "`path` is missing but required for outputs",
+            ))?
+            .as_str()
+            .ok_or(serde::de::Error::invalid_type(
+                serde::de::Unexpected::Other("certainly not a string"),
+                &"a string",
+            ))?;
+
+        let path = StorePathRef::from_absolute_path(path.as_bytes())
+            .map_err(|_| serde::de::Error::invalid_value(Unexpected::Str(path), &"StorePath"))?;
+        Ok(Self {
+            path: Some(path.to_owned()),
+            ca_hash: CAHash::from_map::<D>(&fields)?,
+        })
+    }
+}
+
+impl Output {
+    pub fn is_fixed(&self) -> bool {
+        self.ca_hash.is_some()
+    }
+
+    /// The output path as a string -- use `""` to indicate an unset output path.
+    pub fn path_str(&self) -> Cow<str> {
+        match &self.path {
+            None => Cow::Borrowed(""),
+            Some(path) => Cow::Owned(path.to_absolute_path()),
+        }
+    }
+
+    pub fn validate(&self, validate_output_paths: bool) -> Result<(), OutputError> {
+        if let Some(fixed_output_hash) = &self.ca_hash {
+            match fixed_output_hash {
+                CAHash::Flat(_) | CAHash::Nar(_) => {
+                    // all hashes allowed for Flat, and Nar.
+                }
+                _ => return Err(OutputError::InvalidCAHash(fixed_output_hash.clone())),
+            }
+        }
+
+        if validate_output_paths && self.path.is_none() {
+            return Err(OutputError::MissingOutputPath);
+        }
+        Ok(())
+    }
+}
+
+/// This ensures that a potentially valid input addressed
+/// output is deserialized as a non-fixed output.
+#[test]
+fn deserialize_valid_input_addressed_output() {
+    let json_bytes = r#"
+    {
+      "path": "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432"
+    }"#;
+    let output: Output = serde_json::from_str(json_bytes).expect("must parse");
+
+    assert!(!output.is_fixed());
+}
+
+/// This ensures that a potentially valid fixed output
+/// output deserializes fine as a fixed output.
+#[test]
+fn deserialize_valid_fixed_output() {
+    let json_bytes = r#"
+    {
+        "path": "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+        "hash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+        "hashAlgo": "r:sha256"
+    }"#;
+    let output: Output = serde_json::from_str(json_bytes).expect("must parse");
+
+    assert!(output.is_fixed());
+}
+
+/// This ensures that parsing an input with the invalid hash encoding
+/// will result in a parsing failure.
+#[test]
+fn deserialize_with_error_invalid_hash_encoding_fixed_output() {
+    let json_bytes = r#"
+    {
+        "path": "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+        "hash": "IAMNOTVALIDNIXBASE32",
+        "hashAlgo": "r:sha256"
+    }"#;
+    let output: Result<Output, _> = serde_json::from_str(json_bytes);
+
+    assert!(output.is_err());
+}
+
+/// This ensures that parsing an input with the wrong hash algo
+/// will result in a parsing failure.
+#[test]
+fn deserialize_with_error_invalid_hash_algo_fixed_output() {
+    let json_bytes = r#"
+    {
+        "path": "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+        "hash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+        "hashAlgo": "r:sha1024"
+    }"#;
+    let output: Result<Output, _> = serde_json::from_str(json_bytes);
+
+    assert!(output.is_err());
+}
+
+/// This ensures that parsing an input with the missing hash algo but present hash will result in a
+/// parsing failure.
+#[test]
+fn deserialize_with_error_missing_hash_algo_fixed_output() {
+    let json_bytes = r#"
+    {
+        "path": "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+        "hash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+    }"#;
+    let output: Result<Output, _> = serde_json::from_str(json_bytes);
+
+    assert!(output.is_err());
+}
+
+/// This ensures that parsing an input with the missing hash but present hash algo will result in a
+/// parsing failure.
+#[test]
+fn deserialize_with_error_missing_hash_fixed_output() {
+    let json_bytes = r#"
+    {
+        "path": "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+        "hashAlgo": "r:sha1024"
+    }"#;
+    let output: Result<Output, _> = serde_json::from_str(json_bytes);
+
+    assert!(output.is_err());
+}
+
+#[test]
+fn serialize_deserialize() {
+    let json_bytes = r#"
+    {
+      "path": "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432"
+    }"#;
+    let output: Output = serde_json::from_str(json_bytes).expect("must parse");
+
+    let s = serde_json::to_string(&output).expect("Serialize");
+    let output2: Output = serde_json::from_str(&s).expect("must parse again");
+
+    assert_eq!(output, output2);
+}
+
+#[test]
+fn serialize_deserialize_fixed() {
+    let json_bytes = r#"
+    {
+        "path": "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+        "hash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+        "hashAlgo": "r:sha256"
+    }"#;
+    let output: Output = serde_json::from_str(json_bytes).expect("must parse");
+
+    let s = serde_json::to_string_pretty(&output).expect("Serialize");
+    let output2: Output = serde_json::from_str(&s).expect("must parse again");
+
+    assert_eq!(output, output2);
+}
diff --git a/tvix/nix-compat/src/derivation/parse_error.rs b/tvix/nix-compat/src/derivation/parse_error.rs
new file mode 100644
index 0000000000..fc97f1a988
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/parse_error.rs
@@ -0,0 +1,87 @@
+//! This contains error and result types that can happen while parsing
+//! Derivations from ATerm.
+use nom::IResult;
+
+use crate::{
+    nixhash,
+    store_path::{self, StorePath},
+};
+
+pub type NomResult<I, O> = IResult<I, O, NomError<I>>;
+
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum ErrorKind {
+    /// duplicate key in map
+    #[error("duplicate map key: {0}")]
+    DuplicateMapKey(String),
+
+    /// Input derivation has two outputs with the same name
+    #[error("duplicate output name {1} for input derivation {0}")]
+    DuplicateInputDerivationOutputName(String, String),
+
+    #[error("duplicate input source: {0}")]
+    DuplicateInputSource(StorePath),
+
+    #[error("nix hash error: {0}")]
+    NixHashError(nixhash::Error),
+
+    #[error("store path error: {0}")]
+    StorePathError(#[from] store_path::Error),
+
+    #[error("nom error: {0:?}")]
+    Nom(nom::error::ErrorKind),
+}
+
+/// Our own error type to pass along parser-related errors.
+#[derive(Debug, PartialEq)]
+pub struct NomError<I> {
+    /// position of the error in the input data
+    pub input: I,
+    /// error code
+    pub code: ErrorKind,
+}
+
+impl<I, E> nom::error::FromExternalError<I, E> for NomError<I> {
+    fn from_external_error(input: I, kind: nom::error::ErrorKind, _e: E) -> Self {
+        Self {
+            input,
+            code: ErrorKind::Nom(kind),
+        }
+    }
+}
+
+impl<I> nom::error::ParseError<I> for NomError<I> {
+    fn from_error_kind(input: I, kind: nom::error::ErrorKind) -> Self {
+        Self {
+            input,
+            code: ErrorKind::Nom(kind),
+        }
+    }
+
+    // FUTUREWORK: implement, so we have support for backtracking through the
+    // parse tree?
+    fn append(_input: I, _kind: nom::error::ErrorKind, other: Self) -> Self {
+        other
+    }
+}
+
+/// This wraps a [nom::error::Error] into our error.
+impl<I> From<nom::error::Error<I>> for NomError<I> {
+    fn from(value: nom::error::Error<I>) -> Self {
+        Self {
+            input: value.input,
+            code: ErrorKind::Nom(value.code),
+        }
+    }
+}
+
+/// This essentially implements
+/// `From<nom::Err<nom::error::Error<I>>>` for `nom::Err<NomError<I>>`,
+/// which we can't because `nom::Err<_>` is a foreign type.
+pub(crate) fn into_nomerror<I>(e: nom::Err<nom::error::Error<I>>) -> nom::Err<NomError<I>> {
+    match e {
+        nom::Err::Incomplete(n) => nom::Err::Incomplete(n),
+        nom::Err::Error(e) => nom::Err::Error(e.into()),
+        nom::Err::Failure(e) => nom::Err::Failure(e.into()),
+    }
+}
diff --git a/tvix/nix-compat/src/derivation/parser.rs b/tvix/nix-compat/src/derivation/parser.rs
new file mode 100644
index 0000000000..2775294960
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/parser.rs
@@ -0,0 +1,585 @@
+//! This module constructs a [Derivation] by parsing its [ATerm][]
+//! serialization.
+//!
+//! [ATerm]: http://program-transformation.org/Tools/ATermFormat.html
+
+use bstr::BString;
+use nom::bytes::complete::tag;
+use nom::character::complete::char as nomchar;
+use nom::combinator::{all_consuming, map_res};
+use nom::multi::{separated_list0, separated_list1};
+use nom::sequence::{delimited, preceded, separated_pair, terminated, tuple};
+use std::collections::{btree_map, BTreeMap, BTreeSet};
+use thiserror;
+
+use crate::derivation::parse_error::{into_nomerror, ErrorKind, NomError, NomResult};
+use crate::derivation::{write, CAHash, Derivation, Output};
+use crate::store_path::{self, StorePath, StorePathRef};
+use crate::{aterm, nixhash};
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error<I> {
+    #[error("parsing error: {0}")]
+    Parser(NomError<I>),
+    #[error("premature EOF")]
+    Incomplete,
+    #[error("validation error: {0}")]
+    Validation(super::DerivationError),
+}
+
+pub(crate) fn parse(i: &[u8]) -> Result<Derivation, Error<&[u8]>> {
+    match all_consuming(parse_derivation)(i) {
+        Ok((rest, derivation)) => {
+            // this shouldn't happen, as all_consuming shouldn't return.
+            debug_assert!(rest.is_empty());
+
+            // invoke validate
+            derivation.validate(true).map_err(Error::Validation)?;
+
+            Ok(derivation)
+        }
+        Err(nom::Err::Incomplete(_)) => Err(Error::Incomplete),
+        Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(Error::Parser(e)),
+    }
+}
+
+/// Consume a string containing the algo, and optionally a `r:`
+/// prefix, and a digest (bytes), return a [CAHash::Nar] or [CAHash::Flat].
+fn from_algo_and_mode_and_digest<B: AsRef<[u8]>>(
+    algo_and_mode: &str,
+    digest: B,
+) -> crate::nixhash::NixHashResult<CAHash> {
+    Ok(match algo_and_mode.strip_prefix("r:") {
+        Some(algo) => nixhash::CAHash::Nar(nixhash::from_algo_and_digest(
+            algo.try_into()?,
+            digest.as_ref(),
+        )?),
+        None => nixhash::CAHash::Flat(nixhash::from_algo_and_digest(
+            algo_and_mode.try_into()?,
+            digest.as_ref(),
+        )?),
+    })
+}
+
+/// Parse one output in ATerm. This is 4 string fields inside parans:
+/// output name, output path, algo (and mode), digest.
+/// Returns the output name and [Output] struct.
+fn parse_output(i: &[u8]) -> NomResult<&[u8], (String, Output)> {
+    delimited(
+        nomchar('('),
+        map_res(
+            |i| {
+                tuple((
+                    terminated(aterm::parse_string_field, nomchar(',')),
+                    terminated(aterm::parse_string_field, nomchar(',')),
+                    terminated(aterm::parse_string_field, nomchar(',')),
+                    aterm::parse_bstr_field,
+                ))(i)
+                .map_err(into_nomerror)
+            },
+            |(output_name, output_path, algo_and_mode, encoded_digest)| {
+                // convert these 4 fields into an [Output].
+                let ca_hash_res = {
+                    if algo_and_mode.is_empty() && encoded_digest.is_empty() {
+                        None
+                    } else {
+                        match data_encoding::HEXLOWER.decode(&encoded_digest) {
+                            Ok(digest) => {
+                                Some(from_algo_and_mode_and_digest(&algo_and_mode, digest))
+                            }
+                            Err(e) => Some(Err(nixhash::Error::InvalidBase64Encoding(e))),
+                        }
+                    }
+                }
+                .transpose();
+
+                match ca_hash_res {
+                    Ok(hash_with_mode) => Ok((
+                        output_name,
+                        Output {
+                            // TODO: Check if allowing empty paths here actually makes sense
+                            //       or we should make this code stricter.
+                            path: if output_path.is_empty() {
+                                None
+                            } else {
+                                Some(string_to_store_path(i, output_path)?)
+                            },
+                            ca_hash: hash_with_mode,
+                        },
+                    )),
+                    Err(e) => Err(nom::Err::Failure(NomError {
+                        input: i,
+                        code: ErrorKind::NixHashError(e),
+                    })),
+                }
+            },
+        ),
+        nomchar(')'),
+    )(i)
+}
+
+/// Parse multiple outputs in ATerm. This is a list of things acccepted by
+/// parse_output, and takes care of turning the (String, Output) returned from
+/// it to a BTreeMap.
+/// We don't use parse_kv here, as it's dealing with 2-tuples, and these are
+/// 4-tuples.
+fn parse_outputs(i: &[u8]) -> NomResult<&[u8], BTreeMap<String, Output>> {
+    let res = delimited(
+        nomchar('['),
+        separated_list1(tag(","), parse_output),
+        nomchar(']'),
+    )(i);
+
+    match res {
+        Ok((rst, outputs_lst)) => {
+            let mut outputs: BTreeMap<String, Output> = BTreeMap::default();
+            for (output_name, output) in outputs_lst.into_iter() {
+                if outputs.contains_key(&output_name) {
+                    return Err(nom::Err::Failure(NomError {
+                        input: i,
+                        code: ErrorKind::DuplicateMapKey(output_name),
+                    }));
+                }
+                outputs.insert(output_name, output);
+            }
+            Ok((rst, outputs))
+        }
+        // pass regular parse errors along
+        Err(e) => Err(e),
+    }
+}
+
+fn parse_input_derivations(i: &[u8]) -> NomResult<&[u8], BTreeMap<StorePath, BTreeSet<String>>> {
+    let (i, input_derivations_list) = parse_kv::<Vec<String>, _>(aterm::parse_str_list)(i)?;
+
+    // This is a HashMap of drv paths to a list of output names.
+    let mut input_derivations: BTreeMap<StorePath, BTreeSet<String>> = BTreeMap::new();
+
+    for (input_derivation, output_names) in input_derivations_list {
+        let mut new_output_names = BTreeSet::new();
+        for output_name in output_names.into_iter() {
+            if new_output_names.contains(&output_name) {
+                return Err(nom::Err::Failure(NomError {
+                    input: i,
+                    code: ErrorKind::DuplicateInputDerivationOutputName(
+                        input_derivation.to_string(),
+                        output_name.to_string(),
+                    ),
+                }));
+            }
+            new_output_names.insert(output_name);
+        }
+
+        let input_derivation: StorePath = string_to_store_path(i, input_derivation)?;
+
+        input_derivations.insert(input_derivation, new_output_names);
+    }
+
+    Ok((i, input_derivations))
+}
+
+fn parse_input_sources(i: &[u8]) -> NomResult<&[u8], BTreeSet<StorePath>> {
+    let (i, input_sources_lst) = aterm::parse_str_list(i).map_err(into_nomerror)?;
+
+    let mut input_sources: BTreeSet<_> = BTreeSet::new();
+    for input_source in input_sources_lst.into_iter() {
+        let input_source: StorePath = string_to_store_path(i, input_source)?;
+        if input_sources.contains(&input_source) {
+            return Err(nom::Err::Failure(NomError {
+                input: i,
+                code: ErrorKind::DuplicateInputSource(input_source),
+            }));
+        } else {
+            input_sources.insert(input_source);
+        }
+    }
+
+    Ok((i, input_sources))
+}
+
+fn string_to_store_path(
+    i: &[u8],
+    path_str: String,
+) -> Result<StorePath, nom::Err<NomError<&[u8]>>> {
+    #[cfg(debug_assertions)]
+    let path_str2 = path_str.clone();
+
+    let path: StorePath = StorePathRef::from_absolute_path(path_str.as_bytes())
+        .map_err(|e: store_path::Error| {
+            nom::Err::Failure(NomError {
+                input: i,
+                code: e.into(),
+            })
+        })?
+        .to_owned();
+
+    #[cfg(debug_assertions)]
+    assert_eq!(path_str2, path.to_absolute_path());
+
+    Ok(path)
+}
+
+pub fn parse_derivation(i: &[u8]) -> NomResult<&[u8], Derivation> {
+    use nom::Parser;
+    preceded(
+        tag(write::DERIVATION_PREFIX),
+        delimited(
+            // inside parens
+            nomchar('('),
+            // tuple requires all errors to be of the same type, so we need to be a
+            // bit verbose here wrapping generic IResult into [NomATermResult].
+            tuple((
+                // parse outputs
+                terminated(parse_outputs, nomchar(',')),
+                // // parse input derivations
+                terminated(parse_input_derivations, nomchar(',')),
+                // // parse input sources
+                terminated(parse_input_sources, nomchar(',')),
+                // // parse system
+                |i| terminated(aterm::parse_string_field, nomchar(','))(i).map_err(into_nomerror),
+                // // parse builder
+                |i| terminated(aterm::parse_string_field, nomchar(','))(i).map_err(into_nomerror),
+                // // parse arguments
+                |i| terminated(aterm::parse_str_list, nomchar(','))(i).map_err(into_nomerror),
+                // parse environment
+                parse_kv::<BString, _>(aterm::parse_bstr_field),
+            )),
+            nomchar(')'),
+        )
+        .map(
+            |(
+                outputs,
+                input_derivations,
+                input_sources,
+                system,
+                builder,
+                arguments,
+                environment,
+            )| {
+                Derivation {
+                    arguments,
+                    builder,
+                    environment,
+                    input_derivations,
+                    input_sources,
+                    outputs,
+                    system,
+                }
+            },
+        ),
+    )(i)
+}
+
+/// Parse a list of key/value pairs into a BTreeMap.
+/// The parser for the values can be passed in.
+/// In terms of ATerm, this is just a 2-tuple,
+/// but we have the additional restriction that the first element needs to be
+/// unique across all tuples.
+pub(crate) fn parse_kv<'a, V, VF>(
+    vf: VF,
+) -> impl FnMut(&'a [u8]) -> NomResult<&'a [u8], BTreeMap<String, V>> + 'static
+where
+    VF: FnMut(&'a [u8]) -> nom::IResult<&'a [u8], V, nom::error::Error<&'a [u8]>> + Clone + 'static,
+{
+    move |i|
+    // inside brackets
+    delimited(
+        nomchar('['),
+        |ii| {
+            let res = separated_list0(
+                nomchar(','),
+                // inside parens
+                delimited(
+                    nomchar('('),
+                    separated_pair(
+                        aterm::parse_string_field,
+                        nomchar(','),
+                        vf.clone(),
+                    ),
+                    nomchar(')'),
+                ),
+            )(ii).map_err(into_nomerror);
+
+            match res {
+                Ok((rest, pairs)) => {
+                    let mut kvs: BTreeMap<String, V> = BTreeMap::new();
+                    for (k, v) in pairs.into_iter() {
+                        // collect the 2-tuple to a BTreeMap,
+                        // and fail if the key was already seen before.
+                        match kvs.entry(k) {
+                            btree_map::Entry::Vacant(e) => { e.insert(v); },
+                            btree_map::Entry::Occupied(e) => {
+                                return Err(nom::Err::Failure(NomError {
+                                    input: i,
+                                    code: ErrorKind::DuplicateMapKey(e.key().clone()),
+                                }));
+                            }
+                        }
+                    }
+                    Ok((rest, kvs))
+                }
+                Err(e) => Err(e),
+            }
+        },
+        nomchar(']'),
+    )(i)
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::store_path::StorePathRef;
+    use std::collections::{BTreeMap, BTreeSet};
+
+    use crate::{
+        derivation::{
+            parse_error::ErrorKind, parser::from_algo_and_mode_and_digest, CAHash, NixHash, Output,
+        },
+        store_path::StorePath,
+    };
+    use bstr::{BString, ByteSlice};
+    use hex_literal::hex;
+    use lazy_static::lazy_static;
+    use rstest::rstest;
+
+    const DIGEST_SHA256: [u8; 32] =
+        hex!("a5ce9c155ed09397614646c9717fc7cd94b1023d7b76b618d409e4fefd6e9d39");
+
+    lazy_static! {
+        pub static ref NIXHASH_SHA256: NixHash = NixHash::Sha256(DIGEST_SHA256);
+        static ref EXP_MULTI_OUTPUTS: BTreeMap<String, Output> = {
+            let mut b = BTreeMap::new();
+            b.insert(
+                "lib".to_string(),
+                Output {
+                    path: Some(
+                        StorePath::from_bytes(
+                            b"2vixb94v0hy2xc6p7mbnxxcyc095yyia-has-multi-out-lib",
+                        )
+                        .unwrap(),
+                    ),
+                    ca_hash: None,
+                },
+            );
+            b.insert(
+                "out".to_string(),
+                Output {
+                    path: Some(
+                        StorePath::from_bytes(
+                            b"55lwldka5nyxa08wnvlizyqw02ihy8ic-has-multi-out".as_bytes(),
+                        )
+                        .unwrap(),
+                    ),
+                    ca_hash: None,
+                },
+            );
+            b
+        };
+        static ref EXP_AB_MAP: BTreeMap<String, BString> = {
+            let mut b = BTreeMap::new();
+            b.insert("a".to_string(), b"1".as_bstr().to_owned());
+            b.insert("b".to_string(), b"2".as_bstr().to_owned());
+            b
+        };
+        static ref EXP_INPUT_DERIVATIONS_SIMPLE: BTreeMap<StorePath, BTreeSet<String>> = {
+            let mut b = BTreeMap::new();
+            b.insert(
+                StorePath::from_bytes(b"8bjm87p310sb7r2r0sg4xrynlvg86j8k-hello-2.12.1.tar.gz.drv")
+                    .unwrap(),
+                {
+                    let mut output_names = BTreeSet::new();
+                    output_names.insert("out".to_string());
+                    output_names
+                },
+            );
+            b.insert(
+                StorePath::from_bytes(b"p3jc8aw45dza6h52v81j7lk69khckmcj-bash-5.2-p15.drv")
+                    .unwrap(),
+                {
+                    let mut output_names = BTreeSet::new();
+                    output_names.insert("out".to_string());
+                    output_names.insert("lib".to_string());
+                    output_names
+                },
+            );
+            b
+        };
+        static ref EXP_INPUT_DERIVATIONS_SIMPLE_ATERM: String = {
+            format!(
+                "[(\"{0}\",[\"out\"]),(\"{1}\",[\"out\",\"lib\"])]",
+                "/nix/store/8bjm87p310sb7r2r0sg4xrynlvg86j8k-hello-2.12.1.tar.gz.drv",
+                "/nix/store/p3jc8aw45dza6h52v81j7lk69khckmcj-bash-5.2-p15.drv"
+            )
+        };
+        static ref EXP_INPUT_SOURCES_SIMPLE: BTreeSet<String> = {
+            let mut b = BTreeSet::new();
+            b.insert("/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-has-multi-out".to_string());
+            b.insert("/nix/store/2vixb94v0hy2xc6p7mbnxxcyc095yyia-has-multi-out-lib".to_string());
+            b
+        };
+    }
+
+    /// Ensure parsing KVs works
+    #[rstest]
+    #[case::empty(b"[]", &BTreeMap::new(), b"")]
+    #[case::simple(b"[(\"a\",\"1\"),(\"b\",\"2\")]", &EXP_AB_MAP, b"")]
+    fn parse_kv(
+        #[case] input: &'static [u8],
+        #[case] expected: &BTreeMap<String, BString>,
+        #[case] exp_rest: &[u8],
+    ) {
+        let (rest, parsed) = super::parse_kv::<BString, _>(crate::aterm::parse_bstr_field)(input)
+            .expect("must parse");
+        assert_eq!(exp_rest, rest, "expected remainder");
+        assert_eq!(*expected, parsed);
+    }
+
+    /// Ensures the kv parser complains about duplicate map keys
+    #[test]
+    fn parse_kv_fail_dup_keys() {
+        let input: &'static [u8] = b"[(\"a\",\"1\"),(\"a\",\"2\")]";
+        let e = super::parse_kv::<BString, _>(crate::aterm::parse_bstr_field)(input)
+            .expect_err("must fail");
+
+        match e {
+            nom::Err::Failure(e) => {
+                assert_eq!(ErrorKind::DuplicateMapKey("a".to_string()), e.code);
+            }
+            _ => panic!("unexpected error"),
+        }
+    }
+
+    /// Ensure parsing input derivations works.
+    #[rstest]
+    #[case::empty(b"[]", &BTreeMap::new())]
+    #[case::simple(EXP_INPUT_DERIVATIONS_SIMPLE_ATERM.as_bytes(), &EXP_INPUT_DERIVATIONS_SIMPLE)]
+    fn parse_input_derivations(
+        #[case] input: &'static [u8],
+        #[case] expected: &BTreeMap<StorePath, BTreeSet<String>>,
+    ) {
+        let (rest, parsed) = super::parse_input_derivations(input).expect("must parse");
+
+        assert_eq!(expected, &parsed, "parsed mismatch");
+        assert!(rest.is_empty(), "rest must be empty");
+    }
+
+    /// Ensures the input derivation parser complains about duplicate output names
+    #[test]
+    fn parse_input_derivations_fail_dup_output_names() {
+        let input_str = format!(
+            "[(\"{0}\",[\"out\"]),(\"{1}\",[\"out\",\"out\"])]",
+            "/nix/store/8bjm87p310sb7r2r0sg4xrynlvg86j8k-hello-2.12.1.tar.gz.drv",
+            "/nix/store/p3jc8aw45dza6h52v81j7lk69khckmcj-bash-5.2-p15.drv"
+        );
+        let e = super::parse_input_derivations(input_str.as_bytes()).expect_err("must fail");
+
+        match e {
+            nom::Err::Failure(e) => {
+                assert_eq!(
+                    ErrorKind::DuplicateInputDerivationOutputName(
+                        "/nix/store/p3jc8aw45dza6h52v81j7lk69khckmcj-bash-5.2-p15.drv".to_string(),
+                        "out".to_string()
+                    ),
+                    e.code
+                );
+            }
+            _ => panic!("unexpected error"),
+        }
+    }
+
+    /// Ensure parsing input sources works
+    #[rstest]
+    #[case::empty(b"[]", &BTreeSet::new())]
+    #[case::simple(b"[\"/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-has-multi-out\",\"/nix/store/2vixb94v0hy2xc6p7mbnxxcyc095yyia-has-multi-out-lib\"]", &EXP_INPUT_SOURCES_SIMPLE)]
+    fn parse_input_sources(#[case] input: &'static [u8], #[case] expected: &BTreeSet<String>) {
+        let (rest, parsed) = super::parse_input_sources(input).expect("must parse");
+
+        assert_eq!(
+            expected,
+            &parsed
+                .iter()
+                .map(StorePath::to_absolute_path)
+                .collect::<BTreeSet<_>>(),
+            "parsed mismatch"
+        );
+        assert!(rest.is_empty(), "rest must be empty");
+    }
+
+    /// Ensures the input sources parser complains about duplicate input sources
+    #[test]
+    fn parse_input_sources_fail_dup_keys() {
+        let input: &'static [u8] = b"[\"/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-foo\",\"/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-foo\"]";
+        let e = super::parse_input_sources(input).expect_err("must fail");
+
+        match e {
+            nom::Err::Failure(e) => {
+                assert_eq!(
+                    ErrorKind::DuplicateInputSource(
+                        StorePathRef::from_absolute_path(
+                            "/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-foo".as_bytes()
+                        )
+                        .unwrap()
+                        .to_owned()
+                    ),
+                    e.code
+                );
+            }
+            _ => panic!("unexpected error"),
+        }
+    }
+
+    #[rstest]
+    #[case::simple(
+        br#"("out","/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo","","")"#,
+        ("out".to_string(), Output {
+            path: Some(
+                StorePathRef::from_absolute_path("/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo".as_bytes()).unwrap().to_owned()),
+            ca_hash: None
+        })
+    )]
+    #[case::fod(
+        br#"("out","/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar","r:sha256","08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba")"#,
+        ("out".to_string(), Output {
+            path: Some(
+                StorePathRef::from_absolute_path(
+                "/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar".as_bytes()).unwrap().to_owned()),
+            ca_hash: Some(from_algo_and_mode_and_digest("r:sha256",
+                   data_encoding::HEXLOWER.decode(b"08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba").unwrap()            ).unwrap()),
+        })
+     )]
+    fn parse_output(#[case] input: &[u8], #[case] expected: (String, Output)) {
+        let (rest, parsed) = super::parse_output(input).expect("must parse");
+        assert!(rest.is_empty());
+        assert_eq!(expected, parsed);
+    }
+
+    #[rstest]
+    #[case::multi_out(
+        br#"[("lib","/nix/store/2vixb94v0hy2xc6p7mbnxxcyc095yyia-has-multi-out-lib","",""),("out","/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-has-multi-out","","")]"#,
+        &EXP_MULTI_OUTPUTS
+    )]
+    fn parse_outputs(#[case] input: &[u8], #[case] expected: &BTreeMap<String, Output>) {
+        let (rest, parsed) = super::parse_outputs(input).expect("must parse");
+        assert!(rest.is_empty());
+        assert_eq!(*expected, parsed);
+    }
+
+    #[rstest]
+    #[case::sha256_flat("sha256", &DIGEST_SHA256, CAHash::Flat(NIXHASH_SHA256.clone()))]
+    #[case::sha256_recursive("r:sha256", &DIGEST_SHA256, CAHash::Nar(NIXHASH_SHA256.clone()))]
+    fn test_from_algo_and_mode_and_digest(
+        #[case] algo_and_mode: &str,
+        #[case] digest: &[u8],
+        #[case] expected: CAHash,
+    ) {
+        assert_eq!(
+            expected,
+            from_algo_and_mode_and_digest(algo_and_mode, digest).unwrap()
+        );
+    }
+
+    #[test]
+    fn from_algo_and_mode_and_digest_failure() {
+        assert!(from_algo_and_mode_and_digest("r:sha256", []).is_err());
+        assert!(from_algo_and_mode_and_digest("ha256", DIGEST_SHA256).is_err());
+    }
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/duplicate.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/duplicate.drv
new file mode 100644
index 0000000000..072561a29e
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/duplicate.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo","","")],[("/nix/store/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv",["out"])],[],":",":",[],[("bar","/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar"),("builder",":"),("name","foo"),("name","bar"),("out","/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv
new file mode 100644
index 0000000000..a4fea3c5f4
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar","r:sha256","08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba")],[],[],":",":",[],[("builder",":"),("name","bar"),("out","/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar"),("outputHash","08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"),("outputHashAlgo","sha256"),("outputHashMode","recursive"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv.json
new file mode 100644
index 0000000000..c8bbc4cbb5
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv.json
@@ -0,0 +1,23 @@
+{
+  "args": [],
+  "builder": ":",
+  "env": {
+    "builder": ":",
+    "name": "bar",
+    "out": "/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar",
+    "outputHash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+    "outputHashAlgo": "sha256",
+    "outputHashMode": "recursive",
+    "system": ":"
+  },
+  "inputDrvs": {},
+  "inputSrcs": [],
+  "outputs": {
+    "out": {
+      "hash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+      "hashAlgo": "r:sha256",
+      "path": "/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar"
+    }
+  },
+  "system": ":"
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/292w8yzv5nn7nhdpxcs8b7vby2p27s09-nested-json.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/292w8yzv5nn7nhdpxcs8b7vby2p27s09-nested-json.drv
new file mode 100644
index 0000000000..f0d9230a5a
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/292w8yzv5nn7nhdpxcs8b7vby2p27s09-nested-json.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/pzr7lsd3q9pqsnb42r9b23jc5sh8irvn-nested-json","","")],[],[],":",":",[],[("builder",":"),("json","{\"hello\":\"moto\\n\"}"),("name","nested-json"),("out","/nix/store/pzr7lsd3q9pqsnb42r9b23jc5sh8irvn-nested-json"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/292w8yzv5nn7nhdpxcs8b7vby2p27s09-nested-json.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/292w8yzv5nn7nhdpxcs8b7vby2p27s09-nested-json.drv.json
new file mode 100644
index 0000000000..9cb0b43b4c
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/292w8yzv5nn7nhdpxcs8b7vby2p27s09-nested-json.drv.json
@@ -0,0 +1,19 @@
+{
+  "args": [],
+  "builder": ":",
+  "env": {
+    "builder": ":",
+    "json": "{\"hello\":\"moto\\n\"}",
+    "name": "nested-json",
+    "out": "/nix/store/pzr7lsd3q9pqsnb42r9b23jc5sh8irvn-nested-json",
+    "system": ":"
+  },
+  "inputDrvs": {},
+  "inputSrcs": [],
+  "outputs": {
+    "out": {
+      "path": "/nix/store/pzr7lsd3q9pqsnb42r9b23jc5sh8irvn-nested-json"
+    }
+  },
+  "system": ":"
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv
new file mode 100644
index 0000000000..a2cf9d31f9
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo","","")],[("/nix/store/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv",["out"])],[],":",":",[],[("bar","/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar"),("builder",":"),("name","foo"),("out","/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv.json
new file mode 100644
index 0000000000..957a85ccab
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv.json
@@ -0,0 +1,23 @@
+{
+  "args": [],
+  "builder": ":",
+  "env": {
+    "bar": "/nix/store/4q0pg5zpfmznxscq3avycvf9xdvx50n3-bar",
+    "builder": ":",
+    "name": "foo",
+    "out": "/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo",
+    "system": ":"
+  },
+  "inputDrvs": {
+    "/nix/store/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv": [
+      "out"
+    ]
+  },
+  "inputSrcs": [],
+  "outputs": {
+    "out": {
+      "path": "/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo"
+    }
+  },
+  "system": ":"
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv
new file mode 100644
index 0000000000..bbe88c02c7
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/vgvdj6nf7s8kvfbl2skbpwz9kc7xjazc-unicode","","")],[],[],":",":",[],[("builder",":"),("letters","rΓ€ksmΓΆrgΓ₯s\nrΓΈdgrΓΈd med flΓΈde\nLΓΌbeck\nθ‚₯ηŒͺ\nこんにけは / 今ζ—₯は\nπŸŒ\n"),("name","unicode"),("out","/nix/store/vgvdj6nf7s8kvfbl2skbpwz9kc7xjazc-unicode"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv.json
new file mode 100644
index 0000000000..f8f33c1bba
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv.json
@@ -0,0 +1,19 @@
+{
+  "outputs": {
+    "out": {
+      "path": "/nix/store/vgvdj6nf7s8kvfbl2skbpwz9kc7xjazc-unicode"
+    }
+  },
+  "inputSrcs": [],
+  "inputDrvs": {},
+  "system": ":",
+  "builder": ":",
+  "args": [],
+  "env": {
+    "builder": ":",
+    "letters": "rΓ€ksmΓΆrgΓ₯s\nrΓΈdgrΓΈd med flΓΈde\nLΓΌbeck\nθ‚₯ηŒͺ\nこんにけは / 今ζ—₯は\nπŸŒ\n",
+    "name": "unicode",
+    "out": "/nix/store/vgvdj6nf7s8kvfbl2skbpwz9kc7xjazc-unicode",
+    "system": ":"
+  }
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv
new file mode 100644
index 0000000000..4b9338c0b9
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/6a39dl014j57bqka7qx25k0vb20vkqm6-structured-attrs","","")],[],[],":",":",[],[("__json","{\"builder\":\":\",\"name\":\"structured-attrs\",\"system\":\":\"}"),("out","/nix/store/6a39dl014j57bqka7qx25k0vb20vkqm6-structured-attrs")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv.json
new file mode 100644
index 0000000000..74e3d7df55
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv.json
@@ -0,0 +1,16 @@
+{
+  "args": [],
+  "builder": ":",
+  "env": {
+    "__json": "{\"builder\":\":\",\"name\":\"structured-attrs\",\"system\":\":\"}",
+    "out": "/nix/store/6a39dl014j57bqka7qx25k0vb20vkqm6-structured-attrs"
+  },
+  "inputDrvs": {},
+  "inputSrcs": [],
+  "outputs": {
+    "out": {
+      "path": "/nix/store/6a39dl014j57bqka7qx25k0vb20vkqm6-structured-attrs"
+    }
+  },
+  "system": ":"
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv
new file mode 100644
index 0000000000..1699c2a75e
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo","","")],[("/nix/store/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv",["out"])],[],":",":",[],[("bar","/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar"),("builder",":"),("name","foo"),("out","/nix/store/fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv.json
new file mode 100644
index 0000000000..831d27956d
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv.json
@@ -0,0 +1,23 @@
+{
+  "args": [],
+  "builder": ":",
+  "env": {
+    "bar": "/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar",
+    "builder": ":",
+    "name": "foo",
+    "out": "/nix/store/fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo",
+    "system": ":"
+  },
+  "inputDrvs": {
+    "/nix/store/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv": [
+      "out"
+    ]
+  },
+  "inputSrcs": [],
+  "outputs": {
+    "out": {
+      "path": "/nix/store/fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo"
+    }
+  },
+  "system": ":"
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv
new file mode 100644
index 0000000000..523612238c
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv
@@ -0,0 +1 @@
+Derive([("lib","/nix/store/2vixb94v0hy2xc6p7mbnxxcyc095yyia-has-multi-out-lib","",""),("out","/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-has-multi-out","","")],[],[],":",":",[],[("builder",":"),("lib","/nix/store/2vixb94v0hy2xc6p7mbnxxcyc095yyia-has-multi-out-lib"),("name","has-multi-out"),("out","/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-has-multi-out"),("outputs","out lib"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv.json
new file mode 100644
index 0000000000..0bd7a2991c
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv.json
@@ -0,0 +1,23 @@
+{
+  "args": [],
+  "builder": ":",
+  "env": {
+    "builder": ":",
+    "lib": "/nix/store/2vixb94v0hy2xc6p7mbnxxcyc095yyia-has-multi-out-lib",
+    "name": "has-multi-out",
+    "out": "/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-has-multi-out",
+    "outputs": "out lib",
+    "system": ":"
+  },
+  "inputDrvs": {},
+  "inputSrcs": [],
+  "outputs": {
+    "lib": {
+      "path": "/nix/store/2vixb94v0hy2xc6p7mbnxxcyc095yyia-has-multi-out-lib"
+    },
+    "out": {
+      "path": "/nix/store/55lwldka5nyxa08wnvlizyqw02ihy8ic-has-multi-out"
+    }
+  },
+  "system": ":"
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv
new file mode 100644
index 0000000000..6a7a35c58c
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/drr2mjp9fp9vvzsf5f9p0a80j33dxy7m-cp1252","","")],[],[],":",":",[],[("builder",":"),("chars","ΕΔΦ"),("name","cp1252"),("out","/nix/store/drr2mjp9fp9vvzsf5f9p0a80j33dxy7m-cp1252"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv.json
new file mode 100644
index 0000000000..9d6ba8b797
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv.json
@@ -0,0 +1,21 @@
+{
+  "/nix/store/m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv": {
+    "outputs": {
+      "out": {
+        "path": "/nix/store/drr2mjp9fp9vvzsf5f9p0a80j33dxy7m-cp1252"
+      }
+    },
+    "inputSrcs": [],
+    "inputDrvs": {},
+    "system": ":",
+    "builder": ":",
+    "args": [],
+    "env": {
+      "builder": ":",
+      "chars": "ΕΔΦ",
+      "name": "cp1252",
+      "out": "/nix/store/drr2mjp9fp9vvzsf5f9p0a80j33dxy7m-cp1252",
+      "system": ":"
+    }
+  }
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv
new file mode 100644
index 0000000000..559e93ed0e
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar","r:sha1","0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")],[],[],":",":",[],[("builder",":"),("name","bar"),("out","/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar"),("outputHash","0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"),("outputHashAlgo","sha1"),("outputHashMode","recursive"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv.json
new file mode 100644
index 0000000000..e297d27159
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv.json
@@ -0,0 +1,23 @@
+{
+  "args": [],
+  "builder": ":",
+  "env": {
+    "builder": ":",
+    "name": "bar",
+    "out": "/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar",
+    "outputHash": "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33",
+    "outputHashAlgo": "sha1",
+    "outputHashMode": "recursive",
+    "system": ":"
+  },
+  "inputDrvs": {},
+  "inputSrcs": [],
+  "outputs": {
+    "out": {
+      "hash": "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33",
+      "hashAlgo": "r:sha1",
+      "path": "/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar"
+    }
+  },
+  "system": ":"
+}
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv
new file mode 100644
index 0000000000..b19fd8eb2c
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv
@@ -0,0 +1 @@
+Derive([("out","/nix/store/x1f6jfq9qgb6i8jrmpifkn9c64fg4hcm-latin1","","")],[],[],":",":",[],[("builder",":"),("chars","ΕΔΦ"),("name","latin1"),("out","/nix/store/x1f6jfq9qgb6i8jrmpifkn9c64fg4hcm-latin1"),("system",":")])
\ No newline at end of file
diff --git a/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv.json b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv.json
new file mode 100644
index 0000000000..ffd5c08da8
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/derivation_tests/ok/x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv.json
@@ -0,0 +1,21 @@
+{
+  "/nix/store/x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv": {
+    "outputs": {
+      "out": {
+        "path": "/nix/store/x1f6jfq9qgb6i8jrmpifkn9c64fg4hcm-latin1"
+      }
+    },
+    "inputSrcs": [],
+    "inputDrvs": {},
+    "system": ":",
+    "builder": ":",
+    "args": [],
+    "env": {
+      "builder": ":",
+      "chars": "ΕΔΦ",
+      "name": "latin1",
+      "out": "/nix/store/x1f6jfq9qgb6i8jrmpifkn9c64fg4hcm-latin1",
+      "system": ":"
+    }
+  }
+}
diff --git a/tvix/nix-compat/src/derivation/tests/mod.rs b/tvix/nix-compat/src/derivation/tests/mod.rs
new file mode 100644
index 0000000000..48d4e8926a
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/tests/mod.rs
@@ -0,0 +1,436 @@
+use super::parse_error::ErrorKind;
+use crate::derivation::output::Output;
+use crate::derivation::parse_error::NomError;
+use crate::derivation::parser::Error;
+use crate::derivation::Derivation;
+use crate::store_path::StorePath;
+use bstr::{BStr, BString};
+use hex_literal::hex;
+use rstest::rstest;
+use std::collections::BTreeSet;
+use std::fs;
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
+
+const RESOURCES_PATHS: &str = "src/derivation/tests/derivation_tests";
+
+#[rstest]
+fn check_serialization(
+    #[files("src/derivation/tests/derivation_tests/ok/*.drv")]
+    #[exclude("(cp1252)|(latin1)")] // skip JSON files known to fail parsing
+    path_to_drv_file: PathBuf,
+) {
+    let json_bytes =
+        fs::read(path_to_drv_file.with_extension("drv.json")).expect("unable to read JSON");
+    let derivation: Derivation =
+        serde_json::from_slice(&json_bytes).expect("JSON was not well-formatted");
+
+    let mut serialized_derivation = Vec::new();
+    derivation.serialize(&mut serialized_derivation).unwrap();
+
+    let expected = fs::read(&path_to_drv_file).expect("unable to read .drv");
+
+    assert_eq!(expected, BStr::new(&serialized_derivation));
+}
+
+#[rstest]
+fn validate(
+    #[files("src/derivation/tests/derivation_tests/ok/*.drv")]
+    #[exclude("(cp1252)|(latin1)")] // skip JSON files known to fail parsing
+    path_to_drv_file: PathBuf,
+) {
+    let json_bytes =
+        fs::read(path_to_drv_file.with_extension("drv.json")).expect("unable to read JSON");
+    let derivation: Derivation =
+        serde_json::from_slice(&json_bytes).expect("JSON was not well-formatted");
+
+    derivation
+        .validate(true)
+        .expect("derivation failed to validate")
+}
+
+#[rstest]
+fn check_to_aterm_bytes(
+    #[files("src/derivation/tests/derivation_tests/ok/*.drv")]
+    #[exclude("(cp1252)|(latin1)")] // skip JSON files known to fail parsing
+    path_to_drv_file: PathBuf,
+) {
+    let json_bytes =
+        fs::read(path_to_drv_file.with_extension("drv.json")).expect("unable to read JSON");
+    let derivation: Derivation =
+        serde_json::from_slice(&json_bytes).expect("JSON was not well-formatted");
+
+    let expected = fs::read(&path_to_drv_file).expect("unable to read .drv");
+
+    assert_eq!(expected, BStr::new(&derivation.to_aterm_bytes()));
+}
+
+/// Reads in derivations in ATerm representation, parses with that parser,
+/// then compares the structs with the ones obtained by parsing the JSON
+/// representations.
+#[rstest]
+fn from_aterm_bytes(
+    #[files("src/derivation/tests/derivation_tests/ok/*.drv")] path_to_drv_file: PathBuf,
+) {
+    // Read in ATerm representation.
+    let aterm_bytes = fs::read(&path_to_drv_file).expect("unable to read .drv");
+    let parsed_drv = Derivation::from_aterm_bytes(&aterm_bytes).expect("must succeed");
+
+    // For where we're able to load JSON fixtures, parse them and compare the structs.
+    // For where we're not, compare the bytes manually.
+    if path_to_drv_file.file_name().is_some_and(|s| {
+        s.as_encoded_bytes().ends_with(b"cp1252.drv")
+            || s.as_encoded_bytes().ends_with(b"latin1.drv")
+    }) {
+        assert_eq!(
+            &[0xc5, 0xc4, 0xd6][..],
+            parsed_drv.environment.get("chars").unwrap(),
+            "expected bytes to match",
+        );
+    } else {
+        let json_bytes =
+            fs::read(path_to_drv_file.with_extension("drv.json")).expect("unable to read JSON");
+        let fixture_derivation: Derivation =
+            serde_json::from_slice(&json_bytes).expect("JSON was not well-formatted");
+
+        assert_eq!(fixture_derivation, parsed_drv);
+    }
+
+    // Finally, write the ATerm serialization to another buffer, ensuring it's
+    // stable (and we compare all fields we couldn't compare in the non-utf8
+    // derivations)
+
+    assert_eq!(
+        &aterm_bytes,
+        &BString::new(parsed_drv.to_aterm_bytes()),
+        "expected serialized ATerm to match initial input"
+    );
+}
+
+#[test]
+fn from_aterm_bytes_duplicate_map_key() {
+    let buf: Vec<u8> =
+        fs::read(format!("{}/{}", RESOURCES_PATHS, "duplicate.drv")).expect("unable to read .drv");
+
+    let err = Derivation::from_aterm_bytes(&buf).expect_err("must fail");
+
+    match err {
+        Error::Parser(NomError { input: _, code }) => {
+            assert_eq!(code, ErrorKind::DuplicateMapKey("name".to_string()));
+        }
+        _ => {
+            panic!("unexpected error");
+        }
+    }
+}
+
+/// Read in a derivation in ATerm, but add some garbage at the end.
+/// Ensure the parser detects and fails in this case.
+#[test]
+fn from_aterm_bytes_trailer() {
+    let mut buf: Vec<u8> = fs::read(format!(
+        "{}/ok/{}",
+        RESOURCES_PATHS, "0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv"
+    ))
+    .expect("unable to read .drv");
+
+    buf.push(0x00);
+
+    Derivation::from_aterm_bytes(&buf).expect_err("must fail");
+}
+
+#[rstest]
+#[case::fixed_sha256("bar", "0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv")]
+#[case::simple_sha256("foo", "4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv")]
+#[case::fixed_sha1("bar", "ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv")]
+#[case::simple_sha1("foo", "ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv")]
+#[case::multiple_outputs("has-multi-out", "h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv")]
+#[case::structured_attrs(
+    "structured-attrs",
+    "9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv"
+)]
+#[case::unicode("unicode", "52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv")]
+fn derivation_path(#[case] name: &str, #[case] expected_path: &str) {
+    let json_bytes = fs::read(format!("{}/ok/{}.json", RESOURCES_PATHS, expected_path))
+        .expect("unable to read JSON");
+    let derivation: Derivation =
+        serde_json::from_slice(&json_bytes).expect("JSON was not well-formatted");
+
+    assert_eq!(
+        derivation.calculate_derivation_path(name).unwrap(),
+        StorePath::from_str(expected_path).unwrap()
+    );
+}
+
+/// This trims all output paths from a Derivation struct,
+/// by setting outputs[$outputName].path and environment[$outputName] to the empty string.
+fn derivation_without_output_paths(derivation: &Derivation) -> Derivation {
+    let mut trimmed_env = derivation.environment.clone();
+    let mut trimmed_outputs = derivation.outputs.clone();
+
+    for (output_name, output) in &derivation.outputs {
+        trimmed_env.insert(output_name.clone(), "".into());
+        assert!(trimmed_outputs.contains_key(output_name));
+        trimmed_outputs.insert(
+            output_name.to_string(),
+            Output {
+                path: None,
+                ..output.clone()
+            },
+        );
+    }
+
+    // replace environment and outputs with the trimmed variants
+    Derivation {
+        environment: trimmed_env,
+        outputs: trimmed_outputs,
+        ..derivation.clone()
+    }
+}
+
+#[rstest]
+#[case::fixed_sha256("0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv", hex!("724f3e3634fce4cbbbd3483287b8798588e80280660b9a63fd13a1bc90485b33"))]
+#[case::fixed_sha1("ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv", hex!("c79aebd0ce3269393d4a1fde2cbd1d975d879b40f0bf40a48f550edc107fd5df"))]
+fn hash_derivation_modulo_fixed(#[case] drv_path: &str, #[case] expected_digest: [u8; 32]) {
+    // read in the fixture
+    let json_bytes =
+        fs::read(format!("{}/ok/{}.json", RESOURCES_PATHS, drv_path)).expect("unable to read JSON");
+    let drv: Derivation = serde_json::from_slice(&json_bytes).expect("must deserialize");
+
+    let actual = drv.hash_derivation_modulo(|_| panic!("must not be called"));
+    assert_eq!(expected_digest, actual);
+}
+
+/// This reads a Derivation (in A-Term), trims out all fields containing
+/// calculated output paths, then triggers the output path calculation and
+/// compares the struct to match what was originally read in.
+#[rstest]
+#[case::fixed_sha256("bar", "0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv")]
+#[case::simple_sha256("foo", "4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv")]
+#[case::fixed_sha1("bar", "ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv")]
+#[case::simple_sha1("foo", "ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv")]
+#[case::multiple_outputs("has-multi-out", "h32dahq0bx5rp1krcdx3a53asj21jvhk-has-multi-out.drv")]
+#[case::structured_attrs(
+    "structured-attrs",
+    "9lj1lkjm2ag622mh4h9rpy6j607an8g2-structured-attrs.drv"
+)]
+#[case::unicode("unicode", "52a9id8hx688hvlnz4d1n25ml1jdykz0-unicode.drv")]
+#[case::cp1252("cp1252", "m1vfixn8iprlf0v9abmlrz7mjw1xj8kp-cp1252.drv")]
+#[case::latin1("latin1", "x6p0hg79i3wg0kkv7699935f7rrj9jf3-latin1.drv")]
+fn output_paths(#[case] name: &str, #[case] drv_path_str: &str) {
+    // read in the derivation
+    let expected_derivation = Derivation::from_aterm_bytes(
+        &fs::read(format!("{}/ok/{}", RESOURCES_PATHS, drv_path_str)).expect("unable to read .drv"),
+    )
+    .expect("must succeed");
+
+    // create a version without output paths, simulating we constructed the
+    // struct.
+    let mut derivation = derivation_without_output_paths(&expected_derivation);
+
+    // calculate the hash_derivation_modulo of Derivation
+    // We don't expect the lookup function to be called for most derivations.
+    let actual_hash_derivation_modulo = derivation.hash_derivation_modulo(|parent_drv_path| {
+        // 4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv may lookup /nix/store/0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv
+        // ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv may lookup /nix/store/ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv
+        if name == "foo"
+            && ((drv_path_str == "4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv"
+                && parent_drv_path.to_string() == "0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv")
+                || (drv_path_str == "ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv"
+                    && parent_drv_path.to_string() == "ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv"))
+        {
+            // do the lookup, by reading in the fixture of the requested
+            // drv_name, and calculating its drv replacement (on the non-stripped version)
+            // In a real-world scenario you would have already done this during construction.
+
+            let json_bytes = fs::read(format!(
+                "{}/ok/{}.json",
+                RESOURCES_PATHS,
+                Path::new(&parent_drv_path.to_string())
+                    .file_name()
+                    .unwrap()
+                    .to_string_lossy()
+            ))
+            .expect("unable to read JSON");
+
+            let drv: Derivation = serde_json::from_slice(&json_bytes).expect("must deserialize");
+
+            // calculate hash_derivation_modulo for each parent.
+            // This may not trigger subsequent requests, as both parents are FOD.
+            drv.hash_derivation_modulo(|_| panic!("must not lookup"))
+        } else {
+            // we only expect this to be called in the "foo" testcase, for the "bar derivations"
+            panic!("may only be called for foo testcase on bar derivations");
+        }
+    });
+
+    derivation
+        .calculate_output_paths(name, &actual_hash_derivation_modulo)
+        .unwrap();
+
+    // The derivation should now look like it was before
+    assert_eq!(expected_derivation, derivation);
+}
+
+/// Exercises the output path calculation functions like a constructing client
+/// (an implementation of builtins.derivation) would do:
+///
+/// ```nix
+/// rec {
+///   bar = builtins.derivation {
+///     name = "bar";
+///     builder = ":";
+///     system = ":";
+///     outputHash = "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba";
+///     outputHashAlgo = "sha256";
+///     outputHashMode = "recursive";
+///   };
+///
+///   foo = builtins.derivation {
+///     name = "foo";
+///     builder = ":";
+///     system = ":";
+///     inherit bar;
+///   };
+/// }
+/// ```
+/// It first assembles the bar derivation, does the output path calculation on
+/// it, then continues with the foo derivation.
+///
+/// The code ensures the resulting Derivations match our fixtures.
+#[test]
+fn output_path_construction() {
+    // create the bar derivation
+    let mut bar_drv = Derivation {
+        builder: ":".to_string(),
+        system: ":".to_string(),
+        ..Default::default()
+    };
+
+    // assemble bar env
+    let bar_env = &mut bar_drv.environment;
+    bar_env.insert("builder".to_string(), ":".into());
+    bar_env.insert("name".to_string(), "bar".into());
+    bar_env.insert("out".to_string(), "".into()); // will be calculated
+    bar_env.insert(
+        "outputHash".to_string(),
+        "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba".into(),
+    );
+    bar_env.insert("outputHashAlgo".to_string(), "sha256".into());
+    bar_env.insert("outputHashMode".to_string(), "recursive".into());
+    bar_env.insert("system".to_string(), ":".into());
+
+    // assemble bar outputs
+    bar_drv.outputs.insert(
+        "out".to_string(),
+        Output {
+            path: None, // will be calculated
+            ca_hash: Some(crate::nixhash::CAHash::Nar(
+                crate::nixhash::from_algo_and_digest(
+                    crate::nixhash::HashAlgo::Sha256,
+                    &data_encoding::HEXLOWER
+                        .decode(
+                            "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"
+                                .as_bytes(),
+                        )
+                        .unwrap(),
+                )
+                .unwrap(),
+            )),
+        },
+    );
+
+    // calculate bar output paths
+    let bar_calc_result = bar_drv.calculate_output_paths(
+        "bar",
+        &bar_drv.hash_derivation_modulo(|_| panic!("is FOD, should not lookup")),
+    );
+    assert!(bar_calc_result.is_ok());
+
+    // ensure it matches our bar fixture
+    let bar_json_bytes = fs::read(format!(
+        "{}/ok/{}.json",
+        RESOURCES_PATHS, "0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv"
+    ))
+    .expect("unable to read JSON");
+    let bar_drv_expected: Derivation =
+        serde_json::from_slice(&bar_json_bytes).expect("must deserialize");
+    assert_eq!(bar_drv_expected, bar_drv);
+
+    // now construct foo, which requires bar_drv
+    // Note how we refer to the output path, drv name and replacement_str (with calculated output paths) of bar.
+    let bar_output_path = &bar_drv.outputs.get("out").expect("must exist").path;
+    let bar_drv_hash_derivation_modulo =
+        bar_drv.hash_derivation_modulo(|_| panic!("is FOD, should not lookup"));
+
+    let bar_drv_path = bar_drv
+        .calculate_derivation_path("bar")
+        .expect("must succeed");
+
+    // create foo derivation
+    let mut foo_drv = Derivation {
+        builder: ":".to_string(),
+        system: ":".to_string(),
+        ..Default::default()
+    };
+
+    // assemble foo env
+    let foo_env = &mut foo_drv.environment;
+    // foo_env.insert("bar".to_string(), StorePathRef:: bar_output_path.to_owned().try_into().unwrap());
+    foo_env.insert(
+        "bar".to_string(),
+        bar_output_path
+            .as_ref()
+            .unwrap()
+            .to_absolute_path()
+            .as_bytes()
+            .into(),
+    );
+    foo_env.insert("builder".to_string(), ":".into());
+    foo_env.insert("name".to_string(), "foo".into());
+    foo_env.insert("out".to_string(), "".into()); // will be calculated
+    foo_env.insert("system".to_string(), ":".into());
+
+    // asssemble foo outputs
+    foo_drv.outputs.insert(
+        "out".to_string(),
+        Output {
+            path: None, // will be calculated
+            ca_hash: None,
+        },
+    );
+
+    // assemble foo input_derivations
+    foo_drv
+        .input_derivations
+        .insert(bar_drv_path, BTreeSet::from(["out".to_string()]));
+
+    // calculate foo output paths
+    let foo_calc_result = foo_drv.calculate_output_paths(
+        "foo",
+        &foo_drv.hash_derivation_modulo(|drv_path| {
+            if drv_path.to_string() != "0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv" {
+                panic!("lookup called with unexpected drv_path: {}", drv_path);
+            }
+            bar_drv_hash_derivation_modulo
+        }),
+    );
+    assert!(foo_calc_result.is_ok());
+
+    // ensure it matches our foo fixture
+    let foo_json_bytes = fs::read(format!(
+        "{}/ok/{}.json",
+        RESOURCES_PATHS, "4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv",
+    ))
+    .expect("unable to read JSON");
+    let foo_drv_expected: Derivation =
+        serde_json::from_slice(&foo_json_bytes).expect("must deserialize");
+    assert_eq!(foo_drv_expected, foo_drv);
+
+    assert_eq!(
+        StorePath::from_str("4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv").expect("must succeed"),
+        foo_drv
+            .calculate_derivation_path("foo")
+            .expect("must succeed")
+    );
+}
diff --git a/tvix/nix-compat/src/derivation/validate.rs b/tvix/nix-compat/src/derivation/validate.rs
new file mode 100644
index 0000000000..e7b24d84ee
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/validate.rs
@@ -0,0 +1,141 @@
+use crate::derivation::{Derivation, DerivationError};
+use crate::store_path;
+
+impl Derivation {
+    /// validate ensures a Derivation struct is properly populated,
+    /// and returns a [DerivationError] if not.
+    ///
+    /// if `validate_output_paths` is set to false, the output paths are
+    /// excluded from validation.
+    ///
+    /// This is helpful to validate struct population before invoking
+    /// [Derivation::calculate_output_paths].
+    pub fn validate(&self, validate_output_paths: bool) -> Result<(), DerivationError> {
+        // Ensure the number of outputs is > 1
+        if self.outputs.is_empty() {
+            return Err(DerivationError::NoOutputs());
+        }
+
+        // Validate all outputs
+        for (output_name, output) in &self.outputs {
+            // empty output names are invalid.
+            //
+            // `drv` is an invalid output name too, as this would cause
+            // a `builtins.derivation` call to return an attrset with a
+            // `drvPath` key (which already exists) and has a different
+            // meaning.
+            //
+            // Other output names that don't match the name restrictions from
+            // [StorePathRef] will fail the [StorePathRef::validate_name] check.
+            if output_name.is_empty()
+                || output_name == "drv"
+                || store_path::validate_name(output_name.as_bytes()).is_err()
+            {
+                return Err(DerivationError::InvalidOutputName(output_name.to_string()));
+            }
+
+            if output.is_fixed() {
+                if self.outputs.len() != 1 {
+                    return Err(DerivationError::MoreThanOneOutputButFixed());
+                }
+                if output_name != "out" {
+                    return Err(DerivationError::InvalidOutputNameForFixed(
+                        output_name.to_string(),
+                    ));
+                }
+            }
+
+            if let Err(e) = output.validate(validate_output_paths) {
+                return Err(DerivationError::InvalidOutput(output_name.to_string(), e));
+            }
+        }
+
+        // Validate all input_derivations
+        for (input_derivation_path, output_names) in &self.input_derivations {
+            // Validate input_derivation_path
+            if !input_derivation_path.name().ends_with(".drv") {
+                return Err(DerivationError::InvalidInputDerivationPrefix(
+                    input_derivation_path.to_string(),
+                ));
+            }
+
+            if output_names.is_empty() {
+                return Err(DerivationError::EmptyInputDerivationOutputNames(
+                    input_derivation_path.to_string(),
+                ));
+            }
+
+            for output_name in output_names.iter() {
+                // empty output names are invalid.
+                //
+                // `drv` is an invalid output name too, as this would cause
+                // a `builtins.derivation` call to return an attrset with a
+                // `drvPath` key (which already exists) and has a different
+                // meaning.
+                //
+                // Other output names that don't match the name restrictions from
+                // [StorePath] will fail the [StorePathRef::validate_name] check.
+                if output_name.is_empty()
+                    || output_name == "drv"
+                    || store_path::validate_name(output_name.as_bytes()).is_err()
+                {
+                    return Err(DerivationError::InvalidInputDerivationOutputName(
+                        input_derivation_path.to_string(),
+                        output_name.to_string(),
+                    ));
+                }
+            }
+        }
+
+        // validate platform
+        if self.system.is_empty() {
+            return Err(DerivationError::InvalidPlatform(self.system.to_string()));
+        }
+
+        // validate builder
+        if self.builder.is_empty() {
+            return Err(DerivationError::InvalidBuilder(self.builder.to_string()));
+        }
+
+        // validate env, none of the keys may be empty.
+        // We skip the `name` validation seen in go-nix.
+        for k in self.environment.keys() {
+            if k.is_empty() {
+                return Err(DerivationError::InvalidEnvironmentKey(k.to_string()));
+            }
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use std::collections::BTreeMap;
+
+    use crate::derivation::{CAHash, Derivation, Output};
+
+    /// Regression test: produce a Derivation that's almost valid, except its
+    /// fixed-output output has the wrong hash specified.
+    #[test]
+    fn output_validate() {
+        let mut outputs = BTreeMap::new();
+        outputs.insert(
+            "out".to_string(),
+            Output {
+                path: None,
+                ca_hash: Some(CAHash::Text([0; 32])), // This is disallowed
+            },
+        );
+
+        let drv = Derivation {
+            arguments: vec![],
+            builder: "/bin/sh".to_string(),
+            outputs,
+            system: "x86_64-linux".to_string(),
+            ..Default::default()
+        };
+
+        drv.validate(false).expect_err("must fail");
+    }
+}
diff --git a/tvix/nix-compat/src/derivation/write.rs b/tvix/nix-compat/src/derivation/write.rs
new file mode 100644
index 0000000000..735b781574
--- /dev/null
+++ b/tvix/nix-compat/src/derivation/write.rs
@@ -0,0 +1,257 @@
+//! This module implements the serialisation of derivations into the
+//! [ATerm][] format used by C++ Nix.
+//!
+//! [ATerm]: http://program-transformation.org/Tools/ATermFormat.html
+
+use crate::aterm::escape_bytes;
+use crate::derivation::{ca_kind_prefix, output::Output};
+use crate::nixbase32;
+use crate::store_path::{StorePath, StorePathRef, STORE_DIR_WITH_SLASH};
+use bstr::BString;
+use data_encoding::HEXLOWER;
+
+use std::{
+    collections::{BTreeMap, BTreeSet},
+    io,
+    io::Error,
+    io::Write,
+};
+
+pub const DERIVATION_PREFIX: &str = "Derive";
+pub const PAREN_OPEN: char = '(';
+pub const PAREN_CLOSE: char = ')';
+pub const BRACKET_OPEN: char = '[';
+pub const BRACKET_CLOSE: char = ']';
+pub const COMMA: char = ',';
+pub const QUOTE: char = '"';
+
+/// Something that can be written as ATerm.
+///
+/// Note that we mostly use explicit `write_*` calls
+/// instead since the serialization of the items depends on
+/// the context a lot.
+pub(crate) trait AtermWriteable {
+    fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()>;
+
+    fn aterm_bytes(&self) -> Vec<u8> {
+        let mut bytes = Vec::new();
+        self.aterm_write(&mut bytes)
+            .expect("unexpected write errors to Vec");
+        bytes
+    }
+}
+
+impl AtermWriteable for StorePathRef<'_> {
+    fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
+        write_char(writer, QUOTE)?;
+        writer.write_all(STORE_DIR_WITH_SLASH.as_bytes())?;
+        writer.write_all(nixbase32::encode(self.digest()).as_bytes())?;
+        write_char(writer, '-')?;
+        writer.write_all(self.name().as_bytes())?;
+        write_char(writer, QUOTE)?;
+        Ok(())
+    }
+}
+
+impl AtermWriteable for StorePath {
+    fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
+        let r: StorePathRef = self.into();
+        r.aterm_write(writer)
+    }
+}
+
+impl AtermWriteable for String {
+    fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
+        write_field(writer, self, true)
+    }
+}
+
+impl AtermWriteable for [u8; 32] {
+    fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
+        write_field(writer, HEXLOWER.encode(self), false)
+    }
+}
+
+// Writes a character to the writer.
+pub(crate) fn write_char(writer: &mut impl Write, c: char) -> io::Result<()> {
+    let mut buf = [0; 4];
+    let b = c.encode_utf8(&mut buf).as_bytes();
+    writer.write_all(b)
+}
+
+// Write a string `s` as a quoted field to the writer.
+// The `escape` argument controls whether escaping will be skipped.
+// This is the case if `s` is known to only contain characters that need no
+// escaping.
+pub(crate) fn write_field<S: AsRef<[u8]>>(
+    writer: &mut impl Write,
+    s: S,
+    escape: bool,
+) -> io::Result<()> {
+    write_char(writer, QUOTE)?;
+
+    if !escape {
+        writer.write_all(s.as_ref())?;
+    } else {
+        writer.write_all(&escape_bytes(s.as_ref()))?;
+    }
+
+    write_char(writer, QUOTE)?;
+
+    Ok(())
+}
+
+fn write_array_elements<S: AsRef<[u8]>>(
+    writer: &mut impl Write,
+    elements: &[S],
+) -> Result<(), io::Error> {
+    for (index, element) in elements.iter().enumerate() {
+        if index > 0 {
+            write_char(writer, COMMA)?;
+        }
+
+        write_field(writer, element, true)?;
+    }
+
+    Ok(())
+}
+
+pub(crate) fn write_outputs(
+    writer: &mut impl Write,
+    outputs: &BTreeMap<String, Output>,
+) -> Result<(), io::Error> {
+    write_char(writer, BRACKET_OPEN)?;
+    for (ii, (output_name, output)) in outputs.iter().enumerate() {
+        if ii > 0 {
+            write_char(writer, COMMA)?;
+        }
+
+        write_char(writer, PAREN_OPEN)?;
+
+        let path_str = output.path_str();
+        let mut elements: Vec<&str> = vec![output_name, &path_str];
+
+        let (mode_and_algo, digest) = match &output.ca_hash {
+            Some(ca_hash) => (
+                format!("{}{}", ca_kind_prefix(ca_hash), ca_hash.hash().algo()),
+                data_encoding::HEXLOWER.encode(ca_hash.hash().digest_as_bytes()),
+            ),
+            None => ("".to_string(), "".to_string()),
+        };
+
+        elements.push(&mode_and_algo);
+        elements.push(&digest);
+
+        write_array_elements(writer, &elements)?;
+
+        write_char(writer, PAREN_CLOSE)?;
+    }
+    write_char(writer, BRACKET_CLOSE)?;
+
+    Ok(())
+}
+
+pub(crate) fn write_input_derivations(
+    writer: &mut impl Write,
+    input_derivations: &BTreeMap<impl AtermWriteable, BTreeSet<String>>,
+) -> Result<(), io::Error> {
+    write_char(writer, BRACKET_OPEN)?;
+
+    for (ii, (input_derivation_aterm, output_names)) in input_derivations.iter().enumerate() {
+        if ii > 0 {
+            write_char(writer, COMMA)?;
+        }
+
+        write_char(writer, PAREN_OPEN)?;
+        input_derivation_aterm.aterm_write(writer)?;
+        write_char(writer, COMMA)?;
+
+        write_char(writer, BRACKET_OPEN)?;
+        write_array_elements(
+            writer,
+            &output_names
+                .iter()
+                .map(String::as_bytes)
+                .collect::<Vec<_>>(),
+        )?;
+        write_char(writer, BRACKET_CLOSE)?;
+
+        write_char(writer, PAREN_CLOSE)?;
+    }
+
+    write_char(writer, BRACKET_CLOSE)?;
+
+    Ok(())
+}
+
+pub(crate) fn write_input_sources(
+    writer: &mut impl Write,
+    input_sources: &BTreeSet<StorePath>,
+) -> Result<(), io::Error> {
+    write_char(writer, BRACKET_OPEN)?;
+    write_array_elements(
+        writer,
+        &input_sources
+            .iter()
+            .map(StorePath::to_absolute_path)
+            .collect::<Vec<_>>(),
+    )?;
+    write_char(writer, BRACKET_CLOSE)?;
+
+    Ok(())
+}
+
+pub(crate) fn write_system(writer: &mut impl Write, platform: &str) -> Result<(), Error> {
+    write_field(writer, platform, true)?;
+    Ok(())
+}
+
+pub(crate) fn write_builder(writer: &mut impl Write, builder: &str) -> Result<(), Error> {
+    write_field(writer, builder, true)?;
+    Ok(())
+}
+
+pub(crate) fn write_arguments(
+    writer: &mut impl Write,
+    arguments: &[String],
+) -> Result<(), io::Error> {
+    write_char(writer, BRACKET_OPEN)?;
+    write_array_elements(
+        writer,
+        &arguments
+            .iter()
+            .map(|s| s.as_bytes().to_vec().into())
+            .collect::<Vec<BString>>(),
+    )?;
+    write_char(writer, BRACKET_CLOSE)?;
+
+    Ok(())
+}
+
+pub(crate) fn write_environment<E, K, V>(
+    writer: &mut impl Write,
+    environment: E,
+) -> Result<(), io::Error>
+where
+    E: IntoIterator<Item = (K, V)>,
+    K: AsRef<[u8]>,
+    V: AsRef<[u8]>,
+{
+    write_char(writer, BRACKET_OPEN)?;
+
+    for (i, (k, v)) in environment.into_iter().enumerate() {
+        if i > 0 {
+            write_char(writer, COMMA)?;
+        }
+
+        write_char(writer, PAREN_OPEN)?;
+        write_field(writer, k, false)?;
+        write_char(writer, COMMA)?;
+        write_field(writer, v, true)?;
+        write_char(writer, PAREN_CLOSE)?;
+    }
+
+    write_char(writer, BRACKET_CLOSE)?;
+
+    Ok(())
+}
diff --git a/tvix/nix-compat/src/lib.rs b/tvix/nix-compat/src/lib.rs
new file mode 100644
index 0000000000..a71ede3eec
--- /dev/null
+++ b/tvix/nix-compat/src/lib.rs
@@ -0,0 +1,18 @@
+pub(crate) mod aterm;
+pub mod derivation;
+pub mod nar;
+pub mod narinfo;
+pub mod nixbase32;
+pub mod nixhash;
+pub mod path_info;
+pub mod store_path;
+
+#[cfg(feature = "wire")]
+pub mod wire;
+
+#[cfg(feature = "wire")]
+mod nix_daemon;
+#[cfg(feature = "wire")]
+pub use nix_daemon::worker_protocol;
+#[cfg(feature = "wire")]
+pub use nix_daemon::ProtocolVersion;
diff --git a/tvix/nix-compat/src/nar/mod.rs b/tvix/nix-compat/src/nar/mod.rs
new file mode 100644
index 0000000000..058977f4fc
--- /dev/null
+++ b/tvix/nix-compat/src/nar/mod.rs
@@ -0,0 +1,4 @@
+mod wire;
+
+pub mod reader;
+pub mod writer;
diff --git a/tvix/nix-compat/src/nar/reader/mod.rs b/tvix/nix-compat/src/nar/reader/mod.rs
new file mode 100644
index 0000000000..ecf9c3d789
--- /dev/null
+++ b/tvix/nix-compat/src/nar/reader/mod.rs
@@ -0,0 +1,479 @@
+//! Parser for the Nix archive format, aka NAR.
+//!
+//! NAR files (and their hashed representations) are used in C++ Nix for
+//! a variety of things, including addressing fixed-output derivations
+//! and transferring store paths between Nix stores.
+
+use std::io::{
+    self, BufRead,
+    ErrorKind::{InvalidData, UnexpectedEof},
+    Read, Write,
+};
+
+#[cfg(not(debug_assertions))]
+use std::marker::PhantomData;
+
+// Required reading for understanding this module.
+use crate::nar::wire;
+
+mod read;
+#[cfg(test)]
+mod test;
+
+pub type Reader<'a> = dyn BufRead + Send + 'a;
+
+struct ArchiveReader<'a, 'r> {
+    inner: &'a mut Reader<'r>,
+
+    /// In debug mode, also track when we need to abandon this archive reader.
+    /// The archive reader must be abandoned when:
+    ///   * An error is encountered at any point
+    ///   * A file or directory reader is dropped before being read entirely.
+    /// All of these checks vanish in release mode.
+    status: ArchiveReaderStatus<'a>,
+}
+
+macro_rules! try_or_poison {
+    ($it:expr, $ex:expr) => {
+        match $ex {
+            Ok(x) => x,
+            Err(e) => {
+                $it.status.poison();
+                return Err(e.into());
+            }
+        }
+    };
+}
+/// Start reading a NAR file from `reader`.
+pub fn open<'a, 'r>(reader: &'a mut Reader<'r>) -> io::Result<Node<'a, 'r>> {
+    read::token(reader, &wire::TOK_NAR)?;
+    Node::new(ArchiveReader {
+        inner: reader,
+        status: ArchiveReaderStatus::top(),
+    })
+}
+
+pub enum Node<'a, 'r> {
+    Symlink {
+        target: Vec<u8>,
+    },
+    File {
+        executable: bool,
+        reader: FileReader<'a, 'r>,
+    },
+    Directory(DirReader<'a, 'r>),
+}
+
+impl<'a, 'r> Node<'a, 'r> {
+    /// Start reading a [Node], matching the next [wire::Node].
+    ///
+    /// Reading the terminating [wire::TOK_PAR] is done immediately for [Node::Symlink],
+    /// but is otherwise left to [DirReader] or [FileReader].
+    fn new(mut reader: ArchiveReader<'a, 'r>) -> io::Result<Self> {
+        Ok(match read::tag(reader.inner)? {
+            wire::Node::Sym => {
+                let target =
+                    try_or_poison!(reader, read::bytes(reader.inner, wire::MAX_TARGET_LEN));
+
+                if target.is_empty() || target.contains(&0) {
+                    reader.status.poison();
+                    return Err(InvalidData.into());
+                }
+
+                try_or_poison!(reader, read::token(reader.inner, &wire::TOK_PAR));
+                reader.status.ready_parent(); // Immediately allow reading from parent again
+
+                Node::Symlink { target }
+            }
+            tag @ (wire::Node::Reg | wire::Node::Exe) => {
+                let len = try_or_poison!(&mut reader, read::u64(reader.inner));
+
+                Node::File {
+                    executable: tag == wire::Node::Exe,
+                    reader: FileReader::new(reader, len)?,
+                }
+            }
+            wire::Node::Dir => Node::Directory(DirReader::new(reader)),
+        })
+    }
+}
+
+/// File contents, readable through the [Read] trait.
+///
+/// It comes with some caveats:
+///  * You must always read the entire file, unless you intend to abandon the entire archive reader.
+///  * You must abandon the entire archive reader upon the first error.
+///
+/// It's fine to read exactly `reader.len()` bytes without ever seeing an explicit EOF.
+pub struct FileReader<'a, 'r> {
+    reader: ArchiveReader<'a, 'r>,
+    len: u64,
+    /// Truncated original file length for padding computation.
+    /// We only care about the 3 least significant bits; semantically, this is a u3.
+    pad: u8,
+}
+
+impl<'a, 'r> FileReader<'a, 'r> {
+    /// Instantiate a new reader, starting after [wire::TOK_REG] or [wire::TOK_EXE].
+    /// We handle the terminating [wire::TOK_PAR] on semantic EOF.
+    fn new(mut reader: ArchiveReader<'a, 'r>, len: u64) -> io::Result<Self> {
+        // For zero-length files, we have to read the terminating TOK_PAR
+        // immediately, since FileReader::read may never be called; we've
+        // already reached semantic EOF by definition.
+        if len == 0 {
+            read::token(reader.inner, &wire::TOK_PAR)?;
+            reader.status.ready_parent();
+        }
+
+        Ok(Self {
+            reader,
+            len,
+            pad: len as u8,
+        })
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.len == 0
+    }
+
+    pub fn len(&self) -> u64 {
+        self.len
+    }
+}
+
+impl FileReader<'_, '_> {
+    /// Equivalent to [BufRead::fill_buf]
+    ///
+    /// We can't directly implement [BufRead], because [FileReader::consume] needs
+    /// to perform fallible I/O.
+    pub fn fill_buf(&mut self) -> io::Result<&[u8]> {
+        if self.is_empty() {
+            return Ok(&[]);
+        }
+
+        self.reader.check_correct();
+
+        let mut buf = try_or_poison!(self.reader, self.reader.inner.fill_buf());
+
+        if buf.is_empty() {
+            self.reader.status.poison();
+            return Err(UnexpectedEof.into());
+        }
+
+        if buf.len() as u64 > self.len {
+            buf = &buf[..self.len as usize];
+        }
+
+        Ok(buf)
+    }
+
+    /// Analogous to [BufRead::consume], differing only in that it needs
+    /// to perform I/O in order to read padding and terminators.
+    pub fn consume(&mut self, n: usize) -> io::Result<()> {
+        if n == 0 {
+            return Ok(());
+        }
+
+        self.reader.check_correct();
+
+        self.len = self
+            .len
+            .checked_sub(n as u64)
+            .expect("consumed bytes past EOF");
+
+        self.reader.inner.consume(n);
+
+        if self.is_empty() {
+            self.finish()?;
+        }
+
+        Ok(())
+    }
+
+    /// Copy the (remaining) contents of the file into `dst`.
+    pub fn copy(&mut self, mut dst: impl Write) -> io::Result<()> {
+        while !self.is_empty() {
+            let buf = self.fill_buf()?;
+            let n = try_or_poison!(self.reader, dst.write(buf));
+            self.consume(n)?;
+        }
+
+        Ok(())
+    }
+}
+
+impl Read for FileReader<'_, '_> {
+    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
+        if buf.is_empty() || self.is_empty() {
+            return Ok(0);
+        }
+
+        self.reader.check_correct();
+
+        if buf.len() as u64 > self.len {
+            buf = &mut buf[..self.len as usize];
+        }
+
+        let n = try_or_poison!(self.reader, self.reader.inner.read(buf));
+        self.len -= n as u64;
+
+        if n == 0 {
+            self.reader.status.poison();
+            return Err(UnexpectedEof.into());
+        }
+
+        if self.is_empty() {
+            self.finish()?;
+        }
+
+        Ok(n)
+    }
+}
+
+impl FileReader<'_, '_> {
+    /// We've reached semantic EOF, consume and verify the padding and terminating TOK_PAR.
+    /// Files are padded to 64 bits (8 bytes), just like any other byte string in the wire format.
+    fn finish(&mut self) -> io::Result<()> {
+        let pad = (self.pad & 7) as usize;
+
+        if pad != 0 {
+            let mut buf = [0; 8];
+            try_or_poison!(self.reader, self.reader.inner.read_exact(&mut buf[pad..]));
+
+            if buf != [0; 8] {
+                self.reader.status.poison();
+                return Err(InvalidData.into());
+            }
+        }
+
+        try_or_poison!(self.reader, read::token(self.reader.inner, &wire::TOK_PAR));
+
+        // Done with reading this file, allow going back up the chain of readers
+        self.reader.status.ready_parent();
+
+        Ok(())
+    }
+}
+
+/// A directory iterator, yielding a sequence of [Node]s.
+/// It must be fully consumed before reading further from the [DirReader] that produced it, if any.
+pub struct DirReader<'a, 'r> {
+    reader: ArchiveReader<'a, 'r>,
+    /// Previous directory entry name.
+    /// We have to hang onto this to enforce name monotonicity.
+    prev_name: Option<Vec<u8>>,
+}
+
+pub struct Entry<'a, 'r> {
+    pub name: Vec<u8>,
+    pub node: Node<'a, 'r>,
+}
+
+impl<'a, 'r> DirReader<'a, 'r> {
+    fn new(reader: ArchiveReader<'a, 'r>) -> Self {
+        Self {
+            reader,
+            prev_name: None,
+        }
+    }
+
+    /// Read the next [Entry] from the directory.
+    ///
+    /// We explicitly don't implement [Iterator], since treating this as
+    /// a regular Rust iterator will surely lead you astray.
+    ///
+    ///  * You must always consume the entire iterator, unless you abandon the entire archive reader.
+    ///  * You must abandon the entire archive reader on the first error.
+    ///  * You must abandon the directory reader upon the first [None].
+    ///  * Even if you know the amount of elements up front, you must keep reading until you encounter [None].
+    #[allow(clippy::should_implement_trait)]
+    pub fn next(&mut self) -> io::Result<Option<Entry<'_, 'r>>> {
+        self.reader.check_correct();
+
+        // COME FROM the previous iteration: if we've already read an entry,
+        // read its terminating TOK_PAR here.
+        if self.prev_name.is_some() {
+            try_or_poison!(self.reader, read::token(self.reader.inner, &wire::TOK_PAR));
+        }
+
+        // Determine if there are more entries to follow
+        if let wire::Entry::None = try_or_poison!(self.reader, read::tag(self.reader.inner)) {
+            // We've reached the end of this directory.
+            self.reader.status.ready_parent();
+            return Ok(None);
+        }
+
+        let name = try_or_poison!(
+            self.reader,
+            read::bytes(self.reader.inner, wire::MAX_NAME_LEN)
+        );
+
+        if name.is_empty()
+            || name.contains(&0)
+            || name.contains(&b'/')
+            || name == b"."
+            || name == b".."
+        {
+            self.reader.status.poison();
+            return Err(InvalidData.into());
+        }
+
+        // Enforce strict monotonicity of directory entry names.
+        match &mut self.prev_name {
+            None => {
+                self.prev_name = Some(name.clone());
+            }
+            Some(prev_name) => {
+                if *prev_name >= name {
+                    self.reader.status.poison();
+                    return Err(InvalidData.into());
+                }
+
+                name[..].clone_into(prev_name);
+            }
+        }
+
+        try_or_poison!(self.reader, read::token(self.reader.inner, &wire::TOK_NOD));
+
+        Ok(Some(Entry {
+            name,
+            // Don't need to worry about poisoning here: Node::new will do it for us if needed
+            node: Node::new(self.reader.child())?,
+        }))
+    }
+}
+
+/// We use a stack of statuses to:
+///   * Share poisoned state across all objects from the same underlying reader,
+///     so we can check they are abandoned when an error occurs
+///   * Make sure only the most recently created object is read from, and is fully exhausted
+///     before anything it was created from is used again.
+enum ArchiveReaderStatus<'a> {
+    #[cfg(not(debug_assertions))]
+    None(PhantomData<&'a ()>),
+    #[cfg(debug_assertions)]
+    StackTop { poisoned: bool, ready: bool },
+    #[cfg(debug_assertions)]
+    StackChild {
+        poisoned: &'a mut bool,
+        parent_ready: &'a mut bool,
+        ready: bool,
+    },
+}
+
+impl ArchiveReaderStatus<'_> {
+    fn top() -> Self {
+        #[cfg(debug_assertions)]
+        {
+            ArchiveReaderStatus::StackTop {
+                poisoned: false,
+                ready: true,
+            }
+        }
+
+        #[cfg(not(debug_assertions))]
+        ArchiveReaderStatus::None(PhantomData)
+    }
+
+    /// Poison all the objects sharing the same reader, to be used when an error occurs
+    fn poison(&mut self) {
+        match self {
+            #[cfg(not(debug_assertions))]
+            ArchiveReaderStatus::None(_) => {}
+            #[cfg(debug_assertions)]
+            ArchiveReaderStatus::StackTop { poisoned: x, .. } => *x = true,
+            #[cfg(debug_assertions)]
+            ArchiveReaderStatus::StackChild { poisoned: x, .. } => **x = true,
+        }
+    }
+
+    /// Mark the parent as ready, allowing it to be used again and preventing this reference to the reader being used again.
+    fn ready_parent(&mut self) {
+        match self {
+            #[cfg(not(debug_assertions))]
+            ArchiveReaderStatus::None(_) => {}
+            #[cfg(debug_assertions)]
+            ArchiveReaderStatus::StackTop { ready, .. } => {
+                *ready = false;
+            }
+            #[cfg(debug_assertions)]
+            ArchiveReaderStatus::StackChild {
+                ready,
+                parent_ready,
+                ..
+            } => {
+                *ready = false;
+                **parent_ready = true;
+            }
+        };
+    }
+
+    fn poisoned(&self) -> bool {
+        match self {
+            #[cfg(not(debug_assertions))]
+            ArchiveReaderStatus::None(_) => false,
+            #[cfg(debug_assertions)]
+            ArchiveReaderStatus::StackTop { poisoned, .. } => *poisoned,
+            #[cfg(debug_assertions)]
+            ArchiveReaderStatus::StackChild { poisoned, .. } => **poisoned,
+        }
+    }
+
+    fn ready(&self) -> bool {
+        match self {
+            #[cfg(not(debug_assertions))]
+            ArchiveReaderStatus::None(_) => true,
+            #[cfg(debug_assertions)]
+            ArchiveReaderStatus::StackTop { ready, .. } => *ready,
+            #[cfg(debug_assertions)]
+            ArchiveReaderStatus::StackChild { ready, .. } => *ready,
+        }
+    }
+}
+
+impl<'a, 'r> ArchiveReader<'a, 'r> {
+    /// Create a new child reader from this one.
+    /// In debug mode, this reader will panic if called before the new child is exhausted / calls `ready_parent`
+    fn child(&mut self) -> ArchiveReader<'_, 'r> {
+        ArchiveReader {
+            inner: self.inner,
+            #[cfg(not(debug_assertions))]
+            status: ArchiveReaderStatus::None(PhantomData),
+            #[cfg(debug_assertions)]
+            status: match &mut self.status {
+                ArchiveReaderStatus::StackTop { poisoned, ready } => {
+                    *ready = false;
+                    ArchiveReaderStatus::StackChild {
+                        poisoned,
+                        parent_ready: ready,
+                        ready: true,
+                    }
+                }
+                ArchiveReaderStatus::StackChild {
+                    poisoned, ready, ..
+                } => {
+                    *ready = false;
+                    ArchiveReaderStatus::StackChild {
+                        poisoned,
+                        parent_ready: ready,
+                        ready: true,
+                    }
+                }
+            },
+        }
+    }
+
+    /// Check the reader is in the correct status.
+    /// Only does anything when debug assertions are on.
+    #[inline(always)]
+    fn check_correct(&self) {
+        assert!(
+            !self.status.poisoned(),
+            "Archive reader used after it was meant to be abandoned!"
+        );
+        assert!(
+            self.status.ready(),
+            "Non-ready archive reader used! (Should've been reading from something else)"
+        );
+    }
+}
diff --git a/tvix/nix-compat/src/nar/reader/read.rs b/tvix/nix-compat/src/nar/reader/read.rs
new file mode 100644
index 0000000000..1ce1613764
--- /dev/null
+++ b/tvix/nix-compat/src/nar/reader/read.rs
@@ -0,0 +1,109 @@
+//! Helpers for reading [crate::nar::wire] format.
+
+use std::io::{
+    self,
+    ErrorKind::{Interrupted, InvalidData, UnexpectedEof},
+};
+
+use super::Reader;
+use crate::nar::wire::Tag;
+
+/// Consume a little-endian [prim@u64] from the reader.
+pub fn u64(reader: &mut Reader) -> io::Result<u64> {
+    let mut buf = [0; 8];
+    reader.read_exact(&mut buf)?;
+    Ok(u64::from_le_bytes(buf))
+}
+
+/// Consume a byte string of up to `max_len` bytes from the reader.
+pub fn bytes(reader: &mut Reader, max_len: usize) -> io::Result<Vec<u8>> {
+    assert!(max_len <= isize::MAX as usize);
+
+    // read the length, and reject excessively large values
+    let len = self::u64(reader)?;
+    if len > max_len as u64 {
+        return Err(InvalidData.into());
+    }
+    // we know the length fits in a usize now
+    let len = len as usize;
+
+    // read the data and padding into a buffer
+    let buf_len = (len + 7) & !7;
+    let mut buf = vec![0; buf_len];
+    reader.read_exact(&mut buf)?;
+
+    // verify that the padding is all zeroes
+    for b in buf.drain(len..) {
+        if b != 0 {
+            return Err(InvalidData.into());
+        }
+    }
+
+    Ok(buf)
+}
+
+/// Consume a known token from the reader.
+pub fn token<const N: usize>(reader: &mut Reader, token: &[u8; N]) -> io::Result<()> {
+    let mut buf = [0u8; N];
+
+    // This implements something similar to [Read::read_exact], but verifies that
+    // the input data matches the token while we read it. These two slices respectively
+    // represent the remaining token to be verified, and the remaining input buffer.
+    let mut token = &token[..];
+    let mut buf = &mut buf[..];
+
+    while !token.is_empty() {
+        match reader.read(buf) {
+            Ok(0) => {
+                return Err(UnexpectedEof.into());
+            }
+            Ok(n) => {
+                let (t, b);
+                (t, token) = token.split_at(n);
+                (b, buf) = buf.split_at_mut(n);
+
+                if t != b {
+                    return Err(InvalidData.into());
+                }
+            }
+            Err(e) => {
+                if e.kind() != Interrupted {
+                    return Err(e);
+                }
+            }
+        }
+    }
+
+    Ok(())
+}
+
+/// Consume a [Tag] from the reader.
+pub fn tag<T: Tag>(reader: &mut Reader) -> io::Result<T> {
+    let mut buf = T::make_buf();
+    let buf = buf.as_mut();
+
+    // first read the known minimum length…
+    reader.read_exact(&mut buf[..T::MIN])?;
+
+    // then decide which tag we're expecting
+    let tag = T::from_u8(buf[T::OFF]).ok_or(InvalidData)?;
+    let (head, tail) = tag.as_bytes().split_at(T::MIN);
+
+    // make sure what we've read so far is valid
+    if buf[..T::MIN] != *head {
+        return Err(InvalidData.into());
+    }
+
+    // …then read the rest, if any
+    if !tail.is_empty() {
+        let rest = tail.len();
+        reader.read_exact(&mut buf[..rest])?;
+
+        // and make sure it's what we expect
+        if buf[..rest] != *tail {
+            return Err(InvalidData.into());
+        }
+    }
+
+    Ok(tag)
+}
diff --git a/tvix/nix-compat/src/nar/reader/test.rs b/tvix/nix-compat/src/nar/reader/test.rs
new file mode 100644
index 0000000000..02dc4767c9
--- /dev/null
+++ b/tvix/nix-compat/src/nar/reader/test.rs
@@ -0,0 +1,278 @@
+use std::io::Read;
+
+use crate::nar;
+
+#[test]
+fn symlink() {
+    let mut f = std::io::Cursor::new(include_bytes!("../tests/symlink.nar"));
+    let node = nar::reader::open(&mut f).unwrap();
+
+    match node {
+        nar::reader::Node::Symlink { target } => {
+            assert_eq!(
+                &b"/nix/store/somewhereelse"[..],
+                &target,
+                "target must match"
+            );
+        }
+        _ => panic!("unexpected type"),
+    }
+}
+
+#[test]
+fn file() {
+    let mut f = std::io::Cursor::new(include_bytes!("../tests/helloworld.nar"));
+    let node = nar::reader::open(&mut f).unwrap();
+
+    match node {
+        nar::reader::Node::File {
+            executable,
+            mut reader,
+        } => {
+            assert!(!executable);
+            let mut buf = vec![];
+            reader.read_to_end(&mut buf).expect("read must succeed");
+            assert_eq!(&b"Hello World!"[..], &buf);
+        }
+        _ => panic!("unexpected type"),
+    }
+}
+
+#[test]
+fn complicated() {
+    let mut f = std::io::Cursor::new(include_bytes!("../tests/complicated.nar"));
+    let node = nar::reader::open(&mut f).unwrap();
+
+    match node {
+        nar::reader::Node::Directory(mut dir_reader) => {
+            // first entry is .keep, an empty regular file.
+            must_read_file(
+                ".keep",
+                dir_reader
+                    .next()
+                    .expect("next must succeed")
+                    .expect("must be some"),
+            );
+
+            // second entry is aa, a symlink to /nix/store/somewhereelse
+            must_be_symlink(
+                "aa",
+                "/nix/store/somewhereelse",
+                dir_reader
+                    .next()
+                    .expect("next must be some")
+                    .expect("must be some"),
+            );
+
+            {
+                // third entry is a directory called "keep"
+                let entry = dir_reader
+                    .next()
+                    .expect("next must be some")
+                    .expect("must be some");
+
+                assert_eq!(&b"keep"[..], &entry.name);
+
+                match entry.node {
+                    nar::reader::Node::Directory(mut subdir_reader) => {
+                        {
+                            // first entry is .keep, an empty regular file.
+                            let entry = subdir_reader
+                                .next()
+                                .expect("next must succeed")
+                                .expect("must be some");
+
+                            must_read_file(".keep", entry);
+                        }
+
+                        // we must read the None
+                        assert!(
+                            subdir_reader.next().expect("next must succeed").is_none(),
+                            "keep directory contains only .keep"
+                        );
+                    }
+                    _ => panic!("unexpected type for keep/.keep"),
+                }
+            };
+
+            // reading more entries yields None (and we actually must read until this)
+            assert!(dir_reader.next().expect("must succeed").is_none());
+        }
+        _ => panic!("unexpected type"),
+    }
+}
+
+#[test]
+#[should_panic]
+fn file_read_abandoned() {
+    let mut f = std::io::Cursor::new(include_bytes!("../tests/complicated.nar"));
+    let node = nar::reader::open(&mut f).unwrap();
+
+    match node {
+        nar::reader::Node::Directory(mut dir_reader) => {
+            // first entry is .keep, an empty regular file.
+            {
+                let entry = dir_reader
+                    .next()
+                    .expect("next must succeed")
+                    .expect("must be some");
+
+                assert_eq!(&b".keep"[..], &entry.name);
+                // don't bother to finish reading it.
+            };
+
+            // this should panic (not return an error), because we are meant to abandon the archive reader now.
+            assert!(dir_reader.next().expect("must succeed").is_none());
+        }
+        _ => panic!("unexpected type"),
+    }
+}
+
+#[test]
+#[should_panic]
+fn dir_read_abandoned() {
+    let mut f = std::io::Cursor::new(include_bytes!("../tests/complicated.nar"));
+    let node = nar::reader::open(&mut f).unwrap();
+
+    match node {
+        nar::reader::Node::Directory(mut dir_reader) => {
+            // first entry is .keep, an empty regular file.
+            must_read_file(
+                ".keep",
+                dir_reader
+                    .next()
+                    .expect("next must succeed")
+                    .expect("must be some"),
+            );
+
+            // second entry is aa, a symlink to /nix/store/somewhereelse
+            must_be_symlink(
+                "aa",
+                "/nix/store/somewhereelse",
+                dir_reader
+                    .next()
+                    .expect("next must be some")
+                    .expect("must be some"),
+            );
+
+            {
+                // third entry is a directory called "keep"
+                let entry = dir_reader
+                    .next()
+                    .expect("next must be some")
+                    .expect("must be some");
+
+                assert_eq!(&b"keep"[..], &entry.name);
+
+                match entry.node {
+                    nar::reader::Node::Directory(_) => {
+                        // don't finish using it, which poisons the archive reader
+                    }
+                    _ => panic!("unexpected type for keep/.keep"),
+                }
+            };
+
+            // this should panic, because we didn't finish reading the child subdirectory
+            assert!(dir_reader.next().expect("must succeed").is_none());
+        }
+        _ => panic!("unexpected type"),
+    }
+}
+
+#[test]
+#[should_panic]
+fn dir_read_after_none() {
+    let mut f = std::io::Cursor::new(include_bytes!("../tests/complicated.nar"));
+    let node = nar::reader::open(&mut f).unwrap();
+
+    match node {
+        nar::reader::Node::Directory(mut dir_reader) => {
+            // first entry is .keep, an empty regular file.
+            must_read_file(
+                ".keep",
+                dir_reader
+                    .next()
+                    .expect("next must succeed")
+                    .expect("must be some"),
+            );
+
+            // second entry is aa, a symlink to /nix/store/somewhereelse
+            must_be_symlink(
+                "aa",
+                "/nix/store/somewhereelse",
+                dir_reader
+                    .next()
+                    .expect("next must be some")
+                    .expect("must be some"),
+            );
+
+            {
+                // third entry is a directory called "keep"
+                let entry = dir_reader
+                    .next()
+                    .expect("next must be some")
+                    .expect("must be some");
+
+                assert_eq!(&b"keep"[..], &entry.name);
+
+                match entry.node {
+                    nar::reader::Node::Directory(mut subdir_reader) => {
+                        // first entry is .keep, an empty regular file.
+                        must_read_file(
+                            ".keep",
+                            subdir_reader
+                                .next()
+                                .expect("next must succeed")
+                                .expect("must be some"),
+                        );
+
+                        // we must read the None
+                        assert!(
+                            subdir_reader.next().expect("next must succeed").is_none(),
+                            "keep directory contains only .keep"
+                        );
+                    }
+                    _ => panic!("unexpected type for keep/.keep"),
+                }
+            };
+
+            // reading more entries yields None (and we actually must read until this)
+            assert!(dir_reader.next().expect("must succeed").is_none());
+
+            // this should panic, because we already got a none so we're meant to stop.
+            dir_reader.next().unwrap();
+            unreachable!()
+        }
+        _ => panic!("unexpected type"),
+    }
+}
+
+fn must_read_file(name: &'static str, entry: nar::reader::Entry<'_, '_>) {
+    assert_eq!(name.as_bytes(), &entry.name);
+
+    match entry.node {
+        nar::reader::Node::File {
+            executable,
+            mut reader,
+        } => {
+            assert!(!executable);
+            assert_eq!(reader.read(&mut [0]).unwrap(), 0);
+        }
+        _ => panic!("unexpected type for {}", name),
+    }
+}
+
+fn must_be_symlink(
+    name: &'static str,
+    exp_target: &'static str,
+    entry: nar::reader::Entry<'_, '_>,
+) {
+    assert_eq!(name.as_bytes(), &entry.name);
+
+    match entry.node {
+        nar::reader::Node::Symlink { target } => {
+            assert_eq!(exp_target.as_bytes(), &target);
+        }
+        _ => panic!("unexpected type for {}", name),
+    }
+}
diff --git a/tvix/nix-compat/src/nar/tests/complicated.nar b/tvix/nix-compat/src/nar/tests/complicated.nar
new file mode 100644
index 0000000000..6a137f5fbb
--- /dev/null
+++ b/tvix/nix-compat/src/nar/tests/complicated.nar
Binary files differdiff --git a/tvix/nix-compat/src/nar/tests/helloworld.nar b/tvix/nix-compat/src/nar/tests/helloworld.nar
new file mode 100644
index 0000000000..2e12681152
--- /dev/null
+++ b/tvix/nix-compat/src/nar/tests/helloworld.nar
Binary files differdiff --git a/tvix/nix-compat/src/nar/tests/symlink.nar b/tvix/nix-compat/src/nar/tests/symlink.nar
new file mode 100644
index 0000000000..7990e4ad5b
--- /dev/null
+++ b/tvix/nix-compat/src/nar/tests/symlink.nar
Binary files differdiff --git a/tvix/nix-compat/src/nar/wire/mod.rs b/tvix/nix-compat/src/nar/wire/mod.rs
new file mode 100644
index 0000000000..b9e0212495
--- /dev/null
+++ b/tvix/nix-compat/src/nar/wire/mod.rs
@@ -0,0 +1,133 @@
+//! NAR wire format, without I/O details, since those differ between
+//! the synchronous and asynchronous implementations.
+//!
+//! The wire format is an S-expression format, encoded onto the wire
+//! using simple encoding rules.
+//!
+//! # Encoding
+//!
+//! Lengths are represented as 64-bit unsigned integers in little-endian
+//! format. Byte strings, including file contents and syntactic strings
+//! part of the grammar, are prefixed by their 64-bit length, and padded
+//! to 8-byte (64-bit) alignment with zero bytes. The zero-length string
+//! is therefore encoded as eight zero bytes representing its length.
+//!
+//! # Grammar
+//!
+//! The NAR grammar is as follows:
+//! ```plain
+//! archive ::= "nix-archive-1" node
+//!
+//! node ::= "(" "type" "symlink" "target" string ")"
+//!      ||= "(" "type" "regular" ("executable" "")? "contents" string ")"
+//!      ||= "(" "type" "directory" entry* ")"
+//!
+//! entry ::= "entry" "(" "name" string "node" node ")"
+//! ```
+//!
+//! We rewrite it to pull together the purely syntactic elements into
+//! unified tokens, producing an equivalent grammar that can be parsed
+//! and serialized more elegantly:
+//! ```plain
+//! archive ::= TOK_NAR node
+//! node ::= TOK_SYM string             TOK_PAR
+//!      ||= (TOK_REG | TOK_EXE) string TOK_PAR
+//!      ||= TOK_DIR entry*             TOK_PAR
+//!
+//! entry ::= TOK_ENT string TOK_NOD node TOK_PAR
+//!
+//! TOK_NAR ::= "nix-archive-1" "(" "type"
+//! TOK_SYM ::= "symlink" "target"
+//! TOK_REG ::= "regular" "contents"
+//! TOK_EXE ::= "regular" "executable" ""
+//! TOK_DIR ::= "directory"
+//! TOK_ENT ::= "entry" "(" "name"
+//! TOK_NOD ::= "node" "(" "type"
+//! TOK_PAR ::= ")"
+//! ```
+//!
+//! # Restrictions
+//!
+//! NOTE: These restrictions are not (and cannot be) enforced by this module,
+//! but must be enforced by its consumers, [super::reader] and [super::writer].
+//!
+//! Directory entry names cannot have the reserved names `.` and `..`, nor contain
+//! forward slashes. They must appear in strictly ascending lexicographic order
+//! within a directory, and can be at most [MAX_NAME_LEN] bytes in length.
+//!
+//! Symlink targets can be at most [MAX_TARGET_LEN] bytes in length.
+//!
+//! Neither is permitted to be empty, or contain null bytes.
+
+// These values are the standard Linux length limits
+/// Maximum length of a directory entry name
+pub const MAX_NAME_LEN: usize = 255;
+/// Maximum length of a symlink target
+pub const MAX_TARGET_LEN: usize = 4095;
+
+#[cfg(test)]
+fn token(xs: &[&str]) -> Vec<u8> {
+    let mut out = vec![];
+    for x in xs {
+        let len = x.len() as u64;
+        out.extend_from_slice(&len.to_le_bytes());
+        out.extend_from_slice(x.as_bytes());
+
+        let n = x.len() & 7;
+        if n != 0 {
+            const ZERO: [u8; 8] = [0; 8];
+            out.extend_from_slice(&ZERO[n..]);
+        }
+    }
+    out
+}
+
+pub const TOK_NAR: [u8; 56] = *b"\x0d\0\0\0\0\0\0\0nix-archive-1\0\0\0\x01\0\0\0\0\0\0\0(\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0type\0\0\0\0";
+pub const TOK_SYM: [u8; 32] = *b"\x07\0\0\0\0\0\0\0symlink\0\x06\0\0\0\0\0\0\0target\0\0";
+pub const TOK_REG: [u8; 32] = *b"\x07\0\0\0\0\0\0\0regular\0\x08\0\0\0\0\0\0\0contents";
+pub const TOK_EXE: [u8; 64] = *b"\x07\0\0\0\0\0\0\0regular\0\x0a\0\0\0\0\0\0\0executable\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0contents";
+pub const TOK_DIR: [u8; 24] = *b"\x09\0\0\0\0\0\0\0directory\0\0\0\0\0\0\0";
+pub const TOK_ENT: [u8; 48] = *b"\x05\0\0\0\0\0\0\0entry\0\0\0\x01\0\0\0\0\0\0\0(\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0name\0\0\0\0";
+pub const TOK_NOD: [u8; 48] = *b"\x04\0\0\0\0\0\0\0node\0\0\0\0\x01\0\0\0\0\0\0\0(\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0type\0\0\0\0";
+pub const TOK_PAR: [u8; 16] = *b"\x01\0\0\0\0\0\0\0)\0\0\0\0\0\0\0";
+
+#[test]
+fn tokens() {
+    let cases: &[(&[u8], &[&str])] = &[
+        (&TOK_NAR, &["nix-archive-1", "(", "type"]),
+        (&TOK_SYM, &["symlink", "target"]),
+        (&TOK_REG, &["regular", "contents"]),
+        (&TOK_EXE, &["regular", "executable", "", "contents"]),
+        (&TOK_DIR, &["directory"]),
+        (&TOK_ENT, &["entry", "(", "name"]),
+        (&TOK_NOD, &["node", "(", "type"]),
+        (&TOK_PAR, &[")"]),
+    ];
+
+    for &(tok, xs) in cases {
+        assert_eq!(tok, token(xs));
+    }
+}
+
+pub use tag::Tag;
+mod tag;
+
+tag::make! {
+    /// These are the node tokens, succeeding [TOK_NAR] or [TOK_NOD],
+    /// and preceding the next variable-length element.
+    pub enum Node[16] {
+        Sym = TOK_SYM,
+        Reg = TOK_REG,
+        Exe = TOK_EXE,
+        Dir = TOK_DIR,
+    }
+
+    /// Directory entry or terminator
+    pub enum Entry[0] {
+        /// End of directory
+        None = TOK_PAR,
+        /// Directory entry
+        /// Followed by a name string, [TOK_NOD], and a [Node].
+        Some = TOK_ENT,
+    }
+}
diff --git a/tvix/nix-compat/src/nar/wire/tag.rs b/tvix/nix-compat/src/nar/wire/tag.rs
new file mode 100644
index 0000000000..4982a0d707
--- /dev/null
+++ b/tvix/nix-compat/src/nar/wire/tag.rs
@@ -0,0 +1,166 @@
+/// A type implementing Tag represents a static hash set of byte strings,
+/// with a very simple perfect hash function: every element has a unique
+/// discriminant at a common byte offset. The values of the type represent
+/// the members by this single discriminant byte; they are indices into the
+/// hash set.
+pub trait Tag: Sized {
+    /// Discriminant offset
+    const OFF: usize;
+    /// Minimum variant length
+    const MIN: usize;
+
+    /// Minimal suitably sized buffer for reading the wire representation
+    ///
+    /// HACK: This is a workaround for const generics limitations.
+    type Buf: AsMut<[u8]> + Send;
+
+    /// Make an instance of [Self::Buf]
+    fn make_buf() -> Self::Buf;
+
+    /// Convert a discriminant into the corresponding variant
+    fn from_u8(x: u8) -> Option<Self>;
+
+    /// Convert a variant back into the wire representation
+    fn as_bytes(&self) -> &'static [u8];
+}
+
+/// Generate an enum implementing [Tag], enforcing at compile time that
+/// the discriminant values are distinct.
+macro_rules! make {
+    (
+        $(
+            $(#[doc = $doc:expr])*
+            $vis:vis enum $Enum:ident[$off:expr] {
+                $(
+                    $(#[doc = $var_doc:expr])*
+                    $Var:ident = $TOK:ident,
+                )+
+            }
+        )*
+    ) => {
+        $(
+            $(#[doc = $doc])*
+            #[derive(Debug, PartialEq, Eq)]
+            #[repr(u8)]
+            $vis enum $Enum {
+                $(
+                    $(#[doc = $var_doc])*
+                    $Var = $TOK[$Enum::OFF]
+                ),+
+            }
+
+            impl Tag for $Enum {
+                /// Discriminant offset
+                const OFF: usize = $off;
+                /// Minimum variant length
+                const MIN: usize = tag::min_of(&[$($TOK.len()),+]);
+
+                /// Minimal suitably sized buffer for reading the wire representation
+                type Buf = [u8; tag::buf_of(&[$($TOK.len()),+])];
+
+                /// Make an instance of [Self::Buf]
+                #[inline(always)]
+                fn make_buf() -> Self::Buf {
+                    [0u8; tag::buf_of(&[$($TOK.len()),+])]
+                }
+
+                /// Convert a discriminant into the corresponding variant
+                #[inline(always)]
+                fn from_u8(x: u8) -> Option<Self> {
+                    #[allow(non_upper_case_globals)]
+                    mod __variant {
+                        $(
+                            pub const $Var: u8 = super::$Enum::$Var as u8;
+                        )+
+                    }
+
+                    match x {
+                        $(__variant::$Var => Some(Self::$Var),)+
+                        _ => None
+                    }
+                }
+
+                /// Convert a variant back into the wire representation
+                #[inline(always)]
+                fn as_bytes(&self) -> &'static [u8] {
+                    match self {
+                        $(Self::$Var => &$TOK,)+
+                    }
+                }
+            }
+        )*
+    };
+}
+
+// The following functions are written somewhat unusually,
+// since they're const functions that cannot use iterators.
+
+/// Maximum element of a slice
+const fn max_of(mut xs: &[usize]) -> usize {
+    let mut y = usize::MIN;
+    while let &[x, ref tail @ ..] = xs {
+        y = if x > y { x } else { y };
+        xs = tail;
+    }
+    y
+}
+
+/// Minimum element of a slice
+pub const fn min_of(mut xs: &[usize]) -> usize {
+    let mut y = usize::MAX;
+    while let &[x, ref tail @ ..] = xs {
+        y = if x < y { x } else { y };
+        xs = tail;
+    }
+    y
+}
+
+/// Minimum buffer size to contain either of `0..Tag::MIN` and `Tag::MIN..`
+/// at a particular time, for all possible tag wire representations, given
+/// the sizes of all wire representations.
+///
+/// # Example
+///
+/// ```plain
+/// OFF = 16
+/// MIN = 24
+/// MAX = 64
+///
+/// BUF = max(MIN, MAX-MIN)
+///     = max(24, 64-24)
+///     = max(24, 40)
+///     = 40
+/// ```
+pub const fn buf_of(xs: &[usize]) -> usize {
+    max_of(&[min_of(xs), max_of(xs) - min_of(xs)])
+}
+
+pub(crate) use make;
+
+#[cfg(test)]
+mod test {
+    use super::super::tag::{self, Tag};
+
+    const TOK_A: [u8; 3] = [0xed, 0xef, 0x1c];
+    const TOK_B: [u8; 3] = [0xed, 0xf0, 0x1c];
+
+    const OFFSET: usize = 1;
+
+    make! {
+        enum Token[OFFSET] {
+            A = TOK_A,
+            B = TOK_B,
+        }
+    }
+
+    #[test]
+    fn example() {
+        assert_eq!(Token::from_u8(0xed), None);
+
+        let tag = Token::from_u8(0xef).unwrap();
+        assert_eq!(tag.as_bytes(), &TOK_A[..]);
+
+        let tag = Token::from_u8(0xf0).unwrap();
+        assert_eq!(tag.as_bytes(), &TOK_B[..]);
+    }
+}
diff --git a/tvix/nix-compat/src/nar/writer/async.rs b/tvix/nix-compat/src/nar/writer/async.rs
new file mode 100644
index 0000000000..a2ce68fc3c
--- /dev/null
+++ b/tvix/nix-compat/src/nar/writer/async.rs
@@ -0,0 +1,235 @@
+//! Implements an interface for writing the Nix archive format (NAR).
+//!
+//! NAR files (and their hashed representations) are used in C++ Nix for
+//! addressing fixed-output derivations and a variety of other things.
+//!
+//! NAR files can be output to any type that implements [`AsyncWrite`], and content
+//! can be read from any type that implementes [`AsyncBufRead`].
+//!
+//! Writing a single file might look like this:
+//!
+//! ```rust
+//! # futures::executor::block_on(async {
+//! # use tokio::io::BufReader;
+//! # let some_file: Vec<u8> = vec![0, 1, 2, 3, 4];
+//!
+//! // Output location to write the NAR to.
+//! let mut sink: Vec<u8> = Vec::new();
+//!
+//! // Instantiate writer for this output location.
+//! let mut nar = nix_compat::nar::writer::r#async::open(&mut sink).await?;
+//!
+//! // Acquire metadata for the single file to output, and pass it in a
+//! // `BufRead`-implementing type.
+//!
+//! let executable = false;
+//! let size = some_file.len() as u64;
+//! let mut reader = BufReader::new(some_file.as_slice());
+//! nar.file(executable, size, &mut reader).await?;
+//! # Ok::<(), std::io::Error>(())
+//! # });
+//! ```
+
+use crate::nar::wire;
+use std::{
+    io::{
+        self,
+        ErrorKind::{InvalidInput, UnexpectedEof},
+    },
+    pin::Pin,
+};
+use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt};
+
+/// Convenience type alias for types implementing [`AsyncWrite`].
+pub type Writer<'a> = dyn AsyncWrite + Unpin + Send + 'a;
+
+/// Create a new NAR, writing the output to the specified writer.
+pub async fn open<'a, 'w: 'a>(writer: &'a mut Writer<'w>) -> io::Result<Node<'a, 'w>> {
+    let mut node = Node { writer };
+    node.write(&wire::TOK_NAR).await?;
+    Ok(node)
+}
+
+/// Single node in a NAR file.
+///
+/// A NAR can be thought of as a tree of nodes represented by this type. Each
+/// node can be a file, a symlink or a directory containing other nodes.
+pub struct Node<'a, 'w: 'a> {
+    writer: &'a mut Writer<'w>,
+}
+
+impl<'a, 'w> Node<'a, 'w> {
+    async fn write(&mut self, data: &[u8]) -> io::Result<()> {
+        self.writer.write_all(data).await
+    }
+
+    async fn pad(&mut self, n: u64) -> io::Result<()> {
+        match (n & 7) as usize {
+            0 => Ok(()),
+            n => self.write(&[0; 8][n..]).await,
+        }
+    }
+
+    /// Make this node a symlink.
+    pub async fn symlink(mut self, target: &[u8]) -> io::Result<()> {
+        debug_assert!(
+            target.len() <= wire::MAX_TARGET_LEN,
+            "target.len() > {}",
+            wire::MAX_TARGET_LEN
+        );
+        debug_assert!(!target.is_empty(), "target is empty");
+        debug_assert!(!target.contains(&0), "target contains null byte");
+
+        self.write(&wire::TOK_SYM).await?;
+        self.write(&target.len().to_le_bytes()).await?;
+        self.write(target).await?;
+        self.pad(target.len() as u64).await?;
+        self.write(&wire::TOK_PAR).await?;
+        Ok(())
+    }
+
+    /// Make this node a single file.
+    pub async fn file(
+        mut self,
+        executable: bool,
+        size: u64,
+        reader: &mut (dyn AsyncBufRead + Unpin + Send),
+    ) -> io::Result<()> {
+        self.write(if executable {
+            &wire::TOK_EXE
+        } else {
+            &wire::TOK_REG
+        })
+        .await?;
+
+        self.write(&size.to_le_bytes()).await?;
+
+        let mut need = size;
+        while need != 0 {
+            let data = reader.fill_buf().await?;
+
+            if data.is_empty() {
+                return Err(UnexpectedEof.into());
+            }
+
+            let n = need.min(data.len() as u64) as usize;
+            self.write(&data[..n]).await?;
+
+            need -= n as u64;
+            Pin::new(&mut *reader).consume(n);
+        }
+
+        // bail if there's still data left in the passed reader.
+        // This uses the same code as [BufRead::has_data_left] (unstable).
+        if reader.fill_buf().await.map(|b| !b.is_empty())? {
+            return Err(io::Error::new(
+                InvalidInput,
+                "reader contained more data than specified size",
+            ));
+        }
+
+        self.pad(size).await?;
+        self.write(&wire::TOK_PAR).await?;
+
+        Ok(())
+    }
+
+    /// Make this node a directory, the content of which is set using the
+    /// resulting [`Directory`] value.
+    ///
+    /// It is the caller's responsibility to invoke [`Directory::close`],
+    /// or invalid archives will be produced silently.
+    pub async fn directory(mut self) -> io::Result<Directory<'a, 'w>> {
+        self.write(&wire::TOK_DIR).await?;
+        Ok(Directory::new(self))
+    }
+}
+
+#[cfg(debug_assertions)]
+type Name = Vec<u8>;
+#[cfg(not(debug_assertions))]
+type Name = ();
+
+fn into_name(_name: &[u8]) -> Name {
+    #[cfg(debug_assertions)]
+    _name.to_owned()
+}
+
+/// Content of a NAR node that represents a directory.
+pub struct Directory<'a, 'w> {
+    node: Node<'a, 'w>,
+    prev_name: Option<Name>,
+}
+
+impl<'a, 'w> Directory<'a, 'w> {
+    fn new(node: Node<'a, 'w>) -> Self {
+        Self {
+            node,
+            prev_name: None,
+        }
+    }
+
+    /// Add an entry to the directory.
+    ///
+    /// The entry is simply another [`Node`], which can then be filled like the
+    /// root of a NAR (including, of course, by nesting directories).
+    ///
+    /// It is the caller's responsibility to ensure that directory entries are
+    /// written in order of ascending name. If this is not ensured, this method
+    /// may panic or silently produce invalid archives.
+    pub async fn entry(&mut self, name: &[u8]) -> io::Result<Node<'_, 'w>> {
+        debug_assert!(
+            name.len() <= wire::MAX_NAME_LEN,
+            "name.len() > {}",
+            wire::MAX_NAME_LEN
+        );
+        debug_assert!(!name.is_empty(), "name is empty");
+        debug_assert!(!name.contains(&0), "name contains null byte");
+        debug_assert!(!name.contains(&b'/'), "name contains {:?}", '/');
+        debug_assert!(name != b".", "name == {:?}", ".");
+        debug_assert!(name != b"..", "name == {:?}", "..");
+
+        match self.prev_name {
+            None => {
+                self.prev_name = Some(into_name(name));
+            }
+            Some(ref mut _prev_name) => {
+                #[cfg(debug_assertions)]
+                {
+                    use bstr::ByteSlice;
+                    assert!(
+                        &**_prev_name < name,
+                        "misordered names: {:?} >= {:?}",
+                        _prev_name.as_bstr(),
+                        name.as_bstr()
+                    );
+                    name.clone_into(_prev_name);
+                }
+                self.node.write(&wire::TOK_PAR).await?;
+            }
+        }
+
+        self.node.write(&wire::TOK_ENT).await?;
+        self.node.write(&name.len().to_le_bytes()).await?;
+        self.node.write(name).await?;
+        self.node.pad(name.len() as u64).await?;
+        self.node.write(&wire::TOK_NOD).await?;
+
+        Ok(Node {
+            writer: &mut *self.node.writer,
+        })
+    }
+
+    /// Close a directory and write terminators for the directory to the NAR.
+    ///
+    /// **Important:** This *must* be called when all entries have been written
+    /// in a directory, otherwise the resulting NAR file will be invalid.
+    pub async fn close(mut self) -> io::Result<()> {
+        if self.prev_name.is_some() {
+            self.node.write(&wire::TOK_PAR).await?;
+        }
+
+        self.node.write(&wire::TOK_PAR).await?;
+        Ok(())
+    }
+}
diff --git a/tvix/nix-compat/src/nar/writer/mod.rs b/tvix/nix-compat/src/nar/writer/mod.rs
new file mode 100644
index 0000000000..fe8ccccb37
--- /dev/null
+++ b/tvix/nix-compat/src/nar/writer/mod.rs
@@ -0,0 +1,9 @@
+pub use sync::*;
+
+pub mod sync;
+
+#[cfg(test)]
+mod test;
+
+#[cfg(feature = "async")]
+pub mod r#async;
diff --git a/tvix/nix-compat/src/nar/writer/sync.rs b/tvix/nix-compat/src/nar/writer/sync.rs
new file mode 100644
index 0000000000..6270129028
--- /dev/null
+++ b/tvix/nix-compat/src/nar/writer/sync.rs
@@ -0,0 +1,224 @@
+//! Implements an interface for writing the Nix archive format (NAR).
+//!
+//! NAR files (and their hashed representations) are used in C++ Nix for
+//! addressing fixed-output derivations and a variety of other things.
+//!
+//! NAR files can be output to any type that implements [`Write`], and content
+//! can be read from any type that implementes [`BufRead`].
+//!
+//! Writing a single file might look like this:
+//!
+//! ```rust
+//! # use std::io::BufReader;
+//! # let some_file: Vec<u8> = vec![0, 1, 2, 3, 4];
+//!
+//! // Output location to write the NAR to.
+//! let mut sink: Vec<u8> = Vec::new();
+//!
+//! // Instantiate writer for this output location.
+//! let mut nar = nix_compat::nar::writer::open(&mut sink)?;
+//!
+//! // Acquire metadata for the single file to output, and pass it in a
+//! // `BufRead`-implementing type.
+//!
+//! let executable = false;
+//! let size = some_file.len() as u64;
+//! let mut reader = BufReader::new(some_file.as_slice());
+//! nar.file(executable, size, &mut reader)?;
+//! # Ok::<(), std::io::Error>(())
+//! ```
+
+use crate::nar::wire;
+use std::io::{
+    self, BufRead,
+    ErrorKind::{InvalidInput, UnexpectedEof},
+    Write,
+};
+
+/// Convenience type alias for types implementing [`Write`].
+pub type Writer<'a> = dyn Write + Send + 'a;
+
+/// Create a new NAR, writing the output to the specified writer.
+pub fn open<'a, 'w: 'a>(writer: &'a mut Writer<'w>) -> io::Result<Node<'a, 'w>> {
+    let mut node = Node { writer };
+    node.write(&wire::TOK_NAR)?;
+    Ok(node)
+}
+
+/// Single node in a NAR file.
+///
+/// A NAR can be thought of as a tree of nodes represented by this type. Each
+/// node can be a file, a symlink or a directory containing other nodes.
+pub struct Node<'a, 'w: 'a> {
+    writer: &'a mut Writer<'w>,
+}
+
+impl<'a, 'w> Node<'a, 'w> {
+    fn write(&mut self, data: &[u8]) -> io::Result<()> {
+        self.writer.write_all(data)
+    }
+
+    fn pad(&mut self, n: u64) -> io::Result<()> {
+        match (n & 7) as usize {
+            0 => Ok(()),
+            n => self.write(&[0; 8][n..]),
+        }
+    }
+
+    /// Make this node a symlink.
+    pub fn symlink(mut self, target: &[u8]) -> io::Result<()> {
+        debug_assert!(
+            target.len() <= wire::MAX_TARGET_LEN,
+            "target.len() > {}",
+            wire::MAX_TARGET_LEN
+        );
+        debug_assert!(!target.is_empty(), "target is empty");
+        debug_assert!(!target.contains(&0), "target contains null byte");
+
+        self.write(&wire::TOK_SYM)?;
+        self.write(&target.len().to_le_bytes())?;
+        self.write(target)?;
+        self.pad(target.len() as u64)?;
+        self.write(&wire::TOK_PAR)?;
+        Ok(())
+    }
+
+    /// Make this node a single file.
+    pub fn file(mut self, executable: bool, size: u64, reader: &mut dyn BufRead) -> io::Result<()> {
+        self.write(if executable {
+            &wire::TOK_EXE
+        } else {
+            &wire::TOK_REG
+        })?;
+
+        self.write(&size.to_le_bytes())?;
+
+        let mut need = size;
+        while need != 0 {
+            let data = reader.fill_buf()?;
+
+            if data.is_empty() {
+                return Err(UnexpectedEof.into());
+            }
+
+            let n = need.min(data.len() as u64) as usize;
+            self.write(&data[..n])?;
+
+            need -= n as u64;
+            reader.consume(n);
+        }
+
+        // bail if there's still data left in the passed reader.
+        // This uses the same code as [BufRead::has_data_left] (unstable).
+        if reader.fill_buf().map(|b| !b.is_empty())? {
+            return Err(io::Error::new(
+                InvalidInput,
+                "reader contained more data than specified size",
+            ));
+        }
+
+        self.pad(size)?;
+        self.write(&wire::TOK_PAR)?;
+
+        Ok(())
+    }
+
+    /// Make this node a directory, the content of which is set using the
+    /// resulting [`Directory`] value.
+    ///
+    /// It is the caller's responsibility to invoke [`Directory::close`],
+    /// or invalid archives will be produced silently.
+    pub fn directory(mut self) -> io::Result<Directory<'a, 'w>> {
+        self.write(&wire::TOK_DIR)?;
+        Ok(Directory::new(self))
+    }
+}
+
+#[cfg(debug_assertions)]
+type Name = Vec<u8>;
+#[cfg(not(debug_assertions))]
+type Name = ();
+
+fn into_name(_name: &[u8]) -> Name {
+    #[cfg(debug_assertions)]
+    _name.to_owned()
+}
+
+/// Content of a NAR node that represents a directory.
+pub struct Directory<'a, 'w> {
+    node: Node<'a, 'w>,
+    prev_name: Option<Name>,
+}
+
+impl<'a, 'w> Directory<'a, 'w> {
+    fn new(node: Node<'a, 'w>) -> Self {
+        Self {
+            node,
+            prev_name: None,
+        }
+    }
+
+    /// Add an entry to the directory.
+    ///
+    /// The entry is simply another [`Node`], which can then be filled like the
+    /// root of a NAR (including, of course, by nesting directories).
+    ///
+    /// It is the caller's responsibility to ensure that directory entries are
+    /// written in order of ascending name. If this is not ensured, this method
+    /// may panic or silently produce invalid archives.
+    pub fn entry(&mut self, name: &[u8]) -> io::Result<Node<'_, 'w>> {
+        debug_assert!(
+            name.len() <= wire::MAX_NAME_LEN,
+            "name.len() > {}",
+            wire::MAX_NAME_LEN
+        );
+        debug_assert!(!name.is_empty(), "name is empty");
+        debug_assert!(!name.contains(&0), "name contains null byte");
+        debug_assert!(!name.contains(&b'/'), "name contains {:?}", '/');
+        debug_assert!(name != b".", "name == {:?}", ".");
+        debug_assert!(name != b"..", "name == {:?}", "..");
+
+        match self.prev_name {
+            None => {
+                self.prev_name = Some(into_name(name));
+            }
+            Some(ref mut _prev_name) => {
+                #[cfg(debug_assertions)]
+                {
+                    use bstr::ByteSlice;
+                    assert!(
+                        &**_prev_name < name,
+                        "misordered names: {:?} >= {:?}",
+                        _prev_name.as_bstr(),
+                        name.as_bstr()
+                    );
+                    name.clone_into(_prev_name);
+                }
+                self.node.write(&wire::TOK_PAR)?;
+            }
+        }
+
+        self.node.write(&wire::TOK_ENT)?;
+        self.node.write(&name.len().to_le_bytes())?;
+        self.node.write(name)?;
+        self.node.pad(name.len() as u64)?;
+        self.node.write(&wire::TOK_NOD)?;
+
+        Ok(Node {
+            writer: &mut *self.node.writer,
+        })
+    }
+
+    /// Close a directory and write terminators for the directory to the NAR.
+    ///
+    /// **Important:** This *must* be called when all entries have been written
+    /// in a directory, otherwise the resulting NAR file will be invalid.
+    pub fn close(mut self) -> io::Result<()> {
+        if self.prev_name.is_some() {
+            self.node.write(&wire::TOK_PAR)?;
+        }
+
+        self.node.write(&wire::TOK_PAR)?;
+        Ok(())
+    }
+}
diff --git a/tvix/nix-compat/src/nar/writer/test.rs b/tvix/nix-compat/src/nar/writer/test.rs
new file mode 100644
index 0000000000..d7f18a49af
--- /dev/null
+++ b/tvix/nix-compat/src/nar/writer/test.rs
@@ -0,0 +1,128 @@
+use crate::nar;
+
+#[test]
+fn symlink() {
+    let mut buf = vec![];
+    let node = nar::writer::open(&mut buf).unwrap();
+
+    node.symlink("/nix/store/somewhereelse".as_bytes()).unwrap();
+
+    assert_eq!(include_bytes!("../tests/symlink.nar"), buf.as_slice());
+}
+
+#[cfg(feature = "async")]
+#[tokio::test]
+async fn symlink_async() {
+    let mut buf = vec![];
+
+    let node = nar::writer::r#async::open(&mut buf).await.unwrap();
+    node.symlink("/nix/store/somewhereelse".as_bytes())
+        .await
+        .unwrap();
+
+    assert_eq!(include_bytes!("../tests/symlink.nar"), buf.as_slice());
+}
+
+#[test]
+fn file() {
+    let mut buf = vec![];
+    let node = nar::writer::open(&mut buf).unwrap();
+
+    let file_contents = "Hello World!".to_string();
+    node.file(
+        false,
+        file_contents.len() as u64,
+        &mut std::io::Cursor::new(file_contents),
+    )
+    .unwrap();
+
+    assert_eq!(include_bytes!("../tests/helloworld.nar"), buf.as_slice());
+}
+
+#[cfg(feature = "async")]
+#[tokio::test]
+async fn file_async() {
+    use std::io::Cursor;
+
+    let mut buf = vec![];
+
+    let node = nar::writer::r#async::open(&mut buf).await.unwrap();
+
+    let file_contents = "Hello World!".to_string();
+    node.file(
+        false,
+        file_contents.len() as u64,
+        &mut Cursor::new(file_contents),
+    )
+    .await
+    .unwrap();
+
+    assert_eq!(include_bytes!("../tests/helloworld.nar"), buf.as_slice());
+}
+
+#[test]
+fn complicated() {
+    let mut buf = vec![];
+    let node = nar::writer::open(&mut buf).unwrap();
+
+    let mut dir_node = node.directory().unwrap();
+
+    let e = dir_node.entry(".keep".as_bytes()).unwrap();
+    e.file(false, 0, &mut std::io::Cursor::new([]))
+        .expect("read .keep must succeed");
+
+    let e = dir_node.entry("aa".as_bytes()).unwrap();
+    e.symlink("/nix/store/somewhereelse".as_bytes())
+        .expect("symlink must succeed");
+
+    let e = dir_node.entry("keep".as_bytes()).unwrap();
+    let mut subdir_node = e.directory().expect("directory must succeed");
+
+    let e_sub = subdir_node
+        .entry(".keep".as_bytes())
+        .expect("subdir entry must succeed");
+    e_sub.file(false, 0, &mut std::io::Cursor::new([])).unwrap();
+
+    // close the subdir, and then the dir, which is required.
+    subdir_node.close().unwrap();
+    dir_node.close().unwrap();
+
+    assert_eq!(include_bytes!("../tests/complicated.nar"), buf.as_slice());
+}
+
+#[cfg(feature = "async")]
+#[tokio::test]
+async fn complicated_async() {
+    use std::io::Cursor;
+
+    let mut buf = vec![];
+
+    let node = nar::writer::r#async::open(&mut buf).await.unwrap();
+
+    let mut dir_node = node.directory().await.unwrap();
+
+    let e = dir_node.entry(".keep".as_bytes()).await.unwrap();
+    e.file(false, 0, &mut Cursor::new([]))
+        .await
+        .expect("read .keep must succeed");
+
+    let e = dir_node.entry("aa".as_bytes()).await.unwrap();
+    e.symlink("/nix/store/somewhereelse".as_bytes())
+        .await
+        .expect("symlink must succeed");
+
+    let e = dir_node.entry("keep".as_bytes()).await.unwrap();
+    let mut subdir_node = e.directory().await.expect("directory must succeed");
+
+    let e_sub = subdir_node
+        .entry(".keep".as_bytes())
+        .await
+        .expect("subdir entry must succeed");
+    e_sub.file(false, 0, &mut Cursor::new([])).await.unwrap();
+
+    // close the subdir, and then the dir, which is required.
+    subdir_node.close().await.unwrap();
+    dir_node.close().await.unwrap();
+
+    assert_eq!(include_bytes!("../tests/complicated.nar"), buf.as_slice());
+}
diff --git a/tvix/nix-compat/src/narinfo/fingerprint.rs b/tvix/nix-compat/src/narinfo/fingerprint.rs
new file mode 100644
index 0000000000..3e02aca571
--- /dev/null
+++ b/tvix/nix-compat/src/narinfo/fingerprint.rs
@@ -0,0 +1,50 @@
+use crate::{nixbase32, store_path::StorePathRef};
+
+/// Computes the fingerprint string for certain fields in a [super::NarInfo].
+/// This fingerprint is signed by an ed25519 key, and in the case of a Nix HTTP
+/// Binary cache, included in the NARInfo files served from there.
+pub fn fingerprint<'a, R: Iterator<Item = &'a StorePathRef<'a>>>(
+    store_path: &StorePathRef,
+    nar_sha256: &[u8; 32],
+    nar_size: u64,
+    references: R,
+) -> String {
+    format!(
+        "1;{};sha256:{};{};{}",
+        store_path.to_absolute_path(),
+        nixbase32::encode(nar_sha256),
+        nar_size,
+        // references are absolute paths, joined with `,`.
+        references
+            .map(|r| r.to_absolute_path())
+            .collect::<Vec<String>>()
+            .join(",")
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::narinfo::NarInfo;
+
+    const NARINFO_STR: &str = r#"StorePath: /nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin
+URL: nar/05ra3y72i3qjri7xskf9qj8kb29r6naqy1sqpbs3azi3xcigmj56.nar.xz
+Compression: xz
+FileHash: sha256:05ra3y72i3qjri7xskf9qj8kb29r6naqy1sqpbs3azi3xcigmj56
+FileSize: 68852
+NarHash: sha256:1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0
+NarSize: 196040
+References: 0jqd0rlxzra1rs38rdxl43yh6rxchgc6-curl-7.82.0 6w8g7njm4mck5dmjxws0z1xnrxvl81xa-glibc-2.34-115 j5jxw3iy7bbz4a57fh9g2xm2gxmyal8h-zlib-1.2.12 yxvjs9drzsphm9pcf42a4byzj1kb9m7k-openssl-1.1.1n
+Deriver: 5rwxzi7pal3qhpsyfc16gzkh939q1np6-curl-7.82.0.drv
+Sig: cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==
+Sig: test1:519iiVLx/c4Rdt5DNt6Y2Jm6hcWE9+XY69ygiWSZCNGVcmOcyL64uVAJ3cV8vaTusIZdbTnYo9Y7vDNeTmmMBQ==
+"#;
+
+    #[test]
+    fn fingerprint() {
+        let parsed = NarInfo::parse(NARINFO_STR).expect("must parse");
+        assert_eq!(
+            "1;/nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin;sha256:1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0;196040;/nix/store/0jqd0rlxzra1rs38rdxl43yh6rxchgc6-curl-7.82.0,/nix/store/6w8g7njm4mck5dmjxws0z1xnrxvl81xa-glibc-2.34-115,/nix/store/j5jxw3iy7bbz4a57fh9g2xm2gxmyal8h-zlib-1.2.12,/nix/store/yxvjs9drzsphm9pcf42a4byzj1kb9m7k-openssl-1.1.1n",
+            parsed.fingerprint()
+        );
+    }
+}
diff --git a/tvix/nix-compat/src/narinfo/mod.rs b/tvix/nix-compat/src/narinfo/mod.rs
new file mode 100644
index 0000000000..b1c10bceb2
--- /dev/null
+++ b/tvix/nix-compat/src/narinfo/mod.rs
@@ -0,0 +1,527 @@
+//! NAR info files describe a store path in a traditional Nix binary cache.
+//! Over the wire, they are formatted as "Key: value" pairs separated by newlines.
+//!
+//! It contains four kinds of information:
+//! 1. the description of the store path itself
+//!    * store path prefix, digest, and name
+//!    * NAR hash and size
+//!    * references
+//! 2. authenticity information
+//!    * zero or more signatures over that description
+//!    * an optional [CAHash] for content-addressed paths (fixed outputs, sources, and derivations)
+//! 3. derivation metadata
+//!    * deriver (the derivation that produced this path)
+//!    * system (the system value of that derivation)
+//! 4. cache-specific information
+//!    * URL of the compressed NAR, relative to the NAR info file
+//!    * compression algorithm used for the NAR
+//!    * hash and size of the compressed NAR
+
+use bitflags::bitflags;
+use data_encoding::HEXLOWER;
+use std::{
+    fmt::{self, Display},
+    mem,
+};
+
+use crate::{nixbase32, nixhash::CAHash, store_path::StorePathRef};
+
+mod fingerprint;
+mod public_keys;
+mod signature;
+
+pub use fingerprint::fingerprint;
+
+pub use public_keys::{Error as PubKeyError, PubKey};
+pub use signature::{Error as SignatureError, Signature};
+
+#[derive(Debug)]
+pub struct NarInfo<'a> {
+    pub flags: Flags,
+    // core (authenticated, but unverified here)
+    /// Store path described by this [NarInfo]
+    pub store_path: StorePathRef<'a>,
+    /// SHA-256 digest of the NAR file
+    pub nar_hash: [u8; 32],
+    /// Size of the NAR file in bytes
+    pub nar_size: u64,
+    /// Store paths known to be referenced by the contents
+    pub references: Vec<StorePathRef<'a>>,
+    // authenticity
+    /// Ed25519 signature over the path fingerprint
+    pub signatures: Vec<Signature<'a>>,
+    /// Content address (for content-defined paths)
+    pub ca: Option<CAHash>,
+    // derivation metadata
+    /// Nix system triple of [NarInfo::deriver]
+    pub system: Option<&'a str>,
+    /// Store path of the derivation that produced this. The last .drv suffix is stripped.
+    pub deriver: Option<StorePathRef<'a>>,
+    // cache-specific untrusted metadata
+    /// Relative URL of the compressed NAR file
+    pub url: &'a str,
+    /// Compression method of the NAR file
+    /// `None` means `Compression: none`.
+    ///
+    /// Nix interprets a missing `Compression` field as `Some("bzip2")`,
+    /// so we do as well. We haven't found any examples of this in the
+    /// wild, not even in the cache.nixos.org dataset.
+    pub compression: Option<&'a str>,
+    /// SHA-256 digest of the file at `url`
+    pub file_hash: Option<[u8; 32]>,
+    /// Size of the file at `url` in bytes
+    pub file_size: Option<u64>,
+}
+
+bitflags! {
+    /// TODO(edef): be conscious of these when roundtripping
+    #[derive(Debug, Copy, Clone)]
+    pub struct Flags: u8 {
+        const UNKNOWN_FIELD = 1 << 0;
+        const COMPRESSION_DEFAULT = 1 << 1;
+        // Format quirks encountered in the cache.nixos.org dataset
+        const REFERENCES_OUT_OF_ORDER = 1 << 2;
+        const NAR_HASH_HEX = 1 << 3;
+    }
+}
+
+impl<'a> NarInfo<'a> {
+    pub fn parse(input: &'a str) -> Result<Self, Error> {
+        let mut flags = Flags::empty();
+        let mut store_path = None;
+        let mut url = None;
+        let mut compression = None;
+        let mut file_hash = None;
+        let mut file_size = None;
+        let mut nar_hash = None;
+        let mut nar_size = None;
+        let mut references = None;
+        let mut system = None;
+        let mut deriver = None;
+        let mut signatures = vec![];
+        let mut ca = None;
+
+        for line in input.lines() {
+            let (tag, val) = line
+                .split_once(':')
+                .ok_or_else(|| Error::InvalidLine(line.to_string()))?;
+
+            let val = val
+                .strip_prefix(' ')
+                .ok_or_else(|| Error::InvalidLine(line.to_string()))?;
+
+            match tag {
+                "StorePath" => {
+                    let val = val
+                        .strip_prefix("/nix/store/")
+                        .ok_or(Error::InvalidStorePath(
+                            crate::store_path::Error::MissingStoreDir,
+                        ))?;
+                    let val = StorePathRef::from_bytes(val.as_bytes())
+                        .map_err(Error::InvalidStorePath)?;
+
+                    if store_path.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                "URL" => {
+                    if val.is_empty() {
+                        return Err(Error::EmptyField(tag.to_string()));
+                    }
+
+                    if url.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                "Compression" => {
+                    if val.is_empty() {
+                        return Err(Error::EmptyField(tag.to_string()));
+                    }
+
+                    if compression.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                "FileHash" => {
+                    let val = val
+                        .strip_prefix("sha256:")
+                        .ok_or_else(|| Error::MissingPrefixForHash(tag.to_string()))?;
+                    let val = nixbase32::decode_fixed::<32>(val)
+                        .map_err(|e| Error::UnableToDecodeHash(tag.to_string(), e))?;
+
+                    if file_hash.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                "FileSize" => {
+                    let val = val
+                        .parse::<u64>()
+                        .map_err(|_| Error::UnableToParseSize(tag.to_string(), val.to_string()))?;
+
+                    if file_size.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                "NarHash" => {
+                    let val = val
+                        .strip_prefix("sha256:")
+                        .ok_or_else(|| Error::MissingPrefixForHash(tag.to_string()))?;
+
+                    let val = if val.len() != HEXLOWER.encode_len(32) {
+                        nixbase32::decode_fixed::<32>(val)
+                    } else {
+                        flags |= Flags::NAR_HASH_HEX;
+
+                        let val = val.as_bytes();
+                        let mut buf = [0u8; 32];
+
+                        HEXLOWER
+                            .decode_mut(val, &mut buf)
+                            .map_err(|e| e.error)
+                            .map(|_| buf)
+                    };
+
+                    let val = val.map_err(|e| Error::UnableToDecodeHash(tag.to_string(), e))?;
+
+                    if nar_hash.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                "NarSize" => {
+                    let val = val
+                        .parse::<u64>()
+                        .map_err(|_| Error::UnableToParseSize(tag.to_string(), val.to_string()))?;
+
+                    if nar_size.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                "References" => {
+                    let val: Vec<StorePathRef> = if !val.is_empty() {
+                        let mut prev = "";
+                        val.split(' ')
+                            .enumerate()
+                            .map(|(i, s)| {
+                                // TODO(edef): track *duplicates* if this occurs
+                                if mem::replace(&mut prev, s) >= s {
+                                    flags |= Flags::REFERENCES_OUT_OF_ORDER;
+                                }
+
+                                StorePathRef::from_bytes(s.as_bytes())
+                                    .map_err(|err| Error::InvalidReference(i, err))
+                            })
+                            .collect::<Result<_, _>>()?
+                    } else {
+                        vec![]
+                    };
+
+                    if references.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                "System" => {
+                    if val.is_empty() {
+                        return Err(Error::EmptyField(tag.to_string()));
+                    }
+
+                    if system.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                "Deriver" => {
+                    match val.strip_suffix(".drv") {
+                        Some(val) => {
+                            let val = StorePathRef::from_bytes(val.as_bytes())
+                                .map_err(Error::InvalidDeriverStorePath)?;
+
+                            if deriver.replace(val).is_some() {
+                                return Err(Error::DuplicateField(tag.to_string()));
+                            }
+                        }
+                        None => {
+                            return Err(Error::InvalidDeriverStorePathMissingSuffix);
+                        }
+                    };
+                }
+                "Sig" => {
+                    let val = Signature::parse(val)
+                        .map_err(|e| Error::UnableToParseSignature(signatures.len(), e))?;
+
+                    signatures.push(val);
+                }
+                "CA" => {
+                    let val = CAHash::from_nix_hex_str(val)
+                        .ok_or_else(|| Error::UnableToParseCA(val.to_string()))?;
+
+                    if ca.replace(val).is_some() {
+                        return Err(Error::DuplicateField(tag.to_string()));
+                    }
+                }
+                _ => {
+                    flags |= Flags::UNKNOWN_FIELD;
+                }
+            }
+        }
+
+        Ok(NarInfo {
+            store_path: store_path.ok_or(Error::MissingField("StorePath"))?,
+            nar_hash: nar_hash.ok_or(Error::MissingField("NarHash"))?,
+            nar_size: nar_size.ok_or(Error::MissingField("NarSize"))?,
+            references: references.ok_or(Error::MissingField("References"))?,
+            signatures,
+            ca,
+            system,
+            deriver,
+            url: url.ok_or(Error::MissingField("URL"))?,
+            compression: match compression {
+                Some("none") => None,
+                None => {
+                    flags |= Flags::COMPRESSION_DEFAULT;
+                    Some("bzip2")
+                }
+                _ => compression,
+            },
+            file_hash,
+            file_size,
+            flags,
+        })
+    }
+
+    /// Computes the fingerprint string for certain fields in this [NarInfo].
+    /// This fingerprint is signed in [self.signatures].
+    pub fn fingerprint(&self) -> String {
+        fingerprint(
+            &self.store_path,
+            &self.nar_hash,
+            self.nar_size,
+            self.references.iter(),
+        )
+    }
+}
+
+impl Display for NarInfo<'_> {
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(w, "StorePath: /nix/store/{}", self.store_path)?;
+        writeln!(w, "URL: {}", self.url)?;
+
+        if let Some(compression) = self.compression {
+            writeln!(w, "Compression: {compression}")?;
+        }
+
+        if let Some(file_hash) = self.file_hash {
+            writeln!(w, "FileHash: sha256:{}", nixbase32::encode(&file_hash),)?;
+        }
+
+        if let Some(file_size) = self.file_size {
+            writeln!(w, "FileSize: {file_size}")?;
+        }
+
+        writeln!(w, "NarHash: sha256:{}", nixbase32::encode(&self.nar_hash),)?;
+        writeln!(w, "NarSize: {}", self.nar_size)?;
+
+        write!(w, "References:")?;
+        if self.references.is_empty() {
+            write!(w, " ")?;
+        } else {
+            for path in &self.references {
+                write!(w, " {path}")?;
+            }
+        }
+        writeln!(w)?;
+
+        if let Some(deriver) = &self.deriver {
+            writeln!(w, "Deriver: {deriver}.drv")?;
+        }
+
+        if let Some(system) = self.system {
+            writeln!(w, "System: {system}")?;
+        }
+
+        for sig in &self.signatures {
+            writeln!(w, "Sig: {sig}")?;
+        }
+
+        if let Some(ca) = &self.ca {
+            writeln!(w, "CA: {}", ca.to_nix_nixbase32_string())?;
+        }
+
+        Ok(())
+    }
+}
+
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+    #[error("duplicate field: {0}")]
+    DuplicateField(String),
+
+    #[error("missing field: {0}")]
+    MissingField(&'static str),
+
+    #[error("invalid line: {0}")]
+    InvalidLine(String),
+
+    #[error("invalid StorePath: {0}")]
+    InvalidStorePath(crate::store_path::Error),
+
+    #[error("field {0} may not be empty string")]
+    EmptyField(String),
+
+    #[error("invalid {0}: {1}")]
+    UnableToParseSize(String, String),
+
+    #[error("unable to parse #{0} reference: {1}")]
+    InvalidReference(usize, crate::store_path::Error),
+
+    #[error("invalid Deriver store path: {0}")]
+    InvalidDeriverStorePath(crate::store_path::Error),
+
+    #[error("invalid Deriver store path, must end with .drv")]
+    InvalidDeriverStorePathMissingSuffix,
+
+    #[error("missing prefix for {0}")]
+    MissingPrefixForHash(String),
+
+    #[error("unable to decode {0}: {1}")]
+    UnableToDecodeHash(String, data_encoding::DecodeError),
+
+    #[error("unable to parse signature #{0}: {1}")]
+    UnableToParseSignature(usize, SignatureError),
+
+    #[error("unable to parse CA field: {0}")]
+    UnableToParseCA(String),
+}
+
+#[cfg(test)]
+mod test {
+    use hex_literal::hex;
+    use lazy_static::lazy_static;
+    use pretty_assertions::assert_eq;
+    use std::{io, str};
+
+    use crate::{
+        nixhash::{CAHash, NixHash},
+        store_path::StorePathRef,
+    };
+
+    use super::{Flags, NarInfo};
+
+    lazy_static! {
+        static ref CASES: &'static [&'static str] = {
+            let data = zstd::decode_all(io::Cursor::new(include_bytes!(
+                "../../testdata/narinfo.zst"
+            )))
+            .unwrap();
+            let data = str::from_utf8(Vec::leak(data)).unwrap();
+            Vec::leak(
+                data.split_inclusive("\n\n")
+                    .map(|s| s.strip_suffix('\n').unwrap())
+                    .collect::<Vec<_>>(),
+            )
+        };
+    }
+
+    #[test]
+    fn roundtrip() {
+        for &input in *CASES {
+            let parsed = NarInfo::parse(input).expect("should parse");
+            let output = format!("{parsed}");
+            assert_eq!(input, output, "should roundtrip");
+        }
+    }
+
+    #[test]
+    fn references_out_of_order() {
+        let parsed = NarInfo::parse(
+            r#"StorePath: /nix/store/xi429w4ddvb1r77978hm7jfb2jsn559r-gcc-3.4.6
+URL: nar/1hr09cgkyw1hcsfkv5qp5jlpmf2mqrkrqs3xj5zklq9c1h9544ff.nar.bz2
+Compression: bzip2
+FileHash: sha256:1hr09cgkyw1hcsfkv5qp5jlpmf2mqrkrqs3xj5zklq9c1h9544ff
+FileSize: 4006
+NarHash: sha256:0ik9mpqxpd9hv325hdblj2nawqj5w7951qdyy8ikxgwr6fq7m11c
+NarSize: 21264
+References: a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7 7w2acjgalb0cm7b3bg8yswza4l7iil9y-binutils-2.18 mm631h09mj964hm9q04l5fd8vw12j1mm-bash-3.2-p39 nx2zs2qd6snfcpzw4a0jnh26z9m0yihz-gcc-3.4.6 xi429w4ddvb1r77978hm7jfb2jsn559r-gcc-3.4.6
+Deriver: 2dzpn70c1hawczwhg9aavqk18zp9zsva-gcc-3.4.6.drv
+Sig: cache.nixos.org-1:o1DTsjCz0PofLJ216P2RBuSulI8BAb6zHxWE4N+tzlcELk5Uk/GO2SCxWTRN5wJutLZZ+cHTMdWqOHF88KGQDg==
+"#).expect("should parse");
+
+        assert!(parsed.flags.contains(Flags::REFERENCES_OUT_OF_ORDER));
+        assert_eq!(
+            vec![
+                "a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7",
+                "7w2acjgalb0cm7b3bg8yswza4l7iil9y-binutils-2.18",
+                "mm631h09mj964hm9q04l5fd8vw12j1mm-bash-3.2-p39",
+                "nx2zs2qd6snfcpzw4a0jnh26z9m0yihz-gcc-3.4.6",
+                "xi429w4ddvb1r77978hm7jfb2jsn559r-gcc-3.4.6"
+            ],
+            parsed
+                .references
+                .iter()
+                .map(StorePathRef::to_string)
+                .collect::<Vec<_>>(),
+        );
+    }
+
+    #[test]
+    fn ca_nar_hash_sha1() {
+        let parsed = NarInfo::parse(
+            r#"StorePath: /nix/store/k20pahypzvr49fy82cw5sx72hdfg3qcr-texlive-hyphenex-37354
+URL: nar/0i5biw0g01514llhfswxy6xfav8lxxdq1xg6ik7hgsqbpw0f06yi.nar.xz
+Compression: xz
+FileHash: sha256:0i5biw0g01514llhfswxy6xfav8lxxdq1xg6ik7hgsqbpw0f06yi
+FileSize: 7120
+NarHash: sha256:0h1bm4sj1cnfkxgyhvgi8df1qavnnv94sd0v09wcrm971602shfg
+NarSize: 22552
+References: 
+Sig: cache.nixos.org-1:u01BybwQhyI5H1bW1EIWXssMDhDDIvXOG5uh8Qzgdyjz6U1qg6DHhMAvXZOUStIj6X5t4/ufFgR8i3fjf0bMAw==
+CA: fixed:r:sha1:1ak1ymbmsfx7z8kh09jzkr3a4dvkrfjw
+"#).expect("should parse");
+
+        assert_eq!(
+            parsed.ca,
+            Some(CAHash::Nar(NixHash::Sha1(hex!(
+                "5cba3c77236ae4f9650270a27fbad375551fa60a"
+            ))))
+        );
+    }
+
+    #[test]
+    fn compression_default() {
+        // This doesn't exist as such in cache.nixos.org.
+        // We explicitly removed the compression field for the sake of this test.
+        let parsed = NarInfo::parse(r#"StorePath: /nix/store/a1jjalr4csx9hcga7fnm122aqabrjnch-digikam-2.6.0
+URL: nar/1fzimfnvq2k8b40n4g54abmncpx2ddckh6qlb77pgq6xiysyil69.nar.bz2
+FileHash: sha256:1fzimfnvq2k8b40n4g54abmncpx2ddckh6qlb77pgq6xiysyil69
+FileSize: 43503778
+NarHash: sha256:0zpbbwipqzr5p8mlpag9wrsp5hlaxkq7gax5jj0hg3vvdziypcw5
+NarSize: 100658640
+References: 0izkyk7bq2ag9393nvnhgm87p75cq09w-liblqr-1-0.4.1 1cslpgyb7vb30inj3210jv6agqv42jxz-qca-2.0.3 1sya3bwjxkzpkmwn67gfzp4gz4g62l36-libXrandr-1.3.1 26yxdaa9z0ma5sgw02i670rsqnl57crs-glib-2.30.3 27lnjh99236kmhbpc5747599zcymfzmg-qt-4.8.2 2v6x378vcfvyxilkvihs60zha54z2x2y-qjson-0.7.1 45hgr3fbnr45n795hn2x7hsymp0h2j2m-libjpeg-8c 4kw1b212s80ap2iyibxrimcqb5imhfj7-libkexiv2-4.7.4 7dvylm5crlc0sfafcc0n46mb5ch67q0j-glibc-2.13 a05cbh1awjbl1rbyb2ynyf4k42v5a9a7-boost-1.47.0 a1jjalr4csx9hcga7fnm122aqabrjnch-digikam-2.6.0 aav5ffg8wlnilgnvdb2jnrv2aam4zmmz-perl-5.14.2 ab0m9h30nsr13w48qriv0k350kmwx567-kdelibs-4.7.4 avffkd49cqvpwdkzry8bn69dkbw4cy29-lensfun-0.2.5 cy8rl8h4yp2j3h8987vkklg328q3wmjz-gcc-4.6.3 dmmh5ihyg1r2dm4azgsfj2kprj92czlg-libSM-1.2.0 fl56j5n4shfw9c0r6vs2i4f1h9zx5kac-soprano-2.7.6 g15cmvh15ggdjcwapskngv20q4yhix40-jasper-1.900.1 i04maxd0din6v92rnqcwl9yra0kl2vk5-marble-4.7.4 kqjjb3m26rdddwwwkk8v45821aps877k-libICE-1.0.7 lxz9r135wkndvi642z4bjgmvyypsgirb-libtiff-3.9.4 m9c8i0a6cl30lcqp654dqkbag3wjmd00-libX11-1.4.1 mpnj4k2ijrgyfkh48fg96nzcmklfh5pl-coreutils-8.15 nppljblap477s0893c151lyq7r7n5v1q-zlib-1.2.7 nw9mdbyp8kyn3v4vkdzq0gsnqbc4mnx3-expat-2.0.1 p1a0dn931mzdkvj6h5yzshbmgxba5r0z-libgphoto2-2.4.11 pvjj07xa1cfkad3gwk376nzdrgknbcqm-mesa-7.11.2 pzcxag98jqccp9ycbxknyh0w95pgnsk4-lcms-1.19 qfi5pgds33kg6vlnxsmj0hyl74vcmyiz-libpng-1.5.10 scm6bj86s3qh3s3x0b9ayjp6755p4q86-mysql-5.1.54 sd23qspcyg385va0lr35xgz3hvlqphg6-libkipi-4.7.4 svmbrhc6kzfzakv20a7zrfl6kbr5mfpq-kdepimlibs-4.7.4 v7kh3h7xfwjz4hgffg3gwrfzjff9bw9d-bash-4.2-p24 vi17f22064djgpk0w248da348q8gxkww-libkdcraw-4.7.4 wkjdzmj3z4dcbsc9f833zs6krdgg2krk-phonon-4.6.0 xf3i3awqi0035ixy2qyb6hk4c92r3vrn-opencv-2.4.2 y1vr0nz8i59x59501020nh2k1dw3bhwq-libusb-0.1.12 yf3hin2hb6i08n7zrk8g3acy54rhg9bp-libXext-1.2.0
+Deriver: la77dr44phk5m5jnl4dvk01cwpykyw9s-digikam-2.6.0.drv
+System: i686-linux
+Sig: cache.nixos.org-1:92fl0i5q7EyegCj5Yf4L0bENkWuVAtgveiRcTEEUH0P6HvCE1xFcPbz/0Pf6Np+K1LPzHK+s5RHOmVoxRsvsDg==
+"#).expect("should parse");
+
+        assert!(parsed.flags.contains(Flags::COMPRESSION_DEFAULT));
+        assert_eq!(parsed.compression, Some("bzip2"));
+    }
+
+    #[test]
+    fn nar_hash_hex() {
+        let parsed = NarInfo::parse(r#"StorePath: /nix/store/0vpqfxbkx0ffrnhbws6g9qwhmliksz7f-perl-HTTP-Cookies-6.01
+URL: nar/1rv1m9inydm1r4krw8hmwg1hs86d0nxddd1pbhihx7l7fycjvfk3.nar.xz
+Compression: xz
+FileHash: sha256:1rv1m9inydm1r4krw8hmwg1hs86d0nxddd1pbhihx7l7fycjvfk3
+FileSize: 19912
+NarHash: sha256:60adfd293a4d81ad7cd7e47263cbb3fc846309ef91b154a08ba672b558f94ff3
+NarSize: 45840
+References: 0vpqfxbkx0ffrnhbws6g9qwhmliksz7f-perl-HTTP-Cookies-6.01 9vrhbib2lxd9pjlg6fnl5b82gblidrcr-perl-HTTP-Message-6.06 wy20zslqxzxxfpzzk0rajh41d7a6mlnf-perl-HTTP-Date-6.02
+Deriver: fb4ihlq3psnsjq95mvvs49rwpplpc8zj-perl-HTTP-Cookies-6.01.drv
+Sig: cache.nixos.org-1:HhaiY36Uk3XV1JGe9d9xHnzAapqJXprU1YZZzSzxE97jCuO5RR7vlG2kF7MSC5thwRyxAtdghdSz3AqFi+QSCw==
+"#).expect("should parse");
+
+        assert!(parsed.flags.contains(Flags::NAR_HASH_HEX));
+        assert_eq!(
+            hex!("60adfd293a4d81ad7cd7e47263cbb3fc846309ef91b154a08ba672b558f94ff3"),
+            parsed.nar_hash,
+        );
+    }
+}
diff --git a/tvix/nix-compat/src/narinfo/public_keys.rs b/tvix/nix-compat/src/narinfo/public_keys.rs
new file mode 100644
index 0000000000..27dd90e096
--- /dev/null
+++ b/tvix/nix-compat/src/narinfo/public_keys.rs
@@ -0,0 +1,152 @@
+//! This module defines data structures and parsers for the public key format
+//! used inside Nix to verify signatures on .narinfo files.
+
+use std::fmt::Display;
+
+use data_encoding::BASE64;
+use ed25519_dalek::{VerifyingKey, PUBLIC_KEY_LENGTH};
+
+use super::Signature;
+
+/// This represents a ed25519 public key and "name".
+/// These are normally passed in the `trusted-public-keys` Nix config option,
+/// and consist of a name and base64-encoded ed25519 pubkey, separated by a `:`.
+#[derive(Debug)]
+pub struct PubKey {
+    name: String,
+    verifying_key: VerifyingKey,
+}
+
+impl PubKey {
+    pub fn new(name: String, verifying_key: VerifyingKey) -> Self {
+        Self {
+            name,
+            verifying_key,
+        }
+    }
+
+    pub fn parse(input: &str) -> Result<Self, Error> {
+        let (name, bytes64) = input.split_once(':').ok_or(Error::MissingSeparator)?;
+
+        if name.is_empty()
+            || !name
+                .chars()
+                .all(|c| char::is_alphanumeric(c) || c == '-' || c == '.')
+        {
+            return Err(Error::InvalidName(name.to_string()));
+        }
+
+        if bytes64.len() != BASE64.encode_len(PUBLIC_KEY_LENGTH) {
+            return Err(Error::InvalidPubKeyLen(bytes64.len()));
+        }
+
+        let mut buf = [0; PUBLIC_KEY_LENGTH + 1];
+        let mut bytes = [0; PUBLIC_KEY_LENGTH];
+        match BASE64.decode_mut(bytes64.as_bytes(), &mut buf) {
+            Ok(PUBLIC_KEY_LENGTH) => {
+                bytes.copy_from_slice(&buf[..PUBLIC_KEY_LENGTH]);
+            }
+            Ok(_) => unreachable!(),
+            // keeping DecodePartial gets annoying lifetime-wise
+            Err(_) => return Err(Error::DecodeError(input.to_string())),
+        }
+
+        let verifying_key = VerifyingKey::from_bytes(&bytes).map_err(Error::InvalidVerifyingKey)?;
+
+        Ok(Self {
+            name: name.to_string(),
+            verifying_key,
+        })
+    }
+
+    pub fn name(&self) -> &str {
+        &self.name
+    }
+
+    /// Verify the passed in signature is a correct signature for the passed in fingerprint and is signed
+    /// by the key material referred to by [Self],
+    /// which means the name in the signature has to match,
+    /// and the signature bytes themselves need to be a valid signature made by
+    /// the signing key identified by [Self::verifying key].
+    pub fn verify(&self, fingerprint: &str, signature: &Signature) -> bool {
+        if self.name() != signature.name() {
+            return false;
+        }
+
+        return signature.verify(fingerprint.as_bytes(), &self.verifying_key);
+    }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    #[error("Invalid name: {0}")]
+    InvalidName(String),
+    #[error("Missing separator")]
+    MissingSeparator,
+    #[error("Invalid pubkey len: {0}")]
+    InvalidPubKeyLen(usize),
+    #[error("VerifyingKey error: {0}")]
+    InvalidVerifyingKey(ed25519_dalek::SignatureError),
+    #[error("Unable to base64-decode pubkey: {0}")]
+    DecodeError(String),
+}
+
+impl Display for PubKey {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "{}:{}",
+            self.name,
+            BASE64.encode(self.verifying_key.as_bytes())
+        )
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use data_encoding::BASE64;
+    use ed25519_dalek::PUBLIC_KEY_LENGTH;
+    use rstest::rstest;
+
+    use crate::narinfo::Signature;
+
+    use super::PubKey;
+    const FINGERPRINT: &str = "1;/nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin;sha256:1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0;196040;/nix/store/0jqd0rlxzra1rs38rdxl43yh6rxchgc6-curl-7.82.0,/nix/store/6w8g7njm4mck5dmjxws0z1xnrxvl81xa-glibc-2.34-115,/nix/store/j5jxw3iy7bbz4a57fh9g2xm2gxmyal8h-zlib-1.2.12,/nix/store/yxvjs9drzsphm9pcf42a4byzj1kb9m7k-openssl-1.1.1n";
+
+    #[rstest]
+    #[case::cache_nixos_org("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=", "cache.nixos.org-1", &BASE64.decode(b"6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=").unwrap()[..].try_into().unwrap())]
+    #[case::cache_nixos_org_different_name("cheesecake:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=", "cheesecake", &BASE64.decode(b"6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=").unwrap()[..].try_into().unwrap())]
+    #[case::test_1("test1:tLAEn+EeaBUJYqEpTd2yeerr7Ic6+0vWe+aXL/vYUpE=", "test1", &BASE64.decode(b"tLAEn+EeaBUJYqEpTd2yeerr7Ic6+0vWe+aXL/vYUpE=").unwrap()[..].try_into().unwrap())]
+    fn parse(
+        #[case] input: &'static str,
+        #[case] exp_name: &'static str,
+        #[case] exp_verifying_key_bytes: &[u8; PUBLIC_KEY_LENGTH],
+    ) {
+        let pubkey = PubKey::parse(input).expect("must parse");
+        assert_eq!(exp_name, pubkey.name());
+        assert_eq!(exp_verifying_key_bytes, pubkey.verifying_key.as_bytes());
+    }
+
+    #[rstest]
+    #[case::empty_name("6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=")]
+    #[case::missing_padding("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY")]
+    #[case::wrong_length("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDS")]
+    fn parse_fail(#[case] input: &'static str) {
+        PubKey::parse(input).expect_err("must fail");
+    }
+
+    #[rstest]
+    #[case::correct_cache_nixos_org("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=", FINGERPRINT, "cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", true)]
+    #[case::wrong_name_mismatch("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=", FINGERPRINT, "cache.nixos.org:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", false)]
+    fn verify(
+        #[case] pubkey_str: &'static str,
+        #[case] fingerprint: &'static str,
+        #[case] signature_str: &'static str,
+        #[case] expected: bool,
+    ) {
+        let pubkey = PubKey::parse(pubkey_str).expect("must parse");
+        let signature = Signature::parse(signature_str).expect("must parse");
+
+        assert_eq!(expected, pubkey.verify(fingerprint, &signature));
+    }
+}
diff --git a/tvix/nix-compat/src/narinfo/signature.rs b/tvix/nix-compat/src/narinfo/signature.rs
new file mode 100644
index 0000000000..fd197e771d
--- /dev/null
+++ b/tvix/nix-compat/src/narinfo/signature.rs
@@ -0,0 +1,184 @@
+use std::fmt::{self, Display};
+
+use data_encoding::BASE64;
+use ed25519_dalek::SIGNATURE_LENGTH;
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Signature<'a> {
+    name: &'a str,
+    bytes: [u8; SIGNATURE_LENGTH],
+}
+
+impl<'a> Signature<'a> {
+    pub fn new(name: &'a str, bytes: [u8; SIGNATURE_LENGTH]) -> Self {
+        Self { name, bytes }
+    }
+
+    pub fn parse(input: &'a str) -> Result<Self, Error> {
+        let (name, bytes64) = input.split_once(':').ok_or(Error::MissingSeparator)?;
+
+        if name.is_empty()
+            || !name
+                .chars()
+                .all(|c| char::is_alphanumeric(c) || c == '-' || c == '.')
+        {
+            return Err(Error::InvalidName(name.to_string()));
+        }
+
+        if bytes64.len() != BASE64.encode_len(SIGNATURE_LENGTH) {
+            return Err(Error::InvalidSignatureLen(bytes64.len()));
+        }
+
+        let mut bytes = [0; SIGNATURE_LENGTH];
+        let mut buf = [0; SIGNATURE_LENGTH + 2];
+        match BASE64.decode_mut(bytes64.as_bytes(), &mut buf) {
+            Ok(SIGNATURE_LENGTH) => bytes.copy_from_slice(&buf[..SIGNATURE_LENGTH]),
+            Ok(_) => unreachable!(),
+            // keeping DecodePartial gets annoying lifetime-wise
+            Err(_) => return Err(Error::DecodeError(input.to_string())),
+        }
+
+        Ok(Signature { name, bytes })
+    }
+
+    pub fn name(&self) -> &'a str {
+        self.name
+    }
+
+    pub fn bytes(&self) -> &[u8; SIGNATURE_LENGTH] {
+        &self.bytes
+    }
+
+    /// For a given fingerprint and ed25519 verifying key, ensure if the signature is valid.
+    pub fn verify(&self, fingerprint: &[u8], verifying_key: &ed25519_dalek::VerifyingKey) -> bool {
+        let signature = ed25519_dalek::Signature::from_bytes(self.bytes());
+
+        verifying_key.verify_strict(fingerprint, &signature).is_ok()
+    }
+}
+
+impl<'de: 'a, 'a> Deserialize<'de> for Signature<'a> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let str: &'de str = Deserialize::deserialize(deserializer)?;
+        Self::parse(str).map_err(|_| {
+            serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"Signature")
+        })
+    }
+}
+
+impl<'a> Serialize for Signature<'a> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let string: String = self.to_string();
+
+        string.serialize(serializer)
+    }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    #[error("Invalid name: {0}")]
+    InvalidName(String),
+    #[error("Missing separator")]
+    MissingSeparator,
+    #[error("Invalid signature len: (expected {} b64-encoded, got {}", BASE64.encode_len(SIGNATURE_LENGTH), .0)]
+    InvalidSignatureLen(usize),
+    #[error("Unable to base64-decode signature: {0}")]
+    DecodeError(String),
+}
+
+impl Display for Signature<'_> {
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
+        write!(w, "{}:{}", self.name, BASE64.encode(&self.bytes))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use data_encoding::BASE64;
+    use ed25519_dalek::VerifyingKey;
+    use hex_literal::hex;
+    use lazy_static::lazy_static;
+
+    use super::Signature;
+    use rstest::rstest;
+
+    const FINGERPRINT: &str = "1;/nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin;sha256:1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0;196040;/nix/store/0jqd0rlxzra1rs38rdxl43yh6rxchgc6-curl-7.82.0,/nix/store/6w8g7njm4mck5dmjxws0z1xnrxvl81xa-glibc-2.34-115,/nix/store/j5jxw3iy7bbz4a57fh9g2xm2gxmyal8h-zlib-1.2.12,/nix/store/yxvjs9drzsphm9pcf42a4byzj1kb9m7k-openssl-1.1.1n";
+
+    // The signing key labelled as `cache.nixos.org-1`,
+    lazy_static! {
+        static ref PUB_CACHE_NIXOS_ORG_1: VerifyingKey = ed25519_dalek::VerifyingKey::from_bytes(
+            BASE64
+                .decode(b"6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=")
+                .unwrap()[..]
+                .try_into()
+                .unwrap()
+        )
+        .unwrap();
+        static ref PUB_TEST_1: VerifyingKey = ed25519_dalek::VerifyingKey::from_bytes(
+            BASE64
+                .decode(b"tLAEn+EeaBUJYqEpTd2yeerr7Ic6+0vWe+aXL/vYUpE=")
+                .unwrap()[..]
+                .try_into()
+                .unwrap()
+        )
+        .unwrap();
+    }
+
+    #[rstest]
+    #[case::valid_cache_nixos_org_1(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", FINGERPRINT, true)]
+    #[case::valid_test1(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", FINGERPRINT, true)]
+    #[case::valid_cache_nixos_org_different_name(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-2:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", FINGERPRINT, true)]
+    #[case::fail_invalid_cache_nixos_org_1_signature(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-1:TsTTb000000000000000000000000ytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", FINGERPRINT, false)]
+    #[case::fail_valid_sig_but_wrong_fp_cache_nixos_org_1(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", &FINGERPRINT[0..5], false)]
+    fn verify_sigs(
+        #[case] verifying_key: &VerifyingKey,
+        #[case] sig_str: &'static str,
+        #[case] fp: &str,
+        #[case] expect_valid: bool,
+    ) {
+        let sig = Signature::parse(sig_str).expect("must parse");
+        assert_eq!(expect_valid, sig.verify(fp.as_bytes(), verifying_key));
+    }
+
+    #[rstest]
+    #[case::wrong_length("cache.nixos.org-1:o1DTsjCz0PofLJ216P2RBuSulI8BAb6zHxWE4N+tzlcELk5Uk/GO2SCxWTRN5wJutLZZ+cHTMdWqOHF8")]
+    #[case::wrong_name_newline("test\n:u01BybwQhyI5H1bW1EIWXssMDhDDIvXOG5uh8Qzgdyjz6U1qg6DHhMAvXZOUStIj6X5t4/ufFgR8i3fjf0bMAw==")]
+    #[case::wrong_name_space("test :u01BybwQhyI5H1bW1EIWXssMDhDDIvXOG5uh8Qzgdyjz6U1qg6DHhMAvXZOUStIj6X5t4/ufFgR8i3fjf0bMAw==")]
+    #[case::empty_name(
+        ":u01BybwQhyI5H1bW1EIWXssMDhDDIvXOG5uh8Qzgdyjz6U1qg6DHhMAvXZOUStIj6X5t4/ufFgR8i3fjf0bMAw=="
+    )]
+    #[case::b64_only(
+        "u01BybwQhyI5H1bW1EIWXssMDhDDIvXOG5uh8Qzgdyjz6U1qg6DHhMAvXZOUStIj6X5t4/ufFgR8i3fjf0bMAw=="
+    )]
+    fn parse_fail(#[case] input: &'static str) {
+        Signature::parse(input).expect_err("must fail");
+    }
+
+    #[test]
+    fn serialize_deserialize() {
+        let signature_actual = Signature {
+            name: "cache.nixos.org-1",
+            bytes: hex!(
+                r#"4e c4 d3 6f 75 86 4d 92  a9 86 f6 1d 04 75 f0 a3
+                   ac 1e 54 82 e6 4f 2b 54  8c b0 7e bd c5 fc f5 f3
+                   a3 8d 18 9c 08 79 8a 03  84 42 3c c5 4b 92 3e 93
+                   30 9e 06 31 7d c7 3d 55  91 74 3d 61 91 e2 99 05"#
+            ),
+        };
+        let signature_str_json = "\"cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==\"";
+
+        let serialized = serde_json::to_string(&signature_actual).expect("must serialize");
+        assert_eq!(signature_str_json, &serialized);
+
+        let deserialized: Signature<'_> =
+            serde_json::from_str(signature_str_json).expect("must deserialize");
+        assert_eq!(&signature_actual, &deserialized);
+    }
+}
diff --git a/tvix/nix-compat/src/nix_daemon/mod.rs b/tvix/nix-compat/src/nix_daemon/mod.rs
new file mode 100644
index 0000000000..fe652377d1
--- /dev/null
+++ b/tvix/nix-compat/src/nix_daemon/mod.rs
@@ -0,0 +1,4 @@
+pub mod worker_protocol;
+
+mod protocol_version;
+pub use protocol_version::ProtocolVersion;
diff --git a/tvix/nix-compat/src/nix_daemon/protocol_version.rs b/tvix/nix-compat/src/nix_daemon/protocol_version.rs
new file mode 100644
index 0000000000..8fd2b085c9
--- /dev/null
+++ b/tvix/nix-compat/src/nix_daemon/protocol_version.rs
@@ -0,0 +1,123 @@
+/// Protocol versions are represented as a u16.
+/// The upper 8 bits are the major version, the lower bits the minor.
+/// This is not aware of any endianness, use [crate::wire::read_u64] to get an
+/// u64 first, and the try_from() impl from here if you're receiving over the
+/// Nix Worker protocol.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct ProtocolVersion(u16);
+
+impl ProtocolVersion {
+    pub const fn from_parts(major: u8, minor: u8) -> Self {
+        Self(((major as u16) << 8) | minor as u16)
+    }
+
+    pub fn major(&self) -> u8 {
+        ((self.0 & 0xff00) >> 8) as u8
+    }
+
+    pub fn minor(&self) -> u8 {
+        (self.0 & 0x00ff) as u8
+    }
+}
+
+impl PartialOrd for ProtocolVersion {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for ProtocolVersion {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        match self.major().cmp(&other.major()) {
+            std::cmp::Ordering::Less => std::cmp::Ordering::Less,
+            std::cmp::Ordering::Greater => std::cmp::Ordering::Greater,
+            std::cmp::Ordering::Equal => {
+                // same major, compare minor
+                self.minor().cmp(&other.minor())
+            }
+        }
+    }
+}
+
+impl From<u16> for ProtocolVersion {
+    fn from(value: u16) -> Self {
+        Self::from_parts(((value & 0xff00) >> 8) as u8, (value & 0x00ff) as u8)
+    }
+}
+
+impl TryFrom<u64> for ProtocolVersion {
+    type Error = &'static str;
+
+    fn try_from(value: u64) -> Result<Self, Self::Error> {
+        if value & !0xffff != 0 {
+            return Err("only two least significant bits might be populated");
+        }
+
+        Ok((value as u16).into())
+    }
+}
+
+impl From<ProtocolVersion> for u16 {
+    fn from(value: ProtocolVersion) -> Self {
+        value.0
+    }
+}
+
+impl From<ProtocolVersion> for u64 {
+    fn from(value: ProtocolVersion) -> Self {
+        value.0 as u64
+    }
+}
+
+impl std::fmt::Display for ProtocolVersion {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}.{}", self.major(), self.minor())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::ProtocolVersion;
+
+    #[test]
+    fn from_parts() {
+        let version = ProtocolVersion::from_parts(1, 37);
+        assert_eq!(version.major(), 1, "correct major");
+        assert_eq!(version.minor(), 37, "correct minor");
+        assert_eq!("1.37", &version.to_string(), "to_string");
+
+        assert_eq!(0x0125, Into::<u16>::into(version));
+        assert_eq!(0x0125, Into::<u64>::into(version));
+    }
+
+    #[test]
+    fn from_u16() {
+        let version = ProtocolVersion::from(0x0125_u16);
+        assert_eq!("1.37", &version.to_string());
+    }
+
+    #[test]
+    fn from_u64() {
+        let version = ProtocolVersion::try_from(0x0125_u64).expect("must succeed");
+        assert_eq!("1.37", &version.to_string());
+    }
+
+    /// This contains data in higher bits, which should fail.
+    #[test]
+    fn from_u64_fail() {
+        ProtocolVersion::try_from(0xaa0125_u64).expect_err("must fail");
+    }
+
+    #[test]
+    fn ord() {
+        let v0_37 = ProtocolVersion::from_parts(0, 37);
+        let v1_37 = ProtocolVersion::from_parts(1, 37);
+        let v1_40 = ProtocolVersion::from_parts(1, 40);
+
+        assert!(v0_37 < v1_37);
+        assert!(v1_37 > v0_37);
+        assert!(v1_37 < v1_40);
+        assert!(v1_40 > v1_37);
+        assert!(v1_40 <= v1_40);
+    }
+}
diff --git a/tvix/nix-compat/src/nix_daemon/worker_protocol.rs b/tvix/nix-compat/src/nix_daemon/worker_protocol.rs
new file mode 100644
index 0000000000..7e3adc0db2
--- /dev/null
+++ b/tvix/nix-compat/src/nix_daemon/worker_protocol.rs
@@ -0,0 +1,434 @@
+use std::{
+    collections::HashMap,
+    io::{Error, ErrorKind},
+};
+
+use enum_primitive_derive::Primitive;
+use num_traits::{FromPrimitive, ToPrimitive};
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+
+use crate::wire;
+
+use super::ProtocolVersion;
+
+static WORKER_MAGIC_1: u64 = 0x6e697863; // "nixc"
+static WORKER_MAGIC_2: u64 = 0x6478696f; // "dxio"
+pub static STDERR_LAST: u64 = 0x616c7473; // "alts"
+
+/// | Nix version     | Protocol |
+/// |-----------------|----------|
+/// | 0.11            | 1.02     |
+/// | 0.12            | 1.04     |
+/// | 0.13            | 1.05     |
+/// | 0.14            | 1.05     |
+/// | 0.15            | 1.05     |
+/// | 0.16            | 1.06     |
+/// | 1.0             | 1.10     |
+/// | 1.1             | 1.11     |
+/// | 1.2             | 1.12     |
+/// | 1.3 - 1.5.3     | 1.13     |
+/// | 1.6 - 1.10      | 1.14     |
+/// | 1.11 - 1.11.16  | 1.15     |
+/// | 2.0 - 2.0.4     | 1.20     |
+/// | 2.1 - 2.3.18    | 1.21     |
+/// | 2.4 - 2.6.1     | 1.32     |
+/// | 2.7.0           | 1.33     |
+/// | 2.8.0 - 2.14.1  | 1.34     |
+/// | 2.15.0 - 2.19.4 | 1.35     |
+/// | 2.20.0 - 2.22.0 | 1.37     |
+static PROTOCOL_VERSION: ProtocolVersion = ProtocolVersion::from_parts(1, 37);
+
+/// Max length of a Nix setting name/value. In bytes.
+///
+/// This value has been arbitrarily choosen after looking the nix.conf
+/// manpage. Don't hesitate to increase it if it's too limiting.
+pub static MAX_SETTING_SIZE: usize = 1024;
+
+/// Worker Operation
+///
+/// These operations are encoded as unsigned 64 bits before being sent
+/// to the wire. See the [read_op] and
+/// [write_op] operations to serialize/deserialize the
+/// operation on the wire.
+///
+/// Note: for now, we're using the Nix 2.20 operation description. The
+/// operations marked as obsolete are obsolete for Nix 2.20, not
+/// necessarily for Nix 2.3. We'll revisit this later on.
+#[derive(Debug, PartialEq, Primitive)]
+pub enum Operation {
+    IsValidPath = 1,
+    HasSubstitutes = 3,
+    QueryPathHash = 4,   // obsolete
+    QueryReferences = 5, // obsolete
+    QueryReferrers = 6,
+    AddToStore = 7,
+    AddTextToStore = 8, // obsolete since 1.25, Nix 3.0. Use WorkerProto::Op::AddToStore
+    BuildPaths = 9,
+    EnsurePath = 10,
+    AddTempRoot = 11,
+    AddIndirectRoot = 12,
+    SyncWithGC = 13,
+    FindRoots = 14,
+    ExportPath = 16,   // obsolete
+    QueryDeriver = 18, // obsolete
+    SetOptions = 19,
+    CollectGarbage = 20,
+    QuerySubstitutablePathInfo = 21,
+    QueryDerivationOutputs = 22, // obsolete
+    QueryAllValidPaths = 23,
+    QueryFailedPaths = 24,
+    ClearFailedPaths = 25,
+    QueryPathInfo = 26,
+    ImportPaths = 27,                // obsolete
+    QueryDerivationOutputNames = 28, // obsolete
+    QueryPathFromHashPart = 29,
+    QuerySubstitutablePathInfos = 30,
+    QueryValidPaths = 31,
+    QuerySubstitutablePaths = 32,
+    QueryValidDerivers = 33,
+    OptimiseStore = 34,
+    VerifyStore = 35,
+    BuildDerivation = 36,
+    AddSignatures = 37,
+    NarFromPath = 38,
+    AddToStoreNar = 39,
+    QueryMissing = 40,
+    QueryDerivationOutputMap = 41,
+    RegisterDrvOutput = 42,
+    QueryRealisation = 43,
+    AddMultipleToStore = 44,
+    AddBuildLog = 45,
+    BuildPathsWithResults = 46,
+    AddPermRoot = 47,
+}
+
+/// Log verbosity. In the Nix wire protocol, the client requests a
+/// verbosity level to the daemon, which in turns does not produce any
+/// log below this verbosity.
+#[derive(Debug, PartialEq, Primitive)]
+pub enum Verbosity {
+    LvlError = 0,
+    LvlWarn = 1,
+    LvlNotice = 2,
+    LvlInfo = 3,
+    LvlTalkative = 4,
+    LvlChatty = 5,
+    LvlDebug = 6,
+    LvlVomit = 7,
+}
+
+/// Settings requested by the client. These settings are applied to a
+/// connection to between the daemon and a client.
+#[derive(Debug, PartialEq)]
+pub struct ClientSettings {
+    pub keep_failed: bool,
+    pub keep_going: bool,
+    pub try_fallback: bool,
+    pub verbosity: Verbosity,
+    pub max_build_jobs: u64,
+    pub max_silent_time: u64,
+    pub verbose_build: bool,
+    pub build_cores: u64,
+    pub use_substitutes: bool,
+    /// Key/Value dictionary in charge of overriding the settings set
+    /// by the Nix config file.
+    ///
+    /// Some settings can be safely overidden,
+    /// some other require the user running the Nix client to be part
+    /// of the trusted users group.
+    pub overrides: HashMap<String, String>,
+}
+
+/// Reads the client settings from the wire.
+///
+/// Note: this function **only** reads the settings. It does not
+/// manage the log state with the daemon. You'll have to do that on
+/// your own. A minimal log implementation will consist in sending
+/// back [STDERR_LAST] to the client after reading the client
+/// settings.
+///
+/// FUTUREWORK: write serialization.
+pub async fn read_client_settings<R: AsyncReadExt + Unpin>(
+    r: &mut R,
+    client_version: ProtocolVersion,
+) -> std::io::Result<ClientSettings> {
+    let keep_failed = r.read_u64_le().await? != 0;
+    let keep_going = r.read_u64_le().await? != 0;
+    let try_fallback = r.read_u64_le().await? != 0;
+    let verbosity_uint = r.read_u64_le().await?;
+    let verbosity = Verbosity::from_u64(verbosity_uint).ok_or_else(|| {
+        Error::new(
+            ErrorKind::InvalidData,
+            format!("Can't convert integer {} to verbosity", verbosity_uint),
+        )
+    })?;
+    let max_build_jobs = r.read_u64_le().await?;
+    let max_silent_time = r.read_u64_le().await?;
+    _ = r.read_u64_le().await?; // obsolete useBuildHook
+    let verbose_build = r.read_u64_le().await? != 0;
+    _ = r.read_u64_le().await?; // obsolete logType
+    _ = r.read_u64_le().await?; // obsolete printBuildTrace
+    let build_cores = r.read_u64_le().await?;
+    let use_substitutes = r.read_u64_le().await? != 0;
+    let mut overrides = HashMap::new();
+    if client_version.minor() >= 12 {
+        let num_overrides = r.read_u64_le().await?;
+        for _ in 0..num_overrides {
+            let name = wire::read_string(r, 0..=MAX_SETTING_SIZE).await?;
+            let value = wire::read_string(r, 0..=MAX_SETTING_SIZE).await?;
+            overrides.insert(name, value);
+        }
+    }
+    Ok(ClientSettings {
+        keep_failed,
+        keep_going,
+        try_fallback,
+        verbosity,
+        max_build_jobs,
+        max_silent_time,
+        verbose_build,
+        build_cores,
+        use_substitutes,
+        overrides,
+    })
+}
+
+/// Performs the initial handshake the server is sending to a connecting client.
+///
+/// During the handshake, the client first send a magic u64, to which
+/// the daemon needs to respond with another magic u64.
+/// Then, the daemon retrieves the client version, and discards a bunch of now
+/// obsolete data.
+///
+/// # Arguments
+///
+/// * conn: connection with the Nix client.
+/// * nix_version: semantic version of the Nix daemon. "2.18.2" for
+///   instance.
+/// * trusted: trust level of the Nix client.
+///
+/// # Return
+///
+/// The protocol version of the client.
+pub async fn server_handshake_client<'a, RW: 'a>(
+    mut conn: &'a mut RW,
+    nix_version: &str,
+    trusted: Trust,
+) -> std::io::Result<ProtocolVersion>
+where
+    &'a mut RW: AsyncReadExt + AsyncWriteExt + Unpin,
+{
+    let worker_magic_1 = conn.read_u64_le().await?;
+    if worker_magic_1 != WORKER_MAGIC_1 {
+        Err(std::io::Error::new(
+            ErrorKind::InvalidData,
+            format!("Incorrect worker magic number received: {}", worker_magic_1),
+        ))
+    } else {
+        conn.write_u64_le(WORKER_MAGIC_2).await?;
+        conn.write_u64_le(PROTOCOL_VERSION.into()).await?;
+        conn.flush().await?;
+        let client_version = conn.read_u64_le().await?;
+        // Parse into ProtocolVersion.
+        let client_version: ProtocolVersion = client_version
+            .try_into()
+            .map_err(|e| Error::new(ErrorKind::Unsupported, e))?;
+        if client_version < ProtocolVersion::from_parts(1, 10) {
+            return Err(Error::new(
+                ErrorKind::Unsupported,
+                format!("The nix client version {} is too old", client_version),
+            ));
+        }
+        if client_version.minor() >= 14 {
+            // Obsolete CPU affinity.
+            let read_affinity = conn.read_u64_le().await?;
+            if read_affinity != 0 {
+                let _cpu_affinity = conn.read_u64_le().await?;
+            };
+        }
+        if client_version.minor() >= 11 {
+            // Obsolete reserveSpace
+            let _reserve_space = conn.read_u64_le().await?;
+        }
+        if client_version.minor() >= 33 {
+            // Nix version. We're plain lying, we're not Nix, but eh…
+            // Setting it to the 2.3 lineage. Not 100% sure this is a
+            // good idea.
+            wire::write_bytes(&mut conn, nix_version).await?;
+            conn.flush().await?;
+        }
+        if client_version.minor() >= 35 {
+            write_worker_trust_level(&mut conn, trusted).await?;
+        }
+        Ok(client_version)
+    }
+}
+
+/// Read a worker [Operation] from the wire.
+pub async fn read_op<R: AsyncReadExt + Unpin>(r: &mut R) -> std::io::Result<Operation> {
+    let op_number = r.read_u64_le().await?;
+    Operation::from_u64(op_number).ok_or(Error::new(
+        ErrorKind::InvalidData,
+        format!("Invalid OP number {}", op_number),
+    ))
+}
+
+/// Write a worker [Operation] to the wire.
+pub async fn write_op<W: AsyncWriteExt + Unpin>(w: &mut W, op: &Operation) -> std::io::Result<()> {
+    let op = Operation::to_u64(op).ok_or(Error::new(
+        ErrorKind::Other,
+        format!("Can't convert the OP {:?} to u64", op),
+    ))?;
+    w.write_u64(op).await
+}
+
+#[derive(Debug, PartialEq)]
+pub enum Trust {
+    Trusted,
+    NotTrusted,
+}
+
+/// Write the worker [Trust] level to the wire.
+///
+/// Cpp Nix has a legacy third option: u8 0. This option is meant to
+/// be used as a backward compatible measure. Since we're not
+/// targetting protocol versions pre-dating the trust notion, we
+/// decided not to implement it here.
+pub async fn write_worker_trust_level<W>(conn: &mut W, t: Trust) -> std::io::Result<()>
+where
+    W: AsyncReadExt + AsyncWriteExt + Unpin,
+{
+    match t {
+        Trust::Trusted => conn.write_u64_le(1).await,
+        Trust::NotTrusted => conn.write_u64_le(2).await,
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use hex_literal::hex;
+    use tokio_test::io::Builder;
+
+    #[tokio::test]
+    async fn test_init_hanshake() {
+        let mut test_conn = tokio_test::io::Builder::new()
+            .read(&WORKER_MAGIC_1.to_le_bytes())
+            .write(&WORKER_MAGIC_2.to_le_bytes())
+            .write(&[37, 1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+            // Let's say the client is in sync with the daemon
+            // protocol-wise
+            .read(&[37, 1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+            // cpu affinity
+            .read(&[0; 8])
+            // reservespace
+            .read(&[0; 8])
+            // version (size)
+            .write(&[0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+            // version (data == 2.18.2 + padding)
+            .write(&[50, 46, 49, 56, 46, 50, 0, 0])
+            // Trusted (1 == client trusted
+            .write(&[1, 0, 0, 0, 0, 0, 0, 0])
+            .build();
+        let client_version = server_handshake_client(&mut test_conn, "2.18.2", Trust::Trusted)
+            .await
+            .unwrap();
+
+        assert_eq!(client_version, PROTOCOL_VERSION)
+    }
+
+    #[tokio::test]
+    async fn test_read_client_settings_without_overrides() {
+        // Client settings bits captured from a Nix 2.3.17 run w/ sockdump (protocol version 21).
+        let wire_bits = hex!(
+            "00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             02 00 00 00 00 00 00 00 \
+             10 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             01 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             01 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00"
+        );
+        let mut mock = Builder::new().read(&wire_bits).build();
+        let settings = read_client_settings(&mut mock, ProtocolVersion::from_parts(1, 21))
+            .await
+            .expect("should parse");
+        let expected = ClientSettings {
+            keep_failed: false,
+            keep_going: false,
+            try_fallback: false,
+            verbosity: Verbosity::LvlNotice,
+            max_build_jobs: 16,
+            max_silent_time: 0,
+            verbose_build: false,
+            build_cores: 0,
+            use_substitutes: true,
+            overrides: HashMap::new(),
+        };
+        assert_eq!(settings, expected);
+    }
+
+    #[tokio::test]
+    async fn test_read_client_settings_with_overrides() {
+        // Client settings bits captured from a Nix 2.3.17 run w/ sockdump (protocol version 21).
+        let wire_bits = hex!(
+            "00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             02 00 00 00 00 00 00 00 \
+             10 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             01 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             00 00 00 00 00 00 00 00 \
+             01 00 00 00 00 00 00 00 \
+             02 00 00 00 00 00 00 00 \
+             0c 00 00 00 00 00 00 00 \
+             61 6c 6c 6f 77 65 64 2d \
+             75 72 69 73 00 00 00 00 \
+             1e 00 00 00 00 00 00 00 \
+             68 74 74 70 73 3a 2f 2f \
+             62 6f 72 64 65 61 75 78 \
+             2e 67 75 69 78 2e 67 6e \
+             75 2e 6f 72 67 2f 00 00 \
+             0d 00 00 00 00 00 00 00 \
+             61 6c 6c 6f 77 65 64 2d \
+             75 73 65 72 73 00 00 00 \
+             0b 00 00 00 00 00 00 00 \
+             6a 65 61 6e 20 70 69 65 \
+             72 72 65 00 00 00 00 00"
+        );
+        let mut mock = Builder::new().read(&wire_bits).build();
+        let settings = read_client_settings(&mut mock, ProtocolVersion::from_parts(1, 21))
+            .await
+            .expect("should parse");
+        let overrides = HashMap::from([
+            (
+                String::from("allowed-uris"),
+                String::from("https://bordeaux.guix.gnu.org/"),
+            ),
+            (String::from("allowed-users"), String::from("jean pierre")),
+        ]);
+        let expected = ClientSettings {
+            keep_failed: false,
+            keep_going: false,
+            try_fallback: false,
+            verbosity: Verbosity::LvlNotice,
+            max_build_jobs: 16,
+            max_silent_time: 0,
+            verbose_build: false,
+            build_cores: 0,
+            use_substitutes: true,
+            overrides,
+        };
+        assert_eq!(settings, expected);
+    }
+}
diff --git a/tvix/nix-compat/src/nixbase32.rs b/tvix/nix-compat/src/nixbase32.rs
new file mode 100644
index 0000000000..b7ffc1dc2b
--- /dev/null
+++ b/tvix/nix-compat/src/nixbase32.rs
@@ -0,0 +1,206 @@
+//! Implements the slightly odd "base32" encoding that's used in Nix.
+//!
+//! Nix uses a custom alphabet. Contrary to other implementations (RFC4648),
+//! encoding to "nix base32" doesn't use any padding, and reads in characters
+//! in reverse order.
+//!
+//! This is also the main reason why we can't use `data_encoding::Encoding` -
+//! it gets things wrong if there normally would be a need for padding.
+
+use std::fmt::Write;
+
+use data_encoding::{DecodeError, DecodeKind};
+
+const ALPHABET: &[u8; 32] = b"0123456789abcdfghijklmnpqrsvwxyz";
+
+/// Returns encoded input
+pub fn encode(input: &[u8]) -> String {
+    let output_len = encode_len(input.len());
+    let mut output = String::with_capacity(output_len);
+
+    for n in (0..output_len).rev() {
+        let b = n * 5; // bit offset within the entire input
+        let i = b / 8; // input byte index
+        let j = b % 8; // bit offset within that input byte
+
+        // 5-bit words aren't aligned to bytes
+        // we can only read byte-aligned units
+        // read 16 bits then shift and mask to 5
+        let c = {
+            let mut word = input[i] as u16;
+            if let Some(&msb) = input.get(i + 1) {
+                word |= (msb as u16) << 8;
+            }
+            (word >> j) & 0x1f
+        };
+
+        output.write_char(ALPHABET[c as usize] as char).unwrap();
+    }
+
+    output
+}
+
+/// This maps a nixbase32-encoded character to its binary representation, which
+/// is also the index of the character in the alphabet. Invalid characters are
+/// mapped to 0xFF, which is itself an invalid value.
+const BASE32_ORD: [u8; 256] = {
+    let mut ord = [0xFF; 256];
+    let mut alphabet = ALPHABET.as_slice();
+    let mut i = 0;
+
+    while let &[c, ref tail @ ..] = alphabet {
+        ord[c as usize] = i;
+        alphabet = tail;
+        i += 1;
+    }
+
+    ord
+};
+
+/// Returns decoded input
+pub fn decode(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
+    let input = input.as_ref();
+
+    let output_len = decode_len(input.len());
+    let mut output: Vec<u8> = vec![0x00; output_len];
+
+    decode_inner(input, &mut output)?;
+    Ok(output)
+}
+
+pub fn decode_fixed<const K: usize>(input: impl AsRef<[u8]>) -> Result<[u8; K], DecodeError> {
+    let input = input.as_ref();
+
+    if input.len() != encode_len(K) {
+        return Err(DecodeError {
+            position: input.len().min(encode_len(K)),
+            kind: DecodeKind::Length,
+        });
+    }
+
+    let mut output = [0; K];
+    decode_inner(input, &mut output)?;
+    Ok(output)
+}
+
+fn decode_inner(input: &[u8], output: &mut [u8]) -> Result<(), DecodeError> {
+    // loop over all characters in reverse, and keep the iteration count in n.
+    let mut carry = 0;
+    let mut mask = 0;
+    for (n, &c) in input.iter().rev().enumerate() {
+        let b = n * 5;
+        let i = b / 8;
+        let j = b % 8;
+
+        let digit = BASE32_ORD[c as usize];
+        let value = (digit as u16) << j;
+        output[i] |= value as u8 | carry;
+        carry = (value >> 8) as u8;
+
+        mask |= digit;
+    }
+
+    if mask == 0xFF {
+        return Err(DecodeError {
+            position: find_invalid(input),
+            kind: DecodeKind::Symbol,
+        });
+    }
+
+    // if we're at the end, but have a nonzero carry, the encoding is invalid.
+    if carry != 0 {
+        return Err(DecodeError {
+            position: 0,
+            kind: DecodeKind::Trailing,
+        });
+    }
+
+    Ok(())
+}
+
+fn find_invalid(input: &[u8]) -> usize {
+    for (i, &c) in input.iter().enumerate() {
+        if !ALPHABET.contains(&c) {
+            return i;
+        }
+    }
+
+    unreachable!()
+}
+
+/// Returns the decoded length of an input of length len.
+pub const fn decode_len(len: usize) -> usize {
+    (len * 5) / 8
+}
+
+/// Returns the encoded length of an input of length len
+pub const fn encode_len(len: usize) -> usize {
+    (len * 8 + 4) / 5
+}
+
+#[cfg(test)]
+mod tests {
+    use hex_literal::hex;
+    use rstest::rstest;
+
+    #[rstest]
+    #[case::empty_bytes("", &[])]
+    #[case::one_byte("0z", &hex!("1f"))]
+    #[case::store_path("00bgd045z0d4icpbc2yyz4gx48ak44la", &hex!("8a12321522fd91efbd60ebb2481af88580f61600"))]
+    #[case::sha256("0c5b8vw40dy178xlpddw65q9gf1h2186jcc3p4swinwggbllv8mk", &hex!("b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30"))]
+    #[test]
+    fn encode(#[case] enc: &str, #[case] dec: &[u8]) {
+        assert_eq!(enc, super::encode(dec));
+    }
+
+    #[rstest]
+    #[case::empty_bytes("", Some(&[][..]) )]
+    #[case::one_byte("0z", Some(&hex!("1f")[..]))]
+    #[case::store_path("00bgd045z0d4icpbc2yyz4gx48ak44la", Some(&hex!("8a12321522fd91efbd60ebb2481af88580f61600")[..]))]
+    #[case::sha256("0c5b8vw40dy178xlpddw65q9gf1h2186jcc3p4swinwggbllv8mk", Some(&hex!("b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30")[..]))]
+    // this is invalid encoding, because it encodes 10 1-bits, so the carry
+    // would be 2 1-bits
+    #[case::invalid_encoding_1("zz", None)]
+    // this is an even more specific example - it'd decode as 00000000 11
+    #[case::invalid_encoding_2("c0", None)]
+    #[test]
+    fn decode(#[case] enc: &str, #[case] dec: Option<&[u8]>) {
+        match dec {
+            Some(dec) => {
+                // The decode needs to match what's passed in dec
+                assert_eq!(dec, super::decode(enc).unwrap());
+            }
+            None => {
+                // the decode needs to be an error
+                assert!(super::decode(enc).is_err());
+            }
+        }
+    }
+
+    #[test]
+    fn decode_fixed() {
+        assert_eq!(
+            super::decode_fixed("00bgd045z0d4icpbc2yyz4gx48ak44la").unwrap(),
+            hex!("8a12321522fd91efbd60ebb2481af88580f61600")
+        );
+        assert_eq!(
+            super::decode_fixed::<32>("00").unwrap_err(),
+            super::DecodeError {
+                position: 2,
+                kind: super::DecodeKind::Length
+            }
+        );
+    }
+
+    #[test]
+    fn encode_len() {
+        assert_eq!(super::encode_len(0), 0);
+        assert_eq!(super::encode_len(20), 32);
+    }
+
+    #[test]
+    fn decode_len() {
+        assert_eq!(super::decode_len(0), 0);
+        assert_eq!(super::decode_len(32), 20);
+    }
+}
diff --git a/tvix/nix-compat/src/nixhash/algos.rs b/tvix/nix-compat/src/nixhash/algos.rs
new file mode 100644
index 0000000000..ac8915314c
--- /dev/null
+++ b/tvix/nix-compat/src/nixhash/algos.rs
@@ -0,0 +1,75 @@
+use std::fmt::Display;
+
+use serde::{Deserialize, Serialize};
+
+use crate::nixhash::Error;
+
+/// This are the hash algorithms supported by cppnix.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum HashAlgo {
+    Md5,
+    Sha1,
+    Sha256,
+    Sha512,
+}
+
+impl HashAlgo {
+    // return the number of bytes in the digest of the given hash algo.
+    pub fn digest_length(&self) -> usize {
+        match self {
+            HashAlgo::Sha1 => 20,
+            HashAlgo::Sha256 => 32,
+            HashAlgo::Sha512 => 64,
+            HashAlgo::Md5 => 16,
+        }
+    }
+}
+
+impl Display for HashAlgo {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match &self {
+            HashAlgo::Md5 => write!(f, "md5"),
+            HashAlgo::Sha1 => write!(f, "sha1"),
+            HashAlgo::Sha256 => write!(f, "sha256"),
+            HashAlgo::Sha512 => write!(f, "sha512"),
+        }
+    }
+}
+
+impl Serialize for HashAlgo {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        serializer.collect_str(&self)
+    }
+}
+
+impl<'de> Deserialize<'de> for HashAlgo {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let s: &str = Deserialize::deserialize(deserializer)?;
+        HashAlgo::try_from(s).map_err(serde::de::Error::custom)
+    }
+}
+
+/// TODO(Raito): this could be automated via macros, I suppose.
+/// But this may be more expensive than just doing it by hand
+/// and ensuring that is kept in sync.
+pub const SUPPORTED_ALGOS: [&str; 4] = ["md5", "sha1", "sha256", "sha512"];
+
+impl TryFrom<&str> for HashAlgo {
+    type Error = Error;
+
+    fn try_from(algo_str: &str) -> Result<Self, Self::Error> {
+        match algo_str {
+            "md5" => Ok(Self::Md5),
+            "sha1" => Ok(Self::Sha1),
+            "sha256" => Ok(Self::Sha256),
+            "sha512" => Ok(Self::Sha512),
+            _ => Err(Error::InvalidAlgo(algo_str.to_string())),
+        }
+    }
+}
diff --git a/tvix/nix-compat/src/nixhash/ca_hash.rs b/tvix/nix-compat/src/nixhash/ca_hash.rs
new file mode 100644
index 0000000000..2bf5f966ce
--- /dev/null
+++ b/tvix/nix-compat/src/nixhash/ca_hash.rs
@@ -0,0 +1,343 @@
+use crate::nixbase32;
+use crate::nixhash::{HashAlgo, NixHash};
+use serde::de::Unexpected;
+use serde::ser::SerializeMap;
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
+use serde_json::{Map, Value};
+use std::borrow::Cow;
+
+use super::algos::SUPPORTED_ALGOS;
+use super::decode_digest;
+
+/// A Nix CAHash describes a content-addressed hash of a path.
+///
+/// The way Nix prints it as a string is a bit confusing, but there's essentially
+/// three modes, `Flat`, `Nar` and `Text`.
+/// `Flat` and `Nar` support all 4 algos that [NixHash] supports
+/// (sha1, md5, sha256, sha512), `Text` only supports sha256.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum CAHash {
+    Flat(NixHash),  // "fixed flat"
+    Nar(NixHash),   // "fixed recursive"
+    Text([u8; 32]), // "text", only supports sha256
+}
+
+/// Representation for the supported hash modes.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum HashMode {
+    Flat,
+    Nar,
+    Text,
+}
+
+impl CAHash {
+    pub fn hash(&self) -> Cow<NixHash> {
+        match *self {
+            CAHash::Flat(ref digest) => Cow::Borrowed(digest),
+            CAHash::Nar(ref digest) => Cow::Borrowed(digest),
+            CAHash::Text(digest) => Cow::Owned(NixHash::Sha256(digest)),
+        }
+    }
+
+    pub fn mode(&self) -> HashMode {
+        match self {
+            CAHash::Flat(_) => HashMode::Flat,
+            CAHash::Nar(_) => HashMode::Nar,
+            CAHash::Text(_) => HashMode::Text,
+        }
+    }
+
+    /// Constructs a [CAHash] from the textual representation,
+    /// which is one of the three:
+    /// - `text:sha256:$nixbase32sha256digest`
+    /// - `fixed:r:$algo:$nixbase32digest`
+    /// - `fixed:$algo:$nixbase32digest`
+    /// which is the format that's used in the NARInfo for example.
+    pub fn from_nix_hex_str(s: &str) -> Option<Self> {
+        let (tag, s) = s.split_once(':')?;
+
+        match tag {
+            "text" => {
+                let digest = s.strip_prefix("sha256:")?;
+                let digest = nixbase32::decode_fixed(digest).ok()?;
+                Some(CAHash::Text(digest))
+            }
+            "fixed" => {
+                if let Some(s) = s.strip_prefix("r:") {
+                    NixHash::from_nix_hex_str(s).map(CAHash::Nar)
+                } else {
+                    NixHash::from_nix_hex_str(s).map(CAHash::Flat)
+                }
+            }
+            _ => None,
+        }
+    }
+
+    /// Formats a [CAHash] in the Nix default hash format, which is the format
+    /// that's used in NARInfos for example.
+    pub fn to_nix_nixbase32_string(&self) -> String {
+        match self {
+            CAHash::Flat(nh) => format!("fixed:{}", nh.to_nix_nixbase32_string()),
+            CAHash::Nar(nh) => format!("fixed:r:{}", nh.to_nix_nixbase32_string()),
+            CAHash::Text(digest) => {
+                format!("text:sha256:{}", nixbase32::encode(digest))
+            }
+        }
+    }
+
+    /// This takes a serde_json::Map and turns it into this structure. This is necessary to do such
+    /// shenigans because we have external consumers, like the Derivation parser, who would like to
+    /// know whether we have a invalid or a missing NixHashWithMode structure in another structure,
+    /// e.g. Output.
+    /// This means we have this combinatorial situation:
+    /// - no hash, no hashAlgo: no [CAHash] so we return Ok(None).
+    /// - present hash, missing hashAlgo: invalid, we will return missing_field
+    /// - missing hash, present hashAlgo: same
+    /// - present hash, present hashAlgo: either we return ourselves or a type/value validation
+    /// error.
+    /// This function is for internal consumption regarding those needs until we have a better
+    /// solution. Now this is said, let's explain how this works.
+    ///
+    /// We want to map the serde data model into a [CAHash].
+    ///
+    /// The serde data model has a `hash` field (containing a digest in nixbase32),
+    /// and a `hashAlgo` field, containing the stringified hash algo.
+    /// In case the hash is recursive, hashAlgo also has a `r:` prefix.
+    ///
+    /// This is to match how `nix show-derivation` command shows them in JSON
+    /// representation.
+    pub(crate) fn from_map<'de, D>(map: &Map<String, Value>) -> Result<Option<Self>, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        // If we don't have hash neither hashAlgo, let's just return None.
+        if !map.contains_key("hash") && !map.contains_key("hashAlgo") {
+            return Ok(None);
+        }
+
+        let hash_algo_v = map.get("hashAlgo").ok_or_else(|| {
+            serde::de::Error::missing_field(
+                "couldn't extract `hashAlgo` key, but `hash` key present",
+            )
+        })?;
+        let hash_algo = hash_algo_v.as_str().ok_or_else(|| {
+            serde::de::Error::invalid_type(Unexpected::Other(&hash_algo_v.to_string()), &"a string")
+        })?;
+        let (mode_is_nar, hash_algo) = if let Some(s) = hash_algo.strip_prefix("r:") {
+            (true, s)
+        } else {
+            (false, hash_algo)
+        };
+        let hash_algo = HashAlgo::try_from(hash_algo).map_err(|e| {
+            serde::de::Error::invalid_value(
+                Unexpected::Other(&e.to_string()),
+                &format!("one of {}", SUPPORTED_ALGOS.join(",")).as_str(),
+            )
+        })?;
+
+        let hash_v = map.get("hash").ok_or_else(|| {
+            serde::de::Error::missing_field(
+                "couldn't extract `hash` key but `hashAlgo` key present",
+            )
+        })?;
+        let hash = hash_v.as_str().ok_or_else(|| {
+            serde::de::Error::invalid_type(Unexpected::Other(&hash_v.to_string()), &"a string")
+        })?;
+        let hash = decode_digest(hash.as_bytes(), hash_algo)
+            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
+        if mode_is_nar {
+            Ok(Some(Self::Nar(hash)))
+        } else {
+            Ok(Some(Self::Flat(hash)))
+        }
+    }
+}
+
+impl Serialize for CAHash {
+    /// map a CAHash into the serde data model.
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut map = serializer.serialize_map(Some(2))?;
+        match self {
+            CAHash::Flat(h) => {
+                map.serialize_entry("hash", &nixbase32::encode(h.digest_as_bytes()))?;
+                map.serialize_entry("hashAlgo", &h.algo())?;
+            }
+            CAHash::Nar(h) => {
+                map.serialize_entry("hash", &nixbase32::encode(h.digest_as_bytes()))?;
+                map.serialize_entry("hashAlgo", &format!("r:{}", &h.algo()))?;
+            }
+            // It is not legal for derivations to use this (which is where
+            // we're currently exercising [Serialize] mostly,
+            // but it's still good to be able to serialize other CA hashes too.
+            CAHash::Text(h) => {
+                map.serialize_entry("hash", &nixbase32::encode(h.as_ref()))?;
+                map.serialize_entry("hashAlgo", "text")?;
+            }
+        };
+        map.end()
+    }
+}
+
+impl<'de> Deserialize<'de> for CAHash {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let value = Self::from_map::<D>(&Map::deserialize(deserializer)?)?;
+
+        match value {
+            None => Err(serde::de::Error::custom("couldn't parse as map")),
+            Some(v) => Ok(v),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{derivation::CAHash, nixhash};
+
+    #[test]
+    fn serialize_flat() {
+        let json_bytes = r#"{
+  "hash": "1fnf2m46ya7r7afkcb8ba2j0sc4a85m749sh9jz64g4hx6z3r088",
+  "hashAlgo": "sha256"
+}"#;
+        let hash = CAHash::Flat(
+            nixhash::from_nix_str(
+                "sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+            )
+            .unwrap(),
+        );
+        let serialized = serde_json::to_string_pretty(&hash).unwrap();
+        assert_eq!(serialized, json_bytes);
+    }
+
+    #[test]
+    fn serialize_nar() {
+        let json_bytes = r#"{
+  "hash": "1fnf2m46ya7r7afkcb8ba2j0sc4a85m749sh9jz64g4hx6z3r088",
+  "hashAlgo": "r:sha256"
+}"#;
+        let hash = CAHash::Nar(
+            nixhash::from_nix_str(
+                "sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+            )
+            .unwrap(),
+        );
+        let serialized = serde_json::to_string_pretty(&hash).unwrap();
+        assert_eq!(serialized, json_bytes);
+    }
+
+    #[test]
+    fn deserialize_flat() {
+        let json_bytes = r#"
+        {
+            "hash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+            "hashAlgo": "sha256"
+        }"#;
+        let hash: CAHash = serde_json::from_str(json_bytes).expect("must parse");
+
+        assert_eq!(
+            hash,
+            CAHash::Flat(
+                nixhash::from_nix_str(
+                    "sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"
+                )
+                .unwrap()
+            )
+        );
+    }
+
+    #[test]
+    fn deserialize_hex() {
+        let json_bytes = r#"
+        {
+            "hash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+            "hashAlgo": "r:sha256"
+        }"#;
+        let hash: CAHash = serde_json::from_str(json_bytes).expect("must parse");
+
+        assert_eq!(
+            hash,
+            CAHash::Nar(
+                nixhash::from_nix_str(
+                    "sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"
+                )
+                .unwrap()
+            )
+        );
+    }
+
+    #[test]
+    fn deserialize_nixbase32() {
+        let json_bytes = r#"
+        {
+            "hash": "1fnf2m46ya7r7afkcb8ba2j0sc4a85m749sh9jz64g4hx6z3r088",
+            "hashAlgo": "r:sha256"
+        }"#;
+        let hash: CAHash = serde_json::from_str(json_bytes).expect("must parse");
+
+        assert_eq!(
+            hash,
+            CAHash::Nar(
+                nixhash::from_nix_str(
+                    "sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"
+                )
+                .unwrap()
+            )
+        );
+    }
+
+    #[test]
+    fn deserialize_base64() {
+        let json_bytes = r#"
+        {
+            "hash": "CIE8vumQPGK+TFAncmpBijANpFALLTadOvkob0gVzro=",
+            "hashAlgo": "r:sha256"
+        }"#;
+        let hash: CAHash = serde_json::from_str(json_bytes).expect("must parse");
+
+        assert_eq!(
+            hash,
+            CAHash::Nar(
+                nixhash::from_nix_str(
+                    "sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"
+                )
+                .unwrap()
+            )
+        );
+    }
+
+    #[test]
+    fn serialize_deserialize_nar() {
+        let json_bytes = r#"
+        {
+            "hash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+            "hashAlgo": "r:sha256"
+        }"#;
+        let hash: CAHash = serde_json::from_str(json_bytes).expect("must parse");
+
+        let serialized = serde_json::to_string(&hash).expect("Serialize");
+        let hash2: CAHash = serde_json::from_str(&serialized).expect("must parse again");
+
+        assert_eq!(hash, hash2);
+    }
+
+    #[test]
+    fn serialize_deserialize_flat() {
+        let json_bytes = r#"
+        {
+            "hash": "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
+            "hashAlgo": "sha256"
+        }"#;
+        let hash: CAHash = serde_json::from_str(json_bytes).expect("must parse");
+
+        let serialized = serde_json::to_string(&hash).expect("Serialize");
+        let hash2: CAHash = serde_json::from_str(&serialized).expect("must parse again");
+
+        assert_eq!(hash, hash2);
+    }
+}
diff --git a/tvix/nix-compat/src/nixhash/mod.rs b/tvix/nix-compat/src/nixhash/mod.rs
new file mode 100644
index 0000000000..d86cb8b79f
--- /dev/null
+++ b/tvix/nix-compat/src/nixhash/mod.rs
@@ -0,0 +1,602 @@
+use crate::nixbase32;
+use bstr::ByteSlice;
+use data_encoding::{BASE64, BASE64_NOPAD, HEXLOWER};
+use serde::Deserialize;
+use serde::Serialize;
+use std::cmp::Ordering;
+use std::fmt::Display;
+use thiserror;
+
+mod algos;
+mod ca_hash;
+
+pub use algos::HashAlgo;
+pub use ca_hash::CAHash;
+pub use ca_hash::HashMode as CAHashMode;
+
+/// NixHash represents hashes known by Nix.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum NixHash {
+    Md5([u8; 16]),
+    Sha1([u8; 20]),
+    Sha256([u8; 32]),
+    Sha512(Box<[u8; 64]>),
+}
+
+/// Same order as sorting the corresponding nixbase32 strings.
+///
+/// This order is used in the ATerm serialization of a derivation
+/// and thus affects the calculated output hash.
+impl Ord for NixHash {
+    fn cmp(&self, other: &NixHash) -> Ordering {
+        self.digest_as_bytes().cmp(other.digest_as_bytes())
+    }
+}
+
+// See Ord for reason to implement this manually.
+impl PartialOrd for NixHash {
+    fn partial_cmp(&self, other: &NixHash) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Display for NixHash {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+        write!(
+            f,
+            "{}-{}",
+            self.algo(),
+            nixbase32::encode(self.digest_as_bytes())
+        )
+    }
+}
+
+/// convenience Result type for all nixhash parsing Results.
+pub type NixHashResult<V> = std::result::Result<V, Error>;
+
+impl NixHash {
+    /// returns the algo as [HashAlgo].
+    pub fn algo(&self) -> HashAlgo {
+        match self {
+            NixHash::Md5(_) => HashAlgo::Md5,
+            NixHash::Sha1(_) => HashAlgo::Sha1,
+            NixHash::Sha256(_) => HashAlgo::Sha256,
+            NixHash::Sha512(_) => HashAlgo::Sha512,
+        }
+    }
+
+    /// returns the digest as variable-length byte slice.
+    pub fn digest_as_bytes(&self) -> &[u8] {
+        match self {
+            NixHash::Md5(digest) => digest,
+            NixHash::Sha1(digest) => digest,
+            NixHash::Sha256(digest) => digest,
+            NixHash::Sha512(digest) => digest.as_ref(),
+        }
+    }
+
+    /// Constructs a [NixHash] from the Nix default hash format,
+    /// the inverse of [Self::to_nix_hex_string].
+    pub fn from_nix_hex_str(s: &str) -> Option<Self> {
+        let (tag, digest) = s.split_once(':')?;
+
+        (match tag {
+            "md5" => nixbase32::decode_fixed(digest).map(NixHash::Md5),
+            "sha1" => nixbase32::decode_fixed(digest).map(NixHash::Sha1),
+            "sha256" => nixbase32::decode_fixed(digest).map(NixHash::Sha256),
+            "sha512" => nixbase32::decode_fixed(digest)
+                .map(Box::new)
+                .map(NixHash::Sha512),
+            _ => return None,
+        })
+        .ok()
+    }
+
+    /// Formats a [NixHash] in the Nix default hash format,
+    /// which is the algo, followed by a colon, then the lower hex encoded digest.
+    pub fn to_nix_hex_string(&self) -> String {
+        format!("{}:{}", self.algo(), self.to_plain_hex_string())
+    }
+
+    /// Formats a [NixHash] in the format that's used inside CAHash,
+    /// which is the algo, followed by a colon, then the nixbase32-encoded digest.
+    pub(crate) fn to_nix_nixbase32_string(&self) -> String {
+        format!(
+            "{}:{}",
+            self.algo(),
+            nixbase32::encode(self.digest_as_bytes())
+        )
+    }
+
+    /// Returns the digest as a hex string -- without any algorithm prefix.
+    pub fn to_plain_hex_string(&self) -> String {
+        HEXLOWER.encode(self.digest_as_bytes())
+    }
+}
+
+impl TryFrom<(HashAlgo, &[u8])> for NixHash {
+    type Error = Error;
+
+    /// Constructs a new [NixHash] by specifying [HashAlgo] and digest.
+    /// It can fail if the passed digest length doesn't match what's expected for
+    /// the passed algo.
+    fn try_from(value: (HashAlgo, &[u8])) -> NixHashResult<Self> {
+        let (algo, digest) = value;
+        from_algo_and_digest(algo, digest)
+    }
+}
+
+impl<'de> Deserialize<'de> for NixHash {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let str: &'de str = Deserialize::deserialize(deserializer)?;
+        from_str(str, None).map_err(|_| {
+            serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"NixHash")
+        })
+    }
+}
+
+impl Serialize for NixHash {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        // encode as SRI
+        let string = format!("{}-{}", self.algo(), BASE64.encode(self.digest_as_bytes()));
+        string.serialize(serializer)
+    }
+}
+
+/// Constructs a new [NixHash] by specifying [HashAlgo] and digest.
+/// It can fail if the passed digest length doesn't match what's expected for
+/// the passed algo.
+pub fn from_algo_and_digest(algo: HashAlgo, digest: &[u8]) -> NixHashResult<NixHash> {
+    if digest.len() != algo.digest_length() {
+        return Err(Error::InvalidEncodedDigestLength(digest.len(), algo));
+    }
+
+    Ok(match algo {
+        HashAlgo::Md5 => NixHash::Md5(digest.try_into().unwrap()),
+        HashAlgo::Sha1 => NixHash::Sha1(digest.try_into().unwrap()),
+        HashAlgo::Sha256 => NixHash::Sha256(digest.try_into().unwrap()),
+        HashAlgo::Sha512 => NixHash::Sha512(Box::new(digest.try_into().unwrap())),
+    })
+}
+
+/// Errors related to NixHash construction.
+#[derive(Debug, Eq, PartialEq, thiserror::Error)]
+pub enum Error {
+    #[error("invalid hash algo: {0}")]
+    InvalidAlgo(String),
+    #[error("invalid SRI string: {0}")]
+    InvalidSRI(String),
+    #[error("invalid encoded digest length '{0}' for algo {1}")]
+    InvalidEncodedDigestLength(usize, HashAlgo),
+    #[error("invalid base16 encoding: {0}")]
+    InvalidBase16Encoding(data_encoding::DecodeError),
+    #[error("invalid base32 encoding: {0}")]
+    InvalidBase32Encoding(data_encoding::DecodeError),
+    #[error("invalid base64 encoding: {0}")]
+    InvalidBase64Encoding(data_encoding::DecodeError),
+    #[error("conflicting hash algo: {0} (hash_algo) vs {1} (inline)")]
+    ConflictingHashAlgos(HashAlgo, HashAlgo),
+    #[error("missing inline hash algo, but no externally-specified algo: {0}")]
+    MissingInlineHashAlgo(String),
+}
+
+/// Nix allows specifying hashes in various encodings, and magically just
+/// derives the encoding.
+/// This function parses strings to a NixHash.
+///
+/// Hashes can be:
+/// - Nix hash strings
+/// - SRI hashes
+/// - bare digests
+///
+/// Encoding for Nix hash strings or bare digests can be:
+/// - base16 (lowerhex),
+/// - nixbase32,
+/// - base64 (StdEncoding)
+/// - sri string
+///
+/// The encoding is derived from the length of the string and the hash type.
+/// The hash is communicated out-of-band, but might also be in-band (in the
+/// case of a nix hash string or SRI), in which it needs to be consistent with the
+/// one communicated out-of-band.
+pub fn from_str(s: &str, algo_str: Option<&str>) -> NixHashResult<NixHash> {
+    // if algo_str is some, parse or bail out
+    let algo: Option<HashAlgo> = if let Some(algo_str) = algo_str {
+        Some(algo_str.try_into()?)
+    } else {
+        None
+    };
+
+    // Peek at the beginning of the string to detect SRI hashes.
+    if s.starts_with("sha1-")
+        || s.starts_with("sha256-")
+        || s.starts_with("sha512-")
+        || s.starts_with("md5-")
+    {
+        let parsed_nixhash = from_sri_str(s)?;
+
+        // ensure the algo matches with what has been passed externally, if so.
+        if let Some(algo) = algo {
+            if algo != parsed_nixhash.algo() {
+                return Err(Error::ConflictingHashAlgos(algo, parsed_nixhash.algo()));
+            }
+        }
+        return Ok(parsed_nixhash);
+    }
+
+    // Peek at the beginning again to see if it's a Nix Hash
+    if s.starts_with("sha1:")
+        || s.starts_with("sha256:")
+        || s.starts_with("sha512:")
+        || s.starts_with("md5:")
+    {
+        let parsed_nixhash = from_nix_str(s)?;
+        // ensure the algo matches with what has been passed externally, if so.
+        if let Some(algo) = algo {
+            if algo != parsed_nixhash.algo() {
+                return Err(Error::ConflictingHashAlgos(algo, parsed_nixhash.algo()));
+            }
+        }
+        return Ok(parsed_nixhash);
+    }
+
+    // Neither of these, assume a bare digest, so there MUST be an externally-passed algo.
+    match algo {
+        // Fail if there isn't.
+        None => Err(Error::MissingInlineHashAlgo(s.to_string())),
+        Some(algo) => decode_digest(s.as_bytes(), algo),
+    }
+}
+
+/// Parses a Nix hash string ($algo:$digest) to a NixHash.
+pub fn from_nix_str(s: &str) -> NixHashResult<NixHash> {
+    if let Some(rest) = s.strip_prefix("sha1:") {
+        decode_digest(rest.as_bytes(), HashAlgo::Sha1)
+    } else if let Some(rest) = s.strip_prefix("sha256:") {
+        decode_digest(rest.as_bytes(), HashAlgo::Sha256)
+    } else if let Some(rest) = s.strip_prefix("sha512:") {
+        decode_digest(rest.as_bytes(), HashAlgo::Sha512)
+    } else if let Some(rest) = s.strip_prefix("md5:") {
+        decode_digest(rest.as_bytes(), HashAlgo::Md5)
+    } else {
+        Err(Error::InvalidAlgo(s.to_string()))
+    }
+}
+
+/// Parses a Nix SRI string to a NixHash.
+/// Contrary to the SRI spec, Nix doesn't have an understanding of passing
+/// multiple hashes (with different algos) in SRI hashes.
+/// It instead simply cuts everything off after the expected length for the
+/// specified algo, and tries to parse the rest in permissive base64 (allowing
+/// missing padding).
+pub fn from_sri_str(s: &str) -> NixHashResult<NixHash> {
+    // split at the first occurence of "-"
+    let (algo_str, digest_str) = s
+        .split_once('-')
+        .ok_or_else(|| Error::InvalidSRI(s.to_string()))?;
+
+    // try to map the part before that `-` to a supported hash algo:
+    let algo: HashAlgo = algo_str.try_into()?;
+
+    // For the digest string, Nix ignores everything after the expected BASE64
+    // (with padding) length, to account for the fact SRI allows specifying more
+    // than one checksum, so shorten it.
+    let digest_str = {
+        let encoded_max_len = BASE64.encode_len(algo.digest_length());
+        if digest_str.len() > encoded_max_len {
+            digest_str[..encoded_max_len].as_bytes()
+        } else {
+            digest_str.as_bytes()
+        }
+    };
+
+    // if the digest string is too small to fit even the BASE64_NOPAD version, bail out.
+    if digest_str.len() < BASE64_NOPAD.encode_len(algo.digest_length()) {
+        return Err(Error::InvalidEncodedDigestLength(digest_str.len(), algo));
+    }
+
+    // trim potential padding, and use a version that does not do trailing bit
+    // checking.
+    let mut spec = BASE64_NOPAD.specification();
+    spec.check_trailing_bits = false;
+    let encoding = spec
+        .encoding()
+        .expect("Tvix bug: failed to get the special base64 encoder for Nix SRI hashes");
+
+    let digest = encoding
+        .decode(digest_str.trim_end_with(|c| c == '='))
+        .map_err(Error::InvalidBase64Encoding)?;
+
+    from_algo_and_digest(algo, &digest)
+}
+
+/// Decode a plain digest depending on the hash algo specified externally.
+/// hexlower, nixbase32 and base64 encodings are supported - the encoding is
+/// inferred from the input length.
+fn decode_digest(s: &[u8], algo: HashAlgo) -> NixHashResult<NixHash> {
+    // for the chosen hash algo, calculate the expected (decoded) digest length
+    // (as bytes)
+    let digest = if s.len() == HEXLOWER.encode_len(algo.digest_length()) {
+        HEXLOWER
+            .decode(s.as_ref())
+            .map_err(Error::InvalidBase16Encoding)?
+    } else if s.len() == nixbase32::encode_len(algo.digest_length()) {
+        nixbase32::decode(s).map_err(Error::InvalidBase32Encoding)?
+    } else if s.len() == BASE64.encode_len(algo.digest_length()) {
+        BASE64
+            .decode(s.as_ref())
+            .map_err(Error::InvalidBase64Encoding)?
+    } else {
+        Err(Error::InvalidEncodedDigestLength(s.len(), algo))?
+    };
+
+    Ok(from_algo_and_digest(algo, &digest).unwrap())
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        nixbase32,
+        nixhash::{self, HashAlgo, NixHash},
+    };
+    use data_encoding::{BASE64, BASE64_NOPAD, HEXLOWER};
+    use hex_literal::hex;
+    use rstest::rstest;
+
+    const DIGEST_SHA1: [u8; 20] = hex!("6016777997c30ab02413cf5095622cd7924283ac");
+    const DIGEST_SHA256: [u8; 32] =
+        hex!("a5ce9c155ed09397614646c9717fc7cd94b1023d7b76b618d409e4fefd6e9d39");
+    const DIGEST_SHA512: [u8; 64] = hex!("ab40d0be3541f0774bba7815d13d10b03252e96e95f7dbb4ee99a3b431c21662fd6971a020160e39848aa5f305b9be0f78727b2b0789e39f124d21e92b8f39ef");
+    const DIGEST_MD5: [u8; 16] = hex!("c4874a8897440b393d862d8fd459073f");
+
+    fn to_base16(digest: &[u8]) -> String {
+        HEXLOWER.encode(digest)
+    }
+
+    fn to_nixbase32(digest: &[u8]) -> String {
+        nixbase32::encode(digest)
+    }
+
+    fn to_base64(digest: &[u8]) -> String {
+        BASE64.encode(digest)
+    }
+
+    fn to_base64_nopad(digest: &[u8]) -> String {
+        BASE64_NOPAD.encode(digest)
+    }
+
+    // TODO
+    fn make_nixhash(algo: &HashAlgo, digest_encoded: String) -> String {
+        format!("{}:{}", algo, digest_encoded)
+    }
+    fn make_sri_string(algo: &HashAlgo, digest_encoded: String) -> String {
+        format!("{}-{}", algo, digest_encoded)
+    }
+
+    /// Test parsing a hash string in various formats, and also when/how the out-of-band algo is needed.
+    #[rstest]
+    #[case::sha1(&NixHash::Sha1(DIGEST_SHA1))]
+    #[case::sha256(&NixHash::Sha256(DIGEST_SHA256))]
+    #[case::sha512(&NixHash::Sha512(Box::new(DIGEST_SHA512)))]
+    #[case::md5(&NixHash::Md5(DIGEST_MD5))]
+    fn from_str(#[case] expected_hash: &NixHash) {
+        let algo = &expected_hash.algo();
+        let digest = expected_hash.digest_as_bytes();
+        // parse SRI
+        {
+            // base64 without out-of-band algo
+            let s = make_sri_string(algo, to_base64(digest));
+            let h = nixhash::from_str(&s, None).expect("must succeed");
+            assert_eq!(expected_hash, &h);
+
+            // base64 with out-of-band-algo
+            let s = make_sri_string(algo, to_base64(digest));
+            let h = nixhash::from_str(&s, Some(&expected_hash.algo().to_string()))
+                .expect("must succeed");
+            assert_eq!(expected_hash, &h);
+
+            // base64_nopad without out-of-band algo
+            let s = make_sri_string(algo, to_base64_nopad(digest));
+            let h = nixhash::from_str(&s, None).expect("must succeed");
+            assert_eq!(expected_hash, &h);
+
+            // base64_nopad with out-of-band-algo
+            let s = make_sri_string(algo, to_base64_nopad(digest));
+            let h = nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed");
+            assert_eq!(expected_hash, &h);
+        }
+
+        // parse plain base16. should succeed with algo out-of-band, but fail without.
+        {
+            let s = to_base16(digest);
+            nixhash::from_str(&s, None).expect_err("must fail");
+            let h = nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed");
+            assert_eq!(expected_hash, &h);
+        }
+
+        // parse plain nixbase32. should succeed with algo out-of-band, but fail without.
+        {
+            let s = to_nixbase32(digest);
+            nixhash::from_str(&s, None).expect_err("must fail");
+            let h = nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed");
+            assert_eq!(expected_hash, &h);
+        }
+
+        // parse plain base64. should succeed with algo out-of-band, but fail without.
+        {
+            let s = to_base64(digest);
+            nixhash::from_str(&s, None).expect_err("must fail");
+            let h = nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed");
+            assert_eq!(expected_hash, &h);
+        }
+
+        // parse Nix hash strings
+        {
+            // base16. should succeed with both algo out-of-band and in-band.
+            {
+                let s = make_nixhash(algo, to_base16(digest));
+                assert_eq!(
+                    expected_hash,
+                    &nixhash::from_str(&s, None).expect("must succeed")
+                );
+                assert_eq!(
+                    expected_hash,
+                    &nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed")
+                );
+            }
+            // nixbase32. should succeed with both algo out-of-band and in-band.
+            {
+                let s = make_nixhash(algo, to_nixbase32(digest));
+                assert_eq!(
+                    expected_hash,
+                    &nixhash::from_str(&s, None).expect("must succeed")
+                );
+                assert_eq!(
+                    expected_hash,
+                    &nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed")
+                );
+            }
+            // base64. should succeed with both algo out-of-band and in-band.
+            {
+                let s = make_nixhash(algo, to_base64(digest));
+                assert_eq!(
+                    expected_hash,
+                    &nixhash::from_str(&s, None).expect("must succeed")
+                );
+                assert_eq!(
+                    expected_hash,
+                    &nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed")
+                );
+            }
+        }
+    }
+
+    /// Test parsing an SRI hash via the [nixhash::from_sri_str] method.
+    #[test]
+    fn from_sri_str() {
+        let nix_hash = nixhash::from_sri_str("sha256-pc6cFV7Qk5dhRkbJcX/HzZSxAj17drYY1Ank/v1unTk=")
+            .expect("must succeed");
+
+        assert_eq!(HashAlgo::Sha256, nix_hash.algo());
+        assert_eq!(
+            &hex!("a5ce9c155ed09397614646c9717fc7cd94b1023d7b76b618d409e4fefd6e9d39"),
+            nix_hash.digest_as_bytes()
+        )
+    }
+
+    /// Test parsing sha512 SRI hash with various paddings, Nix accepts all of them.
+    #[rstest]
+    #[case::no_padding("sha512-7g91TBvYoYQorRTqo+rYD/i5YnWvUBLnqDhPHxBJDaBW7smuPMeRp6E6JOFuVN9bzN0QnH1ToUU0u9c2CjALEQ")]
+    #[case::too_little_padding("sha512-7g91TBvYoYQorRTqo+rYD/i5YnWvUBLnqDhPHxBJDaBW7smuPMeRp6E6JOFuVN9bzN0QnH1ToUU0u9c2CjALEQ=")]
+    #[case::correct_padding("sha512-7g91TBvYoYQorRTqo+rYD/i5YnWvUBLnqDhPHxBJDaBW7smuPMeRp6E6JOFuVN9bzN0QnH1ToUU0u9c2CjALEQ==")]
+    #[case::too_much_padding("sha512-7g91TBvYoYQorRTqo+rYD/i5YnWvUBLnqDhPHxBJDaBW7smuPMeRp6E6JOFuVN9bzN0QnH1ToUU0u9c2CjALEQ===")]
+    #[case::additional_suffix_ignored("sha512-7g91TBvYoYQorRTqo+rYD/i5YnWvUBLnqDhPHxBJDaBW7smuPMeRp6E6JOFuVN9bzN0QnH1ToUU0u9c2CjALEQ== cheesecake")]
+    fn from_sri_str_sha512_paddings(#[case] sri_str: &str) {
+        let nix_hash = nixhash::from_sri_str(sri_str).expect("must succeed");
+
+        assert_eq!(HashAlgo::Sha512, nix_hash.algo());
+        assert_eq!(
+            &hex!("ee0f754c1bd8a18428ad14eaa3ead80ff8b96275af5012e7a8384f1f10490da056eec9ae3cc791a7a13a24e16e54df5bccdd109c7d53a14534bbd7360a300b11"),
+            nix_hash.digest_as_bytes()
+        )
+    }
+
+    /// Ensure we detect truncated base64 digests, where the digest size
+    /// doesn't match what's expected from that hash function.
+    #[test]
+    fn from_sri_str_truncated() {
+        nixhash::from_sri_str("sha256-pc6cFV7Qk5dhRkbJcX/HzZSxAj17drYY1Ank")
+            .expect_err("must fail");
+    }
+
+    /// Ensure we fail on SRI hashes that Nix doesn't support.
+    #[test]
+    fn from_sri_str_unsupported() {
+        nixhash::from_sri_str(
+            "sha384-o4UVSl89mIB0sFUK+3jQbG+C9Zc9dRlV/Xd3KAvXEbhqxu0J5OAdg6b6VHKHwQ7U",
+        )
+        .expect_err("must fail");
+    }
+
+    /// Ensure we reject invalid base64 encoding
+    #[test]
+    fn from_sri_str_invalid_base64() {
+        nixhash::from_sri_str("sha256-invalid=base64").expect_err("must fail");
+    }
+
+    /// Nix also accepts SRI strings with missing padding, but only in case the
+    /// string is expressed as SRI, so it still needs to have a `sha256-` prefix.
+    ///
+    /// This both seems to work if it is passed with and without specifying the
+    /// hash algo out-of-band (hash = "sha256-…" or sha256 = "sha256-…")
+    ///
+    /// Passing the same broken base64 string, but not as SRI, while passing
+    /// the hash algo out-of-band does not work.
+    #[test]
+    fn sha256_broken_padding() {
+        let broken_base64 = "fgIr3TyFGDAXP5+qoAaiMKDg/a1MlT6Fv/S/DaA24S8";
+        // if padded with a trailing '='
+        let expected_digest =
+            hex!("7e022bdd3c851830173f9faaa006a230a0e0fdad4c953e85bff4bf0da036e12f");
+
+        // passing hash algo out of band should succeed
+        let nix_hash = nixhash::from_str(&format!("sha256-{}", &broken_base64), Some("sha256"))
+            .expect("must succeed");
+        assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
+
+        // not passing hash algo out of band should succeed
+        let nix_hash =
+            nixhash::from_str(&format!("sha256-{}", &broken_base64), None).expect("must succeed");
+        assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
+
+        // not passing SRI, but hash algo out of band should fail
+        nixhash::from_str(broken_base64, Some("sha256")).expect_err("must fail");
+    }
+
+    /// As we decided to pass our hashes by trimming `=` completely,
+    /// we need to take into account hashes with padding requirements which
+    /// contains trailing bits which would be checked by `BASE64_NOPAD` and would
+    /// make the verification crash.
+    ///
+    /// This base64 has a trailing non-zero bit at bit 42.
+    #[test]
+    fn sha256_weird_base64() {
+        let weird_base64 = "syceJMUEknBDCHK8eGs6rUU3IQn+HnQfURfCrDxYPa9=";
+        let expected_digest =
+            hex!("b3271e24c5049270430872bc786b3aad45372109fe1e741f5117c2ac3c583daf");
+
+        let nix_hash = nixhash::from_str(&format!("sha256-{}", &weird_base64), Some("sha256"))
+            .expect("must succeed");
+        assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
+
+        // not passing hash algo out of band should succeed
+        let nix_hash =
+            nixhash::from_str(&format!("sha256-{}", &weird_base64), None).expect("must succeed");
+        assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
+
+        // not passing SRI, but hash algo out of band should fail
+        nixhash::from_str(weird_base64, Some("sha256")).expect_err("must fail");
+    }
+
+    #[test]
+    fn serialize_deserialize() {
+        let nixhash_actual = NixHash::Sha256(hex!(
+            "b3271e24c5049270430872bc786b3aad45372109fe1e741f5117c2ac3c583daf"
+        ));
+        let nixhash_str_json = "\"sha256-syceJMUEknBDCHK8eGs6rUU3IQn+HnQfURfCrDxYPa8=\"";
+
+        let serialized = serde_json::to_string(&nixhash_actual).expect("can serialize");
+
+        assert_eq!(nixhash_str_json, &serialized);
+
+        let deserialized: NixHash =
+            serde_json::from_str(nixhash_str_json).expect("must deserialize");
+        assert_eq!(&nixhash_actual, &deserialized);
+    }
+}
diff --git a/tvix/nix-compat/src/path_info.rs b/tvix/nix-compat/src/path_info.rs
new file mode 100644
index 0000000000..f289ebde33
--- /dev/null
+++ b/tvix/nix-compat/src/path_info.rs
@@ -0,0 +1,121 @@
+use crate::{nixbase32, nixhash::NixHash, store_path::StorePathRef};
+use serde::{Deserialize, Serialize};
+use std::collections::BTreeSet;
+
+/// Represents information about a Store Path that Nix provides inside the build
+/// if the exportReferencesGraph feature is used.
+/// This is not to be confused with the format Nix uses in its `nix path-info` command.
+/// It includes some more fields, like `registrationTime`, `signatures` and `ultimate`,
+/// does not include the `closureSize` and encodes `narHash` as SRI.
+#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
+pub struct ExportedPathInfo<'a> {
+    #[serde(rename = "closureSize")]
+    pub closure_size: u64,
+
+    #[serde(
+        rename = "narHash",
+        serialize_with = "to_nix_nixbase32_string",
+        deserialize_with = "from_nix_nixbase32_string"
+    )]
+    pub nar_sha256: [u8; 32],
+
+    #[serde(rename = "narSize")]
+    pub nar_size: u64,
+
+    #[serde(borrow)]
+    pub path: StorePathRef<'a>,
+
+    /// The list of other Store Paths this Store Path refers to.
+    /// StorePathRef does Ord by the nixbase32-encoded string repr, so this is correct.
+    pub references: BTreeSet<StorePathRef<'a>>,
+    // more recent versions of Nix also have a `valid: true` field here, Nix 2.3 doesn't,
+    // and nothing seems to use it.
+}
+
+/// ExportedPathInfo are ordered by their `path` field.
+impl Ord for ExportedPathInfo<'_> {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.path.cmp(&other.path)
+    }
+}
+
+impl PartialOrd for ExportedPathInfo<'_> {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+fn to_nix_nixbase32_string<S>(v: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: serde::Serializer,
+{
+    let string = NixHash::Sha256(*v).to_nix_nixbase32_string();
+    string.serialize(serializer)
+}
+
+/// The length of a sha256 digest, nixbase32-encoded.
+const NIXBASE32_SHA256_ENCODE_LEN: usize = nixbase32::encode_len(32);
+
+fn from_nix_nixbase32_string<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
+where
+    D: serde::Deserializer<'de>,
+{
+    let str: &'de str = Deserialize::deserialize(deserializer)?;
+
+    let digest_str = str.strip_prefix("sha256:").ok_or_else(|| {
+        serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"sha256:…")
+    })?;
+
+    let digest_str: [u8; NIXBASE32_SHA256_ENCODE_LEN] =
+        digest_str.as_bytes().try_into().map_err(|_| {
+            serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"valid digest len")
+        })?;
+
+    let digest: [u8; 32] = nixbase32::decode_fixed(digest_str).map_err(|_| {
+        serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"valid nixbase32")
+    })?;
+
+    Ok(digest)
+}
+
+#[cfg(test)]
+mod tests {
+    use hex_literal::hex;
+
+    use super::*;
+
+    /// Ensure we can create the same JSON as the exportReferencesGraph feature
+    #[test]
+    fn serialize_deserialize() {
+        // JSON extracted from a build of
+        // stdenv.mkDerivation { name = "hello"; __structuredAttrs = true; exportReferencesGraph.blub = [ pkgs.hello ]; nativeBuildInputs = [pkgs.jq]; buildCommand = "jq -rc .blub $NIX_ATTRS_JSON_FILE > $out"; }
+        let pathinfos_str_json = r#"[{"closureSize":1828984,"narHash":"sha256:11vm2x1ajhzsrzw7lsyss51mmr3b6yll9wdjn51bh7liwkpc8ila","narSize":1828984,"path":"/nix/store/7n0mbqydcipkpbxm24fab066lxk68aqk-libunistring-1.1","references":["/nix/store/7n0mbqydcipkpbxm24fab066lxk68aqk-libunistring-1.1"]},{"closureSize":32696176,"narHash":"sha256:0alzbhjxdcsmr1pk7z0bdh46r2xpq3xs3k9y82bi4bx5pklcvw5x","narSize":226560,"path":"/nix/store/dbghhbq1x39yxgkv3vkgfwbxrmw9nfzi-hello-2.12.1","references":["/nix/store/dbghhbq1x39yxgkv3vkgfwbxrmw9nfzi-hello-2.12.1","/nix/store/ddwyrxif62r8n6xclvskjyy6szdhvj60-glibc-2.39-5"]},{"closureSize":32469616,"narHash":"sha256:1zw5p05fh0k836ybfxkskv8apcv2m3pm2wa6y90wqn5w5kjyj13c","narSize":30119936,"path":"/nix/store/ddwyrxif62r8n6xclvskjyy6szdhvj60-glibc-2.39-5","references":["/nix/store/ddwyrxif62r8n6xclvskjyy6szdhvj60-glibc-2.39-5","/nix/store/rxganm4ibf31qngal3j3psp20mak37yy-xgcc-13.2.0-libgcc","/nix/store/s32cldbh9pfzd9z82izi12mdlrw0yf8q-libidn2-2.3.7"]},{"closureSize":159560,"narHash":"sha256:10q8iyvfmpfck3yiisnj1j8vp6lq3km17r26sr95zpdf9mgmk69s","narSize":159560,"path":"/nix/store/rxganm4ibf31qngal3j3psp20mak37yy-xgcc-13.2.0-libgcc","references":[]},{"closureSize":2190120,"narHash":"sha256:1cv997nzxbd91jhmzwnhxa1ahlzp5ffli8m4a5npcq8zg0vb1kwg","narSize":361136,"path":"/nix/store/s32cldbh9pfzd9z82izi12mdlrw0yf8q-libidn2-2.3.7","references":["/nix/store/7n0mbqydcipkpbxm24fab066lxk68aqk-libunistring-1.1","/nix/store/s32cldbh9pfzd9z82izi12mdlrw0yf8q-libidn2-2.3.7"]}]"#;
+
+        // We ensure it roundtrips (to check the sorting is correct)
+        let deserialized: BTreeSet<ExportedPathInfo> =
+            serde_json::from_str(pathinfos_str_json).expect("must serialize");
+
+        let serialized_again = serde_json::to_string(&deserialized).expect("must deserialize");
+        assert_eq!(pathinfos_str_json, serialized_again);
+
+        // Also compare one specific item to be populated as expected.
+        assert_eq!(
+            &ExportedPathInfo {
+                closure_size: 1828984,
+                nar_sha256: hex!(
+                    "8a46c4eee4911eb842b1b2f144a9376be45a43d1da6b7af8cffa43a942177587"
+                ),
+                nar_size: 1828984,
+                path: StorePathRef::from_bytes(
+                    b"7n0mbqydcipkpbxm24fab066lxk68aqk-libunistring-1.1"
+                )
+                .expect("must parse"),
+                references: BTreeSet::from_iter([StorePathRef::from_bytes(
+                    b"7n0mbqydcipkpbxm24fab066lxk68aqk-libunistring-1.1"
+                )
+                .unwrap()]),
+            },
+            deserialized.first().unwrap()
+        );
+    }
+}
diff --git a/tvix/nix-compat/src/store_path/mod.rs b/tvix/nix-compat/src/store_path/mod.rs
new file mode 100644
index 0000000000..ff7ede77e1
--- /dev/null
+++ b/tvix/nix-compat/src/store_path/mod.rs
@@ -0,0 +1,635 @@
+use crate::nixbase32;
+use data_encoding::{DecodeError, BASE64};
+use serde::{Deserialize, Serialize};
+use std::{
+    fmt,
+    path::PathBuf,
+    str::{self, FromStr},
+};
+use thiserror;
+
+#[cfg(target_family = "unix")]
+use std::os::unix::ffi::OsStringExt;
+
+mod utils;
+
+pub use utils::*;
+
+pub const DIGEST_SIZE: usize = 20;
+pub const ENCODED_DIGEST_SIZE: usize = nixbase32::encode_len(DIGEST_SIZE);
+
+// The store dir prefix, without trailing slash.
+// That's usually where the Nix store is mounted at.
+pub const STORE_DIR: &str = "/nix/store";
+pub const STORE_DIR_WITH_SLASH: &str = "/nix/store/";
+
+/// Errors that can occur when parsing a literal store path
+#[derive(Debug, PartialEq, Eq, thiserror::Error)]
+pub enum Error {
+    #[error("Dash is missing between hash and name")]
+    MissingDash,
+    #[error("Hash encoding is invalid: {0}")]
+    InvalidHashEncoding(#[from] DecodeError),
+    #[error("Invalid length")]
+    InvalidLength,
+    #[error(
+        "Invalid name: \"{}\", character at position {} is invalid",
+        std::str::from_utf8(.0).unwrap_or(&BASE64.encode(.0)),
+        .1,
+    )]
+    InvalidName(Vec<u8>, u8),
+    #[error("Tried to parse an absolute path which was missing the store dir prefix.")]
+    MissingStoreDir,
+}
+
+/// Represents a path in the Nix store (a direct child of [STORE_DIR]).
+///
+/// It consists of a digest (20 bytes), and a name, which is a string.
+/// The name may only contain ASCII alphanumeric, or one of the following
+/// characters: `-`, `_`, `.`, `+`, `?`, `=`.
+/// The name is usually used to describe the pname and version of a package.
+/// Derivation paths can also be represented as store paths, their names just
+/// end with the `.drv` prefix.
+///
+/// A [StorePath] does not encode any additional subpath "inside" the store
+/// path.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct StorePath {
+    digest: [u8; DIGEST_SIZE],
+    name: String,
+}
+
+impl StorePath {
+    pub fn digest(&self) -> &[u8; DIGEST_SIZE] {
+        &self.digest
+    }
+
+    pub fn name(&self) -> &str {
+        self.name.as_ref()
+    }
+
+    pub fn as_ref(&self) -> StorePathRef<'_> {
+        StorePathRef {
+            digest: self.digest,
+            name: &self.name,
+        }
+    }
+}
+
+impl PartialOrd for StorePath {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+/// `StorePath`s are sorted by their reverse digest to match the sorting order
+/// of the nixbase32-encoded string.
+impl Ord for StorePath {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.as_ref().cmp(&other.as_ref())
+    }
+}
+
+impl FromStr for StorePath {
+    type Err = Error;
+
+    /// Construct a [StorePath] by passing the `$digest-$name` string
+    /// that comes after [STORE_DIR_WITH_SLASH].
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Self::from_bytes(s.as_bytes())
+    }
+}
+
+impl StorePath {
+    /// Construct a [StorePath] by passing the `$digest-$name` string
+    /// that comes after [STORE_DIR_WITH_SLASH].
+    pub fn from_bytes(s: &[u8]) -> Result<StorePath, Error> {
+        Ok(StorePathRef::from_bytes(s)?.to_owned())
+    }
+
+    /// Decompose a string into a [StorePath] and a [PathBuf] containing the
+    /// rest of the path, or an error.
+    #[cfg(target_family = "unix")]
+    pub fn from_absolute_path_full(s: &str) -> Result<(StorePath, PathBuf), Error> {
+        // strip [STORE_DIR_WITH_SLASH] from s
+        match s.strip_prefix(STORE_DIR_WITH_SLASH) {
+            None => Err(Error::MissingStoreDir),
+            Some(rest) => {
+                // put rest in a PathBuf
+                let mut p = PathBuf::new();
+                p.push(rest);
+
+                let mut it = p.components();
+
+                // The first component of the rest must be parse-able as a [StorePath]
+                if let Some(first_component) = it.next() {
+                    // convert first component to StorePath
+                    let first_component_bytes = first_component.as_os_str().to_owned().into_vec();
+                    let store_path = StorePath::from_bytes(&first_component_bytes)?;
+                    // collect rest
+                    let rest_buf: PathBuf = it.collect();
+                    Ok((store_path, rest_buf))
+                } else {
+                    Err(Error::InvalidLength) // Well, or missing "/"?
+                }
+            }
+        }
+    }
+
+    /// Returns an absolute store path string.
+    /// That is just the string representation, prefixed with the store prefix
+    /// ([STORE_DIR_WITH_SLASH]),
+    pub fn to_absolute_path(&self) -> String {
+        let sp_ref: StorePathRef = self.into();
+        sp_ref.to_absolute_path()
+    }
+}
+
+impl<'de> Deserialize<'de> for StorePath {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let r = <StorePathRef<'de> as Deserialize<'de>>::deserialize(deserializer)?;
+        Ok(r.to_owned())
+    }
+}
+
+impl Serialize for StorePath {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let r: StorePathRef = self.into();
+        r.serialize(serializer)
+    }
+}
+
+/// Like [StorePath], but without a heap allocation for the name.
+/// Used by [StorePath] for parsing.
+///
+#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
+pub struct StorePathRef<'a> {
+    digest: [u8; DIGEST_SIZE],
+    name: &'a str,
+}
+
+impl<'a> From<&'a StorePath> for StorePathRef<'a> {
+    fn from(&StorePath { digest, ref name }: &'a StorePath) -> Self {
+        StorePathRef {
+            digest,
+            name: name.as_ref(),
+        }
+    }
+}
+
+impl<'a> PartialOrd for StorePathRef<'a> {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+/// `StorePathRef`s are sorted by their reverse digest to match the sorting order
+/// of the nixbase32-encoded string.
+impl<'a> Ord for StorePathRef<'a> {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.digest.iter().rev().cmp(other.digest.iter().rev())
+    }
+}
+
+impl<'a> StorePathRef<'a> {
+    pub fn digest(&self) -> &[u8; DIGEST_SIZE] {
+        &self.digest
+    }
+
+    pub fn name(&self) -> &'a str {
+        self.name
+    }
+
+    pub fn to_owned(&self) -> StorePath {
+        StorePath {
+            digest: self.digest,
+            name: self.name.to_owned(),
+        }
+    }
+
+    /// Construct a [StorePathRef] from a name and digest.
+    /// The name is validated, and the digest checked for size.
+    pub fn from_name_and_digest(name: &'a str, digest: &[u8]) -> Result<Self, Error> {
+        let digest_fixed = digest.try_into().map_err(|_| Error::InvalidLength)?;
+        Self::from_name_and_digest_fixed(name, digest_fixed)
+    }
+
+    /// Construct a [StorePathRef] from a name and digest of correct length.
+    /// The name is validated.
+    pub fn from_name_and_digest_fixed(
+        name: &'a str,
+        digest: [u8; DIGEST_SIZE],
+    ) -> Result<Self, Error> {
+        Ok(Self {
+            name: validate_name(name.as_bytes())?,
+            digest,
+        })
+    }
+
+    /// Construct a [StorePathRef] from an absolute store path string.
+    /// This is equivalent to calling [StorePathRef::from_bytes], but stripping
+    /// the [STORE_DIR_WITH_SLASH] prefix before.
+    pub fn from_absolute_path(s: &'a [u8]) -> Result<Self, Error> {
+        match s.strip_prefix(STORE_DIR_WITH_SLASH.as_bytes()) {
+            Some(s_stripped) => Self::from_bytes(s_stripped),
+            None => Err(Error::MissingStoreDir),
+        }
+    }
+
+    /// Construct a [StorePathRef] by passing the `$digest-$name` string
+    /// that comes after [STORE_DIR_WITH_SLASH].
+    pub fn from_bytes(s: &'a [u8]) -> Result<Self, Error> {
+        // the whole string needs to be at least:
+        //
+        // - 32 characters (encoded hash)
+        // - 1 dash
+        // - 1 character for the name
+        if s.len() < ENCODED_DIGEST_SIZE + 2 {
+            Err(Error::InvalidLength)?
+        }
+
+        let digest = nixbase32::decode_fixed(&s[..ENCODED_DIGEST_SIZE])?;
+
+        if s[ENCODED_DIGEST_SIZE] != b'-' {
+            return Err(Error::MissingDash);
+        }
+
+        Ok(StorePathRef {
+            digest,
+            name: validate_name(&s[ENCODED_DIGEST_SIZE + 1..])?,
+        })
+    }
+
+    /// Returns an absolute store path string.
+    /// That is just the string representation, prefixed with the store prefix
+    /// ([STORE_DIR_WITH_SLASH]),
+    pub fn to_absolute_path(&self) -> String {
+        format!("{}{}", STORE_DIR_WITH_SLASH, self)
+    }
+}
+
+impl<'de: 'a, 'a> Deserialize<'de> for StorePathRef<'a> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let string: &'de str = Deserialize::deserialize(deserializer)?;
+        let stripped: Option<&str> = string.strip_prefix(STORE_DIR_WITH_SLASH);
+        let stripped: &str = stripped.ok_or_else(|| {
+            serde::de::Error::invalid_value(
+                serde::de::Unexpected::Str(string),
+                &"store path prefix",
+            )
+        })?;
+        StorePathRef::from_bytes(stripped.as_bytes()).map_err(|_| {
+            serde::de::Error::invalid_value(serde::de::Unexpected::Str(string), &"StorePath")
+        })
+    }
+}
+
+impl Serialize for StorePathRef<'_> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let string: String = self.to_absolute_path();
+        string.serialize(serializer)
+    }
+}
+
+/// NAME_CHARS contains `true` for bytes that are valid in store path names.
+static NAME_CHARS: [bool; 256] = {
+    let mut tbl = [false; 256];
+    let mut c = 0;
+
+    loop {
+        tbl[c as usize] = matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'+' | b'-' | b'_' | b'?' | b'=' | b'.');
+
+        if c == u8::MAX {
+            break;
+        }
+
+        c += 1;
+    }
+
+    tbl
+};
+
+/// Checks a given &[u8] to match the restrictions for [StorePath::name], and
+/// returns the name as string if successful.
+pub(crate) fn validate_name(s: &(impl AsRef<[u8]> + ?Sized)) -> Result<&str, Error> {
+    let s = s.as_ref();
+
+    // Empty or excessively long names are not allowed.
+    if s.is_empty() || s.len() > 211 {
+        return Err(Error::InvalidLength);
+    }
+
+    let mut valid = true;
+    for &c in s {
+        valid = valid && NAME_CHARS[c as usize];
+    }
+
+    if !valid {
+        for (i, &c) in s.iter().enumerate() {
+            if !NAME_CHARS[c as usize] {
+                return Err(Error::InvalidName(s.to_vec(), i as u8));
+            }
+        }
+
+        unreachable!();
+    }
+
+    // SAFETY: We permit a subset of ASCII, which guarantees valid UTF-8.
+    Ok(unsafe { str::from_utf8_unchecked(s) })
+}
+
+impl fmt::Display for StorePath {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        StorePathRef::from(self).fmt(f)
+    }
+}
+
+impl fmt::Display for StorePathRef<'_> {
+    /// The string representation of a store path starts with a digest (20
+    /// bytes), [crate::nixbase32]-encoded, followed by a `-`,
+    /// and ends with the name.
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}-{}", nixbase32::encode(&self.digest), self.name)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Error;
+    use std::cmp::Ordering;
+    use std::path::PathBuf;
+
+    use crate::store_path::{StorePath, StorePathRef, DIGEST_SIZE};
+    use hex_literal::hex;
+    use pretty_assertions::assert_eq;
+    use rstest::rstest;
+    use serde::Deserialize;
+
+    #[derive(Deserialize)]
+    /// An example struct, holding a StorePathRef.
+    /// Used to test deserializing StorePathRef.
+    struct Container<'a> {
+        #[serde(borrow)]
+        store_path: StorePathRef<'a>,
+    }
+
+    #[test]
+    fn happy_path() {
+        let example_nix_path_str =
+            "00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432";
+        let nixpath = StorePath::from_bytes(example_nix_path_str.as_bytes())
+            .expect("Error parsing example string");
+
+        let expected_digest: [u8; DIGEST_SIZE] = hex!("8a12321522fd91efbd60ebb2481af88580f61600");
+
+        assert_eq!("net-tools-1.60_p20170221182432", nixpath.name);
+        assert_eq!(nixpath.digest, expected_digest);
+
+        assert_eq!(example_nix_path_str, nixpath.to_string())
+    }
+
+    #[test]
+    fn store_path_ordering() {
+        let store_paths = [
+            "/nix/store/0lk5dgi01r933abzfj9c9wlndg82yd3g-psutil-5.9.6.tar.gz.drv",
+            "/nix/store/1xj43bva89f9qmwm37zl7r3d7m67i9ck-shorttoc-1.3-tex.drv",
+            "/nix/store/2gb633czchi20jq1kqv70rx2yvvgins8-lifted-base-0.2.3.12.tar.gz.drv",
+            "/nix/store/2vksym3r3zqhp15q3fpvw2mnvffv11b9-docbook-xml-4.5.zip.drv",
+            "/nix/store/5q918awszjcz5720xvpc2czbg1sdqsf0-rust_renaming-0.1.0-lib",
+            "/nix/store/7jw30i342sr2p1fmz5xcfnch65h4zbd9-dbus-1.14.10.tar.xz.drv",
+            "/nix/store/96yqwqhnp3qya4rf4n0rcl0lwvrylp6k-eap8021x-222.40.1.tar.gz.drv",
+            "/nix/store/9gjqg36a1v0axyprbya1hkaylmnffixg-virtualenv-20.24.5.tar.gz.drv",
+            "/nix/store/a4i5mci2g9ada6ff7ks38g11dg6iqyb8-perl-5.32.1.drv",
+            "/nix/store/a5g76ljava4h5pxlggz3aqdhs3a4fk6p-ToolchainInfo.plist.drv",
+            "/nix/store/db46l7d6nswgz4ffp1mmd56vjf9g51v6-version.plist.drv",
+            "/nix/store/g6f7w20sd7vwy0rc1r4bfsw4ciclrm4q-crates-io-num_cpus-1.12.0.drv",
+            "/nix/store/iw82n1wwssb8g6772yddn8c3vafgv9np-bootstrap-stage1-sysctl-stdenv-darwin.drv",
+            "/nix/store/lp78d1y5wxpcn32d5c4r7xgbjwiw0cgf-logo.svg.drv",
+            "/nix/store/mf00ank13scv1f9l1zypqdpaawjhfr3s-python3.11-psutil-5.9.6.drv",
+            "/nix/store/mpfml61ra7pz90124jx9r3av0kvkz2w1-perl5.36.0-Encode-Locale-1.05",
+            "/nix/store/qhsvwx4h87skk7c4mx0xljgiy3z93i23-source.drv",
+            "/nix/store/riv7d73adim8hq7i04pr8kd0jnj93nav-fdk-aac-2.0.2.tar.gz.drv",
+            "/nix/store/s64b9031wga7vmpvgk16xwxjr0z9ln65-human-signals-5.0.0.tgz-extracted",
+            "/nix/store/w6svg3m2xdh6dhx0gl1nwa48g57d3hxh-thiserror-1.0.49",
+        ];
+
+        for w in store_paths.windows(2) {
+            if w.len() < 2 {
+                continue;
+            }
+            let (pa, _) = StorePath::from_absolute_path_full(w[0]).expect("parseable");
+            let (pb, _) = StorePath::from_absolute_path_full(w[1]).expect("parseable");
+            assert_eq!(
+                Ordering::Less,
+                pa.cmp(&pb),
+                "{:?} not less than {:?}",
+                w[0],
+                w[1]
+            );
+        }
+    }
+
+    /// This is the store path *accepted* when `nix-store --add`'ing an
+    /// empty `.gitignore` file.
+    ///
+    /// Nix 2.4 accidentally permitted this behaviour, but the revert came
+    /// too late to beat Hyrum's law. It is now considered permissible.
+    ///
+    /// https://github.com/NixOS/nix/pull/9095 (revert)
+    /// https://github.com/NixOS/nix/pull/9867 (revert-of-revert)
+    #[test]
+    fn starts_with_dot() {
+        StorePath::from_bytes(b"fli4bwscgna7lpm7v5xgnjxrxh0yc7ra-.gitignore")
+            .expect("must succeed");
+    }
+
+    #[test]
+    fn empty_name() {
+        StorePath::from_bytes(b"00bgd045z0d4icpbc2yy-").expect_err("must fail");
+    }
+
+    #[test]
+    fn excessive_length() {
+        StorePath::from_bytes(b"00bgd045z0d4icpbc2yy-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+            .expect_err("must fail");
+    }
+
+    #[test]
+    fn invalid_hash_length() {
+        StorePath::from_bytes(b"00bgd045z0d4icpbc2yy-net-tools-1.60_p20170221182432")
+            .expect_err("must fail");
+    }
+
+    #[test]
+    fn invalid_encoding_hash() {
+        StorePath::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48aku4la-net-tools-1.60_p20170221182432")
+            .expect_err("must fail");
+    }
+
+    #[test]
+    fn more_than_just_the_bare_nix_store_path() {
+        StorePath::from_bytes(
+            b"00bgd045z0d4icpbc2yyz4gx48aku4la-net-tools-1.60_p20170221182432/bin/arp",
+        )
+        .expect_err("must fail");
+    }
+
+    #[test]
+    fn no_dash_between_hash_and_name() {
+        StorePath::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48ak44lanet-tools-1.60_p20170221182432")
+            .expect_err("must fail");
+    }
+
+    #[test]
+    fn absolute_path() {
+        let example_nix_path_str =
+            "00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432";
+        let nixpath_expected =
+            StorePathRef::from_bytes(example_nix_path_str.as_bytes()).expect("must parse");
+
+        let nixpath_actual = StorePathRef::from_absolute_path(
+            "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432".as_bytes(),
+        )
+        .expect("must parse");
+
+        assert_eq!(nixpath_expected, nixpath_actual);
+
+        assert_eq!(
+            "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+            nixpath_actual.to_absolute_path(),
+        );
+    }
+
+    #[test]
+    fn absolute_path_missing_prefix() {
+        assert_eq!(
+            Error::MissingStoreDir,
+            StorePathRef::from_absolute_path(b"foobar-123").expect_err("must fail")
+        );
+    }
+
+    #[test]
+    fn serialize_ref() {
+        let nixpath_actual = StorePathRef::from_bytes(
+            b"00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+        )
+        .expect("can parse");
+
+        let serialized = serde_json::to_string(&nixpath_actual).expect("can serialize");
+
+        assert_eq!(
+            "\"/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432\"",
+            &serialized
+        );
+    }
+
+    #[test]
+    fn serialize_owned() {
+        let nixpath_actual = StorePath::from_bytes(
+            b"00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+        )
+        .expect("can parse");
+
+        let serialized = serde_json::to_string(&nixpath_actual).expect("can serialize");
+
+        assert_eq!(
+            "\"/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432\"",
+            &serialized
+        );
+    }
+
+    #[test]
+    fn deserialize_ref() {
+        let store_path_str_json =
+            "\"/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432\"";
+
+        let store_path: StorePathRef<'_> =
+            serde_json::from_str(store_path_str_json).expect("valid json");
+
+        assert_eq!(
+            "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+            store_path.to_absolute_path()
+        );
+    }
+
+    #[test]
+    fn deserialize_ref_container() {
+        let str_json = "{\"store_path\":\"/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432\"}";
+
+        let container: Container<'_> = serde_json::from_str(str_json).expect("must deserialize");
+
+        assert_eq!(
+            "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+            container.store_path.to_absolute_path()
+        );
+    }
+
+    #[test]
+    fn deserialize_owned() {
+        let store_path_str_json =
+            "\"/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432\"";
+
+        let store_path: StorePath = serde_json::from_str(store_path_str_json).expect("valid json");
+
+        assert_eq!(
+            "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+            store_path.to_absolute_path()
+        );
+    }
+
+    #[rstest]
+    #[case::without_prefix(
+        "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
+        StorePath::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432").unwrap(), PathBuf::new())]
+    #[case::without_prefix_but_trailing_slash(
+        "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432/",
+        StorePath::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432").unwrap(), PathBuf::new())]
+    #[case::with_prefix(
+        "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432/bin/arp",
+        StorePath::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432").unwrap(), PathBuf::from("bin/arp"))]
+    #[case::with_prefix_and_trailing_slash(
+        "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432/bin/arp/",
+        StorePath::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432").unwrap(), PathBuf::from("bin/arp/"))]
+    fn from_absolute_path_full(
+        #[case] s: &str,
+        #[case] exp_store_path: StorePath,
+        #[case] exp_path: PathBuf,
+    ) {
+        let (actual_store_path, actual_path) =
+            StorePath::from_absolute_path_full(s).expect("must succeed");
+
+        assert_eq!(exp_store_path, actual_store_path);
+        assert_eq!(exp_path, actual_path);
+    }
+
+    #[test]
+    fn from_absolute_path_errors() {
+        assert_eq!(
+            Error::InvalidLength,
+            StorePath::from_absolute_path_full("/nix/store/").expect_err("must fail")
+        );
+        assert_eq!(
+            Error::InvalidLength,
+            StorePath::from_absolute_path_full("/nix/store/foo").expect_err("must fail")
+        );
+        assert_eq!(
+            Error::MissingStoreDir,
+            StorePath::from_absolute_path_full(
+                "00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432"
+            )
+            .expect_err("must fail")
+        );
+    }
+}
diff --git a/tvix/nix-compat/src/store_path/utils.rs b/tvix/nix-compat/src/store_path/utils.rs
new file mode 100644
index 0000000000..d6f390db85
--- /dev/null
+++ b/tvix/nix-compat/src/store_path/utils.rs
@@ -0,0 +1,293 @@
+use crate::nixbase32;
+use crate::nixhash::{CAHash, NixHash};
+use crate::store_path::{Error, StorePathRef, STORE_DIR};
+use data_encoding::HEXLOWER;
+use sha2::{Digest, Sha256};
+use thiserror;
+
+/// Errors that can occur when creating a content-addressed store path.
+///
+/// This wraps the main [crate::store_path::Error]..
+#[derive(Debug, PartialEq, Eq, thiserror::Error)]
+pub enum BuildStorePathError {
+    #[error("Invalid Store Path: {0}")]
+    InvalidStorePath(Error),
+    /// This error occurs when we have references outside the SHA-256 +
+    /// Recursive case. The restriction comes from upstream Nix. It may be
+    /// lifted at some point but there isn't a pressing need to anticipate that.
+    #[error("References were not supported as much as requested")]
+    InvalidReference(),
+}
+
+/// compress_hash takes an arbitrarily long sequence of bytes (usually
+/// a hash digest), and returns a sequence of bytes of length
+/// OUTPUT_SIZE.
+///
+/// It's calculated by rotating through the bytes in the output buffer
+/// (zero- initialized), and XOR'ing with each byte of the passed
+/// input. It consumes 1 byte at a time, and XOR's it with the current
+/// value in the output buffer.
+///
+/// This mimics equivalent functionality in C++ Nix.
+pub fn compress_hash<const OUTPUT_SIZE: usize>(input: &[u8]) -> [u8; OUTPUT_SIZE] {
+    let mut output = [0; OUTPUT_SIZE];
+
+    for (ii, ch) in input.iter().enumerate() {
+        output[ii % OUTPUT_SIZE] ^= ch;
+    }
+
+    output
+}
+
+/// This builds a store path, by calculating the text_hash_string of either a
+/// derivation or a literal text file that may contain references.
+/// If you don't want to have to pass the entire contents, you might want to use
+/// [build_ca_path] instead.
+pub fn build_text_path<S: AsRef<str>, I: IntoIterator<Item = S>, C: AsRef<[u8]>>(
+    name: &str,
+    content: C,
+    references: I,
+) -> Result<StorePathRef<'_>, BuildStorePathError> {
+    // produce the sha256 digest of the contents
+    let content_digest = Sha256::new_with_prefix(content).finalize().into();
+
+    build_ca_path(name, &CAHash::Text(content_digest), references, false)
+}
+
+/// This builds a store path from a [CAHash] and a list of references.
+pub fn build_ca_path<'a, S: AsRef<str>, I: IntoIterator<Item = S>>(
+    name: &'a str,
+    ca_hash: &CAHash,
+    references: I,
+    self_reference: bool,
+) -> Result<StorePathRef<'a>, BuildStorePathError> {
+    // self references are only allowed for CAHash::Nar(NixHash::Sha256(_)).
+    if self_reference && matches!(ca_hash, CAHash::Nar(NixHash::Sha256(_))) {
+        return Err(BuildStorePathError::InvalidReference());
+    }
+
+    /// Helper function, used for the non-sha256 [CAHash::Nar] and all [CAHash::Flat].
+    fn fixed_out_digest(prefix: &str, hash: &NixHash) -> [u8; 32] {
+        Sha256::new_with_prefix(format!("{}:{}:", prefix, hash.to_nix_hex_string()))
+            .finalize()
+            .into()
+    }
+
+    let (ty, inner_digest) = match &ca_hash {
+        CAHash::Text(ref digest) => (make_references_string("text", references, false), *digest),
+        CAHash::Nar(NixHash::Sha256(ref digest)) => (
+            make_references_string("source", references, self_reference),
+            *digest,
+        ),
+
+        // for all other CAHash::Nar, another custom scheme is used.
+        CAHash::Nar(ref hash) => {
+            if references.into_iter().next().is_some() {
+                return Err(BuildStorePathError::InvalidReference());
+            }
+
+            (
+                "output:out".to_string(),
+                fixed_out_digest("fixed:out:r", hash),
+            )
+        }
+        // CaHash::Flat is using something very similar, except the `r:` prefix.
+        CAHash::Flat(ref hash) => {
+            if references.into_iter().next().is_some() {
+                return Err(BuildStorePathError::InvalidReference());
+            }
+
+            (
+                "output:out".to_string(),
+                fixed_out_digest("fixed:out", hash),
+            )
+        }
+    };
+
+    build_store_path_from_fingerprint_parts(&ty, &inner_digest, name)
+        .map_err(BuildStorePathError::InvalidStorePath)
+}
+
+/// For given NAR sha256 digest and name, return the new [StorePathRef] this
+/// would have, or an error, in case the name is invalid.
+pub fn build_nar_based_store_path<'a>(
+    nar_sha256_digest: &[u8; 32],
+    name: &'a str,
+) -> Result<StorePathRef<'a>, BuildStorePathError> {
+    let nar_hash_with_mode = CAHash::Nar(NixHash::Sha256(nar_sha256_digest.to_owned()));
+
+    build_ca_path(name, &nar_hash_with_mode, Vec::<String>::new(), false)
+}
+
+/// This builds an input-addressed store path.
+///
+/// Input-addresed store paths are always derivation outputs, the "input" in question is the
+/// derivation and its closure.
+pub fn build_output_path<'a>(
+    drv_sha256: &[u8; 32],
+    output_name: &str,
+    output_path_name: &'a str,
+) -> Result<StorePathRef<'a>, Error> {
+    build_store_path_from_fingerprint_parts(
+        &(String::from("output:") + output_name),
+        drv_sha256,
+        output_path_name,
+    )
+}
+
+/// This builds a store path from fingerprint parts.
+/// Usually, that function is used from [build_text_path] and
+/// passed a "text hash string" (starting with "text:" as fingerprint),
+/// but other fingerprints starting with "output:" are also used in Derivation
+/// output path calculation.
+///
+/// The fingerprint is hashed with sha256, and its digest is compressed to 20
+/// bytes.
+/// Inside a StorePath, that digest is printed nixbase32-encoded
+/// (32 characters).
+fn build_store_path_from_fingerprint_parts<'a>(
+    ty: &str,
+    inner_digest: &[u8; 32],
+    name: &'a str,
+) -> Result<StorePathRef<'a>, Error> {
+    let fingerprint = format!(
+        "{ty}:sha256:{}:{STORE_DIR}:{name}",
+        HEXLOWER.encode(inner_digest)
+    );
+    // name validation happens in here.
+    StorePathRef::from_name_and_digest_fixed(
+        name,
+        compress_hash(&Sha256::new_with_prefix(fingerprint).finalize()),
+    )
+}
+
+/// This contains the Nix logic to create "text hash strings", which are used
+/// in `builtins.toFile`, as well as in Derivation Path calculation.
+///
+/// A text hash is calculated by concatenating the following fields, separated by a `:`:
+///
+///  - text
+///  - references, individually joined by `:`
+///  - the nix_hash_string representation of the sha256 digest of some contents
+///  - the value of `storeDir`
+///  - the name
+fn make_references_string<S: AsRef<str>, I: IntoIterator<Item = S>>(
+    ty: &str,
+    references: I,
+    self_ref: bool,
+) -> String {
+    let mut s = String::from(ty);
+
+    for reference in references {
+        s.push(':');
+        s.push_str(reference.as_ref());
+    }
+
+    if self_ref {
+        s.push_str(":self");
+    }
+
+    s
+}
+
+/// Nix placeholders (i.e. values returned by `builtins.placeholder`)
+/// are used to populate outputs with paths that must be
+/// string-replaced with the actual placeholders later, at runtime.
+///
+/// The actual placeholder is basically just a SHA256 hash encoded in
+/// cppnix format.
+pub fn hash_placeholder(name: &str) -> String {
+    let digest = Sha256::new_with_prefix(format!("nix-output:{}", name)).finalize();
+
+    format!("/{}", nixbase32::encode(&digest))
+}
+
+#[cfg(test)]
+mod test {
+    use hex_literal::hex;
+
+    use super::*;
+    use crate::nixhash::{CAHash, NixHash};
+
+    #[test]
+    fn build_text_path_with_zero_references() {
+        // This hash should match `builtins.toFile`, e.g.:
+        //
+        // nix-repl> builtins.toFile "foo" "bar"
+        // "/nix/store/vxjiwkjkn7x4079qvh1jkl5pn05j2aw0-foo"
+
+        let store_path = build_text_path("foo", "bar", Vec::<String>::new())
+            .expect("build_store_path() should succeed");
+
+        assert_eq!(
+            store_path.to_absolute_path().as_str(),
+            "/nix/store/vxjiwkjkn7x4079qvh1jkl5pn05j2aw0-foo"
+        );
+    }
+
+    #[test]
+    fn build_text_path_with_non_zero_references() {
+        // This hash should match:
+        //
+        // nix-repl> builtins.toFile "baz" "${builtins.toFile "foo" "bar"}"
+        // "/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz"
+
+        let inner = build_text_path("foo", "bar", Vec::<String>::new())
+            .expect("path_with_references() should succeed");
+        let inner_path = inner.to_absolute_path();
+
+        let outer = build_text_path("baz", &inner_path, vec![inner_path.as_str()])
+            .expect("path_with_references() should succeed");
+
+        assert_eq!(
+            outer.to_absolute_path().as_str(),
+            "/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz"
+        );
+    }
+
+    #[test]
+    fn build_sha1_path() {
+        let outer = build_ca_path(
+            "bar",
+            &CAHash::Nar(NixHash::Sha1(hex!(
+                "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
+            ))),
+            Vec::<String>::new(),
+            false,
+        )
+        .expect("path_with_references() should succeed");
+
+        assert_eq!(
+            outer.to_absolute_path().as_str(),
+            "/nix/store/mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar"
+        );
+    }
+
+    #[test]
+    fn build_store_path_with_non_zero_references() {
+        // This hash should match:
+        //
+        // nix-repl> builtins.toFile "baz" "${builtins.toFile "foo" "bar"}"
+        // "/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz"
+        //
+        // $ nix store make-content-addressed /nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz
+        // rewrote '/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz' to '/nix/store/s89y431zzhmdn3k8r96rvakryddkpv2v-baz'
+        let outer = build_ca_path(
+            "baz",
+            &CAHash::Nar(NixHash::Sha256(
+                nixbase32::decode(b"1xqkzcb3909fp07qngljr4wcdnrh1gdam1m2n29i6hhrxlmkgkv1")
+                    .expect("nixbase32 should decode")
+                    .try_into()
+                    .expect("should have right len"),
+            )),
+            vec!["/nix/store/dxwkwjzdaq7ka55pkk252gh32bgpmql4-foo"],
+            false,
+        )
+        .expect("path_with_references() should succeed");
+
+        assert_eq!(
+            outer.to_absolute_path().as_str(),
+            "/nix/store/s89y431zzhmdn3k8r96rvakryddkpv2v-baz"
+        );
+    }
+}
diff --git a/tvix/nix-compat/src/wire/bytes/mod.rs b/tvix/nix-compat/src/wire/bytes/mod.rs
new file mode 100644
index 0000000000..740a7ebfd0
--- /dev/null
+++ b/tvix/nix-compat/src/wire/bytes/mod.rs
@@ -0,0 +1,229 @@
+use std::{
+    io::{Error, ErrorKind},
+    ops::RangeInclusive,
+};
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+
+mod reader;
+pub use reader::BytesReader;
+mod writer;
+pub use writer::BytesWriter;
+
+/// 8 null bytes, used to write out padding.
+const EMPTY_BYTES: &[u8; 8] = &[0u8; 8];
+
+/// The length of the size field, in bytes is always 8.
+const LEN_SIZE: usize = 8;
+
+#[allow(dead_code)]
+/// Read a "bytes wire packet" from the AsyncRead.
+/// Rejects reading more than `allowed_size` bytes of payload.
+///
+/// The packet is made up of three parts:
+/// - a length header, u64, LE-encoded
+/// - the payload itself
+/// - null bytes to the next 8 byte boundary
+///
+/// Ensures the payload size fits into the `allowed_size` passed,
+/// and that the padding is actual null bytes.
+///
+/// On success, the returned `Vec<u8>` only contains the payload itself.
+/// On failure (for example if a too large byte packet was sent), the reader
+/// becomes unusable.
+///
+/// This buffers the entire payload into memory,
+/// a streaming version is available at [crate::wire::bytes::BytesReader].
+pub async fn read_bytes<R>(
+    r: &mut R,
+    allowed_size: RangeInclusive<usize>,
+) -> std::io::Result<Vec<u8>>
+where
+    R: AsyncReadExt + Unpin,
+{
+    // read the length field
+    let len = r.read_u64_le().await?;
+    let len: usize = len
+        .try_into()
+        .ok()
+        .filter(|len| allowed_size.contains(len))
+        .ok_or_else(|| {
+            std::io::Error::new(
+                std::io::ErrorKind::InvalidData,
+                "signalled package size not in allowed range",
+            )
+        })?;
+
+    // calculate the total length, including padding.
+    // byte packets are padded to 8 byte blocks each.
+    let padded_len = padding_len(len as u64) as u64 + (len as u64);
+    let mut limited_reader = r.take(padded_len);
+
+    let mut buf = Vec::new();
+
+    let s = limited_reader.read_to_end(&mut buf).await?;
+
+    // make sure we got exactly the number of bytes, and not less.
+    if s as u64 != padded_len {
+        return Err(std::io::ErrorKind::UnexpectedEof.into());
+    }
+
+    let (_content, padding) = buf.split_at(len);
+
+    // ensure the padding is all zeroes.
+    if !padding.iter().all(|e| *e == b'\0') {
+        return Err(std::io::Error::new(
+            std::io::ErrorKind::InvalidData,
+            "padding is not all zeroes",
+        ));
+    }
+
+    // return the data without the padding
+    buf.truncate(len);
+    Ok(buf)
+}
+
+/// Read a "bytes wire packet" of from the AsyncRead and tries to parse as string.
+/// Internally uses [read_bytes].
+/// Rejects reading more than `allowed_size` bytes of payload.
+pub async fn read_string<R>(
+    r: &mut R,
+    allowed_size: RangeInclusive<usize>,
+) -> std::io::Result<String>
+where
+    R: AsyncReadExt + Unpin,
+{
+    let bytes = read_bytes(r, allowed_size).await?;
+    String::from_utf8(bytes).map_err(|e| Error::new(ErrorKind::InvalidData, e))
+}
+
+/// Writes a "bytes wire packet" to a (hopefully buffered) [AsyncWriteExt].
+///
+/// Accepts anything implementing AsRef<[u8]> as payload.
+///
+/// See [read_bytes] for a description of the format.
+///
+/// Note: if performance matters to you, make sure your
+/// [AsyncWriteExt] handle is buffered. This function is quite
+/// write-intesive.
+pub async fn write_bytes<W: AsyncWriteExt + Unpin, B: AsRef<[u8]>>(
+    w: &mut W,
+    b: B,
+) -> std::io::Result<()> {
+    // write the size packet.
+    w.write_u64_le(b.as_ref().len() as u64).await?;
+
+    // write the payload
+    w.write_all(b.as_ref()).await?;
+
+    // write padding if needed
+    let padding_len = padding_len(b.as_ref().len() as u64) as usize;
+    if padding_len != 0 {
+        w.write_all(&EMPTY_BYTES[..padding_len]).await?;
+    }
+    Ok(())
+}
+
+/// Computes the number of bytes we should add to len (a length in
+/// bytes) to be aligned on 64 bits (8 bytes).
+fn padding_len(len: u64) -> u8 {
+    let aligned = len.wrapping_add(7) & !7;
+    aligned.wrapping_sub(len) as u8
+}
+
+#[cfg(test)]
+mod tests {
+    use tokio_test::{assert_ok, io::Builder};
+
+    use super::*;
+    use hex_literal::hex;
+
+    /// The maximum length of bytes packets we're willing to accept in the test
+    /// cases.
+    const MAX_LEN: usize = 1024;
+
+    #[tokio::test]
+    async fn test_read_8_bytes() {
+        let mut mock = Builder::new()
+            .read(&8u64.to_le_bytes())
+            .read(&12345678u64.to_le_bytes())
+            .build();
+
+        assert_eq!(
+            &12345678u64.to_le_bytes(),
+            read_bytes(&mut mock, 0..=MAX_LEN).await.unwrap().as_slice()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_read_9_bytes() {
+        let mut mock = Builder::new()
+            .read(&9u64.to_le_bytes())
+            .read(&hex!("01020304050607080900000000000000"))
+            .build();
+
+        assert_eq!(
+            hex!("010203040506070809"),
+            read_bytes(&mut mock, 0..=MAX_LEN).await.unwrap().as_slice()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_read_0_bytes() {
+        // A empty byte packet is essentially just the 0 length field.
+        // No data is read, and there's zero padding.
+        let mut mock = Builder::new().read(&0u64.to_le_bytes()).build();
+
+        assert_eq!(
+            hex!(""),
+            read_bytes(&mut mock, 0..=MAX_LEN).await.unwrap().as_slice()
+        );
+    }
+
+    #[tokio::test]
+    /// Ensure we don't read any further than the size field if the length
+    /// doesn't match the range we want to accept.
+    async fn test_read_reject_too_large() {
+        let mut mock = Builder::new().read(&100u64.to_le_bytes()).build();
+
+        read_bytes(&mut mock, 10..=10)
+            .await
+            .expect_err("expect this to fail");
+    }
+
+    #[tokio::test]
+    async fn test_write_bytes_no_padding() {
+        let input = hex!("6478696f34657661");
+        let len = input.len() as u64;
+        let mut mock = Builder::new()
+            .write(&len.to_le_bytes())
+            .write(&input)
+            .build();
+        assert_ok!(write_bytes(&mut mock, &input).await)
+    }
+    #[tokio::test]
+    async fn test_write_bytes_with_padding() {
+        let input = hex!("322e332e3137");
+        let len = input.len() as u64;
+        let mut mock = Builder::new()
+            .write(&len.to_le_bytes())
+            .write(&hex!("322e332e31370000"))
+            .build();
+        assert_ok!(write_bytes(&mut mock, &input).await)
+    }
+
+    #[tokio::test]
+    async fn test_write_string() {
+        let input = "Hello, World!";
+        let len = input.len() as u64;
+        let mut mock = Builder::new()
+            .write(&len.to_le_bytes())
+            .write(&hex!("48656c6c6f2c20576f726c6421000000"))
+            .build();
+        assert_ok!(write_bytes(&mut mock, &input).await)
+    }
+
+    #[test]
+    fn padding_len_u64_max() {
+        assert_eq!(padding_len(u64::MAX), 1);
+    }
+}
diff --git a/tvix/nix-compat/src/wire/bytes/reader/mod.rs b/tvix/nix-compat/src/wire/bytes/reader/mod.rs
new file mode 100644
index 0000000000..50398d9b9e
--- /dev/null
+++ b/tvix/nix-compat/src/wire/bytes/reader/mod.rs
@@ -0,0 +1,427 @@
+use std::{
+    future::Future,
+    io,
+    ops::RangeBounds,
+    pin::Pin,
+    task::{self, ready, Poll},
+};
+use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
+
+use trailer::{read_trailer, ReadTrailer, Trailer};
+mod trailer;
+
+/// Reads a "bytes wire packet" from the underlying reader.
+/// The format is the same as in [crate::wire::bytes::read_bytes],
+/// however this structure provides a [AsyncRead] interface,
+/// allowing to not having to pass around the entire payload in memory.
+///
+/// It is constructed by reading a size with [BytesReader::new],
+/// and yields payload data until the end of the packet is reached.
+///
+/// It will not return the final bytes before all padding has been successfully
+/// consumed as well, but the full length of the reader must be consumed.
+///
+/// If the data is not read all the way to the end, or an error is encountered,
+/// the underlying reader is no longer usable and might return garbage.
+#[derive(Debug)]
+pub struct BytesReader<R> {
+    state: State<R>,
+}
+
+#[derive(Debug)]
+enum State<R> {
+    /// Full 8-byte blocks are being read and released to the caller.
+    Body {
+        reader: Option<R>,
+        consumed: u64,
+        /// The total length of all user data contained in both the body and trailer.
+        user_len: u64,
+    },
+    /// The trailer is in the process of being read.
+    ReadTrailer(ReadTrailer<R>),
+    /// The trailer has been fully read and validated,
+    /// and data can now be released to the caller.
+    ReleaseTrailer { consumed: u8, data: Trailer },
+}
+
+impl<R> BytesReader<R>
+where
+    R: AsyncRead + Unpin,
+{
+    /// Constructs a new BytesReader, using the underlying passed reader.
+    pub async fn new<S: RangeBounds<u64>>(mut reader: R, allowed_size: S) -> io::Result<Self> {
+        let size = reader.read_u64_le().await?;
+
+        if !allowed_size.contains(&size) {
+            return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid size"));
+        }
+
+        Ok(Self {
+            state: State::Body {
+                reader: Some(reader),
+                consumed: 0,
+                user_len: size,
+            },
+        })
+    }
+
+    /// Returns whether there is any remaining data to be read.
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    /// Remaining data length, ie not including data already read.
+    ///
+    /// If the size has not been read yet, this is [None].
+    pub fn len(&self) -> u64 {
+        match self.state {
+            State::Body {
+                consumed, user_len, ..
+            } => user_len - consumed,
+            State::ReadTrailer(ref fut) => fut.len() as u64,
+            State::ReleaseTrailer { consumed, ref data } => data.len() as u64 - consumed as u64,
+        }
+    }
+}
+
+impl<R: AsyncRead + Unpin> AsyncRead for BytesReader<R> {
+    fn poll_read(
+        mut self: Pin<&mut Self>,
+        cx: &mut task::Context,
+        buf: &mut ReadBuf,
+    ) -> Poll<io::Result<()>> {
+        let this = &mut self.state;
+
+        loop {
+            match this {
+                State::Body {
+                    reader,
+                    consumed,
+                    user_len,
+                } => {
+                    let body_len = *user_len & !7;
+                    let remaining = body_len - *consumed;
+
+                    let reader = if remaining == 0 {
+                        let reader = reader.take().unwrap();
+                        let user_len = (*user_len & 7) as u8;
+                        *this = State::ReadTrailer(read_trailer(reader, user_len));
+                        continue;
+                    } else {
+                        reader.as_mut().unwrap()
+                    };
+
+                    let mut bytes_read = 0;
+                    ready!(with_limited(buf, remaining, |buf| {
+                        let ret = Pin::new(reader).poll_read(cx, buf);
+                        bytes_read = buf.initialized().len();
+                        ret
+                    }))?;
+
+                    *consumed += bytes_read as u64;
+
+                    return if bytes_read != 0 {
+                        Ok(())
+                    } else {
+                        Err(io::ErrorKind::UnexpectedEof.into())
+                    }
+                    .into();
+                }
+                State::ReadTrailer(fut) => {
+                    *this = State::ReleaseTrailer {
+                        consumed: 0,
+                        data: ready!(Pin::new(fut).poll(cx))?,
+                    };
+                }
+                State::ReleaseTrailer { consumed, data } => {
+                    let data = &data[*consumed as usize..];
+                    let data = &data[..usize::min(data.len(), buf.remaining())];
+
+                    buf.put_slice(data);
+                    *consumed += data.len() as u8;
+
+                    return Ok(()).into();
+                }
+            }
+        }
+    }
+}
+
+/// Make a limited version of `buf`, consisting only of up to `n` bytes of the unfilled section, and call `f` with it.
+/// After `f` returns, we propagate the filled cursor advancement back to `buf`.
+fn with_limited<R>(buf: &mut ReadBuf, n: u64, f: impl FnOnce(&mut ReadBuf) -> R) -> R {
+    let mut nbuf = buf.take(n.try_into().unwrap_or(usize::MAX));
+    let ptr = nbuf.initialized().as_ptr();
+    let ret = f(&mut nbuf);
+
+    // SAFETY: `ReadBuf::take` only returns the *unfilled* section of `buf`,
+    // so anything filled is new, initialized data.
+    //
+    // We verify that `nbuf` still points to the same buffer,
+    // so we're sure it hasn't been swapped out.
+    unsafe {
+        // ensure our buffer hasn't been swapped out
+        assert_eq!(nbuf.initialized().as_ptr(), ptr);
+
+        let n = nbuf.filled().len();
+        buf.assume_init(n);
+        buf.advance(n);
+    }
+
+    ret
+}
+
+#[cfg(test)]
+mod tests {
+    use std::time::Duration;
+
+    use crate::wire::bytes::{padding_len, write_bytes};
+    use hex_literal::hex;
+    use lazy_static::lazy_static;
+    use rstest::rstest;
+    use tokio::io::AsyncReadExt;
+    use tokio_test::io::Builder;
+
+    use super::*;
+
+    /// The maximum length of bytes packets we're willing to accept in the test
+    /// cases.
+    const MAX_LEN: u64 = 1024;
+
+    lazy_static! {
+        pub static ref LARGE_PAYLOAD: Vec<u8> = (0..255).collect::<Vec<u8>>().repeat(4 * 1024);
+    }
+
+    /// Helper function, calling the (simpler) write_bytes with the payload.
+    /// We use this to create data we want to read from the wire.
+    async fn produce_packet_bytes(payload: &[u8]) -> Vec<u8> {
+        let mut exp = vec![];
+        write_bytes(&mut exp, payload).await.unwrap();
+        exp
+    }
+
+    /// Read bytes packets of various length, and ensure read_to_end returns the
+    /// expected payload.
+    #[rstest]
+    #[case::empty(&[])] // empty bytes packet
+    #[case::size_1b(&[0xff])] // 1 bytes payload
+    #[case::size_8b(&hex!("0001020304050607"))] // 8 bytes payload (no padding)
+    #[case::size_9b(&hex!("000102030405060708"))] // 9 bytes payload (7 bytes padding)
+    #[case::size_1m(LARGE_PAYLOAD.as_slice())] // larger bytes packet
+    #[tokio::test]
+    async fn read_payload_correct(#[case] payload: &[u8]) {
+        let mut mock = Builder::new()
+            .read(&produce_packet_bytes(payload).await)
+            .build();
+
+        let mut r = BytesReader::new(&mut mock, ..=LARGE_PAYLOAD.len() as u64)
+            .await
+            .unwrap();
+        let mut buf = Vec::new();
+        r.read_to_end(&mut buf).await.expect("must succeed");
+
+        assert_eq!(payload, &buf[..]);
+    }
+
+    /// Fail if the bytes packet is larger than allowed
+    #[tokio::test]
+    async fn read_bigger_than_allowed_fail() {
+        let payload = LARGE_PAYLOAD.as_slice();
+        let mut mock = Builder::new()
+            .read(&produce_packet_bytes(payload).await[0..8]) // We stop reading after the size packet
+            .build();
+
+        assert_eq!(
+            BytesReader::new(&mut mock, ..2048)
+                .await
+                .unwrap_err()
+                .kind(),
+            io::ErrorKind::InvalidData
+        );
+    }
+
+    /// Fail if the bytes packet is smaller than allowed
+    #[tokio::test]
+    async fn read_smaller_than_allowed_fail() {
+        let payload = &[0x00, 0x01, 0x02];
+        let mut mock = Builder::new()
+            .read(&produce_packet_bytes(payload).await[0..8]) // We stop reading after the size packet
+            .build();
+
+        assert_eq!(
+            BytesReader::new(&mut mock, 1024..2048)
+                .await
+                .unwrap_err()
+                .kind(),
+            io::ErrorKind::InvalidData
+        );
+    }
+
+    /// Fail if the padding is not all zeroes
+    #[tokio::test]
+    async fn read_fail_if_nonzero_padding() {
+        let payload = &[0x00, 0x01, 0x02];
+        let mut packet_bytes = produce_packet_bytes(payload).await;
+        // Flip some bits in the padding
+        packet_bytes[12] = 0xff;
+        let mut mock = Builder::new().read(&packet_bytes).build(); // We stop reading after the faulty bit
+
+        let mut r = BytesReader::new(&mut mock, ..MAX_LEN).await.unwrap();
+        let mut buf = Vec::new();
+
+        r.read_to_end(&mut buf).await.expect_err("must fail");
+    }
+
+    /// Start a 9 bytes payload packet, but have the underlying reader return
+    /// EOF in the middle of the size packet (after 4 bytes).
+    /// We should get an unexpected EOF error, already when trying to read the
+    /// first byte (of payload)
+    #[tokio::test]
+    async fn read_9b_eof_during_size() {
+        let payload = &hex!("FF0102030405060708");
+        let mut mock = Builder::new()
+            .read(&produce_packet_bytes(payload).await[..4])
+            .build();
+
+        assert_eq!(
+            BytesReader::new(&mut mock, ..MAX_LEN)
+                .await
+                .expect_err("must fail")
+                .kind(),
+            io::ErrorKind::UnexpectedEof
+        );
+    }
+
+    /// Start a 9 bytes payload packet, but have the underlying reader return
+    /// EOF in the middle of the payload (4 bytes into the payload).
+    /// We should get an unexpected EOF error, after reading the first 4 bytes
+    /// (successfully).
+    #[tokio::test]
+    async fn read_9b_eof_during_payload() {
+        let payload = &hex!("FF0102030405060708");
+        let mut mock = Builder::new()
+            .read(&produce_packet_bytes(payload).await[..8 + 4])
+            .build();
+
+        let mut r = BytesReader::new(&mut mock, ..MAX_LEN).await.unwrap();
+        let mut buf = [0; 9];
+
+        r.read_exact(&mut buf[..4]).await.expect("must succeed");
+
+        assert_eq!(
+            r.read_exact(&mut buf[4..=4])
+                .await
+                .expect_err("must fail")
+                .kind(),
+            std::io::ErrorKind::UnexpectedEof
+        );
+    }
+
+    /// Start a 9 bytes payload packet, but don't supply the necessary padding.
+    /// This is expected to always fail before returning the final data.
+    #[rstest]
+    #[case::before_padding(8 + 9)]
+    #[case::during_padding(8 + 9 + 2)]
+    #[case::after_padding(8 + 9 + padding_len(9) as usize - 1)]
+    #[tokio::test]
+    async fn read_9b_eof_after_payload(#[case] offset: usize) {
+        let payload = &hex!("FF0102030405060708");
+        let mut mock = Builder::new()
+            .read(&produce_packet_bytes(payload).await[..offset])
+            .build();
+
+        let mut r = BytesReader::new(&mut mock, ..MAX_LEN).await.unwrap();
+
+        // read_exact of the payload *body* will succeed, but a subsequent read will
+        // return UnexpectedEof error.
+        assert_eq!(r.read_exact(&mut [0; 8]).await.unwrap(), 8);
+        assert_eq!(
+            r.read_exact(&mut [0]).await.unwrap_err().kind(),
+            std::io::ErrorKind::UnexpectedEof
+        );
+    }
+
+    /// Start a 9 bytes payload packet, but return an error after a certain position.
+    /// Ensure that error is propagated.
+    #[rstest]
+    #[case::during_size(4)]
+    #[case::before_payload(8)]
+    #[case::during_payload(8 + 4)]
+    #[case::before_padding(8 + 4)]
+    #[case::during_padding(8 + 9 + 2)]
+    #[tokio::test]
+    async fn propagate_error_from_reader(#[case] offset: usize) {
+        let payload = &hex!("FF0102030405060708");
+        let mut mock = Builder::new()
+            .read(&produce_packet_bytes(payload).await[..offset])
+            .read_error(std::io::Error::new(std::io::ErrorKind::Other, "foo"))
+            .build();
+
+        // Either length reading or data reading can fail, depending on which test case we're in.
+        let err: io::Error = async {
+            let mut r = BytesReader::new(&mut mock, ..MAX_LEN).await?;
+            let mut buf = Vec::new();
+
+            r.read_to_end(&mut buf).await?;
+
+            Ok(())
+        }
+        .await
+        .expect_err("must fail");
+
+        assert_eq!(
+            err.kind(),
+            std::io::ErrorKind::Other,
+            "error kind must match"
+        );
+
+        assert_eq!(
+            err.into_inner().unwrap().to_string(),
+            "foo",
+            "error payload must contain foo"
+        );
+    }
+
+    /// If there's an error right after the padding, we don't propagate it, as
+    /// we're done reading. We just return EOF.
+    #[tokio::test]
+    async fn no_error_after_eof() {
+        let payload = &hex!("FF0102030405060708");
+        let mut mock = Builder::new()
+            .read(&produce_packet_bytes(payload).await)
+            .read_error(std::io::Error::new(std::io::ErrorKind::Other, "foo"))
+            .build();
+
+        let mut r = BytesReader::new(&mut mock, ..MAX_LEN).await.unwrap();
+        let mut buf = Vec::new();
+
+        r.read_to_end(&mut buf).await.expect("must succeed");
+        assert_eq!(buf.as_slice(), payload);
+    }
+
+    /// Introduce various stalls in various places of the packet, to ensure we
+    /// handle these cases properly, too.
+    #[rstest]
+    #[case::beginning(0)]
+    #[case::before_payload(8)]
+    #[case::during_payload(8 + 4)]
+    #[case::before_padding(8 + 4)]
+    #[case::during_padding(8 + 9 + 2)]
+    #[tokio::test]
+    async fn read_payload_correct_pending(#[case] offset: usize) {
+        let payload = &hex!("FF0102030405060708");
+        let mut mock = Builder::new()
+            .read(&produce_packet_bytes(payload).await[..offset])
+            .wait(Duration::from_nanos(0))
+            .read(&produce_packet_bytes(payload).await[offset..])
+            .build();
+
+        let mut r = BytesReader::new(&mut mock, ..=LARGE_PAYLOAD.len() as u64)
+            .await
+            .unwrap();
+        let mut buf = Vec::new();
+        r.read_to_end(&mut buf).await.expect("must succeed");
+
+        assert_eq!(payload, &buf[..]);
+    }
+}
diff --git a/tvix/nix-compat/src/wire/bytes/reader/trailer.rs b/tvix/nix-compat/src/wire/bytes/reader/trailer.rs
new file mode 100644
index 0000000000..858026bf71
--- /dev/null
+++ b/tvix/nix-compat/src/wire/bytes/reader/trailer.rs
@@ -0,0 +1,197 @@
+use std::{
+    future::Future,
+    marker::PhantomData,
+    ops::Deref,
+    pin::Pin,
+    task::{self, ready, Poll},
+};
+
+use tokio::io::{self, AsyncRead, ReadBuf};
+
+/// Trailer represents up to 7 bytes of data read as part of the trailer block(s)
+#[derive(Debug)]
+pub(crate) struct Trailer {
+    data_len: u8,
+    buf: [u8; 7],
+}
+
+impl Deref for Trailer {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        &self.buf[..self.data_len as usize]
+    }
+}
+
+/// Tag defines a "trailer tag": specific, fixed bytes that must follow wire data.
+pub(crate) trait Tag {
+    /// The expected suffix
+    ///
+    /// The first 7 bytes may be ignored, and it must be an 8-byte aligned size.
+    const PATTERN: &'static [u8];
+
+    /// Suitably sized buffer for reading [Self::PATTERN]
+    ///
+    /// HACK: This is a workaround for const generics limitations.
+    type Buf: AsRef<[u8]> + AsMut<[u8]> + Unpin;
+
+    /// Make an instance of [Self::Buf]
+    fn make_buf() -> Self::Buf;
+}
+
+#[derive(Debug)]
+pub(crate) enum Pad {}
+
+impl Tag for Pad {
+    const PATTERN: &'static [u8] = &[0; 8];
+
+    type Buf = [u8; 8];
+
+    fn make_buf() -> Self::Buf {
+        [0; 8]
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct ReadTrailer<R, T: Tag = Pad> {
+    reader: R,
+    data_len: u8,
+    filled: u8,
+    buf: T::Buf,
+    _phantom: PhantomData<fn(T) -> T>,
+}
+
+/// read_trailer returns a [Future] that reads a trailer with a given [Tag] from `reader`
+pub(crate) fn read_trailer<R: AsyncRead + Unpin, T: Tag>(
+    reader: R,
+    data_len: u8,
+) -> ReadTrailer<R, T> {
+    assert!(data_len < 8, "payload in trailer must be less than 8 bytes");
+
+    let buf = T::make_buf();
+    assert_eq!(buf.as_ref().len(), T::PATTERN.len());
+    assert_eq!(T::PATTERN.len() % 8, 0);
+
+    ReadTrailer {
+        reader,
+        data_len,
+        filled: if data_len != 0 { 0 } else { 8 },
+        buf,
+        _phantom: PhantomData,
+    }
+}
+
+impl<R, T: Tag> ReadTrailer<R, T> {
+    pub fn len(&self) -> u8 {
+        self.data_len
+    }
+}
+
+impl<R: AsyncRead + Unpin, T: Tag> Future for ReadTrailer<R, T> {
+    type Output = io::Result<Trailer>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll<Self::Output> {
+        let this = &mut *self;
+
+        loop {
+            if this.filled >= this.data_len {
+                let check_range = || this.data_len as usize..this.filled as usize;
+
+                if this.buf.as_ref()[check_range()] != T::PATTERN[check_range()] {
+                    return Err(io::Error::new(
+                        io::ErrorKind::InvalidData,
+                        "invalid trailer",
+                    ))
+                    .into();
+                }
+            }
+
+            if this.filled as usize == T::PATTERN.len() {
+                let mut buf = [0; 7];
+                buf.copy_from_slice(&this.buf.as_ref()[..7]);
+
+                return Ok(Trailer {
+                    data_len: this.data_len,
+                    buf,
+                })
+                .into();
+            }
+
+            let mut buf = ReadBuf::new(this.buf.as_mut());
+            buf.advance(this.filled as usize);
+
+            ready!(Pin::new(&mut this.reader).poll_read(cx, &mut buf))?;
+
+            this.filled = {
+                let prev_filled = this.filled;
+                let filled = buf.filled().len() as u8;
+
+                if filled == prev_filled {
+                    return Err(io::ErrorKind::UnexpectedEof.into()).into();
+                }
+
+                filled
+            };
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::time::Duration;
+
+    use super::*;
+
+    #[tokio::test]
+    async fn unexpected_eof() {
+        let reader = tokio_test::io::Builder::new()
+            .read(&[0xed])
+            .wait(Duration::ZERO)
+            .read(&[0xef, 0x00])
+            .build();
+
+        assert_eq!(
+            read_trailer::<_, Pad>(reader, 2).await.unwrap_err().kind(),
+            io::ErrorKind::UnexpectedEof
+        );
+    }
+
+    #[tokio::test]
+    async fn invalid_padding() {
+        let reader = tokio_test::io::Builder::new()
+            .read(&[0xed])
+            .wait(Duration::ZERO)
+            .read(&[0xef, 0x01, 0x00])
+            .wait(Duration::ZERO)
+            .build();
+
+        assert_eq!(
+            read_trailer::<_, Pad>(reader, 2).await.unwrap_err().kind(),
+            io::ErrorKind::InvalidData
+        );
+    }
+
+    #[tokio::test]
+    async fn success() {
+        let reader = tokio_test::io::Builder::new()
+            .read(&[0xed])
+            .wait(Duration::ZERO)
+            .read(&[0xef, 0x00])
+            .wait(Duration::ZERO)
+            .read(&[0x00, 0x00, 0x00, 0x00, 0x00])
+            .build();
+
+        assert_eq!(
+            &*read_trailer::<_, Pad>(reader, 2).await.unwrap(),
+            &[0xed, 0xef]
+        );
+    }
+
+    #[tokio::test]
+    async fn no_padding() {
+        assert!(read_trailer::<_, Pad>(io::empty(), 0)
+            .await
+            .unwrap()
+            .is_empty());
+    }
+}
diff --git a/tvix/nix-compat/src/wire/bytes/writer.rs b/tvix/nix-compat/src/wire/bytes/writer.rs
new file mode 100644
index 0000000000..f5632771e9
--- /dev/null
+++ b/tvix/nix-compat/src/wire/bytes/writer.rs
@@ -0,0 +1,538 @@
+use pin_project_lite::pin_project;
+use std::task::{ready, Poll};
+
+use tokio::io::AsyncWrite;
+
+use super::{padding_len, EMPTY_BYTES, LEN_SIZE};
+
+pin_project! {
+    /// Writes a "bytes wire packet" to the underlying writer.
+    /// The format is the same as in [crate::wire::bytes::write_bytes],
+    /// however this structure provides a [AsyncWrite] interface,
+    /// allowing to not having to pass around the entire payload in memory.
+    ///
+    /// It internally takes care of writing (non-payload) framing (size and
+    /// padding).
+    ///
+    /// During construction, the expected payload size needs to be provided.
+    ///
+    /// After writing the payload to it, the user MUST call flush (or shutdown),
+    /// which will validate the written payload size to match, and write the
+    /// necessary padding.
+    ///
+    /// In case flush is not called at the end, invalid data might be sent
+    /// silently.
+    ///
+    /// The underlying writer returning `Ok(0)` is considered an EOF situation,
+    /// which is stronger than the "typically means the underlying object is no
+    /// longer able to accept bytes" interpretation from the docs. If such a
+    /// situation occurs, an error is returned.
+    ///
+    /// The struct holds three fields, the underlying writer, the (expected)
+    /// payload length, and an enum, tracking the state.
+    pub struct BytesWriter<W>
+    where
+        W: AsyncWrite,
+    {
+        #[pin]
+        inner: W,
+        payload_len: u64,
+        state: BytesPacketPosition,
+    }
+}
+
+/// Models the position inside a "bytes wire packet" that the writer is in.
+/// It can be in three different stages, inside size, payload or padding fields.
+/// The number tracks the number of bytes written inside the specific field.
+/// There shall be no ambiguous states, at the end of a stage we immediately
+/// move to the beginning of the next one:
+/// - Size(LEN_SIZE) must be expressed as Payload(0)
+/// - Payload(self.payload_len) must be expressed as Padding(0)
+///
+/// Padding(padding_len) means we're at the end of the bytes wire packet.
+#[derive(Clone, Debug, PartialEq, Eq)]
+enum BytesPacketPosition {
+    Size(usize),
+    Payload(u64),
+    Padding(usize),
+}
+
+impl<W> BytesWriter<W>
+where
+    W: AsyncWrite,
+{
+    /// Constructs a new BytesWriter, using the underlying passed writer.
+    pub fn new(w: W, payload_len: u64) -> Self {
+        Self {
+            inner: w,
+            payload_len,
+            state: BytesPacketPosition::Size(0),
+        }
+    }
+}
+
+/// Returns an error if the passed usize is 0.
+#[inline]
+fn ensure_nonzero_bytes_written(bytes_written: usize) -> Result<usize, std::io::Error> {
+    if bytes_written == 0 {
+        Err(std::io::Error::new(
+            std::io::ErrorKind::WriteZero,
+            "underlying writer accepted 0 bytes",
+        ))
+    } else {
+        Ok(bytes_written)
+    }
+}
+
+impl<W> AsyncWrite for BytesWriter<W>
+where
+    W: AsyncWrite,
+{
+    fn poll_write(
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+        buf: &[u8],
+    ) -> Poll<Result<usize, std::io::Error>> {
+        // Use a loop, so we can deal with (multiple) state transitions.
+        let mut this = self.project();
+
+        loop {
+            match *this.state {
+                BytesPacketPosition::Size(LEN_SIZE) => unreachable!(),
+                BytesPacketPosition::Size(pos) => {
+                    let size_field = &this.payload_len.to_le_bytes();
+
+                    let bytes_written = ensure_nonzero_bytes_written(ready!(this
+                        .inner
+                        .as_mut()
+                        .poll_write(cx, &size_field[pos..]))?)?;
+
+                    let new_pos = pos + bytes_written;
+                    if new_pos == LEN_SIZE {
+                        *this.state = BytesPacketPosition::Payload(0);
+                    } else {
+                        *this.state = BytesPacketPosition::Size(new_pos);
+                    }
+                }
+                BytesPacketPosition::Payload(pos) => {
+                    // Ensure we still have space for more payload
+                    if pos + (buf.len() as u64) > *this.payload_len {
+                        return Poll::Ready(Err(std::io::Error::new(
+                            std::io::ErrorKind::InvalidData,
+                            "tried to write excess bytes",
+                        )));
+                    }
+                    let bytes_written = ready!(this.inner.as_mut().poll_write(cx, buf))?;
+                    ensure_nonzero_bytes_written(bytes_written)?;
+                    let new_pos = pos + (bytes_written as u64);
+                    if new_pos == *this.payload_len {
+                        *this.state = BytesPacketPosition::Padding(0)
+                    } else {
+                        *this.state = BytesPacketPosition::Payload(new_pos)
+                    }
+
+                    return Poll::Ready(Ok(bytes_written));
+                }
+                // If we're already in padding state, there should be no more payload left to write!
+                BytesPacketPosition::Padding(_pos) => {
+                    return Poll::Ready(Err(std::io::Error::new(
+                        std::io::ErrorKind::InvalidData,
+                        "tried to write excess bytes",
+                    )))
+                }
+            }
+        }
+    }
+
+    fn poll_flush(
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> Poll<Result<(), std::io::Error>> {
+        let mut this = self.project();
+
+        loop {
+            match *this.state {
+                BytesPacketPosition::Size(LEN_SIZE) => unreachable!(),
+                BytesPacketPosition::Size(pos) => {
+                    // More bytes to write in the size field
+                    let size_field = &this.payload_len.to_le_bytes()[..];
+                    let bytes_written = ensure_nonzero_bytes_written(ready!(this
+                        .inner
+                        .as_mut()
+                        .poll_write(cx, &size_field[pos..]))?)?;
+                    let new_pos = pos + bytes_written;
+                    if new_pos == LEN_SIZE {
+                        // Size field written, now ready to receive payload
+                        *this.state = BytesPacketPosition::Payload(0);
+                    } else {
+                        *this.state = BytesPacketPosition::Size(new_pos);
+                    }
+                }
+                BytesPacketPosition::Payload(_pos) => {
+                    // If we're at position 0 and want to write 0 bytes of payload
+                    // in total, we can transition to padding.
+                    // Otherwise, break, as we're expecting more payload to
+                    // be written.
+                    if *this.payload_len == 0 {
+                        *this.state = BytesPacketPosition::Padding(0);
+                    } else {
+                        break;
+                    }
+                }
+                BytesPacketPosition::Padding(pos) => {
+                    // Write remaining padding, if there is padding to write.
+                    let total_padding_len = padding_len(*this.payload_len) as usize;
+
+                    if pos != total_padding_len {
+                        let bytes_written = ensure_nonzero_bytes_written(ready!(this
+                            .inner
+                            .as_mut()
+                            .poll_write(cx, &EMPTY_BYTES[pos..total_padding_len]))?)?;
+                        *this.state = BytesPacketPosition::Padding(pos + bytes_written);
+                    } else {
+                        // everything written, break
+                        break;
+                    }
+                }
+            }
+        }
+        // Flush the underlying writer.
+        this.inner.as_mut().poll_flush(cx)
+    }
+
+    fn poll_shutdown(
+        mut self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> Poll<Result<(), std::io::Error>> {
+        // Call flush.
+        ready!(self.as_mut().poll_flush(cx))?;
+
+        let this = self.project();
+
+        // After a flush, being inside the padding state, and at the end of the padding
+        // is the only way to prevent a dirty shutdown.
+        if let BytesPacketPosition::Padding(pos) = *this.state {
+            let padding_len = padding_len(*this.payload_len) as usize;
+            if padding_len == pos {
+                // Shutdown the underlying writer
+                return this.inner.poll_shutdown(cx);
+            }
+        }
+
+        // Shutdown the underlying writer, bubbling up any errors.
+        ready!(this.inner.poll_shutdown(cx))?;
+
+        // return an error about unclean shutdown
+        Poll::Ready(Err(std::io::Error::new(
+            std::io::ErrorKind::BrokenPipe,
+            "unclean shutdown",
+        )))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::time::Duration;
+
+    use crate::wire::bytes::write_bytes;
+    use hex_literal::hex;
+    use lazy_static::lazy_static;
+    use tokio::io::AsyncWriteExt;
+    use tokio_test::{assert_err, assert_ok, io::Builder};
+
+    use super::*;
+
+    lazy_static! {
+        pub static ref LARGE_PAYLOAD: Vec<u8> = (0..255).collect::<Vec<u8>>().repeat(4 * 1024);
+    }
+
+    /// Helper function, calling the (simpler) write_bytes with the payload.
+    /// We use this to create data we want to see on the wire.
+    async fn produce_exp_bytes(payload: &[u8]) -> Vec<u8> {
+        let mut exp = vec![];
+        write_bytes(&mut exp, payload).await.unwrap();
+        exp
+    }
+
+    /// Write an empty bytes packet.
+    #[tokio::test]
+    async fn write_empty() {
+        let payload = &[];
+        let mut mock = Builder::new()
+            .write(&produce_exp_bytes(payload).await)
+            .build();
+
+        let mut w = BytesWriter::new(&mut mock, 0);
+        assert_ok!(w.write_all(&[]).await, "write all data");
+        assert_ok!(w.flush().await, "flush");
+    }
+
+    /// Write an empty bytes packet, not calling write.
+    #[tokio::test]
+    async fn write_empty_only_flush() {
+        let payload = &[];
+        let mut mock = Builder::new()
+            .write(&produce_exp_bytes(payload).await)
+            .build();
+
+        let mut w = BytesWriter::new(&mut mock, 0);
+        assert_ok!(w.flush().await, "flush");
+    }
+
+    /// Write an empty bytes packet, not calling write or flush, only shutdown.
+    #[tokio::test]
+    async fn write_empty_only_shutdown() {
+        let payload = &[];
+        let mut mock = Builder::new()
+            .write(&produce_exp_bytes(payload).await)
+            .build();
+
+        let mut w = BytesWriter::new(&mut mock, 0);
+        assert_ok!(w.shutdown().await, "shutdown");
+    }
+
+    /// Write a 1 bytes packet
+    #[tokio::test]
+    async fn write_1b() {
+        let payload = &[0xff];
+
+        let mut mock = Builder::new()
+            .write(&produce_exp_bytes(payload).await)
+            .build();
+
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+        assert_ok!(w.write_all(payload).await);
+        assert_ok!(w.flush().await, "flush");
+    }
+
+    /// Write a 8 bytes payload (no padding)
+    #[tokio::test]
+    async fn write_8b() {
+        let payload = &hex!("0001020304050607");
+
+        let mut mock = Builder::new()
+            .write(&produce_exp_bytes(payload).await)
+            .build();
+
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+        assert_ok!(w.write_all(payload).await);
+        assert_ok!(w.flush().await, "flush");
+    }
+
+    /// Write a 9 bytes payload (7 bytes padding)
+    #[tokio::test]
+    async fn write_9b() {
+        let payload = &hex!("000102030405060708");
+
+        let mut mock = Builder::new()
+            .write(&produce_exp_bytes(payload).await)
+            .build();
+
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+        assert_ok!(w.write_all(payload).await);
+        assert_ok!(w.flush().await, "flush");
+    }
+
+    /// Write a 9 bytes packet very granularly, with a lot of flushing in between,
+    /// and a shutdown at the end.
+    #[tokio::test]
+    async fn write_9b_flush() {
+        let payload = &hex!("000102030405060708");
+        let exp_bytes = produce_exp_bytes(payload).await;
+
+        let mut mock = Builder::new().write(&exp_bytes).build();
+
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+        assert_ok!(w.flush().await);
+
+        assert_ok!(w.write_all(&payload[..4]).await);
+        assert_ok!(w.flush().await);
+
+        // empty write, cause why not
+        assert_ok!(w.write_all(&[]).await);
+        assert_ok!(w.flush().await);
+
+        assert_ok!(w.write_all(&payload[4..]).await);
+        assert_ok!(w.flush().await);
+        assert_ok!(w.shutdown().await);
+    }
+
+    /// Write a 9 bytes packet, but cause the sink to only accept half of the
+    /// padding, ensuring we correctly write (only) the rest of the padding later.
+    /// We write another 2 bytes of "bait", where a faulty implementation (pre
+    /// cl/11384) would put too many null bytes.
+    #[tokio::test]
+    async fn write_9b_write_padding_2steps() {
+        let payload = &hex!("000102030405060708");
+        let exp_bytes = produce_exp_bytes(payload).await;
+
+        let mut mock = Builder::new()
+            .write(&exp_bytes[0..8]) // size
+            .write(&exp_bytes[8..17]) // payload
+            .write(&exp_bytes[17..19]) // padding (2 of 7 bytes)
+            // insert a wait to prevent Mock from merging the two writes into one
+            .wait(Duration::from_nanos(1))
+            .write(&hex!("0000000000ffff")) // padding (5 of 7 bytes, plus 2 bytes of "bait")
+            .build();
+
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+        assert_ok!(w.write_all(&payload[..]).await);
+        assert_ok!(w.flush().await);
+        // Write bait
+        assert_ok!(mock.write_all(&hex!("ffff")).await);
+    }
+
+    /// Write a larger bytes packet
+    #[tokio::test]
+    async fn write_1m() {
+        let payload = LARGE_PAYLOAD.as_slice();
+        let exp_bytes = produce_exp_bytes(payload).await;
+
+        let mut mock = Builder::new().write(&exp_bytes).build();
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+
+        assert_ok!(w.write_all(payload).await);
+        assert_ok!(w.flush().await, "flush");
+    }
+
+    /// Not calling flush at the end, but shutdown is also ok if we wrote all
+    /// bytes we promised to write (as shutdown implies flush)
+    #[tokio::test]
+    async fn write_shutdown_without_flush_end() {
+        let payload = &[0xf0, 0xff];
+        let exp_bytes = produce_exp_bytes(payload).await;
+
+        let mut mock = Builder::new().write(&exp_bytes).build();
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+
+        // call flush to write the size field
+        assert_ok!(w.flush().await);
+
+        // write payload
+        assert_ok!(w.write_all(payload).await);
+
+        // call shutdown
+        assert_ok!(w.shutdown().await);
+    }
+
+    /// Writing more bytes than previously signalled should fail.
+    #[tokio::test]
+    async fn write_more_than_signalled_fail() {
+        let mut buf = Vec::new();
+        let mut w = BytesWriter::new(&mut buf, 2);
+
+        assert_err!(w.write_all(&hex!("000102")).await);
+    }
+    /// Writing more bytes than previously signalled, but in two parts
+    #[tokio::test]
+    async fn write_more_than_signalled_split_fail() {
+        let mut buf = Vec::new();
+        let mut w = BytesWriter::new(&mut buf, 2);
+
+        // write two bytes
+        assert_ok!(w.write_all(&hex!("0001")).await);
+
+        // write the excess byte.
+        assert_err!(w.write_all(&hex!("02")).await);
+    }
+
+    /// Writing more bytes than previously signalled, but flushing after the
+    /// signalled amount should fail.
+    #[tokio::test]
+    async fn write_more_than_signalled_flush_fail() {
+        let mut buf = Vec::new();
+        let mut w = BytesWriter::new(&mut buf, 2);
+
+        // write two bytes, then flush
+        assert_ok!(w.write_all(&hex!("0001")).await);
+        assert_ok!(w.flush().await);
+
+        // write the excess byte.
+        assert_err!(w.write_all(&hex!("02")).await);
+    }
+
+    /// Calling shutdown while not having written all bytes that were promised
+    /// returns an error.
+    /// Note there's still cases of silent corruption if the user doesn't call
+    /// shutdown explicitly (only drops).
+    #[tokio::test]
+    async fn premature_shutdown() {
+        let payload = &[0xf0, 0xff];
+        let mut buf = Vec::new();
+        let mut w = BytesWriter::new(&mut buf, payload.len() as u64);
+
+        // call flush to write the size field
+        assert_ok!(w.flush().await);
+
+        // write half of the payload (!)
+        assert_ok!(w.write_all(&payload[0..1]).await);
+
+        // call shutdown, ensure it fails
+        assert_err!(w.shutdown().await);
+    }
+
+    /// Write to a Writer that fails to write during the size packet (after 4 bytes).
+    /// Ensure this error gets propagated on the first call to write.
+    #[tokio::test]
+    async fn inner_writer_fail_during_size_firstwrite() {
+        let payload = &[0xf0];
+
+        let mut mock = Builder::new()
+            .write(&1u32.to_le_bytes())
+            .write_error(std::io::Error::new(std::io::ErrorKind::Other, "🍿"))
+            .build();
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+
+        assert_err!(w.write_all(payload).await);
+    }
+
+    /// Write to a Writer that fails to write during the size packet (after 4 bytes).
+    /// Ensure this error gets propagated during an initial flush
+    #[tokio::test]
+    async fn inner_writer_fail_during_size_initial_flush() {
+        let payload = &[0xf0];
+
+        let mut mock = Builder::new()
+            .write(&1u32.to_le_bytes())
+            .write_error(std::io::Error::new(std::io::ErrorKind::Other, "🍿"))
+            .build();
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+
+        assert_err!(w.flush().await);
+    }
+
+    /// Write to a writer that fails to write during the payload (after 9 bytes).
+    /// Ensure this error gets propagated when we're writing this byte.
+    #[tokio::test]
+    async fn inner_writer_fail_during_write() {
+        let payload = &hex!("f0ff");
+
+        let mut mock = Builder::new()
+            .write(&2u64.to_le_bytes())
+            .write(&hex!("f0"))
+            .write_error(std::io::Error::new(std::io::ErrorKind::Other, "🍿"))
+            .build();
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+
+        assert_ok!(w.write(&hex!("f0")).await);
+        assert_err!(w.write(&hex!("ff")).await);
+    }
+
+    /// Write to a writer that fails to write during the padding (after 10 bytes).
+    /// Ensure this error gets propagated during a flush.
+    #[tokio::test]
+    async fn inner_writer_fail_during_padding_flush() {
+        let payload = &hex!("f0");
+
+        let mut mock = Builder::new()
+            .write(&1u64.to_le_bytes())
+            .write(&hex!("f0"))
+            .write(&hex!("00"))
+            .write_error(std::io::Error::new(std::io::ErrorKind::Other, "🍿"))
+            .build();
+        let mut w = BytesWriter::new(&mut mock, payload.len() as u64);
+
+        assert_ok!(w.write(&hex!("f0")).await);
+        assert_err!(w.flush().await);
+    }
+}
diff --git a/tvix/nix-compat/src/wire/mod.rs b/tvix/nix-compat/src/wire/mod.rs
new file mode 100644
index 0000000000..a197e3a1f4
--- /dev/null
+++ b/tvix/nix-compat/src/wire/mod.rs
@@ -0,0 +1,5 @@
+//! Module parsing and emitting the wire format used by Nix, both in the
+//! nix-daemon protocol as well as in the NAR format.
+
+mod bytes;
+pub use bytes::*;
diff --git a/tvix/nix-compat/testdata/narinfo.zst b/tvix/nix-compat/testdata/narinfo.zst
new file mode 100644
index 0000000000..361a422da8
--- /dev/null
+++ b/tvix/nix-compat/testdata/narinfo.zst
Binary files differdiff --git a/tvix/nix-lang-test-suite/README.md b/tvix/nix-lang-test-suite/README.md
new file mode 100644
index 0000000000..68f87f20f5
--- /dev/null
+++ b/tvix/nix-lang-test-suite/README.md
@@ -0,0 +1,140 @@
+# The Implementation Independent Nix Language Test Suite
+
+## Design Notes
+
+### Requirements
+
+- It should work with potentially any Nix implementation and with all serious
+  currently available ones (C++ Nix, hnix, Tvix, …). How much of it the
+  implementations pass, is of course an orthogonal question.
+- It should be easy to add test cases, independent of any specific
+  implementation.
+- It should be simple to ignore test cases and mark know failures
+  (similar to the notyetpassing mechanism in the Tvix test suite).
+
+### Test Case Types
+
+This is a summary of relevant kinds of test cases that can be found in the wild,
+usually testing some kind of concrete implementation, but also doubling up as a
+potential test case for _any_ Nix implementation. For the most part, this is the
+`lang` test suite of C++ Nix which is also used by Tvix and hnix.
+
+- **parse** test cases: Parsing the given expression should either *succeed* or
+  *fail*.
+
+  - C++ Nix doesn't have any expected output for the success cases while
+    `rnix-parser` checks them against its own textual AST representation.
+  - For the failure cases, `rnix-parser` and C++ Nix (as of recently) have
+    expected error messages/representations.
+
+  Both error and failure cases probably are hard to implement against expected
+  output/error messages for a generic test suite. Even if standardized error
+  codes are implemented (see below), it is doubtful whether it'd be useful
+  to have a dedicated code for every kind of parse/lex failure.
+- (strict) **eval** test cases: Evaluating the given expression should either
+  *fail* or *succeed* and yield a given result.
+
+  - **eval-okay** (success) tests currently require three things:
+
+    1. Successful evaluation after deeply forcing and printing the evaluation
+       result (i.e. `nix-instantiate --eval --strict`)
+    2. That the output matches an expected output exactly (string equality).
+       For this the output of `nix-instantiate(1)` is used, sometimes with
+       the addition of the `--xml --no-location` or `--json` flags.
+    3. Optionally, stderr may need to be equal to an expected string exactly
+       which would test e.g. `builtins.trace` messages or deprecation warnings
+       (C++ Nix).
+
+       This extra check is currently not supported by the Tvix test suite.
+
+  - **eval-fail** tests require that the given expression fails to evaluate. C++
+    Nix has recently started to also check the error messages via the stderr
+    mechanism described above. This is not supported by Tvix at the moment.
+- _lazy_ eval test cases: This is currently only supported by the `nix_oracle`
+  test suite in Tvix which compares the evaluation result of expressions to the
+  output of `nix-instantiate(1)` without `--strict`. By relying on the fact
+  that the resulting value is not forced deeply before printing, it can be
+  observed whether certain expressions are thunked or not.
+
+  This is somewhat fragile as permissible optimizations may prevent a thunk from
+  being created. However, this should not be an issue if the cases are chosen
+  carefully. Empirically, this test suite was useful for catching some instances
+  of overzealous evaluation early in development of Tvix.
+
+- **identity** test cases require that the given expression evaluates to a
+  value whose printed representation is the same (string equal to) the original
+  expression. Such test cases only exist in the Tvix test suite.
+
+  Of course only a limited number of expression satisfy this, but it is
+  useful for testing `nix-instantiate(1)` style value printing. Consequently,
+  it is kind of on the edge of what you can call a language test.
+
+### Extra Dependencies of Some Test Cases
+
+- **Filesystem**: Some test cases `import` other files or use `builtins.readFile`,
+  `builtins.readDir` and friends.
+- **Working and Home Directory**: Tests involving relative and home relative paths
+  need knowledge of the current and home directory to correctly interpret the output.
+  C++ Nix does a [search and replace on the test output for this purpose][cpp-nix-pwd-sed]
+- **Nix Store**: Some tests add files to the store, either via path interpolation,
+  `builtins.toFile` or `builtins.derivation`.
+
+  Additionally, it should be considered that Import-from-Derivation may be
+  interesting to test in the future. Currently, the Tvix and C++ Nix test
+  suites all pass with Import-from-Derivation disabled, i.e. a dummy store
+  implementation is enough.
+
+  Note that the absence of a store dependency ideally also influences the test
+  execution: In Tvix, for example, store independent tests can be executed
+  with a store backend that immediately errors out, verifying that the test
+  is, in fact, store independent.
+- **Environment**: The C++ Nix test suite sets a single environment variable,
+  `TEST_VAR=foo`. Additionally, `NIX_PATH` and `HOME` are sometimes set (the
+  latter is probably not a great idea, since it is not terribly reliable).
+- **Nix Path**: A predetermined Nix Path (via `NIX_PATH` and/or command line
+  arguments) needs to be set for some test cases.
+- **Nix flags**: Some tests need to have extra flags passed to `nix-instantiate(1)`
+  in order to work. This is done using a `.flags` file
+
+### Expected Output Considerations
+
+#### Success
+
+The expected output of `eval-okay` test cases (which are the majority of test
+cases) uses the standard strict output of `nix-instantiate(1)` in most cases
+which is nice to read and easy to work with. However, some more obscure aspects
+of this output inevitably leak into the test cases, namely the cycle detection
+and printing and (in the case of Tvix) the printing of thunks. Unfortunately,
+the output has been changed after Nix 2.3, bringing it closer to the output of
+`nix eval`, but in an inconsistent manner (e.g. `<CYCLE>` was changed to
+`Β«repeatedΒ»`, but `<LAMBDA>` remained). As a consequence, it is not always
+possible to write C++ Nix version independent test cases.
+
+It is unclear whether a satisfying solution (for a common test suite) can
+be achieved here as it has become a somewhat contentious [issue whether
+or not nix-instantiate should have a stable output](cpp-nix-attr-elision-printing-pr).
+
+A solution may be to use the XML output, specifically the `--xml --no-location`
+flags to `nix-instantiate(1)` for some of these instances. As it (hopefully)
+corresponds to `builtins.toXML`, there should be a greater incentive to keep it
+stable. It does support (only via `nix-instantiate(1)`, though) printing
+unevaluated thunks, but has no kind of cycle detection (which is fair enough for
+its intended purpose).
+
+#### Failure
+
+C++ Nix has recently (some time after Nix 2.3, probably much later actually)
+started checking error messages via expected stderr output. This naturally
+won't work for a implementation independent language test suite:
+
+- It is fine to have differing phrasing for error messages or localize them.
+- Printed error positions and stack traces may be slightly different depending
+  on implementation internals.
+- Formatting will almost certainly differ.
+
+Consequently, just checking for failure when running the test suite should be
+an option. Long term, it may be interesting to have standardized error codes
+and portable error code reporting.
+
+[cpp-nix-pwd-sed]: https://github.com/NixOS/nix/blob/2cb9c7c68102193e7d34fabe6102474fc7f98010/tests/functional/lang.sh#L109
+[cpp-nix-attr-elision-printing-pr]: https://github.com/NixOS/nix/pull/9606
diff --git a/tvix/proto/LICENSE b/tvix/proto/LICENSE
deleted file mode 100644
index 36878fe4cb..0000000000
--- a/tvix/proto/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-Copyright Β© 2021 The Tvix Authors
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-β€œSoftware”), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
diff --git a/tvix/proto/default.nix b/tvix/proto/default.nix
deleted file mode 100644
index 7ff03ba3b7..0000000000
--- a/tvix/proto/default.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-# Build protocol buffer definitions to ensure that protos are valid in
-# CI. Note that the output of this build target is not actually used
-# anywhere, it just functions as a CI check for now.
-{ pkgs, ... }:
-
-pkgs.runCommandNoCC "tvix-cc-proto" {} ''
-  mkdir $out
-  ${pkgs.protobuf}/bin/protoc -I ${./.} evaluator.proto --cpp_out=$out
-''
diff --git a/tvix/proto/evaluator.proto b/tvix/proto/evaluator.proto
deleted file mode 100644
index 710a28fb9d..0000000000
--- a/tvix/proto/evaluator.proto
+++ /dev/null
@@ -1,144 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright Β© 2021 The Tvix Authors
-syntax = "proto3";
-
-package tvix.proto.evaluator.v1;
-
-service EvaluatorService {
-  rpc Evaluate(stream EvaluateRequest) returns (stream EvaluateResponse) {}
-}
-
-//
-// Message types for EvaluateRequest
-//
-
-message EvaluateFile {
-  // Absolute path at which the evaluator can find the file to be
-  // evaluated.
-  string file_path = 1;
-
-  // Optional attribute that should be evaluated within the file,
-  // assuming that the value it evaluates to is an attribute set.
-  optional string attribute = 2;
-
-  // Additional arguments to pass into the evaluation, with which the
-  // file's top-level function will be auto-called.
-  map<string, NixValue> arguments = 3;
-}
-
-message EvaluateExpression {
-  // Literal Nix expression to evaluate.
-  string expression = 1;
-
-  // Working directory in which the expression should be evaluated.
-  string working_directory = 2;
-}
-
-message BuildResultChunk {
-  string drv_hash = 1;
-  string output = 2;
-  bytes data = 3;
-
-  // This field may be set on the first build result chunk returned
-  // to the evaluator, indicating the total size of the output that
-  // is going to be streamed in bytes.
-  //
-  // If set, the evaluator can use this to appropriately allocate a
-  // buffer for the output.
-  optional int64 output_size = 4;
-}
-
-// Indicates that a single build has completed successfully. In case
-// that the build outputs were required by the evaluator this also
-// indicates that the output has been returned completely.
-message BuildSuccess {
-  string drv_hash = 1;
-  string output = 2;
-}
-
-// Describes an error that occured during a single build.
-//
-// TODO: We might want a more sophisticated error type.
-message BuildError {
-  string drv_hash = 1;
-  string output = 2;
-  string error = 3;
-}
-
-message BuildResult {
-  oneof build_result {
-    BuildSuccess build_success = 1;
-    BuildError build_error = 2;
-  }
-}
-
-
-/// Messages sent to the evaluator by the build coordinator.
-message EvaluateRequest {
-  oneof message {
-    // Ask the evaluator to evaluate the specified file, and
-    // optionally attribute within that file. Must be the first
-    // message.
-    EvaluateFile evaluate_file = 1;
-
-    // Ask the evaluator to evaluate the specified Nix expression.
-    // Must be the first message.
-    EvaluateExpression evaluate_expression = 2;
-
-    // Send the chunks of a build result, in response to a
-    // BuildRequest.
-    //
-    // Note: This message might change as the store protocol is
-    // designed, as it is possible that mechanisms for transferring
-    // files might be reused between the protocols.
-    BuildResultChunk build_result_chunk = 3;
-
-    // Indicate the result of a single build. See the documentation
-    // for the message types defined above for semantic details.
-    BuildResult build_result = 4;
-  }
-}
-
-//
-// Message types for EvaluateResponse
-//
-
-// TODO: Placeholder type.
-message Derivation {
-  string drv = 1;
-}
-
-// TODO: Placeholder type.
-message NixValue {
-  string value = 1;
-}
-
-// TODO: Placeholder type.
-message NixError {
-  string value = 1;
-}
-
-message BuildRequest {
-  Derivation drv = 1;
-  string output = 2;
-}
-
-// Messages returned to the coordinator by the evaluator.
-message EvaluateResponse {
-  oneof message {
-    // A derivation that was instantiated while reducing the graph,
-    // and whose output is not required by the evaluator.
-    Derivation derivation = 1;
-
-    // A derivation that was instantiated while reducing the graph,
-    // and whose output is required by the evaluator (IFD).
-    BuildRequest build_request = 2;
-
-    // The final value yielded by the evaluation. Stream is closed
-    // after this.
-    NixValue done = 3;
-
-    // Evaluation error. Stream is closed after this.
-    NixError error = 4;
-  }
-}
diff --git a/tvix/scripts/bench-windtunnel.sh b/tvix/scripts/bench-windtunnel.sh
new file mode 100755
index 0000000000..aa359a8a82
--- /dev/null
+++ b/tvix/scripts/bench-windtunnel.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -i bash ../.. -A tvix.shell
+
+# Benchmark script that runs inside the Windtunnel build agent
+
+set -euo pipefail
+
+echo "Running benchmarks for tvix..."
+pushd "$(dirname "$(dirname "$0")")"
+cargo bench
+windtunnel-cli report -f criterion-rust .
+popd
+
+echo "Running tvix macrobenchmarks..."
+pushd "$(dirname "$(dirname "$0")")"
+
+depot_nixpkgs_path="$(nix eval --raw '("${((import ../third_party/sources {}).nixpkgs)}")')"
+pinned_nixpkgs_path="$(nix eval --raw '(builtins.fetchTarball {url = "https://github.com/NixOS/nixpkgs/archive/91050ea1e57e50388fa87a3302ba12d188ef723a.tar.gz"; sha256 = "1hf6cgaci1n186kkkjq106ryf8mmlq9vnwgfwh625wa8hfgdn4dm";})')"
+
+cargo build --release --bin tvix
+hyperfine --export-json ./results.json \
+    -n 'tvix-eval-depot-nixpkgs-hello' "target/release/tvix -E '(import ${depot_nixpkgs_path} {}).hello.outPath'" \
+    -n 'tvix-eval-depot-nixpkgs-cross-hello' "target/release/tvix -E '(import ${depot_nixpkgs_path} {}).pkgsCross.aarch64-multiplatform.hello.outPath'" \
+    -n 'tvix-eval-pinned-nixpkgs-hello' "target/release/tvix -E '(import ${pinned_nixpkgs_path} {}).hello.outPath'" \
+    -n 'tvix-eval-pinned-nixpkgs-cross-hello' "target/release/tvix -E '(import ${pinned_nixpkgs_path} {}).pkgsCross.aarch64-multiplatform.hello.outPath'"
+windtunnel-cli report -f hyperfine-json ./results.json
+popd
diff --git a/tvix/serde/.skip-subtree b/tvix/serde/.skip-subtree
new file mode 100644
index 0000000000..21b2d0d358
--- /dev/null
+++ b/tvix/serde/.skip-subtree
@@ -0,0 +1 @@
+The foods.nix can not be read by readTree.
diff --git a/tvix/serde/Cargo.toml b/tvix/serde/Cargo.toml
new file mode 100644
index 0000000000..5652126ada
--- /dev/null
+++ b/tvix/serde/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "tvix-serde"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+tvix-eval = { path = "../eval" }
+serde = { version = "1.0", features = ["derive"] }
+bstr = { version = "1.8.0", features = ["serde"] }
diff --git a/tvix/serde/default.nix b/tvix/serde/default.nix
new file mode 100644
index 0000000000..5880bd24f6
--- /dev/null
+++ b/tvix/serde/default.nix
@@ -0,0 +1,5 @@
+{ depot, ... }:
+
+depot.tvix.crates.workspaceMembers.tvix-serde.build.override {
+  runTests = true;
+}
diff --git a/tvix/serde/examples/cfg-demo.rs b/tvix/serde/examples/cfg-demo.rs
new file mode 100644
index 0000000000..5774a81f77
--- /dev/null
+++ b/tvix/serde/examples/cfg-demo.rs
@@ -0,0 +1,35 @@
+//! This program demonstrates how to use tvix_serde to deserialise
+//! program configuration (or other data) from Nix code.
+//!
+//! This makes it possible to use Nix as an embedded config language.
+//! For greater control over evaluation, and for features like adding
+//! additional builtins, depending directly on tvix_eval would be
+//! required.
+use serde::Deserialize;
+use std::collections::HashMap;
+
+#[derive(Debug, Deserialize)]
+enum Flavour {
+    Tasty,
+    Okay,
+    Eww,
+}
+
+#[allow(dead_code)]
+#[derive(Debug, Deserialize)]
+struct Data {
+    name: String,
+    foods: HashMap<String, Flavour>,
+}
+
+fn main() {
+    // Get the content from wherever, read it from a file, receive it
+    // over the network - whatever floats your boat! We'll include it
+    // as a string.
+    let code = include_str!("foods.nix");
+
+    // Now you can use tvix_serde to deserialise the struct:
+    let foods: Data = tvix_serde::from_str(code).expect("deserialisation should succeed");
+
+    println!("These are the foods:\n{:#?}", foods);
+}
diff --git a/tvix/serde/examples/foods.nix b/tvix/serde/examples/foods.nix
new file mode 100644
index 0000000000..c8733cd3ef
--- /dev/null
+++ b/tvix/serde/examples/foods.nix
@@ -0,0 +1,22 @@
+# This is content for the `Data` struct, written in intentionally
+# convoluted Nix code.
+let
+  mkFlavour = flavour: name: {
+    inherit name;
+    value = flavour;
+  };
+
+  tasty = mkFlavour "Tasty";
+  okay = mkFlavour "Okay";
+  eww = mkFlavour "Eww";
+in
+{
+  name = "exhaustive list of foods";
+
+  foods = builtins.listToAttrs [
+    (tasty "beef")
+    (okay "tomatoes")
+    (eww "olives")
+    (tasty "coffee")
+  ];
+}
diff --git a/tvix/serde/examples/nixpkgs.rs b/tvix/serde/examples/nixpkgs.rs
new file mode 100644
index 0000000000..ad8c4160b5
--- /dev/null
+++ b/tvix/serde/examples/nixpkgs.rs
@@ -0,0 +1,34 @@
+//! This program demonstrates deserialising some configuration
+//! structure from Nix code that makes use of nixpkgs.lib
+//!
+//! This example does not add the full set of Nix features (i.e.
+//! builds & derivations).
+
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize)]
+struct Config {
+    host: String,
+    port: usize,
+}
+
+fn main() {
+    let code = r#"
+    let
+       lib = import <nixpkgs/lib>;
+       host = lib.strings.concatStringsSep "." ["foo" "example" "com"];
+    in {
+      inherit host;
+      port = 4242;
+    }
+    "#;
+
+    let result = tvix_serde::from_str_with_config::<Config, _>(code, |eval| {
+        eval.enable_impure(None);
+    });
+
+    match result {
+        Ok(cfg) => println!("Config says: {}:{}", cfg.host, cfg.port),
+        Err(e) => eprintln!("{:?} / {}", e, e),
+    }
+}
diff --git a/tvix/serde/src/de.rs b/tvix/serde/src/de.rs
new file mode 100644
index 0000000000..428b9e2e81
--- /dev/null
+++ b/tvix/serde/src/de.rs
@@ -0,0 +1,475 @@
+//! Deserialisation from Nix to Rust values.
+
+use bstr::ByteSlice;
+use serde::de::value::{MapDeserializer, SeqDeserializer};
+use serde::de::{self, EnumAccess, VariantAccess};
+pub use tvix_eval::Evaluation;
+use tvix_eval::{EvalIO, Value};
+
+use crate::error::Error;
+
+struct NixDeserializer {
+    value: tvix_eval::Value,
+}
+
+impl NixDeserializer {
+    fn new(value: Value) -> Self {
+        if let Value::Thunk(thunk) = value {
+            Self::new(thunk.value().clone())
+        } else {
+            Self { value }
+        }
+    }
+}
+
+impl de::IntoDeserializer<'_, Error> for NixDeserializer {
+    type Deserializer = Self;
+
+    fn into_deserializer(self) -> Self::Deserializer {
+        self
+    }
+}
+
+/// Evaluate the Nix code in `src` and attempt to deserialise the
+/// value it returns to `T`.
+pub fn from_str<'code, T>(src: &'code str) -> Result<T, Error>
+where
+    T: serde::Deserialize<'code>,
+{
+    from_str_with_config(src, |_| /* no extra config */ ())
+}
+
+/// Evaluate the Nix code in `src`, with extra configuration for the
+/// `tvix_eval::Evaluation` provided by the given closure.
+pub fn from_str_with_config<'code, T, F>(src: &'code str, config: F) -> Result<T, Error>
+where
+    T: serde::Deserialize<'code>,
+    F: FnOnce(&mut Evaluation<Box<dyn EvalIO>>),
+{
+    // First step is to evaluate the Nix code ...
+    let mut eval = Evaluation::new_pure();
+    config(&mut eval);
+
+    eval.strict = true;
+    let result = eval.evaluate(src, None);
+
+    if !result.errors.is_empty() {
+        return Err(Error::NixErrors {
+            errors: result.errors,
+        });
+    }
+
+    let de = NixDeserializer::new(result.value.expect("value should be present on success"));
+
+    T::deserialize(de)
+}
+
+fn unexpected(expected: &'static str, got: &Value) -> Error {
+    Error::UnexpectedType {
+        expected,
+        got: got.type_of(),
+    }
+}
+
+fn visit_integer<I: TryFrom<i64>>(v: &Value) -> Result<I, Error> {
+    match v {
+        Value::Integer(i) => I::try_from(*i).map_err(|_| Error::IntegerConversion {
+            got: *i,
+            need: std::any::type_name::<I>(),
+        }),
+
+        _ => Err(unexpected("integer", v)),
+    }
+}
+
+impl<'de> de::Deserializer<'de> for NixDeserializer {
+    type Error = Error;
+
+    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        match self.value {
+            Value::Null => visitor.visit_unit(),
+            Value::Bool(b) => visitor.visit_bool(b),
+            Value::Integer(i) => visitor.visit_i64(i),
+            Value::Float(f) => visitor.visit_f64(f),
+            Value::String(s) => visitor.visit_string(s.to_string()),
+            Value::Path(p) => visitor.visit_string(p.to_string_lossy().into()), // TODO: hmm
+            Value::Attrs(_) => self.deserialize_map(visitor),
+            Value::List(_) => self.deserialize_seq(visitor),
+
+            // tvix-eval types that can not be deserialized through serde.
+            Value::Closure(_)
+            | Value::Builtin(_)
+            | Value::Thunk(_)
+            | Value::AttrNotFound
+            | Value::Blueprint(_)
+            | Value::DeferredUpvalue(_)
+            | Value::UnresolvedPath(_)
+            | Value::Json(..)
+            | Value::Catchable(_)
+            | Value::FinaliseRequest(_) => Err(Error::Unserializable {
+                value_type: self.value.type_of(),
+            }),
+        }
+    }
+
+    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        match self.value {
+            Value::Bool(b) => visitor.visit_bool(b),
+            _ => Err(unexpected("bool", &self.value)),
+        }
+    }
+
+    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_i8(visit_integer(&self.value)?)
+    }
+
+    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_i16(visit_integer(&self.value)?)
+    }
+
+    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_i32(visit_integer(&self.value)?)
+    }
+
+    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_i64(visit_integer(&self.value)?)
+    }
+
+    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_u8(visit_integer(&self.value)?)
+    }
+
+    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_u16(visit_integer(&self.value)?)
+    }
+
+    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_u32(visit_integer(&self.value)?)
+    }
+
+    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_u64(visit_integer(&self.value)?)
+    }
+
+    fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if let Value::Float(f) = self.value {
+            return visitor.visit_f32(f as f32);
+        }
+
+        Err(unexpected("float", &self.value))
+    }
+
+    fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if let Value::Float(f) = self.value {
+            return visitor.visit_f64(f);
+        }
+
+        Err(unexpected("float", &self.value))
+    }
+
+    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if let Value::String(s) = &self.value {
+            let chars = s.chars().collect::<Vec<_>>();
+            if chars.len() == 1 {
+                return visitor.visit_char(chars[0]);
+            }
+        }
+
+        Err(unexpected("char", &self.value))
+    }
+
+    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if let Value::String(s) = &self.value {
+            if let Ok(s) = s.to_str() {
+                return visitor.visit_str(s);
+            }
+        }
+
+        Err(unexpected("string", &self.value))
+    }
+
+    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if let Value::String(s) = &self.value {
+            if let Ok(s) = s.to_str() {
+                return visitor.visit_str(s);
+            }
+        }
+
+        Err(unexpected("string", &self.value))
+    }
+
+    fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        unimplemented!()
+    }
+
+    fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        unimplemented!()
+    }
+
+    // Note that this can not distinguish between a serialisation of
+    // `Some(())` and `None`.
+    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if let Value::Null = self.value {
+            visitor.visit_none()
+        } else {
+            visitor.visit_some(self)
+        }
+    }
+
+    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if let Value::Null = self.value {
+            return visitor.visit_unit();
+        }
+
+        Err(unexpected("null", &self.value))
+    }
+
+    fn deserialize_unit_struct<V>(
+        self,
+        _name: &'static str,
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        self.deserialize_unit(visitor)
+    }
+
+    fn deserialize_newtype_struct<V>(
+        self,
+        _name: &'static str,
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_newtype_struct(self)
+    }
+
+    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if let Value::List(list) = self.value {
+            let mut seq = SeqDeserializer::new(list.into_iter().map(NixDeserializer::new));
+            let result = visitor.visit_seq(&mut seq)?;
+            seq.end()?;
+            return Ok(result);
+        }
+
+        Err(unexpected("list", &self.value))
+    }
+
+    fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        // just represent tuples as lists ...
+        self.deserialize_seq(visitor)
+    }
+
+    fn deserialize_tuple_struct<V>(
+        self,
+        _name: &'static str,
+        _len: usize,
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        // same as above
+        self.deserialize_seq(visitor)
+    }
+
+    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if let Value::Attrs(attrs) = self.value {
+            let mut map = MapDeserializer::new(attrs.into_iter().map(|(k, v)| {
+                (
+                    NixDeserializer::new(Value::from(k)),
+                    NixDeserializer::new(v),
+                )
+            }));
+            let result = visitor.visit_map(&mut map)?;
+            map.end()?;
+            return Ok(result);
+        }
+
+        Err(unexpected("map", &self.value))
+    }
+
+    fn deserialize_struct<V>(
+        self,
+        _name: &'static str,
+        _fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        self.deserialize_map(visitor)
+    }
+
+    // This method is responsible for deserializing the externally
+    // tagged enum variant serialisation.
+    fn deserialize_enum<V>(
+        self,
+        name: &'static str,
+        _variants: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        match self.value {
+            // a string represents a unit variant
+            Value::String(ref s) => {
+                if let Ok(s) = s.to_str() {
+                    visitor.visit_enum(de::value::StrDeserializer::new(s))
+                } else {
+                    Err(unexpected(name, &self.value))
+                }
+            }
+
+            // an attribute set however represents an externally
+            // tagged enum with content
+            Value::Attrs(attrs) => visitor.visit_enum(Enum(*attrs)),
+
+            _ => Err(unexpected(name, &self.value)),
+        }
+    }
+
+    fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        self.deserialize_str(visitor)
+    }
+
+    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_unit()
+    }
+}
+
+struct Enum(tvix_eval::NixAttrs);
+
+impl<'de> EnumAccess<'de> for Enum {
+    type Error = Error;
+    type Variant = NixDeserializer;
+
+    // TODO: pass the known variants down here and check against them
+    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
+    where
+        V: de::DeserializeSeed<'de>,
+    {
+        if self.0.len() != 1 {
+            return Err(Error::AmbiguousEnum);
+        }
+
+        let (key, value) = self.0.into_iter().next().expect("length asserted above");
+        if let Ok(k) = key.to_str() {
+            let val = seed.deserialize(de::value::StrDeserializer::<Error>::new(k))?;
+            Ok((val, NixDeserializer::new(value)))
+        } else {
+            Err(unexpected("string", &key.clone().into()))
+        }
+    }
+}
+
+impl<'de> VariantAccess<'de> for NixDeserializer {
+    type Error = Error;
+
+    fn unit_variant(self) -> Result<(), Self::Error> {
+        // If this case is hit, a user specified the name of a unit
+        // enum variant but gave it content. Unit enum deserialisation
+        // is handled in `deserialize_enum` above.
+        Err(Error::UnitEnumContent)
+    }
+
+    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
+    where
+        T: de::DeserializeSeed<'de>,
+    {
+        seed.deserialize(self)
+    }
+
+    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        de::Deserializer::deserialize_seq(self, visitor)
+    }
+
+    fn struct_variant<V>(
+        self,
+        _fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        de::Deserializer::deserialize_map(self, visitor)
+    }
+}
diff --git a/tvix/serde/src/de_tests.rs b/tvix/serde/src/de_tests.rs
new file mode 100644
index 0000000000..1c3acd1c2f
--- /dev/null
+++ b/tvix/serde/src/de_tests.rs
@@ -0,0 +1,245 @@
+use serde::Deserialize;
+use std::collections::HashMap;
+use tvix_eval::builtin_macros::builtins;
+
+use crate::de::{from_str, from_str_with_config};
+
+#[test]
+fn deserialize_none() {
+    let result: Option<usize> = from_str("null").expect("should deserialize");
+    assert_eq!(None, result);
+}
+
+#[test]
+fn deserialize_some() {
+    let result: Option<usize> = from_str("40 + 2").expect("should deserialize");
+    assert_eq!(Some(42), result);
+}
+
+#[test]
+fn deserialize_string() {
+    let result: String = from_str(
+        r#"
+      let greeter = name: "Hello ${name}!";
+      in greeter "Slartibartfast"
+    "#,
+    )
+    .expect("should deserialize");
+
+    assert_eq!(result, "Hello Slartibartfast!");
+}
+
+#[test]
+fn deserialize_empty_list() {
+    let result: Vec<usize> = from_str("[ ]").expect("should deserialize");
+    assert!(result.is_empty())
+}
+
+#[test]
+fn deserialize_integer_list() {
+    let result: Vec<usize> =
+        from_str("builtins.map (n: n + 2) [ 21 40 67 ]").expect("should deserialize");
+    assert_eq!(result, vec![23, 42, 69]);
+}
+
+#[test]
+fn deserialize_empty_map() {
+    let result: HashMap<String, usize> = from_str("{ }").expect("should deserialize");
+    assert!(result.is_empty());
+}
+
+#[test]
+fn deserialize_integer_map() {
+    let result: HashMap<String, usize> = from_str("{ age = 40 + 2; }").expect("should deserialize");
+    assert_eq!(result.len(), 1);
+    assert_eq!(*result.get("age").unwrap(), 42);
+}
+
+#[test]
+fn deserialize_struct() {
+    #[derive(Debug, Deserialize, PartialEq)]
+    struct Person {
+        name: String,
+        age: usize,
+    }
+
+    let result: Person = from_str(
+        r#"
+    {
+      name = "Slartibartfast";
+      age = 42;
+    }
+    "#,
+    )
+    .expect("should deserialize");
+
+    assert_eq!(
+        result,
+        Person {
+            name: "Slartibartfast".into(),
+            age: 42,
+        }
+    );
+}
+
+#[test]
+fn deserialize_newtype() {
+    #[derive(Debug, Deserialize, PartialEq)]
+    struct Number(usize);
+
+    let result: Number = from_str("42").expect("should deserialize");
+    assert_eq!(result, Number(42));
+}
+
+#[test]
+fn deserialize_tuple() {
+    let result: (String, usize) = from_str(r#" [ "foo" 42 ] "#).expect("should deserialize");
+    assert_eq!(result, ("foo".into(), 42));
+}
+
+#[test]
+fn deserialize_unit_enum() {
+    #[derive(Debug, Deserialize, PartialEq)]
+    enum Foo {
+        Bar,
+        Baz,
+    }
+
+    let result: Foo = from_str("\"Baz\"").expect("should deserialize");
+    assert_eq!(result, Foo::Baz);
+}
+
+#[test]
+fn deserialize_tuple_enum() {
+    #[derive(Debug, Deserialize, PartialEq)]
+    enum Foo {
+        Bar,
+        Baz(String, usize),
+    }
+
+    let result: Foo = from_str(
+        r#"
+    {
+      Baz = [ "Slartibartfast" 42 ];
+    }
+    "#,
+    )
+    .expect("should deserialize");
+
+    assert_eq!(result, Foo::Baz("Slartibartfast".into(), 42));
+}
+
+#[test]
+fn deserialize_struct_enum() {
+    #[derive(Debug, Deserialize, PartialEq)]
+    enum Foo {
+        Bar,
+        Baz { name: String, age: usize },
+    }
+
+    let result: Foo = from_str(
+        r#"
+    {
+      Baz.name = "Slartibartfast";
+      Baz.age = 42;
+    }
+    "#,
+    )
+    .expect("should deserialize");
+
+    assert_eq!(
+        result,
+        Foo::Baz {
+            name: "Slartibartfast".into(),
+            age: 42
+        }
+    );
+}
+
+#[test]
+fn deserialize_enum_all() {
+    #[derive(Debug, Deserialize, PartialEq)]
+    #[serde(rename_all = "snake_case")]
+    enum TestEnum {
+        Unit,
+        Tuple(String, String),
+        Struct { name: String, age: usize },
+    }
+
+    let result: Vec<TestEnum> = from_str(
+        r#"
+      let
+        mkTuple = country: drink: { tuple = [ country drink ]; };
+      in
+      [
+        (mkTuple "UK" "cask ale")
+
+        "unit"
+
+        {
+          struct.name = "Slartibartfast";
+          struct.age = 42;
+        }
+
+        (mkTuple "Russia" "квас")
+      ]
+    "#,
+    )
+    .expect("should deserialize");
+
+    let expected = vec![
+        TestEnum::Tuple("UK".into(), "cask ale".into()),
+        TestEnum::Unit,
+        TestEnum::Struct {
+            name: "Slartibartfast".into(),
+            age: 42,
+        },
+        TestEnum::Tuple("Russia".into(), "квас".into()),
+    ];
+
+    assert_eq!(result, expected);
+}
+
+#[test]
+fn deserialize_with_config() {
+    let result: String = from_str_with_config("builtins.testWithConfig", |eval| {
+        // Add a literal string builtin that just returns `"ok"`.
+        eval.src_builtins.push(("testWithConfig", "\"ok\""));
+    })
+    .expect("should deserialize");
+
+    assert_eq!(result, "ok");
+}
+
+#[builtins]
+mod test_builtins {
+    use bstr::ByteSlice;
+    use tvix_eval::generators::{Gen, GenCo};
+    use tvix_eval::{ErrorKind, NixString, Value};
+
+    #[builtin("prependHello")]
+    pub async fn builtin_prepend_hello(co: GenCo, x: Value) -> Result<Value, ErrorKind> {
+        match x {
+            Value::String(s) => {
+                let new_string = NixString::from(format!("hello {}", s.to_str().unwrap()));
+                Ok(Value::from(new_string))
+            }
+            _ => Err(ErrorKind::TypeError {
+                expected: "string",
+                actual: "not string",
+            }),
+        }
+    }
+}
+
+#[test]
+fn deserialize_with_extra_builtin() {
+    let code = "builtins.prependHello \"world\"";
+
+    let result: String = from_str_with_config(code, |eval| {
+        eval.builtins.append(&mut test_builtins::builtins());
+    })
+    .expect("should deserialize");
+
+    assert_eq!(result, "hello world");
+}
diff --git a/tvix/serde/src/error.rs b/tvix/serde/src/error.rs
new file mode 100644
index 0000000000..d921cc4b4b
--- /dev/null
+++ b/tvix/serde/src/error.rs
@@ -0,0 +1,99 @@
+//! When serialising Nix goes wrong ...
+
+use std::error;
+use std::fmt::Display;
+
+#[derive(Clone, Debug)]
+pub enum Error {
+    /// Attempted to deserialise an unsupported Nix value (such as a
+    /// function) that can not be represented by the
+    /// [`serde::Deserialize`] trait.
+    Unserializable { value_type: &'static str },
+
+    /// Expected to deserialize a value that is unsupported by Nix.
+    Unsupported { wanted: &'static str },
+
+    /// Expected a specific type, but got something else on the Nix side.
+    UnexpectedType {
+        expected: &'static str,
+        got: &'static str,
+    },
+
+    /// Deserialisation error returned from `serde::de`.
+    Deserialization(String),
+
+    /// Deserialized integer did not fit.
+    IntegerConversion { got: i64, need: &'static str },
+
+    /// Evaluation of the supplied Nix code failed while computing the
+    /// value for deserialisation.
+    NixErrors { errors: Vec<tvix_eval::Error> },
+
+    /// Could not determine an externally tagged enum representation.
+    AmbiguousEnum,
+
+    /// Attempted to provide content to a unit enum.
+    UnitEnumContent,
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Error::Unserializable { value_type } => write!(
+                f,
+                "can not deserialise a Nix '{}' into a Rust type",
+                value_type
+            ),
+
+            Error::Unsupported { wanted } => {
+                write!(f, "can not deserialize a '{}' from a Nix value", wanted)
+            }
+
+            Error::UnexpectedType { expected, got } => {
+                write!(f, "expected type {}, but got Nix type {}", expected, got)
+            }
+
+            Error::NixErrors { errors } => {
+                writeln!(
+                    f,
+                    "{} occured during Nix evaluation: ",
+                    if errors.len() == 1 { "error" } else { "errors" }
+                )?;
+
+                for err in errors {
+                    writeln!(f, "{}", err.fancy_format_str())?;
+                }
+
+                Ok(())
+            }
+
+            Error::Deserialization(err) => write!(f, "deserialisation error occured: {}", err),
+
+            Error::IntegerConversion { got, need } => {
+                write!(f, "i64({}) does not fit in a {}", got, need)
+            }
+
+            Error::AmbiguousEnum => write!(f, "could not determine enum variant: ambiguous keys"),
+
+            Error::UnitEnumContent => write!(f, "provided content for unit enum variant"),
+        }
+    }
+}
+
+impl error::Error for Error {
+    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+        match self {
+            Self::NixErrors { errors, .. } => errors.first().map(|e| e as &dyn error::Error),
+            _ => None,
+        }
+    }
+}
+
+impl serde::de::Error for Error {
+    fn custom<T>(err: T) -> Self
+    where
+        T: Display,
+    {
+        Self::Deserialization(err.to_string())
+    }
+}
diff --git a/tvix/serde/src/lib.rs b/tvix/serde/src/lib.rs
new file mode 100644
index 0000000000..6a44affdc0
--- /dev/null
+++ b/tvix/serde/src/lib.rs
@@ -0,0 +1,12 @@
+//! `tvix-serde` implements (de-)serialisation of Rust data structures
+//! to/from Nix. This is intended to make it easy to use Nix as as
+//! configuration language.
+
+mod de;
+mod error;
+
+pub use de::from_str;
+pub use de::from_str_with_config;
+
+#[cfg(test)]
+mod de_tests;
diff --git a/tvix/shell.nix b/tvix/shell.nix
index 600d96d76d..f0d8ab1657 100644
--- a/tvix/shell.nix
+++ b/tvix/shell.nix
@@ -1,10 +1,62 @@
-let
-  depot = (import ./.. {});
-  pkgs = depot.third_party.nixpkgs;
+# This file is shell.nix in the tvix josh workspace,
+# *and* used to provide the //tvix:shell attribute in a full depot checkout.
+# Hence, it may not use depot as a toplevel argument.
 
-in pkgs.mkShell {
-  buildInputs = [
-    pkgs.rustup
+{
+  # This falls back to the tvix josh workspace-provided nixpkgs checkout.
+  # In the case of depot, it's always set explicitly.
+  pkgs ? (import ./nixpkgs {
+    depotOverlays = false;
+    depot.third_party.sources = import ./sources { };
+    additionalOverlays = [
+      (self: super: {
+        # https://github.com/googleapis/google-cloud-go/pull/9665
+        cbtemulator = super.cbtemulator.overrideAttrs (old: {
+          patches = old.patches or [ ] ++ [
+            ./nixpkgs/cbtemulator-uds.patch
+          ];
+        });
+      })
+    ];
+  })
+, ...
+}:
+
+pkgs.mkShell {
+  name = "tvix-rust-dev-env";
+  packages = [
+    pkgs.buf-language-server
+    pkgs.cargo
+    pkgs.cargo-machete
+    pkgs.cargo-expand
+    pkgs.clippy
+    pkgs.evans
+    pkgs.fuse
+    pkgs.go
+    pkgs.grpcurl
+    pkgs.hyperfine
+    pkgs.mdbook
+    pkgs.mdbook-plantuml
+    pkgs.nix_2_3 # b/313
+    pkgs.pkg-config
     pkgs.rust-analyzer
+    pkgs.rustc
+    pkgs.rustfmt
+    pkgs.plantuml
+    pkgs.protobuf
+  ] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
+    # We need these two dependencies in the ambient environment to be able to
+    # `cargo build` on MacOS.
+    pkgs.libiconv
+    pkgs.buildPackages.darwin.apple_sdk.frameworks.Security
   ];
+
+  # Set TVIX_BENCH_NIX_PATH to a somewhat pinned nixpkgs path.
+  # This is for invoking `cargo bench` imperatively on the developer machine.
+  # For tvix benchmarking across longer periods of time (by CI), we probably
+  # should also benchmark with a more static nixpkgs checkout, so nixpkgs
+  # refactorings are not observed as eval perf changes.
+  shellHook = ''
+    export TVIX_BENCH_NIX_PATH=nixpkgs=${pkgs.path}
+  '';
 }
diff --git a/tvix/src/bin/nix-store.rs b/tvix/src/bin/nix-store.rs
deleted file mode 100644
index 72e3bb2204..0000000000
--- a/tvix/src/bin/nix-store.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-fn main() {
-    main_args(std::env::args().collect()).unwrap_or_else(|e| e.exit());
-}
-
-pub fn main_args(args: Vec<String>) -> clap::Result<NixResult> {
-    let matches = clap::App::new("nix-store")
-        .subcommand(clap::App::new("--add").arg(clap::Arg::new("FILE").required(true).index(1)))
-        .try_get_matches_from(args.iter())?;
-    if let Some(add) = matches.subcommand_matches("--add") {
-        let file = add.value_of("FILE").expect("--add needs a file");
-        let file_contents =
-            std::fs::read_to_string(file).expect(&format!("file {} does not exist", file));
-        Ok(NixResult::FileAddedToStore {
-            content: file_contents,
-        })
-    } else {
-        panic!("read some arguments that we do not know: {:?}", args)
-    }
-}
-
-#[derive(Debug, Eq, PartialEq)]
-pub enum NixResult {
-    FileAddedToStore { content: String },
-}
-
-#[cfg(test)]
-mod integration_tests {
-    use std::{collections::VecDeque, io::Write};
-
-    use super::*;
-
-    #[derive(Debug)]
-    enum NixOutput {
-        Err {
-            status: i32,
-            stdout: String,
-            stderr: String,
-        },
-        Ok {
-            stdout: String,
-            stderr: String,
-        },
-    }
-
-    fn run_nix_command(cmd: &str, args: Vec<String>) -> NixOutput {
-        let out = std::process::Command::new(cmd)
-            .args(args)
-            .output()
-            .expect(&format!("could not run {}", cmd));
-        match out.status.code().expect("no status code!") {
-            0 => NixOutput::Ok {
-                stdout: String::from_utf8_lossy(&out.stdout).trim_end().to_string(),
-                stderr: String::from_utf8_lossy(&out.stderr).trim_end().to_string(),
-            },
-            status => NixOutput::Err {
-                status,
-                stdout: String::from_utf8_lossy(&out.stdout).trim_end().to_string(),
-                stderr: String::from_utf8_lossy(&out.stderr).trim_end().to_string(),
-            },
-        }
-    }
-
-    fn nix_nix_store<'a>(args: Vec<String>) -> NixResult {
-        match run_nix_command("nix-store", args) {
-            err @ NixOutput::Err { .. } => panic!("nix-store --add failed: {:#?}", err),
-            NixOutput::Ok { stdout, .. } => NixResult::FileAddedToStore {
-                content: std::fs::read_to_string(&stdout)
-                    .expect(&format!("cannot open {} as store file", stdout)),
-            },
-        }
-    }
-
-    fn tvix_nix_store<'a>(args: Vec<String>) -> NixResult {
-        eprintln!("running tvix with arguments {:?}", args);
-        let mut args = VecDeque::from(args);
-        args.push_front("tvix-store".to_string());
-        super::main_args(Vec::from(args))
-            .unwrap_or_else(|e| panic!("clap command line parsing failed:\n{}", e))
-    }
-
-    #[test]
-    fn test_nix_store_add() {
-        let file_content = "I am a copied file";
-        let mut tempfile = tempfile::NamedTempFile::new().expect("cannot create temp file");
-        tempfile
-            .write_all(file_content.as_bytes())
-            .expect("could not write to tempfile");
-        assert_eq!(
-            tvix_nix_store(vec![
-                "--add".to_string(),
-                tempfile.path().as_os_str().to_string_lossy().into_owned()
-            ]),
-            nix_nix_store(vec![
-                "--add".to_string(),
-                tempfile.path().as_os_str().to_string_lossy().into_owned()
-            ]),
-            "added file contents were not the same"
-        );
-
-        // make sure the tempfile lives till here
-        drop(tempfile)
-    }
-}
diff --git a/tvix/src/main.rs b/tvix/src/main.rs
deleted file mode 100644
index 40086e6f27..0000000000
--- a/tvix/src/main.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-fn main() {
-    println!("Hello, tvix!");
-}
diff --git a/tvix/store-go/LICENSE b/tvix/store-go/LICENSE
new file mode 100644
index 0000000000..2034ada6fd
--- /dev/null
+++ b/tvix/store-go/LICENSE
@@ -0,0 +1,21 @@
+Copyright Β© The Tvix Authors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+β€œSoftware”), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/tvix/store-go/README.md b/tvix/store-go/README.md
new file mode 100644
index 0000000000..594513412d
--- /dev/null
+++ b/tvix/store-go/README.md
@@ -0,0 +1,10 @@
+# store-go
+
+This directory contains generated golang bindings, both for the tvix-store data
+models, as well as the gRPC bindings.
+
+They are generated with `mg run //tvix:store-go:regenerate`.
+These files end with `.pb.go`, and are ensured to be up to date by Ci check.
+
+Additionally, code useful when interacting with these data structures
+(ending just with `.go`) is provided.
diff --git a/tvix/store-go/default.nix b/tvix/store-go/default.nix
new file mode 100644
index 0000000000..e4c3efd7ad
--- /dev/null
+++ b/tvix/store-go/default.nix
@@ -0,0 +1,31 @@
+{ depot, pkgs, ... }:
+
+let
+  regenerate = pkgs.writeShellScript "regenerate" ''
+    (cd $(git rev-parse --show-toplevel)/tvix/store-go && rm *.pb.go && cp ${depot.tvix.store.protos.go-bindings}/*.pb.go . && chmod +w *.pb.go)
+  '';
+in
+(pkgs.buildGoModule {
+  name = "store-go";
+  src = depot.third_party.gitignoreSource ./.;
+  vendorHash = "sha256-JAxjSI4efCwbAUbvS7AQ5ZbVlf3ebGDBzDFMTK7dvl4=";
+}).overrideAttrs (_: {
+  meta.ci.extraSteps = {
+    check = {
+      label = ":water_buffalo: ensure generated protobuf files match";
+      needsOutput = true;
+      command = pkgs.writeShellScript "pb-go-check" ''
+        ${regenerate}
+        if [[ -n "$(git status --porcelain -unormal)" ]]; then
+            echo "-----------------------------"
+            echo ".pb.go files need to be updated, mg run //tvix/store-go/regenerate"
+            echo "-----------------------------"
+            git status -unormal
+            exit 1
+        fi
+      '';
+      alwaysRun = true;
+    };
+  };
+  passthru.regenerate = regenerate;
+})
diff --git a/tvix/store-go/export.go b/tvix/store-go/export.go
new file mode 100644
index 0000000000..c68e015cdb
--- /dev/null
+++ b/tvix/store-go/export.go
@@ -0,0 +1,273 @@
+package storev1
+
+import (
+	"fmt"
+	"io"
+	"path"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	"github.com/nix-community/go-nix/pkg/nar"
+)
+
+type DirectoryLookupFn func([]byte) (*castorev1pb.Directory, error)
+type BlobLookupFn func([]byte) (io.ReadCloser, error)
+
+// Export will traverse a given root node, and write the contents in NAR format
+// to the passed Writer.
+// It uses directoryLookupFn and blobLookupFn to resolve references.
+func Export(
+	w io.Writer,
+	rootNode *castorev1pb.Node,
+	directoryLookupFn DirectoryLookupFn,
+	blobLookupFn BlobLookupFn,
+) error {
+	// initialize a NAR writer
+	narWriter, err := nar.NewWriter(w)
+	if err != nil {
+		return fmt.Errorf("unable to initialize nar writer: %w", err)
+	}
+	defer narWriter.Close()
+
+	// populate rootHeader
+	rootHeader := &nar.Header{
+		Path: "/",
+	}
+
+	// populate a stack
+	// we will push paths and directories to it when entering a directory,
+	// and emit individual elements to the NAR writer, draining the Directory object.
+	// once it's empty, we can pop it off the stack.
+	var stackPaths = []string{}
+	var stackDirectories = []*castorev1pb.Directory{}
+
+	// peek at the pathInfo root and assemble the root node and write to writer
+	// in the case of a regular file, we retrieve and write the contents, close and exit
+	// in the case of a symlink, we write the symlink, close and exit
+	if fileNode := rootNode.GetFile(); fileNode != nil {
+		rootHeader.Type = nar.TypeRegular
+		rootHeader.Size = int64(fileNode.GetSize())
+		rootHeader.Executable = fileNode.GetExecutable()
+		err := narWriter.WriteHeader(rootHeader)
+		if err != nil {
+			return fmt.Errorf("unable to write root header: %w", err)
+		}
+
+		// if it's a regular file, retrieve and write the contents
+		blobReader, err := blobLookupFn(fileNode.GetDigest())
+		if err != nil {
+			return fmt.Errorf("unable to lookup blob: %w", err)
+		}
+		defer blobReader.Close()
+
+		_, err = io.Copy(narWriter, blobReader)
+		if err != nil {
+			return fmt.Errorf("unable to read from blobReader: %w", err)
+		}
+
+		err = blobReader.Close()
+		if err != nil {
+			return fmt.Errorf("unable to close content reader: %w", err)
+		}
+
+		err = narWriter.Close()
+		if err != nil {
+			return fmt.Errorf("unable to close nar reader: %w", err)
+		}
+
+		return nil
+	} else if symlinkNode := rootNode.GetSymlink(); symlinkNode != nil {
+		rootHeader.Type = nar.TypeSymlink
+		rootHeader.LinkTarget = string(symlinkNode.GetTarget())
+		err := narWriter.WriteHeader(rootHeader)
+		if err != nil {
+			return fmt.Errorf("unable to write root header: %w", err)
+		}
+
+		err = narWriter.Close()
+		if err != nil {
+			return fmt.Errorf("unable to close nar reader: %w", err)
+		}
+	} else if directoryNode := rootNode.GetDirectory(); directoryNode != nil {
+		// We have a directory at the root, look it up and put in on the stack.
+		directory, err := directoryLookupFn(directoryNode.GetDigest())
+		if err != nil {
+			return fmt.Errorf("unable to lookup directory: %w", err)
+		}
+		stackDirectories = append(stackDirectories, directory)
+		stackPaths = append(stackPaths, "/")
+
+		err = narWriter.WriteHeader(&nar.Header{
+			Path: "/",
+			Type: nar.TypeDirectory,
+		})
+
+		if err != nil {
+			return fmt.Errorf("error writing header: %w", err)
+		}
+	} else {
+		panic("invalid type") // unreachable
+	}
+
+	// as long as the stack is not empty, we keep running.
+	for {
+		if len(stackDirectories) == 0 {
+			return nil
+		}
+
+		// Peek at the current top of the stack.
+		topOfStack := stackDirectories[len(stackDirectories)-1]
+		topOfStackPath := stackPaths[len(stackPaths)-1]
+
+		// get the next element that's lexicographically smallest, and drain it from
+		// the current directory on top of the stack.
+		nextNode := drainNextNode(topOfStack)
+
+		// If nextNode returns nil, there's nothing left in the directory node, so we
+		// can emit it from the stack.
+		// Contrary to the import case, we don't emit the node popping from the stack, but when pushing.
+		if nextNode == nil {
+			// pop off stack
+			stackDirectories = stackDirectories[:len(stackDirectories)-1]
+			stackPaths = stackPaths[:len(stackPaths)-1]
+
+			continue
+		}
+
+		switch n := (nextNode).(type) {
+		case *castorev1pb.DirectoryNode:
+			err := narWriter.WriteHeader(&nar.Header{
+				Path: path.Join(topOfStackPath, string(n.GetName())),
+				Type: nar.TypeDirectory,
+			})
+			if err != nil {
+				return fmt.Errorf("unable to write nar header: %w", err)
+			}
+
+			d, err := directoryLookupFn(n.GetDigest())
+			if err != nil {
+				return fmt.Errorf("unable to lookup directory: %w", err)
+			}
+
+			// add to stack
+			stackDirectories = append(stackDirectories, d)
+			stackPaths = append(stackPaths, path.Join(topOfStackPath, string(n.GetName())))
+		case *castorev1pb.FileNode:
+			err := narWriter.WriteHeader(&nar.Header{
+				Path:       path.Join(topOfStackPath, string(n.GetName())),
+				Type:       nar.TypeRegular,
+				Size:       int64(n.GetSize()),
+				Executable: n.GetExecutable(),
+			})
+			if err != nil {
+				return fmt.Errorf("unable to write nar header: %w", err)
+			}
+
+			// copy file contents
+			contentReader, err := blobLookupFn(n.GetDigest())
+			if err != nil {
+				return fmt.Errorf("unable to get blob: %w", err)
+			}
+			defer contentReader.Close()
+
+			_, err = io.Copy(narWriter, contentReader)
+			if err != nil {
+				return fmt.Errorf("unable to copy contents from contentReader: %w", err)
+			}
+
+			err = contentReader.Close()
+			if err != nil {
+				return fmt.Errorf("unable to close content reader: %w", err)
+			}
+		case *castorev1pb.SymlinkNode:
+			err := narWriter.WriteHeader(&nar.Header{
+				Path:       path.Join(topOfStackPath, string(n.GetName())),
+				Type:       nar.TypeSymlink,
+				LinkTarget: string(n.GetTarget()),
+			})
+			if err != nil {
+				return fmt.Errorf("unable to write nar header: %w", err)
+			}
+		}
+	}
+}
+
+// drainNextNode will drain a directory message with one of its child nodes,
+// whichever comes first alphabetically.
+func drainNextNode(d *castorev1pb.Directory) interface{} {
+	switch v := (smallestNode(d)).(type) {
+	case *castorev1pb.DirectoryNode:
+		d.Directories = d.Directories[1:]
+		return v
+	case *castorev1pb.FileNode:
+		d.Files = d.Files[1:]
+		return v
+	case *castorev1pb.SymlinkNode:
+		d.Symlinks = d.Symlinks[1:]
+		return v
+	case nil:
+		return nil
+	default:
+		panic("invalid type encountered")
+	}
+}
+
+// smallestNode will return the node from a directory message,
+// whichever comes first alphabetically.
+func smallestNode(d *castorev1pb.Directory) interface{} {
+	childDirectories := d.GetDirectories()
+	childFiles := d.GetFiles()
+	childSymlinks := d.GetSymlinks()
+
+	if len(childDirectories) > 0 {
+		if len(childFiles) > 0 {
+			if len(childSymlinks) > 0 {
+				// directories,files,symlinks
+				return smallerNode(smallerNode(childDirectories[0], childFiles[0]), childSymlinks[0])
+			} else {
+				// directories,files,!symlinks
+				return smallerNode(childDirectories[0], childFiles[0])
+			}
+		} else {
+			// directories,!files
+			if len(childSymlinks) > 0 {
+				// directories,!files,symlinks
+				return smallerNode(childDirectories[0], childSymlinks[0])
+			} else {
+				// directories,!files,!symlinks
+				return childDirectories[0]
+			}
+		}
+	} else {
+		// !directories
+		if len(childFiles) > 0 {
+			// !directories,files
+			if len(childSymlinks) > 0 {
+				// !directories,files,symlinks
+				return smallerNode(childFiles[0], childSymlinks[0])
+			} else {
+				// !directories,files,!symlinks
+				return childFiles[0]
+			}
+		} else {
+			//!directories,!files
+			if len(childSymlinks) > 0 {
+				//!directories,!files,symlinks
+				return childSymlinks[0]
+			} else {
+				//!directories,!files,!symlinks
+				return nil
+			}
+		}
+	}
+}
+
+// smallerNode compares two nodes by their name,
+// and returns the one with the smaller name.
+// both nodes may not be nil, we do check for these cases in smallestNode.
+func smallerNode(a interface{ GetName() []byte }, b interface{ GetName() []byte }) interface{ GetName() []byte } {
+	if string(a.GetName()) < string(b.GetName()) {
+		return a
+	} else {
+		return b
+	}
+}
diff --git a/tvix/store-go/export_test.go b/tvix/store-go/export_test.go
new file mode 100644
index 0000000000..6814df6414
--- /dev/null
+++ b/tvix/store-go/export_test.go
@@ -0,0 +1,134 @@
+package storev1_test
+
+import (
+	"bytes"
+	"io"
+	"os"
+	"testing"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	storev1pb "code.tvl.fyi/tvix/store-go"
+	"github.com/stretchr/testify/require"
+)
+
+func mustDirectoryDigest(d *castorev1pb.Directory) []byte {
+	dgst, err := d.Digest()
+	if err != nil {
+		panic(err)
+	}
+	return dgst
+}
+
+func TestSymlink(t *testing.T) {
+	node := &castorev1pb.Node{
+		Node: &castorev1pb.Node_Symlink{
+			Symlink: &castorev1pb.SymlinkNode{
+				Name:   []byte("doesntmatter"),
+				Target: []byte("/nix/store/somewhereelse"),
+			},
+		},
+	}
+
+	var buf bytes.Buffer
+
+	err := storev1pb.Export(&buf, node, func([]byte) (*castorev1pb.Directory, error) {
+		panic("no directories expected")
+	}, func([]byte) (io.ReadCloser, error) {
+		panic("no files expected")
+	})
+	require.NoError(t, err, "exporter shouldn't fail")
+
+	f, err := os.Open("testdata/symlink.nar")
+	require.NoError(t, err)
+
+	bytesExpected, err := io.ReadAll(f)
+	if err != nil {
+		panic(err)
+	}
+
+	require.Equal(t, bytesExpected, buf.Bytes(), "expected nar contents to match")
+}
+
+func TestRegular(t *testing.T) {
+	// The blake3 digest of the 0x01 byte.
+	BLAKE3_DIGEST_0X01 := []byte{
+		0x48, 0xfc, 0x72, 0x1f, 0xbb, 0xc1, 0x72, 0xe0, 0x92, 0x5f, 0xa2, 0x7a, 0xf1, 0x67, 0x1d,
+		0xe2, 0x25, 0xba, 0x92, 0x71, 0x34, 0x80, 0x29, 0x98, 0xb1, 0x0a, 0x15, 0x68, 0xa1, 0x88,
+		0x65, 0x2b,
+	}
+
+	node := &castorev1pb.Node{
+		Node: &castorev1pb.Node_File{
+			File: &castorev1pb.FileNode{
+				Name:       []byte("doesntmatter"),
+				Digest:     BLAKE3_DIGEST_0X01,
+				Size:       1,
+				Executable: false,
+			},
+		},
+	}
+
+	var buf bytes.Buffer
+
+	err := storev1pb.Export(&buf, node, func([]byte) (*castorev1pb.Directory, error) {
+		panic("no directories expected")
+	}, func(blobRef []byte) (io.ReadCloser, error) {
+		if !bytes.Equal(blobRef, BLAKE3_DIGEST_0X01) {
+			panic("unexpected blobref")
+		}
+		return io.NopCloser(bytes.NewBuffer([]byte{0x01})), nil
+	})
+	require.NoError(t, err, "exporter shouldn't fail")
+
+	f, err := os.Open("testdata/onebyteregular.nar")
+	require.NoError(t, err)
+
+	bytesExpected, err := io.ReadAll(f)
+	if err != nil {
+		panic(err)
+	}
+
+	require.Equal(t, bytesExpected, buf.Bytes(), "expected nar contents to match")
+}
+
+func TestEmptyDirectory(t *testing.T) {
+	// construct empty directory node this refers to
+	emptyDirectory := &castorev1pb.Directory{
+		Directories: []*castorev1pb.DirectoryNode{},
+		Files:       []*castorev1pb.FileNode{},
+		Symlinks:    []*castorev1pb.SymlinkNode{},
+	}
+	emptyDirectoryDigest := mustDirectoryDigest(emptyDirectory)
+
+	node := &castorev1pb.Node{
+		Node: &castorev1pb.Node_Directory{
+			Directory: &castorev1pb.DirectoryNode{
+				Name:   []byte("doesntmatter"),
+				Digest: emptyDirectoryDigest,
+				Size:   0,
+			},
+		},
+	}
+
+	var buf bytes.Buffer
+
+	err := storev1pb.Export(&buf, node, func(directoryRef []byte) (*castorev1pb.Directory, error) {
+		if !bytes.Equal(directoryRef, emptyDirectoryDigest) {
+			panic("unexpected directoryRef")
+		}
+		return emptyDirectory, nil
+	}, func([]byte) (io.ReadCloser, error) {
+		panic("no files expected")
+	})
+	require.NoError(t, err, "exporter shouldn't fail")
+
+	f, err := os.Open("testdata/emptydirectory.nar")
+	require.NoError(t, err)
+
+	bytesExpected, err := io.ReadAll(f)
+	if err != nil {
+		panic(err)
+	}
+
+	require.Equal(t, bytesExpected, buf.Bytes(), "expected nar contents to match")
+}
diff --git a/tvix/store-go/go.mod b/tvix/store-go/go.mod
new file mode 100644
index 0000000000..bd8450b000
--- /dev/null
+++ b/tvix/store-go/go.mod
@@ -0,0 +1,25 @@
+module code.tvl.fyi/tvix/store-go
+
+go 1.19
+
+require (
+	code.tvl.fyi/tvix/castore-go v0.0.0-20231105151352-990d6ba2175e
+	github.com/google/go-cmp v0.5.9
+	github.com/nix-community/go-nix v0.0.0-20231009143713-ebca3299475b
+	github.com/stretchr/testify v1.8.1
+	google.golang.org/grpc v1.59.0
+	google.golang.org/protobuf v1.31.0
+)
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.5 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/sys v0.14.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	lukechampine.com/blake3 v1.2.1 // indirect
+)
diff --git a/tvix/store-go/go.sum b/tvix/store-go/go.sum
new file mode 100644
index 0000000000..1b4bb2e708
--- /dev/null
+++ b/tvix/store-go/go.sum
@@ -0,0 +1,47 @@
+code.tvl.fyi/tvix/castore-go v0.0.0-20231105151352-990d6ba2175e h1:Nj+anfyEYeEdhnIo2BG/N1ZwQl1IvI7AH3TbNDLwUOA=
+code.tvl.fyi/tvix/castore-go v0.0.0-20231105151352-990d6ba2175e/go.mod h1:+vKbozsa04yy2TWh3kUVU568jaza3Hf0p1jAEoMoCwA=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
+github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/nix-community/go-nix v0.0.0-20231009143713-ebca3299475b h1:AWEKOdDO3JnHApQDOmONEKLXbMCQJhYJJfJpiWB9VGI=
+github.com/nix-community/go-nix v0.0.0-20231009143713-ebca3299475b/go.mod h1:hHM9UK2zOCjvmiLgeaW4LVbOW/vBaRWFJGzfi31/slQ=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE=
+google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
+google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
+lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
diff --git a/tvix/store-go/pathinfo.go b/tvix/store-go/pathinfo.go
new file mode 100644
index 0000000000..d0384c4fe2
--- /dev/null
+++ b/tvix/store-go/pathinfo.go
@@ -0,0 +1,99 @@
+package storev1
+
+import (
+	"bytes"
+	"crypto/sha256"
+	"encoding/base64"
+	"fmt"
+
+	"github.com/nix-community/go-nix/pkg/storepath"
+)
+
+// Validate performs some checks on the PathInfo struct, returning either the
+// StorePath of the root node, or an error.
+func (p *PathInfo) Validate() (*storepath.StorePath, error) {
+	// ensure References has the right number of bytes.
+	for i, reference := range p.GetReferences() {
+		if len(reference) != storepath.PathHashSize {
+			return nil, fmt.Errorf("invalid length of digest at position %d, expected %d, got %d", i, storepath.PathHashSize, len(reference))
+		}
+	}
+
+	// If there's a Narinfo field populated..
+	if narInfo := p.GetNarinfo(); narInfo != nil {
+		// ensure the NarSha256 digest has the correct length.
+		if len(narInfo.GetNarSha256()) != sha256.Size {
+			return nil, fmt.Errorf("invalid number of bytes for NarSha256: expected %d, got %d", sha256.Size, len(narInfo.GetNarSha256()))
+		}
+
+		// ensure the number of references matches len(References).
+		if len(narInfo.GetReferenceNames()) != len(p.GetReferences()) {
+			return nil, fmt.Errorf("inconsistent number of references: %d (references) vs %d (narinfo)", len(narInfo.GetReferenceNames()), len(p.GetReferences()))
+		}
+
+		// for each ReferenceName…
+		for i, referenceName := range narInfo.GetReferenceNames() {
+			// ensure it parses to a store path
+			storePath, err := storepath.FromString(referenceName)
+			if err != nil {
+				return nil, fmt.Errorf("invalid ReferenceName at position %d: %w", i, err)
+			}
+
+			// ensure the digest matches the one at References[i]
+			if !bytes.Equal(p.GetReferences()[i], storePath.Digest) {
+				return nil, fmt.Errorf(
+					"digest in ReferenceName at position %d does not match digest in PathInfo, expected %s, got %s",
+					i,
+					base64.StdEncoding.EncodeToString(p.GetReferences()[i]),
+					base64.StdEncoding.EncodeToString(storePath.Digest),
+				)
+			}
+		}
+
+		// If the Deriver field is populated, ensure it parses to a StorePath.
+		// We can't check for it to *not* end with .drv, as the .drv files produced by
+		// recursive Nix end with multiple .drv suffixes, and only one is popped when
+		// converting to this field.
+		if deriver := narInfo.GetDeriver(); deriver != nil {
+			deriverStorePath := storepath.StorePath{
+				Name:   string(deriver.GetName()),
+				Digest: deriver.GetDigest(),
+			}
+			if err := deriverStorePath.Validate(); err != nil {
+				return nil, fmt.Errorf("invalid deriver field: %w", err)
+			}
+		}
+	}
+
+	// ensure there is a (root) node present
+	rootNode := p.GetNode()
+	if rootNode == nil {
+		return nil, fmt.Errorf("root node must be set")
+	}
+
+	if err := rootNode.Validate(); err != nil {
+		return nil, fmt.Errorf("root node failed validation: %w", err)
+	}
+
+	// for all three node types, ensure the name properly parses to a store path.
+	// This is a stricter check as the ones already performed in the rootNode.Validate() call.
+	var rootNodeName []byte
+
+	if node := rootNode.GetDirectory(); node != nil {
+		rootNodeName = node.GetName()
+	} else if node := rootNode.GetFile(); node != nil {
+		rootNodeName = node.GetName()
+	} else if node := rootNode.GetSymlink(); node != nil {
+		rootNodeName = node.GetName()
+	} else {
+		// already caught by rootNode.Validate()
+		panic("unreachable")
+	}
+
+	storePath, err := storepath.FromString(string(rootNodeName))
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse root node name %s as StorePath: %w", rootNodeName, err)
+	}
+
+	return storePath, nil
+}
diff --git a/tvix/store-go/pathinfo.pb.go b/tvix/store-go/pathinfo.pb.go
new file mode 100644
index 0000000000..a4915a3c1f
--- /dev/null
+++ b/tvix/store-go/pathinfo.pb.go
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        (unknown)
+// source: tvix/store/protos/pathinfo.proto
+
+package storev1
+
+import (
+	castore_go "code.tvl.fyi/tvix/castore-go"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type NARInfo_CA_Hash int32
+
+const (
+	// produced when uploading fixed-output store paths using NAR-based
+	// hashing (`outputHashMode = "recursive"`).
+	NARInfo_CA_NAR_SHA256 NARInfo_CA_Hash = 0
+	NARInfo_CA_NAR_SHA1   NARInfo_CA_Hash = 1
+	NARInfo_CA_NAR_SHA512 NARInfo_CA_Hash = 2
+	NARInfo_CA_NAR_MD5    NARInfo_CA_Hash = 3
+	// Produced when uploading .drv files or outputs produced by
+	// builtins.toFile.
+	// Produces equivalent digests as FLAT_SHA256, but is a separate
+	// hashing type in Nix, affecting output path calculation.
+	NARInfo_CA_TEXT_SHA256 NARInfo_CA_Hash = 4
+	// Produced when using fixed-output derivations with
+	// `outputHashMode = "flat"`.
+	NARInfo_CA_FLAT_SHA1   NARInfo_CA_Hash = 5
+	NARInfo_CA_FLAT_MD5    NARInfo_CA_Hash = 6
+	NARInfo_CA_FLAT_SHA256 NARInfo_CA_Hash = 7
+	NARInfo_CA_FLAT_SHA512 NARInfo_CA_Hash = 8
+)
+
+// Enum value maps for NARInfo_CA_Hash.
+var (
+	NARInfo_CA_Hash_name = map[int32]string{
+		0: "NAR_SHA256",
+		1: "NAR_SHA1",
+		2: "NAR_SHA512",
+		3: "NAR_MD5",
+		4: "TEXT_SHA256",
+		5: "FLAT_SHA1",
+		6: "FLAT_MD5",
+		7: "FLAT_SHA256",
+		8: "FLAT_SHA512",
+	}
+	NARInfo_CA_Hash_value = map[string]int32{
+		"NAR_SHA256":  0,
+		"NAR_SHA1":    1,
+		"NAR_SHA512":  2,
+		"NAR_MD5":     3,
+		"TEXT_SHA256": 4,
+		"FLAT_SHA1":   5,
+		"FLAT_MD5":    6,
+		"FLAT_SHA256": 7,
+		"FLAT_SHA512": 8,
+	}
+)
+
+func (x NARInfo_CA_Hash) Enum() *NARInfo_CA_Hash {
+	p := new(NARInfo_CA_Hash)
+	*p = x
+	return p
+}
+
+func (x NARInfo_CA_Hash) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (NARInfo_CA_Hash) Descriptor() protoreflect.EnumDescriptor {
+	return file_tvix_store_protos_pathinfo_proto_enumTypes[0].Descriptor()
+}
+
+func (NARInfo_CA_Hash) Type() protoreflect.EnumType {
+	return &file_tvix_store_protos_pathinfo_proto_enumTypes[0]
+}
+
+func (x NARInfo_CA_Hash) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use NARInfo_CA_Hash.Descriptor instead.
+func (NARInfo_CA_Hash) EnumDescriptor() ([]byte, []int) {
+	return file_tvix_store_protos_pathinfo_proto_rawDescGZIP(), []int{2, 1, 0}
+}
+
+// PathInfo shows information about a Nix Store Path.
+// That's a single element inside /nix/store.
+type PathInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The path can be a directory, file or symlink.
+	Node *castore_go.Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
+	// List of references (output path hashes)
+	// This really is the raw *bytes*, after decoding nixbase32, and not a
+	// base32-encoded string.
+	References [][]byte `protobuf:"bytes,2,rep,name=references,proto3" json:"references,omitempty"`
+	// see below.
+	Narinfo *NARInfo `protobuf:"bytes,3,opt,name=narinfo,proto3" json:"narinfo,omitempty"`
+}
+
+func (x *PathInfo) Reset() {
+	*x = PathInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PathInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PathInfo) ProtoMessage() {}
+
+func (x *PathInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PathInfo.ProtoReflect.Descriptor instead.
+func (*PathInfo) Descriptor() ([]byte, []int) {
+	return file_tvix_store_protos_pathinfo_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *PathInfo) GetNode() *castore_go.Node {
+	if x != nil {
+		return x.Node
+	}
+	return nil
+}
+
+func (x *PathInfo) GetReferences() [][]byte {
+	if x != nil {
+		return x.References
+	}
+	return nil
+}
+
+func (x *PathInfo) GetNarinfo() *NARInfo {
+	if x != nil {
+		return x.Narinfo
+	}
+	return nil
+}
+
+// Represents a path in the Nix store (a direct child of STORE_DIR).
+// It is commonly formatted by a nixbase32-encoding the digest, and
+// concatenating the name, separated by a `-`.
+type StorePath struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The string after digest and `-`.
+	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	// The digest (20 bytes).
+	Digest []byte `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"`
+}
+
+func (x *StorePath) Reset() {
+	*x = StorePath{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *StorePath) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*StorePath) ProtoMessage() {}
+
+func (x *StorePath) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use StorePath.ProtoReflect.Descriptor instead.
+func (*StorePath) Descriptor() ([]byte, []int) {
+	return file_tvix_store_protos_pathinfo_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *StorePath) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *StorePath) GetDigest() []byte {
+	if x != nil {
+		return x.Digest
+	}
+	return nil
+}
+
+// Nix C++ uses NAR (Nix Archive) as a format to transfer store paths,
+// and stores metadata and signatures in NARInfo files.
+// Store all these attributes in a separate message.
+//
+// This is useful to render .narinfo files to clients, or to preserve/validate
+// these signatures.
+// As verifying these signatures requires the whole NAR file to be synthesized,
+// moving to another signature scheme is desired.
+// Even then, it still makes sense to hold this data, for old clients.
+type NARInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// This size of the NAR file, in bytes.
+	NarSize uint64 `protobuf:"varint,1,opt,name=nar_size,json=narSize,proto3" json:"nar_size,omitempty"`
+	// The sha256 of the NAR file representation.
+	NarSha256 []byte `protobuf:"bytes,2,opt,name=nar_sha256,json=narSha256,proto3" json:"nar_sha256,omitempty"`
+	// The signatures in a .narinfo file.
+	Signatures []*NARInfo_Signature `protobuf:"bytes,3,rep,name=signatures,proto3" json:"signatures,omitempty"`
+	// A list of references. To validate .narinfo signatures, a fingerprint needs
+	// to be constructed.
+	// This fingerprint doesn't just contain the hashes of the output paths of all
+	// references (like PathInfo.references), but their whole (base)names, so we
+	// need to keep them somewhere.
+	ReferenceNames []string `protobuf:"bytes,4,rep,name=reference_names,json=referenceNames,proto3" json:"reference_names,omitempty"`
+	// The StorePath of the .drv file producing this output.
+	// The .drv suffix is omitted in its `name` field.
+	Deriver *StorePath `protobuf:"bytes,5,opt,name=deriver,proto3" json:"deriver,omitempty"`
+	// The CA field in the .narinfo.
+	// Its textual representations seen in the wild are one of the following:
+	//   - `fixed:r:sha256:1gcky5hlf5vqfzpyhihydmm54grhc94mcs8w7xr8613qsqb1v2j6`
+	//     fixed-output derivations using "recursive" `outputHashMode`.
+	//   - `fixed:sha256:19xqkh72crbcba7flwxyi3n293vav6d7qkzkh2v4zfyi4iia8vj8
+	//     fixed-output derivations using "flat" `outputHashMode`
+	//   - `text:sha256:19xqkh72crbcba7flwxyi3n293vav6d7qkzkh2v4zfyi4iia8vj8`
+	//     Text hashing, used for uploaded .drv files and outputs produced by
+	//     builtins.toFile.
+	//
+	// Semantically, they can be split into the following components:
+	//   - "content address prefix". Currently, "fixed" and "text" are supported.
+	//   - "hash mode". Currently, "flat" and "recursive" are supported.
+	//   - "hash type". The underlying hash function used.
+	//     Currently, sha1, md5, sha256, sha512.
+	//   - "digest". The digest itself.
+	//
+	// There are some restrictions on the possible combinations.
+	// For example, `text` and `fixed:recursive` always imply sha256.
+	//
+	// We use an enum to encode the possible combinations, and optimize for the
+	// common case, `fixed:recursive`, identified as `NAR_SHA256`.
+	Ca *NARInfo_CA `protobuf:"bytes,6,opt,name=ca,proto3" json:"ca,omitempty"`
+}
+
+func (x *NARInfo) Reset() {
+	*x = NARInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *NARInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NARInfo) ProtoMessage() {}
+
+func (x *NARInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NARInfo.ProtoReflect.Descriptor instead.
+func (*NARInfo) Descriptor() ([]byte, []int) {
+	return file_tvix_store_protos_pathinfo_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *NARInfo) GetNarSize() uint64 {
+	if x != nil {
+		return x.NarSize
+	}
+	return 0
+}
+
+func (x *NARInfo) GetNarSha256() []byte {
+	if x != nil {
+		return x.NarSha256
+	}
+	return nil
+}
+
+func (x *NARInfo) GetSignatures() []*NARInfo_Signature {
+	if x != nil {
+		return x.Signatures
+	}
+	return nil
+}
+
+func (x *NARInfo) GetReferenceNames() []string {
+	if x != nil {
+		return x.ReferenceNames
+	}
+	return nil
+}
+
+func (x *NARInfo) GetDeriver() *StorePath {
+	if x != nil {
+		return x.Deriver
+	}
+	return nil
+}
+
+func (x *NARInfo) GetCa() *NARInfo_CA {
+	if x != nil {
+		return x.Ca
+	}
+	return nil
+}
+
+// This represents a (parsed) signature line in a .narinfo file.
+type NARInfo_Signature struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+}
+
+func (x *NARInfo_Signature) Reset() {
+	*x = NARInfo_Signature{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *NARInfo_Signature) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NARInfo_Signature) ProtoMessage() {}
+
+func (x *NARInfo_Signature) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NARInfo_Signature.ProtoReflect.Descriptor instead.
+func (*NARInfo_Signature) Descriptor() ([]byte, []int) {
+	return file_tvix_store_protos_pathinfo_proto_rawDescGZIP(), []int{2, 0}
+}
+
+func (x *NARInfo_Signature) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *NARInfo_Signature) GetData() []byte {
+	if x != nil {
+		return x.Data
+	}
+	return nil
+}
+
+type NARInfo_CA struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The hashing type used.
+	Type NARInfo_CA_Hash `protobuf:"varint,1,opt,name=type,proto3,enum=tvix.store.v1.NARInfo_CA_Hash" json:"type,omitempty"`
+	// The digest, in raw bytes.
+	Digest []byte `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"`
+}
+
+func (x *NARInfo_CA) Reset() {
+	*x = NARInfo_CA{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *NARInfo_CA) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NARInfo_CA) ProtoMessage() {}
+
+func (x *NARInfo_CA) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_store_protos_pathinfo_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NARInfo_CA.ProtoReflect.Descriptor instead.
+func (*NARInfo_CA) Descriptor() ([]byte, []int) {
+	return file_tvix_store_protos_pathinfo_proto_rawDescGZIP(), []int{2, 1}
+}
+
+func (x *NARInfo_CA) GetType() NARInfo_CA_Hash {
+	if x != nil {
+		return x.Type
+	}
+	return NARInfo_CA_NAR_SHA256
+}
+
+func (x *NARInfo_CA) GetDigest() []byte {
+	if x != nil {
+		return x.Digest
+	}
+	return nil
+}
+
+var File_tvix_store_protos_pathinfo_proto protoreflect.FileDescriptor
+
+var file_tvix_store_protos_pathinfo_proto_rawDesc = []byte{
+	0x0a, 0x20, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x73, 0x2f, 0x70, 0x61, 0x74, 0x68, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x12, 0x0d, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76,
+	0x31, 0x1a, 0x21, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x22, 0x87, 0x01, 0x0a, 0x08, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66,
+	0x6f, 0x12, 0x29, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x15, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76,
+	0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x1e, 0x0a, 0x0a,
+	0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c,
+	0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x07,
+	0x6e, 0x61, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
+	0x74, 0x76, 0x69, 0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x41,
+	0x52, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6e, 0x61, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x37,
+	0x0a, 0x09, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+	0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
+	0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0xa9, 0x04, 0x0a, 0x07, 0x4e, 0x41, 0x52, 0x49,
+	0x6e, 0x66, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x61, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6e, 0x61, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d,
+	0x0a, 0x0a, 0x6e, 0x61, 0x72, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0c, 0x52, 0x09, 0x6e, 0x61, 0x72, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x40, 0x0a,
+	0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x20, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76,
+	0x31, 0x2e, 0x4e, 0x41, 0x52, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74,
+	0x75, 0x72, 0x65, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12,
+	0x27, 0x0a, 0x0f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d,
+	0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
+	0x6e, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x64, 0x65, 0x72, 0x69,
+	0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x76, 0x69, 0x78,
+	0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x50,
+	0x61, 0x74, 0x68, 0x52, 0x07, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x02,
+	0x63, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e,
+	0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x41, 0x52, 0x49, 0x6e, 0x66, 0x6f,
+	0x2e, 0x43, 0x41, 0x52, 0x02, 0x63, 0x61, 0x1a, 0x33, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61,
+	0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0xe4, 0x01, 0x0a,
+	0x02, 0x43, 0x41, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0e, 0x32, 0x1e, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76,
+	0x31, 0x2e, 0x4e, 0x41, 0x52, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x43, 0x41, 0x2e, 0x48, 0x61, 0x73,
+	0x68, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73,
+	0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x22,
+	0x91, 0x01, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x41, 0x52, 0x5f,
+	0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x41, 0x52, 0x5f,
+	0x53, 0x48, 0x41, 0x31, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x41, 0x52, 0x5f, 0x53, 0x48,
+	0x41, 0x35, 0x31, 0x32, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x41, 0x52, 0x5f, 0x4d, 0x44,
+	0x35, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x53, 0x48, 0x41, 0x32,
+	0x35, 0x36, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4c, 0x41, 0x54, 0x5f, 0x53, 0x48, 0x41,
+	0x31, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4c, 0x41, 0x54, 0x5f, 0x4d, 0x44, 0x35, 0x10,
+	0x06, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x4c, 0x41, 0x54, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36,
+	0x10, 0x07, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x4c, 0x41, 0x54, 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31,
+	0x32, 0x10, 0x08, 0x42, 0x24, 0x5a, 0x22, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e,
+	0x66, 0x79, 0x69, 0x2f, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2d, 0x67,
+	0x6f, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x33,
+}
+
+var (
+	file_tvix_store_protos_pathinfo_proto_rawDescOnce sync.Once
+	file_tvix_store_protos_pathinfo_proto_rawDescData = file_tvix_store_protos_pathinfo_proto_rawDesc
+)
+
+func file_tvix_store_protos_pathinfo_proto_rawDescGZIP() []byte {
+	file_tvix_store_protos_pathinfo_proto_rawDescOnce.Do(func() {
+		file_tvix_store_protos_pathinfo_proto_rawDescData = protoimpl.X.CompressGZIP(file_tvix_store_protos_pathinfo_proto_rawDescData)
+	})
+	return file_tvix_store_protos_pathinfo_proto_rawDescData
+}
+
+var file_tvix_store_protos_pathinfo_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_tvix_store_protos_pathinfo_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_tvix_store_protos_pathinfo_proto_goTypes = []interface{}{
+	(NARInfo_CA_Hash)(0),      // 0: tvix.store.v1.NARInfo.CA.Hash
+	(*PathInfo)(nil),          // 1: tvix.store.v1.PathInfo
+	(*StorePath)(nil),         // 2: tvix.store.v1.StorePath
+	(*NARInfo)(nil),           // 3: tvix.store.v1.NARInfo
+	(*NARInfo_Signature)(nil), // 4: tvix.store.v1.NARInfo.Signature
+	(*NARInfo_CA)(nil),        // 5: tvix.store.v1.NARInfo.CA
+	(*castore_go.Node)(nil),   // 6: tvix.castore.v1.Node
+}
+var file_tvix_store_protos_pathinfo_proto_depIdxs = []int32{
+	6, // 0: tvix.store.v1.PathInfo.node:type_name -> tvix.castore.v1.Node
+	3, // 1: tvix.store.v1.PathInfo.narinfo:type_name -> tvix.store.v1.NARInfo
+	4, // 2: tvix.store.v1.NARInfo.signatures:type_name -> tvix.store.v1.NARInfo.Signature
+	2, // 3: tvix.store.v1.NARInfo.deriver:type_name -> tvix.store.v1.StorePath
+	5, // 4: tvix.store.v1.NARInfo.ca:type_name -> tvix.store.v1.NARInfo.CA
+	0, // 5: tvix.store.v1.NARInfo.CA.type:type_name -> tvix.store.v1.NARInfo.CA.Hash
+	6, // [6:6] is the sub-list for method output_type
+	6, // [6:6] is the sub-list for method input_type
+	6, // [6:6] is the sub-list for extension type_name
+	6, // [6:6] is the sub-list for extension extendee
+	0, // [0:6] is the sub-list for field type_name
+}
+
+func init() { file_tvix_store_protos_pathinfo_proto_init() }
+func file_tvix_store_protos_pathinfo_proto_init() {
+	if File_tvix_store_protos_pathinfo_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_tvix_store_protos_pathinfo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PathInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_store_protos_pathinfo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*StorePath); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_store_protos_pathinfo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*NARInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_store_protos_pathinfo_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*NARInfo_Signature); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_store_protos_pathinfo_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*NARInfo_CA); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_tvix_store_protos_pathinfo_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   5,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_tvix_store_protos_pathinfo_proto_goTypes,
+		DependencyIndexes: file_tvix_store_protos_pathinfo_proto_depIdxs,
+		EnumInfos:         file_tvix_store_protos_pathinfo_proto_enumTypes,
+		MessageInfos:      file_tvix_store_protos_pathinfo_proto_msgTypes,
+	}.Build()
+	File_tvix_store_protos_pathinfo_proto = out.File
+	file_tvix_store_protos_pathinfo_proto_rawDesc = nil
+	file_tvix_store_protos_pathinfo_proto_goTypes = nil
+	file_tvix_store_protos_pathinfo_proto_depIdxs = nil
+}
diff --git a/tvix/store-go/pathinfo_test.go b/tvix/store-go/pathinfo_test.go
new file mode 100644
index 0000000000..e248f52c8d
--- /dev/null
+++ b/tvix/store-go/pathinfo_test.go
@@ -0,0 +1,149 @@
+package storev1_test
+
+import (
+	"path"
+	"testing"
+
+	"github.com/nix-community/go-nix/pkg/storepath"
+	"github.com/stretchr/testify/assert"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	storev1pb "code.tvl.fyi/tvix/store-go"
+)
+
+const (
+	EXAMPLE_STORE_PATH = "00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p2017022118243"
+)
+
+var (
+	exampleStorePathDigest = []byte{
+		0x8a, 0x12, 0x32, 0x15, 0x22, 0xfd, 0x91, 0xef, 0xbd, 0x60, 0xeb, 0xb2, 0x48, 0x1a, 0xf8, 0x85,
+		0x80, 0xf6, 0x16, 0x00}
+)
+
+func genPathInfoSymlink() *storev1pb.PathInfo {
+	return &storev1pb.PathInfo{
+		Node: &castorev1pb.Node{
+			Node: &castorev1pb.Node_Symlink{
+				Symlink: &castorev1pb.SymlinkNode{
+					Name:   []byte("00000000000000000000000000000000-dummy"),
+					Target: []byte("/nix/store/somewhereelse"),
+				},
+			},
+		},
+		References: [][]byte{exampleStorePathDigest},
+		Narinfo: &storev1pb.NARInfo{
+			NarSize:        0,
+			NarSha256:      []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+			Signatures:     []*storev1pb.NARInfo_Signature{},
+			ReferenceNames: []string{EXAMPLE_STORE_PATH},
+		},
+	}
+}
+
+func genPathInfoSymlinkThin() *storev1pb.PathInfo {
+	pi := genPathInfoSymlink()
+	pi.Narinfo = nil
+
+	return pi
+}
+
+func TestValidate(t *testing.T) {
+	t.Run("happy symlink", func(t *testing.T) {
+		storePath, err := genPathInfoSymlink().Validate()
+		assert.NoError(t, err, "PathInfo must validate")
+		assert.Equal(t, "00000000000000000000000000000000-dummy", storePath.String())
+	})
+
+	t.Run("happy symlink thin", func(t *testing.T) {
+		storePath, err := genPathInfoSymlinkThin().Validate()
+		assert.NoError(t, err, "PathInfo must validate")
+		assert.Equal(t, "00000000000000000000000000000000-dummy", storePath.String())
+	})
+
+	t.Run("invalid nar_sha256", func(t *testing.T) {
+		pi := genPathInfoSymlink()
+
+		// create broken references, where the reference digest is wrong
+		pi.Narinfo.NarSha256 = []byte{0xbe, 0xef}
+
+		_, err := pi.Validate()
+		assert.Error(t, err, "must not validate")
+	})
+
+	t.Run("invalid reference digest", func(t *testing.T) {
+		pi := genPathInfoSymlink()
+
+		// create broken references, where the reference digest is wrong
+		pi.References = append(pi.References, []byte{0x00})
+
+		_, err := pi.Validate()
+		assert.Error(t, err, "must not validate")
+	})
+
+	t.Run("invalid reference name", func(t *testing.T) {
+		pi := genPathInfoSymlink()
+
+		// make the reference name an invalid store path
+		pi.Narinfo.ReferenceNames[0] = "00000000000000000000000000000000-"
+
+		_, err := pi.Validate()
+		assert.Error(t, err, "must not validate")
+	})
+
+	t.Run("reference name digest mismatch", func(t *testing.T) {
+		pi := genPathInfoSymlink()
+
+		// cause the digest for the reference to mismatch
+		pi.Narinfo.ReferenceNames[0] = "11111111111111111111111111111111-dummy"
+
+		_, err := pi.Validate()
+		assert.Error(t, err, "must not validate")
+	})
+
+	t.Run("nil root node", func(t *testing.T) {
+		pi := genPathInfoSymlink()
+
+		pi.Node = nil
+
+		_, err := pi.Validate()
+		assert.Error(t, err, "must not validate")
+	})
+
+	t.Run("invalid root node name", func(t *testing.T) {
+		pi := genPathInfoSymlink()
+
+		// make the reference name an invalid store path - it may not be absolute
+		symlinkNode := pi.Node.GetSymlink()
+		symlinkNode.Name = []byte(path.Join(storepath.StoreDir, "00000000000000000000000000000000-dummy"))
+
+		_, err := pi.Validate()
+		assert.Error(t, err, "must not validate")
+	})
+
+	t.Run("happy deriver", func(t *testing.T) {
+		pi := genPathInfoSymlink()
+
+		// add the Deriver Field.
+		pi.Narinfo.Deriver = &storev1pb.StorePath{
+			Digest: exampleStorePathDigest,
+			Name:   "foo",
+		}
+
+		_, err := pi.Validate()
+		assert.NoError(t, err, "must validate")
+	})
+
+	t.Run("invalid deriver", func(t *testing.T) {
+		pi := genPathInfoSymlink()
+
+		// add the Deriver Field, with a broken digest
+		pi.Narinfo.Deriver = &storev1pb.StorePath{
+			Digest: []byte{},
+			Name:   "foo2",
+		}
+		_, err := pi.Validate()
+		assert.Error(t, err, "must not validate")
+	})
+
+}
diff --git a/tvix/store-go/pick_next_node_test.go b/tvix/store-go/pick_next_node_test.go
new file mode 100644
index 0000000000..55a6b034f1
--- /dev/null
+++ b/tvix/store-go/pick_next_node_test.go
@@ -0,0 +1,51 @@
+package storev1
+
+import (
+	"testing"
+
+	castorev1pb "code.tvl.fyi/tvix/castore-go"
+	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/require"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func requireProtoEq(t *testing.T, expected interface{}, actual interface{}) {
+	if diff := cmp.Diff(expected, actual, protocmp.Transform()); diff != "" {
+		t.Errorf("unexpected difference:\n%v", diff)
+	}
+}
+
+func TestPopNextNode(t *testing.T) {
+	t.Run("empty directory", func(t *testing.T) {
+		d := &castorev1pb.Directory{
+			Directories: []*castorev1pb.DirectoryNode{},
+			Files:       []*castorev1pb.FileNode{},
+			Symlinks:    []*castorev1pb.SymlinkNode{},
+		}
+
+		n := drainNextNode(d)
+		require.Equal(t, nil, n)
+	})
+	t.Run("only directories", func(t *testing.T) {
+		ds := &castorev1pb.Directory{
+			Directories: []*castorev1pb.DirectoryNode{{
+				Name:   []byte("a"),
+				Digest: []byte{},
+				Size:   0,
+			}, {
+				Name:   []byte("b"),
+				Digest: []byte{},
+				Size:   0,
+			}},
+			Files:    []*castorev1pb.FileNode{},
+			Symlinks: []*castorev1pb.SymlinkNode{},
+		}
+
+		n := drainNextNode(ds)
+		requireProtoEq(t, &castorev1pb.DirectoryNode{
+			Name:   []byte("a"),
+			Digest: []byte{},
+			Size:   0,
+		}, n)
+	})
+}
diff --git a/tvix/store-go/rpc_pathinfo.pb.go b/tvix/store-go/rpc_pathinfo.pb.go
new file mode 100644
index 0000000000..883ffb3f01
--- /dev/null
+++ b/tvix/store-go/rpc_pathinfo.pb.go
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        (unknown)
+// source: tvix/store/protos/rpc_pathinfo.proto
+
+package storev1
+
+import (
+	castore_go "code.tvl.fyi/tvix/castore-go"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// The parameters that can be used to lookup a (single) PathInfo object.
+// Currently, only a lookup by output hash is supported.
+type GetPathInfoRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to ByWhat:
+	//
+	//	*GetPathInfoRequest_ByOutputHash
+	ByWhat isGetPathInfoRequest_ByWhat `protobuf_oneof:"by_what"`
+}
+
+func (x *GetPathInfoRequest) Reset() {
+	*x = GetPathInfoRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetPathInfoRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetPathInfoRequest) ProtoMessage() {}
+
+func (x *GetPathInfoRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetPathInfoRequest.ProtoReflect.Descriptor instead.
+func (*GetPathInfoRequest) Descriptor() ([]byte, []int) {
+	return file_tvix_store_protos_rpc_pathinfo_proto_rawDescGZIP(), []int{0}
+}
+
+func (m *GetPathInfoRequest) GetByWhat() isGetPathInfoRequest_ByWhat {
+	if m != nil {
+		return m.ByWhat
+	}
+	return nil
+}
+
+func (x *GetPathInfoRequest) GetByOutputHash() []byte {
+	if x, ok := x.GetByWhat().(*GetPathInfoRequest_ByOutputHash); ok {
+		return x.ByOutputHash
+	}
+	return nil
+}
+
+type isGetPathInfoRequest_ByWhat interface {
+	isGetPathInfoRequest_ByWhat()
+}
+
+type GetPathInfoRequest_ByOutputHash struct {
+	// The output hash of a nix path (20 bytes).
+	// This is the nixbase32-decoded portion of a Nix output path, so to substitute
+	// /nix/store/xm35nga2g20mz5sm5l6n8v3bdm86yj83-cowsay-3.04
+	// this field would contain nixbase32dec("xm35nga2g20mz5sm5l6n8v3bdm86yj83").
+	ByOutputHash []byte `protobuf:"bytes,1,opt,name=by_output_hash,json=byOutputHash,proto3,oneof"`
+}
+
+func (*GetPathInfoRequest_ByOutputHash) isGetPathInfoRequest_ByWhat() {}
+
+// The parameters that can be used to lookup (multiple) PathInfo objects.
+// Currently no filtering is possible, all objects are returned.
+type ListPathInfoRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *ListPathInfoRequest) Reset() {
+	*x = ListPathInfoRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ListPathInfoRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ListPathInfoRequest) ProtoMessage() {}
+
+func (x *ListPathInfoRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ListPathInfoRequest.ProtoReflect.Descriptor instead.
+func (*ListPathInfoRequest) Descriptor() ([]byte, []int) {
+	return file_tvix_store_protos_rpc_pathinfo_proto_rawDescGZIP(), []int{1}
+}
+
+// CalculateNARResponse is the response returned by the CalculateNAR request.
+//
+// It contains the size of the NAR representation (in bytes), and the sha56
+// digest.
+type CalculateNARResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// This size of the NAR file, in bytes.
+	NarSize uint64 `protobuf:"varint,1,opt,name=nar_size,json=narSize,proto3" json:"nar_size,omitempty"`
+	// The sha256 of the NAR file representation.
+	NarSha256 []byte `protobuf:"bytes,2,opt,name=nar_sha256,json=narSha256,proto3" json:"nar_sha256,omitempty"`
+}
+
+func (x *CalculateNARResponse) Reset() {
+	*x = CalculateNARResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CalculateNARResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CalculateNARResponse) ProtoMessage() {}
+
+func (x *CalculateNARResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CalculateNARResponse.ProtoReflect.Descriptor instead.
+func (*CalculateNARResponse) Descriptor() ([]byte, []int) {
+	return file_tvix_store_protos_rpc_pathinfo_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *CalculateNARResponse) GetNarSize() uint64 {
+	if x != nil {
+		return x.NarSize
+	}
+	return 0
+}
+
+func (x *CalculateNARResponse) GetNarSha256() []byte {
+	if x != nil {
+		return x.NarSha256
+	}
+	return nil
+}
+
+var File_tvix_store_protos_rpc_pathinfo_proto protoreflect.FileDescriptor
+
+var file_tvix_store_protos_rpc_pathinfo_proto_rawDesc = []byte{
+	0x0a, 0x24, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x69, 0x6e, 0x66, 0x6f,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x73, 0x74, 0x6f,
+	0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x21, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74,
+	0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f,
+	0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x73,
+	0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x61, 0x74, 0x68,
+	0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x47, 0x0a, 0x12, 0x47, 0x65,
+	0x74, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x12, 0x26, 0x0a, 0x0e, 0x62, 0x79, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x68, 0x61,
+	0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x79, 0x4f, 0x75,
+	0x74, 0x70, 0x75, 0x74, 0x48, 0x61, 0x73, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x62, 0x79, 0x5f, 0x77,
+	0x68, 0x61, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x49,
+	0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x50, 0x0a, 0x14, 0x43, 0x61,
+	0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x41, 0x52, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x61, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6e, 0x61, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a,
+	0x0a, 0x6e, 0x61, 0x72, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x0c, 0x52, 0x09, 0x6e, 0x61, 0x72, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x32, 0xa0, 0x02, 0x0a,
+	0x0f, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+	0x12, 0x41, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x21, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x73,
+	0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x49,
+	0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x76, 0x69,
+	0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x49,
+	0x6e, 0x66, 0x6f, 0x12, 0x37, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x17, 0x2e, 0x74, 0x76, 0x69,
+	0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x49,
+	0x6e, 0x66, 0x6f, 0x1a, 0x17, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65,
+	0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4a, 0x0a, 0x0c,
+	0x43, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x41, 0x52, 0x12, 0x15, 0x2e, 0x74,
+	0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e,
+	0x6f, 0x64, 0x65, 0x1a, 0x23, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65,
+	0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x41, 0x52,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74,
+	0x12, 0x22, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31,
+	0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x73, 0x74, 0x6f, 0x72,
+	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x30, 0x01, 0x42,
+	0x24, 0x5a, 0x22, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e, 0x66, 0x79, 0x69, 0x2f,
+	0x74, 0x76, 0x69, 0x78, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2d, 0x67, 0x6f, 0x3b, 0x73, 0x74,
+	0x6f, 0x72, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_tvix_store_protos_rpc_pathinfo_proto_rawDescOnce sync.Once
+	file_tvix_store_protos_rpc_pathinfo_proto_rawDescData = file_tvix_store_protos_rpc_pathinfo_proto_rawDesc
+)
+
+func file_tvix_store_protos_rpc_pathinfo_proto_rawDescGZIP() []byte {
+	file_tvix_store_protos_rpc_pathinfo_proto_rawDescOnce.Do(func() {
+		file_tvix_store_protos_rpc_pathinfo_proto_rawDescData = protoimpl.X.CompressGZIP(file_tvix_store_protos_rpc_pathinfo_proto_rawDescData)
+	})
+	return file_tvix_store_protos_rpc_pathinfo_proto_rawDescData
+}
+
+var file_tvix_store_protos_rpc_pathinfo_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_tvix_store_protos_rpc_pathinfo_proto_goTypes = []interface{}{
+	(*GetPathInfoRequest)(nil),   // 0: tvix.store.v1.GetPathInfoRequest
+	(*ListPathInfoRequest)(nil),  // 1: tvix.store.v1.ListPathInfoRequest
+	(*CalculateNARResponse)(nil), // 2: tvix.store.v1.CalculateNARResponse
+	(*PathInfo)(nil),             // 3: tvix.store.v1.PathInfo
+	(*castore_go.Node)(nil),      // 4: tvix.castore.v1.Node
+}
+var file_tvix_store_protos_rpc_pathinfo_proto_depIdxs = []int32{
+	0, // 0: tvix.store.v1.PathInfoService.Get:input_type -> tvix.store.v1.GetPathInfoRequest
+	3, // 1: tvix.store.v1.PathInfoService.Put:input_type -> tvix.store.v1.PathInfo
+	4, // 2: tvix.store.v1.PathInfoService.CalculateNAR:input_type -> tvix.castore.v1.Node
+	1, // 3: tvix.store.v1.PathInfoService.List:input_type -> tvix.store.v1.ListPathInfoRequest
+	3, // 4: tvix.store.v1.PathInfoService.Get:output_type -> tvix.store.v1.PathInfo
+	3, // 5: tvix.store.v1.PathInfoService.Put:output_type -> tvix.store.v1.PathInfo
+	2, // 6: tvix.store.v1.PathInfoService.CalculateNAR:output_type -> tvix.store.v1.CalculateNARResponse
+	3, // 7: tvix.store.v1.PathInfoService.List:output_type -> tvix.store.v1.PathInfo
+	4, // [4:8] is the sub-list for method output_type
+	0, // [0:4] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_tvix_store_protos_rpc_pathinfo_proto_init() }
+func file_tvix_store_protos_rpc_pathinfo_proto_init() {
+	if File_tvix_store_protos_rpc_pathinfo_proto != nil {
+		return
+	}
+	file_tvix_store_protos_pathinfo_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetPathInfoRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ListPathInfoRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CalculateNARResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_tvix_store_protos_rpc_pathinfo_proto_msgTypes[0].OneofWrappers = []interface{}{
+		(*GetPathInfoRequest_ByOutputHash)(nil),
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_tvix_store_protos_rpc_pathinfo_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   3,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_tvix_store_protos_rpc_pathinfo_proto_goTypes,
+		DependencyIndexes: file_tvix_store_protos_rpc_pathinfo_proto_depIdxs,
+		MessageInfos:      file_tvix_store_protos_rpc_pathinfo_proto_msgTypes,
+	}.Build()
+	File_tvix_store_protos_rpc_pathinfo_proto = out.File
+	file_tvix_store_protos_rpc_pathinfo_proto_rawDesc = nil
+	file_tvix_store_protos_rpc_pathinfo_proto_goTypes = nil
+	file_tvix_store_protos_rpc_pathinfo_proto_depIdxs = nil
+}
diff --git a/tvix/store-go/rpc_pathinfo_grpc.pb.go b/tvix/store-go/rpc_pathinfo_grpc.pb.go
new file mode 100644
index 0000000000..8d6c0ff841
--- /dev/null
+++ b/tvix/store-go/rpc_pathinfo_grpc.pb.go
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.3.0
+// - protoc             (unknown)
+// source: tvix/store/protos/rpc_pathinfo.proto
+
+package storev1
+
+import (
+	castore_go "code.tvl.fyi/tvix/castore-go"
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+const (
+	PathInfoService_Get_FullMethodName          = "/tvix.store.v1.PathInfoService/Get"
+	PathInfoService_Put_FullMethodName          = "/tvix.store.v1.PathInfoService/Put"
+	PathInfoService_CalculateNAR_FullMethodName = "/tvix.store.v1.PathInfoService/CalculateNAR"
+	PathInfoService_List_FullMethodName         = "/tvix.store.v1.PathInfoService/List"
+)
+
+// PathInfoServiceClient is the client API for PathInfoService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type PathInfoServiceClient interface {
+	// Return a PathInfo message matching the criteria specified in the
+	// GetPathInfoRequest message.
+	Get(ctx context.Context, in *GetPathInfoRequest, opts ...grpc.CallOption) (*PathInfo, error)
+	// Upload a PathInfo object to the remote end. It MUST not return until the
+	// PathInfo object has been written on the the remote end.
+	//
+	// The remote end MAY check if a potential DirectoryNode has already been
+	// uploaded.
+	//
+	// Uploading clients SHOULD obviously not steer other machines to try to
+	// substitute before from the remote end before having finished uploading
+	// PathInfo, Directories and Blobs.
+	// The returned PathInfo object MAY contain additional narinfo signatures, but
+	// is otherwise left untouched.
+	Put(ctx context.Context, in *PathInfo, opts ...grpc.CallOption) (*PathInfo, error)
+	// Calculate the NAR representation of the contents specified by the
+	// root_node. The calculation SHOULD be cached server-side for subsequent
+	// requests.
+	//
+	// All references (to blobs or Directory messages) MUST already exist in the
+	// store.
+	//
+	// The method can be used to produce a Nix fixed-output path, which contains
+	// the (compressed) sha256 of the NAR content representation in the root_node
+	// name (suffixed with the name).
+	//
+	// It can also be used to calculate arbitrary NAR hashes of output paths, in
+	// case a legacy Nix Binary Cache frontend is provided.
+	CalculateNAR(ctx context.Context, in *castore_go.Node, opts ...grpc.CallOption) (*CalculateNARResponse, error)
+	// Return a stream of PathInfo messages matching the criteria specified in
+	// ListPathInfoRequest.
+	List(ctx context.Context, in *ListPathInfoRequest, opts ...grpc.CallOption) (PathInfoService_ListClient, error)
+}
+
+type pathInfoServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewPathInfoServiceClient(cc grpc.ClientConnInterface) PathInfoServiceClient {
+	return &pathInfoServiceClient{cc}
+}
+
+func (c *pathInfoServiceClient) Get(ctx context.Context, in *GetPathInfoRequest, opts ...grpc.CallOption) (*PathInfo, error) {
+	out := new(PathInfo)
+	err := c.cc.Invoke(ctx, PathInfoService_Get_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *pathInfoServiceClient) Put(ctx context.Context, in *PathInfo, opts ...grpc.CallOption) (*PathInfo, error) {
+	out := new(PathInfo)
+	err := c.cc.Invoke(ctx, PathInfoService_Put_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *pathInfoServiceClient) CalculateNAR(ctx context.Context, in *castore_go.Node, opts ...grpc.CallOption) (*CalculateNARResponse, error) {
+	out := new(CalculateNARResponse)
+	err := c.cc.Invoke(ctx, PathInfoService_CalculateNAR_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *pathInfoServiceClient) List(ctx context.Context, in *ListPathInfoRequest, opts ...grpc.CallOption) (PathInfoService_ListClient, error) {
+	stream, err := c.cc.NewStream(ctx, &PathInfoService_ServiceDesc.Streams[0], PathInfoService_List_FullMethodName, opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &pathInfoServiceListClient{stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+type PathInfoService_ListClient interface {
+	Recv() (*PathInfo, error)
+	grpc.ClientStream
+}
+
+type pathInfoServiceListClient struct {
+	grpc.ClientStream
+}
+
+func (x *pathInfoServiceListClient) Recv() (*PathInfo, error) {
+	m := new(PathInfo)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// PathInfoServiceServer is the server API for PathInfoService service.
+// All implementations must embed UnimplementedPathInfoServiceServer
+// for forward compatibility
+type PathInfoServiceServer interface {
+	// Return a PathInfo message matching the criteria specified in the
+	// GetPathInfoRequest message.
+	Get(context.Context, *GetPathInfoRequest) (*PathInfo, error)
+	// Upload a PathInfo object to the remote end. It MUST not return until the
+	// PathInfo object has been written on the the remote end.
+	//
+	// The remote end MAY check if a potential DirectoryNode has already been
+	// uploaded.
+	//
+	// Uploading clients SHOULD obviously not steer other machines to try to
+	// substitute before from the remote end before having finished uploading
+	// PathInfo, Directories and Blobs.
+	// The returned PathInfo object MAY contain additional narinfo signatures, but
+	// is otherwise left untouched.
+	Put(context.Context, *PathInfo) (*PathInfo, error)
+	// Calculate the NAR representation of the contents specified by the
+	// root_node. The calculation SHOULD be cached server-side for subsequent
+	// requests.
+	//
+	// All references (to blobs or Directory messages) MUST already exist in the
+	// store.
+	//
+	// The method can be used to produce a Nix fixed-output path, which contains
+	// the (compressed) sha256 of the NAR content representation in the root_node
+	// name (suffixed with the name).
+	//
+	// It can also be used to calculate arbitrary NAR hashes of output paths, in
+	// case a legacy Nix Binary Cache frontend is provided.
+	CalculateNAR(context.Context, *castore_go.Node) (*CalculateNARResponse, error)
+	// Return a stream of PathInfo messages matching the criteria specified in
+	// ListPathInfoRequest.
+	List(*ListPathInfoRequest, PathInfoService_ListServer) error
+	mustEmbedUnimplementedPathInfoServiceServer()
+}
+
+// UnimplementedPathInfoServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedPathInfoServiceServer struct {
+}
+
+func (UnimplementedPathInfoServiceServer) Get(context.Context, *GetPathInfoRequest) (*PathInfo, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
+}
+func (UnimplementedPathInfoServiceServer) Put(context.Context, *PathInfo) (*PathInfo, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Put not implemented")
+}
+func (UnimplementedPathInfoServiceServer) CalculateNAR(context.Context, *castore_go.Node) (*CalculateNARResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CalculateNAR not implemented")
+}
+func (UnimplementedPathInfoServiceServer) List(*ListPathInfoRequest, PathInfoService_ListServer) error {
+	return status.Errorf(codes.Unimplemented, "method List not implemented")
+}
+func (UnimplementedPathInfoServiceServer) mustEmbedUnimplementedPathInfoServiceServer() {}
+
+// UnsafePathInfoServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to PathInfoServiceServer will
+// result in compilation errors.
+type UnsafePathInfoServiceServer interface {
+	mustEmbedUnimplementedPathInfoServiceServer()
+}
+
+func RegisterPathInfoServiceServer(s grpc.ServiceRegistrar, srv PathInfoServiceServer) {
+	s.RegisterService(&PathInfoService_ServiceDesc, srv)
+}
+
+func _PathInfoService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetPathInfoRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(PathInfoServiceServer).Get(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: PathInfoService_Get_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(PathInfoServiceServer).Get(ctx, req.(*GetPathInfoRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _PathInfoService_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(PathInfo)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(PathInfoServiceServer).Put(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: PathInfoService_Put_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(PathInfoServiceServer).Put(ctx, req.(*PathInfo))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _PathInfoService_CalculateNAR_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(castore_go.Node)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(PathInfoServiceServer).CalculateNAR(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: PathInfoService_CalculateNAR_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(PathInfoServiceServer).CalculateNAR(ctx, req.(*castore_go.Node))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _PathInfoService_List_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(ListPathInfoRequest)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(PathInfoServiceServer).List(m, &pathInfoServiceListServer{stream})
+}
+
+type PathInfoService_ListServer interface {
+	Send(*PathInfo) error
+	grpc.ServerStream
+}
+
+type pathInfoServiceListServer struct {
+	grpc.ServerStream
+}
+
+func (x *pathInfoServiceListServer) Send(m *PathInfo) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+// PathInfoService_ServiceDesc is the grpc.ServiceDesc for PathInfoService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var PathInfoService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "tvix.store.v1.PathInfoService",
+	HandlerType: (*PathInfoServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Get",
+			Handler:    _PathInfoService_Get_Handler,
+		},
+		{
+			MethodName: "Put",
+			Handler:    _PathInfoService_Put_Handler,
+		},
+		{
+			MethodName: "CalculateNAR",
+			Handler:    _PathInfoService_CalculateNAR_Handler,
+		},
+	},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "List",
+			Handler:       _PathInfoService_List_Handler,
+			ServerStreams: true,
+		},
+	},
+	Metadata: "tvix/store/protos/rpc_pathinfo.proto",
+}
diff --git a/tvix/store-go/testdata/emptydirectory.nar b/tvix/store-go/testdata/emptydirectory.nar
new file mode 100644
index 0000000000..baba558622
--- /dev/null
+++ b/tvix/store-go/testdata/emptydirectory.nar
Binary files differdiff --git a/tvix/store-go/testdata/onebyteregular.nar b/tvix/store-go/testdata/onebyteregular.nar
new file mode 100644
index 0000000000..b8c94932bf
--- /dev/null
+++ b/tvix/store-go/testdata/onebyteregular.nar
Binary files differdiff --git a/tvix/store-go/testdata/symlink.nar b/tvix/store-go/testdata/symlink.nar
new file mode 100644
index 0000000000..7990e4ad5b
--- /dev/null
+++ b/tvix/store-go/testdata/symlink.nar
Binary files differdiff --git a/tvix/store/Cargo.toml b/tvix/store/Cargo.toml
new file mode 100644
index 0000000000..4c0e9be49a
--- /dev/null
+++ b/tvix/store/Cargo.toml
@@ -0,0 +1,80 @@
+[package]
+name = "tvix-store"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.68"
+async-stream = "0.3.5"
+blake3 = { version = "1.3.1", features = ["rayon", "std"] }
+bstr = "1.6.0"
+bytes = "1.4.0"
+clap = { version = "4.0", features = ["derive", "env"] }
+count-write = "0.1.0"
+data-encoding = "2.3.3"
+futures = "0.3.30"
+lazy_static = "1.4.0"
+nix-compat = { path = "../nix-compat", features = ["async"] }
+pin-project-lite = "0.2.13"
+prost = "0.12.1"
+opentelemetry = { version = "0.21.0", optional = true}
+opentelemetry-otlp = { version = "0.14.0", optional = true }
+opentelemetry_sdk = { version = "0.21.0", features = ["rt-tokio"], optional = true}
+serde = { version = "1.0.197", features = [ "derive" ] }
+serde_json = "1.0"
+serde_with = "3.7.0"
+serde_qs = "0.12.0"
+sha2 = "0.10.6"
+sled = { version = "0.34.7" }
+thiserror = "1.0.38"
+tokio = { version = "1.32.0", features = ["fs", "macros", "net", "rt", "rt-multi-thread", "signal"] }
+tokio-listener = { version = "0.3.2", features = [ "tonic011" ] }
+tokio-stream = { version = "0.1.14", features = ["fs"] }
+tokio-util = { version = "0.7.9", features = ["io", "io-util", "compat"] }
+tonic = { version = "0.11.0", features = ["tls", "tls-roots"] }
+tower = "0.4.13"
+tracing = "0.1.37"
+tracing-opentelemetry = "0.22.0"
+tracing-subscriber = { version = "0.3.16", features = ["env-filter", "json"] }
+tvix-castore = { path = "../castore" }
+url = "2.4.0"
+walkdir = "2.4.0"
+async-recursion = "1.0.5"
+reqwest = { version = "0.11.22", features = ["rustls-tls-native-roots", "stream"], default-features = false }
+xz2 = "0.1.7"
+
+[dependencies.tonic-reflection]
+optional = true
+version = "0.11.0"
+
+[dependencies.bigtable_rs]
+optional = true
+# https://github.com/liufuyang/bigtable_rs/pull/72
+git = "https://github.com/flokli/bigtable_rs"
+rev = "0af404741dfc40eb9fa99cf4d4140a09c5c20df7"
+
+[build-dependencies]
+prost-build = "0.12.1"
+tonic-build = "0.11.0"
+
+[dev-dependencies]
+async-process = "2.1.0"
+rstest = "0.19.0"
+rstest_reuse = "0.6.0"
+tempfile = "3.3.0"
+tokio-retry = "0.3.0"
+
+[features]
+default = ["cloud", "fuse", "otlp", "tonic-reflection"]
+cloud = [
+  "dep:bigtable_rs",
+  "tvix-castore/cloud"
+]
+fuse = ["tvix-castore/fuse"]
+otlp = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry_sdk"]
+tonic-reflection = ["dep:tonic-reflection", "tvix-castore/tonic-reflection"]
+virtiofs = ["tvix-castore/virtiofs"]
+# Whether to run the integration tests.
+# Requires the following packages in $PATH:
+# cbtemulator, google-cloud-bigtable-tool
+integration = []
diff --git a/tvix/store/README.md b/tvix/store/README.md
new file mode 100644
index 0000000000..a9d29671d8
--- /dev/null
+++ b/tvix/store/README.md
@@ -0,0 +1,63 @@
+# //tvix/store
+
+This contains the code hosting the tvix-store.
+
+For the local store, Nix realizes files on the filesystem in `/nix/store` (and
+maintains some metadata in a SQLite database). For "remote stores", it
+communicates this metadata in NAR (Nix ARchive) and NARInfo format.
+
+Compared to the Nix model, `tvix-store` stores data on a much more granular
+level than that, which provides more deduplication possibilities, and more
+granular copying.
+
+However, enough information is preserved to still be able to render NAR and
+NARInfo when needed.
+
+## More Information
+The store consists out of two different gRPC services, `tvix.castore.v1` for
+the low-level content-addressed bits, and `tvix.store.v1` for the Nix and
+`StorePath`-specific bits.
+
+Check the `protos/` subfolder both here and in `castore` for the definition of
+the exact RPC methods and messages.
+
+## Interacting with the GRPC service manually
+The shell environment in `//tvix` provides `evans`, which is an interactive
+REPL-based gPRC client.
+
+You can use it to connect to a `tvix-store` and call the various RPC methods.
+
+```shell
+$ cargo run -- daemon &
+$ evans --host localhost --port 8000 -r repl
+  ______
+ |  ____|
+ | |__    __   __   __ _   _ __    ___
+ |  __|   \ \ / /  / _. | | '_ \  / __|
+ | |____   \ V /  | (_| | | | | | \__ \
+ |______|   \_/    \__,_| |_| |_| |___/
+
+ more expressive universal gRPC client
+
+
+localhost:8000> package tvix.castore.v1
+tvix.castore.v1@localhost:8000> service BlobService
+
+tvix.castore.v1.BlobService@localhost:8000> call Put --bytes-from-file
+data (TYPE_BYTES) => /run/current-system/system
+{
+  "digest": "KOM3/IHEx7YfInAnlJpAElYezq0Sxn9fRz7xuClwNfA="
+}
+
+tvix.castore.v1.BlobService@localhost:8000> call Read --bytes-as-base64
+digest (TYPE_BYTES) => KOM3/IHEx7YfInAnlJpAElYezq0Sxn9fRz7xuClwNfA=
+{
+  "data": "eDg2XzY0LWxpbnV4"
+}
+
+$ echo eDg2XzY0LWxpbnV4 | base64 -d
+x86_64-linux
+```
+
+Thanks to `tvix-store` providing gRPC Server Reflection (with `reflection`
+feature), you don't need to point `evans` to the `.proto` files.
diff --git a/tvix/store/build.rs b/tvix/store/build.rs
new file mode 100644
index 0000000000..809fa29578
--- /dev/null
+++ b/tvix/store/build.rs
@@ -0,0 +1,38 @@
+use std::io::Result;
+
+fn main() -> Result<()> {
+    #[allow(unused_mut)]
+    let mut builder = tonic_build::configure();
+
+    #[cfg(feature = "tonic-reflection")]
+    {
+        let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
+        let descriptor_path = out_dir.join("tvix.store.v1.bin");
+
+        builder = builder.file_descriptor_set_path(descriptor_path);
+    };
+
+    // https://github.com/hyperium/tonic/issues/908
+    let mut config = prost_build::Config::new();
+    config.bytes(["."]);
+    config.extern_path(".tvix.castore.v1", "::tvix_castore::proto");
+
+    builder
+        .build_server(true)
+        .build_client(true)
+        .emit_rerun_if_changed(false)
+        .compile_with_config(
+            config,
+            &[
+                "tvix/store/protos/pathinfo.proto",
+                "tvix/store/protos/rpc_pathinfo.proto",
+            ],
+            // If we are in running `cargo build` manually, using `../..` works fine,
+            // but in case we run inside a nix build, we need to instead point PROTO_ROOT
+            // to a sparseTree containing that structure.
+            &[match std::env::var_os("PROTO_ROOT") {
+                Some(proto_root) => proto_root.to_str().unwrap().to_owned(),
+                None => "../..".to_string(),
+            }],
+        )
+}
diff --git a/tvix/store/default.nix b/tvix/store/default.nix
new file mode 100644
index 0000000000..ad47994f24
--- /dev/null
+++ b/tvix/store/default.nix
@@ -0,0 +1,52 @@
+{ depot, pkgs, ... }:
+
+let
+  mkImportCheck = p: expectedPath: {
+    label = ":nix :import ${p} with tvix-store import";
+    needsOutput = true;
+    command = pkgs.writeShellScript "tvix-import-check" ''
+      export BLOB_SERVICE_ADDR=memory://
+      export DIRECTORY_SERVICE_ADDR=memory://
+      export PATH_INFO_SERVICE_ADDR=memory://
+      TVIX_STORE_OUTPUT=$(result/bin/tvix-store import ${p})
+      EXPECTED='${/* the vebatim expected Tvix output: */expectedPath}'
+
+      echo "tvix-store output: ''${TVIX_STORE_OUTPUT}"
+      if [ "$TVIX_STORE_OUTPUT" != "$EXPECTED" ]; then
+        echo "Correct would have been ''${EXPECTED}"
+        exit 1
+      fi
+
+      echo "Output was correct."
+    '';
+  };
+in
+
+(depot.tvix.crates.workspaceMembers.tvix-store.build.override {
+  runTests = true;
+  testPreRun = ''
+    export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
+  '';
+
+  # enable some optional features.
+  features = [ "default" "cloud" ]
+    # virtiofs feature currently fails to build on Darwin.
+    ++ pkgs.lib.optional pkgs.stdenv.isLinux "virtiofs";
+}).overrideAttrs (_: {
+  meta.ci.targets = [ "integration-tests" ];
+  meta.ci.extraSteps = {
+    import-docs = (mkImportCheck "tvix/store/docs" ./docs);
+  };
+  passthru.integration-tests = depot.tvix.crates.workspaceMembers.tvix-store.build.override {
+    runTests = true;
+    testPreRun = ''
+      export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
+      export PATH="$PATH:${pkgs.lib.makeBinPath [pkgs.cbtemulator pkgs.google-cloud-bigtable-tool]}"
+    '';
+
+    # enable some optional features.
+    features = [ "default" "cloud" "integration" ]
+      # virtiofs feature currently fails to build on Darwin.
+      ++ pkgs.lib.optional pkgs.stdenv.isLinux "virtiofs";
+  };
+})
diff --git a/tvix/store/docs/api.md b/tvix/store/docs/api.md
new file mode 100644
index 0000000000..c1dacc89a5
--- /dev/null
+++ b/tvix/store/docs/api.md
@@ -0,0 +1,288 @@
+tvix-[ca]store API
+==============
+
+This document outlines the design of the API exposed by tvix-castore and tvix-
+store, as well as other implementations of this store protocol.
+
+This document is meant to be read side-by-side with
+[castore.md](../../tvix-castore/docs/castore.md) which describes the data model
+in more detail.
+
+The store API has four main consumers:
+
+1. The evaluator (or more correctly, the CLI/coordinator, in the Tvix
+   case) communicates with the store to:
+
+   * Upload files and directories (e.g. from `builtins.path`, or `src = ./path`
+     Nix expressions).
+   * Read files from the store where necessary (e.g. when `nixpkgs` is
+     located in the store, or for IFD).
+
+2. The builder communicates with the store to:
+
+   * Upload files and directories after a build, to persist build artifacts in
+     the store.
+
+3. Tvix clients (such as users that have Tvix installed, or, depending
+   on perspective, builder environments) expect the store to
+   "materialise" on disk to provide a directory layout with store
+   paths.
+
+4. Stores may communicate with other stores, to substitute already built store
+   paths, i.e. a store acts as a binary cache for other stores.
+
+The store API attempts to reuse parts of its API between these three
+consumers by making similarities explicit in the protocol. This leads
+to a protocol that is slightly more complex than a simple "file
+upload/download"-system, but at significantly greater efficiency, both in terms
+of deduplication opportunities as well as granularity.
+
+## The Store model
+
+Contents inside a tvix-store can be grouped into three different message types:
+
+ * Blobs
+ * Directories
+ * PathInfo (see further down)
+
+(check `castore.md` for more detailed field descriptions)
+
+### Blobs
+A blob object contains the literal file contents of regular (or executable)
+files.
+
+### Directory
+A directory object describes the direct children of a directory.
+
+It contains:
+ - name of child (regular or executable) files, and their [blake3][blake3] hash.
+ - name of child symlinks, and their target (as string)
+ - name of child directories, and their [blake3][blake3] hash (forming a Merkle DAG)
+
+### Content-addressed Store Model
+For example, lets consider a directory layout like this, with some
+imaginary hashes of file contents:
+
+```
+.
+β”œβ”€β”€ file-1.txt        hash: 5891b5b522d5df086d0ff0b110fb
+└── nested
+    └── file-2.txt    hash: abc6fd595fc079d3114d4b71a4d8
+```
+
+A hash for the *directory* `nested` can be created by creating the `Directory`
+object:
+
+```json
+{
+  "directories": [],
+  "files": [{
+    "name": "file-2.txt",
+    "digest": "abc6fd595fc079d3114d4b71a4d8",
+    "size": 123,
+  }],
+  "symlink": [],
+}
+```
+
+And then hashing a serialised form of that data structure. We use the blake3
+hash of the canonical protobuf representation. Let's assume the hash was
+`ff0029485729bcde993720749232`.
+
+To create the directory object one layer up, we now refer to our `nested`
+directory object in `directories`, and to `file-1.txt` in `files`:
+
+```json
+{
+  "directories": [{
+    "name": "nested",
+    "digest": "ff0029485729bcde993720749232",
+    "size": 1,
+  }],
+  "files": [{
+    "name": "file-1.txt",
+    "digest": "5891b5b522d5df086d0ff0b110fb",
+    "size": 124,
+  }]
+}
+```
+
+This Merkle DAG of Directory objects, and flat store of blobs can be used to
+describe any file/directory/symlink inside a store path. Due to its content-
+addressed nature, it'll automatically deduplicate (re-)used (sub)directories,
+and allow substitution from any (untrusted) source.
+
+The thing that's now only missing is the metadata to map/"mount" from the
+content-addressed world to a physical path.
+
+### PathInfo
+As most paths in the Nix store currently are input-addressed [^input-addressed],
+and the `tvix-castore` data model is also not intrinsically using NAR hashes,
+we need something mapping from an input-addressed "output path hash" (or a Nix-
+specific content-addressed path) to the contents in the `tvix-castore` world.
+
+That's what `PathInfo` provides. It embeds the root node (Directory, File or
+Symlink) at a given store path.
+
+The root nodes' `name` field is populated with the (base)name inside
+`/nix/store`, so `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-pname-1.2.3`.
+
+The `PathInfo` message also stores references to other store paths, and some
+more NARInfo-specific metadata (signatures, narhash, narsize).
+
+
+## API overview
+
+There's three different services:
+
+### BlobService
+`BlobService` can be used to store and retrieve blobs of data, used to host
+regular file contents.
+
+It is content-addressed, using [blake3][blake3]
+as a hashing function.
+
+As blake3 is a tree hash, there's an opportunity to do
+[verified streaming][bao] of parts of the file,
+which doesn't need to trust any more information than the root hash itself.
+Future extensions of the `BlobService` protocol will enable this.
+
+### DirectoryService
+`DirectoryService` allows lookups (and uploads) of `Directory` messages, and
+whole reference graphs of them.
+
+
+### PathInfoService
+The PathInfo service provides lookups from a store path hash to a `PathInfo`
+message.
+
+## Example flows
+
+Below there are some common use cases of tvix-store, and how the different
+services are used.
+
+###  Upload files and directories
+This is needed for `builtins.path` or `src = ./path` in Nix expressions (A), as
+well as for uploading build artifacts to a store (B).
+
+The path specified needs to be (recursively, BFS-style) traversed.
+ * All file contents need to be hashed with blake3, and submitted to the
+   *BlobService* if not already present.
+   A reference to them needs to be added to the parent Directory object that's
+   constructed.
+ * All symlinks need to be added to the parent directory they reside in.
+ * Whenever a Directory has been fully traversed, it needs to be uploaded to
+   the *DirectoryService* and a reference to it needs to be added to the parent
+   Directory object.
+
+Most of the hashing / directory traversal/uploading can happen in parallel,
+as long as Directory objects only refer to Directory objects and Blobs that
+have already been uploaded.
+
+When reaching the root, a `PathInfo` object needs to be constructed.
+
+ * In the case of content-addressed paths (A), the name of the root node is
+   based on the NAR representation of the contents.
+   It might make sense to be able to offload the NAR calculation to the store,
+   which can cache it.
+ * In the case of build artifacts (B), the output path is input-addressed and
+   known upfront.
+
+Contrary to Nix, this has the advantage of not having to upload a lot of things
+to the store that didn't change.
+
+### Reading files from the store from the evaluator
+This is the case when `nixpkgs` is located in the store, or IFD in general.
+
+The store client asks the `PathInfoService` for the `PathInfo` of the output
+path in the request, and looks at the root node.
+
+If something other than the root of the store path is requested, like for
+example `maintainers/maintainer-list.nix`, the root_node Directory is inspected
+and potentially a chain of `Directory` objects requested from
+*DirectoryService*. [^n+1query].
+
+When the desired file is reached, the *BlobService* can be used to read the
+contents of this file, and return it back to the evaluator.
+
+FUTUREWORK: define how importing from symlinks should/does work.
+
+Contrary to Nix, this has the advantage of not having to copy all of the
+contents of a store path to the evaluating machine, but really only fetching
+the files the evaluator currently cares about.
+
+### Materializing store paths on disk
+This is useful for people running a Tvix-only system, or running builds on a
+"Tvix remote builder" in its own mount namespace.
+
+In a system with Nix installed, we can't simply manually "extract" things to
+`/nix/store`, as Nix assumes to own all writes to this location.
+In these use cases, we're probably better off exposing a tvix-store as a local
+binary cache (that's what `//tvix/nar-bridge` does).
+
+Assuming we are in an environment where we control `/nix/store` exclusively, a
+"realize to disk" would either "extract" things from the `tvix-store` to a
+filesystem, or expose a `FUSE`/`virtio-fs` filesystem.
+
+The latter is already implemented, and particularly interesting for (remote)
+build workloads, as build inputs can be realized on-demand, which saves copying
+around a lot of never- accessed files.
+
+In both cases, the API interactions are similar.
+ * The *PathInfoService* is asked for the `PathInfo` of the requested store path.
+ * If everything should be "extracted", the *DirectoryService* is asked for all
+   `Directory` objects in the closure, the file structure is created, all Blobs
+   are downloaded and placed in their corresponding location and all symlinks
+   are created accordingly.
+ * If this is a FUSE filesystem, we can decide to only request a subset,
+   similar to the "Reading files from the store from the evaluator" use case,
+   even though it might make sense to keep all Directory objects around.
+   (See the caveat in "Trust model" though!)
+
+### Stores communicating with other stores
+The gRPC API exposed by the tvix-store allows composing multiple stores, and
+implementing some caching strategies, that store clients don't need to be aware
+of.
+
+ * For example, a caching strategy could have a fast local tvix-store, that's
+   asked first and filled with data from a slower remote tvix-store.
+
+ * Multiple stores could be asked for the same data, and whatever store returns
+   the right data first wins.
+
+
+## Trust model / Distribution
+As already described above, the only non-content-addressed service is the
+`PathInfo` service.
+
+This means, all other messages (such as `Blob` and `Directory` messages) can be
+substituted from many different, untrusted sources/mirrors, which will make
+plugging in additional substitution strategies like IPFS, local network
+neighbors super simple. That's also why it's living in the `tvix-castore` crate.
+
+As for `PathInfo`, we don't specify an additional signature mechanism yet, but
+carry the NAR-based signatures from Nix along.
+
+This means, if we don't trust a remote `PathInfo` object, we currently need to
+"stream" the NAR representation to validate these signatures.
+
+However, the slow part is downloading of NAR files, and considering we have
+more granularity available, we might only need to download some small blobs,
+rather than a whole NAR file.
+
+A future signature mechanism, that is only signing (parts of) the `PathInfo`
+message, which only points to content-addressed data will enable verified
+partial access into a store path, opening up opportunities for lazy filesystem
+access etc.
+
+
+
+[blake3]: https://github.com/BLAKE3-team/BLAKE3
+[bao]: https://github.com/oconnor663/bao
+[^input-addressed]: Nix hashes the A-Term representation of a .drv, after doing
+                    some replacements on refered Input Derivations to calculate
+                    output paths.
+[^n+1query]: This would expose an N+1 query problem. However it's not a problem
+             in practice, as there's usually always a "local" caching store in
+             the loop, and *DirectoryService* supports a recursive lookup for
+             all `Directory` children of a `Directory`
diff --git a/tvix/store/protos/LICENSE b/tvix/store/protos/LICENSE
new file mode 100644
index 0000000000..2034ada6fd
--- /dev/null
+++ b/tvix/store/protos/LICENSE
@@ -0,0 +1,21 @@
+Copyright Β© The Tvix Authors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+β€œSoftware”), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/tvix/store/protos/default.nix b/tvix/store/protos/default.nix
new file mode 100644
index 0000000000..56345d9338
--- /dev/null
+++ b/tvix/store/protos/default.nix
@@ -0,0 +1,55 @@
+{ depot, pkgs, ... }:
+let
+  protos = depot.nix.sparseTree {
+    name = "store-protos";
+    root = depot.path.origSrc;
+    paths = [
+      # We need to include castore.proto (only), as it's referred.
+      ../../castore/protos/castore.proto
+      ./pathinfo.proto
+      ./rpc_pathinfo.proto
+      ../../../buf.yaml
+      ../../../buf.gen.yaml
+    ];
+  };
+in
+depot.nix.readTree.drvTargets {
+  inherit protos;
+
+  # Lints and ensures formatting of the proto files.
+  check = pkgs.stdenv.mkDerivation {
+    name = "proto-check";
+    src = protos;
+
+    nativeBuildInputs = [
+      pkgs.buf
+    ];
+
+    buildPhase = ''
+      export HOME=$TMPDIR
+      buf lint
+      buf format -d --exit-code
+      touch $out
+    '';
+  };
+
+  # Produces the golang bindings.
+  go-bindings = pkgs.stdenv.mkDerivation {
+    name = "go-bindings";
+    src = protos;
+
+    nativeBuildInputs = [
+      pkgs.buf
+      pkgs.protoc-gen-go
+      pkgs.protoc-gen-go-grpc
+    ];
+
+    buildPhase = ''
+      export HOME=$TMPDIR
+      buf generate
+
+      mkdir -p $out
+      cp tvix/store/protos/*.pb.go $out/
+    '';
+  };
+}
diff --git a/tvix/store/protos/pathinfo.proto b/tvix/store/protos/pathinfo.proto
new file mode 100644
index 0000000000..b03e7e938e
--- /dev/null
+++ b/tvix/store/protos/pathinfo.proto
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+syntax = "proto3";
+
+package tvix.store.v1;
+
+import "tvix/castore/protos/castore.proto";
+
+option go_package = "code.tvl.fyi/tvix/store-go;storev1";
+
+// PathInfo shows information about a Nix Store Path.
+// That's a single element inside /nix/store.
+message PathInfo {
+  // The path can be a directory, file or symlink.
+  tvix.castore.v1.Node node = 1;
+
+  // List of references (output path hashes)
+  // This really is the raw *bytes*, after decoding nixbase32, and not a
+  // base32-encoded string.
+  repeated bytes references = 2;
+
+  // see below.
+  NARInfo narinfo = 3;
+}
+
+// Represents a path in the Nix store (a direct child of STORE_DIR).
+// It is commonly formatted by a nixbase32-encoding the digest, and
+// concatenating the name, separated by a `-`.
+message StorePath {
+  // The string after digest and `-`.
+  string name = 1;
+
+  // The digest (20 bytes).
+  bytes digest = 2;
+}
+
+// Nix C++ uses NAR (Nix Archive) as a format to transfer store paths,
+// and stores metadata and signatures in NARInfo files.
+// Store all these attributes in a separate message.
+//
+// This is useful to render .narinfo files to clients, or to preserve/validate
+// these signatures.
+// As verifying these signatures requires the whole NAR file to be synthesized,
+// moving to another signature scheme is desired.
+// Even then, it still makes sense to hold this data, for old clients.
+message NARInfo {
+  // This represents a (parsed) signature line in a .narinfo file.
+  message Signature {
+    string name = 1;
+    bytes data = 2;
+  }
+
+  // This size of the NAR file, in bytes.
+  uint64 nar_size = 1;
+
+  // The sha256 of the NAR file representation.
+  bytes nar_sha256 = 2;
+
+  // The signatures in a .narinfo file.
+  repeated Signature signatures = 3;
+
+  // A list of references. To validate .narinfo signatures, a fingerprint needs
+  // to be constructed.
+  // This fingerprint doesn't just contain the hashes of the output paths of all
+  // references (like PathInfo.references), but their whole (base)names, so we
+  // need to keep them somewhere.
+  repeated string reference_names = 4;
+
+  // The StorePath of the .drv file producing this output.
+  // The .drv suffix is omitted in its `name` field.
+  StorePath deriver = 5;
+
+  // The CA field in the .narinfo.
+  // Its textual representations seen in the wild are one of the following:
+  //  - `fixed:r:sha256:1gcky5hlf5vqfzpyhihydmm54grhc94mcs8w7xr8613qsqb1v2j6`
+  //    fixed-output derivations using "recursive" `outputHashMode`.
+  //  - `fixed:sha256:19xqkh72crbcba7flwxyi3n293vav6d7qkzkh2v4zfyi4iia8vj8
+  //    fixed-output derivations using "flat" `outputHashMode`
+  //  - `text:sha256:19xqkh72crbcba7flwxyi3n293vav6d7qkzkh2v4zfyi4iia8vj8`
+  //    Text hashing, used for uploaded .drv files and outputs produced by
+  //    builtins.toFile.
+  //
+  // Semantically, they can be split into the following components:
+  //  - "content address prefix". Currently, "fixed" and "text" are supported.
+  //  - "hash mode". Currently, "flat" and "recursive" are supported.
+  //  - "hash type". The underlying hash function used.
+  //    Currently, sha1, md5, sha256, sha512.
+  //  - "digest". The digest itself.
+  //
+  // There are some restrictions on the possible combinations.
+  // For example, `text` and `fixed:recursive` always imply sha256.
+  //
+  // We use an enum to encode the possible combinations, and optimize for the
+  // common case, `fixed:recursive`, identified as `NAR_SHA256`.
+  CA ca = 6;
+
+  message CA {
+    enum Hash {
+      // produced when uploading fixed-output store paths using NAR-based
+      // hashing (`outputHashMode = "recursive"`).
+      NAR_SHA256 = 0;
+      NAR_SHA1 = 1;
+      NAR_SHA512 = 2;
+      NAR_MD5 = 3;
+
+      // Produced when uploading .drv files or outputs produced by
+      // builtins.toFile.
+      // Produces equivalent digests as FLAT_SHA256, but is a separate
+      // hashing type in Nix, affecting output path calculation.
+      TEXT_SHA256 = 4;
+
+      // Produced when using fixed-output derivations with
+      // `outputHashMode = "flat"`.
+      FLAT_SHA1 = 5;
+      FLAT_MD5 = 6;
+      FLAT_SHA256 = 7;
+      FLAT_SHA512 = 8;
+
+      // TODO: what happens in Rust if we introduce a new enum kind here?
+    }
+
+    // The hashing type used.
+    Hash type = 1;
+
+    // The digest, in raw bytes.
+    bytes digest = 2;
+  }
+}
diff --git a/tvix/store/protos/rpc_pathinfo.proto b/tvix/store/protos/rpc_pathinfo.proto
new file mode 100644
index 0000000000..c1c91658ad
--- /dev/null
+++ b/tvix/store/protos/rpc_pathinfo.proto
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: MIT
+// Copyright Β© 2022 The Tvix Authors
+syntax = "proto3";
+
+package tvix.store.v1;
+
+import "tvix/castore/protos/castore.proto";
+import "tvix/store/protos/pathinfo.proto";
+
+option go_package = "code.tvl.fyi/tvix/store-go;storev1";
+
+service PathInfoService {
+  // Return a PathInfo message matching the criteria specified in the
+  // GetPathInfoRequest message.
+  rpc Get(GetPathInfoRequest) returns (PathInfo);
+
+  // Upload a PathInfo object to the remote end. It MUST not return until the
+  // PathInfo object has been written on the the remote end.
+  //
+  // The remote end MAY check if a potential DirectoryNode has already been
+  // uploaded.
+  //
+  // Uploading clients SHOULD obviously not steer other machines to try to
+  // substitute before from the remote end before having finished uploading
+  // PathInfo, Directories and Blobs.
+  // The returned PathInfo object MAY contain additional narinfo signatures, but
+  // is otherwise left untouched.
+  rpc Put(PathInfo) returns (PathInfo);
+
+  // Calculate the NAR representation of the contents specified by the
+  // root_node. The calculation SHOULD be cached server-side for subsequent
+  // requests.
+  //
+  // All references (to blobs or Directory messages) MUST already exist in the
+  // store.
+  //
+  // The method can be used to produce a Nix fixed-output path, which contains
+  // the (compressed) sha256 of the NAR content representation in the root_node
+  // name (suffixed with the name).
+  //
+  // It can also be used to calculate arbitrary NAR hashes of output paths, in
+  // case a legacy Nix Binary Cache frontend is provided.
+  rpc CalculateNAR(tvix.castore.v1.Node) returns (CalculateNARResponse);
+
+  // Return a stream of PathInfo messages matching the criteria specified in
+  // ListPathInfoRequest.
+  rpc List(ListPathInfoRequest) returns (stream PathInfo);
+}
+
+// The parameters that can be used to lookup a (single) PathInfo object.
+// Currently, only a lookup by output hash is supported.
+message GetPathInfoRequest {
+  oneof by_what {
+    // The output hash of a nix path (20 bytes).
+    // This is the nixbase32-decoded portion of a Nix output path, so to substitute
+    // /nix/store/xm35nga2g20mz5sm5l6n8v3bdm86yj83-cowsay-3.04
+    // this field would contain nixbase32dec("xm35nga2g20mz5sm5l6n8v3bdm86yj83").
+    bytes by_output_hash = 1;
+  }
+}
+
+// The parameters that can be used to lookup (multiple) PathInfo objects.
+// Currently no filtering is possible, all objects are returned.
+message ListPathInfoRequest {}
+
+// CalculateNARResponse is the response returned by the CalculateNAR request.
+//
+// It contains the size of the NAR representation (in bytes), and the sha56
+// digest.
+message CalculateNARResponse {
+  // This size of the NAR file, in bytes.
+  uint64 nar_size = 1;
+
+  // The sha256 of the NAR file representation.
+  bytes nar_sha256 = 2;
+}
diff --git a/tvix/store/src/bin/tvix-store.rs b/tvix/store/src/bin/tvix-store.rs
new file mode 100644
index 0000000000..fa30501e78
--- /dev/null
+++ b/tvix/store/src/bin/tvix-store.rs
@@ -0,0 +1,563 @@
+use clap::Parser;
+use clap::Subcommand;
+
+use futures::future::try_join_all;
+use futures::StreamExt;
+use futures::TryStreamExt;
+use nix_compat::path_info::ExportedPathInfo;
+use serde::Deserialize;
+use serde::Serialize;
+use std::path::PathBuf;
+use std::sync::Arc;
+use tokio_listener::Listener;
+use tokio_listener::SystemOptions;
+use tokio_listener::UserOptions;
+use tonic::transport::Server;
+use tracing::info;
+use tracing::Level;
+use tracing_subscriber::EnvFilter;
+use tracing_subscriber::Layer;
+use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
+use tvix_castore::import::fs::ingest_path;
+use tvix_store::proto::NarInfo;
+use tvix_store::proto::PathInfo;
+
+use tvix_castore::proto::blob_service_server::BlobServiceServer;
+use tvix_castore::proto::directory_service_server::DirectoryServiceServer;
+use tvix_castore::proto::GRPCBlobServiceWrapper;
+use tvix_castore::proto::GRPCDirectoryServiceWrapper;
+use tvix_store::pathinfoservice::PathInfoService;
+use tvix_store::proto::path_info_service_server::PathInfoServiceServer;
+use tvix_store::proto::GRPCPathInfoServiceWrapper;
+
+#[cfg(any(feature = "fuse", feature = "virtiofs"))]
+use tvix_store::pathinfoservice::make_fs;
+
+#[cfg(feature = "fuse")]
+use tvix_castore::fs::fuse::FuseDaemon;
+
+#[cfg(feature = "otlp")]
+use opentelemetry::KeyValue;
+#[cfg(feature = "otlp")]
+use opentelemetry_sdk::{
+    resource::{ResourceDetector, SdkProvidedResourceDetector},
+    trace::BatchConfig,
+    Resource,
+};
+
+#[cfg(feature = "virtiofs")]
+use tvix_castore::fs::virtiofs::start_virtiofs_daemon;
+
+#[cfg(feature = "tonic-reflection")]
+use tvix_castore::proto::FILE_DESCRIPTOR_SET as CASTORE_FILE_DESCRIPTOR_SET;
+#[cfg(feature = "tonic-reflection")]
+use tvix_store::proto::FILE_DESCRIPTOR_SET;
+
+#[derive(Parser)]
+#[command(author, version, about, long_about = None)]
+struct Cli {
+    /// Whether to configure OTLP. Set --otlp=false to disable.
+    #[arg(long, default_missing_value = "true", default_value = "true", num_args(0..=1), require_equals(true), action(clap::ArgAction::Set))]
+    otlp: bool,
+
+    /// A global log level to use when printing logs.
+    /// It's also possible to set `RUST_LOG` according to
+    /// `tracing_subscriber::filter::EnvFilter`, which will always have
+    /// priority.
+    #[arg(long)]
+    log_level: Option<Level>,
+
+    #[command(subcommand)]
+    command: Commands,
+}
+
+#[derive(Subcommand)]
+enum Commands {
+    /// Runs the tvix-store daemon.
+    Daemon {
+        #[arg(long, short = 'l')]
+        listen_address: Option<String>,
+
+        #[arg(
+            long,
+            env,
+            default_value = "objectstore+file:///var/lib/tvix-store/blobs.object_store"
+        )]
+        blob_service_addr: String,
+
+        #[arg(
+            long,
+            env,
+            default_value = "sled:///var/lib/tvix-store/directories.sled"
+        )]
+        directory_service_addr: String,
+
+        #[arg(long, env, default_value = "sled:///var/lib/tvix-store/pathinfo.sled")]
+        path_info_service_addr: String,
+    },
+    /// Imports a list of paths into the store, print the store path for each of them.
+    Import {
+        #[clap(value_name = "PATH")]
+        paths: Vec<PathBuf>,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        blob_service_addr: String,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        directory_service_addr: String,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        path_info_service_addr: String,
+    },
+
+    /// Copies a list of store paths on the system into tvix-store.
+    Copy {
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        blob_service_addr: String,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        directory_service_addr: String,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        path_info_service_addr: String,
+
+        /// A path pointing to a JSON file produced by the Nix
+        /// `__structuredAttrs` containing reference graph information provided
+        /// by the `exportReferencesGraph` feature.
+        ///
+        /// This can be used to invoke tvix-store inside a Nix derivation
+        /// copying to a Tvix store (or outside, if the JSON file is copied
+        /// out).
+        ///
+        /// Currently limited to the `closure` key inside that JSON file.
+        #[arg(value_name = "NIX_ATTRS_JSON_FILE", env = "NIX_ATTRS_JSON_FILE")]
+        reference_graph_path: PathBuf,
+    },
+    /// Mounts a tvix-store at the given mountpoint
+    #[cfg(feature = "fuse")]
+    Mount {
+        #[clap(value_name = "PATH")]
+        dest: PathBuf,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        blob_service_addr: String,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        directory_service_addr: String,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        path_info_service_addr: String,
+
+        /// Number of FUSE threads to spawn.
+        #[arg(long, env, default_value_t = default_threads())]
+        threads: usize,
+
+        #[arg(long, env, default_value_t = false)]
+        /// Whether to configure the mountpoint with allow_other.
+        /// Requires /etc/fuse.conf to contain the `user_allow_other`
+        /// option, configured via `programs.fuse.userAllowOther` on NixOS.
+        allow_other: bool,
+
+        /// Whether to list elements at the root of the mount point.
+        /// This is useful if your PathInfoService doesn't provide an
+        /// (exhaustive) listing.
+        #[clap(long, short, action)]
+        list_root: bool,
+
+        #[arg(long, default_value_t = true)]
+        /// Whether to expose blob and directory digests as extended attributes.
+        show_xattr: bool,
+    },
+    /// Starts a tvix-store virtiofs daemon at the given socket path.
+    #[cfg(feature = "virtiofs")]
+    #[command(name = "virtiofs")]
+    VirtioFs {
+        #[clap(value_name = "PATH")]
+        socket: PathBuf,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        blob_service_addr: String,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        directory_service_addr: String,
+
+        #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
+        path_info_service_addr: String,
+
+        /// Whether to list elements at the root of the mount point.
+        /// This is useful if your PathInfoService doesn't provide an
+        /// (exhaustive) listing.
+        #[clap(long, short, action)]
+        list_root: bool,
+
+        #[arg(long, default_value_t = true)]
+        /// Whether to expose blob and directory digests as extended attributes.
+        show_xattr: bool,
+    },
+}
+
+#[cfg(all(feature = "fuse", not(target_os = "macos")))]
+fn default_threads() -> usize {
+    std::thread::available_parallelism()
+        .map(|threads| threads.into())
+        .unwrap_or(4)
+}
+// On MacFUSE only a single channel will receive ENODEV when the file system is
+// unmounted and so all the other channels will block forever.
+// See https://github.com/osxfuse/osxfuse/issues/974
+#[cfg(all(feature = "fuse", target_os = "macos"))]
+fn default_threads() -> usize {
+    1
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let cli = Cli::parse();
+
+    // configure log settings
+    let level = cli.log_level.unwrap_or(Level::INFO);
+
+    // Set up the tracing subscriber.
+    let subscriber = tracing_subscriber::registry().with(
+        tracing_subscriber::fmt::Layer::new()
+            .with_writer(std::io::stderr)
+            .compact()
+            .with_filter(
+                EnvFilter::builder()
+                    .with_default_directive(level.into())
+                    .from_env()
+                    .expect("invalid RUST_LOG"),
+            ),
+    );
+
+    // Add the otlp layer (when otlp is enabled, and it's not disabled in the CLI)
+    // then init the registry.
+    // If the feature is feature-flagged out, just init without adding the layer.
+    // It's necessary to do this separately, as every with() call chains the
+    // layer into the type of the registry.
+    #[cfg(feature = "otlp")]
+    {
+        let subscriber = if cli.otlp {
+            let tracer = opentelemetry_otlp::new_pipeline()
+                .tracing()
+                .with_exporter(opentelemetry_otlp::new_exporter().tonic())
+                .with_batch_config(BatchConfig::default())
+                .with_trace_config(opentelemetry_sdk::trace::config().with_resource({
+                    // use SdkProvidedResourceDetector.detect to detect resources,
+                    // but replace the default service name with our default.
+                    // https://github.com/open-telemetry/opentelemetry-rust/issues/1298
+                    let resources =
+                        SdkProvidedResourceDetector.detect(std::time::Duration::from_secs(0));
+                    // SdkProvidedResourceDetector currently always sets
+                    // `service.name`, but we don't like its default.
+                    if resources.get("service.name".into()).unwrap() == "unknown_service".into() {
+                        resources.merge(&Resource::new([KeyValue::new(
+                            "service.name",
+                            "tvix.store",
+                        )]))
+                    } else {
+                        resources
+                    }
+                }))
+                .install_batch(opentelemetry_sdk::runtime::Tokio)?;
+
+            // Create a tracing layer with the configured tracer
+            let layer = tracing_opentelemetry::layer().with_tracer(tracer);
+
+            subscriber.with(Some(layer))
+        } else {
+            subscriber.with(None)
+        };
+
+        subscriber.try_init()?;
+    }
+
+    // Init the registry (when otlp is not enabled)
+    #[cfg(not(feature = "otlp"))]
+    {
+        subscriber.try_init()?;
+    }
+
+    match cli.command {
+        Commands::Daemon {
+            listen_address,
+            blob_service_addr,
+            directory_service_addr,
+            path_info_service_addr,
+        } => {
+            // initialize stores
+            let (blob_service, directory_service, path_info_service) =
+                tvix_store::utils::construct_services(
+                    blob_service_addr,
+                    directory_service_addr,
+                    path_info_service_addr,
+                )
+                .await?;
+
+            let listen_address = listen_address
+                .unwrap_or_else(|| "[::]:8000".to_string())
+                .parse()
+                .unwrap();
+
+            let mut server = Server::builder();
+
+            #[allow(unused_mut)]
+            let mut router = server
+                .add_service(BlobServiceServer::new(GRPCBlobServiceWrapper::new(
+                    blob_service,
+                )))
+                .add_service(DirectoryServiceServer::new(
+                    GRPCDirectoryServiceWrapper::new(directory_service),
+                ))
+                .add_service(PathInfoServiceServer::new(GRPCPathInfoServiceWrapper::new(
+                    Arc::from(path_info_service),
+                )));
+
+            #[cfg(feature = "tonic-reflection")]
+            {
+                let reflection_svc = tonic_reflection::server::Builder::configure()
+                    .register_encoded_file_descriptor_set(CASTORE_FILE_DESCRIPTOR_SET)
+                    .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
+                    .build()?;
+                router = router.add_service(reflection_svc);
+            }
+
+            info!(listen_address=%listen_address, "starting daemon");
+
+            let listener = Listener::bind(
+                &listen_address,
+                &SystemOptions::default(),
+                &UserOptions::default(),
+            )
+            .await?;
+
+            router.serve_with_incoming(listener).await?;
+        }
+        Commands::Import {
+            paths,
+            blob_service_addr,
+            directory_service_addr,
+            path_info_service_addr,
+        } => {
+            // FUTUREWORK: allow flat for single files?
+            let (blob_service, directory_service, path_info_service) =
+                tvix_store::utils::construct_services(
+                    blob_service_addr,
+                    directory_service_addr,
+                    path_info_service_addr,
+                )
+                .await?;
+
+            // Arc the PathInfoService, as we clone it .
+            let path_info_service: Arc<dyn PathInfoService> = path_info_service.into();
+
+            let tasks = paths
+                .into_iter()
+                .map(|path| {
+                    tokio::task::spawn({
+                        let blob_service = blob_service.clone();
+                        let directory_service = directory_service.clone();
+                        let path_info_service = path_info_service.clone();
+
+                        async move {
+                            if let Ok(name) = tvix_store::import::path_to_name(&path) {
+                                let resp = tvix_store::import::import_path_as_nar_ca(
+                                    &path,
+                                    name,
+                                    blob_service,
+                                    directory_service,
+                                    path_info_service,
+                                )
+                                .await;
+                                if let Ok(output_path) = resp {
+                                    // If the import was successful, print the path to stdout.
+                                    println!("{}", output_path.to_absolute_path());
+                                }
+                            }
+                        }
+                    })
+                })
+                .collect::<Vec<_>>();
+
+            try_join_all(tasks).await?;
+        }
+        Commands::Copy {
+            blob_service_addr,
+            directory_service_addr,
+            path_info_service_addr,
+            reference_graph_path,
+        } => {
+            let (blob_service, directory_service, path_info_service) =
+                tvix_store::utils::construct_services(
+                    blob_service_addr,
+                    directory_service_addr,
+                    path_info_service_addr,
+                )
+                .await?;
+
+            // Parse the file at reference_graph_path.
+            let reference_graph_json = tokio::fs::read(&reference_graph_path).await?;
+
+            #[derive(Deserialize, Serialize)]
+            struct ReferenceGraph<'a> {
+                #[serde(borrow)]
+                closure: Vec<ExportedPathInfo<'a>>,
+            }
+
+            let reference_graph: ReferenceGraph<'_> =
+                serde_json::from_slice(reference_graph_json.as_slice())?;
+
+            // Arc the PathInfoService, as we clone it .
+            let path_info_service: Arc<dyn PathInfoService> = path_info_service.into();
+
+            // From our reference graph, lookup all pathinfos that might exist.
+            let elems: Vec<_> = futures::stream::iter(reference_graph.closure)
+                .map(|elem| {
+                    let path_info_service = path_info_service.clone();
+                    async move {
+                        path_info_service
+                            .get(*elem.path.digest())
+                            .await
+                            .map(|resp| (elem, resp))
+                    }
+                })
+                .buffer_unordered(50)
+                // Filter out all that are already uploaded.
+                // TODO: check if there's a better combinator for this
+                .try_filter_map(|(elem, path_info)| {
+                    std::future::ready(if path_info.is_none() {
+                        Ok(Some(elem))
+                    } else {
+                        Ok(None)
+                    })
+                })
+                .try_collect()
+                .await?;
+
+            // Run ingest_path on all of them.
+            let uploads: Vec<_> = futures::stream::iter(elems)
+                .map(|elem| {
+                    // Map to a future returning the root node, alongside with the closure info.
+                    let blob_service = blob_service.clone();
+                    let directory_service = directory_service.clone();
+                    async move {
+                        // Ingest the given path.
+
+                        ingest_path(
+                            blob_service,
+                            directory_service,
+                            PathBuf::from(elem.path.to_absolute_path()),
+                        )
+                        .await
+                        .map(|root_node| (elem, root_node))
+                    }
+                })
+                .buffer_unordered(10)
+                .try_collect()
+                .await?;
+
+            // Insert them into the PathInfoService.
+            // FUTUREWORK: do this properly respecting the reference graph.
+            for (elem, root_node) in uploads {
+                // Create and upload a PathInfo pointing to the root_node,
+                // annotated with information we have from the reference graph.
+                let path_info = PathInfo {
+                    node: Some(tvix_castore::proto::Node {
+                        node: Some(root_node),
+                    }),
+                    references: Vec::from_iter(
+                        elem.references.iter().map(|e| e.digest().to_vec().into()),
+                    ),
+                    narinfo: Some(NarInfo {
+                        nar_size: elem.nar_size,
+                        nar_sha256: elem.nar_sha256.to_vec().into(),
+                        signatures: vec![],
+                        reference_names: Vec::from_iter(
+                            elem.references.iter().map(|e| e.to_string()),
+                        ),
+                        deriver: None,
+                        ca: None,
+                    }),
+                };
+
+                path_info_service.put(path_info).await?;
+            }
+        }
+        #[cfg(feature = "fuse")]
+        Commands::Mount {
+            dest,
+            blob_service_addr,
+            directory_service_addr,
+            path_info_service_addr,
+            list_root,
+            threads,
+            allow_other,
+            show_xattr,
+        } => {
+            let (blob_service, directory_service, path_info_service) =
+                tvix_store::utils::construct_services(
+                    blob_service_addr,
+                    directory_service_addr,
+                    path_info_service_addr,
+                )
+                .await?;
+
+            let mut fuse_daemon = tokio::task::spawn_blocking(move || {
+                let fs = make_fs(
+                    blob_service,
+                    directory_service,
+                    Arc::from(path_info_service),
+                    list_root,
+                    show_xattr,
+                );
+                info!(mount_path=?dest, "mounting");
+
+                FuseDaemon::new(fs, &dest, threads, allow_other)
+            })
+            .await??;
+
+            // grab a handle to unmount the file system, and register a signal
+            // handler.
+            tokio::spawn(async move {
+                tokio::signal::ctrl_c().await.unwrap();
+                info!("interrupt received, unmounting…");
+                tokio::task::spawn_blocking(move || fuse_daemon.unmount()).await??;
+                info!("unmount occured, terminating…");
+                Ok::<_, std::io::Error>(())
+            })
+            .await??;
+        }
+        #[cfg(feature = "virtiofs")]
+        Commands::VirtioFs {
+            socket,
+            blob_service_addr,
+            directory_service_addr,
+            path_info_service_addr,
+            list_root,
+            show_xattr,
+        } => {
+            let (blob_service, directory_service, path_info_service) =
+                tvix_store::utils::construct_services(
+                    blob_service_addr,
+                    directory_service_addr,
+                    path_info_service_addr,
+                )
+                .await?;
+
+            tokio::task::spawn_blocking(move || {
+                let fs = make_fs(
+                    blob_service,
+                    directory_service,
+                    Arc::from(path_info_service),
+                    list_root,
+                    show_xattr,
+                );
+                info!(socket_path=?socket, "starting virtiofs-daemon");
+
+                start_virtiofs_daemon(fs, socket)
+            })
+            .await??;
+        }
+    };
+    Ok(())
+}
diff --git a/tvix/store/src/import.rs b/tvix/store/src/import.rs
new file mode 100644
index 0000000000..88cebc8be6
--- /dev/null
+++ b/tvix/store/src/import.rs
@@ -0,0 +1,180 @@
+use std::path::Path;
+use tracing::{debug, instrument};
+use tvix_castore::{
+    blobservice::BlobService, directoryservice::DirectoryService, import::fs::ingest_path,
+    proto::node::Node, B3Digest,
+};
+
+use nix_compat::{
+    nixhash::{CAHash, NixHash},
+    store_path::{self, StorePath},
+};
+
+use crate::{
+    pathinfoservice::PathInfoService,
+    proto::{nar_info, NarInfo, PathInfo},
+};
+
+impl From<CAHash> for nar_info::Ca {
+    fn from(value: CAHash) -> Self {
+        let hash_type: nar_info::ca::Hash = (&value).into();
+        let digest: bytes::Bytes = value.hash().to_string().into();
+        nar_info::Ca {
+            r#type: hash_type.into(),
+            digest,
+        }
+    }
+}
+
+pub fn log_node(node: &Node, path: &Path) {
+    match node {
+        Node::Directory(directory_node) => {
+            debug!(
+                path = ?path,
+                name = ?directory_node.name,
+                digest = %B3Digest::try_from(directory_node.digest.clone()).unwrap(),
+                "import successful",
+            )
+        }
+        Node::File(file_node) => {
+            debug!(
+                path = ?path,
+                name = ?file_node.name,
+                digest = %B3Digest::try_from(file_node.digest.clone()).unwrap(),
+                "import successful"
+            )
+        }
+        Node::Symlink(symlink_node) => {
+            debug!(
+                path = ?path,
+                name = ?symlink_node.name,
+                target = ?symlink_node.target,
+                "import successful"
+            )
+        }
+    }
+}
+
+/// Transform a path into its base name and returns an [`std::io::Error`] if it is `..` or if the
+/// basename is not valid unicode.
+#[inline]
+pub fn path_to_name(path: &Path) -> std::io::Result<&str> {
+    path.file_name()
+        .and_then(|file_name| file_name.to_str())
+        .ok_or_else(|| {
+            std::io::Error::new(
+                std::io::ErrorKind::InvalidInput,
+                "path must not be .. and the basename valid unicode",
+            )
+        })
+}
+
+/// Takes the NAR size, SHA-256 of the NAR representation, the root node and optionally
+/// a CA hash information.
+///
+/// Returns the path information object for a NAR-style object.
+///
+/// This [`PathInfo`] can be further filled for signatures, deriver or verified for the expected
+/// hashes.
+#[inline]
+pub fn derive_nar_ca_path_info(
+    nar_size: u64,
+    nar_sha256: [u8; 32],
+    ca: Option<CAHash>,
+    root_node: Node,
+) -> PathInfo {
+    // assemble the [crate::proto::PathInfo] object.
+    PathInfo {
+        node: Some(tvix_castore::proto::Node {
+            node: Some(root_node),
+        }),
+        // There's no reference scanning on path contents ingested like this.
+        references: vec![],
+        narinfo: Some(NarInfo {
+            nar_size,
+            nar_sha256: nar_sha256.to_vec().into(),
+            signatures: vec![],
+            reference_names: vec![],
+            deriver: None,
+            ca: ca.map(|ca_hash| ca_hash.into()),
+        }),
+    }
+}
+
+/// Ingest the given path `path` and register the resulting output path in the
+/// [`PathInfoService`] as a recursive fixed output NAR.
+#[instrument(skip_all, fields(store_name=name, path=?path), err)]
+pub async fn import_path_as_nar_ca<BS, DS, PS, P>(
+    path: P,
+    name: &str,
+    blob_service: BS,
+    directory_service: DS,
+    path_info_service: PS,
+) -> Result<StorePath, std::io::Error>
+where
+    P: AsRef<Path> + std::fmt::Debug,
+    BS: BlobService + Clone,
+    DS: AsRef<dyn DirectoryService>,
+    PS: AsRef<dyn PathInfoService>,
+{
+    let root_node = ingest_path(blob_service, directory_service, path.as_ref())
+        .await
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
+
+    // Ask the PathInfoService for the NAR size and sha256
+    let (nar_size, nar_sha256) = path_info_service.as_ref().calculate_nar(&root_node).await?;
+
+    // Calculate the output path. This might still fail, as some names are illegal.
+    // FUTUREWORK: express the `name` at the type level to be valid and move the conversion
+    // at the caller level.
+    let output_path = store_path::build_nar_based_store_path(&nar_sha256, name).map_err(|_| {
+        std::io::Error::new(
+            std::io::ErrorKind::InvalidData,
+            format!("invalid name: {}", name),
+        )
+    })?;
+
+    // assemble a new root_node with a name that is derived from the nar hash.
+    let root_node = root_node.rename(output_path.to_string().into_bytes().into());
+    log_node(&root_node, path.as_ref());
+
+    let path_info = derive_nar_ca_path_info(
+        nar_size,
+        nar_sha256,
+        Some(CAHash::Nar(NixHash::Sha256(nar_sha256))),
+        root_node,
+    );
+
+    // This new [`PathInfo`] that we get back from there might contain additional signatures or
+    // information set by the service itself. In this function, we silently swallow it because
+    // callers doesn't really need it.
+    let _path_info = path_info_service.as_ref().put(path_info).await?;
+
+    Ok(output_path.to_owned())
+}
+
+#[cfg(test)]
+mod tests {
+    use std::{ffi::OsStr, path::PathBuf};
+
+    use crate::import::path_to_name;
+    use rstest::rstest;
+
+    #[rstest]
+    #[case::simple_path("a/b/c", "c")]
+    #[case::simple_path_containing_dotdot("a/b/../c", "c")]
+    #[case::path_containing_multiple_dotdot("a/b/../c/d/../e", "e")]
+
+    fn test_path_to_name(#[case] path: &str, #[case] expected_name: &str) {
+        let path: PathBuf = path.into();
+        assert_eq!(path_to_name(&path).expect("must succeed"), expected_name);
+    }
+
+    #[rstest]
+    #[case::path_ending_in_dotdot(b"a/b/..")]
+    #[case::non_unicode_path(b"\xf8\xa1\xa1\xa1\xa1")]
+    fn test_invalid_path_to_name(#[case] invalid_path: &[u8]) {
+        let path: PathBuf = unsafe { OsStr::from_encoded_bytes_unchecked(invalid_path) }.into();
+        path_to_name(&path).expect_err("must fail");
+    }
+}
diff --git a/tvix/store/src/lib.rs b/tvix/store/src/lib.rs
new file mode 100644
index 0000000000..8c32aaf885
--- /dev/null
+++ b/tvix/store/src/lib.rs
@@ -0,0 +1,14 @@
+pub mod import;
+pub mod nar;
+pub mod pathinfoservice;
+pub mod proto;
+pub mod utils;
+
+#[cfg(test)]
+mod tests;
+
+// That's what the rstest_reuse README asks us do, and fails about being unable
+// to find rstest_reuse in crate root.
+#[cfg(test)]
+#[allow(clippy::single_component_path_imports)]
+use rstest_reuse;
diff --git a/tvix/store/src/nar/import.rs b/tvix/store/src/nar/import.rs
new file mode 100644
index 0000000000..6f4dcdea5d
--- /dev/null
+++ b/tvix/store/src/nar/import.rs
@@ -0,0 +1,355 @@
+use bytes::Bytes;
+use nix_compat::nar;
+use std::io::{self, BufRead};
+use tokio_util::io::SyncIoBridge;
+use tracing::warn;
+use tvix_castore::{
+    blobservice::BlobService,
+    directoryservice::{DirectoryPutter, DirectoryService},
+    proto::{self as castorepb},
+    B3Digest,
+};
+
+/// Accepts a reader providing a NAR.
+/// Will traverse it, uploading blobs to the given [BlobService], and
+/// directories to the given [DirectoryService].
+/// On success, the root node is returned.
+/// This function is not async (because the NAR reader is not)
+/// and calls [tokio::task::block_in_place] when interacting with backing
+/// services, so make sure to only call this with spawn_blocking.
+pub fn read_nar<R, BS, DS>(
+    r: &mut R,
+    blob_service: BS,
+    directory_service: DS,
+) -> io::Result<castorepb::node::Node>
+where
+    R: BufRead + Send,
+    BS: AsRef<dyn BlobService>,
+    DS: AsRef<dyn DirectoryService>,
+{
+    let handle = tokio::runtime::Handle::current();
+
+    let directory_putter = directory_service.as_ref().put_multiple_start();
+
+    let node = nix_compat::nar::reader::open(r)?;
+    let (root_node, mut directory_putter, _) = process_node(
+        handle.clone(),
+        "".into(), // this is the root node, it has an empty name
+        node,
+        &blob_service,
+        directory_putter,
+    )?;
+
+    // In case the root node points to a directory, we need to close
+    // [directory_putter], and ensure the digest we got back from there matches
+    // what the root node is pointing to.
+    if let castorepb::node::Node::Directory(ref directory_node) = root_node {
+        // Close directory_putter to make sure all directories have been inserted.
+        let directory_putter_digest =
+            handle.block_on(handle.spawn(async move { directory_putter.close().await }))??;
+        let root_directory_node_digest: B3Digest =
+            directory_node.digest.clone().try_into().unwrap();
+
+        if directory_putter_digest != root_directory_node_digest {
+            warn!(
+                root_directory_node_digest = %root_directory_node_digest,
+                directory_putter_digest =%directory_putter_digest,
+                "directory digest mismatch",
+            );
+            return Err(io::Error::new(
+                io::ErrorKind::Other,
+                "directory digest mismatch",
+            ));
+        }
+    }
+    // In case it's not a Directory, [directory_putter] doesn't need to be
+    // closed (as we didn't end up uploading anything).
+    // It can just be dropped, as documented in its trait.
+
+    Ok(root_node)
+}
+
+/// This is called on a [nar::reader::Node] and returns a [castorepb::node::Node].
+/// It does so by handling all three kinds, and recursing for directories.
+///
+/// [DirectoryPutter] is passed around, so a single instance of it can be used,
+/// which is sufficient, as this reads through the whole NAR linerarly.
+fn process_node<BS>(
+    handle: tokio::runtime::Handle,
+    name: bytes::Bytes,
+    node: nar::reader::Node,
+    blob_service: BS,
+    directory_putter: Box<dyn DirectoryPutter>,
+) -> io::Result<(castorepb::node::Node, Box<dyn DirectoryPutter>, BS)>
+where
+    BS: AsRef<dyn BlobService>,
+{
+    Ok(match node {
+        nar::reader::Node::Symlink { target } => (
+            castorepb::node::Node::Symlink(castorepb::SymlinkNode {
+                name,
+                target: target.into(),
+            }),
+            directory_putter,
+            blob_service,
+        ),
+        nar::reader::Node::File { executable, reader } => (
+            castorepb::node::Node::File(process_file_reader(
+                handle,
+                name,
+                reader,
+                executable,
+                &blob_service,
+            )?),
+            directory_putter,
+            blob_service,
+        ),
+        nar::reader::Node::Directory(dir_reader) => {
+            let (directory_node, directory_putter, blob_service_back) =
+                process_dir_reader(handle, name, dir_reader, blob_service, directory_putter)?;
+
+            (
+                castorepb::node::Node::Directory(directory_node),
+                directory_putter,
+                blob_service_back,
+            )
+        }
+    })
+}
+
+/// Given a name and [nar::reader::FileReader], this ingests the file into the
+/// passed [BlobService] and returns a [castorepb::FileNode].
+fn process_file_reader<BS>(
+    handle: tokio::runtime::Handle,
+    name: Bytes,
+    mut file_reader: nar::reader::FileReader,
+    executable: bool,
+    blob_service: BS,
+) -> io::Result<castorepb::FileNode>
+where
+    BS: AsRef<dyn BlobService>,
+{
+    // store the length. If we read any other length, reading will fail.
+    let expected_len = file_reader.len();
+
+    // prepare writing a new blob.
+    let blob_writer = handle.block_on(async { blob_service.as_ref().open_write().await });
+
+    // write the blob.
+    let mut blob_writer = {
+        let mut dst = SyncIoBridge::new(blob_writer);
+
+        file_reader.copy(&mut dst)?;
+        dst.shutdown()?;
+
+        // return back the blob_writer
+        dst.into_inner()
+    };
+
+    // close the blob_writer, retrieve the digest.
+    let blob_digest = handle.block_on(async { blob_writer.close().await })?;
+
+    Ok(castorepb::FileNode {
+        name,
+        digest: blob_digest.into(),
+        size: expected_len,
+        executable,
+    })
+}
+
+/// Given a name and [nar::reader::DirReader], this returns a [castorepb::DirectoryNode].
+/// It uses [process_node] to iterate over all children.
+///
+/// [DirectoryPutter] is passed around, so a single instance of it can be used,
+/// which is sufficient, as this reads through the whole NAR linerarly.
+fn process_dir_reader<BS>(
+    handle: tokio::runtime::Handle,
+    name: Bytes,
+    mut dir_reader: nar::reader::DirReader,
+    blob_service: BS,
+    directory_putter: Box<dyn DirectoryPutter>,
+) -> io::Result<(castorepb::DirectoryNode, Box<dyn DirectoryPutter>, BS)>
+where
+    BS: AsRef<dyn BlobService>,
+{
+    let mut directory = castorepb::Directory::default();
+
+    let mut directory_putter = directory_putter;
+    let mut blob_service = blob_service;
+    while let Some(entry) = dir_reader.next()? {
+        let (node, directory_putter_back, blob_service_back) = process_node(
+            handle.clone(),
+            entry.name.into(),
+            entry.node,
+            blob_service,
+            directory_putter,
+        )?;
+
+        blob_service = blob_service_back;
+        directory_putter = directory_putter_back;
+
+        match node {
+            castorepb::node::Node::Directory(node) => directory.directories.push(node),
+            castorepb::node::Node::File(node) => directory.files.push(node),
+            castorepb::node::Node::Symlink(node) => directory.symlinks.push(node),
+        }
+    }
+
+    // calculate digest and size.
+    let directory_digest = directory.digest();
+    let directory_size = directory.size();
+
+    // upload the directory. This is a bit more verbose, as we want to get back
+    // directory_putter for later reuse.
+    let directory_putter = handle.block_on(handle.spawn(async move {
+        directory_putter.put(directory).await?;
+        Ok::<_, io::Error>(directory_putter)
+    }))??;
+
+    Ok((
+        castorepb::DirectoryNode {
+            name,
+            digest: directory_digest.into(),
+            size: directory_size,
+        },
+        directory_putter,
+        blob_service,
+    ))
+}
+
+#[cfg(test)]
+mod test {
+    use crate::nar::read_nar;
+    use std::io::Cursor;
+    use std::sync::Arc;
+
+    use rstest::*;
+    use tokio_stream::StreamExt;
+    use tvix_castore::blobservice::BlobService;
+    use tvix_castore::directoryservice::DirectoryService;
+    use tvix_castore::fixtures::{
+        DIRECTORY_COMPLICATED, DIRECTORY_WITH_KEEP, EMPTY_BLOB_DIGEST, HELLOWORLD_BLOB_CONTENTS,
+        HELLOWORLD_BLOB_DIGEST,
+    };
+    use tvix_castore::proto as castorepb;
+
+    use crate::tests::fixtures::{
+        blob_service, directory_service, NAR_CONTENTS_COMPLICATED, NAR_CONTENTS_HELLOWORLD,
+        NAR_CONTENTS_SYMLINK,
+    };
+
+    #[rstest]
+    #[tokio::test]
+    async fn single_symlink(
+        blob_service: Arc<dyn BlobService>,
+        directory_service: Arc<dyn DirectoryService>,
+    ) {
+        let handle = tokio::runtime::Handle::current();
+
+        let root_node = handle
+            .spawn_blocking(|| {
+                read_nar(
+                    &mut Cursor::new(&NAR_CONTENTS_SYMLINK.clone()),
+                    blob_service,
+                    directory_service,
+                )
+            })
+            .await
+            .unwrap()
+            .expect("must parse");
+
+        assert_eq!(
+            castorepb::node::Node::Symlink(castorepb::SymlinkNode {
+                name: "".into(), // name must be empty
+                target: "/nix/store/somewhereelse".into(),
+            }),
+            root_node
+        );
+    }
+
+    #[rstest]
+    #[tokio::test]
+    async fn single_file(
+        blob_service: Arc<dyn BlobService>,
+        directory_service: Arc<dyn DirectoryService>,
+    ) {
+        let handle = tokio::runtime::Handle::current();
+
+        let root_node = handle
+            .spawn_blocking({
+                let blob_service = blob_service.clone();
+                move || {
+                    read_nar(
+                        &mut Cursor::new(&NAR_CONTENTS_HELLOWORLD.clone()),
+                        blob_service,
+                        directory_service,
+                    )
+                }
+            })
+            .await
+            .unwrap()
+            .expect("must parse");
+
+        assert_eq!(
+            castorepb::node::Node::File(castorepb::FileNode {
+                name: "".into(), // name must be empty
+                digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+                size: HELLOWORLD_BLOB_CONTENTS.len() as u64,
+                executable: false,
+            }),
+            root_node
+        );
+
+        // blobservice must contain the blob
+        assert!(blob_service.has(&HELLOWORLD_BLOB_DIGEST).await.unwrap());
+    }
+
+    #[rstest]
+    #[tokio::test]
+    async fn complicated(
+        blob_service: Arc<dyn BlobService>,
+        directory_service: Arc<dyn DirectoryService>,
+    ) {
+        let handle = tokio::runtime::Handle::current();
+
+        let root_node = handle
+            .spawn_blocking({
+                let blob_service = blob_service.clone();
+                let directory_service = directory_service.clone();
+                || {
+                    read_nar(
+                        &mut Cursor::new(&NAR_CONTENTS_COMPLICATED.clone()),
+                        blob_service,
+                        directory_service,
+                    )
+                }
+            })
+            .await
+            .unwrap()
+            .expect("must parse");
+
+        assert_eq!(
+            castorepb::node::Node::Directory(castorepb::DirectoryNode {
+                name: "".into(), // name must be empty
+                digest: DIRECTORY_COMPLICATED.digest().into(),
+                size: DIRECTORY_COMPLICATED.size(),
+            }),
+            root_node,
+        );
+
+        // blobservice must contain the blob
+        assert!(blob_service.has(&EMPTY_BLOB_DIGEST).await.unwrap());
+
+        // directoryservice must contain the directories, at least with get_recursive.
+        let resp: Result<Vec<castorepb::Directory>, _> = directory_service
+            .get_recursive(&DIRECTORY_COMPLICATED.digest())
+            .collect()
+            .await;
+
+        let directories = resp.unwrap();
+
+        assert_eq!(2, directories.len());
+        assert_eq!(DIRECTORY_COMPLICATED.clone(), directories[0]);
+        assert_eq!(DIRECTORY_WITH_KEEP.clone(), directories[1]);
+    }
+}
diff --git a/tvix/store/src/nar/mod.rs b/tvix/store/src/nar/mod.rs
new file mode 100644
index 0000000000..49bb92fb0f
--- /dev/null
+++ b/tvix/store/src/nar/mod.rs
@@ -0,0 +1,26 @@
+use tvix_castore::B3Digest;
+
+mod import;
+mod renderer;
+pub use import::read_nar;
+pub use renderer::calculate_size_and_sha256;
+pub use renderer::write_nar;
+
+/// Errors that can encounter while rendering NARs.
+#[derive(Debug, thiserror::Error)]
+pub enum RenderError {
+    #[error("failure talking to a backing store client: {0}")]
+    StoreError(#[source] std::io::Error),
+
+    #[error("unable to find directory {}, referred from {:?}", .0, .1)]
+    DirectoryNotFound(B3Digest, bytes::Bytes),
+
+    #[error("unable to find blob {}, referred from {:?}", .0, .1)]
+    BlobNotFound(B3Digest, bytes::Bytes),
+
+    #[error("unexpected size in metadata for blob {}, referred from {:?} returned, expected {}, got {}", .0, .1, .2, .3)]
+    UnexpectedBlobMeta(B3Digest, bytes::Bytes, u32, u32),
+
+    #[error("failure using the NAR writer: {0}")]
+    NARWriterError(std::io::Error),
+}
diff --git a/tvix/store/src/nar/renderer.rs b/tvix/store/src/nar/renderer.rs
new file mode 100644
index 0000000000..0816b8e973
--- /dev/null
+++ b/tvix/store/src/nar/renderer.rs
@@ -0,0 +1,185 @@
+use crate::utils::AsyncIoBridge;
+
+use super::RenderError;
+use async_recursion::async_recursion;
+use count_write::CountWrite;
+use nix_compat::nar::writer::r#async as nar_writer;
+use sha2::{Digest, Sha256};
+use tokio::io::{self, AsyncWrite, BufReader};
+use tvix_castore::{
+    blobservice::BlobService,
+    directoryservice::DirectoryService,
+    proto::{self as castorepb, NamedNode},
+};
+
+/// Invoke [write_nar], and return the size and sha256 digest of the produced
+/// NAR output.
+pub async fn calculate_size_and_sha256<BS, DS>(
+    root_node: &castorepb::node::Node,
+    blob_service: BS,
+    directory_service: DS,
+) -> Result<(u64, [u8; 32]), RenderError>
+where
+    BS: BlobService + Send,
+    DS: DirectoryService + Send,
+{
+    let mut h = Sha256::new();
+    let mut cw = CountWrite::from(&mut h);
+
+    write_nar(
+        // The hasher doesn't speak async. It doesn't
+        // actually do any I/O, so it's fine to wrap.
+        AsyncIoBridge(&mut cw),
+        root_node,
+        blob_service,
+        directory_service,
+    )
+    .await?;
+
+    Ok((cw.count(), h.finalize().into()))
+}
+
+/// Accepts a [castorepb::node::Node] pointing to the root of a (store) path,
+/// and uses the passed blob_service and directory_service to perform the
+/// necessary lookups as it traverses the structure.
+/// The contents in NAR serialization are writen to the passed [AsyncWrite].
+pub async fn write_nar<W, BS, DS>(
+    mut w: W,
+    proto_root_node: &castorepb::node::Node,
+    blob_service: BS,
+    directory_service: DS,
+) -> Result<(), RenderError>
+where
+    W: AsyncWrite + Unpin + Send,
+    BS: BlobService + Send,
+    DS: DirectoryService + Send,
+{
+    // Initialize NAR writer
+    let nar_root_node = nar_writer::open(&mut w)
+        .await
+        .map_err(RenderError::NARWriterError)?;
+
+    walk_node(
+        nar_root_node,
+        proto_root_node,
+        blob_service,
+        directory_service,
+    )
+    .await?;
+
+    Ok(())
+}
+
+/// Process an intermediate node in the structure.
+/// This consumes the node.
+#[async_recursion]
+async fn walk_node<BS, DS>(
+    nar_node: nar_writer::Node<'async_recursion, '_>,
+    proto_node: &castorepb::node::Node,
+    blob_service: BS,
+    directory_service: DS,
+) -> Result<(BS, DS), RenderError>
+where
+    BS: BlobService + Send,
+    DS: DirectoryService + Send,
+{
+    match proto_node {
+        castorepb::node::Node::Symlink(proto_symlink_node) => {
+            nar_node
+                .symlink(&proto_symlink_node.target)
+                .await
+                .map_err(RenderError::NARWriterError)?;
+        }
+        castorepb::node::Node::File(proto_file_node) => {
+            let digest_len = proto_file_node.digest.len();
+            let digest = proto_file_node.digest.clone().try_into().map_err(|_| {
+                RenderError::StoreError(io::Error::new(
+                    io::ErrorKind::Other,
+                    format!("invalid digest len {} in file node", digest_len),
+                ))
+            })?;
+
+            let mut blob_reader = match blob_service
+                .open_read(&digest)
+                .await
+                .map_err(RenderError::StoreError)?
+            {
+                Some(blob_reader) => Ok(BufReader::new(blob_reader)),
+                None => Err(RenderError::NARWriterError(io::Error::new(
+                    io::ErrorKind::NotFound,
+                    format!("blob with digest {} not found", &digest),
+                ))),
+            }?;
+
+            nar_node
+                .file(
+                    proto_file_node.executable,
+                    proto_file_node.size,
+                    &mut blob_reader,
+                )
+                .await
+                .map_err(RenderError::NARWriterError)?;
+        }
+        castorepb::node::Node::Directory(proto_directory_node) => {
+            let digest_len = proto_directory_node.digest.len();
+            let digest = proto_directory_node
+                .digest
+                .clone()
+                .try_into()
+                .map_err(|_| {
+                    RenderError::StoreError(io::Error::new(
+                        io::ErrorKind::InvalidData,
+                        format!("invalid digest len {} in directory node", digest_len),
+                    ))
+                })?;
+
+            // look it up with the directory service
+            match directory_service
+                .get(&digest)
+                .await
+                .map_err(|e| RenderError::StoreError(e.into()))?
+            {
+                // if it's None, that's an error!
+                None => Err(RenderError::DirectoryNotFound(
+                    digest,
+                    proto_directory_node.name.clone(),
+                ))?,
+                Some(proto_directory) => {
+                    // start a directory node
+                    let mut nar_node_directory = nar_node
+                        .directory()
+                        .await
+                        .map_err(RenderError::NARWriterError)?;
+
+                    // We put blob_service, directory_service back here whenever we come up from
+                    // the recursion.
+                    let mut blob_service = blob_service;
+                    let mut directory_service = directory_service;
+
+                    // for each node in the directory, create a new entry with its name,
+                    // and then recurse on that entry.
+                    for proto_node in proto_directory.nodes() {
+                        let child_node = nar_node_directory
+                            .entry(proto_node.get_name())
+                            .await
+                            .map_err(RenderError::NARWriterError)?;
+
+                        (blob_service, directory_service) =
+                            walk_node(child_node, &proto_node, blob_service, directory_service)
+                                .await?;
+                    }
+
+                    // close the directory
+                    nar_node_directory
+                        .close()
+                        .await
+                        .map_err(RenderError::NARWriterError)?;
+
+                    return Ok((blob_service, directory_service));
+                }
+            }
+        }
+    }
+
+    Ok((blob_service, directory_service))
+}
diff --git a/tvix/store/src/pathinfoservice/bigtable.rs b/tvix/store/src/pathinfoservice/bigtable.rs
new file mode 100644
index 0000000000..6fb52abbfd
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/bigtable.rs
@@ -0,0 +1,401 @@
+use super::PathInfoService;
+use crate::proto;
+use crate::proto::PathInfo;
+use async_stream::try_stream;
+use bigtable_rs::{bigtable, google::bigtable::v2 as bigtable_v2};
+use bytes::Bytes;
+use data_encoding::HEXLOWER;
+use futures::stream::BoxStream;
+use prost::Message;
+use serde::{Deserialize, Serialize};
+use serde_with::{serde_as, DurationSeconds};
+use tonic::async_trait;
+use tracing::trace;
+use tvix_castore::proto as castorepb;
+use tvix_castore::Error;
+
+/// There should not be more than 10 MiB in a single cell.
+/// https://cloud.google.com/bigtable/docs/schema-design#cells
+const CELL_SIZE_LIMIT: u64 = 10 * 1024 * 1024;
+
+/// Provides a [DirectoryService] implementation using
+/// [Bigtable](https://cloud.google.com/bigtable/docs/)
+/// as an underlying K/V store.
+///
+/// # Data format
+/// We use Bigtable as a plain K/V store.
+/// The row key is the digest of the store path, in hexlower.
+/// Inside the row, we currently have a single column/cell, again using the
+/// hexlower store path digest.
+/// Its value is the PathInfo message, serialized in canonical protobuf.
+/// We currently only populate this column.
+///
+/// Listing is ranging over all rows, and calculate_nar is returning a
+/// "unimplemented" error.
+#[derive(Clone)]
+pub struct BigtablePathInfoService {
+    client: bigtable::BigTable,
+    params: BigtableParameters,
+
+    #[cfg(test)]
+    #[allow(dead_code)]
+    /// Holds the temporary directory containing the unix socket, and the
+    /// spawned emulator process.
+    emulator: std::sync::Arc<(tempfile::TempDir, async_process::Child)>,
+}
+
+/// Represents configuration of [BigtablePathInfoService].
+/// This currently conflates both connect parameters and data model/client
+/// behaviour parameters.
+#[serde_as]
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub struct BigtableParameters {
+    project_id: String,
+    instance_name: String,
+    #[serde(default)]
+    is_read_only: bool,
+    #[serde(default = "default_channel_size")]
+    channel_size: usize,
+
+    #[serde_as(as = "Option<DurationSeconds<String>>")]
+    #[serde(default = "default_timeout")]
+    timeout: Option<std::time::Duration>,
+    table_name: String,
+    family_name: String,
+
+    #[serde(default = "default_app_profile_id")]
+    app_profile_id: String,
+}
+
+fn default_app_profile_id() -> String {
+    "default".to_owned()
+}
+
+fn default_channel_size() -> usize {
+    4
+}
+
+fn default_timeout() -> Option<std::time::Duration> {
+    Some(std::time::Duration::from_secs(4))
+}
+
+impl BigtablePathInfoService {
+    #[cfg(not(test))]
+    pub async fn connect(params: BigtableParameters) -> Result<Self, bigtable::Error> {
+        let connection = bigtable::BigTableConnection::new(
+            &params.project_id,
+            &params.instance_name,
+            params.is_read_only,
+            params.channel_size,
+            params.timeout,
+        )
+        .await?;
+
+        Ok(Self {
+            client: connection.client(),
+            params,
+        })
+    }
+
+    #[cfg(test)]
+    pub async fn connect(params: BigtableParameters) -> Result<Self, bigtable::Error> {
+        use std::time::Duration;
+
+        use async_process::{Command, Stdio};
+        use tempfile::TempDir;
+        use tokio_retry::{strategy::ExponentialBackoff, Retry};
+
+        let tmpdir = TempDir::new().unwrap();
+
+        let socket_path = tmpdir.path().join("cbtemulator.sock");
+
+        let emulator_process = Command::new("cbtemulator")
+            .arg("-address")
+            .arg(socket_path.clone())
+            .stderr(Stdio::piped())
+            .stdout(Stdio::piped())
+            .kill_on_drop(true)
+            .spawn()
+            .expect("failed to spawn emulator");
+
+        Retry::spawn(
+            ExponentialBackoff::from_millis(20)
+                .max_delay(Duration::from_secs(1))
+                .take(3),
+            || async {
+                if socket_path.exists() {
+                    Ok(())
+                } else {
+                    Err(())
+                }
+            },
+        )
+        .await
+        .expect("failed to wait for socket");
+
+        // populate the emulator
+        for cmd in &[
+            vec!["createtable", &params.table_name],
+            vec!["createfamily", &params.table_name, &params.family_name],
+        ] {
+            Command::new("cbt")
+                .args({
+                    let mut args = vec![
+                        "-instance",
+                        &params.instance_name,
+                        "-project",
+                        &params.project_id,
+                    ];
+                    args.extend_from_slice(cmd);
+                    args
+                })
+                .env(
+                    "BIGTABLE_EMULATOR_HOST",
+                    format!("unix://{}", socket_path.to_string_lossy()),
+                )
+                .output()
+                .await
+                .expect("failed to run cbt setup command");
+        }
+
+        let connection = bigtable_rs::bigtable::BigTableConnection::new_with_emulator(
+            &format!("unix://{}", socket_path.to_string_lossy()),
+            &params.project_id,
+            &params.instance_name,
+            false,
+            None,
+        )?;
+
+        Ok(Self {
+            client: connection.client(),
+            params,
+            emulator: (tmpdir, emulator_process).into(),
+        })
+    }
+}
+
+/// Derives the row/column key for a given output path.
+/// We use hexlower encoding, also because it can't be misinterpreted as RE2.
+fn derive_pathinfo_key(digest: &[u8; 20]) -> String {
+    HEXLOWER.encode(digest)
+}
+
+#[async_trait]
+impl PathInfoService for BigtablePathInfoService {
+    async fn get(&self, digest: [u8; 20]) -> Result<Option<PathInfo>, Error> {
+        let mut client = self.client.clone();
+        let path_info_key = derive_pathinfo_key(&digest);
+
+        let request = bigtable_v2::ReadRowsRequest {
+            app_profile_id: self.params.app_profile_id.to_string(),
+            table_name: client.get_full_table_name(&self.params.table_name),
+            rows_limit: 1,
+            rows: Some(bigtable_v2::RowSet {
+                row_keys: vec![path_info_key.clone().into()],
+                row_ranges: vec![],
+            }),
+            // Filter selected family name, and column qualifier matching the digest.
+            // The latter is to ensure we don't fail once we start adding more metadata.
+            filter: Some(bigtable_v2::RowFilter {
+                filter: Some(bigtable_v2::row_filter::Filter::Chain(
+                    bigtable_v2::row_filter::Chain {
+                        filters: vec![
+                            bigtable_v2::RowFilter {
+                                filter: Some(
+                                    bigtable_v2::row_filter::Filter::FamilyNameRegexFilter(
+                                        self.params.family_name.to_string(),
+                                    ),
+                                ),
+                            },
+                            bigtable_v2::RowFilter {
+                                filter: Some(
+                                    bigtable_v2::row_filter::Filter::ColumnQualifierRegexFilter(
+                                        path_info_key.clone().into(),
+                                    ),
+                                ),
+                            },
+                        ],
+                    },
+                )),
+            }),
+            ..Default::default()
+        };
+
+        let mut response = client
+            .read_rows(request)
+            .await
+            .map_err(|e| Error::StorageError(format!("unable to read rows: {}", e)))?;
+
+        if response.len() != 1 {
+            if response.len() > 1 {
+                // This shouldn't happen, we limit number of rows to 1
+                return Err(Error::StorageError(
+                    "got more than one row from bigtable".into(),
+                ));
+            }
+            // else, this is simply a "not found".
+            return Ok(None);
+        }
+
+        let (row_key, mut cells) = response.pop().unwrap();
+        if row_key != path_info_key.as_bytes() {
+            // This shouldn't happen, we requested this row key.
+            return Err(Error::StorageError(
+                "got wrong row key from bigtable".into(),
+            ));
+        }
+
+        let cell = cells
+            .pop()
+            .ok_or_else(|| Error::StorageError("found no cells".into()))?;
+
+        // Ensure there's only one cell (so no more left after the pop())
+        // This shouldn't happen, We filter out other cells in our query.
+        if !cells.is_empty() {
+            return Err(Error::StorageError(
+                "more than one cell returned from bigtable".into(),
+            ));
+        }
+
+        // We also require the qualifier to be correct in the filter above,
+        // so this shouldn't happen.
+        if path_info_key.as_bytes() != cell.qualifier {
+            return Err(Error::StorageError("unexpected cell qualifier".into()));
+        }
+
+        // Try to parse the value into a PathInfo message
+        let path_info = proto::PathInfo::decode(Bytes::from(cell.value))
+            .map_err(|e| Error::StorageError(format!("unable to decode pathinfo proto: {}", e)))?;
+
+        let store_path = path_info
+            .validate()
+            .map_err(|e| Error::StorageError(format!("invalid PathInfo: {}", e)))?;
+
+        if store_path.digest() != &digest {
+            return Err(Error::StorageError("PathInfo has unexpected digest".into()));
+        }
+
+        Ok(Some(path_info))
+    }
+
+    async fn put(&self, path_info: PathInfo) -> Result<PathInfo, Error> {
+        let store_path = path_info
+            .validate()
+            .map_err(|e| Error::InvalidRequest(format!("pathinfo failed validation: {}", e)))?;
+
+        let mut client = self.client.clone();
+        let path_info_key = derive_pathinfo_key(store_path.digest());
+
+        let data = path_info.encode_to_vec();
+        if data.len() as u64 > CELL_SIZE_LIMIT {
+            return Err(Error::StorageError(
+                "PathInfo exceeds cell limit on Bigtable".into(),
+            ));
+        }
+
+        let resp = client
+            .check_and_mutate_row(bigtable_v2::CheckAndMutateRowRequest {
+                table_name: client.get_full_table_name(&self.params.table_name),
+                app_profile_id: self.params.app_profile_id.to_string(),
+                row_key: path_info_key.clone().into(),
+                predicate_filter: Some(bigtable_v2::RowFilter {
+                    filter: Some(bigtable_v2::row_filter::Filter::ColumnQualifierRegexFilter(
+                        path_info_key.clone().into(),
+                    )),
+                }),
+                // If the column was already found, do nothing.
+                true_mutations: vec![],
+                // Else, do the insert.
+                false_mutations: vec![
+                    // https://cloud.google.com/bigtable/docs/writes
+                    bigtable_v2::Mutation {
+                        mutation: Some(bigtable_v2::mutation::Mutation::SetCell(
+                            bigtable_v2::mutation::SetCell {
+                                family_name: self.params.family_name.to_string(),
+                                column_qualifier: path_info_key.clone().into(),
+                                timestamp_micros: -1, // use server time to fill timestamp
+                                value: data,
+                            },
+                        )),
+                    },
+                ],
+            })
+            .await
+            .map_err(|e| Error::StorageError(format!("unable to mutate rows: {}", e)))?;
+
+        if resp.predicate_matched {
+            trace!("already existed")
+        }
+
+        Ok(path_info)
+    }
+
+    async fn calculate_nar(
+        &self,
+        _root_node: &castorepb::node::Node,
+    ) -> Result<(u64, [u8; 32]), Error> {
+        return Err(Error::StorageError("unimplemented".into()));
+    }
+
+    fn list(&self) -> BoxStream<'static, Result<PathInfo, Error>> {
+        let mut client = self.client.clone();
+
+        let request = bigtable_v2::ReadRowsRequest {
+            app_profile_id: self.params.app_profile_id.to_string(),
+            table_name: client.get_full_table_name(&self.params.table_name),
+            filter: Some(bigtable_v2::RowFilter {
+                filter: Some(bigtable_v2::row_filter::Filter::FamilyNameRegexFilter(
+                    self.params.family_name.to_string(),
+                )),
+            }),
+            ..Default::default()
+        };
+
+        let stream = try_stream! {
+            // TODO: add pagination, we don't want to hold all of this in memory.
+            let response = client
+                .read_rows(request)
+                .await
+                .map_err(|e| Error::StorageError(format!("unable to read rows: {}", e)))?;
+
+            for (row_key, mut cells) in response {
+                let cell = cells
+                    .pop()
+                    .ok_or_else(|| Error::StorageError("found no cells".into()))?;
+
+                // The cell must have the same qualifier as the row key
+                if row_key != cell.qualifier {
+                    Err(Error::StorageError("unexpected cell qualifier".into()))?;
+                }
+
+                // Ensure there's only one cell (so no more left after the pop())
+                // This shouldn't happen, We filter out other cells in our query.
+                if !cells.is_empty() {
+
+                    Err(Error::StorageError(
+                        "more than one cell returned from bigtable".into(),
+                    ))?
+                }
+
+                // Try to parse the value into a PathInfo message.
+                let path_info = proto::PathInfo::decode(Bytes::from(cell.value))
+                    .map_err(|e| Error::StorageError(format!("unable to decode pathinfo proto: {}", e)))?;
+
+                // Validate the containing PathInfo, ensure its StorePath digest
+                // matches row key.
+                let store_path = path_info
+                    .validate()
+                    .map_err(|e| Error::StorageError(format!("invalid PathInfo: {}", e)))?;
+
+                if store_path.digest().as_slice() != row_key.as_slice() {
+                    Err(Error::StorageError("PathInfo has unexpected digest".into()))?
+                }
+
+
+                yield path_info
+            }
+        };
+
+        Box::pin(stream)
+    }
+}
diff --git a/tvix/store/src/pathinfoservice/from_addr.rs b/tvix/store/src/pathinfoservice/from_addr.rs
new file mode 100644
index 0000000000..f22884ca47
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/from_addr.rs
@@ -0,0 +1,236 @@
+use crate::proto::path_info_service_client::PathInfoServiceClient;
+
+use super::{
+    GRPCPathInfoService, MemoryPathInfoService, NixHTTPPathInfoService, PathInfoService,
+    SledPathInfoService,
+};
+
+use nix_compat::narinfo;
+use std::sync::Arc;
+use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService, Error};
+use url::Url;
+
+/// Constructs a new instance of a [PathInfoService] from an URI.
+///
+/// The following URIs are supported:
+/// - `memory:`
+///   Uses a in-memory implementation.
+/// - `sled:`
+///   Uses a in-memory sled implementation.
+/// - `sled:///absolute/path/to/somewhere`
+///   Uses sled, using a path on the disk for persistency. Can be only opened
+///   from one process at the same time.
+/// - `nix+https://cache.nixos.org?trusted-public-keys=cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=`
+///   Exposes the Nix binary cache as a PathInfoService, ingesting NARs into the
+///   {Blob,Directory}Service. You almost certainly want to use this with some cache.
+///   The `trusted-public-keys` URL parameter can be provided, which will then
+///   enable signature verification.
+/// - `grpc+unix:///absolute/path/to/somewhere`
+///   Connects to a local tvix-store gRPC service via Unix socket.
+/// - `grpc+http://host:port`, `grpc+https://host:port`
+///    Connects to a (remote) tvix-store gRPC service.
+///
+/// As the [PathInfoService] needs to talk to [BlobService] and [DirectoryService],
+/// these also need to be passed in.
+pub async fn from_addr(
+    uri: &str,
+    blob_service: Arc<dyn BlobService>,
+    directory_service: Arc<dyn DirectoryService>,
+) -> Result<Box<dyn PathInfoService>, Error> {
+    #[allow(unused_mut)]
+    let mut url =
+        Url::parse(uri).map_err(|e| Error::StorageError(format!("unable to parse url: {}", e)))?;
+
+    let path_info_service: Box<dyn PathInfoService> = match url.scheme() {
+        "memory" => {
+            // memory doesn't support host or path in the URL.
+            if url.has_host() || !url.path().is_empty() {
+                return Err(Error::StorageError("invalid url".to_string()));
+            }
+            Box::new(MemoryPathInfoService::new(blob_service, directory_service))
+        }
+        "sled" => {
+            // sled doesn't support host, and a path can be provided (otherwise
+            // it'll live in memory only).
+            if url.has_host() {
+                return Err(Error::StorageError("no host allowed".to_string()));
+            }
+
+            if url.path() == "/" {
+                return Err(Error::StorageError(
+                    "cowardly refusing to open / with sled".to_string(),
+                ));
+            }
+
+            // TODO: expose other parameters as URL parameters?
+
+            Box::new(if url.path().is_empty() {
+                SledPathInfoService::new_temporary(blob_service, directory_service)
+                    .map_err(|e| Error::StorageError(e.to_string()))?
+            } else {
+                SledPathInfoService::new(url.path(), blob_service, directory_service)
+                    .map_err(|e| Error::StorageError(e.to_string()))?
+            })
+        }
+        "nix+http" | "nix+https" => {
+            // Stringify the URL and remove the nix+ prefix.
+            // We can't use `url.set_scheme(rest)`, as it disallows
+            // setting something http(s) that previously wasn't.
+            let new_url = Url::parse(url.to_string().strip_prefix("nix+").unwrap()).unwrap();
+
+            let mut nix_http_path_info_service =
+                NixHTTPPathInfoService::new(new_url, blob_service, directory_service);
+
+            let pairs = &url.query_pairs();
+            for (k, v) in pairs.into_iter() {
+                if k == "trusted-public-keys" {
+                    let pubkey_strs: Vec<_> = v.split_ascii_whitespace().collect();
+
+                    let mut pubkeys: Vec<narinfo::PubKey> = Vec::with_capacity(pubkey_strs.len());
+                    for pubkey_str in pubkey_strs {
+                        pubkeys.push(narinfo::PubKey::parse(pubkey_str).map_err(|e| {
+                            Error::StorageError(format!("invalid public key: {e}"))
+                        })?);
+                    }
+
+                    nix_http_path_info_service.set_public_keys(pubkeys);
+                }
+            }
+
+            Box::new(nix_http_path_info_service)
+        }
+        scheme if scheme.starts_with("grpc+") => {
+            // schemes starting with grpc+ go to the GRPCPathInfoService.
+            //   That's normally grpc+unix for unix sockets, and grpc+http(s) for the HTTP counterparts.
+            // - In the case of unix sockets, there must be a path, but may not be a host.
+            // - In the case of non-unix sockets, there must be a host, but no path.
+            // Constructing the channel is handled by tvix_castore::channel::from_url.
+            let client =
+                PathInfoServiceClient::new(tvix_castore::tonic::channel_from_url(&url).await?);
+            Box::new(GRPCPathInfoService::from_client(client))
+        }
+        #[cfg(feature = "cloud")]
+        "bigtable" => {
+            use super::bigtable::BigtableParameters;
+            use super::BigtablePathInfoService;
+
+            // parse the instance name from the hostname.
+            let instance_name = url
+                .host_str()
+                .ok_or_else(|| Error::StorageError("instance name missing".into()))?
+                .to_string();
+
+            // … but add it to the query string now, so we just need to parse that.
+            url.query_pairs_mut()
+                .append_pair("instance_name", &instance_name);
+
+            let params: BigtableParameters = serde_qs::from_str(url.query().unwrap_or_default())
+                .map_err(|e| Error::InvalidRequest(format!("failed to parse parameters: {}", e)))?;
+
+            Box::new(
+                BigtablePathInfoService::connect(params)
+                    .await
+                    .map_err(|e| Error::StorageError(e.to_string()))?,
+            )
+        }
+        _ => Err(Error::StorageError(format!(
+            "unknown scheme: {}",
+            url.scheme()
+        )))?,
+    };
+
+    Ok(path_info_service)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::from_addr;
+    use lazy_static::lazy_static;
+    use rstest::rstest;
+    use std::sync::Arc;
+    use tempfile::TempDir;
+    use tvix_castore::{
+        blobservice::{BlobService, MemoryBlobService},
+        directoryservice::{DirectoryService, MemoryDirectoryService},
+    };
+
+    lazy_static! {
+        static ref TMPDIR_SLED_1: TempDir = TempDir::new().unwrap();
+        static ref TMPDIR_SLED_2: TempDir = TempDir::new().unwrap();
+    }
+
+    // the gRPC tests below don't fail, because we connect lazily.
+
+    #[rstest]
+    /// This uses a unsupported scheme.
+    #[case::unsupported_scheme("http://foo.example/test", false)]
+    /// This configures sled in temporary mode.
+    #[case::sled_temporary("sled://", true)]
+    /// This configures sled with /, which should fail.
+    #[case::sled_invalid_root("sled:///", false)]
+    /// This configures sled with a host, not path, which should fail.
+    #[case::sled_invalid_host("sled://foo.example", false)]
+    /// This configures sled with a valid path path, which should succeed.
+    #[case::sled_valid_path(&format!("sled://{}", &TMPDIR_SLED_1.path().to_str().unwrap()), true)]
+    /// This configures sled with a host, and a valid path path, which should fail.
+    #[case::sled_invalid_host_with_valid_path(&format!("sled://foo.example{}", &TMPDIR_SLED_2.path().to_str().unwrap()), false)]
+    /// This correctly sets the scheme, and doesn't set a path.
+    #[case::memory_valid("memory://", true)]
+    /// This sets a memory url host to `foo`
+    #[case::memory_invalid_host("memory://foo", false)]
+    /// This sets a memory url path to "/", which is invalid.
+    #[case::memory_invalid_root_path("memory:///", false)]
+    /// This sets a memory url path to "/foo", which is invalid.
+    #[case::memory_invalid_root_path_foo("memory:///foo", false)]
+    /// Correct Scheme for the cache.nixos.org binary cache.
+    #[case::correct_nix_https("nix+https://cache.nixos.org", true)]
+    /// Correct Scheme for the cache.nixos.org binary cache (HTTP URL).
+    #[case::correct_nix_http("nix+http://cache.nixos.org", true)]
+    /// Correct Scheme for Nix HTTP Binary cache, with a subpath.
+    #[case::correct_nix_http_with_subpath("nix+http://192.0.2.1/foo", true)]
+    /// Correct Scheme for Nix HTTP Binary cache, with a subpath and port.
+    #[case::correct_nix_http_with_subpath_and_port("nix+http://[::1]:8080/foo", true)]
+    /// Correct Scheme for the cache.nixos.org binary cache, and correct trusted public key set
+    #[case::correct_nix_https_with_trusted_public_key("nix+https://cache.nixos.org?trusted-public-keys=cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=", true)]
+    /// Correct Scheme for the cache.nixos.org binary cache, and two correct trusted public keys set
+    #[case::correct_nix_https_with_two_trusted_public_keys("nix+https://cache.nixos.org?trusted-public-keys=cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=%20foo:jp4fCEx9tBEId/L0ZsVJ26k0wC0fu7vJqLjjIGFkup8=", true)]
+    /// Correct scheme to connect to a unix socket.
+    #[case::grpc_valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
+    /// Correct scheme for unix socket, but setting a host too, which is invalid.
+    #[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
+    /// Correct scheme to connect to localhost, with port 12345
+    #[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
+    /// Correct scheme to connect to localhost over http, without specifying a port.
+    #[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
+    /// Correct scheme to connect to localhost over http, but with additional path, which is invalid.
+    #[case::grpc_invalid_host_and_path("grpc+http://localhost/some-path", false)]
+    /// A valid example for Bigtable.
+    #[cfg_attr(
+        all(feature = "cloud", feature = "integration"),
+        case::bigtable_valid(
+            "bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1",
+            true
+        )
+    )]
+    /// An invalid example for Bigtable, missing fields
+    #[cfg_attr(
+        all(feature = "cloud", feature = "integration"),
+        case::bigtable_invalid_missing_fields("bigtable://instance-1", false)
+    )]
+    #[tokio::test]
+    async fn test_from_addr_tokio(#[case] uri_str: &str, #[case] exp_succeed: bool) {
+        let blob_service: Arc<dyn BlobService> = Arc::from(MemoryBlobService::default());
+        let directory_service: Arc<dyn DirectoryService> =
+            Arc::from(MemoryDirectoryService::default());
+
+        let resp = from_addr(uri_str, blob_service, directory_service).await;
+
+        if exp_succeed {
+            resp.expect("should succeed");
+        } else {
+            assert!(resp.is_err(), "should fail");
+        }
+    }
+}
diff --git a/tvix/store/src/pathinfoservice/fs/mod.rs b/tvix/store/src/pathinfoservice/fs/mod.rs
new file mode 100644
index 0000000000..aa64b1c01f
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/fs/mod.rs
@@ -0,0 +1,81 @@
+use futures::stream::BoxStream;
+use futures::StreamExt;
+use tonic::async_trait;
+use tvix_castore::fs::{RootNodes, TvixStoreFs};
+use tvix_castore::proto as castorepb;
+use tvix_castore::Error;
+use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
+
+use super::PathInfoService;
+
+/// Helper to construct a [TvixStoreFs] from a [BlobService], [DirectoryService]
+/// and [PathInfoService].
+/// This avoids users to have to interact with the wrapper struct directly, as
+/// it leaks into the type signature of TvixStoreFS.
+pub fn make_fs<BS, DS, PS>(
+    blob_service: BS,
+    directory_service: DS,
+    path_info_service: PS,
+    list_root: bool,
+    show_xattr: bool,
+) -> TvixStoreFs<BS, DS, RootNodesWrapper<PS>>
+where
+    BS: AsRef<dyn BlobService> + Send + Clone + 'static,
+    DS: AsRef<dyn DirectoryService> + Send + Clone + 'static,
+    PS: AsRef<dyn PathInfoService> + Send + Sync + Clone + 'static,
+{
+    TvixStoreFs::new(
+        blob_service,
+        directory_service,
+        RootNodesWrapper(path_info_service),
+        list_root,
+        show_xattr,
+    )
+}
+
+/// Wrapper to satisfy Rust's orphan rules for trait implementations, as
+/// RootNodes is coming from the [tvix-castore] crate.
+#[doc(hidden)]
+#[derive(Clone, Debug)]
+pub struct RootNodesWrapper<T>(pub(crate) T);
+
+/// Implements root node lookup for any [PathInfoService]. This represents a flat
+/// directory structure like /nix/store where each entry in the root filesystem
+/// directory corresponds to a CA node.
+#[cfg(any(feature = "fuse", feature = "virtiofs"))]
+#[async_trait]
+impl<T> RootNodes for RootNodesWrapper<T>
+where
+    T: AsRef<dyn PathInfoService> + Send + Sync,
+{
+    async fn get_by_basename(&self, name: &[u8]) -> Result<Option<castorepb::node::Node>, Error> {
+        let Ok(store_path) = nix_compat::store_path::StorePath::from_bytes(name) else {
+            return Ok(None);
+        };
+
+        Ok(self
+            .0
+            .as_ref()
+            .get(*store_path.digest())
+            .await?
+            .map(|path_info| {
+                path_info
+                    .node
+                    .expect("missing root node")
+                    .node
+                    .expect("empty node")
+            }))
+    }
+
+    fn list(&self) -> BoxStream<Result<castorepb::node::Node, Error>> {
+        Box::pin(self.0.as_ref().list().map(|result| {
+            result.map(|path_info| {
+                path_info
+                    .node
+                    .expect("missing root node")
+                    .node
+                    .expect("empty node")
+            })
+        }))
+    }
+}
diff --git a/tvix/store/src/pathinfoservice/grpc.rs b/tvix/store/src/pathinfoservice/grpc.rs
new file mode 100644
index 0000000000..1138ebdc19
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/grpc.rs
@@ -0,0 +1,216 @@
+use super::PathInfoService;
+use crate::proto::{self, ListPathInfoRequest, PathInfo};
+use async_stream::try_stream;
+use data_encoding::BASE64;
+use futures::stream::BoxStream;
+use tonic::{async_trait, transport::Channel, Code};
+use tracing::instrument;
+use tvix_castore::{proto as castorepb, Error};
+
+/// Connects to a (remote) tvix-store PathInfoService over gRPC.
+#[derive(Clone)]
+pub struct GRPCPathInfoService {
+    /// The internal reference to a gRPC client.
+    /// Cloning it is cheap, and it internally handles concurrent requests.
+    grpc_client: proto::path_info_service_client::PathInfoServiceClient<Channel>,
+}
+
+impl GRPCPathInfoService {
+    /// construct a [GRPCPathInfoService] from a [proto::path_info_service_client::PathInfoServiceClient].
+    /// panics if called outside the context of a tokio runtime.
+    pub fn from_client(
+        grpc_client: proto::path_info_service_client::PathInfoServiceClient<Channel>,
+    ) -> Self {
+        Self { grpc_client }
+    }
+}
+
+#[async_trait]
+impl PathInfoService for GRPCPathInfoService {
+    #[instrument(level = "trace", skip_all, fields(path_info.digest = BASE64.encode(&digest)))]
+    async fn get(&self, digest: [u8; 20]) -> Result<Option<PathInfo>, Error> {
+        let path_info = self
+            .grpc_client
+            .clone()
+            .get(proto::GetPathInfoRequest {
+                by_what: Some(proto::get_path_info_request::ByWhat::ByOutputHash(
+                    digest.to_vec().into(),
+                )),
+            })
+            .await;
+
+        match path_info {
+            Ok(path_info) => {
+                let path_info = path_info.into_inner();
+
+                path_info
+                    .validate()
+                    .map_err(|e| Error::StorageError(format!("invalid pathinfo: {}", e)))?;
+
+                Ok(Some(path_info))
+            }
+            Err(e) if e.code() == Code::NotFound => Ok(None),
+            Err(e) => Err(Error::StorageError(e.to_string())),
+        }
+    }
+
+    #[instrument(level = "trace", skip_all, fields(path_info.root_node = ?path_info.node))]
+    async fn put(&self, path_info: PathInfo) -> Result<PathInfo, Error> {
+        let path_info = self
+            .grpc_client
+            .clone()
+            .put(path_info)
+            .await
+            .map_err(|e| Error::StorageError(e.to_string()))?
+            .into_inner();
+
+        Ok(path_info)
+    }
+
+    #[instrument(level = "trace", skip_all, fields(root_node = ?root_node))]
+    async fn calculate_nar(
+        &self,
+        root_node: &castorepb::node::Node,
+    ) -> Result<(u64, [u8; 32]), Error> {
+        let path_info = self
+            .grpc_client
+            .clone()
+            .calculate_nar(castorepb::Node {
+                node: Some(root_node.clone()),
+            })
+            .await
+            .map_err(|e| Error::StorageError(e.to_string()))?
+            .into_inner();
+
+        let nar_sha256: [u8; 32] = path_info
+            .nar_sha256
+            .to_vec()
+            .try_into()
+            .map_err(|_e| Error::StorageError("invalid digest length".to_string()))?;
+
+        Ok((path_info.nar_size, nar_sha256))
+    }
+
+    #[instrument(level = "trace", skip_all)]
+    fn list(&self) -> BoxStream<'static, Result<PathInfo, Error>> {
+        let mut grpc_client = self.grpc_client.clone();
+
+        let stream = try_stream! {
+            let resp = grpc_client.list(ListPathInfoRequest::default()).await;
+
+            let mut stream = resp.map_err(|e| Error::StorageError(e.to_string()))?.into_inner();
+
+            loop {
+                match stream.message().await {
+                    Ok(o) => match o {
+                        Some(pathinfo) => {
+                            // validate the pathinfo
+                            if let Err(e) = pathinfo.validate() {
+                                Err(Error::StorageError(format!(
+                                    "pathinfo {:?} failed validation: {}",
+                                    pathinfo, e
+                                )))?;
+                            }
+                            yield pathinfo
+                        }
+                        None => {
+                            return;
+                        },
+                    },
+                    Err(e) => Err(Error::StorageError(e.to_string()))?,
+                }
+            }
+        };
+
+        Box::pin(stream)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::sync::Arc;
+    use std::time::Duration;
+
+    use rstest::*;
+    use tempfile::TempDir;
+    use tokio::net::UnixListener;
+    use tokio_retry::strategy::ExponentialBackoff;
+    use tokio_retry::Retry;
+    use tokio_stream::wrappers::UnixListenerStream;
+    use tvix_castore::blobservice::BlobService;
+    use tvix_castore::directoryservice::DirectoryService;
+
+    use crate::pathinfoservice::MemoryPathInfoService;
+    use crate::proto::path_info_service_client::PathInfoServiceClient;
+    use crate::proto::GRPCPathInfoServiceWrapper;
+    use crate::tests::fixtures::{self, blob_service, directory_service};
+
+    use super::GRPCPathInfoService;
+    use super::PathInfoService;
+
+    /// This ensures connecting via gRPC works as expected.
+    #[rstest]
+    #[tokio::test]
+    async fn test_valid_unix_path_ping_pong(
+        blob_service: Arc<dyn BlobService>,
+        directory_service: Arc<dyn DirectoryService>,
+    ) {
+        let tmpdir = TempDir::new().unwrap();
+        let socket_path = tmpdir.path().join("daemon");
+
+        let path_clone = socket_path.clone();
+
+        // Spin up a server
+        tokio::spawn(async {
+            let uds = UnixListener::bind(path_clone).unwrap();
+            let uds_stream = UnixListenerStream::new(uds);
+
+            // spin up a new server
+            let mut server = tonic::transport::Server::builder();
+            let router = server.add_service(
+                crate::proto::path_info_service_server::PathInfoServiceServer::new(
+                    GRPCPathInfoServiceWrapper::new(Box::new(MemoryPathInfoService::new(
+                        blob_service,
+                        directory_service,
+                    ))
+                        as Box<dyn PathInfoService>),
+                ),
+            );
+            router.serve_with_incoming(uds_stream).await
+        });
+
+        // wait for the socket to be created
+        Retry::spawn(
+            ExponentialBackoff::from_millis(20).max_delay(Duration::from_secs(10)),
+            || async {
+                if socket_path.exists() {
+                    Ok(())
+                } else {
+                    Err(())
+                }
+            },
+        )
+        .await
+        .expect("failed to wait for socket");
+
+        // prepare a client
+        let grpc_client = {
+            let url = url::Url::parse(&format!("grpc+unix://{}", socket_path.display()))
+                .expect("must parse");
+            let client = PathInfoServiceClient::new(
+                tvix_castore::tonic::channel_from_url(&url)
+                    .await
+                    .expect("must succeed"),
+            );
+
+            GRPCPathInfoService::from_client(client)
+        };
+
+        let path_info = grpc_client
+            .get(fixtures::DUMMY_PATH_DIGEST)
+            .await
+            .expect("must not be error");
+
+        assert!(path_info.is_none());
+    }
+}
diff --git a/tvix/store/src/pathinfoservice/memory.rs b/tvix/store/src/pathinfoservice/memory.rs
new file mode 100644
index 0000000000..f8435dbbf8
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/memory.rs
@@ -0,0 +1,84 @@
+use super::PathInfoService;
+use crate::{nar::calculate_size_and_sha256, proto::PathInfo};
+use futures::stream::{iter, BoxStream};
+use std::{
+    collections::HashMap,
+    sync::{Arc, RwLock},
+};
+use tonic::async_trait;
+use tvix_castore::proto as castorepb;
+use tvix_castore::Error;
+use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
+
+pub struct MemoryPathInfoService<BS, DS> {
+    db: Arc<RwLock<HashMap<[u8; 20], PathInfo>>>,
+
+    blob_service: BS,
+    directory_service: DS,
+}
+
+impl<BS, DS> MemoryPathInfoService<BS, DS> {
+    pub fn new(blob_service: BS, directory_service: DS) -> Self {
+        Self {
+            db: Default::default(),
+            blob_service,
+            directory_service,
+        }
+    }
+}
+
+#[async_trait]
+impl<BS, DS> PathInfoService for MemoryPathInfoService<BS, DS>
+where
+    BS: AsRef<dyn BlobService> + Send + Sync,
+    DS: AsRef<dyn DirectoryService> + Send + Sync,
+{
+    async fn get(&self, digest: [u8; 20]) -> Result<Option<PathInfo>, Error> {
+        let db = self.db.read().unwrap();
+
+        match db.get(&digest) {
+            None => Ok(None),
+            Some(path_info) => Ok(Some(path_info.clone())),
+        }
+    }
+
+    async fn put(&self, path_info: PathInfo) -> Result<PathInfo, Error> {
+        // Call validate on the received PathInfo message.
+        match path_info.validate() {
+            Err(e) => Err(Error::InvalidRequest(format!(
+                "failed to validate PathInfo: {}",
+                e
+            ))),
+
+            // In case the PathInfo is valid, and we were able to extract a NixPath, store it in the database.
+            // This overwrites existing PathInfo objects.
+            Ok(nix_path) => {
+                let mut db = self.db.write().unwrap();
+                db.insert(*nix_path.digest(), path_info.clone());
+
+                Ok(path_info)
+            }
+        }
+    }
+
+    async fn calculate_nar(
+        &self,
+        root_node: &castorepb::node::Node,
+    ) -> Result<(u64, [u8; 32]), Error> {
+        calculate_size_and_sha256(root_node, &self.blob_service, &self.directory_service)
+            .await
+            .map_err(|e| Error::StorageError(e.to_string()))
+    }
+
+    fn list(&self) -> BoxStream<'static, Result<PathInfo, Error>> {
+        let db = self.db.read().unwrap();
+
+        // Copy all elements into a list.
+        // This is a bit ugly, because we can't have db escape the lifetime
+        // of this function, but elements need to be returned owned anyways, and this in-
+        // memory impl is only for testing purposes anyways.
+        let items: Vec<_> = db.iter().map(|(_k, v)| Ok(v.clone())).collect();
+
+        Box::pin(iter(items))
+    }
+}
diff --git a/tvix/store/src/pathinfoservice/mod.rs b/tvix/store/src/pathinfoservice/mod.rs
new file mode 100644
index 0000000000..c1a482bbb5
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/mod.rs
@@ -0,0 +1,85 @@
+mod from_addr;
+mod grpc;
+mod memory;
+mod nix_http;
+mod sled;
+
+#[cfg(any(feature = "fuse", feature = "virtiofs"))]
+mod fs;
+
+#[cfg(test)]
+mod tests;
+
+use futures::stream::BoxStream;
+use tonic::async_trait;
+use tvix_castore::proto as castorepb;
+use tvix_castore::Error;
+
+use crate::proto::PathInfo;
+
+pub use self::from_addr::from_addr;
+pub use self::grpc::GRPCPathInfoService;
+pub use self::memory::MemoryPathInfoService;
+pub use self::nix_http::NixHTTPPathInfoService;
+pub use self::sled::SledPathInfoService;
+
+#[cfg(feature = "cloud")]
+mod bigtable;
+#[cfg(feature = "cloud")]
+pub use self::bigtable::BigtablePathInfoService;
+
+#[cfg(any(feature = "fuse", feature = "virtiofs"))]
+pub use self::fs::make_fs;
+
+/// The base trait all PathInfo services need to implement.
+#[async_trait]
+pub trait PathInfoService: Send + Sync {
+    /// Retrieve a PathInfo message by the output digest.
+    async fn get(&self, digest: [u8; 20]) -> Result<Option<PathInfo>, Error>;
+
+    /// Store a PathInfo message. Implementations MUST call validate and reject
+    /// invalid messages.
+    async fn put(&self, path_info: PathInfo) -> Result<PathInfo, Error>;
+
+    /// Return the nar size and nar sha256 digest for a given root node.
+    /// This can be used to calculate NAR-based output paths,
+    /// and implementations are encouraged to cache it.
+    async fn calculate_nar(
+        &self,
+        root_node: &castorepb::node::Node,
+    ) -> Result<(u64, [u8; 32]), Error>;
+
+    /// Iterate over all PathInfo objects in the store.
+    /// Implementations can decide to disallow listing.
+    ///
+    /// This returns a pinned, boxed stream. The pinning allows for it to be polled easily,
+    /// and the box allows different underlying stream implementations to be returned since
+    /// Rust doesn't support this as a generic in traits yet. This is the same thing that
+    /// [async_trait] generates, but for streams instead of futures.
+    fn list(&self) -> BoxStream<'static, Result<PathInfo, Error>>;
+}
+
+#[async_trait]
+impl<A> PathInfoService for A
+where
+    A: AsRef<dyn PathInfoService> + Send + Sync,
+{
+    async fn get(&self, digest: [u8; 20]) -> Result<Option<PathInfo>, Error> {
+        self.as_ref().get(digest).await
+    }
+
+    async fn put(&self, path_info: PathInfo) -> Result<PathInfo, Error> {
+        self.as_ref().put(path_info).await
+    }
+
+    async fn calculate_nar(
+        &self,
+        root_node: &castorepb::node::Node,
+    ) -> Result<(u64, [u8; 32]), Error> {
+        self.as_ref().calculate_nar(root_node).await
+    }
+
+    fn list(&self) -> BoxStream<'static, Result<PathInfo, Error>> {
+        self.as_ref().list()
+    }
+}
diff --git a/tvix/store/src/pathinfoservice/nix_http.rs b/tvix/store/src/pathinfoservice/nix_http.rs
new file mode 100644
index 0000000000..bdb0e2c3cb
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/nix_http.rs
@@ -0,0 +1,314 @@
+use std::io::{self, BufRead, Read, Write};
+
+use data_encoding::BASE64;
+use futures::{stream::BoxStream, TryStreamExt};
+use nix_compat::{
+    narinfo::{self, NarInfo},
+    nixbase32,
+    nixhash::NixHash,
+};
+use reqwest::StatusCode;
+use sha2::{digest::FixedOutput, Digest, Sha256};
+use tonic::async_trait;
+use tracing::{debug, instrument, warn};
+use tvix_castore::{
+    blobservice::BlobService, directoryservice::DirectoryService, proto as castorepb, Error,
+};
+
+use crate::proto::PathInfo;
+
+use super::PathInfoService;
+
+/// NixHTTPPathInfoService acts as a bridge in between the Nix HTTP Binary cache
+/// protocol provided by Nix binary caches such as cache.nixos.org, and the Tvix
+/// Store Model.
+/// It implements the [PathInfoService] trait in an interesting way:
+/// Every [PathInfoService::get] fetches the .narinfo and referred NAR file,
+/// inserting components into a [BlobService] and [DirectoryService], then
+/// returning a [PathInfo] struct with the root.
+///
+/// Due to this being quite a costly operation, clients are expected to layer
+/// this service with store composition, so they're only ingested once.
+///
+/// The client is expected to be (indirectly) using the same [BlobService] and
+/// [DirectoryService], so able to fetch referred Directories and Blobs.
+/// [PathInfoService::put] and [PathInfoService::calculate_nar] are not
+/// implemented and return an error if called.
+/// TODO: what about reading from nix-cache-info?
+pub struct NixHTTPPathInfoService<BS, DS> {
+    base_url: url::Url,
+    http_client: reqwest::Client,
+
+    blob_service: BS,
+    directory_service: DS,
+
+    /// An optional list of [narinfo::PubKey].
+    /// If set, the .narinfo files received need to have correct signature by at least one of these.
+    public_keys: Option<Vec<narinfo::PubKey>>,
+}
+
+impl<BS, DS> NixHTTPPathInfoService<BS, DS> {
+    pub fn new(base_url: url::Url, blob_service: BS, directory_service: DS) -> Self {
+        Self {
+            base_url,
+            http_client: reqwest::Client::new(),
+            blob_service,
+            directory_service,
+
+            public_keys: None,
+        }
+    }
+
+    /// Configures [Self] to validate NARInfo fingerprints with the public keys passed.
+    pub fn set_public_keys(&mut self, public_keys: Vec<narinfo::PubKey>) {
+        self.public_keys = Some(public_keys);
+    }
+}
+
+#[async_trait]
+impl<BS, DS> PathInfoService for NixHTTPPathInfoService<BS, DS>
+where
+    BS: AsRef<dyn BlobService> + Send + Sync + Clone + 'static,
+    DS: AsRef<dyn DirectoryService> + Send + Sync + Clone + 'static,
+{
+    #[instrument(skip_all, err, fields(path.digest=BASE64.encode(&digest)))]
+    async fn get(&self, digest: [u8; 20]) -> Result<Option<PathInfo>, Error> {
+        let narinfo_url = self
+            .base_url
+            .join(&format!("{}.narinfo", nixbase32::encode(&digest)))
+            .map_err(|e| {
+                warn!(e = %e, "unable to join URL");
+                io::Error::new(io::ErrorKind::InvalidInput, "unable to join url")
+            })?;
+
+        debug!(narinfo_url= %narinfo_url, "constructed NARInfo url");
+
+        let resp = self
+            .http_client
+            .get(narinfo_url)
+            .send()
+            .await
+            .map_err(|e| {
+                warn!(e=%e,"unable to send NARInfo request");
+                io::Error::new(
+                    io::ErrorKind::InvalidInput,
+                    "unable to send NARInfo request",
+                )
+            })?;
+
+        // In the case of a 404, return a NotFound.
+        // We also return a NotFound in case of a 403 - this is to match the behaviour as Nix,
+        // when querying nix-cache.s3.amazonaws.com directly, rather than cache.nixos.org.
+        if resp.status() == StatusCode::NOT_FOUND || resp.status() == StatusCode::FORBIDDEN {
+            return Ok(None);
+        }
+
+        let narinfo_str = resp.text().await.map_err(|e| {
+            warn!(e=%e,"unable to decode response as string");
+            io::Error::new(
+                io::ErrorKind::InvalidData,
+                "unable to decode response as string",
+            )
+        })?;
+
+        // parse the received narinfo
+        let narinfo = NarInfo::parse(&narinfo_str).map_err(|e| {
+            warn!(e=%e,"unable to parse response as NarInfo");
+            io::Error::new(
+                io::ErrorKind::InvalidData,
+                "unable to parse response as NarInfo",
+            )
+        })?;
+
+        // if [self.public_keys] is set, ensure there's at least one valid signature.
+        if let Some(public_keys) = &self.public_keys {
+            let fingerprint = narinfo.fingerprint();
+
+            if !public_keys.iter().any(|pubkey| {
+                narinfo
+                    .signatures
+                    .iter()
+                    .any(|sig| pubkey.verify(&fingerprint, sig))
+            }) {
+                warn!("no valid signature found");
+                Err(io::Error::new(
+                    io::ErrorKind::InvalidData,
+                    "no valid signature found",
+                ))?;
+            }
+        }
+
+        // Convert to a (sparse) PathInfo. We still need to populate the node field,
+        // and for this we need to download the NAR file.
+        // FUTUREWORK: Keep some database around mapping from narsha256 to
+        // (unnamed) rootnode, so we can use that (and the name from the
+        // StorePath) and avoid downloading the same NAR a second time.
+        let pathinfo: PathInfo = (&narinfo).into();
+
+        // create a request for the NAR file itself.
+        let nar_url = self.base_url.join(narinfo.url).map_err(|e| {
+            warn!(e = %e, "unable to join URL");
+            io::Error::new(io::ErrorKind::InvalidInput, "unable to join url")
+        })?;
+        debug!(nar_url= %nar_url, "constructed NAR url");
+
+        let resp = self
+            .http_client
+            .get(nar_url.clone())
+            .send()
+            .await
+            .map_err(|e| {
+                warn!(e=%e,"unable to send NAR request");
+                io::Error::new(io::ErrorKind::InvalidInput, "unable to send NAR request")
+            })?;
+
+        // if the request is not successful, return an error.
+        if !resp.status().is_success() {
+            return Err(Error::StorageError(format!(
+                "unable to retrieve NAR at {}, status {}",
+                nar_url,
+                resp.status()
+            )));
+        }
+
+        // get an AsyncRead of the response body.
+        let async_r = tokio_util::io::StreamReader::new(resp.bytes_stream().map_err(|e| {
+            let e = e.without_url();
+            warn!(e=%e, "failed to get response body");
+            io::Error::new(io::ErrorKind::BrokenPipe, e.to_string())
+        }));
+        let sync_r = tokio_util::io::SyncIoBridge::new(async_r);
+
+        // handle decompression, by wrapping the reader.
+        let sync_r: Box<dyn BufRead + Send> = match narinfo.compression {
+            Some("none") => Box::new(sync_r),
+            Some("xz") => Box::new(io::BufReader::new(xz2::read::XzDecoder::new(sync_r))),
+            Some(comp) => {
+                return Err(Error::InvalidRequest(
+                    format!("unsupported compression: {}", comp).to_string(),
+                ))
+            }
+            None => {
+                return Err(Error::InvalidRequest(
+                    "unsupported compression: bzip2".to_string(),
+                ))
+            }
+        };
+
+        let res = tokio::task::spawn_blocking({
+            let blob_service = self.blob_service.clone();
+            let directory_service = self.directory_service.clone();
+            move || -> io::Result<_> {
+                // Wrap the reader once more, so we can calculate NarSize and NarHash
+                let mut sync_r = io::BufReader::new(NarReader::from(sync_r));
+                let root_node = crate::nar::read_nar(&mut sync_r, blob_service, directory_service)?;
+
+                let (_, nar_hash, nar_size) = sync_r.into_inner().into_inner();
+
+                Ok((root_node, nar_hash, nar_size))
+            }
+        })
+        .await
+        .unwrap();
+
+        match res {
+            Ok((root_node, nar_hash, nar_size)) => {
+                // ensure the ingested narhash and narsize do actually match.
+                if narinfo.nar_size != nar_size {
+                    warn!(
+                        narinfo.nar_size = narinfo.nar_size,
+                        http.nar_size = nar_size,
+                        "NARSize mismatch"
+                    );
+                    Err(io::Error::new(
+                        io::ErrorKind::InvalidData,
+                        "NarSize mismatch".to_string(),
+                    ))?;
+                }
+                if narinfo.nar_hash != nar_hash {
+                    warn!(
+                        narinfo.nar_hash = %NixHash::Sha256(narinfo.nar_hash),
+                        http.nar_hash = %NixHash::Sha256(nar_hash),
+                        "NarHash mismatch"
+                    );
+                    Err(io::Error::new(
+                        io::ErrorKind::InvalidData,
+                        "NarHash mismatch".to_string(),
+                    ))?;
+                }
+
+                Ok(Some(PathInfo {
+                    node: Some(castorepb::Node {
+                        // set the name of the root node to the digest-name of the store path.
+                        node: Some(
+                            root_node.rename(narinfo.store_path.to_string().to_owned().into()),
+                        ),
+                    }),
+                    references: pathinfo.references,
+                    narinfo: pathinfo.narinfo,
+                }))
+            }
+            Err(e) => Err(e.into()),
+        }
+    }
+
+    #[instrument(skip_all, fields(path_info=?_path_info))]
+    async fn put(&self, _path_info: PathInfo) -> Result<PathInfo, Error> {
+        Err(Error::InvalidRequest(
+            "put not supported for this backend".to_string(),
+        ))
+    }
+
+    #[instrument(skip_all, fields(root_node=?root_node))]
+    async fn calculate_nar(
+        &self,
+        root_node: &castorepb::node::Node,
+    ) -> Result<(u64, [u8; 32]), Error> {
+        Err(Error::InvalidRequest(
+            "calculate_nar not supported for this backend".to_string(),
+        ))
+    }
+
+    fn list(&self) -> BoxStream<'static, Result<PathInfo, Error>> {
+        Box::pin(futures::stream::once(async {
+            Err(Error::InvalidRequest(
+                "list not supported for this backend".to_string(),
+            ))
+        }))
+    }
+}
+
+/// Small helper reader implementing [std::io::Read].
+/// It can be used to wrap another reader, counts the number of bytes read
+/// and the sha256 digest of the contents.
+struct NarReader<R: Read> {
+    r: R,
+
+    sha256: sha2::Sha256,
+    bytes_read: u64,
+}
+
+impl<R: Read> NarReader<R> {
+    pub fn from(inner: R) -> Self {
+        Self {
+            r: inner,
+            sha256: Sha256::new(),
+            bytes_read: 0,
+        }
+    }
+
+    /// Returns the (remaining) inner reader, the sha256 digest and the number of bytes read.
+    pub fn into_inner(self) -> (R, [u8; 32], u64) {
+        (self.r, self.sha256.finalize_fixed().into(), self.bytes_read)
+    }
+}
+
+impl<R: Read> Read for NarReader<R> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        self.r.read(buf).map(|n| {
+            self.bytes_read += n as u64;
+            self.sha256.write_all(&buf[..n]).unwrap();
+            n
+        })
+    }
+}
diff --git a/tvix/store/src/pathinfoservice/sled.rs b/tvix/store/src/pathinfoservice/sled.rs
new file mode 100644
index 0000000000..0255c031e2
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/sled.rs
@@ -0,0 +1,125 @@
+use super::PathInfoService;
+use crate::nar::calculate_size_and_sha256;
+use crate::proto::PathInfo;
+use data_encoding::BASE64;
+use futures::stream::iter;
+use futures::stream::BoxStream;
+use prost::Message;
+use std::path::Path;
+use tonic::async_trait;
+use tracing::instrument;
+use tracing::warn;
+use tvix_castore::proto as castorepb;
+use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService, Error};
+
+/// SledPathInfoService stores PathInfo in a [sled](https://github.com/spacejam/sled).
+///
+/// The PathInfo messages are stored as encoded protos, and keyed by their output hash,
+/// as that's currently the only request type available.
+pub struct SledPathInfoService<BS, DS> {
+    db: sled::Db,
+
+    blob_service: BS,
+    directory_service: DS,
+}
+
+impl<BS, DS> SledPathInfoService<BS, DS> {
+    pub fn new<P: AsRef<Path>>(
+        p: P,
+        blob_service: BS,
+        directory_service: DS,
+    ) -> Result<Self, sled::Error> {
+        let config = sled::Config::default()
+            .use_compression(false) // is a required parameter
+            .path(p);
+        let db = config.open()?;
+
+        Ok(Self {
+            db,
+            blob_service,
+            directory_service,
+        })
+    }
+
+    pub fn new_temporary(blob_service: BS, directory_service: DS) -> Result<Self, sled::Error> {
+        let config = sled::Config::default().temporary(true);
+        let db = config.open()?;
+
+        Ok(Self {
+            db,
+            blob_service,
+            directory_service,
+        })
+    }
+}
+
+#[async_trait]
+impl<BS, DS> PathInfoService for SledPathInfoService<BS, DS>
+where
+    BS: AsRef<dyn BlobService> + Send + Sync,
+    DS: AsRef<dyn DirectoryService> + Send + Sync,
+{
+    #[instrument(level = "trace", skip_all, fields(path_info.digest = BASE64.encode(&digest)))]
+    async fn get(&self, digest: [u8; 20]) -> Result<Option<PathInfo>, Error> {
+        match self.db.get(digest).map_err(|e| {
+            warn!("failed to retrieve PathInfo: {}", e);
+            Error::StorageError(format!("failed to retrieve PathInfo: {}", e))
+        })? {
+            None => Ok(None),
+            Some(data) => {
+                let path_info = PathInfo::decode(&*data).map_err(|e| {
+                    warn!("failed to decode stored PathInfo: {}", e);
+                    Error::StorageError(format!("failed to decode stored PathInfo: {}", e))
+                })?;
+                Ok(Some(path_info))
+            }
+        }
+    }
+
+    #[instrument(level = "trace", skip_all, fields(path_info.root_node = ?path_info.node))]
+    async fn put(&self, path_info: PathInfo) -> Result<PathInfo, Error> {
+        // Call validate on the received PathInfo message.
+        let store_path = path_info
+            .validate()
+            .map_err(|e| Error::InvalidRequest(format!("failed to validate PathInfo: {}", e)))?;
+
+        // In case the PathInfo is valid, we were able to parse a StorePath.
+        // Store it in the database, keyed by its digest.
+        // This overwrites existing PathInfo objects.
+        self.db
+            .insert(store_path.digest(), path_info.encode_to_vec())
+            .map_err(|e| {
+                warn!("failed to insert PathInfo: {}", e);
+                Error::StorageError(format! {
+                    "failed to insert PathInfo: {}", e
+                })
+            })?;
+
+        Ok(path_info)
+    }
+
+    #[instrument(level = "trace", skip_all, fields(root_node = ?root_node))]
+    async fn calculate_nar(
+        &self,
+        root_node: &castorepb::node::Node,
+    ) -> Result<(u64, [u8; 32]), Error> {
+        calculate_size_and_sha256(root_node, &self.blob_service, &self.directory_service)
+            .await
+            .map_err(|e| Error::StorageError(e.to_string()))
+    }
+
+    fn list(&self) -> BoxStream<'static, Result<PathInfo, Error>> {
+        Box::pin(iter(self.db.iter().values().map(|v| {
+            let data = v.map_err(|e| {
+                warn!("failed to retrieve PathInfo: {}", e);
+                Error::StorageError(format!("failed to retrieve PathInfo: {}", e))
+            })?;
+
+            let path_info = PathInfo::decode(&*data).map_err(|e| {
+                warn!("failed to decode stored PathInfo: {}", e);
+                Error::StorageError(format!("failed to decode stored PathInfo: {}", e))
+            })?;
+            Ok(path_info)
+        })))
+    }
+}
diff --git a/tvix/store/src/pathinfoservice/tests/mod.rs b/tvix/store/src/pathinfoservice/tests/mod.rs
new file mode 100644
index 0000000000..9719371592
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/tests/mod.rs
@@ -0,0 +1,113 @@
+//! This contains test scenarios that a given [PathInfoService] needs to pass.
+//! We use [rstest] and [rstest_reuse] to provide all services we want to test
+//! against, and then apply this template to all test functions.
+
+use rstest::*;
+use rstest_reuse::{self, *};
+use std::sync::Arc;
+use tvix_castore::proto as castorepb;
+use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
+
+use super::PathInfoService;
+use crate::proto::PathInfo;
+use crate::tests::fixtures::DUMMY_PATH_DIGEST;
+
+mod utils;
+use self::utils::make_grpc_path_info_service_client;
+
+/// Convenience type alias batching all three servives together.
+#[allow(clippy::upper_case_acronyms)]
+type BSDSPS = (
+    Arc<dyn BlobService>,
+    Arc<dyn DirectoryService>,
+    Box<dyn PathInfoService>,
+);
+
+/// Creates a PathInfoService using a new Memory{Blob,Directory}Service.
+/// We return a 3-tuple containing all of them, as some tests want to interact
+/// with all three.
+pub async fn make_path_info_service(uri: &str) -> BSDSPS {
+    let blob_service: Arc<dyn BlobService> = tvix_castore::blobservice::from_addr("memory://")
+        .await
+        .unwrap()
+        .into();
+    let directory_service: Arc<dyn DirectoryService> =
+        tvix_castore::directoryservice::from_addr("memory://")
+            .await
+            .unwrap()
+            .into();
+
+    (
+        blob_service.clone(),
+        directory_service.clone(),
+        crate::pathinfoservice::from_addr(uri, blob_service, directory_service)
+            .await
+            .unwrap(),
+    )
+}
+
+#[template]
+#[rstest]
+#[case::memory(make_path_info_service("memory://").await)]
+#[case::grpc(make_grpc_path_info_service_client().await)]
+#[case::sled(make_path_info_service("sled://").await)]
+#[cfg_attr(all(feature = "cloud",feature="integration"), case::bigtable(make_path_info_service("bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1").await))]
+pub fn path_info_services(
+    #[case] services: (
+        impl BlobService,
+        impl DirectoryService,
+        impl PathInfoService,
+    ),
+) {
+}
+
+// FUTUREWORK: add more tests rejecting invalid PathInfo messages.
+// A subset of them should also ensure references to other PathInfos, or
+// elements in {Blob,Directory}Service do exist.
+
+/// Trying to get a non-existent PathInfo should return Ok(None).
+#[apply(path_info_services)]
+#[tokio::test]
+async fn not_found(services: BSDSPS) {
+    let (_, _, path_info_service) = services;
+    assert!(path_info_service
+        .get(DUMMY_PATH_DIGEST)
+        .await
+        .expect("must succeed")
+        .is_none());
+}
+
+/// Put a PathInfo into the store, get it back.
+#[apply(path_info_services)]
+#[tokio::test]
+async fn put_get(services: BSDSPS) {
+    let (_, _, path_info_service) = services;
+
+    let path_info = PathInfo {
+        node: Some(castorepb::Node {
+            node: Some(castorepb::node::Node::Symlink(castorepb::SymlinkNode {
+                name: "00000000000000000000000000000000-foo".into(),
+                target: "doesntmatter".into(),
+            })),
+        }),
+        ..Default::default()
+    };
+
+    // insert
+    let resp = path_info_service
+        .put(path_info.clone())
+        .await
+        .expect("must succeed");
+
+    // expect the returned PathInfo to be equal (for now)
+    // in the future, some stores might add additional fields/signatures.
+    assert_eq!(path_info, resp);
+
+    // get it back
+    let resp = path_info_service
+        .get(DUMMY_PATH_DIGEST)
+        .await
+        .expect("must succeed");
+
+    assert_eq!(Some(path_info), resp);
+}
diff --git a/tvix/store/src/pathinfoservice/tests/utils.rs b/tvix/store/src/pathinfoservice/tests/utils.rs
new file mode 100644
index 0000000000..31ec57aade
--- /dev/null
+++ b/tvix/store/src/pathinfoservice/tests/utils.rs
@@ -0,0 +1,60 @@
+use std::sync::Arc;
+
+use tonic::transport::{Endpoint, Server, Uri};
+
+use crate::{
+    pathinfoservice::{GRPCPathInfoService, MemoryPathInfoService, PathInfoService},
+    proto::{
+        path_info_service_client::PathInfoServiceClient,
+        path_info_service_server::PathInfoServiceServer, GRPCPathInfoServiceWrapper,
+    },
+    tests::fixtures::{blob_service, directory_service},
+};
+
+/// Constructs and returns a gRPC PathInfoService.
+/// We also return memory-based {Blob,Directory}Service,
+/// as the consumer of this function accepts a 3-tuple.
+pub async fn make_grpc_path_info_service_client() -> super::BSDSPS {
+    let (left, right) = tokio::io::duplex(64);
+
+    let blob_service = blob_service();
+    let directory_service = directory_service();
+
+    // spin up a server, which will only connect once, to the left side.
+    tokio::spawn({
+        let blob_service = blob_service.clone();
+        let directory_service = directory_service.clone();
+        async move {
+            let path_info_service: Arc<dyn PathInfoService> =
+                Arc::from(MemoryPathInfoService::new(blob_service, directory_service));
+
+            // spin up a new DirectoryService
+            let mut server = Server::builder();
+            let router = server.add_service(PathInfoServiceServer::new(
+                GRPCPathInfoServiceWrapper::new(path_info_service),
+            ));
+
+            router
+                .serve_with_incoming(tokio_stream::once(Ok::<_, std::io::Error>(left)))
+                .await
+        }
+    });
+
+    // Create a client, connecting to the right side. The URI is unused.
+    let mut maybe_right = Some(right);
+
+    let path_info_service = Box::new(GRPCPathInfoService::from_client(
+        PathInfoServiceClient::new(
+            Endpoint::try_from("http://[::]:50051")
+                .unwrap()
+                .connect_with_connector(tower::service_fn(move |_: Uri| {
+                    let right = maybe_right.take().unwrap();
+                    async move { Ok::<_, std::io::Error>(right) }
+                }))
+                .await
+                .unwrap(),
+        ),
+    ));
+
+    (blob_service, directory_service, path_info_service)
+}
diff --git a/tvix/store/src/proto/grpc_pathinfoservice_wrapper.rs b/tvix/store/src/proto/grpc_pathinfoservice_wrapper.rs
new file mode 100644
index 0000000000..9f45818227
--- /dev/null
+++ b/tvix/store/src/proto/grpc_pathinfoservice_wrapper.rs
@@ -0,0 +1,121 @@
+use crate::nar::RenderError;
+use crate::pathinfoservice::PathInfoService;
+use crate::proto;
+use futures::{stream::BoxStream, TryStreamExt};
+use std::ops::Deref;
+use tonic::{async_trait, Request, Response, Result, Status};
+use tracing::{instrument, warn};
+use tvix_castore::proto as castorepb;
+
+pub struct GRPCPathInfoServiceWrapper<PS> {
+    inner: PS,
+    // FUTUREWORK: allow exposing without allowing listing
+}
+
+impl<PS> GRPCPathInfoServiceWrapper<PS> {
+    pub fn new(path_info_service: PS) -> Self {
+        Self {
+            inner: path_info_service,
+        }
+    }
+}
+
+#[async_trait]
+impl<PS> proto::path_info_service_server::PathInfoService for GRPCPathInfoServiceWrapper<PS>
+where
+    PS: Deref<Target = dyn PathInfoService> + Send + Sync + 'static,
+{
+    type ListStream = BoxStream<'static, tonic::Result<proto::PathInfo, Status>>;
+
+    #[instrument(skip_all)]
+    async fn get(
+        &self,
+        request: Request<proto::GetPathInfoRequest>,
+    ) -> Result<Response<proto::PathInfo>> {
+        match request.into_inner().by_what {
+            None => Err(Status::unimplemented("by_what needs to be specified")),
+            Some(proto::get_path_info_request::ByWhat::ByOutputHash(output_digest)) => {
+                let digest: [u8; 20] = output_digest
+                    .to_vec()
+                    .try_into()
+                    .map_err(|_e| Status::invalid_argument("invalid output digest length"))?;
+                match self.inner.get(digest).await {
+                    Ok(None) => Err(Status::not_found("PathInfo not found")),
+                    Ok(Some(path_info)) => Ok(Response::new(path_info)),
+                    Err(e) => {
+                        warn!(err = %e, "failed to get PathInfo");
+                        Err(e.into())
+                    }
+                }
+            }
+        }
+    }
+
+    #[instrument(skip_all)]
+    async fn put(&self, request: Request<proto::PathInfo>) -> Result<Response<proto::PathInfo>> {
+        let path_info = request.into_inner();
+
+        // Store the PathInfo in the client. Clients MUST validate the data
+        // they receive, so we don't validate additionally here.
+        match self.inner.put(path_info).await {
+            Ok(path_info_new) => Ok(Response::new(path_info_new)),
+            Err(e) => {
+                warn!(err = %e, "failed to put PathInfo");
+                Err(e.into())
+            }
+        }
+    }
+
+    #[instrument(skip_all)]
+    async fn calculate_nar(
+        &self,
+        request: Request<castorepb::Node>,
+    ) -> Result<Response<proto::CalculateNarResponse>> {
+        match request.into_inner().node {
+            None => Err(Status::invalid_argument("no root node sent")),
+            Some(root_node) => {
+                if let Err(e) = root_node.validate() {
+                    warn!(err = %e, "invalid root node");
+                    Err(Status::invalid_argument("invalid root node"))?
+                }
+
+                match self.inner.calculate_nar(&root_node).await {
+                    Ok((nar_size, nar_sha256)) => Ok(Response::new(proto::CalculateNarResponse {
+                        nar_size,
+                        nar_sha256: nar_sha256.to_vec().into(),
+                    })),
+                    Err(e) => {
+                        warn!(err = %e, "error during NAR calculation");
+                        Err(e.into())
+                    }
+                }
+            }
+        }
+    }
+
+    #[instrument(skip_all, err)]
+    async fn list(
+        &self,
+        _request: Request<proto::ListPathInfoRequest>,
+    ) -> Result<Response<Self::ListStream>, Status> {
+        let stream = Box::pin(
+            self.inner
+                .list()
+                .map_err(|e| Status::internal(e.to_string())),
+        );
+
+        Ok(Response::new(Box::pin(stream)))
+    }
+}
+
+impl From<RenderError> for tonic::Status {
+    fn from(value: RenderError) -> Self {
+        match value {
+            RenderError::BlobNotFound(_, _) => Self::not_found(value.to_string()),
+            RenderError::DirectoryNotFound(_, _) => Self::not_found(value.to_string()),
+            RenderError::NARWriterError(_) => Self::internal(value.to_string()),
+            RenderError::StoreError(_) => Self::internal(value.to_string()),
+            RenderError::UnexpectedBlobMeta(_, _, _, _) => Self::internal(value.to_string()),
+        }
+    }
+}
diff --git a/tvix/store/src/proto/mod.rs b/tvix/store/src/proto/mod.rs
new file mode 100644
index 0000000000..a09839c8bd
--- /dev/null
+++ b/tvix/store/src/proto/mod.rs
@@ -0,0 +1,374 @@
+#![allow(clippy::derive_partial_eq_without_eq, non_snake_case)]
+use bstr::ByteSlice;
+use bytes::Bytes;
+use data_encoding::BASE64;
+// https://github.com/hyperium/tonic/issues/1056
+use nix_compat::{
+    narinfo::Flags,
+    nixhash::{CAHash, NixHash},
+    store_path::{self, StorePathRef},
+};
+use thiserror::Error;
+use tvix_castore::proto::{self as castorepb, NamedNode, ValidateNodeError};
+
+mod grpc_pathinfoservice_wrapper;
+
+pub use grpc_pathinfoservice_wrapper::GRPCPathInfoServiceWrapper;
+
+tonic::include_proto!("tvix.store.v1");
+
+#[cfg(feature = "tonic-reflection")]
+/// Compiled file descriptors for implementing [gRPC
+/// reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) with e.g.
+/// [`tonic_reflection`](https://docs.rs/tonic-reflection).
+pub const FILE_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("tvix.store.v1");
+
+#[cfg(test)]
+mod tests;
+
+/// Errors that can occur during the validation of PathInfo messages.
+#[derive(Debug, Error, PartialEq)]
+pub enum ValidatePathInfoError {
+    /// Invalid length of a reference
+    #[error("Invalid length of digest at position {}, expected {}, got {}", .0, store_path::DIGEST_SIZE, .1)]
+    InvalidReferenceDigestLen(usize, usize),
+
+    /// No node present
+    #[error("No node present")]
+    NoNodePresent,
+
+    /// Node fails validation
+    #[error("Invalid root node: {:?}", .0.to_string())]
+    InvalidRootNode(ValidateNodeError),
+
+    /// Invalid node name encountered. Root nodes in PathInfos have more strict name requirements
+    #[error("Failed to parse {} as StorePath: {1}", .0.to_str_lossy())]
+    InvalidNodeName(Vec<u8>, store_path::Error),
+
+    /// The digest in narinfo.nar_sha256 has an invalid len.
+    #[error("Invalid narinfo.nar_sha256 length: expected {}, got {}", 32, .0)]
+    InvalidNarSha256DigestLen(usize),
+
+    /// The number of references in the narinfo.reference_names field does not match
+    /// the number of references in the .references field.
+    #[error("Inconsistent Number of References: {0} (references) vs {1} (narinfo)")]
+    InconsistentNumberOfReferences(usize, usize),
+
+    /// A string in narinfo.reference_names does not parse to a [store_path::StorePath].
+    #[error("Invalid reference_name at position {0}: {1}")]
+    InvalidNarinfoReferenceName(usize, String),
+
+    /// The digest in the parsed `.narinfo.reference_names[i]` does not match
+    /// the one in `.references[i]`.`
+    #[error("digest in reference_name at position {} does not match digest in PathInfo, expected {}, got {}", .0, BASE64.encode(.1), BASE64.encode(.2))]
+    InconsistentNarinfoReferenceNameDigest(
+        usize,
+        [u8; store_path::DIGEST_SIZE],
+        [u8; store_path::DIGEST_SIZE],
+    ),
+
+    /// The deriver field is invalid.
+    #[error("deriver field is invalid: {0}")]
+    InvalidDeriverField(store_path::Error),
+}
+
+/// Parses a root node name.
+///
+/// On success, this returns the parsed [store_path::StorePathRef].
+/// On error, it returns an error generated from the supplied constructor.
+fn parse_node_name_root<E>(
+    name: &[u8],
+    err: fn(Vec<u8>, store_path::Error) -> E,
+) -> Result<store_path::StorePathRef<'_>, E> {
+    store_path::StorePathRef::from_bytes(name).map_err(|e| err(name.to_vec(), e))
+}
+
+impl PathInfo {
+    /// validate performs some checks on the PathInfo struct,
+    /// Returning either a [store_path::StorePath] of the root node, or a
+    /// [ValidatePathInfoError].
+    pub fn validate(&self) -> Result<store_path::StorePathRef<'_>, ValidatePathInfoError> {
+        // ensure the references have the right number of bytes.
+        for (i, reference) in self.references.iter().enumerate() {
+            if reference.len() != store_path::DIGEST_SIZE {
+                return Err(ValidatePathInfoError::InvalidReferenceDigestLen(
+                    i,
+                    reference.len(),
+                ));
+            }
+        }
+
+        // If there is a narinfo field populated…
+        if let Some(narinfo) = &self.narinfo {
+            // ensure the nar_sha256 digest has the correct length.
+            if narinfo.nar_sha256.len() != 32 {
+                return Err(ValidatePathInfoError::InvalidNarSha256DigestLen(
+                    narinfo.nar_sha256.len(),
+                ));
+            }
+
+            // ensure the number of references there matches PathInfo.references count.
+            if narinfo.reference_names.len() != self.references.len() {
+                return Err(ValidatePathInfoError::InconsistentNumberOfReferences(
+                    self.references.len(),
+                    narinfo.reference_names.len(),
+                ));
+            }
+
+            // parse references in reference_names.
+            for (i, reference_name_str) in narinfo.reference_names.iter().enumerate() {
+                // ensure thy parse as (non-absolute) store path
+                let reference_names_store_path = store_path::StorePath::from_bytes(
+                    reference_name_str.as_bytes(),
+                )
+                .map_err(|_| {
+                    ValidatePathInfoError::InvalidNarinfoReferenceName(
+                        i,
+                        reference_name_str.to_owned(),
+                    )
+                })?;
+
+                // ensure their digest matches the one at self.references[i].
+                {
+                    // This is safe, because we ensured the proper length earlier already.
+                    let reference_digest = self.references[i].to_vec().try_into().unwrap();
+
+                    if reference_names_store_path.digest() != &reference_digest {
+                        return Err(
+                            ValidatePathInfoError::InconsistentNarinfoReferenceNameDigest(
+                                i,
+                                reference_digest,
+                                *reference_names_store_path.digest(),
+                            ),
+                        );
+                    }
+                }
+
+                // If the Deriver field is populated, ensure it parses to a
+                // [store_path::StorePath].
+                // We can't check for it to *not* end with .drv, as the .drv files produced by
+                // recursive Nix end with multiple .drv suffixes, and only one is popped when
+                // converting to this field.
+                if let Some(deriver) = &narinfo.deriver {
+                    store_path::StorePathRef::from_name_and_digest(&deriver.name, &deriver.digest)
+                        .map_err(ValidatePathInfoError::InvalidDeriverField)?;
+                }
+            }
+        }
+
+        // Ensure there is a (root) node present, and it properly parses to a [store_path::StorePath].
+        let root_nix_path = match &self.node {
+            None | Some(castorepb::Node { node: None }) => {
+                Err(ValidatePathInfoError::NoNodePresent)?
+            }
+            Some(castorepb::Node { node: Some(node) }) => {
+                node.validate()
+                    .map_err(ValidatePathInfoError::InvalidRootNode)?;
+                // parse the name of the node itself and return
+                parse_node_name_root(node.get_name(), ValidatePathInfoError::InvalidNodeName)?
+            }
+        };
+
+        // return the root nix path
+        Ok(root_nix_path)
+    }
+
+    /// With self and its store path name, this reconstructs a
+    /// [nix_compat::narinfo::NarInfo<'_>].
+    /// It can be used to validate Signatures, or get back a (sparse) NarInfo
+    /// struct to prepare writing it out.
+    ///
+    /// It assumes self to be validated first, and will only return None if the
+    /// `narinfo` field is unpopulated.
+    ///
+    /// It does very little allocation (a Vec each for `signatures` and
+    /// `references`), the rest points to data owned elsewhere.
+    ///
+    /// Keep in mind this is not able to reconstruct all data present in the
+    /// NarInfo<'_>, as some of it is not stored at all:
+    /// - the `system`, `file_hash` and `file_size` fields are set to `None`.
+    /// - the URL is set to an empty string.
+    /// - Compression is set to "none"
+    ///
+    /// If you want to render it out to a string and be able to parse it back
+    /// in, at least URL *must* be set again.
+    pub fn to_narinfo<'a>(
+        &'a self,
+        store_path: store_path::StorePathRef<'a>,
+    ) -> Option<nix_compat::narinfo::NarInfo<'_>> {
+        let narinfo = &self.narinfo.as_ref()?;
+
+        Some(nix_compat::narinfo::NarInfo {
+            flags: Flags::empty(),
+            store_path,
+            nar_hash: narinfo
+                .nar_sha256
+                .as_ref()
+                .try_into()
+                .expect("invalid narhash"),
+            nar_size: narinfo.nar_size,
+            references: narinfo
+                .reference_names
+                .iter()
+                .map(|ref_name| {
+                    // This shouldn't pass validation
+                    StorePathRef::from_bytes(ref_name.as_bytes()).expect("invalid reference")
+                })
+                .collect(),
+            signatures: narinfo
+                .signatures
+                .iter()
+                .map(|sig| {
+                    nix_compat::narinfo::Signature::new(
+                        &sig.name,
+                        // This shouldn't pass validation
+                        sig.data[..].try_into().expect("invalid signature len"),
+                    )
+                })
+                .collect(),
+            ca: narinfo
+                .ca
+                .as_ref()
+                .map(|ca| ca.try_into().expect("invalid ca")),
+            system: None,
+            deriver: narinfo.deriver.as_ref().map(|deriver| {
+                StorePathRef::from_name_and_digest(&deriver.name, &deriver.digest)
+                    .expect("invalid deriver")
+            }),
+            url: "",
+            compression: Some("none"),
+            file_hash: None,
+            file_size: None,
+        })
+    }
+}
+
+/// Errors that can occur when converting from a [nar_info::Ca] to a (stricter)
+/// [nix_compat::nixhash::CAHash].
+#[derive(Debug, Error, PartialEq)]
+pub enum ConvertCAError {
+    /// Invalid length of a reference
+    #[error("Invalid digest length '{0}' for type {1}")]
+    InvalidReferenceDigestLen(usize, &'static str),
+    /// Unknown Hash type
+    #[error("Unknown hash type: {0}")]
+    UnknownHashType(i32),
+}
+
+impl TryFrom<&nar_info::Ca> for nix_compat::nixhash::CAHash {
+    type Error = ConvertCAError;
+
+    fn try_from(value: &nar_info::Ca) -> Result<Self, Self::Error> {
+        Ok(match value.r#type {
+            typ if typ == nar_info::ca::Hash::FlatMd5 as i32 => {
+                Self::Flat(NixHash::Md5(value.digest[..].try_into().map_err(|_| {
+                    ConvertCAError::InvalidReferenceDigestLen(value.digest.len(), "FlatMd5")
+                })?))
+            }
+            typ if typ == nar_info::ca::Hash::FlatSha1 as i32 => {
+                Self::Flat(NixHash::Sha1(value.digest[..].try_into().map_err(
+                    |_| ConvertCAError::InvalidReferenceDigestLen(value.digest.len(), "FlatSha1"),
+                )?))
+            }
+            typ if typ == nar_info::ca::Hash::FlatSha256 as i32 => {
+                Self::Flat(NixHash::Sha256(value.digest[..].try_into().map_err(
+                    |_| ConvertCAError::InvalidReferenceDigestLen(value.digest.len(), "FlatSha256"),
+                )?))
+            }
+            typ if typ == nar_info::ca::Hash::FlatSha512 as i32 => Self::Flat(NixHash::Sha512(
+                Box::new(value.digest[..].try_into().map_err(|_| {
+                    ConvertCAError::InvalidReferenceDigestLen(value.digest.len(), "FlatSha512")
+                })?),
+            )),
+            typ if typ == nar_info::ca::Hash::NarMd5 as i32 => {
+                Self::Nar(NixHash::Md5(value.digest[..].try_into().map_err(|_| {
+                    ConvertCAError::InvalidReferenceDigestLen(value.digest.len(), "NarMd5")
+                })?))
+            }
+            typ if typ == nar_info::ca::Hash::NarSha1 as i32 => {
+                Self::Nar(NixHash::Sha1(value.digest[..].try_into().map_err(
+                    |_| ConvertCAError::InvalidReferenceDigestLen(value.digest.len(), "NarSha1"),
+                )?))
+            }
+            typ if typ == nar_info::ca::Hash::NarSha256 as i32 => {
+                Self::Nar(NixHash::Sha256(value.digest[..].try_into().map_err(
+                    |_| ConvertCAError::InvalidReferenceDigestLen(value.digest.len(), "NarSha256"),
+                )?))
+            }
+            typ if typ == nar_info::ca::Hash::NarSha512 as i32 => Self::Nar(NixHash::Sha512(
+                Box::new(value.digest[..].try_into().map_err(|_| {
+                    ConvertCAError::InvalidReferenceDigestLen(value.digest.len(), "NarSha512")
+                })?),
+            )),
+            typ => return Err(ConvertCAError::UnknownHashType(typ)),
+        })
+    }
+}
+
+impl From<&nix_compat::nixhash::CAHash> for nar_info::ca::Hash {
+    fn from(value: &nix_compat::nixhash::CAHash) -> Self {
+        match value {
+            CAHash::Flat(NixHash::Md5(_)) => nar_info::ca::Hash::FlatMd5,
+            CAHash::Flat(NixHash::Sha1(_)) => nar_info::ca::Hash::FlatSha1,
+            CAHash::Flat(NixHash::Sha256(_)) => nar_info::ca::Hash::FlatSha256,
+            CAHash::Flat(NixHash::Sha512(_)) => nar_info::ca::Hash::FlatSha512,
+            CAHash::Nar(NixHash::Md5(_)) => nar_info::ca::Hash::NarMd5,
+            CAHash::Nar(NixHash::Sha1(_)) => nar_info::ca::Hash::NarSha1,
+            CAHash::Nar(NixHash::Sha256(_)) => nar_info::ca::Hash::NarSha256,
+            CAHash::Nar(NixHash::Sha512(_)) => nar_info::ca::Hash::NarSha512,
+            CAHash::Text(_) => nar_info::ca::Hash::TextSha256,
+        }
+    }
+}
+
+impl From<&nix_compat::nixhash::CAHash> for nar_info::Ca {
+    fn from(value: &nix_compat::nixhash::CAHash) -> Self {
+        nar_info::Ca {
+            r#type: Into::<nar_info::ca::Hash>::into(value) as i32,
+            digest: value.hash().digest_as_bytes().to_vec().into(),
+        }
+    }
+}
+
+impl From<&nix_compat::narinfo::NarInfo<'_>> for NarInfo {
+    /// Converts from a NarInfo (returned from the NARInfo parser) to the proto-
+    /// level NarInfo struct.
+    fn from(value: &nix_compat::narinfo::NarInfo<'_>) -> Self {
+        let signatures = value
+            .signatures
+            .iter()
+            .map(|sig| nar_info::Signature {
+                name: sig.name().to_string(),
+                data: Bytes::copy_from_slice(sig.bytes()),
+            })
+            .collect();
+
+        NarInfo {
+            nar_size: value.nar_size,
+            nar_sha256: Bytes::copy_from_slice(&value.nar_hash),
+            signatures,
+            reference_names: value.references.iter().map(|r| r.to_string()).collect(),
+            deriver: value.deriver.as_ref().map(|sp| StorePath {
+                name: sp.name().to_owned(),
+                digest: Bytes::copy_from_slice(sp.digest()),
+            }),
+            ca: value.ca.as_ref().map(|ca| ca.into()),
+        }
+    }
+}
+
+impl From<&nix_compat::narinfo::NarInfo<'_>> for PathInfo {
+    /// Converts from a NarInfo (returned from the NARInfo parser) to a PathInfo
+    /// struct with the node set to None.
+    fn from(value: &nix_compat::narinfo::NarInfo<'_>) -> Self {
+        Self {
+            node: None,
+            references: value
+                .references
+                .iter()
+                .map(|x| Bytes::copy_from_slice(x.digest()))
+                .collect(),
+            narinfo: Some(value.into()),
+        }
+    }
+}
diff --git a/tvix/store/src/proto/tests/mod.rs b/tvix/store/src/proto/tests/mod.rs
new file mode 100644
index 0000000000..c9c6702027
--- /dev/null
+++ b/tvix/store/src/proto/tests/mod.rs
@@ -0,0 +1 @@
+mod pathinfo;
diff --git a/tvix/store/src/proto/tests/pathinfo.rs b/tvix/store/src/proto/tests/pathinfo.rs
new file mode 100644
index 0000000000..4d0834878d
--- /dev/null
+++ b/tvix/store/src/proto/tests/pathinfo.rs
@@ -0,0 +1,431 @@
+use crate::proto::{nar_info::Signature, NarInfo, PathInfo, ValidatePathInfoError};
+use crate::tests::fixtures::*;
+use bytes::Bytes;
+use data_encoding::BASE64;
+use nix_compat::nixbase32;
+use nix_compat::store_path::{self, StorePathRef};
+use rstest::rstest;
+use tvix_castore::proto as castorepb;
+
+#[rstest]
+#[case::no_node(None, Err(ValidatePathInfoError::NoNodePresent))]
+#[case::no_node_2(Some(castorepb::Node { node: None}), Err(ValidatePathInfoError::NoNodePresent))]
+
+fn validate_pathinfo(
+    #[case] node: Option<castorepb::Node>,
+    #[case] exp_result: Result<StorePathRef, ValidatePathInfoError>,
+) {
+    // construct the PathInfo object
+    let p = PathInfo {
+        node,
+        ..Default::default()
+    };
+
+    assert_eq!(exp_result, p.validate());
+
+    let err = p.validate().expect_err("validation should fail");
+    assert!(matches!(err, ValidatePathInfoError::NoNodePresent));
+}
+
+#[rstest]
+#[case::ok(castorepb::DirectoryNode {
+        name: DUMMY_PATH.into(),
+        digest: DUMMY_DIGEST.clone().into(),
+        size: 0,
+}, Ok(StorePathRef::from_bytes(DUMMY_PATH.as_bytes()).unwrap()))]
+#[case::invalid_digest_length(castorepb::DirectoryNode {
+        name: DUMMY_PATH.into(),
+        digest: Bytes::new(),
+        size: 0,
+}, Err(ValidatePathInfoError::InvalidRootNode(castorepb::ValidateNodeError::InvalidDigestLen(0))))]
+#[case::invalid_node_name_no_storepath(castorepb::DirectoryNode {
+        name: "invalid".into(),
+        digest: DUMMY_DIGEST.clone().into(),
+        size: 0,
+}, Err(ValidatePathInfoError::InvalidNodeName(
+        "invalid".into(),
+        store_path::Error::InvalidLength
+)))]
+fn validate_directory(
+    #[case] directory_node: castorepb::DirectoryNode,
+    #[case] exp_result: Result<StorePathRef, ValidatePathInfoError>,
+) {
+    // construct the PathInfo object
+    let p = PathInfo {
+        node: Some(castorepb::Node {
+            node: Some(castorepb::node::Node::Directory(directory_node)),
+        }),
+        ..Default::default()
+    };
+    assert_eq!(exp_result, p.validate());
+}
+
+#[rstest]
+#[case::ok(
+    castorepb::FileNode {
+        name: DUMMY_PATH.into(),
+        digest: DUMMY_DIGEST.clone().into(),
+        size: 0,
+        executable: false,
+    },
+    Ok(StorePathRef::from_bytes(DUMMY_PATH.as_bytes()).unwrap())
+)]
+#[case::invalid_digest_len(
+    castorepb::FileNode {
+        name: DUMMY_PATH.into(),
+        digest: Bytes::new(),
+        ..Default::default()
+    },
+    Err(ValidatePathInfoError::InvalidRootNode(castorepb::ValidateNodeError::InvalidDigestLen(0)))
+)]
+#[case::invalid_node_name(
+    castorepb::FileNode {
+        name: "invalid".into(),
+        digest: DUMMY_DIGEST.clone().into(),
+        ..Default::default()
+    },
+    Err(ValidatePathInfoError::InvalidNodeName(
+        "invalid".into(),
+        store_path::Error::InvalidLength
+    ))
+)]
+fn validate_file(
+    #[case] file_node: castorepb::FileNode,
+    #[case] exp_result: Result<StorePathRef, ValidatePathInfoError>,
+) {
+    // construct the PathInfo object
+    let p = PathInfo {
+        node: Some(castorepb::Node {
+            node: Some(castorepb::node::Node::File(file_node)),
+        }),
+        ..Default::default()
+    };
+    assert_eq!(exp_result, p.validate());
+}
+
+#[rstest]
+#[case::ok(
+    castorepb::SymlinkNode {
+        name: DUMMY_PATH.into(),
+        target: "foo".into(),
+    },
+    Ok(StorePathRef::from_bytes(DUMMY_PATH.as_bytes()).unwrap())
+)]
+#[case::invalid_node_name(
+    castorepb::SymlinkNode {
+        name: "invalid".into(),
+        target: "foo".into(),
+    },
+    Err(ValidatePathInfoError::InvalidNodeName(
+        "invalid".into(),
+        store_path::Error::InvalidLength
+    ))
+)]
+fn validate_symlink(
+    #[case] symlink_node: castorepb::SymlinkNode,
+    #[case] exp_result: Result<StorePathRef, ValidatePathInfoError>,
+) {
+    // construct the PathInfo object
+    let p = PathInfo {
+        node: Some(castorepb::Node {
+            node: Some(castorepb::node::Node::Symlink(symlink_node)),
+        }),
+        ..Default::default()
+    };
+    assert_eq!(exp_result, p.validate());
+}
+
+/// Ensure parsing a correct PathInfo without narinfo populated succeeds.
+#[test]
+fn validate_references_without_narinfo_ok() {
+    assert!(PATH_INFO_WITHOUT_NARINFO.validate().is_ok());
+}
+
+/// Ensure parsing a correct PathInfo with narinfo populated succeeds.
+#[test]
+fn validate_references_with_narinfo_ok() {
+    assert!(PATH_INFO_WITH_NARINFO.validate().is_ok());
+}
+
+/// Create a PathInfo with a wrong digest length in narinfo.nar_sha256, and
+/// ensure validation fails.
+#[test]
+fn validate_wrong_nar_sha256() {
+    let mut path_info = PATH_INFO_WITH_NARINFO.clone();
+    path_info.narinfo.as_mut().unwrap().nar_sha256 = vec![0xbe, 0xef].into();
+
+    match path_info.validate().expect_err("must_fail") {
+        ValidatePathInfoError::InvalidNarSha256DigestLen(2) => {}
+        e => panic!("unexpected error: {:?}", e),
+    };
+}
+
+/// Create a PathInfo with a wrong count of narinfo.reference_names,
+/// and ensure validation fails.
+#[test]
+fn validate_inconsistent_num_refs_fail() {
+    let mut path_info = PATH_INFO_WITH_NARINFO.clone();
+    path_info.narinfo.as_mut().unwrap().reference_names = vec![];
+
+    match path_info.validate().expect_err("must_fail") {
+        ValidatePathInfoError::InconsistentNumberOfReferences(1, 0) => {}
+        e => panic!("unexpected error: {:?}", e),
+    };
+}
+
+/// Create a PathInfo with a wrong digest length in references.
+#[test]
+fn validate_invalid_reference_digest_len() {
+    let mut path_info = PATH_INFO_WITHOUT_NARINFO.clone();
+    path_info.references.push(vec![0xff, 0xff].into());
+
+    match path_info.validate().expect_err("must fail") {
+        ValidatePathInfoError::InvalidReferenceDigestLen(
+            1, // position
+            2, // unexpected digest len
+        ) => {}
+        e => panic!("unexpected error: {:?}", e),
+    };
+}
+
+/// Create a PathInfo with a narinfo.reference_name[1] that is no valid store path.
+#[test]
+fn validate_invalid_narinfo_reference_name() {
+    let mut path_info = PATH_INFO_WITH_NARINFO.clone();
+
+    // This is invalid, as the store prefix is not part of reference_names.
+    path_info.narinfo.as_mut().unwrap().reference_names[0] =
+        "/nix/store/00000000000000000000000000000000-dummy".to_string();
+
+    match path_info.validate().expect_err("must fail") {
+        ValidatePathInfoError::InvalidNarinfoReferenceName(0, reference_name) => {
+            assert_eq!(
+                "/nix/store/00000000000000000000000000000000-dummy",
+                reference_name
+            );
+        }
+        e => panic!("unexpected error: {:?}", e),
+    }
+}
+
+/// Create a PathInfo with a narinfo.reference_name[0] that doesn't match references[0].
+#[test]
+fn validate_inconsistent_narinfo_reference_name_digest() {
+    let mut path_info = PATH_INFO_WITH_NARINFO.clone();
+
+    // mutate the first reference, they were all zeroes before
+    path_info.references[0] = vec![0xff; store_path::DIGEST_SIZE].into();
+
+    match path_info.validate().expect_err("must fail") {
+        ValidatePathInfoError::InconsistentNarinfoReferenceNameDigest(0, e_expected, e_actual) => {
+            assert_eq!(path_info.references[0][..], e_expected[..]);
+            assert_eq!(DUMMY_PATH_DIGEST, e_actual);
+        }
+        e => panic!("unexpected error: {:?}", e),
+    }
+}
+
+/// Create a node with an empty symlink target, and ensure it fails validation.
+#[test]
+fn validate_symlink_empty_target_invalid() {
+    let node = castorepb::node::Node::Symlink(castorepb::SymlinkNode {
+        name: "foo".into(),
+        target: "".into(),
+    });
+
+    node.validate().expect_err("must fail validation");
+}
+
+/// Create a node with a symlink target including null bytes, and ensure it
+/// fails validation.
+#[test]
+fn validate_symlink_target_null_byte_invalid() {
+    let node = castorepb::node::Node::Symlink(castorepb::SymlinkNode {
+        name: "foo".into(),
+        target: "foo\0".into(),
+    });
+
+    node.validate().expect_err("must fail validation");
+}
+
+/// Create a PathInfo with a correct deriver field and ensure it succeeds.
+#[test]
+fn validate_valid_deriver() {
+    let mut path_info = PATH_INFO_WITH_NARINFO.clone();
+
+    // add a valid deriver
+    let narinfo = path_info.narinfo.as_mut().unwrap();
+    narinfo.deriver = Some(crate::proto::StorePath {
+        name: "foo".to_string(),
+        digest: Bytes::from(DUMMY_PATH_DIGEST.as_slice()),
+    });
+
+    path_info.validate().expect("must validate");
+}
+
+/// Create a PathInfo with a broken deriver field and ensure it fails.
+#[test]
+fn validate_invalid_deriver() {
+    let mut path_info = PATH_INFO_WITH_NARINFO.clone();
+
+    // add a broken deriver (invalid digest)
+    let narinfo = path_info.narinfo.as_mut().unwrap();
+    narinfo.deriver = Some(crate::proto::StorePath {
+        name: "foo".to_string(),
+        digest: vec![].into(),
+    });
+
+    match path_info.validate().expect_err("must fail validation") {
+        ValidatePathInfoError::InvalidDeriverField(_) => {}
+        e => panic!("unexpected error: {:?}", e),
+    }
+}
+
+#[test]
+fn from_nixcompat_narinfo() {
+    let narinfo_parsed = nix_compat::narinfo::NarInfo::parse(
+        r#"StorePath: /nix/store/s66mzxpvicwk07gjbjfw9izjfa797vsw-hello-2.12.1
+URL: nar/1nhgq6wcggx0plpy4991h3ginj6hipsdslv4fd4zml1n707j26yq.nar.xz
+Compression: xz
+FileHash: sha256:1nhgq6wcggx0plpy4991h3ginj6hipsdslv4fd4zml1n707j26yq
+FileSize: 50088
+NarHash: sha256:0yzhigwjl6bws649vcs2asa4lbs8hg93hyix187gc7s7a74w5h80
+NarSize: 226488
+References: 3n58xw4373jp0ljirf06d8077j15pc4j-glibc-2.37-8 s66mzxpvicwk07gjbjfw9izjfa797vsw-hello-2.12.1
+Deriver: ib3sh3pcz10wsmavxvkdbayhqivbghlq-hello-2.12.1.drv
+Sig: cache.nixos.org-1:8ijECciSFzWHwwGVOIVYdp2fOIOJAfmzGHPQVwpktfTQJF6kMPPDre7UtFw3o+VqenC5P8RikKOAAfN7CvPEAg=="#).expect("must parse");
+
+    assert_eq!(
+        PathInfo {
+            node: None,
+            references: vec![
+                Bytes::copy_from_slice(&nixbase32::decode_fixed::<20>("3n58xw4373jp0ljirf06d8077j15pc4j").unwrap()),
+                Bytes::copy_from_slice(&nixbase32::decode_fixed::<20>("s66mzxpvicwk07gjbjfw9izjfa797vsw").unwrap()),
+            ],
+            narinfo: Some(
+                NarInfo {
+                    nar_size: 226488,
+                    nar_sha256: Bytes::copy_from_slice(
+                        &nixbase32::decode_fixed::<32>("0yzhigwjl6bws649vcs2asa4lbs8hg93hyix187gc7s7a74w5h80".as_bytes())
+                            .unwrap()
+                    ),
+                    signatures: vec![Signature {
+                        name: "cache.nixos.org-1".to_string(),
+                        data: BASE64.decode("8ijECciSFzWHwwGVOIVYdp2fOIOJAfmzGHPQVwpktfTQJF6kMPPDre7UtFw3o+VqenC5P8RikKOAAfN7CvPEAg==".as_bytes()).unwrap().into(),
+                    }],
+                    reference_names: vec![
+                        "3n58xw4373jp0ljirf06d8077j15pc4j-glibc-2.37-8".to_string(),
+                        "s66mzxpvicwk07gjbjfw9izjfa797vsw-hello-2.12.1".to_string()
+                    ],
+                    deriver: Some(crate::proto::StorePath {
+                        digest: Bytes::copy_from_slice(&nixbase32::decode_fixed::<20>("ib3sh3pcz10wsmavxvkdbayhqivbghlq").unwrap()),
+                        name: "hello-2.12.1".to_string(),
+                     }),
+                    ca: None,
+                }
+            )
+        },
+        (&narinfo_parsed).into(),
+    );
+}
+
+#[test]
+fn from_nixcompat_narinfo_fod() {
+    let narinfo_parsed = nix_compat::narinfo::NarInfo::parse(
+        r#"StorePath: /nix/store/pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz
+URL: nar/1zjrhzhaizsrlsvdkqfl073vivmxcqnzkff4s50i0cdf541ary1r.nar.xz
+Compression: xz
+FileHash: sha256:1zjrhzhaizsrlsvdkqfl073vivmxcqnzkff4s50i0cdf541ary1r
+FileSize: 1033524
+NarHash: sha256:1lvqpbk2k1sb39z8jfxixf7p7v8sj4z6mmpa44nnmff3w1y6h8lh
+NarSize: 1033416
+References: 
+Deriver: dyivpmlaq2km6c11i0s6bi6mbsx0ylqf-hello-2.12.1.tar.gz.drv
+Sig: cache.nixos.org-1:ywnIG629nQZQhEr6/HLDrLT/mUEp5J1LC6NmWSlJRWL/nM7oGItJQUYWGLvYGhSQvHrhIuvMpjNmBNh/WWqCDg==
+CA: fixed:sha256:086vqwk2wl8zfs47sq2xpjc9k066ilmb8z6dn0q6ymwjzlm196cd"#
+    ).expect("must parse");
+
+    assert_eq!(
+        PathInfo {
+            node: None,
+            references: vec![],
+            narinfo: Some(
+                NarInfo {
+                    nar_size: 1033416,
+                    nar_sha256: Bytes::copy_from_slice(
+                        &nixbase32::decode_fixed::<32>(
+                            "1lvqpbk2k1sb39z8jfxixf7p7v8sj4z6mmpa44nnmff3w1y6h8lh"
+                        )
+                        .unwrap()
+                    ),
+                    signatures: vec![Signature {
+                        name: "cache.nixos.org-1".to_string(),
+                        data: BASE64
+                            .decode("ywnIG629nQZQhEr6/HLDrLT/mUEp5J1LC6NmWSlJRWL/nM7oGItJQUYWGLvYGhSQvHrhIuvMpjNmBNh/WWqCDg==".as_bytes())
+                            .unwrap()
+                            .into(),
+                    }],
+                    reference_names: vec![],
+                    deriver: Some(crate::proto::StorePath {
+                        digest: Bytes::copy_from_slice(
+                            &nixbase32::decode_fixed::<20>("dyivpmlaq2km6c11i0s6bi6mbsx0ylqf").unwrap()
+                        ),
+                        name: "hello-2.12.1.tar.gz".to_string(),
+                    }),
+                    ca: Some(crate::proto::nar_info::Ca {
+                        r#type: crate::proto::nar_info::ca::Hash::FlatSha256.into(),
+                        digest: Bytes::copy_from_slice(
+                            &nixbase32::decode_fixed::<32>(
+                                "086vqwk2wl8zfs47sq2xpjc9k066ilmb8z6dn0q6ymwjzlm196cd"
+                            )
+                            .unwrap()
+                        )
+                    }),
+                }
+            ),
+        },
+        (&narinfo_parsed).into()
+    );
+}
+
+/// Exercise .as_narinfo() on a PathInfo and ensure important fields are preserved..
+#[test]
+fn as_narinfo() {
+    let narinfo_parsed = nix_compat::narinfo::NarInfo::parse(
+        r#"StorePath: /nix/store/pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz
+URL: nar/1zjrhzhaizsrlsvdkqfl073vivmxcqnzkff4s50i0cdf541ary1r.nar.xz
+Compression: xz
+FileHash: sha256:1zjrhzhaizsrlsvdkqfl073vivmxcqnzkff4s50i0cdf541ary1r
+FileSize: 1033524
+NarHash: sha256:1lvqpbk2k1sb39z8jfxixf7p7v8sj4z6mmpa44nnmff3w1y6h8lh
+NarSize: 1033416
+References: 
+Deriver: dyivpmlaq2km6c11i0s6bi6mbsx0ylqf-hello-2.12.1.tar.gz.drv
+Sig: cache.nixos.org-1:ywnIG629nQZQhEr6/HLDrLT/mUEp5J1LC6NmWSlJRWL/nM7oGItJQUYWGLvYGhSQvHrhIuvMpjNmBNh/WWqCDg==
+CA: fixed:sha256:086vqwk2wl8zfs47sq2xpjc9k066ilmb8z6dn0q6ymwjzlm196cd"#
+    ).expect("must parse");
+
+    let path_info: PathInfo = (&narinfo_parsed).into();
+
+    let mut narinfo_returned = path_info
+        .to_narinfo(
+            StorePathRef::from_bytes(b"pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz")
+                .expect("invalid storepath"),
+        )
+        .expect("must be some");
+    narinfo_returned.url = "some.nar";
+
+    assert_eq!(
+        r#"StorePath: /nix/store/pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz
+URL: some.nar
+Compression: none
+NarHash: sha256:1lvqpbk2k1sb39z8jfxixf7p7v8sj4z6mmpa44nnmff3w1y6h8lh
+NarSize: 1033416
+References: 
+Deriver: dyivpmlaq2km6c11i0s6bi6mbsx0ylqf-hello-2.12.1.tar.gz.drv
+Sig: cache.nixos.org-1:ywnIG629nQZQhEr6/HLDrLT/mUEp5J1LC6NmWSlJRWL/nM7oGItJQUYWGLvYGhSQvHrhIuvMpjNmBNh/WWqCDg==
+CA: fixed:sha256:086vqwk2wl8zfs47sq2xpjc9k066ilmb8z6dn0q6ymwjzlm196cd
+"#,
+        narinfo_returned.to_string(),
+    );
+}
diff --git a/tvix/store/src/tests/fixtures.rs b/tvix/store/src/tests/fixtures.rs
new file mode 100644
index 0000000000..1c8359a2c0
--- /dev/null
+++ b/tvix/store/src/tests/fixtures.rs
@@ -0,0 +1,142 @@
+use lazy_static::lazy_static;
+use rstest::*;
+use std::sync::Arc;
+pub use tvix_castore::fixtures::*;
+use tvix_castore::{
+    blobservice::{BlobService, MemoryBlobService},
+    directoryservice::{DirectoryService, MemoryDirectoryService},
+    proto as castorepb,
+};
+
+use crate::proto::{
+    nar_info::{ca, Ca},
+    NarInfo, PathInfo,
+};
+
+pub const DUMMY_PATH: &str = "00000000000000000000000000000000-dummy";
+pub const DUMMY_PATH_DIGEST: [u8; 20] = [0; 20];
+
+lazy_static! {
+    /// The NAR representation of a symlink pointing to `/nix/store/somewhereelse`
+    pub static ref NAR_CONTENTS_SYMLINK: Vec<u8> = vec![
+        13, 0, 0, 0, 0, 0, 0, 0, b'n', b'i', b'x', b'-', b'a', b'r', b'c', b'h', b'i', b'v', b'e', b'-', b'1', 0,
+        0, 0, // "nix-archive-1"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b't', b'y', b'p', b'e', 0, 0, 0, 0, // "type"
+        7, 0, 0, 0, 0, 0, 0, 0, b's', b'y', b'm', b'l', b'i', b'n', b'k', 0, // "symlink"
+        6, 0, 0, 0, 0, 0, 0, 0, b't', b'a', b'r', b'g', b'e', b't', 0, 0, // target
+        24, 0, 0, 0, 0, 0, 0, 0, b'/', b'n', b'i', b'x', b'/', b's', b't', b'o', b'r', b'e', b'/', b's', b'o',
+        b'm', b'e', b'w', b'h', b'e', b'r', b'e', b'e', b'l', b's',
+        b'e', // "/nix/store/somewhereelse"
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0 // ")"
+    ];
+
+    /// The NAR representation of a regular file with the contents "Hello World!"
+    pub static ref NAR_CONTENTS_HELLOWORLD: Vec<u8> = vec![
+        13, 0, 0, 0, 0, 0, 0, 0, b'n', b'i', b'x', b'-', b'a', b'r', b'c', b'h', b'i', b'v', b'e', b'-', b'1', 0,
+        0, 0, // "nix-archive-1"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b't', b'y', b'p', b'e', 0, 0, 0, 0, // "type"
+        7, 0, 0, 0, 0, 0, 0, 0, b'r', b'e', b'g', b'u', b'l', b'a', b'r', 0, // "regular"
+        8, 0, 0, 0, 0, 0, 0, 0, b'c', b'o', b'n', b't', b'e', b'n', b't', b's', // "contents"
+        12, 0, 0, 0, 0, 0, 0, 0, b'H', b'e', b'l', b'l', b'o', b' ', b'W', b'o', b'r', b'l', b'd', b'!', 0, 0,
+        0, 0, // "Hello World!"
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0 // ")"
+    ];
+
+    /// The NAR representation of a more complicated directory structure.
+    pub static ref NAR_CONTENTS_COMPLICATED: Vec<u8> = vec![
+        13, 0, 0, 0, 0, 0, 0, 0, b'n', b'i', b'x', b'-', b'a', b'r', b'c', b'h', b'i', b'v', b'e', b'-', b'1', 0,
+        0, 0, // "nix-archive-1"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b't', b'y', b'p', b'e', 0, 0, 0, 0, // "type"
+        9, 0, 0, 0, 0, 0, 0, 0, b'd', b'i', b'r', b'e', b'c', b't', b'o', b'r', b'y', 0, 0, 0, 0, 0, 0, 0, // "directory"
+        5, 0, 0, 0, 0, 0, 0, 0, b'e', b'n', b't', b'r', b'y', 0, 0, 0, // "entry"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b'n', b'a', b'm', b'e', 0, 0, 0, 0, // "name"
+        5, 0, 0, 0, 0, 0, 0, 0, b'.', b'k', b'e', b'e', b'p', 0, 0, 0, // ".keep"
+        4, 0, 0, 0, 0, 0, 0, 0, b'n', b'o', b'd', b'e', 0, 0, 0, 0, // "node"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b't', b'y', b'p', b'e', 0, 0, 0, 0, // "type"
+        7, 0, 0, 0, 0, 0, 0, 0, b'r', b'e', b'g', b'u', b'l', b'a', b'r', 0, // "regular"
+        8, 0, 0, 0, 0, 0, 0, 0, b'c', b'o', b'n', b't', b'e', b'n', b't', b's', // "contents"
+        0, 0, 0, 0, 0, 0, 0, 0, // ""
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0, // ")"
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0, // ")"
+        5, 0, 0, 0, 0, 0, 0, 0, b'e', b'n', b't', b'r', b'y', 0, 0, 0, // "entry"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b'n', b'a', b'm', b'e', 0, 0, 0, 0, // "name"
+        2, 0, 0, 0, 0, 0, 0, 0, b'a', b'a', 0, 0, 0, 0, 0, 0, // "aa"
+        4, 0, 0, 0, 0, 0, 0, 0, b'n', b'o', b'd', b'e', 0, 0, 0, 0, // "node"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b't', b'y', b'p', b'e', 0, 0, 0, 0, // "type"
+        7, 0, 0, 0, 0, 0, 0, 0, b's', b'y', b'm', b'l', b'i', b'n', b'k', 0, // "symlink"
+        6, 0, 0, 0, 0, 0, 0, 0, b't', b'a', b'r', b'g', b'e', b't', 0, 0, // target
+        24, 0, 0, 0, 0, 0, 0, 0, b'/', b'n', b'i', b'x', b'/', b's', b't', b'o', b'r', b'e', b'/', b's', b'o',
+        b'm', b'e', b'w', b'h', b'e', b'r', b'e', b'e', b'l', b's',
+        b'e', // "/nix/store/somewhereelse"
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0, // ")"
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0, // ")"
+        5, 0, 0, 0, 0, 0, 0, 0, b'e', b'n', b't', b'r', b'y', 0, 0, 0, // "entry"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b'n', b'a', b'm', b'e', 0, 0, 0, 0, // "name"
+        4, 0, 0, 0, 0, 0, 0, 0, b'k', b'e', b'e', b'p', 0, 0, 0, 0, // "keep"
+        4, 0, 0, 0, 0, 0, 0, 0, b'n', b'o', b'd', b'e', 0, 0, 0, 0, // "node"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b't', b'y', b'p', b'e', 0, 0, 0, 0, // "type"
+        9, 0, 0, 0, 0, 0, 0, 0, b'd', b'i', b'r', b'e', b'c', b't', b'o', b'r', b'y', 0, 0, 0, 0, 0, 0, 0, // "directory"
+        5, 0, 0, 0, 0, 0, 0, 0, b'e', b'n', b't', b'r', b'y', 0, 0, 0, // "entry"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b'n', b'a', b'm', b'e', 0, 0, 0, 0, // "name"
+        5, 0, 0, 0, 0, 0, 0, 0, 46, 107, 101, 101, 112, 0, 0, 0, // ".keep"
+        4, 0, 0, 0, 0, 0, 0, 0, 110, 111, 100, 101, 0, 0, 0, 0, // "node"
+        1, 0, 0, 0, 0, 0, 0, 0, b'(', 0, 0, 0, 0, 0, 0, 0, // "("
+        4, 0, 0, 0, 0, 0, 0, 0, b't', b'y', b'p', b'e', 0, 0, 0, 0, // "type"
+        7, 0, 0, 0, 0, 0, 0, 0, b'r', b'e', b'g', b'u', b'l', b'a', b'r', 0, // "regular"
+        8, 0, 0, 0, 0, 0, 0, 0, b'c', b'o', b'n', b't', b'e', b'n', b't', b's', // "contents"
+        0, 0, 0, 0, 0, 0, 0, 0, // ""
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0, // ")"
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0, // ")"
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0, // ")"
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0, // ")"
+        1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0, // ")"
+    ];
+
+    /// A PathInfo message without .narinfo populated.
+    pub static ref PATH_INFO_WITHOUT_NARINFO : PathInfo = PathInfo {
+        node: Some(castorepb::Node {
+            node: Some(castorepb::node::Node::Directory(castorepb::DirectoryNode {
+                name: DUMMY_PATH.into(),
+                digest: DUMMY_DIGEST.clone().into(),
+                size: 0,
+            })),
+        }),
+        references: vec![DUMMY_PATH_DIGEST.as_slice().into()],
+        narinfo: None,
+    };
+
+    /// A PathInfo message with .narinfo populated.
+    /// The references in `narinfo.reference_names` aligns with what's in
+    /// `references`.
+    pub static ref PATH_INFO_WITH_NARINFO : PathInfo = PathInfo {
+        narinfo: Some(NarInfo {
+            nar_size: 0,
+            nar_sha256: DUMMY_DIGEST.clone().into(),
+            signatures: vec![],
+            reference_names: vec![DUMMY_PATH.to_string()],
+            deriver: None,
+            ca: Some(Ca { r#type: ca::Hash::NarSha256.into(), digest:  DUMMY_DIGEST.clone().into() })
+        }),
+      ..PATH_INFO_WITHOUT_NARINFO.clone()
+    };
+}
+
+#[fixture]
+pub(crate) fn blob_service() -> Arc<dyn BlobService> {
+    Arc::from(MemoryBlobService::default())
+}
+
+#[fixture]
+pub(crate) fn directory_service() -> Arc<dyn DirectoryService> {
+    Arc::from(MemoryDirectoryService::default())
+}
diff --git a/tvix/store/src/tests/mod.rs b/tvix/store/src/tests/mod.rs
new file mode 100644
index 0000000000..1e7fc3f6b4
--- /dev/null
+++ b/tvix/store/src/tests/mod.rs
@@ -0,0 +1,2 @@
+pub mod fixtures;
+mod nar_renderer;
diff --git a/tvix/store/src/tests/nar_renderer.rs b/tvix/store/src/tests/nar_renderer.rs
new file mode 100644
index 0000000000..8bfb5a72bb
--- /dev/null
+++ b/tvix/store/src/tests/nar_renderer.rs
@@ -0,0 +1,228 @@
+use crate::nar::calculate_size_and_sha256;
+use crate::nar::write_nar;
+use crate::tests::fixtures::blob_service;
+use crate::tests::fixtures::directory_service;
+use crate::tests::fixtures::*;
+use rstest::*;
+use sha2::{Digest, Sha256};
+use std::io;
+use std::sync::Arc;
+use tokio::io::sink;
+use tvix_castore::blobservice::BlobService;
+use tvix_castore::directoryservice::DirectoryService;
+use tvix_castore::proto as castorepb;
+
+#[rstest]
+#[tokio::test]
+async fn single_symlink(
+    blob_service: Arc<dyn BlobService>,
+    directory_service: Arc<dyn DirectoryService>,
+) {
+    let mut buf: Vec<u8> = vec![];
+
+    write_nar(
+        &mut buf,
+        &castorepb::node::Node::Symlink(castorepb::SymlinkNode {
+            name: "doesntmatter".into(),
+            target: "/nix/store/somewhereelse".into(),
+        }),
+        // don't put anything in the stores, as we don't actually do any requests.
+        blob_service,
+        directory_service,
+    )
+    .await
+    .expect("must succeed");
+
+    assert_eq!(buf, NAR_CONTENTS_SYMLINK.to_vec());
+}
+
+/// Make sure the NARRenderer fails if a referred blob doesn't exist.
+#[rstest]
+#[tokio::test]
+async fn single_file_missing_blob(
+    blob_service: Arc<dyn BlobService>,
+    directory_service: Arc<dyn DirectoryService>,
+) {
+    let e = write_nar(
+        sink(),
+        &castorepb::node::Node::File(castorepb::FileNode {
+            name: "doesntmatter".into(),
+            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+            size: HELLOWORLD_BLOB_CONTENTS.len() as u64,
+            executable: false,
+        }),
+        // the blobservice is empty intentionally, to provoke the error.
+        blob_service,
+        directory_service,
+    )
+    .await
+    .expect_err("must fail");
+
+    match e {
+        crate::nar::RenderError::NARWriterError(e) => {
+            assert_eq!(io::ErrorKind::NotFound, e.kind());
+        }
+        _ => panic!("unexpected error: {:?}", e),
+    }
+}
+
+/// Make sure the NAR Renderer fails if the returned blob meta has another size
+/// than specified in the proto node.
+#[rstest]
+#[tokio::test]
+async fn single_file_wrong_blob_size(
+    blob_service: Arc<dyn BlobService>,
+    directory_service: Arc<dyn DirectoryService>,
+) {
+    // insert blob into the store
+    let mut writer = blob_service.open_write().await;
+    tokio::io::copy(
+        &mut io::Cursor::new(HELLOWORLD_BLOB_CONTENTS.to_vec()),
+        &mut writer,
+    )
+    .await
+    .unwrap();
+    assert_eq!(
+        HELLOWORLD_BLOB_DIGEST.clone(),
+        writer.close().await.unwrap()
+    );
+
+    // Test with a root FileNode of a too big size
+    let e = write_nar(
+        sink(),
+        &castorepb::node::Node::File(castorepb::FileNode {
+            name: "doesntmatter".into(),
+            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+            size: 42, // <- note the wrong size here!
+            executable: false,
+        }),
+        blob_service.clone(),
+        directory_service.clone(),
+    )
+    .await
+    .expect_err("must fail");
+
+    match e {
+        crate::nar::RenderError::NARWriterError(e) => {
+            assert_eq!(io::ErrorKind::UnexpectedEof, e.kind());
+        }
+        _ => panic!("unexpected error: {:?}", e),
+    }
+
+    // Test with a root FileNode of a too small size
+    let e = write_nar(
+        sink(),
+        &castorepb::node::Node::File(castorepb::FileNode {
+            name: "doesntmatter".into(),
+            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+            size: 2, // <- note the wrong size here!
+            executable: false,
+        }),
+        blob_service,
+        directory_service,
+    )
+    .await
+    .expect_err("must fail");
+
+    match e {
+        crate::nar::RenderError::NARWriterError(e) => {
+            assert_eq!(io::ErrorKind::InvalidInput, e.kind());
+        }
+        _ => panic!("unexpected error: {:?}", e),
+    }
+}
+
+#[rstest]
+#[tokio::test]
+async fn single_file(
+    blob_service: Arc<dyn BlobService>,
+    directory_service: Arc<dyn DirectoryService>,
+) {
+    // insert blob into the store
+    let mut writer = blob_service.open_write().await;
+    tokio::io::copy(&mut io::Cursor::new(HELLOWORLD_BLOB_CONTENTS), &mut writer)
+        .await
+        .unwrap();
+
+    assert_eq!(
+        HELLOWORLD_BLOB_DIGEST.clone(),
+        writer.close().await.unwrap()
+    );
+
+    let mut buf: Vec<u8> = vec![];
+
+    write_nar(
+        &mut buf,
+        &castorepb::node::Node::File(castorepb::FileNode {
+            name: "doesntmatter".into(),
+            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+            size: HELLOWORLD_BLOB_CONTENTS.len() as u64,
+            executable: false,
+        }),
+        blob_service,
+        directory_service,
+    )
+    .await
+    .expect("must succeed");
+
+    assert_eq!(buf, NAR_CONTENTS_HELLOWORLD.to_vec());
+}
+
+#[rstest]
+#[tokio::test]
+async fn test_complicated(
+    blob_service: Arc<dyn BlobService>,
+    directory_service: Arc<dyn DirectoryService>,
+) {
+    // put all data into the stores.
+    // insert blob into the store
+    let mut writer = blob_service.open_write().await;
+    tokio::io::copy(&mut io::Cursor::new(EMPTY_BLOB_CONTENTS), &mut writer)
+        .await
+        .unwrap();
+    assert_eq!(EMPTY_BLOB_DIGEST.clone(), writer.close().await.unwrap());
+
+    // insert directories
+    directory_service
+        .put(DIRECTORY_WITH_KEEP.clone())
+        .await
+        .unwrap();
+    directory_service
+        .put(DIRECTORY_COMPLICATED.clone())
+        .await
+        .unwrap();
+
+    let mut buf: Vec<u8> = vec![];
+
+    write_nar(
+        &mut buf,
+        &castorepb::node::Node::Directory(castorepb::DirectoryNode {
+            name: "doesntmatter".into(),
+            digest: DIRECTORY_COMPLICATED.digest().into(),
+            size: DIRECTORY_COMPLICATED.size(),
+        }),
+        blob_service.clone(),
+        directory_service.clone(),
+    )
+    .await
+    .expect("must succeed");
+
+    assert_eq!(buf, NAR_CONTENTS_COMPLICATED.to_vec());
+
+    // ensure calculate_nar does return the correct sha256 digest and sum.
+    let (nar_size, nar_digest) = calculate_size_and_sha256(
+        &castorepb::node::Node::Directory(castorepb::DirectoryNode {
+            name: "doesntmatter".into(),
+            digest: DIRECTORY_COMPLICATED.digest().into(),
+            size: DIRECTORY_COMPLICATED.size(),
+        }),
+        blob_service,
+        directory_service,
+    )
+    .await
+    .expect("must succeed");
+
+    assert_eq!(NAR_CONTENTS_COMPLICATED.len() as u64, nar_size);
+    let d = Sha256::digest(NAR_CONTENTS_COMPLICATED.clone());
+    assert_eq!(d.as_slice(), nar_digest);
+}
diff --git a/tvix/store/src/utils.rs b/tvix/store/src/utils.rs
new file mode 100644
index 0000000000..0b171377bd
--- /dev/null
+++ b/tvix/store/src/utils.rs
@@ -0,0 +1,65 @@
+use std::sync::Arc;
+use std::{
+    pin::Pin,
+    task::{self, Poll},
+};
+use tokio::io::{self, AsyncWrite};
+
+use tvix_castore::{
+    blobservice::{self, BlobService},
+    directoryservice::{self, DirectoryService},
+};
+
+use crate::pathinfoservice::{self, PathInfoService};
+
+/// Construct the three store handles from their addrs.
+pub async fn construct_services(
+    blob_service_addr: impl AsRef<str>,
+    directory_service_addr: impl AsRef<str>,
+    path_info_service_addr: impl AsRef<str>,
+) -> std::io::Result<(
+    Arc<dyn BlobService>,
+    Arc<dyn DirectoryService>,
+    Box<dyn PathInfoService>,
+)> {
+    let blob_service: Arc<dyn BlobService> = blobservice::from_addr(blob_service_addr.as_ref())
+        .await?
+        .into();
+    let directory_service: Arc<dyn DirectoryService> =
+        directoryservice::from_addr(directory_service_addr.as_ref())
+            .await?
+            .into();
+    let path_info_service = pathinfoservice::from_addr(
+        path_info_service_addr.as_ref(),
+        blob_service.clone(),
+        directory_service.clone(),
+    )
+    .await?;
+
+    Ok((blob_service, directory_service, path_info_service))
+}
+
+/// The inverse of [tokio_util::io::SyncIoBridge].
+/// Don't use this with anything that actually does blocking I/O.
+pub struct AsyncIoBridge<T>(pub T);
+
+impl<W: std::io::Write + Unpin> AsyncWrite for AsyncIoBridge<W> {
+    fn poll_write(
+        self: Pin<&mut Self>,
+        _cx: &mut task::Context<'_>,
+        buf: &[u8],
+    ) -> Poll<io::Result<usize>> {
+        Poll::Ready(self.get_mut().0.write(buf))
+    }
+
+    fn poll_flush(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {
+        Poll::Ready(self.get_mut().0.flush())
+    }
+
+    fn poll_shutdown(
+        self: Pin<&mut Self>,
+        _cx: &mut task::Context<'_>,
+    ) -> Poll<Result<(), io::Error>> {
+        Poll::Ready(Ok(()))
+    }
+}
diff --git a/tvix/tools/crunch-v2/.gitignore b/tvix/tools/crunch-v2/.gitignore
new file mode 100644
index 0000000000..4bed5da93f
--- /dev/null
+++ b/tvix/tools/crunch-v2/.gitignore
@@ -0,0 +1 @@
+*.parquet
diff --git a/tvix/tools/crunch-v2/Cargo.lock b/tvix/tools/crunch-v2/Cargo.lock
new file mode 100644
index 0000000000..cff5509d0b
--- /dev/null
+++ b/tvix/tools/crunch-v2/Cargo.lock
@@ -0,0 +1,3214 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
+name = "argminmax"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "202108b46429b765ef483f8a24d5c46f48c14acfdacc086dd4ab6dddf6bcdbd2"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "array-init-cursor"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7d0a018de4f6aa429b9d33d69edf69072b1c5b1cb8d3e4a5f7ef898fc3eb76"
+
+[[package]]
+name = "arrayref"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+
+[[package]]
+name = "arrow-format"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07884ea216994cdc32a2d5f8274a8bee979cfe90274b83f86f440866ee3132c7"
+dependencies = [
+ "planus",
+ "serde",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "atoi"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "atoi_simd"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc41b65e01b6851bdcd2d741824e6b310d571396bf3915e31e4792034ee65126"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "blake3"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if",
+ "constant_time_eq",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "brotli"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
+
+[[package]]
+name = "brotli-decompressor"
+version = "2.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+]
+
+[[package]]
+name = "bstr"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c"
+dependencies = [
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "bytemuck"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "bzip2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
+dependencies = [
+ "bzip2-sys",
+ "libc",
+]
+
+[[package]]
+name = "bzip2-sys"
+version = "0.1.11+1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "jobserver",
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "serde",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "clap"
+version = "4.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "console"
+version = "0.15.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
+dependencies = [
+ "encode_unicode",
+ "lazy_static",
+ "libc",
+ "unicode-width",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
+
+[[package]]
+name = "constant_time_eq"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crunch-v2"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "blake3",
+ "bstr",
+ "bytes",
+ "bzip2",
+ "clap",
+ "digest 0.10.7",
+ "fastcdc",
+ "futures",
+ "indicatif",
+ "lazy_static",
+ "nix-compat",
+ "polars",
+ "prost",
+ "prost-build",
+ "rusoto_core",
+ "rusoto_s3",
+ "sha2 0.10.8",
+ "sled",
+ "tokio",
+ "xz2",
+ "zstd",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "crypto-mac"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest 0.10.7",
+ "fiat-crypto",
+ "platforms",
+ "rustc_version",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+
+[[package]]
+name = "der"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer 0.10.4",
+ "crypto-common",
+]
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "serde",
+ "sha2 0.10.8",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "encode_unicode"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+
+[[package]]
+name = "enum_dispatch"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e"
+dependencies = [
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "ethnum"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "fast-float"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c"
+
+[[package]]
+name = "fastcdc"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a71061d097bfa9a5a4d2efdec57990d9a88745020b365191d37e48541a1628f2"
+
+[[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7"
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "flate2"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign_vec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee1b05cbd864bcaecbd3455d6d967862d446e4ebfc3c2e5e5b9841e53cba6673"
+
+[[package]]
+name = "fs2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
+
+[[package]]
+name = "futures-task"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
+
+[[package]]
+name = "futures-util"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "h2"
+version = "0.3.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+ "rayon",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hmac"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+dependencies = [
+ "crypto-mac",
+ "digest 0.9.0",
+]
+
+[[package]]
+name = "home"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "http"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "0.14.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2 0.4.10",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c"
+dependencies = [
+ "http",
+ "hyper",
+ "log",
+ "rustls",
+ "rustls-native-certs",
+ "tokio",
+ "tokio-rustls",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "indicatif"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
+dependencies = [
+ "console",
+ "instant",
+ "number_prefix",
+ "portable-atomic",
+ "unicode-width",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "itertools"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "jobserver"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "libredox"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
+dependencies = [
+ "bitflags 2.4.1",
+ "libc",
+ "redox_syscall 0.4.1",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "lz4"
+version = "1.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1"
+dependencies = [
+ "libc",
+ "lz4-sys",
+]
+
+[[package]]
+name = "lz4-sys"
+version = "1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "lzma-sys"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "md-5"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
+dependencies = [
+ "block-buffer 0.9.0",
+ "digest 0.9.0",
+ "opaque-debug",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "memmap2"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "multimap"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+
+[[package]]
+name = "multiversion"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2c7b9d7fe61760ce5ea19532ead98541f6b4c495d87247aff9826445cf6872a"
+dependencies = [
+ "multiversion-macros",
+ "target-features",
+]
+
+[[package]]
+name = "multiversion-macros"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26a83d8500ed06d68877e9de1dde76c1dbb83885dcdbda4ef44ccbc3fbda2ac8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "target-features",
+]
+
+[[package]]
+name = "nix-compat"
+version = "0.1.0"
+dependencies = [
+ "bitflags 2.4.1",
+ "bstr",
+ "data-encoding",
+ "ed25519",
+ "ed25519-dalek",
+ "glob",
+ "nom",
+ "serde",
+ "serde_json",
+ "sha2 0.10.8",
+ "thiserror",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "now"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d89e9874397a1f0a52fc1f197a8effd9735223cb2390e9dcc83ac6cd02923d0"
+dependencies = [
+ "chrono",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "number_prefix"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
+
+[[package]]
+name = "object"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core 0.8.6",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.9",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall 0.2.16",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.4.1",
+ "smallvec",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "parquet-format-safe"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1131c54b167dd4e4799ce762e1ab01549ebb94d5bdd13e6ec1b467491c378e1f"
+dependencies = [
+ "async-trait",
+ "futures",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "petgraph"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "planus"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1691dd09e82f428ce8d6310bd6d5da2557c82ff17694d2a32cad7242aea89f"
+dependencies = [
+ "array-init-cursor",
+]
+
+[[package]]
+name = "platforms"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0"
+
+[[package]]
+name = "polars"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8e52f9236eb722da0990a70bbb1216dcc7a77bcb00c63439d2d982823e90d5"
+dependencies = [
+ "getrandom",
+ "polars-core",
+ "polars-io",
+ "polars-lazy",
+ "polars-ops",
+ "polars-sql",
+ "version_check",
+]
+
+[[package]]
+name = "polars-arrow"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd503430a6d9779b07915d858865fe998317ef3cfef8973881f578ac5d4baae7"
+dependencies = [
+ "ahash",
+ "arrow-format",
+ "atoi_simd",
+ "bytemuck",
+ "chrono",
+ "dyn-clone",
+ "either",
+ "ethnum",
+ "fast-float",
+ "foreign_vec",
+ "futures",
+ "getrandom",
+ "hashbrown",
+ "itoa",
+ "lz4",
+ "multiversion",
+ "num-traits",
+ "polars-error",
+ "polars-utils",
+ "rustc_version",
+ "ryu",
+ "simdutf8",
+ "streaming-iterator",
+ "strength_reduce",
+ "zstd",
+]
+
+[[package]]
+name = "polars-core"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae73d5b8e55decde670caba1cc82b61f14bfb9a72503198f0997d657a98dcfd6"
+dependencies = [
+ "ahash",
+ "bitflags 2.4.1",
+ "bytemuck",
+ "chrono",
+ "either",
+ "hashbrown",
+ "indexmap",
+ "num-traits",
+ "once_cell",
+ "polars-arrow",
+ "polars-error",
+ "polars-row",
+ "polars-utils",
+ "rand",
+ "rand_distr",
+ "rayon",
+ "regex",
+ "smartstring",
+ "thiserror",
+ "version_check",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "polars-error"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb0520d68eaa9993ae0c741409d1526beff5b8f48e1d73e4381616f8152cf488"
+dependencies = [
+ "arrow-format",
+ "regex",
+ "simdutf8",
+ "thiserror",
+]
+
+[[package]]
+name = "polars-io"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96e10a0745acd6009db64bef0ceb9e23a70b1c27b26a0a6517c91f3e6363bc06"
+dependencies = [
+ "ahash",
+ "async-trait",
+ "bytes",
+ "futures",
+ "home",
+ "memchr",
+ "memmap2",
+ "num-traits",
+ "once_cell",
+ "percent-encoding",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-parquet",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "smartstring",
+ "tokio",
+ "tokio-util",
+]
+
+[[package]]
+name = "polars-lazy"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3555f759705be6dd0d3762d16a0b8787b2dc4da73b57465f3b2bf1a070ba8f20"
+dependencies = [
+ "ahash",
+ "bitflags 2.4.1",
+ "glob",
+ "once_cell",
+ "polars-arrow",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-pipe",
+ "polars-plan",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "smartstring",
+ "version_check",
+]
+
+[[package]]
+name = "polars-ops"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a7eb218296aaa7f79945f08288ca32ca3cf25fa505649eeee689ec21eebf636"
+dependencies = [
+ "ahash",
+ "argminmax",
+ "bytemuck",
+ "either",
+ "hashbrown",
+ "indexmap",
+ "memchr",
+ "num-traits",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "smartstring",
+ "version_check",
+]
+
+[[package]]
+name = "polars-parquet"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "146010e4b7dd4d2d0e58ddc762f6361f77d7a0385c54471199370c17164f67dd"
+dependencies = [
+ "ahash",
+ "async-stream",
+ "base64 0.21.5",
+ "brotli",
+ "ethnum",
+ "flate2",
+ "futures",
+ "lz4",
+ "num-traits",
+ "parquet-format-safe",
+ "polars-arrow",
+ "polars-error",
+ "polars-utils",
+ "seq-macro",
+ "simdutf8",
+ "snap",
+ "streaming-decompression",
+ "zstd",
+]
+
+[[package]]
+name = "polars-pipe"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66094e7df64c932a9a7bdfe7df0c65efdcb192096e11a6a765a9778f78b4bdec"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-queue",
+ "enum_dispatch",
+ "hashbrown",
+ "num-traits",
+ "polars-arrow",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-plan",
+ "polars-row",
+ "polars-utils",
+ "rayon",
+ "smartstring",
+ "version_check",
+]
+
+[[package]]
+name = "polars-plan"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10e32a0958ef854b132bad7f8369cb3237254635d5e864c99505bc0bc1035fbc"
+dependencies = [
+ "ahash",
+ "bytemuck",
+ "once_cell",
+ "percent-encoding",
+ "polars-arrow",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-parquet",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "smartstring",
+ "strum_macros",
+ "version_check",
+]
+
+[[package]]
+name = "polars-row"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d135ab81cac2906ba74ea8984c7e6025d081ae5867615bcefb4d84dfdb456dac"
+dependencies = [
+ "polars-arrow",
+ "polars-error",
+ "polars-utils",
+]
+
+[[package]]
+name = "polars-sql"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8dbd7786849a5e3ad1fde188bf38141632f626e3a57319b0bbf7a5f1d75519e"
+dependencies = [
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-lazy",
+ "polars-plan",
+ "rand",
+ "serde",
+ "serde_json",
+ "sqlparser",
+]
+
+[[package]]
+name = "polars-time"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aae56f79e9cedd617773c1c8f5ca84a31a8b1d593714959d5f799e7bdd98fe51"
+dependencies = [
+ "atoi",
+ "chrono",
+ "now",
+ "once_cell",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-ops",
+ "polars-utils",
+ "regex",
+ "smartstring",
+]
+
+[[package]]
+name = "polars-utils"
+version = "0.35.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da6ce68169fe61d46958c8eab7447360f30f2f23f6e24a0ce703a14b0a3cfbfc"
+dependencies = [
+ "ahash",
+ "bytemuck",
+ "hashbrown",
+ "indexmap",
+ "num-traits",
+ "once_cell",
+ "polars-error",
+ "rayon",
+ "smartstring",
+ "sysinfo",
+ "version_check",
+]
+
+[[package]]
+name = "portable-atomic"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prost"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5a410fc7882af66deb8d01d01737353cf3ad6204c408177ba494291a626312"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa3d084c8704911bfefb2771be2f9b6c5c0da7343a71e0021ee3c665cada738"
+dependencies = [
+ "bytes",
+ "heck",
+ "itertools",
+ "log",
+ "multimap",
+ "once_cell",
+ "petgraph",
+ "prettyplease",
+ "prost",
+ "prost-types",
+ "regex",
+ "syn 2.0.39",
+ "tempfile",
+ "which",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065717a5dfaca4a83d2fe57db3487b311365200000551d7a364e715dbf4346bc"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8339f32236f590281e2f6368276441394fcd1b2133b549cc895d0ae80f2f9a52"
+dependencies = [
+ "prost",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_distr"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+dependencies = [
+ "num-traits",
+ "rand",
+]
+
+[[package]]
+name = "rayon"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin 0.5.2",
+ "untrusted 0.7.1",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
+dependencies = [
+ "cc",
+ "getrandom",
+ "libc",
+ "spin 0.9.8",
+ "untrusted 0.9.0",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rusoto_core"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1db30db44ea73551326269adcf7a2169428a054f14faf9e1768f2163494f2fa2"
+dependencies = [
+ "async-trait",
+ "base64 0.13.1",
+ "bytes",
+ "crc32fast",
+ "futures",
+ "http",
+ "hyper",
+ "hyper-rustls",
+ "lazy_static",
+ "log",
+ "rusoto_credential",
+ "rusoto_signature",
+ "rustc_version",
+ "serde",
+ "serde_json",
+ "tokio",
+ "xml-rs",
+]
+
+[[package]]
+name = "rusoto_credential"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee0a6c13db5aad6047b6a44ef023dbbc21a056b6dab5be3b79ce4283d5c02d05"
+dependencies = [
+ "async-trait",
+ "chrono",
+ "dirs-next",
+ "futures",
+ "hyper",
+ "serde",
+ "serde_json",
+ "shlex",
+ "tokio",
+ "zeroize",
+]
+
+[[package]]
+name = "rusoto_s3"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7aae4677183411f6b0b412d66194ef5403293917d66e70ab118f07cc24c5b14d"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures",
+ "rusoto_core",
+ "xml-rs",
+]
+
+[[package]]
+name = "rusoto_signature"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5ae95491c8b4847931e291b151127eccd6ff8ca13f33603eb3d0035ecb05272"
+dependencies = [
+ "base64 0.13.1",
+ "bytes",
+ "chrono",
+ "digest 0.9.0",
+ "futures",
+ "hex",
+ "hmac",
+ "http",
+ "hyper",
+ "log",
+ "md-5",
+ "percent-encoding",
+ "pin-project-lite",
+ "rusoto_credential",
+ "rustc_version",
+ "serde",
+ "sha2 0.9.9",
+ "tokio",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234"
+dependencies = [
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
+dependencies = [
+ "log",
+ "ring 0.16.20",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
+dependencies = [
+ "base64 0.21.5",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "schannel"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "sct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
+dependencies = [
+ "ring 0.17.5",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
+
+[[package]]
+name = "seq-macro"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
+
+[[package]]
+name = "serde"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer 0.9.0",
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.9.0",
+ "opaque-debug",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.10.7",
+ "sha2-asm",
+]
+
+[[package]]
+name = "sha2-asm"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f27ba7066011e3fb30d808b51affff34f0a66d3a03a58edd787c6e420e40e44e"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "shlex"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "simdutf8"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "sled"
+version = "0.34.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
+dependencies = [
+ "crc32fast",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+ "fs2",
+ "fxhash",
+ "libc",
+ "log",
+ "parking_lot 0.11.2",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
+
+[[package]]
+name = "smartstring"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
+dependencies = [
+ "autocfg",
+ "static_assertions",
+ "version_check",
+]
+
+[[package]]
+name = "snap"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831"
+
+[[package]]
+name = "socket2"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "spki"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "sqlparser"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743b4dc2cbde11890ccb254a8fc9d537fa41b36da00de2a1c5e9848c9bc42bd7"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "streaming-decompression"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf6cc3b19bfb128a8ad11026086e31d3ce9ad23f8ea37354b31383a187c44cf3"
+dependencies = [
+ "fallible-streaming-iterator",
+]
+
+[[package]]
+name = "streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
+
+[[package]]
+name = "strength_reduce"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "strum_macros"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "subtle"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.29.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a18d114d420ada3a891e6bc8e96a2023402203296a47cdd65083377dad18ba5"
+dependencies = [
+ "cfg-if",
+ "core-foundation-sys",
+ "libc",
+ "ntapi",
+ "once_cell",
+ "winapi",
+]
+
+[[package]]
+name = "target-features"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfb5fa503293557c5158bd215fdc225695e567a77e453f5d4452a50a193969bd"
+
+[[package]]
+name = "tempfile"
+version = "3.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall 0.4.1",
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "tokio"
+version = "1.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "parking_lot 0.12.1",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2 0.5.5",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.23.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
+dependencies = [
+ "rustls",
+ "tokio",
+ "webpki",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
+
+[[package]]
+name = "web-sys"
+version = "0.3.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
+dependencies = [
+ "ring 0.17.5",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.51.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
+
+[[package]]
+name = "xxhash-rust"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9828b178da53440fa9c766a3d2f73f7cf5d0ac1fe3980c1e5018d899fd19e07b"
+
+[[package]]
+name = "xz2"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
+dependencies = [
+ "lzma-sys",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.7.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+
+[[package]]
+name = "zstd"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.9+zstd.1.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
diff --git a/tvix/tools/crunch-v2/Cargo.nix b/tvix/tools/crunch-v2/Cargo.nix
new file mode 100644
index 0000000000..35c09ecee7
--- /dev/null
+++ b/tvix/tools/crunch-v2/Cargo.nix
@@ -0,0 +1,11881 @@
+# This file was @generated by crate2nix 0.13.0 with the command:
+#   "generate" "--all-features"
+# See https://github.com/kolloch/crate2nix for more info.
+
+{ nixpkgs ? <nixpkgs>
+, pkgs ? import nixpkgs { config = { }; }
+, lib ? pkgs.lib
+, stdenv ? pkgs.stdenv
+, buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate
+  # This is used as the `crateOverrides` argument for `buildRustCrate`.
+, defaultCrateOverrides ? pkgs.defaultCrateOverrides
+  # The features to enable for the root_crate or the workspace_members.
+, rootFeatures ? [ "default" ]
+  # If true, throw errors instead of issueing deprecation warnings.
+, strictDeprecation ? false
+  # Used for conditional compilation based on CPU feature detection.
+, targetFeatures ? [ ]
+  # Whether to perform release builds: longer compile times, faster binaries.
+, release ? true
+  # Additional crate2nix configuration if it exists.
+, crateConfig ? if builtins.pathExists ./crate-config.nix
+  then pkgs.callPackage ./crate-config.nix { }
+  else { }
+}:
+
+rec {
+  #
+  # "public" attributes that we attempt to keep stable with new versions of crate2nix.
+  #
+
+  rootCrate = rec {
+    packageId = "crunch-v2";
+
+    # Use this attribute to refer to the derivation building your root crate package.
+    # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }.
+    build = internal.buildRustCrateWithFeatures {
+      inherit packageId;
+    };
+
+    # Debug support which might change between releases.
+    # File a bug if you depend on any for non-debug work!
+    debug = internal.debugCrate { inherit packageId; };
+  };
+  # Refer your crate build derivation by name here.
+  # You can override the features with
+  # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }.
+  workspaceMembers = {
+    "crunch-v2" = rec {
+      packageId = "crunch-v2";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "crunch-v2";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+  };
+
+  # A derivation that joins the outputs of all workspace members together.
+  allWorkspaceMembers = pkgs.symlinkJoin {
+    name = "all-workspace-members";
+    paths =
+      let members = builtins.attrValues workspaceMembers;
+      in builtins.map (m: m.build) members;
+  };
+
+  #
+  # "internal" ("private") attributes that may change in every new version of crate2nix.
+  #
+
+  internal = rec {
+    # Build and dependency information for crates.
+    # Many of the fields are passed one-to-one to buildRustCrate.
+    #
+    # Noteworthy:
+    # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate.
+    #   but with additional information which is used during dependency/feature resolution.
+    # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging.
+    # * `devDependencies` as of now not used by `buildRustCrate` but used to
+    #   inject test dependencies into the build
+
+    crates = {
+      "addr2line" = rec {
+        crateName = "addr2line";
+        version = "0.21.0";
+        edition = "2018";
+        sha256 = "1jx0k3iwyqr8klqbzk6kjvr496yd94aspis10vwsj5wy7gib4c4a";
+        dependencies = [
+          {
+            name = "gimli";
+            packageId = "gimli";
+            usesDefaultFeatures = false;
+            features = [ "read" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "rustc-demangle" "cpp_demangle" "std-object" "fallible-iterator" "smallvec" "memmap2" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "memmap2" = [ "dep:memmap2" ];
+          "object" = [ "dep:object" ];
+          "rustc-demangle" = [ "dep:rustc-demangle" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "gimli/rustc-dep-of-std" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "gimli/std" ];
+          "std-object" = [ "std" "object" "object/std" "object/compression" "gimli/endian-reader" ];
+        };
+      };
+      "adler" = rec {
+        crateName = "adler";
+        version = "1.0.2";
+        edition = "2015";
+        sha256 = "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj";
+        authors = [
+          "Jonas Schievink <jonasschievink@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "ahash" = rec {
+        crateName = "ahash";
+        version = "0.8.6";
+        edition = "2018";
+        sha256 = "0yn9i8nc6mmv28ig9w3dga571q09vg9f1f650mi5z8phx42r6hli";
+        authors = [
+          "Tom Kaitchuck <Tom.Kaitchuck@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!(("arm" == target."arch" or null) && ("none" == target."os" or null)));
+            features = [ "unstable" "alloc" ];
+          }
+          {
+            name = "zerocopy";
+            packageId = "zerocopy";
+            usesDefaultFeatures = false;
+            features = [ "simd" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "atomic-polyfill" = [ "dep:atomic-polyfill" "once_cell/atomic-polyfill" ];
+          "compile-time-rng" = [ "const-random" ];
+          "const-random" = [ "dep:const-random" ];
+          "default" = [ "std" "runtime-rng" ];
+          "getrandom" = [ "dep:getrandom" ];
+          "runtime-rng" = [ "getrandom" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "getrandom" "runtime-rng" "std" ];
+      };
+      "aho-corasick" = rec {
+        crateName = "aho-corasick";
+        version = "1.1.2";
+        edition = "2021";
+        sha256 = "1w510wnixvlgimkx1zjbvlxh6xps2vjgfqgwf5a6adlbjp5rv5mj";
+        libName = "aho_corasick";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf-literal" ];
+          "logging" = [ "dep:log" ];
+          "perf-literal" = [ "dep:memchr" ];
+          "std" = [ "memchr?/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "perf-literal" "std" ];
+      };
+      "alloc-no-stdlib" = rec {
+        crateName = "alloc-no-stdlib";
+        version = "2.0.4";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "1cy6r2sfv5y5cigv86vms7n5nlwhx1rbyxwcraqnmm1rxiib2yyc";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+        ];
+        features = { };
+      };
+      "alloc-stdlib" = rec {
+        crateName = "alloc-stdlib";
+        version = "0.2.2";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "1kkfbld20ab4165p29v172h8g0wvq8i06z8vnng14whw0isq5ywl";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+        ];
+        dependencies = [
+          {
+            name = "alloc-no-stdlib";
+            packageId = "alloc-no-stdlib";
+          }
+        ];
+        features = { };
+      };
+      "allocator-api2" = rec {
+        crateName = "allocator-api2";
+        version = "0.2.16";
+        edition = "2018";
+        sha256 = "1iayppgq4wqbfbfcqmsbwgamj0s65012sskfvyx07pxavk3gyhh9";
+        authors = [
+          "Zakarum <zaq.dev@icloud.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "android-tzdata" = rec {
+        crateName = "android-tzdata";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        authors = [
+          "RumovZ"
+        ];
+
+      };
+      "android_system_properties" = rec {
+        crateName = "android_system_properties";
+        version = "0.1.5";
+        edition = "2018";
+        sha256 = "04b3wrz12837j7mdczqd95b732gw5q7q66cv4yn4646lvccp57l1";
+        authors = [
+          "Nicolas Silva <nical@fastmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "anstream" = rec {
+        crateName = "anstream";
+        version = "0.6.11";
+        edition = "2021";
+        sha256 = "19dndamalavhjwp4i74k8hdijcixb7gsfa6ycwyc1r8xn6y1wbkf";
+        dependencies = [
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "anstyle-parse";
+            packageId = "anstyle-parse";
+          }
+          {
+            name = "anstyle-query";
+            packageId = "anstyle-query";
+            optional = true;
+          }
+          {
+            name = "anstyle-wincon";
+            packageId = "anstyle-wincon";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "colorchoice";
+            packageId = "colorchoice";
+          }
+          {
+            name = "utf8parse";
+            packageId = "utf8parse";
+          }
+        ];
+        features = {
+          "auto" = [ "dep:anstyle-query" ];
+          "default" = [ "auto" "wincon" ];
+          "wincon" = [ "dep:anstyle-wincon" ];
+        };
+        resolvedDefaultFeatures = [ "auto" "default" "wincon" ];
+      };
+      "anstyle" = rec {
+        crateName = "anstyle";
+        version = "1.0.4";
+        edition = "2021";
+        sha256 = "11yxw02b6parn29s757z96rgiqbn8qy0fk9a3p3bhczm85dhfybh";
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "anstyle-parse" = rec {
+        crateName = "anstyle-parse";
+        version = "0.2.3";
+        edition = "2021";
+        sha256 = "134jhzrz89labrdwxxnjxqjdg06qvaflj1wkfnmyapwyldfwcnn7";
+        dependencies = [
+          {
+            name = "utf8parse";
+            packageId = "utf8parse";
+            optional = true;
+          }
+        ];
+        features = {
+          "core" = [ "dep:arrayvec" ];
+          "default" = [ "utf8" ];
+          "utf8" = [ "dep:utf8parse" ];
+        };
+        resolvedDefaultFeatures = [ "default" "utf8" ];
+      };
+      "anstyle-query" = rec {
+        crateName = "anstyle-query";
+        version = "1.0.2";
+        edition = "2021";
+        sha256 = "0j3na4b1nma39g4x7cwvj009awxckjf3z2vkwhldgka44hqj72g2";
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_System_Console" "Win32_Foundation" ];
+          }
+        ];
+
+      };
+      "anstyle-wincon" = rec {
+        crateName = "anstyle-wincon";
+        version = "3.0.2";
+        edition = "2021";
+        sha256 = "19v0fv400bmp4niqpzxnhg83vz12mmqv7l2l8vi80qcdxj0lpm8w";
+        dependencies = [
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_System_Console" "Win32_Foundation" ];
+          }
+        ];
+
+      };
+      "anyhow" = rec {
+        crateName = "anyhow";
+        version = "1.0.75";
+        edition = "2018";
+        sha256 = "1rmcjkim91c5mw7h9wn8nv0k6x118yz0xg0z1q18svgn42mqqrm4";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            optional = true;
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "backtrace" "default" "std" ];
+      };
+      "argminmax" = rec {
+        crateName = "argminmax";
+        version = "0.6.1";
+        edition = "2021";
+        sha256 = "1lnvpkvdsvdbsinhik6srx5c2j3gqkaj92iz93pnbdr9cjs0h890";
+        authors = [
+          "Jeroen Van Der Donckt"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "arrow" = [ "dep:arrow" ];
+          "arrow2" = [ "dep:arrow2" ];
+          "default" = [ "nightly_simd" "float" ];
+          "half" = [ "dep:half" ];
+          "ndarray" = [ "dep:ndarray" ];
+        };
+        resolvedDefaultFeatures = [ "float" ];
+      };
+      "array-init-cursor" = rec {
+        crateName = "array-init-cursor";
+        version = "0.2.0";
+        edition = "2021";
+        sha256 = "0xpbqf7qkvzplpjd7f0wbcf2n1v9vygdccwxkd1amxp4il0hlzdz";
+
+      };
+      "arrayref" = rec {
+        crateName = "arrayref";
+        version = "0.3.7";
+        edition = "2015";
+        sha256 = "0ia5ndyxqkzdymqr4ls53jdmajf09adjimg5kvw65kkprg930jbb";
+        authors = [
+          "David Roundy <roundyd@physics.oregonstate.edu>"
+        ];
+
+      };
+      "arrayvec" = rec {
+        crateName = "arrayvec";
+        version = "0.7.4";
+        edition = "2018";
+        sha256 = "04b7n722jij0v3fnm3qk072d5ysc2q30rl9fz33zpfhzah30mlwn";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+      };
+      "arrow-format" = rec {
+        crateName = "arrow-format";
+        version = "0.8.1";
+        edition = "2018";
+        sha256 = "1irj67p6c224dzw86jr7j3z9r5zfid52gy6ml8rdqk4r2si4x207";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "planus";
+            packageId = "planus";
+            optional = true;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "derive" "std" ];
+          }
+        ];
+        features = {
+          "flight-data" = [ "prost" "prost-derive" ];
+          "flight-service" = [ "flight-data" "tonic" ];
+          "full" = [ "ipc" "flight-data" "flight-service" ];
+          "ipc" = [ "planus" "serde" ];
+          "planus" = [ "dep:planus" ];
+          "prost" = [ "dep:prost" ];
+          "prost-derive" = [ "dep:prost-derive" ];
+          "serde" = [ "dep:serde" ];
+          "tonic" = [ "dep:tonic" ];
+        };
+        resolvedDefaultFeatures = [ "default" "ipc" "planus" "serde" ];
+      };
+      "async-stream" = rec {
+        crateName = "async-stream";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream-impl";
+            packageId = "async-stream-impl";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "async-stream-impl" = rec {
+        crateName = "async-stream-impl";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
+        procMacro = true;
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "async-trait" = rec {
+        crateName = "async-trait";
+        version = "0.1.74";
+        edition = "2021";
+        sha256 = "1ydhbsqjqqa6bxbv0kgys2wq2vi3jpwjy57dk162ajwppgqkfrd6";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "atoi" = rec {
+        crateName = "atoi";
+        version = "2.0.0";
+        edition = "2021";
+        sha256 = "0a05h42fggmy7h0ajjv6m7z72l924i7igbx13hk9d8pyign9k3gj";
+        authors = [
+          "Markus Klein"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "num-traits/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "atoi_simd" = rec {
+        crateName = "atoi_simd";
+        version = "0.15.3";
+        edition = "2018";
+        sha256 = "09jiwr7074j73viiafdzjq9mf39idd784hfpsbf1p1dn05gbchgw";
+        authors = [
+          "Dmitry Rodionov <gh@rdmtr.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "arrayvec/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "autocfg" = rec {
+        crateName = "autocfg";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "1ylp3cb47ylzabimazvbz9ms6ap784zhb6syaz6c1jqpmcmq0s6l";
+        authors = [
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+
+      };
+      "backtrace" = rec {
+        crateName = "backtrace";
+        version = "0.3.69";
+        edition = "2018";
+        sha256 = "0dsq23dhw4pfndkx2nsa1ml2g31idm7ss7ljxp8d57avygivg290";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "addr2line";
+            packageId = "addr2line";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "object";
+            packageId = "object";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+            features = [ "read_core" "elf" "macho" "pe" "unaligned" "archive" ];
+          }
+          {
+            name = "rustc-demangle";
+            packageId = "rustc-demangle";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "std" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "serialize-rustc" = [ "rustc-serialize" ];
+          "serialize-serde" = [ "serde" ];
+          "verify-winapi" = [ "winapi/dbghelp" "winapi/handleapi" "winapi/libloaderapi" "winapi/memoryapi" "winapi/minwindef" "winapi/processthreadsapi" "winapi/synchapi" "winapi/tlhelp32" "winapi/winbase" "winapi/winnt" ];
+          "winapi" = [ "dep:winapi" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64 0.13.1" = rec {
+        crateName = "base64";
+        version = "0.13.1";
+        edition = "2018";
+        sha256 = "1s494mqmzjb766fy1kqlccgfg2sdcjb6hzbvzqv2jw65fdi5h6wy";
+        authors = [
+          "Alice Maz <alice@alicemaz.com>"
+          "Marshall Pierce <marshall@mpierce.org>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64 0.21.5" = rec {
+        crateName = "base64";
+        version = "0.21.5";
+        edition = "2018";
+        sha256 = "1y8x2xs9nszj5ix7gg4ycn5a6wy7ca74zxwqri3bdqzdjha6lqrm";
+        authors = [
+          "Alice Maz <alice@alicemaz.com>"
+          "Marshall Pierce <marshall@mpierce.org>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "base64ct" = rec {
+        crateName = "base64ct";
+        version = "1.6.0";
+        edition = "2021";
+        sha256 = "0nvdba4jb8aikv60az40x2w1y96sjdq8z3yp09rwzmkhiwv1lg4c";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "bitflags 1.3.2" = rec {
+        crateName = "bitflags";
+        version = "1.3.2";
+        edition = "2018";
+        sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bitflags 2.4.1" = rec {
+        crateName = "bitflags";
+        version = "2.4.1";
+        edition = "2021";
+        sha256 = "01ryy3kd671b0ll4bhdvhsz67vwz1lz53fz504injrd7wpv64xrj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "blake3" = rec {
+        crateName = "blake3";
+        version = "1.5.0";
+        edition = "2021";
+        sha256 = "11ysh12zcqq6xkjxh5cbrmnwzalprm3z552i5ff7wm5za9hz0c82";
+        authors = [
+          "Jack O'Connor <oconnor663@gmail.com>"
+          "Samuel Neves"
+        ];
+        dependencies = [
+          {
+            name = "arrayref";
+            packageId = "arrayref";
+          }
+          {
+            name = "arrayvec";
+            packageId = "arrayvec";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "constant_time_eq";
+            packageId = "constant_time_eq";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "digest" = [ "dep:digest" ];
+          "mmap" = [ "std" "dep:memmap2" ];
+          "rayon" = [ "dep:rayon" "std" ];
+          "serde" = [ "dep:serde" ];
+          "traits-preview" = [ "digest" ];
+          "zeroize" = [ "dep:zeroize" "arrayvec/zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "block-buffer 0.10.4" = rec {
+        crateName = "block-buffer";
+        version = "0.10.4";
+        edition = "2018";
+        sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+
+      };
+      "block-buffer 0.9.0" = rec {
+        crateName = "block-buffer";
+        version = "0.9.0";
+        edition = "2018";
+        sha256 = "1r4pf90s7d7lj1wdjhlnqa26vvbm6pnc33z138lxpnp9srpi2lj1";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+        features = {
+          "block-padding" = [ "dep:block-padding" ];
+        };
+      };
+      "brotli" = rec {
+        crateName = "brotli";
+        version = "3.4.0";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "03qhcq09a6f8y4gm0bmsn7jrq5804cwpkcx3fyay1g7lgsj78q2i";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+          "The Brotli Authors"
+        ];
+        dependencies = [
+          {
+            name = "alloc-no-stdlib";
+            packageId = "alloc-no-stdlib";
+          }
+          {
+            name = "alloc-stdlib";
+            packageId = "alloc-stdlib";
+            optional = true;
+          }
+          {
+            name = "brotli-decompressor";
+            packageId = "brotli-decompressor";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc-stdlib" = [ "dep:alloc-stdlib" ];
+          "benchmark" = [ "brotli-decompressor/benchmark" ];
+          "default" = [ "std" "ffi-api" ];
+          "disable-timer" = [ "brotli-decompressor/disable-timer" ];
+          "seccomp" = [ "brotli-decompressor/seccomp" ];
+          "sha2" = [ "dep:sha2" ];
+          "std" = [ "alloc-stdlib" "brotli-decompressor/std" ];
+          "validation" = [ "sha2" ];
+        };
+        resolvedDefaultFeatures = [ "alloc-stdlib" "default" "ffi-api" "std" ];
+      };
+      "brotli-decompressor" = rec {
+        crateName = "brotli-decompressor";
+        version = "2.5.1";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "0kyyh9701dwqzwvn2frff4ww0zibikqd1s1xvl7n1pfpc3z4lbjf";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+          "The Brotli Authors"
+        ];
+        dependencies = [
+          {
+            name = "alloc-no-stdlib";
+            packageId = "alloc-no-stdlib";
+          }
+          {
+            name = "alloc-stdlib";
+            packageId = "alloc-stdlib";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc-stdlib" = [ "dep:alloc-stdlib" ];
+          "default" = [ "std" ];
+          "std" = [ "alloc-stdlib" ];
+          "unsafe" = [ "alloc-no-stdlib/unsafe" "alloc-stdlib/unsafe" ];
+        };
+        resolvedDefaultFeatures = [ "alloc-stdlib" "std" ];
+      };
+      "bstr" = rec {
+        crateName = "bstr";
+        version = "1.8.0";
+        edition = "2021";
+        sha256 = "0313sqdf0a40vhpnrlkf54zhr76rmlyxzhx00sq8822shfl36bsl";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "dfa-search" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "memchr/alloc" "serde?/alloc" ];
+          "default" = [ "std" "unicode" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" "memchr/std" "serde?/std" ];
+          "unicode" = [ "dep:regex-automata" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "serde" "std" "unicode" ];
+      };
+      "bumpalo" = rec {
+        crateName = "bumpalo";
+        version = "3.14.0";
+        edition = "2021";
+        sha256 = "1v4arnv9kwk54v5d0qqpv4vyw2sgr660nk0w3apzixi1cm3yfc3z";
+        authors = [
+          "Nick Fitzgerald <fitzgen@gmail.com>"
+        ];
+        features = {
+          "allocator-api2" = [ "dep:allocator-api2" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bytemuck" = rec {
+        crateName = "bytemuck";
+        version = "1.14.0";
+        edition = "2018";
+        sha256 = "1ik1ma5n3bg700skkzhx50zjk7kj7mbsphi773if17l04pn2hk9p";
+        authors = [
+          "Lokathor <zefria@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytemuck_derive";
+            packageId = "bytemuck_derive";
+            optional = true;
+          }
+        ];
+        features = {
+          "bytemuck_derive" = [ "dep:bytemuck_derive" ];
+          "derive" = [ "bytemuck_derive" ];
+          "extern_crate_std" = [ "extern_crate_alloc" ];
+        };
+        resolvedDefaultFeatures = [ "bytemuck_derive" "derive" "extern_crate_alloc" ];
+      };
+      "bytemuck_derive" = rec {
+        crateName = "bytemuck_derive";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "1cgj75df2v32l4fmvnp25xxkkz4lp6hz76f7hfhd55wgbzmvfnln";
+        procMacro = true;
+        authors = [
+          "Lokathor <zefria@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+          }
+        ];
+
+      };
+      "byteorder" = rec {
+        crateName = "byteorder";
+        version = "1.5.0";
+        edition = "2021";
+        sha256 = "0jzncxyf404mwqdbspihyzpkndfgda450l0893pz5xj685cg5l0z";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "bytes" = rec {
+        crateName = "bytes";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "bzip2" = rec {
+        crateName = "bzip2";
+        version = "0.4.4";
+        edition = "2015";
+        sha256 = "1y27wgqkx3k2jmh4k26vra2kqjq1qc1asww8hac3cv1zxyk1dcdx";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "bzip2-sys";
+            packageId = "bzip2-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "futures" = [ "dep:futures" ];
+          "static" = [ "bzip2-sys/static" ];
+          "tokio" = [ "tokio-io" "futures" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+        };
+      };
+      "bzip2-sys" = rec {
+        crateName = "bzip2-sys";
+        version = "0.1.11+1.0.8";
+        edition = "2015";
+        links = "bzip2";
+        sha256 = "1p2crnv8d8gpz5c2vlvzl0j55i3yqg5bi0kwsl1531x77xgraskk";
+        libName = "bzip2_sys";
+        libPath = "lib.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+        ];
+        features = { };
+      };
+      "cc" = rec {
+        crateName = "cc";
+        version = "1.0.83";
+        edition = "2018";
+        crateBin = [ ];
+        sha256 = "1l643zidlb5iy1dskc5ggqs4wqa29a02f44piczqc8zcnsq4y5zi";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "jobserver";
+            packageId = "jobserver";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        features = {
+          "jobserver" = [ "dep:jobserver" ];
+          "parallel" = [ "jobserver" ];
+        };
+        resolvedDefaultFeatures = [ "jobserver" "parallel" ];
+      };
+      "cfg-if" = rec {
+        crateName = "cfg-if";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "chrono" = rec {
+        crateName = "chrono";
+        version = "0.4.31";
+        edition = "2021";
+        sha256 = "0f6vg67pipm8cziad2yms6a639pssnvysk1m05dd9crymmdnhb3z";
+        dependencies = [
+          {
+            name = "android-tzdata";
+            packageId = "android-tzdata";
+            optional = true;
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "iana-time-zone";
+            packageId = "iana-time-zone";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+            features = [ "fallback" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "android-tzdata" = [ "dep:android-tzdata" ];
+          "arbitrary" = [ "dep:arbitrary" ];
+          "clock" = [ "std" "winapi" "iana-time-zone" "android-tzdata" ];
+          "default" = [ "clock" "std" "oldtime" "wasmbind" ];
+          "iana-time-zone" = [ "dep:iana-time-zone" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "pure-rust-locales" = [ "dep:pure-rust-locales" ];
+          "rkyv" = [ "dep:rkyv" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "unstable-locales" = [ "pure-rust-locales" "alloc" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+          "wasmbind" = [ "wasm-bindgen" "js-sys" ];
+          "winapi" = [ "windows-targets" ];
+          "windows-targets" = [ "dep:windows-targets" ];
+        };
+        resolvedDefaultFeatures = [ "android-tzdata" "clock" "iana-time-zone" "serde" "std" "winapi" "windows-targets" ];
+      };
+      "clap" = rec {
+        crateName = "clap";
+        version = "4.4.18";
+        edition = "2021";
+        crateBin = [ ];
+        sha256 = "0p46h346y8nval6gwzh27if3icbi9dwl95fg5ir36ihrqip8smqy";
+        dependencies = [
+          {
+            name = "clap_builder";
+            packageId = "clap_builder";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "clap_derive";
+            packageId = "clap_derive";
+            optional = true;
+          }
+        ];
+        features = {
+          "cargo" = [ "clap_builder/cargo" ];
+          "color" = [ "clap_builder/color" ];
+          "debug" = [ "clap_builder/debug" "clap_derive?/debug" ];
+          "default" = [ "std" "color" "help" "usage" "error-context" "suggestions" ];
+          "deprecated" = [ "clap_builder/deprecated" "clap_derive?/deprecated" ];
+          "derive" = [ "dep:clap_derive" ];
+          "env" = [ "clap_builder/env" ];
+          "error-context" = [ "clap_builder/error-context" ];
+          "help" = [ "clap_builder/help" ];
+          "std" = [ "clap_builder/std" ];
+          "string" = [ "clap_builder/string" ];
+          "suggestions" = [ "clap_builder/suggestions" ];
+          "unicode" = [ "clap_builder/unicode" ];
+          "unstable-doc" = [ "clap_builder/unstable-doc" "derive" ];
+          "unstable-styles" = [ "clap_builder/unstable-styles" ];
+          "unstable-v5" = [ "clap_builder/unstable-v5" "clap_derive?/unstable-v5" "deprecated" ];
+          "usage" = [ "clap_builder/usage" ];
+          "wrap_help" = [ "clap_builder/wrap_help" ];
+        };
+        resolvedDefaultFeatures = [ "color" "default" "derive" "error-context" "help" "std" "suggestions" "usage" ];
+      };
+      "clap_builder" = rec {
+        crateName = "clap_builder";
+        version = "4.4.18";
+        edition = "2021";
+        sha256 = "1iyif47075caa4x1p3ygk18b07lb4xl4k48w4c061i2hxi0dzx2d";
+        dependencies = [
+          {
+            name = "anstream";
+            packageId = "anstream";
+            optional = true;
+          }
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "clap_lex";
+            packageId = "clap_lex";
+          }
+          {
+            name = "strsim";
+            packageId = "strsim";
+            optional = true;
+          }
+        ];
+        features = {
+          "color" = [ "dep:anstream" ];
+          "debug" = [ "dep:backtrace" ];
+          "default" = [ "std" "color" "help" "usage" "error-context" "suggestions" ];
+          "std" = [ "anstyle/std" ];
+          "suggestions" = [ "dep:strsim" "error-context" ];
+          "unicode" = [ "dep:unicode-width" "dep:unicase" ];
+          "unstable-doc" = [ "cargo" "wrap_help" "env" "unicode" "string" ];
+          "unstable-styles" = [ "color" ];
+          "unstable-v5" = [ "deprecated" ];
+          "wrap_help" = [ "help" "dep:terminal_size" ];
+        };
+        resolvedDefaultFeatures = [ "color" "error-context" "help" "std" "suggestions" "usage" ];
+      };
+      "clap_derive" = rec {
+        crateName = "clap_derive";
+        version = "4.4.7";
+        edition = "2021";
+        sha256 = "0hk4hcxl56qwqsf4hmf7c0gr19r9fbxk0ah2bgkr36pmmaph966g";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "raw-deprecated" = [ "deprecated" ];
+          "unstable-v5" = [ "deprecated" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "clap_lex" = rec {
+        crateName = "clap_lex";
+        version = "0.6.0";
+        edition = "2021";
+        sha256 = "1l8bragdvim7mva9flvd159dskn2bdkpl0jqrr41wnjfn8pcfbvh";
+
+      };
+      "colorchoice" = rec {
+        crateName = "colorchoice";
+        version = "1.0.0";
+        edition = "2021";
+        sha256 = "1ix7w85kwvyybwi2jdkl3yva2r2bvdcc3ka2grjfzfgrapqimgxc";
+
+      };
+      "console" = rec {
+        crateName = "console";
+        version = "0.15.7";
+        edition = "2018";
+        sha256 = "1y6cbwadid5g4fyn4xq9c0s7mfavqqfg6prs9p3gvphfqw6f09n9";
+        authors = [
+          "Armin Ronacher <armin.ronacher@active-4.com>"
+        ];
+        dependencies = [
+          {
+            name = "encode_unicode";
+            packageId = "encode_unicode";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "unicode-width";
+            packageId = "unicode-width";
+            optional = true;
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.45.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_System_Console" "Win32_Storage_FileSystem" "Win32_UI_Input_KeyboardAndMouse" ];
+          }
+        ];
+        features = {
+          "default" = [ "unicode-width" "ansi-parsing" ];
+          "unicode-width" = [ "dep:unicode-width" ];
+          "windows-console-colors" = [ "ansi-parsing" ];
+        };
+        resolvedDefaultFeatures = [ "ansi-parsing" "unicode-width" ];
+      };
+      "const-oid" = rec {
+        crateName = "const-oid";
+        version = "0.9.5";
+        edition = "2021";
+        sha256 = "0vxb4d25mgk8y0phay7j078limx2553716ixsr1x5605k31j5h98";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+        };
+      };
+      "constant_time_eq" = rec {
+        crateName = "constant_time_eq";
+        version = "0.3.0";
+        edition = "2021";
+        sha256 = "1hl0y8frzlhpr58rh8rlg4bm53ax09ikj2i5fk7gpyphvhq4s57p";
+        authors = [
+          "Cesar Eduardo Barros <cesarb@cesarb.eti.br>"
+        ];
+        features = { };
+      };
+      "core-foundation" = rec {
+        crateName = "core-foundation";
+        version = "0.9.3";
+        edition = "2015";
+        sha256 = "0ii1ihpjb30fk38gdikm5wqlkmyr8k46fh4k2r8sagz5dng7ljhr";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "chrono" = [ "dep:chrono" ];
+          "mac_os_10_7_support" = [ "core-foundation-sys/mac_os_10_7_support" ];
+          "mac_os_10_8_features" = [ "core-foundation-sys/mac_os_10_8_features" ];
+          "uuid" = [ "dep:uuid" ];
+          "with-chrono" = [ "chrono" ];
+          "with-uuid" = [ "uuid" ];
+        };
+      };
+      "core-foundation-sys" = rec {
+        crateName = "core-foundation-sys";
+        version = "0.8.4";
+        edition = "2015";
+        sha256 = "1yhf471qj6snnm2mcswai47vsbc9w30y4abmdp4crb4av87sb5p4";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = { };
+      };
+      "cpufeatures" = rec {
+        crateName = "cpufeatures";
+        version = "0.2.11";
+        edition = "2018";
+        sha256 = "1l0gzsyy576n017g9bf0vkv5hhg9cpz1h1libxyfdlzcgbh0yhnf";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-linux-android");
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("apple" == target."vendor" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("loongarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+        ];
+
+      };
+      "crc32fast" = rec {
+        crateName = "crc32fast";
+        version = "1.3.2";
+        edition = "2015";
+        sha256 = "03c8f29yx293yf43xar946xbls1g60c207m9drf8ilqhr25vsh5m";
+        authors = [
+          "Sam Rijs <srijs@airpost.net>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crossbeam-channel" = rec {
+        crateName = "crossbeam-channel";
+        version = "0.5.8";
+        edition = "2018";
+        sha256 = "004jz4wxp9k26z657i7rsh9s7586dklx2c5aqf1n3w1dgzvjng53";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "crossbeam-utils" = [ "dep:crossbeam-utils" ];
+          "default" = [ "std" ];
+          "std" = [ "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "crossbeam-utils" "default" "std" ];
+      };
+      "crossbeam-deque" = rec {
+        crateName = "crossbeam-deque";
+        version = "0.8.3";
+        edition = "2018";
+        sha256 = "1vqczbcild7nczh5z116w8w46z991kpjyw7qxkf24c14apwdcvyf";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "crossbeam-epoch";
+            packageId = "crossbeam-epoch";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "crossbeam-epoch" = [ "dep:crossbeam-epoch" ];
+          "crossbeam-utils" = [ "dep:crossbeam-utils" ];
+          "default" = [ "std" ];
+          "std" = [ "crossbeam-epoch/std" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "crossbeam-epoch" "crossbeam-utils" "default" "std" ];
+      };
+      "crossbeam-epoch" = rec {
+        crateName = "crossbeam-epoch";
+        version = "0.9.15";
+        edition = "2018";
+        sha256 = "1ixwc3cq816wb8rlh3ix4jnybqbyyq4l61nwlx0mfm3ck0s148df";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memoffset";
+            packageId = "memoffset";
+          }
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "loom" = [ "loom-crate" "crossbeam-utils/loom" ];
+          "loom-crate" = [ "dep:loom-crate" ];
+          "nightly" = [ "crossbeam-utils/nightly" ];
+          "std" = [ "alloc" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "crossbeam-queue" = rec {
+        crateName = "crossbeam-queue";
+        version = "0.3.8";
+        edition = "2018";
+        sha256 = "1p9s6n4ckwdgxkb7a8ay9zjzmgc8ppfbxix2vr07rwskibmb7kyi";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "nightly" = [ "crossbeam-utils/nightly" ];
+          "std" = [ "alloc" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "crossbeam-utils" = rec {
+        crateName = "crossbeam-utils";
+        version = "0.8.16";
+        edition = "2018";
+        sha256 = "153j0gikblz7n7qdvdi8pslhi008s1yp9cmny6vw07ad7pbb48js";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "loom" = [ "dep:loom" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crunch-v2" = rec {
+        crateName = "crunch-v2";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "crunch-v2";
+            path = "src/main.rs";
+            requiredFeatures = [ ];
+          }
+          {
+            name = "extract";
+            path = "src/bin/extract.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./.; }
+          else ./.;
+        dependencies = [
+          {
+            name = "anyhow";
+            packageId = "anyhow";
+            features = [ "backtrace" ];
+          }
+          {
+            name = "blake3";
+            packageId = "blake3";
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "bzip2";
+            packageId = "bzip2";
+          }
+          {
+            name = "clap";
+            packageId = "clap";
+            features = [ "derive" ];
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.10.7";
+          }
+          {
+            name = "fastcdc";
+            packageId = "fastcdc";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "indicatif";
+            packageId = "indicatif";
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+          }
+          {
+            name = "polars";
+            packageId = "polars";
+            usesDefaultFeatures = false;
+            features = [ "parquet" "lazy" "sql" "dtype-struct" ];
+          }
+          {
+            name = "prost";
+            packageId = "prost";
+          }
+          {
+            name = "rusoto_core";
+            packageId = "rusoto_core";
+            usesDefaultFeatures = false;
+            features = [ "hyper-rustls" ];
+          }
+          {
+            name = "rusoto_s3";
+            packageId = "rusoto_s3";
+            usesDefaultFeatures = false;
+            features = [ "rustls" ];
+          }
+          {
+            name = "sha2";
+            packageId = "sha2 0.10.8";
+            features = [ "asm" ];
+          }
+          {
+            name = "sled";
+            packageId = "sled";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+          {
+            name = "xz2";
+            packageId = "xz2";
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "prost-build";
+            packageId = "prost-build";
+          }
+        ];
+
+      };
+      "crypto-common" = rec {
+        crateName = "crypto-common";
+        version = "0.1.6";
+        edition = "2018";
+        sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+            features = [ "more_lengths" ];
+          }
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        features = {
+          "getrandom" = [ "rand_core/getrandom" ];
+          "rand_core" = [ "dep:rand_core" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "crypto-mac" = rec {
+        crateName = "crypto-mac";
+        version = "0.11.1";
+        edition = "2018";
+        sha256 = "05672ncc54h66vph42s0a42ljl69bwnqjh0x4xgj2v1395psildi";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "cipher" = [ "dep:cipher" ];
+          "dev" = [ "blobby" ];
+        };
+      };
+      "curve25519-dalek" = rec {
+        crateName = "curve25519-dalek";
+        version = "4.1.1";
+        edition = "2021";
+        sha256 = "0p7ns5917k6369gajrsbfj24llc5zfm635yh3abla7sb5rm8r6z8";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: ("x86_64" == target."arch" or null);
+          }
+          {
+            name = "curve25519-dalek-derive";
+            packageId = "curve25519-dalek-derive";
+            target = { target, features }: ((!("fiat" == target."curve25519_dalek_backend" or null)) && (!("serial" == target."curve25519_dalek_backend" or null)) && ("x86_64" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.10.7";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "fiat-crypto";
+            packageId = "fiat-crypto";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("fiat" == target."curve25519_dalek_backend" or null);
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "platforms";
+            packageId = "platforms";
+          }
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "default" = [ "alloc" "precomputed-tables" "zeroize" ];
+          "digest" = [ "dep:digest" ];
+          "ff" = [ "dep:ff" ];
+          "group" = [ "dep:group" "rand_core" ];
+          "group-bits" = [ "group" "ff/bits" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "digest" "precomputed-tables" "zeroize" ];
+      };
+      "curve25519-dalek-derive" = rec {
+        crateName = "curve25519-dalek-derive";
+        version = "0.1.1";
+        edition = "2021";
+        sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "data-encoding" = rec {
+        crateName = "data-encoding";
+        version = "2.4.0";
+        edition = "2018";
+        sha256 = "023k3dk8422jgbj7k72g63x51h1mhv91dhw1j4h205vzh6fnrrn2";
+        authors = [
+          "Julien Cretin <git@ia0.eu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "der" = rec {
+        crateName = "der";
+        version = "0.7.8";
+        edition = "2021";
+        sha256 = "070bwiyr80800h31c5zd96ckkgagfjgnrrdmz3dzg2lccsd3dypz";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "const-oid";
+            packageId = "const-oid";
+            optional = true;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "arbitrary" = [ "dep:arbitrary" "const-oid?/arbitrary" "std" ];
+          "bytes" = [ "dep:bytes" "alloc" ];
+          "derive" = [ "dep:der_derive" ];
+          "flagset" = [ "dep:flagset" ];
+          "oid" = [ "dep:const-oid" ];
+          "pem" = [ "dep:pem-rfc7468" "alloc" "zeroize" ];
+          "std" = [ "alloc" ];
+          "time" = [ "dep:time" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "oid" "std" "zeroize" ];
+      };
+      "digest 0.10.7" = rec {
+        crateName = "digest";
+        version = "0.10.7";
+        edition = "2018";
+        sha256 = "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer 0.10.4";
+            optional = true;
+          }
+          {
+            name = "crypto-common";
+            packageId = "crypto-common";
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "block-buffer" = [ "dep:block-buffer" ];
+          "const-oid" = [ "dep:const-oid" ];
+          "core-api" = [ "block-buffer" ];
+          "default" = [ "core-api" ];
+          "dev" = [ "blobby" ];
+          "mac" = [ "subtle" ];
+          "oid" = [ "const-oid" ];
+          "rand_core" = [ "crypto-common/rand_core" ];
+          "std" = [ "alloc" "crypto-common/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "block-buffer" "core-api" "default" "std" ];
+      };
+      "digest 0.9.0" = rec {
+        crateName = "digest";
+        version = "0.9.0";
+        edition = "2018";
+        sha256 = "0rmhvk33rgvd6ll71z8sng91a52rw14p0drjn1da0mqa138n1pfk";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "dev" = [ "blobby" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "dirs-next" = rec {
+        crateName = "dirs-next";
+        version = "2.0.0";
+        edition = "2018";
+        sha256 = "1q9kr151h9681wwp6is18750ssghz6j9j7qm7qi1ngcwy7mzi35r";
+        authors = [
+          "The @xdg-rs members"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "dirs-sys-next";
+            packageId = "dirs-sys-next";
+          }
+        ];
+
+      };
+      "dirs-sys-next" = rec {
+        crateName = "dirs-sys-next";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "0kavhavdxv4phzj4l0psvh55hszwnr0rcz8sxbvx20pyqi2a3gaf";
+        authors = [
+          "The @xdg-rs members"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_users";
+            packageId = "redox_users";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "knownfolders" "objbase" "shlobj" "winbase" "winerror" ];
+          }
+        ];
+
+      };
+      "dyn-clone" = rec {
+        crateName = "dyn-clone";
+        version = "1.0.16";
+        edition = "2018";
+        sha256 = "0pa9kas6a241pbx0q82ipwi4f7m7wwyzkkc725caky24gl4j4nsl";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "ed25519" = rec {
+        crateName = "ed25519";
+        version = "2.2.3";
+        edition = "2021";
+        sha256 = "0lydzdf26zbn82g7xfczcac9d7mzm3qgx934ijjrd5hjpjx32m8i";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "pkcs8";
+            packageId = "pkcs8";
+            optional = true;
+          }
+          {
+            name = "signature";
+            packageId = "signature";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "pkcs8?/alloc" ];
+          "default" = [ "std" ];
+          "pem" = [ "alloc" "pkcs8/pem" ];
+          "pkcs8" = [ "dep:pkcs8" ];
+          "serde" = [ "dep:serde" ];
+          "serde_bytes" = [ "serde" "dep:serde_bytes" ];
+          "std" = [ "pkcs8?/std" "signature/std" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "ed25519-dalek" = rec {
+        crateName = "ed25519-dalek";
+        version = "2.1.0";
+        edition = "2021";
+        sha256 = "1h13qm789m9gdjl6jazss80hqi8ll37m0afwcnw23zcbqjp8wqhz";
+        authors = [
+          "isis lovecruft <isis@patternsinthevoid.net>"
+          "Tony Arcieri <bascule@gmail.com>"
+          "Michael Rosenberg <michael@mrosenberg.pub>"
+        ];
+        dependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" ];
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "sha2";
+            packageId = "sha2 0.10.8";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" "rand_core" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "curve25519-dalek/alloc" "ed25519/alloc" "serde?/alloc" "zeroize/alloc" ];
+          "asm" = [ "sha2/asm" ];
+          "batch" = [ "alloc" "merlin" "rand_core" ];
+          "default" = [ "fast" "std" "zeroize" ];
+          "digest" = [ "signature/digest" ];
+          "fast" = [ "curve25519-dalek/precomputed-tables" ];
+          "legacy_compatibility" = [ "curve25519-dalek/legacy_compatibility" ];
+          "merlin" = [ "dep:merlin" ];
+          "pem" = [ "alloc" "ed25519/pem" "pkcs8" ];
+          "pkcs8" = [ "ed25519/pkcs8" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" "ed25519/serde" ];
+          "signature" = [ "dep:signature" ];
+          "std" = [ "alloc" "ed25519/std" "serde?/std" "sha2/std" ];
+          "zeroize" = [ "dep:zeroize" "curve25519-dalek/zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fast" "std" "zeroize" ];
+      };
+      "either" = rec {
+        crateName = "either";
+        version = "1.9.0";
+        edition = "2018";
+        sha256 = "01qy3anr7jal5lpc20791vxrw0nl6vksb5j7x56q2fycgcyy8sm2";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "use_std" ];
+      };
+      "encode_unicode" = rec {
+        crateName = "encode_unicode";
+        version = "0.3.6";
+        edition = "2015";
+        sha256 = "07w3vzrhxh9lpjgsg2y5bwzfar2aq35mdznvcp3zjl0ssj7d4mx3";
+        authors = [
+          "TorbjΓΈrn Birch Moltu <t.b.moltu@lyse.net>"
+        ];
+        features = {
+          "ascii" = [ "dep:ascii" ];
+          "clippy" = [ "dep:clippy" ];
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "enum_dispatch" = rec {
+        crateName = "enum_dispatch";
+        version = "0.3.12";
+        edition = "2018";
+        sha256 = "03l998igqfzkykmj8i5qlbwhv2id9jn98fkkl82lv3dvg0q32cwg";
+        procMacro = true;
+        authors = [
+          "Anton Lazarev <https://antonok.com>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "equivalent" = rec {
+        crateName = "equivalent";
+        version = "1.0.1";
+        edition = "2015";
+        sha256 = "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl";
+
+      };
+      "errno" = rec {
+        crateName = "errno";
+        version = "0.3.7";
+        edition = "2018";
+        sha256 = "1f4f7s0wngfxwh6a4kq3aws3i37xnzm3m4d86xw2lz3z9qcsfn7j";
+        authors = [
+          "Chris Wong <lambda.fairy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_System_Diagnostics_Debug" ];
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "libc/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "ethnum" = rec {
+        crateName = "ethnum";
+        version = "1.5.0";
+        edition = "2021";
+        sha256 = "0b68ngvisb0d40vc6h30zlhghbb3mc8wlxjbf8gnmavk1dca435r";
+        authors = [
+          "Nicholas Rodrigues Lordello <nlordell@gmail.com>"
+        ];
+        features = {
+          "ethnum-intrinsics" = [ "dep:ethnum-intrinsics" ];
+          "llvm-intrinsics" = [ "ethnum-intrinsics" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "fallible-streaming-iterator" = rec {
+        crateName = "fallible-streaming-iterator";
+        version = "0.1.9";
+        edition = "2015";
+        sha256 = "0nj6j26p71bjy8h42x6jahx1hn0ng6mc2miwpgwnp8vnwqf4jq3k";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        features = { };
+      };
+      "fast-float" = rec {
+        crateName = "fast-float";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "0g7kfll3xyh99kc7r352lhljnwvgayxxa6saifb6725inikmyxlm";
+        authors = [
+          "Ivan Smirnov <i.s.smirnov@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "fastcdc" = rec {
+        crateName = "fastcdc";
+        version = "3.1.0";
+        edition = "2018";
+        sha256 = "1wi82qd58j3ysf8m2dhb092qga6rj1wwbppgsajabadzjz862457";
+        authors = [
+          "Nathan Fiedler <nathanfiedler@fastmail.fm>"
+        ];
+        features = {
+          "async-stream" = [ "dep:async-stream" ];
+          "futures" = [ "dep:futures" ];
+          "tokio" = [ "dep:tokio" "tokio-stream" "async-stream" ];
+          "tokio-stream" = [ "dep:tokio-stream" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "fastrand" = rec {
+        crateName = "fastrand";
+        version = "2.0.1";
+        edition = "2018";
+        sha256 = "19flpv5zbzpf0rk4x77z4zf25in0brg8l7m304d3yrf47qvwxjr5";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "getrandom" = [ "dep:getrandom" ];
+          "js" = [ "std" "getrandom" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "fiat-crypto" = rec {
+        crateName = "fiat-crypto";
+        version = "0.2.5";
+        edition = "2018";
+        sha256 = "1dxn0g50pv0ppal779vi7k40fr55pbhkyv4in7i13pgl4sn3wmr7";
+        authors = [
+          "Fiat Crypto library authors <jgross@mit.edu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+      };
+      "fixedbitset" = rec {
+        crateName = "fixedbitset";
+        version = "0.4.2";
+        edition = "2015";
+        sha256 = "101v41amgv5n9h4hcghvrbfk5vrncx1jwm35rn5szv4rk55i7rqc";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "flate2" = rec {
+        crateName = "flate2";
+        version = "1.0.28";
+        edition = "2018";
+        sha256 = "03llhsh4gqdirnfxxb9g2w9n0721dyn4yjir3pz7z4vjaxb3yc26";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Josh Triplett <josh@joshtriplett.org>"
+        ];
+        dependencies = [
+          {
+            name = "crc32fast";
+            packageId = "crc32fast";
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "with-alloc" ];
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!("emscripten" == target."os" or null)));
+            features = [ "with-alloc" ];
+          }
+        ];
+        features = {
+          "any_zlib" = [ "any_impl" ];
+          "cloudflare-zlib-sys" = [ "dep:cloudflare-zlib-sys" ];
+          "cloudflare_zlib" = [ "any_zlib" "cloudflare-zlib-sys" ];
+          "default" = [ "rust_backend" ];
+          "libz-ng-sys" = [ "dep:libz-ng-sys" ];
+          "libz-sys" = [ "dep:libz-sys" ];
+          "miniz-sys" = [ "rust_backend" ];
+          "miniz_oxide" = [ "dep:miniz_oxide" ];
+          "rust_backend" = [ "miniz_oxide" "any_impl" ];
+          "zlib" = [ "any_zlib" "libz-sys" ];
+          "zlib-default" = [ "any_zlib" "libz-sys/default" ];
+          "zlib-ng" = [ "any_zlib" "libz-ng-sys" ];
+          "zlib-ng-compat" = [ "zlib" "libz-sys/zlib-ng" ];
+        };
+        resolvedDefaultFeatures = [ "any_impl" "miniz_oxide" "rust_backend" ];
+      };
+      "fnv" = rec {
+        crateName = "fnv";
+        version = "1.0.7";
+        edition = "2015";
+        sha256 = "1hc2mcqha06aibcaza94vbi81j6pr9a1bbxrxjfhc91zin8yr7iz";
+        libPath = "lib.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "foreign_vec" = rec {
+        crateName = "foreign_vec";
+        version = "0.1.0";
+        edition = "2021";
+        sha256 = "0wv6p8yfahcqbdg2wg7wxgj4dm32g2b6spa5sg5sxg34v35ha6zf";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+        ];
+
+      };
+      "fs2" = rec {
+        crateName = "fs2";
+        version = "0.4.3";
+        edition = "2015";
+        sha256 = "04v2hwk7035c088f19mfl5b1lz84gnvv2hv6m935n0hmirszqr4m";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "handleapi" "processthreadsapi" "winerror" "fileapi" "winbase" "std" ];
+          }
+        ];
+
+      };
+      "futures" = rec {
+        crateName = "futures";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "0dak2ilpcmyjrb1j54fzy9hlw6vd10vqljq9gd59pbrq9dqr00ns";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-executor";
+            packageId = "futures-executor";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" "futures-sink/alloc" "futures-channel/alloc" "futures-util/alloc" ];
+          "async-await" = [ "futures-util/async-await" "futures-util/async-await-macro" ];
+          "bilock" = [ "futures-util/bilock" ];
+          "compat" = [ "std" "futures-util/compat" ];
+          "default" = [ "std" "async-await" "executor" ];
+          "executor" = [ "std" "futures-executor/std" ];
+          "futures-executor" = [ "dep:futures-executor" ];
+          "io-compat" = [ "compat" "futures-util/io-compat" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "futures-io/std" "futures-sink/std" "futures-util/std" "futures-util/io" "futures-util/channel" ];
+          "thread-pool" = [ "executor" "futures-executor/thread-pool" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" "futures-channel/unstable" "futures-io/unstable" "futures-util/unstable" ];
+          "write-all-vectored" = [ "futures-util/write-all-vectored" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "default" "executor" "futures-executor" "std" ];
+      };
+      "futures-channel" = rec {
+        crateName = "futures-channel";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1jxsifvrbqzdadk0svbax71cba5d3qg3wgjq8i160mxmd1kdckgz";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" ];
+          "default" = [ "std" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "sink" = [ "futures-sink" ];
+          "std" = [ "alloc" "futures-core/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "futures-sink" "sink" "std" ];
+      };
+      "futures-core" = rec {
+        crateName = "futures-core";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1308bpj0g36nhx2y6bl4mm6f1gnh9xyvvw2q2wpdgnb6dv3247gb";
+        features = {
+          "default" = [ "std" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-executor" = rec {
+        crateName = "futures-executor";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1g4pjni0sw28djx6mlcfz584abm2lpifz86cmng0kkxh7mlvhkqg";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "std" = [ "futures-core/std" "futures-task/std" "futures-util/std" ];
+          "thread-pool" = [ "std" "num_cpus" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "futures-io" = rec {
+        crateName = "futures-io";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1ajsljgny3zfxwahba9byjzclrgvm1ypakca8z854k2w7cb4mwwb";
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "futures-macro" = rec {
+        crateName = "futures-macro";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1nwd18i8kvpkdfwm045hddjli0n96zi7pn6f99zi9c74j7ym7cak";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "futures-sink" = rec {
+        crateName = "futures-sink";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "05q8jykqddxzp8nwf00wjk5m5mqi546d7i8hsxma7hiqxrw36vg3";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-task" = rec {
+        crateName = "futures-task";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1qmsss8rb5ppql4qvd4r70h9gpfcpd0bg2b3qilxrnhdkc397lgg";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "futures-util" = rec {
+        crateName = "futures-util";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "0141rkqh0psj4h8x8lgsl1p29dhqr7z2wcixkcbs60z74kb2d5d1";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-macro";
+            packageId = "futures-macro";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "pin-utils";
+            packageId = "pin-utils";
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" ];
+          "async-await-macro" = [ "async-await" "futures-macro" ];
+          "channel" = [ "std" "futures-channel" ];
+          "compat" = [ "std" "futures_01" ];
+          "default" = [ "std" "async-await" "async-await-macro" ];
+          "futures-channel" = [ "dep:futures-channel" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-macro" = [ "dep:futures-macro" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "futures_01" = [ "dep:futures_01" ];
+          "io" = [ "std" "futures-io" "memchr" ];
+          "io-compat" = [ "io" "compat" "tokio-io" ];
+          "memchr" = [ "dep:memchr" ];
+          "portable-atomic" = [ "futures-core/portable-atomic" ];
+          "sink" = [ "futures-sink" ];
+          "slab" = [ "dep:slab" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "slab" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" ];
+          "write-all-vectored" = [ "io" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "async-await-macro" "channel" "futures-channel" "futures-io" "futures-macro" "futures-sink" "io" "memchr" "sink" "slab" "std" ];
+      };
+      "fxhash" = rec {
+        crateName = "fxhash";
+        version = "0.2.1";
+        edition = "2015";
+        sha256 = "037mb9ichariqi45xm6mz0b11pa92gj38ba0409z3iz239sns6y3";
+        libPath = "lib.rs";
+        authors = [
+          "cbreeden <github@u.breeden.cc>"
+        ];
+        dependencies = [
+          {
+            name = "byteorder";
+            packageId = "byteorder";
+          }
+        ];
+
+      };
+      "generic-array" = rec {
+        crateName = "generic-array";
+        version = "0.14.7";
+        edition = "2015";
+        sha256 = "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45";
+        libName = "generic_array";
+        authors = [
+          "BartΕ‚omiej KamiΕ„ski <fizyk20@gmail.com>"
+          "Aaron Trent <novacrazy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "more_lengths" ];
+      };
+      "getrandom" = rec {
+        crateName = "getrandom";
+        version = "0.2.11";
+        edition = "2018";
+        sha256 = "03q7120cc2kn7ry013i67zmjl2g9q73h1ks5z08hq5v9syz0d47y";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            optional = true;
+            target = { target, features }: ((("wasm32" == target."arch" or null) || ("wasm64" == target."arch" or null)) && ("unknown" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((("wasm32" == target."arch" or null) || ("wasm64" == target."arch" or null)) && ("unknown" == target."os" or null));
+          }
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "js" = [ "wasm-bindgen" "js-sys" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "libc/rustc-dep-of-std" "wasi/rustc-dep-of-std" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "js" "js-sys" "std" "wasm-bindgen" ];
+      };
+      "gimli" = rec {
+        crateName = "gimli";
+        version = "0.28.0";
+        edition = "2018";
+        sha256 = "1h7hcl3chfvd2gfrrxjymnwj7anqxjslvz20kcargkvsya2dgf3g";
+        features = {
+          "default" = [ "read-all" "write" ];
+          "endian-reader" = [ "read" "dep:stable_deref_trait" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "read" = [ "read-core" ];
+          "read-all" = [ "read" "std" "fallible-iterator" "endian-reader" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" ];
+          "std" = [ "fallible-iterator?/std" "stable_deref_trait?/std" ];
+          "write" = [ "dep:indexmap" ];
+        };
+        resolvedDefaultFeatures = [ "read" "read-core" ];
+      };
+      "glob" = rec {
+        crateName = "glob";
+        version = "0.3.1";
+        edition = "2015";
+        sha256 = "16zca52nglanv23q5qrwd5jinw3d3as5ylya6y1pbx47vkxvrynj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+
+      };
+      "h2" = rec {
+        crateName = "h2";
+        version = "0.3.22";
+        edition = "2018";
+        sha256 = "0y41jlflvw8niifdirgng67zdmic62cjf5m2z69hzrpn5qr50qjd";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            features = [ "codec" "io" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt-multi-thread" "macros" "sync" "net" ];
+          }
+        ];
+        features = { };
+      };
+      "hashbrown" = rec {
+        crateName = "hashbrown";
+        version = "0.14.2";
+        edition = "2021";
+        sha256 = "0mj1x1d16acxf4zg7wr7q2x8pgzfi1bzpifygcsxmg4d2n972gpr";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "allocator-api2";
+            packageId = "allocator-api2";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "alloc" = [ "dep:alloc" ];
+          "allocator-api2" = [ "dep:allocator-api2" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "ahash" "inline-more" "allocator-api2" ];
+          "equivalent" = [ "dep:equivalent" ];
+          "nightly" = [ "allocator-api2?/nightly" "bumpalo/allocator_api" ];
+          "rayon" = [ "dep:rayon" ];
+          "rkyv" = [ "dep:rkyv" ];
+          "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "ahash" "allocator-api2" "default" "inline-more" "raw" "rayon" ];
+      };
+      "heck" = rec {
+        crateName = "heck";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1a7mqsnycv5z4z5vnv1k34548jzmc0ajic7c1j8jsaspnhw5ql4m";
+        authors = [
+          "Without Boats <woboats@gmail.com>"
+        ];
+        features = {
+          "unicode" = [ "unicode-segmentation" ];
+          "unicode-segmentation" = [ "dep:unicode-segmentation" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "hermit-abi" = rec {
+        crateName = "hermit-abi";
+        version = "0.3.3";
+        edition = "2021";
+        sha256 = "1dyc8qsjh876n74a3rcz8h43s27nj1sypdhsn2ms61bd3b47wzyp";
+        authors = [
+          "Stefan Lankes"
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins/rustc-dep-of-std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "hex" = rec {
+        crateName = "hex";
+        version = "0.4.3";
+        edition = "2018";
+        sha256 = "0w1a4davm1lgzpamwnba907aysmlrnygbqmfis2mqjx5m552a93z";
+        authors = [
+          "KokaKiwi <kokakiwi@kokakiwi.net>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "hmac" = rec {
+        crateName = "hmac";
+        version = "0.11.0";
+        edition = "2018";
+        sha256 = "16z61aibdg4di40sqi4ks2s4rz6r29w4sx4gvblfph3yxch26aia";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "crypto-mac";
+            packageId = "crypto-mac";
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.9.0";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "crypto-mac";
+            packageId = "crypto-mac";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "std" = [ "crypto-mac/std" ];
+        };
+      };
+      "home" = rec {
+        crateName = "home";
+        version = "0.5.5";
+        edition = "2018";
+        sha256 = "1nqx1krijvpd03d96avsdyknd12h8hs3xhxwgqghf8v9xxzc4i2l";
+        authors = [
+          "Brian Anderson <andersrb@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_UI_Shell" ];
+          }
+        ];
+
+      };
+      "http" = rec {
+        crateName = "http";
+        version = "0.2.11";
+        edition = "2018";
+        sha256 = "1fwz3mhh86h5kfnr5767jlx9agpdggclq7xsqx930fflzakb2iw9";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+        ];
+
+      };
+      "http-body" = rec {
+        crateName = "http-body";
+        version = "0.4.5";
+        edition = "2018";
+        sha256 = "1l967qwwlvhp198xdrnc0p5d7jwfcp6q2lm510j6zqw4s4b8zwym";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "httparse" = rec {
+        crateName = "httparse";
+        version = "1.8.0";
+        edition = "2018";
+        sha256 = "010rrfahm1jss3p022fqf3j3jmm72vhn4iqhykahb9ynpaag75yq";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "httpdate" = rec {
+        crateName = "httpdate";
+        version = "1.0.3";
+        edition = "2021";
+        sha256 = "1aa9rd2sac0zhjqh24c9xvir96g188zldkx0hr6dnnlx5904cfyz";
+        authors = [
+          "Pyfisch <pyfisch@posteo.org>"
+        ];
+
+      };
+      "hyper" = rec {
+        crateName = "hyper";
+        version = "0.14.27";
+        edition = "2018";
+        sha256 = "0s2l74p3harvjgb0bvaxlxgxq71vpfrzv0cqz2p9w8d8akbczcgz";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "h2";
+            packageId = "h2";
+            optional = true;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "httparse";
+            packageId = "httparse";
+          }
+          {
+            name = "httpdate";
+            packageId = "httpdate";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2 0.4.10";
+            optional = true;
+            features = [ "all" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "want";
+            packageId = "want";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "macros" "io-std" "io-util" "rt" "rt-multi-thread" "sync" "time" "test-util" ];
+          }
+        ];
+        features = {
+          "ffi" = [ "libc" ];
+          "full" = [ "client" "http1" "http2" "server" "stream" "runtime" ];
+          "h2" = [ "dep:h2" ];
+          "http2" = [ "h2" ];
+          "libc" = [ "dep:libc" ];
+          "runtime" = [ "tcp" "tokio/rt" "tokio/time" ];
+          "socket2" = [ "dep:socket2" ];
+          "tcp" = [ "socket2" "tokio/net" "tokio/rt" "tokio/time" ];
+        };
+        resolvedDefaultFeatures = [ "client" "default" "h2" "http1" "http2" "runtime" "socket2" "stream" "tcp" ];
+      };
+      "hyper-rustls" = rec {
+        crateName = "hyper-rustls";
+        version = "0.23.2";
+        edition = "2018";
+        sha256 = "0736s6a32dqr107f943xaz1n05flbinq6l19lq1wsrxkc5g9d20p";
+        dependencies = [
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            usesDefaultFeatures = false;
+            features = [ "client" ];
+          }
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "rustls";
+            packageId = "rustls";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rustls-native-certs";
+            packageId = "rustls-native-certs";
+            optional = true;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "tokio-rustls";
+            packageId = "tokio-rustls";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "full" ];
+          }
+          {
+            name = "rustls";
+            packageId = "rustls";
+            usesDefaultFeatures = false;
+            features = [ "tls12" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-std" "macros" "net" "rt-multi-thread" ];
+          }
+        ];
+        features = {
+          "default" = [ "native-tokio" "http1" "tls12" "logging" ];
+          "http1" = [ "hyper/http1" ];
+          "http2" = [ "hyper/http2" ];
+          "log" = [ "dep:log" ];
+          "logging" = [ "log" "tokio-rustls/logging" "rustls/logging" ];
+          "native-tokio" = [ "tokio-runtime" "rustls-native-certs" ];
+          "rustls-native-certs" = [ "dep:rustls-native-certs" ];
+          "tls12" = [ "tokio-rustls/tls12" "rustls/tls12" ];
+          "tokio-runtime" = [ "hyper/runtime" ];
+          "webpki-roots" = [ "dep:webpki-roots" ];
+          "webpki-tokio" = [ "tokio-runtime" "webpki-roots" ];
+        };
+        resolvedDefaultFeatures = [ "http1" "http2" "log" "logging" "native-tokio" "rustls-native-certs" "tls12" "tokio-runtime" ];
+      };
+      "iana-time-zone" = rec {
+        crateName = "iana-time-zone";
+        version = "0.1.58";
+        edition = "2018";
+        sha256 = "081vcr8z8ddhl5r1ywif6grnswk01b2ac4nks2bhn8zzdimvh9l3";
+        authors = [
+          "Andrew Straw <strawman@astraw.com>"
+          "RenΓ© Kijewski <rene.kijewski@fu-berlin.de>"
+          "Ryan Lopopolo <rjl@hyperbo.la>"
+        ];
+        dependencies = [
+          {
+            name = "android_system_properties";
+            packageId = "android_system_properties";
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "iana-time-zone-haiku";
+            packageId = "iana-time-zone-haiku";
+            target = { target, features }: ("haiku" == target."os" or null);
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "windows-core";
+            packageId = "windows-core";
+            target = { target, features }: ("windows" == target."os" or null);
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "fallback" ];
+      };
+      "iana-time-zone-haiku" = rec {
+        crateName = "iana-time-zone-haiku";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        authors = [
+          "RenΓ© Kijewski <crates.io@k6i.de>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "indexmap" = rec {
+        crateName = "indexmap";
+        version = "2.1.0";
+        edition = "2021";
+        sha256 = "07rxrqmryr1xfnmhrjlz8ic6jw28v6h5cig3ws2c9d0wifhy2c6m";
+        dependencies = [
+          {
+            name = "equivalent";
+            packageId = "equivalent";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            usesDefaultFeatures = false;
+            features = [ "raw" ];
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-rayon" = [ "dep:rustc-rayon" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "indicatif" = rec {
+        crateName = "indicatif";
+        version = "0.17.7";
+        edition = "2021";
+        sha256 = "098ggvg7ps4097p5n9hmb3pqqy10bi8vjfzb7pci79xrklf78a7v";
+        dependencies = [
+          {
+            name = "console";
+            packageId = "console";
+            usesDefaultFeatures = false;
+            features = [ "ansi-parsing" ];
+          }
+          {
+            name = "instant";
+            packageId = "instant";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "number_prefix";
+            packageId = "number_prefix";
+          }
+          {
+            name = "portable-atomic";
+            packageId = "portable-atomic";
+          }
+          {
+            name = "unicode-width";
+            packageId = "unicode-width";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "unicode-width" "console/unicode-width" ];
+          "futures" = [ "dep:futures-core" ];
+          "improved_unicode" = [ "unicode-segmentation" "unicode-width" "console/unicode-width" ];
+          "in_memory" = [ "vt100" ];
+          "rayon" = [ "dep:rayon" ];
+          "tokio" = [ "dep:tokio" ];
+          "unicode-segmentation" = [ "dep:unicode-segmentation" ];
+          "unicode-width" = [ "dep:unicode-width" ];
+          "vt100" = [ "dep:vt100" ];
+        };
+        resolvedDefaultFeatures = [ "default" "unicode-width" ];
+      };
+      "instant" = rec {
+        crateName = "instant";
+        version = "0.1.12";
+        edition = "2018";
+        sha256 = "0b2bx5qdlwayriidhrag8vhy10kdfimfhmb3jnjmsz2h9j1bwnvs";
+        authors = [
+          "sebcrozet <developer@crozet.re>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "js-sys" = [ "dep:js-sys" ];
+          "stdweb" = [ "dep:stdweb" ];
+          "wasm-bindgen" = [ "js-sys" "wasm-bindgen_rs" "web-sys" ];
+          "wasm-bindgen_rs" = [ "dep:wasm-bindgen_rs" ];
+          "web-sys" = [ "dep:web-sys" ];
+        };
+      };
+      "itertools" = rec {
+        crateName = "itertools";
+        version = "0.11.0";
+        edition = "2018";
+        sha256 = "0mzyqcc59azx9g5cg6fs8k529gvh4463smmka6jvzs3cd2jp7hdi";
+        authors = [
+          "bluss"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "use_std" ];
+          "use_std" = [ "use_alloc" "either/use_std" ];
+        };
+        resolvedDefaultFeatures = [ "use_alloc" ];
+      };
+      "itoa" = rec {
+        crateName = "itoa";
+        version = "1.0.9";
+        edition = "2018";
+        sha256 = "0f6cpb4yqzhkrhhg6kqsw3wnmmhdnnffi6r2xzy248gzi2v0l5dg";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "jobserver" = rec {
+        crateName = "jobserver";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "0z9w6vfqwbr6hfk9yaw7kydlh6f7k39xdlszxlh39in4acwzcdwc";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+
+      };
+      "js-sys" = rec {
+        crateName = "js-sys";
+        version = "0.3.65";
+        edition = "2018";
+        sha256 = "1s1gaxgzpqfyygc7f2pwp9y128rh5f8zvsc4nm5yazgna9cw7h2l";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+        ];
+
+      };
+      "lazy_static" = rec {
+        crateName = "lazy_static";
+        version = "1.4.0";
+        edition = "2015";
+        sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2";
+        authors = [
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        features = {
+          "spin" = [ "dep:spin" ];
+          "spin_no_std" = [ "spin" ];
+        };
+      };
+      "libc" = rec {
+        crateName = "libc";
+        version = "0.2.150";
+        edition = "2015";
+        sha256 = "0g10n8c830alndgjb8xk1i9kz5z727np90z1z81119pr8d3jmnc9";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ];
+          "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "extra_traits" "std" ];
+      };
+      "libm" = rec {
+        crateName = "libm";
+        version = "0.2.8";
+        edition = "2018";
+        sha256 = "0n4hk1rs8pzw8hdfmwn96c4568s93kfxqgcqswr7sajd2diaihjf";
+        authors = [
+          "Jorge Aparicio <jorge@japaric.io>"
+        ];
+        features = {
+          "musl-reference-tests" = [ "rand" ];
+          "rand" = [ "dep:rand" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "libredox" = rec {
+        crateName = "libredox";
+        version = "0.0.1";
+        edition = "2021";
+        sha256 = "1s2fh4ikpp9xl0lsl01pi0n8pw1q9s3ld452vd8qh1v63v537j45";
+        authors = [
+          "4lDO2 <4lDO2@protonmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.4.1";
+          }
+        ];
+        features = {
+          "default" = [ "scheme" "call" ];
+          "scheme" = [ "call" ];
+        };
+        resolvedDefaultFeatures = [ "call" ];
+      };
+      "linux-raw-sys" = rec {
+        crateName = "linux-raw-sys";
+        version = "0.4.11";
+        edition = "2021";
+        sha256 = "0adqqaya81s7k5r323g65pw6q85pxd1x4prz9whh5i4abysqi54n";
+        authors = [
+          "Dan Gohman <dev@sunfishcode.online>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" "general" "errno" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "no_std" ];
+        };
+        resolvedDefaultFeatures = [ "elf" "errno" "general" "ioctl" "no_std" ];
+      };
+      "lock_api" = rec {
+        crateName = "lock_api";
+        version = "0.4.11";
+        edition = "2018";
+        sha256 = "0iggx0h4jx63xm35861106af3jkxq06fpqhpkhgw0axi2n38y5iw";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "atomic_usize" ];
+          "owning_ref" = [ "dep:owning_ref" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "atomic_usize" "default" ];
+      };
+      "log" = rec {
+        crateName = "log";
+        version = "0.4.20";
+        edition = "2015";
+        sha256 = "13rf7wphnwd61vazpxr7fiycin6cb1g8fmvgqg18i464p0y1drmm";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "kv_unstable" = [ "value-bag" ];
+          "kv_unstable_serde" = [ "kv_unstable_std" "value-bag/serde" "serde" ];
+          "kv_unstable_std" = [ "std" "kv_unstable" "value-bag/error" ];
+          "kv_unstable_sval" = [ "kv_unstable" "value-bag/sval" "sval" "sval_ref" ];
+          "serde" = [ "dep:serde" ];
+          "sval" = [ "dep:sval" ];
+          "sval_ref" = [ "dep:sval_ref" ];
+          "value-bag" = [ "dep:value-bag" ];
+        };
+      };
+      "lz4" = rec {
+        crateName = "lz4";
+        version = "1.24.0";
+        edition = "2018";
+        crateBin = [ ];
+        sha256 = "1wad97k0asgvaj16ydd09gqs2yvgaanzcvqglrhffv7kdpc2v7ky";
+        authors = [
+          "Jens Heyens <jens.heyens@ewetel.net>"
+          "Artem V. Navrotskiy <bozaro@buzzsoft.ru>"
+          "Patrick Marks <pmarks@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "lz4-sys";
+            packageId = "lz4-sys";
+          }
+        ];
+
+      };
+      "lz4-sys" = rec {
+        crateName = "lz4-sys";
+        version = "1.9.4";
+        edition = "2015";
+        links = "lz4";
+        sha256 = "0059ik4xlvnss5qfh6l691psk4g3350ljxaykzv10yr0gqqppljp";
+        authors = [
+          "Jens Heyens <jens.heyens@ewetel.net>"
+          "Artem V. Navrotskiy <bozaro@buzzsoft.ru>"
+          "Patrick Marks <pmarks@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "lzma-sys" = rec {
+        crateName = "lzma-sys";
+        version = "0.1.20";
+        edition = "2018";
+        links = "lzma";
+        sha256 = "09sxp20waxyglgn3cjz8qjkspb3ryz2fwx4rigkwvrk46ymh9njz";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+        ];
+        features = { };
+      };
+      "md-5" = rec {
+        crateName = "md-5";
+        version = "0.9.1";
+        edition = "2018";
+        sha256 = "059ajjacz1q3cms7vl6cvhdqs4qdw2nnwj9dq99ryzv0p6djfnkv";
+        libName = "md5";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer 0.9.0";
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.9.0";
+          }
+          {
+            name = "opaque-debug";
+            packageId = "opaque-debug";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest 0.9.0";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "md5-asm" ];
+          "default" = [ "std" ];
+          "md5-asm" = [ "dep:md5-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "memchr" = rec {
+        crateName = "memchr";
+        version = "2.6.4";
+        edition = "2021";
+        sha256 = "0rq1ka8790ns41j147npvxcqcl2anxyngsdimy85ag2api0fwrgn";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+          "bluss"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "logging" = [ "dep:log" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "std" = [ "alloc" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "memmap2" = rec {
+        crateName = "memmap2";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1il82b0mw304jlwvl0m89aa8bj5dgmm3vbb0jg8lqlrk0p98i4zl";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Yevhenii Reizner <razrfalcon@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        features = {
+          "stable_deref_trait" = [ "dep:stable_deref_trait" ];
+        };
+      };
+      "memoffset" = rec {
+        crateName = "memoffset";
+        version = "0.9.0";
+        edition = "2015";
+        sha256 = "0v20ihhdzkfw1jx00a7zjpk2dcp5qjq6lz302nyqamd9c4f4nqss";
+        authors = [
+          "Gilad Naaman <gilad.naaman@gmail.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "minimal-lexical" = rec {
+        crateName = "minimal-lexical";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "miniz_oxide" = rec {
+        crateName = "miniz_oxide";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1ivl3rbbdm53bzscrd01g60l46lz5krl270487d8lhjvwl5hx0g7";
+        authors = [
+          "Frommi <daniil.liferenko@gmail.com>"
+          "oyvindln <oyvindln@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "adler";
+            packageId = "adler";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "with-alloc" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "adler/rustc-dep-of-std" ];
+          "simd" = [ "simd-adler32" ];
+          "simd-adler32" = [ "dep:simd-adler32" ];
+        };
+        resolvedDefaultFeatures = [ "with-alloc" ];
+      };
+      "mio" = rec {
+        crateName = "mio";
+        version = "0.8.9";
+        edition = "2018";
+        sha256 = "1l23hg513c23nhcdzvk25caaj28mic6qgqadbn8axgj6bqf2ikix";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_Storage_FileSystem" "Win32_System_IO" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = {
+          "default" = [ "log" ];
+          "log" = [ "dep:log" ];
+          "os-ext" = [ "os-poll" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_Security" ];
+        };
+        resolvedDefaultFeatures = [ "net" "os-ext" "os-poll" ];
+      };
+      "multimap" = rec {
+        crateName = "multimap";
+        version = "0.8.3";
+        edition = "2015";
+        sha256 = "0sicyz4n500vdhgcxn4g8jz97cp1ijir1rnbgph3pmx9ckz4dkp5";
+        authors = [
+          "HΓ₯var NΓΈvik <havar.novik@gmail.com>"
+        ];
+        features = {
+          "default" = [ "serde_impl" ];
+          "serde" = [ "dep:serde" ];
+          "serde_impl" = [ "serde" ];
+        };
+      };
+      "multiversion" = rec {
+        crateName = "multiversion";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "0al7yrf489lqzxx291sx9566n7slk2njwlqrxbjhqxk1zvbvkixj";
+        authors = [
+          "Caleb Zulawski <caleb.zulawski@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "multiversion-macros";
+            packageId = "multiversion-macros";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "target-features";
+            packageId = "target-features";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "multiversion-macros/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "multiversion-macros" = rec {
+        crateName = "multiversion-macros";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "1j1avbxw7jscyi7dmnywhlwbiny1fvg1vpp9fy4dc1pd022kva16";
+        procMacro = true;
+        authors = [
+          "Caleb Zulawski <caleb.zulawski@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "full" "extra-traits" "visit-mut" ];
+          }
+          {
+            name = "target-features";
+            packageId = "target-features";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "nix-compat" = rec {
+        crateName = "nix-compat";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [ ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ../../nix-compat; }
+          else ../../nix-compat;
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+            features = [ "alloc" "unicode" "serde" ];
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+          }
+          {
+            name = "ed25519-dalek";
+            packageId = "ed25519-dalek";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "nom";
+            packageId = "nom";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2 0.10.8";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+        ];
+        features = {
+          "async" = [ "futures-util" ];
+          "futures-util" = [ "dep:futures-util" ];
+        };
+      };
+      "nom" = rec {
+        crateName = "nom";
+        version = "7.1.3";
+        edition = "2018";
+        sha256 = "0jha9901wxam390jcf5pfa0qqfrgh8li787jx2ip0yk5b8y9hwyj";
+        authors = [
+          "contact@geoffroycouprie.com"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "minimal-lexical";
+            packageId = "minimal-lexical";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" "memchr/std" "minimal-lexical/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "now" = rec {
+        crateName = "now";
+        version = "0.1.3";
+        edition = "2018";
+        sha256 = "1l135786rb43rjfhwfdj7hi3b5zxxyl9gwf15yjz18cp8f3yk2bd";
+        authors = [
+          "Kilerd <blove694@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "clock" "std" ];
+          }
+        ];
+
+      };
+      "ntapi" = rec {
+        crateName = "ntapi";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1r38zhbwdvkis2mzs6671cm1p6djgsl49i7bwxzrvhwicdf8k8z8";
+        authors = [
+          "MSxDOS <melcodos@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi";
+            packageId = "winapi";
+            features = [ "cfg" "evntrace" "in6addr" "inaddr" "minwinbase" "ntsecapi" "windef" "winioctl" ];
+          }
+        ];
+        features = {
+          "default" = [ "user" ];
+          "impl-default" = [ "winapi/impl-default" ];
+        };
+        resolvedDefaultFeatures = [ "default" "user" ];
+      };
+      "num-traits" = rec {
+        crateName = "num-traits";
+        version = "0.2.17";
+        edition = "2018";
+        sha256 = "0z16bi5zwgfysz6765v3rd6whfbjpihx3mhsn4dg8dzj2c221qrr";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "libm";
+            packageId = "libm";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "libm" = [ "dep:libm" ];
+        };
+        resolvedDefaultFeatures = [ "default" "libm" "std" ];
+      };
+      "num_cpus" = rec {
+        crateName = "num_cpus";
+        version = "1.16.0";
+        edition = "2015";
+        sha256 = "0hra6ihpnh06dvfvz9ipscys0xfqa9ca9hzp384d5m02ssvgqqa1";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(target."windows" or false));
+          }
+        ];
+
+      };
+      "number_prefix" = rec {
+        crateName = "number_prefix";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "1wvh13wvlajqxkb1filsfzbrnq0vrmrw298v2j3sy82z1rm282w3";
+        authors = [
+          "Benjamin Sago <ogham@bsago.me>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "object" = rec {
+        crateName = "object";
+        version = "0.32.1";
+        edition = "2018";
+        sha256 = "1c02x4kvqpnl3wn7gz9idm4jrbirbycyqjgiw6lm1g9k77fzkxcw";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "all" = [ "read" "write" "std" "compression" "wasm" ];
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "compression" = [ "dep:flate2" "dep:ruzstd" "std" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "read" "compression" ];
+          "doc" = [ "read_core" "write_std" "std" "compression" "archive" "coff" "elf" "macho" "pe" "wasm" "xcoff" ];
+          "pe" = [ "coff" ];
+          "read" = [ "read_core" "archive" "coff" "elf" "macho" "pe" "xcoff" "unaligned" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "alloc" "memchr/rustc-dep-of-std" ];
+          "std" = [ "memchr/std" ];
+          "unstable-all" = [ "all" "unstable" ];
+          "wasm" = [ "dep:wasmparser" ];
+          "write" = [ "write_std" "coff" "elf" "macho" "pe" "xcoff" ];
+          "write_core" = [ "dep:crc32fast" "dep:indexmap" "dep:hashbrown" ];
+          "write_std" = [ "write_core" "std" "indexmap?/std" "crc32fast?/std" ];
+        };
+        resolvedDefaultFeatures = [ "archive" "coff" "elf" "macho" "pe" "read_core" "unaligned" ];
+      };
+      "once_cell" = rec {
+        crateName = "once_cell";
+        version = "1.18.0";
+        edition = "2021";
+        sha256 = "0vapcd5ambwck95wyz3ymlim35jirgnqn9a0qmi19msymv95v2yx";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        features = {
+          "alloc" = [ "race" ];
+          "atomic-polyfill" = [ "critical-section" ];
+          "critical-section" = [ "dep:critical-section" "dep:atomic-polyfill" ];
+          "default" = [ "std" ];
+          "parking_lot" = [ "dep:parking_lot_core" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "race" "std" "unstable" ];
+      };
+      "opaque-debug" = rec {
+        crateName = "opaque-debug";
+        version = "0.3.0";
+        edition = "2018";
+        sha256 = "1m8kzi4nd6shdqimn0mgb24f0hxslhnqd1whakyq06wcqd086jk2";
+        authors = [
+          "RustCrypto Developers"
+        ];
+
+      };
+      "openssl-probe" = rec {
+        crateName = "openssl-probe";
+        version = "0.1.5";
+        edition = "2015";
+        sha256 = "1kq18qm48rvkwgcggfkqq6pm948190czqc94d6bm2sir5hq1l0gz";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+
+      };
+      "parking_lot 0.11.2" = rec {
+        crateName = "parking_lot";
+        version = "0.11.2";
+        edition = "2018";
+        sha256 = "16gzf41bxmm10x82bla8d6wfppy9ym3fxsmdjyvn61m66s0bf5vx";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "instant";
+            packageId = "instant";
+          }
+          {
+            name = "lock_api";
+            packageId = "lock_api";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core 0.8.6";
+          }
+        ];
+        features = {
+          "arc_lock" = [ "lock_api/arc_lock" ];
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+          "stdweb" = [ "instant/stdweb" ];
+          "wasm-bindgen" = [ "instant/wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot 0.12.1" = rec {
+        crateName = "parking_lot";
+        version = "0.12.1";
+        edition = "2018";
+        sha256 = "13r2xk7mnxfc5g0g6dkdxqdqad99j7s7z8zhzz4npw5r0g0v4hip";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lock_api";
+            packageId = "lock_api";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core 0.9.9";
+          }
+        ];
+        features = {
+          "arc_lock" = [ "lock_api/arc_lock" ];
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot_core 0.8.6" = rec {
+        crateName = "parking_lot_core";
+        version = "0.8.6";
+        edition = "2018";
+        sha256 = "1p2nfcbr0b9lm9rglgm28k6mwyjwgm4knipsmqbgqaxdy3kcz8k0";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "instant";
+            packageId = "instant";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.2.16";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "winnt" "ntstatus" "minwindef" "winerror" "winbase" "errhandlingapi" "handleapi" ];
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "parking_lot_core 0.9.9" = rec {
+        crateName = "parking_lot_core";
+        version = "0.9.9";
+        edition = "2018";
+        sha256 = "13h0imw1aq86wj28gxkblhkzx6z1gk8q18n0v76qmmj6cliajhjc";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.4.1";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "parquet-format-safe" = rec {
+        crateName = "parquet-format-safe";
+        version = "0.2.4";
+        edition = "2021";
+        sha256 = "07wf6wf4jrxlq5p3xldxsnabp7jl06my2qp7kiwy9m3x2r5wac8i";
+        authors = [
+          "Apache Thrift contributors <dev@thrift.apache.org>"
+          "Jorge Leitao <jorgecarleitao@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+            optional = true;
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+        ];
+        features = {
+          "async" = [ "futures" "async-trait" ];
+          "async-trait" = [ "dep:async-trait" ];
+          "full" = [ "async" ];
+          "futures" = [ "dep:futures" ];
+        };
+        resolvedDefaultFeatures = [ "async" "async-trait" "default" "futures" ];
+      };
+      "percent-encoding" = rec {
+        crateName = "percent-encoding";
+        version = "2.3.0";
+        edition = "2018";
+        sha256 = "152slflmparkh27hprw62sph8rv77wckzhwl2dhqk6bf563lfalv";
+        authors = [
+          "The rust-url developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "petgraph" = rec {
+        crateName = "petgraph";
+        version = "0.6.4";
+        edition = "2018";
+        sha256 = "1ac6wfq5f5pzcv0nvzzfgjbwg2kwslpnzsw5wcmxlscfcb9azlz1";
+        authors = [
+          "bluss"
+          "mitchmindtree"
+        ];
+        dependencies = [
+          {
+            name = "fixedbitset";
+            packageId = "fixedbitset";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+          }
+        ];
+        features = {
+          "all" = [ "unstable" "quickcheck" "matrix_graph" "stable_graph" "graphmap" ];
+          "default" = [ "graphmap" "stable_graph" "matrix_graph" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "serde" = [ "dep:serde" ];
+          "serde-1" = [ "serde" "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+          "unstable" = [ "generate" ];
+        };
+      };
+      "pin-project-lite" = rec {
+        crateName = "pin-project-lite";
+        version = "0.2.13";
+        edition = "2018";
+        sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+
+      };
+      "pin-utils" = rec {
+        crateName = "pin-utils";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        authors = [
+          "Josef Brandl <mail@josefbrandl.de>"
+        ];
+
+      };
+      "pkcs8" = rec {
+        crateName = "pkcs8";
+        version = "0.10.2";
+        edition = "2021";
+        sha256 = "1dx7w21gvn07azszgqd3ryjhyphsrjrmq5mmz1fbxkj5g0vv4l7r";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+          {
+            name = "spki";
+            packageId = "spki";
+          }
+        ];
+        features = {
+          "3des" = [ "encryption" "pkcs5/3des" ];
+          "alloc" = [ "der/alloc" "der/zeroize" "spki/alloc" ];
+          "des-insecure" = [ "encryption" "pkcs5/des-insecure" ];
+          "encryption" = [ "alloc" "pkcs5/alloc" "pkcs5/pbes2" "rand_core" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "pem" = [ "alloc" "der/pem" "spki/pem" ];
+          "pkcs5" = [ "dep:pkcs5" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "sha1-insecure" = [ "encryption" "pkcs5/sha1-insecure" ];
+          "std" = [ "alloc" "der/std" "spki/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "pkg-config" = rec {
+        crateName = "pkg-config";
+        version = "0.3.27";
+        edition = "2015";
+        sha256 = "0r39ryh1magcq4cz5g9x88jllsnxnhcqr753islvyk4jp9h2h1r6";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+
+      };
+      "planus" = rec {
+        crateName = "planus";
+        version = "0.3.1";
+        edition = "2021";
+        sha256 = "17x8mr175b9clg998xpi5z45f9fsspb0ncfnx2644bz817fr25pw";
+        dependencies = [
+          {
+            name = "array-init-cursor";
+            packageId = "array-init-cursor";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "platforms" = rec {
+        crateName = "platforms";
+        version = "3.2.0";
+        edition = "2018";
+        sha256 = "1c6bzwn877aqdbbmyqsl753ycbciwvbdh4lpzijb8vrfb4zsprhl";
+        authors = [
+          "Tony Arcieri <bascule@gmail.com>"
+          "Sergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "polars" = rec {
+        crateName = "polars";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "1mch7s185nfj74scc06bgfkwgp0n2axhp9wh17d25dvf4gwm53nz";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            target = { target, features }: (builtins.elem "wasm" target."family");
+            features = [ "js" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-lazy";
+            packageId = "polars-lazy";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-sql";
+            packageId = "polars-sql";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "abs" = [ "polars-ops/abs" "polars-lazy?/abs" ];
+          "algo" = [ "polars-algo" ];
+          "approx_unique" = [ "polars-lazy?/approx_unique" "polars-ops/approx_unique" ];
+          "arg_where" = [ "polars-lazy?/arg_where" ];
+          "asof_join" = [ "polars-core/asof_join" "polars-lazy?/asof_join" "polars-ops/asof_join" ];
+          "async" = [ "polars-lazy?/async" ];
+          "avro" = [ "polars-io" "polars-io/avro" ];
+          "avx512" = [ "polars-core/avx512" ];
+          "aws" = [ "async" "cloud" "polars-io/aws" ];
+          "azure" = [ "async" "cloud" "polars-io/azure" ];
+          "bench" = [ "lazy" ];
+          "bigidx" = [ "polars-core/bigidx" "polars-lazy?/bigidx" "polars-ops/big_idx" ];
+          "binary_encoding" = [ "polars-ops/binary_encoding" "polars-lazy?/binary_encoding" ];
+          "checked_arithmetic" = [ "polars-core/checked_arithmetic" ];
+          "chunked_ids" = [ "polars-lazy?/chunked_ids" "polars-core/chunked_ids" "polars-ops/chunked_ids" ];
+          "cloud" = [ "polars-lazy?/cloud" "polars-io/cloud" ];
+          "cloud_write" = [ "cloud" "polars-lazy?/cloud_write" ];
+          "coalesce" = [ "polars-lazy?/coalesce" ];
+          "concat_str" = [ "polars-lazy?/concat_str" ];
+          "cov" = [ "polars-lazy/cov" ];
+          "cross_join" = [ "polars-lazy?/cross_join" "polars-ops/cross_join" ];
+          "cse" = [ "polars-lazy?/cse" ];
+          "csv" = [ "polars-io" "polars-io/csv" "polars-lazy?/csv" "polars-sql?/csv" ];
+          "cum_agg" = [ "polars-ops/cum_agg" "polars-lazy?/cum_agg" ];
+          "cumulative_eval" = [ "polars-lazy?/cumulative_eval" ];
+          "cutqcut" = [ "polars-lazy?/cutqcut" ];
+          "dataframe_arithmetic" = [ "polars-core/dataframe_arithmetic" ];
+          "date_offset" = [ "polars-lazy?/date_offset" ];
+          "decompress" = [ "polars-io/decompress" ];
+          "decompress-fast" = [ "polars-io/decompress-fast" ];
+          "default" = [ "docs" "zip_with" "csv" "temporal" "fmt" "dtype-slim" ];
+          "describe" = [ "polars-core/describe" ];
+          "diagonal_concat" = [ "polars-core/diagonal_concat" "polars-lazy?/diagonal_concat" "polars-sql?/diagonal_concat" ];
+          "diff" = [ "polars-ops/diff" "polars-lazy?/diff" ];
+          "docs" = [ "polars-core/docs" ];
+          "docs-selection" = [ "csv" "json" "parquet" "ipc" "ipc_streaming" "dtype-full" "is_in" "rows" "docs" "strings" "object" "lazy" "temporal" "random" "zip_with" "round_series" "checked_arithmetic" "ndarray" "repeat_by" "is_first_distinct" "is_last_distinct" "asof_join" "cross_join" "concat_str" "string_to_integer" "decompress" "mode" "take_opt_iter" "cum_agg" "rolling_window" "interpolate" "diff" "rank" "range" "diagonal_concat" "horizontal_concat" "abs" "dot_diagram" "string_encoding" "product" "to_dummies" "describe" "list_eval" "cumulative_eval" "timezones" "arg_where" "propagate_nans" "coalesce" "dynamic_group_by" "extract_groups" ];
+          "dot_diagram" = [ "polars-lazy?/dot_diagram" ];
+          "dot_product" = [ "polars-core/dot_product" ];
+          "dtype-array" = [ "polars-core/dtype-array" "polars-lazy?/dtype-array" "polars-ops/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" "polars-io/dtype-categorical" "polars-lazy?/dtype-categorical" "polars-ops/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-lazy?/dtype-date" "polars-io/dtype-date" "polars-time?/dtype-date" "polars-core/dtype-date" "polars-ops/dtype-date" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-lazy?/dtype-datetime" "polars-io/dtype-datetime" "polars-time?/dtype-datetime" "polars-ops/dtype-datetime" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" "polars-lazy?/dtype-decimal" "polars-ops/dtype-decimal" "polars-io/dtype-decimal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-lazy?/dtype-duration" "polars-time?/dtype-duration" "polars-core/dtype-duration" "polars-ops/dtype-duration" ];
+          "dtype-full" = [ "dtype-date" "dtype-datetime" "dtype-duration" "dtype-time" "dtype-array" "dtype-i8" "dtype-i16" "dtype-decimal" "dtype-u8" "dtype-u16" "dtype-categorical" "dtype-struct" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" "polars-lazy?/dtype-i16" "polars-ops/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" "polars-lazy?/dtype-i8" "polars-ops/dtype-i8" ];
+          "dtype-slim" = [ "dtype-date" "dtype-datetime" "dtype-duration" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" "polars-lazy?/dtype-struct" "polars-ops/dtype-struct" "polars-io/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-io/dtype-time" "polars-time?/dtype-time" "polars-ops/dtype-time" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" "polars-lazy?/dtype-u16" "polars-ops/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" "polars-lazy?/dtype-u8" "polars-ops/dtype-u8" ];
+          "dynamic_group_by" = [ "polars-core/dynamic_group_by" "polars-lazy?/dynamic_group_by" ];
+          "ewma" = [ "polars-ops/ewma" "polars-lazy?/ewma" ];
+          "extract_groups" = [ "polars-lazy?/extract_groups" ];
+          "extract_jsonpath" = [ "polars-core/strings" "polars-ops/extract_jsonpath" "polars-ops/strings" "polars-lazy?/extract_jsonpath" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "fmt_no_tty" = [ "polars-core/fmt_no_tty" ];
+          "fused" = [ "polars-ops/fused" "polars-lazy?/fused" ];
+          "gcp" = [ "async" "cloud" "polars-io/gcp" ];
+          "group_by_list" = [ "polars-core/group_by_list" "polars-ops/group_by_list" ];
+          "horizontal_concat" = [ "polars-core/horizontal_concat" ];
+          "http" = [ "async" "cloud" "polars-io/http" ];
+          "interpolate" = [ "polars-ops/interpolate" "polars-lazy?/interpolate" ];
+          "ipc" = [ "polars-io" "polars-io/ipc" "polars-lazy?/ipc" "polars-sql?/ipc" ];
+          "ipc_streaming" = [ "polars-io" "polars-io/ipc_streaming" "polars-lazy?/ipc" ];
+          "is_first_distinct" = [ "polars-lazy?/is_first_distinct" "polars-ops/is_first_distinct" ];
+          "is_in" = [ "polars-lazy?/is_in" ];
+          "is_last_distinct" = [ "polars-lazy?/is_last_distinct" "polars-ops/is_last_distinct" ];
+          "is_unique" = [ "polars-lazy?/is_unique" "polars-ops/is_unique" ];
+          "json" = [ "polars-io" "polars-io/json" "polars-lazy?/json" "polars-sql?/json" "dtype-struct" ];
+          "lazy" = [ "polars-core/lazy" "polars-lazy" ];
+          "lazy_regex" = [ "polars-lazy?/regex" ];
+          "list_any_all" = [ "polars-lazy?/list_any_all" ];
+          "list_count" = [ "polars-ops/list_count" "polars-lazy?/list_count" ];
+          "list_drop_nulls" = [ "polars-lazy?/list_drop_nulls" ];
+          "list_eval" = [ "polars-lazy?/list_eval" ];
+          "list_gather" = [ "polars-ops/list_gather" "polars-lazy?/list_gather" ];
+          "list_sample" = [ "polars-lazy?/list_sample" ];
+          "list_sets" = [ "polars-lazy?/list_sets" ];
+          "list_to_struct" = [ "polars-ops/list_to_struct" "polars-lazy?/list_to_struct" ];
+          "log" = [ "polars-ops/log" "polars-lazy?/log" ];
+          "merge_sorted" = [ "polars-lazy?/merge_sorted" ];
+          "meta" = [ "polars-lazy?/meta" ];
+          "mode" = [ "polars-ops/mode" "polars-lazy?/mode" ];
+          "moment" = [ "polars-ops/moment" "polars-lazy?/moment" ];
+          "ndarray" = [ "polars-core/ndarray" ];
+          "nightly" = [ "polars-core/nightly" "polars-ops/nightly" "simd" "polars-lazy?/nightly" "polars-sql/nightly" ];
+          "object" = [ "polars-core/object" "polars-lazy?/object" "polars-io/object" ];
+          "parquet" = [ "polars-io" "polars-lazy?/parquet" "polars-io/parquet" "polars-sql?/parquet" ];
+          "partition_by" = [ "polars-core/partition_by" ];
+          "pct_change" = [ "polars-ops/pct_change" "polars-lazy?/pct_change" ];
+          "peaks" = [ "polars-lazy/peaks" ];
+          "performant" = [ "polars-core/performant" "chunked_ids" "dtype-u8" "dtype-u16" "dtype-struct" "cse" "polars-ops/performant" "streaming" "fused" ];
+          "pivot" = [ "polars-lazy?/pivot" ];
+          "polars-algo" = [ "dep:polars-algo" ];
+          "polars-io" = [ "dep:polars-io" ];
+          "polars-lazy" = [ "dep:polars-lazy" ];
+          "polars-sql" = [ "dep:polars-sql" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "product" = [ "polars-core/product" ];
+          "propagate_nans" = [ "polars-lazy?/propagate_nans" ];
+          "random" = [ "polars-core/random" "polars-lazy?/random" "polars-ops/random" ];
+          "range" = [ "polars-lazy?/range" ];
+          "rank" = [ "polars-lazy?/rank" "polars-ops/rank" ];
+          "reinterpret" = [ "polars-core/reinterpret" ];
+          "repeat_by" = [ "polars-ops/repeat_by" "polars-lazy?/repeat_by" ];
+          "rle" = [ "polars-lazy?/rle" ];
+          "rolling_window" = [ "polars-core/rolling_window" "polars-lazy?/rolling_window" "polars-time/rolling_window" ];
+          "round_series" = [ "polars-ops/round_series" "polars-lazy?/round_series" ];
+          "row_hash" = [ "polars-core/row_hash" "polars-lazy?/row_hash" ];
+          "rows" = [ "polars-core/rows" ];
+          "search_sorted" = [ "polars-lazy?/search_sorted" ];
+          "semi_anti_join" = [ "polars-lazy?/semi_anti_join" "polars-ops/semi_anti_join" "polars-sql?/semi_anti_join" ];
+          "serde" = [ "polars-core/serde" ];
+          "serde-lazy" = [ "polars-core/serde-lazy" "polars-lazy?/serde" "polars-time?/serde" "polars-io/serde" "polars-ops/serde" ];
+          "sign" = [ "polars-lazy?/sign" ];
+          "simd" = [ "polars-core/simd" "polars-io/simd" "polars-ops/simd" ];
+          "sort_multiple" = [ "polars-core/sort_multiple" ];
+          "sql" = [ "polars-sql" ];
+          "streaming" = [ "polars-lazy?/streaming" ];
+          "string_encoding" = [ "polars-ops/string_encoding" "polars-lazy?/string_encoding" "polars-core/strings" ];
+          "string_pad" = [ "polars-lazy?/string_pad" "polars-ops/string_pad" ];
+          "string_to_integer" = [ "polars-lazy?/string_to_integer" "polars-ops/string_to_integer" ];
+          "strings" = [ "polars-core/strings" "polars-lazy?/strings" "polars-ops/strings" ];
+          "take_opt_iter" = [ "polars-core/take_opt_iter" ];
+          "temporal" = [ "polars-core/temporal" "polars-lazy?/temporal" "polars-io/temporal" "polars-time" ];
+          "test" = [ "lazy" "rolling_window" "rank" "round_series" "csv" "dtype-categorical" "cum_agg" "fmt" "diff" "abs" "parquet" "ipc" "ipc_streaming" "json" ];
+          "timezones" = [ "polars-core/timezones" "polars-lazy?/timezones" "polars-io/timezones" ];
+          "to_dummies" = [ "polars-ops/to_dummies" ];
+          "top_k" = [ "polars-lazy?/top_k" ];
+          "trigonometry" = [ "polars-lazy?/trigonometry" ];
+          "true_div" = [ "polars-lazy?/true_div" ];
+          "unique_counts" = [ "polars-ops/unique_counts" "polars-lazy?/unique_counts" ];
+          "zip_with" = [ "polars-core/zip_with" "polars-ops/zip_with" ];
+        };
+        resolvedDefaultFeatures = [ "dtype-struct" "lazy" "parquet" "polars-io" "polars-lazy" "polars-sql" "sql" ];
+      };
+      "polars-arrow" = rec {
+        crateName = "polars-arrow";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "1rxa9dfsqy7mh4w9gy7y7kpig0wrzrjqi1axj43rnxyrlqq38l6x";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+          "Apache Arrow <dev@arrow.apache.org>"
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "arrow-format";
+            packageId = "arrow-format";
+            optional = true;
+            features = [ "ipc" ];
+          }
+          {
+            name = "atoi_simd";
+            packageId = "atoi_simd";
+            optional = true;
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "dyn-clone";
+            packageId = "dyn-clone";
+          }
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "ethnum";
+            packageId = "ethnum";
+          }
+          {
+            name = "fast-float";
+            packageId = "fast-float";
+            optional = true;
+          }
+          {
+            name = "foreign_vec";
+            packageId = "foreign_vec";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "wasm32-unknown-unknown");
+            features = [ "js" ];
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+            optional = true;
+          }
+          {
+            name = "lz4";
+            packageId = "lz4";
+            optional = true;
+          }
+          {
+            name = "multiversion";
+            packageId = "multiversion";
+            optional = true;
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+            optional = true;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+          }
+          {
+            name = "streaming-iterator";
+            packageId = "streaming-iterator";
+          }
+          {
+            name = "strength_reduce";
+            packageId = "strength_reduce";
+            optional = true;
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "arrow-array" = [ "dep:arrow-array" ];
+          "arrow-buffer" = [ "dep:arrow-buffer" ];
+          "arrow-data" = [ "dep:arrow-data" ];
+          "arrow-format" = [ "dep:arrow-format" ];
+          "arrow-schema" = [ "dep:arrow-schema" ];
+          "arrow_rs" = [ "arrow-buffer" "arrow-schema" "arrow-data" "arrow-array" ];
+          "async-stream" = [ "dep:async-stream" ];
+          "atoi" = [ "dep:atoi" ];
+          "atoi_simd" = [ "dep:atoi_simd" ];
+          "avro-schema" = [ "dep:avro-schema" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "compute" = [ "compute_aggregate" "compute_arithmetics" "compute_bitwise" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_hash" "compute_if_then_else" "compute_take" "compute_temporal" ];
+          "compute_aggregate" = [ "multiversion" ];
+          "compute_arithmetics" = [ "strength_reduce" "compute_arithmetics_decimal" ];
+          "compute_arithmetics_decimal" = [ "strength_reduce" ];
+          "compute_cast" = [ "compute_take" "ryu" "atoi_simd" "itoa" "fast-float" ];
+          "compute_comparison" = [ "compute_take" "compute_boolean" ];
+          "compute_hash" = [ "multiversion" ];
+          "dtype-decimal" = [ "atoi" ];
+          "fast-float" = [ "dep:fast-float" ];
+          "full" = [ "arrow_rs" "io_ipc" "io_flight" "io_ipc_write_async" "io_ipc_read_async" "io_ipc_compression" "io_avro" "io_avro_compression" "io_avro_async" "regex-syntax" "compute" "chrono-tz" ];
+          "futures" = [ "dep:futures" ];
+          "hex" = [ "dep:hex" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "io_avro" = [ "avro-schema" "polars-error/avro-schema" ];
+          "io_avro_async" = [ "avro-schema/async" ];
+          "io_avro_compression" = [ "avro-schema/compression" ];
+          "io_flight" = [ "io_ipc" "arrow-format/flight-data" ];
+          "io_ipc" = [ "arrow-format" "polars-error/arrow-format" ];
+          "io_ipc_compression" = [ "lz4" "zstd" ];
+          "io_ipc_read_async" = [ "io_ipc" "futures" "async-stream" ];
+          "io_ipc_write_async" = [ "io_ipc" "futures" ];
+          "itoa" = [ "dep:itoa" ];
+          "lz4" = [ "dep:lz4" ];
+          "multiversion" = [ "dep:multiversion" ];
+          "regex" = [ "dep:regex" ];
+          "regex-syntax" = [ "dep:regex-syntax" ];
+          "ryu" = [ "dep:ryu" ];
+          "serde" = [ "dep:serde" ];
+          "strength_reduce" = [ "dep:strength_reduce" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "arrow-format" "atoi_simd" "compute" "compute_aggregate" "compute_arithmetics" "compute_arithmetics_decimal" "compute_bitwise" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_hash" "compute_if_then_else" "compute_take" "compute_temporal" "fast-float" "futures" "io_ipc" "io_ipc_compression" "io_ipc_write_async" "itoa" "lz4" "multiversion" "ryu" "strength_reduce" "strings" "temporal" "zstd" ];
+      };
+      "polars-core" = rec {
+        crateName = "polars-core";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "1mnginlmgmlp167ij0r5lywvy50zns1cr8db1ikxxv2xwnwdawxf";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-row";
+            packageId = "polars-row";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            optional = true;
+            features = [ "small_rng" "std" ];
+          }
+          {
+            name = "rand_distr";
+            packageId = "rand_distr";
+            optional = true;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "xxhash-rust";
+            packageId = "xxhash-rust";
+            features = [ "xxh3" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "arrow-array" = [ "dep:arrow-array" ];
+          "arrow_rs" = [ "arrow-array" "arrow/arrow_rs" ];
+          "bigidx" = [ "arrow/bigidx" "polars-utils/bigidx" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "comfy-table" = [ "dep:comfy-table" ];
+          "default" = [ "algorithm_group_by" ];
+          "docs-selection" = [ "ndarray" "rows" "docs" "strings" "object" "lazy" "temporal" "random" "zip_with" "checked_arithmetic" "is_first_distinct" "is_last_distinct" "asof_join" "dot_product" "row_hash" "rolling_window" "dtype-categorical" "dtype-decimal" "diagonal_concat" "horizontal_concat" "dataframe_arithmetic" "product" "describe" "chunked_ids" "partition_by" "algorithm_group_by" ];
+          "dtype-array" = [ "arrow/dtype-array" ];
+          "dtype-date" = [ "temporal" ];
+          "dtype-datetime" = [ "temporal" ];
+          "dtype-decimal" = [ "dep:itoap" "arrow/dtype-decimal" ];
+          "dtype-duration" = [ "temporal" ];
+          "dtype-time" = [ "temporal" ];
+          "dynamic_group_by" = [ "dtype-datetime" "dtype-date" ];
+          "fmt" = [ "comfy-table/tty" ];
+          "fmt_no_tty" = [ "comfy-table" ];
+          "ndarray" = [ "dep:ndarray" ];
+          "nightly" = [ "simd" "hashbrown/nightly" "polars-utils/nightly" "arrow/nightly" ];
+          "object" = [ "serde_json" ];
+          "performant" = [ "arrow/performant" "reinterpret" ];
+          "rand" = [ "dep:rand" ];
+          "rand_distr" = [ "dep:rand_distr" ];
+          "random" = [ "rand" "rand_distr" ];
+          "regex" = [ "dep:regex" ];
+          "serde" = [ "dep:serde" "smartstring/serde" "bitflags/serde" ];
+          "serde-lazy" = [ "serde" "arrow/serde" "indexmap/serde" "smartstring/serde" "chrono/serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "simd" = [ "arrow/simd" ];
+          "strings" = [ "regex" "arrow/strings" "polars-error/regex" ];
+          "temporal" = [ "regex" "chrono" "polars-error/regex" ];
+          "timezones" = [ "chrono-tz" "arrow/chrono-tz" "arrow/timezones" ];
+        };
+        resolvedDefaultFeatures = [ "algorithm_group_by" "chrono" "chunked_ids" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-struct" "dtype-time" "lazy" "rand" "rand_distr" "random" "regex" "reinterpret" "rows" "strings" "temporal" "zip_with" ];
+      };
+      "polars-error" = rec {
+        crateName = "polars-error";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "127l5hazh5hn73j767cfyjwgbvvbab8hj53l1jp976daivb201gb";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "arrow-format";
+            packageId = "arrow-format";
+            optional = true;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        features = {
+          "arrow-format" = [ "dep:arrow-format" ];
+          "avro-schema" = [ "dep:avro-schema" ];
+          "object_store" = [ "dep:object_store" ];
+          "regex" = [ "dep:regex" ];
+        };
+        resolvedDefaultFeatures = [ "arrow-format" "regex" ];
+      };
+      "polars-io" = rec {
+        crateName = "polars-io";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "01mwcdikw7y92xjhlsmj4wf0p9r3kvmhrvsbnsfh1mmc8l3hmqcn";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+            optional = true;
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "home";
+            packageId = "home";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "memmap2";
+            packageId = "memmap2";
+            rename = "memmap";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-parquet";
+            packageId = "polars-parquet";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "net" "rt-multi-thread" "time" "sync" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            optional = true;
+            features = [ "io" "io-util" ];
+          }
+        ];
+        features = {
+          "async" = [ "async-trait" "futures" "tokio" "tokio-util" "arrow/io_ipc_write_async" "polars-error/regex" "polars-parquet?/async" ];
+          "async-trait" = [ "dep:async-trait" ];
+          "atoi_simd" = [ "dep:atoi_simd" ];
+          "avro" = [ "arrow/io_avro" "arrow/io_avro_compression" ];
+          "aws" = [ "object_store/aws" "cloud" "reqwest" ];
+          "azure" = [ "object_store/azure" "cloud" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "cloud" = [ "object_store" "async" "polars-error/object_store" "url" ];
+          "csv" = [ "atoi_simd" "polars-core/rows" "itoa" "ryu" "fast-float" "simdutf8" ];
+          "decompress" = [ "flate2/rust_backend" "zstd" ];
+          "decompress-fast" = [ "flate2/zlib-ng" "zstd" ];
+          "default" = [ "decompress" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-time/dtype-date" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-core/temporal" "polars-time/dtype-datetime" "chrono" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-core/temporal" "polars-time/dtype-time" ];
+          "fast-float" = [ "dep:fast-float" ];
+          "flate2" = [ "dep:flate2" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "futures" = [ "dep:futures" ];
+          "gcp" = [ "object_store/gcp" "cloud" ];
+          "http" = [ "object_store/http" "cloud" ];
+          "ipc" = [ "arrow/io_ipc" "arrow/io_ipc_compression" ];
+          "ipc_streaming" = [ "arrow/io_ipc" "arrow/io_ipc_compression" ];
+          "itoa" = [ "dep:itoa" ];
+          "json" = [ "polars-json" "simd-json" "atoi_simd" "serde_json" "dtype-struct" "csv" ];
+          "object_store" = [ "dep:object_store" ];
+          "parquet" = [ "polars-parquet" "polars-parquet/compression" ];
+          "partition" = [ "polars-core/partition_by" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "polars-parquet" = [ "dep:polars-parquet" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "python" = [ "polars-error/python" ];
+          "reqwest" = [ "dep:reqwest" ];
+          "ryu" = [ "dep:ryu" ];
+          "serde" = [ "dep:serde" "polars-core/serde-lazy" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "simd-json" = [ "dep:simd-json" ];
+          "simdutf8" = [ "dep:simdutf8" ];
+          "temporal" = [ "dtype-datetime" "dtype-date" "dtype-time" ];
+          "timezones" = [ "chrono-tz" "dtype-datetime" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "url" = [ "dep:url" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "async" "async-trait" "dtype-struct" "futures" "ipc" "lazy" "parquet" "polars-parquet" "tokio" "tokio-util" ];
+      };
+      "polars-lazy" = rec {
+        crateName = "polars-lazy";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "084gp9qa1w9b7dglcmrvlx6xrcl7hw5nmlb26w6xvrjvf1czfm9m";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "lazy" "zip_with" "random" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            usesDefaultFeatures = false;
+            features = [ "lazy" ];
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-pipe";
+            packageId = "polars-pipe";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-plan";
+            packageId = "polars-plan";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-time";
+            packageId = "polars-time";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "abs" = [ "polars-plan/abs" ];
+          "approx_unique" = [ "polars-plan/approx_unique" ];
+          "arg_where" = [ "polars-plan/arg_where" ];
+          "asof_join" = [ "polars-plan/asof_join" "polars-time" "polars-ops/asof_join" ];
+          "async" = [ "polars-plan/async" "polars-io/cloud" "polars-pipe?/async" ];
+          "bigidx" = [ "polars-plan/bigidx" ];
+          "binary_encoding" = [ "polars-plan/binary_encoding" ];
+          "chunked_ids" = [ "polars-plan/chunked_ids" "polars-core/chunked_ids" "polars-ops/chunked_ids" ];
+          "cloud" = [ "async" "polars-pipe?/cloud" "polars-plan/cloud" "tokio" "futures" ];
+          "cloud_write" = [ "cloud" ];
+          "coalesce" = [ "polars-plan/coalesce" ];
+          "concat_str" = [ "polars-plan/concat_str" ];
+          "cov" = [ "polars-ops/cov" "polars-plan/cov" ];
+          "cross_join" = [ "polars-plan/cross_join" "polars-pipe?/cross_join" "polars-ops/cross_join" ];
+          "cse" = [ "polars-plan/cse" ];
+          "csv" = [ "polars-io/csv" "polars-plan/csv" "polars-pipe?/csv" ];
+          "cum_agg" = [ "polars-plan/cum_agg" ];
+          "cutqcut" = [ "polars-plan/cutqcut" "polars-ops/cutqcut" ];
+          "date_offset" = [ "polars-plan/date_offset" ];
+          "diff" = [ "polars-plan/diff" "polars-plan/diff" ];
+          "dot_diagram" = [ "polars-plan/dot_diagram" ];
+          "dtype-array" = [ "polars-plan/dtype-array" "polars-pipe?/dtype-array" "polars-ops/dtype-array" ];
+          "dtype-categorical" = [ "polars-plan/dtype-categorical" "polars-pipe?/dtype-categorical" ];
+          "dtype-date" = [ "polars-plan/dtype-date" "polars-time/dtype-date" "temporal" ];
+          "dtype-datetime" = [ "polars-plan/dtype-datetime" "polars-time/dtype-datetime" "temporal" ];
+          "dtype-decimal" = [ "polars-plan/dtype-decimal" "polars-pipe?/dtype-decimal" ];
+          "dtype-duration" = [ "polars-plan/dtype-duration" "polars-time/dtype-duration" "temporal" ];
+          "dtype-i16" = [ "polars-plan/dtype-i16" "polars-pipe?/dtype-i16" ];
+          "dtype-i8" = [ "polars-plan/dtype-i8" "polars-pipe?/dtype-i8" ];
+          "dtype-struct" = [ "polars-plan/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "temporal" ];
+          "dtype-u16" = [ "polars-plan/dtype-u16" "polars-pipe?/dtype-u16" ];
+          "dtype-u8" = [ "polars-plan/dtype-u8" "polars-pipe?/dtype-u8" ];
+          "dynamic_group_by" = [ "polars-plan/dynamic_group_by" "polars-time" "temporal" ];
+          "ewma" = [ "polars-plan/ewma" ];
+          "extract_groups" = [ "polars-plan/extract_groups" ];
+          "extract_jsonpath" = [ "polars-plan/extract_jsonpath" "polars-ops/extract_jsonpath" ];
+          "fmt" = [ "polars-core/fmt" "polars-plan/fmt" ];
+          "fused" = [ "polars-plan/fused" "polars-ops/fused" ];
+          "futures" = [ "dep:futures" ];
+          "interpolate" = [ "polars-plan/interpolate" ];
+          "ipc" = [ "polars-io/ipc" "polars-plan/ipc" "polars-pipe?/ipc" ];
+          "is_first_distinct" = [ "polars-plan/is_first_distinct" ];
+          "is_in" = [ "polars-plan/is_in" "polars-ops/is_in" ];
+          "is_last_distinct" = [ "polars-plan/is_last_distinct" ];
+          "is_unique" = [ "polars-plan/is_unique" ];
+          "json" = [ "polars-io/json" "polars-plan/json" "polars-json" ];
+          "list_any_all" = [ "polars-ops/list_any_all" "polars-plan/list_any_all" ];
+          "list_count" = [ "polars-ops/list_count" "polars-plan/list_count" ];
+          "list_drop_nulls" = [ "polars-ops/list_drop_nulls" "polars-plan/list_drop_nulls" ];
+          "list_gather" = [ "polars-ops/list_gather" "polars-plan/list_gather" ];
+          "list_sample" = [ "polars-ops/list_sample" "polars-plan/list_sample" ];
+          "list_sets" = [ "polars-plan/list_sets" "polars-ops/list_sets" ];
+          "list_to_struct" = [ "polars-plan/list_to_struct" ];
+          "log" = [ "polars-plan/log" ];
+          "merge_sorted" = [ "polars-plan/merge_sorted" ];
+          "meta" = [ "polars-plan/meta" ];
+          "mode" = [ "polars-plan/mode" ];
+          "moment" = [ "polars-plan/moment" "polars-ops/moment" ];
+          "nightly" = [ "polars-core/nightly" "polars-pipe?/nightly" "polars-plan/nightly" ];
+          "object" = [ "polars-plan/object" ];
+          "panic_on_schema" = [ "polars-plan/panic_on_schema" ];
+          "parquet" = [ "polars-io/parquet" "polars-plan/parquet" "polars-pipe?/parquet" ];
+          "pct_change" = [ "polars-plan/pct_change" ];
+          "peaks" = [ "polars-plan/peaks" ];
+          "pivot" = [ "polars-core/rows" "polars-ops/pivot" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "polars-pipe" = [ "dep:polars-pipe" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "propagate_nans" = [ "polars-plan/propagate_nans" ];
+          "pyo3" = [ "dep:pyo3" ];
+          "python" = [ "pyo3" "polars-plan/python" "polars-core/python" "polars-io/python" ];
+          "random" = [ "polars-plan/random" ];
+          "range" = [ "polars-plan/range" ];
+          "rank" = [ "polars-plan/rank" ];
+          "regex" = [ "polars-plan/regex" ];
+          "repeat_by" = [ "polars-plan/repeat_by" ];
+          "rle" = [ "polars-plan/rle" "polars-ops/rle" ];
+          "rolling_window" = [ "polars-plan/rolling_window" "polars-time/rolling_window" ];
+          "round_series" = [ "polars-plan/round_series" "polars-ops/round_series" ];
+          "row_hash" = [ "polars-plan/row_hash" ];
+          "search_sorted" = [ "polars-plan/search_sorted" ];
+          "semi_anti_join" = [ "polars-plan/semi_anti_join" ];
+          "serde" = [ "polars-plan/serde" "arrow/serde" "polars-core/serde-lazy" "polars-time?/serde" "polars-io/serde" "polars-ops/serde" ];
+          "sign" = [ "polars-plan/sign" ];
+          "streaming" = [ "chunked_ids" "polars-pipe" "polars-plan/streaming" "polars-ops/chunked_ids" ];
+          "string_encoding" = [ "polars-plan/string_encoding" ];
+          "string_pad" = [ "polars-plan/string_pad" ];
+          "string_to_integer" = [ "polars-plan/string_to_integer" ];
+          "strings" = [ "polars-plan/strings" ];
+          "temporal" = [ "dtype-datetime" "dtype-date" "dtype-time" "dtype-duration" "polars-plan/temporal" ];
+          "test" = [ "polars-plan/debugging" "panic_on_schema" "rolling_window" "rank" "round_series" "csv" "dtype-categorical" "cum_agg" "regex" "polars-core/fmt" "diff" "abs" "parquet" "ipc" "dtype-date" ];
+          "test_all" = [ "test" "strings" "regex" "ipc" "row_hash" "string_pad" "string_to_integer" "search_sorted" "top_k" "pivot" "semi_anti_join" "cse" ];
+          "timezones" = [ "polars-plan/timezones" ];
+          "tokio" = [ "dep:tokio" ];
+          "top_k" = [ "polars-plan/top_k" ];
+          "trigonometry" = [ "polars-plan/trigonometry" ];
+          "true_div" = [ "polars-plan/true_div" ];
+          "unique_counts" = [ "polars-plan/unique_counts" ];
+        };
+        resolvedDefaultFeatures = [ "abs" "cross_join" "cum_agg" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-struct" "dtype-time" "is_in" "log" "meta" "parquet" "polars-time" "regex" "round_series" "strings" "temporal" "trigonometry" ];
+      };
+      "polars-ops" = rec {
+        crateName = "polars-ops";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "0dpnxcgc57k8xvp4jmjhz8jwz8rclf62h22zjiwpzaka54cb4zhs";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "argminmax";
+            packageId = "argminmax";
+            usesDefaultFeatures = false;
+            features = [ "float" ];
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "algorithm_group_by" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "asof_join" = [ "polars-core/asof_join" ];
+          "base64" = [ "dep:base64" ];
+          "big_idx" = [ "polars-core/bigidx" ];
+          "binary_encoding" = [ "base64" "hex" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "chunked_ids" = [ "polars-core/chunked_ids" ];
+          "cutqcut" = [ "dtype-categorical" "dtype-struct" ];
+          "dtype-array" = [ "polars-core/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-core/temporal" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-core/temporal" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-core/temporal" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" "polars-core/temporal" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-core/temporal" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" ];
+          "extract_groups" = [ "dtype-struct" "polars-core/regex" ];
+          "extract_jsonpath" = [ "serde_json" "jsonpath_lib" "polars-json" ];
+          "group_by_list" = [ "polars-core/group_by_list" ];
+          "hex" = [ "dep:hex" ];
+          "is_in" = [ "polars-core/reinterpret" ];
+          "jsonpath_lib" = [ "dep:jsonpath_lib" ];
+          "list_to_struct" = [ "polars-core/dtype-struct" ];
+          "nightly" = [ "polars-utils/nightly" ];
+          "object" = [ "polars-core/object" ];
+          "pct_change" = [ "diff" ];
+          "performant" = [ "polars-core/performant" "fused" ];
+          "pivot" = [ "polars-core/reinterpret" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "rand" = [ "dep:rand" ];
+          "rand_distr" = [ "dep:rand_distr" ];
+          "random" = [ "rand" "rand_distr" ];
+          "rank" = [ "rand" ];
+          "rle" = [ "dtype-struct" ];
+          "rolling_window" = [ "polars-core/rolling_window" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "simd" = [ "argminmax/nightly_simd" ];
+          "string_encoding" = [ "base64" "hex" ];
+          "string_pad" = [ "polars-core/strings" ];
+          "string_to_integer" = [ "polars-core/strings" ];
+          "strings" = [ "polars-core/strings" ];
+          "timezones" = [ "chrono-tz" "chrono" ];
+          "zip_with" = [ "polars-core/zip_with" ];
+        };
+        resolvedDefaultFeatures = [ "abs" "cross_join" "cum_agg" "dtype-struct" "is_in" "log" "round_series" "search_sorted" "strings" "zip_with" ];
+      };
+      "polars-parquet" = rec {
+        crateName = "polars-parquet";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "1pb79wb1f31pk48lfm2w72hdfxqz6vv65iyxb072skfxnzj10q0l";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+          "Apache Arrow <dev@arrow.apache.org>"
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+            optional = true;
+          }
+          {
+            name = "base64";
+            packageId = "base64 0.21.5";
+          }
+          {
+            name = "brotli";
+            packageId = "brotli";
+            optional = true;
+          }
+          {
+            name = "ethnum";
+            packageId = "ethnum";
+          }
+          {
+            name = "flate2";
+            packageId = "flate2";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "lz4";
+            packageId = "lz4";
+            optional = true;
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "parquet-format-safe";
+            packageId = "parquet-format-safe";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" "io_ipc" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "seq-macro";
+            packageId = "seq-macro";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+          }
+          {
+            name = "snap";
+            packageId = "snap";
+            optional = true;
+          }
+          {
+            name = "streaming-decompression";
+            packageId = "streaming-decompression";
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "async" = [ "async-stream" "futures" "parquet-format-safe/async" ];
+          "async-stream" = [ "dep:async-stream" ];
+          "bloom_filter" = [ "xxhash-rust" ];
+          "brotli" = [ "dep:brotli" ];
+          "compression" = [ "zstd" "gzip" "snappy" "lz4" "brotli" ];
+          "fallible-streaming-iterator" = [ "dep:fallible-streaming-iterator" ];
+          "flate2" = [ "dep:flate2" ];
+          "futures" = [ "dep:futures" ];
+          "gzip" = [ "flate2/rust_backend" ];
+          "gzip_zlib_ng" = [ "flate2/zlib-ng" ];
+          "lz4" = [ "dep:lz4" ];
+          "serde" = [ "dep:serde" ];
+          "serde_types" = [ "serde" ];
+          "snap" = [ "dep:snap" ];
+          "snappy" = [ "snap" ];
+          "xxhash-rust" = [ "dep:xxhash-rust" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "async" "async-stream" "brotli" "compression" "flate2" "futures" "gzip" "lz4" "snap" "snappy" "zstd" ];
+      };
+      "polars-pipe" = rec {
+        crateName = "polars-pipe";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "1v5xniw8yxx9cnksc4bf169b3p7gcl6dzryzgfd2m4scyrylw2b6";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-channel";
+            packageId = "crossbeam-channel";
+          }
+          {
+            name = "crossbeam-queue";
+            packageId = "crossbeam-queue";
+          }
+          {
+            name = "enum_dispatch";
+            packageId = "enum_dispatch";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "lazy" "zip_with" "random" "rows" "chunked_ids" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            usesDefaultFeatures = false;
+            features = [ "ipc" ];
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+            features = [ "search_sorted" ];
+          }
+          {
+            name = "polars-plan";
+            packageId = "polars-plan";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-row";
+            packageId = "polars-row";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+            features = [ "sysinfo" ];
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "async" = [ "polars-plan/async" "polars-io/async" ];
+          "cloud" = [ "async" "polars-io/cloud" "polars-plan/cloud" "tokio" "futures" ];
+          "cross_join" = [ "polars-ops/cross_join" ];
+          "csv" = [ "polars-plan/csv" "polars-io/csv" ];
+          "dtype-array" = [ "polars-core/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" ];
+          "futures" = [ "dep:futures" ];
+          "ipc" = [ "polars-plan/ipc" "polars-io/ipc" ];
+          "nightly" = [ "polars-core/nightly" "polars-utils/nightly" "hashbrown/nightly" ];
+          "parquet" = [ "polars-plan/parquet" "polars-io/parquet" "polars-io/async" ];
+          "test" = [ "polars-core/chunked_ids" ];
+          "tokio" = [ "dep:tokio" ];
+        };
+        resolvedDefaultFeatures = [ "cross_join" "parquet" ];
+      };
+      "polars-plan" = rec {
+        crateName = "polars-plan";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "1g2z0g0hpg05jp4n9s6m6m32adrjrdlq6zxd5c9lp1ggb04jmqqh";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "lazy" "zip_with" "random" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            usesDefaultFeatures = false;
+            features = [ "lazy" ];
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+            features = [ "zip_with" ];
+          }
+          {
+            name = "polars-parquet";
+            packageId = "polars-parquet";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-time";
+            packageId = "polars-time";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "strum_macros";
+            packageId = "strum_macros";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "abs" = [ "polars-ops/abs" ];
+          "approx_unique" = [ "polars-ops/approx_unique" ];
+          "asof_join" = [ "polars-core/asof_join" "polars-time" "polars-ops/asof_join" ];
+          "async" = [ "polars-io/async" ];
+          "bigidx" = [ "polars-core/bigidx" ];
+          "binary_encoding" = [ "polars-ops/binary_encoding" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "chunked_ids" = [ "polars-core/chunked_ids" ];
+          "ciborium" = [ "dep:ciborium" ];
+          "cloud" = [ "async" "polars-io/cloud" ];
+          "cov" = [ "polars-ops/cov" ];
+          "cross_join" = [ "polars-ops/cross_join" ];
+          "csv" = [ "polars-io/csv" ];
+          "cum_agg" = [ "polars-ops/cum_agg" ];
+          "cutqcut" = [ "polars-ops/cutqcut" ];
+          "date_offset" = [ "polars-time" "chrono" ];
+          "diff" = [ "polars-ops/diff" ];
+          "dtype-array" = [ "polars-core/dtype-array" "polars-ops/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-time/dtype-date" "temporal" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-time/dtype-datetime" "temporal" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-time/dtype-duration" "temporal" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-time/dtype-time" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" ];
+          "dynamic_group_by" = [ "polars-core/dynamic_group_by" ];
+          "ewma" = [ "polars-ops/ewma" ];
+          "extract_groups" = [ "regex" "dtype-struct" "polars-ops/extract_groups" ];
+          "extract_jsonpath" = [ "polars-ops/extract_jsonpath" ];
+          "ffi_plugin" = [ "libloading" "polars-ffi" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "fused" = [ "polars-ops/fused" ];
+          "futures" = [ "dep:futures" ];
+          "interpolate" = [ "polars-ops/interpolate" ];
+          "ipc" = [ "polars-io/ipc" ];
+          "is_first_distinct" = [ "polars-core/is_first_distinct" "polars-ops/is_first_distinct" ];
+          "is_in" = [ "polars-ops/is_in" ];
+          "is_last_distinct" = [ "polars-core/is_last_distinct" "polars-ops/is_last_distinct" ];
+          "is_unique" = [ "polars-ops/is_unique" ];
+          "json" = [ "polars-io/json" ];
+          "libloading" = [ "dep:libloading" ];
+          "list_any_all" = [ "polars-ops/list_any_all" ];
+          "list_count" = [ "polars-ops/list_count" ];
+          "list_drop_nulls" = [ "polars-ops/list_drop_nulls" ];
+          "list_gather" = [ "polars-ops/list_gather" ];
+          "list_sample" = [ "polars-ops/list_sample" ];
+          "list_sets" = [ "polars-ops/list_sets" ];
+          "list_to_struct" = [ "polars-ops/list_to_struct" ];
+          "log" = [ "polars-ops/log" ];
+          "merge_sorted" = [ "polars-ops/merge_sorted" ];
+          "mode" = [ "polars-ops/mode" ];
+          "moment" = [ "polars-ops/moment" ];
+          "nightly" = [ "polars-utils/nightly" "polars-ops/nightly" ];
+          "object" = [ "polars-core/object" ];
+          "parquet" = [ "polars-io/parquet" "polars-parquet" ];
+          "pct_change" = [ "polars-ops/pct_change" ];
+          "peaks" = [ "polars-ops/peaks" ];
+          "pivot" = [ "polars-core/rows" "polars-ops/pivot" ];
+          "polars-ffi" = [ "dep:polars-ffi" ];
+          "polars-parquet" = [ "dep:polars-parquet" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "propagate_nans" = [ "polars-ops/propagate_nans" ];
+          "python" = [ "dep:pyo3" "ciborium" ];
+          "random" = [ "polars-core/random" ];
+          "rank" = [ "polars-ops/rank" ];
+          "regex" = [ "dep:regex" ];
+          "repeat_by" = [ "polars-ops/repeat_by" ];
+          "rle" = [ "polars-ops/rle" ];
+          "rolling_window" = [ "polars-core/rolling_window" "polars-time/rolling_window" "polars-ops/rolling_window" "polars-time/rolling_window" ];
+          "round_series" = [ "polars-ops/round_series" ];
+          "row_hash" = [ "polars-core/row_hash" "polars-ops/hash" ];
+          "search_sorted" = [ "polars-ops/search_sorted" ];
+          "semi_anti_join" = [ "polars-ops/semi_anti_join" ];
+          "serde" = [ "dep:serde" "polars-core/serde-lazy" "polars-time/serde" "polars-io/serde" "polars-ops/serde" ];
+          "string_encoding" = [ "polars-ops/string_encoding" ];
+          "string_pad" = [ "polars-ops/string_pad" ];
+          "string_to_integer" = [ "polars-ops/string_to_integer" ];
+          "strings" = [ "polars-core/strings" "polars-ops/strings" ];
+          "temporal" = [ "polars-core/temporal" "dtype-date" "dtype-datetime" "dtype-time" ];
+          "timezones" = [ "chrono-tz" "polars-time/timezones" "polars-core/timezones" "regex" ];
+          "top_k" = [ "polars-ops/top_k" ];
+          "unique_counts" = [ "polars-ops/unique_counts" ];
+        };
+        resolvedDefaultFeatures = [ "abs" "cross_join" "cum_agg" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-struct" "dtype-time" "is_in" "log" "meta" "parquet" "polars-parquet" "polars-time" "regex" "round_series" "strings" "temporal" "trigonometry" ];
+      };
+      "polars-row" = rec {
+        crateName = "polars-row";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "1b3d8pdxz12dzg75nqb7b2p83l15c1z4r6589sknp462ra0sndfi";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+
+      };
+      "polars-sql" = rec {
+        crateName = "polars-sql";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "17jiflfmyymz1fdk2mrsdri2yqs1h7rqn66y3yny79a9d1wdgnxq";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-lazy";
+            packageId = "polars-lazy";
+            usesDefaultFeatures = false;
+            features = [ "strings" "cross_join" "trigonometry" "abs" "round_series" "log" "regex" "is_in" "meta" "cum_agg" "dtype-date" ];
+          }
+          {
+            name = "polars-plan";
+            packageId = "polars-plan";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sqlparser";
+            packageId = "sqlparser";
+          }
+        ];
+        features = {
+          "csv" = [ "polars-lazy/csv" ];
+          "diagonal_concat" = [ "polars-lazy/diagonal_concat" ];
+          "ipc" = [ "polars-lazy/ipc" ];
+          "json" = [ "polars-lazy/json" ];
+          "parquet" = [ "polars-lazy/parquet" ];
+          "semi_anti_join" = [ "polars-lazy/semi_anti_join" ];
+        };
+        resolvedDefaultFeatures = [ "parquet" ];
+      };
+      "polars-time" = rec {
+        crateName = "polars-time";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "0lgyk3fpp7krbyfra51pb4fqn6m3hk5gbj61fdvn3pffx5wnzrda";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "atoi";
+            packageId = "atoi";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "now";
+            packageId = "now";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" "compute" "temporal" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "dtype-datetime" "dtype-duration" "dtype-time" "dtype-date" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        features = {
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-core/temporal" ];
+          "dtype-datetime" = [ "polars-core/dtype-date" "polars-core/temporal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-core/temporal" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-core/temporal" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "rolling_window" = [ "polars-core/rolling_window" "dtype-duration" ];
+          "serde" = [ "dep:serde" ];
+          "test" = [ "dtype-date" "dtype-datetime" "polars-core/fmt" ];
+          "timezones" = [ "chrono-tz" "dtype-datetime" "polars-core/timezones" "arrow/timezones" "polars-ops/timezones" ];
+        };
+        resolvedDefaultFeatures = [ "dtype-date" "dtype-datetime" "dtype-duration" "dtype-time" ];
+      };
+      "polars-utils" = rec {
+        crateName = "polars-utils";
+        version = "0.35.4";
+        edition = "2021";
+        sha256 = "1z7v7h54p883ww64mqpn4cphzwv0fd2bgsn8b1lx8qgyd60ycv6s";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "sysinfo";
+            packageId = "sysinfo";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "sysinfo" = [ "dep:sysinfo" ];
+        };
+        resolvedDefaultFeatures = [ "sysinfo" ];
+      };
+      "portable-atomic" = rec {
+        crateName = "portable-atomic";
+        version = "1.5.1";
+        edition = "2018";
+        sha256 = "0fxg0i7n3wmffbfn95nwi062srdg40bwkj5143w1kk6pgw7apk1v";
+        features = {
+          "critical-section" = [ "dep:critical-section" ];
+          "default" = [ "fallback" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "fallback" ];
+      };
+      "ppv-lite86" = rec {
+        crateName = "ppv-lite86";
+        version = "0.2.17";
+        edition = "2018";
+        sha256 = "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v";
+        authors = [
+          "The CryptoCorrosion Contributors"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "simd" "std" ];
+      };
+      "prettyplease" = rec {
+        crateName = "prettyplease";
+        version = "0.2.15";
+        edition = "2021";
+        links = "prettyplease02";
+        sha256 = "17az47j29q76gnyqvd5giryjz2fp7zw7vzcka1rb8ndbfgbmn05f";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            usesDefaultFeatures = false;
+            features = [ "full" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            usesDefaultFeatures = false;
+            features = [ "parsing" ];
+          }
+        ];
+        features = {
+          "verbatim" = [ "syn/parsing" ];
+        };
+      };
+      "proc-macro2" = rec {
+        crateName = "proc-macro2";
+        version = "1.0.69";
+        edition = "2021";
+        sha256 = "1nljgyllbm3yr3pa081bf83gxh6l4zvjqzaldw7v4mj9xfgihk0k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "prost" = rec {
+        crateName = "prost";
+        version = "0.12.2";
+        edition = "2021";
+        sha256 = "04k3c8d2k554gcbhii04canz6g1m6wbx00cdxdnzcal8qw7l2njs";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "prost-derive";
+            packageId = "prost-derive";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "prost-derive" "std" ];
+          "prost-derive" = [ "dep:prost-derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "prost-derive" "std" ];
+      };
+      "prost-build" = rec {
+        crateName = "prost-build";
+        version = "0.12.2";
+        edition = "2021";
+        sha256 = "0f57mmf6cg7f4401x9s3fgdc1idnz7i1nxxjxyzi2jbhr22d18qz";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "itertools";
+            packageId = "itertools";
+            usesDefaultFeatures = false;
+            features = [ "use_alloc" ];
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "multimap";
+            packageId = "multimap";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "petgraph";
+            packageId = "petgraph";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "prettyplease";
+            packageId = "prettyplease";
+            optional = true;
+          }
+          {
+            name = "prost";
+            packageId = "prost";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "prost-types";
+            packageId = "prost-types";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            usesDefaultFeatures = false;
+            features = [ "std" "unicode-bool" ];
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            optional = true;
+            features = [ "full" ];
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+          }
+          {
+            name = "which";
+            packageId = "which";
+          }
+        ];
+        features = {
+          "cleanup-markdown" = [ "pulldown-cmark" "pulldown-cmark-to-cmark" ];
+          "default" = [ "format" ];
+          "format" = [ "prettyplease" "syn" ];
+          "prettyplease" = [ "dep:prettyplease" ];
+          "pulldown-cmark" = [ "dep:pulldown-cmark" ];
+          "pulldown-cmark-to-cmark" = [ "dep:pulldown-cmark-to-cmark" ];
+          "syn" = [ "dep:syn" ];
+        };
+        resolvedDefaultFeatures = [ "default" "format" "prettyplease" "syn" ];
+      };
+      "prost-derive" = rec {
+        crateName = "prost-derive";
+        version = "0.12.2";
+        edition = "2021";
+        sha256 = "1g268fzmswaf6rx1sm8000h6a4rigd4b6zg55wysi95cvyjifmq6";
+        procMacro = true;
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "anyhow";
+            packageId = "anyhow";
+          }
+          {
+            name = "itertools";
+            packageId = "itertools";
+            usesDefaultFeatures = false;
+            features = [ "use_alloc" ];
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "extra-traits" ];
+          }
+        ];
+
+      };
+      "prost-types" = rec {
+        crateName = "prost-types";
+        version = "0.12.2";
+        edition = "2021";
+        sha256 = "0lls5w7yh2jxi764kd9k44dwskrr85j2fs335wg2i47m6qig6fc3";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Lucio Franco <luciofranco14@gmail.com"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "prost";
+            packageId = "prost";
+            usesDefaultFeatures = false;
+            features = [ "prost-derive" ];
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "prost/std" ];
+        };
+      };
+      "quote" = rec {
+        crateName = "quote";
+        version = "1.0.33";
+        edition = "2018";
+        sha256 = "1biw54hbbr12wdwjac55z1m2x2rylciw83qnjn564a3096jgqrsj";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "rand" = rec {
+        crateName = "rand";
+        version = "0.8.5";
+        edition = "2018";
+        sha256 = "013l6931nn7gkc23jz5mm3qdhf93jjf0fg64nz2lp4i51qd8vbrl";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "rand_chacha";
+            packageId = "rand_chacha";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "alloc" = [ "rand_core/alloc" ];
+          "default" = [ "std" "std_rng" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "libc" = [ "dep:libc" ];
+          "log" = [ "dep:log" ];
+          "packed_simd" = [ "dep:packed_simd" ];
+          "rand_chacha" = [ "dep:rand_chacha" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" "rand_core/serde1" ];
+          "simd_support" = [ "packed_simd" ];
+          "std" = [ "rand_core/std" "rand_chacha/std" "alloc" "getrandom" "libc" ];
+          "std_rng" = [ "rand_chacha" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "getrandom" "libc" "rand_chacha" "small_rng" "std" "std_rng" ];
+      };
+      "rand_chacha" = rec {
+        crateName = "rand_chacha";
+        version = "0.3.1";
+        edition = "2018";
+        sha256 = "123x2adin558xbhvqb8w4f6syjsdkmqff8cxwhmjacpsl1ihmhg6";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+          "The CryptoCorrosion Contributors"
+        ];
+        dependencies = [
+          {
+            name = "ppv-lite86";
+            packageId = "ppv-lite86";
+            usesDefaultFeatures = false;
+            features = [ "simd" ];
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "ppv-lite86/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "rand_core" = rec {
+        crateName = "rand_core";
+        version = "0.6.4";
+        edition = "2018";
+        sha256 = "0b4j2v4cb5krak1pv6kakv4sz6xcwbrmy2zckc32hsigbrwy82zc";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+        ];
+        features = {
+          "getrandom" = [ "dep:getrandom" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "alloc" "getrandom" "getrandom/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "getrandom" "std" ];
+      };
+      "rand_distr" = rec {
+        crateName = "rand_distr";
+        version = "0.4.3";
+        edition = "2018";
+        sha256 = "0cgfwg3z0pkqhrl0x90c77kx70r6g9z4m6fxq9v0h2ibr2dhpjrj";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+            features = [ "libm" ];
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rand";
+            packageId = "rand";
+            usesDefaultFeatures = false;
+            features = [ "std_rng" "std" "small_rng" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "rand/alloc" ];
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" "rand/serde1" ];
+          "std" = [ "alloc" "rand/std" ];
+          "std_math" = [ "num-traits/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "rayon" = rec {
+        crateName = "rayon";
+        version = "1.8.0";
+        edition = "2021";
+        sha256 = "1cfdnvchf7j4cpha5jkcrrsr61li9i9lp5ak7xdq6d3pvc1xn9ww";
+        authors = [
+          "Niko Matsakis <niko@alum.mit.edu>"
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon-core";
+            packageId = "rayon-core";
+          }
+        ];
+
+      };
+      "rayon-core" = rec {
+        crateName = "rayon-core";
+        version = "1.12.0";
+        edition = "2021";
+        links = "rayon-core";
+        sha256 = "1vaq0q71yfvcwlmia0iqf6ixj2fibjcf2xjy92n1m1izv1mgpqsw";
+        authors = [
+          "Niko Matsakis <niko@alum.mit.edu>"
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-deque";
+            packageId = "crossbeam-deque";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+        ];
+
+      };
+      "redox_syscall 0.2.16" = rec {
+        crateName = "redox_syscall";
+        version = "0.2.16";
+        edition = "2018";
+        sha256 = "16jicm96kjyzm802cxdd1k9jmcph0db1a4lhslcnhjsvhp0mhnpv";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+
+      };
+      "redox_syscall 0.4.1" = rec {
+        crateName = "redox_syscall";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1aiifyz5dnybfvkk4cdab9p2kmphag1yad6iknc7aszlxxldf8j7";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+        features = {
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "bitflags/rustc-dep-of-std" ];
+        };
+      };
+      "redox_users" = rec {
+        crateName = "redox_users";
+        version = "0.4.4";
+        edition = "2021";
+        sha256 = "1d1c7dhbb62sh8jrq9dhvqcyxqsh3wg8qknsi94iwq3r0wh7k151";
+        authors = [
+          "Jose Narvaez <goyox86@gmail.com>"
+          "Wesley Hershberger <mggmugginsmc@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            features = [ "std" ];
+          }
+          {
+            name = "libredox";
+            packageId = "libredox";
+            usesDefaultFeatures = false;
+            features = [ "call" ];
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        features = {
+          "auth" = [ "rust-argon2" "zeroize" ];
+          "default" = [ "auth" ];
+          "rust-argon2" = [ "dep:rust-argon2" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+      };
+      "regex" = rec {
+        crateName = "regex";
+        version = "1.10.2";
+        edition = "2021";
+        sha256 = "0hxkd814n4irind8im5c9am221ri6bprx49nc7yxv02ykhd9a2rq";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata";
+            usesDefaultFeatures = false;
+            features = [ "alloc" "syntax" "meta" "nfa-pikevm" ];
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf" "unicode" "regex-syntax/default" ];
+          "logging" = [ "aho-corasick?/logging" "memchr?/logging" "regex-automata/logging" ];
+          "perf" = [ "perf-cache" "perf-dfa" "perf-onepass" "perf-backtrack" "perf-inline" "perf-literal" ];
+          "perf-backtrack" = [ "regex-automata/nfa-backtrack" ];
+          "perf-dfa" = [ "regex-automata/hybrid" ];
+          "perf-dfa-full" = [ "regex-automata/dfa-build" "regex-automata/dfa-search" ];
+          "perf-inline" = [ "regex-automata/perf-inline" ];
+          "perf-literal" = [ "dep:aho-corasick" "dep:memchr" "regex-automata/perf-literal" ];
+          "perf-onepass" = [ "regex-automata/dfa-onepass" ];
+          "std" = [ "aho-corasick?/std" "memchr?/std" "regex-automata/std" "regex-syntax/std" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "regex-automata/unicode" "regex-syntax/unicode" ];
+          "unicode-age" = [ "regex-automata/unicode-age" "regex-syntax/unicode-age" ];
+          "unicode-bool" = [ "regex-automata/unicode-bool" "regex-syntax/unicode-bool" ];
+          "unicode-case" = [ "regex-automata/unicode-case" "regex-syntax/unicode-case" ];
+          "unicode-gencat" = [ "regex-automata/unicode-gencat" "regex-syntax/unicode-gencat" ];
+          "unicode-perl" = [ "regex-automata/unicode-perl" "regex-automata/unicode-word-boundary" "regex-syntax/unicode-perl" ];
+          "unicode-script" = [ "regex-automata/unicode-script" "regex-syntax/unicode-script" ];
+          "unicode-segment" = [ "regex-automata/unicode-segment" "regex-syntax/unicode-segment" ];
+          "unstable" = [ "pattern" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "perf" "perf-backtrack" "perf-cache" "perf-dfa" "perf-inline" "perf-literal" "perf-onepass" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "regex-automata" = rec {
+        crateName = "regex-automata";
+        version = "0.4.3";
+        edition = "2021";
+        sha256 = "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "syntax" "perf" "unicode" "meta" "nfa" "dfa" "hybrid" ];
+          "dfa" = [ "dfa-build" "dfa-search" "dfa-onepass" ];
+          "dfa-build" = [ "nfa-thompson" "dfa-search" ];
+          "dfa-onepass" = [ "nfa-thompson" ];
+          "hybrid" = [ "alloc" "nfa-thompson" ];
+          "internal-instrument" = [ "internal-instrument-pikevm" ];
+          "internal-instrument-pikevm" = [ "logging" "std" ];
+          "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ];
+          "meta" = [ "syntax" "nfa-pikevm" ];
+          "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ];
+          "nfa-backtrack" = [ "nfa-thompson" ];
+          "nfa-pikevm" = [ "nfa-thompson" ];
+          "nfa-thompson" = [ "alloc" ];
+          "perf" = [ "perf-inline" "perf-literal" ];
+          "perf-literal" = [ "perf-literal-substring" "perf-literal-multisubstring" ];
+          "perf-literal-multisubstring" = [ "std" "dep:aho-corasick" ];
+          "perf-literal-substring" = [ "aho-corasick?/perf-literal" "dep:memchr" ];
+          "std" = [ "regex-syntax?/std" "memchr?/std" "aho-corasick?/std" "alloc" ];
+          "syntax" = [ "dep:regex-syntax" "alloc" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" "regex-syntax?/unicode" ];
+          "unicode-age" = [ "regex-syntax?/unicode-age" ];
+          "unicode-bool" = [ "regex-syntax?/unicode-bool" ];
+          "unicode-case" = [ "regex-syntax?/unicode-case" ];
+          "unicode-gencat" = [ "regex-syntax?/unicode-gencat" ];
+          "unicode-perl" = [ "regex-syntax?/unicode-perl" ];
+          "unicode-script" = [ "regex-syntax?/unicode-script" ];
+          "unicode-segment" = [ "regex-syntax?/unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "dfa-onepass" "dfa-search" "hybrid" "meta" "nfa-backtrack" "nfa-pikevm" "nfa-thompson" "perf-inline" "perf-literal" "perf-literal-multisubstring" "perf-literal-substring" "std" "syntax" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" ];
+      };
+      "regex-syntax" = rec {
+        crateName = "regex-syntax";
+        version = "0.8.2";
+        edition = "2021";
+        sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" "unicode" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "ring 0.16.20" = rec {
+        crateName = "ring";
+        version = "0.16.20";
+        edition = "2018";
+        links = "ring-asm";
+        sha256 = "1z682xp7v38ayq9g9nkbhhfpj6ygralmlx7wdmsfv8rnw99cylrh";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("android" == target."os" or null) || ("linux" == target."os" or null));
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (("android" == target."os" or null) || ("linux" == target."os" or null));
+            features = [ "std" ];
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("dragonfly" == target."os" or null) || ("freebsd" == target."os" or null) || ("illumos" == target."os" or null) || ("netbsd" == target."os" or null) || ("openbsd" == target."os" or null) || ("solaris" == target."os" or null));
+            features = [ "std" ];
+          }
+          {
+            name = "spin";
+            packageId = "spin 0.5.2";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("x86" == target."arch" or null) || ("x86_64" == target."arch" or null) || ((("aarch64" == target."arch" or null) || ("arm" == target."arch" or null)) && (("android" == target."os" or null) || ("fuchsia" == target."os" or null) || ("linux" == target."os" or null))));
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted 0.7.1";
+          }
+          {
+            name = "web-sys";
+            packageId = "web-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("wasm32" == target."arch" or null) && ("unknown" == target."vendor" or null) && ("unknown" == target."os" or null) && ("" == target."env" or null));
+            features = [ "Crypto" "Window" ];
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("windows" == target."os" or null);
+            features = [ "ntsecapi" "wtypesbase" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((target."unix" or false) || (target."windows" or false));
+          }
+        ];
+        features = {
+          "default" = [ "alloc" "dev_urandom_fallback" ];
+          "dev_urandom_fallback" = [ "once_cell" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "dev_urandom_fallback" "once_cell" ];
+      };
+      "ring 0.17.5" = rec {
+        crateName = "ring";
+        version = "0.17.5";
+        edition = "2021";
+        links = "ring_core_0_17_5";
+        sha256 = "02sd768l7594rm3jw048z7kkml7zcyw4ir62p6cxirap8wq0a0pv";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("android" == target."os" or null) || ("linux" == target."os" or null));
+          }
+          {
+            name = "spin";
+            packageId = "spin 0.9.8";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("x86" == target."arch" or null) || ("x86_64" == target."arch" or null) || ((("aarch64" == target."arch" or null) || ("arm" == target."arch" or null)) && (("android" == target."os" or null) || ("fuchsia" == target."os" or null) || ("linux" == target."os" or null) || ("windows" == target."os" or null))));
+            features = [ "once" ];
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted 0.9.0";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("windows" == target."os" or null));
+            features = [ "Win32_Foundation" "Win32_System_Threading" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((target."unix" or false) || (target."windows" or false) || ("wasi" == target."os" or null));
+          }
+        ];
+        features = {
+          "default" = [ "alloc" "dev_urandom_fallback" ];
+          "std" = [ "alloc" ];
+          "wasm32_unknown_unknown_js" = [ "getrandom/js" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "dev_urandom_fallback" ];
+      };
+      "rusoto_core" = rec {
+        crateName = "rusoto_core";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "18ig9x4n68cgfvhzkyhl9w2qlhk945xczbb9c8r52dd79ss0vcqx";
+        authors = [
+          "Anthony DiMarco <ocramida@gmail.com>"
+          "Jimmy Cuadra <jimmy@jimmycuadra.com>"
+          "Matthew Mayer <matthewkmayer@gmail.com>"
+          "Nikita Pekin <contact@nikitapek.in>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "base64";
+            packageId = "base64 0.13.1";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "crc32fast";
+            packageId = "crc32fast";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "client" "http1" "http2" "tcp" ];
+          }
+          {
+            name = "hyper-rustls";
+            packageId = "hyper-rustls";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "http1" "http2" "tls12" "logging" ];
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "rusoto_credential";
+            packageId = "rusoto_credential";
+          }
+          {
+            name = "rusoto_signature";
+            packageId = "rusoto_signature";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "time" "io-util" ];
+          }
+          {
+            name = "xml-rs";
+            packageId = "xml-rs";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" ];
+          }
+        ];
+        features = {
+          "default" = [ "native-tls" ];
+          "encoding" = [ "flate2" ];
+          "flate2" = [ "dep:flate2" ];
+          "hyper-rustls" = [ "dep:hyper-rustls" ];
+          "hyper-tls" = [ "dep:hyper-tls" ];
+          "native-tls" = [ "hyper-tls" ];
+          "nightly-testing" = [ "rusoto_credential/nightly-testing" ];
+          "rustls" = [ "hyper-rustls/native-tokio" ];
+          "rustls-webpki" = [ "hyper-rustls/webpki-tokio" ];
+        };
+        resolvedDefaultFeatures = [ "hyper-rustls" "rustls" ];
+      };
+      "rusoto_credential" = rec {
+        crateName = "rusoto_credential";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "019dq3aq6hnfg4xvxdfsnrba08dwvciz0km4nr3n1basvc9nq2pf";
+        authors = [
+          "Anthony DiMarco <ocramida@gmail.com>"
+          "Jimmy Cuadra <jimmy@jimmycuadra.com>"
+          "Matthew Mayer <matthewkmayer@gmail.com>"
+          "Nikita Pekin <contact@nikitapek.in>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "clock" "serde" ];
+          }
+          {
+            name = "dirs-next";
+            packageId = "dirs-next";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "client" "http1" "tcp" "stream" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "shlex";
+            packageId = "shlex";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "process" "sync" "time" ];
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "rt-multi-thread" ];
+          }
+        ];
+        features = { };
+      };
+      "rusoto_s3" = rec {
+        crateName = "rusoto_s3";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "0kdiqljcq1wg26mp0vnn2wwjj0slxya63mhjnjqgc49l31vldbks";
+        authors = [
+          "Anthony DiMarco <ocramida@gmail.com>"
+          "Jimmy Cuadra <jimmy@jimmycuadra.com>"
+          "Matthew Mayer <matthewkmayer@gmail.com>"
+          "Nikita Pekin <contact@nikitapek.in>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "rusoto_core";
+            packageId = "rusoto_core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "xml-rs";
+            packageId = "xml-rs";
+          }
+        ];
+        features = {
+          "default" = [ "native-tls" ];
+          "deserialize_structs" = [ "bytes/serde" "serde" "serde_derive" ];
+          "native-tls" = [ "rusoto_core/native-tls" ];
+          "rustls" = [ "rusoto_core/rustls" ];
+          "serde" = [ "dep:serde" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+          "serialize_structs" = [ "bytes/serde" "serde" "serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "rustls" ];
+      };
+      "rusoto_signature" = rec {
+        crateName = "rusoto_signature";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "0wjjn3n3a01xxc1kdwqkrbw6zkgc4w8ia6r93s9lfj4b3i4rbbm5";
+        authors = [
+          "Anthony DiMarco <ocramida@gmail.com>"
+          "Jimmy Cuadra <jimmy@jimmycuadra.com>"
+          "Matthew Mayer <matthewkmayer@gmail.com>"
+          "Nikita Pekin <contact@nikitapek.in>"
+        ];
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64 0.13.1";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "clock" ];
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.9.0";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "hex";
+            packageId = "hex";
+          }
+          {
+            name = "hmac";
+            packageId = "hmac";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "stream" ];
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "md-5";
+            packageId = "md-5";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "rusoto_credential";
+            packageId = "rusoto_credential";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2 0.9.9";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "rt-multi-thread" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" ];
+          }
+        ];
+
+      };
+      "rustc-demangle" = rec {
+        crateName = "rustc-demangle";
+        version = "0.1.23";
+        edition = "2015";
+        sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "rustc_version" = rec {
+        crateName = "rustc_version";
+        version = "0.4.0";
+        edition = "2018";
+        sha256 = "0rpk9rcdk405xhbmgclsh4pai0svn49x35aggl4nhbkd4a2zb85z";
+        authors = [
+          "Dirkjan Ochtman <dirkjan@ochtman.nl>"
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "semver";
+            packageId = "semver";
+          }
+        ];
+
+      };
+      "rustix" = rec {
+        crateName = "rustix";
+        version = "0.38.24";
+        edition = "2021";
+        sha256 = "0d72f5q2csk5mff87jrzlfgpxv44c2f8s0m183f9r920qgb83ncs";
+        authors = [
+          "Dan Gohman <dev@sunfishcode.online>"
+          "Jakub Konka <kubkon@jakubkonka.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."windows" or false)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+            features = [ "extra_traits" ];
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."windows" or false)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+            features = [ "extra_traits" ];
+          }
+          {
+            name = "linux-raw-sys";
+            packageId = "linux-raw-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((("android" == target."os" or null) || ("linux" == target."os" or null)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+            features = [ "general" "ioctl" "no_std" ];
+          }
+          {
+            name = "linux-raw-sys";
+            packageId = "linux-raw-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+            features = [ "general" "errno" "ioctl" "no_std" "elf" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_NetworkManagement_IpHelper" "Win32_System_Threading" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "all-apis" = [ "event" "fs" "io_uring" "mm" "mount" "net" "param" "pipe" "process" "procfs" "pty" "rand" "runtime" "shm" "stdio" "system" "termios" "thread" "time" ];
+          "default" = [ "std" "use-libc-auxv" ];
+          "io_uring" = [ "event" "fs" "net" "linux-raw-sys/io_uring" ];
+          "itoa" = [ "dep:itoa" ];
+          "libc" = [ "dep:libc" ];
+          "libc_errno" = [ "dep:libc_errno" ];
+          "linux_latest" = [ "linux_4_11" ];
+          "net" = [ "linux-raw-sys/net" "linux-raw-sys/netlink" "linux-raw-sys/if_ether" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "param" = [ "fs" ];
+          "process" = [ "linux-raw-sys/prctl" ];
+          "procfs" = [ "once_cell" "itoa" "fs" ];
+          "pty" = [ "itoa" "fs" ];
+          "runtime" = [ "linux-raw-sys/prctl" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" "linux-raw-sys/rustc-dep-of-std" "bitflags/rustc-dep-of-std" "compiler_builtins?/rustc-dep-of-std" ];
+          "shm" = [ "fs" ];
+          "std" = [ "bitflags/std" "alloc" "libc?/std" "libc_errno?/std" ];
+          "system" = [ "linux-raw-sys/system" ];
+          "thread" = [ "linux-raw-sys/prctl" ];
+          "use-libc" = [ "libc_errno" "libc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fs" "std" "use-libc-auxv" ];
+      };
+      "rustls" = rec {
+        crateName = "rustls";
+        version = "0.20.9";
+        edition = "2018";
+        sha256 = "16byazb8jfr06kgbijy92bdk0ila806g6a00a6l9x64mqpgf700v";
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "ring";
+            packageId = "ring 0.16.20";
+          }
+          {
+            name = "sct";
+            packageId = "sct";
+          }
+          {
+            name = "webpki";
+            packageId = "webpki";
+            features = [ "alloc" "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "default" = [ "logging" "tls12" ];
+          "log" = [ "dep:log" ];
+          "logging" = [ "log" ];
+          "read_buf" = [ "rustversion" ];
+          "rustversion" = [ "dep:rustversion" ];
+        };
+        resolvedDefaultFeatures = [ "log" "logging" "tls12" ];
+      };
+      "rustls-native-certs" = rec {
+        crateName = "rustls-native-certs";
+        version = "0.6.3";
+        edition = "2021";
+        sha256 = "007zind70rd5rfsrkdcfm8vn09j8sg02phg9334kark6rdscxam9";
+        dependencies = [
+          {
+            name = "openssl-probe";
+            packageId = "openssl-probe";
+            target = { target, features }: ((target."unix" or false) && (!("macos" == target."os" or null)));
+          }
+          {
+            name = "rustls-pemfile";
+            packageId = "rustls-pemfile";
+          }
+          {
+            name = "schannel";
+            packageId = "schannel";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "security-framework";
+            packageId = "security-framework";
+            target = { target, features }: ("macos" == target."os" or null);
+          }
+        ];
+
+      };
+      "rustls-pemfile" = rec {
+        crateName = "rustls-pemfile";
+        version = "1.0.4";
+        edition = "2018";
+        sha256 = "1324n5bcns0rnw6vywr5agff3rwfvzphi7rmbyzwnv6glkhclx0w";
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64 0.21.5";
+          }
+        ];
+
+      };
+      "rustversion" = rec {
+        crateName = "rustversion";
+        version = "1.0.14";
+        edition = "2018";
+        sha256 = "1x1pz1yynk5xzzrazk2svmidj69jhz89dz5vrc28sixl20x1iz3z";
+        procMacro = true;
+        build = "build/build.rs";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "ryu" = rec {
+        crateName = "ryu";
+        version = "1.0.15";
+        edition = "2018";
+        sha256 = "0hfphpn1xnpzxwj8qg916ga1lyc33lc03lnf1gb3wwpglj6wrm0s";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "schannel" = rec {
+        crateName = "schannel";
+        version = "0.1.22";
+        edition = "2018";
+        sha256 = "126zy5jb95fc5hvzyjwiq6lc81r08rdcn6affn00ispp9jzk6dqc";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Steffen Butzer <steffen.butzer@outlook.com>"
+        ];
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            features = [ "Win32_Foundation" "Win32_Security_Cryptography" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_System_Memory" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            features = [ "Win32_System_SystemInformation" "Win32_System_Time" ];
+          }
+        ];
+
+      };
+      "scopeguard" = rec {
+        crateName = "scopeguard";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "0jcz9sd47zlsgcnm1hdw0664krxwb5gczlif4qngj2aif8vky54l";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+        };
+      };
+      "sct" = rec {
+        crateName = "sct";
+        version = "0.7.1";
+        edition = "2021";
+        sha256 = "056lmi2xkzdg1dbai6ha3n57s18cbip4pnmpdhyljli3m99n216s";
+        authors = [
+          "Joseph Birr-Pixton <jpixton@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ring";
+            packageId = "ring 0.17.5";
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted 0.9.0";
+          }
+        ];
+
+      };
+      "security-framework" = rec {
+        crateName = "security-framework";
+        version = "2.9.2";
+        edition = "2021";
+        sha256 = "1pplxk15s5yxvi2m1sz5xfmjibp96cscdcl432w9jzbk0frlzdh5";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Kornel <kornel@geekhood.net>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "core-foundation";
+            packageId = "core-foundation";
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "security-framework-sys";
+            packageId = "security-framework-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "OSX_10_10" = [ "OSX_10_9" "security-framework-sys/OSX_10_10" ];
+          "OSX_10_11" = [ "OSX_10_10" "security-framework-sys/OSX_10_11" ];
+          "OSX_10_12" = [ "OSX_10_11" "security-framework-sys/OSX_10_12" ];
+          "OSX_10_13" = [ "OSX_10_12" "security-framework-sys/OSX_10_13" "alpn" "session-tickets" "serial-number-bigint" ];
+          "OSX_10_14" = [ "OSX_10_13" "security-framework-sys/OSX_10_14" ];
+          "OSX_10_15" = [ "OSX_10_14" "security-framework-sys/OSX_10_15" ];
+          "OSX_10_9" = [ "security-framework-sys/OSX_10_9" ];
+          "default" = [ "OSX_10_9" ];
+          "log" = [ "dep:log" ];
+          "serial-number-bigint" = [ "dep:num-bigint" ];
+        };
+        resolvedDefaultFeatures = [ "OSX_10_9" "default" ];
+      };
+      "security-framework-sys" = rec {
+        crateName = "security-framework-sys";
+        version = "2.9.1";
+        edition = "2021";
+        sha256 = "0yhciwlsy9dh0ps1gw3197kvyqx1bvc4knrhiznhid6kax196cp9";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Kornel <kornel@geekhood.net>"
+        ];
+        dependencies = [
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "OSX_10_10" = [ "OSX_10_9" ];
+          "OSX_10_11" = [ "OSX_10_10" ];
+          "OSX_10_12" = [ "OSX_10_11" ];
+          "OSX_10_13" = [ "OSX_10_12" ];
+          "OSX_10_14" = [ "OSX_10_13" ];
+          "OSX_10_15" = [ "OSX_10_14" ];
+          "default" = [ "OSX_10_9" ];
+        };
+        resolvedDefaultFeatures = [ "OSX_10_9" ];
+      };
+      "semver" = rec {
+        crateName = "semver";
+        version = "1.0.20";
+        edition = "2018";
+        sha256 = "140hmbfa743hbmah1zjf07s8apavhvn04204qjigjiz5w6iscvw3";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "seq-macro" = rec {
+        crateName = "seq-macro";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "1d50kbaslrrd0374ivx15jg57f03y5xzil1wd2ajlvajzlkbzw53";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "serde" = rec {
+        crateName = "serde";
+        version = "1.0.192";
+        edition = "2018";
+        sha256 = "00ghhaabyrnr2cn504lckyqzh3fwr8k7pxnhhardr1djhj2a18mw";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            optional = true;
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "derive" "serde_derive" "std" ];
+      };
+      "serde_derive" = rec {
+        crateName = "serde_derive";
+        version = "1.0.192";
+        edition = "2015";
+        sha256 = "1hgvm47ffd748sx22z1da7mgcfjmpr60gqzkff0a9yn9przj1iyn";
+        procMacro = true;
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "serde_json" = rec {
+        crateName = "serde_json";
+        version = "1.0.108";
+        edition = "2021";
+        sha256 = "0ssj59s7lpzqh1m50kfzlnrip0p0jg9lmhn4098i33a0mhz7w71x";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" ];
+          "default" = [ "std" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "preserve_order" = [ "indexmap" "std" ];
+          "std" = [ "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sha2 0.10.8" = rec {
+        crateName = "sha2";
+        version = "0.10.8";
+        edition = "2018";
+        sha256 = "1j1x78zk9il95w9iv46dh9wm73r6xrgj32y6lzzw7bxws9dbfgbr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.10.7";
+          }
+          {
+            name = "sha2-asm";
+            packageId = "sha2-asm";
+            optional = true;
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null));
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest 0.10.7";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha2-asm" ];
+          "asm-aarch64" = [ "asm" ];
+          "default" = [ "std" ];
+          "oid" = [ "digest/oid" ];
+          "sha2-asm" = [ "dep:sha2-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "asm" "default" "sha2-asm" "std" ];
+      };
+      "sha2 0.9.9" = rec {
+        crateName = "sha2";
+        version = "0.9.9";
+        edition = "2018";
+        sha256 = "006q2f0ar26xcjxqz8zsncfgz86zqa5dkwlwv03rhx1rpzhs2n2d";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer 0.9.0";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.9.0";
+          }
+          {
+            name = "opaque-debug";
+            packageId = "opaque-debug";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest 0.9.0";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha2-asm" ];
+          "asm-aarch64" = [ "asm" ];
+          "default" = [ "std" ];
+          "sha2-asm" = [ "dep:sha2-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sha2-asm" = rec {
+        crateName = "sha2-asm";
+        version = "0.6.3";
+        edition = "2018";
+        sha256 = "0kp480744vkwg3fqx98379nsdw1lzzzimd88v0qgpqqic03afyzj";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "shlex" = rec {
+        crateName = "shlex";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "1033pj9dyb76nm5yv597nnvj3zpvr2aw9rm5wy0gah3dk99f1km7";
+        authors = [
+          "comex <comexk@gmail.com>"
+          "Fenhl <fenhl@fenhl.net>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "signal-hook-registry" = rec {
+        crateName = "signal-hook-registry";
+        version = "1.4.1";
+        edition = "2015";
+        sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        authors = [
+          "Michal 'vorner' Vaner <vorner@vorner.cz>"
+          "Masaki Hara <ackie.h.gmai@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "signature" = rec {
+        crateName = "signature";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "1pi9hd5vqfr3q3k49k37z06p7gs5si0in32qia4mmr1dancr6m3p";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "derive" = [ "dep:derive" ];
+          "digest" = [ "dep:digest" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "std" = [ "alloc" "rand_core?/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "simdutf8" = rec {
+        crateName = "simdutf8";
+        version = "0.1.4";
+        edition = "2018";
+        sha256 = "0fi6zvnldaw7g726wnm9vvpv4s89s5jsk7fgp3rg2l99amw64zzj";
+        authors = [
+          "Hans Kratz <hans@appfour.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "slab" = rec {
+        crateName = "slab";
+        version = "0.4.9";
+        edition = "2018";
+        sha256 = "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sled" = rec {
+        crateName = "sled";
+        version = "0.34.7";
+        edition = "2018";
+        sha256 = "0dcr2s7cylj5mb33ci3kpx7fz797jwvysnl5airrir9cgirv95kz";
+        authors = [
+          "Tyler Neely <t@jujit.su>"
+        ];
+        dependencies = [
+          {
+            name = "crc32fast";
+            packageId = "crc32fast";
+          }
+          {
+            name = "crossbeam-epoch";
+            packageId = "crossbeam-epoch";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+          {
+            name = "fs2";
+            packageId = "fs2";
+            target = { target, features }: (("linux" == target."os" or null) || ("macos" == target."os" or null) || ("windows" == target."os" or null));
+          }
+          {
+            name = "fxhash";
+            packageId = "fxhash";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot 0.11.2";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "color-backtrace" = [ "dep:color-backtrace" ];
+          "compression" = [ "zstd" ];
+          "default" = [ "no_metrics" ];
+          "io_uring" = [ "rio" ];
+          "no_logs" = [ "log/max_level_off" ];
+          "pretty_backtrace" = [ "color-backtrace" ];
+          "rio" = [ "dep:rio" ];
+          "testing" = [ "event_log" "lock_free_delays" "compression" "failpoints" "backtrace" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "default" "no_metrics" ];
+      };
+      "smallvec" = rec {
+        crateName = "smallvec";
+        version = "1.11.2";
+        edition = "2018";
+        sha256 = "0w79x38f7c0np7hqfmzrif9zmn0avjvvm31b166zdk9d1aad1k2d";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "const_new" = [ "const_generics" ];
+          "drain_keep_rest" = [ "drain_filter" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "smartstring" = rec {
+        crateName = "smartstring";
+        version = "1.0.1";
+        edition = "2021";
+        sha256 = "0agf4x0jz79r30aqibyfjm1h9hrjdh0harcqcvb2vapv7rijrdrz";
+        authors = [
+          "Bodil Stokke <bodil@bodil.org>"
+        ];
+        dependencies = [
+          {
+            name = "static_assertions";
+            packageId = "static_assertions";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" ];
+          "proptest" = [ "dep:proptest" ];
+          "serde" = [ "dep:serde" ];
+          "test" = [ "std" "arbitrary" "arbitrary/derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "snap" = rec {
+        crateName = "snap";
+        version = "1.1.0";
+        edition = "2018";
+        sha256 = "0c882cs4wbyi34nw8njpxa729gyi6sj71h8rj4ykbdvyxyv0m7sy";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+
+      };
+      "socket2 0.4.10" = rec {
+        crateName = "socket2";
+        version = "0.4.10";
+        edition = "2018";
+        sha256 = "03ack54dxhgfifzsj14k7qa3r5c9wqy3v6mqhlim99cc03y1cycz";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "handleapi" "ws2ipdef" "ws2tcpip" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "all" ];
+      };
+      "socket2 0.5.5" = rec {
+        crateName = "socket2";
+        version = "0.5.5";
+        edition = "2021";
+        sha256 = "1sgq315f1njky114ip7wcy83qlphv9qclprfjwvxcpfblmcsqpvv";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "all" ];
+      };
+      "spin 0.5.2" = rec {
+        crateName = "spin";
+        version = "0.5.2";
+        edition = "2015";
+        sha256 = "0b84m6dbzrwf2kxylnw82d3dr8w06av7rfkr8s85fb5f43rwyqvf";
+        authors = [
+          "Mathijs van de Nes <git@mathijs.vd-nes.nl>"
+          "John Ericson <git@JohnEricson.me>"
+        ];
+
+      };
+      "spin 0.9.8" = rec {
+        crateName = "spin";
+        version = "0.9.8";
+        edition = "2015";
+        sha256 = "0rvam5r0p3a6qhc18scqpvpgb3ckzyqxpgdfyjnghh8ja7byi039";
+        authors = [
+          "Mathijs van de Nes <git@mathijs.vd-nes.nl>"
+          "John Ericson <git@JohnEricson.me>"
+          "Joshua Barretto <joshua.s.barretto@gmail.com>"
+        ];
+        features = {
+          "barrier" = [ "mutex" ];
+          "default" = [ "lock_api" "mutex" "spin_mutex" "rwlock" "once" "lazy" "barrier" ];
+          "fair_mutex" = [ "mutex" ];
+          "lazy" = [ "once" ];
+          "lock_api" = [ "lock_api_crate" ];
+          "lock_api_crate" = [ "dep:lock_api_crate" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "portable_atomic" = [ "portable-atomic" ];
+          "spin_mutex" = [ "mutex" ];
+          "ticket_mutex" = [ "mutex" ];
+          "use_ticket_mutex" = [ "mutex" "ticket_mutex" ];
+        };
+        resolvedDefaultFeatures = [ "once" ];
+      };
+      "spki" = rec {
+        crateName = "spki";
+        version = "0.7.2";
+        edition = "2021";
+        sha256 = "0jhq00sv4w3psdi6li3vjjmspc6z2d9b1wc1srbljircy1p9j7lx";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "base64ct";
+            packageId = "base64ct";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "base64ct?/alloc" "der/alloc" ];
+          "arbitrary" = [ "std" "dep:arbitrary" "der/arbitrary" ];
+          "base64" = [ "dep:base64ct" ];
+          "fingerprint" = [ "sha2" ];
+          "pem" = [ "alloc" "der/pem" ];
+          "sha2" = [ "dep:sha2" ];
+          "std" = [ "der/std" "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "sqlparser" = rec {
+        crateName = "sqlparser";
+        version = "0.39.0";
+        edition = "2021";
+        sha256 = "1mrbqjdqr179qnhy43d0dnrl3yipsp4qyji5rc68j4fyrg14sfvl";
+        authors = [
+          "Andy Grove <andygrove73@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "bigdecimal" = [ "dep:bigdecimal" ];
+          "default" = [ "std" ];
+          "json_example" = [ "serde_json" "serde" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "sqlparser_derive" = [ "dep:sqlparser_derive" ];
+          "visitor" = [ "sqlparser_derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "static_assertions" = rec {
+        crateName = "static_assertions";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "0gsl6xmw10gvn3zs1rv99laj5ig7ylffnh71f9l34js4nr4r7sx2";
+        authors = [
+          "Nikolai Vazquez"
+        ];
+        features = { };
+      };
+      "streaming-decompression" = rec {
+        crateName = "streaming-decompression";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "1wscqj3s30qknda778wf7z99mknk65p0h9hhs658l4pvkfqw6v5z";
+        dependencies = [
+          {
+            name = "fallible-streaming-iterator";
+            packageId = "fallible-streaming-iterator";
+          }
+        ];
+
+      };
+      "streaming-iterator" = rec {
+        crateName = "streaming-iterator";
+        version = "0.1.9";
+        edition = "2021";
+        sha256 = "0845zdv8qb7zwqzglpqc0830i43xh3fb6vqms155wz85qfvk28ib";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+      };
+      "strength_reduce" = rec {
+        crateName = "strength_reduce";
+        version = "0.2.4";
+        edition = "2015";
+        sha256 = "10jdq9dijjdkb20wg1dmwg447rnj37jbq0mwvbadvqi2gys5x2gy";
+        authors = [
+          "Elliott Mahler <join.together@gmail.com>"
+        ];
+
+      };
+      "strsim" = rec {
+        crateName = "strsim";
+        version = "0.10.0";
+        edition = "2015";
+        sha256 = "08s69r4rcrahwnickvi0kq49z524ci50capybln83mg6b473qivk";
+        authors = [
+          "Danny Guo <danny@dannyguo.com>"
+        ];
+
+      };
+      "strum_macros" = rec {
+        crateName = "strum_macros";
+        version = "0.25.3";
+        edition = "2018";
+        sha256 = "184y62g474zqb2f7n16x3ghvlyjbh50viw32p9w9l5lwmjlizp13";
+        procMacro = true;
+        authors = [
+          "Peter Glotfelty <peter.glotfelty@microsoft.com>"
+        ];
+        dependencies = [
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "parsing" "extra-traits" ];
+          }
+        ];
+
+      };
+      "subtle" = rec {
+        crateName = "subtle";
+        version = "2.4.1";
+        edition = "2015";
+        sha256 = "00b6jzh9gzb0h9n25g06nqr90z3xzqppfhhb260s1hjhh4pg7pkb";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        features = {
+          "default" = [ "std" "i128" ];
+        };
+      };
+      "syn 1.0.109" = rec {
+        crateName = "syn";
+        version = "1.0.109";
+        edition = "2018";
+        sha256 = "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit-mut" ];
+      };
+      "syn 2.0.39" = rec {
+        crateName = "syn";
+        version = "2.0.39";
+        edition = "2021";
+        sha256 = "0ymyhxnk1yi4pzf72qk3lrdm9lgjwcrcwci0hhz5vx7wya88prr3";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit" "visit-mut" ];
+      };
+      "sysinfo" = rec {
+        crateName = "sysinfo";
+        version = "0.29.10";
+        edition = "2018";
+        sha256 = "19cbs7d7fcq8cpfpr94n68h04d02lab8xg76j6la7b90shad260a";
+        authors = [
+          "Guillaume Gomez <guillaume1.gomez@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(("unknown" == target."os" or null) || ("wasm32" == target."arch" or null)));
+          }
+          {
+            name = "ntapi";
+            packageId = "ntapi";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            target = { target, features }: ((target."windows" or false) || ("linux" == target."os" or null) || ("android" == target."os" or null));
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "errhandlingapi" "fileapi" "handleapi" "heapapi" "ifdef" "ioapiset" "minwindef" "pdh" "psapi" "synchapi" "sysinfoapi" "winbase" "winerror" "winioctl" "winnt" "oleauto" "wbemcli" "rpcdce" "combaseapi" "objidl" "powerbase" "netioapi" "lmcons" "lmaccess" "lmapibuf" "memoryapi" "ntlsa" "securitybaseapi" "shellapi" "std" "iphlpapi" "winsock2" "sddl" ];
+          }
+        ];
+        features = {
+          "apple-app-store" = [ "apple-sandbox" ];
+          "debug" = [ "libc/extra_traits" ];
+          "default" = [ "multithread" ];
+          "multithread" = [ "rayon" ];
+          "rayon" = [ "dep:rayon" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "target-features" = rec {
+        crateName = "target-features";
+        version = "0.1.5";
+        edition = "2021";
+        sha256 = "1gb974chm9aj8ifkyibylxkyb5an4bf5y8dxb18pqmck698gmdfg";
+        authors = [
+          "Caleb Zulawski <caleb.zulawski@gmail.com>"
+        ];
+
+      };
+      "tempfile" = rec {
+        crateName = "tempfile";
+        version = "3.8.1";
+        edition = "2018";
+        sha256 = "1r88v07zdafzf46y63vs39rmzwl4vqd4g2c5qarz9mqa8nnavwby";
+        authors = [
+          "Steven Allen <steven@stebalien.com>"
+          "The Rust Project Developers"
+          "Ashley Mannix <ashleymannix@live.com.au>"
+          "Jason White <me@jasonwhite.io>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "fastrand";
+            packageId = "fastrand";
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall 0.4.1";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            target = { target, features }: ((target."unix" or false) || ("wasi" == target."os" or null));
+            features = [ "fs" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Storage_FileSystem" "Win32_Foundation" ];
+          }
+        ];
+        features = { };
+      };
+      "thiserror" = rec {
+        crateName = "thiserror";
+        version = "1.0.50";
+        edition = "2021";
+        sha256 = "1ll2sfbrxks8jja161zh1pgm3yssr7aawdmaa2xmcwcsbh7j39zr";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "thiserror-impl";
+            packageId = "thiserror-impl";
+          }
+        ];
+
+      };
+      "thiserror-impl" = rec {
+        crateName = "thiserror-impl";
+        version = "1.0.50";
+        edition = "2021";
+        sha256 = "1f0lmam4765sfnwr4b1n00y14vxh10g0311mkk0adr80pi02wsr6";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+          }
+        ];
+
+      };
+      "tokio" = rec {
+        crateName = "tokio";
+        version = "1.34.0";
+        edition = "2021";
+        sha256 = "1fgmssdga42a2hn9spm9dh1v9ajpcbs4r3svmzvk9s0iciv19h6h";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            target = { target, features }: (target."tokio_taskdump" or false);
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+            optional = true;
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot 0.12.1";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "signal-hook-registry";
+            packageId = "signal-hook-registry";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2 0.5.5";
+            optional = true;
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+            features = [ "all" ];
+          }
+          {
+            name = "tokio-macros";
+            packageId = "tokio-macros";
+            optional = true;
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2 0.5.5";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Security_Authorization" ];
+          }
+        ];
+        features = {
+          "bytes" = [ "dep:bytes" ];
+          "full" = [ "fs" "io-util" "io-std" "macros" "net" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "sync" "time" ];
+          "io-util" = [ "bytes" ];
+          "libc" = [ "dep:libc" ];
+          "macros" = [ "tokio-macros" ];
+          "mio" = [ "dep:mio" ];
+          "net" = [ "libc" "mio/os-poll" "mio/os-ext" "mio/net" "socket2" "windows-sys/Win32_Foundation" "windows-sys/Win32_Security" "windows-sys/Win32_Storage_FileSystem" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_System_SystemServices" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "process" = [ "bytes" "libc" "mio/os-poll" "mio/os-ext" "mio/net" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Threading" "windows-sys/Win32_System_WindowsProgramming" ];
+          "rt-multi-thread" = [ "num_cpus" "rt" ];
+          "signal" = [ "libc" "mio/os-poll" "mio/net" "mio/os-ext" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Console" ];
+          "signal-hook-registry" = [ "dep:signal-hook-registry" ];
+          "socket2" = [ "dep:socket2" ];
+          "test-util" = [ "rt" "sync" "time" ];
+          "tokio-macros" = [ "dep:tokio-macros" ];
+          "tracing" = [ "dep:tracing" ];
+          "windows-sys" = [ "dep:windows-sys" ];
+        };
+        resolvedDefaultFeatures = [ "bytes" "default" "fs" "full" "io-std" "io-util" "libc" "macros" "mio" "net" "num_cpus" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "time" "tokio-macros" "windows-sys" ];
+      };
+      "tokio-macros" = rec {
+        crateName = "tokio-macros";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-rustls" = rec {
+        crateName = "tokio-rustls";
+        version = "0.23.4";
+        edition = "2018";
+        sha256 = "0nfsmmi8l1lgpbfy6079d5i13984djzcxrdr9jc06ghi0cwyhgn4";
+        authors = [
+          "quininer kel <quininer@live.com>"
+        ];
+        dependencies = [
+          {
+            name = "rustls";
+            packageId = "rustls";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "webpki";
+            packageId = "webpki";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "dangerous_configuration" = [ "rustls/dangerous_configuration" ];
+          "default" = [ "logging" "tls12" ];
+          "logging" = [ "rustls/logging" ];
+          "tls12" = [ "rustls/tls12" ];
+        };
+        resolvedDefaultFeatures = [ "logging" "tls12" ];
+      };
+      "tokio-util" = rec {
+        crateName = "tokio-util";
+        version = "0.7.10";
+        edition = "2021";
+        sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "__docs_rs" = [ "futures-util" ];
+          "codec" = [ "tracing" ];
+          "compat" = [ "futures-io" ];
+          "full" = [ "codec" "compat" "io-util" "time" "net" "rt" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hashbrown" = [ "dep:hashbrown" ];
+          "io-util" = [ "io" "tokio/rt" "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "rt" = [ "tokio/rt" "tokio/sync" "futures-util" "hashbrown" ];
+          "slab" = [ "dep:slab" ];
+          "time" = [ "tokio/time" "slab" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "codec" "default" "io" "io-util" "tracing" ];
+      };
+      "tower-service" = rec {
+        crateName = "tower-service";
+        version = "0.3.2";
+        edition = "2018";
+        sha256 = "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+
+      };
+      "tracing" = rec {
+        crateName = "tracing";
+        version = "0.1.40";
+        edition = "2018";
+        sha256 = "1vv48dac9zgj9650pg2b4d0j3w6f3x9gbggf43scq5hrlysklln3";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "attributes" = [ "tracing-attributes" ];
+          "default" = [ "std" "attributes" ];
+          "log" = [ "dep:log" ];
+          "log-always" = [ "log" ];
+          "std" = [ "tracing-core/std" ];
+          "tracing-attributes" = [ "dep:tracing-attributes" ];
+          "valuable" = [ "tracing-core/valuable" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "tracing-core" = rec {
+        crateName = "tracing-core";
+        version = "0.1.32";
+        edition = "2018";
+        sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "std" "valuable/std" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "std" = [ "once_cell" ];
+          "valuable" = [ "dep:valuable" ];
+        };
+        resolvedDefaultFeatures = [ "once_cell" "std" ];
+      };
+      "try-lock" = rec {
+        crateName = "try-lock";
+        version = "0.2.4";
+        edition = "2015";
+        sha256 = "1vc15paa4zi06ixsxihwbvfn24d708nsyg1ncgqwcrn42byyqa1m";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+
+      };
+      "typenum" = rec {
+        crateName = "typenum";
+        version = "1.17.0";
+        edition = "2018";
+        sha256 = "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2";
+        build = "build/main.rs";
+        authors = [
+          "Paho Lurie-Gregg <paho@paholg.com>"
+          "Andre Bogus <bogusandre@gmail.com>"
+        ];
+        features = {
+          "scale-info" = [ "dep:scale-info" ];
+          "scale_info" = [ "scale-info/derive" ];
+        };
+      };
+      "unicode-ident" = rec {
+        crateName = "unicode-ident";
+        version = "1.0.12";
+        edition = "2018";
+        sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "unicode-width" = rec {
+        crateName = "unicode-width";
+        version = "0.1.11";
+        edition = "2015";
+        sha256 = "11ds4ydhg8g7l06rlmh712q41qsrd0j0h00n1jm74kww3kqk65z5";
+        authors = [
+          "kwantam <kwantam@gmail.com>"
+          "Manish Goregaokar <manishsmail@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "std" "core" "compiler_builtins" ];
+          "std" = [ "dep:std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "untrusted 0.7.1" = rec {
+        crateName = "untrusted";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "0jkbqaj9d3v5a91pp3wp9mffvng1nhycx6sh4qkdd9qyr62ccmm1";
+        libPath = "src/untrusted.rs";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+
+      };
+      "untrusted 0.9.0" = rec {
+        crateName = "untrusted";
+        version = "0.9.0";
+        edition = "2018";
+        sha256 = "1ha7ib98vkc538x0z60gfn0fc5whqdd85mb87dvisdcaifi6vjwf";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+
+      };
+      "utf8parse" = rec {
+        crateName = "utf8parse";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "02ip1a0az0qmc2786vxk2nqwsgcwf17d3a38fkf0q7hrmwh9c6vi";
+        authors = [
+          "Joe Wilm <joe@jwilm.com>"
+          "Christian Duerr <contact@christianduerr.com>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "version_check" = rec {
+        crateName = "version_check";
+        version = "0.9.4";
+        edition = "2015";
+        sha256 = "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9";
+        authors = [
+          "Sergio Benitez <sb@sergio.bz>"
+        ];
+
+      };
+      "want" = rec {
+        crateName = "want";
+        version = "0.3.1";
+        edition = "2018";
+        sha256 = "03hbfrnvqqdchb5kgxyavb9jabwza0dmh2vw5kg0dq8rxl57d9xz";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "try-lock";
+            packageId = "try-lock";
+          }
+        ];
+
+      };
+      "wasi" = rec {
+        crateName = "wasi";
+        version = "0.11.0+wasi-snapshot-preview1";
+        edition = "2018";
+        sha256 = "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw";
+        authors = [
+          "The Cranelift Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "rustc-std-workspace-alloc" ];
+          "rustc-std-workspace-alloc" = [ "dep:rustc-std-workspace-alloc" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "wasm-bindgen" = rec {
+        crateName = "wasm-bindgen";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "1khgsh4z9bga35mjhg41dl7523i69ffc5m8ckhqaw6ssyabc5bkx";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "wasm-bindgen-macro";
+            packageId = "wasm-bindgen-macro";
+          }
+        ];
+        features = {
+          "default" = [ "spans" "std" ];
+          "enable-interning" = [ "std" ];
+          "gg-alloc" = [ "wasm-bindgen-test/gg-alloc" ];
+          "serde" = [ "dep:serde" ];
+          "serde-serialize" = [ "serde" "serde_json" "std" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "spans" = [ "wasm-bindgen-macro/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro/strict-macro" ];
+          "xxx_debug_only_print_generated_code" = [ "wasm-bindgen-macro/xxx_debug_only_print_generated_code" ];
+        };
+        resolvedDefaultFeatures = [ "default" "spans" "std" ];
+      };
+      "wasm-bindgen-backend" = rec {
+        crateName = "wasm-bindgen-backend";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "05zj8yl243rvs87rhicq2l1d6443lnm6k90khf744khf9ikg95z3";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "bumpalo";
+            packageId = "bumpalo";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro" = rec {
+        crateName = "wasm-bindgen-macro";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "1chn3wgw9awmvs0fpmazbqyc5rwfgy3pj7lzwczmzb887dxh2qar";
+        procMacro = true;
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "wasm-bindgen-macro-support";
+            packageId = "wasm-bindgen-macro-support";
+          }
+        ];
+        features = {
+          "spans" = [ "wasm-bindgen-macro-support/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro-support/strict-macro" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro-support" = rec {
+        crateName = "wasm-bindgen-macro-support";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "01rrzg3y1apqygsjz1jg0n7p831nm4kdyxmxyl85x7v6mf6kndf5";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "visit" "full" ];
+          }
+          {
+            name = "wasm-bindgen-backend";
+            packageId = "wasm-bindgen-backend";
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+          "spans" = [ "wasm-bindgen-backend/spans" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-shared" = rec {
+        crateName = "wasm-bindgen-shared";
+        version = "0.2.88";
+        edition = "2018";
+        links = "wasm_bindgen";
+        sha256 = "02vmw2rzsla1qm0zgfng4kqz52xn8k54v8ads4g1macv09fnq10d";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+
+      };
+      "web-sys" = rec {
+        crateName = "web-sys";
+        version = "0.3.65";
+        edition = "2018";
+        sha256 = "11ba406ca9qssc21c37v49sn2y2gsdn6c3nva4hjf8v3yv2rkd2x";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+        ];
+        features = {
+          "AbortSignal" = [ "EventTarget" ];
+          "AnalyserNode" = [ "AudioNode" "EventTarget" ];
+          "Animation" = [ "EventTarget" ];
+          "AnimationEvent" = [ "Event" ];
+          "AnimationPlaybackEvent" = [ "Event" ];
+          "Attr" = [ "EventTarget" "Node" ];
+          "AudioBufferSourceNode" = [ "AudioNode" "AudioScheduledSourceNode" "EventTarget" ];
+          "AudioContext" = [ "BaseAudioContext" "EventTarget" ];
+          "AudioDestinationNode" = [ "AudioNode" "EventTarget" ];
+          "AudioNode" = [ "EventTarget" ];
+          "AudioProcessingEvent" = [ "Event" ];
+          "AudioScheduledSourceNode" = [ "AudioNode" "EventTarget" ];
+          "AudioStreamTrack" = [ "EventTarget" "MediaStreamTrack" ];
+          "AudioTrackList" = [ "EventTarget" ];
+          "AudioWorklet" = [ "Worklet" ];
+          "AudioWorkletGlobalScope" = [ "WorkletGlobalScope" ];
+          "AudioWorkletNode" = [ "AudioNode" "EventTarget" ];
+          "AuthenticatorAssertionResponse" = [ "AuthenticatorResponse" ];
+          "AuthenticatorAttestationResponse" = [ "AuthenticatorResponse" ];
+          "BaseAudioContext" = [ "EventTarget" ];
+          "BatteryManager" = [ "EventTarget" ];
+          "BeforeUnloadEvent" = [ "Event" ];
+          "BiquadFilterNode" = [ "AudioNode" "EventTarget" ];
+          "BlobEvent" = [ "Event" ];
+          "Bluetooth" = [ "EventTarget" ];
+          "BluetoothAdvertisingEvent" = [ "Event" ];
+          "BluetoothDevice" = [ "EventTarget" ];
+          "BluetoothPermissionResult" = [ "EventTarget" "PermissionStatus" ];
+          "BluetoothRemoteGattCharacteristic" = [ "EventTarget" ];
+          "BluetoothRemoteGattService" = [ "EventTarget" ];
+          "BroadcastChannel" = [ "EventTarget" ];
+          "CanvasCaptureMediaStream" = [ "EventTarget" "MediaStream" ];
+          "CanvasCaptureMediaStreamTrack" = [ "EventTarget" "MediaStreamTrack" ];
+          "CdataSection" = [ "CharacterData" "EventTarget" "Node" "Text" ];
+          "ChannelMergerNode" = [ "AudioNode" "EventTarget" ];
+          "ChannelSplitterNode" = [ "AudioNode" "EventTarget" ];
+          "CharacterData" = [ "EventTarget" "Node" ];
+          "ChromeWorker" = [ "EventTarget" "Worker" ];
+          "Clipboard" = [ "EventTarget" ];
+          "ClipboardEvent" = [ "Event" ];
+          "CloseEvent" = [ "Event" ];
+          "Comment" = [ "CharacterData" "EventTarget" "Node" ];
+          "CompositionEvent" = [ "Event" "UiEvent" ];
+          "ConstantSourceNode" = [ "AudioNode" "AudioScheduledSourceNode" "EventTarget" ];
+          "ConvolverNode" = [ "AudioNode" "EventTarget" ];
+          "CssAnimation" = [ "Animation" "EventTarget" ];
+          "CssConditionRule" = [ "CssGroupingRule" "CssRule" ];
+          "CssCounterStyleRule" = [ "CssRule" ];
+          "CssFontFaceRule" = [ "CssRule" ];
+          "CssFontFeatureValuesRule" = [ "CssRule" ];
+          "CssGroupingRule" = [ "CssRule" ];
+          "CssImportRule" = [ "CssRule" ];
+          "CssKeyframeRule" = [ "CssRule" ];
+          "CssKeyframesRule" = [ "CssRule" ];
+          "CssMediaRule" = [ "CssConditionRule" "CssGroupingRule" "CssRule" ];
+          "CssNamespaceRule" = [ "CssRule" ];
+          "CssPageRule" = [ "CssRule" ];
+          "CssStyleRule" = [ "CssRule" ];
+          "CssStyleSheet" = [ "StyleSheet" ];
+          "CssSupportsRule" = [ "CssConditionRule" "CssGroupingRule" "CssRule" ];
+          "CssTransition" = [ "Animation" "EventTarget" ];
+          "CustomEvent" = [ "Event" ];
+          "DedicatedWorkerGlobalScope" = [ "EventTarget" "WorkerGlobalScope" ];
+          "DelayNode" = [ "AudioNode" "EventTarget" ];
+          "DeviceLightEvent" = [ "Event" ];
+          "DeviceMotionEvent" = [ "Event" ];
+          "DeviceOrientationEvent" = [ "Event" ];
+          "DeviceProximityEvent" = [ "Event" ];
+          "Document" = [ "EventTarget" "Node" ];
+          "DocumentFragment" = [ "EventTarget" "Node" ];
+          "DocumentTimeline" = [ "AnimationTimeline" ];
+          "DocumentType" = [ "EventTarget" "Node" ];
+          "DomMatrix" = [ "DomMatrixReadOnly" ];
+          "DomPoint" = [ "DomPointReadOnly" ];
+          "DomRect" = [ "DomRectReadOnly" ];
+          "DomRequest" = [ "EventTarget" ];
+          "DragEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "DynamicsCompressorNode" = [ "AudioNode" "EventTarget" ];
+          "Element" = [ "EventTarget" "Node" ];
+          "ErrorEvent" = [ "Event" ];
+          "EventSource" = [ "EventTarget" ];
+          "ExtendableEvent" = [ "Event" ];
+          "ExtendableMessageEvent" = [ "Event" "ExtendableEvent" ];
+          "FetchEvent" = [ "Event" "ExtendableEvent" ];
+          "FetchObserver" = [ "EventTarget" ];
+          "File" = [ "Blob" ];
+          "FileReader" = [ "EventTarget" ];
+          "FileSystemDirectoryEntry" = [ "FileSystemEntry" ];
+          "FileSystemDirectoryHandle" = [ "FileSystemHandle" ];
+          "FileSystemFileEntry" = [ "FileSystemEntry" ];
+          "FileSystemFileHandle" = [ "FileSystemHandle" ];
+          "FileSystemWritableFileStream" = [ "WritableStream" ];
+          "FocusEvent" = [ "Event" "UiEvent" ];
+          "FontFaceSet" = [ "EventTarget" ];
+          "FontFaceSetLoadEvent" = [ "Event" ];
+          "GainNode" = [ "AudioNode" "EventTarget" ];
+          "GamepadAxisMoveEvent" = [ "Event" "GamepadEvent" ];
+          "GamepadButtonEvent" = [ "Event" "GamepadEvent" ];
+          "GamepadEvent" = [ "Event" ];
+          "GpuDevice" = [ "EventTarget" ];
+          "GpuInternalError" = [ "GpuError" ];
+          "GpuOutOfMemoryError" = [ "GpuError" ];
+          "GpuPipelineError" = [ "DomException" ];
+          "GpuUncapturedErrorEvent" = [ "Event" ];
+          "GpuValidationError" = [ "GpuError" ];
+          "HashChangeEvent" = [ "Event" ];
+          "Hid" = [ "EventTarget" ];
+          "HidConnectionEvent" = [ "Event" ];
+          "HidDevice" = [ "EventTarget" ];
+          "HidInputReportEvent" = [ "Event" ];
+          "HtmlAnchorElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlAreaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlAudioElement" = [ "Element" "EventTarget" "HtmlElement" "HtmlMediaElement" "Node" ];
+          "HtmlBaseElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlBodyElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlBrElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlButtonElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlCanvasElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDataElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDataListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDetailsElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDialogElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDirectoryElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDivElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDocument" = [ "Document" "EventTarget" "Node" ];
+          "HtmlElement" = [ "Element" "EventTarget" "Node" ];
+          "HtmlEmbedElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFieldSetElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFontElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFormControlsCollection" = [ "HtmlCollection" ];
+          "HtmlFormElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFrameElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFrameSetElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHeadElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHeadingElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHrElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHtmlElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlIFrameElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlImageElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlInputElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLabelElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLegendElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLiElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLinkElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMapElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMediaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMenuElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMenuItemElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMetaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMeterElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlModElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlObjectElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOptGroupElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOptionElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOptionsCollection" = [ "HtmlCollection" ];
+          "HtmlOutputElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlParagraphElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlParamElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlPictureElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlPreElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlProgressElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlQuoteElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlScriptElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSelectElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSlotElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSourceElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSpanElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlStyleElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableCaptionElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableCellElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableColElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableRowElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableSectionElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTemplateElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTextAreaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTimeElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTitleElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTrackElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlUListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlUnknownElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlVideoElement" = [ "Element" "EventTarget" "HtmlElement" "HtmlMediaElement" "Node" ];
+          "IdbCursorWithValue" = [ "IdbCursor" ];
+          "IdbDatabase" = [ "EventTarget" ];
+          "IdbFileHandle" = [ "EventTarget" ];
+          "IdbFileRequest" = [ "DomRequest" "EventTarget" ];
+          "IdbLocaleAwareKeyRange" = [ "IdbKeyRange" ];
+          "IdbMutableFile" = [ "EventTarget" ];
+          "IdbOpenDbRequest" = [ "EventTarget" "IdbRequest" ];
+          "IdbRequest" = [ "EventTarget" ];
+          "IdbTransaction" = [ "EventTarget" ];
+          "IdbVersionChangeEvent" = [ "Event" ];
+          "IirFilterNode" = [ "AudioNode" "EventTarget" ];
+          "ImageCaptureErrorEvent" = [ "Event" ];
+          "ImageTrack" = [ "EventTarget" ];
+          "InputEvent" = [ "Event" "UiEvent" ];
+          "KeyboardEvent" = [ "Event" "UiEvent" ];
+          "KeyframeEffect" = [ "AnimationEffect" ];
+          "LocalMediaStream" = [ "EventTarget" "MediaStream" ];
+          "MediaDevices" = [ "EventTarget" ];
+          "MediaElementAudioSourceNode" = [ "AudioNode" "EventTarget" ];
+          "MediaEncryptedEvent" = [ "Event" ];
+          "MediaKeyError" = [ "Event" ];
+          "MediaKeyMessageEvent" = [ "Event" ];
+          "MediaKeySession" = [ "EventTarget" ];
+          "MediaQueryList" = [ "EventTarget" ];
+          "MediaQueryListEvent" = [ "Event" ];
+          "MediaRecorder" = [ "EventTarget" ];
+          "MediaRecorderErrorEvent" = [ "Event" ];
+          "MediaSource" = [ "EventTarget" ];
+          "MediaStream" = [ "EventTarget" ];
+          "MediaStreamAudioDestinationNode" = [ "AudioNode" "EventTarget" ];
+          "MediaStreamAudioSourceNode" = [ "AudioNode" "EventTarget" ];
+          "MediaStreamEvent" = [ "Event" ];
+          "MediaStreamTrack" = [ "EventTarget" ];
+          "MediaStreamTrackEvent" = [ "Event" ];
+          "MediaStreamTrackGenerator" = [ "EventTarget" "MediaStreamTrack" ];
+          "MessageEvent" = [ "Event" ];
+          "MessagePort" = [ "EventTarget" ];
+          "MidiAccess" = [ "EventTarget" ];
+          "MidiConnectionEvent" = [ "Event" ];
+          "MidiInput" = [ "EventTarget" "MidiPort" ];
+          "MidiMessageEvent" = [ "Event" ];
+          "MidiOutput" = [ "EventTarget" "MidiPort" ];
+          "MidiPort" = [ "EventTarget" ];
+          "MouseEvent" = [ "Event" "UiEvent" ];
+          "MouseScrollEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "MutationEvent" = [ "Event" ];
+          "NetworkInformation" = [ "EventTarget" ];
+          "Node" = [ "EventTarget" ];
+          "Notification" = [ "EventTarget" ];
+          "NotificationEvent" = [ "Event" "ExtendableEvent" ];
+          "OfflineAudioCompletionEvent" = [ "Event" ];
+          "OfflineAudioContext" = [ "BaseAudioContext" "EventTarget" ];
+          "OfflineResourceList" = [ "EventTarget" ];
+          "OffscreenCanvas" = [ "EventTarget" ];
+          "OscillatorNode" = [ "AudioNode" "AudioScheduledSourceNode" "EventTarget" ];
+          "PageTransitionEvent" = [ "Event" ];
+          "PaintWorkletGlobalScope" = [ "WorkletGlobalScope" ];
+          "PannerNode" = [ "AudioNode" "EventTarget" ];
+          "PaymentMethodChangeEvent" = [ "Event" "PaymentRequestUpdateEvent" ];
+          "PaymentRequestUpdateEvent" = [ "Event" ];
+          "Performance" = [ "EventTarget" ];
+          "PerformanceMark" = [ "PerformanceEntry" ];
+          "PerformanceMeasure" = [ "PerformanceEntry" ];
+          "PerformanceNavigationTiming" = [ "PerformanceEntry" "PerformanceResourceTiming" ];
+          "PerformanceResourceTiming" = [ "PerformanceEntry" ];
+          "PermissionStatus" = [ "EventTarget" ];
+          "PointerEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "PopStateEvent" = [ "Event" ];
+          "PopupBlockedEvent" = [ "Event" ];
+          "PresentationAvailability" = [ "EventTarget" ];
+          "PresentationConnection" = [ "EventTarget" ];
+          "PresentationConnectionAvailableEvent" = [ "Event" ];
+          "PresentationConnectionCloseEvent" = [ "Event" ];
+          "PresentationConnectionList" = [ "EventTarget" ];
+          "PresentationRequest" = [ "EventTarget" ];
+          "ProcessingInstruction" = [ "CharacterData" "EventTarget" "Node" ];
+          "ProgressEvent" = [ "Event" ];
+          "PromiseRejectionEvent" = [ "Event" ];
+          "PublicKeyCredential" = [ "Credential" ];
+          "PushEvent" = [ "Event" "ExtendableEvent" ];
+          "RadioNodeList" = [ "NodeList" ];
+          "RtcDataChannel" = [ "EventTarget" ];
+          "RtcDataChannelEvent" = [ "Event" ];
+          "RtcPeerConnection" = [ "EventTarget" ];
+          "RtcPeerConnectionIceEvent" = [ "Event" ];
+          "RtcTrackEvent" = [ "Event" ];
+          "RtcdtmfSender" = [ "EventTarget" ];
+          "RtcdtmfToneChangeEvent" = [ "Event" ];
+          "Screen" = [ "EventTarget" ];
+          "ScreenOrientation" = [ "EventTarget" ];
+          "ScriptProcessorNode" = [ "AudioNode" "EventTarget" ];
+          "ScrollAreaEvent" = [ "Event" "UiEvent" ];
+          "SecurityPolicyViolationEvent" = [ "Event" ];
+          "Serial" = [ "EventTarget" ];
+          "SerialPort" = [ "EventTarget" ];
+          "ServiceWorker" = [ "EventTarget" ];
+          "ServiceWorkerContainer" = [ "EventTarget" ];
+          "ServiceWorkerGlobalScope" = [ "EventTarget" "WorkerGlobalScope" ];
+          "ServiceWorkerRegistration" = [ "EventTarget" ];
+          "ShadowRoot" = [ "DocumentFragment" "EventTarget" "Node" ];
+          "SharedWorker" = [ "EventTarget" ];
+          "SharedWorkerGlobalScope" = [ "EventTarget" "WorkerGlobalScope" ];
+          "SourceBuffer" = [ "EventTarget" ];
+          "SourceBufferList" = [ "EventTarget" ];
+          "SpeechRecognition" = [ "EventTarget" ];
+          "SpeechRecognitionError" = [ "Event" ];
+          "SpeechRecognitionEvent" = [ "Event" ];
+          "SpeechSynthesis" = [ "EventTarget" ];
+          "SpeechSynthesisErrorEvent" = [ "Event" "SpeechSynthesisEvent" ];
+          "SpeechSynthesisEvent" = [ "Event" ];
+          "SpeechSynthesisUtterance" = [ "EventTarget" ];
+          "StereoPannerNode" = [ "AudioNode" "EventTarget" ];
+          "StorageEvent" = [ "Event" ];
+          "SubmitEvent" = [ "Event" ];
+          "SvgAnimateElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgAnimateMotionElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgAnimateTransformElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgAnimationElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgCircleElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgClipPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgComponentTransferFunctionElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgDefsElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgDescElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgElement" = [ "Element" "EventTarget" "Node" ];
+          "SvgEllipseElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgFilterElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgForeignObjectElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgGeometryElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgGradientElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgGraphicsElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgImageElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgLineElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgLinearGradientElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGradientElement" ];
+          "SvgMarkerElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgMaskElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgMetadataElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgPathSegArcAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegArcRel" = [ "SvgPathSeg" ];
+          "SvgPathSegClosePath" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicRel" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicSmoothAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicSmoothRel" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticRel" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticSmoothAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticSmoothRel" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoHorizontalAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoHorizontalRel" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoRel" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoVerticalAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoVerticalRel" = [ "SvgPathSeg" ];
+          "SvgPathSegMovetoAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegMovetoRel" = [ "SvgPathSeg" ];
+          "SvgPatternElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgPolygonElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgPolylineElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgRadialGradientElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGradientElement" ];
+          "SvgRectElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgScriptElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgSetElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgStopElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgStyleElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgSwitchElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgSymbolElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgTextContentElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgTextElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" "SvgTextPositioningElement" ];
+          "SvgTextPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" ];
+          "SvgTextPositioningElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" ];
+          "SvgTitleElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgUseElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgViewElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgaElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgfeBlendElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeColorMatrixElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeComponentTransferElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeCompositeElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeConvolveMatrixElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDiffuseLightingElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDisplacementMapElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDistantLightElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDropShadowElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeFloodElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeFuncAElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeFuncBElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeFuncGElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeFuncRElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeGaussianBlurElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeImageElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeMergeElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeMergeNodeElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeMorphologyElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeOffsetElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfePointLightElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeSpecularLightingElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeSpotLightElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeTileElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeTurbulenceElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvggElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgmPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgsvgElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgtSpanElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" "SvgTextPositioningElement" ];
+          "TaskController" = [ "AbortController" ];
+          "TaskPriorityChangeEvent" = [ "Event" ];
+          "TaskSignal" = [ "AbortSignal" "EventTarget" ];
+          "TcpServerSocket" = [ "EventTarget" ];
+          "TcpServerSocketEvent" = [ "Event" ];
+          "TcpSocket" = [ "EventTarget" ];
+          "TcpSocketErrorEvent" = [ "Event" ];
+          "TcpSocketEvent" = [ "Event" ];
+          "Text" = [ "CharacterData" "EventTarget" "Node" ];
+          "TextTrack" = [ "EventTarget" ];
+          "TextTrackCue" = [ "EventTarget" ];
+          "TextTrackList" = [ "EventTarget" ];
+          "TimeEvent" = [ "Event" ];
+          "TouchEvent" = [ "Event" "UiEvent" ];
+          "TrackEvent" = [ "Event" ];
+          "TransitionEvent" = [ "Event" ];
+          "UiEvent" = [ "Event" ];
+          "Usb" = [ "EventTarget" ];
+          "UsbConnectionEvent" = [ "Event" ];
+          "UsbPermissionResult" = [ "EventTarget" "PermissionStatus" ];
+          "UserProximityEvent" = [ "Event" ];
+          "ValueEvent" = [ "Event" ];
+          "VideoStreamTrack" = [ "EventTarget" "MediaStreamTrack" ];
+          "VideoTrackList" = [ "EventTarget" ];
+          "VrDisplay" = [ "EventTarget" ];
+          "VttCue" = [ "EventTarget" "TextTrackCue" ];
+          "WakeLockSentinel" = [ "EventTarget" ];
+          "WaveShaperNode" = [ "AudioNode" "EventTarget" ];
+          "WebGlContextEvent" = [ "Event" ];
+          "WebKitCssMatrix" = [ "DomMatrix" "DomMatrixReadOnly" ];
+          "WebSocket" = [ "EventTarget" ];
+          "WebTransportError" = [ "DomException" ];
+          "WebTransportReceiveStream" = [ "ReadableStream" ];
+          "WebTransportSendStream" = [ "WritableStream" ];
+          "WheelEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "Window" = [ "EventTarget" ];
+          "WindowClient" = [ "Client" ];
+          "Worker" = [ "EventTarget" ];
+          "WorkerDebuggerGlobalScope" = [ "EventTarget" ];
+          "WorkerGlobalScope" = [ "EventTarget" ];
+          "XmlDocument" = [ "Document" "EventTarget" "Node" ];
+          "XmlHttpRequest" = [ "EventTarget" "XmlHttpRequestEventTarget" ];
+          "XmlHttpRequestEventTarget" = [ "EventTarget" ];
+          "XmlHttpRequestUpload" = [ "EventTarget" "XmlHttpRequestEventTarget" ];
+          "XrBoundedReferenceSpace" = [ "EventTarget" "XrReferenceSpace" "XrSpace" ];
+          "XrInputSourceEvent" = [ "Event" ];
+          "XrInputSourcesChangeEvent" = [ "Event" ];
+          "XrJointPose" = [ "XrPose" ];
+          "XrJointSpace" = [ "EventTarget" "XrSpace" ];
+          "XrLayer" = [ "EventTarget" ];
+          "XrPermissionStatus" = [ "EventTarget" "PermissionStatus" ];
+          "XrReferenceSpace" = [ "EventTarget" "XrSpace" ];
+          "XrReferenceSpaceEvent" = [ "Event" ];
+          "XrSession" = [ "EventTarget" ];
+          "XrSessionEvent" = [ "Event" ];
+          "XrSpace" = [ "EventTarget" ];
+          "XrSystem" = [ "EventTarget" ];
+          "XrViewerPose" = [ "XrPose" ];
+          "XrWebGlLayer" = [ "EventTarget" "XrLayer" ];
+        };
+        resolvedDefaultFeatures = [ "Crypto" "EventTarget" "Window" ];
+      };
+      "webpki" = rec {
+        crateName = "webpki";
+        version = "0.22.4";
+        edition = "2018";
+        sha256 = "0lwv7jdlcqjjqqhxcrapnyk5bz4lvr12q444b50gzl3krsjswqzd";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+        dependencies = [
+          {
+            name = "ring";
+            packageId = "ring 0.17.5";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted 0.9.0";
+          }
+        ];
+        features = {
+          "alloc" = [ "ring/alloc" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "which" = rec {
+        crateName = "which";
+        version = "4.4.2";
+        edition = "2021";
+        sha256 = "1ixzmx3svsv5hbdvd8vdhd3qwvf6ns8jdpif1wmwsy10k90j9fl7";
+        authors = [
+          "Harry Fei <tiziyuanfang@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "home";
+            packageId = "home";
+            target = { target, features }: ((target."windows" or false) || (target."unix" or false) || ("redox" == target."os" or null));
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
+            features = [ "fs" "std" ];
+          }
+        ];
+        features = {
+          "regex" = [ "dep:regex" ];
+        };
+      };
+      "winapi" = rec {
+        crateName = "winapi";
+        version = "0.3.9";
+        edition = "2015";
+        sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-i686-pc-windows-gnu";
+            packageId = "winapi-i686-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+          }
+          {
+            name = "winapi-x86_64-pc-windows-gnu";
+            packageId = "winapi-x86_64-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+          }
+        ];
+        features = {
+          "debug" = [ "impl-debug" ];
+        };
+        resolvedDefaultFeatures = [ "cfg" "combaseapi" "errhandlingapi" "evntrace" "fileapi" "handleapi" "heapapi" "ifdef" "in6addr" "inaddr" "ioapiset" "iphlpapi" "knownfolders" "lmaccess" "lmapibuf" "lmcons" "memoryapi" "minwinbase" "minwindef" "netioapi" "ntlsa" "ntsecapi" "ntstatus" "objbase" "objidl" "oleauto" "pdh" "powerbase" "processthreadsapi" "psapi" "rpcdce" "sddl" "securitybaseapi" "shellapi" "shlobj" "std" "synchapi" "sysinfoapi" "wbemcli" "winbase" "windef" "winerror" "winioctl" "winnt" "winsock2" "ws2ipdef" "ws2tcpip" "wtypesbase" ];
+      };
+      "winapi-i686-pc-windows-gnu" = rec {
+        crateName = "winapi-i686-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi-x86_64-pc-windows-gnu" = rec {
+        crateName = "winapi-x86_64-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "windows-core" = rec {
+        crateName = "windows-core";
+        version = "0.51.1";
+        edition = "2021";
+        sha256 = "0r1f57hsshsghjyc7ypp2s0i78f7b1vr93w68sdb8baxyf2czy7i";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "windows-sys 0.45.0" = rec {
+        crateName = "windows-sys";
+        version = "0.45.0";
+        edition = "2018";
+        sha256 = "1l36bcqm4g89pknfp8r9rl1w4bn017q6a8qlx8viv0xjxzjkna3m";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.42.2";
+            target = { target, features }: (!(target."windows_raw_dylib" or false));
+          }
+        ];
+        features = {
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_UI" "Win32_UI_Input" "Win32_UI_Input_KeyboardAndMouse" "default" ];
+      };
+      "windows-sys 0.48.0" = rec {
+        crateName = "windows-sys";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+          }
+        ];
+        features = {
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_NetworkManagement" "Win32_NetworkManagement_IpHelper" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_Security_Cryptography" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_Memory" "Win32_System_Pipes" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_WindowsProgramming" "Win32_UI" "Win32_UI_Shell" "default" ];
+      };
+      "windows-sys 0.52.0" = rec {
+        crateName = "windows-sys";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+          }
+        ];
+        features = {
+          "Wdk_Foundation" = [ "Wdk" ];
+          "Wdk_Graphics" = [ "Wdk" ];
+          "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ];
+          "Wdk_Storage" = [ "Wdk" ];
+          "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ];
+          "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ];
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_IO" = [ "Wdk_System" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Wdk_System_Registry" = [ "Wdk_System" ];
+          "Wdk_System_SystemInformation" = [ "Wdk_System" ];
+          "Wdk_System_SystemServices" = [ "Wdk_System" ];
+          "Wdk_System_Threading" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_Nvme" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_Variant" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_System" "Win32_System_Console" "default" ];
+      };
+      "windows-targets 0.42.2" = rec {
+        crateName = "windows-targets";
+        version = "0.42.2";
+        edition = "2018";
+        sha256 = "0wfhnib2fisxlx8c507dbmh97kgij4r6kcxdi0f9nk6l1k080lcf";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-msvc");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-uwp-windows-msvc");
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-uwp-windows-gnu");
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-msvc");
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-uwp-windows-msvc");
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-uwp-windows-gnu");
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-msvc");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.42.2";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-uwp-windows-msvc");
+          }
+        ];
+
+      };
+      "windows-targets 0.48.5" = rec {
+        crateName = "windows-targets";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.48.5";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows-targets 0.52.0" = rec {
+        crateName = "windows-targets";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1kg7a27ynzw8zz3krdgy6w5gbqcji27j1sz4p7xk2j5j8082064a";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.52.0";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.52.0";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.52.0";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.52.0";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.52.0";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.52.0";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.52.0";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.42.2" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.42.2";
+        edition = "2018";
+        sha256 = "1y4q0qmvl0lvp7syxvfykafvmwal5hrjb4fmv04bqs0bawc52yjr";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.48.5" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.52.0" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1shmn1kbdc0bpphcxz0vlph96bxz0h1jlmh93s9agf2dbpin8xyb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.42.2" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.42.2";
+        edition = "2018";
+        sha256 = "0hsdikjl5sa1fva5qskpwlxzpc5q9l909fpl1w6yy1hglrj8i3p0";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.48.5" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.52.0" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1vvmy1ypvzdvxn9yf0b8ygfl85gl2gpcyvsvqppsmlpisil07amv";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.42.2" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.42.2";
+        edition = "2018";
+        sha256 = "0kx866dfrby88lqs9v1vgmrkk1z6af9lhaghh5maj7d4imyr47f6";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.48.5" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.52.0" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "04zkglz4p3pjsns5gbz85v4s5aw102raz4spj4b0lmm33z5kg1m2";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.42.2" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.42.2";
+        edition = "2018";
+        sha256 = "0q0h9m2aq1pygc199pa5jgc952qhcnf0zn688454i7v4xjv41n24";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.48.5" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.52.0" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "16kvmbvx0vr0zbgnaz6nsks9ycvfh5xp05bjrhq65kj623iyirgz";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.42.2" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.42.2";
+        edition = "2018";
+        sha256 = "0dnbf2xnp3xrvy8v9mgs3var4zq9v9yh9kv79035rdgyp2w15scd";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.48.5" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.52.0" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1zdy4qn178sil5sdm63lm7f0kkcjg6gvdwmcprd2yjmwn8ns6vrx";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.42.2" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.42.2";
+        edition = "2018";
+        sha256 = "18wl9r8qbsl475j39zvawlidp1bsbinliwfymr43fibdld31pm16";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.48.5" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.52.0" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "17lllq4l2k1lqgcnw1cccphxp9vs7inq99kjlm2lfl9zklg7wr8s";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.42.2" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.42.2";
+        edition = "2018";
+        sha256 = "1w5r0q0yzx827d10dpjza2ww0j8iajqhmb54s735hhaj66imvv4s";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.48.5" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.52.0" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "012wfq37f18c09ij5m6rniw7xxn5fcvrxbqd0wd8vgnl3hfn9yfz";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "xml-rs" = rec {
+        crateName = "xml-rs";
+        version = "0.8.19";
+        edition = "2021";
+        crateBin = [ ];
+        sha256 = "0nnpvk3fv32hgh7vs9gbg2swmzxx5yz73f4b7rak7q39q2x9rjqg";
+        libName = "xml";
+        authors = [
+          "Vladimir Matveev <vmatveev@citrine.cc>"
+        ];
+
+      };
+      "xxhash-rust" = rec {
+        crateName = "xxhash-rust";
+        version = "0.8.7";
+        edition = "2018";
+        sha256 = "0yz037yrkn0qa0g0r6733ynd1xbw7zvx58v6qylhyi2kv9wb2a4q";
+        authors = [
+          "Douman <douman@gmx.se>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "xxh3" ];
+      };
+      "xz2" = rec {
+        crateName = "xz2";
+        version = "0.1.7";
+        edition = "2018";
+        sha256 = "1qk7nzpblizvayyq4xzi4b0zacmmbqr6vb9fc0v1avyp17f4931q";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "lzma-sys";
+            packageId = "lzma-sys";
+          }
+        ];
+        features = {
+          "futures" = [ "dep:futures" ];
+          "static" = [ "lzma-sys/static" ];
+          "tokio" = [ "tokio-io" "futures" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+        };
+      };
+      "zerocopy" = rec {
+        crateName = "zerocopy";
+        version = "0.7.26";
+        edition = "2018";
+        sha256 = "184s51bzn8mpx3p9h6klrlppv9b7ja1b8y9998jr36jmj1a42zp9";
+        authors = [
+          "Joshua Liebow-Feeser <joshlf@google.com>"
+        ];
+        dependencies = [
+          {
+            name = "zerocopy-derive";
+            packageId = "zerocopy-derive";
+            optional = true;
+          }
+          {
+            name = "zerocopy-derive";
+            packageId = "zerocopy-derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "zerocopy-derive";
+            packageId = "zerocopy-derive";
+          }
+        ];
+        features = {
+          "__internal_use_only_features_that_work_on_stable" = [ "alloc" "derive" "simd" ];
+          "byteorder" = [ "dep:byteorder" ];
+          "default" = [ "byteorder" ];
+          "derive" = [ "zerocopy-derive" ];
+          "simd-nightly" = [ "simd" ];
+          "zerocopy-derive" = [ "dep:zerocopy-derive" ];
+        };
+        resolvedDefaultFeatures = [ "simd" ];
+      };
+      "zerocopy-derive" = rec {
+        crateName = "zerocopy-derive";
+        version = "0.7.26";
+        edition = "2018";
+        sha256 = "17v8yshfa23z5az2xhclpwrlzih26nj7imwbra12i5b6y764hznx";
+        procMacro = true;
+        authors = [
+          "Joshua Liebow-Feeser <joshlf@google.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+          }
+        ];
+
+      };
+      "zeroize" = rec {
+        crateName = "zeroize";
+        version = "1.7.0";
+        edition = "2021";
+        sha256 = "0bfvby7k9pdp6623p98yz2irqnamcyzpn7zh20nqmdn68b0lwnsj";
+        authors = [
+          "The RustCrypto Project Developers"
+        ];
+        features = {
+          "default" = [ "alloc" ];
+          "derive" = [ "zeroize_derive" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "zeroize_derive" = [ "dep:zeroize_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" ];
+      };
+      "zstd" = rec {
+        crateName = "zstd";
+        version = "0.13.0";
+        edition = "2018";
+        sha256 = "0401q54s9r35x2i7m1kwppgkj79g0pb6xz3xpby7qlkdb44k7yxz";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "zstd-safe";
+            packageId = "zstd-safe";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        features = {
+          "arrays" = [ "zstd-safe/arrays" ];
+          "bindgen" = [ "zstd-safe/bindgen" ];
+          "debug" = [ "zstd-safe/debug" ];
+          "default" = [ "legacy" "arrays" "zdict_builder" ];
+          "experimental" = [ "zstd-safe/experimental" ];
+          "fat-lto" = [ "zstd-safe/fat-lto" ];
+          "legacy" = [ "zstd-safe/legacy" ];
+          "no_asm" = [ "zstd-safe/no_asm" ];
+          "pkg-config" = [ "zstd-safe/pkg-config" ];
+          "thin" = [ "zstd-safe/thin" ];
+          "thin-lto" = [ "zstd-safe/thin-lto" ];
+          "zdict_builder" = [ "zstd-safe/zdict_builder" ];
+          "zstdmt" = [ "zstd-safe/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "default" "legacy" "zdict_builder" ];
+      };
+      "zstd-safe" = rec {
+        crateName = "zstd-safe";
+        version = "7.0.0";
+        edition = "2018";
+        sha256 = "0gpav2lcibrpmyslmjkcn3w0w64qif3jjljd2h8lr4p249s7qx23";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "zstd-sys";
+            packageId = "zstd-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "bindgen" = [ "zstd-sys/bindgen" ];
+          "debug" = [ "zstd-sys/debug" ];
+          "default" = [ "legacy" "arrays" "zdict_builder" ];
+          "experimental" = [ "zstd-sys/experimental" ];
+          "fat-lto" = [ "zstd-sys/fat-lto" ];
+          "legacy" = [ "zstd-sys/legacy" ];
+          "no_asm" = [ "zstd-sys/no_asm" ];
+          "pkg-config" = [ "zstd-sys/pkg-config" ];
+          "std" = [ "zstd-sys/std" ];
+          "thin" = [ "zstd-sys/thin" ];
+          "thin-lto" = [ "zstd-sys/thin-lto" ];
+          "zdict_builder" = [ "zstd-sys/zdict_builder" ];
+          "zstdmt" = [ "zstd-sys/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "legacy" "std" "zdict_builder" ];
+      };
+      "zstd-sys" = rec {
+        crateName = "zstd-sys";
+        version = "2.0.9+zstd.1.5.5";
+        edition = "2018";
+        links = "zstd";
+        sha256 = "0mk6a2367swdi22zg03lcackpnvgq96d7120awd4i83lm2lfy5ly";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            features = [ "parallel" ];
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+        ];
+        features = {
+          "bindgen" = [ "dep:bindgen" ];
+          "default" = [ "legacy" "zdict_builder" ];
+        };
+        resolvedDefaultFeatures = [ "legacy" "std" "zdict_builder" ];
+      };
+    };
+
+    #
+    # crate2nix/default.nix (excerpt start)
+    #
+
+    /* Target (platform) data for conditional dependencies.
+      This corresponds roughly to what buildRustCrate is setting.
+    */
+    makeDefaultTarget = platform: {
+      unix = platform.isUnix;
+      windows = platform.isWindows;
+      fuchsia = true;
+      test = false;
+
+      /* We are choosing an arbitrary rust version to grab `lib` from,
+      which is unfortunate, but `lib` has been version-agnostic the
+      whole time so this is good enough for now.
+      */
+      os = pkgs.rust.lib.toTargetOs platform;
+      arch = pkgs.rust.lib.toTargetArch platform;
+      family = pkgs.rust.lib.toTargetFamily platform;
+      vendor = pkgs.rust.lib.toTargetVendor platform;
+      env = "gnu";
+      endian =
+        if platform.parsed.cpu.significantByte.name == "littleEndian"
+        then "little" else "big";
+      pointer_width = toString platform.parsed.cpu.bits;
+      debug_assertions = false;
+    };
+
+    /* Filters common temp files and build files. */
+    # TODO(pkolloch): Substitute with gitignore filter
+    sourceFilter = name: type:
+      let
+        baseName = builtins.baseNameOf (builtins.toString name);
+      in
+        ! (
+          # Filter out git
+          baseName == ".gitignore"
+          || (type == "directory" && baseName == ".git")
+
+          # Filter out build results
+          || (
+            type == "directory" && (
+              baseName == "target"
+              || baseName == "_site"
+              || baseName == ".sass-cache"
+              || baseName == ".jekyll-metadata"
+              || baseName == "build-artifacts"
+            )
+          )
+
+          # Filter out nix-build result symlinks
+          || (
+            type == "symlink" && lib.hasPrefix "result" baseName
+          )
+
+          # Filter out IDE config
+          || (
+            type == "directory" && (
+              baseName == ".idea" || baseName == ".vscode"
+            )
+          ) || lib.hasSuffix ".iml" baseName
+
+          # Filter out nix build files
+          || baseName == "Cargo.nix"
+
+          # Filter out editor backup / swap files.
+          || lib.hasSuffix "~" baseName
+          || builtins.match "^\\.sw[a-z]$$" baseName != null
+          || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null
+          || lib.hasSuffix ".tmp" baseName
+          || lib.hasSuffix ".bak" baseName
+          || baseName == "tests.nix"
+        );
+
+    /* Returns a crate which depends on successful test execution
+      of crate given as the second argument.
+
+      testCrateFlags: list of flags to pass to the test exectuable
+      testInputs: list of packages that should be available during test execution
+    */
+    crateWithTest = { crate, testCrate, testCrateFlags, testInputs, testPreRun, testPostRun }:
+      assert builtins.typeOf testCrateFlags == "list";
+      assert builtins.typeOf testInputs == "list";
+      assert builtins.typeOf testPreRun == "string";
+      assert builtins.typeOf testPostRun == "string";
+      let
+        # override the `crate` so that it will build and execute tests instead of
+        # building the actual lib and bin targets We just have to pass `--test`
+        # to rustc and it will do the right thing.  We execute the tests and copy
+        # their log and the test executables to $out for later inspection.
+        test =
+          let
+            drv = testCrate.override
+              (
+                _: {
+                  buildTests = true;
+                }
+              );
+            # If the user hasn't set any pre/post commands, we don't want to
+            # insert empty lines. This means that any existing users of crate2nix
+            # don't get a spurious rebuild unless they set these explicitly.
+            testCommand = pkgs.lib.concatStringsSep "\n"
+              (pkgs.lib.filter (s: s != "") [
+                testPreRun
+                "$f $testCrateFlags 2>&1 | tee -a $out"
+                testPostRun
+              ]);
+          in
+          pkgs.runCommand "run-tests-${testCrate.name}"
+            {
+              inherit testCrateFlags;
+              buildInputs = testInputs;
+            } ''
+            set -e
+
+            export RUST_BACKTRACE=1
+
+            # recreate a file hierarchy as when running tests with cargo
+
+            # the source for test data
+            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
+            # instead of $NIX_BUILD_TOP/
+            # because we compiled those test binaries in the former and not the latter.
+            # So all paths will expect source tree to be there and not in the build top directly.
+            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
+            # TODO(raitobezarius): I believe there could be more edge cases if `crate.sourceRoot`
+            # do exist but it's very hard to reason about them, so let's wait until the first bug report.
+            mkdir -p source/
+            cd source/
+
+            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+
+            # build outputs
+            testRoot=target/debug
+            mkdir -p $testRoot
+
+            # executables of the crate
+            # we copy to prevent std::env::current_exe() to resolve to a store location
+            for i in ${crate}/bin/*; do
+              cp "$i" "$testRoot"
+            done
+            chmod +w -R .
+
+            # test harness executables are suffixed with a hash, like cargo does
+            # this allows to prevent name collision with the main
+            # executables of the crate
+            hash=$(basename $out)
+            for file in ${drv}/tests/*; do
+              f=$testRoot/$(basename $file)-$hash
+              cp $file $f
+              ${testCommand}
+            done
+          '';
+      in
+      pkgs.runCommand "${crate.name}-linked"
+        {
+          inherit (crate) outputs crateName;
+          passthru = (crate.passthru or { }) // {
+            inherit test;
+          };
+        }
+        (lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+          echo tested by ${test}
+        '' + ''
+          ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs}
+        '');
+
+    /* A restricted overridable version of builtRustCratesWithFeatures. */
+    buildRustCrateWithFeatures =
+      { packageId
+      , features ? rootFeatures
+      , crateOverrides ? defaultCrateOverrides
+      , buildRustCrateForPkgsFunc ? null
+      , runTests ? false
+      , testCrateFlags ? [ ]
+      , testInputs ? [ ]
+        # Any command to run immediatelly before a test is executed.
+      , testPreRun ? ""
+        # Any command run immediatelly after a test is executed.
+      , testPostRun ? ""
+      }:
+      lib.makeOverridable
+        (
+          { features
+          , crateOverrides
+          , runTests
+          , testCrateFlags
+          , testInputs
+          , testPreRun
+          , testPostRun
+          }:
+          let
+            buildRustCrateForPkgsFuncOverriden =
+              if buildRustCrateForPkgsFunc != null
+              then buildRustCrateForPkgsFunc
+              else
+                (
+                  if crateOverrides == pkgs.defaultCrateOverrides
+                  then buildRustCrateForPkgs
+                  else
+                    pkgs: (buildRustCrateForPkgs pkgs).override {
+                      defaultCrateOverrides = crateOverrides;
+                    }
+                );
+            builtRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = false;
+            };
+            builtTestRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = true;
+            };
+            drv = builtRustCrates.crates.${packageId};
+            testDrv = builtTestRustCrates.crates.${packageId};
+            derivation =
+              if runTests then
+                crateWithTest
+                  {
+                    crate = drv;
+                    testCrate = testDrv;
+                    inherit testCrateFlags testInputs testPreRun testPostRun;
+                  }
+              else drv;
+          in
+          derivation
+        )
+        { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun; };
+
+    /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc
+      for the corresponding crate.
+    */
+    builtRustCratesWithFeatures =
+      { packageId
+      , features
+      , crateConfigs ? crates
+      , buildRustCrateForPkgsFunc
+      , runTests
+      , makeTarget ? makeDefaultTarget
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isList features);
+        assert (builtins.isAttrs (makeTarget stdenv.hostPlatform));
+        assert (builtins.isBool runTests);
+        let
+          rootPackageId = packageId;
+          mergedFeatures = mergePackageFeatures
+            (
+              args // {
+                inherit rootPackageId;
+                target = makeTarget stdenv.hostPlatform // { test = runTests; };
+              }
+            );
+          # Memoize built packages so that reappearing packages are only built once.
+          builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs;
+          mkBuiltByPackageIdByPkgs = pkgs:
+            let
+              self = {
+                crates = lib.mapAttrs (packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId) crateConfigs;
+                target = makeTarget pkgs.stdenv.hostPlatform;
+                build = mkBuiltByPackageIdByPkgs pkgs.buildPackages;
+              };
+            in
+            self;
+          buildByPackageIdForPkgsImpl = self: pkgs: packageId:
+            let
+              features = mergedFeatures."${packageId}" or [ ];
+              crateConfig' = crateConfigs."${packageId}";
+              crateConfig =
+                builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ];
+              devDependencies =
+                lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig'.devDependencies or [ ]);
+              dependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self) target;
+                  buildByPackageId = depPackageId:
+                    # proc_macro crates must be compiled for the build architecture
+                    if crateConfigs.${depPackageId}.procMacro or false
+                    then self.build.crates.${depPackageId}
+                    else self.crates.${depPackageId};
+                  dependencies =
+                    (crateConfig.dependencies or [ ])
+                    ++ devDependencies;
+                };
+              buildDependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self.build) target;
+                  buildByPackageId = depPackageId:
+                    self.build.crates.${depPackageId};
+                  dependencies = crateConfig.buildDependencies or [ ];
+                };
+              dependenciesWithRenames =
+                let
+                  buildDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self) target;
+                    dependencies = crateConfig.dependencies or [ ] ++ devDependencies;
+                  };
+                  hostDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self.build) target;
+                    dependencies = crateConfig.buildDependencies or [ ];
+                  };
+                in
+                lib.filter (d: d ? "rename") (hostDeps ++ buildDeps);
+              # Crate renames have the form:
+              #
+              # {
+              #    crate_name = [
+              #       { version = "1.2.3"; rename = "crate_name01"; }
+              #    ];
+              #    # ...
+              # }
+              crateRenames =
+                let
+                  grouped =
+                    lib.groupBy
+                      (dependency: dependency.name)
+                      dependenciesWithRenames;
+                  versionAndRename = dep:
+                    let
+                      package = crateConfigs."${dep.packageId}";
+                    in
+                    { inherit (dep) rename; inherit (package) version; };
+                in
+                lib.mapAttrs (name: builtins.map versionAndRename) grouped;
+            in
+            buildRustCrateForPkgsFunc pkgs
+              (
+                crateConfig // {
+                  # https://github.com/NixOS/nixpkgs/issues/218712
+                  dontStrip = stdenv.hostPlatform.isDarwin;
+                  src = crateConfig.src or (
+                    pkgs.fetchurl rec {
+                      name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz";
+                      # https://www.pietroalbini.org/blog/downloading-crates-io/
+                      # Not rate-limited, CDN URL.
+                      url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate";
+                      sha256 =
+                        assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}");
+                        crateConfig.sha256;
+                    }
+                  );
+                  extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}";
+                  inherit features dependencies buildDependencies crateRenames release;
+                }
+              );
+        in
+        builtByPackageIdByPkgs;
+
+    /* Returns the actual derivations for the given dependencies. */
+    dependencyDerivations =
+      { buildByPackageId
+      , features
+      , dependencies
+      , target
+      }:
+        assert (builtins.isList features);
+        assert (builtins.isList dependencies);
+        assert (builtins.isAttrs target);
+        let
+          enabledDependencies = filterEnabledDependencies {
+            inherit dependencies features target;
+          };
+          depDerivation = dependency: buildByPackageId dependency.packageId;
+        in
+        map depDerivation enabledDependencies;
+
+    /* Returns a sanitized version of val with all values substituted that cannot
+      be serialized as JSON.
+    */
+    sanitizeForJson = val:
+      if builtins.isAttrs val
+      then lib.mapAttrs (n: sanitizeForJson) val
+      else if builtins.isList val
+      then builtins.map sanitizeForJson val
+      else if builtins.isFunction val
+      then "function"
+      else val;
+
+    /* Returns various tools to debug a crate. */
+    debugCrate = { packageId, target ? makeDefaultTarget stdenv.hostPlatform }:
+      assert (builtins.isString packageId);
+      let
+        debug = rec {
+          # The built tree as passed to buildRustCrate.
+          buildTree = buildRustCrateWithFeatures {
+            buildRustCrateForPkgsFunc = _: lib.id;
+            inherit packageId;
+          };
+          sanitizedBuildTree = sanitizeForJson buildTree;
+          dependencyTree = sanitizeForJson
+            (
+              buildRustCrateWithFeatures {
+                buildRustCrateForPkgsFunc = _: crate: {
+                  "01_crateName" = crate.crateName or false;
+                  "02_features" = crate.features or [ ];
+                  "03_dependencies" = crate.dependencies or [ ];
+                };
+                inherit packageId;
+              }
+            );
+          mergedPackageFeatures = mergePackageFeatures {
+            features = rootFeatures;
+            inherit packageId target;
+          };
+          diffedDefaultPackageFeatures = diffDefaultPackageFeatures {
+            inherit packageId target;
+          };
+        };
+      in
+      { internal = debug; };
+
+    /* Returns differences between cargo default features and crate2nix default
+      features.
+
+      This is useful for verifying the feature resolution in crate2nix.
+    */
+    diffDefaultPackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , target
+      }:
+        assert (builtins.isAttrs crateConfigs);
+        let
+          prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; });
+          mergedFeatures =
+            prefixValues
+              "crate2nix"
+              (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; });
+          configs = prefixValues "cargo" crateConfigs;
+          combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ];
+          onlyInCargo =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined);
+          onlyInCrate2Nix =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined);
+          differentFeatures = lib.filterAttrs
+            (
+              n: v:
+                (v ? "crate2nix")
+                && (v ? "cargo")
+                && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ])
+            )
+            combined;
+        in
+        builtins.toJSON {
+          inherit onlyInCargo onlyInCrate2Nix differentFeatures;
+        };
+
+    /* Returns an attrset mapping packageId to the list of enabled features.
+
+      If multiple paths to a dependency enable different features, the
+      corresponding feature sets are merged. Features in rust are additive.
+    */
+    mergePackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , rootPackageId ? packageId
+      , features ? rootFeatures
+      , dependencyPath ? [ crates.${packageId}.crateName ]
+      , featuresByPackageId ? { }
+      , target
+        # Adds devDependencies to the crate with rootPackageId.
+      , runTests ? false
+      , ...
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isString rootPackageId);
+        assert (builtins.isList features);
+        assert (builtins.isList dependencyPath);
+        assert (builtins.isAttrs featuresByPackageId);
+        assert (builtins.isAttrs target);
+        assert (builtins.isBool runTests);
+        let
+          crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
+          expandedFeatures = expandFeatures (crateConfig.features or { }) features;
+          enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
+          depWithResolvedFeatures = dependency:
+            let
+              inherit (dependency) packageId;
+              features = dependencyFeatures enabledFeatures dependency;
+            in
+            { inherit packageId features; };
+          resolveDependencies = cache: path: dependencies:
+            assert (builtins.isAttrs cache);
+            assert (builtins.isList dependencies);
+            let
+              enabledDependencies = filterEnabledDependencies {
+                inherit dependencies target;
+                features = enabledFeatures;
+              };
+              directDependencies = map depWithResolvedFeatures enabledDependencies;
+              foldOverCache = op: lib.foldl op cache directDependencies;
+            in
+            foldOverCache
+              (
+                cache: { packageId, features }:
+                  let
+                    cacheFeatures = cache.${packageId} or [ ];
+                    combinedFeatures = sortedUnique (cacheFeatures ++ features);
+                  in
+                  if cache ? ${packageId} && cache.${packageId} == combinedFeatures
+                  then cache
+                  else
+                    mergePackageFeatures {
+                      features = combinedFeatures;
+                      featuresByPackageId = cache;
+                      inherit crateConfigs packageId target runTests rootPackageId;
+                    }
+              );
+          cacheWithSelf =
+            let
+              cacheFeatures = featuresByPackageId.${packageId} or [ ];
+              combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
+            in
+            featuresByPackageId // {
+              "${packageId}" = combinedFeatures;
+            };
+          cacheWithDependencies =
+            resolveDependencies cacheWithSelf "dep"
+              (
+                crateConfig.dependencies or [ ]
+                ++ lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig.devDependencies or [ ])
+              );
+          cacheWithAll =
+            resolveDependencies
+              cacheWithDependencies "build"
+              (crateConfig.buildDependencies or [ ]);
+        in
+        cacheWithAll;
+
+    /* Returns the enabled dependencies given the enabled features. */
+    filterEnabledDependencies = { dependencies, features, target }:
+      assert (builtins.isList dependencies);
+      assert (builtins.isList features);
+      assert (builtins.isAttrs target);
+
+      lib.filter
+        (
+          dep:
+          let
+            targetFunc = dep.target or (features: true);
+          in
+          targetFunc { inherit features target; }
+          && (
+            !(dep.optional or false)
+            || builtins.any (doesFeatureEnableDependency dep) features
+          )
+        )
+        dependencies;
+
+    /* Returns whether the given feature should enable the given dependency. */
+    doesFeatureEnableDependency = dependency: feature:
+      let
+        name = dependency.rename or dependency.name;
+        prefix = "${name}/";
+        len = builtins.stringLength prefix;
+        startsWithPrefix = builtins.substring 0 len feature == prefix;
+      in
+      feature == name || feature == "dep:" + name || startsWithPrefix;
+
+    /* Returns the expanded features for the given inputFeatures by applying the
+      rules in featureMap.
+
+      featureMap is an attribute set which maps feature names to lists of further
+      feature names to enable in case this feature is selected.
+    */
+    expandFeatures = featureMap: inputFeatures:
+      assert (builtins.isAttrs featureMap);
+      assert (builtins.isList inputFeatures);
+      let
+        expandFeaturesNoCycle = oldSeen: inputFeatures:
+          if inputFeatures != [ ]
+          then
+            let
+              # The feature we're currently expanding.
+              feature = builtins.head inputFeatures;
+              # All the features we've seen/expanded so far, including the one
+              # we're currently processing.
+              seen = oldSeen // { ${feature} = 1; };
+              # Expand the feature but be careful to not re-introduce a feature
+              # that we've already seen: this can easily cause a cycle, see issue
+              # #209.
+              enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]);
+            in
+            [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
+          # No more features left, nothing to expand to.
+          else [ ];
+        outFeatures = expandFeaturesNoCycle { } inputFeatures;
+      in
+      sortedUnique outFeatures;
+
+    /* This function adds optional dependencies as features if they are enabled
+      indirectly by dependency features. This function mimics Cargo's behavior
+      described in a note at:
+      https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
+    */
+    enableFeatures = dependencies: features:
+      assert (builtins.isList features);
+      assert (builtins.isList dependencies);
+      let
+        additionalFeatures = lib.concatMap
+          (
+            dependency:
+              assert (builtins.isAttrs dependency);
+              let
+                enabled = builtins.any (doesFeatureEnableDependency dependency) features;
+              in
+              if (dependency.optional or false) && enabled
+              then [ (dependency.rename or dependency.name) ]
+              else [ ]
+          )
+          dependencies;
+      in
+      sortedUnique (features ++ additionalFeatures);
+
+    /*
+      Returns the actual features for the given dependency.
+
+      features: The features of the crate that refers this dependency.
+    */
+    dependencyFeatures = features: dependency:
+      assert (builtins.isList features);
+      assert (builtins.isAttrs dependency);
+      let
+        defaultOrNil =
+          if dependency.usesDefaultFeatures or true
+          then [ "default" ]
+          else [ ];
+        explicitFeatures = dependency.features or [ ];
+        additionalDependencyFeatures =
+          let
+            name = dependency.rename or dependency.name;
+            stripPrefixMatch = prefix: s:
+              if lib.hasPrefix prefix s
+              then lib.removePrefix prefix s
+              else null;
+            extractFeature = feature: lib.findFirst
+              (f: f != null)
+              null
+              (map (prefix: stripPrefixMatch prefix feature) [
+                (name + "/")
+                (name + "?/")
+              ]);
+            dependencyFeatures = lib.filter (f: f != null) (map extractFeature features);
+          in
+          dependencyFeatures;
+      in
+      defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures;
+
+    /* Sorts and removes duplicates from a list of strings. */
+    sortedUnique = features:
+      assert (builtins.isList features);
+      assert (builtins.all builtins.isString features);
+      let
+        outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features;
+        outFeaturesUnique = builtins.attrNames outFeaturesSet;
+      in
+      builtins.sort (a: b: a < b) outFeaturesUnique;
+
+    deprecationWarning = message: value:
+      if strictDeprecation
+      then builtins.throw "strictDeprecation enabled, aborting: ${message}"
+      else builtins.trace message value;
+
+    #
+    # crate2nix/default.nix (excerpt end)
+    #
+  };
+}
+
diff --git a/tvix/tools/crunch-v2/Cargo.toml b/tvix/tools/crunch-v2/Cargo.toml
new file mode 100644
index 0000000000..1e3f025250
--- /dev/null
+++ b/tvix/tools/crunch-v2/Cargo.toml
@@ -0,0 +1,39 @@
+[package]
+name = "crunch-v2"
+version = "0.1.0"
+edition = "2021"
+
+[workspace]
+members = ["."]
+
+[dependencies]
+anyhow = { version = "1.0.75", features = ["backtrace"] }
+lazy_static = "1.4.0"
+
+bstr = "1.8.0"
+bytes = "1.5.0"
+
+futures = "0.3.29"
+tokio = { version = "1.34.0", features = ["full"] }
+
+rusoto_core = { version = "0.48.0", default-features = false, features = ["hyper-rustls"] }
+rusoto_s3 = { version = "0.48.0", default-features = false, features = ["rustls"] }
+
+nix-compat = { version = "0.1.0", path = "../../nix-compat" }
+sled = "0.34.7"
+
+fastcdc = "3.1.0"
+blake3 = "1.5.0"
+sha2 = { version = "0.10.8", features = ["asm"] }
+digest = "0.10.7"
+
+bzip2 = "0.4.4"
+xz2 = "0.1.7"
+zstd = "0.13.0"
+prost = "0.12.2"
+polars = { version = "0.35.4", default-features = false, features = ["parquet", "lazy", "sql", "dtype-struct"] }
+indicatif = "0.17.7"
+clap = { version = "4.4.18", features = ["derive"] }
+
+[build-dependencies]
+prost-build = "0.12.2"
diff --git a/tvix/tools/crunch-v2/OWNERS b/tvix/tools/crunch-v2/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/tvix/tools/crunch-v2/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/tvix/tools/crunch-v2/build.rs b/tvix/tools/crunch-v2/build.rs
new file mode 100644
index 0000000000..25e6d0be21
--- /dev/null
+++ b/tvix/tools/crunch-v2/build.rs
@@ -0,0 +1,6 @@
+use std::io::Result;
+
+fn main() -> Result<()> {
+    prost_build::compile_protos(&["protos/flatstore.proto"], &["protos/"])?;
+    Ok(())
+}
diff --git a/tvix/tools/crunch-v2/default.nix b/tvix/tools/crunch-v2/default.nix
new file mode 100644
index 0000000000..689e86be65
--- /dev/null
+++ b/tvix/tools/crunch-v2/default.nix
@@ -0,0 +1,15 @@
+{ pkgs, ... }:
+
+let
+  crates = import ./Cargo.nix {
+    inherit pkgs;
+    nixpkgs = pkgs.path;
+
+    defaultCrateOverrides = pkgs.defaultCrateOverrides // {
+      crunch-v2 = prev: {
+        nativeBuildInputs = (prev.nativeBuildInputs or [ ]) ++ [ pkgs.buildPackages.protobuf ];
+      };
+    };
+  };
+in
+crates.rootCrate.build
diff --git a/tvix/tools/crunch-v2/protos/flatstore.proto b/tvix/tools/crunch-v2/protos/flatstore.proto
new file mode 100644
index 0000000000..2f2838fc75
--- /dev/null
+++ b/tvix/tools/crunch-v2/protos/flatstore.proto
@@ -0,0 +1,38 @@
+syntax = "proto3";
+
+package tvix.flatstore.v1;
+
+message Path {
+    bytes nar_hash = 1;
+
+    oneof node {
+        DirectoryNode directory = 2;
+        FileNode file = 3;
+        SymlinkNode symlink = 4;
+    }
+}
+
+message DirectoryNode {
+    bytes name = 1;
+    repeated DirectoryNode directories = 2;
+    repeated FileNode files = 3;
+    repeated SymlinkNode symlinks = 4;
+}
+
+message FileNode {
+    bytes name = 1;
+    bytes hash = 2;
+    repeated Chunk chunks = 3;
+    bool executable = 4;
+}
+
+message Chunk {
+    bytes hash = 1;
+    uint32 size = 2;
+    uint32 size_compressed = 3;
+}
+
+message SymlinkNode {
+    bytes name = 1;
+    bytes target = 2;
+}
diff --git a/tvix/tools/crunch-v2/src/bin/extract.rs b/tvix/tools/crunch-v2/src/bin/extract.rs
new file mode 100644
index 0000000000..416d201f4e
--- /dev/null
+++ b/tvix/tools/crunch-v2/src/bin/extract.rs
@@ -0,0 +1,155 @@
+//! This tool lossily converts a Sled database produced by crunch-v2 into a Parquet file for analysis.
+//! The resulting `crunch.parquet` has columns file_hash`, `nar_hash`, and `chunk`.
+//! The first two are SHA-256 hashes of the compressed file and the NAR it decompresses to.
+//! `chunk` is a struct array corresponding to [crunch_v2::proto::Chunk] messages.
+//! They are concatenated without any additional structure, so nothing but the chunk list is preserved.
+
+use anyhow::Result;
+use clap::Parser;
+use indicatif::{ProgressBar, ProgressStyle};
+use std::fs::File;
+use std::path::PathBuf;
+
+use crunch_v2::proto::{self, path::Node};
+use prost::Message;
+
+use polars::{
+    chunked_array::builder::AnonymousOwnedListBuilder,
+    prelude::{
+        df, BinaryChunkedBuilder, ChunkedBuilder, DataFrame, DataType, Field, ListBuilderTrait,
+        NamedFrom, ParquetWriter, PrimitiveChunkedBuilder, Series, UInt32Type,
+    },
+    series::IntoSeries,
+};
+
+#[derive(Parser)]
+struct Args {
+    /// Path to the sled database that's read from.
+    #[clap(default_value = "crunch.db")]
+    infile: PathBuf,
+
+    /// Path to the resulting parquet file that's written.
+    #[clap(default_value = "crunch.parquet")]
+    outfile: PathBuf,
+}
+
+fn main() -> Result<()> {
+    let args = Args::parse();
+
+    let w = ParquetWriter::new(File::create(args.outfile)?);
+
+    let db: sled::Db = sled::open(&args.infile).unwrap();
+    let files_tree: sled::Tree = db.open_tree("files").unwrap();
+
+    let progress =
+        ProgressBar::new(files_tree.len() as u64).with_style(ProgressStyle::with_template(
+            "{elapsed_precise}/{duration_precise} {wide_bar} {pos}/{len}",
+        )?);
+
+    let mut frame = FrameBuilder::new();
+    for entry in &files_tree {
+        let (file_hash, pb) = entry?;
+        frame.push(
+            file_hash[..].try_into().unwrap(),
+            proto::Path::decode(&pb[..])?,
+        );
+        progress.inc(1);
+    }
+
+    w.finish(&mut frame.finish())?;
+
+    Ok(())
+}
+
+struct FrameBuilder {
+    file_hash: BinaryChunkedBuilder,
+    nar_hash: BinaryChunkedBuilder,
+    chunk: AnonymousOwnedListBuilder,
+}
+
+impl FrameBuilder {
+    fn new() -> Self {
+        Self {
+            file_hash: BinaryChunkedBuilder::new("file_hash", 0, 0),
+            nar_hash: BinaryChunkedBuilder::new("nar_hash", 0, 0),
+            chunk: AnonymousOwnedListBuilder::new(
+                "chunk",
+                0,
+                Some(DataType::Struct(vec![
+                    Field::new("hash", DataType::Binary),
+                    Field::new("size", DataType::UInt32),
+                    Field::new("size_compressed", DataType::UInt32),
+                ])),
+            ),
+        }
+    }
+
+    fn push(&mut self, file_hash: [u8; 32], pb: proto::Path) {
+        self.file_hash.append_value(&file_hash[..]);
+        self.nar_hash.append_value(pb.nar_hash);
+        self.chunk
+            .append_series(&ChunkFrameBuilder::new(pb.node.unwrap()))
+            .unwrap();
+    }
+
+    fn finish(mut self) -> DataFrame {
+        df! {
+            "file_hash" => self.file_hash.finish().into_series(),
+            "nar_hash" => self.nar_hash.finish().into_series(),
+            "chunk" => self.chunk.finish().into_series()
+        }
+        .unwrap()
+    }
+}
+
+struct ChunkFrameBuilder {
+    hash: BinaryChunkedBuilder,
+    size: PrimitiveChunkedBuilder<UInt32Type>,
+    size_compressed: PrimitiveChunkedBuilder<UInt32Type>,
+}
+
+impl ChunkFrameBuilder {
+    fn new(node: proto::path::Node) -> Series {
+        let mut this = Self {
+            hash: BinaryChunkedBuilder::new("hash", 0, 0),
+            size: PrimitiveChunkedBuilder::new("size", 0),
+            size_compressed: PrimitiveChunkedBuilder::new("size_compressed", 0),
+        };
+
+        this.push(node);
+        this.finish()
+    }
+
+    fn push(&mut self, node: Node) {
+        match node {
+            Node::Directory(node) => {
+                for node in node.files {
+                    self.push(Node::File(node));
+                }
+
+                for node in node.directories {
+                    self.push(Node::Directory(node));
+                }
+            }
+            Node::File(node) => {
+                for chunk in node.chunks {
+                    self.hash.append_value(&chunk.hash);
+                    self.size.append_value(chunk.size);
+                    self.size_compressed.append_value(chunk.size_compressed);
+                }
+            }
+            Node::Symlink(_) => {}
+        }
+    }
+
+    fn finish(self) -> Series {
+        df! {
+            "hash" => self.hash.finish().into_series(),
+            "size" => self.size.finish().into_series(),
+            "size_compressed" => self.size_compressed.finish().into_series()
+        }
+        .unwrap()
+        .into_struct("chunk")
+        .into_series()
+    }
+}
diff --git a/tvix/tools/crunch-v2/src/lib.rs b/tvix/tools/crunch-v2/src/lib.rs
new file mode 100644
index 0000000000..09ea2e75d5
--- /dev/null
+++ b/tvix/tools/crunch-v2/src/lib.rs
@@ -0,0 +1,3 @@
+pub mod proto {
+    include!(concat!(env!("OUT_DIR"), "/tvix.flatstore.v1.rs"));
+}
diff --git a/tvix/tools/crunch-v2/src/main.rs b/tvix/tools/crunch-v2/src/main.rs
new file mode 100644
index 0000000000..a5d538f6be
--- /dev/null
+++ b/tvix/tools/crunch-v2/src/main.rs
@@ -0,0 +1,309 @@
+//! This is a tool for ingesting subsets of cache.nixos.org into its own flattened castore format.
+//! Currently, produced chunks are not preserved, and this purely serves as a way of measuring
+//! compression/deduplication ratios for various chunking and compression parameters.
+//!
+//! NARs to be ingested are read from `ingest.parquet`, and filtered by an SQL expression provided as a program argument.
+//! The `file_hash` column should contain SHA-256 hashes of the compressed data, corresponding to the `FileHash` narinfo field.
+//! The `compression` column should contain either `"bzip2"` or `"xz"`, corresponding to the `Compression` narinfo field.
+//! Additional columns are ignored, but can be used by the SQL filter expression.
+//!
+//! flatstore protobufs are written to a sled database named `crunch.db`, addressed by file hash.
+
+use crunch_v2::proto;
+
+mod remote;
+
+use anyhow::Result;
+use clap::Parser;
+use futures::{stream, StreamExt, TryStreamExt};
+use indicatif::{ProgressBar, ProgressStyle};
+use std::{
+    io::{self, BufRead, Read, Write},
+    path::PathBuf,
+    ptr,
+};
+
+use polars::{
+    prelude::{col, LazyFrame, ScanArgsParquet},
+    sql::sql_expr,
+};
+
+use fastcdc::v2020::{ChunkData, StreamCDC};
+use nix_compat::nar::reader as nar;
+
+use digest::Digest;
+use prost::Message;
+use sha2::Sha256;
+
+#[derive(Parser)]
+struct Args {
+    /// Path to an existing parquet file.
+    /// The `file_hash` column should contain SHA-256 hashes of the compressed
+    /// data, corresponding to the `FileHash` narinfo field.
+    /// The `compression` column should contain either `"bzip2"` or `"xz"`,
+    /// corresponding to the `Compression` narinfo field.
+    /// Additional columns are ignored, but can be used by the SQL filter expression.
+    #[clap(long, default_value = "ingest.parquet")]
+    infile: PathBuf,
+
+    /// Filter expression to filter elements in the parquet file for.
+    filter: String,
+
+    /// Average chunk size for FastCDC, in KiB.
+    /// min value is half, max value double of that number.
+    #[clap(long, default_value_t = 256)]
+    avg_chunk_size: u32,
+
+    /// Path to the sled database where results are written to (flatstore
+    /// protobufs, addressed by file hash).
+    #[clap(long, default_value = "crunch.db")]
+    outfile: PathBuf,
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+    let args = Args::parse();
+
+    let filter = sql_expr(args.filter)?;
+    let avg_chunk_size = args.avg_chunk_size * 1024;
+
+    let df = LazyFrame::scan_parquet(&args.infile, ScanArgsParquet::default())?
+        .filter(filter)
+        .select([col("file_hash"), col("compression")])
+        .drop_nulls(None)
+        .collect()?;
+
+    let progress = ProgressBar::new(df.height() as u64).with_style(ProgressStyle::with_template(
+        "{elapsed_precise}/{duration_precise} {wide_bar} {pos}/{len}",
+    )?);
+
+    let file_hash = df
+        .column("file_hash")?
+        .binary()?
+        .into_iter()
+        .map(|h| -> [u8; 32] { h.unwrap().try_into().unwrap() });
+
+    let compression = df
+        .column("compression")?
+        .utf8()?
+        .into_iter()
+        .map(|c| c.unwrap());
+
+    let db: sled::Db = sled::open(args.outfile).unwrap();
+    let files_tree = db.open_tree("files").unwrap();
+
+    let res = stream::iter(file_hash.zip(compression))
+        .map(Ok)
+        .try_for_each_concurrent(Some(16), |(file_hash, compression)| {
+            let progress = progress.clone();
+            let files_tree = files_tree.clone();
+            async move {
+                if files_tree.contains_key(&file_hash)? {
+                    progress.inc(1);
+                    return Ok(());
+                }
+
+                let reader = remote::nar(file_hash, compression).await?;
+
+                tokio::task::spawn_blocking(move || {
+                    let mut reader = Sha256Reader::from(reader);
+
+                    let path =
+                        ingest(nar::open(&mut reader)?, vec![], avg_chunk_size).map(|node| {
+                            proto::Path {
+                                nar_hash: reader.finalize().as_slice().into(),
+                                node: Some(node),
+                            }
+                        })?;
+
+                    files_tree.insert(file_hash, path.encode_to_vec())?;
+                    progress.inc(1);
+
+                    Ok::<_, anyhow::Error>(())
+                })
+                .await?
+            }
+        })
+        .await;
+
+    let flush = files_tree.flush_async().await;
+
+    res?;
+    flush?;
+
+    Ok(())
+}
+
+fn ingest(node: nar::Node, name: Vec<u8>, avg_chunk_size: u32) -> Result<proto::path::Node> {
+    match node {
+        nar::Node::Symlink { target } => Ok(proto::path::Node::Symlink(proto::SymlinkNode {
+            name,
+            target,
+        })),
+
+        nar::Node::Directory(mut reader) => {
+            let mut directories = vec![];
+            let mut files = vec![];
+            let mut symlinks = vec![];
+
+            while let Some(node) = reader.next()? {
+                match ingest(node.node, node.name, avg_chunk_size)? {
+                    proto::path::Node::Directory(node) => {
+                        directories.push(node);
+                    }
+                    proto::path::Node::File(node) => {
+                        files.push(node);
+                    }
+                    proto::path::Node::Symlink(node) => {
+                        symlinks.push(node);
+                    }
+                }
+            }
+
+            Ok(proto::path::Node::Directory(proto::DirectoryNode {
+                name,
+                directories,
+                files,
+                symlinks,
+            }))
+        }
+
+        nar::Node::File { executable, reader } => {
+            let mut reader = B3Reader::from(reader);
+            let mut chunks = vec![];
+
+            for chunk in StreamCDC::new(
+                &mut reader,
+                avg_chunk_size / 2,
+                avg_chunk_size,
+                avg_chunk_size * 2,
+            ) {
+                let ChunkData {
+                    length: size, data, ..
+                } = chunk?;
+
+                let hash = blake3::hash(&data);
+                let size_compressed = zstd_size(&data, 9);
+
+                chunks.push(proto::Chunk {
+                    hash: hash.as_bytes().as_slice().into(),
+                    size: size.try_into().unwrap(),
+                    size_compressed: size_compressed.try_into().unwrap(),
+                });
+            }
+
+            Ok(proto::path::Node::File(proto::FileNode {
+                name,
+                hash: reader.finalize().as_bytes().as_slice().into(),
+                chunks,
+                executable,
+            }))
+        }
+    }
+}
+
+struct Sha256Reader<R> {
+    inner: R,
+    hasher: Sha256,
+    buf: *const [u8],
+}
+
+const ZERO_BUF: *const [u8] = ptr::slice_from_raw_parts(1 as *const u8, 0);
+
+unsafe impl<R: Send> Send for Sha256Reader<R> {}
+
+impl<R> From<R> for Sha256Reader<R> {
+    fn from(value: R) -> Self {
+        Self {
+            inner: value,
+            hasher: Sha256::new(),
+            buf: ZERO_BUF,
+        }
+    }
+}
+
+impl<R: Read> Read for Sha256Reader<R> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        self.buf = ZERO_BUF;
+        let n = self.inner.read(buf)?;
+        self.hasher.update(&buf[..n]);
+        Ok(n)
+    }
+}
+
+impl<R: BufRead> BufRead for Sha256Reader<R> {
+    fn fill_buf(&mut self) -> io::Result<&[u8]> {
+        self.buf = ZERO_BUF;
+        let buf = self.inner.fill_buf()?;
+        self.buf = buf as *const [u8];
+        Ok(buf)
+    }
+
+    fn consume(&mut self, amt: usize) {
+        // UNSAFETY: This assumes that `R::consume` doesn't invalidate the buffer.
+        // That's not a sound assumption in general, though it is likely to hold.
+        // TODO(edef): refactor this codebase to write a fresh NAR for verification purposes
+        // we already buffer full chunks, so there's no pressing need to reuse the input buffers
+        unsafe {
+            let (head, buf) = (*self.buf).split_at(amt);
+            self.buf = buf as *const [u8];
+            self.hasher.update(head);
+            self.inner.consume(amt);
+        }
+    }
+}
+
+impl<R> Sha256Reader<R> {
+    fn finalize(self) -> [u8; 32] {
+        self.hasher.finalize().into()
+    }
+}
+
+struct B3Reader<R> {
+    inner: R,
+    hasher: blake3::Hasher,
+}
+
+impl<R> From<R> for B3Reader<R> {
+    fn from(value: R) -> Self {
+        Self {
+            inner: value,
+            hasher: blake3::Hasher::new(),
+        }
+    }
+}
+
+impl<R: Read> Read for B3Reader<R> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        let n = self.inner.read(buf)?;
+        self.hasher.update(&buf[..n]);
+        Ok(n)
+    }
+}
+
+impl<R> B3Reader<R> {
+    fn finalize(self) -> blake3::Hash {
+        self.hasher.finalize()
+    }
+}
+
+fn zstd_size(data: &[u8], level: i32) -> u64 {
+    let mut w = zstd::Encoder::new(CountingWriter::default(), level).unwrap();
+    w.write_all(&data).unwrap();
+    let CountingWriter(size) = w.finish().unwrap();
+    size
+}
+
+#[derive(Default)]
+struct CountingWriter(u64);
+
+impl Write for CountingWriter {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.0 += buf.len() as u64;
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
diff --git a/tvix/tools/crunch-v2/src/remote.rs b/tvix/tools/crunch-v2/src/remote.rs
new file mode 100644
index 0000000000..93952ecd73
--- /dev/null
+++ b/tvix/tools/crunch-v2/src/remote.rs
@@ -0,0 +1,211 @@
+use std::{
+    cmp,
+    io::{self, BufRead, BufReader, Read},
+    pin::Pin,
+    task::{self, Poll},
+};
+
+use anyhow::{bail, Result};
+use bytes::{Buf, Bytes};
+use futures::{future::BoxFuture, Future, FutureExt, Stream, StreamExt};
+use lazy_static::lazy_static;
+use tokio::runtime::Handle;
+
+use nix_compat::nixbase32;
+
+use rusoto_core::{ByteStream, Region};
+use rusoto_s3::{GetObjectOutput, GetObjectRequest, S3Client, S3};
+
+use bzip2::read::BzDecoder;
+use xz2::read::XzDecoder;
+
+lazy_static! {
+    static ref S3_CLIENT: S3Client = S3Client::new(Region::UsEast1);
+}
+
+const BUCKET: &str = "nix-cache";
+
+pub async fn nar(
+    file_hash: [u8; 32],
+    compression: &str,
+) -> Result<Box<BufReader<dyn Read + Send>>> {
+    let (extension, decompress): (&'static str, fn(_) -> Box<_>) = match compression {
+        "bzip2" => ("bz2", decompress_bz2),
+        "xz" => ("xz", decompress_xz),
+        _ => bail!("unknown compression: {compression}"),
+    };
+
+    Ok(decompress(
+        FileStream::new(FileKey {
+            file_hash,
+            extension,
+        })
+        .await?
+        .into(),
+    ))
+}
+
+fn decompress_xz(reader: FileStreamReader) -> Box<BufReader<dyn Read + Send>> {
+    Box::new(BufReader::new(XzDecoder::new(reader)))
+}
+
+fn decompress_bz2(reader: FileStreamReader) -> Box<BufReader<dyn Read + Send>> {
+    Box::new(BufReader::new(BzDecoder::new(reader)))
+}
+
+struct FileStreamReader {
+    inner: FileStream,
+    buffer: Bytes,
+}
+
+impl From<FileStream> for FileStreamReader {
+    fn from(value: FileStream) -> Self {
+        FileStreamReader {
+            inner: value,
+            buffer: Bytes::new(),
+        }
+    }
+}
+
+impl Read for FileStreamReader {
+    fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
+        let src = self.fill_buf()?;
+        let n = cmp::min(src.len(), dst.len());
+        dst[..n].copy_from_slice(&src[..n]);
+        self.consume(n);
+        Ok(n)
+    }
+}
+
+impl BufRead for FileStreamReader {
+    fn fill_buf(&mut self) -> io::Result<&[u8]> {
+        if !self.buffer.is_empty() {
+            return Ok(&self.buffer);
+        }
+
+        self.buffer = Handle::current()
+            .block_on(self.inner.next())
+            .transpose()?
+            .unwrap_or_default();
+
+        Ok(&self.buffer)
+    }
+
+    fn consume(&mut self, cnt: usize) {
+        self.buffer.advance(cnt);
+    }
+}
+
+struct FileKey {
+    file_hash: [u8; 32],
+    extension: &'static str,
+}
+
+impl FileKey {
+    fn get(
+        &self,
+        offset: u64,
+        e_tag: Option<&str>,
+    ) -> impl Future<Output = io::Result<GetObjectOutput>> + Send + 'static {
+        let input = GetObjectRequest {
+            bucket: BUCKET.to_string(),
+            key: format!(
+                "nar/{}.nar.{}",
+                nixbase32::encode(&self.file_hash),
+                self.extension
+            ),
+            if_match: e_tag.map(str::to_owned),
+            range: Some(format!("bytes {}-", offset + 1)),
+            ..Default::default()
+        };
+
+        async {
+            S3_CLIENT
+                .get_object(input)
+                .await
+                .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
+        }
+    }
+}
+
+struct FileStream {
+    key: FileKey,
+    e_tag: String,
+    offset: u64,
+    length: u64,
+    inner: FileStreamState,
+}
+
+enum FileStreamState {
+    Response(BoxFuture<'static, io::Result<GetObjectOutput>>),
+    Body(ByteStream),
+    Eof,
+}
+
+impl FileStream {
+    pub async fn new(key: FileKey) -> io::Result<Self> {
+        let resp = key.get(0, None).await?;
+
+        Ok(FileStream {
+            key,
+            e_tag: resp.e_tag.unwrap(),
+            offset: 0,
+            length: resp.content_length.unwrap().try_into().unwrap(),
+            inner: FileStreamState::Body(resp.body.unwrap()),
+        })
+    }
+}
+
+macro_rules! poll {
+    ($expr:expr) => {
+        match $expr {
+            Poll::Pending => {
+                return Poll::Pending;
+            }
+            Poll::Ready(value) => value,
+        }
+    };
+}
+
+impl Stream for FileStream {
+    type Item = io::Result<Bytes>;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll<Option<Self::Item>> {
+        let this = self.get_mut();
+
+        let chunk = loop {
+            match &mut this.inner {
+                FileStreamState::Response(resp) => match poll!(resp.poll_unpin(cx)) {
+                    Err(err) => {
+                        this.inner = FileStreamState::Eof;
+                        return Poll::Ready(Some(Err(err)));
+                    }
+                    Ok(resp) => {
+                        this.inner = FileStreamState::Body(resp.body.unwrap());
+                    }
+                },
+                FileStreamState::Body(body) => match poll!(body.poll_next_unpin(cx)) {
+                    None | Some(Err(_)) => {
+                        this.inner = FileStreamState::Response(
+                            this.key.get(this.offset, Some(&this.e_tag)).boxed(),
+                        );
+                    }
+                    Some(Ok(chunk)) => {
+                        break chunk;
+                    }
+                },
+                FileStreamState::Eof => {
+                    return Poll::Ready(None);
+                }
+            }
+        };
+
+        this.offset += chunk.len() as u64;
+
+        if this.offset >= this.length {
+            this.inner = FileStreamState::Eof;
+        }
+
+        Poll::Ready(Some(Ok(chunk)))
+    }
+}
diff --git a/tvix/tools/narinfo2parquet/Cargo.lock b/tvix/tools/narinfo2parquet/Cargo.lock
new file mode 100644
index 0000000000..e59f70732d
--- /dev/null
+++ b/tvix/tools/narinfo2parquet/Cargo.lock
@@ -0,0 +1,2144 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
+name = "argminmax"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "202108b46429b765ef483f8a24d5c46f48c14acfdacc086dd4ab6dddf6bcdbd2"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "array-init-cursor"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7d0a018de4f6aa429b9d33d69edf69072b1c5b1cb8d3e4a5f7ef898fc3eb76"
+
+[[package]]
+name = "arrow-format"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07884ea216994cdc32a2d5f8274a8bee979cfe90274b83f86f440866ee3132c7"
+dependencies = [
+ "planus",
+ "serde",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "atoi"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "atoi_simd"
+version = "0.15.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ae037714f313c1353189ead58ef9eec30a8e8dc101b2622d461418fd59e28a9"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "brotli"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
+
+[[package]]
+name = "brotli-decompressor"
+version = "2.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+]
+
+[[package]]
+name = "bstr"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019"
+dependencies = [
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "bytemuck"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "jobserver",
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "platforms",
+ "rustc_version",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+
+[[package]]
+name = "der"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "enum_dispatch"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e"
+dependencies = [
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "ethnum"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "fast-float"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c"
+
+[[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7"
+
+[[package]]
+name = "flate2"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "foreign_vec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee1b05cbd864bcaecbd3455d6d967862d446e4ebfc3c2e5e5b9841e53cba6673"
+
+[[package]]
+name = "futures"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
+
+[[package]]
+name = "futures-task"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
+
+[[package]]
+name = "futures-util"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+ "rayon",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
+
+[[package]]
+name = "home"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core 0.51.1",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "jemalloc-sys"
+version = "0.5.4+5.3.0-patched"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "jemallocator"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc"
+dependencies = [
+ "jemalloc-sys",
+ "libc",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "lz4"
+version = "1.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1"
+dependencies = [
+ "libc",
+ "lz4-sys",
+]
+
+[[package]]
+name = "lz4-sys"
+version = "1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "memmap2"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "multiversion"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2c7b9d7fe61760ce5ea19532ead98541f6b4c495d87247aff9826445cf6872a"
+dependencies = [
+ "multiversion-macros",
+ "target-features",
+]
+
+[[package]]
+name = "multiversion-macros"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26a83d8500ed06d68877e9de1dde76c1dbb83885dcdbda4ef44ccbc3fbda2ac8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "target-features",
+]
+
+[[package]]
+name = "narinfo2parquet"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "jemallocator",
+ "nix-compat",
+ "polars",
+ "tempfile-fast",
+ "zstd",
+]
+
+[[package]]
+name = "nix-compat"
+version = "0.1.0"
+dependencies = [
+ "bitflags 2.4.1",
+ "bstr",
+ "data-encoding",
+ "ed25519",
+ "ed25519-dalek",
+ "glob",
+ "nom",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "now"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d89e9874397a1f0a52fc1f197a8effd9735223cb2390e9dcc83ac6cd02923d0"
+dependencies = [
+ "chrono",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "parquet-format-safe"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1131c54b167dd4e4799ce762e1ab01549ebb94d5bdd13e6ec1b467491c378e1f"
+dependencies = [
+ "async-trait",
+ "futures",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "planus"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1691dd09e82f428ce8d6310bd6d5da2557c82ff17694d2a32cad7242aea89f"
+dependencies = [
+ "array-init-cursor",
+]
+
+[[package]]
+name = "platforms"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0"
+
+[[package]]
+name = "polars"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938048fcda6a8e2ace6eb168bee1b415a92423ce51e418b853bf08fc40349b6b"
+dependencies = [
+ "getrandom",
+ "polars-core",
+ "polars-io",
+ "polars-lazy",
+ "polars-ops",
+ "polars-sql",
+ "version_check",
+]
+
+[[package]]
+name = "polars-arrow"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce68a02f698ff7787c261aea1b4c040a8fe183a8fb200e2436d7f35d95a1b86f"
+dependencies = [
+ "ahash",
+ "arrow-format",
+ "atoi_simd",
+ "bytemuck",
+ "chrono",
+ "dyn-clone",
+ "either",
+ "ethnum",
+ "fast-float",
+ "foreign_vec",
+ "futures",
+ "getrandom",
+ "hashbrown",
+ "itoa",
+ "lz4",
+ "multiversion",
+ "num-traits",
+ "polars-error",
+ "polars-utils",
+ "ryu",
+ "simdutf8",
+ "streaming-iterator",
+ "strength_reduce",
+ "version_check",
+ "zstd",
+]
+
+[[package]]
+name = "polars-compute"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b14fbc5f141b29b656a4cec4802632e5bff10bf801c6809c6bbfbd4078a044dd"
+dependencies = [
+ "bytemuck",
+ "num-traits",
+ "polars-arrow",
+ "polars-utils",
+ "version_check",
+]
+
+[[package]]
+name = "polars-core"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0f5efe734b6cbe5f97ea769be8360df5324fade396f1f3f5ad7fe9360ca4a23"
+dependencies = [
+ "ahash",
+ "bitflags 2.4.1",
+ "bytemuck",
+ "chrono",
+ "either",
+ "hashbrown",
+ "indexmap",
+ "num-traits",
+ "once_cell",
+ "polars-arrow",
+ "polars-compute",
+ "polars-error",
+ "polars-row",
+ "polars-utils",
+ "rand",
+ "rand_distr",
+ "rayon",
+ "regex",
+ "smartstring",
+ "thiserror",
+ "version_check",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "polars-error"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6396de788f99ebfc9968e7b6f523e23000506cde4ba6dfc62ae4ce949002a886"
+dependencies = [
+ "arrow-format",
+ "regex",
+ "simdutf8",
+ "thiserror",
+]
+
+[[package]]
+name = "polars-io"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d0458efe8946f4718fd352f230c0db5a37926bd0d2bd25af79dc24746abaaea"
+dependencies = [
+ "ahash",
+ "async-trait",
+ "bytes",
+ "futures",
+ "home",
+ "memchr",
+ "memmap2",
+ "num-traits",
+ "once_cell",
+ "percent-encoding",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-parquet",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "smartstring",
+ "tokio",
+ "tokio-util",
+]
+
+[[package]]
+name = "polars-lazy"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d7105b40905bb38e8fc4a7fd736594b7491baa12fad3ac492969ca221a1b5d5"
+dependencies = [
+ "ahash",
+ "bitflags 2.4.1",
+ "glob",
+ "once_cell",
+ "polars-arrow",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-pipe",
+ "polars-plan",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "smartstring",
+ "version_check",
+]
+
+[[package]]
+name = "polars-ops"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e09afc456ab11e75e5dcb43e00a01c71f3a46a2781e450054acb6bb096ca78e"
+dependencies = [
+ "ahash",
+ "argminmax",
+ "bytemuck",
+ "either",
+ "hashbrown",
+ "indexmap",
+ "memchr",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-error",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "smartstring",
+ "version_check",
+]
+
+[[package]]
+name = "polars-parquet"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ba24d67b1f64ab85143033dd46fa090b13c0f74acdf91b0780c16aecf005e3d"
+dependencies = [
+ "ahash",
+ "async-stream",
+ "base64",
+ "brotli",
+ "ethnum",
+ "flate2",
+ "futures",
+ "lz4",
+ "num-traits",
+ "parquet-format-safe",
+ "polars-arrow",
+ "polars-error",
+ "polars-utils",
+ "seq-macro",
+ "simdutf8",
+ "snap",
+ "streaming-decompression",
+ "zstd",
+]
+
+[[package]]
+name = "polars-pipe"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b7ead073cc3917027d77b59861a9f071db47125de9314f8907db1a0a3e4100"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-queue",
+ "enum_dispatch",
+ "hashbrown",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-plan",
+ "polars-row",
+ "polars-utils",
+ "rayon",
+ "smartstring",
+ "version_check",
+]
+
+[[package]]
+name = "polars-plan"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384a175624d050c31c473ee11df9d7af5d729ae626375e522158cfb3d150acd0"
+dependencies = [
+ "ahash",
+ "bytemuck",
+ "once_cell",
+ "percent-encoding",
+ "polars-arrow",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-parquet",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "smartstring",
+ "strum_macros",
+ "version_check",
+]
+
+[[package]]
+name = "polars-row"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32322f7acbb83db3e9c7697dc821be73d06238da89c817dcc8bc1549a5e9c72f"
+dependencies = [
+ "polars-arrow",
+ "polars-error",
+ "polars-utils",
+]
+
+[[package]]
+name = "polars-sql"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f0b4c6ddffdfd0453e84bc3918572c633014d661d166654399cf93752aa95b5"
+dependencies = [
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-lazy",
+ "polars-plan",
+ "rand",
+ "serde",
+ "serde_json",
+ "sqlparser",
+]
+
+[[package]]
+name = "polars-time"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee2649fc96bd1b6584e0e4a4b3ca7d22ed3d117a990e63ad438ecb26f7544d0"
+dependencies = [
+ "atoi",
+ "chrono",
+ "now",
+ "once_cell",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-ops",
+ "polars-utils",
+ "regex",
+ "smartstring",
+]
+
+[[package]]
+name = "polars-utils"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b174ca4a77ad47d7b91a0460aaae65bbf874c8bfbaaa5308675dadef3976bbda"
+dependencies = [
+ "ahash",
+ "bytemuck",
+ "hashbrown",
+ "indexmap",
+ "num-traits",
+ "once_cell",
+ "polars-error",
+ "rayon",
+ "smartstring",
+ "sysinfo",
+ "version_check",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_distr"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+dependencies = [
+ "num-traits",
+ "rand",
+]
+
+[[package]]
+name = "rayon"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+dependencies = [
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
+
+[[package]]
+name = "seq-macro"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
+
+[[package]]
+name = "serde"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "simdutf8"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smartstring"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
+dependencies = [
+ "autocfg",
+ "static_assertions",
+ "version_check",
+]
+
+[[package]]
+name = "snap"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831"
+
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "spki"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "sqlparser"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743b4dc2cbde11890ccb254a8fc9d537fa41b36da00de2a1c5e9848c9bc42bd7"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "streaming-decompression"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf6cc3b19bfb128a8ad11026086e31d3ce9ad23f8ea37354b31383a187c44cf3"
+dependencies = [
+ "fallible-streaming-iterator",
+]
+
+[[package]]
+name = "streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
+
+[[package]]
+name = "strength_reduce"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
+
+[[package]]
+name = "strum_macros"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.30.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2"
+dependencies = [
+ "cfg-if",
+ "core-foundation-sys",
+ "libc",
+ "ntapi",
+ "once_cell",
+ "windows",
+]
+
+[[package]]
+name = "target-features"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfb5fa503293557c5158bd215fdc225695e567a77e453f5d4452a50a193969bd"
+
+[[package]]
+name = "tempfile"
+version = "3.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "tempfile-fast"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a74be8531b1a9d607004a32b8f50dd8093b09ec6b0a6af004e33051068e87af6"
+dependencies = [
+ "libc",
+ "rand",
+ "tempfile",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "tokio"
+version = "1.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "pin-project-lite",
+ "socket2",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+dependencies = [
+ "windows-core 0.52.0",
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.51.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
+[[package]]
+name = "xxhash-rust"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9828b178da53440fa9c766a3d2f73f7cf5d0ac1fe3980c1e5018d899fd19e07b"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+
+[[package]]
+name = "zstd"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.9+zstd.1.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
diff --git a/tvix/tools/narinfo2parquet/Cargo.nix b/tvix/tools/narinfo2parquet/Cargo.nix
new file mode 100644
index 0000000000..27a5d684b6
--- /dev/null
+++ b/tvix/tools/narinfo2parquet/Cargo.nix
@@ -0,0 +1,8528 @@
+# This file was @generated by crate2nix 0.13.0 with the command:
+#   "generate" "--all-features"
+# See https://github.com/kolloch/crate2nix for more info.
+
+{ nixpkgs ? <nixpkgs>
+, pkgs ? import nixpkgs { config = { }; }
+, lib ? pkgs.lib
+, stdenv ? pkgs.stdenv
+, buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate
+  # This is used as the `crateOverrides` argument for `buildRustCrate`.
+, defaultCrateOverrides ? pkgs.defaultCrateOverrides
+  # The features to enable for the root_crate or the workspace_members.
+, rootFeatures ? [ "default" ]
+  # If true, throw errors instead of issueing deprecation warnings.
+, strictDeprecation ? false
+  # Used for conditional compilation based on CPU feature detection.
+, targetFeatures ? [ ]
+  # Whether to perform release builds: longer compile times, faster binaries.
+, release ? true
+  # Additional crate2nix configuration if it exists.
+, crateConfig ? if builtins.pathExists ./crate-config.nix
+  then pkgs.callPackage ./crate-config.nix { }
+  else { }
+}:
+
+rec {
+  #
+  # "public" attributes that we attempt to keep stable with new versions of crate2nix.
+  #
+
+  rootCrate = rec {
+    packageId = "narinfo2parquet";
+
+    # Use this attribute to refer to the derivation building your root crate package.
+    # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }.
+    build = internal.buildRustCrateWithFeatures {
+      inherit packageId;
+    };
+
+    # Debug support which might change between releases.
+    # File a bug if you depend on any for non-debug work!
+    debug = internal.debugCrate { inherit packageId; };
+  };
+  # Refer your crate build derivation by name here.
+  # You can override the features with
+  # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }.
+  workspaceMembers = {
+    "narinfo2parquet" = rec {
+      packageId = "narinfo2parquet";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "narinfo2parquet";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+  };
+
+  # A derivation that joins the outputs of all workspace members together.
+  allWorkspaceMembers = pkgs.symlinkJoin {
+    name = "all-workspace-members";
+    paths =
+      let members = builtins.attrValues workspaceMembers;
+      in builtins.map (m: m.build) members;
+  };
+
+  #
+  # "internal" ("private") attributes that may change in every new version of crate2nix.
+  #
+
+  internal = rec {
+    # Build and dependency information for crates.
+    # Many of the fields are passed one-to-one to buildRustCrate.
+    #
+    # Noteworthy:
+    # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate.
+    #   but with additional information which is used during dependency/feature resolution.
+    # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging.
+    # * `devDependencies` as of now not used by `buildRustCrate` but used to
+    #   inject test dependencies into the build
+
+    crates = {
+      "addr2line" = rec {
+        crateName = "addr2line";
+        version = "0.21.0";
+        edition = "2018";
+        sha256 = "1jx0k3iwyqr8klqbzk6kjvr496yd94aspis10vwsj5wy7gib4c4a";
+        dependencies = [
+          {
+            name = "gimli";
+            packageId = "gimli";
+            usesDefaultFeatures = false;
+            features = [ "read" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "rustc-demangle" "cpp_demangle" "std-object" "fallible-iterator" "smallvec" "memmap2" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "memmap2" = [ "dep:memmap2" ];
+          "object" = [ "dep:object" ];
+          "rustc-demangle" = [ "dep:rustc-demangle" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "gimli/rustc-dep-of-std" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "gimli/std" ];
+          "std-object" = [ "std" "object" "object/std" "object/compression" "gimli/endian-reader" ];
+        };
+      };
+      "adler" = rec {
+        crateName = "adler";
+        version = "1.0.2";
+        edition = "2015";
+        sha256 = "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj";
+        authors = [
+          "Jonas Schievink <jonasschievink@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "ahash" = rec {
+        crateName = "ahash";
+        version = "0.8.6";
+        edition = "2018";
+        sha256 = "0yn9i8nc6mmv28ig9w3dga571q09vg9f1f650mi5z8phx42r6hli";
+        authors = [
+          "Tom Kaitchuck <Tom.Kaitchuck@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!(("arm" == target."arch" or null) && ("none" == target."os" or null)));
+            features = [ "unstable" "alloc" ];
+          }
+          {
+            name = "zerocopy";
+            packageId = "zerocopy";
+            usesDefaultFeatures = false;
+            features = [ "simd" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "atomic-polyfill" = [ "dep:atomic-polyfill" "once_cell/atomic-polyfill" ];
+          "compile-time-rng" = [ "const-random" ];
+          "const-random" = [ "dep:const-random" ];
+          "default" = [ "std" "runtime-rng" ];
+          "getrandom" = [ "dep:getrandom" ];
+          "runtime-rng" = [ "getrandom" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "getrandom" "runtime-rng" "std" ];
+      };
+      "aho-corasick" = rec {
+        crateName = "aho-corasick";
+        version = "1.1.2";
+        edition = "2021";
+        sha256 = "1w510wnixvlgimkx1zjbvlxh6xps2vjgfqgwf5a6adlbjp5rv5mj";
+        libName = "aho_corasick";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf-literal" ];
+          "logging" = [ "dep:log" ];
+          "perf-literal" = [ "dep:memchr" ];
+          "std" = [ "memchr?/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "perf-literal" "std" ];
+      };
+      "alloc-no-stdlib" = rec {
+        crateName = "alloc-no-stdlib";
+        version = "2.0.4";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "1cy6r2sfv5y5cigv86vms7n5nlwhx1rbyxwcraqnmm1rxiib2yyc";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+        ];
+        features = { };
+      };
+      "alloc-stdlib" = rec {
+        crateName = "alloc-stdlib";
+        version = "0.2.2";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "1kkfbld20ab4165p29v172h8g0wvq8i06z8vnng14whw0isq5ywl";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+        ];
+        dependencies = [
+          {
+            name = "alloc-no-stdlib";
+            packageId = "alloc-no-stdlib";
+          }
+        ];
+        features = { };
+      };
+      "allocator-api2" = rec {
+        crateName = "allocator-api2";
+        version = "0.2.16";
+        edition = "2018";
+        sha256 = "1iayppgq4wqbfbfcqmsbwgamj0s65012sskfvyx07pxavk3gyhh9";
+        authors = [
+          "Zakarum <zaq.dev@icloud.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "android-tzdata" = rec {
+        crateName = "android-tzdata";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        authors = [
+          "RumovZ"
+        ];
+
+      };
+      "android_system_properties" = rec {
+        crateName = "android_system_properties";
+        version = "0.1.5";
+        edition = "2018";
+        sha256 = "04b3wrz12837j7mdczqd95b732gw5q7q66cv4yn4646lvccp57l1";
+        authors = [
+          "Nicolas Silva <nical@fastmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "anyhow" = rec {
+        crateName = "anyhow";
+        version = "1.0.75";
+        edition = "2018";
+        sha256 = "1rmcjkim91c5mw7h9wn8nv0k6x118yz0xg0z1q18svgn42mqqrm4";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            optional = true;
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "backtrace" "default" "std" ];
+      };
+      "argminmax" = rec {
+        crateName = "argminmax";
+        version = "0.6.1";
+        edition = "2021";
+        sha256 = "1lnvpkvdsvdbsinhik6srx5c2j3gqkaj92iz93pnbdr9cjs0h890";
+        authors = [
+          "Jeroen Van Der Donckt"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "arrow" = [ "dep:arrow" ];
+          "arrow2" = [ "dep:arrow2" ];
+          "default" = [ "nightly_simd" "float" ];
+          "half" = [ "dep:half" ];
+          "ndarray" = [ "dep:ndarray" ];
+        };
+        resolvedDefaultFeatures = [ "float" ];
+      };
+      "array-init-cursor" = rec {
+        crateName = "array-init-cursor";
+        version = "0.2.0";
+        edition = "2021";
+        sha256 = "0xpbqf7qkvzplpjd7f0wbcf2n1v9vygdccwxkd1amxp4il0hlzdz";
+
+      };
+      "arrow-format" = rec {
+        crateName = "arrow-format";
+        version = "0.8.1";
+        edition = "2018";
+        sha256 = "1irj67p6c224dzw86jr7j3z9r5zfid52gy6ml8rdqk4r2si4x207";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "planus";
+            packageId = "planus";
+            optional = true;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "derive" "std" ];
+          }
+        ];
+        features = {
+          "flight-data" = [ "prost" "prost-derive" ];
+          "flight-service" = [ "flight-data" "tonic" ];
+          "full" = [ "ipc" "flight-data" "flight-service" ];
+          "ipc" = [ "planus" "serde" ];
+          "planus" = [ "dep:planus" ];
+          "prost" = [ "dep:prost" ];
+          "prost-derive" = [ "dep:prost-derive" ];
+          "serde" = [ "dep:serde" ];
+          "tonic" = [ "dep:tonic" ];
+        };
+        resolvedDefaultFeatures = [ "default" "ipc" "planus" "serde" ];
+      };
+      "async-stream" = rec {
+        crateName = "async-stream";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream-impl";
+            packageId = "async-stream-impl";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "async-stream-impl" = rec {
+        crateName = "async-stream-impl";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
+        procMacro = true;
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "async-trait" = rec {
+        crateName = "async-trait";
+        version = "0.1.74";
+        edition = "2021";
+        sha256 = "1ydhbsqjqqa6bxbv0kgys2wq2vi3jpwjy57dk162ajwppgqkfrd6";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "atoi" = rec {
+        crateName = "atoi";
+        version = "2.0.0";
+        edition = "2021";
+        sha256 = "0a05h42fggmy7h0ajjv6m7z72l924i7igbx13hk9d8pyign9k3gj";
+        authors = [
+          "Markus Klein"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "num-traits/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "atoi_simd" = rec {
+        crateName = "atoi_simd";
+        version = "0.15.6";
+        edition = "2018";
+        sha256 = "1a98kvaqyhb1shi2c6qhvklahc7ckvpmibcy319i6g1i9xqkgq4s";
+        authors = [
+          "Dmitry Rodionov <gh@rdmtr.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "arrayvec/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "autocfg" = rec {
+        crateName = "autocfg";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "1ylp3cb47ylzabimazvbz9ms6ap784zhb6syaz6c1jqpmcmq0s6l";
+        authors = [
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+
+      };
+      "backtrace" = rec {
+        crateName = "backtrace";
+        version = "0.3.69";
+        edition = "2018";
+        sha256 = "0dsq23dhw4pfndkx2nsa1ml2g31idm7ss7ljxp8d57avygivg290";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "addr2line";
+            packageId = "addr2line";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "object";
+            packageId = "object";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+            features = [ "read_core" "elf" "macho" "pe" "unaligned" "archive" ];
+          }
+          {
+            name = "rustc-demangle";
+            packageId = "rustc-demangle";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "std" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "serialize-rustc" = [ "rustc-serialize" ];
+          "serialize-serde" = [ "serde" ];
+          "verify-winapi" = [ "winapi/dbghelp" "winapi/handleapi" "winapi/libloaderapi" "winapi/memoryapi" "winapi/minwindef" "winapi/processthreadsapi" "winapi/synchapi" "winapi/tlhelp32" "winapi/winbase" "winapi/winnt" ];
+          "winapi" = [ "dep:winapi" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64" = rec {
+        crateName = "base64";
+        version = "0.21.5";
+        edition = "2018";
+        sha256 = "1y8x2xs9nszj5ix7gg4ycn5a6wy7ca74zxwqri3bdqzdjha6lqrm";
+        authors = [
+          "Alice Maz <alice@alicemaz.com>"
+          "Marshall Pierce <marshall@mpierce.org>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "base64ct" = rec {
+        crateName = "base64ct";
+        version = "1.6.0";
+        edition = "2021";
+        sha256 = "0nvdba4jb8aikv60az40x2w1y96sjdq8z3yp09rwzmkhiwv1lg4c";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "bitflags 1.3.2" = rec {
+        crateName = "bitflags";
+        version = "1.3.2";
+        edition = "2018";
+        sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bitflags 2.4.1" = rec {
+        crateName = "bitflags";
+        version = "2.4.1";
+        edition = "2021";
+        sha256 = "01ryy3kd671b0ll4bhdvhsz67vwz1lz53fz504injrd7wpv64xrj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "block-buffer" = rec {
+        crateName = "block-buffer";
+        version = "0.10.4";
+        edition = "2018";
+        sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+
+      };
+      "brotli" = rec {
+        crateName = "brotli";
+        version = "3.4.0";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "03qhcq09a6f8y4gm0bmsn7jrq5804cwpkcx3fyay1g7lgsj78q2i";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+          "The Brotli Authors"
+        ];
+        dependencies = [
+          {
+            name = "alloc-no-stdlib";
+            packageId = "alloc-no-stdlib";
+          }
+          {
+            name = "alloc-stdlib";
+            packageId = "alloc-stdlib";
+            optional = true;
+          }
+          {
+            name = "brotli-decompressor";
+            packageId = "brotli-decompressor";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc-stdlib" = [ "dep:alloc-stdlib" ];
+          "benchmark" = [ "brotli-decompressor/benchmark" ];
+          "default" = [ "std" "ffi-api" ];
+          "disable-timer" = [ "brotli-decompressor/disable-timer" ];
+          "seccomp" = [ "brotli-decompressor/seccomp" ];
+          "sha2" = [ "dep:sha2" ];
+          "std" = [ "alloc-stdlib" "brotli-decompressor/std" ];
+          "validation" = [ "sha2" ];
+        };
+        resolvedDefaultFeatures = [ "alloc-stdlib" "default" "ffi-api" "std" ];
+      };
+      "brotli-decompressor" = rec {
+        crateName = "brotli-decompressor";
+        version = "2.5.1";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "0kyyh9701dwqzwvn2frff4ww0zibikqd1s1xvl7n1pfpc3z4lbjf";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+          "The Brotli Authors"
+        ];
+        dependencies = [
+          {
+            name = "alloc-no-stdlib";
+            packageId = "alloc-no-stdlib";
+          }
+          {
+            name = "alloc-stdlib";
+            packageId = "alloc-stdlib";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc-stdlib" = [ "dep:alloc-stdlib" ];
+          "default" = [ "std" ];
+          "std" = [ "alloc-stdlib" ];
+          "unsafe" = [ "alloc-no-stdlib/unsafe" "alloc-stdlib/unsafe" ];
+        };
+        resolvedDefaultFeatures = [ "alloc-stdlib" "std" ];
+      };
+      "bstr" = rec {
+        crateName = "bstr";
+        version = "1.7.0";
+        edition = "2021";
+        sha256 = "06gh43qpgdqfsfpykw9y4708y0qclajwc2bbsymkv3yk5pxxg6n7";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "dfa-search" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "memchr/alloc" "serde?/alloc" ];
+          "default" = [ "std" "unicode" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" "memchr/std" "serde?/std" ];
+          "unicode" = [ "dep:regex-automata" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "serde" "std" "unicode" ];
+      };
+      "bumpalo" = rec {
+        crateName = "bumpalo";
+        version = "3.14.0";
+        edition = "2021";
+        sha256 = "1v4arnv9kwk54v5d0qqpv4vyw2sgr660nk0w3apzixi1cm3yfc3z";
+        authors = [
+          "Nick Fitzgerald <fitzgen@gmail.com>"
+        ];
+        features = {
+          "allocator-api2" = [ "dep:allocator-api2" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bytemuck" = rec {
+        crateName = "bytemuck";
+        version = "1.14.0";
+        edition = "2018";
+        sha256 = "1ik1ma5n3bg700skkzhx50zjk7kj7mbsphi773if17l04pn2hk9p";
+        authors = [
+          "Lokathor <zefria@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytemuck_derive";
+            packageId = "bytemuck_derive";
+            optional = true;
+          }
+        ];
+        features = {
+          "bytemuck_derive" = [ "dep:bytemuck_derive" ];
+          "derive" = [ "bytemuck_derive" ];
+          "extern_crate_std" = [ "extern_crate_alloc" ];
+        };
+        resolvedDefaultFeatures = [ "bytemuck_derive" "derive" "extern_crate_alloc" ];
+      };
+      "bytemuck_derive" = rec {
+        crateName = "bytemuck_derive";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "1cgj75df2v32l4fmvnp25xxkkz4lp6hz76f7hfhd55wgbzmvfnln";
+        procMacro = true;
+        authors = [
+          "Lokathor <zefria@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+          }
+        ];
+
+      };
+      "bytes" = rec {
+        crateName = "bytes";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "cc" = rec {
+        crateName = "cc";
+        version = "1.0.83";
+        edition = "2018";
+        crateBin = [ ];
+        sha256 = "1l643zidlb5iy1dskc5ggqs4wqa29a02f44piczqc8zcnsq4y5zi";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "jobserver";
+            packageId = "jobserver";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        features = {
+          "jobserver" = [ "dep:jobserver" ];
+          "parallel" = [ "jobserver" ];
+        };
+        resolvedDefaultFeatures = [ "jobserver" "parallel" ];
+      };
+      "cfg-if" = rec {
+        crateName = "cfg-if";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "chrono" = rec {
+        crateName = "chrono";
+        version = "0.4.31";
+        edition = "2021";
+        sha256 = "0f6vg67pipm8cziad2yms6a639pssnvysk1m05dd9crymmdnhb3z";
+        dependencies = [
+          {
+            name = "android-tzdata";
+            packageId = "android-tzdata";
+            optional = true;
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "iana-time-zone";
+            packageId = "iana-time-zone";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+            features = [ "fallback" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "android-tzdata" = [ "dep:android-tzdata" ];
+          "arbitrary" = [ "dep:arbitrary" ];
+          "clock" = [ "std" "winapi" "iana-time-zone" "android-tzdata" ];
+          "default" = [ "clock" "std" "oldtime" "wasmbind" ];
+          "iana-time-zone" = [ "dep:iana-time-zone" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "pure-rust-locales" = [ "dep:pure-rust-locales" ];
+          "rkyv" = [ "dep:rkyv" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "unstable-locales" = [ "pure-rust-locales" "alloc" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+          "wasmbind" = [ "wasm-bindgen" "js-sys" ];
+          "winapi" = [ "windows-targets" ];
+          "windows-targets" = [ "dep:windows-targets" ];
+        };
+        resolvedDefaultFeatures = [ "android-tzdata" "clock" "iana-time-zone" "std" "winapi" "windows-targets" ];
+      };
+      "const-oid" = rec {
+        crateName = "const-oid";
+        version = "0.9.5";
+        edition = "2021";
+        sha256 = "0vxb4d25mgk8y0phay7j078limx2553716ixsr1x5605k31j5h98";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+        };
+      };
+      "core-foundation-sys" = rec {
+        crateName = "core-foundation-sys";
+        version = "0.8.4";
+        edition = "2015";
+        sha256 = "1yhf471qj6snnm2mcswai47vsbc9w30y4abmdp4crb4av87sb5p4";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = { };
+      };
+      "cpufeatures" = rec {
+        crateName = "cpufeatures";
+        version = "0.2.11";
+        edition = "2018";
+        sha256 = "1l0gzsyy576n017g9bf0vkv5hhg9cpz1h1libxyfdlzcgbh0yhnf";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-linux-android");
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("apple" == target."vendor" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("loongarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+        ];
+
+      };
+      "crc32fast" = rec {
+        crateName = "crc32fast";
+        version = "1.3.2";
+        edition = "2015";
+        sha256 = "03c8f29yx293yf43xar946xbls1g60c207m9drf8ilqhr25vsh5m";
+        authors = [
+          "Sam Rijs <srijs@airpost.net>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crossbeam-channel" = rec {
+        crateName = "crossbeam-channel";
+        version = "0.5.8";
+        edition = "2018";
+        sha256 = "004jz4wxp9k26z657i7rsh9s7586dklx2c5aqf1n3w1dgzvjng53";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "crossbeam-utils" = [ "dep:crossbeam-utils" ];
+          "default" = [ "std" ];
+          "std" = [ "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "crossbeam-utils" "default" "std" ];
+      };
+      "crossbeam-deque" = rec {
+        crateName = "crossbeam-deque";
+        version = "0.8.3";
+        edition = "2018";
+        sha256 = "1vqczbcild7nczh5z116w8w46z991kpjyw7qxkf24c14apwdcvyf";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "crossbeam-epoch";
+            packageId = "crossbeam-epoch";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "crossbeam-epoch" = [ "dep:crossbeam-epoch" ];
+          "crossbeam-utils" = [ "dep:crossbeam-utils" ];
+          "default" = [ "std" ];
+          "std" = [ "crossbeam-epoch/std" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "crossbeam-epoch" "crossbeam-utils" "default" "std" ];
+      };
+      "crossbeam-epoch" = rec {
+        crateName = "crossbeam-epoch";
+        version = "0.9.15";
+        edition = "2018";
+        sha256 = "1ixwc3cq816wb8rlh3ix4jnybqbyyq4l61nwlx0mfm3ck0s148df";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memoffset";
+            packageId = "memoffset";
+          }
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "loom" = [ "loom-crate" "crossbeam-utils/loom" ];
+          "loom-crate" = [ "dep:loom-crate" ];
+          "nightly" = [ "crossbeam-utils/nightly" ];
+          "std" = [ "alloc" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "crossbeam-queue" = rec {
+        crateName = "crossbeam-queue";
+        version = "0.3.8";
+        edition = "2018";
+        sha256 = "1p9s6n4ckwdgxkb7a8ay9zjzmgc8ppfbxix2vr07rwskibmb7kyi";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "nightly" = [ "crossbeam-utils/nightly" ];
+          "std" = [ "alloc" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "crossbeam-utils" = rec {
+        crateName = "crossbeam-utils";
+        version = "0.8.16";
+        edition = "2018";
+        sha256 = "153j0gikblz7n7qdvdi8pslhi008s1yp9cmny6vw07ad7pbb48js";
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "loom" = [ "dep:loom" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crypto-common" = rec {
+        crateName = "crypto-common";
+        version = "0.1.6";
+        edition = "2018";
+        sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+            features = [ "more_lengths" ];
+          }
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        features = {
+          "getrandom" = [ "rand_core/getrandom" ];
+          "rand_core" = [ "dep:rand_core" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "curve25519-dalek" = rec {
+        crateName = "curve25519-dalek";
+        version = "4.1.1";
+        edition = "2021";
+        sha256 = "0p7ns5917k6369gajrsbfj24llc5zfm635yh3abla7sb5rm8r6z8";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: ("x86_64" == target."arch" or null);
+          }
+          {
+            name = "curve25519-dalek-derive";
+            packageId = "curve25519-dalek-derive";
+            target = { target, features }: ((!("fiat" == target."curve25519_dalek_backend" or null)) && (!("serial" == target."curve25519_dalek_backend" or null)) && ("x86_64" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "fiat-crypto";
+            packageId = "fiat-crypto";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("fiat" == target."curve25519_dalek_backend" or null);
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "platforms";
+            packageId = "platforms";
+          }
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "default" = [ "alloc" "precomputed-tables" "zeroize" ];
+          "digest" = [ "dep:digest" ];
+          "ff" = [ "dep:ff" ];
+          "group" = [ "dep:group" "rand_core" ];
+          "group-bits" = [ "group" "ff/bits" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "digest" "precomputed-tables" "zeroize" ];
+      };
+      "curve25519-dalek-derive" = rec {
+        crateName = "curve25519-dalek-derive";
+        version = "0.1.1";
+        edition = "2021";
+        sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "data-encoding" = rec {
+        crateName = "data-encoding";
+        version = "2.4.0";
+        edition = "2018";
+        sha256 = "023k3dk8422jgbj7k72g63x51h1mhv91dhw1j4h205vzh6fnrrn2";
+        authors = [
+          "Julien Cretin <git@ia0.eu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "der" = rec {
+        crateName = "der";
+        version = "0.7.8";
+        edition = "2021";
+        sha256 = "070bwiyr80800h31c5zd96ckkgagfjgnrrdmz3dzg2lccsd3dypz";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "const-oid";
+            packageId = "const-oid";
+            optional = true;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "arbitrary" = [ "dep:arbitrary" "const-oid?/arbitrary" "std" ];
+          "bytes" = [ "dep:bytes" "alloc" ];
+          "derive" = [ "dep:der_derive" ];
+          "flagset" = [ "dep:flagset" ];
+          "oid" = [ "dep:const-oid" ];
+          "pem" = [ "dep:pem-rfc7468" "alloc" "zeroize" ];
+          "std" = [ "alloc" ];
+          "time" = [ "dep:time" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "oid" "std" "zeroize" ];
+      };
+      "digest" = rec {
+        crateName = "digest";
+        version = "0.10.7";
+        edition = "2018";
+        sha256 = "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer";
+            optional = true;
+          }
+          {
+            name = "crypto-common";
+            packageId = "crypto-common";
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "block-buffer" = [ "dep:block-buffer" ];
+          "const-oid" = [ "dep:const-oid" ];
+          "core-api" = [ "block-buffer" ];
+          "default" = [ "core-api" ];
+          "dev" = [ "blobby" ];
+          "mac" = [ "subtle" ];
+          "oid" = [ "const-oid" ];
+          "rand_core" = [ "crypto-common/rand_core" ];
+          "std" = [ "alloc" "crypto-common/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "block-buffer" "core-api" "default" "std" ];
+      };
+      "dyn-clone" = rec {
+        crateName = "dyn-clone";
+        version = "1.0.16";
+        edition = "2018";
+        sha256 = "0pa9kas6a241pbx0q82ipwi4f7m7wwyzkkc725caky24gl4j4nsl";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "ed25519" = rec {
+        crateName = "ed25519";
+        version = "2.2.3";
+        edition = "2021";
+        sha256 = "0lydzdf26zbn82g7xfczcac9d7mzm3qgx934ijjrd5hjpjx32m8i";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "pkcs8";
+            packageId = "pkcs8";
+            optional = true;
+          }
+          {
+            name = "signature";
+            packageId = "signature";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "pkcs8?/alloc" ];
+          "default" = [ "std" ];
+          "pem" = [ "alloc" "pkcs8/pem" ];
+          "pkcs8" = [ "dep:pkcs8" ];
+          "serde" = [ "dep:serde" ];
+          "serde_bytes" = [ "serde" "dep:serde_bytes" ];
+          "std" = [ "pkcs8?/std" "signature/std" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "ed25519-dalek" = rec {
+        crateName = "ed25519-dalek";
+        version = "2.1.0";
+        edition = "2021";
+        sha256 = "1h13qm789m9gdjl6jazss80hqi8ll37m0afwcnw23zcbqjp8wqhz";
+        authors = [
+          "isis lovecruft <isis@patternsinthevoid.net>"
+          "Tony Arcieri <bascule@gmail.com>"
+          "Michael Rosenberg <michael@mrosenberg.pub>"
+        ];
+        dependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" ];
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" "rand_core" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "curve25519-dalek/alloc" "ed25519/alloc" "serde?/alloc" "zeroize/alloc" ];
+          "asm" = [ "sha2/asm" ];
+          "batch" = [ "alloc" "merlin" "rand_core" ];
+          "default" = [ "fast" "std" "zeroize" ];
+          "digest" = [ "signature/digest" ];
+          "fast" = [ "curve25519-dalek/precomputed-tables" ];
+          "legacy_compatibility" = [ "curve25519-dalek/legacy_compatibility" ];
+          "merlin" = [ "dep:merlin" ];
+          "pem" = [ "alloc" "ed25519/pem" "pkcs8" ];
+          "pkcs8" = [ "ed25519/pkcs8" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" "ed25519/serde" ];
+          "signature" = [ "dep:signature" ];
+          "std" = [ "alloc" "ed25519/std" "serde?/std" "sha2/std" ];
+          "zeroize" = [ "dep:zeroize" "curve25519-dalek/zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fast" "std" "zeroize" ];
+      };
+      "either" = rec {
+        crateName = "either";
+        version = "1.9.0";
+        edition = "2018";
+        sha256 = "01qy3anr7jal5lpc20791vxrw0nl6vksb5j7x56q2fycgcyy8sm2";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "use_std" ];
+      };
+      "enum_dispatch" = rec {
+        crateName = "enum_dispatch";
+        version = "0.3.12";
+        edition = "2018";
+        sha256 = "03l998igqfzkykmj8i5qlbwhv2id9jn98fkkl82lv3dvg0q32cwg";
+        procMacro = true;
+        authors = [
+          "Anton Lazarev <https://antonok.com>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "equivalent" = rec {
+        crateName = "equivalent";
+        version = "1.0.1";
+        edition = "2015";
+        sha256 = "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl";
+
+      };
+      "errno" = rec {
+        crateName = "errno";
+        version = "0.3.6";
+        edition = "2018";
+        sha256 = "0vp3dwidinw62hgx8ai5si3zldcwnq9x5cf6ra0iypsssq7fw63w";
+        authors = [
+          "Chris Wong <lambda.fairy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_System_Diagnostics_Debug" ];
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "libc/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "ethnum" = rec {
+        crateName = "ethnum";
+        version = "1.5.0";
+        edition = "2021";
+        sha256 = "0b68ngvisb0d40vc6h30zlhghbb3mc8wlxjbf8gnmavk1dca435r";
+        authors = [
+          "Nicholas Rodrigues Lordello <nlordell@gmail.com>"
+        ];
+        features = {
+          "ethnum-intrinsics" = [ "dep:ethnum-intrinsics" ];
+          "llvm-intrinsics" = [ "ethnum-intrinsics" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "fallible-streaming-iterator" = rec {
+        crateName = "fallible-streaming-iterator";
+        version = "0.1.9";
+        edition = "2015";
+        sha256 = "0nj6j26p71bjy8h42x6jahx1hn0ng6mc2miwpgwnp8vnwqf4jq3k";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        features = { };
+      };
+      "fast-float" = rec {
+        crateName = "fast-float";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "0g7kfll3xyh99kc7r352lhljnwvgayxxa6saifb6725inikmyxlm";
+        authors = [
+          "Ivan Smirnov <i.s.smirnov@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "fastrand" = rec {
+        crateName = "fastrand";
+        version = "2.0.1";
+        edition = "2018";
+        sha256 = "19flpv5zbzpf0rk4x77z4zf25in0brg8l7m304d3yrf47qvwxjr5";
+        authors = [
+          "Stjepan Glavina <stjepang@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "getrandom" = [ "dep:getrandom" ];
+          "js" = [ "std" "getrandom" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "fiat-crypto" = rec {
+        crateName = "fiat-crypto";
+        version = "0.2.5";
+        edition = "2018";
+        sha256 = "1dxn0g50pv0ppal779vi7k40fr55pbhkyv4in7i13pgl4sn3wmr7";
+        authors = [
+          "Fiat Crypto library authors <jgross@mit.edu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+      };
+      "flate2" = rec {
+        crateName = "flate2";
+        version = "1.0.28";
+        edition = "2018";
+        sha256 = "03llhsh4gqdirnfxxb9g2w9n0721dyn4yjir3pz7z4vjaxb3yc26";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Josh Triplett <josh@joshtriplett.org>"
+        ];
+        dependencies = [
+          {
+            name = "crc32fast";
+            packageId = "crc32fast";
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "with-alloc" ];
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!("emscripten" == target."os" or null)));
+            features = [ "with-alloc" ];
+          }
+        ];
+        features = {
+          "any_zlib" = [ "any_impl" ];
+          "cloudflare-zlib-sys" = [ "dep:cloudflare-zlib-sys" ];
+          "cloudflare_zlib" = [ "any_zlib" "cloudflare-zlib-sys" ];
+          "default" = [ "rust_backend" ];
+          "libz-ng-sys" = [ "dep:libz-ng-sys" ];
+          "libz-sys" = [ "dep:libz-sys" ];
+          "miniz-sys" = [ "rust_backend" ];
+          "miniz_oxide" = [ "dep:miniz_oxide" ];
+          "rust_backend" = [ "miniz_oxide" "any_impl" ];
+          "zlib" = [ "any_zlib" "libz-sys" ];
+          "zlib-default" = [ "any_zlib" "libz-sys/default" ];
+          "zlib-ng" = [ "any_zlib" "libz-ng-sys" ];
+          "zlib-ng-compat" = [ "zlib" "libz-sys/zlib-ng" ];
+        };
+        resolvedDefaultFeatures = [ "any_impl" "miniz_oxide" "rust_backend" ];
+      };
+      "foreign_vec" = rec {
+        crateName = "foreign_vec";
+        version = "0.1.0";
+        edition = "2021";
+        sha256 = "0wv6p8yfahcqbdg2wg7wxgj4dm32g2b6spa5sg5sxg34v35ha6zf";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+        ];
+
+      };
+      "futures" = rec {
+        crateName = "futures";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "0dak2ilpcmyjrb1j54fzy9hlw6vd10vqljq9gd59pbrq9dqr00ns";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-executor";
+            packageId = "futures-executor";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" "futures-sink/alloc" "futures-channel/alloc" "futures-util/alloc" ];
+          "async-await" = [ "futures-util/async-await" "futures-util/async-await-macro" ];
+          "bilock" = [ "futures-util/bilock" ];
+          "compat" = [ "std" "futures-util/compat" ];
+          "default" = [ "std" "async-await" "executor" ];
+          "executor" = [ "std" "futures-executor/std" ];
+          "futures-executor" = [ "dep:futures-executor" ];
+          "io-compat" = [ "compat" "futures-util/io-compat" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "futures-io/std" "futures-sink/std" "futures-util/std" "futures-util/io" "futures-util/channel" ];
+          "thread-pool" = [ "executor" "futures-executor/thread-pool" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" "futures-channel/unstable" "futures-io/unstable" "futures-util/unstable" ];
+          "write-all-vectored" = [ "futures-util/write-all-vectored" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "default" "executor" "futures-executor" "std" ];
+      };
+      "futures-channel" = rec {
+        crateName = "futures-channel";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1jxsifvrbqzdadk0svbax71cba5d3qg3wgjq8i160mxmd1kdckgz";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" ];
+          "default" = [ "std" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "sink" = [ "futures-sink" ];
+          "std" = [ "alloc" "futures-core/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "futures-sink" "sink" "std" ];
+      };
+      "futures-core" = rec {
+        crateName = "futures-core";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1308bpj0g36nhx2y6bl4mm6f1gnh9xyvvw2q2wpdgnb6dv3247gb";
+        features = {
+          "default" = [ "std" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-executor" = rec {
+        crateName = "futures-executor";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1g4pjni0sw28djx6mlcfz584abm2lpifz86cmng0kkxh7mlvhkqg";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "std" = [ "futures-core/std" "futures-task/std" "futures-util/std" ];
+          "thread-pool" = [ "std" "num_cpus" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "futures-io" = rec {
+        crateName = "futures-io";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1ajsljgny3zfxwahba9byjzclrgvm1ypakca8z854k2w7cb4mwwb";
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "futures-macro" = rec {
+        crateName = "futures-macro";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1nwd18i8kvpkdfwm045hddjli0n96zi7pn6f99zi9c74j7ym7cak";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "futures-sink" = rec {
+        crateName = "futures-sink";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "05q8jykqddxzp8nwf00wjk5m5mqi546d7i8hsxma7hiqxrw36vg3";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-task" = rec {
+        crateName = "futures-task";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "1qmsss8rb5ppql4qvd4r70h9gpfcpd0bg2b3qilxrnhdkc397lgg";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "futures-util" = rec {
+        crateName = "futures-util";
+        version = "0.3.29";
+        edition = "2018";
+        sha256 = "0141rkqh0psj4h8x8lgsl1p29dhqr7z2wcixkcbs60z74kb2d5d1";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-macro";
+            packageId = "futures-macro";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "pin-utils";
+            packageId = "pin-utils";
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" ];
+          "async-await-macro" = [ "async-await" "futures-macro" ];
+          "channel" = [ "std" "futures-channel" ];
+          "compat" = [ "std" "futures_01" ];
+          "default" = [ "std" "async-await" "async-await-macro" ];
+          "futures-channel" = [ "dep:futures-channel" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-macro" = [ "dep:futures-macro" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "futures_01" = [ "dep:futures_01" ];
+          "io" = [ "std" "futures-io" "memchr" ];
+          "io-compat" = [ "io" "compat" "tokio-io" ];
+          "memchr" = [ "dep:memchr" ];
+          "portable-atomic" = [ "futures-core/portable-atomic" ];
+          "sink" = [ "futures-sink" ];
+          "slab" = [ "dep:slab" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "slab" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" ];
+          "write-all-vectored" = [ "io" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "async-await-macro" "channel" "futures-channel" "futures-io" "futures-macro" "futures-sink" "io" "memchr" "sink" "slab" "std" ];
+      };
+      "generic-array" = rec {
+        crateName = "generic-array";
+        version = "0.14.7";
+        edition = "2015";
+        sha256 = "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45";
+        libName = "generic_array";
+        authors = [
+          "BartΕ‚omiej KamiΕ„ski <fizyk20@gmail.com>"
+          "Aaron Trent <novacrazy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "more_lengths" ];
+      };
+      "getrandom" = rec {
+        crateName = "getrandom";
+        version = "0.2.11";
+        edition = "2018";
+        sha256 = "03q7120cc2kn7ry013i67zmjl2g9q73h1ks5z08hq5v9syz0d47y";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            optional = true;
+            target = { target, features }: ((("wasm32" == target."arch" or null) || ("wasm64" == target."arch" or null)) && ("unknown" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((("wasm32" == target."arch" or null) || ("wasm64" == target."arch" or null)) && ("unknown" == target."os" or null));
+          }
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "js" = [ "wasm-bindgen" "js-sys" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "libc/rustc-dep-of-std" "wasi/rustc-dep-of-std" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "js" "js-sys" "std" "wasm-bindgen" ];
+      };
+      "gimli" = rec {
+        crateName = "gimli";
+        version = "0.28.0";
+        edition = "2018";
+        sha256 = "1h7hcl3chfvd2gfrrxjymnwj7anqxjslvz20kcargkvsya2dgf3g";
+        features = {
+          "default" = [ "read-all" "write" ];
+          "endian-reader" = [ "read" "dep:stable_deref_trait" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "read" = [ "read-core" ];
+          "read-all" = [ "read" "std" "fallible-iterator" "endian-reader" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" ];
+          "std" = [ "fallible-iterator?/std" "stable_deref_trait?/std" ];
+          "write" = [ "dep:indexmap" ];
+        };
+        resolvedDefaultFeatures = [ "read" "read-core" ];
+      };
+      "glob" = rec {
+        crateName = "glob";
+        version = "0.3.1";
+        edition = "2015";
+        sha256 = "16zca52nglanv23q5qrwd5jinw3d3as5ylya6y1pbx47vkxvrynj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+
+      };
+      "hashbrown" = rec {
+        crateName = "hashbrown";
+        version = "0.14.2";
+        edition = "2021";
+        sha256 = "0mj1x1d16acxf4zg7wr7q2x8pgzfi1bzpifygcsxmg4d2n972gpr";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "allocator-api2";
+            packageId = "allocator-api2";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "alloc" = [ "dep:alloc" ];
+          "allocator-api2" = [ "dep:allocator-api2" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "ahash" "inline-more" "allocator-api2" ];
+          "equivalent" = [ "dep:equivalent" ];
+          "nightly" = [ "allocator-api2?/nightly" "bumpalo/allocator_api" ];
+          "rayon" = [ "dep:rayon" ];
+          "rkyv" = [ "dep:rkyv" ];
+          "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "ahash" "allocator-api2" "default" "inline-more" "raw" "rayon" ];
+      };
+      "heck" = rec {
+        crateName = "heck";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1a7mqsnycv5z4z5vnv1k34548jzmc0ajic7c1j8jsaspnhw5ql4m";
+        authors = [
+          "Without Boats <woboats@gmail.com>"
+        ];
+        features = {
+          "unicode" = [ "unicode-segmentation" ];
+          "unicode-segmentation" = [ "dep:unicode-segmentation" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "hermit-abi" = rec {
+        crateName = "hermit-abi";
+        version = "0.3.3";
+        edition = "2021";
+        sha256 = "1dyc8qsjh876n74a3rcz8h43s27nj1sypdhsn2ms61bd3b47wzyp";
+        authors = [
+          "Stefan Lankes"
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins/rustc-dep-of-std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "home" = rec {
+        crateName = "home";
+        version = "0.5.5";
+        edition = "2018";
+        sha256 = "1nqx1krijvpd03d96avsdyknd12h8hs3xhxwgqghf8v9xxzc4i2l";
+        authors = [
+          "Brian Anderson <andersrb@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_UI_Shell" ];
+          }
+        ];
+
+      };
+      "iana-time-zone" = rec {
+        crateName = "iana-time-zone";
+        version = "0.1.58";
+        edition = "2018";
+        sha256 = "081vcr8z8ddhl5r1ywif6grnswk01b2ac4nks2bhn8zzdimvh9l3";
+        authors = [
+          "Andrew Straw <strawman@astraw.com>"
+          "RenΓ© Kijewski <rene.kijewski@fu-berlin.de>"
+          "Ryan Lopopolo <rjl@hyperbo.la>"
+        ];
+        dependencies = [
+          {
+            name = "android_system_properties";
+            packageId = "android_system_properties";
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "iana-time-zone-haiku";
+            packageId = "iana-time-zone-haiku";
+            target = { target, features }: ("haiku" == target."os" or null);
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "windows-core";
+            packageId = "windows-core 0.51.1";
+            target = { target, features }: ("windows" == target."os" or null);
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "fallback" ];
+      };
+      "iana-time-zone-haiku" = rec {
+        crateName = "iana-time-zone-haiku";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        authors = [
+          "RenΓ© Kijewski <crates.io@k6i.de>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "indexmap" = rec {
+        crateName = "indexmap";
+        version = "2.1.0";
+        edition = "2021";
+        sha256 = "07rxrqmryr1xfnmhrjlz8ic6jw28v6h5cig3ws2c9d0wifhy2c6m";
+        dependencies = [
+          {
+            name = "equivalent";
+            packageId = "equivalent";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            usesDefaultFeatures = false;
+            features = [ "raw" ];
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-rayon" = [ "dep:rustc-rayon" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "itoa" = rec {
+        crateName = "itoa";
+        version = "1.0.9";
+        edition = "2018";
+        sha256 = "0f6cpb4yqzhkrhhg6kqsw3wnmmhdnnffi6r2xzy248gzi2v0l5dg";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "jemalloc-sys" = rec {
+        crateName = "jemalloc-sys";
+        version = "0.5.4+5.3.0-patched";
+        edition = "2018";
+        links = "jemalloc";
+        sha256 = "1wpbpwhfs6wd484cdfpl0zdf441ann9wj0fypy67i8ffw531jv5c";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>"
+          "The TiKV Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "background_threads" = [ "background_threads_runtime_support" ];
+          "default" = [ "background_threads_runtime_support" ];
+        };
+        resolvedDefaultFeatures = [ "background_threads_runtime_support" ];
+      };
+      "jemallocator" = rec {
+        crateName = "jemallocator";
+        version = "0.5.4";
+        edition = "2018";
+        sha256 = "1g6k9ly6wxj53bp8lz9lg9nj4s662k6612jydw71aqwfkx53gpm0";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>"
+          "Simon Sapin <simon.sapin@exyr.org>"
+          "Steven Fackler <sfackler@gmail.com>"
+          "The TiKV Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "jemalloc-sys";
+            packageId = "jemalloc-sys";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "background_threads" = [ "jemalloc-sys/background_threads" ];
+          "background_threads_runtime_support" = [ "jemalloc-sys/background_threads_runtime_support" ];
+          "debug" = [ "jemalloc-sys/debug" ];
+          "default" = [ "background_threads_runtime_support" ];
+          "disable_initial_exec_tls" = [ "jemalloc-sys/disable_initial_exec_tls" ];
+          "profiling" = [ "jemalloc-sys/profiling" ];
+          "stats" = [ "jemalloc-sys/stats" ];
+          "unprefixed_malloc_on_supported_platforms" = [ "jemalloc-sys/unprefixed_malloc_on_supported_platforms" ];
+        };
+        resolvedDefaultFeatures = [ "background_threads_runtime_support" "default" ];
+      };
+      "jobserver" = rec {
+        crateName = "jobserver";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "0z9w6vfqwbr6hfk9yaw7kydlh6f7k39xdlszxlh39in4acwzcdwc";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+
+      };
+      "js-sys" = rec {
+        crateName = "js-sys";
+        version = "0.3.65";
+        edition = "2018";
+        sha256 = "1s1gaxgzpqfyygc7f2pwp9y128rh5f8zvsc4nm5yazgna9cw7h2l";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+        ];
+
+      };
+      "libc" = rec {
+        crateName = "libc";
+        version = "0.2.150";
+        edition = "2015";
+        sha256 = "0g10n8c830alndgjb8xk1i9kz5z727np90z1z81119pr8d3jmnc9";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ];
+          "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "extra_traits" "std" ];
+      };
+      "libm" = rec {
+        crateName = "libm";
+        version = "0.2.8";
+        edition = "2018";
+        sha256 = "0n4hk1rs8pzw8hdfmwn96c4568s93kfxqgcqswr7sajd2diaihjf";
+        authors = [
+          "Jorge Aparicio <jorge@japaric.io>"
+        ];
+        features = {
+          "musl-reference-tests" = [ "rand" ];
+          "rand" = [ "dep:rand" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "linux-raw-sys" = rec {
+        crateName = "linux-raw-sys";
+        version = "0.4.10";
+        edition = "2021";
+        sha256 = "0gz0671d4hgrdngrryaajxl962ny4g40pykg0vq0pr32q3l7j96s";
+        authors = [
+          "Dan Gohman <dev@sunfishcode.online>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" "general" "errno" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "no_std" ];
+        };
+        resolvedDefaultFeatures = [ "elf" "errno" "general" "ioctl" "no_std" ];
+      };
+      "log" = rec {
+        crateName = "log";
+        version = "0.4.20";
+        edition = "2015";
+        sha256 = "13rf7wphnwd61vazpxr7fiycin6cb1g8fmvgqg18i464p0y1drmm";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "kv_unstable" = [ "value-bag" ];
+          "kv_unstable_serde" = [ "kv_unstable_std" "value-bag/serde" "serde" ];
+          "kv_unstable_std" = [ "std" "kv_unstable" "value-bag/error" ];
+          "kv_unstable_sval" = [ "kv_unstable" "value-bag/sval" "sval" "sval_ref" ];
+          "serde" = [ "dep:serde" ];
+          "sval" = [ "dep:sval" ];
+          "sval_ref" = [ "dep:sval_ref" ];
+          "value-bag" = [ "dep:value-bag" ];
+        };
+      };
+      "lz4" = rec {
+        crateName = "lz4";
+        version = "1.24.0";
+        edition = "2018";
+        crateBin = [ ];
+        sha256 = "1wad97k0asgvaj16ydd09gqs2yvgaanzcvqglrhffv7kdpc2v7ky";
+        authors = [
+          "Jens Heyens <jens.heyens@ewetel.net>"
+          "Artem V. Navrotskiy <bozaro@buzzsoft.ru>"
+          "Patrick Marks <pmarks@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "lz4-sys";
+            packageId = "lz4-sys";
+          }
+        ];
+
+      };
+      "lz4-sys" = rec {
+        crateName = "lz4-sys";
+        version = "1.9.4";
+        edition = "2015";
+        links = "lz4";
+        sha256 = "0059ik4xlvnss5qfh6l691psk4g3350ljxaykzv10yr0gqqppljp";
+        authors = [
+          "Jens Heyens <jens.heyens@ewetel.net>"
+          "Artem V. Navrotskiy <bozaro@buzzsoft.ru>"
+          "Patrick Marks <pmarks@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "memchr" = rec {
+        crateName = "memchr";
+        version = "2.6.4";
+        edition = "2021";
+        sha256 = "0rq1ka8790ns41j147npvxcqcl2anxyngsdimy85ag2api0fwrgn";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+          "bluss"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "logging" = [ "dep:log" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "std" = [ "alloc" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "memmap2" = rec {
+        crateName = "memmap2";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1il82b0mw304jlwvl0m89aa8bj5dgmm3vbb0jg8lqlrk0p98i4zl";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Yevhenii Reizner <razrfalcon@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        features = {
+          "stable_deref_trait" = [ "dep:stable_deref_trait" ];
+        };
+      };
+      "memoffset" = rec {
+        crateName = "memoffset";
+        version = "0.9.0";
+        edition = "2015";
+        sha256 = "0v20ihhdzkfw1jx00a7zjpk2dcp5qjq6lz302nyqamd9c4f4nqss";
+        authors = [
+          "Gilad Naaman <gilad.naaman@gmail.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "minimal-lexical" = rec {
+        crateName = "minimal-lexical";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "miniz_oxide" = rec {
+        crateName = "miniz_oxide";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1ivl3rbbdm53bzscrd01g60l46lz5krl270487d8lhjvwl5hx0g7";
+        authors = [
+          "Frommi <daniil.liferenko@gmail.com>"
+          "oyvindln <oyvindln@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "adler";
+            packageId = "adler";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "with-alloc" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "adler/rustc-dep-of-std" ];
+          "simd" = [ "simd-adler32" ];
+          "simd-adler32" = [ "dep:simd-adler32" ];
+        };
+        resolvedDefaultFeatures = [ "with-alloc" ];
+      };
+      "mio" = rec {
+        crateName = "mio";
+        version = "0.8.9";
+        edition = "2018";
+        sha256 = "1l23hg513c23nhcdzvk25caaj28mic6qgqadbn8axgj6bqf2ikix";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_Storage_FileSystem" "Win32_System_IO" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = {
+          "default" = [ "log" ];
+          "log" = [ "dep:log" ];
+          "os-ext" = [ "os-poll" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_Security" ];
+        };
+        resolvedDefaultFeatures = [ "net" "os-ext" "os-poll" ];
+      };
+      "multiversion" = rec {
+        crateName = "multiversion";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "0al7yrf489lqzxx291sx9566n7slk2njwlqrxbjhqxk1zvbvkixj";
+        authors = [
+          "Caleb Zulawski <caleb.zulawski@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "multiversion-macros";
+            packageId = "multiversion-macros";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "target-features";
+            packageId = "target-features";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "multiversion-macros/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "multiversion-macros" = rec {
+        crateName = "multiversion-macros";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "1j1avbxw7jscyi7dmnywhlwbiny1fvg1vpp9fy4dc1pd022kva16";
+        procMacro = true;
+        authors = [
+          "Caleb Zulawski <caleb.zulawski@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "full" "extra-traits" "visit-mut" ];
+          }
+          {
+            name = "target-features";
+            packageId = "target-features";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "narinfo2parquet" = rec {
+        crateName = "narinfo2parquet";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "narinfo2parquet";
+            path = "src/main.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./.; }
+          else ./.;
+        dependencies = [
+          {
+            name = "anyhow";
+            packageId = "anyhow";
+            features = [ "backtrace" ];
+          }
+          {
+            name = "jemallocator";
+            packageId = "jemallocator";
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+          }
+          {
+            name = "polars";
+            packageId = "polars";
+            usesDefaultFeatures = false;
+            features = [ "parquet" "polars-io" "dtype-categorical" ];
+          }
+          {
+            name = "tempfile-fast";
+            packageId = "tempfile-fast";
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+          }
+        ];
+
+      };
+      "nix-compat" = rec {
+        crateName = "nix-compat";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [ ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ../../nix-compat; }
+          else ../../nix-compat;
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+            features = [ "alloc" "unicode" "serde" ];
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+          }
+          {
+            name = "ed25519-dalek";
+            packageId = "ed25519-dalek";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "nom";
+            packageId = "nom";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+        ];
+        features = {
+          "async" = [ "futures-util" ];
+          "futures-util" = [ "dep:futures-util" ];
+        };
+      };
+      "nom" = rec {
+        crateName = "nom";
+        version = "7.1.3";
+        edition = "2018";
+        sha256 = "0jha9901wxam390jcf5pfa0qqfrgh8li787jx2ip0yk5b8y9hwyj";
+        authors = [
+          "contact@geoffroycouprie.com"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "minimal-lexical";
+            packageId = "minimal-lexical";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" "memchr/std" "minimal-lexical/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "now" = rec {
+        crateName = "now";
+        version = "0.1.3";
+        edition = "2018";
+        sha256 = "1l135786rb43rjfhwfdj7hi3b5zxxyl9gwf15yjz18cp8f3yk2bd";
+        authors = [
+          "Kilerd <blove694@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "clock" "std" ];
+          }
+        ];
+
+      };
+      "ntapi" = rec {
+        crateName = "ntapi";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1r38zhbwdvkis2mzs6671cm1p6djgsl49i7bwxzrvhwicdf8k8z8";
+        authors = [
+          "MSxDOS <melcodos@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi";
+            packageId = "winapi";
+            features = [ "cfg" "evntrace" "in6addr" "inaddr" "minwinbase" "ntsecapi" "windef" "winioctl" ];
+          }
+        ];
+        features = {
+          "default" = [ "user" ];
+          "impl-default" = [ "winapi/impl-default" ];
+        };
+        resolvedDefaultFeatures = [ "default" "user" ];
+      };
+      "num-traits" = rec {
+        crateName = "num-traits";
+        version = "0.2.17";
+        edition = "2018";
+        sha256 = "0z16bi5zwgfysz6765v3rd6whfbjpihx3mhsn4dg8dzj2c221qrr";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "libm";
+            packageId = "libm";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "libm" = [ "dep:libm" ];
+        };
+        resolvedDefaultFeatures = [ "default" "libm" "std" ];
+      };
+      "num_cpus" = rec {
+        crateName = "num_cpus";
+        version = "1.16.0";
+        edition = "2015";
+        sha256 = "0hra6ihpnh06dvfvz9ipscys0xfqa9ca9hzp384d5m02ssvgqqa1";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(target."windows" or false));
+          }
+        ];
+
+      };
+      "object" = rec {
+        crateName = "object";
+        version = "0.32.1";
+        edition = "2018";
+        sha256 = "1c02x4kvqpnl3wn7gz9idm4jrbirbycyqjgiw6lm1g9k77fzkxcw";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "all" = [ "read" "write" "std" "compression" "wasm" ];
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "compression" = [ "dep:flate2" "dep:ruzstd" "std" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "read" "compression" ];
+          "doc" = [ "read_core" "write_std" "std" "compression" "archive" "coff" "elf" "macho" "pe" "wasm" "xcoff" ];
+          "pe" = [ "coff" ];
+          "read" = [ "read_core" "archive" "coff" "elf" "macho" "pe" "xcoff" "unaligned" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "alloc" "memchr/rustc-dep-of-std" ];
+          "std" = [ "memchr/std" ];
+          "unstable-all" = [ "all" "unstable" ];
+          "wasm" = [ "dep:wasmparser" ];
+          "write" = [ "write_std" "coff" "elf" "macho" "pe" "xcoff" ];
+          "write_core" = [ "dep:crc32fast" "dep:indexmap" "dep:hashbrown" ];
+          "write_std" = [ "write_core" "std" "indexmap?/std" "crc32fast?/std" ];
+        };
+        resolvedDefaultFeatures = [ "archive" "coff" "elf" "macho" "pe" "read_core" "unaligned" ];
+      };
+      "once_cell" = rec {
+        crateName = "once_cell";
+        version = "1.18.0";
+        edition = "2021";
+        sha256 = "0vapcd5ambwck95wyz3ymlim35jirgnqn9a0qmi19msymv95v2yx";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        features = {
+          "alloc" = [ "race" ];
+          "atomic-polyfill" = [ "critical-section" ];
+          "critical-section" = [ "dep:critical-section" "dep:atomic-polyfill" ];
+          "default" = [ "std" ];
+          "parking_lot" = [ "dep:parking_lot_core" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "race" "std" "unstable" ];
+      };
+      "parquet-format-safe" = rec {
+        crateName = "parquet-format-safe";
+        version = "0.2.4";
+        edition = "2021";
+        sha256 = "07wf6wf4jrxlq5p3xldxsnabp7jl06my2qp7kiwy9m3x2r5wac8i";
+        authors = [
+          "Apache Thrift contributors <dev@thrift.apache.org>"
+          "Jorge Leitao <jorgecarleitao@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+            optional = true;
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+        ];
+        features = {
+          "async" = [ "futures" "async-trait" ];
+          "async-trait" = [ "dep:async-trait" ];
+          "full" = [ "async" ];
+          "futures" = [ "dep:futures" ];
+        };
+        resolvedDefaultFeatures = [ "async" "async-trait" "default" "futures" ];
+      };
+      "percent-encoding" = rec {
+        crateName = "percent-encoding";
+        version = "2.3.0";
+        edition = "2018";
+        sha256 = "152slflmparkh27hprw62sph8rv77wckzhwl2dhqk6bf563lfalv";
+        authors = [
+          "The rust-url developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "pin-project-lite" = rec {
+        crateName = "pin-project-lite";
+        version = "0.2.13";
+        edition = "2018";
+        sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+
+      };
+      "pin-utils" = rec {
+        crateName = "pin-utils";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        authors = [
+          "Josef Brandl <mail@josefbrandl.de>"
+        ];
+
+      };
+      "pkcs8" = rec {
+        crateName = "pkcs8";
+        version = "0.10.2";
+        edition = "2021";
+        sha256 = "1dx7w21gvn07azszgqd3ryjhyphsrjrmq5mmz1fbxkj5g0vv4l7r";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+          {
+            name = "spki";
+            packageId = "spki";
+          }
+        ];
+        features = {
+          "3des" = [ "encryption" "pkcs5/3des" ];
+          "alloc" = [ "der/alloc" "der/zeroize" "spki/alloc" ];
+          "des-insecure" = [ "encryption" "pkcs5/des-insecure" ];
+          "encryption" = [ "alloc" "pkcs5/alloc" "pkcs5/pbes2" "rand_core" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "pem" = [ "alloc" "der/pem" "spki/pem" ];
+          "pkcs5" = [ "dep:pkcs5" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "sha1-insecure" = [ "encryption" "pkcs5/sha1-insecure" ];
+          "std" = [ "alloc" "der/std" "spki/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "pkg-config" = rec {
+        crateName = "pkg-config";
+        version = "0.3.27";
+        edition = "2015";
+        sha256 = "0r39ryh1magcq4cz5g9x88jllsnxnhcqr753islvyk4jp9h2h1r6";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+
+      };
+      "planus" = rec {
+        crateName = "planus";
+        version = "0.3.1";
+        edition = "2021";
+        sha256 = "17x8mr175b9clg998xpi5z45f9fsspb0ncfnx2644bz817fr25pw";
+        dependencies = [
+          {
+            name = "array-init-cursor";
+            packageId = "array-init-cursor";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "platforms" = rec {
+        crateName = "platforms";
+        version = "3.2.0";
+        edition = "2018";
+        sha256 = "1c6bzwn877aqdbbmyqsl753ycbciwvbdh4lpzijb8vrfb4zsprhl";
+        authors = [
+          "Tony Arcieri <bascule@gmail.com>"
+          "Sergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "polars" = rec {
+        crateName = "polars";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "0swv6i0gq25zafw1ir2irqij9a8mnkhvws5idv72m3kavby4i04k";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            target = { target, features }: (builtins.elem "wasm" target."family");
+            features = [ "js" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "algorithm_group_by" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-lazy";
+            packageId = "polars-lazy";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-sql";
+            packageId = "polars-sql";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "abs" = [ "polars-ops/abs" "polars-lazy?/abs" ];
+          "approx_unique" = [ "polars-lazy?/approx_unique" "polars-ops/approx_unique" ];
+          "arg_where" = [ "polars-lazy?/arg_where" ];
+          "array_any_all" = [ "polars-lazy?/array_any_all" "dtype-array" ];
+          "asof_join" = [ "polars-core/asof_join" "polars-lazy?/asof_join" "polars-ops/asof_join" ];
+          "async" = [ "polars-lazy?/async" ];
+          "avro" = [ "polars-io" "polars-io/avro" ];
+          "avx512" = [ "polars-core/avx512" ];
+          "aws" = [ "async" "cloud" "polars-io/aws" ];
+          "azure" = [ "async" "cloud" "polars-io/azure" ];
+          "bench" = [ "lazy" ];
+          "bigidx" = [ "polars-core/bigidx" "polars-lazy?/bigidx" "polars-ops/big_idx" ];
+          "binary_encoding" = [ "polars-ops/binary_encoding" "polars-lazy?/binary_encoding" ];
+          "checked_arithmetic" = [ "polars-core/checked_arithmetic" ];
+          "chunked_ids" = [ "polars-lazy?/chunked_ids" "polars-core/chunked_ids" "polars-ops/chunked_ids" ];
+          "cloud" = [ "polars-lazy?/cloud" "polars-io/cloud" ];
+          "cloud_write" = [ "cloud" "polars-lazy?/cloud_write" ];
+          "coalesce" = [ "polars-lazy?/coalesce" ];
+          "concat_str" = [ "polars-lazy?/concat_str" ];
+          "cov" = [ "polars-lazy/cov" ];
+          "cross_join" = [ "polars-lazy?/cross_join" "polars-ops/cross_join" ];
+          "cse" = [ "polars-lazy?/cse" ];
+          "csv" = [ "polars-io" "polars-io/csv" "polars-lazy?/csv" "polars-sql?/csv" ];
+          "cum_agg" = [ "polars-ops/cum_agg" "polars-lazy?/cum_agg" ];
+          "cumulative_eval" = [ "polars-lazy?/cumulative_eval" ];
+          "cutqcut" = [ "polars-lazy?/cutqcut" ];
+          "dataframe_arithmetic" = [ "polars-core/dataframe_arithmetic" ];
+          "date_offset" = [ "polars-lazy?/date_offset" ];
+          "decompress" = [ "polars-io/decompress" ];
+          "decompress-fast" = [ "polars-io/decompress-fast" ];
+          "default" = [ "docs" "zip_with" "csv" "temporal" "fmt" "dtype-slim" ];
+          "describe" = [ "polars-core/describe" ];
+          "diagonal_concat" = [ "polars-core/diagonal_concat" "polars-lazy?/diagonal_concat" "polars-sql?/diagonal_concat" ];
+          "diff" = [ "polars-ops/diff" "polars-lazy?/diff" ];
+          "docs" = [ "polars-core/docs" ];
+          "docs-selection" = [ "csv" "json" "parquet" "ipc" "ipc_streaming" "dtype-full" "is_in" "rows" "docs" "strings" "object" "lazy" "temporal" "random" "zip_with" "round_series" "checked_arithmetic" "ndarray" "repeat_by" "is_first_distinct" "is_last_distinct" "asof_join" "cross_join" "concat_str" "string_reverse" "string_to_integer" "decompress" "mode" "take_opt_iter" "cum_agg" "rolling_window" "interpolate" "diff" "rank" "range" "diagonal_concat" "horizontal_concat" "abs" "dot_diagram" "string_encoding" "product" "to_dummies" "describe" "list_eval" "cumulative_eval" "timezones" "arg_where" "propagate_nans" "coalesce" "dynamic_group_by" "extract_groups" "replace" ];
+          "dot_diagram" = [ "polars-lazy?/dot_diagram" ];
+          "dot_product" = [ "polars-core/dot_product" ];
+          "dtype-array" = [ "polars-core/dtype-array" "polars-lazy?/dtype-array" "polars-ops/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" "polars-io/dtype-categorical" "polars-lazy?/dtype-categorical" "polars-ops/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-lazy?/dtype-date" "polars-io/dtype-date" "polars-time?/dtype-date" "polars-core/dtype-date" "polars-ops/dtype-date" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-lazy?/dtype-datetime" "polars-io/dtype-datetime" "polars-time?/dtype-datetime" "polars-ops/dtype-datetime" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" "polars-lazy?/dtype-decimal" "polars-ops/dtype-decimal" "polars-io/dtype-decimal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-lazy?/dtype-duration" "polars-time?/dtype-duration" "polars-core/dtype-duration" "polars-ops/dtype-duration" ];
+          "dtype-full" = [ "dtype-date" "dtype-datetime" "dtype-duration" "dtype-time" "dtype-array" "dtype-i8" "dtype-i16" "dtype-decimal" "dtype-u8" "dtype-u16" "dtype-categorical" "dtype-struct" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" "polars-lazy?/dtype-i16" "polars-ops/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" "polars-lazy?/dtype-i8" "polars-ops/dtype-i8" ];
+          "dtype-slim" = [ "dtype-date" "dtype-datetime" "dtype-duration" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" "polars-lazy?/dtype-struct" "polars-ops/dtype-struct" "polars-io/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-io/dtype-time" "polars-time?/dtype-time" "polars-ops/dtype-time" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" "polars-lazy?/dtype-u16" "polars-ops/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" "polars-lazy?/dtype-u8" "polars-ops/dtype-u8" ];
+          "dynamic_group_by" = [ "polars-core/dynamic_group_by" "polars-lazy?/dynamic_group_by" ];
+          "ewma" = [ "polars-ops/ewma" "polars-lazy?/ewma" ];
+          "extract_groups" = [ "polars-lazy?/extract_groups" ];
+          "extract_jsonpath" = [ "polars-core/strings" "polars-ops/extract_jsonpath" "polars-ops/strings" "polars-lazy?/extract_jsonpath" ];
+          "find_many" = [ "polars-plan/find_many" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "fmt_no_tty" = [ "polars-core/fmt_no_tty" ];
+          "fused" = [ "polars-ops/fused" "polars-lazy?/fused" ];
+          "gcp" = [ "async" "cloud" "polars-io/gcp" ];
+          "group_by_list" = [ "polars-core/group_by_list" "polars-ops/group_by_list" ];
+          "hist" = [ "polars-ops/hist" "polars-lazy/hist" ];
+          "horizontal_concat" = [ "polars-core/horizontal_concat" "polars-lazy?/horizontal_concat" ];
+          "http" = [ "async" "cloud" "polars-io/http" ];
+          "interpolate" = [ "polars-ops/interpolate" "polars-lazy?/interpolate" ];
+          "ipc" = [ "polars-io" "polars-io/ipc" "polars-lazy?/ipc" "polars-sql?/ipc" ];
+          "ipc_streaming" = [ "polars-io" "polars-io/ipc_streaming" "polars-lazy?/ipc" ];
+          "is_first_distinct" = [ "polars-lazy?/is_first_distinct" "polars-ops/is_first_distinct" ];
+          "is_in" = [ "polars-lazy?/is_in" ];
+          "is_last_distinct" = [ "polars-lazy?/is_last_distinct" "polars-ops/is_last_distinct" ];
+          "is_unique" = [ "polars-lazy?/is_unique" "polars-ops/is_unique" ];
+          "json" = [ "polars-io" "polars-io/json" "polars-lazy?/json" "polars-sql?/json" "dtype-struct" ];
+          "lazy" = [ "polars-core/lazy" "polars-lazy" ];
+          "lazy_regex" = [ "polars-lazy?/regex" ];
+          "list_any_all" = [ "polars-lazy?/list_any_all" ];
+          "list_count" = [ "polars-ops/list_count" "polars-lazy?/list_count" ];
+          "list_drop_nulls" = [ "polars-lazy?/list_drop_nulls" ];
+          "list_eval" = [ "polars-lazy?/list_eval" ];
+          "list_gather" = [ "polars-ops/list_gather" "polars-lazy?/list_gather" ];
+          "list_sample" = [ "polars-lazy?/list_sample" ];
+          "list_sets" = [ "polars-lazy?/list_sets" ];
+          "list_to_struct" = [ "polars-ops/list_to_struct" "polars-lazy?/list_to_struct" ];
+          "log" = [ "polars-ops/log" "polars-lazy?/log" ];
+          "merge_sorted" = [ "polars-lazy?/merge_sorted" ];
+          "meta" = [ "polars-lazy?/meta" ];
+          "mode" = [ "polars-ops/mode" "polars-lazy?/mode" ];
+          "moment" = [ "polars-ops/moment" "polars-lazy?/moment" ];
+          "ndarray" = [ "polars-core/ndarray" ];
+          "nightly" = [ "polars-core/nightly" "polars-ops?/nightly" "simd" "polars-lazy?/nightly" "polars-sql/nightly" ];
+          "object" = [ "polars-core/object" "polars-lazy?/object" "polars-io/object" ];
+          "parquet" = [ "polars-io" "polars-lazy?/parquet" "polars-io/parquet" "polars-sql?/parquet" ];
+          "partition_by" = [ "polars-core/partition_by" ];
+          "pct_change" = [ "polars-ops/pct_change" "polars-lazy?/pct_change" ];
+          "peaks" = [ "polars-lazy/peaks" ];
+          "performant" = [ "polars-core/performant" "chunked_ids" "dtype-u8" "dtype-u16" "dtype-struct" "cse" "polars-ops/performant" "streaming" "fused" ];
+          "pivot" = [ "polars-lazy?/pivot" ];
+          "polars-io" = [ "dep:polars-io" ];
+          "polars-lazy" = [ "dep:polars-lazy" ];
+          "polars-ops" = [ "dep:polars-ops" ];
+          "polars-plan" = [ "dep:polars-plan" ];
+          "polars-sql" = [ "dep:polars-sql" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "product" = [ "polars-core/product" ];
+          "propagate_nans" = [ "polars-lazy?/propagate_nans" ];
+          "random" = [ "polars-core/random" "polars-lazy?/random" "polars-ops/random" ];
+          "range" = [ "polars-lazy?/range" ];
+          "rank" = [ "polars-lazy?/rank" "polars-ops/rank" ];
+          "reinterpret" = [ "polars-core/reinterpret" ];
+          "repeat_by" = [ "polars-ops/repeat_by" "polars-lazy?/repeat_by" ];
+          "replace" = [ "polars-ops/replace" "polars-lazy?/replace" ];
+          "rle" = [ "polars-lazy?/rle" ];
+          "rolling_window" = [ "polars-core/rolling_window" "polars-lazy?/rolling_window" "polars-time/rolling_window" ];
+          "round_series" = [ "polars-ops/round_series" "polars-lazy?/round_series" ];
+          "row_hash" = [ "polars-core/row_hash" "polars-lazy?/row_hash" ];
+          "rows" = [ "polars-core/rows" ];
+          "search_sorted" = [ "polars-lazy?/search_sorted" ];
+          "semi_anti_join" = [ "polars-lazy?/semi_anti_join" "polars-ops/semi_anti_join" "polars-sql?/semi_anti_join" ];
+          "serde" = [ "polars-core/serde" ];
+          "serde-lazy" = [ "polars-core/serde-lazy" "polars-lazy?/serde" "polars-time?/serde" "polars-io?/serde" "polars-ops?/serde" ];
+          "sign" = [ "polars-lazy?/sign" ];
+          "simd" = [ "polars-core/simd" "polars-io/simd" "polars-ops?/simd" ];
+          "sql" = [ "polars-sql" ];
+          "streaming" = [ "polars-lazy?/streaming" ];
+          "string_encoding" = [ "polars-ops/string_encoding" "polars-lazy?/string_encoding" "polars-core/strings" ];
+          "string_pad" = [ "polars-lazy?/string_pad" "polars-ops/string_pad" ];
+          "string_reverse" = [ "polars-lazy?/string_reverse" "polars-ops/string_reverse" ];
+          "string_to_integer" = [ "polars-lazy?/string_to_integer" "polars-ops/string_to_integer" ];
+          "strings" = [ "polars-core/strings" "polars-lazy?/strings" "polars-ops/strings" ];
+          "take_opt_iter" = [ "polars-core/take_opt_iter" ];
+          "temporal" = [ "polars-core/temporal" "polars-lazy?/temporal" "polars-io/temporal" "polars-time" ];
+          "test" = [ "lazy" "rolling_window" "rank" "round_series" "csv" "dtype-categorical" "cum_agg" "fmt" "diff" "abs" "parquet" "ipc" "ipc_streaming" "json" ];
+          "timezones" = [ "polars-core/timezones" "polars-lazy?/timezones" "polars-io/timezones" ];
+          "to_dummies" = [ "polars-ops/to_dummies" ];
+          "top_k" = [ "polars-lazy?/top_k" ];
+          "trigonometry" = [ "polars-lazy?/trigonometry" ];
+          "true_div" = [ "polars-lazy?/true_div" ];
+          "unique_counts" = [ "polars-ops/unique_counts" "polars-lazy?/unique_counts" ];
+          "zip_with" = [ "polars-core/zip_with" ];
+        };
+        resolvedDefaultFeatures = [ "dtype-categorical" "parquet" "polars-io" "polars-ops" ];
+      };
+      "polars-arrow" = rec {
+        crateName = "polars-arrow";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "0vxql6amvwyp6qj0w87vm21y33qa0i61pshs4ry7ixwgd4ps0s6f";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+          "Apache Arrow <dev@arrow.apache.org>"
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "arrow-format";
+            packageId = "arrow-format";
+            optional = true;
+            features = [ "ipc" ];
+          }
+          {
+            name = "atoi_simd";
+            packageId = "atoi_simd";
+            optional = true;
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "dyn-clone";
+            packageId = "dyn-clone";
+          }
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "ethnum";
+            packageId = "ethnum";
+          }
+          {
+            name = "fast-float";
+            packageId = "fast-float";
+            optional = true;
+          }
+          {
+            name = "foreign_vec";
+            packageId = "foreign_vec";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "wasm32-unknown-unknown");
+            features = [ "js" ];
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+            optional = true;
+          }
+          {
+            name = "lz4";
+            packageId = "lz4";
+            optional = true;
+          }
+          {
+            name = "multiversion";
+            packageId = "multiversion";
+            optional = true;
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+            optional = true;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+          }
+          {
+            name = "streaming-iterator";
+            packageId = "streaming-iterator";
+          }
+          {
+            name = "strength_reduce";
+            packageId = "strength_reduce";
+            optional = true;
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "arrow-array" = [ "dep:arrow-array" ];
+          "arrow-buffer" = [ "dep:arrow-buffer" ];
+          "arrow-data" = [ "dep:arrow-data" ];
+          "arrow-format" = [ "dep:arrow-format" ];
+          "arrow-schema" = [ "dep:arrow-schema" ];
+          "arrow_rs" = [ "arrow-buffer" "arrow-schema" "arrow-data" "arrow-array" ];
+          "async-stream" = [ "dep:async-stream" ];
+          "atoi" = [ "dep:atoi" ];
+          "atoi_simd" = [ "dep:atoi_simd" ];
+          "avro-schema" = [ "dep:avro-schema" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "compute" = [ "compute_aggregate" "compute_arithmetics" "compute_bitwise" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_hash" "compute_if_then_else" "compute_take" "compute_temporal" ];
+          "compute_aggregate" = [ "multiversion" ];
+          "compute_arithmetics" = [ "strength_reduce" "compute_arithmetics_decimal" ];
+          "compute_arithmetics_decimal" = [ "strength_reduce" ];
+          "compute_cast" = [ "compute_take" "ryu" "atoi_simd" "itoa" "fast-float" ];
+          "compute_comparison" = [ "compute_take" "compute_boolean" ];
+          "compute_hash" = [ "multiversion" ];
+          "dtype-decimal" = [ "atoi" ];
+          "fast-float" = [ "dep:fast-float" ];
+          "full" = [ "arrow_rs" "io_ipc" "io_flight" "io_ipc_write_async" "io_ipc_read_async" "io_ipc_compression" "io_avro" "io_avro_compression" "io_avro_async" "regex-syntax" "compute" "chrono-tz" ];
+          "futures" = [ "dep:futures" ];
+          "hex" = [ "dep:hex" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "io_avro" = [ "avro-schema" "polars-error/avro-schema" ];
+          "io_avro_async" = [ "avro-schema/async" ];
+          "io_avro_compression" = [ "avro-schema/compression" ];
+          "io_flight" = [ "io_ipc" "arrow-format/flight-data" ];
+          "io_ipc" = [ "arrow-format" "polars-error/arrow-format" ];
+          "io_ipc_compression" = [ "lz4" "zstd" ];
+          "io_ipc_read_async" = [ "io_ipc" "futures" "async-stream" ];
+          "io_ipc_write_async" = [ "io_ipc" "futures" ];
+          "itoa" = [ "dep:itoa" ];
+          "lz4" = [ "dep:lz4" ];
+          "multiversion" = [ "dep:multiversion" ];
+          "regex" = [ "dep:regex" ];
+          "regex-syntax" = [ "dep:regex-syntax" ];
+          "ryu" = [ "dep:ryu" ];
+          "serde" = [ "dep:serde" ];
+          "strength_reduce" = [ "dep:strength_reduce" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "arrow-format" "atoi_simd" "compute" "compute_aggregate" "compute_arithmetics" "compute_arithmetics_decimal" "compute_bitwise" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_hash" "compute_if_then_else" "compute_take" "compute_temporal" "fast-float" "futures" "io_ipc" "io_ipc_compression" "io_ipc_write_async" "itoa" "lz4" "multiversion" "ryu" "strength_reduce" "strings" "temporal" "zstd" ];
+      };
+      "polars-compute" = rec {
+        crateName = "polars-compute";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1pa4l1w41gdzdff81ih1z05z3gz568k81i6flibbca8v2igvqkxi";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = { };
+      };
+      "polars-core" = rec {
+        crateName = "polars-core";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "08sar9h97znpb8ziyvrrvvx28lyzc21vwsd7gvwybjxn6kkyzxfh";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-compute";
+            packageId = "polars-compute";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-row";
+            packageId = "polars-row";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            optional = true;
+            features = [ "small_rng" "std" ];
+          }
+          {
+            name = "rand_distr";
+            packageId = "rand_distr";
+            optional = true;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "xxhash-rust";
+            packageId = "xxhash-rust";
+            features = [ "xxh3" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "arrow-array" = [ "dep:arrow-array" ];
+          "arrow_rs" = [ "arrow-array" "arrow/arrow_rs" ];
+          "bigidx" = [ "arrow/bigidx" "polars-utils/bigidx" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "comfy-table" = [ "dep:comfy-table" ];
+          "default" = [ "algorithm_group_by" ];
+          "docs-selection" = [ "ndarray" "rows" "docs" "strings" "object" "lazy" "temporal" "random" "zip_with" "checked_arithmetic" "is_first_distinct" "is_last_distinct" "asof_join" "dot_product" "row_hash" "rolling_window" "dtype-categorical" "dtype-decimal" "diagonal_concat" "horizontal_concat" "dataframe_arithmetic" "product" "describe" "chunked_ids" "partition_by" "algorithm_group_by" ];
+          "dtype-array" = [ "arrow/dtype-array" "polars-compute/dtype-array" ];
+          "dtype-date" = [ "temporal" ];
+          "dtype-datetime" = [ "temporal" ];
+          "dtype-decimal" = [ "dep:itoap" "arrow/dtype-decimal" ];
+          "dtype-duration" = [ "temporal" ];
+          "dtype-time" = [ "temporal" ];
+          "dynamic_group_by" = [ "dtype-datetime" "dtype-date" ];
+          "fmt" = [ "comfy-table/tty" ];
+          "fmt_no_tty" = [ "comfy-table" ];
+          "ndarray" = [ "dep:ndarray" ];
+          "nightly" = [ "simd" "hashbrown/nightly" "polars-utils/nightly" "arrow/nightly" ];
+          "object" = [ "serde_json" ];
+          "performant" = [ "arrow/performant" "reinterpret" ];
+          "rand" = [ "dep:rand" ];
+          "rand_distr" = [ "dep:rand_distr" ];
+          "random" = [ "rand" "rand_distr" ];
+          "regex" = [ "dep:regex" ];
+          "serde" = [ "dep:serde" "smartstring/serde" "bitflags/serde" ];
+          "serde-lazy" = [ "serde" "arrow/serde" "indexmap/serde" "smartstring/serde" "chrono/serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "simd" = [ "arrow/simd" "polars-compute/simd" ];
+          "strings" = [ "regex" "arrow/strings" "polars-error/regex" ];
+          "temporal" = [ "regex" "chrono" "polars-error/regex" ];
+          "timezones" = [ "chrono-tz" "arrow/chrono-tz" "arrow/timezones" ];
+        };
+        resolvedDefaultFeatures = [ "algorithm_group_by" "chrono" "chunked_ids" "dtype-categorical" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-i16" "dtype-i8" "dtype-time" "lazy" "rand" "rand_distr" "random" "regex" "reinterpret" "rows" "strings" "temporal" "zip_with" ];
+      };
+      "polars-error" = rec {
+        crateName = "polars-error";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "11m80a899kp45b3dz9jbvrn5001hw8izbdp7d2czrswrixwdx5k3";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "arrow-format";
+            packageId = "arrow-format";
+            optional = true;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        features = {
+          "arrow-format" = [ "dep:arrow-format" ];
+          "avro-schema" = [ "dep:avro-schema" ];
+          "object_store" = [ "dep:object_store" ];
+          "regex" = [ "dep:regex" ];
+        };
+        resolvedDefaultFeatures = [ "arrow-format" "regex" ];
+      };
+      "polars-io" = rec {
+        crateName = "polars-io";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1smamd34ghlxyxdd4aqdplk7k8xm1l626brmzlc4fvwlx3pmh13x";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+            optional = true;
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "home";
+            packageId = "home";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "memmap2";
+            packageId = "memmap2";
+            rename = "memmap";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-parquet";
+            packageId = "polars-parquet";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "net" "rt-multi-thread" "time" "sync" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            optional = true;
+            features = [ "io" "io-util" ];
+          }
+        ];
+        features = {
+          "async" = [ "async-trait" "futures" "tokio" "tokio-util" "arrow/io_ipc_write_async" "polars-error/regex" "polars-parquet?/async" ];
+          "async-trait" = [ "dep:async-trait" ];
+          "atoi_simd" = [ "dep:atoi_simd" ];
+          "avro" = [ "arrow/io_avro" "arrow/io_avro_compression" ];
+          "aws" = [ "object_store/aws" "cloud" "reqwest" ];
+          "azure" = [ "object_store/azure" "cloud" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "cloud" = [ "object_store" "async" "polars-error/object_store" "url" ];
+          "csv" = [ "atoi_simd" "polars-core/rows" "itoa" "ryu" "fast-float" "simdutf8" ];
+          "decompress" = [ "flate2/rust_backend" "zstd" ];
+          "decompress-fast" = [ "flate2/zlib-ng" "zstd" ];
+          "default" = [ "decompress" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-time/dtype-date" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-core/temporal" "polars-time/dtype-datetime" "chrono" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-core/temporal" "polars-time/dtype-time" ];
+          "fast-float" = [ "dep:fast-float" ];
+          "flate2" = [ "dep:flate2" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "futures" = [ "dep:futures" ];
+          "gcp" = [ "object_store/gcp" "cloud" ];
+          "http" = [ "object_store/http" "cloud" ];
+          "ipc" = [ "arrow/io_ipc" "arrow/io_ipc_compression" ];
+          "ipc_streaming" = [ "arrow/io_ipc" "arrow/io_ipc_compression" ];
+          "itoa" = [ "dep:itoa" ];
+          "json" = [ "polars-json" "simd-json" "atoi_simd" "serde_json" "dtype-struct" "csv" ];
+          "object_store" = [ "dep:object_store" ];
+          "parquet" = [ "polars-parquet" "polars-parquet/compression" ];
+          "partition" = [ "polars-core/partition_by" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "polars-parquet" = [ "dep:polars-parquet" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "python" = [ "polars-error/python" ];
+          "reqwest" = [ "dep:reqwest" ];
+          "ryu" = [ "dep:ryu" ];
+          "serde" = [ "dep:serde" "polars-core/serde-lazy" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "simd-json" = [ "dep:simd-json" ];
+          "simdutf8" = [ "dep:simdutf8" ];
+          "temporal" = [ "dtype-datetime" "dtype-date" "dtype-time" ];
+          "timezones" = [ "chrono-tz" "dtype-datetime" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "url" = [ "dep:url" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "async" "async-trait" "dtype-categorical" "futures" "ipc" "lazy" "parquet" "polars-parquet" "tokio" "tokio-util" ];
+      };
+      "polars-lazy" = rec {
+        crateName = "polars-lazy";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1mdml4hs574njb23mb9gl6x92x2bb4vdfzsazkl3ifq516s0awcx";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "lazy" "zip_with" "random" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            usesDefaultFeatures = false;
+            features = [ "lazy" ];
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-pipe";
+            packageId = "polars-pipe";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-plan";
+            packageId = "polars-plan";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-time";
+            packageId = "polars-time";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "abs" = [ "polars-plan/abs" ];
+          "approx_unique" = [ "polars-plan/approx_unique" ];
+          "arg_where" = [ "polars-plan/arg_where" ];
+          "array_any_all" = [ "polars-ops/array_any_all" "polars-plan/array_any_all" "dtype-array" ];
+          "asof_join" = [ "polars-plan/asof_join" "polars-time" "polars-ops/asof_join" ];
+          "async" = [ "polars-plan/async" "polars-io/cloud" "polars-pipe?/async" ];
+          "bigidx" = [ "polars-plan/bigidx" ];
+          "binary_encoding" = [ "polars-plan/binary_encoding" ];
+          "chunked_ids" = [ "polars-plan/chunked_ids" "polars-core/chunked_ids" "polars-ops/chunked_ids" ];
+          "cloud" = [ "async" "polars-pipe?/cloud" "polars-plan/cloud" "tokio" "futures" ];
+          "cloud_write" = [ "cloud" ];
+          "coalesce" = [ "polars-plan/coalesce" ];
+          "concat_str" = [ "polars-plan/concat_str" ];
+          "cov" = [ "polars-ops/cov" "polars-plan/cov" ];
+          "cross_join" = [ "polars-plan/cross_join" "polars-pipe?/cross_join" "polars-ops/cross_join" ];
+          "cse" = [ "polars-plan/cse" ];
+          "csv" = [ "polars-io/csv" "polars-plan/csv" "polars-pipe?/csv" ];
+          "cum_agg" = [ "polars-plan/cum_agg" ];
+          "cutqcut" = [ "polars-plan/cutqcut" "polars-ops/cutqcut" ];
+          "date_offset" = [ "polars-plan/date_offset" ];
+          "diff" = [ "polars-plan/diff" "polars-plan/diff" ];
+          "dot_diagram" = [ "polars-plan/dot_diagram" ];
+          "dtype-array" = [ "polars-plan/dtype-array" "polars-pipe?/dtype-array" "polars-ops/dtype-array" ];
+          "dtype-categorical" = [ "polars-plan/dtype-categorical" "polars-pipe?/dtype-categorical" ];
+          "dtype-date" = [ "polars-plan/dtype-date" "polars-time/dtype-date" "temporal" ];
+          "dtype-datetime" = [ "polars-plan/dtype-datetime" "polars-time/dtype-datetime" "temporal" ];
+          "dtype-decimal" = [ "polars-plan/dtype-decimal" "polars-pipe?/dtype-decimal" ];
+          "dtype-duration" = [ "polars-plan/dtype-duration" "polars-time/dtype-duration" "temporal" ];
+          "dtype-i16" = [ "polars-plan/dtype-i16" "polars-pipe?/dtype-i16" ];
+          "dtype-i8" = [ "polars-plan/dtype-i8" "polars-pipe?/dtype-i8" ];
+          "dtype-struct" = [ "polars-plan/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "temporal" ];
+          "dtype-u16" = [ "polars-plan/dtype-u16" "polars-pipe?/dtype-u16" ];
+          "dtype-u8" = [ "polars-plan/dtype-u8" "polars-pipe?/dtype-u8" ];
+          "dynamic_group_by" = [ "polars-plan/dynamic_group_by" "polars-time" "temporal" ];
+          "ewma" = [ "polars-plan/ewma" ];
+          "extract_groups" = [ "polars-plan/extract_groups" ];
+          "extract_jsonpath" = [ "polars-plan/extract_jsonpath" "polars-ops/extract_jsonpath" ];
+          "fmt" = [ "polars-core/fmt" "polars-plan/fmt" ];
+          "fused" = [ "polars-plan/fused" "polars-ops/fused" ];
+          "futures" = [ "dep:futures" ];
+          "hist" = [ "polars-plan/hist" ];
+          "horizontal_concat" = [ "polars-plan/horizontal_concat" "polars-core/horizontal_concat" ];
+          "interpolate" = [ "polars-plan/interpolate" ];
+          "ipc" = [ "polars-io/ipc" "polars-plan/ipc" "polars-pipe?/ipc" ];
+          "is_first_distinct" = [ "polars-plan/is_first_distinct" ];
+          "is_in" = [ "polars-plan/is_in" "polars-ops/is_in" ];
+          "is_last_distinct" = [ "polars-plan/is_last_distinct" ];
+          "is_unique" = [ "polars-plan/is_unique" ];
+          "json" = [ "polars-io/json" "polars-plan/json" "polars-json" "polars-pipe/json" ];
+          "list_any_all" = [ "polars-ops/list_any_all" "polars-plan/list_any_all" ];
+          "list_count" = [ "polars-ops/list_count" "polars-plan/list_count" ];
+          "list_drop_nulls" = [ "polars-ops/list_drop_nulls" "polars-plan/list_drop_nulls" ];
+          "list_gather" = [ "polars-ops/list_gather" "polars-plan/list_gather" ];
+          "list_sample" = [ "polars-ops/list_sample" "polars-plan/list_sample" ];
+          "list_sets" = [ "polars-plan/list_sets" "polars-ops/list_sets" ];
+          "list_to_struct" = [ "polars-plan/list_to_struct" ];
+          "log" = [ "polars-plan/log" ];
+          "merge_sorted" = [ "polars-plan/merge_sorted" ];
+          "meta" = [ "polars-plan/meta" ];
+          "mode" = [ "polars-plan/mode" ];
+          "moment" = [ "polars-plan/moment" "polars-ops/moment" ];
+          "nightly" = [ "polars-core/nightly" "polars-pipe?/nightly" "polars-plan/nightly" ];
+          "object" = [ "polars-plan/object" ];
+          "panic_on_schema" = [ "polars-plan/panic_on_schema" ];
+          "parquet" = [ "polars-io/parquet" "polars-plan/parquet" "polars-pipe?/parquet" ];
+          "pct_change" = [ "polars-plan/pct_change" ];
+          "peaks" = [ "polars-plan/peaks" ];
+          "pivot" = [ "polars-core/rows" "polars-ops/pivot" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "polars-pipe" = [ "dep:polars-pipe" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "propagate_nans" = [ "polars-plan/propagate_nans" ];
+          "pyo3" = [ "dep:pyo3" ];
+          "python" = [ "pyo3" "polars-plan/python" "polars-core/python" "polars-io/python" ];
+          "random" = [ "polars-plan/random" ];
+          "range" = [ "polars-plan/range" ];
+          "rank" = [ "polars-plan/rank" ];
+          "regex" = [ "polars-plan/regex" ];
+          "repeat_by" = [ "polars-plan/repeat_by" ];
+          "replace" = [ "polars-plan/replace" ];
+          "rle" = [ "polars-plan/rle" "polars-ops/rle" ];
+          "rolling_window" = [ "polars-plan/rolling_window" "polars-time/rolling_window" ];
+          "round_series" = [ "polars-plan/round_series" "polars-ops/round_series" ];
+          "row_hash" = [ "polars-plan/row_hash" ];
+          "search_sorted" = [ "polars-plan/search_sorted" ];
+          "semi_anti_join" = [ "polars-plan/semi_anti_join" ];
+          "serde" = [ "polars-plan/serde" "arrow/serde" "polars-core/serde-lazy" "polars-time?/serde" "polars-io/serde" "polars-ops/serde" ];
+          "sign" = [ "polars-plan/sign" ];
+          "streaming" = [ "chunked_ids" "polars-pipe" "polars-plan/streaming" "polars-ops/chunked_ids" ];
+          "string_encoding" = [ "polars-plan/string_encoding" ];
+          "string_pad" = [ "polars-plan/string_pad" ];
+          "string_reverse" = [ "polars-plan/string_reverse" ];
+          "string_to_integer" = [ "polars-plan/string_to_integer" ];
+          "strings" = [ "polars-plan/strings" ];
+          "temporal" = [ "dtype-datetime" "dtype-date" "dtype-time" "dtype-i8" "dtype-i16" "dtype-duration" "polars-plan/temporal" ];
+          "test" = [ "polars-plan/debugging" "panic_on_schema" "rolling_window" "rank" "round_series" "csv" "dtype-categorical" "cum_agg" "regex" "polars-core/fmt" "diff" "abs" "parquet" "ipc" "dtype-date" ];
+          "test_all" = [ "test" "strings" "regex" "ipc" "row_hash" "string_pad" "string_to_integer" "search_sorted" "top_k" "pivot" "semi_anti_join" "cse" ];
+          "timezones" = [ "polars-plan/timezones" ];
+          "tokio" = [ "dep:tokio" ];
+          "top_k" = [ "polars-plan/top_k" ];
+          "trigonometry" = [ "polars-plan/trigonometry" ];
+          "true_div" = [ "polars-plan/true_div" ];
+          "unique_counts" = [ "polars-plan/unique_counts" ];
+        };
+        resolvedDefaultFeatures = [ "abs" "cross_join" "cum_agg" "dtype-categorical" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-i16" "dtype-i8" "dtype-time" "is_in" "log" "meta" "parquet" "polars-time" "regex" "round_series" "strings" "temporal" "trigonometry" ];
+      };
+      "polars-ops" = rec {
+        crateName = "polars-ops";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "13m7dh4vpdmcah04a7kql933l7y7045f0hybbmgff4dbav2ay29f";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "argminmax";
+            packageId = "argminmax";
+            usesDefaultFeatures = false;
+            features = [ "float" ];
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-compute";
+            packageId = "polars-compute";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "algorithm_group_by" "zip_with" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "aho-corasick" = [ "dep:aho-corasick" ];
+          "array_any_all" = [ "dtype-array" ];
+          "asof_join" = [ "polars-core/asof_join" ];
+          "base64" = [ "dep:base64" ];
+          "big_idx" = [ "polars-core/bigidx" ];
+          "binary_encoding" = [ "base64" "hex" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "chunked_ids" = [ "polars-core/chunked_ids" ];
+          "cutqcut" = [ "dtype-categorical" "dtype-struct" ];
+          "dtype-array" = [ "polars-core/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-core/temporal" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-core/temporal" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-core/temporal" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" "polars-core/temporal" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-core/temporal" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" ];
+          "extract_groups" = [ "dtype-struct" "polars-core/regex" ];
+          "extract_jsonpath" = [ "serde_json" "jsonpath_lib" "polars-json" ];
+          "find_many" = [ "aho-corasick" ];
+          "group_by_list" = [ "polars-core/group_by_list" ];
+          "hex" = [ "dep:hex" ];
+          "hist" = [ "dtype-categorical" "dtype-struct" ];
+          "is_in" = [ "polars-core/reinterpret" ];
+          "jsonpath_lib" = [ "dep:jsonpath_lib" ];
+          "list_to_struct" = [ "polars-core/dtype-struct" ];
+          "nightly" = [ "polars-utils/nightly" ];
+          "object" = [ "polars-core/object" ];
+          "pct_change" = [ "diff" ];
+          "performant" = [ "polars-core/performant" "fused" ];
+          "pivot" = [ "polars-core/reinterpret" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "rand" = [ "dep:rand" ];
+          "rand_distr" = [ "dep:rand_distr" ];
+          "random" = [ "rand" "rand_distr" ];
+          "rank" = [ "rand" ];
+          "replace" = [ "is_in" ];
+          "rle" = [ "dtype-struct" ];
+          "rolling_window" = [ "polars-core/rolling_window" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "simd" = [ "argminmax/nightly_simd" ];
+          "string_encoding" = [ "base64" "hex" ];
+          "string_pad" = [ "polars-core/strings" ];
+          "string_reverse" = [ "polars-core/strings" "unicode-reverse" ];
+          "string_to_integer" = [ "polars-core/strings" ];
+          "strings" = [ "polars-core/strings" ];
+          "timezones" = [ "chrono-tz" "chrono" ];
+          "unicode-reverse" = [ "dep:unicode-reverse" ];
+        };
+        resolvedDefaultFeatures = [ "abs" "cross_join" "cum_agg" "dtype-categorical" "is_in" "log" "round_series" "search_sorted" "strings" ];
+      };
+      "polars-parquet" = rec {
+        crateName = "polars-parquet";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "0gay037sw5hcg2q93pxcfh7krcchl1px8g838d8vhjpnn5klv8kv";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+          "Apache Arrow <dev@arrow.apache.org>"
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+            optional = true;
+          }
+          {
+            name = "base64";
+            packageId = "base64";
+          }
+          {
+            name = "brotli";
+            packageId = "brotli";
+            optional = true;
+          }
+          {
+            name = "ethnum";
+            packageId = "ethnum";
+          }
+          {
+            name = "flate2";
+            packageId = "flate2";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "lz4";
+            packageId = "lz4";
+            optional = true;
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "parquet-format-safe";
+            packageId = "parquet-format-safe";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" "io_ipc" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "seq-macro";
+            packageId = "seq-macro";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+          }
+          {
+            name = "snap";
+            packageId = "snap";
+            optional = true;
+          }
+          {
+            name = "streaming-decompression";
+            packageId = "streaming-decompression";
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "async" = [ "async-stream" "futures" "parquet-format-safe/async" ];
+          "async-stream" = [ "dep:async-stream" ];
+          "bloom_filter" = [ "xxhash-rust" ];
+          "brotli" = [ "dep:brotli" ];
+          "compression" = [ "zstd" "gzip" "snappy" "lz4" "brotli" ];
+          "fallible-streaming-iterator" = [ "dep:fallible-streaming-iterator" ];
+          "flate2" = [ "dep:flate2" ];
+          "futures" = [ "dep:futures" ];
+          "gzip" = [ "flate2/rust_backend" ];
+          "gzip_zlib_ng" = [ "flate2/zlib-ng" ];
+          "lz4" = [ "dep:lz4" ];
+          "serde" = [ "dep:serde" ];
+          "serde_types" = [ "serde" ];
+          "snap" = [ "dep:snap" ];
+          "snappy" = [ "snap" ];
+          "xxhash-rust" = [ "dep:xxhash-rust" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "async" "async-stream" "brotli" "compression" "flate2" "futures" "gzip" "lz4" "snap" "snappy" "zstd" ];
+      };
+      "polars-pipe" = rec {
+        crateName = "polars-pipe";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "00217q51mnq7i57k3sax293xnwghm5hridbpgl11fffcfg8fmdyr";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-channel";
+            packageId = "crossbeam-channel";
+          }
+          {
+            name = "crossbeam-queue";
+            packageId = "crossbeam-queue";
+          }
+          {
+            name = "enum_dispatch";
+            packageId = "enum_dispatch";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-compute";
+            packageId = "polars-compute";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "lazy" "zip_with" "random" "rows" "chunked_ids" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            usesDefaultFeatures = false;
+            features = [ "ipc" ];
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+            features = [ "search_sorted" ];
+          }
+          {
+            name = "polars-plan";
+            packageId = "polars-plan";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-row";
+            packageId = "polars-row";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+            features = [ "sysinfo" ];
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "async" = [ "polars-plan/async" "polars-io/async" ];
+          "cloud" = [ "async" "polars-io/cloud" "polars-plan/cloud" "tokio" "futures" ];
+          "cross_join" = [ "polars-ops/cross_join" ];
+          "csv" = [ "polars-plan/csv" "polars-io/csv" ];
+          "dtype-array" = [ "polars-core/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" ];
+          "futures" = [ "dep:futures" ];
+          "ipc" = [ "polars-plan/ipc" "polars-io/ipc" ];
+          "json" = [ "polars-plan/json" "polars-io/json" ];
+          "nightly" = [ "polars-core/nightly" "polars-utils/nightly" "hashbrown/nightly" ];
+          "parquet" = [ "polars-plan/parquet" "polars-io/parquet" "polars-io/async" ];
+          "test" = [ "polars-core/chunked_ids" ];
+          "tokio" = [ "dep:tokio" ];
+        };
+        resolvedDefaultFeatures = [ "cross_join" "dtype-categorical" "dtype-i16" "dtype-i8" "parquet" ];
+      };
+      "polars-plan" = rec {
+        crateName = "polars-plan";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1l5ca38v7ksq4595wdr6wsd74pdgszwivq9y8wfc6l6h4ib1fjiq";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "lazy" "zip_with" "random" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            usesDefaultFeatures = false;
+            features = [ "lazy" ];
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-parquet";
+            packageId = "polars-parquet";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-time";
+            packageId = "polars-time";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "strum_macros";
+            packageId = "strum_macros";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "abs" = [ "polars-ops/abs" ];
+          "approx_unique" = [ "polars-ops/approx_unique" ];
+          "array_any_all" = [ "polars-ops/array_any_all" "dtype-array" ];
+          "asof_join" = [ "polars-core/asof_join" "polars-time" "polars-ops/asof_join" ];
+          "async" = [ "polars-io/async" ];
+          "bigidx" = [ "polars-core/bigidx" ];
+          "binary_encoding" = [ "polars-ops/binary_encoding" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "chunked_ids" = [ "polars-core/chunked_ids" ];
+          "ciborium" = [ "dep:ciborium" ];
+          "cloud" = [ "async" "polars-io/cloud" ];
+          "cov" = [ "polars-ops/cov" ];
+          "cross_join" = [ "polars-ops/cross_join" ];
+          "csv" = [ "polars-io/csv" ];
+          "cum_agg" = [ "polars-ops/cum_agg" ];
+          "cutqcut" = [ "polars-ops/cutqcut" ];
+          "date_offset" = [ "polars-time" "chrono" ];
+          "diff" = [ "polars-ops/diff" ];
+          "dtype-array" = [ "polars-core/dtype-array" "polars-ops/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-time/dtype-date" "temporal" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-time/dtype-datetime" "temporal" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-time/dtype-duration" "temporal" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-time/dtype-time" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" ];
+          "dynamic_group_by" = [ "polars-core/dynamic_group_by" ];
+          "ewma" = [ "polars-ops/ewma" ];
+          "extract_groups" = [ "regex" "dtype-struct" "polars-ops/extract_groups" ];
+          "extract_jsonpath" = [ "polars-ops/extract_jsonpath" ];
+          "ffi_plugin" = [ "libloading" "polars-ffi" ];
+          "find_many" = [ "polars-ops/find_many" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "fused" = [ "polars-ops/fused" ];
+          "futures" = [ "dep:futures" ];
+          "hist" = [ "polars-ops/hist" ];
+          "interpolate" = [ "polars-ops/interpolate" ];
+          "ipc" = [ "polars-io/ipc" ];
+          "is_first_distinct" = [ "polars-core/is_first_distinct" "polars-ops/is_first_distinct" ];
+          "is_in" = [ "polars-ops/is_in" ];
+          "is_last_distinct" = [ "polars-core/is_last_distinct" "polars-ops/is_last_distinct" ];
+          "is_unique" = [ "polars-ops/is_unique" ];
+          "json" = [ "polars-io/json" "polars-json" ];
+          "libloading" = [ "dep:libloading" ];
+          "list_any_all" = [ "polars-ops/list_any_all" ];
+          "list_count" = [ "polars-ops/list_count" ];
+          "list_drop_nulls" = [ "polars-ops/list_drop_nulls" ];
+          "list_gather" = [ "polars-ops/list_gather" ];
+          "list_sample" = [ "polars-ops/list_sample" ];
+          "list_sets" = [ "polars-ops/list_sets" ];
+          "list_to_struct" = [ "polars-ops/list_to_struct" ];
+          "log" = [ "polars-ops/log" ];
+          "merge_sorted" = [ "polars-ops/merge_sorted" ];
+          "mode" = [ "polars-ops/mode" ];
+          "moment" = [ "polars-ops/moment" ];
+          "nightly" = [ "polars-utils/nightly" "polars-ops/nightly" ];
+          "object" = [ "polars-core/object" ];
+          "parquet" = [ "polars-io/parquet" "polars-parquet" ];
+          "pct_change" = [ "polars-ops/pct_change" ];
+          "peaks" = [ "polars-ops/peaks" ];
+          "pivot" = [ "polars-core/rows" "polars-ops/pivot" ];
+          "polars-ffi" = [ "dep:polars-ffi" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "polars-parquet" = [ "dep:polars-parquet" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "propagate_nans" = [ "polars-ops/propagate_nans" ];
+          "python" = [ "dep:pyo3" "ciborium" ];
+          "random" = [ "polars-core/random" ];
+          "rank" = [ "polars-ops/rank" ];
+          "regex" = [ "dep:regex" ];
+          "repeat_by" = [ "polars-ops/repeat_by" ];
+          "replace" = [ "polars-ops/replace" ];
+          "rle" = [ "polars-ops/rle" ];
+          "rolling_window" = [ "polars-core/rolling_window" "polars-time/rolling_window" "polars-ops/rolling_window" "polars-time/rolling_window" ];
+          "round_series" = [ "polars-ops/round_series" ];
+          "row_hash" = [ "polars-core/row_hash" "polars-ops/hash" ];
+          "search_sorted" = [ "polars-ops/search_sorted" ];
+          "semi_anti_join" = [ "polars-ops/semi_anti_join" ];
+          "serde" = [ "dep:serde" "polars-core/serde-lazy" "polars-time/serde" "polars-io/serde" "polars-ops/serde" ];
+          "string_encoding" = [ "polars-ops/string_encoding" ];
+          "string_pad" = [ "polars-ops/string_pad" ];
+          "string_reverse" = [ "polars-ops/string_reverse" ];
+          "string_to_integer" = [ "polars-ops/string_to_integer" ];
+          "strings" = [ "polars-core/strings" "polars-ops/strings" ];
+          "temporal" = [ "polars-core/temporal" "dtype-date" "dtype-datetime" "dtype-time" "dtype-i8" "dtype-i16" ];
+          "timezones" = [ "chrono-tz" "polars-time/timezones" "polars-core/timezones" "regex" ];
+          "top_k" = [ "polars-ops/top_k" ];
+          "unique_counts" = [ "polars-ops/unique_counts" ];
+        };
+        resolvedDefaultFeatures = [ "abs" "cross_join" "cum_agg" "dtype-categorical" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-i16" "dtype-i8" "dtype-time" "is_in" "log" "meta" "parquet" "polars-parquet" "polars-time" "regex" "round_series" "strings" "temporal" "trigonometry" ];
+      };
+      "polars-row" = rec {
+        crateName = "polars-row";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "0by7x6jlj5dwr3f1gj49v8w65l3kpqhwhzb9qzlv6gdqrdx2ycij";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+
+      };
+      "polars-sql" = rec {
+        crateName = "polars-sql";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1dcmm993gycw75a6c5hxcr6h2cy6fa2r3hsbx19h9zgxvxnlq2wz";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-lazy";
+            packageId = "polars-lazy";
+            usesDefaultFeatures = false;
+            features = [ "strings" "cross_join" "trigonometry" "abs" "round_series" "log" "regex" "is_in" "meta" "cum_agg" "dtype-date" ];
+          }
+          {
+            name = "polars-plan";
+            packageId = "polars-plan";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sqlparser";
+            packageId = "sqlparser";
+          }
+        ];
+        features = {
+          "csv" = [ "polars-lazy/csv" ];
+          "diagonal_concat" = [ "polars-lazy/diagonal_concat" ];
+          "ipc" = [ "polars-lazy/ipc" ];
+          "json" = [ "polars-lazy/json" ];
+          "parquet" = [ "polars-lazy/parquet" ];
+          "semi_anti_join" = [ "polars-lazy/semi_anti_join" ];
+        };
+        resolvedDefaultFeatures = [ "parquet" ];
+      };
+      "polars-time" = rec {
+        crateName = "polars-time";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1l24fmpv5v1qshxfd4592z8x6bnjlwy4njhf9rcbdlbbr6gn9qny";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "atoi";
+            packageId = "atoi";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "now";
+            packageId = "now";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" "compute" "temporal" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "dtype-datetime" "dtype-duration" "dtype-time" "dtype-date" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        features = {
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-core/temporal" ];
+          "dtype-datetime" = [ "polars-core/dtype-date" "polars-core/temporal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-core/temporal" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-core/temporal" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "rolling_window" = [ "polars-core/rolling_window" "dtype-duration" ];
+          "serde" = [ "dep:serde" ];
+          "test" = [ "dtype-date" "dtype-datetime" "polars-core/fmt" ];
+          "timezones" = [ "chrono-tz" "dtype-datetime" "polars-core/timezones" "arrow/timezones" "polars-ops/timezones" ];
+        };
+        resolvedDefaultFeatures = [ "dtype-date" "dtype-datetime" "dtype-duration" "dtype-time" ];
+      };
+      "polars-utils" = rec {
+        crateName = "polars-utils";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1nmvfqwyzbaxcw457amspz479y5vcnpalq043awxfixdfx5clx5i";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "sysinfo";
+            packageId = "sysinfo";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "sysinfo" = [ "dep:sysinfo" ];
+        };
+        resolvedDefaultFeatures = [ "sysinfo" ];
+      };
+      "ppv-lite86" = rec {
+        crateName = "ppv-lite86";
+        version = "0.2.17";
+        edition = "2018";
+        sha256 = "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v";
+        authors = [
+          "The CryptoCorrosion Contributors"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "simd" "std" ];
+      };
+      "proc-macro2" = rec {
+        crateName = "proc-macro2";
+        version = "1.0.69";
+        edition = "2021";
+        sha256 = "1nljgyllbm3yr3pa081bf83gxh6l4zvjqzaldw7v4mj9xfgihk0k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "quote" = rec {
+        crateName = "quote";
+        version = "1.0.33";
+        edition = "2018";
+        sha256 = "1biw54hbbr12wdwjac55z1m2x2rylciw83qnjn564a3096jgqrsj";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "rand" = rec {
+        crateName = "rand";
+        version = "0.8.5";
+        edition = "2018";
+        sha256 = "013l6931nn7gkc23jz5mm3qdhf93jjf0fg64nz2lp4i51qd8vbrl";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "rand_chacha";
+            packageId = "rand_chacha";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "alloc" = [ "rand_core/alloc" ];
+          "default" = [ "std" "std_rng" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "libc" = [ "dep:libc" ];
+          "log" = [ "dep:log" ];
+          "packed_simd" = [ "dep:packed_simd" ];
+          "rand_chacha" = [ "dep:rand_chacha" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" "rand_core/serde1" ];
+          "simd_support" = [ "packed_simd" ];
+          "std" = [ "rand_core/std" "rand_chacha/std" "alloc" "getrandom" "libc" ];
+          "std_rng" = [ "rand_chacha" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "getrandom" "libc" "rand_chacha" "small_rng" "std" "std_rng" ];
+      };
+      "rand_chacha" = rec {
+        crateName = "rand_chacha";
+        version = "0.3.1";
+        edition = "2018";
+        sha256 = "123x2adin558xbhvqb8w4f6syjsdkmqff8cxwhmjacpsl1ihmhg6";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+          "The CryptoCorrosion Contributors"
+        ];
+        dependencies = [
+          {
+            name = "ppv-lite86";
+            packageId = "ppv-lite86";
+            usesDefaultFeatures = false;
+            features = [ "simd" ];
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "ppv-lite86/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "rand_core" = rec {
+        crateName = "rand_core";
+        version = "0.6.4";
+        edition = "2018";
+        sha256 = "0b4j2v4cb5krak1pv6kakv4sz6xcwbrmy2zckc32hsigbrwy82zc";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+        ];
+        features = {
+          "getrandom" = [ "dep:getrandom" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "alloc" "getrandom" "getrandom/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "getrandom" "std" ];
+      };
+      "rand_distr" = rec {
+        crateName = "rand_distr";
+        version = "0.4.3";
+        edition = "2018";
+        sha256 = "0cgfwg3z0pkqhrl0x90c77kx70r6g9z4m6fxq9v0h2ibr2dhpjrj";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+            features = [ "libm" ];
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rand";
+            packageId = "rand";
+            usesDefaultFeatures = false;
+            features = [ "std_rng" "std" "small_rng" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "rand/alloc" ];
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" "rand/serde1" ];
+          "std" = [ "alloc" "rand/std" ];
+          "std_math" = [ "num-traits/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "rayon" = rec {
+        crateName = "rayon";
+        version = "1.8.0";
+        edition = "2021";
+        sha256 = "1cfdnvchf7j4cpha5jkcrrsr61li9i9lp5ak7xdq6d3pvc1xn9ww";
+        authors = [
+          "Niko Matsakis <niko@alum.mit.edu>"
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon-core";
+            packageId = "rayon-core";
+          }
+        ];
+
+      };
+      "rayon-core" = rec {
+        crateName = "rayon-core";
+        version = "1.12.0";
+        edition = "2021";
+        links = "rayon-core";
+        sha256 = "1vaq0q71yfvcwlmia0iqf6ixj2fibjcf2xjy92n1m1izv1mgpqsw";
+        authors = [
+          "Niko Matsakis <niko@alum.mit.edu>"
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-deque";
+            packageId = "crossbeam-deque";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+        ];
+
+      };
+      "redox_syscall" = rec {
+        crateName = "redox_syscall";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1aiifyz5dnybfvkk4cdab9p2kmphag1yad6iknc7aszlxxldf8j7";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+        features = {
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "bitflags/rustc-dep-of-std" ];
+        };
+      };
+      "regex" = rec {
+        crateName = "regex";
+        version = "1.10.2";
+        edition = "2021";
+        sha256 = "0hxkd814n4irind8im5c9am221ri6bprx49nc7yxv02ykhd9a2rq";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata";
+            usesDefaultFeatures = false;
+            features = [ "alloc" "syntax" "meta" "nfa-pikevm" ];
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf" "unicode" "regex-syntax/default" ];
+          "logging" = [ "aho-corasick?/logging" "memchr?/logging" "regex-automata/logging" ];
+          "perf" = [ "perf-cache" "perf-dfa" "perf-onepass" "perf-backtrack" "perf-inline" "perf-literal" ];
+          "perf-backtrack" = [ "regex-automata/nfa-backtrack" ];
+          "perf-dfa" = [ "regex-automata/hybrid" ];
+          "perf-dfa-full" = [ "regex-automata/dfa-build" "regex-automata/dfa-search" ];
+          "perf-inline" = [ "regex-automata/perf-inline" ];
+          "perf-literal" = [ "dep:aho-corasick" "dep:memchr" "regex-automata/perf-literal" ];
+          "perf-onepass" = [ "regex-automata/dfa-onepass" ];
+          "std" = [ "aho-corasick?/std" "memchr?/std" "regex-automata/std" "regex-syntax/std" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "regex-automata/unicode" "regex-syntax/unicode" ];
+          "unicode-age" = [ "regex-automata/unicode-age" "regex-syntax/unicode-age" ];
+          "unicode-bool" = [ "regex-automata/unicode-bool" "regex-syntax/unicode-bool" ];
+          "unicode-case" = [ "regex-automata/unicode-case" "regex-syntax/unicode-case" ];
+          "unicode-gencat" = [ "regex-automata/unicode-gencat" "regex-syntax/unicode-gencat" ];
+          "unicode-perl" = [ "regex-automata/unicode-perl" "regex-automata/unicode-word-boundary" "regex-syntax/unicode-perl" ];
+          "unicode-script" = [ "regex-automata/unicode-script" "regex-syntax/unicode-script" ];
+          "unicode-segment" = [ "regex-automata/unicode-segment" "regex-syntax/unicode-segment" ];
+          "unstable" = [ "pattern" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "perf" "perf-backtrack" "perf-cache" "perf-dfa" "perf-inline" "perf-literal" "perf-onepass" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "regex-automata" = rec {
+        crateName = "regex-automata";
+        version = "0.4.3";
+        edition = "2021";
+        sha256 = "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "syntax" "perf" "unicode" "meta" "nfa" "dfa" "hybrid" ];
+          "dfa" = [ "dfa-build" "dfa-search" "dfa-onepass" ];
+          "dfa-build" = [ "nfa-thompson" "dfa-search" ];
+          "dfa-onepass" = [ "nfa-thompson" ];
+          "hybrid" = [ "alloc" "nfa-thompson" ];
+          "internal-instrument" = [ "internal-instrument-pikevm" ];
+          "internal-instrument-pikevm" = [ "logging" "std" ];
+          "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ];
+          "meta" = [ "syntax" "nfa-pikevm" ];
+          "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ];
+          "nfa-backtrack" = [ "nfa-thompson" ];
+          "nfa-pikevm" = [ "nfa-thompson" ];
+          "nfa-thompson" = [ "alloc" ];
+          "perf" = [ "perf-inline" "perf-literal" ];
+          "perf-literal" = [ "perf-literal-substring" "perf-literal-multisubstring" ];
+          "perf-literal-multisubstring" = [ "std" "dep:aho-corasick" ];
+          "perf-literal-substring" = [ "aho-corasick?/perf-literal" "dep:memchr" ];
+          "std" = [ "regex-syntax?/std" "memchr?/std" "aho-corasick?/std" "alloc" ];
+          "syntax" = [ "dep:regex-syntax" "alloc" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" "regex-syntax?/unicode" ];
+          "unicode-age" = [ "regex-syntax?/unicode-age" ];
+          "unicode-bool" = [ "regex-syntax?/unicode-bool" ];
+          "unicode-case" = [ "regex-syntax?/unicode-case" ];
+          "unicode-gencat" = [ "regex-syntax?/unicode-gencat" ];
+          "unicode-perl" = [ "regex-syntax?/unicode-perl" ];
+          "unicode-script" = [ "regex-syntax?/unicode-script" ];
+          "unicode-segment" = [ "regex-syntax?/unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "dfa-onepass" "dfa-search" "hybrid" "meta" "nfa-backtrack" "nfa-pikevm" "nfa-thompson" "perf-inline" "perf-literal" "perf-literal-multisubstring" "perf-literal-substring" "std" "syntax" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" ];
+      };
+      "regex-syntax" = rec {
+        crateName = "regex-syntax";
+        version = "0.8.2";
+        edition = "2021";
+        sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" "unicode" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "rustc-demangle" = rec {
+        crateName = "rustc-demangle";
+        version = "0.1.23";
+        edition = "2015";
+        sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "rustc_version" = rec {
+        crateName = "rustc_version";
+        version = "0.4.0";
+        edition = "2018";
+        sha256 = "0rpk9rcdk405xhbmgclsh4pai0svn49x35aggl4nhbkd4a2zb85z";
+        authors = [
+          "Dirkjan Ochtman <dirkjan@ochtman.nl>"
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "semver";
+            packageId = "semver";
+          }
+        ];
+
+      };
+      "rustix" = rec {
+        crateName = "rustix";
+        version = "0.38.21";
+        edition = "2021";
+        sha256 = "18q2mx7gnnl1238psb1r0avdw00l8y0jxkxgimyhmmg50q2nnhib";
+        authors = [
+          "Dan Gohman <dev@sunfishcode.online>"
+          "Jakub Konka <kubkon@jakubkonka.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."windows" or false)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+          }
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+            features = [ "extra_traits" ];
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."windows" or false)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+            features = [ "extra_traits" ];
+          }
+          {
+            name = "linux-raw-sys";
+            packageId = "linux-raw-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((("android" == target."os" or null) || ("linux" == target."os" or null)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
+            features = [ "general" "ioctl" "no_std" ];
+          }
+          {
+            name = "linux-raw-sys";
+            packageId = "linux-raw-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
+            features = [ "general" "errno" "ioctl" "no_std" "elf" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_NetworkManagement_IpHelper" "Win32_System_Threading" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "errno";
+            packageId = "errno";
+            rename = "libc_errno";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "all-apis" = [ "event" "fs" "io_uring" "mm" "mount" "net" "param" "pipe" "process" "procfs" "pty" "rand" "runtime" "shm" "stdio" "system" "termios" "thread" "time" ];
+          "default" = [ "std" "use-libc-auxv" ];
+          "io_uring" = [ "event" "fs" "net" "linux-raw-sys/io_uring" ];
+          "itoa" = [ "dep:itoa" ];
+          "libc" = [ "dep:libc" ];
+          "libc_errno" = [ "dep:libc_errno" ];
+          "linux_latest" = [ "linux_4_11" ];
+          "net" = [ "linux-raw-sys/net" "linux-raw-sys/netlink" "linux-raw-sys/if_ether" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "param" = [ "fs" ];
+          "process" = [ "linux-raw-sys/prctl" ];
+          "procfs" = [ "once_cell" "itoa" "fs" ];
+          "pty" = [ "itoa" "fs" ];
+          "runtime" = [ "linux-raw-sys/prctl" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" "linux-raw-sys/rustc-dep-of-std" "bitflags/rustc-dep-of-std" "compiler_builtins?/rustc-dep-of-std" ];
+          "shm" = [ "fs" ];
+          "std" = [ "bitflags/std" "alloc" "libc?/std" "libc_errno?/std" ];
+          "system" = [ "linux-raw-sys/system" ];
+          "thread" = [ "linux-raw-sys/prctl" ];
+          "use-libc" = [ "libc_errno" "libc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fs" "std" "use-libc-auxv" ];
+      };
+      "rustversion" = rec {
+        crateName = "rustversion";
+        version = "1.0.14";
+        edition = "2018";
+        sha256 = "1x1pz1yynk5xzzrazk2svmidj69jhz89dz5vrc28sixl20x1iz3z";
+        procMacro = true;
+        build = "build/build.rs";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "ryu" = rec {
+        crateName = "ryu";
+        version = "1.0.15";
+        edition = "2018";
+        sha256 = "0hfphpn1xnpzxwj8qg916ga1lyc33lc03lnf1gb3wwpglj6wrm0s";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "scopeguard" = rec {
+        crateName = "scopeguard";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "0jcz9sd47zlsgcnm1hdw0664krxwb5gczlif4qngj2aif8vky54l";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+        };
+      };
+      "semver" = rec {
+        crateName = "semver";
+        version = "1.0.20";
+        edition = "2018";
+        sha256 = "140hmbfa743hbmah1zjf07s8apavhvn04204qjigjiz5w6iscvw3";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "seq-macro" = rec {
+        crateName = "seq-macro";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "1d50kbaslrrd0374ivx15jg57f03y5xzil1wd2ajlvajzlkbzw53";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "serde" = rec {
+        crateName = "serde";
+        version = "1.0.192";
+        edition = "2018";
+        sha256 = "00ghhaabyrnr2cn504lckyqzh3fwr8k7pxnhhardr1djhj2a18mw";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            optional = true;
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "derive" "serde_derive" "std" ];
+      };
+      "serde_derive" = rec {
+        crateName = "serde_derive";
+        version = "1.0.192";
+        edition = "2015";
+        sha256 = "1hgvm47ffd748sx22z1da7mgcfjmpr60gqzkff0a9yn9przj1iyn";
+        procMacro = true;
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "serde_json" = rec {
+        crateName = "serde_json";
+        version = "1.0.108";
+        edition = "2021";
+        sha256 = "0ssj59s7lpzqh1m50kfzlnrip0p0jg9lmhn4098i33a0mhz7w71x";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" ];
+          "default" = [ "std" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "preserve_order" = [ "indexmap" "std" ];
+          "std" = [ "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sha2" = rec {
+        crateName = "sha2";
+        version = "0.10.8";
+        edition = "2018";
+        sha256 = "1j1x78zk9il95w9iv46dh9wm73r6xrgj32y6lzzw7bxws9dbfgbr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha2-asm" ];
+          "asm-aarch64" = [ "asm" ];
+          "default" = [ "std" ];
+          "oid" = [ "digest/oid" ];
+          "sha2-asm" = [ "dep:sha2-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "signature" = rec {
+        crateName = "signature";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "1pi9hd5vqfr3q3k49k37z06p7gs5si0in32qia4mmr1dancr6m3p";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "derive" = [ "dep:derive" ];
+          "digest" = [ "dep:digest" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "std" = [ "alloc" "rand_core?/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "simdutf8" = rec {
+        crateName = "simdutf8";
+        version = "0.1.4";
+        edition = "2018";
+        sha256 = "0fi6zvnldaw7g726wnm9vvpv4s89s5jsk7fgp3rg2l99amw64zzj";
+        authors = [
+          "Hans Kratz <hans@appfour.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "slab" = rec {
+        crateName = "slab";
+        version = "0.4.9";
+        edition = "2018";
+        sha256 = "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "smartstring" = rec {
+        crateName = "smartstring";
+        version = "1.0.1";
+        edition = "2021";
+        sha256 = "0agf4x0jz79r30aqibyfjm1h9hrjdh0harcqcvb2vapv7rijrdrz";
+        authors = [
+          "Bodil Stokke <bodil@bodil.org>"
+        ];
+        dependencies = [
+          {
+            name = "static_assertions";
+            packageId = "static_assertions";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" ];
+          "proptest" = [ "dep:proptest" ];
+          "serde" = [ "dep:serde" ];
+          "test" = [ "std" "arbitrary" "arbitrary/derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "snap" = rec {
+        crateName = "snap";
+        version = "1.1.0";
+        edition = "2018";
+        sha256 = "0c882cs4wbyi34nw8njpxa729gyi6sj71h8rj4ykbdvyxyv0m7sy";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+
+      };
+      "socket2" = rec {
+        crateName = "socket2";
+        version = "0.5.5";
+        edition = "2021";
+        sha256 = "1sgq315f1njky114ip7wcy83qlphv9qclprfjwvxcpfblmcsqpvv";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "all" ];
+      };
+      "spki" = rec {
+        crateName = "spki";
+        version = "0.7.2";
+        edition = "2021";
+        sha256 = "0jhq00sv4w3psdi6li3vjjmspc6z2d9b1wc1srbljircy1p9j7lx";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "base64ct";
+            packageId = "base64ct";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "base64ct?/alloc" "der/alloc" ];
+          "arbitrary" = [ "std" "dep:arbitrary" "der/arbitrary" ];
+          "base64" = [ "dep:base64ct" ];
+          "fingerprint" = [ "sha2" ];
+          "pem" = [ "alloc" "der/pem" ];
+          "sha2" = [ "dep:sha2" ];
+          "std" = [ "der/std" "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "sqlparser" = rec {
+        crateName = "sqlparser";
+        version = "0.39.0";
+        edition = "2021";
+        sha256 = "1mrbqjdqr179qnhy43d0dnrl3yipsp4qyji5rc68j4fyrg14sfvl";
+        authors = [
+          "Andy Grove <andygrove73@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "bigdecimal" = [ "dep:bigdecimal" ];
+          "default" = [ "std" ];
+          "json_example" = [ "serde_json" "serde" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "sqlparser_derive" = [ "dep:sqlparser_derive" ];
+          "visitor" = [ "sqlparser_derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "static_assertions" = rec {
+        crateName = "static_assertions";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "0gsl6xmw10gvn3zs1rv99laj5ig7ylffnh71f9l34js4nr4r7sx2";
+        authors = [
+          "Nikolai Vazquez"
+        ];
+        features = { };
+      };
+      "streaming-decompression" = rec {
+        crateName = "streaming-decompression";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "1wscqj3s30qknda778wf7z99mknk65p0h9hhs658l4pvkfqw6v5z";
+        dependencies = [
+          {
+            name = "fallible-streaming-iterator";
+            packageId = "fallible-streaming-iterator";
+          }
+        ];
+
+      };
+      "streaming-iterator" = rec {
+        crateName = "streaming-iterator";
+        version = "0.1.9";
+        edition = "2021";
+        sha256 = "0845zdv8qb7zwqzglpqc0830i43xh3fb6vqms155wz85qfvk28ib";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+      };
+      "strength_reduce" = rec {
+        crateName = "strength_reduce";
+        version = "0.2.4";
+        edition = "2015";
+        sha256 = "10jdq9dijjdkb20wg1dmwg447rnj37jbq0mwvbadvqi2gys5x2gy";
+        authors = [
+          "Elliott Mahler <join.together@gmail.com>"
+        ];
+
+      };
+      "strum_macros" = rec {
+        crateName = "strum_macros";
+        version = "0.25.3";
+        edition = "2018";
+        sha256 = "184y62g474zqb2f7n16x3ghvlyjbh50viw32p9w9l5lwmjlizp13";
+        procMacro = true;
+        authors = [
+          "Peter Glotfelty <peter.glotfelty@microsoft.com>"
+        ];
+        dependencies = [
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "parsing" "extra-traits" ];
+          }
+        ];
+
+      };
+      "subtle" = rec {
+        crateName = "subtle";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "1g2yjs7gffgmdvkkq0wrrh0pxds3q0dv6dhkw9cdpbib656xdkc1";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        features = {
+          "default" = [ "std" "i128" ];
+        };
+      };
+      "syn 1.0.109" = rec {
+        crateName = "syn";
+        version = "1.0.109";
+        edition = "2018";
+        sha256 = "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit-mut" ];
+      };
+      "syn 2.0.39" = rec {
+        crateName = "syn";
+        version = "2.0.39";
+        edition = "2021";
+        sha256 = "0ymyhxnk1yi4pzf72qk3lrdm9lgjwcrcwci0hhz5vx7wya88prr3";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit" "visit-mut" ];
+      };
+      "sysinfo" = rec {
+        crateName = "sysinfo";
+        version = "0.30.5";
+        edition = "2018";
+        sha256 = "1clba87ndskvxddrmwysnjccm6vyr75j24p6ck48jqwgii1z7d0z";
+        authors = [
+          "Guillaume Gomez <guillaume1.gomez@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(("unknown" == target."os" or null) || ("wasm32" == target."arch" or null)));
+          }
+          {
+            name = "ntapi";
+            packageId = "ntapi";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            target = { target, features }: ((target."windows" or false) || ("linux" == target."os" or null) || ("android" == target."os" or null));
+          }
+          {
+            name = "windows";
+            packageId = "windows";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Wdk_System_SystemInformation" "Wdk_System_SystemServices" "Wdk_System_Threading" "Win32_Foundation" "Win32_NetworkManagement_IpHelper" "Win32_NetworkManagement_NetManagement" "Win32_NetworkManagement_Ndis" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication_Identity" "Win32_Security_Authorization" "Win32_Storage_FileSystem" "Win32_System_Com" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_Ioctl" "Win32_System_LibraryLoader" "Win32_System_Kernel" "Win32_System_Memory" "Win32_System_Ole" "Win32_System_Performance" "Win32_System_Power" "Win32_System_ProcessStatus" "Win32_System_Registry" "Win32_System_RemoteDesktop" "Win32_System_Rpc" "Win32_System_SystemInformation" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_Variant" "Win32_System_WindowsProgramming" "Win32_System_Wmi" "Win32_UI_Shell" ];
+          }
+        ];
+        features = {
+          "apple-app-store" = [ "apple-sandbox" ];
+          "debug" = [ "libc/extra_traits" ];
+          "default" = [ "multithread" ];
+          "multithread" = [ "rayon" ];
+          "rayon" = [ "dep:rayon" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "target-features" = rec {
+        crateName = "target-features";
+        version = "0.1.5";
+        edition = "2021";
+        sha256 = "1gb974chm9aj8ifkyibylxkyb5an4bf5y8dxb18pqmck698gmdfg";
+        authors = [
+          "Caleb Zulawski <caleb.zulawski@gmail.com>"
+        ];
+
+      };
+      "tempfile" = rec {
+        crateName = "tempfile";
+        version = "3.8.1";
+        edition = "2018";
+        sha256 = "1r88v07zdafzf46y63vs39rmzwl4vqd4g2c5qarz9mqa8nnavwby";
+        authors = [
+          "Steven Allen <steven@stebalien.com>"
+          "The Rust Project Developers"
+          "Ashley Mannix <ashleymannix@live.com.au>"
+          "Jason White <me@jasonwhite.io>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "fastrand";
+            packageId = "fastrand";
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            target = { target, features }: ((target."unix" or false) || ("wasi" == target."os" or null));
+            features = [ "fs" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Storage_FileSystem" "Win32_Foundation" ];
+          }
+        ];
+        features = { };
+      };
+      "tempfile-fast" = rec {
+        crateName = "tempfile-fast";
+        version = "0.3.4";
+        edition = "2018";
+        sha256 = "1xksx1l1019k9q0az9mhqsgb14w0vm88yax30iq6178s3d9yhjx7";
+        authors = [
+          "Chris West (Faux) <git@goeswhere.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+          }
+          {
+            name = "tempfile";
+            packageId = "tempfile";
+          }
+        ];
+
+      };
+      "thiserror" = rec {
+        crateName = "thiserror";
+        version = "1.0.50";
+        edition = "2021";
+        sha256 = "1ll2sfbrxks8jja161zh1pgm3yssr7aawdmaa2xmcwcsbh7j39zr";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "thiserror-impl";
+            packageId = "thiserror-impl";
+          }
+        ];
+
+      };
+      "thiserror-impl" = rec {
+        crateName = "thiserror-impl";
+        version = "1.0.50";
+        edition = "2021";
+        sha256 = "1f0lmam4765sfnwr4b1n00y14vxh10g0311mkk0adr80pi02wsr6";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+          }
+        ];
+
+      };
+      "tokio" = rec {
+        crateName = "tokio";
+        version = "1.33.0";
+        edition = "2021";
+        sha256 = "0lynj8nfqziviw72qns9mjlhmnm66bsc5bivy5g5x6gp7q720f2g";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            target = { target, features }: (target."tokio_taskdump" or false);
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+            features = [ "all" ];
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Security_Authorization" ];
+          }
+        ];
+        features = {
+          "bytes" = [ "dep:bytes" ];
+          "full" = [ "fs" "io-util" "io-std" "macros" "net" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "sync" "time" ];
+          "io-util" = [ "bytes" ];
+          "libc" = [ "dep:libc" ];
+          "macros" = [ "tokio-macros" ];
+          "mio" = [ "dep:mio" ];
+          "net" = [ "libc" "mio/os-poll" "mio/os-ext" "mio/net" "socket2" "windows-sys/Win32_Foundation" "windows-sys/Win32_Security" "windows-sys/Win32_Storage_FileSystem" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_System_SystemServices" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "process" = [ "bytes" "libc" "mio/os-poll" "mio/os-ext" "mio/net" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Threading" "windows-sys/Win32_System_WindowsProgramming" ];
+          "rt-multi-thread" = [ "num_cpus" "rt" ];
+          "signal" = [ "libc" "mio/os-poll" "mio/net" "mio/os-ext" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Console" ];
+          "signal-hook-registry" = [ "dep:signal-hook-registry" ];
+          "socket2" = [ "dep:socket2" ];
+          "test-util" = [ "rt" "sync" "time" ];
+          "tokio-macros" = [ "dep:tokio-macros" ];
+          "tracing" = [ "dep:tracing" ];
+          "windows-sys" = [ "dep:windows-sys" ];
+        };
+        resolvedDefaultFeatures = [ "bytes" "default" "io-util" "libc" "mio" "net" "num_cpus" "rt" "rt-multi-thread" "socket2" "sync" "time" "windows-sys" ];
+      };
+      "tokio-util" = rec {
+        crateName = "tokio-util";
+        version = "0.7.10";
+        edition = "2021";
+        sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "__docs_rs" = [ "futures-util" ];
+          "codec" = [ "tracing" ];
+          "compat" = [ "futures-io" ];
+          "full" = [ "codec" "compat" "io-util" "time" "net" "rt" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hashbrown" = [ "dep:hashbrown" ];
+          "io-util" = [ "io" "tokio/rt" "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "rt" = [ "tokio/rt" "tokio/sync" "futures-util" "hashbrown" ];
+          "slab" = [ "dep:slab" ];
+          "time" = [ "tokio/time" "slab" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "default" "io" "io-util" ];
+      };
+      "typenum" = rec {
+        crateName = "typenum";
+        version = "1.17.0";
+        edition = "2018";
+        sha256 = "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2";
+        build = "build/main.rs";
+        authors = [
+          "Paho Lurie-Gregg <paho@paholg.com>"
+          "Andre Bogus <bogusandre@gmail.com>"
+        ];
+        features = {
+          "scale-info" = [ "dep:scale-info" ];
+          "scale_info" = [ "scale-info/derive" ];
+        };
+      };
+      "unicode-ident" = rec {
+        crateName = "unicode-ident";
+        version = "1.0.12";
+        edition = "2018";
+        sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "version_check" = rec {
+        crateName = "version_check";
+        version = "0.9.4";
+        edition = "2015";
+        sha256 = "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9";
+        authors = [
+          "Sergio Benitez <sb@sergio.bz>"
+        ];
+
+      };
+      "wasi" = rec {
+        crateName = "wasi";
+        version = "0.11.0+wasi-snapshot-preview1";
+        edition = "2018";
+        sha256 = "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw";
+        authors = [
+          "The Cranelift Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "rustc-std-workspace-alloc" ];
+          "rustc-std-workspace-alloc" = [ "dep:rustc-std-workspace-alloc" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "wasm-bindgen" = rec {
+        crateName = "wasm-bindgen";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "1khgsh4z9bga35mjhg41dl7523i69ffc5m8ckhqaw6ssyabc5bkx";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "wasm-bindgen-macro";
+            packageId = "wasm-bindgen-macro";
+          }
+        ];
+        features = {
+          "default" = [ "spans" "std" ];
+          "enable-interning" = [ "std" ];
+          "gg-alloc" = [ "wasm-bindgen-test/gg-alloc" ];
+          "serde" = [ "dep:serde" ];
+          "serde-serialize" = [ "serde" "serde_json" "std" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "spans" = [ "wasm-bindgen-macro/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro/strict-macro" ];
+          "xxx_debug_only_print_generated_code" = [ "wasm-bindgen-macro/xxx_debug_only_print_generated_code" ];
+        };
+        resolvedDefaultFeatures = [ "default" "spans" "std" ];
+      };
+      "wasm-bindgen-backend" = rec {
+        crateName = "wasm-bindgen-backend";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "05zj8yl243rvs87rhicq2l1d6443lnm6k90khf744khf9ikg95z3";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "bumpalo";
+            packageId = "bumpalo";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "full" ];
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro" = rec {
+        crateName = "wasm-bindgen-macro";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "1chn3wgw9awmvs0fpmazbqyc5rwfgy3pj7lzwczmzb887dxh2qar";
+        procMacro = true;
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "wasm-bindgen-macro-support";
+            packageId = "wasm-bindgen-macro-support";
+          }
+        ];
+        features = {
+          "spans" = [ "wasm-bindgen-macro-support/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro-support/strict-macro" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro-support" = rec {
+        crateName = "wasm-bindgen-macro-support";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "01rrzg3y1apqygsjz1jg0n7p831nm4kdyxmxyl85x7v6mf6kndf5";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            features = [ "visit" "full" ];
+          }
+          {
+            name = "wasm-bindgen-backend";
+            packageId = "wasm-bindgen-backend";
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+          "spans" = [ "wasm-bindgen-backend/spans" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-shared" = rec {
+        crateName = "wasm-bindgen-shared";
+        version = "0.2.88";
+        edition = "2018";
+        links = "wasm_bindgen";
+        sha256 = "02vmw2rzsla1qm0zgfng4kqz52xn8k54v8ads4g1macv09fnq10d";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+
+      };
+      "winapi" = rec {
+        crateName = "winapi";
+        version = "0.3.9";
+        edition = "2015";
+        sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-i686-pc-windows-gnu";
+            packageId = "winapi-i686-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+          }
+          {
+            name = "winapi-x86_64-pc-windows-gnu";
+            packageId = "winapi-x86_64-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+          }
+        ];
+        features = {
+          "debug" = [ "impl-debug" ];
+        };
+        resolvedDefaultFeatures = [ "cfg" "evntrace" "in6addr" "inaddr" "minwinbase" "ntsecapi" "windef" "winioctl" ];
+      };
+      "winapi-i686-pc-windows-gnu" = rec {
+        crateName = "winapi-i686-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi-x86_64-pc-windows-gnu" = rec {
+        crateName = "winapi-x86_64-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "windows" = rec {
+        crateName = "windows";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1gnh210qjlprpd1szaq04rjm1zqgdm9j7l9absg0kawi2rwm72p4";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-core";
+            packageId = "windows-core 0.52.0";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+          }
+        ];
+        features = {
+          "AI_MachineLearning" = [ "AI" ];
+          "ApplicationModel_Activation" = [ "ApplicationModel" ];
+          "ApplicationModel_AppExtensions" = [ "ApplicationModel" ];
+          "ApplicationModel_AppService" = [ "ApplicationModel" ];
+          "ApplicationModel_Appointments" = [ "ApplicationModel" ];
+          "ApplicationModel_Appointments_AppointmentsProvider" = [ "ApplicationModel_Appointments" ];
+          "ApplicationModel_Appointments_DataProvider" = [ "ApplicationModel_Appointments" ];
+          "ApplicationModel_Background" = [ "ApplicationModel" ];
+          "ApplicationModel_Calls" = [ "ApplicationModel" ];
+          "ApplicationModel_Calls_Background" = [ "ApplicationModel_Calls" ];
+          "ApplicationModel_Calls_Provider" = [ "ApplicationModel_Calls" ];
+          "ApplicationModel_Chat" = [ "ApplicationModel" ];
+          "ApplicationModel_CommunicationBlocking" = [ "ApplicationModel" ];
+          "ApplicationModel_Contacts" = [ "ApplicationModel" ];
+          "ApplicationModel_Contacts_DataProvider" = [ "ApplicationModel_Contacts" ];
+          "ApplicationModel_Contacts_Provider" = [ "ApplicationModel_Contacts" ];
+          "ApplicationModel_ConversationalAgent" = [ "ApplicationModel" ];
+          "ApplicationModel_Core" = [ "ApplicationModel" ];
+          "ApplicationModel_DataTransfer" = [ "ApplicationModel" ];
+          "ApplicationModel_DataTransfer_DragDrop" = [ "ApplicationModel_DataTransfer" ];
+          "ApplicationModel_DataTransfer_DragDrop_Core" = [ "ApplicationModel_DataTransfer_DragDrop" ];
+          "ApplicationModel_DataTransfer_ShareTarget" = [ "ApplicationModel_DataTransfer" ];
+          "ApplicationModel_Email" = [ "ApplicationModel" ];
+          "ApplicationModel_Email_DataProvider" = [ "ApplicationModel_Email" ];
+          "ApplicationModel_ExtendedExecution" = [ "ApplicationModel" ];
+          "ApplicationModel_ExtendedExecution_Foreground" = [ "ApplicationModel_ExtendedExecution" ];
+          "ApplicationModel_Holographic" = [ "ApplicationModel" ];
+          "ApplicationModel_LockScreen" = [ "ApplicationModel" ];
+          "ApplicationModel_Payments" = [ "ApplicationModel" ];
+          "ApplicationModel_Payments_Provider" = [ "ApplicationModel_Payments" ];
+          "ApplicationModel_Preview" = [ "ApplicationModel" ];
+          "ApplicationModel_Preview_Holographic" = [ "ApplicationModel_Preview" ];
+          "ApplicationModel_Preview_InkWorkspace" = [ "ApplicationModel_Preview" ];
+          "ApplicationModel_Preview_Notes" = [ "ApplicationModel_Preview" ];
+          "ApplicationModel_Resources" = [ "ApplicationModel" ];
+          "ApplicationModel_Resources_Core" = [ "ApplicationModel_Resources" ];
+          "ApplicationModel_Resources_Management" = [ "ApplicationModel_Resources" ];
+          "ApplicationModel_Search" = [ "ApplicationModel" ];
+          "ApplicationModel_Search_Core" = [ "ApplicationModel_Search" ];
+          "ApplicationModel_Store" = [ "ApplicationModel" ];
+          "ApplicationModel_Store_LicenseManagement" = [ "ApplicationModel_Store" ];
+          "ApplicationModel_Store_Preview" = [ "ApplicationModel_Store" ];
+          "ApplicationModel_Store_Preview_InstallControl" = [ "ApplicationModel_Store_Preview" ];
+          "ApplicationModel_UserActivities" = [ "ApplicationModel" ];
+          "ApplicationModel_UserActivities_Core" = [ "ApplicationModel_UserActivities" ];
+          "ApplicationModel_UserDataAccounts" = [ "ApplicationModel" ];
+          "ApplicationModel_UserDataAccounts_Provider" = [ "ApplicationModel_UserDataAccounts" ];
+          "ApplicationModel_UserDataAccounts_SystemAccess" = [ "ApplicationModel_UserDataAccounts" ];
+          "ApplicationModel_UserDataTasks" = [ "ApplicationModel" ];
+          "ApplicationModel_UserDataTasks_DataProvider" = [ "ApplicationModel_UserDataTasks" ];
+          "ApplicationModel_VoiceCommands" = [ "ApplicationModel" ];
+          "ApplicationModel_Wallet" = [ "ApplicationModel" ];
+          "ApplicationModel_Wallet_System" = [ "ApplicationModel_Wallet" ];
+          "Data_Html" = [ "Data" ];
+          "Data_Json" = [ "Data" ];
+          "Data_Pdf" = [ "Data" ];
+          "Data_Text" = [ "Data" ];
+          "Data_Xml" = [ "Data" ];
+          "Data_Xml_Dom" = [ "Data_Xml" ];
+          "Data_Xml_Xsl" = [ "Data_Xml" ];
+          "Devices_Adc" = [ "Devices" ];
+          "Devices_Adc_Provider" = [ "Devices_Adc" ];
+          "Devices_Background" = [ "Devices" ];
+          "Devices_Bluetooth" = [ "Devices" ];
+          "Devices_Bluetooth_Advertisement" = [ "Devices_Bluetooth" ];
+          "Devices_Bluetooth_Background" = [ "Devices_Bluetooth" ];
+          "Devices_Bluetooth_GenericAttributeProfile" = [ "Devices_Bluetooth" ];
+          "Devices_Bluetooth_Rfcomm" = [ "Devices_Bluetooth" ];
+          "Devices_Custom" = [ "Devices" ];
+          "Devices_Display" = [ "Devices" ];
+          "Devices_Display_Core" = [ "Devices_Display" ];
+          "Devices_Enumeration" = [ "Devices" ];
+          "Devices_Enumeration_Pnp" = [ "Devices_Enumeration" ];
+          "Devices_Geolocation" = [ "Devices" ];
+          "Devices_Geolocation_Geofencing" = [ "Devices_Geolocation" ];
+          "Devices_Geolocation_Provider" = [ "Devices_Geolocation" ];
+          "Devices_Gpio" = [ "Devices" ];
+          "Devices_Gpio_Provider" = [ "Devices_Gpio" ];
+          "Devices_Haptics" = [ "Devices" ];
+          "Devices_HumanInterfaceDevice" = [ "Devices" ];
+          "Devices_I2c" = [ "Devices" ];
+          "Devices_I2c_Provider" = [ "Devices_I2c" ];
+          "Devices_Input" = [ "Devices" ];
+          "Devices_Input_Preview" = [ "Devices_Input" ];
+          "Devices_Lights" = [ "Devices" ];
+          "Devices_Lights_Effects" = [ "Devices_Lights" ];
+          "Devices_Midi" = [ "Devices" ];
+          "Devices_PointOfService" = [ "Devices" ];
+          "Devices_PointOfService_Provider" = [ "Devices_PointOfService" ];
+          "Devices_Portable" = [ "Devices" ];
+          "Devices_Power" = [ "Devices" ];
+          "Devices_Printers" = [ "Devices" ];
+          "Devices_Printers_Extensions" = [ "Devices_Printers" ];
+          "Devices_Pwm" = [ "Devices" ];
+          "Devices_Pwm_Provider" = [ "Devices_Pwm" ];
+          "Devices_Radios" = [ "Devices" ];
+          "Devices_Scanners" = [ "Devices" ];
+          "Devices_Sensors" = [ "Devices" ];
+          "Devices_Sensors_Custom" = [ "Devices_Sensors" ];
+          "Devices_SerialCommunication" = [ "Devices" ];
+          "Devices_SmartCards" = [ "Devices" ];
+          "Devices_Sms" = [ "Devices" ];
+          "Devices_Spi" = [ "Devices" ];
+          "Devices_Spi_Provider" = [ "Devices_Spi" ];
+          "Devices_Usb" = [ "Devices" ];
+          "Devices_WiFi" = [ "Devices" ];
+          "Devices_WiFiDirect" = [ "Devices" ];
+          "Devices_WiFiDirect_Services" = [ "Devices_WiFiDirect" ];
+          "Embedded_DeviceLockdown" = [ "Embedded" ];
+          "Foundation_Collections" = [ "Foundation" ];
+          "Foundation_Diagnostics" = [ "Foundation" ];
+          "Foundation_Metadata" = [ "Foundation" ];
+          "Foundation_Numerics" = [ "Foundation" ];
+          "Gaming_Input" = [ "Gaming" ];
+          "Gaming_Input_Custom" = [ "Gaming_Input" ];
+          "Gaming_Input_ForceFeedback" = [ "Gaming_Input" ];
+          "Gaming_Input_Preview" = [ "Gaming_Input" ];
+          "Gaming_Preview" = [ "Gaming" ];
+          "Gaming_Preview_GamesEnumeration" = [ "Gaming_Preview" ];
+          "Gaming_UI" = [ "Gaming" ];
+          "Gaming_XboxLive" = [ "Gaming" ];
+          "Gaming_XboxLive_Storage" = [ "Gaming_XboxLive" ];
+          "Globalization_Collation" = [ "Globalization" ];
+          "Globalization_DateTimeFormatting" = [ "Globalization" ];
+          "Globalization_Fonts" = [ "Globalization" ];
+          "Globalization_NumberFormatting" = [ "Globalization" ];
+          "Globalization_PhoneNumberFormatting" = [ "Globalization" ];
+          "Graphics_Capture" = [ "Graphics" ];
+          "Graphics_DirectX" = [ "Graphics" ];
+          "Graphics_DirectX_Direct3D11" = [ "Graphics_DirectX" ];
+          "Graphics_Display" = [ "Graphics" ];
+          "Graphics_Display_Core" = [ "Graphics_Display" ];
+          "Graphics_Effects" = [ "Graphics" ];
+          "Graphics_Holographic" = [ "Graphics" ];
+          "Graphics_Imaging" = [ "Graphics" ];
+          "Graphics_Printing" = [ "Graphics" ];
+          "Graphics_Printing3D" = [ "Graphics" ];
+          "Graphics_Printing_OptionDetails" = [ "Graphics_Printing" ];
+          "Graphics_Printing_PrintSupport" = [ "Graphics_Printing" ];
+          "Graphics_Printing_PrintTicket" = [ "Graphics_Printing" ];
+          "Graphics_Printing_Workflow" = [ "Graphics_Printing" ];
+          "Management_Core" = [ "Management" ];
+          "Management_Deployment" = [ "Management" ];
+          "Management_Deployment_Preview" = [ "Management_Deployment" ];
+          "Management_Policies" = [ "Management" ];
+          "Management_Update" = [ "Management" ];
+          "Management_Workplace" = [ "Management" ];
+          "Media_AppBroadcasting" = [ "Media" ];
+          "Media_AppRecording" = [ "Media" ];
+          "Media_Audio" = [ "Media" ];
+          "Media_Capture" = [ "Media" ];
+          "Media_Capture_Core" = [ "Media_Capture" ];
+          "Media_Capture_Frames" = [ "Media_Capture" ];
+          "Media_Casting" = [ "Media" ];
+          "Media_ClosedCaptioning" = [ "Media" ];
+          "Media_ContentRestrictions" = [ "Media" ];
+          "Media_Control" = [ "Media" ];
+          "Media_Core" = [ "Media" ];
+          "Media_Core_Preview" = [ "Media_Core" ];
+          "Media_Devices" = [ "Media" ];
+          "Media_Devices_Core" = [ "Media_Devices" ];
+          "Media_DialProtocol" = [ "Media" ];
+          "Media_Editing" = [ "Media" ];
+          "Media_Effects" = [ "Media" ];
+          "Media_FaceAnalysis" = [ "Media" ];
+          "Media_Import" = [ "Media" ];
+          "Media_MediaProperties" = [ "Media" ];
+          "Media_Miracast" = [ "Media" ];
+          "Media_Ocr" = [ "Media" ];
+          "Media_PlayTo" = [ "Media" ];
+          "Media_Playback" = [ "Media" ];
+          "Media_Playlists" = [ "Media" ];
+          "Media_Protection" = [ "Media" ];
+          "Media_Protection_PlayReady" = [ "Media_Protection" ];
+          "Media_Render" = [ "Media" ];
+          "Media_SpeechRecognition" = [ "Media" ];
+          "Media_SpeechSynthesis" = [ "Media" ];
+          "Media_Streaming" = [ "Media" ];
+          "Media_Streaming_Adaptive" = [ "Media_Streaming" ];
+          "Media_Transcoding" = [ "Media" ];
+          "Networking_BackgroundTransfer" = [ "Networking" ];
+          "Networking_Connectivity" = [ "Networking" ];
+          "Networking_NetworkOperators" = [ "Networking" ];
+          "Networking_Proximity" = [ "Networking" ];
+          "Networking_PushNotifications" = [ "Networking" ];
+          "Networking_ServiceDiscovery" = [ "Networking" ];
+          "Networking_ServiceDiscovery_Dnssd" = [ "Networking_ServiceDiscovery" ];
+          "Networking_Sockets" = [ "Networking" ];
+          "Networking_Vpn" = [ "Networking" ];
+          "Networking_XboxLive" = [ "Networking" ];
+          "Perception_Automation" = [ "Perception" ];
+          "Perception_Automation_Core" = [ "Perception_Automation" ];
+          "Perception_People" = [ "Perception" ];
+          "Perception_Spatial" = [ "Perception" ];
+          "Perception_Spatial_Preview" = [ "Perception_Spatial" ];
+          "Perception_Spatial_Surfaces" = [ "Perception_Spatial" ];
+          "Phone_ApplicationModel" = [ "Phone" ];
+          "Phone_Devices" = [ "Phone" ];
+          "Phone_Devices_Notification" = [ "Phone_Devices" ];
+          "Phone_Devices_Power" = [ "Phone_Devices" ];
+          "Phone_Management" = [ "Phone" ];
+          "Phone_Management_Deployment" = [ "Phone_Management" ];
+          "Phone_Media" = [ "Phone" ];
+          "Phone_Media_Devices" = [ "Phone_Media" ];
+          "Phone_Notification" = [ "Phone" ];
+          "Phone_Notification_Management" = [ "Phone_Notification" ];
+          "Phone_PersonalInformation" = [ "Phone" ];
+          "Phone_PersonalInformation_Provisioning" = [ "Phone_PersonalInformation" ];
+          "Phone_Speech" = [ "Phone" ];
+          "Phone_Speech_Recognition" = [ "Phone_Speech" ];
+          "Phone_StartScreen" = [ "Phone" ];
+          "Phone_System" = [ "Phone" ];
+          "Phone_System_Power" = [ "Phone_System" ];
+          "Phone_System_Profile" = [ "Phone_System" ];
+          "Phone_System_UserProfile" = [ "Phone_System" ];
+          "Phone_System_UserProfile_GameServices" = [ "Phone_System_UserProfile" ];
+          "Phone_System_UserProfile_GameServices_Core" = [ "Phone_System_UserProfile_GameServices" ];
+          "Phone_UI" = [ "Phone" ];
+          "Phone_UI_Input" = [ "Phone_UI" ];
+          "Security_Authentication" = [ "Security" ];
+          "Security_Authentication_Identity" = [ "Security_Authentication" ];
+          "Security_Authentication_Identity_Core" = [ "Security_Authentication_Identity" ];
+          "Security_Authentication_OnlineId" = [ "Security_Authentication" ];
+          "Security_Authentication_Web" = [ "Security_Authentication" ];
+          "Security_Authentication_Web_Core" = [ "Security_Authentication_Web" ];
+          "Security_Authentication_Web_Provider" = [ "Security_Authentication_Web" ];
+          "Security_Authorization" = [ "Security" ];
+          "Security_Authorization_AppCapabilityAccess" = [ "Security_Authorization" ];
+          "Security_Credentials" = [ "Security" ];
+          "Security_Credentials_UI" = [ "Security_Credentials" ];
+          "Security_Cryptography" = [ "Security" ];
+          "Security_Cryptography_Certificates" = [ "Security_Cryptography" ];
+          "Security_Cryptography_Core" = [ "Security_Cryptography" ];
+          "Security_Cryptography_DataProtection" = [ "Security_Cryptography" ];
+          "Security_DataProtection" = [ "Security" ];
+          "Security_EnterpriseData" = [ "Security" ];
+          "Security_ExchangeActiveSyncProvisioning" = [ "Security" ];
+          "Security_Isolation" = [ "Security" ];
+          "Services_Maps" = [ "Services" ];
+          "Services_Maps_Guidance" = [ "Services_Maps" ];
+          "Services_Maps_LocalSearch" = [ "Services_Maps" ];
+          "Services_Maps_OfflineMaps" = [ "Services_Maps" ];
+          "Services_Store" = [ "Services" ];
+          "Services_TargetedContent" = [ "Services" ];
+          "Storage_AccessCache" = [ "Storage" ];
+          "Storage_BulkAccess" = [ "Storage" ];
+          "Storage_Compression" = [ "Storage" ];
+          "Storage_FileProperties" = [ "Storage" ];
+          "Storage_Pickers" = [ "Storage" ];
+          "Storage_Pickers_Provider" = [ "Storage_Pickers" ];
+          "Storage_Provider" = [ "Storage" ];
+          "Storage_Search" = [ "Storage" ];
+          "Storage_Streams" = [ "Storage" ];
+          "System_Diagnostics" = [ "System" ];
+          "System_Diagnostics_DevicePortal" = [ "System_Diagnostics" ];
+          "System_Diagnostics_Telemetry" = [ "System_Diagnostics" ];
+          "System_Diagnostics_TraceReporting" = [ "System_Diagnostics" ];
+          "System_Display" = [ "System" ];
+          "System_Implementation" = [ "System" ];
+          "System_Implementation_FileExplorer" = [ "System_Implementation" ];
+          "System_Inventory" = [ "System" ];
+          "System_Power" = [ "System" ];
+          "System_Profile" = [ "System" ];
+          "System_Profile_SystemManufacturers" = [ "System_Profile" ];
+          "System_RemoteDesktop" = [ "System" ];
+          "System_RemoteDesktop_Input" = [ "System_RemoteDesktop" ];
+          "System_RemoteSystems" = [ "System" ];
+          "System_Threading" = [ "System" ];
+          "System_Threading_Core" = [ "System_Threading" ];
+          "System_Update" = [ "System" ];
+          "System_UserProfile" = [ "System" ];
+          "UI_Accessibility" = [ "UI" ];
+          "UI_ApplicationSettings" = [ "UI" ];
+          "UI_Composition" = [ "UI" ];
+          "UI_Composition_Core" = [ "UI_Composition" ];
+          "UI_Composition_Desktop" = [ "UI_Composition" ];
+          "UI_Composition_Diagnostics" = [ "UI_Composition" ];
+          "UI_Composition_Effects" = [ "UI_Composition" ];
+          "UI_Composition_Interactions" = [ "UI_Composition" ];
+          "UI_Composition_Scenes" = [ "UI_Composition" ];
+          "UI_Core" = [ "UI" ];
+          "UI_Core_AnimationMetrics" = [ "UI_Core" ];
+          "UI_Core_Preview" = [ "UI_Core" ];
+          "UI_Input" = [ "UI" ];
+          "UI_Input_Core" = [ "UI_Input" ];
+          "UI_Input_Inking" = [ "UI_Input" ];
+          "UI_Input_Inking_Analysis" = [ "UI_Input_Inking" ];
+          "UI_Input_Inking_Core" = [ "UI_Input_Inking" ];
+          "UI_Input_Inking_Preview" = [ "UI_Input_Inking" ];
+          "UI_Input_Preview" = [ "UI_Input" ];
+          "UI_Input_Preview_Injection" = [ "UI_Input_Preview" ];
+          "UI_Input_Spatial" = [ "UI_Input" ];
+          "UI_Notifications" = [ "UI" ];
+          "UI_Notifications_Management" = [ "UI_Notifications" ];
+          "UI_Popups" = [ "UI" ];
+          "UI_Shell" = [ "UI" ];
+          "UI_StartScreen" = [ "UI" ];
+          "UI_Text" = [ "UI" ];
+          "UI_Text_Core" = [ "UI_Text" ];
+          "UI_UIAutomation" = [ "UI" ];
+          "UI_UIAutomation_Core" = [ "UI_UIAutomation" ];
+          "UI_ViewManagement" = [ "UI" ];
+          "UI_ViewManagement_Core" = [ "UI_ViewManagement" ];
+          "UI_WebUI" = [ "UI" ];
+          "UI_WebUI_Core" = [ "UI_WebUI" ];
+          "UI_WindowManagement" = [ "UI" ];
+          "UI_WindowManagement_Preview" = [ "UI_WindowManagement" ];
+          "Wdk_Foundation" = [ "Wdk" ];
+          "Wdk_Graphics" = [ "Wdk" ];
+          "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ];
+          "Wdk_Storage" = [ "Wdk" ];
+          "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ];
+          "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ];
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_IO" = [ "Wdk_System" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Wdk_System_Registry" = [ "Wdk_System" ];
+          "Wdk_System_SystemInformation" = [ "Wdk_System" ];
+          "Wdk_System_SystemServices" = [ "Wdk_System" ];
+          "Wdk_System_Threading" = [ "Wdk_System" ];
+          "Web_AtomPub" = [ "Web" ];
+          "Web_Http" = [ "Web" ];
+          "Web_Http_Diagnostics" = [ "Web_Http" ];
+          "Web_Http_Filters" = [ "Web_Http" ];
+          "Web_Http_Headers" = [ "Web_Http" ];
+          "Web_Syndication" = [ "Web" ];
+          "Web_UI" = [ "Web" ];
+          "Web_UI_Interop" = [ "Web_UI" ];
+          "Win32_AI" = [ "Win32" ];
+          "Win32_AI_MachineLearning" = [ "Win32_AI" ];
+          "Win32_AI_MachineLearning_DirectML" = [ "Win32_AI_MachineLearning" ];
+          "Win32_AI_MachineLearning_WinML" = [ "Win32_AI_MachineLearning" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_CompositionSwapchain" = [ "Win32_Graphics" ];
+          "Win32_Graphics_DXCore" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct2D" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct2D_Common" = [ "Win32_Graphics_Direct2D" ];
+          "Win32_Graphics_Direct3D" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D10" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D11" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D11on12" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D12" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D9" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D9on12" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D_Dxc" = [ "Win32_Graphics_Direct3D" ];
+          "Win32_Graphics_Direct3D_Fxc" = [ "Win32_Graphics_Direct3D" ];
+          "Win32_Graphics_DirectComposition" = [ "Win32_Graphics" ];
+          "Win32_Graphics_DirectDraw" = [ "Win32_Graphics" ];
+          "Win32_Graphics_DirectManipulation" = [ "Win32_Graphics" ];
+          "Win32_Graphics_DirectWrite" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Dxgi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Dxgi_Common" = [ "Win32_Graphics_Dxgi" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Imaging" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Imaging_D2D" = [ "Win32_Graphics_Imaging" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectSound" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DirectShow" = [ "Win32_Media" ];
+          "Win32_Media_DirectShow_Tv" = [ "Win32_Media_DirectShow" ];
+          "Win32_Media_DirectShow_Xml" = [ "Win32_Media_DirectShow" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaFoundation" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_PictureAcquisition" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_Nvme" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_SideShow" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_TransactionServer" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_Variant" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WinRT" = [ "Win32_System" ];
+          "Win32_System_WinRT_AllJoyn" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Composition" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_CoreInputView" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Direct3D11" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Display" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Graphics" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Graphics_Capture" = [ "Win32_System_WinRT_Graphics" ];
+          "Win32_System_WinRT_Graphics_Direct2D" = [ "Win32_System_WinRT_Graphics" ];
+          "Win32_System_WinRT_Graphics_Imaging" = [ "Win32_System_WinRT_Graphics" ];
+          "Win32_System_WinRT_Holographic" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Isolation" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_ML" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Media" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Metadata" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Pdf" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Printing" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Shell" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Storage" = [ "Win32_System_WinRT" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+          "implement" = [ "windows-implement" "windows-interface" "windows-core/implement" ];
+          "windows-implement" = [ "dep:windows-implement" ];
+          "windows-interface" = [ "dep:windows-interface" ];
+        };
+        resolvedDefaultFeatures = [ "Wdk" "Wdk_System" "Wdk_System_SystemInformation" "Wdk_System_SystemServices" "Wdk_System_Threading" "Win32" "Win32_Foundation" "Win32_NetworkManagement" "Win32_NetworkManagement_IpHelper" "Win32_NetworkManagement_Ndis" "Win32_NetworkManagement_NetManagement" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication" "Win32_Security_Authentication_Identity" "Win32_Security_Authorization" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Com" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_Ioctl" "Win32_System_Kernel" "Win32_System_LibraryLoader" "Win32_System_Memory" "Win32_System_Ole" "Win32_System_Performance" "Win32_System_Power" "Win32_System_ProcessStatus" "Win32_System_Registry" "Win32_System_RemoteDesktop" "Win32_System_Rpc" "Win32_System_SystemInformation" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_Variant" "Win32_System_WindowsProgramming" "Win32_System_Wmi" "Win32_UI" "Win32_UI_Shell" "default" ];
+      };
+      "windows-core 0.51.1" = rec {
+        crateName = "windows-core";
+        version = "0.51.1";
+        edition = "2021";
+        sha256 = "0r1f57hsshsghjyc7ypp2s0i78f7b1vr93w68sdb8baxyf2czy7i";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "windows-core 0.52.0" = rec {
+        crateName = "windows-core";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1nc3qv7sy24x0nlnb32f7alzpd6f72l4p24vl65vydbyil669ark";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "windows-sys" = rec {
+        crateName = "windows-sys";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+          }
+        ];
+        features = {
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_NetworkManagement" "Win32_NetworkManagement_IpHelper" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_Pipes" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_WindowsProgramming" "Win32_UI" "Win32_UI_Shell" "default" ];
+      };
+      "windows-targets 0.48.5" = rec {
+        crateName = "windows-targets";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.48.5";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows-targets 0.52.0" = rec {
+        crateName = "windows-targets";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1kg7a27ynzw8zz3krdgy6w5gbqcji27j1sz4p7xk2j5j8082064a";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.52.0";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.52.0";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.52.0";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.52.0";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.52.0";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.52.0";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.52.0";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.48.5" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.52.0" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1shmn1kbdc0bpphcxz0vlph96bxz0h1jlmh93s9agf2dbpin8xyb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.48.5" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.52.0" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1vvmy1ypvzdvxn9yf0b8ygfl85gl2gpcyvsvqppsmlpisil07amv";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.48.5" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.52.0" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "04zkglz4p3pjsns5gbz85v4s5aw102raz4spj4b0lmm33z5kg1m2";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.48.5" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.52.0" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "16kvmbvx0vr0zbgnaz6nsks9ycvfh5xp05bjrhq65kj623iyirgz";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.48.5" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.52.0" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1zdy4qn178sil5sdm63lm7f0kkcjg6gvdwmcprd2yjmwn8ns6vrx";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.48.5" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.52.0" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "17lllq4l2k1lqgcnw1cccphxp9vs7inq99kjlm2lfl9zklg7wr8s";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.48.5" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.52.0" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "012wfq37f18c09ij5m6rniw7xxn5fcvrxbqd0wd8vgnl3hfn9yfz";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "xxhash-rust" = rec {
+        crateName = "xxhash-rust";
+        version = "0.8.7";
+        edition = "2018";
+        sha256 = "0yz037yrkn0qa0g0r6733ynd1xbw7zvx58v6qylhyi2kv9wb2a4q";
+        authors = [
+          "Douman <douman@gmx.se>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "xxh3" ];
+      };
+      "zerocopy" = rec {
+        crateName = "zerocopy";
+        version = "0.7.25";
+        edition = "2018";
+        sha256 = "0mv5w4fq1kcpw1ydcb5cvr8zdms5pqy0r60g04ayzpqfgjk6klwc";
+        authors = [
+          "Joshua Liebow-Feeser <joshlf@google.com>"
+        ];
+        dependencies = [
+          {
+            name = "zerocopy-derive";
+            packageId = "zerocopy-derive";
+            optional = true;
+          }
+          {
+            name = "zerocopy-derive";
+            packageId = "zerocopy-derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "zerocopy-derive";
+            packageId = "zerocopy-derive";
+          }
+        ];
+        features = {
+          "__internal_use_only_features_that_work_on_stable" = [ "alloc" "derive" "simd" ];
+          "byteorder" = [ "dep:byteorder" ];
+          "default" = [ "byteorder" ];
+          "derive" = [ "zerocopy-derive" ];
+          "simd-nightly" = [ "simd" ];
+          "zerocopy-derive" = [ "dep:zerocopy-derive" ];
+        };
+        resolvedDefaultFeatures = [ "simd" ];
+      };
+      "zerocopy-derive" = rec {
+        crateName = "zerocopy-derive";
+        version = "0.7.25";
+        edition = "2018";
+        sha256 = "0svxr32pp4lav1vjar127g2r09gpiajxn0yv1k66r8hrlayl1wf2";
+        procMacro = true;
+        authors = [
+          "Joshua Liebow-Feeser <joshlf@google.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+          }
+        ];
+
+      };
+      "zeroize" = rec {
+        crateName = "zeroize";
+        version = "1.7.0";
+        edition = "2021";
+        sha256 = "0bfvby7k9pdp6623p98yz2irqnamcyzpn7zh20nqmdn68b0lwnsj";
+        authors = [
+          "The RustCrypto Project Developers"
+        ];
+        features = {
+          "default" = [ "alloc" ];
+          "derive" = [ "zeroize_derive" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "zeroize_derive" = [ "dep:zeroize_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "zstd" = rec {
+        crateName = "zstd";
+        version = "0.13.0";
+        edition = "2018";
+        sha256 = "0401q54s9r35x2i7m1kwppgkj79g0pb6xz3xpby7qlkdb44k7yxz";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "zstd-safe";
+            packageId = "zstd-safe";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        features = {
+          "arrays" = [ "zstd-safe/arrays" ];
+          "bindgen" = [ "zstd-safe/bindgen" ];
+          "debug" = [ "zstd-safe/debug" ];
+          "default" = [ "legacy" "arrays" "zdict_builder" ];
+          "experimental" = [ "zstd-safe/experimental" ];
+          "fat-lto" = [ "zstd-safe/fat-lto" ];
+          "legacy" = [ "zstd-safe/legacy" ];
+          "no_asm" = [ "zstd-safe/no_asm" ];
+          "pkg-config" = [ "zstd-safe/pkg-config" ];
+          "thin" = [ "zstd-safe/thin" ];
+          "thin-lto" = [ "zstd-safe/thin-lto" ];
+          "zdict_builder" = [ "zstd-safe/zdict_builder" ];
+          "zstdmt" = [ "zstd-safe/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "default" "legacy" "zdict_builder" ];
+      };
+      "zstd-safe" = rec {
+        crateName = "zstd-safe";
+        version = "7.0.0";
+        edition = "2018";
+        sha256 = "0gpav2lcibrpmyslmjkcn3w0w64qif3jjljd2h8lr4p249s7qx23";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "zstd-sys";
+            packageId = "zstd-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "bindgen" = [ "zstd-sys/bindgen" ];
+          "debug" = [ "zstd-sys/debug" ];
+          "default" = [ "legacy" "arrays" "zdict_builder" ];
+          "experimental" = [ "zstd-sys/experimental" ];
+          "fat-lto" = [ "zstd-sys/fat-lto" ];
+          "legacy" = [ "zstd-sys/legacy" ];
+          "no_asm" = [ "zstd-sys/no_asm" ];
+          "pkg-config" = [ "zstd-sys/pkg-config" ];
+          "std" = [ "zstd-sys/std" ];
+          "thin" = [ "zstd-sys/thin" ];
+          "thin-lto" = [ "zstd-sys/thin-lto" ];
+          "zdict_builder" = [ "zstd-sys/zdict_builder" ];
+          "zstdmt" = [ "zstd-sys/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "legacy" "std" "zdict_builder" ];
+      };
+      "zstd-sys" = rec {
+        crateName = "zstd-sys";
+        version = "2.0.9+zstd.1.5.5";
+        edition = "2018";
+        links = "zstd";
+        sha256 = "0mk6a2367swdi22zg03lcackpnvgq96d7120awd4i83lm2lfy5ly";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            features = [ "parallel" ];
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+        ];
+        features = {
+          "bindgen" = [ "dep:bindgen" ];
+          "default" = [ "legacy" "zdict_builder" ];
+        };
+        resolvedDefaultFeatures = [ "legacy" "std" "zdict_builder" ];
+      };
+    };
+
+    #
+    # crate2nix/default.nix (excerpt start)
+    #
+
+    /* Target (platform) data for conditional dependencies.
+      This corresponds roughly to what buildRustCrate is setting.
+    */
+    makeDefaultTarget = platform: {
+      unix = platform.isUnix;
+      windows = platform.isWindows;
+      fuchsia = true;
+      test = false;
+
+      /* We are choosing an arbitrary rust version to grab `lib` from,
+      which is unfortunate, but `lib` has been version-agnostic the
+      whole time so this is good enough for now.
+      */
+      os = pkgs.rust.lib.toTargetOs platform;
+      arch = pkgs.rust.lib.toTargetArch platform;
+      family = pkgs.rust.lib.toTargetFamily platform;
+      vendor = pkgs.rust.lib.toTargetVendor platform;
+      env = "gnu";
+      endian =
+        if platform.parsed.cpu.significantByte.name == "littleEndian"
+        then "little" else "big";
+      pointer_width = toString platform.parsed.cpu.bits;
+      debug_assertions = false;
+    };
+
+    /* Filters common temp files and build files. */
+    # TODO(pkolloch): Substitute with gitignore filter
+    sourceFilter = name: type:
+      let
+        baseName = builtins.baseNameOf (builtins.toString name);
+      in
+        ! (
+          # Filter out git
+          baseName == ".gitignore"
+          || (type == "directory" && baseName == ".git")
+
+          # Filter out build results
+          || (
+            type == "directory" && (
+              baseName == "target"
+              || baseName == "_site"
+              || baseName == ".sass-cache"
+              || baseName == ".jekyll-metadata"
+              || baseName == "build-artifacts"
+            )
+          )
+
+          # Filter out nix-build result symlinks
+          || (
+            type == "symlink" && lib.hasPrefix "result" baseName
+          )
+
+          # Filter out IDE config
+          || (
+            type == "directory" && (
+              baseName == ".idea" || baseName == ".vscode"
+            )
+          ) || lib.hasSuffix ".iml" baseName
+
+          # Filter out nix build files
+          || baseName == "Cargo.nix"
+
+          # Filter out editor backup / swap files.
+          || lib.hasSuffix "~" baseName
+          || builtins.match "^\\.sw[a-z]$$" baseName != null
+          || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null
+          || lib.hasSuffix ".tmp" baseName
+          || lib.hasSuffix ".bak" baseName
+          || baseName == "tests.nix"
+        );
+
+    /* Returns a crate which depends on successful test execution
+      of crate given as the second argument.
+
+      testCrateFlags: list of flags to pass to the test exectuable
+      testInputs: list of packages that should be available during test execution
+    */
+    crateWithTest = { crate, testCrate, testCrateFlags, testInputs, testPreRun, testPostRun }:
+      assert builtins.typeOf testCrateFlags == "list";
+      assert builtins.typeOf testInputs == "list";
+      assert builtins.typeOf testPreRun == "string";
+      assert builtins.typeOf testPostRun == "string";
+      let
+        # override the `crate` so that it will build and execute tests instead of
+        # building the actual lib and bin targets We just have to pass `--test`
+        # to rustc and it will do the right thing.  We execute the tests and copy
+        # their log and the test executables to $out for later inspection.
+        test =
+          let
+            drv = testCrate.override
+              (
+                _: {
+                  buildTests = true;
+                  release = false;
+                }
+              );
+            # If the user hasn't set any pre/post commands, we don't want to
+            # insert empty lines. This means that any existing users of crate2nix
+            # don't get a spurious rebuild unless they set these explicitly.
+            testCommand = pkgs.lib.concatStringsSep "\n"
+              (pkgs.lib.filter (s: s != "") [
+                testPreRun
+                "$f $testCrateFlags 2>&1 | tee -a $out"
+                testPostRun
+              ]);
+          in
+          pkgs.runCommand "run-tests-${testCrate.name}"
+            {
+              inherit testCrateFlags;
+              buildInputs = testInputs;
+            } ''
+            set -e
+
+            export RUST_BACKTRACE=1
+
+            # recreate a file hierarchy as when running tests with cargo
+
+            # the source for test data
+            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
+            # instead of $NIX_BUILD_TOP/
+            # because we compiled those test binaries in the former and not the latter.
+            # So all paths will expect source tree to be there and not in the build top directly.
+            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
+            # TODO(raitobezarius): I believe there could be more edge cases if `crate.sourceRoot`
+            # do exist but it's very hard to reason about them, so let's wait until the first bug report.
+            mkdir -p source/
+            cd source/
+
+            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+
+            # build outputs
+            testRoot=target/debug
+            mkdir -p $testRoot
+
+            # executables of the crate
+            # we copy to prevent std::env::current_exe() to resolve to a store location
+            for i in ${crate}/bin/*; do
+              cp "$i" "$testRoot"
+            done
+            chmod +w -R .
+
+            # test harness executables are suffixed with a hash, like cargo does
+            # this allows to prevent name collision with the main
+            # executables of the crate
+            hash=$(basename $out)
+            for file in ${drv}/tests/*; do
+              f=$testRoot/$(basename $file)-$hash
+              cp $file $f
+              ${testCommand}
+            done
+          '';
+      in
+      pkgs.runCommand "${crate.name}-linked"
+        {
+          inherit (crate) outputs crateName;
+          passthru = (crate.passthru or { }) // {
+            inherit test;
+          };
+        }
+        (lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+          echo tested by ${test}
+        '' + ''
+          ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs}
+        '');
+
+    /* A restricted overridable version of builtRustCratesWithFeatures. */
+    buildRustCrateWithFeatures =
+      { packageId
+      , features ? rootFeatures
+      , crateOverrides ? defaultCrateOverrides
+      , buildRustCrateForPkgsFunc ? null
+      , runTests ? false
+      , testCrateFlags ? [ ]
+      , testInputs ? [ ]
+        # Any command to run immediatelly before a test is executed.
+      , testPreRun ? ""
+        # Any command run immediatelly after a test is executed.
+      , testPostRun ? ""
+      }:
+      lib.makeOverridable
+        (
+          { features
+          , crateOverrides
+          , runTests
+          , testCrateFlags
+          , testInputs
+          , testPreRun
+          , testPostRun
+          }:
+          let
+            buildRustCrateForPkgsFuncOverriden =
+              if buildRustCrateForPkgsFunc != null
+              then buildRustCrateForPkgsFunc
+              else
+                (
+                  if crateOverrides == pkgs.defaultCrateOverrides
+                  then buildRustCrateForPkgs
+                  else
+                    pkgs: (buildRustCrateForPkgs pkgs).override {
+                      defaultCrateOverrides = crateOverrides;
+                    }
+                );
+            builtRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = false;
+            };
+            builtTestRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = true;
+            };
+            drv = builtRustCrates.crates.${packageId};
+            testDrv = builtTestRustCrates.crates.${packageId};
+            derivation =
+              if runTests then
+                crateWithTest
+                  {
+                    crate = drv;
+                    testCrate = testDrv;
+                    inherit testCrateFlags testInputs testPreRun testPostRun;
+                  }
+              else drv;
+          in
+          derivation
+        )
+        { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun; };
+
+    /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc
+      for the corresponding crate.
+    */
+    builtRustCratesWithFeatures =
+      { packageId
+      , features
+      , crateConfigs ? crates
+      , buildRustCrateForPkgsFunc
+      , runTests
+      , makeTarget ? makeDefaultTarget
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isList features);
+        assert (builtins.isAttrs (makeTarget stdenv.hostPlatform));
+        assert (builtins.isBool runTests);
+        let
+          rootPackageId = packageId;
+          mergedFeatures = mergePackageFeatures
+            (
+              args // {
+                inherit rootPackageId;
+                target = makeTarget stdenv.hostPlatform // { test = runTests; };
+              }
+            );
+          # Memoize built packages so that reappearing packages are only built once.
+          builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs;
+          mkBuiltByPackageIdByPkgs = pkgs:
+            let
+              self = {
+                crates = lib.mapAttrs (packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId) crateConfigs;
+                target = makeTarget pkgs.stdenv.hostPlatform;
+                build = mkBuiltByPackageIdByPkgs pkgs.buildPackages;
+              };
+            in
+            self;
+          buildByPackageIdForPkgsImpl = self: pkgs: packageId:
+            let
+              features = mergedFeatures."${packageId}" or [ ];
+              crateConfig' = crateConfigs."${packageId}";
+              crateConfig =
+                builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ];
+              devDependencies =
+                lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig'.devDependencies or [ ]);
+              dependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self) target;
+                  buildByPackageId = depPackageId:
+                    # proc_macro crates must be compiled for the build architecture
+                    if crateConfigs.${depPackageId}.procMacro or false
+                    then self.build.crates.${depPackageId}
+                    else self.crates.${depPackageId};
+                  dependencies =
+                    (crateConfig.dependencies or [ ])
+                    ++ devDependencies;
+                };
+              buildDependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self.build) target;
+                  buildByPackageId = depPackageId:
+                    self.build.crates.${depPackageId};
+                  dependencies = crateConfig.buildDependencies or [ ];
+                };
+              dependenciesWithRenames =
+                let
+                  buildDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self) target;
+                    dependencies = crateConfig.dependencies or [ ] ++ devDependencies;
+                  };
+                  hostDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self.build) target;
+                    dependencies = crateConfig.buildDependencies or [ ];
+                  };
+                in
+                lib.filter (d: d ? "rename") (hostDeps ++ buildDeps);
+              # Crate renames have the form:
+              #
+              # {
+              #    crate_name = [
+              #       { version = "1.2.3"; rename = "crate_name01"; }
+              #    ];
+              #    # ...
+              # }
+              crateRenames =
+                let
+                  grouped =
+                    lib.groupBy
+                      (dependency: dependency.name)
+                      dependenciesWithRenames;
+                  versionAndRename = dep:
+                    let
+                      package = crateConfigs."${dep.packageId}";
+                    in
+                    { inherit (dep) rename; inherit (package) version; };
+                in
+                lib.mapAttrs (name: builtins.map versionAndRename) grouped;
+            in
+            buildRustCrateForPkgsFunc pkgs
+              (
+                crateConfig // {
+                  # https://github.com/NixOS/nixpkgs/issues/218712
+                  dontStrip = stdenv.hostPlatform.isDarwin;
+                  src = crateConfig.src or (
+                    pkgs.fetchurl rec {
+                      name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz";
+                      # https://www.pietroalbini.org/blog/downloading-crates-io/
+                      # Not rate-limited, CDN URL.
+                      url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate";
+                      sha256 =
+                        assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}");
+                        crateConfig.sha256;
+                    }
+                  );
+                  extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}";
+                  inherit features dependencies buildDependencies crateRenames release;
+                }
+              );
+        in
+        builtByPackageIdByPkgs;
+
+    /* Returns the actual derivations for the given dependencies. */
+    dependencyDerivations =
+      { buildByPackageId
+      , features
+      , dependencies
+      , target
+      }:
+        assert (builtins.isList features);
+        assert (builtins.isList dependencies);
+        assert (builtins.isAttrs target);
+        let
+          enabledDependencies = filterEnabledDependencies {
+            inherit dependencies features target;
+          };
+          depDerivation = dependency: buildByPackageId dependency.packageId;
+        in
+        map depDerivation enabledDependencies;
+
+    /* Returns a sanitized version of val with all values substituted that cannot
+      be serialized as JSON.
+    */
+    sanitizeForJson = val:
+      if builtins.isAttrs val
+      then lib.mapAttrs (n: sanitizeForJson) val
+      else if builtins.isList val
+      then builtins.map sanitizeForJson val
+      else if builtins.isFunction val
+      then "function"
+      else val;
+
+    /* Returns various tools to debug a crate. */
+    debugCrate = { packageId, target ? makeDefaultTarget stdenv.hostPlatform }:
+      assert (builtins.isString packageId);
+      let
+        debug = rec {
+          # The built tree as passed to buildRustCrate.
+          buildTree = buildRustCrateWithFeatures {
+            buildRustCrateForPkgsFunc = _: lib.id;
+            inherit packageId;
+          };
+          sanitizedBuildTree = sanitizeForJson buildTree;
+          dependencyTree = sanitizeForJson
+            (
+              buildRustCrateWithFeatures {
+                buildRustCrateForPkgsFunc = _: crate: {
+                  "01_crateName" = crate.crateName or false;
+                  "02_features" = crate.features or [ ];
+                  "03_dependencies" = crate.dependencies or [ ];
+                };
+                inherit packageId;
+              }
+            );
+          mergedPackageFeatures = mergePackageFeatures {
+            features = rootFeatures;
+            inherit packageId target;
+          };
+          diffedDefaultPackageFeatures = diffDefaultPackageFeatures {
+            inherit packageId target;
+          };
+        };
+      in
+      { internal = debug; };
+
+    /* Returns differences between cargo default features and crate2nix default
+      features.
+
+      This is useful for verifying the feature resolution in crate2nix.
+    */
+    diffDefaultPackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , target
+      }:
+        assert (builtins.isAttrs crateConfigs);
+        let
+          prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; });
+          mergedFeatures =
+            prefixValues
+              "crate2nix"
+              (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; });
+          configs = prefixValues "cargo" crateConfigs;
+          combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ];
+          onlyInCargo =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined);
+          onlyInCrate2Nix =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined);
+          differentFeatures = lib.filterAttrs
+            (
+              n: v:
+                (v ? "crate2nix")
+                && (v ? "cargo")
+                && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ])
+            )
+            combined;
+        in
+        builtins.toJSON {
+          inherit onlyInCargo onlyInCrate2Nix differentFeatures;
+        };
+
+    /* Returns an attrset mapping packageId to the list of enabled features.
+
+      If multiple paths to a dependency enable different features, the
+      corresponding feature sets are merged. Features in rust are additive.
+    */
+    mergePackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , rootPackageId ? packageId
+      , features ? rootFeatures
+      , dependencyPath ? [ crates.${packageId}.crateName ]
+      , featuresByPackageId ? { }
+      , target
+        # Adds devDependencies to the crate with rootPackageId.
+      , runTests ? false
+      , ...
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isString rootPackageId);
+        assert (builtins.isList features);
+        assert (builtins.isList dependencyPath);
+        assert (builtins.isAttrs featuresByPackageId);
+        assert (builtins.isAttrs target);
+        assert (builtins.isBool runTests);
+        let
+          crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
+          expandedFeatures = expandFeatures (crateConfig.features or { }) features;
+          enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
+          depWithResolvedFeatures = dependency:
+            let
+              inherit (dependency) packageId;
+              features = dependencyFeatures enabledFeatures dependency;
+            in
+            { inherit packageId features; };
+          resolveDependencies = cache: path: dependencies:
+            assert (builtins.isAttrs cache);
+            assert (builtins.isList dependencies);
+            let
+              enabledDependencies = filterEnabledDependencies {
+                inherit dependencies target;
+                features = enabledFeatures;
+              };
+              directDependencies = map depWithResolvedFeatures enabledDependencies;
+              foldOverCache = op: lib.foldl op cache directDependencies;
+            in
+            foldOverCache
+              (
+                cache: { packageId, features }:
+                  let
+                    cacheFeatures = cache.${packageId} or [ ];
+                    combinedFeatures = sortedUnique (cacheFeatures ++ features);
+                  in
+                  if cache ? ${packageId} && cache.${packageId} == combinedFeatures
+                  then cache
+                  else
+                    mergePackageFeatures {
+                      features = combinedFeatures;
+                      featuresByPackageId = cache;
+                      inherit crateConfigs packageId target runTests rootPackageId;
+                    }
+              );
+          cacheWithSelf =
+            let
+              cacheFeatures = featuresByPackageId.${packageId} or [ ];
+              combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
+            in
+            featuresByPackageId // {
+              "${packageId}" = combinedFeatures;
+            };
+          cacheWithDependencies =
+            resolveDependencies cacheWithSelf "dep"
+              (
+                crateConfig.dependencies or [ ]
+                ++ lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig.devDependencies or [ ])
+              );
+          cacheWithAll =
+            resolveDependencies
+              cacheWithDependencies "build"
+              (crateConfig.buildDependencies or [ ]);
+        in
+        cacheWithAll;
+
+    /* Returns the enabled dependencies given the enabled features. */
+    filterEnabledDependencies = { dependencies, features, target }:
+      assert (builtins.isList dependencies);
+      assert (builtins.isList features);
+      assert (builtins.isAttrs target);
+
+      lib.filter
+        (
+          dep:
+          let
+            targetFunc = dep.target or (features: true);
+          in
+          targetFunc { inherit features target; }
+          && (
+            !(dep.optional or false)
+            || builtins.any (doesFeatureEnableDependency dep) features
+          )
+        )
+        dependencies;
+
+    /* Returns whether the given feature should enable the given dependency. */
+    doesFeatureEnableDependency = dependency: feature:
+      let
+        name = dependency.rename or dependency.name;
+        prefix = "${name}/";
+        len = builtins.stringLength prefix;
+        startsWithPrefix = builtins.substring 0 len feature == prefix;
+      in
+      feature == name || feature == "dep:" + name || startsWithPrefix;
+
+    /* Returns the expanded features for the given inputFeatures by applying the
+      rules in featureMap.
+
+      featureMap is an attribute set which maps feature names to lists of further
+      feature names to enable in case this feature is selected.
+    */
+    expandFeatures = featureMap: inputFeatures:
+      assert (builtins.isAttrs featureMap);
+      assert (builtins.isList inputFeatures);
+      let
+        expandFeaturesNoCycle = oldSeen: inputFeatures:
+          if inputFeatures != [ ]
+          then
+            let
+              # The feature we're currently expanding.
+              feature = builtins.head inputFeatures;
+              # All the features we've seen/expanded so far, including the one
+              # we're currently processing.
+              seen = oldSeen // { ${feature} = 1; };
+              # Expand the feature but be careful to not re-introduce a feature
+              # that we've already seen: this can easily cause a cycle, see issue
+              # #209.
+              enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]);
+            in
+            [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
+          # No more features left, nothing to expand to.
+          else [ ];
+        outFeatures = expandFeaturesNoCycle { } inputFeatures;
+      in
+      sortedUnique outFeatures;
+
+    /* This function adds optional dependencies as features if they are enabled
+      indirectly by dependency features. This function mimics Cargo's behavior
+      described in a note at:
+      https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
+    */
+    enableFeatures = dependencies: features:
+      assert (builtins.isList features);
+      assert (builtins.isList dependencies);
+      let
+        additionalFeatures = lib.concatMap
+          (
+            dependency:
+              assert (builtins.isAttrs dependency);
+              let
+                enabled = builtins.any (doesFeatureEnableDependency dependency) features;
+              in
+              if (dependency.optional or false) && enabled
+              then [ (dependency.rename or dependency.name) ]
+              else [ ]
+          )
+          dependencies;
+      in
+      sortedUnique (features ++ additionalFeatures);
+
+    /*
+      Returns the actual features for the given dependency.
+
+      features: The features of the crate that refers this dependency.
+    */
+    dependencyFeatures = features: dependency:
+      assert (builtins.isList features);
+      assert (builtins.isAttrs dependency);
+      let
+        defaultOrNil =
+          if dependency.usesDefaultFeatures or true
+          then [ "default" ]
+          else [ ];
+        explicitFeatures = dependency.features or [ ];
+        additionalDependencyFeatures =
+          let
+            name = dependency.rename or dependency.name;
+            stripPrefixMatch = prefix: s:
+              if lib.hasPrefix prefix s
+              then lib.removePrefix prefix s
+              else null;
+            extractFeature = feature: lib.findFirst
+              (f: f != null)
+              null
+              (map (prefix: stripPrefixMatch prefix feature) [
+                (name + "/")
+                (name + "?/")
+              ]);
+            dependencyFeatures = lib.filter (f: f != null) (map extractFeature features);
+          in
+          dependencyFeatures;
+      in
+      defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures;
+
+    /* Sorts and removes duplicates from a list of strings. */
+    sortedUnique = features:
+      assert (builtins.isList features);
+      assert (builtins.all builtins.isString features);
+      let
+        outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features;
+        outFeaturesUnique = builtins.attrNames outFeaturesSet;
+      in
+      builtins.sort (a: b: a < b) outFeaturesUnique;
+
+    deprecationWarning = message: value:
+      if strictDeprecation
+      then builtins.throw "strictDeprecation enabled, aborting: ${message}"
+      else builtins.trace message value;
+
+    #
+    # crate2nix/default.nix (excerpt end)
+    #
+  };
+}
+
diff --git a/tvix/tools/narinfo2parquet/Cargo.toml b/tvix/tools/narinfo2parquet/Cargo.toml
new file mode 100644
index 0000000000..3efa9d1df9
--- /dev/null
+++ b/tvix/tools/narinfo2parquet/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "narinfo2parquet"
+version = "0.1.0"
+edition = "2021"
+
+# We can't join the //tvix workspace, because that locks zstd
+# at an ancient version, which is incompatible with polars
+[workspace]
+members = ["."]
+
+[dependencies]
+anyhow = { version = "1.0.75", features = ["backtrace"] }
+jemallocator = "0.5.4"
+nix-compat = { version = "0.1.0", path = "../../nix-compat" }
+tempfile-fast = "0.3.4"
+zstd = "0.13.0"
+
+[dependencies.polars]
+version = "0.36.2"
+default-features = false
+features = [
+    "parquet",
+    "polars-io",
+    "dtype-categorical"
+]
diff --git a/tvix/tools/narinfo2parquet/OWNERS b/tvix/tools/narinfo2parquet/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/tvix/tools/narinfo2parquet/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/tvix/tools/narinfo2parquet/default.nix b/tvix/tools/narinfo2parquet/default.nix
new file mode 100644
index 0000000000..5a84d7f642
--- /dev/null
+++ b/tvix/tools/narinfo2parquet/default.nix
@@ -0,0 +1,3 @@
+{ pkgs, ... }:
+
+(pkgs.callPackage ./Cargo.nix { }).rootCrate.build
diff --git a/tvix/tools/narinfo2parquet/src/main.rs b/tvix/tools/narinfo2parquet/src/main.rs
new file mode 100644
index 0000000000..3b793e02b1
--- /dev/null
+++ b/tvix/tools/narinfo2parquet/src/main.rs
@@ -0,0 +1,264 @@
+//! narinfo2parquet operates on a narinfo.zst directory produced by turbofetch.
+//! It takes the name of a segment file in `narinfo.zst` and writes a Parquet file
+//! with the same name into the `narinfo.pq` directory.
+//!
+//! Run it under GNU Parallel for parallelism:
+//! ```shell
+//! mkdir narinfo.pq && ls narinfo.zst | parallel --bar 'narinfo2parquet {}'
+//! ```
+
+use anyhow::{bail, Context, Result};
+use jemallocator::Jemalloc;
+use nix_compat::{
+    narinfo::{self, NarInfo},
+    nixbase32,
+    nixhash::{CAHash, NixHash},
+};
+use polars::{io::parquet::ParquetWriter, prelude::*};
+use std::{
+    fs::{self, File},
+    io::{self, BufRead, BufReader, Read},
+    path::Path,
+};
+use tempfile_fast::PersistableTempFile;
+
+#[global_allocator]
+static GLOBAL: Jemalloc = Jemalloc;
+
+fn main() -> Result<()> {
+    let file_name = std::env::args().nth(1).expect("file name missing");
+    let input_path = Path::new("narinfo.zst").join(&file_name);
+    let output_path = Path::new("narinfo.pq").join(&file_name);
+
+    match fs::metadata(&output_path) {
+        Err(e) if e.kind() == io::ErrorKind::NotFound => {}
+        Err(e) => bail!(e),
+        Ok(_) => bail!("output path already exists: {output_path:?}"),
+    }
+
+    let reader = File::open(input_path).and_then(zstd::Decoder::new)?;
+    let mut frame = FrameBuilder::default();
+
+    for_each(reader, |s| {
+        let entry = NarInfo::parse(&s).context("couldn't parse entry:\n{s}")?;
+        frame.push(&entry);
+        Ok(())
+    })?;
+
+    let mut frame = frame.finish();
+    let mut writer = PersistableTempFile::new_in(output_path.parent().unwrap())?;
+
+    ParquetWriter::new(&mut writer)
+        .with_compression(ParquetCompression::Gzip(None))
+        .with_statistics(true)
+        .finish(frame.align_chunks())?;
+
+    writer
+        .persist_noclobber(output_path)
+        .map_err(|e| e.error)
+        .context("couldn't commit output file")?;
+
+    Ok(())
+}
+
+fn for_each(reader: impl Read, mut f: impl FnMut(&str) -> Result<()>) -> Result<()> {
+    let mut reader = BufReader::new(reader);
+    let mut group = String::new();
+    loop {
+        let prev_len = group.len();
+
+        if prev_len > 1024 * 1024 {
+            bail!("excessively large segment");
+        }
+
+        reader.read_line(&mut group)?;
+        let (prev, line) = group.split_at(prev_len);
+
+        // EOF
+        if line.is_empty() {
+            break;
+        }
+
+        // skip empty line
+        if line == "\n" {
+            group.pop().unwrap();
+            continue;
+        }
+
+        if !prev.is_empty() && line.starts_with("StorePath:") {
+            f(prev)?;
+            group.drain(..prev_len);
+        }
+    }
+
+    if !group.is_empty() {
+        f(&group)?;
+    }
+
+    Ok(())
+}
+
+/// [FrameBuilder] builds a [DataFrame] out of [NarInfo]s.
+/// The exact format is still in flux.
+///
+/// # Example
+///
+/// ```no_run
+/// |narinfos: &[NarInfo]| -> DataFrame {
+///     let frame_builder = FrameBuilder::default();
+///     narinfos.for_each(|n| frame_builder.push(n));
+///     frame_builder.finish()
+/// }
+/// ```
+struct FrameBuilder {
+    store_path_hash_str: StringChunkedBuilder,
+    store_path_hash: BinaryChunkedBuilder,
+    store_path_name: StringChunkedBuilder,
+    nar_hash: BinaryChunkedBuilder,
+    nar_size: PrimitiveChunkedBuilder<UInt64Type>,
+    references: ListBinaryChunkedBuilder,
+    ca_algo: CategoricalChunkedBuilder<'static>,
+    ca_hash: BinaryChunkedBuilder,
+    signature: BinaryChunkedBuilder,
+    file_hash: BinaryChunkedBuilder,
+    file_size: PrimitiveChunkedBuilder<UInt64Type>,
+    compression: CategoricalChunkedBuilder<'static>,
+    quirk_references_out_of_order: BooleanChunkedBuilder,
+    quirk_nar_hash_hex: BooleanChunkedBuilder,
+}
+
+impl Default for FrameBuilder {
+    fn default() -> Self {
+        Self {
+            store_path_hash_str: StringChunkedBuilder::new("store_path_hash_str", 0, 0),
+            store_path_hash: BinaryChunkedBuilder::new("store_path_hash", 0, 0),
+            store_path_name: StringChunkedBuilder::new("store_path_name", 0, 0),
+            nar_hash: BinaryChunkedBuilder::new("nar_hash", 0, 0),
+            nar_size: PrimitiveChunkedBuilder::new("nar_size", 0),
+            references: ListBinaryChunkedBuilder::new("references", 0, 0),
+            signature: BinaryChunkedBuilder::new("signature", 0, 0),
+            ca_algo: CategoricalChunkedBuilder::new("ca_algo", 0, CategoricalOrdering::Lexical),
+            ca_hash: BinaryChunkedBuilder::new("ca_hash", 0, 0),
+            file_hash: BinaryChunkedBuilder::new("file_hash", 0, 0),
+            file_size: PrimitiveChunkedBuilder::new("file_size", 0),
+            compression: CategoricalChunkedBuilder::new(
+                "compression",
+                0,
+                CategoricalOrdering::Lexical,
+            ),
+            quirk_references_out_of_order: BooleanChunkedBuilder::new(
+                "quirk_references_out_of_order",
+                0,
+            ),
+            quirk_nar_hash_hex: BooleanChunkedBuilder::new("quirk_nar_hash_hex", 0),
+        }
+    }
+}
+
+impl FrameBuilder {
+    fn push(&mut self, entry: &NarInfo) {
+        self.store_path_hash_str
+            .append_value(nixbase32::encode(entry.store_path.digest()));
+
+        self.store_path_hash.append_value(entry.store_path.digest());
+        self.store_path_name.append_value(entry.store_path.name());
+
+        self.nar_hash.append_value(&entry.nar_hash);
+        self.nar_size.append_value(entry.nar_size);
+
+        self.references
+            .append_values_iter(entry.references.iter().map(|r| r.digest().as_slice()));
+
+        assert!(entry.signatures.len() <= 1);
+        self.signature
+            .append_option(entry.signatures.get(0).map(|sig| {
+                assert_eq!(sig.name(), "cache.nixos.org-1");
+                sig.bytes()
+            }));
+
+        if let Some(ca) = &entry.ca {
+            // decompose the CAHash into algo and hash parts
+            // TODO(edef): move this into CAHash
+            let (algo, hash) = match ca {
+                CAHash::Flat(h) => match h {
+                    NixHash::Md5(h) => ("fixed:md5", &h[..]),
+                    NixHash::Sha1(h) => ("fixed:sha1", &h[..]),
+                    NixHash::Sha256(h) => ("fixed:sha256", &h[..]),
+                    NixHash::Sha512(h) => ("fixed:sha512", &h[..]),
+                },
+                CAHash::Nar(h) => match h {
+                    NixHash::Md5(h) => ("fixed:r:md5", &h[..]),
+                    NixHash::Sha1(h) => ("fixed:r:sha1", &h[..]),
+                    NixHash::Sha256(h) => ("fixed:r:sha256", &h[..]),
+                    NixHash::Sha512(h) => ("fixed:r:sha512", &h[..]),
+                },
+                CAHash::Text(h) => ("text:sha256", &h[..]),
+            };
+
+            self.ca_algo.append_value(algo);
+            self.ca_hash.append_value(hash);
+        } else {
+            self.ca_algo.append_null();
+            self.ca_hash.append_null();
+        }
+
+        let file_hash = entry.file_hash.as_ref().unwrap();
+        let file_size = entry.file_size.unwrap();
+
+        self.file_hash.append_value(file_hash);
+        self.file_size.append_value(file_size);
+
+        let (compression, extension) = match entry.compression {
+            Some("bzip2") => ("bzip2", "bz2"),
+            Some("xz") => ("xz", "xz"),
+            Some("zstd") => ("zstd", "zst"),
+            x => panic!("unknown compression algorithm: {x:?}"),
+        };
+
+        self.compression.append_value(compression);
+
+        let mut file_name = nixbase32::encode(file_hash);
+        file_name.push_str(".nar.");
+        file_name.push_str(extension);
+
+        assert_eq!(entry.url.strip_prefix("nar/").unwrap(), file_name);
+
+        {
+            use narinfo::Flags;
+
+            self.quirk_references_out_of_order
+                .append_value(entry.flags.contains(Flags::REFERENCES_OUT_OF_ORDER));
+
+            self.quirk_nar_hash_hex
+                .append_value(entry.flags.contains(Flags::NAR_HASH_HEX));
+
+            let quirks = Flags::REFERENCES_OUT_OF_ORDER | Flags::NAR_HASH_HEX;
+            let unknown_flags = entry.flags.difference(quirks);
+
+            assert!(
+                unknown_flags.is_empty(),
+                "rejecting flags: {unknown_flags:?}"
+            );
+        }
+    }
+
+    fn finish(mut self) -> DataFrame {
+        df! {
+            "store_path_hash_str" => self.store_path_hash_str.finish().into_series(),
+            "store_path_hash" => self.store_path_hash.finish().into_series(),
+            "store_path_name" => self.store_path_name.finish().into_series(),
+            "nar_hash" => self.nar_hash.finish().into_series(),
+            "nar_size" => self.nar_size.finish().into_series(),
+            "references" => self.references.finish().into_series(),
+            "signature" => self.signature.finish().into_series(),
+            "ca_algo" => self.ca_algo.finish().into_series(),
+            "ca_hash" => self.ca_hash.finish().into_series(),
+            "file_hash" => self.file_hash.finish().into_series(),
+            "file_size" => self.file_size.finish().into_series(),
+            "compression" => self.compression.finish().into_series(),
+            "quirk_references_out_of_order" => self.quirk_references_out_of_order.finish().into_series(),
+            "quirk_nar_hash_hex" => self.quirk_nar_hash_hex.finish().into_series()
+        }
+        .unwrap()
+    }
+}
diff --git a/tvix/tools/turbofetch/Cargo.lock b/tvix/tools/turbofetch/Cargo.lock
new file mode 100644
index 0000000000..4d65fc4063
--- /dev/null
+++ b/tvix/tools/turbofetch/Cargo.lock
@@ -0,0 +1,1715 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "aws_lambda_events"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "771c11de66cef31b3f49f0b38f06d94f0af424f60381409fc21972ff9c8f835b"
+dependencies = [
+ "base64 0.21.5",
+ "bytes",
+ "http",
+ "http-body",
+ "http-serde",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
+
+[[package]]
+name = "base64"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "jobserver",
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "serde",
+ "windows-targets",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-mac"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "futures"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
+name = "h2"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hmac"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+dependencies = [
+ "crypto-mac",
+ "digest",
+]
+
+[[package]]
+name = "http"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f95b9abcae896730d42b78e09c155ed4ddf82c07b4de772c64aee5b2d8b7c150"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-serde"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f560b665ad9f1572cfcaf034f7fb84338a7ce945216d64a90fd81f046a3caee"
+dependencies = [
+ "http",
+ "serde",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "0.14.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2 0.4.10",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c"
+dependencies = [
+ "http",
+ "hyper",
+ "log",
+ "rustls",
+ "rustls-native-certs",
+ "tokio",
+ "tokio-rustls",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "jobserver"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lambda_runtime"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "deca8f65d7ce9a8bfddebb49d7d91b22e788a59ca0c5190f26794ab80ed7a702"
+dependencies = [
+ "async-stream",
+ "base64 0.20.0",
+ "bytes",
+ "futures",
+ "http",
+ "http-body",
+ "http-serde",
+ "hyper",
+ "lambda_runtime_api_client",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "tokio",
+ "tokio-stream",
+ "tower",
+ "tracing",
+]
+
+[[package]]
+name = "lambda_runtime_api_client"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "690c5ae01f3acac8c9c3348b556fc443054e9b7f1deaf53e9ebab716282bf0ed"
+dependencies = [
+ "http",
+ "hyper",
+ "tokio",
+ "tower-service",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+
+[[package]]
+name = "libredox"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
+dependencies = [
+ "bitflags 2.4.1",
+ "libc",
+ "redox_syscall",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "mach2"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "magic-buffer"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "003aed0f6b361330d1c549e8ae765758cb9d46f7bace57112e8c25847966ff2e"
+dependencies = [
+ "libc",
+ "mach2",
+ "thiserror",
+ "windows-sys",
+]
+
+[[package]]
+name = "md-5"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
+dependencies = [
+ "block-buffer",
+ "digest",
+ "opaque-debug",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "pin-project"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin 0.5.2",
+ "untrusted 0.7.1",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
+dependencies = [
+ "cc",
+ "getrandom",
+ "libc",
+ "spin 0.9.8",
+ "untrusted 0.9.0",
+ "windows-sys",
+]
+
+[[package]]
+name = "rusoto_core"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1db30db44ea73551326269adcf7a2169428a054f14faf9e1768f2163494f2fa2"
+dependencies = [
+ "async-trait",
+ "base64 0.13.1",
+ "bytes",
+ "crc32fast",
+ "futures",
+ "http",
+ "hyper",
+ "hyper-rustls",
+ "lazy_static",
+ "log",
+ "rusoto_credential",
+ "rusoto_signature",
+ "rustc_version",
+ "serde",
+ "serde_json",
+ "tokio",
+ "xml-rs",
+]
+
+[[package]]
+name = "rusoto_credential"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee0a6c13db5aad6047b6a44ef023dbbc21a056b6dab5be3b79ce4283d5c02d05"
+dependencies = [
+ "async-trait",
+ "chrono",
+ "dirs-next",
+ "futures",
+ "hyper",
+ "serde",
+ "serde_json",
+ "shlex",
+ "tokio",
+ "zeroize",
+]
+
+[[package]]
+name = "rusoto_s3"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7aae4677183411f6b0b412d66194ef5403293917d66e70ab118f07cc24c5b14d"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures",
+ "rusoto_core",
+ "xml-rs",
+]
+
+[[package]]
+name = "rusoto_signature"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5ae95491c8b4847931e291b151127eccd6ff8ca13f33603eb3d0035ecb05272"
+dependencies = [
+ "base64 0.13.1",
+ "bytes",
+ "chrono",
+ "digest",
+ "futures",
+ "hex",
+ "hmac",
+ "http",
+ "hyper",
+ "log",
+ "md-5",
+ "percent-encoding",
+ "pin-project-lite",
+ "rusoto_credential",
+ "rustc_version",
+ "serde",
+ "sha2",
+ "tokio",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustls"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
+dependencies = [
+ "log",
+ "ring 0.16.20",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
+dependencies = [
+ "base64 0.21.5",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "schannel"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "sct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
+dependencies = [
+ "ring 0.17.5",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
+
+[[package]]
+name = "serde"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer",
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+ "opaque-debug",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
+
+[[package]]
+name = "socket2"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "subtle"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+
+[[package]]
+name = "syn"
+version = "2.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tokio"
+version = "1.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2 0.5.5",
+ "tokio-macros",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.23.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
+dependencies = [
+ "rustls",
+ "tokio",
+ "webpki",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-serde"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
+dependencies = [
+ "nu-ansi-term",
+ "serde",
+ "serde_json",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing-core",
+ "tracing-log",
+ "tracing-serde",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
+
+[[package]]
+name = "turbofetch"
+version = "0.1.0"
+dependencies = [
+ "aws_lambda_events",
+ "bytes",
+ "data-encoding",
+ "futures",
+ "httparse",
+ "hyper",
+ "lambda_runtime",
+ "magic-buffer",
+ "rusoto_core",
+ "rusoto_s3",
+ "serde",
+ "serde_json",
+ "tokio",
+ "tower",
+ "tracing",
+ "tracing-subscriber",
+ "zstd",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
+
+[[package]]
+name = "web-sys"
+version = "0.3.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
+dependencies = [
+ "ring 0.17.5",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.51.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
+
+[[package]]
+name = "zeroize"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
+
+[[package]]
+name = "zstd"
+version = "0.9.2+zstd.1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2390ea1bf6c038c39674f22d95f0564725fc06034a47129179810b2fc58caa54"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "4.1.3+zstd.1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e99d81b99fb3c2c2c794e3fe56c305c63d5173a16a46b5850b07c935ffc7db79"
+dependencies = [
+ "libc",
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "1.6.2+zstd.1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f"
+dependencies = [
+ "cc",
+ "libc",
+]
diff --git a/tvix/tools/turbofetch/Cargo.nix b/tvix/tools/turbofetch/Cargo.nix
new file mode 100644
index 0000000000..d7263727e2
--- /dev/null
+++ b/tvix/tools/turbofetch/Cargo.nix
@@ -0,0 +1,6466 @@
+# This file was @generated by crate2nix 0.13.0 with the command:
+#   "generate" "--all-features"
+# See https://github.com/kolloch/crate2nix for more info.
+
+{ nixpkgs ? <nixpkgs>
+, pkgs ? import nixpkgs { config = { }; }
+, lib ? pkgs.lib
+, stdenv ? pkgs.stdenv
+, buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate
+  # This is used as the `crateOverrides` argument for `buildRustCrate`.
+, defaultCrateOverrides ? pkgs.defaultCrateOverrides
+  # The features to enable for the root_crate or the workspace_members.
+, rootFeatures ? [ "default" ]
+  # If true, throw errors instead of issueing deprecation warnings.
+, strictDeprecation ? false
+  # Used for conditional compilation based on CPU feature detection.
+, targetFeatures ? [ ]
+  # Whether to perform release builds: longer compile times, faster binaries.
+, release ? true
+  # Additional crate2nix configuration if it exists.
+, crateConfig ? if builtins.pathExists ./crate-config.nix
+  then pkgs.callPackage ./crate-config.nix { }
+  else { }
+}:
+
+rec {
+  #
+  # "public" attributes that we attempt to keep stable with new versions of crate2nix.
+  #
+
+  rootCrate = rec {
+    packageId = "turbofetch";
+
+    # Use this attribute to refer to the derivation building your root crate package.
+    # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }.
+    build = internal.buildRustCrateWithFeatures {
+      inherit packageId;
+    };
+
+    # Debug support which might change between releases.
+    # File a bug if you depend on any for non-debug work!
+    debug = internal.debugCrate { inherit packageId; };
+  };
+  # Refer your crate build derivation by name here.
+  # You can override the features with
+  # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }.
+  workspaceMembers = {
+    "turbofetch" = rec {
+      packageId = "turbofetch";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "turbofetch";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+  };
+
+  # A derivation that joins the outputs of all workspace members together.
+  allWorkspaceMembers = pkgs.symlinkJoin {
+    name = "all-workspace-members";
+    paths =
+      let members = builtins.attrValues workspaceMembers;
+      in builtins.map (m: m.build) members;
+  };
+
+  #
+  # "internal" ("private") attributes that may change in every new version of crate2nix.
+  #
+
+  internal = rec {
+    # Build and dependency information for crates.
+    # Many of the fields are passed one-to-one to buildRustCrate.
+    #
+    # Noteworthy:
+    # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate.
+    #   but with additional information which is used during dependency/feature resolution.
+    # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging.
+    # * `devDependencies` as of now not used by `buildRustCrate` but used to
+    #   inject test dependencies into the build
+
+    crates = {
+      "addr2line" = rec {
+        crateName = "addr2line";
+        version = "0.21.0";
+        edition = "2018";
+        sha256 = "1jx0k3iwyqr8klqbzk6kjvr496yd94aspis10vwsj5wy7gib4c4a";
+        dependencies = [
+          {
+            name = "gimli";
+            packageId = "gimli";
+            usesDefaultFeatures = false;
+            features = [ "read" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "rustc-demangle" "cpp_demangle" "std-object" "fallible-iterator" "smallvec" "memmap2" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "memmap2" = [ "dep:memmap2" ];
+          "object" = [ "dep:object" ];
+          "rustc-demangle" = [ "dep:rustc-demangle" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "gimli/rustc-dep-of-std" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "gimli/std" ];
+          "std-object" = [ "std" "object" "object/std" "object/compression" "gimli/endian-reader" ];
+        };
+      };
+      "adler" = rec {
+        crateName = "adler";
+        version = "1.0.2";
+        edition = "2015";
+        sha256 = "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj";
+        authors = [
+          "Jonas Schievink <jonasschievink@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "android-tzdata" = rec {
+        crateName = "android-tzdata";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        authors = [
+          "RumovZ"
+        ];
+
+      };
+      "android_system_properties" = rec {
+        crateName = "android_system_properties";
+        version = "0.1.5";
+        edition = "2018";
+        sha256 = "04b3wrz12837j7mdczqd95b732gw5q7q66cv4yn4646lvccp57l1";
+        authors = [
+          "Nicolas Silva <nical@fastmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "async-stream" = rec {
+        crateName = "async-stream";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream-impl";
+            packageId = "async-stream-impl";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "async-stream-impl" = rec {
+        crateName = "async-stream-impl";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
+        procMacro = true;
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "async-trait" = rec {
+        crateName = "async-trait";
+        version = "0.1.74";
+        edition = "2021";
+        sha256 = "1ydhbsqjqqa6bxbv0kgys2wq2vi3jpwjy57dk162ajwppgqkfrd6";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "autocfg" = rec {
+        crateName = "autocfg";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "1ylp3cb47ylzabimazvbz9ms6ap784zhb6syaz6c1jqpmcmq0s6l";
+        authors = [
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+
+      };
+      "aws_lambda_events" = rec {
+        crateName = "aws_lambda_events";
+        version = "0.11.1";
+        edition = "2021";
+        sha256 = "0nw3iyfgywhrqagl1083yqjg82jgv438zczh94zipwyfcvg1273p";
+        authors = [
+          "Christian Legnitto <christian@legnitto.com>"
+          "Sam Rijs <srijs@airpost.net>"
+          "David Calavera <dcalaver@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64 0.21.5";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+            features = [ "serde" ];
+          }
+          {
+            name = "http";
+            packageId = "http";
+            optional = true;
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+            optional = true;
+          }
+          {
+            name = "http-serde";
+            packageId = "http-serde";
+            optional = true;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+        ];
+        features = {
+          "alb" = [ "bytes" "http" "http-body" "http-serde" "query_map" ];
+          "apigw" = [ "bytes" "http" "http-body" "http-serde" "query_map" ];
+          "autoscaling" = [ "chrono" ];
+          "bytes" = [ "dep:bytes" ];
+          "chime_bot" = [ "chrono" ];
+          "chrono" = [ "dep:chrono" ];
+          "cloudwatch_events" = [ "chrono" ];
+          "cloudwatch_logs" = [ "flate2" ];
+          "code_commit" = [ "chrono" ];
+          "codebuild" = [ "chrono" ];
+          "codedeploy" = [ "chrono" ];
+          "codepipeline_cloudwatch" = [ "chrono" ];
+          "default" = [ "activemq" "alb" "apigw" "appsync" "autoscaling" "chime_bot" "clientvpn" "cloudwatch_events" "cloudwatch_logs" "code_commit" "codebuild" "codedeploy" "codepipeline_cloudwatch" "codepipeline_job" "cognito" "config" "connect" "dynamodb" "ecr_scan" "firehose" "iam" "iot" "iot_1_click" "iot_button" "iot_deprecated" "kafka" "kinesis" "kinesis_analytics" "lambda_function_urls" "lex" "rabbitmq" "s3" "s3_batch_job" "ses" "sns" "sqs" "streams" ];
+          "dynamodb" = [ "chrono" "serde_dynamo" "streams" ];
+          "firehose" = [ "chrono" ];
+          "flate2" = [ "dep:flate2" ];
+          "http" = [ "dep:http" ];
+          "http-body" = [ "dep:http-body" ];
+          "http-serde" = [ "dep:http-serde" ];
+          "iot" = [ "bytes" "http" "http-body" "http-serde" "iam" ];
+          "iot_deprecated" = [ "iot" ];
+          "kafka" = [ "chrono" ];
+          "kinesis" = [ "chrono" ];
+          "kinesis_analytics" = [ "kinesis" ];
+          "lambda_function_urls" = [ "bytes" "http" "http-body" "http-serde" ];
+          "query_map" = [ "dep:query_map" ];
+          "s3" = [ "bytes" "chrono" "http" "http-body" "http-serde" ];
+          "s3_batch_job" = [ "s3" ];
+          "serde_dynamo" = [ "dep:serde_dynamo" ];
+          "serde_with" = [ "dep:serde_with" ];
+          "ses" = [ "chrono" ];
+          "sns" = [ "chrono" "serde_with" ];
+          "sqs" = [ "serde_with" ];
+        };
+        resolvedDefaultFeatures = [ "bytes" "http" "http-body" "http-serde" "lambda_function_urls" ];
+      };
+      "backtrace" = rec {
+        crateName = "backtrace";
+        version = "0.3.69";
+        edition = "2018";
+        sha256 = "0dsq23dhw4pfndkx2nsa1ml2g31idm7ss7ljxp8d57avygivg290";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "addr2line";
+            packageId = "addr2line";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "object";
+            packageId = "object";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+            features = [ "read_core" "elf" "macho" "pe" "unaligned" "archive" ];
+          }
+          {
+            name = "rustc-demangle";
+            packageId = "rustc-demangle";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "std" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "serialize-rustc" = [ "rustc-serialize" ];
+          "serialize-serde" = [ "serde" ];
+          "verify-winapi" = [ "winapi/dbghelp" "winapi/handleapi" "winapi/libloaderapi" "winapi/memoryapi" "winapi/minwindef" "winapi/processthreadsapi" "winapi/synchapi" "winapi/tlhelp32" "winapi/winbase" "winapi/winnt" ];
+          "winapi" = [ "dep:winapi" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64 0.13.1" = rec {
+        crateName = "base64";
+        version = "0.13.1";
+        edition = "2018";
+        sha256 = "1s494mqmzjb766fy1kqlccgfg2sdcjb6hzbvzqv2jw65fdi5h6wy";
+        authors = [
+          "Alice Maz <alice@alicemaz.com>"
+          "Marshall Pierce <marshall@mpierce.org>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64 0.20.0" = rec {
+        crateName = "base64";
+        version = "0.20.0";
+        edition = "2021";
+        sha256 = "1r855djiv8rirg37w5arazk42ya5gm5gd2bww75v14w0sy02i8hf";
+        authors = [
+          "Alice Maz <alice@alicemaz.com>"
+          "Marshall Pierce <marshall@mpierce.org>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64 0.21.5" = rec {
+        crateName = "base64";
+        version = "0.21.5";
+        edition = "2018";
+        sha256 = "1y8x2xs9nszj5ix7gg4ycn5a6wy7ca74zxwqri3bdqzdjha6lqrm";
+        authors = [
+          "Alice Maz <alice@alicemaz.com>"
+          "Marshall Pierce <marshall@mpierce.org>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "bitflags 1.3.2" = rec {
+        crateName = "bitflags";
+        version = "1.3.2";
+        edition = "2018";
+        sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bitflags 2.4.1" = rec {
+        crateName = "bitflags";
+        version = "2.4.1";
+        edition = "2021";
+        sha256 = "01ryy3kd671b0ll4bhdvhsz67vwz1lz53fz504injrd7wpv64xrj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "block-buffer" = rec {
+        crateName = "block-buffer";
+        version = "0.9.0";
+        edition = "2018";
+        sha256 = "1r4pf90s7d7lj1wdjhlnqa26vvbm6pnc33z138lxpnp9srpi2lj1";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+        features = {
+          "block-padding" = [ "dep:block-padding" ];
+        };
+      };
+      "bumpalo" = rec {
+        crateName = "bumpalo";
+        version = "3.14.0";
+        edition = "2021";
+        sha256 = "1v4arnv9kwk54v5d0qqpv4vyw2sgr660nk0w3apzixi1cm3yfc3z";
+        authors = [
+          "Nick Fitzgerald <fitzgen@gmail.com>"
+        ];
+        features = {
+          "allocator-api2" = [ "dep:allocator-api2" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bytes" = rec {
+        crateName = "bytes";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "serde" "std" ];
+      };
+      "cc" = rec {
+        crateName = "cc";
+        version = "1.0.83";
+        edition = "2018";
+        crateBin = [ ];
+        sha256 = "1l643zidlb5iy1dskc5ggqs4wqa29a02f44piczqc8zcnsq4y5zi";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "jobserver";
+            packageId = "jobserver";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        features = {
+          "jobserver" = [ "dep:jobserver" ];
+          "parallel" = [ "jobserver" ];
+        };
+        resolvedDefaultFeatures = [ "jobserver" "parallel" ];
+      };
+      "cfg-if" = rec {
+        crateName = "cfg-if";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "chrono" = rec {
+        crateName = "chrono";
+        version = "0.4.31";
+        edition = "2021";
+        sha256 = "0f6vg67pipm8cziad2yms6a639pssnvysk1m05dd9crymmdnhb3z";
+        dependencies = [
+          {
+            name = "android-tzdata";
+            packageId = "android-tzdata";
+            optional = true;
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "iana-time-zone";
+            packageId = "iana-time-zone";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+            features = [ "fallback" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "android-tzdata" = [ "dep:android-tzdata" ];
+          "arbitrary" = [ "dep:arbitrary" ];
+          "clock" = [ "std" "winapi" "iana-time-zone" "android-tzdata" ];
+          "default" = [ "clock" "std" "oldtime" "wasmbind" ];
+          "iana-time-zone" = [ "dep:iana-time-zone" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "pure-rust-locales" = [ "dep:pure-rust-locales" ];
+          "rkyv" = [ "dep:rkyv" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "unstable-locales" = [ "pure-rust-locales" "alloc" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+          "wasmbind" = [ "wasm-bindgen" "js-sys" ];
+          "winapi" = [ "windows-targets" ];
+          "windows-targets" = [ "dep:windows-targets" ];
+        };
+        resolvedDefaultFeatures = [ "android-tzdata" "clock" "iana-time-zone" "serde" "std" "winapi" "windows-targets" ];
+      };
+      "core-foundation" = rec {
+        crateName = "core-foundation";
+        version = "0.9.3";
+        edition = "2015";
+        sha256 = "0ii1ihpjb30fk38gdikm5wqlkmyr8k46fh4k2r8sagz5dng7ljhr";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "chrono" = [ "dep:chrono" ];
+          "mac_os_10_7_support" = [ "core-foundation-sys/mac_os_10_7_support" ];
+          "mac_os_10_8_features" = [ "core-foundation-sys/mac_os_10_8_features" ];
+          "uuid" = [ "dep:uuid" ];
+          "with-chrono" = [ "chrono" ];
+          "with-uuid" = [ "uuid" ];
+        };
+      };
+      "core-foundation-sys" = rec {
+        crateName = "core-foundation-sys";
+        version = "0.8.4";
+        edition = "2015";
+        sha256 = "1yhf471qj6snnm2mcswai47vsbc9w30y4abmdp4crb4av87sb5p4";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = { };
+      };
+      "cpufeatures" = rec {
+        crateName = "cpufeatures";
+        version = "0.2.11";
+        edition = "2018";
+        sha256 = "1l0gzsyy576n017g9bf0vkv5hhg9cpz1h1libxyfdlzcgbh0yhnf";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-linux-android");
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("apple" == target."vendor" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("loongarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+        ];
+
+      };
+      "crc32fast" = rec {
+        crateName = "crc32fast";
+        version = "1.3.2";
+        edition = "2015";
+        sha256 = "03c8f29yx293yf43xar946xbls1g60c207m9drf8ilqhr25vsh5m";
+        authors = [
+          "Sam Rijs <srijs@airpost.net>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crypto-mac" = rec {
+        crateName = "crypto-mac";
+        version = "0.11.1";
+        edition = "2018";
+        sha256 = "05672ncc54h66vph42s0a42ljl69bwnqjh0x4xgj2v1395psildi";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "cipher" = [ "dep:cipher" ];
+          "dev" = [ "blobby" ];
+        };
+      };
+      "data-encoding" = rec {
+        crateName = "data-encoding";
+        version = "2.4.0";
+        edition = "2018";
+        sha256 = "023k3dk8422jgbj7k72g63x51h1mhv91dhw1j4h205vzh6fnrrn2";
+        authors = [
+          "Julien Cretin <git@ia0.eu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "digest" = rec {
+        crateName = "digest";
+        version = "0.9.0";
+        edition = "2018";
+        sha256 = "0rmhvk33rgvd6ll71z8sng91a52rw14p0drjn1da0mqa138n1pfk";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "dev" = [ "blobby" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "dirs-next" = rec {
+        crateName = "dirs-next";
+        version = "2.0.0";
+        edition = "2018";
+        sha256 = "1q9kr151h9681wwp6is18750ssghz6j9j7qm7qi1ngcwy7mzi35r";
+        authors = [
+          "The @xdg-rs members"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "dirs-sys-next";
+            packageId = "dirs-sys-next";
+          }
+        ];
+
+      };
+      "dirs-sys-next" = rec {
+        crateName = "dirs-sys-next";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "0kavhavdxv4phzj4l0psvh55hszwnr0rcz8sxbvx20pyqi2a3gaf";
+        authors = [
+          "The @xdg-rs members"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_users";
+            packageId = "redox_users";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "knownfolders" "objbase" "shlobj" "winbase" "winerror" ];
+          }
+        ];
+
+      };
+      "fnv" = rec {
+        crateName = "fnv";
+        version = "1.0.7";
+        edition = "2015";
+        sha256 = "1hc2mcqha06aibcaza94vbi81j6pr9a1bbxrxjfhc91zin8yr7iz";
+        libPath = "lib.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "futures" = rec {
+        crateName = "futures";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1c04g14bccmprwsvx2j9m2blhwrynq7vhl151lsvcv4gi0b6jp34";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-executor";
+            packageId = "futures-executor";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" "futures-sink/alloc" "futures-channel/alloc" "futures-util/alloc" ];
+          "async-await" = [ "futures-util/async-await" "futures-util/async-await-macro" ];
+          "bilock" = [ "futures-util/bilock" ];
+          "compat" = [ "std" "futures-util/compat" ];
+          "default" = [ "std" "async-await" "executor" ];
+          "executor" = [ "std" "futures-executor/std" ];
+          "futures-executor" = [ "dep:futures-executor" ];
+          "io-compat" = [ "compat" "futures-util/io-compat" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "futures-io/std" "futures-sink/std" "futures-util/std" "futures-util/io" "futures-util/channel" ];
+          "thread-pool" = [ "executor" "futures-executor/thread-pool" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" "futures-channel/unstable" "futures-io/unstable" "futures-util/unstable" ];
+          "write-all-vectored" = [ "futures-util/write-all-vectored" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "default" "executor" "futures-executor" "std" ];
+      };
+      "futures-channel" = rec {
+        crateName = "futures-channel";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" ];
+          "default" = [ "std" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "sink" = [ "futures-sink" ];
+          "std" = [ "alloc" "futures-core/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "futures-sink" "sink" "std" ];
+      };
+      "futures-core" = rec {
+        crateName = "futures-core";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        features = {
+          "default" = [ "std" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-executor" = rec {
+        crateName = "futures-executor";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "07dh08gs9vfll2h36kq32q9xd86xm6lyl9xikmmwlkqnmrrgqxm5";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "std" = [ "futures-core/std" "futures-task/std" "futures-util/std" ];
+          "thread-pool" = [ "std" "num_cpus" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "futures-io" = rec {
+        crateName = "futures-io";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1hgh25isvsr4ybibywhr4dpys8mjnscw4wfxxwca70cn1gi26im4";
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "futures-macro" = rec {
+        crateName = "futures-macro";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "futures-sink" = rec {
+        crateName = "futures-sink";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-task" = rec {
+        crateName = "futures-task";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "futures-util" = rec {
+        crateName = "futures-util";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-macro";
+            packageId = "futures-macro";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "pin-utils";
+            packageId = "pin-utils";
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" ];
+          "async-await-macro" = [ "async-await" "futures-macro" ];
+          "channel" = [ "std" "futures-channel" ];
+          "compat" = [ "std" "futures_01" ];
+          "default" = [ "std" "async-await" "async-await-macro" ];
+          "futures-channel" = [ "dep:futures-channel" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-macro" = [ "dep:futures-macro" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "futures_01" = [ "dep:futures_01" ];
+          "io" = [ "std" "futures-io" "memchr" ];
+          "io-compat" = [ "io" "compat" "tokio-io" ];
+          "memchr" = [ "dep:memchr" ];
+          "portable-atomic" = [ "futures-core/portable-atomic" ];
+          "sink" = [ "futures-sink" ];
+          "slab" = [ "dep:slab" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "slab" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" ];
+          "write-all-vectored" = [ "io" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "async-await-macro" "channel" "futures-channel" "futures-io" "futures-macro" "futures-sink" "io" "memchr" "sink" "slab" "std" ];
+      };
+      "generic-array" = rec {
+        crateName = "generic-array";
+        version = "0.14.7";
+        edition = "2015";
+        sha256 = "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45";
+        libName = "generic_array";
+        authors = [
+          "BartΕ‚omiej KamiΕ„ski <fizyk20@gmail.com>"
+          "Aaron Trent <novacrazy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+      };
+      "getrandom" = rec {
+        crateName = "getrandom";
+        version = "0.2.11";
+        edition = "2018";
+        sha256 = "03q7120cc2kn7ry013i67zmjl2g9q73h1ks5z08hq5v9syz0d47y";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "js" = [ "wasm-bindgen" "js-sys" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "libc/rustc-dep-of-std" "wasi/rustc-dep-of-std" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "gimli" = rec {
+        crateName = "gimli";
+        version = "0.28.0";
+        edition = "2018";
+        sha256 = "1h7hcl3chfvd2gfrrxjymnwj7anqxjslvz20kcargkvsya2dgf3g";
+        features = {
+          "default" = [ "read-all" "write" ];
+          "endian-reader" = [ "read" "dep:stable_deref_trait" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "read" = [ "read-core" ];
+          "read-all" = [ "read" "std" "fallible-iterator" "endian-reader" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" ];
+          "std" = [ "fallible-iterator?/std" "stable_deref_trait?/std" ];
+          "write" = [ "dep:indexmap" ];
+        };
+        resolvedDefaultFeatures = [ "read" "read-core" ];
+      };
+      "h2" = rec {
+        crateName = "h2";
+        version = "0.3.21";
+        edition = "2018";
+        sha256 = "0cq8g5bgk3fihnqicy3g8gc3dpsalzqjg4bjyip9g4my26m27z4i";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            features = [ "codec" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt-multi-thread" "macros" "sync" "net" ];
+          }
+        ];
+        features = { };
+      };
+      "hashbrown" = rec {
+        crateName = "hashbrown";
+        version = "0.12.3";
+        edition = "2021";
+        sha256 = "1268ka4750pyg2pbgsr43f0289l5zah4arir2k4igx5a8c6fg7la";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "ahash-compile-time-rng" = [ "ahash/compile-time-rng" ];
+          "alloc" = [ "dep:alloc" ];
+          "bumpalo" = [ "dep:bumpalo" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "ahash" "inline-more" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "raw" ];
+      };
+      "hermit-abi" = rec {
+        crateName = "hermit-abi";
+        version = "0.3.3";
+        edition = "2021";
+        sha256 = "1dyc8qsjh876n74a3rcz8h43s27nj1sypdhsn2ms61bd3b47wzyp";
+        authors = [
+          "Stefan Lankes"
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins/rustc-dep-of-std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "hex" = rec {
+        crateName = "hex";
+        version = "0.4.3";
+        edition = "2018";
+        sha256 = "0w1a4davm1lgzpamwnba907aysmlrnygbqmfis2mqjx5m552a93z";
+        authors = [
+          "KokaKiwi <kokakiwi@kokakiwi.net>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "hmac" = rec {
+        crateName = "hmac";
+        version = "0.11.0";
+        edition = "2018";
+        sha256 = "16z61aibdg4di40sqi4ks2s4rz6r29w4sx4gvblfph3yxch26aia";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "crypto-mac";
+            packageId = "crypto-mac";
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "crypto-mac";
+            packageId = "crypto-mac";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "std" = [ "crypto-mac/std" ];
+        };
+      };
+      "http" = rec {
+        crateName = "http";
+        version = "0.2.10";
+        edition = "2018";
+        sha256 = "0l61nzcb5rdfchn7gpml0wngipflbqarrq3q5ga30rw9msy9lnzr";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+        ];
+
+      };
+      "http-body" = rec {
+        crateName = "http-body";
+        version = "0.4.5";
+        edition = "2018";
+        sha256 = "1l967qwwlvhp198xdrnc0p5d7jwfcp6q2lm510j6zqw4s4b8zwym";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "http-serde" = rec {
+        crateName = "http-serde";
+        version = "1.1.3";
+        edition = "2021";
+        sha256 = "1vnald3g10gxj15dc5jjjk7aff23p1zly0xgzhn5gwfrb9k0nmkg";
+        authors = [
+          "Kornel <kornel@geekhood.net>"
+        ];
+        dependencies = [
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+
+      };
+      "httparse" = rec {
+        crateName = "httparse";
+        version = "1.8.0";
+        edition = "2018";
+        sha256 = "010rrfahm1jss3p022fqf3j3jmm72vhn4iqhykahb9ynpaag75yq";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "httpdate" = rec {
+        crateName = "httpdate";
+        version = "1.0.3";
+        edition = "2021";
+        sha256 = "1aa9rd2sac0zhjqh24c9xvir96g188zldkx0hr6dnnlx5904cfyz";
+        authors = [
+          "Pyfisch <pyfisch@posteo.org>"
+        ];
+
+      };
+      "hyper" = rec {
+        crateName = "hyper";
+        version = "0.14.27";
+        edition = "2018";
+        sha256 = "0s2l74p3harvjgb0bvaxlxgxq71vpfrzv0cqz2p9w8d8akbczcgz";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "h2";
+            packageId = "h2";
+            optional = true;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "httparse";
+            packageId = "httparse";
+          }
+          {
+            name = "httpdate";
+            packageId = "httpdate";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2 0.4.10";
+            optional = true;
+            features = [ "all" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "want";
+            packageId = "want";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "macros" "io-std" "io-util" "rt" "rt-multi-thread" "sync" "time" "test-util" ];
+          }
+        ];
+        features = {
+          "ffi" = [ "libc" ];
+          "full" = [ "client" "http1" "http2" "server" "stream" "runtime" ];
+          "h2" = [ "dep:h2" ];
+          "http2" = [ "h2" ];
+          "libc" = [ "dep:libc" ];
+          "runtime" = [ "tcp" "tokio/rt" "tokio/time" ];
+          "socket2" = [ "dep:socket2" ];
+          "tcp" = [ "socket2" "tokio/net" "tokio/rt" "tokio/time" ];
+        };
+        resolvedDefaultFeatures = [ "client" "default" "h2" "http1" "http2" "runtime" "server" "socket2" "stream" "tcp" ];
+      };
+      "hyper-rustls" = rec {
+        crateName = "hyper-rustls";
+        version = "0.23.2";
+        edition = "2018";
+        sha256 = "0736s6a32dqr107f943xaz1n05flbinq6l19lq1wsrxkc5g9d20p";
+        dependencies = [
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            usesDefaultFeatures = false;
+            features = [ "client" ];
+          }
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "rustls";
+            packageId = "rustls";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rustls-native-certs";
+            packageId = "rustls-native-certs";
+            optional = true;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "tokio-rustls";
+            packageId = "tokio-rustls";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "full" ];
+          }
+          {
+            name = "rustls";
+            packageId = "rustls";
+            usesDefaultFeatures = false;
+            features = [ "tls12" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-std" "macros" "net" "rt-multi-thread" ];
+          }
+        ];
+        features = {
+          "default" = [ "native-tokio" "http1" "tls12" "logging" ];
+          "http1" = [ "hyper/http1" ];
+          "http2" = [ "hyper/http2" ];
+          "log" = [ "dep:log" ];
+          "logging" = [ "log" "tokio-rustls/logging" "rustls/logging" ];
+          "native-tokio" = [ "tokio-runtime" "rustls-native-certs" ];
+          "rustls-native-certs" = [ "dep:rustls-native-certs" ];
+          "tls12" = [ "tokio-rustls/tls12" "rustls/tls12" ];
+          "tokio-runtime" = [ "hyper/runtime" ];
+          "webpki-roots" = [ "dep:webpki-roots" ];
+          "webpki-tokio" = [ "tokio-runtime" "webpki-roots" ];
+        };
+        resolvedDefaultFeatures = [ "http1" "http2" "log" "logging" "native-tokio" "rustls-native-certs" "tls12" "tokio-runtime" ];
+      };
+      "iana-time-zone" = rec {
+        crateName = "iana-time-zone";
+        version = "0.1.58";
+        edition = "2018";
+        sha256 = "081vcr8z8ddhl5r1ywif6grnswk01b2ac4nks2bhn8zzdimvh9l3";
+        authors = [
+          "Andrew Straw <strawman@astraw.com>"
+          "RenΓ© Kijewski <rene.kijewski@fu-berlin.de>"
+          "Ryan Lopopolo <rjl@hyperbo.la>"
+        ];
+        dependencies = [
+          {
+            name = "android_system_properties";
+            packageId = "android_system_properties";
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "iana-time-zone-haiku";
+            packageId = "iana-time-zone-haiku";
+            target = { target, features }: ("haiku" == target."os" or null);
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "windows-core";
+            packageId = "windows-core";
+            target = { target, features }: ("windows" == target."os" or null);
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "fallback" ];
+      };
+      "iana-time-zone-haiku" = rec {
+        crateName = "iana-time-zone-haiku";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        authors = [
+          "RenΓ© Kijewski <crates.io@k6i.de>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "indexmap" = rec {
+        crateName = "indexmap";
+        version = "1.9.3";
+        edition = "2021";
+        sha256 = "16dxmy7yvk51wvnih3a3im6fp5lmx0wx76i03n06wyak6cwhw1xx";
+        dependencies = [
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            usesDefaultFeatures = false;
+            features = [ "raw" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-rayon" = [ "dep:rustc-rayon" ];
+          "serde" = [ "dep:serde" ];
+          "serde-1" = [ "serde" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "itoa" = rec {
+        crateName = "itoa";
+        version = "1.0.9";
+        edition = "2018";
+        sha256 = "0f6cpb4yqzhkrhhg6kqsw3wnmmhdnnffi6r2xzy248gzi2v0l5dg";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "jobserver" = rec {
+        crateName = "jobserver";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "0z9w6vfqwbr6hfk9yaw7kydlh6f7k39xdlszxlh39in4acwzcdwc";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+
+      };
+      "js-sys" = rec {
+        crateName = "js-sys";
+        version = "0.3.65";
+        edition = "2018";
+        sha256 = "1s1gaxgzpqfyygc7f2pwp9y128rh5f8zvsc4nm5yazgna9cw7h2l";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+        ];
+
+      };
+      "lambda_runtime" = rec {
+        crateName = "lambda_runtime";
+        version = "0.8.3";
+        edition = "2021";
+        sha256 = "00m7sw7bhjkr4q7ikid0kjjqirr23gcxfjdvvvyqp6nfsxjqzjny";
+        authors = [
+          "David Calavera <dcalaver@amazon.com>"
+          "Harold Sun <sunhua@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+          }
+          {
+            name = "base64";
+            packageId = "base64 0.20.0";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "http-serde";
+            packageId = "http-serde";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "http1" "client" "stream" "server" ];
+          }
+          {
+            name = "lambda_runtime_api_client";
+            packageId = "lambda_runtime_api_client";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "serde_path_to_error";
+            packageId = "serde_path_to_error";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "io-util" "sync" "rt-multi-thread" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            features = [ "util" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            features = [ "log" ];
+          }
+        ];
+        features = {
+          "default" = [ "simulated" ];
+        };
+        resolvedDefaultFeatures = [ "default" "simulated" ];
+      };
+      "lambda_runtime_api_client" = rec {
+        crateName = "lambda_runtime_api_client";
+        version = "0.8.0";
+        edition = "2021";
+        sha256 = "1vgh5cl1ddxskqzgbshxgydlw1a3qipmb2rlqg4wijis3zh5l339";
+        authors = [
+          "David Calavera <dcalaver@amazon.com>"
+          "Harold Sun <sunhua@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "http1" "client" "stream" "tcp" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" ];
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+        ];
+
+      };
+      "lazy_static" = rec {
+        crateName = "lazy_static";
+        version = "1.4.0";
+        edition = "2015";
+        sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2";
+        authors = [
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        features = {
+          "spin" = [ "dep:spin" ];
+          "spin_no_std" = [ "spin" ];
+        };
+      };
+      "libc" = rec {
+        crateName = "libc";
+        version = "0.2.150";
+        edition = "2015";
+        sha256 = "0g10n8c830alndgjb8xk1i9kz5z727np90z1z81119pr8d3jmnc9";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ];
+          "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "libredox" = rec {
+        crateName = "libredox";
+        version = "0.0.1";
+        edition = "2021";
+        sha256 = "1s2fh4ikpp9xl0lsl01pi0n8pw1q9s3ld452vd8qh1v63v537j45";
+        authors = [
+          "4lDO2 <4lDO2@protonmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.1";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall";
+          }
+        ];
+        features = {
+          "default" = [ "scheme" "call" ];
+          "scheme" = [ "call" ];
+        };
+        resolvedDefaultFeatures = [ "call" ];
+      };
+      "lock_api" = rec {
+        crateName = "lock_api";
+        version = "0.4.11";
+        edition = "2018";
+        sha256 = "0iggx0h4jx63xm35861106af3jkxq06fpqhpkhgw0axi2n38y5iw";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "atomic_usize" ];
+          "owning_ref" = [ "dep:owning_ref" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "atomic_usize" "default" ];
+      };
+      "log" = rec {
+        crateName = "log";
+        version = "0.4.20";
+        edition = "2015";
+        sha256 = "13rf7wphnwd61vazpxr7fiycin6cb1g8fmvgqg18i464p0y1drmm";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "kv_unstable" = [ "value-bag" ];
+          "kv_unstable_serde" = [ "kv_unstable_std" "value-bag/serde" "serde" ];
+          "kv_unstable_std" = [ "std" "kv_unstable" "value-bag/error" ];
+          "kv_unstable_sval" = [ "kv_unstable" "value-bag/sval" "sval" "sval_ref" ];
+          "serde" = [ "dep:serde" ];
+          "sval" = [ "dep:sval" ];
+          "sval_ref" = [ "dep:sval_ref" ];
+          "value-bag" = [ "dep:value-bag" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "mach2" = rec {
+        crateName = "mach2";
+        version = "0.4.1";
+        edition = "2015";
+        sha256 = "1s5dbscwk0w6czzvhxp9ix9c2djv4fpnj4za9byaclfiphq1h3bd";
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "magic-buffer" = rec {
+        crateName = "magic-buffer";
+        version = "0.1.1";
+        edition = "2021";
+        sha256 = "0bpzcrwq89cc5q8mgkmsyx39vjsqaxvaxs29qp8k04rndc7ysfh0";
+        authors = [
+          "Sebastian Klose <mail@sklose.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ("linux" == target."os" or null);
+          }
+          {
+            name = "mach2";
+            packageId = "mach2";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_System_SystemInformation" "Win32_System_Diagnostics_Debug" "Win32_System_Memory" "Win32_Security" ];
+          }
+        ];
+
+      };
+      "md-5" = rec {
+        crateName = "md-5";
+        version = "0.9.1";
+        edition = "2018";
+        sha256 = "059ajjacz1q3cms7vl6cvhdqs4qdw2nnwj9dq99ryzv0p6djfnkv";
+        libName = "md5";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer";
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+          {
+            name = "opaque-debug";
+            packageId = "opaque-debug";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "md5-asm" ];
+          "default" = [ "std" ];
+          "md5-asm" = [ "dep:md5-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "memchr" = rec {
+        crateName = "memchr";
+        version = "2.6.4";
+        edition = "2021";
+        sha256 = "0rq1ka8790ns41j147npvxcqcl2anxyngsdimy85ag2api0fwrgn";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+          "bluss"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "logging" = [ "dep:log" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "std" = [ "alloc" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "miniz_oxide" = rec {
+        crateName = "miniz_oxide";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1ivl3rbbdm53bzscrd01g60l46lz5krl270487d8lhjvwl5hx0g7";
+        authors = [
+          "Frommi <daniil.liferenko@gmail.com>"
+          "oyvindln <oyvindln@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "adler";
+            packageId = "adler";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "with-alloc" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "adler/rustc-dep-of-std" ];
+          "simd" = [ "simd-adler32" ];
+          "simd-adler32" = [ "dep:simd-adler32" ];
+        };
+      };
+      "mio" = rec {
+        crateName = "mio";
+        version = "0.8.9";
+        edition = "2018";
+        sha256 = "1l23hg513c23nhcdzvk25caaj28mic6qgqadbn8axgj6bqf2ikix";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_Storage_FileSystem" "Win32_System_IO" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = {
+          "default" = [ "log" ];
+          "log" = [ "dep:log" ];
+          "os-ext" = [ "os-poll" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_Security" ];
+        };
+        resolvedDefaultFeatures = [ "net" "os-ext" "os-poll" ];
+      };
+      "nu-ansi-term" = rec {
+        crateName = "nu-ansi-term";
+        version = "0.46.0";
+        edition = "2018";
+        sha256 = "115sywxh53p190lyw97alm14nc004qj5jm5lvdj608z84rbida3p";
+        authors = [
+          "ogham@bsago.me"
+          "Ryan Scheel (Havvy) <ryan.havvy@gmail.com>"
+          "Josh Triplett <josh@joshtriplett.org>"
+          "The Nushell Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "overload";
+            packageId = "overload";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: ("windows" == target."os" or null);
+            features = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "processenv" ];
+          }
+        ];
+        features = {
+          "derive_serde_style" = [ "serde" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "num-traits" = rec {
+        crateName = "num-traits";
+        version = "0.2.17";
+        edition = "2018";
+        sha256 = "0z16bi5zwgfysz6765v3rd6whfbjpihx3mhsn4dg8dzj2c221qrr";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "libm" = [ "dep:libm" ];
+        };
+      };
+      "num_cpus" = rec {
+        crateName = "num_cpus";
+        version = "1.16.0";
+        edition = "2015";
+        sha256 = "0hra6ihpnh06dvfvz9ipscys0xfqa9ca9hzp384d5m02ssvgqqa1";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(target."windows" or false));
+          }
+        ];
+
+      };
+      "object" = rec {
+        crateName = "object";
+        version = "0.32.1";
+        edition = "2018";
+        sha256 = "1c02x4kvqpnl3wn7gz9idm4jrbirbycyqjgiw6lm1g9k77fzkxcw";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "all" = [ "read" "write" "std" "compression" "wasm" ];
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "compression" = [ "dep:flate2" "dep:ruzstd" "std" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "read" "compression" ];
+          "doc" = [ "read_core" "write_std" "std" "compression" "archive" "coff" "elf" "macho" "pe" "wasm" "xcoff" ];
+          "pe" = [ "coff" ];
+          "read" = [ "read_core" "archive" "coff" "elf" "macho" "pe" "xcoff" "unaligned" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "alloc" "memchr/rustc-dep-of-std" ];
+          "std" = [ "memchr/std" ];
+          "unstable-all" = [ "all" "unstable" ];
+          "wasm" = [ "dep:wasmparser" ];
+          "write" = [ "write_std" "coff" "elf" "macho" "pe" "xcoff" ];
+          "write_core" = [ "dep:crc32fast" "dep:indexmap" "dep:hashbrown" ];
+          "write_std" = [ "write_core" "std" "indexmap?/std" "crc32fast?/std" ];
+        };
+        resolvedDefaultFeatures = [ "archive" "coff" "elf" "macho" "pe" "read_core" "unaligned" ];
+      };
+      "once_cell" = rec {
+        crateName = "once_cell";
+        version = "1.18.0";
+        edition = "2021";
+        sha256 = "0vapcd5ambwck95wyz3ymlim35jirgnqn9a0qmi19msymv95v2yx";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        features = {
+          "alloc" = [ "race" ];
+          "atomic-polyfill" = [ "critical-section" ];
+          "critical-section" = [ "dep:critical-section" "dep:atomic-polyfill" ];
+          "default" = [ "std" ];
+          "parking_lot" = [ "dep:parking_lot_core" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "race" "std" ];
+      };
+      "opaque-debug" = rec {
+        crateName = "opaque-debug";
+        version = "0.3.0";
+        edition = "2018";
+        sha256 = "1m8kzi4nd6shdqimn0mgb24f0hxslhnqd1whakyq06wcqd086jk2";
+        authors = [
+          "RustCrypto Developers"
+        ];
+
+      };
+      "openssl-probe" = rec {
+        crateName = "openssl-probe";
+        version = "0.1.5";
+        edition = "2015";
+        sha256 = "1kq18qm48rvkwgcggfkqq6pm948190czqc94d6bm2sir5hq1l0gz";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+
+      };
+      "overload" = rec {
+        crateName = "overload";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "0fdgbaqwknillagy1xq7xfgv60qdbk010diwl7s1p0qx7hb16n5i";
+        authors = [
+          "Daniel Salvadori <danaugrs@gmail.com>"
+        ];
+
+      };
+      "parking_lot" = rec {
+        crateName = "parking_lot";
+        version = "0.12.1";
+        edition = "2018";
+        sha256 = "13r2xk7mnxfc5g0g6dkdxqdqad99j7s7z8zhzz4npw5r0g0v4hip";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lock_api";
+            packageId = "lock_api";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core";
+          }
+        ];
+        features = {
+          "arc_lock" = [ "lock_api/arc_lock" ];
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot_core" = rec {
+        crateName = "parking_lot_core";
+        version = "0.9.9";
+        edition = "2018";
+        sha256 = "13h0imw1aq86wj28gxkblhkzx6z1gk8q18n0v76qmmj6cliajhjc";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "percent-encoding" = rec {
+        crateName = "percent-encoding";
+        version = "2.3.0";
+        edition = "2018";
+        sha256 = "152slflmparkh27hprw62sph8rv77wckzhwl2dhqk6bf563lfalv";
+        authors = [
+          "The rust-url developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "pin-project" = rec {
+        crateName = "pin-project";
+        version = "1.1.3";
+        edition = "2021";
+        sha256 = "08k4cpy8q3j93qqgnrbzkcgpn7g0a88l4a9nm33kyghpdhffv97x";
+        dependencies = [
+          {
+            name = "pin-project-internal";
+            packageId = "pin-project-internal";
+          }
+        ];
+
+      };
+      "pin-project-internal" = rec {
+        crateName = "pin-project-internal";
+        version = "1.1.3";
+        edition = "2021";
+        sha256 = "01a4l3vb84brv9v7wl71chzxra2kynm6yvcjca66xv3ij6fgsna3";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "pin-project-lite" = rec {
+        crateName = "pin-project-lite";
+        version = "0.2.13";
+        edition = "2018";
+        sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+
+      };
+      "pin-utils" = rec {
+        crateName = "pin-utils";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        authors = [
+          "Josef Brandl <mail@josefbrandl.de>"
+        ];
+
+      };
+      "proc-macro2" = rec {
+        crateName = "proc-macro2";
+        version = "1.0.69";
+        edition = "2021";
+        sha256 = "1nljgyllbm3yr3pa081bf83gxh6l4zvjqzaldw7v4mj9xfgihk0k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "quote" = rec {
+        crateName = "quote";
+        version = "1.0.33";
+        edition = "2018";
+        sha256 = "1biw54hbbr12wdwjac55z1m2x2rylciw83qnjn564a3096jgqrsj";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "redox_syscall" = rec {
+        crateName = "redox_syscall";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1aiifyz5dnybfvkk4cdab9p2kmphag1yad6iknc7aszlxxldf8j7";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+        features = {
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "bitflags/rustc-dep-of-std" ];
+        };
+      };
+      "redox_users" = rec {
+        crateName = "redox_users";
+        version = "0.4.4";
+        edition = "2021";
+        sha256 = "1d1c7dhbb62sh8jrq9dhvqcyxqsh3wg8qknsi94iwq3r0wh7k151";
+        authors = [
+          "Jose Narvaez <goyox86@gmail.com>"
+          "Wesley Hershberger <mggmugginsmc@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            features = [ "std" ];
+          }
+          {
+            name = "libredox";
+            packageId = "libredox";
+            usesDefaultFeatures = false;
+            features = [ "call" ];
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        features = {
+          "auth" = [ "rust-argon2" "zeroize" ];
+          "default" = [ "auth" ];
+          "rust-argon2" = [ "dep:rust-argon2" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+      };
+      "ring 0.16.20" = rec {
+        crateName = "ring";
+        version = "0.16.20";
+        edition = "2018";
+        links = "ring-asm";
+        sha256 = "1z682xp7v38ayq9g9nkbhhfpj6ygralmlx7wdmsfv8rnw99cylrh";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("android" == target."os" or null) || ("linux" == target."os" or null));
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (("android" == target."os" or null) || ("linux" == target."os" or null));
+            features = [ "std" ];
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("dragonfly" == target."os" or null) || ("freebsd" == target."os" or null) || ("illumos" == target."os" or null) || ("netbsd" == target."os" or null) || ("openbsd" == target."os" or null) || ("solaris" == target."os" or null));
+            features = [ "std" ];
+          }
+          {
+            name = "spin";
+            packageId = "spin 0.5.2";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("x86" == target."arch" or null) || ("x86_64" == target."arch" or null) || ((("aarch64" == target."arch" or null) || ("arm" == target."arch" or null)) && (("android" == target."os" or null) || ("fuchsia" == target."os" or null) || ("linux" == target."os" or null))));
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted 0.7.1";
+          }
+          {
+            name = "web-sys";
+            packageId = "web-sys";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("wasm32" == target."arch" or null) && ("unknown" == target."vendor" or null) && ("unknown" == target."os" or null) && ("" == target."env" or null));
+            features = [ "Crypto" "Window" ];
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("windows" == target."os" or null);
+            features = [ "ntsecapi" "wtypesbase" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((target."unix" or false) || (target."windows" or false));
+          }
+        ];
+        features = {
+          "default" = [ "alloc" "dev_urandom_fallback" ];
+          "dev_urandom_fallback" = [ "once_cell" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "dev_urandom_fallback" "once_cell" ];
+      };
+      "ring 0.17.5" = rec {
+        crateName = "ring";
+        version = "0.17.5";
+        edition = "2021";
+        links = "ring_core_0_17_5";
+        sha256 = "02sd768l7594rm3jw048z7kkml7zcyw4ir62p6cxirap8wq0a0pv";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("android" == target."os" or null) || ("linux" == target."os" or null));
+          }
+          {
+            name = "spin";
+            packageId = "spin 0.9.8";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("x86" == target."arch" or null) || ("x86_64" == target."arch" or null) || ((("aarch64" == target."arch" or null) || ("arm" == target."arch" or null)) && (("android" == target."os" or null) || ("fuchsia" == target."os" or null) || ("linux" == target."os" or null) || ("windows" == target."os" or null))));
+            features = [ "once" ];
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted 0.9.0";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("windows" == target."os" or null));
+            features = [ "Win32_Foundation" "Win32_System_Threading" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: ((target."unix" or false) || (target."windows" or false) || ("wasi" == target."os" or null));
+          }
+        ];
+        features = {
+          "default" = [ "alloc" "dev_urandom_fallback" ];
+          "std" = [ "alloc" ];
+          "wasm32_unknown_unknown_js" = [ "getrandom/js" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "dev_urandom_fallback" ];
+      };
+      "rusoto_core" = rec {
+        crateName = "rusoto_core";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "18ig9x4n68cgfvhzkyhl9w2qlhk945xczbb9c8r52dd79ss0vcqx";
+        authors = [
+          "Anthony DiMarco <ocramida@gmail.com>"
+          "Jimmy Cuadra <jimmy@jimmycuadra.com>"
+          "Matthew Mayer <matthewkmayer@gmail.com>"
+          "Nikita Pekin <contact@nikitapek.in>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "base64";
+            packageId = "base64 0.13.1";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "crc32fast";
+            packageId = "crc32fast";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "client" "http1" "http2" "tcp" ];
+          }
+          {
+            name = "hyper-rustls";
+            packageId = "hyper-rustls";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "http1" "http2" "tls12" "logging" ];
+          }
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "rusoto_credential";
+            packageId = "rusoto_credential";
+          }
+          {
+            name = "rusoto_signature";
+            packageId = "rusoto_signature";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "time" "io-util" ];
+          }
+          {
+            name = "xml-rs";
+            packageId = "xml-rs";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" ];
+          }
+        ];
+        features = {
+          "default" = [ "native-tls" ];
+          "encoding" = [ "flate2" ];
+          "flate2" = [ "dep:flate2" ];
+          "hyper-rustls" = [ "dep:hyper-rustls" ];
+          "hyper-tls" = [ "dep:hyper-tls" ];
+          "native-tls" = [ "hyper-tls" ];
+          "nightly-testing" = [ "rusoto_credential/nightly-testing" ];
+          "rustls" = [ "hyper-rustls/native-tokio" ];
+          "rustls-webpki" = [ "hyper-rustls/webpki-tokio" ];
+        };
+        resolvedDefaultFeatures = [ "hyper-rustls" "rustls" ];
+      };
+      "rusoto_credential" = rec {
+        crateName = "rusoto_credential";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "019dq3aq6hnfg4xvxdfsnrba08dwvciz0km4nr3n1basvc9nq2pf";
+        authors = [
+          "Anthony DiMarco <ocramida@gmail.com>"
+          "Jimmy Cuadra <jimmy@jimmycuadra.com>"
+          "Matthew Mayer <matthewkmayer@gmail.com>"
+          "Nikita Pekin <contact@nikitapek.in>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "clock" "serde" ];
+          }
+          {
+            name = "dirs-next";
+            packageId = "dirs-next";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "client" "http1" "tcp" "stream" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "shlex";
+            packageId = "shlex";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "process" "sync" "time" ];
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "rt-multi-thread" ];
+          }
+        ];
+        features = { };
+      };
+      "rusoto_s3" = rec {
+        crateName = "rusoto_s3";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "0kdiqljcq1wg26mp0vnn2wwjj0slxya63mhjnjqgc49l31vldbks";
+        authors = [
+          "Anthony DiMarco <ocramida@gmail.com>"
+          "Jimmy Cuadra <jimmy@jimmycuadra.com>"
+          "Matthew Mayer <matthewkmayer@gmail.com>"
+          "Nikita Pekin <contact@nikitapek.in>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "rusoto_core";
+            packageId = "rusoto_core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "xml-rs";
+            packageId = "xml-rs";
+          }
+        ];
+        features = {
+          "default" = [ "native-tls" ];
+          "deserialize_structs" = [ "bytes/serde" "serde" "serde_derive" ];
+          "native-tls" = [ "rusoto_core/native-tls" ];
+          "rustls" = [ "rusoto_core/rustls" ];
+          "serde" = [ "dep:serde" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+          "serialize_structs" = [ "bytes/serde" "serde" "serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "rustls" ];
+      };
+      "rusoto_signature" = rec {
+        crateName = "rusoto_signature";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "0wjjn3n3a01xxc1kdwqkrbw6zkgc4w8ia6r93s9lfj4b3i4rbbm5";
+        authors = [
+          "Anthony DiMarco <ocramida@gmail.com>"
+          "Jimmy Cuadra <jimmy@jimmycuadra.com>"
+          "Matthew Mayer <matthewkmayer@gmail.com>"
+          "Nikita Pekin <contact@nikitapek.in>"
+        ];
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64 0.13.1";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "clock" ];
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "hex";
+            packageId = "hex";
+          }
+          {
+            name = "hmac";
+            packageId = "hmac";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "stream" ];
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "md-5";
+            packageId = "md-5";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "rusoto_credential";
+            packageId = "rusoto_credential";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "rt-multi-thread" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" ];
+          }
+        ];
+
+      };
+      "rustc-demangle" = rec {
+        crateName = "rustc-demangle";
+        version = "0.1.23";
+        edition = "2015";
+        sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "rustc_version" = rec {
+        crateName = "rustc_version";
+        version = "0.4.0";
+        edition = "2018";
+        sha256 = "0rpk9rcdk405xhbmgclsh4pai0svn49x35aggl4nhbkd4a2zb85z";
+        authors = [
+          "Dirkjan Ochtman <dirkjan@ochtman.nl>"
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "semver";
+            packageId = "semver";
+          }
+        ];
+
+      };
+      "rustls" = rec {
+        crateName = "rustls";
+        version = "0.20.9";
+        edition = "2018";
+        sha256 = "16byazb8jfr06kgbijy92bdk0ila806g6a00a6l9x64mqpgf700v";
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "ring";
+            packageId = "ring 0.16.20";
+          }
+          {
+            name = "sct";
+            packageId = "sct";
+          }
+          {
+            name = "webpki";
+            packageId = "webpki";
+            features = [ "alloc" "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "default" = [ "logging" "tls12" ];
+          "log" = [ "dep:log" ];
+          "logging" = [ "log" ];
+          "read_buf" = [ "rustversion" ];
+          "rustversion" = [ "dep:rustversion" ];
+        };
+        resolvedDefaultFeatures = [ "log" "logging" "tls12" ];
+      };
+      "rustls-native-certs" = rec {
+        crateName = "rustls-native-certs";
+        version = "0.6.3";
+        edition = "2021";
+        sha256 = "007zind70rd5rfsrkdcfm8vn09j8sg02phg9334kark6rdscxam9";
+        dependencies = [
+          {
+            name = "openssl-probe";
+            packageId = "openssl-probe";
+            target = { target, features }: ((target."unix" or false) && (!("macos" == target."os" or null)));
+          }
+          {
+            name = "rustls-pemfile";
+            packageId = "rustls-pemfile";
+          }
+          {
+            name = "schannel";
+            packageId = "schannel";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "security-framework";
+            packageId = "security-framework";
+            target = { target, features }: ("macos" == target."os" or null);
+          }
+        ];
+
+      };
+      "rustls-pemfile" = rec {
+        crateName = "rustls-pemfile";
+        version = "1.0.4";
+        edition = "2018";
+        sha256 = "1324n5bcns0rnw6vywr5agff3rwfvzphi7rmbyzwnv6glkhclx0w";
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64 0.21.5";
+          }
+        ];
+
+      };
+      "ryu" = rec {
+        crateName = "ryu";
+        version = "1.0.15";
+        edition = "2018";
+        sha256 = "0hfphpn1xnpzxwj8qg916ga1lyc33lc03lnf1gb3wwpglj6wrm0s";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "schannel" = rec {
+        crateName = "schannel";
+        version = "0.1.22";
+        edition = "2018";
+        sha256 = "126zy5jb95fc5hvzyjwiq6lc81r08rdcn6affn00ispp9jzk6dqc";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Steffen Butzer <steffen.butzer@outlook.com>"
+        ];
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            features = [ "Win32_Foundation" "Win32_Security_Cryptography" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_System_Memory" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            features = [ "Win32_System_SystemInformation" "Win32_System_Time" ];
+          }
+        ];
+
+      };
+      "scopeguard" = rec {
+        crateName = "scopeguard";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "0jcz9sd47zlsgcnm1hdw0664krxwb5gczlif4qngj2aif8vky54l";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+        };
+      };
+      "sct" = rec {
+        crateName = "sct";
+        version = "0.7.1";
+        edition = "2021";
+        sha256 = "056lmi2xkzdg1dbai6ha3n57s18cbip4pnmpdhyljli3m99n216s";
+        authors = [
+          "Joseph Birr-Pixton <jpixton@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ring";
+            packageId = "ring 0.17.5";
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted 0.9.0";
+          }
+        ];
+
+      };
+      "security-framework" = rec {
+        crateName = "security-framework";
+        version = "2.9.2";
+        edition = "2021";
+        sha256 = "1pplxk15s5yxvi2m1sz5xfmjibp96cscdcl432w9jzbk0frlzdh5";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Kornel <kornel@geekhood.net>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "core-foundation";
+            packageId = "core-foundation";
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "security-framework-sys";
+            packageId = "security-framework-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "OSX_10_10" = [ "OSX_10_9" "security-framework-sys/OSX_10_10" ];
+          "OSX_10_11" = [ "OSX_10_10" "security-framework-sys/OSX_10_11" ];
+          "OSX_10_12" = [ "OSX_10_11" "security-framework-sys/OSX_10_12" ];
+          "OSX_10_13" = [ "OSX_10_12" "security-framework-sys/OSX_10_13" "alpn" "session-tickets" "serial-number-bigint" ];
+          "OSX_10_14" = [ "OSX_10_13" "security-framework-sys/OSX_10_14" ];
+          "OSX_10_15" = [ "OSX_10_14" "security-framework-sys/OSX_10_15" ];
+          "OSX_10_9" = [ "security-framework-sys/OSX_10_9" ];
+          "default" = [ "OSX_10_9" ];
+          "log" = [ "dep:log" ];
+          "serial-number-bigint" = [ "dep:num-bigint" ];
+        };
+        resolvedDefaultFeatures = [ "OSX_10_9" "default" ];
+      };
+      "security-framework-sys" = rec {
+        crateName = "security-framework-sys";
+        version = "2.9.1";
+        edition = "2021";
+        sha256 = "0yhciwlsy9dh0ps1gw3197kvyqx1bvc4knrhiznhid6kax196cp9";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+          "Kornel <kornel@geekhood.net>"
+        ];
+        dependencies = [
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        features = {
+          "OSX_10_10" = [ "OSX_10_9" ];
+          "OSX_10_11" = [ "OSX_10_10" ];
+          "OSX_10_12" = [ "OSX_10_11" ];
+          "OSX_10_13" = [ "OSX_10_12" ];
+          "OSX_10_14" = [ "OSX_10_13" ];
+          "OSX_10_15" = [ "OSX_10_14" ];
+          "default" = [ "OSX_10_9" ];
+        };
+        resolvedDefaultFeatures = [ "OSX_10_9" ];
+      };
+      "semver" = rec {
+        crateName = "semver";
+        version = "1.0.20";
+        edition = "2018";
+        sha256 = "140hmbfa743hbmah1zjf07s8apavhvn04204qjigjiz5w6iscvw3";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "serde" = rec {
+        crateName = "serde";
+        version = "1.0.192";
+        edition = "2018";
+        sha256 = "00ghhaabyrnr2cn504lckyqzh3fwr8k7pxnhhardr1djhj2a18mw";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            optional = true;
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "derive" "serde_derive" "std" ];
+      };
+      "serde_derive" = rec {
+        crateName = "serde_derive";
+        version = "1.0.192";
+        edition = "2015";
+        sha256 = "1hgvm47ffd748sx22z1da7mgcfjmpr60gqzkff0a9yn9przj1iyn";
+        procMacro = true;
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "serde_json" = rec {
+        crateName = "serde_json";
+        version = "1.0.108";
+        edition = "2021";
+        sha256 = "0ssj59s7lpzqh1m50kfzlnrip0p0jg9lmhn4098i33a0mhz7w71x";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" ];
+          "default" = [ "std" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "preserve_order" = [ "indexmap" "std" ];
+          "std" = [ "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "serde_path_to_error" = rec {
+        crateName = "serde_path_to_error";
+        version = "0.1.14";
+        edition = "2021";
+        sha256 = "0dc31z4bg0jwn69gcqsczbmcy5y4w6r0vdcc4c38vma9x2ycivjb";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+        ];
+
+      };
+      "sha2" = rec {
+        crateName = "sha2";
+        version = "0.9.9";
+        edition = "2018";
+        sha256 = "006q2f0ar26xcjxqz8zsncfgz86zqa5dkwlwv03rhx1rpzhs2n2d";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+          {
+            name = "opaque-debug";
+            packageId = "opaque-debug";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha2-asm" ];
+          "asm-aarch64" = [ "asm" ];
+          "default" = [ "std" ];
+          "sha2-asm" = [ "dep:sha2-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sharded-slab" = rec {
+        crateName = "sharded-slab";
+        version = "0.1.7";
+        edition = "2018";
+        sha256 = "1xipjr4nqsgw34k7a2cgj9zaasl2ds6jwn89886kww93d32a637l";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+        ];
+        dependencies = [
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+        ];
+        features = {
+          "loom" = [ "dep:loom" ];
+        };
+      };
+      "shlex" = rec {
+        crateName = "shlex";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "1033pj9dyb76nm5yv597nnvj3zpvr2aw9rm5wy0gah3dk99f1km7";
+        authors = [
+          "comex <comexk@gmail.com>"
+          "Fenhl <fenhl@fenhl.net>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "signal-hook-registry" = rec {
+        crateName = "signal-hook-registry";
+        version = "1.4.1";
+        edition = "2015";
+        sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        authors = [
+          "Michal 'vorner' Vaner <vorner@vorner.cz>"
+          "Masaki Hara <ackie.h.gmai@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "slab" = rec {
+        crateName = "slab";
+        version = "0.4.9";
+        edition = "2018";
+        sha256 = "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "smallvec" = rec {
+        crateName = "smallvec";
+        version = "1.11.2";
+        edition = "2018";
+        sha256 = "0w79x38f7c0np7hqfmzrif9zmn0avjvvm31b166zdk9d1aad1k2d";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "const_new" = [ "const_generics" ];
+          "drain_keep_rest" = [ "drain_filter" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "socket2 0.4.10" = rec {
+        crateName = "socket2";
+        version = "0.4.10";
+        edition = "2018";
+        sha256 = "03ack54dxhgfifzsj14k7qa3r5c9wqy3v6mqhlim99cc03y1cycz";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "handleapi" "ws2ipdef" "ws2tcpip" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "all" ];
+      };
+      "socket2 0.5.5" = rec {
+        crateName = "socket2";
+        version = "0.5.5";
+        edition = "2021";
+        sha256 = "1sgq315f1njky114ip7wcy83qlphv9qclprfjwvxcpfblmcsqpvv";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "all" ];
+      };
+      "spin 0.5.2" = rec {
+        crateName = "spin";
+        version = "0.5.2";
+        edition = "2015";
+        sha256 = "0b84m6dbzrwf2kxylnw82d3dr8w06av7rfkr8s85fb5f43rwyqvf";
+        authors = [
+          "Mathijs van de Nes <git@mathijs.vd-nes.nl>"
+          "John Ericson <git@JohnEricson.me>"
+        ];
+
+      };
+      "spin 0.9.8" = rec {
+        crateName = "spin";
+        version = "0.9.8";
+        edition = "2015";
+        sha256 = "0rvam5r0p3a6qhc18scqpvpgb3ckzyqxpgdfyjnghh8ja7byi039";
+        authors = [
+          "Mathijs van de Nes <git@mathijs.vd-nes.nl>"
+          "John Ericson <git@JohnEricson.me>"
+          "Joshua Barretto <joshua.s.barretto@gmail.com>"
+        ];
+        features = {
+          "barrier" = [ "mutex" ];
+          "default" = [ "lock_api" "mutex" "spin_mutex" "rwlock" "once" "lazy" "barrier" ];
+          "fair_mutex" = [ "mutex" ];
+          "lazy" = [ "once" ];
+          "lock_api" = [ "lock_api_crate" ];
+          "lock_api_crate" = [ "dep:lock_api_crate" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "portable_atomic" = [ "portable-atomic" ];
+          "spin_mutex" = [ "mutex" ];
+          "ticket_mutex" = [ "mutex" ];
+          "use_ticket_mutex" = [ "mutex" "ticket_mutex" ];
+        };
+        resolvedDefaultFeatures = [ "once" ];
+      };
+      "subtle" = rec {
+        crateName = "subtle";
+        version = "2.4.1";
+        edition = "2015";
+        sha256 = "00b6jzh9gzb0h9n25g06nqr90z3xzqppfhhb260s1hjhh4pg7pkb";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        features = {
+          "default" = [ "std" "i128" ];
+        };
+      };
+      "syn" = rec {
+        crateName = "syn";
+        version = "2.0.39";
+        edition = "2021";
+        sha256 = "0ymyhxnk1yi4pzf72qk3lrdm9lgjwcrcwci0hhz5vx7wya88prr3";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit" "visit-mut" ];
+      };
+      "thiserror" = rec {
+        crateName = "thiserror";
+        version = "1.0.50";
+        edition = "2021";
+        sha256 = "1ll2sfbrxks8jja161zh1pgm3yssr7aawdmaa2xmcwcsbh7j39zr";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "thiserror-impl";
+            packageId = "thiserror-impl";
+          }
+        ];
+
+      };
+      "thiserror-impl" = rec {
+        crateName = "thiserror-impl";
+        version = "1.0.50";
+        edition = "2021";
+        sha256 = "1f0lmam4765sfnwr4b1n00y14vxh10g0311mkk0adr80pi02wsr6";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+          }
+        ];
+
+      };
+      "thread_local" = rec {
+        crateName = "thread_local";
+        version = "1.1.7";
+        edition = "2021";
+        sha256 = "0lp19jdgvp5m4l60cgxdnl00yw1hlqy8gcywg9bddwng9h36zp9z";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+        ];
+        features = { };
+      };
+      "tokio" = rec {
+        crateName = "tokio";
+        version = "1.34.0";
+        edition = "2021";
+        sha256 = "1fgmssdga42a2hn9spm9dh1v9ajpcbs4r3svmzvk9s0iciv19h6h";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            target = { target, features }: (target."tokio_taskdump" or false);
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+            optional = true;
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "signal-hook-registry";
+            packageId = "signal-hook-registry";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2 0.5.5";
+            optional = true;
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+            features = [ "all" ];
+          }
+          {
+            name = "tokio-macros";
+            packageId = "tokio-macros";
+            optional = true;
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2 0.5.5";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Security_Authorization" ];
+          }
+        ];
+        features = {
+          "bytes" = [ "dep:bytes" ];
+          "full" = [ "fs" "io-util" "io-std" "macros" "net" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "sync" "time" ];
+          "io-util" = [ "bytes" ];
+          "libc" = [ "dep:libc" ];
+          "macros" = [ "tokio-macros" ];
+          "mio" = [ "dep:mio" ];
+          "net" = [ "libc" "mio/os-poll" "mio/os-ext" "mio/net" "socket2" "windows-sys/Win32_Foundation" "windows-sys/Win32_Security" "windows-sys/Win32_Storage_FileSystem" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_System_SystemServices" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "process" = [ "bytes" "libc" "mio/os-poll" "mio/os-ext" "mio/net" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Threading" "windows-sys/Win32_System_WindowsProgramming" ];
+          "rt-multi-thread" = [ "num_cpus" "rt" ];
+          "signal" = [ "libc" "mio/os-poll" "mio/net" "mio/os-ext" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Console" ];
+          "signal-hook-registry" = [ "dep:signal-hook-registry" ];
+          "socket2" = [ "dep:socket2" ];
+          "test-util" = [ "rt" "sync" "time" ];
+          "tokio-macros" = [ "dep:tokio-macros" ];
+          "tracing" = [ "dep:tracing" ];
+          "windows-sys" = [ "dep:windows-sys" ];
+        };
+        resolvedDefaultFeatures = [ "bytes" "default" "fs" "full" "io-std" "io-util" "libc" "macros" "mio" "net" "num_cpus" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "time" "tokio-macros" "windows-sys" ];
+      };
+      "tokio-macros" = rec {
+        crateName = "tokio-macros";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-rustls" = rec {
+        crateName = "tokio-rustls";
+        version = "0.23.4";
+        edition = "2018";
+        sha256 = "0nfsmmi8l1lgpbfy6079d5i13984djzcxrdr9jc06ghi0cwyhgn4";
+        authors = [
+          "quininer kel <quininer@live.com>"
+        ];
+        dependencies = [
+          {
+            name = "rustls";
+            packageId = "rustls";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+          }
+          {
+            name = "webpki";
+            packageId = "webpki";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "dangerous_configuration" = [ "rustls/dangerous_configuration" ];
+          "default" = [ "logging" "tls12" ];
+          "logging" = [ "rustls/logging" ];
+          "tls12" = [ "rustls/tls12" ];
+        };
+        resolvedDefaultFeatures = [ "logging" "tls12" ];
+      };
+      "tokio-stream" = rec {
+        crateName = "tokio-stream";
+        version = "0.1.14";
+        edition = "2021";
+        sha256 = "0hi8hcwavh5sdi1ivc9qc4yvyr32f153c212dpd7sb366y6rhz1r";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" "test-util" ];
+          }
+        ];
+        features = {
+          "default" = [ "time" ];
+          "fs" = [ "tokio/fs" ];
+          "full" = [ "time" "net" "io-util" "fs" "sync" "signal" ];
+          "io-util" = [ "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "signal" = [ "tokio/signal" ];
+          "sync" = [ "tokio/sync" "tokio-util" ];
+          "time" = [ "tokio/time" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+        };
+        resolvedDefaultFeatures = [ "default" "time" ];
+      };
+      "tokio-util" = rec {
+        crateName = "tokio-util";
+        version = "0.7.10";
+        edition = "2021";
+        sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "__docs_rs" = [ "futures-util" ];
+          "codec" = [ "tracing" ];
+          "compat" = [ "futures-io" ];
+          "full" = [ "codec" "compat" "io-util" "time" "net" "rt" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hashbrown" = [ "dep:hashbrown" ];
+          "io-util" = [ "io" "tokio/rt" "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "rt" = [ "tokio/rt" "tokio/sync" "futures-util" "hashbrown" ];
+          "slab" = [ "dep:slab" ];
+          "time" = [ "tokio/time" "slab" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "codec" "default" "tracing" ];
+      };
+      "tower" = rec {
+        crateName = "tower";
+        version = "0.4.13";
+        edition = "2018";
+        sha256 = "073wncyqav4sak1p755hf6vl66njgfc1z1g1di9rxx3cvvh9pymq";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            optional = true;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+        features = {
+          "__common" = [ "futures-core" "pin-project-lite" ];
+          "balance" = [ "discover" "load" "ready-cache" "make" "rand" "slab" ];
+          "buffer" = [ "__common" "tokio/sync" "tokio/rt" "tokio-util" "tracing" ];
+          "default" = [ "log" ];
+          "discover" = [ "__common" ];
+          "filter" = [ "__common" "futures-util" ];
+          "full" = [ "balance" "buffer" "discover" "filter" "hedge" "limit" "load" "load-shed" "make" "ready-cache" "reconnect" "retry" "spawn-ready" "steer" "timeout" "util" ];
+          "futures-core" = [ "dep:futures-core" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hdrhistogram" = [ "dep:hdrhistogram" ];
+          "hedge" = [ "util" "filter" "futures-util" "hdrhistogram" "tokio/time" "tracing" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "limit" = [ "__common" "tokio/time" "tokio/sync" "tokio-util" "tracing" ];
+          "load" = [ "__common" "tokio/time" "tracing" ];
+          "load-shed" = [ "__common" ];
+          "log" = [ "tracing/log" ];
+          "make" = [ "futures-util" "pin-project-lite" "tokio/io-std" ];
+          "pin-project" = [ "dep:pin-project" ];
+          "pin-project-lite" = [ "dep:pin-project-lite" ];
+          "rand" = [ "dep:rand" ];
+          "ready-cache" = [ "futures-core" "futures-util" "indexmap" "tokio/sync" "tracing" "pin-project-lite" ];
+          "reconnect" = [ "make" "tokio/io-std" "tracing" ];
+          "retry" = [ "__common" "tokio/time" ];
+          "slab" = [ "dep:slab" ];
+          "spawn-ready" = [ "__common" "futures-util" "tokio/sync" "tokio/rt" "util" "tracing" ];
+          "timeout" = [ "pin-project-lite" "tokio/time" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-stream" = [ "dep:tokio-stream" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "tracing" = [ "dep:tracing" ];
+          "util" = [ "__common" "futures-util" "pin-project" ];
+        };
+        resolvedDefaultFeatures = [ "__common" "default" "futures-core" "futures-util" "log" "pin-project" "pin-project-lite" "tracing" "util" ];
+      };
+      "tower-layer" = rec {
+        crateName = "tower-layer";
+        version = "0.3.2";
+        edition = "2018";
+        sha256 = "1l7i17k9vlssrdg4s3b0ia5jjkmmxsvv8s9y9ih0jfi8ssz8s362";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+
+      };
+      "tower-service" = rec {
+        crateName = "tower-service";
+        version = "0.3.2";
+        edition = "2018";
+        sha256 = "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+
+      };
+      "tracing" = rec {
+        crateName = "tracing";
+        version = "0.1.40";
+        edition = "2018";
+        sha256 = "1vv48dac9zgj9650pg2b4d0j3w6f3x9gbggf43scq5hrlysklln3";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tracing-attributes";
+            packageId = "tracing-attributes";
+            optional = true;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "attributes" = [ "tracing-attributes" ];
+          "default" = [ "std" "attributes" ];
+          "log" = [ "dep:log" ];
+          "log-always" = [ "log" ];
+          "std" = [ "tracing-core/std" ];
+          "tracing-attributes" = [ "dep:tracing-attributes" ];
+          "valuable" = [ "tracing-core/valuable" ];
+        };
+        resolvedDefaultFeatures = [ "attributes" "default" "log" "std" "tracing-attributes" ];
+      };
+      "tracing-attributes" = rec {
+        crateName = "tracing-attributes";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <dbarsky@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            usesDefaultFeatures = false;
+            features = [ "full" "parsing" "printing" "visit-mut" "clone-impls" "extra-traits" "proc-macro" ];
+          }
+        ];
+        features = { };
+      };
+      "tracing-core" = rec {
+        crateName = "tracing-core";
+        version = "0.1.32";
+        edition = "2018";
+        sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+          }
+          {
+            name = "valuable";
+            packageId = "valuable";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."tracing_unstable" or false);
+          }
+        ];
+        features = {
+          "default" = [ "std" "valuable/std" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "std" = [ "once_cell" ];
+          "valuable" = [ "dep:valuable" ];
+        };
+        resolvedDefaultFeatures = [ "default" "once_cell" "std" "valuable" ];
+      };
+      "tracing-log" = rec {
+        crateName = "tracing-log";
+        version = "0.1.4";
+        edition = "2018";
+        sha256 = "1wmxawaz94sk52i4vs2wg5d5clyks972rqskrvc93rxl14ki2lgp";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+          }
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "default" = [ "log-tracer" "trace-logger" "std" ];
+          "env_logger" = [ "dep:env_logger" ];
+          "interest-cache" = [ "lru" "ahash" ];
+          "lru" = [ "dep:lru" ];
+          "std" = [ "log/std" ];
+        };
+        resolvedDefaultFeatures = [ "log-tracer" "std" ];
+      };
+      "tracing-serde" = rec {
+        crateName = "tracing-serde";
+        version = "0.1.3";
+        edition = "2018";
+        sha256 = "1qfr0va69djvxqvjrx4vqq7p6myy414lx4w1f6amcn0hfwqj2sxw";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+          }
+        ];
+        features = {
+          "valuable" = [ "valuable_crate" "valuable-serde" "tracing-core/valuable" ];
+          "valuable-serde" = [ "dep:valuable-serde" ];
+          "valuable_crate" = [ "dep:valuable_crate" ];
+        };
+      };
+      "tracing-subscriber" = rec {
+        crateName = "tracing-subscriber";
+        version = "0.3.17";
+        edition = "2018";
+        sha256 = "0xvwfpmb943hdy4gzyn7a2azgigf30mfd1kx10gyh5gr6yy539ih";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <me@davidbarsky.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "nu-ansi-term";
+            packageId = "nu-ansi-term";
+            optional = true;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+          }
+          {
+            name = "sharded-slab";
+            packageId = "sharded-slab";
+            optional = true;
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+            optional = true;
+          }
+          {
+            name = "thread_local";
+            packageId = "thread_local";
+            optional = true;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tracing-log";
+            packageId = "tracing-log";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "log-tracer" "std" ];
+          }
+          {
+            name = "tracing-serde";
+            packageId = "tracing-serde";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tracing-log";
+            packageId = "tracing-log";
+          }
+        ];
+        features = {
+          "ansi" = [ "fmt" "nu-ansi-term" ];
+          "default" = [ "smallvec" "fmt" "ansi" "tracing-log" "std" ];
+          "env-filter" = [ "matchers" "regex" "once_cell" "tracing" "std" "thread_local" ];
+          "fmt" = [ "registry" "std" ];
+          "json" = [ "tracing-serde" "serde" "serde_json" ];
+          "local-time" = [ "time/local-offset" ];
+          "matchers" = [ "dep:matchers" ];
+          "nu-ansi-term" = [ "dep:nu-ansi-term" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "regex" = [ "dep:regex" ];
+          "registry" = [ "sharded-slab" "thread_local" "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "sharded-slab" = [ "dep:sharded-slab" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "alloc" "tracing-core/std" ];
+          "thread_local" = [ "dep:thread_local" ];
+          "time" = [ "dep:time" ];
+          "tracing" = [ "dep:tracing" ];
+          "tracing-log" = [ "dep:tracing-log" ];
+          "tracing-serde" = [ "dep:tracing-serde" ];
+          "valuable" = [ "tracing-core/valuable" "valuable_crate" "valuable-serde" "tracing-serde/valuable" ];
+          "valuable-serde" = [ "dep:valuable-serde" ];
+          "valuable_crate" = [ "dep:valuable_crate" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "ansi" "default" "fmt" "json" "nu-ansi-term" "registry" "serde" "serde_json" "sharded-slab" "smallvec" "std" "thread_local" "tracing-log" "tracing-serde" ];
+      };
+      "try-lock" = rec {
+        crateName = "try-lock";
+        version = "0.2.4";
+        edition = "2015";
+        sha256 = "1vc15paa4zi06ixsxihwbvfn24d708nsyg1ncgqwcrn42byyqa1m";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+
+      };
+      "turbofetch" = rec {
+        crateName = "turbofetch";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "turbofetch";
+            path = "src/main.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./.; }
+          else ./.;
+        dependencies = [
+          {
+            name = "aws_lambda_events";
+            packageId = "aws_lambda_events";
+            usesDefaultFeatures = false;
+            features = [ "lambda_function_urls" ];
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "httparse";
+            packageId = "httparse";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "lambda_runtime";
+            packageId = "lambda_runtime";
+          }
+          {
+            name = "magic-buffer";
+            packageId = "magic-buffer";
+          }
+          {
+            name = "rusoto_core";
+            packageId = "rusoto_core";
+            usesDefaultFeatures = false;
+            features = [ "rustls" ];
+          }
+          {
+            name = "rusoto_s3";
+            packageId = "rusoto_s3";
+            usesDefaultFeatures = false;
+            features = [ "rustls" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "tracing-subscriber";
+            packageId = "tracing-subscriber";
+            features = [ "json" ];
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+          }
+        ];
+
+      };
+      "typenum" = rec {
+        crateName = "typenum";
+        version = "1.17.0";
+        edition = "2018";
+        sha256 = "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2";
+        build = "build/main.rs";
+        authors = [
+          "Paho Lurie-Gregg <paho@paholg.com>"
+          "Andre Bogus <bogusandre@gmail.com>"
+        ];
+        features = {
+          "scale-info" = [ "dep:scale-info" ];
+          "scale_info" = [ "scale-info/derive" ];
+        };
+      };
+      "unicode-ident" = rec {
+        crateName = "unicode-ident";
+        version = "1.0.12";
+        edition = "2018";
+        sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "untrusted 0.7.1" = rec {
+        crateName = "untrusted";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "0jkbqaj9d3v5a91pp3wp9mffvng1nhycx6sh4qkdd9qyr62ccmm1";
+        libPath = "src/untrusted.rs";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+
+      };
+      "untrusted 0.9.0" = rec {
+        crateName = "untrusted";
+        version = "0.9.0";
+        edition = "2018";
+        sha256 = "1ha7ib98vkc538x0z60gfn0fc5whqdd85mb87dvisdcaifi6vjwf";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+
+      };
+      "valuable" = rec {
+        crateName = "valuable";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "0v9gp3nkjbl30z0fd56d8mx7w1csk86wwjhfjhr400wh9mfpw2w3";
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "valuable-derive" ];
+          "std" = [ "alloc" ];
+          "valuable-derive" = [ "dep:valuable-derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "version_check" = rec {
+        crateName = "version_check";
+        version = "0.9.4";
+        edition = "2015";
+        sha256 = "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9";
+        authors = [
+          "Sergio Benitez <sb@sergio.bz>"
+        ];
+
+      };
+      "want" = rec {
+        crateName = "want";
+        version = "0.3.1";
+        edition = "2018";
+        sha256 = "03hbfrnvqqdchb5kgxyavb9jabwza0dmh2vw5kg0dq8rxl57d9xz";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "try-lock";
+            packageId = "try-lock";
+          }
+        ];
+
+      };
+      "wasi" = rec {
+        crateName = "wasi";
+        version = "0.11.0+wasi-snapshot-preview1";
+        edition = "2018";
+        sha256 = "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw";
+        authors = [
+          "The Cranelift Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "rustc-std-workspace-alloc" ];
+          "rustc-std-workspace-alloc" = [ "dep:rustc-std-workspace-alloc" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "wasm-bindgen" = rec {
+        crateName = "wasm-bindgen";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "1khgsh4z9bga35mjhg41dl7523i69ffc5m8ckhqaw6ssyabc5bkx";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "wasm-bindgen-macro";
+            packageId = "wasm-bindgen-macro";
+          }
+        ];
+        features = {
+          "default" = [ "spans" "std" ];
+          "enable-interning" = [ "std" ];
+          "gg-alloc" = [ "wasm-bindgen-test/gg-alloc" ];
+          "serde" = [ "dep:serde" ];
+          "serde-serialize" = [ "serde" "serde_json" "std" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "spans" = [ "wasm-bindgen-macro/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro/strict-macro" ];
+          "xxx_debug_only_print_generated_code" = [ "wasm-bindgen-macro/xxx_debug_only_print_generated_code" ];
+        };
+        resolvedDefaultFeatures = [ "default" "spans" "std" ];
+      };
+      "wasm-bindgen-backend" = rec {
+        crateName = "wasm-bindgen-backend";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "05zj8yl243rvs87rhicq2l1d6443lnm6k90khf744khf9ikg95z3";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "bumpalo";
+            packageId = "bumpalo";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro" = rec {
+        crateName = "wasm-bindgen-macro";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "1chn3wgw9awmvs0fpmazbqyc5rwfgy3pj7lzwczmzb887dxh2qar";
+        procMacro = true;
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "wasm-bindgen-macro-support";
+            packageId = "wasm-bindgen-macro-support";
+          }
+        ];
+        features = {
+          "spans" = [ "wasm-bindgen-macro-support/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro-support/strict-macro" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro-support" = rec {
+        crateName = "wasm-bindgen-macro-support";
+        version = "0.2.88";
+        edition = "2018";
+        sha256 = "01rrzg3y1apqygsjz1jg0n7p831nm4kdyxmxyl85x7v6mf6kndf5";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "visit" "full" ];
+          }
+          {
+            name = "wasm-bindgen-backend";
+            packageId = "wasm-bindgen-backend";
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+          "spans" = [ "wasm-bindgen-backend/spans" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-shared" = rec {
+        crateName = "wasm-bindgen-shared";
+        version = "0.2.88";
+        edition = "2018";
+        links = "wasm_bindgen";
+        sha256 = "02vmw2rzsla1qm0zgfng4kqz52xn8k54v8ads4g1macv09fnq10d";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+
+      };
+      "web-sys" = rec {
+        crateName = "web-sys";
+        version = "0.3.65";
+        edition = "2018";
+        sha256 = "11ba406ca9qssc21c37v49sn2y2gsdn6c3nva4hjf8v3yv2rkd2x";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+        ];
+        features = {
+          "AbortSignal" = [ "EventTarget" ];
+          "AnalyserNode" = [ "AudioNode" "EventTarget" ];
+          "Animation" = [ "EventTarget" ];
+          "AnimationEvent" = [ "Event" ];
+          "AnimationPlaybackEvent" = [ "Event" ];
+          "Attr" = [ "EventTarget" "Node" ];
+          "AudioBufferSourceNode" = [ "AudioNode" "AudioScheduledSourceNode" "EventTarget" ];
+          "AudioContext" = [ "BaseAudioContext" "EventTarget" ];
+          "AudioDestinationNode" = [ "AudioNode" "EventTarget" ];
+          "AudioNode" = [ "EventTarget" ];
+          "AudioProcessingEvent" = [ "Event" ];
+          "AudioScheduledSourceNode" = [ "AudioNode" "EventTarget" ];
+          "AudioStreamTrack" = [ "EventTarget" "MediaStreamTrack" ];
+          "AudioTrackList" = [ "EventTarget" ];
+          "AudioWorklet" = [ "Worklet" ];
+          "AudioWorkletGlobalScope" = [ "WorkletGlobalScope" ];
+          "AudioWorkletNode" = [ "AudioNode" "EventTarget" ];
+          "AuthenticatorAssertionResponse" = [ "AuthenticatorResponse" ];
+          "AuthenticatorAttestationResponse" = [ "AuthenticatorResponse" ];
+          "BaseAudioContext" = [ "EventTarget" ];
+          "BatteryManager" = [ "EventTarget" ];
+          "BeforeUnloadEvent" = [ "Event" ];
+          "BiquadFilterNode" = [ "AudioNode" "EventTarget" ];
+          "BlobEvent" = [ "Event" ];
+          "Bluetooth" = [ "EventTarget" ];
+          "BluetoothAdvertisingEvent" = [ "Event" ];
+          "BluetoothDevice" = [ "EventTarget" ];
+          "BluetoothPermissionResult" = [ "EventTarget" "PermissionStatus" ];
+          "BluetoothRemoteGattCharacteristic" = [ "EventTarget" ];
+          "BluetoothRemoteGattService" = [ "EventTarget" ];
+          "BroadcastChannel" = [ "EventTarget" ];
+          "CanvasCaptureMediaStream" = [ "EventTarget" "MediaStream" ];
+          "CanvasCaptureMediaStreamTrack" = [ "EventTarget" "MediaStreamTrack" ];
+          "CdataSection" = [ "CharacterData" "EventTarget" "Node" "Text" ];
+          "ChannelMergerNode" = [ "AudioNode" "EventTarget" ];
+          "ChannelSplitterNode" = [ "AudioNode" "EventTarget" ];
+          "CharacterData" = [ "EventTarget" "Node" ];
+          "ChromeWorker" = [ "EventTarget" "Worker" ];
+          "Clipboard" = [ "EventTarget" ];
+          "ClipboardEvent" = [ "Event" ];
+          "CloseEvent" = [ "Event" ];
+          "Comment" = [ "CharacterData" "EventTarget" "Node" ];
+          "CompositionEvent" = [ "Event" "UiEvent" ];
+          "ConstantSourceNode" = [ "AudioNode" "AudioScheduledSourceNode" "EventTarget" ];
+          "ConvolverNode" = [ "AudioNode" "EventTarget" ];
+          "CssAnimation" = [ "Animation" "EventTarget" ];
+          "CssConditionRule" = [ "CssGroupingRule" "CssRule" ];
+          "CssCounterStyleRule" = [ "CssRule" ];
+          "CssFontFaceRule" = [ "CssRule" ];
+          "CssFontFeatureValuesRule" = [ "CssRule" ];
+          "CssGroupingRule" = [ "CssRule" ];
+          "CssImportRule" = [ "CssRule" ];
+          "CssKeyframeRule" = [ "CssRule" ];
+          "CssKeyframesRule" = [ "CssRule" ];
+          "CssMediaRule" = [ "CssConditionRule" "CssGroupingRule" "CssRule" ];
+          "CssNamespaceRule" = [ "CssRule" ];
+          "CssPageRule" = [ "CssRule" ];
+          "CssStyleRule" = [ "CssRule" ];
+          "CssStyleSheet" = [ "StyleSheet" ];
+          "CssSupportsRule" = [ "CssConditionRule" "CssGroupingRule" "CssRule" ];
+          "CssTransition" = [ "Animation" "EventTarget" ];
+          "CustomEvent" = [ "Event" ];
+          "DedicatedWorkerGlobalScope" = [ "EventTarget" "WorkerGlobalScope" ];
+          "DelayNode" = [ "AudioNode" "EventTarget" ];
+          "DeviceLightEvent" = [ "Event" ];
+          "DeviceMotionEvent" = [ "Event" ];
+          "DeviceOrientationEvent" = [ "Event" ];
+          "DeviceProximityEvent" = [ "Event" ];
+          "Document" = [ "EventTarget" "Node" ];
+          "DocumentFragment" = [ "EventTarget" "Node" ];
+          "DocumentTimeline" = [ "AnimationTimeline" ];
+          "DocumentType" = [ "EventTarget" "Node" ];
+          "DomMatrix" = [ "DomMatrixReadOnly" ];
+          "DomPoint" = [ "DomPointReadOnly" ];
+          "DomRect" = [ "DomRectReadOnly" ];
+          "DomRequest" = [ "EventTarget" ];
+          "DragEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "DynamicsCompressorNode" = [ "AudioNode" "EventTarget" ];
+          "Element" = [ "EventTarget" "Node" ];
+          "ErrorEvent" = [ "Event" ];
+          "EventSource" = [ "EventTarget" ];
+          "ExtendableEvent" = [ "Event" ];
+          "ExtendableMessageEvent" = [ "Event" "ExtendableEvent" ];
+          "FetchEvent" = [ "Event" "ExtendableEvent" ];
+          "FetchObserver" = [ "EventTarget" ];
+          "File" = [ "Blob" ];
+          "FileReader" = [ "EventTarget" ];
+          "FileSystemDirectoryEntry" = [ "FileSystemEntry" ];
+          "FileSystemDirectoryHandle" = [ "FileSystemHandle" ];
+          "FileSystemFileEntry" = [ "FileSystemEntry" ];
+          "FileSystemFileHandle" = [ "FileSystemHandle" ];
+          "FileSystemWritableFileStream" = [ "WritableStream" ];
+          "FocusEvent" = [ "Event" "UiEvent" ];
+          "FontFaceSet" = [ "EventTarget" ];
+          "FontFaceSetLoadEvent" = [ "Event" ];
+          "GainNode" = [ "AudioNode" "EventTarget" ];
+          "GamepadAxisMoveEvent" = [ "Event" "GamepadEvent" ];
+          "GamepadButtonEvent" = [ "Event" "GamepadEvent" ];
+          "GamepadEvent" = [ "Event" ];
+          "GpuDevice" = [ "EventTarget" ];
+          "GpuInternalError" = [ "GpuError" ];
+          "GpuOutOfMemoryError" = [ "GpuError" ];
+          "GpuPipelineError" = [ "DomException" ];
+          "GpuUncapturedErrorEvent" = [ "Event" ];
+          "GpuValidationError" = [ "GpuError" ];
+          "HashChangeEvent" = [ "Event" ];
+          "Hid" = [ "EventTarget" ];
+          "HidConnectionEvent" = [ "Event" ];
+          "HidDevice" = [ "EventTarget" ];
+          "HidInputReportEvent" = [ "Event" ];
+          "HtmlAnchorElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlAreaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlAudioElement" = [ "Element" "EventTarget" "HtmlElement" "HtmlMediaElement" "Node" ];
+          "HtmlBaseElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlBodyElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlBrElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlButtonElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlCanvasElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDataElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDataListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDetailsElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDialogElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDirectoryElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDivElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlDocument" = [ "Document" "EventTarget" "Node" ];
+          "HtmlElement" = [ "Element" "EventTarget" "Node" ];
+          "HtmlEmbedElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFieldSetElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFontElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFormControlsCollection" = [ "HtmlCollection" ];
+          "HtmlFormElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFrameElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlFrameSetElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHeadElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHeadingElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHrElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlHtmlElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlIFrameElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlImageElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlInputElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLabelElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLegendElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLiElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlLinkElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMapElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMediaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMenuElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMenuItemElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMetaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlMeterElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlModElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlObjectElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOptGroupElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOptionElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlOptionsCollection" = [ "HtmlCollection" ];
+          "HtmlOutputElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlParagraphElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlParamElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlPictureElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlPreElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlProgressElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlQuoteElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlScriptElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSelectElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSlotElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSourceElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlSpanElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlStyleElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableCaptionElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableCellElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableColElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableRowElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTableSectionElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTemplateElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTextAreaElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTimeElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTitleElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlTrackElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlUListElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlUnknownElement" = [ "Element" "EventTarget" "HtmlElement" "Node" ];
+          "HtmlVideoElement" = [ "Element" "EventTarget" "HtmlElement" "HtmlMediaElement" "Node" ];
+          "IdbCursorWithValue" = [ "IdbCursor" ];
+          "IdbDatabase" = [ "EventTarget" ];
+          "IdbFileHandle" = [ "EventTarget" ];
+          "IdbFileRequest" = [ "DomRequest" "EventTarget" ];
+          "IdbLocaleAwareKeyRange" = [ "IdbKeyRange" ];
+          "IdbMutableFile" = [ "EventTarget" ];
+          "IdbOpenDbRequest" = [ "EventTarget" "IdbRequest" ];
+          "IdbRequest" = [ "EventTarget" ];
+          "IdbTransaction" = [ "EventTarget" ];
+          "IdbVersionChangeEvent" = [ "Event" ];
+          "IirFilterNode" = [ "AudioNode" "EventTarget" ];
+          "ImageCaptureErrorEvent" = [ "Event" ];
+          "ImageTrack" = [ "EventTarget" ];
+          "InputEvent" = [ "Event" "UiEvent" ];
+          "KeyboardEvent" = [ "Event" "UiEvent" ];
+          "KeyframeEffect" = [ "AnimationEffect" ];
+          "LocalMediaStream" = [ "EventTarget" "MediaStream" ];
+          "MediaDevices" = [ "EventTarget" ];
+          "MediaElementAudioSourceNode" = [ "AudioNode" "EventTarget" ];
+          "MediaEncryptedEvent" = [ "Event" ];
+          "MediaKeyError" = [ "Event" ];
+          "MediaKeyMessageEvent" = [ "Event" ];
+          "MediaKeySession" = [ "EventTarget" ];
+          "MediaQueryList" = [ "EventTarget" ];
+          "MediaQueryListEvent" = [ "Event" ];
+          "MediaRecorder" = [ "EventTarget" ];
+          "MediaRecorderErrorEvent" = [ "Event" ];
+          "MediaSource" = [ "EventTarget" ];
+          "MediaStream" = [ "EventTarget" ];
+          "MediaStreamAudioDestinationNode" = [ "AudioNode" "EventTarget" ];
+          "MediaStreamAudioSourceNode" = [ "AudioNode" "EventTarget" ];
+          "MediaStreamEvent" = [ "Event" ];
+          "MediaStreamTrack" = [ "EventTarget" ];
+          "MediaStreamTrackEvent" = [ "Event" ];
+          "MediaStreamTrackGenerator" = [ "EventTarget" "MediaStreamTrack" ];
+          "MessageEvent" = [ "Event" ];
+          "MessagePort" = [ "EventTarget" ];
+          "MidiAccess" = [ "EventTarget" ];
+          "MidiConnectionEvent" = [ "Event" ];
+          "MidiInput" = [ "EventTarget" "MidiPort" ];
+          "MidiMessageEvent" = [ "Event" ];
+          "MidiOutput" = [ "EventTarget" "MidiPort" ];
+          "MidiPort" = [ "EventTarget" ];
+          "MouseEvent" = [ "Event" "UiEvent" ];
+          "MouseScrollEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "MutationEvent" = [ "Event" ];
+          "NetworkInformation" = [ "EventTarget" ];
+          "Node" = [ "EventTarget" ];
+          "Notification" = [ "EventTarget" ];
+          "NotificationEvent" = [ "Event" "ExtendableEvent" ];
+          "OfflineAudioCompletionEvent" = [ "Event" ];
+          "OfflineAudioContext" = [ "BaseAudioContext" "EventTarget" ];
+          "OfflineResourceList" = [ "EventTarget" ];
+          "OffscreenCanvas" = [ "EventTarget" ];
+          "OscillatorNode" = [ "AudioNode" "AudioScheduledSourceNode" "EventTarget" ];
+          "PageTransitionEvent" = [ "Event" ];
+          "PaintWorkletGlobalScope" = [ "WorkletGlobalScope" ];
+          "PannerNode" = [ "AudioNode" "EventTarget" ];
+          "PaymentMethodChangeEvent" = [ "Event" "PaymentRequestUpdateEvent" ];
+          "PaymentRequestUpdateEvent" = [ "Event" ];
+          "Performance" = [ "EventTarget" ];
+          "PerformanceMark" = [ "PerformanceEntry" ];
+          "PerformanceMeasure" = [ "PerformanceEntry" ];
+          "PerformanceNavigationTiming" = [ "PerformanceEntry" "PerformanceResourceTiming" ];
+          "PerformanceResourceTiming" = [ "PerformanceEntry" ];
+          "PermissionStatus" = [ "EventTarget" ];
+          "PointerEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "PopStateEvent" = [ "Event" ];
+          "PopupBlockedEvent" = [ "Event" ];
+          "PresentationAvailability" = [ "EventTarget" ];
+          "PresentationConnection" = [ "EventTarget" ];
+          "PresentationConnectionAvailableEvent" = [ "Event" ];
+          "PresentationConnectionCloseEvent" = [ "Event" ];
+          "PresentationConnectionList" = [ "EventTarget" ];
+          "PresentationRequest" = [ "EventTarget" ];
+          "ProcessingInstruction" = [ "CharacterData" "EventTarget" "Node" ];
+          "ProgressEvent" = [ "Event" ];
+          "PromiseRejectionEvent" = [ "Event" ];
+          "PublicKeyCredential" = [ "Credential" ];
+          "PushEvent" = [ "Event" "ExtendableEvent" ];
+          "RadioNodeList" = [ "NodeList" ];
+          "RtcDataChannel" = [ "EventTarget" ];
+          "RtcDataChannelEvent" = [ "Event" ];
+          "RtcPeerConnection" = [ "EventTarget" ];
+          "RtcPeerConnectionIceEvent" = [ "Event" ];
+          "RtcTrackEvent" = [ "Event" ];
+          "RtcdtmfSender" = [ "EventTarget" ];
+          "RtcdtmfToneChangeEvent" = [ "Event" ];
+          "Screen" = [ "EventTarget" ];
+          "ScreenOrientation" = [ "EventTarget" ];
+          "ScriptProcessorNode" = [ "AudioNode" "EventTarget" ];
+          "ScrollAreaEvent" = [ "Event" "UiEvent" ];
+          "SecurityPolicyViolationEvent" = [ "Event" ];
+          "Serial" = [ "EventTarget" ];
+          "SerialPort" = [ "EventTarget" ];
+          "ServiceWorker" = [ "EventTarget" ];
+          "ServiceWorkerContainer" = [ "EventTarget" ];
+          "ServiceWorkerGlobalScope" = [ "EventTarget" "WorkerGlobalScope" ];
+          "ServiceWorkerRegistration" = [ "EventTarget" ];
+          "ShadowRoot" = [ "DocumentFragment" "EventTarget" "Node" ];
+          "SharedWorker" = [ "EventTarget" ];
+          "SharedWorkerGlobalScope" = [ "EventTarget" "WorkerGlobalScope" ];
+          "SourceBuffer" = [ "EventTarget" ];
+          "SourceBufferList" = [ "EventTarget" ];
+          "SpeechRecognition" = [ "EventTarget" ];
+          "SpeechRecognitionError" = [ "Event" ];
+          "SpeechRecognitionEvent" = [ "Event" ];
+          "SpeechSynthesis" = [ "EventTarget" ];
+          "SpeechSynthesisErrorEvent" = [ "Event" "SpeechSynthesisEvent" ];
+          "SpeechSynthesisEvent" = [ "Event" ];
+          "SpeechSynthesisUtterance" = [ "EventTarget" ];
+          "StereoPannerNode" = [ "AudioNode" "EventTarget" ];
+          "StorageEvent" = [ "Event" ];
+          "SubmitEvent" = [ "Event" ];
+          "SvgAnimateElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgAnimateMotionElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgAnimateTransformElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgAnimationElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgCircleElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgClipPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgComponentTransferFunctionElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgDefsElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgDescElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgElement" = [ "Element" "EventTarget" "Node" ];
+          "SvgEllipseElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgFilterElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgForeignObjectElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgGeometryElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgGradientElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgGraphicsElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgImageElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgLineElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgLinearGradientElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGradientElement" ];
+          "SvgMarkerElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgMaskElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgMetadataElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgPathSegArcAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegArcRel" = [ "SvgPathSeg" ];
+          "SvgPathSegClosePath" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicRel" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicSmoothAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoCubicSmoothRel" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticRel" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticSmoothAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegCurvetoQuadraticSmoothRel" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoHorizontalAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoHorizontalRel" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoRel" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoVerticalAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegLinetoVerticalRel" = [ "SvgPathSeg" ];
+          "SvgPathSegMovetoAbs" = [ "SvgPathSeg" ];
+          "SvgPathSegMovetoRel" = [ "SvgPathSeg" ];
+          "SvgPatternElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgPolygonElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgPolylineElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgRadialGradientElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGradientElement" ];
+          "SvgRectElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGeometryElement" "SvgGraphicsElement" ];
+          "SvgScriptElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgSetElement" = [ "Element" "EventTarget" "Node" "SvgAnimationElement" "SvgElement" ];
+          "SvgStopElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgStyleElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgSwitchElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgSymbolElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgTextContentElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgTextElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" "SvgTextPositioningElement" ];
+          "SvgTextPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" ];
+          "SvgTextPositioningElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" ];
+          "SvgTitleElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgUseElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgViewElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgaElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgfeBlendElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeColorMatrixElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeComponentTransferElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeCompositeElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeConvolveMatrixElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDiffuseLightingElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDisplacementMapElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDistantLightElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeDropShadowElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeFloodElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeFuncAElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeFuncBElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeFuncGElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeFuncRElement" = [ "Element" "EventTarget" "Node" "SvgComponentTransferFunctionElement" "SvgElement" ];
+          "SvgfeGaussianBlurElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeImageElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeMergeElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeMergeNodeElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeMorphologyElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeOffsetElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfePointLightElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeSpecularLightingElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeSpotLightElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeTileElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgfeTurbulenceElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvggElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgmPathElement" = [ "Element" "EventTarget" "Node" "SvgElement" ];
+          "SvgsvgElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" ];
+          "SvgtSpanElement" = [ "Element" "EventTarget" "Node" "SvgElement" "SvgGraphicsElement" "SvgTextContentElement" "SvgTextPositioningElement" ];
+          "TaskController" = [ "AbortController" ];
+          "TaskPriorityChangeEvent" = [ "Event" ];
+          "TaskSignal" = [ "AbortSignal" "EventTarget" ];
+          "TcpServerSocket" = [ "EventTarget" ];
+          "TcpServerSocketEvent" = [ "Event" ];
+          "TcpSocket" = [ "EventTarget" ];
+          "TcpSocketErrorEvent" = [ "Event" ];
+          "TcpSocketEvent" = [ "Event" ];
+          "Text" = [ "CharacterData" "EventTarget" "Node" ];
+          "TextTrack" = [ "EventTarget" ];
+          "TextTrackCue" = [ "EventTarget" ];
+          "TextTrackList" = [ "EventTarget" ];
+          "TimeEvent" = [ "Event" ];
+          "TouchEvent" = [ "Event" "UiEvent" ];
+          "TrackEvent" = [ "Event" ];
+          "TransitionEvent" = [ "Event" ];
+          "UiEvent" = [ "Event" ];
+          "Usb" = [ "EventTarget" ];
+          "UsbConnectionEvent" = [ "Event" ];
+          "UsbPermissionResult" = [ "EventTarget" "PermissionStatus" ];
+          "UserProximityEvent" = [ "Event" ];
+          "ValueEvent" = [ "Event" ];
+          "VideoStreamTrack" = [ "EventTarget" "MediaStreamTrack" ];
+          "VideoTrackList" = [ "EventTarget" ];
+          "VrDisplay" = [ "EventTarget" ];
+          "VttCue" = [ "EventTarget" "TextTrackCue" ];
+          "WakeLockSentinel" = [ "EventTarget" ];
+          "WaveShaperNode" = [ "AudioNode" "EventTarget" ];
+          "WebGlContextEvent" = [ "Event" ];
+          "WebKitCssMatrix" = [ "DomMatrix" "DomMatrixReadOnly" ];
+          "WebSocket" = [ "EventTarget" ];
+          "WebTransportError" = [ "DomException" ];
+          "WebTransportReceiveStream" = [ "ReadableStream" ];
+          "WebTransportSendStream" = [ "WritableStream" ];
+          "WheelEvent" = [ "Event" "MouseEvent" "UiEvent" ];
+          "Window" = [ "EventTarget" ];
+          "WindowClient" = [ "Client" ];
+          "Worker" = [ "EventTarget" ];
+          "WorkerDebuggerGlobalScope" = [ "EventTarget" ];
+          "WorkerGlobalScope" = [ "EventTarget" ];
+          "XmlDocument" = [ "Document" "EventTarget" "Node" ];
+          "XmlHttpRequest" = [ "EventTarget" "XmlHttpRequestEventTarget" ];
+          "XmlHttpRequestEventTarget" = [ "EventTarget" ];
+          "XmlHttpRequestUpload" = [ "EventTarget" "XmlHttpRequestEventTarget" ];
+          "XrBoundedReferenceSpace" = [ "EventTarget" "XrReferenceSpace" "XrSpace" ];
+          "XrInputSourceEvent" = [ "Event" ];
+          "XrInputSourcesChangeEvent" = [ "Event" ];
+          "XrJointPose" = [ "XrPose" ];
+          "XrJointSpace" = [ "EventTarget" "XrSpace" ];
+          "XrLayer" = [ "EventTarget" ];
+          "XrPermissionStatus" = [ "EventTarget" "PermissionStatus" ];
+          "XrReferenceSpace" = [ "EventTarget" "XrSpace" ];
+          "XrReferenceSpaceEvent" = [ "Event" ];
+          "XrSession" = [ "EventTarget" ];
+          "XrSessionEvent" = [ "Event" ];
+          "XrSpace" = [ "EventTarget" ];
+          "XrSystem" = [ "EventTarget" ];
+          "XrViewerPose" = [ "XrPose" ];
+          "XrWebGlLayer" = [ "EventTarget" "XrLayer" ];
+        };
+        resolvedDefaultFeatures = [ "Crypto" "EventTarget" "Window" ];
+      };
+      "webpki" = rec {
+        crateName = "webpki";
+        version = "0.22.4";
+        edition = "2018";
+        sha256 = "0lwv7jdlcqjjqqhxcrapnyk5bz4lvr12q444b50gzl3krsjswqzd";
+        authors = [
+          "Brian Smith <brian@briansmith.org>"
+        ];
+        dependencies = [
+          {
+            name = "ring";
+            packageId = "ring 0.17.5";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "untrusted";
+            packageId = "untrusted 0.9.0";
+          }
+        ];
+        features = {
+          "alloc" = [ "ring/alloc" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "winapi" = rec {
+        crateName = "winapi";
+        version = "0.3.9";
+        edition = "2015";
+        sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-i686-pc-windows-gnu";
+            packageId = "winapi-i686-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+          }
+          {
+            name = "winapi-x86_64-pc-windows-gnu";
+            packageId = "winapi-x86_64-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+          }
+        ];
+        features = {
+          "debug" = [ "impl-debug" ];
+        };
+        resolvedDefaultFeatures = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "knownfolders" "ntsecapi" "objbase" "processenv" "shlobj" "winbase" "winerror" "ws2ipdef" "ws2tcpip" "wtypesbase" ];
+      };
+      "winapi-i686-pc-windows-gnu" = rec {
+        crateName = "winapi-i686-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi-x86_64-pc-windows-gnu" = rec {
+        crateName = "winapi-x86_64-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "windows-core" = rec {
+        crateName = "windows-core";
+        version = "0.51.1";
+        edition = "2021";
+        sha256 = "0r1f57hsshsghjyc7ypp2s0i78f7b1vr93w68sdb8baxyf2czy7i";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "windows-sys" = rec {
+        crateName = "windows-sys";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets";
+          }
+        ];
+        features = {
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_Security_Cryptography" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_Memory" "Win32_System_Pipes" "Win32_System_SystemInformation" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_WindowsProgramming" "default" ];
+      };
+      "windows-targets" = rec {
+        crateName = "windows-targets";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows_aarch64_gnullvm" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "xml-rs" = rec {
+        crateName = "xml-rs";
+        version = "0.8.19";
+        edition = "2021";
+        crateBin = [ ];
+        sha256 = "0nnpvk3fv32hgh7vs9gbg2swmzxx5yz73f4b7rak7q39q2x9rjqg";
+        libName = "xml";
+        authors = [
+          "Vladimir Matveev <vmatveev@citrine.cc>"
+        ];
+
+      };
+      "zeroize" = rec {
+        crateName = "zeroize";
+        version = "1.6.0";
+        edition = "2021";
+        sha256 = "1ndar43r58zbmasjhrhgas168vxb4i0rwbkcnszhjybwpbqmc29a";
+        authors = [
+          "The RustCrypto Project Developers"
+        ];
+        features = {
+          "default" = [ "alloc" ];
+          "derive" = [ "zeroize_derive" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "zeroize_derive" = [ "dep:zeroize_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" ];
+      };
+      "zstd" = rec {
+        crateName = "zstd";
+        version = "0.9.2+zstd.1.5.1";
+        edition = "2018";
+        sha256 = "0m5aik2jy2w1g68i4isa0c3gq9a7avq9abgjfjbc6f60yqdym413";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "zstd-safe";
+            packageId = "zstd-safe";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        features = {
+          "arrays" = [ "zstd-safe/arrays" ];
+          "bindgen" = [ "zstd-safe/bindgen" ];
+          "debug" = [ "zstd-safe/debug" ];
+          "default" = [ "legacy" "arrays" ];
+          "experimental" = [ "zstd-safe/experimental" ];
+          "legacy" = [ "zstd-safe/legacy" ];
+          "no_asm" = [ "zstd-safe/no_asm" ];
+          "pkg-config" = [ "zstd-safe/pkg-config" ];
+          "thin" = [ "zstd-safe/thin" ];
+          "zstdmt" = [ "zstd-safe/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "default" "legacy" ];
+      };
+      "zstd-safe" = rec {
+        crateName = "zstd-safe";
+        version = "4.1.3+zstd.1.5.1";
+        edition = "2018";
+        sha256 = "0yfvqzzkbj871f2vaikal5rm2gf60p1mdzp3jk3w5hmkkywq37g9";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "zstd-sys";
+            packageId = "zstd-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "bindgen" = [ "zstd-sys/bindgen" ];
+          "debug" = [ "zstd-sys/debug" ];
+          "default" = [ "legacy" "arrays" ];
+          "experimental" = [ "zstd-sys/experimental" ];
+          "legacy" = [ "zstd-sys/legacy" ];
+          "no_asm" = [ "zstd-sys/no_asm" ];
+          "pkg-config" = [ "zstd-sys/pkg-config" ];
+          "std" = [ "zstd-sys/std" ];
+          "thin" = [ "zstd-sys/thin" ];
+          "zstdmt" = [ "zstd-sys/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "legacy" "std" ];
+      };
+      "zstd-sys" = rec {
+        crateName = "zstd-sys";
+        version = "1.6.2+zstd.1.5.1";
+        edition = "2018";
+        links = "zstd";
+        sha256 = "17xcr0mw8ps9hlc8m0dzj7yd52lb9r9ic9fbpxa4994yilj2zbrd";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            features = [ "parallel" ];
+          }
+        ];
+        features = {
+          "bindgen" = [ "dep:bindgen" ];
+          "default" = [ "legacy" ];
+          "pkg-config" = [ "dep:pkg-config" ];
+        };
+        resolvedDefaultFeatures = [ "legacy" "std" ];
+      };
+    };
+
+    #
+    # crate2nix/default.nix (excerpt start)
+    #
+
+    /* Target (platform) data for conditional dependencies.
+      This corresponds roughly to what buildRustCrate is setting.
+    */
+    makeDefaultTarget = platform: {
+      unix = platform.isUnix;
+      windows = platform.isWindows;
+      fuchsia = true;
+      test = false;
+
+      /* We are choosing an arbitrary rust version to grab `lib` from,
+      which is unfortunate, but `lib` has been version-agnostic the
+      whole time so this is good enough for now.
+      */
+      os = pkgs.rust.lib.toTargetOs platform;
+      arch = pkgs.rust.lib.toTargetArch platform;
+      family = pkgs.rust.lib.toTargetFamily platform;
+      vendor = pkgs.rust.lib.toTargetVendor platform;
+      env = "gnu";
+      endian =
+        if platform.parsed.cpu.significantByte.name == "littleEndian"
+        then "little" else "big";
+      pointer_width = toString platform.parsed.cpu.bits;
+      debug_assertions = false;
+    };
+
+    /* Filters common temp files and build files. */
+    # TODO(pkolloch): Substitute with gitignore filter
+    sourceFilter = name: type:
+      let
+        baseName = builtins.baseNameOf (builtins.toString name);
+      in
+        ! (
+          # Filter out git
+          baseName == ".gitignore"
+          || (type == "directory" && baseName == ".git")
+
+          # Filter out build results
+          || (
+            type == "directory" && (
+              baseName == "target"
+              || baseName == "_site"
+              || baseName == ".sass-cache"
+              || baseName == ".jekyll-metadata"
+              || baseName == "build-artifacts"
+            )
+          )
+
+          # Filter out nix-build result symlinks
+          || (
+            type == "symlink" && lib.hasPrefix "result" baseName
+          )
+
+          # Filter out IDE config
+          || (
+            type == "directory" && (
+              baseName == ".idea" || baseName == ".vscode"
+            )
+          ) || lib.hasSuffix ".iml" baseName
+
+          # Filter out nix build files
+          || baseName == "Cargo.nix"
+
+          # Filter out editor backup / swap files.
+          || lib.hasSuffix "~" baseName
+          || builtins.match "^\\.sw[a-z]$$" baseName != null
+          || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null
+          || lib.hasSuffix ".tmp" baseName
+          || lib.hasSuffix ".bak" baseName
+          || baseName == "tests.nix"
+        );
+
+    /* Returns a crate which depends on successful test execution
+      of crate given as the second argument.
+
+      testCrateFlags: list of flags to pass to the test exectuable
+      testInputs: list of packages that should be available during test execution
+    */
+    crateWithTest = { crate, testCrate, testCrateFlags, testInputs, testPreRun, testPostRun }:
+      assert builtins.typeOf testCrateFlags == "list";
+      assert builtins.typeOf testInputs == "list";
+      assert builtins.typeOf testPreRun == "string";
+      assert builtins.typeOf testPostRun == "string";
+      let
+        # override the `crate` so that it will build and execute tests instead of
+        # building the actual lib and bin targets We just have to pass `--test`
+        # to rustc and it will do the right thing.  We execute the tests and copy
+        # their log and the test executables to $out for later inspection.
+        test =
+          let
+            drv = testCrate.override
+              (
+                _: {
+                  buildTests = true;
+                }
+              );
+            # If the user hasn't set any pre/post commands, we don't want to
+            # insert empty lines. This means that any existing users of crate2nix
+            # don't get a spurious rebuild unless they set these explicitly.
+            testCommand = pkgs.lib.concatStringsSep "\n"
+              (pkgs.lib.filter (s: s != "") [
+                testPreRun
+                "$f $testCrateFlags 2>&1 | tee -a $out"
+                testPostRun
+              ]);
+          in
+          pkgs.runCommand "run-tests-${testCrate.name}"
+            {
+              inherit testCrateFlags;
+              buildInputs = testInputs;
+            } ''
+            set -e
+
+            export RUST_BACKTRACE=1
+
+            # recreate a file hierarchy as when running tests with cargo
+
+            # the source for test data
+            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
+            # instead of $NIX_BUILD_TOP/
+            # because we compiled those test binaries in the former and not the latter.
+            # So all paths will expect source tree to be there and not in the build top directly.
+            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
+            # TODO(raitobezarius): I believe there could be more edge cases if `crate.sourceRoot`
+            # do exist but it's very hard to reason about them, so let's wait until the first bug report.
+            mkdir -p source/
+            cd source/
+
+            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+
+            # build outputs
+            testRoot=target/debug
+            mkdir -p $testRoot
+
+            # executables of the crate
+            # we copy to prevent std::env::current_exe() to resolve to a store location
+            for i in ${crate}/bin/*; do
+              cp "$i" "$testRoot"
+            done
+            chmod +w -R .
+
+            # test harness executables are suffixed with a hash, like cargo does
+            # this allows to prevent name collision with the main
+            # executables of the crate
+            hash=$(basename $out)
+            for file in ${drv}/tests/*; do
+              f=$testRoot/$(basename $file)-$hash
+              cp $file $f
+              ${testCommand}
+            done
+          '';
+      in
+      pkgs.runCommand "${crate.name}-linked"
+        {
+          inherit (crate) outputs crateName;
+          passthru = (crate.passthru or { }) // {
+            inherit test;
+          };
+        }
+        (lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+          echo tested by ${test}
+        '' + ''
+          ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs}
+        '');
+
+    /* A restricted overridable version of builtRustCratesWithFeatures. */
+    buildRustCrateWithFeatures =
+      { packageId
+      , features ? rootFeatures
+      , crateOverrides ? defaultCrateOverrides
+      , buildRustCrateForPkgsFunc ? null
+      , runTests ? false
+      , testCrateFlags ? [ ]
+      , testInputs ? [ ]
+        # Any command to run immediatelly before a test is executed.
+      , testPreRun ? ""
+        # Any command run immediatelly after a test is executed.
+      , testPostRun ? ""
+      }:
+      lib.makeOverridable
+        (
+          { features
+          , crateOverrides
+          , runTests
+          , testCrateFlags
+          , testInputs
+          , testPreRun
+          , testPostRun
+          }:
+          let
+            buildRustCrateForPkgsFuncOverriden =
+              if buildRustCrateForPkgsFunc != null
+              then buildRustCrateForPkgsFunc
+              else
+                (
+                  if crateOverrides == pkgs.defaultCrateOverrides
+                  then buildRustCrateForPkgs
+                  else
+                    pkgs: (buildRustCrateForPkgs pkgs).override {
+                      defaultCrateOverrides = crateOverrides;
+                    }
+                );
+            builtRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = false;
+            };
+            builtTestRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = true;
+            };
+            drv = builtRustCrates.crates.${packageId};
+            testDrv = builtTestRustCrates.crates.${packageId};
+            derivation =
+              if runTests then
+                crateWithTest
+                  {
+                    crate = drv;
+                    testCrate = testDrv;
+                    inherit testCrateFlags testInputs testPreRun testPostRun;
+                  }
+              else drv;
+          in
+          derivation
+        )
+        { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun; };
+
+    /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc
+      for the corresponding crate.
+    */
+    builtRustCratesWithFeatures =
+      { packageId
+      , features
+      , crateConfigs ? crates
+      , buildRustCrateForPkgsFunc
+      , runTests
+      , makeTarget ? makeDefaultTarget
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isList features);
+        assert (builtins.isAttrs (makeTarget stdenv.hostPlatform));
+        assert (builtins.isBool runTests);
+        let
+          rootPackageId = packageId;
+          mergedFeatures = mergePackageFeatures
+            (
+              args // {
+                inherit rootPackageId;
+                target = makeTarget stdenv.hostPlatform // { test = runTests; };
+              }
+            );
+          # Memoize built packages so that reappearing packages are only built once.
+          builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs;
+          mkBuiltByPackageIdByPkgs = pkgs:
+            let
+              self = {
+                crates = lib.mapAttrs (packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId) crateConfigs;
+                target = makeTarget pkgs.stdenv.hostPlatform;
+                build = mkBuiltByPackageIdByPkgs pkgs.buildPackages;
+              };
+            in
+            self;
+          buildByPackageIdForPkgsImpl = self: pkgs: packageId:
+            let
+              features = mergedFeatures."${packageId}" or [ ];
+              crateConfig' = crateConfigs."${packageId}";
+              crateConfig =
+                builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ];
+              devDependencies =
+                lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig'.devDependencies or [ ]);
+              dependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self) target;
+                  buildByPackageId = depPackageId:
+                    # proc_macro crates must be compiled for the build architecture
+                    if crateConfigs.${depPackageId}.procMacro or false
+                    then self.build.crates.${depPackageId}
+                    else self.crates.${depPackageId};
+                  dependencies =
+                    (crateConfig.dependencies or [ ])
+                    ++ devDependencies;
+                };
+              buildDependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self.build) target;
+                  buildByPackageId = depPackageId:
+                    self.build.crates.${depPackageId};
+                  dependencies = crateConfig.buildDependencies or [ ];
+                };
+              dependenciesWithRenames =
+                let
+                  buildDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self) target;
+                    dependencies = crateConfig.dependencies or [ ] ++ devDependencies;
+                  };
+                  hostDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self.build) target;
+                    dependencies = crateConfig.buildDependencies or [ ];
+                  };
+                in
+                lib.filter (d: d ? "rename") (hostDeps ++ buildDeps);
+              # Crate renames have the form:
+              #
+              # {
+              #    crate_name = [
+              #       { version = "1.2.3"; rename = "crate_name01"; }
+              #    ];
+              #    # ...
+              # }
+              crateRenames =
+                let
+                  grouped =
+                    lib.groupBy
+                      (dependency: dependency.name)
+                      dependenciesWithRenames;
+                  versionAndRename = dep:
+                    let
+                      package = crateConfigs."${dep.packageId}";
+                    in
+                    { inherit (dep) rename; inherit (package) version; };
+                in
+                lib.mapAttrs (name: builtins.map versionAndRename) grouped;
+            in
+            buildRustCrateForPkgsFunc pkgs
+              (
+                crateConfig // {
+                  # https://github.com/NixOS/nixpkgs/issues/218712
+                  dontStrip = stdenv.hostPlatform.isDarwin;
+                  src = crateConfig.src or (
+                    pkgs.fetchurl rec {
+                      name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz";
+                      # https://www.pietroalbini.org/blog/downloading-crates-io/
+                      # Not rate-limited, CDN URL.
+                      url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate";
+                      sha256 =
+                        assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}");
+                        crateConfig.sha256;
+                    }
+                  );
+                  extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}";
+                  inherit features dependencies buildDependencies crateRenames release;
+                }
+              );
+        in
+        builtByPackageIdByPkgs;
+
+    /* Returns the actual derivations for the given dependencies. */
+    dependencyDerivations =
+      { buildByPackageId
+      , features
+      , dependencies
+      , target
+      }:
+        assert (builtins.isList features);
+        assert (builtins.isList dependencies);
+        assert (builtins.isAttrs target);
+        let
+          enabledDependencies = filterEnabledDependencies {
+            inherit dependencies features target;
+          };
+          depDerivation = dependency: buildByPackageId dependency.packageId;
+        in
+        map depDerivation enabledDependencies;
+
+    /* Returns a sanitized version of val with all values substituted that cannot
+      be serialized as JSON.
+    */
+    sanitizeForJson = val:
+      if builtins.isAttrs val
+      then lib.mapAttrs (n: sanitizeForJson) val
+      else if builtins.isList val
+      then builtins.map sanitizeForJson val
+      else if builtins.isFunction val
+      then "function"
+      else val;
+
+    /* Returns various tools to debug a crate. */
+    debugCrate = { packageId, target ? makeDefaultTarget stdenv.hostPlatform }:
+      assert (builtins.isString packageId);
+      let
+        debug = rec {
+          # The built tree as passed to buildRustCrate.
+          buildTree = buildRustCrateWithFeatures {
+            buildRustCrateForPkgsFunc = _: lib.id;
+            inherit packageId;
+          };
+          sanitizedBuildTree = sanitizeForJson buildTree;
+          dependencyTree = sanitizeForJson
+            (
+              buildRustCrateWithFeatures {
+                buildRustCrateForPkgsFunc = _: crate: {
+                  "01_crateName" = crate.crateName or false;
+                  "02_features" = crate.features or [ ];
+                  "03_dependencies" = crate.dependencies or [ ];
+                };
+                inherit packageId;
+              }
+            );
+          mergedPackageFeatures = mergePackageFeatures {
+            features = rootFeatures;
+            inherit packageId target;
+          };
+          diffedDefaultPackageFeatures = diffDefaultPackageFeatures {
+            inherit packageId target;
+          };
+        };
+      in
+      { internal = debug; };
+
+    /* Returns differences between cargo default features and crate2nix default
+      features.
+
+      This is useful for verifying the feature resolution in crate2nix.
+    */
+    diffDefaultPackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , target
+      }:
+        assert (builtins.isAttrs crateConfigs);
+        let
+          prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; });
+          mergedFeatures =
+            prefixValues
+              "crate2nix"
+              (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; });
+          configs = prefixValues "cargo" crateConfigs;
+          combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ];
+          onlyInCargo =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined);
+          onlyInCrate2Nix =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined);
+          differentFeatures = lib.filterAttrs
+            (
+              n: v:
+                (v ? "crate2nix")
+                && (v ? "cargo")
+                && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ])
+            )
+            combined;
+        in
+        builtins.toJSON {
+          inherit onlyInCargo onlyInCrate2Nix differentFeatures;
+        };
+
+    /* Returns an attrset mapping packageId to the list of enabled features.
+
+      If multiple paths to a dependency enable different features, the
+      corresponding feature sets are merged. Features in rust are additive.
+    */
+    mergePackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , rootPackageId ? packageId
+      , features ? rootFeatures
+      , dependencyPath ? [ crates.${packageId}.crateName ]
+      , featuresByPackageId ? { }
+      , target
+        # Adds devDependencies to the crate with rootPackageId.
+      , runTests ? false
+      , ...
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isString rootPackageId);
+        assert (builtins.isList features);
+        assert (builtins.isList dependencyPath);
+        assert (builtins.isAttrs featuresByPackageId);
+        assert (builtins.isAttrs target);
+        assert (builtins.isBool runTests);
+        let
+          crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
+          expandedFeatures = expandFeatures (crateConfig.features or { }) features;
+          enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
+          depWithResolvedFeatures = dependency:
+            let
+              inherit (dependency) packageId;
+              features = dependencyFeatures enabledFeatures dependency;
+            in
+            { inherit packageId features; };
+          resolveDependencies = cache: path: dependencies:
+            assert (builtins.isAttrs cache);
+            assert (builtins.isList dependencies);
+            let
+              enabledDependencies = filterEnabledDependencies {
+                inherit dependencies target;
+                features = enabledFeatures;
+              };
+              directDependencies = map depWithResolvedFeatures enabledDependencies;
+              foldOverCache = op: lib.foldl op cache directDependencies;
+            in
+            foldOverCache
+              (
+                cache: { packageId, features }:
+                  let
+                    cacheFeatures = cache.${packageId} or [ ];
+                    combinedFeatures = sortedUnique (cacheFeatures ++ features);
+                  in
+                  if cache ? ${packageId} && cache.${packageId} == combinedFeatures
+                  then cache
+                  else
+                    mergePackageFeatures {
+                      features = combinedFeatures;
+                      featuresByPackageId = cache;
+                      inherit crateConfigs packageId target runTests rootPackageId;
+                    }
+              );
+          cacheWithSelf =
+            let
+              cacheFeatures = featuresByPackageId.${packageId} or [ ];
+              combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
+            in
+            featuresByPackageId // {
+              "${packageId}" = combinedFeatures;
+            };
+          cacheWithDependencies =
+            resolveDependencies cacheWithSelf "dep"
+              (
+                crateConfig.dependencies or [ ]
+                ++ lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig.devDependencies or [ ])
+              );
+          cacheWithAll =
+            resolveDependencies
+              cacheWithDependencies "build"
+              (crateConfig.buildDependencies or [ ]);
+        in
+        cacheWithAll;
+
+    /* Returns the enabled dependencies given the enabled features. */
+    filterEnabledDependencies = { dependencies, features, target }:
+      assert (builtins.isList dependencies);
+      assert (builtins.isList features);
+      assert (builtins.isAttrs target);
+
+      lib.filter
+        (
+          dep:
+          let
+            targetFunc = dep.target or (features: true);
+          in
+          targetFunc { inherit features target; }
+          && (
+            !(dep.optional or false)
+            || builtins.any (doesFeatureEnableDependency dep) features
+          )
+        )
+        dependencies;
+
+    /* Returns whether the given feature should enable the given dependency. */
+    doesFeatureEnableDependency = dependency: feature:
+      let
+        name = dependency.rename or dependency.name;
+        prefix = "${name}/";
+        len = builtins.stringLength prefix;
+        startsWithPrefix = builtins.substring 0 len feature == prefix;
+      in
+      feature == name || feature == "dep:" + name || startsWithPrefix;
+
+    /* Returns the expanded features for the given inputFeatures by applying the
+      rules in featureMap.
+
+      featureMap is an attribute set which maps feature names to lists of further
+      feature names to enable in case this feature is selected.
+    */
+    expandFeatures = featureMap: inputFeatures:
+      assert (builtins.isAttrs featureMap);
+      assert (builtins.isList inputFeatures);
+      let
+        expandFeaturesNoCycle = oldSeen: inputFeatures:
+          if inputFeatures != [ ]
+          then
+            let
+              # The feature we're currently expanding.
+              feature = builtins.head inputFeatures;
+              # All the features we've seen/expanded so far, including the one
+              # we're currently processing.
+              seen = oldSeen // { ${feature} = 1; };
+              # Expand the feature but be careful to not re-introduce a feature
+              # that we've already seen: this can easily cause a cycle, see issue
+              # #209.
+              enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]);
+            in
+            [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
+          # No more features left, nothing to expand to.
+          else [ ];
+        outFeatures = expandFeaturesNoCycle { } inputFeatures;
+      in
+      sortedUnique outFeatures;
+
+    /* This function adds optional dependencies as features if they are enabled
+      indirectly by dependency features. This function mimics Cargo's behavior
+      described in a note at:
+      https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
+    */
+    enableFeatures = dependencies: features:
+      assert (builtins.isList features);
+      assert (builtins.isList dependencies);
+      let
+        additionalFeatures = lib.concatMap
+          (
+            dependency:
+              assert (builtins.isAttrs dependency);
+              let
+                enabled = builtins.any (doesFeatureEnableDependency dependency) features;
+              in
+              if (dependency.optional or false) && enabled
+              then [ (dependency.rename or dependency.name) ]
+              else [ ]
+          )
+          dependencies;
+      in
+      sortedUnique (features ++ additionalFeatures);
+
+    /*
+      Returns the actual features for the given dependency.
+
+      features: The features of the crate that refers this dependency.
+    */
+    dependencyFeatures = features: dependency:
+      assert (builtins.isList features);
+      assert (builtins.isAttrs dependency);
+      let
+        defaultOrNil =
+          if dependency.usesDefaultFeatures or true
+          then [ "default" ]
+          else [ ];
+        explicitFeatures = dependency.features or [ ];
+        additionalDependencyFeatures =
+          let
+            name = dependency.rename or dependency.name;
+            stripPrefixMatch = prefix: s:
+              if lib.hasPrefix prefix s
+              then lib.removePrefix prefix s
+              else null;
+            extractFeature = feature: lib.findFirst
+              (f: f != null)
+              null
+              (map (prefix: stripPrefixMatch prefix feature) [
+                (name + "/")
+                (name + "?/")
+              ]);
+            dependencyFeatures = lib.filter (f: f != null) (map extractFeature features);
+          in
+          dependencyFeatures;
+      in
+      defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures;
+
+    /* Sorts and removes duplicates from a list of strings. */
+    sortedUnique = features:
+      assert (builtins.isList features);
+      assert (builtins.all builtins.isString features);
+      let
+        outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features;
+        outFeaturesUnique = builtins.attrNames outFeaturesSet;
+      in
+      builtins.sort (a: b: a < b) outFeaturesUnique;
+
+    deprecationWarning = message: value:
+      if strictDeprecation
+      then builtins.throw "strictDeprecation enabled, aborting: ${message}"
+      else builtins.trace message value;
+
+    #
+    # crate2nix/default.nix (excerpt end)
+    #
+  };
+}
+
diff --git a/tvix/tools/turbofetch/Cargo.toml b/tvix/tools/turbofetch/Cargo.toml
new file mode 100644
index 0000000000..6b2f0e7520
--- /dev/null
+++ b/tvix/tools/turbofetch/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "turbofetch"
+version = "0.1.0"
+edition = "2021"
+
+# We don't join the //tvix workspace, as this is fairly cache.nixos.org-specific.
+[workspace]
+members = ["."]
+
+[dependencies]
+aws_lambda_events = { version = "0.11.1", default-features = false, features = ["lambda_function_urls"] }
+bytes = "1.5.0"
+data-encoding = "2.4.0"
+futures = { version = "0.3.30", default-features = false, features = ["std"] }
+httparse = "1.8.0"
+hyper = { version = "0.14.27", default-features = false }
+lambda_runtime = "0.8.2"
+magic-buffer = "0.1.1"
+rusoto_core = { version = "0.48.0", features = ["rustls"], default-features = false }
+rusoto_s3 = { version = "0.48.0", features = ["rustls"], default-features = false }
+serde_json = "1.0.108"
+serde = { version = "1.0.190", features = ["derive"] }
+tokio = { version = "1.33.0", features = ["full"] }
+tower = "0.4.13"
+# TODO(edef): zstd = "0.13.0"
+zstd = "0.9.0"
+tracing-subscriber = { version = "0.3.17", features = ["json"] }
+tracing = "0.1.40"
diff --git a/tvix/tools/turbofetch/OWNERS b/tvix/tools/turbofetch/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/tvix/tools/turbofetch/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/tvix/tools/turbofetch/default.nix b/tvix/tools/turbofetch/default.nix
new file mode 100644
index 0000000000..bd70f6a5e6
--- /dev/null
+++ b/tvix/tools/turbofetch/default.nix
@@ -0,0 +1,12 @@
+{ lib, pkgs, ... }:
+
+(pkgs.callPackage ./Cargo.nix {
+  defaultCrateOverrides = pkgs.defaultCrateOverrides // {
+
+    ring = prev: {
+      links = ''ring_core_${lib.replaceStrings ["."] ["_"] prev.version}'';
+    };
+  };
+}).rootCrate.build.override {
+  runTests = true;
+}
diff --git a/tvix/tools/turbofetch/deploy.sh b/tvix/tools/turbofetch/deploy.sh
new file mode 100755
index 0000000000..65564ce2ed
--- /dev/null
+++ b/tvix/tools/turbofetch/deploy.sh
@@ -0,0 +1,5 @@
+#! /usr/bin/env nix-shell
+#! nix-shell -i "bash -e"
+#! nix-shell -p cargo-lambda
+cargo lambda build --release
+cargo lambda deploy
diff --git a/tvix/tools/turbofetch/src/buffer.rs b/tvix/tools/turbofetch/src/buffer.rs
new file mode 100644
index 0000000000..d6ff93e3cf
--- /dev/null
+++ b/tvix/tools/turbofetch/src/buffer.rs
@@ -0,0 +1,83 @@
+use magic_buffer::MagicBuffer;
+use std::cell::Cell;
+
+/// Buffer is a FIFO queue for bytes, built on a ring buffer.
+/// It always provides contiguous slices for both the readable and writable parts,
+/// using an underlying buffer that is "mirrored" in virtual memory.
+pub struct Buffer {
+    buffer: MagicBuffer,
+    /// first readable byte
+    head: Cell<usize>,
+    /// first writable byte
+    tail: usize,
+}
+
+impl Buffer {
+    /// Allocate a fresh buffer, with the specified capacity.
+    /// The buffer can contain at most `capacity - 1` bytes.
+    /// The capacity must be a power of two, and at least [Buffer::min_len].
+    pub fn new(capacity: usize) -> Buffer {
+        Buffer {
+            // MagicBuffer::new verifies that `capacity` is a power of two,
+            // and at least MagicBuffer::min_len().
+            buffer: MagicBuffer::new(capacity).unwrap(),
+            // `head == tail` means the buffer is empty.
+            // In order to ensure that this remains unambiguous,
+            // the buffer can only be filled with capacity-1 bytes.
+            head: Cell::new(0),
+            tail: 0,
+        }
+    }
+
+    /// Returns the minimum buffer capacity.
+    /// This depends on the operating system and architecture.
+    pub fn min_capacity() -> usize {
+        MagicBuffer::min_len()
+    }
+
+    /// Return the capacity of the buffer.
+    /// This is equal to `self.data().len() + self.space().len() + 1`.
+    pub fn capacity(&self) -> usize {
+        self.buffer.len()
+    }
+
+    /// Return the valid, readable data in the buffer.
+    pub fn data(&self) -> &[u8] {
+        let len = self.buffer.len();
+        let head = self.head.get();
+
+        if head <= self.tail {
+            &self.buffer[head..self.tail]
+        } else {
+            &self.buffer[head..self.tail + len]
+        }
+    }
+
+    /// Mark `read_len` bytes of the readable data as consumed, freeing the space.
+    pub fn consume(&self, read_len: usize) {
+        debug_assert!(read_len <= self.data().len());
+        let mut head = self.head.get();
+        head += read_len;
+        head &= self.buffer.len() - 1;
+        self.head.set(head);
+    }
+
+    /// Return the empty, writable space in the buffer.
+    pub fn space(&mut self) -> &mut [u8] {
+        let len = self.buffer.len();
+        let head = self.head.get();
+
+        if head <= self.tail {
+            &mut self.buffer[self.tail..head + len - 1]
+        } else {
+            &mut self.buffer[self.tail..head - 1]
+        }
+    }
+
+    /// Mark `written_len` bytes of the writable space as valid, readable data.
+    pub fn commit(&mut self, written_len: usize) {
+        debug_assert!(written_len <= self.space().len());
+        self.tail += written_len;
+        self.tail &= self.buffer.len() - 1;
+    }
+}
diff --git a/tvix/tools/turbofetch/src/lib.rs b/tvix/tools/turbofetch/src/lib.rs
new file mode 100644
index 0000000000..4b62fa4d75
--- /dev/null
+++ b/tvix/tools/turbofetch/src/lib.rs
@@ -0,0 +1,103 @@
+use std::{mem::MaybeUninit, str};
+use tokio::io::{self, AsyncRead, AsyncReadExt};
+
+pub use buffer::Buffer;
+mod buffer;
+
+/// Read as much data into `buffer` as possible.
+/// Returns [io::ErrorKind::OutOfMemory] if the buffer is already full.
+async fn slurp(buffer: &mut Buffer, sock: &mut (impl AsyncRead + Unpin)) -> io::Result<()> {
+    match buffer.space() {
+        [] => Err(io::Error::new(io::ErrorKind::OutOfMemory, "buffer filled")),
+        buf => {
+            let n = sock.read(buf).await?;
+            if n == 0 {
+                return Err(io::ErrorKind::UnexpectedEof.into());
+            }
+            buffer.commit(n);
+
+            Ok(())
+        }
+    }
+}
+
+fn get_content_length(headers: &[httparse::Header]) -> io::Result<u64> {
+    for header in headers {
+        if header.name == "Transfer-Encoding" {
+            return Err(io::Error::new(
+                io::ErrorKind::InvalidData,
+                "Transfer-Encoding is unsupported",
+            ));
+        }
+
+        if header.name == "Content-Length" {
+            return str::from_utf8(header.value)
+                .ok()
+                .and_then(|v| v.parse().ok())
+                .ok_or_else(|| {
+                    io::Error::new(io::ErrorKind::InvalidData, "invalid Content-Length")
+                });
+        }
+    }
+
+    Err(io::Error::new(
+        io::ErrorKind::InvalidData,
+        "Content-Length missing",
+    ))
+}
+
+/// Read an HTTP response from `sock` using `buffer`, returning the response body.
+/// Returns an error if anything but 200 OK is received.
+///
+/// The buffer must have enough space to contain the entire response body.
+/// If there is not enough space, [io::ErrorKind::OutOfMemory] is returned.
+///
+/// The HTTP response must use `Content-Length`, without `Transfer-Encoding`.
+pub async fn parse_response<'a>(
+    sock: &mut (impl AsyncRead + Unpin),
+    buffer: &'a mut Buffer,
+) -> io::Result<&'a [u8]> {
+    let body_len = loop {
+        let mut headers = [MaybeUninit::uninit(); 16];
+        let mut response = httparse::Response::new(&mut []);
+        let status = httparse::ParserConfig::default()
+            .parse_response_with_uninit_headers(&mut response, buffer.data(), &mut headers)
+            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
+
+        if let httparse::Status::Complete(n) = status {
+            buffer.consume(n);
+
+            let code = response.code.unwrap();
+            if code != 200 {
+                return Err(io::Error::new(
+                    io::ErrorKind::Other,
+                    format!("HTTP response {code}"),
+                ));
+            }
+
+            break get_content_length(response.headers)?;
+        }
+
+        slurp(buffer, sock).await?;
+    };
+
+    let buf_len = buffer.space().len() + buffer.data().len();
+
+    if body_len > buf_len as u64 {
+        return Err(io::Error::new(
+            io::ErrorKind::OutOfMemory,
+            "HTTP response body does not fit in buffer",
+        ));
+    }
+
+    let body_len = body_len as usize;
+
+    while buffer.data().len() < body_len {
+        slurp(buffer, sock).await?;
+    }
+
+    let data = buffer.data();
+    buffer.consume(body_len);
+
+    Ok(&data[..body_len])
+}
diff --git a/tvix/tools/turbofetch/src/main.rs b/tvix/tools/turbofetch/src/main.rs
new file mode 100644
index 0000000000..4b3a50eb39
--- /dev/null
+++ b/tvix/tools/turbofetch/src/main.rs
@@ -0,0 +1,220 @@
+//! turbofetch is a high-performance bulk S3 object aggregator.
+//!
+//! It operates on two S3 buckets: a source bucket (nix-cache), and a
+//! work bucket defined at runtime. The work bucket contains a job file
+//! consisting of concatenated 32-character keys, representing narinfo
+//! files in the source bucket, without the `.narinfo` suffix or any
+//! other separators.
+//!
+//! Each run of turbofetch processes a half-open range of indices from the
+//! job file, and outputs a zstd stream of concatenated objects, without
+//! additional separators and in no particular order. These segment files
+//! are written into the work bucket, named for the range of indices they
+//! cover. `/narinfo.zst/000000000c380d40-000000000c385b60` covers the 20k
+//! objects `[0xc380d40, 0xc385b60) = [205000000, 205020000)`. Empirically,
+//! segment files of 20k objects achieve a compression ratio of 4.7x.
+//!
+//! Reassembly is left to narinfo2parquet, which interprets StorePath lines.
+//!
+//! TODO(edef): any retries/error handling whatsoever
+//! Currently, it fails an entire range if anything goes wrong, and doesn't
+//! write any output.
+
+use bytes::Bytes;
+use futures::{stream::FuturesUnordered, Stream, TryStreamExt};
+use rusoto_core::ByteStream;
+use rusoto_s3::{GetObjectRequest, PutObjectRequest, S3Client, S3};
+use serde::Deserialize;
+use std::{io::Write, mem, ops::Range, ptr};
+use tokio::{
+    io::{self, AsyncReadExt, AsyncWriteExt},
+    net::TcpStream,
+};
+
+/// Fetch a group of keys, streaming concatenated chunks as they arrive from S3.
+/// `keys` must be a slice from the job file. Any network error at all fails the
+/// entire batch, and there is no rate limiting.
+fn fetch(keys: &[[u8; 32]]) -> impl Stream<Item = io::Result<Bytes>> {
+    // S3 supports only HTTP/1.1, but we can ease the pain somewhat by using
+    // HTTP pipelining. It terminates the TCP connection after receiving 100
+    // requests, so we chunk the keys up accordingly, and make one connection
+    // for each chunk.
+    keys.chunks(100)
+        .map(|chunk| {
+            const PREFIX: &[u8] = b"GET /nix-cache/";
+            const SUFFIX: &[u8] = b".narinfo HTTP/1.1\nHost: s3.amazonaws.com\n\n";
+            const LENGTH: usize = PREFIX.len() + 32 + SUFFIX.len();
+
+            let mut request = Vec::with_capacity(LENGTH * 100);
+            for key in chunk {
+                request.extend_from_slice(PREFIX);
+                request.extend_from_slice(key);
+                request.extend_from_slice(SUFFIX);
+            }
+
+            (request, chunk.len())
+        })
+        .map(|(request, n)| async move {
+            let (mut read, mut write) = TcpStream::connect("s3.amazonaws.com:80")
+                .await?
+                .into_split();
+
+            let _handle = tokio::spawn(async move {
+                let request = request;
+                write.write_all(&request).await
+            });
+
+            let mut buffer = turbofetch::Buffer::new(512 * 1024);
+            let mut bodies = vec![];
+
+            for _ in 0..n {
+                let body = turbofetch::parse_response(&mut read, &mut buffer).await?;
+                bodies.extend_from_slice(body);
+            }
+
+            Ok::<_, io::Error>(Bytes::from(bodies))
+        })
+        .collect::<FuturesUnordered<_>>()
+}
+
+/// Retrieve a range of keys from the job file.
+async fn get_range(
+    s3: &'static S3Client,
+    bucket: String,
+    key: String,
+    range: Range<u64>,
+) -> io::Result<Box<[[u8; 32]]>> {
+    let resp = s3
+        .get_object(GetObjectRequest {
+            bucket,
+            key,
+            range: Some(format!("bytes={}-{}", range.start * 32, range.end * 32 - 1)),
+            ..GetObjectRequest::default()
+        })
+        .await
+        .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
+
+    let mut body = vec![];
+    resp.body
+        .ok_or(io::ErrorKind::InvalidData)?
+        .into_async_read()
+        .read_to_end(&mut body)
+        .await?;
+
+    let body = exact_chunks(body.into_boxed_slice()).ok_or(io::ErrorKind::InvalidData)?;
+
+    Ok(body)
+}
+
+fn exact_chunks(mut buf: Box<[u8]>) -> Option<Box<[[u8; 32]]>> {
+    // SAFETY: We ensure that `buf.len()` is a multiple of 32, and there are no alignment requirements.
+    unsafe {
+        let ptr = buf.as_mut_ptr();
+        let len = buf.len();
+
+        if len % 32 != 0 {
+            return None;
+        }
+
+        let ptr = ptr as *mut [u8; 32];
+        let len = len / 32;
+        mem::forget(buf);
+
+        Some(Box::from_raw(ptr::slice_from_raw_parts_mut(ptr, len)))
+    }
+}
+
+// TODO(edef): factor this out into a separate entry point
+#[tokio::main(flavor = "current_thread")]
+async fn main() -> Result<(), lambda_runtime::Error> {
+    let s3 = S3Client::new(rusoto_core::Region::UsEast1);
+    let s3 = &*Box::leak(Box::new(s3));
+
+    tracing_subscriber::fmt()
+        .json()
+        .with_max_level(tracing::Level::INFO)
+        // this needs to be set to remove duplicated information in the log.
+        .with_current_span(false)
+        // this needs to be set to false, otherwise ANSI color codes will
+        // show up in a confusing manner in CloudWatch logs.
+        .with_ansi(false)
+        // disabling time is handy because CloudWatch will add the ingestion time.
+        .without_time()
+        // remove the name of the function from every log entry
+        .with_target(false)
+        .init();
+
+    lambda_runtime::run(lambda_runtime::service_fn(|event| func(s3, event))).await
+}
+
+/// Lambda request body
+#[derive(Debug, Deserialize)]
+struct Params {
+    work_bucket: String,
+    job_file: String,
+    start: u64,
+    end: u64,
+}
+
+#[tracing::instrument(skip(s3, event), fields(req_id = %event.context.request_id))]
+async fn func(
+    s3: &'static S3Client,
+    event: lambda_runtime::LambdaEvent<
+        aws_lambda_events::lambda_function_urls::LambdaFunctionUrlRequest,
+    >,
+) -> Result<&'static str, lambda_runtime::Error> {
+    let mut params = event.payload.body.ok_or("no body")?;
+
+    if event.payload.is_base64_encoded {
+        params = String::from_utf8(data_encoding::BASE64.decode(params.as_bytes())?)?;
+    }
+
+    let params: Params = serde_json::from_str(&params)?;
+
+    if params.start >= params.end {
+        return Err("nope".into());
+    }
+
+    let keys = get_range(
+        s3,
+        params.work_bucket.clone(),
+        params.job_file.to_owned(),
+        params.start..params.end,
+    )
+    .await?;
+
+    let zchunks = fetch(&keys)
+        .try_fold(
+            Box::new(zstd::Encoder::new(vec![], zstd::DEFAULT_COMPRESSION_LEVEL).unwrap()),
+            |mut w, buf| {
+                w.write_all(&buf).unwrap();
+                async { Ok(w) }
+            },
+        )
+        .await?;
+
+    let zchunks = to_byte_stream(zchunks.finish().unwrap());
+
+    tracing::info!("we got to put_object");
+
+    s3.put_object(PutObjectRequest {
+        bucket: params.work_bucket,
+        key: format!("narinfo.zst/{:016x}-{:016x}", params.start, params.end),
+        body: Some(zchunks),
+        ..Default::default()
+    })
+    .await
+    .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
+
+    tracing::info!("… and it worked!");
+
+    Ok("OK")
+}
+
+fn to_byte_stream(buffer: Vec<u8>) -> ByteStream {
+    let size_hint = buffer.len();
+    ByteStream::new_with_size(
+        futures::stream::once(async { Ok(buffer.into()) }),
+        size_hint,
+    )
+}
diff --git a/tvix/tools/weave/Cargo.lock b/tvix/tools/weave/Cargo.lock
new file mode 100644
index 0000000000..46a90cb482
--- /dev/null
+++ b/tvix/tools/weave/Cargo.lock
@@ -0,0 +1,2179 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
+name = "argminmax"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "202108b46429b765ef483f8a24d5c46f48c14acfdacc086dd4ab6dddf6bcdbd2"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "array-init-cursor"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7d0a018de4f6aa429b9d33d69edf69072b1c5b1cb8d3e4a5f7ef898fc3eb76"
+
+[[package]]
+name = "arrow-format"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07884ea216994cdc32a2d5f8274a8bee979cfe90274b83f86f440866ee3132c7"
+dependencies = [
+ "planus",
+ "serde",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "atoi"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "atoi_simd"
+version = "0.15.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ae037714f313c1353189ead58ef9eec30a8e8dc101b2622d461418fd59e28a9"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "brotli"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
+
+[[package]]
+name = "brotli-decompressor"
+version = "2.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+]
+
+[[package]]
+name = "bstr"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
+dependencies = [
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "bytemuck"
+version = "1.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea31d69bda4949c1c1562c1e6f042a1caefac98cdc8a298260a2ff41c1e2d42b"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "jobserver",
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "comfy-table"
+version = "7.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686"
+dependencies = [
+ "crossterm",
+ "strum",
+ "strum_macros",
+ "unicode-width",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
+name = "crossterm"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
+dependencies = [
+ "bitflags 2.4.2",
+ "crossterm_winapi",
+ "libc",
+ "parking_lot",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "platforms",
+ "rustc_version",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
+
+[[package]]
+name = "der"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "enum_dispatch"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e"
+dependencies = [
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "ethnum"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "fast-float"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c"
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382"
+
+[[package]]
+name = "flate2"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "foreign_vec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee1b05cbd864bcaecbd3455d6d967862d446e4ebfc3c2e5e5b9841e53cba6673"
+
+[[package]]
+name = "futures"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+ "rayon",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3"
+
+[[package]]
+name = "home"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
+[[package]]
+name = "jobserver"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "lz4"
+version = "1.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1"
+dependencies = [
+ "libc",
+ "lz4-sys",
+]
+
+[[package]]
+name = "lz4-sys"
+version = "1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
+[[package]]
+name = "memmap2"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "multiversion"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2c7b9d7fe61760ce5ea19532ead98541f6b4c495d87247aff9826445cf6872a"
+dependencies = [
+ "multiversion-macros",
+ "target-features",
+]
+
+[[package]]
+name = "multiversion-macros"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26a83d8500ed06d68877e9de1dde76c1dbb83885dcdbda4ef44ccbc3fbda2ac8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "target-features",
+]
+
+[[package]]
+name = "nix-compat"
+version = "0.1.0"
+dependencies = [
+ "bitflags 2.4.2",
+ "bstr",
+ "data-encoding",
+ "ed25519",
+ "ed25519-dalek",
+ "glob",
+ "nom",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "now"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d89e9874397a1f0a52fc1f197a8effd9735223cb2390e9dcc83ac6cd02923d0"
+dependencies = [
+ "chrono",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "owning_ref"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
+dependencies = [
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "parquet-format-safe"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1131c54b167dd4e4799ce762e1ab01549ebb94d5bdd13e6ec1b467491c378e1f"
+dependencies = [
+ "async-trait",
+ "futures",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
+
+[[package]]
+name = "planus"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1691dd09e82f428ce8d6310bd6d5da2557c82ff17694d2a32cad7242aea89f"
+dependencies = [
+ "array-init-cursor",
+]
+
+[[package]]
+name = "platforms"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
+
+[[package]]
+name = "polars"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938048fcda6a8e2ace6eb168bee1b415a92423ce51e418b853bf08fc40349b6b"
+dependencies = [
+ "getrandom",
+ "polars-core",
+ "polars-io",
+ "polars-lazy",
+ "polars-ops",
+ "polars-sql",
+ "polars-time",
+ "version_check",
+]
+
+[[package]]
+name = "polars-arrow"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce68a02f698ff7787c261aea1b4c040a8fe183a8fb200e2436d7f35d95a1b86f"
+dependencies = [
+ "ahash",
+ "arrow-format",
+ "atoi_simd",
+ "bytemuck",
+ "chrono",
+ "dyn-clone",
+ "either",
+ "ethnum",
+ "fast-float",
+ "foreign_vec",
+ "futures",
+ "getrandom",
+ "hashbrown",
+ "itoa",
+ "lz4",
+ "multiversion",
+ "num-traits",
+ "polars-error",
+ "polars-utils",
+ "ryu",
+ "simdutf8",
+ "streaming-iterator",
+ "strength_reduce",
+ "version_check",
+ "zstd",
+]
+
+[[package]]
+name = "polars-compute"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b14fbc5f141b29b656a4cec4802632e5bff10bf801c6809c6bbfbd4078a044dd"
+dependencies = [
+ "bytemuck",
+ "num-traits",
+ "polars-arrow",
+ "polars-utils",
+ "version_check",
+]
+
+[[package]]
+name = "polars-core"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0f5efe734b6cbe5f97ea769be8360df5324fade396f1f3f5ad7fe9360ca4a23"
+dependencies = [
+ "ahash",
+ "bitflags 2.4.2",
+ "bytemuck",
+ "chrono",
+ "comfy-table",
+ "either",
+ "hashbrown",
+ "indexmap",
+ "num-traits",
+ "once_cell",
+ "polars-arrow",
+ "polars-compute",
+ "polars-error",
+ "polars-row",
+ "polars-utils",
+ "rand",
+ "rand_distr",
+ "rayon",
+ "regex",
+ "smartstring",
+ "thiserror",
+ "version_check",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "polars-error"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6396de788f99ebfc9968e7b6f523e23000506cde4ba6dfc62ae4ce949002a886"
+dependencies = [
+ "arrow-format",
+ "regex",
+ "simdutf8",
+ "thiserror",
+]
+
+[[package]]
+name = "polars-io"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d0458efe8946f4718fd352f230c0db5a37926bd0d2bd25af79dc24746abaaea"
+dependencies = [
+ "ahash",
+ "async-trait",
+ "atoi_simd",
+ "bytes",
+ "chrono",
+ "fast-float",
+ "futures",
+ "home",
+ "itoa",
+ "memchr",
+ "memmap2",
+ "num-traits",
+ "once_cell",
+ "percent-encoding",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-parquet",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "ryu",
+ "simdutf8",
+ "smartstring",
+ "tokio",
+ "tokio-util",
+]
+
+[[package]]
+name = "polars-lazy"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d7105b40905bb38e8fc4a7fd736594b7491baa12fad3ac492969ca221a1b5d5"
+dependencies = [
+ "ahash",
+ "bitflags 2.4.2",
+ "glob",
+ "once_cell",
+ "polars-arrow",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-pipe",
+ "polars-plan",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "smartstring",
+ "version_check",
+]
+
+[[package]]
+name = "polars-ops"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e09afc456ab11e75e5dcb43e00a01c71f3a46a2781e450054acb6bb096ca78e"
+dependencies = [
+ "ahash",
+ "argminmax",
+ "bytemuck",
+ "either",
+ "hashbrown",
+ "indexmap",
+ "memchr",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-error",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "smartstring",
+ "version_check",
+]
+
+[[package]]
+name = "polars-parquet"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ba24d67b1f64ab85143033dd46fa090b13c0f74acdf91b0780c16aecf005e3d"
+dependencies = [
+ "ahash",
+ "async-stream",
+ "base64",
+ "brotli",
+ "ethnum",
+ "flate2",
+ "futures",
+ "lz4",
+ "num-traits",
+ "parquet-format-safe",
+ "polars-arrow",
+ "polars-error",
+ "polars-utils",
+ "seq-macro",
+ "simdutf8",
+ "snap",
+ "streaming-decompression",
+ "zstd",
+]
+
+[[package]]
+name = "polars-pipe"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b7ead073cc3917027d77b59861a9f071db47125de9314f8907db1a0a3e4100"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-queue",
+ "enum_dispatch",
+ "hashbrown",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-plan",
+ "polars-row",
+ "polars-utils",
+ "rayon",
+ "smartstring",
+ "version_check",
+]
+
+[[package]]
+name = "polars-plan"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384a175624d050c31c473ee11df9d7af5d729ae626375e522158cfb3d150acd0"
+dependencies = [
+ "ahash",
+ "bytemuck",
+ "once_cell",
+ "percent-encoding",
+ "polars-arrow",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-parquet",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "smartstring",
+ "strum_macros",
+ "version_check",
+]
+
+[[package]]
+name = "polars-row"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32322f7acbb83db3e9c7697dc821be73d06238da89c817dcc8bc1549a5e9c72f"
+dependencies = [
+ "polars-arrow",
+ "polars-error",
+ "polars-utils",
+]
+
+[[package]]
+name = "polars-sql"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f0b4c6ddffdfd0453e84bc3918572c633014d661d166654399cf93752aa95b5"
+dependencies = [
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-lazy",
+ "polars-plan",
+ "rand",
+ "serde",
+ "serde_json",
+ "sqlparser",
+]
+
+[[package]]
+name = "polars-time"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee2649fc96bd1b6584e0e4a4b3ca7d22ed3d117a990e63ad438ecb26f7544d0"
+dependencies = [
+ "atoi",
+ "chrono",
+ "now",
+ "once_cell",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-ops",
+ "polars-utils",
+ "regex",
+ "smartstring",
+]
+
+[[package]]
+name = "polars-utils"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b174ca4a77ad47d7b91a0460aaae65bbf874c8bfbaaa5308675dadef3976bbda"
+dependencies = [
+ "ahash",
+ "bytemuck",
+ "hashbrown",
+ "indexmap",
+ "num-traits",
+ "once_cell",
+ "polars-error",
+ "rayon",
+ "smartstring",
+ "sysinfo",
+ "version_check",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_distr"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+dependencies = [
+ "num-traits",
+ "rand",
+]
+
+[[package]]
+name = "rayon"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "ryu"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
+
+[[package]]
+name = "seq-macro"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
+
+[[package]]
+name = "serde"
+version = "1.0.196"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.196"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.113"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "simdutf8"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
+
+[[package]]
+name = "smartstring"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
+dependencies = [
+ "autocfg",
+ "static_assertions",
+ "version_check",
+]
+
+[[package]]
+name = "snap"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b"
+
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "sqlparser"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743b4dc2cbde11890ccb254a8fc9d537fa41b36da00de2a1c5e9848c9bc42bd7"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "streaming-decompression"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf6cc3b19bfb128a8ad11026086e31d3ce9ad23f8ea37354b31383a187c44cf3"
+dependencies = [
+ "fallible-streaming-iterator",
+]
+
+[[package]]
+name = "streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
+
+[[package]]
+name = "strength_reduce"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
+
+[[package]]
+name = "strum"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
+
+[[package]]
+name = "strum_macros"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.30.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2"
+dependencies = [
+ "cfg-if",
+ "core-foundation-sys",
+ "libc",
+ "ntapi",
+ "once_cell",
+ "windows",
+]
+
+[[package]]
+name = "target-features"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfb5fa503293557c5158bd215fdc225695e567a77e453f5d4452a50a193969bd"
+
+[[package]]
+name = "thiserror"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "tokio"
+version = "1.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
+
+[[package]]
+name = "weave"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "hashbrown",
+ "nix-compat",
+ "owning_ref",
+ "polars",
+ "rayon",
+ "tokio",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+dependencies = [
+ "windows-core",
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
+[[package]]
+name = "xxhash-rust"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53be06678ed9e83edb1745eb72efc0bbcd7b5c3c35711a860906aed827a13d61"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+
+[[package]]
+name = "zstd"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.9+zstd.1.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
diff --git a/tvix/tools/weave/Cargo.nix b/tvix/tools/weave/Cargo.nix
new file mode 100644
index 0000000000..650b655a2d
--- /dev/null
+++ b/tvix/tools/weave/Cargo.nix
@@ -0,0 +1,8809 @@
+# This file was @generated by crate2nix 0.13.0 with the command:
+#   "generate" "--all-features"
+# See https://github.com/kolloch/crate2nix for more info.
+
+{ nixpkgs ? <nixpkgs>
+, pkgs ? import nixpkgs { config = { }; }
+, lib ? pkgs.lib
+, stdenv ? pkgs.stdenv
+, buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate
+  # This is used as the `crateOverrides` argument for `buildRustCrate`.
+, defaultCrateOverrides ? pkgs.defaultCrateOverrides
+  # The features to enable for the root_crate or the workspace_members.
+, rootFeatures ? [ "default" ]
+  # If true, throw errors instead of issueing deprecation warnings.
+, strictDeprecation ? false
+  # Used for conditional compilation based on CPU feature detection.
+, targetFeatures ? [ ]
+  # Whether to perform release builds: longer compile times, faster binaries.
+, release ? true
+  # Additional crate2nix configuration if it exists.
+, crateConfig ? if builtins.pathExists ./crate-config.nix
+  then pkgs.callPackage ./crate-config.nix { }
+  else { }
+}:
+
+rec {
+  #
+  # "public" attributes that we attempt to keep stable with new versions of crate2nix.
+  #
+
+  rootCrate = rec {
+    packageId = "weave";
+
+    # Use this attribute to refer to the derivation building your root crate package.
+    # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }.
+    build = internal.buildRustCrateWithFeatures {
+      inherit packageId;
+    };
+
+    # Debug support which might change between releases.
+    # File a bug if you depend on any for non-debug work!
+    debug = internal.debugCrate { inherit packageId; };
+  };
+  # Refer your crate build derivation by name here.
+  # You can override the features with
+  # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }.
+  workspaceMembers = {
+    "weave" = rec {
+      packageId = "weave";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "weave";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+  };
+
+  # A derivation that joins the outputs of all workspace members together.
+  allWorkspaceMembers = pkgs.symlinkJoin {
+    name = "all-workspace-members";
+    paths =
+      let members = builtins.attrValues workspaceMembers;
+      in builtins.map (m: m.build) members;
+  };
+
+  #
+  # "internal" ("private") attributes that may change in every new version of crate2nix.
+  #
+
+  internal = rec {
+    # Build and dependency information for crates.
+    # Many of the fields are passed one-to-one to buildRustCrate.
+    #
+    # Noteworthy:
+    # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate.
+    #   but with additional information which is used during dependency/feature resolution.
+    # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging.
+    # * `devDependencies` as of now not used by `buildRustCrate` but used to
+    #   inject test dependencies into the build
+
+    crates = {
+      "addr2line" = rec {
+        crateName = "addr2line";
+        version = "0.21.0";
+        edition = "2018";
+        sha256 = "1jx0k3iwyqr8klqbzk6kjvr496yd94aspis10vwsj5wy7gib4c4a";
+        dependencies = [
+          {
+            name = "gimli";
+            packageId = "gimli";
+            usesDefaultFeatures = false;
+            features = [ "read" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "rustc-demangle" "cpp_demangle" "std-object" "fallible-iterator" "smallvec" "memmap2" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "memmap2" = [ "dep:memmap2" ];
+          "object" = [ "dep:object" ];
+          "rustc-demangle" = [ "dep:rustc-demangle" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "gimli/rustc-dep-of-std" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "gimli/std" ];
+          "std-object" = [ "std" "object" "object/std" "object/compression" "gimli/endian-reader" ];
+        };
+      };
+      "adler" = rec {
+        crateName = "adler";
+        version = "1.0.2";
+        edition = "2015";
+        sha256 = "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj";
+        authors = [
+          "Jonas Schievink <jonasschievink@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "ahash" = rec {
+        crateName = "ahash";
+        version = "0.8.7";
+        edition = "2018";
+        sha256 = "008xw6gigwnf0q01ic4ar2y4dqfnzn3kyys6vd4cvfa3imjakhvp";
+        authors = [
+          "Tom Kaitchuck <Tom.Kaitchuck@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!(("arm" == target."arch" or null) && ("none" == target."os" or null)));
+            features = [ "alloc" ];
+          }
+          {
+            name = "zerocopy";
+            packageId = "zerocopy";
+            usesDefaultFeatures = false;
+            features = [ "simd" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "atomic-polyfill" = [ "dep:atomic-polyfill" "once_cell/atomic-polyfill" ];
+          "compile-time-rng" = [ "const-random" ];
+          "const-random" = [ "dep:const-random" ];
+          "default" = [ "std" "runtime-rng" ];
+          "getrandom" = [ "dep:getrandom" ];
+          "runtime-rng" = [ "getrandom" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "getrandom" "runtime-rng" "std" ];
+      };
+      "aho-corasick" = rec {
+        crateName = "aho-corasick";
+        version = "1.1.2";
+        edition = "2021";
+        sha256 = "1w510wnixvlgimkx1zjbvlxh6xps2vjgfqgwf5a6adlbjp5rv5mj";
+        libName = "aho_corasick";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf-literal" ];
+          "logging" = [ "dep:log" ];
+          "perf-literal" = [ "dep:memchr" ];
+          "std" = [ "memchr?/std" ];
+        };
+        resolvedDefaultFeatures = [ "perf-literal" "std" ];
+      };
+      "alloc-no-stdlib" = rec {
+        crateName = "alloc-no-stdlib";
+        version = "2.0.4";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "1cy6r2sfv5y5cigv86vms7n5nlwhx1rbyxwcraqnmm1rxiib2yyc";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+        ];
+        features = { };
+      };
+      "alloc-stdlib" = rec {
+        crateName = "alloc-stdlib";
+        version = "0.2.2";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "1kkfbld20ab4165p29v172h8g0wvq8i06z8vnng14whw0isq5ywl";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+        ];
+        dependencies = [
+          {
+            name = "alloc-no-stdlib";
+            packageId = "alloc-no-stdlib";
+          }
+        ];
+        features = { };
+      };
+      "allocator-api2" = rec {
+        crateName = "allocator-api2";
+        version = "0.2.16";
+        edition = "2018";
+        sha256 = "1iayppgq4wqbfbfcqmsbwgamj0s65012sskfvyx07pxavk3gyhh9";
+        authors = [
+          "Zakarum <zaq.dev@icloud.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "android-tzdata" = rec {
+        crateName = "android-tzdata";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        authors = [
+          "RumovZ"
+        ];
+
+      };
+      "android_system_properties" = rec {
+        crateName = "android_system_properties";
+        version = "0.1.5";
+        edition = "2018";
+        sha256 = "04b3wrz12837j7mdczqd95b732gw5q7q66cv4yn4646lvccp57l1";
+        authors = [
+          "Nicolas Silva <nical@fastmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "anyhow" = rec {
+        crateName = "anyhow";
+        version = "1.0.79";
+        edition = "2018";
+        sha256 = "1ji5irqiwr8yprgqj8zvnli7zd7fz9kzaiddq44jnrl2l289h3h8";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            optional = true;
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "backtrace" "default" "std" ];
+      };
+      "argminmax" = rec {
+        crateName = "argminmax";
+        version = "0.6.1";
+        edition = "2021";
+        sha256 = "1lnvpkvdsvdbsinhik6srx5c2j3gqkaj92iz93pnbdr9cjs0h890";
+        authors = [
+          "Jeroen Van Der Donckt"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "arrow" = [ "dep:arrow" ];
+          "arrow2" = [ "dep:arrow2" ];
+          "default" = [ "nightly_simd" "float" ];
+          "half" = [ "dep:half" ];
+          "ndarray" = [ "dep:ndarray" ];
+        };
+        resolvedDefaultFeatures = [ "float" ];
+      };
+      "array-init-cursor" = rec {
+        crateName = "array-init-cursor";
+        version = "0.2.0";
+        edition = "2021";
+        sha256 = "0xpbqf7qkvzplpjd7f0wbcf2n1v9vygdccwxkd1amxp4il0hlzdz";
+
+      };
+      "arrow-format" = rec {
+        crateName = "arrow-format";
+        version = "0.8.1";
+        edition = "2018";
+        sha256 = "1irj67p6c224dzw86jr7j3z9r5zfid52gy6ml8rdqk4r2si4x207";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "planus";
+            packageId = "planus";
+            optional = true;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "derive" "std" ];
+          }
+        ];
+        features = {
+          "flight-data" = [ "prost" "prost-derive" ];
+          "flight-service" = [ "flight-data" "tonic" ];
+          "full" = [ "ipc" "flight-data" "flight-service" ];
+          "ipc" = [ "planus" "serde" ];
+          "planus" = [ "dep:planus" ];
+          "prost" = [ "dep:prost" ];
+          "prost-derive" = [ "dep:prost-derive" ];
+          "serde" = [ "dep:serde" ];
+          "tonic" = [ "dep:tonic" ];
+        };
+        resolvedDefaultFeatures = [ "default" "ipc" "planus" "serde" ];
+      };
+      "async-stream" = rec {
+        crateName = "async-stream";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream-impl";
+            packageId = "async-stream-impl";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "async-stream-impl" = rec {
+        crateName = "async-stream-impl";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
+        procMacro = true;
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "async-trait" = rec {
+        crateName = "async-trait";
+        version = "0.1.77";
+        edition = "2021";
+        sha256 = "1adf1jh2yg39rkpmqjqyr9xyd6849p0d95425i6imgbhx0syx069";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "atoi" = rec {
+        crateName = "atoi";
+        version = "2.0.0";
+        edition = "2021";
+        sha256 = "0a05h42fggmy7h0ajjv6m7z72l924i7igbx13hk9d8pyign9k3gj";
+        authors = [
+          "Markus Klein"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "num-traits/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "atoi_simd" = rec {
+        crateName = "atoi_simd";
+        version = "0.15.6";
+        edition = "2018";
+        sha256 = "1a98kvaqyhb1shi2c6qhvklahc7ckvpmibcy319i6g1i9xqkgq4s";
+        authors = [
+          "Dmitry Rodionov <gh@rdmtr.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "arrayvec/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "autocfg" = rec {
+        crateName = "autocfg";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "1ylp3cb47ylzabimazvbz9ms6ap784zhb6syaz6c1jqpmcmq0s6l";
+        authors = [
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+
+      };
+      "backtrace" = rec {
+        crateName = "backtrace";
+        version = "0.3.69";
+        edition = "2018";
+        sha256 = "0dsq23dhw4pfndkx2nsa1ml2g31idm7ss7ljxp8d57avygivg290";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "addr2line";
+            packageId = "addr2line";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "object";
+            packageId = "object";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+            features = [ "read_core" "elf" "macho" "pe" "unaligned" "archive" ];
+          }
+          {
+            name = "rustc-demangle";
+            packageId = "rustc-demangle";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "std" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "serialize-rustc" = [ "rustc-serialize" ];
+          "serialize-serde" = [ "serde" ];
+          "verify-winapi" = [ "winapi/dbghelp" "winapi/handleapi" "winapi/libloaderapi" "winapi/memoryapi" "winapi/minwindef" "winapi/processthreadsapi" "winapi/synchapi" "winapi/tlhelp32" "winapi/winbase" "winapi/winnt" ];
+          "winapi" = [ "dep:winapi" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64" = rec {
+        crateName = "base64";
+        version = "0.21.7";
+        edition = "2018";
+        sha256 = "0rw52yvsk75kar9wgqfwgb414kvil1gn7mqkrhn9zf1537mpsacx";
+        authors = [
+          "Alice Maz <alice@alicemaz.com>"
+          "Marshall Pierce <marshall@mpierce.org>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "base64ct" = rec {
+        crateName = "base64ct";
+        version = "1.6.0";
+        edition = "2021";
+        sha256 = "0nvdba4jb8aikv60az40x2w1y96sjdq8z3yp09rwzmkhiwv1lg4c";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "bitflags 1.3.2" = rec {
+        crateName = "bitflags";
+        version = "1.3.2";
+        edition = "2018";
+        sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bitflags 2.4.2" = rec {
+        crateName = "bitflags";
+        version = "2.4.2";
+        edition = "2021";
+        sha256 = "1pqd142hyqlzr7p9djxq2ff0jx07a2sb2xp9lhw69cbf80s0jmzd";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "block-buffer" = rec {
+        crateName = "block-buffer";
+        version = "0.10.4";
+        edition = "2018";
+        sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+
+      };
+      "brotli" = rec {
+        crateName = "brotli";
+        version = "3.4.0";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "03qhcq09a6f8y4gm0bmsn7jrq5804cwpkcx3fyay1g7lgsj78q2i";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+          "The Brotli Authors"
+        ];
+        dependencies = [
+          {
+            name = "alloc-no-stdlib";
+            packageId = "alloc-no-stdlib";
+          }
+          {
+            name = "alloc-stdlib";
+            packageId = "alloc-stdlib";
+            optional = true;
+          }
+          {
+            name = "brotli-decompressor";
+            packageId = "brotli-decompressor";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc-stdlib" = [ "dep:alloc-stdlib" ];
+          "benchmark" = [ "brotli-decompressor/benchmark" ];
+          "default" = [ "std" "ffi-api" ];
+          "disable-timer" = [ "brotli-decompressor/disable-timer" ];
+          "seccomp" = [ "brotli-decompressor/seccomp" ];
+          "sha2" = [ "dep:sha2" ];
+          "std" = [ "alloc-stdlib" "brotli-decompressor/std" ];
+          "validation" = [ "sha2" ];
+        };
+        resolvedDefaultFeatures = [ "alloc-stdlib" "default" "ffi-api" "std" ];
+      };
+      "brotli-decompressor" = rec {
+        crateName = "brotli-decompressor";
+        version = "2.5.1";
+        edition = "2015";
+        crateBin = [ ];
+        sha256 = "0kyyh9701dwqzwvn2frff4ww0zibikqd1s1xvl7n1pfpc3z4lbjf";
+        authors = [
+          "Daniel Reiter Horn <danielrh@dropbox.com>"
+          "The Brotli Authors"
+        ];
+        dependencies = [
+          {
+            name = "alloc-no-stdlib";
+            packageId = "alloc-no-stdlib";
+          }
+          {
+            name = "alloc-stdlib";
+            packageId = "alloc-stdlib";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc-stdlib" = [ "dep:alloc-stdlib" ];
+          "default" = [ "std" ];
+          "std" = [ "alloc-stdlib" ];
+          "unsafe" = [ "alloc-no-stdlib/unsafe" "alloc-stdlib/unsafe" ];
+        };
+        resolvedDefaultFeatures = [ "alloc-stdlib" "std" ];
+      };
+      "bstr" = rec {
+        crateName = "bstr";
+        version = "1.9.0";
+        edition = "2021";
+        sha256 = "1p6hzf3wqwwynv6w4pn17jg21amfafph9kb5sfvf1idlli8h13y4";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "dfa-search" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "memchr/alloc" "serde?/alloc" ];
+          "default" = [ "std" "unicode" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" "memchr/std" "serde?/std" ];
+          "unicode" = [ "dep:regex-automata" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "serde" "std" "unicode" ];
+      };
+      "bumpalo" = rec {
+        crateName = "bumpalo";
+        version = "3.14.0";
+        edition = "2021";
+        sha256 = "1v4arnv9kwk54v5d0qqpv4vyw2sgr660nk0w3apzixi1cm3yfc3z";
+        authors = [
+          "Nick Fitzgerald <fitzgen@gmail.com>"
+        ];
+        features = {
+          "allocator-api2" = [ "dep:allocator-api2" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bytemuck" = rec {
+        crateName = "bytemuck";
+        version = "1.14.2";
+        edition = "2018";
+        sha256 = "0aylwb0l3zx2c212k2nwik4zmbhw5826y7icav0w2ja9vadxccga";
+        authors = [
+          "Lokathor <zefria@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytemuck_derive";
+            packageId = "bytemuck_derive";
+            optional = true;
+          }
+        ];
+        features = {
+          "bytemuck_derive" = [ "dep:bytemuck_derive" ];
+          "derive" = [ "bytemuck_derive" ];
+          "extern_crate_std" = [ "extern_crate_alloc" ];
+        };
+        resolvedDefaultFeatures = [ "bytemuck_derive" "derive" "extern_crate_alloc" ];
+      };
+      "bytemuck_derive" = rec {
+        crateName = "bytemuck_derive";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "1cgj75df2v32l4fmvnp25xxkkz4lp6hz76f7hfhd55wgbzmvfnln";
+        procMacro = true;
+        authors = [
+          "Lokathor <zefria@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+          }
+        ];
+
+      };
+      "bytes" = rec {
+        crateName = "bytes";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "cc" = rec {
+        crateName = "cc";
+        version = "1.0.83";
+        edition = "2018";
+        crateBin = [ ];
+        sha256 = "1l643zidlb5iy1dskc5ggqs4wqa29a02f44piczqc8zcnsq4y5zi";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "jobserver";
+            packageId = "jobserver";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        features = {
+          "jobserver" = [ "dep:jobserver" ];
+          "parallel" = [ "jobserver" ];
+        };
+        resolvedDefaultFeatures = [ "jobserver" "parallel" ];
+      };
+      "cfg-if" = rec {
+        crateName = "cfg-if";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "chrono" = rec {
+        crateName = "chrono";
+        version = "0.4.33";
+        edition = "2021";
+        sha256 = "1szr180x4srkwvmzq5ahqnf3m7yjjllfmgp7k3hsrr556l76j4wz";
+        dependencies = [
+          {
+            name = "android-tzdata";
+            packageId = "android-tzdata";
+            optional = true;
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "iana-time-zone";
+            packageId = "iana-time-zone";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+            features = [ "fallback" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "android-tzdata" = [ "dep:android-tzdata" ];
+          "arbitrary" = [ "dep:arbitrary" ];
+          "clock" = [ "winapi" "iana-time-zone" "android-tzdata" "now" ];
+          "default" = [ "clock" "std" "oldtime" "wasmbind" ];
+          "iana-time-zone" = [ "dep:iana-time-zone" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "now" = [ "std" ];
+          "pure-rust-locales" = [ "dep:pure-rust-locales" ];
+          "rkyv" = [ "dep:rkyv" "rkyv/size_32" ];
+          "rkyv-16" = [ "dep:rkyv" "rkyv?/size_16" ];
+          "rkyv-32" = [ "dep:rkyv" "rkyv?/size_32" ];
+          "rkyv-64" = [ "dep:rkyv" "rkyv?/size_64" ];
+          "rkyv-validation" = [ "rkyv?/validation" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "unstable-locales" = [ "pure-rust-locales" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+          "wasmbind" = [ "wasm-bindgen" "js-sys" ];
+          "winapi" = [ "windows-targets" ];
+          "windows-targets" = [ "dep:windows-targets" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "android-tzdata" "clock" "iana-time-zone" "now" "std" "winapi" "windows-targets" ];
+      };
+      "comfy-table" = rec {
+        crateName = "comfy-table";
+        version = "7.1.0";
+        edition = "2021";
+        sha256 = "11i6sm6vznv9982hqpbrba43vfd7vv7zqzlywdc4qykvdhyh8r3w";
+        authors = [
+          "Arne Beer <contact@arne.beer>"
+        ];
+        dependencies = [
+          {
+            name = "crossterm";
+            packageId = "crossterm";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (!(target."windows" or false));
+          }
+          {
+            name = "crossterm";
+            packageId = "crossterm";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."windows" or false);
+            features = [ "windows" ];
+          }
+          {
+            name = "strum";
+            packageId = "strum";
+          }
+          {
+            name = "strum_macros";
+            packageId = "strum_macros";
+          }
+          {
+            name = "unicode-width";
+            packageId = "unicode-width";
+          }
+        ];
+        features = {
+          "console" = [ "dep:console" ];
+          "crossterm" = [ "dep:crossterm" ];
+          "custom_styling" = [ "console" ];
+          "default" = [ "tty" ];
+          "reexport_crossterm" = [ "tty" ];
+          "tty" = [ "crossterm" ];
+        };
+        resolvedDefaultFeatures = [ "crossterm" "tty" ];
+      };
+      "const-oid" = rec {
+        crateName = "const-oid";
+        version = "0.9.6";
+        edition = "2021";
+        sha256 = "1y0jnqaq7p2wvspnx7qj76m7hjcqpz73qzvr9l2p9n2s51vr6if2";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+        };
+      };
+      "core-foundation-sys" = rec {
+        crateName = "core-foundation-sys";
+        version = "0.8.6";
+        edition = "2018";
+        sha256 = "13w6sdf06r0hn7bx2b45zxsg1mm2phz34jikm6xc5qrbr6djpsh6";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "default" = [ "link" ];
+        };
+        resolvedDefaultFeatures = [ "default" "link" ];
+      };
+      "cpufeatures" = rec {
+        crateName = "cpufeatures";
+        version = "0.2.12";
+        edition = "2018";
+        sha256 = "012m7rrak4girqlii3jnqwrr73gv1i980q4wra5yyyhvzwk5xzjk";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-linux-android");
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("apple" == target."vendor" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("loongarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+        ];
+
+      };
+      "crc32fast" = rec {
+        crateName = "crc32fast";
+        version = "1.3.2";
+        edition = "2015";
+        sha256 = "03c8f29yx293yf43xar946xbls1g60c207m9drf8ilqhr25vsh5m";
+        authors = [
+          "Sam Rijs <srijs@airpost.net>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crossbeam-channel" = rec {
+        crateName = "crossbeam-channel";
+        version = "0.5.11";
+        edition = "2021";
+        sha256 = "16v48qdflpw3hgdik70bhsj7hympna79q7ci47rw0mlgnxsw2v8p";
+        dependencies = [
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crossbeam-deque" = rec {
+        crateName = "crossbeam-deque";
+        version = "0.8.5";
+        edition = "2021";
+        sha256 = "03bp38ljx4wj6vvy4fbhx41q8f585zyqix6pncz1mkz93z08qgv1";
+        dependencies = [
+          {
+            name = "crossbeam-epoch";
+            packageId = "crossbeam-epoch";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "crossbeam-epoch/std" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crossbeam-epoch" = rec {
+        crateName = "crossbeam-epoch";
+        version = "0.9.18";
+        edition = "2021";
+        sha256 = "03j2np8llwf376m3fxqx859mgp9f83hj1w34153c7a9c7i5ar0jv";
+        dependencies = [
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "loom" = [ "loom-crate" "crossbeam-utils/loom" ];
+          "loom-crate" = [ "dep:loom-crate" ];
+          "nightly" = [ "crossbeam-utils/nightly" ];
+          "std" = [ "alloc" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "crossbeam-queue" = rec {
+        crateName = "crossbeam-queue";
+        version = "0.3.11";
+        edition = "2021";
+        sha256 = "0d8y8y3z48r9javzj67v3p2yfswd278myz1j9vzc4sp7snslc0yz";
+        dependencies = [
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "nightly" = [ "crossbeam-utils/nightly" ];
+          "std" = [ "alloc" "crossbeam-utils/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "crossbeam-utils" = rec {
+        crateName = "crossbeam-utils";
+        version = "0.8.19";
+        edition = "2021";
+        sha256 = "0iakrb1b8fjqrag7wphl94d10irhbh2fw1g444xslsywqyn3p3i4";
+        features = {
+          "default" = [ "std" ];
+          "loom" = [ "dep:loom" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "crossterm" = rec {
+        crateName = "crossterm";
+        version = "0.27.0";
+        edition = "2021";
+        sha256 = "1pr413ki440xgddlmkrc4j1bfx1h8rpmll87zn8ykja1bm2gwxpl";
+        authors = [
+          "T. Post"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "crossterm_winapi";
+            packageId = "crossterm_winapi";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+            features = [ "winuser" "winerror" ];
+          }
+        ];
+        features = {
+          "default" = [ "bracketed-paste" "windows" "events" ];
+          "event-stream" = [ "dep:futures-core" "events" ];
+          "events" = [ "dep:mio" "dep:signal-hook" "dep:signal-hook-mio" ];
+          "filedescriptor" = [ "dep:filedescriptor" ];
+          "serde" = [ "dep:serde" "bitflags/serde" ];
+          "use-dev-tty" = [ "filedescriptor" ];
+          "windows" = [ "dep:winapi" "dep:crossterm_winapi" ];
+        };
+        resolvedDefaultFeatures = [ "windows" ];
+      };
+      "crossterm_winapi" = rec {
+        crateName = "crossterm_winapi";
+        version = "0.9.1";
+        edition = "2018";
+        sha256 = "0axbfb2ykbwbpf1hmxwpawwfs8wvmkcka5m561l7yp36ldi7rpdc";
+        authors = [
+          "T. Post"
+        ];
+        dependencies = [
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: (target."windows" or false);
+            features = [ "winbase" "consoleapi" "processenv" "handleapi" "synchapi" "impl-default" ];
+          }
+        ];
+
+      };
+      "crypto-common" = rec {
+        crateName = "crypto-common";
+        version = "0.1.6";
+        edition = "2018";
+        sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+            features = [ "more_lengths" ];
+          }
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        features = {
+          "getrandom" = [ "rand_core/getrandom" ];
+          "rand_core" = [ "dep:rand_core" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "curve25519-dalek" = rec {
+        crateName = "curve25519-dalek";
+        version = "4.1.2";
+        edition = "2021";
+        sha256 = "0j7kqchcgycs4a11gvlda93h9w2jr05nn4hjpfyh2kn94a4pnrqa";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: ("x86_64" == target."arch" or null);
+          }
+          {
+            name = "curve25519-dalek-derive";
+            packageId = "curve25519-dalek-derive";
+            target = { target, features }: ((!("fiat" == target."curve25519_dalek_backend" or null)) && (!("serial" == target."curve25519_dalek_backend" or null)) && ("x86_64" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "fiat-crypto";
+            packageId = "fiat-crypto";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("fiat" == target."curve25519_dalek_backend" or null);
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "platforms";
+            packageId = "platforms";
+          }
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "default" = [ "alloc" "precomputed-tables" "zeroize" ];
+          "digest" = [ "dep:digest" ];
+          "ff" = [ "dep:ff" ];
+          "group" = [ "dep:group" "rand_core" ];
+          "group-bits" = [ "group" "ff/bits" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "digest" "precomputed-tables" "zeroize" ];
+      };
+      "curve25519-dalek-derive" = rec {
+        crateName = "curve25519-dalek-derive";
+        version = "0.1.1";
+        edition = "2021";
+        sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "data-encoding" = rec {
+        crateName = "data-encoding";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "1rcbnwfmfxhlshzbn3r7srm3azqha3mn33yxyqxkzz2wpqcjm5ky";
+        authors = [
+          "Julien Cretin <git@ia0.eu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "der" = rec {
+        crateName = "der";
+        version = "0.7.8";
+        edition = "2021";
+        sha256 = "070bwiyr80800h31c5zd96ckkgagfjgnrrdmz3dzg2lccsd3dypz";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "const-oid";
+            packageId = "const-oid";
+            optional = true;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "arbitrary" = [ "dep:arbitrary" "const-oid?/arbitrary" "std" ];
+          "bytes" = [ "dep:bytes" "alloc" ];
+          "derive" = [ "dep:der_derive" ];
+          "flagset" = [ "dep:flagset" ];
+          "oid" = [ "dep:const-oid" ];
+          "pem" = [ "dep:pem-rfc7468" "alloc" "zeroize" ];
+          "std" = [ "alloc" ];
+          "time" = [ "dep:time" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "oid" "std" "zeroize" ];
+      };
+      "digest" = rec {
+        crateName = "digest";
+        version = "0.10.7";
+        edition = "2018";
+        sha256 = "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer";
+            optional = true;
+          }
+          {
+            name = "crypto-common";
+            packageId = "crypto-common";
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "block-buffer" = [ "dep:block-buffer" ];
+          "const-oid" = [ "dep:const-oid" ];
+          "core-api" = [ "block-buffer" ];
+          "default" = [ "core-api" ];
+          "dev" = [ "blobby" ];
+          "mac" = [ "subtle" ];
+          "oid" = [ "const-oid" ];
+          "rand_core" = [ "crypto-common/rand_core" ];
+          "std" = [ "alloc" "crypto-common/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "block-buffer" "core-api" "default" "std" ];
+      };
+      "dyn-clone" = rec {
+        crateName = "dyn-clone";
+        version = "1.0.16";
+        edition = "2018";
+        sha256 = "0pa9kas6a241pbx0q82ipwi4f7m7wwyzkkc725caky24gl4j4nsl";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "ed25519" = rec {
+        crateName = "ed25519";
+        version = "2.2.3";
+        edition = "2021";
+        sha256 = "0lydzdf26zbn82g7xfczcac9d7mzm3qgx934ijjrd5hjpjx32m8i";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "pkcs8";
+            packageId = "pkcs8";
+            optional = true;
+          }
+          {
+            name = "signature";
+            packageId = "signature";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "pkcs8?/alloc" ];
+          "default" = [ "std" ];
+          "pem" = [ "alloc" "pkcs8/pem" ];
+          "pkcs8" = [ "dep:pkcs8" ];
+          "serde" = [ "dep:serde" ];
+          "serde_bytes" = [ "serde" "dep:serde_bytes" ];
+          "std" = [ "pkcs8?/std" "signature/std" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "ed25519-dalek" = rec {
+        crateName = "ed25519-dalek";
+        version = "2.1.1";
+        edition = "2021";
+        sha256 = "0w88cafwglg9hjizldbmlza0ns3hls81zk1bcih3m5m3h67algaa";
+        authors = [
+          "isis lovecruft <isis@patternsinthevoid.net>"
+          "Tony Arcieri <bascule@gmail.com>"
+          "Michael Rosenberg <michael@mrosenberg.pub>"
+        ];
+        dependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" ];
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" "rand_core" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "curve25519-dalek/alloc" "ed25519/alloc" "serde?/alloc" "zeroize/alloc" ];
+          "asm" = [ "sha2/asm" ];
+          "batch" = [ "alloc" "merlin" "rand_core" ];
+          "default" = [ "fast" "std" "zeroize" ];
+          "digest" = [ "signature/digest" ];
+          "fast" = [ "curve25519-dalek/precomputed-tables" ];
+          "legacy_compatibility" = [ "curve25519-dalek/legacy_compatibility" ];
+          "merlin" = [ "dep:merlin" ];
+          "pem" = [ "alloc" "ed25519/pem" "pkcs8" ];
+          "pkcs8" = [ "ed25519/pkcs8" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" "ed25519/serde" ];
+          "signature" = [ "dep:signature" ];
+          "std" = [ "alloc" "ed25519/std" "serde?/std" "sha2/std" ];
+          "zeroize" = [ "dep:zeroize" "curve25519-dalek/zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fast" "std" "zeroize" ];
+      };
+      "either" = rec {
+        crateName = "either";
+        version = "1.9.0";
+        edition = "2018";
+        sha256 = "01qy3anr7jal5lpc20791vxrw0nl6vksb5j7x56q2fycgcyy8sm2";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "use_std" ];
+      };
+      "enum_dispatch" = rec {
+        crateName = "enum_dispatch";
+        version = "0.3.12";
+        edition = "2018";
+        sha256 = "03l998igqfzkykmj8i5qlbwhv2id9jn98fkkl82lv3dvg0q32cwg";
+        procMacro = true;
+        authors = [
+          "Anton Lazarev <https://antonok.com>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "equivalent" = rec {
+        crateName = "equivalent";
+        version = "1.0.1";
+        edition = "2015";
+        sha256 = "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl";
+
+      };
+      "ethnum" = rec {
+        crateName = "ethnum";
+        version = "1.5.0";
+        edition = "2021";
+        sha256 = "0b68ngvisb0d40vc6h30zlhghbb3mc8wlxjbf8gnmavk1dca435r";
+        authors = [
+          "Nicholas Rodrigues Lordello <nlordell@gmail.com>"
+        ];
+        features = {
+          "ethnum-intrinsics" = [ "dep:ethnum-intrinsics" ];
+          "llvm-intrinsics" = [ "ethnum-intrinsics" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "fallible-streaming-iterator" = rec {
+        crateName = "fallible-streaming-iterator";
+        version = "0.1.9";
+        edition = "2015";
+        sha256 = "0nj6j26p71bjy8h42x6jahx1hn0ng6mc2miwpgwnp8vnwqf4jq3k";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        features = { };
+      };
+      "fast-float" = rec {
+        crateName = "fast-float";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "0g7kfll3xyh99kc7r352lhljnwvgayxxa6saifb6725inikmyxlm";
+        authors = [
+          "Ivan Smirnov <i.s.smirnov@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "fiat-crypto" = rec {
+        crateName = "fiat-crypto";
+        version = "0.2.6";
+        edition = "2018";
+        sha256 = "10hkkkjynhibvchznkxx81gwxqarn9i5sgz40d6xxb8xzhsz8xhn";
+        authors = [
+          "Fiat Crypto library authors <jgross@mit.edu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+      };
+      "flate2" = rec {
+        crateName = "flate2";
+        version = "1.0.28";
+        edition = "2018";
+        sha256 = "03llhsh4gqdirnfxxb9g2w9n0721dyn4yjir3pz7z4vjaxb3yc26";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Josh Triplett <josh@joshtriplett.org>"
+        ];
+        dependencies = [
+          {
+            name = "crc32fast";
+            packageId = "crc32fast";
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "with-alloc" ];
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (("wasm32" == target."arch" or null) && (!("emscripten" == target."os" or null)));
+            features = [ "with-alloc" ];
+          }
+        ];
+        features = {
+          "any_zlib" = [ "any_impl" ];
+          "cloudflare-zlib-sys" = [ "dep:cloudflare-zlib-sys" ];
+          "cloudflare_zlib" = [ "any_zlib" "cloudflare-zlib-sys" ];
+          "default" = [ "rust_backend" ];
+          "libz-ng-sys" = [ "dep:libz-ng-sys" ];
+          "libz-sys" = [ "dep:libz-sys" ];
+          "miniz-sys" = [ "rust_backend" ];
+          "miniz_oxide" = [ "dep:miniz_oxide" ];
+          "rust_backend" = [ "miniz_oxide" "any_impl" ];
+          "zlib" = [ "any_zlib" "libz-sys" ];
+          "zlib-default" = [ "any_zlib" "libz-sys/default" ];
+          "zlib-ng" = [ "any_zlib" "libz-ng-sys" ];
+          "zlib-ng-compat" = [ "zlib" "libz-sys/zlib-ng" ];
+        };
+        resolvedDefaultFeatures = [ "any_impl" "miniz_oxide" "rust_backend" ];
+      };
+      "foreign_vec" = rec {
+        crateName = "foreign_vec";
+        version = "0.1.0";
+        edition = "2021";
+        sha256 = "0wv6p8yfahcqbdg2wg7wxgj4dm32g2b6spa5sg5sxg34v35ha6zf";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+        ];
+
+      };
+      "futures" = rec {
+        crateName = "futures";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1c04g14bccmprwsvx2j9m2blhwrynq7vhl151lsvcv4gi0b6jp34";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-executor";
+            packageId = "futures-executor";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" "futures-sink/alloc" "futures-channel/alloc" "futures-util/alloc" ];
+          "async-await" = [ "futures-util/async-await" "futures-util/async-await-macro" ];
+          "bilock" = [ "futures-util/bilock" ];
+          "compat" = [ "std" "futures-util/compat" ];
+          "default" = [ "std" "async-await" "executor" ];
+          "executor" = [ "std" "futures-executor/std" ];
+          "futures-executor" = [ "dep:futures-executor" ];
+          "io-compat" = [ "compat" "futures-util/io-compat" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "futures-io/std" "futures-sink/std" "futures-util/std" "futures-util/io" "futures-util/channel" ];
+          "thread-pool" = [ "executor" "futures-executor/thread-pool" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" "futures-channel/unstable" "futures-io/unstable" "futures-util/unstable" ];
+          "write-all-vectored" = [ "futures-util/write-all-vectored" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "default" "executor" "futures-executor" "std" ];
+      };
+      "futures-channel" = rec {
+        crateName = "futures-channel";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" ];
+          "default" = [ "std" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "sink" = [ "futures-sink" ];
+          "std" = [ "alloc" "futures-core/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "futures-sink" "sink" "std" ];
+      };
+      "futures-core" = rec {
+        crateName = "futures-core";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        features = {
+          "default" = [ "std" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-executor" = rec {
+        crateName = "futures-executor";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "07dh08gs9vfll2h36kq32q9xd86xm6lyl9xikmmwlkqnmrrgqxm5";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "std" = [ "futures-core/std" "futures-task/std" "futures-util/std" ];
+          "thread-pool" = [ "std" "num_cpus" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "futures-io" = rec {
+        crateName = "futures-io";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1hgh25isvsr4ybibywhr4dpys8mjnscw4wfxxwca70cn1gi26im4";
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "futures-macro" = rec {
+        crateName = "futures-macro";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "futures-sink" = rec {
+        crateName = "futures-sink";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-task" = rec {
+        crateName = "futures-task";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "futures-util" = rec {
+        crateName = "futures-util";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        dependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-io";
+            packageId = "futures-io";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "futures-macro";
+            packageId = "futures-macro";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "pin-utils";
+            packageId = "pin-utils";
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" ];
+          "async-await-macro" = [ "async-await" "futures-macro" ];
+          "channel" = [ "std" "futures-channel" ];
+          "compat" = [ "std" "futures_01" ];
+          "default" = [ "std" "async-await" "async-await-macro" ];
+          "futures-channel" = [ "dep:futures-channel" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-macro" = [ "dep:futures-macro" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "futures_01" = [ "dep:futures_01" ];
+          "io" = [ "std" "futures-io" "memchr" ];
+          "io-compat" = [ "io" "compat" "tokio-io" ];
+          "memchr" = [ "dep:memchr" ];
+          "portable-atomic" = [ "futures-core/portable-atomic" ];
+          "sink" = [ "futures-sink" ];
+          "slab" = [ "dep:slab" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "slab" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" ];
+          "write-all-vectored" = [ "io" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "async-await-macro" "channel" "futures-channel" "futures-io" "futures-macro" "futures-sink" "io" "memchr" "sink" "slab" "std" ];
+      };
+      "generic-array" = rec {
+        crateName = "generic-array";
+        version = "0.14.7";
+        edition = "2015";
+        sha256 = "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45";
+        libName = "generic_array";
+        authors = [
+          "BartΕ‚omiej KamiΕ„ski <fizyk20@gmail.com>"
+          "Aaron Trent <novacrazy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "more_lengths" ];
+      };
+      "getrandom" = rec {
+        crateName = "getrandom";
+        version = "0.2.12";
+        edition = "2018";
+        sha256 = "1d8jb9bv38nkwlqqdjcav6gxckgwc9g30pm3qq506rvncpm9400r";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            optional = true;
+            target = { target, features }: ((("wasm32" == target."arch" or null) || ("wasm64" == target."arch" or null)) && ("unknown" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: ((("wasm32" == target."arch" or null) || ("wasm64" == target."arch" or null)) && ("unknown" == target."os" or null));
+          }
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "js" = [ "wasm-bindgen" "js-sys" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "libc/rustc-dep-of-std" "wasi/rustc-dep-of-std" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "js" "js-sys" "std" "wasm-bindgen" ];
+      };
+      "gimli" = rec {
+        crateName = "gimli";
+        version = "0.28.1";
+        edition = "2018";
+        sha256 = "0lv23wc8rxvmjia3mcxc6hj9vkqnv1bqq0h8nzjcgf71mrxx6wa2";
+        features = {
+          "default" = [ "read-all" "write" ];
+          "endian-reader" = [ "read" "dep:stable_deref_trait" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "read" = [ "read-core" ];
+          "read-all" = [ "read" "std" "fallible-iterator" "endian-reader" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" ];
+          "std" = [ "fallible-iterator?/std" "stable_deref_trait?/std" ];
+          "write" = [ "dep:indexmap" ];
+        };
+        resolvedDefaultFeatures = [ "read" "read-core" ];
+      };
+      "glob" = rec {
+        crateName = "glob";
+        version = "0.3.1";
+        edition = "2015";
+        sha256 = "16zca52nglanv23q5qrwd5jinw3d3as5ylya6y1pbx47vkxvrynj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+
+      };
+      "hashbrown" = rec {
+        crateName = "hashbrown";
+        version = "0.14.3";
+        edition = "2021";
+        sha256 = "012nywlg0lj9kwanh69my5x67vjlfmzfi9a0rq4qvis2j8fil3r9";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "allocator-api2";
+            packageId = "allocator-api2";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+            optional = true;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "alloc" = [ "dep:alloc" ];
+          "allocator-api2" = [ "dep:allocator-api2" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "ahash" "inline-more" "allocator-api2" ];
+          "equivalent" = [ "dep:equivalent" ];
+          "nightly" = [ "allocator-api2?/nightly" "bumpalo/allocator_api" ];
+          "rayon" = [ "dep:rayon" ];
+          "rkyv" = [ "dep:rkyv" ];
+          "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "ahash" "allocator-api2" "default" "inline-more" "raw" "rayon" ];
+      };
+      "heck" = rec {
+        crateName = "heck";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1a7mqsnycv5z4z5vnv1k34548jzmc0ajic7c1j8jsaspnhw5ql4m";
+        authors = [
+          "Without Boats <woboats@gmail.com>"
+        ];
+        features = {
+          "unicode" = [ "unicode-segmentation" ];
+          "unicode-segmentation" = [ "dep:unicode-segmentation" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "hermit-abi" = rec {
+        crateName = "hermit-abi";
+        version = "0.3.5";
+        edition = "2021";
+        sha256 = "1hw2bxkzyvr0rbnpj0lkasi8h8qf3lyb63hp760cn22fjqaj3inh";
+        authors = [
+          "Stefan Lankes"
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins/rustc-dep-of-std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "home" = rec {
+        crateName = "home";
+        version = "0.5.9";
+        edition = "2021";
+        sha256 = "19grxyg35rqfd802pcc9ys1q3lafzlcjcv2pl2s5q8xpyr5kblg3";
+        authors = [
+          "Brian Anderson <andersrb@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_UI_Shell" "Win32_System_Com" ];
+          }
+        ];
+
+      };
+      "iana-time-zone" = rec {
+        crateName = "iana-time-zone";
+        version = "0.1.60";
+        edition = "2018";
+        sha256 = "0hdid5xz3jznm04lysjm3vi93h3c523w0hcc3xba47jl3ddbpzz7";
+        authors = [
+          "Andrew Straw <strawman@astraw.com>"
+          "RenΓ© Kijewski <rene.kijewski@fu-berlin.de>"
+          "Ryan Lopopolo <rjl@hyperbo.la>"
+        ];
+        dependencies = [
+          {
+            name = "android_system_properties";
+            packageId = "android_system_properties";
+            target = { target, features }: ("android" == target."os" or null);
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "iana-time-zone-haiku";
+            packageId = "iana-time-zone-haiku";
+            target = { target, features }: ("haiku" == target."os" or null);
+          }
+          {
+            name = "js-sys";
+            packageId = "js-sys";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+            target = { target, features }: ("wasm32" == target."arch" or null);
+          }
+          {
+            name = "windows-core";
+            packageId = "windows-core";
+            target = { target, features }: ("windows" == target."os" or null);
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "fallback" ];
+      };
+      "iana-time-zone-haiku" = rec {
+        crateName = "iana-time-zone-haiku";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        authors = [
+          "RenΓ© Kijewski <crates.io@k6i.de>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "indexmap" = rec {
+        crateName = "indexmap";
+        version = "2.2.2";
+        edition = "2021";
+        sha256 = "087mafd9f98rp1xk2jc1rsp5yyqz63yi30cy8yx6c8s14bj2ljw2";
+        dependencies = [
+          {
+            name = "equivalent";
+            packageId = "equivalent";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            usesDefaultFeatures = false;
+            features = [ "raw" ];
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-rayon" = [ "dep:rustc-rayon" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "itoa" = rec {
+        crateName = "itoa";
+        version = "1.0.10";
+        edition = "2018";
+        sha256 = "0k7xjfki7mnv6yzjrbnbnjllg86acmbnk4izz2jmm1hx2wd6v95i";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "jobserver" = rec {
+        crateName = "jobserver";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "0z9w6vfqwbr6hfk9yaw7kydlh6f7k39xdlszxlh39in4acwzcdwc";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+
+      };
+      "js-sys" = rec {
+        crateName = "js-sys";
+        version = "0.3.68";
+        edition = "2018";
+        sha256 = "1vm98fhnhs4w6yakchi9ip7ar95900k9vkr24a21qlwd6r5xlv20";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "wasm-bindgen";
+            packageId = "wasm-bindgen";
+          }
+        ];
+
+      };
+      "libc" = rec {
+        crateName = "libc";
+        version = "0.2.153";
+        edition = "2015";
+        sha256 = "1gg7m1ils5dms5miq9fyllrcp0jxnbpgkx71chd2i0lafa8qy6cw";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ];
+          "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "libm" = rec {
+        crateName = "libm";
+        version = "0.2.8";
+        edition = "2018";
+        sha256 = "0n4hk1rs8pzw8hdfmwn96c4568s93kfxqgcqswr7sajd2diaihjf";
+        authors = [
+          "Jorge Aparicio <jorge@japaric.io>"
+        ];
+        features = {
+          "musl-reference-tests" = [ "rand" ];
+          "rand" = [ "dep:rand" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "lock_api" = rec {
+        crateName = "lock_api";
+        version = "0.4.11";
+        edition = "2018";
+        sha256 = "0iggx0h4jx63xm35861106af3jkxq06fpqhpkhgw0axi2n38y5iw";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "atomic_usize" ];
+          "owning_ref" = [ "dep:owning_ref" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "atomic_usize" "default" ];
+      };
+      "log" = rec {
+        crateName = "log";
+        version = "0.4.20";
+        edition = "2015";
+        sha256 = "13rf7wphnwd61vazpxr7fiycin6cb1g8fmvgqg18i464p0y1drmm";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "kv_unstable" = [ "value-bag" ];
+          "kv_unstable_serde" = [ "kv_unstable_std" "value-bag/serde" "serde" ];
+          "kv_unstable_std" = [ "std" "kv_unstable" "value-bag/error" ];
+          "kv_unstable_sval" = [ "kv_unstable" "value-bag/sval" "sval" "sval_ref" ];
+          "serde" = [ "dep:serde" ];
+          "sval" = [ "dep:sval" ];
+          "sval_ref" = [ "dep:sval_ref" ];
+          "value-bag" = [ "dep:value-bag" ];
+        };
+      };
+      "lz4" = rec {
+        crateName = "lz4";
+        version = "1.24.0";
+        edition = "2018";
+        crateBin = [ ];
+        sha256 = "1wad97k0asgvaj16ydd09gqs2yvgaanzcvqglrhffv7kdpc2v7ky";
+        authors = [
+          "Jens Heyens <jens.heyens@ewetel.net>"
+          "Artem V. Navrotskiy <bozaro@buzzsoft.ru>"
+          "Patrick Marks <pmarks@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+          {
+            name = "lz4-sys";
+            packageId = "lz4-sys";
+          }
+        ];
+
+      };
+      "lz4-sys" = rec {
+        crateName = "lz4-sys";
+        version = "1.9.4";
+        edition = "2015";
+        links = "lz4";
+        sha256 = "0059ik4xlvnss5qfh6l691psk4g3350ljxaykzv10yr0gqqppljp";
+        authors = [
+          "Jens Heyens <jens.heyens@ewetel.net>"
+          "Artem V. Navrotskiy <bozaro@buzzsoft.ru>"
+          "Patrick Marks <pmarks@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+
+      };
+      "memchr" = rec {
+        crateName = "memchr";
+        version = "2.7.1";
+        edition = "2021";
+        sha256 = "0jf1kicqa4vs9lyzj4v4y1p90q0dh87hvhsdd5xvhnp527sw8gaj";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+          "bluss"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "logging" = [ "dep:log" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "std" = [ "alloc" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "memmap2" = rec {
+        crateName = "memmap2";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1il82b0mw304jlwvl0m89aa8bj5dgmm3vbb0jg8lqlrk0p98i4zl";
+        authors = [
+          "Dan Burkert <dan@danburkert.com>"
+          "Yevhenii Reizner <razrfalcon@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+        ];
+        features = {
+          "stable_deref_trait" = [ "dep:stable_deref_trait" ];
+        };
+      };
+      "minimal-lexical" = rec {
+        crateName = "minimal-lexical";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "miniz_oxide" = rec {
+        crateName = "miniz_oxide";
+        version = "0.7.2";
+        edition = "2018";
+        sha256 = "19qlxb21s6kabgqq61mk7kd1qk2invyygj076jz6i1gj2lz1z0cx";
+        authors = [
+          "Frommi <daniil.liferenko@gmail.com>"
+          "oyvindln <oyvindln@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "adler";
+            packageId = "adler";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "with-alloc" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "adler/rustc-dep-of-std" ];
+          "simd" = [ "simd-adler32" ];
+          "simd-adler32" = [ "dep:simd-adler32" ];
+        };
+        resolvedDefaultFeatures = [ "with-alloc" ];
+      };
+      "mio" = rec {
+        crateName = "mio";
+        version = "0.8.10";
+        edition = "2018";
+        sha256 = "02gyaxvaia9zzi4drrw59k9s0j6pa5d1y2kv7iplwjipdqlhngcg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_Storage_FileSystem" "Win32_System_IO" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = {
+          "default" = [ "log" ];
+          "log" = [ "dep:log" ];
+          "os-ext" = [ "os-poll" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_Security" ];
+        };
+        resolvedDefaultFeatures = [ "net" "os-ext" "os-poll" ];
+      };
+      "multiversion" = rec {
+        crateName = "multiversion";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "0al7yrf489lqzxx291sx9566n7slk2njwlqrxbjhqxk1zvbvkixj";
+        authors = [
+          "Caleb Zulawski <caleb.zulawski@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "multiversion-macros";
+            packageId = "multiversion-macros";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "target-features";
+            packageId = "target-features";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "multiversion-macros/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "multiversion-macros" = rec {
+        crateName = "multiversion-macros";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "1j1avbxw7jscyi7dmnywhlwbiny1fvg1vpp9fy4dc1pd022kva16";
+        procMacro = true;
+        authors = [
+          "Caleb Zulawski <caleb.zulawski@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 1.0.109";
+            features = [ "full" "extra-traits" "visit-mut" ];
+          }
+          {
+            name = "target-features";
+            packageId = "target-features";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "nix-compat" = rec {
+        crateName = "nix-compat";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [ ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ../../nix-compat; }
+          else ../../nix-compat;
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+            features = [ "alloc" "unicode" "serde" ];
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+          }
+          {
+            name = "ed25519-dalek";
+            packageId = "ed25519-dalek";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "nom";
+            packageId = "nom";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+        ];
+        features = {
+          "async" = [ "futures-util" ];
+          "futures-util" = [ "dep:futures-util" ];
+        };
+      };
+      "nom" = rec {
+        crateName = "nom";
+        version = "7.1.3";
+        edition = "2018";
+        sha256 = "0jha9901wxam390jcf5pfa0qqfrgh8li787jx2ip0yk5b8y9hwyj";
+        authors = [
+          "contact@geoffroycouprie.com"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "minimal-lexical";
+            packageId = "minimal-lexical";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" "memchr/std" "minimal-lexical/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "now" = rec {
+        crateName = "now";
+        version = "0.1.3";
+        edition = "2018";
+        sha256 = "1l135786rb43rjfhwfdj7hi3b5zxxyl9gwf15yjz18cp8f3yk2bd";
+        authors = [
+          "Kilerd <blove694@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "clock" "std" ];
+          }
+        ];
+
+      };
+      "ntapi" = rec {
+        crateName = "ntapi";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1r38zhbwdvkis2mzs6671cm1p6djgsl49i7bwxzrvhwicdf8k8z8";
+        authors = [
+          "MSxDOS <melcodos@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi";
+            packageId = "winapi";
+            features = [ "cfg" "evntrace" "in6addr" "inaddr" "minwinbase" "ntsecapi" "windef" "winioctl" ];
+          }
+        ];
+        features = {
+          "default" = [ "user" ];
+          "impl-default" = [ "winapi/impl-default" ];
+        };
+        resolvedDefaultFeatures = [ "default" "user" ];
+      };
+      "num-traits" = rec {
+        crateName = "num-traits";
+        version = "0.2.18";
+        edition = "2018";
+        sha256 = "0yjib8p2p9kzmaz48xwhs69w5dh1wipph9jgnillzd2x33jz03fs";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "libm";
+            packageId = "libm";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "libm" = [ "dep:libm" ];
+        };
+        resolvedDefaultFeatures = [ "default" "libm" "std" ];
+      };
+      "num_cpus" = rec {
+        crateName = "num_cpus";
+        version = "1.16.0";
+        edition = "2015";
+        sha256 = "0hra6ihpnh06dvfvz9ipscys0xfqa9ca9hzp384d5m02ssvgqqa1";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(target."windows" or false));
+          }
+        ];
+
+      };
+      "object" = rec {
+        crateName = "object";
+        version = "0.32.2";
+        edition = "2018";
+        sha256 = "0hc4cjwyngiy6k51hlzrlsxgv5z25vv7c2cp0ky1lckfic0259m6";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "all" = [ "read" "write" "std" "compression" "wasm" ];
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "compression" = [ "dep:flate2" "dep:ruzstd" "std" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "read" "compression" ];
+          "doc" = [ "read_core" "write_std" "std" "compression" "archive" "coff" "elf" "macho" "pe" "wasm" "xcoff" ];
+          "pe" = [ "coff" ];
+          "read" = [ "read_core" "archive" "coff" "elf" "macho" "pe" "xcoff" "unaligned" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "alloc" "memchr/rustc-dep-of-std" ];
+          "std" = [ "memchr/std" ];
+          "unstable-all" = [ "all" "unstable" ];
+          "wasm" = [ "dep:wasmparser" ];
+          "write" = [ "write_std" "coff" "elf" "macho" "pe" "xcoff" ];
+          "write_core" = [ "dep:crc32fast" "dep:indexmap" "dep:hashbrown" ];
+          "write_std" = [ "write_core" "std" "indexmap?/std" "crc32fast?/std" ];
+        };
+        resolvedDefaultFeatures = [ "archive" "coff" "elf" "macho" "pe" "read_core" "unaligned" ];
+      };
+      "once_cell" = rec {
+        crateName = "once_cell";
+        version = "1.19.0";
+        edition = "2021";
+        sha256 = "14kvw7px5z96dk4dwdm1r9cqhhy2cyj1l5n5b29mynbb8yr15nrz";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        features = {
+          "alloc" = [ "race" ];
+          "atomic-polyfill" = [ "critical-section" ];
+          "critical-section" = [ "dep:critical-section" "portable-atomic" ];
+          "default" = [ "std" ];
+          "parking_lot" = [ "dep:parking_lot_core" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "race" "std" ];
+      };
+      "owning_ref" = rec {
+        crateName = "owning_ref";
+        version = "0.4.1";
+        edition = "2015";
+        sha256 = "1kjj9m28wjv452jw49p1mp3d8ql058x78v4bz00avr7rvsnmpxbg";
+        authors = [
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "stable_deref_trait";
+            packageId = "stable_deref_trait";
+          }
+        ];
+
+      };
+      "parking_lot" = rec {
+        crateName = "parking_lot";
+        version = "0.12.1";
+        edition = "2018";
+        sha256 = "13r2xk7mnxfc5g0g6dkdxqdqad99j7s7z8zhzz4npw5r0g0v4hip";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lock_api";
+            packageId = "lock_api";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core";
+          }
+        ];
+        features = {
+          "arc_lock" = [ "lock_api/arc_lock" ];
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot_core" = rec {
+        crateName = "parking_lot_core";
+        version = "0.9.9";
+        edition = "2018";
+        sha256 = "13h0imw1aq86wj28gxkblhkzx6z1gk8q18n0v76qmmj6cliajhjc";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "parquet-format-safe" = rec {
+        crateName = "parquet-format-safe";
+        version = "0.2.4";
+        edition = "2021";
+        sha256 = "07wf6wf4jrxlq5p3xldxsnabp7jl06my2qp7kiwy9m3x2r5wac8i";
+        authors = [
+          "Apache Thrift contributors <dev@thrift.apache.org>"
+          "Jorge Leitao <jorgecarleitao@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+            optional = true;
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+        ];
+        features = {
+          "async" = [ "futures" "async-trait" ];
+          "async-trait" = [ "dep:async-trait" ];
+          "full" = [ "async" ];
+          "futures" = [ "dep:futures" ];
+        };
+        resolvedDefaultFeatures = [ "async" "async-trait" "default" "futures" ];
+      };
+      "percent-encoding" = rec {
+        crateName = "percent-encoding";
+        version = "2.3.1";
+        edition = "2018";
+        sha256 = "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573";
+        authors = [
+          "The rust-url developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "pin-project-lite" = rec {
+        crateName = "pin-project-lite";
+        version = "0.2.13";
+        edition = "2018";
+        sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+
+      };
+      "pin-utils" = rec {
+        crateName = "pin-utils";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        authors = [
+          "Josef Brandl <mail@josefbrandl.de>"
+        ];
+
+      };
+      "pkcs8" = rec {
+        crateName = "pkcs8";
+        version = "0.10.2";
+        edition = "2021";
+        sha256 = "1dx7w21gvn07azszgqd3ryjhyphsrjrmq5mmz1fbxkj5g0vv4l7r";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+          {
+            name = "spki";
+            packageId = "spki";
+          }
+        ];
+        features = {
+          "3des" = [ "encryption" "pkcs5/3des" ];
+          "alloc" = [ "der/alloc" "der/zeroize" "spki/alloc" ];
+          "des-insecure" = [ "encryption" "pkcs5/des-insecure" ];
+          "encryption" = [ "alloc" "pkcs5/alloc" "pkcs5/pbes2" "rand_core" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "pem" = [ "alloc" "der/pem" "spki/pem" ];
+          "pkcs5" = [ "dep:pkcs5" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "sha1-insecure" = [ "encryption" "pkcs5/sha1-insecure" ];
+          "std" = [ "alloc" "der/std" "spki/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "pkg-config" = rec {
+        crateName = "pkg-config";
+        version = "0.3.29";
+        edition = "2015";
+        sha256 = "1jy6158v1316khkpmq2sjj1vgbnbnw51wffx7p0k0l9h9vlys019";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+
+      };
+      "planus" = rec {
+        crateName = "planus";
+        version = "0.3.1";
+        edition = "2021";
+        sha256 = "17x8mr175b9clg998xpi5z45f9fsspb0ncfnx2644bz817fr25pw";
+        dependencies = [
+          {
+            name = "array-init-cursor";
+            packageId = "array-init-cursor";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "platforms" = rec {
+        crateName = "platforms";
+        version = "3.3.0";
+        edition = "2018";
+        sha256 = "0k7q6pigmnvgpfasvssb12m2pv3pc94zrhrfg9by3h3wmhyfqvb2";
+        authors = [
+          "Tony Arcieri <bascule@gmail.com>"
+          "Sergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "polars" = rec {
+        crateName = "polars";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "0swv6i0gq25zafw1ir2irqij9a8mnkhvws5idv72m3kavby4i04k";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            target = { target, features }: (builtins.elem "wasm" target."family");
+            features = [ "js" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "algorithm_group_by" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-lazy";
+            packageId = "polars-lazy";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-sql";
+            packageId = "polars-sql";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-time";
+            packageId = "polars-time";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "abs" = [ "polars-ops/abs" "polars-lazy?/abs" ];
+          "approx_unique" = [ "polars-lazy?/approx_unique" "polars-ops/approx_unique" ];
+          "arg_where" = [ "polars-lazy?/arg_where" ];
+          "array_any_all" = [ "polars-lazy?/array_any_all" "dtype-array" ];
+          "asof_join" = [ "polars-core/asof_join" "polars-lazy?/asof_join" "polars-ops/asof_join" ];
+          "async" = [ "polars-lazy?/async" ];
+          "avro" = [ "polars-io" "polars-io/avro" ];
+          "avx512" = [ "polars-core/avx512" ];
+          "aws" = [ "async" "cloud" "polars-io/aws" ];
+          "azure" = [ "async" "cloud" "polars-io/azure" ];
+          "bench" = [ "lazy" ];
+          "bigidx" = [ "polars-core/bigidx" "polars-lazy?/bigidx" "polars-ops/big_idx" ];
+          "binary_encoding" = [ "polars-ops/binary_encoding" "polars-lazy?/binary_encoding" ];
+          "checked_arithmetic" = [ "polars-core/checked_arithmetic" ];
+          "chunked_ids" = [ "polars-lazy?/chunked_ids" "polars-core/chunked_ids" "polars-ops/chunked_ids" ];
+          "cloud" = [ "polars-lazy?/cloud" "polars-io/cloud" ];
+          "cloud_write" = [ "cloud" "polars-lazy?/cloud_write" ];
+          "coalesce" = [ "polars-lazy?/coalesce" ];
+          "concat_str" = [ "polars-lazy?/concat_str" ];
+          "cov" = [ "polars-lazy/cov" ];
+          "cross_join" = [ "polars-lazy?/cross_join" "polars-ops/cross_join" ];
+          "cse" = [ "polars-lazy?/cse" ];
+          "csv" = [ "polars-io" "polars-io/csv" "polars-lazy?/csv" "polars-sql?/csv" ];
+          "cum_agg" = [ "polars-ops/cum_agg" "polars-lazy?/cum_agg" ];
+          "cumulative_eval" = [ "polars-lazy?/cumulative_eval" ];
+          "cutqcut" = [ "polars-lazy?/cutqcut" ];
+          "dataframe_arithmetic" = [ "polars-core/dataframe_arithmetic" ];
+          "date_offset" = [ "polars-lazy?/date_offset" ];
+          "decompress" = [ "polars-io/decompress" ];
+          "decompress-fast" = [ "polars-io/decompress-fast" ];
+          "default" = [ "docs" "zip_with" "csv" "temporal" "fmt" "dtype-slim" ];
+          "describe" = [ "polars-core/describe" ];
+          "diagonal_concat" = [ "polars-core/diagonal_concat" "polars-lazy?/diagonal_concat" "polars-sql?/diagonal_concat" ];
+          "diff" = [ "polars-ops/diff" "polars-lazy?/diff" ];
+          "docs" = [ "polars-core/docs" ];
+          "docs-selection" = [ "csv" "json" "parquet" "ipc" "ipc_streaming" "dtype-full" "is_in" "rows" "docs" "strings" "object" "lazy" "temporal" "random" "zip_with" "round_series" "checked_arithmetic" "ndarray" "repeat_by" "is_first_distinct" "is_last_distinct" "asof_join" "cross_join" "concat_str" "string_reverse" "string_to_integer" "decompress" "mode" "take_opt_iter" "cum_agg" "rolling_window" "interpolate" "diff" "rank" "range" "diagonal_concat" "horizontal_concat" "abs" "dot_diagram" "string_encoding" "product" "to_dummies" "describe" "list_eval" "cumulative_eval" "timezones" "arg_where" "propagate_nans" "coalesce" "dynamic_group_by" "extract_groups" "replace" ];
+          "dot_diagram" = [ "polars-lazy?/dot_diagram" ];
+          "dot_product" = [ "polars-core/dot_product" ];
+          "dtype-array" = [ "polars-core/dtype-array" "polars-lazy?/dtype-array" "polars-ops/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" "polars-io/dtype-categorical" "polars-lazy?/dtype-categorical" "polars-ops/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-lazy?/dtype-date" "polars-io/dtype-date" "polars-time?/dtype-date" "polars-core/dtype-date" "polars-ops/dtype-date" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-lazy?/dtype-datetime" "polars-io/dtype-datetime" "polars-time?/dtype-datetime" "polars-ops/dtype-datetime" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" "polars-lazy?/dtype-decimal" "polars-ops/dtype-decimal" "polars-io/dtype-decimal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-lazy?/dtype-duration" "polars-time?/dtype-duration" "polars-core/dtype-duration" "polars-ops/dtype-duration" ];
+          "dtype-full" = [ "dtype-date" "dtype-datetime" "dtype-duration" "dtype-time" "dtype-array" "dtype-i8" "dtype-i16" "dtype-decimal" "dtype-u8" "dtype-u16" "dtype-categorical" "dtype-struct" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" "polars-lazy?/dtype-i16" "polars-ops/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" "polars-lazy?/dtype-i8" "polars-ops/dtype-i8" ];
+          "dtype-slim" = [ "dtype-date" "dtype-datetime" "dtype-duration" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" "polars-lazy?/dtype-struct" "polars-ops/dtype-struct" "polars-io/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-io/dtype-time" "polars-time?/dtype-time" "polars-ops/dtype-time" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" "polars-lazy?/dtype-u16" "polars-ops/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" "polars-lazy?/dtype-u8" "polars-ops/dtype-u8" ];
+          "dynamic_group_by" = [ "polars-core/dynamic_group_by" "polars-lazy?/dynamic_group_by" ];
+          "ewma" = [ "polars-ops/ewma" "polars-lazy?/ewma" ];
+          "extract_groups" = [ "polars-lazy?/extract_groups" ];
+          "extract_jsonpath" = [ "polars-core/strings" "polars-ops/extract_jsonpath" "polars-ops/strings" "polars-lazy?/extract_jsonpath" ];
+          "find_many" = [ "polars-plan/find_many" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "fmt_no_tty" = [ "polars-core/fmt_no_tty" ];
+          "fused" = [ "polars-ops/fused" "polars-lazy?/fused" ];
+          "gcp" = [ "async" "cloud" "polars-io/gcp" ];
+          "group_by_list" = [ "polars-core/group_by_list" "polars-ops/group_by_list" ];
+          "hist" = [ "polars-ops/hist" "polars-lazy/hist" ];
+          "horizontal_concat" = [ "polars-core/horizontal_concat" "polars-lazy?/horizontal_concat" ];
+          "http" = [ "async" "cloud" "polars-io/http" ];
+          "interpolate" = [ "polars-ops/interpolate" "polars-lazy?/interpolate" ];
+          "ipc" = [ "polars-io" "polars-io/ipc" "polars-lazy?/ipc" "polars-sql?/ipc" ];
+          "ipc_streaming" = [ "polars-io" "polars-io/ipc_streaming" "polars-lazy?/ipc" ];
+          "is_first_distinct" = [ "polars-lazy?/is_first_distinct" "polars-ops/is_first_distinct" ];
+          "is_in" = [ "polars-lazy?/is_in" ];
+          "is_last_distinct" = [ "polars-lazy?/is_last_distinct" "polars-ops/is_last_distinct" ];
+          "is_unique" = [ "polars-lazy?/is_unique" "polars-ops/is_unique" ];
+          "json" = [ "polars-io" "polars-io/json" "polars-lazy?/json" "polars-sql?/json" "dtype-struct" ];
+          "lazy" = [ "polars-core/lazy" "polars-lazy" ];
+          "lazy_regex" = [ "polars-lazy?/regex" ];
+          "list_any_all" = [ "polars-lazy?/list_any_all" ];
+          "list_count" = [ "polars-ops/list_count" "polars-lazy?/list_count" ];
+          "list_drop_nulls" = [ "polars-lazy?/list_drop_nulls" ];
+          "list_eval" = [ "polars-lazy?/list_eval" ];
+          "list_gather" = [ "polars-ops/list_gather" "polars-lazy?/list_gather" ];
+          "list_sample" = [ "polars-lazy?/list_sample" ];
+          "list_sets" = [ "polars-lazy?/list_sets" ];
+          "list_to_struct" = [ "polars-ops/list_to_struct" "polars-lazy?/list_to_struct" ];
+          "log" = [ "polars-ops/log" "polars-lazy?/log" ];
+          "merge_sorted" = [ "polars-lazy?/merge_sorted" ];
+          "meta" = [ "polars-lazy?/meta" ];
+          "mode" = [ "polars-ops/mode" "polars-lazy?/mode" ];
+          "moment" = [ "polars-ops/moment" "polars-lazy?/moment" ];
+          "ndarray" = [ "polars-core/ndarray" ];
+          "nightly" = [ "polars-core/nightly" "polars-ops?/nightly" "simd" "polars-lazy?/nightly" "polars-sql/nightly" ];
+          "object" = [ "polars-core/object" "polars-lazy?/object" "polars-io/object" ];
+          "parquet" = [ "polars-io" "polars-lazy?/parquet" "polars-io/parquet" "polars-sql?/parquet" ];
+          "partition_by" = [ "polars-core/partition_by" ];
+          "pct_change" = [ "polars-ops/pct_change" "polars-lazy?/pct_change" ];
+          "peaks" = [ "polars-lazy/peaks" ];
+          "performant" = [ "polars-core/performant" "chunked_ids" "dtype-u8" "dtype-u16" "dtype-struct" "cse" "polars-ops/performant" "streaming" "fused" ];
+          "pivot" = [ "polars-lazy?/pivot" ];
+          "polars-io" = [ "dep:polars-io" ];
+          "polars-lazy" = [ "dep:polars-lazy" ];
+          "polars-ops" = [ "dep:polars-ops" ];
+          "polars-plan" = [ "dep:polars-plan" ];
+          "polars-sql" = [ "dep:polars-sql" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "product" = [ "polars-core/product" ];
+          "propagate_nans" = [ "polars-lazy?/propagate_nans" ];
+          "random" = [ "polars-core/random" "polars-lazy?/random" "polars-ops/random" ];
+          "range" = [ "polars-lazy?/range" ];
+          "rank" = [ "polars-lazy?/rank" "polars-ops/rank" ];
+          "reinterpret" = [ "polars-core/reinterpret" ];
+          "repeat_by" = [ "polars-ops/repeat_by" "polars-lazy?/repeat_by" ];
+          "replace" = [ "polars-ops/replace" "polars-lazy?/replace" ];
+          "rle" = [ "polars-lazy?/rle" ];
+          "rolling_window" = [ "polars-core/rolling_window" "polars-lazy?/rolling_window" "polars-time/rolling_window" ];
+          "round_series" = [ "polars-ops/round_series" "polars-lazy?/round_series" ];
+          "row_hash" = [ "polars-core/row_hash" "polars-lazy?/row_hash" ];
+          "rows" = [ "polars-core/rows" ];
+          "search_sorted" = [ "polars-lazy?/search_sorted" ];
+          "semi_anti_join" = [ "polars-lazy?/semi_anti_join" "polars-ops/semi_anti_join" "polars-sql?/semi_anti_join" ];
+          "serde" = [ "polars-core/serde" ];
+          "serde-lazy" = [ "polars-core/serde-lazy" "polars-lazy?/serde" "polars-time?/serde" "polars-io?/serde" "polars-ops?/serde" ];
+          "sign" = [ "polars-lazy?/sign" ];
+          "simd" = [ "polars-core/simd" "polars-io/simd" "polars-ops?/simd" ];
+          "sql" = [ "polars-sql" ];
+          "streaming" = [ "polars-lazy?/streaming" ];
+          "string_encoding" = [ "polars-ops/string_encoding" "polars-lazy?/string_encoding" "polars-core/strings" ];
+          "string_pad" = [ "polars-lazy?/string_pad" "polars-ops/string_pad" ];
+          "string_reverse" = [ "polars-lazy?/string_reverse" "polars-ops/string_reverse" ];
+          "string_to_integer" = [ "polars-lazy?/string_to_integer" "polars-ops/string_to_integer" ];
+          "strings" = [ "polars-core/strings" "polars-lazy?/strings" "polars-ops/strings" ];
+          "take_opt_iter" = [ "polars-core/take_opt_iter" ];
+          "temporal" = [ "polars-core/temporal" "polars-lazy?/temporal" "polars-io/temporal" "polars-time" ];
+          "test" = [ "lazy" "rolling_window" "rank" "round_series" "csv" "dtype-categorical" "cum_agg" "fmt" "diff" "abs" "parquet" "ipc" "ipc_streaming" "json" ];
+          "timezones" = [ "polars-core/timezones" "polars-lazy?/timezones" "polars-io/timezones" ];
+          "to_dummies" = [ "polars-ops/to_dummies" ];
+          "top_k" = [ "polars-lazy?/top_k" ];
+          "trigonometry" = [ "polars-lazy?/trigonometry" ];
+          "true_div" = [ "polars-lazy?/true_div" ];
+          "unique_counts" = [ "polars-ops/unique_counts" "polars-lazy?/unique_counts" ];
+          "zip_with" = [ "polars-core/zip_with" ];
+        };
+        resolvedDefaultFeatures = [ "csv" "default" "docs" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-slim" "fmt" "parquet" "polars-io" "polars-ops" "polars-time" "temporal" "zip_with" ];
+      };
+      "polars-arrow" = rec {
+        crateName = "polars-arrow";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "0vxql6amvwyp6qj0w87vm21y33qa0i61pshs4ry7ixwgd4ps0s6f";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+          "Apache Arrow <dev@arrow.apache.org>"
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "arrow-format";
+            packageId = "arrow-format";
+            optional = true;
+            features = [ "ipc" ];
+          }
+          {
+            name = "atoi_simd";
+            packageId = "atoi_simd";
+            optional = true;
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "dyn-clone";
+            packageId = "dyn-clone";
+          }
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "ethnum";
+            packageId = "ethnum";
+          }
+          {
+            name = "fast-float";
+            packageId = "fast-float";
+            optional = true;
+          }
+          {
+            name = "foreign_vec";
+            packageId = "foreign_vec";
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "wasm32-unknown-unknown");
+            features = [ "js" ];
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+            optional = true;
+          }
+          {
+            name = "lz4";
+            packageId = "lz4";
+            optional = true;
+          }
+          {
+            name = "multiversion";
+            packageId = "multiversion";
+            optional = true;
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+            optional = true;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+          }
+          {
+            name = "streaming-iterator";
+            packageId = "streaming-iterator";
+          }
+          {
+            name = "strength_reduce";
+            packageId = "strength_reduce";
+            optional = true;
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+            optional = true;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "arrow-array" = [ "dep:arrow-array" ];
+          "arrow-buffer" = [ "dep:arrow-buffer" ];
+          "arrow-data" = [ "dep:arrow-data" ];
+          "arrow-format" = [ "dep:arrow-format" ];
+          "arrow-schema" = [ "dep:arrow-schema" ];
+          "arrow_rs" = [ "arrow-buffer" "arrow-schema" "arrow-data" "arrow-array" ];
+          "async-stream" = [ "dep:async-stream" ];
+          "atoi" = [ "dep:atoi" ];
+          "atoi_simd" = [ "dep:atoi_simd" ];
+          "avro-schema" = [ "dep:avro-schema" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "compute" = [ "compute_aggregate" "compute_arithmetics" "compute_bitwise" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_hash" "compute_if_then_else" "compute_take" "compute_temporal" ];
+          "compute_aggregate" = [ "multiversion" ];
+          "compute_arithmetics" = [ "strength_reduce" "compute_arithmetics_decimal" ];
+          "compute_arithmetics_decimal" = [ "strength_reduce" ];
+          "compute_cast" = [ "compute_take" "ryu" "atoi_simd" "itoa" "fast-float" ];
+          "compute_comparison" = [ "compute_take" "compute_boolean" ];
+          "compute_hash" = [ "multiversion" ];
+          "dtype-decimal" = [ "atoi" ];
+          "fast-float" = [ "dep:fast-float" ];
+          "full" = [ "arrow_rs" "io_ipc" "io_flight" "io_ipc_write_async" "io_ipc_read_async" "io_ipc_compression" "io_avro" "io_avro_compression" "io_avro_async" "regex-syntax" "compute" "chrono-tz" ];
+          "futures" = [ "dep:futures" ];
+          "hex" = [ "dep:hex" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "io_avro" = [ "avro-schema" "polars-error/avro-schema" ];
+          "io_avro_async" = [ "avro-schema/async" ];
+          "io_avro_compression" = [ "avro-schema/compression" ];
+          "io_flight" = [ "io_ipc" "arrow-format/flight-data" ];
+          "io_ipc" = [ "arrow-format" "polars-error/arrow-format" ];
+          "io_ipc_compression" = [ "lz4" "zstd" ];
+          "io_ipc_read_async" = [ "io_ipc" "futures" "async-stream" ];
+          "io_ipc_write_async" = [ "io_ipc" "futures" ];
+          "itoa" = [ "dep:itoa" ];
+          "lz4" = [ "dep:lz4" ];
+          "multiversion" = [ "dep:multiversion" ];
+          "regex" = [ "dep:regex" ];
+          "regex-syntax" = [ "dep:regex-syntax" ];
+          "ryu" = [ "dep:ryu" ];
+          "serde" = [ "dep:serde" ];
+          "strength_reduce" = [ "dep:strength_reduce" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "arrow-format" "atoi_simd" "compute" "compute_aggregate" "compute_arithmetics" "compute_arithmetics_decimal" "compute_bitwise" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_hash" "compute_if_then_else" "compute_take" "compute_temporal" "fast-float" "futures" "io_ipc" "io_ipc_compression" "io_ipc_write_async" "itoa" "lz4" "multiversion" "ryu" "strength_reduce" "strings" "temporal" "zstd" ];
+      };
+      "polars-compute" = rec {
+        crateName = "polars-compute";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1pa4l1w41gdzdff81ih1z05z3gz568k81i6flibbca8v2igvqkxi";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = { };
+      };
+      "polars-core" = rec {
+        crateName = "polars-core";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "08sar9h97znpb8ziyvrrvvx28lyzc21vwsd7gvwybjxn6kkyzxfh";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "comfy-table";
+            packageId = "comfy-table";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-compute";
+            packageId = "polars-compute";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-row";
+            packageId = "polars-row";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            optional = true;
+            features = [ "small_rng" "std" ];
+          }
+          {
+            name = "rand_distr";
+            packageId = "rand_distr";
+            optional = true;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "xxhash-rust";
+            packageId = "xxhash-rust";
+            features = [ "xxh3" ];
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "arrow-array" = [ "dep:arrow-array" ];
+          "arrow_rs" = [ "arrow-array" "arrow/arrow_rs" ];
+          "bigidx" = [ "arrow/bigidx" "polars-utils/bigidx" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "comfy-table" = [ "dep:comfy-table" ];
+          "default" = [ "algorithm_group_by" ];
+          "docs-selection" = [ "ndarray" "rows" "docs" "strings" "object" "lazy" "temporal" "random" "zip_with" "checked_arithmetic" "is_first_distinct" "is_last_distinct" "asof_join" "dot_product" "row_hash" "rolling_window" "dtype-categorical" "dtype-decimal" "diagonal_concat" "horizontal_concat" "dataframe_arithmetic" "product" "describe" "chunked_ids" "partition_by" "algorithm_group_by" ];
+          "dtype-array" = [ "arrow/dtype-array" "polars-compute/dtype-array" ];
+          "dtype-date" = [ "temporal" ];
+          "dtype-datetime" = [ "temporal" ];
+          "dtype-decimal" = [ "dep:itoap" "arrow/dtype-decimal" ];
+          "dtype-duration" = [ "temporal" ];
+          "dtype-time" = [ "temporal" ];
+          "dynamic_group_by" = [ "dtype-datetime" "dtype-date" ];
+          "fmt" = [ "comfy-table/tty" ];
+          "fmt_no_tty" = [ "comfy-table" ];
+          "ndarray" = [ "dep:ndarray" ];
+          "nightly" = [ "simd" "hashbrown/nightly" "polars-utils/nightly" "arrow/nightly" ];
+          "object" = [ "serde_json" ];
+          "performant" = [ "arrow/performant" "reinterpret" ];
+          "rand" = [ "dep:rand" ];
+          "rand_distr" = [ "dep:rand_distr" ];
+          "random" = [ "rand" "rand_distr" ];
+          "regex" = [ "dep:regex" ];
+          "serde" = [ "dep:serde" "smartstring/serde" "bitflags/serde" ];
+          "serde-lazy" = [ "serde" "arrow/serde" "indexmap/serde" "smartstring/serde" "chrono/serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "simd" = [ "arrow/simd" "polars-compute/simd" ];
+          "strings" = [ "regex" "arrow/strings" "polars-error/regex" ];
+          "temporal" = [ "regex" "chrono" "polars-error/regex" ];
+          "timezones" = [ "chrono-tz" "arrow/chrono-tz" "arrow/timezones" ];
+        };
+        resolvedDefaultFeatures = [ "algorithm_group_by" "chrono" "chunked_ids" "comfy-table" "docs" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-i16" "dtype-i8" "dtype-time" "fmt" "lazy" "rand" "rand_distr" "random" "regex" "reinterpret" "rows" "strings" "temporal" "zip_with" ];
+      };
+      "polars-error" = rec {
+        crateName = "polars-error";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "11m80a899kp45b3dz9jbvrn5001hw8izbdp7d2czrswrixwdx5k3";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "arrow-format";
+            packageId = "arrow-format";
+            optional = true;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        features = {
+          "arrow-format" = [ "dep:arrow-format" ];
+          "avro-schema" = [ "dep:avro-schema" ];
+          "object_store" = [ "dep:object_store" ];
+          "regex" = [ "dep:regex" ];
+        };
+        resolvedDefaultFeatures = [ "arrow-format" "regex" ];
+      };
+      "polars-io" = rec {
+        crateName = "polars-io";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1smamd34ghlxyxdd4aqdplk7k8xm1l626brmzlc4fvwlx3pmh13x";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+            optional = true;
+          }
+          {
+            name = "atoi_simd";
+            packageId = "atoi_simd";
+            optional = true;
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "fast-float";
+            packageId = "fast-float";
+            optional = true;
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "home";
+            packageId = "home";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+            optional = true;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "memmap2";
+            packageId = "memmap2";
+            rename = "memmap";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-parquet";
+            packageId = "polars-parquet";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-time";
+            packageId = "polars-time";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+            optional = true;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+            optional = true;
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "net" "rt-multi-thread" "time" "sync" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            optional = true;
+            features = [ "io" "io-util" ];
+          }
+        ];
+        features = {
+          "async" = [ "async-trait" "futures" "tokio" "tokio-util" "arrow/io_ipc_write_async" "polars-error/regex" "polars-parquet?/async" ];
+          "async-trait" = [ "dep:async-trait" ];
+          "atoi_simd" = [ "dep:atoi_simd" ];
+          "avro" = [ "arrow/io_avro" "arrow/io_avro_compression" ];
+          "aws" = [ "object_store/aws" "cloud" "reqwest" ];
+          "azure" = [ "object_store/azure" "cloud" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "cloud" = [ "object_store" "async" "polars-error/object_store" "url" ];
+          "csv" = [ "atoi_simd" "polars-core/rows" "itoa" "ryu" "fast-float" "simdutf8" ];
+          "decompress" = [ "flate2/rust_backend" "zstd" ];
+          "decompress-fast" = [ "flate2/zlib-ng" "zstd" ];
+          "default" = [ "decompress" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-time/dtype-date" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-core/temporal" "polars-time/dtype-datetime" "chrono" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-core/temporal" "polars-time/dtype-time" ];
+          "fast-float" = [ "dep:fast-float" ];
+          "flate2" = [ "dep:flate2" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "futures" = [ "dep:futures" ];
+          "gcp" = [ "object_store/gcp" "cloud" ];
+          "http" = [ "object_store/http" "cloud" ];
+          "ipc" = [ "arrow/io_ipc" "arrow/io_ipc_compression" ];
+          "ipc_streaming" = [ "arrow/io_ipc" "arrow/io_ipc_compression" ];
+          "itoa" = [ "dep:itoa" ];
+          "json" = [ "polars-json" "simd-json" "atoi_simd" "serde_json" "dtype-struct" "csv" ];
+          "object_store" = [ "dep:object_store" ];
+          "parquet" = [ "polars-parquet" "polars-parquet/compression" ];
+          "partition" = [ "polars-core/partition_by" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "polars-parquet" = [ "dep:polars-parquet" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "python" = [ "polars-error/python" ];
+          "reqwest" = [ "dep:reqwest" ];
+          "ryu" = [ "dep:ryu" ];
+          "serde" = [ "dep:serde" "polars-core/serde-lazy" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "simd-json" = [ "dep:simd-json" ];
+          "simdutf8" = [ "dep:simdutf8" ];
+          "temporal" = [ "dtype-datetime" "dtype-date" "dtype-time" ];
+          "timezones" = [ "chrono-tz" "dtype-datetime" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "url" = [ "dep:url" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "async" "async-trait" "atoi_simd" "chrono" "csv" "dtype-date" "dtype-datetime" "dtype-time" "fast-float" "futures" "ipc" "itoa" "lazy" "parquet" "polars-parquet" "polars-time" "ryu" "simdutf8" "temporal" "tokio" "tokio-util" ];
+      };
+      "polars-lazy" = rec {
+        crateName = "polars-lazy";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1mdml4hs574njb23mb9gl6x92x2bb4vdfzsazkl3ifq516s0awcx";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "lazy" "zip_with" "random" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            usesDefaultFeatures = false;
+            features = [ "lazy" ];
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-pipe";
+            packageId = "polars-pipe";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-plan";
+            packageId = "polars-plan";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-time";
+            packageId = "polars-time";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "abs" = [ "polars-plan/abs" ];
+          "approx_unique" = [ "polars-plan/approx_unique" ];
+          "arg_where" = [ "polars-plan/arg_where" ];
+          "array_any_all" = [ "polars-ops/array_any_all" "polars-plan/array_any_all" "dtype-array" ];
+          "asof_join" = [ "polars-plan/asof_join" "polars-time" "polars-ops/asof_join" ];
+          "async" = [ "polars-plan/async" "polars-io/cloud" "polars-pipe?/async" ];
+          "bigidx" = [ "polars-plan/bigidx" ];
+          "binary_encoding" = [ "polars-plan/binary_encoding" ];
+          "chunked_ids" = [ "polars-plan/chunked_ids" "polars-core/chunked_ids" "polars-ops/chunked_ids" ];
+          "cloud" = [ "async" "polars-pipe?/cloud" "polars-plan/cloud" "tokio" "futures" ];
+          "cloud_write" = [ "cloud" ];
+          "coalesce" = [ "polars-plan/coalesce" ];
+          "concat_str" = [ "polars-plan/concat_str" ];
+          "cov" = [ "polars-ops/cov" "polars-plan/cov" ];
+          "cross_join" = [ "polars-plan/cross_join" "polars-pipe?/cross_join" "polars-ops/cross_join" ];
+          "cse" = [ "polars-plan/cse" ];
+          "csv" = [ "polars-io/csv" "polars-plan/csv" "polars-pipe?/csv" ];
+          "cum_agg" = [ "polars-plan/cum_agg" ];
+          "cutqcut" = [ "polars-plan/cutqcut" "polars-ops/cutqcut" ];
+          "date_offset" = [ "polars-plan/date_offset" ];
+          "diff" = [ "polars-plan/diff" "polars-plan/diff" ];
+          "dot_diagram" = [ "polars-plan/dot_diagram" ];
+          "dtype-array" = [ "polars-plan/dtype-array" "polars-pipe?/dtype-array" "polars-ops/dtype-array" ];
+          "dtype-categorical" = [ "polars-plan/dtype-categorical" "polars-pipe?/dtype-categorical" ];
+          "dtype-date" = [ "polars-plan/dtype-date" "polars-time/dtype-date" "temporal" ];
+          "dtype-datetime" = [ "polars-plan/dtype-datetime" "polars-time/dtype-datetime" "temporal" ];
+          "dtype-decimal" = [ "polars-plan/dtype-decimal" "polars-pipe?/dtype-decimal" ];
+          "dtype-duration" = [ "polars-plan/dtype-duration" "polars-time/dtype-duration" "temporal" ];
+          "dtype-i16" = [ "polars-plan/dtype-i16" "polars-pipe?/dtype-i16" ];
+          "dtype-i8" = [ "polars-plan/dtype-i8" "polars-pipe?/dtype-i8" ];
+          "dtype-struct" = [ "polars-plan/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "temporal" ];
+          "dtype-u16" = [ "polars-plan/dtype-u16" "polars-pipe?/dtype-u16" ];
+          "dtype-u8" = [ "polars-plan/dtype-u8" "polars-pipe?/dtype-u8" ];
+          "dynamic_group_by" = [ "polars-plan/dynamic_group_by" "polars-time" "temporal" ];
+          "ewma" = [ "polars-plan/ewma" ];
+          "extract_groups" = [ "polars-plan/extract_groups" ];
+          "extract_jsonpath" = [ "polars-plan/extract_jsonpath" "polars-ops/extract_jsonpath" ];
+          "fmt" = [ "polars-core/fmt" "polars-plan/fmt" ];
+          "fused" = [ "polars-plan/fused" "polars-ops/fused" ];
+          "futures" = [ "dep:futures" ];
+          "hist" = [ "polars-plan/hist" ];
+          "horizontal_concat" = [ "polars-plan/horizontal_concat" "polars-core/horizontal_concat" ];
+          "interpolate" = [ "polars-plan/interpolate" ];
+          "ipc" = [ "polars-io/ipc" "polars-plan/ipc" "polars-pipe?/ipc" ];
+          "is_first_distinct" = [ "polars-plan/is_first_distinct" ];
+          "is_in" = [ "polars-plan/is_in" "polars-ops/is_in" ];
+          "is_last_distinct" = [ "polars-plan/is_last_distinct" ];
+          "is_unique" = [ "polars-plan/is_unique" ];
+          "json" = [ "polars-io/json" "polars-plan/json" "polars-json" "polars-pipe/json" ];
+          "list_any_all" = [ "polars-ops/list_any_all" "polars-plan/list_any_all" ];
+          "list_count" = [ "polars-ops/list_count" "polars-plan/list_count" ];
+          "list_drop_nulls" = [ "polars-ops/list_drop_nulls" "polars-plan/list_drop_nulls" ];
+          "list_gather" = [ "polars-ops/list_gather" "polars-plan/list_gather" ];
+          "list_sample" = [ "polars-ops/list_sample" "polars-plan/list_sample" ];
+          "list_sets" = [ "polars-plan/list_sets" "polars-ops/list_sets" ];
+          "list_to_struct" = [ "polars-plan/list_to_struct" ];
+          "log" = [ "polars-plan/log" ];
+          "merge_sorted" = [ "polars-plan/merge_sorted" ];
+          "meta" = [ "polars-plan/meta" ];
+          "mode" = [ "polars-plan/mode" ];
+          "moment" = [ "polars-plan/moment" "polars-ops/moment" ];
+          "nightly" = [ "polars-core/nightly" "polars-pipe?/nightly" "polars-plan/nightly" ];
+          "object" = [ "polars-plan/object" ];
+          "panic_on_schema" = [ "polars-plan/panic_on_schema" ];
+          "parquet" = [ "polars-io/parquet" "polars-plan/parquet" "polars-pipe?/parquet" ];
+          "pct_change" = [ "polars-plan/pct_change" ];
+          "peaks" = [ "polars-plan/peaks" ];
+          "pivot" = [ "polars-core/rows" "polars-ops/pivot" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "polars-pipe" = [ "dep:polars-pipe" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "propagate_nans" = [ "polars-plan/propagate_nans" ];
+          "pyo3" = [ "dep:pyo3" ];
+          "python" = [ "pyo3" "polars-plan/python" "polars-core/python" "polars-io/python" ];
+          "random" = [ "polars-plan/random" ];
+          "range" = [ "polars-plan/range" ];
+          "rank" = [ "polars-plan/rank" ];
+          "regex" = [ "polars-plan/regex" ];
+          "repeat_by" = [ "polars-plan/repeat_by" ];
+          "replace" = [ "polars-plan/replace" ];
+          "rle" = [ "polars-plan/rle" "polars-ops/rle" ];
+          "rolling_window" = [ "polars-plan/rolling_window" "polars-time/rolling_window" ];
+          "round_series" = [ "polars-plan/round_series" "polars-ops/round_series" ];
+          "row_hash" = [ "polars-plan/row_hash" ];
+          "search_sorted" = [ "polars-plan/search_sorted" ];
+          "semi_anti_join" = [ "polars-plan/semi_anti_join" ];
+          "serde" = [ "polars-plan/serde" "arrow/serde" "polars-core/serde-lazy" "polars-time?/serde" "polars-io/serde" "polars-ops/serde" ];
+          "sign" = [ "polars-plan/sign" ];
+          "streaming" = [ "chunked_ids" "polars-pipe" "polars-plan/streaming" "polars-ops/chunked_ids" ];
+          "string_encoding" = [ "polars-plan/string_encoding" ];
+          "string_pad" = [ "polars-plan/string_pad" ];
+          "string_reverse" = [ "polars-plan/string_reverse" ];
+          "string_to_integer" = [ "polars-plan/string_to_integer" ];
+          "strings" = [ "polars-plan/strings" ];
+          "temporal" = [ "dtype-datetime" "dtype-date" "dtype-time" "dtype-i8" "dtype-i16" "dtype-duration" "polars-plan/temporal" ];
+          "test" = [ "polars-plan/debugging" "panic_on_schema" "rolling_window" "rank" "round_series" "csv" "dtype-categorical" "cum_agg" "regex" "polars-core/fmt" "diff" "abs" "parquet" "ipc" "dtype-date" ];
+          "test_all" = [ "test" "strings" "regex" "ipc" "row_hash" "string_pad" "string_to_integer" "search_sorted" "top_k" "pivot" "semi_anti_join" "cse" ];
+          "timezones" = [ "polars-plan/timezones" ];
+          "tokio" = [ "dep:tokio" ];
+          "top_k" = [ "polars-plan/top_k" ];
+          "trigonometry" = [ "polars-plan/trigonometry" ];
+          "true_div" = [ "polars-plan/true_div" ];
+          "unique_counts" = [ "polars-plan/unique_counts" ];
+        };
+        resolvedDefaultFeatures = [ "abs" "cross_join" "csv" "cum_agg" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-i16" "dtype-i8" "dtype-time" "is_in" "log" "meta" "parquet" "polars-time" "regex" "round_series" "strings" "temporal" "trigonometry" ];
+      };
+      "polars-ops" = rec {
+        crateName = "polars-ops";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "13m7dh4vpdmcah04a7kql933l7y7045f0hybbmgff4dbav2ay29f";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "argminmax";
+            packageId = "argminmax";
+            usesDefaultFeatures = false;
+            features = [ "float" ];
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "either";
+            packageId = "either";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-compute";
+            packageId = "polars-compute";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "algorithm_group_by" "zip_with" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "aho-corasick" = [ "dep:aho-corasick" ];
+          "array_any_all" = [ "dtype-array" ];
+          "asof_join" = [ "polars-core/asof_join" ];
+          "base64" = [ "dep:base64" ];
+          "big_idx" = [ "polars-core/bigidx" ];
+          "binary_encoding" = [ "base64" "hex" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "chunked_ids" = [ "polars-core/chunked_ids" ];
+          "cutqcut" = [ "dtype-categorical" "dtype-struct" ];
+          "dtype-array" = [ "polars-core/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-core/temporal" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-core/temporal" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-core/temporal" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" "polars-core/temporal" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-core/temporal" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" ];
+          "extract_groups" = [ "dtype-struct" "polars-core/regex" ];
+          "extract_jsonpath" = [ "serde_json" "jsonpath_lib" "polars-json" ];
+          "find_many" = [ "aho-corasick" ];
+          "group_by_list" = [ "polars-core/group_by_list" ];
+          "hex" = [ "dep:hex" ];
+          "hist" = [ "dtype-categorical" "dtype-struct" ];
+          "is_in" = [ "polars-core/reinterpret" ];
+          "jsonpath_lib" = [ "dep:jsonpath_lib" ];
+          "list_to_struct" = [ "polars-core/dtype-struct" ];
+          "nightly" = [ "polars-utils/nightly" ];
+          "object" = [ "polars-core/object" ];
+          "pct_change" = [ "diff" ];
+          "performant" = [ "polars-core/performant" "fused" ];
+          "pivot" = [ "polars-core/reinterpret" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "rand" = [ "dep:rand" ];
+          "rand_distr" = [ "dep:rand_distr" ];
+          "random" = [ "rand" "rand_distr" ];
+          "rank" = [ "rand" ];
+          "replace" = [ "is_in" ];
+          "rle" = [ "dtype-struct" ];
+          "rolling_window" = [ "polars-core/rolling_window" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "simd" = [ "argminmax/nightly_simd" ];
+          "string_encoding" = [ "base64" "hex" ];
+          "string_pad" = [ "polars-core/strings" ];
+          "string_reverse" = [ "polars-core/strings" "unicode-reverse" ];
+          "string_to_integer" = [ "polars-core/strings" ];
+          "strings" = [ "polars-core/strings" ];
+          "timezones" = [ "chrono-tz" "chrono" ];
+          "unicode-reverse" = [ "dep:unicode-reverse" ];
+        };
+        resolvedDefaultFeatures = [ "abs" "cross_join" "cum_agg" "dtype-date" "dtype-datetime" "dtype-duration" "is_in" "log" "round_series" "search_sorted" "strings" ];
+      };
+      "polars-parquet" = rec {
+        crateName = "polars-parquet";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "0gay037sw5hcg2q93pxcfh7krcchl1px8g838d8vhjpnn5klv8kv";
+        authors = [
+          "Jorge C. Leitao <jorgecarleitao@gmail.com>"
+          "Apache Arrow <dev@arrow.apache.org>"
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+            optional = true;
+          }
+          {
+            name = "base64";
+            packageId = "base64";
+          }
+          {
+            name = "brotli";
+            packageId = "brotli";
+            optional = true;
+          }
+          {
+            name = "ethnum";
+            packageId = "ethnum";
+          }
+          {
+            name = "flate2";
+            packageId = "flate2";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+            optional = true;
+          }
+          {
+            name = "lz4";
+            packageId = "lz4";
+            optional = true;
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "parquet-format-safe";
+            packageId = "parquet-format-safe";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" "io_ipc" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "seq-macro";
+            packageId = "seq-macro";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "simdutf8";
+            packageId = "simdutf8";
+          }
+          {
+            name = "snap";
+            packageId = "snap";
+            optional = true;
+          }
+          {
+            name = "streaming-decompression";
+            packageId = "streaming-decompression";
+          }
+          {
+            name = "zstd";
+            packageId = "zstd";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "async" = [ "async-stream" "futures" "parquet-format-safe/async" ];
+          "async-stream" = [ "dep:async-stream" ];
+          "bloom_filter" = [ "xxhash-rust" ];
+          "brotli" = [ "dep:brotli" ];
+          "compression" = [ "zstd" "gzip" "snappy" "lz4" "brotli" ];
+          "fallible-streaming-iterator" = [ "dep:fallible-streaming-iterator" ];
+          "flate2" = [ "dep:flate2" ];
+          "futures" = [ "dep:futures" ];
+          "gzip" = [ "flate2/rust_backend" ];
+          "gzip_zlib_ng" = [ "flate2/zlib-ng" ];
+          "lz4" = [ "dep:lz4" ];
+          "serde" = [ "dep:serde" ];
+          "serde_types" = [ "serde" ];
+          "snap" = [ "dep:snap" ];
+          "snappy" = [ "snap" ];
+          "xxhash-rust" = [ "dep:xxhash-rust" ];
+          "zstd" = [ "dep:zstd" ];
+        };
+        resolvedDefaultFeatures = [ "async" "async-stream" "brotli" "compression" "flate2" "futures" "gzip" "lz4" "snap" "snappy" "zstd" ];
+      };
+      "polars-pipe" = rec {
+        crateName = "polars-pipe";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "00217q51mnq7i57k3sax293xnwghm5hridbpgl11fffcfg8fmdyr";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-channel";
+            packageId = "crossbeam-channel";
+          }
+          {
+            name = "crossbeam-queue";
+            packageId = "crossbeam-queue";
+          }
+          {
+            name = "enum_dispatch";
+            packageId = "enum_dispatch";
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-compute";
+            packageId = "polars-compute";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "lazy" "zip_with" "random" "rows" "chunked_ids" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            usesDefaultFeatures = false;
+            features = [ "ipc" ];
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+            features = [ "search_sorted" ];
+          }
+          {
+            name = "polars-plan";
+            packageId = "polars-plan";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-row";
+            packageId = "polars-row";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+            features = [ "sysinfo" ];
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "async" = [ "polars-plan/async" "polars-io/async" ];
+          "cloud" = [ "async" "polars-io/cloud" "polars-plan/cloud" "tokio" "futures" ];
+          "cross_join" = [ "polars-ops/cross_join" ];
+          "csv" = [ "polars-plan/csv" "polars-io/csv" ];
+          "dtype-array" = [ "polars-core/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" ];
+          "futures" = [ "dep:futures" ];
+          "ipc" = [ "polars-plan/ipc" "polars-io/ipc" ];
+          "json" = [ "polars-plan/json" "polars-io/json" ];
+          "nightly" = [ "polars-core/nightly" "polars-utils/nightly" "hashbrown/nightly" ];
+          "parquet" = [ "polars-plan/parquet" "polars-io/parquet" "polars-io/async" ];
+          "test" = [ "polars-core/chunked_ids" ];
+          "tokio" = [ "dep:tokio" ];
+        };
+        resolvedDefaultFeatures = [ "cross_join" "csv" "dtype-i16" "dtype-i8" "parquet" ];
+      };
+      "polars-plan" = rec {
+        crateName = "polars-plan";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1l5ca38v7ksq4595wdr6wsd74pdgszwivq9y8wfc6l6h4ib1fjiq";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "lazy" "zip_with" "random" ];
+          }
+          {
+            name = "polars-io";
+            packageId = "polars-io";
+            usesDefaultFeatures = false;
+            features = [ "lazy" ];
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-parquet";
+            packageId = "polars-parquet";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-time";
+            packageId = "polars-time";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+            optional = true;
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "strum_macros";
+            packageId = "strum_macros";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "abs" = [ "polars-ops/abs" ];
+          "approx_unique" = [ "polars-ops/approx_unique" ];
+          "array_any_all" = [ "polars-ops/array_any_all" "dtype-array" ];
+          "asof_join" = [ "polars-core/asof_join" "polars-time" "polars-ops/asof_join" ];
+          "async" = [ "polars-io/async" ];
+          "bigidx" = [ "polars-core/bigidx" ];
+          "binary_encoding" = [ "polars-ops/binary_encoding" ];
+          "chrono" = [ "dep:chrono" ];
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "chunked_ids" = [ "polars-core/chunked_ids" ];
+          "ciborium" = [ "dep:ciborium" ];
+          "cloud" = [ "async" "polars-io/cloud" ];
+          "cov" = [ "polars-ops/cov" ];
+          "cross_join" = [ "polars-ops/cross_join" ];
+          "csv" = [ "polars-io/csv" ];
+          "cum_agg" = [ "polars-ops/cum_agg" ];
+          "cutqcut" = [ "polars-ops/cutqcut" ];
+          "date_offset" = [ "polars-time" "chrono" ];
+          "diff" = [ "polars-ops/diff" ];
+          "dtype-array" = [ "polars-core/dtype-array" "polars-ops/dtype-array" ];
+          "dtype-categorical" = [ "polars-core/dtype-categorical" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-time/dtype-date" "temporal" ];
+          "dtype-datetime" = [ "polars-core/dtype-datetime" "polars-time/dtype-datetime" "temporal" ];
+          "dtype-decimal" = [ "polars-core/dtype-decimal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-time/dtype-duration" "temporal" ];
+          "dtype-i16" = [ "polars-core/dtype-i16" ];
+          "dtype-i8" = [ "polars-core/dtype-i8" ];
+          "dtype-struct" = [ "polars-core/dtype-struct" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-time/dtype-time" ];
+          "dtype-u16" = [ "polars-core/dtype-u16" ];
+          "dtype-u8" = [ "polars-core/dtype-u8" ];
+          "dynamic_group_by" = [ "polars-core/dynamic_group_by" ];
+          "ewma" = [ "polars-ops/ewma" ];
+          "extract_groups" = [ "regex" "dtype-struct" "polars-ops/extract_groups" ];
+          "extract_jsonpath" = [ "polars-ops/extract_jsonpath" ];
+          "ffi_plugin" = [ "libloading" "polars-ffi" ];
+          "find_many" = [ "polars-ops/find_many" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "fused" = [ "polars-ops/fused" ];
+          "futures" = [ "dep:futures" ];
+          "hist" = [ "polars-ops/hist" ];
+          "interpolate" = [ "polars-ops/interpolate" ];
+          "ipc" = [ "polars-io/ipc" ];
+          "is_first_distinct" = [ "polars-core/is_first_distinct" "polars-ops/is_first_distinct" ];
+          "is_in" = [ "polars-ops/is_in" ];
+          "is_last_distinct" = [ "polars-core/is_last_distinct" "polars-ops/is_last_distinct" ];
+          "is_unique" = [ "polars-ops/is_unique" ];
+          "json" = [ "polars-io/json" "polars-json" ];
+          "libloading" = [ "dep:libloading" ];
+          "list_any_all" = [ "polars-ops/list_any_all" ];
+          "list_count" = [ "polars-ops/list_count" ];
+          "list_drop_nulls" = [ "polars-ops/list_drop_nulls" ];
+          "list_gather" = [ "polars-ops/list_gather" ];
+          "list_sample" = [ "polars-ops/list_sample" ];
+          "list_sets" = [ "polars-ops/list_sets" ];
+          "list_to_struct" = [ "polars-ops/list_to_struct" ];
+          "log" = [ "polars-ops/log" ];
+          "merge_sorted" = [ "polars-ops/merge_sorted" ];
+          "mode" = [ "polars-ops/mode" ];
+          "moment" = [ "polars-ops/moment" ];
+          "nightly" = [ "polars-utils/nightly" "polars-ops/nightly" ];
+          "object" = [ "polars-core/object" ];
+          "parquet" = [ "polars-io/parquet" "polars-parquet" ];
+          "pct_change" = [ "polars-ops/pct_change" ];
+          "peaks" = [ "polars-ops/peaks" ];
+          "pivot" = [ "polars-core/rows" "polars-ops/pivot" ];
+          "polars-ffi" = [ "dep:polars-ffi" ];
+          "polars-json" = [ "dep:polars-json" ];
+          "polars-parquet" = [ "dep:polars-parquet" ];
+          "polars-time" = [ "dep:polars-time" ];
+          "propagate_nans" = [ "polars-ops/propagate_nans" ];
+          "python" = [ "dep:pyo3" "ciborium" ];
+          "random" = [ "polars-core/random" ];
+          "rank" = [ "polars-ops/rank" ];
+          "regex" = [ "dep:regex" ];
+          "repeat_by" = [ "polars-ops/repeat_by" ];
+          "replace" = [ "polars-ops/replace" ];
+          "rle" = [ "polars-ops/rle" ];
+          "rolling_window" = [ "polars-core/rolling_window" "polars-time/rolling_window" "polars-ops/rolling_window" "polars-time/rolling_window" ];
+          "round_series" = [ "polars-ops/round_series" ];
+          "row_hash" = [ "polars-core/row_hash" "polars-ops/hash" ];
+          "search_sorted" = [ "polars-ops/search_sorted" ];
+          "semi_anti_join" = [ "polars-ops/semi_anti_join" ];
+          "serde" = [ "dep:serde" "polars-core/serde-lazy" "polars-time/serde" "polars-io/serde" "polars-ops/serde" ];
+          "string_encoding" = [ "polars-ops/string_encoding" ];
+          "string_pad" = [ "polars-ops/string_pad" ];
+          "string_reverse" = [ "polars-ops/string_reverse" ];
+          "string_to_integer" = [ "polars-ops/string_to_integer" ];
+          "strings" = [ "polars-core/strings" "polars-ops/strings" ];
+          "temporal" = [ "polars-core/temporal" "dtype-date" "dtype-datetime" "dtype-time" "dtype-i8" "dtype-i16" ];
+          "timezones" = [ "chrono-tz" "polars-time/timezones" "polars-core/timezones" "regex" ];
+          "top_k" = [ "polars-ops/top_k" ];
+          "unique_counts" = [ "polars-ops/unique_counts" ];
+        };
+        resolvedDefaultFeatures = [ "abs" "cross_join" "csv" "cum_agg" "dtype-date" "dtype-datetime" "dtype-duration" "dtype-i16" "dtype-i8" "dtype-time" "is_in" "log" "meta" "parquet" "polars-parquet" "polars-time" "regex" "round_series" "strings" "temporal" "trigonometry" ];
+      };
+      "polars-row" = rec {
+        crateName = "polars-row";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "0by7x6jlj5dwr3f1gj49v8w65l3kpqhwhzb9qzlv6gdqrdx2ycij";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+        ];
+
+      };
+      "polars-sql" = rec {
+        crateName = "polars-sql";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1dcmm993gycw75a6c5hxcr6h2cy6fa2r3hsbx19h9zgxvxnlq2wz";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-lazy";
+            packageId = "polars-lazy";
+            usesDefaultFeatures = false;
+            features = [ "strings" "cross_join" "trigonometry" "abs" "round_series" "log" "regex" "is_in" "meta" "cum_agg" "dtype-date" ];
+          }
+          {
+            name = "polars-plan";
+            packageId = "polars-plan";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sqlparser";
+            packageId = "sqlparser";
+          }
+        ];
+        features = {
+          "csv" = [ "polars-lazy/csv" ];
+          "diagonal_concat" = [ "polars-lazy/diagonal_concat" ];
+          "ipc" = [ "polars-lazy/ipc" ];
+          "json" = [ "polars-lazy/json" ];
+          "parquet" = [ "polars-lazy/parquet" ];
+          "semi_anti_join" = [ "polars-lazy/semi_anti_join" ];
+        };
+        resolvedDefaultFeatures = [ "csv" "parquet" ];
+      };
+      "polars-time" = rec {
+        crateName = "polars-time";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1l24fmpv5v1qshxfd4592z8x6bnjlwy4njhf9rcbdlbbr6gn9qny";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "atoi";
+            packageId = "atoi";
+          }
+          {
+            name = "chrono";
+            packageId = "chrono";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+          {
+            name = "now";
+            packageId = "now";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-arrow";
+            packageId = "polars-arrow";
+            rename = "arrow";
+            usesDefaultFeatures = false;
+            features = [ "compute_aggregate" "compute_arithmetics" "compute_boolean" "compute_boolean_kleene" "compute_cast" "compute_comparison" "compute_concatenate" "compute_filter" "compute_if_then_else" "compute" "temporal" ];
+          }
+          {
+            name = "polars-core";
+            packageId = "polars-core";
+            usesDefaultFeatures = false;
+            features = [ "dtype-datetime" "dtype-duration" "dtype-time" "dtype-date" ];
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-ops";
+            packageId = "polars-ops";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "polars-utils";
+            packageId = "polars-utils";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex";
+            packageId = "regex";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+        ];
+        features = {
+          "chrono-tz" = [ "dep:chrono-tz" ];
+          "dtype-date" = [ "polars-core/dtype-date" "polars-core/temporal" ];
+          "dtype-datetime" = [ "polars-core/dtype-date" "polars-core/temporal" ];
+          "dtype-duration" = [ "polars-core/dtype-duration" "polars-core/temporal" ];
+          "dtype-time" = [ "polars-core/dtype-time" "polars-core/temporal" ];
+          "fmt" = [ "polars-core/fmt" ];
+          "rolling_window" = [ "polars-core/rolling_window" "dtype-duration" ];
+          "serde" = [ "dep:serde" ];
+          "test" = [ "dtype-date" "dtype-datetime" "polars-core/fmt" ];
+          "timezones" = [ "chrono-tz" "dtype-datetime" "polars-core/timezones" "arrow/timezones" "polars-ops/timezones" ];
+        };
+        resolvedDefaultFeatures = [ "dtype-date" "dtype-datetime" "dtype-duration" "dtype-time" ];
+      };
+      "polars-utils" = rec {
+        crateName = "polars-utils";
+        version = "0.36.2";
+        edition = "2021";
+        sha256 = "1nmvfqwyzbaxcw457amspz479y5vcnpalq043awxfixdfx5clx5i";
+        authors = [
+          "Ritchie Vink <ritchie46@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "ahash";
+            packageId = "ahash";
+          }
+          {
+            name = "bytemuck";
+            packageId = "bytemuck";
+            features = [ "derive" "extern_crate_alloc" ];
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            features = [ "rayon" "ahash" ];
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "polars-error";
+            packageId = "polars-error";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "smartstring";
+            packageId = "smartstring";
+          }
+          {
+            name = "sysinfo";
+            packageId = "sysinfo";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "sysinfo" = [ "dep:sysinfo" ];
+        };
+        resolvedDefaultFeatures = [ "sysinfo" ];
+      };
+      "ppv-lite86" = rec {
+        crateName = "ppv-lite86";
+        version = "0.2.17";
+        edition = "2018";
+        sha256 = "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v";
+        authors = [
+          "The CryptoCorrosion Contributors"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "simd" "std" ];
+      };
+      "proc-macro2" = rec {
+        crateName = "proc-macro2";
+        version = "1.0.78";
+        edition = "2021";
+        sha256 = "1bjak27pqdn4f4ih1c9nr3manzyavsgqmf76ygw9k76q8pb2lhp2";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "quote" = rec {
+        crateName = "quote";
+        version = "1.0.35";
+        edition = "2018";
+        sha256 = "1vv8r2ncaz4pqdr78x7f138ka595sp2ncr1sa2plm4zxbsmwj7i9";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "rand" = rec {
+        crateName = "rand";
+        version = "0.8.5";
+        edition = "2018";
+        sha256 = "013l6931nn7gkc23jz5mm3qdhf93jjf0fg64nz2lp4i51qd8vbrl";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "rand_chacha";
+            packageId = "rand_chacha";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "alloc" = [ "rand_core/alloc" ];
+          "default" = [ "std" "std_rng" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "libc" = [ "dep:libc" ];
+          "log" = [ "dep:log" ];
+          "packed_simd" = [ "dep:packed_simd" ];
+          "rand_chacha" = [ "dep:rand_chacha" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" "rand_core/serde1" ];
+          "simd_support" = [ "packed_simd" ];
+          "std" = [ "rand_core/std" "rand_chacha/std" "alloc" "getrandom" "libc" ];
+          "std_rng" = [ "rand_chacha" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "getrandom" "libc" "rand_chacha" "small_rng" "std" "std_rng" ];
+      };
+      "rand_chacha" = rec {
+        crateName = "rand_chacha";
+        version = "0.3.1";
+        edition = "2018";
+        sha256 = "123x2adin558xbhvqb8w4f6syjsdkmqff8cxwhmjacpsl1ihmhg6";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+          "The CryptoCorrosion Contributors"
+        ];
+        dependencies = [
+          {
+            name = "ppv-lite86";
+            packageId = "ppv-lite86";
+            usesDefaultFeatures = false;
+            features = [ "simd" ];
+          }
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "ppv-lite86/std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "rand_core" = rec {
+        crateName = "rand_core";
+        version = "0.6.4";
+        edition = "2018";
+        sha256 = "0b4j2v4cb5krak1pv6kakv4sz6xcwbrmy2zckc32hsigbrwy82zc";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+        ];
+        features = {
+          "getrandom" = [ "dep:getrandom" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "alloc" "getrandom" "getrandom/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "getrandom" "std" ];
+      };
+      "rand_distr" = rec {
+        crateName = "rand_distr";
+        version = "0.4.3";
+        edition = "2018";
+        sha256 = "0cgfwg3z0pkqhrl0x90c77kx70r6g9z4m6fxq9v0h2ibr2dhpjrj";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+            features = [ "libm" ];
+          }
+          {
+            name = "rand";
+            packageId = "rand";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rand";
+            packageId = "rand";
+            usesDefaultFeatures = false;
+            features = [ "std_rng" "std" "small_rng" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "rand/alloc" ];
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" "rand/serde1" ];
+          "std" = [ "alloc" "rand/std" ];
+          "std_math" = [ "num-traits/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "rayon" = rec {
+        crateName = "rayon";
+        version = "1.8.1";
+        edition = "2021";
+        sha256 = "0lg0488xwpj5jsfz2gfczcrpclbjl8221mj5vdrhg8bp3883fwps";
+        authors = [
+          "Niko Matsakis <niko@alum.mit.edu>"
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "either";
+            packageId = "either";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "rayon-core";
+            packageId = "rayon-core";
+          }
+        ];
+        features = {
+          "web_spin_lock" = [ "dep:wasm_sync" "rayon-core/web_spin_lock" ];
+        };
+      };
+      "rayon-core" = rec {
+        crateName = "rayon-core";
+        version = "1.12.1";
+        edition = "2021";
+        links = "rayon-core";
+        sha256 = "1qpwim68ai5h0j7axa8ai8z0payaawv3id0lrgkqmapx7lx8fr8l";
+        authors = [
+          "Niko Matsakis <niko@alum.mit.edu>"
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "crossbeam-deque";
+            packageId = "crossbeam-deque";
+          }
+          {
+            name = "crossbeam-utils";
+            packageId = "crossbeam-utils";
+          }
+        ];
+        features = {
+          "web_spin_lock" = [ "dep:wasm_sync" ];
+        };
+      };
+      "redox_syscall" = rec {
+        crateName = "redox_syscall";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1aiifyz5dnybfvkk4cdab9p2kmphag1yad6iknc7aszlxxldf8j7";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+        features = {
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "bitflags/rustc-dep-of-std" ];
+        };
+      };
+      "regex" = rec {
+        crateName = "regex";
+        version = "1.10.3";
+        edition = "2021";
+        sha256 = "05cvihqy0wgnh9i8a9y2n803n5azg2h0b7nlqy6rsvxhy00vwbdn";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata";
+            usesDefaultFeatures = false;
+            features = [ "alloc" "syntax" "meta" "nfa-pikevm" ];
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "perf" "unicode" "regex-syntax/default" ];
+          "logging" = [ "aho-corasick?/logging" "memchr?/logging" "regex-automata/logging" ];
+          "perf" = [ "perf-cache" "perf-dfa" "perf-onepass" "perf-backtrack" "perf-inline" "perf-literal" ];
+          "perf-backtrack" = [ "regex-automata/nfa-backtrack" ];
+          "perf-dfa" = [ "regex-automata/hybrid" ];
+          "perf-dfa-full" = [ "regex-automata/dfa-build" "regex-automata/dfa-search" ];
+          "perf-inline" = [ "regex-automata/perf-inline" ];
+          "perf-literal" = [ "dep:aho-corasick" "dep:memchr" "regex-automata/perf-literal" ];
+          "perf-onepass" = [ "regex-automata/dfa-onepass" ];
+          "std" = [ "aho-corasick?/std" "memchr?/std" "regex-automata/std" "regex-syntax/std" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "regex-automata/unicode" "regex-syntax/unicode" ];
+          "unicode-age" = [ "regex-automata/unicode-age" "regex-syntax/unicode-age" ];
+          "unicode-bool" = [ "regex-automata/unicode-bool" "regex-syntax/unicode-bool" ];
+          "unicode-case" = [ "regex-automata/unicode-case" "regex-syntax/unicode-case" ];
+          "unicode-gencat" = [ "regex-automata/unicode-gencat" "regex-syntax/unicode-gencat" ];
+          "unicode-perl" = [ "regex-automata/unicode-perl" "regex-automata/unicode-word-boundary" "regex-syntax/unicode-perl" ];
+          "unicode-script" = [ "regex-automata/unicode-script" "regex-syntax/unicode-script" ];
+          "unicode-segment" = [ "regex-automata/unicode-segment" "regex-syntax/unicode-segment" ];
+          "unstable" = [ "pattern" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "perf" "perf-backtrack" "perf-cache" "perf-dfa" "perf-inline" "perf-literal" "perf-onepass" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "regex-automata" = rec {
+        crateName = "regex-automata";
+        version = "0.4.5";
+        edition = "2021";
+        sha256 = "1karc80mx15z435rm1jg3sqylnc58nxi15gqypcd1inkzzpqgfav";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "aho-corasick";
+            packageId = "aho-corasick";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-syntax";
+            packageId = "regex-syntax";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" "syntax" "perf" "unicode" "meta" "nfa" "dfa" "hybrid" ];
+          "dfa" = [ "dfa-build" "dfa-search" "dfa-onepass" ];
+          "dfa-build" = [ "nfa-thompson" "dfa-search" ];
+          "dfa-onepass" = [ "nfa-thompson" ];
+          "hybrid" = [ "alloc" "nfa-thompson" ];
+          "internal-instrument" = [ "internal-instrument-pikevm" ];
+          "internal-instrument-pikevm" = [ "logging" "std" ];
+          "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ];
+          "meta" = [ "syntax" "nfa-pikevm" ];
+          "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ];
+          "nfa-backtrack" = [ "nfa-thompson" ];
+          "nfa-pikevm" = [ "nfa-thompson" ];
+          "nfa-thompson" = [ "alloc" ];
+          "perf" = [ "perf-inline" "perf-literal" ];
+          "perf-literal" = [ "perf-literal-substring" "perf-literal-multisubstring" ];
+          "perf-literal-multisubstring" = [ "std" "dep:aho-corasick" ];
+          "perf-literal-substring" = [ "aho-corasick?/perf-literal" "dep:memchr" ];
+          "std" = [ "regex-syntax?/std" "memchr?/std" "aho-corasick?/std" "alloc" ];
+          "syntax" = [ "dep:regex-syntax" "alloc" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" "regex-syntax?/unicode" ];
+          "unicode-age" = [ "regex-syntax?/unicode-age" ];
+          "unicode-bool" = [ "regex-syntax?/unicode-bool" ];
+          "unicode-case" = [ "regex-syntax?/unicode-case" ];
+          "unicode-gencat" = [ "regex-syntax?/unicode-gencat" ];
+          "unicode-perl" = [ "regex-syntax?/unicode-perl" ];
+          "unicode-script" = [ "regex-syntax?/unicode-script" ];
+          "unicode-segment" = [ "regex-syntax?/unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "dfa-onepass" "dfa-search" "hybrid" "meta" "nfa-backtrack" "nfa-pikevm" "nfa-thompson" "perf-inline" "perf-literal" "perf-literal-multisubstring" "perf-literal-substring" "std" "syntax" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" ];
+      };
+      "regex-syntax" = rec {
+        crateName = "regex-syntax";
+        version = "0.8.2";
+        edition = "2021";
+        sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" "unicode" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ];
+      };
+      "rustc-demangle" = rec {
+        crateName = "rustc-demangle";
+        version = "0.1.23";
+        edition = "2015";
+        sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "rustc_version" = rec {
+        crateName = "rustc_version";
+        version = "0.4.0";
+        edition = "2018";
+        sha256 = "0rpk9rcdk405xhbmgclsh4pai0svn49x35aggl4nhbkd4a2zb85z";
+        authors = [
+          "Dirkjan Ochtman <dirkjan@ochtman.nl>"
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "semver";
+            packageId = "semver";
+          }
+        ];
+
+      };
+      "rustversion" = rec {
+        crateName = "rustversion";
+        version = "1.0.14";
+        edition = "2018";
+        sha256 = "1x1pz1yynk5xzzrazk2svmidj69jhz89dz5vrc28sixl20x1iz3z";
+        procMacro = true;
+        build = "build/build.rs";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "ryu" = rec {
+        crateName = "ryu";
+        version = "1.0.16";
+        edition = "2018";
+        sha256 = "0k7b90xr48ag5bzmfjp82rljasw2fx28xr3bg1lrpx7b5sljm3gr";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "scopeguard" = rec {
+        crateName = "scopeguard";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "0jcz9sd47zlsgcnm1hdw0664krxwb5gczlif4qngj2aif8vky54l";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+        };
+      };
+      "semver" = rec {
+        crateName = "semver";
+        version = "1.0.21";
+        edition = "2018";
+        sha256 = "1c49snqlfcx93xym1cgwx8zcspmyyxm37xa2fyfgjx1vhalxfzmr";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "seq-macro" = rec {
+        crateName = "seq-macro";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "1d50kbaslrrd0374ivx15jg57f03y5xzil1wd2ajlvajzlkbzw53";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "serde" = rec {
+        crateName = "serde";
+        version = "1.0.196";
+        edition = "2018";
+        sha256 = "0civrvhbwwk442xhlkfdkkdn478by486qxmackq6k3501zk2c047";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            optional = true;
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "derive" "serde_derive" "std" ];
+      };
+      "serde_derive" = rec {
+        crateName = "serde_derive";
+        version = "1.0.196";
+        edition = "2015";
+        sha256 = "0rybziqrfaxkaxrybkhrps7zv3ibxnjdk0fwais16zayr5h57j1k";
+        procMacro = true;
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            usesDefaultFeatures = false;
+            features = [ "clone-impls" "derive" "parsing" "printing" "proc-macro" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "serde_json" = rec {
+        crateName = "serde_json";
+        version = "1.0.113";
+        edition = "2021";
+        sha256 = "0ycaiff7ar4qx5sy9kvi1kv9rnnfl15kcfmhxiiwknn3n5q1p039";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" ];
+          "default" = [ "std" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "preserve_order" = [ "indexmap" "std" ];
+          "std" = [ "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sha2" = rec {
+        crateName = "sha2";
+        version = "0.10.8";
+        edition = "2018";
+        sha256 = "1j1x78zk9il95w9iv46dh9wm73r6xrgj32y6lzzw7bxws9dbfgbr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha2-asm" ];
+          "asm-aarch64" = [ "asm" ];
+          "default" = [ "std" ];
+          "oid" = [ "digest/oid" ];
+          "sha2-asm" = [ "dep:sha2-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "signal-hook-registry" = rec {
+        crateName = "signal-hook-registry";
+        version = "1.4.1";
+        edition = "2015";
+        sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        authors = [
+          "Michal 'vorner' Vaner <vorner@vorner.cz>"
+          "Masaki Hara <ackie.h.gmai@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "signature" = rec {
+        crateName = "signature";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "1pi9hd5vqfr3q3k49k37z06p7gs5si0in32qia4mmr1dancr6m3p";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "derive" = [ "dep:derive" ];
+          "digest" = [ "dep:digest" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "std" = [ "alloc" "rand_core?/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "simdutf8" = rec {
+        crateName = "simdutf8";
+        version = "0.1.4";
+        edition = "2018";
+        sha256 = "0fi6zvnldaw7g726wnm9vvpv4s89s5jsk7fgp3rg2l99amw64zzj";
+        authors = [
+          "Hans Kratz <hans@appfour.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "slab" = rec {
+        crateName = "slab";
+        version = "0.4.9";
+        edition = "2018";
+        sha256 = "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "smallvec" = rec {
+        crateName = "smallvec";
+        version = "1.13.1";
+        edition = "2018";
+        sha256 = "1mzk9j117pn3k1gabys0b7nz8cdjsx5xc6q7fwnm8r0an62d7v76";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "const_new" = [ "const_generics" ];
+          "drain_keep_rest" = [ "drain_filter" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "smartstring" = rec {
+        crateName = "smartstring";
+        version = "1.0.1";
+        edition = "2021";
+        sha256 = "0agf4x0jz79r30aqibyfjm1h9hrjdh0harcqcvb2vapv7rijrdrz";
+        authors = [
+          "Bodil Stokke <bodil@bodil.org>"
+        ];
+        dependencies = [
+          {
+            name = "static_assertions";
+            packageId = "static_assertions";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "default" = [ "std" ];
+          "proptest" = [ "dep:proptest" ];
+          "serde" = [ "dep:serde" ];
+          "test" = [ "std" "arbitrary" "arbitrary/derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "snap" = rec {
+        crateName = "snap";
+        version = "1.1.1";
+        edition = "2018";
+        sha256 = "0fxw80m831l76a5zxcwmz2aq7mcwc1pp345pnljl4cv1kbxnfsqv";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+
+      };
+      "socket2" = rec {
+        crateName = "socket2";
+        version = "0.5.5";
+        edition = "2021";
+        sha256 = "1sgq315f1njky114ip7wcy83qlphv9qclprfjwvxcpfblmcsqpvv";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "all" ];
+      };
+      "spki" = rec {
+        crateName = "spki";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "17fj8k5fmx4w9mp27l970clrh5qa7r5sjdvbsln987xhb34dc7nr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "base64ct";
+            packageId = "base64ct";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "base64ct?/alloc" "der/alloc" ];
+          "arbitrary" = [ "std" "dep:arbitrary" "der/arbitrary" ];
+          "base64" = [ "dep:base64ct" ];
+          "fingerprint" = [ "sha2" ];
+          "pem" = [ "alloc" "der/pem" ];
+          "sha2" = [ "dep:sha2" ];
+          "std" = [ "der/std" "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "sqlparser" = rec {
+        crateName = "sqlparser";
+        version = "0.39.0";
+        edition = "2021";
+        sha256 = "1mrbqjdqr179qnhy43d0dnrl3yipsp4qyji5rc68j4fyrg14sfvl";
+        authors = [
+          "Andy Grove <andygrove73@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "bigdecimal" = [ "dep:bigdecimal" ];
+          "default" = [ "std" ];
+          "json_example" = [ "serde_json" "serde" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "sqlparser_derive" = [ "dep:sqlparser_derive" ];
+          "visitor" = [ "sqlparser_derive" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "stable_deref_trait" = rec {
+        crateName = "stable_deref_trait";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "1lxjr8q2n534b2lhkxd6l6wcddzjvnksi58zv11f9y0jjmr15wd8";
+        authors = [
+          "Robert Grosse <n210241048576@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "static_assertions" = rec {
+        crateName = "static_assertions";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "0gsl6xmw10gvn3zs1rv99laj5ig7ylffnh71f9l34js4nr4r7sx2";
+        authors = [
+          "Nikolai Vazquez"
+        ];
+        features = { };
+      };
+      "streaming-decompression" = rec {
+        crateName = "streaming-decompression";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "1wscqj3s30qknda778wf7z99mknk65p0h9hhs658l4pvkfqw6v5z";
+        dependencies = [
+          {
+            name = "fallible-streaming-iterator";
+            packageId = "fallible-streaming-iterator";
+          }
+        ];
+
+      };
+      "streaming-iterator" = rec {
+        crateName = "streaming-iterator";
+        version = "0.1.9";
+        edition = "2021";
+        sha256 = "0845zdv8qb7zwqzglpqc0830i43xh3fb6vqms155wz85qfvk28ib";
+        authors = [
+          "Steven Fackler <sfackler@gmail.com>"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+      };
+      "strength_reduce" = rec {
+        crateName = "strength_reduce";
+        version = "0.2.4";
+        edition = "2015";
+        sha256 = "10jdq9dijjdkb20wg1dmwg447rnj37jbq0mwvbadvqi2gys5x2gy";
+        authors = [
+          "Elliott Mahler <join.together@gmail.com>"
+        ];
+
+      };
+      "strum" = rec {
+        crateName = "strum";
+        version = "0.25.0";
+        edition = "2018";
+        sha256 = "09g1q55ms8vax1z0mxlbva3vm8n2r1179kfvbccnkjcidzm58399";
+        authors = [
+          "Peter Glotfelty <peter.glotfelty@microsoft.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "strum_macros" ];
+          "phf" = [ "dep:phf" ];
+          "strum_macros" = [ "dep:strum_macros" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "strum_macros" = rec {
+        crateName = "strum_macros";
+        version = "0.25.3";
+        edition = "2018";
+        sha256 = "184y62g474zqb2f7n16x3ghvlyjbh50viw32p9w9l5lwmjlizp13";
+        procMacro = true;
+        authors = [
+          "Peter Glotfelty <peter.glotfelty@microsoft.com>"
+        ];
+        dependencies = [
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "parsing" "extra-traits" ];
+          }
+        ];
+
+      };
+      "subtle" = rec {
+        crateName = "subtle";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "1g2yjs7gffgmdvkkq0wrrh0pxds3q0dv6dhkw9cdpbib656xdkc1";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        features = {
+          "default" = [ "std" "i128" ];
+        };
+      };
+      "syn 1.0.109" = rec {
+        crateName = "syn";
+        version = "1.0.109";
+        edition = "2018";
+        sha256 = "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit-mut" ];
+      };
+      "syn 2.0.48" = rec {
+        crateName = "syn";
+        version = "2.0.48";
+        edition = "2021";
+        sha256 = "0gqgfygmrxmp8q32lia9p294kdd501ybn6kn2h4gqza0irik2d8g";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit" "visit-mut" ];
+      };
+      "sysinfo" = rec {
+        crateName = "sysinfo";
+        version = "0.30.5";
+        edition = "2018";
+        sha256 = "1clba87ndskvxddrmwysnjccm6vyr75j24p6ck48jqwgii1z7d0z";
+        authors = [
+          "Guillaume Gomez <guillaume1.gomez@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "core-foundation-sys";
+            packageId = "core-foundation-sys";
+            target = { target, features }: (("macos" == target."os" or null) || ("ios" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(("unknown" == target."os" or null) || ("wasm32" == target."arch" or null)));
+          }
+          {
+            name = "ntapi";
+            packageId = "ntapi";
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            target = { target, features }: ((target."windows" or false) || ("linux" == target."os" or null) || ("android" == target."os" or null));
+          }
+          {
+            name = "windows";
+            packageId = "windows";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Wdk_System_SystemInformation" "Wdk_System_SystemServices" "Wdk_System_Threading" "Win32_Foundation" "Win32_NetworkManagement_IpHelper" "Win32_NetworkManagement_NetManagement" "Win32_NetworkManagement_Ndis" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication_Identity" "Win32_Security_Authorization" "Win32_Storage_FileSystem" "Win32_System_Com" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_Ioctl" "Win32_System_LibraryLoader" "Win32_System_Kernel" "Win32_System_Memory" "Win32_System_Ole" "Win32_System_Performance" "Win32_System_Power" "Win32_System_ProcessStatus" "Win32_System_Registry" "Win32_System_RemoteDesktop" "Win32_System_Rpc" "Win32_System_SystemInformation" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_Variant" "Win32_System_WindowsProgramming" "Win32_System_Wmi" "Win32_UI_Shell" ];
+          }
+        ];
+        features = {
+          "apple-app-store" = [ "apple-sandbox" ];
+          "debug" = [ "libc/extra_traits" ];
+          "default" = [ "multithread" ];
+          "multithread" = [ "rayon" ];
+          "rayon" = [ "dep:rayon" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "target-features" = rec {
+        crateName = "target-features";
+        version = "0.1.5";
+        edition = "2021";
+        sha256 = "1gb974chm9aj8ifkyibylxkyb5an4bf5y8dxb18pqmck698gmdfg";
+        authors = [
+          "Caleb Zulawski <caleb.zulawski@gmail.com>"
+        ];
+
+      };
+      "thiserror" = rec {
+        crateName = "thiserror";
+        version = "1.0.56";
+        edition = "2021";
+        sha256 = "1b9hnzngjan4d89zjs16i01bcpcnvdwklyh73lj16xk28p37hhym";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "thiserror-impl";
+            packageId = "thiserror-impl";
+          }
+        ];
+
+      };
+      "thiserror-impl" = rec {
+        crateName = "thiserror-impl";
+        version = "1.0.56";
+        edition = "2021";
+        sha256 = "0w9ldp8fa574ilz4dn7y7scpcq66vdjy59qal8qdpwsh7faal3zs";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+          }
+        ];
+
+      };
+      "tokio" = rec {
+        crateName = "tokio";
+        version = "1.36.0";
+        edition = "2021";
+        sha256 = "0c89p36zbd4abr1z3l5mipp43x7z4c9b4vp4s6r8y0gs2mjmya31";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            target = { target, features }: (target."tokio_taskdump" or false);
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+            optional = true;
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "signal-hook-registry";
+            packageId = "signal-hook-registry";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+            features = [ "all" ];
+          }
+          {
+            name = "tokio-macros";
+            packageId = "tokio-macros";
+            optional = true;
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Security_Authorization" ];
+          }
+        ];
+        features = {
+          "bytes" = [ "dep:bytes" ];
+          "full" = [ "fs" "io-util" "io-std" "macros" "net" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "sync" "time" ];
+          "io-util" = [ "bytes" ];
+          "libc" = [ "dep:libc" ];
+          "macros" = [ "tokio-macros" ];
+          "mio" = [ "dep:mio" ];
+          "net" = [ "libc" "mio/os-poll" "mio/os-ext" "mio/net" "socket2" "windows-sys/Win32_Foundation" "windows-sys/Win32_Security" "windows-sys/Win32_Storage_FileSystem" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_System_SystemServices" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "process" = [ "bytes" "libc" "mio/os-poll" "mio/os-ext" "mio/net" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Threading" "windows-sys/Win32_System_WindowsProgramming" ];
+          "rt-multi-thread" = [ "num_cpus" "rt" ];
+          "signal" = [ "libc" "mio/os-poll" "mio/net" "mio/os-ext" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Console" ];
+          "signal-hook-registry" = [ "dep:signal-hook-registry" ];
+          "socket2" = [ "dep:socket2" ];
+          "test-util" = [ "rt" "sync" "time" ];
+          "tokio-macros" = [ "dep:tokio-macros" ];
+          "tracing" = [ "dep:tracing" ];
+          "windows-sys" = [ "dep:windows-sys" ];
+        };
+        resolvedDefaultFeatures = [ "bytes" "default" "fs" "full" "io-std" "io-util" "libc" "macros" "mio" "net" "num_cpus" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "time" "tokio-macros" "windows-sys" ];
+      };
+      "tokio-macros" = rec {
+        crateName = "tokio-macros";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-util" = rec {
+        crateName = "tokio-util";
+        version = "0.7.10";
+        edition = "2021";
+        sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "__docs_rs" = [ "futures-util" ];
+          "codec" = [ "tracing" ];
+          "compat" = [ "futures-io" ];
+          "full" = [ "codec" "compat" "io-util" "time" "net" "rt" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hashbrown" = [ "dep:hashbrown" ];
+          "io-util" = [ "io" "tokio/rt" "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "rt" = [ "tokio/rt" "tokio/sync" "futures-util" "hashbrown" ];
+          "slab" = [ "dep:slab" ];
+          "time" = [ "tokio/time" "slab" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "default" "io" "io-util" ];
+      };
+      "typenum" = rec {
+        crateName = "typenum";
+        version = "1.17.0";
+        edition = "2018";
+        sha256 = "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2";
+        build = "build/main.rs";
+        authors = [
+          "Paho Lurie-Gregg <paho@paholg.com>"
+          "Andre Bogus <bogusandre@gmail.com>"
+        ];
+        features = {
+          "scale-info" = [ "dep:scale-info" ];
+          "scale_info" = [ "scale-info/derive" ];
+        };
+      };
+      "unicode-ident" = rec {
+        crateName = "unicode-ident";
+        version = "1.0.12";
+        edition = "2018";
+        sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "unicode-width" = rec {
+        crateName = "unicode-width";
+        version = "0.1.11";
+        edition = "2015";
+        sha256 = "11ds4ydhg8g7l06rlmh712q41qsrd0j0h00n1jm74kww3kqk65z5";
+        authors = [
+          "kwantam <kwantam@gmail.com>"
+          "Manish Goregaokar <manishsmail@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "std" "core" "compiler_builtins" ];
+          "std" = [ "dep:std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "version_check" = rec {
+        crateName = "version_check";
+        version = "0.9.4";
+        edition = "2015";
+        sha256 = "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9";
+        authors = [
+          "Sergio Benitez <sb@sergio.bz>"
+        ];
+
+      };
+      "wasi" = rec {
+        crateName = "wasi";
+        version = "0.11.0+wasi-snapshot-preview1";
+        edition = "2018";
+        sha256 = "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw";
+        authors = [
+          "The Cranelift Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "rustc-std-workspace-alloc" ];
+          "rustc-std-workspace-alloc" = [ "dep:rustc-std-workspace-alloc" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "wasm-bindgen" = rec {
+        crateName = "wasm-bindgen";
+        version = "0.2.91";
+        edition = "2018";
+        sha256 = "0zwbb07ln4m5hh6axamc701nnj090nd66syxbf6bagzf189j9qf1";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "wasm-bindgen-macro";
+            packageId = "wasm-bindgen-macro";
+          }
+        ];
+        features = {
+          "default" = [ "spans" "std" ];
+          "enable-interning" = [ "std" ];
+          "gg-alloc" = [ "wasm-bindgen-test/gg-alloc" ];
+          "serde" = [ "dep:serde" ];
+          "serde-serialize" = [ "serde" "serde_json" "std" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "spans" = [ "wasm-bindgen-macro/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro/strict-macro" ];
+          "xxx_debug_only_print_generated_code" = [ "wasm-bindgen-macro/xxx_debug_only_print_generated_code" ];
+        };
+        resolvedDefaultFeatures = [ "default" "spans" "std" ];
+      };
+      "wasm-bindgen-backend" = rec {
+        crateName = "wasm-bindgen-backend";
+        version = "0.2.91";
+        edition = "2018";
+        sha256 = "02zpi9sjzhd8kfv1yj9m1bs4a41ik9ii5bc8hjf60arm1j8f3ry9";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "bumpalo";
+            packageId = "bumpalo";
+          }
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "full" ];
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro" = rec {
+        crateName = "wasm-bindgen-macro";
+        version = "0.2.91";
+        edition = "2018";
+        sha256 = "1va6dilw9kcnvsg5043h5b9mwc5sgq0lyhj9fif2n62qsgigj2mk";
+        procMacro = true;
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "wasm-bindgen-macro-support";
+            packageId = "wasm-bindgen-macro-support";
+          }
+        ];
+        features = {
+          "spans" = [ "wasm-bindgen-macro-support/spans" ];
+          "strict-macro" = [ "wasm-bindgen-macro-support/strict-macro" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-macro-support" = rec {
+        crateName = "wasm-bindgen-macro-support";
+        version = "0.2.91";
+        edition = "2018";
+        sha256 = "0rlyl3yzwbcnc691mvx78m1wbqf1qs52mlc3g88bh7ihwrdk4bv4";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            features = [ "visit" "full" ];
+          }
+          {
+            name = "wasm-bindgen-backend";
+            packageId = "wasm-bindgen-backend";
+          }
+          {
+            name = "wasm-bindgen-shared";
+            packageId = "wasm-bindgen-shared";
+          }
+        ];
+        features = {
+          "extra-traits" = [ "syn/extra-traits" ];
+          "spans" = [ "wasm-bindgen-backend/spans" ];
+        };
+        resolvedDefaultFeatures = [ "spans" ];
+      };
+      "wasm-bindgen-shared" = rec {
+        crateName = "wasm-bindgen-shared";
+        version = "0.2.91";
+        edition = "2018";
+        links = "wasm_bindgen";
+        sha256 = "0f4qmjv57ppwi4xpdxgcd77vz9vmvlrnybg8dj430hzhvk96n62g";
+        authors = [
+          "The wasm-bindgen Developers"
+        ];
+
+      };
+      "weave" = rec {
+        crateName = "weave";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "swizzle";
+            path = "src/bin/swizzle.rs";
+            requiredFeatures = [ ];
+          }
+          {
+            name = "weave";
+            path = "src/main.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./.; }
+          else ./.;
+        dependencies = [
+          {
+            name = "anyhow";
+            packageId = "anyhow";
+            features = [ "backtrace" ];
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+          }
+          {
+            name = "owning_ref";
+            packageId = "owning_ref";
+          }
+          {
+            name = "polars";
+            packageId = "polars";
+            features = [ "parquet" ];
+          }
+          {
+            name = "rayon";
+            packageId = "rayon";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "winapi" = rec {
+        crateName = "winapi";
+        version = "0.3.9";
+        edition = "2015";
+        sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-i686-pc-windows-gnu";
+            packageId = "winapi-i686-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+          }
+          {
+            name = "winapi-x86_64-pc-windows-gnu";
+            packageId = "winapi-x86_64-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+          }
+        ];
+        features = {
+          "debug" = [ "impl-debug" ];
+        };
+        resolvedDefaultFeatures = [ "cfg" "consoleapi" "evntrace" "handleapi" "impl-default" "in6addr" "inaddr" "minwinbase" "ntsecapi" "processenv" "synchapi" "winbase" "windef" "winerror" "winioctl" "winuser" ];
+      };
+      "winapi-i686-pc-windows-gnu" = rec {
+        crateName = "winapi-i686-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi-x86_64-pc-windows-gnu" = rec {
+        crateName = "winapi-x86_64-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "windows" = rec {
+        crateName = "windows";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1gnh210qjlprpd1szaq04rjm1zqgdm9j7l9absg0kawi2rwm72p4";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-core";
+            packageId = "windows-core";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+          }
+        ];
+        features = {
+          "AI_MachineLearning" = [ "AI" ];
+          "ApplicationModel_Activation" = [ "ApplicationModel" ];
+          "ApplicationModel_AppExtensions" = [ "ApplicationModel" ];
+          "ApplicationModel_AppService" = [ "ApplicationModel" ];
+          "ApplicationModel_Appointments" = [ "ApplicationModel" ];
+          "ApplicationModel_Appointments_AppointmentsProvider" = [ "ApplicationModel_Appointments" ];
+          "ApplicationModel_Appointments_DataProvider" = [ "ApplicationModel_Appointments" ];
+          "ApplicationModel_Background" = [ "ApplicationModel" ];
+          "ApplicationModel_Calls" = [ "ApplicationModel" ];
+          "ApplicationModel_Calls_Background" = [ "ApplicationModel_Calls" ];
+          "ApplicationModel_Calls_Provider" = [ "ApplicationModel_Calls" ];
+          "ApplicationModel_Chat" = [ "ApplicationModel" ];
+          "ApplicationModel_CommunicationBlocking" = [ "ApplicationModel" ];
+          "ApplicationModel_Contacts" = [ "ApplicationModel" ];
+          "ApplicationModel_Contacts_DataProvider" = [ "ApplicationModel_Contacts" ];
+          "ApplicationModel_Contacts_Provider" = [ "ApplicationModel_Contacts" ];
+          "ApplicationModel_ConversationalAgent" = [ "ApplicationModel" ];
+          "ApplicationModel_Core" = [ "ApplicationModel" ];
+          "ApplicationModel_DataTransfer" = [ "ApplicationModel" ];
+          "ApplicationModel_DataTransfer_DragDrop" = [ "ApplicationModel_DataTransfer" ];
+          "ApplicationModel_DataTransfer_DragDrop_Core" = [ "ApplicationModel_DataTransfer_DragDrop" ];
+          "ApplicationModel_DataTransfer_ShareTarget" = [ "ApplicationModel_DataTransfer" ];
+          "ApplicationModel_Email" = [ "ApplicationModel" ];
+          "ApplicationModel_Email_DataProvider" = [ "ApplicationModel_Email" ];
+          "ApplicationModel_ExtendedExecution" = [ "ApplicationModel" ];
+          "ApplicationModel_ExtendedExecution_Foreground" = [ "ApplicationModel_ExtendedExecution" ];
+          "ApplicationModel_Holographic" = [ "ApplicationModel" ];
+          "ApplicationModel_LockScreen" = [ "ApplicationModel" ];
+          "ApplicationModel_Payments" = [ "ApplicationModel" ];
+          "ApplicationModel_Payments_Provider" = [ "ApplicationModel_Payments" ];
+          "ApplicationModel_Preview" = [ "ApplicationModel" ];
+          "ApplicationModel_Preview_Holographic" = [ "ApplicationModel_Preview" ];
+          "ApplicationModel_Preview_InkWorkspace" = [ "ApplicationModel_Preview" ];
+          "ApplicationModel_Preview_Notes" = [ "ApplicationModel_Preview" ];
+          "ApplicationModel_Resources" = [ "ApplicationModel" ];
+          "ApplicationModel_Resources_Core" = [ "ApplicationModel_Resources" ];
+          "ApplicationModel_Resources_Management" = [ "ApplicationModel_Resources" ];
+          "ApplicationModel_Search" = [ "ApplicationModel" ];
+          "ApplicationModel_Search_Core" = [ "ApplicationModel_Search" ];
+          "ApplicationModel_Store" = [ "ApplicationModel" ];
+          "ApplicationModel_Store_LicenseManagement" = [ "ApplicationModel_Store" ];
+          "ApplicationModel_Store_Preview" = [ "ApplicationModel_Store" ];
+          "ApplicationModel_Store_Preview_InstallControl" = [ "ApplicationModel_Store_Preview" ];
+          "ApplicationModel_UserActivities" = [ "ApplicationModel" ];
+          "ApplicationModel_UserActivities_Core" = [ "ApplicationModel_UserActivities" ];
+          "ApplicationModel_UserDataAccounts" = [ "ApplicationModel" ];
+          "ApplicationModel_UserDataAccounts_Provider" = [ "ApplicationModel_UserDataAccounts" ];
+          "ApplicationModel_UserDataAccounts_SystemAccess" = [ "ApplicationModel_UserDataAccounts" ];
+          "ApplicationModel_UserDataTasks" = [ "ApplicationModel" ];
+          "ApplicationModel_UserDataTasks_DataProvider" = [ "ApplicationModel_UserDataTasks" ];
+          "ApplicationModel_VoiceCommands" = [ "ApplicationModel" ];
+          "ApplicationModel_Wallet" = [ "ApplicationModel" ];
+          "ApplicationModel_Wallet_System" = [ "ApplicationModel_Wallet" ];
+          "Data_Html" = [ "Data" ];
+          "Data_Json" = [ "Data" ];
+          "Data_Pdf" = [ "Data" ];
+          "Data_Text" = [ "Data" ];
+          "Data_Xml" = [ "Data" ];
+          "Data_Xml_Dom" = [ "Data_Xml" ];
+          "Data_Xml_Xsl" = [ "Data_Xml" ];
+          "Devices_Adc" = [ "Devices" ];
+          "Devices_Adc_Provider" = [ "Devices_Adc" ];
+          "Devices_Background" = [ "Devices" ];
+          "Devices_Bluetooth" = [ "Devices" ];
+          "Devices_Bluetooth_Advertisement" = [ "Devices_Bluetooth" ];
+          "Devices_Bluetooth_Background" = [ "Devices_Bluetooth" ];
+          "Devices_Bluetooth_GenericAttributeProfile" = [ "Devices_Bluetooth" ];
+          "Devices_Bluetooth_Rfcomm" = [ "Devices_Bluetooth" ];
+          "Devices_Custom" = [ "Devices" ];
+          "Devices_Display" = [ "Devices" ];
+          "Devices_Display_Core" = [ "Devices_Display" ];
+          "Devices_Enumeration" = [ "Devices" ];
+          "Devices_Enumeration_Pnp" = [ "Devices_Enumeration" ];
+          "Devices_Geolocation" = [ "Devices" ];
+          "Devices_Geolocation_Geofencing" = [ "Devices_Geolocation" ];
+          "Devices_Geolocation_Provider" = [ "Devices_Geolocation" ];
+          "Devices_Gpio" = [ "Devices" ];
+          "Devices_Gpio_Provider" = [ "Devices_Gpio" ];
+          "Devices_Haptics" = [ "Devices" ];
+          "Devices_HumanInterfaceDevice" = [ "Devices" ];
+          "Devices_I2c" = [ "Devices" ];
+          "Devices_I2c_Provider" = [ "Devices_I2c" ];
+          "Devices_Input" = [ "Devices" ];
+          "Devices_Input_Preview" = [ "Devices_Input" ];
+          "Devices_Lights" = [ "Devices" ];
+          "Devices_Lights_Effects" = [ "Devices_Lights" ];
+          "Devices_Midi" = [ "Devices" ];
+          "Devices_PointOfService" = [ "Devices" ];
+          "Devices_PointOfService_Provider" = [ "Devices_PointOfService" ];
+          "Devices_Portable" = [ "Devices" ];
+          "Devices_Power" = [ "Devices" ];
+          "Devices_Printers" = [ "Devices" ];
+          "Devices_Printers_Extensions" = [ "Devices_Printers" ];
+          "Devices_Pwm" = [ "Devices" ];
+          "Devices_Pwm_Provider" = [ "Devices_Pwm" ];
+          "Devices_Radios" = [ "Devices" ];
+          "Devices_Scanners" = [ "Devices" ];
+          "Devices_Sensors" = [ "Devices" ];
+          "Devices_Sensors_Custom" = [ "Devices_Sensors" ];
+          "Devices_SerialCommunication" = [ "Devices" ];
+          "Devices_SmartCards" = [ "Devices" ];
+          "Devices_Sms" = [ "Devices" ];
+          "Devices_Spi" = [ "Devices" ];
+          "Devices_Spi_Provider" = [ "Devices_Spi" ];
+          "Devices_Usb" = [ "Devices" ];
+          "Devices_WiFi" = [ "Devices" ];
+          "Devices_WiFiDirect" = [ "Devices" ];
+          "Devices_WiFiDirect_Services" = [ "Devices_WiFiDirect" ];
+          "Embedded_DeviceLockdown" = [ "Embedded" ];
+          "Foundation_Collections" = [ "Foundation" ];
+          "Foundation_Diagnostics" = [ "Foundation" ];
+          "Foundation_Metadata" = [ "Foundation" ];
+          "Foundation_Numerics" = [ "Foundation" ];
+          "Gaming_Input" = [ "Gaming" ];
+          "Gaming_Input_Custom" = [ "Gaming_Input" ];
+          "Gaming_Input_ForceFeedback" = [ "Gaming_Input" ];
+          "Gaming_Input_Preview" = [ "Gaming_Input" ];
+          "Gaming_Preview" = [ "Gaming" ];
+          "Gaming_Preview_GamesEnumeration" = [ "Gaming_Preview" ];
+          "Gaming_UI" = [ "Gaming" ];
+          "Gaming_XboxLive" = [ "Gaming" ];
+          "Gaming_XboxLive_Storage" = [ "Gaming_XboxLive" ];
+          "Globalization_Collation" = [ "Globalization" ];
+          "Globalization_DateTimeFormatting" = [ "Globalization" ];
+          "Globalization_Fonts" = [ "Globalization" ];
+          "Globalization_NumberFormatting" = [ "Globalization" ];
+          "Globalization_PhoneNumberFormatting" = [ "Globalization" ];
+          "Graphics_Capture" = [ "Graphics" ];
+          "Graphics_DirectX" = [ "Graphics" ];
+          "Graphics_DirectX_Direct3D11" = [ "Graphics_DirectX" ];
+          "Graphics_Display" = [ "Graphics" ];
+          "Graphics_Display_Core" = [ "Graphics_Display" ];
+          "Graphics_Effects" = [ "Graphics" ];
+          "Graphics_Holographic" = [ "Graphics" ];
+          "Graphics_Imaging" = [ "Graphics" ];
+          "Graphics_Printing" = [ "Graphics" ];
+          "Graphics_Printing3D" = [ "Graphics" ];
+          "Graphics_Printing_OptionDetails" = [ "Graphics_Printing" ];
+          "Graphics_Printing_PrintSupport" = [ "Graphics_Printing" ];
+          "Graphics_Printing_PrintTicket" = [ "Graphics_Printing" ];
+          "Graphics_Printing_Workflow" = [ "Graphics_Printing" ];
+          "Management_Core" = [ "Management" ];
+          "Management_Deployment" = [ "Management" ];
+          "Management_Deployment_Preview" = [ "Management_Deployment" ];
+          "Management_Policies" = [ "Management" ];
+          "Management_Update" = [ "Management" ];
+          "Management_Workplace" = [ "Management" ];
+          "Media_AppBroadcasting" = [ "Media" ];
+          "Media_AppRecording" = [ "Media" ];
+          "Media_Audio" = [ "Media" ];
+          "Media_Capture" = [ "Media" ];
+          "Media_Capture_Core" = [ "Media_Capture" ];
+          "Media_Capture_Frames" = [ "Media_Capture" ];
+          "Media_Casting" = [ "Media" ];
+          "Media_ClosedCaptioning" = [ "Media" ];
+          "Media_ContentRestrictions" = [ "Media" ];
+          "Media_Control" = [ "Media" ];
+          "Media_Core" = [ "Media" ];
+          "Media_Core_Preview" = [ "Media_Core" ];
+          "Media_Devices" = [ "Media" ];
+          "Media_Devices_Core" = [ "Media_Devices" ];
+          "Media_DialProtocol" = [ "Media" ];
+          "Media_Editing" = [ "Media" ];
+          "Media_Effects" = [ "Media" ];
+          "Media_FaceAnalysis" = [ "Media" ];
+          "Media_Import" = [ "Media" ];
+          "Media_MediaProperties" = [ "Media" ];
+          "Media_Miracast" = [ "Media" ];
+          "Media_Ocr" = [ "Media" ];
+          "Media_PlayTo" = [ "Media" ];
+          "Media_Playback" = [ "Media" ];
+          "Media_Playlists" = [ "Media" ];
+          "Media_Protection" = [ "Media" ];
+          "Media_Protection_PlayReady" = [ "Media_Protection" ];
+          "Media_Render" = [ "Media" ];
+          "Media_SpeechRecognition" = [ "Media" ];
+          "Media_SpeechSynthesis" = [ "Media" ];
+          "Media_Streaming" = [ "Media" ];
+          "Media_Streaming_Adaptive" = [ "Media_Streaming" ];
+          "Media_Transcoding" = [ "Media" ];
+          "Networking_BackgroundTransfer" = [ "Networking" ];
+          "Networking_Connectivity" = [ "Networking" ];
+          "Networking_NetworkOperators" = [ "Networking" ];
+          "Networking_Proximity" = [ "Networking" ];
+          "Networking_PushNotifications" = [ "Networking" ];
+          "Networking_ServiceDiscovery" = [ "Networking" ];
+          "Networking_ServiceDiscovery_Dnssd" = [ "Networking_ServiceDiscovery" ];
+          "Networking_Sockets" = [ "Networking" ];
+          "Networking_Vpn" = [ "Networking" ];
+          "Networking_XboxLive" = [ "Networking" ];
+          "Perception_Automation" = [ "Perception" ];
+          "Perception_Automation_Core" = [ "Perception_Automation" ];
+          "Perception_People" = [ "Perception" ];
+          "Perception_Spatial" = [ "Perception" ];
+          "Perception_Spatial_Preview" = [ "Perception_Spatial" ];
+          "Perception_Spatial_Surfaces" = [ "Perception_Spatial" ];
+          "Phone_ApplicationModel" = [ "Phone" ];
+          "Phone_Devices" = [ "Phone" ];
+          "Phone_Devices_Notification" = [ "Phone_Devices" ];
+          "Phone_Devices_Power" = [ "Phone_Devices" ];
+          "Phone_Management" = [ "Phone" ];
+          "Phone_Management_Deployment" = [ "Phone_Management" ];
+          "Phone_Media" = [ "Phone" ];
+          "Phone_Media_Devices" = [ "Phone_Media" ];
+          "Phone_Notification" = [ "Phone" ];
+          "Phone_Notification_Management" = [ "Phone_Notification" ];
+          "Phone_PersonalInformation" = [ "Phone" ];
+          "Phone_PersonalInformation_Provisioning" = [ "Phone_PersonalInformation" ];
+          "Phone_Speech" = [ "Phone" ];
+          "Phone_Speech_Recognition" = [ "Phone_Speech" ];
+          "Phone_StartScreen" = [ "Phone" ];
+          "Phone_System" = [ "Phone" ];
+          "Phone_System_Power" = [ "Phone_System" ];
+          "Phone_System_Profile" = [ "Phone_System" ];
+          "Phone_System_UserProfile" = [ "Phone_System" ];
+          "Phone_System_UserProfile_GameServices" = [ "Phone_System_UserProfile" ];
+          "Phone_System_UserProfile_GameServices_Core" = [ "Phone_System_UserProfile_GameServices" ];
+          "Phone_UI" = [ "Phone" ];
+          "Phone_UI_Input" = [ "Phone_UI" ];
+          "Security_Authentication" = [ "Security" ];
+          "Security_Authentication_Identity" = [ "Security_Authentication" ];
+          "Security_Authentication_Identity_Core" = [ "Security_Authentication_Identity" ];
+          "Security_Authentication_OnlineId" = [ "Security_Authentication" ];
+          "Security_Authentication_Web" = [ "Security_Authentication" ];
+          "Security_Authentication_Web_Core" = [ "Security_Authentication_Web" ];
+          "Security_Authentication_Web_Provider" = [ "Security_Authentication_Web" ];
+          "Security_Authorization" = [ "Security" ];
+          "Security_Authorization_AppCapabilityAccess" = [ "Security_Authorization" ];
+          "Security_Credentials" = [ "Security" ];
+          "Security_Credentials_UI" = [ "Security_Credentials" ];
+          "Security_Cryptography" = [ "Security" ];
+          "Security_Cryptography_Certificates" = [ "Security_Cryptography" ];
+          "Security_Cryptography_Core" = [ "Security_Cryptography" ];
+          "Security_Cryptography_DataProtection" = [ "Security_Cryptography" ];
+          "Security_DataProtection" = [ "Security" ];
+          "Security_EnterpriseData" = [ "Security" ];
+          "Security_ExchangeActiveSyncProvisioning" = [ "Security" ];
+          "Security_Isolation" = [ "Security" ];
+          "Services_Maps" = [ "Services" ];
+          "Services_Maps_Guidance" = [ "Services_Maps" ];
+          "Services_Maps_LocalSearch" = [ "Services_Maps" ];
+          "Services_Maps_OfflineMaps" = [ "Services_Maps" ];
+          "Services_Store" = [ "Services" ];
+          "Services_TargetedContent" = [ "Services" ];
+          "Storage_AccessCache" = [ "Storage" ];
+          "Storage_BulkAccess" = [ "Storage" ];
+          "Storage_Compression" = [ "Storage" ];
+          "Storage_FileProperties" = [ "Storage" ];
+          "Storage_Pickers" = [ "Storage" ];
+          "Storage_Pickers_Provider" = [ "Storage_Pickers" ];
+          "Storage_Provider" = [ "Storage" ];
+          "Storage_Search" = [ "Storage" ];
+          "Storage_Streams" = [ "Storage" ];
+          "System_Diagnostics" = [ "System" ];
+          "System_Diagnostics_DevicePortal" = [ "System_Diagnostics" ];
+          "System_Diagnostics_Telemetry" = [ "System_Diagnostics" ];
+          "System_Diagnostics_TraceReporting" = [ "System_Diagnostics" ];
+          "System_Display" = [ "System" ];
+          "System_Implementation" = [ "System" ];
+          "System_Implementation_FileExplorer" = [ "System_Implementation" ];
+          "System_Inventory" = [ "System" ];
+          "System_Power" = [ "System" ];
+          "System_Profile" = [ "System" ];
+          "System_Profile_SystemManufacturers" = [ "System_Profile" ];
+          "System_RemoteDesktop" = [ "System" ];
+          "System_RemoteDesktop_Input" = [ "System_RemoteDesktop" ];
+          "System_RemoteSystems" = [ "System" ];
+          "System_Threading" = [ "System" ];
+          "System_Threading_Core" = [ "System_Threading" ];
+          "System_Update" = [ "System" ];
+          "System_UserProfile" = [ "System" ];
+          "UI_Accessibility" = [ "UI" ];
+          "UI_ApplicationSettings" = [ "UI" ];
+          "UI_Composition" = [ "UI" ];
+          "UI_Composition_Core" = [ "UI_Composition" ];
+          "UI_Composition_Desktop" = [ "UI_Composition" ];
+          "UI_Composition_Diagnostics" = [ "UI_Composition" ];
+          "UI_Composition_Effects" = [ "UI_Composition" ];
+          "UI_Composition_Interactions" = [ "UI_Composition" ];
+          "UI_Composition_Scenes" = [ "UI_Composition" ];
+          "UI_Core" = [ "UI" ];
+          "UI_Core_AnimationMetrics" = [ "UI_Core" ];
+          "UI_Core_Preview" = [ "UI_Core" ];
+          "UI_Input" = [ "UI" ];
+          "UI_Input_Core" = [ "UI_Input" ];
+          "UI_Input_Inking" = [ "UI_Input" ];
+          "UI_Input_Inking_Analysis" = [ "UI_Input_Inking" ];
+          "UI_Input_Inking_Core" = [ "UI_Input_Inking" ];
+          "UI_Input_Inking_Preview" = [ "UI_Input_Inking" ];
+          "UI_Input_Preview" = [ "UI_Input" ];
+          "UI_Input_Preview_Injection" = [ "UI_Input_Preview" ];
+          "UI_Input_Spatial" = [ "UI_Input" ];
+          "UI_Notifications" = [ "UI" ];
+          "UI_Notifications_Management" = [ "UI_Notifications" ];
+          "UI_Popups" = [ "UI" ];
+          "UI_Shell" = [ "UI" ];
+          "UI_StartScreen" = [ "UI" ];
+          "UI_Text" = [ "UI" ];
+          "UI_Text_Core" = [ "UI_Text" ];
+          "UI_UIAutomation" = [ "UI" ];
+          "UI_UIAutomation_Core" = [ "UI_UIAutomation" ];
+          "UI_ViewManagement" = [ "UI" ];
+          "UI_ViewManagement_Core" = [ "UI_ViewManagement" ];
+          "UI_WebUI" = [ "UI" ];
+          "UI_WebUI_Core" = [ "UI_WebUI" ];
+          "UI_WindowManagement" = [ "UI" ];
+          "UI_WindowManagement_Preview" = [ "UI_WindowManagement" ];
+          "Wdk_Foundation" = [ "Wdk" ];
+          "Wdk_Graphics" = [ "Wdk" ];
+          "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ];
+          "Wdk_Storage" = [ "Wdk" ];
+          "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ];
+          "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ];
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_IO" = [ "Wdk_System" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Wdk_System_Registry" = [ "Wdk_System" ];
+          "Wdk_System_SystemInformation" = [ "Wdk_System" ];
+          "Wdk_System_SystemServices" = [ "Wdk_System" ];
+          "Wdk_System_Threading" = [ "Wdk_System" ];
+          "Web_AtomPub" = [ "Web" ];
+          "Web_Http" = [ "Web" ];
+          "Web_Http_Diagnostics" = [ "Web_Http" ];
+          "Web_Http_Filters" = [ "Web_Http" ];
+          "Web_Http_Headers" = [ "Web_Http" ];
+          "Web_Syndication" = [ "Web" ];
+          "Web_UI" = [ "Web" ];
+          "Web_UI_Interop" = [ "Web_UI" ];
+          "Win32_AI" = [ "Win32" ];
+          "Win32_AI_MachineLearning" = [ "Win32_AI" ];
+          "Win32_AI_MachineLearning_DirectML" = [ "Win32_AI_MachineLearning" ];
+          "Win32_AI_MachineLearning_WinML" = [ "Win32_AI_MachineLearning" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_CompositionSwapchain" = [ "Win32_Graphics" ];
+          "Win32_Graphics_DXCore" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct2D" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct2D_Common" = [ "Win32_Graphics_Direct2D" ];
+          "Win32_Graphics_Direct3D" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D10" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D11" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D11on12" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D12" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D9" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D9on12" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Direct3D_Dxc" = [ "Win32_Graphics_Direct3D" ];
+          "Win32_Graphics_Direct3D_Fxc" = [ "Win32_Graphics_Direct3D" ];
+          "Win32_Graphics_DirectComposition" = [ "Win32_Graphics" ];
+          "Win32_Graphics_DirectDraw" = [ "Win32_Graphics" ];
+          "Win32_Graphics_DirectManipulation" = [ "Win32_Graphics" ];
+          "Win32_Graphics_DirectWrite" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Dxgi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Dxgi_Common" = [ "Win32_Graphics_Dxgi" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Imaging" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Imaging_D2D" = [ "Win32_Graphics_Imaging" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectSound" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DirectShow" = [ "Win32_Media" ];
+          "Win32_Media_DirectShow_Tv" = [ "Win32_Media_DirectShow" ];
+          "Win32_Media_DirectShow_Xml" = [ "Win32_Media_DirectShow" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaFoundation" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_PictureAcquisition" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_Nvme" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_SideShow" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_TransactionServer" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_Variant" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WinRT" = [ "Win32_System" ];
+          "Win32_System_WinRT_AllJoyn" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Composition" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_CoreInputView" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Direct3D11" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Display" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Graphics" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Graphics_Capture" = [ "Win32_System_WinRT_Graphics" ];
+          "Win32_System_WinRT_Graphics_Direct2D" = [ "Win32_System_WinRT_Graphics" ];
+          "Win32_System_WinRT_Graphics_Imaging" = [ "Win32_System_WinRT_Graphics" ];
+          "Win32_System_WinRT_Holographic" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Isolation" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_ML" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Media" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Metadata" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Pdf" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Printing" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Shell" = [ "Win32_System_WinRT" ];
+          "Win32_System_WinRT_Storage" = [ "Win32_System_WinRT" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+          "implement" = [ "windows-implement" "windows-interface" "windows-core/implement" ];
+          "windows-implement" = [ "dep:windows-implement" ];
+          "windows-interface" = [ "dep:windows-interface" ];
+        };
+        resolvedDefaultFeatures = [ "Wdk" "Wdk_System" "Wdk_System_SystemInformation" "Wdk_System_SystemServices" "Wdk_System_Threading" "Win32" "Win32_Foundation" "Win32_NetworkManagement" "Win32_NetworkManagement_IpHelper" "Win32_NetworkManagement_Ndis" "Win32_NetworkManagement_NetManagement" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication" "Win32_Security_Authentication_Identity" "Win32_Security_Authorization" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Com" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_Ioctl" "Win32_System_Kernel" "Win32_System_LibraryLoader" "Win32_System_Memory" "Win32_System_Ole" "Win32_System_Performance" "Win32_System_Power" "Win32_System_ProcessStatus" "Win32_System_Registry" "Win32_System_RemoteDesktop" "Win32_System_Rpc" "Win32_System_SystemInformation" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_Variant" "Win32_System_WindowsProgramming" "Win32_System_Wmi" "Win32_UI" "Win32_UI_Shell" "default" ];
+      };
+      "windows-core" = rec {
+        crateName = "windows-core";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1nc3qv7sy24x0nlnb32f7alzpd6f72l4p24vl65vydbyil669ark";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "windows-sys 0.48.0" = rec {
+        crateName = "windows-sys";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+          }
+        ];
+        features = {
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_IO" "Win32_System_Pipes" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_WindowsProgramming" "default" ];
+      };
+      "windows-sys 0.52.0" = rec {
+        crateName = "windows-sys";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.0";
+          }
+        ];
+        features = {
+          "Wdk_Foundation" = [ "Wdk" ];
+          "Wdk_Graphics" = [ "Wdk" ];
+          "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ];
+          "Wdk_Storage" = [ "Wdk" ];
+          "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ];
+          "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ];
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_IO" = [ "Wdk_System" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Wdk_System_Registry" = [ "Wdk_System" ];
+          "Wdk_System_SystemInformation" = [ "Wdk_System" ];
+          "Wdk_System_SystemServices" = [ "Wdk_System" ];
+          "Wdk_System_Threading" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_Nvme" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_Variant" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_System" "Win32_System_Com" "Win32_UI" "Win32_UI_Shell" "default" ];
+      };
+      "windows-targets 0.48.5" = rec {
+        crateName = "windows-targets";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.48.5";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows-targets 0.52.0" = rec {
+        crateName = "windows-targets";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1kg7a27ynzw8zz3krdgy6w5gbqcji27j1sz4p7xk2j5j8082064a";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.52.0";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.52.0";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.52.0";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.52.0";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.52.0";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.52.0";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.52.0";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.48.5" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.52.0" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1shmn1kbdc0bpphcxz0vlph96bxz0h1jlmh93s9agf2dbpin8xyb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.48.5" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.52.0" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1vvmy1ypvzdvxn9yf0b8ygfl85gl2gpcyvsvqppsmlpisil07amv";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.48.5" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.52.0" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "04zkglz4p3pjsns5gbz85v4s5aw102raz4spj4b0lmm33z5kg1m2";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.48.5" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.52.0" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "16kvmbvx0vr0zbgnaz6nsks9ycvfh5xp05bjrhq65kj623iyirgz";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.48.5" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.52.0" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "1zdy4qn178sil5sdm63lm7f0kkcjg6gvdwmcprd2yjmwn8ns6vrx";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.48.5" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.52.0" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "17lllq4l2k1lqgcnw1cccphxp9vs7inq99kjlm2lfl9zklg7wr8s";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.48.5" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.52.0" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "012wfq37f18c09ij5m6rniw7xxn5fcvrxbqd0wd8vgnl3hfn9yfz";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "xxhash-rust" = rec {
+        crateName = "xxhash-rust";
+        version = "0.8.8";
+        edition = "2018";
+        sha256 = "0q9xl4kxibh61631lw9m7if7pkdvq3pp5ss52zdkxs6rirkhdgjk";
+        authors = [
+          "Douman <douman@gmx.se>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "xxh3" ];
+      };
+      "zerocopy" = rec {
+        crateName = "zerocopy";
+        version = "0.7.32";
+        edition = "2018";
+        sha256 = "1ghnfxw69kx5d1aqfd5fsfrra9dgpz17yqx84nd4ryjk3sbd7m3l";
+        authors = [
+          "Joshua Liebow-Feeser <joshlf@google.com>"
+        ];
+        dependencies = [
+          {
+            name = "zerocopy-derive";
+            packageId = "zerocopy-derive";
+            optional = true;
+          }
+          {
+            name = "zerocopy-derive";
+            packageId = "zerocopy-derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "zerocopy-derive";
+            packageId = "zerocopy-derive";
+          }
+        ];
+        features = {
+          "__internal_use_only_features_that_work_on_stable" = [ "alloc" "derive" "simd" ];
+          "byteorder" = [ "dep:byteorder" ];
+          "default" = [ "byteorder" ];
+          "derive" = [ "zerocopy-derive" ];
+          "simd-nightly" = [ "simd" ];
+          "zerocopy-derive" = [ "dep:zerocopy-derive" ];
+        };
+        resolvedDefaultFeatures = [ "simd" ];
+      };
+      "zerocopy-derive" = rec {
+        crateName = "zerocopy-derive";
+        version = "0.7.32";
+        edition = "2018";
+        sha256 = "19nj11md42aijyqnfx8pa647fjzhz537xyc624rajwwfrn6b3qcw";
+        procMacro = true;
+        authors = [
+          "Joshua Liebow-Feeser <joshlf@google.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+          }
+        ];
+
+      };
+      "zeroize" = rec {
+        crateName = "zeroize";
+        version = "1.7.0";
+        edition = "2021";
+        sha256 = "0bfvby7k9pdp6623p98yz2irqnamcyzpn7zh20nqmdn68b0lwnsj";
+        authors = [
+          "The RustCrypto Project Developers"
+        ];
+        features = {
+          "default" = [ "alloc" ];
+          "derive" = [ "zeroize_derive" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "zeroize_derive" = [ "dep:zeroize_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "zstd" = rec {
+        crateName = "zstd";
+        version = "0.13.0";
+        edition = "2018";
+        sha256 = "0401q54s9r35x2i7m1kwppgkj79g0pb6xz3xpby7qlkdb44k7yxz";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "zstd-safe";
+            packageId = "zstd-safe";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        features = {
+          "arrays" = [ "zstd-safe/arrays" ];
+          "bindgen" = [ "zstd-safe/bindgen" ];
+          "debug" = [ "zstd-safe/debug" ];
+          "default" = [ "legacy" "arrays" "zdict_builder" ];
+          "experimental" = [ "zstd-safe/experimental" ];
+          "fat-lto" = [ "zstd-safe/fat-lto" ];
+          "legacy" = [ "zstd-safe/legacy" ];
+          "no_asm" = [ "zstd-safe/no_asm" ];
+          "pkg-config" = [ "zstd-safe/pkg-config" ];
+          "thin" = [ "zstd-safe/thin" ];
+          "thin-lto" = [ "zstd-safe/thin-lto" ];
+          "zdict_builder" = [ "zstd-safe/zdict_builder" ];
+          "zstdmt" = [ "zstd-safe/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "default" "legacy" "zdict_builder" ];
+      };
+      "zstd-safe" = rec {
+        crateName = "zstd-safe";
+        version = "7.0.0";
+        edition = "2018";
+        sha256 = "0gpav2lcibrpmyslmjkcn3w0w64qif3jjljd2h8lr4p249s7qx23";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "zstd-sys";
+            packageId = "zstd-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "bindgen" = [ "zstd-sys/bindgen" ];
+          "debug" = [ "zstd-sys/debug" ];
+          "default" = [ "legacy" "arrays" "zdict_builder" ];
+          "experimental" = [ "zstd-sys/experimental" ];
+          "fat-lto" = [ "zstd-sys/fat-lto" ];
+          "legacy" = [ "zstd-sys/legacy" ];
+          "no_asm" = [ "zstd-sys/no_asm" ];
+          "pkg-config" = [ "zstd-sys/pkg-config" ];
+          "std" = [ "zstd-sys/std" ];
+          "thin" = [ "zstd-sys/thin" ];
+          "thin-lto" = [ "zstd-sys/thin-lto" ];
+          "zdict_builder" = [ "zstd-sys/zdict_builder" ];
+          "zstdmt" = [ "zstd-sys/zstdmt" ];
+        };
+        resolvedDefaultFeatures = [ "arrays" "legacy" "std" "zdict_builder" ];
+      };
+      "zstd-sys" = rec {
+        crateName = "zstd-sys";
+        version = "2.0.9+zstd.1.5.5";
+        edition = "2018";
+        links = "zstd";
+        sha256 = "0mk6a2367swdi22zg03lcackpnvgq96d7120awd4i83lm2lfy5ly";
+        authors = [
+          "Alexandre Bury <alexandre.bury@gmail.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+            features = [ "parallel" ];
+          }
+          {
+            name = "pkg-config";
+            packageId = "pkg-config";
+          }
+        ];
+        features = {
+          "bindgen" = [ "dep:bindgen" ];
+          "default" = [ "legacy" "zdict_builder" ];
+        };
+        resolvedDefaultFeatures = [ "legacy" "std" "zdict_builder" ];
+      };
+    };
+
+    #
+    # crate2nix/default.nix (excerpt start)
+    #
+
+    /* Target (platform) data for conditional dependencies.
+      This corresponds roughly to what buildRustCrate is setting.
+    */
+    makeDefaultTarget = platform: {
+      unix = platform.isUnix;
+      windows = platform.isWindows;
+      fuchsia = true;
+      test = false;
+
+      /* We are choosing an arbitrary rust version to grab `lib` from,
+      which is unfortunate, but `lib` has been version-agnostic the
+      whole time so this is good enough for now.
+      */
+      os = pkgs.rust.lib.toTargetOs platform;
+      arch = pkgs.rust.lib.toTargetArch platform;
+      family = pkgs.rust.lib.toTargetFamily platform;
+      vendor = pkgs.rust.lib.toTargetVendor platform;
+      env = "gnu";
+      endian =
+        if platform.parsed.cpu.significantByte.name == "littleEndian"
+        then "little" else "big";
+      pointer_width = toString platform.parsed.cpu.bits;
+      debug_assertions = false;
+    };
+
+    /* Filters common temp files and build files. */
+    # TODO(pkolloch): Substitute with gitignore filter
+    sourceFilter = name: type:
+      let
+        baseName = builtins.baseNameOf (builtins.toString name);
+      in
+        ! (
+          # Filter out git
+          baseName == ".gitignore"
+          || (type == "directory" && baseName == ".git")
+
+          # Filter out build results
+          || (
+            type == "directory" && (
+              baseName == "target"
+              || baseName == "_site"
+              || baseName == ".sass-cache"
+              || baseName == ".jekyll-metadata"
+              || baseName == "build-artifacts"
+            )
+          )
+
+          # Filter out nix-build result symlinks
+          || (
+            type == "symlink" && lib.hasPrefix "result" baseName
+          )
+
+          # Filter out IDE config
+          || (
+            type == "directory" && (
+              baseName == ".idea" || baseName == ".vscode"
+            )
+          ) || lib.hasSuffix ".iml" baseName
+
+          # Filter out nix build files
+          || baseName == "Cargo.nix"
+
+          # Filter out editor backup / swap files.
+          || lib.hasSuffix "~" baseName
+          || builtins.match "^\\.sw[a-z]$$" baseName != null
+          || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null
+          || lib.hasSuffix ".tmp" baseName
+          || lib.hasSuffix ".bak" baseName
+          || baseName == "tests.nix"
+        );
+
+    /* Returns a crate which depends on successful test execution
+      of crate given as the second argument.
+
+      testCrateFlags: list of flags to pass to the test exectuable
+      testInputs: list of packages that should be available during test execution
+    */
+    crateWithTest = { crate, testCrate, testCrateFlags, testInputs, testPreRun, testPostRun }:
+      assert builtins.typeOf testCrateFlags == "list";
+      assert builtins.typeOf testInputs == "list";
+      assert builtins.typeOf testPreRun == "string";
+      assert builtins.typeOf testPostRun == "string";
+      let
+        # override the `crate` so that it will build and execute tests instead of
+        # building the actual lib and bin targets We just have to pass `--test`
+        # to rustc and it will do the right thing.  We execute the tests and copy
+        # their log and the test executables to $out for later inspection.
+        test =
+          let
+            drv = testCrate.override
+              (
+                _: {
+                  buildTests = true;
+                }
+              );
+            # If the user hasn't set any pre/post commands, we don't want to
+            # insert empty lines. This means that any existing users of crate2nix
+            # don't get a spurious rebuild unless they set these explicitly.
+            testCommand = pkgs.lib.concatStringsSep "\n"
+              (pkgs.lib.filter (s: s != "") [
+                testPreRun
+                "$f $testCrateFlags 2>&1 | tee -a $out"
+                testPostRun
+              ]);
+          in
+          pkgs.runCommand "run-tests-${testCrate.name}"
+            {
+              inherit testCrateFlags;
+              buildInputs = testInputs;
+            } ''
+            set -e
+
+            export RUST_BACKTRACE=1
+
+            # recreate a file hierarchy as when running tests with cargo
+
+            # the source for test data
+            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
+            # instead of $NIX_BUILD_TOP/
+            # because we compiled those test binaries in the former and not the latter.
+            # So all paths will expect source tree to be there and not in the build top directly.
+            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
+            # TODO(raitobezarius): I believe there could be more edge cases if `crate.sourceRoot`
+            # do exist but it's very hard to reason about them, so let's wait until the first bug report.
+            mkdir -p source/
+            cd source/
+
+            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+
+            # build outputs
+            testRoot=target/debug
+            mkdir -p $testRoot
+
+            # executables of the crate
+            # we copy to prevent std::env::current_exe() to resolve to a store location
+            for i in ${crate}/bin/*; do
+              cp "$i" "$testRoot"
+            done
+            chmod +w -R .
+
+            # test harness executables are suffixed with a hash, like cargo does
+            # this allows to prevent name collision with the main
+            # executables of the crate
+            hash=$(basename $out)
+            for file in ${drv}/tests/*; do
+              f=$testRoot/$(basename $file)-$hash
+              cp $file $f
+              ${testCommand}
+            done
+          '';
+      in
+      pkgs.runCommand "${crate.name}-linked"
+        {
+          inherit (crate) outputs crateName;
+          passthru = (crate.passthru or { }) // {
+            inherit test;
+          };
+        }
+        (lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+          echo tested by ${test}
+        '' + ''
+          ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs}
+        '');
+
+    /* A restricted overridable version of builtRustCratesWithFeatures. */
+    buildRustCrateWithFeatures =
+      { packageId
+      , features ? rootFeatures
+      , crateOverrides ? defaultCrateOverrides
+      , buildRustCrateForPkgsFunc ? null
+      , runTests ? false
+      , testCrateFlags ? [ ]
+      , testInputs ? [ ]
+        # Any command to run immediatelly before a test is executed.
+      , testPreRun ? ""
+        # Any command run immediatelly after a test is executed.
+      , testPostRun ? ""
+      }:
+      lib.makeOverridable
+        (
+          { features
+          , crateOverrides
+          , runTests
+          , testCrateFlags
+          , testInputs
+          , testPreRun
+          , testPostRun
+          }:
+          let
+            buildRustCrateForPkgsFuncOverriden =
+              if buildRustCrateForPkgsFunc != null
+              then buildRustCrateForPkgsFunc
+              else
+                (
+                  if crateOverrides == pkgs.defaultCrateOverrides
+                  then buildRustCrateForPkgs
+                  else
+                    pkgs: (buildRustCrateForPkgs pkgs).override {
+                      defaultCrateOverrides = crateOverrides;
+                    }
+                );
+            builtRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = false;
+            };
+            builtTestRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = true;
+            };
+            drv = builtRustCrates.crates.${packageId};
+            testDrv = builtTestRustCrates.crates.${packageId};
+            derivation =
+              if runTests then
+                crateWithTest
+                  {
+                    crate = drv;
+                    testCrate = testDrv;
+                    inherit testCrateFlags testInputs testPreRun testPostRun;
+                  }
+              else drv;
+          in
+          derivation
+        )
+        { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun; };
+
+    /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc
+      for the corresponding crate.
+    */
+    builtRustCratesWithFeatures =
+      { packageId
+      , features
+      , crateConfigs ? crates
+      , buildRustCrateForPkgsFunc
+      , runTests
+      , makeTarget ? makeDefaultTarget
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isList features);
+        assert (builtins.isAttrs (makeTarget stdenv.hostPlatform));
+        assert (builtins.isBool runTests);
+        let
+          rootPackageId = packageId;
+          mergedFeatures = mergePackageFeatures
+            (
+              args // {
+                inherit rootPackageId;
+                target = makeTarget stdenv.hostPlatform // { test = runTests; };
+              }
+            );
+          # Memoize built packages so that reappearing packages are only built once.
+          builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs;
+          mkBuiltByPackageIdByPkgs = pkgs:
+            let
+              self = {
+                crates = lib.mapAttrs (packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId) crateConfigs;
+                target = makeTarget pkgs.stdenv.hostPlatform;
+                build = mkBuiltByPackageIdByPkgs pkgs.buildPackages;
+              };
+            in
+            self;
+          buildByPackageIdForPkgsImpl = self: pkgs: packageId:
+            let
+              features = mergedFeatures."${packageId}" or [ ];
+              crateConfig' = crateConfigs."${packageId}";
+              crateConfig =
+                builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ];
+              devDependencies =
+                lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig'.devDependencies or [ ]);
+              dependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self) target;
+                  buildByPackageId = depPackageId:
+                    # proc_macro crates must be compiled for the build architecture
+                    if crateConfigs.${depPackageId}.procMacro or false
+                    then self.build.crates.${depPackageId}
+                    else self.crates.${depPackageId};
+                  dependencies =
+                    (crateConfig.dependencies or [ ])
+                    ++ devDependencies;
+                };
+              buildDependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self.build) target;
+                  buildByPackageId = depPackageId:
+                    self.build.crates.${depPackageId};
+                  dependencies = crateConfig.buildDependencies or [ ];
+                };
+              dependenciesWithRenames =
+                let
+                  buildDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self) target;
+                    dependencies = crateConfig.dependencies or [ ] ++ devDependencies;
+                  };
+                  hostDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self.build) target;
+                    dependencies = crateConfig.buildDependencies or [ ];
+                  };
+                in
+                lib.filter (d: d ? "rename") (hostDeps ++ buildDeps);
+              # Crate renames have the form:
+              #
+              # {
+              #    crate_name = [
+              #       { version = "1.2.3"; rename = "crate_name01"; }
+              #    ];
+              #    # ...
+              # }
+              crateRenames =
+                let
+                  grouped =
+                    lib.groupBy
+                      (dependency: dependency.name)
+                      dependenciesWithRenames;
+                  versionAndRename = dep:
+                    let
+                      package = crateConfigs."${dep.packageId}";
+                    in
+                    { inherit (dep) rename; inherit (package) version; };
+                in
+                lib.mapAttrs (name: builtins.map versionAndRename) grouped;
+            in
+            buildRustCrateForPkgsFunc pkgs
+              (
+                crateConfig // {
+                  # https://github.com/NixOS/nixpkgs/issues/218712
+                  dontStrip = stdenv.hostPlatform.isDarwin;
+                  src = crateConfig.src or (
+                    pkgs.fetchurl rec {
+                      name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz";
+                      # https://www.pietroalbini.org/blog/downloading-crates-io/
+                      # Not rate-limited, CDN URL.
+                      url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate";
+                      sha256 =
+                        assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}");
+                        crateConfig.sha256;
+                    }
+                  );
+                  extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}";
+                  inherit features dependencies buildDependencies crateRenames release;
+                }
+              );
+        in
+        builtByPackageIdByPkgs;
+
+    /* Returns the actual derivations for the given dependencies. */
+    dependencyDerivations =
+      { buildByPackageId
+      , features
+      , dependencies
+      , target
+      }:
+        assert (builtins.isList features);
+        assert (builtins.isList dependencies);
+        assert (builtins.isAttrs target);
+        let
+          enabledDependencies = filterEnabledDependencies {
+            inherit dependencies features target;
+          };
+          depDerivation = dependency: buildByPackageId dependency.packageId;
+        in
+        map depDerivation enabledDependencies;
+
+    /* Returns a sanitized version of val with all values substituted that cannot
+      be serialized as JSON.
+    */
+    sanitizeForJson = val:
+      if builtins.isAttrs val
+      then lib.mapAttrs (n: sanitizeForJson) val
+      else if builtins.isList val
+      then builtins.map sanitizeForJson val
+      else if builtins.isFunction val
+      then "function"
+      else val;
+
+    /* Returns various tools to debug a crate. */
+    debugCrate = { packageId, target ? makeDefaultTarget stdenv.hostPlatform }:
+      assert (builtins.isString packageId);
+      let
+        debug = rec {
+          # The built tree as passed to buildRustCrate.
+          buildTree = buildRustCrateWithFeatures {
+            buildRustCrateForPkgsFunc = _: lib.id;
+            inherit packageId;
+          };
+          sanitizedBuildTree = sanitizeForJson buildTree;
+          dependencyTree = sanitizeForJson
+            (
+              buildRustCrateWithFeatures {
+                buildRustCrateForPkgsFunc = _: crate: {
+                  "01_crateName" = crate.crateName or false;
+                  "02_features" = crate.features or [ ];
+                  "03_dependencies" = crate.dependencies or [ ];
+                };
+                inherit packageId;
+              }
+            );
+          mergedPackageFeatures = mergePackageFeatures {
+            features = rootFeatures;
+            inherit packageId target;
+          };
+          diffedDefaultPackageFeatures = diffDefaultPackageFeatures {
+            inherit packageId target;
+          };
+        };
+      in
+      { internal = debug; };
+
+    /* Returns differences between cargo default features and crate2nix default
+      features.
+
+      This is useful for verifying the feature resolution in crate2nix.
+    */
+    diffDefaultPackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , target
+      }:
+        assert (builtins.isAttrs crateConfigs);
+        let
+          prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; });
+          mergedFeatures =
+            prefixValues
+              "crate2nix"
+              (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; });
+          configs = prefixValues "cargo" crateConfigs;
+          combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ];
+          onlyInCargo =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined);
+          onlyInCrate2Nix =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined);
+          differentFeatures = lib.filterAttrs
+            (
+              n: v:
+                (v ? "crate2nix")
+                && (v ? "cargo")
+                && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ])
+            )
+            combined;
+        in
+        builtins.toJSON {
+          inherit onlyInCargo onlyInCrate2Nix differentFeatures;
+        };
+
+    /* Returns an attrset mapping packageId to the list of enabled features.
+
+      If multiple paths to a dependency enable different features, the
+      corresponding feature sets are merged. Features in rust are additive.
+    */
+    mergePackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , rootPackageId ? packageId
+      , features ? rootFeatures
+      , dependencyPath ? [ crates.${packageId}.crateName ]
+      , featuresByPackageId ? { }
+      , target
+        # Adds devDependencies to the crate with rootPackageId.
+      , runTests ? false
+      , ...
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isString rootPackageId);
+        assert (builtins.isList features);
+        assert (builtins.isList dependencyPath);
+        assert (builtins.isAttrs featuresByPackageId);
+        assert (builtins.isAttrs target);
+        assert (builtins.isBool runTests);
+        let
+          crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
+          expandedFeatures = expandFeatures (crateConfig.features or { }) features;
+          enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
+          depWithResolvedFeatures = dependency:
+            let
+              inherit (dependency) packageId;
+              features = dependencyFeatures enabledFeatures dependency;
+            in
+            { inherit packageId features; };
+          resolveDependencies = cache: path: dependencies:
+            assert (builtins.isAttrs cache);
+            assert (builtins.isList dependencies);
+            let
+              enabledDependencies = filterEnabledDependencies {
+                inherit dependencies target;
+                features = enabledFeatures;
+              };
+              directDependencies = map depWithResolvedFeatures enabledDependencies;
+              foldOverCache = op: lib.foldl op cache directDependencies;
+            in
+            foldOverCache
+              (
+                cache: { packageId, features }:
+                  let
+                    cacheFeatures = cache.${packageId} or [ ];
+                    combinedFeatures = sortedUnique (cacheFeatures ++ features);
+                  in
+                  if cache ? ${packageId} && cache.${packageId} == combinedFeatures
+                  then cache
+                  else
+                    mergePackageFeatures {
+                      features = combinedFeatures;
+                      featuresByPackageId = cache;
+                      inherit crateConfigs packageId target runTests rootPackageId;
+                    }
+              );
+          cacheWithSelf =
+            let
+              cacheFeatures = featuresByPackageId.${packageId} or [ ];
+              combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
+            in
+            featuresByPackageId // {
+              "${packageId}" = combinedFeatures;
+            };
+          cacheWithDependencies =
+            resolveDependencies cacheWithSelf "dep"
+              (
+                crateConfig.dependencies or [ ]
+                ++ lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig.devDependencies or [ ])
+              );
+          cacheWithAll =
+            resolveDependencies
+              cacheWithDependencies "build"
+              (crateConfig.buildDependencies or [ ]);
+        in
+        cacheWithAll;
+
+    /* Returns the enabled dependencies given the enabled features. */
+    filterEnabledDependencies = { dependencies, features, target }:
+      assert (builtins.isList dependencies);
+      assert (builtins.isList features);
+      assert (builtins.isAttrs target);
+
+      lib.filter
+        (
+          dep:
+          let
+            targetFunc = dep.target or (features: true);
+          in
+          targetFunc { inherit features target; }
+          && (
+            !(dep.optional or false)
+            || builtins.any (doesFeatureEnableDependency dep) features
+          )
+        )
+        dependencies;
+
+    /* Returns whether the given feature should enable the given dependency. */
+    doesFeatureEnableDependency = dependency: feature:
+      let
+        name = dependency.rename or dependency.name;
+        prefix = "${name}/";
+        len = builtins.stringLength prefix;
+        startsWithPrefix = builtins.substring 0 len feature == prefix;
+      in
+      feature == name || feature == "dep:" + name || startsWithPrefix;
+
+    /* Returns the expanded features for the given inputFeatures by applying the
+      rules in featureMap.
+
+      featureMap is an attribute set which maps feature names to lists of further
+      feature names to enable in case this feature is selected.
+    */
+    expandFeatures = featureMap: inputFeatures:
+      assert (builtins.isAttrs featureMap);
+      assert (builtins.isList inputFeatures);
+      let
+        expandFeaturesNoCycle = oldSeen: inputFeatures:
+          if inputFeatures != [ ]
+          then
+            let
+              # The feature we're currently expanding.
+              feature = builtins.head inputFeatures;
+              # All the features we've seen/expanded so far, including the one
+              # we're currently processing.
+              seen = oldSeen // { ${feature} = 1; };
+              # Expand the feature but be careful to not re-introduce a feature
+              # that we've already seen: this can easily cause a cycle, see issue
+              # #209.
+              enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]);
+            in
+            [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
+          # No more features left, nothing to expand to.
+          else [ ];
+        outFeatures = expandFeaturesNoCycle { } inputFeatures;
+      in
+      sortedUnique outFeatures;
+
+    /* This function adds optional dependencies as features if they are enabled
+      indirectly by dependency features. This function mimics Cargo's behavior
+      described in a note at:
+      https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
+    */
+    enableFeatures = dependencies: features:
+      assert (builtins.isList features);
+      assert (builtins.isList dependencies);
+      let
+        additionalFeatures = lib.concatMap
+          (
+            dependency:
+              assert (builtins.isAttrs dependency);
+              let
+                enabled = builtins.any (doesFeatureEnableDependency dependency) features;
+              in
+              if (dependency.optional or false) && enabled
+              then [ (dependency.rename or dependency.name) ]
+              else [ ]
+          )
+          dependencies;
+      in
+      sortedUnique (features ++ additionalFeatures);
+
+    /*
+      Returns the actual features for the given dependency.
+
+      features: The features of the crate that refers this dependency.
+    */
+    dependencyFeatures = features: dependency:
+      assert (builtins.isList features);
+      assert (builtins.isAttrs dependency);
+      let
+        defaultOrNil =
+          if dependency.usesDefaultFeatures or true
+          then [ "default" ]
+          else [ ];
+        explicitFeatures = dependency.features or [ ];
+        additionalDependencyFeatures =
+          let
+            name = dependency.rename or dependency.name;
+            stripPrefixMatch = prefix: s:
+              if lib.hasPrefix prefix s
+              then lib.removePrefix prefix s
+              else null;
+            extractFeature = feature: lib.findFirst
+              (f: f != null)
+              null
+              (map (prefix: stripPrefixMatch prefix feature) [
+                (name + "/")
+                (name + "?/")
+              ]);
+            dependencyFeatures = lib.filter (f: f != null) (map extractFeature features);
+          in
+          dependencyFeatures;
+      in
+      defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures;
+
+    /* Sorts and removes duplicates from a list of strings. */
+    sortedUnique = features:
+      assert (builtins.isList features);
+      assert (builtins.all builtins.isString features);
+      let
+        outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features;
+        outFeaturesUnique = builtins.attrNames outFeaturesSet;
+      in
+      builtins.sort (a: b: a < b) outFeaturesUnique;
+
+    deprecationWarning = message: value:
+      if strictDeprecation
+      then builtins.throw "strictDeprecation enabled, aborting: ${message}"
+      else builtins.trace message value;
+
+    #
+    # crate2nix/default.nix (excerpt end)
+    #
+  };
+}
+
diff --git a/tvix/tools/weave/Cargo.toml b/tvix/tools/weave/Cargo.toml
new file mode 100644
index 0000000000..7c6c2d2f0c
--- /dev/null
+++ b/tvix/tools/weave/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "weave"
+version = "0.1.0"
+edition = "2021"
+
+[workspace]
+members = ["."]
+
+# TODO(edef): cut down on required features, this is kind of a grab bag right now
+[dependencies]
+anyhow = { version = "1.0.79", features = ["backtrace"] }
+hashbrown = "0.14.3"
+nix-compat = { version = "0.1.0", path = "../../nix-compat" }
+owning_ref = "0.4.1"
+rayon = "1.8.1"
+tokio = { version = "1.36.0", features = ["full"] }
+
+[dependencies.polars]
+version = "0.36.2"
+features = ["parquet"]
diff --git a/tvix/tools/weave/OWNERS b/tvix/tools/weave/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/tvix/tools/weave/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/tvix/tools/weave/default.nix b/tvix/tools/weave/default.nix
new file mode 100644
index 0000000000..5a84d7f642
--- /dev/null
+++ b/tvix/tools/weave/default.nix
@@ -0,0 +1,3 @@
+{ pkgs, ... }:
+
+(pkgs.callPackage ./Cargo.nix { }).rootCrate.build
diff --git a/tvix/tools/weave/src/bin/swizzle.rs b/tvix/tools/weave/src/bin/swizzle.rs
new file mode 100644
index 0000000000..68c1858126
--- /dev/null
+++ b/tvix/tools/weave/src/bin/swizzle.rs
@@ -0,0 +1,114 @@
+//! Swizzle reads a `narinfo.parquet` file, usually produced by `narinfo2parquet`.
+//!
+//! It swizzles the reference list, ie it converts the references from absolute,
+//! global identifiers (store path hashes) to indices into the `store_path_hash`
+//! column (ie, row numbers), so that we can later walk the reference graph
+//! efficiently.
+//!
+//! Path hashes are represented as non-null, 20-byte `Binary` values.
+//! The indices are represented as 32-bit unsigned integers, with in-band nulls
+//! represented by [INDEX_NULL] (the all-1 bit pattern), to permit swizzling
+//! partial datasets.
+//!
+//! In essence, it converts from names to pointers, so that `weave` can simply
+//! chase pointers to trace the live set. This replaces an `O(log(n))` lookup
+//! with `O(1)` indexing, and produces a much denser representation that actually
+//! fits in memory.
+//!
+//! The in-memory representation is at least 80% smaller, and the indices compress
+//! well in Parquet due to both temporal locality of reference and the power law
+//! distribution of reference "popularity".
+//!
+//! Only two columns are read from `narinfo.parquet`:
+//!
+//!  * `store_path_hash :: PathHash`
+//!  * `references :: List[PathHash]`
+//!
+//! Output is written to `narinfo-references.parquet` in the form of a single
+//! `List[u32]` column, `reference_idxs`.
+//!
+//! This file is inherently bound to the corresponding `narinfo.parquet`,
+//! since it essentially contains pointers into this file.
+
+use anyhow::Result;
+use hashbrown::HashTable;
+use polars::prelude::*;
+use rayon::prelude::*;
+use std::fs::File;
+use tokio::runtime::Runtime;
+
+use weave::{as_fixed_binary, hash64, load_ph_array, DONE, INDEX_NULL};
+
+fn main() -> Result<()> {
+    let ph_array = load_ph_array()?;
+
+    // TODO(edef): re-parallelise this
+    // We originally parallelised on chunks, but ph_array is only a single chunk, due to how Parquet loading works.
+    // TODO(edef): outline the 64-bit hash prefix? it's an indirection, but it saves ~2G of memory
+    eprint!("… build index\r");
+    let ph_map: HashTable<(u64, u32)> = {
+        let mut ph_map = HashTable::with_capacity(ph_array.len());
+
+        for (offset, item) in ph_array.iter().enumerate() {
+            let offset = offset as u32;
+            let hash = hash64(item);
+            ph_map.insert_unique(hash, (hash, offset), |&(hash, _)| hash);
+        }
+
+        ph_map
+    };
+    eprintln!("{DONE}");
+
+    eprint!("… swizzle references\r");
+    let mut pq = ParquetReader::new(File::open("narinfo.parquet")?)
+        .with_columns(Some(vec!["references".into()]))
+        .batched(1 << 16)?;
+
+    let mut reference_idxs =
+        Series::new_empty("reference_idxs", &DataType::List(DataType::UInt32.into()));
+
+    let mut bounce = vec![];
+    let runtime = Runtime::new()?;
+    while let Some(batches) = runtime.block_on(pq.next_batches(48))? {
+        batches
+            .into_par_iter()
+            .map(|df| -> ListChunked {
+                df.column("references")
+                    .unwrap()
+                    .list()
+                    .unwrap()
+                    .apply_to_inner(&|series: Series| -> PolarsResult<Series> {
+                        let series = series.binary()?;
+                        let mut out: Vec<u32> = Vec::with_capacity(series.len());
+
+                        out.extend(as_fixed_binary::<20>(series).flat_map(|xs| xs).map(|key| {
+                            let hash = hash64(&key);
+                            ph_map
+                                .find(hash, |&(candidate_hash, candidate_index)| {
+                                    candidate_hash == hash
+                                        && &ph_array[candidate_index as usize] == key
+                                })
+                                .map(|&(_, index)| index)
+                                .unwrap_or(INDEX_NULL)
+                        }));
+
+                        Ok(Series::from_vec("reference_idxs", out))
+                    })
+                    .unwrap()
+            })
+            .collect_into_vec(&mut bounce);
+
+        for batch in bounce.drain(..) {
+            reference_idxs.append(&batch.into_series())?;
+        }
+    }
+    eprintln!("{DONE}");
+
+    eprint!("… writing output\r");
+    ParquetWriter::new(File::create("narinfo-references.parquet")?).finish(&mut df! {
+        "reference_idxs" => reference_idxs,
+    }?)?;
+    eprintln!("{DONE}");
+
+    Ok(())
+}
diff --git a/tvix/tools/weave/src/bytes.rs b/tvix/tools/weave/src/bytes.rs
new file mode 100644
index 0000000000..c6dc2ebb44
--- /dev/null
+++ b/tvix/tools/weave/src/bytes.rs
@@ -0,0 +1,27 @@
+use owning_ref::{OwningRef, StableAddress};
+use polars::export::arrow::buffer::Buffer;
+use std::ops::Deref;
+
+/// An shared `[[u8; N]]` backed by a Polars [Buffer].
+pub type FixedBytes<const N: usize> = OwningRef<Bytes, [[u8; N]]>;
+
+/// Wrapper struct to make [Buffer] implement [StableAddress].
+/// TODO(edef): upstream the `impl`
+pub struct Bytes(pub Buffer<u8>);
+
+/// SAFETY: [Buffer] is always an Arc+Vec indirection.
+unsafe impl StableAddress for Bytes {}
+
+impl Bytes {
+    pub fn map<U: ?Sized>(self, f: impl FnOnce(&[u8]) -> &U) -> OwningRef<Self, U> {
+        OwningRef::new(self).map(f)
+    }
+}
+
+impl Deref for Bytes {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        &*self.0
+    }
+}
diff --git a/tvix/tools/weave/src/lib.rs b/tvix/tools/weave/src/lib.rs
new file mode 100644
index 0000000000..bc2221bf5c
--- /dev/null
+++ b/tvix/tools/weave/src/lib.rs
@@ -0,0 +1,106 @@
+use anyhow::Result;
+use rayon::prelude::*;
+use std::{fs::File, ops::Range, slice};
+
+use polars::{
+    datatypes::BinaryChunked,
+    export::arrow::array::BinaryArray,
+    prelude::{ParquetReader, SerReader},
+};
+
+pub use crate::bytes::*;
+mod bytes;
+
+pub const INDEX_NULL: u32 = !0;
+pub const DONE: &str = "\u{2714}";
+
+/// A terrific hash function, turning 20 bytes of cryptographic hash
+/// into 8 bytes of cryptographic hash.
+pub fn hash64(h: &[u8; 20]) -> u64 {
+    let mut buf = [0; 8];
+    buf.copy_from_slice(&h[..8]);
+    u64::from_ne_bytes(buf)
+}
+
+/// Read a dense `store_path_hash` array from `narinfo.parquet`,
+/// returning it as an owned [FixedBytes].
+pub fn load_ph_array() -> Result<FixedBytes<20>> {
+    eprint!("… load store_path_hash\r");
+    // TODO(edef): this could use a further pushdown, since polars is more hindrance than help here
+    // We know this has to fit in memory (we can't mmap it without further encoding constraints),
+    // and we want a single `Vec<[u8; 20]>` of the data.
+    let ph_array = into_fixed_binary_rechunk::<20>(
+        ParquetReader::new(File::open("narinfo.parquet").unwrap())
+            .with_columns(Some(vec!["store_path_hash".into()]))
+            .set_rechunk(true)
+            .finish()?
+            .column("store_path_hash")?
+            .binary()?,
+    );
+
+    u32::try_from(ph_array.len()).expect("dataset exceeds 2^32");
+    eprintln!("{DONE}");
+
+    Ok(ph_array)
+}
+
+/// Iterator over `&[[u8; N]]` from a dense [BinaryChunked].
+pub fn as_fixed_binary<const N: usize>(
+    chunked: &BinaryChunked,
+) -> impl Iterator<Item = &[[u8; N]]> + DoubleEndedIterator {
+    chunked.downcast_iter().map(|array| {
+        let range = assert_fixed_dense::<N>(array);
+        exact_chunks(&array.values()[range]).unwrap()
+    })
+}
+
+/// Convert a dense [BinaryChunked] into a single chunk as [FixedBytes],
+/// without taking a reference to the offsets array and validity bitmap.
+fn into_fixed_binary_rechunk<const N: usize>(chunked: &BinaryChunked) -> FixedBytes<N> {
+    let chunked = chunked.rechunk();
+    let mut iter = chunked.downcast_iter();
+    let array = iter.next().unwrap();
+
+    let range = assert_fixed_dense::<N>(array);
+    Bytes(array.values().clone().sliced(range.start, range.len()))
+        .map(|buf| exact_chunks(buf).unwrap())
+}
+
+/// Ensures that the supplied Arrow array consists of densely packed bytestrings of length `N`.
+/// In other words, ensure that it is free of nulls, and that the offsets have a fixed stride of `N`.
+#[must_use = "only the range returned is guaranteed to be conformant"]
+fn assert_fixed_dense<const N: usize>(array: &BinaryArray<i64>) -> Range<usize> {
+    let null_count = array.validity().map_or(0, |bits| bits.unset_bits());
+    if null_count > 0 {
+        panic!("null values present");
+    }
+
+    let offsets = array.offsets();
+    let length_check = offsets
+        .as_slice()
+        .par_windows(2)
+        .all(|w| (w[1] - w[0]) == N as i64);
+
+    if !length_check {
+        panic!("lengths are inconsistent");
+    }
+
+    (*offsets.first() as usize)..(*offsets.last() as usize)
+}
+
+fn exact_chunks<const K: usize>(buf: &[u8]) -> Option<&[[u8; K]]> {
+    // SAFETY: We ensure that `buf.len()` is a multiple of K, and there are no alignment requirements.
+    unsafe {
+        let ptr = buf.as_ptr();
+        let len = buf.len();
+
+        if len % K != 0 {
+            return None;
+        }
+
+        let ptr = ptr as *mut [u8; K];
+        let len = len / K;
+
+        Some(slice::from_raw_parts(ptr, len))
+    }
+}
diff --git a/tvix/tools/weave/src/main.rs b/tvix/tools/weave/src/main.rs
new file mode 100644
index 0000000000..e8a1990a0d
--- /dev/null
+++ b/tvix/tools/weave/src/main.rs
@@ -0,0 +1,216 @@
+//! Weave resolves a list of roots from `nixpkgs.roots` against `narinfo.parquet`,
+//! and then uses the reference graph from the accompanying `narinfo-references.parquet`
+//! produced by `swizzle` to collect the closure of the roots.
+//!
+//! They are written to `live_idxs.parquet`, which only has one column, representing
+//! the row numbers in `narinfo.parquet` corresponding to live paths.
+
+use anyhow::Result;
+use hashbrown::{hash_table, HashTable};
+use nix_compat::nixbase32;
+use rayon::prelude::*;
+use std::{
+    collections::{BTreeMap, HashSet},
+    fs::{self, File},
+    ops::Index,
+    sync::atomic::{AtomicU32, Ordering},
+};
+
+use polars::{
+    datatypes::StaticArray,
+    export::arrow::{array::UInt32Array, offset::OffsetsBuffer},
+    prelude::*,
+};
+
+use weave::{hash64, DONE, INDEX_NULL};
+
+fn main() -> Result<()> {
+    eprint!("… parse roots\r");
+    let roots: PathSet32 = {
+        let mut roots = Vec::new();
+        fs::read("nixpkgs.roots")?
+            .par_chunks_exact(32 + 1)
+            .map(|e| nixbase32::decode_fixed::<20>(&e[0..32]).unwrap())
+            .collect_into_vec(&mut roots);
+
+        roots.iter().collect()
+    };
+    eprintln!("{DONE}");
+
+    {
+        let ph_array = weave::load_ph_array()?;
+
+        eprint!("… resolve roots\r");
+        ph_array.par_iter().enumerate().for_each(|(idx, h)| {
+            if let Some(idx_slot) = roots.find(h) {
+                idx_slot
+                    .compare_exchange(INDEX_NULL, idx as u32, Ordering::SeqCst, Ordering::SeqCst)
+                    .expect("duplicate entry");
+            }
+        });
+        eprintln!("{DONE}");
+    }
+
+    let mut todo = HashSet::with_capacity(roots.len());
+    {
+        let mut unknown_roots = 0usize;
+        for (_, idx) in roots.table {
+            let idx = idx.into_inner();
+            if idx == INDEX_NULL {
+                unknown_roots += 1;
+                continue;
+            }
+            todo.insert(idx);
+        }
+        println!("skipping {unknown_roots} unknown roots");
+    }
+
+    eprint!("… load reference_idxs\r");
+    let ri_array = ParquetReader::new(File::open("narinfo-references.parquet")?)
+        .finish()?
+        .column("reference_idxs")?
+        .list()?
+        .clone();
+
+    let ri_array = {
+        ChunkedList::new(ri_array.downcast_iter().map(|chunk| {
+            (
+                chunk.offsets(),
+                chunk
+                    .values()
+                    .as_any()
+                    .downcast_ref::<UInt32Array>()
+                    .unwrap()
+                    .as_slice()
+                    .unwrap(),
+            )
+        }))
+    };
+    eprintln!("{DONE}");
+
+    let mut seen = todo.clone();
+    while !todo.is_empty() {
+        println!("todo: {} seen: {}", todo.len(), seen.len());
+
+        todo = todo
+            .par_iter()
+            .flat_map(|&parent| {
+                if parent == INDEX_NULL {
+                    return vec![];
+                }
+
+                ri_array[parent as usize]
+                    .iter()
+                    .cloned()
+                    .filter(|child| !seen.contains(child))
+                    .collect::<Vec<u32>>()
+            })
+            .collect();
+
+        for &index in &todo {
+            seen.insert(index);
+        }
+    }
+
+    println!("done: {} paths", seen.len());
+
+    if seen.remove(&INDEX_NULL) {
+        println!("WARNING: missing edges");
+    }
+
+    eprint!("… gathering live set\r");
+    let mut seen: Vec<u32> = seen.into_iter().collect();
+    seen.par_sort();
+    eprintln!("{DONE}");
+
+    eprint!("… writing output\r");
+    ParquetWriter::new(File::create("live_idxs.parquet")?).finish(&mut df! {
+        "live_idx" => seen,
+    }?)?;
+    eprintln!("{DONE}");
+
+    Ok(())
+}
+
+struct PathSet32 {
+    table: HashTable<([u8; 20], AtomicU32)>,
+}
+
+impl PathSet32 {
+    fn with_capacity(capacity: usize) -> Self {
+        Self {
+            table: HashTable::with_capacity(capacity),
+        }
+    }
+
+    fn insert(&mut self, value: &[u8; 20]) -> bool {
+        let hash = hash64(value);
+
+        match self
+            .table
+            .entry(hash, |(x, _)| x == value, |(x, _)| hash64(x))
+        {
+            hash_table::Entry::Occupied(_) => false,
+            hash_table::Entry::Vacant(entry) => {
+                entry.insert((*value, AtomicU32::new(INDEX_NULL)));
+                true
+            }
+        }
+    }
+
+    fn find(&self, value: &[u8; 20]) -> Option<&AtomicU32> {
+        let hash = hash64(value);
+        self.table
+            .find(hash, |(x, _)| x == value)
+            .as_ref()
+            .map(|(_, x)| x)
+    }
+
+    fn len(&self) -> usize {
+        self.table.len()
+    }
+}
+
+impl<'a> FromIterator<&'a [u8; 20]> for PathSet32 {
+    fn from_iter<T: IntoIterator<Item = &'a [u8; 20]>>(iter: T) -> Self {
+        let iter = iter.into_iter();
+        let mut this = Self::with_capacity(iter.size_hint().0);
+
+        for item in iter {
+            this.insert(item);
+        }
+
+        this
+    }
+}
+
+struct ChunkedList<'a, T> {
+    by_offset: BTreeMap<usize, (&'a OffsetsBuffer<i64>, &'a [T])>,
+}
+
+impl<'a, T> ChunkedList<'a, T> {
+    fn new(chunks: impl IntoIterator<Item = (&'a OffsetsBuffer<i64>, &'a [T])>) -> Self {
+        let mut next_offset = 0usize;
+        ChunkedList {
+            by_offset: chunks
+                .into_iter()
+                .map(|(offsets, values)| {
+                    let offset = next_offset;
+                    next_offset = next_offset.checked_add(offsets.len_proxy()).unwrap();
+
+                    (offset, (offsets, values))
+                })
+                .collect(),
+        }
+    }
+}
+
+impl<'a, T> Index<usize> for ChunkedList<'a, T> {
+    type Output = [T];
+
+    fn index(&self, index: usize) -> &Self::Output {
+        let (&base, &(offsets, values)) = self.by_offset.range(..=index).next_back().unwrap();
+        let (start, end) = offsets.start_end(index - base);
+        &values[start..end]
+    }
+}
diff --git a/tvix/verify-lang-tests/default.nix b/tvix/verify-lang-tests/default.nix
new file mode 100644
index 0000000000..772c1c5320
--- /dev/null
+++ b/tvix/verify-lang-tests/default.nix
@@ -0,0 +1,226 @@
+# SPDX-License-Identifier: LGPL-2.1-only
+# SPDX-FileCopyrightText: Β© 2022 The TVL Contributors
+# SPDX-FileCopyrightText: Β© 2004-2022 The Nix Contributors
+#
+# Execute language tests found in tvix_tests and nix_tests
+# using the C++ Nix implementation. Based on NixOS/nix:tests/lang.sh.
+{ depot, pkgs, lib, ... }:
+
+let
+  testRoot = ../eval/src/tests;
+
+  inherit (pkgs.buildPackages) nix nix_latest;
+
+  parseTest = dir: baseName:
+    let
+      tokens = builtins.match "(eval|parse)-(okay|fail).+\\.nix" baseName;
+    in
+    if tokens == null
+    then null
+    else {
+      type = builtins.elemAt tokens 0;
+      expectedSuccess = (builtins.elemAt tokens 1) == "okay";
+      fileName = "${dir}/${baseName}";
+    };
+
+  allLangTests =
+    lib.concatMap
+      (
+        dir:
+        lib.pipe
+          (builtins.readDir (testRoot + "/${dir}"))
+          [
+            builtins.attrNames
+            (builtins.map (parseTest dir))
+            (builtins.filter (t: t != null))
+          ]
+      ) [ "nix_tests" "nix_tests/notyetpassing" "tvix_tests" "tvix_tests/notyetpassing" ];
+
+  skippedLangTests = {
+    # TODO(sterni): set up NIX_PATH in sandbox
+    "eval-okay-search-path.nix" = true;
+    # Floating point precision differs between tvix and Nix
+    "eval-okay-fromjson.nix" = true;
+    # C++ Nix can't TCO
+    "eval-okay-tail-call-1.nix" = true;
+    # Ordering change after 2.3
+    "eval-okay-xml.nix" = [ nix ];
+    # Missing builtins in Nix 2.3
+    "eval-okay-ceil.nix" = [ nix ];
+    "eval-okay-floor-ceil.nix" = [ nix ];
+    "eval-okay-floor.nix" = [ nix ];
+    "eval-okay-groupBy.nix" = [ nix ];
+    "eval-okay-zipAttrsWith.nix" = [ nix ];
+    "eval-okay-builtins-group-by-propagate-catchable.nix" = [ nix ];
+    # Comparable lists are not in Nix 2.3
+    "eval-okay-sort.nix" = [ nix ];
+    "eval-okay-compare-lists.nix" = [ nix ];
+    "eval-okay-value-pointer-compare.nix" = [ nix ];
+    "eval-okay-builtins-genericClosure-pointer-equality.nix" = [ nix ];
+    "eval-okay-list-comparison.nix" = [ nix ];
+    # getAttrPos gains support for functionArgs-returned sets after 2.3
+    "eval-okay-getattrpos-functionargs.nix" = [ nix ];
+    # groupBy appeared (long) after 2.3
+    "eval-okay-builtins-groupby-thunk.nix" = [ nix ];
+    # import is no longer considered a curried primop in Nix > 2.3
+    "eval-okay-import-display.nix" = [ nix ];
+    # Cycle detection and formatting changed sometime after Nix 2.3
+    "eval-okay-cycle-display-cpp-nix-2.13.nix" = [ nix ];
+    # builtins.replaceStrings becomes lazier in Nix 2.16
+    "eval-okay-replacestrings.nix" = [ nix ];
+    # builtins.readFileType is added in Nix 2.15
+    "eval-okay-readFileType.nix" = [ nix ];
+    # builtins.fromTOML gains support for timestamps in Nix 2.16
+    "eval-okay-fromTOML-timestamps.nix" = [ nix ];
+    # identifier formatting changed in Nix 2.17 due to cppnix commit
+    # b72bc4a972fe568744d98b89d63adcd504cb586c
+    "eval-okay-identifier-formatting.nix" = [ nix ];
+
+    # Differing strictness in the function argument for some builtins in Nix 2.18
+    # https://github.com/NixOS/nix/issues/9779
+    "eval-okay-builtins-map-propagate-catchable.nix" = [ nix_latest ];
+    "eval-okay-builtins-gen-list-propagate-catchable.nix" = [ nix_latest ];
+    "eval-okay-builtins-replace-strings-propagate-catchable.nix" =
+      [ nix_latest ];
+    "eval-okay-builtins-map-function-strictness.nix" = [ nix_latest ];
+    "eval-okay-builtins-genList-function-strictness.nix" = [ nix_latest ];
+
+    # TODO(sterni): support diffing working directory and home relative paths
+    # like C++ Nix test suite (using string replacement).
+    "eval-okay-path-antiquotation.nix" = true;
+  };
+
+  runCppNixLangTests = cpp-nix:
+    let
+      testCommand = { fileName, type, expectedSuccess, ... }:
+        let
+          testBase = lib.removeSuffix ".nix" fileName;
+          expFile =
+            let
+              possibleFiles =
+                builtins.filter
+                  (path: builtins.pathExists (testRoot + "/${path}"))
+                  (builtins.map
+                    (ext: "${testBase}.${ext}")
+                    [ "exp" "exp.xml" ]);
+            in
+            if possibleFiles == [ ] then null else builtins.head possibleFiles;
+          outFile = "${testBase}.out";
+
+          # Skip if skippedLangTests prescribes it (possibly just for the current nix)
+          # or if we are missing an exp file for an eval-okay test.
+          skip =
+            let
+              doSkip = skippedLangTests.${builtins.baseNameOf fileName} or false;
+            in
+            if type == "eval" && expectedSuccess && (expFile == null) then true
+            else if builtins.isBool doSkip then doSkip
+            else builtins.any (drv: cpp-nix == drv) doSkip;
+
+          flagsFile = "${testBase}.flags";
+
+          instantiateFlags =
+            lib.escapeShellArgs
+              (
+                [ "--${type}" fileName ]
+                ++ lib.optionals (type == "eval") [ "--strict" ]
+                ++ lib.optionals (expFile != null && lib.hasSuffix "xml" expFile)
+                  [
+                    "--no-location"
+                    "--xml"
+                  ]
+              )
+            + lib.optionalString (builtins.pathExists (testRoot + "/${flagsFile}"))
+              " $(cat '${flagsFile}')";
+        in
+
+        if skip
+        then "echo \"SKIP ${type} ${fileName}\"\n"
+        else ''
+          thisTestPassed=true
+
+          echo "RUN  ${type} ${fileName} ${
+            lib.optionalString (!expectedSuccess) "(expecting failure)"
+          }"
+
+          if ! expect ${if expectedSuccess then "0" else "1"} \
+                 nix-instantiate ${instantiateFlags} \
+                 ${if expectedSuccess then "1" else "2"}> \
+                 ${if expFile != null then outFile else "/dev/null"};
+          then
+            echo -n "FAIL"
+            thisTestPassed=false
+          fi
+        '' + lib.optionalString (expFile != null) ''
+          if ! diff --color=always -u '${outFile}' '${expFile}'; then
+            thisTestPassed=false
+          fi
+        '' + ''
+          if $thisTestPassed; then
+            echo -n "PASS"
+          else
+            echo -n "FAIL"
+            passed=false
+          fi
+
+          echo " ${type} ${fileName}"
+
+          unset thisTestPassed
+        '';
+    in
+
+    pkgs.stdenv.mkDerivation {
+      name = "cpp-${cpp-nix.name}-run-lang-tests";
+
+      nativeBuildInputs = [ cpp-nix ];
+
+      # Obtain tests via the unpackPhase
+      src = testRoot;
+      dontConfigure = true;
+
+      # Environment expected by the test suite
+      TEST_VAR = "foo";
+
+      buildPhase = ''
+        # Make nix-instantiate happy in the sandbox
+        export NIX_STORE_DIR="$(realpath "$(mktemp -d store.XXXXXXXXXX)")"
+        export NIX_STATE_DIR="$(realpath "$(mktemp -d state.XXXXXXXXXX)")"
+
+        # Helper function to check expected exit code
+        expect() {
+          local expected res
+          expected="$1"
+          shift
+          set +e
+          "$@"
+          res="$?"
+          set -e
+          [[ $res -eq $expected ]]
+        }
+
+        # Track test results so far
+        passed=true
+
+        source "$testCommandsPath"
+      '';
+
+      # Actually runs into the argv limit
+      passAsFile = [ "testCommands" ];
+      testCommands = lib.concatMapStrings testCommand allLangTests;
+
+      installPhase = ''
+        if $passed; then
+          touch $out
+        else
+          echo "Some test(s) failed!"
+          exit 1
+        fi
+      '';
+    };
+
+in
+
+depot.nix.readTree.drvTargets {
+  "nix-2.3" = runCppNixLangTests nix;
+  "nix-${lib.versions.majorMinor nix_latest.version}" = runCppNixLangTests nix_latest;
+}
diff --git a/tvix/website/default.nix b/tvix/website/default.nix
new file mode 100644
index 0000000000..a2fc247e4a
--- /dev/null
+++ b/tvix/website/default.nix
@@ -0,0 +1,46 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  description = "Rust implementation of the purely-functional Nix package manager";
+
+  # https://developers.google.com/search/docs/advanced/structured-data/
+  # https://schema.org/SoftwareApplication
+  structuredData = {
+    "@context" = "https://schema.org";
+    "@type" = "SoftwareApplication";
+    name = "Tvix";
+    url = "https://tvix.dev";
+    abstract = description;
+    applicationCategory = "DeveloperApplication";
+    contributor = "https://tvl.fyi";
+    image = "https://tvix.dev/logo.webp";
+  };
+
+  # All Tvix-related blog posts from the main TVL website
+  tvixPosts = builtins.filter
+    (post: !(post.draft or false) && (lib.hasInfix "Tvix" post.title))
+    depot.web.tvl.blog.posts;
+
+  postListEntries = map (p: "* [${p.title}](https://tvl.fyi/blog/${p.key})") tvixPosts;
+
+  landing = depot.web.tvl.template {
+    title = "Tvix - A new implementation of Nix";
+    content = ''
+      ${builtins.readFile ./landing-en.md}
+      ${builtins.concatStringsSep "\n" postListEntries}
+    '';
+
+    extraHead = ''
+      <meta name="description" content="${description}">
+      <script type="application/ld+json">
+        ${builtins.toJSON structuredData}
+      </script>
+    '';
+  };
+
+in
+pkgs.runCommand "tvix-website" { } ''
+  mkdir $out
+  cp ${landing} $out/index.html
+  cp ${depot.tvix.logo}/logo.webp $out/
+''
diff --git a/tvix/website/landing-en.md b/tvix/website/landing-en.md
new file mode 100644
index 0000000000..61a011dee9
--- /dev/null
+++ b/tvix/website/landing-en.md
@@ -0,0 +1,42 @@
+<img class="tvl-logo" src="./logo.webp"
+     alt="A candy bar in different shades of blue that says 'Tvix by TVL' on it">
+
+------------------
+
+Tvix is a new implementation of Nix, a purely-functional package manager. It
+aims to have a modular implementation, in which different components can be
+reused or replaced based on the use-case.
+
+Tvix is developed as a GPLv3-licensed open-source project by
+[TVL][], with source code available in the [TVL monorepo][].
+
+There are several projects within Tvix, such as:
+
+* `//tvix/castore` - subtree storage/transfer in a content-addressed fashion
+* `//tvix/cli` - preliminary REPL & CLI implementation for Tvix
+* `//tvix/eval` - an implementation of the Nix programming language
+* `//tvix/nar-bridge` - a HTTP webserver providing a Nix HTTP Binary Cache interface in front of a tvix-store
+* `//tvix/nix-compat` - a Rust library for compatibility with C++ Nix, features like encodings and hashing schemes and formats
+* `//tvix/serde` - a Rust library for using the Nix language for app configuration
+* `//tvix/store` - a "filesystem" linking Nix store paths and metadata with the content-addressed layer
+* ... and a handful others!
+
+The language evaluator can be toyed with in [Tvixbolt][], and you can check out
+the [Tvix README][] ([GitHub mirror][gh]) for additional information on the
+project and development workflows.
+
+Developer documentation for some parts of Tvix is [available online][docs].
+
+Benchmarks are run nightly on new commits by [windtunnel][wt].
+
+[TVL]: https://tvl.fyi
+[TVL monorepo]: https://cs.tvl.fyi/depot/-/tree/tvix
+[Tvixbolt]: https://bolt.tvix.dev
+[Tvix README]: https://code.tvl.fyi/about/tvix
+[gh]: https://github.com/tvlfyi/tvix/
+[docs]: https://docs.tvix.dev
+[wt]: https://staging.windtunnel.ci/tvl/tvix
+
+-------------------
+
+Check out the latest Tvix-related blog posts from TVL's website:
diff --git a/users/Profpatsch/.envrc b/users/Profpatsch/.envrc
new file mode 100644
index 0000000000..c91f923756
--- /dev/null
+++ b/users/Profpatsch/.envrc
@@ -0,0 +1,5 @@
+if pass apps/declib/mastodon_access_token >/dev/null; then
+    export DECLIB_MASTODON_ACCESS_TOKEN=$(pass apps/declib/mastodon_access_token)
+fi
+
+eval "$(lorri direnv)"
diff --git a/users/Profpatsch/.gitignore b/users/Profpatsch/.gitignore
new file mode 100644
index 0000000000..c33954f53a
--- /dev/null
+++ b/users/Profpatsch/.gitignore
@@ -0,0 +1 @@
+dist-newstyle/
diff --git a/users/Profpatsch/.hlint.yaml b/users/Profpatsch/.hlint.yaml
new file mode 100644
index 0000000000..f00f78c525
--- /dev/null
+++ b/users/Profpatsch/.hlint.yaml
@@ -0,0 +1,357 @@
+# HLint configuration file
+# https://github.com/ndmitchell/hlint
+# Run `hlint --default` to see the example configuration file.
+##########################
+
+# WARNING: These need to be synced with the default-extensions field
+# in the cabal file.
+- arguments: [-XGHC2021, -XOverloadedRecordDot]
+
+# Ignore some builtin hints
+
+# often functions are more readable with explicit arguments
+- ignore: { name: Eta reduce }
+
+# these redundancy warnings are just completely irrelevant
+- ignore: { name: Redundant bracket }
+- ignore: { name: Move brackets to avoid $ }
+- ignore: { name: Redundant $ }
+- ignore: { name: Redundant do }
+- ignore: { name: Redundant multi-way if }
+
+# allow case-matching on bool, because why not
+- ignore: { name: Use if }
+
+# hlint cannot distinguish actual newtypes from data types
+# that accidentally have only one field
+# (but might have more in the future).
+# Since it’s a mostly irrelevant runtime optimization, we don’t care.
+- ignore: { name: Use newtype instead of data }
+
+# these lead to harder-to-read/more implicit code
+- ignore: { name: Use fmap }
+- ignore: { name: Use <$> }
+- ignore: { name: Use tuple-section }
+- ignore: { name: Use forM_ }
+- ignore: { name: Functor law }
+# fst and snd are usually a code smell and should be explicit matches, _naming the ignored side.
+- ignore: { name: Use fst }
+- ignore: { name: Use snd }
+- ignore: { name: Use fromMaybe }
+- ignore: { name: Use const }
+- ignore: { name: Replace case with maybe }
+- ignore: { name: Replace case with fromMaybe }
+- ignore: { name: Avoid lambda }
+- ignore: { name: Avoid lambda using `infix` }
+- ignore: { name: Use curry }
+- ignore: { name: Use uncurry }
+- ignore: { name: Use first }
+- ignore: { name: Redundant first }
+- ignore: { name: Use second }
+- ignore: { name: Use bimap }
+# just use `not x`
+- ignore: { name: Use unless }
+- ignore: { name: Redundant <&> }
+
+# list comprehensions are a seldomly used part of the Haskell language
+# and they introduce syntactic overhead that is usually not worth the conciseness
+- ignore: { name: Use list comprehension }
+
+# Seems to be buggy in cases
+- ignore: { name: Use section }
+
+# multiple maps in a row are usually used for clarity,
+# and the compiler will optimize them away, thank you very much.
+- ignore: { name: Use map once }
+- ignore: { name: Fuse foldr/map }
+- ignore: { name: Fuse traverse/map }
+- ignore: { name: Fuse traverse_/map }
+- ignore: { name: Fuse traverse/<$> }
+
+# this is silly, why would I use a special function if I can just (heh) `== Nothing`
+- ignore: { name: Use isNothing }
+
+# The duplication heuristic is not very smart
+# and more annoying than helpful.
+# see https://github.com/ndmitchell/hlint/issues/1009
+- ignore: { name: Reduce duplication }
+
+# Stops the pattern match trick
+- ignore: { name: Use record patterns }
+- ignore: { name: Use null }
+- ignore: { name: Use uncurry }
+
+# we don’t want void, see below
+- ignore: { name: Use void }
+
+- functions:
+    # disallow Enum instance functions, they are partial
+    - name: Prelude.succ
+      within: [Relude.Extra.Enum]
+      message: "Dangerous, will fail for highest element"
+    - name: Prelude.pred
+      within: [Relude.Extra.Enum]
+      message: "Dangerous, will fail for lowest element"
+    - name: Prelude.toEnum
+      within: []
+      message: "Extremely partial"
+    - name: Prelude.fromEnum
+      within: []
+      message: "Dangerous for most uses"
+    - name: Prelude.enumFrom
+      within: []
+    - name: Prelude.enumFromThen
+      within: []
+    - name: Prelude.enumFromThenTo
+      within: []
+    - name: Prelude.oundedEnumFrom
+      within: []
+    - name: Prelude.boundedEnumFromThen
+      within: []
+
+    - name: Text.Read.readMaybe
+      within:
+        # The BSON ObjectId depends on Read for parsing
+        - Milkmap.Milkmap
+        - Milkmap.FieldData.Value
+      message: "`readMaybe` is probably not what you want for parsing values, please use the `FieldParser` module."
+
+    # `void` discards its argument and is polymorphic,
+    # thus making it brittle in the face of code changes.
+    # (see https://tech.freckle.com/2020/09/23/void-is-a-smell/)
+    # Use an explicit `_ <- …` instead.
+    - name: Data.Functor.void
+      within: []
+      message: "`void` leads to bugs. Use an explicit `_ <- …` instead"
+
+    - name: Data.Foldable.length
+      within: ["MyPrelude"]
+      message: "`Data.Foldable.length` is dangerous to use, because it also works on types you wouldn’t expect, like `length (3,4) == 1` and `length (Just 2) == 1`. Use the `length` function for your specific type instead, for example `List.length` or `Map.length`."
+
+    - name: Prelude.length
+      within: ["MyPrelude"]
+      message: "`Prelude.length` is dangerous to use, because it also works on types you wouldn’t expect, like `length (3,4) == 1` and `length (Just 2) == 1`. Use the `length` function for your specific type instead, for example `List.length` or `Map.length`."
+
+    # Using an explicit lambda with its argument β€œunderscored”
+    # is more clear in every case.
+    # e.g. `const True` => `\_request -> True`
+    # shows the reader that the ignored argument was a request.
+    - name: Prelude.const
+      within: []
+      message: "Replace `const` with an explicit lambda with type annotation for code clarity and type safety, e.g.: `const True` => `\\(_ :: Request) -> True`. If you really don’t want to spell out the type (which might lead to bugs!), you can also use something like `\_request -> True`."
+
+    - name: Data.List.nub
+      within: []
+      message: "O(nΒ²), use `Data.Containers.ListUtils.nubOrd"
+
+    - name: Prelude.maximum
+      within: []
+      message: "`maximum` crashes on empty list; use non-empty lists and `maximum1`"
+
+    - name: Data.List.maximum
+      within: []
+      message: "`maximum` crashes on empty list; use non-empty lists and `maximum1`"
+
+    - name: Prelude.minimum
+      within: []
+      message: "`minimum` crashes on empty list; use non-empty lists and `minimum1`"
+
+    - name: Data.List.minimum
+      within: []
+      message: "`minimum` crashes on empty list; use non-empty lists and `minimum1`"
+
+    - name: Data.Foldable.maximum
+      within: []
+      message: "`maximum` crashes on empty foldable stucture; use Foldable1 and `maximum1`."
+
+    - name: Data.Foldable.minimum
+      within: []
+      message: "`minimum` crashes on empty foldable stucture; use Foldable1 and `minimum1`."
+
+    # Using prelude functions instead of stdlib functions
+
+    - name: "Data.Text.Encoding.encodeUtf8"
+      within: ["MyPrelude"]
+      message: "Use `textToBytesUtf8`"
+
+    - name: "Data.Text.Lazy.Encoding.encodeUtf8"
+      within: ["MyPrelude"]
+      message: "Use `textToBytesUtf8Lazy`"
+
+    - name: "Data.Text.Encoding.decodeUtf8'"
+      within: ["MyPrelude"]
+      message: "Use `bytesToTextUtf8`"
+
+    - name: "Data.Text.Encoding.Lazy.decodeUtf8'"
+      within: ["MyPrelude"]
+      message: "Use `bytesToTextUtf8Lazy`"
+
+    - name: "Data.Text.Encoding.decodeUtf8"
+      within: ["MyPrelude"]
+      message: "Either check for errors with `bytesToTextUtf8`, decode leniently with unicode replacement characters with `bytesToTextUtf8Lenient` or use the crashing version `bytesToTextUtf8Unsafe` (discouraged)."
+
+    - name: "Data.Text.Encoding.Lazy.decodeUtf8"
+      within: ["MyPrelude"]
+      message: "Either check for errors with `bytesToTextUtf8Lazy`, decode leniently with unicode replacement characters with `bytesToTextUtf8LenientLazy` or use the crashing version `bytesToTextUtf8UnsafeLazy` (discouraged)."
+
+    - name: "Data.Text.Lazy.toStrict"
+      within: ["MyPrelude"]
+      message: "Use `toStrict`"
+
+    - name: "Data.Text.Lazy.fromStrict"
+      within: ["MyPrelude"]
+      message: "Use `toLazy`"
+
+    - name: "Data.ByteString.Lazy.toStrict"
+      within: ["MyPrelude"]
+      message: "Use `toStrictBytes`"
+
+    - name: "Data.ByteString.Lazy.fromStrict"
+      within: ["MyPrelude"]
+      message: "Use `toLazyBytes`"
+
+    - name: "Data.Text.unpack"
+      within: ["MyPrelude"]
+      message: "Use `textToString`"
+
+    - name: "Data.Text.pack"
+      within: ["MyPrelude"]
+      message: "Use `stringToText`"
+
+    - name: "Data.Maybe.listToMaybe"
+      within: []
+      message: |
+        `listToMaybe`` throws away everything but the first element of a list (it is essentially `safeHead`).
+        If that is what you want, please use a pattern match like
+
+        ```
+        case xs of
+          [] -> …
+          (x:_) -> …
+        ```
+
+    - name: "Data.List.head"
+      within: []
+      message: |
+        `List.head` fails on an empty list. I didn’t think I have to say this, but please use a pattern match on the list, like:
+
+        ```
+        case xs of
+          [] -> … error handling …
+          (x:_) -> …
+        ```
+
+        Also think about why the rest of the list should be ignored.
+
+    - name: "Prelude.head"
+      within: []
+      message: |
+        `List.head` fails on an empty list. I didn’t think I have to say this, but please use a pattern match on the list, like.
+
+        ```
+        case xs of
+          [] -> … error handling …
+          (x:_) -> …
+        ```
+
+        Also think about why the rest of the list should be ignored.
+
+    - name: "Data.Maybe.fromJust"
+      within: []
+      message: |
+        `Maybe.fromJust` is obviously partial. Please use a pattern match.
+
+        In case you actually want to throw an error on an empty list,
+        please add an error message, like so:
+
+        ```
+        myMaybe & annotate "my error message" & unwrapError
+        ```
+
+        If you are in `IO`, use `unwrapIOError` instead,
+        or throw a monad-specific error.
+
+    - name: "Data.Either.fromLeft"
+      within: []
+      message: |
+        `Either.fromLeft` is obviously partial. Please use a pattern match.
+
+    - name: "Data.Either.fromRight"
+      within: []
+      message: |
+        `Either.fromRight` is obviously partial. Please use a pattern match.
+
+# Make restricted functions into an error if found
+- error: { name: "Avoid restricted function, see comment in .hlint.yaml" }
+
+# Some functions that have (more modern) aliases.
+# They are not dangerous per se,
+# but we want to make it easier to read our code so we should
+# make sure we don’t use too many things that are renames.
+
+- hint:
+    lhs: "undefined"
+    rhs: "todo"
+    note: "`undefined` is a silent error, `todo` will display a warning as long as it exists in the code."
+
+- hint:
+    lhs: "return"
+    rhs: "pure"
+    note: "Use `pure` from `Applicative` instead, it’s the exact same function."
+
+- hint:
+    lhs: "mapM"
+    rhs: "traverse"
+    note: "Use `traverse` from `Traversable` instead. It’s the exact same function."
+
+- hint:
+    lhs: "mapM_"
+    rhs: "traverse_"
+    note: "Use `traverse_` from `Traversable` instead. It’s the exact same function."
+
+- hint:
+    lhs: "forM"
+    rhs: "for"
+    note: "Use `for` from `Traversable` instead. It’s the exact same function."
+
+- hint:
+    lhs: "forM_"
+    rhs: "for_"
+    note: "Use `for_` from `Traversable` instead. It’s the exact same function."
+
+- hint:
+    lhs: "stringToText (show x)"
+    rhs: "showToText x"
+
+- hint:
+    lhs: "Data.Set.toList (Data.Set.fromList x)"
+    rhs: "List.nubOrd x"
+    note: "`nubOrd` removes duplicate elements from a list."
+
+- modules:
+    # Disallowed Modules
+    - name: Data.Map
+      within: []
+      message: "Lazy maps leak space, use `import Data.Map.Strict as Map` instead"
+    - name: Control.Monad.Writer
+      within: []
+      message: "Lazy writers leak space, use `Control.Monad.Trans.Writer.CPS` instead"
+    - name: Control.Monad.Trans.Writer.Lazy
+      within: []
+      message: "Lazy writers leak space, use `Control.Monad.Trans.Writer.CPS` instead"
+    - name: Control.Monad.Trans.Writer.Strict
+      within: []
+      message: "Even strict writers leak space, use `Control.Monad.Trans.Writer.CPS` instead"
+
+    # Qualified module imports
+    - { name: Data.Map.Strict, as: Map }
+    - { name: Data.HashMap.Strict, as: HashMap }
+    - { name: Data.Set, as: Set }
+    - { name: Data.ByteString.Char8, as: Char8 }
+    - { name: Data.ByteString.Lazy.Char8, as: Char8.Lazy }
+    - { name: Data.Text, as: Text }
+    - { name: Data.Vector, as: Vector }
+    - { name: Data.Vault.Lazy, as: Vault }
+    - { name: Data.Aeson, as: Json }
+    - { name: Data.Aeson.Types, as: Json }
+    - { name: Data.Aeson.BetterErrors as Json }
diff --git a/users/Profpatsch/.vscode/launch.json b/users/Profpatsch/.vscode/launch.json
new file mode 100644
index 0000000000..baa087d437
--- /dev/null
+++ b/users/Profpatsch/.vscode/launch.json
@@ -0,0 +1,18 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "run declib",
+            "type": "node",
+            "cwd": "${workspaceFolder}/declib",
+            "request": "launch",
+            "runtimeExecutable": "ninja",
+            "runtimeArgs": [
+                "run",
+            ],
+        }
+    ]
+}
diff --git a/users/Profpatsch/.vscode/settings.json b/users/Profpatsch/.vscode/settings.json
new file mode 100644
index 0000000000..7984076c16
--- /dev/null
+++ b/users/Profpatsch/.vscode/settings.json
@@ -0,0 +1,25 @@
+{
+    "sqltools.connections": [
+        {
+            "previewLimit": 50,
+            "driver": "SQLite",
+            "name": "cas-serve",
+            "database": "${workspaceFolder:Profpatsch}/cas-serve/data.sqlite"
+        }
+    ],
+    "sqltools.useNodeRuntime": true,
+    "editor.formatOnSave": true,
+    "[typescript]": {
+        "editor.defaultFormatter": "esbenp.prettier-vscode"
+    },
+    "[javascript]": {
+        "editor.defaultFormatter": "esbenp.prettier-vscode"
+    },
+    "[json]": {
+        "editor.defaultFormatter": "esbenp.prettier-vscode"
+    },
+    "purescript.codegenTargets": [
+        "corefn"
+    ],
+    "purescript.foreignExt": "nix"
+}
diff --git a/users/Profpatsch/OWNERS b/users/Profpatsch/OWNERS
index 5a73d4c3a1..ac23e72256 100644
--- a/users/Profpatsch/OWNERS
+++ b/users/Profpatsch/OWNERS
@@ -1,4 +1,4 @@
-inherited: false
-owners:
-  - Profpatsch
-  - sterni
+set noparent
+
+Profpatsch
+sterni
diff --git a/users/Profpatsch/README.md b/users/Profpatsch/README.md
new file mode 100644
index 0000000000..5bb74cd758
--- /dev/null
+++ b/users/Profpatsch/README.md
@@ -0,0 +1,10 @@
+# Profpatsch’s assemblage of peculiarities and curiosities
+
+Welcome, Welcome.
+
+Welcome to my user dir, where we optimize f*** around, in order to optimize finding out.
+
+![fafo graph](./fafo.jpg)
+
+DISCLAIMER: All of this code is of the β€œdo not try at work” sort, unless noted otherwise.
+You might try at home, however. Get inspired or get grossed out, whichever you like.
diff --git a/users/Profpatsch/alacritty.nix b/users/Profpatsch/alacritty.nix
new file mode 100644
index 0000000000..d3461c4aad
--- /dev/null
+++ b/users/Profpatsch/alacritty.nix
@@ -0,0 +1,27 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.alacritty [ "alacritty" ];
+
+  config =
+    {
+      alacritty-config = { font.size = 18; scrolling.history = 100000; };
+      #  This disables the dpi-sensitive scaling (cause otherwise the font will be humongous on my laptop screen)
+      alacritty-env.WINIT_X11_SCALE_FACTOR = 1;
+    };
+
+
+  config-file = (pkgs.formats.toml { }).generate "alacritty.conf" config.alacritty-config;
+
+  alacritty = depot.nix.writeExecline "alacritty" { } (
+    (lib.concatLists (lib.mapAttrsToList (k: v: [ "export" k (toString v) ]) config.alacritty-env))
+    ++ [
+      bins.alacritty
+      "--config-file"
+      config-file
+      "$@"
+    ]
+  );
+
+in
+alacritty
diff --git a/users/Profpatsch/aliases.nix b/users/Profpatsch/aliases.nix
new file mode 100644
index 0000000000..109de8ce33
--- /dev/null
+++ b/users/Profpatsch/aliases.nix
@@ -0,0 +1,88 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.findutils [ "find" ];
+
+in
+depot.nix.readTree.drvTargets {
+
+  findia = depot.nix.writeExecline "findia"
+    {
+      readNArgs = 1;
+      # TODO: comment out, thanks to sterni blocking the runExecline change
+      # meta.description = ''
+      #   Find case-insensitive anywhere (globbing)
+
+      #   Usage: findia <pattern> <more find(1) arguments>
+      # '';
+    } [
+    bins.find
+    "-iname"
+    "*\${1}*"
+    "$@"
+  ];
+
+  findial = depot.nix.writeExecline "findial"
+    {
+      readNArgs = 1;
+      # TODO: comment out, thanks to sterni blocking the runExecline change
+      # meta.description = ''
+      #   Find case-insensitive anywhere (globbing), follow symlinks";
+
+      #   Usage: findial <pattern> <more find(1) arguments>
+      # '';
+    } [
+    bins.find
+    "-L"
+    "-iname"
+    "*\${1}*"
+    "$@"
+  ];
+
+  findian = depot.nix.writeExecline "findian"
+    {
+      readNArgs = 2;
+      # TODO: comment out, thanks to sterni blocking the runExecline change
+      # meta.description = ''
+      #   Find case-insensitive anywhere (globbing) in directory
+
+      #   Usage: findian <directory> <pattern> <more find(1) arguments>
+      # '';
+    } [
+    bins.find
+    "$1"
+    "-iname"
+    "*\${2}*"
+    "$@"
+  ];
+
+  findiap = depot.nix.writeExecline "findiap"
+    {
+      readNArgs = 2;
+      # TODO: comment out, thanks to sterni blocking the runExecline change
+      # meta.description = ''
+      #   Find case-insensitive anywhere (globbing) in directory, the pattern allows for paths.
+
+      #   Usage: findiap <directory> <pattern> <more find(1) arguments>
+      # '';
+    } [
+    bins.find
+    "$1"
+    "-ipath"
+    "*\${2}*"
+    "$@"
+  ];
+
+  bell = depot.nix.writeExecline "bell" { } [
+    "if"
+    [
+      "pactl"
+      "upload-sample"
+      "${pkgs.sound-theme-freedesktop}/share/sounds/freedesktop/stereo/complete.oga"
+      "bell-window-system"
+    ]
+    "pactl"
+    "play-sample"
+    "bell-window-system"
+  ];
+}
diff --git a/users/Profpatsch/arglib/ArglibNetencode.hs b/users/Profpatsch/arglib/ArglibNetencode.hs
new file mode 100644
index 0000000000..4531151ca2
--- /dev/null
+++ b/users/Profpatsch/arglib/ArglibNetencode.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module ArglibNetencode where
+
+import Data.Attoparsec.ByteString qualified as Atto
+import ExecHelpers
+import Label
+import Netencode qualified
+import PossehlAnalyticsPrelude
+import System.Posix.Env.ByteString qualified as ByteEnv
+
+arglibNetencode :: CurrentProgramName -> Maybe (Label "arglibEnvvar" Text) -> IO Netencode.T
+arglibNetencode progName mEnvvar = do
+  let envvar = mEnvvar <&> (.arglibEnvvar) & fromMaybe "ARGLIB_NETENCODE" & textToBytesUtf8
+  ByteEnv.getEnv envvar >>= \case
+    Nothing -> dieUserError progName [fmt|could not read args, envvar {envvar} not set|]
+    Just bytes ->
+      case Atto.parseOnly (Netencode.netencodeParser <* Atto.endOfInput) bytes of
+        Left err -> dieEnvironmentProblem progName [fmt|arglib parsing error: {err}|]
+        Right t -> do
+          ByteEnv.unsetEnv envvar
+          pure t
diff --git a/users/Profpatsch/arglib/arglib-netencode.cabal b/users/Profpatsch/arglib/arglib-netencode.cabal
new file mode 100644
index 0000000000..42b524f405
--- /dev/null
+++ b/users/Profpatsch/arglib/arglib-netencode.cabal
@@ -0,0 +1,65 @@
+cabal-version:      3.0
+name:               arglib-netencode
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+    exposed-modules:          ArglibNetencode
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        pa-label,
+        netencode,
+        exec-helpers,
+        attoparsec,
+        unix
diff --git a/users/Profpatsch/arglib/netencode.nix b/users/Profpatsch/arglib/netencode.nix
index 50f4c11c2d..83a94ddd6c 100644
--- a/users/Profpatsch/arglib/netencode.nix
+++ b/users/Profpatsch/arglib/netencode.nix
@@ -1,40 +1,81 @@
 { depot, pkgs, lib, ... }:
 
 let
-  netencode = {
-    rust = depot.nix.writers.rustSimpleLib {
+
+  # Add the given nix arguments to the program as ARGLIB_NETENCODE envvar
+  #
+  # Calls `netencode.gen.dwim` on the provided nix args value.
+  with-args = name: args: prog: depot.nix.writeExecline "${name}-with-args" { } [
+    "export"
+    "ARGLIB_NETENCODE"
+    (depot.users.Profpatsch.netencode.gen.dwim args)
+    prog
+  ];
+
+  rust = depot.nix.writers.rustSimpleLib
+    {
       name = "arglib-netencode";
       dependencies = [
         depot.users.Profpatsch.execline.exec-helpers
         depot.users.Profpatsch.netencode.netencode-rs
       ];
     } ''
-      extern crate netencode;
-      extern crate exec_helpers;
-
-      use netencode::{T};
-      use std::os::unix::ffi::OsStrExt;
-
-      pub fn arglib_netencode(prog_name: &str, env: Option<&std::ffi::OsStr>) -> T {
-          let env = match env {
-              None => std::ffi::OsStr::from_bytes("ARGLIB_NETENCODE".as_bytes()),
-              Some(a) => a
-          };
-          let t = match std::env::var_os(env) {
-              None => exec_helpers::die_user_error(prog_name, format!("could not read args, envvar {} not set", env.to_string_lossy())),
-              // TODO: good error handling for the different parser errors
-              Some(soup) => match netencode::parse::t_t(soup.as_bytes()) {
-                  Ok((remainder, t)) => match remainder.is_empty() {
-                      true => t,
-                      false => exec_helpers::die_environment_problem(prog_name, format!("arglib: there was some unparsed bytes remaining: {:?}", remainder))
-                  },
-                  Err(err) => exec_helpers::die_environment_problem(prog_name, format!("arglib parsing error: {:?}", err))
-              }
-          };
-          std::env::remove_var(env);
-          t
-      }
-    '';
+    extern crate netencode;
+    extern crate exec_helpers;
+
+    use netencode::{T};
+    use std::os::unix::ffi::OsStrExt;
+
+    pub fn arglib_netencode(prog_name: &str, env: Option<&std::ffi::OsStr>) -> T {
+        let env = match env {
+            None => std::ffi::OsStr::from_bytes("ARGLIB_NETENCODE".as_bytes()),
+            Some(a) => a
+        };
+        let t = match std::env::var_os(env) {
+            None => exec_helpers::die_user_error(prog_name, format!("could not read args, envvar {} not set", env.to_string_lossy())),
+            // TODO: good error handling for the different parser errors
+            Some(soup) => match netencode::parse::t_t(soup.as_bytes()) {
+                Ok((remainder, t)) => match remainder.is_empty() {
+                    true => t,
+                    false => exec_helpers::die_environment_problem(prog_name, format!("arglib: there was some unparsed bytes remaining: {:?}", remainder))
+                },
+                Err(err) => exec_helpers::die_environment_problem(prog_name, format!("arglib parsing error: {:?}", err))
+            }
+        };
+        std::env::remove_var(env);
+        t
+    }
+  '';
+
+  haskell = pkgs.haskellPackages.mkDerivation {
+    pname = "arglib-netencode";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./arglib-netencode.cabal
+      ./ArglibNetencode.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
+      depot.users.Profpatsch.netencode.netencode-hs
+      depot.users.Profpatsch.execline.exec-helpers-hs
+    ];
+
+    isLibrary = true;
+    license = lib.licenses.mit;
+
+
   };
 
-in depot.nix.readTree.drvTargets netencode
+
+in
+depot.nix.readTree.drvTargets {
+  inherit
+    with-args
+    rust
+    haskell
+    ;
+}
diff --git a/users/Profpatsch/atomically-write.nix b/users/Profpatsch/atomically-write.nix
index d5039d3e46..c4d07cfbb1 100644
--- a/users/Profpatsch/atomically-write.nix
+++ b/users/Profpatsch/atomically-write.nix
@@ -25,4 +25,5 @@ let
     mv "$tmp/out" "$to"
   '';
 
-in atomically-write
+in
+atomically-write
diff --git a/users/Profpatsch/blog/README.md b/users/Profpatsch/blog/README.md
new file mode 100644
index 0000000000..0753ebdea5
--- /dev/null
+++ b/users/Profpatsch/blog/README.md
@@ -0,0 +1,7 @@
+# (Parts of) my website
+
+This is a part of https://profpatsch.de/, notably the blog posts.
+
+The other parts can be found in [vuizvui](https://github.com/openlab-aux/vuizvui/tree/master/pkgs/profpatsch/profpatsch.de). It’s a mess.
+
+And yes, this implements a webserver & routing engine with nix, execline & s6 utils. β€œBis einer weint”, as we say in German.
diff --git a/users/Profpatsch/blog/default.nix b/users/Profpatsch/blog/default.nix
index d3c5c596b4..f233eda9bb 100644
--- a/users/Profpatsch/blog/default.nix
+++ b/users/Profpatsch/blog/default.nix
@@ -2,11 +2,11 @@
 
 let
   bins = depot.nix.getBins pkgs.lowdown [ "lowdown" ]
-      // depot.nix.getBins pkgs.cdb [ "cdbget" "cdbmake" "cdbdump" ]
-      // depot.nix.getBins pkgs.coreutils [ "mv" "cat" "printf" "test" ]
-      // depot.nix.getBins pkgs.s6-networking [ "s6-tcpserver" ]
-      // depot.nix.getBins pkgs.time [ "time" ]
-      ;
+    // depot.nix.getBins pkgs.cdb [ "cdbget" "cdbmake" "cdbdump" ]
+    // depot.nix.getBins pkgs.coreutils [ "mv" "cat" "printf" "test" ]
+    // depot.nix.getBins pkgs.s6-networking [ "s6-tcpserver" ]
+    // depot.nix.getBins pkgs.time [ "time" ]
+  ;
 
   # /
   # TODO: use
@@ -14,7 +14,7 @@ let
     {
       route = [ "notes" ];
       name = "Notes";
-      page = {cssFile}: router cssFile;
+      page = { cssFile }: router cssFile;
     }
     {
       route = [ "projects" ];
@@ -26,23 +26,41 @@ let
   # /notes/*
   notes = [
     {
-      route = [ "notes" "preventing-oom" ];
-      name = "Preventing out-of-memory (OOM) errors on Linux";
-      page = {cssFile}: markdownToHtml {
-        name = "preventing-oom";
-        markdown = ./notes/preventing-oom.md;
+      route = [ "notes" "private-trackers-are-markets" ];
+      name = "Private bittorrent trackers are markets";
+      page = { cssFile }: markdownToHtml {
+        name = "private-trackers-are-markets";
+        markdown = ./notes/private-trackers-are-markets.md;
+        inherit cssFile;
+      };
+    }
+    {
+      route = [ "notes" "an-idealized-conflang" ];
+      name = "An Idealized Configuration Language";
+      page = { cssFile }: markdownToHtml {
+        name = "an-idealized-conflang";
+        markdown = ./notes/an-idealized-conflang.md;
         inherit cssFile;
       };
     }
     {
       route = [ "notes" "rust-string-conversions" ];
       name = "Converting between different String types in Rust";
-      page = {cssFile}: markdownToHtml {
+      page = { cssFile }: markdownToHtml {
         name = "rust-string-conversions";
         markdown = ./notes/rust-string-conversions.md;
         inherit cssFile;
       };
     }
+    {
+      route = [ "notes" "preventing-oom" ];
+      name = "Preventing out-of-memory (OOM) errors on Linux";
+      page = { cssFile }: markdownToHtml {
+        name = "preventing-oom";
+        markdown = ./notes/preventing-oom.md;
+        inherit cssFile;
+      };
+    }
   ];
 
   projects = [
@@ -69,48 +87,52 @@ let
       title = "Ligature Emulation in Emacs";
       subtitle = "It’s not pretty, but the results are";
       description = "How to set up ligatures using <code>prettify-symbols-mode</code> and the Hasklig/FiraCode fonts.";
-      page = {cssFile}: markdownToHtml {
+      page = { cssFile }: markdownToHtml {
         name = "2017-05-04-ligature-emluation-in-emacs";
         markdown = ./posts/2017-05-04-ligature-emulation-in-emacs.md;
         inherit cssFile;
       };
       route = [ "posts" "2017-05-04-ligature-emluation-in-emacs" ];
-      tags = ["emacs"];
+      tags = [ "emacs" ];
     }
   ];
 
   # convert a markdown file to html via lowdown
-  markdownToHtml = {
-    name,
-    # the file to convert
-    markdown,
-    # css file to add to the final result, as { route }
-    cssFile
-  }:
-    depot.nix.runExecline "${name}.html" {} ([
-      "importas" "out" "out"
+  markdownToHtml =
+    { name
+    , # the file to convert
+      markdown
+    , # css file to add to the final result, as { route }
+      cssFile
+    }:
+    depot.nix.runExecline "${name}.html" { } ([
+      "importas"
+      "out"
+      "out"
       (depot.users.Profpatsch.lib.debugExec "")
       bins.lowdown
-        "-s" "-Thtml"
-      ] ++
-        (lib.optional (cssFile != null) (["-M" "css=${mkRoute cssFile.route}"]))
-      ++ [
-        "-o" "$out"
-        markdown
+      "-s"
+      "-Thtml"
+    ] ++
+    (lib.optional (cssFile != null) ([ "-M" "css=${mkRoute cssFile.route}" ]))
+    ++ [
+      "-o"
+      "$out"
+      markdown
     ]);
 
   # takes a { route … } attrset and converts the route lists to an absolute path
   fullRoute = attrs: lib.pipe attrs [
-    (map (x@{route, ...}: x // { route = mkRoute route; }))
+    (map (x@{ route, ... }: x // { route = mkRoute route; }))
   ];
 
   # a cdb from route to a netencoded version of data for each route
   router = cssFile: lib.pipe (notes ++ posts) [
     (map (r: with depot.users.Profpatsch.lens;
-      lib.pipe r [
-        (over (field "route") mkRoute)
-        (over (field "page") (_ { inherit cssFile; }))
-      ]))
+    lib.pipe r [
+      (over (field "route") mkRoute)
+      (over (field "page") (_ { inherit cssFile; }))
+    ]))
     (map (x: {
       name = x.route;
       value = depot.users.Profpatsch.netencode.gen.dwim x;
@@ -121,24 +143,33 @@ let
 
   # Create a link to the given source file/directory, given the relative path in the depot repo.
   # Checks that the file exists at evaluation time.
-  depotCgitLink = {
-    # relative path from the depot root (without leading /).
-    relativePath
-  }:
-    assert
+  depotCgitLink =
+    {
+      # relative path from the depot root (without leading /).
+      relativePath
+    }:
+      assert
       (lib.assertMsg
-        (builtins.pathExists (depot.path.origSrc + "/${relativePath}"))
+        (builtins.pathExists (depot.path.origSrc + ("/" + relativePath)))
         "depotCgitLink: path /${relativePath} does not exist in depot, and depot.path was ${toString depot.path}");
       "https://code.tvl.fyi/tree/${relativePath}";
 
   # look up a route by path ($1)
   router-lookup = cssFile: depot.nix.writeExecline "router-lookup" { readNArgs = 1; } [
-    cdbLookup (router cssFile) "$1"
+    cdbLookup
+    (router cssFile)
+    "$1"
   ];
 
   runExeclineStdout = name: args: cmd: depot.nix.runExecline name args ([
-    "importas" "-ui" "out" "out"
-    "redirfd" "-w" "1" "$out"
+    "importas"
+    "-ui"
+    "out"
+    "out"
+    "redirfd"
+    "-w"
+    "1"
+    "$out"
   ] ++ cmd);
 
   notes-index-html =
@@ -170,106 +201,167 @@ let
   html = s: s;
 
   projects-index-html =
-  let o = projects;
-  in ''
-    <dl>
-    ${scope o (o: ''
-      <dt><a href="${str o.link}">${esc o.name}</a></dt>
-      <dd>${html o.description}</dd>
-    '')}
-    </dl>
-  '';
+    let o = projects;
+    in ''
+      <dl>
+      ${scope o (o: ''
+        <dt><a href="${str o.link}">${esc o.name}</a></dt>
+        <dd>${html o.description}</dd>
+      '')}
+      </dl>
+    '';
 
   projects-index = pkgs.writeText "projects-index.html" projects-index-html;
 
   posts-index-html =
-  let o = fullRoute posts;
-  in ''
-    <dl>
-    ${scope o (o: ''
-      <dt>${str o.date} <a href="${str o.route}">${esc o.title}</a></dt>
-      <dd>${html o.description}</dd>
-    '')}
-    </dl>
-  '';
+    let o = fullRoute posts;
+    in ''
+      <dl>
+      ${scope o (o: ''
+        <dt>${str o.date} <a href="${str o.route}">${esc o.title}</a></dt>
+        <dd>${html o.description}</dd>
+      '')}
+      </dl>
+    '';
 
   posts-index = pkgs.writeText "projects-index.html" posts-index-html;
 
   arglibNetencode = val: depot.nix.writeExecline "arglib-netencode" { } [
-    "export" "ARGLIB_NETENCODE" (depot.users.Profpatsch.netencode.gen.dwim val)
+    "export"
+    "ARGLIB_NETENCODE"
+    (depot.users.Profpatsch.netencode.gen.dwim val)
     "$@"
   ];
 
   # A simple http server that serves the site. Yes, it’s horrible.
-  site-server = { cssFile, port }: depot.nix.writeExecline "blog-server" {} [
+  site-server = { cssFile, port }: depot.nix.writeExecline "blog-server" { } [
     (depot.users.Profpatsch.lib.runInEmptyEnv [ "PATH" ])
-    bins.s6-tcpserver "127.0.0.1" port
-    bins.time "--format=time: %es" "--"
-    runOr return400
-    "pipeline" [
+    bins.s6-tcpserver
+    "127.0.0.1"
+    port
+    bins.time
+    "--format=time: %es"
+    "--"
+    runOr
+    return400
+    "pipeline"
+    [
       (arglibNetencode {
         what = "request";
       })
       depot.users.Profpatsch.read-http
     ]
     depot.users.Profpatsch.netencode.record-splice-env
-    runOr return500
-    "importas" "-i" "path" "path"
-    "if" [ depot.tools.eprintf "GET \${path}\n" ]
-    runOr return404
-    "backtick" "-ni" "TEMPLATE_DATA" [
+    runOr
+    return500
+    "importas"
+    "-i"
+    "path"
+    "path"
+    "if"
+    [ depot.tools.eprintf "GET \${path}\n" ]
+    runOr
+    return404
+    "backtick"
+    "-ni"
+    "TEMPLATE_DATA"
+    [
       # TODO: factor this out of here, this is routing not serving
-      "ifelse" [ bins.test "$path" "=" "/notes" ]
-        [ "export" "content-type" "text/html"
-          "export" "serve-file" notes-index
-          depot.users.Profpatsch.netencode.env-splice-record
-        ]
-      "ifelse" [ bins.test "$path" "=" "/projects" ]
-        [ "export" "content-type" "text/html"
-          "export" "serve-file" projects-index
-          depot.users.Profpatsch.netencode.env-splice-record
-        ]
-      "ifelse" [ bins.test "$path" "=" "/posts" ]
-        [ "export" "content-type" "text/html"
-          "export" "serve-file" posts-index
-          depot.users.Profpatsch.netencode.env-splice-record
-        ]
+      "ifelse"
+      [ bins.test "$path" "=" "/notes" ]
+      [
+        "export"
+        "content-type"
+        "text/html"
+        "export"
+        "serve-file"
+        notes-index
+        depot.users.Profpatsch.netencode.env-splice-record
+      ]
+      "ifelse"
+      [ bins.test "$path" "=" "/projects" ]
+      [
+        "export"
+        "content-type"
+        "text/html"
+        "export"
+        "serve-file"
+        projects-index
+        depot.users.Profpatsch.netencode.env-splice-record
+      ]
+      "ifelse"
+      [ bins.test "$path" "=" "/posts" ]
+      [
+        "export"
+        "content-type"
+        "text/html"
+        "export"
+        "serve-file"
+        posts-index
+        depot.users.Profpatsch.netencode.env-splice-record
+      ]
       # TODO: ignore potential query arguments. See 404 message
-      "pipeline" [ (router-lookup cssFile) "$path" ]
+      "pipeline"
+      [ (router-lookup cssFile) "$path" ]
       depot.users.Profpatsch.netencode.record-splice-env
-      "importas" "-ui" "page" "page"
-      "export" "content-type" "text/html"
-      "export" "serve-file" "$page"
+      "importas"
+      "-ui"
+      "page"
+      "page"
+      "export"
+      "content-type"
+      "text/html"
+      "export"
+      "serve-file"
+      "$page"
       depot.users.Profpatsch.netencode.env-splice-record
     ]
-    runOr return500
-    "if" [
-      "pipeline" [ bins.printf ''
-        HTTP/1.1 200 OK
-        Content-Type: {{{content-type}}}; charset=UTF-8
-        Connection: close
-
-      '' ]
+    runOr
+    return500
+    "if"
+    [
+      "pipeline"
+      [
+        bins.printf
+        ''
+          HTTP/1.1 200 OK
+          Content-Type: {{{content-type}}}; charset=UTF-8
+          Connection: close
+
+        ''
+      ]
       depot.users.Profpatsch.netencode.netencode-mustache
     ]
-    "pipeline" [ "importas" "t" "TEMPLATE_DATA" bins.printf "%s" "$t" ]
+    "pipeline"
+    [ "importas" "t" "TEMPLATE_DATA" bins.printf "%s" "$t" ]
     depot.users.Profpatsch.netencode.record-splice-env
-    "importas" "-ui" "serve-file" "serve-file"
-    bins.cat "$serve-file"
+    "importas"
+    "-ui"
+    "serve-file"
+    "serve-file"
+    bins.cat
+    "$serve-file"
   ];
 
   # run argv or $1 if argv returns a failure status code.
   runOr = depot.nix.writeExecline "run-or" { readNArgs = 1; } [
-    "foreground" [ "$@" ]
-    "importas" "?" "?"
-    "ifelse" [ bins.test "$?" "-eq" "0" ]
-    []
-    "if" [ depot.tools.eprintf "runOr: exited \${?}, running \${1}\n" ]
+    "foreground"
+    [ "$@" ]
+    "importas"
+    "?"
+    "?"
+    "ifelse"
+    [ bins.test "$?" "-eq" "0" ]
+    [ ]
+    "if"
+    [ depot.tools.eprintf "runOr: exited \${?}, running \${1}\n" ]
     "$1"
   ];
 
-  return400 = depot.nix.writeExecline "return400" {} [
-    bins.printf "%s" ''
+  return400 = depot.nix.writeExecline "return400" { } [
+    bins.printf
+    "%s"
+    ''
       HTTP/1.1 400 Bad Request
       Content-Type: text/plain; charset=UTF-8
       Connection: close
@@ -277,8 +369,10 @@ let
     ''
   ];
 
-  return404 = depot.nix.writeExecline "return404" {} [
-    bins.printf "%s" ''
+  return404 = depot.nix.writeExecline "return404" { } [
+    bins.printf
+    "%s"
+    ''
       HTTP/1.1 404 Not Found
       Content-Type: text/plain; charset=UTF-8
       Connection: close
@@ -287,8 +381,10 @@ let
     ''
   ];
 
-  return500 = depot.nix.writeExecline "return500" {} [
-    bins.printf "%s" ''
+  return500 = depot.nix.writeExecline "return500" { } [
+    bins.printf
+    "%s"
+    ''
       HTTP/1.1 500 Internal Server Error
       Content-Type: text/plain; charset=UTF-8
       Connection: close
@@ -297,10 +393,11 @@ let
     ''
   ];
 
-  capture-stdin = depot.nix.writers.rustSimple {
-    name = "capture-stdin";
-    dependencies = [ depot.users.Profpatsch.execline.exec-helpers ];
-  } ''
+  capture-stdin = depot.nix.writers.rustSimple
+    {
+      name = "capture-stdin";
+      dependencies = [ depot.users.Profpatsch.execline.exec-helpers ];
+    } ''
     extern crate exec_helpers;
     use std::io::Read;
     fn main() {
@@ -337,29 +434,40 @@ let
   cdbRecords =
     with depot.nix.yants;
     defun [ (attrs (either drv string)) string ]
-    (attrs:
-      (lib.concatStrings (lib.mapAttrsToList cdbRecord attrs)) + "\n");
+      (attrs:
+        (lib.concatStrings (lib.mapAttrsToList cdbRecord attrs)) + "\n");
 
   # run cdbmake on a list of key/value pairs (strings
-  cdbMake = name: attrs: depot.nix.runExecline "${name}.cdb" {
-    stdin = cdbRecords attrs;
-  } [
-    "importas" "out" "out"
+  cdbMake = name: attrs: depot.nix.runExecline "${name}.cdb"
+    {
+      stdin = cdbRecords attrs;
+    } [
+    "importas"
+    "out"
+    "out"
     depot.users.Profpatsch.lib.eprint-stdin
-    "if" [ bins.cdbmake "db" "tmp" ]
-    bins.mv "db" "$out"
+    "if"
+    [ bins.cdbmake "db" "tmp" ]
+    bins.mv
+    "db"
+    "$out"
   ];
 
   # look up a key ($2) in the given cdb ($1)
   cdbLookup = depot.nix.writeExecline "cdb-lookup" { readNArgs = 2; } [
     # cdb ($1) on stdin
-    "redirfd" "-r" "0" "$1"
+    "redirfd"
+    "-r"
+    "0"
+    "$1"
     # key ($2) lookup
-    bins.cdbget "$2"
+    bins.cdbget
+    "$2"
   ];
 
-in depot.nix.readTree.drvTargets {
-   inherit
+in
+depot.nix.readTree.drvTargets {
+  inherit
     router
     depotCgitLink
     site-server
diff --git a/users/Profpatsch/blog/notes/an-idealized-conflang.md b/users/Profpatsch/blog/notes/an-idealized-conflang.md
new file mode 100644
index 0000000000..5c6b39f6e8
--- /dev/null
+++ b/users/Profpatsch/blog/notes/an-idealized-conflang.md
@@ -0,0 +1,298 @@
+tags: netencode, json
+date: 2022-03-31
+certainty: likely
+status: initial
+title: An idealized Configuration Language
+
+# An Idealized Configuration Language
+
+JSON brought us one step closer to what an idealized configuration language is,
+which I define as β€œdata, stripped of all externalities of the system it is working in”.
+
+Specifically, JSON is very close to what I consider the minimal properties to represent structured data.
+
+## A short history, according to me
+
+In the beginning, Lisp defined s-expressions as a stand-in for an actual syntax.
+Then, people figured out that it’s also a way to represent structured data.
+It has scalars, which can be nested into lists, recursively.
+
+```
+(this is (a (list) (of lists)))
+```
+
+This provides the first three rules of our idealized language:
+
+1. A **scalar** is a primitive value that is domain-specific.
+   We can assume a bunch of bytes here, or a text or an integer.
+   
+2. A **list** gives an ordering to `0..n` (or `1..n`) values
+   
+3. Both a scalar and a list are the *same kind* of β€œthing” (from here on called **value**),
+   lists can be created from arbitrary values *recursively*
+   (for example scalars, or lists of scalars and other lists)
+
+
+Later, ASN.1 came and had the important insight that the same idealized data structure
+can be represented in different fashions,
+for example as a binary-efficient version and a human-readable format.
+
+Then, XML β€œgraced” the world for a decade or two, and the main lesson from it was
+that you don’t want to mix markup languages and configuration languages,
+and that you don’t want a committee to design these things.
+
+---
+
+In the meantime, Brendan Eich designed Javascript. Its prototype-based object system
+arguably stripped down the rituals of existing OO-systems.
+Douglas Crockford later extracted the object format (minus functions) into a syntax, and we got JSON.
+
+```
+{
+  "foo": [
+    { "nested": "attrs" },
+    "some text"
+  ],
+  "bar": 42
+}
+```
+
+JSON adds another fundamental idea into the mix:
+
+4. **Records** are unordered collections of `name`/`value` pairs.
+   A `name` is defined to be a unicode string, so a semantic descriptor of the nested `value`.
+
+Unfortunately, the JSON syntax does not actually specify any semantics of records (`objects` in JSON lingo),
+in particular it does not mention what the meaning is if a `name` appears twice in one record.
+
+If records can have multiple entries with the same `name`, suddenly ordering becomes important!
+But wait, remember earlier we defined *lists* to impose ordering on two values.
+So in order to rectify that problem, we say that
+
+5. A `name` can only appear in a record *once*, names must be unique.
+
+This is the current state of the programming community at large,
+where most β€œmodern” configuration languages basically use a version of the JSON model
+as their underlying data structure. (However not all of them use the same version.)
+
+## Improving JSON’s data model
+
+We are not yet at the final β€œidealized” configuration language, though.
+
+Modern languages like Standard ML define their data types as a mixture of 
+
+* *records* (β€œstructs” in the C lingo)
+* and *sums* (which you can think about as enums that can hold more `value`s inside them)
+
+This allows to express the common pattern where some fields in a record are only meaningful
+if another fieldβ€”the so-called `tag`-fieldβ€”is set to a specific value.
+
+An easy example: if a request can fail with an error message or succeed with a result.
+
+You could model that as 
+
+```
+{
+  "was_error": true,
+  "error_message": "there was an error"
+}
+```
+
+or
+
+```
+{
+  "was_error": false,
+  "result": 42
+}
+```
+
+in your JSON representation.
+
+But in a ML-like language (like, for example, Rust), you would instead model it as
+
+```
+type RequestResult 
+  = Error { error_message: String }
+  | Success { result: i64 }
+```
+
+where the distinction in `Error` or `Success` makes it clear that `error_message` and `result`
+only exist in one of these cases, not the other.
+
+We *can* encode exactly that idea into JSON in multiple ways, but not a β€œblessed” way.
+
+For example, another way to encode the above would be
+
+```
+{ 
+  "Error": { 
+    "error_message": "there was an error"
+  }
+}
+```
+
+and
+
+```
+{ 
+  "Success": { 
+    "result": 42
+  }
+}
+```
+
+Particularly notice the difference between the language representation, where the type is β€œclosed”only `Success` or `Error` can happenβ€”
+and the data representation where the type is β€œopen”, more cases could potentially exist.
+
+This is an important differentiation from a type system:
+Our idealized configuration language just gives more structure to a bag of data,
+it does not restrict which value can be where.
+Think of a value in an unityped language, like Python.
+
+
+So far we have the notion of 
+
+1. a scalar (a primitive)
+2. a list (ordering on values)
+3. a record (unordered collection of named values)
+
+and in order to get the β€œopen” `tag`ged enumeration values, we introduce
+
+4. a `tag`, which gives a name to a value
+
+We can then redefine `record` to mean β€œan unordered collection of `tag`ged values”,
+which further reduces the amount of concepts needed.
+
+And that’s it, this is the full idealized configuration language.
+
+
+## Some examples of data modelling with tags
+
+This is all well and good, but what does it look like in practice?
+
+For these examples I will be using JSON with a new `< "tag": value >` syntax
+to represent `tag`s.
+
+From a compatibility standpoint, `tag`s (or sum types) have dual properties to record types.
+
+With a record, when you have a producer that *adds* a field to it, the consumer will still be able to handle the record (provided the semantics of the existing fields is not changed by the new field).
+
+With a tag, *removing* a tag from the producer will mean that the consumer will still be able to handle the tag. It might do one β€œdead” check on the removed `tag`, but can still handle the remaining ones just fine.
+
+<!-- TODO: some illustration here -->
+    
+An example of how that is applied in practice is that in `protobuf3`, fields of a record are *always* optional fields.
+
+We can model optional fields by wrapping them in `< "Some": value >` or `< "None": {} >` (where the actual value of the `None` is ignored or always an empty record).
+
+So a protobuf with the fields `foo: int` and `bar: string` has to be parsed by the receiver als containing *four* possibilities:
+
+β„–|foo|bar|
+|--:|---|---|
+|1|`<"None":{}>`|`<"None":{}>`|
+|2|`<"Some":42>`|`<"None":{}>`|
+|3|`<"None":{}>`|`<"Some":"x">`|
+|4|`<"Some":42>`|`<"Some":"x">`|
+
+Now, iff the receiver actually handles all four possibilities
+(and doesn’t just crash if a field is not set, as customary in million-dollar-mistake languages),
+it’s easy to see how removing a field from the producer is semantically equal to always setting it to `<"None":{}>`.
+Since all receivers should be ready to receive `None` for every field, this provides a simple forward-compatibility scheme.
+
+We can abstract this to any kind of tag value:
+If you start with β€œmore” tags, you give yourself space to remove them later without breaking compatibility, typically called β€œforward compatibility”.
+
+
+## To empty list/record or not to
+
+Something to think about is whether records and fields should be defined
+to always contain at least one element.
+
+As it stands, JSON has multiple ways of expressing the β€œempty value”:
+
+* `null`
+* `[]`
+* `{}`
+* `""`
+* *leave out the field*
+
+and two of those come from the possibility of having empty structured values.
+
+## Representations of this language
+
+This line of thought originally fell out of me designing [`netencode`](https://code.tvl.fyi/tree/users/Profpatsch/netencode/README.md)
+as a small human-debuggable format for pipeline serialization.
+
+In addition to the concepts mentioned here (especially tags),
+it provides a better set of scalars than JSON (specifically arbitrary bytestrings),
+but it cannot practically be written or modified by hand,
+which might be a good thing depending on how you look at it.
+
+---
+
+The way that is compatible with the rest of the ecosystem is probably to use a subset of json
+to represent our idealized language.
+
+There is multiple ways of encoding tags in json, which each have their pros and cons.
+
+The most common is probably the β€œtag field” variant, where the tag is pulled into the nested record:
+
+```
+{
+  "_tag": "Success",
+  "result": 42
+}
+```
+
+Which has the advantage that people know how to deal with it and that it’s easy to β€œjust add another field”,
+plus it is backward-compatible when you had a record in the first place.
+
+It has multiple disadvantages however:
+
+* If your value wasn’t a record (e.g. an int) before, you have to put it in a record and assign an arbitrary name to its field
+* People are not forced to β€œunwrap” the tag first, so they are going to forget to check it
+* The magic β€œ_tag” name cannot be used by any of the record’s fields
+
+
+An in-between version of this with less downsides is to always push a json record onto the stack:
+
+```
+{
+  "tag": "Success",
+  "value": {
+    "result": 42
+  }
+}
+```
+
+This makes it harder for people to miss checking the `tag`, but still possible of course.
+It also makes it easily possible to inspect the contents of `value` without knowing the
+exhaustive list of `tag`s, which can be useful in practice (though often not sound!).
+It also gets rid of the β€œ_tag” field name clash problem.
+
+Disadvantages:
+
+* Breaks the backwards-compatibility with an existing record-based approach if you want to introduce `tag`s
+* Verbosity of representation
+* hard to distinguish a record with the `tag` and `value` fields from a `tag`ed value (though you know the type layout of your data on a higher level, don’t you? ;) )
+
+
+The final, β€œmost pure” representation is the one I gave in the original introduction:
+
+```
+{
+  "Success": {
+    "result": 42
+  }
+}
+```
+
+Now you *have* to match on the `tag` name first, before you can actually access your data,
+and it’s less verbose than the above representation.
+
+Disavantages:
+
+* You also have to *know* what `tag`s to expect, it’s harder to query cause you need to extract the keys and values from the dict and then take the first one.
+* Doing a β€œtag backwards compat” check is harder,
+  because you can’t just check whether `_tag` or `tag`/`value` are the keys in the dict.
diff --git a/users/Profpatsch/blog/notes/private-trackers-are-markets.md b/users/Profpatsch/blog/notes/private-trackers-are-markets.md
new file mode 100644
index 0000000000..88fe5f07e5
--- /dev/null
+++ b/users/Profpatsch/blog/notes/private-trackers-are-markets.md
@@ -0,0 +1,46 @@
+# Private bittorrent trackers are markets
+
+Private bittorrent trackers have a currency called ratio,
+which is the bits you upload divided the bits you download.
+
+You have to keep the ratio above a certain lower limit,
+otherwise you get banned from the market or have to cut a deal with the moderators β†’ bancruptcy
+
+New liquidity (?) is introduced to the market by so-called β€œfreeleech” events or tokens,
+which essentially allow you to exchange a token (or some time in the case of time-restricted freeleech)
+for some data, which can then be seeded to generate future profits without spending ratio.
+
+Sometimes, ratio is pulled from the market by allowing to exchange it into website perks,
+like forum titles or other benefits like chat-memberships. This has a deflationary effect.
+It could be compared to β€œvanity items” in MMOs, which don’t grant a mechanical advantage in the market.
+Is there a real-world equivalent? i.e. allowing rich people to exchange some of their worth
+for vanity items instead of investing it for future gain?
+
+Sometimes, ratio can be traded for more than just transferred bits,
+for example by requesting a torrent for a certain album or movie,
+paying some ratio for the fulfillment of the request.
+
+---
+
+Based on how bittorrent works, usually multiple people β€œseed” a torrent.
+This means multiple people can answer a request for trading ratio.
+Part of the request (i.e. the first 30% of a movie)
+can be fulfilled by one party, part of it by a second or even more parties.
+
+For small requests (e.g. albums), often the time between announcing the trade
+and filling the trade is important for who is able to fill it.
+Getting a 1 second head-start vastly increases your chance of a handshake
+and starting the transmission, so on average you get a vastly higher ratio gain from that torrent.
+Meaning that using a bittorrent client which is fast to answer as a seeder will lead to better outcomes.
+This could be compared to mechanisms seen in high-speed trading.
+
+---
+
+Of course these market-mechanisms are in service of a wider policy goal,
+which is to ensure the constant availability of as much high-quality data as possible.
+There is more mechanisms at play on these trackers that all contribute to this goal
+(possible keywords to research: trumping, freeleech for underseeded torrents).
+
+In general, it is important to remember that markets are only a tool,
+never an end in themselves, as neoliberalists would like us to believe.
+They always are in service of a wider goal or policy. We live in a society.
diff --git a/users/Profpatsch/cabal.project b/users/Profpatsch/cabal.project
new file mode 100644
index 0000000000..26b6186969
--- /dev/null
+++ b/users/Profpatsch/cabal.project
@@ -0,0 +1,14 @@
+packages:
+  ./my-prelude/my-prelude.cabal
+  ./my-webstuff/my-webstuff.cabal
+  ./netencode/netencode.cabal
+  ./arglib/arglib-netencode.cabal
+  ./execline/exec-helpers.cabal
+  ./htmx-experiment/htmx-experiment.cabal
+  ./mailbox-org/mailbox-org.cabal
+  ./cas-serve/cas-serve.cabal
+  ./jbovlaste-sqlite/jbovlaste-sqlite.cabal
+  ./whatcd-resolver/whatcd-resolver.cabal
+  ./openlab-tools/openlab-tools.cabal
+  ./httzip/httzip.cabal
+  ./my-xmonad/my-xmonad.cabal
diff --git a/users/Profpatsch/cas-serve/CasServe.hs b/users/Profpatsch/cas-serve/CasServe.hs
new file mode 100644
index 0000000000..62636fe9c1
--- /dev/null
+++ b/users/Profpatsch/cas-serve/CasServe.hs
@@ -0,0 +1,247 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Main where
+
+import ArglibNetencode (arglibNetencode)
+import Control.Applicative
+import Control.Monad.Reader
+import Crypto.Hash qualified as Crypto
+import Data.ByteArray qualified as ByteArray
+import Data.ByteString.Lazy qualified as ByteString.Lazy
+import Data.ByteString.Lazy qualified as Lazy
+import Data.Functor.Compose
+import Data.Int (Int64)
+import Data.List qualified as List
+import Data.Text qualified as Text
+import Data.Text.IO qualified as Text
+import Database.SQLite.Simple (NamedParam ((:=)))
+import Database.SQLite.Simple qualified as Sqlite
+import Database.SQLite.Simple.FromField qualified as Sqlite
+import Database.SQLite.Simple.QQ qualified as Sqlite
+import Label
+import Netencode.Parse qualified as Net
+import Network.HTTP.Types qualified as Http
+import Network.Wai qualified as Wai
+import Network.Wai.Handler.Warp qualified as Warp
+import PossehlAnalyticsPrelude
+import System.IO (stderr)
+
+parseArglib = do
+  let env = label @"arglibEnvvar" "CAS_SERVE_ARGS"
+  let asApi =
+        Net.asRecord >>> do
+          address <- label @"bindToAddress" <$> (Net.key "bindToAddress" >>> Net.asText)
+          port <- label @"port" <$> (Net.key "port" >>> Net.asText)
+          pure (T2 address port)
+  arglibNetencode "cas-serve" (Just env)
+    <&> Net.runParse
+      [fmt|Cannot parse arguments in "{env.arglibEnvvar}"|]
+      ( Net.asRecord >>> do
+          publicApi <- label @"publicApi" <$> (Net.key "publicApi" >>> asApi)
+          privateApi <- label @"privateApi" <$> (Net.key "privateApi" >>> asApi)
+          pure $ T2 publicApi privateApi
+      )
+
+main :: IO ()
+main = do
+  withEnv $ \env ->
+    Warp.runSettings
+      (Warp.defaultSettings & Warp.setPort 7070)
+      (api env)
+
+withEnv :: (Env -> IO a) -> IO a
+withEnv inner = do
+  withSqlite "./data.sqlite" $ \envData -> do
+    withSqlite "./wordlist.sqlite" $ \envWordlist -> inner Env {..}
+
+withSqlite :: String -> (Sqlite.Connection -> IO a) -> IO a
+withSqlite fileName inner = Sqlite.withConnection fileName $ \conn -> do
+  Sqlite.setTrace conn (Just (\msg -> Text.hPutStrLn stderr [fmt|{fileName}: {msg}|]))
+  Sqlite.execute conn [Sqlite.sql|PRAGMA foreign_keys = ON|] ()
+  inner conn
+
+api :: Env -> Wai.Application
+api env req respond = do
+  case runHandler (getById <|> insertById) req env of
+    Nothing -> respond $ Wai.responseLBS Http.status404 [] "endpoint does not exist."
+    Just handler' -> do
+      handler' >>= \case
+        Left (status, err) -> respond $ Wai.responseLBS status [] (err & toLazyBytes)
+        Right (headers, body) ->
+          respond $
+            Wai.responseLBS
+              Http.status200
+              headers
+              (body & toLazyBytes)
+
+data Env = Env
+  { envWordlist :: Sqlite.Connection,
+    envData :: Sqlite.Connection
+  }
+
+-- | I don’t need any fancy routing in this, so a handler is just something that returns a @Just (IO a)@ if it wants to handle the request.
+newtype Handler a
+  = Handler (ReaderT (Wai.Request, Env) (Compose Maybe IO) a)
+  deriving newtype (Functor, Applicative, Alternative)
+
+handler :: ((Wai.Request, Env) -> Maybe (IO a)) -> Handler a
+handler f = Handler (ReaderT (Compose . f))
+
+runHandler :: Handler a -> Wai.Request -> Env -> Maybe (IO a)
+runHandler (Handler handler') req env = getCompose $ handler' & (\readerT -> runReaderT readerT (req, env))
+
+getById ::
+  Handler
+    ( Either
+        (Http.Status, ByteString)
+        ([(Http.HeaderName, ByteString)], ByteString)
+    )
+getById = handler $ \(req, env) -> do
+  guard ((req & Wai.requestMethod) == Http.methodGet)
+  case req & Wai.pathInfo of
+    ["v0", "by-id", filename] -> Just $ do
+      Sqlite.queryNamed
+        @( T3
+             "mimetype"
+             Text
+             "content"
+             ByteString
+             "size"
+             Int
+         )
+        (env.envData)
+        [Sqlite.sql|
+        SELECT
+          mimetype,
+          cast (content AS blob) as content,
+          size
+        FROM file_content
+        JOIN file_references
+          ON file_references.file_content = file_content.hash_sha256
+        WHERE
+          file_references.reference_type = 'by-id'
+          AND (file_references.name || file_references.extension) = :filename
+       |]
+        [":filename" Sqlite.:= filename]
+        <&> \case
+          [] -> Left (Http.status404, "File not found.")
+          [res] ->
+            Right
+              ( [ ("Content-Type", res.mimetype & textToBytesUtf8),
+                  ("Content-Length", res.size & showToText & textToBytesUtf8)
+                ],
+                -- TODO: should this be lazy/streamed?
+                res.content
+              )
+          _more -> Left "file_references must be unique (in type and name)" & unwrapError
+    _ -> Nothing
+
+insertById :: Handler (Either a ([(Http.HeaderName, ByteString)], ByteString))
+insertById = handler $ \(req, env) -> do
+  guard ((req & Wai.requestMethod) == Http.methodPost)
+  case req & Wai.pathInfo of
+    ["v0", "by-id"] -> Just $ do
+      let maybeText bytes = case bytesToTextUtf8 bytes of
+            Left _err -> Nothing
+            Right t -> Just t
+      let mimeType =
+            ( (req & Wai.requestHeaders & List.lookup "X-Cas-Serve-Mimetype" >>= maybeText)
+                <|> (req & Wai.requestHeaders & List.lookup "Content-Type" >>= maybeText)
+            )
+              & fromMaybe "application/octet-stream"
+
+      let magicFileEnding mimeType' = case Text.split (== '/') mimeType' of
+            [_, ""] -> Nothing
+            ["", _] -> Nothing
+            [_, "any"] -> Nothing
+            ["image", ty] -> Just (Text.cons '.' ty)
+            ["video", ty] -> Just (Text.cons '.' ty)
+            ["text", "plain"] -> Just ".txt"
+            ["text", "html"] -> Just ".html"
+            ["application", "pdf"] -> Just ".pdf"
+            ["application", "json"] -> Just ".json"
+            _ -> Nothing
+
+      let extension =
+            ( (req & Wai.requestHeaders & List.lookup "X-Cas-Serve-FileExtension" >>= maybeText)
+                <|> ( (req & Wai.requestHeaders & List.lookup "Content-Type")
+                        >>= maybeText
+                        >>= magicFileEnding
+                    )
+            )
+              -- Just the empty extension if we can’t figure it out.
+              & fromMaybe ""
+
+      body <- Wai.consumeRequestBodyStrict req
+      let hash :: Crypto.Digest Crypto.SHA256 = Crypto.hashlazy body
+      let hashBytes = hash & ByteArray.convert @(Crypto.Digest Crypto.SHA256) @ByteString
+      let len = ByteString.Lazy.length body
+      name <- getNameFromWordlist env
+      let fullname = name <> extension
+
+      let conn = env.envData
+      Sqlite.withTransaction conn $ do
+        Sqlite.executeNamed
+          conn
+          [Sqlite.sql|
+            INSERT INTO file_content
+              (content, hash_sha256, size)
+              VALUES
+              (:content, :hash_sha256, :size)
+              ON CONFLICT (hash_sha256) DO NOTHING
+          |]
+          [ ":content" := (body :: Lazy.ByteString),
+            ":hash_sha256" := (hashBytes :: ByteString),
+            ":size" := (len :: Int64)
+          ]
+
+        -- TODO: we are not checking if the name already exists,
+        -- we just assume that 1633^3 is enough to not get any collisions for now.
+        -- If the name exists, the user gets a 500.
+        Sqlite.executeNamed
+          conn
+          [Sqlite.sql|
+            INSERT INTO file_references
+              (file_content, reference_type, name, extension, mimetype)
+            VALUES
+              (:file_content, :reference_type, :name, :extension, :mimetype)
+          |]
+          [ ":file_content" := (hashBytes :: ByteString),
+            ":reference_type" := ("by-id" :: Text),
+            ":name" := name,
+            ":extension" := (extension :: Text),
+            ":mimetype" := (mimeType :: Text)
+          ]
+      pure $
+        Right
+          ( [("Content-Type", "text/plain")],
+            [fmt|/v0/by-id/{fullname}|]
+          )
+    _ -> Nothing
+
+-- Get a random name from a wordlist, that is three words connected by @-@.
+getNameFromWordlist :: Env -> IO Text
+getNameFromWordlist env =
+  do
+    let numberOfWords = 3 :: Int
+    Sqlite.queryNamed @(Sqlite.Only Text)
+      (env.envWordlist)
+      [Sqlite.sql|SELECT word FROM wordlist ORDER BY RANDOM() LIMIT :words|]
+      [":words" Sqlite.:= numberOfWords]
+    <&> map Sqlite.fromOnly
+    <&> Text.intercalate "-"
+
+-- | We can use a Rec with a named list of types to parse a returning row of sqlite!!
+instance
+  ( Sqlite.FromField t1,
+    Sqlite.FromField t2,
+    Sqlite.FromField t3
+  ) =>
+  Sqlite.FromRow (T3 l1 t1 l2 t2 l3 t3)
+  where
+  fromRow = do
+    T3
+      <$> (label @l1 <$> Sqlite.field)
+      <*> (label @l2 <$> Sqlite.field)
+      <*> (label @l3 <$> Sqlite.field)
diff --git a/users/Profpatsch/cas-serve/cas-serve.cabal b/users/Profpatsch/cas-serve/cas-serve.cabal
new file mode 100644
index 0000000000..d14776700a
--- /dev/null
+++ b/users/Profpatsch/cas-serve/cas-serve.cabal
@@ -0,0 +1,73 @@
+cabal-version:      3.0
+name:               cas-serve
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+executable cas-serve
+    import: common-options
+
+    main-is:          CasServe.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        pa-label,
+        arglib-netencode,
+        netencode,
+        text,
+        sqlite-simple,
+        http-types,
+        wai,
+        warp,
+        mtl,
+        bytestring,
+        memory,
+        crypton,
diff --git a/users/Profpatsch/cas-serve/default.nix b/users/Profpatsch/cas-serve/default.nix
new file mode 100644
index 0000000000..14c3e4aa13
--- /dev/null
+++ b/users/Profpatsch/cas-serve/default.nix
@@ -0,0 +1,38 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.sqlite [ "sqlite3" ];
+
+  cas-serve = pkgs.haskellPackages.mkDerivation {
+    pname = "cas-serve";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./cas-serve.cabal
+      ./CasServe.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.crypton
+      pkgs.haskellPackages.wai
+      pkgs.haskellPackages.warp
+      pkgs.haskellPackages.sqlite-simple
+      depot.users.Profpatsch.arglib.netencode.haskell
+      depot.users.Profpatsch.netencode.netencode-hs
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+  create-cas-database = depot.nix.writeExecline "create-cas-database" { readNArgs = 1; } [
+    bins.sqlite3
+    "$1"
+    "-init"
+    ./schema.sql
+  ];
+in
+cas-serve
diff --git a/users/Profpatsch/cas-serve/schema.sql b/users/Profpatsch/cas-serve/schema.sql
new file mode 100644
index 0000000000..b61a7a1ad5
--- /dev/null
+++ b/users/Profpatsch/cas-serve/schema.sql
@@ -0,0 +1,38 @@
+-- SQLite
+.dump
+
+PRAGMA foreign_keys = ON;
+
+BEGIN transaction;
+
+create table if not exists file_content (
+  content blob NOT NULL,
+  hash_sha256 blob PRIMARY KEY,
+  size integer NOT NULL
+) WITHOUT ROWID;
+
+
+create table if not exists file_references (
+  rowid integer PRIMARY KEY,
+  file_content NOT NULL REFERENCES file_content ON DELETE CASCADE,
+  reference_type text NOT NULL,
+  name text NOT NULL,
+  extension text NOT NULL,
+  mimetype text NOT NULL
+);
+
+create unique index if not exists file_references_type_name_unique on file_references (reference_type, name);
+
+-- insert into file_content values ('mycontent', 'myhash', 9);
+-- insert into file_references values (NULL, 'myhash', 'by-id', 'myschranz', '.txt', 'text/plain');
+-- insert into file_content values (readfile('/home/philip/Pictures/screenshot.png'), 'anotherhash', 999);
+-- insert into file_references values (NULL, 'anotherhash', 'by-id', 'img', '.png', 'image/png');
+
+select * from file_content;
+
+select * from file_references;
+
+COMMIT;
+
+-- drop table file_content;
+-- drop table file_references;
diff --git a/users/Profpatsch/cas-serve/wordlist.json b/users/Profpatsch/cas-serve/wordlist.json
new file mode 100644
index 0000000000..cc4bc62ad1
--- /dev/null
+++ b/users/Profpatsch/cas-serve/wordlist.json
@@ -0,0 +1 @@
+ [ "acrobat", "africa", "alaska", "albert", "albino", "album", "alcohol", "alex", "alpha", "amadeus", "amanda", "amazon", "america", "analog", "animal", "antenna", "antonio", "apollo", "april", "aroma", "artist", "aspirin", "athlete", "atlas", "banana", "bandit", "banjo", "bikini", "bingo", "bonus", "camera", "canada", "carbon", "casino", "catalog", "cinema", "citizen", "cobra", "comet", "compact", "complex", "context", "credit", "critic", "crystal", "culture", "david", "delta", "dialog", "diploma", "doctor", "domino", "dragon", "drama", "extra", "fabric", "final", "focus", "forum", "galaxy", "gallery", "global", "harmony", "hotel", "humor", "index", "japan", "kilo", "lemon", "liter", "lotus", "mango", "melon", "menu", "meter", "metro", "mineral", "model", "music", "object", "piano", "pirate", "plastic", "radio", "report", "signal", "sport", "studio", "subject", "super", "tango", "taxi", "tempo", "tennis", "textile", "tokyo", "total", "tourist", "video", "visa", "academy", "alfred", "atlanta", "atomic", "barbara", "bazaar", "brother", "budget", "cabaret", "cadet", "candle", "capsule", "caviar", "channel", "chapter", "circle", "cobalt", "comrade", "condor", "crimson", "cyclone", "darwin", "declare", "denver", "desert", "divide", "dolby", "domain", "double", "eagle", "echo", "eclipse", "editor", "educate", "edward", "effect", "electra", "emerald", "emotion", "empire", "eternal", "evening", "exhibit", "expand", "explore", "extreme", "ferrari", "forget", "freedom", "friday", "fuji", "galileo", "genesis", "gravity", "habitat", "hamlet", "harlem", "helium", "holiday", "hunter", "ibiza", "iceberg", "imagine", "infant", "isotope", "jackson", "jamaica", "jasmine", "java", "jessica", "kitchen", "lazarus", "letter", "license", "lithium", "loyal", "lucky", "magenta", "manual", "marble", "maxwell", "mayor", "monarch", "monday", "money", "morning", "mother", "mystery", "native", "nectar", "nelson", "network", "nikita", "nobel", "nobody", "nominal", "norway", "nothing", "number", "october", "office", "oliver", "opinion", "option", "order", "outside", "package", "pandora", "panther", "papa", "pattern", "pedro", "pencil", "people", "phantom", "philips", "pioneer", "pluto", "podium", "portal", "potato", "process", "proxy", "pupil", "python", "quality", "quarter", "quiet", "rabbit", "radical", "radius", "rainbow", "ramirez", "ravioli", "raymond", "respect", "respond", "result", "resume", "richard", "river", "roger", "roman", "rondo", "sabrina", "salary", "salsa", "sample", "samuel", "saturn", "savage", "scarlet", "scorpio", "sector", "serpent", "shampoo", "sharon", "silence", "simple", "society", "sonar", "sonata", "soprano", "sparta", "spider", "sponsor", "abraham", "action", "active", "actor", "adam", "address", "admiral", "adrian", "agenda", "agent", "airline", "airport", "alabama", "aladdin", "alarm", "algebra", "alibi", "alice", "alien", "almond", "alpine", "amber", "amigo", "ammonia", "analyze", "anatomy", "angel", "annual", "answer", "apple", "archive", "arctic", "arena", "arizona", "armada", "arnold", "arsenal", "arthur", "asia", "aspect", "athena", "audio", "august", "austria", "avenue", "average", "axiom", "aztec", "bagel", "baker", "balance", "ballad", "ballet", "bambino", "bamboo", "baron", "basic", "basket", "battery", "belgium", "benefit", "berlin", "bermuda", "bernard", "bicycle", "binary", "biology", "bishop", "blitz", "block", "blonde", "bonjour", "boris", "boston", "bottle", "boxer", "brandy", "bravo", "brazil", "bridge", "british", "bronze", "brown", "bruce", "bruno", "brush", "burger", "burma", "cabinet", "cactus", "cafe", "cairo", "calypso", "camel", "campus", "canal", "cannon", "canoe", "cantina", "canvas", "canyon", "capital", "caramel", "caravan", "career", "cargo", "carlo", "carol", "carpet", "cartel", "cartoon", "castle", "castro", "cecilia", "cement", "center", "century", "ceramic", "chamber", "chance", "change", "chaos", "charlie", "charm", "charter", "cheese", "chef", "chemist", "cherry", "chess", "chicago", "chicken", "chief", "china", "cigar", "circus", "city", "clara", "classic", "claudia", "clean", "client", "climax", "clinic", "clock", "club", "cockpit", "coconut", "cola", "collect", "colombo", "colony", "color", "combat", "comedy", "command", "company", "concert", "connect", "consul", "contact", "contour", "control", "convert", "copy", "corner", "corona", "correct", "cosmos", "couple", "courage", "cowboy", "craft", "crash", "cricket", "crown", "cuba", "dallas", "dance", "daniel", "decade", "decimal", "degree", "delete", "deliver", "delphi", "deluxe", "demand", "demo", "denmark", "derby", "design", "detect", "develop", "diagram", "diamond", "diana", "diego", "diesel", "diet", "digital", "dilemma", "direct", "disco", "disney", "distant", "dollar", "dolphin", "donald", "drink", "driver", "dublin", "duet", "dynamic", "earth", "east", "ecology", "economy", "edgar", "egypt", "elastic", "elegant", "element", "elite", "elvis", "email", "empty", "energy", "engine", "english", "episode", "equator", "escape", "escort", "ethnic", "europe", "everest", "evident", "exact", "example", "exit", "exotic", "export", "express", "factor", "falcon", "family", "fantasy", "fashion", "fiber", "fiction", "fidel", "fiesta", "figure", "film", "filter", "finance", "finish", "finland", "first", "flag", "flash", "florida", "flower", "fluid", "flute", "folio", "ford", "forest", "formal", "formula", "fortune", "forward", "fragile", "france", "frank", "fresh", "friend", "frozen", "future", "gabriel", "gamma", "garage", "garcia", "garden", "garlic", "gemini", "general", "genetic", "genius", "germany", "gloria", "gold", "golf", "gondola", "gong", "good", "gordon", "gorilla", "grand", "granite", "graph", "green", "group", "guide", "guitar", "guru", "hand", "happy", "harbor", "harvard", "havana", "hawaii", "helena", "hello", "henry", "hilton", "history", "horizon", "house", "human", "icon", "idea", "igloo", "igor", "image", "impact", "import", "india", "indigo", "input", "insect", "instant", "iris", "italian", "jacket", "jacob", "jaguar", "janet", "jargon", "jazz", "jeep", "john", "joker", "jordan", "judo", "jumbo", "june", "jungle", "junior", "jupiter", "karate", "karma", "kayak", "kermit", "king", "koala", "korea", "labor", "lady", "lagoon", "laptop", "laser", "latin", "lava", "lecture", "left", "legal", "level", "lexicon", "liberal", "libra", "lily", "limbo", "limit", "linda", "linear", "lion", "liquid", "little", "llama", "lobby", "lobster", "local", "logic", "logo", "lola", "london", "lucas", "lunar", "machine", "macro", "madam", "madonna", "madrid", "maestro", "magic", "magnet", "magnum", "mailbox", "major", "mama", "mambo", "manager", "manila", "marco", "marina", "market", "mars", "martin", "marvin", "mary", "master", "matrix", "maximum", "media", "medical", "mega", "melody", "memo", "mental", "mentor", "mercury", "message", "metal", "meteor", "method", "mexico", "miami", "micro", "milk", "million", "minimum", "minus", "minute", "miracle", "mirage", "miranda", "mister", "mixer", "mobile", "modem", "modern", "modular", "moment", "monaco", "monica", "monitor", "mono", "monster", "montana", "morgan", "motel", "motif", "motor", "mozart", "multi", "museum", "mustang", "natural", "neon", "nepal", "neptune", "nerve", "neutral", "nevada", "news", "next", "ninja", "nirvana", "normal", "nova", "novel", "nuclear", "numeric", "nylon", "oasis", "observe", "ocean", "octopus", "olivia", "olympic", "omega", "opera", "optic", "optimal", "orange", "orbit", "organic", "orient", "origin", "orlando", "oscar", "oxford", "oxygen", "ozone", "pablo", "pacific", "pagoda", "palace", "pamela", "panama", "pancake", "panda", "panel", "panic", "paradox", "pardon", "paris", "parker", "parking", "parody", "partner", "passage", "passive", "pasta", "pastel", "patent", "patient", "patriot", "patrol", "pegasus", "pelican", "penguin", "pepper", "percent", "perfect", "perfume", "period", "permit", "person", "peru", "phone", "photo", "picasso", "picnic", "picture", "pigment", "pilgrim", "pilot", "pixel", "pizza", "planet", "plasma", "plaza", "pocket", "poem", "poetic", "poker", "polaris", "police", "politic", "polo", "polygon", "pony", "popcorn", "popular", "postage", "precise", "prefix", "premium", "present", "price", "prince", "printer", "prism", "private", "prize", "product", "profile", "program", "project", "protect", "proton", "public", "pulse", "puma", "pump", "pyramid", "queen", "radar", "ralph", "random", "rapid", "rebel", "record", "recycle", "reflex", "reform", "regard", "regular", "relax", "reptile", "reverse", "ricardo", "right", "ringo", "risk", "ritual", "robert", "robot", "rocket", "rodeo", "romeo", "royal", "russian", "safari", "salad", "salami", "salmon", "salon", "salute", "samba", "sandra", "santana", "sardine", "school", "scoop", "scratch", "screen", "script", "scroll", "second", "secret", "section", "segment", "select", "seminar", "senator", "senior", "sensor", "serial", "service", "shadow", "sharp", "sheriff", "shock", "short", "shrink", "sierra", "silicon", "silk", "silver", "similar", "simon", "single", "siren", "slang", "slogan", "smart", "smoke", "snake", "social", "soda", "solar", "solid", "solo", "sonic", "source", "soviet", "special", "speed", "sphere", "spiral", "spirit", "spring", "static", "status", "stereo", "stone", "stop", "street", "strong", "student", "style", "sultan", "susan", "sushi", "suzuki", "switch", "symbol", "system", "tactic", "tahiti", "talent", "tarzan", "telex", "texas", "theory", "thermos", "tiger", "titanic", "tomato", "topic", "tornado", "toronto", "torpedo", "totem", "tractor", "traffic", "transit", "trapeze", "travel", "tribal", "trick", "trident", "trilogy", "tripod", "tropic", "trumpet", "tulip", "tuna", "turbo", "twist", "ultra", "uniform", "union", "uranium", "vacuum", "valid", "vampire", "vanilla", "vatican", "velvet", "ventura", "venus", "vertigo", "veteran", "victor", "vienna", "viking", "village", "vincent", "violet", "violin", "virtual", "virus", "vision", "visitor", "visual", "vitamin", "viva", "vocal", "vodka", "volcano", "voltage", "volume", "voyage", "water", "weekend", "welcome", "western", "window", "winter", "wizard", "wolf", "world", "xray", "yankee", "yoga", "yogurt", "yoyo", "zebra", "zero", "zigzag", "zipper", "zodiac", "zoom", "acid", "adios", "agatha", "alamo", "alert", "almanac", "aloha", "andrea", "anita", "arcade", "aurora", "avalon", "baby", "baggage", "balloon", "bank", "basil", "begin", "biscuit", "blue", "bombay", "botanic", "brain", "brenda", "brigade", "cable", "calibre", "carmen", "cello", "celtic", "chariot", "chrome", "citrus", "civil", "cloud", "combine", "common", "cool", "copper", "coral", "crater", "cubic", "cupid", "cycle", "depend", "door", "dream", "dynasty", "edison", "edition", "enigma", "equal", "eric", "event", "evita", "exodus", "extend", "famous", "farmer", "food", "fossil", "frog", "fruit", "geneva", "gentle", "george", "giant", "gilbert", "gossip", "gram", "greek", "grille", "hammer", "harvest", "hazard", "heaven", "herbert", "heroic", "hexagon", "husband", "immune", "inca", "inch", "initial", "isabel", "ivory", "jason", "jerome", "joel", "joshua", "journal", "judge", "juliet", "jump", "justice", "kimono", "kinetic", "leonid", "leopard", "lima", "maze", "medusa", "member", "memphis", "michael", "miguel", "milan", "mile", "miller", "mimic", "mimosa", "mission", "monkey", "moral", "moses", "mouse", "nancy", "natasha", "nebula", "nickel", "nina", "noise", "orchid", "oregano", "origami", "orinoco", "orion", "othello", "paper", "paprika", "prelude", "prepare", "pretend", "promise", "prosper", "provide", "puzzle", "remote", "repair", "reply", "rival", "riviera", "robin", "rose", "rover", "rudolf", "saga", "sahara", "scholar", "shelter", "ship", "shoe", "sigma", "sister", "sleep", "smile", "spain", "spark", "split", "spray", "square", "stadium", "star", "storm", "story", "strange", "stretch", "stuart", "subway", "sugar", "sulfur", "summer", "survive", "sweet", "swim", "table", "taboo", "target", "teacher", "telecom", "temple", "tibet", "ticket", "tina", "today", "toga", "tommy", "tower", "trivial", "tunnel", "turtle", "twin", "uncle", "unicorn", "unique", "update", "valery", "vega", "version", "voodoo", "warning", "william", "wonder", "year", "yellow", "young", "absent", "absorb", "absurd", "accent", "alfonso", "alias", "ambient", "anagram", "andy", "anvil", "appear", "apropos", "archer", "ariel", "armor", "arrow", "austin", "avatar", "axis", "baboon", "bahama", "bali", "balsa", "barcode", "bazooka", "beach", "beast", "beatles", "beauty", "before", "benny", "betty", "between", "beyond", "billy", "bison", "blast", "bless", "bogart", "bonanza", "book", "border", "brave", "bread", "break", "broken", "bucket", "buenos", "buffalo", "bundle", "button", "buzzer", "byte", "caesar", "camilla", "canary", "candid", "carrot", "cave", "chant", "child", "choice", "chris", "cipher", "clarion", "clark", "clever", "cliff", "clone", "conan", "conduct", "congo", "costume", "cotton", "cover", "crack", "current", "danube", "data", "decide", "deposit", "desire", "detail", "dexter", "dinner", "donor", "druid", "drum", "easy", "eddie", "enjoy", "enrico", "epoxy", "erosion", "except", "exile", "explain", "fame", "fast", "father", "felix", "field", "fiona", "fire", "fish", "flame", "flex", "flipper", "float", "flood", "floor", "forbid", "forever", "fractal", "frame", "freddie", "front", "fuel", "gallop", "game", "garbo", "gate", "gelatin", "gibson", "ginger", "giraffe", "gizmo", "glass", "goblin", "gopher", "grace", "gray", "gregory", "grid", "griffin", "ground", "guest", "gustav", "gyro", "hair", "halt", "harris", "heart", "heavy", "herman", "hippie", "hobby", "honey", "hope", "horse", "hostel", "hydro", "imitate", "info", "ingrid", "inside", "invent", "invest", "invite", "ivan", "james", "jester", "jimmy", "join", "joseph", "juice", "julius", "july", "kansas", "karl", "kevin", "kiwi", "ladder", "lake", "laura", "learn", "legacy", "legend", "lesson", "life", "light", "list", "locate", "lopez", "lorenzo", "love", "lunch", "malta", "mammal", "margin", "margo", "marion", "mask", "match", "mayday", "meaning", "mercy", "middle", "mike", "mirror", "modest", "morph", "morris", "mystic", "nadia", "nato", "navy", "needle", "neuron", "never", "newton", "nice", "night", "nissan", "nitro", "nixon", "north", "oberon", "octavia", "ohio", "olga", "open", "opus", "orca", "oval", "owner", "page", "paint", "palma", "parent", "parlor", "parole", "paul", "peace", "pearl", "perform", "phoenix", "phrase", "pierre", "pinball", "place", "plate", "plato", "plume", "pogo", "point", "polka", "poncho", "powder", "prague", "press", "presto", "pretty", "prime", "promo", "quest", "quick", "quiz", "quota", "race", "rachel", "raja", "ranger", "region", "remark", "rent", "reward", "rhino", "ribbon", "rider", "road", "rodent", "round", "rubber", "ruby", "rufus", "sabine", "saddle", "sailor", "saint", "salt", "scale", "scuba", "season", "secure", "shake", "shallow", "shannon", "shave", "shelf", "sherman", "shine", "shirt", "side", "sinatra", "sincere", "size", "slalom", "slow", "small", "snow", "sofia", "song", "sound", "south", "speech", "spell", "spend", "spoon", "stage", "stamp", "stand", "state", "stella", "stick", "sting", "stock", "store", "sunday", "sunset", "support", "supreme", "sweden", "swing", "tape", "tavern", "think", "thomas", "tictac", "time", "toast", "tobacco", "tonight", "torch", "torso", "touch", "toyota", "trade", "tribune", "trinity", "triton", "truck", "trust", "type", "under", "unit", "urban", "urgent", "user", "value", "vendor", "venice", "verona", "vibrate", "virgo", "visible", "vista", "vital", "voice", "vortex", "waiter", "watch", "wave", "weather", "wedding", "wheel", "whiskey", "wisdom", "android", "annex", "armani", "cake", "confide", "deal", "define", "dispute", "genuine", "idiom", "impress", "include", "ironic", "null", "nurse", "obscure", "prefer", "prodigy", "ego", "fax", "jet", "job", "rio", "ski", "yes" ]
diff --git a/users/Profpatsch/cas-serve/wordlist.sqlite b/users/Profpatsch/cas-serve/wordlist.sqlite
new file mode 100644
index 0000000000..5074474ba0
--- /dev/null
+++ b/users/Profpatsch/cas-serve/wordlist.sqlite
Binary files differdiff --git a/users/Profpatsch/cdb.nix b/users/Profpatsch/cdb.nix
index 8cfaa3ea7a..86e0a2d58f 100644
--- a/users/Profpatsch/cdb.nix
+++ b/users/Profpatsch/cdb.nix
@@ -1,14 +1,15 @@
 { depot, pkgs, ... }:
 
 let
-  cdbListToNetencode = depot.nix.writers.rustSimple {
-    name = "cdb-list-to-netencode";
-    dependencies = [
-      depot.third_party.rust-crates.nom
-      depot.users.Profpatsch.execline.exec-helpers
-      depot.users.Profpatsch.netencode.netencode-rs
-    ];
-  } ''
+  cdbListToNetencode = depot.nix.writers.rustSimple
+    {
+      name = "cdb-list-to-netencode";
+      dependencies = [
+        depot.third_party.rust-crates.nom
+        depot.users.Profpatsch.execline.exec-helpers
+        depot.users.Profpatsch.netencode.netencode-rs
+      ];
+    } ''
     extern crate nom;
     extern crate exec_helpers;
     extern crate netencode;
@@ -84,7 +85,8 @@ let
 
   '';
 
-in {
+in
+{
   inherit
     cdbListToNetencode
     ;
diff --git a/users/Profpatsch/declib/.eslintrc.json b/users/Profpatsch/declib/.eslintrc.json
new file mode 100644
index 0000000000..9cffc711db
--- /dev/null
+++ b/users/Profpatsch/declib/.eslintrc.json
@@ -0,0 +1,14 @@
+{
+  "extends": ["eslint:recommended", "plugin:@typescript-eslint/strict-type-checked"],
+  "parser": "@typescript-eslint/parser",
+  "plugins": ["@typescript-eslint"],
+  "parserOptions": {
+    "project": true
+  },
+  "root": true,
+  "rules": {
+    "no-unused-vars": "warn",
+    "prefer-const": "warn",
+    "@typescript-eslint/no-unused-vars": "warn"
+  }
+}
diff --git a/users/Profpatsch/declib/.gitignore b/users/Profpatsch/declib/.gitignore
new file mode 100644
index 0000000000..8b56bf4ede
--- /dev/null
+++ b/users/Profpatsch/declib/.gitignore
@@ -0,0 +1,6 @@
+/node_modules/
+/.ninja/
+/output/
+
+# ignore for now
+/package.lock.json
diff --git a/users/Profpatsch/declib/.prettierrc b/users/Profpatsch/declib/.prettierrc
new file mode 100644
index 0000000000..7258fb81e0
--- /dev/null
+++ b/users/Profpatsch/declib/.prettierrc
@@ -0,0 +1,8 @@
+{
+  "trailingComma": "all",
+  "tabWidth": 2,
+  "semi": true,
+  "singleQuote": true,
+  "printWidth": 100,
+  "arrowParens": "avoid"
+}
diff --git a/users/Profpatsch/declib/README.md b/users/Profpatsch/declib/README.md
new file mode 100644
index 0000000000..11a8bf21a5
--- /dev/null
+++ b/users/Profpatsch/declib/README.md
@@ -0,0 +1,4 @@
+# Decentralized Library
+
+https://en.wikipedia.org/wiki/Distributed_library
+https://faculty.ist.psu.edu/jjansen/academic/pubs/ride98/ride98.html
diff --git a/users/Profpatsch/declib/build.ninja b/users/Profpatsch/declib/build.ninja
new file mode 100644
index 0000000000..f8844fc9be
--- /dev/null
+++ b/users/Profpatsch/declib/build.ninja
@@ -0,0 +1,16 @@
+
+builddir = .ninja
+
+outdir = ./output
+jsdir = $outdir/js
+
+rule tsc
+  command = node_modules/.bin/tsc
+
+build $outdir/index.js: tsc | index.ts tsconfig.json
+
+rule run
+  command = node $in
+
+build run: run $outdir/index.js
+  pool = console
diff --git a/users/Profpatsch/declib/index.ts b/users/Profpatsch/declib/index.ts
new file mode 100644
index 0000000000..c6a26f0922
--- /dev/null
+++ b/users/Profpatsch/declib/index.ts
@@ -0,0 +1,245 @@
+import generator, { MegalodonInterface } from 'megalodon';
+import { Account } from 'megalodon/lib/src/entities/account';
+import * as masto from 'megalodon/lib/src/entities/notification';
+import { Status } from 'megalodon/lib/src/entities/status';
+import * as rxjs from 'rxjs';
+import { Observable } from 'rxjs';
+import { NodeEventHandler } from 'rxjs/internal/observable/fromEvent';
+import * as sqlite from 'sqlite';
+import sqlite3 from 'sqlite3';
+import * as parse5 from 'parse5';
+import { mergeMap } from 'rxjs/operators';
+
+type Events =
+  | { type: 'connect'; event: [] }
+  | { type: 'update'; event: Status }
+  | { type: 'notification'; event: Notification }
+  | { type: 'delete'; event: number }
+  | { type: 'error'; event: Error }
+  | { type: 'heartbeat'; event: [] }
+  | { type: 'close'; event: [] }
+  | { type: 'parser-error'; event: Error };
+
+type Notification = masto.Notification & {
+  type: 'favourite' | 'reblog' | 'status' | 'mention' | 'poll' | 'update';
+  status: NonNullable<masto.Notification['status']>;
+  account: NonNullable<masto.Notification['account']>;
+};
+
+class Main {
+  private client: MegalodonInterface;
+  private socket: Observable<Events>;
+  private state!: State;
+  private config: {
+    databaseFile?: string;
+    baseServer: string;
+  };
+
+  private constructor() {
+    this.config = {
+      databaseFile: process.env['DECLIB_DATABASE_FILE'],
+      baseServer: process.env['DECLIB_MASTODON_SERVER'] ?? 'mastodon.xyz',
+    };
+    const ACCESS_TOKEN = process.env['DECLIB_MASTODON_ACCESS_TOKEN'];
+
+    if (!ACCESS_TOKEN) {
+      console.error('Please set DECLIB_MASTODON_ACCESS_TOKEN');
+      process.exit(1);
+    }
+    this.client = generator('mastodon', `https://${this.config.baseServer}`, ACCESS_TOKEN);
+    const websocket = this.client.publicSocket();
+    function mk<Name extends string, Type>(name: Name): Observable<{ type: Name; event: Type }> {
+      const wrap =
+        (h: NodeEventHandler) =>
+        (event: Type): void => {
+          h({ type: name, event });
+        };
+      return rxjs.fromEventPattern<{ type: Name; event: Type }>(
+        hdl => websocket.on(name, wrap(hdl)),
+        hdl => websocket.removeListener(name, wrap(hdl)),
+      );
+    }
+    this.socket = rxjs.merge(
+      mk<'connect', []>('connect'),
+      mk<'update', Status>('update'),
+      mk<'notification', Notification>('notification'),
+      mk<'delete', number>('delete'),
+      mk<'error', Error>('error'),
+      mk<'heartbeat', []>('heartbeat'),
+      mk<'close', []>('close'),
+      mk<'parser-error', Error>('parser-error'),
+    );
+  }
+
+  static async init(): Promise<Main> {
+    const self = new Main();
+    self.state = await State.init(self.config);
+    return self;
+  }
+
+  public main() {
+    // const res = await this.getAcc({ username: 'grindhold', server: 'chaos.social' });
+    // const res = await this.getAcc({ username: 'Profpatsch', server: 'mastodon.xyz' });
+    // const res = await this.getStatus('111862170899069698');
+    this.socket
+      .pipe(
+        mergeMap(async event => {
+          switch (event.type) {
+            case 'update': {
+              await this.state.addStatus(event.event);
+              console.log(`${event.event.account.acct}: ${event.event.content}`);
+              console.log(await this.state.databaseInternal.all(`SELECT * from status`));
+              break;
+            }
+            case 'notification': {
+              console.log(`NOTIFICATION (${event.event.type}):`);
+              console.log(event.event);
+              console.log(event.event.status.content);
+              const content = parseContent(event.event.status.content);
+              if (content) {
+                switch (content.command) {
+                  case 'addbook': {
+                    if (content.content[0]) {
+                      const book = {
+                        $owner: event.event.account.acct,
+                        $bookid: content.content[0],
+                      };
+                      console.log('adding book', book);
+                      await this.state.addBook(book);
+                      await this.client.postStatus(
+                        `@${event.event.account.acct} I have inserted book "${book.$bookid}" for you.`,
+                        {
+                          in_reply_to_id: event.event.status.id,
+                          visibility: 'direct',
+                        },
+                      );
+                    }
+                  }
+                }
+              }
+              break;
+            }
+            default: {
+              console.log(event);
+            }
+          }
+        }),
+      )
+      .subscribe();
+  }
+
+  private async getStatus(id: string): Promise<Status | null> {
+    return (await this.client.getStatus(id)).data;
+  }
+
+  private async getAcc(user: { username: string; server: string }): Promise<Account | null> {
+    const fullAccount = `${user.username}@${user.server}`;
+    const res = await this.client.searchAccount(fullAccount, {
+      limit: 10,
+    });
+    const accs = res.data.filter(acc =>
+      this.config.baseServer === user.server
+        ? (acc.acct = user.username)
+        : acc.acct === fullAccount,
+    );
+    return accs[0] ?? null;
+  }
+}
+
+type Interaction = {
+  originalStatus: { id: string };
+  lastStatus: { id: string };
+};
+
+class State {
+  db!: sqlite.Database;
+  private constructor() {}
+
+  static async init(config: { databaseFile?: string }): Promise<State> {
+    const s = new State();
+    s.db = await sqlite.open({
+      filename: config.databaseFile ?? ':memory:',
+      driver: sqlite3.Database,
+    });
+    await s.db.run('CREATE TABLE books (owner text, bookid text)');
+    await s.db.run('CREATE TABLE status (id text primary key, content json)');
+    return s;
+  }
+
+  async addBook(opts: { $owner: string; $bookid: string }) {
+    return await this.db.run('INSERT INTO books (owner, bookid) VALUES ($owner, $bookid)', opts);
+  }
+
+  async addStatus($status: Status) {
+    return await this.db.run(
+      `
+      INSERT INTO status (id, content) VALUES ($id, $status)
+      ON CONFLICT (id) DO UPDATE SET id = $id, content = $status
+      `,
+      {
+        $id: $status.id,
+        $status: JSON.stringify($status),
+      },
+    );
+  }
+
+  get databaseInternal() {
+    return this.db;
+  }
+}
+
+/** Parse the message; take the plain text, first line is the command any any successive lines are content */
+function parseContent(html: string): { command: string; content: string[] } | null {
+  const plain = contentToPlainText(html).split('\n');
+  if (plain[0]) {
+    return { command: plain[0].replace(' ', '').trim(), content: plain.slice(1) };
+  } else {
+    return null;
+  }
+}
+
+/** Convert the Html content to a plain text (best effort), keeping line breaks */
+function contentToPlainText(html: string): string {
+  const queue: parse5.DefaultTreeAdapterMap['childNode'][] = [];
+  queue.push(...parse5.parseFragment(html).childNodes);
+  let res = '';
+  let endOfP = false;
+  for (const el of queue) {
+    switch (el.nodeName) {
+      case '#text': {
+        res += (el as parse5.DefaultTreeAdapterMap['textNode']).value;
+        break;
+      }
+      case 'br': {
+        res += '\n';
+        break;
+      }
+      case 'p': {
+        if (endOfP) {
+          res += '\n';
+          endOfP = false;
+        }
+        queue.push(...el.childNodes);
+        endOfP = true;
+        break;
+      }
+      case 'span': {
+        break;
+      }
+      default: {
+        console.warn('unknown element in message: ', el);
+        break;
+      }
+    }
+  }
+  return res.trim();
+}
+
+Main.init().then(
+  m => {
+    m.main();
+  },
+  rej => {
+    throw rej;
+  },
+);
diff --git a/users/Profpatsch/declib/package.json b/users/Profpatsch/declib/package.json
new file mode 100644
index 0000000000..93176e8581
--- /dev/null
+++ b/users/Profpatsch/declib/package.json
@@ -0,0 +1,25 @@
+{
+  "name": "declib",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.ts",
+  "type": "commonjs",
+  "scripts": {
+    "run": "ninja run"
+  },
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "megalodon": "^9.2.2",
+    "parse5": "^7.1.2",
+    "rxjs": "^7.8.1",
+    "sqlite": "^5.1.1",
+    "sqlite3": "^5.1.7"
+  },
+  "devDependencies": {
+    "@typescript-eslint/eslint-plugin": "^6.21.0",
+    "@typescript-eslint/parser": "^6.21.0",
+    "eslint": "^8.56.0",
+    "typescript": "^5.3.3"
+  }
+}
diff --git a/users/Profpatsch/declib/tsconfig.json b/users/Profpatsch/declib/tsconfig.json
new file mode 100644
index 0000000000..b7f2f4c18b
--- /dev/null
+++ b/users/Profpatsch/declib/tsconfig.json
@@ -0,0 +1,25 @@
+{
+  "compilerOptions": {
+    "strict": true,
+    "module": "NodeNext",
+    "sourceMap": true,
+    "outDir": "output",
+    "target": "ES6",
+    "lib": [],
+    "typeRoots": ["node_modules/@types", "shims/@types"],
+    "moduleResolution": "NodeNext",
+
+    // importHelpers & downlevelIteration will reduce the generated javascript for new language features.
+    // `importHelpers` requires the `tslib` dependency.
+    // "downlevelIteration": true,
+    // "importHelpers": true
+    "noFallthroughCasesInSwitch": true,
+    "noImplicitOverride": true,
+    "noImplicitReturns": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "noUncheckedIndexedAccess": true,
+
+  },
+
+  "files": ["index.ts"]
+}
diff --git a/users/Profpatsch/dhall/lib.dhall b/users/Profpatsch/dhall/lib.dhall
new file mode 100644
index 0000000000..fb97ba6070
--- /dev/null
+++ b/users/Profpatsch/dhall/lib.dhall
@@ -0,0 +1,84 @@
+let List/map
+    : βˆ€(a : Type) β†’ βˆ€(b : Type) β†’ (a β†’ b) β†’ List a β†’ List b
+    = Ξ»(a : Type) β†’
+      Ξ»(b : Type) β†’
+      Ξ»(f : a β†’ b) β†’
+      Ξ»(xs : List a) β†’
+        List/build
+          b
+          ( Ξ»(list : Type) β†’
+            Ξ»(cons : b β†’ list β†’ list) β†’
+              List/fold a xs list (Ξ»(x : a) β†’ cons (f x))
+          )
+
+let
+
+    --| Concatenate a `List` of `List`s into a single `List`
+    List/concat
+    : βˆ€(a : Type) β†’ List (List a) β†’ List a
+    = Ξ»(a : Type) β†’
+      Ξ»(xss : List (List a)) β†’
+        List/build
+          a
+          ( Ξ»(list : Type) β†’
+            Ξ»(cons : a β†’ list β†’ list) β†’
+            Ξ»(nil : list) β†’
+              List/fold
+                (List a)
+                xss
+                list
+                (Ξ»(xs : List a) β†’ Ξ»(ys : list) β†’ List/fold a xs list cons ys)
+                nil
+          )
+
+let
+
+
+    -- Transform a list by applying a function to each element and flattening the results
+    List/concatMap
+    : βˆ€(a : Type) β†’ βˆ€(b : Type) β†’ (a β†’ List b) β†’ List a β†’ List b
+    = Ξ»(a : Type) β†’
+      Ξ»(b : Type) β†’
+      Ξ»(f : a β†’ List b) β†’
+      Ξ»(xs : List a) β†’
+        List/build
+          b
+          ( Ξ»(list : Type) β†’
+            Ξ»(cons : b β†’ list β†’ list) β†’
+              List/fold a xs list (Ξ»(x : a) β†’ List/fold b (f x) list cons)
+          )
+
+let Status = < Empty | NonEmpty : Text >
+
+let
+
+    {-|
+    Transform each value in a `List` to `Text` and then concatenate them with a
+    separator in between each value
+    -}
+    Text/concatMapSep
+    : βˆ€(separator : Text) β†’ βˆ€(a : Type) β†’ (a β†’ Text) β†’ List a β†’ Text
+    = Ξ»(separator : Text) β†’
+      Ξ»(a : Type) β†’
+      Ξ»(f : a β†’ Text) β†’
+      Ξ»(elements : List a) β†’
+        let status =
+              List/fold
+                a
+                elements
+                Status
+                ( Ξ»(x : a) β†’
+                  Ξ»(status : Status) β†’
+                    merge
+                      { Empty = Status.NonEmpty (f x)
+                      , NonEmpty =
+                          Ξ»(result : Text) β†’
+                            Status.NonEmpty (f x ++ separator ++ result)
+                      }
+                      status
+                )
+                Status.Empty
+
+        in  merge { Empty = "", NonEmpty = Ξ»(result : Text) β†’ result } status
+
+in  { List/map, List/concat, List/concatMap, Text/concatMapSep }
diff --git a/users/Profpatsch/emacs-tree-sitter-move/README.md b/users/Profpatsch/emacs-tree-sitter-move/README.md
new file mode 100644
index 0000000000..ae8d763d61
--- /dev/null
+++ b/users/Profpatsch/emacs-tree-sitter-move/README.md
@@ -0,0 +1,5 @@
+# emacs-tree-sitter-move
+
+An experiment in whether we can implement structural editing in emacs using the tree-sitter parser.
+
+What currently works: loading a tree-sitter gramma, navigating the AST left/right/up/down.
diff --git a/users/Profpatsch/emacs-tree-sitter-move/default.nix b/users/Profpatsch/emacs-tree-sitter-move/default.nix
index fdc059c089..a9f259d96d 100644
--- a/users/Profpatsch/emacs-tree-sitter-move/default.nix
+++ b/users/Profpatsch/emacs-tree-sitter-move/default.nix
@@ -1,3 +1,3 @@
 # nothing yet (TODO: expose shell & tool)
-{...}:
-{}
+{ ... }:
+{ }
diff --git a/users/Profpatsch/emacs-tree-sitter-move/shell.nix b/users/Profpatsch/emacs-tree-sitter-move/shell.nix
index 81d622ac73..f400d5c021 100644
--- a/users/Profpatsch/emacs-tree-sitter-move/shell.nix
+++ b/users/Profpatsch/emacs-tree-sitter-move/shell.nix
@@ -1,14 +1,15 @@
-{ pkgs ? import ../../../third_party {}, ... }:
+{ pkgs ? import ../../../third_party { }, ... }:
 let
   inherit (pkgs) lib;
 
-  treeSitterGrammars = pkgs.runCommandLocal "grammars" {} ''
+  treeSitterGrammars = pkgs.runCommandLocal "grammars" { } ''
     mkdir -p $out/bin
     ${lib.concatStringsSep "\n"
       (lib.mapAttrsToList (name: src: "ln -s ${src}/parser $out/bin/${name}.so") pkgs.tree-sitter.builtGrammars)};
   '';
 
-in pkgs.mkShell {
+in
+pkgs.mkShell {
   buildInputs = [
     pkgs.tree-sitter.builtGrammars.python
   ];
diff --git a/users/Profpatsch/exactSource.nix b/users/Profpatsch/exactSource.nix
new file mode 100644
index 0000000000..5c713b5b1c
--- /dev/null
+++ b/users/Profpatsch/exactSource.nix
@@ -0,0 +1,90 @@
+{ ... }:
+# SPDX-License-Identifier: MIT
+# Created by Graham Christensen
+# version from https://github.com/grahamc/mayday/blob/c48f7583e622fe2e695a2a929de34679e5818816/exact-source.nix
+
+let
+  # Require that every path specified does exist.
+  #
+  # By default, Nix won't complain if you refer to a missing file
+  # if you don't actually use it:
+  #
+  #     nix-repl> ./bogus
+  #     /home/grahamc/playground/bogus
+  #
+  #     nix-repl> toString ./bogus
+  #     "/home/grahamc/playground/bogus"
+  #
+  # so in order for this interface to be *exact*, we must
+  # specifically require every provided path exists:
+  #
+  #     nix-repl> "${./bogus}"
+  #     error: getting attributes of path
+  #     '/home/grahamc/playground/bogus': No such file or
+  #     directory
+  requireAllPathsExist = paths:
+    let
+      validation = builtins.map (path: "${path}") paths;
+    in
+    builtins.deepSeq validation paths;
+
+  # Break down a given path in to a list of all of the path and
+  # its parent directories.
+  #
+  # `builtins.path` / `builtins.filterSource` will ask about
+  # a containing directory, and we must say YES otherwise it will
+  # not include anything below it.
+  #
+  # Concretely, convert: "/foo/baz/tux" in to:
+  #     [ "/foo/baz/tux" "/foo/baz" "/foo" ]
+  recursivelyPopDir = path:
+    if path == "/" then [ ]
+    else [ path ] ++ (recursivelyPopDir (builtins.dirOf path));
+
+  # Given a list of of strings, dedup the list and return a
+  # list of all unique strings.
+  #
+  # Note: only works on strings ;):
+  #
+  # First convert [ "foo" "foo" "bar" ] in to:
+  #     [
+  #       { name = "foo"; value = ""; }
+  #       { name = "foo"; value = ""; }
+  #       { name = "bar"; value = ""; }
+  #     ]
+  # then convert that to { "foo" = ""; "bar" = ""; }
+  # then get the attribute names, "foo" and "bar".
+  dedup = strings:
+    let
+      name_value_pairs = builtins.map
+        (string: { name = string; value = ""; })
+        strings;
+      attrset_of_strings = builtins.listToAttrs name_value_pairs;
+    in
+    builtins.attrNames attrset_of_strings;
+
+  exactSource = source_root: paths:
+    let
+      all_possible_paths =
+        let
+          # Convert all the paths in to relative paths on disk.
+          # ie: stringPaths will contain [ "/home/grahamc/playground/..." ];
+          # instead of /nix/store paths.
+          string_paths = builtins.map toString
+            (requireAllPathsExist paths);
+
+          all_paths_with_duplicates = builtins.concatMap
+            recursivelyPopDir
+            string_paths;
+        in
+        dedup all_paths_with_duplicates;
+
+      pathIsSpecified = path:
+        builtins.elem path all_possible_paths;
+    in
+    builtins.path {
+      path = source_root;
+      filter = (path: _type: pathIsSpecified path);
+    };
+in
+exactSource
diff --git a/users/Profpatsch/execline/ExecHelpers.hs b/users/Profpatsch/execline/ExecHelpers.hs
new file mode 100644
index 0000000000..438047b2b9
--- /dev/null
+++ b/users/Profpatsch/execline/ExecHelpers.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TypeApplications #-}
+
+module ExecHelpers where
+
+import Data.String (IsString)
+import MyPrelude
+import qualified System.Exit as Sys
+
+newtype CurrentProgramName = CurrentProgramName { unCurrentProgramName :: Text }
+  deriving newtype (Show, Eq, Ord, IsString)
+
+-- | Exit 1 to signify a generic expected error
+-- (e.g. something that sometimes just goes wrong, like a nix build).
+dieExpectedError :: CurrentProgramName -> Text -> IO a
+dieExpectedError = dieWith 1
+
+-- | Exit 100 to signify a user error (β€œthe user is holding it wrong”).
+--  This is a permanent error, if the program is executed the same way
+-- it should crash with 100 again.
+dieUserError :: CurrentProgramName -> Text -> IO a
+dieUserError = dieWith 100
+
+-- |  Exit 101 to signify an unexpected crash (failing assertion or panic).
+diePanic :: CurrentProgramName -> Text -> IO a
+diePanic = dieWith 101
+
+-- | Exit 111 to signify a temporary error (such as resource exhaustion)
+dieTemporary :: CurrentProgramName -> Text -> IO a
+dieTemporary = dieWith 111
+
+-- |  Exit 126 to signify an environment problem
+-- (the user has set up stuff incorrectly so the program cannot work)
+dieEnvironmentProblem :: CurrentProgramName -> Text -> IO a
+dieEnvironmentProblem = dieWith 126
+
+-- | Exit 127 to signify a missing executable.
+dieMissingExecutable :: CurrentProgramName -> Text -> IO a
+dieMissingExecutable = dieWith 127
+
+dieWith :: Natural -> CurrentProgramName -> Text -> IO a
+dieWith status currentProgramName msg = do
+  putStderrLn [fmt|{currentProgramName & unCurrentProgramName}: {msg}|]
+  Sys.exitWith
+    (Sys.ExitFailure (status & fromIntegral @Natural @Int))
diff --git a/users/Profpatsch/execline/default.nix b/users/Profpatsch/execline/default.nix
index c6a8d284a6..04d07895c6 100644
--- a/users/Profpatsch/execline/default.nix
+++ b/users/Profpatsch/execline/default.nix
@@ -1,16 +1,30 @@
 { depot, pkgs, lib, ... }:
 
 let
-  exec-helpers = depot.nix.writers.rustSimpleLib {
-    name = "exec-helpers";
-  } (builtins.readFile ./exec_helpers.rs);
-
-  print-one-env = depot.nix.writers.rustSimple {
-    name = "print-one-env";
-    dependencies = [
-      depot.users.Profpatsch.execline.exec-helpers
+  exec-helpers-hs = pkgs.haskellPackages.mkDerivation {
+    pname = "exec-helpers";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./exec-helpers.cabal
+      ./ExecHelpers.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-prelude
     ];
-  } ''
+
+    isLibrary = true;
+    license = lib.licenses.mit;
+  };
+
+  print-one-env = depot.nix.writers.rustSimple
+    {
+      name = "print-one-env";
+      dependencies = [
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
     extern crate exec_helpers;
     use std::os::unix::ffi::OsStrExt;
     use std::io::Write;
@@ -25,9 +39,32 @@ let
     }
   '';
 
-in depot.nix.readTree.drvTargets {
+  setsid = depot.nix.writers.rustSimple
+    {
+      name = "setsid";
+      dependencies = [
+        depot.users.Profpatsch.execline.exec-helpers
+        depot.third_party.rust-crates.libc
+      ];
+    } ''
+    use std::os::unix::ffi::OsStrExt;
+    use std::ffi::OsStr;
+
+    fn main() {
+      let (args, prog) = exec_helpers::args_for_exec("setsid", 1);
+      let envvar = OsStr::from_bytes(&args.get(0).expect("first argument must be envvar name to set"));
+      let sid: i32 = unsafe { libc::setsid() };
+      std::env::set_var(envvar, format!("{}", sid));
+      let env: Vec<(&[u8], &[u8])> = vec![];
+      exec_helpers::exec_into_args("getid", prog, env);
+    }
+  '';
+
+in
+depot.nix.readTree.drvTargets {
   inherit
-    exec-helpers
+    exec-helpers-hs
     print-one-env
+    setsid
     ;
 }
diff --git a/users/Profpatsch/execline/exec-helpers.cabal b/users/Profpatsch/execline/exec-helpers.cabal
new file mode 100644
index 0000000000..b472ff6bd5
--- /dev/null
+++ b/users/Profpatsch/execline/exec-helpers.cabal
@@ -0,0 +1,14 @@
+cabal-version:      3.0
+name:               exec-helpers
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+library
+    exposed-modules:          ExecHelpers
+
+    build-depends:
+        base >=4.15 && <5,
+        my-prelude
+
+    default-language: Haskell2010
diff --git a/users/Profpatsch/execline/exec-helpers/Cargo.lock b/users/Profpatsch/execline/exec-helpers/Cargo.lock
new file mode 100644
index 0000000000..1753cc949d
--- /dev/null
+++ b/users/Profpatsch/execline/exec-helpers/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "exec_helpers"
+version = "0.1.0"
diff --git a/users/Profpatsch/execline/exec-helpers/Cargo.toml b/users/Profpatsch/execline/exec-helpers/Cargo.toml
new file mode 100644
index 0000000000..6642b66ee3
--- /dev/null
+++ b/users/Profpatsch/execline/exec-helpers/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "exec_helpers"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "exec_helpers"
+path = "exec_helpers.rs"
diff --git a/users/Profpatsch/execline/exec-helpers/default.nix b/users/Profpatsch/execline/exec-helpers/default.nix
new file mode 100644
index 0000000000..5545d41d9d
--- /dev/null
+++ b/users/Profpatsch/execline/exec-helpers/default.nix
@@ -0,0 +1,6 @@
+{ depot, ... }:
+depot.nix.writers.rustSimpleLib
+{
+  name = "exec-helpers";
+}
+  (builtins.readFile ./exec_helpers.rs)
diff --git a/users/Profpatsch/execline/exec-helpers/exec_helpers.rs b/users/Profpatsch/execline/exec-helpers/exec_helpers.rs
new file mode 100644
index 0000000000..a57cbca353
--- /dev/null
+++ b/users/Profpatsch/execline/exec-helpers/exec_helpers.rs
@@ -0,0 +1,149 @@
+use std::ffi::OsStr;
+use std::os::unix::ffi::{OsStrExt, OsStringExt};
+use std::os::unix::process::CommandExt;
+
+pub fn no_args(current_prog_name: &str) -> () {
+    let mut args = std::env::args_os();
+    // remove argv[0]
+    let _ = args.nth(0);
+    if args.len() > 0 {
+        die_user_error(
+            current_prog_name,
+            format!("Expected no arguments, got {:?}", args.collect::<Vec<_>>()),
+        )
+    }
+}
+
+pub fn args(current_prog_name: &str, no_of_positional_args: usize) -> Vec<Vec<u8>> {
+    let mut args = std::env::args_os();
+    // remove argv[0]
+    let _ = args.nth(0);
+    if args.len() != no_of_positional_args {
+        die_user_error(
+            current_prog_name,
+            format!(
+                "Expected {} arguments, got {}, namely {:?}",
+                no_of_positional_args,
+                args.len(),
+                args.collect::<Vec<_>>()
+            ),
+        )
+    }
+    args.map(|arg| arg.into_vec()).collect()
+}
+
+pub fn args_for_exec(
+    current_prog_name: &str,
+    no_of_positional_args: usize,
+) -> (Vec<Vec<u8>>, Vec<Vec<u8>>) {
+    let mut args = std::env::args_os();
+    // remove argv[0]
+    let _ = args.nth(0);
+    let mut args = args.map(|arg| arg.into_vec());
+    let mut pos_args = vec![];
+    // get positional args
+    for i in 1..no_of_positional_args + 1 {
+        pos_args.push(args.nth(0).expect(&format!(
+            "{}: expects {} positional args, only got {}",
+            current_prog_name, no_of_positional_args, i
+        )));
+    }
+    // prog... is the rest of the iterator
+    let prog: Vec<Vec<u8>> = args.collect();
+    (pos_args, prog)
+}
+
+pub fn exec_into_args<'a, 'b, Args, Arg, Env, Key, Val>(
+    current_prog_name: &str,
+    args: Args,
+    env_additions: Env,
+) -> !
+where
+    Args: IntoIterator<Item = Arg>,
+    Arg: AsRef<[u8]>,
+    Env: IntoIterator<Item = (Key, Val)>,
+    Key: AsRef<[u8]>,
+    Val: AsRef<[u8]>,
+{
+    // TODO: is this possible without collecting into a Vec first, just leaving it an IntoIterator?
+    let args = args.into_iter().collect::<Vec<Arg>>();
+    let mut args = args.iter().map(|v| OsStr::from_bytes(v.as_ref()));
+    let prog = args.nth(0).expect(&format!(
+        "{}: first argument must be an executable",
+        current_prog_name
+    ));
+    // TODO: same here
+    let env = env_additions.into_iter().collect::<Vec<(Key, Val)>>();
+    let env = env
+        .iter()
+        .map(|(k, v)| (OsStr::from_bytes(k.as_ref()), OsStr::from_bytes(v.as_ref())));
+    let err = std::process::Command::new(prog).args(args).envs(env).exec();
+    die_missing_executable(
+        current_prog_name,
+        format!(
+            "exec failed: {}, while trying to execing into {:?}",
+            err, prog
+        ),
+    );
+}
+
+/// Exit 1 to signify a generic expected error
+/// (e.g. something that sometimes just goes wrong, like a nix build).
+pub fn die_expected_error<S>(current_prog_name: &str, msg: S) -> !
+where
+    S: AsRef<str>,
+{
+    die_with(1, current_prog_name, msg)
+}
+
+/// Exit 100 to signify a user error (β€œthe user is holding it wrong”).
+/// This is a permanent error, if the program is executed the same way
+/// it should crash with 100 again.
+pub fn die_user_error<S>(current_prog_name: &str, msg: S) -> !
+where
+    S: AsRef<str>,
+{
+    die_with(100, current_prog_name, msg)
+}
+
+/// Exit 101 to signify an unexpected crash (failing assertion or panic).
+/// This is the same exit code that `panic!()` emits.
+pub fn die_panic<S>(current_prog_name: &str, msg: S) -> !
+where
+    S: AsRef<str>,
+{
+    die_with(101, current_prog_name, msg)
+}
+
+/// Exit 111 to signify a temporary error (such as resource exhaustion)
+pub fn die_temporary<S>(current_prog_name: &str, msg: S) -> !
+where
+    S: AsRef<str>,
+{
+    die_with(111, current_prog_name, msg)
+}
+
+/// Exit 126 to signify an environment problem
+/// (the user has set up stuff incorrectly so the program cannot work)
+pub fn die_environment_problem<S>(current_prog_name: &str, msg: S) -> !
+where
+    S: AsRef<str>,
+{
+    die_with(126, current_prog_name, msg)
+}
+
+/// Exit 127 to signify a missing executable.
+pub fn die_missing_executable<S>(current_prog_name: &str, msg: S) -> !
+where
+    S: AsRef<str>,
+{
+    die_with(127, current_prog_name, msg)
+}
+
+fn die_with<S>(status: i32, current_prog_name: &str, msg: S) -> !
+where
+    S: AsRef<str>,
+{
+    eprintln!("{}: {}", current_prog_name, msg.as_ref());
+    std::process::exit(status)
+}
diff --git a/users/Profpatsch/execline/exec_helpers.rs b/users/Profpatsch/execline/exec_helpers.rs
deleted file mode 100644
index b9e1f57973..0000000000
--- a/users/Profpatsch/execline/exec_helpers.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-use std::os::unix::process::CommandExt;
-use std::ffi::OsStr;
-use std::os::unix::ffi::{OsStringExt, OsStrExt};
-
-pub fn no_args(current_prog_name: &str) -> () {
-    let mut args = std::env::args_os();
-    // remove argv[0]
-    let _ = args.nth(0);
-    if args.len() > 0 {
-        die_user_error(current_prog_name, format!("Expected no arguments, got {:?}", args.collect::<Vec<_>>()))
-    }
-}
-
-pub fn args(current_prog_name: &str, no_of_positional_args: usize) -> Vec<Vec<u8>> {
-    let mut args = std::env::args_os();
-    // remove argv[0]
-    let _ = args.nth(0);
-    if args.len() != no_of_positional_args {
-        die_user_error(current_prog_name, format!("Expected {} arguments, got {}, namely {:?}", no_of_positional_args, args.len(), args.collect::<Vec<_>>()))
-    }
-    args.map(|arg| arg.into_vec()).collect()
-}
-
-pub fn args_for_exec(current_prog_name: &str, no_of_positional_args: usize) -> (Vec<Vec<u8>>, Vec<Vec<u8>>) {
-    let mut args = std::env::args_os();
-    // remove argv[0]
-    let _ = args.nth(0);
-    let mut args = args.map(|arg| arg.into_vec());
-    let mut pos_args = vec![];
-    // get positional args
-    for i in 1..no_of_positional_args+1 {
-            pos_args.push(
-                args.nth(0).expect(
-                    &format!("{}: expects {} positional args, only got {}", current_prog_name, no_of_positional_args, i))
-            );
-    }
-    // prog... is the rest of the iterator
-    let prog : Vec<Vec<u8>> = args.collect();
-    (pos_args, prog)
-}
-
-pub fn exec_into_args<'a, 'b, Args, Arg, Env, Key, Val>(current_prog_name: &str, args: Args, env_additions: Env) -> !
-    where
-    Args: IntoIterator<Item = Arg>,
-    Arg: AsRef<[u8]>,
-    Env: IntoIterator<Item = (Key, Val)>,
-    Key: AsRef<[u8]>,
-    Val: AsRef<[u8]>,
-{
-    // TODO: is this possible without collecting into a Vec first, just leaving it an IntoIterator?
-    let args = args.into_iter().collect::<Vec<Arg>>();
-    let mut args = args.iter().map(|v| OsStr::from_bytes(v.as_ref()));
-    let prog = args.nth(0).expect(&format!("{}: first argument must be an executable", current_prog_name));
-    // TODO: same here
-    let env = env_additions.into_iter().collect::<Vec<(Key, Val)>>();
-    let env = env.iter().map(|(k,v)| (OsStr::from_bytes(k.as_ref()), OsStr::from_bytes(v.as_ref())));
-    let err = std::process::Command::new(prog).args(args).envs(env).exec();
-    die_missing_executable(current_prog_name, format!("exec failed: {}, while trying to execing into {:?}", err, prog));
-}
-
-/// Exit 1 to signify a generic expected error
-/// (e.g. something that sometimes just goes wrong, like a nix build).
-pub fn die_expected_error<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
-{
-  die_with(1, current_prog_name, msg)
-}
-
-/// Exit 100 to signify a user error (β€œthe user is holding it wrong”).
-/// This is a permanent error, if the program is executed the same way
-/// it should crash with 100 again.
-pub fn die_user_error<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
-{
-    die_with(100, current_prog_name, msg)
-}
-
-/// Exit 101 to signify an unexpected crash (failing assertion or panic).
-/// This is the same exit code that `panic!()` emits.
-pub fn die_panic<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
-{
-    die_with(101, current_prog_name, msg)
-}
-
-/// Exit 111 to signify a temporary error (such as resource exhaustion)
-pub fn die_temporary<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
-{
-    die_with(111, current_prog_name, msg)
-}
-
-/// Exit 126 to signify an environment problem
-/// (the user has set up stuff incorrectly so the program cannot work)
-pub fn die_environment_problem<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
-{
-    die_with(126, current_prog_name, msg)
-}
-
-/// Exit 127 to signify a missing executable.
-pub fn die_missing_executable<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
-{
-    die_with(127, current_prog_name, msg)
-}
-
-fn die_with<S>(status: i32, current_prog_name: &str, msg: S) -> !
-    where S: AsRef<str>
-{
-    eprintln!("{}: {}", current_prog_name, msg.as_ref());
-    std::process::exit(status)
-}
diff --git a/users/Profpatsch/fafo.jpg b/users/Profpatsch/fafo.jpg
new file mode 100644
index 0000000000..78f11d208e
--- /dev/null
+++ b/users/Profpatsch/fafo.jpg
Binary files differdiff --git a/users/Profpatsch/git-db/default.nix b/users/Profpatsch/git-db/default.nix
index 7c6f1aee7c..ad5d927677 100644
--- a/users/Profpatsch/git-db/default.nix
+++ b/users/Profpatsch/git-db/default.nix
@@ -1,8 +1,10 @@
 { depot, pkgs, lib, ... }:
 
-depot.nix.writers.rustSimple {
+depot.nix.writers.rustSimple
+{
   name = "git-db";
   dependencies = [
     depot.third_party.rust-crates.git2
   ];
-} (builtins.readFile ./git-db.rs)
+}
+  (builtins.readFile ./git-db.rs)
diff --git a/users/Profpatsch/git-db/git-db.rs b/users/Profpatsch/git-db/git-db.rs
index 5c6bb1f856..c8019bf036 100644
--- a/users/Profpatsch/git-db/git-db.rs
+++ b/users/Profpatsch/git-db/git-db.rs
@@ -2,7 +2,7 @@ extern crate git2;
 use std::os::unix::ffi::OsStrExt;
 use std::path::PathBuf;
 
-const DEFAULT_BRANCH : &str = "refs/heads/main";
+const DEFAULT_BRANCH: &str = "refs/heads/main";
 
 fn main() {
     let git_db_dir = std::env::var_os("GIT_DB_DIR").expect("set GIT_DB_DIR");
@@ -16,8 +16,12 @@ fn main() {
             .bare(true)
             .mkpath(true)
             .description("git-db database")
-            .initial_head(DEFAULT_BRANCH)
-    ).expect(&format!("unable to create or open bare git repo at {}", &git_db.display()));
+            .initial_head(DEFAULT_BRANCH),
+    )
+    .expect(&format!(
+        "unable to create or open bare git repo at {}",
+        &git_db.display()
+    ));
 
     let mut index = repo.index().expect("cannot get the git index file");
     eprintln!("{:#?}", index.version());
@@ -34,8 +38,9 @@ fn main() {
 
     let data = "hi, it’s me".as_bytes();
 
-    index.add_frombuffer(
-        &git2::IndexEntry {
+    index
+        .add_frombuffer(
+            &git2::IndexEntry {
             mtime: now_git_time,
             ctime: now_git_time,
             // don’t make sense
@@ -50,25 +55,26 @@ fn main() {
             flags_extended: 0,
             path: "hi.txt".as_bytes().to_owned(),
         },
-        data
-    ).expect("could not add data to index");
+            data,
+        )
+        .expect("could not add data to index");
 
     let oid = index.write_tree().expect("could not write index tree");
 
-    let to_add_tree = repo.find_tree(oid)
+    let to_add_tree = repo
+        .find_tree(oid)
         .expect("we just created this tree, where did it go?");
 
     let parent_commits = match repo.find_reference(DEFAULT_BRANCH) {
-        Ok(ref_) => vec![
-            ref_
-            .peel_to_commit()
-            .expect(&format!("reference {} does not point to a commit", DEFAULT_BRANCH))
-        ],
+        Ok(ref_) => vec![ref_.peel_to_commit().expect(&format!(
+            "reference {} does not point to a commit",
+            DEFAULT_BRANCH
+        ))],
         Err(err) => match err.code() {
             // no commit exists yet
             git2::ErrorCode::NotFound => vec![],
             _ => panic!("could not read latest commit from {}", DEFAULT_BRANCH),
-        }
+        },
     };
     repo.commit(
         Some(DEFAULT_BRANCH),
@@ -79,7 +85,6 @@ fn main() {
          I wonder if it supports extended commit descriptions?\n",
         &to_add_tree,
         &parent_commits.iter().collect::<Vec<_>>()[..],
-    ).expect("could not commit the index we just wrote");
-
-
+    )
+    .expect("could not commit the index we just wrote");
 }
diff --git a/users/Profpatsch/haskell-module-deps/README.md b/users/Profpatsch/haskell-module-deps/README.md
new file mode 100644
index 0000000000..b4f35beac5
--- /dev/null
+++ b/users/Profpatsch/haskell-module-deps/README.md
@@ -0,0 +1,5 @@
+# haskell-module-deps
+
+An executable that when run in a project directory containing `.hs` files in `./src` will output a png/graph of how those modules import each other, transitively.
+
+Useful for getting an overview, finding weird import edges, figuring out how to get more compilation parallelism into your Haskell project.
diff --git a/users/Profpatsch/haskell-module-deps/default.nix b/users/Profpatsch/haskell-module-deps/default.nix
new file mode 100644
index 0000000000..71cc0a5b0d
--- /dev/null
+++ b/users/Profpatsch/haskell-module-deps/default.nix
@@ -0,0 +1,55 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.zathura [ "zathura" ]
+    // depot.nix.getBins pkgs.haskellPackages.graphmod [ "graphmod" ]
+    // depot.nix.getBins pkgs.graphviz [ "dot" ]
+  ;
+
+  # Display a graph of all modules in a project and how they depend on each other.
+  # Takes the project directory as argument.
+  # Open in zathura.
+  haskell-module-deps = depot.nix.writeExecline "haskell-module-deps" { } [
+    "pipeline"
+    [ haskell-module-deps-with-filetype "pdf" "$@" ]
+    bins.zathura
+    "-"
+  ];
+
+  # Display a graph of all modules in a project and how they depend on each other.
+  # Takes the project directory as argument.
+  # Print a png to stdout.
+  haskell-module-deps-png = depot.nix.writeExecline "haskell-module-deps-png" { } [
+    haskell-module-deps-with-filetype
+    "png"
+    "$@"
+  ];
+
+  # Display a graph of all modules in a project and how they depend on each other.
+  # Takes the file type to generate as first argument
+  # and the project directory as second argument.
+  haskell-module-deps-with-filetype = pkgs.writers.writeBash "haskell-module-deps-with-filetype" ''
+    set -euo pipefail
+    shopt -s globstar
+    filetype="$1"
+    rootDir="$2"
+    ${bins.graphmod} \
+      ${/*silence warnings for missing external dependencies*/""} \
+      --quiet \
+      ${/*applies some kind of import simplification*/""} \
+      --prune-edges \
+      "$rootDir"/src/**/*.hs \
+      | ${bins.dot} \
+          ${/*otherwise it’s a bit cramped*/""} \
+          -Gsize="20,20!" \
+          -T"$filetype"
+  '';
+
+in
+depot.nix.readTree.drvTargets {
+  inherit
+    haskell-module-deps
+    haskell-module-deps-png
+    haskell-module-deps-with-filetype
+    ;
+}
diff --git a/users/Profpatsch/haskell-module-deps/example-output-dhall-haskell.png b/users/Profpatsch/haskell-module-deps/example-output-dhall-haskell.png
new file mode 100644
index 0000000000..53725c49e8
--- /dev/null
+++ b/users/Profpatsch/haskell-module-deps/example-output-dhall-haskell.png
Binary files differdiff --git a/users/Profpatsch/hie.yaml b/users/Profpatsch/hie.yaml
new file mode 100644
index 0000000000..1b5ae942ad
--- /dev/null
+++ b/users/Profpatsch/hie.yaml
@@ -0,0 +1,36 @@
+cradle:
+  cabal:
+    - path: "./my-prelude"
+      component: "lib:my-prelude"
+    - path: "./my-webstuff"
+      component: "lib:my-webstuff"
+    - path: "./netencode"
+      component: "lib:netencode"
+    - path: "./arglib"
+      component: "lib:arglib-netencode"
+    - path: "./execline"
+      component: "lib:exec-helpers"
+    - path: "./htmx-experiment/src"
+      component: "lib:htmx-experiment"
+    - path: "./htmx-experiment/Main.hs"
+      component: "htmx-experiment:exe:htmx-experiment"
+    - path: "./mailbox-org/src"
+      component: "lib:mailbox-org"
+    - path: "./mailbox-org/MailboxOrg.hs"
+      component: "mailbox-org:exe:mailbox-org"
+    - path: "./cas-serve/CasServe.hs"
+      component: "cas-serve:exe:cas-serve"
+    - path: "./jbovlaste-sqlite/JbovlasteSqlite.hs"
+      component: "jbovlaste-sqlite:exe:jbovlaste-sqlite"
+    - path: "./whatcd-resolver/src"
+      component: "lib:whatcd-resolver"
+    - path: "./whatcd-resolver/Main.hs"
+      component: "whatcd-resolver:exe:whatcd-resolver"
+    - path: "./openlab-tools/src"
+      component: "lib:openlab-tools"
+    - path: "./openlab-tools/Main.hs"
+      component: "openlab-tools:exe:openlab-tools"
+    - path: "./httzip/Httzip.hs"
+      component: "httzip:exe:httzip"
+    - path: "./my-xmonad/Xmonad.hs"
+      component: "my-xmonad:exe:xmonad"
diff --git a/users/Profpatsch/htmx-experiment/Main.hs b/users/Profpatsch/htmx-experiment/Main.hs
new file mode 100644
index 0000000000..29ce8610ff
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/Main.hs
@@ -0,0 +1,4 @@
+import HtmxExperiment qualified
+
+main :: IO ()
+main = HtmxExperiment.main
diff --git a/users/Profpatsch/htmx-experiment/default.nix b/users/Profpatsch/htmx-experiment/default.nix
new file mode 100644
index 0000000000..ef1a28bd2b
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/default.nix
@@ -0,0 +1,46 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  htmx-experiment = pkgs.haskellPackages.mkDerivation {
+    pname = "htmx-experiment";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./htmx-experiment.cabal
+      ./Main.hs
+      ./src/HtmxExperiment.hs
+      ./src/ServerErrors.hs
+      ./src/ValidationParseT.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-webstuff
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.blaze-html
+      pkgs.haskellPackages.blaze-markup
+      pkgs.haskellPackages.bytestring
+      pkgs.haskellPackages.dlist
+      pkgs.haskellPackages.http-types
+      pkgs.haskellPackages.ihp-hsx
+      pkgs.haskellPackages.monad-logger
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.pa-field-parser
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.selective
+      pkgs.haskellPackages.text
+      pkgs.haskellPackages.unliftio
+      pkgs.haskellPackages.wai
+      pkgs.haskellPackages.warp
+
+    ];
+
+    isLibrary = false;
+    isExecutable = true;
+    license = lib.licenses.mit;
+  };
+
+
+in
+htmx-experiment
diff --git a/users/Profpatsch/htmx-experiment/htmx-experiment.cabal b/users/Profpatsch/htmx-experiment/htmx-experiment.cabal
new file mode 100644
index 0000000000..e9a0d93614
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/htmx-experiment.cabal
@@ -0,0 +1,89 @@
+cabal-version:      3.0
+name:               htmx-experiment
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+library
+    import: common-options
+    exposed-modules:
+      HtmxExperiment,
+      ServerErrors,
+      ValidationParseT
+    hs-source-dirs: ./src
+
+    build-depends:
+        base >=4.15 && <5,
+        -- http-api-data
+        blaze-html,
+        blaze-markup,
+        bytestring,
+        dlist,
+        http-types,
+        ihp-hsx,
+        monad-logger,
+        pa-error-tree,
+        pa-field-parser,
+        pa-label,
+        pa-prelude,
+        my-webstuff,
+        selective,
+        text,
+        unliftio,
+        wai,
+        warp
+
+
+executable htmx-experiment
+    import: common-options
+    main-is: Main.hs
+
+    build-depends:
+        htmx-experiment,
+        base >=4.15 && <5,
diff --git a/users/Profpatsch/htmx-experiment/src/HtmxExperiment.hs b/users/Profpatsch/htmx-experiment/src/HtmxExperiment.hs
new file mode 100644
index 0000000000..225206a584
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/src/HtmxExperiment.hs
@@ -0,0 +1,377 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+module HtmxExperiment where
+
+import Control.Category qualified as Cat
+import Control.Exception qualified as Exc
+import Control.Monad.Logger
+import Control.Selective (Selective (select))
+import Control.Selective qualified as Selective
+import Data.ByteString qualified as Bytes
+import Data.DList (DList)
+import Data.Functor.Compose
+import Data.List qualified as List
+import Data.Maybe (maybeToList)
+import Data.Maybe qualified as Maybe
+import Data.Monoid qualified as Monoid
+import Data.Text qualified as Text
+import FieldParser hiding (nonEmpty)
+import GHC.TypeLits (KnownSymbol, symbolVal)
+import IHP.HSX.QQ (hsx)
+import Label
+import Multipart2 (FormValidation (FormValidation), FormValidationResult, MultipartParseT, failFormValidation)
+import Multipart2 qualified as Multipart
+import Network.HTTP.Types qualified as Http
+import Network.Wai qualified as Wai
+import Network.Wai.Handler.Warp qualified as Warp
+import PossehlAnalyticsPrelude
+import ServerErrors (ServerError (..), throwUserErrorTree)
+import Text.Blaze.Html5 (Html, docTypeHtml)
+import Text.Blaze.Renderer.Utf8 (renderMarkup)
+import UnliftIO (MonadUnliftIO (withRunInIO))
+import Prelude hiding (compare)
+
+-- data Routes
+--   = Root
+--   | Register
+--   | RegisterSubmit
+
+-- data Router url = Router
+--   { parse :: Routes.URLParser url,
+--     print :: url -> [Text]
+--   }
+
+-- routerPathInfo :: Routes.PathInfo a => Router a
+-- routerPathInfo =
+--   Router
+--     { parse = Routes.fromPathSegments,
+--       print = Routes.toPathSegments
+--     }
+
+-- subroute :: Text -> Router subUrl -> Router subUrl
+-- subroute path inner =
+--   Router
+--     { parse = Routes.segment path *> inner.parse,
+--       print = \url -> path : inner.print url
+--     }
+
+-- routerLeaf :: a -> Router a
+-- routerLeaf a =
+--   Router
+--     { parse = pure a,
+--       print = \_ -> []
+--     }
+
+-- routerToSite ::
+--   ((url -> [(Text, Maybe Text)] -> Text) -> url -> a) ->
+--   Router url ->
+--   Routes.Site url a
+-- routerToSite handler router =
+--   Routes.Site
+--     { handleSite = handler,
+--       formatPathSegments = (\x -> (x, [])) . router.print,
+--       parsePathSegments = Routes.parseSegments router.parse
+--     }
+
+-- handlers queryParams = \case
+--   Root -> "root"
+--   Register -> "register"
+--   RegisterSubmit -> "registersubmit"
+
+newtype Router handler from to = Router {unRouter :: from -> [Text] -> (Maybe handler, to)}
+  deriving
+    (Functor, Applicative)
+    via ( Compose
+            ((->) from)
+            ( Compose
+                ((->) [Text])
+                ((,) (Monoid.First handler))
+            )
+        )
+
+data Routes r handler = Routes
+  { users :: r (Label "register" handler)
+  }
+
+data Endpoint handler subroutes = Endpoint
+  { root :: handler,
+    subroutes :: subroutes
+  }
+  deriving stock (Show, Eq)
+
+data Handler = Handler {url :: Text}
+
+-- myRoute :: Router () from (Endpoint (Routes (Endpoint ()) Handler) b)
+-- myRoute =
+--   root $ do
+--     users <- fixed "users" () $ fixedFinal @"register" ()
+--     pure $ Routes {..}
+
+-- -- | the root and its children
+-- root :: routes from a -> routes from (Endpoint a b)
+-- root = todo
+
+-- | A fixed sub-route with children
+fixed :: Text -> handler -> Router handler from a -> Router handler from (Endpoint handler a)
+fixed route handler inner = Router $ \from -> \case
+  [final]
+    | route == final ->
+        ( Just handler,
+          Endpoint
+            { root = handler,
+              subroutes = (inner.unRouter from []) & snd
+            }
+        )
+  (this : more)
+    | route == this ->
+        ( (inner.unRouter from more) & fst,
+          Endpoint
+            { root = handler,
+              subroutes = (inner.unRouter from more) & snd
+            }
+        )
+  _ -> (Nothing, Endpoint {root = handler, subroutes = (inner.unRouter from []) & snd})
+
+-- integer ::
+--   forall routeName routes from a.
+--   Router (T2 routeName Integer "more" from) a ->
+--   Router from (Endpoint () a)
+-- integer inner = Router $ \case
+--   (path, []) ->
+--     runFieldParser Field.signedDecimal path
+--   (path, more) ->
+--     inner.unRouter more (runFieldParser Field.signedDecimal path)
+
+-- -- | A leaf route
+-- fixedFinal :: forall route handler from. (KnownSymbol route) => handler -> Router handler from (Label route Handler)
+-- fixedFinal handler = do
+--   let route = symbolText @route
+--   Rounter $ \from -> \case
+--     [final] | route == final -> (Just handler, label @route (Handler from))
+--     _ -> (Nothing, label @route handler)
+
+-- | Get the text of a symbol via TypeApplications
+symbolText :: forall sym. KnownSymbol sym => Text
+symbolText = do
+  symbolVal (Proxy :: Proxy sym)
+    & stringToText
+
+main :: IO ()
+main = runStderrLoggingT @IO $ do
+  withRunInIO @(LoggingT IO) $ \runInIO -> do
+    Warp.run 8080 $ \req respond -> catchServerError respond $ do
+      let respondOk res = Wai.responseLBS Http.ok200 [] (renderMarkup res)
+      let htmlRoot inner =
+            docTypeHtml
+              [hsx|
+            <head>
+              <script src="https://unpkg.com/htmx.org@1.9.2" integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h" crossorigin="anonymous"></script>
+            </head>
+            <body>
+              {inner}
+            </body>
+        |]
+      res <-
+        case req & Wai.pathInfo of
+          [] ->
+            pure $
+              respondOk $
+                htmlRoot
+                  [hsx|
+                      <div id="register_buttons">
+                        <button hx-get="/register" hx-target="body" hx-push-url="/register">Register an account</button>
+                        <button hx-get="/login" hx-target="body">Login</button>
+                      </div>
+              |]
+          ["register"] ->
+            pure $ respondOk $ fullEndpoint req $ \case
+              FullPage -> htmlRoot $ registerForm mempty
+              Snippet -> registerForm mempty
+          ["register", "submit"] -> do
+            FormValidation body <-
+              req
+                & parsePostBody
+                  registerFormValidate
+                & runInIO
+            case body of
+              -- if the parse succeeds, ignore any of the data
+              (_, Just a) -> pure $ respondOk $ htmlRoot [hsx|{a}|]
+              (errs, Nothing) -> pure $ respondOk $ htmlRoot $ registerForm errs
+          other ->
+            pure $ respondOk [hsx|no route here at {other}|]
+      respond $ res
+  where
+    catchServerError respond io =
+      Exc.catch io (\(ex :: ServerError) -> respond $ Wai.responseLBS ex.status [] ex.errBody)
+
+parsePostBody ::
+  (MonadIO m, MonadThrow m, MonadLogger m) =>
+  MultipartParseT backend m b ->
+  Wai.Request ->
+  m b
+parsePostBody parser req =
+  Multipart.parseMultipartOrThrow throwUserErrorTree parser req
+
+-- migrate :: IO (Label "numberOfRowsAffected" Natural)
+-- migrate =
+--   Init.runAppTest $ do
+--     runTransaction $
+--       execute
+--         [sql|
+--         CREATE TABLE IF NOT EXISTS experiments.users (
+--           id SERIAL PRIMARY KEY,
+--           email TEXT NOT NULL,
+--           registration_pending_token TEXT NULL
+--         )
+--         |]
+--         ()
+
+data HsxRequest
+  = Snippet
+  | FullPage
+
+fullEndpoint :: Wai.Request -> (HsxRequest -> t) -> t
+fullEndpoint req act = do
+  let isHxRequest = req & Wai.requestHeaders & List.find (\h -> (h & fst) == "HX-Request") & Maybe.isJust
+  if isHxRequest
+    then act Snippet
+    else act FullPage
+
+data FormField = FormField
+  { label_ :: Html,
+    required :: Bool,
+    id_ :: Text,
+    name :: ByteString,
+    type_ :: Text,
+    placeholder :: Maybe Text
+  }
+
+inputHtml ::
+  FormField ->
+  DList FormValidationResult ->
+  Html
+inputHtml (FormField {..}) validationResults = do
+  let validation =
+        validationResults
+          & toList
+          & mapMaybe
+            ( \v ->
+                if v.formFieldName == name
+                  then
+                    Just
+                      ( T2
+                          (label @"errors" (maybeToList v.hasError))
+                          (label @"originalValue" (Monoid.First (Just v.originalValue)))
+                      )
+                  else Nothing
+            )
+          & mconcat
+  let isFirstError =
+        validationResults
+          & List.find (\res -> Maybe.isJust res.hasError && res.formFieldName == name)
+          & Maybe.isJust
+  [hsx|
+      <label for={id_}>{label_}
+        <input
+          autofocus={isFirstError}
+          onfocus="this.select()"
+          required={required}
+          id={id_}
+          name={name}
+          type={type_}
+          placeholder={placeholder}
+          value={validation.originalValue.getFirst}
+        />
+        <p id="{id_}.validation">{validation.errors & nonEmpty <&> toList <&> map prettyError <&> Text.intercalate "; "}</p>
+      </label>
+  |]
+
+registerForm :: DList FormValidationResult -> Html
+registerForm validationErrors =
+  let fields =
+        mconcat
+          [ inputHtml $
+              FormField
+                { label_ = "Your Email:",
+                  required = True,
+                  id_ = "register_email",
+                  name = "email",
+                  type_ = "email",
+                  placeholder = Just "your@email.com"
+                },
+            inputHtml $
+              FormField
+                { label_ = "New password:",
+                  required = True,
+                  id_ = "register_password",
+                  name = "password",
+                  type_ = "password",
+                  placeholder = Just "hunter2"
+                },
+            inputHtml $
+              FormField
+                { label_ = "Repeated password:",
+                  required = True,
+                  id_ = "register_password_repeated",
+                  name = "password_repeated",
+                  type_ = "password",
+                  placeholder = Just "hunter2"
+                }
+          ]
+   in [hsx|
+  <form hx-post="/register/submit">
+    <fieldset>
+      <legend>Register user</legend>
+      {fields validationErrors}
+      <button id="register_submit_button" name="register">
+        Register
+      </button>
+    </fieldset>
+  </form>
+  |]
+
+registerFormValidate ::
+  Applicative m =>
+  MultipartParseT
+    w
+    m
+    (FormValidation (T2 "email" ByteString "password" ByteString))
+registerFormValidate = do
+  let emailFP = FieldParser $ \b ->
+        if
+            | Bytes.elem (charToWordUnsafe '@') b -> Right b
+            | otherwise -> Left [fmt|This is not an email address: "{b & bytesToTextUtf8Unsafe}"|]
+
+  getCompose @(MultipartParseT _ _) @FormValidation $ do
+    email <- Compose $ Multipart.fieldLabel' @"email" "email" emailFP
+    password <-
+      aEqB
+        "password_repeated"
+        "The two password fields must be the same"
+        (Compose $ Multipart.field' "password" Cat.id)
+        (\field -> Compose $ Multipart.field' field Cat.id)
+    pure $ T2 email (label @"password" password)
+  where
+    aEqB field validateErr fCompare fValidate =
+      Selective.fromMaybeS
+        -- TODO: this check only reached if the field itself is valid. Could we combine those errors?
+        (Compose $ pure $ failFormValidation (T2 (label @"formFieldName" field) (label @"originalValue" "")) validateErr)
+        $ do
+          compare <- fCompare
+          validate <- fValidate field
+          pure $ if compare == validate then Just validate else Nothing
+
+-- | A lifted version of 'Data.Maybe.fromMaybe'.
+fromMaybeS :: Selective f => f a -> f (Maybe a) -> f a
+fromMaybeS ifNothing fma =
+  select
+    ( fma <&> \case
+        Nothing -> Left ()
+        Just a -> Right a
+    )
+    ( do
+        a <- ifNothing
+        pure (\() -> a)
+    )
diff --git a/users/Profpatsch/htmx-experiment/src/ServerErrors.hs b/users/Profpatsch/htmx-experiment/src/ServerErrors.hs
new file mode 100644
index 0000000000..0fca7ab464
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/src/ServerErrors.hs
@@ -0,0 +1,244 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module ServerErrors where
+
+import Control.Exception (Exception)
+import Control.Monad.Logger (MonadLogger, logError, logWarn)
+import Data.ByteString.Lazy qualified as Bytes.Lazy
+import Data.Error.Tree
+import Network.HTTP.Types qualified as Http
+import PossehlAnalyticsPrelude
+
+data ServerError = ServerError
+  { status :: Http.Status,
+    errBody :: Bytes.Lazy.ByteString
+  }
+  deriving stock (Show)
+  deriving anyclass (Exception)
+
+emptyServerError :: Http.Status -> ServerError
+emptyServerError status = ServerError {status, errBody = ""}
+
+-- | Throw a user error.
+--
+-- β€œUser” here is a client using our API, not a human user.
+-- So we throw a `HTTP 400` error, which means the API was used incorrectly.
+--
+-- We also log the error as a warning, because it probably signifies a programming bug in our client.
+--
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+throwUserError ::
+  (MonadLogger m, MonadThrow m) =>
+  -- | The error to log & throw to the user
+  Error ->
+  m b
+throwUserError err = do
+  -- TODO: should we make this into a macro to keep the line numbers?
+  $logWarn (err & errorContext "There was a β€œuser holding it wrong” error, check the client code" & prettyError)
+  throwM
+    ServerError
+      { status = Http.badRequest400,
+        errBody = err & prettyError & textToBytesUtf8 & toLazyBytes
+      }
+
+-- | Throw a user error.
+--
+-- β€œUser” here is a client using our API, not a human user.
+-- So we throw a `HTTP 400` error, which means the API was used incorrectly.
+--
+-- We also log the error as a warning, because it probably signifies a programming bug in our client.
+--
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+throwUserErrorTree ::
+  (MonadLogger m, MonadThrow m) =>
+  -- | The error to log & throw to the user
+  ErrorTree ->
+  m b
+throwUserErrorTree err = do
+  -- TODO: should we make this into a macro to keep the line numbers?
+  $logWarn (err & nestedError "There was a β€œuser holding it wrong” error, check the client code" & prettyErrorTree)
+  throwM
+    ServerError
+      { status = Http.badRequest400,
+        errBody = err & prettyErrorTree & textToBytesUtf8 & toLazyBytes
+      }
+
+-- | Unwrap the `Either` and if `Left` throw a user error.
+--
+-- Intended to use in a pipeline, e.g.:
+--
+-- @@
+-- doSomething
+--   >>= orUserError "Oh no something did not work"
+--   >>= doSomethingElse
+-- @@
+--
+-- β€œUser” here is a client using our API, not a human user.
+-- So we throw a `HTTP 400` error, which means the API was used incorrectly.
+--
+-- We also log the error as a warning, because it probably signifies a programming bug in our client.
+--
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+orUserError ::
+  (MonadThrow m, MonadLogger m) =>
+  -- | The message to add as a context to the error being thrown
+  Text ->
+  -- | Result to unwrap and potentially throw
+  Either Error a ->
+  m a
+orUserError outerMsg eErrA =
+  orUserErrorTree outerMsg (first singleError eErrA)
+
+-- | Unwrap the `Either` and if `Left` throw a user error. Will pretty-print the 'ErrorTree'
+--
+-- Intended to use in a pipeline, e.g.:
+--
+-- @@
+-- doSomething
+--   >>= orUserErrorTree "Oh no something did not work"
+--   >>= doSomethingElse
+-- @@
+--
+-- β€œUser” here is a client using our API, not a human user.
+-- So we throw a `HTTP 400` error, which means the API was used incorrectly.
+--
+-- We also log the error as a warning, because it probably signifies a programming bug in our client.
+--
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+orUserErrorTree ::
+  (MonadThrow m, MonadLogger m) =>
+  -- | The message to add as a context to the 'ErrorTree' being thrown
+  Text ->
+  -- | Result to unwrap and potentially throw
+  Either ErrorTree a ->
+  m a
+orUserErrorTree outerMsg = \case
+  Right a -> pure a
+  Left err -> do
+    -- TODO: this outer message should probably be added as a separate root instead of adding to the root error?
+    let tree = errorTreeContext outerMsg err
+    -- TODO: should we make this into a macro to keep the line numbers?
+    $logWarn (errorTreeContext "There was a β€œuser holding it wrong” error, check the client code" tree & prettyErrorTree)
+    throwM
+      ServerError
+        { status = Http.badRequest400,
+          errBody = tree & prettyErrorTree & textToBytesUtf8 & toLazyBytes
+        }
+
+-- | Throw an internal error.
+--
+-- β€œInternal” here means some assertion that we depend on failed,
+-- e.g. some database request returned a wrong result/number of results
+-- or some invariant that we expect to hold failed.
+--
+-- This prints the full error to the log,
+-- and returns a β€œHTTP 500” error without the message.
+--
+-- If you want to signify a mishandling of the API (e.g. a wrong request), throw a `userError`.
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+throwInternalError ::
+  (MonadLogger m, MonadThrow m) =>
+  -- | The error to log internally
+  Error ->
+  m b
+throwInternalError err = do
+  -- TODO: should we make this into a macro to keep the line numbers?
+  $logError
+    (err & prettyError)
+  throwM $ emptyServerError Http.internalServerError500
+
+-- | Throw an internal error.
+--
+-- β€œInternal” here means some assertion that we depend on failed,
+-- e.g. some database request returned a wrong result/number of results
+-- or some invariant that we expect to hold failed.
+--
+-- This prints the full error to the log,
+-- and returns a β€œHTTP 500” error without the message.
+--
+-- If you want to signify a mishandling of the API (e.g. a wrong request), throw a `userError`.
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+throwInternalErrorTree ::
+  (MonadLogger m, MonadThrow m) =>
+  -- | The error to log internally
+  ErrorTree ->
+  m b
+throwInternalErrorTree err = do
+  -- TODO: should we make this into a macro to keep the line numbers?
+  $logError
+    (err & prettyErrorTree)
+  throwM $ emptyServerError   Http.internalServerError500
+
+-- | Unwrap the `Either` and if `Left` throw an internal error.
+--
+-- Intended to use in a pipeline, e.g.:
+--
+-- @@
+-- doSomething
+--   >>= orInternalError "Oh no something did not work"
+--   >>= doSomethingElse
+-- @@
+--
+-- β€œInternal” here means some assertion that we depend on failed,
+-- e.g. some database request returned a wrong result/number of results
+-- or some invariant that we expect to hold failed.
+--
+-- This prints the full error to the log,
+-- and returns a β€œHTTP 500” error without the message.
+--
+-- If you want to signify a mishandling of the API (e.g. a wrong request), throw a `userError`.
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+orInternalError ::
+  (MonadThrow m, MonadLogger m) =>
+  -- | The message to add as a context to the error being thrown
+  Text ->
+  -- | Result to unwrap and potentially throw
+  Either Error a ->
+  m a
+orInternalError outerMsg eErrA = orInternalErrorTree outerMsg (first singleError eErrA)
+
+-- | Unwrap the `Either` and if `Left` throw an internal error. Will pretty-print the 'ErrorTree'.
+--
+-- Intended to use in a pipeline, e.g.:
+--
+-- @@
+-- doSomething
+--   >>= orInternalErrorTree "Oh no something did not work"
+--   >>= doSomethingElse
+-- @@
+--
+-- β€œInternal” here means some assertion that we depend on failed,
+-- e.g. some database request returned a wrong result/number of results
+-- or some invariant that we expect to hold failed.
+--
+-- This prints the full error to the log,
+-- and returns a β€œHTTP 500” error without the message.
+--
+-- If you want to signify a mishandling of the API (e.g. a wrong request), throw a `userError`.
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+orInternalErrorTree ::
+  (MonadThrow m, MonadLogger m) =>
+  -- | The message to add as a context to the 'ErrorTree' being thrown
+  Text ->
+  -- | Result to unwrap and potentially throw
+  Either ErrorTree a ->
+  m a
+orInternalErrorTree outerMsg = \case
+  Right a -> pure a
+  Left err -> do
+    -- TODO: this outer message should probably be added as a separate root instead of adding to the root error?
+    let tree = errorTreeContext outerMsg err
+    -- TODO: should we make this into a macro to keep the line numbers?
+    $logError (tree & prettyErrorTree)
+    throwM $ emptyServerError   Http.internalServerError500
diff --git a/users/Profpatsch/htmx-experiment/src/ValidationParseT.hs b/users/Profpatsch/htmx-experiment/src/ValidationParseT.hs
new file mode 100644
index 0000000000..ffb6c2f395
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/src/ValidationParseT.hs
@@ -0,0 +1,40 @@
+module ValidationParseT where
+
+import Control.Monad.Logger (MonadLogger)
+import Control.Selective (Selective)
+import Data.Error.Tree
+import Data.Functor.Compose (Compose (..))
+import PossehlAnalyticsPrelude
+import ServerErrors
+
+-- | A simple way to create an Applicative parser that parses from some environment.
+--
+-- Use with DerivingVia. Grep codebase for examples.
+newtype ValidationParseT env m a = ValidationParseT {unValidationParseT :: env -> m (Validation (NonEmpty Error) a)}
+  deriving
+    (Functor, Applicative, Selective)
+    via ( Compose
+            ((->) env)
+            (Compose m (Validation (NonEmpty Error)))
+        )
+
+-- | Helper that runs the given parser and throws a user error if the parsing failed.
+runValidationParseTOrUserError ::
+  forall validationParseT env m a.
+  ( Coercible validationParseT (ValidationParseT env m a),
+    MonadLogger m,
+    MonadThrow m
+  ) =>
+  -- | toplevel error message to throw if the parsing fails
+  Error ->
+  -- | The parser which should be run
+  validationParseT ->
+  -- | input to the parser
+  env ->
+  m a
+{-# INLINE runValidationParseTOrUserError #-}
+runValidationParseTOrUserError contextError parser env =
+  (coerce @_ @(ValidationParseT _ _ _) parser).unValidationParseT env
+    >>= \case
+      Failure errs -> throwUserErrorTree (errorTree contextError errs)
+      Success a -> pure a
diff --git a/users/Profpatsch/httzip/Httzip.hs b/users/Profpatsch/httzip/Httzip.hs
new file mode 100644
index 0000000000..761cd1d2ea
--- /dev/null
+++ b/users/Profpatsch/httzip/Httzip.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Main where
+
+import Conduit ((.|))
+import Data.Binary.Builder qualified as Builder
+import Data.Conduit qualified as Cond
+import Data.Conduit.Combinators qualified as Cond
+import Data.Conduit.Process.Typed qualified as Cond
+import Data.Conduit.Process.Typed qualified as Proc
+import Data.List qualified as List
+import Data.Text qualified as Text
+import Network.HTTP.Types qualified as Http
+import Network.Wai qualified as Wai
+import Network.Wai.Conduit qualified as Wai.Conduit
+import Network.Wai.Handler.Warp qualified as Warp
+import PossehlAnalyticsPrelude
+import System.Directory qualified as Dir
+import System.FilePath ((</>))
+import System.FilePath qualified as File
+import System.Posix qualified as Unix
+
+-- Webserver that returns folders under CWD as .zip archives (recursively)
+main :: IO ()
+main = do
+  currentDirectory <- Dir.getCurrentDirectory >>= Dir.canonicalizePath
+  run currentDirectory
+
+run :: FilePath -> IO ()
+run dir = do
+  currentDirectory <- Dir.canonicalizePath dir
+  putStderrLn $ [fmt|current {show currentDirectory}|]
+  Warp.run 7070 $ \req respond -> do
+    let respondHtml status content = respond $ Wai.responseLBS status [("Content-Type", "text/html")] content
+    case req & Wai.pathInfo of
+      [] -> respond $ Wai.responseLBS Http.status200 [("Content-Type", "text/html")] "any directory will be returned as .zip!"
+      filePath -> do
+        absoluteWantedFilepath <- Dir.canonicalizePath (currentDirectory </> (File.joinPath (filePath <&> textToString)))
+        -- I hope this prevents any shenanigans lol
+        let noCurrentDirPrefix = List.stripPrefix (File.addTrailingPathSeparator currentDirectory) absoluteWantedFilepath
+        if
+            | (any (Text.elem '/') filePath) -> putStderrLn "tried %2F encoding" >> respondHtml Http.status400 "no"
+            | Nothing <- noCurrentDirPrefix -> putStderrLn "tried parent dir with .." >> respondHtml Http.status400 "no^2"
+            | Just wantedFilePath <- noCurrentDirPrefix -> do
+                putStderrLn $ [fmt|wanted {show wantedFilePath}|]
+                ex <- Unix.fileExist wantedFilePath
+                if ex
+                  then do
+                    status <- Unix.getFileStatus wantedFilePath
+                    if status & Unix.isDirectory
+                      then do
+                        zipDir <- zipDirectory wantedFilePath
+                        Proc.withProcessWait zipDir $ \process -> do
+                          let stream =
+                                Proc.getStdout process
+                                  .| Cond.map (\bytes -> Cond.Chunk $ Builder.fromByteString bytes)
+                          -- TODO: how to handle broken zip? Is it just gonna return a 500? But the stream is already starting, so hard!
+                          respond $ Wai.Conduit.responseSource Http.ok200 [("Content-Type", "application/zip")] stream
+                      else respondHtml Http.status404 "not found"
+                  else respondHtml Http.status404 "not found"
+  where
+    zipDirectory toZipDir = do
+      putStderrLn [fmt|running $ zip {show ["--recurse-paths", "-", toZipDir]}|]
+      pure $
+        Proc.proc "zip" ["--recurse-paths", "-", toZipDir]
+          & Proc.setStdout Cond.createSource
diff --git a/users/Profpatsch/httzip/default.nix b/users/Profpatsch/httzip/default.nix
new file mode 100644
index 0000000000..35d8a69d56
--- /dev/null
+++ b/users/Profpatsch/httzip/default.nix
@@ -0,0 +1,40 @@
+{ depot, pkgs, lib, ... }:
+
+let
+
+  httzip = pkgs.haskellPackages.mkDerivation {
+    pname = "httzip";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./httzip.cabal
+      ./Httzip.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.warp
+      pkgs.haskellPackages.wai
+      pkgs.haskellPackages.wai-conduit
+      pkgs.haskellPackages.conduit-extra
+      pkgs.haskellPackages.conduit
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+  bins = depot.nix.getBins httzip [ "httzip" ];
+
+in
+depot.nix.writeExecline "httzip-wrapped" { } [
+  "importas"
+  "-i"
+  "PATH"
+  "PATH"
+  "export"
+  "PATH"
+  "${pkgs.zip}/bin:$${PATH}"
+  bins.httzip
+]
diff --git a/users/Profpatsch/httzip/httzip.cabal b/users/Profpatsch/httzip/httzip.cabal
new file mode 100644
index 0000000000..c463a6a5fe
--- /dev/null
+++ b/users/Profpatsch/httzip/httzip.cabal
@@ -0,0 +1,73 @@
+cabal-version:      3.0
+name:               httzip
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+executable httzip
+    import: common-options
+
+    main-is:          Httzip.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        bytestring,
+        text,
+        warp,
+        wai,
+        http-types,
+        directory,
+        filepath,
+        unix,
+        wai-conduit,
+        conduit,
+        conduit-extra,
+        binary
diff --git a/users/Profpatsch/ical-smolify/IcalSmolify.hs b/users/Profpatsch/ical-smolify/IcalSmolify.hs
new file mode 100644
index 0000000000..77264d1693
--- /dev/null
+++ b/users/Profpatsch/ical-smolify/IcalSmolify.hs
@@ -0,0 +1,124 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Main where
+
+import qualified Data.ByteString.Lazy as Bytes.Lazy
+import qualified Data.CaseInsensitive as CaseInsensitive
+import qualified Data.Default as Default
+import qualified Data.Map.Strict as Map
+import qualified Data.Set as Set
+import ExecHelpers (dieUserError, CurrentProgramName)
+import MyPrelude
+import qualified System.Environment as Env
+import Text.ICalendar
+import Prelude hiding (log)
+
+main :: IO ()
+main = do
+  Env.getArgs >>= \case
+    [] -> dieUserError progName "First argument must be the ics file name"
+    (file : _) ->
+      do
+        parse file
+        >>= traverse_
+          ( \vcal ->
+              vcal
+                & stripSingleTimezone
+                & minify
+                & printICalendar Default.def
+                & Bytes.Lazy.putStr
+          )
+
+progName :: CurrentProgramName
+progName = "ical-smolify"
+
+log :: Error -> IO ()
+log err = do
+  putStderrLn (errorContext "ical-smolify" err & prettyError)
+
+parse :: FilePath -> IO [VCalendar]
+parse file = do
+  parseICalendarFile Default.def file >>= \case
+    Left err -> do
+      dieUserError progName [fmt|Cannot parse ical file: {err}|]
+    Right (cals, warnings) -> do
+      for_ warnings (\warn -> log [fmt|Warning: {warn}|])
+      pure cals
+
+-- | Converts a single timezone definition to the corresponding X-WR-Timezone field.
+stripSingleTimezone :: VCalendar -> VCalendar
+stripSingleTimezone vcal =
+  case vcal & vcTimeZones & Map.toList of
+    [] -> vcal
+    [(_, tz)] -> do
+      let xtz =
+            OtherProperty
+              { otherName = CaseInsensitive.mk "X-WR-TIMEZONE",
+                otherValue = tz & vtzId & tzidValue & textToBytesUtf8Lazy,
+                otherParams = OtherParams Set.empty
+              }
+      vcal
+        { vcOther =
+            vcal & vcOther
+              -- remove any existing x-wr-timezone fields
+              & Set.filter (\prop -> (prop & otherName) /= (xtz & otherName))
+              & Set.insert xtz,
+          vcTimeZones = Map.empty
+        }
+    _more -> vcal
+
+-- | Minify the vcalendar event by throwing away everything that’s not an event.
+minify :: VCalendar -> VCalendar
+minify vcal =
+  vcal
+    { vcProdId = ProdId "" (OtherParams Set.empty),
+      -- , vcVersion    :: ICalVersion
+      -- , vcScale      :: Scale
+      -- , vcMethod     :: Maybe Method
+      -- , vcOther      :: …
+      -- , vcTimeZones  :: Map Text VTimeZone
+      vcEvents = Map.map minifyEvent (vcal & vcEvents),
+      vcTodos = Map.empty,
+      vcJournals = Map.empty,
+      vcFreeBusys = Map.empty,
+      vcOtherComps = Set.empty
+    }
+
+minifyEvent :: VEvent -> VEvent
+minifyEvent vev =
+  vev
+--  { veDTStamp       :: DTStamp
+--   , veUID           :: UID
+--   , veClass         :: Class -- ^ 'def' = 'Public'
+--   , veDTStart       :: Maybe DTStart
+--   , veCreated       :: Maybe Created
+--   , veDescription   :: Maybe Description
+--   , veGeo           :: Maybe Geo
+--   , veLastMod       :: Maybe LastModified
+--   , veLocation      :: Maybe Location
+--   , veOrganizer     :: Maybe Organizer
+--   , vePriority      :: Priority -- ^ 'def' = 0
+--   , veSeq           :: Sequence -- ^ 'def' = 0
+--   , veStatus        :: Maybe EventStatus
+--   , veSummary       :: Maybe Summary
+--   , veTransp        :: TimeTransparency -- ^ 'def' = 'Opaque'
+--   , veUrl           :: Maybe URL
+--   , veRecurId       :: Maybe RecurrenceId
+--   , veRRule         :: Set RRule
+--   , veDTEndDuration :: Maybe (Either DTEnd DurationProp)
+--   , veAttach        :: Set Attachment
+--   , veAttendee      :: Set Attendee
+--   , veCategories    :: Set Categories
+--   , veComment       :: Set Comment
+--   , veContact       :: Set Contact
+--   , veExDate        :: Set ExDate
+--   , veRStatus       :: Set RequestStatus
+--   , veRelated       :: Set RelatedTo
+--   , veResources     :: Set Resources
+--   , veRDate         :: Set RDate
+--   , veAlarms        :: Set VAlarm
+--   , veOther         :: Set OtherProperty
+--   }
diff --git a/users/Profpatsch/ical-smolify/README.md b/users/Profpatsch/ical-smolify/README.md
new file mode 100644
index 0000000000..86c166d3c1
--- /dev/null
+++ b/users/Profpatsch/ical-smolify/README.md
@@ -0,0 +1,5 @@
+# ical-smolify
+
+Ensmallen an `ical` by stripping out redundant information like timezone definitions.
+
+The idea here was that after running through this preprocessor, it fits into a QR code (~2000bits) that can be scanned with your phone (for automatically adding to mobile calendar).
diff --git a/users/Profpatsch/ical-smolify/default.nix b/users/Profpatsch/ical-smolify/default.nix
new file mode 100644
index 0000000000..bf766db0e9
--- /dev/null
+++ b/users/Profpatsch/ical-smolify/default.nix
@@ -0,0 +1,23 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  ical-smolify = pkgs.writers.writeHaskell "ical-smolify"
+    {
+      libraries = [
+        pkgs.haskellPackages.iCalendar
+        depot.users.Profpatsch.my-prelude
+        depot.users.Profpatsch.execline.exec-helpers-hs
+
+      ];
+      ghcArgs = [ "-threaded" ];
+    } ./IcalSmolify.hs;
+
+in
+
+ical-smolify.overrideAttrs (old: {
+  meta = lib.recursiveUpdate old.meta or { } {
+    # Dependency iCalendar no longer builds in nixpkgs due to a lack of maintenance upstream
+    # https://github.com/nixos/nixpkgs/commit/13d10cc6e302e7d5800c6a08c1728b14c3801e26
+    ci.skip = true;
+  };
+})
diff --git a/users/Profpatsch/ical-smolify/ical-smolify.cabal b/users/Profpatsch/ical-smolify/ical-smolify.cabal
new file mode 100644
index 0000000000..d7a46c581d
--- /dev/null
+++ b/users/Profpatsch/ical-smolify/ical-smolify.cabal
@@ -0,0 +1,18 @@
+cabal-version:      3.0
+name:               ical-smolify
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+executable ical-smolify
+    main-is: IcalSmolify.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        my-prelude,
+        exec-helpers
+        data-default
+        case-insensitive
+        iCalendar
+
+    default-language: Haskell2010
diff --git a/users/Profpatsch/imap-idle.nix b/users/Profpatsch/imap-idle.nix
index 3ad5375d89..84af5d0e54 100644
--- a/users/Profpatsch/imap-idle.nix
+++ b/users/Profpatsch/imap-idle.nix
@@ -1,14 +1,17 @@
 { depot, pkgs, lib, ... }:
 
 let
-  imap-idle = depot.nix.writers.rustSimple {
-    name = "imap-idle";
-    dependencies = [
-      depot.users.Profpatsch.arglib.netencode.rust
-      depot.third_party.rust-crates.imap
-      depot.third_party.rust-crates.epoll
-      depot.users.Profpatsch.execline.exec-helpers
-    ];
-  } (builtins.readFile ./imap-idle.rs);
+  imap-idle = depot.nix.writers.rustSimple
+    {
+      name = "imap-idle";
+      dependencies = [
+        depot.users.Profpatsch.arglib.netencode.rust
+        depot.third_party.rust-crates.imap
+        depot.third_party.rust-crates.epoll
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    }
+    (builtins.readFile ./imap-idle.rs);
 
-in imap-idle
+in
+imap-idle
diff --git a/users/Profpatsch/imap-idle.rs b/users/Profpatsch/imap-idle.rs
index 9dce736d0d..937847b879 100644
--- a/users/Profpatsch/imap-idle.rs
+++ b/users/Profpatsch/imap-idle.rs
@@ -1,16 +1,16 @@
 extern crate exec_helpers;
 // extern crate arglib_netencode;
 // extern crate netencode;
-extern crate imap;
 extern crate epoll;
+extern crate imap;
 
 // use netencode::dec;
+use imap::extensions::idle::SetReadTimeout;
 use std::convert::TryFrom;
-use std::io::{Read, Write};
 use std::fs::File;
-use std::os::unix::io::{FromRawFd, AsRawFd, RawFd};
+use std::io::{Read, Write};
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use std::time::Duration;
-use imap::extensions::idle::SetReadTimeout;
 
 /// Implements an UCSPI client that wraps fd 6 & 7
 /// and implements Write and Read with a timeout.
@@ -33,7 +33,7 @@ impl UcspiClient {
                 read: File::from_raw_fd(6),
                 read_epoll_fd,
                 read_timeout: None,
-                write: File::from_raw_fd(7)
+                write: File::from_raw_fd(7),
             })
         }
     }
@@ -54,21 +54,23 @@ impl SetReadTimeout for UcspiClient {
 impl Read for UcspiClient {
     // TODO: test the epoll code with a short timeout
     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
-        const NO_DATA : u64 = 0;
+        const NO_DATA: u64 = 0;
         // in order to implement the read_timeout,
         // we use epoll to wait for either data or time out
         epoll::ctl(
             self.read_epoll_fd,
             epoll::ControlOptions::EPOLL_CTL_ADD,
             self.read.as_raw_fd(),
-            epoll::Event::new(epoll::Events::EPOLLIN, NO_DATA)
+            epoll::Event::new(epoll::Events::EPOLLIN, NO_DATA),
         )?;
         let UNUSED = epoll::Event::new(epoll::Events::EPOLLIN, NO_DATA);
         let wait = epoll::wait(
             self.read_epoll_fd,
             match self.read_timeout {
-                Some(duration) => i32::try_from(duration.as_millis()).expect("duration too big for epoll"),
-                None => -1 // infinite
+                Some(duration) => {
+                    i32::try_from(duration.as_millis()).expect("duration too big for epoll")
+                }
+                None => -1, // infinite
             },
             // event that was generated; but we don’t care
             &mut vec![UNUSED; 1][..],
@@ -79,11 +81,14 @@ impl Read for UcspiClient {
             self.read_epoll_fd,
             epoll::ControlOptions::EPOLL_CTL_DEL,
             self.read.as_raw_fd(),
-            UNUSED
+            UNUSED,
         )?;
         match wait {
             // timeout happened (0 events)
-            Ok(0) => Err(std::io::Error::new(std::io::ErrorKind::TimedOut, "ucspi read timeout")),
+            Ok(0) => Err(std::io::Error::new(
+                std::io::ErrorKind::TimedOut,
+                "ucspi read timeout",
+            )),
             // its ready for reading, we can read
             Ok(_) => self.read.read(buf),
             // error
@@ -110,18 +115,21 @@ fn main() {
     let username = std::env::var("IMAP_USERNAME").expect("username");
     let password = std::env::var("IMAP_PASSWORD").expect("password");
 
-    let net = unsafe {
-        UcspiClient::new_from_6_and_7().expect("no ucspi client for you")
-    };
+    let net = unsafe { UcspiClient::new_from_6_and_7().expect("no ucspi client for you") };
     let client = imap::Client::new(net);
-    let mut session = client.login(username, password).map_err(|(err, _)| err).expect("unable to login");
+    let mut session = client
+        .login(username, password)
+        .map_err(|(err, _)| err)
+        .expect("unable to login");
     eprintln!("{:#?}", session);
     let list = session.list(None, Some("*"));
     eprintln!("{:#?}", list);
     let mailbox = session.examine("INBOX");
     eprintln!("{:#?}", mailbox);
     fn now() -> String {
-        String::from_utf8_lossy(&std::process::Command::new("date").output().unwrap().stdout).trim_right().to_string()
+        String::from_utf8_lossy(&std::process::Command::new("date").output().unwrap().stdout)
+            .trim_right()
+            .to_string()
     }
     loop {
         eprintln!("{}: idling on INBOX", now());
diff --git a/users/Profpatsch/importDhall.nix b/users/Profpatsch/importDhall.nix
new file mode 100644
index 0000000000..1947ad1ce1
--- /dev/null
+++ b/users/Profpatsch/importDhall.nix
@@ -0,0 +1,93 @@
+{ pkgs, depot, lib, ... }:
+let
+
+  # import the dhall file as nix expression via dhall-nix.
+  # Converts the normalized dhall expression to a nix file,
+  # puts it in the store and imports it.
+  # Types are erased, functions are converted to nix functions,
+  # unions values are nix functions that take a record of match
+  # functions for their alternatives.
+  # TODO: document better
+  importDhall =
+    {
+      # Root path of the dhall file tree to import (will be filtered by files)
+      root
+    , # A list of files which should be taken from `root` (relative paths).
+      # This is for minimizing the amount of things that have to be copied to the store.
+      # TODO: can you have directory prefixes?
+      files
+    , # The path of the dhall file which should be evaluated, relative to `root`, has to be in `files`
+      main
+    , # List of dependencies (TODO: what is a dependency?)
+      deps
+    , # dhall type of `main`, or `null` if anything should be possible.
+      type ? null
+    }:
+    let
+      absRoot = path: toString root + "/" + path;
+      src =
+        depot.users.Profpatsch.exactSource
+          root
+          # exactSource wants nix paths, but I think relative paths
+          # as strings are more intuitive.
+          ([ (absRoot main) ] ++ (map absRoot files));
+
+      cache = ".cache";
+      cacheDhall = "${cache}/dhall";
+
+      hadTypeAnnot = type != null;
+      typeAnnot = lib.optionalString hadTypeAnnot ": ${type}";
+
+      convert = pkgs.runCommandLocal "dhall-to-nix" { inherit deps; } ''
+        mkdir -p ${cacheDhall}
+        for dep in $deps; do
+          ${pkgs.xorg.lndir}/bin/lndir -silent $dep/${cacheDhall} ${cacheDhall}
+        done
+
+        export XDG_CACHE_HOME=$(pwd)/${cache}
+        # go into the source directory, so that the type can import files.
+        # TODO: This is a bit of a hack hrm.
+        cd "${src}"
+        printf 'Generating dhall nix code. Run
+        %s --file %s
+        to reproduce
+        ' \
+          ${pkgs.dhall}/bin/dhall \
+          ${absRoot main}
+        ${if hadTypeAnnot then ''
+            printf '%s' ${lib.escapeShellArg "${src}/${main} ${typeAnnot}"} \
+              | ${pkgs.dhall-nix}/bin/dhall-to-nix \
+              > $out
+          ''
+          else ''
+            printf 'No type annotation given, the dhall expression type was:\n'
+            ${pkgs.dhall}/bin/dhall type --file "${src}/${main}"
+            printf '%s' ${lib.escapeShellArg "${src}/${main}"} \
+              | ${pkgs.dhall-nix}/bin/dhall-to-nix \
+              > $out
+          ''}
+
+      '';
+    in
+    import convert;
+
+
+  # read dhall file in as JSON, then import as nix expression.
+  # The dhall file must not try to import from non-local URLs!
+  readDhallFileAsJson = dhallType: file:
+    let
+      convert = pkgs.runCommandLocal "dhall-to-json" { } ''
+        printf '%s' ${lib.escapeShellArg "${file} : ${dhallType}"} \
+          | ${pkgs.dhall-json}/bin/dhall-to-json \
+          > $out
+      '';
+    in
+    builtins.fromJSON (builtins.readFile convert);
+
+in
+{
+  inherit
+    importDhall
+    readDhallFileAsJson
+    ;
+}
diff --git a/users/Profpatsch/ini/default.nix b/users/Profpatsch/ini/default.nix
new file mode 100644
index 0000000000..e1a7a1a7b6
--- /dev/null
+++ b/users/Profpatsch/ini/default.nix
@@ -0,0 +1,6 @@
+{ depot, ... }:
+{
+  externs = {
+    renderIni = depot.users.Profpatsch.toINI { };
+  };
+}
diff --git a/users/Profpatsch/ini/ini.dhall b/users/Profpatsch/ini/ini.dhall
new file mode 100644
index 0000000000..f2efbc0af4
--- /dev/null
+++ b/users/Profpatsch/ini/ini.dhall
@@ -0,0 +1,36 @@
+let lib = ../dhall/lib.dhall
+
+let NameVal = Ξ»(T : Type) β†’ { name : Text, value : T }
+
+let ValueList = Ξ»(T : Type) β†’ List (NameVal T)
+
+let Section = ValueList Text
+
+let Sections = ValueList Section
+
+let Ini = { globalSection : Section, sections : Sections }
+
+let
+    -- Takes to INI files and merges their global sections and their section lists,
+    -- without duplicating by section name.
+    appendInis =
+      Ξ»(inis : List Ini) β†’
+          { globalSection =
+              lib.List/concat
+                (NameVal Text)
+                (lib.List/map Ini Section (Ξ»(i : Ini) β†’ i.globalSection) inis)
+          , sections =
+              lib.List/concat
+                (NameVal Section)
+                (lib.List/map Ini Sections (Ξ»(i : Ini) β†’ i.sections) inis)
+          }
+        : Ini
+
+let
+    -- Signatures of functions that are input via FFI.
+    Externs =
+      { -- given a dsl of functions to create an Ini, render the ini file
+        renderIni : Ini β†’ Text
+      }
+
+in  { NameVal, ValueList, Section, Sections, Ini, appendInis, Externs }
diff --git a/users/Profpatsch/jaeger.nix b/users/Profpatsch/jaeger.nix
new file mode 100644
index 0000000000..374e40df1a
--- /dev/null
+++ b/users/Profpatsch/jaeger.nix
@@ -0,0 +1,46 @@
+{ depot, pkgs, ... }:
+let
+  drv =
+    pkgs.stdenv.mkDerivation {
+      pname = "jaeger";
+      version = "1.49.0";
+      src = pkgs.fetchurl {
+        url = "https://github.com/jaegertracing/jaeger/releases/download/v1.49.0/jaeger-1.49.0-linux-amd64.tar.gz";
+        hash = "sha256-QhxISDlk/t431EesgVkHWTe7yiw2B+yyfq//GLP0As4=";
+      };
+      phases = [ "unpackPhase" "installPhase" "fixupPhase" ];
+      installPhase = ''
+        mkdir -p $out/bin
+        install ./jaeger-all-in-one $out/bin
+      '';
+    };
+  image =
+    pkgs.dockerTools.buildImage {
+      name = "jaeger";
+      tag = "1.49.0";
+      copyToRoot = drv;
+      config = {
+        Cmd = [ "/bin/jaeger-all-in-one" ];
+      };
+
+    };
+
+  runner =
+    depot.nix.writeExecline "jaeger-docker-run" { } [
+      "if"
+      [ "docker" "load" "-i" image ]
+      "docker"
+      "run"
+      "--rm"
+      "--name"
+      "jaeger"
+      # Web UI
+      "-p"
+      "16686:16686"
+      # Opentelemetry
+      "-p"
+      "4318:4318"
+      "jaeger:1.49.0"
+    ];
+in
+runner
diff --git a/users/Profpatsch/jbovlaste-sqlite/JbovlasteSqlite.hs b/users/Profpatsch/jbovlaste-sqlite/JbovlasteSqlite.hs
new file mode 100644
index 0000000000..8dae9cd026
--- /dev/null
+++ b/users/Profpatsch/jbovlaste-sqlite/JbovlasteSqlite.hs
@@ -0,0 +1,389 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Main where
+
+import Conduit ((.|))
+import Conduit qualified as Cond
+import Control.Category qualified as Cat
+import Control.Foldl qualified as Fold
+import Data.ByteString.Internal qualified as Bytes
+import Data.Error.Tree
+import Data.Int (Int64)
+import Data.List qualified as List
+import Data.Map.Strict qualified as Map
+import Data.Maybe (catMaybes)
+import Data.Text qualified as Text
+import Data.Text.IO qualified as Text
+import Database.SQLite.Simple qualified as Sqlite
+import Database.SQLite.Simple.FromField qualified as Sqlite
+import Database.SQLite.Simple.QQ qualified as Sqlite
+import FieldParser qualified as Field
+import Label
+import Parse
+import PossehlAnalyticsPrelude
+import Text.XML (def)
+import Text.XML qualified as Xml
+import Prelude hiding (init, maybe)
+
+main :: IO ()
+main = do
+  f <- file
+  f.documentRoot
+    & filterDown
+    & toTree
+    & prettyErrorTree
+    & Text.putStrLn
+
+test :: IO ()
+test = do
+  withEnv $ \env -> do
+    migrate env
+    f <- file
+    parseJbovlasteXml f
+      & \case
+        Left errs -> Text.putStrLn $ prettyErrorTree errs
+        Right valsi -> insertValsi env valsi
+
+filterDown :: Xml.Element -> Xml.Element
+filterDown el =
+  el
+    & filterElementsRec noUsers
+    & downTo (T2 (label @"maxdepth" 5) (label @"maxlistitems" 30))
+
+data Valsi = Valsi
+  { word :: Text,
+    definition :: Text,
+    definitionId :: Natural,
+    typ :: Text,
+    selmaho :: Maybe Text,
+    notes :: Maybe Text,
+    glosswords :: [T2 "word" Text "sense" (Maybe Text)],
+    keywords :: [T3 "word" Text "place" Natural "sense" (Maybe Text)]
+  }
+  deriving stock (Show)
+
+insertValsi :: Env -> [Valsi] -> IO ()
+insertValsi env vs = do
+  Sqlite.withTransaction env.envData $
+    do
+      valsiIds <-
+        Cond.yieldMany vs
+          .| Cond.mapMC
+            ( \v ->
+                Sqlite.queryNamed
+                  @(Sqlite.Only Int64)
+                  env.envData
+                  [Sqlite.sql|
+                     INSERT INTO valsi
+                       (word , definition , type , selmaho , notes )
+                       VALUES
+                       (:word, :definition, :type, :selmaho, :notes)
+                       RETURNING (id)
+                   |]
+                  [ ":word" Sqlite.:= v.word,
+                    ":definition" Sqlite.:= v.definition,
+                    ":type" Sqlite.:= v.typ,
+                    ":selmaho" Sqlite.:= v.selmaho,
+                    ":notes" Sqlite.:= v.notes
+                  ]
+                  >>= \case
+                    [one] -> pure one
+                    _ -> error "more or less than one result"
+            )
+          .| Cond.sinkList
+          & Cond.runConduit
+      for_ (zip valsiIds vs) $ \(Sqlite.Only vId, v) -> do
+        for_ v.glosswords $ \g -> do
+          Sqlite.executeNamed
+            env.envData
+            [Sqlite.sql|
+                      INSERT INTO glosswords
+                        (valsi_id , word , sense )
+                        VALUES
+                        (:valsi_id, :word, :sense)
+                    |]
+            [ ":valsi_id" Sqlite.:= vId,
+              ":word" Sqlite.:= g.word,
+              ":sense" Sqlite.:= g.sense
+            ]
+      for_ (zip valsiIds vs) $ \(Sqlite.Only vId, v) -> do
+        for_ v.keywords $ \g -> do
+          Sqlite.executeNamed
+            env.envData
+            [Sqlite.sql|
+                      INSERT INTO keywords
+                        (valsi_id , word , place , sense )
+                        VALUES
+                        (:valsi_id, :word, :place, :sense)
+                    |]
+            [ ":valsi_id" Sqlite.:= vId,
+              ":word" Sqlite.:= g.word,
+              ":place" Sqlite.:= (g.place & fromIntegral @Natural @Int),
+              ":sense" Sqlite.:= g.sense
+            ]
+
+migrate :: (HasField "envData" p Sqlite.Connection) => p -> IO ()
+migrate env = do
+  let x q = Sqlite.execute env.envData q ()
+  x
+    [Sqlite.sql|
+      CREATE TABLE IF NOT EXISTS valsi (
+        id integer PRIMARY KEY,
+        word text NOT NULL,
+        definition text NOT NULL,
+        type text NOT NULL,
+        selmaho text NULL,
+        notes text NULL
+      )
+     |]
+  x
+    [Sqlite.sql|
+      CREATE TABLE IF NOT EXISTS glosswords (
+        id integer PRIMARY KEY,
+        valsi_id integer NOT NULL,
+        word text NOT NULL,
+        sense text NULL,
+        FOREIGN KEY(valsi_id) REFERENCES valsi(id)
+      )
+    |]
+  x
+    [Sqlite.sql|
+      CREATE TABLE IF NOT EXISTS keywords (
+        id integer PRIMARY KEY,
+        valsi_id integer NOT NULL,
+        word text NOT NULL,
+        place integer NOT NULL,
+        sense text NULL,
+        FOREIGN KEY(valsi_id) REFERENCES valsi(id)
+      )
+    |]
+
+data Env = Env
+  { envData :: Sqlite.Connection
+  }
+
+withEnv :: (Env -> IO a) -> IO a
+withEnv inner = do
+  withSqlite "./jbovlaste.sqlite" $ \envData -> inner Env {..}
+
+withSqlite :: String -> (Sqlite.Connection -> IO a) -> IO a
+withSqlite fileName inner = Sqlite.withConnection fileName $ \conn -> do
+  -- Sqlite.setTrace conn (Just (\msg -> Text.hPutStrLn IO.stderr [fmt|{fileName}: {msg}|]))
+  Sqlite.execute conn [Sqlite.sql|PRAGMA foreign_keys = ON|] ()
+  inner conn
+
+parseJbovlasteXml :: (HasField "documentRoot" r Xml.Element) => r -> Either ErrorTree [Valsi]
+parseJbovlasteXml xml =
+  xml.documentRoot
+    & runParse
+      "cannot parse jbovlaste.xml"
+      parser
+  where
+    parser =
+      (element "dictionary" <&> (.elementNodes) <&> mapMaybe nodeElementMay)
+        >>> ( find
+                ( element "direction"
+                    >>> do
+                      (attribute "from" >>> exactly showToText "lojban")
+                      *> (attribute "to" >>> exactly showToText "English")
+                      *> Cat.id
+                )
+                <&> (\x -> x.elementNodes <&> nodeElementMay)
+            )
+        >>> (multiple (maybe valsi) <&> catMaybes)
+    valsi =
+      element "valsi"
+        >>> do
+          let subNodes =
+                ( Cat.id
+                    <&> (.elementNodes)
+                    <&> mapMaybe nodeElementMay
+                )
+
+          let subElementContent elName =
+                subNodes
+                  >>> ( (find (element elName))
+                          <&> (.elementNodes)
+                      )
+                  >>> exactlyOne
+                  >>> content
+          let optionalSubElementContent elName =
+                subNodes
+                  >>> ((findAll (element elName) >>> zeroOrOne))
+                  >>> (maybe (lmap (.elementNodes) exactlyOne >>> content))
+
+          word <- attribute "word"
+          typ <- attribute "type"
+          selmaho <- optionalSubElementContent "selmaho"
+          definition <- subElementContent "definition"
+          definitionId <- subElementContent "definitionid" >>> fieldParser Field.decimalNatural
+          notes <- optionalSubElementContent "notes"
+          glosswords <-
+            (subNodes >>> findAll (element "glossword"))
+              >>> ( multiple $ do
+                      word' <- label @"word" <$> (attribute "word")
+                      sense <- label @"sense" <$> (attributeMay "sense")
+                      pure $ T2 word' sense
+                  )
+          keywords <-
+            (subNodes >>> findAll (element "keyword"))
+              >>> ( multiple $ do
+                      word' <- label @"word" <$> (attribute "word")
+                      place <- label @"place" <$> (attribute "place" >>> fieldParser Field.decimalNatural)
+                      sense <- label @"sense" <$> (attributeMay "sense")
+                      pure $ T3 word' place sense
+                  )
+
+          pure $ Valsi {..}
+
+file :: IO Xml.Document
+file = Xml.readFile def "./jbovlaste-en.xml"
+
+-- | Filter XML elements recursively based on the given predicate
+filterElementsRec :: (Xml.Element -> Bool) -> Xml.Element -> Xml.Element
+filterElementsRec f el =
+  el
+    { Xml.elementNodes =
+        mapMaybe
+          ( \case
+              Xml.NodeElement el' ->
+                if f el'
+                  then Just $ Xml.NodeElement $ filterElementsRec f el'
+                  else Nothing
+              other -> Just other
+          )
+          el.elementNodes
+    }
+
+-- | no <user> allowed
+noUsers :: Xml.Element -> Bool
+noUsers el = el.elementName.nameLocalName /= "user"
+
+downTo :: (T2 "maxdepth" Int "maxlistitems" Int) -> Xml.Element -> Xml.Element
+downTo n el =
+  if n.maxdepth > 0
+    then
+      el
+        { Xml.elementNodes =
+            ( do
+                let eleven = take (n.maxlistitems + 1) $ map down el.elementNodes
+                if List.length eleven == (n.maxlistitems + 1)
+                  then eleven <> [Xml.NodeComment "snip!"]
+                  else eleven
+            )
+        }
+    else el {Xml.elementNodes = [Xml.NodeComment "snip!"]}
+  where
+    down =
+      \case
+        Xml.NodeElement el' ->
+          Xml.NodeElement $
+            downTo
+              ( T2
+                  (label @"maxdepth" $ n.maxdepth - 1)
+                  (label @"maxlistitems" n.maxlistitems)
+              )
+              el'
+        more -> more
+
+toTree :: Xml.Element -> ErrorTree
+toTree el = do
+  case el.elementNodes & filter (not . isEmptyContent) & nonEmpty of
+    Nothing -> singleError (newError (prettyXmlElement el))
+    Just (n :| []) | not $ isElementNode n -> singleError $ errorContext (prettyXmlElement el) (nodeErrorNoElement n)
+    Just nodes -> nestedMultiError (newError (prettyXmlElement el)) (nodes <&> node)
+  where
+    isEmptyContent = \case
+      Xml.NodeContent c -> c & Text.all Bytes.isSpaceChar8
+      _ -> False
+    isElementNode = \case
+      Xml.NodeElement _ -> True
+      _ -> False
+
+    node :: Xml.Node -> ErrorTree
+    node = \case
+      Xml.NodeElement el' -> toTree el'
+      other -> singleError $ nodeErrorNoElement other
+
+    nodeErrorNoElement :: Xml.Node -> Error
+    nodeErrorNoElement = \case
+      Xml.NodeInstruction i -> [fmt|Instruction: {i & show}|]
+      Xml.NodeContent c -> [fmt|"{c & Text.replace "\"" "\\\""}"|]
+      Xml.NodeComment c -> [fmt|<!-- {c} -->|]
+      Xml.NodeElement _ -> error "NodeElement not allowed here"
+
+prettyXmlName :: Xml.Name -> Text
+prettyXmlName n = [fmt|{n.namePrefix & fromMaybe ""}{n.nameLocalName}|]
+
+prettyXmlElement :: Xml.Element -> Text
+prettyXmlElement el =
+  if not $ null el.elementAttributes
+    then [fmt|<{prettyXmlName el.elementName}: {attrs el.elementAttributes}>|]
+    else [fmt|<{prettyXmlName el.elementName}>|]
+  where
+    attrs :: Map Xml.Name Text -> Text
+    attrs a = a & Map.toList <&> (\(k, v) -> [fmt|{prettyXmlName k}={v}|]) & Text.intercalate ", " & \s -> [fmt|({s})|]
+
+nodeElementMay :: Xml.Node -> Maybe Xml.Element
+nodeElementMay = \case
+  Xml.NodeElement el -> Just el
+  _ -> Nothing
+
+element :: Text -> Parse Xml.Element Xml.Element
+element name = Parse $ \(ctx, el) ->
+  if el.elementName.nameLocalName == name
+    then Success (ctx & addContext (prettyXmlName el.elementName), el)
+    else Failure $ singleton [fmt|Expected element named <{name}> but got {el & prettyXmlElement} at {showContext ctx}|]
+
+content :: Parse Xml.Node Text
+content = Parse $ \(ctx, node) -> case node of
+  Xml.NodeContent t -> Success (ctx, t)
+  -- TODO: give an example of the node content?
+  n -> Failure $ singleton [fmt|Expected a content node, but got a {n & nodeType} node, at {showContext ctx}|]
+    where
+      nodeType = \case
+        Xml.NodeContent _ -> "content" :: Text
+        Xml.NodeComment _ -> "comment"
+        Xml.NodeInstruction _ -> "instruction"
+        Xml.NodeElement _ -> "element"
+
+attribute :: Text -> Parse Xml.Element Text
+attribute name = Parse $ \(ctx, el) ->
+  case el.elementAttributes & Map.mapKeys (.nameLocalName) & Map.lookup name of
+    Just a -> Success (ctx & addContext [fmt|{{attr:{name}}}|], a)
+    Nothing -> Failure $ singleton [fmt|Attribute "{name}" missing at {showContext ctx}|]
+
+attributeMay :: Text -> Parse Xml.Element (Maybe Text)
+attributeMay name = Parse $ \(ctx, el) ->
+  case el.elementAttributes & Map.mapKeys (.nameLocalName) & Map.lookup name of
+    Just a -> Success (ctx & addContext [fmt|{{attr:{name}}}|], Just a)
+    Nothing -> Success (ctx, Nothing)
+
+instance
+  ( Sqlite.FromField t1,
+    Sqlite.FromField t2,
+    Sqlite.FromField t3
+  ) =>
+  Sqlite.FromRow (T3 l1 t1 l2 t2 l3 t3)
+  where
+  fromRow = do
+    T3
+      <$> (label @l1 <$> Sqlite.field)
+      <*> (label @l2 <$> Sqlite.field)
+      <*> (label @l3 <$> Sqlite.field)
+
+foldRows ::
+  forall row params b.
+  (Sqlite.FromRow row, Sqlite.ToRow params) =>
+  Sqlite.Connection ->
+  Sqlite.Query ->
+  params ->
+  Fold.Fold row b ->
+  IO b
+foldRows conn qry params = Fold.purely f
+  where
+    f :: forall x. (x -> row -> x) -> x -> (x -> b) -> IO b
+    f acc init extract = do
+      x <- Sqlite.fold conn qry params init (\a r -> pure $ acc a r)
+      pure $ extract x
diff --git a/users/Profpatsch/jbovlaste-sqlite/default.nix b/users/Profpatsch/jbovlaste-sqlite/default.nix
new file mode 100644
index 0000000000..ea59fdec39
--- /dev/null
+++ b/users/Profpatsch/jbovlaste-sqlite/default.nix
@@ -0,0 +1,33 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  #   bins = depot.nix.getBins pkgs.sqlite ["sqlite3"];
+
+  jbovlaste-sqlite = pkgs.haskellPackages.mkDerivation {
+    pname = "jbovlaste-sqlite";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./jbovlaste-sqlite.cabal
+      ./JbovlasteSqlite.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.pa-field-parser
+      depot.users.Profpatsch.my-prelude
+      pkgs.haskellPackages.foldl
+      pkgs.haskellPackages.sqlite-simple
+      pkgs.haskellPackages.xml-conduit
+
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+in
+jbovlaste-sqlite
diff --git a/users/Profpatsch/jbovlaste-sqlite/jbovlaste-sqlite.cabal b/users/Profpatsch/jbovlaste-sqlite/jbovlaste-sqlite.cabal
new file mode 100644
index 0000000000..f677615a16
--- /dev/null
+++ b/users/Profpatsch/jbovlaste-sqlite/jbovlaste-sqlite.cabal
@@ -0,0 +1,76 @@
+cabal-version:      3.0
+name:               jbovlaste-sqlite
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+executable jbovlaste-sqlite
+    import: common-options
+
+    main-is:          JbovlasteSqlite.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        pa-label,
+        pa-error-tree,
+        pa-field-parser,
+        my-prelude,
+        containers,
+        selective,
+        semigroupoids,
+        validation-selective,
+        sqlite-simple,
+        foldl,
+        conduit,
+        bytestring,
+        text,
+        sqlite-simple,
+        xml-conduit,
diff --git a/users/Profpatsch/lens.nix b/users/Profpatsch/lens.nix
index 58d9c27f52..28f7506bdd 100644
--- a/users/Profpatsch/lens.nix
+++ b/users/Profpatsch/lens.nix
@@ -32,7 +32,7 @@ let
     inherit fst snd;
   };
 
-  swap = {fst, snd}: {
+  swap = { fst, snd }: {
     fst = snd;
     snd = fst;
   };
@@ -71,7 +71,7 @@ let
   lensP = strong: to: pab:
     strong.dimap
       to
-      ({fst,snd}: snd fst)
+      ({ fst, snd }: snd fst)
       (strong.firstP pab);
 
   # first element of a tuple
@@ -112,7 +112,8 @@ let
       (map (accessor: accessor profunctorSubclass) accessors);
 
 
-in {
+in
+{
   inherit
     id
     _
diff --git a/users/Profpatsch/lib.nix b/users/Profpatsch/lib.nix
index e3d59b7d8b..879d87755d 100644
--- a/users/Profpatsch/lib.nix
+++ b/users/Profpatsch/lib.nix
@@ -1,34 +1,49 @@
 { depot, pkgs, ... }:
 let
   bins = depot.nix.getBins pkgs.coreutils [ "printf" "echo" "cat" "printenv" "tee" ]
-      // depot.nix.getBins pkgs.bash [ "bash" ]
-      // depot.nix.getBins pkgs.fdtools [ "multitee" ]
-      ;
+    // depot.nix.getBins pkgs.bash [ "bash" ]
+    // depot.nix.getBins pkgs.fdtools [ "multitee" ]
+  ;
 
   # Print `msg` and and argv to stderr, then execute into argv
-  debugExec = msg: depot.nix.writeExecline "debug-exec" {} [
-    "if" [
-      "fdmove" "-c" "1" "2"
-      "if" [ bins.printf "%s: " msg ]
-      "if" [ bins.echo "$@" ]
+  debugExec = msg: depot.nix.writeExecline "debug-exec" { } [
+    "if"
+    [
+      "fdmove"
+      "-c"
+      "1"
+      "2"
+      "if"
+      [ bins.printf "%s: " msg ]
+      "if"
+      [ bins.echo "$@" ]
     ]
     "$@"
   ];
 
   # Print stdin to stderr and stdout
-  eprint-stdin = depot.nix.writeExecline "eprint-stdin" {} [
-    "pipeline" [ bins.multitee "0-1,2" ] "$@"
+  eprint-stdin = depot.nix.writeExecline "eprint-stdin" { } [
+    "pipeline"
+    [ bins.multitee "0-1,2" ]
+    "$@"
   ];
 
   # Assume the input on stdin is netencode, pretty print it to stderr and forward it to stdout
-  eprint-stdin-netencode = depot.nix.writeExecline "eprint-stdin-netencode" {} [
-    "pipeline" [
+  eprint-stdin-netencode = depot.nix.writeExecline "eprint-stdin-netencode" { } [
+    "pipeline"
+    [
       # move stdout to 3
-      "fdmove" "3" "1"
+      "fdmove"
+      "3"
+      "1"
       # the multitee copies stdin to 1 (the other pipeline end) and 3 (the stdout of the outer pipeline block)
-      "pipeline" [ bins.multitee "0-1,3" ]
+      "pipeline"
+      [ bins.multitee "0-1,3" ]
       # make stderr the stdout of pretty, merging with the stderr of pretty
-      "fdmove" "-c" "1" "2"
+      "fdmove"
+      "-c"
+      "1"
+      "2"
       depot.users.Profpatsch.netencode.pretty
     ]
     "$@"
@@ -36,9 +51,11 @@ let
 
   # print the given environment variable in $1 to stderr, then execute into the rest of argv
   eprintenv = depot.nix.writeExecline "eprintenv" { readNArgs = 1; } [
-    "ifelse" [ "fdmove" "-c" "1" "2" bins.printenv "$1" ]
+    "ifelse"
+    [ "fdmove" "-c" "1" "2" bins.printenv "$1" ]
     [ "$@" ]
-    "if" [ depot.tools.eprintf "eprintenv: could not find \"\${1}\" in the environment\n" ]
+    "if"
+    [ depot.tools.eprintf "eprintenv: could not find \"\${1}\" in the environment\n" ]
     "$@"
   ];
 
@@ -52,24 +69,34 @@ let
   #   stdout: foo\n
   #   stderr: foo\nbar\n
   split-stdin = depot.nix.writeExecline "split-stdin" { argMode = "env"; } [
-    "pipeline" [
+    "pipeline"
+    [
       # this is horrible yes but the quickest way I knew how to implement it
-      "runblock" "1" bins.bash "-c" ''${bins.tee} >("$@")'' "bash-split-stdin"
+      "runblock"
+      "1"
+      bins.bash
+      "-c"
+      ''${bins.tee} >("$@")''
+      "bash-split-stdin"
     ]
-    "runblock" "-r" "1"
+    "runblock"
+    "-r"
+    "1"
   ];
 
   # remove everything but a few selected environment variables
   runInEmptyEnv = keepVars:
     let
-        importas = pkgs.lib.concatMap (var: [ "importas" "-i" var var ]) keepVars;
-        # we have to explicitely call export here, because PATH is probably empty
-        export = pkgs.lib.concatMap (var: [ "${pkgs.execline}/bin/export" var ''''${${var}}'' ]) keepVars;
-    in depot.nix.writeExecline "empty-env" {}
-         (importas ++ [ "emptyenv" ] ++ export ++ [ "${pkgs.execline}/bin/exec" "$@" ]);
+      importas = pkgs.lib.concatMap (var: [ "importas" "-i" var var ]) keepVars;
+      # we have to explicitely call export here, because PATH is probably empty
+      export = pkgs.lib.concatMap (var: [ "${pkgs.execline}/bin/export" var ''''${${var}}'' ]) keepVars;
+    in
+    depot.nix.writeExecline "empty-env" { }
+      (importas ++ [ "emptyenv" ] ++ export ++ [ "${pkgs.execline}/bin/exec" "$@" ]);
 
 
-in {
+in
+{
   inherit
     debugExec
     eprint-stdin
diff --git a/users/Profpatsch/lorri-wait-for-eval/LorriWaitForEval.hs b/users/Profpatsch/lorri-wait-for-eval/LorriWaitForEval.hs
new file mode 100644
index 0000000000..a1a4586401
--- /dev/null
+++ b/users/Profpatsch/lorri-wait-for-eval/LorriWaitForEval.hs
@@ -0,0 +1,173 @@
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE NumericUnderscores #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Main where
+
+import Conduit
+import Conduit qualified as Cond
+import Control.Concurrent
+import Control.Concurrent.Async qualified as Async
+import Control.Monad
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Bifunctor
+import Data.Conduit.Binary qualified as Conduit.Binary
+import Data.Conduit.Combinators qualified as Cond
+import Data.Conduit.Process
+import Data.Error
+import Data.Function
+import Data.Functor
+import Data.Text.IO (hPutStrLn)
+import MyPrelude
+import System.Directory qualified as Dir
+import System.Environment qualified as Env
+import System.Exit qualified as Exit
+import System.FilePath (takeDirectory)
+import System.FilePath.Posix qualified as FilePath
+import System.IO (stderr)
+import System.Posix qualified as Posix
+import Prelude hiding (log)
+
+data LorriEvent = LorriEvent
+  { nixFile :: Text,
+    eventType :: LorriEventType
+  }
+  deriving stock (Show)
+
+data LorriEventType
+  = Completed
+  | Started
+  | EvalFailure
+  deriving stock (Show)
+
+main :: IO ()
+main = do
+  argv <- Env.getArgs <&> nonEmpty
+
+  dir <- Dir.getCurrentDirectory
+  shellNix <-
+    findShellNix dir >>= \case
+      Nothing -> Exit.die [fmt|could not find any shell.nix in or above the directory {dir}|]
+      Just s -> pure s
+  getEventChan :: MVar (Chan LorriEvent) <- newEmptyMVar
+  Async.race_
+    ( do
+        sendEventChan :: Chan LorriEvent <- newChan
+        (exitCode, ()) <-
+          sourceProcessWithConsumer
+            (proc "lorri" ["internal", "stream-events"])
+            $
+            -- first, we want to send a message over the chan that the process is running (for timeout)
+            liftIO (putMVar getEventChan sendEventChan)
+              *> Conduit.Binary.lines
+              .| Cond.mapC
+                ( \jsonBytes ->
+                    (jsonBytes :: ByteString)
+                      & Json.parseStrict
+                        ( Json.key
+                            "Completed"
+                            ( do
+                                nixFile <- Json.key "nix_file" Json.asText
+                                pure LorriEvent {nixFile, eventType = Completed}
+                            )
+                            Json.<|> Json.key
+                              "Started"
+                              ( do
+                                  nixFile <- Json.key "nix_file" Json.asText
+                                  pure LorriEvent {nixFile, eventType = Started}
+                              )
+                            Json.<|> Json.key
+                              "Failure"
+                              ( do
+                                  nixFile <- Json.key "nix_file" Json.asText
+                                  pure LorriEvent {nixFile, eventType = EvalFailure}
+                              )
+                        )
+                      & first Json.displayError'
+                      & first (map newError)
+                      & first (smushErrors [fmt|Cannot parse line returned by lorri: {jsonBytes & bytesToTextUtf8Lenient}|])
+                      & unwrapError
+                )
+              .| (Cond.mapM_ (\ev -> writeChan sendEventChan ev))
+
+        log [fmt|lorri internal stream-events exited {show exitCode}|]
+    )
+    ( do
+        let waitMs ms = threadDelay (ms * 1000)
+
+        -- log [fmt|Waiting for lorri event for {shellNix}|]
+
+        eventChan <- takeMVar getEventChan
+
+        let isOurEvent ev = FilePath.normalise (ev & nixFile & textToString) == FilePath.normalise shellNix
+
+        let handleEvent ev =
+              case ev & eventType of
+                Started ->
+                  log [fmt|waiting for lorri build to finish|]
+                Completed -> do
+                  log [fmt|build completed|]
+                  exec (inDirenvDir (takeDirectory shellNix) <$> argv)
+                EvalFailure -> do
+                  log [fmt|evaluation failed! for path {ev & nixFile}|]
+                  Exit.exitWith (Exit.ExitFailure 111)
+
+        -- wait for 100ms for the first message from lorri,
+        -- or else assume lorri is not building the project yet
+        Async.race
+          (waitMs 100)
+          ( do
+              -- find the first event that we can use
+              let go = do
+                    ev <- readChan eventChan
+                    if isOurEvent ev then pure ev else go
+              go
+          )
+          >>= \case
+            Left () -> do
+              log [fmt|No event received from lorri, assuming this is the first evaluation|]
+              exec argv
+            Right ch -> handleEvent ch
+
+        runConduit $
+          repeatMC (readChan eventChan)
+            .| filterC isOurEvent
+            .| mapM_C handleEvent
+    )
+  where
+    inDirenvDir dir' argv' = ("direnv" :| ["exec", dir']) <> argv'
+    exec = \case
+      Just (exe :| args') -> Posix.executeFile exe True args' Nothing
+      Nothing -> Exit.exitSuccess
+
+log :: Text -> IO ()
+log msg = hPutStrLn stderr [fmt|lorri-wait-for-eval: {msg}|]
+
+-- | Searches from the current directory upwards, until it finds the `shell.nix`.
+findShellNix :: FilePath -> IO (Maybe FilePath)
+findShellNix curDir = do
+  let go :: (FilePath -> IO (Maybe FilePath))
+      go dir = do
+        let file = dir FilePath.</> "shell.nix"
+        Dir.doesFileExist file >>= \case
+          True -> pure (Just file)
+          False -> do
+            let parent = FilePath.takeDirectory dir
+            if parent == dir
+              then pure Nothing
+              else go parent
+  go (FilePath.normalise curDir)
+
+smushErrors :: Foldable t => Text -> t Error -> Error
+smushErrors msg errs =
+  errs
+    -- hrm, pretty printing and then creating a new error is kinda shady
+    & foldMap (\err -> "\n- " <> prettyError err)
+    & newError
+    & errorContext msg
diff --git a/users/Profpatsch/lorri-wait-for-eval/README.md b/users/Profpatsch/lorri-wait-for-eval/README.md
new file mode 100644
index 0000000000..9c5d8ef9e3
--- /dev/null
+++ b/users/Profpatsch/lorri-wait-for-eval/README.md
@@ -0,0 +1,7 @@
+# lorri-wait-for-eval
+
+A helper script for [lorri](https://github.com/nix-community/lorri), which wraps a command and executes it once lorri is finished evaluating the current `shell.nix`, and uses the new environment.
+
+This is useful when you need the new shell environment to be in scope of the command, but don’t want to waste time waiting for it to finish.
+
+This should really be a feature of lorri, but I couldn’t be assed to touch rust :P
diff --git a/users/Profpatsch/lorri-wait-for-eval/default.nix b/users/Profpatsch/lorri-wait-for-eval/default.nix
new file mode 100644
index 0000000000..90c6365fed
--- /dev/null
+++ b/users/Profpatsch/lorri-wait-for-eval/default.nix
@@ -0,0 +1,20 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  lorri-wait-for-eval = pkgs.writers.writeHaskell "lorri-wait-for-eval"
+    {
+      libraries = [
+        depot.users.Profpatsch.my-prelude
+        pkgs.haskellPackages.async
+        pkgs.haskellPackages.aeson-better-errors
+        pkgs.haskellPackages.conduit-extra
+        pkgs.haskellPackages.error
+        pkgs.haskellPackages.PyF
+        pkgs.haskellPackages.unliftio
+      ];
+      ghcArgs = [ "-threaded" ];
+
+    } ./LorriWaitForEval.hs;
+
+in
+lorri-wait-for-eval
diff --git a/users/Profpatsch/mailbox-org/MailboxOrg.hs b/users/Profpatsch/mailbox-org/MailboxOrg.hs
new file mode 100644
index 0000000000..6c5820080c
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/MailboxOrg.hs
@@ -0,0 +1,523 @@
+{-# LANGUAGE ApplicativeDo #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DerivingVia #-}
+{-# LANGUAGE GHC2021 #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE NoFieldSelectors #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Main where
+
+import Aeson (parseErrorTree)
+import AesonQQ (aesonQQ)
+import ArglibNetencode
+import Control.Exception (try)
+import Control.Monad (replicateM)
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.ByteString qualified as ByteString
+import Data.ByteString.Lazy qualified as Lazy
+import Data.Char qualified as Char
+import "pa-error-tree" Data.Error.Tree
+import Data.Functor.Compose
+import Data.List qualified as List
+import Data.Map.Strict qualified as Map
+import Data.Text qualified as Text
+import ExecHelpers
+import Label
+import Netencode qualified
+import Netencode.Parse qualified as NetParse
+import Network.HTTP.Conduit qualified as Client
+import Network.HTTP.Simple qualified as Client
+import PossehlAnalyticsPrelude
+import Pretty
+import System.Directory qualified as File
+import System.Environment qualified as Env
+import System.Exit (ExitCode (ExitFailure, ExitSuccess))
+import System.Exit qualified as Exit
+import System.FilePath ((</>))
+import System.Process.Typed qualified as Process
+import System.Random qualified as Random
+import System.Random.Stateful qualified as Random
+import Prelude hiding (log)
+
+secret :: Tools -> IO (T2 "email" ByteString "password" ByteString)
+secret tools = do
+  T2
+    (label @"email" "mail@profpatsch.de")
+    <$> (label @"password" <$> fromPass "email/mailbox.org")
+  where
+    fromPass name =
+      tools.pass & runToolExpect0 [name]
+
+progName :: CurrentProgramName
+progName = "mailbox-org"
+
+log :: Error -> IO ()
+log err = do
+  putStderrLn (errorContext progName.unCurrentProgramName err & prettyError)
+
+data Tools = Tools
+  { pass :: Tool
+  }
+  deriving stock (Show)
+
+newtype Tool = Tool {unTool :: FilePath}
+  deriving stock (Show)
+
+parseTools :: Applicative m => (Text -> m (Either Error Tool)) -> m (Either ErrorTree Tools)
+parseTools getTool = do
+  let parser =
+        ( do
+            pass <- get "pass"
+            pure Tools {..}
+        )
+  parser & finalize
+  where
+    get name = name & getTool <&> eitherToListValidation & Compose
+    finalize p =
+      p.getCompose
+        <&> first (errorTree "Error reading tools")
+        <&> validationToEither
+
+main :: IO ()
+main =
+  arglibNetencode progName Nothing
+    >>= parseToolsArglib
+    >>= secret
+    >>= run applyFilters
+
+run ::
+  ( HasField "email" dat ByteString,
+    HasField "password" dat ByteString
+  ) =>
+  (Session -> IO ()) ->
+  dat ->
+  IO ()
+run act loginData = do
+  session <- login loginData
+  act session
+
+listFilterConfig :: Session -> IO ()
+listFilterConfig session = do
+  mailfilter
+    session
+    "config"
+    mempty
+    (Json.key "data" Json.asObject)
+    ()
+    >>= printPretty
+
+applyFilterRule ::
+  (HasField "folderId" dat Text) =>
+  dat ->
+  Session ->
+  IO ()
+applyFilterRule dat session = do
+  mailfilter
+    session
+    "apply"
+    ( T2
+        (label @"extraQueryParams" [("folderId", Just (dat.folderId & textToBytesUtf8))])
+        mempty
+    )
+    (Json.key "data" Json.asArray >> pure ())
+    (Json.Object mempty)
+
+data FilterRule = FilterRule
+  { actioncmds :: NonEmpty Json.Object,
+    test :: NonEmpty Json.Object
+  }
+
+data MailfilterList = MailfilterList
+  { id_ :: Json.Value,
+    rulename :: Text
+  }
+  deriving stock (Show, Eq)
+
+simpleRule ::
+  ( HasField "rulename" r Text,
+    HasField "id" r Natural,
+    HasField "emailContains" r Text,
+    HasField "subjectStartsWith" r Text
+  ) =>
+  r ->
+  Json.Value
+simpleRule dat = do
+  [aesonQQ|{
+    "id": |dat.id & enc @Natural|,
+    "position": 3,
+    "rulename": |dat.rulename & enc @Text|,
+    "active": true,
+    "flags": [],
+    "test": {
+      "id": "allof",
+      "tests": [
+        {
+          "id": "from",
+          "comparison": "contains",
+          "values": [
+            |dat.emailContains & enc @Text|
+          ]
+        },
+        {
+          "id": "subject",
+          "comparison": "startswith",
+          "values": [
+            |dat.subjectStartsWith & enc @Text|
+          ]
+        }
+      ]
+    },
+    "actioncmds": [
+      {
+        "id": "move",
+        "into": "default0/Archive"
+      },
+      {
+        "id": "stop"
+      }
+    ]
+  }|]
+  where
+    enc :: forall a. Json.ToJSON a => a -> Lazy.ByteString
+    enc val = val & Json.toJSON & Json.encode
+
+applyFilters :: Session -> IO ()
+applyFilters session = do
+  filters <-
+    mailfilter
+      session
+      "list"
+      mempty
+      ( Json.key "data" $ do
+          ( Json.eachInArray $ asDat @"mailfilter" $ do
+              id_ <- Json.key "id" Json.asValue
+              rulename <- Json.key "rulename" Json.asText
+              pure MailfilterList {..}
+            )
+            <&> mapFromListOn (\dat -> getLabel @"rulename" dat.parsed)
+      )
+      ([] :: [()])
+  let goal = Map.fromList [(label @"rulename" "another", 32 :: Integer), (label @"rulename" "xyz", 23)]
+  let actions = declarativeUpdate goal filters
+  log [fmt|To * create: {actions.toCreate & Map.keys & show}, * update: {actions.toUpdate & Map.keys & show}, * delete: {actions.toDelete & Map.keys & show}|]
+
+-- where
+-- filters
+--   & Map.elems
+--   & traverse_
+--     ( updateIfDifferent
+--         session
+--         ( \el ->
+--             pure $
+--               el.original.mailfilter
+--                 & KeyMap.insert "active" (Json.Bool False)
+--         )
+--         (pure ())
+--     )
+
+-- updateIfDifferent ::
+--   forall label parsed.
+--   ( HasField "id_" parsed Json.Value,
+--     HasField "rulename" parsed Text
+--   ) =>
+--   Session ->
+--   (Dat label Json.Object parsed -> IO Json.Object) ->
+--   Json.Parse Error () ->
+--   Dat label Json.Object parsed ->
+--   IO ()
+-- updateIfDifferent session switcheroo parser dat = do
+--   new <- switcheroo dat
+--   if new /= getField @label dat.original
+--     then do
+--       log [fmt|Updating filter "{dat.parsed.rulename}" (id {dat.parsed.id_ & show @Json.Value})|]
+--       mailfilter
+--         session
+--         "update"
+--         mempty
+--         parser
+--         new
+--     else do
+--       log [fmt|Skipping updating filter "{dat.parsed.rulename}" (id {dat.parsed.id_ & show @Json.Value}) because nothing changed.|]
+
+-- | https://oxpedia.org/wiki/index.php?title=HTTP_API_MailFilter
+mailfilter ::
+  ( Json.ToJSON a,
+    Show b
+  ) =>
+  Session ->
+  ByteString ->
+  T2
+    "extraQueryParams"
+    Client.Query
+    "httpMethod"
+    (Maybe ByteString) ->
+  Json.Parse Error b ->
+  a ->
+  IO b
+mailfilter session action opts parser body = do
+  req <-
+    Client.parseRequest "https://office.mailbox.org/appsuite/api/mailfilter/v2"
+      <&> Client.setQueryString
+        ( [ ("action", Just action),
+            ("colums", Just "1")
+          ]
+            <> opts.extraQueryParams
+        )
+      <&> Client.setRequestMethod (opts.httpMethod & fromMaybe "PUT")
+      <&> Client.setRequestBodyJSON body
+      <&> addSession session
+  req
+    & httpJSON [fmt|Cannot parse result for {req & prettyRequestShort}|] parser
+    >>= okOrDie
+    -- >>= (\resp -> printPretty resp >> pure resp)
+    <&> Client.responseBody
+  where
+    prettyRequestShort :: Client.Request -> Text
+    prettyRequestShort req = [fmt|request {req & Client.method}: {req & Client.host}{req & Client.path}{req & Client.queryString}|]
+
+-- | Given a goal and the actual state, return which elements to delete, update and create.
+declarativeUpdate ::
+  Ord k =>
+  -- | goal map
+  Map k a ->
+  -- | actual map
+  Map k b ->
+  T3
+    "toCreate"
+    (Map k a)
+    "toDelete"
+    (Map k b)
+    "toUpdate"
+    (Map k a)
+declarativeUpdate goal actual =
+  T3
+    (label @"toCreate" $ goal `Map.difference` actual)
+    (label @"toDelete" $ actual `Map.difference` goal)
+    (label @"toUpdate" $ goal `Map.intersection` actual)
+
+newtype Session = Session Client.CookieJar
+
+httpJSON ::
+  Error ->
+  Json.Parse Error b ->
+  Client.Request ->
+  IO (Client.Response b)
+httpJSON errMsg parser req = do
+  req
+    & Client.httpJSON @_ @Json.Value
+    >>= traverse
+      ( \val -> do
+          case val of
+            Json.Object obj
+              | "error" `KeyMap.member` obj
+                  && "error_desc" `KeyMap.member` obj -> do
+                  printPretty obj
+                  diePanic' "Server returned above inline error"
+            _ -> pure ()
+          val & Json.parseValue parser & \case
+            Left errs ->
+              errs
+                & parseErrorTree errMsg
+                & diePanic'
+            Right a -> pure a
+      )
+
+data Dat label orig parsed = Dat
+  { original :: Label label orig,
+    parsed :: parsed
+  }
+  deriving stock (Show, Eq)
+
+asDat ::
+  forall label err m a.
+  Monad m =>
+  Json.ParseT err m a ->
+  Json.ParseT err m (Dat label Json.Object a)
+asDat parser = do
+  original <- label @label <$> Json.asObject
+  parsed <- parser
+  pure Dat {..}
+
+addSession :: Session -> Client.Request -> Client.Request
+addSession (Session jar) req = do
+  let sessionId =
+        jar
+          & Client.destroyCookieJar
+          & List.find (\c -> "open-xchange-session-" `ByteString.isPrefixOf` c.cookie_name)
+          & annotate "The cookie jar did not contain an open-exchange-session-*"
+          & unwrapError
+          & (.cookie_value)
+
+  let req' = req & Client.addToRequestQueryString [("session", Just sessionId)]
+  req' {Client.cookieJar = Just jar}
+
+-- | Log into the mailbox.org service, and return the session secret cookies.
+login :: (HasField "email" dat ByteString, HasField "password" dat ByteString) => dat -> IO Session
+login dat = do
+  rnd <- randomString
+  req <-
+    Client.parseRequest "https://office.mailbox.org/ajax/login"
+      <&> Client.setQueryString
+        [ ("action", Just "formlogin"),
+          ("authId", Just $ ("mbo-" <> rnd) & stringToText & textToBytesUtf8)
+        ]
+      <&> Client.urlEncodedBody
+        [ ("version", "Form+Login"),
+          ("autologin", "true"),
+          ("client", "open-xchange-appsuite"),
+          ("uiWebPath", "/appsuite/"),
+          ("login", dat.email),
+          ("password", dat.password)
+        ]
+  Client.httpNoBody req
+    >>= okOrDie
+    <&> Client.responseCookieJar
+    <&> Session
+  where
+    -- For some reason they want the client to pass a random string
+    -- which is used for the session?β€½!?
+    randomString = do
+      gen <- Random.newIOGenM =<< Random.newStdGen
+      let chars = ['a' .. 'z'] <> ['A' .. 'Z'] <> ['0' .. '9']
+      let len = 11
+      Random.uniformRM (0, List.length chars - 1) gen
+        & replicateM len
+        <&> map (\index -> chars !! index)
+
+okOrDie :: Show a => Client.Response a -> IO (Client.Response a)
+okOrDie resp =
+  case resp & Client.getResponseStatusCode of
+    200 -> pure resp
+    _ -> do
+      printPretty resp
+      diePanic' "non-200 result"
+
+diePanic' :: ErrorTree -> IO a
+diePanic' errs = errs & prettyErrorTree & diePanic progName
+
+-- | Parse the tools from the given arglib input, and check that the executables exist
+parseToolsArglib :: Netencode.T -> IO Tools
+parseToolsArglib t = do
+  let oneTool name =
+        NetParse.asText
+          <&> textToString
+          <&> ( \path ->
+                  path
+                    & File.getPermissions
+                    <&> File.executable
+                    <&> ( \case
+                            False -> Left $ [fmt|Tool "{name}" is not an executable|]
+                            True -> Right (Tool path)
+                        )
+              )
+  let allTools =
+        parseTools (\name -> Compose $ NetParse.key name >>> oneTool name)
+          & getCompose
+  t
+    & NetParse.runParse
+      "test"
+      -- TODO: a proper ParseT for netencode values
+      ( NetParse.asRecord
+          >>> NetParse.key "BINS"
+          >>> NetParse.asRecord
+          >>> allTools
+      )
+    & orDo diePanic'
+    & join @IO
+    >>= orDo (\errs -> errs & diePanic')
+
+-- | Just assume the tools exist by name in the environment.
+parseToolsToolname :: IO Tools
+parseToolsToolname =
+  parseTools
+    ( \name ->
+        checkInPath name <&> \case
+          False -> Left [fmt|"Cannot find "{name}" in PATH|]
+          True -> Right $ Tool (name & textToString)
+    )
+    >>= orDo diePanic'
+
+checkInPath :: Text -> IO Bool
+checkInPath name = do
+  Env.lookupEnv "PATH"
+    <&> annotate "No PATH set"
+    >>= orDo diePanic'
+    <&> stringToText
+    <&> Text.split (== ':')
+    <&> filter (/= "")
+    >>= traverse
+      ( \p ->
+          File.getPermissions ((textToString p) </> (textToString name))
+            <&> File.executable
+            & try @IOError
+            >>= \case
+              Left _ioError -> pure False
+              Right isExe -> pure isExe
+      )
+    <&> or
+
+orDo :: Applicative f => (t -> f a) -> Either t a -> f a
+orDo f = \case
+  Left e -> f e
+  Right a -> pure a
+
+runTool :: [Text] -> Tool -> IO (Exit.ExitCode, ByteString)
+runTool args tool = do
+  let bashArgs = prettyArgsForBash ((tool.unTool & stringToText) : args)
+  log [fmt|Running: $ {bashArgs}|]
+  Process.proc
+    tool.unTool
+    (args <&> textToString)
+    & Process.readProcessStdout
+    <&> second toStrictBytes
+    <&> second stripWhitespaceFromEnd
+
+-- | Like `runCommandExpect0`, run the given tool, given a tool accessor.
+runToolExpect0 :: [Text] -> Tool -> IO ByteString
+runToolExpect0 args tool =
+  tool & runTool args >>= \(ex, stdout) -> do
+    checkStatus0 tool.unTool ex
+    pure stdout
+
+-- | Check whether a command exited 0 or crash.
+checkStatus0 :: FilePath -> ExitCode -> IO ()
+checkStatus0 executable = \case
+  ExitSuccess -> pure ()
+  ExitFailure status -> do
+    diePanic' [fmt|Command `{executable}` did not exit with status 0 (success), but status {status}|]
+
+stripWhitespaceFromEnd :: ByteString -> ByteString
+stripWhitespaceFromEnd = ByteString.reverse . ByteString.dropWhile (\w -> w == charToWordUnsafe '\n') . ByteString.reverse
+
+-- | Pretty print a command line in a way that can be copied to bash.
+prettyArgsForBash :: [Text] -> Text
+prettyArgsForBash = Text.intercalate " " . map simpleBashEscape
+
+-- | Simple escaping for bash words. If they contain anything that’s not ascii chars
+-- and a bunch of often-used special characters, put the word in single quotes.
+simpleBashEscape :: Text -> Text
+simpleBashEscape t = do
+  case Text.find (not . isSimple) t of
+    Just _ -> escapeSingleQuote t
+    Nothing -> t
+  where
+    -- any word that is just ascii characters is simple (no spaces or control characters)
+    -- or contains a few often-used characters like - or .
+    isSimple c =
+      Char.isAsciiLower c
+        || Char.isAsciiUpper c
+        || Char.isDigit c
+        -- These are benign, bash will not interpret them as special characters.
+        || List.elem c ['-', '.', ':', '/']
+    -- Put the word in single quotes
+    -- If there is a single quote in the word,
+    -- close the single quoted word, add a single quote, open the word again
+    escapeSingleQuote t' = "'" <> Text.replace "'" "'\\''" t' <> "'"
diff --git a/users/Profpatsch/mailbox-org/README.md b/users/Profpatsch/mailbox-org/README.md
new file mode 100644
index 0000000000..b84e7b59c1
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/README.md
@@ -0,0 +1,7 @@
+# mailbox-org
+
+Interfacing with the API of [https://mailbox.org/]().
+
+They use [open-xchange](https://www.open-xchange.com/resources/oxpedia) as their App Suite, so we have to work with/reverse engineer their weird API.
+
+Intended so I have a way of uploading Sieve rules into their system semi-automatically.
diff --git a/users/Profpatsch/mailbox-org/default.nix b/users/Profpatsch/mailbox-org/default.nix
new file mode 100644
index 0000000000..73bd28292d
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/default.nix
@@ -0,0 +1,38 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  mailbox-org = pkgs.haskellPackages.mkDerivation {
+    pname = "mailbox-org";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./mailbox-org.cabal
+      ./src/AesonQQ.hs
+      ./MailboxOrg.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-prelude
+      depot.users.Profpatsch.execline.exec-helpers-hs
+      depot.users.Profpatsch.arglib.netencode.haskell
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.aeson
+      pkgs.haskellPackages.http-conduit
+      pkgs.haskellPackages.aeson-better-errors
+    ];
+
+    isLibrary = false;
+    isExecutable = true;
+    license = lib.licenses.mit;
+  };
+
+
+in
+lib.pipe mailbox-org [
+  (x: (depot.nix.getBins x [ "mailbox-org" ]).mailbox-org)
+  (depot.users.Profpatsch.arglib.netencode.with-args "mailbox-org" {
+    BINS = depot.nix.getBins pkgs.dovecot_pigeonhole [ "sieve-test" ];
+  })
+]
diff --git a/users/Profpatsch/mailbox-org/mailbox-org.cabal b/users/Profpatsch/mailbox-org/mailbox-org.cabal
new file mode 100644
index 0000000000..a1b041447b
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/mailbox-org.cabal
@@ -0,0 +1,95 @@
+cabal-version:      3.0
+name:               mailbox-org
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+
+    hs-source-dirs: src
+
+    exposed-modules:
+        AesonQQ
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        aeson,
+        PyF,
+        template-haskell
+
+
+
+executable mailbox-org
+    import: common-options
+    main-is: MailboxOrg.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        mailbox-org,
+        my-prelude,
+        pa-prelude,
+        pa-label,
+        pa-error-tree,
+        exec-helpers,
+        netencode,
+        text,
+        directory,
+        filepath,
+        arglib-netencode,
+        random,
+        http-conduit,
+        aeson,
+        aeson-better-errors,
+        bytestring,
+        typed-process,
+        containers,
diff --git a/users/Profpatsch/mailbox-org/src/AesonQQ.hs b/users/Profpatsch/mailbox-org/src/AesonQQ.hs
new file mode 100644
index 0000000000..2ac3d533ae
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/src/AesonQQ.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE TemplateHaskellQuotes #-}
+
+module AesonQQ where
+
+import Data.Aeson qualified as Json
+import Language.Haskell.TH.Quote (QuasiQuoter)
+import PossehlAnalyticsPrelude
+import PyF qualified
+import PyF.Internal.QQ qualified as PyFConf
+
+aesonQQ :: QuasiQuoter
+aesonQQ =
+  PyF.mkFormatter
+    "aesonQQ"
+    PyF.defaultConfig
+      { PyFConf.delimiters = Just ('|', '|'),
+        PyFConf.postProcess = \exp_ -> do
+          -- TODO: this does not throw an error at compilation time if the json does not parse
+          [|
+            case Json.eitherDecodeStrict' @Json.Value $ textToBytesUtf8 $ stringToText $(exp_) of
+              Left err -> error err
+              Right a -> a
+            |]
+      }
diff --git a/users/Profpatsch/my-prelude/README.md b/users/Profpatsch/my-prelude/README.md
new file mode 100644
index 0000000000..2cc068579a
--- /dev/null
+++ b/users/Profpatsch/my-prelude/README.md
@@ -0,0 +1,42 @@
+# My Haskell Prelude
+
+Contains various modules I’ve found useful when writing Haskell.
+
+## Contents
+
+A short overview:
+
+### `MyPrelude.hs`
+
+A collection of re-exports and extra functions. This does *not* replace the `Prelude` module from `base`, but rather should be imported *in addition* to `Prelude`.
+
+Stuff like bad functions from prelude (partial stuff, or plain horrible stuff) are handled by a custom `.hlint` file, which you can find in [../.hlint.yaml]().
+
+The common style of haskell they try to enable is what I call β€œleft-to-right Haskell”,
+where one mostly prefers forward-chaining operators like `&`/`<&>`/`>>=` to backwards operators like `$`/`<$>`/`<=<`. In addition, all transformation function should follow the scheme of `aToB` instead of `B.fromA`, e.g. `Text.unpack`/`Text.pack` -> `textToString`/`stringToText`. Includes a bunch of text conversion functions one needs all the time, in the same style.
+
+These have been battle-tested in a production codebase of ~30k lines of Haskell.
+
+### `Label.hs`
+
+A very useful collection of anonymous labbeled tuples and enums of size 2 and 3. Assumes GHC >9.2 for `RecordDotSyntax` support.
+
+### `Pretty.hs`
+
+Colorful multiline pretty-printing of Haskell values.
+
+### `Test.hs`
+
+A wrapper around `hspec` which produces colorful test diffs.
+
+### `Aeson.hs`
+
+Helpers around Json parsing.
+
+### `Data.Error.Tree`
+
+Collect errors (from [`Data.Error`](https://hackage.haskell.org/package/error-1.0.0.0/docs/Data-Error.html)) into a tree, then display them in a nested fashion. Super useful for e.g. collecting and displaying nested parsing errors.
+
+### `RunCommand.hs`
+
+A module wrapping the process API with some helpful defaults for executing commands and printing what is executed to stderr.
diff --git a/users/Profpatsch/my-prelude/default.nix b/users/Profpatsch/my-prelude/default.nix
new file mode 100644
index 0000000000..e445115416
--- /dev/null
+++ b/users/Profpatsch/my-prelude/default.nix
@@ -0,0 +1,51 @@
+{ depot, pkgs, lib, ... }:
+
+pkgs.haskellPackages.mkDerivation {
+  pname = "my-prelude";
+  version = "0.0.1-unreleased";
+
+  src = depot.users.Profpatsch.exactSource ./. [
+    ./my-prelude.cabal
+    ./src/Aeson.hs
+    ./src/AtLeast.hs
+    ./src/MyPrelude.hs
+    ./src/Test.hs
+    ./src/Parse.hs
+    ./src/Pretty.hs
+    ./src/Seconds.hs
+    ./src/Tool.hs
+    ./src/ValidationParseT.hs
+    ./src/Postgres/Decoder.hs
+    ./src/Postgres/MonadPostgres.hs
+  ];
+
+  isLibrary = true;
+
+  libraryHaskellDepends = [
+    pkgs.haskellPackages.pa-prelude
+    pkgs.haskellPackages.pa-label
+    pkgs.haskellPackages.pa-error-tree
+    pkgs.haskellPackages.pa-json
+    pkgs.haskellPackages.pa-pretty
+    pkgs.haskellPackages.pa-field-parser
+    pkgs.haskellPackages.aeson-better-errors
+    pkgs.haskellPackages.foldl
+    pkgs.haskellPackages.resource-pool
+    pkgs.haskellPackages.error
+    pkgs.haskellPackages.hs-opentelemetry-api
+    pkgs.haskellPackages.hspec
+    pkgs.haskellPackages.hspec-expectations-pretty-diff
+    pkgs.haskellPackages.monad-logger
+    pkgs.haskellPackages.postgresql-simple
+    pkgs.haskellPackages.profunctors
+    pkgs.haskellPackages.PyF
+    pkgs.haskellPackages.semigroupoids
+    pkgs.haskellPackages.these
+    pkgs.haskellPackages.unliftio
+    pkgs.haskellPackages.validation-selective
+    pkgs.haskellPackages.vector
+  ];
+
+  license = lib.licenses.mit;
+
+}
diff --git a/users/Profpatsch/my-prelude/my-prelude.cabal b/users/Profpatsch/my-prelude/my-prelude.cabal
new file mode 100644
index 0000000000..95a8399f37
--- /dev/null
+++ b/users/Profpatsch/my-prelude/my-prelude.cabal
@@ -0,0 +1,120 @@
+cabal-version:      3.0
+name:               my-prelude
+version:            0.0.1.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+    -- allows defining pattern synonyms, but also the `import Foo (pattern FooPattern)` import syntax
+    PatternSynonyms
+  default-language: GHC2021
+
+
+library
+    import: common-options
+    hs-source-dirs: src
+    exposed-modules:
+      MyPrelude
+      Aeson
+      AtLeast
+      Test
+      Postgres.Decoder
+      Postgres.MonadPostgres
+      ValidationParseT
+      Parse
+      Pretty
+      Seconds
+      Tool
+
+    -- Modules included in this executable, other than Main.
+    -- other-modules:
+
+    -- LANGUAGE extensions used by modules in this package.
+    -- other-extensions:
+    build-depends:
+       base >=4.15 && <5
+     , pa-prelude
+     , pa-label
+     , pa-error-tree
+     , pa-json
+     , pa-pretty
+     , pa-field-parser
+     , aeson
+     , aeson-better-errors
+     , bytestring
+     , containers
+     , foldl
+     , unordered-containers
+     , resource-pool
+     , resourcet
+     , scientific
+     , time
+     , error
+     , exceptions
+     , filepath
+     , hspec
+     , hspec-expectations-pretty-diff
+     , hs-opentelemetry-api
+     , monad-logger
+     , mtl
+     , postgresql-simple
+     , profunctors
+     , PyF
+     , semigroupoids
+     , selective
+     , template-haskell
+     , text
+     , these
+     , unix
+     , unliftio
+     , validation-selective
+     , vector
+     , ghc-boot
+     -- for Pretty
+     , aeson-pretty
+     , hscolour
+     , ansi-terminal
+     , nicify-lib
diff --git a/users/Profpatsch/my-prelude/src/Aeson.hs b/users/Profpatsch/my-prelude/src/Aeson.hs
new file mode 100644
index 0000000000..73d6116082
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Aeson.hs
@@ -0,0 +1,176 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE GHC2021 #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+module Aeson where
+
+import Data.Aeson (Value (..))
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.Error.Tree
+import Data.Maybe (catMaybes)
+import Data.Vector qualified as Vector
+import Label
+import PossehlAnalyticsPrelude
+import Test.Hspec (describe, it, shouldBe)
+import Test.Hspec qualified as Hspec
+
+-- | Convert a 'Json.ParseError' to a corresponding 'ErrorTree'
+parseErrorTree :: Error -> Json.ParseError Error -> ErrorTree
+parseErrorTree contextMsg errs =
+  errs
+    & Json.displayError prettyError
+    <&> newError
+    & nonEmpty
+    & \case
+      Nothing -> singleError contextMsg
+      Just errs' -> errorTree contextMsg errs'
+
+-- | Parse a key from the object, Γ  la 'Json.key', return a labelled value.
+--
+-- We don’t provide a version that infers the json object key,
+-- since that conflates internal naming with the external API, which is dangerous.
+--
+-- @@
+-- do
+--   txt <- keyLabel @"myLabel" "jsonKeyName" Json.asText
+--   pure (txt :: Label "myLabel" Text)
+-- @@
+keyLabel ::
+  forall label err m a.
+  Monad m =>
+  Text ->
+  Json.ParseT err m a ->
+  Json.ParseT err m (Label label a)
+keyLabel = do
+  keyLabel' (Proxy @label)
+
+-- | Parse a key from the object, Γ  la 'Json.key', return a labelled value.
+-- Version of 'keyLabel' that requires a proxy.
+--
+-- @@
+-- do
+--   txt <- keyLabel' (Proxy @"myLabel") "jsonKeyName" Json.asText
+--   pure (txt :: Label "myLabel" Text)
+-- @@
+keyLabel' ::
+  forall label err m a.
+  Monad m =>
+  Proxy label ->
+  Text ->
+  Json.ParseT err m a ->
+  Json.ParseT err m (Label label a)
+keyLabel' Proxy key parser = label @label <$> Json.key key parser
+
+-- | Parse an optional key from the object, Γ  la 'Json.keyMay', return a labelled value.
+--
+-- We don’t provide a version that infers the json object key,
+-- since that conflates internal naming with the external API, which is dangerous.
+--
+-- @@
+-- do
+--   txt <- keyLabelMay @"myLabel" "jsonKeyName" Json.asText
+--   pure (txt :: Label "myLabel" (Maybe Text))
+-- @@
+keyLabelMay ::
+  forall label err m a.
+  Monad m =>
+  Text ->
+  Json.ParseT err m a ->
+  Json.ParseT err m (Label label (Maybe a))
+keyLabelMay = do
+  keyLabelMay' (Proxy @label)
+
+-- | Parse an optional key from the object, Γ  la 'Json.keyMay', return a labelled value.
+-- Version of 'keyLabelMay' that requires a proxy.
+--
+-- @@
+-- do
+--   txt <- keyLabelMay' (Proxy @"myLabel") "jsonKeyName" Json.asText
+--   pure (txt :: Label "myLabel" (Maybe Text))
+-- @@
+keyLabelMay' ::
+  forall label err m a.
+  Monad m =>
+  Proxy label ->
+  Text ->
+  Json.ParseT err m a ->
+  Json.ParseT err m (Label label (Maybe a))
+keyLabelMay' Proxy key parser = label @label <$> Json.keyMay key parser
+
+-- | Like 'Json.key', but allows a list of keys that are tried in order.
+--
+-- This is intended for renaming keys in an object.
+-- The first key is the most up-to-date version of a key, the others are for backward-compatibility.
+--
+-- If a key (new or old) exists, the inner parser will always be executed for that key.
+keyRenamed :: Monad m => NonEmpty Text -> Json.ParseT err m a -> Json.ParseT err m a
+keyRenamed (newKey :| oldKeys) inner =
+  keyRenamedTryOldKeys oldKeys inner >>= \case
+    Nothing -> Json.key newKey inner
+    Just parse -> parse
+
+-- | Like 'Json.keyMay', but allows a list of keys that are tried in order.
+--
+-- This is intended for renaming keys in an object.
+-- The first key is the most up-to-date version of a key, the others are for backward-compatibility.
+--
+-- If a key (new or old) exists, the inner parser will always be executed for that key.
+keyRenamedMay :: Monad m => NonEmpty Text -> Json.ParseT err m a -> Json.ParseT err m (Maybe a)
+keyRenamedMay (newKey :| oldKeys) inner =
+  keyRenamedTryOldKeys oldKeys inner >>= \case
+    Nothing -> Json.keyMay newKey inner
+    Just parse -> Just <$> parse
+
+-- | Helper function for 'keyRenamed' and 'keyRenamedMay' that returns the parser for the first old key that exists, if any.
+keyRenamedTryOldKeys :: Monad m => [Text] -> Json.ParseT err m a -> Json.ParseT err m (Maybe (Json.ParseT err m a))
+keyRenamedTryOldKeys oldKeys inner = do
+  oldKeys & traverse tryOld <&> catMaybes <&> nonEmpty <&> \case
+    Nothing -> Nothing
+    Just (old :| _moreOld) -> Just old
+  where
+    tryOld key =
+      Json.keyMay key (pure ()) <&> \case
+        Just () -> Just $ Json.key key inner
+        Nothing -> Nothing
+
+test_keyRenamed :: Hspec.Spec
+test_keyRenamed = do
+  describe "keyRenamed" $ do
+    let parser = keyRenamed ("new" :| ["old"]) Json.asText
+    let p = Json.parseValue @() parser
+    it "accepts the new key and the old key" $ do
+      p (Object (KeyMap.singleton "new" (String "text")))
+        `shouldBe` (Right "text")
+      p (Object (KeyMap.singleton "old" (String "text")))
+        `shouldBe` (Right "text")
+    it "fails with the old key in the error if the inner parser is wrong" $ do
+      p (Object (KeyMap.singleton "old" Null))
+        `shouldBe` (Left (Json.BadSchema [Json.ObjectKey "old"] (Json.WrongType Json.TyString Null)))
+    it "fails with the new key in the error if the inner parser is wrong" $ do
+      p (Object (KeyMap.singleton "new" Null))
+        `shouldBe` (Left (Json.BadSchema [Json.ObjectKey "new"] (Json.WrongType Json.TyString Null)))
+    it "fails if the key is missing" $ do
+      p (Object KeyMap.empty)
+        `shouldBe` (Left (Json.BadSchema [] (Json.KeyMissing "new")))
+  describe "keyRenamedMay" $ do
+    let parser = keyRenamedMay ("new" :| ["old"]) Json.asText
+    let p = Json.parseValue @() parser
+    it "accepts the new key and the old key" $ do
+      p (Object (KeyMap.singleton "new" (String "text")))
+        `shouldBe` (Right (Just "text"))
+      p (Object (KeyMap.singleton "old" (String "text")))
+        `shouldBe` (Right (Just "text"))
+    it "allows the old and new key to be missing" $ do
+      p (Object KeyMap.empty)
+        `shouldBe` (Right Nothing)
+
+-- | Create a json array from a list of json values.
+jsonArray :: [Value] -> Value
+jsonArray xs = xs & Vector.fromList & Array
diff --git a/users/Profpatsch/my-prelude/src/AtLeast.hs b/users/Profpatsch/my-prelude/src/AtLeast.hs
new file mode 100644
index 0000000000..3857c3a7cf
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/AtLeast.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module AtLeast where
+
+import Data.Aeson (FromJSON (parseJSON))
+import Data.Aeson.BetterErrors qualified as Json
+import FieldParser (FieldParser)
+import FieldParser qualified as Field
+import GHC.Records (HasField (..))
+import GHC.TypeLits (KnownNat, natVal)
+import PossehlAnalyticsPrelude
+  ( Natural,
+    Proxy (Proxy),
+    fmt,
+    prettyError,
+    (&),
+  )
+
+-- | A natural number that must be at least as big as the type literal.
+newtype AtLeast (min :: Natural) num = AtLeast num
+  -- Just use the instances of the wrapped number type
+  deriving newtype (Eq, Show)
+
+-- | This is the β€œdestructor” for `AtLeast`, because of the phantom type (@min@) it cannot be inferred automatically.
+instance HasField "unAtLeast" (AtLeast min num) num where
+  getField (AtLeast num) = num
+
+parseAtLeast ::
+  forall min num.
+  (KnownNat min, Integral num, Show num) =>
+  FieldParser num (AtLeast min num)
+parseAtLeast =
+  let minInt = natVal (Proxy @min)
+   in Field.FieldParser $ \from ->
+        if from >= (minInt & fromIntegral)
+          then Right (AtLeast from)
+          else Left [fmt|Must be at least {minInt & show} but was {from & show}|]
+
+instance
+  (KnownNat min, FromJSON num, Integral num, Bounded num, Show num) =>
+  FromJSON (AtLeast min num)
+  where
+  parseJSON =
+    Json.toAesonParser
+      prettyError
+      ( do
+          num <- Json.fromAesonParser @_ @num
+          case Field.runFieldParser (parseAtLeast @min @num) num of
+            Left err -> Json.throwCustomError err
+            Right a -> pure a
+      )
diff --git a/users/Profpatsch/my-prelude/src/MyPrelude.hs b/users/Profpatsch/my-prelude/src/MyPrelude.hs
new file mode 100644
index 0000000000..880983c47e
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/MyPrelude.hs
@@ -0,0 +1,776 @@
+{-# LANGUAGE ImplicitParams #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module MyPrelude
+  ( -- * Text conversions
+    Text,
+    ByteString,
+    Word8,
+    fmt,
+    textToString,
+    stringToText,
+    stringToBytesUtf8,
+    showToText,
+    textToBytesUtf8,
+    textToBytesUtf8Lazy,
+    bytesToTextUtf8,
+    bytesToTextUtf8Lazy,
+    bytesToTextUtf8Lenient,
+    bytesToTextUtf8LenientLazy,
+    bytesToTextUtf8Unsafe,
+    bytesToTextUtf8UnsafeLazy,
+    toStrict,
+    toLazy,
+    toStrictBytes,
+    toLazyBytes,
+    charToWordUnsafe,
+
+    -- * IO
+    putStrLn,
+    putStderrLn,
+    exitWithMessage,
+
+    -- * WIP code
+    todo,
+
+    -- * Records
+    HasField,
+
+    -- * Control flow
+    doAs,
+    (&),
+    (<&>),
+    (<|>),
+    foldMap1,
+    foldMap',
+    join,
+    when,
+    unless,
+    guard,
+    ExceptT (..),
+    runExceptT,
+    MonadThrow,
+    throwM,
+    MonadIO,
+    liftIO,
+    MonadReader,
+    asks,
+    Bifunctor,
+    first,
+    second,
+    bimap,
+    both,
+    foldMap,
+    fold,
+    foldl',
+    fromMaybe,
+    mapMaybe,
+    findMaybe,
+    Traversable,
+    for,
+    for_,
+    traverse,
+    traverse_,
+    traverseFold,
+    traverseFold1,
+    traverseFoldDefault,
+    MonadTrans,
+    lift,
+
+    -- * Data types
+    Coercible,
+    coerce,
+    Proxy (Proxy),
+    Map,
+    annotate,
+    Validation (Success, Failure),
+    failure,
+    successes,
+    failures,
+    traverseValidate,
+    traverseValidateM,
+    traverseValidateM_,
+    eitherToValidation,
+    eitherToListValidation,
+    validationToEither,
+    These (This, That, These),
+    eitherToThese,
+    eitherToListThese,
+    validationToThese,
+    thenThese,
+    thenValidate,
+    thenValidateM,
+    NonEmpty ((:|)),
+    pattern IsEmpty,
+    pattern IsNonEmpty,
+    singleton,
+    nonEmpty,
+    nonEmptyDef,
+    overNonEmpty,
+    zipNonEmpty,
+    zipWithNonEmpty,
+    zip3NonEmpty,
+    zipWith3NonEmpty,
+    zip4NonEmpty,
+    toList,
+    lengthNatural,
+    maximum1,
+    minimum1,
+    maximumBy1,
+    minimumBy1,
+    Vector,
+    Generic,
+    Lift,
+    Semigroup,
+    sconcat,
+    Monoid,
+    mconcat,
+    ifTrue,
+    ifExists,
+    Void,
+    absurd,
+    Identity (Identity, runIdentity),
+    Natural,
+    intToNatural,
+    Scientific,
+    Contravariant,
+    contramap,
+    (>$<),
+    (>&<),
+    Profunctor,
+    dimap,
+    lmap,
+    rmap,
+    Semigroupoid,
+    Category,
+    (>>>),
+    (&>>),
+    Any,
+
+    -- * Enum definition
+    inverseFunction,
+    inverseMap,
+    enumerateAll,
+
+    -- * Map helpers
+    mapFromListOn,
+    mapFromListOnMerge,
+
+    -- * Error handling
+    HasCallStack,
+    module Data.Error,
+  )
+where
+
+import Control.Applicative ((<|>))
+import Control.Category (Category, (>>>))
+import Control.Foldl.NonEmpty qualified as Foldl1
+import Control.Monad (guard, join, unless, when)
+import Control.Monad.Catch (MonadThrow (throwM))
+import Control.Monad.Except
+  ( ExceptT (..),
+    runExceptT,
+  )
+import Control.Monad.IO.Class (MonadIO, liftIO)
+import Control.Monad.Identity (Identity (Identity))
+import Control.Monad.Reader (MonadReader, asks)
+import Control.Monad.Trans (MonadTrans (lift))
+import Data.Bifunctor (Bifunctor, bimap, first, second)
+import Data.ByteString
+  ( ByteString,
+  )
+import Data.ByteString.Lazy qualified
+import Data.Char qualified
+import Data.Coerce (Coercible, coerce)
+import Data.Data (Proxy (Proxy))
+import Data.Error
+import Data.Foldable (Foldable (foldMap', toList), fold, foldl', for_, sequenceA_, traverse_)
+import Data.Foldable qualified as Foldable
+import Data.Function ((&))
+import Data.Functor ((<&>))
+import Data.Functor.Contravariant (Contravariant (contramap), (>$<))
+import Data.Functor.Identity (Identity (runIdentity))
+import Data.List (zip4)
+import Data.List.NonEmpty (NonEmpty ((:|)), nonEmpty)
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Map.Strict
+  ( Map,
+  )
+import Data.Map.Strict qualified as Map
+import Data.Maybe (fromMaybe, mapMaybe)
+import Data.Maybe qualified as Maybe
+import Data.Profunctor (Profunctor, dimap, lmap, rmap)
+import Data.Scientific (Scientific)
+import Data.Semigroup (sconcat)
+import Data.Semigroup.Foldable (Foldable1 (fold1), foldMap1)
+import Data.Semigroup.Traversable (Traversable1)
+import Data.Semigroupoid (Semigroupoid (o))
+import Data.Text
+  ( Text,
+  )
+import Data.Text qualified
+import Data.Text.Encoding qualified
+import Data.Text.Encoding.Error qualified
+import Data.Text.Lazy qualified
+import Data.Text.Lazy.Encoding qualified
+import Data.These (These (That, These, This))
+import Data.Traversable (for)
+import Data.Vector (Vector)
+import Data.Void (Void, absurd)
+import Data.Word (Word8)
+import GHC.Exception (errorCallWithCallStackException)
+import GHC.Exts (Any, RuntimeRep, TYPE, raise#)
+import GHC.Generics (Generic)
+import GHC.Natural (Natural)
+import GHC.Records (HasField)
+import GHC.Stack (HasCallStack)
+import GHC.Utils.Encoding qualified as GHC
+import Language.Haskell.TH.Syntax (Lift)
+import PyF (fmt)
+import System.Exit qualified
+import System.IO qualified
+import Validation
+  ( Validation (Failure, Success),
+    eitherToValidation,
+    failure,
+    failures,
+    successes,
+    validationToEither,
+  )
+
+-- | Mark a `do`-block with the type of the Monad/Applicativ it uses.
+-- Only intended for reading ease and making code easier to understand,
+-- especially do-blocks that use unconventional monads (like Maybe or List).
+--
+-- Example:
+--
+-- @
+-- doAs @Maybe $ do
+--  a <- Just 'a'
+--  b <- Just 'b'
+--  pure (a, b)
+-- @
+doAs :: forall m a. m a -> m a
+doAs = id
+
+-- | Forward-applying 'contramap', like '&'/'$' and '<&>'/'<$>' but for '>$<'.
+(>&<) :: (Contravariant f) => f b -> (a -> b) -> f a
+(>&<) = flip contramap
+
+infixl 5 >&<
+
+-- | Forward semigroupoid application. The same as '(>>>)', but 'Semigroupoid' is not a superclass of 'Category' (yet).
+--
+-- Specialized examples:
+--
+-- @
+-- for functions : (a -> b) -> (b -> c) -> (a -> c)
+-- for Folds: Fold a b -> Fold b c -> Fold a c
+-- @
+(&>>) :: (Semigroupoid s) => s a b -> s b c -> s a c
+(&>>) = flip Data.Semigroupoid.o
+
+-- like >>>
+infixr 1 &>>
+
+-- | encode a Text to a UTF-8 encoded Bytestring
+textToBytesUtf8 :: Text -> ByteString
+textToBytesUtf8 = Data.Text.Encoding.encodeUtf8
+
+-- | encode a lazy Text to a UTF-8 encoded lazy Bytestring
+textToBytesUtf8Lazy :: Data.Text.Lazy.Text -> Data.ByteString.Lazy.ByteString
+textToBytesUtf8Lazy = Data.Text.Lazy.Encoding.encodeUtf8
+
+bytesToTextUtf8 :: ByteString -> Either Error Text
+bytesToTextUtf8 = first exceptionToError . Data.Text.Encoding.decodeUtf8'
+
+bytesToTextUtf8Lazy :: Data.ByteString.Lazy.ByteString -> Either Error Data.Text.Lazy.Text
+bytesToTextUtf8Lazy = first exceptionToError . Data.Text.Lazy.Encoding.decodeUtf8'
+
+-- | decode a Text from a ByteString that is assumed to be UTF-8 (crash if that is not the case)
+bytesToTextUtf8Unsafe :: ByteString -> Text
+bytesToTextUtf8Unsafe = Data.Text.Encoding.decodeUtf8
+
+-- | decode a Text from a ByteString that is assumed to be UTF-8 (crash if that is not the case)
+bytesToTextUtf8UnsafeLazy :: Data.ByteString.Lazy.ByteString -> Data.Text.Lazy.Text
+bytesToTextUtf8UnsafeLazy = Data.Text.Lazy.Encoding.decodeUtf8
+
+-- | decode a Text from a ByteString that is assumed to be UTF-8,
+-- replace non-UTF-8 characters with the replacment char U+FFFD.
+bytesToTextUtf8Lenient :: Data.ByteString.ByteString -> Data.Text.Text
+bytesToTextUtf8Lenient =
+  Data.Text.Encoding.decodeUtf8With Data.Text.Encoding.Error.lenientDecode
+
+-- | decode a lazy Text from a lazy ByteString that is assumed to be UTF-8,
+-- replace non-UTF-8 characters with the replacment char U+FFFD.
+bytesToTextUtf8LenientLazy :: Data.ByteString.Lazy.ByteString -> Data.Text.Lazy.Text
+bytesToTextUtf8LenientLazy =
+  Data.Text.Lazy.Encoding.decodeUtf8With Data.Text.Encoding.Error.lenientDecode
+
+-- | Make a lazy 'Text' strict.
+toStrict :: Data.Text.Lazy.Text -> Text
+toStrict = Data.Text.Lazy.toStrict
+
+-- | Make a strict 'Text' lazy.
+toLazy :: Text -> Data.Text.Lazy.Text
+toLazy = Data.Text.Lazy.fromStrict
+
+-- | Make a lazy 'ByteString' strict.
+toStrictBytes :: Data.ByteString.Lazy.ByteString -> ByteString
+toStrictBytes = Data.ByteString.Lazy.toStrict
+
+-- | Make a strict 'ByteString' lazy.
+toLazyBytes :: ByteString -> Data.ByteString.Lazy.ByteString
+toLazyBytes = Data.ByteString.Lazy.fromStrict
+
+-- | Convert a (performant) 'Text' into an (imperformant) list-of-char 'String'.
+--
+-- Some libraries (like @time@ or @network-uri@) still use the `String` as their interface. We only want to convert to string at the edges, otherwise use 'Text'.
+--
+-- ATTN: Don’t use `String` in code if you can avoid it, prefer `Text` instead.
+textToString :: Text -> String
+textToString = Data.Text.unpack
+
+-- | Convert an (imperformant) list-of-char 'String' into a (performant) 'Text' .
+--
+-- Some libraries (like @time@ or @network-uri@) still use the `String` as their interface. We want to convert 'String' to 'Text' as soon as possible and only use 'Text' in our code.
+--
+-- ATTN: Don’t use `String` in code if you can avoid it, prefer `Text` instead.
+stringToText :: String -> Text
+stringToText = Data.Text.pack
+
+-- | Encode a String to an UTF-8 encoded Bytestring
+--
+-- ATTN: Don’t use `String` in code if you can avoid it, prefer `Text` instead.
+stringToBytesUtf8 :: String -> ByteString
+-- TODO(Profpatsch): use a stable interface
+stringToBytesUtf8 = GHC.utf8EncodeByteString
+
+-- | Like `show`, but generate a 'Text'
+--
+-- ATTN: This goes via `String` and thus is fairly inefficient.
+-- We should add a good display library at one point.
+--
+-- ATTN: unlike `show`, this forces the whole @'a
+-- so only use if you want to display the whole thing.
+showToText :: (Show a) => a -> Text
+showToText = stringToText . show
+
+-- | Unsafe conversion between 'Char' and 'Word8'. This is a no-op and
+-- silently truncates to 8 bits Chars > '\255'. It is provided as
+-- convenience for ByteString construction.
+--
+-- Use if you want to get the 'Word8' representation of a character literal.
+-- Don’t use on arbitrary characters!
+--
+-- >>> charToWordUnsafe ','
+-- 44
+charToWordUnsafe :: Char -> Word8
+{-# INLINE charToWordUnsafe #-}
+charToWordUnsafe = fromIntegral . Data.Char.ord
+
+pattern IsEmpty :: [a]
+pattern IsEmpty <- (null -> True)
+  where
+    IsEmpty = []
+
+pattern IsNonEmpty :: NonEmpty a -> [a]
+pattern IsNonEmpty n <- (nonEmpty -> Just n)
+  where
+    IsNonEmpty n = toList n
+
+{-# COMPLETE IsEmpty, IsNonEmpty #-}
+
+-- | Single element in a (non-empty) list.
+singleton :: a -> NonEmpty a
+singleton a = a :| []
+
+-- | If the given list is empty, use the given default element and return a non-empty list.
+nonEmptyDef :: a -> [a] -> NonEmpty a
+nonEmptyDef def xs =
+  xs & nonEmpty & \case
+    Nothing -> def :| []
+    Just ne -> ne
+
+-- | If the list is not empty, run the given function with a NonEmpty list, otherwise just return []
+overNonEmpty :: (Applicative f) => (NonEmpty a -> f [b]) -> [a] -> f [b]
+overNonEmpty f xs = case xs of
+  IsEmpty -> pure []
+  IsNonEmpty xs' -> f xs'
+
+-- | Zip two non-empty lists.
+zipNonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty (a, b)
+{-# INLINE zipNonEmpty #-}
+zipNonEmpty ~(a :| as) ~(b :| bs) = (a, b) :| zip as bs
+
+-- | Zip two non-empty lists, combining them with the given function
+zipWithNonEmpty :: (a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
+{-# INLINE zipWithNonEmpty #-}
+zipWithNonEmpty = NonEmpty.zipWith
+
+-- | Zip three non-empty lists.
+zip3NonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty (a, b, c)
+{-# INLINE zip3NonEmpty #-}
+zip3NonEmpty ~(a :| as) ~(b :| bs) ~(c :| cs) = (a, b, c) :| zip3 as bs cs
+
+-- | Zip three non-empty lists, combining them with the given function
+zipWith3NonEmpty :: (a -> b -> c -> d) -> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d
+{-# INLINE zipWith3NonEmpty #-}
+zipWith3NonEmpty f ~(x :| xs) ~(y :| ys) ~(z :| zs) = f x y z :| zipWith3 f xs ys zs
+
+-- | Zip four non-empty lists
+zip4NonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d -> NonEmpty (a, b, c, d)
+{-# INLINE zip4NonEmpty #-}
+zip4NonEmpty ~(a :| as) ~(b :| bs) ~(c :| cs) ~(d :| ds) = (a, b, c, d) :| zip4 as bs cs ds
+
+-- | We don’t want to use Foldable’s `length`, because it is too polymorphic and can lead to bugs.
+-- Only list-y things should have a length.
+class (Foldable f) => Lengthy f
+
+instance Lengthy []
+
+instance Lengthy NonEmpty
+
+instance Lengthy Vector
+
+lengthNatural :: (Lengthy f) => f a -> Natural
+lengthNatural xs =
+  xs
+    & Foldable.length
+    -- length can never be negative or something went really, really wrong
+    & fromIntegral @Int @Natural
+
+-- | @O(n)@. Get the maximum element from a non-empty structure (strict).
+maximum1 :: (Foldable1 f, Ord a) => f a -> a
+maximum1 = Foldl1.fold1 Foldl1.maximum
+
+-- | @O(n)@. Get the maximum element from a non-empty structure, using the given comparator (strict).
+maximumBy1 :: (Foldable1 f) => (a -> a -> Ordering) -> f a -> a
+maximumBy1 f = Foldl1.fold1 (Foldl1.maximumBy f)
+
+-- | @O(n)@. Get the minimum element from a non-empty structure (strict).
+minimum1 :: (Foldable1 f, Ord a) => f a -> a
+minimum1 = Foldl1.fold1 Foldl1.minimum
+
+-- | @O(n)@. Get the minimum element from a non-empty structure, using the given comparator (strict).
+minimumBy1 :: (Foldable1 f) => (a -> a -> Ordering) -> f a -> a
+minimumBy1 f = Foldl1.fold1 (Foldl1.minimumBy f)
+
+-- | Annotate a 'Maybe' with an error message and turn it into an 'Either'.
+annotate :: err -> Maybe a -> Either err a
+annotate err = \case
+  Nothing -> Left err
+  Just a -> Right a
+
+-- | Map the same function over both sides of a Bifunctor (e.g. a tuple).
+both :: (Bifunctor bi) => (a -> b) -> bi a a -> bi b b
+both f = bimap f f
+
+-- | Find the first element for which pred returns `Just a`, and return the `a`.
+--
+-- Example:
+-- @
+-- >>> :set -XTypeApplications
+-- >>> import qualified Text.Read
+--
+-- >>> findMaybe (Text.Read.readMaybe @Int) ["foo"]
+-- Nothing
+-- >>> findMaybe (Text.Read.readMaybe @Int) ["foo", "34.40", "34", "abc"]
+-- Just 34
+findMaybe :: (Foldable t) => (a -> Maybe b) -> t a -> Maybe b
+findMaybe mPred list =
+  let pred' x = Maybe.isJust $ mPred x
+   in case Foldable.find pred' list of
+        Just a -> mPred a
+        Nothing -> Nothing
+
+-- | 'traverse' with a function returning 'Either' and collect all errors that happen, if they happen.
+--
+-- Does not shortcut on error, so will always traverse the whole list/'Traversable' structure.
+--
+-- This is a useful error handling function in many circumstances,
+-- because it won’t only return the first error that happens, but rather all of them.
+traverseValidate :: forall t a err b. (Traversable t) => (a -> Either err b) -> t a -> Either (NonEmpty err) (t b)
+traverseValidate f as =
+  as
+    & traverse @t @(Validation _) (eitherToListValidation . f)
+    & validationToEither
+
+-- | 'traverse' with a function returning 'm Either' and collect all errors that happen, if they happen.
+--
+-- Does not shortcut on error, so will always traverse the whole list/'Traversable' structure.
+--
+-- This is a useful error handling function in many circumstances,
+-- because it won’t only return the first error that happens, but rather all of them.
+traverseValidateM :: forall t m a err b. (Traversable t, Applicative m) => (a -> m (Either err b)) -> t a -> m (Either (NonEmpty err) (t b))
+traverseValidateM f as =
+  as
+    & traverse @t @m (\a -> a & f <&> eitherToListValidation)
+    <&> sequenceA @t @(Validation _)
+    <&> validationToEither
+
+-- | 'traverse_' with a function returning 'm Either' and collect all errors that happen, if they happen.
+--
+-- Does not shortcut on error, so will always traverse the whole list/'Traversable' structure.
+--
+-- This is a useful error handling function in many circumstances,
+-- because it won’t only return the first error that happens, but rather all of them.
+traverseValidateM_ :: forall t m a err. (Traversable t, Applicative m) => (a -> m (Either err ())) -> t a -> m (Either (NonEmpty err) ())
+traverseValidateM_ f as =
+  as
+    & traverse @t @m (\a -> a & f <&> eitherToListValidation)
+    <&> sequenceA_ @t @(Validation _)
+    <&> validationToEither
+
+-- | Like 'eitherToValidation', but puts the Error side into a NonEmpty list
+-- to make it combine with other validations.
+--
+-- See also 'validateEithers', if you have a list of Either and want to collect all errors.
+eitherToListValidation :: Either a c -> Validation (NonEmpty a) c
+eitherToListValidation = first singleton . eitherToValidation
+
+-- | Convert an 'Either' to a 'These'.
+eitherToThese :: Either err a -> These err a
+eitherToThese (Left err) = This err
+eitherToThese (Right a) = That a
+
+-- | Like 'eitherToThese', but puts the Error side into a NonEmpty list
+-- to make it combine with other theses.
+eitherToListThese :: Either err a -> These (NonEmpty err) a
+eitherToListThese (Left e) = This (singleton e)
+eitherToListThese (Right a) = That a
+
+-- | Convert a 'Validation' to a 'These'.
+validationToThese :: Validation err a -> These err a
+validationToThese (Failure err) = This err
+validationToThese (Success a) = That a
+
+-- | Nested '>>=' of a These inside some other @m@.
+--
+-- Use if you want to collect errors and successes, and want to chain multiple function returning 'These'.
+thenThese ::
+  (Monad m, Semigroup err) =>
+  (a -> m (These err b)) ->
+  m (These err a) ->
+  m (These err b)
+thenThese f x = do
+  th <- x
+  join <$> traverse f th
+
+-- | Nested validating bind-like combinator.
+--
+-- Use if you want to collect errors, and want to chain multiple functions returning 'Validation'.
+thenValidate ::
+  (a -> Validation err b) ->
+  Validation err a ->
+  Validation err b
+thenValidate f = \case
+  Success a -> f a
+  Failure err -> Failure err
+
+-- | Nested validating bind-like combinator inside some other @m@.
+--
+-- Use if you want to collect errors, and want to chain multiple functions returning 'Validation'.
+thenValidateM ::
+  (Monad m) =>
+  (a -> m (Validation err b)) ->
+  m (Validation err a) ->
+  m (Validation err b)
+thenValidateM f x =
+  eitherToValidation <$> do
+    x' <- validationToEither <$> x
+    case x' of
+      Left err -> pure $ Left err
+      Right a -> validationToEither <$> f a
+
+-- | Put the text to @stderr@.
+putStderrLn :: Text -> IO ()
+putStderrLn msg =
+  System.IO.hPutStrLn System.IO.stderr $ textToString msg
+
+exitWithMessage :: Text -> IO a
+exitWithMessage msg = do
+  putStderrLn msg
+  System.Exit.exitWith $ System.Exit.ExitFailure (-1)
+
+-- | Run some function producing applicative over a traversable data structure,
+-- then collect the results in a Monoid.
+--
+-- Very helpful with side-effecting functions returning @(Validation err a)@:
+--
+-- @
+-- let
+--   f :: Text -> IO (Validation (NonEmpty Error) Text)
+--   f t = pure $ if t == "foo" then Success t else Failure (singleton ("not foo: " <> t))
+--
+-- in traverseFold f [ "foo", "bar", "baz" ]
+--   == Failure ("not foo bar" :| ["not foo baz"])
+-- @
+--
+-- … since @(Semigroup err => Validation err a)@ is a @Semigroup@/@Monoid@ itself.
+traverseFold :: (Applicative ap, Traversable t, Monoid m) => (a -> ap m) -> t a -> ap m
+{-# INLINE traverseFold #-}
+traverseFold f xs =
+  -- note: could be weakened to (Foldable t) via `getAp . foldMap (Ap . f)`
+  fold <$> traverse f xs
+
+-- | Like 'traverseFold', but fold over a semigroup instead of a Monoid, by providing a starting element.
+traverseFoldDefault :: (Applicative ap, Traversable t, Semigroup m) => m -> (a -> ap m) -> t a -> ap m
+{-# INLINE traverseFoldDefault #-}
+traverseFoldDefault def f xs = foldDef def <$> traverse f xs
+  where
+    foldDef = foldr (<>)
+
+-- | Same as 'traverseFold', but with a 'Semigroup' and 'Traversable1' restriction.
+traverseFold1 :: (Applicative ap, Traversable1 t, Semigroup s) => (a -> ap s) -> t a -> ap s
+{-# INLINE traverseFold1 #-}
+-- note: cannot be weakened to (Foldable1 t) because there is no `Ap` for Semigroup (No `Apply` typeclass)
+traverseFold1 f xs = fold1 <$> traverse f xs
+
+-- | Use this in places where the code is still to be implemented.
+--
+-- It always type-checks and will show a warning at compile time if it was forgotten in the code.
+--
+-- Use instead of 'error' and 'undefined' for code that hasn’t been written.
+--
+-- Uses the same trick as https://hackage.haskell.org/package/protolude-0.3.0/docs/src/Protolude.Error.html#error
+{-# WARNING todo "'todo' (undefined code) remains in code" #-}
+todo :: forall (r :: RuntimeRep). forall (a :: TYPE r). (HasCallStack) => a
+todo = raise# (errorCallWithCallStackException "This code was not yet implemented: TODO" ?callStack)
+
+-- | Convert an integer to a 'Natural' if possible
+--
+-- Named the same as the function from "GHC.Natural", but does not crash.
+intToNatural :: (Integral a) => a -> Maybe Natural
+intToNatural i =
+  if i < 0
+    then Nothing
+    else Just $ fromIntegral i
+
+-- | @inverseFunction f@ creates a function that is the inverse of a given function
+-- @f@. It does so by constructing 'M.Map' internally for each value @f a@. The
+-- implementation makes sure that the 'M.Map' is constructed only once and then
+-- shared for every call.
+--
+-- __Memory usage note:__ don't inverse functions that have types like 'Int'
+-- as their result. In this case the created 'M.Map' will have huge size.
+--
+-- The complexity of reversed mapping is \(\mathcal{O}(\log n)\).
+--
+-- __Performance note:__ make sure to specialize monomorphic type of your functions
+-- that use 'inverseFunction' to avoid 'M.Map' reconstruction.
+--
+-- One of the common 'inverseFunction' use-case is inverting the 'show' or a 'show'-like
+-- function.
+--
+-- >>> data Color = Red | Green | Blue deriving (Show, Enum, Bounded)
+-- >>> parse = inverseFunction show :: String -> Maybe Color
+-- >>> parse "Red"
+-- Just Red
+-- >>> parse "Black"
+-- Nothing
+--
+-- __Correctness note:__ 'inverseFunction' expects /injective function/ as its argument,
+-- i.e. the function must map distinct arguments to distinct values.
+--
+-- Typical usage of this function looks like this:
+--
+-- @
+-- __data__ GhcVer
+--    = Ghc802
+--    | Ghc822
+--    | Ghc844
+--    | Ghc865
+--    | Ghc881
+--    __deriving__ ('Eq', 'Ord', 'Show', 'Enum', 'Bounded')
+--
+-- showGhcVer :: GhcVer -> 'Text'
+-- showGhcVer = \\__case__
+--    Ghc802 -> "8.0.2"
+--    Ghc822 -> "8.2.2"
+--    Ghc844 -> "8.4.4"
+--    Ghc865 -> "8.6.5"
+--    Ghc881 -> "8.8.1"
+--
+-- parseGhcVer :: 'Text' -> 'Maybe' GhcVer
+-- parseGhcVer = 'inverseFunction' showGhcVer
+--
+-- Taken from relude’s @Relude.Extra.Enum@.
+inverseFunction ::
+  forall a k.
+  (Bounded a, Enum a, Ord k) =>
+  (a -> k) ->
+  (k -> Maybe a)
+inverseFunction f k = Map.lookup k $ inverseMap f
+
+-- | Like `inverseFunction`, but instead of returning the function
+-- it returns a mapping from all possible outputs to their possible inputs.
+--
+-- This has the same restrictions of 'inverseFunction'.
+inverseMap :: forall a k. (Bounded a, Enum a, Ord k) => (a -> k) -> Map k a
+inverseMap f = enumerateAll <&> (\a -> (f a, a)) & Map.fromList
+
+-- | All possible values in this enum.
+enumerateAll :: (Enum a, Bounded a) => [a]
+enumerateAll = [minBound .. maxBound]
+
+-- | Create a 'Map' from a list of values, extracting the map key from each value. Like 'Map.fromList'.
+--
+-- Attention: if the key is not unique, the earliest value with the key will be in the map.
+mapFromListOn :: (Ord key) => (a -> key) -> [a] -> Map key a
+mapFromListOn f xs = xs <&> (\x -> (f x, x)) & Map.fromList
+
+-- | Create a 'Map' from a list of values, merging multiple values at the same key with '<>' (left-to-right)
+--
+-- `f` has to extract the key and value. Value must be mergable.
+--
+-- Attention: if the key is not unique, the earliest value with the key will be in the map.
+mapFromListOnMerge :: (Ord key, Semigroup s) => (a -> (key, s)) -> [a] -> Map key s
+mapFromListOnMerge f xs =
+  xs
+    <&> (\x -> f x)
+    & Map.fromListWith
+      -- we have to flip (`<>`) because `Map.fromListWith` merges its values β€œthe other way around”
+      (flip (<>))
+
+-- | If the predicate is true, return the @m@, else 'mempty'.
+--
+-- This can be used (together with `ifExists`) to e.g. create lists with optional elements:
+--
+-- >>> import Data.Monoid (Sum(..))
+--
+-- >>> :{ mconcat [
+--   ifTrue (1 == 1) [1],
+--   [2, 3, 4],
+--   ifTrue False [5],
+-- ]
+-- :}
+-- [1,2,3,4]
+--
+-- Or any other Monoid:
+--
+-- >>> mconcat [ Sum 1, ifTrue (1 == 1) (Sum 2), Sum 3 ]
+
+-- Sum {getSum = 6}
+
+ifTrue :: (Monoid m) => Bool -> m -> m
+ifTrue pred' m = if pred' then m else mempty
+
+-- | If the given @Maybe@ is @Just@, return the result of `f` wrapped in `pure`, else return `mempty`.
+
+-- This can be used (together with `ifTrue`) to e.g. create lists with optional elements:
+--
+-- >>> import Data.Monoid (Sum(..))
+--
+-- >>> :{ mconcat [
+-- unknown command '{'
+--
+-- Or any other Monoid:
+--
+-- >>> mconcat [ Sum 1, ifExists id (Just 2), Sum 3 ]
+-- Sum {getSum = 6}
+
+ifExists :: (Monoid (f b), Applicative f) => (a -> b) -> Maybe a -> f b
+ifExists f m = m & foldMap @Maybe (pure . f)
diff --git a/users/Profpatsch/my-prelude/src/Parse.hs b/users/Profpatsch/my-prelude/src/Parse.hs
new file mode 100644
index 0000000000..65a0b0d39e
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Parse.hs
@@ -0,0 +1,174 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Parse where
+
+import Control.Category qualified
+import Control.Selective (Selective)
+import Data.Error.Tree
+import Data.Functor.Compose
+import Data.List qualified as List
+import Data.Monoid (First (..))
+import Data.Semigroup.Traversable
+import Data.Semigroupoid qualified as Semigroupoid
+import Data.Text qualified as Text
+import FieldParser (FieldParser)
+import FieldParser qualified as Field
+import PossehlAnalyticsPrelude
+import Validation (partitionValidations)
+import Prelude hiding (init, maybe)
+import Prelude qualified
+
+-- | A generic applicative β€œvertical” parser.
+-- Similar to `FieldParser`, but made for parsing whole structures and collect all errors in an `ErrorTree`.
+newtype Parse from to = Parse ((Context, from) -> Validation (NonEmpty ErrorTree) (Context, to))
+  deriving
+    (Functor, Applicative, Selective)
+    via ( Compose
+            ( Compose
+                ((->) (Context, from))
+                (Validation (NonEmpty ErrorTree))
+            )
+            ((,) Context)
+        )
+
+-- | Every parser can add to the context, like e.g. an element parser will add the name of the element it should be parsing.
+-- This should be added to the error message of each parser, with `showContext`.
+newtype Context = Context (Maybe [Text])
+  deriving stock (Show)
+  deriving (Semigroup, Monoid) via (First [Text])
+
+instance Semigroupoid Parse where
+  o p2 p1 = Parse $ \from -> case runParse' p1 from of
+    Failure err -> Failure err
+    Success to1 -> runParse' p2 to1
+
+instance Category Parse where
+  (.) = Semigroupoid.o
+  id = Parse $ \t -> Success t
+
+instance Profunctor Parse where
+  lmap f (Parse p) = Parse $ lmap (second f) p
+  rmap = (<$>)
+
+runParse :: Error -> Parse from to -> from -> Either ErrorTree to
+runParse errMsg parser t =
+  (Context (Just ["$"]), t)
+    & runParse' parser
+    <&> snd
+    & first (nestedMultiError errMsg)
+    & validationToEither
+
+runParse' :: Parse from to -> (Context, from) -> Validation (NonEmpty ErrorTree) (Context, to)
+runParse' (Parse f) from = f from
+
+showContext :: Context -> Text
+showContext (Context context) = context & fromMaybe [] & List.reverse & Text.intercalate "."
+
+addContext :: Text -> Context -> Context
+addContext x (Context mxs) = Context (Just $ x : (mxs & fromMaybe []))
+
+mkParsePushContext :: Text -> ((Context, from) -> Either ErrorTree to) -> Parse from to
+mkParsePushContext toPush f = Parse $ \(ctx, from) -> case f (ctx, from) of
+  Right to -> Success (addContext toPush ctx, to)
+  Left err -> Failure $ singleton err
+
+mkParseNoContext :: (from -> Either ErrorTree to) -> Parse from to
+mkParseNoContext f = Parse $ \(ctx, from) -> case f from of
+  Right to -> Success (ctx, to)
+  Left err -> Failure $ singleton err
+
+-- | Accept only exactly the given value
+exactly :: (Eq from) => (from -> Text) -> from -> Parse from from
+exactly errDisplay from = Parse $ \(ctx, from') ->
+  if from == from'
+    then Success (ctx, from')
+    else Failure $ singleton [fmt|Field has to be exactly {errDisplay from}, was: {errDisplay from'} at {showContext ctx}|]
+
+-- | Make a parser to parse the whole list
+multiple :: Parse a1 a2 -> Parse [a1] [a2]
+multiple inner = dimap nonEmpty (Prelude.maybe [] toList) (maybe $ multipleNE inner)
+
+-- | Make a parser to parse the whole non-empty list
+multipleNE :: Parse from to -> Parse (NonEmpty from) (NonEmpty to)
+multipleNE inner = Parse $ \(ctx, from) ->
+  from
+    & zipIndex
+    & traverse (\(idx, f) -> runParse' inner (ctx, f) & first (singleton . nestedMultiError [fmt|{idx}|]))
+    -- we assume that, since the same parser is used everywhere, the context will be the same as well (TODO: correct?)
+    & second (\((ctx', y) :| ys) -> (ctx', y :| (snd <$> ys)))
+
+-- | Like '(>>>)', but returns the intermediate result alongside the final parse result.
+andParse :: Parse to to2 -> Parse from to -> Parse from (to, to2)
+andParse outer inner = Parse $ \from -> case runParse' inner from of
+  Failure err -> Failure err
+  Success (ctx, to) -> runParse' outer (ctx, to) <&> (second (to,))
+
+-- | Lift a parser into an optional value
+maybe :: Parse from to -> Parse (Maybe from) (Maybe to)
+maybe inner = Parse $ \(ctx, m) -> case m of
+  Nothing -> Success (ctx, Nothing)
+  Just a -> runParse' inner (ctx, a) & second (fmap Just)
+
+-- | Assert that there is exactly one element in the list
+exactlyOne :: Parse [from] from
+exactlyOne = Parse $ \(ctx, xs) -> case xs of
+  [] -> Failure $ singleton [fmt|Expected exactly 1 element, but got 0, at {ctx & showContext}|]
+  [one] -> Success (ctx, one)
+  _more -> Failure $ singleton [fmt|Expected exactly 1 element, but got 2, at {ctx & showContext}|]
+
+-- | Assert that there is exactly zero or one element in the list
+zeroOrOne :: Parse [from] (Maybe from)
+zeroOrOne = Parse $ \(ctx, xs) -> case xs of
+  [] -> Success (ctx, Nothing)
+  [one] -> Success (ctx, Just one)
+  _more -> Failure $ singleton [fmt|Expected exactly 1 element, but got 2, at {ctx & showContext}|]
+
+-- | Find the first element on which the sub-parser succeeds; if there was no match, return all error messages.
+find :: Parse from to -> Parse [from] to
+find inner = Parse $ \(ctx, xs) -> case xs of
+  [] -> failure [fmt|Wanted to get the first sub-parser that succeeds, but there were no elements in the list, at {ctx & showContext}|]
+  (y : ys) -> runParse' (findNE' inner) (ctx, y :| ys)
+
+-- | Find the first element on which the sub-parser succeeds; if there was no match, return all error messages.
+findNE' :: Parse from to -> Parse (NonEmpty from) to
+findNE' inner = Parse $ \(ctx, xs) ->
+  xs
+    <&> (\x -> runParse' inner (ctx, x))
+    & traverse1
+      ( \case
+          Success a -> Left a
+          Failure e -> Right e
+      )
+    & \case
+      Left a -> Success a
+      Right errs ->
+        errs
+          & zipIndex
+          <&> (\(idx, errs') -> nestedMultiError [fmt|{idx}|] errs')
+          & nestedMultiError [fmt|None of these sub-parsers succeeded|]
+          & singleton
+          & Failure
+
+-- | Find all elements on which the sub-parser succeeds; if there was no match, return an empty list
+findAll :: Parse from to -> Parse [from] [to]
+findAll inner = Parse $ \(ctx, xs) ->
+  xs
+    <&> (\x -> runParse' inner (ctx, x))
+    & partitionValidations
+    & \case
+      (_miss, []) ->
+        -- in this case we just arbitrarily forward the original context …
+        Success (ctx, [])
+      (_miss, (hitCtx, hit) : hits) -> Success (hitCtx, hit : (hits <&> snd))
+
+-- | convert a 'FieldParser' into a 'Parse'.
+fieldParser :: FieldParser from to -> Parse from to
+fieldParser fp = Parse $ \(ctx, from) -> case Field.runFieldParser fp from of
+  Right a -> Success (ctx, a)
+  Left err -> Failure $ singleton (singleError err)
+
+zipNonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty (a, b)
+zipNonEmpty (x :| xs) (y :| ys) = (x, y) :| zip xs ys
+
+zipIndex :: NonEmpty b -> NonEmpty (Natural, b)
+zipIndex = zipNonEmpty (1 :| [2 :: Natural ..])
diff --git a/users/Profpatsch/my-prelude/src/Postgres/Decoder.hs b/users/Profpatsch/my-prelude/src/Postgres/Decoder.hs
new file mode 100644
index 0000000000..008b89b4ba
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Postgres/Decoder.hs
@@ -0,0 +1,94 @@
+module Postgres.Decoder where
+
+import Control.Applicative (Alternative)
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Error.Tree
+import Data.Typeable (Typeable)
+import Database.PostgreSQL.Simple (Binary (fromBinary))
+import Database.PostgreSQL.Simple.FromField qualified as PG
+import Database.PostgreSQL.Simple.FromRow qualified as PG
+import Json qualified
+import Label
+import PossehlAnalyticsPrelude
+
+-- | A Decoder of postgres values. Allows embedding more complex parsers (like a 'Json.ParseT').
+newtype Decoder a = Decoder (PG.RowParser a)
+  deriving newtype (Functor, Applicative, Alternative, Monad)
+
+-- | Parse a `bytea` field, equivalent to @Binary ByteString@ but avoids the pitfall of having to use 'Binary'.
+bytea :: Decoder ByteString
+bytea = fromField @(Binary ByteString) <&> (.fromBinary)
+
+-- | Parse a nullable `bytea` field, equivalent to @Binary ByteString@ but avoids the pitfall of having to use 'Binary'.
+byteaMay :: Decoder (Maybe ByteString)
+byteaMay = fromField @(Maybe (Binary ByteString)) <&> fmap (.fromBinary)
+
+-- | Turn any type that implements 'PG.fromField' into a 'Decoder'. Use type applications to prevent accidental conversions:
+--
+-- @
+-- fromField @Text :: Decoder Text
+-- @
+fromField :: PG.FromField a => Decoder a
+fromField = Decoder $ PG.fieldWith PG.fromField
+
+-- | Turn any type that implements 'PG.fromField' into a 'Decoder' and wrap the result into the given 'Label'. Use type applications to prevent accidental conversions:
+--
+-- @
+-- fromField @"myField" @Text :: Decoder (Label "myField" Text)
+-- @
+fromFieldLabel :: forall lbl a. PG.FromField a => Decoder (Label lbl a)
+fromFieldLabel = label @lbl <$> fromField
+
+-- | Parse fields out of a json value returned from the database.
+--
+-- ATTN: The whole json record has to be transferred before it is parsed,
+-- so if you only need a tiny bit of it, use `->` and `->>` in your SQL statement
+-- and return only the fields you need from the query.
+--
+-- In that case pay attention to NULL though:
+--
+-- @
+-- SELECT '{"foo": {}}'::jsonb->>'foo' IS NULL
+-- β†’ TRUE
+-- @
+--
+-- Also note: `->>` will coerce the json value to @text@, regardless of the content.
+-- So the JSON object @{"foo": {}}"@ would be returned as the text: @"{\"foo\": {}}"@.
+json :: Typeable a => Json.ParseT ErrorTree Identity a -> Decoder a
+json parser = Decoder $ PG.fieldWith $ \field bytes -> do
+  val <- PG.fromField @Json.Value field bytes
+  case Json.parseValue parser val of
+    Left err ->
+      PG.returnError
+        PG.ConversionFailed
+        field
+        (err & Json.parseErrorTree "Cannot decode jsonb column" & prettyErrorTree & textToString)
+    Right a -> pure a
+
+-- | Parse fields out of a nullable json value returned from the database.
+--
+-- ATTN: The whole json record has to be transferred before it is parsed,
+-- so if you only need a tiny bit of it, use `->` and `->>` in your SQL statement
+-- and return only the fields you need from the query.
+--
+-- In that case pay attention to NULL though:
+--
+-- @
+-- SELECT '{"foo": {}}'::jsonb->>'foo' IS NULL
+-- β†’ TRUE
+-- @
+--
+-- Also note: `->>` will coerce the json value to @text@, regardless of the content.
+-- So the JSON object @{"foo": {}}"@ would be returned as the text: @"{\"foo\": {}}"@.
+jsonMay :: Typeable a => Json.ParseT ErrorTree Identity a -> Decoder (Maybe a)
+jsonMay parser = Decoder $ PG.fieldWith $ \field bytes -> do
+  val <- PG.fromField @(Maybe Json.Value) field bytes
+  case Json.parseValue parser <$> val of
+    Nothing -> pure Nothing
+    Just (Left err) ->
+      PG.returnError
+        PG.ConversionFailed
+        field
+        (err & Json.parseErrorTree "Cannot decode jsonb column" & prettyErrorTree & textToString)
+    Just (Right a) -> pure (Just a)
diff --git a/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs b/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs
new file mode 100644
index 0000000000..f83a6d7fcf
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs
@@ -0,0 +1,760 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Postgres.MonadPostgres where
+
+import AtLeast (AtLeast)
+import Control.Exception
+import Control.Foldl qualified as Fold
+import Control.Monad.Logger.CallStack (MonadLogger, logDebug, logWarn)
+import Control.Monad.Reader (MonadReader (ask), ReaderT (..))
+import Control.Monad.Trans.Resource
+import Data.Aeson (FromJSON)
+import Data.Error.Tree
+import Data.HashMap.Strict qualified as HashMap
+import Data.Int (Int64)
+import Data.Kind (Type)
+import Data.List qualified as List
+import Data.Pool (Pool)
+import Data.Pool qualified as Pool
+import Data.Text qualified as Text
+import Data.Typeable (Typeable)
+import Database.PostgreSQL.Simple (Connection, FormatError, FromRow, Query, QueryError, ResultError, SqlError, ToRow)
+import Database.PostgreSQL.Simple qualified as PG
+import Database.PostgreSQL.Simple qualified as Postgres
+import Database.PostgreSQL.Simple.FromRow qualified as PG
+import Database.PostgreSQL.Simple.ToField (ToField)
+import Database.PostgreSQL.Simple.ToRow (ToRow (toRow))
+import Database.PostgreSQL.Simple.Types (Query (..))
+import GHC.Records (getField)
+import Label
+import OpenTelemetry.Trace.Core qualified as Otel hiding (inSpan, inSpan')
+import OpenTelemetry.Trace.Monad qualified as Otel
+import PossehlAnalyticsPrelude
+import Postgres.Decoder
+import Postgres.Decoder qualified as Dec
+import Pretty (showPretty)
+import Seconds
+import System.Exit (ExitCode (..))
+import Tool
+import UnliftIO (MonadUnliftIO (withRunInIO))
+import UnliftIO.Process qualified as Process
+import UnliftIO.Resource qualified as Resource
+import Prelude hiding (init, span)
+
+-- | Postgres queries/commands that can be executed within a running transaction.
+--
+-- These are implemented with the @postgresql-simple@ primitives of the same name
+-- and will behave the same unless othewise documented.
+class (Monad m) => MonadPostgres (m :: Type -> Type) where
+  -- | Execute an INSERT, UPDATE, or other SQL query that is not expected to return results.
+
+  -- Returns the number of rows affected.
+  execute ::
+    (ToRow params, Typeable params) =>
+    Query ->
+    params ->
+    Transaction m (Label "numberOfRowsAffected" Natural)
+
+  -- | Execute a multi-row INSERT, UPDATE, or other SQL query that is not expected to return results.
+  --
+  -- Returns the number of rows affected. If the list of parameters is empty,
+  -- this function will simply return 0 without issuing the query to the backend.
+  -- If this is not desired, consider using the 'PG.Values' constructor instead.
+  executeMany ::
+    (ToRow params, Typeable params) =>
+    Query ->
+    NonEmpty params ->
+    Transaction m (Label "numberOfRowsAffected" Natural)
+
+  -- | Execute INSERT ... RETURNING, UPDATE ... RETURNING,
+  -- or other SQL query that accepts multi-row input and is expected to return results.
+  -- Note that it is possible to write query conn "INSERT ... RETURNING ..." ...
+  -- in cases where you are only inserting a single row,
+  -- and do not need functionality analogous to 'executeMany'.
+  --
+  -- If the list of parameters is empty, this function will simply return [] without issuing the query to the backend. If this is not desired, consider using the 'PG.Values' constructor instead.
+  executeManyReturningWith :: (ToRow q) => Query -> NonEmpty q -> Decoder r -> Transaction m [r]
+
+  -- | Run a query, passing parameters and result row parser.
+  queryWith ::
+    (PG.ToRow params, Typeable params, Typeable r) =>
+    PG.Query ->
+    params ->
+    Decoder r ->
+    Transaction m [r]
+
+  -- | Run a query without any parameters and result row parser.
+  queryWith_ ::
+    (Typeable r) =>
+    PG.Query ->
+    Decoder r ->
+    Transaction m [r]
+
+  -- | Run a query, passing parameters, and fold over the resulting rows.
+  --
+  -- This doesn’t have to realize the full list of results in memory,
+  -- rather results are streamed incrementally from the database.
+  --
+  -- When dealing with small results, it may be simpler (and perhaps faster) to use query instead.
+  --
+  -- This fold is _not_ strict. The stream consumer is responsible
+  -- for forcing the evaluation of its result to avoid space leaks.
+  --
+  -- If you can, prefer aggregating in the database itself.
+  foldRowsWithAcc ::
+    (ToRow params, Typeable row, Typeable params) =>
+    Query ->
+    params ->
+    Decoder row ->
+    a ->
+    (a -> row -> Transaction m a) ->
+    Transaction m a
+
+  -- | Run a given transaction in a transaction block, rolling back the transaction
+  -- if any exception (postgres or Haskell Exception) is thrown during execution.
+  --
+  -- Re-throws the exception.
+  --
+  -- Don’t do any long-running things on the Haskell side during a transaction,
+  -- because it will block a database connection and potentially also lock
+  -- database tables from being written or read by other clients.
+  --
+  -- Nonetheless, try to push transactions as far out to the handlers as possible,
+  -- don’t do something like @runTransaction $ query …@, because it will lead people
+  -- to accidentally start nested transactions (the inner transaction is run on a new connections,
+  -- thus can’t see any changes done by the outer transaction).
+  -- Only handlers should run transactions.
+  runTransaction :: Transaction m a -> m a
+
+-- | Run a query, passing parameters. Prefer 'queryWith' if possible.
+query ::
+  forall m params r.
+  (PG.ToRow params, PG.FromRow r, Typeable params, Typeable r, MonadPostgres m) =>
+  PG.Query ->
+  params ->
+  Transaction m [r]
+query qry params = queryWith qry params (Decoder PG.fromRow)
+
+-- | Run a query without any parameters. Prefer 'queryWith' if possible.
+--
+-- TODO: I think(?) this can always be replaced by passing @()@ to 'query', remove?
+query_ ::
+  forall m r.
+  (Typeable r, PG.FromRow r, MonadPostgres m) =>
+  PG.Query ->
+  Transaction m [r]
+query_ qry = queryWith_ qry (Decoder PG.fromRow)
+
+-- TODO: implement via fold, so that the result doesn’t have to be realized in memory
+querySingleRow ::
+  ( MonadPostgres m,
+    ToRow qParams,
+    Typeable qParams,
+    FromRow a,
+    Typeable a,
+    MonadThrow m
+  ) =>
+  Query ->
+  qParams ->
+  Transaction m a
+querySingleRow qry params = do
+  query qry params >>= ensureSingleRow
+
+-- TODO: implement via fold, so that the result doesn’t have to be realized in memory
+querySingleRowWith ::
+  ( MonadPostgres m,
+    ToRow qParams,
+    Typeable qParams,
+    Typeable a,
+    MonadThrow m
+  ) =>
+  Query ->
+  qParams ->
+  Decoder a ->
+  Transaction m a
+querySingleRowWith qry params decoder = do
+  queryWith qry params decoder >>= ensureSingleRow
+
+-- TODO: implement via fold, so that the result doesn’t have to be realized in memory
+querySingleRowMaybe ::
+  ( MonadPostgres m,
+    ToRow qParams,
+    Typeable qParams,
+    FromRow a,
+    Typeable a,
+    MonadThrow m
+  ) =>
+  Query ->
+  qParams ->
+  Transaction m (Maybe a)
+querySingleRowMaybe qry params = do
+  rows <- query qry params
+  case rows of
+    [] -> pure Nothing
+    [one] -> pure (Just one)
+    -- TODO: Should we MonadThrow this here? It’s really an implementation detail of MonadPostgres
+    -- that a database function can error out, should probably handled by the instances.
+    more -> throwM $ SingleRowError {numberOfRowsReturned = (List.length more)}
+
+ensureSingleRow ::
+  (MonadThrow m) =>
+  [a] ->
+  m a
+ensureSingleRow = \case
+  -- TODO: Should we MonadThrow this here? It’s really an implementation detail of MonadPostgres
+  -- that a database function can error out, should probably handled by the instances.
+  [] -> throwM (SingleRowError {numberOfRowsReturned = 0})
+  [one] -> pure one
+  more ->
+    throwM $
+      SingleRowError
+        { numberOfRowsReturned =
+            -- TODO: this is VERY bad, because it requires to parse the full database output, even if there’s 10000000000 elements
+            List.length more
+        }
+
+ensureNoneOrSingleRow ::
+  (MonadThrow m) =>
+  [a] ->
+  m (Maybe a)
+ensureNoneOrSingleRow = \case
+  -- TODO: Should we MonadThrow this here? It’s really an implementation detail of MonadPostgres
+  -- that a database function can error out, should probably handled by the instances.
+  [] -> pure Nothing
+  [one] -> pure $ Just one
+  more ->
+    throwM $
+      SingleRowError
+        { numberOfRowsReturned =
+            -- TODO: this is VERY bad, because it requires to parse the full database output, even if there’s 10000000000 elements
+            List.length more
+        }
+
+-- | Run a query, passing parameters, and fold over the resulting rows.
+--
+-- This doesn’t have to realize the full list of results in memory,
+-- rather results are streamed incrementally from the database.
+--
+-- When dealing with small results, it may be simpler (and perhaps faster) to use query instead.
+--
+-- The results are folded strictly by the 'Fold.Fold' that is passed.
+--
+-- If you can, prefer aggregating in the database itself.
+foldRowsWith ::
+  forall row params m b.
+  ( MonadPostgres m,
+    PG.ToRow params,
+    Typeable row,
+    Typeable params
+  ) =>
+  PG.Query ->
+  params ->
+  Decoder row ->
+  Fold.Fold row b ->
+  Transaction m b
+foldRowsWith qry params decoder = Fold.purely f
+  where
+    f :: forall x. (x -> row -> x) -> x -> (x -> b) -> Transaction m b
+    f acc init extract = do
+      x <- foldRowsWithAcc qry params decoder init (\a r -> pure $ acc a r)
+      pure $ extract x
+
+newtype Transaction m a = Transaction {unTransaction :: (ReaderT Connection m a)}
+  deriving newtype
+    ( Functor,
+      Applicative,
+      Monad,
+      MonadThrow,
+      MonadLogger,
+      MonadIO,
+      MonadUnliftIO,
+      MonadTrans,
+      Otel.MonadTracer
+    )
+
+-- | [Resource Pool](http://hackage.haskell.org/package/resource-pool-0.2.3.2/docs/Data-Pool.html) configuration.
+data PoolingInfo = PoolingInfo
+  { -- | Minimal amount of resources that are
+    --   always available.
+    numberOfStripes :: AtLeast 1 Int,
+    -- | Time after which extra resources
+    --   (above minimum) can stay in the pool
+    --   without being used.
+    unusedResourceOpenTime :: Seconds,
+    -- | Max number of resources that can be
+    --   in the Pool at any time
+    maxOpenResourcesAcrossAllStripes :: AtLeast 1 Int
+  }
+  deriving stock (Generic, Eq, Show)
+  deriving anyclass (FromJSON)
+
+initMonadPostgres ::
+  (Text -> IO ()) ->
+  -- | Info describing the connection to the Postgres DB
+  Postgres.ConnectInfo ->
+  -- | Configuration info for pooling attributes
+  PoolingInfo ->
+  -- | Created Postgres connection pool
+  ResourceT IO (Pool Postgres.Connection)
+initMonadPostgres logInfoFn connectInfo poolingInfo = do
+  (_releaseKey, connPool) <-
+    Resource.allocate
+      (logInfoFn "Creating Postgres Connection Pool" >> createPGConnPool)
+      (\pool -> logInfoFn "Destroying Postgres Connection Pool" >> destroyPGConnPool pool)
+  pure connPool
+  where
+    -- \| Create a Postgres connection pool
+    createPGConnPool ::
+      IO (Pool Postgres.Connection)
+    createPGConnPool =
+      Pool.newPool $
+        Pool.defaultPoolConfig
+          {- resource init action -} poolCreateResource
+          {- resource destruction -} poolfreeResource
+          ( poolingInfo.unusedResourceOpenTime.unSeconds
+              & fromIntegral @Natural @Double
+          )
+          (poolingInfo.maxOpenResourcesAcrossAllStripes.unAtLeast)
+      where
+        poolCreateResource = Postgres.connect connectInfo
+        poolfreeResource = Postgres.close
+
+    -- \| Destroy a Postgres connection pool
+    destroyPGConnPool ::
+      -- \| Pool to be destroyed
+      (Pool Postgres.Connection) ->
+      IO ()
+    destroyPGConnPool p = Pool.destroyAllResources p
+
+-- | Improve a possible error message, by adding some context to it.
+--
+-- The given Exception type is caught, 'show'n and pretty-printed.
+--
+-- In case we get an `IOError`, we display it in a reasonable fashion.
+addErrorInformation ::
+  forall exc a.
+  (Exception exc) =>
+  Text.Text ->
+  IO a ->
+  IO a
+addErrorInformation msg io =
+  io
+    & try @exc
+    <&> first (showPretty >>> newError >>> errorContext msg)
+    & try @IOError
+    <&> first (showToError >>> errorContext "IOError" >>> errorContext msg)
+    <&> join @(Either Error)
+    >>= unwrapIOError
+
+-- | Catch any Postgres exception that gets thrown,
+-- print the query that was run and the query parameters,
+-- then rethrow inside an 'Error'.
+handlePGException ::
+  forall a params tools m.
+  ( ToRow params,
+    MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  tools ->
+  Text ->
+  Query ->
+  -- | Depending on whether we used `format` or `formatMany`.
+  Either params (NonEmpty params) ->
+  IO a ->
+  Transaction m a
+handlePGException tools queryType query' params io = do
+  withRunInIO $ \unliftIO ->
+    io
+      `catches` [ Handler $ unliftIO . logQueryException @SqlError,
+                  Handler $ unliftIO . logQueryException @QueryError,
+                  Handler $ unliftIO . logQueryException @ResultError,
+                  Handler $ unliftIO . logFormatException
+                ]
+  where
+    -- TODO: use throwInternalError here (after pulling it into the MonadPostgres class)
+    throwAsError = unwrapIOError . Left . newError
+    throwErr err = liftIO $ throwAsError $ prettyErrorTree $ nestedMultiError "A Postgres query failed" err
+    logQueryException :: (Exception e) => e -> Transaction m a
+    logQueryException exc = do
+      formattedQuery <- case params of
+        Left one -> pgFormatQuery' tools query' one
+        Right many -> pgFormatQueryMany' tools query' many
+      throwErr
+        ( singleError [fmt|Query Type: {queryType}|]
+            :| [ nestedError "Exception" (exc & showPretty & newError & singleError),
+                 nestedError "Query" (formattedQuery & newError & singleError)
+               ]
+        )
+    logFormatException :: FormatError -> Transaction m a
+    logFormatException fe = throwErr (fe & showPretty & newError & singleError & singleton)
+
+-- | Perform a Postgres action within a transaction
+withPGTransaction ::
+  -- | Postgres connection pool to be used for the action
+  (Pool Postgres.Connection) ->
+  -- | DB-action to be performed
+  (Postgres.Connection -> IO a) ->
+  -- | Result of the DB-action
+  IO a
+withPGTransaction connPool f =
+  Pool.withResource
+    connPool
+    (\conn -> Postgres.withTransaction conn (f conn))
+
+runPGTransactionImpl ::
+  (MonadUnliftIO m) =>
+  m (Pool Postgres.Connection) ->
+  Transaction m a ->
+  m a
+{-# INLINE runPGTransactionImpl #-}
+runPGTransactionImpl zoom (Transaction transaction) = do
+  pool <- zoom
+  withRunInIO $ \unliftIO ->
+    withPGTransaction pool $ \conn -> do
+      unliftIO $ runReaderT transaction conn
+
+executeImpl ::
+  (ToRow params, MonadUnliftIO m, MonadLogger m, HasField "pgFormat" tools Tool, Otel.MonadTracer m) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  params ->
+  Transaction m (Label "numberOfRowsAffected" Natural)
+{-# INLINE executeImpl #-}
+executeImpl zoomTools zoomDebugLogDatabaseQueries qry params =
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasSingleParam params)
+    conn <- Transaction ask
+    PG.execute conn qry params
+      & handlePGException tools "execute" qry (Left params)
+      >>= toNumberOfRowsAffected "executeImpl"
+
+executeImpl_ ::
+  (MonadUnliftIO m, MonadLogger m, HasField "pgFormat" tools Tool, Otel.MonadTracer m) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  Transaction m (Label "numberOfRowsAffected" Natural)
+{-# INLINE executeImpl_ #-}
+executeImpl_ zoomTools zoomDebugLogDatabaseQueries qry =
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled @() tools span logDatabaseQueries qry HasNoParams
+    conn <- Transaction ask
+    PG.execute_ conn qry
+      & handlePGException tools "execute_" qry (Left ())
+      >>= toNumberOfRowsAffected "executeImpl_"
+
+executeManyImpl ::
+  (ToRow params, MonadUnliftIO m, MonadLogger m, HasField "pgFormat" tools Tool, Otel.MonadTracer m) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  NonEmpty params ->
+  Transaction m (Label "numberOfRowsAffected" Natural)
+executeManyImpl zoomTools zoomDebugLogDatabaseQueries qry params =
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasMultiParams params)
+    conn <- Transaction ask
+    PG.executeMany conn qry (params & toList)
+      & handlePGException tools "executeMany" qry (Right params)
+      >>= toNumberOfRowsAffected "executeManyImpl"
+
+toNumberOfRowsAffected :: (MonadIO m) => Text -> Int64 -> m (Label "numberOfRowsAffected" Natural)
+toNumberOfRowsAffected functionName i64 =
+  i64
+    & intToNatural
+    & annotate [fmt|{functionName}: postgres returned a negative number of rows affected: {i64}|]
+    -- we throw this directly in IO here, because we don’t want to e.g. have to propagate MonadThrow through user code (it’s an assertion)
+    & unwrapIOError
+    & liftIO
+    <&> label @"numberOfRowsAffected"
+
+executeManyReturningWithImpl ::
+  (ToRow params, MonadUnliftIO m, MonadLogger m, HasField "pgFormat" tools Tool, Otel.MonadTracer m) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  NonEmpty params ->
+  Decoder r ->
+  Transaction m [r]
+{-# INLINE executeManyReturningWithImpl #-}
+executeManyReturningWithImpl zoomTools zoomDebugLogDatabaseQueries qry params (Decoder fromRow) = do
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasMultiParams params)
+    conn <- Transaction ask
+    PG.returningWith fromRow conn qry (params & toList)
+      & handlePGException tools "executeManyReturning" qry (Right params)
+
+foldRowsWithAccImpl ::
+  ( ToRow params,
+    MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool,
+    Otel.MonadTracer m
+  ) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  params ->
+  Decoder row ->
+  a ->
+  (a -> row -> Transaction m a) ->
+  Transaction m a
+{-# INLINE foldRowsWithAccImpl #-}
+foldRowsWithAccImpl zoomTools zoomDebugLogDatabaseQueries qry params (Decoder rowParser) accumulator f = do
+  Otel.inSpan' "Postgres Query (foldRowsWithAcc)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasSingleParam params)
+    conn <- Transaction ask
+    withRunInIO
+      ( \runInIO ->
+          do
+            PG.foldWithOptionsAndParser
+              PG.defaultFoldOptions
+              rowParser
+              conn
+              qry
+              params
+              accumulator
+              (\acc row -> runInIO $ f acc row)
+              & handlePGException tools "fold" qry (Left params)
+              & runInIO
+      )
+
+pgFormatQueryNoParams' ::
+  (MonadIO m, MonadLogger m, HasField "pgFormat" tools Tool) =>
+  tools ->
+  Query ->
+  Transaction m Text
+pgFormatQueryNoParams' tools q =
+  lift $ pgFormatQueryByteString tools q.fromQuery
+
+pgFormatQuery ::
+  (ToRow params, MonadIO m) =>
+  Query ->
+  params ->
+  Transaction m ByteString
+pgFormatQuery qry params = Transaction $ do
+  conn <- ask
+  liftIO $ PG.formatQuery conn qry params
+
+pgFormatQueryMany ::
+  (MonadIO m, ToRow params) =>
+  Query ->
+  NonEmpty params ->
+  Transaction m ByteString
+pgFormatQueryMany qry params = Transaction $ do
+  conn <- ask
+  liftIO $
+    PG.formatMany
+      conn
+      qry
+      ( params
+          -- upstream is partial on empty list, see https://github.com/haskellari/postgresql-simple/issues/129
+          & toList
+      )
+
+queryWithImpl ::
+  ( ToRow params,
+    MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool,
+    Otel.MonadTracer m
+  ) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  params ->
+  Decoder r ->
+  Transaction m [r]
+{-# INLINE queryWithImpl #-}
+queryWithImpl zoomTools zoomDebugLogDatabaseQueries qry params (Decoder fromRow) = do
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasSingleParam params)
+    conn <- Transaction ask
+    PG.queryWith fromRow conn qry params
+      & handlePGException tools "query" qry (Left params)
+
+queryWithImpl_ ::
+  ( MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  m tools ->
+  Query ->
+  Decoder r ->
+  Transaction m [r]
+{-# INLINE queryWithImpl_ #-}
+queryWithImpl_ zoomTools qry (Decoder fromRow) = do
+  tools <- lift @Transaction zoomTools
+  conn <- Transaction ask
+  liftIO (PG.queryWith_ fromRow conn qry)
+    & handlePGException tools "query" qry (Left ())
+
+data SingleRowError = SingleRowError
+  { -- | How many columns were actually returned by the query
+    numberOfRowsReturned :: Int
+  }
+  deriving stock (Show)
+
+instance Exception SingleRowError where
+  displayException (SingleRowError {..}) = [fmt|Single row expected from SQL query result, {numberOfRowsReturned} rows were returned instead."|]
+
+pgFormatQuery' ::
+  ( MonadIO m,
+    ToRow params,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  tools ->
+  Query ->
+  params ->
+  Transaction m Text
+pgFormatQuery' tools q p =
+  pgFormatQuery q p
+    >>= lift . pgFormatQueryByteString tools
+
+pgFormatQueryMany' ::
+  ( MonadIO m,
+    ToRow params,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  tools ->
+  Query ->
+  NonEmpty params ->
+  Transaction m Text
+pgFormatQueryMany' tools q p =
+  pgFormatQueryMany q p
+    >>= lift . pgFormatQueryByteString tools
+
+-- | Read the executable name "pg_format"
+postgresToolsParser :: ToolParserT IO (Label "pgFormat" Tool)
+postgresToolsParser = label @"pgFormat" <$> readTool "pg_format"
+
+pgFormatQueryByteString ::
+  ( MonadIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  tools ->
+  ByteString ->
+  m Text
+pgFormatQueryByteString tools queryBytes = do
+  do
+    (exitCode, stdout, stderr) <-
+      Process.readProcessWithExitCode
+        tools.pgFormat.toolPath
+        ["-"]
+        (queryBytes & bytesToTextUtf8Lenient & textToString)
+    case exitCode of
+      ExitSuccess -> pure (stdout & stringToText)
+      ExitFailure status -> do
+        logWarn [fmt|pg_format failed with status {status} while formatting the query, using original query string. Is there a syntax error?|]
+        logDebug
+          ( prettyErrorTree
+              ( nestedMultiError
+                  "pg_format output"
+                  ( nestedError "stdout" (singleError (stdout & stringToText & newError))
+                      :| [(nestedError "stderr" (singleError (stderr & stringToText & newError)))]
+                  )
+              )
+          )
+        logDebug [fmt|pg_format stdout: stderr|]
+        pure (queryBytes & bytesToTextUtf8Lenient)
+
+data DebugLogDatabaseQueries
+  = -- | Do not log the database queries
+    DontLogDatabaseQueries
+  | -- | Log the database queries as debug output;
+    LogDatabaseQueries
+  | -- | Log the database queries as debug output and additionally the EXPLAIN output (from the query analyzer, not the actual values after execution cause that’s a bit harder to do)
+    LogDatabaseQueriesAndExplain
+  deriving stock (Show, Enum, Bounded)
+
+data HasQueryParams param
+  = HasNoParams
+  | HasSingleParam param
+  | HasMultiParams (NonEmpty param)
+
+-- | Log the postgres query depending on the given setting
+traceQueryIfEnabled ::
+  ( ToRow params,
+    MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool,
+    Otel.MonadTracer m
+  ) =>
+  tools ->
+  Otel.Span ->
+  DebugLogDatabaseQueries ->
+  Query ->
+  HasQueryParams params ->
+  Transaction m ()
+traceQueryIfEnabled tools span logDatabaseQueries qry params = do
+  -- In case we have query logging enabled, we want to do that
+  let formattedQuery = case params of
+        HasNoParams -> pgFormatQueryNoParams' tools qry
+        HasSingleParam p -> pgFormatQuery' tools qry p
+        HasMultiParams ps -> pgFormatQueryMany' tools qry ps
+  let doLog errs =
+        Otel.addAttributes
+          span
+          $ HashMap.fromList
+          $ ( ("_.postgres.query", Otel.toAttribute @Text errs.query)
+                : ( errs.explain
+                      & foldMap
+                        ( \ex ->
+                            [("_.postgres.explain", Otel.toAttribute @Text ex)]
+                        )
+                  )
+            )
+  let doExplain = do
+        q <- formattedQuery
+        Otel.inSpan "Postgres EXPLAIN Query" Otel.defaultSpanArguments $ do
+          queryWithImpl_
+            (pure tools)
+            ( "EXPLAIN "
+                <> (
+                     -- TODO: this is not nice, but the only way to get the `executeMany` form to work with this
+                     -- because we need the query with all elements already interpolated.
+                     Query (q & textToBytesUtf8)
+                   )
+            )
+            (Dec.fromField @Text)
+            <&> Text.intercalate "\n"
+  case logDatabaseQueries of
+    DontLogDatabaseQueries -> pure ()
+    LogDatabaseQueries -> do
+      q <- formattedQuery
+      doLog (T2 (label @"query" q) (label @"explain" Nothing))
+    LogDatabaseQueriesAndExplain -> do
+      q <- formattedQuery
+      -- XXX: stuff like `CREATE SCHEMA` cannot be EXPLAINed, so we should catch exceptions here
+      -- and just ignore anything that errors (if it errors because of a problem with the query, it would have been caught by the query itself.
+      ex <- doExplain
+      doLog (T2 (label @"query" q) (label @"explain" (Just ex)))
+
+instance (ToField t1) => ToRow (Label l1 t1) where
+  toRow t2 = toRow $ PG.Only $ getField @l1 t2
+
+instance (ToField t1, ToField t2) => ToRow (T2 l1 t1 l2 t2) where
+  toRow t2 = toRow (getField @l1 t2, getField @l2 t2)
+
+instance (ToField t1, ToField t2, ToField t3) => ToRow (T3 l1 t1 l2 t2 l3 t3) where
+  toRow t3 = toRow (getField @l1 t3, getField @l2 t3, getField @l3 t3)
diff --git a/users/Profpatsch/my-prelude/src/Pretty.hs b/users/Profpatsch/my-prelude/src/Pretty.hs
new file mode 100644
index 0000000000..d9d4ce132b
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Pretty.hs
@@ -0,0 +1,108 @@
+module Pretty
+  ( -- * Pretty printing for error messages
+    Err,
+    showPretty,
+    showPrettyJson,
+    showedStringPretty,
+    printPretty,
+    printShowedStringPretty,
+    -- constructors hidden
+    prettyErrs,
+    message,
+    messageString,
+    pretty,
+    prettyString,
+    hscolour',
+  )
+where
+
+import Data.Aeson qualified as Json
+import Data.Aeson.Encode.Pretty qualified as Aeson.Pretty
+import Data.List qualified as List
+import Data.Text.Lazy.Builder qualified as Text.Builder
+import Language.Haskell.HsColour
+  ( Output (TTYg),
+    hscolour,
+  )
+import Language.Haskell.HsColour.ANSI (TerminalType (..))
+import Language.Haskell.HsColour.Colourise
+  ( defaultColourPrefs,
+  )
+import PossehlAnalyticsPrelude
+import System.Console.ANSI (setSGRCode)
+import System.Console.ANSI.Types
+  ( Color (Red),
+    ColorIntensity (Dull),
+    ConsoleLayer (Foreground),
+    SGR (Reset, SetColor),
+  )
+import Text.Nicify (nicify)
+
+-- | Print any 'Show'able type to stderr, formatted nicely and in color. Very helpful for debugging.
+printPretty :: (Show a) => a -> IO ()
+printPretty a =
+  a & showPretty & putStderrLn
+
+showPretty :: (Show a) => a -> Text
+showPretty a = a & pretty & (: []) & prettyErrs & stringToText
+
+-- | Pretty-print a string that was produced by `show` to stderr, formatted nicely and in color.
+printShowedStringPretty :: String -> IO ()
+printShowedStringPretty s = s & showedStringPretty & putStderrLn
+
+-- | Pretty-print a string that was produced by `show`
+showedStringPretty :: String -> Text
+showedStringPretty s = s & ErrPrettyString & (: []) & prettyErrs & stringToText
+
+showPrettyJson :: Json.Value -> Text
+showPrettyJson val =
+  val
+    & Aeson.Pretty.encodePrettyToTextBuilder
+    & Text.Builder.toLazyText
+    & toStrict
+
+-- | Display a list of 'Err's as a colored error message
+-- and abort the test.
+prettyErrs :: [Err] -> String
+prettyErrs errs = res
+  where
+    res = List.intercalate "\n" $ map one errs
+    one = \case
+      ErrMsg s -> color Red s
+      ErrPrettyString s -> prettyShowString s
+    -- Pretty print a String that was produced by 'show'
+    prettyShowString :: String -> String
+    prettyShowString = hscolour' . nicify
+
+-- | Small DSL for pretty-printing errors
+data Err
+  = -- | Message to display in the error
+    ErrMsg String
+  | -- | Pretty print a String that was produced by 'show'
+    ErrPrettyString String
+
+-- | Plain message to display, as 'Text'
+message :: Text -> Err
+message = ErrMsg . textToString
+
+-- | Plain message to display, as 'String'
+messageString :: String -> Err
+messageString = ErrMsg
+
+-- | Any 'Show'able to pretty print
+pretty :: (Show a) => a -> Err
+pretty x = ErrPrettyString $ show x
+
+-- | Pretty print a String that was produced by 'show'
+prettyString :: String -> Err
+prettyString s = ErrPrettyString s
+
+-- Prettifying Helpers, mostly stolen from
+-- https://hackage.haskell.org/package/hspec-expectations-pretty-diff-0.7.2.5/docs/src/Test.Hspec.Expectations.Pretty.html#prettyColor
+
+hscolour' :: String -> String
+hscolour' =
+  hscolour (TTYg Ansi16Colour) defaultColourPrefs False False "" False
+
+color :: Color -> String -> String
+color c s = setSGRCode [SetColor Foreground Dull c] ++ s ++ setSGRCode [Reset]
diff --git a/users/Profpatsch/my-prelude/src/Seconds.hs b/users/Profpatsch/my-prelude/src/Seconds.hs
new file mode 100644
index 0000000000..8d05f30be8
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Seconds.hs
@@ -0,0 +1,55 @@
+module Seconds where
+
+import Data.Aeson (FromJSON)
+import Data.Aeson qualified as Json
+import Data.Aeson.Types (FromJSON (parseJSON))
+import Data.Scientific
+import Data.Time (NominalDiffTime)
+import FieldParser
+import FieldParser qualified as Field
+import GHC.Natural (naturalToInteger)
+import PossehlAnalyticsPrelude
+
+-- | A natural number of seconds.
+newtype Seconds = Seconds {unSeconds :: Natural}
+  deriving stock (Eq, Show)
+
+-- | Parse a decimal number as a number of seconds
+textToSeconds :: FieldParser Text Seconds
+textToSeconds = Seconds <$> Field.decimalNatural
+
+scientificToSeconds :: FieldParser Scientific Seconds
+scientificToSeconds =
+  ( Field.boundedScientificIntegral @Int "Number of seconds"
+      >>> Field.integralToNatural
+  )
+    & rmap Seconds
+
+-- Microseconds, represented internally with a 64 bit Int
+newtype MicrosecondsInt = MicrosecondsInt {unMicrosecondsInt :: Int}
+  deriving stock (Eq, Show)
+
+-- | Try to fit a number of seconds into a MicrosecondsInt
+secondsToMicrosecondsInt :: FieldParser Seconds MicrosecondsInt
+secondsToMicrosecondsInt =
+  lmap
+    (\sec -> naturalToInteger sec.unSeconds * 1_000_000)
+    (Field.bounded "Could not fit into an Int after multiplying with 1_000_000 (seconds to microseconds)")
+    & rmap MicrosecondsInt
+
+secondsToNominalDiffTime :: Seconds -> NominalDiffTime
+secondsToNominalDiffTime sec =
+  sec.unSeconds
+    & naturalToInteger
+    & fromInteger @NominalDiffTime
+
+instance FromJSON Seconds where
+  parseJSON = Field.toParseJSON jsonNumberToSeconds
+
+-- | Parse a json number as a number of seconds.
+jsonNumberToSeconds :: FieldParser' Error Json.Value Seconds
+jsonNumberToSeconds = Field.jsonNumber >>> scientificToSeconds
+
+-- | Return the number of seconds in a week
+secondsInAWeek :: Seconds
+secondsInAWeek = Seconds (3600 * 24 * 7)
diff --git a/users/Profpatsch/my-prelude/src/Test.hs b/users/Profpatsch/my-prelude/src/Test.hs
new file mode 100644
index 0000000000..862ee16c25
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Test.hs
@@ -0,0 +1,115 @@
+{-# LANGUAGE LambdaCase #-}
+
+{- Generate Test suites.
+
+Restricted version of hspec, introduction: http://hspec.github.io/getting-started.html
+-}
+module Test
+  ( Spec,
+    runTest,
+    testMain,
+
+    -- * Structure
+    describe,
+    it,
+
+    -- * Expectations
+    Expectation,
+    testOk,
+    testErr,
+    shouldBe,
+    shouldNotBe,
+    shouldSatisfy,
+    shouldNotSatisfy,
+
+    -- * Setup & Teardown (hooks http://hspec.github.io/writing-specs.html#using-hooks)
+    before,
+    before_,
+    beforeWith,
+    beforeAll,
+    beforeAll_,
+    beforeAllWith,
+    after,
+    after_,
+    afterAll,
+    afterAll_,
+    around,
+    around_,
+    aroundWith,
+    aroundAll,
+    aroundAllWith,
+
+    -- * Common helpful predicates (use with 'shouldSatisfy')
+    isRight,
+    isLeft,
+
+    -- * Pretty printing of errors
+    errColored,
+    module Pretty,
+  )
+where
+
+-- export more expectations if needed
+
+import Data.Either
+  ( isLeft,
+    isRight,
+  )
+import Pretty
+import Test.Hspec
+  ( Expectation,
+    HasCallStack,
+    Spec,
+    after,
+    afterAll,
+    afterAll_,
+    after_,
+    around,
+    aroundAll,
+    aroundAllWith,
+    aroundWith,
+    around_,
+    before,
+    beforeAll,
+    beforeAllWith,
+    beforeAll_,
+    beforeWith,
+    before_,
+    describe,
+    hspec,
+    it,
+  )
+import Test.Hspec.Expectations.Pretty
+  ( expectationFailure,
+    shouldBe,
+    shouldNotBe,
+    shouldNotSatisfy,
+    shouldSatisfy,
+  )
+
+-- | Run a test directly (e.g. from the repl)
+runTest :: Spec -> IO ()
+runTest = hspec
+
+-- | Run a testsuite
+testMain ::
+  -- | Name of the test suite
+  String ->
+  -- | The tests in this test module
+  Spec ->
+  IO ()
+testMain testSuiteName tests = hspec $ describe testSuiteName tests
+
+-- | test successful
+testOk :: Expectation
+testOk = pure ()
+
+-- | Abort the test with an error message.
+-- If you want to display a Haskell type, use `errColored`.
+testErr :: HasCallStack => String -> Expectation
+testErr = expectationFailure
+
+-- | Display a list of 'Err's as a colored error message
+-- and abort the test.
+errColored :: [Pretty.Err] -> Expectation
+errColored = testErr . Pretty.prettyErrs
diff --git a/users/Profpatsch/my-prelude/src/Tool.hs b/users/Profpatsch/my-prelude/src/Tool.hs
new file mode 100644
index 0000000000..b773f4444e
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Tool.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Tool where
+
+import Data.Error.Tree
+import Label
+import PossehlAnalyticsPrelude
+import System.Environment qualified as Env
+import System.Exit qualified as Exit
+import System.FilePath ((</>))
+import System.Posix qualified as Posix
+import ValidationParseT
+
+data Tool = Tool
+  { -- | absolute path to the executable
+    toolPath :: FilePath
+  }
+  deriving stock (Show)
+
+-- | Reads all tools from the @toolsEnvVar@ variable or aborts.
+readTools ::
+  Label "toolsEnvVar" Text ->
+  -- | Parser for Tools we bring with us at build time.
+  --
+  -- These are executables that we need available, and that we have to ship with the distribution of @pa-cli@.
+  ToolParserT IO tools ->
+  IO tools
+readTools env toolParser =
+  Env.lookupEnv (env.toolsEnvVar & textToString) >>= \case
+    Nothing -> do
+      Exit.die [fmt|Please set {env.toolsEnvVar} to a directory with all tools we need (see `Tools` in the code).|]
+    Just toolsDir ->
+      (Posix.fileExist toolsDir & ifTrueOrErr () [fmt|{env.toolsEnvVar} directory does not exist: {toolsDir}|])
+        & thenValidateM
+          ( \() ->
+              (Posix.getFileStatus toolsDir <&> Posix.isDirectory)
+                & ifTrueOrErr () [fmt|{env.toolsEnvVar} does not point to a directory: {toolsDir}|]
+          )
+        & thenValidateM
+          (\() -> toolParser.unToolParser toolsDir)
+        <&> first (errorTree [fmt|Could not find all tools in {env.toolsEnvVar}|])
+        >>= \case
+          Failure err -> Exit.die (err & prettyErrorTree & textToString)
+          Success t -> pure t
+
+newtype ToolParserT m a = ToolParserT
+  { unToolParser ::
+      FilePath ->
+      m (Validation (NonEmpty Error) a)
+  }
+  deriving
+    (Functor, Applicative)
+    via (ValidationParseT FilePath m)
+
+-- | Given a file path and the name of the tool executable, see whether it is an executable and return its full path.
+readTool :: Text -> ToolParserT IO Tool
+readTool exeName = ToolParserT $ \toolDir -> do
+  let toolPath :: FilePath = toolDir </> (exeName & textToString)
+  let read' = True
+  let write = False
+  let exec = True
+  Posix.fileExist toolPath
+    & ifTrueOrErr () [fmt|Tool does not exist: {toolPath}|]
+    & thenValidateM
+      ( \() ->
+          Posix.fileAccess toolPath read' write exec
+            & ifTrueOrErr (Tool {..}) [fmt|Tool is not readable/executable: {toolPath}|]
+      )
+
+-- | helper
+ifTrueOrErr :: (Functor f) => a -> Text -> f Bool -> f (Validation (NonEmpty Error) a)
+ifTrueOrErr true err io =
+  io <&> \case
+    True -> Success true
+    False -> Failure $ singleton $ newError err
diff --git a/users/Profpatsch/my-prelude/src/ValidationParseT.hs b/users/Profpatsch/my-prelude/src/ValidationParseT.hs
new file mode 100644
index 0000000000..593b7ebf39
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/ValidationParseT.hs
@@ -0,0 +1,16 @@
+module ValidationParseT where
+
+import Control.Selective (Selective)
+import Data.Functor.Compose (Compose (..))
+import PossehlAnalyticsPrelude
+
+-- | A simple way to create an Applicative parser that parses from some environment.
+--
+-- Use with DerivingVia. Grep codebase for examples.
+newtype ValidationParseT env m a = ValidationParseT {unValidationParseT :: env -> m (Validation (NonEmpty Error) a)}
+  deriving
+    (Functor, Applicative, Selective)
+    via ( Compose
+            ((->) env)
+            (Compose m (Validation (NonEmpty Error)))
+        )
diff --git a/users/Profpatsch/my-webstuff/default.nix b/users/Profpatsch/my-webstuff/default.nix
new file mode 100644
index 0000000000..0067235be2
--- /dev/null
+++ b/users/Profpatsch/my-webstuff/default.nix
@@ -0,0 +1,27 @@
+{ depot, pkgs, lib, ... }:
+
+pkgs.haskellPackages.mkDerivation {
+  pname = "my-webstuff";
+  version = "0.0.1-unreleased";
+
+  src = depot.users.Profpatsch.exactSource ./. [
+    ./my-webstuff.cabal
+    ./src/Multipart2.hs
+  ];
+
+  isLibrary = true;
+
+  libraryHaskellDepends = [
+    depot.users.Profpatsch.my-prelude
+    pkgs.haskellPackages.dlist
+    pkgs.haskellPackages.monad-logger
+    pkgs.haskellPackages.pa-error-tree
+    pkgs.haskellPackages.pa-field-parser
+    pkgs.haskellPackages.pa-prelude
+    pkgs.haskellPackages.selective
+    pkgs.haskellPackages.wai-extra
+  ];
+
+  license = lib.licenses.mit;
+
+}
diff --git a/users/Profpatsch/my-webstuff/my-webstuff.cabal b/users/Profpatsch/my-webstuff/my-webstuff.cabal
new file mode 100644
index 0000000000..fb42d9f6a5
--- /dev/null
+++ b/users/Profpatsch/my-webstuff/my-webstuff.cabal
@@ -0,0 +1,72 @@
+cabal-version:      3.0
+name:               my-webstuff
+version:            0.0.1.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+    hs-source-dirs: src
+    exposed-modules:
+      Multipart2
+
+    build-depends:
+       base >=4.15 && <5
+     , my-prelude
+     , pa-prelude
+     , pa-label
+     , pa-error-tree
+     , pa-field-parser
+     , bytestring
+     , monad-logger
+     , dlist
+     , selective
+     , wai
+     , wai-extra
diff --git a/users/Profpatsch/my-webstuff/src/Multipart2.hs b/users/Profpatsch/my-webstuff/src/Multipart2.hs
new file mode 100644
index 0000000000..5c283a3c1b
--- /dev/null
+++ b/users/Profpatsch/my-webstuff/src/Multipart2.hs
@@ -0,0 +1,220 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Multipart2 where
+
+import Control.Monad.Logger (MonadLogger)
+import Control.Selective (Selective)
+import Data.ByteString.Lazy qualified as Lazy
+import Data.DList (DList)
+import Data.DList qualified as DList
+import Data.Error.Tree
+import Data.Functor.Compose
+import Data.List qualified as List
+import FieldParser
+import Label
+import Network.Wai qualified as Wai
+import Network.Wai.Parse qualified as Wai
+import PossehlAnalyticsPrelude
+import ValidationParseT
+
+data FormFields = FormFields
+  { inputs :: [Wai.Param],
+    files :: [MultipartFile Lazy.ByteString]
+  }
+
+-- | A parser for a HTTP multipart form (a form sent by the browser)
+newtype MultipartParseT backend m a = MultipartParseT
+  { unMultipartParseT ::
+      FormFields ->
+      m (Validation (NonEmpty Error) a)
+  }
+  deriving
+    (Functor, Applicative, Selective)
+    via (ValidationParseT FormFields m)
+
+-- | After parsing a form, either we get the result or a list of form fields that failed
+newtype FormValidation a
+  = FormValidation
+      (DList FormValidationResult, Maybe a)
+  deriving (Functor, Applicative, Selective) via (Compose ((,) (DList FormValidationResult)) Maybe)
+  deriving stock (Show)
+
+data FormValidationResult = FormValidationResult
+  { hasError :: Maybe Error,
+    formFieldName :: ByteString,
+    originalValue :: ByteString
+  }
+  deriving stock (Show)
+
+mkFormValidationResult ::
+  ( HasField "formFieldName" form ByteString,
+    HasField "originalValue" form ByteString
+  ) =>
+  form ->
+  Maybe Error ->
+  FormValidationResult
+mkFormValidationResult form err =
+  FormValidationResult
+    { hasError = err,
+      formFieldName = form.formFieldName,
+      originalValue = form.originalValue
+    }
+
+eitherToFormValidation ::
+  ( HasField "formFieldName" form ByteString,
+    HasField "originalValue" form ByteString
+  ) =>
+  form ->
+  Either Error a ->
+  FormValidation a
+eitherToFormValidation form = \case
+  Left err ->
+    FormValidation $ (DList.singleton $ mkFormValidationResult form (Just err), Nothing)
+  Right a ->
+    FormValidation $ ((DList.singleton $ mkFormValidationResult form Nothing), Just a)
+
+failFormValidation ::
+  ( HasField "formFieldName" form ByteString,
+    HasField "originalValue" form ByteString
+  ) =>
+  form ->
+  Error ->
+  FormValidation a
+failFormValidation form err =
+  FormValidation (DList.singleton $ mkFormValidationResult form (Just err), Nothing)
+
+-- | Parse the multipart form or throw a user error with a descriptive error message.
+parseMultipartOrThrow ::
+  (MonadLogger m, MonadIO m) =>
+  (ErrorTree -> m a) ->
+  MultipartParseT backend m a ->
+  Wai.Request ->
+  m a
+parseMultipartOrThrow throwF parser req = do
+  -- TODO: this throws all errors with `error`, so leads to 500 on bad input …
+  formFields <-
+    liftIO $
+      Wai.parseRequestBodyEx
+        Wai.defaultParseRequestBodyOptions
+        Wai.lbsBackEnd
+        req
+  parser.unMultipartParseT
+    FormFields
+      { inputs = fst formFields,
+        files = map fileDataToMultipartFile $ snd formFields
+      }
+    >>= \case
+      Failure errs -> throwF (errorTree "Cannot parse the multipart form" errs)
+      Success a -> pure a
+
+-- | Parse the field out of the multipart message
+field :: (Applicative m) => ByteString -> FieldParser ByteString a -> MultipartParseT backend m a
+field fieldName fieldParser = MultipartParseT $ \mp ->
+  mp.inputs
+    & findMaybe (\input -> if fst input == fieldName then Just (snd input) else Nothing)
+    & annotate [fmt|Field "{fieldName}" does not exist in the multipart form|]
+    >>= runFieldParser fieldParser
+    & eitherToListValidation
+    & pure
+
+-- | Parse the field out of the multipart message
+field' :: (Applicative m) => ByteString -> FieldParser ByteString a -> MultipartParseT backend m (FormValidation a)
+field' fieldName fieldParser = MultipartParseT $ \mp ->
+  mp.inputs
+    & findMaybe (\input -> if fst input == fieldName then Just $ snd input else Nothing)
+    & annotate [fmt|Field "{fieldName}" does not exist in the multipart form|]
+    <&> ( \originalValue ->
+            originalValue
+              & runFieldParser fieldParser
+              & eitherToFormValidation
+                ( T2
+                    (label @"formFieldName" fieldName)
+                    (label @"originalValue" originalValue)
+                )
+        )
+    & eitherToListValidation
+    & pure
+
+-- | Parse the field out of the multipart message, and into a 'Label' of the given name.
+fieldLabel :: forall lbl backend m a. (Applicative m) => ByteString -> FieldParser ByteString a -> MultipartParseT backend m (Label lbl a)
+fieldLabel fieldName fieldParser = label @lbl <$> field fieldName fieldParser
+
+-- | Parse the field out of the multipart message, and into a 'Label' of the given name.
+fieldLabel' :: forall lbl backend m a. (Applicative m) => ByteString -> FieldParser ByteString a -> MultipartParseT backend m (FormValidation (Label lbl a))
+fieldLabel' fieldName fieldParser = fmap (label @lbl) <$> field' fieldName fieldParser
+
+-- | parse all fields out of the multipart message, with the same parser
+allFields :: (Applicative m) => FieldParser (T2 "key" ByteString "value" ByteString) b -> MultipartParseT backend m [b]
+allFields fieldParser = MultipartParseT $ \mp ->
+  mp.inputs
+    <&> tupToT2 @"key" @"value"
+    & traverseValidate (runFieldParser fieldParser)
+    & eitherToValidation
+    & pure
+
+tupToT2 :: forall l1 l2 t1 t2. (t1, t2) -> T2 l1 t1 l2 t2
+tupToT2 (a, b) = T2 (label a) (label b)
+
+-- | Parse a file by name out of the multipart message
+file ::
+  (Applicative m) =>
+  ByteString ->
+  MultipartParseT backend m (MultipartFile Lazy.ByteString)
+file fieldName = MultipartParseT $ \mp ->
+  mp.files
+    & List.find (\input -> input.multipartNameAttribute == fieldName)
+    & annotate [fmt|File "{fieldName}" does not exist in the multipart form|]
+    & ( \case
+          Left err -> Failure (singleton err)
+          Right filePath -> Success filePath
+      )
+    & pure
+
+-- | Return all files from the multipart message
+allFiles ::
+  (Applicative m) =>
+  MultipartParseT backend m [MultipartFile Lazy.ByteString]
+allFiles = MultipartParseT $ \mp -> do
+  pure $ Success $ mp.files
+
+-- | Ensure there is exactly one file and return it (ignoring the field name)
+exactlyOneFile ::
+  (Applicative m) =>
+  MultipartParseT backend m (MultipartFile Lazy.ByteString)
+exactlyOneFile = MultipartParseT $ \mp ->
+  mp.files
+    & \case
+      [] -> pure $ failParse "Expected to receive a file, but the multipart form did not contain any files"
+      [file_] -> pure $ Success file_
+      more -> pure $ failParse [fmt|Expected to receive exactly one file, but the multipart form contained {List.length more} files|]
+  where
+    -- \| Fail to parse the multipart form with the given error message.
+    failParse :: Text -> Validation (NonEmpty Error) a
+    failParse = Failure . singleton . newError
+
+newtype GetFileContent backend m content = GetFileContent
+  {unGetFileContent :: (Wai.Request -> m (Either Error content))}
+
+-- | A file field in a multipart message.
+data MultipartFile content = MultipartFile
+  { -- | @name@ attribute of the corresponding HTML @\<input\>@
+    multipartNameAttribute :: ByteString,
+    -- | name of the file on the client's disk
+    fileNameOnDisk :: ByteString,
+    -- | MIME type for the file
+    fileMimeType :: ByteString,
+    -- | Content of the file
+    content :: content
+  }
+
+-- | Convert the multipart library struct of a multipart file to our own.
+fileDataToMultipartFile ::
+  Wai.File Lazy.ByteString ->
+  (MultipartFile Lazy.ByteString)
+fileDataToMultipartFile (multipartNameAttribute, file_) = do
+  MultipartFile
+    { multipartNameAttribute,
+      fileNameOnDisk = file_.fileName,
+      fileMimeType = file_.fileContentType,
+      content = file_.fileContent
+    }
diff --git a/users/Profpatsch/my-xmonad/Xmonad.hs b/users/Profpatsch/my-xmonad/Xmonad.hs
new file mode 100644
index 0000000000..bb727ac2f1
--- /dev/null
+++ b/users/Profpatsch/my-xmonad/Xmonad.hs
@@ -0,0 +1,127 @@
+module Main where
+
+import Data.Function ((&))
+import XMonad
+import XMonad qualified as Xmonad
+import XMonad.Hooks.EwmhDesktops (ewmh)
+import XMonad.Layout.Decoration
+import XMonad.Layout.MultiToggle
+import XMonad.Layout.MultiToggle.Instances (StdTransformers (..))
+import XMonad.Layout.Tabbed (TabbedDecoration)
+import XMonad.Layout.Tabbed qualified as Tabbed
+import XMonad.StackSet qualified as StackSet
+import XMonad.Util.Cursor (setDefaultCursor)
+import XMonad.Util.EZConfig (additionalKeys, additionalKeysP, removeKeysP)
+
+data Mode = Normal | Presentation
+
+main :: IO ()
+main = do
+  let config = ewmh myConfig
+  dirs <- Xmonad.getDirectories
+  Xmonad.launch config dirs
+
+myConfig ::
+  XConfig
+    ( MultiToggle
+        ( HCons
+            StdTransformers
+            XMonad.Layout.MultiToggle.EOT
+        )
+        ( ModifiedLayout
+            ( Decoration
+                TabbedDecoration
+                DefaultShrinker
+            )
+            Tall
+        )
+    )
+myConfig =
+  conf
+    { modMask = modKey,
+      terminal = term Normal,
+      focusedBorderColor = "#859900",
+      layoutHook = layout,
+      startupHook = setDefaultCursor xC_heart,
+      workspaces = workspaceNames
+    }
+    `additionalKeysP` ( [
+                          -- fullscreen
+                          ("M-e", sendMessage $ Toggle NBFULL),
+                          -- i3-like keybindings, because I’m spoiled
+                          ("M-S-x", kill),
+                          -- exchange M-Ret and M-S-Ret
+                          ("M-<Return>", spawn $ term Normal),
+                          ("C-M-<Return>", spawn $ term Presentation),
+                          ("M-S-<Return>", windows StackSet.swapMaster)
+                          -- open simple exec dmenu
+                        ]
+                          ++
+                          -- something something workspaces
+                          [ (otherModMasks ++ "M-" ++ [key], action tag)
+                            | (tag, key) <- zip workspaceNames "123456789",
+                              (otherModMasks, action) <-
+                                [ ("", windows . StackSet.greedyView),
+                                  ("S-", windows . StackSet.shift)
+                                ]
+                          ]
+                          ++
+                          -- mod-{w,e,r} %! Switch to physical/Xinerama screens 1, 2, or 3
+                          -- mod-shift-{w,e,r} %! Move client to screen 1, 2, or 3
+                          [ ("M-v", focusToScreen 0),
+                            -- , ("M-l", focusToScreen 1)
+                            ("M-c", focusToScreen 2),
+                            ("M-S-v", windowToScreen 0),
+                            ("M-S-l", windowToScreen 1),
+                            ("M-S-c", windowToScreen 2)
+                          ]
+                          -- ((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
+                          --   | (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
+                          --    , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
+                      )
+    `additionalKeys`
+    -- arrow keys should move as well (hjkl blindness)
+    [ ((modKey, xK_Up), windows StackSet.focusUp),
+      ((modKey, xK_Down), windows StackSet.focusDown)
+    ]
+    `removeKeysP` [
+                    -- previous kill command
+                    "M-S-c",
+                    -- It is way to easy to kill everything by default
+                    "M-S-q",
+                    -- no idea, I want to use it for Mozc
+                    "M-n"
+                  ]
+  where
+    conf = def
+    workspaceNames = conf & workspaces
+    modKey = mod4Mask
+    -- TODO: meh
+    term :: Mode -> String
+    -- TODO: get terminal-emulator from the system config (currently alacritty)
+    term Normal = "terminal-emulator"
+    term Presentation = "notify-send TODO: currently not terminal presentation mode implemented" -- "terminal- -u ~/.config/lilyterm/pres.conf"
+    toScreen with _number = screenWorkspace 0 >>= \ws -> whenJust ws (windows . with)
+    focusToScreen = toScreen StackSet.view
+    windowToScreen = toScreen StackSet.shift
+
+-- copied from Xmonad.Config
+layout ::
+  MultiToggle
+    (HCons StdTransformers EOT)
+    (ModifiedLayout (Decoration TabbedDecoration DefaultShrinker) Tall)
+    Window
+layout =
+  tiled
+    & Tabbed.addTabsBottom Tabbed.shrinkText def
+    & toggleFullscreen
+  where
+    -- default tiling algorithm partitions the screen into two panes
+    tiled = Tall nmaster delta ratio
+    -- The default number of windows in the master pane
+    nmaster = 1
+    -- Default proportion of screen occupied by master pane
+    ratio = 1 / 2
+    -- Percent of screen to increment by when resizing panes
+    delta = 3 / 100
+    toggleFullscreen = mkToggle1 NBFULL
diff --git a/users/Profpatsch/my-xmonad/default.nix b/users/Profpatsch/my-xmonad/default.nix
new file mode 100644
index 0000000000..708d50e960
--- /dev/null
+++ b/users/Profpatsch/my-xmonad/default.nix
@@ -0,0 +1,25 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  #   bins = depot.nix.getBins pkgs.sqlite ["sqlite3"];
+
+  my-xmonad = pkgs.haskellPackages.mkDerivation {
+    pname = "my-xmonad";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./my-xmonad.cabal
+      ./Xmonad.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.xmonad-contrib
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+in
+my-xmonad
diff --git a/users/Profpatsch/my-xmonad/my-xmonad.cabal b/users/Profpatsch/my-xmonad/my-xmonad.cabal
new file mode 100644
index 0000000000..175c6c1633
--- /dev/null
+++ b/users/Profpatsch/my-xmonad/my-xmonad.cabal
@@ -0,0 +1,62 @@
+cabal-version:      3.0
+name:               my-xmonad
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+executable xmonad
+    import: common-options
+
+    main-is: Xmonad.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        xmonad,
+        xmonad-contrib
diff --git a/users/Profpatsch/netencode/Netencode.hs b/users/Profpatsch/netencode/Netencode.hs
new file mode 100644
index 0000000000..ca93ab2fef
--- /dev/null
+++ b/users/Profpatsch/netencode/Netencode.hs
@@ -0,0 +1,433 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Netencode where
+
+import Control.Applicative (many)
+import Data.Attoparsec.ByteString qualified as Atto
+import Data.Attoparsec.ByteString.Char8 qualified as Atto.Char
+import Data.ByteString qualified as ByteString
+import Data.ByteString.Builder (Builder)
+import Data.ByteString.Builder qualified as Builder
+import Data.ByteString.Lazy qualified as ByteString.Lazy
+import Data.Fix (Fix (Fix))
+import Data.Fix qualified as Fix
+import Data.Functor.Classes (Eq1 (liftEq))
+import Data.Int (Int16, Int32, Int64, Int8)
+import Data.Map.NonEmpty (NEMap)
+import Data.Map.NonEmpty qualified as NEMap
+import Data.Semigroup qualified as Semi
+import Data.String (IsString)
+import Data.Word (Word16, Word32, Word64)
+import GHC.Exts (fromString)
+import Hedgehog qualified as Hedge
+import Hedgehog.Gen qualified as Gen
+import Hedgehog.Range qualified as Range
+import PossehlAnalyticsPrelude
+import Text.Show.Deriving
+import Prelude hiding (sum)
+
+-- | Netencode type base functor.
+--
+-- Recursive elements have a @rec@.
+data TF rec
+  = -- | Unit value
+    Unit
+  | -- | Boolean (2^1)
+    N1 Bool
+  | -- | Byte (2^3)
+    N3 Word8
+  | -- | 64-bit Natural (2^6)
+    N6 Word64
+  | -- | 64-bit Integer (2^6)
+    I6 Int64
+  | -- | Unicode Text
+    Text Text
+  | -- | Arbitrary Bytestring
+    Bytes ByteString
+  | -- | A constructor of a(n open) Sum
+    Sum (Tag Text rec)
+  | -- | Record
+    Record (NEMap Text rec)
+  | -- | List
+    List [rec]
+  deriving stock (Show, Eq, Functor)
+
+instance Eq1 TF where
+  liftEq _ Unit Unit = True
+  liftEq _ (N1 b) (N1 b') = b == b'
+  liftEq _ (N3 w8) (N3 w8') = w8 == w8'
+  liftEq _ (N6 w64) (N6 w64') = w64 == w64'
+  liftEq _ (I6 i64) (I6 i64') = i64 == i64'
+  liftEq _ (Text t) (Text t') = t == t'
+  liftEq _ (Bytes b) (Bytes b') = b == b'
+  liftEq eq (Sum t) (Sum t') = eq (t.tagVal) (t'.tagVal)
+  liftEq eq (Record m) (Record m') = liftEq eq m m'
+  liftEq eq (List xs) (List xs') = liftEq eq xs xs'
+  liftEq _ _ _ = False
+
+-- | A tagged value
+data Tag tag val = Tag
+  { tagTag :: tag,
+    tagVal :: val
+  }
+  deriving stock (Show, Eq, Functor)
+
+$(Text.Show.Deriving.deriveShow1 ''Tag)
+$(Text.Show.Deriving.deriveShow1 ''TF)
+
+-- | The Netencode type
+newtype T = T {unT :: Fix TF}
+  deriving stock (Eq, Show)
+
+-- | Create a unit
+unit :: T
+unit = T $ Fix Unit
+
+-- | Create a boolean
+n1 :: Bool -> T
+n1 = T . Fix . N1
+
+-- | Create a byte
+n3 :: Word8 -> T
+n3 = T . Fix . N3
+
+-- | Create a 64-bit natural
+n6 :: Word64 -> T
+n6 = T . Fix . N6
+
+-- | Create a 64-bit integer
+i6 :: Int64 -> T
+i6 = T . Fix . I6
+
+-- | Create a UTF-8 unicode text
+text :: Text -> T
+text = T . Fix . Text
+
+-- | Create an arbitrary bytestring
+bytes :: ByteString -> T
+bytes = T . Fix . Bytes
+
+-- | Create a tagged value from a tag name and a value
+tag :: Text -> T -> T
+tag key val = T $ Fix $ Sum $ coerce @(Tag Text T) @(Tag Text (Fix TF)) $ Tag key val
+
+-- | Create a record from a non-empty map
+record :: NEMap Text T -> T
+record = T . Fix . Record . coerce @(NEMap Text T) @(NEMap Text (Fix TF))
+
+-- | Create a list
+list :: [T] -> T
+list = T . Fix . List . coerce @[T] @([Fix TF])
+
+-- | Stable encoding of a netencode value. Record keys will be sorted lexicographically ascending.
+netencodeEncodeStable :: T -> Builder
+netencodeEncodeStable (T fix) = Fix.foldFix (netencodeEncodeStableF id) fix
+
+-- | Stable encoding of a netencode functor value. Record keys will be sorted lexicographically ascending.
+--
+-- The given function is used for encoding the recursive values.
+netencodeEncodeStableF :: (rec -> Builder) -> TF rec -> Builder
+netencodeEncodeStableF inner tf = builder go
+  where
+    -- TODO: directly pass in BL?
+    innerBL = fromBuilder . inner
+    go = case tf of
+      Unit -> "u,"
+      N1 False -> "n1:0,"
+      N1 True -> "n1:1,"
+      N3 w8 -> "n3:" <> fromBuilder (Builder.word8Dec w8) <> ","
+      N6 w64 -> "n6:" <> fromBuilder (Builder.word64Dec w64) <> ","
+      I6 i64 -> "i6:" <> fromBuilder (Builder.int64Dec i64) <> ","
+      Text t ->
+        let b = fromText t
+         in "t" <> builderLenDec b <> ":" <> b <> ","
+      Bytes b -> "b" <> builderLenDec (fromByteString b) <> ":" <> fromByteString b <> ","
+      Sum (Tag key val) -> encTag key val
+      Record m ->
+        -- NEMap uses Map internally, and that folds in lexicographic ascending order over the key.
+        -- Since these are `Text` in our case, this is stable.
+        let mBuilder = m & NEMap.foldMapWithKey encTag
+         in "{" <> builderLenDec mBuilder <> ":" <> mBuilder <> "}"
+      List xs ->
+        let xsBuilder = xs <&> innerBL & mconcat
+         in "[" <> builderLenDec xsBuilder <> ":" <> xsBuilder <> "]"
+      where
+        encTag key val =
+          let bKey = fromText key
+           in "<" <> builderLenDec bKey <> ":" <> bKey <> "|" <> innerBL val
+
+-- | A builder that knows its own size in bytes
+newtype BL = BL (Builder, Semi.Sum Natural)
+  deriving newtype (Monoid, Semigroup)
+
+instance IsString BL where
+  fromString s =
+    BL
+      ( fromString @Builder s,
+        fromString @ByteString s
+          & ByteString.length
+          & intToNatural
+          & fromMaybe 0
+          & Semi.Sum
+      )
+
+-- | Retrieve the builder
+builder :: BL -> Builder
+builder (BL (b, _)) = b
+
+-- | Retrieve the bytestring length
+builderLen :: BL -> Natural
+builderLen (BL (_, len)) = Semi.getSum $ len
+
+-- | Take a 'BL' and create a new 'BL' that represents the length as a decimal integer
+builderLenDec :: BL -> BL
+builderLenDec (BL (_, len)) =
+  let b = Builder.intDec $ (len & Semi.getSum & fromIntegral @Natural @Int)
+   in b & fromBuilder
+
+-- | Create a 'BL' from a 'Builder'.
+--
+-- Not efficient, goes back to a lazy bytestring to get the length
+fromBuilder :: Builder -> BL
+fromBuilder b =
+  BL
+    ( b,
+      b
+        & Builder.toLazyByteString
+        & ByteString.Lazy.length
+        & fromIntegral @Int64 @Natural
+        & Semi.Sum
+    )
+
+-- | Create a 'BL' from a 'ByteString'.
+fromByteString :: ByteString -> BL
+fromByteString b =
+  BL
+    ( Builder.byteString b,
+      b
+        & ByteString.length
+        & fromIntegral @Int @Natural
+        & Semi.Sum
+    )
+
+-- | Create a 'BL' from a 'Text'.
+fromText :: Text -> BL
+fromText t = t & textToBytesUtf8 & fromByteString
+
+-- | Parser for a netencode value.
+netencodeParser :: Atto.Parser T
+netencodeParser = T <$> go
+  where
+    go = Fix <$> netencodeParserF go
+
+-- | Parser for one level of a netencode value. Requires a parser for the recursion.
+netencodeParserF :: Atto.Parser rec -> Atto.Parser (TF rec)
+netencodeParserF inner = do
+  typeTag <- Atto.Char.anyChar
+  case typeTag of
+    't' -> Text <$> textParser
+    'b' -> Bytes <$> bytesParser
+    'u' -> unitParser
+    '<' -> Sum <$> tagParser
+    '{' -> Record <$> recordParser
+    '[' -> List <$> listParser
+    'n' -> naturalParser
+    'i' -> I6 <$> intParser
+    c -> fail ([c] <> " is not a valid netencode tag")
+  where
+    bytesParser = do
+      len <- boundedDecimalFail Atto.<?> "bytes is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "bytes did not have : after length"
+      bytes' <- Atto.take len
+      _ <- Atto.Char.char ',' Atto.<?> "bytes did not end with ,"
+      pure bytes'
+
+    textParser = do
+      len <- boundedDecimalFail Atto.<?> "text is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "text did not have : after length"
+      text' <-
+        Atto.take len <&> bytesToTextUtf8 >>= \case
+          Left err -> fail [fmt|cannot decode text as utf8: {err & prettyError}|]
+          Right t -> pure t
+      _ <- Atto.Char.char ',' Atto.<?> "text did not end with ,"
+      pure text'
+
+    unitParser = do
+      _ <- Atto.Char.char ',' Atto.<?> "unit did not end with ,"
+      pure $ Unit
+
+    tagParser = do
+      len <- boundedDecimalFail Atto.<?> "tag is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "tag did not have : after length"
+      tagTag <-
+        Atto.take len <&> bytesToTextUtf8 >>= \case
+          Left err -> fail [fmt|cannot decode tag key as utf8: {err & prettyError}|]
+          Right t -> pure t
+      _ <- Atto.Char.char '|' Atto.<?> "tag was missing the key/value separator (|)"
+      tagVal <- inner
+      pure $ Tag {..}
+
+    recordParser = do
+      -- TODO: the record does not use its inner length because we are descending into the inner parsers.
+      -- This is a smell! In theory it can be used to skip parsing the whole inner keys.
+      _len <- boundedDecimalFail Atto.<?> "record is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "record did not have : after length"
+      record' <-
+        many (Atto.Char.char '<' >> tagParser) <&> nonEmpty >>= \case
+          Nothing -> fail "record is not allowed to have 0 elements"
+          Just tags ->
+            pure $
+              tags
+                <&> (\t -> (t.tagTag, t.tagVal))
+                -- later keys are preferred if they are duplicates, according to the standard
+                & NEMap.fromList
+      _ <- Atto.Char.char '}' Atto.<?> "record did not end with }"
+      pure record'
+
+    listParser = do
+      -- TODO: the list does not use its inner length because we are descending into the inner parsers.
+      -- This is a smell! In theory it can be used to skip parsing the whole inner keys.
+      _len <- boundedDecimalFail Atto.<?> "list is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "list did not have : after length"
+      -- TODO: allow empty lists?
+      list' <- many inner
+      _ <- Atto.Char.char ']' Atto.<?> "list did not end with ]"
+      pure list'
+
+    intParser = do
+      let p :: forall parseSize. (Bounded parseSize, Integral parseSize) => (Integer -> Atto.Parser Int64)
+          p n = do
+            _ <- Atto.Char.char ':' Atto.<?> [fmt|i{n & show} did not have : after length|]
+            isNegative <- Atto.option False (Atto.Char.char '-' <&> \_c -> True)
+            int <-
+              boundedDecimal @parseSize >>= \case
+                Nothing -> fail [fmt|cannot parse into i{n & show}, the number is too big (would overflow)|]
+                Just i ->
+                  pure $
+                    if isNegative
+                      then -- TODO: this should alread be done in the decimal parser, @minBound@ cannot be parsed cause it’s one more than @(-maxBound)@!
+                        (-i)
+                      else i
+            _ <- Atto.Char.char ',' Atto.<?> [fmt|i{n & show} did not end with ,|]
+            pure $ fromIntegral @parseSize @Int64 int
+      digit <- Atto.Char.digit
+      case digit of
+        -- TODO: separate parser for i1 and i2 that makes sure the boundaries are right!
+        '1' -> p @Int8 1
+        '2' -> p @Int8 2
+        '3' -> p @Int8 3
+        '4' -> p @Int16 4
+        '5' -> p @Int32 5
+        '6' -> p @Int64 6
+        '7' -> fail [fmt|i parser only supports numbers up to size 6, was 7|]
+        '8' -> fail [fmt|i parser only supports numbers up to size 6, was 8|]
+        '9' -> fail [fmt|i parser only supports numbers up to size 6, was 9|]
+        o -> fail [fmt|i number with length {o & show} not possible|]
+
+    naturalParser = do
+      let p :: forall parseSize finalSize. (Bounded parseSize, Integral parseSize, Num finalSize) => (Integer -> Atto.Parser finalSize)
+          p n = do
+            _ <- Atto.Char.char ':' Atto.<?> [fmt|n{n & show} did not have : after length|]
+            int <-
+              boundedDecimal @parseSize >>= \case
+                Nothing -> fail [fmt|cannot parse into n{n & show}, the number is too big (would overflow)|]
+                Just i -> pure i
+
+            _ <- Atto.Char.char ',' Atto.<?> [fmt|n{n & show} did not end with ,|]
+            pure $ fromIntegral @parseSize @finalSize int
+      let b n = do
+            _ <- Atto.Char.char ':' Atto.<?> [fmt|n{n & show} did not have : after length|]
+            bool <-
+              (Atto.Char.char '0' >> pure False)
+                <|> (Atto.Char.char '1' >> pure True)
+            _ <- Atto.Char.char ',' Atto.<?> [fmt|n{n & show} did not end with ,|]
+            pure bool
+
+      digit <- Atto.Char.digit
+      case digit of
+        -- TODO: separate parser for n1 and n2 that makes sure the boundaries are right!
+        '1' -> N1 <$> b 1
+        '2' -> N3 <$> p @Word8 @Word8 2
+        '3' -> N3 <$> p @Word8 @Word8 3
+        '4' -> N6 <$> p @Word16 @Word64 4
+        '5' -> N6 <$> p @Word32 @Word64 5
+        '6' -> N6 <$> p @Word64 @Word64 6
+        '7' -> fail [fmt|n parser only supports numbers up to size 6, was 7|]
+        '8' -> fail [fmt|n parser only supports numbers up to size 6, was 8|]
+        '9' -> fail [fmt|n parser only supports numbers up to size 6, was 9|]
+        o -> fail [fmt|n number with length {o & show} not possible|]
+
+-- | Parser for a bounded decimal that does not overflow the decimal.
+--
+--  via https://www.extrema.is/blog/2021/10/20/parsing-bounded-integers
+boundedDecimal :: forall a. (Bounded a, Integral a) => Atto.Parser (Maybe a)
+boundedDecimal = do
+  i :: Integer <- decimal
+  pure $
+    if (i :: Integer) > fromIntegral (maxBound :: a)
+      then Nothing
+      else Just $ fromIntegral i
+  where
+    -- Copied from @Attoparsec.Text@ and adjusted to bytestring
+    decimal :: (Integral a2) => Atto.Parser a2
+    decimal = ByteString.foldl' step 0 <$> Atto.Char.takeWhile1 Atto.Char.isDigit
+      where
+        step a c = a * 10 + fromIntegral (c - 48)
+{-# SPECIALIZE boundedDecimal :: Atto.Parser (Maybe Int) #-}
+{-# SPECIALIZE boundedDecimal :: Atto.Parser (Maybe Int64) #-}
+{-# SPECIALIZE boundedDecimal :: Atto.Parser (Maybe Word8) #-}
+{-# SPECIALIZE boundedDecimal :: Atto.Parser (Maybe Word64) #-}
+
+-- | 'boundedDecimal', but fail the parser if the decimal overflows.
+boundedDecimalFail :: Atto.Parser Int
+boundedDecimalFail =
+  boundedDecimal >>= \case
+    Nothing -> fail "decimal out of range"
+    Just a -> pure a
+
+-- | Hedgehog generator for a netencode value.
+genNetencode :: Hedge.MonadGen m => m T
+genNetencode =
+  Gen.recursive
+    Gen.choice
+    [ -- these are bundled into one Gen, so that scalar elements get chosen less frequently, and the generator produces nicely nested examples
+      Gen.frequency
+        [ (1, pure unit),
+          (1, n1 <$> Gen.bool),
+          (1, n3 <$> Gen.element [0, 1, 5]),
+          (1, n6 <$> Gen.element [0, 1, 5]),
+          (1, i6 <$> Gen.element [-1, 1, 5]),
+          (2, text <$> Gen.text (Range.linear 1 10) Gen.lower),
+          (2, bytes <$> Gen.bytes (Range.linear 1 10))
+        ]
+    ]
+    [ do
+        key <- Gen.text (Range.linear 3 10) Gen.lower
+        val <- genNetencode
+        pure $ tag key val,
+      record
+        <$> ( let k = Gen.text (Range.linear 3 10) Gen.lower
+                  v = genNetencode
+               in NEMap.insertMap
+                    <$> k
+                    <*> v
+                    <*> ( (Gen.map (Range.linear 0 3)) $
+                            (,) <$> k <*> v
+                        )
+            )
+    ]
+
+-- | Hedgehog property: encoding a netencode value and parsing it again returns the same result.
+prop_netencodeRoundtrip :: Hedge.Property
+prop_netencodeRoundtrip = Hedge.property $ do
+  enc <- Hedge.forAll genNetencode
+  ( Atto.parseOnly
+      netencodeParser
+      ( netencodeEncodeStable enc
+          & Builder.toLazyByteString
+          & toStrictBytes
+      )
+    )
+    Hedge.=== (Right enc)
diff --git a/users/Profpatsch/netencode/Netencode/Parse.hs b/users/Profpatsch/netencode/Netencode/Parse.hs
new file mode 100644
index 0000000000..184fb5f912
--- /dev/null
+++ b/users/Profpatsch/netencode/Netencode/Parse.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Netencode.Parse where
+
+import Control.Category qualified
+import Control.Selective (Selective)
+import Data.Error.Tree
+import Data.Fix (Fix (..))
+import Data.Functor.Compose
+import Data.List qualified as List
+import Data.Map.NonEmpty (NEMap)
+import Data.Map.NonEmpty qualified as NEMap
+import Data.Semigroupoid qualified as Semigroupiod
+import Data.Semigroupoid qualified as Semigroupoid
+import Data.Text qualified as Text
+import Netencode qualified
+import PossehlAnalyticsPrelude
+import Prelude hiding (log)
+
+newtype Parse from to
+  = -- TODO: the way @Context = [Text]@ has to be forwarded to everything is kinda shitty.
+    -- This is essentially just a difference list, and can probably be treated as a function in the output?
+    Parse (([Text], from) -> Validation (NonEmpty ErrorTree) ([Text], to))
+  deriving
+    (Functor, Applicative, Selective)
+    via ( Compose
+            ( Compose
+                ((->) ([Text], from))
+                (Validation (NonEmpty ErrorTree))
+            )
+            ((,) [Text])
+        )
+
+instance Semigroupoid Parse where
+  o p2 p1 = Parse $ \from -> case runParse' p1 from of
+    Failure err -> Failure err
+    Success to1 -> runParse' p2 to1
+
+instance Category Parse where
+  (.) = Semigroupoid.o
+  id = Parse $ \t -> Success t
+
+runParse :: Error -> Parse from to -> from -> Either ErrorTree to
+runParse errMsg parser t =
+  (["$"], t)
+    & runParse' parser
+    <&> snd
+    & first (nestedMultiError errMsg)
+    & validationToEither
+
+runParse' :: Parse from to -> ([Text], from) -> Validation (NonEmpty ErrorTree) ([Text], to)
+runParse' (Parse f) from = f from
+
+parseEither :: (([Text], from) -> Either ErrorTree ([Text], to)) -> Parse from to
+parseEither f = Parse $ \from -> f from & eitherToListValidation
+
+tAs :: (Netencode.TF (Fix Netencode.TF) -> Either ([Text] -> ErrorTree) to) -> Parse Netencode.T to
+tAs f = parseEither ((\(context, Netencode.T (Fix tf)) -> f tf & bimap ($ context) (context,)))
+
+key :: Text -> Parse (NEMap Text to) to
+key name = parseEither $ \(context, rec) ->
+  rec
+    & NEMap.lookup name
+    & annotate (errorTreeContext (showContext context) [fmt|Key "{name}" does not exist|])
+    <&> (addContext name context,)
+
+showContext :: [Text] -> Text
+showContext context = context & List.reverse & Text.intercalate "."
+
+addContext :: a -> [a] -> [a]
+addContext = (:)
+
+asText :: Parse Netencode.T Text
+asText = tAs $ \case
+  Netencode.Text t -> pure t
+  other -> typeError "of text" other
+
+asBytes :: Parse Netencode.T ByteString
+asBytes = tAs $ \case
+  Netencode.Bytes b -> pure b
+  other -> typeError "of bytes" other
+
+asRecord :: Parse Netencode.T (NEMap Text (Netencode.T))
+asRecord = tAs $ \case
+  Netencode.Record rec -> pure (rec <&> Netencode.T)
+  other -> typeError "a record" other
+
+typeError :: Text -> Netencode.TF ignored -> (Either ([Text] -> ErrorTree) b)
+typeError should is = do
+  let otherS = is <&> (\_ -> ("…" :: String)) & show
+  Left $ \context -> errorTreeContext (showContext context) [fmt|Value is not {should}, but a {otherS}|]
+
+orThrowParseError ::
+  Parse (Either Error to) to
+orThrowParseError = Parse $ \case
+  (context, Left err) ->
+    err
+      & singleError
+      & errorTreeContext (showContext context)
+      & singleton
+      & Failure
+  (context, Right to) -> Success (context, to)
diff --git a/users/Profpatsch/netencode/README.md b/users/Profpatsch/netencode/README.md
index 67cb843a58..3538a110a6 100644
--- a/users/Profpatsch/netencode/README.md
+++ b/users/Profpatsch/netencode/README.md
@@ -73,7 +73,11 @@ A tag (`<`) gives a value a name. The tag is UTF-8 encoded, starting with its le
 ### records (products/records), also maps
 
 A record (`{`) is a concatenation of tags (`<`). It needs to be closed with `}`.
-If tag names repeat the later ones should be ignored. Ordering does not matter.
+
+If tag names repeat the *earlier* ones should be ignored.
+Using the last tag corresponds with the way most languages handle converting a list of tuples to Maps, by using a for-loop and Map.insert without checking the contents first. Otherwise you’d have to revert the list first or remember which keys you already inserted.
+
+Ordering of tags in a record does not matter.
 
 Similar to text, records start with the length of their *whole encoded content*, in bytes. This makes it possible to treat their contents as opaque bytestrings.
 
@@ -81,7 +85,7 @@ Similar to text, records start with the length of their *whole encoded content*,
 * A record with one empty field, `foo`: `{9:<3:foo|u,}`
 * A record with two fields, `foo` and `x`: `{21:<3:foo|u,<1:x|t3:baz,}`
 * The same record: `{21:<1:x|t3:baz,<3:foo|u,}`
-* The same record (later occurences of fields are ignored): `{28:<1:x|t3:baz,<3:foo|u,<1:x|u,}`
+* The same record (earlier occurences of fields are ignored): `{<1:x|u,28:<1:x|t3:baz,<3:foo|u,}`
 
 ### sums (tagged unions)
 
@@ -98,6 +102,24 @@ Similar to records, lists start with the length of their whole encoded content.
 * The list with text `foo` followed by i3 `-42`: `[14:t3:foo,i3:-42,]`
 * The list with `Some` and `None` tags: `[33:<4:Some|t3:foo,<4None|u,<4None|u,]`
 
+## parser security considerations
+
+The length field is a decimal number that is not length-restricted,
+meaning an attacker could give an infinitely long length (or extremely long)
+thus overflowing your parser if you are not careful.
+
+You should thus put a practical length limit to the length of length fields,
+which implicitely enforces a length limit on how long the value itself can be.
+
+Start by defining a max value length in bytes.
+Then count the number of decimals in that number.
+
+So if your max length is 1024 bytes, your length field can be a maximum `count_digits(1024) == 4` bytes long.
+
+Thus, if you restrict your parser to a length field of 4 bytes,
+it should also never parse anything longer than 1024 bytes for the value
+(plus 1 byte for the type tag, 4 bytes for the length, and 2 bytes for the separator & ending character).
+
 ## motivation
 
 TODO
diff --git a/users/Profpatsch/netencode/default.nix b/users/Profpatsch/netencode/default.nix
index 739bda3d78..6e7dce489a 100644
--- a/users/Profpatsch/netencode/default.nix
+++ b/users/Profpatsch/netencode/default.nix
@@ -1,101 +1,133 @@
 { depot, pkgs, lib, ... }:
 
 let
-  netencode-rs = depot.nix.writers.rustSimpleLib {
+  netencode-rs = depot.nix.writers.rustSimpleLib
+    {
       name = "netencode";
       dependencies = [
         depot.third_party.rust-crates.nom
         depot.users.Profpatsch.execline.exec-helpers
       ];
-    } (builtins.readFile ./netencode.rs);
+    }
+    (builtins.readFile ./netencode.rs);
 
-  gen = import ./gen.nix { inherit lib; };
+  netencode-hs = pkgs.haskellPackages.mkDerivation {
+    pname = "netencode";
+    version = "0.1.0";
 
-  pretty-rs = depot.nix.writers.rustSimpleLib {
-    name = "netencode-pretty";
-    dependencies = [
-      netencode-rs
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./netencode.cabal
+      ./Netencode.hs
+      ./Netencode/Parse.hs
     ];
-  } (builtins.readFile ./pretty.rs);
-
-  pretty = depot.nix.writers.rustSimple {
-    name = "netencode-pretty";
-    dependencies = [
-      netencode-rs
-      pretty-rs
-      depot.users.Profpatsch.execline.exec-helpers
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.hedgehog
+      pkgs.haskellPackages.nonempty-containers
+      pkgs.haskellPackages.deriving-compat
+      pkgs.haskellPackages.data-fix
+      pkgs.haskellPackages.bytestring
+      pkgs.haskellPackages.attoparsec
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
     ];
-  } ''
+
+    isLibrary = true;
+    license = lib.licenses.mit;
+
+
+  };
+
+  gen = import ./gen.nix { inherit lib; };
+
+  pretty-rs = depot.nix.writers.rustSimpleLib
+    {
+      name = "netencode-pretty";
+      dependencies = [
+        netencode-rs
+      ];
+    }
+    (builtins.readFile ./pretty.rs);
+
+  pretty = depot.nix.writers.rustSimple
+    {
+      name = "netencode-pretty";
+      dependencies = [
+        netencode-rs
+        pretty-rs
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
     extern crate netencode;
     extern crate netencode_pretty;
     extern crate exec_helpers;
 
     fn main() {
       let (_, prog) = exec_helpers::args_for_exec("netencode-pretty", 0);
-      let mut buf = vec![];
-      let u = netencode::u_from_stdin_or_die_user_error("netencode-pretty", &mut buf);
-      match netencode_pretty::Pretty::from_u(u).print_multiline(&mut std::io::stdout()) {
+      let t = netencode::t_from_stdin_or_die_user_error("netencode-pretty");
+      match netencode_pretty::Pretty::from_u(t.to_u()).print_multiline(&mut std::io::stdout()) {
         Ok(()) => {},
         Err(err) => exec_helpers::die_temporary("netencode-pretty", format!("could not write to stdout: {}", err))
       }
     }
   '';
 
-  netencode-mustache = depot.nix.writers.rustSimple {
-    name = "netencode_mustache";
-    dependencies = [
-      depot.users.Profpatsch.arglib.netencode.rust
-      netencode-rs
-      depot.third_party.rust-crates.mustache
-    ];
-  } (builtins.readFile ./netencode-mustache.rs);
+  netencode-mustache = depot.nix.writers.rustSimple
+    {
+      name = "netencode_mustache";
+      dependencies = [
+        depot.users.Profpatsch.arglib.netencode.rust
+        netencode-rs
+        depot.third_party.rust-crates.mustache
+      ];
+    }
+    (builtins.readFile ./netencode-mustache.rs);
 
 
-  record-get = depot.nix.writers.rustSimple {
-    name = "record-get";
-    dependencies = [
-      netencode-rs
-      depot.users.Profpatsch.execline.exec-helpers
-      depot.users.Profpatsch.arglib.netencode.rust
-    ];
-  } ''
+  record-get = depot.nix.writers.rustSimple
+    {
+      name = "record-get";
+      dependencies = [
+        netencode-rs
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
     extern crate netencode;
-    extern crate arglib_netencode;
     extern crate exec_helpers;
     use netencode::{encode, dec};
     use netencode::dec::{Decoder, DecodeError};
 
     fn main() {
-        let mut buf = vec![];
         let args = exec_helpers::args("record-get", 1);
         let field = match std::str::from_utf8(&args[0]) {
             Ok(f) => f,
             Err(_e) => exec_helpers::die_user_error("record-get", format!("The field name needs to be valid unicode"))
         };
-        let u = netencode::u_from_stdin_or_die_user_error("record-get", &mut buf);
-        match (dec::RecordDot {field, inner: dec::AnyU }).dec(u) {
+        let t = netencode::t_from_stdin_or_die_user_error("record-get");
+        match (dec::RecordDot {field, inner: dec::AnyU }).dec(t.to_u()) {
             Ok(u) => encode(&mut std::io::stdout(), &u).expect("encoding to stdout failed"),
             Err(DecodeError(err)) => exec_helpers::die_user_error("record-get", err)
         }
     }
   '';
 
-  record-splice-env = depot.nix.writers.rustSimple {
-    name = "record-splice-env";
-    dependencies = [
-      netencode-rs
-      depot.users.Profpatsch.execline.exec-helpers
-    ];
-  } ''
+  record-splice-env = depot.nix.writers.rustSimple
+    {
+      name = "record-splice-env";
+      dependencies = [
+        netencode-rs
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
     extern crate netencode;
     extern crate exec_helpers;
     use netencode::dec::{Record, Try, ScalarAsBytes, Decoder, DecodeError};
 
     fn main() {
-        let mut buf = vec![];
-        let u = netencode::u_from_stdin_or_die_user_error("record-splice-env", &mut buf);
+        let t = netencode::t_from_stdin_or_die_user_error("record-splice-env");
         let (_, prog) = exec_helpers::args_for_exec("record-splice-env", 0);
-        match Record(Try(ScalarAsBytes)).dec(u) {
+        match Record(Try(ScalarAsBytes)).dec(t.to_u()) {
             Ok(map) => {
                 exec_helpers::exec_into_args(
                     "record-splice-env",
@@ -109,13 +141,14 @@ let
     }
   '';
 
-  env-splice-record = depot.nix.writers.rustSimple {
-    name = "env-splice-record";
-    dependencies = [
-      netencode-rs
-      depot.users.Profpatsch.execline.exec-helpers
-    ];
-  } ''
+  env-splice-record = depot.nix.writers.rustSimple
+    {
+      name = "env-splice-record";
+      dependencies = [
+        netencode-rs
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
     extern crate netencode;
     extern crate exec_helpers;
     use netencode::{T};
@@ -135,9 +168,11 @@ let
     }
   '';
 
-in depot.nix.readTree.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   inherit
     netencode-rs
+    netencode-hs
     pretty-rs
     pretty
     netencode-mustache
diff --git a/users/Profpatsch/netencode/gen.nix b/users/Profpatsch/netencode/gen.nix
index 305ff7b08d..efc9629ca0 100644
--- a/users/Profpatsch/netencode/gen.nix
+++ b/users/Profpatsch/netencode/gen.nix
@@ -27,29 +27,33 @@ let
   concatStrings = builtins.concatStringsSep "";
 
   record = lokv: netstring "{" "}"
-    (concatStrings (map ({key, val}: tag key val) lokv));
+    (concatStrings (map ({ key, val }: tag key val) lokv));
 
   list = l: netstring "[" "]" (concatStrings l);
 
   dwim = val:
-    let match = {
-      "bool" = n1;
-      "int" = i6;
-      "string" = text;
-      "set" = attrs:
-        # it could be a derivation, then just return the path
-        if attrs.type or "" == "derivation" then text "${attrs}"
-        else
-          record (lib.mapAttrsToList
-          (k: v: {
-            key = k;
-            val = dwim v;
-          }) attrs);
-      "list" = l: list (map dwim l);
-    };
-    in match.${builtins.typeOf val} val;
+    let
+      match = {
+        "bool" = n1;
+        "int" = i6;
+        "string" = text;
+        "set" = attrs:
+          # it could be a derivation, then just return the path
+          if attrs.type or "" == "derivation" then text "${attrs}"
+          else
+            record (lib.mapAttrsToList
+              (k: v: {
+                key = k;
+                val = dwim v;
+              })
+              attrs);
+        "list" = l: list (map dwim l);
+      };
+    in
+    match.${builtins.typeOf val} val;
 
-in {
+in
+{
   inherit
     unit
     n1
diff --git a/users/Profpatsch/netencode/netencode-mustache.rs b/users/Profpatsch/netencode/netencode-mustache.rs
index ee7bafed22..73ed5be1de 100644
--- a/users/Profpatsch/netencode/netencode-mustache.rs
+++ b/users/Profpatsch/netencode/netencode-mustache.rs
@@ -1,12 +1,12 @@
-extern crate netencode;
-extern crate mustache;
 extern crate arglib_netencode;
+extern crate mustache;
+extern crate netencode;
 
-use mustache::{Data};
-use netencode::{T};
+use mustache::Data;
+use netencode::T;
 use std::collections::HashMap;
-use std::os::unix::ffi::{OsStrExt};
-use std::io::{Read};
+use std::io::Read;
+use std::os::unix::ffi::OsStrExt;
 
 fn netencode_to_mustache_data_dwim(t: T) -> Data {
     match t {
@@ -25,27 +25,26 @@ fn netencode_to_mustache_data_dwim(t: T) -> Data {
         T::Record(xs) => Data::Map(
             xs.into_iter()
                 .map(|(key, val)| (key, netencode_to_mustache_data_dwim(val)))
-                .collect::<HashMap<_,_>>()
+                .collect::<HashMap<_, _>>(),
         ),
         T::List(xs) => Data::Vec(
             xs.into_iter()
                 .map(|x| netencode_to_mustache_data_dwim(x))
-                .collect::<Vec<_>>()
+                .collect::<Vec<_>>(),
         ),
     }
 }
 
 pub fn from_stdin() -> () {
-    let data = netencode_to_mustache_data_dwim(
-        arglib_netencode::arglib_netencode("netencode-mustache", Some(std::ffi::OsStr::new("TEMPLATE_DATA")))
-    );
+    let data = netencode_to_mustache_data_dwim(arglib_netencode::arglib_netencode(
+        "netencode-mustache",
+        Some(std::ffi::OsStr::new("TEMPLATE_DATA")),
+    ));
     let mut stdin = String::new();
     std::io::stdin().read_to_string(&mut stdin).unwrap();
     mustache::compile_str(&stdin)
-        .and_then(|templ| templ.render_data(
-            &mut std::io::stdout(),
-            &data
-        )).unwrap()
+        .and_then(|templ| templ.render_data(&mut std::io::stdout(), &data))
+        .unwrap()
 }
 
 pub fn main() {
diff --git a/users/Profpatsch/netencode/netencode.cabal b/users/Profpatsch/netencode/netencode.cabal
new file mode 100644
index 0000000000..7bff4487bb
--- /dev/null
+++ b/users/Profpatsch/netencode/netencode.cabal
@@ -0,0 +1,74 @@
+cabal-version:      3.0
+name:               netencode
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+    exposed-modules:
+        Netencode,
+        Netencode.Parse
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        pa-label,
+        pa-error-tree,
+        hedgehog,
+        nonempty-containers,
+        deriving-compat,
+        data-fix,
+        bytestring,
+        attoparsec,
+        text,
+        semigroupoids,
+        selective
diff --git a/users/Profpatsch/netencode/netencode.rs b/users/Profpatsch/netencode/netencode.rs
index 5bd43f992f..34a8fcef09 100644
--- a/users/Profpatsch/netencode/netencode.rs
+++ b/users/Profpatsch/netencode/netencode.rs
@@ -1,9 +1,9 @@
-extern crate nom;
 extern crate exec_helpers;
+extern crate nom;
 
 use std::collections::HashMap;
-use std::io::{Write, Read};
-use std::fmt::{Display, Debug};
+use std::fmt::{Debug, Display};
+use std::io::{Read, Write};
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum T {
@@ -46,22 +46,19 @@ impl T {
             T::I7(i) => U::I7(*i),
             T::Text(t) => U::Text(t.as_str()),
             T::Binary(v) => U::Binary(v),
-            T::Sum(Tag { tag, val }) => U::Sum(
-                Tag { tag: tag.as_str(), val: Box::new(val.to_u()) }
-            ),
-            T::Record(map) => U::Record(
-                map.iter().map(|(k, v)| (k.as_str(), v.to_u())).collect()
-            ),
-            T::List(l) => U::List(
-                l.iter().map(|v| v.to_u()).collect::<Vec<U<'a>>>()
-            ),
+            T::Sum(Tag { tag, val }) => U::Sum(Tag {
+                tag: tag.as_str(),
+                val: Box::new(val.to_u()),
+            }),
+            T::Record(map) => U::Record(map.iter().map(|(k, v)| (k.as_str(), v.to_u())).collect()),
+            T::List(l) => U::List(l.iter().map(|v| v.to_u()).collect::<Vec<U<'a>>>()),
         }
     }
 
     pub fn encode<'a>(&'a self) -> Vec<u8> {
         match self {
             // TODO: don’t go via U, inefficient
-            o => o.to_u().encode()
+            o => o.to_u().encode(),
         }
     }
 }
@@ -110,15 +107,16 @@ impl<'a> U<'a> {
             U::I7(i) => T::I7(*i),
             U::Text(t) => T::Text((*t).to_owned()),
             U::Binary(v) => T::Binary((*v).to_owned()),
-            U::Sum(Tag { tag, val }) => T::Sum(
-                Tag { tag: (*tag).to_owned(), val: Box::new(val.to_t()) }
-            ),
+            U::Sum(Tag { tag, val }) => T::Sum(Tag {
+                tag: (*tag).to_owned(),
+                val: Box::new(val.to_t()),
+            }),
             U::Record(map) => T::Record(
-                map.iter().map(|(k, v)| ((*k).to_owned(), v.to_t())).collect::<HashMap<String, T>>()
-            ),
-            U::List(l) => T::List(
-                l.iter().map(|v| v.to_t()).collect::<Vec<T>>()
+                map.iter()
+                    .map(|(k, v)| ((*k).to_owned(), v.to_t()))
+                    .collect::<HashMap<String, T>>(),
             ),
+            U::List(l) => T::List(l.iter().map(|v| v.to_t()).collect::<Vec<T>>()),
         }
     }
 }
@@ -127,16 +125,18 @@ impl<'a> U<'a> {
 pub struct Tag<S, A> {
     // TODO: make into &str
     pub tag: S,
-    pub val: Box<A>
+    pub val: Box<A>,
 }
 
 impl<S, A> Tag<S, A> {
     fn map<F, B>(self, f: F) -> Tag<S, B>
-        where F: Fn(A) -> B {
-          Tag {
-              tag: self.tag,
-              val: Box::new(f(*self.val))
-          }
+    where
+        F: Fn(A) -> B,
+    {
+        Tag {
+            tag: self.tag,
+            val: Box::new(f(*self.val)),
+        }
     }
 }
 
@@ -147,77 +147,170 @@ fn encode_tag<W: Write>(w: &mut W, tag: &str, val: &U) -> std::io::Result<()> {
 }
 
 pub fn encode<W: Write>(w: &mut W, u: &U) -> std::io::Result<()> {
-  match u {
-      U::Unit => write!(w, "u,"),
-      U::N1(b) => if *b { write!(w, "n1:1,") } else { write!(w, "n1:0,") },
-      U::N3(n) => write!(w, "n3:{},", n),
-      U::N6(n) => write!(w, "n6:{},", n),
-      U::N7(n) => write!(w, "n7:{},", n),
-      U::I3(i) => write!(w, "i3:{},", i),
-      U::I6(i) => write!(w, "i6:{},", i),
-      U::I7(i) => write!(w, "i7:{},", i),
-      U::Text(s) => {
-          write!(w, "t{}:", s.len());
-          w.write_all(s.as_bytes());
-          write!(w, ",")
-      }
-      U::Binary(s) => {
-          write!(w, "b{}:", s.len());
-          w.write_all(&s);
-          write!(w, ",")
-      },
-      U::Sum(Tag{tag, val}) => encode_tag(w, tag, val),
-      U::Record(m) => {
-          let mut c = std::io::Cursor::new(vec![]);
-          for (k, v) in m {
-              encode_tag(&mut c, k, v)?;
-          }
-          write!(w, "{{{}:", c.get_ref().len())?;
-          w.write_all(c.get_ref())?;
-          write!(w, "}}")
-      },
-      U::List(l) => {
-          let mut c = std::io::Cursor::new(vec![]);
-          for u in l {
-              encode(&mut c, u)?;
-          }
-          write!(w, "[{}:", c.get_ref().len())?;
-          w.write_all(c.get_ref())?;
-          write!(w, "]")
-      }
-  }
+    match u {
+        U::Unit => write!(w, "u,"),
+        U::N1(b) => {
+            if *b {
+                write!(w, "n1:1,")
+            } else {
+                write!(w, "n1:0,")
+            }
+        }
+        U::N3(n) => write!(w, "n3:{},", n),
+        U::N6(n) => write!(w, "n6:{},", n),
+        U::N7(n) => write!(w, "n7:{},", n),
+        U::I3(i) => write!(w, "i3:{},", i),
+        U::I6(i) => write!(w, "i6:{},", i),
+        U::I7(i) => write!(w, "i7:{},", i),
+        U::Text(s) => {
+            write!(w, "t{}:", s.len());
+            w.write_all(s.as_bytes());
+            write!(w, ",")
+        }
+        U::Binary(s) => {
+            write!(w, "b{}:", s.len());
+            w.write_all(&s);
+            write!(w, ",")
+        }
+        U::Sum(Tag { tag, val }) => encode_tag(w, tag, val),
+        U::Record(m) => {
+            let mut c = std::io::Cursor::new(vec![]);
+            for (k, v) in m {
+                encode_tag(&mut c, k, v)?;
+            }
+            write!(w, "{{{}:", c.get_ref().len())?;
+            w.write_all(c.get_ref())?;
+            write!(w, "}}")
+        }
+        U::List(l) => {
+            let mut c = std::io::Cursor::new(vec![]);
+            for u in l {
+                encode(&mut c, u)?;
+            }
+            write!(w, "[{}:", c.get_ref().len())?;
+            w.write_all(c.get_ref())?;
+            write!(w, "]")
+        }
+    }
 }
 
 pub fn text(s: String) -> T {
     T::Text(s)
 }
 
-pub fn u_from_stdin_or_die_user_error<'a>(prog_name: &'_ str, stdin_buf: &'a mut Vec<u8>) -> U<'a> {
-    std::io::stdin().lock().read_to_end(stdin_buf);
-    let u = match parse::u_u(stdin_buf) {
-        Ok((rest, u)) => match rest {
-            b"" => u,
-            _ => exec_helpers::die_user_error(prog_name, format!("stdin contained some soup after netencode value: {:?}", String::from_utf8_lossy(rest)))
-        },
-        Err(err) => exec_helpers::die_user_error(prog_name, format!("unable to parse netencode from stdin: {:?}", err))
-    };
-    u
+pub fn t_from_stdin_or_die_user_error<'a>(prog_name: &'_ str) -> T {
+    match t_from_stdin_or_die_user_error_with_rest(prog_name, &vec![]) {
+        None => exec_helpers::die_user_error(prog_name, "stdin was empty"),
+        Some((rest, t)) => {
+            if rest.is_empty() {
+                t
+            } else {
+                exec_helpers::die_user_error(
+                    prog_name,
+                    format!(
+                        "stdin contained some soup after netencode value: {:?}",
+                        String::from_utf8_lossy(&rest)
+                    ),
+                )
+            }
+        }
+    }
+}
+
+/// Read a netencode value from stdin incrementally, return bytes that could not be read.
+/// Nothing if there was nothing to read from stdin & no initial_bytes were provided.
+/// These can be passed back as `initial_bytes` if more values should be read.
+pub fn t_from_stdin_or_die_user_error_with_rest<'a>(
+    prog_name: &'_ str,
+    initial_bytes: &[u8],
+) -> Option<(Vec<u8>, T)> {
+    let mut chonker = Chunkyboi::new(std::io::stdin().lock(), 4096);
+    // The vec to pass to the parser on each step
+    let mut parser_vec: Vec<u8> = initial_bytes.to_vec();
+    // whether stdin was already empty
+    let mut was_empty: bool = false;
+    loop {
+        match chonker.next() {
+            None => {
+                if parser_vec.is_empty() {
+                    return None;
+                } else {
+                    was_empty = true
+                }
+            }
+            Some(Err(err)) => exec_helpers::die_temporary(
+                prog_name,
+                &format!("could not read from stdin: {:?}", err),
+            ),
+            Some(Ok(mut new_bytes)) => parser_vec.append(&mut new_bytes),
+        }
+
+        match parse::t_t(&parser_vec) {
+            Ok((rest, t)) => return Some((rest.to_owned(), t)),
+            Err(nom::Err::Incomplete(Needed)) => {
+                if was_empty {
+                    exec_helpers::die_user_error(
+                        prog_name,
+                        &format!(
+                            "unable to parse netencode from stdin, input incomplete: {:?}",
+                            parser_vec
+                        ),
+                    );
+                }
+                // read more from stdin and try parsing again
+                continue;
+            }
+            Err(err) => exec_helpers::die_user_error(
+                prog_name,
+                &format!("unable to parse netencode from stdin: {:?}", err),
+            ),
+        }
+    }
+}
+
+// iter helper
+// TODO: put into its own module
+struct Chunkyboi<T> {
+    inner: T,
+    buf: Vec<u8>,
+}
+
+impl<R: Read> Chunkyboi<R> {
+    fn new(inner: R, chunksize: usize) -> Self {
+        let buf = vec![0; chunksize];
+        Chunkyboi { inner, buf }
+    }
+}
+
+impl<R: Read> Iterator for Chunkyboi<R> {
+    type Item = std::io::Result<Vec<u8>>;
+
+    fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
+        match self.inner.read(&mut self.buf) {
+            Ok(0) => None,
+            Ok(read) => {
+                // clone a new buffer so we can reuse the internal one
+                Some(Ok(self.buf[..read].to_owned()))
+            }
+            Err(err) => Some(Err(err)),
+        }
+    }
 }
 
 pub mod parse {
-    use super::{T, Tag, U};
+    use super::{Tag, T, U};
 
-    use std::str::FromStr;
-    use std::ops::Neg;
     use std::collections::HashMap;
+    use std::ops::Neg;
+    use std::str::FromStr;
 
-    use nom::{IResult};
-    use nom::branch::{alt};
+    use nom::branch::alt;
     use nom::bytes::streaming::{tag, take};
-    use nom::character::streaming::{digit1, char};
-    use nom::sequence::{tuple};
-    use nom::combinator::{map, map_res, flat_map, map_parser, opt};
+    use nom::character::streaming::{char, digit1};
+    use nom::combinator::{flat_map, map, map_parser, map_res, opt};
     use nom::error::{context, ErrorKind, ParseError};
+    use nom::sequence::tuple;
+    use nom::IResult;
 
     fn unit_t(s: &[u8]) -> IResult<&[u8], ()> {
         let (s, _) = context("unit", tag("u,"))(s)?;
@@ -227,9 +320,9 @@ pub mod parse {
     fn usize_t(s: &[u8]) -> IResult<&[u8], usize> {
         context(
             "usize",
-            map_res(
-                map_res(digit1, |n| std::str::from_utf8(n)),
-                |s| s.parse::<usize>())
+            map_res(map_res(digit1, |n| std::str::from_utf8(n)), |s| {
+                s.parse::<usize>()
+            }),
         )(s)
     }
 
@@ -238,87 +331,77 @@ pub mod parse {
             // This is the point where we check the descriminator;
             // if the beginning char does not match, we can immediately return.
             let (s, _) = char(begin)(s)?;
-            let (s, (len, _)) = tuple((
-                usize_t,
-                char(':')
-            ))(s)?;
-            let (s, (res, _)) = tuple((
-                take(len),
-                char(end)
-            ))(s)?;
+            let (s, (len, _)) = tuple((usize_t, char(':')))(s)?;
+            let (s, (res, _)) = tuple((take(len), char(end)))(s)?;
             Ok((s, res))
         }
     }
 
-
     fn uint_t<'a, I: FromStr + 'a>(t: &'static str) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], I> {
         move |s: &'a [u8]| {
             let (s, (_, _, int, _)) = tuple((
                 tag(t.as_bytes()),
                 char(':'),
-                map_res(
-                    map_res(digit1, |n: &[u8]| std::str::from_utf8(n)),
-                    |s| s.parse::<I>()
-                ),
-                char(',')
+                map_res(map_res(digit1, |n: &[u8]| std::str::from_utf8(n)), |s| {
+                    s.parse::<I>()
+                }),
+                char(','),
             ))(s)?;
             Ok((s, int))
         }
     }
 
     fn bool_t<'a>() -> impl Fn(&'a [u8]) -> IResult<&'a [u8], bool> {
-        context("bool", alt((
-            map(tag("n1:0,"), |_| false),
-            map(tag("n1:1,"), |_| true),
-        )))
-    }
-
-    fn int_t<'a, I: FromStr + Neg<Output=I>>(t: &'static str) -> impl Fn(&'a [u8]) -> IResult<&[u8], I> {
         context(
-            t,
-            move |s: &'a [u8]| {
-                let (s, (_, _, neg, int, _)) = tuple((
-                    tag(t.as_bytes()),
-                    char(':'),
-                    opt(char('-')),
-                    map_res(
-                        map_res(digit1, |n: &[u8]| std::str::from_utf8(n)),
-                        |s| s.parse::<I>()
-                    ),
-                    char(',')
-                ))(s)?;
-                let res = match neg {
-                    Some(_) => -int,
-                    None => int,
-                };
-                Ok((s, res))
-            }
+            "bool",
+            alt((map(tag("n1:0,"), |_| false), map(tag("n1:1,"), |_| true))),
         )
     }
 
+    fn int_t<'a, I: FromStr + Neg<Output = I>>(
+        t: &'static str,
+    ) -> impl Fn(&'a [u8]) -> IResult<&[u8], I> {
+        context(t, move |s: &'a [u8]| {
+            let (s, (_, _, neg, int, _)) = tuple((
+                tag(t.as_bytes()),
+                char(':'),
+                opt(char('-')),
+                map_res(map_res(digit1, |n: &[u8]| std::str::from_utf8(n)), |s| {
+                    s.parse::<I>()
+                }),
+                char(','),
+            ))(s)?;
+            let res = match neg {
+                Some(_) => -int,
+                None => int,
+            };
+            Ok((s, res))
+        })
+    }
+
     fn tag_t(s: &[u8]) -> IResult<&[u8], Tag<String, T>> {
         // recurses into the main parser
-        map(tag_g(t_t),
-            |Tag {tag, val}|
-            Tag {
-                tag: tag.to_string(),
-                val
-            })(s)
+        map(tag_g(t_t), |Tag { tag, val }| Tag {
+            tag: tag.to_string(),
+            val,
+        })(s)
     }
 
     fn tag_g<'a, P, O>(inner: P) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Tag<&'a str, O>>
     where
-        P: Fn(&'a [u8]) -> IResult<&'a [u8], O>
+        P: Fn(&'a [u8]) -> IResult<&'a [u8], O>,
     {
         move |s: &[u8]| {
             let (s, tag) = sized('<', '|')(s)?;
             let (s, val) = inner(s)?;
-            Ok((s, Tag {
-                tag: std::str::from_utf8(tag)
-                    .map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
-                val: Box::new(val)
-            }))
-
+            Ok((
+                s,
+                Tag {
+                    tag: std::str::from_utf8(tag)
+                        .map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
+                    val: Box::new(val),
+                },
+            ))
         }
     }
 
@@ -330,9 +413,9 @@ pub mod parse {
 
     fn text_g(s: &[u8]) -> IResult<&[u8], &str> {
         let (s, res) = sized('t', ',')(s)?;
-        Ok((s,
-            std::str::from_utf8(res)
-                .map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
+        Ok((
+            s,
+            std::str::from_utf8(res).map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
         ))
     }
 
@@ -374,22 +457,24 @@ pub mod parse {
     {
         map_parser(
             sized('[', ']'),
-            nom::multi::many0(inner_no_empty_string(inner))
+            nom::multi::many0(inner_no_empty_string(inner)),
         )
     }
 
     fn record_t<'a>(s: &'a [u8]) -> IResult<&'a [u8], HashMap<String, T>> {
         let (s, r) = record_g(t_t)(s)?;
-        Ok((s,
+        Ok((
+            s,
             r.into_iter()
-            .map(|(k, v)| (k.to_string(), v))
-            .collect::<HashMap<_,_>>()))
+                .map(|(k, v)| (k.to_string(), v))
+                .collect::<HashMap<_, _>>(),
+        ))
     }
 
     fn record_g<'a, P, O>(inner: P) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], HashMap<&'a str, O>>
     where
         O: Clone,
-        P: Fn(&'a [u8]) -> IResult<&'a [u8], O>
+        P: Fn(&'a [u8]) -> IResult<&'a [u8], O>,
     {
         move |s: &'a [u8]| {
             let (s, map) = map_parser(
@@ -397,19 +482,17 @@ pub mod parse {
                 nom::multi::fold_many0(
                     inner_no_empty_string(tag_g(&inner)),
                     HashMap::new(),
-                    |mut acc: HashMap<_,_>, Tag { tag, mut val }| {
-                        // ignore duplicated tag names that appear later
+                    |mut acc: HashMap<_, _>, Tag { tag, mut val }| {
+                        // ignore earlier tags with the same name
                         // according to netencode spec
-                        if ! acc.contains_key(tag) {
-                            acc.insert(tag, *val);
-                        }
+                        let _ = acc.insert(tag, *val);
                         acc
-                    }
-                )
+                    },
+                ),
             )(s)?;
             if map.is_empty() {
                 // records must not be empty, according to the spec
-                Err(nom::Err::Failure((s,nom::error::ErrorKind::Many1)))
+                Err(nom::Err::Failure((s, nom::error::ErrorKind::Many1)))
             } else {
                 Ok((s, map))
             }
@@ -424,7 +507,6 @@ pub mod parse {
             map(tag_g(u_u), |t| U::Sum(t)),
             map(list_g(u_u), U::List),
             map(record_g(u_u), U::Record),
-
             map(bool_t(), |u| U::N1(u)),
             map(uint_t("n3"), |u| U::N3(u)),
             map(uint_t("n6"), |u| U::N6(u)),
@@ -432,7 +514,6 @@ pub mod parse {
             map(int_t("i3"), |u| U::I3(u)),
             map(int_t("i6"), |u| U::I6(u)),
             map(int_t("i7"), |u| U::I7(u)),
-
             // less common
             map(uint_t("n2"), |u| U::N3(u)),
             map(uint_t("n4"), |u| U::N6(u)),
@@ -445,7 +526,7 @@ pub mod parse {
         ))(s)
     }
 
-    pub fn t_t(s: &[u8]) -> IResult<&[u8], T>  {
+    pub fn t_t(s: &[u8]) -> IResult<&[u8], T> {
         alt((
             text,
             binary(),
@@ -453,7 +534,6 @@ pub mod parse {
             map(tag_t, |t| T::Sum(t)),
             map(list_t, |l| T::List(l)),
             map(record_t, |p| T::Record(p)),
-
             map(bool_t(), |u| T::N1(u)),
             // 8, 64 and 128 bit
             map(uint_t("n3"), |u| T::N3(u)),
@@ -462,7 +542,6 @@ pub mod parse {
             map(int_t("i3"), |u| T::I3(u)),
             map(int_t("i6"), |u| T::I6(u)),
             map(int_t("i7"), |u| T::I7(u)),
-
             // less common
             map(uint_t("n2"), |u| T::N3(u)),
             map(uint_t("n4"), |u| T::N6(u)),
@@ -481,30 +560,18 @@ pub mod parse {
 
         #[test]
         fn test_parse_unit_t() {
-            assert_eq!(
-                unit_t("u,".as_bytes()),
-                Ok(("".as_bytes(), ()))
-            );
+            assert_eq!(unit_t("u,".as_bytes()), Ok(("".as_bytes(), ())));
         }
 
         #[test]
         fn test_parse_bool_t() {
-            assert_eq!(
-                bool_t()("n1:0,".as_bytes()),
-                Ok(("".as_bytes(), false))
-            );
-            assert_eq!(
-                bool_t()("n1:1,".as_bytes()),
-                Ok(("".as_bytes(), true))
-            );
+            assert_eq!(bool_t()("n1:0,".as_bytes()), Ok(("".as_bytes(), false)));
+            assert_eq!(bool_t()("n1:1,".as_bytes()), Ok(("".as_bytes(), true)));
         }
 
         #[test]
         fn test_parse_usize_t() {
-            assert_eq!(
-                usize_t("32foo".as_bytes()),
-                Ok(("foo".as_bytes(), 32))
-            );
+            assert_eq!(usize_t("32foo".as_bytes()), Ok(("foo".as_bytes(), 32)));
         }
 
         #[test]
@@ -515,7 +582,10 @@ pub mod parse {
             );
             assert_eq!(
                 uint_t::<u8>("n3")("n3:1024,abc".as_bytes()),
-                Err(nom::Err::Error(("1024,abc".as_bytes(), nom::error::ErrorKind::MapRes)))
+                Err(nom::Err::Error((
+                    "1024,abc".as_bytes(),
+                    nom::error::ErrorKind::MapRes
+                )))
             );
             assert_eq!(
                 int_t::<i64>("i6")("i6:-23,abc".as_bytes()),
@@ -544,18 +614,21 @@ pub mod parse {
             assert_eq!(
                 text("t5:hello,".as_bytes()),
                 Ok(("".as_bytes(), T::Text("hello".to_owned()))),
-                "{}", r"t5:hello,"
+                "{}",
+                r"t5:hello,"
             );
             assert_eq!(
                 text("t4:fo".as_bytes()),
                 // The content of the text should be 4 long
                 Err(nom::Err::Incomplete(nom::Needed::Size(4))),
-                "{}", r"t4:fo,"
+                "{}",
+                r"t4:fo,"
             );
             assert_eq!(
                 text("t9:今ζ—₯は,".as_bytes()),
                 Ok(("".as_bytes(), T::Text("今ζ—₯は".to_owned()))),
-                "{}", r"t9:今ζ—₯は,"
+                "{}",
+                r"t9:今ζ—₯は,"
             );
         }
 
@@ -564,24 +637,28 @@ pub mod parse {
             assert_eq!(
                 binary()("b5:hello,".as_bytes()),
                 Ok(("".as_bytes(), T::Binary(Vec::from("hello".to_owned())))),
-                "{}", r"b5:hello,"
+                "{}",
+                r"b5:hello,"
             );
             assert_eq!(
                 binary()("b4:fo".as_bytes()),
                 // The content of the byte should be 4 long
                 Err(nom::Err::Incomplete(nom::Needed::Size(4))),
-                "{}", r"b4:fo,"
+                "{}",
+                r"b4:fo,"
             );
             assert_eq!(
                 binary()("b4:foob".as_bytes()),
                 // The content is 4 bytes now, but the finishing , is missing
                 Err(nom::Err::Incomplete(nom::Needed::Size(1))),
-                    "{}", r"b4:fo,"
-                );
+                "{}",
+                r"b4:fo,"
+            );
             assert_eq!(
                 binary()("b9:今ζ—₯は,".as_bytes()),
                 Ok(("".as_bytes(), T::Binary(Vec::from("今ζ—₯は".as_bytes())))),
-                "{}", r"b9:今ζ—₯は,"
+                "{}",
+                r"b9:今ζ—₯は,"
             );
         }
 
@@ -590,25 +667,23 @@ pub mod parse {
             assert_eq!(
                 list_t("[0:]".as_bytes()),
                 Ok(("".as_bytes(), vec![])),
-                "{}", r"[0:]"
+                "{}",
+                r"[0:]"
             );
             assert_eq!(
                 list_t("[6:u,u,u,]".as_bytes()),
-                Ok(("".as_bytes(), vec![
-                    T::Unit,
-                    T::Unit,
-                    T::Unit,
-                ])),
-                "{}", r"[6:u,u,u,]"
+                Ok(("".as_bytes(), vec![T::Unit, T::Unit, T::Unit,])),
+                "{}",
+                r"[6:u,u,u,]"
             );
             assert_eq!(
                 list_t("[15:u,[7:t3:foo,]u,]".as_bytes()),
-                Ok(("".as_bytes(), vec![
-                    T::Unit,
-                    T::List(vec![T::Text("foo".to_owned())]),
-                    T::Unit,
-                ])),
-                "{}", r"[15:u,[7:t3:foo,]u,]"
+                Ok((
+                    "".as_bytes(),
+                    vec![T::Unit, T::List(vec![T::Text("foo".to_owned())]), T::Unit,]
+                )),
+                "{}",
+                r"[15:u,[7:t3:foo,]u,]"
             );
         }
 
@@ -616,27 +691,40 @@ pub mod parse {
         fn test_record() {
             assert_eq!(
                 record_t("{21:<1:a|u,<1:b|u,<1:c|u,}".as_bytes()),
-                Ok(("".as_bytes(), vec![
-                    ("a".to_owned(), T::Unit),
-                    ("b".to_owned(), T::Unit),
-                    ("c".to_owned(), T::Unit),
-                ].into_iter().collect::<HashMap<String, T>>())),
-                "{}", r"{21:<1:a|u,<1:b|u,<1:c|u,}"
+                Ok((
+                    "".as_bytes(),
+                    vec![
+                        ("a".to_owned(), T::Unit),
+                        ("b".to_owned(), T::Unit),
+                        ("c".to_owned(), T::Unit),
+                    ]
+                    .into_iter()
+                    .collect::<HashMap<String, T>>()
+                )),
+                "{}",
+                r"{21:<1:a|u,<1:b|u,<1:c|u,}"
             );
             // duplicated keys are ignored (first is taken)
             assert_eq!(
                 record_t("{25:<1:a|u,<1:b|u,<1:a|i1:-1,}".as_bytes()),
-                Ok(("".as_bytes(), vec![
-                    ("a".to_owned(), T::Unit),
-                    ("b".to_owned(), T::Unit),
-                ].into_iter().collect::<HashMap<_,_>>())),
-                "{}", r"{25:<1:a|u,<1:b|u,<1:a|i1:-1,}"
+                Ok((
+                    "".as_bytes(),
+                    vec![("a".to_owned(), T::I3(-1)), ("b".to_owned(), T::Unit),]
+                        .into_iter()
+                        .collect::<HashMap<_, _>>()
+                )),
+                "{}",
+                r"{25:<1:a|u,<1:b|u,<1:a|i1:-1,}"
             );
             // empty records are not allowed
             assert_eq!(
                 record_t("{0:}".as_bytes()),
-                Err(nom::Err::Failure(("".as_bytes(), nom::error::ErrorKind::Many1))),
-                "{}", r"{0:}"
+                Err(nom::Err::Failure((
+                    "".as_bytes(),
+                    nom::error::ErrorKind::Many1
+                ))),
+                "{}",
+                r"{0:}"
             );
         }
 
@@ -645,37 +733,62 @@ pub mod parse {
             assert_eq!(
                 t_t("n3:255,".as_bytes()),
                 Ok(("".as_bytes(), T::N3(255))),
-                "{}", r"n3:255,"
+                "{}",
+                r"n3:255,"
             );
             assert_eq!(
                 t_t("t6:halloo,".as_bytes()),
                 Ok(("".as_bytes(), T::Text("halloo".to_owned()))),
-                "{}", r"t6:halloo,"
+                "{}",
+                r"t6:halloo,"
             );
             assert_eq!(
                 t_t("<3:foo|t6:halloo,".as_bytes()),
-                Ok(("".as_bytes(), T::Sum (Tag {
-                    tag: "foo".to_owned(),
-                    val: Box::new(T::Text("halloo".to_owned()))
-                }))),
-                "{}", r"<3:foo|t6:halloo,"
+                Ok((
+                    "".as_bytes(),
+                    T::Sum(Tag {
+                        tag: "foo".to_owned(),
+                        val: Box::new(T::Text("halloo".to_owned()))
+                    })
+                )),
+                "{}",
+                r"<3:foo|t6:halloo,"
             );
             // { a: Unit
             // , foo: List <A: Unit | B: List i3> }
             assert_eq!(
                 t_t("{52:<1:a|u,<3:foo|[33:<1:A|u,<1:A|n1:1,<1:B|[7:i3:127,]]}".as_bytes()),
-                Ok(("".as_bytes(), T::Record(vec![
-                    ("a".to_owned(), T::Unit),
-                    ("foo".to_owned(), T::List(vec![
-                        T::Sum(Tag { tag: "A".to_owned(), val: Box::new(T::Unit) }),
-                        T::Sum(Tag { tag: "A".to_owned(), val: Box::new(T::N1(true)) }),
-                        T::Sum(Tag { tag: "B".to_owned(), val: Box::new(T::List(vec![T::I3(127)])) }),
-                    ]))
-                ].into_iter().collect::<HashMap<String, T>>()))),
-                "{}", r"{52:<1:a|u,<3:foo|[33:<1:A|u,<1:A|n1:1,<1:B|[7:i3:127,]]}"
+                Ok((
+                    "".as_bytes(),
+                    T::Record(
+                        vec![
+                            ("a".to_owned(), T::Unit),
+                            (
+                                "foo".to_owned(),
+                                T::List(vec![
+                                    T::Sum(Tag {
+                                        tag: "A".to_owned(),
+                                        val: Box::new(T::Unit)
+                                    }),
+                                    T::Sum(Tag {
+                                        tag: "A".to_owned(),
+                                        val: Box::new(T::N1(true))
+                                    }),
+                                    T::Sum(Tag {
+                                        tag: "B".to_owned(),
+                                        val: Box::new(T::List(vec![T::I3(127)]))
+                                    }),
+                                ])
+                            )
+                        ]
+                        .into_iter()
+                        .collect::<HashMap<String, T>>()
+                    )
+                )),
+                "{}",
+                r"{52:<1:a|u,<3:foo|[33:<1:A|u,<1:A|n1:1,<1:B|[7:i3:127,]]}"
             );
         }
-
     }
 }
 
@@ -735,7 +848,10 @@ pub mod dec {
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match u {
                 U::Binary(b) => Ok(b),
-                other => Err(DecodeError(format!("Cannot decode {:?} into Binary", other))),
+                other => Err(DecodeError(format!(
+                    "Cannot decode {:?} into Binary",
+                    other
+                ))),
             }
         }
     }
@@ -766,16 +882,17 @@ pub mod dec {
     pub struct Record<T>(pub T);
 
     impl<'a, Inner> Decoder<'a> for Record<Inner>
-        where Inner: Decoder<'a>
+    where
+        Inner: Decoder<'a>,
     {
         type A = HashMap<&'a str, Inner::A>;
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match u {
-                U::Record(map) =>
-                    map.into_iter()
+                U::Record(map) => map
+                    .into_iter()
                     .map(|(k, v)| self.0.dec(v).map(|v2| (k, v2)))
                     .collect::<Result<Self::A, _>>(),
-                o => Err(DecodeError(format!("Cannot decode {:?} into record", o)))
+                o => Err(DecodeError(format!("Cannot decode {:?} into record", o))),
             }
         }
     }
@@ -784,18 +901,22 @@ pub mod dec {
     #[derive(Clone, Copy)]
     pub struct RecordDot<'a, T> {
         pub field: &'a str,
-        pub inner: T
+        pub inner: T,
     }
 
-    impl <'a, Inner> Decoder<'a> for RecordDot<'_, Inner>
-        where Inner: Decoder<'a> + Clone
+    impl<'a, Inner> Decoder<'a> for RecordDot<'_, Inner>
+    where
+        Inner: Decoder<'a> + Clone,
     {
         type A = Inner::A;
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match Record(self.inner.clone()).dec(u) {
                 Ok(mut map) => match map.remove(self.field) {
                     Some(inner) => Ok(inner),
-                    None => Err(DecodeError(format!("Cannot find `{}` in record map", self.field))),
+                    None => Err(DecodeError(format!(
+                        "Cannot find `{}` in record map",
+                        self.field
+                    ))),
                 },
                 Err(err) => Err(err),
             }
@@ -804,23 +925,27 @@ pub mod dec {
 
     /// Equals one of the listed `A`s exactly, after decoding.
     #[derive(Clone)]
-    pub struct OneOf<T, A>{
+    pub struct OneOf<T, A> {
         pub inner: T,
         pub list: Vec<A>,
     }
 
-    impl <'a, Inner> Decoder<'a> for OneOf<Inner, Inner::A>
-        where Inner: Decoder<'a>,
-              Inner::A: Display + Debug + PartialEq
+    impl<'a, Inner> Decoder<'a> for OneOf<Inner, Inner::A>
+    where
+        Inner: Decoder<'a>,
+        Inner::A: Display + Debug + PartialEq,
     {
         type A = Inner::A;
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match self.inner.dec(u) {
                 Ok(inner) => match self.list.iter().any(|x| x.eq(&inner)) {
                     true => Ok(inner),
-                    false => Err(DecodeError(format!("{} is not one of {:?}", inner, self.list)))
+                    false => Err(DecodeError(format!(
+                        "{} is not one of {:?}",
+                        inner, self.list
+                    ))),
                 },
-                Err(err) => Err(err)
+                Err(err) => Err(err),
             }
         }
     }
@@ -829,16 +954,16 @@ pub mod dec {
     #[derive(Clone)]
     pub struct Try<T>(pub T);
 
-    impl <'a, Inner> Decoder<'a> for Try<Inner>
-        where Inner: Decoder<'a>
+    impl<'a, Inner> Decoder<'a> for Try<Inner>
+    where
+        Inner: Decoder<'a>,
     {
         type A = Option<Inner::A>;
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match self.0.dec(u) {
                 Ok(inner) => Ok(Some(inner)),
-                Err(err) => Ok(None)
+                Err(err) => Ok(None),
             }
         }
     }
-
 }
diff --git a/users/Profpatsch/netencode/pretty.rs b/users/Profpatsch/netencode/pretty.rs
index 8fec24a60e..935c3d4a8a 100644
--- a/users/Profpatsch/netencode/pretty.rs
+++ b/users/Profpatsch/netencode/pretty.rs
@@ -1,6 +1,6 @@
 extern crate netencode;
 
-use netencode::{U, T, Tag};
+use netencode::{Tag, T, U};
 
 pub enum Pretty {
     Single {
@@ -20,7 +20,7 @@ pub enum Pretty {
         r#type: char,
         length: String,
         vals: Vec<Pretty>,
-        trailer: char
+        trailer: char,
     },
 }
 
@@ -39,7 +39,7 @@ impl Pretty {
                 r#type: 't',
                 length: format!("{}:", s.len()),
                 val: s.to_string(),
-                trailer: ','
+                trailer: ',',
             },
             U::Binary(s) => Pretty::Single {
                 r#type: 'b',
@@ -47,15 +47,18 @@ impl Pretty {
                 // For pretty printing we want the string to be visible obviously.
                 // Instead of not supporting binary, let’s use lossy conversion.
                 val: String::from_utf8_lossy(s).into_owned(),
-                trailer: ','
+                trailer: ',',
             },
-            U::Sum(Tag{tag, val}) => Self::pretty_tag(tag, Self::from_u(*val)),
+            U::Sum(Tag { tag, val }) => Self::pretty_tag(tag, Self::from_u(*val)),
             U::Record(m) => Pretty::Multi {
                 r#type: '{',
                 // TODO: we are losing the size here, should we recompute it? Keep it?
                 length: String::from(""),
-                vals: m.into_iter().map(|(k, v)| Self::pretty_tag(k, Self::from_u(v))).collect(),
-                trailer: '}'
+                vals: m
+                    .into_iter()
+                    .map(|(k, v)| Self::pretty_tag(k, Self::from_u(v)))
+                    .collect(),
+                trailer: '}',
             },
             U::List(l) => Pretty::Multi {
                 r#type: '[',
@@ -68,13 +71,14 @@ impl Pretty {
     }
 
     fn scalar<D>(r#type: char, length: &str, d: D) -> Pretty
-    where D: std::fmt::Display
+    where
+        D: std::fmt::Display,
     {
         Pretty::Single {
             r#type,
             length: length.to_string(),
             val: format!("{}", d),
-            trailer: ','
+            trailer: ',',
         }
     }
 
@@ -89,43 +93,62 @@ impl Pretty {
     }
 
     pub fn print_multiline<W>(&self, mut w: &mut W) -> std::io::Result<()>
-        where W: std::io::Write
+    where
+        W: std::io::Write,
     {
         Self::go(&mut w, self, 0, true);
         write!(w, "\n")
     }
 
     fn go<W>(mut w: &mut W, p: &Pretty, depth: usize, is_newline: bool) -> std::io::Result<()>
-        where W: std::io::Write
+    where
+        W: std::io::Write,
     {
-        const full : usize = 4;
-        const half : usize = 2;
-        let i = &vec![b' '; depth*full];
-        let iandhalf = &vec![b' '; depth*full + half];
-        let (i, iandhalf) = unsafe {(
-            std::str::from_utf8_unchecked(i),
-            std::str::from_utf8_unchecked(iandhalf),
-        )};
+        const full: usize = 4;
+        const half: usize = 2;
+        let i = &vec![b' '; depth * full];
+        let iandhalf = &vec![b' '; depth * full + half];
+        let (i, iandhalf) = unsafe {
+            (
+                std::str::from_utf8_unchecked(i),
+                std::str::from_utf8_unchecked(iandhalf),
+            )
+        };
         if is_newline {
             write!(&mut w, "{}", i);
         }
         match p {
-            Pretty::Single {r#type, length, val, trailer} =>
-                write!(&mut w, "{} {}{}", r#type, val, trailer),
-            Pretty::Tag { r#type, length, key, inner, val } => {
+            Pretty::Single {
+                r#type,
+                length,
+                val,
+                trailer,
+            } => write!(&mut w, "{} {}{}", r#type, val, trailer),
+            Pretty::Tag {
+                r#type,
+                length,
+                key,
+                inner,
+                val,
+            } => {
                 write!(&mut w, "{} {} {}", r#type, key, inner)?;
                 Self::go::<W>(&mut w, val, depth, false)
-            },
+            }
             // if the length is 0 or 1, we print on one line,
             // only if there’s more than one element we split the resulting value.
             // we never break lines on arbitrary column sizes, since that is just silly.
-            Pretty::Multi {r#type, length, vals, trailer} => match vals.len() {
+            Pretty::Multi {
+                r#type,
+                length,
+                vals,
+                trailer,
+            } => match vals.len() {
                 0 => write!(&mut w, "{} {}", r#type, trailer),
                 1 => {
                     write!(&mut w, "{} ", r#type);
                     Self::go::<W>(&mut w, &vals[0], depth, false)?;
                     write!(&mut w, "{}", trailer)
-                },
+                }
                 more => {
                     write!(&mut w, "\n{}{} \n", iandhalf, r#type)?;
                     for v in vals {
diff --git a/users/Profpatsch/netstring/default.nix b/users/Profpatsch/netstring/default.nix
index b4990cae67..047fe6bae1 100644
--- a/users/Profpatsch/netstring/default.nix
+++ b/users/Profpatsch/netstring/default.nix
@@ -1,20 +1,16 @@
 { lib, pkgs, depot, ... }:
 let
-  toNetstring = s:
-    "${toString (builtins.stringLength s)}:${s},";
+  toNetstring = depot.nix.netstring.fromString;
 
   toNetstringList = xs:
     lib.concatStrings (map toNetstring xs);
 
-  toNetstringKeyVal = attrs:
-    lib.concatStrings
-      (lib.mapAttrsToList
-        (k: v: toNetstring (toNetstring k + toNetstring v))
-        attrs);
+  toNetstringKeyVal = depot.nix.netstring.attrsToKeyValList;
 
-  python-netstring = depot.users.Profpatsch.writers.python3Lib {
-    name = "netstring";
-  } ''
+  python-netstring = depot.users.Profpatsch.writers.python3Lib
+    {
+      name = "netstring";
+    } ''
     def read_netstring(bytes):
         (int_length, rest) = bytes.split(sep=b':', maxsplit=1)
         val = rest[:int(int_length)]
@@ -39,9 +35,10 @@ let
         return res
   '';
 
-  rust-netstring = depot.nix.writers.rustSimpleLib {
-    name = "netstring";
-  } ''
+  rust-netstring = depot.nix.writers.rustSimpleLib
+    {
+      name = "netstring";
+    } ''
     pub fn to_netstring(s: &[u8]) -> Vec<u8> {
         let len = s.len();
         // length of the integer as ascii
@@ -55,12 +52,13 @@ let
     }
   '';
 
-in depot.nix.readTree.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   inherit
     toNetstring
     toNetstringList
     toNetstringKeyVal
     python-netstring
     rust-netstring
-      ;
+    ;
 }
diff --git a/users/Profpatsch/netstring/tests/default.nix b/users/Profpatsch/netstring/tests/default.nix
index 710ba3d305..6a1062988f 100644
--- a/users/Profpatsch/netstring/tests/default.nix
+++ b/users/Profpatsch/netstring/tests/default.nix
@@ -2,12 +2,13 @@
 
 let
 
-  python-netstring-test = depot.users.Profpatsch.writers.python3 {
-    name = "python-netstring-test";
-    libraries = p: [
-      depot.users.Profpatsch.netstring.python-netstring
-    ];
-  } ''
+  python-netstring-test = depot.users.Profpatsch.writers.python3
+    {
+      name = "python-netstring-test";
+      libraries = p: [
+        depot.users.Profpatsch.netstring.python-netstring
+      ];
+    } ''
     import netstring
 
     def assEq(left, right):
@@ -33,12 +34,13 @@ let
     )
   '';
 
-  rust-netstring-test = depot.nix.writers.rustSimple {
-    name = "rust-netstring-test";
-    dependencies = [
-      depot.users.Profpatsch.netstring.rust-netstring
-    ];
-  } ''
+  rust-netstring-test = depot.nix.writers.rustSimple
+    {
+      name = "rust-netstring-test";
+      dependencies = [
+        depot.users.Profpatsch.netstring.rust-netstring
+      ];
+    } ''
     extern crate netstring;
 
     fn main() {
@@ -53,7 +55,8 @@ let
     }
   '';
 
-in depot.nix.readTree.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   inherit
     python-netstring-test
     rust-netstring-test
diff --git a/users/Profpatsch/nix-home/README.md b/users/Profpatsch/nix-home/README.md
new file mode 100644
index 0000000000..222978bc8c
--- /dev/null
+++ b/users/Profpatsch/nix-home/README.md
@@ -0,0 +1,7 @@
+# nix-home
+
+My very much simplified version of [home-manager](https://github.com/nix-community/home-manager/).
+
+Only takes care about installing symlinks into `$HOME`, and uses [`GNU stow`](https://www.gnu.org/software/stow/) for doing the actual mutating.
+
+No support for services (yet).
diff --git a/users/Profpatsch/nix-home/default.nix b/users/Profpatsch/nix-home/default.nix
index cf9ab0d4d4..ee154c549a 100644
--- a/users/Profpatsch/nix-home/default.nix
+++ b/users/Profpatsch/nix-home/default.nix
@@ -2,102 +2,211 @@
 
 let
   bins = depot.nix.getBins pkgs.stow [ "stow" ]
-      // depot.nix.getBins pkgs.coreutils [ "mkdir" "ln" "printenv" "rm" ]
-      // depot.nix.getBins pkgs.xe [ "xe" ]
-      // depot.nix.getBins pkgs.lr [ "lr" ]
-      // depot.nix.getBins pkgs.nix [ "nix-store" ]
-      ;
+    // depot.nix.getBins pkgs.coreutils [ "mkdir" "ln" "printenv" "rm" ]
+    // depot.nix.getBins pkgs.xe [ "xe" ]
+    // depot.nix.getBins pkgs.lr [ "lr" ]
+    // depot.nix.getBins pkgs.nix [ "nix-store" ]
+  ;
 
   # run stow to populate the target directory with the given stow package, read from stowDir.
   # Bear in mind that `stowDirOriginPath` should always be semantically bound to the given `stowDir`, otherwise stow might become rather confused.
-  runStow = {
-    # β€œstow package” to stow (see manpage)
-    stowPackage,
-    # β€œtarget directory” to stow in (see manpage)
-    targetDir,
-    # The β€œstow directory” (see manpage), containing β€œstow packages” (see manpage)
-    stowDir,
-    # representative directory for the stowDir in the file system, against which stow will create relative links.
-    # ATTN: this is always overwritten with the contents of `stowDir`! You shouldn’t re-use the same `stowDirOriginPath` for different `stowDir`s, otherwise there might be surprises.
-    stowDirOriginPath,
-  }: depot.nix.writeExecline "stow-${stowPackage}" {} [
-    # first, create a temporary stow directory to use as source
-    # (stow will use it to determine the origin of files)
-    "if" [ bins.mkdir "-p" stowDirOriginPath ]
-    # remove old symlinks
-    "if" [
-      "pipeline" [
-        bins.lr "-0" "-t" "depth == 1 && type == l" stowDirOriginPath
+  runStow =
+    {
+      # β€œstow package” to stow (see manpage)
+      # TODO: allow this function to un-stow multiple packages!
+      stowPackage
+    , # β€œtarget directory” to stow in (see manpage)
+      targetDir
+    , # The β€œstow directory” (see manpage), containing β€œstow packages” (see manpage)
+      stowDir
+    , # representative directory for the stowDir in the file system, against which stow will create relative links.
+      # ATTN: this is always overwritten with the contents of `stowDir`! You shouldn’t re-use the same `stowDirOriginPath` for different `stowDir`s, otherwise there might be surprises.
+      stowDirOriginPath
+    ,
+    }: depot.nix.writeExecline "stow-${stowPackage}" { } [
+      # first, create a temporary stow directory to use as source
+      # (stow will use it to determine the origin of files)
+      "if"
+      [ bins.mkdir "-p" stowDirOriginPath ]
+      # remove old symlinks
+      "if"
+      [
+        "pipeline"
+        [
+          bins.lr
+          "-0"
+          "-t"
+          "depth == 1 && type == l"
+          stowDirOriginPath
+        ]
+        bins.xe
+        "-0"
+        bins.rm
       ]
-      bins.xe "-0" bins.rm
-    ]
-    # create an indirect gc root so our config is not cleaned under our asses by a garbage collect
-    "if" [
-      bins.nix-store
+      # create an indirect gc root so our config is not cleaned under our asses by a garbage collect
+      "if"
+      [
+        bins.nix-store
         "--realise"
         "--indirect"
-        "--add-root" "${stowDirOriginPath}/.nix-stowdir-gc-root"
+        "--add-root"
+        "${stowDirOriginPath}/.nix-stowdir-gc-root"
         stowDir
-    ]
-    # populate with new stow targets
-    "if" [
-      "elglob" "-w0" "stowPackages" "${stowDir}/*"
-      bins.ln "--force" "-st" stowDirOriginPath "$stowPackages"
-    ]
-    # stow always looks for $HOME/.stowrc to read more arguments
-    "export" "HOME" "/homeless-shelter"
-    bins.stow
+      ]
+      # populate with new stow targets
+      "if"
+      [
+        "elglob"
+        "-w0"
+        "stowPackages"
+        "${stowDir}/*"
+        bins.ln
+        "--force"
+        "-st"
+        stowDirOriginPath
+        "$stowPackages"
+      ]
+      # stow always looks for $HOME/.stowrc to read more arguments
+      "export"
+      "HOME"
+      "/homeless-shelter"
+      bins.stow
       # always run restow for now; this does more stat but will remove stale links
       "--restow"
-      "--dir" stowDirOriginPath
-      "--target" targetDir
+      "--dir"
+      stowDirOriginPath
+      "--target"
+      targetDir
       stowPackage
-  ];
+    ];
 
   # create a stow dir from a list of drv paths and a stow package name.
   makeStowDir =
     (with depot.nix.yants;
-     defun
-       [ (list (struct {
+    defun
+      [
+        (list (struct {
           originalDir = drv;
           stowPackage = string;
         }))
         drv
-       ] )
-    (dirs:
-      depot.nix.runExecline "make-stow-dir" {
-        stdin = lib.pipe dirs [
-          (map depot.users.Profpatsch.netencode.gen.dwim)
-          depot.users.Profpatsch.netstring.toNetstringList
-        ];
-      } [
-        "importas" "out" "out"
-        "if" [ bins.mkdir "-p" "$out" ]
-        "forstdin" "-d" "" "-o" "0" "line"
-        "pipeline" [
-          depot.users.Profpatsch.execline.print-one-env "line"
-        ]
-        depot.users.Profpatsch.netencode.record-splice-env
-        "importas" "-ui" "originalDir" "originalDir"
-        "importas" "-ui" "stowPackage" "stowPackage"
-        bins.ln "-sT" "$originalDir" "\${out}/\${stowPackage}"
-      ]);
+      ])
+      (dirs:
+        depot.nix.runExecline "make-stow-dir"
+          {
+            stdin = lib.pipe dirs [
+              (map depot.users.Profpatsch.netencode.gen.dwim)
+              depot.users.Profpatsch.netstring.toNetstringList
+            ];
+          } [
+          "importas"
+          "out"
+          "out"
+          "if"
+          [ bins.mkdir "-p" "$out" ]
+          "forstdin"
+          "-d"
+          ""
+          "-o"
+          "0"
+          "line"
+          "pipeline"
+          [
+            depot.users.Profpatsch.execline.print-one-env
+            "line"
+          ]
+          depot.users.Profpatsch.netencode.record-splice-env
+          "importas"
+          "-ui"
+          "originalDir"
+          "originalDir"
+          "importas"
+          "-ui"
+          "stowPackage"
+          "stowPackage"
+          bins.ln
+          "-sT"
+          "$originalDir"
+          "\${out}/\${stowPackage}"
+        ]);
+
+  # this is a dumb way of generating a pure list of packages from a depot namespace.
+  readTreeNamespaceDrvs = namespace:
+    lib.pipe namespace [
+      (lib.filterAttrs (_: v: lib.isDerivation v))
+      (lib.mapAttrsToList (k: v: {
+        name = k;
+        drv = v;
+      }))
+    ];
+
+  scriptsStow =
+    lib.pipe { } [
+      (_: makeStowDir [{
+        stowPackage = "scripts";
+        originalDir = pkgs.linkFarm "scripts-farm"
+          ([
+            {
+              name = "scripts/ytextr";
+              path = depot.users.Profpatsch.ytextr;
+            }
+            {
+              name = "scripts/lorri-wait-for-eval";
+              path = depot.users.Profpatsch.lorri-wait-for-eval;
+            }
+            {
+              name = "scripts/lw";
+              path = depot.users.Profpatsch.lorri-wait-for-eval;
+            }
+
+          ]
+          ++
+          (lib.pipe depot.users.Profpatsch.aliases [
+            readTreeNamespaceDrvs
+            (map ({ name, drv }: {
+              name = "scripts/${name}";
+              path = drv;
+            }))
+          ]));
+      }])
+      (d: runStow {
+        stowDir = d;
+        stowPackage = "scripts";
+        targetDir = "/home/philip";
+        stowDirOriginPath = "/home/philip/.local/share/nix-home/stow-origin";
+      })
+    ];
+
+
+
+  terminalEmulatorStow =
+    lib.pipe { } [
+      (_: makeStowDir [{
+        stowPackage = "terminal-emulator";
+        originalDir = pkgs.linkFarm "terminal-emulator-farm"
+          ([
+            {
+              name = "bin/terminal-emulator";
+              path = depot.users.Profpatsch.alacritty;
+            }
+          ]);
+
+      }])
+      (d: runStow {
+        stowDir = d;
+        stowPackage = "terminal-emulator";
+        targetDir = "/home/philip";
+        # TODO: this should only be done once, in a single runStow instead of multiple
+        stowDirOriginPath = "/home/philip/.local/share/nix-home/stow-origin-terminal-emulator";
+      })
+    ];
 
 in
 
-# TODO: temp setup
-lib.pipe {} [
-  (_: makeStowDir [{
-    stowPackage = "scripts";
-    originalDir = pkgs.linkFarm "scripts-farm" [
-        { name = "scripts/ytextr";
-          path = depot.users.Profpatsch.ytextr; }
-      ];
-  }])
-  (d: runStow {
-    stowDir = d;
-    stowPackage = "scripts";
-    targetDir = "/home/philip";
-    stowDirOriginPath = "/home/philip/.local/share/nix-home/stow-origin";
-  })
+# TODO: run multiple stows with runStow?
+  # TODO: temp setup
+depot.nix.writeExecline "nix-home" { } [
+  "if"
+  [ scriptsStow ]
+  terminalEmulatorStow
 ]
diff --git a/users/Profpatsch/nix-tools.nix b/users/Profpatsch/nix-tools.nix
new file mode 100644
index 0000000000..4f29274573
--- /dev/null
+++ b/users/Profpatsch/nix-tools.nix
@@ -0,0 +1,159 @@
+{ depot, pkgs, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.nix [ "nix-build" "nix-instantiate" ];
+
+  # TODO: both of these don’t prevent `result` from being created. good? bad?
+
+  # Usage (execline syntax):
+  #    nix-run { -A foo <more_nix_options> } args...
+  #
+  # Takes an execline block of `nix-build` arguments, which should produce an executable store path.
+  # Then runs the store path with `prog...`.
+  nix-run = depot.nix.writeExecline "nix-run" { argMode = "env"; } [
+    "backtick"
+    "-iE"
+    "storepath"
+    [
+      runblock
+      "1"
+      bins.nix-build
+    ]
+    runblock
+    "-r"
+    "2"
+    "$storepath"
+  ];
+
+  # Usage (execline syntax):
+  #    nix-run-bin { -A foo <more_nix_options> } <foo_bin_name> args...
+  #
+  # Takes an execline block of `nix-build` arguments, which should produce a store path with a bin/ directory in it.
+  # Then runs the given command line with the given arguments. All executables in the built storepath’s bin directory are prepended to `PATH`.
+  nix-run-bin = depot.nix.writeExecline "nix-run-bin" { argMode = "env"; } [
+    "backtick"
+    "-iE"
+    "storepath"
+    [
+      runblock
+      "1"
+      bins.nix-build
+    ]
+    "importas"
+    "-ui"
+    "PATH"
+    "PATH"
+    "export"
+    "PATH"
+    "\${storepath}/bin:\${PATH}"
+    runblock
+    "-r"
+    "2"
+  ];
+
+  nix-eval = depot.nix.writeExecline "nix-eval" { } [
+    bins.nix-instantiate
+    "--read-write-mode"
+    "--eval"
+    "--strict"
+    "$@"
+  ];
+
+  # This is a rewrite of execline’s runblock.
+  # It adds the feature that instead of just
+  # executing the block it reads, it can also
+  # pass it as argv to given commands.
+  #
+  # This is going to be added to a future version
+  # of execline by skarnet, but for now it’s easier
+  # to just dirtily reimplement it in Python.
+  #
+  # TODO: this was added to recent execline versions,
+  # but it doesn’t seem to be a drop-in replacement,
+  # if I use execline’s runblock in nix-run-bin above,
+  # I get errors like
+  # > export: fatal: unable to exec runblock: Success
+  runblock = pkgs.writers.writePython3 "runblock"
+    {
+      flakeIgnore = [ "E501" "E226" ];
+    } ''
+    import sys
+    import os
+    from pathlib import Path
+
+    skip = False
+    one = sys.argv[1]
+    if one == "-r":
+        skip = True
+        block_number = int(sys.argv[2])
+        block_start = 3
+    elif one.startswith("-"):
+        print("runblock-python: only -r supported", file=sys.stderr)
+        sys.exit(100)
+    else:
+        block_number = int(one)
+        block_start = 2
+
+    execline_argv_no = int(os.getenvb(b"#"))
+    runblock_argv = [os.getenv(str(no)) for no in range(1, execline_argv_no + 1)]
+
+
+    def parse_block(args):
+        new_args = []
+        if args == []:
+            print(
+                "runblock-python: empty block",
+                file=sys.stderr
+            )
+            sys.exit(100)
+        for arg in args:
+            if arg == "":
+                break
+            elif arg.startswith(" "):
+                new_args.append(arg[1:])
+            else:
+                print(
+                    "runblock-python: unterminated block: {}".format(args),
+                    file=sys.stderr
+                )
+                sys.exit(100)
+        args_rest = args[len(new_args)+1:]
+        return (new_args, args_rest)
+
+
+    if skip:
+        rest = runblock_argv
+        for _ in range(0, block_number-1):
+            (_, rest) = parse_block(rest)
+        new_argv = rest
+    else:
+        new_argv = []
+        rest = runblock_argv
+        for _ in range(0, block_number):
+            (new_argv, rest) = parse_block(rest)
+
+    given_argv = sys.argv[block_start:]
+    run = given_argv + new_argv
+    if os.path.isabs(run[0]):
+        # TODO: ideally I’d check if it’s an executable here, but it was too hard to figure out and I couldn’t be bothered tbh
+        if not Path(run[0]).is_file():
+            print(
+                "runblock-python: Executable {} does not exist or is not a file.".format(run[0]),
+                file=sys.stderr
+            )
+            sys.exit(100)
+    os.execvp(
+        file=run[0],
+        args=run
+    )
+  '';
+
+
+in
+{
+  inherit
+    nix-run
+    nix-run-bin
+    nix-eval
+    ;
+}
diff --git a/users/Profpatsch/nixpkgs-rewriter/MetaStdenvLib.hs b/users/Profpatsch/nixpkgs-rewriter/MetaStdenvLib.hs
deleted file mode 100644
index 3ed96a7b6e..0000000000
--- a/users/Profpatsch/nixpkgs-rewriter/MetaStdenvLib.hs
+++ /dev/null
@@ -1,80 +0,0 @@
-{-# LANGUAGE PartialTypeSignatures #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE NamedFieldPuns #-}
-import Nix.Parser
-import Nix.Expr.Types
-import Nix.Expr.Types.Annotated
-import System.Environment (getArgs)
-import System.Exit (die)
-import Data.Fix (Fix(..))
-import qualified Data.Text as Text
-import qualified Data.ByteString.Lazy.Char8 as BL
-import qualified Data.Aeson as A
-import qualified Data.Aeson.Encoding as A
-import Data.Function ((&))
-import qualified System.IO as IO
-import qualified Text.Megaparsec.Pos as MP
-
-main = do
-  (nixFile:_) <- getArgs
-  (parseNixFileLoc nixFile :: IO _) >>= \case
-    Failure err -> do
-      ePutStrLn $ show err
-      die "oh no"
-    Success expr -> do
-      case snd $ match expr of
-        NoArguments -> do
-          ePutStrLn $ "NoArguments in " <> nixFile
-          printPairs mempty
-        YesLib vars -> do
-          ePutStrLn $ "lib in " <> show vars <> " in " <> nixFile
-          printPairs mempty
-        NoLib vars srcSpan -> do
-          ePutStrLn $ nixFile <> " needs lib added"
-          printPairs
-            $ "fileName" A..= nixFile
-            <> "fromLine" A..= (srcSpan & spanBegin & sourceLine)
-            <> "fromColumn" A..= (srcSpan & spanBegin & sourceColumn)
-            <> "toLine" A..= (srcSpan & spanEnd & sourceLine)
-            <> "toColumn" A..= (srcSpan & spanEnd & sourceColumn)
-
-printPairs pairs = BL.putStrLn $ A.encodingToLazyByteString $ A.pairs pairs
-
-ePutStrLn = IO.hPutStrLn IO.stderr
-
-data Descend = YesDesc | NoDesc
-  deriving Show
-data Matched =  NoArguments | NoLib [VarName] SrcSpan | YesLib [VarName]
-  deriving Show
-
-match :: Fix (Compose (Ann SrcSpan) NExprF) -> (Descend, Matched)
-match = \case
-  (AnnE outerSpan (NAbs (ParamSet params _ _) (AnnE innerSpan _))) -> (NoDesc,
-    let vars = map fst params in
-    case (any (== "lib") vars) of
-      True -> YesLib vars
-      False ->
-          -- The span of the arglist is from the beginning of the match
-          -- to the beginning of the inner expression
-          let varSpan = SrcSpan
-                { spanBegin = outerSpan & spanBegin
-                -- -1 to prevent the spans from overlapping
-                , spanEnd = sourcePosMinus1 (innerSpan & spanBegin) }
-          in NoLib vars varSpan)
-  _ -> (NoDesc, NoArguments)
-
--- | Remove one from a source positon.
---
--- That means if the current position is at the very beginning of a line,
--- jump to the previous line.
-sourcePosMinus1 :: SourcePos -> SourcePos
-sourcePosMinus1 src@(SourcePos { sourceLine, sourceColumn }) =
-  let
-    col = MP.mkPos $ max (MP.unPos sourceColumn - 1) 1
-    line = MP.mkPos $ case MP.unPos sourceColumn of
-      1 -> max (MP.unPos sourceLine - 1) 1
-      _ -> MP.unPos sourceLine
-  in src
-    { sourceLine = line
-    , sourceColumn = col }
diff --git a/users/Profpatsch/nixpkgs-rewriter/default.nix b/users/Profpatsch/nixpkgs-rewriter/default.nix
deleted file mode 100644
index 787162d497..0000000000
--- a/users/Profpatsch/nixpkgs-rewriter/default.nix
+++ /dev/null
@@ -1,112 +0,0 @@
-{ depot, pkgs, ... }:
-let
-  inherit (depot.nix)
-    writeExecline
-    ;
-  inherit (depot.users.Profpatsch.lib)
-    debugExec
-    ;
-
-  bins = depot.nix.getBins pkgs.coreutils [ "head" "shuf" ]
-      // depot.nix.getBins pkgs.jq [ "jq" ]
-      // depot.nix.getBins pkgs.findutils [ "xargs" ]
-      // depot.nix.getBins pkgs.gnused [ "sed" ]
-      ;
-
-  export-json-object = pkgs.writers.writePython3 "export-json-object" {} ''
-    import json
-    import sys
-    import os
-
-    d = json.load(sys.stdin)
-
-    if d == {}:
-        sys.exit(0)
-
-    for k, v in d.items():
-        os.environ[k] = str(v)
-
-    os.execvp(sys.argv[1], sys.argv[1:])
-  '';
-
-  meta-stdenv-lib = pkgs.writers.writeHaskell "meta-stdenv-lib" {
-    libraries = [
-      pkgs.haskellPackages.hnix
-      pkgs.haskellPackages.aeson
-    ];
-  } ./MetaStdenvLib.hs;
-
-  replace-between-lines = writeExecline "replace-between-lines" { readNArgs = 1; } [
-    "importas" "-ui" "file" "fileName"
-    "importas" "-ui" "from" "fromLine"
-    "importas" "-ui" "to" "toLine"
-    "if" [ depot.tools.eprintf "%s-%s\n" "$from" "$to" ]
-    (debugExec "adding lib")
-    bins.sed
-      "-e" "\${from},\${to} \${1}"
-      "-i" "$file"
-  ];
-
-  add-lib-if-necessary = writeExecline "add-lib-if-necessary" { readNArgs = 1; } [
-    "pipeline" [ meta-stdenv-lib "$1" ]
-     export-json-object
-     # first replace any stdenv.lib mentions in the arg header
-     # if this is not done, the replace below kills these.
-     # Since we want it anyway ultimately, let’s do it here.
-     "if" [ replace-between-lines "s/stdenv\.lib/lib/" ]
-     # then add the lib argument
-     # (has to be before stdenv, otherwise default arguments might be in the way)
-     replace-between-lines "s/stdenv/lib, stdenv/"
-  ];
-
-  metaString = ''meta = with stdenv.lib; {'';
-
-  replace-stdenv-lib = pkgs.writers.writeBash "replace-stdenv-lib" ''
-    set -euo pipefail
-    sourceDir="$1"
-    for file in $(
-      ${pkgs.ripgrep}/bin/rg \
-        --files-with-matches \
-        --fixed-strings \
-        -e '${metaString}' \
-        "$sourceDir"
-    )
-    do
-      echo "replacing stdenv.lib meta in $file" >&2
-      ${bins.sed} -e '/${metaString}/ s/stdenv.lib/lib/' \
-          -i "$file"
-      ${add-lib-if-necessary} "$file"
-    done
-  '';
-
-  instantiate-nixpkgs-randomly = writeExecline "instantiate-nixpkgs-randomly" { readNArgs = 1; } [
-    "export" "NIXPKGS_ALLOW_BROKEN" "1"
-    "export" "NIXPKGS_ALLOW_UNFREE" "1"
-    "export" "NIXPKGS_ALLOW_INSECURE" "1"
-    "export" "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM" "1"
-    "pipeline" [
-      "nix"
-        "eval"
-        "--raw"
-        ''(
-          let pkgs = import ''${1} {};
-          in builtins.toJSON (builtins.attrNames pkgs)
-        )''
-    ]
-    "pipeline" [ bins.jq "-r" ".[]" ]
-    "pipeline" [ bins.shuf ]
-    "pipeline" [ bins.head "-n" "1000" ]
-    bins.xargs "-I" "{}" "-n1"
-    "if" [ depot.tools.eprintf "instantiating %s\n" "{}" ]
-    "nix-instantiate" "$1" "-A" "{}"
-  ];
-
-in depot.nix.readTree.drvTargets {
-  inherit
-   instantiate-nixpkgs-randomly
-  # requires hnix, which we don’t want in tvl for now
-  # uncomment manually if you want to use it.
-  #   meta-stdenv-lib
-  #   replace-stdenv-lib
-    ;
-}
diff --git a/users/Profpatsch/openlab-tools/Main.hs b/users/Profpatsch/openlab-tools/Main.hs
new file mode 100644
index 0000000000..d5f958a38a
--- /dev/null
+++ b/users/Profpatsch/openlab-tools/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import OpenlabTools qualified
+
+main :: IO ()
+main = OpenlabTools.main
diff --git a/users/Profpatsch/openlab-tools/default.nix b/users/Profpatsch/openlab-tools/default.nix
new file mode 100644
index 0000000000..82641989f7
--- /dev/null
+++ b/users/Profpatsch/openlab-tools/default.nix
@@ -0,0 +1,69 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  #   bins = depot.nix.getBins pkgs.sqlite ["sqlite3"];
+
+  openlab-tools = pkgs.haskellPackages.mkDerivation {
+    pname = "openlab-tools";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./openlab-tools.cabal
+      ./Main.hs
+      ./src/OpenlabTools.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-prelude
+      depot.users.Profpatsch.my-webstuff
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-json
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.pa-field-parser
+      pkgs.haskellPackages.pa-run-command
+      pkgs.haskellPackages.aeson-better-errors
+      pkgs.haskellPackages.blaze-html
+      pkgs.haskellPackages.deepseq
+      pkgs.haskellPackages.case-insensitive
+      pkgs.haskellPackages.hs-opentelemetry-sdk
+      pkgs.haskellPackages.http-conduit
+      pkgs.haskellPackages.http-types
+      pkgs.haskellPackages.ihp-hsx
+      pkgs.haskellPackages.monad-logger
+      pkgs.haskellPackages.selective
+      pkgs.haskellPackages.unliftio
+      pkgs.haskellPackages.wai-extra
+      pkgs.haskellPackages.warp
+      pkgs.haskellPackages.tagsoup
+      pkgs.haskellPackages.time
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+  bins = depot.nix.getBins openlab-tools [ "openlab-tools" ];
+
+in
+
+depot.nix.writeExecline "openlab-tools-wrapped" { } [
+  "importas"
+  "-i"
+  "PATH"
+  "PATH"
+  "export"
+  "PATH"
+  "${pkgs.postgresql}/bin:$${PATH}"
+  "export"
+  "OPENLAB_TOOLS_TOOLS"
+  (pkgs.linkFarm "openlab-tools-tools" [
+    {
+      name = "pg_format";
+      path = "${pkgs.pgformatter}/bin/pg_format";
+    }
+  ])
+  bins.openlab-tools
+]
+
diff --git a/users/Profpatsch/openlab-tools/openlab-tools.cabal b/users/Profpatsch/openlab-tools/openlab-tools.cabal
new file mode 100644
index 0000000000..b7d217e051
--- /dev/null
+++ b/users/Profpatsch/openlab-tools/openlab-tools.cabal
@@ -0,0 +1,111 @@
+cabal-version:      3.0
+name:               openlab-tools
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+
+    hs-source-dirs: src
+
+    exposed-modules:
+       OpenlabTools
+
+    build-depends:
+        base >=4.15 && <5,
+        text,
+        my-prelude,
+        my-webstuff,
+        pa-prelude,
+        pa-error-tree,
+        pa-label,
+        pa-json,
+        pa-field-parser,
+        pa-run-command,
+        aeson-better-errors,
+        aeson,
+        blaze-html,
+        bytestring,
+        containers,
+        deepseq,
+        unordered-containers,
+        exceptions,
+        filepath,
+        hs-opentelemetry-sdk,
+        hs-opentelemetry-api,
+        http-conduit,
+        http-types,
+        ihp-hsx,
+        monad-logger,
+        mtl,
+        network-uri,
+        scientific,
+        selective,
+        unliftio,
+        wai-extra,
+        wai,
+        warp,
+        tagsoup,
+        time,
+        stm,
+        case-insensitive
+
+executable openlab-tools
+    import: common-options
+
+    main-is: Main.hs
+
+    ghc-options:
+      -threaded
+
+    build-depends:
+        base >=4.15 && <5,
+        openlab-tools
diff --git a/users/Profpatsch/openlab-tools/src/OpenlabTools.hs b/users/Profpatsch/openlab-tools/src/OpenlabTools.hs
new file mode 100644
index 0000000000..9fe51aba18
--- /dev/null
+++ b/users/Profpatsch/openlab-tools/src/OpenlabTools.hs
@@ -0,0 +1,551 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module OpenlabTools where
+
+import Control.Concurrent.STM hiding (atomically, readTVarIO)
+import Control.DeepSeq (NFData, deepseq)
+import Control.Monad.Logger qualified as Logger
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Aeson.BetterErrors qualified as Json
+import Data.CaseInsensitive qualified as CaseInsensitive
+import Data.Error.Tree
+import Data.HashMap.Strict qualified as HashMap
+import Data.List qualified as List
+import Data.Maybe (listToMaybe)
+import Data.Text qualified as Text
+import Data.Time (NominalDiffTime, UTCTime (utctDayTime), diffUTCTime, getCurrentTime)
+import Data.Time qualified as Time
+import Data.Time.Clock (addUTCTime)
+import Data.Time.Format qualified as Time.Format
+import Debug.Trace
+import FieldParser (FieldParser' (..))
+import FieldParser qualified as Field
+import GHC.Records (HasField (..))
+import GHC.Stack qualified
+import IHP.HSX.QQ (hsx)
+import Json qualified
+import Label
+import Network.HTTP.Client.Conduit qualified as Http
+import Network.HTTP.Simple qualified as Http
+import Network.HTTP.Types
+import Network.HTTP.Types qualified as Http
+import Network.Wai qualified as Wai
+import Network.Wai.Handler.Warp qualified as Warp
+import Network.Wai.Parse qualified as Wai
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import OpenTelemetry.Trace.Core qualified as Otel hiding (inSpan, inSpan')
+import OpenTelemetry.Trace.Monad qualified as Otel
+import Parse (Parse)
+import Parse qualified
+import PossehlAnalyticsPrelude
+import Pretty
+import System.Environment qualified as Env
+import System.IO qualified as IO
+import Text.Blaze.Html.Renderer.Pretty qualified as Html.Pretty
+import Text.Blaze.Html.Renderer.Utf8 qualified as Html
+import Text.Blaze.Html5 qualified as Html
+import Text.HTML.TagSoup qualified as Soup
+import UnliftIO hiding (Handler, newTVarIO)
+import Prelude hiding (span, until)
+
+mapallSpaceOla :: Text
+mapallSpaceOla = "https://mapall.space/heatmap/show.php?id=OpenLab+Augsburg"
+
+mainPage :: Html.Html
+mainPage =
+  Html.docTypeHtml
+    [hsx|
+          <head>
+            <title>Openlab Augsburg Tools</title>
+            <meta charset="utf-8">
+            <meta name="viewport" content="width=device-width, initial-scale=1">
+          </head>
+
+          <body>
+            <p>Welcome to the OpenLab Augsburg tools thingy. The idea is to provide some services that can be embedded into our other pages.</p>
+
+            <h2>What’s there</h2>
+            <ul>
+              <li>
+                A <a href="snips/table-opening-hours-last-week">table displaying the opening hours last week</a>, courtesy of <a href={mapallSpaceOla}>mapall.space</a>.
+              </li>
+            </ul>
+
+
+            <h2>Show me the code/how to contribute</h2>
+
+            <p>The source code can be found <a href="https://code.tvl.fyi/tree/users/Profpatsch/openlab-tools">in my user dir in the tvl repo</a>.</p>
+
+            <p>To build the server, clone the repository from <a href="https://code.tvl.fyi/depot.git">https://code.tvl.fyi/depot.git</a>.
+            Then <code>cd</code> into <code>users/Profpatsch</code>, run <code>nix-shell</code>.
+            </p>
+
+            <p>You can now run the server with <code>cabal repl openlab-tools/`</code> by executing the <code>main</code> function inside the GHC repl. It starts on port <code>9099</code>.
+            <br>
+            To try out changes to the code, stop the server with <kbd><kbd>Ctrl</kbd>+<kbd>z</kbd></kbd> and type <code>:reload</code>, then <code>main</code> again.
+            <br>
+            Finally, from within <code>users/Profpatsch</code> you can start a working development environment by installing <var>vscode</var> or <var>vscodium</var> and the <var>Haskell</var> extension. Then run <code>code .</code> from within the directory.
+            </p>
+
+            <p>Once you have a patch, <a href="https://matrix.to/#/@profpatsch:augsburg.one">contact me on Matrix</a> or DM me at <code>irc/libera</code>, nick <code>Profpatsch</code>.
+            </p>
+          </body>
+        |]
+
+debug :: Bool
+debug = False
+
+runApp :: IO ()
+runApp = withTracer $ \tracer -> do
+  let renderHtml =
+        if debug
+          then Html.Pretty.renderHtml >>> stringToText >>> textToBytesUtf8 >>> toLazyBytes
+          else Html.renderHtml
+
+  let runApplication ::
+        (MonadUnliftIO m, MonadLogger m) =>
+        ( Wai.Request ->
+          (Wai.Response -> m Wai.ResponseReceived) ->
+          m Wai.ResponseReceived
+        ) ->
+        m ()
+      runApplication app = do
+        withRunInIO $ \runInIO -> Warp.run 9099 $ \req respond -> do
+          let catchAppException act =
+                try act >>= \case
+                  Right a -> pure a
+                  Left (AppException err) -> do
+                    runInIO (logError err)
+                    respond (Wai.responseLBS Http.status500 [] "")
+          liftIO $ catchAppException (runInIO $ app req (\resp -> liftIO $ respond resp))
+
+  let appT :: AppT IO () = do
+        let h extra res = Wai.responseLBS Http.ok200 (("Content-Type", "text/html") : extra) res
+        runHandlers
+          runApplication
+          [ Handler
+              { path = "",
+                body =
+                  Body
+                    (pure ())
+                    (\((), _) -> pure $ h [] (renderHtml mainPage))
+              },
+            Handler
+              { path = "snips/table-opening-hours-last-week",
+                body =
+                  Body
+                    ((label @"ifModifiedSince" <$> parseIfModifiedSince))
+                    ( \(req', cache) -> do
+                        now <- liftIO getCurrentTime <&> mkSecondTime
+                        new <- updateCacheIfNewer now cache heatmap
+                        let cacheToHeaders =
+                              [ ("Last-Modified", new.lastModified & formatHeaderTime),
+                                ("Expires", new.until & formatHeaderTime),
+                                ( "Cache-Control",
+                                  let maxAge = new.until `diffSecondTime` now
+                                   in [fmt|max-age={maxAge & floor @NominalDiffTime @Int  & show}, immutable|]
+                                )
+                              ]
+                        if
+                            -- If the last cache update is newer or equal to the requested version, we can tell the browser it’s fine
+                            | Just modifiedSince <- req'.ifModifiedSince,
+                              modifiedSince >= new.lastModified ->
+                                pure $ Wai.responseLBS Http.status304 cacheToHeaders ""
+                            | otherwise ->
+                                pure $ h cacheToHeaders (new.result & toLazyBytes)
+                    )
+              }
+          ]
+
+  runReaderT (appT :: AppT IO ()).unAppT Context {..}
+  where
+    -- "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified#syntax"
+    headerFormat = "%a, %d %b %0Y %T GMT"
+    formatHeaderTime (SecondTime t) =
+      t
+        & Time.Format.formatTime
+          @UTCTime
+          Time.Format.defaultTimeLocale
+          headerFormat
+        & stringToText
+        & textToBytesUtf8
+    parseHeaderTime =
+      Field.utf8
+        >>> ( FieldParser $ \t ->
+                t
+                  & textToString
+                  & Time.Format.parseTimeM
+                    @Maybe
+                    @UTCTime
+                    {-no leading whitespace -} False
+                    Time.Format.defaultTimeLocale
+                    headerFormat
+                  & annotate [fmt|Cannot parse header timestamp "{t}"|]
+            )
+    parseIfModifiedSince :: Parse Wai.Request (Maybe SecondTime)
+    parseIfModifiedSince =
+      lmap
+        ( (.requestHeaders)
+            >>> findMaybe
+              ( \(h, v) ->
+                  if "If-Modified-Since" == CaseInsensitive.mk h then Just v else Nothing
+              )
+        )
+        (Parse.maybe $ Parse.fieldParser parseHeaderTime)
+        & rmap (fmap mkSecondTime)
+
+parseRequest :: (MonadThrow f, MonadIO f) => Otel.Span -> Parse from a -> from -> f a
+parseRequest span parser req =
+  Parse.runParse "Unable to parse the HTTP request" parser req
+    & assertM span id
+
+heatmap :: AppT IO ByteString
+heatmap = do
+  Http.httpBS [fmt|GET {mapallSpaceOla}|]
+    <&> (.responseBody)
+    <&> Soup.parseTags
+    <&> Soup.canonicalizeTags
+    <&> findHeatmap
+    <&> fromMaybe (htmlToTags [hsx|<p>Uh oh! could not fetch the table from <a href={mapallSpaceOla}>{mapallSpaceOla}</a></p>|])
+    <&> Soup.renderTags
+  where
+    firstSection f t = t & Soup.sections f & listToMaybe
+    match :: Soup.Tag ByteString -> Soup.Tag ByteString -> Bool
+    match x (t :: Soup.Tag ByteString) = (Soup.~==) @ByteString t x
+    findHeatmap t =
+      t
+        & firstSection (match (Soup.TagOpen ("") [("class", "heatmap")]))
+        >>= firstSection (match (Soup.TagOpen "table" []))
+        <&> getTable
+        <&> (<> htmlToTags [hsx|<figcaption>source: <a href={mapallSpaceOla} target="_blank">mapall.space</a></figcaption>|])
+        <&> wrapTagStream (T2 (label @"el" "figure") (label @"attrs" []))
+
+    -- get the table from opening tag to closing tag (allowing nested tables)
+    getTable = go 0
+      where
+        go _ [] = []
+        go d (el : els)
+          | match (Soup.TagOpen "table" []) el = el : go (d + 1) els
+          | match (Soup.TagClose "table") el = if d <= 1 then [el] else el : go (traceShowId $ d - 1) els
+          | otherwise = el : go d els
+
+    htmlToTags :: Html.Html -> [Soup.Tag ByteString]
+    htmlToTags h = h & Html.renderHtml & toStrictBytes & Soup.parseTags
+
+    -- TODO: this is dog-slow because of the whole list recreation!
+    wrapTagStream ::
+      T2 "el" ByteString "attrs" [Soup.Attribute ByteString] ->
+      [Soup.Tag ByteString] ->
+      [Soup.Tag ByteString]
+    wrapTagStream tag inner = (Soup.TagOpen (tag.el) tag.attrs : inner) <> [Soup.TagClose tag.el]
+
+main :: IO ()
+main =
+  runApp
+
+-- ( do
+--     -- todo: trace that to the init functions as well
+--     Otel.inSpan "whatcd-resolver main function" Otel.defaultSpanArguments $ do
+--       _ <- runTransaction migrate
+--       htmlUi
+-- )
+
+data Handler m = Handler
+  { path :: Text,
+    body :: Body m
+  }
+
+data Body m
+  = forall a.
+    Body
+      (Parse Wai.Request a)
+      ((a, TVar (Cache ByteString)) -> m Wai.Response)
+
+runHandlers ::
+  (Otel.MonadTracer m, MonadUnliftIO m, MonadThrow m) =>
+  -- ( (Wai.Request -> (Wai.Response -> m Wai.ResponseReceived) -> m Wai.ResponseReceived) ->
+  --   m ()
+  -- ) ->
+  ( (Wai.Request -> (Wai.Response -> m a) -> m a) ->
+    m ()
+  ) ->
+  [Handler m] ->
+  m ()
+runHandlers runApplication handlers = do
+  withCaches ::
+    [ T2
+        "handler"
+        (Handler m)
+        "cache"
+        (TVar (Cache ByteString))
+    ] <-
+    handlers
+      & traverse
+        ( \h -> do
+            cache <- liftIO $ newCache h.path "nothing yet"
+            pure $ T2 (label @"handler" h) (label @"cache" cache)
+        )
+  runApplication $ \req respond -> do
+    let mHandler =
+          withCaches
+            & List.find
+              ( \h ->
+                  (h.handler.path)
+                    == (req & Wai.pathInfo & Text.intercalate "/")
+              )
+    case mHandler of
+      Nothing -> respond $ Wai.responseLBS Http.status404 [] "nothing here (yet)"
+      Just handler -> do
+        inSpan' "TODO" $ \span -> do
+          case handler.handler.body of
+            Body parse runHandler -> do
+              req' <- req & parseRequest span parse
+              resp <- runHandler (req', handler.cache)
+              respond resp
+
+inSpan :: (MonadUnliftIO m, Otel.MonadTracer m) => Text -> m a -> m a
+inSpan name = Otel.inSpan name Otel.defaultSpanArguments
+
+inSpan' :: (MonadUnliftIO m, Otel.MonadTracer m) => Text -> (Otel.Span -> m a) -> m a
+-- inSpan' name =  Otel.inSpan' name Otel.defaultSpanArguments
+inSpan' _name act = act (error "todo telemetry disabled")
+
+zipT2 ::
+  forall l1 l2 t1 t2.
+  ( HasField l1 (T2 l1 [t1] l2 [t2]) [t1],
+    HasField l2 (T2 l1 [t1] l2 [t2]) [t2]
+  ) =>
+  T2 l1 [t1] l2 [t2] ->
+  [T2 l1 t1 l2 t2]
+zipT2 xs =
+  zipWith
+    (\t1 t2 -> T2 (label @l1 t1) (label @l2 t2))
+    (getField @l1 xs)
+    (getField @l2 xs)
+
+unzipT2 :: forall l1 t1 l2 t2. [T2 l1 t1 l2 t2] -> T2 l1 [t1] l2 [t2]
+unzipT2 xs = xs <&> toTup & unzip & fromTup
+  where
+    toTup :: forall a b. T2 a t1 b t2 -> (t1, t2)
+    toTup (T2 a b) = (getField @a a, getField @b b)
+    fromTup :: (a, b) -> T2 l1 a l2 b
+    fromTup (t1, t2) = T2 (label @l1 t1) (label @l2 t2)
+
+unzipT3 :: forall l1 t1 l2 t2 l3 t3. [T3 l1 t1 l2 t2 l3 t3] -> T3 l1 [t1] l2 [t2] l3 [t3]
+unzipT3 xs = xs <&> toTup & unzip3 & fromTup
+  where
+    toTup :: forall a b c. T3 a t1 b t2 c t3 -> (t1, t2, t3)
+    toTup (T3 a b c) = (getField @a a, getField @b b, getField @c c)
+    fromTup :: (a, b, c) -> T3 l1 a l2 b l3 c
+    fromTup (t1, t2, t3) = T3 (label @l1 t1) (label @l2 t2) (label @l3 t3)
+
+newtype Optional a = OptionalInternal (Maybe a)
+
+mkOptional :: a -> Optional a
+mkOptional defaultValue = OptionalInternal $ Just defaultValue
+
+defaults :: Optional a
+defaults = OptionalInternal Nothing
+
+instance HasField "withDefault" (Optional a) (a -> a) where
+  getField (OptionalInternal m) defaultValue = case m of
+    Nothing -> defaultValue
+    Just a -> a
+
+httpJson ::
+  ( MonadIO m,
+    MonadThrow m
+  ) =>
+  (Optional (Label "contentType" ByteString)) ->
+  Otel.Span ->
+  Json.Parse ErrorTree b ->
+  Http.Request ->
+  m b
+httpJson opts span parser req = do
+  let opts' = opts.withDefault (label @"contentType" "application/json")
+  Http.httpBS req
+    >>= assertM
+      span
+      ( \resp -> do
+          let statusCode = resp & Http.responseStatus & (.statusCode)
+              contentType =
+                resp
+                  & Http.responseHeaders
+                  & List.lookup "content-type"
+                  <&> Wai.parseContentType
+                  <&> (\(ct, _mimeAttributes) -> ct)
+          if
+              | statusCode == 200,
+                Just ct <- contentType,
+                ct == opts'.contentType ->
+                  Right $ (resp & Http.responseBody)
+              | statusCode == 200,
+                Just otherType <- contentType ->
+                  Left [fmt|Server returned a non-json body, with content-type "{otherType}"|]
+              | statusCode == 200,
+                Nothing <- contentType ->
+                  Left [fmt|Server returned a body with unspecified content type|]
+              | code <- statusCode -> Left [fmt|Server returned an non-200 error code, code {code}: {resp & showPretty}|]
+      )
+    >>= assertM
+      span
+      ( \body ->
+          Json.parseStrict parser body
+            & first (Json.parseErrorTree "could not parse redacted response")
+      )
+
+assertM :: (MonadThrow f, MonadIO f) => Otel.Span -> (t -> Either ErrorTree a) -> t -> f a
+assertM span f v = case f v of
+  Right a -> pure a
+  Left err -> appThrowTree span err
+
+-- | UTC time that is only specific to the second
+newtype SecondTime = SecondTime {unSecondTime :: UTCTime}
+  deriving newtype (Show, Eq, Ord)
+
+mkSecondTime :: UTCTime -> SecondTime
+mkSecondTime utcTime = SecondTime utcTime {utctDayTime = Time.secondsToDiffTime $ floor utcTime.utctDayTime}
+
+diffSecondTime :: SecondTime -> SecondTime -> NominalDiffTime
+diffSecondTime (SecondTime a) (SecondTime b) = diffUTCTime a b
+
+data Cache a = Cache
+  { name :: !Text,
+    until :: !SecondTime,
+    lastModified :: !SecondTime,
+    result :: !a
+  }
+  deriving (Show)
+
+newCache :: Text -> a -> IO (TVar (Cache a))
+newCache name result = do
+  let until = mkSecondTime $ Time.UTCTime {utctDay = Time.ModifiedJulianDay 1, utctDayTime = 1}
+  let lastModified = until
+  newTVarIO $ Cache {..}
+
+updateCache :: (NFData a, Eq a) => SecondTime -> TVar (Cache a) -> a -> STM (Cache a)
+updateCache now cache result' = do
+  -- make sure we don’t hold onto the world by deepseq-ing and evaluating to WHNF
+  let !result = deepseq result' result'
+  let until = mkSecondTime $ (5 * 60) `addUTCTime` now.unSecondTime
+  !toWrite <- do
+    old <- readTVar cache
+    let name = old.name
+    -- only update the lastModified time iff the content changed (this is helpful for HTTP caching with If-Modified-Since)
+    if old.result == result
+      then do
+        let lastModified = old.lastModified
+        pure $ Cache {..}
+      else do
+        let lastModified = now
+        pure $ Cache {..}
+  _ <- writeTVar cache $! toWrite
+  pure toWrite
+
+-- | Run the given action iff the cache is stale, otherwise just return the item from the cache.
+updateCacheIfNewer :: (MonadUnliftIO m, NFData b, Eq b) => SecondTime -> TVar (Cache b) -> m b -> m (Cache b)
+updateCacheIfNewer now cache act = withRunInIO $ \runInIO -> do
+  old <- readTVarIO cache
+  if old.until < now
+    then do
+      res <- runInIO act
+      atomically $ updateCache now cache res
+    else pure old
+
+-- pgFormat <- readTools (label @"toolsEnvVar" "OPENLAB_TOOLS_TOOLS") (readTool "pg_format")
+-- let config = label @"logDatabaseQueries" LogDatabaseQueries
+-- pgConnPool <-
+--   Pool.newPool $
+--     Pool.defaultPoolConfig
+--       {- resource init action -} (Postgres.connectPostgreSQL (db & TmpPg.toConnectionString))
+--       {- resource destruction -} Postgres.close
+--       {- unusedResourceOpenTime -} 10
+--       {- max resources across all stripes -} 20
+-- transmissionSessionId <- newEmptyMVar
+-- let newAppT = do
+--       logInfo [fmt|Running with config: {showPretty config}|]
+--       logInfo [fmt|Connected to database at {db & TmpPg.toDataDirectory} on socket {db & TmpPg.toConnectionString}|]
+--       appT
+-- runReaderT newAppT.unAppT Context {..}
+
+withTracer :: (Otel.Tracer -> IO c) -> IO c
+withTracer f = do
+  setDefaultEnv "OTEL_SERVICE_NAME" "whatcd-resolver"
+  bracket
+    -- Install the SDK, pulling configuration from the environment
+    Otel.initializeGlobalTracerProvider
+    -- Ensure that any spans that haven't been exported yet are flushed
+    Otel.shutdownTracerProvider
+    -- Get a tracer so you can create spans
+    (\tracerProvider -> f $ Otel.makeTracer tracerProvider "whatcd-resolver" Otel.tracerOptions)
+
+setDefaultEnv :: String -> String -> IO ()
+setDefaultEnv envName defaultValue = do
+  Env.lookupEnv envName >>= \case
+    Just _env -> pure ()
+    Nothing -> Env.setEnv envName defaultValue
+
+data Context = Context
+  { tracer :: Otel.Tracer
+  }
+
+newtype AppT m a = AppT {unAppT :: ReaderT Context m a}
+  deriving newtype (Functor, Applicative, Monad, MonadIO, MonadUnliftIO, MonadThrow)
+
+data AppException = AppException Text
+  deriving stock (Show)
+  deriving anyclass (Exception)
+
+-- | A specialized variant of @addEvent@ that records attributes conforming to
+-- the OpenTelemetry specification's
+-- <https://github.com/open-telemetry/opentelemetry-specification/blob/49c2f56f3c0468ceb2b69518bcadadd96e0a5a8b/specification/trace/semantic_conventions/exceptions.md semantic conventions>
+--
+-- @since 0.0.1.0
+recordException ::
+  ( MonadIO m,
+    HasField "message" r Text,
+    HasField "type_" r Text
+  ) =>
+  Otel.Span ->
+  r ->
+  m ()
+recordException span dat = liftIO $ do
+  callStack <- GHC.Stack.whoCreated dat.message
+  newEventTimestamp <- Just <$> Otel.getTimestamp
+  Otel.addEvent span $
+    Otel.NewEvent
+      { newEventName = "exception",
+        newEventAttributes =
+          HashMap.fromList
+            [ ("exception.type", Otel.toAttribute @Text dat.type_),
+              ("exception.message", Otel.toAttribute @Text dat.message),
+              ("exception.stacktrace", Otel.toAttribute @Text $ Text.unlines $ map stringToText callStack)
+            ],
+        ..
+      }
+
+appThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> ErrorTree -> m a
+appThrowTree span exc = do
+  let msg = prettyErrorTree exc
+  -- recordException
+  --   span
+  --   ( T2
+  --       (label @"type_" "AppException")
+  --       (label @"message" msg)
+  --   )
+  throwM $ AppException msg
+
+orAppThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> Either ErrorTree a -> m a
+orAppThrowTree span = \case
+  Left err -> appThrowTree span err
+  Right a -> pure a
+
+instance (MonadIO m) => MonadLogger (AppT m) where
+  monadLoggerLog loc src lvl msg = liftIO $ Logger.defaultOutput IO.stderr loc src lvl (Logger.toLogStr msg)
+
+instance (Monad m) => Otel.MonadTracer (AppT m) where
+  getTracer = AppT $ asks (.tracer)
diff --git a/users/Profpatsch/read-http.nix b/users/Profpatsch/read-http.nix
index 854a11b7d0..d9ad6fc30d 100644
--- a/users/Profpatsch/read-http.nix
+++ b/users/Profpatsch/read-http.nix
@@ -2,15 +2,18 @@
 
 let
 
-  read-http = depot.nix.writers.rustSimple {
-    name = "read-http";
-    dependencies = [
-      depot.third_party.rust-crates.ascii
-      depot.third_party.rust-crates.httparse
-      depot.users.Profpatsch.netencode.netencode-rs
-      depot.users.Profpatsch.arglib.netencode.rust
-      depot.users.Profpatsch.execline.exec-helpers
-    ];
-  } (builtins.readFile ./read-http.rs);
+  read-http = depot.nix.writers.rustSimple
+    {
+      name = "read-http";
+      dependencies = [
+        depot.third_party.rust-crates.ascii
+        depot.third_party.rust-crates.httparse
+        depot.users.Profpatsch.netencode.netencode-rs
+        depot.users.Profpatsch.arglib.netencode.rust
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    }
+    (builtins.readFile ./read-http.rs);
 
-in read-http
+in
+read-http
diff --git a/users/Profpatsch/read-http.rs b/users/Profpatsch/read-http.rs
index 50ff663b99..2b24e6beb1 100644
--- a/users/Profpatsch/read-http.rs
+++ b/users/Profpatsch/read-http.rs
@@ -1,37 +1,35 @@
-extern crate httparse;
-extern crate netencode;
 extern crate arglib_netencode;
 extern crate ascii;
 extern crate exec_helpers;
+extern crate httparse;
+extern crate netencode;
 
-use std::os::unix::io::FromRawFd;
-use std::io::Read;
-use std::io::Write;
+use exec_helpers::{die_expected_error, die_temporary, die_user_error};
 use std::collections::HashMap;
-use exec_helpers::{die_user_error, die_expected_error, die_temporary};
+use std::io::{Read, Write};
+use std::os::unix::io::FromRawFd;
 
-use netencode::{U, T, dec};
 use netencode::dec::Decoder;
+use netencode::{dec, T, U};
 
 enum What {
     Request,
-    Response
+    Response,
 }
 
 // reads a http request (stdin), and writes all headers to stdout, as netencoded record.
 // The keys are text, but can be lists of text iff headers appear multiple times, so beware.
 fn main() -> std::io::Result<()> {
-
     exec_helpers::no_args("read-http");
 
     let args = dec::RecordDot {
         field: "what",
         inner: dec::OneOf {
             list: vec!["request", "response"],
-            inner: dec::Text
-        }
+            inner: dec::Text,
+        },
     };
-    let what : What = match args.dec(arglib_netencode::arglib_netencode("read-http", None).to_u()) {
+    let what: What = match args.dec(arglib_netencode::arglib_netencode("read-http", None).to_u()) {
         Ok("request") => What::Request,
         Ok("response") => What::Response,
         Ok(v) => panic!("shouldn’t happen!, value was: {}", v),
@@ -39,7 +37,8 @@ fn main() -> std::io::Result<()> {
     };
 
     fn read_stdin_to_complete<F>(mut parse: F) -> ()
-        where F: FnMut(&[u8]) -> httparse::Result<usize>
+    where
+        F: FnMut(&[u8]) -> httparse::Result<usize>,
     {
         let mut res = httparse::Status::Partial;
         loop {
@@ -48,16 +47,22 @@ fn main() -> std::io::Result<()> {
             }
             let mut buf = [0; 2048];
             match std::io::stdin().read(&mut buf[..]) {
-                Ok(size) => if size == 0 {
-                    break;
-                },
-                Err(err) => die_temporary("read-http", format!("could not read from stdin, {:?}", err))
+                Ok(size) => {
+                    if size == 0 {
+                        break;
+                    }
+                }
+                Err(err) => {
+                    die_temporary("read-http", format!("could not read from stdin, {:?}", err))
+                }
             }
             match parse(&buf) {
                 Ok(status) => {
                     res = status;
                 }
-                Err(err) => die_temporary("read-http", format!("httparse parsing failed: {:#?}", err))
+                Err(err) => {
+                    die_temporary("read-http", format!("httparse parsing failed: {:#?}", err))
+                }
             }
         }
     }
@@ -66,7 +71,10 @@ fn main() -> std::io::Result<()> {
         let mut res = HashMap::new();
         for httparse::Header { name, value } in headers {
             let val = ascii::AsciiStr::from_ascii(*value)
-                .expect(&format!("read-http: we require header values to be ASCII, but the header {} was {:?}", name, value))
+                .expect(&format!(
+                    "read-http: we require header values to be ASCII, but the header {} was {:?}",
+                    name, value
+                ))
                 .as_str();
             // lowercase the header names, since the standard doesn’t care
             // and we want unique strings to match against
@@ -77,13 +85,13 @@ fn main() -> std::io::Result<()> {
                     let name_lower = name.to_lowercase();
                     let _ = res.insert(name_lower, U::List(vec![U::Text(t), U::Text(val)]));
                     ()
-                },
+                }
                 Some(U::List(mut l)) => {
                     let name_lower = name.to_lowercase();
                     l.push(U::Text(val));
                     let _ = res.insert(name_lower, U::List(l));
                     ()
-                },
+                }
                 Some(o) => panic!("read-http: header not text nor list: {:?}", o),
             }
         }
@@ -98,12 +106,14 @@ fn main() -> std::io::Result<()> {
             match chonker.next() {
                 Some(Ok(chunk)) => {
                     buf.extend_from_slice(&chunk);
-                    if chunk.windows(4).any(|c| c == b"\r\n\r\n" ) {
+                    if chunk.windows(4).any(|c| c == b"\r\n\r\n") {
                         return Some(());
                     }
-                },
-                Some(Err(err)) => die_temporary("read-http", format!("error reading from stdin: {:?}", err)),
-                None => return None
+                }
+                Some(Err(err)) => {
+                    die_temporary("read-http", format!("error reading from stdin: {:?}", err))
+                }
+                None => return None,
             }
         }
     }
@@ -118,68 +128,99 @@ fn main() -> std::io::Result<()> {
             let mut buf: Vec<u8> = vec![];
             match read_till_end_of_header(&mut buf, stdin.lock()) {
                 Some(()) => match req.parse(&buf) {
-                    Ok(httparse::Status::Complete(_body_start)) => {},
-                    Ok(httparse::Status::Partial) => die_expected_error("read-http", "httparse should have gotten a full header"),
-                    Err(err) => die_expected_error("read-http", format!("httparse response parsing failed: {:#?}", err))
+                    Ok(httparse::Status::Complete(_body_start)) => {}
+                    Ok(httparse::Status::Partial) => {
+                        die_expected_error("read-http", "httparse should have gotten a full header")
+                    }
+                    Err(err) => die_expected_error(
+                        "read-http",
+                        format!("httparse response parsing failed: {:#?}", err),
+                    ),
                 },
-                None => die_expected_error("read-http", format!("httparse end of stdin reached before able to parse request headers"))
+                None => die_expected_error(
+                    "read-http",
+                    format!("httparse end of stdin reached before able to parse request headers"),
+                ),
             }
             let method = req.method.expect("method must be filled on complete parse");
             let path = req.path.expect("path must be filled on complete parse");
             write_dict_req(method, path, &normalize_headers(req.headers))
-        },
+        }
         Response => {
             let mut resp = httparse::Response::new(&mut headers);
             let mut buf: Vec<u8> = vec![];
             match read_till_end_of_header(&mut buf, stdin.lock()) {
                 Some(()) => match resp.parse(&buf) {
-                    Ok(httparse::Status::Complete(_body_start)) => {},
-                    Ok(httparse::Status::Partial) => die_expected_error("read-http", "httparse should have gotten a full header"),
-                    Err(err) => die_expected_error("read-http", format!("httparse response parsing failed: {:#?}", err))
+                    Ok(httparse::Status::Complete(_body_start)) => {}
+                    Ok(httparse::Status::Partial) => {
+                        die_expected_error("read-http", "httparse should have gotten a full header")
+                    }
+                    Err(err) => die_expected_error(
+                        "read-http",
+                        format!("httparse response parsing failed: {:#?}", err),
+                    ),
                 },
-                None => die_expected_error("read-http", format!("httparse end of stdin reached before able to parse response headers"))
+                None => die_expected_error(
+                    "read-http",
+                    format!("httparse end of stdin reached before able to parse response headers"),
+                ),
             }
             let code = resp.code.expect("code must be filled on complete parse");
-            let reason = resp.reason.expect("reason must be filled on complete parse");
+            let reason = resp
+                .reason
+                .expect("reason must be filled on complete parse");
             write_dict_resp(code, reason, &normalize_headers(resp.headers))
         }
     }
 }
 
-fn write_dict_req<'a, 'buf>(method: &'buf str, path: &'buf str, headers: &'a HashMap<String, U<'a>>) -> std::io::Result<()> {
-    let mut http = vec![
-        ("method", U::Text(method)),
-        ("path", U::Text(path)),
-    ].into_iter().collect();
+fn write_dict_req<'a, 'buf>(
+    method: &'buf str,
+    path: &'buf str,
+    headers: &'a HashMap<String, U<'a>>,
+) -> std::io::Result<()> {
+    let mut http = vec![("method", U::Text(method)), ("path", U::Text(path))]
+        .into_iter()
+        .collect();
     write_dict(http, headers)
 }
 
-fn write_dict_resp<'a, 'buf>(code: u16, reason: &'buf str, headers: &'a HashMap<String, U<'a>>) -> std::io::Result<()> {
+fn write_dict_resp<'a, 'buf>(
+    code: u16,
+    reason: &'buf str,
+    headers: &'a HashMap<String, U<'a>>,
+) -> std::io::Result<()> {
     let mut http = vec![
         ("status", U::N6(code as u64)),
         ("status-text", U::Text(reason)),
-    ].into_iter().collect();
+    ]
+    .into_iter()
+    .collect();
     write_dict(http, headers)
 }
 
-
-fn write_dict<'buf, 'a>(mut http: HashMap<&str, U<'a>>, headers: &'a HashMap<String, U<'a>>) -> std::io::Result<()> {
-    match http.insert("headers", U::Record(
-        headers.iter().map(|(k,v)| (k.as_str(), v.clone())).collect()
-    )) {
+fn write_dict<'buf, 'a>(
+    mut http: HashMap<&str, U<'a>>,
+    headers: &'a HashMap<String, U<'a>>,
+) -> std::io::Result<()> {
+    match http.insert(
+        "headers",
+        U::Record(
+            headers
+                .iter()
+                .map(|(k, v)| (k.as_str(), v.clone()))
+                .collect(),
+        ),
+    ) {
         None => (),
         Some(_) => panic!("read-http: headers already in dict"),
     };
-    netencode::encode(
-        &mut std::io::stdout(),
-        &U::Record(http)
-    )?;
+    netencode::encode(&mut std::io::stdout(), &U::Record(http))?;
     Ok(())
 }
 
-
 // iter helper
-
+// TODO: put into its own module
 struct Chunkyboi<T> {
     inner: T,
     buf: Vec<u8>,
@@ -188,10 +229,7 @@ struct Chunkyboi<T> {
 impl<R: Read> Chunkyboi<R> {
     fn new(inner: R, chunksize: usize) -> Self {
         let buf = vec![0; chunksize];
-        Chunkyboi {
-            inner,
-            buf
-        }
+        Chunkyboi { inner, buf }
     }
 }
 
@@ -205,7 +243,7 @@ impl<R: Read> Iterator for Chunkyboi<R> {
                 // clone a new buffer so we can reuse the internal one
                 Some(Ok(self.buf[..read].to_owned()))
             }
-            Err(err) => Some(Err(err))
+            Err(err) => Some(Err(err)),
         }
     }
 }
diff --git a/users/Profpatsch/reverse-haskell-deps.hs b/users/Profpatsch/reverse-haskell-deps.hs
deleted file mode 100644
index 6b644df9ec..0000000000
--- a/users/Profpatsch/reverse-haskell-deps.hs
+++ /dev/null
@@ -1,72 +0,0 @@
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE MultiWayIf #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-import qualified Text.HTML.TagSoup as Tag
-import qualified Data.Text as Text
-import Data.Text (Text)
-import qualified Data.List as List
-import Data.Maybe
-import Text.Nicify
-import qualified Text.Read as Read
-import Numeric.Natural
-import Data.Either
-import qualified Data.ByteString as ByteString
-import qualified Data.Text.Encoding
-
-parseNat :: Text.Text -> Maybe Natural
-parseNat = Read.readMaybe . Text.unpack
-
-printNice :: Show a => a -> IO ()
-printNice = putStrLn . nicify . show
-
-type Tag = Tag.Tag Text.Text
-
-main = do
-  reverseHtml <- readStdinUtf8
-  printNice $ List.sortOn snd $ packagesAndReverseDeps reverseHtml
-
-  where
-    readStdinUtf8 = Data.Text.Encoding.decodeUtf8 <$> ByteString.getContents
-
--- | reads the table provided by https://packdeps.haskellers.com/reverse
--- figuring out all sections (starting with the link to the package name),
--- then figuring out the name of the package and the first column,
--- which is the number of reverse dependencies of the package
-packagesAndReverseDeps reverseHtml = do
-  let tags = Tag.parseTags reverseHtml
-  let sections =  Tag.partitions (isJust . reverseLink) tags
-  let sectionNames = map (fromJust . reverseLink . head) sections
-  mapMaybe
-    (\(name :: Text.Text, sect) -> do
-        reverseDeps <- firstNaturalNumber sect
-        pure (sectionPackageName name sect, reverseDeps) :: Maybe (Text.Text, Natural))
-    $ zip sectionNames sections
-
-
-  where
-    reverseLink = \case
-      Tag.TagOpen "a" attrs -> mapFind attrReverseLink attrs
-      _ -> Nothing
-
-    attrReverseLink = \case
-      ("href", lnk) -> if
-          | "packdeps.haskellers.com/reverse/" `Text.isInfixOf` lnk -> Just lnk
-          | otherwise -> Nothing
-      _ -> Nothing
-
-    sectionPackageName :: Text -> [Tag] -> Text
-    sectionPackageName sectionName = \case
-      (_: Tag.TagText name : _) -> name
-      (_: el : _) -> sectionName
-      xs -> sectionName
-
-
-    firstNaturalNumber :: [Tag] -> Maybe Natural
-    firstNaturalNumber =
-      mapFind (\case
-        Tag.TagText t -> parseNat t
-        _ -> Nothing)
-
-    mapFind :: (a -> Maybe b) -> [a] -> Maybe b
-    mapFind f xs = fromJust . f <$> List.find (isJust . f) xs
diff --git a/users/Profpatsch/reverse-haskell-deps.nix b/users/Profpatsch/reverse-haskell-deps.nix
deleted file mode 100644
index b47347ea9f..0000000000
--- a/users/Profpatsch/reverse-haskell-deps.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-{ depot, pkgs, ... }:
-
-# Parses https://packdeps.haskellers.com/reverse
-# and outputs the amount of reverse dependencies of each hackage package.
-
-let
-
-  rev = depot.nix.writeExecline "reverse-haskell-deps" {} [
-    "pipeline" [
-      "${pkgs.curl}/bin/curl" "-L" "https://packdeps.haskellers.com/reverse"
-    ]
-    rev-hs
-
-  ];
-
-  rev-hs = pkgs.writers.writeHaskell "revers-haskell-deps-hs" {
-    libraries =  [
-      pkgs.haskellPackages.nicify-lib
-      pkgs.haskellPackages.tagsoup
-    ];
-
-  }
-    ./reverse-haskell-deps.hs;
-
-
-in rev
diff --git a/users/Profpatsch/reverse-haskell-deps/README.md b/users/Profpatsch/reverse-haskell-deps/README.md
new file mode 100644
index 0000000000..efc288cae4
--- /dev/null
+++ b/users/Profpatsch/reverse-haskell-deps/README.md
@@ -0,0 +1,3 @@
+# reverse-haskell-deps
+
+Parse the HTML at `https://packdeps.haskellers.com/reverse` to get the data about Haskell package reverse dependencies in a structured way (they should just expose that as a json tbh).
diff --git a/users/Profpatsch/reverse-haskell-deps/ReverseHaskellDeps.hs b/users/Profpatsch/reverse-haskell-deps/ReverseHaskellDeps.hs
new file mode 100644
index 0000000000..0e18ce8a6b
--- /dev/null
+++ b/users/Profpatsch/reverse-haskell-deps/ReverseHaskellDeps.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Main where
+
+import Data.ByteString qualified as ByteString
+import Data.Either
+import Data.List qualified as List
+import Data.Maybe
+import Data.Text (Text)
+import Data.Text qualified as Text
+import Data.Text.Encoding qualified
+import MyPrelude
+import Numeric.Natural
+import Text.HTML.TagSoup qualified as Tag
+import Text.Nicify
+import Text.Read qualified as Read
+
+parseNat :: Text -> Maybe Natural
+parseNat = Read.readMaybe . textToString
+
+printNice :: Show a => a -> IO ()
+printNice = putStrLn . nicify . show
+
+type Tag = Tag.Tag Text
+
+main = do
+  reverseHtml <- readStdinUtf8
+  printNice $ List.sortOn snd $ packagesAndReverseDeps reverseHtml
+  where
+    readStdinUtf8 = bytesToTextUtf8Lenient <$> ByteString.getContents
+
+-- | reads the table provided by https://packdeps.haskellers.com/reverse
+-- figuring out all sections (starting with the link to the package name),
+-- then figuring out the name of the package and the first column,
+-- which is the number of reverse dependencies of the package
+packagesAndReverseDeps :: Text -> [(Text, Natural)]
+packagesAndReverseDeps reverseHtml = do
+  let tags = Tag.parseTags reverseHtml
+  let sections = Tag.partitions (isJust . reverseLink) tags
+  let sectionName [] = "<unknown section>"
+      sectionName (sect : _) = sect & reverseLink & fromMaybe "<unknown section>"
+  let sectionNames = map sectionName sections
+  mapMaybe
+    ( \(name :: Text, sect) -> do
+        reverseDeps <- firstNaturalNumber sect
+        pure (sectionPackageName name sect, reverseDeps) :: Maybe (Text, Natural)
+    )
+    $ zip sectionNames sections
+  where
+    reverseLink = \case
+      Tag.TagOpen "a" attrs -> findMaybe attrReverseLink attrs
+      _ -> Nothing
+
+    attrReverseLink = \case
+      ("href", lnk) ->
+        if
+            | "packdeps.haskellers.com/reverse/" `Text.isInfixOf` lnk -> Just lnk
+            | otherwise -> Nothing
+      _ -> Nothing
+
+    sectionPackageName :: Text -> [Tag] -> Text
+    sectionPackageName sectionName = \case
+      (_ : Tag.TagText name : _) -> name
+      (_ : el : _) -> sectionName
+      xs -> sectionName
+
+    firstNaturalNumber :: [Tag] -> Maybe Natural
+    firstNaturalNumber =
+      findMaybe
+        ( \case
+            Tag.TagText t -> parseNat t
+            _ -> Nothing
+        )
diff --git a/users/Profpatsch/reverse-haskell-deps/default.nix b/users/Profpatsch/reverse-haskell-deps/default.nix
new file mode 100644
index 0000000000..b0a44420d7
--- /dev/null
+++ b/users/Profpatsch/reverse-haskell-deps/default.nix
@@ -0,0 +1,32 @@
+{ depot, pkgs, ... }:
+
+# Parses https://packdeps.haskellers.com/reverse
+# and outputs the amount of reverse dependencies of each hackage package.
+
+let
+
+  rev = depot.nix.writeExecline "reverse-haskell-deps" { } [
+    "pipeline"
+    [
+      "${pkgs.curl}/bin/curl"
+      "-L"
+      "https://packdeps.haskellers.com/reverse"
+    ]
+    rev-hs
+
+  ];
+
+  rev-hs = pkgs.writers.writeHaskell "revers-haskell-deps-hs"
+    {
+      libraries = [
+        depot.users.Profpatsch.my-prelude
+        pkgs.haskellPackages.nicify-lib
+        pkgs.haskellPackages.tagsoup
+      ];
+      ghcArgs = [ "-threaded" ];
+    }
+    ./ReverseHaskellDeps.hs;
+
+
+in
+rev
diff --git a/users/Profpatsch/reverse-haskell-deps/reverse-haskell-deps.cabal b/users/Profpatsch/reverse-haskell-deps/reverse-haskell-deps.cabal
new file mode 100644
index 0000000000..4792f52adf
--- /dev/null
+++ b/users/Profpatsch/reverse-haskell-deps/reverse-haskell-deps.cabal
@@ -0,0 +1,16 @@
+cabal-version:      3.0
+name:               reverse-haskell-deps
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+library
+    exposed-modules:          ReverseHaskellDeps.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        my-prelude,
+        tagsoup,
+        nicify-lib
+
+    default-language: Haskell2010
diff --git a/users/Profpatsch/shell.nix b/users/Profpatsch/shell.nix
new file mode 100644
index 0000000000..b5095d476f
--- /dev/null
+++ b/users/Profpatsch/shell.nix
@@ -0,0 +1,110 @@
+# generic shell.nix that can be used for most of my projects here,
+# until I figure out a way to have composable shells.
+let root = (import ../../. { }); in
+{ pkgs ? root.third_party.nixpkgs, depot ? root, ... }:
+
+pkgs.mkShell {
+
+  buildInputs = [
+    pkgs.sqlite-interactive
+    pkgs.sqlite-utils
+    pkgs.haskell-language-server
+    pkgs.cabal-install
+    (pkgs.haskellPackages.ghcWithHoogle (h: [
+      h.async
+      h.aeson-better-errors
+      h.blaze-html
+      h.conduit-extra
+      h.error
+      h.monad-logger
+      h.pa-field-parser
+      h.pa-label
+      h.pa-json
+      h.pa-pretty
+      h.pa-run-command
+      h.ihp-hsx
+      h.PyF
+      h.foldl
+      h.unliftio
+      h.xml-conduit
+      h.wai
+      h.wai-extra
+      h.warp
+      h.profunctors
+      h.semigroupoids
+      h.validation-selective
+      h.free
+      h.cryptonite-conduit
+      h.sqlite-simple
+      h.hedgehog
+      h.http-conduit
+      h.http-conduit
+      h.wai-conduit
+      h.nonempty-containers
+      h.deriving-compat
+      h.unix
+      h.tagsoup
+      h.attoparsec
+      h.iCalendar
+      h.case-insensitive
+      h.hscolour
+      h.nicify-lib
+      h.hspec
+      h.hspec-expectations-pretty-diff
+      h.tmp-postgres
+      h.postgresql-simple
+      h.resource-pool
+      h.xmonad-contrib
+      h.hs-opentelemetry-sdk
+      h.punycode
+    ]))
+
+    pkgs.rustup
+    pkgs.pkg-config
+    pkgs.fuse
+    pkgs.postgresql_14
+    pkgs.nodejs
+    pkgs.ninja
+    pkgs.s6
+    pkgs.caddy
+
+    (depot.nix.binify {
+      name = "nix-run";
+      exe = depot.users.Profpatsch.nix-tools.nix-run;
+    })
+  ];
+
+  DEPOT_ROOT = toString ./../..;
+  PROFPATSCH_ROOT = toString ./.;
+
+  WHATCD_RESOLVER_TOOLS = pkgs.linkFarm "whatcd-resolver-tools" [
+    {
+      name = "pg_format";
+      path = "${pkgs.pgformatter}/bin/pg_format";
+    }
+  ];
+
+  # DECLIB_MASTODON_ACCESS_TOKEN read from `pass` in .envrc.
+
+  RUSTC_WRAPPER =
+    let
+      wrapperArgFile = libs: pkgs.writeText "rustc-wrapper-args"
+        (pkgs.lib.concatStringsSep
+          "\n"
+          (pkgs.lib.concatLists
+            (map
+              (lib: [
+                "-L"
+                "${pkgs.lib.getLib lib}/lib"
+              ])
+              libs)));
+    in
+    depot.nix.writeExecline "rustc-wrapper" { readNArgs = 1; } [
+      "$1"
+      "$@"
+      "@${wrapperArgFile [
+      depot.third_party.rust-crates.nom
+    ]}"
+    ];
+
+}
diff --git a/users/Profpatsch/shortcuttable/default.nix b/users/Profpatsch/shortcuttable/default.nix
new file mode 100644
index 0000000000..13ba220400
--- /dev/null
+++ b/users/Profpatsch/shortcuttable/default.nix
@@ -0,0 +1,172 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  # run prog... and restart whenever SIGHUP is received
+  #
+  # this is useful for binding to a shortcut.
+  #
+  # Unfortunately, this requires a bunch of workarounds around the semantics of `trap`,
+  # but the general idea of bundling subprocesses with `setsid` is somewhat sound.
+  runShortcuttable =
+    depot.nix.writeExecline "run-shortcuttable" { } [
+      "importas"
+      "-i"
+      "run"
+      "XDG_RUNTIME_DIR"
+      "if"
+      [ "mkdir" "-p" "\${run}/shortcuttable/test" ]
+      "getpid"
+      "-E"
+      "controlpid"
+      savePid
+      "\${run}/shortcuttable/test/control"
+      "$controlpid"
+
+      # start the program
+      "background"
+      [
+        startSaveSID
+        "\${run}/shortcuttable/test/running-sid"
+        "$@"
+      ]
+
+      "trap"
+      [
+        "SIGHUP"
+        [
+          "if"
+          [ "echo" "got hup" ]
+          "if"
+          [
+            "if"
+            [ "echo" "killing our child processes" ]
+            "envfile"
+            "\${run}/shortcuttable/test/running-sid"
+            "importas"
+            "-ui"
+            "child_sid"
+            "pid"
+            "foreground"
+            [ "ps" "-f" "--sid" "$child_sid" ]
+            ctrlCCtrlDSid
+            "$child_sid"
+          ]
+          "if"
+          [ "echo" "restarting into" "$@" ]
+          "background"
+          [
+            startSaveSID
+            "\${run}/shortcuttable/test/running-sid"
+            "$@"
+          ]
+        ]
+        "SIGTERM"
+        [
+          (killShortcuttable { signal = "TERM"; })
+          "\${run}/shortcuttable/test/running-sid"
+          "\${run}/shortcuttable/test/exit"
+        ]
+        "SIGINT"
+        [
+          (killShortcuttable { signal = "INT"; })
+          "\${run}/shortcuttable/test/running-sid"
+          "\${run}/shortcuttable/test/exit"
+        ]
+      ]
+      depot.users.Profpatsch.execline.setsid
+      "child_sid"
+      "getpid"
+      "-E"
+      "exitpid"
+      savePid
+      "\${run}/shortcuttable/test/exit"
+      "$exitpid"
+      "sleep"
+      "infinity"
+    ];
+
+  killShortcuttable = { signal }: depot.nix.writeExecline "kill-shortcuttable" { readNArgs = 2; } [
+    "if"
+    [ "echo" "got SIG${signal}, quitting" ]
+    "if"
+    [
+      "envfile"
+      "$1"
+      "importas"
+      "-ui"
+      "child_sid"
+      "pid"
+      "foreground"
+      [ "ps" "-f" "--sid" "$child_sid" ]
+      ctrlCCtrlDSid
+      "$child_sid"
+    ]
+    "if"
+    [ "echo" "killing shortcuttable loop" ]
+    "envfile"
+    "$2"
+    "importas"
+    "-ui"
+    "trap_pid"
+    "pid"
+    "foreground"
+    [ "ps" "-fp" "$trap_pid" ]
+    "kill"
+    "--signal"
+    signal
+    "$trap_pid"
+  ];
+
+  savePid = depot.nix.writeExecline "save-pid" { readNArgs = 2; } [
+    "if"
+    [ "echo" "saving process:" ]
+    "if"
+    [ "ps" "-fp" "$2" ]
+    "if"
+    [
+      "redirfd"
+      "-w"
+      "1"
+      "$1"
+      "printf"
+      "pid = %s\n"
+      "$2"
+    ]
+    "$@"
+  ];
+
+  # try to kill process, first with SIGTERM then SIGQUIT (in case it’s a repl)
+  ctrlCCtrlDSid = depot.nix.writeExecline "ctrl-c-ctrl-d" { readNArgs = 1; } [
+    "ifelse"
+    "-n"
+    [ "kill" "--signal" "TERM" "--" "-\${1}" ]
+    [
+      "if"
+      [ "echo" "could not kill via SIGTERM, trying SIGQUIT …" ]
+      "ifelse"
+      "-n"
+      [ "kill" "--signal" "QUIT" "--" "-\${1}" ]
+      [ "echo" "SIGQUIT failed as well, keeping it running" ]
+      "$@"
+    ]
+    "$@"
+  ];
+
+  startSaveSID = depot.nix.writeExecline "start-save-sid" { readNArgs = 1; } [
+    depot.users.Profpatsch.execline.setsid
+    "child_sid"
+    "importas"
+    "-ui"
+    "child_sid"
+    "child_sid"
+    "if"
+    [ "echo" "children sid:" "$child_sid" ]
+    savePid
+    "$1"
+    "$child_sid"
+    "$@"
+  ];
+
+
+in
+runShortcuttable
diff --git a/users/Profpatsch/struct-edit/default.nix b/users/Profpatsch/struct-edit/default.nix
deleted file mode 100644
index 970cdd4d02..0000000000
--- a/users/Profpatsch/struct-edit/default.nix
+++ /dev/null
@@ -1,13 +0,0 @@
-{ depot, ... }:
-depot.nix.buildGo.program {
-    name = "struct-edit";
-    srcs = [
-      ./main.go
-    ];
-    deps = [
-      depot.third_party.gopkgs."github.com".charmbracelet.bubbletea
-      depot.third_party.gopkgs."github.com".charmbracelet.lipgloss
-      depot.third_party.gopkgs."github.com".muesli.termenv
-      depot.third_party.gopkgs."github.com".mattn.go-isatty
-    ];
-}
diff --git a/users/Profpatsch/struct-edit/main.go b/users/Profpatsch/struct-edit/main.go
deleted file mode 100644
index c1a7013385..0000000000
--- a/users/Profpatsch/struct-edit/main.go
+++ /dev/null
@@ -1,431 +0,0 @@
-package main
-
-import (
-	json "encoding/json"
-	"fmt"
-	"log"
-	"os"
-	"sort"
-	"strings"
-
-	tea "github.com/charmbracelet/bubbletea"
-	lipgloss "github.com/charmbracelet/lipgloss"
-	// termenv "github.com/muesli/termenv"
-	// isatty "github.com/mattn/go-isatty"
-)
-
-// Keeps the full data structure and a path that indexes our current position into it.
-type model struct {
-	path []index
-	data val
-}
-
-// an index into a value, uint for lists and string for maps.
-// nil for any scalar value.
-// TODO: use an actual interface for these
-type index interface{}
-
-/// recursive value that we can represent.
-type val struct {
-	// the β€œtype” of value; see tag const belove
-	tag tag
-	// last known position of our cursor
-	last_index index
-	// documentation (TODO)
-	doc string
-	// the actual value;
-	// the actual structure is behind a pointer so we can replace the struct.
-	// determined by the tag
-	// tagString -> *string
-	// tagFloat -> *float64
-	// tagList -> *[]val
-	// tagMap -> *map[string]val
-	val interface{}
-}
-
-type tag string
-
-const (
-	tagString tag = "string"
-	tagFloat  tag = "float"
-	tagList   tag = "list"
-	tagMap    tag = "map"
-)
-
-// print a value, flat
-func (v val) Render() string {
-	s := ""
-	switch v.tag {
-	case tagString:
-		s += *v.val.(*string)
-	case tagFloat:
-		s += fmt.Sprint(*v.val.(*float64))
-	case tagList:
-		s += "[ "
-		vs := []string{}
-		for _, enum := range v.enumerate() {
-			vs = append(vs, enum.v.Render())
-		}
-		s += strings.Join(vs, ", ")
-		s += " ]"
-	case tagMap:
-		s += "{ "
-		vs := []string{}
-		for _, enum := range v.enumerate() {
-			vs = append(vs, fmt.Sprintf("%s: %s", enum.i.(string), enum.v.Render()))
-		}
-		s += strings.Join(vs, ", ")
-		s += " }"
-	default:
-		s += fmt.Sprintf("<unknown: %v>", v)
-	}
-	return s
-}
-
-// render an index, depending on the type
-func renderIndex(i index) (s string) {
-	switch i := i.(type) {
-	case nil:
-		s = ""
-	// list index
-	case uint:
-		s = "*"
-	// map index
-	case string:
-		s = i + ":"
-	}
-	return
-}
-
-// take an arbitrary (within restrictions) go value and construct a val from it
-func makeVal(i interface{}) val {
-	var v val
-	switch i := i.(type) {
-	case string:
-		v = val{
-			tag:        tagString,
-			last_index: index(nil),
-			doc:        "",
-			val:        &i,
-		}
-	case float64:
-		v = val{
-			tag:        tagFloat,
-			last_index: index(nil),
-			doc:        "",
-			val:        &i,
-		}
-	case []interface{}:
-		ls := []val{}
-		for _, i := range i {
-			ls = append(ls, makeVal(i))
-		}
-		v = val{
-			tag:        tagList,
-			last_index: pos1Inner(tagList, &ls),
-			doc:        "",
-			val:        &ls,
-		}
-	case map[string]interface{}:
-		ls := map[string]val{}
-		for k, i := range i {
-			ls[k] = makeVal(i)
-		}
-		v = val{
-			tag:        tagMap,
-			last_index: pos1Inner(tagMap, &ls),
-			doc:        "",
-			val:        &ls,
-		}
-	default:
-		log.Fatalf("makeVal: cannot read json of type %T", i)
-	}
-	return v
-}
-
-// return an index that points at the first entry in val
-func (v val) pos1() index {
-	return v.enumerate()[0].i
-}
-
-func pos1Inner(tag tag, v interface{}) index {
-	return enumerateInner(tag, v)[0].i
-}
-
-type enumerate struct {
-	i index
-	v val
-}
-
-// enumerate gives us a stable ordering of elements in this val.
-// for scalars it’s just a nil index & the val itself.
-// Guaranteed to always return at least one element.
-func (v val) enumerate() (e []enumerate) {
-	e = enumerateInner(v.tag, v.val)
-	if e == nil {
-		e = append(e, enumerate{
-			i: nil,
-			v: v,
-		})
-	}
-	return
-}
-
-// like enumerate, but returns an empty slice for scalars without inner vals.
-func enumerateInner(tag tag, v interface{}) (e []enumerate) {
-	switch tag {
-	case tagString:
-		fallthrough
-	case tagFloat:
-		e = nil
-	case tagList:
-		for i, v := range *v.(*[]val) {
-			e = append(e, enumerate{i: index(uint(i)), v: v})
-		}
-	case tagMap:
-		// map sorting order is not stable (actually randomized thank jabber)
-		// so let’s sort them
-		keys := []string{}
-		m := *v.(*map[string]val)
-		for k, _ := range m {
-			keys = append(keys, k)
-		}
-		sort.Strings(keys)
-		for _, k := range keys {
-			e = append(e, enumerate{i: index(k), v: m[k]})
-		}
-	default:
-		log.Fatalf("unknown val tag %s, %v", tag, v)
-	}
-	return
-}
-
-func (m model) PathString() string {
-	s := "/ "
-	var is []string
-	for _, v := range m.path {
-		is = append(is, fmt.Sprintf("%v", v))
-	}
-	s += strings.Join(is, " / ")
-	return s
-}
-
-// walk the given path down in data, to get the value at that point.
-// Assumes that all path indexes are valid indexes into data.
-// Returns a pointer to the value at point, in order to be able to change it.
-func walk(data *val, path []index) (*val, bool, error) {
-	res := data
-	atPath := func(index int) string {
-		return fmt.Sprintf("at path %v", path[:index+1])
-	}
-	errf := func(ty string, val interface{}, index int) error {
-		return fmt.Errorf("walk: can’t walk into %s %v %s", ty, val, atPath(index))
-	}
-	for i, p := range path {
-		switch res.tag {
-		case tagString:
-			return nil, true, nil
-		case tagFloat:
-			return nil, true, nil
-		case tagList:
-			switch p := p.(type) {
-			case uint:
-				list := *res.val.(*[]val)
-				if int(p) >= len(list) || p < 0 {
-					return nil, false, fmt.Errorf("index out of bounds %s", atPath(i))
-				}
-				res = &list[p]
-			default:
-				return nil, false, fmt.Errorf("not a list index %s", atPath(i))
-			}
-		case tagMap:
-			switch p := p.(type) {
-			case string:
-				m := *res.val.(*map[string]val)
-				if a, ok := m[p]; ok {
-					res = &a
-				} else {
-					return nil, false, fmt.Errorf("index %s not in map %s", p, atPath(i))
-				}
-			default:
-				return nil, false, fmt.Errorf("not a map index %v %s", p, atPath(i))
-			}
-
-		default:
-			return nil, false, errf(string(res.tag), res.val, i)
-		}
-	}
-	return res, false, nil
-}
-
-// descend into the selected index. Assumes that the index is valid.
-// Will not descend into scalars.
-func (m model) descend() (model, error) {
-	// TODO: two walks?!
-	this, _, err := walk(&m.data, m.path)
-	if err != nil {
-		return m, err
-	}
-	newPath := append(m.path, this.last_index)
-	_, bounce, err := walk(&m.data, newPath)
-	if err != nil {
-		return m, err
-	}
-	// only descend if we *can*
-	if !bounce {
-		m.path = newPath
-	}
-	return m, nil
-}
-
-// ascend to one level up. stops at the root.
-func (m model) ascend() (model, error) {
-	if len(m.path) > 0 {
-		m.path = m.path[:len(m.path)-1]
-		_, _, err := walk(&m.data, m.path)
-		return m, err
-	}
-	return m, nil
-}
-
-/// go to the next item, or wraparound
-func (min model) next() (m model, err error) {
-	m = min
-	this, _, err := walk(&m.data, m.path)
-	if err != nil {
-		return
-	}
-	enumL := this.enumerate()
-	setNext := false
-	for _, enum := range enumL {
-		if setNext {
-			this.last_index = enum.i
-			setNext = false
-			break
-		}
-		if enum.i == this.last_index {
-			setNext = true
-		}
-	}
-	// wraparound
-	if setNext {
-		this.last_index = enumL[0].i
-	}
-	return
-}
-
-/// go to the previous item, or wraparound
-func (min model) prev() (m model, err error) {
-	m = min
-	this, _, err := walk(&m.data, m.path)
-	if err != nil {
-		return
-	}
-	enumL := this.enumerate()
-	// last element, wraparound
-	prevIndex := enumL[len(enumL)-1].i
-	for _, enum := range enumL {
-		if enum.i == this.last_index {
-			this.last_index = prevIndex
-			break
-		}
-		prevIndex = enum.i
-	}
-	return
-}
-
-/// bubbletea implementations
-
-func (m model) Init() tea.Cmd {
-	return nil
-}
-
-func initialModel(v interface{}) model {
-	val := makeVal(v)
-	return model{
-		path: []index{},
-		data: val,
-	}
-}
-
-func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
-	var err error
-	switch msg := msg.(type) {
-	case tea.KeyMsg:
-		switch msg.String() {
-		case "ctrl+c", "q":
-			return m, tea.Quit
-
-		case "up":
-			m, err = m.prev()
-
-		case "down":
-			m, err = m.next()
-
-		case "right":
-			m, err = m.descend()
-
-		case "left":
-			m, err = m.ascend()
-
-			// 	case "enter":
-			// 		_, ok := m.selected[m.cursor]
-			// 		if ok {
-			// 			delete(m.selected, m.cursor)
-			// 		} else {
-			// 			m.selected[m.cursor] = struct{}{}
-			// 		}
-		}
-
-	}
-	if err != nil {
-		log.Fatal(err)
-	}
-	return m, nil
-}
-
-var pathColor = lipgloss.NewStyle().
-	// light blue
-	Foreground(lipgloss.Color("12"))
-
-var selectedColor = lipgloss.NewStyle().
-	Bold(true)
-
-func (m model) View() string {
-	s := pathColor.Render(m.PathString())
-	cur, _, err := walk(&m.data, m.path)
-	if err != nil {
-		log.Fatal(err)
-	}
-	s += cur.doc + "\n"
-	s += "\n"
-	for _, enum := range cur.enumerate() {
-		is := renderIndex(enum.i)
-		if is != "" {
-			s += is + " "
-		}
-		if enum.i == cur.last_index {
-			s += selectedColor.Render(enum.v.Render())
-		} else {
-			s += enum.v.Render()
-		}
-		s += "\n"
-	}
-
-	// s += fmt.Sprintf("%v\n", m)
-	// s += fmt.Sprintf("%v\n", cur)
-
-	return s
-}
-
-func main() {
-	var input interface{}
-	err := json.NewDecoder(os.Stdin).Decode(&input)
-	if err != nil {
-		log.Fatal("json from stdin: ", err)
-	}
-	p := tea.NewProgram(initialModel(input))
-	if err := p.Start(); err != nil {
-		log.Fatal("bubbletea TUI error: ", err)
-	}
-}
diff --git a/users/Profpatsch/sync-abfall-ics-aichach-friedberg/README.md b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/README.md
new file mode 100644
index 0000000000..e0a6aa2fb8
--- /dev/null
+++ b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/README.md
@@ -0,0 +1,3 @@
+# sync-abfall-ics-aichach-friedberg
+
+A small tool to sync the ICS files for the local trash collection times at https://abfallwirtschaft.lra-aic-fdb.de/
diff --git a/users/Profpatsch/sync-abfall-ics-aichach-friedberg/default.nix b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/default.nix
new file mode 100644
index 0000000000..739274cb6f
--- /dev/null
+++ b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/default.nix
@@ -0,0 +1,31 @@
+{ depot, pkgs, ... }:
+
+let
+  sync-to-dir = depot.users.Profpatsch.writers.python3
+    {
+      name = "sync-ics-to-dir";
+      libraries = (py: [
+        py.httpx
+        py.icalendar
+      ]);
+    } ./sync-ics-to-dir.py;
+
+  config =
+    depot.users.Profpatsch.importDhall.importDhall
+      {
+        root = ./..;
+        files = [
+          "sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall"
+          "dhall/lib.dhall"
+          "ini/ini.dhall"
+        ];
+        main = "sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall";
+        deps = [
+        ];
+      }
+      depot.users.Profpatsch.ini.externs;
+
+
+
+in
+{ inherit config; }
diff --git a/users/Profpatsch/sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall
new file mode 100644
index 0000000000..2a7ac84979
--- /dev/null
+++ b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall
@@ -0,0 +1,139 @@
+let Ini = ../ini/ini.dhall
+
+let Lib = ../dhall/lib.dhall
+
+in  \(Ini/externs : Ini.Externs) ->
+      let Vdirsyncer =
+            let StorageType =
+                  < FileSystem : { path : Text, fileext : < ICS > }
+                  | Http : { url : Text }
+                  >
+
+            let Collection = < FromA | FromB | Collection : Text >
+
+            let Collections =
+                  < Unspecified | TheseCollections : List Collection >
+
+            let Storage = { storageName : Text, storage : StorageType }
+
+            in  { Storage
+                , StorageType
+                , Collection
+                , Collections
+                , Pair =
+                    { pairName : Text
+                    , a : Storage
+                    , b : Storage
+                    , collections : Collections
+                    }
+                }
+
+      let toIniSections
+          : Vdirsyncer.Pair -> Ini.Sections
+          = \(pair : Vdirsyncer.Pair) ->
+              let
+                  -- we assume the names are [a-zA-Z_]
+                  renderList =
+                    \(l : List Text) ->
+                          "["
+                      ++  Lib.Text/concatMapSep
+                            ", "
+                            Text
+                            (\(t : Text) -> "\"${t}\"")
+                            l
+                      ++  "]"
+
+              in  let nv = \(name : Text) -> \(value : Text) -> { name, value }
+
+                  let mkStorage =
+                        \(storage : Vdirsyncer.Storage) ->
+                          { name = "storage ${storage.storageName}"
+                          , value =
+                              merge
+                                { FileSystem =
+                                    \ ( fs
+                                      : { path : Text, fileext : < ICS > }
+                                      ) ->
+                                      [ nv "type" "filesystem"
+                                      , nv
+                                          "fileext"
+                                          (merge { ICS = ".ics" } fs.fileext)
+                                      , nv "path" fs.path
+                                      ]
+                                , Http =
+                                    \(http : { url : Text }) ->
+                                      [ nv "type" "http", nv "url" http.url ]
+                                }
+                                storage.storage
+                          }
+
+                  in  [ { name = "pair ${pair.pairName}"
+                        , value =
+                          [ nv "a" pair.a.storageName
+                          , nv "b" pair.b.storageName
+                          , nv
+                              "collections"
+                              ( merge
+                                  { Unspecified = "none"
+                                  , TheseCollections =
+                                      \(colls : List Vdirsyncer.Collection) ->
+                                        renderList
+                                          ( Lib.List/map
+                                              Vdirsyncer.Collection
+                                              Text
+                                              ( \ ( coll
+                                                  : Vdirsyncer.Collection
+                                                  ) ->
+                                                  merge
+                                                    { FromA = "from a"
+                                                    , FromB = "from b"
+                                                    , Collection =
+                                                        \(t : Text) -> t
+                                                    }
+                                                    coll
+                                              )
+                                              colls
+                                          )
+                                  }
+                                  pair.collections
+                              )
+                          ]
+                        }
+                      , mkStorage pair.a
+                      , mkStorage pair.b
+                      ]
+
+      in  { example =
+              Ini/externs.renderIni
+                ( Ini.appendInis
+                    ( Lib.List/map
+                        Vdirsyncer.Pair
+                        Ini.Ini
+                        ( \(pair : Vdirsyncer.Pair) ->
+                            { globalSection = [] : Ini.Section
+                            , sections = toIniSections pair
+                            }
+                        )
+                        (   [ { pairName = "testPair"
+                              , a =
+                                { storageName = "mystor"
+                                , storage =
+                                    Vdirsyncer.StorageType.FileSystem
+                                      { path = "./test-ics"
+                                      , fileext = < ICS >.ICS
+                                      }
+                                }
+                              , b =
+                                { storageName = "mystor"
+                                , storage =
+                                    Vdirsyncer.StorageType.Http
+                                      { url = "https://profpatsch.de" }
+                                }
+                              , collections = Vdirsyncer.Collections.Unspecified
+                              }
+                            ]
+                          : List Vdirsyncer.Pair
+                        )
+                    )
+                )
+          }
diff --git a/users/Profpatsch/sync-abfall-ics-aichach-friedberg/sync-ics-to-dir.py b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/sync-ics-to-dir.py
new file mode 100644
index 0000000000..4af3b9fb85
--- /dev/null
+++ b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/sync-ics-to-dir.py
@@ -0,0 +1,133 @@
+# horrible little module that fetches ICS files for the local trash public service.
+#
+# It tries its best to not overwrite existing ICS files in case the upstream goes down
+# or returns empty ICS files.
+import sys
+import httpx
+import asyncio
+import icalendar
+from datetime import datetime
+import syslog
+import os.path
+
+# Internal id for the street (extracted from the ics download url)
+ortsteil_id = "e9c32ab3-df25-4660-b88e-abda91897d7a"
+
+# They are using a numeric encoding to refer to different kinds of trash
+fraktionen = {
+    "restmΓΌll": "1",
+    "bio": "5",
+    "papier": "7",
+    "gelbe_tonne": "13",
+    "problemmΓΌllsammlung": "20"
+}
+
+def ics_url(year):
+  frakt = ','.join(fraktionen.values())
+  return f'https://awido.cubefour.de/Customer/aic-fdb/KalenderICS.aspx?oid={ortsteil_id}&jahr={year}&fraktionen={frakt}&reminder=1.12:00'
+
+def fetchers_for_years(start_year, no_of_years_in_future):
+    """given a starting year, and a number of years in the future,
+    return the years for which to fetch ics files"""
+    current_year = datetime.now().year
+    max_year = current_year + no_of_years_in_future
+    return {
+        "passed_years": range(start_year, current_year),
+        "this_and_future_years": range(current_year, 1 + max_year)
+    }
+
+async def fetch_ics(c, url):
+    """fetch an ICS file from an URL"""
+    try:
+        resp = await c.get(url)
+    except Exception as e:
+        return { "ics_does_not_exist_exc": e }
+
+    if resp.is_error:
+        return { "ics_does_not_exist": resp }
+    else:
+        try:
+            ics = icalendar.Calendar.from_ical(resp.content)
+            return { "ics": { "ics_parsed": ics, "ics_bytes": resp.content } }
+        except ValueError as e:
+            return { "ics_cannot_be_parsed": e }
+
+def ics_has_events(ics):
+    """Determine if there is any event in the ICS, otherwise we can assume it’s an empty file"""
+    for item in ics.walk():
+      if isinstance(item, icalendar.Event):
+        return True
+    return False
+
+async def write_nonempty_ics(directory, year, ics):
+    # only overwrite if the new ics has any events
+    if ics_has_events(ics['ics_parsed']):
+        path = os.path.join(directory, f"{year}.ics")
+        with open(path, "wb") as f:
+            f.write(ics['ics_bytes'])
+            info(f"wrote ics for year {year} to file {path}")
+    else:
+        info(f"ics for year {year} was empty, skipping")
+
+
+def main():
+    ics_directory = os.getenv("ICS_DIRECTORY", None)
+    if not ics_directory:
+        critical("please set ICS_DIRECTORY")
+    start_year = int(os.getenv("ICS_START_YEAR", 2022))
+    future_years = int(os.getenv("ICS_FUTURE_YEARS", 2))
+
+    years = fetchers_for_years(start_year, no_of_years_in_future=future_years)
+
+
+    async def go():
+        async with httpx.AsyncClient(follow_redirects=True) as c:
+            info(f"fetching ics for passed years: {years['passed_years']}")
+            for year in years["passed_years"]:
+                match await fetch_ics(c, ics_url(year)):
+                    case { "ics_does_not_exist_exc": error }:
+                       warn(f"The ics for the year {year} is gone, error when requesting: {error} for url {ics_url(year)}")
+                    case { "ics_does_not_exist": resp }:
+                       warn(f"The ics for the year {year} is gone, server returned status {resp.status} for url {ics_url(year)}")
+                    case { "ics_cannot_be_parsed": error }:
+                       warn(f"The returned ICS could not be parsed: {error} for url {ics_url(year)}")
+                    case { "ics": ics }:
+                       info(f"fetched ics from {ics_url(year)}")
+                       await write_nonempty_ics(ics_directory, year, ics)
+                    case _:
+                       critical("unknown case for ics result")
+
+
+            info(f"fetching ics for current and upcoming years: {years['this_and_future_years']}")
+            for year in years["this_and_future_years"]:
+                match await fetch_ics(c, ics_url(year)):
+                    case { "ics_does_not_exist_exc": error }:
+                       critical(f"The ics for the year {year} is not available, error when requesting: {error} for url {ics_url(year)}")
+                    case { "ics_does_not_exist": resp }:
+                       critical(f"The ics for the year {year} is not available, server returned status {resp.status} for url {ics_url(year)}")
+                    case { "ics_cannot_be_parsed": error }:
+                       critical(f"The returned ICS could not be parsed: {error} for url {ics_url(year)}")
+                    case { "ics": ics }:
+                       info(f"fetched ics from {ics_url(year)}")
+                       await write_nonempty_ics(ics_directory, year, ics)
+                    case _:
+                       critical("unknown case for ics result")
+
+    asyncio.run(go())
+
+def info(msg):
+    syslog.syslog(syslog.LOG_INFO, msg)
+
+def critical(msg):
+    syslog.syslog(syslog.LOG_CRIT, msg)
+    sys.exit(1)
+
+def warn(msg):
+    syslog.syslog(syslog.LOG_WARNING, msg)
+
+def debug(msg):
+    syslog.syslog(syslog.LOG_DEBUG, msg)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/users/Profpatsch/tagtime/README.md b/users/Profpatsch/tagtime/README.md
new file mode 100644
index 0000000000..ab2c7d14e5
--- /dev/null
+++ b/users/Profpatsch/tagtime/README.md
@@ -0,0 +1,18 @@
+# tagtime reimplementation
+
+What’s great about original perl tagtime?
+
+* timestamps are deterministic from the beginning (keep)
+* the tagging system should just work (tm)
+
+What’s the problem with the original perl tagtime?
+
+* it uses a bad, arbitrary file format -> sqlite3
+* the query window does not time out, so it’s easy to miss that it’s open (often hidden behind another window), and then the following pings might never appear)
+* There’s a bug with tags containing a `.` -> sqlite3
+
+What would be cool to have?
+
+* multi-entry mode (ping on phone and laptop and merge the replies eventually since they will apply to single timestamps)
+* simplifying reporting based on fuzzy matching & history
+* auto-generate nice time reports with hours for work items
diff --git a/users/Profpatsch/toINI.nix b/users/Profpatsch/toINI.nix
new file mode 100644
index 0000000000..537505d30b
--- /dev/null
+++ b/users/Profpatsch/toINI.nix
@@ -0,0 +1,79 @@
+{ lib, ... }:
+let
+  /* Generate an INI-style config file from an attrset
+   * specifying the global section (no header), and a
+   * list of sections which contain name/value pairs.
+   *
+   * generators.toINI {} {
+   *   globalSection = [
+   *     { name = "someGlobalKey"; value = "hi"; }
+   *   ];
+   *   sections = [
+   *     { name = "foo"; value = [
+   *         { name = "hi"; value = "${pkgs.hello}"; }
+   *         { name = "ciao"; value = "bar"; }
+   *       ];
+   *     }
+   *     { name = "baz";
+   *       value = [ { name = "also, integers"; value = 42; } ];
+   *     }
+   *   ];
+   * }
+   *
+   *> someGlobalKey=hi
+   *>
+   *> [foo]
+   *> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
+   *> ciao=bar
+   *>
+   *> [baz]
+   *> also, integers=42
+   *>
+   *
+   * The mk* configuration attributes can generically change
+   * the way sections and key-value strings are generated.
+   *
+   * Order of the sections and of keys is preserved,
+   * duplicate keys are allowed.
+   */
+  toINI =
+    {
+      # apply transformations (e.g. escapes) to section names
+      mkSectionName ? (name: lib.strings.escape [ "[" "]" ] name)
+    , # format a setting line from key and value
+      mkKeyValue ? lib.generators.mkKeyValueDefault { } "="
+    ,
+    }: { globalSection, sections }:
+    let
+      mkSection = sectName: sectValues: ''
+        [${mkSectionName sectName}]
+      '' + toKeyValue { inherit mkKeyValue; } sectValues;
+      # map input to ini sections
+      mkSections = lib.strings.concatMapStringsSep "\n"
+        ({ name, value }: mkSection name value)
+        sections;
+      mkGlobalSection =
+        if globalSection == [ ]
+        then ""
+        else toKeyValue { inherit mkKeyValue; } globalSection
+          + "\n";
+    in
+    mkGlobalSection
+    + mkSections;
+
+  /* Generate a name-value-style config file from a list.
+   *
+   * mkKeyValue is the same as in toINI.
+   */
+  toKeyValue =
+    { mkKeyValue ? lib.generators.mkKeyValueDefault { } "="
+    ,
+    }:
+    let
+      mkLine = k: v: mkKeyValue k v + "\n";
+      mkLines = k: v: [ (mkLine k v) ];
+    in
+    nameValues: lib.strings.concatStrings (lib.concatLists (map ({ name, value }: mkLines name value) nameValues));
+
+in
+toINI
diff --git a/users/Profpatsch/tree-sitter.nix b/users/Profpatsch/tree-sitter.nix
index 4f81b8e7a7..2224da2a3b 100644
--- a/users/Profpatsch/tree-sitter.nix
+++ b/users/Profpatsch/tree-sitter.nix
@@ -2,17 +2,18 @@
 
 let
   bins = depot.nix.getBins pkgs.coreutils [ "head" "printf" "cat" ]
-      // depot.nix.getBins pkgs.ncurses [ "tput" ]
-      // depot.nix.getBins pkgs.bc [ "bc" ]
-      // depot.nix.getBins pkgs.ocamlPackages.sexp [ "sexp" ];
-
-  print-ast = depot.nix.writers.rustSimple {
-    name = "print-ast";
-    dependencies = with depot.third_party.rust-crates; [
-      libloading
-      tree-sitter
-    ];
-  } ''
+    // depot.nix.getBins pkgs.ncurses [ "tput" ]
+    // depot.nix.getBins pkgs.bc [ "bc" ]
+    // depot.nix.getBins pkgs.ocamlPackages.sexp [ "sexp" ];
+
+  print-ast = depot.nix.writers.rustSimple
+    {
+      name = "print-ast";
+      dependencies = with depot.third_party.rust-crates; [
+        libloading
+        tree-sitter
+      ];
+    } ''
     extern crate libloading;
     extern crate tree_sitter;
     use std::mem;
@@ -58,13 +59,14 @@ let
     };
   };
 
-  watch-file-modified = depot.nix.writers.rustSimple {
-    name = "watch-file-modified";
-    dependencies = [
-      depot.third_party.rust-crates.inotify
-      depot.users.Profpatsch.netstring.rust-netstring
-    ];
-  } ''
+  watch-file-modified = depot.nix.writers.rustSimple
+    {
+      name = "watch-file-modified";
+      dependencies = [
+        depot.third_party.rust-crates.inotify
+        depot.users.Profpatsch.netstring.rust-netstring
+      ];
+    } ''
     extern crate inotify;
     extern crate netstring;
     use inotify::{EventMask, WatchMask, Inotify};
@@ -101,75 +103,103 @@ let
   '';
 
   # clear screen and set LINES and COLUMNS to terminal height & width
-  clear-screen = depot.nix.writeExecline "clear-screen" {} [
-    "if" [ bins.tput "clear" ]
-    "backtick" "-in" "LINES" [ bins.tput "lines" ]
-    "backtick" "-in" "COLUMNS" [ bins.tput "cols" ]
+  clear-screen = depot.nix.writeExecline "clear-screen" { } [
+    "if"
+    [ bins.tput "clear" ]
+    "backtick"
+    "-in"
+    "LINES"
+    [ bins.tput "lines" ]
+    "backtick"
+    "-in"
+    "COLUMNS"
+    [ bins.tput "cols" ]
     "$@"
   ];
 
   print-nix-file = depot.nix.writeExecline "print-nix-file" { readNArgs = 1; } [
-    "pipeline" [ print-ast "${tree-sitter-nix}/parser" "tree_sitter_nix" "$1" ]
-    "pipeline" [ bins.sexp "print" ]
+    "pipeline"
+    [ print-ast "${tree-sitter-nix}/parser" "tree_sitter_nix" "$1" ]
+    "pipeline"
+    [ bins.sexp "print" ]
     clear-screen
-    "importas" "-ui" "lines" "LINES"
-    "backtick" "-in" "ls" [
+    "importas"
+    "-ui"
+    "lines"
+    "LINES"
+    "backtick"
+    "-in"
+    "ls"
+    [
       "pipeline"
-        # when you pull out bc to decrement an integer it’s time to switch to python lol
-        [ bins.printf "x=%s; --x\n" "$lines" ]
-        bins.bc
+      # when you pull out bc to decrement an integer it’s time to switch to python lol
+      [ bins.printf "x=%s; --x\n" "$lines" ]
+      bins.bc
     ]
-    "importas" "-ui" "l" "ls"
-    bins.head "-n\${l}"
+    "importas"
+    "-ui"
+    "l"
+    "ls"
+    bins.head
+    "-n\${l}"
   ];
 
   print-nix-file-on-update = depot.nix.writeExecline "print-nix-file-on-update" { readNArgs = 1; } [
-    "if" [ print-nix-file "$1" ]
-    "pipeline" [ watch-file-modified "$1" ]
-    "forstdin" "-d" "" "file"
-    "importas" "file" "file"
-    print-nix-file "$file"
+    "if"
+    [ print-nix-file "$1" ]
+    "pipeline"
+    [ watch-file-modified "$1" ]
+    "forstdin"
+    "-d"
+    ""
+    "file"
+    "importas"
+    "file"
+    "file"
+    print-nix-file
+    "$file"
   ];
 
   # copied from nixpkgs
   buildTreeSitterGrammar =
-      {
-        # language name
-        language
-        # source for the language grammar
-      , source
-      }:
-
-      pkgs.stdenv.mkDerivation {
-
-        pname = "${language}-grammar";
-        inherit (pkgs.tree-sitter) version;
-
-        src = source;
-
-        buildInputs = [ pkgs.tree-sitter ];
-
-        dontUnpack = true;
-        configurePhase= ":";
-        buildPhase = ''
-          runHook preBuild
-          scanner_cc="$src/src/scanner.cc"
-          if [ ! -f "$scanner_cc" ]; then
-            scanner_cc=""
-          fi
-          $CXX -I$src/src/ -c $scanner_cc
-          $CC -I$src/src/ -shared -o parser -Os  scanner.o $src/src/parser.c -lstdc++
-          runHook postBuild
-        '';
-        installPhase = ''
-          runHook preInstall
-          mkdir $out
-          mv parser $out/
-          runHook postInstall
-        '';
-      };
-
-in depot.nix.readTree.drvTargets {
+    {
+      # language name
+      language
+      # source for the language grammar
+    , source
+    }:
+
+    pkgs.stdenv.mkDerivation {
+
+      pname = "${language}-grammar";
+      inherit (pkgs.tree-sitter) version;
+
+      src = source;
+
+      buildInputs = [ pkgs.tree-sitter ];
+
+      dontUnpack = true;
+      configurePhase = ":";
+      buildPhase = ''
+        runHook preBuild
+        scanner_cc="$src/src/scanner.cc"
+        if [ ! -f "$scanner_cc" ]; then
+          scanner_cc=""
+        fi
+        $CXX -I$src/src/ -c $scanner_cc
+        $CC -I$src/src/ -shared -o parser -Os  scanner.o $src/src/parser.c -lstdc++
+        runHook postBuild
+      '';
+      installPhase = ''
+        runHook preInstall
+        mkdir $out
+        mv parser $out/
+        runHook postInstall
+      '';
+    };
+
+in
+depot.nix.readTree.drvTargets {
   inherit
     print-ast
     tree-sitter-nix
diff --git a/users/Profpatsch/whatcd-resolver/Main.hs b/users/Profpatsch/whatcd-resolver/Main.hs
new file mode 100644
index 0000000000..21cd80cbf0
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import WhatcdResolver qualified
+
+main :: IO ()
+main = WhatcdResolver.main
diff --git a/users/Profpatsch/whatcd-resolver/README.md b/users/Profpatsch/whatcd-resolver/README.md
new file mode 100644
index 0000000000..d1902e546a
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/README.md
@@ -0,0 +1,21 @@
+# whatcd-resolver
+
+To run:
+
+```
+ninja run-services
+```
+
+in one terminal (starts the background tasks)
+
+```
+ninja run
+```
+
+to start the server. It runs on `9092`.
+
+You need to be in the `nix-shell` in `./..`.
+
+You need to set the `pass` key `internet/redacted/api-keys/whatcd-resolver` to an API key for RED.
+
+You need to have a transmission-rpc-daemon listening on port `9091` (no auth, try ssh port forwarding lol).
diff --git a/users/Profpatsch/whatcd-resolver/build.ninja b/users/Profpatsch/whatcd-resolver/build.ninja
new file mode 100644
index 0000000000..ff6ba8df04
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/build.ninja
@@ -0,0 +1,20 @@
+
+builddir = .ninja
+
+outdir = ./output
+
+rule run-services
+  command = s6-svscan ./services
+
+rule run
+  command = execlineb -c '$
+    importas -i DEPOT_ROOT DEPOT_ROOT $
+    importas -i PROFPATSCH_ROOT PROFPATSCH_ROOT cd $$PROFPATSCH_ROOT $
+    nix-run { $$DEPOT_ROOT -A users.Profpatsch.shortcuttable } cabal repl whatcd-resolver/ --repl-options "-e main" $
+    '
+
+build run-services: run-services
+  pool = console
+
+build run: run
+  pool = console
diff --git a/users/Profpatsch/whatcd-resolver/default.nix b/users/Profpatsch/whatcd-resolver/default.nix
new file mode 100644
index 0000000000..27468507ac
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/default.nix
@@ -0,0 +1,76 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  #   bins = depot.nix.getBins pkgs.sqlite ["sqlite3"];
+
+  whatcd-resolver = pkgs.haskellPackages.mkDerivation {
+    pname = "whatcd-resolver";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./whatcd-resolver.cabal
+      ./Main.hs
+      ./src/WhatcdResolver.hs
+      ./src/AppT.hs
+      ./src/JsonLd.hs
+      ./src/Optional.hs
+      ./src/Html.hs
+      ./src/Http.hs
+      ./src/Transmission.hs
+      ./src/Redacted.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-prelude
+      depot.users.Profpatsch.my-webstuff
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-json
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.pa-field-parser
+      pkgs.haskellPackages.pa-run-command
+      pkgs.haskellPackages.aeson-better-errors
+      pkgs.haskellPackages.blaze-html
+      pkgs.haskellPackages.hs-opentelemetry-sdk
+      pkgs.haskellPackages.http-conduit
+      pkgs.haskellPackages.http-types
+      pkgs.haskellPackages.ihp-hsx
+      pkgs.haskellPackages.monad-logger
+      pkgs.haskellPackages.resource-pool
+      pkgs.haskellPackages.postgresql-simple
+      pkgs.haskellPackages.tmp-postgres
+      pkgs.haskellPackages.unliftio
+      pkgs.haskellPackages.wai-extra
+      pkgs.haskellPackages.warp
+      pkgs.haskellPackages.punycode
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+  bins = depot.nix.getBins whatcd-resolver [ "whatcd-resolver" ];
+
+in
+
+depot.nix.writeExecline "whatcd-resolver-wrapped" { } [
+  "importas"
+  "-i"
+  "PATH"
+  "PATH"
+  "export"
+  "PATH"
+  # TODO: figure out how to automatically migrate to a new postgres version with tmp_postgres (dump?)
+  "${pkgs.postgresql_14}/bin:$${PATH}"
+  "export"
+  "WHATCD_RESOLVER_TOOLS"
+  (pkgs.linkFarm "whatcd-resolver-tools" [
+    {
+      name = "pg_format";
+      path = "${pkgs.pgformatter}/bin/pg_format";
+    }
+  ])
+  bins.whatcd-resolver
+]
+
diff --git a/users/Profpatsch/whatcd-resolver/notes.org b/users/Profpatsch/whatcd-resolver/notes.org
new file mode 100644
index 0000000000..24662c0f32
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/notes.org
@@ -0,0 +1,48 @@
+* The Glorious what.cdΒΉ Resolver
+
+  ΒΉ: At the time of writing, what.cd didn’t even exist anymore
+
+** Idea
+   
+   Stream your music (or media) from a private tracker transparently.
+   β€œSpotify for torrents”
+
+** Technical
+
+   You need to have a seedbox, which runs a server program.
+   The server manages queries, downloads torrents and requested files, and
+   provides http streams to the downloaded files (while caching them for
+   seeding).
+
+   Clients then use the API to search for music (e.g. query for artists or
+   tracks) and get back the promise of a stream to the resolved file (a bit how
+   resolvers in the Tomahawk Player work)
+
+*** The Server
+
+**** Resolving queries
+
+     ~resolve :: Query -> IO Identifiers~
+
+     A query is a search input for content (could be an artist or a movie name
+     or something)
+
+     There have to be multiple providers, depending on the site used
+     (e.g. one for Gazelle trackers, one for Piratebay) and some intermediate
+     structure (e.g. for going through Musicbrainz first).
+
+     Output is a unique identifier for a fetchable resource; this could be a
+     link to a torrent combined with a file/directory in said torrent.
+
+**** Fetching Identifiers
+
+     ~fetch :: Identifier -> IO (Promise Stream)~
+
+     Takes an Identifier (which should provide all information on how to grab
+     the media file and returns a stream to the media file once it’s ready.
+     
+     For torrents, this probably consists of telling the torrent
+     library/application to fetch a certain torrent and start downloading the
+     required files in it. The torrent fetcher would also need to do seeding and
+     space management, since one usually has to keep a ratio and hard drive
+     space is not unlimited.
diff --git a/users/Profpatsch/whatcd-resolver/server-notes.org b/users/Profpatsch/whatcd-resolver/server-notes.org
new file mode 100644
index 0000000000..cb990aba3d
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/server-notes.org
@@ -0,0 +1,2 @@
+* whatcd-resolver-server
+
diff --git a/users/Profpatsch/whatcd-resolver/services/.gitignore b/users/Profpatsch/whatcd-resolver/services/.gitignore
new file mode 100644
index 0000000000..5cdb254e8c
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/services/.gitignore
@@ -0,0 +1,3 @@
+/.s6-svscan/
+/**/event/
+/**/supervise/
diff --git a/users/Profpatsch/whatcd-resolver/services/jaeger/run b/users/Profpatsch/whatcd-resolver/services/jaeger/run
new file mode 100755
index 0000000000..41332f8bb6
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/services/jaeger/run
@@ -0,0 +1,3 @@
+#!/usr/bin/env execlineb
+importas -i DEPOT_ROOT DEPOT_ROOT
+nix-run { $DEPOT_ROOT -A users.Profpatsch.jaeger -kK --builders '' }
diff --git a/users/Profpatsch/whatcd-resolver/services/reverse-proxy/run b/users/Profpatsch/whatcd-resolver/services/reverse-proxy/run
new file mode 100755
index 0000000000..7081b35f5a
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/services/reverse-proxy/run
@@ -0,0 +1,2 @@
+#!/usr/bin/env execlineb
+caddy reverse-proxy --from :9092 --to :9093
diff --git a/users/Profpatsch/whatcd-resolver/src/AppT.hs b/users/Profpatsch/whatcd-resolver/src/AppT.hs
new file mode 100644
index 0000000000..7afd430745
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/AppT.hs
@@ -0,0 +1,151 @@
+{-# LANGUAGE DeriveAnyClass #-}
+
+module AppT where
+
+import Control.Monad.Logger qualified as Logger
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Error.Tree
+import Data.HashMap.Strict (HashMap)
+import Data.HashMap.Strict qualified as HashMap
+import Data.Pool (Pool)
+import Data.Text qualified as Text
+import Database.PostgreSQL.Simple qualified as Postgres
+import GHC.Stack qualified
+import Label
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import OpenTelemetry.Trace.Core qualified as Otel hiding (inSpan, inSpan')
+import OpenTelemetry.Trace.Monad qualified as Otel
+import PossehlAnalyticsPrelude
+import Postgres.MonadPostgres
+import System.IO qualified as IO
+import Tool (Tool)
+import UnliftIO
+import Prelude hiding (span)
+
+data Context = Context
+  { config :: Label "logDatabaseQueries" DebugLogDatabaseQueries,
+    tracer :: Otel.Tracer,
+    pgFormat :: Tool,
+    pgConnPool :: Pool Postgres.Connection,
+    transmissionSessionId :: MVar ByteString
+  }
+
+newtype AppT m a = AppT {unAppT :: ReaderT Context m a}
+  deriving newtype (Functor, Applicative, Monad, MonadIO, MonadUnliftIO, MonadThrow)
+
+data AppException = AppException Text
+  deriving stock (Show)
+  deriving anyclass (Exception)
+
+-- *  Logging & Opentelemetry
+
+instance (MonadIO m) => MonadLogger (AppT m) where
+  monadLoggerLog loc src lvl msg = liftIO $ Logger.defaultOutput IO.stderr loc src lvl (Logger.toLogStr msg)
+
+instance (Monad m) => Otel.MonadTracer (AppT m) where
+  getTracer = AppT $ asks (.tracer)
+
+class (MonadUnliftIO m, Otel.MonadTracer m) => MonadOtel m
+
+instance (MonadUnliftIO m) => MonadOtel (AppT m)
+
+instance (MonadOtel m) => MonadOtel (Transaction m)
+
+inSpan :: (MonadOtel m) => Text -> m a -> m a
+inSpan name = Otel.inSpan name Otel.defaultSpanArguments
+
+inSpan' :: (MonadOtel m) => Text -> (Otel.Span -> m a) -> m a
+inSpan' name = Otel.inSpan' name Otel.defaultSpanArguments
+
+-- | Add the attribute to the span, prefixing it with the `_` namespace (to easier distinguish our application’s tags from standard tags)
+addAttribute :: (MonadIO m, Otel.ToAttribute a) => Otel.Span -> Text -> a -> m ()
+addAttribute span key a = Otel.addAttribute span ("_." <> key) a
+
+-- | Add the attributes to the span, prefixing each key with the `_` namespace (to easier distinguish our application’s tags from standard tags)
+addAttributes :: (MonadIO m) => Otel.Span -> HashMap Text Otel.Attribute -> m ()
+addAttributes span attrs = Otel.addAttributes span $ attrs & HashMap.mapKeys ("_." <>)
+
+appThrowTreeNewSpan :: (MonadThrow m, MonadOtel m) => Text -> ErrorTree -> m a
+appThrowTreeNewSpan spanName exc = inSpan' spanName $ \span -> do
+  let msg = prettyErrorTree exc
+  recordException
+    span
+    ( T2
+        (label @"type_" "AppException")
+        (label @"message" msg)
+    )
+  throwM $ AppException msg
+
+appThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> ErrorTree -> m a
+appThrowTree span exc = do
+  let msg = prettyErrorTree exc
+  recordException
+    span
+    ( T2
+        (label @"type_" "AppException")
+        (label @"message" msg)
+    )
+  throwM $ AppException msg
+
+orAppThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> Either ErrorTree a -> m a
+orAppThrowTree span = \case
+  Left err -> appThrowTree span err
+  Right a -> pure a
+
+assertM :: (MonadThrow f, MonadIO f) => Otel.Span -> (t -> Either ErrorTree a) -> t -> f a
+assertM span f v = case f v of
+  Right a -> pure a
+  Left err -> appThrowTree span err
+
+assertMNewSpan :: (MonadThrow f, MonadOtel f) => Text -> (t -> Either ErrorTree a) -> t -> f a
+assertMNewSpan spanName f v = case f v of
+  Right a -> pure a
+  Left err -> appThrowTreeNewSpan spanName err
+
+-- | A specialized variant of @addEvent@ that records attributes conforming to
+-- the OpenTelemetry specification's
+-- <https://github.com/open-telemetry/opentelemetry-specification/blob/49c2f56f3c0468ceb2b69518bcadadd96e0a5a8b/specification/trace/semantic_conventions/exceptions.md semantic conventions>
+--
+-- @since 0.0.1.0
+recordException ::
+  ( MonadIO m,
+    HasField "message" r Text,
+    HasField "type_" r Text
+  ) =>
+  Otel.Span ->
+  r ->
+  m ()
+recordException span dat = liftIO $ do
+  callStack <- GHC.Stack.whoCreated dat.message
+  newEventTimestamp <- Just <$> Otel.getTimestamp
+  Otel.addEvent span $
+    Otel.NewEvent
+      { newEventName = "exception",
+        newEventAttributes =
+          HashMap.fromList
+            [ ("exception.type", Otel.toAttribute @Text dat.type_),
+              ("exception.message", Otel.toAttribute @Text dat.message),
+              ("exception.stacktrace", Otel.toAttribute @Text $ Text.unlines $ map stringToText callStack)
+            ],
+        ..
+      }
+
+-- * Postgres
+
+instance (MonadThrow m, MonadUnliftIO m) => MonadPostgres (AppT m) where
+  execute = executeImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  executeMany = executeManyImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  executeManyReturningWith = executeManyReturningWithImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  queryWith = queryWithImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  queryWith_ = queryWithImpl_ (AppT ask)
+
+  foldRowsWithAcc = foldRowsWithAccImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  runTransaction = runPGTransaction
+
+runPGTransaction :: (MonadUnliftIO m) => Transaction (AppT m) a -> AppT m a
+runPGTransaction (Transaction transaction) = do
+  pool <- AppT ask <&> (.pgConnPool)
+  withRunInIO $ \unliftIO ->
+    withPGTransaction pool $ \conn -> do
+      unliftIO $ runReaderT transaction conn
diff --git a/users/Profpatsch/whatcd-resolver/src/Html.hs b/users/Profpatsch/whatcd-resolver/src/Html.hs
new file mode 100644
index 0000000000..49b87b23dc
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Html.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Html where
+
+import Data.Aeson qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Map.Strict qualified as Map
+import IHP.HSX.QQ (hsx)
+import PossehlAnalyticsPrelude
+import Text.Blaze.Html (Html)
+import Text.Blaze.Html5 qualified as Html
+import Prelude hiding (span)
+
+-- | Render an arbitrary json value to HTML in a more-or-less reasonable fashion.
+mkVal :: Json.Value -> Html
+mkVal = \case
+  Json.Number n -> Html.toHtml @Text $ showToText n
+  Json.String s -> Html.toHtml @Text s
+  Json.Bool True -> [hsx|<em>true</em>|]
+  Json.Bool False -> [hsx|<em>false</em>|]
+  Json.Null -> [hsx|<em>null</em>|]
+  Json.Array arr -> toOrderedList mkVal arr
+  Json.Object obj ->
+    obj
+      & KeyMap.toMapText
+      & toDefinitionList (Html.toHtml @Text) mkVal
+
+toOrderedList :: (Foldable t1) => (t2 -> Html) -> t1 t2 -> Html
+toOrderedList mkValFn arr =
+  arr
+    & foldMap (\el -> Html.li $ mkValFn el)
+    & Html.ol
+
+toUnorderedList :: (Foldable t1) => (t2 -> Html) -> t1 t2 -> Html
+toUnorderedList mkValFn arr =
+  arr
+    & foldMap (\el -> Html.li $ mkValFn el)
+    & Html.ul
+
+-- | Render a definition list from a Map
+toDefinitionList :: (Text -> Html) -> (t -> Html) -> Map Text t -> Html
+toDefinitionList mkKeyFn mkValFn obj =
+  obj
+    & Map.toList
+    & foldMap (\(k, v) -> Html.dt (mkKeyFn k) <> Html.dd (mkValFn v))
+    & Html.dl
+
+-- | Render a table-like structure of json values as an HTML table.
+toTable :: [[(Text, Json.Value)]] -> Html
+toTable xs =
+  case xs & nonEmpty of
+    Nothing ->
+      [hsx|<p>No results.</p>|]
+    Just xs' -> do
+      let headers = xs' & NonEmpty.head <&> fst <&> (\h -> [hsx|<th>{h}</th>|]) & mconcat
+      let vals = xs' & foldMap (Html.tr . foldMap (Html.td . mkVal . snd))
+      [hsx|
+              <table class="table">
+                <thead>
+                  <tr>
+                  {headers}
+                  </tr>
+                </thead>
+                <tbody>
+                  {vals}
+                </tbody>
+              </table>
+          |]
diff --git a/users/Profpatsch/whatcd-resolver/src/Http.hs b/users/Profpatsch/whatcd-resolver/src/Http.hs
new file mode 100644
index 0000000000..4fdbb306ad
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Http.hs
@@ -0,0 +1,129 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Http
+  ( doRequestJson,
+    RequestOptions (..),
+    mkRequestOptions,
+    setRequestMethod,
+    setRequestBodyLBS,
+    setRequestHeader,
+    getResponseStatus,
+    getResponseHeader,
+    getResponseBody,
+  )
+where
+
+import AppT
+import Data.CaseInsensitive (CI (original))
+import Data.Char qualified as Char
+import Data.Int (Int64)
+import Data.List qualified as List
+import Data.Text qualified as Text
+import Data.Text.Lazy qualified as Lazy.Text
+import Data.Text.Punycode qualified as Punycode
+import Json.Enc qualified as Enc
+import MyPrelude
+import Network.HTTP.Client
+import Network.HTTP.Simple
+import OpenTelemetry.Attributes qualified as Otel
+import Optional
+import Prelude hiding (span)
+
+data RequestOptions = RequestOptions
+  { method :: ByteString,
+    host :: Text,
+    port :: Optional Int,
+    path :: Optional [Text],
+    headers :: Optional [Header],
+    usePlainHttp :: Optional Bool
+  }
+
+mkRequestOptions :: (HasField "method" r ByteString, HasField "host" r Text) => r -> RequestOptions
+mkRequestOptions opts =
+  RequestOptions
+    { method = opts.method,
+      port = defaults,
+      host = opts.host,
+      path = defaults,
+      headers = defaults,
+      usePlainHttp = defaults
+    }
+
+doRequestJson ::
+  (MonadOtel m) =>
+  RequestOptions ->
+  Enc.Enc ->
+  m (Response ByteString)
+doRequestJson opts val = inSpan' "HTTP Request (JSON)" $ \span -> do
+  let x = requestToXhCommandLine opts val
+  let attrs = [100, 200 .. fromIntegral @Int @Int64 (x & Text.length)]
+  for_ attrs $ \n -> do
+    addAttribute span [fmt|request.xh.{n}|] (Lazy.Text.repeat 'x' & Lazy.Text.take n & toStrict & Otel.TextAttribute)
+  addAttribute span "request.xh" (requestToXhCommandLine opts val)
+  defaultRequest {secure = not (opts & optsUsePlainHttp)}
+    & setRequestHost (opts & optsHost)
+    & setRequestPort (opts & optsPort)
+    -- TODO: is this automatically escaped by the library?
+    & setRequestPath (opts & optsPath)
+    & setRequestHeaders (opts & optsHeaders)
+    & setRequestMethod opts.method
+    & setRequestBodyLBS (Enc.encToBytesUtf8Lazy val)
+    & httpBS
+
+optsHost :: RequestOptions -> ByteString
+optsHost opts =
+  if opts.host & Text.isAscii
+    then opts.host & textToBytesUtf8
+    else opts.host & Punycode.encode
+
+optsUsePlainHttp :: RequestOptions -> Bool
+optsUsePlainHttp opts = opts.usePlainHttp.withDefault False
+
+optsPort :: RequestOptions -> Int
+optsPort opts = opts.port.withDefault (if opts & optsUsePlainHttp then 80 else 443)
+
+optsPath :: RequestOptions -> ByteString
+optsPath opts = opts.path.withDefault [] & Text.intercalate "/" & ("/" <>) & textToBytesUtf8
+
+optsHeaders :: RequestOptions -> [Header]
+optsHeaders opts = opts.headers.withDefault []
+
+-- | Create a string that can be pasted on the command line to invoke the same HTTP request via the `xh` tool (curl but nicer syntax)
+requestToXhCommandLine :: RequestOptions -> Enc.Enc -> Text
+requestToXhCommandLine opts val = do
+  let protocol = if opts & optsUsePlainHttp then "http" :: Text else "https"
+  let url = [fmt|{protocol}://{opts & optsHost}:{opts & optsPort}{opts & optsPath}|]
+  let headers = opts & optsHeaders <&> \(hdr, v) -> hdr.original <> ":" <> v
+
+  prettyArgsForBash $
+    mconcat
+      [ ["xh", url],
+        headers <&> bytesToTextUtf8Lenient,
+        ["--raw"],
+        [val & Enc.encToBytesUtf8 & bytesToTextUtf8Lenient]
+      ]
+
+-- | Pretty print a command line in a way that can be copied to bash.
+prettyArgsForBash :: [Text] -> Text
+prettyArgsForBash = Text.intercalate " " . map simpleBashEscape
+
+-- | Simple escaping for bash words. If they contain anything that’s not ascii chars
+-- and a bunch of often-used special characters, put the word in single quotes.
+simpleBashEscape :: Text -> Text
+simpleBashEscape t = do
+  case Text.find (not . isSimple) t of
+    Just _ -> escapeSingleQuote t
+    Nothing -> t
+  where
+    -- any word that is just ascii characters is simple (no spaces or control characters)
+    -- or contains a few often-used characters like - or .
+    isSimple c =
+      Char.isAsciiLower c
+        || Char.isAsciiUpper c
+        || Char.isDigit c
+        -- These are benign, bash will not interpret them as special characters.
+        || List.elem c ['-', '.', ':', '/']
+    -- Put the word in single quotes
+    -- If there is a single quote in the word,
+    -- close the single quoted word, add a single quote, open the word again
+    escapeSingleQuote t' = "'" <> Text.replace "'" "'\\''" t' <> "'"
diff --git a/users/Profpatsch/whatcd-resolver/src/JsonLd.hs b/users/Profpatsch/whatcd-resolver/src/JsonLd.hs
new file mode 100644
index 0000000000..16b1ab991b
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/JsonLd.hs
@@ -0,0 +1,138 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module JsonLd where
+
+import AppT
+import Control.Monad.Reader
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.ByteString.Builder qualified as Builder
+import Data.List qualified as List
+import Data.Map.Strict qualified as Map
+import Data.Set (Set)
+import Data.Set qualified as Set
+import Html qualified
+import IHP.HSX.QQ (hsx)
+import Json qualified
+import Label
+import MyPrelude
+import Network.HTTP.Client.Conduit qualified as Http
+import Network.HTTP.Simple qualified as Http
+import Network.HTTP.Types.URI qualified as Url
+import Network.URI (URI)
+import Optional
+import Redacted
+import Text.Blaze.Html (Html)
+import Prelude hiding (span)
+
+-- | A recursive `json+ld` structure.
+data Jsonld
+  = JsonldObject JsonldObject
+  | JsonldAnonymousObject JsonldAnonymousObject
+  | JsonldArray [Jsonld]
+  | JsonldField Json.Value
+  deriving stock (Show, Eq)
+
+-- | A json+ld object, that is something which can be further expanded by following the URL in its `id_` field.
+data JsonldObject = JsonldObject'
+  { -- | `@type` field; currently just the plain value without taking into account the json+ld context
+    type_ :: Set Text,
+    -- | `@id` field, usually a link to follow for expanding the object to its full glory
+    id_ :: Text,
+    -- | any fields of this object that remote deemed important enough to already pre-emptively include in the object; to get all fields resolve the URL in `id_`.
+    previewFields :: Map Text Jsonld
+  }
+  deriving stock (Show, Eq)
+
+-- | A json+ld object that cannot be inspected further by resolving its ID
+data JsonldAnonymousObject = JsonldAnonymousObject'
+  { -- | `@type` field; currently just the plain value without taking into account the json+ld context
+    type_ :: Set Text,
+    -- | fields of this anonymous object
+    fields :: Map Text Jsonld
+  }
+  deriving stock (Show, Eq)
+
+jsonldParser :: (Monad m) => Json.ParseT err m Jsonld
+jsonldParser =
+  Json.asValue >>= \cur -> do
+    if
+      | Json.Object _ <- cur -> do
+          type_ <-
+            Json.keyMay "@type" (Json.asArraySet Json.asText Json.<|> (Set.singleton <$> Json.asText))
+              <&> fromMaybe Set.empty
+          idMay <- Json.keyMay "@id" $ Json.asText
+          fields <-
+            Json.asObjectMap jsonldParser
+              <&> Map.delete "@type"
+              <&> Map.delete "@id"
+
+          if
+            | Just id_ <- idMay -> do
+                pure $ JsonldObject $ JsonldObject' {previewFields = fields, ..}
+            | otherwise -> pure $ JsonldAnonymousObject $ JsonldAnonymousObject' {..}
+      | Json.Array _ <- cur -> do
+          JsonldArray <$> Json.eachInArray jsonldParser
+      | otherwise -> pure $ JsonldField cur
+
+renderJsonld :: Jsonld -> Html
+renderJsonld = \case
+  JsonldObject obj -> renderObject obj (Just obj.id_) obj.previewFields
+  JsonldAnonymousObject obj -> renderObject obj Nothing obj.fields
+  JsonldArray arr ->
+    Html.toOrderedList renderJsonld arr
+  JsonldField f -> Html.mkVal f
+  where
+    renderObject obj mId_ fields = do
+      let id_ =
+            mId_ <&> \i ->
+              [hsx|
+                  <dt>Url</dt>
+                  <dd><a href={i}>{i}</a></dd>
+                  |]
+          getMoreButton =
+            mId_ <&> \i ->
+              [hsx|
+              <div>
+                <button
+                  hx-get={snippetHref i}
+                  hx-target="closest dl"
+                  hx-swap="outerHTML"
+                >more fields …</button>
+              </div>
+            |]
+      [hsx|
+      <dl>
+        <dt>Type</dt>
+        <dd>{obj.type_ & toList & schemaTypes}</dd>
+        {id_}
+        <dt>Fields</dt>
+        <dd>
+          {fields & Html.toDefinitionList schemaType renderJsonld}
+          {getMoreButton}
+        </dd>
+      </dl>
+    |]
+    snippetHref target =
+      Builder.toLazyByteString $
+        "/snips/jsonld/render"
+          <> Url.renderQueryBuilder True [("target", Just (textToBytesUtf8 target))]
+
+    schemaTypes xs =
+      xs
+        <&> schemaType
+        & List.intersperse ", "
+        & mconcat
+    schemaType t =
+      let href :: Text = [fmt|https://schema.org/{t}|] in [hsx|<a href={href} target="_blank">{t}</a>|]
+
+httpGetJsonLd :: (MonadThrow m, MonadOtel m) => (URI, Http.Request) -> m Jsonld
+httpGetJsonLd (uri, req) = inSpan' "Fetch json+ld" $ \span -> do
+  addAttribute span "json+ld.targetUrl" (uri & showToText)
+  httpJson
+    (mkOptional (label @"contentType" "application/ld+json"))
+    jsonldParser
+    ( req
+        & Http.setRequestMethod "GET"
+        & Http.setRequestHeader "Accept" ["application/ld+json"]
+    )
diff --git a/users/Profpatsch/whatcd-resolver/src/Optional.hs b/users/Profpatsch/whatcd-resolver/src/Optional.hs
new file mode 100644
index 0000000000..9791c84970
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Optional.hs
@@ -0,0 +1,18 @@
+module Optional where
+
+import GHC.Records (getField)
+import MyPrelude
+
+newtype Optional a = OptionalInternal (Maybe a)
+  deriving newtype (Functor)
+
+mkOptional :: a -> Optional a
+mkOptional defaultValue = OptionalInternal $ Just defaultValue
+
+defaults :: Optional a
+defaults = OptionalInternal Nothing
+
+instance HasField "withDefault" (Optional a) (a -> a) where
+  getField (OptionalInternal m) defaultValue = case m of
+    Nothing -> defaultValue
+    Just a -> a
diff --git a/users/Profpatsch/whatcd-resolver/src/Redacted.hs b/users/Profpatsch/whatcd-resolver/src/Redacted.hs
new file mode 100644
index 0000000000..4369c18408
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Redacted.hs
@@ -0,0 +1,537 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Redacted where
+
+import AppT
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.Error.Tree
+import Data.List qualified as List
+import Database.PostgreSQL.Simple (Binary (Binary), Only (..))
+import Database.PostgreSQL.Simple.SqlQQ (sql)
+import Database.PostgreSQL.Simple.Types (PGArray (PGArray))
+import FieldParser qualified as Field
+import Json qualified
+import Label
+import MyPrelude
+import Network.HTTP.Client.Conduit qualified as Http
+import Network.HTTP.Simple qualified as Http
+import Network.HTTP.Types
+import Network.Wai.Parse qualified as Wai
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import Optional
+import Postgres.Decoder qualified as Dec
+import Postgres.MonadPostgres
+import Pretty
+import RunCommand (runCommandExpect0)
+import Prelude hiding (span)
+
+redactedSearch ::
+  (MonadLogger m, MonadThrow m, MonadOtel m) =>
+  [(ByteString, ByteString)] ->
+  Json.Parse ErrorTree a ->
+  m a
+redactedSearch advanced parser =
+  inSpan "Redacted API Search" $
+    redactedApiRequestJson
+      ( T2
+          (label @"action" "browse")
+          (label @"actionArgs" ((advanced <&> second Just)))
+      )
+      parser
+
+redactedGetTorrentFile ::
+  ( MonadLogger m,
+    MonadThrow m,
+    HasField "torrentId" dat Int,
+    MonadOtel m
+  ) =>
+  dat ->
+  m ByteString
+redactedGetTorrentFile dat = inSpan' "Redacted Get Torrent File" $ \span -> do
+  req <-
+    mkRedactedApiRequest
+      ( T2
+          (label @"action" "download")
+          ( label @"actionArgs"
+              [ ("id", Just (dat.torrentId & showToText @Int & textToBytesUtf8))
+              -- try using tokens as long as we have them (TODO: what if there’s no tokens left?
+              -- ANSWER: it breaks:
+              -- responseBody = "{\"status\":\"failure\",\"error\":\"You do not have any freeleech tokens left. Please use the regular DL link.\"}",
+              -- ("usetoken", Just "1")
+              ]
+          )
+      )
+  httpTorrent span req
+
+-- fix
+--   ( \io -> do
+--       logInfo "delay"
+--       liftIO $ threadDelay 10_000_000
+--       io
+--   )
+
+exampleSearch :: (MonadThrow m, MonadLogger m, MonadPostgres m, MonadOtel m) => m (Transaction m ())
+exampleSearch = do
+  t1 <-
+    redactedSearchAndInsert
+      [ ("searchstr", "cherish"),
+        ("artistname", "kirinji"),
+        -- ("year", "1982"),
+        -- ("format", "MP3"),
+        -- ("releasetype", "album"),
+        ("order_by", "year")
+      ]
+  t3 <-
+    redactedSearchAndInsert
+      [ ("searchstr", "mouss et hakim"),
+        ("artistname", "mouss et hakim"),
+        -- ("year", "1982"),
+        -- ("format", "MP3"),
+        -- ("releasetype", "album"),
+        ("order_by", "year")
+      ]
+  t2 <-
+    redactedSearchAndInsert
+      [ ("searchstr", "thriller"),
+        ("artistname", "michael jackson"),
+        -- ("year", "1982"),
+        -- ("format", "MP3"),
+        -- ("releasetype", "album"),
+        ("order_by", "year")
+      ]
+  pure (t1 >> t2 >> t3)
+
+-- | Do the search, return a transaction that inserts all results from all pages of the search.
+redactedSearchAndInsert ::
+  forall m.
+  ( MonadLogger m,
+    MonadPostgres m,
+    MonadThrow m,
+    MonadOtel m
+  ) =>
+  [(ByteString, ByteString)] ->
+  m (Transaction m ())
+redactedSearchAndInsert extraArguments = do
+  logInfo [fmt|Doing redacted search with arguments: {showPretty extraArguments}|]
+  -- The first search returns the amount of pages, so we use that to query all results piece by piece.
+  firstPage <- go Nothing
+  let remainingPages = firstPage.pages - 1
+  logInfo [fmt|Got the first page, found {remainingPages} more pages|]
+  let otherPagesNum = [(2 :: Natural) .. remainingPages]
+  otherPages <- traverse go (Just <$> otherPagesNum)
+  pure $
+    (firstPage : otherPages)
+      & concatMap (.tourGroups)
+      & \case
+        IsNonEmpty tgs -> tgs & insertTourGroupsAndTorrents
+        IsEmpty -> pure ()
+  where
+    go mpage =
+      redactedSearch
+        ( extraArguments
+            -- pass the page (for every search but the first one)
+            <> (mpage & ifExists (\page -> ("page", (page :: Natural) & showToText & textToBytesUtf8)))
+        )
+        ( do
+            status <- Json.key "status" Json.asText
+            when (status /= "success") $ do
+              Json.throwCustomError [fmt|Status was not "success", but {status}|]
+            Json.key "response" $ do
+              pages <-
+                Json.keyMay "pages" (Field.toJsonParser (Field.mapError singleError $ Field.jsonNumber >>> Field.boundedScientificIntegral @Int "not an Integer" >>> Field.integralToNatural))
+                  -- in case the field is missing, let’s assume there is only one page
+                  <&> fromMaybe 1
+              Json.key "results" $ do
+                tourGroups <-
+                  label @"tourGroups"
+                    <$> ( Json.eachInArray $ do
+                            groupId <- Json.keyLabel @"groupId" "groupId" (Json.asIntegral @_ @Int)
+                            groupName <- Json.keyLabel @"groupName" "groupName" Json.asText
+                            fullJsonResult <-
+                              label @"fullJsonResult"
+                                <$> ( Json.asObject
+                                        -- remove torrents cause they are inserted separately below
+                                        <&> KeyMap.filterWithKey (\k _ -> k /= "torrents")
+                                        <&> Json.Object
+                                    )
+                            let tourGroup = T3 groupId groupName fullJsonResult
+                            torrents <- Json.keyLabel @"torrents" "torrents" $
+                              Json.eachInArray $ do
+                                torrentId <- Json.keyLabel @"torrentId" "torrentId" (Json.asIntegral @_ @Int)
+                                fullJsonResultT <- label @"fullJsonResult" <$> Json.asValue
+                                pure $ T2 torrentId fullJsonResultT
+                            pure (T2 (label @"tourGroup" tourGroup) torrents)
+                        )
+                pure
+                  ( T2
+                      (label @"pages" pages)
+                      tourGroups
+                  )
+        )
+    insertTourGroupsAndTorrents ::
+      NonEmpty
+        ( T2
+            "tourGroup"
+            (T3 "groupId" Int "groupName" Text "fullJsonResult" Json.Value)
+            "torrents"
+            [T2 "torrentId" Int "fullJsonResult" Json.Value]
+        ) ->
+      Transaction m ()
+    insertTourGroupsAndTorrents dat = do
+      let tourGroups = dat <&> (.tourGroup)
+      let torrents = dat <&> (.torrents)
+      insertTourGroups tourGroups
+        >>= ( \res ->
+                insertTorrents $
+                  zipT2 $
+                    T2
+                      (label @"torrentGroupIdPg" $ res <&> (.tourGroupIdPg))
+                      (label @"torrents" (torrents & toList))
+            )
+    insertTourGroups ::
+      NonEmpty
+        ( T3
+            "groupId"
+            Int
+            "groupName"
+            Text
+            "fullJsonResult"
+            Json.Value
+        ) ->
+      Transaction m [Label "tourGroupIdPg" Int]
+    insertTourGroups dats = do
+      let groupNames =
+            dats <&> \dat -> [fmt|{dat.groupId}: {dat.groupName}|]
+      logInfo [fmt|Inserting tour groups for {showPretty groupNames}|]
+      _ <-
+        execute
+          [fmt|
+                  DELETE FROM redacted.torrent_groups
+                  WHERE group_id = ANY (?::integer[])
+              |]
+          (Only $ (dats <&> (.groupId) & toList & PGArray :: PGArray Int))
+      executeManyReturningWith
+        [fmt|
+              INSERT INTO redacted.torrent_groups (
+                group_id, group_name, full_json_result
+              ) VALUES
+              ( ?, ? , ? )
+              ON CONFLICT (group_id) DO UPDATE SET
+                group_id = excluded.group_id,
+                group_name = excluded.group_name,
+                full_json_result = excluded.full_json_result
+              RETURNING (id)
+            |]
+        ( dats <&> \dat ->
+            ( dat.groupId,
+              dat.groupName,
+              dat.fullJsonResult
+            )
+        )
+        (label @"tourGroupIdPg" <$> Dec.fromField @Int)
+
+    insertTorrents ::
+      [ T2
+          "torrentGroupIdPg"
+          Int
+          "torrents"
+          [T2 "torrentId" Int "fullJsonResult" Json.Value]
+      ] ->
+      Transaction m ()
+    insertTorrents dats = do
+      _ <-
+        execute
+          [sql|
+            DELETE FROM redacted.torrents_json
+            WHERE torrent_id = ANY (?::integer[])
+          |]
+          ( Only $
+              PGArray
+                [ torrent.torrentId
+                  | dat <- dats,
+                    torrent <- dat.torrents
+                ]
+          )
+
+      execute
+        [sql|
+          INSERT INTO redacted.torrents_json
+            ( torrent_group
+            , torrent_id
+            , full_json_result)
+          SELECT *
+          FROM UNNEST(
+              ?::integer[]
+            , ?::integer[]
+            , ?::jsonb[]
+          ) AS inputs(
+              torrent_group
+            , torrent_id
+            , full_json_result)
+          |]
+        ( [ ( dat.torrentGroupIdPg :: Int,
+              group.torrentId :: Int,
+              group.fullJsonResult :: Json.Value
+            )
+            | dat <- dats,
+              group <- dat.torrents
+          ]
+            & unzip3PGArray
+        )
+      pure ()
+
+unzip3PGArray :: [(a1, a2, a3)] -> (PGArray a1, PGArray a2, PGArray a3)
+unzip3PGArray xs = xs & unzip3 & \(a, b, c) -> (PGArray a, PGArray b, PGArray c)
+
+redactedGetTorrentFileAndInsert ::
+  ( HasField "torrentId" r Int,
+    MonadPostgres m,
+    MonadThrow m,
+    MonadLogger m,
+    MonadOtel m
+  ) =>
+  r ->
+  Transaction m (Label "torrentFile" ByteString)
+redactedGetTorrentFileAndInsert dat = inSpan' "Redacted Get Torrent File and Insert" $ \span -> do
+  bytes <- redactedGetTorrentFile dat
+  execute
+    [sql|
+    UPDATE redacted.torrents_json
+    SET torrent_file = ?::bytea
+    WHERE torrent_id = ?::integer
+  |]
+    ( (Binary bytes :: Binary ByteString),
+      dat.torrentId
+    )
+    >>= assertOneUpdated span "redactedGetTorrentFileAndInsert"
+    >>= \() -> pure (label @"torrentFile" bytes)
+
+getTorrentFileById ::
+  ( MonadPostgres m,
+    HasField "torrentId" r Int,
+    MonadThrow m
+  ) =>
+  r ->
+  Transaction m (Maybe (Label "torrentFile" ByteString))
+getTorrentFileById dat = do
+  queryWith
+    [sql|
+    SELECT torrent_file
+    FROM redacted.torrents
+    WHERE torrent_id = ?::integer
+  |]
+    (Only $ (dat.torrentId :: Int))
+    (fmap @Maybe (label @"torrentFile") <$> Dec.byteaMay)
+    >>= ensureSingleRow
+
+updateTransmissionTorrentHashById ::
+  ( MonadPostgres m,
+    HasField "torrentId" r Int,
+    HasField "torrentHash" r Text
+  ) =>
+  r ->
+  Transaction m (Label "numberOfRowsAffected" Natural)
+updateTransmissionTorrentHashById dat = do
+  execute
+    [sql|
+    UPDATE redacted.torrents_json
+    SET transmission_torrent_hash = ?::text
+    WHERE torrent_id = ?::integer
+    |]
+    ( dat.torrentHash :: Text,
+      dat.torrentId :: Int
+    )
+
+assertOneUpdated ::
+  (HasField "numberOfRowsAffected" r Natural, MonadThrow m, MonadIO m) =>
+  Otel.Span ->
+  Text ->
+  r ->
+  m ()
+assertOneUpdated span name x = case x.numberOfRowsAffected of
+  1 -> pure ()
+  n -> appThrowTree span ([fmt|{name :: Text}: Expected to update exactly one row, but updated {n :: Natural} row(s)|])
+
+data TorrentData transmissionInfo = TorrentData
+  { groupId :: Int,
+    torrentId :: Int,
+    seedingWeight :: Int,
+    torrentJson :: Json.Value,
+    torrentGroupJson :: T3 "artist" Text "groupName" Text "groupYear" Int,
+    torrentStatus :: TorrentStatus transmissionInfo
+  }
+
+data TorrentStatus transmissionInfo
+  = NoTorrentFileYet
+  | NotInTransmissionYet
+  | InTransmission (T2 "torrentHash" Text "transmissionInfo" transmissionInfo)
+
+getTorrentById :: (MonadPostgres m, HasField "torrentId" r Int, MonadThrow m) => r -> Transaction m Json.Value
+getTorrentById dat = do
+  queryWith
+    [sql|
+    SELECT full_json_result FROM redacted.torrents
+    WHERE torrent_id = ?::integer
+  |]
+    (getLabel @"torrentId" dat)
+    (Dec.json Json.asValue)
+    >>= ensureSingleRow
+
+-- | Find the best torrent for each torrent group (based on the seeding_weight)
+getBestTorrents :: (MonadPostgres m) => Transaction m [TorrentData ()]
+getBestTorrents = do
+  queryWith
+    [sql|
+      SELECT * FROM (
+        SELECT DISTINCT ON (group_id)
+          tg.group_id,
+          t.torrent_id,
+          seeding_weight,
+          t.full_json_result AS torrent_json,
+          tg.full_json_result AS torrent_group_json,
+          t.torrent_file IS NOT NULL,
+          t.transmission_torrent_hash
+        FROM redacted.torrents t
+        JOIN redacted.torrent_groups tg ON tg.id = t.torrent_group
+        ORDER BY group_id, seeding_weight DESC
+      ) as _
+      ORDER BY seeding_weight DESC
+    |]
+    ()
+    ( do
+        groupId <- Dec.fromField @Int
+        torrentId <- Dec.fromField @Int
+        seedingWeight <- Dec.fromField @Int
+        torrentJson <- Dec.json Json.asValue
+        torrentGroupJson <-
+          ( Dec.json $ do
+              artist <- Json.keyLabel @"artist" "artist" Json.asText
+              groupName <- Json.keyLabel @"groupName" "groupName" Json.asText
+              groupYear <- Json.keyLabel @"groupYear" "groupYear" (Json.asIntegral @_ @Int)
+              pure $ T3 artist groupName groupYear
+            )
+        hasTorrentFile <- Dec.fromField @Bool
+        transmissionTorrentHash <-
+          Dec.fromField @(Maybe Text)
+        pure $
+          TorrentData
+            { torrentStatus =
+                if
+                  | not hasTorrentFile -> NoTorrentFileYet
+                  | Nothing <- transmissionTorrentHash -> NotInTransmissionYet
+                  | Just hash <- transmissionTorrentHash ->
+                      InTransmission $
+                        T2 (label @"torrentHash" hash) (label @"transmissionInfo" ()),
+              ..
+            }
+    )
+
+-- | Do a request to the redacted API. If you know what that is, you know how to find the API docs.
+mkRedactedApiRequest ::
+  ( MonadThrow m,
+    MonadIO m,
+    MonadLogger m,
+    HasField "action" p ByteString,
+    HasField "actionArgs" p [(ByteString, Maybe ByteString)]
+  ) =>
+  p ->
+  m Http.Request
+mkRedactedApiRequest dat = do
+  authKey <- runCommandExpect0 "pass" ["internet/redacted/api-keys/whatcd-resolver"]
+  pure $
+    [fmt|https://redacted.ch/ajax.php|]
+      & Http.setRequestMethod "GET"
+      & Http.setQueryString (("action", Just dat.action) : dat.actionArgs)
+      & Http.setRequestHeader "Authorization" [authKey]
+
+httpTorrent ::
+  ( MonadIO m,
+    MonadThrow m
+  ) =>
+  Otel.Span ->
+  Http.Request ->
+  m ByteString
+httpTorrent span req =
+  Http.httpBS req
+    >>= assertM
+      span
+      ( \resp -> do
+          let statusCode = resp & Http.responseStatus & (.statusCode)
+              contentType =
+                resp
+                  & Http.responseHeaders
+                  & List.lookup "content-type"
+                  <&> Wai.parseContentType
+                  <&> (\(ct, _mimeAttributes) -> ct)
+          if
+            | statusCode == 200,
+              Just "application/x-bittorrent" <- contentType ->
+                Right $ (resp & Http.responseBody)
+            | statusCode == 200,
+              Just otherType <- contentType ->
+                Left [fmt|Redacted returned a non-torrent body, with content-type "{otherType}"|]
+            | statusCode == 200,
+              Nothing <- contentType ->
+                Left [fmt|Redacted returned a body with unspecified content type|]
+            | code <- statusCode -> Left [fmt|Redacted returned an non-200 error code, code {code}: {resp & showPretty}|]
+      )
+
+httpJson ::
+  ( MonadThrow m,
+    MonadOtel m
+  ) =>
+  (Optional (Label "contentType" ByteString)) ->
+  Json.Parse ErrorTree b ->
+  Http.Request ->
+  m b
+httpJson opts parser req = inSpan' "HTTP Request (JSON)" $ \span -> do
+  let opts' = opts.withDefault (label @"contentType" "application/json")
+  Http.httpBS req
+    >>= assertM
+      span
+      ( \resp -> do
+          let statusCode = resp & Http.responseStatus & (.statusCode)
+              contentType =
+                resp
+                  & Http.responseHeaders
+                  & List.lookup "content-type"
+                  <&> Wai.parseContentType
+                  <&> (\(ct, _mimeAttributes) -> ct)
+          if
+            | statusCode == 200,
+              Just ct <- contentType,
+              ct == opts'.contentType ->
+                Right $ (resp & Http.responseBody)
+            | statusCode == 200,
+              Just otherType <- contentType ->
+                Left [fmt|Server returned a non-json body, with content-type "{otherType}"|]
+            | statusCode == 200,
+              Nothing <- contentType ->
+                Left [fmt|Server returned a body with unspecified content type|]
+            | code <- statusCode -> Left [fmt|Server returned an non-200 error code, code {code}: {resp & showPretty}|]
+      )
+    >>= assertM
+      span
+      ( \body ->
+          Json.parseStrict parser body
+            & first (Json.parseErrorTree "could not parse redacted response")
+      )
+
+redactedApiRequestJson ::
+  ( MonadThrow m,
+    MonadLogger m,
+    HasField "action" p ByteString,
+    HasField "actionArgs" p [(ByteString, Maybe ByteString)],
+    MonadOtel m
+  ) =>
+  p ->
+  Json.Parse ErrorTree a ->
+  m a
+redactedApiRequestJson dat parser =
+  do
+    mkRedactedApiRequest dat
+    >>= httpJson defaults parser
diff --git a/users/Profpatsch/whatcd-resolver/src/Transmission.hs b/users/Profpatsch/whatcd-resolver/src/Transmission.hs
new file mode 100644
index 0000000000..66dbeb9ce7
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Transmission.hs
@@ -0,0 +1,306 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Transmission where
+
+import AppT
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.Error.Tree
+import Data.HashMap.Strict qualified as HashMap
+import Data.List qualified as List
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Map.Strict qualified as Map
+import Database.PostgreSQL.Simple (Only (..))
+import Database.PostgreSQL.Simple.Types (PGArray (PGArray))
+import FieldParser (FieldParser' (..))
+import FieldParser qualified as Field
+import Html qualified
+import Http qualified
+import Json qualified
+import Json.Enc (Enc)
+import Json.Enc qualified as Enc
+import Label
+import MyPrelude
+import Network.HTTP.Types
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import Optional
+import Postgres.MonadPostgres
+import Pretty
+import Text.Blaze.Html (Html)
+import UnliftIO
+import Prelude hiding (span)
+
+-- | A value between (inclusive) 0% and (inclusive) 100%. Precise to 1% steps.
+newtype Percentage = Percentage {unPercentage :: Int}
+  deriving stock (Show)
+
+-- | Parse a scientific into a Percentage
+scientificPercentage :: FieldParser' Error Scientific Percentage
+scientificPercentage =
+  Field.boundedScientificRealFloat @Float
+    >>> ( FieldParser $ \f ->
+            if
+              | f < 0 -> Left "percentage cannot be negative"
+              | f > 1 -> Left "percentage cannot be over 100%"
+              | otherwise -> Right $ Percentage $ ceiling (f * 100)
+        )
+
+-- | Fetch the current status from transmission, and remove the tranmission hash from our database
+-- iff it does not exist in transmission anymore
+getAndUpdateTransmissionTorrentsStatus ::
+  ( MonadTransmission m,
+    MonadThrow m,
+    MonadLogger m,
+    MonadPostgres m,
+    MonadOtel m
+  ) =>
+  Map (Label "torrentHash" Text) () ->
+  (Transaction m (Map (Label "torrentHash" Text) (Label "percentDone" Percentage)))
+getAndUpdateTransmissionTorrentsStatus knownTorrents = do
+  let fields = ["hashString", "percentDone"]
+  actualTorrents <-
+    lift @Transaction $
+      doTransmissionRequest'
+        ( transmissionRequestListOnlyTorrents
+            ( T2
+                (label @"fields" fields)
+                (label @"ids" (Map.keys knownTorrents))
+            )
+            $ do
+              torrentHash <- Json.keyLabel @"torrentHash" "hashString" Json.asText
+              percentDone <- Json.keyLabel @"percentDone" "percentDone" (Field.toJsonParser $ Field.jsonNumber >>> scientificPercentage)
+              pure (torrentHash, percentDone)
+        )
+        <&> Map.fromList
+  let toDelete = Map.difference knownTorrents actualTorrents
+  execute
+    [fmt|
+    UPDATE redacted.torrents_json
+    SET transmission_torrent_hash = NULL
+    WHERE transmission_torrent_hash = ANY (?::text[])
+  |]
+    $ Only (toDelete & Map.keys <&> (.torrentHash) & PGArray :: PGArray Text)
+  pure actualTorrents
+
+getTransmissionTorrentsTable ::
+  (MonadTransmission m, MonadThrow m, MonadLogger m, MonadOtel m) => m Html
+getTransmissionTorrentsTable = do
+  let fields =
+        [ "hashString",
+          "name",
+          "percentDone",
+          "percentComplete",
+          "downloadDir",
+          "files"
+        ]
+  doTransmissionRequest'
+    ( transmissionRequestListAllTorrents fields $ do
+        Json.asObject <&> KeyMap.toMapText
+    )
+    <&> \resp ->
+      Html.toTable
+        ( resp
+            & List.sortOn (\m -> m & Map.lookup "percentDone" & fromMaybe (Json.Number 0))
+            <&> Map.toList
+            -- TODO
+            & List.take 100
+        )
+
+data TransmissionRequest = TransmissionRequest
+  { method :: Text,
+    arguments :: Map Text Enc,
+    tag :: Maybe Int
+  }
+  deriving stock (Show)
+
+transmissionConnectionConfig :: T3 "host" Text "port" Int "usePlainHttp" Bool
+transmissionConnectionConfig = (T3 (label @"host" "localhost") (label @"port" 9091) (label @"usePlainHttp" True))
+
+transmissionRequestListAllTorrents :: (Monad m) => [Text] -> Json.ParseT e m out -> (TransmissionRequest, Json.ParseT e m [out])
+transmissionRequestListAllTorrents fields parseTorrent =
+  ( TransmissionRequest
+      { method = "torrent-get",
+        arguments =
+          Map.fromList
+            [ ("fields", Enc.list Enc.text fields)
+            ],
+        tag = Nothing
+      },
+    Json.key "torrents" $ Json.eachInArray parseTorrent
+  )
+
+transmissionRequestListOnlyTorrents ::
+  ( HasField "ids" r1 [(Label "torrentHash" Text)],
+    HasField "fields" r1 [Text],
+    Monad m
+  ) =>
+  r1 ->
+  Json.ParseT e m out ->
+  (TransmissionRequest, Json.ParseT e m [out])
+transmissionRequestListOnlyTorrents dat parseTorrent =
+  ( TransmissionRequest
+      { method = "torrent-get",
+        arguments =
+          Map.fromList
+            [ ("ids", Enc.list (\i -> Enc.text i.torrentHash) dat.ids),
+              ("fields", Enc.list Enc.text dat.fields)
+            ],
+        tag = Nothing
+      },
+    Json.key "torrents" $ Json.eachInArray parseTorrent
+  )
+
+transmissionRequestAddTorrent ::
+  (HasField "torrentFile" r ByteString, Monad m) =>
+  r ->
+  ( TransmissionRequest,
+    Json.ParseT err m (T2 "torrentHash" Text "torrentName" Text)
+  )
+transmissionRequestAddTorrent dat =
+  ( TransmissionRequest
+      { method = "torrent-add",
+        arguments =
+          Map.fromList
+            [ ("metainfo", Enc.base64Bytes dat.torrentFile),
+              ("paused", Enc.bool False)
+            ],
+        tag = Nothing
+      },
+    do
+      let p method = Json.key method $ do
+            hash <- Json.keyLabel @"torrentHash" "hashString" Json.asText
+            name <- Json.keyLabel @"torrentName" "name" Json.asText
+            pure $ T2 hash name
+      p "torrent-duplicate" Json.<|> p "torrent-added"
+  )
+
+data TransmissionResponse output = TransmissionResponse
+  { result :: TransmissionResponseStatus,
+    arguments :: Maybe output,
+    tag :: Maybe Int
+  }
+  deriving stock (Show)
+
+data TransmissionResponseStatus
+  = TransmissionResponseSuccess
+  | TransmissionResponseFailure Text
+  deriving stock (Show)
+
+doTransmissionRequest' ::
+  ( MonadTransmission m,
+    MonadThrow m,
+    MonadLogger m,
+    MonadOtel m
+  ) =>
+  (TransmissionRequest, Json.Parse Error output) ->
+  m output
+doTransmissionRequest' req = inSpan' "Transmission Request" $ \span -> do
+  resp <-
+    doTransmissionRequest
+      span
+      transmissionConnectionConfig
+      req
+  case resp.result of
+    TransmissionResponseFailure err -> appThrowTree span (nestedError "Transmission RPC error" $ singleError $ newError err)
+    TransmissionResponseSuccess -> case resp.arguments of
+      Nothing -> appThrowTree span "Transmission RPC error: No `arguments` field in response"
+      Just out -> pure out
+
+-- | Contact the transmission RPC, and do the CSRF protection dance.
+--
+-- Spec: https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md
+doTransmissionRequest ::
+  ( MonadTransmission m,
+    HasField "host" t1 Text,
+    HasField "port" t1 Int,
+    HasField "usePlainHttp" t1 Bool,
+    MonadThrow m,
+    MonadLogger m,
+    MonadOtel m
+  ) =>
+  Otel.Span ->
+  t1 ->
+  (TransmissionRequest, Json.Parse Error output) ->
+  m (TransmissionResponse output)
+doTransmissionRequest span dat (req, parser) = do
+  sessionId <- getTransmissionId
+  let textArg t = (Enc.text t, Otel.toAttribute @Text t)
+  let encArg enc = (enc, Otel.toAttribute @Text $ enc & Enc.encToTextPretty)
+  let intArg i = (Enc.int i, Otel.toAttribute @Int i)
+
+  let body :: [(Text, (Enc, Otel.Attribute))] =
+        ( [ ("method", req.method & textArg),
+            ("arguments", encArg $ Enc.map id req.arguments)
+          ]
+            <> (req.tag & foldMap (\t -> [("tag", t & intArg)]))
+        )
+  addAttributes
+    span
+    ( HashMap.fromList $
+        body
+          <&> bimap
+            (\k -> [fmt|transmission.{k}|])
+            (\(_, attr) -> attr)
+    )
+  resp <-
+    Http.doRequestJson
+      ( (Http.mkRequestOptions (T2 (label @"method" "POST") (label @"host" dat.host)))
+          { Http.path = mkOptional ["transmission", "rpc"],
+            Http.port = mkOptional dat.port,
+            Http.headers = mkOptional $ (sessionId & ifExists ("X-Transmission-Session-Id",)),
+            Http.usePlainHttp = mkOptional dat.usePlainHttp
+          }
+      )
+      (body <&> second fst & Enc.object)
+  -- Implement the CSRF protection thingy
+  case resp & Http.getResponseStatus & (.statusCode) of
+    409 -> do
+      tid <-
+        resp
+          & Http.getResponseHeader "X-Transmission-Session-Id"
+          & nonEmpty
+          & annotate [fmt|Missing "X-Transmission-Session-Id" header in 409 response: {showPretty resp}|]
+          & unwrapIOError
+          & liftIO
+          <&> NonEmpty.head
+      setTransmissionId tid
+      doTransmissionRequest span dat (req, parser)
+    200 ->
+      resp
+        & Http.getResponseBody
+        & Json.parseStrict
+          ( Json.mapError singleError $ do
+              result <-
+                Json.key "result" Json.asText <&> \case
+                  "success" -> TransmissionResponseSuccess
+                  err -> TransmissionResponseFailure err
+              arguments <-
+                Json.keyMay "arguments" parser
+              tag <-
+                Json.keyMay
+                  "tag"
+                  (Field.toJsonParser (Field.jsonNumber >>> Field.boundedScientificIntegral "tag too long"))
+              pure TransmissionResponse {..}
+          )
+        & first (Json.parseErrorTree "Cannot parse transmission RPC response")
+        & \case
+          Right a -> pure a
+          Left err -> do
+            case Json.eitherDecodeStrict' @Json.Value (resp & Http.getResponseBody) of
+              Left _err -> pure ()
+              Right val -> logInfo [fmt|failing transmission response: {showPrettyJson val}|]
+            appThrowTree span err
+    _ -> liftIO $ unwrapIOError $ Left [fmt|Non-200 response: {showPretty resp}|]
+
+class MonadTransmission m where
+  getTransmissionId :: m (Maybe ByteString)
+  setTransmissionId :: ByteString -> m ()
+
+instance (MonadIO m) => MonadTransmission (AppT m) where
+  getTransmissionId = AppT (asks (.transmissionSessionId)) >>= tryTakeMVar
+  setTransmissionId t = do
+    var <- AppT $ asks (.transmissionSessionId)
+    putMVar var t
diff --git a/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs b/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs
new file mode 100644
index 0000000000..f1902bac8c
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs
@@ -0,0 +1,698 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module WhatcdResolver where
+
+import AppT
+import Control.Category qualified as Cat
+import Control.Monad.Catch.Pure (runCatch)
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.HashMap.Strict qualified as HashMap
+import Data.List qualified as List
+import Data.Map.Strict qualified as Map
+import Data.Pool qualified as Pool
+import Data.Text qualified as Text
+import Database.PostgreSQL.Simple qualified as Postgres
+import Database.PostgreSQL.Simple.SqlQQ (sql)
+import Database.PostgreSQL.Simple.Types (PGArray (PGArray))
+import Database.Postgres.Temp qualified as TmpPg
+import FieldParser (FieldParser, FieldParser' (..))
+import FieldParser qualified as Field
+import Html qualified
+import IHP.HSX.QQ (hsx)
+import Json qualified
+import Json.Enc (Enc)
+import Json.Enc qualified as Enc
+import JsonLd
+import Label
+import Multipart2 qualified as Multipart
+import MyPrelude
+import Network.HTTP.Client.Conduit qualified as Http
+import Network.HTTP.Simple qualified as Http
+import Network.HTTP.Types
+import Network.HTTP.Types qualified as Http
+import Network.URI (URI)
+import Network.URI qualified
+import Network.URI qualified as URI
+import Network.Wai (ResponseReceived)
+import Network.Wai qualified as Wai
+import Network.Wai.Handler.Warp qualified as Warp
+import Network.Wai.Parse qualified as Wai
+import OpenTelemetry.Attributes qualified as Otel
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import OpenTelemetry.Trace.Monad qualified as Otel
+import Parse (Parse)
+import Parse qualified
+import Postgres.Decoder qualified as Dec
+import Postgres.MonadPostgres
+import Pretty
+import Redacted
+import System.Directory qualified as Dir
+import System.Directory qualified as Xdg
+import System.Environment qualified as Env
+import System.FilePath ((</>))
+import Text.Blaze.Html (Html)
+import Text.Blaze.Html.Renderer.Pretty qualified as Html.Pretty
+import Text.Blaze.Html.Renderer.Utf8 qualified as Html
+import Text.Blaze.Html5 qualified as Html
+import Tool (readTool, readTools)
+import Transmission
+import UnliftIO hiding (Handler)
+import Prelude hiding (span)
+
+main :: IO ()
+main =
+  runAppWith
+    ( do
+        -- todo: trace that to the init functions as well
+        Otel.inSpan "whatcd-resolver main function" Otel.defaultSpanArguments $ do
+          _ <- runTransaction migrate
+          htmlUi
+    )
+    <&> first showToError
+    >>= expectIOError "could not start whatcd-resolver"
+
+htmlUi :: AppT IO ()
+htmlUi = do
+  let debug = True
+  uniqueRunId <-
+    runTransaction $
+      querySingleRowWith
+        [sql|
+            SELECT gen_random_uuid()::text
+        |]
+        ()
+        (Dec.fromField @Text)
+
+  withRunInIO $ \runInIO -> Warp.run 9093 $ \req respond -> do
+    let catchAppException act =
+          try act >>= \case
+            Right a -> pure a
+            Left (AppException err) -> do
+              runInIO (logError err)
+              respond (Wai.responseLBS Http.status500 [] "")
+
+    catchAppException $ do
+      let mp span parser =
+            Multipart.parseMultipartOrThrow
+              (appThrowTree span)
+              parser
+              req
+
+      let torrentIdMp span =
+            mp
+              span
+              ( do
+                  label @"torrentId" <$> Multipart.field "torrent-id" ((Field.utf8 >>> Field.signedDecimal >>> Field.bounded @Int "int"))
+              )
+      let parseQueryArgs span parser =
+            Parse.runParse "Unable to find the right request query arguments" (lmap Wai.queryString parser) req
+              & assertM span id
+
+      let parseQueryArgsNewSpan spanName parser =
+            Parse.runParse "Unable to find the right request query arguments" (lmap Wai.queryString parser) req
+              & assertMNewSpan spanName id
+
+      let handlers :: Handlers (AppT IO)
+          handlers respond =
+            Map.fromList
+              [ ("", respond.h (mainHtml uniqueRunId)),
+                ( "snips/redacted/search",
+                  respond.h $
+                    \span -> do
+                      dat <-
+                        mp
+                          span
+                          ( do
+                              label @"searchstr" <$> Multipart.field "redacted-search" Cat.id
+                          )
+                      snipsRedactedSearch dat
+                ),
+                ( "snips/redacted/torrentDataJson",
+                  respond.h $ \span -> do
+                    dat <- torrentIdMp span
+                    Html.mkVal <$> (runTransaction $ getTorrentById dat)
+                ),
+                ( "snips/redacted/getTorrentFile",
+                  respond.h $ \span -> do
+                    dat <- torrentIdMp span
+                    runTransaction $ do
+                      inserted <- redactedGetTorrentFileAndInsert dat
+                      running <-
+                        lift @Transaction $
+                          doTransmissionRequest' (transmissionRequestAddTorrent inserted)
+                      updateTransmissionTorrentHashById
+                        ( T2
+                            (getLabel @"torrentHash" running)
+                            (getLabel @"torrentId" dat)
+                        )
+                      pure $
+                        everySecond
+                          "snips/transmission/getTorrentState"
+                          (Enc.object [("torrent-hash", Enc.text running.torrentHash)])
+                          "Starting"
+                ),
+                -- TODO: this is bad duplication??
+                ( "snips/redacted/startTorrentFile",
+                  respond.h $ \span -> do
+                    dat <- torrentIdMp span
+                    runTransaction $ do
+                      file <-
+                        getTorrentFileById dat
+                          <&> annotate [fmt|No torrent file for torrentId "{dat.torrentId}"|]
+                          >>= orAppThrowTree span
+
+                      running <-
+                        lift @Transaction $
+                          doTransmissionRequest' (transmissionRequestAddTorrent file)
+                      updateTransmissionTorrentHashById
+                        ( T2
+                            (getLabel @"torrentHash" running)
+                            (getLabel @"torrentId" dat)
+                        )
+                      pure $
+                        everySecond
+                          "snips/transmission/getTorrentState"
+                          (Enc.object [("torrent-hash", Enc.text running.torrentHash)])
+                          "Starting"
+                ),
+                ( "snips/transmission/getTorrentState",
+                  respond.h $ \span -> do
+                    dat <- mp span $ label @"torrentHash" <$> Multipart.field "torrent-hash" Field.utf8
+                    status <-
+                      doTransmissionRequest'
+                        ( transmissionRequestListOnlyTorrents
+                            ( T2
+                                (label @"ids" [label @"torrentHash" dat.torrentHash])
+                                (label @"fields" ["hashString"])
+                            )
+                            (Json.keyLabel @"torrentHash" "hashString" Json.asText)
+                        )
+                        <&> List.find (\torrent -> torrent.torrentHash == dat.torrentHash)
+
+                    pure $
+                      case status of
+                        Nothing -> [hsx|ERROR unknown|]
+                        Just _torrent -> [hsx|Running|]
+                ),
+                ( "snips/jsonld/render",
+                  respond.h $ \span -> do
+                    qry <-
+                      parseQueryArgs
+                        span
+                        ( label @"target"
+                            <$> ( (singleQueryArgument "target" Field.utf8 >>> textToURI)
+                                    & Parse.andParse uriToHttpClientRequest
+                                )
+                        )
+                    jsonld <- httpGetJsonLd (qry.target)
+                    pure $ renderJsonld jsonld
+                ),
+                ( "autorefresh",
+                  respond.plain $ do
+                    qry <-
+                      parseQueryArgsNewSpan
+                        "Autorefresh Query Parse"
+                        ( label @"hasItBeenRestarted"
+                            <$> singleQueryArgument "hasItBeenRestarted" Field.utf8
+                        )
+                    pure $
+                      Wai.responseLBS
+                        Http.ok200
+                        ( [("Content-Type", "text/html")]
+                            <> if uniqueRunId /= qry.hasItBeenRestarted
+                              then -- cause the client side to refresh
+                                [("HX-Refresh", "true")]
+                              else []
+                        )
+                        ""
+                )
+              ]
+      runInIO $
+        runHandlers
+          debug
+          (\respond -> respond.h $ (mainHtml uniqueRunId))
+          handlers
+          req
+          respond
+  where
+    everySecond :: Text -> Enc -> Html -> Html
+    everySecond call extraData innerHtml = [hsx|<div hx-trigger="every 1s" hx-swap="outerHTML" hx-post={call} hx-vals={Enc.encToBytesUtf8 extraData}>{innerHtml}</div>|]
+
+    mainHtml :: Text -> Otel.Span -> AppT IO Html
+    mainHtml uniqueRunId _span = runTransaction $ do
+      jsonld <-
+        httpGetJsonLd
+          ( URI.parseURI "https://musicbrainz.org/work/92000fd4-d304-406d-aeb4-6bdbeed318ec" & annotate "not an URI" & unwrapError,
+            "https://musicbrainz.org/work/92000fd4-d304-406d-aeb4-6bdbeed318ec"
+          )
+          <&> renderJsonld
+      bestTorrentsTable <- getBestTorrentsTable
+      -- transmissionTorrentsTable <- lift @Transaction getTransmissionTorrentsTable
+      pure $
+        Html.docTypeHtml
+          [hsx|
+      <head>
+        <title>whatcd-resolver</title>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
+        <script src="https://unpkg.com/htmx.org@1.9.2" integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h" crossorigin="anonymous"></script>
+        <style>
+          dl {
+            margin: 1em;
+            padding: 0.5em 1em;
+            border: thin solid;
+          }
+        </style>
+      </head>
+      <body>
+        {jsonld}
+        <form
+          hx-post="/snips/redacted/search"
+          hx-target="#redacted-search-results">
+          <label for="redacted-search">Redacted Search</label>
+          <input
+            id="redacted-search"
+            type="text"
+            name="redacted-search" />
+          <button type="submit" hx-disabled-elt="this">Search</button>
+          <div class="htmx-indicator">Search running!</div>
+        </form>
+        <div id="redacted-search-results">
+          {bestTorrentsTable}
+        </div>
+        <!-- refresh the page if the uniqueRunId is different -->
+        <input
+             hidden
+             type="text"
+             id="autorefresh"
+             name="hasItBeenRestarted"
+             value={uniqueRunId}
+             hx-get="/autorefresh"
+             hx-trigger="every 5s"
+             hx-swap="none"
+        />
+      </body>
+    |]
+
+type Handlers m = HandlerResponses m -> Map Text (m ResponseReceived)
+
+type HandlerResponses m = T2 "h" ((Otel.Span -> m Html) -> m ResponseReceived) "plain" (m Wai.Response -> m ResponseReceived)
+
+runHandlers ::
+  (MonadOtel m) =>
+  Bool ->
+  (HandlerResponses m -> m ResponseReceived) ->
+  (HandlerResponses m -> Map Text (m ResponseReceived)) ->
+  Wai.Request ->
+  (Wai.Response -> IO ResponseReceived) ->
+  m ResponseReceived
+runHandlers debug defaultHandler handlers req respond = withRunInIO $ \runInIO -> do
+  let renderHtml =
+        if debug
+          then Html.Pretty.renderHtml >>> stringToText >>> textToBytesUtf8 >>> toLazyBytes
+          else Html.renderHtml
+  let hh route act =
+        Otel.inSpan'
+          [fmt|Route {route }|]
+          ( Otel.defaultSpanArguments
+              { Otel.attributes =
+                  HashMap.fromList
+                    [ ("server.path", Otel.toAttribute @Text route)
+                    ]
+              }
+          )
+          ( \span -> do
+              res <- act span
+              liftIO $ respond . Wai.responseLBS Http.ok200 ([("Content-Type", "text/html")] <> res.extraHeaders) . renderHtml $ res.html
+          )
+  let h route act = hh route (\span -> act span <&> (\html -> T2 (label @"html" html) (label @"extraHeaders" [])))
+
+  let path = (req & Wai.pathInfo & Text.intercalate "/")
+  let handlerResponses =
+        ( T2
+            (label @"h" (h path))
+            (label @"plain" (\m -> liftIO $ runInIO m >>= respond))
+        )
+  let handler =
+        (handlers handlerResponses)
+          & Map.lookup path
+          & fromMaybe (defaultHandler handlerResponses)
+  runInIO handler
+
+singleQueryArgument :: Text -> FieldParser ByteString to -> Parse Http.Query to
+singleQueryArgument field inner =
+  Parse.mkParsePushContext
+    field
+    ( \(ctx, qry) -> case qry
+        & mapMaybe
+          ( \(k, v) ->
+              if k == (field & textToBytesUtf8)
+                then Just v
+                else Nothing
+          ) of
+        [] -> Left [fmt|No such query argument "{field}", at {ctx & Parse.showContext}|]
+        [Nothing] -> Left [fmt|Expected one query argument with a value, but "{field}" was a query flag|]
+        [Just one] -> Right one
+        more -> Left [fmt|More than one value for query argument "{field}": {show more}, at {ctx & Parse.showContext}|]
+    )
+    >>> Parse.fieldParser inner
+
+-- | Make sure we can parse the given Text into an URI.
+textToURI :: Parse Text URI
+textToURI =
+  Parse.fieldParser
+    ( FieldParser $ \text ->
+        text
+          & textToString
+          & Network.URI.parseURI
+          & annotate [fmt|Cannot parse this as a URL: "{text}"|]
+    )
+
+-- | Make sure we can parse the given URI into a Request.
+--
+-- This tries to work around the horrible, horrible interface in Http.Client.
+uriToHttpClientRequest :: Parse URI Http.Request
+uriToHttpClientRequest =
+  Parse.mkParseNoContext
+    ( \url ->
+        (url & Http.requestFromURI)
+          & runCatch
+          & first (checkException @Http.HttpException)
+          & \case
+            Left (Right (Http.InvalidUrlException urlText reason)) ->
+              Left [fmt|Unable to set the url "{urlText}" as request URL, reason: {reason}|]
+            Left (Right exc@(Http.HttpExceptionRequest _ _)) ->
+              Left [fmt|Weird! Should not get a HttpExceptionRequest when parsing an URL (bad library design), was {exc & displayException}|]
+            Left (Left someExc) ->
+              Left [fmt|Weird! Should not get anyhting but a HttpException when parsing an URL (bad library design), was {someExc & displayException}|]
+            Right req -> pure req
+    )
+
+checkException :: (Exception b) => SomeException -> Either SomeException b
+checkException some = case fromException some of
+  Nothing -> Left some
+  Just e -> Right e
+
+snipsRedactedSearch ::
+  ( MonadLogger m,
+    MonadPostgres m,
+    HasField "searchstr" r ByteString,
+    MonadThrow m,
+    MonadTransmission m,
+    MonadOtel m
+  ) =>
+  r ->
+  m Html
+snipsRedactedSearch dat = do
+  t <-
+    redactedSearchAndInsert
+      [ ("searchstr", dat.searchstr),
+        ("releasetype", "album")
+      ]
+  runTransaction $ do
+    t
+    getBestTorrentsTable
+
+getBestTorrentsTable ::
+  ( MonadTransmission m,
+    MonadThrow m,
+    MonadLogger m,
+    MonadPostgres m,
+    MonadOtel m
+  ) =>
+  Transaction m Html
+getBestTorrentsTable = do
+  bestStale :: [TorrentData ()] <- getBestTorrents
+  actual <-
+    getAndUpdateTransmissionTorrentsStatus
+      ( bestStale
+          & mapMaybe
+            ( \td -> case td.torrentStatus of
+                InTransmission h -> Just h
+                _ -> Nothing
+            )
+          <&> (\t -> (getLabel @"torrentHash" t, t.transmissionInfo))
+          & Map.fromList
+      )
+  let fresh =
+        bestStale
+          --  we have to update the status of every torrent that’s not in tranmission anymore
+          -- TODO I feel like it’s easier (& more correct?) to just do the database request again …
+          <&> ( \td -> case td.torrentStatus of
+                  InTransmission info ->
+                    case actual & Map.lookup (getLabel @"torrentHash" info) of
+                      -- TODO this is also pretty dumb, cause it assumes that we have the torrent file if it was in transmission before,
+                      -- which is an internal factum that is established in getBestTorrents (and might change later)
+                      Nothing -> td {torrentStatus = NotInTransmissionYet}
+                      Just transmissionInfo -> td {torrentStatus = InTransmission (T2 (getLabel @"torrentHash" info) (label @"transmissionInfo" transmissionInfo))}
+                  NotInTransmissionYet -> td {torrentStatus = NotInTransmissionYet}
+                  NoTorrentFileYet -> td {torrentStatus = NoTorrentFileYet}
+              )
+  let localTorrent b = case b.torrentStatus of
+        NoTorrentFileYet -> [hsx|<button hx-post="snips/redacted/getTorrentFile" hx-swap="outerHTML" hx-vals={Enc.encToBytesUtf8 $ Enc.object [("torrent-id", Enc.int b.torrentId)]}>Upload Torrent</button>|]
+        InTransmission info -> [hsx|{info.transmissionInfo.percentDone.unPercentage}% done|]
+        NotInTransmissionYet -> [hsx|<button hx-post="snips/redacted/startTorrentFile" hx-swap="outerHTML" hx-vals={Enc.encToBytesUtf8 $ Enc.object [("torrent-id", Enc.int b.torrentId)]}>Start Torrent</button>|]
+  let bestRows =
+        fresh
+          & foldMap
+            ( \b -> do
+                [hsx|
+                  <tr>
+                  <td>{localTorrent b}</td>
+                  <td>{Html.toHtml @Int b.groupId}</td>
+                  <td>{Html.toHtml @Text b.torrentGroupJson.artist}</td>
+                  <td>{Html.toHtml @Text b.torrentGroupJson.groupName}</td>
+                  <td>{Html.toHtml @Int b.seedingWeight}</td>
+                  <td><details hx-trigger="toggle once" hx-post="snips/redacted/torrentDataJson" hx-vals={Enc.encToBytesUtf8 $ Enc.object [("torrent-id", Enc.int b.torrentId)]}></details></td>
+                  </tr>
+                |]
+            )
+  pure $
+    [hsx|
+        <table class="table">
+          <thead>
+            <tr>
+              <th>Local</th>
+              <th>Group ID</th>
+              <th>Artist</th>
+              <th>Name</th>
+              <th>Weight</th>
+              <th>Torrent</th>
+              <th>Torrent Group</th>
+            </tr>
+          </thead>
+          <tbody>
+            {bestRows}
+          </tbody>
+        </table>
+      |]
+
+getTransmissionTorrentsTable ::
+  (MonadTransmission m, MonadThrow m, MonadLogger m, MonadOtel m) => m Html
+getTransmissionTorrentsTable = do
+  let fields =
+        [ "hashString",
+          "name",
+          "percentDone",
+          "percentComplete",
+          "downloadDir",
+          "files"
+        ]
+  doTransmissionRequest'
+    ( transmissionRequestListAllTorrents fields $ do
+        Json.asObject <&> KeyMap.toMapText
+    )
+    <&> \resp ->
+      Html.toTable
+        ( resp
+            & List.sortOn (\m -> m & Map.lookup "percentDone" & fromMaybe (Json.Number 0))
+            <&> Map.toList
+            -- TODO
+            & List.take 100
+        )
+
+unzip3PGArray :: [(a1, a2, a3)] -> (PGArray a1, PGArray a2, PGArray a3)
+unzip3PGArray xs = xs & unzip3 & \(a, b, c) -> (PGArray a, PGArray b, PGArray c)
+
+assertOneUpdated ::
+  (HasField "numberOfRowsAffected" r Natural, MonadThrow m, MonadIO m) =>
+  Otel.Span ->
+  Text ->
+  r ->
+  m ()
+assertOneUpdated span name x = case x.numberOfRowsAffected of
+  1 -> pure ()
+  n -> appThrowTree span ([fmt|{name :: Text}: Expected to update exactly one row, but updated {n :: Natural} row(s)|])
+
+migrate ::
+  ( MonadPostgres m,
+    MonadOtel m
+  ) =>
+  Transaction m (Label "numberOfRowsAffected" Natural)
+migrate = inSpan "Database Migration" $ do
+  execute
+    [sql|
+    CREATE SCHEMA IF NOT EXISTS redacted;
+
+    CREATE TABLE IF NOT EXISTS redacted.torrent_groups (
+      id SERIAL PRIMARY KEY,
+      group_id INTEGER,
+      group_name TEXT,
+      full_json_result JSONB,
+      UNIQUE(group_id)
+    );
+
+    CREATE TABLE IF NOT EXISTS redacted.torrents_json (
+      id SERIAL PRIMARY KEY,
+      torrent_id INTEGER,
+      torrent_group SERIAL NOT NULL REFERENCES redacted.torrent_groups(id) ON DELETE CASCADE,
+      full_json_result JSONB,
+      UNIQUE(torrent_id)
+    );
+
+    ALTER TABLE redacted.torrents_json
+    ADD COLUMN IF NOT EXISTS torrent_file bytea NULL;
+    ALTER TABLE redacted.torrents_json
+    ADD COLUMN IF NOT EXISTS transmission_torrent_hash text NULL;
+
+    -- inflect out values of the full json
+
+    CREATE OR REPLACE VIEW redacted.torrents AS
+    SELECT
+      t.id,
+      t.torrent_id,
+      t.torrent_group,
+      -- the seeding weight is used to find the best torrent in a group.
+      ( ((full_json_result->'seeders')::integer*3
+        + (full_json_result->'snatches')::integer
+        )
+      -- prefer remasters by multiplying them with 3
+      * (CASE
+          WHEN full_json_result->>'remasterTitle' ILIKE '%remaster%'
+          THEN 3
+          ELSE 1
+         END)
+      )
+      AS seeding_weight,
+      t.full_json_result,
+      t.torrent_file,
+      t.transmission_torrent_hash
+    FROM redacted.torrents_json t;
+
+    CREATE INDEX IF NOT EXISTS torrents_json_seeding ON redacted.torrents_json(((full_json_result->'seeding')::integer));
+    CREATE INDEX IF NOT EXISTS torrents_json_snatches ON redacted.torrents_json(((full_json_result->'snatches')::integer));
+  |]
+    ()
+
+httpTorrent ::
+  ( MonadIO m,
+    MonadThrow m
+  ) =>
+  Otel.Span ->
+  Http.Request ->
+  m ByteString
+httpTorrent span req =
+  Http.httpBS req
+    >>= assertM
+      span
+      ( \resp -> do
+          let statusCode = resp & Http.responseStatus & (.statusCode)
+              contentType =
+                resp
+                  & Http.responseHeaders
+                  & List.lookup "content-type"
+                  <&> Wai.parseContentType
+                  <&> (\(ct, _mimeAttributes) -> ct)
+          if
+            | statusCode == 200,
+              Just "application/x-bittorrent" <- contentType ->
+                Right $ (resp & Http.responseBody)
+            | statusCode == 200,
+              Just otherType <- contentType ->
+                Left [fmt|Redacted returned a non-torrent body, with content-type "{otherType}"|]
+            | statusCode == 200,
+              Nothing <- contentType ->
+                Left [fmt|Redacted returned a body with unspecified content type|]
+            | code <- statusCode -> Left [fmt|Redacted returned an non-200 error code, code {code}: {resp & showPretty}|]
+      )
+
+runAppWith :: AppT IO a -> IO (Either TmpPg.StartError a)
+runAppWith appT = withTracer $ \tracer -> withDb $ \db -> do
+  pgFormat <- readTools (label @"toolsEnvVar" "WHATCD_RESOLVER_TOOLS") (readTool "pg_format")
+  let config = label @"logDatabaseQueries" LogDatabaseQueries
+  pgConnPool <-
+    Pool.newPool $
+      Pool.defaultPoolConfig
+        {- resource init action -} (Postgres.connectPostgreSQL (db & TmpPg.toConnectionString))
+        {- resource destruction -} Postgres.close
+        {- unusedResourceOpenTime -} 10
+        {- max resources across all stripes -} 20
+  transmissionSessionId <- newEmptyMVar
+  let newAppT = do
+        logInfo [fmt|Running with config: {showPretty config}|]
+        logInfo [fmt|Connected to database at {db & TmpPg.toDataDirectory} on socket {db & TmpPg.toConnectionString}|]
+        appT
+  runReaderT newAppT.unAppT Context {..}
+
+withTracer :: (Otel.Tracer -> IO c) -> IO c
+withTracer f = do
+  setDefaultEnv "OTEL_SERVICE_NAME" "whatcd-resolver"
+  bracket
+    -- Install the SDK, pulling configuration from the environment
+    ( do
+        (processors, opts) <- Otel.getTracerProviderInitializationOptions
+        tp <-
+          Otel.createTracerProvider
+            processors
+            -- workaround the attribute length bug https://github.com/iand675/hs-opentelemetry/issues/113
+            ( opts
+                { Otel.tracerProviderOptionsAttributeLimits =
+                    opts.tracerProviderOptionsAttributeLimits
+                      { Otel.attributeCountLimit = Just 65_000
+                      }
+                }
+            )
+        Otel.setGlobalTracerProvider tp
+        pure tp
+    )
+    -- Ensure that any spans that haven't been exported yet are flushed
+    Otel.shutdownTracerProvider
+    -- Get a tracer so you can create spans
+    (\tracerProvider -> f $ Otel.makeTracer tracerProvider "whatcd-resolver" Otel.tracerOptions)
+
+setDefaultEnv :: String -> String -> IO ()
+setDefaultEnv envName defaultValue = do
+  Env.lookupEnv envName >>= \case
+    Just _env -> pure ()
+    Nothing -> Env.setEnv envName defaultValue
+
+withDb :: (TmpPg.DB -> IO a) -> IO (Either TmpPg.StartError a)
+withDb act = do
+  dataDir <- Xdg.getXdgDirectory Xdg.XdgData "whatcd-resolver"
+  let databaseDir = dataDir </> "database"
+  let socketDir = dataDir </> "database-socket"
+  Dir.createDirectoryIfMissing True socketDir
+  initDbConfig <-
+    Dir.doesDirectoryExist databaseDir >>= \case
+      True -> pure TmpPg.Zlich
+      False -> do
+        putStderrLn [fmt|Database does not exist yet, creating in "{databaseDir}"|]
+        Dir.createDirectoryIfMissing True databaseDir
+        pure TmpPg.DontCare
+  let cfg =
+        mempty
+          { TmpPg.dataDirectory = TmpPg.Permanent (databaseDir),
+            TmpPg.socketDirectory = TmpPg.Permanent socketDir,
+            TmpPg.port = pure $ Just 5431,
+            TmpPg.initDbConfig
+          }
+  TmpPg.withConfig cfg $ \db -> do
+    -- print [fmt|data dir: {db & TmpPg.toDataDirectory}|]
+    -- print [fmt|conn string: {db & TmpPg.toConnectionString}|]
+    act db
diff --git a/users/Profpatsch/whatcd-resolver/whatcd-resolver.cabal b/users/Profpatsch/whatcd-resolver/whatcd-resolver.cabal
new file mode 100644
index 0000000000..a9bd04827b
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/whatcd-resolver.cabal
@@ -0,0 +1,121 @@
+cabal-version:      3.0
+name:               whatcd-resolver
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Allow the same record field name to be declared twice per module.
+    -- This works, because we use `OverloadedRecordDot` everywhere (enforced by `NoFieldSelectors`).
+    DuplicateRecordFields
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+    -- allows defining pattern synonyms, but also the `import Foo (pattern FooPattern)` import syntax
+    PatternSynonyms
+
+  default-language: GHC2021
+
+library
+    import: common-options
+
+    hs-source-dirs: src
+
+    exposed-modules:
+       WhatcdResolver
+       AppT
+       JsonLd
+       Optional
+       Http
+       Html
+       Transmission
+       Redacted
+
+    build-depends:
+        base >=4.15 && <5,
+        text,
+        my-prelude,
+        my-webstuff,
+        pa-prelude,
+        pa-error-tree,
+        pa-label,
+        pa-json,
+        pa-field-parser,
+        pa-run-command,
+        aeson-better-errors,
+        aeson,
+        blaze-html,
+        bytestring,
+        case-insensitive,
+        containers,
+        unordered-containers,
+        directory,
+        exceptions,
+        filepath,
+        hs-opentelemetry-sdk,
+        hs-opentelemetry-api,
+        http-conduit,
+        http-types,
+        http-client,
+        ihp-hsx,
+        monad-logger,
+        mtl,
+        network-uri,
+        resource-pool,
+        postgresql-simple,
+        punycode,
+        tmp-postgres,
+        unliftio,
+        wai-extra,
+        wai,
+        warp,
+
+executable whatcd-resolver
+    import: common-options
+
+    main-is: Main.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        whatcd-resolver
diff --git a/users/Profpatsch/writers/default.nix b/users/Profpatsch/writers/default.nix
index 3151a9d3bd..9fb69231a1 100644
--- a/users/Profpatsch/writers/default.nix
+++ b/users/Profpatsch/writers/default.nix
@@ -1,7 +1,7 @@
 { depot, pkgs, lib, ... }:
 let
-  bins = depot.nix.getBins pkgs.s6-portable-utils ["s6-mkdir" "s6-cat" "s6-ln" "s6-ls" "s6-touch" ]
-      // depot.nix.getBins pkgs.coreutils ["printf" ];
+  bins = depot.nix.getBins pkgs.s6-portable-utils [ "s6-mkdir" "s6-cat" "s6-ln" "s6-ls" "s6-touch" ]
+    // depot.nix.getBins pkgs.coreutils [ "printf" ];
 
   inherit (depot.nix.yants) defun struct restrict attrs list string drv any;
 
@@ -11,66 +11,110 @@ let
     restrict
       "flake error"
       (s: lib.any (prefix: (builtins.substring 0 1 s) == prefix)
-          [ "E" "W" ])
+        [ "E" "W" ])
       string;
   Libraries = defun [ (attrs any) (list drv) ];
 
-  python3 = {
-    name,
-    libraries ? (_: []),
-    flakeIgnore ? []
-  }: pkgs.writers.writePython3 name {
-    libraries = Libraries libraries pkgs.python3Packages;
-    flakeIgnore =
-      let ignoreTheseErrors = [
-        # whitespace after {
-        "E201"
-        # whitespace before }
-        "E202"
-        # fuck 4-space indentation
-        "E121" "E111"
-        # who cares about blank lines …
-        # … at end of files
-        "W391"
-        # … between functions
-        "E302" "E305"
-      ];
-      in list FlakeError (ignoreTheseErrors ++ flakeIgnore);
-  };
+  pythonPackages = pkgs.python310Packages;
+  buildPythonPackages = pkgs.buildPackages.python310Packages;
+  python = pythonPackages.python;
+
+  python3 =
+    { name
+    , libraries ? (_: [ ])
+    , flakeIgnore ? [ ]
+    }:
+    let
+    in
+    pkgs.writers.makePythonWriter python pythonPackages buildPythonPackages name {
+      libraries = Libraries libraries pythonPackages;
+      flakeIgnore =
+        let
+          ignoreTheseErrors = [
+            # whitespace after {
+            "E201"
+            # whitespace before }
+            "E202"
+            # fuck 4-space indentation
+            "E121"
+            "E111"
+            # who cares about blank lines …
+            # … at end of files
+            "W391"
+            # … between functions
+            "E302"
+            "E305"
+            # … if there’s too many of them
+            "E303"
+            # or lines that are too long
+            "E501"
+          ];
+        in
+        list FlakeError (ignoreTheseErrors ++ flakeIgnore);
+    };
 
   # TODO: add the same flake check as the pyhon3 writer
-  python3Lib = { name, libraries ? (_: []) }: moduleString:
-    let srcTree = depot.nix.runExecline.local name { stdin = moduleString; } [
-      "importas" "out" "out"
-      "if" [ bins.s6-mkdir "-p" "\${out}/${name}" ]
-      "if" [
-        "redirfd" "-w" "1" "\${out}/setup.py"
-        bins.printf ''
-          from distutils.core import setup
+  python3Lib = { name, libraries ? (_: [ ]) }: moduleString:
+    let
+      srcTree = depot.nix.runExecline.local name { stdin = moduleString; } [
+        "importas"
+        "out"
+        "out"
+        "if"
+        [ bins.s6-mkdir "-p" "\${out}/${name}" ]
+        "if"
+        [
+          "redirfd"
+          "-w"
+          "1"
+          "\${out}/setup.py"
+          bins.printf
+          ''
+            from distutils.core import setup
 
-          setup(
-            name='%s',
-            packages=['%s']
-          )
-        '' name name
-      ]
-      "if" [
-        # redirect stdin to the init py
-        "redirfd" "-w" "1" "\${out}/${name}/__init__.py"
-        bins.s6-cat
-      ]
-    ];
-    in pkgs.python3Packages.buildPythonPackage {
+            setup(
+              name='%s',
+              packages=['%s']
+            )
+          ''
+          name
+          name
+        ]
+        "if"
+        [
+          # redirect stdin to the init py
+          "redirfd"
+          "-w"
+          "1"
+          "\${out}/${name}/__init__.py"
+          bins.s6-cat
+        ]
+      ];
+    in
+    pythonPackages.buildPythonPackage {
       inherit name;
       src = srcTree;
-      propagatedBuildInputs = libraries pkgs.python3Packages;
+      propagatedBuildInputs = libraries pythonPackages;
       doCheck = false;
     };
 
 
-in {
+  ghcBins = libraries: depot.nix.getBins (pkgs.ghc.withPackages (_: libraries)) [ "runghc" ];
+
+  writeHaskellInteractive = name: { libraries, ghcArgs ? [ ] }: path:
+    depot.nix.writeExecline name { } ([
+      (ghcBins libraries).runghc
+      "--"
+    ] ++ ghcArgs ++ [
+      "--"
+      path
+    ]);
+
+in
+{
   inherit
     python3
     python3Lib
+    writeHaskellInteractive
     ;
 }
diff --git a/users/Profpatsch/writers/tests/default.nix b/users/Profpatsch/writers/tests/default.nix
index dc760af9e1..879aae82f7 100644
--- a/users/Profpatsch/writers/tests/default.nix
+++ b/users/Profpatsch/writers/tests/default.nix
@@ -10,38 +10,46 @@ let
     coreutils
     ;
 
-  run = drv: depot.nix.runExecline.local "run-${drv.name}" {} [
-    "if" [ drv ]
-    "importas" "out" "out"
-    "${coreutils}/bin/touch" "$out"
+  run = drv: depot.nix.runExecline.local "run-${drv.name}" { } [
+    "if"
+    [ drv ]
+    "importas"
+    "out"
+    "out"
+    "${coreutils}/bin/touch"
+    "$out"
   ];
 
-  pythonTransitiveLib = python3Lib {
-    name = "transitive";
-  } ''
+  pythonTransitiveLib = python3Lib
+    {
+      name = "transitive";
+    } ''
     def transitive(s):
       return s + " 1 2 3"
   '';
 
-  pythonTestLib = python3Lib {
-    name = "test_lib";
-    libraries = _: [ pythonTransitiveLib ];
-  } ''
+  pythonTestLib = python3Lib
+    {
+      name = "test_lib";
+      libraries = _: [ pythonTransitiveLib ];
+    } ''
     import transitive
     def test():
       return transitive.transitive("test")
   '';
 
-  pythonWithLib = run (python3 {
-    name = "python-with-lib";
-    libraries = _: [ pythonTestLib ];
-  } ''
+  pythonWithLib = run (python3
+    {
+      name = "python-with-lib";
+      libraries = _: [ pythonTestLib ];
+    } ''
     import test_lib
 
-    assert(test_lib.test() == "test 1 2 3")
+    assert test_lib.test() == "test 1 2 3"
   '');
 
-in depot.nix.readTree.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   inherit
     pythonWithLib
     ;
diff --git a/users/Profpatsch/ytextr/README.md b/users/Profpatsch/ytextr/README.md
new file mode 100644
index 0000000000..f1e40d8e68
--- /dev/null
+++ b/users/Profpatsch/ytextr/README.md
@@ -0,0 +1,5 @@
+# ytextr
+
+Wrapper around `yt-dlp` for downloading videos in good default quality with good default settings.
+
+Will always download the most up-to-date `yt-dlp` first, because the software usually stops working after a few weeks and needs to be updated, so just using `<nixpkgs>` often fails.
diff --git a/users/Profpatsch/ytextr/create-symlink-farm.nix b/users/Profpatsch/ytextr/create-symlink-farm.nix
index 583a3a90f5..7b3a45b916 100644
--- a/users/Profpatsch/ytextr/create-symlink-farm.nix
+++ b/users/Profpatsch/ytextr/create-symlink-farm.nix
@@ -1,9 +1,10 @@
 {
   # list of package attribute names to get at run time
-  packageNamesAtRuntimeJsonPath,
+  packageNamesAtRuntimeJsonPath
+,
 }:
 let
-  pkgs = import <nixpkgs> {};
+  pkgs = import <nixpkgs> { };
 
   getPkg = pkgName: pkgs.${pkgName};
 
@@ -12,7 +13,7 @@ let
   runtime = map getPkg packageNamesAtRuntime;
 
 in
-  pkgs.symlinkJoin {
-    name = "symlink-farm";
-    paths = runtime;
-  }
+pkgs.symlinkJoin {
+  name = "symlink-farm";
+  paths = runtime;
+}
diff --git a/users/Profpatsch/ytextr/default.nix b/users/Profpatsch/ytextr/default.nix
index dba6bbb8b4..3f3f073113 100644
--- a/users/Profpatsch/ytextr/default.nix
+++ b/users/Profpatsch/ytextr/default.nix
@@ -12,48 +12,71 @@
 
 let
   bins = depot.nix.getBins pkgs.nix [ "nix-build" ]
-      // depot.nix.getBins pkgs.bubblewrap [ "bwrap" ];
+    // depot.nix.getBins pkgs.bubblewrap [ "bwrap" ];
 
   # Run a command, with the given packages in scope, and `packageNamesAtRuntime` being fetched at the start in the given nix `channel`.
-  nix-run-with-channel = {
-    # The channel to get `packageNamesAtRuntime` from
-    channel,
-    # executable to run with `packageNamesAtRuntime` in PATH
-    # and the argv
-    executable,
-    # A list of nixpkgs package attribute names that should be put into PATH when running `command`.
-    packageNamesAtRuntime,
-  }: depot.nix.writeExecline "nix-run-with-channel-${channel}" {} [
-    # TODO: prevent race condition by writing a temporary gc root
-    "backtick" "-iE" "storepath" [
-      bins.nix-build
-        "-I" "nixpkgs=channel:${channel}"
+  nix-run-with-channel =
+    {
+      # The channel to get `packageNamesAtRuntime` from
+      channel
+    , # executable to run with `packageNamesAtRuntime` in PATH
+      # and the argv
+      executable
+    , # A list of nixpkgs package attribute names that should be put into PATH when running `command`.
+      packageNamesAtRuntime
+    ,
+    }: depot.nix.writeExecline "nix-run-with-channel-${channel}" { } [
+      # TODO: prevent race condition by writing a temporary gc root
+      "backtick"
+      "-iE"
+      "storepath"
+      [
+        bins.nix-build
+        "-I"
+        "nixpkgs=channel:${channel}"
         "--arg"
-          "packageNamesAtRuntimeJsonPath"
-          (pkgs.writeText "packageNamesAtRuntime.json" (builtins.toJSON packageNamesAtRuntime))
+        "packageNamesAtRuntimeJsonPath"
+        (pkgs.writeText "packageNamesAtRuntime.json" (builtins.toJSON packageNamesAtRuntime))
         ./create-symlink-farm.nix
-    ]
-    "importas" "-ui" "PATH" "PATH"
-    "export" "PATH" "\${storepath}/bin:\${PATH}"
-    executable "$@"
-  ];
+      ]
+      "importas"
+      "-ui"
+      "PATH"
+      "PATH"
+      "export"
+      "PATH"
+      "\${storepath}/bin:\${PATH}"
+      executable
+      "$@"
+    ];
 
-in nix-run-with-channel {
+in
+nix-run-with-channel {
   channel = "nixos-unstable";
   packageNamesAtRuntime = [ "yt-dlp" ];
-  executable = depot.nix.writeExecline "ytextr" { readNArgs = 1; } [
-    "getcwd" "-E" "cwd"
+  executable = depot.nix.writeExecline "ytextr" { } [
+    "getcwd"
+    "-E"
+    "cwd"
     bins.bwrap
-      "--ro-bind" "/nix/store" "/nix/store"
-      "--ro-bind" "/etc" "/etc"
-      "--bind" "$cwd" "$cwd"
-        "yt-dlp"
-        "--no-playlist"
-        "--write-sub"
-        "--all-subs"
-        "--embed-subs"
-        "--merge-output-format" "mkv"
-        "-f" "bestvideo[height<=?1080]+bestaudio/best"
-        "$1"
+    "--ro-bind"
+    "/nix/store"
+    "/nix/store"
+    "--ro-bind"
+    "/etc"
+    "/etc"
+    "--bind"
+    "$cwd"
+    "$cwd"
+    "yt-dlp"
+    "--no-playlist"
+    "--write-sub"
+    "--all-subs"
+    "--embed-subs"
+    "--merge-output-format"
+    "mkv"
+    "-f"
+    "bestvideo[height<=?1080]+bestaudio/best"
+    "$@"
   ];
 }
diff --git a/users/aaqaishtyaq/OWNERS b/users/aaqaishtyaq/OWNERS
new file mode 100644
index 0000000000..99c4a74244
--- /dev/null
+++ b/users/aaqaishtyaq/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+aaqaishtyaq
diff --git a/users/aspen/OWNERS b/users/aspen/OWNERS
new file mode 100644
index 0000000000..3dff20d574
--- /dev/null
+++ b/users/aspen/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+aspen
diff --git a/users/aspen/achilles/.envrc b/users/aspen/achilles/.envrc
new file mode 100644
index 0000000000..b80e28b4b8
--- /dev/null
+++ b/users/aspen/achilles/.envrc
@@ -0,0 +1,2 @@
+source_up
+eval "$(lorri direnv)"
diff --git a/users/grfn/achilles/.gitignore b/users/aspen/achilles/.gitignore
index ea8c4bf7f3..ea8c4bf7f3 100644
--- a/users/grfn/achilles/.gitignore
+++ b/users/aspen/achilles/.gitignore
diff --git a/users/aspen/achilles/Cargo.lock b/users/aspen/achilles/Cargo.lock
new file mode 100644
index 0000000000..3c767db1e4
--- /dev/null
+++ b/users/aspen/achilles/Cargo.lock
@@ -0,0 +1,885 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "achilles"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bimap",
+ "clap",
+ "crate-root",
+ "derive_more",
+ "inkwell",
+ "itertools",
+ "lazy_static",
+ "llvm-sys",
+ "nom",
+ "nom-trace",
+ "pratt",
+ "pretty_assertions",
+ "proptest",
+ "test-strategy",
+ "thiserror",
+ "void",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
+
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bimap"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b"
+
+[[package]]
+name = "bit-set"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitvec"
+version = "0.19.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
+dependencies = [
+ "funty",
+ "radium",
+ "tap",
+ "wyz",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "3.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_lex",
+ "indexmap",
+ "strsim",
+ "termcolor",
+ "textwrap",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "crate-root"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59c6fe4622b269032d2c5140a592d67a9c409031d286174fcde172fbed86f0d3"
+
+[[package]]
+name = "ctor"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn",
+]
+
+[[package]]
+name = "diff"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
+
+[[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+
+[[package]]
+name = "fastrand"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "funty"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
+
+[[package]]
+name = "getrandom"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "inkwell"
+version = "0.1.0"
+source = "git+https://github.com/TheDan64/inkwell?branch=master#6ab2b19e1b90be55fa4f9f056f29bd1ed557b990"
+dependencies = [
+ "either",
+ "inkwell_internals",
+ "libc",
+ "llvm-sys",
+ "once_cell",
+ "parking_lot",
+]
+
+[[package]]
+name = "inkwell_internals"
+version = "0.5.0"
+source = "git+https://github.com/TheDan64/inkwell?branch=master#6ab2b19e1b90be55fa4f9f056f29bd1ed557b990"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lexical-core"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
+dependencies = [
+ "arrayvec",
+ "bitflags",
+ "cfg-if",
+ "ryu",
+ "static_assertions",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.125"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
+
+[[package]]
+name = "llvm-sys"
+version = "110.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b918288a585ac36703abefcbc5d4c43137b604ec0c2d39abefb55e25c7501dc"
+dependencies = [
+ "cc",
+ "lazy_static",
+ "libc",
+ "regex",
+ "semver 0.11.0",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "memchr"
+version = "2.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+
+[[package]]
+name = "nom"
+version = "6.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
+dependencies = [
+ "bitvec",
+ "funty",
+ "lexical-core",
+ "memchr",
+ "version_check",
+]
+
+[[package]]
+name = "nom-trace"
+version = "0.2.1"
+source = "git+https://github.com/glittershark/nom-trace?branch=nom-6#6168d2e15cc51efd12d80260159b76a764dba138"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+
+[[package]]
+name = "output_vt100"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys",
+]
+
+[[package]]
+name = "pest"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+dependencies = [
+ "ucd-trie",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
+[[package]]
+name = "pratt"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31bbc12f7936a7b195790dd6d9b982b66c54f45ff6766decf25c44cac302dce"
+
+[[package]]
+name = "pretty_assertions"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b"
+dependencies = [
+ "ansi_term",
+ "ctor",
+ "diff",
+ "output_vt100",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "proptest"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
+dependencies = [
+ "bit-set",
+ "bitflags",
+ "byteorder",
+ "lazy_static",
+ "num-traits",
+ "quick-error 2.0.1",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax",
+ "rusty-fork",
+ "tempfile",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
+[[package]]
+name = "quote"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "radium"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver 1.0.9",
+]
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error 1.2.3",
+ "tempfile",
+ "wait-timeout",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "semver"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
+
+[[package]]
+name = "semver-parser"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
+dependencies = [
+ "pest",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "structmeta"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bd9c2155aa89fb2c2cb87d99a610c689e7c47099b3e9f1c8a8f53faf4e3d2e3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "structmeta-derive",
+ "syn",
+]
+
+[[package]]
+name = "structmeta-derive"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bafede0d0a2f21910f36d47b1558caae3076ed80f6f3ad0fc85a91e6ba7e5938"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "tap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+
+[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "test-strategy"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22c726321a7c108ca1de4ed2e6a362ead7193ecfbe0b326c5dff602b65a09e6a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "structmeta",
+ "syn",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
+
+[[package]]
+name = "thiserror"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
+name = "wyz"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
diff --git a/users/grfn/achilles/Cargo.toml b/users/aspen/achilles/Cargo.toml
index f091399a0d..f091399a0d 100644
--- a/users/grfn/achilles/Cargo.toml
+++ b/users/aspen/achilles/Cargo.toml
diff --git a/users/grfn/achilles/ach/.gitignore b/users/aspen/achilles/ach/.gitignore
index ac5296ebbd..ac5296ebbd 100644
--- a/users/grfn/achilles/ach/.gitignore
+++ b/users/aspen/achilles/ach/.gitignore
diff --git a/users/grfn/achilles/ach/Makefile b/users/aspen/achilles/ach/Makefile
index 3a8cd2865e..3a8cd2865e 100644
--- a/users/grfn/achilles/ach/Makefile
+++ b/users/aspen/achilles/ach/Makefile
diff --git a/users/grfn/achilles/ach/externs.ach b/users/aspen/achilles/ach/externs.ach
index faf8ce90e3..faf8ce90e3 100644
--- a/users/grfn/achilles/ach/externs.ach
+++ b/users/aspen/achilles/ach/externs.ach
diff --git a/users/grfn/achilles/ach/functions.ach b/users/aspen/achilles/ach/functions.ach
index dc6e7a1f3e..dc6e7a1f3e 100644
--- a/users/grfn/achilles/ach/functions.ach
+++ b/users/aspen/achilles/ach/functions.ach
diff --git a/users/grfn/achilles/ach/simple.ach b/users/aspen/achilles/ach/simple.ach
index 20f1677235..20f1677235 100644
--- a/users/grfn/achilles/ach/simple.ach
+++ b/users/aspen/achilles/ach/simple.ach
diff --git a/users/grfn/achilles/ach/units.ach b/users/aspen/achilles/ach/units.ach
index 70635d978c..70635d978c 100644
--- a/users/grfn/achilles/ach/units.ach
+++ b/users/aspen/achilles/ach/units.ach
diff --git a/users/aspen/achilles/default.nix b/users/aspen/achilles/default.nix
new file mode 100644
index 0000000000..714be60728
--- /dev/null
+++ b/users/aspen/achilles/default.nix
@@ -0,0 +1,27 @@
+{ depot, pkgs, ... }:
+
+let
+  llvmPackages = pkgs.llvmPackages_11;
+in
+
+depot.third_party.naersk.buildPackage {
+  src = ./.;
+
+  buildInputs = [
+    llvmPackages.clang
+    llvmPackages.llvm
+    llvmPackages.bintools
+    llvmPackages.libclang.lib
+  ] ++ (with pkgs; [
+    zlib
+    ncurses
+    libxml2
+    libffi
+    pkg-config
+  ]);
+
+  doCheck = true;
+
+  # Trouble linking against LLVM, maybe since rustc's llvmPackages got bumped?
+  meta.ci.skip = true;
+}
diff --git a/users/aspen/achilles/shell.nix b/users/aspen/achilles/shell.nix
new file mode 100644
index 0000000000..1434cf8a32
--- /dev/null
+++ b/users/aspen/achilles/shell.nix
@@ -0,0 +1,18 @@
+with (import ../../.. { }).third_party.nixpkgs;
+
+mkShell {
+  buildInputs = [
+    clang_11
+    llvm_11.lib
+    llvmPackages_11.bintools
+    llvmPackages_11.clang
+    llvmPackages_11.libclang.lib
+    zlib
+    ncurses
+    libxml2
+    libffi
+    pkg-config
+  ];
+
+  LLVM_SYS_110_PREFIX = llvmPackages_11.bintools;
+}
diff --git a/users/grfn/achilles/src/ast/hir.rs b/users/aspen/achilles/src/ast/hir.rs
index cdfaef567d..cdfaef567d 100644
--- a/users/grfn/achilles/src/ast/hir.rs
+++ b/users/aspen/achilles/src/ast/hir.rs
diff --git a/users/grfn/achilles/src/ast/mod.rs b/users/aspen/achilles/src/ast/mod.rs
index 5438d29d2c..5438d29d2c 100644
--- a/users/grfn/achilles/src/ast/mod.rs
+++ b/users/aspen/achilles/src/ast/mod.rs
diff --git a/users/grfn/achilles/src/codegen/llvm.rs b/users/aspen/achilles/src/codegen/llvm.rs
index 9a71ac954e..9a71ac954e 100644
--- a/users/grfn/achilles/src/codegen/llvm.rs
+++ b/users/aspen/achilles/src/codegen/llvm.rs
diff --git a/users/grfn/achilles/src/codegen/mod.rs b/users/aspen/achilles/src/codegen/mod.rs
index 8ef057dba0..8ef057dba0 100644
--- a/users/grfn/achilles/src/codegen/mod.rs
+++ b/users/aspen/achilles/src/codegen/mod.rs
diff --git a/users/grfn/achilles/src/commands/check.rs b/users/aspen/achilles/src/commands/check.rs
index 0bea482c14..0bea482c14 100644
--- a/users/grfn/achilles/src/commands/check.rs
+++ b/users/aspen/achilles/src/commands/check.rs
diff --git a/users/grfn/achilles/src/commands/compile.rs b/users/aspen/achilles/src/commands/compile.rs
index be8767575a..be8767575a 100644
--- a/users/grfn/achilles/src/commands/compile.rs
+++ b/users/aspen/achilles/src/commands/compile.rs
diff --git a/users/aspen/achilles/src/commands/eval.rs b/users/aspen/achilles/src/commands/eval.rs
new file mode 100644
index 0000000000..efd7399ed1
--- /dev/null
+++ b/users/aspen/achilles/src/commands/eval.rs
@@ -0,0 +1,28 @@
+use clap::Clap;
+
+use crate::{codegen, interpreter, parser, tc, Result};
+
+/// Evaluate an expression and print its result
+#[derive(Clap)]
+pub struct Eval {
+    /// JIT-compile with LLVM instead of interpreting
+    #[clap(long)]
+    jit: bool,
+
+    /// Expression to evaluate
+    expr: String,
+}
+
+impl Eval {
+    pub fn run(self) -> Result<()> {
+        let (_, parsed) = parser::expr(&self.expr)?;
+        let hir = tc::typecheck_expr(parsed)?;
+        let result = if self.jit {
+            codegen::jit_eval::<i64>(&hir)?.into()
+        } else {
+            interpreter::eval(&hir)?
+        };
+        println!("{}", result);
+        Ok(())
+    }
+}
diff --git a/users/grfn/achilles/src/commands/mod.rs b/users/aspen/achilles/src/commands/mod.rs
index fd0a822708..fd0a822708 100644
--- a/users/grfn/achilles/src/commands/mod.rs
+++ b/users/aspen/achilles/src/commands/mod.rs
diff --git a/users/grfn/achilles/src/common/env.rs b/users/aspen/achilles/src/common/env.rs
index 59a5e46c46..59a5e46c46 100644
--- a/users/grfn/achilles/src/common/env.rs
+++ b/users/aspen/achilles/src/common/env.rs
diff --git a/users/grfn/achilles/src/common/error.rs b/users/aspen/achilles/src/common/error.rs
index 51575a895e..51575a895e 100644
--- a/users/grfn/achilles/src/common/error.rs
+++ b/users/aspen/achilles/src/common/error.rs
diff --git a/users/grfn/achilles/src/common/mod.rs b/users/aspen/achilles/src/common/mod.rs
index 8368a6dd18..8368a6dd18 100644
--- a/users/grfn/achilles/src/common/mod.rs
+++ b/users/aspen/achilles/src/common/mod.rs
diff --git a/users/grfn/achilles/src/common/namer.rs b/users/aspen/achilles/src/common/namer.rs
index 016e9f6ed9..016e9f6ed9 100644
--- a/users/grfn/achilles/src/common/namer.rs
+++ b/users/aspen/achilles/src/common/namer.rs
diff --git a/users/grfn/achilles/src/compiler.rs b/users/aspen/achilles/src/compiler.rs
index 45b215473d..45b215473d 100644
--- a/users/grfn/achilles/src/compiler.rs
+++ b/users/aspen/achilles/src/compiler.rs
diff --git a/users/grfn/achilles/src/interpreter/error.rs b/users/aspen/achilles/src/interpreter/error.rs
index 268d6f479a..268d6f479a 100644
--- a/users/grfn/achilles/src/interpreter/error.rs
+++ b/users/aspen/achilles/src/interpreter/error.rs
diff --git a/users/grfn/achilles/src/interpreter/mod.rs b/users/aspen/achilles/src/interpreter/mod.rs
index 70df7a0724..70df7a0724 100644
--- a/users/grfn/achilles/src/interpreter/mod.rs
+++ b/users/aspen/achilles/src/interpreter/mod.rs
diff --git a/users/grfn/achilles/src/interpreter/value.rs b/users/aspen/achilles/src/interpreter/value.rs
index 272d1167a3..272d1167a3 100644
--- a/users/grfn/achilles/src/interpreter/value.rs
+++ b/users/aspen/achilles/src/interpreter/value.rs
diff --git a/users/grfn/achilles/src/main.rs b/users/aspen/achilles/src/main.rs
index 5ae1b59b3a..5ae1b59b3a 100644
--- a/users/grfn/achilles/src/main.rs
+++ b/users/aspen/achilles/src/main.rs
diff --git a/users/grfn/achilles/src/parser/expr.rs b/users/aspen/achilles/src/parser/expr.rs
index f596b18970..b18ce4a0dc 100644
--- a/users/grfn/achilles/src/parser/expr.rs
+++ b/users/aspen/achilles/src/parser/expr.rs
@@ -1,9 +1,8 @@
 use std::borrow::Cow;
 
-use nom::alt;
 use nom::character::complete::{digit1, multispace0, multispace1};
 use nom::{
-    call, char, complete, delimited, do_parse, flat_map, many0, map, named, opt, parse_to,
+    alt, call, char, complete, delimited, do_parse, flat_map, many0, map, named, opt, parse_to,
     preceded, separated_list0, separated_list1, tag, tuple,
 };
 use pratt::{Affix, Associativity, PrattParser, Precedence};
diff --git a/users/grfn/achilles/src/parser/macros.rs b/users/aspen/achilles/src/parser/macros.rs
index 406e5c0e69..406e5c0e69 100644
--- a/users/grfn/achilles/src/parser/macros.rs
+++ b/users/aspen/achilles/src/parser/macros.rs
diff --git a/users/grfn/achilles/src/parser/mod.rs b/users/aspen/achilles/src/parser/mod.rs
index e088cbca10..e088cbca10 100644
--- a/users/grfn/achilles/src/parser/mod.rs
+++ b/users/aspen/achilles/src/parser/mod.rs
diff --git a/users/grfn/achilles/src/parser/type_.rs b/users/aspen/achilles/src/parser/type_.rs
index b80f0e0860..b80f0e0860 100644
--- a/users/grfn/achilles/src/parser/type_.rs
+++ b/users/aspen/achilles/src/parser/type_.rs
diff --git a/users/grfn/achilles/src/parser/util.rs b/users/aspen/achilles/src/parser/util.rs
index bb53fb7fff..bb53fb7fff 100644
--- a/users/grfn/achilles/src/parser/util.rs
+++ b/users/aspen/achilles/src/parser/util.rs
diff --git a/users/grfn/achilles/src/passes/hir/mod.rs b/users/aspen/achilles/src/passes/hir/mod.rs
index 872c449eb0..872c449eb0 100644
--- a/users/grfn/achilles/src/passes/hir/mod.rs
+++ b/users/aspen/achilles/src/passes/hir/mod.rs
diff --git a/users/grfn/achilles/src/passes/hir/monomorphize.rs b/users/aspen/achilles/src/passes/hir/monomorphize.rs
index 251a988f4f..251a988f4f 100644
--- a/users/grfn/achilles/src/passes/hir/monomorphize.rs
+++ b/users/aspen/achilles/src/passes/hir/monomorphize.rs
diff --git a/users/grfn/achilles/src/passes/hir/strip_positive_units.rs b/users/aspen/achilles/src/passes/hir/strip_positive_units.rs
index 85ee1cce48..85ee1cce48 100644
--- a/users/grfn/achilles/src/passes/hir/strip_positive_units.rs
+++ b/users/aspen/achilles/src/passes/hir/strip_positive_units.rs
diff --git a/users/grfn/achilles/src/passes/mod.rs b/users/aspen/achilles/src/passes/mod.rs
index 306869bef1..306869bef1 100644
--- a/users/grfn/achilles/src/passes/mod.rs
+++ b/users/aspen/achilles/src/passes/mod.rs
diff --git a/users/grfn/achilles/src/tc/mod.rs b/users/aspen/achilles/src/tc/mod.rs
index 5825bab1fb..5825bab1fb 100644
--- a/users/grfn/achilles/src/tc/mod.rs
+++ b/users/aspen/achilles/src/tc/mod.rs
diff --git a/users/grfn/achilles/tests/compile.rs b/users/aspen/achilles/tests/compile.rs
index 0f1086bfd8..0f1086bfd8 100644
--- a/users/grfn/achilles/tests/compile.rs
+++ b/users/aspen/achilles/tests/compile.rs
diff --git a/users/grfn/bbbg/.clj-kondo/config.edn b/users/aspen/bbbg/.clj-kondo/config.edn
index 8faddb77ec..8faddb77ec 100644
--- a/users/grfn/bbbg/.clj-kondo/config.edn
+++ b/users/aspen/bbbg/.clj-kondo/config.edn
diff --git a/users/grfn/achilles/.envrc b/users/aspen/bbbg/.envrc
index 051d09d292..051d09d292 100644
--- a/users/grfn/achilles/.envrc
+++ b/users/aspen/bbbg/.envrc
diff --git a/users/grfn/bbbg/.gitignore b/users/aspen/bbbg/.gitignore
index 99dbfc4436..99dbfc4436 100644
--- a/users/grfn/bbbg/.gitignore
+++ b/users/aspen/bbbg/.gitignore
diff --git a/users/grfn/bbbg/Makefile b/users/aspen/bbbg/Makefile
index fc45477984..fc45477984 100644
--- a/users/grfn/bbbg/Makefile
+++ b/users/aspen/bbbg/Makefile
diff --git a/users/aspen/bbbg/README.md b/users/aspen/bbbg/README.md
new file mode 100644
index 0000000000..41f59319cb
--- /dev/null
+++ b/users/aspen/bbbg/README.md
@@ -0,0 +1,129 @@
+# Brooklyn-Based Board Gaming signup sheet
+
+This directory contains a small web application that acts as a signup
+sheet and attendee tracking system for [my local board gaming
+meetup](https://www.meetup.com/brooklyn-based-board-gaming/).
+
+## Development
+
+### Installing dependencies
+
+#### With Nix + Docker ("blessed way")
+
+Prerequisites:
+
+-   [Nix](https://nixos.org/)
+-   [lorri](https://github.com/nix-community/lorri)
+-   [Docker](https://www.docker.com/)
+
+From this directory in a full checkout of depot, run the following
+commands to install all development dependencies:
+
+``` shell-session
+$ pwd
+/path/to/depot/users/aspen/bbbg
+$ direnv allow
+$ lorri watch --once # Wait for a single nix shell build
+```
+
+Then, to run a docker container with the development database:
+
+``` shell-session
+$ pwd
+/path/to/depot/users/aspen/bbbg
+$ arion up -d
+```
+
+#### Choose-your-own-adventure
+
+Note that the **authoritative** source for dev dependencies is the `shell.nix`
+file in this directory - those may diverge from what's written here; if so
+follow those versions rather than these.
+
+-   Install the [clojure command-line
+    tools](https://clojure.org/guides/getting_started), with openjdk 11
+-   Install and run a postgresql 12 database, with:
+    -   A user with superuser priveleges, the username `bbbg` and the
+        password `password`
+    -   A database called `bbbg` owned by that user.
+-   Export the following environment variables in a context visible by
+    whatever method you use to run the application:
+    -   `PGHOST=localhost`
+    -   `PGUSER=bbbg`
+    -   `PGDATABASE=bbbg`
+    -   `PGPASSWORD=bbbg`
+
+### Running the application
+
+Before running the app, you'll need an oauth2 client-id and client secret for a
+Discord app. The application can either load those from a
+[pass](https://www.passwordstore.org/) password store, or read them from
+plaintext files in a directory. In either case, they should be accessible at the
+paths `bbbg/discord-client-id` and `bbbg/discord-client-secret` respectively.
+
+#### From the command line
+
+``` shell-session
+$ clj -A:dev
+Clojure 1.11.0-alpha3
+user=> (require 'bbbg.core)
+nil
+user=> ;; Optionally, if you're using a directory with plaintext files for the discord client ID and client secret:
+user=> (bbbg.util.dev-secrets/set-backend! [:dir "/path/to/that/directory"])
+user=> (bbbg.core/run-dev)
+##<SystemMap>
+user=> (bbbg.db/migrate! (:db bbbg.core/system))
+11:57:26.536 [main] INFO  migratus.core - Starting migrations {  }
+11:57:26.538 [main] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... {  }
+11:57:26.883 [main] INFO  com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.impossibl.postgres.jdbc.PGDirectConnection@3cae770e {  }
+11:57:26.884 [main] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. {  }
+11:57:26.923 [main] INFO  migratus.core - Ending migrations {  }
+nil
+```
+
+This will run a web server for the application listening at
+<http://localhost:8888>
+
+#### In Emacs, with [CIDER](https://docs.cider.mx/cider/index.html) + [direnv](https://github.com/wbolster/emacs-direnv)
+
+Open `//users/aspen/bbbg/src/bbbg/core.clj` in a buffer, then follow the
+instructions at the end of the file
+
+## Deployment
+
+### With nix+terraform
+
+Deployment configuration is located in the `tf.nix` file, which is
+currently tightly coupled to my own infrastructure and AWS account but
+could hypothetically be adjusted to be general-purpose.
+
+To deploy a new version of the application, after following "installing
+dependencies" above, run the following command in a context with ec2
+credentials available:
+
+``` shell-session
+$ terraform apply
+```
+
+The current deploy configuration includes:
+
+-   An ec2 instance running nixos, with a postgresql database and the
+    bbbg application running as a service, behind nginx with an
+    auto-renewing letsencrypt cert
+-   The DNS A record for `bbbg.gws.fyi` pointing at that ec2 instance,
+    in the cloudflare zone for `gws.fyi`
+
+### Otherwise
+
+Β―\\\_(ツ)_/Β―
+
+You'll need:
+
+-   An uberjar for bbbg; the canonical way of building that is `nix-build
+    /path/to/depot -A users.aspen.bbbg.server-jar` but I\'m not sure how that
+    works outside of nix
+-   A postgresql database
+-   Environment variables telling the app how to connect to that
+    database. See `config.systemd.services.bbbg-server.environment` in
+    `module.nix` for which env vars are currently being exported by the
+    NixOS module that runs the production version of the app
diff --git a/users/grfn/bbbg/arion-compose.nix b/users/aspen/bbbg/arion-compose.nix
index c8a6dd156d..c8a6dd156d 100644
--- a/users/grfn/bbbg/arion-compose.nix
+++ b/users/aspen/bbbg/arion-compose.nix
diff --git a/users/aspen/bbbg/arion-pkgs.nix b/users/aspen/bbbg/arion-pkgs.nix
new file mode 100644
index 0000000000..c6d603be2a
--- /dev/null
+++ b/users/aspen/bbbg/arion-pkgs.nix
@@ -0,0 +1,2 @@
+let depot = import ../../.. { };
+in depot.third_party.nixpkgs
diff --git a/users/aspen/bbbg/default.nix b/users/aspen/bbbg/default.nix
new file mode 100644
index 0000000000..6afb68353c
--- /dev/null
+++ b/users/aspen/bbbg/default.nix
@@ -0,0 +1,82 @@
+args@{ depot, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  inherit (depot.third_party) gitignoreSource;
+
+  deps = import ./deps.nix {
+    inherit (pkgs) fetchMavenArtifact fetchgit lib;
+  };
+in
+rec {
+  meta.ci.targets = [
+    "db-util"
+    "server"
+    "tf"
+  ];
+
+  depsPaths = deps.makePaths { };
+
+  resources = builtins.filterSource (_: type: type != "symlink") ./resources;
+
+  classpath.dev = concatStringsSep ":" (
+    (map gitignoreSource [ ./src ./test ./env/dev ]) ++ [ resources ] ++ depsPaths
+  );
+
+  classpath.test = concatStringsSep ":" (
+    (map gitignoreSource [ ./src ./test ./env/test ]) ++ [ resources ] ++ depsPaths
+  );
+
+  classpath.prod = concatStringsSep ":" (
+    (map gitignoreSource [ ./src ./env/prod ]) ++ [ resources ] ++ depsPaths
+  );
+
+  testClojure = pkgs.writeShellScript "test-clojure" ''
+    export HOME=$(pwd)
+    ${pkgs.clojure}/bin/clojure -Scp ${depsPaths}
+  '';
+
+  mkJar = name: opts:
+    with pkgs;
+    assert (hasSuffix ".jar" name);
+    stdenv.mkDerivation rec {
+      inherit name;
+      dontUnpack = true;
+      buildPhase = ''
+        export HOME=$(pwd)
+        cp ${./pom.xml} pom.xml
+        cp ${./deps.edn} deps.edn
+        ${clojure}/bin/clojure \
+          -Scp ${classpath.prod} \
+          -A:uberjar \
+          ${name} \
+          -C ${opts}
+      '';
+
+      doCheck = true;
+
+      checkPhase = ''
+        echo "checking for existence of ${name}"
+        [ -f ${name} ]
+      '';
+
+      installPhase = ''
+        cp ${name} $out
+      '';
+    };
+
+  db-util-jar = mkJar "bbbg-db-util.jar" "-m bbbg.db";
+
+  db-util = pkgs.writeShellScriptBin "bbbg-db-util" ''
+    exec ${pkgs.openjdk17_headless}/bin/java -jar ${db-util-jar} "$@"
+  '';
+
+  server-jar = mkJar "bbbg-server.jar" "-m bbbg.core";
+
+  server = pkgs.writeShellScriptBin "bbbg-server" ''
+    exec ${pkgs.openjdk17_headless}/bin/java -jar ${server-jar} "$@"
+  '';
+
+  tf = import ./tf.nix args;
+}
diff --git a/users/grfn/bbbg/deps.edn b/users/aspen/bbbg/deps.edn
index 39ce843c22..39ce843c22 100644
--- a/users/grfn/bbbg/deps.edn
+++ b/users/aspen/bbbg/deps.edn
diff --git a/users/aspen/bbbg/deps.nix b/users/aspen/bbbg/deps.nix
new file mode 100644
index 0000000000..02f5ecb468
--- /dev/null
+++ b/users/aspen/bbbg/deps.nix
@@ -0,0 +1,1494 @@
+# generated by clj2nix-1.1.0-rc
+{ fetchMavenArtifact, fetchgit, lib }:
+
+let
+  repos = [
+    "https://repo1.maven.org/maven2/"
+    "https://repo.clojars.org/"
+  ];
+
+in
+rec {
+  makePaths = { extraClasspaths ? [ ] }:
+    if (builtins.typeOf extraClasspaths != "list")
+    then builtins.throw "extraClasspaths must be of type 'list'!"
+    else (lib.concatMap
+      (dep:
+        builtins.map
+          (path:
+            if builtins.isString path then
+              path
+            else if builtins.hasAttr "jar" path then
+              path.jar
+            else if builtins.hasAttr "outPath" path then
+              path.outPath
+            else
+              path
+          )
+          dep.paths)
+      packages) ++ extraClasspaths;
+  makeClasspaths = { extraClasspaths ? [ ] }:
+    if (builtins.typeOf extraClasspaths != "list")
+    then builtins.throw "extraClasspaths must be of type 'list'!"
+    else builtins.concatStringsSep ":" (makePaths { inherit extraClasspaths; });
+  packageSources = builtins.map (dep: dep.src) packages;
+  packages = [
+    rec {
+      name = "cambium.logback.json/cambium";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cambium.logback.json";
+        groupId = "cambium";
+        sha512 = "8e3f32bc1e11071ddc8700204333ba653585de7985c03d14c351950a7896975092e9deffd658bfec7b0b8b9cc72dc025d8e5179a185bd25da26e500218ec37a5";
+        version = "0.4.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clojure/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clojure";
+        groupId = "org.clojure";
+        sha512 = "a242514f623a17601b360886563c4a4fe09335e4e16522ac42bbcacda073ae77651cfed446daae7fe74061bb7dff5adc454769c0edc0ded350136c3c707e75b9";
+        version = "1.11.0-alpha3";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "joda-time/joda-time";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "joda-time";
+        groupId = "joda-time";
+        sha512 = "012fb9aa9b00b456f72a92374855a7f062f8617c026c436eee2cda67dffa2f8622201909c0f4f454bb346ff5a3ed6f60c236fafb19fa66f612d9861f27b38d3a";
+        version = "2.10";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-codec/commons-codec";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-codec";
+        groupId = "commons-codec";
+        sha512 = "da30a716770795fce390e4dd340a8b728f220c6572383ffef55bd5839655d5611fcc06128b2144f6cdcb36f53072a12ec80b04afee787665e7ad0b6e888a6787";
+        version = "1.15";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "HikariCP/com.zaxxer";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "HikariCP";
+        groupId = "com.zaxxer";
+        sha512 = "a41b6d8b1c4656e633459824f10320965976eeead01bd5cb24911040073181730e61feb797aef89d9e01c922e89cb58654f364df0a6b1bf62ab3e6f9cc367d77";
+        version = "5.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-devel/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-devel";
+        groupId = "ring";
+        sha512 = "79a1ec9f9d03aa4fa0426353970b13468ee65ce314b51ab7a2682212a196a9b5c985eacdee5dbc6ff2f1b536a4e06d0e85e9dd7cc9a49958735c9c4e6d427fd5";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient";
+        groupId = "io.prometheus";
+        sha512 = "60af1cefff04e7036467eae54f5930d5677e4ab066f8ed38a391b54df17733acfefac45e19ee53cef289347bddce5fc69a2766f4e580d21a22cfd9e2348e2723";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-lang3/org.apache.commons";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-lang3";
+        groupId = "org.apache.commons";
+        sha512 = "fbdbc0943cb3498b0148e86a39b773f97c8e6013740f72dbc727faeabea402073e2cc8c4d68198e5fc6b08a13b7700236292e99d4785f2c9989f2e5fac11fd81";
+        version = "3.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.logging/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.logging";
+        groupId = "org.clojure";
+        sha512 = "b7a9680f1156fc7c1574a4364ca550d47668ba727fc80110fdd00c159bedb45c5be82f09cdfb8e8e988e3381e2cf8881ea70651e38001e3eaa4ece31ad0bf0c5";
+        version = "1.2.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "core.specs.alpha/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "core.specs.alpha";
+        groupId = "org.clojure";
+        sha512 = "f521f95b362a47bb35f7c85528c34537f905fb3dd24f2284201e445635a0df701b35d8419d53c6507cc78d3717c1f83cda35ea4c82abd8943cd2ab3de3fcad70";
+        version = "0.2.62";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-common/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-common";
+        groupId = "io.netty";
+        sha512 = "7efc2f6774a3dbe8408fe182e19830b5b7a994a0d1b0eb50699df691c2450befa05ac205bbf341ad57bef3a04281ce435031e97e725c5c4edfc705a418828ce8";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-databind/com.fasterxml.jackson.core";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-databind";
+        groupId = "com.fasterxml.jackson.core";
+        sha512 = "9f771e78af669b1e1683d6c5903bbf4790aaa88b6b420c2018437da318c3fa4220cd7fa726f3e42a1b8075def1fdbd3744937c15f3bcedfca3050199247363e8";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "expound/expound";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "expound";
+        groupId = "expound";
+        sha512 = "ca0a57cfd215cff6be36d1f83461ec2d0559c0eae172c8a8bd6e1676d49933d3c30a71192889bd75d813581707d5eda0ec05de03326396bc0cedebf2d71811e5";
+        version = "0.8.10";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "spec.alpha/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "spec.alpha";
+        groupId = "org.clojure";
+        sha512 = "ddfe4fa84622abd8ac56e2aa565a56e6bdc0bf330f377ff3e269ddc241bb9dbcac332c13502dfd4c09c2c08fe24d8d2e8cf3d04a1bc819ca5657b4e41feaa7c2";
+        version = "0.3.218";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.cli/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.cli";
+        groupId = "org.clojure";
+        sha512 = "1d88aa03eb6a664bf2c0ce22c45e7296d54d716e29b11904115be80ea1661623cf3e81fc222d164047058239010eb678af92ffedc7c3006475cceb59f3b21265";
+        version = "1.0.206";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "compojure/compojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "compojure";
+        groupId = "compojure";
+        sha512 = "1f4ba1354bd95772963a4ef0e129dde59d16f4f9fac0f89f2505a1d5de3b4527e45073219c0478e0b3285da46793e7c145ec5a55a9dae2fca6b77dc8d67b4db6";
+        version = "1.6.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-fileupload/commons-fileupload";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-fileupload";
+        groupId = "commons-fileupload";
+        sha512 = "a8780b7dd7ab68f9e1df38e77a5207c45ff50ec53d8b1476570d069edc8f59e52fb1d0fc534d7e513ac5a01b385ba73c320794c82369a72bd6d817a3b3b21f39";
+        version = "1.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jetty-http/org.eclipse.jetty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jetty-http";
+        groupId = "org.eclipse.jetty";
+        sha512 = "60422ff3ef311f1d9d7340c2accdf611d40e738a39e9128967175ede4990439f4725995988849957742d488f749dd2e0740f74dc5bd9b3364e32fbaa66689308";
+        version = "9.4.42.v20210604";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jetty-util/org.eclipse.jetty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jetty-util";
+        groupId = "org.eclipse.jetty";
+        sha512 = "d69084e2cfe0c3af1dc7ee2745d563549a4068b6e8aed5cd2b9f31167168fb64d418c4134a6dfb811b627ec0051d7ff71e0a02e4e775d18a53543d0871c44730";
+        version = "9.4.42.v20210604";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "janino/org.codehaus.janino";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "janino";
+        groupId = "org.codehaus.janino";
+        sha512 = "6853d7d53d3629df43a3a17ff5c989f59ec14e9030be5f67426deb9d0797fa3996b0609d582c65f22a4f7680c941b39ab6d466c480b2fea4bf92218a9b89651d";
+        version = "3.1.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jcl-over-slf4j/org.slf4j";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jcl-over-slf4j";
+        groupId = "org.slf4j";
+        sha512 = "23662fe407fcdbcba8865a8cd3f8bb09d4eb178a2a6511a32e35b995722b345e73f5dc1dd85d2d0a5c707db05aa57e0b3d0b96b59e55403fc486343d5ca4c0d6";
+        version = "2.0.0-alpha4";
+
+      };
+      paths = [ src ];
+    }
+
+    (rec {
+      name = "io.github.cognitect-labs/test-runner";
+      src = fetchgit {
+        name = "test-runner";
+        url = "https://github.com/cognitect-labs/test-runner";
+        rev = "cc75980b43011773162b485f46f939dc5fba91e4";
+        sha256 = "1661ddmmqva1yiz9p09i5l32lfpi0a99h56022zgvz03nca2ksbg";
+      };
+      paths = map (path: src + path) [
+        "/src"
+      ];
+    })
+
+    rec {
+      name = "cambium.logback.core/cambium";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cambium.logback.core";
+        groupId = "cambium";
+        sha512 = "83ee9a583dd8a7b2e82e0981b4e51b005095a27257eb1b07165d9701645609060c466ae67fb9431f524a544d52b71fa00009b8acf05aadbeb549043515f9b382";
+        version = "0.4.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpasyncclient/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpasyncclient";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "0a80db5dbf772f02d02ba6c7c163e8da9517dd7195714b495acb845c429580c1fc926d3e71c115e75be8c145651dce2fdfa0dc380132f7809c14b3ad95492aee";
+        version = "4.1.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-jackson/ch.qos.logback.contrib";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-jackson";
+        groupId = "ch.qos.logback.contrib";
+        sha512 = "d9a3d4cb6cf4eda6fc18e2d374007d27c6ddba98e989a8d8a01b49859b280450113f685df6e16c5fbe0472bc9e26308bc7e8b7e0aedab9404cf0b492d7511685";
+        version = "0.1.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_tracer_otel/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_tracer_otel";
+        groupId = "io.prometheus";
+        sha512 = "bce192e6162cb3ada7dd6c2d10456e78bce71c170faa09bad2896272fa1bd4a036288d707f3d47747991d8946c74fe21c565713fb15c7052305eb753c94dd939";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-codec/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-codec";
+        groupId = "io.netty";
+        sha512 = "f6d9c4a5b508ca0d5f0e213473088f5d7b2e184e447dc092e69227109e28da9b8e68b2238ca6ab4e9915bacacf59cc0dce6ebcbbb05dad34a03b7976d9670c51";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-oauth2/ring-oauth2";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-oauth2";
+        groupId = "ring-oauth2";
+        sha512 = "3ed765b4bbb5749fcdcdb501b93ab656a413ade5af24c7aa34639718ed1fd0a5f325b05bd135540d56e55cbb456a2cb7852ba0e45bc5233e28229986eef75bb9";
+        version = "0.2.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.macro/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.macro";
+        groupId = "org.clojure";
+        sha512 = "65ce5e29379620ac458274c53cd9926e4b764fcaebb1a2b3bc8aef86bbe10c79e654b028bc4328905d2495a680fa90f5002cf5c47885f6449fad43a04a594b26";
+        version = "0.1.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-dataformat-cbor/com.fasterxml.jackson.dataformat";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-dataformat-cbor";
+        groupId = "com.fasterxml.jackson.dataformat";
+        sha512 = "ea5d049eac1b94666479c5e36de14d8fa4b7f24cb92f0f310d2ec2b4de66ef9023161060e67228ef2d7420a002ef861db12a29cad0864638c21612da49686f4f";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "depstar/seancorfield";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "depstar";
+        groupId = "seancorfield";
+        sha512 = "0f4458b39b8b1949755bc2fe64b239673a9efa3a0140998464bbbcab216ec847344c1b8920611f7c9ca07261850f3a08144ae221cc2c41813a080189e32f9c10";
+        version = "1.0.94";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-core/ch.qos.logback";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-core";
+        groupId = "ch.qos.logback";
+        sha512 = "fc554548f499e284007eeecf76bf4e1995effb6ac8a6262aa96118f623bf9085a9d5bec3741833dd3cae6a76b2ff78c6d0a1fe68bc01213207c93d8e2da345ca";
+        version = "1.3.0-alpha12";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "honeysql/honeysql";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "honeysql";
+        groupId = "honeysql";
+        sha512 = "74d1d93c968b33686848e3bf8934f3b5f002c2b69b1b55a3a3b172c952e9991324e6e95e3a0ce2fecf1de0d3a036f4dff7286df689f0733f253909464e0269f6";
+        version = "1.0.461";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-buffer/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-buffer";
+        groupId = "io.netty";
+        sha512 = "181b55d99d8d46bbf5f67f05bdccb0381af23a9fca3e6d935e6cde727b132c67133de1c3d81ed19b04c1a5b232be0de16ec1de7e81b532878bc69564237c15dc";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "slingshot/slingshot";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "slingshot";
+        groupId = "slingshot";
+        sha512 = "ff2b2a27b441d230261c7f3ec8c38aa551865e05ab6438a74bd12bfcbc5f6bdc88199d42aaf5932b47df84f3d2700c8f514b9f4e9b5da28d29da7ff6b09a7fb5";
+        version = "0.12.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpcore-nio/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpcore-nio";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "002af5f72b68a4ff1b1ff46b788013283d195e1d62ee1d7b102aa930b30f77f7e215a6d18edbea0fccd18fb1fa3a66cc4aef6070d72d6d1886f0044dfe0e16c7";
+        version = "4.4.10";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-jetty-adapter/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-jetty-adapter";
+        groupId = "ring";
+        sha512 = "93075903ad73a8b73cb77ee9f53ed33594f40a5dafe8129089adb4cfa333e37468764203c00244568f02abf0c0eee9f5d9a9f96c420919027cf2746a41ec38e3";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_tracer_common/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_tracer_common";
+        groupId = "io.prometheus";
+        sha512 = "6f717af63340efd84c5467ae752be7e66f586f0e8b57adb5b7a8ef99b223203ed829aad6797f6ef1811d6d861b00a621a1288c9271ec2ba77018d6d9eb9e7987";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "component/com.stuartsierra";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "component";
+        groupId = "com.stuartsierra";
+        sha512 = "108b02f51165ad07c2cf5232fbd954d052880c2456e6fb6db3342bda6851c76b73bf9145f03fb0df2b5782fe39f368b2868780c1e8e2dfa2ab2c68dd97f34ab7";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-handler/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-handler";
+        groupId = "io.netty";
+        sha512 = "48874727553dd7084f5c48d90de123704ae334837c3a103f598887bb21405dd62c57603b59300ac2fcdd936f0af99ed0730487fb9fb8917d236b8fe3f78f3c02";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "yuicompressor/com.yahoo.platform.yui";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "yuicompressor";
+        groupId = "com.yahoo.platform.yui";
+        sha512 = "ba2588bd50eaa3005b1919daad9f9c86a33351ceb9b7b5f0a9a498a548cc523e99f9345917a64303f8e23925feea226386d3eac01f640f788d1be4c7cf0315e0";
+        version = "2.4.8";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-io/commons-io";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-io";
+        groupId = "commons-io";
+        sha512 = "6af22dffaaecd1553147e788b5cf50368582318f396e456fe9ff33f5175836713a5d700e51720465c932c2b1987daa83027358005812d6a95d5755432de3a79d";
+        version = "2.10.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.namespace/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.namespace";
+        groupId = "org.clojure";
+        sha512 = "2cdb9c5d9bc4fd01dae182e9ad4b91eeaa2487003a977e7d8d5e66f562a9544b59f558710eccf421ea63cbbfa953ac8944fe9b9a76049fb82a47eb2bdcb3a4d7";
+        version = "1.1.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "honeysql/com.github.seancorfield";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "honeysql";
+        groupId = "com.github.seancorfield";
+        sha512 = "a0e5ebbf922aaf170c2d74ec0efc0df7e3bda92d0b8cc5f40ee4c8ddcb8c7e0e46556fac381513e0ac76b10f681c14c2d2569010c2f8eab4ff04f6373c2bf229";
+        version = "2.2.840";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-core/com.fasterxml.jackson.core";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-core";
+        groupId = "com.fasterxml.jackson.core";
+        sha512 = "428e0ebb16dd4c74ab0adf712058fd0dc0cd788f6e6f90c60c627da6577b345fac60a30694e111f1cd4e3e8bf79a1f1b820d30ada114984b26c28e299e326eaa";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clj-time/clj-time";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clj-time";
+        groupId = "clj-time";
+        sha512 = "cfeb46af59fd4112aa5a5d0087a39355f0fc19514b4c02bc6c3d9f81c9bda40491686207836e9a7943aebeb82a3b36f4e8b7407a8908c5ef151122644b278d75";
+        version = "0.15.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clj-http/clj-http";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clj-http";
+        groupId = "clj-http";
+        sha512 = "9884557d4f38068cb3234aec80acc0de8f9716645529693ffd9bd6db8221f5d1cf9e2d1b8bf7c7df4215d71372b02d83043ebf8fc27dc422552b32c9bdba1602";
+        version = "3.12.3";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jul-to-slf4j/org.slf4j";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jul-to-slf4j";
+        groupId = "org.slf4j";
+        sha512 = "350cfb889248d724b27dce697f635f12d9db463f107830b9518ce184dc4cc1ab3933eb5bdab08515e69766c3d5be24547dac289d6406c44eca90717230714b91";
+        version = "2.0.0-alpha4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "migratus/migratus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "migratus";
+        groupId = "migratus";
+        sha512 = "ee5ce8601930d063e0d9d90fc8e165b78fc1587bfd7e0fc9922735bc2f9fc27f8cf8bf10d49d6fd57b899ac4b250145bd653915ed770424416e026ba37d1b604";
+        version = "1.3.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpcore/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpcore";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "f16a652f4a7b87dbf7cb16f8590d54a3f719c4c7b2f8883ce59db2d73be4701b64f2ca8a2c45aca6a5dbeaddeedff0c280a03722f70c076e239b645faa54eff9";
+        version = "4.4.14";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpclient-cache/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpclient-cache";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "e150e8dc49c8c9972d8b324b56bb292b15e2f0e686f1292c4edac975615dfb16e5edb8ab325e614732a7d43a03061ca4fe93fe1e1f7487851a4d4d3af50a61f9";
+        version = "4.5.13";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "instaparse/instaparse";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "instaparse";
+        groupId = "instaparse";
+        sha512 = "ec2fcf4a09319a8fa9489b08fd9c9a5fe6e63155dde74d096f947fabc4f68d3d1bf68faf21e175e80eaee785f563a1903d30c550f93fb13a16a240609e3dfa2e";
+        version = "1.4.8";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "honeysql-postgres/nilenso";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "honeysql-postgres";
+        groupId = "nilenso";
+        sha512 = "d4accd3b8819cf715ecdb29496cf5a6a5ad3871fd579e55c7148d4e05774cb896c681b0c6f84df88aa9cd8e6ef9bfd65788ede9a49ba365ad0e32ee350091879";
+        version = "0.4.112";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clj-tuple/clj-tuple";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clj-tuple";
+        groupId = "clj-tuple";
+        sha512 = "dd626944d0aba679a21b164ed0c77ea84449359361496cba810f83b9fdeab751e5889963888098ce4bf8afa112dbda0a46ed60348a9c01ad36a2e255deb7ab6d";
+        version = "0.2.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-annotations/com.fasterxml.jackson.core";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-annotations";
+        groupId = "com.fasterxml.jackson.core";
+        sha512 = "6fdad6c5bb71a97331a662fe26265aacab6869f3307a710697d5c2f256fd48935764bfb0b3505a2cbb1605daf0b7350abdf84a1b1cf2bb1e91d9184565243c8e";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "hiccup/hiccup";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "hiccup";
+        groupId = "hiccup";
+        sha512 = "034f15be46c35029f41869c912f82cb2929fbbb0524ea64bd98dcdb9cf09875b28c75e926fa5fff53942b0f9e543e85a73a2d03c3f2112eecae30fcef8b148f4";
+        version = "1.0.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "riddley/riddley";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "riddley";
+        groupId = "riddley";
+        sha512 = "b478ecba9d1ab9d38c84a42354586fcece763000907b40c97bc43c0f16dc560b0860144efe410193cb3b7cb0149fbc1724fdd737cc3ba53de23618f5b30e6f9f";
+        version = "0.1.12";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "java.classpath/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "java.classpath";
+        groupId = "org.clojure";
+        sha512 = "90cd8edeaea02bd908d8cfb0cf5b1cf901aeb38ea3f4971c4b813d33210438aae6fff8e724a8272d2ea9441d373e7d936fa5870e309c1e9721299f662dbbdb9a";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_pushgateway/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_pushgateway";
+        groupId = "io.prometheus";
+        sha512 = "31c8878929f516ba7030cc9ec4ac4cbcb09955a9fdae23c6904bc481e40e70e1b3e05619c49b646119077ef6f57c430cc7944f6bafdbca24c9efa8145474fcf7";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ns-tracker/ns-tracker";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ns-tracker";
+        groupId = "ns-tracker";
+        sha512 = "cfb6c2c9f899b43d1284acdc572b34b977936c4df734b38137dfea045421b74d529509cde23695f1dc5ee06d046c2f6b61a2cd98058da1c7220c21dd0361964f";
+        version = "0.4.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clout/clout";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clout";
+        groupId = "clout";
+        sha512 = "99d6e1a8c5726ca4e5d12b280a39e6d1182d734922600f27d588d3d65fbc830c5e03f9e0421ff25c819deee4d1f389fd3906222716ace1eb17ce70ef9c5e8f4b";
+        version = "2.2.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-logging/commons-logging";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-logging";
+        groupId = "commons-logging";
+        sha512 = "ed00dbfabd9ae00efa26dd400983601d076fe36408b7d6520084b447e5d1fa527ce65bd6afdcb58506c3a808323d28e88f26cb99c6f5db9ff64f6525ecdfa557";
+        version = "1.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clojure.java-time/clojure.java-time";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clojure.java-time";
+        groupId = "clojure.java-time";
+        sha512 = "62d8a286ec3393594e7f84eba22dbb02c1305a80a18b2574058ae963d3f3e829ff960c8b66e89069e6c071a11f869203134c6c4cdec6f8e516c9b314796c8108";
+        version = "0.3.3";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "data.csv/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "data.csv";
+        groupId = "org.clojure";
+        sha512 = "b039775a859ed27eca8f8ae74ccb6afde3ad1fe2b3cbe542240c324d60fe1237e495eb1300ee9eb4ff4ef59f01faf7aec6ef1dd6a025ee4fe556c1d91acfcf1b";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_tracer_otel_agent/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_tracer_otel_agent";
+        groupId = "io.prometheus";
+        sha512 = "97694210d9a5b48a7cb9dda2a187432c4813edb3051edfa5832a0a471e0b2d5988dab92b70c292e78f59b169345deb5c1c706361fd726f3dc2480766dedfdcec";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "next.jdbc/com.github.seancorfield";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "next.jdbc";
+        groupId = "com.github.seancorfield";
+        sha512 = "0b4b01ba126bb8b1e2c14262db9fca75456b274d09535d9a7bb386699bf20dc9ac11590d210769e7429ca59ebfdfbb06916b3ff275cc817d74eac5bbabdab8f2";
+        version = "1.2.761";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "java.jdbc/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "java.jdbc";
+        groupId = "org.clojure";
+        sha512 = "6162b7774dca58b62a94bc5a04ba845e4c7065c9c589cc3bb802becfec0baf0989a338c1bf9a5db7c3128873702840d5f2451628f3aac977245975d65a683b7d";
+        version = "0.7.11";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-transport/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-transport";
+        groupId = "io.netty";
+        sha512 = "c11d690ffeaf3267b2166f73a43108fb89d588fcef3f6d3053bf4b6f6669483baa618fd97438010692a6fa28334372d5a31b7c0996961d4eabb60cbdc358a536";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "crypto-random/crypto-random";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "crypto-random";
+        groupId = "crypto-random";
+        sha512 = "3520df744f250dbe061d1a5d7a05b7143f3a67a4c3f9ad87b8044ee68a36a702a0bcb3a203e35d380899dd01c28e01988b0a7af914b942ccbe0c35c9bdb22e11";
+        version = "1.2.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-transport-native-unix-common/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-transport-native-unix-common";
+        groupId = "io.netty";
+        sha512 = "b63e5f8a44b7f37f3dba378bd06af64dd1d7be3f0b1a7d47ad139ff06e0212b4c7081275b1b5b12183aeb72eb5f9bf9ef03ed8c78bc302aeb4817dca7bd89f3a";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-codec/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-codec";
+        groupId = "ring";
+        sha512 = "38b9775a794831b8afd8d66991a75aa5910cd50952c9035866bf9cc01353810aedafbc3f35d8f9e56981ebf9e5c37c00b968759ed087d2855348b3f46d8d0487";
+        version = "1.1.3";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "spy/com.impossibl.pgjdbc-ng";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "spy";
+        groupId = "com.impossibl.pgjdbc-ng";
+        sha512 = "173615c39aa6015a732e329217b40e3ea1c304c9c168d2764d6ef23ab8775e2f4432339bc22d049662561f09d3fd890b5415738620d64dcedb762d5da26b4ebb";
+        version = "0.8.9";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-json-core/ch.qos.logback.contrib";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-json-core";
+        groupId = "ch.qos.logback.contrib";
+        sha512 = "2a826036f21997e2979fda83ae3e33cf62f3b2b2df15a7b11d1fd8a52163b09f0f2f8d72f5fdcea0ec1289b3d27727ed5e6b0bcdf4c5d741f4bac07b7b6139e8";
+        version = "0.1.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpclient/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpclient";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "3567739186e551f84cad3e4b6b270c5b8b19aba297675a96bcdff3663ff7d20d188611d21f675fe5ff1bfd7d8ca31362070910d7b92ab1b699872a120aa6f089";
+        version = "4.5.13";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "crypto-equality/crypto-equality";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "crypto-equality";
+        groupId = "crypto-equality";
+        sha512 = "54cf3bd28f633665962bf6b41f5ccbf2634d0db210a739e10a7b12f635e13c7ef532efe1d5d8c0120bb46478bbd08000b179f4c2dd52123242dab79fea97d6a6";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "cheshire/cheshire";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cheshire";
+        groupId = "cheshire";
+        sha512 = "855e9c42a8d1c64f4db5cda45e31e914eb5ed99a715e8d7a5759a9c4ab6c69a82353635ca7b0837880c6cf9b41b11184ae11e09cbf2c07aa13db32c539e5dfd4";
+        version = "5.10.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tigris/tigris";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tigris";
+        groupId = "tigris";
+        sha512 = "fdff4ef5e7175a973aaef98de4f37dee8e125fc711c495382e280aaf3e11341fe8925d52567ca60f3f1795511ade11bc23461c88959632dfae3cf50374d02bf6";
+        version = "0.1.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "config/yogthos";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "config";
+        groupId = "yogthos";
+        sha512 = "3437992d192465edc74aec5259d5e0c0ad7e631dff860b2ee14cef27f13cee7c60487202cf00fc160a95fb0b85ce1ddf56cbdd0c008b47ac598061bf115f6a23";
+        version = "1.1.9";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jetty-io/org.eclipse.jetty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jetty-io";
+        groupId = "org.eclipse.jetty";
+        sha512 = "a8c5f73089daa0c8b27f836acddf40bcbf07bbb2571a4d73653be8aac3fb339022f546326722f216bad78a68886934d24db9bec54235124592dd29dbeab69051";
+        version = "9.4.42.v20210604";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-json-classic/ch.qos.logback.contrib";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-json-classic";
+        groupId = "ch.qos.logback.contrib";
+        sha512 = "d30bf70217d316914d83d46cc15783f656354084087d59cbc0620a746f10b4a42e56d33b3e50a8b3596a64ec8314730bf5ff9a3f7dc3417bdd0582665be009ec";
+        version = "0.1.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.reader/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.reader";
+        groupId = "org.clojure";
+        sha512 = "3481259c7a1eac719db2921e60173686726a0c2b65879d51a64d516a37f6120db8ffbb74b8bd273404285d7b25143ab5c7ced37e7c0eaf4ab1e44586ccd3c651";
+        version = "1.3.6";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_common/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_common";
+        groupId = "io.prometheus";
+        sha512 = "dedd003638eb3651c112e2d697ac94eb4e3b3e32c94fa41bb1efe2c889a347cdc7bd13256e05423f3370592d4fd65faf8db57f0387ab75814d7fa77b14cbbadf";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-compiler/org.codehaus.janino";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-compiler";
+        groupId = "org.codehaus.janino";
+        sha512 = "f0778b891ef14d8ee6776747eab0b25da716cdc530752a81aedec2a77570e2f66402179b9408a6efde8125c808eb060a720d2f4977c1f1d022bdaae7eac8d011";
+        version = "3.1.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "servlet-api/javax.servlet";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "servlet-api";
+        groupId = "javax.servlet";
+        sha512 = "363ba5590436ab82067b7a2e14b481aeb2b12ca4048d7a1519a2e549b2d3c09ddf718ac64dc2be6c2fc24c51fdc9c8160261329403113369588ce27d87771db6";
+        version = "2.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "iapetos/clj-commons";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "iapetos";
+        groupId = "clj-commons";
+        sha512 = "d17f36c0cf0ec78db5e893e5c033f8562b31650bda6f5ee582e68f84a07a3631d04d6f69e4e18b1ca64e732c180fa669dfb69a78849e13f601cd563a7a8aab94";
+        version = "0.1.12";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "javax.servlet-api/javax.servlet";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "javax.servlet-api";
+        groupId = "javax.servlet";
+        sha512 = "32f7e3565c6cdf3d9a562f8fd597fe5059af0cf6b05b772a144a74bbc95927ac275eb38374538ec1c72adcce4c8e1e2c9f774a7b545db56b8085af0065e4a1e5";
+        version = "3.1.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "potemkin/potemkin";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "potemkin";
+        groupId = "potemkin";
+        sha512 = "5abc050bf7ff0b27d8c45aaa5e378201980815b711b2db99735db73304576c17e285026ea48a714bf0b0df7ad7a008de38b7d182cdc0e8989f4be1e6b3afa8aa";
+        version = "0.4.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-resolver/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-resolver";
+        groupId = "io.netty";
+        sha512 = "fabf893de74264caa1799c15d184ed8f20b7bf9b1c41abb29f29adf728a934951f97892a4924634f9efbda17c8cf74ea3ff97bafca616711e3c5f79b8ed9ef3e";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-transport-native-epoll/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-transport-native-epoll";
+        groupId = "io.netty";
+        sha512 = "6fbc2dd2622699f3fc1f329acbd94baf7f1d8923c5cfcae262e6f2d64b4fd71b606561bce5e2b511dff8e052cdade930091fab683fd98713f6b62a622a2c6254";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clj-stacktrace/clj-stacktrace";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clj-stacktrace";
+        groupId = "clj-stacktrace";
+        sha512 = "993f8a544203801fc074eefacee8e553e426422b3492d47b857d87ac73cde72c91e29f629382b9eae8cf9600bc2c4c29d2e7169e509c46302ab973c86e73af0c";
+        version = "0.2.8";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "cambium.codec-cheshire/cambium";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cambium.codec-cheshire";
+        groupId = "cambium";
+        sha512 = "614491cf752a597f29ae29885db6c1ed191341303d89183bee52e4e2c76eb8eb14693562ad09484f379a074b36d97085e848ec3845e069440e6422506c1636f1";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "slf4j-api/org.slf4j";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "slf4j-api";
+        groupId = "org.slf4j";
+        sha512 = "ad705ab6fd5cd904ef6861c0adf08af19593cf6a486b18de548fe3d68e57b1baa7e02947584fd4dcc350ddcddcf906c01e8d9ba7943a202690d0d788627696b5";
+        version = "2.0.0-alpha4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "test.check/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "test.check";
+        groupId = "org.clojure";
+        sha512 = "b8d7a330b0b5514cd6a00c4382052fab51c3c9d3bc53133f8506791fa670e7c5ecd65094977ea5ced91f59623b0abd1ab8feeec96d63c5c6e459b265a655c577";
+        version = "1.1.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-logger/ring-logger";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-logger";
+        groupId = "ring-logger";
+        sha512 = "b675a61c173289fc610d84920ba40178bf62b3bc680923cb66866d78ee2a508296b27a1ab14b66bfbe0304a64166a7e3c3ddee36564dd4a2f988861bce455a3a";
+        version = "1.0.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-servlet/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-servlet";
+        groupId = "ring";
+        sha512 = "3d8e6ec224e13d54810a945c0b6c0d2d863736a48d8c4bfc8fadb96b6b0fa9baa638644d0d92d8a53650b188e6e75d391731b08b26eb0f551e90a7504e7f4267";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-classic/ch.qos.logback";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-classic";
+        groupId = "ch.qos.logback";
+        sha512 = "f9fe0f126061f4abe3973b631b8d8244ba9e9d77783479a6500d629d772050dee508a001fc14d2131407fbdd0d33dd6b8aeb9b1ea9125b471bb8412e8de659e6";
+        version = "1.3.0-alpha12";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "dependency/com.stuartsierra";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "dependency";
+        groupId = "com.stuartsierra";
+        sha512 = "d32fbc4813bd16f2ed8c82e2915e1fb564e88422159bd3580a85c8cd969d1bbbe315bdc13d29c2f0eaceeeafcf649ee712c8df4532464d560aaeae4ae5953866";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "camel-snake-kebab/camel-snake-kebab";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "camel-snake-kebab";
+        groupId = "camel-snake-kebab";
+        sha512 = "589d34b500560b7113760a16bfb6f0ccd8f162a1ce8c9bc829495432159ba9c95aebf6bc43aa126237a0525806a205a05f9910122074902b659e7fd151d176b1";
+        version = "0.4.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring";
+        groupId = "ring";
+        sha512 = "93c48fb670736b91fb41d8076e1e9c4f53c67693d15e75290da319e7d7881b829a24180029b3a0fa051473c6c77ac3c97b519254ebf2b2c9538b185e79b69162";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-transport-native-kqueue/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-transport-native-kqueue";
+        groupId = "io.netty";
+        sha512 = "87e10c06e394a1698d65381d3be8336f753c55e3e899e297510161d0c72540023f30f9032322957e035ead793204a084b988bc21a2bc312fcf7567a22d02a3c4";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "java.data/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "java.data";
+        groupId = "org.clojure";
+        sha512 = "225e1eafd1a659278212d831f7cd8609359f8c880ef3d69b4ade6301ce3c511307ce31d94cb82d5407314b990bd04714ec26273bb3036b248116a7a75fa75e1f";
+        version = "1.0.95";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jetty-server/org.eclipse.jetty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jetty-server";
+        groupId = "org.eclipse.jetty";
+        sha512 = "b347f8a6e5b84e0f460037027e238a61edec710ade768c95e7be13dcea498abe43d5e622ee69ac7494138d1a8fcf92e07b7deab569c554831c57baad71c53b9b";
+        version = "9.4.42.v20210604";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpmime/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpmime";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "e1b0ee84bce78576074dc1b6836a69d8f5518eade38562e6890e3ddaa72b7f54bf735c8e2286142c58cddf45f745da31261e5d73b7d8092eb6ecfb20946eb36c";
+        version = "4.5.13";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "log4j-over-slf4j/org.slf4j";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "log4j-over-slf4j";
+        groupId = "org.slf4j";
+        sha512 = "48fa023c57294b73b9bd2f53e3dd3169e03426e5b3aa9d80e1bb1a9abf927fc26ef9f64d02b9769d5577d83094d0f41f044d35bb3b4f6037d66d6b2f19b484a1";
+        version = "2.0.0-alpha4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-core/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-core";
+        groupId = "ring";
+        sha512 = "38d7214a3fc1b80ab55999036638dd1971272e01bec4cb8e0ee0a4aa83f51b8c41ba8a5850b0660227f067d2f9c6d75c0c0737725ea02762bbf8d192dc72febe";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "cambium.core/cambium";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cambium.core";
+        groupId = "cambium";
+        sha512 = "0e1fe626c6d0b31aad84ea2e4466273065925548ee5915f442b7997ebfe795faea36dbeac50a0f8c16bbd20d877511e3f8c4ff4f2b916a4538513aaa5cc20112";
+        version = "1.1.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "medley/medley";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "medley";
+        groupId = "medley";
+        sha512 = "749ef43b5ea2cae7dc96db871cdd15c7b3c9cfbd96828c20ab08e67d39a5e938357d15994d8d413bc68678285d6c666f2a7296fbf305706d03b3007254e3c55c";
+        version = "1.3.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "garden/garden";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "garden";
+        groupId = "garden";
+        sha512 = "2cc29f071b68bf451835f76de351ac2efb930b5df9ca7237fdca439d3c4d797d7fa207a147886efe1738ab1c50b76c1e366bf9ffcd6f286b0b211260aedd0b25";
+        version = "1.3.10";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-dataformat-smile/com.fasterxml.jackson.dataformat";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-dataformat-smile";
+        groupId = "com.fasterxml.jackson.dataformat";
+        sha512 = "69676964a2b09516b8ffd0d847b6f9a9b843424185453731b548c25e7e9ce30e808c56d66923f9183e2b5c1ba007421b146a6806e768b8e6b07470d60227f1dd";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jaxb-api/javax.xml.bind";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jaxb-api";
+        groupId = "javax.xml.bind";
+        sha512 = "0c5bfc2c9f655bf5e6d596e0c196dcb9344d6dc78bf774207c8f8b6be59f69addf2b3121e81491983eff648dfbd55002b9878132de190825dad3ef3a1265b367";
+        version = "2.3.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "pgjdbc-ng/com.impossibl.pgjdbc-ng";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "pgjdbc-ng";
+        groupId = "com.impossibl.pgjdbc-ng";
+        sha512 = "a34ac9146257329f6e9b354f13f564c65dbea6463addae383e3918d3a64c90c67f5f7fda6b5c3866de991a568d6690edb3fb09f2507593390a6e30ec0c79e02c";
+        version = "0.8.9";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "http-kit/http-kit";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "http-kit";
+        groupId = "http-kit";
+        sha512 = "4186a2429984745e18730aa8fd545f1fc1812083819ebf77aecfc04e0d31585358a5e25a308c7f21d81359418bbc72390c281f5ed91ae116cf1af79860ba22c3";
+        version = "2.5.3";
+
+      };
+      paths = [ src ];
+    }
+
+  ];
+}
+  
\ No newline at end of file
diff --git a/users/grfn/bbbg/env/dev/bbbg-signup/env.clj b/users/aspen/bbbg/env/dev/bbbg-signup/env.clj
index c30e328ffa..c30e328ffa 100644
--- a/users/grfn/bbbg/env/dev/bbbg-signup/env.clj
+++ b/users/aspen/bbbg/env/dev/bbbg-signup/env.clj
diff --git a/users/grfn/bbbg/env/dev/logback.xml b/users/aspen/bbbg/env/dev/logback.xml
index 7aa21978bb..7aa21978bb 100644
--- a/users/grfn/bbbg/env/dev/logback.xml
+++ b/users/aspen/bbbg/env/dev/logback.xml
diff --git a/users/grfn/bbbg/env/prod/bbbg-signup/env.clj b/users/aspen/bbbg/env/prod/bbbg-signup/env.clj
index 46e8cd67e3..46e8cd67e3 100644
--- a/users/grfn/bbbg/env/prod/bbbg-signup/env.clj
+++ b/users/aspen/bbbg/env/prod/bbbg-signup/env.clj
diff --git a/users/grfn/bbbg/env/prod/logback.xml b/users/aspen/bbbg/env/prod/logback.xml
index b81118ed6b..b81118ed6b 100644
--- a/users/grfn/bbbg/env/prod/logback.xml
+++ b/users/aspen/bbbg/env/prod/logback.xml
diff --git a/users/grfn/bbbg/env/test/bbbg-signup/env.clj b/users/aspen/bbbg/env/test/bbbg-signup/env.clj
index 352147a6d0..352147a6d0 100644
--- a/users/grfn/bbbg/env/test/bbbg-signup/env.clj
+++ b/users/aspen/bbbg/env/test/bbbg-signup/env.clj
diff --git a/users/grfn/bbbg/env/test/logback.xml b/users/aspen/bbbg/env/test/logback.xml
index 8554f3d0ed..8554f3d0ed 100644
--- a/users/grfn/bbbg/env/test/logback.xml
+++ b/users/aspen/bbbg/env/test/logback.xml
diff --git a/users/aspen/bbbg/module.nix b/users/aspen/bbbg/module.nix
new file mode 100644
index 0000000000..c5bacdf4d7
--- /dev/null
+++ b/users/aspen/bbbg/module.nix
@@ -0,0 +1,137 @@
+{ config, lib, pkgs, depot, ... }:
+
+let
+  bbbg = depot.users.aspen.bbbg;
+  cfg = config.services.bbbg;
+in
+{
+  options = with lib; {
+    services.bbbg = {
+      enable = mkEnableOption "BBBG Server";
+
+      port = mkOption {
+        type = types.int;
+        default = 7222;
+        description = "Port to listen to for the HTTP server";
+      };
+
+      domain = mkOption {
+        type = types.str;
+        default = "bbbg.gws.fyi";
+        description = "Domain to host under";
+      };
+
+      proxy = {
+        enable = mkEnableOption "NGINX reverse proxy";
+      };
+
+      database = {
+        enable = mkEnableOption "BBBG Database Server";
+
+        user = mkOption {
+          type = types.str;
+          default = "bbbg";
+          description = "Database username";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Database host";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "bbbg";
+          description = "Database name";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 5432;
+          description = "Database host";
+        };
+      };
+    };
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf cfg.enable {
+      systemd.services.bbbg-server = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          DynamicUser = true;
+          Restart = "always";
+          EnvironmentFile = config.age.secretsDir + "/bbbg";
+        };
+
+        environment = {
+          PGHOST = cfg.database.host;
+          PGUSER = cfg.database.user;
+          PGDATABASE = cfg.database.name;
+          PORT = toString cfg.port;
+          BASE_URL = "https://${cfg.domain}";
+        };
+
+        script = "${bbbg.server}/bin/bbbg-server";
+      };
+
+      systemd.services.migrate-bbbg = {
+        description = "Run database migrations for BBBG";
+        wantedBy = [ "bbbg-server.service" ];
+        after = ([ "network.target" ]
+          ++ (if cfg.database.enable
+        then [ "postgresql.service" ]
+        else [ ]));
+
+        serviceConfig = {
+          Type = "oneshot";
+          EnvironmentFile = config.age.secretsDir + "/bbbg";
+        };
+
+        environment = {
+          PGHOST = cfg.database.host;
+          PGUSER = cfg.database.user;
+          PGDATABASE = cfg.database.name;
+        };
+
+        script = "${bbbg.db-util}/bin/bbbg-db-util migrate";
+      };
+    })
+    (lib.mkIf cfg.database.enable {
+      services.postgresql = {
+        enable = true;
+        authentication = lib.mkForce ''
+          local all all trust
+          host all all 127.0.0.1/32 password
+          host all all ::1/128 password
+          hostnossl all all 127.0.0.1/32 password
+          hostnossl all all ::1/128  password
+        '';
+
+        ensureDatabases = [
+          cfg.database.name
+        ];
+
+        ensureUsers = [{
+          name = cfg.database.user;
+          ensurePermissions = {
+            "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES";
+          };
+        }];
+      };
+    })
+    (lib.mkIf cfg.proxy.enable {
+      services.nginx = {
+        enable = true;
+        virtualHosts."${cfg.domain}" = {
+          enableACME = true;
+          forceSSL = true;
+          locations."/".proxyPass = "http://localhost:${toString cfg.port}";
+        };
+      };
+    })
+  ];
+}
diff --git a/users/grfn/bbbg/pom.xml b/users/aspen/bbbg/pom.xml
index 012c0985f1..012c0985f1 100644
--- a/users/grfn/bbbg/pom.xml
+++ b/users/aspen/bbbg/pom.xml
diff --git a/users/grfn/bbbg/resources/base.css b/users/aspen/bbbg/resources/base.css
index c86c3f24f0..c86c3f24f0 100644
--- a/users/grfn/bbbg/resources/base.css
+++ b/users/aspen/bbbg/resources/base.css
diff --git a/users/grfn/bbbg/resources/migrations/20211212165646-init-schema.down.sql b/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.down.sql
index 69b818a4f4..69b818a4f4 100644
--- a/users/grfn/bbbg/resources/migrations/20211212165646-init-schema.down.sql
+++ b/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.down.sql
diff --git a/users/grfn/bbbg/resources/migrations/20211212165646-init-schema.up.sql b/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.up.sql
index 9718d84748..9718d84748 100644
--- a/users/grfn/bbbg/resources/migrations/20211212165646-init-schema.up.sql
+++ b/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.up.sql
diff --git a/users/grfn/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql b/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql
index 936abf6c7d..936abf6c7d 100644
--- a/users/grfn/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql
+++ b/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql
diff --git a/users/grfn/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql b/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql
index 5e82dcb171..5e82dcb171 100644
--- a/users/grfn/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql
+++ b/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql
diff --git a/users/grfn/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql b/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql
index cbee0c00ac..cbee0c00ac 100644
--- a/users/grfn/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql
+++ b/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql
diff --git a/users/grfn/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql b/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql
index 5895cad56b..5895cad56b 100644
--- a/users/grfn/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql
+++ b/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql
diff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff
index 1c83d8518d..1c83d8518d 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2
index 9dc5c7f158..9dc5c7f158 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff
index 71476d858f..71476d858f 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2
index 0fb9838c9d..0fb9838c9d 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff
index e7f8a31ba3..e7f8a31ba3 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2
index 29cc1a9734..29cc1a9734 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff
index 79203dd780..79203dd780 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2
index 0abb707aed..0abb707aed 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff
index 65415571a7..65415571a7 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2
index 674e6eabe7..674e6eabe7 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff
index 67f1e85379..67f1e85379 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2
index 469aede09c..469aede09c 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff
index 676a065e24..676a065e24 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff
Binary files differdiff --git a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2
index 70788c2732..70788c2732 100644
--- a/users/grfn/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2
Binary files differdiff --git a/users/grfn/bbbg/resources/public/main.js b/users/aspen/bbbg/resources/public/main.js
index 87c0b64d0a..87c0b64d0a 100644
--- a/users/grfn/bbbg/resources/public/main.js
+++ b/users/aspen/bbbg/resources/public/main.js
diff --git a/users/grfn/bbbg/resources/public/robots.txt b/users/aspen/bbbg/resources/public/robots.txt
index 1f53798bb4..1f53798bb4 100644
--- a/users/grfn/bbbg/resources/public/robots.txt
+++ b/users/aspen/bbbg/resources/public/robots.txt
diff --git a/users/aspen/bbbg/shell.nix b/users/aspen/bbbg/shell.nix
new file mode 100644
index 0000000000..c253a2b9ba
--- /dev/null
+++ b/users/aspen/bbbg/shell.nix
@@ -0,0 +1,29 @@
+let
+  depot = import ../../.. { };
+in
+with depot.third_party.nixpkgs;
+
+mkShell {
+  buildInputs = [
+    arion
+    depot.third_party.clj2nix
+    clojure
+    openjdk11_headless
+    postgresql_12
+    nix-prefetch-git
+    (writeShellScriptBin "terraform" ''
+      set -e
+      module=$(nix-build ~/code/depot -A users.grfn.bbbg.tf.module)
+      rm -f ~/tfstate/bbbg/*.json
+      cp ''${module}/*.json ~/tfstate/bbbg
+      exec ${depot.users.aspen.bbbg.tf.terraform}/bin/terraform \
+        -chdir=/home/grfn/tfstate/bbbg \
+        "$@"
+    '')
+  ];
+
+  PGHOST = "localhost";
+  PGUSER = "bbbg";
+  PGDATABASE = "bbbg";
+  PGPASSWORD = "password";
+}
diff --git a/users/grfn/bbbg/src/bbbg/attendee.clj b/users/aspen/bbbg/src/bbbg/attendee.clj
index 49a6d621de..49a6d621de 100644
--- a/users/grfn/bbbg/src/bbbg/attendee.clj
+++ b/users/aspen/bbbg/src/bbbg/attendee.clj
diff --git a/users/grfn/bbbg/src/bbbg/attendee_check.clj b/users/aspen/bbbg/src/bbbg/attendee_check.clj
index f34c41198e..f34c41198e 100644
--- a/users/grfn/bbbg/src/bbbg/attendee_check.clj
+++ b/users/aspen/bbbg/src/bbbg/attendee_check.clj
diff --git a/users/aspen/bbbg/src/bbbg/core.clj b/users/aspen/bbbg/src/bbbg/core.clj
new file mode 100644
index 0000000000..632774d5cd
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/core.clj
@@ -0,0 +1,69 @@
+(ns bbbg.core
+  (:gen-class)
+  (:require
+   [bbbg.db :as db]
+   [bbbg.web :as web]
+   [clojure.spec.alpha :as s]
+   [clojure.spec.test.alpha :as stest]
+   [com.stuartsierra.component :as component]
+   [expound.alpha :as exp]))
+
+(s/def ::config
+  (s/merge
+   ::db/config
+   ::web/config))
+
+(defn make-system [config]
+  (component/system-map
+   :db (db/make-database config)
+   :web (web/make-server config)))
+
+(defn env->config []
+  (s/assert
+   ::config
+   (merge
+    (db/env->config)
+    (web/env->config))))
+
+(defn dev-config []
+  (s/assert
+   ::config
+   (merge
+    (db/dev-config)
+    (web/dev-config))))
+
+(defonce system nil)
+
+(defn init-dev []
+  (s/check-asserts true)
+  (set! s/*explain-out* exp/printer)
+  (stest/instrument))
+
+(defn run-dev []
+  (init-dev)
+  (alter-var-root
+   #'system
+   (fn [sys]
+     (when sys
+       (component/start sys))
+     (component/start (make-system (dev-config))))))
+
+(defn -main [& _args]
+  (alter-var-root
+   #'system
+   (constantly (component/start (make-system (env->config))))))
+
+(comment
+  ;; To run the application:
+  ;; 1. `M-x cider-jack-in`
+  ;; 2. `M-x cider-load-buffer` in this buffer
+  ;; 3. (optionally) configure the secrets backend in `bbbg.util.dev-secrets`
+  ;; 4. Put your cursor after the following form and run `M-x cider-eval-last-sexp`
+  ;;
+  ;; A web server will be listening on http://localhost:8888
+
+  (do
+    (run-dev)
+    (bbbg.db/migrate! (:db system)))
+
+  )
diff --git a/users/grfn/bbbg/src/bbbg/db.clj b/users/aspen/bbbg/src/bbbg/db.clj
index 5bbf88925a..5bbf88925a 100644
--- a/users/grfn/bbbg/src/bbbg/db.clj
+++ b/users/aspen/bbbg/src/bbbg/db.clj
diff --git a/users/grfn/bbbg/src/bbbg/db/attendee.clj b/users/aspen/bbbg/src/bbbg/db/attendee.clj
index da5ee29321..da5ee29321 100644
--- a/users/grfn/bbbg/src/bbbg/db/attendee.clj
+++ b/users/aspen/bbbg/src/bbbg/db/attendee.clj
diff --git a/users/grfn/bbbg/src/bbbg/db/attendee_check.clj b/users/aspen/bbbg/src/bbbg/db/attendee_check.clj
index 492f786bd6..492f786bd6 100644
--- a/users/grfn/bbbg/src/bbbg/db/attendee_check.clj
+++ b/users/aspen/bbbg/src/bbbg/db/attendee_check.clj
diff --git a/users/grfn/bbbg/src/bbbg/db/event.clj b/users/aspen/bbbg/src/bbbg/db/event.clj
index 5e93691dfa..1b5a4e11ec 100644
--- a/users/grfn/bbbg/src/bbbg/db/event.clj
+++ b/users/aspen/bbbg/src/bbbg/db/event.clj
@@ -6,7 +6,7 @@
    [bbbg.util.sql :refer [count-where]]
    [honeysql.helpers
     :refer [merge-group-by merge-left-join merge-select merge-where]]
-   [java-time :refer [local-date]]))
+   [java-time :refer [local-date local-date-time local-time]]))
 
 (defn create! [db event]
   (db/insert! db :event (select-keys event [::event/date])))
@@ -29,8 +29,31 @@
   ([db day]
    (db/list db (on-day day))))
 
+
+(def end-of-day-hour
+  ;; 7am utc = 3am nyc
+  7)
+
+(defn current-day
+  ([] (current-day (local-date-time)))
+  ([dt]
+   (if (<= 0
+           (.getHour (local-time dt))
+           end-of-day-hour)
+     (java-time/minus
+      (local-date dt)
+      (java-time/days 1))
+     (local-date dt))))
+
+(comment
+  (current-day
+   (local-date-time
+    2022 5 1
+    1 13 0))
+  )
+
 (defn today
-  ([] (on-day (local-date)))
+  ([] (on-day (current-day)))
   ([db] (db/list db (today))))
 
 (defn upcoming
diff --git a/users/grfn/bbbg/src/bbbg/db/event_attendee.clj b/users/aspen/bbbg/src/bbbg/db/event_attendee.clj
index 31411e5d45..31411e5d45 100644
--- a/users/grfn/bbbg/src/bbbg/db/event_attendee.clj
+++ b/users/aspen/bbbg/src/bbbg/db/event_attendee.clj
diff --git a/users/grfn/bbbg/src/bbbg/db/user.clj b/users/aspen/bbbg/src/bbbg/db/user.clj
index 700105ef63..700105ef63 100644
--- a/users/grfn/bbbg/src/bbbg/db/user.clj
+++ b/users/aspen/bbbg/src/bbbg/db/user.clj
diff --git a/users/grfn/bbbg/src/bbbg/discord.clj b/users/aspen/bbbg/src/bbbg/discord.clj
index ce8568ad82..e854ec1d14 100644
--- a/users/grfn/bbbg/src/bbbg/discord.clj
+++ b/users/aspen/bbbg/src/bbbg/discord.clj
@@ -1,8 +1,9 @@
 (ns bbbg.discord
   (:refer-clojure :exclude [get])
-  (:require [clj-http.client :as http]
-            [clojure.string :as str]
-            [bbbg.util.core :as u]))
+  (:require
+   [bbbg.util.dev-secrets :refer [secret]]
+   [clj-http.client :as http]
+   [clojure.string :as str]))
 
 (def base-uri "https://discord.com/api")
 
@@ -33,7 +34,7 @@
   (get token (str "/users/@me/guilds/" guild-id "/member")))
 
 (comment
-  (def token {:token (u/pass "bbbg/test-token")})
+  (def token {:token (secret "bbbg/test-token")})
   (me token)
   (guilds token)
   (guild-member token "841295283564052510")
diff --git a/users/grfn/bbbg/src/bbbg/discord/auth.clj b/users/aspen/bbbg/src/bbbg/discord/auth.clj
index 0b04df558b..35bc580e39 100644
--- a/users/grfn/bbbg/src/bbbg/discord/auth.clj
+++ b/users/aspen/bbbg/src/bbbg/discord/auth.clj
@@ -2,6 +2,7 @@
   (:require
    [bbbg.discord :as discord]
    [bbbg.util.core :as u]
+   [bbbg.util.dev-secrets :refer [secret]]
    clj-time.coerce
    [clojure.spec.alpha :as s]
    [config.core :refer [env]]
@@ -33,11 +34,10 @@
 (defn dev-config []
   (s/assert
    ::config
-   {::client-id (u/pass "bbbg/discord-client-id")
-    ::client-secret (u/pass "bbbg/discord-client-secret")
+   {::client-id (secret "bbbg/discord-client-id")
+    ::client-secret (secret "bbbg/discord-client-secret")
     ::bbbg-guild-id "841295283564052510"
-    ;; TODO this might not be the right id
-    ::bbbg-organizer-role "874846495873040395"}))
+    ::bbbg-organizer-role "908428000817725470"}))
 
 ;;;
 
diff --git a/users/grfn/bbbg/src/bbbg/event.clj b/users/aspen/bbbg/src/bbbg/event.clj
index aa0578f354..aa0578f354 100644
--- a/users/grfn/bbbg/src/bbbg/event.clj
+++ b/users/aspen/bbbg/src/bbbg/event.clj
diff --git a/users/grfn/bbbg/src/bbbg/event_attendee.clj b/users/aspen/bbbg/src/bbbg/event_attendee.clj
index 7b6b4c2764..7b6b4c2764 100644
--- a/users/grfn/bbbg/src/bbbg/event_attendee.clj
+++ b/users/aspen/bbbg/src/bbbg/event_attendee.clj
diff --git a/users/grfn/bbbg/src/bbbg/handlers/attendee_checks.clj b/users/aspen/bbbg/src/bbbg/handlers/attendee_checks.clj
index d7307c4067..d7307c4067 100644
--- a/users/grfn/bbbg/src/bbbg/handlers/attendee_checks.clj
+++ b/users/aspen/bbbg/src/bbbg/handlers/attendee_checks.clj
diff --git a/users/grfn/bbbg/src/bbbg/handlers/attendees.clj b/users/aspen/bbbg/src/bbbg/handlers/attendees.clj
index ce84b88e97..ce84b88e97 100644
--- a/users/grfn/bbbg/src/bbbg/handlers/attendees.clj
+++ b/users/aspen/bbbg/src/bbbg/handlers/attendees.clj
diff --git a/users/grfn/bbbg/src/bbbg/handlers/core.clj b/users/aspen/bbbg/src/bbbg/handlers/core.clj
index caa679ee87..caa679ee87 100644
--- a/users/grfn/bbbg/src/bbbg/handlers/core.clj
+++ b/users/aspen/bbbg/src/bbbg/handlers/core.clj
diff --git a/users/grfn/bbbg/src/bbbg/handlers/events.clj b/users/aspen/bbbg/src/bbbg/handlers/events.clj
index 6f6d6f3585..6f6d6f3585 100644
--- a/users/grfn/bbbg/src/bbbg/handlers/events.clj
+++ b/users/aspen/bbbg/src/bbbg/handlers/events.clj
diff --git a/users/grfn/bbbg/src/bbbg/handlers/home.clj b/users/aspen/bbbg/src/bbbg/handlers/home.clj
index 17d4875536..17d4875536 100644
--- a/users/grfn/bbbg/src/bbbg/handlers/home.clj
+++ b/users/aspen/bbbg/src/bbbg/handlers/home.clj
diff --git a/users/grfn/bbbg/src/bbbg/handlers/signup_form.clj b/users/aspen/bbbg/src/bbbg/handlers/signup_form.clj
index ed1d7644f5..ed1d7644f5 100644
--- a/users/grfn/bbbg/src/bbbg/handlers/signup_form.clj
+++ b/users/aspen/bbbg/src/bbbg/handlers/signup_form.clj
diff --git a/users/grfn/bbbg/src/bbbg/meetup/import.clj b/users/aspen/bbbg/src/bbbg/meetup/import.clj
index d13d63e16c..bbf8678976 100644
--- a/users/grfn/bbbg/src/bbbg/meetup/import.clj
+++ b/users/aspen/bbbg/src/bbbg/meetup/import.clj
@@ -116,7 +116,7 @@
 ;;; Have you been to one of our events before? Note, attendance at all events will require proof of vaccination until further notice.
 
 (comment
-  (def -filename- "/home/grfn/code/depot/users/grfn/bbbg/sample-data.tsv")
+  (def -filename- "/home/aspen/code/depot/users/aspen/bbbg/sample-data.tsv")
   (def event-id #uuid "09f8fed6-7480-451b-89a2-bb4edaeae657")
 
   (read-attendees -filename-)
diff --git a/users/grfn/bbbg/src/bbbg/meetup_user.clj b/users/aspen/bbbg/src/bbbg/meetup_user.clj
index 945d681c6f..945d681c6f 100644
--- a/users/grfn/bbbg/src/bbbg/meetup_user.clj
+++ b/users/aspen/bbbg/src/bbbg/meetup_user.clj
diff --git a/users/grfn/bbbg/src/bbbg/styles.clj b/users/aspen/bbbg/src/bbbg/styles.clj
index a860ae6076..a860ae6076 100644
--- a/users/grfn/bbbg/src/bbbg/styles.clj
+++ b/users/aspen/bbbg/src/bbbg/styles.clj
diff --git a/users/grfn/bbbg/src/bbbg/user.clj b/users/aspen/bbbg/src/bbbg/user.clj
index f48c8d7338..f48c8d7338 100644
--- a/users/grfn/bbbg/src/bbbg/user.clj
+++ b/users/aspen/bbbg/src/bbbg/user.clj
diff --git a/users/grfn/bbbg/src/bbbg/util/core.clj b/users/aspen/bbbg/src/bbbg/util/core.clj
index d458aa5592..d458aa5592 100644
--- a/users/grfn/bbbg/src/bbbg/util/core.clj
+++ b/users/aspen/bbbg/src/bbbg/util/core.clj
diff --git a/users/aspen/bbbg/src/bbbg/util/dev_secrets.clj b/users/aspen/bbbg/src/bbbg/util/dev_secrets.clj
new file mode 100644
index 0000000000..88f1b50caa
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/util/dev_secrets.clj
@@ -0,0 +1,59 @@
+(ns bbbg.util.dev-secrets
+  "Utility library for loading secrets during development from multiple
+  backends.
+
+  # Supported backends
+
+  - [Pass][0] (the default)
+
+        (bbbg.util.dev-secrets/set-backend! :pass)
+
+    Loads all secrets by shelling out to `pass <secret-name>`
+
+    [0]: https://www.passwordstore.org/
+
+  - Directory
+
+        (bbbg.util.dev-secrets/set-backend! [:dir \"/path/to/secret/directory\"])
+
+     Loads all secrets by reading the secret name as a (plaintext!) file rooted
+     at the given directory"
+  (:require [bbbg.util.core :as u]
+            [clojure.string :as str]
+            [clojure.java.io :as io]))
+
+(def ^:dynamic *secret-backend* :pass)
+
+(defn set-backend!
+  "Change the default secret-backend"
+  [backend]
+  (alter-var-root #'*secret-backend* (constantly backend)))
+
+(defmulti ^:private load-secret
+  (fn [backend _secret]
+    (if (coll? backend) (first backend) backend)))
+
+(defmethod load-secret :pass [_ secret]
+  (u/pass secret))
+
+(defmethod load-secret :dir [[_ dir] secret]
+  (str/trim (slurp (io/file dir secret))))
+
+(defn secret
+  "Load the value for the given `secret-name' from the currently selected
+  backend"
+  [secret-name]
+  (load-secret *secret-backend* secret-name))
+
+(comment
+  (secret "bbbg/discord-client-id")
+
+  (binding [*secret-backend* [:dir "/tmp/bbbg-secrets"]]
+    (secret "bbbg/discord-client-id"))
+
+  (set-backend! [:dir "/tmp/bbbg-secrets"])
+  (secret "bbbg/discord-client-id")
+
+  (set-backend! :pass)
+  (secret "bbbg/discord-client-id")
+  )
diff --git a/users/grfn/bbbg/src/bbbg/util/display.clj b/users/aspen/bbbg/src/bbbg/util/display.clj
index 40716632a3..40716632a3 100644
--- a/users/grfn/bbbg/src/bbbg/util/display.clj
+++ b/users/aspen/bbbg/src/bbbg/util/display.clj
diff --git a/users/grfn/bbbg/src/bbbg/util/spec.clj b/users/aspen/bbbg/src/bbbg/util/spec.clj
index 89ac926699..89ac926699 100644
--- a/users/grfn/bbbg/src/bbbg/util/spec.clj
+++ b/users/aspen/bbbg/src/bbbg/util/spec.clj
diff --git a/users/grfn/bbbg/src/bbbg/util/sql.clj b/users/aspen/bbbg/src/bbbg/util/sql.clj
index 988959fd06..988959fd06 100644
--- a/users/grfn/bbbg/src/bbbg/util/sql.clj
+++ b/users/aspen/bbbg/src/bbbg/util/sql.clj
diff --git a/users/grfn/bbbg/src/bbbg/util/time.clj b/users/aspen/bbbg/src/bbbg/util/time.clj
index 0278f89f5e..0278f89f5e 100644
--- a/users/grfn/bbbg/src/bbbg/util/time.clj
+++ b/users/aspen/bbbg/src/bbbg/util/time.clj
diff --git a/users/grfn/bbbg/src/bbbg/views/flash.clj b/users/aspen/bbbg/src/bbbg/views/flash.clj
index a44b21d4cb..a44b21d4cb 100644
--- a/users/grfn/bbbg/src/bbbg/views/flash.clj
+++ b/users/aspen/bbbg/src/bbbg/views/flash.clj
diff --git a/users/grfn/bbbg/src/bbbg/web.clj b/users/aspen/bbbg/src/bbbg/web.clj
index f9755577a5..f9755577a5 100644
--- a/users/grfn/bbbg/src/bbbg/web.clj
+++ b/users/aspen/bbbg/src/bbbg/web.clj
diff --git a/users/grfn/bbbg/test/bbbg/meetup/import_test.clj b/users/aspen/bbbg/test/bbbg/meetup/import_test.clj
index d7d698a58c..d7d698a58c 100644
--- a/users/grfn/bbbg/test/bbbg/meetup/import_test.clj
+++ b/users/aspen/bbbg/test/bbbg/meetup/import_test.clj
diff --git a/users/grfn/bbbg/tf.nix b/users/aspen/bbbg/tf.nix
index 097ad59579..e6ea69dfd0 100644
--- a/users/grfn/bbbg/tf.nix
+++ b/users/aspen/bbbg/tf.nix
@@ -1,16 +1,19 @@
 { depot, ... }:
 
 let
-  inherit (depot.users.grfn)
+  inherit (depot.users.aspen)
     terraform
-  ;
+    ;
 
-in terraform.workspace "bbbg" {
+in
+terraform.workspace "bbbg"
+{
   plugins = (p: with p; [
     aws
     cloudflare
   ]);
-} {
+}
+{
   machine = terraform.nixosMachine {
     name = "bbbg";
     instanceType = "t3a.small";
@@ -47,13 +50,13 @@ in terraform.workspace "bbbg" {
         ];
         shell = pkgs.zsh;
         openssh.authorizedKeys.keys = [
-          depot.users.grfn.keys.main
+          depot.users.aspen.keys.main
         ];
       };
 
       security.sudo.extraRules = [{
-        groups = ["wheel"];
-        commands = [{ command = "ALL"; options = ["NOPASSWD"]; }];
+        groups = [ "wheel" ];
+        commands = [{ command = "ALL"; options = [ "NOPASSWD" ]; }];
       }];
 
       nix.gc = {
@@ -64,7 +67,7 @@ in terraform.workspace "bbbg" {
 
       age.secrets = {
         bbbg.file =
-          depot.users.grfn.secrets."bbbg.age";
+          depot.users.aspen.secrets."bbbg.age";
       };
 
       services.bbbg.enable = true;
diff --git a/users/grfn/emacs.d/+bindings.el b/users/aspen/emacs.d/+bindings.el
index 0faa7f9ed8..0bcc922635 100644
--- a/users/grfn/emacs.d/+bindings.el
+++ b/users/aspen/emacs.d/+bindings.el
@@ -119,6 +119,9 @@ private/hlissner/snippets."
 
 (evil-set-command-property 'flycheck-next-error :repeat nil)
 (evil-set-command-property 'flycheck-prev-error :repeat nil)
+(evil-set-command-property 'flycheck-previous-error :repeat nil)
+(evil-set-command-property 'smerge-next :repeat nil)
+(evil-set-command-property 'smerge-prev :repeat nil)
 
 ;;;
 
@@ -376,7 +379,7 @@ private/hlissner/snippets."
      :desc "Slack Unreads"         :n  "u" #'slack-select-unread-rooms
      :desc "Slack Threads"         :n  "r" #'slack-all-threads
 
-     :desc "Email"                 :n "m" #'notmuch-tree-jump-search
+     :desc "Email"                 :n "m" #'notmuch-jump-search
 
      (:desc "ERC" :prefix "e"
        :desc "Channel" :n "c" #'erc-switch-to-buffer)
@@ -809,7 +812,6 @@ private/hlissner/snippets."
  ;; Rust
  (:after rust
    (:map rust-mode-map
-     "K"     #'racer-describe
      "g RET" #'cargo-process-test))
 
  ;; Elixir
@@ -1427,4 +1429,11 @@ If invoked with a prefix ARG eval the expression after inserting it"
 
  (:map prolog-mode-map
   :n "g SPC" #'prolog-compile-buffer
-  :n "g \\" #'run-prolog))
+  :n "g \\" #'run-prolog)
+
+ (:map tuareg-mode-map
+  :n "g RET" (Ξ»! () (compile "dune build @@runtest"))
+  :n "g SPC" #'dune-promote
+  :n "g \\" #'utop
+  :n "g y" #'merlin-locate-type
+  "C-c C-f" (Ξ»! () (compile "dune fmt"))))
diff --git a/users/grfn/emacs.d/+commands.el b/users/aspen/emacs.d/+commands.el
index 518f185cb9..518f185cb9 100644
--- a/users/grfn/emacs.d/+commands.el
+++ b/users/aspen/emacs.d/+commands.el
diff --git a/users/grfn/emacs.d/+private.el.gpg b/users/aspen/emacs.d/+private.el.gpg
index 6273c67d6e..6273c67d6e 100644
--- a/users/grfn/emacs.d/+private.el.gpg
+++ b/users/aspen/emacs.d/+private.el.gpg
Binary files differdiff --git a/users/grfn/emacs.d/.gitignore b/users/aspen/emacs.d/.gitignore
index 1fd0e39887..1fd0e39887 100644
--- a/users/grfn/emacs.d/.gitignore
+++ b/users/aspen/emacs.d/.gitignore
diff --git a/users/grfn/emacs.d/autoload/evil.el b/users/aspen/emacs.d/autoload/evil.el
index 319c93c05e..319c93c05e 100644
--- a/users/grfn/emacs.d/autoload/evil.el
+++ b/users/aspen/emacs.d/autoload/evil.el
diff --git a/users/grfn/emacs.d/autoload/hlissner.el b/users/aspen/emacs.d/autoload/hlissner.el
index 87b2236d12..87b2236d12 100644
--- a/users/grfn/emacs.d/autoload/hlissner.el
+++ b/users/aspen/emacs.d/autoload/hlissner.el
diff --git a/users/grfn/emacs.d/clocked-in-elt.el b/users/aspen/emacs.d/clocked-in-elt.el
index be4161441d..be4161441d 100644
--- a/users/grfn/emacs.d/clocked-in-elt.el
+++ b/users/aspen/emacs.d/clocked-in-elt.el
diff --git a/users/grfn/emacs.d/clojure.el b/users/aspen/emacs.d/clojure.el
index f001a3e12b..f001a3e12b 100644
--- a/users/grfn/emacs.d/clojure.el
+++ b/users/aspen/emacs.d/clojure.el
diff --git a/users/grfn/emacs.d/company-sql.el b/users/aspen/emacs.d/company-sql.el
index e623aa2de1..e623aa2de1 100644
--- a/users/grfn/emacs.d/company-sql.el
+++ b/users/aspen/emacs.d/company-sql.el
diff --git a/users/grfn/emacs.d/config.el b/users/aspen/emacs.d/config.el
index 06178d7cc9..6398feace8 100644
--- a/users/grfn/emacs.d/config.el
+++ b/users/aspen/emacs.d/config.el
@@ -228,12 +228,13 @@
 (use-package! org-tracker
   :hook (org-mode . org-tracker-mode)
   :config
-  (setq org-tracker-state-alist '(("INBOX" . "Inbox")
+  (setq org-tracker-state-alist '(("INBOX" . "Triage")
                                   ("BACKLOG" . "Backlog")
-                                  ("TODO" . "Selected for Development")
+                                  ("TODO" . "Todo")
                                   ("ACTIVE" . "In Progress")
                                   ("PR" . "Code Review")
-                                  ("DONE" . "Done"))
+                                  ("DONE" . "Done")
+                                  ("CANCELLED" . "Canceled"))
         org-tracker-username "griffin@readyset.io"
         org-tracker-claim-ticket-on-status-update '("ACTIVE" "PR" "DONE")
         org-tracker-create-stories-with-labels 'existing)
@@ -346,7 +347,9 @@
   (set-face-foreground 'slack-message-output-header +solarized-s-base01)
   (set-face-attribute 'slack-message-output-header nil :underline nil)
   (set-face-attribute 'slack-message-output-text nil :height 1.0)
-  )
+  (set-face-attribute 'caml-types-expr-face
+                      nil
+                      :background +solarized-s-base2))
 
 (after! solarized-theme
   (set-face-foreground 'font-lock-doc-face +solarized-s-base1)
@@ -467,13 +470,53 @@
 
 (setq projectile-create-missing-test-files 't)
 
+(setq grfn/tracker-refs-re
+      (rx line-start
+          (or "Refs" "Fixes")
+          ": "
+          (one-or-more graph)
+          line-end))
+
+(defun grfn/add-tracker-reference-to-commit-message ()
+  (interactive)
+  (when-let* ((ticket-id (grfn/org-clocked-in-ticket-id)))
+    (save-excursion
+      (save-match-data
+        (goto-char (point-min))
+        ;; Don't add one if we've already got one
+        (unless (search-forward-regexp grfn/tracker-refs-re nil t)
+          (or
+           (and
+            (search-forward-regexp (rx line-start "Change-Id:") nil t)
+            (forward-line -1))
+           (and
+            (search-forward-regexp (rx line-start "# Please enter") nil t)
+            (forward-line -2)))
+          (insert (format "\nRefs: %s" ticket-id)))))))
+
+(defun grfn/switch-tracker-refs-fixes ()
+  (interactive)
+  (save-excursion
+    (save-match-data
+      (if (not (search-forward-regexp grfn/tracker-refs-re nil t))
+          (message "Could not find reference to ticket")
+        (goto-char (point-at-bol))
+        (save-restriction
+          (narrow-to-region (point-at-bol)
+                            (point-at-eol))
+          (or
+           (and (search-forward "Refs" nil t)
+                (replace-match "Fixes"))
+           (and (search-forward "Fixes" nil t)
+                (replace-match "Refs"))))))))
+
 (after! magit
   (map! :map magit-mode-map
         ;; :n "] ]" #'magit-section-forward
         ;; :n "[ [" #'magit-section-backward
         )
 
-  (define-suffix-command magit-commit-wip ()
+  (transient-define-suffix magit-commit-wip ()
     (interactive)
     (magit-commit-create '("-m" "wip")))
 
@@ -482,11 +525,11 @@
     ["c"]
     (list "W" "Commit WIP" #'magit-commit-wip))
 
-  (define-suffix-command magit-reset-head-back ()
+  (transient-define-suffix magit-reset-head-back ()
     (interactive)
     (magit-reset-mixed "HEAD~"))
 
-  (define-suffix-command magit-reset-head-previous ()
+  (transient-define-suffix magit-reset-head-previous ()
     (interactive)
     (magit-reset-mixed "HEAD@{1}"))
 
@@ -537,7 +580,13 @@
   (transient-append-suffix
     #'magit-branch
     ["c"]
-    (list "M" "Rename branch to Tracker ticket" #'magit-rename-org-tracker-branch)))
+    (list "M" "Rename branch to Tracker ticket" #'magit-rename-org-tracker-branch))
+
+  )
+
+(add-hook 'git-commit-setup-hook #'grfn/add-tracker-reference-to-commit-message)
+(map! (:map git-commit-mode-map
+       "C-c C-f" #'grfn/switch-tracker-refs-fixes))
 
 ;; (defun grfn/split-window-more-sensibly (&optional window)
 ;;   (let ((window (or window (selected-window))))
@@ -740,15 +789,8 @@
         cider-save-file-on-load 't)
   )
 
-(defun +org-clocked-in-element ()
-  (when-let ((item (car org-clock-history)))
-    (save-mark-and-excursion
-    (with-current-buffer (marker-buffer item)
-      (goto-char (marker-position item))
-      (org-element-at-point)))))
-
 (comment
- (setq elt (+org-clocked-in-item))
+ (setq elt (+org-clocked-in-element))
 
  (eq 'headline (car elt))
  (plist-get (cadr elt) :raw-value)
@@ -854,8 +896,7 @@
   (map! [remap counsel-org-capture] #'org-capture
         [remap org-capture] #'org-capture))
 
-(use-package! evil-snipe :disabled t)
-(evil-snipe-mode -1)
+(remove-hook 'doom-first-input-hook #'evil-snipe-mode)
 
 (use-package! rainbow-mode)
 
@@ -1081,3 +1122,18 @@
 (set-popup-rule!
   "^\\*gud-"
   :quit nil)
+
+(setq elcord-editor-icon "emacs_icon")
+
+;;; ocaml
+
+(after! merlin-mode
+  (set-face-attribute
+   'caml-types-expr-face
+   nil
+   :background +solarized-s-base2)
+  (add-hook! merlin-mode
+     (setq whitespace-line-column 90)))
+
+(use-package! dune-format
+  :hook (dune-mode . dune-format-on-save-mode))
diff --git a/users/aspen/emacs.d/cpp.el b/users/aspen/emacs.d/cpp.el
new file mode 100644
index 0000000000..6068736eca
--- /dev/null
+++ b/users/aspen/emacs.d/cpp.el
@@ -0,0 +1,39 @@
+;;; -*- lexical-binding: t; -*-
+
+
+(load! "google-c-style")
+
+(after! flycheck
+  (add-to-list 'flycheck-disabled-checkers 'c/c++-gcc)
+  (add-to-list 'flycheck-disabled-checkers 'c/c++-clang))
+
+(defun +aspen/cpp-setup ()
+  (when (s-starts-with?
+         "/home/aspen/code/depot/third_party/nix"
+         (buffer-file-name))
+    (setq lsp-clients-clangd-executable "/home/aspen/code/depot/users/aspen/emacs.d/nix-clangd.sh"
+          lsp-clients-clangd-args nil)
+    (google-set-c-style)
+    (lsp)
+    (add-to-list 'flycheck-disabled-checkers 'c/c++-gcc)
+    (add-to-list 'flycheck-disabled-checkers 'c/c++-clang)))
+
+(add-hook 'c++-mode-hook #'+aspen/cpp-setup)
+
+(use-package! protobuf-mode)
+
+(use-package! clang-format+
+  :config
+  (add-hook 'c-mode-common-hook #'clang-format+-mode))
+
+(map!
+ (:map c++-mode-map
+  :leader
+  (:n "/ i" #'counsel-semantic-or-imenu)))
+
+(comment
+ (setq
+  lsp-clients-clangd-executable
+  "/home/aspen/code/depot/third_party/nix/clangd.sh"
+  lsp-clients-clangd-args nil)
+ )
diff --git a/users/grfn/emacs.d/email.el b/users/aspen/emacs.d/email.el
index 83076898b4..70360d0072 100644
--- a/users/grfn/emacs.d/email.el
+++ b/users/aspen/emacs.d/email.el
@@ -40,3 +40,14 @@
                    (eq (car action)
                        'display-buffer-same-window))
   :ignore t)
+
+(defun apply-thread-patchset (repo branch)
+  (interactive "Dgit repo: \nsnew branch name: ")
+  (let ((tid notmuch-show-thread-id)
+        (tmp "/tmp/notmuch-patchset"))
+    (shell-command (format "notmuch-extract-patch %s > %s && ( cd %s && git checkout -b %s && git am %s )"
+                           (shell-quote-argument tid)
+                           (shell-quote-argument tmp)
+                           (shell-quote-argument (expand-file-name repo))
+                           (shell-quote-argument branch)
+                           (shell-quote-argument tmp)))))
diff --git a/users/grfn/emacs.d/github-org.el b/users/aspen/emacs.d/github-org.el
index f4f9d2e370..f4f9d2e370 100644
--- a/users/grfn/emacs.d/github-org.el
+++ b/users/aspen/emacs.d/github-org.el
diff --git a/users/grfn/emacs.d/google-c-style.el b/users/aspen/emacs.d/google-c-style.el
index 9bb12c61aa..9bb12c61aa 100644
--- a/users/grfn/emacs.d/google-c-style.el
+++ b/users/aspen/emacs.d/google-c-style.el
diff --git a/users/grfn/emacs.d/grid.el b/users/aspen/emacs.d/grid.el
index 75776a38cd..75776a38cd 100644
--- a/users/grfn/emacs.d/grid.el
+++ b/users/aspen/emacs.d/grid.el
diff --git a/users/aspen/emacs.d/init.el b/users/aspen/emacs.d/init.el
new file mode 100644
index 0000000000..46530ab950
--- /dev/null
+++ b/users/aspen/emacs.d/init.el
@@ -0,0 +1,175 @@
+;;; -*- lexical-binding: t; -*-
+
+(defvar native-comp-deferred-compilation-deny-list nil)
+
+(doom! :completion
+       company           ; the ultimate code completion backend
+       (ivy +fuzzy
+            +prescient)               ; a search engine for love and life
+
+       :ui
+       ;;deft              ; notational velocity for Emacs
+       doom              ; what makes DOOM look the way it does
+       ;doom-dashboard    ; a nifty splash screen for Emacs
+       doom-quit         ; DOOM quit-message prompts when you quit Emacs
+       ;fill-column       ; a `fill-column' indicator
+       hl-todo           ; highlight TODO/FIXME/NOTE tags
+       ;;indent-guides     ; highlighted indent columns
+       modeline          ; snazzy, Atom-inspired modeline, plus API
+       nav-flash         ; blink the current line after jumping
+       ;;neotree           ; a project drawer, like NERDTree for vim
+       ophints           ; highlight the region an operation acts on
+       (popup            ; tame sudden yet inevitable temporary windows
+        +all             ; catch all popups that start with an asterix
+        +defaults)       ; default popup rules
+       ;; ligatures         ; replace bits of code with pretty symbols
+       ;; tabbar            ; FIXME an (incomplete) tab bar for Emacs
+       ;; treemacs          ; a project drawer, like neotree but cooler
+       unicode           ; extended unicode support for various languages
+       vc-gutter         ; vcs diff in the fringe
+       vi-tilde-fringe   ; fringe tildes to mark beyond EOB
+       ;;window-select     ; visually switch windows
+       workspaces        ; tab emulation, persistence & separate workspaces
+
+       :editor
+       (evil +everywhere); come to the dark side, we have cookies
+       file-templates    ; auto-snippets for empty files
+       fold              ; (nigh) universal code folding
+       ;;(format +onsave)  ; automated prettiness
+       ;;god               ; run Emacs commands without modifier keys
+       ;;lispy             ; vim for lisp, for people who dont like vim
+       ;;multiple-cursors  ; editing in many places at once
+       ;;objed             ; text object editing for the innocent
+       ;;parinfer          ; turn lisp into python, sort of
+       ;;rotate-text       ; cycle region at point between text candidates
+       snippets          ; my elves. They type so I don't have to
+       word-wrap
+
+       :emacs
+       dired             ; making dired pretty [functional]
+       electric          ; smarter, keyword-based electric-indent
+       ;;eshell            ; a consistent, cross-platform shell (WIP)
+       ;;term              ; terminals in Emacs
+       (undo +tree)
+       vc                ; version-control and Emacs, sitting in a tree
+
+       :tools
+       ;;ansible
+       ;;debugger          ; FIXME stepping through code, to help you add bugs
+       ;;direnv
+       docker
+       ;;editorconfig      ; let someone else argue about tabs vs spaces
+       ;; ein               ; tame Jupyter notebooks with emacs
+       (eval +overlay)              ; run code, run (also, repls)
+       gist              ; interacting with github gists
+       (lookup           ; helps you navigate your code and documentation
+        +docsets)        ; ...or in Dash docsets locally
+       lsp
+       ;;macos             ; MacOS-specific commands
+       magit             ; a git porcelain for Emacs
+       make              ; run make tasks from Emacs
+       pass              ; password manager for nerds
+       pdf               ; pdf enhancements
+       ;;prodigy           ; FIXME managing external services & code builders
+       ;;rgb               ; creating color strings
+       ;;terraform         ; infrastructure as code
+       ;;tmux              ; an API for interacting with tmux
+       tree-sitter       ; syntax and parsing, sitting in a tree...
+       ;;upload            ; map local to remote projects via ssh/ftp
+       ;;wakatime
+       ;;vterm             ; another terminals in Emacs
+
+       :checkers
+       syntax          ; tasing you for every semicolon you forget
+       ; spell           ; tasing you for misspelling mispelling
+
+       :lang
+       agda              ; types of types of types of types...
+       ;;assembly          ; assembly for fun or debugging
+       cc                ; C/C++/Obj-C madness
+       clojure           ; java with a lisp
+       common-lisp       ; if you've seen one lisp, you've seen them all
+       ; coq               ; proofs-as-programs
+       ;;crystal           ; ruby at the speed of c
+       ;;csharp            ; unity, .NET, and mono shenanigans
+       data              ; config/data formats
+       erlang            ; an elegant language for a more civilized age
+       elixir            ; erlang done right
+       ;;elm               ; care for a cup of TEA?
+       emacs-lisp        ; drown in parentheses
+       ;;ess               ; emacs speaks statistics
+       ;;go                ; the hipster dialect
+       ;; (haskell +intero) ; a language that's lazier than I am
+       haskell ; a language that's lazier than I am
+       ;;hy                ; readability of scheme w/ speed of python
+       ;; idris             ;
+       ;;(java +meghanada) ; the poster child for carpal tunnel syndrome
+       javascript        ; all(hope(abandon(ye(who(enter(here))))))
+       julia             ; a better, faster MATLAB
+       ;;kotlin            ; a better, slicker Java(Script)
+       latex             ; writing papers in Emacs has never been so fun
+       ;;ledger            ; an accounting system in Emacs
+       lua               ; one-based indices? one-based indices
+       markdown          ; writing docs for people to ignore
+       ;;nim               ; python + lisp at the speed of c
+       nix               ; I hereby declare "nix geht mehr!"
+       ocaml             ; an objective camel
+       (org              ; organize your plain life in plain text
+        +dragndrop       ; drag & drop files/images into org buffers
+        +attach          ; custom attachment system
+        +babel           ; running code in org
+        +capture         ; org-capture in and outside of Emacs
+        +export          ; Exporting org to whatever you want
+        ;; +habit           ; Keep track of your habits
+        +present         ; Emacs for presentations
+        +pretty
+        +brain
+        +protocol)       ; Support for org-protocol:// links
+       ;;perl              ; write code no one else can comprehend
+       ;;php               ; perl's insecure younger brother
+       ;;plantuml          ; diagrams for confusing people more
+       purescript        ; javascript, but functional
+       (python +lsp)            ; beautiful is better than ugly
+       ;;qt                ; the 'cutest' gui framework ever
+       racket            ; a DSL for DSLs
+       rest              ; Emacs as a REST client
+       ;;ruby              ; 1.step do {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
+       (rust +tree-sitter)              ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
+       ;;scala             ; java, but good
+       (sh +fish)        ; she sells (ba|z|fi)sh shells on the C xor
+       ;;solidity          ; do you need a blockchain? No.
+       ;;swift             ; who asked for emoji variables?
+       ;;terra             ; Earth and Moon in alignment for performance.
+       ;;web               ; the tubes
+       ;;vala              ; GObjective-C
+       zig
+
+       ;; Applications are complex and opinionated modules that transform Emacs
+       ;; toward a specific purpose. They may have additional dependencies and
+       ;; should be loaded late.
+       :app
+       ;;(email +gmail)    ; emacs as an email client
+       irc               ; how neckbeards socialize
+       ;;(rss +org)        ; emacs as an RSS reader
+       twitter           ; twitter client https://twitter.com/vnought
+       ;;(write            ; emacs as a word processor (latex + org + markdown)
+       ;; +wordnut         ; wordnet (wn) search
+       ;; +langtool)       ; a proofreader (grammar/style check) for Emacs
+
+       :email
+       ;; (mu4e +gmail)
+       notmuch
+
+       :collab
+       ;;floobits          ; peer programming for a price
+       ;;impatient-mode    ; show off code over HTTP
+
+       :config
+       ;; For literate config users. This will tangle+compile a config.org
+       ;; literate config in your `doom-private-dir' whenever it changes.
+       ;;literate
+
+       ;; The default module sets reasonable defaults for Emacs. It also
+       ;; provides a Spacemacs-inspired keybinding scheme and a smartparens
+       ;; config. Use it as a reference for your own modules.
+       (default +bindings +smartparens))
diff --git a/users/grfn/emacs.d/irc.el b/users/aspen/emacs.d/irc.el
index 117869599d..117869599d 100644
--- a/users/grfn/emacs.d/irc.el
+++ b/users/aspen/emacs.d/irc.el
diff --git a/users/grfn/emacs.d/lisp.el b/users/aspen/emacs.d/lisp.el
index c45cc7e6e3..c45cc7e6e3 100644
--- a/users/grfn/emacs.d/lisp.el
+++ b/users/aspen/emacs.d/lisp.el
diff --git a/users/grfn/emacs.d/nix-clangd.sh b/users/aspen/emacs.d/nix-clangd.sh
index 16f6252d8b..16f6252d8b 100755
--- a/users/grfn/emacs.d/nix-clangd.sh
+++ b/users/aspen/emacs.d/nix-clangd.sh
diff --git a/users/grfn/emacs.d/nix.el b/users/aspen/emacs.d/nix.el
index ec5b474af2..ec5b474af2 100644
--- a/users/grfn/emacs.d/nix.el
+++ b/users/aspen/emacs.d/nix.el
diff --git a/users/grfn/emacs.d/org-alerts.el b/users/aspen/emacs.d/org-alerts.el
index 8e6c3e0417..8e6c3e0417 100644
--- a/users/grfn/emacs.d/org-alerts.el
+++ b/users/aspen/emacs.d/org-alerts.el
diff --git a/users/aspen/emacs.d/org-config.el b/users/aspen/emacs.d/org-config.el
new file mode 100644
index 0000000000..b8d88d3195
--- /dev/null
+++ b/users/aspen/emacs.d/org-config.el
@@ -0,0 +1,191 @@
+;;; -*- lexical-binding: t; -*-
+
+(defun +aspen/org-setup ()
+  (setq-local truncate-lines -1)
+  (display-line-numbers-mode -1)
+  (line-number-mode -1))
+
+(add-hook 'org-mode-hook #'+aspen/org-setup)
+
+(defun notes-file (f)
+  (concat org-directory (if (string-prefix-p "/" f) "" "/") f))
+
+(defun aspen/org-project-tag->key (tag)
+  (s-replace-regexp "^project__" "" tag))
+
+(defun aspen/org-project-tag->name (tag)
+  (s-titleized-words
+   (s-join " " (s-split "_" (aspen/org-project-tag->key tag)))))
+
+(defun aspen/org-project-tag->keys (tag)
+  (s-join "" (cons "p"
+                   (-map (lambda (s) (substring-no-properties s 0 1))
+                         (s-split "_" (aspen/org-project-tag->key tag))))))
+
+(defun aspen/org-projects->agenda-commands (project-tags)
+  (loop for tag in project-tags
+        collect `(,(aspen/org-project-tag->keys tag)
+                  ,(aspen/org-project-tag->name tag)
+                  tags-todo
+                  ,tag)))
+
+(defun aspen/org-projects ()
+  (loop for (tag) in
+        (org-global-tags-completion-table
+         (directory-files-recursively "~/notes" "\\.org$"))
+        when (s-starts-with-p "project__" tag)
+        collect tag))
+
+(comment
+ (aspen/org-projects->agenda-commands (aspen/org-projects))
+ )
+
+(setq
+ org-directory (expand-file-name "~/notes")
+ +org-dir (expand-file-name "~/notes")
+ org-default-notes-file (concat org-directory "/inbox.org")
+ +org-default-todo-file (concat org-directory "/inbox.org")
+ org-agenda-files (directory-files-recursively
+                   "~/notes" "\\.org$")
+ org-refile-targets '((org-agenda-files :maxlevel . 3))
+ org-outline-path-complete-in-steps nil
+ org-refile-use-outline-path t
+ org-file-apps `((auto-mode . emacs)
+                 (,(rx (or (and "." (optional "x") (optional "htm") (optional "l") buffer-end)
+                           (and buffer-start "http" (optional "s") "://")))
+                  . "firefox %s")
+                 (,(rx ".pdf" buffer-end) . "apvlv %s")
+                 (,(rx "." (or "png"
+                               "jpg"
+                               "jpeg"
+                               "gif"
+                               "tif"
+                               "tiff")
+                       buffer-end)
+                  . "feh %s"))
+ org-log-done 'time
+ org-archive-location "~/notes/trash::* From %s"
+ org-cycle-separator-lines 2
+ org-hidden-keywords '(title)
+ org-tags-column -130
+ org-ellipsis "…"
+ org-imenu-depth 9
+ org-capture-templates
+ `(("t" "Todo" entry
+    (file +org-default-todo-file)
+    "* TODO %?\n%i"
+    :kill-buffer t)
+
+   ("m" "Email" entry
+    (file +org-default-todo-file)
+    "* TODO [[%L][%:subject]] :email:\n%i")
+
+   ("n" "Notes" entry
+    (file +org-default-todo-file)
+    "* %U %?\n%i"
+    :prepend t
+    :kill-buffer t)
+
+   ("c" "Task note" entry
+    (clock)
+    "* %U %?\n%i[%l[Context]]\n"
+    :kill-buffer t
+    :unnarrowed t)
+
+   ("p" "Projects")
+   ("px" "Xanthous" entry
+    (file+headline ,(notes-file "xanthous.org") "Backlog")
+    "* TODO %?\nContext %a\nIn task: %K")
+   ("pt" "Tvix" entry
+    (file+headline ,(notes-file "tvix.org") "Tvix TODO")
+    "* TODO %?\nContext %a\nIn task: %K")
+   ("pw" "Windtunnel" entry
+    (file+headline ,(notes-file "windtunnel.org") "Inbox")
+    "* TODO %i%?\nContext: %a\nIn task: %K")
+   )
+
+ org-capture-templates-contexts
+ `(("px" ((in-file . "/home/aspen/code/depot/users/aspen/xanthous/.*")))
+   ("e" ((in-mode . "notmuch-show-mode"))))
+
+ org-deadline-warning-days 1
+ org-agenda-skip-scheduled-if-deadline-is-shown 'todo
+ org-todo-keywords '((sequence "TODO(t)" "ACTIVE(a)" "|" "DONE(d)" "RUNNING(r)")
+                     (sequence "NEXT(n)" "WAITING(w)" "LATER(l)" "|" "CANCELLED(c)"))
+ org-agenda-custom-commands
+ `(("i" "Inbox" tags "inbox")
+   ("r" "Running jobs" todo "RUNNING")
+   ("w" "@Work" tags-todo "@work")
+   ("n" . "Next...")
+   ("nw" "Next @Work" tags-todo "@work&next")
+   ("nt" "Next tooling" tags-todo "tooling")
+
+   ("p" . "Project...")
+   ,@(aspen/org-projects->agenda-commands (aspen/org-projects)))
+
+ org-agenda-dim-blocked-tasks nil
+ org-enforce-todo-dependencies nil
+
+ org-babel-clojure-backend 'cider)
+
+(defun +aspen/insert-work-template ()
+  (interactive)
+  (goto-char (point-min))
+  (forward-line)
+  (insert "#+TODO: TODO(t) NEXT(n) ACTIVE(a) | DONE(d) PR(p) RUNNING(r) TESTING(D)
+#+TODO: BLOCKED(b) BACKLOG(l) PROPOSED(o) | CANCELLED(c)
+#+FILETAGS: @work
+#+FILETAGS: @work
+#+PROPERTY: Effort_ALL 0 4:00 8:00 12:00 20:00 32:00
+#+PROPERTY: ESTIMATE_ALL 0 1 2 3 5 8
+#+PROPERTY: STORY-TYPE_ALL Feature Bug Chore
+#+PROPERTY: NOBLOCKING t
+#+COLUMNS: %TODO %40ITEM(Task) %17EFFORT(Estimated){:} %CLOCKSUM(Time Spent) %17STORY-TYPE(Type) %TAGS"))
+
+(defun +aspen/insert-org-template ()
+  (interactive)
+  (pcase (buffer-file-name)
+    ((s-contains "/work/") (+aspen/insert-work-template))))
+
+;;; TODO: this doesn't work?
+(define-auto-insert "\\.org?$" #'aspen/insert-org-template t)
+
+(defun forge--post-submit-around---link-pr-to-org-item
+    (orig)
+  (let ((cb (funcall orig)))
+    (lambda (value headers status req)
+      (prog1 (funcall cb value headers status req)
+        (aspen/at-org-clocked-in-item
+         (let ((url (alist-get 'html_url value))
+               (number (alist-get 'number value)))
+           (org-set-property
+            "pull-request"
+            (org-make-link-string
+             url
+             (format "%s/%s/%d"
+                     (->> value
+                          (alist-get 'base)
+                          (alist-get 'repo)
+                          (alist-get 'name))
+                     (->> value
+                          (alist-get 'base)
+                          (alist-get 'repo)
+                          (alist-get 'owner)
+                          (alist-get 'login))
+                     number)))))))))
+
+(advice-add
+ #'forge--post-submit-callback
+ :around #'forge--post-submit-around---link-pr-to-org-item)
+
+(set-face-foreground 'org-block +solarized-s-base00)
+(setq whitespace-global-modes '(not org-mode magit-mode vterm-mode))
+(setf (alist-get 'file org-link-frame-setup) 'find-file-other-window)
+(set-face-foreground 'org-block +solarized-s-base00)
+
+;; (add-hook! org-mode
+;;   (set-company-backend! 'org-mode
+;;     '(:separate company-ob-postgresql
+;;                 company-dabbrev
+;;                 company-yasnippet
+;;                 company-ispell)))
diff --git a/users/grfn/emacs.d/org-gcal.el b/users/aspen/emacs.d/org-gcal.el
index 3e315c5e60..3e315c5e60 100644
--- a/users/grfn/emacs.d/org-gcal.el
+++ b/users/aspen/emacs.d/org-gcal.el
diff --git a/users/grfn/emacs.d/org-query.el b/users/aspen/emacs.d/org-query.el
index 022832c05f..9d3b3358a9 100644
--- a/users/grfn/emacs.d/org-query.el
+++ b/users/aspen/emacs.d/org-query.el
@@ -114,3 +114,30 @@
 (comment
  (grfn/org-current-clocked-in-task-message)
  )
+
+(cl-defgeneric grfn/org-tracker-ticket-id-label (backend elt)
+  (org-tracker-backend/extract-issue-id backend elt))
+(cl-defmethod grfn/org-tracker-ticket-id-label
+  ((backend org-tracker-linear-backend) elt)
+  (when-let* ((link (plist-get elt :LINEAR-KEY)))
+    (string-match
+     (rx "[[" (one-or-more anything) "]"
+         "[" (group (one-or-more anything)) "]]")
+     link)
+    (match-string 1 link)))
+
+(defun grfn/org-clocked-in-ticket-id ()
+  (grfn/at-org-clocked-in-item
+   (when-let* ((backend (org-tracker-current-backend t)))
+     (grfn/org-tracker-ticket-id-label
+      backend
+      (cadr (org-element-at-point))))))
+
+(comment
+ (grfn/at-org-clocked-in-item
+  (org-tracker-backend/extract-issue-id
+   (org-tracker-current-backend)
+   (cadr (org-element-at-point))))
+
+ (grfn/org-clocked-in-ticket-id)
+ )
diff --git a/users/aspen/emacs.d/packages.el b/users/aspen/emacs.d/packages.el
new file mode 100644
index 0000000000..15a3843f4d
--- /dev/null
+++ b/users/aspen/emacs.d/packages.el
@@ -0,0 +1,154 @@
+;; -*- no-byte-compile: t; -*-
+;;; private/grfn/packages.el
+
+(package! moody)
+
+;; Editor
+(package! solarized-theme)
+(package! fill-column-indicator)
+(package! flx)
+(package! general
+  :recipe (:host github :repo "noctuid/general.el"))
+(package! fill-column-indicator)
+(package! writeroom-mode)
+(package! dash)
+(package! w3m)
+(package! rainbow-mode)
+(package! string-inflection)
+
+;;; Org
+(package! org-tracker
+  :recipe (:host file
+           :local-repo "~/code/org-tracker"))
+(package! jiralib2)
+(package! org-alert)
+(package! ob-http)
+(package! ob-ipython)
+(package! ob-async)
+(package! org-recent-headings)
+(package! org-sticky-header)
+(package! gnuplot)
+(package! gnuplot-mode)
+(package! org-d20)
+
+;; Presentation
+(package! epresent)
+(package! org-tree-slide)
+(package! ox-reveal)
+
+;; Slack etc
+(package! slack)
+(package! alert)
+
+;; Git
+(package! evil-magit)
+(package! marshal)
+(package! forge)
+(package!
+  github-review
+  :recipe
+  (:host github
+         :repo "charignon/github-review"
+         :files ("github-review.el")))
+
+;; Elisp
+(package! dash)
+(package! dash-functional)
+(package! s)
+(package! request)
+(package! predd
+  :recipe (:host github :repo "skeeto/predd"))
+(package! aio)
+
+;; Haskell
+(package! lsp-haskell)
+(package! counsel-etags)
+
+;;; LSP
+(package! lsp-mode)
+(package! lsp-ui :recipe (:host github :repo "emacs-lsp/lsp-ui"))
+(package! company-lsp)
+(package! lsp-treemacs)
+
+;; Rust
+;; (package! rustic :disable t)
+;; (package! racer :disable t)
+(package! cargo)
+
+;; Lisp
+(package! paxedit)
+
+;; Javascript
+(package! flow-minor-mode)
+(package! flycheck-flow)
+(package! company-flow)
+(package! prettier-js)
+
+;; GraphQL
+(package! graphql-mode)
+
+;; Haskell
+(package! lsp-mode)
+(package! lsp-ui)
+(package! lsp-haskell)
+(package! company-lsp)
+;; (package! lsp-imenu)
+
+;; Clojure
+(package! flycheck-clojure)
+
+;; SQL
+(package! sqlup-mode)
+(package! emacsql)
+(package! emacsql-psql)
+
+;;; Python
+(package! pyimport)
+;; (package! yapfify)
+(package! blacken)
+
+
+;;; Desktop interaction
+(package! counsel-spotify)
+
+;;; Dhall
+(package! dhall-mode)
+
+;;; Kubernetes
+(package! kubernetes)
+(package! kubernetes-evil)
+(package! k8s-mode)
+
+;;; Stack Exchange
+(package! sx)
+
+;;; Nix
+(package! nix-update
+  :recipe (:host github
+           :repo "glittershark/nix-update-el"))
+(package! direnv)
+
+;;; Sequence diagrams
+(package! wsd-mode
+  :recipe (:host github
+           :repo "josteink/wsd-mode"))
+
+;;; logic?
+(package! metal-mercury-mode
+  :recipe (:host github
+                 :repo "ahungry/metal-mercury-mode"))
+(package! flycheck-mercury)
+
+(package! terraform-mode)
+(package! company-terraform)
+
+(package! jsonnet-mode)
+
+;;;
+(package! znc
+  :recipe (:host github
+                 :repo "sshirokov/ZNC.el"))
+
+;;; cpp
+(package! protobuf-mode)
+(package! clang-format+)
diff --git a/users/grfn/emacs.d/rust.el b/users/aspen/emacs.d/rust.el
index 689d843e78..9988d16a53 100644
--- a/users/grfn/emacs.d/rust.el
+++ b/users/aspen/emacs.d/rust.el
@@ -5,18 +5,20 @@
 (defun grfn/rust-setup ()
   (interactive)
 
-  (push '(?> . ("<" . ">")) evil-surround-pairs-alist)
-  (push '(?< . ("< " . " >")) evil-surround-pairs-alist)
+  (direnv--maybe-update-environment)
 
-  (setq lsp-rust-server 'rust-analyzer)
+  (+evil-embrace-angle-bracket-modes-hook-h)
+
+  ;; (setq lsp-rust-server 'rust-analyzer)
   (setq-local whitespace-line-column 100
               fill-column 100)
-  (setq rust-format-show-buffer nil)
+  ;; (setq rust-format-show-buffer nil)
   (setq lsp-rust-analyzer-import-merge-behaviour "last"
         lsp-rust-analyzer-cargo-watch-command "clippy"
         lsp-rust-analyzer-cargo-watch-args ["--target-dir" "/home/grfn/code/readyset/readyset/target/rust-analyzer"]
+        rustic-format-trigger 'on-save
         lsp-ui-doc-enable t)
-  (rust-enable-format-on-save)
+  ;; (rust-enable-format-on-save)
   (lsp))
 
 (add-hook 'rust-mode-hook #'grfn/rust-setup)
@@ -26,6 +28,7 @@
   :n "g RET" #'lsp-rust-analyzer-run
   :n "g R" #'lsp-find-references
   :n "g d" #'lsp-find-definition
+  :n "g Y" #'lsp-goto-type-definition
   (:localleader
    "m" #'lsp-rust-analyzer-expand-macro)))
 
diff --git a/users/grfn/emacs.d/show-matching-paren.el b/users/aspen/emacs.d/show-matching-paren.el
index ab65a912a8..ab65a912a8 100644
--- a/users/grfn/emacs.d/show-matching-paren.el
+++ b/users/aspen/emacs.d/show-matching-paren.el
diff --git a/users/grfn/emacs.d/slack-snippets.el b/users/aspen/emacs.d/slack-snippets.el
index b5bd4db748..b5bd4db748 100644
--- a/users/grfn/emacs.d/slack-snippets.el
+++ b/users/aspen/emacs.d/slack-snippets.el
diff --git a/users/grfn/emacs.d/slack.el b/users/aspen/emacs.d/slack.el
index 54d3b40b09..54d3b40b09 100644
--- a/users/grfn/emacs.d/slack.el
+++ b/users/aspen/emacs.d/slack.el
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/annotation b/users/aspen/emacs.d/snippets/haskell-mode/annotation
index 8a2854d759..8a2854d759 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/annotation
+++ b/users/aspen/emacs.d/snippets/haskell-mode/annotation
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/benchmark-module b/users/aspen/emacs.d/snippets/haskell-mode/benchmark-module
index cbb1646e41..cbb1646e41 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/benchmark-module
+++ b/users/aspen/emacs.d/snippets/haskell-mode/benchmark-module
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/header b/users/aspen/emacs.d/snippets/haskell-mode/header
index fdd8250d86..fdd8250d86 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/header
+++ b/users/aspen/emacs.d/snippets/haskell-mode/header
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/hedgehog-generator b/users/aspen/emacs.d/snippets/haskell-mode/hedgehog-generator
index 68863f7054..68863f7054 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/hedgehog-generator
+++ b/users/aspen/emacs.d/snippets/haskell-mode/hedgehog-generator
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/hedgehog-property b/users/aspen/emacs.d/snippets/haskell-mode/hedgehog-property
index bf39a2a3ee..bf39a2a3ee 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/hedgehog-property
+++ b/users/aspen/emacs.d/snippets/haskell-mode/hedgehog-property
diff --git a/users/aspen/emacs.d/snippets/haskell-mode/hlint b/users/aspen/emacs.d/snippets/haskell-mode/hlint
new file mode 100644
index 0000000000..f25a9b8d40
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/haskell-mode/hlint
@@ -0,0 +1,8 @@
+# -*- mode: snippet -*-
+# name: hlint
+# uuid: hlint
+# expand-env: ((yas-indent-line 'fixed))
+# key: hlint
+# condition: t
+# --
+{-# ANN module ("Hlint: ignore $1" :: String) #- }
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/import-i b/users/aspen/emacs.d/snippets/haskell-mode/import-i
index 4a7fca2c2f..4a7fca2c2f 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/import-i
+++ b/users/aspen/emacs.d/snippets/haskell-mode/import-i
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/inl b/users/aspen/emacs.d/snippets/haskell-mode/inl
index 6e17b83d71..6e17b83d71 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/inl
+++ b/users/aspen/emacs.d/snippets/haskell-mode/inl
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/inline b/users/aspen/emacs.d/snippets/haskell-mode/inline
index 1beafbe50b..1beafbe50b 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/inline
+++ b/users/aspen/emacs.d/snippets/haskell-mode/inline
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/language pragma b/users/aspen/emacs.d/snippets/haskell-mode/language pragma
index 6f84720f45..6f84720f45 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/language pragma
+++ b/users/aspen/emacs.d/snippets/haskell-mode/language pragma
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/lens.field b/users/aspen/emacs.d/snippets/haskell-mode/lens.field
index b22ea3d2e8..b22ea3d2e8 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/lens.field
+++ b/users/aspen/emacs.d/snippets/haskell-mode/lens.field
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/module b/users/aspen/emacs.d/snippets/haskell-mode/module
index 4554d33f9b..4554d33f9b 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/module
+++ b/users/aspen/emacs.d/snippets/haskell-mode/module
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/shut up, hlint b/users/aspen/emacs.d/snippets/haskell-mode/shut up, hlint
index fccff1d66f..fccff1d66f 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/shut up, hlint
+++ b/users/aspen/emacs.d/snippets/haskell-mode/shut up, hlint
diff --git a/users/aspen/emacs.d/snippets/haskell-mode/test-group b/users/aspen/emacs.d/snippets/haskell-mode/test-group
new file mode 100644
index 0000000000..bf6a66f8a3
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/haskell-mode/test-group
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: test-group
+# uuid: test-group
+# key: testGroup
+# condition: t
+# --
+testGroup "${1:name}"
+[ $0
+]
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/test-module b/users/aspen/emacs.d/snippets/haskell-mode/test-module
index 036b0ae998..036b0ae998 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/test-module
+++ b/users/aspen/emacs.d/snippets/haskell-mode/test-module
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/undefined b/users/aspen/emacs.d/snippets/haskell-mode/undefined
index 7bcd99b571..7bcd99b571 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/undefined
+++ b/users/aspen/emacs.d/snippets/haskell-mode/undefined
diff --git a/users/grfn/emacs.d/snippets/js2-mode/action-type b/users/aspen/emacs.d/snippets/js2-mode/action-type
index ef8d1a3863..ef8d1a3863 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/action-type
+++ b/users/aspen/emacs.d/snippets/js2-mode/action-type
diff --git a/users/grfn/emacs.d/snippets/js2-mode/before b/users/aspen/emacs.d/snippets/js2-mode/before
index 4569b65831..4569b65831 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/before
+++ b/users/aspen/emacs.d/snippets/js2-mode/before
diff --git a/users/grfn/emacs.d/snippets/js2-mode/context b/users/aspen/emacs.d/snippets/js2-mode/context
index d83809f3c3..d83809f3c3 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/context
+++ b/users/aspen/emacs.d/snippets/js2-mode/context
diff --git a/users/grfn/emacs.d/snippets/js2-mode/describe b/users/aspen/emacs.d/snippets/js2-mode/describe
index bd0198181d..bd0198181d 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/describe
+++ b/users/aspen/emacs.d/snippets/js2-mode/describe
diff --git a/users/grfn/emacs.d/snippets/js2-mode/expect b/users/aspen/emacs.d/snippets/js2-mode/expect
index eba41ef330..eba41ef330 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/expect
+++ b/users/aspen/emacs.d/snippets/js2-mode/expect
diff --git a/users/grfn/emacs.d/snippets/js2-mode/function b/users/aspen/emacs.d/snippets/js2-mode/function
index b423044b44..b423044b44 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/function
+++ b/users/aspen/emacs.d/snippets/js2-mode/function
diff --git a/users/grfn/emacs.d/snippets/js2-mode/header b/users/aspen/emacs.d/snippets/js2-mode/header
index 3e303764cb..3e303764cb 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/header
+++ b/users/aspen/emacs.d/snippets/js2-mode/header
diff --git a/users/grfn/emacs.d/snippets/js2-mode/it b/users/aspen/emacs.d/snippets/js2-mode/it
index a451cfc08a..a451cfc08a 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/it
+++ b/users/aspen/emacs.d/snippets/js2-mode/it
diff --git a/users/grfn/emacs.d/snippets/js2-mode/it-pending b/users/aspen/emacs.d/snippets/js2-mode/it-pending
index 00da312e10..00da312e10 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/it-pending
+++ b/users/aspen/emacs.d/snippets/js2-mode/it-pending
diff --git a/users/grfn/emacs.d/snippets/js2-mode/module b/users/aspen/emacs.d/snippets/js2-mode/module
index dc79819d89..dc79819d89 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/module
+++ b/users/aspen/emacs.d/snippets/js2-mode/module
diff --git a/users/grfn/emacs.d/snippets/js2-mode/record b/users/aspen/emacs.d/snippets/js2-mode/record
index 0bb0f02436..0bb0f02436 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/record
+++ b/users/aspen/emacs.d/snippets/js2-mode/record
diff --git a/users/grfn/emacs.d/snippets/js2-mode/test b/users/aspen/emacs.d/snippets/js2-mode/test
index 938d490a74..938d490a74 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/test
+++ b/users/aspen/emacs.d/snippets/js2-mode/test
diff --git a/users/aspen/emacs.d/snippets/nix-mode/fetchFromGitHub b/users/aspen/emacs.d/snippets/nix-mode/fetchFromGitHub
new file mode 100644
index 0000000000..d2447e4b5a
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/nix-mode/fetchFromGitHub
@@ -0,0 +1,12 @@
+# -*- mode: snippet -*-
+# name: fetchFromGitHub
+# uuid: fetchFromGitHub
+# key: fetchFromGitHub
+# condition: t
+# --
+fetchFromGitHub {
+                owner = "$1";
+                repo = "$2";
+                rev = "$3";
+                sha256 = "0000000000000000000000000000000000000000000000000000";
+}
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/nix-mode/pythonPackage b/users/aspen/emacs.d/snippets/nix-mode/pythonPackage
index 0a74c21e18..0a74c21e18 100644
--- a/users/grfn/emacs.d/snippets/nix-mode/pythonPackage
+++ b/users/aspen/emacs.d/snippets/nix-mode/pythonPackage
diff --git a/users/aspen/emacs.d/snippets/nix-mode/sha256 b/users/aspen/emacs.d/snippets/nix-mode/sha256
new file mode 100644
index 0000000000..bc640e5ab0
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/nix-mode/sha256
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: sha256
+# uuid: sha256
+# key: sha256
+# condition: t
+# --
+sha256 = "0000000000000000000000000000000000000000000000000000";
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/org-mode/SQL source block b/users/aspen/emacs.d/snippets/org-mode/SQL source block
index b5d43fd6bc..b5d43fd6bc 100644
--- a/users/grfn/emacs.d/snippets/org-mode/SQL source block
+++ b/users/aspen/emacs.d/snippets/org-mode/SQL source block
diff --git a/users/aspen/emacs.d/snippets/org-mode/combat b/users/aspen/emacs.d/snippets/org-mode/combat
new file mode 100644
index 0000000000..b4db0f433a
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/org-mode/combat
@@ -0,0 +1,13 @@
+# -*- mode: snippet -*-
+# name: combat
+# uuid: combat
+# key: combat
+# condition: t
+# --
+|             | initiative | max hp | current hp | status |      |
+|-------------+------------+--------+------------+--------+------|
+| Barty Barty |            |        |            |        | <--- |
+| Hectoroth   |            |        |            |        |      |
+| Xanadu      |            |        |            |        |      |
+| Aurora      |            |        |            |        |      |
+| EFB         |            |        |            |        |      |
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/org-mode/date b/users/aspen/emacs.d/snippets/org-mode/date
index 297529cdac..297529cdac 100644
--- a/users/grfn/emacs.d/snippets/org-mode/date
+++ b/users/aspen/emacs.d/snippets/org-mode/date
diff --git a/users/grfn/emacs.d/snippets/org-mode/date-time b/users/aspen/emacs.d/snippets/org-mode/date-time
index fde469276c..fde469276c 100644
--- a/users/grfn/emacs.d/snippets/org-mode/date-time
+++ b/users/aspen/emacs.d/snippets/org-mode/date-time
diff --git a/users/grfn/emacs.d/snippets/org-mode/description b/users/aspen/emacs.d/snippets/org-mode/description
index a43bc95cc3..a43bc95cc3 100644
--- a/users/grfn/emacs.d/snippets/org-mode/description
+++ b/users/aspen/emacs.d/snippets/org-mode/description
diff --git a/users/grfn/emacs.d/snippets/org-mode/nologdone b/users/aspen/emacs.d/snippets/org-mode/nologdone
index e5be85d6b3..e5be85d6b3 100644
--- a/users/grfn/emacs.d/snippets/org-mode/nologdone
+++ b/users/aspen/emacs.d/snippets/org-mode/nologdone
diff --git a/users/grfn/emacs.d/snippets/org-mode/python source block b/users/aspen/emacs.d/snippets/org-mode/python source block
index 247ae51b0b..247ae51b0b 100644
--- a/users/grfn/emacs.d/snippets/org-mode/python source block
+++ b/users/aspen/emacs.d/snippets/org-mode/python source block
diff --git a/users/grfn/emacs.d/snippets/org-mode/reveal b/users/aspen/emacs.d/snippets/org-mode/reveal
index 1bdbdfa5dc..1bdbdfa5dc 100644
--- a/users/grfn/emacs.d/snippets/org-mode/reveal
+++ b/users/aspen/emacs.d/snippets/org-mode/reveal
diff --git a/users/grfn/emacs.d/snippets/org-mode/transaction b/users/aspen/emacs.d/snippets/org-mode/transaction
index 37f2dd31ca..37f2dd31ca 100644
--- a/users/grfn/emacs.d/snippets/org-mode/transaction
+++ b/users/aspen/emacs.d/snippets/org-mode/transaction
diff --git a/users/aspen/emacs.d/snippets/prolog-mode/use-module b/users/aspen/emacs.d/snippets/prolog-mode/use-module
new file mode 100644
index 0000000000..75fd19b641
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/prolog-mode/use-module
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: use-module
+# uuid: use-module
+# key: use
+# condition: t
+# --
+:- use_module(${1:library($2)}${3:, [$4]}).
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/python-mode/add_column b/users/aspen/emacs.d/snippets/python-mode/add_column
index 47e83850d5..47e83850d5 100644
--- a/users/grfn/emacs.d/snippets/python-mode/add_column
+++ b/users/aspen/emacs.d/snippets/python-mode/add_column
diff --git a/users/aspen/emacs.d/snippets/python-mode/decorate b/users/aspen/emacs.d/snippets/python-mode/decorate
new file mode 100644
index 0000000000..4f96748572
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/python-mode/decorate
@@ -0,0 +1,15 @@
+# -*- mode: snippet -*-
+# name: decorate
+# uuid: decorate
+# key: decorate
+# condition: t
+# --
+def wrap(inner):
+    @wraps(inner)
+    def wrapped(*args, **kwargs):
+        ret = inner(*args, **kwargs)
+        return ret
+
+    return wrapped
+
+return wrap
\ No newline at end of file
diff --git a/users/aspen/emacs.d/snippets/python-mode/dunder b/users/aspen/emacs.d/snippets/python-mode/dunder
new file mode 100644
index 0000000000..71d99dddc6
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/python-mode/dunder
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: dunder
+# uuid: dunder
+# key: du
+# condition: t
+# --
+__$1__$0
\ No newline at end of file
diff --git a/users/aspen/emacs.d/snippets/python-mode/name b/users/aspen/emacs.d/snippets/python-mode/name
new file mode 100644
index 0000000000..1495cc91d9
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/python-mode/name
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: name
+# uuid: name
+# key: name
+# condition: t
+# --
+__name__
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/python-mode/op.get_bind.execute b/users/aspen/emacs.d/snippets/python-mode/op.get_bind.execute
index aba801c6ba..aba801c6ba 100644
--- a/users/grfn/emacs.d/snippets/python-mode/op.get_bind.execute
+++ b/users/aspen/emacs.d/snippets/python-mode/op.get_bind.execute
diff --git a/users/aspen/emacs.d/snippets/python-mode/pdb b/users/aspen/emacs.d/snippets/python-mode/pdb
new file mode 100644
index 0000000000..41c6f87cbf
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/python-mode/pdb
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: pdb
+# uuid: pdb
+# key: pdb
+# condition: t
+# --
+import pdb; pdb.set_trace()
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/rust-mode/#[macro_use] b/users/aspen/emacs.d/snippets/rust-mode/#[macro_use]
index fea942a337..fea942a337 100644
--- a/users/grfn/emacs.d/snippets/rust-mode/#[macro_use]
+++ b/users/aspen/emacs.d/snippets/rust-mode/#[macro_use]
diff --git a/users/aspen/emacs.d/snippets/rust-mode/async test b/users/aspen/emacs.d/snippets/rust-mode/async test
new file mode 100644
index 0000000000..2352d7b56b
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/rust-mode/async test
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: async test
+# uuid: atest
+# key: atest
+# condition: t
+# --
+#[tokio::test${1:(flavor = "multi_thread")}]
+async fn ${2:test_name}() {
+   `%`$0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs.d/snippets/rust-mode/benchmark b/users/aspen/emacs.d/snippets/rust-mode/benchmark
new file mode 100644
index 0000000000..9ec4307538
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/rust-mode/benchmark
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: benchmark
+# uuid: benchmark
+# key: bench
+# condition: t
+# --
+#[bench]
+fn ${1:benchmark_name}(b: &mut Bencher) {
+   `%`b.iter(|| $0);
+}
\ No newline at end of file
diff --git a/users/aspen/emacs.d/snippets/rust-mode/proptest b/users/aspen/emacs.d/snippets/rust-mode/proptest
new file mode 100644
index 0000000000..be12af4911
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/rust-mode/proptest
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: proptest
+# uuid: proptest
+# key: proptest
+# condition: t
+# --
+#[proptest]
+fn ${1:test_name}($2) {
+   `%`$0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs.d/snippets/rust-mode/test-module b/users/aspen/emacs.d/snippets/rust-mode/test-module
new file mode 100644
index 0000000000..bfa2ca2d18
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/rust-mode/test-module
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: test-module
+# uuid: test-module
+# key: tmod
+# condition: t
+# --
+mod $1 {
+    use super::*;
+
+    $0
+}
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/rust-mode/tests b/users/aspen/emacs.d/snippets/rust-mode/tests
index 0a476ab586..0a476ab586 100644
--- a/users/grfn/emacs.d/snippets/rust-mode/tests
+++ b/users/aspen/emacs.d/snippets/rust-mode/tests
diff --git a/users/grfn/emacs.d/snippets/snippet-mode/indent b/users/aspen/emacs.d/snippets/snippet-mode/indent
index d38ffceafb..d38ffceafb 100644
--- a/users/grfn/emacs.d/snippets/snippet-mode/indent
+++ b/users/aspen/emacs.d/snippets/snippet-mode/indent
diff --git a/users/grfn/emacs.d/snippets/sql-mode/count(*) group by b/users/aspen/emacs.d/snippets/sql-mode/count(*) group by
index 6acc46ff39..6acc46ff39 100644
--- a/users/grfn/emacs.d/snippets/sql-mode/count(*) group by
+++ b/users/aspen/emacs.d/snippets/sql-mode/count(*) group by
diff --git a/users/aspen/emacs.d/snippets/terraform-mode/variable b/users/aspen/emacs.d/snippets/terraform-mode/variable
new file mode 100644
index 0000000000..14822f1a05
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/terraform-mode/variable
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: variable
+# uuid: variable
+# key: var
+# condition: t
+# --
+variable "${1:name}" {
+  type = ${2:string}
+  ${3:default = ${4:default}}
+}
+$0
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/text-mode/date b/users/aspen/emacs.d/snippets/text-mode/date
index 7b94311470..7b94311470 100644
--- a/users/grfn/emacs.d/snippets/text-mode/date
+++ b/users/aspen/emacs.d/snippets/text-mode/date
diff --git a/users/grfn/emacs.d/splitjoin.el b/users/aspen/emacs.d/splitjoin.el
index dbc9704d79..dbc9704d79 100644
--- a/users/grfn/emacs.d/splitjoin.el
+++ b/users/aspen/emacs.d/splitjoin.el
diff --git a/users/grfn/emacs.d/sql-strings.el b/users/aspen/emacs.d/sql-strings.el
index eef397a24e..eef397a24e 100644
--- a/users/grfn/emacs.d/sql-strings.el
+++ b/users/aspen/emacs.d/sql-strings.el
diff --git a/users/grfn/emacs.d/terraform.el b/users/aspen/emacs.d/terraform.el
index 2d69c9bad9..2d69c9bad9 100644
--- a/users/grfn/emacs.d/terraform.el
+++ b/users/aspen/emacs.d/terraform.el
diff --git a/users/grfn/emacs.d/tests/splitjoin_test.el b/users/aspen/emacs.d/tests/splitjoin_test.el
index 6495a1a595..6495a1a595 100644
--- a/users/grfn/emacs.d/tests/splitjoin_test.el
+++ b/users/aspen/emacs.d/tests/splitjoin_test.el
diff --git a/users/grfn/emacs.d/themes/grfn-solarized-light-theme.el b/users/aspen/emacs.d/themes/grfn-solarized-light-theme.el
index ae00b6b5fc..ae00b6b5fc 100644
--- a/users/grfn/emacs.d/themes/grfn-solarized-light-theme.el
+++ b/users/aspen/emacs.d/themes/grfn-solarized-light-theme.el
diff --git a/users/grfn/emacs.d/utils.el b/users/aspen/emacs.d/utils.el
index 21192753a2..21192753a2 100644
--- a/users/grfn/emacs.d/utils.el
+++ b/users/aspen/emacs.d/utils.el
diff --git a/users/grfn/emacs.d/vterm.el b/users/aspen/emacs.d/vterm.el
index a7fdea46da..a7fdea46da 100644
--- a/users/grfn/emacs.d/vterm.el
+++ b/users/aspen/emacs.d/vterm.el
diff --git a/users/aspen/emacs/.gitignore b/users/aspen/emacs/.gitignore
new file mode 100644
index 0000000000..f5236c1235
--- /dev/null
+++ b/users/aspen/emacs/.gitignore
@@ -0,0 +1,2 @@
+custom.el
+config.el
diff --git a/users/aspen/emacs/config.org b/users/aspen/emacs/config.org
new file mode 100644
index 0000000000..b3762affe4
--- /dev/null
+++ b/users/aspen/emacs/config.org
@@ -0,0 +1,1393 @@
+# Local variables:
+# lexical-binding: t
+# eval: (paxedit-mode 1)
+# eval: (display-line-numbers-mode 1)
+# eval: (flyspell-mode -1)
+# eval: (org-config-mode 1)
+# End:
+
+#+title: Emacs Config
+#+PROPERTY: header-args:emacs-lisp :results silent
+#+PROPERTY: header-args:elisp :results silent
+
+#+begin_src emacs-lisp :tangle yes
+;; -*- lexical-binding: t; -*-
+#+end_src
+
+* Utils
+#+begin_src elisp :tangle yes
+(use-package! dash)
+#+end_src
+
+** Elisp extras
+
+#+begin_src elisp :tangle yes
+(defmacro comment (&rest _body)
+  "Comment out one or more s-expressions"
+  nil)
+
+(defun inc (x) "Returns x + 1" (+ 1 x))
+(defun dec (x) "Returns x - 1" (- x 1))
+
+(defun average (ns)
+  "Arithmetic mean of xs"
+  (if (null ns) nil
+    (/ (apply #'+ ns)
+       (length ns))))
+
+(defun alist-set (alist-symbol key value)
+  "Set VALUE of a KEY in ALIST-SYMBOL."
+  (set alist-symbol (cons (list key value) (assq-delete-all key (eval alist-symbol)))))
+
+(defun rx-words (&rest words)
+  (rx-to-string
+   `(and symbol-start (group (or ,@words)) symbol-end)))
+#+end_src
+
+#+begin_src elisp :tangle no :results example
+(average (list 1 2 3 4))
+#+end_src
+
+** Text editing utils
+*** Reading strings
+#+begin_src elisp :tangle yes
+(defun get-char (&optional point)
+  "Get the character at the given `point' (defaulting to the current point),
+without properties"
+  (let ((point (or point (point))))
+    (buffer-substring-no-properties point (+ 1 point))))
+
+(defun get-line (&optional lineno)
+  "Read the line number `lineno', or the current line if `lineno' is nil, and
+return it as a string stripped of all text properties"
+  (let ((current-line (line-number-at-pos)))
+    (if (or (not lineno)
+            (= current-line lineno))
+        (thing-at-point 'line t)
+      (save-mark-and-excursion
+       (line-move (- lineno (line-number-at-pos)))
+       (thing-at-point 'line t)))))
+
+(defun get-line-point ()
+  "Get the position in the current line of the point"
+  (- (point) (line-beginning-position)))
+
+;; Moving in the file
+
+(defun goto-line-char (pt)
+  "Moves the point to the given position expressed as an offset from the start
+of the line"
+  (goto-char (+ (line-beginning-position) pt)))
+
+(defun goto-eol ()
+  "Moves to the end of the current line"
+  (goto-char (line-end-position)))
+
+(defun goto-regex-on-line (regex)
+  "Moves the point to the first occurrence of `regex' on the current line.
+Returns nil if the regex did not match, non-nil otherwise"
+  (when-let ((current-line (get-line))
+             (line-char (string-match regex current-line)))
+    (goto-line-char line-char)))
+
+(defun goto-regex-on-line-r (regex)
+  "Moves the point to the *last* occurrence of `regex' on the current line.
+Returns nil if the regex did not match, non-nil otherwise"
+  (when-let ((current-line (get-line))
+             (modified-regex (concat ".*\\(" regex "\\)"))
+             (_ (string-match modified-regex current-line))
+             (match-start (match-beginning 1)))
+    (goto-line-char match-start)))
+#+end_src
+
+#+begin_src elisp :tangle no
+(progn
+  (string-match (rx (and (zero-or-more anything)
+                         (group "foo" "foo")))
+                "foofoofoo")
+  (match-beginning 1))
+#+end_src
+
+*** Changing file contents
+#+begin_src elisp :tangle yes
+(defmacro saving-excursion (&rest body)
+  `(Ξ»! () (save-excursion ,@body)))
+
+(defun delete-line ()
+  "Remove the line at the current point"
+  (delete-region (line-beginning-position)
+                 (inc (line-end-position))))
+
+(defmacro modify-then-indent (&rest body)
+  "Modify text in the buffer according to body, then re-indent from where the
+  cursor started to where the cursor ended up, then return the cursor to where
+  it started."
+  `(let ((beg (line-beginning-position))
+         (orig-line-char (- (point) (line-beginning-position))))
+     (atomic-change-group
+       (save-mark-and-excursion
+        ,@body
+        (evil-indent beg (+ (line-end-position) 1))))
+     (goto-line-char orig-line-char)))
+
+(pcase-defmacro s-starts-with (prefix)
+  `(pred (s-starts-with-p ,prefix)))
+
+(pcase-defmacro s-contains (needle &optional ignore-case)
+  `(pred (s-contains-p ,needle
+                       ,@(when ignore-case (list ignore-case)))))
+#+end_src
+
+#+begin_src elisp :tangle no
+(pcase "foo"
+  ((s-contains "bar") 1)
+  ((s-contains "o") 2))
+#+end_src
+
+** Evil utils
+#+begin_src elisp :tangle yes
+(defmacro define-move-and-insert
+    (name &rest body)
+  `(defun ,name (count &optional vcount skip-empty-lines)
+     ;; Following interactive form taken from the source for `evil-insert'
+     (interactive
+      (list (prefix-numeric-value current-prefix-arg)
+            (and (evil-visual-state-p)
+                 (memq (evil-visual-type) '(line block))
+                 (save-excursion
+                   (let ((m (mark)))
+                     ;; go to upper-left corner temporarily so
+                     ;; `count-lines' yields accurate results
+                     (evil-visual-rotate 'upper-left)
+                     (prog1 (count-lines evil-visual-beginning evil-visual-end)
+                       (set-mark m)))))
+            (evil-visual-state-p)))
+     (atomic-change-group
+       ,@body
+       (evil-insert count vcount skip-empty-lines))))
+#+end_src
+
+* Name and email
+#+begin_src emacs-lisp
+(setq user-full-name "Aspen Smith"
+      user-mail-address "root@gws.fyi")
+#+end_src
+
+* Visual style
+#+begin_src elisp :tangle yes
+(let ((font-family (pcase system-type
+                     ('darwin "MesloLGSDZ NF")
+                     ('gnu/linux "Meslo LGSDZ Nerd Font"))))
+  (setq doom-font (font-spec :family font-family :height 113)
+        doom-big-font (font-spec :family font-family :size 24)
+        doom-big-font-increment 5
+        doom-variable-pitch-font (font-spec :family font-family)
+        doom-theme 'doom-solarized-light))
+
+(setq display-line-numbers-type t)
+
+(setq doom-modeline-buffer-file-name-style 'relative-to-project
+      doom-modeline-modal-icon nil
+      doom-modeline-github t
+      doom-modeline-height 12)
+#+end_src
+
+#+begin_src elisp :tangle yes
+(setq whitespace-style '(face lines-tail))
+(global-whitespace-mode t)
+(add-hook 'org-mode-hook (lambda () (whitespace-mode -1)) t)
+#+end_src
+
+** Theme
+[[https://davidjohnstone.net/lch-lab-colour-gradient-picker][LAB colour gradient picker]] is a good tool for trying to find "halfway points" between two colours
+
+*** Variables
+#+begin_src elisp :tangle no
+(rainbow-mode)
+#+end_src
+
+#+name: solarized-vars
+#+begin_src elisp :tangle yes
+(setq +solarized-s-base03    "#002b36"
+      +solarized-s-base02    "#073642"
+      ;; emphasized content
+      +solarized-s-base01    "#586e75"
+      ;; primary content
+      +solarized-s-base00    "#657b83"
+      +solarized-s-base0     "#839496"
+      ;; comments
+      +solarized-s-base1     "#93a1a1"
+      ;; background highlight light
+      +solarized-s-base2     "#eee8d5"
+      ;; background light
+      +solarized-s-base3     "#fdf6e3"
+
+      +solarized-halfway-highlight "#f5efdc"
+
+      ;; Solarized accented colors
+      +solarized-yellow    "#b58900"
+      +solarized-orange    "#cb4b16"
+      +solarized-red       "#dc322f"
+      +solarized-magenta   "#d33682"
+      +solarized-violet    "#6c71c4"
+      +solarized-blue      "#268bd2"
+      +solarized-cyan      "#2aa198"
+      +solarized-green     "#859900"
+
+      ;; Darker and lighter accented colors
+      ;; Only use these in exceptional circumstances!
+      +solarized-yellow-d  "#7B6000"
+      +solarized-yellow-l  "#DEB542"
+      +solarized-orange-d  "#8B2C02"
+      +solarized-orange-l  "#F2804F"
+      +solarized-red-d     "#990A1B"
+      +solarized-red-l     "#FF6E64"
+      +solarized-magenta-d "#93115C"
+      +solarized-magenta-l "#F771AC"
+      +solarized-violet-d  "#3F4D91"
+      +solarized-violet-l  "#9EA0E5"
+      +solarized-blue-d    "#00629D"
+      +solarized-blue-l    "#69B7F0"
+      +solarized-cyan-d    "#00736F"
+      +solarized-cyan-l    "#69CABF"
+      +solarized-green-d   "#546E00"
+      +solarized-green-l   "#B4C342")
+#+end_src
+
+*** Overrides
+
+#+name: overrides-for-solarized-light
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(cursor :background ,+solarized-s-base00)
+  `(font-lock-doc-face :foreground ,+solarized-s-base1)
+  `(font-lock-preprocessor-face :foreground ,+solarized-red :bold nil)
+  `(font-lock-keyword-face :foreground ,+solarized-green :bold nil)
+  `(font-lock-builtin-face :foreground ,+solarized-s-base01 :bold t)
+  `(font-lock-function-name-face :foreground ,+solarized-blue)
+  `(font-lock-constant-face :foreground ,+solarized-blue)
+  `(font-lock-type-face :italic nil)
+  `(highlight-numbers-number :bold nil)
+  `(highlight :background ,+solarized-s-base2)
+  `(solaire-hl-line-face :background ,+solarized-halfway-highlight)
+  `(hl-line :background ,+solarized-s-base2)
+
+  `(linum :background ,+solarized-s-base2 :foreground ,+solarized-s-base1)
+  `(line-number :background ,+solarized-s-base2 :foreground ,+solarized-s-base1)
+  `(line-number-current-line :background ,+solarized-s-base2 :foreground ,+solarized-s-base1)
+  `(fringe :background ,+solarized-s-base2)
+
+  `(whitespace-line :foreground ,+solarized-red :underline t)
+
+  `(haskell-operator-face :foreground ,+solarized-green)
+  `(haskell-keyword-face :foreground ,+solarized-cyan)
+
+  `(magit-branch-local :foreground ,+solarized-blue :bold t)
+  `(magit-branch-remote :foreground ,+solarized-green :bold t)
+  `(magit-branch-remote-head :foreground ,+solarized-green :bold t :box t)
+  `(magit-branch-current :box t :bold t)
+  `(magit-header-line :background nil :foreground ,+solarized-yellow :bold t :box nil)
+  `(diff-refine-added :foreground "#dbdb9c" :background "#5b6e35" :bold nil)
+  `(magit-diff-added-highlight :foreground "#657827" :background "#efeac7" :bold nil)
+  `(diff-refine-removed :background "#8e433d" :foreground "#ffb9a1" :bold nil)
+  `(magit-diff-removed-highlight :foreground "#a33c35" :background "#ffdec8" :bold nil)
+  `(magit-diff-hunk-heading :background "#f8e8c6" :foreground "#876d26" :bold nil)
+  `(magit-diff-hunk-heading-highlight :background "#f1d49b" :foreground "#766634" :bold nil)
+  `(magit-section-heading :foreground "#b58900")
+  `(magit-filename :foreground ,+solarized-s-base00)
+  `(magit-diff-context-highlight :background ,+solarized-halfway-highlight)
+
+  `(transient-delimiter :foreground ,+solarized-s-base1)
+  `(transient-inapt-suffix :foreground ,+solarized-s-base1)
+  `(transient-inactive-value :foreground ,+solarized-s-base1)
+  `(transient-inactive-argument :foreground ,+solarized-s-base1)
+  `(transient-key-exit :foreground ,+solarized-green :bold t)
+  `(transient-key-stay :foreground ,+solarized-blue :bold t)
+  )
+  #+end_src
+
+* Keybindings and navigation
+Get the hell out of here, snipe!
+#+begin_src elisp :tangle yes
+(remove-hook 'doom-first-input-hook #'evil-snipe-mode)
+#+end_src
+
+#+begin_src emacs-lisp :tangle yes
+(map!
+ (:leader
+  "b" #'consult-buffer
+  "r" #'consult-recent-file))
+#+end_src
+
+** Flycheck
+#+begin_src elisp :tangle yes
+(evil-set-command-property 'flycheck-next-error :repeat nil)
+(evil-set-command-property 'flycheck-prev-error :repeat nil)
+(evil-set-command-property 'flycheck-previous-error :repeat nil)
+
+(map!
+ (:map flycheck-mode-map
+  :m  "]e" #'flycheck-next-error
+  :m  "[e" #'flycheck-previous-error))
+#+end_src
+
+** Smerge
+#+begin_src elisp :tangle yes
+(evil-set-command-property 'smerge-next :repeat nil)
+(evil-set-command-property 'smerge-prev :repeat nil)
+
+(map!
+ :n "] n" #'smerge-next
+ :n "[ n" #'smerge-prev
+ (:leader
+  (:desc "smerge" :prefix "g m"
+   :desc "Keep Current" :n "SPC" #'smerge-keep-current
+   :desc "Keep All"     :n "a" #'smerge-keep-all
+   :desc "Keep Upper"   :n "u" #'smerge-keep-upper
+   :desc "Keep Lower"   :n "l" #'smerge-keep-lower)))
+t
+ #+end_src
+
+** Vinegar-style dired
+#+begin_src elisp :tangle yes
+(defun dired-mode-p () (eq 'dired-mode major-mode))
+
+(defun aspen/dired-minus ()
+  (interactive)
+  (if (dired-mode-p)
+      (dired-up-directory)
+    (when buffer-file-name
+      (-> (buffer-file-name)
+          (f-dirname)
+          (dired)))))
+
+(map!
+ :n "-" #'aspen/dired-minus
+ (:map dired-mode-map
+       "-" #'aspen/dired-minus))
+#+end_src
+
+** Lisp mappings
+*** Use paxedit
+#+begin_src elisp :tangle yes
+(use-package! paxedit
+  :hook ((emacs-lisp-mode . paxedit-mode)
+         (clojure-mode . paxedit-mode)
+         (common-lisp-mode . paxedit-mode)))
+#+end_src
+
+*** Paxedit functions
+
+#+begin_src elisp :tangle yes
+(define-move-and-insert aspen/insert-at-sexp-end
+  (when (not (equal (get-char) "("))
+    (backward-up-list))
+  (forward-sexp)
+  (backward-char))
+
+(define-move-and-insert aspen/insert-at-sexp-start
+  (backward-up-list)
+  (forward-char))
+
+(define-move-and-insert aspen/insert-at-form-start
+  (backward-sexp)
+  (backward-char)
+  (insert " "))
+
+(define-move-and-insert aspen/insert-at-form-end
+  (forward-sexp)
+  (insert " "))
+
+(defun aspen/paxedit-kill (&optional n)
+  (interactive "p")
+  (or (paxedit-comment-kill)
+      (when (paxedit-symbol-cursor-within?)
+        (paxedit-symbol-kill))
+      (paxedit-implicit-sexp-kill n)
+      (paxedit-sexp-kill n)
+      (message paxedit-message-kill)))
+#+end_src
+
+*** Paxedit mappings
+#+begin_src elisp :tangle yes
+(map!
+ (:after paxedit
+         (:map paxedit-mode-map
+          :i ";"                          #'paxedit-insert-semicolon
+          :i "("                          #'paxedit-open-round
+          :i "["                          #'paxedit-open-bracket
+          :i "{"                          #'paxedit-open-curly
+          :n [remap evil-yank-line]       #'paxedit-copy
+          :n [remap evil-delete-line]     #'aspen/paxedit-kill
+          :n "g o"                        #'paxedit-sexp-raise
+          :n [remap evil-join-whitespace] #'paxedit-compress
+          :n "g S"                        #'paxedit-format-1
+          :n "g k"                        #'paxedit-backward-up
+          :n "g j"                        #'paxedit-backward-end)))
+
+(require 'general)
+(general-evil-setup t)
+
+(nmap
+  ">" (general-key-dispatch 'evil-shift-right
+        "e" 'paxedit-transpose-forward
+        ")" 'sp-forward-slurp-sexp
+        "(" 'sp-backward-barf-sexp
+        "I" 'aspen/insert-at-sexp-end
+        ;; "a" 'grfn/insert-at-form-end
+        ))
+
+(nmap
+  "<" (general-key-dispatch 'evil-shift-left
+        "e" 'paxedit-transpose-backward
+        ")" 'sp-forward-barf-sexp
+        "(" 'sp-backward-slurp-sexp
+        "I" 'aspen/insert-at-sexp-start
+        ;; "a" 'grfn/insert-at-form-start
+        ))
+#+end_src
+
+*** Eval functions
+#+begin_src elisp :tangle yes
+(use-package! predd)
+
+(predd-defmulti eval-sexp (lambda (form) major-mode))
+
+(predd-defmethod eval-sexp 'clojure-mode (form)
+  (cider-interactive-eval form))
+
+(predd-defmethod eval-sexp 'emacs-lisp-mode (form)
+  (pp-eval-expression form))
+
+(predd-defmulti eval-sexp-region (lambda (_beg _end) major-mode))
+
+(predd-defmethod eval-sexp-region 'clojure-mode (beg end)
+  (cider-interactive-eval nil nil (list beg end)))
+
+(predd-defmethod eval-sexp-region 'emacs-lisp-mode (beg end)
+  (pp-eval-expression (read (buffer-substring beg end))))
+
+(predd-defmulti eval-sexp-region-context (lambda (_beg _end _context) major-mode))
+
+(predd-defmethod eval-sexp-region-context 'clojure-mode (beg end context)
+  (cider--eval-in-context (buffer-substring beg end)))
+
+(defun pp-eval-context-region (beg end context)
+  (interactive "r\nxContext: ")
+  (let* ((inner-expr (read (buffer-substring beg end)))
+         (full-expr (list 'let* context inner-expr)))
+    (pp-eval-expression full-expr)))
+
+(predd-defmethod eval-sexp-region-context 'emacs-lisp-mode (beg end context)
+  (pp-eval-context-region beg end context))
+
+(predd-defmulti preceding-sexp (lambda () major-mode))
+
+(predd-defmethod preceding-sexp 'clojure-mode ()
+  (cider-last-sexp))
+
+(predd-defmethod preceding-sexp 'emacs-lisp-mode ()
+  (elisp--preceding-sexp))
+
+(defun eval-sexp-at-point ()
+  (interactive)
+  (let ((bounds (bounds-of-thing-at-point 'sexp)))
+    (eval-sexp-region (car bounds)
+                      (cdr bounds))))
+
+(defun eval-last-sexp (_)
+  (interactive)
+  (eval-sexp (preceding-sexp)))
+
+;;;
+
+(defun cider-insert-current-sexp-in-repl (&optional arg)
+  "Insert the expression at point in the REPL buffer.
+If invoked with a prefix ARG eval the expression after inserting it"
+  (interactive "P")
+  (cider-insert-in-repl (cider-sexp-at-point) arg))
+
+(evil-define-operator fireplace-send (beg end)
+  (cider-insert-current-sexp-in-repl nil nil (list beg end)))
+
+(defun +clojure-pprint-expr (form)
+  (format "(with-out-str (clojure.pprint/pprint %s))"
+          form))
+
+(defun cider-eval-read-and-print-handler (&optional buffer)
+  "Make a handler for evaluating and reading then printing result in BUFFER."
+  (nrepl-make-response-handler
+   (or buffer (current-buffer))
+   (lambda (buffer value)
+     (let ((value* (read value)))
+       (with-current-buffer buffer
+         (insert
+          (if (derived-mode-p 'cider-clojure-interaction-mode)
+              (format "\n%s\n" value*)
+            value*)))))
+   (lambda (_buffer out) (cider-emit-interactive-eval-output out))
+   (lambda (_buffer err) (cider-emit-interactive-eval-err-output err))
+   '()))
+
+(defun cider-eval-and-replace (beg end)
+  "Evaluate the expression in region and replace it with its result"
+  (interactive "r")
+  (let ((form (buffer-substring beg end)))
+    (cider-nrepl-sync-request:eval form)
+    (kill-region beg end)
+    (cider-interactive-eval
+     (+clojure-pprint-expr form)
+     (cider-eval-read-and-print-handler))))
+
+(defun cider-eval-current-sexp-and-replace ()
+  "Evaluate the expression at point and replace it with its result"
+  (interactive)
+  (apply #'cider-eval-and-replace (cider-sexp-at-point 'bounds)))
+
+;;;
+#+end_src
+
+*** Eval bindings
+fireplace-esque eval binding
+
+#+begin_src elisp :tangle yes
+(evil-define-operator fireplace-eval (beg end)
+  (eval-sexp-region beg end))
+
+(evil-define-operator fireplace-replace (beg end)
+  (cider-eval-and-replace beg end))
+
+(evil-define-operator fireplace-eval-context (beg end)
+  (eval-sexp-region-context beg end))
+
+(nmap :keymaps 'cider-mode-map
+  "c" (general-key-dispatch 'evil-change
+        "p" (general-key-dispatch 'fireplace-eval
+              "p" 'cider-eval-sexp-at-point
+              "c" 'cider-eval-last-sexp
+              "d" 'cider-eval-defun-at-point
+              "r" 'cider-test-run-test)
+        "q" (general-key-dispatch 'fireplace-send
+              "q" 'cider-insert-current-sexp-in-repl
+              "c" 'cider-insert-last-sexp-in-repl)
+        "x" (general-key-dispatch 'fireplace-eval-context
+              "x" 'cider-eval-sexp-at-point-in-context
+              "c" 'cider-eval-last-sexp-in-context)
+        "!" (general-key-dispatch 'fireplace-replace
+              "!" 'cider-eval-current-sexp-and-replace
+              "c" 'cider-eval-last-sexp-and-replace)
+        "y" 'cider-copy-last-result))
+
+;;;
+
+(nmap :keymaps 'emacs-lisp-mode-map
+  "c" (general-key-dispatch 'evil-change
+        "p" (general-key-dispatch 'fireplace-eval
+              "p" 'eval-sexp-at-point
+              "c" 'eval-last-sexp
+              "d" 'eval-defun
+              "r" 'cider-test-run-test)
+        "x" (general-key-dispatch 'fireplace-eval-context
+              "x" 'cider-eval-sexp-at-point-in-context
+              "c" 'cider-eval-last-sexp-in-context)
+        "!" (general-key-dispatch 'fireplace-replace
+              "!" 'cider-eval-current-sexp-and-replace
+              "c" 'cider-eval-last-sexp-and-replace)
+        "y" 'cider-copy-last-result))
+
+(nmap :keymaps 'sly-mode-map
+  "c" (general-key-dispatch 'evil-change
+        "p" (general-key-dispatch 'sly-eval
+              ;; "p" 'eval-sexp-at-point
+              "c" 'sly-eval-last-expression
+              "d" 'sly-eval-defun
+              ;; "r" 'cider-test-run-test
+              )
+        ;; "x" (general-key-dispatch 'fireplace-eval-context
+        ;;       "x" 'cider-eval-sexp-at-point-in-context
+        ;;       "c" 'cider-eval-last-sexp-in-context
+        ;;       )
+        ;; "!" (general-key-dispatch 'fireplace-replace
+        ;;       "!" 'cider-eval-current-sexp-and-replace
+        ;;       "c" 'cider-eval-last-sexp-and-replace)
+        ;; "y" 'cider-copy-last-result
+        ))
+
+#+end_src
+
+** Coerce
+
+#+begin_src elisp :tangle yes
+(use-package! string-inflection
+  :config
+  (nmap "c" (general-key-dispatch 'evil-change
+              "r c" (saving-excursion (string-inflection-lower-camelcase))
+              "r C" (saving-excursion (string-inflection-camelcase))
+              "r m" (saving-excursion (string-inflection-camelcase))
+              "r s" (saving-excursion (string-inflection-underscore))
+              "r u" (saving-excursion (string-inflection-upcase))
+              "r -" (saving-excursion (string-inflection-kebab-case))
+              "r k" (saving-excursion (string-inflection-kebab-case))
+              ;; "r ." (saving-excursion (string-inflection-dot-case))
+              ;; "r ." (saving-excursion (string-inflection-space-case))
+              ;; "r ." (saving-excursion (string-inflection-title-case))
+              )))
+#+end_src
+
+* Mode-specific config
+** org-mode
+#+begin_src elisp :tangle yes
+(after! org
+  (load! "org-config")
+  (load! "org-query"))
+#+end_src
+
+*** Theme overrides
+
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(org-drawer :foreground ,+solarized-s-base1 :bold t)
+  `(org-block :foreground ,+solarized-s-base00)
+  `(org-meta-line :foreground ,+solarized-s-base1 :italic t)
+  `(org-document-title :foreground ,+solarized-s-base01 :height 1.3)
+  `(org-done :foreground ,+solarized-green)
+  `(org-headline-done :foreground ,+solarized-green)
+  `(org-special-keyword :foreground ,+solarized-s-base1 :bold t)
+  `(org-date :foreground ,+solarized-blue :underline t)
+  `(org-table
+    :foreground ,+solarized-s-base0  ; used to be green, I think I like this better?
+    :italic t)
+  `(org-link :foreground ,+solarized-yellow)
+  `(org-todo :foreground ,+solarized-cyan)
+  `(org-code :foreground ,+solarized-s-base1)
+  `(org-block-begin-line :foreground ,+solarized-s-base1 :italic t)
+  `(org-block-end-line :foreground ,+solarized-s-base1 :italic t)
+  `(org-document-info-keyword :foreground ,+solarized-s-base1 :italic t)
+
+  `(org-level-1 :foreground ,+solarized-red)
+  `(org-level-2 :foreground ,+solarized-green)
+  `(org-level-3 :foreground ,+solarized-blue)
+  `(org-level-4 :foreground ,+solarized-yellow)
+  `(org-level-5 :foreground ,+solarized-cyan)
+  `(org-level-6 :foreground ,+solarized-violet)
+  `(org-level-7 :foreground ,+solarized-magenta)
+  `(org-level-8 :foreground ,+solarized-blue))
+#+end_src
+
+*** Commands
+#+begin_src elisp :tangle yes
+(defun grfn/insert-new-src-block ()
+  (interactive)
+  (let* ((current-src-block (org-element-at-point))
+         (src-block-head (save-excursion
+                           (goto-char (org-element-property
+                                       :begin current-src-block))
+                           (let ((line (thing-at-point 'line t)))
+                             (if (not (s-starts-with? "#+NAME:" (s-trim line)))
+                                 line
+                               (forward-line)
+                               (thing-at-point 'line t)))))
+         (point-to-insert
+          (if-let (results-loc (org-babel-where-is-src-block-result))
+              (save-excursion
+                (goto-char results-loc)
+                (org-element-property
+                 :end
+                 (org-element-at-point)))
+            (org-element-property :end (org-element-at-point)))))
+    (goto-char point-to-insert)
+    (insert "\n")
+    (insert src-block-head)
+    (let ((contents (point-marker)))
+      (insert "\n#+END_SRC\n")
+      (goto-char contents))))
+
+(defun grfn/+org-insert-item (orig direction)
+  (interactive)
+  (if (and (org-in-src-block-p)
+           (equal direction 'below))
+      (grfn/insert-new-src-block)
+    (funcall orig direction)))
+
+(advice-add #'+org--insert-item :around #'grfn/+org-insert-item)
+#+end_src
+*** Bindings
+#+begin_src elisp :tangle yes
+(map!
+ (:after org
+  :n "C-c C-x C-o" #'org-clock-out
+  (:leader
+   "n k" #'org-archive-subtree-default)
+
+  (:map org-capture-mode-map
+   :n "g RET" #'org-capture-finalize
+   :n "g \\"  #'org-captue-refile)))
+#+end_src
+
+** magit
+#+begin_src elisp :tangle yes
+(after! magit
+  (map! :map magit-mode-map
+        ;; :n "] ]" #'magit-section-forward
+        ;; :n "[ [" #'magit-section-backward
+        )
+
+  (transient-define-suffix magit-commit-wip ()
+    (interactive)
+    (magit-commit-create '("-m" "wip")))
+
+  (transient-append-suffix
+    #'magit-commit
+    ["c"]
+    (list "W" "Commit WIP" #'magit-commit-wip))
+
+  (transient-define-suffix magit-reset-head-back ()
+    (interactive)
+    (magit-reset-mixed "HEAD~"))
+
+  (transient-define-suffix magit-reset-head-previous ()
+    (interactive)
+    (magit-reset-mixed "HEAD@{1}"))
+
+  (transient-append-suffix
+    #'magit-reset
+    ["f"]
+    (list "b" "Reset HEAD~"    #'magit-reset-head-back))
+  (transient-append-suffix
+    #'magit-reset
+    ["f"]
+    (list "o" "Reset HEAD@{1}" #'magit-reset-head-previous)))
+#+end_src
+
+** elisp
+*** Org config mode
+The minor-mode for *this file*!
+
+#+begin_src elisp :tangle yes
+(after! smartparens
+  (sp-local-pair 'org-config-mode "'" "'" :actions nil)
+  (sp-local-pair 'org-config-mode "`" "`" :actions nil))
+
+(define-minor-mode org-config-mode
+  "Minor-mode for tangled org .el config"
+  :group 'org
+  :lighter "Org-config"
+  :keymap '()
+  (sp-update-local-pairs 'org-config-mode))
+#+end_src
+
+*** Bindings
+#+begin_src elisp :tangle yes
+(map!
+ (:map emacs-lisp-mode-map
+  :n "g SPC" #'eval-buffer
+  :n "g RET" (Ξ»! () (ert t)) ))
+#+end_src
+
+** tuareg
+*** Config
+
+#+begin_src elisp :tangle yes
+
+(defun aspen/tuareg-setup ()
+  (setq-local sp-max-pair-length (->> '("begin" "sig" "struct")
+                                      (--map (length it))
+                                      (-max))
+              whitespace-line-column 80))
+
+(add-hook 'tuareg-mode-hook #'aspen/tuareg-setup)
+
+(defun sp-tuareg-post-handler (id action context)
+  (when (equal action 'insert)
+    (save-excursion
+      (insert "x")
+      (newline)
+      (indent-according-to-mode))
+    (delete-char 1)))
+
+(after! smartparens-ml
+  (sp-local-pair 'tuareg-mode "module" "end" :actions nil)
+
+  (dolist (pair-start '("begin" "sig" "struct"))
+    (sp-local-pair 'tuareg-mode
+                   pair-start "end"
+                   :when '(("SPC" "RET" "<evil-ret>"))
+                   :unless '(sp-in-string-p)
+                   :actions '(insert navigate)
+                   :post-handlers '(sp-tuareg-post-handler))))
+nil
+    #+end_src
+
+#+begin_src elisp :tangle yes
+(after! dune-mode
+  (add-hook 'dune-mode-hook 'paxedit-mode))
+#+end_src
+
+*** Bindings
+#+begin_src elisp :tangle yes
+(map!
+ (:map tuareg-mode-map
+  :n "g RET" (Ξ»! () (compile "dune build @@runtest"))
+  :n "g SPC" #'dune-promote
+  :n "g \\" #'utop
+  :n "g y" #'merlin-locate-type
+  "C-c C-f" (Ξ»! () (compile "dune fmt"))))
+#+end_src
+
+*** Theme overrides
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(tuareg-font-lock-governing-face :foreground ,+solarized-s-base01 :bold t)
+  `(tuareg-font-lock-label-face :foreground ,+solarized-blue)
+  `(tuareg-font-lock-constructor-face :foreground ,+solarized-yellow)
+  `(tuareg-font-lock-operator-face :foreground ,+solarized-red)
+  `(tuareg-font-lock-attribute-face :foreground ,+solarized-red :bold nil)
+  `(tuareg-font-lock-extension-node-face :background nil :inherit 'font-lock-preprocessor-face)
+  `(merlin-eldoc-occurrences-face :background ,+solarized-s-base2)
+  `(merlin-type-face :background ,+solarized-s-base2)
+  `(utop-prompt :foreground ,+solarized-blue)
+  `(utop-frozen :foreground ,+solarized-s-base1 :italic t)
+  `(vertico-group-title :foreground ,+solarized-s-base1)
+  `(vertico-group-header :foreground ,+solarized-s-base1))
+#+end_src
+
+** clojure
+
+*** Setup
+
+#+begin_src elisp :tangle yes
+(defun clojure-thing-at-point-setup ()
+  (interactive)
+  ;; Used by cider-find-dwim to parse the symbol at point
+  (setq-local
+   thing-at-point-file-name-chars
+   (concat thing-at-point-file-name-chars
+           "><!?")))
+
+(defun +grfn/clojure-setup ()
+  ;; (flycheck-select-checker 'clj-kondo)
+  (require 'flycheck)
+  (push 'clojure-cider-kibit flycheck-disabled-checkers)
+  (push 'clojure-cider-eastwood flycheck-disabled-checkers)
+  (push 'clojure-cider-typed flycheck-disabled-checkers)
+  )
+
+(after! clojure-mode
+  (define-clojure-indent
+    (PUT 2)
+    (POST 2)
+    (GET 2)
+    (PATCH 2)
+    (DELETE 2)
+    (context 2)
+    (checking 3)
+    (match 1)
+    (domonad 0)
+    (describe 1)
+    (before 1)
+    (it 2))
+
+  (add-hook 'clojure-mode-hook #'clojure-thing-at-point-setup)
+  (add-hook 'clojure-mode-hook #'+grfn/clojure-setup))
+
+(use-package! flycheck-clojure
+  ;; :disabled t
+  :after (flycheck cider)
+  :config
+  (flycheck-clojure-setup))
+
+(after! clj-refactor
+  (setq cljr-magic-requires :prompt
+        cljr-clojure-test-declaration "[clojure.test :refer :all]"
+        cljr-cljc-clojure-test-declaration"#?(:clj [clojure.test :refer :all]
+:cljs [cljs.test :refer-macros [deftest is testing]])"
+        )
+  (add-to-list
+   'cljr-magic-require-namespaces
+   '("s" . "clojure.spec.alpha")))
+
+(set-popup-rule! "^\\*cider-test-report" :size 0.4)
+nil
+#+end_src
+
+*** Commands
+
+#+begin_src elisp :tangle yes
+(defun grfn/run-clj-or-cljs-test ()
+  (interactive)
+  (message "Running tests...")
+  (cl-case (cider-repl-type-for-buffer)
+    (cljs
+     (cider-interactive-eval
+      "(with-out-str (cljs.test/run-tests))"
+      (nrepl-make-response-handler
+       (current-buffer)
+       (lambda (_ value)
+         (with-output-to-temp-buffer "*cljs-test-results*"
+           (print
+            (->> value
+                 (s-replace "\"" "")
+                 (s-replace "\\n" "\n")))))
+       nil nil nil)))
+    (('clj 'multi)
+     (funcall-interactively
+      #'cider-test-run-ns-tests
+      nil))))
+
+(defun cider-copy-last-result ()
+  (interactive)
+  (cider-interactive-eval
+   "*1"
+   (nrepl-make-response-handler
+    (current-buffer)
+    (lambda (_ value)
+      (kill-new value)
+      (message "Copied last result (%s) to clipboard"
+               (if (= (length value) 1) "1 char"
+                 (format "%d chars" (length value)))))
+    nil nil nil)))
+
+#+end_src
+
+*** Bindings
+
+
+#+begin_src elisp :tangle yes
+(map!
+ (:after
+  clojure-mode
+  (:map clojure-mode-map
+   :n "] f" 'forward-sexp
+   :n "[ f" 'backward-sexp))
+
+ (:after
+  cider-mode
+  (:map cider-mode-map
+   :n "g SPC" 'cider-eval-buffer
+   :n "g \\"  'cider-switch-to-repl-buffer
+   :n "K"     'cider-doc
+   :n "g K"   'cider-apropos
+   :n "g d"   'cider-find-dwim
+   :n "C-w ]" 'cider-find-dwim-other-window
+   ;; :n "g RET" 'cider-test-run-ns-tests
+   :n "g RET" 'grfn/run-clj-or-cljs-test
+   :n "g r" #'cljr-rename-symbol
+
+   "C-c C-r r" 'cljr-add-require-to-ns
+   "C-c C-r i" 'cljr-add-import-to-ns
+
+   (:localleader
+    ;; :desc "Inspect last result" :n "i" 'cider-inspect-last-result
+    ;; :desc "Search for documentation" :n "h s" 'cider-apropos-doc
+    :desc "Add require to ns" :n "n r" 'cljr-add-require-to-ns
+    :desc "Add import to ns" :n "n i" 'cljr-add-import-to-ns))
+  (:map cider-repl-mode-map
+   :n "g \\" 'cider-switch-to-last-clojure-buffer)))
+ #+end_src
+
+** rust
+#+begin_src elisp :tangle yes
+(defun aspen/rust-setup ()
+  (interactive)
+  (+evil-embrace-angle-bracket-modes-hook-h)
+  (setq-local whitespace-line-column 100
+              fill-column 100))
+
+(add-hook 'rust-mode-hook #'aspen/rust-setup)
+#+end_src
+
+*** Bindings
+
+#+begin_src elisp :tangle yes
+(map!
+ (:map rust-mode-map
+  :n "g RET" #'lsp-rust-analyzer-run
+  :n "g R" #'lsp-find-references
+  :n "g d" #'lsp-find-definition
+  :n "g Y" #'lsp-goto-type-definition
+  (:localleader
+   "m" #'lsp-rust-analyzer-expand-macro)))
+#+end_src
+
+*** Theme overrides
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(rust-unsafe :foreground ,+solarized-red))
+#+end_src
+
+** common-lisp
+*** Commands
+#+begin_src emacs-lisp :tangle yes
+(defun aspen/sly-panettone ()
+  (interactive)
+  (sly
+   (concat
+    (s-trim
+     (shell-command-to-string
+      "nix-build -o sbcl -E 'with import ~/code/depot {}; nix.buildLisp.sbclWith [web.panettone]'"))
+    "/bin/sbcl")))
+
+(defun aspen/setup-lisp ()
+  (interactive)
+  (rainbow-delimiters-mode)
+  (paxedit-mode 1)
+  (flycheck-mode -1))
+
+(add-hook 'common-lisp-mode-hook #'aspen/setup-lisp)
+
+(defun sly-run-tests ()
+  (interactive)
+  ;; TODO: handle other test frameworks
+  (let ((orig-window (get-buffer-window)))
+    (sly-eval '(fiveam:run!))
+    (funcall-interactively #'sly-mrepl-sync)
+    (select-window orig-window)))
+#+end_src
+
+*** Bindings
+
+#+begin_src emacs-lisp :tangle yes
+(map!
+ (:map sly-mode-map
+  :n "g \\" #'sly-mrepl-sync
+  :n "g d" #'sly-edit-definition
+  :n "K" #'sly-documentation
+  :n "g SPC" #'sly-compile-and-load-file
+  :n "g RET" #'sly-run-tests)
+
+ (:map sly-mrepl-mode-map
+  "C-k" #'sly-mrepl-previous-prompt
+  "C-r" #'isearch-backward))
+#+end_src
+
+* Completion
+** Corfu
+#+begin_src emacs-lisp :tangle yes
+(setopt +corfu-want-ret-to-confirm nil)
+
+(use-package! corfu
+  :demand t
+  :bind (:map corfu-map
+              ("TAB" . corfu-next)
+              ([tab] . corfu-next)
+              ("S-TAB" . corfu-previous)
+              ([backtab] . corfu-previous))
+  :init (setopt corfu-on-exact-match 'insert
+                corfu-preselect 'prompt
+                completion-cycle-threshold 1
+                corfu-quit-no-match t
+                corfu-quit-at-boundary t)
+  :config
+  (map! :map corfu-map
+        :i "TAB" #'corfu-next
+        :i [tab] #'corfu-next
+        :i "S-TAB" #'corfu-previous
+        :i [backtab] #'corfu-previous))
+#+end_src
+
+** Fuzzy search
+#+begin_src emacs-lisp :tangle yes
+(use-package! hotfuzz
+  :after (orderless corfu)
+  :config
+  (setopt completion-styles '(hotfuzz basic)
+          completion-ignore-case t))
+#+end_src
+
+* Email
+#+begin_src elisp :tangle yes
+(after! notmuch
+  (setq notmuch-saved-searches
+        '((:name "inbox" :query "tag:inbox tag:important not tag:trash" :key "i")
+          (:name "flagged" :query "tag:flagged" :key "f")
+          (:name "sent" :query "tag:sent" :key "s")
+          (:name "drafts" :query "tag:draft" :key "d")
+
+          (:name "work" :query "tag:inbox and tag:important and path:work/**"
+                 :key "w")
+          (:name "personal" :query "tag:inbox and tag:important and path:personal/**"
+                 :key "p"))
+        message-send-mail-function 'message-send-mail-with-sendmail
+        message-sendmail-f-is-evil 't
+        message-sendmail-envelope-from 'header
+        message-sendmail-extra-arguments '("--read-envelope-from")))
+
+(defun aspen/notmuch-sync ()
+  (interactive)
+  (let* ((search-buffer (current-buffer))
+         (proc (start-process-shell-command
+                "notmuch-sync"
+                "*notmuch-sync*"
+                "cd ~/mail/personal/ && gmi sync"))
+         (buf (process-buffer proc)))
+
+    (set-process-sentinel
+     proc
+     (lambda (proc msg)
+       (internal-default-process-sentinel proc msg)
+       (when (and (string= msg "finished\n"))
+         (kill-buffer buf)
+         (with-current-buffer search-buffer
+           (when (eq major-mode 'notmuch-search-mode)
+             (notmuch-refresh-this-buffer))))))
+
+    (with-current-buffer buf
+      (+popup-buffer-mode))
+    (display-buffer buf '(display-buffer-at-bottom . ()))))
+
+(set-popup-rule!
+  "^\\*notmuch-sync\\*$"
+  :select nil
+  :quit 'other)
+
+(map! :map notmuch-search-mode-map
+      :n "g SPC" #'aspen/notmuch-sync)
+#+end_src
+
+** Bindings
+#+begin_src emacs-lisp :tangle yes
+(map!
+ (:leader
+  :desc "Email" :n "o m" #'notmuch-jump-search
+  :desc "Search email" "s M" #'consult-notmuch))
+#+end_src
+
+** Theme
+
+#+begin_src emacs-lisp :tangle yes
+(custom-set-faces!
+  `(notmuch-message-summary-face
+    :background ,+solarized-halfway-highlight))
+#+end_src
+
+* Misc
+** TVL
+#+begin_src emacs-lisp :tangle yes
+(require 'tvl)
+#+end_src
+
+** Matchit
+#+begin_src elisp :tangle yes
+(use-package! evil-matchit)
+#+end_src
+** Direnv
+#+begin_src elisp :tangle yes
+(use-package! direnv
+  :config (direnv-mode))
+#+end_src
+** IRC
+*** Connecting to IRC
+
+#+begin_src elisp :tangle yes
+(defvar irc-servers
+  '("hackint"
+    "libera"))
+
+(defun irc-connect (server)
+  (interactive
+   (list (completing-read "Server: " irc-servers)))
+  (let ((pw (-> (shell-command-to-string
+                 (format "pass irccloud/%s" server))
+                (s-trim)
+                (s-lines)
+                (-last-item)))
+        (gnutls-verify-error nil))
+    (erc-tls :server "bnc.irccloud.com"
+             :port 6697
+             :nick "aspen"
+             :password (concat "bnc@"
+                               (s-trim (shell-command-to-string "hostname"))
+                               ":"
+                               pw))))
+
+(defun aspen/switch-to-erc-buffer-or-connect ()
+  (interactive)
+  (if (functionp 'erc-switch-to-buffer)
+      (call-interactively #'erc-switch-to-buffer)
+    (call-interactively #'irc-connect)))
+#+end_src
+
+#+begin_src elisp :tangle yes
+(map! :leader "o I" #'irc-connect
+      :leader "o i" #'aspen/switch-to-erc-buffer-or-connect)
+#+end_src
+
+*** IRC alerts
+#+begin_src elisp :tangle yes
+(use-package! alert)
+
+(defgroup erc-alert nil
+  "Alert me using alert.el for important ERC messages"
+  :group 'erc)
+
+(defcustom erc-noise-regexp
+  "\\(Logging in:\\|Signing off\\|You're now away\\|Welcome back\\)"
+  "This regexp matches unwanted noise."
+  :type 'regexp
+  :group 'erc)
+
+(setq tvl-enabled? t)
+
+(defun disable-tvl-notifications ()
+  (interactive)
+  (setq tvl-enabled? nil))
+
+(defun enable-tvl-notifications ()
+  (interactive)
+  (setq tvl-enabled? t))
+
+(defun erc-alert-important-p (info)
+  (let ((message (plist-get info :message))
+        (erc-message (-> info (plist-get :data) (plist-get :message)))
+        (erc-channel (-> info (plist-get :data) (plist-get :channel))))
+    (and erc-message
+         (not (or (string-match "^\\** *Users on #" message)
+                  (string-match erc-noise-regexp
+                                message)))
+         (or (and tvl-enabled?
+                  (string-equal erc-channel "#tvl"))
+             (string-match "grfn" message)))))
+
+(comment
+ last-info
+ erc-noise-regexp
+ (setq tvl-enabled? nil)
+ )
+
+(defun my-erc-hook (&optional match-type nick message)
+  "Shows a notification, when user's nick was mentioned.
+If the buffer is currently not visible, makes it sticky."
+  (setq last-message message)
+  (if (or (null match-type) (not (eq match-type 'fool)))
+      (let (alert-log-messages)
+        (alert (or message (buffer-string))
+               :severity (if (string-match "grfn" (or message ""))
+                             'high 'low)
+               :title (or nick (buffer-name))
+               :data `(:message ,(or message (buffer-string))
+                                :channel ,(or nick (buffer-name)))))))
+
+(add-hook 'erc-text-matched-hook 'my-erc-hook)
+(add-hook 'erc-insert-modify-hook 'my-erc-hook)
+
+(defun my-erc-define-alerts (&rest ignore)
+  ;; Unless the user has recently typed in the ERC buffer, highlight the fringe
+  (alert-add-rule
+   :status   '(buried visible idle)
+   :severity '(moderate high urgent)
+   :mode     'erc-mode
+   :predicate
+   #'(lambda (info)
+       (and (not (eq (current-buffer) (plist-get info :buffer)))
+            (string-match "grfn:" (plist-get info :message))))
+   :persistent
+   #'(lambda (info)
+       ;; If the buffer is buried, or the user has been idle for
+       ;; `alert-reveal-idle-time' seconds, make this alert
+       ;; persistent.  Normally, alerts become persistent after
+       ;; `alert-persist-idle-time' seconds.
+       (memq (plist-get info :status) '(buried idle)))
+   :style 'message
+   :continue t)
+
+  (alert-add-rule
+   :status 'buried
+   :mode   'erc-mode
+   :predicate #'erc-alert-important-p
+   :style 'libnotify
+   :append t)
+
+  (alert-add-rule
+   :status 'buried
+   :mode   'erc-mode
+   :predicate #'erc-alert-important-p
+   :style 'message
+   :append t)
+
+  (alert-add-rule
+   :mode 'erc-mode
+   :predicate #'erc-alert-important-p
+   :style 'log
+   :append t)
+
+  (alert-add-rule :mode 'erc-mode :style 'ignore :append t))
+
+(add-hook 'erc-connect-pre-hook 'my-erc-define-alerts)
+#+end_src
+
+*** Don't send ~:q~, etc, to the server
+#+begin_src elisp :tangle yes
+(defun fix-irc-message (msg)
+  (let ((msg (s-trim msg)))
+    (if (string-equal msg ":q") "" msg)))
+(advice-add #'erc-user-input :filter-return #'fix-irc-message)
+#+end_src
+
+*** Theme overrides
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(erc-button :foreground ,+solarized-blue))
+#+end_src
+
+*** TODO Nick rainbow colors
+Stole this from https://github.com/jtdaugherty/emacs-config/blob/master/common/erc-nick-colors.el.
+
+IT doesn't work though :(
+
+#+begin_src elisp :tangle yes
+(setq nick-face-list '())
+
+;; Define the list of colors to use when coloring IRC nicks.
+(setq-default erc-colors-list (list +solarized-yellow
+                                    +solarized-orange
+                                    +solarized-red
+                                    +solarized-magenta
+                                    +solarized-violet
+                                    +solarized-blue
+                                    +solarized-cyan
+                                    +solarized-green))
+
+(defun build-nick-face-list ()
+  "build-nick-face-list builds a list of new faces using the
+foreground colors specified in erc-colors-list.  The nick faces
+created here will be used to format IRC nicks."
+  (let ((i -1))
+    (setq nick-face-list
+          (mapcar
+           (lambda (COLOR)
+             (setq i (1+ i))
+             (list (custom-declare-face
+                    (make-symbol (format "erc-nick-face-%d" i))
+                    (list (list t (list :foreground COLOR)))
+                    (format "Nick face %d" i))))
+           erc-colors-list))))
+
+(defun erc-insert-nick-colors ()
+  "This insert-modify hook looks for nicks in new messages and
+computes md5(nick) and uses substring(md5_value, 0, 4) mod (length
+nick-face-list) to index the face list and produce the same face for a
+given nick each time it is seen.  We get a lot of collisions this way,
+unfortunately, but it's better than some other methods I tried.
+Additionally, if you change the order or size of the erc-colors-list,
+you'll change the colors used for nicks."
+  (if (null nick-face-list) (build-nick-face-list))
+  (save-excursion
+    (goto-char (point-min))
+    (if (looking-at "<\\([^>]*\\)>")
+        (let ((nick (match-string 1)))
+          (put-text-property (match-beginning 1) (match-end 1)
+                             'face (nth
+                                    (mod (string-to-number
+                                          (substring (md5 nick) 0 4) 16)
+                                         (length nick-face-list))
+                                    nick-face-list))))))
+
+;; This adds the ERC message insert hook.
+(add-hook 'erc-insert-modify-hook 'erc-insert-nick-colors)
+#+end_src
+
+* Hacks
+Not having this breaks elisp documentation :(
+#+begin_src elisp :tangle yes
+(defvar elisp-demos-user-files nil)
+#+end_src
diff --git a/users/aspen/emacs/init.el b/users/aspen/emacs/init.el
new file mode 100644
index 0000000000..7674d088b5
--- /dev/null
+++ b/users/aspen/emacs/init.el
@@ -0,0 +1,199 @@
+;;; init.el -*- lexical-binding: t; -*-
+
+;; This file controls what Doom modules are enabled and what order they load
+;; in. Remember to run 'doom sync' after modifying it!
+
+;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's
+;;      documentation. There you'll find a link to Doom's Module Index where all
+;;      of our modules are listed, including what flags they support.
+
+;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or
+;;      'C-c c k' for non-vim users) to view its documentation. This works on
+;;      flags as well (those symbols that start with a plus).
+;;
+;;      Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
+;;      directory (for easy access to its source code).
+
+(doom! :input
+       ;;bidi              ; (tfel ot) thgir etirw uoy gnipleh
+       ;;chinese
+       ;;japanese
+       ;;layout            ; auie,ctsrnm is the superior home row
+
+       :completion
+       ;; company             ; the ultimate code completion backend
+       corfu
+       ;;helm              ; the *other* search engine for love and life
+       ;;ido               ; the other *other* search engine...
+       ;;ivy               ; a search engine for love and life
+       vertico           ; the search engine of the future
+
+       :ui
+       ;;deft              ; notational velocity for Emacs
+       doom              ; what makes DOOM look the way it does
+       doom-dashboard    ; a nifty splash screen for Emacs
+       ;;doom-quit         ; DOOM quit-message prompts when you quit Emacs
+       (emoji +unicode)  ; πŸ™‚
+       hl-todo           ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
+       ;;hydra
+       ;;indent-guides     ; highlighted indent columns
+       ;;ligatures         ; ligatures and symbols to make your code pretty again
+       ;;minimap           ; show a map of the code on the side
+       modeline          ; snazzy, Atom-inspired modeline, plus API
+       ;;nav-flash         ; blink cursor line after big motions
+       ;;neotree           ; a project drawer, like NERDTree for vim
+       ophints           ; highlight the region an operation acts on
+       (popup +defaults)   ; tame sudden yet inevitable temporary windows
+       ;;tabs              ; a tab bar for Emacs
+       ;;treemacs          ; a project drawer, like neotree but cooler
+       ;;unicode           ; extended unicode support for various languages
+       (vc-gutter +pretty) ; vcs diff in the fringe
+       vi-tilde-fringe   ; fringe tildes to mark beyond EOB
+       ;;window-select     ; visually switch windows
+       workspaces        ; tab emulation, persistence & separate workspaces
+       ;;zen               ; distraction-free coding or writing
+
+       :editor
+       (evil +everywhere); come to the dark side, we have cookies
+       file-templates    ; auto-snippets for empty files
+       fold              ; (nigh) universal code folding
+       (format +onsave)  ; automated prettiness
+       ;;god               ; run Emacs commands without modifier keys
+       ;;lispy             ; vim for lisp, for people who don't like vim
+       ;;multiple-cursors  ; editing in many places at once
+       ;;objed             ; text object editing for the innocent
+       ;;parinfer          ; turn lisp into python, sort of
+       ;;rotate-text       ; cycle region at point between text candidates
+       snippets          ; my elves. They type so I don't have to
+       word-wrap         ; soft wrapping with language-aware indent
+
+       :emacs
+       dired             ; making dired pretty [functional]
+       electric          ; smarter, keyword-based electric-indent
+       ;;ibuffer         ; interactive buffer management
+       undo              ; persistent, smarter undo for your inevitable mistakes
+       vc                ; version-control and Emacs, sitting in a tree
+
+       :term
+       ;;eshell            ; the elisp shell that works everywhere
+       ;;shell             ; simple shell REPL for Emacs
+       ;;term              ; basic terminal emulator for Emacs
+       vterm             ; the best terminal emulation in Emacs
+
+       :checkers
+       syntax              ; tasing you for every semicolon you forget
+       (spell +flyspell) ; tasing you for misspelling mispelling
+       ;;grammar           ; tasing grammar mistake every you make
+
+       :tools
+       ;;ansible
+       ;;biblio            ; Writes a PhD for you (citation needed)
+       ;;debugger          ; FIXME stepping through code, to help you add bugs
+       direnv
+       docker
+       ;;editorconfig      ; let someone else argue about tabs vs spaces
+       ;;ein               ; tame Jupyter notebooks with emacs
+       (eval +overlay)     ; run code, run (also, repls)
+       ;;gist              ; interacting with github gists
+       lookup              ; navigate your code and its documentation
+       lsp               ; M-x vscode
+       magit             ; a git porcelain for Emacs
+       ;;make              ; run make tasks from Emacs
+       pass              ; password manager for nerds
+       ;;pdf               ; pdf enhancements
+       ;;prodigy           ; FIXME managing external services & code builders
+       ;;rgb               ; creating color strings
+       ;;taskrunner        ; taskrunner for all your projects
+       terraform         ; infrastructure as code
+       ;;tmux              ; an API for interacting with tmux
+       ;;tree-sitter       ; syntax and parsing, sitting in a tree...
+       ;;upload            ; map local to remote projects via ssh/ftp
+
+       :os
+       (:if IS-MAC macos)  ; improve compatibility with macOS
+       ;;tty               ; improve the terminal Emacs experience
+
+       :lang
+       agda              ; types of types of types of types...
+       ;;beancount         ; mind the GAAP
+       ;;(cc +lsp)         ; C > C++ == 1
+       clojure           ; java with a lisp
+       common-lisp       ; if you've seen one lisp, you've seen them all
+       ;;coq               ; proofs-as-programs
+       ;;crystal           ; ruby at the speed of c
+       ;;csharp            ; unity, .NET, and mono shenanigans
+       data              ; config/data formats
+       ;;(dart +flutter)   ; paint ui and not much else
+       ;;dhall
+       ;;elixir            ; erlang done right
+       ;;elm               ; care for a cup of TEA?
+       emacs-lisp        ; drown in parentheses
+       ;;erlang            ; an elegant language for a more civilized age
+       ;;ess               ; emacs speaks statistics
+       ;;factor
+       ;;faust             ; dsp, but you get to keep your soul
+       ;;fortran           ; in FORTRAN, GOD is REAL (unless declared INTEGER)
+       ;;fsharp            ; ML stands for Microsoft's Language
+       ;;fstar             ; (dependent) types and (monadic) effects and Z3
+       ;;gdscript          ; the language you waited for
+       ;;(go +lsp)         ; the hipster dialect
+       ;;(graphql +lsp)    ; Give queries a REST
+       (haskell +lsp)    ; a language that's lazier than I am
+       ;;hy                ; readability of scheme w/ speed of python
+       ;;idris             ; a language you can depend on
+       json              ; At least it ain't XML
+       ;;(java +lsp)       ; the poster child for carpal tunnel syndrome
+       (javascript +lsp)        ; all(hope(abandon(ye(who(enter(here))))))
+       ;;julia             ; a better, faster MATLAB
+       ;;kotlin            ; a better, slicker Java(Script)
+       ;;latex             ; writing papers in Emacs has never been so fun
+       ;;lean              ; for folks with too much to prove
+       ;;ledger            ; be audit you can be
+       ;;lua               ; one-based indices? one-based indices
+       markdown          ; writing docs for people to ignore
+       ;;nim               ; python + lisp at the speed of c
+       nix               ; I hereby declare "nix geht mehr!"
+       ocaml             ; an objective camel
+       (org               ; organize your plain life in plain text
+        +gnuplot
+        +present
+        +pretty
+        )
+       ;;php               ; perl's insecure younger brother
+       ;;plantuml          ; diagrams for confusing people more
+       ;;purescript        ; javascript, but functional
+       python            ; beautiful is better than ugly
+       ;;qt                ; the 'cutest' gui framework ever
+       ;;racket            ; a DSL for DSLs
+       ;;raku              ; the artist formerly known as perl6
+       ;;rest              ; Emacs as a REST client
+       ;;rst               ; ReST in peace
+       ;;(ruby +rails)     ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
+       (rust +lsp)       ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
+       ;;scala             ; java, but good
+       ;;(scheme +guile)   ; a fully conniving family of lisps
+       sh                ; she sells {ba,z,fi}sh shells on the C xor
+       ;;sml
+       ;;solidity          ; do you need a blockchain? No.
+       ;;swift             ; who asked for emoji variables?
+       ;;terra             ; Earth and Moon in alignment for performance.
+       web               ; the tubes
+       yaml              ; JSON, but readable
+       ;;zig               ; C, but simpler
+
+       :email
+       ;;(mu4e +org +gmail)
+       notmuch
+       ;;(wanderlust +gmail)
+
+       :app
+       ;;calendar
+       ;;emms
+       ;;everywhere        ; *leave* Emacs!? You must be joking
+       irc               ; how neckbeards socialize
+       ;;(rss +org)        ; emacs as an RSS reader
+       ;;twitter           ; twitter client https://twitter.com/vnought
+
+       :config
+       literate
+       (default +bindings +smartparens))
diff --git a/users/aspen/emacs/org-config.el b/users/aspen/emacs/org-config.el
new file mode 100644
index 0000000000..89cf7486fb
--- /dev/null
+++ b/users/aspen/emacs/org-config.el
@@ -0,0 +1,141 @@
+;;; org-config.el -*- lexical-binding: t; -*-
+
+(defun +aspen/org-setup ()
+  (setq-local truncate-lines -1)
+  (display-line-numbers-mode -1)
+  (line-number-mode -1)
+  (when-let*
+      ((path (buffer-file-name))
+       (fn (file-name-nondirectory path))
+       (equal (string-equal fn "config.org")))
+    (paxedit-mode 1)
+    (display-line-numbers-mode 1)
+    (flyspell-mode -1)
+    (org-config-mode 1)))
+
+(add-hook 'org-mode-hook #'+aspen/org-setup 50)
+
+(defun notes-file (f)
+  (concat org-directory (if (string-prefix-p "/" f) "" "/") f))
+
+(defun aspen/org-project-tag->key (tag)
+  (s-replace-regexp "^project__" "" tag))
+
+(defun aspen/org-project-tag->name (tag)
+  (s-titleized-words
+   (s-join " " (s-split "_" (aspen/org-project-tag->key tag)))))
+
+(defun aspen/org-project-tag->keys (tag)
+  (s-join "" (cons "p"
+                   (-map (lambda (s) (substring-no-properties s 0 1))
+                         (s-split "_" (aspen/org-project-tag->key tag))))))
+
+(defun aspen/org-projects->agenda-commands (project-tags)
+  (cl-loop for tag in project-tags
+           collect `(,(aspen/org-project-tag->keys tag)
+                     ,(aspen/org-project-tag->name tag)
+                     tags-todo
+                     ,tag)))
+
+(defun aspen/org-projects ()
+  (cl-loop for (tag) in
+           (org-global-tags-completion-table
+            (directory-files-recursively "~/notes" "\\.org$"))
+           when (s-starts-with-p "project__" tag)
+           collect tag))
+
+(comment
+ (aspen/org-projects->agenda-commands (aspen/org-projects))
+ )
+
+(setq
+ org-directory (expand-file-name "~/notes")
+ +org-dir (expand-file-name "~/notes")
+ org-default-notes-file (concat org-directory "/inbox.org")
+ +org-default-todo-file (concat org-directory "/inbox.org")
+ org-agenda-files (directory-files-recursively
+                   "~/notes" "\\.org$")
+ org-refile-targets '((org-agenda-files :maxlevel . 3))
+ org-outline-path-complete-in-steps nil
+ org-refile-use-outline-path t
+ org-file-apps `((auto-mode . emacs)
+                 (,(rx (or (and "." (optional "x") (optional "htm") (optional "l") buffer-end)
+                           (and buffer-start "http" (optional "s") "://")))
+                  . "firefox %s")
+                 (,(rx ".pdf" buffer-end) . "apvlv %s")
+                 (,(rx "." (or "png"
+                               "jpg"
+                               "jpeg"
+                               "gif"
+                               "tif"
+                               "tiff")
+                       buffer-end)
+                  . "feh %s"))
+ org-log-done 'time
+ org-archive-location "~/notes/trash::* From %s"
+ org-cycle-separator-lines 2
+ org-hidden-keywords '(title)
+ org-tags-column -130
+ org-ellipsis "…"
+ org-imenu-depth 9
+ org-capture-templates
+ `(("t" "Todo" entry
+    (file +org-default-todo-file)
+    "* TODO %?\n%i"
+    :kill-buffer t)
+
+   ("m" "Email" entry
+    (file +org-default-todo-file)
+    "* TODO [[%L][%:subject]] :email:\n%i")
+
+   ("n" "Notes" entry
+    (file +org-default-todo-file)
+    "* %U %?\n%i"
+    :prepend t
+    :kill-buffer t)
+
+   ("c" "Task note" entry
+    (clock)
+    "* %U %?\n%i[%l[Context]]\n"
+    :kill-buffer t
+    :unnarrowed t)
+
+   ("p" "Projects")
+   ("px" "Xanthous" entry
+    (file+headline ,(notes-file "xanthous.org") "Backlog")
+    "* TODO %?\nContext %a\nIn task: %K")
+   ("pt" "Tvix" entry
+    (file+headline ,(notes-file "tvix.org") "Tvix TODO")
+    "* TODO %?\nContext %a\nIn task: %K")
+   ("pw" "Windtunnel" entry
+    (file+headline ,(notes-file "windtunnel.org") "Inbox")
+    "* TODO %i%?\nContext: %a\nIn task: %K")
+   )
+
+ org-capture-templates-contexts
+ `(("px" ((in-file . "/home/aspen/code/depot/users/aspen/xanthous/.*")))
+   ("e" ((in-mode . "notmuch-show-mode"))))
+
+ org-deadline-warning-days 1
+ org-agenda-skip-scheduled-if-deadline-is-shown 'todo
+ org-todo-keywords '((sequence "TODO(t)" "ACTIVE(a)" "|" "DONE(d)" "RUNNING(r)")
+                     (sequence "NEXT(n)" "WAITING(w)" "LATER(l)" "|" "CANCELLED(c)"))
+ org-agenda-custom-commands
+ `(("i" "Inbox" tags "inbox")
+   ("r" "Running jobs" todo "RUNNING")
+   ("w" "@Work" tags-todo "@work")
+   ("n" . "Next...")
+   ("nw" "Next @Work" tags-todo "@work&next")
+   ("nt" "Next tooling" tags-todo "tooling")
+
+   ;; ("p" . "Project...")
+   ;; ,@(aspen/org-projects->agenda-commands (aspen/org-projects))
+   )
+
+ org-agenda-dim-blocked-tasks nil
+ org-enforce-todo-dependencies nil
+
+ org-babel-clojure-backend 'cider)
+
+(setq whitespace-global-modes '(not org-mode magit-mode vterm-mode))
+(setf (alist-get 'file org-link-frame-setup) 'find-file-other-window)
diff --git a/users/aspen/emacs/packages.el b/users/aspen/emacs/packages.el
new file mode 100644
index 0000000000..0bcc345d88
--- /dev/null
+++ b/users/aspen/emacs/packages.el
@@ -0,0 +1,14 @@
+;; -*- no-byte-compile: t; -*-
+;;; $DOOMDIR/packages.el
+
+(package! dash)
+(package! paxedit)
+(package! predd
+  :recipe (:host github :repo "skeeto/predd"))
+(package! direnv)
+(package! alert)
+(package! flycheck-clojure)
+(package! evil-matchit)
+(package! string-inflection)
+(package! protobuf-mode)
+(package! hotfuzz)
diff --git a/users/aspen/emacs/snippets/haskell-mode/annotation b/users/aspen/emacs/snippets/haskell-mode/annotation
new file mode 100644
index 0000000000..8a2854d759
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/annotation
@@ -0,0 +1,5 @@
+# key: ann
+# name: annotation
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# ANN ${1:module} ("${2:HLint: ignore ${3:Reduce duplication}}" :: String) #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/benchmark-module b/users/aspen/emacs/snippets/haskell-mode/benchmark-module
new file mode 100644
index 0000000000..cbb1646e41
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/benchmark-module
@@ -0,0 +1,26 @@
+# key: bench
+# name: benchmark-module
+# expand-env: ((yas-indent-line (quote fixed)))
+# --
+--------------------------------------------------------------------------------
+module ${1:`(if (not buffer-file-name) "Module"
+                (let ((name (file-name-sans-extension (buffer-file-name)))
+                      (case-fold-search nil))
+                     (if (cl-search "bench/" name)
+                         (replace-regexp-in-string "/" "."
+                           (replace-regexp-in-string "^\/[^A-Z]*" ""
+                             (car (last (split-string name "src")))))
+                         (file-name-nondirectory name))))`} ( benchmark, main ) where
+--------------------------------------------------------------------------------
+import Bench.Prelude
+--------------------------------------------------------------------------------
+import ${1:$(s-chop-suffix "Bench" yas-text)}
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain [benchmark]
+
+--------------------------------------------------------------------------------
+
+benchmark :: Benchmark
+benchmark = bgroup "${1:$(->> yas-text (s-chop-suffix "Bench") (s-split ".") -last-item)}" [bench "something dumb" $ nf (1 +) (1 :: Int)]
diff --git a/users/aspen/emacs/snippets/haskell-mode/header b/users/aspen/emacs/snippets/haskell-mode/header
new file mode 100644
index 0000000000..fdd8250d86
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/header
@@ -0,0 +1,5 @@
+# key: hh
+# name: header
+# expand-env: ((yas-indent-line 'fixed))
+# --
+--------------------------------------------------------------------------------$2
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/hedgehog-generator b/users/aspen/emacs/snippets/haskell-mode/hedgehog-generator
new file mode 100644
index 0000000000..68863f7054
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/hedgehog-generator
@@ -0,0 +1,8 @@
+# key: gen
+# name: Hedgehog Generator
+# expand-env: ((yas-indent-line (quote fixed)))
+# --
+gen${1:Foo} :: Gen $1
+gen$1 = do
+  $2
+  pure $1{..}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/hedgehog-property b/users/aspen/emacs/snippets/haskell-mode/hedgehog-property
new file mode 100644
index 0000000000..bf39a2a3ee
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/hedgehog-property
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: Hedgehog Property
+# key: hprop
+# expand-env: ((yas-indent-line 'fixed))
+# --
+hprop_${1:somethingIsAlwaysTrue} :: Property
+hprop_$1 = property $ do
+  ${2:x} <- forAll ${3:Gen.int $ Range.linear 1 100}
+  ${4:x === x}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/hlint b/users/aspen/emacs/snippets/haskell-mode/hlint
new file mode 100644
index 0000000000..f25a9b8d40
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/hlint
@@ -0,0 +1,8 @@
+# -*- mode: snippet -*-
+# name: hlint
+# uuid: hlint
+# expand-env: ((yas-indent-line 'fixed))
+# key: hlint
+# condition: t
+# --
+{-# ANN module ("Hlint: ignore $1" :: String) #- }
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/import-i b/users/aspen/emacs/snippets/haskell-mode/import-i
new file mode 100644
index 0000000000..4a7fca2c2f
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/import-i
@@ -0,0 +1,4 @@
+# key: i
+# name: import-i
+# --
+import           ${1:Prelude}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/inl b/users/aspen/emacs/snippets/haskell-mode/inl
new file mode 100644
index 0000000000..6e17b83d71
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/inl
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: inl
+# key: inl
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# INLINE $1 #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/inline b/users/aspen/emacs/snippets/haskell-mode/inline
new file mode 100644
index 0000000000..1beafbe50b
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/inline
@@ -0,0 +1,5 @@
+# key: inline
+# name: inline
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# INLINE $1 #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/language pragma b/users/aspen/emacs/snippets/haskell-mode/language pragma
new file mode 100644
index 0000000000..6f84720f45
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/language pragma
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: language pragma
+# key: lang
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# LANGUAGE $1 #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/lens.field b/users/aspen/emacs/snippets/haskell-mode/lens.field
new file mode 100644
index 0000000000..b22ea3d2e8
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/lens.field
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: lens.field
+# key: lens
+# expand-env: ((yas-indent-line 'fixed))
+# --
+${1:field} :: Lens' ${2:Source} ${3:Target}
+$1 = lens _${4:sourceField} $ \\${2:$(-> yas-text s-word-initials s-downcase)} ${4:$(-> yas-text s-word-initials s-downcase)} -> ${2:$(-> yas-text s-word-initials s-downcase)} { _$4 = ${4:$(-> yas-text s-word-initials s-downcase)} }
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/module b/users/aspen/emacs/snippets/haskell-mode/module
new file mode 100644
index 0000000000..4554d33f9b
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/module
@@ -0,0 +1,32 @@
+# -*- mode: snippet -*-
+# key: module
+# name: module
+# condition: (= (length "module") (current-column))
+# expand-env: ((yas-indent-line 'fixed))
+# contributor: Luke Hoersten <luke@hoersten.org>
+# --
+--------------------------------------------------------------------------------
+-- |
+-- Module      : $1
+-- Description : $2
+-- Maintainer  : Griffin Smith <grfn@urbint.com>
+-- Maturity    : ${3:Draft, Usable, Maintained, OR MatureAF}
+--
+-- $4
+--------------------------------------------------------------------------------
+module ${1:`(if (not buffer-file-name) "Module"
+                (let ((name (file-name-sans-extension (buffer-file-name)))
+                      (case-fold-search nil))
+                     (if (or (cl-search "src/" name)
+                             (cl-search "test/" name))
+                         (replace-regexp-in-string "/" "."
+                           (replace-regexp-in-string "^\/[^A-Z]*" ""
+                             (car (last (split-string name "src")))))
+                         (file-name-nondirectory name))))`}
+  (
+  ) where
+--------------------------------------------------------------------------------
+import Prelude
+--------------------------------------------------------------------------------
+
+$0
diff --git a/users/aspen/emacs/snippets/haskell-mode/shut up, hlint b/users/aspen/emacs/snippets/haskell-mode/shut up, hlint
new file mode 100644
index 0000000000..fccff1d66f
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/shut up, hlint
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: shut up, hlint
+# key: dupl
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/test-group b/users/aspen/emacs/snippets/haskell-mode/test-group
new file mode 100644
index 0000000000..bf6a66f8a3
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/test-group
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: test-group
+# uuid: test-group
+# key: testGroup
+# condition: t
+# --
+testGroup "${1:name}"
+[ $0
+]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/test-module b/users/aspen/emacs/snippets/haskell-mode/test-module
new file mode 100644
index 0000000000..036b0ae998
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/test-module
@@ -0,0 +1,27 @@
+# -*- mode: snippet -*-
+# name: test-module
+# key: test
+# expand-env: ((yas-indent-line 'fixed))
+# --
+--------------------------------------------------------------------------------
+module ${1:`(if (not buffer-file-name) "Module"
+                (let ((name (file-name-sans-extension (buffer-file-name)))
+                      (case-fold-search nil))
+                     (if (cl-search "test/" name)
+                         (replace-regexp-in-string "/" "."
+                           (replace-regexp-in-string "^\/[^A-Z]*" ""
+                             (car (last (split-string name "src")))))
+                         (file-name-nondirectory name))))`} (main, test) where
+--------------------------------------------------------------------------------
+import           Test.Prelude
+--------------------------------------------------------------------------------
+import           ${1:$(s-chop-suffix "Spec" yas-text)}
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain test
+
+test :: TestTree
+test = testGroup "$1"
+  [ $0
+  ]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/undefined b/users/aspen/emacs/snippets/haskell-mode/undefined
new file mode 100644
index 0000000000..7bcd99b571
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/undefined
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: undefined
+# key: u
+# expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil))
+# --
+undefined$1
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/action-type b/users/aspen/emacs/snippets/js2-mode/action-type
new file mode 100644
index 0000000000..ef8d1a3863
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/action-type
@@ -0,0 +1,4 @@
+# key: at
+# name: action-type
+# --
+export const ${1:FOO_BAR$(->> yas-text s-upcase (s-replace-all '(("-" . "_") (" " . "_"))))}: '${3:ns}/${1:$(-> yas-text s-dashed-words)}' = '$3/${1:$(-> yas-text s-dashed-words)}'$5
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/before b/users/aspen/emacs/snippets/js2-mode/before
new file mode 100644
index 0000000000..4569b65831
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/before
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: before
+# key: bef
+# --
+before(function() {
+                  $1
+})
diff --git a/users/aspen/emacs/snippets/js2-mode/context b/users/aspen/emacs/snippets/js2-mode/context
new file mode 100644
index 0000000000..d83809f3c3
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/context
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: context
+# key: context
+# --
+context('$1', function() {
+              $2
+})
diff --git a/users/aspen/emacs/snippets/js2-mode/describe b/users/aspen/emacs/snippets/js2-mode/describe
new file mode 100644
index 0000000000..bd0198181d
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/describe
@@ -0,0 +1,6 @@
+# key: desc
+# name: describe
+# --
+describe('$1', () => {
+  $2
+})
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/expect b/users/aspen/emacs/snippets/js2-mode/expect
new file mode 100644
index 0000000000..eba41ef330
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/expect
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: expect
+# key: ex
+# --
+expect($1).$2
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/function b/users/aspen/emacs/snippets/js2-mode/function
new file mode 100644
index 0000000000..b423044b44
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/function
@@ -0,0 +1,6 @@
+# key: f
+# name: function
+# --
+function $1($2) {
+         $3
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/header b/users/aspen/emacs/snippets/js2-mode/header
new file mode 100644
index 0000000000..3e303764cb
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/header
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: header
+# key: hh
+# expand-env: ((yas-indent-line 'fixed))
+# --
+////////////////////////////////////////////////////////////////////////////////
diff --git a/users/aspen/emacs/snippets/js2-mode/it b/users/aspen/emacs/snippets/js2-mode/it
new file mode 100644
index 0000000000..a451cfc08a
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/it
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: it
+# key: it
+# --
+it('$1', () => {
+  $2
+})
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/it-pending b/users/aspen/emacs/snippets/js2-mode/it-pending
new file mode 100644
index 0000000000..00da312e10
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/it-pending
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: it-pending
+# key: xi
+# --
+it('$1')$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/module b/users/aspen/emacs/snippets/js2-mode/module
new file mode 100644
index 0000000000..dc79819d89
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/module
@@ -0,0 +1,12 @@
+# key: module
+# name: module
+# expand-env: ((yas-indent-line (quote fixed)))
+# condition: (= (length "module") (current-column))
+# --
+/**
+ * @fileOverview $1
+ * @name ${2:`(file-name-nondirectory (buffer-file-name))`}
+ * @author Griffin Smith
+ * @license Proprietary
+ */
+$3
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/record b/users/aspen/emacs/snippets/js2-mode/record
new file mode 100644
index 0000000000..0bb0f02436
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/record
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: record
+# key: rec
+# --
+export default class $1 extends Record({
+  $2
+}) {}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/test b/users/aspen/emacs/snippets/js2-mode/test
new file mode 100644
index 0000000000..938d490a74
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/test
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: test
+# key: test
+# --
+test('$1', () => {
+  $2
+})
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/nix-mode/fetchFromGitHub b/users/aspen/emacs/snippets/nix-mode/fetchFromGitHub
new file mode 100644
index 0000000000..d2447e4b5a
--- /dev/null
+++ b/users/aspen/emacs/snippets/nix-mode/fetchFromGitHub
@@ -0,0 +1,12 @@
+# -*- mode: snippet -*-
+# name: fetchFromGitHub
+# uuid: fetchFromGitHub
+# key: fetchFromGitHub
+# condition: t
+# --
+fetchFromGitHub {
+                owner = "$1";
+                repo = "$2";
+                rev = "$3";
+                sha256 = "0000000000000000000000000000000000000000000000000000";
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/nix-mode/pythonPackage b/users/aspen/emacs/snippets/nix-mode/pythonPackage
new file mode 100644
index 0000000000..0a74c21e18
--- /dev/null
+++ b/users/aspen/emacs/snippets/nix-mode/pythonPackage
@@ -0,0 +1,16 @@
+# key: pypkg
+# name: pythonPackage
+# condition: t
+# --
+${1:pname} = buildPythonPackage rec {
+           name = "\${pname}-\${version}";
+           pname = "$1";
+           version = "${2:1.0.0}";
+           src = fetchPypi {
+               inherit pname version;
+               sha256 = "0000000000000000000000000000000000000000000000000000";
+           };
+           propagatedBuildInputs = with pythonSelf; [
+               $3
+           ];
+};
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/nix-mode/sha256 b/users/aspen/emacs/snippets/nix-mode/sha256
new file mode 100644
index 0000000000..bc640e5ab0
--- /dev/null
+++ b/users/aspen/emacs/snippets/nix-mode/sha256
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: sha256
+# uuid: sha256
+# key: sha256
+# condition: t
+# --
+sha256 = "0000000000000000000000000000000000000000000000000000";
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/SQL source block b/users/aspen/emacs/snippets/org-mode/SQL source block
new file mode 100644
index 0000000000..b5d43fd6bc
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/SQL source block
@@ -0,0 +1,6 @@
+# key: sql
+# name: SQL source block
+# --
+#+BEGIN_SRC sql ${1::async}
+$2
+#+END_SRC
diff --git a/users/aspen/emacs/snippets/org-mode/combat b/users/aspen/emacs/snippets/org-mode/combat
new file mode 100644
index 0000000000..b4db0f433a
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/combat
@@ -0,0 +1,13 @@
+# -*- mode: snippet -*-
+# name: combat
+# uuid: combat
+# key: combat
+# condition: t
+# --
+|             | initiative | max hp | current hp | status |      |
+|-------------+------------+--------+------------+--------+------|
+| Barty Barty |            |        |            |        | <--- |
+| Hectoroth   |            |        |            |        |      |
+| Xanadu      |            |        |            |        |      |
+| Aurora      |            |        |            |        |      |
+| EFB         |            |        |            |        |      |
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/date b/users/aspen/emacs/snippets/org-mode/date
new file mode 100644
index 0000000000..297529cdac
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/date
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# key: date
+# name: date.org
+# --
+[`(format-time-string "%Y-%m-%d")`]$0
diff --git a/users/aspen/emacs/snippets/org-mode/date-time b/users/aspen/emacs/snippets/org-mode/date-time
new file mode 100644
index 0000000000..fde469276c
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/date-time
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: date-time
+# key: dt
+# --
+[`(format-time-string "%Y-%m-%d %H:%m:%S")`]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/description b/users/aspen/emacs/snippets/org-mode/description
new file mode 100644
index 0000000000..a43bc95cc3
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/description
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: description
+# key: desc
+# --
+:DESCRIPTION:
+$1
+:END:
diff --git a/users/aspen/emacs/snippets/org-mode/nologdone b/users/aspen/emacs/snippets/org-mode/nologdone
new file mode 100644
index 0000000000..e5be85d6b3
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/nologdone
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: nologdone
+# key: nologdone
+# --
+#+STARTUP: nologdone$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/python source block b/users/aspen/emacs/snippets/org-mode/python source block
new file mode 100644
index 0000000000..247ae51b0b
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/python source block
@@ -0,0 +1,6 @@
+# key: py
+# name: Python source block
+# --
+#+BEGIN_SRC python
+$0
+#+END_SRC
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/reveal b/users/aspen/emacs/snippets/org-mode/reveal
new file mode 100644
index 0000000000..1bdbdfa5dc
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/reveal
@@ -0,0 +1,6 @@
+# key: reveal
+# name: reveal
+# condition: t
+# --
+#+ATTR_REVEAL: :frag ${1:roll-in}
+$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/transaction b/users/aspen/emacs/snippets/org-mode/transaction
new file mode 100644
index 0000000000..37f2dd31ca
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/transaction
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: transaction
+# key: begin
+# --
+BEGIN;
+$0
+ROLLBACK;
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/prolog-mode/tests b/users/aspen/emacs/snippets/prolog-mode/tests
new file mode 100644
index 0000000000..a9d92a0d5b
--- /dev/null
+++ b/users/aspen/emacs/snippets/prolog-mode/tests
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: tests
+# uuid: tests
+# key: tests
+# condition: t
+# --
+:- begin_tests(${1:name}).
+
+$0
+
+:- end_tests($1).
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/prolog-mode/use-module b/users/aspen/emacs/snippets/prolog-mode/use-module
new file mode 100644
index 0000000000..75fd19b641
--- /dev/null
+++ b/users/aspen/emacs/snippets/prolog-mode/use-module
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: use-module
+# uuid: use-module
+# key: use
+# condition: t
+# --
+:- use_module(${1:library($2)}${3:, [$4]}).
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/add_column b/users/aspen/emacs/snippets/python-mode/add_column
new file mode 100644
index 0000000000..47e83850d5
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/add_column
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: add_column
+# key: op.add_column
+# --
+op.add_column('${1:table}', sa.Column('${2:name}', sa.${3:String()}))$0
diff --git a/users/aspen/emacs/snippets/python-mode/decorate b/users/aspen/emacs/snippets/python-mode/decorate
new file mode 100644
index 0000000000..4f96748572
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/decorate
@@ -0,0 +1,15 @@
+# -*- mode: snippet -*-
+# name: decorate
+# uuid: decorate
+# key: decorate
+# condition: t
+# --
+def wrap(inner):
+    @wraps(inner)
+    def wrapped(*args, **kwargs):
+        ret = inner(*args, **kwargs)
+        return ret
+
+    return wrapped
+
+return wrap
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/dunder b/users/aspen/emacs/snippets/python-mode/dunder
new file mode 100644
index 0000000000..71d99dddc6
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/dunder
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: dunder
+# uuid: dunder
+# key: du
+# condition: t
+# --
+__$1__$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/name b/users/aspen/emacs/snippets/python-mode/name
new file mode 100644
index 0000000000..1495cc91d9
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/name
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: name
+# uuid: name
+# key: name
+# condition: t
+# --
+__name__
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/op.get_bind.execute b/users/aspen/emacs/snippets/python-mode/op.get_bind.execute
new file mode 100644
index 0000000000..aba801c6ba
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/op.get_bind.execute
@@ -0,0 +1,7 @@
+# key: exec
+# name: op.get_bind.execute
+# --
+op.get_bind().execute(
+    """
+    `(progn (sqlup-mode) "")`$1
+    """)
diff --git a/users/aspen/emacs/snippets/python-mode/pdb b/users/aspen/emacs/snippets/python-mode/pdb
new file mode 100644
index 0000000000..41c6f87cbf
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/pdb
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: pdb
+# uuid: pdb
+# key: pdb
+# condition: t
+# --
+import pdb; pdb.set_trace()
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/#[macro_use] b/users/aspen/emacs/snippets/rust-mode/#[macro_use]
new file mode 100644
index 0000000000..fea942a337
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/#[macro_use]
@@ -0,0 +1,5 @@
+# key: macro_use
+# name: #[macro_use]
+# --
+#[macro_use]
+${1:extern crate} ${2:something};$0
diff --git a/users/aspen/emacs/snippets/rust-mode/async test b/users/aspen/emacs/snippets/rust-mode/async test
new file mode 100644
index 0000000000..2352d7b56b
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/async test
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: async test
+# uuid: atest
+# key: atest
+# condition: t
+# --
+#[tokio::test${1:(flavor = "multi_thread")}]
+async fn ${2:test_name}() {
+   `%`$0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/benchmark b/users/aspen/emacs/snippets/rust-mode/benchmark
new file mode 100644
index 0000000000..9ec4307538
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/benchmark
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: benchmark
+# uuid: benchmark
+# key: bench
+# condition: t
+# --
+#[bench]
+fn ${1:benchmark_name}(b: &mut Bencher) {
+   `%`b.iter(|| $0);
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/proptest b/users/aspen/emacs/snippets/rust-mode/proptest
new file mode 100644
index 0000000000..be12af4911
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/proptest
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: proptest
+# uuid: proptest
+# key: proptest
+# condition: t
+# --
+#[proptest]
+fn ${1:test_name}($2) {
+   `%`$0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/test-module b/users/aspen/emacs/snippets/rust-mode/test-module
new file mode 100644
index 0000000000..bfa2ca2d18
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/test-module
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: test-module
+# uuid: test-module
+# key: tmod
+# condition: t
+# --
+mod $1 {
+    use super::*;
+
+    $0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/tests b/users/aspen/emacs/snippets/rust-mode/tests
new file mode 100644
index 0000000000..0a476ab586
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/tests
@@ -0,0 +1,9 @@
+# key: tests
+# name: test module
+# --
+#[cfg(test)]
+mod ${1:tests} {
+    use super::*;
+
+    $0
+}
diff --git a/users/aspen/emacs/snippets/snippet-mode/indent b/users/aspen/emacs/snippets/snippet-mode/indent
new file mode 100644
index 0000000000..d38ffceafb
--- /dev/null
+++ b/users/aspen/emacs/snippets/snippet-mode/indent
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: indent
+# key: indent
+# --
+# expand-env: ((yas-indent-line 'fixed))
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/sql-mode/count(*) group by b/users/aspen/emacs/snippets/sql-mode/count(*) group by
new file mode 100644
index 0000000000..6acc46ff39
--- /dev/null
+++ b/users/aspen/emacs/snippets/sql-mode/count(*) group by
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: count(*) group by
+# key: countby
+# --
+SELECT count(*), ${1:column} FROM ${2:table} GROUP BY $1;
diff --git a/users/aspen/emacs/snippets/terraform-mode/variable b/users/aspen/emacs/snippets/terraform-mode/variable
new file mode 100644
index 0000000000..14822f1a05
--- /dev/null
+++ b/users/aspen/emacs/snippets/terraform-mode/variable
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: variable
+# uuid: variable
+# key: var
+# condition: t
+# --
+variable "${1:name}" {
+  type = ${2:string}
+  ${3:default = ${4:default}}
+}
+$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/text-mode/date b/users/aspen/emacs/snippets/text-mode/date
new file mode 100644
index 0000000000..7b94311470
--- /dev/null
+++ b/users/aspen/emacs/snippets/text-mode/date
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# name: date
+# key: date
+# --
+`(format-time-string "%Y-%m-%d")`$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/tuareg-mode/expect-test b/users/aspen/emacs/snippets/tuareg-mode/expect-test
new file mode 100644
index 0000000000..e0b541fce4
--- /dev/null
+++ b/users/aspen/emacs/snippets/tuareg-mode/expect-test
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: expect-test
+# uuid: expect-test
+# key: exp
+# condition: t
+# --
+let%expect_test "${1:name}" =
+        ${2:<body>};
+        [%expect {| $3 |}]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/tuareg-mode/module b/users/aspen/emacs/snippets/tuareg-mode/module
new file mode 100644
index 0000000000..9b1701e3a2
--- /dev/null
+++ b/users/aspen/emacs/snippets/tuareg-mode/module
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: module
+# uuid: module
+# key: mod
+# condition: t
+# --
+module ${1:Name} = struct
+       $0
+end
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/tuareg-mode/test-module b/users/aspen/emacs/snippets/tuareg-mode/test-module
new file mode 100644
index 0000000000..b16176e5f3
--- /dev/null
+++ b/users/aspen/emacs/snippets/tuareg-mode/test-module
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: test-module
+# uuid: test-module
+# key: tmod
+# condition: t
+# --
+let%test_module ${1:_} =
+  (module struct
+    $0
+  end)
\ No newline at end of file
diff --git a/users/aspen/goodcry-band/flower-icon.svg b/users/aspen/goodcry-band/flower-icon.svg
new file mode 100644
index 0000000000..b6be590293
--- /dev/null
+++ b/users/aspen/goodcry-band/flower-icon.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1771.11 1435.15"><defs><style>.cls-1{stroke-width:0px;}</style></defs><g id="Vector_layer"><path class="cls-1" d="M1771.11,875.48c-.33-35.4-10.09-70.1-27.29-101.01-11.08-19.24-20.82-39.78-36.96-55.42-10.4-9.11-17.83-20.72-26.21-31.56-9.33-10.95-21.45-19-30.38-30.36-27.35-22.24-12.76-34.32-1.94-60.9,8.79-20.05,13.71-41.66,19.19-62.79,3.57-18.58.18-37.72-1.37-56.39-1.58-22.27-1.44-45.19-10.32-66.32-14.2-47.8-36.49-95.61-75.58-128.05-27.22-18.93-57.09-39.56-91.51-39.59-9.48.98-18.42,4.67-27.92,5.76-42.77,16.76-26.99-42.54-37.06-64.98-7.1-26.54-27.89-32.48-52.51-35.43-35.43-4.89-71.98,9.13-106.54-3.49-27.14-13.8-43.12-42.06-67.02-60.24-55.12-45.05-129.47-62.61-199.5-64.59-34.05-.15-68.54-.65-102.33,3.82-19.99,5.97-41.12,6.98-60.85,13.73-11.73,4.12-21.82,11.56-32.67,17.44-14.45,6.32-26.44,17.64-31.74,32.68-41.27-86.06-119.21-114.31-200.79-60.09-37.38,32.01-56.19,79.49-66.8,126.38-7.07,39.24-12.98,79.64-1.49,118.7,4.49,14.6,11.02,28.51,17.25,42.44,5.46,12.25,22.04,36.07,11.56,47.99-12.29-15.01-16.12-37.44-24.48-55.11-12.75-41.95-18.76-86.69-38.95-126.03-8.5-12.98-18.32-25.96-32.06-33.73-14.3-6.37-31.26-12.33-46.87-7.92-31.92,11.75-45.64,44.4-55.98,74.29-5.87-22.48-17.95-43.18-38.16-55.48-15.69-13.73-36.01-16.25-56.09-15.83-50.91-2.01-98.86,27.31-134.89,61.03C39.6,275.57,4.76,342.32.4,439.12c-1.8,28,2.8,56.49,8.82,83.77,16.71,43,43.38,81.31,75.86,113.96-2.74,32.46.58,65.14,11.69,95.83-31.32-24.65-81.15-18.06-83.79,27.85-3.23,22.9-4.91,46.38-2.93,69.45,5.2,20.4,8.5,41.54,16.9,60.96,18.83,41.77,41.21,82.18,71.7,116.58,20.37,30.63,47.6,55.15,73.07,81.38,10.37,11.22,21.87,21.21,33.82,30.69,17.27,17.57,36.39,32.9,55.96,47.8,10.67,8.76,23.16,14.67,34.97,21.63,15.61,11.02,32.8,19.22,50.78,25.58,17.47,7.91,35.94,13,54.66,16.96,19.23,5.22,38.06,12.06,57.93,14.63,24.68,3.06,48.25,12.66,73.43,11.62,7.18-1.6,15.25-2.2,22.17-5.03,22.35,16.36,45.55,31.57,69.54,45.57,83.91,37.44,178.97,48.34,270.03,38.15-15.17,30.55,14.79,56.84,37.84,72.5,15.94,10.07,33.68,18.07,52.24,21.71,21.14,2.19,42.5,4.71,63.78,4.44,45.11-2.87,90.41-7.14,134.22-18.8,46.89-12.93,93.05-32.46,132.57-61.02,37.78-30.46,74.07-64.12,96.9-107.61,11.75,27.84,44.29,37.48,72.01,30.87,31.21-4.75,72.45-43.66,100.77-74.45,22.14,27.29,73.15-50.99,85.41-69.68,16.52-28.25,36.67-54.5,50.46-84.23,25.92-55.19,45.48-113.63,49.89-174.72ZM1460.9,276.15c8.88-1.45,16.57-7.19,25.88-5.6,38.05.11,69.23,26.2,95.06,51.54,22.91,26.92,38.54,59.74,47.92,93.66,2.39,12.99,4.44,26,8.06,38.73,5.51,21.17,4.93,43.11,2.43,64.68-.22,16.89-3.18,33.46-8.58,49.26-2.69-15.06-7.16-30.8-13.57-47.56-18.23-48.57-51.88-88.69-77.18-133.44-13.07-22.62-30.2-42.54-45-64.02-15.04-17.3-32.62-32.22-51.4-45.35,4.85.02,10.27-.58,16.36-1.91ZM794.3,100.59c7.64-9.84,14.83-21.6,26.28-27.31,17.39-5.79,34.62-11.96,51.74-18.52,97.43-13.85,208.66-16.11,294.28,39.46,34.23,21.67,53.11,63.65,92.83,77.16,16.61,5.49,34.67,5.99,52.03,5.23,21.49-2.47,43.3-4.12,64.79-.68,15.89,1.73,23.66,12.75,24.22,27.96,2.01,18.94,3.67,38.68,10.47,53.03-39.82-19.54-81.21-14.12-118.72,16.16-26.28,21.78-52.37,43.89-75.28,69.27-26.39,25.53-52.02,51.64-75.32,80.03-23.57,30.02-46.59,61.01-74.53,87.2-26.54,27.89-59.1,48.33-89.41,71.53-17.91-25.07-48.34-25.96-77.12-19.25.58-3.03.88-6.3.85-9.78,2.27-22.58-5-44.45-8.94-66.45-3.19-18.91-7.79-37.5-13.77-55.73-4.32-10.56-4.98-21.17-3.54-32.39.32-23.57,2.43-47.29-.8-70.75-3.2-19.68-4.45-39.95-11.75-58.7-5.45-13.08-13.18-25.05-19.89-37.51-6.18-11.07-14.93-20.28-22.35-30.46-6.96-11.13-12.44-23.08-18.79-34.54-12.95-18.54-20.28-44.56-7.27-64.97ZM976.33,625.44c10.76,17.07,28.85,26.47,45.26,37.11-20.03,6.69-40.98,19.19-62.62,13.63-.43-.16-.86-.34-1.25-.57-5.97-7.79-.57-15.63,3.67-22.74,5.38-8.92,10.95-17.77,14.94-27.43ZM869.12,501.61c3.31,17.37,7.21,35.47,6.43,53.1-7.77-11.15-4.84-38.49-6.43-53.1ZM848,333.84c-5.35-15.16-10.23-30.46-15.18-45.75-6.65-17.66-13.04-35.39-19.71-53.04,8.41,12.5,16.73,25.1,21.3,39.6,7.94,18.88,14.81,38.4,13.58,59.18ZM88.3,601.18c-28.81-34.4-49.07-66-58.54-110.42-9.6-82.28,10.7-159.42,58.88-226.72,24.07-32.65,54.03-62.77,90.98-80.41,19.48-10.85,42.08-15.61,64.29-14.33,10.47,2.03,20.17,6.87,30.57,9.31,37.38,15.8,33.97,66.88,33.95,101.01-1.88,33.09-4.35,66.13-6.87,99.18-3.62,24.48-.95,48.94-.18,73.5,1.66,32.76,4.12,65.55,11.27,97.64-26.21-22.48-58.46-37.58-92.41-43.82-40.78-11.29-81.13-1.9-98.56,39.96-18.74,12.39-30.71,32.91-33.38,55.1ZM334.87,610.31c23.05,26.91,41.89,57.88,54.95,90.79-36.04-38.71-63.88-83.9-98.64-123.66-8.51-10.24-20.26-16.73-31.31-23.82-13.1-9.08-27.21-16.58-42.48-21.31,46.73,11.66,88.67,39.39,117.47,78ZM332.1,503.73c1.95,7.45,2.99,15.05,2.79,22.76-1.06-7.57-1.98-15.16-2.79-22.76ZM114.44,615.85c.74-5.82,1.52-11.63,2.45-17.42,2.77,14.91,7,29.54,13.47,43.28-5.93-4.84-11.73-8.96-17.29-14.7.43-3.72.9-7.44,1.38-11.16ZM111.3,661.38c19.46,16.13,39.09,31.95,55.06,51.72,16.66,16.85,27.56,38.57,43.05,56.42,21.41,20.13,42.37,40.75,63.56,61.14,11.57,9.66,21.98,22.93,36.89,27.21,22.22.92,17.47-26.05,2.2-33.47-14.76-14.88-30.85-28.45-46.46-42.48-24.9-25.97-49.23-52.56-72.26-80.21-9.81-10.94-15.8-24.3-22.42-37.24-15.16-26.47-26.4-56.31-29.53-86.36.57-5.28,1.67-10.5,2.86-15.68,31.15-19.97,68.4-6.45,97.75,10.47,16.32,11.99,31.14,26.3,45.44,40.62,24.27,28.88,43.09,61.94,68.43,89.89,10.43,10.79,19.72,24.83,34.28,30.13,12.67,4.33,27.75-3.32,28.77-17.34,2.63-37.24-44.91-97.48-66.53-128.42,8.67-10.03,13.28-23.77,10.48-36.93.33-12.55-1.32-24.83-3.8-37.11-.62-23.3-4.68-46.26-6.93-69.43-4.54-36.88-9.37-73.89-8.4-111.11-2.57-24.7-.77-48.96,4.12-73.39,6.75-30.4,13.32-63.87,36.28-86.56,57.97-32.22,74.96,66.65,85.91,104.54,12.17,28.63,23.88,117.38,64.24,112.62,9.97-1.04,18.73-7.05,22.74-16.3,11.58-25.22-6.04-50.26-14.89-73.35-10.28-21.68-18.74-44.83-20.05-68.97-2.58-55.92,14.63-112.1,42.91-160.03,22.76-31.39,66.7-50.84,105.14-43.38,40.58,10.02,68.66,46.59,81.02,84.92,8.35,28.47,16.77,56.84,21.36,86.18,7.76,24.01,16.84,47.67,25.73,71.31,15.69,49.6,33.95,98.35,49.09,148.13.34,10.29-.95,20.47-2.61,30.6-2.23,17.46-1.06,35.15-2.09,52.7-2.07,22.02.43,43.7,8.76,64.27,1.98,4,4.18,7.35,6.53,10.09-18.84,7.48-38.8,4.69-58.34,1.31-32.1.35-85.85-28.12-110.1-.58-20.21,23.8-.45,57.49,23.61,69.73,29.65,18.66,79.95,12.81,95.05,49.68,3.48,8.33-.05,16.2-.3,24.66.64,16.51,3.06,34.82,14.9,47.29,34.84,34.8,75.25,42.75,117.08,14.53,33.39,20.64,15.06,64.05,66.8,83.16,27.3,8.23,49.21-15.85,74.01-21.83,23.39-3.97,32.88,12.87,12.12,27.95-20.67,15.81-51.96,31.06-46.48,62.25,3.57,26.4,61.82,71.49,11.47,84.41-15.38,3.61-38.86,3.06-38.55-17.85,1.06-8.45,3.48-16.98,1.76-25.61-2.7-14.51-11.62-30.87-27.55-33.2-14.16-.6-29.2,5.7-42.72-1.42-43.25-22.24-17.84-27.6-35.17-61.5-14.95-35.59-63.38-30.7-91.49-14.67-12.83,5.54-23.46,14.47-34.1,23.31-8.03,5.06-13.63,17.73-24.32,15.81-6-15.53,20.34-37.57,22.97-58.44,2.92-11.99-4.24-24.97-15.79-29.16-9.23-3.06-19.67-2.12-27.99-7.88-12.86-16.46-24.25-34.06-33.58-52.76-8.92-20.17-12.26-45.08-30.8-59.31-12.79-9.14-30.2-12.68-44.87-6.1-8.82,3.95-15.59,11.31-22,18.36-7.33,9.47-11.79,21.28-23.7,26.19-12.31,5.44-26.04,7.16-39.28,9.02-10.99.57-22.5-.91-32.96,3.28-16.75,6.65-29.32,26.66-21.28,44.2,10.53,28.33,41.16,39.15,68.7,43.31,15.69,1.54,31.74.01,47.18,3.74,25.94,7.59,58.99,25.78,17.03,43.03-10.84,3.4-22.51,3.51-33.79,4.33-8.53-.17-17.03-4.38-25.5-1.91-48.6,19.81-18.72,78.15,18,93.1,25.89,16.55,46.83,12.4,75.34,8.38,19.34-2.71,21.17-5.6,35.71,10.49,10.41,14.93,8,36.65,24.78,47.28,17.97,9.46,41.78,11.17,60.58,3,16.49-7.83,28.72-17.65,32.6-36.22,14.22-11.78,32.75-6.88,48.67-1.63,17.38,6.74,37.39,11.35,50.3,25.62,28.26,29.07,8.32,28.99-6.95,40.69-44.87-19.19-99.3-.94-125.47,39.81-8.38,11.74-16.92,23.5-23.33,36.46-7.26,15.64-13.14,33.12-24.63,46.14-15.36,11.6-11.72-51.75-17.11-60.92-5.91-19.44-13.17-39.02-26.85-54.42-10.75-11.19-19.73-24.05-31.09-34.61-9.29-7.86-19.61-14.35-29.38-21.6-35.21-23.33-73.94-44.36-115.64-52.62-34.02-1.47-72.59-2.5-99.67,21.57-4.02,4.69-7.47,9.83-10.35,15.27-7.8-7-18.54-11.32-26.42-18.18-60.52-39.09-117.75-82.22-171.52-130.32-35.89-36.56-65.16-79.39-83.83-127.19-6.23-11.64-6.67-24.85-8.74-37.6-2.15-11.6-3.97-23.2-3.66-35.03ZM385.59,1028.94c-2.28-.29-4.55-.69-6.74-1.39-40.37-17.97-74.89-47.36-105.97-78.35,16.13,11.57,30.06,25.98,47.52,35.69,20.99,15.74,44.7,27.8,65.19,44.04ZM520.9,1229.92c-9.86-1.53-20.79-4.42-31.1-4.71-12.99-1.41-25.25-6.52-38.06-8.95-23.99-4.06-47.48-9.48-70.02-18.78-15.03-4.58-29.96-9.44-44.03-16.54-36.74-14.55-66.63-41.01-97.15-65.43-13.76-9.49-25.33-21.6-36.89-33.68-17.66-16.96-36.28-33.17-51.25-52.65-8.88-11.4-19.87-20.89-29.32-31.79-10.3-11.81-18-25.58-26.55-38.64-23.31-30.48-41.14-64.78-53.36-101.17-6.71-16.45-3.69-34.34-5.41-51.62-.18-16.75-1.77-33.77,1.77-50.29,1.54-3.78,5.71-7.97,9.37-10.15-.47.32-.9.61-1.37.94,22.48-7.46,51.81,20.27,62.48,38.51,22.98,25.35,35,57.88,57.34,83.61,37.58,50.02,77.76,98.82,128.54,135.93,24.87,20.02,49.5,45.84,82.83,49.46,5.38.51,10.9.03,15.96-1.35-.19,12.2,1.87,24.43,6.25,35.7,17.1,45.19,54.22,78.97,88.98,111.01,11.65,10.55,23.59,20.73,35.79,30.57-1.6-.02-3.21-.01-4.8.03ZM926.61,1303.58c-45.82,12.53-95.58,10.91-142.64,6.74-22.9-1.06-45.29-5.96-68.05-8.42-13.96-2.89-27.06-8.9-40.85-12.54-20.02-7.77-38.98-18.24-58.58-27.05-36.44-23.15-70.85-49.29-103.16-77.89-16.42-14.82-32.86-29.65-46.82-46.88-16.63-17.08-32.49-35.56-40.57-58.36-8.2-25.11-4.74-55.22,17.2-72.34,9.7-5.07,21.18-5.52,31.77-7.79,18.13-3.74,35.55,3.76,53.29,6.37,37.41,6.83,69.98,29.9,100.42,51.62,11.9,12.38,27.28,21.17,37.81,34.81,22.03,29.65,29.08,54.8,31.35,91.12,1.02,23.18,16.61,49.86,43.16,41.69,14.51-6.21,21.8-21.67,29.68-34.42,13.46-20.81,21.28-45.08,36.75-64.5,41.18-48.81,96.1-38.48,135.1,6.92,56.28,67.28,27.62,109.15-15.89,170.91ZM1356.98,1266.48c-24.13,25.97-55.25,43.16-86.52,59-31.55,18.74-65.82,31.58-99.21,46.46-34.07,11.31-69.21,22.03-105.43,21.89-22.92,1.44-45.79,2.66-68.58-1.13-12.23-.96-24.61-1.27-36.44-4.81-31.08-12.81-33-30.58-18.42-58.44,11.46-8.92,25.56-11.34,39.51-13.63,22.06-3.86,44.25-7.82,66.72-7.68,38.69-2.74,77.27-8.08,115.31-15.85,53-13.52,103.66-35.31,151.44-61.78,12.96-6.95,25.89-14,38.38-21.77,13.57-6.82,26.27-20.48,42.02-20.53,5.44,27.57-21.91,58.41-38.78,78.28ZM1485.27,1251.85c-18.36,5.92-40.47,2.7-48.58-16.83-3.94-17.02-2.26-34.98-.67-52.25,1.04-8.73-2.22-17.25-.28-25.88,1.92-11.2-.72-22.38-1.7-33.54-5.26-64.63,25.85-72.52,45.89-120.67,16.2-43.4-16.24-83.42-17.14-126.48,14.78-25.89,30.23-53.13,30.52-83.78,1.11-18.02,2.21-44.2-16.71-53.35-15.4-15.05-36.41-24.78-57.93-26.54-18.96-2.2-35.23,9.28-53.28,12.29-13.66,3.06-26.04,9.91-39.2,14.48-17.35,5.23-33.45,13.36-49.16,22.27-30.19,16.08-60.98,32.56-88.28,52.92-6.76,8.02-3.27,20.93,6.48,24.61,7.04,2.08,14.86,1.04,22.1.83,9.77-1.45,18.51-6.43,27.18-10.91,13.84-6.84,28.97-11.49,41.11-21.34,28.34-20.15,57.55-39.65,88.02-56.35,20.62-4.64,49.5-9.85,69.18-1.13-20.6,32.99-55.54,101.96-47.8,140.64-16.65,17.3-37.63,29.42-56.98,43.35-16.9,7.01-34.2,13.25-51.21,19.69-8.82,5.05-9.24,19.19.01,24.07,16.48,6.09,35.83-4.02,52.77-5.88,18.2-3.15,35.85-8.25,53.42-13.84,8.78-1.65,17.85-1.31,26.62-2.97,10.04-2.15,20.19-5.62,30.35-2.17,5.23,28.45-7.19,55.96-20.01,80.69-13.51,12.47-27.52,24.52-42.35,35.38-12.24,9.15-27.57,11.45-41.74,16.21-27.06,10.47-54.95,17.51-83.37,23.17-33.03,7.99-67.65,8.69-99.99,19.19-9.2,1.97-22.03,2.86-24.57,13.91-2.67,9.51,6.56,18.36,15.89,17.32,7.98-2.26,16.12-2.36,24.46-3.56,25.86-4.07,51.3-10.3,77.33-13.39,35.89-5.73,147.2-30.71,152.85,23.02-22.81-15.12-80.81,28.91-103.67,40.53-29.59,16.38-60.59,29.89-91.58,43.35-31.87,10.4-64.31,20.71-97.6,25.18-19.87,4.21-40.04,3.1-60.15,4.46-29.64,4.99-59.95,5.93-89.21,13.05,57.44-76.41,39.21-148.41-34.4-204.64,10.4-7.64,19.72-19.15,22.94-31.9.81-12.81-5.4-26.2-14.04-35.48-26.34-21.25-57.1-39.89-91.82-41.07-18.34-1.88-39.59-2.77-52.39,13.03-6.04,8.01-6.55,19.81-13.82,26.73-15.35,5.72-40.8,12.31-49.94-5.87-3.71-10.32-2.82-22.32-11.06-30.92-14.52-20.29-42.39-31.96-66.22-22.04-32.44,9.22-81.61-6.54-85.12-44.82,11.75-6.85,27.33-1.64,40.41-3.13,9.76-.64,19.42-2.54,28.55-6.11,48.23-15.79,47.9-61.86,5-84.59-19.44-8.52-41.22-9.23-61.89-12.92-22.77-2.68-55.99-2.03-62.25-30.06.46-.36.93-.71,1.41-1.04,49.06-22.1,61.53,16.68,115.67-54.54,34.01-30.38,41.58,30.65,51.81,51.81,10,18.48,22.11,36.57,36.7,51.75,10.11,8.36,23.08,12.28,35.64,15.28.15,2.46-.46,4.89-1.54,7.09-7.68,14.49-17.23,28.34-21.46,44.37-9.01,23.18,18.04,48.47,39.92,35.23,36.75-16.55,43.49-43.44,90.42-47.9,18.89-2.81,29.36,13.05,25.72,30.43,1.36,14.48,8.7,29.15,19.67,38.74,13.36,10.4,28.94,20.84,46.37,21.47,32.23-.52,38.48-6.15,32.53,31.24-2.1,14.36,6.17,29.35,18.14,37.05,20.51,14.75,56.22,10.84,76.06-3.43,31.34-22.47,14.55-61.63-5.02-86.1-4.65-8.21-13.35-20.77-6.86-29.71,22.69-19.57,61.62-35.38,55.02-71.68-8.69-30.28-46.31-37.54-71.97-24.7-27.44,16.56-51.29,30.05-67.99-7.85-4.99-17.6-15.54-33.17-26.58-47.56-10.12-11.53-28.91-14.12-42.41-7.45-6.05,2.82-9.8,8.63-15.53,11.85-16.61,3.53-34.6.19-48.68-9.43-29.23-22.23-21.02-34.26-21.7-65.53-5.39-32.49-36.53-51.7-66.29-59.2-15.41-6.34-32.33-8.94-47.23-16.33-7.71-5.81-14.04-14.89-13.74-24.87,4.41-10.47,26.2-1.23,35.68-1.2,18.37,2.46,36.13,7.31,54.19,11.14,32.96,5.06,66.61-2.33,96.92-15.36,17.97-7.67,73.04-14.45,58.13,19.6-11.12,23.14-33.59,51.26-20.83,77.54,11.08,15.38,32.97,18.39,50.81,14.41,22.08-3.4,40.8-17.23,63.44-17.95,1.71,1.64,13.81-3.91,15.44-2.18,1.94,2.35-6.58,11.94-4.63,14.27-43.96,68.69,42.61,97.83,95.15,70.78,33.38-16.03,47.18-54.04,77.36-73.6,15.17-8.27,31.06-15.13,46.36-23.16,21.76-10.04,44.26-18.32,62.62-34.27,28.12-18,53.17-41.63,72.66-68.73,16.03-21.5,11.47-59.55-22.42-45.47-23.4,14.02-45.14,31.2-64.2,50.74-11.15,8.9-22.25,17.87-32.55,27.76-15.19,14.19-29.82,28.96-45.84,42.23-14.65,14.1-30.04,27.07-47.86,37.01-30.09,23.32-80.12,48.72-110.63,13.07,8.98-22.1-13.61-39.84-34.34-38.79-11.27-8.01-22.64-15.88-33.58-24.34-9.39-6.71-20.12-12.84-23.81-24.54,0,0-.09.31-.09.31-.19-.77-.36-1.54-.51-2.32,16.63-18.58,39.83-30.08,58.92-45.96,19.26-15.5,36.96-32.57,53.44-50.99,30.64-36.95,58.96-75.75,93.41-109.38,18.38-21.55,39.01-40.97,59.87-60.08,19.79-20.35,41.19-39.04,64.6-55.14,56.77-33,114.64,22.85,151.63,61.96,19,18.02,27.95,43.17,43.96,63.54,29.05,45.32,61.11,89.6,77.96,141.29,10.87,34.96,11.61,74.44-1.56,108.83-21.47,58.49-63.54,106.12-95.98,158.43-8.89,14.45.39,31.84,17.94,29.22,13.99-3.42,24.64-12.79,35.17-22.41,25.76-26.23,36.57-82.91,83.13-76.76,16.2,10.84,26.9,29.12,31.09,48.17,9.5,22.02,12.71,34.68,18.73,68.48,8.7,42.72,1.67,86.8.42,104.78-6.85,20.25-15.5,39.92-21.71,60.47-35.09,71.66-77.19,141.48-138.3,194.14-13.4,11.11-26.47,23.69-43.19,29.52ZM1413.82,910.32c.37-.36.73-.73,1.09-1.09,8.35,2.33,17.23-.16,23.9-5.49,2.37,7.1,5.75,13.85,7.68,21.09-11.8.85-23.83-.49-35.43,2.17-8.65,1.85-16.57,6.16-25.19,8.05,9.55-7.97,19.07-15.99,27.94-24.72ZM1457.36,776.71c1.79,30.73-16.39,57.24-32.95,81.56,7.56-28.42,18.47-55.95,32.95-81.56ZM1408.49,1119.24c-1.89-1.33-3.84-2.57-5.91-3.63-9.28-4.76-19.49-7.22-29.46-10.11,11.35-6.05,22.44-12.54,34.05-18.12-.65,10.65,1.41,21.21,1.31,31.86ZM1073.16,737.59c.12.34.24.68.37,1.01-2.99-6.12-.1-12.56,2.85-18.07,21.48,18.79,53.78,21.24,79.96,11.77-22.32,16.1-39.73,23.92-67.41,16.02-5.99-2.27-12.83-5.51-15.98-11.35.19.56.21.58.22.61ZM1307.12,624.34c14.17-11.75,26.48-25.63,40.94-37,14.47-9.19,25.81-22.18,39.56-32.26-10.14,19.49-26.88,33.41-44.58,45.77-11.32,8.85-22.66,17.74-35.92,23.5ZM1708.32,1014.94c-9.26,21.81-23.1,41.29-33.46,62.59-7.81,13.01-15.95,26-24.46,38.81,14.11-22.98,26.54-46.93,36.06-72.15,11.03-20.71,22.62-41.45,24.85-65.28,2.55-12.85,7.95-25.15,8.62-38.34.64-11.57-.87-23.09-.66-34.66-2.58-22.74-2.47-34.02-5.04-50.9-1.28-16.91-10.25-31.51-15.04-47.4-3.81-25.83-11.73-54.59-31.58-72.71-23.68-19.29-54.09-22.19-77.81-1.4,14.25-23.19,25.48-44.85,33.16-66.27,5.92,7.5,13.11,14.71,20.71,21.8,9.09,13.28,21.54,23.31,33.33,34.01,10.88,12.93,19.23,27.85,29.68,41.17,30.81,49.68,42.79,84.43,34.43,143.36-9.36,36.26-18.84,72.52-32.8,107.36Z"/></g></svg>
\ No newline at end of file
diff --git a/users/aspen/goodcry-band/flower-leaves.svg b/users/aspen/goodcry-band/flower-leaves.svg
new file mode 100644
index 0000000000..5a3b6771a1
--- /dev/null
+++ b/users/aspen/goodcry-band/flower-leaves.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2090.36 1497.03"><defs><style>.cls-1{stroke-width:0px;fill:#b4ada5;}</style></defs><g id="Vector_layer"><path class="cls-1" d="M2086.65,1083.82c6.75-34.75,4.12-70.71-6.56-104.43-7.01-21.07-12.45-43.14-25.14-61.68-8.37-11.01-13.33-23.87-19.38-36.17-6.95-12.59-17.22-22.9-23.7-35.82-22.36-27.25-5.64-36.18,10.27-60.06,12.62-17.89,21.75-38.08,31.35-57.69,7.21-17.5,7.71-36.93,9.93-55.53,2.9-22.14,7.62-44.57,3.14-67.05-4.37-49.67-16.65-100.97-48.47-140.57-22.89-23.98-48.03-50.17-81.75-57.08-9.49-.94-18.99.9-28.51.07-45.26,7.88-17.95-47.07-23.33-71.08-1.65-27.43-20.83-37.4-44.38-45.2-33.74-11.87-72.35-5.44-103.69-24.71-23.83-18.95-33.85-49.82-53.63-72.42-45.01-55.16-114.35-87.21-182.57-103.15-33.33-6.95-67.03-14.33-101.03-16.7-20.78,1.85-41.69-1.38-62.36,1.3-12.32,1.69-23.69,6.97-35.49,10.56-15.42,3.31-29.43,12.01-37.63,25.68-23.24-92.57-93.97-135.83-184.74-98.99-43.02,23.9-70.94,66.66-90.71,110.49-14.77,37.04-28.63,75.44-25.17,116.01,1.48,15.21,5.1,30.14,8.43,45.03,2.9,13.1,14.39,39.74,1.74,49.33-9.04-17.16-8.31-39.9-12.97-58.89-4.11-43.65-1.06-88.69-12.98-131.27-5.74-14.42-12.76-29.1-24.67-39.45-12.74-9.1-28.17-18.33-44.34-17.12-33.62,5.14-53.59,34.38-69.69,61.6-1.26-23.2-8.96-45.9-26.31-61.98-12.63-16.59-32.03-23.12-51.79-26.72-49.48-12.15-102.32,7.01-144.36,32.85-76.23,57.07-123.7,115.51-147.32,209.49-7.36,27.08-8.55,55.91-8.1,83.84,7.78,45.48,26.26,88.34,51.56,126.82-9.17,31.26-12.44,63.94-7.69,96.23-25.76-30.41-75.91-33.91-87.66,10.55-7.74,21.79-14.08,44.46-16.74,67.46,1.02,21.03.03,42.4,4.38,63.1,2.39,10.56,5,21.09,7.88,31.54-16.25,1.4-32.32,4.74-47.47,11.14-26.58,5.04-50.9,16.37-75.64,26.77-46.43,16.02-87.08,44.1-128.39,69.99-45.04,33.44-86.36,72.96-122.33,115.93-2.25,2.43-3.79,5.42-4.64,8.65-5.06,5-5.47,13.88-.14,19.08,1.89,2.01,4.02,3.13,6.26,3.6.17.16.34.32.51.47,0,0-.22-.25-.39-.45,0,0,0,0,.01,0,7.55,8.69,19.12,11.94,29.01,17.21,13.28,5.16,27.84,4.83,41.74,7.19,19.77,4.56,39.7,5.31,59.89,4.32,20.16.8,40.31-.01,60.46-.72,11.86-.36,23.56-2.44,35.38-3.36,21.08-.85,42.24-.99,62.88-5.92,19.77-4.19,39.6-8.13,59.37-12.27,17.73-4.78,35.28-10.31,52.59-16.52-12.32,11.74-24.53,23.57-34.8,37.23-11.17,15.17-25.19,27.98-35.97,43.43-8.63,14.81-20.87,27.83-27.88,43.4-19.79,31.32-35.32,65.95-53.04,98.71-13.39,27.24-33.3,55.57,1.63,75.87,17.05,10.89,38.22,11.96,57.68,15.43,15.61,4.26,31.71,3.54,47.7,2.68,16.49-.74,33,.79,49.48.12,16.08-1.42,31.88-5,48.05-5.87,38.09-4.28,75.86-10.82,113.78-16.02,29.61-9.02,60.24-14.2,89.52-24.29,46.43-13.97,92.09-30.67,135.75-51.83,10.71-5.52,21.5-10.9,32.24-16.37.5-.26.97-.56,1.42-.89,13.5,13.96,27.51,27.46,42.03,40.43,74.73,53.45,165.71,83.12,256.96,91.33-20.97,26.91,3.14,58.65,22.59,78.6,13.61,13.05,29.39,24.44,46.85,31.71,20.27,6.37,40.7,13.11,61.61,17.1,44.78,6.2,90.02,11.06,135.27,8.39,48.53-3.31,97.66-13.22,142.09-33.3,43.1-22.3,85.39-48.03,116.45-86.08,5.95,29.63,35.91,45.57,64.39,44.63,31.53,1.58,79.71-28.3,113.62-52.82,16.24,31.17,81.86-35.35,97.61-51.22,21.83-24.38,46.82-46.08,66.27-72.45,36.43-48.89,67.26-102.25,83.79-161.23ZM1902.44,434.59c8.99.36,17.67-3.74,26.48-.31,37.26,7.71,62.59,39.5,82.85,69.49,17.07,30.96,25.83,66.23,28.24,101.35-.25,13.2-.85,26.37.16,39.56,1.17,21.84-3.79,43.23-10.54,63.86-3.59,16.51-9.8,32.15-18.25,46.55.38-15.29-.86-31.61-3.79-49.31-8.16-51.24-33.12-97.27-48.96-146.17-8.29-24.77-21.09-47.72-31.3-71.72-11.28-19.96-25.53-38.08-41.3-54.71,4.75.99,10.18,1.48,16.41,1.4ZM1284.36,129.39c9.46-8.11,18.85-18.21,31.2-21.51,18.19-2.2,36.31-4.8,54.4-7.81,98.23,5.9,207.68,25.9,280.46,97.46,29.21,28.07,39.32,72.98,75.55,94.15,15.18,8.7,32.78,12.79,49.93,15.52,21.55,1.87,43.25,4.61,63.62,12.28,15.23,4.87,20.64,17.22,18.15,32.24-1.82,18.95-4.13,38.63-.33,54.05-35.11-27.1-76.76-30.06-119.55-7.89-30.1,16.09-60.08,32.54-87.6,52.83-30.96,19.74-61.29,40.2-89.79,63.37-29.09,24.7-57.84,50.47-90.45,70.55-31.58,22.02-67.56,35.55-101.9,52.23-12.54-28.14-42.18-35.1-71.72-34.27,1.17-2.85,2.12-5.99,2.79-9.41,6.74-21.67,3.98-44.55,4.51-66.9.65-19.16-.14-38.3-2.36-57.36-2.12-11.21-.65-21.74,3-32.44,5.02-23.03,11.83-45.85,13.35-69.48.8-19.93,3.62-40.03.22-59.87-2.72-13.91-7.91-27.18-11.99-40.73-3.84-12.08-10.57-22.85-15.82-34.31-4.59-12.29-7.57-25.1-11.51-37.6-8.98-20.75-10.96-47.72,5.86-65.11ZM1357.85,680.02c7.13,18.87,22.98,31.7,36.93,45.4-20.97,2.55-43.99,10.61-64.08.84-.39-.24-.77-.5-1.12-.81-4.29-8.82,2.56-15.43,8.14-21.55,7.05-7.66,14.28-15.23,20.12-23.89ZM1277.54,537.27c-.22,17.68-.02,36.2-4.31,53.31-5.39-12.48,2.95-38.68,4.31-53.31ZM1290.37,368.66c-2.21-15.92-3.94-31.89-5.73-47.86-2.99-18.63-5.71-37.28-8.71-55.9,5.74,13.93,11.37,27.94,12.96,43.06,4,20.09,6.84,40.58,1.48,60.7ZM492.57,478.83c-21.35-39.46-34.89-74.48-35.29-119.89,7.03-82.54,42.34-154.06,102.99-210.38,30.11-27.18,65.48-50.71,105.21-60.61,21.25-6.74,44.35-6.89,65.85-1.19,9.85,4.08,18.39,10.77,28.09,15.23,33.47,22.95,19.92,72.32,13.09,105.76-8.45,32.05-17.47,63.93-26.55,95.81-8.43,23.26-10.71,47.77-14.86,71.98-4.92,32.43-9.06,65.06-8.47,97.92-21.19-27.27-49.78-48.5-81.79-61.4-37.7-19.21-79.12-18.07-104.55,19.46-20.83,8.4-36.67,26.11-43.71,47.32ZM732.35,537.03c17.21,30.97,29.48,65.08,35.71,99.94-27.58-45.13-45.82-94.97-71.94-140.88-6.29-11.73-16.51-20.44-25.92-29.6-11.02-11.51-23.35-21.68-37.37-29.36,43.46,20.77,79.01,56.31,99.52,99.9ZM750.93,432.05c.42,7.69-.08,15.34-1.81,22.85.47-7.63,1.09-15.25,1.81-22.85ZM515.26,498.42c1.89-5.56,3.82-11.09,5.88-16.58-.26,15.16.96,30.34,4.55,45.1-4.85-5.92-9.7-11.12-14-17.86,1.17-3.56,2.37-7.11,3.58-10.66ZM503.08,542.41c15.84,19.69,31.91,39.12,43.62,61.67,12.96,19.84,19.3,43.29,30.91,63.88,16.96,24.01,33.38,48.4,50.06,72.6,9.41,11.78,16.96,26.86,30.71,34.04,21.59,5.34,22.32-22.03,8.84-32.36-11.49-17.53-24.55-34.04-37.04-50.91-19.2-30.42-37.73-61.34-54.78-93.03-7.43-12.68-10.62-26.96-14.53-40.97-9.56-28.96-14.61-60.45-11.68-90.51,1.62-5.06,3.74-9.96,5.93-14.79,34.51-13.34,68.31,7.34,93.69,29.79,13.59,15.01,25.26,31.99,36.4,48.88,18.01,33.14,29.84,69.3,49.1,101.75,8.06,12.65,14.36,28.27,27.57,36.37,11.55,6.78,27.86,2.29,31.65-11.24,10.02-35.96-24.53-104.49-39.53-139.12,10.5-8.1,17.77-20.64,17.65-34.09,2.83-12.24,3.67-24.59,3.69-37.12,4.05-22.95,4.66-46.26,7.09-69.42,2.92-37.05,5.58-74.27,13.97-110.55,2.42-24.71,9.03-48.12,18.7-71.09,12.69-28.44,25.82-59.92,52.84-77.56,63.24-19.99,60.13,80.28,63.29,119.6,6.2,30.48-.05,119.78,40.44,123.18,9.97.98,19.76-3.17,25.54-11.42,16.39-22.4,4.12-50.45.07-74.85-5.74-23.29-9.41-47.67-5.87-71.58,8.65-55.31,36.73-106.92,74.02-148.23,28.57-26.21,75.51-36.49,111.68-21.5,37.76,17.92,57.97,59.37,62.42,99.4,2.5,29.56,5.08,59.05,3.71,88.71,2.81,25.08,6.98,50.08,10.97,75.01,5.47,51.74,13.61,103.15,18.5,154.95-1.73,10.15-5.02,19.87-8.67,29.46-5.67,16.67-8.06,34.23-12.58,51.22-6.43,21.16-8.31,42.9-4.26,64.72,1.14,4.32,2.63,8.04,4.38,11.2-19.95,3.56-38.95-3.16-57.43-10.37-31.52-6.07-78.51-44.71-107.77-22.57-24.56,19.28-11.93,56.24,9.2,73.04,25.32,24.2,75.77,28.52,83.21,67.67,1.74,8.85-3.29,15.86-5.22,24.1-2.67,16.31-3.96,34.73,5.15,49.32,27.19,41.06,65.19,56.92,111.82,37.63,28.59,26.89,1.95,65.77,48.84,94.83,25.1,13.52,51.38-5.7,76.87-6.61,23.71.78,29.65,19.18,6.29,29.81-23.41,11.36-57.12,20.05-57.98,51.71-1.78,26.58,46.29,82.4-5.63,85-15.79.46-38.69-4.77-34.21-25.19,2.73-8.07,6.81-15.94,6.84-24.74.25-14.75-5.22-32.57-20.36-38.04-13.76-3.41-29.75-.25-41.58-9.92-37.94-30.44-11.96-30.61-22.18-67.29-7.53-37.86-55.96-42.75-86.71-32.65-13.68,2.86-25.87,9.49-38.07,16.03-8.88,3.35-16.9,14.65-26.98,10.63-2.77-16.41,27.44-32.75,34.18-52.68,5.26-11.16.83-25.31-9.64-31.72-8.44-4.84-18.85-6.01-25.86-13.32-9.32-18.69-16.95-38.22-22.36-58.41-4.71-21.55-3-46.62-18.33-64.26-10.7-11.52-27.05-18.46-42.75-14.94-9.43,2.11-17.54,7.96-25.23,13.59-9.08,7.82-15.81,18.5-28.45,20.93-13.15,2.87-26.95,1.81-40.29.99-10.88-1.63-21.86-5.38-32.95-3.37-17.74,3.17-34.06,20.26-29.68,39.05,4.66,29.86,32.51,46.58,58.66,56.16,15.07,4.64,31.1,6.35,45.49,13.09,23.9,12.62,52.65,37.05,8.09,45.57-11.3,1.16-22.76-1.06-33.97-2.51-8.32-1.87-15.81-7.7-24.6-6.97-51.58,9.7-33.96,72.83-.97,94.82,22.06,21.39,43.41,21.51,72.15,23.26,19.49,1.21,21.86-1.26,32.89,17.41,7.22,16.71.52,37.51,14.84,51.28,15.72,12.86,38.7,19.29,58.76,15.05,17.72-4.38,31.66-11.56,39.18-28.98,16.29-8.7,33.46-.2,48.01,8.13,15.68,10.07,34.37,18.59,44.17,35.16,21.88,34.13-8.5,23.11-14.94,38.48-40.13-27.77-97.11-20.76-130.89,13.94-10.55,9.83-21.27,19.65-30.15,31.06-10.24,13.87-19.49,29.83-33.35,40.29-17.36,8.3-1.14-53.05-4.59-63.11-1.91-20.22-5.1-40.87-15.44-58.69-8.3-13.11-14.52-27.51-23.55-40.12-7.54-9.55-16.35-17.98-24.47-27.04-29.84-29.9-63.58-58.23-102.8-74.66-33.04-8.23-70.63-16.96-101.97,1.22-4.87,3.8-9.28,8.14-13.19,12.89-6.25-8.42-15.9-14.8-22.25-23.1-51.49-50.39-98.95-104.09-142.02-161.96-27.86-42.99-47.99-90.81-56.73-141.38-3.78-12.65-1.57-25.68-1.05-38.59.21-11.8.74-23.52,3.42-35.06ZM698.4,957.36c-2.18-.74-4.32-1.58-6.33-2.71-35.96-25.68-63.92-61.37-88.18-97.94,13.49,14.56,24.27,31.46,39.43,44.47,17.42,19.62,38.24,36.17,55.08,56.18ZM402.14,669.37c3.17-16.45,5.01-33.44,11.78-48.92,2.27-3.4,7.19-6.66,11.21-8.08-.52.22-1,.42-1.53.65,23.52-2.82,46.72,30.21,53.52,50.21,17.45,29.43,22.73,63.71,39.48,93.38,26.83,56.52,56.45,112.37,98.79,158.88,20.37,24.59,39.34,54.8,71.28,65.01,5.17,1.57,10.67,2.21,15.91,1.87-2.63,11.92-3.05,24.31-1.01,36.23,7.72,47.7,37.35,88.21,65,126.55,9.3,12.66,18.97,25.02,28.96,37.1-1.57-.34-3.14-.65-4.71-.93-9.36-3.47-19.49-8.48-29.54-10.83-12.45-3.98-23.43-11.43-35.5-16.38-22.7-8.77-44.63-18.78-64.86-32.39-13.81-7.49-27.47-15.23-39.83-25.01-33.09-21.6-57.09-53.49-82.11-83.52-11.59-12.04-20.5-26.23-29.42-40.38-13.91-20.15-28.92-39.75-39.69-61.83-6.42-12.94-15.3-24.44-22.38-37-7.74-13.63-12.53-28.66-18.3-43.16-4.89-10.08-9.24-20.38-13.09-30.87,2.65-6.29-.31-13.03-5.6-16.5-6.31-20.32-10.77-41.23-13.38-62.42-3.29-17.46,3.25-34.38,5.02-51.66ZM485.49,1170.78c-10.62,6.91-22.35,11.62-33.91,16.68-2.81-4.53-8.35-7.47-16.36-5.57-9.55,1.34-18.96-1.56-28.4-2.95-16.68-2.15-33.52-2.48-50.19-4.72-7.97-.67-15.91-1.77-23.86-2.76,1.11-2.17,2.21-4.34,3.29-6.52,12.24-17.32,23.9-35.21,36.88-52.11.75-1.23,1.52-2.45,2.31-3.65,7.83,5.39,21.74,6.1,29.88,9.3,20.03,4.07,39.24,10.85,58.6,17.3,17.93,6.21,36.45,11.87,55.07,16.39-11.31,5.81-22.57,11.74-33.32,18.6ZM354.03,1236.28c-24.32,10.51-48.77,20.73-73.14,31.13,11.41-23.7,24.88-46.54,37.71-69.56,12.62,3.62,32.18,2.41,42.37,4.29,16.75,1.24,33.43,3.67,50.16,5.13-19.14,9.45-38.27,18.92-57.09,29.01ZM553.55,1132.27c-3.43-1.19-7.23-1.18-10.85-1.65-6.23-2.02-12.19-4.95-18.7-6.11-27.04-7.53-53.72-16.44-81.21-22.48-17.01-4.25-33.63-9.9-50.56-14.5,6.83-7.59,14.09-14.84,21.25-22.18,8.81-9.25,16.65-19.37,25.85-28.24,15.87-12.32,30.75-25.63,46.8-37.67,1.96-1.46,4.37-2.12,6.66-2.97,3.58,4.66,7.28,9.23,11.07,13.74,13.41,20.67,29.08,39.51,45.29,58.02,8.71,10.71,19.76,19,29.95,28.18,4.1,4.35,8.4,8.46,12.88,12.36-12.82,7.78-25.89,15.23-38.43,23.51ZM342.19,818.11c14.19-1.66,27.63-6.7,41.8-8.04,2.21-.16,4.39-.69,6.56-1.25,7.03,21.13,15.52,41.72,26.07,61.38-7.29,1.15-14.57,2.41-21.83,3.74-.25-.62-.54-1.24-.9-1.84-2.86-5.12-8.81-7.28-13.84-9.71-18.69-13.39-38.81-24.55-58.32-36.69,6.8-2.59,13.58-5.22,20.46-7.59ZM289.22,837.21c5.83,5.81,16,7.92,22.23,13.4,16.33,9.83,31.17,22.14,48.5,30.24-9.4,1.97-18.79,4-28.18,6.05-18.83,5.06-37.65,10.21-56.38,15.64-7.33-3.72-16.42-4.69-23.3-9.57-14.69-6.52-29.95-11.77-45.31-16.59,26.78-14.55,54.09-28.7,82.43-39.17ZM174.88,893.79c1.77-.96,3.54-1.92,5.31-2.89.23.53.49,1.06.8,1.57,4.67,7.61,14.88,7.86,22.27,11.69,10.04,4.03,20.29,7.5,30.49,11.06-17.23,5.54-34.34,11.45-51.26,17.89-27.41,13.65-57.34,21.66-84.3,36.18-10.38,6.06-21.57,10.24-32.66,14.7,34.25-32.72,69.61-64.34,109.35-90.21ZM51.37,1027.44c-2.46-.64-5.17-1.08-7.86-1.65,17.63-8.33,35.09-16.86,52.2-26.29,23.52-8.95,46.38-19.38,69.05-30.24,2.74-.85,5.46-1.73,8.18-2.63-.96,1.17-1.93,2.34-2.89,3.5-11.71,11.84-22.59,24.43-32.94,37.48-7.9,9.75-16.13,19.22-24.19,28.83-.07,0-.13-.01-.2-.02-21.02.87-41.43-2.03-61.34-8.98ZM145.92,1038.74c.09-.08.17-.17.26-.25,11.03-12.59,22.97-24.35,33.68-37.22,13.18-16.29,27.52-31.54,41.37-47.21,2.2-1.84,3.56-4.09,4.23-6.47,8-2.94,16.04-5.79,24.15-8.43,13.11-5.37,26.56-9.65,40.17-13.41-10.85,12.52-19.26,27.04-30.31,39.46-11.22,15.96-22.77,32.17-34.48,48.58-4.76,6.71-12.94,15.51-10.04,23.37-2.58.05-5.17.05-7.76-.02-20.39.92-40.85,2.04-61.26,1.62ZM240.53,1035.08c.52-.76,1.02-1.53,1.49-2.29,6.71-8.33,13.28-16.75,19.88-25.17,15.91-17.43,27.84-37.79,43.09-55.71,5.36-6.27,9.09-13.66,14.15-20.12,4.28-4.51,9.39-9.04,10.53-15.44.03-.15.04-.3.06-.45,13.6-3.13,27.21-6.2,40.68-9.72,8.08-1.47,16.16-2.97,24.24-4.44-2.63,2.8-5.18,5.68-7.42,8.81-16.79,25.69-34.98,50.4-50.85,76.74-4.15,6.02-8.41,12.02-12.91,17.78-5.99,5.44-11.72,13.32-9.11,20.48-12.79,2.01-25.67,3.42-38.63,4.14-11.75,1.78-23.44,3.87-35.2,5.4ZM344.1,1019.77c3.9-5.99,7.43-12.44,11.2-16.21,24.25-29.73,44.11-62.48,66.79-93.31,3.2-3.27,7-6.15,9.16-10.24,12.64,24.47,28.59,47.03,43.58,70.22-8.04,3.71-16.66,9.24-23.71,11.59-34.21,16.18-70.1,29.43-107.02,37.95ZM290.88,1308.2c-4.92-2.62-14.6-3.11-19.2-6.92,4.32-1.86,8.51-3.98,12.09-5.31,16.44-7.16,32.99-14.02,49.33-21.41,11.19-5.6,22.49-10.92,33.89-16.04-10.94,16.54-19.59,34.53-27.35,52.75-.49,1.24-.79,2.43-.96,3.59-16.19-.25-32.15-2.09-47.79-6.67ZM408.16,1314.82c-13.87,1.97-27.78,2.88-41.67,2.4,4.92-11.49,11.56-22.12,17.6-33.06,6.64-10.28,10.62-22.72,19.92-30.97,3.91-3.93,4.67-8.58,3.5-12.67,12.59-6.18,24.97-12.8,37.9-18.3,14.04-7.02,27.74-14.66,41.61-22-2.34,11.28-2.86,22.95-2.92,34.55-1.82,16.59-4.97,32.99-6.99,49.55-.29,8.29-4.59,17-4.41,25.29-21.44,2.67-42.91,4.36-64.53,5.21ZM569.91,1293.19c-22.57,7.18-45.83,9.82-69.16,12.72.52-3.25.95-6.51,1.82-9.4,4.72-19.63,7.7-39.62,8.47-59.8,1.33-12.55,2.49-25.12,3.23-37.71,2.04-4.81,5.54-9.6,5.18-14.84,17.87-9.78,34.03-22.24,52.97-30.08,9.37-5.26,17.14-11.82,26.95-16.92,5.55-3.23,10.05-7.79,15.38-11.22,2.95,1.94,5.95,3.83,8.98,5.65,3.91,2.83,7.92,5.5,12.01,8.02.56,9.76-1.7,19.21-2.61,28.91-.3,20.14-3.57,39.98-5.24,60.03.03,10.89-5.93,42.95-2.29,53.61-18.35,4.72-36.91,8.53-55.71,11.02ZM782.2,1226.39c-31.37,13.54-63.05,26.5-95.59,36.96-12.3,4.33-24.7,8.43-37.21,12.18,1.93-14.68,5.34-29.16,6.66-43.95,1.64-12.47,2.43-25.02,2.92-37.58,1.29-12.7,3.64-25.41,2.45-38.22-.05-.78-.04-1.54,0-2.31,4.13,1.96,8.29,3.85,12.49,5.67,17.8,8.95,34.88,19.43,53.84,25.91,23.57,7.93,44.74,22.04,69.63,26.06,5.92-.11,12.27.53,18.35.07-.64.35-1.27.71-1.88,1.05-10.46,4.94-21.09,9.48-31.66,14.17ZM1173.64,1334.56c-47.4,3.12-95.83-8.4-141.11-21.9-22.23-5.61-43.19-14.89-65-21.84-13.1-5.62-24.73-14.13-37.52-20.44-18.07-11.61-34.55-25.66-51.99-38.21-31.08-29.96-59.57-62.45-85.52-96.93-13.13-17.8-26.27-35.62-36.51-55.29-12.88-20.06-24.73-41.33-28.09-65.29-3.02-26.24,6.39-55.05,31.31-67.44,10.52-3.03,21.86-1.18,32.69-1.29,18.51-.04,34.09,10.79,50.94,16.89,35.29,14.17,62.59,43.28,88.09,70.64,9.19,14.51,22.5,26.19,30.09,41.67,15.67,33.45,17.54,59.51,12.51,95.55-3.63,22.92,6.31,52.17,33.96,49.48,15.46-3.18,25.69-16.88,35.96-27.8,17.35-17.7,29.86-39.92,48.9-55.86,50.11-39.6,101.85-18.5,130.99,33.78,41.7,77.16,5.25,112.47-49.72,164.29ZM1602.74,1384.19c-28.84,20.62-62.76,31.26-96.56,40.52-34.66,12.06-70.81,17.79-106.49,25.7-35.65,4.27-72.21,7.76-107.68.38-22.75-3.17-45.4-6.54-66.97-14.81-11.79-3.38-23.86-6.16-34.74-11.99-27.89-18.76-26.23-36.56-6.37-60.94,13.01-6.45,27.31-6,41.43-5.47,22.39.63,44.92,1.18,66.91,5.8,38.46,5.05,77.33,7.53,116.15,7.51,54.63-2.66,108.62-13.89,160.73-30.28,14.08-4.22,28.17-8.54,41.96-13.66,14.66-3.97,29.83-14.82,45.27-11.72-.18,28.1-33.14,52.85-53.64,68.95ZM1731.37,1395.49c-19.17,2.14-40.19-5.44-44.24-26.19-.46-17.46,4.78-34.72,9.78-51.33,2.76-8.35,1.27-17.35,4.89-25.41,4.12-10.59,3.77-22.07,5.03-33.2,7.76-64.38,39.81-65.9,69.08-109.07,24.54-39.29.76-84.98,8.47-127.36,19.65-22.42,40.23-46.02,46.64-75.99,4.68-17.43,11-42.87-5.71-55.61-12.08-17.82-30.73-31.56-51.46-37.58-18.14-5.95-36.38,2.05-54.66,1.4-13.99.27-27.49,4.51-41.3,6.36-18.05,1.66-35.44,6.41-52.61,12-32.79,9.72-66.25,19.72-97.07,34.22-8.22,6.51-7.39,19.85,1.44,25.41,6.48,3.44,14.35,3.98,21.49,5.23,9.86.53,19.43-2.6,28.81-5.26,14.92-3.94,30.69-5.47,44.54-12.7,31.8-14.08,64.31-27.35,97.5-37.63,21.14-.43,50.47.23,68.01,12.72-26.78,28.21-74.79,88.8-74.94,128.25-19.77,13.62-42.75,21.31-64.49,31.09-17.96,3.49-36.16,6.15-54.11,9.06-9.65,3.19-12.88,16.96-4.8,23.59,14.93,9.26,35.92,3.22,52.88,4.78,18.46.55,36.78-.92,55.11-2.89,8.94.13,17.75,2.28,26.68,2.41,10.26-.1,20.9-1.47,30.18,3.94-.56,28.92-18.22,53.4-35.73,75.06-15.73,9.51-31.86,18.53-48.57,26.21-13.82,6.52-29.3,5.71-44.14,7.54-28.61,4.85-57.34,6.17-86.32,6.05-33.96,1.23-68.02-5.01-101.81-1.17-9.4.09-22.16-1.6-26.86,8.72-4.52,8.78,2.76,19.3,12.11,20.15,8.28-.62,16.27.91,24.68,1.39,26.15,1.18,52.32.16,78.44,2.33,36.31,1.56,150.37-.68,145.16,53.09-19.32-19.38-84.96,12.18-109.68,19-32.26,10.14-65.34,17.18-98.39,24.17-33.31,3.82-67.15,7.45-100.66,5.17-20.31.15-39.85-4.96-59.83-7.65-30.04-1.04-59.92-6.17-90.02-5.04,71.55-63.39,68.07-137.59,7.18-207.38,11.72-5.41,23.15-14.82,28.85-26.67,3.36-12.39-.06-26.75-6.67-37.57-21.56-26.09-47.97-50.5-81.77-58.59-17.59-5.5-38.24-10.63-53.94,2.3-7.52,6.64-10.37,18.11-18.89,23.43-16.19,2.54-42.44,3.91-47.77-15.73-1.57-10.85,1.7-22.43-4.66-32.5-10.17-22.78-35.15-39.78-60.48-34.83-33.63,2.55-78.66-22.71-74.45-60.92,12.88-4.37,27.1,3.85,40.22,5.01,9.69,1.32,19.53,1.39,29.19-.29,50.41-5.84,59.29-51.04,21.8-81.89-17.34-12.24-38.54-17.28-58.06-25.03-21.77-7.17-54.46-13.18-54.99-41.89.52-.26,1.05-.51,1.59-.74,52.49-11.85,56.96,28.63,124.23-30.33,39.39-22.97,34.62,38.34,40.41,61.11,6.11,20.11,14.36,40.25,25.62,58.04,8.23,10.21,20.16,16.64,31.87,22.09-.34,2.44-1.43,4.7-2.92,6.64-10.42,12.67-22.54,24.33-29.89,39.19-13.46,20.91,7.99,51.1,32.08,42.5,39.32-8.87,51.29-33.87,98.17-28.87,19.07,1.02,26.17,18.65,19.12,34.96-1.56,14.46,2.7,30.3,11.53,41.89,11.02,12.86,24.19,26.2,41.15,30.3,31.68,5.93,38.94,1.66,25.63,37.11-4.92,13.66.18,29.99,10.37,39.93,17.15,18.55,52.92,21.85,75.21,11.83,35.2-15.76,26.57-57.48,12.28-85.37-2.92-8.98-8.93-23.01-.79-30.48,26.14-14.64,67.45-22.36,68.23-59.24-2.47-31.4-37.88-46.04-65.58-38.58-30.2,10.74-56.26,19.2-65.05-21.28-1.37-18.25-8.6-35.61-16.54-51.91-7.61-13.32-25.5-19.62-40.07-15.78-6.49,1.55-11.33,6.5-17.59,8.51-16.98.14-33.94-6.72-45.82-18.97-24.2-27.63-13.75-37.77-8.17-68.54,1.21-32.91-25.46-57.95-53.12-71.25-13.83-9.29-29.89-15.22-43.01-25.44-6.4-7.23-10.79-17.39-8.49-27.11,6.42-9.38,25.91,4.03,35.2,5.95,17.5,6.08,33.94,14.38,50.87,21.74,31.28,11.54,65.73,11.02,98.04,4.31,19.14-3.92,74.45.43,53.04,30.82-15.52,20.45-43.15,43.52-35.9,71.81,7.78,17.29,28.63,24.61,46.9,24.27,22.31,1.08,43.42-8.73,65.75-4.91,1.35,1.95,2.66,3.92,3.91,5.95,1.43,2.69,2.82,5.39,4.27,8.06-56.79,58.52,22.21,104.37,79.09,88.36,35.91-9.04,57.02-43.53,90.5-56.66,16.52-5.07,33.46-8.61,50.05-13.43,23.33-5.49,47.03-9.1,68.21-21.07,31.15-12.02,60.42-30.17,84.92-52.83,20-17.86,23.14-56.06-12.88-49.04-25.73,9.06-50.46,21.55-73.04,36.89-12.7,6.49-25.37,13.06-37.44,20.7-17.72,10.87-35,22.42-53.36,32.22-17.17,10.89-34.84,20.52-54.29,26.7-34.14,16.84-88.24,31.73-111.01-9.3,13.21-19.86-5.38-41.76-25.9-44.87-9.44-10.1-19.01-20.08-28.04-30.56-7.86-8.46-17.15-16.6-18.43-28.8,0,0-.15.29-.15.29-.04-.79-.05-1.58-.03-2.37,20-14.89,45.04-21.52,66.91-33.27,21.97-11.34,42.72-24.53,62.55-39.28,37.4-30.08,72.9-62.45,113.38-88.51,22.32-17.44,46.41-32.35,70.67-46.9,23.46-15.98,48.16-30.02,74.32-41.12,62.22-20.99,107.76,45.29,136.2,91.01,15.02,21.45,18.76,47.89,30.38,71.04,19.41,50.21,41.98,100,48.15,154.02,3.66,36.42-3.5,75.26-23.27,106.32-32.73,53.02-83.46,91.28-125.69,136.06-11.6,12.38-5.98,31.28,11.74,32.22,14.39-.56,26.7-7.61,38.94-14.93,30.48-20.56,52.4-73.93,96.79-58.6,13.7,13.86,20.54,33.9,20.84,53.41,4.91,23.47.39,47.35,4.67,70.84.95,17.1-1.47,34.56-3.92,51.46-6.38,16.86-11.79,33.92-16.6,51.29-10.75,18.47-23.17,36.02-33.35,54.91-48.7,63.2-103.9,123.2-174.3,162.6-15.35,8.21-30.67,17.92-48.21,20.3ZM1729.6,1046.57c.43-.28.86-.56,1.29-.85,7.71,3.95,16.92,3.29,24.52-.61.9,7.43,2.87,14.72,3.32,22.2-11.73-1.53-23.25-5.24-35.15-4.95-8.84.09-17.47,2.72-26.29,2.85,10.95-5.9,21.88-11.86,32.32-18.64ZM1798.95,924.36c-4.38,30.47-27.49,52.81-48.58,73.33,13.09-26.33,29.28-51.13,48.58-73.33ZM1682.63,1250.21c-1.58-1.68-3.25-3.28-5.06-4.74-8.14-6.52-17.66-10.97-26.84-15.79,12.33-3.66,24.49-7.81,36.98-10.96-2.77,10.3-2.86,21.07-5.08,31.48ZM1430.32,809.26c.05.36.1.71.16,1.07-1.71-6.59,2.41-12.32,6.4-17.13,17.29,22.7,48.45,31.56,76,27.51-25.09,11.31-43.71,15.5-69.25,2.23-5.41-3.42-11.47-7.97-13.4-14.31.08.58.09.61.09.64ZM1682.19,745.04c16.23-8.69,31.07-19.83,47.51-28.08,16.02-6.11,29.72-16.57,45.21-23.71-13.83,17.08-33.01,27.37-52.83,35.94-12.86,6.41-25.74,12.85-39.89,15.84ZM1997.26,1207.92c-13.43,19.52-30.88,35.85-45.29,54.65-10.26,11.19-20.82,22.28-31.72,33.14,18.42-19.7,35.38-40.68,49.74-63.49,14.94-18.09,30.45-36.1,37.39-59,5.06-12.08,12.81-23.05,16.11-35.84,2.94-11.21,3.76-22.8,6.28-34.09,5.28-16.48,4.38-33.83,5.23-50.88,2.12-16.82-3.75-32.92-5.26-49.45,1.43-26.07-.58-55.84-16.41-77.55-19.35-23.63-48.57-32.55-75.96-16.92,18.6-19.88,33.93-38.86,45.73-58.31,4.31,8.53,9.9,17.03,15.93,25.5,6.25,14.83,16.44,27.14,25.87,39.98,8.07,14.85,13.28,31.13,20.85,46.27,20.26,54.83,25.06,91.27,5.09,147.35-16.41,33.66-32.94,67.29-53.59,98.64Z"/></g></svg>
diff --git a/users/aspen/goodcry-band/flower.png b/users/aspen/goodcry-band/flower.png
new file mode 100644
index 0000000000..b5ad24bac6
--- /dev/null
+++ b/users/aspen/goodcry-band/flower.png
Binary files differdiff --git a/users/aspen/goodcry-band/index.css b/users/aspen/goodcry-band/index.css
new file mode 100644
index 0000000000..ef2aaee882
--- /dev/null
+++ b/users/aspen/goodcry-band/index.css
@@ -0,0 +1,170 @@
+@import "./reset.css";
+
+:root {
+    --gray: #b4ada5;
+    --black: #2f2715;
+    --background: #fff9f4;
+    --blue: #195f9a;
+    --lavender: #5f6eb2;
+}
+
+html {
+    width: 100%;
+}
+
+body {
+    background-color: var(--background);
+    color: var(--black);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    width: 100%;
+    overflow-x: hidden;
+    font-family: "Helvetica", sans-serif;
+}
+
+h1,
+h2,
+subtitle {
+    font-family: "Playfair Display", serif;
+}
+
+header {
+    display: flex !important;
+    flex-direction: column;
+    align-items: center;
+    font-size: 30px;
+    display: inline-block;
+    text-align: center;
+    max-width: 100%;
+    overflow-x: hidden;
+}
+
+@media (min-width: 800px) {
+    header {
+        overflow-x: initial;
+    }
+}
+
+subtitle {
+    display: block;
+    padding-top: 0.5rem;
+    padding-bottom: 2.7rem;
+}
+
+hr {
+    border-top-style: none;
+    border-left-style: none;
+    border-right-style: none;
+    border-bottom: 1px solid var(--gray);
+    width: 35%;
+    margin: 1.4rem 0;
+}
+
+header hr {
+    margin-top: 0;
+    margin-bottom: 2.7rem;
+}
+
+.header-image {
+    width: 75%;
+    max-width: 75%;
+    transform: translateX(4%);
+    margin-top: 1rem;
+}
+
+@media (min-width: 600px) {
+    .header-image {
+        max-width: 600px;
+        margin-bottom: 1.2rem;
+    }
+}
+
+h2 {
+    margin-bottom: 1.4rem;
+    border-bottom: 1px solid var(--gray);
+    padding-bottom: 0.5rem;
+}
+
+.content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+
+a {
+    color: var(--blue);
+    text-decoration: none;
+}
+
+p {
+    text-align: center;
+    max-width: 400px;
+    padding: 0 1rem;
+}
+
+a:active {
+    text-decoration: underline;
+    color: var(--lavender);
+}
+
+.link-list {
+    padding: 0 1rem;
+    list-style: none;
+    font-size: 1.2em;
+    text-align: center;
+}
+
+@media (min-width: 800px) {
+    .link-list {
+        padding: 0;
+    }
+}
+
+.link-list li > * {
+    display: block;
+    padding: 0.4rem 0;
+}
+
+.social-links {
+    margin-top: 3rem;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+}
+
+.social-links > * + * {
+    margin-left: 0.5rem;
+}
+
+.social-links svg {
+    fill: var(--gray);
+}
+
+.social-links a {
+    color: var(--gray);
+    font-size: 24px;
+    vertical-align: middle;
+}
+
+.social-links a:hover {
+    color: var(--blue);
+}
+
+.social-links a:active {
+    color: var(--lavender);
+    text-decoration: none;
+}
+
+.social-links a:hover svg {
+    fill: var(--blue);
+}
+
+.social-links a:active svg {
+    fill: var(--lavender);
+}
+
+footer img {
+    width: 80px;
+    margin: 3rem 0;
+}
diff --git a/users/aspen/goodcry-band/index.html b/users/aspen/goodcry-band/index.html
new file mode 100644
index 0000000000..ea9ec2f654
--- /dev/null
+++ b/users/aspen/goodcry-band/index.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="x-ua-compatible" content="ie=edge" />
+    <title>Good Cry</title>
+    <meta name="description" content="" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <link rel="stylesheet" href="index.css" type="text/css" media="screen" />
+    <link rel="preconnect" href="https://fonts.googleapis.com" />
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
+    <link
+      href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap"
+      rel="stylesheet"
+    />
+  </head>
+  <body>
+    <header class="header">
+      <img class="header-image" src="./flower.png" alt="Flower" />
+      <h1 class="title">Good Cry</h1>
+      <subtitle> just let it all out, baby </subtitle>
+      <hr />
+    </header>
+    <h2>Shows</h2>
+    <ul class="link-list">
+      <li>
+        <a
+          href="https://www.eventbrite.com/e/skylar-pocket-w-leche-malo-good-cry-tickets-836634866407"
+        >
+          February 24th, 2024 <br />
+          @ The Broadway in Brooklyn NY
+        </a>
+      </li>
+    </ul>
+    <div class="content">
+      <hr />
+      <p>
+        Good Cry is rock n roll by way of grief for an unreachable future. Good
+        Cry is songs about the trials and tribulations of NYC trans love and
+        community. Good Cry is the joy in the heartache
+      </p>
+      <hr />
+      <p>
+        DM us
+        <a href="https://www.instagram.com/goodcryband_bk/">on Instagram</a> for
+        booking inquiries
+      </p>
+    </div>
+    <nav class="social-links">
+      <a href="https://www.instagram.com/goodcryband_bk/" title="Instagram">
+        <svg
+          xmlns="http://www.w3.org/2000/svg"
+          width="24"
+          height="24"
+          viewBox="0 0 24 24"
+        >
+          <path
+            d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"
+          />
+        </svg>
+      </a>
+      <a href="mailto:hello@goodcry.band" title="Email"> @ </a>
+    </nav>
+    <footer>
+      <img src="./flower-leaves.svg" alt="Flower" />
+    </footer>
+  </body>
+</html>
diff --git a/users/aspen/goodcry-band/reset.css b/users/aspen/goodcry-band/reset.css
new file mode 100644
index 0000000000..23dcf53d29
--- /dev/null
+++ b/users/aspen/goodcry-band/reset.css
@@ -0,0 +1,45 @@
+*,
+*::before,
+*::after {
+    box-sizing: border-box;
+}
+
+* {
+    margin: 0;
+}
+
+body {
+    line-height: 1.5;
+    -webkit-font-smoothing: antialiased;
+}
+
+img,
+picture,
+video,
+canvas,
+svg {
+    display: block;
+    max-width: 100%;
+}
+
+input,
+button,
+textarea,
+select {
+    font: inherit;
+}
+
+p,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+    overflow-wrap: break-word;
+}
+
+#root,
+#__next {
+    isolation: isolate;
+}
diff --git a/users/grfn/keyboard/.gitignore b/users/aspen/keyboard/.gitignore
index b2be92b7db..b2be92b7db 100644
--- a/users/grfn/keyboard/.gitignore
+++ b/users/aspen/keyboard/.gitignore
diff --git a/users/grfn/keyboard/README.org b/users/aspen/keyboard/README.org
index b085883a10..b085883a10 100644
--- a/users/grfn/keyboard/README.org
+++ b/users/aspen/keyboard/README.org
diff --git a/users/aspen/keyboard/default.nix b/users/aspen/keyboard/default.nix
new file mode 100644
index 0000000000..929ec7d628
--- /dev/null
+++ b/users/aspen/keyboard/default.nix
@@ -0,0 +1,73 @@
+{ pkgs, ... }:
+
+with pkgs;
+
+let avrlibc = pkgsCross.avr.libcCross; in
+
+rec {
+  qmkSource = fetchgit {
+    url = "https://github.com/qmk/qmk_firmware";
+    rev = "ab1650606c36f85018257aba65d9c3ff8ec42e71";
+    sha256 = "1k59flkvhjzmfl0yz9z37lqhvad7m9r5wy1p1sjk5274rsmylh79";
+    fetchSubmodules = true;
+  };
+
+  layout = stdenv.mkDerivation rec {
+    name = "ergodox_ez_grfn.hex";
+
+    src = qmkSource;
+
+    buildInputs = [
+      dfu-programmer
+      dfu-util
+      diffutils
+      git
+      python3
+      pkgsCross.avr.buildPackages.binutils
+      pkgsCross.avr.buildPackages.gcc
+      avrlibc
+      avrdude
+    ];
+
+    AVR_CFLAGS = [
+      "-isystem ${avrlibc}/avr/include"
+      "-L${avrlibc}/avr/lib/avr5"
+      # GCC 12 has improved array-bounds warnings, failing the build of QMK.
+      # Newer versions of the firmware would work probably, but they heavily
+      # altered the build system, so it is non-trivial. Backporting the patch
+      # that fixes it seems difficult – the next change to the offending matrix.c
+      # after the pinned qmkSource commit is
+      # https://github.com/qmk/qmk_firmware/commit/11c308d436180974b7719ce78cdffdd83a1302c0
+      # which heavily changes the way the code works.
+      #
+      # TODO(grfn): address this properly
+      "-Wno-error=array-bounds"
+    ];
+
+    AVR_ASFLAGS = AVR_CFLAGS;
+
+    patches = [ ./increase-tapping-delay.patch ];
+
+    postPatch = ''
+      mkdir keyboards/ergodox_ez/keymaps/grfn
+      cp ${./keymap.c} keyboards/ergodox_ez/keymaps/grfn/keymap.c
+    '';
+
+    buildPhase = ''
+      make ergodox_ez:grfn
+    '';
+
+    installPhase = ''
+      cp ergodox_ez_grfn.hex $out
+    '';
+  };
+
+  flash = writeShellScript "flash.sh" ''
+    ${teensy-loader-cli}/bin/teensy-loader-cli \
+      -v \
+      --mcu=atmega32u4 \
+      -w ${layout}
+  '';
+
+  meta.ci.targets = [ "layout" ];
+}
diff --git a/users/aspen/keyboard/flash b/users/aspen/keyboard/flash
new file mode 100755
index 0000000000..ab6557a44c
--- /dev/null
+++ b/users/aspen/keyboard/flash
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+exec "$(nix-build --no-out-link ../../.. -A users.aspen.keyboard.flash)"
diff --git a/users/grfn/keyboard/increase-tapping-delay.patch b/users/aspen/keyboard/increase-tapping-delay.patch
index 316c435fed..316c435fed 100644
--- a/users/grfn/keyboard/increase-tapping-delay.patch
+++ b/users/aspen/keyboard/increase-tapping-delay.patch
diff --git a/users/aspen/keyboard/keymap.c b/users/aspen/keyboard/keymap.c
new file mode 100644
index 0000000000..741b7b2cfd
--- /dev/null
+++ b/users/aspen/keyboard/keymap.c
@@ -0,0 +1,206 @@
+#include QMK_KEYBOARD_H
+#include "debug.h"
+#include "action_layer.h"
+#include "version.h"
+
+
+#include "keymap_german.h"
+
+#include "keymap_nordic.h"
+
+
+
+enum custom_keycodes {
+  PLACEHOLDER = SAFE_RANGE, // can always be here
+  EPRM,
+  VRSN,
+  RGB_SLD,
+
+  EX_PIPE, // |>
+  THIN_ARROW, // ->
+  FAT_ARROW, // =>
+};
+
+
+
+#define LAMBDA UC(0x03BB)
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+
+  [0] = LAYOUT_ergodox(
+      KC_EQUAL,       KC_1,           KC_2,   KC_3,   KC_4,   KC_5,   KC_LEFT,
+      KC_TAB,         KC_Q,           KC_W,   KC_E,   KC_R,   KC_T,   KC_LALT,
+      KC_ESCAPE,      KC_A,           KC_S,   KC_D,   KC_F,   KC_G,
+      KC_LSFT,        CTL_T(KC_Z),    KC_X,   KC_C,   KC_V,   KC_B,   KC_TAB,
+      LT(1,KC_GRAVE), KC_QUOTE,       LALT(KC_LSHIFT),KC_LEFT,KC_RIGHT,
+                                        ALT_T(KC_APPLICATION),      KC_SPACE,
+                                                                    KC_LBRACKET,
+                                        KC_LGUI, LSFT_T(KC_BSPACE),    KC_COLN,
+
+      KC_MY_COMPUTER, KC_6,   KC_7,   KC_8,       KC_9,       KC_0,               KC_MINUS,
+      KC_RALT,      KC_Y,   KC_U,   KC_I,       KC_O,       KC_P,               KC_BSLASH,
+                    KC_H,   KC_J,   KC_K,       KC_L,       LT(2,KC_SCOLON),    LT(1,KC_QUOTE),
+      KC_MINUS,     KC_N,   KC_M,   KC_COMMA,   KC_DOT,     CTL_T(KC_SLASH),    KC_RSFT,
+                    KC_DOWN,KC_UP,  KC_LBRACKET,KC_RBRACKET,MO(1),
+
+      KC_PAUSE,  TG(3),
+      KC_RBRACKET,
+      KC_COLN,  RSFT_T(KC_ENTER),   KC_SPACE
+   ),
+
+  [1] = LAYOUT_ergodox(
+      KC_ESCAPE,        KC_F1,          KC_F2,          KC_F3,          KC_F4,      KC_F5,          KC_TRANSPARENT,
+      KC_TRANSPARENT,   KC_EXLM,        KC_AT,          KC_LCBR,        KC_RCBR,    KC_PIPE,        KC_RABK,
+      KC_TRANSPARENT,   KC_HASH,        KC_DLR,         KC_LPRN,        KC_RPRN,    KC_UNDERSCORE,
+      KC_LABK,          KC_PERC,          KC_CIRC,        KC_LBRACKET,    KC_RBRACKET,    KC_TILD,    KC_TRANSPARENT,
+      KC_TRANSPARENT,   KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+                                                        RGB_MOD,  KC_TRANSPARENT,
+                                                                  KC_TRANSPARENT,
+                                                        RGB_VAD,    RGB_VAI, EX_PIPE,
+
+      KC_TRANSPARENT,   KC_F6,          KC_F7,          KC_F8,          KC_F9,      KC_F10,         KC_F11,
+      KC_PGUP,          KC_UP,          KC_7,           KC_8,           KC_9,       KC_ASTR,        KC_F12,
+                        KC_DOWN,        KC_4,           KC_5,           KC_6,       KC_PLUS,        KC_TRANSPARENT,
+      KC_PGDOWN,        KC_AMPR,        KC_1,           KC_2,           KC_3,       KC_BSLASH,      KC_TRANSPARENT,
+                                        KC_TRANSPARENT, KC_DOT,         KC_0,       KC_EQUAL,       KC_TRANSPARENT,
+      RGB_TOG,          RGB_SLD,
+      THIN_ARROW,
+      EX_PIPE,          RGB_HUD,    RGB_HUI
+  ),
+
+  [2] = LAYOUT_ergodox(
+      KC_SCROLLLOCK,  KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_UP,       KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_LEFT,     KC_MS_DOWN,     KC_MS_RIGHT,    KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_BTN1,     KC_MS_BTN2,
+                                                       KC_TRANSPARENT,                 KC_TRANSPARENT,
+                                                                                       KC_TRANSPARENT,
+                                                       KC_MS_BTN1,     KC_MS_BTN2,     KC_TRANSPARENT,
+
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,    KC_TRANSPARENT,      KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,    KC_TRANSPARENT,      KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
+                      KC_TRANSPARENT, KC_MS_WH_DOWN,     KC_MS_WH_UP,         KC_TRANSPARENT,      KC_TRANSPARENT, KC_MEDIA_PLAY_PAUSE,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,    KC_MEDIA_PREV_TRACK, KC_MEDIA_NEXT_TRACK, KC_TRANSPARENT, KC_TRANSPARENT,
+                                      KC_AUDIO_VOL_DOWN, KC_AUDIO_VOL_UP,     KC_AUDIO_MUTE,       KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_WWW_BACK),
+
+  // FPS layout
+  [3] = LAYOUT_ergodox(
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+                                                      KC_TRANSPARENT,           KC_TRANSPARENT,
+                                                                                KC_TRANSPARENT,
+                                                      KC_SPACE, KC_TRANSPARENT, KC_TRANSPARENT,
+
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
+                      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
+                                      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_TRANSPARENT, TG(3),
+      KC_TRANSPARENT,
+      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT),
+};
+
+const uint16_t PROGMEM fn_actions[] = {
+  [1] = ACTION_LAYER_TAP_TOGGLE(1)
+};
+
+// leaving this in place for compatibilty with old keymaps cloned and re-compiled.
+const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
+{
+      switch(id) {
+        case 0:
+        if (record->event.pressed) {
+          SEND_STRING (QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION);
+        }
+        break;
+      }
+    return MACRO_NONE;
+};
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+  switch (keycode) {
+    // dynamically generate these.
+    case EPRM:
+      if (record->event.pressed) {
+        eeconfig_init();
+      }
+      return false;
+      break;
+    case VRSN:
+      if (record->event.pressed) {
+        SEND_STRING (QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION);
+      }
+      return false;
+      break;
+    case RGB_SLD:
+      if (record->event.pressed) {
+        rgblight_mode(1);
+      }
+      return false;
+      break;
+    case EX_PIPE:
+      if (record->event.pressed) {
+        SEND_STRING ( "|> " );
+      }
+      return false;
+      break;
+    case THIN_ARROW:
+      if (record->event.pressed) {
+        SEND_STRING ( "-> " );
+      }
+      return false;
+      break;
+
+
+  }
+  return true;
+}
+
+void matrix_scan_user(void) {
+
+    uint8_t layer = biton32(layer_state);
+
+    ergodox_board_led_off();
+    ergodox_right_led_1_off();
+    ergodox_right_led_2_off();
+    ergodox_right_led_3_off();
+    switch (layer) {
+        case 1:
+            ergodox_right_led_1_on();
+            break;
+        case 2:
+            ergodox_right_led_2_on();
+            break;
+        case 3:
+            ergodox_right_led_3_on();
+            break;
+        case 4:
+            ergodox_right_led_1_on();
+            ergodox_right_led_2_on();
+            break;
+        case 5:
+            ergodox_right_led_1_on();
+            ergodox_right_led_3_on();
+            break;
+        case 6:
+            ergodox_right_led_2_on();
+            ergodox_right_led_3_on();
+            break;
+        case 7:
+            ergodox_right_led_1_on();
+            ergodox_right_led_2_on();
+            ergodox_right_led_3_on();
+            break;
+        default:
+            break;
+    }
+
+};
diff --git a/users/grfn/keys.nix b/users/aspen/keys.nix
index 29d5a3fa63..29d5a3fa63 100644
--- a/users/grfn/keys.nix
+++ b/users/aspen/keys.nix
diff --git a/users/grfn/org-clubhouse/.gitignore b/users/aspen/org-clubhouse/.gitignore
index 2a7dd97deb..2a7dd97deb 100644
--- a/users/grfn/org-clubhouse/.gitignore
+++ b/users/aspen/org-clubhouse/.gitignore
diff --git a/users/grfn/org-clubhouse/CODE_OF_CONDUCT.org b/users/aspen/org-clubhouse/CODE_OF_CONDUCT.org
index f15e387d54..f15e387d54 100644
--- a/users/grfn/org-clubhouse/CODE_OF_CONDUCT.org
+++ b/users/aspen/org-clubhouse/CODE_OF_CONDUCT.org
diff --git a/users/grfn/org-clubhouse/LICENSE b/users/aspen/org-clubhouse/LICENSE
index 1777f0fac3..1777f0fac3 100644
--- a/users/grfn/org-clubhouse/LICENSE
+++ b/users/aspen/org-clubhouse/LICENSE
diff --git a/users/grfn/org-clubhouse/README.org b/users/aspen/org-clubhouse/README.org
index 9cd8fbe892..9cd8fbe892 100644
--- a/users/grfn/org-clubhouse/README.org
+++ b/users/aspen/org-clubhouse/README.org
diff --git a/users/grfn/org-clubhouse/org-clubhouse.el b/users/aspen/org-clubhouse/org-clubhouse.el
index e6e29b5751..e6e29b5751 100644
--- a/users/grfn/org-clubhouse/org-clubhouse.el
+++ b/users/aspen/org-clubhouse/org-clubhouse.el
diff --git a/users/aspen/pkgs/cargo-hakari.nix b/users/aspen/pkgs/cargo-hakari.nix
new file mode 100644
index 0000000000..b6f4e7e400
--- /dev/null
+++ b/users/aspen/pkgs/cargo-hakari.nix
@@ -0,0 +1,27 @@
+{ pkgs, ... }:
+
+with pkgs;
+
+rustPlatform.buildRustPackage rec {
+  pname = "cargo-hakari";
+  version = "0.9.13";
+
+  src = fetchFromGitHub {
+    owner = "facebookincubator";
+    repo = "cargo-guppy";
+    rev = "cargo-hakari-${version}";
+    sha256 = "11ds2zryxdd6rvszkpphb0xnfg7rqisg6kixrwyiydjrm5rdjg9d";
+  };
+
+  cargoSha256 = "0b2hjyak5v4m3g5zjk2q8bdb4iv3015qw1rmhpclv4cv48lcmdbb";
+
+  buildAndTestSubdir = "tools/cargo-hakari";
+
+  nativeBuildInputs = [
+    pkg-config
+  ];
+
+  buildInputs = [
+    openssl
+  ];
+}
diff --git a/users/aspen/pkgs/cargo-nextest.nix b/users/aspen/pkgs/cargo-nextest.nix
new file mode 100644
index 0000000000..dbf3bd7eef
--- /dev/null
+++ b/users/aspen/pkgs/cargo-nextest.nix
@@ -0,0 +1,27 @@
+{ pkgs, ... }:
+
+with pkgs;
+
+rustPlatform.buildRustPackage rec {
+  pname = "cargo-nextest";
+  version = "0.9.36";
+
+  src = fetchFromGitHub {
+    owner = "nextest-rs";
+    repo = "nextest";
+    rev = "cargo-nextest-${version}";
+    sha256 = "1g40r38bqmdhc0dy07pj27vkc64d3fw6v5z2vwn82xld2h9dg7w2";
+  };
+
+  cargoSha256 = "1g862azgkn3xk3v3chs8hv1b1prj1pq2vfzbhcx6ir9l00kv6gcv";
+
+  cargoTestFlags = [
+    "--"
+    "--skip"
+    "tests_integration::test_relocated_run"
+    "--skip"
+    "tests_integration::test_run"
+    "--skip"
+    "tests_integration::test_run_after_build"
+  ];
+}
diff --git a/users/aspen/pkgs/notmuch-extract-patch.nix b/users/aspen/pkgs/notmuch-extract-patch.nix
new file mode 100644
index 0000000000..7f00f925ec
--- /dev/null
+++ b/users/aspen/pkgs/notmuch-extract-patch.nix
@@ -0,0 +1,18 @@
+{ pkgs, ... }:
+
+let
+  inherit (pkgs) lib;
+  src = pkgs.fetchurl {
+    url = "https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch";
+    sha256 = "0nawkl04sj7psw6ikzay7kydj3dhd0fkwghcsf5rzaw4bmp4kbax";
+  };
+
+in
+pkgs.runCommand "notmuch-extract-patch"
+{
+  buildInputs = [ pkgs.python3 ];
+} ''
+  mkdir -p $out/bin
+  install -m 755 ${src} $out/bin/notmuch-extract-patch
+  patchShebangs $out/bin
+''
diff --git a/users/aspen/pkgs/py3status.nix b/users/aspen/pkgs/py3status.nix
new file mode 100644
index 0000000000..89f52d9674
--- /dev/null
+++ b/users/aspen/pkgs/py3status.nix
@@ -0,0 +1,12 @@
+{ pkgs, ... }:
+
+pkgs.python3Packages.py3status.overridePythonAttrs (old: rec {
+  name = "${pname}-${old.version}";
+  pname = "py3status-glittershark";
+  src = pkgs.fetchFromGitHub {
+    owner = "glittershark";
+    repo = "py3status";
+    rev = "f243be1458cdabd5a7524adb76b5db99006c810c";
+    sha256 = "0ffmv91562yk0wigriw4d5nfg2b32wqx8x78qvdqkawzvgbwrwvl";
+  };
+})
diff --git a/users/grfn/resume/chimera.png b/users/aspen/resume/chimera.png
index 6dde989c53..6dde989c53 100644
--- a/users/grfn/resume/chimera.png
+++ b/users/aspen/resume/chimera.png
Binary files differdiff --git a/users/grfn/resume/collection.sty b/users/aspen/resume/collection.sty
index 4f1540a9d2..4f1540a9d2 100644
--- a/users/grfn/resume/collection.sty
+++ b/users/aspen/resume/collection.sty
diff --git a/users/aspen/resume/default.nix b/users/aspen/resume/default.nix
new file mode 100644
index 0000000000..4454e74c82
--- /dev/null
+++ b/users/aspen/resume/default.nix
@@ -0,0 +1,40 @@
+{ pkgs, ... }:
+
+with pkgs.lib;
+
+pkgs.runCommand "resume.pdf"
+{
+  buildInputs = [
+    (pkgs.texlive.combine {
+      inherit (pkgs.texlive)
+        capt-of
+        collection-fontsrecommended
+        enumitem
+        etoolbox
+        fancyvrb
+        float
+        fncychap
+        framed
+        l3packages
+        microtype
+        needspace
+        parskip
+        scheme-basic
+        tabulary
+        titlesec
+        ulem
+        upquote
+        varwidth
+        wrapfig
+        xcolor
+        ;
+    })
+  ];
+} ''
+  cp ${builtins.filterSource (path: type:
+    type == "regular" &&
+    any (ext: hasSuffix ext path) [".sty" ".cls" ".tex" ".png"]
+  ) ./.}/* .
+  pdflatex ./resume.tex
+  cp resume.pdf $out
+''
diff --git a/users/grfn/resume/helvetica.sty b/users/aspen/resume/helvetica.sty
index dacc129a10..dacc129a10 100644
--- a/users/grfn/resume/helvetica.sty
+++ b/users/aspen/resume/helvetica.sty
diff --git a/users/grfn/resume/moderncv.cls b/users/aspen/resume/moderncv.cls
index a40f807337..3248907133 100644
--- a/users/grfn/resume/moderncv.cls
+++ b/users/aspen/resume/moderncv.cls
@@ -219,6 +219,7 @@
 % defines one's name

 % usage: \name{<firstname>}{<lastname>}

 \newcommand*{\name}[2]{\def\@firstname{#1}\def\@lastname{#2}}

+\newcommand*{\pronouns}[1]{\def\@pronouns{#1}}

 % defines one's title (optional)

 % usage: \title{<title>}

 \renewcommand*{\title}[1]{\def\@title{#1}}

@@ -284,7 +285,7 @@
 % usage: \moderncvstyle{<style variant name>}

 \newcommand*{\moderncvstyle}[1]{

   \RequirePackage{moderncvstyle#1}}

-  

+

 % loads a color scheme

 % usage: \moderncvcolor{<color scheme name>}

 \newcommand*{\moderncvcolor}[1]{

diff --git a/users/grfn/resume/moderncvcolorblack.sty b/users/aspen/resume/moderncvcolorblack.sty
index 3a6e1477f3..3a6e1477f3 100644
--- a/users/grfn/resume/moderncvcolorblack.sty
+++ b/users/aspen/resume/moderncvcolorblack.sty
diff --git a/users/grfn/resume/moderncvcolorblue.sty b/users/aspen/resume/moderncvcolorblue.sty
index 7b949c704a..7b949c704a 100644
--- a/users/grfn/resume/moderncvcolorblue.sty
+++ b/users/aspen/resume/moderncvcolorblue.sty
diff --git a/users/grfn/resume/moderncvcolorgreen.sty b/users/aspen/resume/moderncvcolorgreen.sty
index 4de7f848a0..4de7f848a0 100644
--- a/users/grfn/resume/moderncvcolorgreen.sty
+++ b/users/aspen/resume/moderncvcolorgreen.sty
diff --git a/users/grfn/resume/moderncvcolorgrey.sty b/users/aspen/resume/moderncvcolorgrey.sty
index 9018726a23..9018726a23 100644
--- a/users/grfn/resume/moderncvcolorgrey.sty
+++ b/users/aspen/resume/moderncvcolorgrey.sty
diff --git a/users/grfn/resume/moderncvcolororange.sty b/users/aspen/resume/moderncvcolororange.sty
index 134ae24011..134ae24011 100644
--- a/users/grfn/resume/moderncvcolororange.sty
+++ b/users/aspen/resume/moderncvcolororange.sty
diff --git a/users/grfn/resume/moderncvcolorpurple.sty b/users/aspen/resume/moderncvcolorpurple.sty
index d3dc5345b0..d3dc5345b0 100644
--- a/users/grfn/resume/moderncvcolorpurple.sty
+++ b/users/aspen/resume/moderncvcolorpurple.sty
diff --git a/users/grfn/resume/moderncvcolorred.sty b/users/aspen/resume/moderncvcolorred.sty
index 681181997d..681181997d 100644
--- a/users/grfn/resume/moderncvcolorred.sty
+++ b/users/aspen/resume/moderncvcolorred.sty
diff --git a/users/grfn/resume/moderncvcompatibility.sty b/users/aspen/resume/moderncvcompatibility.sty
index 1fc53f2180..1fc53f2180 100644
--- a/users/grfn/resume/moderncvcompatibility.sty
+++ b/users/aspen/resume/moderncvcompatibility.sty
diff --git a/users/grfn/resume/moderncviconsletters.sty b/users/aspen/resume/moderncviconsletters.sty
index 0a4e2864be..0a4e2864be 100644
--- a/users/grfn/resume/moderncviconsletters.sty
+++ b/users/aspen/resume/moderncviconsletters.sty
diff --git a/users/grfn/resume/moderncviconsmarvosym.sty b/users/aspen/resume/moderncviconsmarvosym.sty
index eb1b1ec727..eb1b1ec727 100644
--- a/users/grfn/resume/moderncviconsmarvosym.sty
+++ b/users/aspen/resume/moderncviconsmarvosym.sty
diff --git a/users/grfn/resume/moderncvstylebanking.sty b/users/aspen/resume/moderncvstylebanking.sty
index fb0b70fdcd..fb0b70fdcd 100644
--- a/users/grfn/resume/moderncvstylebanking.sty
+++ b/users/aspen/resume/moderncvstylebanking.sty
diff --git a/users/grfn/resume/moderncvstylecasual.sty b/users/aspen/resume/moderncvstylecasual.sty
index e375e7612a..f8cf856d1a 100644
--- a/users/grfn/resume/moderncvstylecasual.sty
+++ b/users/aspen/resume/moderncvstylecasual.sty
@@ -64,6 +64,7 @@
 % fonts

 \renewcommand*{\namefont}{\fontsize{38}{40}\mdseries\upshape}

 \renewcommand*{\addressfont}{\normalsize\mdseries\slshape}

+\newcommand*{\pronounsfont}{\fontsize{18}{40}\mdseries\upshape}

 

 % commands

 \renewcommand*{\makecvtitle}{%

@@ -86,7 +87,7 @@
   \@initializelength{\makecvtitlepicturewidth}%

   \settowidth{\makecvtitlepicturewidth}{\usebox{\makecvtitlepicturebox}}%

   \parbox[b]{\textwidth-\makecvtitlepicturewidth}{%

-    \raggedleft\namefont{\color{color2!50}\@firstname} {\color{color2}\@lastname}}\\[-.35em]% alternate design: \MakeLowercase and no space

+    \raggedleft\namefont{\color{color2!50}\@firstname} {\color{color2}\@lastname} {\color{color2!50}\pronounsfont{\@pronouns}}}\\[-.35em]% alternate design: \MakeLowercase and no space

   {\color{color2!50}\rule{\textwidth}{.25ex}}%

   % optional title

   \ifthenelse{\equal{\@title}{}}{}{\\[1.25em]\null\hfill\titlestyle{\@title}}\\[2.5em]% \null is required as there is no box on the line after \\, so glue (and leaders) disappears; this is in contrast to after \par, where the next line starts with an indent box (even after \noindent).

diff --git a/users/grfn/resume/moderncvstyleclassic.sty b/users/aspen/resume/moderncvstyleclassic.sty
index 63cf97aa3b..63cf97aa3b 100644
--- a/users/grfn/resume/moderncvstyleclassic.sty
+++ b/users/aspen/resume/moderncvstyleclassic.sty
diff --git a/users/grfn/resume/moderncvstyleempty.sty b/users/aspen/resume/moderncvstyleempty.sty
index 85932464d1..85932464d1 100644
--- a/users/grfn/resume/moderncvstyleempty.sty
+++ b/users/aspen/resume/moderncvstyleempty.sty
diff --git a/users/grfn/resume/moderncvstyleoldstyle.sty b/users/aspen/resume/moderncvstyleoldstyle.sty
index ff732f4e2a..ff732f4e2a 100644
--- a/users/grfn/resume/moderncvstyleoldstyle.sty
+++ b/users/aspen/resume/moderncvstyleoldstyle.sty
diff --git a/users/grfn/resume/picture.png b/users/aspen/resume/picture.png
index 63b21b5320..63b21b5320 100644
--- a/users/grfn/resume/picture.png
+++ b/users/aspen/resume/picture.png
Binary files differdiff --git a/users/aspen/resume/resume.tex b/users/aspen/resume/resume.tex
new file mode 100644
index 0000000000..fb226c4ddf
--- /dev/null
+++ b/users/aspen/resume/resume.tex
@@ -0,0 +1,252 @@
+%% start of file `template.tex'.
+%% Copyright 2006-2013 Xavier Danaux (xdanaux@gmail.com).
+%% Copyright 2014-2023 Griffin Smith (root@gws.fyi).
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License version 1.3c,
+% available at http://www.latex-project.org/lppl/.
+
+
+\documentclass[10pt,a4paper,sans]{moderncv}        % possible options include font size ('10pt', '11pt' and '12pt'), paper size ('a4paper', 'letterpaper', 'a5paper', 'legalpaper', 'executivepaper' and 'landscape') and font family ('sans' and 'roman')
+
+\usepackage[inline]{enumitem}
+
+
+% moderncv themes
+% style options are 'casual' (default), 'classic', 'oldstyle' and 'banking'
+\moderncvstyle{casual}
+% color options 'blue' (default), 'orange', 'green', 'red', 'purple', 'grey' and 'black'
+\moderncvcolor{black}
+% to set the default font; use '\sfdefault' for the default sans serif font,
+% '\rmdefault' for the default roman one, or any tex font name
+%\renewcommand{\familydefault}{\sfdefault}
+\nopagenumbers{}
+
+\usepackage[utf8]{inputenc}
+
+\usepackage[scale=0.8, margin=0.65in]{geometry}
+\setlength{\hintscolumnwidth}{2.6cm}
+
+\name{Aspen}{Smith}
+\pronouns{she/her}
+\title{Software Engineer}
+\phone[mobile]{(720) 206-7218}
+\email{aspen@gws.fyi}
+\homepage{gws.fyi}
+\extrainfo{she/her}
+
+
+\begin{document}
+\makecvtitle{}
+\section{Skills}
+\cvitem{Rust}{Expertise in high-performance, low latency, low-level systems
+development with Rust, including everything from fundamental data structure
+implementation to asynchronous distributed systems development}
+\cvitem{Clojure}{Extensive experience architecting, deploying, and building
+complex web applications in Clojure and Clojurescript, with a focus on
+Re-Frame and Reagent. Experience testing distributed systems in Clojure using
+Jepsen.}
+\cvitem{Haskell}{Passionate love for pure functional programming as a hobbyist
+pursuit, but also practical experience building production systems in Haskell at
+scale, and using Haskell's advanced type system extensions where appropriate to
+deliver increased ergonomics and safety.}
+\cvitem{Nix}{Experience with adopting and teaching nix at scale in a production
+stack both for local development dependencies and for configuring and building
+production software. Core contributer to a fork of the nix implementation itself
+(tvix) aimed at providing increased safety, performance, and flexibility.}
+\cvitem{Unix/Linux}{Experience with administrating highly available distributed
+systems. Passion for the Unix philosophy of discrete, composable units of
+functionality.}
+\cvitem{Ruby}{Experience building both full-stack applications with Ruby on
+Rails in addition to smaller microservices and custom frameworks. Deep
+understanding of the internals of the Ruby interpreter and object system.}
+\cvitem{Javascript}{Experience developing real-time responsive single-page web
+applications using React, in addition to significant contributions to the React
+open-source community.}
+\cvitem{SQL}{Deep understanding of relational databases as an
+implementer, in the context of an innovative new database implementing a query
+planner and incremental materialization for the PostgreSQL and MySQL dialects of
+SQL from the ground up -- and of course also a user}
+
+\subsection{Additional Tools}
+\cvitem{}{\footnotesize
+    \begin{itemize*}
+        \item Vim
+        \item Emacs (yes, also)
+        \item Kubernetes
+        \item Git
+        \item Terraform
+        \item AWS
+        \item GCP
+        \item Datomic
+        \item Elasticsearch
+        \item Redis
+        \item Docker
+        \item Java
+        \item Scala
+        \item QuickCheck (and similar tools)
+        \item Jepsen
+        \item Python
+        \item Elixir
+    \end{itemize*}
+    \newline
+    \textbf{Novice Level:}
+    \begin{itemize*}
+        \item C++
+        \item Erlang
+        \item Prolog
+        \item Idris
+        \item Agda
+        \item Tensorflow
+    \end{itemize*}}
+
+\section{Experience}
+\cventry{2020--2023}{Staff Software Engineer}{ReadySet}{Remote}{}
+{Founding engineer at a startup bringing a high performance
+  partially-stateful, incrementally-maintained SQL database based on the Noria
+  thesis to market
+  \begin{itemize}
+    \item Served as the main technical leadership for the project throughout its
+          maturation from a research codebase to a production-grade system
+    \item Extended the Noria PhD thesis by implementing methods from multiple
+          research papers, masters theses, and other papers from database
+          research, in addition to original database research and development.
+    \item Invented or helped develop multiple novel database techniques in
+          partially materialized dataflow, including index planning and
+          selection, pagination, post-lookup aggregate processing, partial
+          ``straddled'' joins, weak indexes for correct execution of partial
+          joins, and more.
+    \item Invented novel ways to test SQL databases, including a new deterministic
+          generator for SQL queries.
+    \item Developed the clustered high availability distributed runtime mode from
+          a buggy research feature into a production ready distributed system
+          that passed a suite of Jepsen tests.
+    \item Implemented a significant fraction of the SQL query planner, which
+          required both implementing algorithms specified in database research
+          papers and inventing new techniques to work around the limitations of
+          partially materialized dataflow
+    \item Optimized critical components of the code base, including algorithmic
+          optimizations, CPU cache analysis, low-level data structures, and
+          broad system runtime analysis
+    \item Implemented a type inference engine and expression evaluator that
+          supported multiple dialects of SQL configured at compile-time, with
+          maximum code reuse while preserving maintainability
+    \item Mentored multiple junior and senior engineers
+    \item Open-Source contributions visible at
+          \url{https://github.com/readysettech/readyset/commits?author=glittershark}
+  \end{itemize}}
+\cventry{2019--2020}{Engineering Manager}{Urbint}{New York, NY}{}
+{\begin{itemize}
+   \item Lead of the platform team with two direct reports - a senior SRE and
+     a senior software engineer.
+   \item Performed user research on developers, project managers, product
+     managers, and other internal stakeholders to build the roadmap for the
+     platform team.
+   \item Built and maintained a system to deploy one-off full stack
+     application instances from pull requests to enable easier testing.
+   \item Led a large, multi-project migration between CI systems that resulted
+     in a decrease of average build times from 2 hours to less than 10 minutes.
+   \item Maintained and extended Nix-based build and development
+     infrastructure for both software engineers and machine learning engineers.
+ \end{itemize}}
+\cventry{2018--2019}{Senior Software Engineer}{Urbint}{New York, NY}{}
+{\begin{itemize}
+   \item Built, trained, and maintained a large, deep-learning-based
+     image-detection model for semi-automated (human-in-the-loop) video
+     classification.
+   \item Designed, built, and maintained a novel in-house tool for collection of
+     training data.
+   \item Maintained and guaranteed reliability of a large data pipeline for
+     video processing and classification.
+ \end{itemize}}
+\cventry{2017--2018}{Senior Software Engineer}{Urbint}{New York, NY}{}
+{\begin{itemize}
+   \item Integral in the architecture of a novel, serializable ACID
+     transactional graph database built on RocksDB, first in Elixir then in
+     Haskell.
+   \item Helped ship customer deliverables involving multi-day data
+     processing jobs for disparate data sources.
+   \item Instructed other developers in the use of and theory behind Haskell
+   \item Brought computational graph theory to bear on the problem of unifying
+     disparate, highly heterogeneous data sources across the world of open data.
+ \end{itemize}}
+\cventry{2016--2017}{Senior Software Engineer}{SecurityScorecard, Inc.}{New York, NY}{}
+{Lead frontend developer for a rapidly-moving and growing security software startup.
+  \begin{itemize}
+    \item Took part in collaborative product design meetings to make UX
+      tradeoffs with product designers and managers.
+    \item Drove application architecture for a large, complex, data-driven frontend
+      application.
+    \item Championed increased use of production monitoring and alerting.
+    \item Worked with business stakeholders to set long- and short-term priorities for
+      application development.
+    \item Mentored junior team members.
+  \end{itemize}}
+\cventry{2015--2016}{Lead Developer}{Nomi, Inc.}{New York, NY}{}
+{Lead web services developer transitioning to a full-stack role implementing
+  shared software components and architecting a large, complex microservices
+  application ingesting hundreds of gigabytes of IoT data per week.
+  \begin{itemize}
+    \item Lead application architecture of the majority of the backend services to
+      encourage consistent REST API design and code sharing.
+    \item Championed the use of Haskell for rapid, safe development of the API Gateway
+      service.
+    \item Took ownership of operations and server maintenance of a >100-instance AWS
+      account using Puppet.
+  \end{itemize}}
+\cventry{2014--2015}{Lead Developer}{LandlordsNY, LLC}{New York, NY}{}
+{Sole engineer for a small startup connecting landlords and property managers and
+  facilitating the online sharing of information in a historically technology-averse
+  industry.
+  \begin{itemize}
+    \item Drove product design, visual design, and UX architecture for a major revamping
+      of the core product.
+    \item Interfaced with customers to set priorities for new feature development.
+    \item Conducted hiring and recruiting to build out an engineering team.
+  \end{itemize}}
+\cventry{2012--2014}{Associate Developer}{Visionlink Inc.}{Boulder, CO}{}
+{Integral member of an agile development team building the nation's most-used Information
+  and Referral platform for organizations such as United Way Worldwide and the American Red
+  Cross.
+  \begin{itemize}
+    \item Refactored and revamped legacy code to increase performance and long-term
+      maintainablity.
+    \item Worked on several triage-teams to rapidly fix production bugs with strict deadlines.
+    \item Built a complex, yet highly-performant tool for searching human services by category.
+    \item Acted as a core designer and developer of a major product revamp.
+      \begin{itemize}
+        \item Drove a complete rethinking of the data model in the product, leading to greater
+          unification, simplicity, and consistency;
+        \item Championed the adoption of a test-driven-development model;
+        \item Drove product documentation and code standardization.
+      \end{itemize}
+  \end{itemize}}
+
+\section{Project Highlights}
+\newcommand{\project}[3]{\item \textbf{#1} -- \textit{#2}\newline{}#3}
+\cvitem{}{
+  \begin{itemize}
+    \project{How much does Rust's bounds checking actually cost?}
+    {\url{https://blog.readyset.io/bounds-checks/}}{Blog post providing a deep
+        evaluation of the runtime cost of bounds checking in safe languages like Rust.
+        Front page of Hacker News, doubled month-over-month ReadySet waitlist signups}
+    \project{Tvix}{\url{https://cs.tvl.fyi/depot/-/blob/third\_party/nix/README.md}}{
+        Fork of the Nix build tool delivering increased reliability, code
+        quality, and pluggability}
+    \project{Panettone}{\url{https://cs.tvl.fyi/depot/-/tree/web/panettone}}{
+        Aggressively simple bug-tracker developed in Common Lisp for the community
+        involved in the development of Tvix. Hosted at https://b.tvl.fyi}
+    \project{Org-Clubhouse}{\url{https://github.com/glittershark/org-clubhouse}}{
+        Emacs library for integration between org-mode and the Clubhouse issue
+        tracker}
+    \project{Github Bug Bounty}{\url{https://bounty.github.com/researchers/glittershark.html}}{
+        Discovered and responsibly disclosed a persistent XSS on Github's main
+        website}
+    \project{core-async-storage}{\url{https://github.com/glittershark/core-async-storage}}{
+        Simple Clojurescript wrapper around React Native's AsyncStorage using
+        core.async}
+  \end{itemize}
+}
+
+\end{document}
+% vim: set tw=95 colorcolumn=-1:
diff --git a/users/grfn/resume/tweaklist.sty b/users/aspen/resume/tweaklist.sty
index adc9398932..adc9398932 100644
--- a/users/grfn/resume/tweaklist.sty
+++ b/users/aspen/resume/tweaklist.sty
diff --git a/users/grfn/bbbg/.envrc b/users/aspen/secrets/.envrc
index 051d09d292..051d09d292 100644
--- a/users/grfn/bbbg/.envrc
+++ b/users/aspen/secrets/.envrc
diff --git a/users/aspen/secrets/bbbg.age b/users/aspen/secrets/bbbg.age
new file mode 100644
index 0000000000..ebc0df2338
--- /dev/null
+++ b/users/aspen/secrets/bbbg.age
Binary files differdiff --git a/users/aspen/secrets/buildkite-ssh-key.age b/users/aspen/secrets/buildkite-ssh-key.age
new file mode 100644
index 0000000000..d9587f11df
--- /dev/null
+++ b/users/aspen/secrets/buildkite-ssh-key.age
Binary files differdiff --git a/users/aspen/secrets/buildkite-token.age b/users/aspen/secrets/buildkite-token.age
new file mode 100644
index 0000000000..320ee06c09
--- /dev/null
+++ b/users/aspen/secrets/buildkite-token.age
Binary files differdiff --git a/users/aspen/secrets/cloudflare.age b/users/aspen/secrets/cloudflare.age
new file mode 100644
index 0000000000..4f42ee7821
--- /dev/null
+++ b/users/aspen/secrets/cloudflare.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 CpJBgQ AVkUs8tuzVlDq3FH/zRrBr5f4KR05fONM6iCluq6hyM
+feS2cxFowSWfDdUQjtmIiMc5338n805yownSZ/ZWfS8
+-> ssh-ed25519 LfBFbQ F67irB+DYQ8WMhaFcO+3o0O0lJsf+tWFZ9cSGSuHgA8
+EKS4zRGUEgeldjxdx4sIsnorWHoeTlXa9LJtNf9lkAM
+-> QvY:XSvC-grease 04
+pBnXsOF6qugcSBp+pw
+--- +g65NbIxu6bVVerS93kYZpEO5ssUZfCD+sZMzOjDUdU
+RξTΐmaF[BÊΊψΥ0ƒa_&Λ•=3dlzRVi΄6-9α:ώό³ΏU.EΘΰ…	όͺ —JΞ™ŒίχΖΫA·-€qྟχ·Π|‘™Π}}a=žHΊ+]m™tΐ―Rψ–ΰ%9\˜υJt€š|1BΏ
\ No newline at end of file
diff --git a/users/aspen/secrets/ddclient-password.age b/users/aspen/secrets/ddclient-password.age
new file mode 100644
index 0000000000..8d25e3b539
--- /dev/null
+++ b/users/aspen/secrets/ddclient-password.age
Binary files differdiff --git a/users/grfn/secrets/default.nix b/users/aspen/secrets/default.nix
index 26b1998f56..26b1998f56 100644
--- a/users/grfn/secrets/default.nix
+++ b/users/aspen/secrets/default.nix
diff --git a/users/aspen/secrets/secrets.nix b/users/aspen/secrets/secrets.nix
new file mode 100644
index 0000000000..5bfb1c3eb0
--- /dev/null
+++ b/users/aspen/secrets/secrets.nix
@@ -0,0 +1,15 @@
+let
+  grfn = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMcBGBoWd5pPIIQQP52rcFOQN3wAY0J/+K2fuU6SffjA";
+  mugwump = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFE2fxPgWO+zeQoLBTgsgxP7Vg7QNHlrQ+Rb3fHFTomB";
+  ogopogo = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINoS7PqM8d7xc8nn0yfiPGfRaH8U/nq2Jm27nRO3L5P0";
+  bbbg = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL/VzrNEY47KPTce3dgfORkAbweWkr4BI8j54BAIs7bG";
+in
+
+{
+  "bbbg.age".publicKeys = [ grfn mugwump bbbg ];
+  "cloudflare.age".publicKeys = [ grfn mugwump ];
+  "ddclient-password.age".publicKeys = [ grfn mugwump ];
+  "buildkite-ssh-key.age".publicKeys = [ grfn mugwump ogopogo ];
+  "buildkite-token.age".publicKeys = [ grfn mugwump ogopogo ];
+  "windtunnel-bot-github-token.age".publicKeys = [ grfn mugwump ogopogo ];
+}
diff --git a/users/aspen/secrets/shell.nix b/users/aspen/secrets/shell.nix
new file mode 100644
index 0000000000..6e70458d19
--- /dev/null
+++ b/users/aspen/secrets/shell.nix
@@ -0,0 +1,8 @@
+let
+  depot = import ../../.. { };
+in
+depot.third_party.nixpkgs.mkShell {
+  buildInputs = [
+    depot.third_party.agenix.cli
+  ];
+}
diff --git a/users/aspen/secrets/windtunnel-bot-github-token.age b/users/aspen/secrets/windtunnel-bot-github-token.age
new file mode 100644
index 0000000000..daae999582
--- /dev/null
+++ b/users/aspen/secrets/windtunnel-bot-github-token.age
@@ -0,0 +1,11 @@
+age-encryption.org/v1
+-> ssh-ed25519 CpJBgQ YaZ2VHyXofn2qnxRrOYO4yPPu77BEPFq/cbnfa+5WAA
+VgJQoyJVxirvASD0aDsuzmbNJdIP0kpHa5b72Ri7kr8
+-> ssh-ed25519 LfBFbQ cXXW3kQzZL7sU4heujIJGzvfpbX0toL2AgsJl5AZPEg
+mhkKn69c/QeCJhYAFgx/MsHrIrXim3OcjkZ/rrckVLs
+-> ssh-ed25519 GeE7sQ /XcP3pWg+aKF1F0sPu6RpYv3Rfj2J/QI0yjg3Wgfjm0
+d+rsgbMlDJx0VrjD4/nO4UcM10hcrLxcPA3QlY1t7sQ
+-> "0?-grease k}d?h6 |v
+7mV6AFUdCMCrkmLVQaWJPQ
+--- I9Ls9AWMkSFCKw7y4pLoTkeGw7h5iROwXLuUm0nfuj8
+~‚v‰8‚&‚ό£Ή3\²ύ.»%$Ό›ΙΊ°³tςσˆΨQ©ˆΐ¨α”ΕιΌΝœ}ˆ—σ,BEΗh
w96”ηφ?ΣU
\ No newline at end of file
diff --git a/users/grfn/system/.gitignore b/users/aspen/system/.gitignore
index 41fbeb02c4..41fbeb02c4 100644
--- a/users/grfn/system/.gitignore
+++ b/users/aspen/system/.gitignore
diff --git a/users/riking/dotfiles/regolith/flags/first-time-setup-r1-4-1 b/users/aspen/system/home/.skip-subtree
index e69de29bb2..e69de29bb2 100644
--- a/users/riking/dotfiles/regolith/flags/first-time-setup-r1-4-1
+++ b/users/aspen/system/home/.skip-subtree
diff --git a/users/aspen/system/home/common/solarized.nix b/users/aspen/system/home/common/solarized.nix
new file mode 100644
index 0000000000..554ee0523e
--- /dev/null
+++ b/users/aspen/system/home/common/solarized.nix
@@ -0,0 +1,18 @@
+rec {
+  base03 = "#002B36";
+  base02 = "#073642";
+  base01 = "#586e75";
+  base00 = "#657b83";
+  base0 = "#839496";
+  base1 = "#93a1a1";
+  base2 = "#eee8d5";
+  base3 = "#fdf6e3";
+  yellow = "#b58900";
+  orange = "#cb4b16";
+  red = "#dc322f";
+  magenta = "#d33682";
+  violet = "#6c71c4";
+  blue = "#268bd2";
+  cyan = "#2aa198";
+  green = "#859900";
+}
diff --git a/users/aspen/system/home/default.nix b/users/aspen/system/home/default.nix
new file mode 100644
index 0000000000..90df02b378
--- /dev/null
+++ b/users/aspen/system/home/default.nix
@@ -0,0 +1,42 @@
+{ pkgs, depot, lib, ... }:
+
+with lib;
+
+rec {
+  home = confPath: (import (pkgs.home-manager.src + "/modules") {
+    inherit pkgs;
+
+    configuration = { config, lib, ... }: {
+      imports = [ confPath ];
+      lib.depot = depot;
+
+      # home-manager exposes no API to override the package set that
+      # is used, unless called from the NixOS module.
+      #
+      # To get around it, the module argument is overridden here.
+      _module.args.pkgs = mkForce pkgs;
+    };
+  });
+
+  dobharchu = home ./machines/dobharchu.nix;
+
+  dobharchuHome = dobharchu.activation-script;
+
+  ogopogo = home ./machines/ogopogo.nix;
+
+  ogopogoHome = ogopogo.activation-script;
+
+  yeren = home ./machines/yeren.nix;
+
+  yerenHome = yeren.activation-script;
+
+  lusca = home ./machines/lusca.nix;
+
+  luscaHome = lusca.activation-script;
+
+  meta.ci.targets = [
+    "ogopogoHome"
+    "luscaHome"
+    "yerenHome"
+  ];
+}
diff --git a/users/grfn/system/home/home.nix b/users/aspen/system/home/home.nix
index 39045c147d..39045c147d 100644
--- a/users/grfn/system/home/home.nix
+++ b/users/aspen/system/home/home.nix
diff --git a/users/aspen/system/home/machines/dobharchu.nix b/users/aspen/system/home/machines/dobharchu.nix
new file mode 100644
index 0000000000..c26f3baef1
--- /dev/null
+++ b/users/aspen/system/home/machines/dobharchu.nix
@@ -0,0 +1,20 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../platforms/darwin.nix
+    ../modules/common.nix
+    # ../modules/games.nix
+  ];
+
+  home.packages = with pkgs; [
+    coreutils
+    gnupg
+    nix-prefetch-github
+    pass
+    pinentry_mac
+  ];
+
+  programs.home-manager.enable = true;
+  home.stateVersion = "21.11";
+}
diff --git a/users/aspen/system/home/machines/lusca.nix b/users/aspen/system/home/machines/lusca.nix
new file mode 100644
index 0000000000..fc5f606639
--- /dev/null
+++ b/users/aspen/system/home/machines/lusca.nix
@@ -0,0 +1,34 @@
+{ pkgs, lib, config, ... }:
+
+let
+  inherit (builtins) pathExists;
+in
+{
+  imports = [
+    ../platforms/linux.nix
+    ../modules/common.nix
+
+    ../modules/email.nix
+    ../modules/desktop.nix
+  ] ++ (lib.optional (pathExists ../modules/private.nix)
+    ../modules/private.nix);
+
+  home.username = lib.mkForce "aspen";
+  home.homeDirectory = lib.mkForce "/home/aspen";
+
+  # for when hacking
+  programs.home-manager.enable = true;
+  home.stateVersion = "20.03";
+
+  system.machine = {
+    wirelessInterface = "wlp1s0";
+    i3FontSize = 9;
+    battery = 1;
+  };
+
+  programs.alacritty.settings.font.size = lib.mkForce 5.5;
+
+  home.packages = with pkgs; [ discord steam tdesktop slack ];
+
+  xsession.windowManager.i3.config.keybindings.XF86AudioMedia = "exec lock";
+}
diff --git a/users/aspen/system/home/machines/ogopogo.nix b/users/aspen/system/home/machines/ogopogo.nix
new file mode 100644
index 0000000000..37396a5aa1
--- /dev/null
+++ b/users/aspen/system/home/machines/ogopogo.nix
@@ -0,0 +1,78 @@
+{ pkgs, lib, config, ... }:
+
+let
+  inherit (builtins) pathExists;
+  laptopKeyboardId = "5";
+in
+
+{
+  imports = [
+    ../platforms/linux.nix
+    ../modules/common.nix
+    ../modules/desktop.nix
+    ../modules/games.nix
+    ../modules/obs.nix
+    ../modules/development/agda.nix
+    ../modules/development/readyset.nix
+    ../modules/development/ocaml.nix
+  ] ++ (lib.optional (pathExists ../modules/private.nix) ../modules/private.nix);
+
+  programs.home-manager.enable = true;
+  home.stateVersion = "21.11";
+
+  system.machine = {
+    wirelessInterface = "wlp4s0";
+    i3FontSize = 9;
+    battery = null;
+  };
+
+  home.packages = with pkgs; [
+    zoom-us
+    slack
+    mariadb
+    graphviz
+    gnuplot
+    mypaint
+    xdot
+    tdesktop
+    subsurface
+    (discord.override rec {
+      version = "0.0.22";
+      src = fetchurl {
+        url = "https://dl.discordapp.net/apps/linux/${version}/discord-${version}.tar.gz";
+        sha256 = "19xbmrd782m4lp2l0ww5v3ip227g0z8pplxigxga96q43rvp6p0p";
+      };
+    })
+    steam
+  ];
+
+  systemd.user.services.laptop-keyboard = {
+    Unit = {
+      Description = "Swap caps+escape and alt+super, but only on the built-in laptop keyboard";
+      After = [ "graphical-session-pre.target" ];
+      PartOf = [ "graphical-session.target" ];
+    };
+
+    Install = { WantedBy = [ "graphical-session.target" ]; };
+
+    Service = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStart = (
+        "${pkgs.xorg.setxkbmap}/bin/setxkbmap "
+        + "-device ${laptopKeyboardId} "
+        + "-option caps:swapescape "
+        + "-option compose:ralt "
+        + "-option altwin:swap_alt_win"
+      );
+    };
+  };
+
+  xsession.windowManager.i3.config.keybindings.F9 = "exec lock";
+
+  # Telegram adds this to ~/.config/mimeapps.list if it isn't already there,
+  # preventing home manager from installing (since it doesn't want to overwrite
+  # the file)
+  xdg.mimeApps.defaultApplications."x-scheme-handler/tg" =
+    "userapp-Telegram Desktop-K290F1.desktop";
+}
diff --git a/users/aspen/system/home/machines/roswell.nix b/users/aspen/system/home/machines/roswell.nix
new file mode 100644
index 0000000000..135477b12d
--- /dev/null
+++ b/users/aspen/system/home/machines/roswell.nix
@@ -0,0 +1,63 @@
+{ pkgs, lib, config, ... }:
+
+let
+  inherit (builtins) pathExists;
+in
+
+{
+  imports = [
+    ../platforms/linux.nix
+    ../modules/shell.nix
+    ../modules/development.nix
+    ../modules/emacs.nix
+    ../modules/vim.nix
+    ../modules/development/readyset.nix
+    ../modules/tmux.nix
+  ] ++ (lib.optional (pathExists ../modules/private.nix) ../modules/private.nix);
+
+  home.packages = with pkgs; [
+    # System utilities
+    bat
+    htop
+    killall
+    bind
+    zip
+    unzip
+    tree
+    nmap
+    bc
+    pv
+
+    # Security
+    gnupg
+    keybase
+    openssl
+
+    # Nix things
+    nixfmt
+    nix-prefetch-github
+    nixpkgs-review
+    cachix
+
+    # ReadySet stuff
+    nodejs
+    mysql80
+
+    (writeShellScriptBin "xdg-open" "echo xdg-open: \"$@\"")
+  ];
+
+  programs.password-store.enable = true;
+
+  programs.home-manager.enable = true;
+  home.stateVersion = "20.03";
+
+  xsession.enable = lib.mkForce false;
+
+  services.lorri.enable = true;
+
+  programs.direnv = {
+    enable = true;
+    enableBashIntegration = true;
+    enableZshIntegration = true;
+  };
+}
diff --git a/users/aspen/system/home/machines/yeren.nix b/users/aspen/system/home/machines/yeren.nix
new file mode 100644
index 0000000000..9a7a561b5e
--- /dev/null
+++ b/users/aspen/system/home/machines/yeren.nix
@@ -0,0 +1,77 @@
+{ pkgs, lib, config, ... }:
+
+let
+  inherit (builtins) pathExists;
+  laptopKeyboardId = "5";
+in
+
+{
+  imports = [
+    ../platforms/linux.nix
+    ../modules/common.nix
+    ../modules/desktop.nix
+    ../modules/development/agda.nix
+    ../modules/development/readyset.nix
+    ../modules/development/ocaml.nix
+  ] ++ (lib.optional (pathExists ../modules/private.nix) ../modules/private.nix);
+
+  # for when hacking
+  programs.home-manager.enable = true;
+  home.stateVersion = "20.03";
+
+  system.machine = {
+    wirelessInterface = "wlp0s20f3";
+    i3FontSize = 9;
+  };
+
+  home.packages = with pkgs; [
+    zoom-us
+    slack
+    mariadb
+    graphviz
+    gnuplot
+    mypaint
+    xdot
+    tdesktop
+    subsurface
+    discord
+    steam
+  ];
+
+  systemd.user.services.laptop-keyboard = {
+    Unit = {
+      Description = "Swap caps+escape and alt+super, but only on the built-in laptop keyboard";
+      After = [ "graphical-session-pre.target" ];
+      PartOf = [ "graphical-session.target" ];
+    };
+
+    Install = { WantedBy = [ "graphical-session.target" ]; };
+
+    Service = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStart = (
+        "${pkgs.xorg.setxkbmap}/bin/setxkbmap "
+        + "-device ${laptopKeyboardId} "
+        + "-option caps:swapescape "
+        + "-option compose:ralt "
+        + "-option altwin:swap_alt_win"
+      );
+    };
+  };
+
+  xsession.windowManager.i3.config.keybindings.F9 = "exec lock";
+
+  xdg.mimeApps.defaultApplications."x-scheme-handler/tg" =
+    "telegramdesktop.desktop";
+
+  programs.zsh.shellAliases = {
+    "graph" = "curl -s localhost:6033/graph | dot -Tpng | feh -";
+  };
+
+  programs.ssh.matchBlocks."grfn-dev" = {
+    host = "grfn-dev";
+    forwardAgent = true;
+    user = "ubuntu";
+  };
+}
diff --git a/users/aspen/system/home/modules/.gitignore b/users/aspen/system/home/modules/.gitignore
new file mode 100644
index 0000000000..a211cae6c6
--- /dev/null
+++ b/users/aspen/system/home/modules/.gitignore
@@ -0,0 +1 @@
+private.nix
diff --git a/users/aspen/system/home/modules/alacritty.nix b/users/aspen/system/home/modules/alacritty.nix
new file mode 100644
index 0000000000..561cab4d79
--- /dev/null
+++ b/users/aspen/system/home/modules/alacritty.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+{
+  programs.alacritty = {
+    enable = true;
+    settings = {
+      font.size = 6;
+      font.normal.family = "Meslo LGSDZ Nerd Font";
+
+      keyboard.bindings = [
+        {
+          key = "Escape";
+          mods = "Control";
+          action = "ToggleViMode";
+        }
+      ];
+
+      colors = with import ../common/solarized.nix; rec {
+        draw_bold_text_with_bright_colors = false;
+
+        # Default colors
+        primary = {
+          background = base3;
+          foreground = base00;
+        };
+
+        cursor = {
+          text = base3;
+          cursor = base00;
+        };
+
+        # Normal colors
+        normal = {
+          inherit red green yellow blue magenta cyan;
+          black = base02;
+          white = base2;
+        };
+
+        # Bright colors
+        # bright = normal;
+        bright = {
+          black = base03;
+          red = orange;
+          green = base01;
+          yellow = base00;
+          blue = base0;
+          magenta = violet;
+          cyan = base1;
+          white = base3;
+        };
+
+        vi_mode_cursor.cursor = red;
+      };
+    };
+  };
+}
diff --git a/users/aspen/system/home/modules/common.nix b/users/aspen/system/home/modules/common.nix
new file mode 100644
index 0000000000..b51ae1c7db
--- /dev/null
+++ b/users/aspen/system/home/modules/common.nix
@@ -0,0 +1,88 @@
+{ config, lib, pkgs, ... }:
+
+# Everything in here needs to work on linux or darwin, with or without a desktop
+# environment
+
+{
+  imports = [
+    ../modules/shell.nix
+    # ../modules/development.nix
+    ../modules/emacs.nix
+    ../modules/vim.nix
+    ../modules/tarsnap.nix
+    ../modules/twitter.nix
+    ../modules/lib/cloneRepo.nix
+  ];
+
+  home.username = "aspen";
+  home.homeDirectory = "/home/aspen";
+
+  programs.password-store.enable = true;
+
+  aspen.impure.clonedRepos.passwordStore = {
+    github = "glittershark/pass";
+    path = ".local/share/password-store";
+  };
+
+  home.packages = with pkgs; [
+    # System utilities
+    bat
+    htop
+    killall
+    bind
+    zip
+    unzip
+    tree
+    nmap
+    bc
+    pv
+
+    # Security
+    gnupg
+    keybase
+    openssl
+
+    # Nix things
+    nixfmt
+    nix-prefetch-github
+    nixpkgs-review
+    cachix
+    (writeShellScriptBin "rebuild-mugwump" ''
+      set -eo pipefail
+      cd ~/code/depot
+      nix build -f . users.aspen.system.system.mugwumpSystem -o /tmp/mugwump
+      nix copy -f . users.aspen.system.system.mugwumpSystem \
+        --to ssh://mugwump
+      system=$(readlink -ef /tmp/mugwump)
+      ssh mugwump sudo nix-env -p /nix/var/nix/profiles/system --set $system
+      ssh mugwump sudo $system/bin/switch-to-configuration switch
+      rm /tmp/mugwump
+    '')
+    (writeShellScriptBin "rebuild-roswell" ''
+      set -eo pipefail
+      cd ~/code/depot
+      nix build -f . users.aspen.system.system.roswellSystem -o /tmp/roswell
+      nix copy -f . users.aspen.system.system.roswellSystem \
+        --to ssh://roswell
+      system=$(readlink -ef /tmp/roswell)
+      ssh roswell sudo nix-env -p /nix/var/nix/profiles/system --set $system
+      ssh roswell sudo $system/bin/switch-to-configuration switch
+      rm /tmp/roswell
+    '')
+    (writeShellScriptBin "rebuild-home" ''
+      set -eo pipefail
+      cd ~/code/depot
+      home=$(nix-build -A users.aspen.system.home.$(hostname)Home -o /tmp/home)
+      nix-env -p /nix/var/nix/per-user/aspen/home --set $home
+      $home/activate
+    '')
+  ];
+
+  programs.ssh = { enable = true; };
+
+  programs.direnv = {
+    enable = true;
+    enableBashIntegration = true;
+    enableZshIntegration = true;
+  };
+}
diff --git a/users/aspen/system/home/modules/desktop.nix b/users/aspen/system/home/modules/desktop.nix
new file mode 100644
index 0000000000..e1841cd3c3
--- /dev/null
+++ b/users/aspen/system/home/modules/desktop.nix
@@ -0,0 +1,40 @@
+{ config, lib, pkgs, ... }:
+
+# Things that only work in the presence of a linux desktop environment
+
+{
+  imports = [
+    ./i3.nix
+    ./alacritty.nix
+  ];
+
+  home.packages = with pkgs; [
+    (writeShellApplication {
+      name = "edit-input";
+
+      runtimeInputs = [ xdotool xclip ];
+      text = ''
+        set -euo pipefail
+
+        sleep 0.2
+        xdotool key ctrl+a ctrl+c
+        xclip -out -selection clipboard > /tmp/EDIT
+        emacsclient -c /tmp/EDIT
+        xclip -in -selection clipboard < /tmp/EDIT
+        sleep 0.2
+        xdotool key ctrl+v
+        rm /tmp/EDIT
+      '';
+    })
+  ];
+
+  services.syncthing.tray.enable = true;
+
+  gtk = {
+    enable = true;
+    gtk3.bookmarks = [
+      "file:///home/aspen/code"
+      "file:///home/aspen/notes"
+    ];
+  };
+}
diff --git a/users/aspen/system/home/modules/development.nix b/users/aspen/system/home/modules/development.nix
new file mode 100644
index 0000000000..ca6ef131a3
--- /dev/null
+++ b/users/aspen/system/home/modules/development.nix
@@ -0,0 +1,217 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  clj2nix = pkgs.callPackage
+    (pkgs.fetchFromGitHub {
+      owner = "hlolli";
+      repo = "clj2nix";
+      rev = "3ab3480a25e850b35d1f532a5e4e7b3202232383";
+      sha256 = "1lry026mlpxp1j563qs13nhxf37i2zpl7lh0lgfdwc44afybqka6";
+    })
+    { };
+
+  pg-dump-upsert = pkgs.buildGoModule rec {
+    pname = "pg-dump-upsert";
+    version = "165258deaebded5e9b88f7a0acf3a4b7350e7bf4";
+
+    src = pkgs.fetchFromGitHub {
+      owner = "tomyl";
+      repo = "pg-dump-upsert";
+      rev = version;
+      sha256 = "1an4h8jjbj3r618ykjwk9brii4h9cxjqy47c4c8rivnvhimgf4wm";
+    };
+
+    vendorHash = "sha256:1a5fx6mrv30cl46kswicd8lf5i5shn1fykchvbnbhdpgxhbz6qi4";
+  };
+
+in
+
+with lib;
+
+{
+  imports = [
+    ./lib/zshFunctions.nix
+    # TODO(aspen): agda build is broken in the nixpkgs checkout
+    # ./development/agda.nix
+    ./development/rust.nix
+  ];
+
+  home.packages = with pkgs; [
+    jq
+    yq
+    gron
+    gitAndTools.tig
+    gitAndTools.gh
+    shellcheck
+    httpie
+    entr
+    gnumake
+    inetutils
+    tokei
+    jsonnet
+    ngrok
+    amber
+    ocamlPackages.patdiff
+
+    gdb
+    lldb
+    hyperfine
+    clang-tools
+
+    clj2nix
+    clojure
+    leiningen
+    clj-kondo
+
+    pg-dump-upsert
+
+    nodePackages.prettier
+  ] ++ optionals (stdenv.isLinux) [
+    # TODO(aspen): replace with stable again once the current julia debacle
+    # is resolved upstream, see https://github.com/NixOS/nixpkgs/pull/121114
+    julia_16-bin
+    valgrind
+
+    linuxPackages.perf
+    rr
+  ];
+
+  programs.git = {
+    enable = true;
+    package = pkgs.gitFull;
+    userEmail = "root@gws.fyi";
+    userName = "Aspen Smith";
+    ignores = [
+      "*.sw*"
+      ".classpath"
+      ".project"
+      ".settings/"
+      ".dir-locals.el"
+      ".stack-work-profiling"
+      ".projectile"
+    ];
+    extraConfig = {
+      github.user = "glittershark";
+      merge.conflictstyle = "diff3";
+      rerere.enabled = "true";
+      advice.skippedCherryPicks = "false";
+    };
+
+    delta = {
+      enable = true;
+      options = {
+        syntax-theme = "Solarized (light)";
+        hunk-style = "plain";
+        commit-style = "box";
+      };
+    };
+  };
+
+  home.file.".gdbinit".text = ''
+    set history filename ~/.gdb_history
+    set history save on
+    set history size unlimited
+    set history remove-duplicates unlimited
+    set history expansion on
+  '';
+
+  home.file.".psqlrc".text = ''
+    \set QUIET 1
+
+    \timing
+    \set ON_ERROR_ROLLBACK interactive
+    \set VERBOSITY verbose
+    \x auto
+    \set PROMPT1 '%[%033[1m%]%M/%/%R%[%033[0m%]%# '
+    \set PROMPT2 '...%# '
+    \set HISTFILE ~/.psql_history- :DBNAME
+    \set HISTCONTROL ignoredups
+    \pset null [null]
+
+    \pset linestyle 'unicode'
+    \pset unicode_border_linestyle single
+    \pset unicode_column_linestyle single
+    \pset unicode_header_linestyle double
+
+    \unset QUIET
+  '';
+
+  programs.readline = {
+    enable = true;
+    extraConfig = ''
+      set editing-mode vi
+    '';
+  };
+
+  programs.zsh = {
+    shellAliases = {
+      # Git
+      "gwip" = "git add . && git commit -am wip";
+      "gpr" = "g pull-request";
+      "gcl" = "git clone";
+      "grs" = "gr --soft";
+      "grhh" = "grh HEAD";
+      "grh" = "gr --hard";
+      "gr" = "git reset";
+      "gcb" = "gc -b";
+      "gco" = "gc";
+      "gcd" = "gc development";
+      "gcm" = "gc master";
+      "gcc" = "gc canon";
+      "gc" = "git checkout";
+      "gbg" = "git branch | grep";
+      "gba" = "git branch -a";
+      "gb" = "git branch";
+      "gcv" = "git commit --verbose";
+      "gci" = "git commit";
+      "gm" = "git merge";
+      "gdc" = "gd --cached";
+      "gd" = "git diff";
+      "gsl" = "git stash list";
+      "gss" = "git show stash";
+      "gsad" = "git stash drop";
+      "gsa" = "git stash";
+      "gst" = "gs";
+      "gs" = "git status";
+      "gg" = "gl --decorate --oneline --graph --date-order --all";
+      "gl" = "git log";
+      "gf" = "git fetch";
+      "gur" = "gu --rebase";
+      "gu" = "git pull";
+      "gpf" = "gp -f";
+      "gpa" = "gp --all";
+      "gpu" = "git push -u origin \"$(git symbolic-ref --short HEAD)\"";
+      "gp" = "git push";
+      "ganw" = "git diff -w --no-color | git apply --cached --ignore-whitespace";
+      "ga" = "git add";
+      "gnp" = "git --no-pager";
+      "g" = "git";
+      "grim" = "git fetch && git rebase -i --autostash origin/master";
+      "grom" = "git fetch && git rebase --autostash origin/master";
+      "groc" = "git fetch && git rebase --autostash origin/canon";
+      "grc" = "git rebase --continue";
+      "gcan" = "git commit --amend --no-edit";
+      "grl" = "git reflog";
+
+      # Haskell
+      "crl" = "cabal repl";
+      "cr" = "cabal run";
+      "cnb" = "cabal new-build";
+      "cob" = "cabal old-build";
+      "cnr" = "cabal new-run";
+      "cor" = "cabal old-run";
+      "ho" = "hoogle";
+    };
+
+    functions = {
+      gdelmerged = ''
+        git branch --merged | egrep -v 'master' | tr -d '+ ' | xargs git branch -d
+      '';
+
+      gref = ''
+        git show -s --pretty=reference "$1" | xclip -selection clipboard
+      '';
+    };
+  };
+}
diff --git a/users/grfn/system/home/modules/development/agda.nix b/users/aspen/system/home/modules/development/agda.nix
index afd22a306d..55381994c4 100644
--- a/users/grfn/system/home/modules/development/agda.nix
+++ b/users/aspen/system/home/modules/development/agda.nix
@@ -29,7 +29,7 @@ in
       ]))
   ];
 
-  grfn.impure.clonedRepos = {
+  aspen.impure.clonedRepos = {
     agda-stdlib = {
       github = "agda/agda-stdlib";
       path = "code/agda-stdlib";
@@ -51,8 +51,8 @@ in
   '';
 
   home.file.".agda/libraries".text = ''
-    /home/grfn/code/agda-stdlib/standard-library.agda-lib
-    /home/grfn/code/agda-categories/agda-categories.agda-lib
+    /home/aspen/code/agda-stdlib/standard-library.agda-lib
+    /home/aspen/code/agda-categories/agda-categories.agda-lib
   '';
 
 }
diff --git a/users/grfn/system/home/modules/development/kube.nix b/users/aspen/system/home/modules/development/kube.nix
index 97ae4760d4..876b0c08df 100644
--- a/users/grfn/system/home/modules/development/kube.nix
+++ b/users/aspen/system/home/modules/development/kube.nix
@@ -16,7 +16,7 @@
     "kpa" = "kubectl get pods --all-namespaces";
     "klf" = "kubectl logs -f";
     "kdep" = "kubectl get deployments";
-    "ked" =  "kubectl edit deployment";
+    "ked" = "kubectl edit deployment";
     "kpw" = "kubectl get pods -w";
     "kew" = "kubectl get events -w";
     "kdel" = "kubectl delete";
diff --git a/users/aspen/system/home/modules/development/ocaml.nix b/users/aspen/system/home/modules/development/ocaml.nix
new file mode 100644
index 0000000000..5dcdd8980e
--- /dev/null
+++ b/users/aspen/system/home/modules/development/ocaml.nix
@@ -0,0 +1,17 @@
+{ config, lib, pkgs, ... }:
+
+{
+  home.packages = with pkgs; [
+    ocaml
+
+    # ocamlPackages.merlin
+    # ocamlPackages.utop
+    # ocamlPackages.ocp-indent
+    # ocamlPackages.ocamlformat
+  ];
+
+  programs.opam = {
+    enable = true;
+    enableZshIntegration = true;
+  };
+}
diff --git a/users/aspen/system/home/modules/development/readyset.nix b/users/aspen/system/home/modules/development/readyset.nix
new file mode 100644
index 0000000000..afe762468a
--- /dev/null
+++ b/users/aspen/system/home/modules/development/readyset.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ./rust.nix
+  ];
+
+  home.packages = with pkgs; [
+    # These go in $PATH so I can run it from rofi and parent to my WM
+    (writeShellScriptBin "dotclip" "xclip -out -selection clipboard | dot -Tpng | feh -")
+    (writeShellScriptBin "dotcontroller" "curl -s localhost:6033/graph | dot -Tpng | feh -")
+
+    rain
+    awscli2
+    ssm-session-manager-plugin
+    amazon-ecr-credential-helper
+    postgresql_15
+
+    # TODO remove override when https://github.com/NixOS/nixpkgs/pull/233826 is merged
+    (sysbench.overrideDerivation (oldAttrs: {
+      configureFlags = oldAttrs.configureFlags ++ [ "--with-pgsql" ];
+      buildInputs = oldAttrs.buildInputs ++ [ postgresql ];
+    }))
+  ];
+
+  programs.zsh.shellAliases = {
+    "tf" = "terraform";
+  };
+
+  home.file.".docker/config.json".text = builtins.toJSON {
+    credHelpers = {
+      "305232526136.dkr.ecr.us-east-2.amazonaws.com" = "ecr-login";
+    };
+  };
+
+  programs.zsh.functions."purge_deployment" = ''
+    for key in $(http :8500/v1/kv/$1 keys==true | jq -r .'[]'); do http DELETE ":8500/v1/kv/$key"; done
+  '';
+}
diff --git a/users/aspen/system/home/modules/development/rust.nix b/users/aspen/system/home/modules/development/rust.nix
new file mode 100644
index 0000000000..c4b20f2315
--- /dev/null
+++ b/users/aspen/system/home/modules/development/rust.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (config.lib) depot;
+in
+
+with lib;
+
+{
+
+  home.packages = with pkgs; [
+    rustup
+    cargo-edit
+    cargo-expand
+    cargo-udeps
+    cargo-bloat
+    sccache
+    evcxr
+
+    depot.users.aspen.pkgs.cargo-hakari
+    depot.users.aspen.pkgs.cargo-nextest
+
+    # benchmarking+profiling
+    cargo-criterion
+    cargo-flamegraph
+    coz
+    inferno
+    hotspot
+  ] ++ optionals (stdenv.isLinux) [
+    cargo-rr
+  ];
+
+  programs.zsh.shellAliases = {
+    "cg" = "cargo";
+    "cb" = "cargo build";
+    "ct" = "cargo test";
+    "ctw" = "fd -e rs | entr cargo test";
+    "cch" = "cargo check";
+  };
+
+  home.file.".cargo/config".text = ''
+    [build]
+    rustc-wrapper = "${pkgs.sccache}/bin/sccache"
+
+    [target.x86_64-unknown-linux-gnu]
+    linker = "clang"
+    rustflags = ["-C", "link-arg=-fuse-ld=${pkgs.mold}/bin/mold"]
+  '';
+}
diff --git a/users/aspen/system/home/modules/emacs.nix b/users/aspen/system/home/modules/emacs.nix
new file mode 100644
index 0000000000..6936da4b9d
--- /dev/null
+++ b/users/aspen/system/home/modules/emacs.nix
@@ -0,0 +1,108 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  # doom-emacs = pkgs.callPackage (builtins.fetchTarball {
+  #   url = https://github.com/vlaci/nix-doom-emacs/archive/master.tar.gz;
+  # }) {
+  #   doomPrivateDir = ./doom.d;  # Directory containing your config.el init.el
+  #                               # and packages.el files
+  # };
+
+  depot = config.lib.depot;
+
+in
+{
+  imports = [
+    ./lib/cloneRepo.nix
+  ];
+
+  # home.packages = [ doom-emacs ];
+  # home.file.".emacs.d/init.el".text = ''
+  #     (load "default.el")
+  # '';
+  #
+
+  config = mkMerge [
+    {
+      home.packages = with pkgs; [
+        # LaTeX (for org export)
+        (pkgs.texlive.combine {
+          inherit (pkgs.texlive)
+            capt-of
+            collection-fontsrecommended
+            dvipng
+            fancyvrb
+            float
+            fncychap
+            framed
+            mathpartir
+            needspace
+            parskip
+            scheme-basic
+            semantic
+            tabulary
+            titlesec
+            ulem
+            upquote
+            varwidth
+            wrapfig
+            bussproofs
+            bussproofs-extra
+            ;
+        })
+
+        ispell
+
+        ripgrep
+        coreutils
+        fd
+        clang
+        gnutls
+        emacsPackages.telega
+      ];
+
+      programs.emacs = {
+        enable = true;
+        package = pkgs.emacs;
+        extraPackages = (epkgs:
+          (with epkgs; [
+            tvlPackages.dottime
+            tvlPackages.tvl
+            vterm
+            telega
+          ])
+        );
+      };
+
+      aspen.impure.clonedRepos = {
+        orgClubhouse = {
+          github = "glittershark/org-clubhouse";
+          path = "code/org-clubhouse";
+        };
+
+        doomEmacs = {
+          github = "hlissner/doom-emacs";
+          path = ".emacs.d";
+          after = [ "emacs.d" ];
+          onClone = "bin/doom install";
+        };
+
+        "emacs.d" = {
+          github = "glittershark/emacs.d";
+          path = ".doom.d";
+          after = [ "orgClubhouse" ];
+        };
+      };
+
+      programs.zsh.shellAliases = {
+        "ec" = "emacsclient";
+      };
+    }
+    (mkIf pkgs.stdenv.isLinux {
+      # Notes
+      services.syncthing.enable = true;
+    })
+  ];
+}
diff --git a/users/aspen/system/home/modules/email.nix b/users/aspen/system/home/modules/email.nix
new file mode 100644
index 0000000000..cb92c40cee
--- /dev/null
+++ b/users/aspen/system/home/modules/email.nix
@@ -0,0 +1,86 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+
+  # from home-manager/modules/services/lieer.nix
+  escapeUnitName = name:
+    let
+      good = upperChars ++ lowerChars ++ stringToCharacters "0123456789-_";
+      subst = c: if any (x: x == c) good then c else "-";
+    in
+    stringAsChars subst name;
+
+  accounts = {
+    personal = {
+      primary = true;
+      address = "root@gws.fyi";
+      aliases = [ "aspen@gws.fyi" "aspen@gws.fyi" ];
+      passEntry = "root-gws-msmtp";
+    };
+  };
+
+in
+{
+  # 2022-09-26: workaround for home-manager defaulting to removed pkgs.gmailieer
+  # attribute, can likely be removed soon
+  programs.lieer.package = pkgs.lieer;
+
+  programs.lieer.enable = true;
+  programs.notmuch.enable = true;
+  services.lieer.enable = true;
+  programs.msmtp.enable = true;
+
+  home.packages = with pkgs; [
+    mu
+    msmtp
+    config.lib.depot.users.aspen.pkgs.notmuch-extract-patch
+  ];
+
+  systemd.user.services = mapAttrs'
+    (name: account: {
+      name = escapeUnitName "lieer-${name}";
+      value.Service = {
+        ExecStart = mkForce "${pkgs.writeShellScript "sync-${name}" ''
+        ${pkgs.lieer}/bin/gmi sync --path ~/mail/${name}
+      ''}";
+        Environment =
+          "NOTMUCH_CONFIG=${config.home.sessionVariables.NOTMUCH_CONFIG}";
+      };
+
+    })
+    accounts;
+
+  accounts.email.maildirBasePath = "mail";
+  accounts.email.accounts = mapAttrs
+    (_:
+      params@{ passEntry, ... }:
+      {
+        realName = "Aspen Smith";
+        passwordCommand = "pass ${passEntry}";
+
+        flavor = "gmail.com";
+
+        imapnotify = {
+          enable = true;
+          boxes = [ "Inbox" ];
+        };
+
+        gpg = {
+          key = "0F11A989879E8BBBFDC1E23644EF5B5E861C09A7";
+          signByDefault = true;
+        };
+
+        notmuch.enable = true;
+        lieer = {
+          enable = true;
+          sync = {
+            enable = true;
+            frequency = "*:*";
+          };
+        };
+        msmtp.enable = true;
+      } // builtins.removeAttrs params [ "passEntry" ])
+    accounts;
+}
diff --git a/users/grfn/system/home/modules/firefox.nix b/users/aspen/system/home/modules/firefox.nix
index c7e78685a5..c7e78685a5 100644
--- a/users/grfn/system/home/modules/firefox.nix
+++ b/users/aspen/system/home/modules/firefox.nix
diff --git a/users/aspen/system/home/modules/games.nix b/users/aspen/system/home/modules/games.nix
new file mode 100644
index 0000000000..dc6331d648
--- /dev/null
+++ b/users/aspen/system/home/modules/games.nix
@@ -0,0 +1,51 @@
+{ config, lib, pkgs, ... }:
+
+with pkgs;
+with lib;
+
+let
+
+  df-orig = dwarf-fortress-packages.dwarf-fortress-original;
+
+  df-full = (dwarf-fortress-packages.dwarf-fortress-full.override {
+    theme = null;
+    enableIntro = false;
+    enableFPS = true;
+    enableDFHack = true;
+  });
+
+  init = runCommand "init.txt" { } ''
+    substitute "${df-orig}/data/init/init_default.txt" $out \
+      --replace "[INTRO:YES]" "[INTRO:NO]" \
+      --replace "[VOLUME:255]" "[VOLUME:0]" \
+      --replace "[FPS:NO]" "[FPS:YES]"
+  '';
+
+  d_init = runCommand "d_init.txt" { } ''
+    substitute "${df-orig}/data/init/d_init_default.txt" $out \
+      --replace "[AUTOSAVE:NONE]" "[AUTOSAVE:SEASONAL]" \
+      --replace "[AUTOSAVE_PAUSE:NO]" "[AUTOSAVE_PAUSE:YES]" \
+      --replace "[INITIAL_SAVE:NO]" "[INITIAL_SAVE:YES]" \
+      --replace "[EMBARK_WARNING_ALWAYS:NO]" "[EMBARK_WARNING_ALWAYS:YES]" \
+      --replace "[VARIED_GROUND_TILES:YES]" "[VARIED_GROUND_TILES:NO]" \
+      --replace "[SHOW_FLOW_AMOUNTS:NO]" "[SHOW_FLOW_AMOUNTS:YES]"
+  '';
+
+  df = runCommand "dwarf-fortress" { } ''
+    mkdir -p $out/bin
+    sed \
+      -e '4icp -f ${init} "$DF_DIR/data/init/init.txt"' \
+      -e '4icp -f ${d_init} "$DF_DIR/data/init/d_init.txt"' \
+      < "${df-full}/bin/dwarf-fortress" >"$out/bin/dwarf-fortress"
+
+    shopt -s extglob
+    ln -s ${df-full}/bin/!(dwarf-fortress) $out/bin
+
+    chmod +x $out/bin/dwarf-fortress
+  '';
+
+in
+mkMerge [
+  { home.packages = [ crawl ]; }
+  (mkIf stdenv.isLinux { home.packages = [ df prismlauncher ]; })
+]
diff --git a/users/aspen/system/home/modules/i3.nix b/users/aspen/system/home/modules/i3.nix
new file mode 100644
index 0000000000..f91527da44
--- /dev/null
+++ b/users/aspen/system/home/modules/i3.nix
@@ -0,0 +1,397 @@
+{ config, lib, pkgs, ... }:
+let
+  inherit (config.lib) depot;
+
+  mod = "Mod4";
+  solarized = import ../common/solarized.nix;
+  # TODO pull this out into lib
+  emacsclient = eval: pkgs.writeShellScript "emacsclient-eval" ''
+    msg=$(emacsclient --eval '${eval}' 2>&1)
+    echo "''${msg:1:-1}"
+  '';
+
+  i3status-conf = pkgs.writeText "i3status.conf" ''
+    general {
+        output_format = i3bar
+        colors = true
+        color_good = "#859900"
+
+        interval = 1
+    }
+
+    order += "external_script current_task"
+    order += "external_script inbox"
+    order += "spotify"
+    order += "weather_owm"
+    order += "volume_status"
+    order += "wireless ${config.system.machine.wirelessInterface}"
+    # order += "ethernet enp3s0f0"
+    order += "cpu_usage"
+    ${lib.optionalString (!isNull config.system.machine.battery) ''
+      order += "battery ${toString config.system.machine.battery}"
+    ''}
+    # order += "volume master"
+    order += "time"
+    order += "tztime utc"
+
+    mpd {
+        format = "%artist - %album - %title"
+    }
+
+    wireless ${config.system.machine.wirelessInterface} {
+        format_up = "W: (%quality - %essid - %bitrate) %ip"
+        format_down = "W: -"
+    }
+
+    ethernet enp3s0f0 {
+        format_up = "E: %ip"
+        format_down = "E: -"
+    }
+
+    battery ${toString config.system.machine.battery} {
+        format = "%status %percentage (%remaining)"
+        path = "/sys/class/power_supply/BAT%d/uevent"
+        low_threshold = 10
+    }
+
+    cpu_usage {
+        format = "CPU: %usage"
+    }
+
+    load {
+        format = "%5min"
+    }
+
+    time {
+        format = "    %a %h %d ⌚   %I:%M     "
+    }
+
+    spotify {
+        color_playing = "#fdf6e3"
+        color_paused = "#93a1a1"
+        format_stopped = ""
+        format_down = ""
+        format = "{title} - {artist} ({album})"
+    }
+
+    external_script inbox {
+        script_path = '${emacsclient "(aspen/num-inbox-items-message)"}'
+        format = 'Inbox: {output}'
+        cache_timeout = 120
+        color = "#93a1a1"
+    }
+
+    external_script current_task {
+        script_path = '${
+          emacsclient "(aspen/org-current-clocked-in-task-message)"
+        }'
+        # format = '{output}'
+        cache_timeout = 60
+        color = "#93a1a1"
+    }
+
+    tztime utc {
+        timezone = "UTC"
+        format = "    %HΒ·%M    "
+    }
+
+    volume_status {
+        format = "☊ {percentage}"
+        format_muted = "☊ X"
+        # device = "default"
+        # mixer_idx = 0
+    }
+
+    weather_owm {
+        api_key = '@owmApiKey@'
+        unit_temperature = 'c'
+        format = '{icon} {temperature}[ {rain}]'
+    }
+  '';
+
+  i3status-command = pkgs.writeShellScript "i3status.sh" ''
+    sed -s "s/@owmApiKey@/$(pass owm-api-key)/" \
+      < ${i3status-conf} \
+      > /tmp/i3status.conf
+    py3status -c /tmp/i3status.conf
+  '';
+
+  inherit (builtins) map;
+  inherit (lib) mkMerge range;
+in
+{
+  options = with lib; {
+    system.machine = {
+      wirelessInterface = mkOption {
+        description = ''
+          Name of the primary wireless interface. Used by i3status, etc.
+        '';
+        default = "wlp3s0";
+        type = types.str;
+      };
+
+      i3FontSize = mkOption {
+        description = "Font size to use in i3 window decorations etc.";
+        default = 6;
+        type = types.int;
+      };
+
+      battery = mkOption {
+        description = "Battery index for this system's battery";
+        default = 0;
+        type = types.nullOr types.int;
+      };
+    };
+  };
+
+  config =
+    let
+      fontName = "MesloLGSDZ";
+      fontSize = config.system.machine.i3FontSize;
+      fonts = {
+        names = [ fontName ];
+        size = fontSize * 1.0;
+      };
+      decorationFont = "${fontName} ${toString fontSize}";
+    in
+    {
+      home.packages = with pkgs; [
+        rofi
+        rofi-pass
+        depot.users.aspen.pkgs.py3status
+        i3lock
+        i3status
+        dconf # for gtk
+
+        # Screenshots
+        maim
+
+        # GIFs
+        picom
+        peek
+
+        (pkgs.writeShellScriptBin "lock" ''
+          playerctl pause
+          ${pkgs.i3lock}/bin/i3lock -c 222222
+        '')
+      ];
+
+      xsession.scriptPath = ".xsession";
+
+      xsession.windowManager.i3 = {
+        enable = true;
+        config = {
+          modifier = mod;
+          keybindings =
+            mkMerge (
+              (map
+                (n: {
+                  "${mod}+${toString n}" =
+                    "workspace ${toString n}";
+                  "${mod}+Shift+${toString n}" =
+                    "move container to workspace ${toString n}";
+                })
+                (range 0 9))
+              ++ [
+                (rec {
+                  "${mod}+h" = "focus left";
+                  "${mod}+j" = "focus down";
+                  "${mod}+k" = "focus up";
+                  "${mod}+l" = "focus right";
+                  "${mod}+semicolon" = "focus parent";
+
+                  "${mod}+Shift+h" = "move left";
+                  "${mod}+Shift+j" = "move down";
+                  "${mod}+Shift+k" = "move up";
+                  "${mod}+Shift+l" = "move right";
+
+                  "${mod}+Shift+x" = "kill";
+
+                  "${mod}+Return" = "exec alacritty";
+
+                  "${mod}+Shift+s" = "split h";
+                  "${mod}+Shift+v" = "split v";
+                  "${mod}+e" = "layout toggle split";
+                  "${mod}+w" = "layout tabbed";
+                  "${mod}+s" = "layout stacking";
+
+                  "${mod}+f" = "fullscreen";
+
+                  "${mod}+Shift+r" = "restart";
+
+                  "${mod}+r" = "mode resize";
+
+                  # Marks
+                  "${mod}+Shift+m" = ''exec i3-input -F "mark %s" -l 1 -P 'Mark: ' '';
+                  "${mod}+m" = ''exec i3-input -F '[con_mark="%s"] focus' -l 1 -P 'Go to: ' '';
+
+                  # Screenshots
+                  "${mod}+q" = "exec \"maim | xclip -selection clipboard -t image/png\"";
+                  "${mod}+Shift+q" = "exec \"maim -s | xclip -selection clipboard -t image/png\"";
+                  "${mod}+Ctrl+q" = "exec ${pkgs.writeShellScript "peek.sh" ''
+              ${pkgs.picom}/bin/picom &
+              picom_pid=$!
+              ${pkgs.peek}/bin/peek || true
+              kill -SIGINT $picom_pid
+            ''}";
+
+                  # Launching applications
+                  "${mod}+u" = "exec ${pkgs.writeShellScript "rofi" ''
+              rofi \
+                -modi 'combi' \
+                -combi-modi "window,drun,ssh,run" \
+                -font '${decorationFont}' \
+                -show combi
+            ''}";
+
+                  # Passwords
+                  "${mod}+p" = "exec rofi-pass -font '${decorationFont}'";
+
+                  # Edit current buffer
+                  "${mod}+v" = "exec edit-input";
+
+                  # Media
+                  "XF86AudioPlay" = "exec playerctl -p spotify play-pause";
+                  "XF86AudioNext" = "exec playerctl -p spotify next";
+                  "XF86AudioPrev" = "exec playerctl -p spotify previous";
+                  "XF86AudioRaiseVolume" = "exec pulseaudio-ctl up";
+                  "XF86AudioLowerVolume" = "exec pulseaudio-ctl down";
+                  "XF86AudioMute" = "exec pulseaudio-ctl mute";
+
+                  # Lock
+                  Pause = "exec lock";
+
+                  # Brightness
+                  "XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl -q s 5%-";
+                  "XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl -q s 5%+";
+
+                  # Sleep/hibernate
+                  # "${mod}+Escape" = "exec systemctl suspend";
+                  # "${mod}+Shift+Escape" = "exec systemctl hibernate";
+
+                  # Scratch buffer
+                  "${mod}+minus" = "scratchpad show";
+                  "${mod}+Shift+minus" = "move scratchpad";
+                  "${mod}+space" = "focus mode_toggle";
+                  "${mod}+Shift+space" = "floating toggle";
+
+                  # Screen Layout
+                  "${mod}+Shift+t" = "exec xrandr --auto";
+
+                  # Notifications
+                  "${mod}+Shift+n" = "exec killall -SIGUSR1 .dunst-wrapped";
+                  "${mod}+n" = "exec killall -SIGUSR2 .dunst-wrapped";
+                  "Control+space" = "exec ${pkgs.dunst}/bin/dunstctl close";
+                  "Control+Shift+space" = "exec ${pkgs.dunst}/bin/dunstctl close-all";
+                  "Control+grave" = "exec ${pkgs.dunst}/bin/dunstctl history-pop";
+                  "Control+Shift+period" = "exec ${pkgs.dunst}/bin/dunstctl action";
+                })
+              ]
+            );
+
+          inherit fonts;
+
+          colors = with solarized; rec {
+            focused = {
+              border = base01;
+              background = base01;
+              text = base3;
+              indicator = red;
+              childBorder = base02;
+            };
+            focusedInactive = focused // {
+              border = base03;
+              background = base03;
+              # text = base1;
+            };
+            unfocused = focusedInactive;
+            background = base03;
+          };
+
+          modes.resize = {
+            l = "resize shrink width 5 px or 5 ppt";
+            k = "resize grow height 5 px or 5 ppt";
+            j = "resize shrink height 5 px or 5 ppt";
+            h = "resize grow width 5 px or 5 ppt";
+
+            Return = "mode \"default\"";
+          };
+
+          bars = [{
+            statusCommand = "${i3status-command}";
+            inherit fonts;
+            position = "top";
+            colors = with solarized; rec {
+              background = base03;
+              statusline = base3;
+              separator = base1;
+              activeWorkspace = {
+                border = base03;
+                background = base1;
+                text = base3;
+              };
+              focusedWorkspace = activeWorkspace;
+              inactiveWorkspace = activeWorkspace // {
+                background = base01;
+              };
+              urgentWorkspace = activeWorkspace // {
+                background = red;
+              };
+            };
+          }];
+
+          window.titlebar = true;
+        };
+      };
+
+      services.dunst = {
+        enable = true;
+        settings = with solarized; {
+          global = {
+            font = "MesloLGSDZ ${toString (config.system.machine.i3FontSize * 1.5)}";
+            allow_markup = true;
+            format = "<b>%s</b>\n%b";
+            sort = true;
+            alignment = "left";
+            geometry = "600x15-40+40";
+            idle_threshold = 120;
+            separator_color = "frame";
+            separator_height = 1;
+            word_wrap = true;
+            padding = 8;
+            horizontal_padding = 8;
+            max_icon_size = 45;
+          };
+
+          frame = {
+            width = 0;
+            color = "#aaaaaa";
+          };
+
+          urgency_low = {
+            background = base03;
+            foreground = base3;
+            timeout = 5;
+          };
+
+          urgency_normal = {
+            background = base02;
+            foreground = base3;
+            timeout = 7;
+          };
+
+          urgency_critical = {
+            background = red;
+            foreground = base3;
+            timeout = 0;
+          };
+        };
+      };
+
+      gtk = {
+        enable = true;
+        iconTheme.name = "Adwaita";
+        theme.name = "Adwaita";
+      };
+    };
+}
diff --git a/users/aspen/system/home/modules/lib/cloneRepo.nix b/users/aspen/system/home/modules/lib/cloneRepo.nix
new file mode 100644
index 0000000000..f3cf59d249
--- /dev/null
+++ b/users/aspen/system/home/modules/lib/cloneRepo.nix
@@ -0,0 +1,76 @@
+{ lib, config, ... }:
+with lib;
+{
+  options = {
+    aspen.impure.clonedRepos = mkOption {
+      description = "Repositories to clone";
+      default = { };
+      type = with types; attrsOf (
+        let
+          sm = submodule {
+            options = {
+              url = mkOption {
+                type = nullOr str;
+                description = "URL of repository to clone";
+                default = null;
+              };
+
+              github = mkOption {
+                type = nullOr str;
+                description = "Github owner/repo of repository to clone";
+                default = null;
+              };
+
+              path = mkOption {
+                type = str;
+                description = "Path to clone to";
+              };
+
+              onClone = mkOption {
+                type = str;
+                description = ''
+                  Shell command to run after cloning the repo for the first time.
+                  Runs inside the repo itself.
+                '';
+                default = "";
+              };
+
+              after = mkOption {
+                type = listOf str;
+                description = "Activation hooks that this repository must be cloned after";
+                default = [ ];
+              };
+            };
+          };
+        in
+        addCheck sm (cr: (! isNull cr.url || ! isNull cr.github))
+      );
+    };
+  };
+
+  config = {
+    home.activation =
+      mapAttrs
+        (_: { url
+            , path
+            , github
+            , onClone
+            , after
+            , ...
+            }:
+          let repoURL = if isNull url then "git@github.com:${github}" else url;
+          in hm.dag.entryAfter ([ "writeBoundary" ] ++ after) ''
+            $DRY_RUN_CMD mkdir -p $(dirname "${path}")
+            if [[ ! -d ${path} ]]; then
+              if $DRY_RUN_CMD git clone "${repoURL}" "${path}"; then
+                pushd ${path}
+                $DRY_RUN_CMD ${onClone}
+                popd
+              else
+                echo "Git repository ${path} failed to clone"
+              fi
+            fi
+          '')
+        config.aspen.impure.clonedRepos;
+  };
+}
diff --git a/users/aspen/system/home/modules/lib/zshFunctions.nix b/users/aspen/system/home/modules/lib/zshFunctions.nix
new file mode 100644
index 0000000000..228dc6379f
--- /dev/null
+++ b/users/aspen/system/home/modules/lib/zshFunctions.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  options = {
+    programs.zsh.functions = mkOption {
+      description = "An attribute set that maps function names to their source";
+      default = { };
+      type = with types; attrsOf (either str path);
+    };
+  };
+
+  config.programs.zsh.initExtra = concatStringsSep "\n" (
+    mapAttrsToList
+      (name: funSrc: ''
+        function ${name}() {
+          ${funSrc}
+        }
+      '')
+      config.programs.zsh.functions
+  );
+}
diff --git a/users/aspen/system/home/modules/obs.nix b/users/aspen/system/home/modules/obs.nix
new file mode 100644
index 0000000000..7962320f8a
--- /dev/null
+++ b/users/aspen/system/home/modules/obs.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (pkgs) obs-studio;
+  obs-input-overlay = pkgs.obs-studio-plugins.input-overlay;
+in
+
+{
+  home.packages = [
+    obs-studio
+    obs-input-overlay
+  ];
+
+  xdg.configFile."obs-studio/plugins/input-overlay/bin/64bit/input-overlay.so".source =
+    "${obs-input-overlay}/lib/obs-plugins/input-overlay.so";
+  xdg.configFile."obs-studio/plugins/input-overlay/data".source =
+    "${obs-input-overlay}/share/obs/obs-plugins/input-overlay";
+}
diff --git a/users/grfn/system/home/modules/ptt.nix b/users/aspen/system/home/modules/ptt.nix
index 436c8f2617..436c8f2617 100644
--- a/users/grfn/system/home/modules/ptt.nix
+++ b/users/aspen/system/home/modules/ptt.nix
diff --git a/users/grfn/system/home/modules/pure.zsh-theme b/users/aspen/system/home/modules/pure.zsh-theme
index b4776e8159..666e28259c 100755
--- a/users/grfn/system/home/modules/pure.zsh-theme
+++ b/users/aspen/system/home/modules/pure.zsh-theme
@@ -92,6 +92,10 @@ prompt_pure_nix_info() {
 }
 
 prompt_pure_precmd() {
+	if [[ "$TERM" == "dumb" ]]; then
+		return
+	fi
+
 	# shows the full path in the title
 	print -Pn '\e]0;%~\a'
 
diff --git a/users/aspen/system/home/modules/rtlsdr.nix b/users/aspen/system/home/modules/rtlsdr.nix
new file mode 100644
index 0000000000..c8a404a1f4
--- /dev/null
+++ b/users/aspen/system/home/modules/rtlsdr.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  nixpkgs-gnuradio = import
+    (pkgs.fetchFromGitHub {
+      owner = "doronbehar";
+      repo = "nixpkgs";
+      rev = "712561aa5f10bfe6112a1726a912585612a70d1f";
+      sha256 = "04yqflbwjcfl9vlplphpj82csqqz9k6m3nj1ybhwgmsc4by7vivl";
+    })
+    { };
+
+in
+
+{
+  home.packages = with pkgs; [
+    rtl-sdr
+    nixpkgs-gnuradio.gnuradio
+    nixpkgs-gnuradio.gnuradio.plugins.osmosdr
+    nixpkgs-gnuradio.gqrx
+  ];
+}
diff --git a/users/aspen/system/home/modules/shell.nix b/users/aspen/system/home/modules/shell.nix
new file mode 100644
index 0000000000..844f76c286
--- /dev/null
+++ b/users/aspen/system/home/modules/shell.nix
@@ -0,0 +1,166 @@
+{ config, lib, pkgs, ... }:
+let
+  shellAliases = rec {
+    # NixOS stuff
+    ncg = "nix-collect-garbage";
+
+    # Nix
+    ns = "nix-shell";
+    nb = "nix build -f .";
+    nbl = "nix build -f . --builders ''"; # nix build local
+    lwo = "lorri watch --once";
+
+    # Docker and friends
+    "dcu" = "docker-compose up";
+    "dcud" = "docker-compose up -d";
+    "dc" = "docker-compose";
+    "dcr" = "docker-compose restart";
+    "dclf" = "docker-compose logs -f";
+    "dck" = "docker";
+    "dockerclean" = "dockercleancontainers && dockercleanimages";
+    "dockercleanimages" =
+      "docker images -a --no-trunc | grep none | awk '{print $$3}' | xargs -L 1 -r docker rmi";
+    "dockercleancontainers" =
+      "docker ps -a --no-trunc| grep 'Exit' | awk '{print $$1}' | xargs -L 1 -r docker rm";
+
+    # Directories
+    stck = "dirs -v";
+    b = "cd ~1";
+    ".." = "cd ..";
+    "..." = "cd ../..";
+    "...." = "cd ../../..";
+    "....." = "cd ../../../..";
+
+    # Aliases from old config
+    "http" = "http --style solarized";
+    "grep" = "grep $GREP_OPTIONS";
+    "df" = "df -h";
+    "ll" = "ls -al";
+    "la" = "ls -a";
+  };
+in
+{
+  home.packages = with pkgs; [ zsh autojump ];
+
+  home.sessionVariables = {
+    EDITOR = "vim";
+    LS_COLORS =
+      "no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.ogg=01;35:*.mp3=01;35:*.wav=01;35:";
+    BROWSER = "firefox";
+    BAT_THEME = "ansi-light";
+  };
+
+  programs.bash = {
+    enable = true;
+    inherit shellAliases;
+  };
+
+  programs.zsh = {
+    enable = true;
+    autosuggestion.enable = true;
+    autocd = true;
+
+    inherit shellAliases;
+
+    history = rec {
+      save = 100000;
+      size = save;
+    };
+
+    oh-my-zsh = {
+      enable = true;
+
+      plugins = [
+        "battery"
+        "colorize"
+        "command-not-found"
+        "github"
+        "gitignore"
+        "postgres"
+        "systemd"
+        "themes"
+        "vi-mode"
+      ];
+
+      custom = "${pkgs.stdenv.mkDerivation {
+        name = "oh-my-zsh-custom";
+        unpackPhase = ":";
+        installPhase = ''
+          mkdir -p $out/themes
+          mkdir -p $out/custom/plugins
+          ln -s ${./pure.zsh-theme} $out/themes/pure.zsh-theme
+        '';
+      }}";
+
+      theme = "pure";
+    };
+
+    plugins = [{
+      name = "pure-theme";
+      src = pkgs.fetchFromGitHub {
+        owner = "sindresorhus";
+        repo = "pure";
+        rev = "0a92b02dd4172f6c64fdc9b81fe6cd4bddb0a23b";
+        sha256 = "0l8jqhmmjn7p32hdjnv121xsjnqd2c0plhzgydv2yzrmqgyvx7cc";
+      };
+    }];
+
+    initExtraFirst = ''
+      if [[ "$TERM" = "dumb" ]]; then
+        return
+      fi
+    '';
+
+    initExtraBeforeCompInit = ''
+      zstyle ':completion:*' completer _complete _ignored _correct _approximate
+      zstyle ':completion:*' matcher-list \'\' 'm:{[:lower:]}={[:upper:]} m:{[:lower:][:upper:]}={[:upper:][:lower:]} r:|[._- :]=** r:|=**' 'l:|=* r:|=*'
+      zstyle ':completion:*' max-errors 5
+      zstyle ':completion:*' use-cache yes
+      zstyle ':completion::complete:grunt::options:' expire 1
+      zstyle ':completion:*' prompt '%e errors'
+      # zstyle :compinstall filename '~/.zshrc'
+      autoload -Uz compinit
+    '';
+
+    initExtra = ''
+      if [[ "$TERM" != "dumb" ]]; then
+        source ${./zshrc}
+        source ${
+          pkgs.fetchFromGitHub {
+            owner = "zsh-users";
+            repo = "zsh-syntax-highlighting";
+            rev = "7678a8a22780141617f809002eeccf054bf8f448";
+            sha256 = "0xh4fbd54kvwwpqvabk8lpw7m80phxdzrd75q3y874jw0xx1a9q6";
+          }
+        }/zsh-syntax-highlighting.zsh
+        source ${pkgs.autojump}/share/autojump/autojump.zsh
+        source ${
+          pkgs.fetchFromGitHub {
+            owner = "chisui";
+            repo = "zsh-nix-shell";
+            rev = "a65382a353eaee5a98f068c330947c032a1263bb";
+            sha256 = "0l41ac5b7p8yyjvpfp438kw7zl9dblrpd7icjg1v3ig3xy87zv0n";
+          }
+        }/nix-shell.plugin.zsh
+
+        export RPS1=""
+        autoload -U promptinit; promptinit
+        prompt pure
+      fi
+
+      if [[ "$TERM" == "dumb" ]]; then
+        unsetopt zle
+        unsetopt prompt_cr
+        unsetopt prompt_subst
+        unset zle_bracketed_paste
+        export PS1='$ '
+      fi
+    '';
+  };
+
+  programs.fzf = {
+    enable = true;
+    enableBashIntegration = true;
+    enableZshIntegration = true;
+  };
+}
diff --git a/users/aspen/system/home/modules/tarsnap.nix b/users/aspen/system/home/modules/tarsnap.nix
new file mode 100644
index 0000000000..fb2a050852
--- /dev/null
+++ b/users/aspen/system/home/modules/tarsnap.nix
@@ -0,0 +1,64 @@
+{ config, lib, pkgs, ... }:
+
+{
+  home.packages = with pkgs; [
+    tarsnap
+  ];
+
+  home.file.".tarsnaprc".text = ''
+    ### Recommended options
+
+    # Tarsnap cache directory
+    cachedir /home/aspen/.cache/tarsnap
+
+    # Tarsnap key file
+    keyfile /home/aspen/.private/tarsnap.key
+
+    # Don't archive files which have the nodump flag set.
+    nodump
+
+    # Print statistics when creating or deleting archives.
+    print-stats
+
+    # Create a checkpoint once per GB of uploaded data.
+    checkpoint-bytes 1G
+
+    ### Commonly useful options
+
+    # Use SI prefixes to make numbers printed by --print-stats more readable.
+    humanize-numbers
+
+    ### Other options, not applicable to most systems
+
+    # Aggressive network behaviour: Use multiple TCP connections when
+    # writing archives.  Use of this option is recommended only in
+    # cases where TCP congestion control is known to be the limiting
+    # factor in upload performance.
+    #aggressive-networking
+
+    # Exclude files and directories matching specified patterns.
+    # Only one file or directory per command; multiple "exclude"
+    # commands may be given.
+    #exclude
+
+    # Include only files and directories matching specified patterns.
+    # Only one file or directory per command; multiple "include"
+    # commands may be given.
+    #include
+
+    # Attempt to reduce tarsnap memory consumption.  This option
+    # will slow down the process of creating archives, but may help
+    # on systems where the average size of files being backed up is
+    # less than 1 MB.
+    #lowmem
+
+    # Try even harder to reduce tarsnap memory consumption.  This can
+    # significantly slow down tarsnap, but reduces its memory usage
+    # by an additional factor of 2 beyond what the lowmem option does.
+    #verylowmem
+
+    # Snapshot time.  Use this option if you are backing up files
+    # from a filesystem snapshot rather than from a "live" filesystem.
+    #snaptime <file>
+  '';
+}
diff --git a/users/aspen/system/home/modules/tmux.nix b/users/aspen/system/home/modules/tmux.nix
new file mode 100644
index 0000000000..adbaa02f32
--- /dev/null
+++ b/users/aspen/system/home/modules/tmux.nix
@@ -0,0 +1,42 @@
+{ config, lib, pkgs, ... }:
+
+{
+  programs.tmux = {
+    enable = true;
+    customPaneNavigationAndResize = true;
+    keyMode = "vi";
+    newSession = true;
+    prefix = "C-a";
+    shell = "${pkgs.zsh}/bin/zsh";
+    shortcut = "a";
+
+    extraConfig = ''
+      set -g status-bg "colour0"
+      set -g message-command-fg "colour7"
+      set -g status-justify "centre"
+      set -g status-left-length "100"
+      set -g status "on"
+      set -g pane-active-border-fg "colour14"
+      set -g message-bg "colour11"
+      set -g status-right-length "100"
+      set -g status-right-attr "none"
+      set -g message-fg "colour7"
+      set -g message-command-bg "colour11"
+      set -g status-attr "none"
+      # set -g status-utf8 "on"
+      set -g pane-border-fg "colour11"
+      set -g status-left-attr "none"
+      setw -g window-status-fg "colour10"
+      setw -g window-status-attr "none"
+      setw -g window-status-activity-bg "colour0"
+      setw -g window-status-activity-attr "none"
+      setw -g window-status-activity-fg "colour14"
+      setw -g window-status-separator ""
+      setw -g window-status-bg "colour0"
+      set -g status-left "#[fg=colour15,bg=colour14,bold] #S #[fg=colour14,bg=colour11,nobold,nounderscore,noitalics]ξ‚°#[fg=colour7,bg=colour11] #F #[fg=colour11,bg=colour0,nobold,nounderscore,noitalics]ξ‚°#[fg=colour10,bg=colour0] #W #[fg=colour0,bg=colour0,nobold,nounderscore,noitalics]ξ‚°"
+      set -g status-right "#{battery_status_bg} Batt: #{battery_percentage} #{battery_remain} | #[fg=colour0,bg=colour0,nobold,nounderscore,noitalics]ξ‚²#[fg=colour10,bg=colour0] %a #[fg=colour11,bg=colour0,nobold,nounderscore,noitalics]ξ‚²#[fg=colour7,bg=colour11] %b %d ξ‚³ %R #[fg=colour14,bg=colour11,nobold,nounderscore,noitalics]ξ‚²#[fg=colour15,bg=colour14] #H "
+      setw -g window-status-format "#[fg=colour0,bg=colour0,nobold,nounderscore,noitalics]ξ‚°#[default] #I ξ‚± #W #[fg=colour0,bg=colour0,nobold,nounderscore,noitalics]ξ‚°"
+      setw -g window-status-current-format "#[fg=colour0,bg=colour11,nobold,nounderscore,noitalics]ξ‚°#[fg=colour7,bg=colour11] #I ξ‚± #W #[fg=colour11,bg=colour0,nobold,nounderscore,noitalics]ξ‚°"
+    '';
+  };
+}
diff --git a/users/grfn/system/home/modules/twitter.nix b/users/aspen/system/home/modules/twitter.nix
index 3cb2e90adc..ab5647e418 100644
--- a/users/grfn/system/home/modules/twitter.nix
+++ b/users/aspen/system/home/modules/twitter.nix
@@ -1,6 +1,10 @@
 { pkgs, lib, ... }:
 
 {
+  imports = [
+    ./lib/zshFunctions.nix
+  ];
+
   home.packages = with pkgs; [
     t
   ];
diff --git a/users/grfn/system/home/modules/vim.nix b/users/aspen/system/home/modules/vim.nix
index b87cb09ad1..b87cb09ad1 100644
--- a/users/grfn/system/home/modules/vim.nix
+++ b/users/aspen/system/home/modules/vim.nix
diff --git a/users/grfn/system/home/modules/vimrc b/users/aspen/system/home/modules/vimrc
index 3e33b5e2be..01572f3946 100644
--- a/users/grfn/system/home/modules/vimrc
+++ b/users/aspen/system/home/modules/vimrc
@@ -22,7 +22,7 @@ set expandtab
 set noerrorbells visualbell t_vb=
 set laststatus=2
 set hidden
-let mapleader = ','
+let mapleader = ' '
 let maplocalleader = '\'
 set undofile
 " set undodir=~/.vim/undo
diff --git a/users/grfn/system/home/modules/zshrc b/users/aspen/system/home/modules/zshrc
index a12173d684..a12173d684 100644
--- a/users/grfn/system/home/modules/zshrc
+++ b/users/aspen/system/home/modules/zshrc
diff --git a/users/grfn/system/home/platforms/darwin.nix b/users/aspen/system/home/platforms/darwin.nix
index cf0375e941..f98b80f269 100644
--- a/users/grfn/system/home/platforms/darwin.nix
+++ b/users/aspen/system/home/platforms/darwin.nix
@@ -10,7 +10,7 @@ with lib;
       pinentry_mac
     ];
 
-    home.activation.linkApplications = lib.hm.dag.entryAfter ["writeBoundary"] ''
+    home.activation.linkApplications = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
       $DRY_RUN_CMD ln -sf $VERBOSE_ARG \
         ~/.nix-profile/Applications/* ~/Applications/
     '';
diff --git a/users/aspen/system/home/platforms/linux.nix b/users/aspen/system/home/platforms/linux.nix
new file mode 100644
index 0000000000..d7a04e872d
--- /dev/null
+++ b/users/aspen/system/home/platforms/linux.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, ... }:
+
+let
+
+  depot = config.lib.depot;
+
+in
+{
+  imports = [
+    ../modules/alacritty.nix
+    ../modules/development.nix
+    ../modules/emacs.nix
+    ../modules/email.nix
+    ../modules/firefox.nix
+    ../modules/shell.nix
+    ../modules/vim.nix
+  ];
+
+  xsession.enable = true;
+
+  home.packages = with pkgs; [
+    # Desktop stuff
+    arandr
+    firefox
+    feh
+    chromium
+    xclip
+    xorg.xev
+    picom
+    peek
+    signal-desktop
+    apvlv # pdf viewer
+    vlc
+    irssi
+    gnutls
+    pandoc
+    barrier
+    gimp # TODO(aspen): use glimpse once it build again
+
+    # System utilities
+    powertop
+    usbutils
+    pciutils
+    gdmap
+    lsof
+    tree
+    nmap
+    iftop
+
+    # Security
+    gnupg
+    keybase
+    openssl
+    yubikey-manager
+    # TODO(aspen): lagging behind yubikey-manager and doesn't support cryptography >= 39
+    # yubikey-manager-qt
+
+    # Spotify...etc
+    spotify
+    playerctl
+  ];
+
+  services.redshift = {
+    enable = true;
+    provider = "geoclue2";
+  };
+
+  services.pasystray.enable = true;
+
+  services.gpg-agent = {
+    enable = true;
+    pinentryPackage = pkgs.pinentry-qt;
+  };
+
+  services.lorri.enable = true;
+
+  services.dropbox = { enable = true; };
+}
diff --git a/users/grfn/system/install b/users/aspen/system/install
index a9a45953da..a9a45953da 100755
--- a/users/grfn/system/install
+++ b/users/aspen/system/install
diff --git a/users/riking/dotfiles/regolith/flags/show-shortcuts b/users/aspen/system/system/.skip-subtree
index e69de29bb2..e69de29bb2 100644
--- a/users/riking/dotfiles/regolith/flags/show-shortcuts
+++ b/users/aspen/system/system/.skip-subtree
diff --git a/users/grfn/system/system/configuration.nix b/users/aspen/system/system/configuration.nix
index eae567015b..eae567015b 100644
--- a/users/grfn/system/system/configuration.nix
+++ b/users/aspen/system/system/configuration.nix
diff --git a/users/aspen/system/system/default.nix b/users/aspen/system/system/default.nix
new file mode 100644
index 0000000000..07bc886c6c
--- /dev/null
+++ b/users/aspen/system/system/default.nix
@@ -0,0 +1,48 @@
+args @ { depot, pkgs, ... }:
+
+rec {
+  mugwump = import ./machines/mugwump.nix;
+
+  mugwumpSystem = (depot.ops.nixos.nixosFor mugwump).system;
+
+  roswell = import ./machines/roswell.nix;
+
+  roswellSystem = (depot.ops.nixos.nixosFor ({ ... }: {
+    imports = [
+      ./machines/roswell.nix
+      "${pkgs.home-manager.src}/nixos"
+    ];
+
+    # Use the same nixpkgs as everything else
+    home-manager.useGlobalPkgs = true;
+
+    home-manager.users.aspen = { config, lib, ... }: {
+      imports = [ ../home/machines/roswell.nix ];
+      lib.depot = depot;
+    };
+  })).system;
+
+  ogopogo = import ./machines/ogopogo.nix;
+
+  ogopogoSystem = (depot.ops.nixos.nixosFor ogopogo).system;
+
+  yeren = import ./machines/yeren.nix;
+
+  yerenSystem = (depot.ops.nixos.nixosFor yeren).system;
+
+  lusca = import ./machines/lusca.nix;
+
+  luscaSystem = (depot.ops.nixos.nixosFor lusca).system;
+
+  iso = import ./iso.nix args;
+
+  meta.ci.targets = [
+    "mugwumpSystem"
+    "roswellSystem"
+    "luscaSystem"
+    "ogopogoSystem"
+    "yerenSystem"
+
+    "iso"
+  ];
+}
diff --git a/users/aspen/system/system/iso.nix b/users/aspen/system/system/iso.nix
new file mode 100644
index 0000000000..ef5d3ed78b
--- /dev/null
+++ b/users/aspen/system/system/iso.nix
@@ -0,0 +1,22 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  configuration = { ... }: {
+    imports = [
+      (pkgs.path + "/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix")
+      (pkgs.path + "/nixos/modules/installer/cd-dvd/channel.nix")
+    ];
+
+    networking.networkmanager.enable = true;
+    networking.useDHCP = false;
+    networking.firewall.enable = false;
+    networking.wireless.enable = lib.mkForce false;
+
+    # TODO(aspen): enabling this (in the minimal profile) fails the iso build,
+    # since gtk+3 needs to be built which fails due to cairo without xlibs
+    environment.noXlibs = false;
+  };
+in
+(depot.third_party.nixos {
+  inherit configuration;
+}).config.system.build.isoImage
diff --git a/users/grfn/system/system/machines/bumblebee.nix b/users/aspen/system/system/machines/bumblebee.nix
index 0fec214092..8bb52f75f0 100644
--- a/users/grfn/system/system/machines/bumblebee.nix
+++ b/users/aspen/system/system/machines/bumblebee.nix
@@ -19,5 +19,5 @@
     thresholdPercentage = 5;
   };
 
-  services.xserver.xkbOptions = "caps:swapescape";
+  services.xserver.xkb.options = "caps:swapescape";
 }
diff --git a/users/aspen/system/system/machines/lusca.nix b/users/aspen/system/system/machines/lusca.nix
new file mode 100644
index 0000000000..782d504aa9
--- /dev/null
+++ b/users/aspen/system/system/machines/lusca.nix
@@ -0,0 +1,142 @@
+{ depot, modulesPath, config, lib, pkgs, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+    ../modules/common.nix
+    ../modules/laptop.nix
+    ../modules/xserver.nix
+    ../modules/fonts.nix
+    ../modules/sound.nix
+    ../modules/tvl.nix
+    ../modules/development.nix
+  ];
+
+  networking.hostName = "lusca";
+
+  system.stateVersion = "24.05";
+
+  time.timeZone = "America/New_York";
+
+  services.avahi = {
+    enable = true;
+    nssmdns4 = true;
+  };
+
+  boot = {
+    initrd = {
+      availableKernelModules =
+        [ "nvme" "xhci_pci" "thunderbolt" "usb_storage" "sd_mod" ];
+      kernelModules = [ ];
+
+      luks.devices."cryptroot".device =
+        "/dev/disk/by-uuid/9e525746-5bca-4451-8710-a6f0e09b751c";
+    };
+
+    kernelModules = [ "kvm-amd" ];
+
+    kernelParams = [
+      "resume=LABEL=SWAP"
+      "resume_offset=795904" # sudo btrfs inspect-internal map-swapfile -r /swap/swapfile
+    ];
+
+    resumeDevice = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+
+    kernel.sysctl = { "kernel.perf_event_paranoid" = -1; };
+  };
+
+  hardware.cpu.amd.updateMicrocode =
+    lib.mkDefault config.hardware.enableRedistributableFirmware;
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+      fsType = "btrfs";
+      options = [ "subvol=root" ];
+    };
+
+    "/home" = {
+      device = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+      fsType = "btrfs";
+      options = [ "subvol=home" ];
+    };
+
+    "/nix" = {
+      device = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+      fsType = "btrfs";
+      options = [ "subvol=nix" ];
+    };
+
+    "/swap" = {
+      device = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+      fsType = "btrfs";
+      options = [ "subvol=swap" ];
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/0E7D-3C3F";
+      fsType = "vfat";
+    };
+  };
+
+  swapDevices = [{ device = "/swap/swapfile"; }];
+
+  systemd.sleep.extraConfig = ''
+    HibernateDelaySec=30m
+    SuspendState=mem
+  '';
+
+  services.earlyoom = {
+    enable = true;
+    freeMemThreshold = 5;
+  };
+
+  services.tailscale.enable = true;
+
+  services.fwupd = {
+    enable = true;
+    extraRemotes = [ "lvfs-testing" ];
+  };
+
+  services.tlp.enable = lib.mkForce false;
+  services.power-profiles-daemon.enable = true;
+
+  services.thermald.enable = true;
+
+  services.fprintd.enable = true;
+  security.pam.services = {
+    login.fprintAuth = true;
+    sudo.fprintAuth = true;
+    i3lock.fprintAuth = true;
+    i3lock-color.fprintAuth = true;
+    lightdm.fprintAuth = true;
+    lightdm-greeter.fprintAuth = true;
+  };
+
+  security.polkit.extraConfig = ''
+    polkit.addRule(function(action, subject) {
+      if (action.id.indexOf("net.reactivated.fprint.") == 0 || action.id.indexOf("net.reactivated.Fprint.") == 0) {
+          polkit.log("action=" + action);
+          polkit.log("subject=" + subject);
+          return polkit.Result.YES;
+      }
+    });
+  '';
+
+  services.udev.extraRules = ''
+    # Ethernet expansion card support
+    ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0bda", ATTR{idProduct}=="8156", ATTR{power/autosuspend}="20"
+  '';
+
+  hardware.sensor.iio.enable = true;
+
+  hardware.opengl.driSupport32Bit = true;
+
+  # TPM
+  security.tpm2 = {
+    enable = true;
+    pkcs11.enable = true;
+    tctiEnvironment.enable = true;
+  };
+  users.users.aspen.extraGroups = [ "tss" ];
+}
diff --git a/users/aspen/system/system/machines/mugwump.nix b/users/aspen/system/system/machines/mugwump.nix
new file mode 100644
index 0000000000..4cfa117134
--- /dev/null
+++ b/users/aspen/system/system/machines/mugwump.nix
@@ -0,0 +1,306 @@
+{ config, lib, pkgs, modulesPath, depot, ... }:
+
+with lib;
+
+{
+  imports = [
+    ../modules/common.nix
+    (modulesPath + "/installer/scan/not-detected.nix")
+    (depot.path.origSrc + "/ops/modules/prometheus-fail2ban-exporter.nix")
+    (depot.path.origSrc + "/users/aspen/xanthous/server/module.nix")
+    (depot.third_party.agenix.src + "/modules/age.nix")
+    depot.third_party.ddclient.module
+  ];
+
+  networking.hostName = "mugwump";
+
+  system.stateVersion = "22.05";
+
+  boot = {
+    loader.systemd-boot.enable = true;
+
+    kernelModules = [ "kvm-intel" ];
+    extraModulePackages = [ ];
+
+    initrd = {
+      availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
+      kernelModules = [
+        "uas"
+        "usbcore"
+        "usb_storage"
+        "vfat"
+        "nls_cp437"
+        "nls_iso8859_1"
+      ];
+
+      postDeviceCommands = pkgs.lib.mkBefore ''
+        mkdir -m 0755 -p /key
+        sleep 2
+        mount -n -t vfat -o ro `findfs UUID=9048-A9D5` /key
+      '';
+
+      luks.devices."cryptroot" = {
+        device = "/dev/disk/by-uuid/803a9028-339c-4617-a213-4fe138161f6d";
+        keyFile = "/key/keyfile";
+        preLVM = false;
+      };
+    };
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/mapper/cryptroot";
+      fsType = "btrfs";
+    };
+    "/boot" = {
+      device = "/dev/disk/by-uuid/7D74-0E4B";
+      fsType = "vfat";
+    };
+  };
+
+  networking.interfaces = {
+    enp0s25.useDHCP = false;
+    wlp2s0.useDHCP = false;
+  };
+
+  networking.firewall.enable = true;
+  networking.firewall.allowedTCPPorts = [ 22 80 443 ];
+
+  security.sudo.extraRules = [{
+    groups = [ "wheel" ];
+    commands = [{ command = "ALL"; options = [ "NOPASSWD" ]; }];
+  }];
+
+  nix.gc.dates = "monthly";
+
+  users.users.aspen.openssh.authorizedKeys.keys = [
+    depot.users.aspen.keys.whitby
+  ];
+
+  age.secrets =
+    let
+      secret = name: depot.users.aspen.secrets."${name}.age";
+    in
+    {
+      cloudflare.file = secret "cloudflare";
+      ddclient-password.file = secret "ddclient-password";
+
+      buildkite-ssh-key = {
+        file = secret "buildkite-ssh-key";
+        group = "keys";
+        mode = "0440";
+      };
+
+      buildkite-token = {
+        file = secret "buildkite-token";
+        group = "keys";
+        mode = "0440";
+      };
+
+      windtunnel-bot-github-token = {
+        file = secret "windtunnel-bot-github-token";
+        group = "keys";
+        mode = "0440";
+      };
+    };
+
+  services.fail2ban = {
+    enable = true;
+    ignoreIP = [
+      "172.16.0.0/16"
+    ];
+  };
+
+  services.openssh = {
+    allowSFTP = false;
+    settings = {
+      PasswordAuthentication = false;
+      PermitRootLogin = "no";
+    };
+  };
+
+  services.grafana = {
+    enable = true;
+    dataDir = "/var/lib/grafana";
+
+    settings = {
+      server = {
+        http_port = 3000;
+        root_url = "https://metrics.gws.fyi";
+        domain = "metrics.gws.fyi";
+      };
+      analytics.reporting_enabled = false;
+    };
+
+    provision = {
+      enable = true;
+      datasources.settings.datasources = [{
+        name = "Prometheus";
+        type = "prometheus";
+        url = "http://localhost:9090";
+      }];
+    };
+  };
+
+  security.acme.defaults.email = "root@gws.fyi";
+  security.acme.acceptTerms = true;
+
+  services.nginx = {
+    enable = true;
+    statusPage = true;
+    recommendedGzipSettings = true;
+    recommendedOptimisation = true;
+    recommendedTlsSettings = true;
+    recommendedProxySettings = true;
+
+    virtualHosts = {
+      "metrics.gws.fyi" = {
+        enableACME = true;
+        forceSSL = true;
+        locations."/" = {
+          proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
+        };
+      };
+    };
+  };
+
+  services.deprecated-ddclient = {
+    package = depot.third_party.ddclient;
+    enable = true;
+    domains = [ "home.gws.fyi" ];
+    interval = "1d";
+    zone = "gws.fyi";
+    protocol = "cloudflare";
+    username = "root@gws.fyi";
+    passwordFile = config.age.secretsDir + "/ddclient-password";
+    quiet = true;
+  };
+
+  security.acme.certs."metrics.gws.fyi" = {
+    dnsProvider = "cloudflare";
+    credentialsFile = config.age.secretsDir + "/cloudflare";
+    webroot = mkForce null;
+  };
+
+  services.prometheus = {
+    enable = true;
+    exporters = {
+      node = {
+        enable = true;
+        openFirewall = false;
+
+        enabledCollectors = [
+          "processes"
+          "systemd"
+          "tcpstat"
+          "wifi"
+        ];
+      };
+
+      nginx = {
+        enable = true;
+        openFirewall = true;
+        sslVerify = false;
+        constLabels = [ "host=mugwump" ];
+      };
+
+      blackbox = {
+        enable = true;
+        openFirewall = true;
+        configFile = pkgs.writeText "blackbox-exporter.yaml" (builtins.toJSON {
+          modules = {
+            https_2xx = {
+              prober = "http";
+              http = {
+                method = "GET";
+                fail_if_ssl = false;
+                fail_if_not_ssl = true;
+                preferred_ip_protocol = "ip4";
+              };
+            };
+          };
+        });
+      };
+    };
+
+    scrapeConfigs = [
+      {
+        job_name = "node";
+        scrape_interval = "5s";
+        static_configs = [{
+          targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ];
+        }];
+      }
+      {
+        job_name = "nginx";
+        scrape_interval = "5s";
+        static_configs = [{
+          targets = [ "localhost:${toString config.services.prometheus.exporters.nginx.port}" ];
+        }];
+      }
+      {
+        job_name = "xanthous_server";
+        scrape_interval = "1s";
+        static_configs = [{
+          targets = [ "localhost:${toString config.services.xanthous-server.metricsPort}" ];
+        }];
+      }
+      {
+        job_name = "blackbox";
+        metrics_path = "/probe";
+        params.module = [ "https_2xx" ];
+        scrape_interval = "5s";
+        static_configs = [{
+          targets = [
+            "https://gws.fyi"
+            "https://windtunnel.ci"
+            "https://app.windtunnel.ci"
+            "https://metrics.gws.fyi"
+          ];
+        }];
+        relabel_configs = [{
+          source_labels = [ "__address__" ];
+          target_label = "__param_target";
+        }
+          {
+            source_labels = [ "__param_target" ];
+            target_label = "instance";
+          }
+          {
+            target_label = "__address__";
+            replacement = "localhost:${toString config.services.prometheus.exporters.blackbox.port}";
+          }];
+      }
+    ];
+  };
+
+  services.xanthous-server.enable = true;
+
+  virtualisation.docker = {
+    enable = true;
+    storageDriver = "btrfs";
+  };
+
+  services.buildkite-agents = listToAttrs (map
+    (n: rec {
+      name = "mugwump-${toString n}";
+      value = {
+        inherit name;
+        enable = true;
+        tokenPath = config.age.secretsDir + "/buildkite-token";
+        privateSshKeyPath = config.age.secretsDir + "/buildkite-ssh-key";
+        runtimePackages = with pkgs; [
+          docker
+          nix
+          gnutar
+          gzip
+        ];
+      };
+    })
+    (range 1 1));
+
+  users.users."buildkite-agent-mugwump-1" = {
+    isSystemUser = true;
+    extraGroups = [ "docker" "keys" ];
+  };
+}
diff --git a/users/aspen/system/system/machines/ogopogo.nix b/users/aspen/system/system/machines/ogopogo.nix
new file mode 100644
index 0000000000..e80a0906db
--- /dev/null
+++ b/users/aspen/system/system/machines/ogopogo.nix
@@ -0,0 +1,107 @@
+{ depot, modulesPath, config, lib, pkgs, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+    (depot.third_party.agenix.src + "/modules/age.nix")
+    ../modules/common.nix
+    ../modules/xserver.nix
+    ../modules/fonts.nix
+    ../modules/sound.nix
+    ../modules/tvl.nix
+    ../modules/development.nix
+    ../modules/wireshark.nix
+  ];
+
+  networking.hostName = "ogopogo";
+
+  system.stateVersion = "22.11";
+
+  boot = {
+    initrd = {
+      availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
+      kernelModules = [ ];
+    };
+
+    kernelModules = [ "kvm-amd" ];
+    blacklistedKernelModules = [ ];
+    extraModulePackages = [ ];
+
+    kernel.sysctl = {
+      "kernel.perf_event_paranoid" = -1;
+    };
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/disk/by-uuid/d67506cf-7039-484d-97c0-00321a7858dc";
+      fsType = "ext4";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/AE73-03A3";
+      fsType = "vfat";
+    };
+
+    "/data" = {
+      device = "/dev/disk/by-uuid/03e0f4dc-9778-42e2-a59e-45522610e509";
+      fsType = "ext4";
+    };
+  };
+
+  swapDevices = [{
+    device = "/dev/disk/by-uuid/8bdae7c8-5160-491f-8cd0-4f0a79acadf9";
+  }];
+
+  services.earlyoom = {
+    enable = true;
+    freeMemThreshold = 5;
+  };
+
+  hardware.enableAllFirmware = true;
+
+  hardware.pulseaudio.extraConfig = ''
+    load-module module-remap-source source_name=KompleteAudio6_1 source_properties=device.description=KompleteAudio6Input1 master=alsa_input.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.multichannel-input remix=no channels=1 master_channel_map=front-left channel_map=mono
+    load-module module-remap-source source_name=KompleteAudio6_2 source_properties=device.description=KompleteAudio6Input2 master=alsa_input.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.multichannel-input remix=no channels=1 master_channel_map=front-right channel_map=mono
+    load-module module-remap-sink sink_name=KompleteAudio6_12 sink_properties=device.description=KompleteAudio6_12 remix=no master=alsa_output.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.analog-surround-21 channels=2 master_channel_map=front-left,front-right channel_map=front-left,front-right
+  '';
+
+  services.fwupd.enable = true;
+
+  services.tailscale.enable = true;
+
+  hardware.keyboard.zsa.enable = true;
+
+  # Nvidia
+  services.xserver = {
+    videoDrivers = [ "nvidia" ];
+    dpi = 100;
+  };
+  hardware.opengl.enable = true;
+  services.picom = {
+    enable = true;
+    vSync = true;
+  };
+  hardware.opengl.driSupport32Bit = true;
+
+  services.postgresql = {
+    enable = true;
+    enableTCPIP = true;
+    authentication = "host all all 0.0.0.0/0 md5";
+    dataDir = "/data/postgresql";
+    package = pkgs.postgresql_15;
+    port = 5431;
+    settings = {
+      wal_level = "logical";
+    };
+  };
+
+  nix.settings.substituters = [ "ssh://grfn@172.16.0.5" ];
+  nix.settings.trusted-substituters = [ "ssh://grfn@172.16.0.5" ];
+  programs.ssh.knownHosts.mugwump = {
+    extraHostNames = [ "172.16.0.5" ];
+    publicKeyFile = pkgs.writeText "mugwump.pub" ''
+      ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFE2fxPgWO+zeQoLBTgsgxP7Vg7QNHlrQ+Rb3fHFTomB
+    '';
+  };
+}
diff --git a/users/aspen/system/system/machines/roswell.nix b/users/aspen/system/system/machines/roswell.nix
new file mode 100644
index 0000000000..da62eec93e
--- /dev/null
+++ b/users/aspen/system/system/machines/roswell.nix
@@ -0,0 +1,27 @@
+{ depot, config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports = [
+    ../modules/common.nix
+    ../modules/development.nix
+    "${modulesPath}/installer/scan/not-detected.nix"
+    "${modulesPath}/virtualisation/amazon-image.nix"
+  ];
+
+  system.stateVersion = "22.05";
+
+  networking.hostName = "roswell";
+
+  boot.loader.systemd-boot.enable = lib.mkForce false;
+  boot.loader.efi.canTouchEfiVariables = lib.mkForce false;
+
+  services.openssh.settings.PasswordAuthentication = false;
+
+  services.tailscale.enable = true;
+
+  security.sudo.wheelNeedsPassword = false;
+
+  environment.systemPackages = with pkgs; [
+    cloud-utils
+  ];
+}
diff --git a/users/aspen/system/system/machines/yeren.nix b/users/aspen/system/system/machines/yeren.nix
new file mode 100644
index 0000000000..653f0cd44c
--- /dev/null
+++ b/users/aspen/system/system/machines/yeren.nix
@@ -0,0 +1,132 @@
+{ depot, modulesPath, config, lib, pkgs, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+    ../modules/common.nix
+    ../modules/laptop.nix
+    ../modules/xserver.nix
+    ../modules/fonts.nix
+    ../modules/sound.nix
+    ../modules/tvl.nix
+    ../modules/development.nix
+  ];
+
+  networking.hostName = "yeren";
+
+  system.stateVersion = "21.03";
+
+  time.timeZone = "America/New_York";
+
+  services.avahi = {
+    enable = true;
+    nssmdns4 = true;
+  };
+
+  boot = {
+    initrd = {
+      availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
+      kernelModules = [ ];
+
+      luks.devices = {
+        "cryptroot".device = "/dev/disk/by-uuid/dcfbc22d-e0d2-411b-8dd3-96704d3aae2e";
+      };
+    };
+
+    kernelModules = [ "kvm-intel" ];
+    blacklistedKernelModules = [ "psmouse" ];
+    extraModulePackages = [
+      config.boot.kernelPackages.digimend
+    ];
+    kernelParams = [
+      "i915.preliminary_hw_support=1"
+      "pcie_aspm=force"
+    ];
+
+    # https://bbs.archlinux.org/viewtopic.php?pid=1933643#p1933643
+    extraModprobeConfig = ''
+      options snd-intel-dspcfg dsp_driver=1
+    '';
+
+    kernel.sysctl = {
+      "kernel.perf_event_paranoid" = -1;
+    };
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/mapper/cryptroot";
+      fsType = "btrfs";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/53A9-248B";
+      fsType = "vfat";
+    };
+  };
+
+  swapDevices = [{
+    device = "/dev/disk/by-uuid/b627cb0e-0451-4f25-94d0-6497e01f0da4";
+  }];
+
+  services.earlyoom = {
+    enable = true;
+    freeMemThreshold = 5;
+  };
+
+  services.xserver = {
+    exportConfiguration = true;
+    extraConfig = ''
+      Section "Device"
+        Identifier  "Intel Graphics"
+        Driver      "intel"
+        Option      "TripleBuffer" "true"
+        Option      "TearFree"     "true"
+        Option      "DRI"          "true"
+        Option      "AccelMethod"  "sna"
+      EndSection
+    '';
+  };
+
+  hardware.firmware = with pkgs; [
+    alsa-firmware
+    sof-firmware
+  ];
+
+  hardware.opengl.extraPackages = with pkgs; [
+    vaapiIntel
+    vaapiVdpau
+    libvdpau-va-gl
+    intel-media-driver
+  ];
+
+  # Disabled for now until libfprint-tod can get a version bump
+  # services.fprintd = {
+  #   enable = true;
+  #   package = pkgs.fprintd-tod;
+  # };
+
+  systemd.services.fprintd.environment.FP_TOD_DRIVERS_DIR =
+    "${pkgs.libfprint-2-tod1-goodix}/usr/lib/libfprint-2/tod-1";
+
+  security.pam.services = {
+    login.fprintAuth = true;
+    sudo.fprintAuth = true;
+    i3lock.fprintAuth = false;
+    i3lock-color.fprintAuth = false;
+    lightdm.fprintAuth = true;
+    lightdm-greeter.fprintAuth = true;
+  };
+
+  hardware.opengl.driSupport32Bit = true;
+
+  hardware.pulseaudio.extraConfig = ''
+    load-module module-remap-source source_name=KompleteAudio6_1 source_properties=device.description=KompleteAudio6Input1 master=alsa_input.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.multichannel-input remix=no channels=1 master_channel_map=front-left channel_map=mono
+    load-module module-remap-source source_name=KompleteAudio6_2 source_properties=device.description=KompleteAudio6Input2 master=alsa_input.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.multichannel-input remix=no channels=1 master_channel_map=front-right channel_map=mono
+    load-module module-remap-sink sink_name=KompleteAudio6_12 sink_properties=device.description=KompleteAudio6_12 remix=no master=alsa_output.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.analog-surround-21 channels=2 master_channel_map=front-left,front-right channel_map=front-left,front-right
+  '';
+
+  services.fwupd.enable = true;
+
+  services.tailscale.enable = true;
+}
diff --git a/users/aspen/system/system/modules/common.nix b/users/aspen/system/system/modules/common.nix
new file mode 100644
index 0000000000..3eaeb2efc6
--- /dev/null
+++ b/users/aspen/system/system/modules/common.nix
@@ -0,0 +1,97 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  depot = import ../../../../.. { };
+
+in
+
+with lib;
+
+{
+  boot = {
+    loader.systemd-boot.enable = true;
+    loader.efi.canTouchEfiVariables = true;
+    tmp.cleanOnBoot = true;
+  };
+
+  networking.useDHCP = false;
+  networking.networkmanager.enable = true;
+  systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
+  systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false;
+
+  i18n = {
+    defaultLocale = "en_US.UTF-8";
+  };
+
+  time.timeZone = lib.mkDefault "America/New_York";
+
+  environment.systemPackages = with pkgs; [
+    wget
+    vim
+    zsh
+    git
+    w3m
+    libnotify
+    file
+    lm_sensors
+    dnsutils
+    htop
+    man-pages
+    man-pages-posix
+  ];
+
+  documentation.dev.enable = true;
+  documentation.man.generateCaches = true;
+
+  services.openssh = {
+    enable = true;
+    settings = { X11Forwarding = true; };
+  };
+
+  users.users.aspen.openssh.authorizedKeys.keys =
+    [ depot.users.aspen.keys.main ];
+
+  programs.ssh.startAgent = true;
+
+  networking.firewall.enable = mkDefault false;
+
+  users.mutableUsers = true;
+  programs.zsh.enable = true;
+  environment.pathsToLink = [ "/share/zsh" ];
+  users.users.aspen = {
+    isNormalUser = true;
+    initialPassword = "password";
+    extraGroups = [
+      "wheel"
+      "networkmanager"
+      "audio"
+    ];
+    shell = pkgs.zsh;
+  };
+
+  nix = {
+    settings.trusted-users = [ "aspen" ];
+    distributedBuilds = true;
+
+    gc = {
+      automatic = true;
+      dates = mkDefault "weekly";
+      options = "--delete-older-than 30d";
+    };
+  };
+
+  services.udev.packages = with pkgs; [
+    yubikey-personalization
+  ];
+
+  services.pcscd.enable = true;
+
+  services.udev.extraRules = ''
+    # UDEV rules for Teensy USB devices
+    ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
+    ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
+    SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", MODE:="0666"
+    KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", MODE:="0666"
+  '';
+}
diff --git a/users/aspen/system/system/modules/desktop.nix b/users/aspen/system/system/modules/desktop.nix
new file mode 100644
index 0000000000..9a5fc825e1
--- /dev/null
+++ b/users/aspen/system/system/modules/desktop.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ./xserver.nix
+    ./fonts.nix
+    ./sound.nix
+    ./kernel.nix
+  ];
+
+  programs.nm-applet.enable = true;
+
+  users.users.aspen.extraGroups = [
+    "audio"
+    "video"
+  ];
+
+  services.geoclue2.enable = true;
+}
diff --git a/users/aspen/system/system/modules/development.nix b/users/aspen/system/system/modules/development.nix
new file mode 100644
index 0000000000..bd5e326b2e
--- /dev/null
+++ b/users/aspen/system/system/modules/development.nix
@@ -0,0 +1,15 @@
+{ config, lib, pkgs, ... }:
+
+{
+  virtualisation.docker.enable = true;
+  users.users.aspen.extraGroups = [ "docker" ];
+
+  security.pam.loginLimits = [
+    {
+      domain = "aspen";
+      type = "soft";
+      item = "nofile";
+      value = "65535";
+    }
+  ];
+}
diff --git a/users/grfn/system/system/modules/fcitx.nix b/users/aspen/system/system/modules/fcitx.nix
index 812f598f9f..812f598f9f 100644
--- a/users/grfn/system/system/modules/fcitx.nix
+++ b/users/aspen/system/system/modules/fcitx.nix
diff --git a/users/aspen/system/system/modules/fonts.nix b/users/aspen/system/system/modules/fonts.nix
new file mode 100644
index 0000000000..598336790a
--- /dev/null
+++ b/users/aspen/system/system/modules/fonts.nix
@@ -0,0 +1,13 @@
+{ config, lib, pkgs, ... }:
+{
+  fonts = {
+    packages = with pkgs; [
+      nerdfonts
+      noto-fonts-emoji
+      twitter-color-emoji
+      weather-icons
+    ];
+
+    fontconfig.defaultFonts.emoji = [ "Twitter Color Emoji" ];
+  };
+}
diff --git a/users/aspen/system/system/modules/laptop.nix b/users/aspen/system/system/modules/laptop.nix
new file mode 100644
index 0000000000..89c880973d
--- /dev/null
+++ b/users/aspen/system/system/modules/laptop.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+{
+  services.logind = {
+    powerKey = "hibernate";
+    powerKeyLongPress = "poweroff";
+    lidSwitch = "suspend-then-hibernate";
+    lidSwitchExternalPower = "ignore";
+  };
+
+  systemd.sleep.extraConfig = ''
+    HibernateDelaySec=30m
+    SuspendState=mem
+  '';
+
+  services.tlp.enable = true;
+
+  services.upower = {
+    enable = true;
+    criticalPowerAction = "Hibernate";
+    percentageAction = 3;
+  };
+}
diff --git a/users/grfn/system/system/modules/reusable/README.org b/users/aspen/system/system/modules/reusable/README.org
index 34d9bfdcb7..34d9bfdcb7 100644
--- a/users/grfn/system/system/modules/reusable/README.org
+++ b/users/aspen/system/system/modules/reusable/README.org
diff --git a/users/grfn/system/system/modules/rtlsdr.nix b/users/aspen/system/system/modules/rtlsdr.nix
index ce58ebb0dc..ce58ebb0dc 100644
--- a/users/grfn/system/system/modules/rtlsdr.nix
+++ b/users/aspen/system/system/modules/rtlsdr.nix
diff --git a/users/grfn/system/system/modules/sound.nix b/users/aspen/system/system/modules/sound.nix
index 07a67a1ec4..07a67a1ec4 100644
--- a/users/grfn/system/system/modules/sound.nix
+++ b/users/aspen/system/system/modules/sound.nix
diff --git a/users/aspen/system/system/modules/tvl.nix b/users/aspen/system/system/modules/tvl.nix
new file mode 100644
index 0000000000..f91315fc79
--- /dev/null
+++ b/users/aspen/system/system/modules/tvl.nix
@@ -0,0 +1,35 @@
+{ config, lib, pkgs, ... }:
+
+{
+  nix = {
+    buildMachines = [{
+      hostName = "whitby.tvl.fyi";
+      sshUser = "aspen";
+      sshKey = "/root/.ssh/id_rsa";
+      system = "x86_64-linux";
+      maxJobs = 64;
+      supportedFeatures = [ "big-parallel" "kvm" "nixos-test" "benchmark" ];
+    }];
+
+    extraOptions = ''
+      builders-use-substitutes = true
+    '';
+
+    settings = {
+      substituters = [
+        "https://cache.nixos.org"
+      ];
+      trusted-substituters = [
+        "https://cache.nixos.org"
+        "ssh://nix-ssh@whitby.tvl.fyi"
+      ];
+    };
+  };
+
+  programs.ssh.knownHosts.whitby = {
+    extraHostNames = [ "whitby" "whitby.tvl.fyi" "49.12.129.211" ];
+    publicKeyFile = pkgs.writeText "whitby.pub" ''
+      ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILNh/w4BSKov0jdz3gKBc98tpoLta5bb87fQXWBhAl2I
+    '';
+  };
+}
diff --git a/users/aspen/system/system/modules/wireshark.nix b/users/aspen/system/system/modules/wireshark.nix
new file mode 100644
index 0000000000..b233d40041
--- /dev/null
+++ b/users/aspen/system/system/modules/wireshark.nix
@@ -0,0 +1,9 @@
+{ config, lib, pkgs, ... }:
+
+{
+  programs.wireshark = {
+    enable = true;
+    package = pkgs.wireshark;
+  };
+  users.users.aspen.extraGroups = [ "wireshark" ];
+}
diff --git a/users/grfn/system/system/modules/xserver.nix b/users/aspen/system/system/modules/xserver.nix
index 35ee44112e..f78edb207e 100644
--- a/users/grfn/system/system/modules/xserver.nix
+++ b/users/aspen/system/system/modules/xserver.nix
@@ -3,7 +3,7 @@
   # Enable the X11 windowing system.
   services.xserver = {
     enable = true;
-    layout = "us";
+    xkb.layout = "us";
 
     libinput.enable = true;
 
diff --git a/users/aspen/terraform/globals.nix b/users/aspen/terraform/globals.nix
new file mode 100644
index 0000000000..c6bc24c22b
--- /dev/null
+++ b/users/aspen/terraform/globals.nix
@@ -0,0 +1,27 @@
+{ pkgs, ... }:
+
+{
+  provider.aws = map
+    (region: {
+      inherit region;
+      alias = region;
+      profile = "personal";
+    }) [
+    "us-east-1"
+    "us-east-2"
+    "us-west-2"
+  ];
+
+  data.external.cloudflare_api_key = {
+    program = [
+      (pkgs.writeShellScript "cloudflare_api_key" ''
+        jq -n --arg api_key "$(pass cloudflare-api-key)" '{"api_key":$api_key}'
+      '')
+    ];
+  };
+
+  provider.cloudflare = {
+    email = "root@gws.fyi";
+    api_key = "\${data.external.cloudflare_api_key.result.api_key}";
+  };
+}
diff --git a/users/grfn/terraform/nixosMachine.nix b/users/aspen/terraform/nixosMachine.nix
index ef8830d66c..23cd838804 100644
--- a/users/grfn/terraform/nixosMachine.nix
+++ b/users/aspen/terraform/nixosMachine.nix
@@ -9,13 +9,13 @@
 , region ? "us-east-2"
 , rootVolumeSizeGb ? 50
 , securityGroupId ? null
-, extraIngressPorts ? []
+, extraIngressPorts ? [ ]
 }:
 
 let
   os = depot.ops.nixos.nixosFor ({ modulesPath, ... }: {
     imports = [
-      "${pkgs.path}/nixos/modules/virtualisation/amazon-image.nix"
+      (pkgs.path + "/nixos/modules/virtualisation/amazon-image.nix")
       configuration
     ];
 
@@ -40,13 +40,14 @@ let
 
   machineResource = "aws_instance.${prefix}machine";
 
-  recursiveMerge = builtins.foldl' lib.recursiveUpdate {};
+  recursiveMerge = builtins.foldl' lib.recursiveUpdate { };
 
   securityGroupId' =
     if isNull securityGroupId
     then "\${aws_security_group.${prefix}group.id}"
     else securityGroupId;
-in recursiveMerge [
+in
+recursiveMerge [
   (lib.optionalAttrs (isNull securityGroupId) {
     resource.aws_security_group."${prefix}group" = {
       provider = "aws.${region}";
@@ -60,12 +61,12 @@ in recursiveMerge [
     resource.aws_security_group_rule.all_egress = {
       provider = "aws.${region}";
       security_group_id = securityGroupId';
-      type            = "egress";
-      protocol        = "-1";
-      from_port       = 0;
-      to_port         = 0;
-      cidr_blocks     = ["0.0.0.0/0"];
-      ipv6_cidr_blocks = ["::/0"];
+      type = "egress";
+      protocol = "-1";
+      from_port = 0;
+      to_port = 0;
+      cidr_blocks = [ "0.0.0.0/0" ];
+      ipv6_cidr_blocks = [ "::/0" ];
 
       description = null;
       prefix_list_ids = null;
@@ -74,12 +75,14 @@ in recursiveMerge [
   })
   rec {
     data.external.my_ip = {
-      program = [(pkgs.writeShellScript "my_ip" ''
-        ${pkgs.jq}/bin/jq \
-          -n \
-          --arg ip "$(curl ifconfig.me)" \
-          '{"ip":$ip}'
-      '')];
+      program = [
+        (pkgs.writeShellScript "my_ip" ''
+          ${pkgs.jq}/bin/jq \
+            -n \
+            --arg ip "$(curl ifconfig.me)" \
+            '{"ip":$ip}'
+        '')
+      ];
     };
 
     resource.aws_security_group_rule.provision_ssh_access = {
@@ -89,8 +92,8 @@ in recursiveMerge [
       protocol = "TCP";
       from_port = 22;
       to_port = 22;
-      cidr_blocks = ["\${data.external.my_ip.result.ip}/32"];
-      ipv6_cidr_blocks = [];
+      cidr_blocks = [ "\${data.external.my_ip.result.ip}/32" ];
+      ipv6_cidr_blocks = [ ];
       description = null;
       prefix_list_ids = null;
       self = null;
@@ -183,21 +186,23 @@ in recursiveMerge [
   }
 
   {
-    resource.aws_security_group_rule = builtins.listToAttrs (map (port: {
-      name = "ingress_${toString port}";
-      value = {
-        provider = "aws.${region}";
-        security_group_id = securityGroupId';
-        type = "ingress";
-        protocol = "TCP";
-        from_port = port;
-        to_port = port;
-        cidr_blocks = ["0.0.0.0/0"];
-        ipv6_cidr_blocks = [];
-        description = null;
-        prefix_list_ids = null;
-        self = null;
-      };
-    }) extraIngressPorts);
+    resource.aws_security_group_rule = builtins.listToAttrs (map
+      (port: {
+        name = "ingress_${toString port}";
+        value = {
+          provider = "aws.${region}";
+          security_group_id = securityGroupId';
+          type = "ingress";
+          protocol = "TCP";
+          from_port = port;
+          to_port = port;
+          cidr_blocks = [ "0.0.0.0/0" ];
+          ipv6_cidr_blocks = [ ];
+          description = null;
+          prefix_list_ids = null;
+          self = null;
+        };
+      })
+      extraIngressPorts);
   }
 ]
diff --git a/users/grfn/terraform/workspace.nix b/users/aspen/terraform/workspace.nix
index c2a0fdb977..f1563d2a84 100644
--- a/users/grfn/terraform/workspace.nix
+++ b/users/aspen/terraform/workspace.nix
@@ -3,7 +3,7 @@ name: { plugins }: module_tf:
 
 let
 
-  inherit (pkgs) lib runCommandNoCC writeText writeScript;
+  inherit (pkgs) lib runCommand writeText writeScript;
   inherit (lib) filterAttrsRecursive;
 
   allPlugins = (p: plugins p ++ (with p; [
@@ -21,22 +21,24 @@ let
   ]));
 
   plugins_tf = {
-    terraform.required_providers = (builtins.listToAttrs (map (p: {
-      name = lib.last (lib.splitString "/" p.provider-source-address);
-      value = {
-        source = p.provider-source-address;
-        version = p.version;
-      };
-    }) (allPlugins pkgs.terraform.plugins)));
+    terraform.required_providers = (builtins.listToAttrs (map
+      (p: {
+        name = lib.last (lib.splitString "/" p.provider-source-address);
+        value = {
+          source = p.provider-source-address;
+          version = p.version;
+        };
+      })
+      (allPlugins pkgs.terraform.plugins)));
   };
 
 
   module_tf' = module_tf // {
-    inherit (depot.users.grfn.terraform) globals;
+    inherit (depot.users.aspen.terraform) globals;
     plugins = plugins_tf;
   };
 
-  module = runCommandNoCC "module" {} ''
+  module = runCommand "module" { } ''
     mkdir $out
     ${lib.concatStrings (lib.mapAttrsToList (k: config_tf:
       (let
@@ -70,7 +72,7 @@ let
   '';
 
   # TODO: import (-config)
-  tfcmds = runCommandNoCC "${name}-tfcmds" {} ''
+  tfcmds = runCommand "${name}-tfcmds" { } ''
     mkdir -p $out/bin
     ln -s ${init} $out/bin/init
     ln -s ${tfcmd} $out/bin/validate
@@ -79,7 +81,8 @@ let
     ln -s ${tfcmd} $out/bin/destroy
   '';
 
-in {
+in
+{
   inherit name module;
   terraform = tf;
   cmds = tfcmds;
@@ -92,7 +95,7 @@ in {
   #   destroy = depot.nix.nixRunWrapper "destroy" tfcmds;
   # };
 
-  test = runCommandNoCC "${name}-test" {} ''
+  test = runCommand "${name}-test" { } ''
     set -e
     export TF_STATE_ROOT=$(pwd)
     ${tfcmds}/bin/init
diff --git a/users/aspen/web/.envrc b/users/aspen/web/.envrc
new file mode 100644
index 0000000000..b80e28b4b8
--- /dev/null
+++ b/users/aspen/web/.envrc
@@ -0,0 +1,2 @@
+source_up
+eval "$(lorri direnv)"
diff --git a/users/grfn/gws.fyi/.gitignore b/users/aspen/web/.gitignore
index 2b72eaed29..2b72eaed29 100644
--- a/users/grfn/gws.fyi/.gitignore
+++ b/users/aspen/web/.gitignore
diff --git a/users/aspen/web/Makefile b/users/aspen/web/Makefile
new file mode 100644
index 0000000000..ee6ed2fd8b
--- /dev/null
+++ b/users/aspen/web/Makefile
@@ -0,0 +1,40 @@
+.PHONY: deploy purge_cf do_deploy renew backup open
+
+deploy: do_deploy purge_cf
+
+purge_cf:
+	@$(shell nix-build `git rev-parse --show-toplevel` -A 'users.aspen.web.purge-cf')/bin/purge-cf.sh
+
+do_deploy:
+	@$(shell nix-build `git rev-parse --show-toplevel` -A 'users.aspen.web')/bin/deploy.sh
+
+
+renew:
+	@echo Renewing...
+	@certbot certonly \
+		--manual \
+		--domain www.gws.fyi \
+		--preferred-challenges dns \
+		--server https://acme-v02.api.letsencrypt.org/directory \
+		--agree-tos \
+		--work-dir $(shell pwd)/letsencrypt/work \
+		--logs-dir $(shell pwd)/letsencrypt/logs \
+		--config-dir $(shell pwd)/letsencrypt/config
+	@echo "Reimporting certificate"
+	@aws acm import-certificate \
+	    --profile personal \
+	    --certificate file://letsencrypt/config/live/www.gws.fyi/cert.pem \
+	    --certificate-chain file://letsencrypt/config/live/www.gws.fyi/fullchain.pem \
+	    --private-key file://letsencrypt/config/live/www.gws.fyi/privkey.pem \
+	    --certificate-arn arn:aws:acm:us-east-1:797089351721:certificate/628e54f3-55f9-49c0-811a-eba516b68e30 \
+		--region us-east-1
+
+backup:
+	@tarsnap -cf $(shell uname -n)-letsencrypt-$(shell date +%Y-%m-%d_%H-%M-%S) \
+		letsencrypt/
+
+open:
+	$$BROWSER "https://www.gws.fyi"
+
+preview:
+	$$BROWSER "$(shell mg build website)/index.html"
diff --git a/users/grfn/gws.fyi/config.el b/users/aspen/web/config.el
index b05d897d3d..b05d897d3d 100644
--- a/users/grfn/gws.fyi/config.el
+++ b/users/aspen/web/config.el
diff --git a/users/aspen/web/default.nix b/users/aspen/web/default.nix
new file mode 100644
index 0000000000..a16cd16c06
--- /dev/null
+++ b/users/aspen/web/default.nix
@@ -0,0 +1,62 @@
+args@{ pkgs, depot, ... }:
+with pkgs;
+let
+  site = import ./site.nix args;
+  resume = import ../resume args;
+  bucket = "s3://gws.fyi";
+  distributionID = "E2ST43JNBH8C64";
+
+  css = runCommand "main.css"
+    {
+      buildInputs = [ pkgs.minify ];
+    } ''
+    minify --type css < ${./main.css} > $out
+  '';
+
+  keys = runCommand "ssh-keys" { } ''
+    touch $out
+    echo "${depot.users.aspen.keys.main}" >> $out
+  '';
+
+  website =
+    runCommand "gws.fyi" { } ''
+      mkdir -p $out
+      cp ${css} $out/main.css
+      cp ${site.index} $out/index.html
+      cp -r ${site.recipes} $out/recipes
+      cp ${resume} $out/resume.pdf
+      cp ${keys} $out/keys
+      cp ${./pubkey.gpg} $out/pubkey.gpg
+    '';
+
+  purge-cf = writeShellApplication {
+    name = "purge-cf.sh";
+    runtimeInputs = [ httpie jq pass ];
+    text = ''
+      cfapi() {
+        http \
+          "https://api.cloudflare.com/client/v4/$1" \
+          X-Auth-Email:root@gws.fyi \
+          "X-Auth-Key: $(pass cloudflare-api-key)" \
+          "''${@:2}"
+      }
+
+      zone_id=$(
+        cfapi zones \
+          | jq -r '.result[] | select(.name == "gws.fyi") | .id'
+      )
+
+      cfapi "zones/$zone_id/purge_cache" purge_everything:=true
+    '';
+  };
+in
+(writeShellApplication {
+  name = "deploy.sh";
+  runtimeInputs = [ awscli2 ];
+  text = ''
+    aws --profile personal s3 sync ${website}/ ${bucket}
+    echo "Deployed to http://gws.fyi"
+  '';
+}).overrideAttrs {
+  passthru = { inherit website site purge-cf; };
+}
diff --git a/users/aspen/web/index.org b/users/aspen/web/index.org
new file mode 100644
index 0000000000..4be79fd797
--- /dev/null
+++ b/users/aspen/web/index.org
@@ -0,0 +1,40 @@
+#+OPTIONS: title:nil toc:nil num:nil
+#+HTML_HEAD: <title>aspen smith</title>
+#+HTML_HEAD: <link rel="stylesheet" href="./main.css">
+
+my name is aspen smith and i'm a software engineer and musician.
+
+* code
+
+- [[https://github.com/glittershark/][github]]
+- [[https://cs.tvl.fyi/depot/-/tree/users/aspen][my directory in the tvl monorepo]]
+
+* work
+
+most recently, i worked on database internals at [[https://readyset.io/][readyset]], an incrementally
+maintained, partially stateful materialized view maintenance system for sql
+that's wire-compatible with postgresql and mysql, based on [[https://github.com/mit-pdos/noria][noria]].
+
+* projects
+
+- [[https://windtunnel.ci/][windtunnel]], a continuous benchmarking software-as-a-service currently accepting early alpha users (send me an email if you want to try it out!)
+- [[https://cs.tvl.fyi/depot/-/tree/users/aspen/achilles][achilles]], a compiler for (what I plan to become) a dependently typed, low-level functional programming language targeting LLVM
+- [[https://github.com/glittershark/org-clubhouse][org-clubhouse]], an emacs package for lightweight integration between [[https://orgmode.org/][org-mode]] and [[https://clubhouse.io/][the clubhouse project management tool]]
+- [[https://cs.tvl.fyi/depot/-/tree/users/aspen/xanthous][xanthous]], a terminal roguelike in haskell that I work on intermittently and exclusively for fun
+
+* music
+
+- https://sacrosanct.bandcamp.com/, a post-rock project with a [[https://bandcamp.com/h34rken][friend of mine]]
+- [[https://soundcloud.com/missingggg][my current soundcloud]], releasing instrumental music under the name *missing*
+- i play bass in [[https://goodcry.band][good cry]], a rock band based in brooklyn
+- you can also find a log of all the music I listen to [[https://www.last.fm/user/wildgriffin45][on last.fm]]
+
+* contact
+
+- [[mailto:web@gws.fyi][web@gws.fyi]]
+- [[https://twitter.com/glittershark1][twitter]]
+- [[https://bsky.app/profile/gws.fyi][bluesky]]
+- https://keybase.io/glittershark
+- aspen on IRC (hackint or libera.chat)
+- [[https://gws.fyi/pubkey.gpg][gpg key: 0F11A989879E8BBBFDC1E23644EF5B5E861C09A7]]
+- signal / telegram / discord available upon request
diff --git a/users/grfn/gws.fyi/main.css b/users/aspen/web/main.css
index cdcd440766..cdcd440766 100644
--- a/users/grfn/gws.fyi/main.css
+++ b/users/aspen/web/main.css
diff --git a/users/aspen/web/orgExportHTML.nix b/users/aspen/web/orgExportHTML.nix
new file mode 100644
index 0000000000..aac4e32e7a
--- /dev/null
+++ b/users/aspen/web/orgExportHTML.nix
@@ -0,0 +1,67 @@
+{ pkgs, depot, ... }:
+
+with pkgs;
+with lib;
+
+let
+
+  emacs = pkgs.emacs28;
+
+in
+
+opts:
+
+let
+  src = if isAttrs opts then opts.src else opts;
+  headline = if isAttrs opts then opts.headline else null;
+
+  bn = builtins.baseNameOf src;
+  filename = elemAt (splitString "." bn) 0;
+
+  outName =
+    if isNull headline
+    then
+      let
+        bn = builtins.baseNameOf src;
+        filename = elemAt (splitString "." bn) 0;
+      in
+      if depot.nix.utils.isDirectory src
+      then filename
+      else filename + ".html"
+    else "${filename}-${replaceStrings [" "] ["-"] filename}.html";
+
+  escapeDoubleQuotes = replaceStrings [ "\"" ] [ "\\\"" ];
+
+  navToHeadline = optionalString (! isNull headline) ''
+    (search-forward "${escapeDoubleQuotes headline}")
+    (org-narrow-to-subtree)
+  '';
+
+in
+
+runCommand outName { inherit src; } ''
+  buildFile() {
+    cp "$1" file.org
+    ${emacs}/bin/emacs --batch \
+      --load ${./config.el} \
+      --visit file.org \
+      --eval "(progn
+        ${escapeDoubleQuotes navToHeadline}
+        (org-html-export-to-html))" \
+      --kill
+    rm file.org
+    substitute file.html "$2" \
+      --replace '<title>&lrm;</title>' ""
+    rm file.html
+  }
+
+  if [ -d $src ]; then
+    for file in $src/*; do
+      result=''${file/$src/$out}
+      mkdir -p $(dirname $result)
+      buildFile $file ''${result/.org/.html}
+    done
+  else
+    buildFile $src $out
+  fi
+''
diff --git a/users/aspen/web/pubkey.gpg b/users/aspen/web/pubkey.gpg
new file mode 100644
index 0000000000..2a7e744cc3
--- /dev/null
+++ b/users/aspen/web/pubkey.gpg
@@ -0,0 +1,206 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFSsgHkBCAC8hkveavGgqfK4zN+ZKWmzToa/YQHHatA68cIzlmXebuevV9bE
+vNVYz6qI9Y6QPvxoIXlnmKzcZkWzE9X3bXdtlucojXeUzVxfkTIWWL50+DMZtzTY
+IGaK4h7J6xuF27S2WZjE18Bf+mvwrGgl2B6eCd2L2Jw3PwdH7LI9+zl0B9Eo6QUJ
+1O1NZu5ry135uzIfs4u1U0s+OVnBcq+y4NR0Tejsw7WwY5/JLlljM2CJYPhFGT7h
+yx6ApI4TeoOwDOXg4UKl78znMpUiJP0yD8u5/BHiq64H4oxkDiRenOneZ9B6vupX
+o0+ri94h1Dirb/D2Bq/LxdNWqLz/xDlU5GEBABEBAAG0HEdyaWZmaW4gU21pdGgg
+PHJvb3RAZ3dzLmZ5aT6JATwEEwEIACYCGwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIe
+AQIXgAUCWVumdgIZAQAKCRBE71tehhwJp2dZB/9QimuyGMtXB3ElfdRLlQ3dT4uT
+KMW38VBMyEXOlMF1VhRolRWp4CTsEUYSJhhNg/b/NW7KgXBaX3ZJEAZqwyttxx0r
+1v9Rysb9yrxkKAsnyNs774sMaEj+Zj+gtik4jxT8EZ3hV3ohCUrEKiImPKkPTeFl
+xYk9QjXZPE4CAMBfmybawIk7cMHp0XzJppia9XAwA7zDXQnoc/rL8feVxXUbcPb1
+RjAtGI/Pg4vhZKCJ4urvtkSvcKXkiqq3taGjIUV1cvTjwTs1ceL32J0oghR6zbIt
+OLj9AfqlLjBY+9Mdyeo+Jd1FGBu8i+C13sv+yLFZHEopi5hnHXgsEvIZwcSviQE5
+BBMBCAAjBQJZW6VqAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQRO9b
+XoYcCafMiwgAggljWWQuVBf/vTK6Yg7VgOo7XEPShzu7mNt1fucCdiAk01NvZV3/
+EnkpuWJHyQ8hPU+xOHxCz22Mhv4IpIhmCSXQiOhGrQ+byPRMXEeyse1Pn7YVW9jx
+7rk5Lh95YYqBy4LLACi8Cvo8OfbwtIPO5G/JKD3rGFHWcANya3ZdNlLr8RlJQa1/
+W7nZ/i+TnfGC7AvaR/5JfwBAxA5YMI4hK+RHiRWGuIPTwYrel2bKToRboXL76fRI
+9pI8SKzuRr7dSX/pCWFLPQRdlwlF3n5iiADo73XyXcOUP2g6oadNzvsqctq6kGML
+KEHAoqhsAUMs+J8sSPcyfhJOIQPQRYExFokCMwQQAQgAHRYhBBP5Ly1Y78tmaShO
+tNywW0z4mvxmBQJZvsjBAAoJENywW0z4mvxm76sP/3DZ3i0f7B7zRCkzzbyYrTU6
+kbZK8FzvGhNenU8JNQ5370lxiebVp7gwUsYORmgcA7mkenqVjSjYNK+/hTNcaiIU
+KVn/TxrrTWtCNE3d5fNHRdR9Ac9FOw7CHJGac3/UOsxE++cpW2+7S0SH59usl+17
+tLbO9YzsTncvsWR8maGnjaTV0Jz+Ccd1GkBHv0Edpmzki+oseqI8YlRK5uNmhuMB
+G06k51hgvjwYpF6fUwRauv77XnI71fRa4m8AX+LGt/NMz9GmFfhDTlC+YnsiVbk8
+bkAAR4axCfKvbUQm5GRs/iKLFBlpwC1/FsjTdgWOWhpF1J/jhiIFZsG4DyqnkJLb
+W7WI6QJPeB05i6EjI+ub/2SGJXVlPIRH9S5DCX8k0QNk2kANplaLoE6+xkki09TE
+4PTEK9c63Hi6X39Z/pgw25/mgArp3VVTSrK1ZIiiL+o9bvTowtc3uHHtG1fV8RMK
+DOfWhI00TqpTend8DsgfpYR4vfLasmDBkXI9GmeIzX4XABVMtb5IS3ZDUNMEgPuk
+kMwrASg8ln8UqZpRMtXPg9l3INiIoXoDfLHjyS4FXBqSLIi778uuNZvYXyJNKYlz
+aYfma4I9m6bpkS9U4gFYnkuX3rAL8RiyKru5EGlE8+GL6PxnRuChj1g0mgazSNBA
+79Af1iA6JpwnUsQxXxkWiQIcBBABCAAGBQJZwDbFAAoJEMelPMWNOx+MLYoP/3CT
+16deQZCNjfVmBKWzvwEEOTQVoRs1xEl63lKtXOrWtStZPPnnIXqfXKi22p/owykB
+WGdE84RgRAGBJYPriHjbxx/BK6eaUkmyekaa0tFadDOpctK/YmTZkp3l/FOCezGl
+QK0je2koxBei63qL+tfFIP4MNTuIwxezrSwCXaXRlsblIvLNBSK8cBHz1WjZG4Ch
+bV4RxoW75teHqtbsLId97WRXojErjIpA6ypqmUEyG9YrOdOFd1QA9QXWm5RO8xhw
+8JTnUDeyrL00H+2zEnxz02vDT5qJyXLBF5FrHgQvvdq9ZJo85/LZH6EZZt8D4N+l
+sWij2wTQZ+GrzhXgOWyWeVWsYiWRegrCVc1HDJnT3c4ALjHDdHQvZjYG1alPT06h
+J+MEdGvPWRcGEuM4m4MP6p/PYq7xqmf4Kxw2JWd1e3gVffRTC04dXcxmMzVHG+vo
+mtdPjquPzawd7rMKqvECbzwBIHSVLAqDvcian+U83fv0NK1vP5wSQFK3531/1xwp
+UVx2jJHpuVw6goW8vm5RMHP3fgaDjBU9E9l7K7UszYr6zUxOGNJ6S1IPkBcz5K4e
+ncrqhG5Tr0mFP+iV6V+bQ5hfBhXIYhVBqieumGD/ZU8QUjyxj/myb4lPoIBsBxxi
+FWwHRTKn8qQFZvJY7xXrX0BsLGAgTxKupEADLR/0tCdHcmlmZmluIFNtaXRoIDx3
+aWxkZ3JpZmZpbjQ1QGdtYWlsLmNvbT6JATcEEwEIACEFAlSsgHkCGwMFCwkIBwIG
+FQgJCgsCBBYCAwECHgECF4AACgkQRO9bXoYcCaed+Qf+JSDZ3odMwlnrbb2kwsld
+uAt9VhRm+dfdIm25nAgxJUxIju8uIgE9v+8dRdGFwrV8pKBYYOCMi8MFNYuu9zS6
+6wXS4opd/DeYDj2yaN0wBYEfeXMCwVLVDHU7AHrsxQWRSxbcUOi2Mm2sig70ZSq2
+iNicX2f6eUSr/4CjocTP6jOqcHd6Di4odEy/hK6ukCCW8ia1Uujh7JYCU7quHnuE
+1N184W2Jf6hUieFC2kE+Nmhix0LsYYe6c1InembHRZ85BpOsWWuE9cS6IuVO/jbN
+ZcgS7NkuCHkG7CubPnSZX/EDwmyr9Pd57tr9BANuDNvTGgcbaXhJj5nlIx/usDsr
+RIkBOgQTAQgAJAIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAUCWCyKLgIZAQAK
+CRBE71tehhwJp/Q/B/9I+URRL9FuzA3bnljchtDVUtPVWfKzPUzKTJkUGnrmO3J6
+YM97JUoU9YZUP0JGvBuTzM4FVqNG7aEdyQBiHa4b7rBzVIUD+hMYlvowJTVKsG3j
+f+TcX4Ms9wTpDRd9+kjZPTONZom5C+a5yTqipKK78CubCQJESQktYJoMr3P3FOG/
+nTi9NCeVEah2ud2fsrfOvqTqpcL02O/jTqA4jDDznw2TmeQ/NMc+Dz5hIIHod8D5
+QXoNPvMPFcCGe3oSPzQ5RlKpF1MvzSb35c9TY5BbOVRZPSUR5elkAE1yrXPpwkNo
+bQl9/2eCxhcOcT9bcP+hmD0O4QJw5h8SALVAYaZJiQIzBBABCAAdFiEEE/kvLVjv
+y2ZpKE603LBbTPia/GYFAlm+yMUACgkQ3LBbTPia/GYgvBAAgKZffTlMvOjnmYB2
+TiG5sn9fqnU8cDmnaVLwD/XLSZ8wI3dBuLnlfEOTIAWlrAnnIXyEdH4isR3zLPuW
+bXLsIy4xW5BddjnWaRpE/2kdPTxzo+tI/JjeJFmxp6MggAMV+XdaxDXTMZGwA3Gv
+ZXqLiTvlCYT3YBM4MtEA7HkXjpQuvYJ2rMFLhTrf8IhWg7PQMJJKqaM2RawwJ7XX
+L4HzoQyHD5iJV8H/1gtSayxHeVwPVJYlUXtpPabbPhRtrJlI39RVCWejhI0h2RU3
+U9lvVA+BP3Dv3l9kDm9XYz9/WH1sYgjatsPSFgStYg4kjvrgIuHN1ALCPv3wZUmW
+0NhV26pTps667M5onEAn+0rfHYp6KF4FUX/3kPgRzuXRt+gAY2BdgFvLd+BucQxz
+oaZzl3ykK9ZwbylgtX6kmaspBE/TcxKwGG8RUJZZf8VEqaIk6nZ24F+oaK9zjLgw
+3eOOWsk6UefibiLGXpTRIIhmBw5ki6LJi2I/IpQ73hOZvyQKlZCKEBJL72MQbZeA
+25xkO8a4ZVdmeyYTJbxL9f10t8ZNBwYJQW7MAhGRKAkg9McrK4Wh73zs8ux+jp9U
+i7wM6HWRARyfbjCoJ3V7Q3FbN+H0zk8/+wvlTabpSNIyUiRp7d9Yw6fmVr/Fq2I5
+55Rlg725RwN/hJHRUvLGkoP1xSCJAhwEEAEIAAYFAlnANsUACgkQx6U8xY07H4z/
+5Q//UGyEHeDu4mm/5h57ryUR32/0NG4yBeQrn5HLaOD+bpWD+Z5ZBV3TOJHeuJMj
+xA/scBHIkfnwW+e7DQxudPBhxp6+ZMyLje6en9uZY/IVGXVKe63+XzRojwJRHhbe
+7YUyym0en1RBWkI/TIdx4KjZ55Xz78/Dmb6QnBCuYr2Vb6h0mZz3p8KoTaIFEWM9
+m9/84Xz2ppCcVBoaavMyo1P4/Fxluh4T51qjR1UeLIuwDdDyS1aYQ9+MXIl8tHz1
+x05HdDbdU7Y2tAg4WUZ5Lg42m9hjRPJcyuw6P+jpF/YrxyAbY962ibZNTep1urTG
+/oBwSnhJ8nIK5ISwkacbyeN3Tu2sCmJceiCfOz1M9CaEffnyxP36ALZsF9jl+PWM
+bGsN+ZeZMwdnPsExBrqNbPn+Ce2LNm/dxIlfrFXJXqm7BZAuOCuL9UAm5vihR/LE
+ijqYWsTI32N8nz4RclU3d1N6w5KZ/XzyVfMYHhhgrncPG/21A1YlR9CBK/FBh7nu
+hBLU2Q92VV47oEIG1BUVHct7SOR1fV4Auvg2d9mTPWZV2mfGjebywaALq6kpc5l0
+bUTDew8FXxXO+arsvM5ngdI/XHIAai9+L13jnsciTjQurpnhC9ZNwUz49pyJSQy4
+fWDrYe/RldGa3ggCioNCZM9oCJUYYXzJpfeCWcCw7SmwcGO0K0dyaWZmaW4gU21p
+dGggPGdzbWl0aEBzZWN1cml0eXNjb3JlY2FyZC5pbz6JAWIEMAEIAEwWIQQPEamJ
+h56Lu/3B4jZE71tehhwJpwUCWcAx0S4dIEkgYW0gbm8gbG9uZ2VyIGVtcGxveWVk
+IGF0IFNlY3VyaXR5U2NvcmVjYXJkAAoJEETvW16GHAmnXKMH/0JpyvK92knWLcBI
+fzatSWHSyTM7CYxIgB6kj6DhIUd5h0ahVx5CLMUNLlBZSdbCKzgifSbP0HLBVC7O
+mn3tCwPuE2GM5dgnxxawaOqK7SdwB8nq1wFZD2uPlFdiocHXRBG7OQH/nkaJP+V/
+eO0F+2vLEER3NxdOuIE1PZVXDRVBaeRGuyG5MXW78PdnV/GizzxoKVBw+ZRIHonj
+IpCozPjFzA3/ZLpQtawKZMyO0un20QEDEXJAoAsgIAyYFYjb568+VBb/8Ghe7V5q
+7yekKOW1HzxBsN6/YGvOBLedKANsG1C180klDQIt6GkBk1V7RUs59DpGRlfYC7la
+X/cewumJATcEEwEIACEFAlgrhmUCGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AA
+CgkQRO9bXoYcCadpQggAok7C6Bm6uOANdfpNuKFiLWyU64OaSEsClMH8LYb4pNKE
+D1n0KM/7K9MPyHtK1JwrksjlVF7iWUoMxToxHMJsNXQ79IbGp8rfjWd0gq/4UmrA
+DjCHl0u/oci/PeBOjSiBg4tufmIaY9QBZ3grvXDqMZRF6sp1WogSW6RyMGrcAeYu
+2b1M/pK0wY3+sMIC8wYtRB8OyCWIYEtWHvQP/oforq+3f+PwunAt9SIAXa/HfIjW
+zIaspOuvn4XWeVXdkDuA1KuAlIBaGpDvV9/NyX/Gs0dM1yDz6YSdkKUMw7H64lE9
+CdfGz+eOrZJ8DrDWJbdVz2MJS71ypY169WJEck3T04kCMwQQAQgAHRYhBBP5Ly1Y
+78tmaShOtNywW0z4mvxmBQJZvsjGAAoJENywW0z4mvxm3AwP/1qwyr5JlHndaU7K
+6osZ/hCL6h+yx/m/tG6HbBCAsV7CO5PxeVZAZoJsEbiPLoQ2FCWyYh6sjKoBlw4o
+n+pPDWZHw///35Y+H4/srCOAWkONpBb9ZQM5Kb943thQXNLrzpzV6itcqLM/Gr3d
+lhgg74srezsculLbLXHo6glxCddEr0a21gSKHu35oTcKkwepF+AmZbjS7wj4uYZC
+AvUVN0HdOhb3c4+PN7Y32qq5GuElmtiUej56GKCBD10XwNKaCABsKIaPArqO+068
+hjb5iur1mkNjdKE7Wl2aqLWwweOwrPAYIGNNh/CHHLCamcRMeapfBqJqsqPdOOuG
+Xjx0JKoVSL8glu+Yj7jWBg78rdm1Uot8nx94THsTOxi7ZT4Ui8GDTc/oQBPewGBz
+bWxGI+P4I7r/ZWJsiKjWXCmduTM5itzSM/zG7mMgoB+Kn9qgla5XM/auiupfHcJc
+ju66rDy1xBDOVuxJ49ga9FY2lrob0aLUB9ROmh8/JlgcRLWFdTbMpTRJU+6chWFn
+R6UG9MPk+H7cG/IrCRPHKtUiHSgYnhCE3E/klIIIMLTGgAbJxt1BidyL2oBCkQwk
+IZwynYwiIDaundIUAoWGf/t5Ca+U9trLG8cNH7g3g7Ojd0WJbOLVMEQ0Lh5nPHdu
+y4C8Ejt95R2tzw5h339GiqEqrsVZtDFHcmlmZmluIFNtaXRoIChLZXliYXNlKSA8
+Z2xpdHRlcnNoYXJrQGtleWJhc2UuaW8+iQE3BBMBCAAhBQJYK4eWAhsDBQsJCAcC
+BhUICQoLAgQWAgMBAh4BAheAAAoJEETvW16GHAmnFZMH/R6FknUGOR/sC+1Zifs8
+TCvXL1oBYcuEDE01c4QzkiFeOekvmjc+JzwOidSvMNXFGw3FoZTyO8C9tClaC1Bc
+kWLSqAPFLpvWF/kJKVKRyOq2RwwyrKLtcDJ1LIJevmzSxnZkGfS2uLQAo3slbhZ3
+Rpcms/9GaZc15NMBEj8sbLkSb88EWiAM9MdFbAlDm4Jp13B6/elw7M2+tK9HSbUl
+cb5m0/9n5zhBADdgZ1NNLFqcrbD01dNBVOgko7wuKDeK9g2CluxwTTLCttdV+7en
+MHX725NrfN30yZDRAv2ZfdGIl4TE0T5XGPmMb6QTwaFxHFcp59eSD5TB5ph+Rxfy
+8QSJAjMEEAEIAB0WIQQT+S8tWO/LZmkoTrTcsFtM+Jr8ZgUCWb7IxgAKCRDcsFtM
++Jr8ZjISEAC3N0/l7M6YOhybEoa9Z8Fr9LBiN5QZ1MU0AUjrBUk/IOQyP5PUagSn
+v0VBUopr9hEdNxuCCNk/DNyQpJPPpJAvTrb6A2/UwBB36i1C1GadtHKi0t4djFnv
+dIcpVLeA2+92bI1q504JTR5zLGuaIGf30YpUnyVOs/tlHYRNl6vHB0ccMl6XtTZG
+Nw/OaSO6zsZ7b2mtjmMmDgpa/A9cyQ6NJMSPGdd8M9wrA7CZNK4SSQABdKRCMQRp
+MHC18rNm14RmJ8skIWsCw3HJYyKGOj7xupHRki9/3W1Knx2q7OSNO5KnbEFfyOmN
+oyIj5NiPb/P1sd5z6qtySlzZ1fYOSQh9HtxCrxGUqW/oQHFb6/ljJA5z4XYasNrM
+DViI++DicrobY4nXYoui0ZLevg/FQ05GpF2ge7z07tk0slRj3ssgZ+TrizRl0VST
+81cvrdNQIUmi1p3I2OzHxu9bRyjWKwOVsklq091y5wZabEvA7JbmA8nNB8HV10OZ
+8mx1IjVha3GrLHiaOk/Gkomd/1p/1gX8VDyVVMezIv0pBJZpamtg0qu3aeF7rv83
+QY44LD0cBDgtohpCu1vqaxz7154seqj3hpmxRDkRAdJqfNuVgJdJhpFZeC8XOf2u
+4wD5jrIoQefLx5ExAXFwQwGDgtJzS3HWYl/q7Csz0z+CjTMsI7yQDokCHAQQAQgA
+BgUCWcA2xQAKCRDHpTzFjTsfjOHSD/9GloneXphdCKAT589zMjIxuWC6pCclk3Qu
+elePI9GgRUS2STkWw7tSmKZ5BFY8ywIE56D7ay/Q02rUYymIQn4oRdbpS4tH6fit
+nYUOBFrHDbLPWX55KpImjY7APoaBCfvF0S+rJuMT+6S4R36UF+PxCv2z5yzkz7u6
+9PvVFRQkgMQmKo3q2PrdSt9xlFtFe9Y5KAT5/UWlz2KNK4/rw2s7FEbQBv9ONzVE
+2txTe1iqUpVmNKN6TpO3hLdj0ldqiPQUsxUuhIdjFPoMXZpsO1t63T9I/jkD+d5/
+DPzVQRVgpf3Dcg5GIpy2PBmW5upSWwxh96QcplLeqN7LmmQLYHOs6mJMmqCtN6lk
+eaks9heso/i2zkIixDWdDPYOlEtnH50zheLtS1J2Ic6Xmn2FmWNS61KtgTfy6MLH
+19Tqz5Dsu+RB8uqGBA499YUJWvBSexW0yRb488EP5SYARhhk5roNOhPDroBXTvEV
+cXVmc54Ze4/cfT2/wwNyHMqbpG8itE0L0Qw+lfET7C0lcWi0tflowIqaFbDiVbEc
+7qk7FoGmTSyqfJJ0NhuDfev/wRwiE1Wak+R+sfQlqk696+vPWcIO3q/Mk9RO/oCM
+7Rf9I7y3ep3zyRAr1JjHvGRiO8IPECUE2LPL9rHonC+h0rJMyERojR2VKDxLCxlm
+/HtaKvUwuLQiR3JpZmZpbiBTbWl0aCA8Z3JpZmZpbkB1cmJpbnQuY29tPokBUgQw
+AQgAPBYhBA8RqYmHnou7/cHiNkTvW16GHAmnBQJfxmAMHh0gTm8gbG9uZ2VyIGVt
+cGxveWVkIGF0IFVyYmludAAKCRBE71tehhwJp77kB/9BmSOkTEBcG98Zk0RE6HvZ
+cJwEDKZhSv+ttQCfqwGp0NAQj5dd+zA9WIMNF0wnX0IHNA0BrZXCnlw8YZTG/hbP
+H6VBmH700xuTlfPiBE+VbUpf5LK2h0TBjAnO3KITLZqc55Y0zev3xCaLY0Fvj1Y4
+TcTDB+julW4qtSmCgVFwe2Viz6Z0X9TQrtzCHXA9WFzRqXjcj6RqQ/tZLhWEHvje
+ZwSGDMR9Pp977OZCDwd6VTl2gmvQpPcTyRVcjOLCZyqI1sPoeMyFnyBMZxbI4sV4
+lv3MuWcRQMXscTzKIcOA8sX/0aX+YOgxI1R/1K/G2dH67CcxLhg3mzIRBkss3xsA
+iQFOBBMBCAA4FiEEDxGpiYeei7v9weI2RO9bXoYcCacFAlnCi/0CGwMFCwkIBwIG
+FQgJCgsCBBYCAwECHgECF4AACgkQRO9bXoYcCacWvAgAkq088Xv4eXI9dpd+Cz8M
+wQELaKZcjtZTFwYnsuJZEWFDAfGPz/gzn2XhX7PoLAK+kRgdA7u93JbSBKC0ibBC
+KHs8NrU9E0CVPQowHDfvUzeG2A0gb0IBg+COMR6CZzMsCljicP1qnaxaMt8AuZkX
+ym0+k4vvOTZGpsTFgJ7OMlW+6U9IC8LlkPxz5dEjsWbY5iOEmd3zt1iSW6iUR+0c
+suDRimxqocptCWeprJy3zRBaJO0y9fP1GZsOchBFHGoZ6mJptxHmnOVXnrHTn/jP
+AXr1ESofNkeTbwedP1JzjhjfrJvgZ1R7vQw/b/9SGMgyU4c8IxIu48pAv/3X/DVV
++YkCHAQQAQgABgUCWcKOvwAKCRDHpTzFjTsfjP+XEACUACOvOZef0avzBnTfom6b
+ewcZ2USnrfqNP0GpVxkDD4e4e38YDFA8zLt1nIkFPtpg28+wcR+mY9by80JGz0ZY
+boNM9sqdGMQJyhJ2h91g/oZnw7jmqvSWLF3LejWLmNsEUal0Y/KG5wK6LKukn5OY
+PmDbiQZPTZcZ4y4neCruzm1NDQ9pbZmGkO1UQ37TTb91hyBDFcIfNoS1LHBU2cF5
+Lsol3TftaYO0QQZ90N2AC5u0i2PpgN6IN8N6pzkQ3bRe+YgeaADaQ67WrFZ0z4zS
+td17Q25OXBOQdv1IYJWFnom1nqGjfzy2MUiNM/ByGyVqrMQFSOdNi4IHyq9qk8MP
+ONMvhtSVNXr+8AlfYBG3fymOKP4RKEg8W/lR+0qmBNu3kxUEjMeTZEN2BYE4Bhq9
+Li1H/j7PZ/Y/fvsXS30ORrMkOunZswd4tCT5sXAaPtIJKmdsx9B5QiN6gqOYY5kG
+QdRR35UvLx1WlrAhuoGNXnL0xUZc8aNQfomLAwQMxLIMXNw6GXxI8DKrc54QFyTE
+VgCd9SmTO3opW/q8dqg2TYO418SF27iiG5RFsghOAb4TrqvqTDccPgv6hy5NPFQj
+G9BRyK86byc/VoXVbhCok/2oJCHDPO5fE5OJozblOyAh1aQIR/LWPUQ9wen+xD14
+tDPeJ+c7VFLSX9lYq9yzJrQfR3JpZmZpbiBTbWl0aCA8Z3JmbkB1cmJpbnQuY29t
+PokBUgQwAQgAPBYhBA8RqYmHnou7/cHiNkTvW16GHAmnBQJfxmAiHh0gTm8gbG9u
+Z2VyIGVtcGxveWVkIGF0IFVyYmludAAKCRBE71tehhwJp0nvB/9tYQQ5Q2dnlzBJ
+hrW8/aiRRkuJjog1ZlOKVc3WsA//qWH3UwPvz2pWMMyExzXMezOmy+mwZ4BgRRzD
+K42o2ICj6wqifzqVr2wGeKsO1qLu0GDdIfokwuQ4RbQqfQ3BHCBWFCJF3kkE1Iee
+h81DPskpI2lhIXGgjzeOA+4JRqmhG4CijXY5BKkMNdsFxHo8qx5WupvjEbF4ziSV
+Uafa2XmMywtzhALvBlHBM5hpO3AGFPC/pX9w64RTPu64jWTYC4ErOhOsjVDNCn8V
+APIvM9Fcoy2JRIC9N3OLlmHBpnbPZvb7b5dv1+yBWOUg1poaRDmyyMtSmS12XXl8
+PrAgiDoHiQFOBBMBCAA4FiEEDxGpiYeei7v9weI2RO9bXoYcCacFAlnCjA0CGwMF
+CwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQRO9bXoYcCafEAQf8DYC4TNjO9kf1
+MD+0Xsd7N/UOWY0osGD0qzkzuDGmvEtj9P82srWNkpQ+7ecGU4o6rK4YMeaBGBIv
+mbI5LpXvT9pAla1jeG2Yv/ig74doU0Ygj1nz2o7ec5+ONz2uqzdYC2GU9gYJPqeN
+fgNlNwMi3DwLOrRg6XxDTTP3iaWGbGXLHFw+kQxuwvV/TeCuT4zU1F1PVUxyD1Cg
+sE4Rx146JPSR07FPxDBGwJCOe4a6ylrZGijPotUEnOQT7Y6CInUt+Q3VHOb/MRtM
+LITJqLCLQkYBhZs92zLC16AxdU905Uwkli+5tRRXb0zgNhWCsX8IVJuMxdJ+Tut6
+gw06osLlj4kCHAQQAQgABgUCWcKOvwAKCRDHpTzFjTsfjM1CEAC3oxjE1GnZT66H
+XzT44v19MVB4Wu5zkLrIdcb3YqszibYXWusP5B/fyqCDHIE4dkmC/kTGSsMO31UO
+//yXsatpOe8785d+9/T5afRE3OUKn0y55bgFwMaMHB3jBe6edaz/dR04ggzBxmAk
+LE+8PdfpZrgYCoORyapnXsh2P0VkH92uSCUhWGIx6+ag813eGBjqfJF0Q++SeRfN
+n4olR1Na/LDFI8eG3Lv2ku0iuHuJoiAPSsQr2/kfFYMFIFA0KtOkJvf/p93irzjB
+Doc3FhDd5iQLoU0dhivkUcTjeyuQtzuvkdb+WKEVTPGNHgtd78jRRDyBh2zLiwNM
+GriD1fgE57UuNMkGg85PYJYAO5br0jxHp4ZSM+jpe3xgwMsnjw9b3eknpXgG2jtt
+vYmc/HpAM11ulcMUksaehg1TryOqbAmO8Ehu0Lqdh6BpQIaSLicZlpF2pymu9SyE
+B4B9ZeLC265NU3GLGDKu1gwrEYcWWOF90Q6mA8XVQoTCtSiczUoxPOjzkBpZUzgu
+jh3pu/TqAYnw4V7kDYimvmnp6GbiW/nc9iugAUzu7CckzlwBKATtPNNmtLnMEr2W
+aOf6JkIVV24RHT61sFhUIqFhbr8mpxRTOv3iXhTYH3VbiujXPd5pHWt0ZmyBJBbU
++uR8PsXf1Il9tzFUdV0f6gqgkkRJyrkBDQRUrIB5AQgAyBP+r0INTlvWhn386d2r
+ax7QoxB1OmTjROrp5Xs+Ahh2VyGWEwGwNDZoHd3dyQO8HXdKePMISP7URBR5QQ+J
+cUATd8K6ZgxVatDoki7jEgGWk161+XLWOAohyVTiCVCteus7KEBar0wVu4WE7JhZ
+pdX6zma58nmA/R8o9SpJNZGlJH/ia7Z2c3H7ag9IRc2B3uuOWLC0trm/2aF65VdY
+c5w3SB+uE2MJb2m7wXGUehMZ+voQcjEm3ptZzageDzdhlf3Ttjq5KJU4XLRmsVaA
+oQ6GrgPr31EEVm6cczCUBwCd/z7SjCsjI79Pk00iDtc7KYtpmsiO11Mly7uX3E30
+MQARAQABiQEfBBgBCAAJBQJUrIB5AhsMAAoJEETvW16GHAmntvkH/0FifrFYRRzR
+1Iz1ki9aPnUlDCL+qrHghsqIxi2cQJfc+iClJ4Ot/FzK1ZIAMtWqMvdTfh2NCD4O
+OYbk71N+rhaA9h6/XXfmVX7Tt/I374oqMKrvoj7derH2an/VVEw0yabC86MYsKvs
+eRWyWLdoqg6it9CckA9hDJ+S4tKrUdaIbp8UdghOW8jUQNcppNQSjA29LQx5fO3w
+mOfmx2QaxtLL6sg7McStE+G6kZdIElH4VQOu8XC9Qu8BtTJrTWTPirPQHqRZcZkQ
+yZ19FzXWsOz+kLSuIoC8OYUohaVWJnXxMi/VtMp+DaOD46yZ0L6wryCaiftQDcWP
+R/QKwpd4hf8=
+=qj6y
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/users/grfn/gws.fyi/recipes/tomato-sauce.org b/users/aspen/web/recipes/tomato-sauce.org
index 74e9b103c6..dec7468ac2 100644
--- a/users/grfn/gws.fyi/recipes/tomato-sauce.org
+++ b/users/aspen/web/recipes/tomato-sauce.org
@@ -34,8 +34,8 @@ some kind of fat (I prefer butter).
      sauce, it's added whole later (so ignore this bullet point).
    - If you feel like it (sometimes I do, usually I don't) you can also mince
      garlic here and sautΓ© that in with everything else. Add a little after the
-     onion, as garlic cooks slower than onion unless you want something roastier
-     (usually you don't for tomato sauce)
+     onion, as garlic cooks faster than onion, unless you want something
+     roastier (usually you don't for tomato sauce)
    - The traditional (so I'm told) thing to do with amatriciana, but also nice
      with all variations, is to add in a little crushed red pepper with the
      fat to flavor it slightly, but do this late so it doesn't burn
diff --git a/users/aspen/web/shell.nix b/users/aspen/web/shell.nix
new file mode 100644
index 0000000000..7e7fccdc93
--- /dev/null
+++ b/users/aspen/web/shell.nix
@@ -0,0 +1,9 @@
+with import <nixpkgs> { config.allowUnfree = true; };
+mkShell {
+  buildInputs = [
+    awscli
+    gnumake
+    tarsnap
+    certbot
+  ];
+}
diff --git a/users/grfn/gws.fyi/site.nix b/users/aspen/web/site.nix
index 33d4a71e7b..057c4d3ee6 100644
--- a/users/grfn/gws.fyi/site.nix
+++ b/users/aspen/web/site.nix
@@ -1,4 +1,4 @@
-args@{ pkgs ? import <nixpkgs> {}, ... }:
+args@{ pkgs ? import <nixpkgs> { }, ... }:
 
 let
 
diff --git a/users/aspen/wigglydonke.rs/.well-known/cf-2fa-verify.txt b/users/aspen/wigglydonke.rs/.well-known/cf-2fa-verify.txt
new file mode 100644
index 0000000000..1012e8282d
--- /dev/null
+++ b/users/aspen/wigglydonke.rs/.well-known/cf-2fa-verify.txt
@@ -0,0 +1 @@
+debe85916969017
diff --git a/users/grfn/wigglydonke.rs/index.html b/users/aspen/wigglydonke.rs/index.html
index 4fd7f25fcf..4fd7f25fcf 100644
--- a/users/grfn/wigglydonke.rs/index.html
+++ b/users/aspen/wigglydonke.rs/index.html
diff --git a/users/grfn/wigglydonke.rs/wd.png b/users/aspen/wigglydonke.rs/wd.png
index 217443e2df..217443e2df 100644
--- a/users/grfn/wigglydonke.rs/wd.png
+++ b/users/aspen/wigglydonke.rs/wd.png
Binary files differdiff --git a/users/grfn/gws.fyi/.envrc b/users/aspen/xanthous/.envrc
index be81feddb1..be81feddb1 100644
--- a/users/grfn/gws.fyi/.envrc
+++ b/users/aspen/xanthous/.envrc
diff --git a/users/grfn/xanthous/.github/actions/nix-build/Dockerfile b/users/aspen/xanthous/.github/actions/nix-build/Dockerfile
index cfe8e35df0..cfe8e35df0 100644
--- a/users/grfn/xanthous/.github/actions/nix-build/Dockerfile
+++ b/users/aspen/xanthous/.github/actions/nix-build/Dockerfile
diff --git a/users/grfn/xanthous/.github/actions/nix-build/entrypoint.sh b/users/aspen/xanthous/.github/actions/nix-build/entrypoint.sh
index cb7aca541a..cb7aca541a 100755
--- a/users/grfn/xanthous/.github/actions/nix-build/entrypoint.sh
+++ b/users/aspen/xanthous/.github/actions/nix-build/entrypoint.sh
diff --git a/users/grfn/xanthous/.github/workflows/haskell.yml b/users/aspen/xanthous/.github/workflows/haskell.yml
index df82de3e8c..df82de3e8c 100644
--- a/users/grfn/xanthous/.github/workflows/haskell.yml
+++ b/users/aspen/xanthous/.github/workflows/haskell.yml
diff --git a/users/grfn/xanthous/.gitignore b/users/aspen/xanthous/.gitignore
index 2ad31c01d4..2ad31c01d4 100644
--- a/users/grfn/xanthous/.gitignore
+++ b/users/aspen/xanthous/.gitignore
diff --git a/users/grfn/xanthous/LICENSE b/users/aspen/xanthous/LICENSE
index 45644ff764..45644ff764 100644
--- a/users/grfn/xanthous/LICENSE
+++ b/users/aspen/xanthous/LICENSE
diff --git a/users/grfn/xanthous/README.org b/users/aspen/xanthous/README.org
index 7e1fedb069..7e1fedb069 100644
--- a/users/grfn/xanthous/README.org
+++ b/users/aspen/xanthous/README.org
diff --git a/users/grfn/xanthous/Setup.hs b/users/aspen/xanthous/Setup.hs
index 9a994af677..9a994af677 100644
--- a/users/grfn/xanthous/Setup.hs
+++ b/users/aspen/xanthous/Setup.hs
diff --git a/users/grfn/xanthous/app/Main.hs b/users/aspen/xanthous/app/Main.hs
index c771a0d932..c771a0d932 100644
--- a/users/grfn/xanthous/app/Main.hs
+++ b/users/aspen/xanthous/app/Main.hs
diff --git a/users/grfn/xanthous/bench/Bench.hs b/users/aspen/xanthous/bench/Bench.hs
index 5889618ee4..5889618ee4 100644
--- a/users/grfn/xanthous/bench/Bench.hs
+++ b/users/aspen/xanthous/bench/Bench.hs
diff --git a/users/grfn/xanthous/bench/Bench/Prelude.hs b/users/aspen/xanthous/bench/Bench/Prelude.hs
index c553abd6d5..c553abd6d5 100644
--- a/users/grfn/xanthous/bench/Bench/Prelude.hs
+++ b/users/aspen/xanthous/bench/Bench/Prelude.hs
diff --git a/users/grfn/xanthous/bench/Xanthous/Generators/UtilBench.hs b/users/aspen/xanthous/bench/Xanthous/Generators/UtilBench.hs
index 56310e691c..56310e691c 100644
--- a/users/grfn/xanthous/bench/Xanthous/Generators/UtilBench.hs
+++ b/users/aspen/xanthous/bench/Xanthous/Generators/UtilBench.hs
diff --git a/users/grfn/xanthous/bench/Xanthous/RandomBench.hs b/users/aspen/xanthous/bench/Xanthous/RandomBench.hs
index fae4af92a7..fae4af92a7 100644
--- a/users/grfn/xanthous/bench/Xanthous/RandomBench.hs
+++ b/users/aspen/xanthous/bench/Xanthous/RandomBench.hs
diff --git a/third_party/overlays/haskell/patches/generic-arbitrary-export-garbitrary.patch b/users/aspen/xanthous/build/generic-arbitrary-export-garbitrary.patch
index f0c936bfca..f0c936bfca 100644
--- a/third_party/overlays/haskell/patches/generic-arbitrary-export-garbitrary.patch
+++ b/users/aspen/xanthous/build/generic-arbitrary-export-garbitrary.patch
diff --git a/users/grfn/xanthous/build/hgeometry-fix-haddock.patch b/users/aspen/xanthous/build/hgeometry-fix-haddock.patch
index 748c65b3e0..748c65b3e0 100644
--- a/users/grfn/xanthous/build/hgeometry-fix-haddock.patch
+++ b/users/aspen/xanthous/build/hgeometry-fix-haddock.patch
diff --git a/users/grfn/xanthous/build/update-comonad-extras.patch b/users/aspen/xanthous/build/update-comonad-extras.patch
index cd1dbe24d3..cd1dbe24d3 100644
--- a/users/grfn/xanthous/build/update-comonad-extras.patch
+++ b/users/aspen/xanthous/build/update-comonad-extras.patch
diff --git a/users/aspen/xanthous/default.nix b/users/aspen/xanthous/default.nix
new file mode 100644
index 0000000000..049c92fb4c
--- /dev/null
+++ b/users/aspen/xanthous/default.nix
@@ -0,0 +1,27 @@
+{ depot ? (import ../../../. { })
+, pkgs ? depot.third_party.nixpkgs
+, ...
+}:
+
+let
+  ignore = depot.third_party.gitignoreSource.gitignoreFilter ./.;
+  src = builtins.path {
+    name = "xanthous-source";
+    path = ./.;
+    filter = path: type:
+      !(type == "directory" && builtins.baseNameOf path == "server")
+      && !(type == "directory" && builtins.baseNameOf path == "docs")
+      && (ignore path type
+      || builtins.baseNameOf path == "package.yaml");
+  };
+  # generated by cabal2nix
+  basePkg = pkgs.haskell.packages.ghc8107.callPackage ./pkg.nix { };
+in
+
+pkgs.haskell.lib.overrideCabal basePkg (default: {
+  inherit src;
+  version = "canon";
+  configureFlags = [
+    "--ghc-option=-Wall --ghc-option=-Werror"
+  ] ++ (default.configureFlags or [ ]);
+})
diff --git a/users/grfn/xanthous/docs/raw-types.org b/users/aspen/xanthous/docs/raw-types.org
index e5bcda0426..e5bcda0426 100644
--- a/users/grfn/xanthous/docs/raw-types.org
+++ b/users/aspen/xanthous/docs/raw-types.org
diff --git a/users/grfn/xanthous/hie.yaml b/users/aspen/xanthous/hie.yaml
index e7cf01d158..e7cf01d158 100644
--- a/users/grfn/xanthous/hie.yaml
+++ b/users/aspen/xanthous/hie.yaml
diff --git a/users/grfn/xanthous/nixpkgs.nix b/users/aspen/xanthous/nixpkgs.nix
index 7d7c164405..7d7c164405 100644
--- a/users/grfn/xanthous/nixpkgs.nix
+++ b/users/aspen/xanthous/nixpkgs.nix
diff --git a/users/grfn/xanthous/package.yaml b/users/aspen/xanthous/package.yaml
index 630dc69c11..15a36fe964 100644
--- a/users/grfn/xanthous/package.yaml
+++ b/users/aspen/xanthous/package.yaml
@@ -111,6 +111,7 @@ default-extensions:
 
 ghc-options:
 - -Wall
+- -fconstraint-solver-iterations=6 # Xanthous.Data, Xanthous.Command
 
 library:
   source-dirs: src
diff --git a/users/aspen/xanthous/pkg.nix b/users/aspen/xanthous/pkg.nix
new file mode 100644
index 0000000000..f8364c467a
--- /dev/null
+++ b/users/aspen/xanthous/pkg.nix
@@ -0,0 +1,349 @@
+{ mkDerivation
+, aeson
+, array
+, async
+, base
+, bifunctors
+, brick
+, checkers
+, classy-prelude
+, comonad
+, comonad-extras
+, constraints
+, containers
+, criterion
+, data-default
+, data-interval
+, deepseq
+, directory
+, fgl
+, fgl-arbitrary
+, file-embed
+, filepath
+, generic-arbitrary
+, generic-lens
+, groups
+, hgeometry
+, hgeometry-combinatorial
+, hpack
+, JuicyPixels
+, lens
+, lens-properties
+, lib
+, lifted-async
+, linear
+, megaparsec
+, mmorph
+, monad-control
+, MonadRandom
+, mtl
+, optparse-applicative
+, parallel
+, parser-combinators
+, pointed
+, QuickCheck
+, quickcheck-instances
+, quickcheck-text
+, random
+, random-extras
+, random-fu
+, random-source
+, Rasterific
+, raw-strings-qq
+, reflection
+, semigroupoids
+, semigroups
+, splitmix
+, stache
+, streams
+, tasty
+, tasty-hunit
+, tasty-quickcheck
+, tasty-rerun
+, text
+, text-zipper
+, tomland
+, transformers
+, vector
+, vty
+, witherable
+, yaml
+, zlib
+}:
+mkDerivation {
+  pname = "xanthous";
+  version = "0.1.0.0";
+  src = ./.;
+  isLibrary = true;
+  isExecutable = true;
+  libraryHaskellDepends = [
+    aeson
+    array
+    async
+    base
+    bifunctors
+    brick
+    checkers
+    classy-prelude
+    comonad
+    comonad-extras
+    constraints
+    containers
+    criterion
+    data-default
+    data-interval
+    deepseq
+    directory
+    fgl
+    fgl-arbitrary
+    file-embed
+    filepath
+    generic-arbitrary
+    generic-lens
+    groups
+    hgeometry
+    hgeometry-combinatorial
+    JuicyPixels
+    lens
+    lifted-async
+    linear
+    megaparsec
+    mmorph
+    monad-control
+    MonadRandom
+    mtl
+    optparse-applicative
+    parallel
+    parser-combinators
+    pointed
+    QuickCheck
+    quickcheck-instances
+    quickcheck-text
+    random
+    random-extras
+    random-fu
+    random-source
+    Rasterific
+    raw-strings-qq
+    reflection
+    semigroupoids
+    semigroups
+    splitmix
+    stache
+    streams
+    text
+    text-zipper
+    tomland
+    transformers
+    vector
+    vty
+    witherable
+    yaml
+    zlib
+  ];
+  libraryToolDepends = [ hpack ];
+  executableHaskellDepends = [
+    aeson
+    array
+    async
+    base
+    bifunctors
+    brick
+    checkers
+    classy-prelude
+    comonad
+    comonad-extras
+    constraints
+    containers
+    criterion
+    data-default
+    data-interval
+    deepseq
+    directory
+    fgl
+    fgl-arbitrary
+    file-embed
+    filepath
+    generic-arbitrary
+    generic-lens
+    groups
+    hgeometry
+    hgeometry-combinatorial
+    JuicyPixels
+    lens
+    lifted-async
+    linear
+    megaparsec
+    mmorph
+    monad-control
+    MonadRandom
+    mtl
+    optparse-applicative
+    parallel
+    parser-combinators
+    pointed
+    QuickCheck
+    quickcheck-instances
+    quickcheck-text
+    random
+    random-extras
+    random-fu
+    random-source
+    Rasterific
+    raw-strings-qq
+    reflection
+    semigroupoids
+    semigroups
+    splitmix
+    stache
+    streams
+    text
+    text-zipper
+    tomland
+    transformers
+    vector
+    vty
+    witherable
+    yaml
+    zlib
+  ];
+  testHaskellDepends = [
+    aeson
+    array
+    async
+    base
+    bifunctors
+    brick
+    checkers
+    classy-prelude
+    comonad
+    comonad-extras
+    constraints
+    containers
+    criterion
+    data-default
+    data-interval
+    deepseq
+    directory
+    fgl
+    fgl-arbitrary
+    file-embed
+    filepath
+    generic-arbitrary
+    generic-lens
+    groups
+    hgeometry
+    hgeometry-combinatorial
+    JuicyPixels
+    lens
+    lens-properties
+    lifted-async
+    linear
+    megaparsec
+    mmorph
+    monad-control
+    MonadRandom
+    mtl
+    optparse-applicative
+    parallel
+    parser-combinators
+    pointed
+    QuickCheck
+    quickcheck-instances
+    quickcheck-text
+    random
+    random-extras
+    random-fu
+    random-source
+    Rasterific
+    raw-strings-qq
+    reflection
+    semigroupoids
+    semigroups
+    splitmix
+    stache
+    streams
+    tasty
+    tasty-hunit
+    tasty-quickcheck
+    tasty-rerun
+    text
+    text-zipper
+    tomland
+    transformers
+    vector
+    vty
+    witherable
+    yaml
+    zlib
+  ];
+  benchmarkHaskellDepends = [
+    aeson
+    array
+    async
+    base
+    bifunctors
+    brick
+    checkers
+    classy-prelude
+    comonad
+    comonad-extras
+    constraints
+    containers
+    criterion
+    data-default
+    data-interval
+    deepseq
+    directory
+    fgl
+    fgl-arbitrary
+    file-embed
+    filepath
+    generic-arbitrary
+    generic-lens
+    groups
+    hgeometry
+    hgeometry-combinatorial
+    JuicyPixels
+    lens
+    lifted-async
+    linear
+    megaparsec
+    mmorph
+    monad-control
+    MonadRandom
+    mtl
+    optparse-applicative
+    parallel
+    parser-combinators
+    pointed
+    QuickCheck
+    quickcheck-instances
+    quickcheck-text
+    random
+    random-extras
+    random-fu
+    random-source
+    Rasterific
+    raw-strings-qq
+    reflection
+    semigroupoids
+    semigroups
+    splitmix
+    stache
+    streams
+    text
+    text-zipper
+    tomland
+    transformers
+    vector
+    vty
+    witherable
+    yaml
+    zlib
+  ];
+  prePatch = "hpack";
+  homepage = "https://github.com/glittershark/xanthous#readme";
+  description = "A WIP TUI RPG";
+  license = lib.licenses.gpl3Only;
+}
diff --git a/users/grfn/secrets/.envrc b/users/aspen/xanthous/server/.envrc
index 051d09d292..051d09d292 100644
--- a/users/grfn/secrets/.envrc
+++ b/users/aspen/xanthous/server/.envrc
diff --git a/users/aspen/xanthous/server/.gitignore b/users/aspen/xanthous/server/.gitignore
new file mode 100644
index 0000000000..2f7896d1d1
--- /dev/null
+++ b/users/aspen/xanthous/server/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/aspen/xanthous/server/Cargo.lock b/users/aspen/xanthous/server/Cargo.lock
new file mode 100644
index 0000000000..173298b158
--- /dev/null
+++ b/users/aspen/xanthous/server/Cargo.lock
@@ -0,0 +1,1874 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aes"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+ "ctr",
+ "opaque-debug",
+]
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "atomic-shim"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67cd4b51d303cf3501c301e8125df442128d3c6d7c69f71b27833d253de47e77"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64ct"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6b4d9b1225d28d360ec6a231d65af1fd99a2a095154c8040689617290569c5c"
+
+[[package]]
+name = "bcrypt-pbkdf"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c38c03b9506bd92bf1ef50665a81eda156f615438f7654bffba58907e6149d7"
+dependencies = [
+ "blowfish",
+ "crypto-mac",
+ "pbkdf2",
+ "sha2",
+ "zeroize",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block-modes"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e"
+dependencies = [
+ "block-padding",
+ "cipher",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
+
+[[package]]
+name = "blowfish"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe3ff3fc1de48c1ac2e3341c4df38b0d1bfb8fdf04632a187c8b75aaa319a7ab"
+dependencies = [
+ "byteorder",
+ "cipher",
+ "opaque-debug",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "bytes"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
+dependencies = [
+ "iana-time-zone",
+ "num-integer",
+ "num-traits",
+ "winapi",
+]
+
+[[package]]
+name = "cipher"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "clap"
+version = "3.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_derive",
+ "clap_lex",
+ "indexmap",
+ "once_cell",
+ "strsim",
+ "termcolor",
+ "textwrap",
+]
+
+[[package]]
+name = "clap_derive"
+version = "3.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "color-eyre"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7"
+dependencies = [
+ "backtrace",
+ "color-spantrace",
+ "eyre",
+ "indenter",
+ "once_cell",
+ "owo-colors",
+ "tracing-error",
+]
+
+[[package]]
+name = "color-spantrace"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1"
+dependencies = [
+ "once_cell",
+ "owo-colors",
+ "tracing-core",
+ "tracing-error",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-mac"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
+[[package]]
+name = "cryptovec"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccc7fa13a6bbb2322d325292c57f4c8e7291595506f8289968a0eb61c3130bdf"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "ctr"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
+name = "dashmap"
+version = "4.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
+dependencies = [
+ "cfg-if",
+ "num_cpus",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
+
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "dirs"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "endian-type"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
+
+[[package]]
+name = "eyre"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
+dependencies = [
+ "indenter",
+ "once_cell",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "futures"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56"
+
+[[package]]
+name = "futures-task"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
+
+[[package]]
+name = "futures-util"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "gimli"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hmac"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+dependencies = [
+ "crypto-mac",
+ "digest",
+]
+
+[[package]]
+name = "http"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
+[[package]]
+name = "hyper"
+version = "0.14.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "js-sys",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "indenter"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
+
+[[package]]
+name = "itoa"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.134"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
+
+[[package]]
+name = "libsodium-sys"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "walkdir",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "mach"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "matchers"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
+dependencies = [
+ "regex-automata",
+]
+
+[[package]]
+name = "md5"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "metrics"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55586aa936c35f34ba8aa5d97356d554311206e1ce1f9e68fe7b07288e5ad827"
+dependencies = [
+ "ahash",
+ "metrics-macros",
+]
+
+[[package]]
+name = "metrics-exporter-prometheus"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "343a5ceb38235928e7a5687412590f07e6d281522dcd9ff51246f8856eef5fe5"
+dependencies = [
+ "hyper",
+ "ipnet",
+ "metrics",
+ "metrics-util",
+ "parking_lot",
+ "quanta",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "metrics-macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0daa0ab3a0ae956d0e2c1f42511422850e577d36a255357d1a7d08d45ee3a2f1"
+dependencies = [
+ "lazy_static",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "syn",
+]
+
+[[package]]
+name = "metrics-util"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1174223789e331d9d47a4a953dac36e397db60fa8d2a111ac505388c6c7fe32e"
+dependencies = [
+ "ahash",
+ "aho-corasick",
+ "atomic-shim",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+ "dashmap",
+ "hashbrown 0.11.2",
+ "indexmap",
+ "metrics",
+ "num_cpus",
+ "ordered-float",
+ "parking_lot",
+ "quanta",
+ "radix_trie",
+ "sketches-ddsketch",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
+dependencies = [
+ "libc",
+ "log",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys",
+]
+
+[[package]]
+name = "nibble_vec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "nix"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "ordered-float"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "os_str_bytes"
+version = "6.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
+
+[[package]]
+name = "owo-colors"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "password-hash"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7"
+dependencies = [
+ "base64ct",
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "pbkdf2"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa"
+dependencies = [
+ "base64ct",
+ "crypto-mac",
+ "hmac",
+ "password-hash",
+ "sha2",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quanta"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8"
+dependencies = [
+ "crossbeam-utils",
+ "libc",
+ "mach",
+ "once_cell",
+ "raw-cpuid",
+ "wasi 0.10.2+wasi-snapshot-preview1",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "radix_trie"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
+dependencies = [
+ "endian-type",
+ "nibble_vec",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "raw-cpuid"
+version = "10.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom",
+ "redox_syscall",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.145"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.145"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer",
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+ "opaque-debug",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "sketches-ddsketch"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04d2ecae5fcf33b122e2e6bd520a57ccf152d2dde3b38c71039df1a6867264ee"
+
+[[package]]
+name = "slab"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
+
+[[package]]
+name = "socket2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "subtle"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+
+[[package]]
+name = "syn"
+version = "1.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
+
+[[package]]
+name = "thiserror"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "thrussh"
+version = "0.33.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e6540238a9adf83df6e66541c182a52acf892ab335595ca965c229ade8536f8"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "cryptovec",
+ "digest",
+ "flate2",
+ "futures",
+ "generic-array",
+ "log",
+ "rand",
+ "sha2",
+ "thiserror",
+ "thrussh-keys",
+ "thrussh-libsodium",
+ "tokio",
+]
+
+[[package]]
+name = "thrussh-keys"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a72cc51a2932b18d92f7289332d8564cec4a5014063722a9d3fdca52c5d8f5ab"
+dependencies = [
+ "aes",
+ "bcrypt-pbkdf",
+ "bit-vec",
+ "block-modes",
+ "byteorder",
+ "cryptovec",
+ "data-encoding",
+ "dirs",
+ "futures",
+ "hmac",
+ "log",
+ "md5",
+ "num-bigint",
+ "num-integer",
+ "pbkdf2",
+ "rand",
+ "serde",
+ "serde_derive",
+ "sha2",
+ "thiserror",
+ "thrussh-libsodium",
+ "tokio",
+ "tokio-stream",
+ "yasna",
+]
+
+[[package]]
+name = "thrussh-libsodium"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfe89c70d27b1cb92e13bc8af63493e890d0de46dae4df0e28233f62b4ed9500"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "libsodium-sys",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "tokio"
+version = "1.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
+dependencies = [
+ "autocfg",
+ "bytes",
+ "libc",
+ "memchr",
+ "mio",
+ "num_cpus",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "winapi",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-error"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
+dependencies = [
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
+dependencies = [
+ "lazy_static",
+ "log",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-serde"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
+dependencies = [
+ "ansi_term",
+ "chrono",
+ "lazy_static",
+ "matchers",
+ "regex",
+ "serde",
+ "serde_json",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-serde",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
+
+[[package]]
+name = "typenum"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
+dependencies = [
+ "log",
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+
+[[package]]
+name = "web-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
+name = "xanthous-server"
+version = "0.1.0"
+dependencies = [
+ "base64ct",
+ "clap",
+ "color-eyre",
+ "eyre",
+ "futures",
+ "libc",
+ "metrics",
+ "metrics-exporter-prometheus",
+ "nix",
+ "pbkdf2",
+ "tempfile",
+ "thrussh",
+ "thrussh-keys",
+ "tokio",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "yasna"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75"
+dependencies = [
+ "bit-vec",
+ "num-bigint",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
diff --git a/users/aspen/xanthous/server/Cargo.toml b/users/aspen/xanthous/server/Cargo.toml
new file mode 100644
index 0000000000..d4a064beb6
--- /dev/null
+++ b/users/aspen/xanthous/server/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "xanthous-server"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+clap = { version = "3.0", features = [ "derive", "env" ] }
+color-eyre = "0.5.11"
+eyre = "0.6.5"
+thrussh = "0.33.5"
+thrussh-keys = "0.21.0"
+tracing = "0.1.29"
+tracing-subscriber = "0.2.25"
+metrics = "0.17.0"
+metrics-exporter-prometheus = "0.6.1"
+futures = "0.3.17"
+libc = "0.2.103"
+nix = "0.23.0"
+
+# Pins for rust 1.55 (2018 edition) until we have 1.56 in nixpkgs-unstable
+pbkdf2 = "<0.9"
+base64ct = "<1.2"
+
+[dependencies.tokio]
+version = "1.13"
+features = ["rt", "rt-multi-thread", "macros", "net", "process", "fs", "signal"]
+
+[dev-dependencies]
+tempfile = "3.2.0"
diff --git a/users/aspen/xanthous/server/default.nix b/users/aspen/xanthous/server/default.nix
new file mode 100644
index 0000000000..572230a56c
--- /dev/null
+++ b/users/aspen/xanthous/server/default.nix
@@ -0,0 +1,24 @@
+args@{ depot ? import ../../../.. { }
+, pkgs ? depot.third_party.nixpkgs
+, ...
+}:
+
+depot.third_party.naersk.buildPackage {
+  name = "xanthous-server";
+  version = "0.0.1";
+  src = depot.third_party.gitignoreSource ./.;
+
+  # Workaround for a potential Nix bug related to restricted eval.
+  # See https://github.com/nix-community/naersk/issues/169
+  root = depot.nix.sparseTree {
+    root = ./.;
+    paths = [
+      ./Cargo.toml
+      ./Cargo.lock
+    ];
+  };
+
+  passthru = {
+    docker = import ./docker.nix args;
+  };
+}
diff --git a/users/aspen/xanthous/server/docker.nix b/users/aspen/xanthous/server/docker.nix
new file mode 100644
index 0000000000..5eaef4553b
--- /dev/null
+++ b/users/aspen/xanthous/server/docker.nix
@@ -0,0 +1,21 @@
+{ depot ? import ../../../.. { }
+, pkgs ? depot.third_party.nixpkgs
+, ...
+}:
+
+let
+  inherit (depot.users.aspen) xanthous;
+  xanthous-server = xanthous.server;
+in
+pkgs.dockerTools.buildLayeredImage {
+  name = "xanthous-server";
+  tag = "latest";
+  contents = [ xanthous xanthous-server ];
+  config = {
+    Cmd = [
+      "${xanthous-server}/bin/xanthous-server"
+      "--xanthous-binary-path"
+      "${xanthous}/bin/xanthous"
+    ];
+  };
+}
diff --git a/users/aspen/xanthous/server/module.nix b/users/aspen/xanthous/server/module.nix
new file mode 100644
index 0000000000..6d1bdc6873
--- /dev/null
+++ b/users/aspen/xanthous/server/module.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, depot, ... }:
+
+let
+  cfg = config.services.xanthous-server;
+in
+{
+  options = with lib; {
+    services.xanthous-server = {
+      enable = mkEnableOption "xanthous server";
+
+      port = mkOption {
+        type = types.int;
+        default = 2222;
+        description = "Port to listen to for SSH connections";
+      };
+
+      metricsPort = mkOption {
+        type = types.int;
+        default = 9000;
+        description = "Port to listen to for prometheus metrics";
+      };
+
+      image = mkOption {
+        type = types.package;
+        default = depot.users.aspen.xanthous.server.docker;
+        description = "OCI image file to run";
+      };
+
+      ed25519SecretKeyFile = mkOption {
+        type = with types; uniq string;
+        description = "Path to the ed25519 secret key for the server";
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    virtualisation.oci-containers.containers."xanthous-server" = {
+      autoStart = true;
+      image = "${cfg.image.imageName}:${cfg.image.imageTag}";
+      imageFile = cfg.image;
+      ports = [
+        "${toString cfg.port}:22"
+        "${toString cfg.metricsPort}:9000"
+      ];
+      environment.SECRET_KEY_FILE = "/secret-key";
+      volumes = [ "/etc/secrets/xanthous-server-secret-key:/secret-key" ];
+    };
+  };
+}
diff --git a/users/aspen/xanthous/server/shell.nix b/users/aspen/xanthous/server/shell.nix
new file mode 100644
index 0000000000..e01c0316a6
--- /dev/null
+++ b/users/aspen/xanthous/server/shell.nix
@@ -0,0 +1,11 @@
+let
+  depot = import ../../../.. { };
+  pkgs = depot.third_party.nixpkgs;
+in
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    rustup
+    rust-analyzer
+  ];
+}
diff --git a/users/aspen/xanthous/server/src/main.rs b/users/aspen/xanthous/server/src/main.rs
new file mode 100644
index 0000000000..1b2c1c104b
--- /dev/null
+++ b/users/aspen/xanthous/server/src/main.rs
@@ -0,0 +1,385 @@
+use std::net::SocketAddr;
+use std::path::PathBuf;
+use std::pin::Pin;
+use std::process::Command;
+use std::str;
+use std::sync::Arc;
+
+use clap::Parser;
+use color_eyre::eyre::Result;
+use eyre::{bail, Context};
+use futures::future::{ready, Ready};
+use futures::Future;
+use metrics_exporter_prometheus::PrometheusBuilder;
+use nix::pty::Winsize;
+use pty::ChildHandle;
+use thrussh::server::{self, Auth, Session};
+use thrussh::{ChannelId, CryptoVec};
+use thrussh_keys::decode_secret_key;
+use thrussh_keys::key::KeyPair;
+use tokio::fs::File;
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio::net::TcpListener;
+use tokio::select;
+use tokio::time::Instant;
+use tracing::{debug, error, info, info_span, trace, warn, Instrument};
+use tracing_subscriber::EnvFilter;
+
+use crate::pty::WaitPid;
+
+mod metrics;
+mod pty;
+
+use crate::metrics::reported::*;
+use crate::metrics::{decrement_gauge, histogram, increment_counter, increment_gauge};
+
+/// SSH-compatible server for playing Xanthous
+#[derive(Parser, Debug)]
+struct Opts {
+    /// Address to bind to
+    #[clap(long, short = 'a', default_value = "0.0.0.0:22")]
+    address: String,
+
+    /// Address to listen to for metrics
+    #[clap(long, default_value = "0.0.0.0:9000")]
+    metrics_address: SocketAddr,
+
+    /// Format to use when emitting log events
+    #[clap(
+        long,
+        env = "LOG_FORMAT",
+        default_value = "full",
+        possible_values = &["compact", "full", "pretty", "json"]
+    )]
+    log_format: String,
+
+    /// Full path to the xanthous binary
+    #[clap(long, env = "XANTHOUS_BINARY_PATH")]
+    xanthous_binary_path: String,
+
+    /// Path to a file containing the ed25519 secret key for the server
+    #[clap(long, env = "SECRET_KEY_FILE")]
+    secret_key_file: PathBuf,
+
+    /// Level to log at
+    #[clap(long, env = "LOG_LEVEL", default_value = "info")]
+    log_level: String,
+}
+
+impl Opts {
+    async fn read_secret_key(&self) -> Result<KeyPair> {
+        let mut file = File::open(&self.secret_key_file)
+            .await
+            .context("Reading secret key file")?;
+        let mut secret_key = Vec::with_capacity(464);
+        file.read_to_end(&mut secret_key).await?;
+        Ok(decode_secret_key(str::from_utf8(&secret_key)?, None)?)
+    }
+
+    async fn ssh_server_config(&self) -> Result<server::Config> {
+        let key_pair = self.read_secret_key().await?;
+
+        Ok(server::Config {
+            server_id: "SSH-2.0-xanthous".to_owned(),
+            keys: vec![key_pair],
+            ..Default::default()
+        })
+    }
+
+    fn init_logging(&self) -> Result<()> {
+        let filter = EnvFilter::try_new(&self.log_level)?;
+        let s = tracing_subscriber::fmt().with_env_filter(filter);
+
+        match self.log_format.as_str() {
+            "compact" => s.compact().init(),
+            "full" => s.init(),
+            "pretty" => s.pretty().init(),
+            "json" => s.json().with_current_span(true).init(),
+            f => bail!("Invalid log format `{}`", f),
+        }
+
+        Ok(())
+    }
+}
+
+struct Handler {
+    address: SocketAddr,
+    xanthous_binary_path: &'static str,
+    username: Option<String>,
+    child: Option<ChildHandle>,
+}
+
+async fn run_child(
+    mut child: pty::Child,
+    mut server_handle: server::Handle,
+    channel_id: ChannelId,
+) -> Result<()> {
+    let mut buf = [0; 2048];
+    loop {
+        select! {
+            r = child.tty.read(&mut buf)  => {
+                let read_bytes = r?;
+                if read_bytes == 0 {
+                    info!("EOF received from process");
+                    let _ = server_handle.close(channel_id).await;
+                    return Ok(())
+                } else {
+                    trace!(?read_bytes, "read bytes from child");
+                    let _ = server_handle.data(channel_id, CryptoVec::from_slice(&buf[..read_bytes])).await;
+                }
+            }
+            status = WaitPid::new(child.pid) => {
+                match status {
+                    Ok(_status) => info!("Child exited"),
+                    Err(error) => error!(%error, "Child failed"),
+                }
+                let _ = server_handle.close(channel_id).await;
+                return Ok(())
+            }
+        }
+    }
+}
+
+impl Handler {
+    async fn spawn_shell(
+        &mut self,
+        mut handle: server::Handle,
+        channel_id: ChannelId,
+        term: String,
+        winsize: Winsize,
+    ) -> Result<()> {
+        let mut cmd = Command::new(self.xanthous_binary_path);
+        cmd.env("TERM", term);
+        if let Some(username) = &self.username {
+            cmd.args(["--name", username]);
+        }
+        cmd.arg("--disable-saving");
+
+        let child = pty::spawn(cmd, Some(winsize), None).await?;
+        info!(pid = %child.pid, "Spawned child");
+        increment_gauge!(RUNNING_PROCESSES, 1.0);
+        self.child = Some(child.handle().await?);
+        tokio::spawn(
+            async move {
+                let span = info_span!("child", pid = %child.pid);
+                if let Err(error) = run_child(child, handle.clone(), channel_id)
+                    .instrument(span.clone())
+                    .await
+                {
+                    span.in_scope(|| error!(%error, "Error running child"));
+                    let _ = handle.close(channel_id).await;
+                }
+                decrement_gauge!(RUNNING_PROCESSES, 1.0);
+            }
+            .in_current_span(),
+        );
+        Ok(())
+    }
+}
+
+#[allow(clippy::type_complexity)]
+impl server::Handler for Handler {
+    type Error = eyre::Error;
+    type FutureAuth = Ready<Result<(Self, Auth)>>;
+    type FutureUnit = Pin<Box<dyn Future<Output = Result<(Self, Session)>> + Send + 'static>>;
+    type FutureBool = Ready<Result<(Self, Session, bool)>>;
+
+    fn finished_auth(self, auth: Auth) -> Self::FutureAuth {
+        ready(Ok((self, auth)))
+    }
+
+    fn finished_bool(self, b: bool, session: Session) -> Self::FutureBool {
+        ready(Ok((self, session, b)))
+    }
+
+    fn finished(self, session: Session) -> Self::FutureUnit {
+        Box::pin(ready(Ok((self, session))))
+    }
+
+    fn auth_none(mut self, username: &str) -> Self::FutureAuth {
+        info!(%username, "Accepted new connection");
+        self.username = Some(username.to_owned());
+        self.finished_auth(Auth::Accept)
+    }
+
+    fn auth_password(mut self, username: &str, _password: &str) -> Self::FutureAuth {
+        info!(%username, "Accepted new connection");
+        self.username = Some(username.to_owned());
+        self.finished_auth(Auth::Accept)
+    }
+
+    fn auth_publickey(
+        mut self,
+        username: &str,
+        _: &thrussh_keys::key::PublicKey,
+    ) -> Self::FutureAuth {
+        info!(%username, "Accepted new connection");
+        self.username = Some(username.to_owned());
+        self.finished_auth(Auth::Accept)
+    }
+
+    fn pty_request(
+        mut self,
+        channel: thrussh::ChannelId,
+        term: &str,
+        col_width: u32,
+        row_height: u32,
+        pix_width: u32,
+        pix_height: u32,
+        modes: &[(thrussh::Pty, u32)],
+        session: Session,
+    ) -> Self::FutureUnit {
+        let term = term.to_owned();
+        let modes = modes.to_vec();
+        Box::pin(async move {
+            debug!(
+                %term,
+                %col_width,
+                %row_height,
+                %pix_width,
+                %pix_height,
+                ?modes,
+                "PTY Requested"
+            );
+
+            self.spawn_shell(
+                session.handle(),
+                channel,
+                term,
+                Winsize {
+                    ws_row: row_height as _,
+                    ws_col: col_width as _,
+                    ws_xpixel: pix_width as _,
+                    ws_ypixel: pix_height as _,
+                },
+            )
+            .await?;
+
+            Ok((self, session))
+        })
+    }
+
+    fn window_change_request(
+        mut self,
+        _channel: ChannelId,
+        col_width: u32,
+        row_height: u32,
+        pix_width: u32,
+        pix_height: u32,
+        session: Session,
+    ) -> Self::FutureUnit {
+        Box::pin(async move {
+            if let Some(child) = self.child.as_mut() {
+                trace!(%row_height, %col_width, "Window resize request received");
+                child
+                    .resize_window(Winsize {
+                        ws_row: row_height as _,
+                        ws_col: col_width as _,
+                        ws_xpixel: pix_width as _,
+                        ws_ypixel: pix_height as _,
+                    })
+                    .await?;
+            } else {
+                warn!("Resize request received without child process; ignoring");
+            }
+
+            Ok((self, session))
+        })
+    }
+
+    fn data(
+        mut self,
+        _channel: thrussh::ChannelId,
+        data: &[u8],
+        session: Session,
+    ) -> Self::FutureUnit {
+        trace!(data = %String::from_utf8_lossy(data), raw_data = ?data);
+        let data = data.to_owned();
+        Box::pin(async move {
+            if let Some(child) = self.child.as_mut() {
+                child.write_all(&data).await?;
+            } else {
+                warn!("Data received without child process; ignoring");
+            }
+
+            Ok((self, session))
+        })
+    }
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+    color_eyre::install()?;
+    let opts = Box::leak::<'static>(Box::new(Opts::parse()));
+    opts.init_logging()?;
+    PrometheusBuilder::new()
+        .listen_address(opts.metrics_address)
+        .install()?;
+    metrics::register();
+
+    let config = Arc::new(opts.ssh_server_config().await?);
+    info!(address = %opts.address, "Listening for new SSH connections");
+    let listener = TcpListener::bind(&opts.address).await?;
+
+    loop {
+        let (stream, address) = listener.accept().await?;
+        increment_counter!(CONNECTIONS_ACCEPTED);
+        increment_gauge!(ACTIVE_CONNECTIONS, 1.0);
+        let config = config.clone();
+        let handler = Handler {
+            xanthous_binary_path: &opts.xanthous_binary_path,
+            address,
+            username: None,
+            child: None,
+        };
+        tokio::spawn(async move {
+            let span = info_span!("client", address = %handler.address);
+            let start = Instant::now();
+            if let Err(error) = server::run_stream(config, stream, handler)
+                .instrument(span.clone())
+                .await
+            {
+                span.in_scope(|| error!(%error));
+            }
+            let duration = start.elapsed();
+            span.in_scope(|| info!(duration_ms = %duration.as_millis(), "Client disconnected"));
+            histogram!(CONNECTION_DURATION, duration);
+            decrement_gauge!(ACTIVE_CONNECTIONS, 1.0);
+        });
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use tempfile::NamedTempFile;
+
+    use super::*;
+
+    #[tokio::test]
+    async fn read_secret_key() {
+        use std::io::Write;
+
+        let mut file = NamedTempFile::new().unwrap();
+        file.write_all(
+            b"
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAYz80xcK7jYxZMAl6apIHKRtB0Z2U78gG39c1QaIhgMwAAAJB9vxK9fb8S
+vQAAAAtzc2gtZWQyNTUxOQAAACAYz80xcK7jYxZMAl6apIHKRtB0Z2U78gG39c1QaIhgMw
+AAAEDNZ0d3lLNBGU6Im4JOpr490TOjm+cB7kMVXjVg3iCowBjPzTFwruNjFkwCXpqkgcpG
+0HRnZTvyAbf1zVBoiGAzAAAACHRlc3Qta2V5AQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
+",
+        )
+        .unwrap();
+
+        let opts: Opts = Opts::parse_from(&[
+            "xanthous-server".as_ref(),
+            "--xanthous-binary-path".as_ref(),
+            "/bin/xanthous".as_ref(),
+            "--secret-key-file".as_ref(),
+            file.path().as_os_str(),
+        ]);
+        opts.read_secret_key().await.unwrap();
+    }
+}
diff --git a/users/grfn/xanthous/server/src/metrics.rs b/users/aspen/xanthous/server/src/metrics.rs
index 6912cdd9c9..6912cdd9c9 100644
--- a/users/grfn/xanthous/server/src/metrics.rs
+++ b/users/aspen/xanthous/server/src/metrics.rs
diff --git a/users/grfn/xanthous/server/src/pty.rs b/users/aspen/xanthous/server/src/pty.rs
index 611130f5bc..234ecd8f23 100644
--- a/users/grfn/xanthous/server/src/pty.rs
+++ b/users/aspen/xanthous/server/src/pty.rs
@@ -6,8 +6,7 @@ use std::task::{Context, Poll};
 
 use eyre::{bail, Result};
 use futures::Future;
-use nix::pty::forkpty;
-use nix::pty::Winsize;
+use nix::pty::{forkpty, Winsize};
 use nix::sys::termios::Termios;
 use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
 use nix::unistd::{ForkResult, Pid};
diff --git a/users/aspen/xanthous/shell.nix b/users/aspen/xanthous/shell.nix
new file mode 100644
index 0000000000..2c41cb4aa8
--- /dev/null
+++ b/users/aspen/xanthous/shell.nix
@@ -0,0 +1,23 @@
+let
+  depot = import ../../../. { };
+  inherit (depot) third_party;
+  pkgs = third_party.nixpkgs;
+in
+
+(pkgs.haskell.packages.ghc8107.extend (pkgs.haskell.lib.packageSourceOverrides {
+  xanthous = third_party.gitignoreSource ./.;
+})).shellFor {
+  packages = p: [ p.xanthous ];
+  withHoogle = true;
+  doBenchmark = true;
+  buildInputs = (with pkgs.haskell.packages.ghc8107; [
+    cabal-install
+    ghc-prof-flamegraph
+    hp2pretty
+    hlint
+    haskell-language-server
+    cabal2nix
+  ]) ++ (with pkgs; [
+    qpdf
+  ]);
+}
diff --git a/users/grfn/xanthous/src/Data/Aeson/Generic/DerivingVia.hs b/users/aspen/xanthous/src/Data/Aeson/Generic/DerivingVia.hs
index e89fcd6211..e89fcd6211 100644
--- a/users/grfn/xanthous/src/Data/Aeson/Generic/DerivingVia.hs
+++ b/users/aspen/xanthous/src/Data/Aeson/Generic/DerivingVia.hs
diff --git a/users/grfn/xanthous/src/Xanthous/AI/Gormlak.hs b/users/aspen/xanthous/src/Xanthous/AI/Gormlak.hs
index 1f2b513ffe..1f2b513ffe 100644
--- a/users/grfn/xanthous/src/Xanthous/AI/Gormlak.hs
+++ b/users/aspen/xanthous/src/Xanthous/AI/Gormlak.hs
diff --git a/users/aspen/xanthous/src/Xanthous/App.hs b/users/aspen/xanthous/src/Xanthous/App.hs
new file mode 100644
index 0000000000..426230cdc2
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/App.hs
@@ -0,0 +1,647 @@
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE RecordWildCards      #-}
+--------------------------------------------------------------------------------
+{-# OPTIONS_GHC -Wno-deferred-type-errors #-}
+module Xanthous.App
+  ( makeApp
+  , RunType(..)
+  ) where
+--------------------------------------------------------------------------------
+import           Xanthous.Prelude
+import           Brick hiding (App, halt, continue, raw)
+import qualified Brick
+import           Graphics.Vty.Attributes (defAttr)
+import           Graphics.Vty.Input.Events (Event(EvKey))
+import           Control.Monad.State (get, gets)
+import           Control.Monad.State.Class (modify)
+import           Data.Aeson (object, ToJSON)
+import qualified Data.Aeson as A
+import qualified Data.Vector as V
+import           System.Exit
+import           System.Directory (doesFileExist)
+import           Data.List.NonEmpty (NonEmpty(..))
+import           Data.Vector.Lens (toVectorOf)
+--------------------------------------------------------------------------------
+import           Xanthous.App.Common
+import           Xanthous.App.Time
+import           Xanthous.App.Prompt
+import           Xanthous.App.Autocommands
+import           Xanthous.Command
+import           Xanthous.Data
+                 ( move
+                 , Dimensions'(Dimensions)
+                 , positioned
+                 , position
+                 , Position
+                 , (|*|)
+                 , Tiles(..), Hitpoints, fromScalar
+                 )
+import           Xanthous.Data.App (ResourceName, Panel(..), AppEvent(..))
+import qualified Xanthous.Data.EntityMap as EntityMap
+import           Xanthous.Data.Levels (prevLevel, nextLevel)
+import qualified Xanthous.Data.Levels as Levels
+import           Xanthous.Data.Entities (blocksObject)
+import           Xanthous.Game
+import           Xanthous.Game.State
+import           Xanthous.Game.Env
+import           Xanthous.Game.Draw (drawGame)
+import           Xanthous.Game.Prompt hiding (Fire)
+import qualified Xanthous.Messages as Messages
+import           Xanthous.Random
+import           Xanthous.Util (removeVectorIndex, useListOf)
+import           Xanthous.Util.Inflection (toSentence)
+import           Xanthous.Physics (throwDistance, bluntThrowDamage)
+import           Xanthous.Data.EntityMap.Graphics (lineOfSight)
+import           Xanthous.Data.EntityMap (EntityID)
+--------------------------------------------------------------------------------
+import           Xanthous.Entities.Common
+                 ( InventoryPosition, describeInventoryPosition, backpack
+                 , wieldableItem, wieldedItems, wielded, itemsWithPosition
+                 , removeItemFromPosition, asWieldedItem
+                 , wieldedItem, items, Hand (..), describeHand, wieldInHand
+                 , WieldedItem, Wielded (..)
+                 )
+import qualified Xanthous.Entities.Character as Character
+import           Xanthous.Entities.Character hiding (pickUpItem)
+import           Xanthous.Entities.Item (Item, weight)
+import qualified Xanthous.Entities.Item as Item
+import           Xanthous.Entities.Creature (Creature)
+import qualified Xanthous.Entities.Creature as Creature
+import           Xanthous.Entities.Environment
+                 (Door, open, closed, locked, GroundMessage(..), Staircase(..))
+import           Xanthous.Entities.RawTypes
+                 ( edible, eatMessage, hitpointsHealed
+                 , attackMessage
+                 )
+import           Xanthous.Generators.Level
+import qualified Xanthous.Generators.Level.CaveAutomata as CaveAutomata
+import qualified Xanthous.Generators.Level.Dungeon as Dungeon
+--------------------------------------------------------------------------------
+
+type App = Brick.App GameState AppEvent ResourceName
+
+data RunType = NewGame | LoadGame FilePath
+  deriving stock (Eq)
+
+makeApp :: GameEnv -> RunType -> IO App
+makeApp env rt = pure $ Brick.App
+  { appDraw = drawGame
+  , appChooseCursor = const headMay
+  , appHandleEvent = \game event -> runAppM (handleEvent event) env game
+  , appStartEvent = case rt of
+      NewGame -> runAppM (startEvent >> get) env
+      LoadGame save -> pure . (savefile ?~ save)
+  , appAttrMap = const $ attrMap defAttr []
+  }
+
+runAppM :: AppM a -> GameEnv -> GameState -> EventM ResourceName a
+runAppM appm ge = fmap fst . runAppT appm ge
+
+startEvent :: AppM ()
+startEvent = do
+  initLevel
+  modify updateCharacterVision
+  use (character . characterName) >>= \case
+    Nothing -> prompt_ @'StringPrompt ["character", "namePrompt"] Uncancellable
+      $ \(StringResult s) -> do
+        character . characterName ?= s
+        say ["welcome"] =<< use character
+    Just n -> say ["welcome"] $ object [ "characterName" A..= n ]
+
+initLevel :: AppM ()
+initLevel = do
+  level <- genLevel 0
+  entities <>= levelToEntityMap level
+  characterPosition .= level ^. levelCharacterPosition
+
+--------------------------------------------------------------------------------
+
+handleEvent :: BrickEvent ResourceName AppEvent -> AppM (Next GameState)
+handleEvent ev = use promptState >>= \case
+  NoPrompt -> handleNoPromptEvent ev
+  WaitingPrompt msg pr -> handlePromptEvent msg pr ev
+
+
+handleNoPromptEvent :: BrickEvent ResourceName AppEvent -> AppM (Next GameState)
+handleNoPromptEvent (VtyEvent (EvKey k mods))
+  | Just command <- commandFromKey k mods
+  = do messageHistory %= nextTurn
+       cancelAutocommand
+       handleCommand command
+handleNoPromptEvent (AppEvent AutoContinue) = do
+  preuse (autocommand . _ActiveAutocommand . _1) >>= traverse_ autoStep
+  continue
+handleNoPromptEvent _ = continue
+
+handleCommand :: Command -> AppM (Next GameState)
+handleCommand Quit = confirm_ ["quit", "confirm"] (liftIO exitSuccess) >> continue
+
+handleCommand Help = showPanel HelpPanel >> continue
+
+handleCommand (Move dir) = do
+  newPos <- uses characterPosition $ move dir
+  collisionAt newPos >>= \case
+    Nothing -> do
+      characterPosition .= newPos
+      stepGameBy =<< uses (character . speed) (|*| Tiles 1)
+      describeEntitiesAt newPos
+    Just Combat -> attackAt newPos
+    Just Stop -> pure ()
+  continue
+
+handleCommand PickUp = do
+  pos <- use characterPosition
+  uses entities (entitiesAtPositionWithType @Item pos) >>= \case
+    [] -> say_ ["pickUp", "nothingToPickUp"]
+    [item] -> pickUpItem item
+    items' ->
+      menu_ ["pickUp", "menu"] Cancellable (entityMenu_ items')
+      $ \(MenuResult item) -> pickUpItem item
+  continue
+  where
+    pickUpItem (itemID, item) = do
+      character %= Character.pickUpItem item
+      entities . at itemID .= Nothing
+      say ["pickUp", "pickUp"] $ object [ "item" A..= item ]
+      stepGameBy 100 -- TODO
+
+handleCommand Drop = do
+  takeItemFromInventory_ ["drop", "menu"] Cancellable id
+    (say_ ["drop", "nothing"])
+    $ \(MenuResult item) -> do
+      entitiesAtCharacter %= (SomeEntity item <|)
+      say ["drop", "dropped"] $ object [ "item" A..= item ]
+  continue
+
+handleCommand PreviousMessage = do
+  messageHistory %= previousMessage
+  continue
+
+handleCommand Open = do
+  prompt_ @'DirectionPrompt ["open", "prompt"] Cancellable
+    $ \(DirectionResult dir) -> do
+      pos <- move dir <$> use characterPosition
+      doors <- uses entities $ entitiesAtPositionWithType @Door pos
+      if | null doors -> say_ ["open", "nothingToOpen"]
+         | any (view $ _2 . locked) doors -> say_ ["open", "locked"]
+         | all (view $ _2 . open) doors   -> say_ ["open", "alreadyOpen"]
+         | otherwise -> do
+             for_ doors $ \(eid, _) ->
+               entities . ix eid . positioned . _SomeEntity . open .= True
+             say_ ["open", "success"]
+      pure ()
+  stepGame -- TODO
+  continue
+
+handleCommand Close = do
+  prompt_ @'DirectionPrompt ["close", "prompt"] Cancellable
+    $ \(DirectionResult dir) -> do
+      pos <- move dir <$> use characterPosition
+      (nonDoors, doors) <- uses entities
+        $ partitionEithers
+        . toList
+        . map ( (matching . aside $ _SomeEntity @Door)
+              . over _2 (view positioned)
+              )
+        . EntityMap.atPositionWithIDs pos
+      if | null doors -> say_ ["close", "nothingToClose"]
+         | all (view $ _2 . closed) doors -> say_ ["close", "alreadyClosed"]
+         | any (view blocksObject . entityAttributes . snd) nonDoors ->
+           say ["close", "blocked"]
+           $ object [ "entityDescriptions"
+                      A..= ( toSentence
+                           . map description
+                           . filter (view blocksObject . entityAttributes)
+                           . map snd
+                           ) nonDoors
+                    , "blockOrBlocks"
+                      A..= ( if length nonDoors == 1
+                             then "blocks"
+                             else "block"
+                           :: Text)
+                    ]
+         | otherwise -> do
+             for_ doors $ \(eid, _) ->
+               entities . ix eid . positioned . _SomeEntity . closed .= True
+             for_ nonDoors $ \(eid, _) ->
+               entities . ix eid . position %= move dir
+             say_ ["close", "success"]
+      pure ()
+  stepGame -- TODO
+  continue
+
+handleCommand Look = do
+  prompt_ @'PointOnMap ["look", "prompt"] Cancellable
+    $ \(PointOnMapResult pos) -> revealedEntitiesAtPosition pos >>= \case
+        Empty -> say_ ["look", "nothing"]
+        ents -> describeEntities ents
+  continue
+
+handleCommand Wait = stepGame >> continue
+
+handleCommand Eat = do
+  uses (character . inventory . backpack)
+       (V.mapMaybe (\item -> (item,) <$> item ^. Item.itemType . edible))
+    >>= \case
+      Empty -> say_ ["eat", "noFood"]
+      food ->
+        let foodMenuItem idx (item, edibleItem)
+              = ( item ^. Item.itemType . char . char
+                , MenuOption (description item) (idx, item, edibleItem))
+                -- TODO refactor to use entityMenu_
+            menuItems = mkMenuItems $ imap foodMenuItem food
+        in menu_ ["eat", "menuPrompt"] Cancellable menuItems
+          $ \(MenuResult (idx, item, edibleItem)) -> do
+            character . inventory . backpack %= removeVectorIndex idx
+            let msg = fromMaybe (Messages.lookup ["eat", "eat"])
+                      $ edibleItem ^. eatMessage
+            character . characterHitpoints' +=
+              edibleItem ^. hitpointsHealed . to fromIntegral
+            message msg $ object ["item" A..= item]
+            stepGame -- TODO
+  continue
+
+handleCommand Read = do
+  -- TODO allow reading things in the inventory (combo direction+menu prompt?)
+  prompt_ @'DirectionPrompt ["read", "prompt"] Cancellable
+    $ \(DirectionResult dir) -> do
+      pos <- uses characterPosition $ move dir
+      uses entities
+        (fmap snd . entitiesAtPositionWithType @GroundMessage pos) >>= \case
+          Empty -> say_ ["read", "nothing"]
+          GroundMessage msg :< Empty ->
+            say ["read", "result"] $ object ["message" A..= msg]
+          msgs ->
+            let readAndContinue Empty = pure ()
+                readAndContinue (msg :< msgs') =
+                  prompt @'Continue
+                    ["read", "result"]
+                    (object ["message" A..= msg])
+                    Cancellable
+                  . const
+                  $ readAndContinue msgs'
+                readAndContinue _ = error "this is total"
+            in readAndContinue msgs
+  continue
+
+handleCommand ShowInventory = showPanel InventoryPanel >> continue
+
+handleCommand DescribeInventory = do
+  selectItemFromInventory_ ["inventory", "describe", "select"] Cancellable id
+    (say_ ["inventory", "describe", "nothing"])
+    $ \(MenuResult (invPos, item)) -> showPanel . ItemDescriptionPanel
+        $ Item.fullDescription item
+        <> "\n\n" <> describeInventoryPosition invPos
+  continue
+
+
+handleCommand Wield = do
+  hs <- use $ character . inventory . wielded
+  selectItem $ \(MenuResult (invPos, (item :: WieldedItem))) -> do
+    selectHand hs $ \(MenuResult hand) -> do
+      character . inventory
+        %= removeItemFromPosition invPos (asWieldedItem # item)
+      prevItems <- character . inventory . wielded %%= wieldInHand hand item
+      character . inventory . backpack
+        <>= fromList (map (view wieldedItem) prevItems)
+      say ["wield", "wielded"] $ object [ "item" A..= item
+                                        , "hand" A..= describeHand hand
+                                        ]
+  continue
+  where
+    selectItem =
+      selectItemFromInventory_ ["wield", "menu"] Cancellable asWieldedItem
+        (say_ ["wield", "nothing"])
+    selectHand hs = menu_ ["wield", "hand"] Cancellable $ handsMenu hs
+    itemsInHand (Hands i _) LeftHand       = toList i
+    itemsInHand (DoubleHanded _) LeftHand  = []
+    itemsInHand (Hands _ i) RightHand      = toList i
+    itemsInHand (DoubleHanded _) RightHand = []
+    itemsInHand (Hands l r) BothHands      = toList l <> toList r
+    itemsInHand (DoubleHanded i) BothHands = [i]
+    describeItems [] = ""
+    describeItems is
+      = " (currently holding "
+      <> (intercalate " and" $ map (view $ wieldedItem . to description) is)
+      <> ")"
+    handsMenu hs = mapFromList
+      . map (second $ \hand ->
+                MenuOption
+                ( describeHand hand
+                <> describeItems (itemsInHand hs hand)
+                )
+                hand
+            )
+      $ [ ('l', LeftHand)
+        , ('r', RightHand)
+        , ('b', BothHands)
+        ]
+
+handleCommand Fire = do
+  selectItemFromInventory_ ["fire", "menu"] Cancellable id
+    (say_ ["fire", "nothing"])
+    $ \(MenuResult (invPos, item)) ->
+      let wt = weight item
+          dist = throwDistance wt
+          dam = bluntThrowDamage wt
+      in if dist < fromScalar 1
+         then say_ ["fire", "zeroRange"]
+         else firePrompt_ ["fire", "target"] Cancellable dist $
+          \(FireResult targetPos) -> do
+              charPos <- use characterPosition
+              mTarget <- uses entities $ firstEnemy . lineOfSight charPos targetPos
+              case mTarget of
+                Just target -> do
+                  creature' <- damageCreature target dam
+                  unless (Creature.isDead creature') $
+                    let msgPath = ["fire", "fired"] <> [if dam == 0
+                                                        then "noDamage"
+                                                        else "someDamage"]
+                    in say msgPath $ object [ "item" A..= item
+                                            , "creature" A..= creature'
+                                            ]
+                Nothing ->
+                  say ["fire", "fired", "noTarget"] $ object [ "item" A..= item ]
+              character . inventory %= removeItemFromPosition invPos item
+              entities . EntityMap.atPosition targetPos %= (SomeEntity item <|)
+              stepGame -- TODO(grfn): should this be based on distance?
+  continue
+  where
+    firstEnemy
+      :: [(Position, Vector (EntityID, SomeEntity))]
+      -> Maybe (EntityID, Creature)
+    firstEnemy los =
+      let enemies = los >>= \(_, es) -> toList $ headMay es
+      in enemies ^? folded . below _SomeEntity
+
+handleCommand Save =
+  view (config . disableSaving) >>= \case
+    True -> say_ ["save", "disabled"] >> continue
+    False -> do
+      -- TODO default save locations / config file?
+      use savefile >>= \case
+        Just filepath ->
+          stringPromptWithDefault_
+            ["save", "location"]
+            Cancellable
+            (pack filepath)
+            promptCallback
+        Nothing -> prompt_ @'StringPrompt ["save", "location"] Cancellable promptCallback
+      continue
+      where
+        promptCallback :: PromptResult 'StringPrompt -> AppM ()
+        promptCallback (StringResult filename) = do
+          sf <- use savefile
+          exists <- liftIO . doesFileExist $ unpack filename
+          if exists && sf /= Just (unpack filename)
+          then confirm ["save", "overwrite"] (object ["filename" A..= filename])
+              $ doSave filename
+          else doSave filename
+        doSave filename = do
+          src <- gets saveGame
+          lift . liftIO $ do
+            writeFile (unpack filename) $ toStrict src
+            exitSuccess
+
+handleCommand GoUp = do
+  hasStairs <- uses entitiesAtCharacter $ elem (SomeEntity UpStaircase)
+  if hasStairs
+  then uses levels prevLevel >>= \case
+    Just levs' -> do
+      cEID <- use characterEntityID
+      pCharacter <- entities . at cEID <<.= Nothing
+      levels .= levs'
+      charPos <- use characterPosition
+      entities . at cEID .= pCharacter
+      characterPosition .= charPos
+    Nothing ->
+      -- TODO in nethack, this leaves the game. Maybe something similar here?
+      say_ ["cant", "goUp"]
+  else say_ ["cant", "goUp"]
+
+  continue
+
+handleCommand GoDown = do
+  hasStairs <- uses entitiesAtCharacter $ elem (SomeEntity DownStaircase)
+
+  if hasStairs
+  then do
+    levs <- use levels
+    let newLevelNum = Levels.pos levs + 1
+    levs' <- nextLevel (levelToGameLevel <$> genLevel newLevelNum) levs
+    cEID <- use characterEntityID
+    pCharacter <- entities . at cEID <<.= Nothing
+    levels .= levs'
+    entities . at cEID .= pCharacter
+    characterPosition .= extract levs' ^. upStaircasePosition
+  else say_ ["cant", "goDown"]
+
+  continue
+
+handleCommand (StartAutoMove dir) = do
+  runAutocommand $ AutoMove dir
+  continue
+
+handleCommand Rest = do
+  say_ ["autocommands", "resting"]
+  runAutocommand AutoRest
+  continue
+
+--
+
+handleCommand ToggleRevealAll = do
+  val <- debugState . allRevealed <%= not
+  say ["debug", "toggleRevealAll"] $ object [ "revealAll" A..= val ]
+  continue
+
+--------------------------------------------------------------------------------
+attackAt :: Position -> AppM ()
+attackAt pos =
+  uses entities (entitiesAtPositionWithType @Creature pos) >>= \case
+    Empty               -> say_ ["combat", "nothingToAttack"]
+    (creature :< Empty) -> attackCreature creature
+    creatures ->
+      menu_ ["combat", "menu"] Cancellable (entityMenu_ creatures)
+      $ \(MenuResult creature) -> attackCreature creature
+ where
+  attackCreature creature = do
+    charDamage <- uses character characterDamage
+    creature' <- damageCreature creature charDamage
+    unless (Creature.isDead creature') $ writeAttackMessage creature'
+    whenM (uses character $ isNothing . weapon) handleFists
+    stepGame
+  weapon chr = chr ^? inventory . wielded . wieldedItems . wieldableItem
+  writeAttackMessage creature = do
+    let params = object ["creature" A..= creature]
+    attackMessages <- uses character getAttackMessages
+    msg <- intercalate " and " <$> for attackMessages (`Messages.render` params)
+    writeMessage $ "You " <> msg
+  getAttackMessages chr =
+    case chr ^.. inventory . wielded . wieldedItems . wieldableItem of
+      [] -> [Messages.lookup ["combat", "hit", "fists"]]
+      is ->
+        is
+        <&> \wi ->
+              fromMaybe (Messages.lookup ["combat", "hit", "generic"])
+              $ wi ^. attackMessage
+
+
+  handleFists = do
+    damageChance <- use $ character . body . knuckles . to fistDamageChance
+    whenM (chance damageChance) $ do
+      damageAmount <- use $ character . body . knuckles . to fistfightingDamage
+      say_ [ "combat" , if damageAmount > 1
+                        then "fistExtraSelfDamage"
+                        else "fistSelfDamage" ]
+      character %= Character.damage damageAmount
+      character . body . knuckles %= damageKnuckles
+
+damageCreature :: (EntityID, Creature) -> Hitpoints -> AppM Creature
+damageCreature (creatureID, creature) dam = do
+  let creature' = Creature.damage dam creature
+      msgParams = object ["creature" A..= creature']
+  if Creature.isDead creature'
+    then do
+      say ["combat", "killed"] msgParams
+      floorItems <- useListOf
+                   $ entities
+                   . ix creatureID
+                   . positioned
+                   . _SomeEntity @Creature
+                   . inventory
+                   . items
+      mCreaturePos <- preuse $ entities . ix creatureID . position
+      entities . at creatureID .= Nothing
+      for_ mCreaturePos $ \creaturePos ->
+        entities . EntityMap.atPosition creaturePos
+          %= (<> fromList (SomeEntity <$> floorItems))
+    else entities . ix creatureID . positioned .= SomeEntity creature'
+  pure creature'
+
+
+entityMenu_
+  :: (Comonad w, Entity entity)
+  => [w entity]
+  -> Map Char (MenuOption (w entity))
+entityMenu_ = mkMenuItems @[_] . map entityMenuItem
+  where
+    entityMenuItem wentity
+      = let entity = extract wentity
+      in (entityMenuChar entity, MenuOption (description entity) wentity)
+
+
+entityMenuChar :: Entity a => a -> Char
+entityMenuChar entity
+  = let ec = entityChar entity ^. char
+    in if ec `elem` (['a'..'z'] ++ ['A'..'Z'])
+        then ec
+        else 'a'
+
+-- | Prompt with an item to select out of the inventory and call callback with
+-- it
+selectItemFromInventory
+  :: forall item params.
+    (ToJSON params)
+  => [Text]            -- ^ Menu message
+  -> params            -- ^ Menu message params
+  -> PromptCancellable -- ^ Is the menu cancellable?
+  -> Prism' Item item  -- ^ Attach some extra information to the item, in a
+                      --   recoverable fashion. Prism vs iso so we can discard
+                      --   items.
+  -> AppM ()            -- ^ Action to take if there are no items matching
+  -> (PromptResult ('Menu (InventoryPosition, item)) -> AppM ())
+  -> AppM ()
+selectItemFromInventory msgPath msgParams cancellable extraInfo onEmpty cb = do
+  uses (character . inventory)
+       (V.mapMaybe (_2 $ preview extraInfo) . toVectorOf itemsWithPosition)
+    >>= \case
+      Empty -> onEmpty
+      items' -> menu msgPath msgParams cancellable (itemMenu items') cb
+  where
+    itemMenu = mkMenuItems . map itemMenuItem
+    itemMenuItem (invPos, extraInfoItem) =
+      let item = extraInfo # extraInfoItem
+      in ( entityMenuChar item
+         , MenuOption
+           (description item <> " (" <> describeInventoryPosition invPos <> ")")
+           (invPos, extraInfoItem)
+         )
+
+-- | Prompt with an item to select out of the inventory and call callback with
+-- it
+selectItemFromInventory_
+  :: forall item.
+    [Text]            -- ^ Menu message
+  -> PromptCancellable -- ^ Is the menu cancellable?
+  -> Prism' Item item  -- ^ Attach some extra information to the item, in a
+                      --   recoverable fashion. Prism vs iso so we can discard
+                      --   items.
+  -> AppM ()            -- ^ Action to take if there are no items matching
+  -> (PromptResult ('Menu (InventoryPosition, item)) -> AppM ())
+  -> AppM ()
+selectItemFromInventory_ msgPath = selectItemFromInventory msgPath ()
+
+-- | Prompt with an item to select out of the inventory, remove it from the
+-- inventory, and call callback with it
+takeItemFromInventory
+  :: forall item params.
+    (ToJSON params)
+  => [Text]            -- ^ Menu message
+  -> params            -- ^ Menu message params
+  -> PromptCancellable -- ^ Is the menu cancellable?
+  -> Prism' Item item  -- ^ Attach some extra information to the item, in a
+                      --   recoverable fashion. Prism vs iso so we can discard
+                      --   items.
+  -> AppM ()            -- ^ Action to take if there are no items matching
+  -> (PromptResult ('Menu item) -> AppM ())
+  -> AppM ()
+takeItemFromInventory msgPath msgParams cancellable extraInfo onEmpty cb =
+  selectItemFromInventory msgPath msgParams cancellable extraInfo onEmpty
+    $ \(MenuResult (invPos, item)) -> do
+      character . inventory
+        %= removeItemFromPosition invPos (item ^. re extraInfo)
+      cb $ MenuResult item
+
+takeItemFromInventory_
+  :: forall item.
+    [Text]            -- ^ Menu message
+  -> PromptCancellable -- ^ Is the menu cancellable?
+  -> Prism' Item item  -- ^ Attach some extra information to the item, in a
+                      --   recoverable fashion. Prism vs iso so we can discard
+                      --   items.
+  -> AppM ()            -- ^ Action to take if there are no items matching
+  -> (PromptResult ('Menu item) -> AppM ())
+  -> AppM ()
+takeItemFromInventory_ msgPath = takeItemFromInventory msgPath ()
+
+-- entityMenu :: Entity entity => [entity] -> Map Char (MenuOption entity)
+-- entityMenu = map (map runIdentity) . entityMenu_ . fmap Identity
+
+showPanel :: Panel -> AppM ()
+showPanel panel = do
+  activePanel ?= panel
+  prompt_ @'Continue ["generic", "continue"] Uncancellable
+    . const
+    $ activePanel .= Nothing
+
+--------------------------------------------------------------------------------
+
+genLevel
+  :: Word -- ^ Level number, starting at 0
+  -> AppM Level
+genLevel num = do
+  let dims = Dimensions 80 80
+  generator <- choose $ CaveAutomata :| [Dungeon]
+  let
+    doGen = case generator of
+      CaveAutomata -> generateLevel SCaveAutomata CaveAutomata.defaultParams
+      Dungeon -> generateLevel SDungeon Dungeon.defaultParams
+  level <- doGen dims num
+  pure $!! level
+
+levelToGameLevel :: Level -> GameLevel
+levelToGameLevel level =
+  let _levelEntities = levelToEntityMap level
+      _upStaircasePosition = level ^. levelCharacterPosition
+      _levelRevealedPositions = mempty
+  in GameLevel {..}
diff --git a/users/grfn/xanthous/src/Xanthous/App/Autocommands.hs b/users/aspen/xanthous/src/Xanthous/App/Autocommands.hs
index 5d4db1a474..5d4db1a474 100644
--- a/users/grfn/xanthous/src/Xanthous/App/Autocommands.hs
+++ b/users/aspen/xanthous/src/Xanthous/App/Autocommands.hs
diff --git a/users/grfn/xanthous/src/Xanthous/App/Common.hs b/users/aspen/xanthous/src/Xanthous/App/Common.hs
index 69ba6f0e05..69ba6f0e05 100644
--- a/users/grfn/xanthous/src/Xanthous/App/Common.hs
+++ b/users/aspen/xanthous/src/Xanthous/App/Common.hs
diff --git a/users/grfn/xanthous/src/Xanthous/App/Prompt.hs b/users/aspen/xanthous/src/Xanthous/App/Prompt.hs
index 799281a1c2..799281a1c2 100644
--- a/users/grfn/xanthous/src/Xanthous/App/Prompt.hs
+++ b/users/aspen/xanthous/src/Xanthous/App/Prompt.hs
diff --git a/users/grfn/xanthous/src/Xanthous/App/Time.hs b/users/aspen/xanthous/src/Xanthous/App/Time.hs
index cca352858d..cca352858d 100644
--- a/users/grfn/xanthous/src/Xanthous/App/Time.hs
+++ b/users/aspen/xanthous/src/Xanthous/App/Time.hs
diff --git a/users/aspen/xanthous/src/Xanthous/Command.hs b/users/aspen/xanthous/src/Xanthous/Command.hs
new file mode 100644
index 0000000000..6e6274a02c
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Command.hs
@@ -0,0 +1,145 @@
+{-# LANGUAGE TemplateHaskell #-}
+--------------------------------------------------------------------------------
+module Xanthous.Command
+  ( -- * Commands
+    Command(..)
+  , commandIsHidden
+    -- * Keybindings
+  , Keybinding(..)
+  , keybindings
+  , commands
+  , commandFromKey
+  , directionFromChar
+  ) where
+--------------------------------------------------------------------------------
+import Xanthous.Prelude hiding (Left, Right, Down, try)
+--------------------------------------------------------------------------------
+import           Graphics.Vty.Input (Key(..), Modifier(..))
+import qualified Data.Char as Char
+import           Data.Aeson (FromJSON (parseJSON), FromJSONKey, FromJSONKeyFunction (FromJSONKeyTextParser))
+import qualified Data.Aeson as A
+import           Data.Aeson.Generic.DerivingVia
+import           Text.Megaparsec (Parsec, errorBundlePretty, parse, eof, try)
+import           Text.Megaparsec.Char (string', char', printChar)
+import           Data.FileEmbed (embedFile)
+import qualified Data.Yaml as Yaml
+import           Test.QuickCheck.Arbitrary
+import           Data.Aeson.Types (Parser)
+--------------------------------------------------------------------------------
+import           Xanthous.Data (Direction(..))
+import           Xanthous.Util.QuickCheck (GenericArbitrary(..))
+--------------------------------------------------------------------------------
+
+data Command
+  = Quit
+  | Help
+  | Move !Direction
+  | StartAutoMove !Direction
+  | PreviousMessage
+  | PickUp
+  | Drop
+  | Open
+  | Close
+  | Wait
+  | Eat
+  | Look
+  | Save
+  | Read
+  | ShowInventory
+  | DescribeInventory
+  | Wield
+  | Fire
+  | GoUp
+  | GoDown
+  | Rest
+
+    -- | TODO replace with `:` commands
+  | ToggleRevealAll
+  deriving stock (Show, Eq, Generic)
+  deriving anyclass (Hashable, NFData)
+  deriving Arbitrary via GenericArbitrary Command
+  deriving (FromJSON)
+       via WithOptions '[ SumEnc UntaggedVal ]
+           Command
+
+-- | Should the command be hidden from the help menu?
+--
+-- Note that this is true for both debug commands and movement commands, as the
+-- latter is documented non-automatically
+commandIsHidden :: Command -> Bool
+commandIsHidden (Move _) = True
+commandIsHidden (StartAutoMove _) = True
+commandIsHidden ToggleRevealAll = True
+commandIsHidden _ = False
+
+--------------------------------------------------------------------------------
+
+data Keybinding = Keybinding !Key ![Modifier]
+  deriving stock (Show, Eq, Generic)
+  deriving anyclass (Hashable, NFData)
+
+parseKeybindingFromText :: Text -> Parser Keybinding
+parseKeybindingFromText
+  = either (fail . errorBundlePretty) pure
+  . parse keybinding "<JSON>"
+  where
+    key :: Parsec Void Text Key
+    key = KUp <$ string' "<up>"
+      <|> KDown <$ string' "<down>"
+      <|> KLeft <$ string' "<left>"
+      <|> KRight <$ string' "<right>"
+      <|> KChar <$> printChar
+
+    modifier :: Parsec Void Text Modifier
+    modifier = modf <* char' '-'
+      where
+        modf = MAlt <$ char' 'a'
+          <|> MMeta <$ char' 'm'
+          <|> MCtrl  <$ char' 'c'
+          <|> MShift  <$ char' 's'
+
+    keybinding :: Parsec Void Text Keybinding
+    keybinding = do
+      mods <- many (try modifier)
+      k <- key
+      eof
+      pure $ Keybinding k mods
+
+instance FromJSON Keybinding where
+  parseJSON = A.withText "Keybinding" parseKeybindingFromText
+
+instance FromJSONKey Keybinding where
+  fromJSONKey = FromJSONKeyTextParser parseKeybindingFromText
+
+rawKeybindings :: ByteString
+rawKeybindings = $(embedFile "src/Xanthous/keybindings.yaml")
+
+keybindings :: HashMap Keybinding Command
+keybindings = either (error . Yaml.prettyPrintParseException) id
+  $ Yaml.decodeEither' rawKeybindings
+
+commands :: HashMap Command Keybinding
+commands = mapFromList . map swap . itoList $ keybindings
+
+commandFromKey :: Key -> [Modifier] -> Maybe Command
+commandFromKey (KChar (directionFromChar -> Just dir)) [] = Just $ Move dir
+commandFromKey (KChar c) []
+  | Char.isUpper c
+  , Just dir <- directionFromChar $ Char.toLower c
+  = Just $ StartAutoMove dir
+commandFromKey k mods = keybindings ^. at keybinding
+  where keybinding = Keybinding k mods
+
+--------------------------------------------------------------------------------
+
+directionFromChar :: Char -> Maybe Direction
+directionFromChar 'h' = Just Left
+directionFromChar 'j' = Just Down
+directionFromChar 'k' = Just Up
+directionFromChar 'l' = Just Right
+directionFromChar 'y' = Just UpLeft
+directionFromChar 'u' = Just UpRight
+directionFromChar 'b' = Just DownLeft
+directionFromChar 'n' = Just DownRight
+directionFromChar '.' = Just Here
+directionFromChar _   = Nothing
diff --git a/users/grfn/xanthous/src/Xanthous/Data.hs b/users/aspen/xanthous/src/Xanthous/Data.hs
index 1b67e0f160..703955206a 100644
--- a/users/grfn/xanthous/src/Xanthous/Data.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data.hs
@@ -131,7 +131,6 @@ import           Data.Coerce
 import           Data.Proxy (Proxy(Proxy))
 --------------------------------------------------------------------------------
 import           Xanthous.Util (EqEqProp(..), EqProp, between)
-import           Xanthous.Util.QuickCheck (GenericArbitrary(..))
 import           Xanthous.Orphans ()
 import           Xanthous.Util.Graphics
 import qualified Linear.Metric as Metric
@@ -192,7 +191,7 @@ y = lens (\(Position _ yy) -> yy) (\(Position xx _) yy -> Position xx yy)
 
 type Position = Position' Int
 
-instance Arbitrary a => Arbitrary (Position' a) where
+instance (Arbitrary a) => Arbitrary (Position' a) where
   arbitrary = genericArbitrary
   shrink (Position px py) = Position <$> shrink px <*> shrink py
 
@@ -314,7 +313,8 @@ data Direction where
   Here      :: Direction
   deriving stock (Show, Eq, Ord, Generic)
   deriving anyclass (CoArbitrary, Function, NFData, ToJSON, FromJSON, Hashable)
-  deriving Arbitrary via GenericArbitrary Direction
+
+deriving via (GenericArbitrary Direction) instance Arbitrary Direction
 
 instance Opposite Direction where
   opposite Up        = Down
@@ -433,7 +433,8 @@ data Neighbors a = Neighbors
   }
   deriving stock (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)
   deriving anyclass (NFData, CoArbitrary, Function, MonoFoldable)
-  deriving Arbitrary via GenericArbitrary (Neighbors a)
+
+deriving via (GenericArbitrary (Neighbors a)) instance (Arbitrary a) => Arbitrary (Neighbors a)
 
 type instance Element (Neighbors a) = a
 
@@ -769,9 +770,12 @@ data Box a = Box
   , _dimensions    :: V2 a
   }
   deriving stock (Show, Eq, Ord, Functor, Generic)
-  deriving Arbitrary via GenericArbitrary (Box a)
 makeFieldsNoPrefix ''Box
 
+-- It seems to be necessary to have an `Arg (V2 a) a` constraint, as a is passed
+-- to V2 internally, in order to make GHC figure out this deriving via correctly.
+deriving via (GenericArbitrary (Box a)) instance (Arbitrary a) => Arbitrary (Box a)
+
 bottomRightCorner :: Num a => Box a -> V2 a
 bottomRightCorner box =
   V2 (box ^. topLeftCorner . L._x + box ^. dimensions . L._x)
diff --git a/users/aspen/xanthous/src/Xanthous/Data/App.hs b/users/aspen/xanthous/src/Xanthous/Data/App.hs
new file mode 100644
index 0000000000..13c4b5d610
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Data/App.hs
@@ -0,0 +1,47 @@
+--------------------------------------------------------------------------------
+module Xanthous.Data.App
+  ( Panel(..)
+  , ResourceName(..)
+  , AppEvent(..)
+  ) where
+--------------------------------------------------------------------------------
+import Xanthous.Prelude
+--------------------------------------------------------------------------------
+import Test.QuickCheck
+import Test.QuickCheck.Instances.Text ()
+import Data.Aeson (ToJSON, FromJSON)
+--------------------------------------------------------------------------------
+import Xanthous.Util.QuickCheck
+--------------------------------------------------------------------------------
+
+-- | Enum for "panels" displayed in the game's UI.
+data Panel
+  = -- | A panel providing help with the game's commands
+    HelpPanel
+  | -- | A panel displaying the character's inventory
+    InventoryPanel
+  | -- | A panel describing an item in the inventory in detail
+    --
+    -- The argument is the full description of the item
+    ItemDescriptionPanel Text
+  deriving stock (Show, Eq, Ord, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function, ToJSON, FromJSON)
+  deriving Arbitrary via GenericArbitrary Panel
+
+
+data ResourceName
+  = MapViewport -- ^ The main viewport where we display the game content
+  | Character   -- ^ The character
+  | MessageBox  -- ^ The box where we display messages to the user
+  | Prompt      -- ^ The game's prompt
+  | Panel Panel -- ^ A panel in the game
+  deriving stock (Show, Eq, Ord, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function, ToJSON, FromJSON)
+  deriving Arbitrary via GenericArbitrary ResourceName
+
+data AppEvent
+  = AutoContinue -- ^ Continue whatever autocommand has been requested by the
+                 --   user
+  deriving stock (Show, Eq, Ord, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function, ToJSON, FromJSON)
+  deriving Arbitrary via GenericArbitrary AppEvent
diff --git a/users/grfn/xanthous/src/Xanthous/Data/Entities.hs b/users/aspen/xanthous/src/Xanthous/Data/Entities.hs
index 39953410f2..39953410f2 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/Entities.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/Entities.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/EntityChar.hs b/users/aspen/xanthous/src/Xanthous/Data/EntityChar.hs
index 855a3462da..855a3462da 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/EntityChar.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/EntityChar.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/EntityMap.hs b/users/aspen/xanthous/src/Xanthous/Data/EntityMap.hs
index 1d9c4d46cd..33a98f1ae5 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/EntityMap.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/EntityMap.hs
@@ -133,7 +133,6 @@ instance FunctorWithIndex EntityID EntityMap
 instance FoldableWithIndex EntityID EntityMap
 
 instance TraversableWithIndex EntityID EntityMap where
-  itraversed = byID . itraversed . rmap sequenceA . distrib
   itraverse = itraverseOf itraversed
 
 type instance Element (EntityMap a) = a
diff --git a/users/grfn/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs b/users/aspen/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs
index 1398c611cf..1398c611cf 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/Levels.hs b/users/aspen/xanthous/src/Xanthous/Data/Levels.hs
index 13251d8afd..13251d8afd 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/Levels.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/Levels.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/Memo.hs b/users/aspen/xanthous/src/Xanthous/Data/Memo.hs
index 2b2ee0f960..2b2ee0f960 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/Memo.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/Memo.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/NestedMap.hs b/users/aspen/xanthous/src/Xanthous/Data/NestedMap.hs
index 1b875d4483..1b875d4483 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/NestedMap.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/NestedMap.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/VectorBag.hs b/users/aspen/xanthous/src/Xanthous/Data/VectorBag.hs
index 2e6d48062a..2e6d48062a 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/VectorBag.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/VectorBag.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Character.hs b/users/aspen/xanthous/src/Xanthous/Entities/Character.hs
index d405cb40d3..c8153086f1 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Character.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Character.hs
@@ -45,7 +45,6 @@ import           Test.QuickCheck.Checkers (EqProp)
 import           Control.Monad.State.Lazy (execState)
 import           Control.Monad.Trans.State.Lazy (execStateT)
 --------------------------------------------------------------------------------
-import           Xanthous.Util.QuickCheck
 import           Xanthous.Game.State
 import           Xanthous.Entities.Item
 import           Xanthous.Entities.Common
@@ -219,7 +218,9 @@ defaultCharacterDamage = 1
 characterDamage :: Character -> Hitpoints
 characterDamage
   = fromMaybe defaultCharacterDamage
-  . preview (inventory . wielded . wieldedItems . wieldableItem . Raw.damage)
+  . filter (/= 0)
+  . Just
+  . sumOf (inventory . wielded . wieldedItems . wieldableItem . Raw.damage)
 
 -- | Is the character fully healed up to or past their initial hitpoints?
 isFullyHealed :: Character -> Bool
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Common.hs b/users/aspen/xanthous/src/Xanthous/Entities/Common.hs
index becd1b1ef6..368b03f25b 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Common.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Common.hs
@@ -20,12 +20,18 @@ module Xanthous.Entities.Common
 
     -- ** Wielded items
   , Wielded(..)
+  , nothingWielded
   , hands
   , leftHand
   , rightHand
   , inLeftHand
   , inRightHand
   , doubleHanded
+  , Hand(..)
+  , itemsInHand
+  , inHand
+  , wieldInHand
+  , describeHand
   , wieldedItems
   , WieldedItem(..)
   , wieldedItem
@@ -95,6 +101,7 @@ data Wielded
        via WithOptions '[ 'SumEnc 'ObjWithSingleField ]
            Wielded
 
+
 nothingWielded :: Wielded
 nothingWielded = Hands Nothing Nothing
 
@@ -124,6 +131,43 @@ wieldedItems :: Traversal' Wielded WieldedItem
 wieldedItems k (DoubleHanded wielded) = DoubleHanded <$> k wielded
 wieldedItems k (Hands l r) = Hands <$> _Just k l <*> _Just k r
 
+
+data Hand
+  = LeftHand
+  | RightHand
+  | BothHands
+  deriving stock (Eq, Show, Ord, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function)
+  deriving Arbitrary via GenericArbitrary Hand
+
+itemsInHand :: Hand -> Wielded -> [WieldedItem]
+itemsInHand LeftHand (DoubleHanded wi) = [wi]
+itemsInHand LeftHand (Hands lh _) = toList lh
+itemsInHand RightHand (DoubleHanded wi) = [wi]
+itemsInHand RightHand (Hands _ rh) = toList rh
+itemsInHand BothHands (DoubleHanded wi) = [wi]
+itemsInHand BothHands (Hands lh rh) = toList lh <> toList rh
+
+inHand :: Hand -> WieldedItem -> Wielded
+inHand LeftHand = inLeftHand
+inHand RightHand = inRightHand
+inHand BothHands = review doubleHanded
+
+wieldInHand :: Hand -> WieldedItem -> Wielded -> ([WieldedItem], Wielded)
+wieldInHand hand item w = (itemsInHand hand w, doWield)
+  where
+    doWield = case (hand, w) of
+      (LeftHand, Hands _ r) -> Hands (Just item) r
+      (LeftHand, DoubleHanded _) -> inLeftHand item
+      (RightHand, Hands l _) -> Hands l (Just item)
+      (RightHand, DoubleHanded _) -> inRightHand item
+      (BothHands, _) -> DoubleHanded item
+
+describeHand :: Hand -> Text
+describeHand LeftHand = "your left hand"
+describeHand RightHand = "your right hand"
+describeHand BothHands = "both hands"
+
 data Inventory = Inventory
   { _backpack :: Vector Item
   , _wielded :: Wielded
@@ -199,27 +243,23 @@ class HasInventory s a | s -> a where
 -- | Representation for where in the inventory an item might be
 data InventoryPosition
   = Backpack
-  | LeftHand
-  | RightHand
-  | BothHands
+  | InHand Hand
   deriving stock (Eq, Show, Ord, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
   deriving Arbitrary via GenericArbitrary InventoryPosition
 
 -- | Return a human-readable description of the given 'InventoryPosition'
 describeInventoryPosition :: InventoryPosition -> Text
-describeInventoryPosition Backpack  = "In backpack"
-describeInventoryPosition LeftHand  = "Wielded, in left hand"
-describeInventoryPosition RightHand = "Wielded, in right hand"
-describeInventoryPosition BothHands = "Wielded, in both hands"
+describeInventoryPosition Backpack       = "In backpack"
+describeInventoryPosition (InHand hand)  = "Wielded, in " <> describeHand hand
 
 -- | Given a position in the inventory, return a traversal on the inventory over
 -- all the items in that position
 inventoryPosition :: InventoryPosition -> Traversal' Inventory Item
 inventoryPosition Backpack = backpack . traversed
-inventoryPosition LeftHand = wielded . leftHand . _Just . wieldedItem
-inventoryPosition RightHand = wielded . leftHand . _Just . wieldedItem
-inventoryPosition BothHands = wielded . doubleHanded . wieldedItem
+inventoryPosition (InHand LeftHand) = wielded . leftHand . _Just . wieldedItem
+inventoryPosition (InHand RightHand) = wielded . leftHand . _Just . wieldedItem
+inventoryPosition (InHand BothHands) = wielded . doubleHanded . wieldedItem
 
 -- | A fold over all the items in the inventory accompanied by their position in
 -- the inventory
@@ -230,20 +270,20 @@ itemsWithPosition = folding $ (<>) <$> backpackItems <*> handItems
   where
     backpackItems = toListOf $ backpack . folded . to (Backpack ,)
     handItems inv = case inv ^. wielded of
-       DoubleHanded i -> pure (BothHands, i ^. wieldedItem)
-       Hands l r -> (l ^.. folded . wieldedItem . to (LeftHand ,))
-                 <> (r ^.. folded . wieldedItem . to (RightHand ,))
+       DoubleHanded i -> pure (InHand BothHands, i ^. wieldedItem)
+       Hands l r -> (l ^.. folded . wieldedItem . to (InHand LeftHand ,))
+                 <> (r ^.. folded . wieldedItem . to (InHand RightHand ,))
 
 -- | Remove the first item equal to 'Item' from the given position in the
 -- inventory
 removeItemFromPosition :: InventoryPosition -> Item -> Inventory -> Inventory
 removeItemFromPosition Backpack item inv
   = inv & backpack %~ removeFirst (== item)
-removeItemFromPosition LeftHand item inv
+removeItemFromPosition (InHand LeftHand) item inv
   = inv & wielded . leftHand %~ filter ((/= item) . view wieldedItem)
-removeItemFromPosition RightHand item inv
+removeItemFromPosition (InHand RightHand) item inv
   = inv & wielded . rightHand %~ filter ((/= item) . view wieldedItem)
-removeItemFromPosition BothHands item inv
+removeItemFromPosition (InHand BothHands) item inv
   | has (wielded . doubleHanded . wieldedItem . filtered (== item)) inv
   = inv & wielded .~ nothingWielded
   | otherwise
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Creature.hs b/users/aspen/xanthous/src/Xanthous/Entities/Creature.hs
index 3ea610795e..3ea610795e 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Creature.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Creature.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs b/users/aspen/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs
index 9d5cc13451..d13ea8055c 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs
@@ -25,7 +25,6 @@ import           Test.QuickCheck
 import           Test.QuickCheck.Arbitrary.Generic
 --------------------------------------------------------------------------------
 import           Xanthous.Data
-import           Xanthous.Util.QuickCheck
 --------------------------------------------------------------------------------
 
 
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Draw/Util.hs b/users/aspen/xanthous/src/Xanthous/Entities/Draw/Util.hs
index aa6c5fa4fc..aa6c5fa4fc 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Draw/Util.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Draw/Util.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Entities.hs b/users/aspen/xanthous/src/Xanthous/Entities/Entities.hs
index a0c037a1b4..a0c037a1b4 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Entities.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Entities.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Entities.hs-boot b/users/aspen/xanthous/src/Xanthous/Entities/Entities.hs-boot
index 519a862c6a..519a862c6a 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Entities.hs-boot
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Entities.hs-boot
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Environment.hs b/users/aspen/xanthous/src/Xanthous/Entities/Environment.hs
index b45a91eabe..b45a91eabe 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Environment.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Environment.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Item.hs b/users/aspen/xanthous/src/Xanthous/Entities/Item.hs
index eadd625696..eadd625696 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Item.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Item.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Marker.hs b/users/aspen/xanthous/src/Xanthous/Entities/Marker.hs
index 14d02872ed..14d02872ed 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Marker.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Marker.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/RawTypes.hs b/users/aspen/xanthous/src/Xanthous/Entities/RawTypes.hs
index 9f5cabecdc..a7021d76cf 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/RawTypes.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/RawTypes.hs
@@ -200,6 +200,15 @@ makeFieldsNoPrefix ''EdibleItem
 
 data WieldableItem = WieldableItem
   { _damage :: !Hitpoints
+    -- | Message to use when the character is using this item to attack a
+    --  creature.
+    --
+    -- Grammatically, this should be of the form "slash at the
+    -- {{creature.creatureType.name}} with your dagger"
+    --
+    -- = Parameters
+    --
+    -- [@creature@ (type: 'Creature')] The creature being attacked
   , _attackMessage :: !(Maybe Message)
     -- | Message to use when a creature is using this item to attack the
     -- character.
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws.hs b/users/aspen/xanthous/src/Xanthous/Entities/Raws.hs
index 10f0d83193..10f0d83193 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml
index 2d30e6986b..12c76fc14b 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml
@@ -9,8 +9,8 @@ Item:
   wieldable:
     damage: 3
     attackMessage:
-      - You slash at the {{creature.creatureType.name}} with your dagger.
-      - You stab the {{creature.creatureType.name}} with your dagger.
+      - slash at the {{creature.creatureType.name}} with your dagger
+      - stab the {{creature.creatureType.name}} with your dagger
     creatureAttackMessage:
       - The {{creature.creatureType.name}} slashes at you with its dagger.
       - The {{creature.creatureType.name}} stabs you with its dagger.
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml
index ad3d9cb147..ad3d9cb147 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/husk.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/husk.yaml
index cdfcde616d..cdfcde616d 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/husk.yaml
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/husk.yaml
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/noodles.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/noodles.yaml
index c0501a18a8..c0501a18a8 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/noodles.yaml
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/noodles.yaml
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/ooze.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/ooze.yaml
index fe427c94ab..fe427c94ab 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/ooze.yaml
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/ooze.yaml
diff --git a/users/aspen/xanthous/src/Xanthous/Entities/Raws/rock.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/rock.yaml
new file mode 100644
index 0000000000..3f4e133fe2
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/rock.yaml
@@ -0,0 +1,10 @@
+Item:
+  name: rock
+  description: a rock
+  longDescription: a medium-sized rock made out of some unknown stone
+  char: .
+  wieldable:
+    damage: 1
+    attackMessage: hit the {{creature.creatureType.name}} in the head with your rock
+  density: [ 1500000, 2500000 ]
+  volume: [ 0.000125, 0.001 ]
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/stick.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/stick.yaml
index a7eae9c366..7f9e1faffe 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/stick.yaml
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/stick.yaml
@@ -9,9 +9,9 @@ Item:
   wieldable:
     damage: 2
     attackMessage:
-      - You bonk the {{creature.creatureType.name}} over the head with your stick.
-      - You bash the {{creature.creatureType.name}} on the noggin with your stick.
-      - You whack the {{creature.creatureType.name}} with your stick.
+      - bonk the {{creature.creatureType.name}} over the head with your stick
+      - bash the {{creature.creatureType.name}} on the noggin with your stick
+      - whack the {{creature.creatureType.name}} with your stick
     creatureAttackMessage:
       - The {{creature.creatureType.name}} bonks you over the head with its stick.
       - The {{creature.creatureType.name}} bashes you on the noggin with its stick.
diff --git a/users/grfn/xanthous/src/Xanthous/Game.hs b/users/aspen/xanthous/src/Xanthous/Game.hs
index 89c23f0de8..89c23f0de8 100644
--- a/users/grfn/xanthous/src/Xanthous/Game.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Arbitrary.hs b/users/aspen/xanthous/src/Xanthous/Game/Arbitrary.hs
index 679bfe5459..679bfe5459 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Arbitrary.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Arbitrary.hs
diff --git a/users/aspen/xanthous/src/Xanthous/Game/Draw.hs b/users/aspen/xanthous/src/Xanthous/Game/Draw.hs
new file mode 100644
index 0000000000..291dfd8b5e
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Game/Draw.hs
@@ -0,0 +1,224 @@
+--------------------------------------------------------------------------------
+module Xanthous.Game.Draw
+  ( drawGame
+  ) where
+--------------------------------------------------------------------------------
+import           Xanthous.Prelude
+--------------------------------------------------------------------------------
+import           Brick hiding (loc, on)
+import           Brick.Widgets.Border
+import           Brick.Widgets.Border.Style
+import           Brick.Widgets.Edit
+import           Control.Monad.State.Lazy (evalState)
+import           Control.Monad.State.Class ( get, MonadState, gets )
+--------------------------------------------------------------------------------
+import           Xanthous.Data
+import           Xanthous.Data.App (ResourceName, Panel(..))
+import qualified Xanthous.Data.App as Resource
+import qualified Xanthous.Data.EntityMap as EntityMap
+import           Xanthous.Game.State
+import           Xanthous.Entities.Common (Wielded(..), wielded, backpack)
+import           Xanthous.Entities.Character
+import           Xanthous.Entities.Item (Item)
+import           Xanthous.Game
+                 ( characterPosition
+                 , character
+                 , revealedEntitiesAtPosition
+                 )
+import           Xanthous.Game.Prompt
+import           Xanthous.Orphans ()
+import Brick.Widgets.Center (hCenter)
+import Xanthous.Command (Keybinding (..), keybindings, Command, commandIsHidden)
+import Graphics.Vty.Input.Events (Modifier(..))
+import Graphics.Vty.Input (Key(..))
+import Brick.Widgets.Table
+--------------------------------------------------------------------------------
+
+cursorPosition :: GameState -> Widget ResourceName -> Widget ResourceName
+cursorPosition game
+  | WaitingPrompt _ (Prompt _ _ (preview promptStatePosition -> Just pos) _ _)
+    <- game ^. promptState
+  = showCursor Resource.Prompt (pos ^. loc)
+  | otherwise
+  = showCursor Resource.Character (game ^. characterPosition . loc)
+
+drawMessages :: MessageHistory -> Widget ResourceName
+drawMessages = txtWrap . (<> " ") . unwords . reverse . oextract
+
+drawPromptState :: GamePromptState m -> Widget ResourceName
+drawPromptState NoPrompt = emptyWidget
+drawPromptState (WaitingPrompt msg (Prompt _ pt ps pri _)) =
+  case (pt, ps, pri) of
+    (SStringPrompt, StringPromptState edit, mDef) ->
+      txt msg
+      <+> txt (maybe "" (\def -> "(default: " <> def <> ") ") mDef)
+      <+> renderEditor (txt . fold) True edit
+    (SDirectionPrompt, DirectionPromptState, _) -> txtWrap msg
+    (SMenu, _, menuItems) ->
+      txtWrap msg
+      <=> foldl' (<=>) emptyWidget (map drawMenuItem $ itoList menuItems)
+    _ -> txtWrap msg
+  where
+    drawMenuItem (chr, MenuOption m _) =
+      str ("[" <> pure chr <> "] ") <+> txtWrap m
+
+drawEntities
+  :: forall m. MonadState GameState m
+  => m (Widget ResourceName)
+drawEntities = do
+  allEnts <- use entities
+  let entityPositions = EntityMap.positions allEnts
+      maxY = fromMaybe 0 $ maximumOf (folded . y) entityPositions
+      maxX = fromMaybe 0 $ maximumOf (folded . x) entityPositions
+      rows = traverse mkRow [0..maxY]
+      mkRow rowY = hBox <$> traverse (renderEntityAt . flip Position rowY) [0..maxX]
+      renderEntityAt pos
+        = renderTopEntity pos <$> revealedEntitiesAtPosition pos
+      renderTopEntity pos ents
+        = let neighbors = EntityMap.neighbors pos allEnts
+          in maybe (str " ") (drawWithNeighbors neighbors)
+             $ maximumBy (compare `on` drawPriority)
+             <$> fromNullable ents
+  vBox <$> rows
+
+drawMap :: MonadState GameState m => m (Widget ResourceName)
+drawMap = do
+  cursorPos <- gets cursorPosition
+  viewport Resource.MapViewport Both . cursorPos <$> drawEntities
+
+bullet :: Char
+bullet = 'β€’'
+
+drawInventoryPanel :: GameState -> Widget ResourceName
+drawInventoryPanel game
+  =   drawWielded  (game ^. character . inventory . wielded)
+  <=> drawBackpack (game ^. character . inventory . backpack)
+  where
+    drawWielded (Hands Nothing Nothing) = emptyWidget
+    drawWielded (DoubleHanded i) =
+      txtWrap $ "You are holding " <> description i <> " in both hands"
+    drawWielded (Hands l r) = drawHand "left" l <=> drawHand "right" r
+    drawHand side = maybe emptyWidget $ \i ->
+      txtWrap ( "You are holding "
+              <> description i
+              <> " in your " <> side <> " hand"
+              )
+      <=> txt " "
+
+    drawBackpack :: Vector Item -> Widget ResourceName
+    drawBackpack Empty = txtWrap "Your backpack is empty right now."
+    drawBackpack backpackItems
+      = txtWrap ( "You are currently carrying the following items in your "
+                <> "backpack:")
+        <=> txt " "
+        <=> foldl' (<=>) emptyWidget
+            (map
+              (txtWrap . ((bullet <| " ") <>) . description)
+              backpackItems)
+
+drawHelpPanel :: Widget ResourceName
+drawHelpPanel
+  = txtWrap "To move in a direction or attack, use vi keys (hjklyubn):"
+  <=> txt " "
+  <=> hCenter keyStar
+  <=> txt " "
+  <=> cmds
+  where
+    keyStar
+      =   txt "y k u"
+      <=> txt " \\|/"
+      <=> txt "h-.-l"
+      <=> txt " /|\\"
+      <=> txt "b j n"
+
+    cmds
+      = renderTable
+      . alignRight 0
+      . setDefaultRowAlignment AlignTop
+      . surroundingBorder False
+      . rowBorders False
+      . columnBorders False
+      . table $ help <&> \(key, cmd) -> [ txt $ key <> " : "
+                                       , hLimitPercent 100 $ txtWrap cmd]
+
+    help =
+      extraHelp <>
+      keybindings
+        ^.. ifolded
+          . filtered (not . commandIsHidden)
+          . withIndex
+          . to (bimap displayKeybinding displayCommand)
+    extraHelp
+      = [("Shift-Dir", "Auto-move")]
+
+    displayCommand = tshow @Command
+    displayKeybinding (Keybinding k mods) = foldMap showMod mods <> showKey k
+
+    showMod MCtrl  = "Ctrl-"
+    showMod MShift = "Shift-"
+    showMod MAlt   = "Alt-"
+    showMod MMeta  = "Meta-"
+
+    showKey (KChar c) = pack [c]
+    showKey KEsc = "<Esc>"
+    showKey KBS = "<Backspace>"
+    showKey KEnter = "<Enter>"
+    showKey KLeft = "<Left>"
+    showKey KRight = "<Right>"
+    showKey KUp = "<Up>"
+    showKey KDown = "<Down>"
+    showKey KUpLeft = "<UpLeft>"
+    showKey KUpRight = "<UpRight>"
+    showKey KDownLeft = "<DownLeft>"
+    showKey KDownRight = "<DownRight>"
+    showKey KCenter = "<Center>"
+    showKey (KFun n) = "<F" <> tshow n <> ">"
+    showKey KBackTab = "<BackTab>"
+    showKey KPrtScr = "<PrtScr>"
+    showKey KPause = "<Pause>"
+    showKey KIns = "<Ins>"
+    showKey KHome = "<Home>"
+    showKey KPageUp = "<PageUp>"
+    showKey KDel = "<Del>"
+    showKey KEnd = "<End>"
+    showKey KPageDown = "<PageDown>"
+    showKey KBegin = "<Begin>"
+    showKey KMenu = "<Menu>"
+
+drawPanel :: GameState -> Panel -> Widget ResourceName
+drawPanel game panel
+  = border
+  . hLimit 35
+  . viewport (Resource.Panel panel) Vertical
+  $ case panel of
+      HelpPanel -> drawHelpPanel
+      InventoryPanel -> drawInventoryPanel game
+      ItemDescriptionPanel desc -> txtWrap desc
+
+drawCharacterInfo :: Character -> Widget ResourceName
+drawCharacterInfo ch = txt " " <+> charName <+> charHitpoints
+  where
+    charName | Just n <- ch ^. characterName
+             = txt $ n <> " "
+             | otherwise
+             = emptyWidget
+    charHitpoints
+        = txt "Hitpoints: "
+      <+> txt (tshow $ let Hitpoints hp = characterHitpoints ch in hp)
+
+drawGame :: GameState -> [Widget ResourceName]
+drawGame = evalState $ do
+  game <- get
+  drawnMap <- drawMap
+  pure
+    . pure
+    . withBorderStyle unicode
+    $ case game ^. promptState of
+        NoPrompt -> drawMessages (game ^. messageHistory)
+        _ -> emptyWidget
+    <=> drawPromptState (game ^. promptState)
+    <=>
+    (maybe emptyWidget (drawPanel game) (game ^. activePanel)
+    <+> border drawnMap
+    )
+    <=> drawCharacterInfo (game ^. character)
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Env.hs b/users/aspen/xanthous/src/Xanthous/Game/Env.hs
index 5d7b275c8a..5d7b275c8a 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Env.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Env.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Lenses.hs b/users/aspen/xanthous/src/Xanthous/Game/Lenses.hs
index c692a3b479..c692a3b479 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Lenses.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Lenses.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Memo.hs b/users/aspen/xanthous/src/Xanthous/Game/Memo.hs
index 9e483a8d4a..154063b5dd 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Memo.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Memo.hs
@@ -45,7 +45,7 @@ emptyMemoState :: MemoState
 emptyMemoState = MemoState { _characterVisiblePositions = UnMemoized }
 {-# INLINE emptyMemoState #-}
 
-clear :: Lens' MemoState (Memoized k v) -> MemoState -> MemoState
+clear :: ASetter' MemoState (Memoized key val) -> MemoState -> MemoState
 clear = flip set UnMemoized
 {-# INLINE clear #-}
 
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Prompt.hs b/users/aspen/xanthous/src/Xanthous/Game/Prompt.hs
index 2d6c0a280f..2d6c0a280f 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Prompt.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Prompt.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Game/State.hs b/users/aspen/xanthous/src/Xanthous/Game/State.hs
index 3025eb15be..13b1ba1588 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/State.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/State.hs
@@ -100,7 +100,6 @@ import qualified Graphics.Vty.Attributes as Vty
 import qualified Graphics.Vty.Image as Vty
 --------------------------------------------------------------------------------
 import           Xanthous.Util (KnownBool(..))
-import           Xanthous.Util.QuickCheck (GenericArbitrary(..))
 import           Xanthous.Data
 import           Xanthous.Data.App
 import           Xanthous.Data.Levels
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level.hs
index fc57402e7d..fc57402e7d 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs
index 03d534ca39..03d534ca39 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/Dungeon.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/Dungeon.hs
index 0be7c0435c..0be7c0435c 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/Dungeon.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/Dungeon.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/LevelContents.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/LevelContents.hs
index 4f8a2f42ee..4f8a2f42ee 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/LevelContents.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/LevelContents.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/Util.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/Util.hs
index 0008eb965c..0008eb965c 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/Util.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/Util.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/Village.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/Village.hs
index ab7de95e68..ab7de95e68 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/Village.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/Village.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Speech.hs b/users/aspen/xanthous/src/Xanthous/Generators/Speech.hs
index 8abc00b6a2..8abc00b6a2 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Speech.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Speech.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Messages.hs b/users/aspen/xanthous/src/Xanthous/Messages.hs
index c273d65082..c273d65082 100644
--- a/users/grfn/xanthous/src/Xanthous/Messages.hs
+++ b/users/aspen/xanthous/src/Xanthous/Messages.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Messages/Template.hs b/users/aspen/xanthous/src/Xanthous/Messages/Template.hs
index 5176880355..5176880355 100644
--- a/users/grfn/xanthous/src/Xanthous/Messages/Template.hs
+++ b/users/aspen/xanthous/src/Xanthous/Messages/Template.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Monad.hs b/users/aspen/xanthous/src/Xanthous/Monad.hs
index db602de56f..db602de56f 100644
--- a/users/grfn/xanthous/src/Xanthous/Monad.hs
+++ b/users/aspen/xanthous/src/Xanthous/Monad.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Orphans.hs b/users/aspen/xanthous/src/Xanthous/Orphans.hs
index e9cfddc0e6..66004163f6 100644
--- a/users/grfn/xanthous/src/Xanthous/Orphans.hs
+++ b/users/aspen/xanthous/src/Xanthous/Orphans.hs
@@ -11,9 +11,11 @@ module Xanthous.Orphans
 --------------------------------------------------------------------------------
 import           Xanthous.Prelude hiding (elements, (.=))
 --------------------------------------------------------------------------------
-import           Data.Aeson
+import           Data.Aeson hiding (Key)
+import qualified Data.Aeson.KeyMap as KM
 import           Data.Aeson.Types (typeMismatch)
 import           Data.List.NonEmpty (NonEmpty(..))
+import qualified Graphics.Vty.Input
 import           Graphics.Vty.Attributes
 import           Brick.Widgets.Edit
 import           Data.Text.Zipper.Generic (GenericTextZipper)
@@ -21,6 +23,7 @@ import           Brick.Widgets.Core (getName)
 import           System.Random.Internal (StdGen (..))
 import           System.Random.SplitMix (SMGen ())
 import           Test.QuickCheck
+-- import           Test.QuickCheck.Arbitrary.Generic (Arg ())
 import           "quickcheck-instances" Test.QuickCheck.Instances ()
 import           Text.Megaparsec (errorBundlePretty)
 import           Text.Megaparsec.Pos
@@ -37,6 +40,7 @@ import           Test.QuickCheck.Checkers (EqProp ((=-=)))
 import           Xanthous.Util.JSON
 import           Xanthous.Util.QuickCheck
 import           Xanthous.Util (EqEqProp(EqEqProp))
+import qualified Graphics.Vty.Input.Events
 --------------------------------------------------------------------------------
 
 instance forall s a.
@@ -304,6 +308,9 @@ deriving stock instance Ord Color
 deriving stock instance Ord a => Ord (MaybeDefault a)
 deriving stock instance Ord Attr
 
+deriving anyclass instance Hashable Graphics.Vty.Input.Events.Key
+deriving anyclass instance Hashable Graphics.Vty.Input.Events.Modifier
+
 --------------------------------------------------------------------------------
 
 instance (SemiSequence a, Arbitrary (Element a), Arbitrary a)
@@ -367,7 +374,7 @@ deriving newtype instance (Arbitrary s, CoArbitrary (m (a, s)))
 
 --------------------------------------------------------------------------------
 
-deriving via (GenericArbitrary (V2 a)) instance Arbitrary a => Arbitrary (V2 a)
+deriving via (GenericArbitrary (V2 a)) instance (Arbitrary a) => Arbitrary (V2 a)
 instance CoArbitrary a => CoArbitrary (V2 a)
 instance Function a => Function (V2 a)
 
@@ -461,7 +468,7 @@ instance forall a. (FromJSON a, Ord a) => FromJSON (Interval a) where
         upper <- parseBound $ arr ^?! ix 1
         pure $ interval lower upper
       parseBound = withObject "Bound" $ \obj -> do
-        when (length obj /= 1) $ fail "Expected an object with a single key"
+        when (KM.size obj /= 1) $ fail "Expected an object with a single key"
         let [(k, v)] = obj ^@.. ifolded
         boundary <- case k of
           "Excluded" -> pure Open
@@ -481,3 +488,8 @@ instance forall a. (FromJSON a, Ord a) => FromJSON (Interval a) where
         pure $ val <=..<= val
       checkLength arr =
         when (length arr /= 2) $ fail "Expected array of length 2"
+
+--------------------------------------------------------------------------------
+
+deriving anyclass instance NFData Graphics.Vty.Input.Key
+deriving anyclass instance NFData Graphics.Vty.Input.Modifier
diff --git a/users/grfn/xanthous/src/Xanthous/Physics.hs b/users/aspen/xanthous/src/Xanthous/Physics.hs
index 37530cbbc2..37530cbbc2 100644
--- a/users/grfn/xanthous/src/Xanthous/Physics.hs
+++ b/users/aspen/xanthous/src/Xanthous/Physics.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Prelude.hs b/users/aspen/xanthous/src/Xanthous/Prelude.hs
index 2cb4299303..2cb4299303 100644
--- a/users/grfn/xanthous/src/Xanthous/Prelude.hs
+++ b/users/aspen/xanthous/src/Xanthous/Prelude.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Random.hs b/users/aspen/xanthous/src/Xanthous/Random.hs
index 329b321b8b..329b321b8b 100644
--- a/users/grfn/xanthous/src/Xanthous/Random.hs
+++ b/users/aspen/xanthous/src/Xanthous/Random.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util.hs b/users/aspen/xanthous/src/Xanthous/Util.hs
index f918340f05..f918340f05 100644
--- a/users/grfn/xanthous/src/Xanthous/Util.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Comonad.hs b/users/aspen/xanthous/src/Xanthous/Util/Comonad.hs
index 9e158cc8e2..9e158cc8e2 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Comonad.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Comonad.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Graph.hs b/users/aspen/xanthous/src/Xanthous/Util/Graph.hs
index 8e5c04f4bf..8e5c04f4bf 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Graph.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Graph.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Graphics.hs b/users/aspen/xanthous/src/Xanthous/Util/Graphics.hs
index 0cb009f45a..0cb009f45a 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Graphics.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Graphics.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Inflection.hs b/users/aspen/xanthous/src/Xanthous/Util/Inflection.hs
index 724f2339dd..724f2339dd 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Inflection.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Inflection.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/JSON.hs b/users/aspen/xanthous/src/Xanthous/Util/JSON.hs
index 91d1328e4a..91d1328e4a 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/JSON.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/JSON.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Optparse.hs b/users/aspen/xanthous/src/Xanthous/Util/Optparse.hs
index dfa6537235..dfa6537235 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Optparse.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Optparse.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/QuickCheck.hs b/users/aspen/xanthous/src/Xanthous/Util/QuickCheck.hs
index be12bc2945..aa881b3227 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/QuickCheck.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/QuickCheck.hs
@@ -14,7 +14,6 @@ import Test.QuickCheck.Function
 import Test.QuickCheck.Instances.ByteString ()
 import Test.QuickCheck.Arbitrary.Generic
 import Data.Aeson
-import GHC.Generics (Rep)
 --------------------------------------------------------------------------------
 
 newtype FunctionShow a = FunctionShow a
@@ -31,12 +30,3 @@ newtype FunctionJSON a = FunctionJSON a
 
 instance (ToJSON a, FromJSON a) => Function (FunctionJSON a) where
   function = functionJSON
-
---------------------------------------------------------------------------------
-
-newtype GenericArbitrary a = GenericArbitrary a
-  deriving newtype Generic
-
-instance (Generic a, GArbitrary rep, Rep a ~ rep)
-  => Arbitrary (GenericArbitrary a) where
-  arbitrary = genericArbitrary
diff --git a/users/aspen/xanthous/src/Xanthous/keybindings.yaml b/users/aspen/xanthous/src/Xanthous/keybindings.yaml
new file mode 100644
index 0000000000..cffb27cb03
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/keybindings.yaml
@@ -0,0 +1,22 @@
+q: Quit
+?: Help
+.: Wait
+C-p: PreviousMessage
+',': PickUp
+d: Drop
+o: Open
+c: Close
+;: Look
+e: Eat
+S: Save
+r: Read
+i: ShowInventory
+I: DescribeInventory
+w: Wield
+f: Fire
+'<': GoUp
+'>': GoDown
+R: Rest
+
+# Debug commands
+M-r: ToggleRevealAll
diff --git a/users/grfn/xanthous/src/Xanthous/messages.yaml b/users/aspen/xanthous/src/Xanthous/messages.yaml
index 27ee841dd9..bc08ec1ad2 100644
--- a/users/grfn/xanthous/src/Xanthous/messages.yaml
+++ b/users/aspen/xanthous/src/Xanthous/messages.yaml
@@ -1,4 +1,4 @@
-welcome: Welcome to Xanthous, {{characterName}}! It's dangerous out there, why not stay inside? Use hjklybnu to move.
+welcome: Welcome to Xanthous, {{characterName}}! It's dangerous out there, why not stay inside? Press ? for help.
 dead:
   - You have died...
   - You die...
@@ -115,8 +115,8 @@ wield:
     - You can't wield anything in your backpack
     - You can't wield anything currently in your backpack
   menu: What would you like to wield?
-  # TODO: use actual hands
-  wielded : You wield the {{wieldedItem.itemType.name}} in your right hand.
+  hand: Wield in which hand?
+  wielded: You wield the {{item.wieldedItem.itemType.name}} in {{hand}}
 
 fire:
   nothing:
diff --git a/users/grfn/xanthous/test/Spec.hs b/users/aspen/xanthous/test/Spec.hs
index 64c10cf21e..51758d6a25 100644
--- a/users/grfn/xanthous/test/Spec.hs
+++ b/users/aspen/xanthous/test/Spec.hs
@@ -1,6 +1,7 @@
 --------------------------------------------------------------------------------
 import           Test.Prelude
 --------------------------------------------------------------------------------
+import qualified Xanthous.CommandSpec
 import qualified Xanthous.Data.EntitiesSpec
 import qualified Xanthous.Data.EntityCharSpec
 import qualified Xanthous.Data.EntityMap.GraphicsSpec
@@ -32,7 +33,8 @@ main = defaultMainWithRerun test
 
 test :: TestTree
 test = testGroup "Xanthous"
-  [ Xanthous.Data.EntitiesSpec.test
+  [ Xanthous.CommandSpec.test
+  , Xanthous.Data.EntitiesSpec.test
   , Xanthous.Data.EntityMap.GraphicsSpec.test
   , Xanthous.Data.EntityMapSpec.test
   , Xanthous.Data.LevelsSpec.test
diff --git a/users/grfn/xanthous/test/Test/Prelude.hs b/users/aspen/xanthous/test/Test/Prelude.hs
index 75c1ebf5e7..75c1ebf5e7 100644
--- a/users/grfn/xanthous/test/Test/Prelude.hs
+++ b/users/aspen/xanthous/test/Test/Prelude.hs
diff --git a/users/aspen/xanthous/test/Xanthous/CommandSpec.hs b/users/aspen/xanthous/test/Xanthous/CommandSpec.hs
new file mode 100644
index 0000000000..13f69a808d
--- /dev/null
+++ b/users/aspen/xanthous/test/Xanthous/CommandSpec.hs
@@ -0,0 +1,40 @@
+--------------------------------------------------------------------------------
+module Xanthous.CommandSpec (main, test) where
+--------------------------------------------------------------------------------
+import           Test.Prelude
+--------------------------------------------------------------------------------
+import           Xanthous.Command
+--------------------------------------------------------------------------------
+import           Data.Aeson (fromJSON, Value(String))
+import qualified Data.Aeson as A
+import           Graphics.Vty.Input (Key(..), Modifier(..))
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain test
+
+test :: TestTree
+test = testGroup "Xanthous.CommandSpec"
+  [ testGroup "keybindings"
+    [ testCase "all are valid" $ keybindings `deepseq` pure ()
+    , testProperty "all non-move commands are bound" $ \cmd ->
+        let isn'tMove = case cmd of
+                          Move _ -> False
+                          StartAutoMove _ -> False
+                          _ -> True
+        in isn'tMove ==> member cmd commands
+    ]
+  , testGroup "instance FromJSON Keybinding" $
+    [ ("q", Keybinding (KChar 'q') [])
+    , ("<up>", Keybinding KUp [])
+    , ("<left>", Keybinding KLeft [])
+    , ("<right>", Keybinding KRight [])
+    , ("<down>", Keybinding KDown [])
+    , ("S-q", Keybinding (KChar 'q') [MShift])
+    , ("C-S-q", Keybinding (KChar 'q') [MCtrl, MShift])
+    , ("m-<UP>", Keybinding KUp [MMeta])
+    , ("S", Keybinding (KChar 'S') [])
+    ] <&> \(s, kb) ->
+      testCase (fromString $ unpack s <> " -> " <> show kb)
+       $ fromJSON (String s) @?= A.Success kb
+  ]
diff --git a/users/grfn/xanthous/test/Xanthous/Data/EntitiesSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/EntitiesSpec.hs
index e403503743..e403503743 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/EntitiesSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/EntitiesSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/EntityCharSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/EntityCharSpec.hs
index 9e8024c9d2..9e8024c9d2 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/EntityCharSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/EntityCharSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs
index fd37548ce8..fd37548ce8 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/EntityMapSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/EntityMapSpec.hs
index 7c5cad0196..7c5cad0196 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/EntityMapSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/EntityMapSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/LevelsSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/LevelsSpec.hs
index a752833162..a752833162 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/LevelsSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/LevelsSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/MemoSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/MemoSpec.hs
index ad81f1984d..ad81f1984d 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/MemoSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/MemoSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/NestedMapSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/NestedMapSpec.hs
index acf7a67268..acf7a67268 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/NestedMapSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/NestedMapSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/DataSpec.hs b/users/aspen/xanthous/test/Xanthous/DataSpec.hs
index 9e67505ba9..9e67505ba9 100644
--- a/users/grfn/xanthous/test/Xanthous/DataSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/DataSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Entities/CharacterSpec.hs b/users/aspen/xanthous/test/Xanthous/Entities/CharacterSpec.hs
index 734cce1efb..734cce1efb 100644
--- a/users/grfn/xanthous/test/Xanthous/Entities/CharacterSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Entities/CharacterSpec.hs
diff --git a/users/aspen/xanthous/test/Xanthous/Entities/CommonSpec.hs b/users/aspen/xanthous/test/Xanthous/Entities/CommonSpec.hs
new file mode 100644
index 0000000000..a6f8401cf7
--- /dev/null
+++ b/users/aspen/xanthous/test/Xanthous/Entities/CommonSpec.hs
@@ -0,0 +1,65 @@
+--------------------------------------------------------------------------------
+module Xanthous.Entities.CommonSpec (main, test) where
+--------------------------------------------------------------------------------
+import           Test.Prelude
+import           Data.Vector.Lens (toVectorOf)
+--------------------------------------------------------------------------------
+import           Xanthous.Entities.Common
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain test
+
+newtype OneHand = OneHand Hand
+  deriving stock Show
+
+instance Arbitrary OneHand where
+  arbitrary = OneHand <$> elements [LeftHand, RightHand]
+
+otherHand :: Hand -> Hand
+otherHand LeftHand = RightHand
+otherHand RightHand = LeftHand
+otherHand BothHands = error "OtherHand BothHands"
+
+test :: TestTree
+test = testGroup "Xanthous.Entities.CommonSpec"
+  [ testGroup "Inventory"
+    [ testProperty "items === itemsWithPosition . _2" $ \inv ->
+        inv ^.. items === inv ^.. itemsWithPosition . _2
+    , testGroup "removeItemFromPosition" $
+      let rewield w inv =
+            let (old, inv') = inv & wielded <<.~ w
+            in inv' & backpack <>~ toVectorOf (wieldedItems . wieldedItem) old
+      in [ (Backpack, \item -> backpack %~ (item ^. wieldedItem <|))
+         , (InHand LeftHand, rewield . inLeftHand)
+         , (InHand RightHand, rewield . inRightHand)
+         , (InHand BothHands, rewield . review doubleHanded)
+         ] <&> \(pos, addItem) ->
+           testProperty (show pos) $ \inv item ->
+             let inv' = addItem item inv
+                 inv'' = removeItemFromPosition pos (item ^. wieldedItem) inv'
+             in inv'' ^.. items === inv ^.. items
+    ]
+  , testGroup "Wielded items"
+    [ testGroup "wieldInHand"
+      [ testProperty "puts the item in the hand" $ \w hand item ->
+          let (_, w') = wieldInHand hand item w
+          in itemsInHand hand w' === [item]
+      , testProperty "returns items in both hands when wielding double-handed"
+        $ \lh rh newItem ->
+          let w = Hands (Just lh) (Just rh)
+              (prevItems, _) = wieldInHand BothHands newItem w
+          in prevItems === [lh, rh]
+      , testProperty "wielding in one hand leaves the item in the other hand"
+        $ \(OneHand h) existingItem newItem ->
+          let (_, w) = wieldInHand h existingItem nothingWielded
+              (prevItems, w') = wieldInHand (otherHand h) newItem w
+          in   prevItems === []
+          .&&. sort (w' ^.. wieldedItems) === sort [existingItem, newItem]
+      , testProperty "always leaves the same items overall" $ \w hand item ->
+          let (prevItems, w') = wieldInHand hand item w
+          in  sort (prevItems <> (w' ^.. wieldedItems))
+          === sort (item : w ^.. wieldedItems)
+      ]
+    ]
+  ]
diff --git a/users/grfn/xanthous/test/Xanthous/Entities/RawTypesSpec.hs b/users/aspen/xanthous/test/Xanthous/Entities/RawTypesSpec.hs
index e23f7faba3..e23f7faba3 100644
--- a/users/grfn/xanthous/test/Xanthous/Entities/RawTypesSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Entities/RawTypesSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Entities/RawsSpec.hs b/users/aspen/xanthous/test/Xanthous/Entities/RawsSpec.hs
index b6c80be51b..b6c80be51b 100644
--- a/users/grfn/xanthous/test/Xanthous/Entities/RawsSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Entities/RawsSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Game/PromptSpec.hs b/users/aspen/xanthous/test/Xanthous/Game/PromptSpec.hs
index d7a3df4aca..d7a3df4aca 100644
--- a/users/grfn/xanthous/test/Xanthous/Game/PromptSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Game/PromptSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Game/StateSpec.hs b/users/aspen/xanthous/test/Xanthous/Game/StateSpec.hs
index 34584f73b2..34584f73b2 100644
--- a/users/grfn/xanthous/test/Xanthous/Game/StateSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Game/StateSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/GameSpec.hs b/users/aspen/xanthous/test/Xanthous/GameSpec.hs
index 2fa8527d0e..2fa8527d0e 100644
--- a/users/grfn/xanthous/test/Xanthous/GameSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/GameSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs b/users/aspen/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs
index b53c657f75..b53c657f75 100644
--- a/users/grfn/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/MessageSpec.hs b/users/aspen/xanthous/test/Xanthous/MessageSpec.hs
index 2068e338ba..2068e338ba 100644
--- a/users/grfn/xanthous/test/Xanthous/MessageSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/MessageSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Messages/TemplateSpec.hs b/users/aspen/xanthous/test/Xanthous/Messages/TemplateSpec.hs
index 2a3873c3b0..2a3873c3b0 100644
--- a/users/grfn/xanthous/test/Xanthous/Messages/TemplateSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Messages/TemplateSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/OrphansSpec.hs b/users/aspen/xanthous/test/Xanthous/OrphansSpec.hs
index 0d800e8a91..0d800e8a91 100644
--- a/users/grfn/xanthous/test/Xanthous/OrphansSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/OrphansSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/RandomSpec.hs b/users/aspen/xanthous/test/Xanthous/RandomSpec.hs
index c88bd95629..c88bd95629 100644
--- a/users/grfn/xanthous/test/Xanthous/RandomSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/RandomSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Util/GraphSpec.hs b/users/aspen/xanthous/test/Xanthous/Util/GraphSpec.hs
index 35ff090b28..35ff090b28 100644
--- a/users/grfn/xanthous/test/Xanthous/Util/GraphSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Util/GraphSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Util/GraphicsSpec.hs b/users/aspen/xanthous/test/Xanthous/Util/GraphicsSpec.hs
index 61e5892803..61e5892803 100644
--- a/users/grfn/xanthous/test/Xanthous/Util/GraphicsSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Util/GraphicsSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Util/InflectionSpec.hs b/users/aspen/xanthous/test/Xanthous/Util/InflectionSpec.hs
index fad8410431..fad8410431 100644
--- a/users/grfn/xanthous/test/Xanthous/Util/InflectionSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Util/InflectionSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/UtilSpec.hs b/users/aspen/xanthous/test/Xanthous/UtilSpec.hs
index 684a03b2c7..684a03b2c7 100644
--- a/users/grfn/xanthous/test/Xanthous/UtilSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/UtilSpec.hs
diff --git a/users/grfn/xanthous/xanthous.cabal b/users/aspen/xanthous/xanthous.cabal
index 987e1f48f6..12222c2673 100644
--- a/users/grfn/xanthous/xanthous.cabal
+++ b/users/aspen/xanthous/xanthous.cabal
@@ -1,10 +1,10 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.34.5.
+-- This file has been generated from package.yaml by hpack version 0.35.0.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 8cae8550487b6092c18c82a0dc29bf22980d416771c66f6fca3e151875c66495
+-- hash: b3bf8e65d621856081832c9d3c8e8ad38799e23a7f5084dc4f972daa654a0ff3
 
 name:           xanthous
 version:        0.1.0.0
@@ -119,7 +119,7 @@ library
       TypeFamilies
       TypeOperators
       ViewPatterns
-  ghc-options: -Wall
+  ghc-options: -Wall -fconstraint-solver-iterations=6
   build-depends:
       JuicyPixels
     , MonadRandom
@@ -220,7 +220,7 @@ executable xanthous
       TypeFamilies
       TypeOperators
       ViewPatterns
-  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -O2
+  ghc-options: -Wall -fconstraint-solver-iterations=6 -threaded -rtsopts -with-rtsopts=-N -O2
   build-depends:
       JuicyPixels
     , MonadRandom
@@ -293,6 +293,7 @@ test-suite test
   main-is: Spec.hs
   other-modules:
       Test.Prelude
+      Xanthous.CommandSpec
       Xanthous.Data.EntitiesSpec
       Xanthous.Data.EntityCharSpec
       Xanthous.Data.EntityMap.GraphicsSpec
@@ -348,7 +349,7 @@ test-suite test
       TypeFamilies
       TypeOperators
       ViewPatterns
-  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -O0
+  ghc-options: -Wall -fconstraint-solver-iterations=6 -threaded -rtsopts -with-rtsopts=-N -O0
   build-depends:
       JuicyPixels
     , MonadRandom
@@ -459,7 +460,7 @@ benchmark benchmark
       TypeFamilies
       TypeOperators
       ViewPatterns
-  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
+  ghc-options: -Wall -fconstraint-solver-iterations=6 -threaded -rtsopts -with-rtsopts=-N
   build-depends:
       JuicyPixels
     , MonadRandom
diff --git a/users/cynthia/OWNERS b/users/cynthia/OWNERS
index da62f3777a..762a4ec9ea 100644
--- a/users/cynthia/OWNERS
+++ b/users/cynthia/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - cynthia
+set noparent
+
+cynthia
diff --git a/users/cynthia/keys.nix b/users/cynthia/keys.nix
index bac8dc1c57..e2f4ce488c 100644
--- a/users/cynthia/keys.nix
+++ b/users/cynthia/keys.nix
@@ -4,4 +4,4 @@
   all = [
     "cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICsj3W6QczgxE3s5GGT8qg0aLrCM+QeRnSq9RkiZtKvz meow"
   ];
-}
\ No newline at end of file
+}
diff --git a/users/edef/OWNERS b/users/edef/OWNERS
index 05f7639c89..c0d1024f14 100644
--- a/users/edef/OWNERS
+++ b/users/edef/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - edef
+set noparent
+
+edef
diff --git a/users/edef/depot-scan/wrap.nix b/users/edef/depot-scan/wrap.nix
index dcb557a24b..77362b3f61 100644
--- a/users/edef/depot-scan/wrap.nix
+++ b/users/edef/depot-scan/wrap.nix
@@ -3,13 +3,14 @@
 let
 
   global = {
-    import = global.scopedImport {};
+    import = global.scopedImport { };
     scopedImport = x: builtins.scopedImport (global // x);
     builtins = builtins // {
       inherit (global) import scopedImport;
       readFile = path: builtins.trace "depot-scan '${toString path}'" (builtins.readFile path);
-      readDir  = path: builtins.trace "depot-scan '${toString path}'" (builtins.readDir  path);
+      readDir = path: builtins.trace "depot-scan '${toString path}'" (builtins.readDir path);
     };
   };
 
-in global.import
+in
+global.import
diff --git a/users/edef/refscan/.gitignore b/users/edef/refscan/.gitignore
new file mode 100644
index 0000000000..ee4b088aee
--- /dev/null
+++ b/users/edef/refscan/.gitignore
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: edef <edef@edef.eu>
+# SPDX-License-Identifier: CC0-1.0
+
+/target
+**/*.rs.bk
diff --git a/users/edef/refscan/Cargo.lock b/users/edef/refscan/Cargo.lock
new file mode 100644
index 0000000000..a3515a75d7
--- /dev/null
+++ b/users/edef/refscan/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "refscan"
+version = "0.1.0"
diff --git a/users/edef/refscan/Cargo.lock.license b/users/edef/refscan/Cargo.lock.license
new file mode 100644
index 0000000000..2cd6276751
--- /dev/null
+++ b/users/edef/refscan/Cargo.lock.license
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: edef <edef@edef.eu>
+SPDX-License-Identifier: CC0-1.0
+
diff --git a/users/edef/refscan/Cargo.toml b/users/edef/refscan/Cargo.toml
new file mode 100644
index 0000000000..dfac9a899e
--- /dev/null
+++ b/users/edef/refscan/Cargo.toml
@@ -0,0 +1,10 @@
+# SPDX-FileCopyrightText: edef <edef@edef.eu>
+# SPDX-License-Identifier: MPL-2.0
+
+[package]
+name = "refscan"
+version = "0.1.0"
+authors = ["edef <edef@edef.eu>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/users/edef/refscan/LICENSES/CC0-1.0.txt b/users/edef/refscan/LICENSES/CC0-1.0.txt
new file mode 100644
index 0000000000..0e259d42c9
--- /dev/null
+++ b/users/edef/refscan/LICENSES/CC0-1.0.txt
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/users/edef/refscan/LICENSES/MPL-2.0.txt b/users/edef/refscan/LICENSES/MPL-2.0.txt
new file mode 100644
index 0000000000..ee6256cdb6
--- /dev/null
+++ b/users/edef/refscan/LICENSES/MPL-2.0.txt
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in 
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.
diff --git a/users/edef/refscan/src/lib.rs b/users/edef/refscan/src/lib.rs
new file mode 100644
index 0000000000..3d4a07f3dd
--- /dev/null
+++ b/users/edef/refscan/src/lib.rs
@@ -0,0 +1,154 @@
+// SPDX-FileCopyrightText: edef <edef@edef.eu>
+// SPDX-License-Identifier: MPL-2.0
+
+use self::simd::u8x32;
+
+fn prefilter(haystack: u8x32) -> u32 {
+    let alp = haystack.gt(u8x32::splat(b'a' - 1)) & haystack.lt(u8x32::splat(b'z' + 1));
+    let num = haystack.gt(u8x32::splat(b'0' - 1)) & haystack.lt(u8x32::splat(b'9' + 1));
+    alp | num
+}
+
+/// scan_clean returns `Err(&buffer[..n])` of known pointer-free data,
+/// or `Ok(buffer)` if the entire buffer is pointer-free.
+pub fn scan_clean(buffer: &[u8]) -> Result<&[u8], &[u8]> {
+    let buffer = {
+        let n = buffer.len() & !31;
+        &buffer[..n]
+    };
+
+    let mut masks = buffer
+        .chunks_exact(32)
+        .map(|chunk| prefilter(u8x32::from_slice_unaligned(chunk)))
+        .enumerate()
+        .map(|e| (e.0 * 32, e.1))
+        .peekable();
+
+    while let Some((offset, mask)) = masks.next() {
+        let peek = masks.peek().map(|x| x.1).unwrap_or(!0 >> 1);
+        let n = (!mask).leading_zeros() + (!peek).trailing_zeros();
+        if n >= 32 {
+            let offset = offset + mask.trailing_zeros() as usize;
+            return Err(&buffer[..offset]);
+        }
+    }
+
+    Ok(buffer)
+}
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn scan_tail() {
+        let buffer = b"_xfbmj7sl2ikicym9x3yq7cms5qx1w39k";
+        assert_eq!(crate::scan_clean(buffer), Err(&buffer[..1]));
+    }
+    #[test]
+    fn scan_straddle() {
+        let buffer = b"________________xfbmj7sl2ikicym9x3yq7cms5qx1w39k________________";
+        assert_eq!(crate::scan_clean(buffer), Err(&buffer[..16]));
+    }
+    #[test]
+    fn scan_clean() {
+        let buffer = b"x_______________xfbmj7sl2ikicym9x3yq-cms5qx1w3-k________________";
+        assert_eq!(crate::scan_clean(buffer), Ok(&buffer[..]));
+    }
+}
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+mod simd {
+    #[cfg(target_arch = "x86")]
+    use std::arch::x86 as arch;
+    #[cfg(target_arch = "x86_64")]
+    use std::arch::x86_64 as arch;
+    use {
+        arch::{__m256i, _mm256_cmpgt_epi8, _mm256_movemask_epi8, _mm256_set1_epi8},
+        std::ptr,
+    };
+
+    #[allow(non_camel_case_types)]
+    #[derive(Copy, Clone)]
+    pub struct u8x32(__m256i);
+
+    impl u8x32 {
+        #[inline(always)]
+        pub fn from_slice_unaligned(slice: &[u8]) -> Self {
+            assert_eq!(slice.len(), 32);
+            u8x32(unsafe { ptr::read_unaligned(slice.as_ptr().cast()) })
+        }
+
+        #[inline(always)]
+        pub fn splat(x: u8) -> Self {
+            u8x32(unsafe { _mm256_set1_epi8(x as i8) })
+        }
+
+        #[inline(always)]
+        pub fn gt(self, b: Self) -> u32 {
+            unsafe { _mm256_movemask_epi8(_mm256_cmpgt_epi8(self.0, b.0)) as u32 }
+        }
+
+        #[inline(always)]
+        pub fn lt(self, b: Self) -> u32 {
+            b.gt(self)
+        }
+    }
+}
+
+#[cfg(target_arch = "aarch64")]
+mod simd {
+    use std::{
+        arch::aarch64::{
+            uint8x16_t as u8x16, vaddv_u8, vandq_u8, vcgtq_u8, vdupq_n_u8, vget_high_u8,
+            vget_low_u8, vshlq_u8,
+        },
+        mem, ptr,
+    };
+
+    #[allow(non_camel_case_types)]
+    #[derive(Copy, Clone)]
+    #[repr(transparent)]
+    pub struct u8x32([u8x16; 2]);
+
+    impl u8x32 {
+        #[cfg(target_endian = "little")]
+        #[inline(always)]
+        pub fn from_slice_unaligned(slice: &[u8]) -> Self {
+            assert_eq!(slice.len(), 32);
+            u8x32(unsafe { ptr::read_unaligned(slice.as_ptr().cast()) })
+        }
+
+        #[inline(always)]
+        pub fn splat(x: u8) -> Self {
+            u8x32(unsafe {
+                let x = vdupq_n_u8(x);
+                [x, x]
+            })
+        }
+
+        #[inline(always)]
+        pub fn gt(&self, b: Self) -> u32 {
+            let u8x32([al, ah]) = *self;
+            let u8x32([bl, bh]) = b;
+
+            fn f(a: u8x16, b: u8x16) -> u32 {
+                unsafe {
+                    let c = vshlq_u8(
+                        vandq_u8(vdupq_n_u8(0x80), vcgtq_u8(a, b)),
+                        mem::transmute([
+                            -7, -6, -5, -4, -3, -2, -1, 0, -7, -6, -5, -4, -3, -2, -1, 0i8,
+                        ]),
+                    );
+
+                    (vaddv_u8(vget_low_u8(c)) as u32) << 0 | (vaddv_u8(vget_high_u8(c)) as u32) << 8
+                }
+            }
+
+            f(al, bl) << 0 | f(ah, bh) << 16
+        }
+
+        #[inline(always)]
+        pub fn lt(self, b: Self) -> u32 {
+            b.gt(self)
+        }
+    }
+}
diff --git a/users/edef/refscan/src/main.rs b/users/edef/refscan/src/main.rs
new file mode 100644
index 0000000000..e572abf0a1
--- /dev/null
+++ b/users/edef/refscan/src/main.rs
@@ -0,0 +1,58 @@
+// SPDX-FileCopyrightText: edef <edef@edef.eu>
+// SPDX-License-Identifier: MPL-2.0
+
+use std::{
+    collections::BTreeSet as Set,
+    convert::TryInto,
+    io::{self, Read},
+    str,
+};
+
+fn main() {
+    let max_refs: Set<[u8; 32]> = include_str!("../testdata/maxrefs")
+        .lines()
+        .map(|l| l.as_bytes().try_into().unwrap())
+        .collect();
+
+    let input = {
+        let stdin = io::stdin();
+        let mut buffer = Vec::new();
+        stdin.lock().read_to_end(&mut buffer).unwrap();
+        buffer
+    };
+
+    let base = input.as_ptr() as usize;
+    let mut input: &[u8] = &input;
+    while input.len() >= 32 {
+        match refscan::scan_clean(&input) {
+            Ok(buffer) | Err(buffer) => {
+                let n = buffer.len();
+                input = &input[n..];
+            }
+        }
+
+        let buffer = {
+            let idx = input.iter().position(|x| match x {
+                b'a'..=b'z' | b'0'..=b'9' => false,
+                _ => true,
+            });
+            idx.map(|idx| &input[..idx]).unwrap_or(input)
+        };
+
+        for chunk in buffer.windows(32) {
+            let offset = (chunk.as_ptr() as usize) - base;
+            let chunk = {
+                let mut fixed = [0u8; 32];
+                fixed.copy_from_slice(chunk);
+                fixed
+            };
+            if max_refs.contains(&chunk) {
+                let seen = unsafe { str::from_utf8_unchecked(&chunk) };
+                println!("{} {}", seen, offset);
+            }
+        }
+
+        let n = buffer.len();
+        input = &input[n..];
+    }
+}
diff --git a/users/edef/refscan/testdata/.gitignore b/users/edef/refscan/testdata/.gitignore
new file mode 100644
index 0000000000..1d278bd6ce
--- /dev/null
+++ b/users/edef/refscan/testdata/.gitignore
@@ -0,0 +1,6 @@
+# SPDX-FileCopyrightText: edef <edef@edef.eu>
+# SPDX-License-Identifier: CC0-1.0
+
+/maxrefs
+/nar
+/result
diff --git a/users/edef/refscan/testdata/generate.sh b/users/edef/refscan/testdata/generate.sh
new file mode 100755
index 0000000000..9f416024c1
--- /dev/null
+++ b/users/edef/refscan/testdata/generate.sh
@@ -0,0 +1,8 @@
+#! /usr/bin/env bash
+# SPDX-FileCopyrightText: edef <edef@edef.eu>
+# SPDX-License-Identifier: CC0-1.0
+set -euo pipefail
+
+drv=$(nix-instantiate '<nixpkgs>' -A ghc)
+nix --extra-experimental-features nix-command show-derivation -r "$drv" | jq -r '.[] | .outputs[].path, .inputSrcs[]' | sort -u | cut -d/ -f4 | cut -d- -f1 > maxrefs
+nix-store --dump "$(nix-build "$drv")" > nar
diff --git a/users/ericvolp12/OWNERS b/users/ericvolp12/OWNERS
index 5a012a695b..a43910023c 100644
--- a/users/ericvolp12/OWNERS
+++ b/users/ericvolp12/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - ericvolp12
+set noparent
+
+ericvolp12
diff --git a/users/espes/OWNERS b/users/espes/OWNERS
new file mode 100644
index 0000000000..257a1b8200
--- /dev/null
+++ b/users/espes/OWNERS
@@ -0,0 +1 @@
+espes
diff --git a/users/eta/OWNERS b/users/eta/OWNERS
index f212e89e2a..59e5e2120a 100644
--- a/users/eta/OWNERS
+++ b/users/eta/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - eta
+set noparent
+
+eta
diff --git a/users/eta/keys.nix b/users/eta/keys.nix
index 247a182843..ebdc8479a5 100644
--- a/users/eta/keys.nix
+++ b/users/eta/keys.nix
@@ -9,4 +9,4 @@ let
     whitby = [ keys.yubikey4 keys.yubikey5 ];
   };
 in
-  configs
+configs
diff --git a/users/firefly/OWNERS b/users/firefly/OWNERS
index 55d62a5723..1b7dd7189f 100644
--- a/users/firefly/OWNERS
+++ b/users/firefly/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - firefly
+set noparent
+
+firefly
diff --git a/users/flokli/OWNERS b/users/flokli/OWNERS
index 63e0fbda3c..a70f5eefff 100644
--- a/users/flokli/OWNERS
+++ b/users/flokli/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - flokli
+set noparent
+
+flokli
diff --git a/users/flokli/archeology/OWNERS b/users/flokli/archeology/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/users/flokli/archeology/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/users/flokli/archeology/README.md b/users/flokli/archeology/README.md
new file mode 100644
index 0000000000..e4cd9b84b0
--- /dev/null
+++ b/users/flokli/archeology/README.md
@@ -0,0 +1,5 @@
+# archeology
+
+This directory contains various scripts and helpers used for nix-archeology tasks.
+
+It's used from some of the archeology instances, as well as standalone.
diff --git a/users/flokli/archeology/default.nix b/users/flokli/archeology/default.nix
new file mode 100644
index 0000000000..d642399cbe
--- /dev/null
+++ b/users/flokli/archeology/default.nix
@@ -0,0 +1,51 @@
+{ depot, pkgs, ... }:
+
+let
+  clickhouseConfigAWS = builtins.toFile "clickhouse-local.xml" ''
+    <clickhouse>
+        <s3>
+          <use_environment_credentials>true</use_environment_credentials>
+        </s3>
+    </clickhouse>
+  '';
+  # clickhouse has a very odd AWS config concept.
+  # Configure it to be a bit more sane.
+  clickhoseLocalFixedAWS = pkgs.runCommand "clickhouse-local-fixed"
+    {
+      nativeBuildInputs = [ pkgs.makeWrapper ];
+    } ''
+    mkdir -p $out/bin
+    makeWrapper ${pkgs.clickhouse}/bin/clickhouse-local $out/bin/clickhouse-local \
+      --append-flags "-C ${clickhouseConfigAWS}"
+  '';
+in
+
+depot.nix.readTree.drvTargets {
+  inherit clickhoseLocalFixedAWS;
+  parse-bucket-logs = pkgs.runCommand "archeology-parse-bucket-logs"
+    {
+      nativeBuildInputs = [ pkgs.makeWrapper ];
+    } ''
+    mkdir -p $out/bin
+    makeWrapper ${(pkgs.writers.writeRust "parse-bucket-logs-unwrapped" {} ./parse_bucket_logs.rs)} $out/bin/archeology-parse-bucket-logs \
+      --prefix PATH : ${pkgs.lib.makeBinPath [ clickhoseLocalFixedAWS ]}
+  '';
+
+  shell = pkgs.mkShell {
+    name = "archeology-shell";
+    packages = with pkgs; [ awscli2 clickhoseLocalFixedAWS rust-analyzer rustc rustfmt ];
+
+    AWS_PROFILE = "sso";
+    AWS_CONFIG_FILE = pkgs.writeText "aws-config" ''
+      [sso-session nixos]
+      sso_region = eu-north-1
+      sso_start_url = https://nixos.awsapps.com/start
+      sso_registration_scopes = sso:account:access
+
+      [profile "sso"]
+      sso_session = nixos
+      sso_account_id = 080433136561
+      sso_role_name = archeologist
+    '';
+  };
+}
diff --git a/users/flokli/archeology/parse_bucket_logs.rs b/users/flokli/archeology/parse_bucket_logs.rs
new file mode 100644
index 0000000000..3ab2e133b3
--- /dev/null
+++ b/users/flokli/archeology/parse_bucket_logs.rs
@@ -0,0 +1,42 @@
+use std::env;
+use std::process::Command;
+use std::process::ExitCode;
+
+fn main() -> ExitCode {
+    let args: Vec<String> = env::args().collect();
+    if args.len() != 3 {
+        eprintln!("needs two args, input s3 url (glob) and output pq file");
+        return ExitCode::FAILURE;
+    }
+
+    let input_files = &args[1];
+    let output_file = &args[2];
+
+    let mut cmd = Command::new("clickhouse-local");
+    cmd.arg("--progress")
+        .arg("-q")
+        .arg(format!(r#"SELECT
+        key,
+        toInt64(nullif(http_status, '-')) AS http_status,
+        toInt64(nullif(object_size_str, '-')) AS object_size,
+        toInt64(nullif(bytes_sent_str, '-')) AS bytes_sent,
+        nullif(user_agent, '-') AS user_agent,
+        operation,
+        nullif(requester, '-') AS requester,
+        parseDateTime(timestamp_str, '%d/%b/%Y:%k:%i:%s %z') AS timestamp
+    FROM s3(
+        '{}',
+        'Regexp',
+        'owner String , bucket String, timestamp_str String, remote_ip String, requester LowCardinality(String), request_id String, operation LowCardinality(String), key String, request_uri String, http_status String, error_code String, bytes_sent_str String, object_size_str String, total_time String, turn_around_time String, referer String, user_agent String, version_id String, host_id String, signature_version String, cipher_suite String, authentication_type String, host_header String, tls_version String, access_point_arn String, acl_required String'
+    )
+    ORDER BY timestamp ASC
+    SETTINGS
+        format_regexp_skip_unmatched = 1,
+        format_regexp = '(\\S+) (\\S+) \\[(.*)\\] (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) ((?:\\S+ \\S+ \\S+)|\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) ("\\S+") (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+).*',
+        output_format_parquet_compression_method = 'zstd'
+    INTO OUTFILE '{}' FORMAT Parquet"#, input_files, output_file));
+
+    cmd.status().expect("clickhouse-local failed");
+
+    ExitCode::SUCCESS
+}
diff --git a/users/flokli/archivist/OWNERS b/users/flokli/archivist/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/users/flokli/archivist/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/users/flokli/archivist/default.nix b/users/flokli/archivist/default.nix
new file mode 100644
index 0000000000..ef49c46db2
--- /dev/null
+++ b/users/flokli/archivist/default.nix
@@ -0,0 +1,28 @@
+{ depot
+, pkgs
+, ...
+}:
+depot.nix.readTree.drvTargets {
+  shell = pkgs.mkShell {
+    name = "archivist-shell";
+    packages = with pkgs; [ awscli2 ];
+
+    AWS_PROFILE = "archivist";
+    AWS_CONFIG_FILE = pkgs.writeText "aws-config" ''
+      [sso-session nixos]
+      sso_region = eu-north-1
+      sso_start_url = https://nixos.awsapps.com/start
+      sso_registration_scopes = sso:account:access
+
+      [profile "archivist"]
+      sso_session = nixos
+      sso_account_id = 286553126452
+      sso_role_name = AWSAdministratorAccess
+
+      [profile "archeologist"]
+      sso_session = nixos
+      sso_account_id = 080433136561
+      sso_role_name = archeologist
+    '';
+  };
+}
diff --git a/users/flokli/ipu6-softisp/README.md b/users/flokli/ipu6-softisp/README.md
new file mode 100644
index 0000000000..2ab727ace4
--- /dev/null
+++ b/users/flokli/ipu6-softisp/README.md
@@ -0,0 +1,29 @@
+# ipu6-softisp
+
+This code adds support for the ipu6 webcams via libcamera, based on the work in
+https://copr.fedorainfracloud.org/coprs/jwrdegoede/ipu6-softisp/.
+
+It's supposed to be included in your NixOS configuration imports, and will:
+
+ - Add some patches to your kernel, which should apply on 6.8.x
+ - Add the `ipu6-camera-bins` firmware (still needed)
+ - Enable some kernel config options
+ - Add an udev rule so libcamera can do DMABUF things
+ - Override `services.pipewire.package` and
+   `services.pipewire.wireplumber.package` to use a pipewire built with a libcamera
+   with support for this webcam.
+
+Please make sure you don't have any of the `hardware.ipu6` options still
+enabled, as they use the closed-source userspace stack and will conflict.
+
+Also make sure to track nixos-unstable for this. This code will get periodically
+updated to be compatible with nixos-unstable!
+
+The testing instructions from
+https://copr.fedorainfracloud.org/coprs/jwrdegoede/ipu6-softisp/ still apply.
+
+`qcam` can be found in `libcamera-qcam` (pending on
+https://github.com/NixOS/nixpkgs/pull/284964 to trickle into master).
+
+Thanks to Hans de Goede for helping me bringing this up, as well as to
+puckipedia for sorting out some pipewire-related confusion.
diff --git a/users/flokli/ipu6-softisp/config.nix b/users/flokli/ipu6-softisp/config.nix
new file mode 100644
index 0000000000..95cb3e5c25
--- /dev/null
+++ b/users/flokli/ipu6-softisp/config.nix
@@ -0,0 +1,74 @@
+{ pkgs, lib, ... }:
+
+let
+  libcamera = pkgs.libcamera.overrideAttrs (old: {
+    mesonFlags = old.mesonFlags or [ ] ++ [
+      "-Dpipelines=simple,ipu3,uvcvideo"
+      "-Dipas=simple,ipu3"
+    ];
+
+    # This is
+    # https://copr-dist-git.fedorainfracloud.org/cgit/jwrdegoede/ipu6-softisp/libcamera.git/plain/libcamera-0.2.0-softisp.patch?h=f39&id=60e6b3d5e366a360a75942073dc0d642e4900982,
+    # but manually piped to git and back, as some renames were not processed properly.
+    # It was later refreshed with https://patchwork.libcamera.org/cover/19663/
+    patches = old.patches or [ ] ++ [
+      ./libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch
+      ./libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch
+      ./libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch
+      ./libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch
+      ./libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch
+      ./libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch
+      ./libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch
+      ./libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch
+      ./libcamera/0009-libcamera-ipa-add-Soft-IPA.patch
+      ./libcamera/0010-libcamera-introduce-SoftwareIsp.patch
+      ./libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
+      ./libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
+      ./libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
+      ./libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
+      ./libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch
+      ./libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
+      ./libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
+      ./libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch
+      ./libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch
+      ./libcamera/0020-ov01a1s-HACK.patch
+      ./libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
+    ];
+  });
+
+  # use patched libcamera
+  pipewire' = (pkgs.pipewire.override {
+    inherit libcamera;
+  });
+
+  wireplumber' = (pkgs.wireplumber.override {
+    pipewire = pipewire';
+  });
+in
+{
+  boot.kernelPatches = [{
+    name = "linux-kernel-test.patch";
+    patch = ./kernel/softisp.patch;
+    extraStructuredConfig = {
+      # needed for /dev/dma_heap
+      DMABUF_HEAPS_CMA = lib.kernel.yes;
+      DMABUF_HEAPS_SYSTEM = lib.kernel.yes;
+      DMABUF_HEAPS = lib.kernel.yes;
+    };
+  }];
+
+
+  services.udev.extraRules = ''
+    KERNEL=="system", SUBSYSTEM=="dma_heap", TAG+="uaccess"
+  '';
+
+  # provide qcam in $PATH.
+  environment.systemPackages = [
+    (libcamera.override {
+      withQcam = true;
+    })
+  ];
+
+  services.pipewire.package = pipewire';
+  services.pipewire.wireplumber.package = wireplumber';
+}
diff --git a/users/flokli/ipu6-softisp/default.nix b/users/flokli/ipu6-softisp/default.nix
new file mode 100644
index 0000000000..1f603dbb42
--- /dev/null
+++ b/users/flokli/ipu6-softisp/default.nix
@@ -0,0 +1,57 @@
+# This file ensures the fixes from ./config.nix build with the version of
+# nixpkgs from depot.
+# If you're an outside user of this, import config.nix as a NixOS module (and
+# check the README.md file).
+
+{ depot
+, pkgs
+, ...
+}:
+
+let
+  systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
+in
+depot.nix.readTree.drvTargets rec {
+  testSystem = systemFor ({ modulesPath, pkgs, ... }: {
+    imports = [
+      # Import the module, this is something a user would do in their config.
+      ./config.nix
+    ];
+
+    # Make sure we use the linuxPackages_latest.
+    boot.kernelPackages = pkgs.linuxPackages_latest;
+
+    # Enable firmware.
+    hardware.enableAllFirmware = true;
+
+    # Set some options necessary to evaluate.
+    boot.loader.systemd-boot.enable = true;
+    fileSystems."/" = {
+      device = "/dev/disk/by-partlabel/root";
+      fsType = "xfs";
+    };
+
+    # Enable pipewire and wireplumber.
+    services.pipewire = {
+      enable = true;
+      wireplumber.enable = true;
+    };
+
+    # Shut off the warning.
+    system.stateVersion = "24.05";
+  });
+
+  # Make sure the firmware requested by the driver is present in our firmware.
+  # We do have a .xz suffix here, but that's fine, since request_firmware does
+  # check ${name}.xz too in case CONFIG_FW_LOADER_COMPRESS is set.
+  # The path needs to be kept in sync with the ones used in the kernel patch.
+  checkFirmware = pkgs.runCommand "check-firmware" { } ''
+    stat ${testSystem}/firmware/intel/ipu/ipu6se_fw.bin.xz
+    stat ${testSystem}/firmware/intel/ipu/ipu6ep_fw.bin.xz
+    stat ${testSystem}/firmware/intel/ipu/ipu6_fw.bin.xz
+    stat ${testSystem}/firmware/intel/ipu/ipu6epmtl_fw.bin.xz
+
+    # all good, succeed build
+    touch $out
+  '';
+}
diff --git a/users/riking/dotfiles/regolith/flags/term-profile b/users/flokli/ipu6-softisp/kernel/.skip-tree
index e69de29bb2..e69de29bb2 100644
--- a/users/riking/dotfiles/regolith/flags/term-profile
+++ b/users/flokli/ipu6-softisp/kernel/.skip-tree
diff --git a/users/flokli/ipu6-softisp/kernel/softisp.patch b/users/flokli/ipu6-softisp/kernel/softisp.patch
new file mode 100644
index 0000000000..8731ed914c
--- /dev/null
+++ b/users/flokli/ipu6-softisp/kernel/softisp.patch
@@ -0,0 +1,18077 @@
+From 037f05a9772f3243907bb826e913971ee20e9487 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:31 +0300
+Subject: [PATCH 01/33] media: mc: Add INTERNAL pad flag
+
+Internal source pads will be used as routing endpoints in V4L2
+[GS]_ROUTING IOCTLs, to indicate that the stream begins in the entity.
+
+Also prevent creating links to pads that have been flagged as internal.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ Documentation/userspace-api/media/glossary.rst             | 6 ++++++
+ Documentation/userspace-api/media/mediactl/media-types.rst | 6 ++++++
+ drivers/media/mc/mc-entity.c                               | 6 +++++-
+ include/uapi/linux/media.h                                 | 1 +
+ 4 files changed, 18 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/userspace-api/media/glossary.rst b/Documentation/userspace-api/media/glossary.rst
+index 96a360edbf3b..f7b99a4527c7 100644
+--- a/Documentation/userspace-api/media/glossary.rst
++++ b/Documentation/userspace-api/media/glossary.rst
+@@ -173,6 +173,12 @@ Glossary
+ 	An integrated circuit that integrates all components of a computer
+ 	or other electronic systems.
+ 
++_media-glossary-stream:
++    Stream
++	A distinct flow of data (image data or metadata) over a media pipeline
++	from source to sink. A source may be e.g. an image sensor and a sink
++	e.g. a memory buffer.
++
+     V4L2 API
+ 	**V4L2 userspace API**
+ 
+diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst
+index 0ffeece1e0c8..28941da27790 100644
+--- a/Documentation/userspace-api/media/mediactl/media-types.rst
++++ b/Documentation/userspace-api/media/mediactl/media-types.rst
+@@ -361,6 +361,7 @@ Types and flags used to represent the media graph elements
+ .. _MEDIA-PAD-FL-SINK:
+ .. _MEDIA-PAD-FL-SOURCE:
+ .. _MEDIA-PAD-FL-MUST-CONNECT:
++.. _MEDIA-PAD-FL-INTERNAL:
+ 
+ .. flat-table:: Media pad flags
+     :header-rows:  0
+@@ -382,6 +383,11 @@ Types and flags used to represent the media graph elements
+ 	  when this flag isn't set; the absence of the flag doesn't imply
+ 	  there is none.
+ 
++    *  -  ``MEDIA_PAD_FL_INTERNAL``
++       -  The internal flag indicates an internal pad that has no external
++	  connections. Such a pad shall not be connected with a link. The
++	  internal flag indicates that the :ref:``stream
++	  <media-glossary-stream>`` either starts or ends in the entity.
+ 
+ One and only one of ``MEDIA_PAD_FL_SINK`` and ``MEDIA_PAD_FL_SOURCE``
+ must be set for every pad.
+diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
+index 543a392f8635..1fc80fd3e5e3 100644
+--- a/drivers/media/mc/mc-entity.c
++++ b/drivers/media/mc/mc-entity.c
+@@ -1075,7 +1075,8 @@ int media_get_pad_index(struct media_entity *entity, u32 pad_type,
+ 
+ 	for (i = 0; i < entity->num_pads; i++) {
+ 		if ((entity->pads[i].flags &
+-		     (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type)
++		     (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE |
++		      MEDIA_PAD_FL_INTERNAL)) != pad_type)
+ 			continue;
+ 
+ 		if (entity->pads[i].sig_type == sig_type)
+@@ -1098,6 +1099,9 @@ media_create_pad_link(struct media_entity *source, u16 source_pad,
+ 		return -EINVAL;
+ 	if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE)))
+ 		return -EINVAL;
++	if (WARN_ON(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE &&
++		    source->pads[source_pad].flags & MEDIA_PAD_FL_INTERNAL))
++		return -EINVAL;
+ 	if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK)))
+ 		return -EINVAL;
+ 
+diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
+index 1c80b1d6bbaf..80cfd12a43fc 100644
+--- a/include/uapi/linux/media.h
++++ b/include/uapi/linux/media.h
+@@ -208,6 +208,7 @@ struct media_entity_desc {
+ #define MEDIA_PAD_FL_SINK			(1U << 0)
+ #define MEDIA_PAD_FL_SOURCE			(1U << 1)
+ #define MEDIA_PAD_FL_MUST_CONNECT		(1U << 2)
++#define MEDIA_PAD_FL_INTERNAL			(1U << 3)
+ 
+ struct media_pad_desc {
+ 	__u32 entity;		/* entity ID */
+-- 
+2.43.2
+
+
+From 5f0cdae874f1c0237936c2c12a9fc019b93de4c9 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:32 +0300
+Subject: [PATCH 02/33] media: uapi: Add generic serial metadata mbus formats
+
+Add generic serial metadata mbus formats. These formats describe data
+width and packing but not the content itself. The reason for specifying
+such formats is that the formats as such are fairly device specific but
+they are still handled by CSI-2 receiver drivers that should not be aware
+of device specific formats. What makes generic metadata formats possible
+is that these formats are parsed by software only, after capturing the
+data to system memory.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ .../media/v4l/subdev-formats.rst              | 257 ++++++++++++++++++
+ include/uapi/linux/media-bus-format.h         |   9 +
+ 2 files changed, 266 insertions(+)
+
+diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst
+index eb3cd20b0cf2..7d107873cddd 100644
+--- a/Documentation/userspace-api/media/v4l/subdev-formats.rst
++++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst
+@@ -8306,3 +8306,260 @@ The following table lists the existing metadata formats.
+ 	both sides of the link and the bus format is a fixed
+ 	metadata format that is not configurable from userspace.
+ 	Width and height will be set to 0 for this format.
++
++Generic Serial Metadata Formats
++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++Generic serial metadata formats are used on serial busses where the actual data
++content is more or less device specific but the data is transmitted and received
++by multiple devices that do not process the data in any way, simply writing
++it to system memory for processing in software at the end of the pipeline.
++
++The more specific variant describing the actual data is used on the internal
++source pad of the originating sub-device.
++
++"b" in an array cell signifies a byte of data, followed by the number of byte
++and finally the bit number in subscript. "p" indicates a padding bit.
++
++.. _media-bus-format-generic-meta:
++
++.. cssclass: longtable
++
++.. flat-table:: Generic Serial Metadata Formats
++    :header-rows:  2
++    :stub-columns: 0
++
++    * - Identifier
++      - Code
++      -
++      - :cspan:`23` Data organization
++    * -
++      -
++      - Bit
++      - 23
++      - 22
++      - 21
++      - 20
++      - 19
++      - 18
++      - 17
++      - 16
++      - 15
++      - 14
++      - 13
++      - 12
++      - 11
++      - 10
++      - 9
++      - 8
++      - 7
++      - 6
++      - 5
++      - 4
++      - 3
++      - 2
++      - 1
++      - 0
++    * .. _MEDIA-BUS-FMT-META-8:
++
++      - MEDIA_BUS_FMT_META_8
++      - 0x8001
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++    * .. _MEDIA-BUS-FMT-META-10:
++
++      - MEDIA_BUS_FMT_META_10
++      - 0x8002
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-12:
++
++      - MEDIA_BUS_FMT_META_12
++      - 0x8003
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-14:
++
++      - MEDIA_BUS_FMT_META_14
++      - 0x8004
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-16:
++
++      - MEDIA_BUS_FMT_META_16
++      - 0x8005
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-20:
++
++      - MEDIA_BUS_FMT_META_20
++      - 0x8007
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-24:
++
++      - MEDIA_BUS_FMT_META_24
++      - 0x8009
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
+diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
+index f05f747e444d..d4c1d991014b 100644
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -174,4 +174,13 @@
+  */
+ #define MEDIA_BUS_FMT_METADATA_FIXED		0x7001
+ 
++/* Generic line based metadata formats for serial buses. Next is 0x8008. */
++#define MEDIA_BUS_FMT_META_8			0x8001
++#define MEDIA_BUS_FMT_META_10			0x8002
++#define MEDIA_BUS_FMT_META_12			0x8003
++#define MEDIA_BUS_FMT_META_14			0x8004
++#define MEDIA_BUS_FMT_META_16			0x8005
++#define MEDIA_BUS_FMT_META_20			0x8006
++#define MEDIA_BUS_FMT_META_24			0x8007
++
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
+-- 
+2.43.2
+
+
+From 8af4eeaee34159605ec86b57fa638a82fd968f31 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:33 +0300
+Subject: [PATCH 03/33] media: uapi: Document which mbus format fields are
+ valid for metadata
+
+Now that metadata mbus formats have been added, it is necessary to define
+which fields in struct v4l2_mbus_format are applicable to them (not many).
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ include/uapi/linux/v4l2-mediabus.h | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h
+index 6b07b73473b5..3cadb3b58b85 100644
+--- a/include/uapi/linux/v4l2-mediabus.h
++++ b/include/uapi/linux/v4l2-mediabus.h
+@@ -19,12 +19,18 @@
+  * @width:	image width
+  * @height:	image height
+  * @code:	data format code (from enum v4l2_mbus_pixelcode)
+- * @field:	used interlacing type (from enum v4l2_field)
+- * @colorspace:	colorspace of the data (from enum v4l2_colorspace)
+- * @ycbcr_enc:	YCbCr encoding of the data (from enum v4l2_ycbcr_encoding)
+- * @hsv_enc:	HSV encoding of the data (from enum v4l2_hsv_encoding)
+- * @quantization: quantization of the data (from enum v4l2_quantization)
+- * @xfer_func:  transfer function of the data (from enum v4l2_xfer_func)
++ * @field:	used interlacing type (from enum v4l2_field), not applicable
++ *		to metadata mbus codes
++ * @colorspace:	colorspace of the data (from enum v4l2_colorspace), zero on
++ *		metadata mbus codes
++ * @ycbcr_enc:	YCbCr encoding of the data (from enum v4l2_ycbcr_encoding), zero
++ *		on metadata mbus codes
++ * @hsv_enc:	HSV encoding of the data (from enum v4l2_hsv_encoding), zero on
++ *		metadata mbus codes
++ * @quantization: quantization of the data (from enum v4l2_quantization), zero
++ *		on metadata mbus codes
++ * @xfer_func:  transfer function of the data (from enum v4l2_xfer_func), zero
++ *		on metadata mbus codes
+  * @flags:	flags (V4L2_MBUS_FRAMEFMT_*)
+  * @reserved:  reserved bytes that can be later used
+  */
+-- 
+2.43.2
+
+
+From ed5884d40def9adfa77841427e52733746158a77 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:34 +0300
+Subject: [PATCH 04/33] media: uapi: Add a macro to tell whether an mbus code
+ is metadata
+
+Add a macro to tell whether a given mbus code is metadata.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ include/uapi/linux/media-bus-format.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
+index d4c1d991014b..6fcd7d276bc6 100644
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -183,4 +183,7 @@
+ #define MEDIA_BUS_FMT_META_20			0x8006
+ #define MEDIA_BUS_FMT_META_24			0x8007
+ 
++#define MEDIA_BUS_FMT_IS_META(code)		\
++	((code) & 0xf000 == 0x7000 || (code) & 0xf000 == 0x8000)
++
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
+-- 
+2.43.2
+
+
+From c0e682a815c5baf012c8963968385c197e7e0943 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:35 +0300
+Subject: [PATCH 05/33] media: uapi: Add generic 8-bit metadata format
+ definitions
+
+Generic 8-bit metadata formats define the in-memory data layout but not
+the format of the data itself. The reasoning for having such formats is to
+allow CSI-2 receiver drivers to receive and DMA drivers to write the data
+to memory without knowing a large number of device specific formats.
+
+These formats may be used only in conjunction of a Media controller
+pipeline where the internal pad of the source sub-device defines the
+specific format of the data (using an mbus code).
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ .../userspace-api/media/v4l/meta-formats.rst  |   1 +
+ .../media/v4l/metafmt-generic.rst             | 331 ++++++++++++++++++
+ drivers/media/v4l2-core/v4l2-ioctl.c          |   8 +
+ include/uapi/linux/videodev2.h                |   9 +
+ 4 files changed, 349 insertions(+)
+ create mode 100644 Documentation/userspace-api/media/v4l/metafmt-generic.rst
+
+diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst
+index 0bb61fc5bc00..919f595576b9 100644
+--- a/Documentation/userspace-api/media/v4l/meta-formats.rst
++++ b/Documentation/userspace-api/media/v4l/meta-formats.rst
+@@ -19,3 +19,4 @@ These formats are used for the :ref:`metadata` interface only.
+     metafmt-vsp1-hgo
+     metafmt-vsp1-hgt
+     metafmt-vivid
++    metafmt-generic
+diff --git a/Documentation/userspace-api/media/v4l/metafmt-generic.rst b/Documentation/userspace-api/media/v4l/metafmt-generic.rst
+new file mode 100644
+index 000000000000..a27bfc721edf
+--- /dev/null
++++ b/Documentation/userspace-api/media/v4l/metafmt-generic.rst
+@@ -0,0 +1,331 @@
++.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
++
++**************************************************************************************************************************************************************************************************************************************************************************************************************************
++V4L2_META_FMT_GENERIC_8 ('MET8'), V4L2_META_FMT_GENERIC_CSI2_10 ('MC1A'), V4L2_META_FMT_GENERIC_CSI2_12 ('MC1C'), V4L2_META_FMT_GENERIC_CSI2_14 ('MC1E'), V4L2_META_FMT_GENERIC_CSI2_16 ('MC1G'), V4L2_META_FMT_GENERIC_CSI2_20 ('MC1K'), V4L2_META_FMT_GENERIC_CSI2_24 ('MC1O'), V4L2_META_FMT_GENERIC_CSI2_2_24 ('MC2O')
++**************************************************************************************************************************************************************************************************************************************************************************************************************************
++
++
++Generic line-based metadata formats
++
++
++Description
++===========
++
++These generic line-based metadata formats define the memory layout of the data
++without defining the format or meaning of the metadata itself. These formats may
++only be used with a Media controller pipeline where the more specific format is
++defined in an :ref:`internal source pad <MEDIA-PAD-FL-INTERNAL>` of the source
++sub-device. See also :ref:`source routes <v4l2-subdev-source-routes>`.
++
++.. _v4l2-meta-fmt-generic-8:
++
++V4L2_META_FMT_GENERIC_8
++-----------------------
++
++The V4L2_META_FMT_GENERIC_8 format is a plain 8-bit metadata format.
++
++This format is also used on CSI-2 on both 8 bits per sample as well as on
++16 bits per sample when two bytes of metadata are packed into one sample.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_8.**
++Each cell is one byte. "M" denotes a byte of metadata.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++    * - start + 4:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++
++.. _v4l2-meta-fmt-generic-csi2-10:
++
++V4L2_META_FMT_GENERIC_CSI2_10
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_10 contains packed 8-bit generic metadata, 10 bits
++for each 8 bits of data. Every four bytes of metadata is followed by a single
++byte of padding. The way the data is stored follows the CSI-2 specification.
++
++This format is also used on CSI-2 on 20 bits per sample format that packs two
++bytes of metadata into one sample.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_10.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++      - p
++    * - start + 5:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-12:
++
++V4L2_META_FMT_GENERIC_CSI2_12
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_12 contains packed 8-bit generic metadata, 12 bits
++for each 8 bits of data. Every four bytes of metadata is followed by two bytes
++of padding. The way the data is stored follows the CSI-2 specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_12.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++      - p
++      - p
++    * - start + 6:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++      - p
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-14:
++
++V4L2_META_FMT_GENERIC_CSI2_14
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_14 contains packed 8-bit generic metadata, 14 bits
++for each 8 bits of data. Every four bytes of metadata is followed by three
++bytes of padding. The way the data is stored follows the CSI-2 specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_14.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++      - p
++      - p
++      - p
++    * - start + 7:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++      - p
++      - p
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-16:
++
++V4L2_META_FMT_GENERIC_CSI2_16
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_16 contains packed 8-bit generic metadata, 16 bits
++for each 8 bits of data. Every byte of metadata is followed by one byte of
++padding. The way the data is stored follows the CSI-2 specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_16.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - p
++      - M\ :sub:`10`
++      - p
++      - M\ :sub:`20`
++      - p
++      - M\ :sub:`30`
++      - p
++    * - start + 8:
++      - M\ :sub:`01`
++      - p
++      - M\ :sub:`11`
++      - p
++      - M\ :sub:`21`
++      - p
++      - M\ :sub:`31`
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-20:
++
++V4L2_META_FMT_GENERIC_CSI2_20
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_20 contains packed 8-bit generic metadata, 20 bits
++for each 8 bits of data. Every byte of metadata is followed by alternating one
++and two bytes of padding. The way the data is stored follows the CSI-2
++specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_20.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - p
++      - M\ :sub:`10`
++      - p
++      - p
++      - M\ :sub:`20`
++      - p
++      - M\ :sub:`30`
++      - p
++      - p
++    * - start + 10:
++      - M\ :sub:`01`
++      - p
++      - M\ :sub:`11`
++      - p
++      - p
++      - M\ :sub:`21`
++      - p
++      - M\ :sub:`31`
++      - p
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-24:
++
++V4L2_META_FMT_GENERIC_CSI2_24
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_24 contains packed 8-bit generic metadata, 24 bits
++for each 8 bits of data. Every byte of metadata is followed by two bytes of
++padding. The way the data is stored follows the CSI-2 specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_24.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - p
++      - p
++      - M\ :sub:`10`
++      - p
++      - p
++      - M\ :sub:`20`
++      - p
++      - p
++      - M\ :sub:`30`
++      - p
++      - p
++    * - start + 12:
++      - M\ :sub:`01`
++      - p
++      - p
++      - M\ :sub:`11`
++      - p
++      - p
++      - M\ :sub:`21`
++      - p
++      - p
++      - M\ :sub:`31`
++      - p
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-2-24:
++
++V4L2_META_FMT_GENERIC_CSI2_2_24
++-------------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_2_24 contains packed 8-bit generic metadata, 24 bits
++for each two times 8 bits of data. Every two bytes of metadata are followed by
++one byte of padding. The way the data is stored follows the CSI-2
++specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_2_24.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - p
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++      - p
++    * - start + 6:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - p
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++      - p
++
+diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
+index 33076af4dfdb..4eb3db1773e1 100644
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1452,6 +1452,14 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
+ 	case V4L2_PIX_FMT_Y210:		descr = "10-bit YUYV Packed"; break;
+ 	case V4L2_PIX_FMT_Y212:		descr = "12-bit YUYV Packed"; break;
+ 	case V4L2_PIX_FMT_Y216:		descr = "16-bit YUYV Packed"; break;
++	case V4L2_META_FMT_GENERIC_8:	descr = "8-bit Generic Metadata"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_10:	descr = "8b Generic Meta, 10b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_12:	descr = "8b Generic Meta, 12b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_14:	descr = "8b Generic Meta, 14b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_16:	descr = "8b Generic Meta, 16b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_20:	descr = "8b Generic Meta, 20b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_24:	descr = "8b Generic Meta, 24b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_2_24:	descr = "2x8b Generic Meta, 24b CSI-2"; break;
+ 
+ 	default:
+ 		/* Compressed formats */
+diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
+index 68e7ac178cc2..2c4e03d47789 100644
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -839,6 +839,15 @@ struct v4l2_pix_format {
+ #define V4L2_META_FMT_RK_ISP1_PARAMS	v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */
+ #define V4L2_META_FMT_RK_ISP1_STAT_3A	v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */
+ 
++#define V4L2_META_FMT_GENERIC_8		v4l2_fourcc('M', 'E', 'T', '8') /* Generic 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_10	v4l2_fourcc('M', 'C', '1', 'A') /* 10-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_12	v4l2_fourcc('M', 'C', '1', 'C') /* 12-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_14	v4l2_fourcc('M', 'C', '1', 'E') /* 14-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_16	v4l2_fourcc('M', 'C', '1', 'G') /* 16-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_20	v4l2_fourcc('M', 'C', '1', 'K') /* 20-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_24	v4l2_fourcc('M', 'C', '1', 'O') /* 24-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_2_24	v4l2_fourcc('M', 'C', '2', 'O') /* 2 bytes of 8-bit metadata, 24-bit CSI-2 packed */
++
+ /* priv field value to indicates that subsequent fields are valid. */
+ #define V4L2_PIX_FMT_PRIV_MAGIC		0xfeedcafe
+ 
+-- 
+2.43.2
+
+
+From 453627c23062ff0aa01e0e46e3b7922ddf82f998 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:36 +0300
+Subject: [PATCH 06/33] media: v4l: Support line-based metadata capture
+
+many camera sensors, among other devices, transmit embedded data and image
+data for each CSI-2 frame. This embedded data typically contains register
+configuration of the sensor that has been used to capture the image data
+of the same frame.
+
+The embedded data is received by the CSI-2 receiver and has the same
+properties as the image data, including that it is line based: it has
+width, height and bytesperline (stride).
+
+Add these fields to struct v4l2_meta_format and document them.
+
+Also add V4L2_FMT_FLAG_META_LINE_BASED to tell a given format is
+line-based i.e. these fields of struct v4l2_meta_format are valid for it.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ .../userspace-api/media/v4l/dev-meta.rst          | 15 +++++++++++++++
+ .../userspace-api/media/v4l/vidioc-enum-fmt.rst   |  7 +++++++
+ .../media/videodev2.h.rst.exceptions              |  1 +
+ drivers/media/v4l2-core/v4l2-ioctl.c              |  5 +++--
+ include/uapi/linux/videodev2.h                    | 10 ++++++++++
+ 5 files changed, 36 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/userspace-api/media/v4l/dev-meta.rst b/Documentation/userspace-api/media/v4l/dev-meta.rst
+index 0e7e1ee1471a..4b24bae6e171 100644
+--- a/Documentation/userspace-api/media/v4l/dev-meta.rst
++++ b/Documentation/userspace-api/media/v4l/dev-meta.rst
+@@ -65,3 +65,18 @@ to 0.
+       - ``buffersize``
+       - Maximum buffer size in bytes required for data. The value is set by the
+         driver.
++    * - __u32
++      - ``width``
++      - Width of a line of metadata in samples. Valid when :c:type`v4l2_fmtdesc`
++	flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See
++	:c:func:`VIDIOC_ENUM_FMT`.
++    * - __u32
++      - ``height``
++      - Number of rows of metadata. Valid when :c:type`v4l2_fmtdesc` flag
++	``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See
++	:c:func:`VIDIOC_ENUM_FMT`.
++    * - __u32
++      - ``bytesperline``
++      - Offset in bytes between the beginning of two consecutive lines. Valid
++	when :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is
++	set, otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`.
+diff --git a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst
+index 000c154b0f98..6d7664345a4e 100644
+--- a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst
++++ b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst
+@@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently:
+ 	The application can ask to configure the quantization of the capture
+ 	device when calling the :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl with
+ 	:ref:`V4L2_PIX_FMT_FLAG_SET_CSC <v4l2-pix-fmt-flag-set-csc>` set.
++    * - ``V4L2_FMT_FLAG_META_LINE_BASED``
++      - 0x0200
++      - The metadata format is line-based. In this case the ``width``,
++	``height`` and ``bytesperline`` fields of :c:type:`v4l2_meta_format` are
++	valid. The buffer consists of ``height`` lines, each having ``width``
++	bytes of data and offset between the beginning of each two consecutive
++	lines is ``bytesperline``.
+ 
+ Return Value
+ ============
+diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+index 3e58aac4ef0b..bdc628e8c1d6 100644
+--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
++++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+@@ -215,6 +215,7 @@ replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags
+ replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags
+ replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags
+ replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags
++replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags
+ 
+ # V4L2 timecode types
+ replace define V4L2_TC_TYPE_24FPS timecode-type
+diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
+index 4eb3db1773e1..1fcec1515bcc 100644
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -343,8 +343,9 @@ static void v4l_print_format(const void *arg, bool write_only)
+ 	case V4L2_BUF_TYPE_META_OUTPUT:
+ 		meta = &p->fmt.meta;
+ 		pixelformat = meta->dataformat;
+-		pr_cont(", dataformat=%p4cc, buffersize=%u\n",
+-			&pixelformat, meta->buffersize);
++		pr_cont(", dataformat=%p4cc, buffersize=%u, width=%u, height=%u, bytesperline=%u\n",
++			&pixelformat, meta->buffersize, meta->width,
++			meta->height, meta->bytesperline);
+ 		break;
+ 	}
+ }
+diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
+index 2c4e03d47789..48fb44772098 100644
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -878,6 +878,7 @@ struct v4l2_fmtdesc {
+ #define V4L2_FMT_FLAG_CSC_YCBCR_ENC		0x0080
+ #define V4L2_FMT_FLAG_CSC_HSV_ENC		V4L2_FMT_FLAG_CSC_YCBCR_ENC
+ #define V4L2_FMT_FLAG_CSC_QUANTIZATION		0x0100
++#define V4L2_FMT_FLAG_META_LINE_BASED		0x0200
+ 
+ 	/* Frame Size and frame rate enumeration */
+ /*
+@@ -2424,10 +2425,19 @@ struct v4l2_sdr_format {
+  * struct v4l2_meta_format - metadata format definition
+  * @dataformat:		little endian four character code (fourcc)
+  * @buffersize:		maximum size in bytes required for data
++ * @width:		number of bytes of data per line (valid for line based
++ *			formats only, see format documentation)
++ * @height:		number of lines of data per buffer (valid for line based
++ *			formats only)
++ * @bytesperline:	offset between the beginnings of two adjacent lines in
++ *			bytes (valid for line based formats only)
+  */
+ struct v4l2_meta_format {
+ 	__u32				dataformat;
+ 	__u32				buffersize;
++	__u32				width;
++	__u32				height;
++	__u32				bytesperline;
+ } __attribute__ ((packed));
+ 
+ /**
+-- 
+2.43.2
+
+
+From 090bb3894bda739ff06e8a431815210b731056b7 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:37 +0300
+Subject: [PATCH 07/33] media: Add media bus codes for MIPI CCS embedded data
+
+Add new MIPI CCS embedded data media bus formats.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ .../media/v4l/subdev-formats.rst              | 32 +++++++++++++++++++
+ include/uapi/linux/media-bus-format.h         | 10 +++++-
+ 2 files changed, 41 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst
+index 7d107873cddd..7afb057a09c5 100644
+--- a/Documentation/userspace-api/media/v4l/subdev-formats.rst
++++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst
+@@ -8563,3 +8563,35 @@ and finally the bit number in subscript. "p" indicates a padding bit.
+       - p
+       - p
+       - p
++
++MIPI CCS Embedded Data Formats
++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++`MIPI CCS <https://www.mipi.org/specifications/camera-command-set>`_ defines an
++metadata format for sensor embedded data, which is used to store the register
++configuration used for capturing a given frame. The format is defined in the CCS
++specification.
++
++The bit depth of the CCS embedded data matches the pixel data bit depth
++configured on the sensor. The formats used and their corresponding generic
++formats are listed in the table below.
++
++.. flat-table: CCS embedded data mbus formats and corresponding generic formats
++    :header-rows: 1
++
++    * - CCS embedded data mbus format
++      - Generic metadata format
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_8
++      - MEDIA_BUS_FMT_META_8
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_10
++      - MEDIA_BUS_FMT_META_10
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_12
++      - MEDIA_BUS_FMT_META_12
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_14
++      - MEDIA_BUS_FMT_META_14
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_16
++      - MEDIA_BUS_FMT_META_16
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_20
++      - MEDIA_BUS_FMT_META_20
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_24
++      - MEDIA_BUS_FMT_META_24
+diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
+index 6fcd7d276bc6..d5014ef3c43a 100644
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -183,7 +183,15 @@
+ #define MEDIA_BUS_FMT_META_20			0x8006
+ #define MEDIA_BUS_FMT_META_24			0x8007
+ 
++/* Specific metadata formats. Next is 0x9008. */
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_8		0x9001
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_10		0x9002
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_12		0x9003
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_14		0x9004
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_16		0x9005
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_20		0x9006
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_24		0x9007
++
+ #define MEDIA_BUS_FMT_IS_META(code)		\
+ 	((code) & 0xf000 == 0x7000 || (code) & 0xf000 == 0x8000)
+-
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
+-- 
+2.43.2
+
+
+From 44f8084f055969874d2216ba4e6e225046931e73 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:15 +0800
+Subject: [PATCH 08/33] media: intel/ipu6: add Intel IPU6 PCI device driver
+
+Intel Image Processing Unit 6th Gen includes input and processing systems
+but the hardware presents itself as a single PCI device in system.
+
+IPU6 PCI device driver basically does PCI configurations and load
+the firmware binary, initialises IPU virtual bus, and sets up platform
+specific variants to support multiple IPU6 devices in single device
+driver.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ .../media/pci/intel/ipu6/ipu6-platform-regs.h | 179 ++++
+ drivers/media/pci/intel/ipu6/ipu6.c           | 966 ++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6.h           | 356 +++++++
+ 3 files changed, 1501 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-platform-regs.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h
+new file mode 100644
+index 000000000000..cae26009c9fc
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h
+@@ -0,0 +1,179 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2018 - 2023 Intel Corporation */
++
++#ifndef IPU6_PLATFORM_REGS_H
++#define IPU6_PLATFORM_REGS_H
++
++#include <linux/bits.h>
++
++/*
++ * IPU6 uses uniform address within IPU6, therefore all subsystem registers
++ * locates in one single space starts from 0 but in different sctions with
++ * different addresses, the subsystem offsets are defined to 0 as the
++ * register definition will have the address offset to 0.
++ */
++#define IPU6_UNIFIED_OFFSET			0
++
++#define IPU6_ISYS_IOMMU0_OFFSET		0x2e0000
++#define IPU6_ISYS_IOMMU1_OFFSET		0x2e0500
++#define IPU6_ISYS_IOMMUI_OFFSET		0x2e0a00
++
++#define IPU6_PSYS_IOMMU0_OFFSET		0x1b0000
++#define IPU6_PSYS_IOMMU1_OFFSET		0x1b0700
++#define IPU6_PSYS_IOMMU1R_OFFSET	0x1b0e00
++#define IPU6_PSYS_IOMMUI_OFFSET		0x1b1500
++
++/* the offset from IOMMU base register */
++#define IPU6_MMU_L1_STREAM_ID_REG_OFFSET	0x0c
++#define IPU6_MMU_L2_STREAM_ID_REG_OFFSET	0x4c
++#define IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET	0x8c
++
++#define IPU6_MMU_INFO_OFFSET		0x8
++
++#define IPU6_ISYS_SPC_OFFSET		0x210000
++
++#define IPU6SE_PSYS_SPC_OFFSET		0x110000
++#define IPU6_PSYS_SPC_OFFSET		0x118000
++
++#define IPU6_ISYS_DMEM_OFFSET		0x200000
++#define IPU6_PSYS_DMEM_OFFSET		0x100000
++
++#define IPU6_REG_ISYS_UNISPART_IRQ_EDGE			0x27c000
++#define IPU6_REG_ISYS_UNISPART_IRQ_MASK			0x27c004
++#define IPU6_REG_ISYS_UNISPART_IRQ_STATUS		0x27c008
++#define IPU6_REG_ISYS_UNISPART_IRQ_CLEAR		0x27c00c
++#define IPU6_REG_ISYS_UNISPART_IRQ_ENABLE		0x27c010
++#define IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE	0x27c014
++#define IPU6_REG_ISYS_UNISPART_SW_IRQ_REG		0x27c414
++#define IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG		0x27c418
++#define IPU6_ISYS_UNISPART_IRQ_CSI0			BIT(2)
++#define IPU6_ISYS_UNISPART_IRQ_CSI1			BIT(3)
++#define IPU6_ISYS_UNISPART_IRQ_SW			BIT(22)
++
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_EDGE			0x2b0200
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_MASK			0x2b0204
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_STATUS		0x2b0208
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_CLEAR			0x2b020c
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_ENABLE		0x2b0210
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_LEVEL_NOT_PULSE	0x2b0214
++
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_EDGE			0x2d2100
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_MASK			0x2d2104
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_STATUS		0x2d2108
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_CLEAR		0x2d210c
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_ENABLE		0x2d2110
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_LEVEL_NOT_PULSE	0x2d2114
++
++/* CDC Burst collector thresholds for isys - 3 FIFOs i = 0..2 */
++#define IPU6_REG_ISYS_CDC_THRESHOLD(i)		(0x27c400 + ((i) * 4))
++
++#define IPU6_CSI_IRQ_NUM_PER_PIPE			4
++#define IPU6SE_ISYS_CSI_PORT_NUM			4
++#define IPU6_ISYS_CSI_PORT_NUM				8
++
++#define IPU6_ISYS_CSI_PORT_IRQ(irq_num)		BIT(irq_num)
++
++/* PKG DIR OFFSET in IMR in secure mode */
++#define IPU6_PKG_DIR_IMR_OFFSET			0x40
++
++#define IPU6_ISYS_REG_SPC_STATUS_CTRL		0x0
++
++#define IPU6_ISYS_SPC_STATUS_START			BIT(1)
++#define IPU6_ISYS_SPC_STATUS_RUN			BIT(3)
++#define IPU6_ISYS_SPC_STATUS_READY			BIT(5)
++#define IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE	BIT(12)
++#define IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH		BIT(13)
++
++#define IPU6_PSYS_REG_SPC_STATUS_CTRL			0x0
++#define IPU6_PSYS_REG_SPC_START_PC			0x4
++#define IPU6_PSYS_REG_SPC_ICACHE_BASE			0x10
++#define IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER	0x14
++
++#define IPU6_PSYS_SPC_STATUS_START			BIT(1)
++#define IPU6_PSYS_SPC_STATUS_RUN			BIT(3)
++#define IPU6_PSYS_SPC_STATUS_READY			BIT(5)
++#define IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE	BIT(12)
++#define IPU6_PSYS_SPC_STATUS_ICACHE_PREFETCH		BIT(13)
++
++#define IPU6_PSYS_REG_SPP0_STATUS_CTRL			0x20000
++
++#define IPU6_INFO_ENABLE_SNOOP			BIT(0)
++#define IPU6_INFO_DEC_FORCE_FLUSH		BIT(1)
++#define IPU6_INFO_DEC_PASS_THROUGH		BIT(2)
++#define IPU6_INFO_ZLW				BIT(3)
++#define IPU6_INFO_REQUEST_DESTINATION_IOSF	BIT(9)
++#define IPU6_INFO_IMR_BASE			BIT(10)
++#define IPU6_INFO_IMR_DESTINED			BIT(11)
++
++#define IPU6_INFO_REQUEST_DESTINATION_PRIMARY IPU6_INFO_REQUEST_DESTINATION_IOSF
++
++/*
++ * s2m_pixel_soc_pixel_remapping is dedicated for the enabling of the
++ * pixel s2m remp ability.Remap here  means that s2m rearange the order
++ * of the pixels in each 4 pixels group.
++ * For examle, mirroring remping means that if input's 4 first pixels
++ * are 1 2 3 4 then in output we should see 4 3 2 1 in this 4 first pixels.
++ * 0xE4 is from s2m MAS document. It means no remapping.
++ */
++#define S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4
++/*
++ * csi_be_soc_pixel_remapping is for the enabling of the pixel remapping.
++ * This remapping is exactly like the stream2mmio remapping.
++ */
++#define CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING    0xe4
++
++#define IPU6_REG_DMA_TOP_AB_GROUP1_BASE_ADDR		0x1ae000
++#define IPU6_REG_DMA_TOP_AB_GROUP2_BASE_ADDR		0x1af000
++#define IPU6_REG_DMA_TOP_AB_RING_MIN_OFFSET(n)		(0x4 + (n) * 0xc)
++#define IPU6_REG_DMA_TOP_AB_RING_MAX_OFFSET(n)		(0x8 + (n) * 0xc)
++#define IPU6_REG_DMA_TOP_AB_RING_ACCESS_OFFSET(n)	(0xc + (n) * 0xc)
++
++enum ipu6_device_ab_group1_target_id {
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R0_SPC_DMEM,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R1_SPC_DMEM,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R2_SPC_DMEM,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R3_SPC_STATUS_REG,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R4_SPC_MASTER_BASE_ADDR,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R5_SPC_PC_STALL,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R6_SPC_EQ,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R7_SPC_RESERVED,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R8_SPC_RESERVED,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R9_SPP0,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R10_SPP1,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R11_CENTRAL_R1,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R12_IRQ,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R13_CENTRAL_R2,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R14_DMA,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R15_DMA,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R16_GP,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R17_ZLW_INSERTER,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R18_AB,
++};
++
++enum nci_ab_access_mode {
++	NCI_AB_ACCESS_MODE_RW,	/* read & write */
++	NCI_AB_ACCESS_MODE_RO,	/* read only */
++	NCI_AB_ACCESS_MODE_WO,	/* write only */
++	NCI_AB_ACCESS_MODE_NA,	/* No access at all */
++};
++
++/* IRQ-related registers in PSYS */
++#define IPU6_REG_PSYS_GPDEV_IRQ_EDGE		0x1aa200
++#define IPU6_REG_PSYS_GPDEV_IRQ_MASK		0x1aa204
++#define IPU6_REG_PSYS_GPDEV_IRQ_STATUS		0x1aa208
++#define IPU6_REG_PSYS_GPDEV_IRQ_CLEAR		0x1aa20c
++#define IPU6_REG_PSYS_GPDEV_IRQ_ENABLE		0x1aa210
++#define IPU6_REG_PSYS_GPDEV_IRQ_LEVEL_NOT_PULSE	0x1aa214
++/* There are 8 FW interrupts, n = 0..7 */
++#define IPU6_PSYS_GPDEV_FWIRQ0			5
++#define IPU6_PSYS_GPDEV_FWIRQ1			6
++#define IPU6_PSYS_GPDEV_FWIRQ2			7
++#define IPU6_PSYS_GPDEV_FWIRQ3			8
++#define IPU6_PSYS_GPDEV_FWIRQ4			9
++#define IPU6_PSYS_GPDEV_FWIRQ5			10
++#define IPU6_PSYS_GPDEV_FWIRQ6			11
++#define IPU6_PSYS_GPDEV_FWIRQ7			12
++#define IPU6_PSYS_GPDEV_IRQ_FWIRQ(n)		BIT(n)
++#define IPU6_REG_PSYS_GPDEV_FWIRQ(n)		(4 * (n) + 0x1aa100)
++
++#endif /* IPU6_PLATFORM_REGS_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c
+new file mode 100644
+index 000000000000..abd40a783729
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6.c
+@@ -0,0 +1,966 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/firmware.h>
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/pci-ats.h>
++#include <linux/pm_runtime.h>
++#include <linux/property.h>
++#include <linux/scatterlist.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include <media/ipu-bridge.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-buttress.h"
++#include "ipu6-cpd.h"
++#include "ipu6-isys.h"
++#include "ipu6-mmu.h"
++#include "ipu6-platform-buttress-regs.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++#include "ipu6-platform-regs.h"
++
++#define IPU6_PCI_BAR		0
++
++struct ipu6_cell_program {
++	u32 magic_number;
++
++	u32 blob_offset;
++	u32 blob_size;
++
++	u32 start[3];
++
++	u32 icache_source;
++	u32 icache_target;
++	u32 icache_size;
++
++	u32 pmem_source;
++	u32 pmem_target;
++	u32 pmem_size;
++
++	u32 data_source;
++	u32 data_target;
++	u32 data_size;
++
++	u32 bss_target;
++	u32 bss_size;
++
++	u32 cell_id;
++	u32 regs_addr;
++
++	u32 cell_pmem_data_bus_address;
++	u32 cell_dmem_data_bus_address;
++	u32 cell_pmem_control_bus_address;
++	u32 cell_dmem_control_bus_address;
++
++	u32 next;
++	u32 dummy[2];
++};
++
++static u32 ipu6se_csi_offsets[] = {
++	IPU6_CSI_PORT_A_ADDR_OFFSET,
++	IPU6_CSI_PORT_B_ADDR_OFFSET,
++	IPU6_CSI_PORT_C_ADDR_OFFSET,
++	IPU6_CSI_PORT_D_ADDR_OFFSET
++};
++
++/*
++ * IPU6 on TGL support maximum 8 csi2 ports
++ * IPU6SE on JSL and IPU6EP on ADL support maximum 4 csi2 ports
++ * IPU6EP on MTL support maximum 6 csi2 ports
++ */
++static u32 ipu6_tgl_csi_offsets[] = {
++	IPU6_CSI_PORT_A_ADDR_OFFSET,
++	IPU6_CSI_PORT_B_ADDR_OFFSET,
++	IPU6_CSI_PORT_C_ADDR_OFFSET,
++	IPU6_CSI_PORT_D_ADDR_OFFSET,
++	IPU6_CSI_PORT_E_ADDR_OFFSET,
++	IPU6_CSI_PORT_F_ADDR_OFFSET,
++	IPU6_CSI_PORT_G_ADDR_OFFSET,
++	IPU6_CSI_PORT_H_ADDR_OFFSET
++};
++
++static u32 ipu6ep_mtl_csi_offsets[] = {
++	IPU6_CSI_PORT_A_ADDR_OFFSET,
++	IPU6_CSI_PORT_B_ADDR_OFFSET,
++	IPU6_CSI_PORT_C_ADDR_OFFSET,
++	IPU6_CSI_PORT_D_ADDR_OFFSET,
++	IPU6_CSI_PORT_E_ADDR_OFFSET,
++	IPU6_CSI_PORT_F_ADDR_OFFSET
++};
++
++static u32 ipu6_csi_offsets[] = {
++	IPU6_CSI_PORT_A_ADDR_OFFSET,
++	IPU6_CSI_PORT_B_ADDR_OFFSET,
++	IPU6_CSI_PORT_C_ADDR_OFFSET,
++	IPU6_CSI_PORT_D_ADDR_OFFSET
++};
++
++static struct ipu6_isys_internal_pdata isys_ipdata = {
++	.hw_variant = {
++		.offset = IPU6_UNIFIED_OFFSET,
++		.nr_mmus = 3,
++		.mmu_hw = {
++			{
++				.offset = IPU6_ISYS_IOMMU0_OFFSET,
++				.info_bits = IPU6_INFO_REQUEST_DESTINATION_IOSF,
++				.nr_l1streams = 16,
++				.l1_block_sz = {
++					3, 8, 2, 2, 2, 2, 2, 2, 1, 1,
++					1, 1, 1, 1, 1, 1
++				},
++				.nr_l2streams = 16,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_ISYS_IOMMU1_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 16,
++				.l1_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 1, 1, 4
++				},
++				.nr_l2streams = 16,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_ISYS_IOMMUI_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 0,
++				.nr_l2streams = 0,
++				.insert_read_before_invalidate = false,
++			},
++		},
++		.cdc_fifos = 3,
++		.cdc_fifo_threshold = {6, 8, 2},
++		.dmem_offset = IPU6_ISYS_DMEM_OFFSET,
++		.spc_offset = IPU6_ISYS_SPC_OFFSET,
++	},
++	.isys_dma_overshoot = IPU6_ISYS_OVERALLOC_MIN,
++};
++
++static struct ipu6_psys_internal_pdata psys_ipdata = {
++	.hw_variant = {
++		.offset = IPU6_UNIFIED_OFFSET,
++		.nr_mmus = 4,
++		.mmu_hw = {
++			{
++				.offset = IPU6_PSYS_IOMMU0_OFFSET,
++				.info_bits =
++				IPU6_INFO_REQUEST_DESTINATION_IOSF,
++				.nr_l1streams = 16,
++				.l1_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.nr_l2streams = 16,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_PSYS_IOMMU1_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 32,
++				.l1_block_sz = {
++					1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 10,
++					5, 4, 14, 6, 4, 14, 6, 4, 8,
++					4, 2, 1, 1, 1, 1, 14
++				},
++				.nr_l2streams = 32,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_PSYS_IOMMU1R_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 16,
++				.l1_block_sz = {
++					1, 4, 4, 4, 4, 16, 8, 4, 32,
++					16, 16, 2, 2, 2, 1, 12
++				},
++				.nr_l2streams = 16,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_PSYS_IOMMUI_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 0,
++				.nr_l2streams = 0,
++				.insert_read_before_invalidate = false,
++			},
++		},
++		.dmem_offset = IPU6_PSYS_DMEM_OFFSET,
++	},
++};
++
++static const struct ipu6_buttress_ctrl isys_buttress_ctrl = {
++	.ratio = IPU6_IS_FREQ_CTL_DEFAULT_RATIO,
++	.qos_floor = IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO,
++	.freq_ctl = IPU6_BUTTRESS_REG_IS_FREQ_CTL,
++	.pwr_sts_shift = IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT,
++	.pwr_sts_mask = IPU6_BUTTRESS_PWR_STATE_IS_PWR_MASK,
++	.pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE,
++	.pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE,
++};
++
++static const struct ipu6_buttress_ctrl psys_buttress_ctrl = {
++	.ratio = IPU6_PS_FREQ_CTL_DEFAULT_RATIO,
++	.qos_floor = IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO,
++	.freq_ctl = IPU6_BUTTRESS_REG_PS_FREQ_CTL,
++	.pwr_sts_shift = IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT,
++	.pwr_sts_mask = IPU6_BUTTRESS_PWR_STATE_PS_PWR_MASK,
++	.pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE,
++	.pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE,
++};
++
++static void
++ipu6_pkg_dir_configure_spc(struct ipu6_device *isp,
++			   const struct ipu6_hw_variants *hw_variant,
++			   int pkg_dir_idx, void __iomem *base,
++			   u64 *pkg_dir, dma_addr_t pkg_dir_vied_address)
++{
++	struct ipu6_cell_program *prog;
++	void __iomem *spc_base;
++	u32 server_fw_addr;
++	dma_addr_t dma_addr;
++	u32 pg_offset;
++
++	server_fw_addr = lower_32_bits(*(pkg_dir + (pkg_dir_idx + 1) * 2));
++	if (pkg_dir_idx == IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX)
++		dma_addr = sg_dma_address(isp->isys->fw_sgt.sgl);
++	else
++		dma_addr = sg_dma_address(isp->psys->fw_sgt.sgl);
++
++	pg_offset = server_fw_addr - dma_addr;
++	prog = (struct ipu6_cell_program *)((u64)isp->cpd_fw->data + pg_offset);
++	spc_base = base + prog->regs_addr;
++	if (spc_base != (base + hw_variant->spc_offset))
++		dev_warn(&isp->pdev->dev,
++			 "SPC reg addr %p not matching value from CPD %p\n",
++			 base + hw_variant->spc_offset, spc_base);
++	writel(server_fw_addr + prog->blob_offset +
++	       prog->icache_source, spc_base + IPU6_PSYS_REG_SPC_ICACHE_BASE);
++	writel(IPU6_INFO_REQUEST_DESTINATION_IOSF,
++	       spc_base + IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER);
++	writel(prog->start[1], spc_base + IPU6_PSYS_REG_SPC_START_PC);
++	writel(pkg_dir_vied_address, base + hw_variant->dmem_offset);
++}
++
++void ipu6_configure_spc(struct ipu6_device *isp,
++			const struct ipu6_hw_variants *hw_variant,
++			int pkg_dir_idx, void __iomem *base, u64 *pkg_dir,
++			dma_addr_t pkg_dir_dma_addr)
++{
++	void __iomem *dmem_base = base + hw_variant->dmem_offset;
++	void __iomem *spc_regs_base = base + hw_variant->spc_offset;
++	u32 val;
++
++	val = readl(spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL);
++	val |= IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE;
++	writel(val, spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL);
++
++	if (isp->secure_mode)
++		writel(IPU6_PKG_DIR_IMR_OFFSET, dmem_base);
++	else
++		ipu6_pkg_dir_configure_spc(isp, hw_variant, pkg_dir_idx, base,
++					   pkg_dir, pkg_dir_dma_addr);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, INTEL_IPU6);
++
++static void ipu6_internal_pdata_init(struct ipu6_device *isp)
++{
++	u8 hw_ver = isp->hw_ver;
++
++	isys_ipdata.num_parallel_streams = IPU6_ISYS_NUM_STREAMS;
++	isys_ipdata.sram_gran_shift = IPU6_SRAM_GRANULARITY_SHIFT;
++	isys_ipdata.sram_gran_size = IPU6_SRAM_GRANULARITY_SIZE;
++	isys_ipdata.max_sram_size = IPU6_MAX_SRAM_SIZE;
++	isys_ipdata.sensor_type_start = IPU6_FW_ISYS_SENSOR_TYPE_START;
++	isys_ipdata.sensor_type_end = IPU6_FW_ISYS_SENSOR_TYPE_END;
++	isys_ipdata.max_streams = IPU6_ISYS_NUM_STREAMS;
++	isys_ipdata.max_send_queues = IPU6_N_MAX_SEND_QUEUES;
++	isys_ipdata.max_sram_blocks = IPU6_NOF_SRAM_BLOCKS_MAX;
++	isys_ipdata.max_devq_size = IPU6_DEV_SEND_QUEUE_SIZE;
++	isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6_csi_offsets);
++	isys_ipdata.csi2.offsets = ipu6_csi_offsets;
++	isys_ipdata.csi2.irq_mask = IPU6_CSI_RX_ERROR_IRQ_MASK;
++	isys_ipdata.csi2.ctrl0_irq_edge = IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE;
++	isys_ipdata.csi2.ctrl0_irq_clear =
++		IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR;
++	isys_ipdata.csi2.ctrl0_irq_mask = IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK;
++	isys_ipdata.csi2.ctrl0_irq_enable =
++		IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE;
++	isys_ipdata.csi2.ctrl0_irq_status =
++		IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS;
++	isys_ipdata.csi2.ctrl0_irq_lnp =
++		IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE;
++	isys_ipdata.enhanced_iwake = is_ipu6ep_mtl(hw_ver) || is_ipu6ep(hw_ver);
++	psys_ipdata.hw_variant.spc_offset = IPU6_PSYS_SPC_OFFSET;
++	isys_ipdata.csi2.fw_access_port_ofs = CSI_REG_HUB_FW_ACCESS_PORT_OFS;
++
++	if (is_ipu6ep(hw_ver)) {
++		isys_ipdata.ltr = IPU6EP_LTR_VALUE;
++		isys_ipdata.memopen_threshold = IPU6EP_MIN_MEMOPEN_TH;
++	}
++
++	if (is_ipu6_tgl(hw_ver)) {
++		isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6_tgl_csi_offsets);
++		isys_ipdata.csi2.offsets = ipu6_tgl_csi_offsets;
++	}
++
++	if (is_ipu6ep_mtl(hw_ver)) {
++		isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6ep_mtl_csi_offsets);
++		isys_ipdata.csi2.offsets = ipu6ep_mtl_csi_offsets;
++
++		isys_ipdata.csi2.ctrl0_irq_edge =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE;
++		isys_ipdata.csi2.ctrl0_irq_clear =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR;
++		isys_ipdata.csi2.ctrl0_irq_mask =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK;
++		isys_ipdata.csi2.ctrl0_irq_enable =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE;
++		isys_ipdata.csi2.ctrl0_irq_lnp =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE;
++		isys_ipdata.csi2.ctrl0_irq_status =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS;
++		isys_ipdata.csi2.fw_access_port_ofs =
++			CSI_REG_HUB_FW_ACCESS_PORT_V6OFS;
++		isys_ipdata.ltr = IPU6EP_MTL_LTR_VALUE;
++		isys_ipdata.memopen_threshold = IPU6EP_MTL_MIN_MEMOPEN_TH;
++	}
++
++	if (is_ipu6se(hw_ver)) {
++		isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6se_csi_offsets);
++		isys_ipdata.csi2.irq_mask = IPU6SE_CSI_RX_ERROR_IRQ_MASK;
++		isys_ipdata.csi2.offsets = ipu6se_csi_offsets;
++		isys_ipdata.num_parallel_streams = IPU6SE_ISYS_NUM_STREAMS;
++		isys_ipdata.sram_gran_shift = IPU6SE_SRAM_GRANULARITY_SHIFT;
++		isys_ipdata.sram_gran_size = IPU6SE_SRAM_GRANULARITY_SIZE;
++		isys_ipdata.max_sram_size = IPU6SE_MAX_SRAM_SIZE;
++		isys_ipdata.sensor_type_start =
++			IPU6SE_FW_ISYS_SENSOR_TYPE_START;
++		isys_ipdata.sensor_type_end = IPU6SE_FW_ISYS_SENSOR_TYPE_END;
++		isys_ipdata.max_streams = IPU6SE_ISYS_NUM_STREAMS;
++		isys_ipdata.max_send_queues = IPU6SE_N_MAX_SEND_QUEUES;
++		isys_ipdata.max_sram_blocks = IPU6SE_NOF_SRAM_BLOCKS_MAX;
++		isys_ipdata.max_devq_size = IPU6SE_DEV_SEND_QUEUE_SIZE;
++		psys_ipdata.hw_variant.spc_offset = IPU6SE_PSYS_SPC_OFFSET;
++	}
++}
++
++static int ipu6_isys_check_fwnode_graph(struct fwnode_handle *fwnode)
++{
++	struct fwnode_handle *endpoint;
++
++	if (IS_ERR_OR_NULL(fwnode))
++		return -EINVAL;
++
++	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
++	if (endpoint) {
++		fwnode_handle_put(endpoint);
++		return 0;
++	}
++
++	return ipu6_isys_check_fwnode_graph(fwnode->secondary);
++}
++
++static struct ipu6_bus_device *
++ipu6_isys_init(struct pci_dev *pdev, struct device *parent,
++	       struct ipu6_buttress_ctrl *ctrl, void __iomem *base,
++	       const struct ipu6_isys_internal_pdata *ipdata)
++{
++	struct device *dev = &pdev->dev;
++	struct fwnode_handle *fwnode = dev_fwnode(dev);
++	struct ipu6_bus_device *isys_adev;
++	struct ipu6_isys_pdata *pdata;
++	int ret;
++
++	/* check fwnode at first, fallback into bridge if no fwnode graph */
++	ret = ipu6_isys_check_fwnode_graph(fwnode);
++	if (ret) {
++		if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) {
++			dev_err(dev,
++				"fwnode graph has no endpoints connection\n");
++			return ERR_PTR(-EINVAL);
++		}
++
++		ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb);
++		if (ret) {
++			dev_err_probe(dev, ret, "IPU6 bridge init failed\n");
++			return ERR_PTR(ret);
++		}
++	}
++
++	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
++	if (!pdata)
++		return ERR_PTR(-ENOMEM);
++
++	pdata->base = base;
++	pdata->ipdata = ipdata;
++
++	isys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl,
++					       IPU6_ISYS_NAME);
++	if (IS_ERR(isys_adev)) {
++		dev_err_probe(dev, PTR_ERR(isys_adev),
++			      "ipu6_bus_initialize_device isys failed\n");
++		kfree(pdata);
++		return ERR_CAST(isys_adev);
++	}
++
++	isys_adev->mmu = ipu6_mmu_init(dev, base, ISYS_MMID,
++				       &ipdata->hw_variant);
++	if (IS_ERR(isys_adev->mmu)) {
++		dev_err_probe(dev, PTR_ERR(isys_adev),
++			      "ipu6_mmu_init(isys_adev->mmu) failed\n");
++		put_device(&isys_adev->auxdev.dev);
++		kfree(pdata);
++		return ERR_CAST(isys_adev->mmu);
++	}
++
++	isys_adev->mmu->dev = &isys_adev->auxdev.dev;
++
++	ret = ipu6_bus_add_device(isys_adev);
++	if (ret) {
++		kfree(pdata);
++		return ERR_PTR(ret);
++	}
++
++	return isys_adev;
++}
++
++static struct ipu6_bus_device *
++ipu6_psys_init(struct pci_dev *pdev, struct device *parent,
++	       struct ipu6_buttress_ctrl *ctrl, void __iomem *base,
++	       const struct ipu6_psys_internal_pdata *ipdata)
++{
++	struct ipu6_bus_device *psys_adev;
++	struct ipu6_psys_pdata *pdata;
++	int ret;
++
++	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
++	if (!pdata)
++		return ERR_PTR(-ENOMEM);
++
++	pdata->base = base;
++	pdata->ipdata = ipdata;
++
++	psys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl,
++					       IPU6_PSYS_NAME);
++	if (IS_ERR(psys_adev)) {
++		dev_err_probe(&pdev->dev, PTR_ERR(psys_adev),
++			      "ipu6_bus_initialize_device psys failed\n");
++		kfree(pdata);
++		return ERR_CAST(psys_adev);
++	}
++
++	psys_adev->mmu = ipu6_mmu_init(&pdev->dev, base, PSYS_MMID,
++				       &ipdata->hw_variant);
++	if (IS_ERR(psys_adev->mmu)) {
++		dev_err_probe(&pdev->dev, PTR_ERR(psys_adev),
++			      "ipu6_mmu_init(psys_adev->mmu) failed\n");
++		put_device(&psys_adev->auxdev.dev);
++		kfree(pdata);
++		return ERR_CAST(psys_adev->mmu);
++	}
++
++	psys_adev->mmu->dev = &psys_adev->auxdev.dev;
++
++	ret = ipu6_bus_add_device(psys_adev);
++	if (ret) {
++		kfree(pdata);
++		return ERR_PTR(ret);
++	}
++
++	return psys_adev;
++}
++
++static int ipu6_pci_config_setup(struct pci_dev *dev, u8 hw_ver)
++{
++	int ret;
++
++	/* disable IPU6 PCI ATS on mtl ES2 */
++	if (is_ipu6ep_mtl(hw_ver) && boot_cpu_data.x86_stepping == 0x2 &&
++	    pci_ats_supported(dev))
++		pci_disable_ats(dev);
++
++	/* No PCI msi capability for IPU6EP */
++	if (is_ipu6ep(hw_ver) || is_ipu6ep_mtl(hw_ver)) {
++		/* likely do nothing as msi not enabled by default */
++		pci_disable_msi(dev);
++		return 0;
++	}
++
++	ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_MSI);
++	if (ret < 0)
++		return dev_err_probe(&dev->dev, ret, "Request msi failed");
++
++	return 0;
++}
++
++static void ipu6_configure_vc_mechanism(struct ipu6_device *isp)
++{
++	u32 val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
++
++	if (IPU6_BTRS_ARB_STALL_MODE_VC0 == IPU6_BTRS_ARB_MODE_TYPE_STALL)
++		val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0;
++	else
++		val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0;
++
++	if (IPU6_BTRS_ARB_STALL_MODE_VC1 == IPU6_BTRS_ARB_MODE_TYPE_STALL)
++		val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1;
++	else
++		val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1;
++
++	writel(val, isp->base + BUTTRESS_REG_BTRS_CTRL);
++}
++
++static int request_cpd_fw(const struct firmware **firmware_p, const char *name,
++			  struct device *device)
++{
++	const struct firmware *fw;
++	struct firmware *dst;
++	int ret = 0;
++
++	ret = request_firmware(&fw, name, device);
++	if (ret)
++		return ret;
++
++	if (is_vmalloc_addr(fw->data)) {
++		*firmware_p = fw;
++		return 0;
++	}
++
++	dst = kzalloc(sizeof(*dst), GFP_KERNEL);
++	if (!dst) {
++		ret = -ENOMEM;
++		goto release_firmware;
++	}
++
++	dst->size = fw->size;
++	dst->data = vmalloc(fw->size);
++	if (!dst->data) {
++		kfree(dst);
++		ret = -ENOMEM;
++		goto release_firmware;
++	}
++
++	memcpy((void *)dst->data, fw->data, fw->size);
++	*firmware_p = dst;
++
++release_firmware:
++	release_firmware(fw);
++
++	return ret;
++}
++
++static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
++{
++	struct ipu6_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL;
++	struct device *dev = &pdev->dev;
++	void __iomem *isys_base = NULL;
++	void __iomem *psys_base = NULL;
++	struct ipu6_device *isp;
++	phys_addr_t phys;
++	u32 val, version, sku_id;
++	int ret;
++
++	isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
++	if (!isp)
++		return -ENOMEM;
++
++	isp->pdev = pdev;
++	INIT_LIST_HEAD(&isp->devices);
++
++	ret = pcim_enable_device(pdev);
++	if (ret)
++		return dev_err_probe(dev, ret, "Enable PCI device failed\n");
++
++	phys = pci_resource_start(pdev, IPU6_PCI_BAR);
++	dev_dbg(dev, "IPU6 PCI bar[%u] = %pa\n", IPU6_PCI_BAR, &phys);
++
++	ret = pcim_iomap_regions(pdev, 1 << IPU6_PCI_BAR, pci_name(pdev));
++	if (ret)
++		return dev_err_probe(dev, ret, "Failed to I/O mem remappinp\n");
++
++	isp->base = pcim_iomap_table(pdev)[IPU6_PCI_BAR];
++	pci_set_drvdata(pdev, isp);
++	pci_set_master(pdev);
++
++	isp->cpd_metadata_cmpnt_size = sizeof(struct ipu6_cpd_metadata_cmpnt);
++	switch (id->device) {
++	case PCI_DEVICE_ID_INTEL_IPU6:
++		isp->hw_ver = IPU6_VER_6;
++		isp->cpd_fw_name = IPU6_FIRMWARE_NAME;
++		break;
++	case PCI_DEVICE_ID_INTEL_IPU6SE:
++		isp->hw_ver = IPU6_VER_6SE;
++		isp->cpd_fw_name = IPU6SE_FIRMWARE_NAME;
++		isp->cpd_metadata_cmpnt_size =
++			sizeof(struct ipu6se_cpd_metadata_cmpnt);
++		break;
++	case PCI_DEVICE_ID_INTEL_IPU6EP_ADLP:
++	case PCI_DEVICE_ID_INTEL_IPU6EP_ADLN:
++	case PCI_DEVICE_ID_INTEL_IPU6EP_RPLP:
++		isp->hw_ver = IPU6_VER_6EP;
++		isp->cpd_fw_name = IPU6EP_FIRMWARE_NAME;
++		break;
++	case PCI_DEVICE_ID_INTEL_IPU6EP_MTL:
++		isp->hw_ver = IPU6_VER_6EP_MTL;
++		isp->cpd_fw_name = IPU6EPMTL_FIRMWARE_NAME;
++		break;
++	default:
++		return dev_err_probe(dev, -ENODEV,
++				     "Unsupported IPU6 device %x\n",
++				     id->device);
++	}
++
++	ipu6_internal_pdata_init(isp);
++
++	isys_base = isp->base + isys_ipdata.hw_variant.offset;
++	psys_base = isp->base + psys_ipdata.hw_variant.offset;
++
++	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(39));
++	if (ret)
++		return dev_err_probe(dev, ret, "Failed to set DMA mask\n");
++
++	ret = dma_set_max_seg_size(dev, UINT_MAX);
++	if (ret)
++		return dev_err_probe(dev, ret, "Failed to set max_seg_size\n");
++
++	ret = ipu6_pci_config_setup(pdev, isp->hw_ver);
++	if (ret)
++		return ret;
++
++	ret = ipu6_buttress_init(isp);
++	if (ret)
++		return ret;
++
++	ret = request_cpd_fw(&isp->cpd_fw, isp->cpd_fw_name, dev);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "Requesting signed firmware %s failed\n",
++			      isp->cpd_fw_name);
++		goto buttress_exit;
++	}
++
++	ret = ipu6_cpd_validate_cpd_file(isp, isp->cpd_fw->data,
++					 isp->cpd_fw->size);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "Failed to validate cpd\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	isys_ctrl = devm_kmemdup(dev, &isys_buttress_ctrl,
++				 sizeof(isys_buttress_ctrl), GFP_KERNEL);
++	if (!isys_ctrl) {
++		ret = -ENOMEM;
++		goto out_ipu6_bus_del_devices;
++	}
++
++	isp->isys = ipu6_isys_init(pdev, dev, isys_ctrl, isys_base,
++				   &isys_ipdata);
++	if (IS_ERR(isp->isys)) {
++		ret = PTR_ERR(isp->isys);
++		goto out_ipu6_bus_del_devices;
++	}
++
++	psys_ctrl = devm_kmemdup(dev, &psys_buttress_ctrl,
++				 sizeof(psys_buttress_ctrl), GFP_KERNEL);
++	if (!psys_ctrl) {
++		ret = -ENOMEM;
++		goto out_ipu6_bus_del_devices;
++	}
++
++	isp->psys = ipu6_psys_init(pdev, &isp->isys->auxdev.dev, psys_ctrl,
++				   psys_base, &psys_ipdata);
++	if (IS_ERR(isp->psys)) {
++		ret = PTR_ERR(isp->psys);
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev);
++	if (ret < 0)
++		goto out_ipu6_bus_del_devices;
++
++	ret = ipu6_mmu_hw_init(isp->psys->mmu);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "Failed to set MMU hardware\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = ipu6_buttress_map_fw_image(isp->psys, isp->cpd_fw,
++					 &isp->psys->fw_sgt);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret, "failed to map fw image\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = ipu6_cpd_create_pkg_dir(isp->psys, isp->cpd_fw->data);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "failed to create pkg dir\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = devm_request_threaded_irq(dev, pdev->irq, ipu6_buttress_isr,
++					ipu6_buttress_isr_threaded,
++					IRQF_SHARED, IPU6_NAME, isp);
++	if (ret) {
++		dev_err_probe(dev, ret, "Requesting irq failed\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = ipu6_buttress_authenticate(isp);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "FW authentication failed\n");
++		goto out_free_irq;
++	}
++
++	ipu6_mmu_hw_cleanup(isp->psys->mmu);
++	pm_runtime_put(&isp->psys->auxdev.dev);
++
++	/* Configure the arbitration mechanisms for VC requests */
++	ipu6_configure_vc_mechanism(isp);
++
++	val = readl(isp->base + BUTTRESS_REG_SKU);
++	sku_id = FIELD_GET(GENMASK(6, 4), val);
++	version = FIELD_GET(GENMASK(3, 0), val);
++	dev_info(dev, "IPU%u-v%u[%x] hardware version %d\n", version, sku_id,
++		 pdev->device, isp->hw_ver);
++
++	pm_runtime_put_noidle(dev);
++	pm_runtime_allow(dev);
++
++	isp->bus_ready_to_probe = true;
++
++	return 0;
++
++out_free_irq:
++	devm_free_irq(dev, pdev->irq, isp);
++out_ipu6_bus_del_devices:
++	if (isp->psys) {
++		ipu6_cpd_free_pkg_dir(isp->psys);
++		ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
++	}
++	if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu))
++		ipu6_mmu_cleanup(isp->psys->mmu);
++	if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu))
++		ipu6_mmu_cleanup(isp->isys->mmu);
++	ipu6_bus_del_devices(pdev);
++	release_firmware(isp->cpd_fw);
++buttress_exit:
++	ipu6_buttress_exit(isp);
++
++	return ret;
++}
++
++static void ipu6_pci_remove(struct pci_dev *pdev)
++{
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	struct ipu6_mmu *isys_mmu = isp->isys->mmu;
++	struct ipu6_mmu *psys_mmu = isp->psys->mmu;
++
++	devm_free_irq(&pdev->dev, pdev->irq, isp);
++	ipu6_cpd_free_pkg_dir(isp->psys);
++
++	ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
++	ipu6_buttress_exit(isp);
++
++	ipu6_bus_del_devices(pdev);
++
++	pm_runtime_forbid(&pdev->dev);
++	pm_runtime_get_noresume(&pdev->dev);
++
++	pci_release_regions(pdev);
++	pci_disable_device(pdev);
++
++	release_firmware(isp->cpd_fw);
++
++	ipu6_mmu_cleanup(psys_mmu);
++	ipu6_mmu_cleanup(isys_mmu);
++}
++
++static void ipu6_pci_reset_prepare(struct pci_dev *pdev)
++{
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++
++	pm_runtime_forbid(&isp->pdev->dev);
++}
++
++static void ipu6_pci_reset_done(struct pci_dev *pdev)
++{
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++
++	ipu6_buttress_restore(isp);
++	if (isp->secure_mode)
++		ipu6_buttress_reset_authentication(isp);
++
++	isp->need_ipc_reset = true;
++	pm_runtime_allow(&isp->pdev->dev);
++}
++
++/*
++ * PCI base driver code requires driver to provide these to enable
++ * PCI device level PM state transitions (D0<->D3)
++ */
++static int ipu6_suspend(struct device *dev)
++{
++	return 0;
++}
++
++static int ipu6_resume(struct device *dev)
++{
++	struct pci_dev *pdev = to_pci_dev(dev);
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	struct ipu6_buttress *b = &isp->buttress;
++	int ret;
++
++	/* Configure the arbitration mechanisms for VC requests */
++	ipu6_configure_vc_mechanism(isp);
++
++	isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
++	dev_info(dev, "IPU6 in %s mode\n",
++		 isp->secure_mode ? "secure" : "non-secure");
++
++	ipu6_buttress_restore(isp);
++
++	ret = ipu6_buttress_ipc_reset(isp, &b->cse);
++	if (ret)
++		dev_err(&isp->pdev->dev, "IPC reset protocol failed!\n");
++
++	ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev);
++	if (ret < 0) {
++		dev_err(&isp->psys->auxdev.dev, "Failed to get runtime PM\n");
++		return 0;
++	}
++
++	ret = ipu6_buttress_authenticate(isp);
++	if (ret)
++		dev_err(&isp->pdev->dev, "FW authentication failed(%d)\n", ret);
++
++	pm_runtime_put(&isp->psys->auxdev.dev);
++
++	return 0;
++}
++
++static int ipu6_runtime_resume(struct device *dev)
++{
++	struct pci_dev *pdev = to_pci_dev(dev);
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	int ret;
++
++	ipu6_configure_vc_mechanism(isp);
++	ipu6_buttress_restore(isp);
++
++	if (isp->need_ipc_reset) {
++		struct ipu6_buttress *b = &isp->buttress;
++
++		isp->need_ipc_reset = false;
++		ret = ipu6_buttress_ipc_reset(isp, &b->cse);
++		if (ret)
++			dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
++	}
++
++	return 0;
++}
++
++static const struct dev_pm_ops ipu6_pm_ops = {
++	SET_SYSTEM_SLEEP_PM_OPS(&ipu6_suspend, &ipu6_resume)
++	SET_RUNTIME_PM_OPS(&ipu6_suspend, &ipu6_runtime_resume, NULL)
++};
++
++static const struct pci_device_id ipu6_pci_tbl[] = {
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6SE) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_ADLP) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_ADLN) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_RPLP) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_MTL) },
++	{ }
++};
++MODULE_DEVICE_TABLE(pci, ipu6_pci_tbl);
++
++static const struct pci_error_handlers pci_err_handlers = {
++	.reset_prepare = ipu6_pci_reset_prepare,
++	.reset_done = ipu6_pci_reset_done,
++};
++
++static struct pci_driver ipu6_pci_driver = {
++	.name = IPU6_NAME,
++	.id_table = ipu6_pci_tbl,
++	.probe = ipu6_pci_probe,
++	.remove = ipu6_pci_remove,
++	.driver = {
++		.pm = pm_ptr(&ipu6_pm_ops),
++	},
++	.err_handler = &pci_err_handlers,
++};
++
++module_pci_driver(ipu6_pci_driver);
++
++MODULE_IMPORT_NS(INTEL_IPU_BRIDGE);
++MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
++MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
++MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
++MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>");
++MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>");
++MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Intel IPU6 PCI driver");
+diff --git a/drivers/media/pci/intel/ipu6/ipu6.h b/drivers/media/pci/intel/ipu6/ipu6.h
+new file mode 100644
+index 000000000000..04e7e7e61ca5
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6.h
+@@ -0,0 +1,356 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_H
++#define IPU6_H
++
++#include <linux/list.h>
++#include <linux/pci.h>
++#include <linux/types.h>
++
++#include "ipu6-buttress.h"
++
++struct firmware;
++struct pci_dev;
++struct ipu6_bus_device;
++
++#define PCI_DEVICE_ID_INTEL_IPU6		0x9a19
++#define PCI_DEVICE_ID_INTEL_IPU6SE		0x4e19
++#define PCI_DEVICE_ID_INTEL_IPU6EP_ADLP		0x465d
++#define PCI_DEVICE_ID_INTEL_IPU6EP_ADLN		0x462e
++#define PCI_DEVICE_ID_INTEL_IPU6EP_RPLP		0xa75d
++#define PCI_DEVICE_ID_INTEL_IPU6EP_MTL		0x7d19
++
++#define IPU6_NAME			"intel-ipu6"
++#define IPU6_MEDIA_DEV_MODEL_NAME	"ipu6"
++
++#define IPU6SE_FIRMWARE_NAME		"intel/ipu6se_fw.bin"
++#define IPU6EP_FIRMWARE_NAME		"intel/ipu6ep_fw.bin"
++#define IPU6_FIRMWARE_NAME		"intel/ipu6_fw.bin"
++#define IPU6EPMTL_FIRMWARE_NAME		"intel/ipu6epmtl_fw.bin"
++
++enum ipu6_version {
++	IPU6_VER_INVALID = 0,
++	IPU6_VER_6 = 1,
++	IPU6_VER_6SE = 3,
++	IPU6_VER_6EP = 5,
++	IPU6_VER_6EP_MTL = 6,
++};
++
++/*
++ * IPU6 - TGL
++ * IPU6SE - JSL
++ * IPU6EP - ADL/RPL
++ * IPU6EP_MTL - MTL
++ */
++static inline bool is_ipu6se(u8 hw_ver)
++{
++	return hw_ver == IPU6_VER_6SE;
++}
++
++static inline bool is_ipu6ep(u8 hw_ver)
++{
++	return hw_ver == IPU6_VER_6EP;
++}
++
++static inline bool is_ipu6ep_mtl(u8 hw_ver)
++{
++	return hw_ver == IPU6_VER_6EP_MTL;
++}
++
++static inline bool is_ipu6_tgl(u8 hw_ver)
++{
++	return hw_ver == IPU6_VER_6;
++}
++
++/*
++ * ISYS DMA can overshoot. For higher resolutions over allocation is one line
++ * but it must be at minimum 1024 bytes. Value could be different in
++ * different versions / generations thus provide it via platform data.
++ */
++#define IPU6_ISYS_OVERALLOC_MIN		1024
++
++/* Physical pages in GDA is 128, page size is 2K for IPU6, 1K for others */
++#define IPU6_DEVICE_GDA_NR_PAGES		128
++
++/* Virtualization factor to calculate the available virtual pages */
++#define IPU6_DEVICE_GDA_VIRT_FACTOR	32
++
++#define NR_OF_MMU_RESOURCES			2
++
++struct ipu6_device {
++	struct pci_dev *pdev;
++	struct list_head devices;
++	struct ipu6_bus_device *isys;
++	struct ipu6_bus_device *psys;
++	struct ipu6_buttress buttress;
++
++	const struct firmware *cpd_fw;
++	const char *cpd_fw_name;
++	u32 cpd_metadata_cmpnt_size;
++
++	void __iomem *base;
++	bool need_ipc_reset;
++	bool secure_mode;
++	u8 hw_ver;
++	bool bus_ready_to_probe;
++};
++
++#define IPU6_ISYS_NAME "isys"
++#define IPU6_PSYS_NAME "psys"
++
++#define IPU6_MMU_MAX_DEVICES		4
++#define IPU6_MMU_ADDR_BITS		32
++/* The firmware is accessible within the first 2 GiB only in non-secure mode. */
++#define IPU6_MMU_ADDR_BITS_NON_SECURE	31
++
++#define IPU6_MMU_MAX_TLB_L1_STREAMS	32
++#define IPU6_MMU_MAX_TLB_L2_STREAMS	32
++#define IPU6_MAX_LI_BLOCK_ADDR		128
++#define IPU6_MAX_L2_BLOCK_ADDR		64
++
++#define IPU6_ISYS_MAX_CSI2_LEGACY_PORTS	4
++#define IPU6_ISYS_MAX_CSI2_COMBO_PORTS	2
++
++#define IPU6_MAX_FRAME_COUNTER	0xff
++
++#define IPU6SE_ISYS_NUM_STREAMS          IPU6SE_NONSECURE_STREAM_ID_MAX
++#define IPU6_ISYS_NUM_STREAMS            IPU6_NONSECURE_STREAM_ID_MAX
++
++/*
++ * To maximize the IOSF utlization, IPU6 need to send requests in bursts.
++ * At the DMA interface with the buttress, there are CDC FIFOs with burst
++ * collection capability. CDC FIFO burst collectors have a configurable
++ * threshold and is configured based on the outcome of performance measurements.
++ *
++ * isys has 3 ports with IOSF interface for VC0, VC1 and VC2
++ * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2
++ *
++ * Threshold values are pre-defined and are arrived at after performance
++ * evaluations on a type of IPU6
++ */
++#define IPU6_MAX_VC_IOSF_PORTS		4
++
++/*
++ * IPU6 must configure correct arbitration mechanism related to the IOSF VC
++ * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on
++ * stall and 1 means stall until the request is completed.
++ */
++#define IPU6_BTRS_ARB_MODE_TYPE_REARB	0
++#define IPU6_BTRS_ARB_MODE_TYPE_STALL	1
++
++/* Currently chosen arbitration mechanism for VC0 */
++#define IPU6_BTRS_ARB_STALL_MODE_VC0	\
++			IPU6_BTRS_ARB_MODE_TYPE_REARB
++
++/* Currently chosen arbitration mechanism for VC1 */
++#define IPU6_BTRS_ARB_STALL_MODE_VC1	\
++			IPU6_BTRS_ARB_MODE_TYPE_REARB
++
++/*
++ * MMU Invalidation HW bug workaround by ZLW mechanism
++ *
++ * Old IPU6 MMUV2 has a bug in the invalidation mechanism which might result in
++ * wrong translation or replication of the translation. This will cause data
++ * corruption. So we cannot directly use the MMU V2 invalidation registers
++ * to invalidate the MMU. Instead, whenever an invalidate is called, we need to
++ * clear the TLB by evicting all the valid translations by filling it with trash
++ * buffer (which is guaranteed not to be used by any other processes). ZLW is
++ * used to fill the L1 and L2 caches with the trash buffer translations. ZLW
++ * or Zero length write, is pre-fetch mechanism to pre-fetch the pages in
++ * advance to the L1 and L2 caches without triggering any memory operations.
++ *
++ * In MMU V2, L1 -> 16 streams and 64 blocks, maximum 16 blocks per stream
++ * One L1 block has 16 entries, hence points to 16 * 4K pages
++ * L2 -> 16 streams and 32 blocks. 2 blocks per streams
++ * One L2 block maps to 1024 L1 entries, hence points to 4MB address range
++ * 2 blocks per L2 stream means, 1 stream points to 8MB range
++ *
++ * As we need to clear the caches and 8MB being the biggest cache size, we need
++ * to have trash buffer which points to 8MB address range. As these trash
++ * buffers are not used for any memory transactions, we need only the least
++ * amount of physical memory. So we reserve 8MB IOVA address range but only
++ * one page is reserved from physical memory. Each of this 8MB IOVA address
++ * range is then mapped to the same physical memory page.
++ */
++/* One L2 entry maps 1024 L1 entries and one L1 entry per page */
++#define IPU6_MMUV2_L2_RANGE		(1024 * PAGE_SIZE)
++/* Max L2 blocks per stream */
++#define IPU6_MMUV2_MAX_L2_BLOCKS	2
++/* Max L1 blocks per stream */
++#define IPU6_MMUV2_MAX_L1_BLOCKS	16
++#define IPU6_MMUV2_TRASH_RANGE	(IPU6_MMUV2_L2_RANGE * IPU6_MMUV2_MAX_L2_BLOCKS)
++/* Entries per L1 block */
++#define MMUV2_ENTRIES_PER_L1_BLOCK	16
++#define MMUV2_TRASH_L1_BLOCK_OFFSET	(MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE)
++#define MMUV2_TRASH_L2_BLOCK_OFFSET	IPU6_MMUV2_L2_RANGE
++
++/*
++ * In some of the IPU6 MMUs, there is provision to configure L1 and L2 page
++ * table caches. Both these L1 and L2 caches are divided into multiple sections
++ * called streams. There is maximum 16 streams for both caches. Each of these
++ * sections are subdivided into multiple blocks. When nr_l1streams = 0 and
++ * nr_l2streams = 0, means the MMU is of type MMU_V1 and do not support
++ * L1/L2 page table caches.
++ *
++ * L1 stream per block sizes are configurable and varies per usecase.
++ * L2 has constant block sizes - 2 blocks per stream.
++ *
++ * MMU1 support pre-fetching of the pages to have less cache lookup misses. To
++ * enable the pre-fetching, MMU1 AT (Address Translator) device registers
++ * need to be configured.
++ *
++ * There are four types of memory accesses which requires ZLW configuration.
++ * ZLW(Zero Length Write) is a mechanism to enable VT-d pre-fetching on IOMMU.
++ *
++ * 1. Sequential Access or 1D mode
++ *	Set ZLW_EN -> 1
++ *	set ZLW_PAGE_CROSS_1D -> 1
++ *	Set ZLW_N to "N" pages so that ZLW will be inserte N pages ahead where
++ *		  N is pre-defined and hardcoded in the platform data
++ *	Set ZLW_2D -> 0
++ *
++ * 2. ZLW 2D mode
++ *	Set ZLW_EN -> 1
++ *	set ZLW_PAGE_CROSS_1D -> 1,
++ *	Set ZLW_N -> 0
++ *	Set ZLW_2D -> 1
++ *
++ * 3. ZLW Enable (no 1D or 2D mode)
++ *	Set ZLW_EN -> 1
++ *	set ZLW_PAGE_CROSS_1D -> 0,
++ *	Set ZLW_N -> 0
++ *	Set ZLW_2D -> 0
++ *
++ * 4. ZLW disable
++ *	Set ZLW_EN -> 0
++ *	set ZLW_PAGE_CROSS_1D -> 0,
++ *	Set ZLW_N -> 0
++ *	Set ZLW_2D -> 0
++ *
++ * To configure the ZLW for the above memory access, four registers are
++ * available. Hence to track these four settings, we have the following entries
++ * in the struct ipu6_mmu_hw. Each of these entries are per stream and
++ * available only for the L1 streams.
++ *
++ * a. l1_zlw_en -> To track zlw enabled per stream (ZLW_EN)
++ * b. l1_zlw_1d_mode -> Track 1D mode per stream. ZLW inserted at page boundary
++ * c. l1_ins_zlw_ahead_pages -> to track how advance the ZLW need to be inserted
++ *			Insert ZLW request N pages ahead address.
++ * d. l1_zlw_2d_mode -> To track 2D mode per stream (ZLW_2D)
++ *
++ *
++ * Currently L1/L2 streams, blocks, AT ZLW configurations etc. are pre-defined
++ * as per the usecase specific calculations. Any change to this pre-defined
++ * table has to happen in sync with IPU6 FW.
++ */
++struct ipu6_mmu_hw {
++	union {
++		unsigned long offset;
++		void __iomem *base;
++	};
++	u32 info_bits;
++	u8 nr_l1streams;
++	/*
++	 * L1 has variable blocks per stream - total of 64 blocks and maximum of
++	 * 16 blocks per stream. Configurable by using the block start address
++	 * per stream. Block start address is calculated from the block size
++	 */
++	u8 l1_block_sz[IPU6_MMU_MAX_TLB_L1_STREAMS];
++	/* Is ZLW is enabled in each stream */
++	bool l1_zlw_en[IPU6_MMU_MAX_TLB_L1_STREAMS];
++	bool l1_zlw_1d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS];
++	u8 l1_ins_zlw_ahead_pages[IPU6_MMU_MAX_TLB_L1_STREAMS];
++	bool l1_zlw_2d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS];
++
++	u32 l1_stream_id_reg_offset;
++	u32 l2_stream_id_reg_offset;
++
++	u8 nr_l2streams;
++	/*
++	 * L2 has fixed 2 blocks per stream. Block address is calculated
++	 * from the block size
++	 */
++	u8 l2_block_sz[IPU6_MMU_MAX_TLB_L2_STREAMS];
++	/* flag to track if WA is needed for successive invalidate HW bug */
++	bool insert_read_before_invalidate;
++};
++
++struct ipu6_mmu_pdata {
++	u32 nr_mmus;
++	struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES];
++	int mmid;
++};
++
++struct ipu6_isys_csi2_pdata {
++	void __iomem *base;
++};
++
++struct ipu6_isys_internal_csi2_pdata {
++	u32 nports;
++	u32 irq_mask;
++	u32 *offsets;
++	u32 ctrl0_irq_edge;
++	u32 ctrl0_irq_clear;
++	u32 ctrl0_irq_mask;
++	u32 ctrl0_irq_enable;
++	u32 ctrl0_irq_lnp;
++	u32 ctrl0_irq_status;
++	u32 fw_access_port_ofs;
++};
++
++struct ipu6_isys_internal_tpg_pdata {
++	u32 ntpgs;
++	u32 *offsets;
++	u32 *sels;
++};
++
++struct ipu6_hw_variants {
++	unsigned long offset;
++	u32 nr_mmus;
++	struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES];
++	u8 cdc_fifos;
++	u8 cdc_fifo_threshold[IPU6_MAX_VC_IOSF_PORTS];
++	u32 dmem_offset;
++	u32 spc_offset;
++};
++
++struct ipu6_isys_internal_pdata {
++	struct ipu6_isys_internal_csi2_pdata csi2;
++	struct ipu6_hw_variants hw_variant;
++	u32 num_parallel_streams;
++	u32 isys_dma_overshoot;
++	u32 sram_gran_shift;
++	u32 sram_gran_size;
++	u32 max_sram_size;
++	u32 max_streams;
++	u32 max_send_queues;
++	u32 max_sram_blocks;
++	u32 max_devq_size;
++	u32 sensor_type_start;
++	u32 sensor_type_end;
++	u32 ltr;
++	u32 memopen_threshold;
++	bool enhanced_iwake;
++};
++
++struct ipu6_isys_pdata {
++	void __iomem *base;
++	const struct ipu6_isys_internal_pdata *ipdata;
++};
++
++struct ipu6_psys_internal_pdata {
++	struct ipu6_hw_variants hw_variant;
++};
++
++struct ipu6_psys_pdata {
++	void __iomem *base;
++	const struct ipu6_psys_internal_pdata *ipdata;
++};
++
++int ipu6_fw_authenticate(void *data, u64 val);
++void ipu6_configure_spc(struct ipu6_device *isp,
++			const struct ipu6_hw_variants *hw_variant,
++			int pkg_dir_idx, void __iomem *base, u64 *pkg_dir,
++			dma_addr_t pkg_dir_dma_addr);
++#endif /* IPU6_H */
+-- 
+2.43.2
+
+
+From f52c1b80222269f99d52b0af5937995e22c9ed6d Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:16 +0800
+Subject: [PATCH 09/33] media: intel/ipu6: add IPU auxiliary devices
+
+Even the IPU input system and processing system are in a single PCI
+device, each system has its own power sequence, the processing system
+power up depends on the input system power up.
+
+Besides, input system and processing system have their own MMU
+hardware for IPU DMA address mapping.
+
+Register the IS/PS devices on auxiliary bus and attach power domain
+to implement the power sequence dependency.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-bus.c | 165 ++++++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-bus.h |  58 +++++++++
+ 2 files changed, 223 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-bus.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-bus.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.c b/drivers/media/pci/intel/ipu6/ipu6-bus.c
+new file mode 100644
+index 000000000000..e81b9a6518a1
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-bus.c
+@@ -0,0 +1,165 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/auxiliary_bus.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++#include <linux/pci.h>
++#include <linux/pm_domain.h>
++#include <linux/pm_runtime.h>
++#include <linux/slab.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-buttress.h"
++#include "ipu6-dma.h"
++
++static int bus_pm_runtime_suspend(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++	int ret;
++
++	ret = pm_generic_runtime_suspend(dev);
++	if (ret)
++		return ret;
++
++	ret = ipu6_buttress_power(dev, adev->ctrl, false);
++	if (!ret)
++		return 0;
++
++	dev_err(dev, "power down failed!\n");
++
++	/* Powering down failed, attempt to resume device now */
++	ret = pm_generic_runtime_resume(dev);
++	if (!ret)
++		return -EBUSY;
++
++	return -EIO;
++}
++
++static int bus_pm_runtime_resume(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++	int ret;
++
++	ret = ipu6_buttress_power(dev, adev->ctrl, true);
++	if (ret)
++		return ret;
++
++	ret = pm_generic_runtime_resume(dev);
++	if (ret)
++		goto out_err;
++
++	return 0;
++
++out_err:
++	ipu6_buttress_power(dev, adev->ctrl, false);
++
++	return -EBUSY;
++}
++
++static struct dev_pm_domain ipu6_bus_pm_domain = {
++	.ops = {
++		.runtime_suspend = bus_pm_runtime_suspend,
++		.runtime_resume = bus_pm_runtime_resume,
++	},
++};
++
++static DEFINE_MUTEX(ipu6_bus_mutex);
++
++static void ipu6_bus_release(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++
++	kfree(adev->pdata);
++	kfree(adev);
++}
++
++struct ipu6_bus_device *
++ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
++			   void *pdata, struct ipu6_buttress_ctrl *ctrl,
++			   char *name)
++{
++	struct auxiliary_device *auxdev;
++	struct ipu6_bus_device *adev;
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	int ret;
++
++	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
++	if (!adev)
++		return ERR_PTR(-ENOMEM);
++
++	adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS :
++				      IPU6_MMU_ADDR_BITS_NON_SECURE);
++	adev->isp = isp;
++	adev->ctrl = ctrl;
++	adev->pdata = pdata;
++	auxdev = &adev->auxdev;
++	auxdev->name = name;
++	auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
++		      PCI_DEVID(pdev->bus->number, pdev->devfn);
++
++	auxdev->dev.parent = parent;
++	auxdev->dev.release = ipu6_bus_release;
++	auxdev->dev.dma_ops = &ipu6_dma_ops;
++	auxdev->dev.dma_mask = &adev->dma_mask;
++	auxdev->dev.dma_parms = pdev->dev.dma_parms;
++	auxdev->dev.coherent_dma_mask = adev->dma_mask;
++
++	ret = auxiliary_device_init(auxdev);
++	if (ret < 0) {
++		dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
++			ret);
++		kfree(adev);
++		return ERR_PTR(ret);
++	}
++
++	dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain);
++
++	pm_runtime_forbid(&adev->auxdev.dev);
++	pm_runtime_enable(&adev->auxdev.dev);
++
++	return adev;
++}
++
++int ipu6_bus_add_device(struct ipu6_bus_device *adev)
++{
++	struct auxiliary_device *auxdev = &adev->auxdev;
++	int ret;
++
++	ret = auxiliary_device_add(auxdev);
++	if (ret) {
++		auxiliary_device_uninit(auxdev);
++		return ret;
++	}
++
++	mutex_lock(&ipu6_bus_mutex);
++	list_add(&adev->list, &adev->isp->devices);
++	mutex_unlock(&ipu6_bus_mutex);
++
++	pm_runtime_allow(&auxdev->dev);
++
++	return 0;
++}
++
++void ipu6_bus_del_devices(struct pci_dev *pdev)
++{
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	struct ipu6_bus_device *adev, *save;
++
++	mutex_lock(&ipu6_bus_mutex);
++
++	list_for_each_entry_safe(adev, save, &isp->devices, list) {
++		pm_runtime_disable(&adev->auxdev.dev);
++		list_del(&adev->list);
++		auxiliary_device_delete(&adev->auxdev);
++		auxiliary_device_uninit(&adev->auxdev);
++	}
++
++	mutex_unlock(&ipu6_bus_mutex);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.h b/drivers/media/pci/intel/ipu6/ipu6-bus.h
+new file mode 100644
+index 000000000000..d46181354836
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-bus.h
+@@ -0,0 +1,58 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_BUS_H
++#define IPU6_BUS_H
++
++#include <linux/auxiliary_bus.h>
++#include <linux/container_of.h>
++#include <linux/device.h>
++#include <linux/irqreturn.h>
++#include <linux/list.h>
++#include <linux/scatterlist.h>
++#include <linux/types.h>
++
++struct firmware;
++struct pci_dev;
++
++#define IPU6_BUS_NAME	IPU6_NAME "-bus"
++
++struct ipu6_buttress_ctrl;
++
++struct ipu6_bus_device {
++	struct auxiliary_device auxdev;
++	struct auxiliary_driver *auxdrv;
++	const struct ipu6_auxdrv_data *auxdrv_data;
++	struct list_head list;
++	void *pdata;
++	struct ipu6_mmu *mmu;
++	struct ipu6_device *isp;
++	struct ipu6_buttress_ctrl *ctrl;
++	u64 dma_mask;
++	const struct firmware *fw;
++	struct sg_table fw_sgt;
++	u64 *pkg_dir;
++	dma_addr_t pkg_dir_dma_addr;
++	unsigned int pkg_dir_size;
++};
++
++struct ipu6_auxdrv_data {
++	irqreturn_t (*isr)(struct ipu6_bus_device *adev);
++	irqreturn_t (*isr_threaded)(struct ipu6_bus_device *adev);
++	bool wake_isr_thread;
++};
++
++#define to_ipu6_bus_device(_dev) \
++	container_of(to_auxiliary_dev(_dev), struct ipu6_bus_device, auxdev)
++#define auxdev_to_adev(_auxdev) \
++	container_of(_auxdev, struct ipu6_bus_device, auxdev)
++#define ipu6_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev)
++
++struct ipu6_bus_device *
++ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
++			   void *pdata, struct ipu6_buttress_ctrl *ctrl,
++			   char *name);
++int ipu6_bus_add_device(struct ipu6_bus_device *adev);
++void ipu6_bus_del_devices(struct pci_dev *pdev);
++
++#endif
+-- 
+2.43.2
+
+
+From a74d85716ec13ff2f55997c73c9f06367174d7a6 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:17 +0800
+Subject: [PATCH 10/33] media: intel/ipu6: add IPU6 buttress interface driver
+
+The IPU6 buttress is the interface between IPU device (input system
+and processing system) with rest of the SoC. It contains overall IPU
+hardware control registers, these control registers are used as the
+interfaces with the Intel Converged Security Engine and Punit to do
+firmware authentication and power management.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-buttress.c  | 912 ++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-buttress.h  | 102 ++
+ .../intel/ipu6/ipu6-platform-buttress-regs.h  | 232 +++++
+ 3 files changed, 1246 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-buttress.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-buttress.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c
+new file mode 100644
+index 000000000000..2f73302812f3
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c
+@@ -0,0 +1,912 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/firmware.h>
++#include <linux/interrupt.h>
++#include <linux/iopoll.h>
++#include <linux/math64.h>
++#include <linux/mm.h>
++#include <linux/mutex.h>
++#include <linux/pci.h>
++#include <linux/pfn.h>
++#include <linux/pm_runtime.h>
++#include <linux/scatterlist.h>
++#include <linux/slab.h>
++#include <linux/time64.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-buttress.h"
++#include "ipu6-platform-buttress-regs.h"
++
++#define BOOTLOADER_STATUS_OFFSET       0x15c
++
++#define BOOTLOADER_MAGIC_KEY		0xb00710ad
++
++#define ENTRY	BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1
++#define EXIT	BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2
++#define QUERY	BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE
++
++#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX	10
++
++#define BUTTRESS_POWER_TIMEOUT_US		(200 * USEC_PER_MSEC)
++
++#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US	(5 * USEC_PER_SEC)
++#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US	(10 * USEC_PER_SEC)
++#define BUTTRESS_CSE_FWRESET_TIMEOUT_US		(100 * USEC_PER_MSEC)
++
++#define BUTTRESS_IPC_TX_TIMEOUT_MS		MSEC_PER_SEC
++#define BUTTRESS_IPC_RX_TIMEOUT_MS		MSEC_PER_SEC
++#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US	(1 * USEC_PER_SEC)
++#define BUTTRESS_TSC_SYNC_TIMEOUT_US		(5 * USEC_PER_MSEC)
++
++#define BUTTRESS_IPC_RESET_RETRY		2000
++#define BUTTRESS_CSE_IPC_RESET_RETRY	4
++#define BUTTRESS_IPC_CMD_SEND_RETRY	1
++
++#define BUTTRESS_MAX_CONSECUTIVE_IRQS	100
++
++static const u32 ipu6_adev_irq_mask[2] = {
++	BUTTRESS_ISR_IS_IRQ,
++	BUTTRESS_ISR_PS_IRQ
++};
++
++int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
++			    struct ipu6_buttress_ipc *ipc)
++{
++	unsigned int retries = BUTTRESS_IPC_RESET_RETRY;
++	struct ipu6_buttress *b = &isp->buttress;
++	u32 val = 0, csr_in_clr;
++
++	if (!isp->secure_mode) {
++		dev_dbg(&isp->pdev->dev, "Skip IPC reset for non-secure mode");
++		return 0;
++	}
++
++	mutex_lock(&b->ipc_mutex);
++
++	/* Clear-by-1 CSR (all bits), corresponding internal states. */
++	val = readl(isp->base + ipc->csr_in);
++	writel(val, isp->base + ipc->csr_in);
++
++	/* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */
++	writel(ENTRY, isp->base + ipc->csr_out);
++	/*
++	 * Clear-by-1 all CSR bits EXCEPT following
++	 * bits:
++	 * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
++	 * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
++	 * C. Possibly custom bits, depending on
++	 * their role.
++	 */
++	csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ |
++		BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID |
++		BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY;
++
++	do {
++		usleep_range(400, 500);
++		val = readl(isp->base + ipc->csr_in);
++		switch (val) {
++		case ENTRY | EXIT:
++		case ENTRY | EXIT | QUERY:
++			/*
++			 * 1) Clear-by-1 CSR bits
++			 * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE2).
++			 * 2) Set peer CSR bit
++			 * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
++			 */
++			writel(ENTRY | EXIT, isp->base + ipc->csr_in);
++			writel(QUERY, isp->base + ipc->csr_out);
++			break;
++		case ENTRY:
++		case ENTRY | QUERY:
++			/*
++			 * 1) Clear-by-1 CSR bits
++			 * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
++			 * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE).
++			 * 2) Set peer CSR bit
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE1.
++			 */
++			writel(ENTRY | QUERY, isp->base + ipc->csr_in);
++			writel(ENTRY, isp->base + ipc->csr_out);
++			break;
++		case EXIT:
++		case EXIT | QUERY:
++			/*
++			 * Clear-by-1 CSR bit
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
++			 * 1) Clear incoming doorbell.
++			 * 2) Clear-by-1 all CSR bits EXCEPT following
++			 * bits:
++			 * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
++			 * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
++			 * C. Possibly custom bits, depending on
++			 * their role.
++			 * 3) Set peer CSR bit
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
++			 */
++			writel(EXIT, isp->base + ipc->csr_in);
++			writel(0, isp->base + ipc->db0_in);
++			writel(csr_in_clr, isp->base + ipc->csr_in);
++			writel(EXIT, isp->base + ipc->csr_out);
++
++			/*
++			 * Read csr_in again to make sure if RST_PHASE2 is done.
++			 * If csr_in is QUERY, it should be handled again.
++			 */
++			usleep_range(200, 300);
++			val = readl(isp->base + ipc->csr_in);
++			if (val & QUERY) {
++				dev_dbg(&isp->pdev->dev,
++					"RST_PHASE2 retry csr_in = %x\n", val);
++				break;
++			}
++			mutex_unlock(&b->ipc_mutex);
++			return 0;
++		case QUERY:
++			/*
++			 * 1) Clear-by-1 CSR bit
++			 * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
++			 * 2) Set peer CSR bit
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE1
++			 */
++			writel(QUERY, isp->base + ipc->csr_in);
++			writel(ENTRY, isp->base + ipc->csr_out);
++			break;
++		default:
++			dev_warn_ratelimited(&isp->pdev->dev,
++					     "Unexpected CSR 0x%x\n", val);
++			break;
++		}
++	} while (retries--);
++
++	mutex_unlock(&b->ipc_mutex);
++	dev_err(&isp->pdev->dev, "Timed out while waiting for CSE\n");
++
++	return -ETIMEDOUT;
++}
++
++static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp,
++					     struct ipu6_buttress_ipc *ipc)
++{
++	writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ,
++	       isp->base + ipc->csr_out);
++}
++
++static int
++ipu6_buttress_ipc_validity_open(struct ipu6_device *isp,
++				struct ipu6_buttress_ipc *ipc)
++{
++	unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID;
++	void __iomem *addr;
++	int ret;
++	u32 val;
++
++	writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ,
++	       isp->base + ipc->csr_out);
++
++	addr = isp->base + ipc->csr_in;
++	ret = readl_poll_timeout(addr, val, val & mask, 200,
++				 BUTTRESS_IPC_VALIDITY_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val);
++		ipu6_buttress_ipc_validity_close(isp, ipc);
++	}
++
++	return ret;
++}
++
++static void ipu6_buttress_ipc_recv(struct ipu6_device *isp,
++				   struct ipu6_buttress_ipc *ipc, u32 *ipc_msg)
++{
++	if (ipc_msg)
++		*ipc_msg = readl(isp->base + ipc->data0_in);
++	writel(0, isp->base + ipc->db0_in);
++}
++
++static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
++				       enum ipu6_buttress_ipc_domain ipc_domain,
++				       struct ipu6_ipc_buttress_bulk_msg *msgs,
++				       u32 size)
++{
++	unsigned long tx_timeout_jiffies, rx_timeout_jiffies;
++	unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY;
++	struct ipu6_buttress *b = &isp->buttress;
++	struct ipu6_buttress_ipc *ipc;
++	u32 val;
++	int ret;
++	int tout;
++
++	ipc = ipc_domain == IPU6_BUTTRESS_IPC_CSE ? &b->cse : &b->ish;
++
++	mutex_lock(&b->ipc_mutex);
++
++	ret = ipu6_buttress_ipc_validity_open(isp, ipc);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "IPC validity open failed\n");
++		goto out;
++	}
++
++	tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS);
++	rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS);
++
++	for (i = 0; i < size; i++) {
++		reinit_completion(&ipc->send_complete);
++		if (msgs[i].require_resp)
++			reinit_completion(&ipc->recv_complete);
++
++		dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n",
++			msgs[i].cmd);
++		writel(msgs[i].cmd, isp->base + ipc->data0_out);
++		val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size;
++		writel(val, isp->base + ipc->db0_out);
++
++		tout = wait_for_completion_timeout(&ipc->send_complete,
++						   tx_timeout_jiffies);
++		if (!tout) {
++			dev_err(&isp->pdev->dev, "send IPC response timeout\n");
++			if (!retry--) {
++				ret = -ETIMEDOUT;
++				goto out;
++			}
++
++			/* Try again if CSE is not responding on first try */
++			writel(0, isp->base + ipc->db0_out);
++			i--;
++			continue;
++		}
++
++		retry = BUTTRESS_IPC_CMD_SEND_RETRY;
++
++		if (!msgs[i].require_resp)
++			continue;
++
++		tout = wait_for_completion_timeout(&ipc->recv_complete,
++						   rx_timeout_jiffies);
++		if (!tout) {
++			dev_err(&isp->pdev->dev, "recv IPC response timeout\n");
++			ret = -ETIMEDOUT;
++			goto out;
++		}
++
++		if (ipc->nack_mask &&
++		    (ipc->recv_data & ipc->nack_mask) == ipc->nack) {
++			dev_err(&isp->pdev->dev,
++				"IPC NACK for cmd 0x%x\n", msgs[i].cmd);
++			ret = -EIO;
++			goto out;
++		}
++
++		if (ipc->recv_data != msgs[i].expected_resp) {
++			dev_err(&isp->pdev->dev,
++				"expected resp: 0x%x, IPC response: 0x%x ",
++				msgs[i].expected_resp, ipc->recv_data);
++			ret = -EIO;
++			goto out;
++		}
++	}
++
++	dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n");
++
++out:
++	ipu6_buttress_ipc_validity_close(isp, ipc);
++	mutex_unlock(&b->ipc_mutex);
++	return ret;
++}
++
++static int
++ipu6_buttress_ipc_send(struct ipu6_device *isp,
++		       enum ipu6_buttress_ipc_domain ipc_domain,
++		       u32 ipc_msg, u32 size, bool require_resp,
++		       u32 expected_resp)
++{
++	struct ipu6_ipc_buttress_bulk_msg msg = {
++		.cmd = ipc_msg,
++		.cmd_size = size,
++		.require_resp = require_resp,
++		.expected_resp = expected_resp,
++	};
++
++	return ipu6_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1);
++}
++
++static irqreturn_t ipu6_buttress_call_isr(struct ipu6_bus_device *adev)
++{
++	irqreturn_t ret = IRQ_WAKE_THREAD;
++
++	if (!adev || !adev->auxdrv || !adev->auxdrv_data)
++		return IRQ_NONE;
++
++	if (adev->auxdrv_data->isr)
++		ret = adev->auxdrv_data->isr(adev);
++
++	if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded)
++		ret = IRQ_NONE;
++
++	return ret;
++}
++
++irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
++{
++	struct ipu6_device *isp = isp_ptr;
++	struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
++	struct ipu6_buttress *b = &isp->buttress;
++	u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS;
++	irqreturn_t ret = IRQ_NONE;
++	u32 disable_irqs = 0;
++	u32 irq_status;
++	u32 i, count = 0;
++
++	pm_runtime_get_noresume(&isp->pdev->dev);
++
++	irq_status = readl(isp->base + reg_irq_sts);
++	if (!irq_status) {
++		pm_runtime_put_noidle(&isp->pdev->dev);
++		return IRQ_NONE;
++	}
++
++	do {
++		writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR);
++
++		for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) {
++			irqreturn_t r = ipu6_buttress_call_isr(adev[i]);
++
++			if (!(irq_status & ipu6_adev_irq_mask[i]))
++				continue;
++
++			if (r == IRQ_WAKE_THREAD) {
++				ret = IRQ_WAKE_THREAD;
++				disable_irqs |= ipu6_adev_irq_mask[i];
++			} else if (ret == IRQ_NONE && r == IRQ_HANDLED) {
++				ret = IRQ_HANDLED;
++			}
++		}
++
++		if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE)
++			ret = IRQ_HANDLED;
++
++		if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) {
++			dev_dbg(&isp->pdev->dev,
++				"BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n");
++			ipu6_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data);
++			complete(&b->cse.recv_complete);
++		}
++
++		if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) {
++			dev_dbg(&isp->pdev->dev,
++				"BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n");
++			ipu6_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data);
++			complete(&b->ish.recv_complete);
++		}
++
++		if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) {
++			dev_dbg(&isp->pdev->dev,
++				"BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
++			complete(&b->cse.send_complete);
++		}
++
++		if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) {
++			dev_dbg(&isp->pdev->dev,
++				"BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
++			complete(&b->ish.send_complete);
++		}
++
++		if (irq_status & BUTTRESS_ISR_SAI_VIOLATION &&
++		    ipu6_buttress_get_secure_mode(isp))
++			dev_err(&isp->pdev->dev,
++				"BUTTRESS_ISR_SAI_VIOLATION\n");
++
++		if (irq_status & (BUTTRESS_ISR_IS_FATAL_MEM_ERR |
++				  BUTTRESS_ISR_PS_FATAL_MEM_ERR))
++			dev_err(&isp->pdev->dev,
++				"BUTTRESS_ISR_FATAL_MEM_ERR\n");
++
++		if (irq_status & BUTTRESS_ISR_UFI_ERROR)
++			dev_err(&isp->pdev->dev, "BUTTRESS_ISR_UFI_ERROR\n");
++
++		if (++count == BUTTRESS_MAX_CONSECUTIVE_IRQS) {
++			dev_err(&isp->pdev->dev, "too many consecutive IRQs\n");
++			ret = IRQ_NONE;
++			break;
++		}
++
++		irq_status = readl(isp->base + reg_irq_sts);
++	} while (irq_status);
++
++	if (disable_irqs)
++		writel(BUTTRESS_IRQS & ~disable_irqs,
++		       isp->base + BUTTRESS_REG_ISR_ENABLE);
++
++	pm_runtime_put(&isp->pdev->dev);
++
++	return ret;
++}
++
++irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr)
++{
++	struct ipu6_device *isp = isp_ptr;
++	struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
++	const struct ipu6_auxdrv_data *drv_data = NULL;
++	irqreturn_t ret = IRQ_NONE;
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask) && adev[i]; i++) {
++		drv_data = adev[i]->auxdrv_data;
++		if (!drv_data)
++			continue;
++
++		if (drv_data->wake_isr_thread &&
++		    drv_data->isr_threaded(adev[i]) == IRQ_HANDLED)
++			ret = IRQ_HANDLED;
++	}
++
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
++
++	return ret;
++}
++
++int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
++			bool on)
++{
++	struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
++	u32 pwr_sts, val;
++	int ret;
++
++	if (!ctrl)
++		return 0;
++
++	mutex_lock(&isp->buttress.power_mutex);
++
++	if (!on) {
++		val = 0;
++		pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift;
++	} else {
++		val = BUTTRESS_FREQ_CTL_START |
++			FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK,
++				   ctrl->ratio) |
++			FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK,
++				   ctrl->qos_floor) |
++			BUTTRESS_FREQ_CTL_ICCMAX_LEVEL;
++
++		pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift;
++	}
++
++	writel(val, isp->base + ctrl->freq_ctl);
++
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE,
++				 val, (val & ctrl->pwr_sts_mask) == pwr_sts,
++				 100, BUTTRESS_POWER_TIMEOUT_US);
++	if (ret)
++		dev_err(&isp->pdev->dev,
++			"Change power status timeout with 0x%x\n", val);
++
++	ctrl->started = !ret && on;
++
++	mutex_unlock(&isp->buttress.power_mutex);
++
++	return ret;
++}
++
++bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp)
++{
++	u32 val;
++
++	val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
++
++	return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE;
++}
++
++bool ipu6_buttress_auth_done(struct ipu6_device *isp)
++{
++	u32 val;
++
++	if (!isp->secure_mode)
++		return true;
++
++	val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
++	val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val);
++
++	return val == BUTTRESS_SECURITY_CTL_AUTH_DONE;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6);
++
++int ipu6_buttress_reset_authentication(struct ipu6_device *isp)
++{
++	int ret;
++	u32 val;
++
++	if (!isp->secure_mode) {
++		dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
++		return 0;
++	}
++
++	writel(BUTTRESS_FW_RESET_CTL_START, isp->base +
++	       BUTTRESS_REG_FW_RESET_CTL);
++
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val,
++				 val & BUTTRESS_FW_RESET_CTL_DONE, 500,
++				 BUTTRESS_CSE_FWRESET_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev,
++			"Time out while resetting authentication state\n");
++		return ret;
++	}
++
++	dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n");
++	writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL);
++	/* leave some time for HW restore */
++	usleep_range(800, 1000);
++
++	return 0;
++}
++
++int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
++			       const struct firmware *fw, struct sg_table *sgt)
++{
++	struct page **pages;
++	const void *addr;
++	unsigned long n_pages;
++	unsigned int i;
++	int ret;
++
++	n_pages = PHYS_PFN(PAGE_ALIGN(fw->size));
++
++	pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL);
++	if (!pages)
++		return -ENOMEM;
++
++	addr = fw->data;
++	for (i = 0; i < n_pages; i++) {
++		struct page *p = vmalloc_to_page(addr);
++
++		if (!p) {
++			ret = -ENOMEM;
++			goto out;
++		}
++		pages[i] = p;
++		addr += PAGE_SIZE;
++	}
++
++	ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size,
++					GFP_KERNEL);
++	if (ret) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	ret = dma_map_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
++	if (ret < 0) {
++		ret = -ENOMEM;
++		sg_free_table(sgt);
++		goto out;
++	}
++
++	dma_sync_sgtable_for_device(&sys->auxdev.dev, sgt, DMA_TO_DEVICE);
++
++out:
++	kfree(pages);
++
++	return ret;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6);
++
++void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
++				  struct sg_table *sgt)
++{
++	dma_unmap_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
++	sg_free_table(sgt);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6);
++
++int ipu6_buttress_authenticate(struct ipu6_device *isp)
++{
++	struct ipu6_buttress *b = &isp->buttress;
++	struct ipu6_psys_pdata *psys_pdata;
++	u32 data, mask, done, fail;
++	int ret;
++
++	if (!isp->secure_mode) {
++		dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
++		return 0;
++	}
++
++	psys_pdata = isp->psys->pdata;
++
++	mutex_lock(&b->auth_mutex);
++
++	if (ipu6_buttress_auth_done(isp)) {
++		ret = 0;
++		goto out_unlock;
++	}
++
++	/*
++	 * Write address of FIT table to FW_SOURCE register
++	 * Let's use fw address. I.e. not using FIT table yet
++	 */
++	data = lower_32_bits(isp->psys->pkg_dir_dma_addr);
++	writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO);
++
++	data = upper_32_bits(isp->psys->pkg_dir_dma_addr);
++	writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI);
++
++	/*
++	 * Write boot_load into IU2CSEDATA0
++	 * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
++	 * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
++	 */
++	dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n");
++
++	ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
++				     BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD,
++				     1, true,
++				     BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
++		goto out_unlock;
++	}
++
++	mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK;
++	done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE;
++	fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED;
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
++				 ((data & mask) == done ||
++				  (data & mask) == fail), 500,
++				 BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE boot_load timeout\n");
++		goto out_unlock;
++	}
++
++	if ((data & mask) == fail) {
++		dev_err(&isp->pdev->dev, "CSE auth failed\n");
++		ret = -EINVAL;
++		goto out_unlock;
++	}
++
++	ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET,
++				 data, data == BOOTLOADER_MAGIC_KEY, 500,
++				 BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n",
++			data);
++		goto out_unlock;
++	}
++
++	/*
++	 * Write authenticate_run into IU2CSEDATA0
++	 * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
++	 * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
++	 */
++	dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n");
++	ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
++				     BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN,
++				     1, true,
++				     BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n");
++		goto out_unlock;
++	}
++
++	done = BUTTRESS_SECURITY_CTL_AUTH_DONE;
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
++				 ((data & mask) == done ||
++				  (data & mask) == fail), 500,
++				 BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE authenticate timeout\n");
++		goto out_unlock;
++	}
++
++	if ((data & mask) == fail) {
++		dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
++		ret = -EINVAL;
++		goto out_unlock;
++	}
++
++	dev_info(&isp->pdev->dev, "CSE authenticate_run done\n");
++
++out_unlock:
++	mutex_unlock(&b->auth_mutex);
++
++	return ret;
++}
++
++static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp)
++{
++	u32 val, mask, done;
++	int ret;
++
++	mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK;
++
++	writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC,
++	       isp->base + BUTTRESS_REG_FABRIC_CMD);
++
++	val = readl(isp->base + BUTTRESS_REG_PWR_STATE);
++	val = FIELD_GET(mask, val);
++	if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) {
++		dev_err(&isp->pdev->dev, "Start tsc sync failed\n");
++		return -EINVAL;
++	}
++
++	done = BUTTRESS_PWR_STATE_HH_STATE_DONE;
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val,
++				 FIELD_GET(mask, val) == done, 500,
++				 BUTTRESS_TSC_SYNC_TIMEOUT_US);
++	if (ret)
++		dev_err(&isp->pdev->dev, "Start tsc sync timeout\n");
++
++	return ret;
++}
++
++int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp)
++{
++	unsigned int i;
++
++	for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
++		u32 val;
++		int ret;
++
++		ret = ipu6_buttress_send_tsc_request(isp);
++		if (ret != -ETIMEDOUT)
++			return ret;
++
++		val = readl(isp->base + BUTTRESS_REG_TSW_CTL);
++		val = val | BUTTRESS_TSW_CTL_SOFT_RESET;
++		writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
++		val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET;
++		writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
++	}
++
++	dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n");
++
++	return -ETIMEDOUT;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6);
++
++void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val)
++{
++	u32 tsc_hi_1, tsc_hi_2, tsc_lo;
++	unsigned long flags;
++
++	local_irq_save(flags);
++	tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI);
++	tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO);
++	tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI);
++	if (tsc_hi_1 == tsc_hi_2) {
++		*val = (u64)tsc_hi_1 << 32 | tsc_lo;
++	} else {
++		/* Check if TSC has rolled over */
++		if (tsc_lo & BIT(31))
++			*val = (u64)tsc_hi_1 << 32 | tsc_lo;
++		else
++			*val = (u64)tsc_hi_2 << 32 | tsc_lo;
++	}
++	local_irq_restore(flags);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6);
++
++u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp)
++{
++	u64 ns = ticks * 10000;
++
++	/*
++	 * converting TSC tick count to ns is calculated by:
++	 * Example (TSC clock frequency is 19.2MHz):
++	 * ns = ticks * 1000 000 000 / 19.2Mhz
++	 *    = ticks * 1000 000 000 / 19200000Hz
++	 *    = ticks * 10000 / 192 ns
++	 */
++	return div_u64(ns, isp->buttress.ref_clk);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6);
++
++void ipu6_buttress_restore(struct ipu6_device *isp)
++{
++	struct ipu6_buttress *b = &isp->buttress;
++
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
++	writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT);
++}
++
++int ipu6_buttress_init(struct ipu6_device *isp)
++{
++	int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY;
++	struct ipu6_buttress *b = &isp->buttress;
++	u32 val;
++
++	mutex_init(&b->power_mutex);
++	mutex_init(&b->auth_mutex);
++	mutex_init(&b->cons_mutex);
++	mutex_init(&b->ipc_mutex);
++	init_completion(&b->ish.send_complete);
++	init_completion(&b->cse.send_complete);
++	init_completion(&b->ish.recv_complete);
++	init_completion(&b->cse.recv_complete);
++
++	b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK;
++	b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK;
++	b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR;
++	b->cse.csr_out = BUTTRESS_REG_IU2CSECSR;
++	b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0;
++	b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0;
++	b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0;
++	b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0;
++
++	/* no ISH on IPU6 */
++	memset(&b->ish, 0, sizeof(b->ish));
++	INIT_LIST_HEAD(&b->constraints);
++
++	isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
++	dev_info(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n",
++		 isp->secure_mode ? "secure" : "non-secure",
++		 readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH),
++		 readl(isp->base + BUTTRESS_REG_CAMERA_MASK));
++
++	b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT);
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
++
++	/* get ref_clk frequency by reading the indication in btrs control */
++	val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
++	val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val);
++
++	switch (val) {
++	case 0x0:
++		b->ref_clk = 240;
++		break;
++	case 0x1:
++		b->ref_clk = 192;
++		break;
++	case 0x2:
++		b->ref_clk = 384;
++		break;
++	default:
++		dev_warn(&isp->pdev->dev,
++			 "Unsupported ref clock, use 19.2Mhz by default.\n");
++		b->ref_clk = 192;
++		break;
++	}
++
++	/* Retry couple of times in case of CSE initialization is delayed */
++	do {
++		ret = ipu6_buttress_ipc_reset(isp, &b->cse);
++		if (ret) {
++			dev_warn(&isp->pdev->dev,
++				 "IPC reset protocol failed, retrying\n");
++		} else {
++			dev_dbg(&isp->pdev->dev, "IPC reset done\n");
++			return 0;
++		}
++	} while (ipc_reset_retry--);
++
++	dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
++
++	mutex_destroy(&b->power_mutex);
++	mutex_destroy(&b->auth_mutex);
++	mutex_destroy(&b->cons_mutex);
++	mutex_destroy(&b->ipc_mutex);
++
++	return ret;
++}
++
++void ipu6_buttress_exit(struct ipu6_device *isp)
++{
++	struct ipu6_buttress *b = &isp->buttress;
++
++	writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE);
++
++	mutex_destroy(&b->power_mutex);
++	mutex_destroy(&b->auth_mutex);
++	mutex_destroy(&b->cons_mutex);
++	mutex_destroy(&b->ipc_mutex);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.h b/drivers/media/pci/intel/ipu6/ipu6-buttress.h
+new file mode 100644
+index 000000000000..558e1d70f4af
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.h
+@@ -0,0 +1,102 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_BUTTRESS_H
++#define IPU6_BUTTRESS_H
++
++#include <linux/completion.h>
++#include <linux/irqreturn.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++
++struct device;
++struct firmware;
++struct ipu6_device;
++struct ipu6_bus_device;
++
++#define BUTTRESS_PS_FREQ_STEP		25U
++#define BUTTRESS_MIN_FORCE_PS_FREQ	(BUTTRESS_PS_FREQ_STEP * 8)
++#define BUTTRESS_MAX_FORCE_PS_FREQ	(BUTTRESS_PS_FREQ_STEP * 32)
++
++#define BUTTRESS_IS_FREQ_STEP		25U
++#define BUTTRESS_MIN_FORCE_IS_FREQ	(BUTTRESS_IS_FREQ_STEP * 8)
++#define BUTTRESS_MAX_FORCE_IS_FREQ	(BUTTRESS_IS_FREQ_STEP * 22)
++
++struct ipu6_buttress_ctrl {
++	u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off;
++	unsigned int ratio;
++	unsigned int qos_floor;
++	bool started;
++};
++
++struct ipu6_buttress_ipc {
++	struct completion send_complete;
++	struct completion recv_complete;
++	u32 nack;
++	u32 nack_mask;
++	u32 recv_data;
++	u32 csr_out;
++	u32 csr_in;
++	u32 db0_in;
++	u32 db0_out;
++	u32 data0_out;
++	u32 data0_in;
++};
++
++struct ipu6_buttress {
++	struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex;
++	struct ipu6_buttress_ipc cse;
++	struct ipu6_buttress_ipc ish;
++	struct list_head constraints;
++	u32 wdt_cached_value;
++	bool force_suspend;
++	u32 ref_clk;
++};
++
++struct ipu6_buttress_sensor_clk_freq {
++	unsigned int rate;
++	unsigned int val;
++};
++
++enum ipu6_buttress_ipc_domain {
++	IPU6_BUTTRESS_IPC_CSE,
++	IPU6_BUTTRESS_IPC_ISH,
++};
++
++struct ipu6_buttress_constraint {
++	struct list_head list;
++	unsigned int min_freq;
++};
++
++struct ipu6_ipc_buttress_bulk_msg {
++	u32 cmd;
++	u32 expected_resp;
++	bool require_resp;
++	u8 cmd_size;
++};
++
++int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
++			    struct ipu6_buttress_ipc *ipc);
++int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
++			       const struct firmware *fw,
++			       struct sg_table *sgt);
++void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
++				  struct sg_table *sgt);
++int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
++			bool on);
++bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp);
++int ipu6_buttress_authenticate(struct ipu6_device *isp);
++int ipu6_buttress_reset_authentication(struct ipu6_device *isp);
++bool ipu6_buttress_auth_done(struct ipu6_device *isp);
++int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp);
++void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val);
++u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp);
++
++irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr);
++irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr);
++int ipu6_buttress_init(struct ipu6_device *isp);
++void ipu6_buttress_exit(struct ipu6_device *isp);
++void ipu6_buttress_csi_port_config(struct ipu6_device *isp,
++				   u32 legacy, u32 combo);
++void ipu6_buttress_restore(struct ipu6_device *isp);
++#endif /* IPU6_BUTTRESS_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
+new file mode 100644
+index 000000000000..87239af96502
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
+@@ -0,0 +1,232 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2023 Intel Corporation */
++
++#ifndef IPU6_PLATFORM_BUTTRESS_REGS_H
++#define IPU6_PLATFORM_BUTTRESS_REGS_H
++
++#include <linux/bits.h>
++
++/* IS_WORKPOINT_REQ */
++#define IPU6_BUTTRESS_REG_IS_FREQ_CTL		0x34
++/* PS_WORKPOINT_REQ */
++#define IPU6_BUTTRESS_REG_PS_FREQ_CTL		0x38
++
++#define IPU6_IS_FREQ_MAX		533
++#define IPU6_IS_FREQ_MIN		200
++#define IPU6_PS_FREQ_MAX		450
++#define IPU6_IS_FREQ_RATIO_BASE		25
++#define IPU6_PS_FREQ_RATIO_BASE		25
++
++/* should be tuned for real silicon */
++#define IPU6_IS_FREQ_CTL_DEFAULT_RATIO		0x08
++#define IPU6SE_IS_FREQ_CTL_DEFAULT_RATIO	0x0a
++#define IPU6_PS_FREQ_CTL_DEFAULT_RATIO		0x0d
++
++#define IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO	0x10
++#define IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO	0x0708
++
++#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT	3
++#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_MASK	GENMASK(4, 3)
++
++#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT	6
++#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_MASK	GENMASK(7, 6)
++
++#define IPU6_BUTTRESS_PWR_STATE_DN_DONE		0x0
++#define IPU6_BUTTRESS_PWR_STATE_UP_PROCESS	0x1
++#define IPU6_BUTTRESS_PWR_STATE_DN_PROCESS	0x2
++#define IPU6_BUTTRESS_PWR_STATE_UP_DONE		0x3
++
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_0	0x270
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_1	0x274
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_2	0x278
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_3	0x27c
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_4	0x280
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_5	0x284
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_6	0x288
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_7	0x28c
++
++#define BUTTRESS_REG_WDT			0x8
++#define BUTTRESS_REG_BTRS_CTRL			0xc
++#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0	BIT(0)
++#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1	BIT(1)
++#define BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND	GENMASK(9, 8)
++
++#define BUTTRESS_REG_FW_RESET_CTL	0x30
++#define BUTTRESS_FW_RESET_CTL_START	BIT(0)
++#define BUTTRESS_FW_RESET_CTL_DONE	BIT(1)
++
++#define BUTTRESS_REG_IS_FREQ_CTL	0x34
++#define BUTTRESS_REG_PS_FREQ_CTL	0x38
++
++#define BUTTRESS_FREQ_CTL_START		BIT(31)
++#define BUTTRESS_FREQ_CTL_ICCMAX_LEVEL		GENMASK(19, 16)
++#define BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK	GENMASK(15, 8)
++#define BUTTRESS_FREQ_CTL_RATIO_MASK	GENMASK(7, 0)
++
++#define BUTTRESS_REG_PWR_STATE	0x5c
++
++#define BUTTRESS_PWR_STATE_RESET		0x0
++#define BUTTRESS_PWR_STATE_PWR_ON_DONE		0x1
++#define BUTTRESS_PWR_STATE_PWR_RDY		0x3
++#define BUTTRESS_PWR_STATE_PWR_IDLE		0x4
++
++#define BUTTRESS_PWR_STATE_HH_STATUS_MASK	GENMASK(12, 11)
++
++enum {
++	BUTTRESS_PWR_STATE_HH_STATE_IDLE,
++	BUTTRESS_PWR_STATE_HH_STATE_IN_PRGS,
++	BUTTRESS_PWR_STATE_HH_STATE_DONE,
++	BUTTRESS_PWR_STATE_HH_STATE_ERR,
++};
++
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK	GENMASK(23, 19)
++
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE			0x0
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PLL_CMP		0x1
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK		0x2
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PG_ACK		0x3
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_ASSRT_CYCLES		0x4
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES1		0x5
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES2		0x6
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DEASSRT_CYCLES	0x7
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_FUSE_WR_CMP	0x8
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_BRK_POINT			0x9
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY			0xa
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_HALT_HALTED		0xb
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DURATION_CNT3		0xc
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK_PD		0xd
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_PD_BRK_POINT		0xe
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PD_PG_ACK0		0xf
++
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK	GENMASK(28, 24)
++
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE			0x0
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_IP_RDY	0x1
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_PRE_CNT_EXH	0x2
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_VGI_PWRGOOD	0x3
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_POST_CNT_EXH	0x4
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WR_PLL_RATIO		0x5
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_CMP		0x6
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_CLKACK		0x7
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_ASSRT_CYCLES		0x8
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES1		0x9
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES2		0xa
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_DEASSRT_CYCLES	0xb
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PU_BRK_PNT		0xc
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_FUSE_ACCPT		0xd
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP			0xf
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_4_HALTED		0x10
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RESET_CNT3		0x11
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_CLKACK		0x12
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_OFF_IND		0x13
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PH4		0x14
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PLL_CMP		0x15
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_CLKACK		0x16
++
++#define BUTTRESS_REG_SECURITY_CTL	0x300
++#define BUTTRESS_REG_SKU		0x314
++#define BUTTRESS_REG_SECURITY_TOUCH	0x318
++#define BUTTRESS_REG_CAMERA_MASK	0x84
++
++#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE	BIT(16)
++#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK	GENMASK(4, 0)
++
++#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE		BIT(0)
++#define BUTTRESS_SECURITY_CTL_AUTH_DONE			BIT(1)
++#define BUTTRESS_SECURITY_CTL_AUTH_FAILED		BIT(3)
++
++#define BUTTRESS_REG_FW_SOURCE_BASE_LO	0x78
++#define BUTTRESS_REG_FW_SOURCE_BASE_HI	0x7C
++#define BUTTRESS_REG_FW_SOURCE_SIZE	0x80
++
++#define BUTTRESS_REG_ISR_STATUS		0x90
++#define BUTTRESS_REG_ISR_ENABLED_STATUS	0x94
++#define BUTTRESS_REG_ISR_ENABLE		0x98
++#define BUTTRESS_REG_ISR_CLEAR		0x9C
++
++#define BUTTRESS_ISR_IS_IRQ			BIT(0)
++#define BUTTRESS_ISR_PS_IRQ			BIT(1)
++#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE	BIT(2)
++#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH	BIT(3)
++#define BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING	BIT(4)
++#define BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING	BIT(5)
++#define BUTTRESS_ISR_CSE_CSR_SET		BIT(6)
++#define BUTTRESS_ISR_ISH_CSR_SET		BIT(7)
++#define BUTTRESS_ISR_SPURIOUS_CMP		BIT(8)
++#define BUTTRESS_ISR_WATCHDOG_EXPIRED		BIT(9)
++#define BUTTRESS_ISR_PUNIT_2_IUNIT_IRQ		BIT(10)
++#define BUTTRESS_ISR_SAI_VIOLATION		BIT(11)
++#define BUTTRESS_ISR_HW_ASSERTION		BIT(12)
++#define BUTTRESS_ISR_IS_CORRECTABLE_MEM_ERR	BIT(13)
++#define BUTTRESS_ISR_IS_FATAL_MEM_ERR		BIT(14)
++#define BUTTRESS_ISR_IS_NON_FATAL_MEM_ERR	BIT(15)
++#define BUTTRESS_ISR_PS_CORRECTABLE_MEM_ERR	BIT(16)
++#define BUTTRESS_ISR_PS_FATAL_MEM_ERR		BIT(17)
++#define BUTTRESS_ISR_PS_NON_FATAL_MEM_ERR	BIT(18)
++#define BUTTRESS_ISR_PS_FAST_THROTTLE		BIT(19)
++#define BUTTRESS_ISR_UFI_ERROR			BIT(20)
++
++#define BUTTRESS_REG_IU2CSEDB0	0x100
++
++#define BUTTRESS_IU2CSEDB0_BUSY		BIT(31)
++#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL	2
++
++#define BUTTRESS_REG_IU2CSEDATA0	0x104
++
++#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD		1
++#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN		2
++#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE		3
++#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH	16
++
++#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE			BIT(0)
++#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE			BIT(1)
++#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE		BIT(2)
++#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE	BIT(4)
++
++#define BUTTRESS_REG_IU2CSECSR		0x108
++
++#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1		BIT(0)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2		BIT(1)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE	BIT(2)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ		BIT(3)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID			BIT(4)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ		BIT(5)
++
++#define BUTTRESS_REG_CSE2IUDB0		0x304
++#define BUTTRESS_REG_CSE2IUCSR		0x30C
++#define BUTTRESS_REG_CSE2IUDATA0	0x308
++
++/* 0x20 == NACK, 0xf == unknown command */
++#define BUTTRESS_CSE2IUDATA0_IPC_NACK      0xf20
++#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK GENMASK(15, 0)
++
++#define BUTTRESS_REG_ISH2IUCSR		0x50
++#define BUTTRESS_REG_ISH2IUDB0		0x54
++#define BUTTRESS_REG_ISH2IUDATA0	0x58
++
++#define BUTTRESS_REG_IU2ISHDB0		0x10C
++#define BUTTRESS_REG_IU2ISHDATA0	0x110
++#define BUTTRESS_REG_IU2ISHDATA1	0x114
++#define BUTTRESS_REG_IU2ISHCSR		0x118
++
++#define BUTTRESS_REG_FABRIC_CMD		0x88
++
++#define BUTTRESS_FABRIC_CMD_START_TSC_SYNC	BIT(0)
++#define BUTTRESS_FABRIC_CMD_IS_DRAIN		BIT(4)
++
++#define BUTTRESS_REG_TSW_CTL		0x120
++#define BUTTRESS_TSW_CTL_SOFT_RESET	BIT(8)
++
++#define BUTTRESS_REG_TSC_LO	0x164
++#define BUTTRESS_REG_TSC_HI	0x168
++
++#define BUTTRESS_IRQS		(BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
++				 BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE |    \
++				 BUTTRESS_ISR_IS_IRQ | BUTTRESS_ISR_PS_IRQ)
++
++#define BUTTRESS_EVENT		 (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
++				  BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING | \
++				  BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE |    \
++				  BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH |    \
++				  BUTTRESS_ISR_SAI_VIOLATION)
++#endif /* IPU6_PLATFORM_BUTTRESS_REGS_H */
+-- 
+2.43.2
+
+
+From 12bd5bdd53a7c829cc5cd61a6887f89ffb036f8f Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:18 +0800
+Subject: [PATCH 11/33] media: intel/ipu6: CPD parsing for get firmware
+ components
+
+For IPU6, firmware is generated and released as signed
+Code Partition Directory (CPD) format file, which is aligned with
+the SPI flash code partition definition. CPD format include CPD
+header, manifest, metadata and module data. Driver can parse them
+according to the CPD layout to acquire each component.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-cpd.c | 362 ++++++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-cpd.h | 105 +++++++
+ 2 files changed, 467 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-cpd.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-cpd.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.c b/drivers/media/pci/intel/ipu6/ipu6-cpd.c
+new file mode 100644
+index 000000000000..b0ffd04c4cd3
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.c
+@@ -0,0 +1,362 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/err.h>
++#include <linux/dma-mapping.h>
++#include <linux/gfp_types.h>
++#include <linux/math64.h>
++#include <linux/sizes.h>
++#include <linux/types.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-cpd.h"
++
++/* 15 entries + header*/
++#define MAX_PKG_DIR_ENT_CNT		16
++/* 2 qword per entry/header */
++#define PKG_DIR_ENT_LEN			2
++/* PKG_DIR size in bytes */
++#define PKG_DIR_SIZE			((MAX_PKG_DIR_ENT_CNT) *	\
++					 (PKG_DIR_ENT_LEN) * sizeof(u64))
++/* _IUPKDR_ */
++#define PKG_DIR_HDR_MARK		0x5f4955504b44525fULL
++
++/* $CPD */
++#define CPD_HDR_MARK			0x44504324
++
++#define MAX_MANIFEST_SIZE		(SZ_2K * sizeof(u32))
++#define MAX_METADATA_SIZE		SZ_64K
++
++#define MAX_COMPONENT_ID		127
++#define MAX_COMPONENT_VERSION		0xffff
++
++#define MANIFEST_IDX	0
++#define METADATA_IDX	1
++#define MODULEDATA_IDX	2
++/*
++ * PKG_DIR Entry (type == id)
++ * 63:56        55      54:48   47:32   31:24   23:0
++ * Rsvd         Rsvd    Type    Version Rsvd    Size
++ */
++#define PKG_DIR_SIZE_MASK	GENMASK(23, 0)
++#define PKG_DIR_VERSION_MASK	GENMASK(47, 32)
++#define PKG_DIR_TYPE_MASK	GENMASK(54, 48)
++
++static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd,
++							    u8 idx)
++{
++	const struct ipu6_cpd_hdr *cpd_hdr = cpd;
++	const struct ipu6_cpd_ent *ent;
++
++	ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len);
++	return ent + idx;
++}
++
++#define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX)
++#define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX)
++#define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX)
++
++static const struct ipu6_cpd_metadata_cmpnt_hdr *
++ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata,
++			    unsigned int metadata_size, u8 idx)
++{
++	size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn);
++	size_t cmpnt_count = metadata_size - extn_size;
++
++	cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size);
++
++	if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) {
++		dev_err(&isp->pdev->dev, "Component index out of range (%d)\n",
++			idx);
++		return ERR_PTR(-EINVAL);
++	}
++
++	return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size;
++}
++
++static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp,
++					   const void *metadata,
++					   unsigned int metadata_size, u8 idx)
++{
++	const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
++
++	cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
++	if (IS_ERR(cmpnt))
++		return PTR_ERR(cmpnt);
++
++	return cmpnt->ver;
++}
++
++static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp,
++					  const void *metadata,
++					  unsigned int metadata_size, u8 idx)
++{
++	const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
++
++	cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
++	if (IS_ERR(cmpnt))
++		return PTR_ERR(cmpnt);
++
++	return cmpnt->id;
++}
++
++static int ipu6_cpd_parse_module_data(struct ipu6_device *isp,
++				      const void *module_data,
++				      unsigned int module_data_size,
++				      dma_addr_t dma_addr_module_data,
++				      u64 *pkg_dir, const void *metadata,
++				      unsigned int metadata_size)
++{
++	const struct ipu6_cpd_module_data_hdr *module_data_hdr;
++	const struct ipu6_cpd_hdr *dir_hdr;
++	const struct ipu6_cpd_ent *dir_ent;
++	unsigned int i;
++	u8 len;
++
++	if (!module_data)
++		return -EINVAL;
++
++	module_data_hdr = module_data;
++	dir_hdr = module_data + module_data_hdr->hdr_len;
++	len = dir_hdr->hdr_len;
++	dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len);
++
++	pkg_dir[0] = PKG_DIR_HDR_MARK;
++	/* pkg_dir entry count = component count + pkg_dir header */
++	pkg_dir[1] = dir_hdr->ent_cnt + 1;
++
++	for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) {
++		u64 *p = &pkg_dir[PKG_DIR_ENT_LEN *  (1 + i)];
++		int ver, id;
++
++		*p++ = dma_addr_module_data + dir_ent->offset;
++		id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata,
++						    metadata_size, i);
++		if (id < 0 || id > MAX_COMPONENT_ID) {
++			dev_err(&isp->pdev->dev, "Invalid CPD component id\n");
++			return -EINVAL;
++		}
++
++		ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata,
++						      metadata_size, i);
++		if (ver < 0 || ver > MAX_COMPONENT_VERSION) {
++			dev_err(&isp->pdev->dev,
++				"Invalid CPD component version\n");
++			return -EINVAL;
++		}
++
++		*p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) |
++			FIELD_PREP(PKG_DIR_TYPE_MASK, id) |
++			FIELD_PREP(PKG_DIR_VERSION_MASK, ver);
++	}
++
++	return 0;
++}
++
++int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src)
++{
++	dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl);
++	const struct ipu6_cpd_ent *ent, *man_ent, *met_ent;
++	struct device *dev = &adev->auxdev.dev;
++	struct ipu6_device *isp = adev->isp;
++	unsigned int man_sz, met_sz;
++	void *pkg_dir_pos;
++	int ret;
++
++	man_ent = ipu6_cpd_get_manifest(src);
++	man_sz = man_ent->len;
++
++	met_ent = ipu6_cpd_get_metadata(src);
++	met_sz = met_ent->len;
++
++	adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz;
++	adev->pkg_dir = dma_alloc_attrs(dev, adev->pkg_dir_size,
++					&adev->pkg_dir_dma_addr, GFP_KERNEL, 0);
++	if (!adev->pkg_dir)
++		return -ENOMEM;
++
++	/*
++	 * pkg_dir entry/header:
++	 * qword | 63:56 | 55   | 54:48 | 47:32 | 31:24 | 23:0
++	 * N         Address/Offset/"_IUPKDR_"
++	 * N + 1 | rsvd  | rsvd | type  | ver   | rsvd  | size
++	 *
++	 * We can ignore other fields that size in N + 1 qword as they
++	 * are 0 anyway. Just setting size for now.
++	 */
++
++	ent = ipu6_cpd_get_moduledata(src);
++
++	ret = ipu6_cpd_parse_module_data(isp, src + ent->offset,
++					 ent->len, dma_addr_src + ent->offset,
++					 adev->pkg_dir, src + met_ent->offset,
++					 met_ent->len);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Failed to parse module data\n");
++		dma_free_attrs(dev, adev->pkg_dir_size,
++			       adev->pkg_dir, adev->pkg_dir_dma_addr, 0);
++		return ret;
++	}
++
++	/* Copy manifest after pkg_dir */
++	pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT;
++	memcpy(pkg_dir_pos, src + man_ent->offset, man_sz);
++
++	/* Copy metadata after manifest */
++	pkg_dir_pos += man_sz;
++	memcpy(pkg_dir_pos, src + met_ent->offset, met_sz);
++
++	dma_sync_single_range_for_device(dev, adev->pkg_dir_dma_addr,
++					 0, adev->pkg_dir_size, DMA_TO_DEVICE);
++
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6);
++
++void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev)
++{
++	dma_free_attrs(&adev->auxdev.dev, adev->pkg_dir_size, adev->pkg_dir,
++		       adev->pkg_dir_dma_addr, 0);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6);
++
++static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd,
++				 unsigned long cpd_size,
++				 unsigned long data_size)
++{
++	const struct ipu6_cpd_hdr *cpd_hdr = cpd;
++	const struct ipu6_cpd_ent *ent;
++	unsigned int i;
++	u8 len;
++
++	len = cpd_hdr->hdr_len;
++
++	/* Ensure cpd hdr is within moduledata */
++	if (cpd_size < len) {
++		dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
++		return -EINVAL;
++	}
++
++	/* Sanity check for CPD header */
++	if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {
++		dev_err(&isp->pdev->dev, "Invalid CPD header\n");
++		return -EINVAL;
++	}
++
++	/* Ensure that all entries are within moduledata */
++	ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len);
++	for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) {
++		if (data_size < ent->offset ||
++		    data_size - ent->offset < ent->len) {
++			dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i);
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,
++					const void *moduledata,
++					u32 moduledata_size)
++{
++	const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata;
++	int ret;
++
++	/* Ensure moduledata hdr is within moduledata */
++	if (moduledata_size < sizeof(*mod_hdr) ||
++	    moduledata_size < mod_hdr->hdr_len) {
++		dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
++		return -EINVAL;
++	}
++
++	dev_info(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date);
++	ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len,
++				    moduledata_size - mod_hdr->hdr_len,
++				    moduledata_size);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n");
++		return ret;
++	}
++
++	return 0;
++}
++
++static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,
++				      const void *metadata, u32 meta_size)
++{
++	const struct ipu6_cpd_metadata_extn *extn = metadata;
++
++	/* Sanity check for metadata size */
++	if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) {
++		dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
++		return -EINVAL;
++	}
++
++	/* Validate extension and image types */
++	if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT ||
++	    extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) {
++		dev_err(&isp->pdev->dev,
++			"Invalid CPD metadata descriptor img_type (%d)\n",
++			extn->img_type);
++		return -EINVAL;
++	}
++
++	/* Validate metadata size multiple of metadata components */
++	if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) {
++		dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n");
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
++			       unsigned long cpd_file_size)
++{
++	const struct ipu6_cpd_hdr *hdr = cpd_file;
++	const struct ipu6_cpd_ent *ent;
++	int ret;
++
++	ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size,
++				    cpd_file_size);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Invalid CPD in file\n");
++		return ret;
++	}
++
++	/* Check for CPD file marker */
++	if (hdr->hdr_mark != CPD_HDR_MARK) {
++		dev_err(&isp->pdev->dev, "Invalid CPD header\n");
++		return -EINVAL;
++	}
++
++	/* Sanity check for manifest size */
++	ent = ipu6_cpd_get_manifest(cpd_file);
++	if (ent->len > MAX_MANIFEST_SIZE) {
++		dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n");
++		return -EINVAL;
++	}
++
++	/* Validate metadata */
++	ent = ipu6_cpd_get_metadata(cpd_file);
++	ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
++		return ret;
++	}
++
++	/* Validate moduledata */
++	ent = ipu6_cpd_get_moduledata(cpd_file);
++	ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset,
++					   ent->len);
++	if (ret)
++		dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n");
++
++	return ret;
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.h b/drivers/media/pci/intel/ipu6/ipu6-cpd.h
+new file mode 100644
+index 000000000000..37465d507386
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.h
+@@ -0,0 +1,105 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2015 - 2023 Intel Corporation */
++
++#ifndef IPU6_CPD_H
++#define IPU6_CPD_H
++
++struct ipu6_device;
++struct ipu6_bus_device;
++
++#define IPU6_CPD_SIZE_OF_FW_ARCH_VERSION	7
++#define IPU6_CPD_SIZE_OF_SYSTEM_VERSION		11
++#define IPU6_CPD_SIZE_OF_COMPONENT_NAME		12
++
++#define IPU6_CPD_METADATA_EXTN_TYPE_IUNIT	0x10
++
++#define IPU6_CPD_METADATA_IMAGE_TYPE_RESERVED		0
++#define IPU6_CPD_METADATA_IMAGE_TYPE_BOOTLOADER		1
++#define IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE	2
++
++#define IPU6_CPD_PKG_DIR_PSYS_SERVER_IDX	0
++#define IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX	1
++
++#define IPU6_CPD_PKG_DIR_CLIENT_PG_TYPE		3
++
++#define IPU6_CPD_METADATA_HASH_KEY_SIZE		48
++#define IPU6SE_CPD_METADATA_HASH_KEY_SIZE	32
++
++struct ipu6_cpd_module_data_hdr {
++	u32 hdr_len;
++	u32 endian;
++	u32 fw_pkg_date;
++	u32 hive_sdk_date;
++	u32 compiler_date;
++	u32 target_platform_type;
++	u8 sys_ver[IPU6_CPD_SIZE_OF_SYSTEM_VERSION];
++	u8 fw_arch_ver[IPU6_CPD_SIZE_OF_FW_ARCH_VERSION];
++	u8 rsvd[2];
++} __packed;
++
++/*
++ * ipu6_cpd_hdr structure updated as the chksum and
++ * sub_partition_name is unused on host side
++ * CSE layout version 1.6 for IPU6SE (hdr_len = 0x10)
++ * CSE layout version 1.7 for IPU6 (hdr_len = 0x14)
++ */
++struct ipu6_cpd_hdr {
++	u32 hdr_mark;
++	u32 ent_cnt;
++	u8 hdr_ver;
++	u8 ent_ver;
++	u8 hdr_len;
++} __packed;
++
++struct ipu6_cpd_ent {
++	u8 name[IPU6_CPD_SIZE_OF_COMPONENT_NAME];
++	u32 offset;
++	u32 len;
++	u8 rsvd[4];
++} __packed;
++
++struct ipu6_cpd_metadata_cmpnt_hdr {
++	u32 id;
++	u32 size;
++	u32 ver;
++} __packed;
++
++struct ipu6_cpd_metadata_cmpnt {
++	struct ipu6_cpd_metadata_cmpnt_hdr hdr;
++	u8 sha2_hash[IPU6_CPD_METADATA_HASH_KEY_SIZE];
++	u32 entry_point;
++	u32 icache_base_offs;
++	u8 attrs[16];
++} __packed;
++
++struct ipu6se_cpd_metadata_cmpnt {
++	struct ipu6_cpd_metadata_cmpnt_hdr hdr;
++	u8 sha2_hash[IPU6SE_CPD_METADATA_HASH_KEY_SIZE];
++	u32 entry_point;
++	u32 icache_base_offs;
++	u8 attrs[16];
++} __packed;
++
++struct ipu6_cpd_metadata_extn {
++	u32 extn_type;
++	u32 len;
++	u32 img_type;
++	u8 rsvd[16];
++} __packed;
++
++struct ipu6_cpd_client_pkg_hdr {
++	u32 prog_list_offs;
++	u32 prog_list_size;
++	u32 prog_desc_offs;
++	u32 prog_desc_size;
++	u32 pg_manifest_offs;
++	u32 pg_manifest_size;
++	u32 prog_bin_offs;
++	u32 prog_bin_size;
++} __packed;
++
++int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src);
++void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev);
++int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
++			       unsigned long cpd_file_size);
++#endif /* IPU6_CPD_H */
+-- 
+2.43.2
+
+
+From 86b4cd540a9cb10de7a521882495f5eba6cc919f Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:19 +0800
+Subject: [PATCH 12/33] media: intel/ipu6: add IPU6 DMA mapping API and MMU
+ table
+
+he Intel IPU6 has an internal microcontroller (scalar processor, SP) which
+is used to execute the firmware. The SP can access IPU internal memory and
+map system DRAM to its an internal 32-bit virtual address space.
+
+This patch adds a driver for the IPU MMU and a DMA mapping implementation
+using the internal MMU. The system IOMMU may be used besides the IPU MMU.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-dma.c | 502 ++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-dma.h |  19 +
+ drivers/media/pci/intel/ipu6/ipu6-mmu.c | 845 ++++++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-mmu.h |  73 ++
+ 4 files changed, 1439 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-dma.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-dma.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-mmu.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-mmu.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.c b/drivers/media/pci/intel/ipu6/ipu6-dma.c
+new file mode 100644
+index 000000000000..3d77c6e5a45e
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-dma.c
+@@ -0,0 +1,502 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/cacheflush.h>
++#include <linux/dma-mapping.h>
++#include <linux/iova.h>
++#include <linux/list.h>
++#include <linux/mm.h>
++#include <linux/vmalloc.h>
++#include <linux/scatterlist.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-dma.h"
++#include "ipu6-mmu.h"
++
++struct vm_info {
++	struct list_head list;
++	struct page **pages;
++	dma_addr_t ipu6_iova;
++	void *vaddr;
++	unsigned long size;
++};
++
++static struct vm_info *get_vm_info(struct ipu6_mmu *mmu, dma_addr_t iova)
++{
++	struct vm_info *info, *save;
++
++	list_for_each_entry_safe(info, save, &mmu->vma_list, list) {
++		if (iova >= info->ipu6_iova &&
++		    iova < (info->ipu6_iova + info->size))
++			return info;
++	}
++
++	return NULL;
++}
++
++static void __dma_clear_buffer(struct page *page, size_t size,
++			       unsigned long attrs)
++{
++	void *ptr;
++
++	if (!page)
++		return;
++	/*
++	 * Ensure that the allocated pages are zeroed, and that any data
++	 * lurking in the kernel direct-mapped region is invalidated.
++	 */
++	ptr = page_address(page);
++	memset(ptr, 0, size);
++	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
++		clflush_cache_range(ptr, size);
++}
++
++static struct page **__dma_alloc_buffer(struct device *dev, size_t size,
++					gfp_t gfp, unsigned long attrs)
++{
++	int count = PHYS_PFN(size);
++	int array_size = count * sizeof(struct page *);
++	struct page **pages;
++	int i = 0;
++
++	pages = kvzalloc(array_size, GFP_KERNEL);
++	if (!pages)
++		return NULL;
++
++	gfp |= __GFP_NOWARN;
++
++	while (count) {
++		int j, order = __fls(count);
++
++		pages[i] = alloc_pages(gfp, order);
++		while (!pages[i] && order)
++			pages[i] = alloc_pages(gfp, --order);
++		if (!pages[i])
++			goto error;
++
++		if (order) {
++			split_page(pages[i], order);
++			j = 1 << order;
++			while (j--)
++				pages[i + j] = pages[i] + j;
++		}
++
++		__dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs);
++		i += 1 << order;
++		count -= 1 << order;
++	}
++
++	return pages;
++error:
++	while (i--)
++		if (pages[i])
++			__free_pages(pages[i], 0);
++	kvfree(pages);
++	return NULL;
++}
++
++static void __dma_free_buffer(struct device *dev, struct page **pages,
++			      size_t size, unsigned long attrs)
++{
++	int count = PHYS_PFN(size);
++	unsigned int i;
++
++	for (i = 0; i < count && pages[i]; i++) {
++		__dma_clear_buffer(pages[i], PAGE_SIZE, attrs);
++		__free_pages(pages[i], 0);
++	}
++
++	kvfree(pages);
++}
++
++static void ipu6_dma_sync_single_for_cpu(struct device *dev,
++					 dma_addr_t dma_handle,
++					 size_t size,
++					 enum dma_data_direction dir)
++{
++	void *vaddr;
++	u32 offset;
++	struct vm_info *info;
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++
++	info = get_vm_info(mmu, dma_handle);
++	if (WARN_ON(!info))
++		return;
++
++	offset = dma_handle - info->ipu6_iova;
++	if (WARN_ON(size > (info->size - offset)))
++		return;
++
++	vaddr = info->vaddr + offset;
++	clflush_cache_range(vaddr, size);
++}
++
++static void ipu6_dma_sync_sg_for_cpu(struct device *dev,
++				     struct scatterlist *sglist,
++				     int nents, enum dma_data_direction dir)
++{
++	struct scatterlist *sg;
++	int i;
++
++	for_each_sg(sglist, sg, nents, i)
++		clflush_cache_range(page_to_virt(sg_page(sg)), sg->length);
++}
++
++static void *ipu6_dma_alloc(struct device *dev, size_t size,
++			    dma_addr_t *dma_handle, gfp_t gfp,
++			    unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
++	dma_addr_t pci_dma_addr, ipu6_iova;
++	struct vm_info *info;
++	unsigned long count;
++	struct page **pages;
++	struct iova *iova;
++	unsigned int i;
++	int ret;
++
++	info = kzalloc(sizeof(*info), GFP_KERNEL);
++	if (!info)
++		return NULL;
++
++	size = PAGE_ALIGN(size);
++	count = PHYS_PFN(size);
++
++	iova = alloc_iova(&mmu->dmap->iovad, count,
++			  PHYS_PFN(dma_get_mask(dev)), 0);
++	if (!iova)
++		goto out_kfree;
++
++	pages = __dma_alloc_buffer(dev, size, gfp, attrs);
++	if (!pages)
++		goto out_free_iova;
++
++	dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n",
++		size, iova->pfn_lo, iova->pfn_hi);
++	for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) {
++		pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0,
++						  PAGE_SIZE, DMA_BIDIRECTIONAL,
++						  attrs);
++		dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n",
++			&pci_dma_addr);
++		if (dma_mapping_error(&pdev->dev, pci_dma_addr)) {
++			dev_err(dev, "pci_dma_mapping for page[%d] failed", i);
++			goto out_unmap;
++		}
++
++		ret = ipu6_mmu_map(mmu->dmap->mmu_info,
++				   PFN_PHYS(iova->pfn_lo + i), pci_dma_addr,
++				   PAGE_SIZE);
++		if (ret) {
++			dev_err(dev, "ipu6_mmu_map for pci_dma[%d] %pad failed",
++				i, &pci_dma_addr);
++			dma_unmap_page_attrs(&pdev->dev, pci_dma_addr,
++					     PAGE_SIZE, DMA_BIDIRECTIONAL,
++					     attrs);
++			goto out_unmap;
++		}
++	}
++
++	info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL);
++	if (!info->vaddr)
++		goto out_unmap;
++
++	*dma_handle = PFN_PHYS(iova->pfn_lo);
++
++	info->pages = pages;
++	info->ipu6_iova = *dma_handle;
++	info->size = size;
++	list_add(&info->list, &mmu->vma_list);
++
++	return info->vaddr;
++
++out_unmap:
++	while (i--) {
++		ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
++		pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
++						     ipu6_iova);
++		dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
++				     DMA_BIDIRECTIONAL, attrs);
++
++		ipu6_mmu_unmap(mmu->dmap->mmu_info, ipu6_iova, PAGE_SIZE);
++	}
++
++	__dma_free_buffer(dev, pages, size, attrs);
++
++out_free_iova:
++	__free_iova(&mmu->dmap->iovad, iova);
++out_kfree:
++	kfree(info);
++
++	return NULL;
++}
++
++static void ipu6_dma_free(struct device *dev, size_t size, void *vaddr,
++			  dma_addr_t dma_handle,
++			  unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
++	struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle));
++	dma_addr_t pci_dma_addr, ipu6_iova;
++	struct vm_info *info;
++	struct page **pages;
++	unsigned int i;
++
++	if (WARN_ON(!iova))
++		return;
++
++	info = get_vm_info(mmu, dma_handle);
++	if (WARN_ON(!info))
++		return;
++
++	if (WARN_ON(!info->vaddr))
++		return;
++
++	if (WARN_ON(!info->pages))
++		return;
++
++	list_del(&info->list);
++
++	size = PAGE_ALIGN(size);
++
++	pages = info->pages;
++
++	vunmap(vaddr);
++
++	for (i = 0; i < PHYS_PFN(size); i++) {
++		ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
++		pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
++						     ipu6_iova);
++		dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
++				     DMA_BIDIRECTIONAL, attrs);
++	}
++
++	ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
++		       PFN_PHYS(iova_size(iova)));
++
++	__dma_free_buffer(dev, pages, size, attrs);
++
++	mmu->tlb_invalidate(mmu);
++
++	__free_iova(&mmu->dmap->iovad, iova);
++
++	kfree(info);
++}
++
++static int ipu6_dma_mmap(struct device *dev, struct vm_area_struct *vma,
++			 void *addr, dma_addr_t iova, size_t size,
++			 unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	size_t count = PHYS_PFN(PAGE_ALIGN(size));
++	struct vm_info *info;
++	size_t i;
++	int ret;
++
++	info = get_vm_info(mmu, iova);
++	if (!info)
++		return -EFAULT;
++
++	if (!info->vaddr)
++		return -EFAULT;
++
++	if (vma->vm_start & ~PAGE_MASK)
++		return -EINVAL;
++
++	if (size > info->size)
++		return -EFAULT;
++
++	for (i = 0; i < count; i++) {
++		ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i),
++				     info->pages[i]);
++		if (ret < 0)
++			return ret;
++	}
++
++	return 0;
++}
++
++static void ipu6_dma_unmap_sg(struct device *dev,
++			      struct scatterlist *sglist,
++			      int nents, enum dma_data_direction dir,
++			      unsigned long attrs)
++{
++	struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct iova *iova = find_iova(&mmu->dmap->iovad,
++				      PHYS_PFN(sg_dma_address(sglist)));
++	int i, npages, count;
++	struct scatterlist *sg;
++	dma_addr_t pci_dma_addr;
++
++	if (!nents)
++		return;
++
++	if (WARN_ON(!iova))
++		return;
++
++	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
++		ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
++
++	/* get the nents as orig_nents given by caller */
++	count = 0;
++	npages = iova_size(iova);
++	for_each_sg(sglist, sg, nents, i) {
++		if (sg_dma_len(sg) == 0 ||
++		    sg_dma_address(sg) == DMA_MAPPING_ERROR)
++			break;
++
++		npages -= PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
++		count++;
++		if (npages <= 0)
++			break;
++	}
++
++	/*
++	 * Before IPU6 mmu unmap, return the pci dma address back to sg
++	 * assume the nents is less than orig_nents as the least granule
++	 * is 1 SZ_4K page
++	 */
++	dev_dbg(dev, "trying to unmap concatenated %u ents\n", count);
++	for_each_sg(sglist, sg, count, i) {
++		dev_dbg(dev, "ipu unmap sg[%d] %pad\n", i, &sg_dma_address(sg));
++		pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
++						     sg_dma_address(sg));
++		dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n",
++			&pci_dma_addr, i);
++		sg_dma_address(sg) = pci_dma_addr;
++	}
++
++	dev_dbg(dev, "ipu6_mmu_unmap low pfn %lu high pfn %lu\n",
++		iova->pfn_lo, iova->pfn_hi);
++	ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
++		       PFN_PHYS(iova_size(iova)));
++
++	mmu->tlb_invalidate(mmu);
++
++	dma_unmap_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
++
++	__free_iova(&mmu->dmap->iovad, iova);
++}
++
++static int ipu6_dma_map_sg(struct device *dev, struct scatterlist *sglist,
++			   int nents, enum dma_data_direction dir,
++			   unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
++	struct scatterlist *sg;
++	struct iova *iova;
++	size_t npages = 0;
++	unsigned long iova_addr;
++	int i, count;
++
++	for_each_sg(sglist, sg, nents, i) {
++		if (sg->offset) {
++			dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n",
++				i, sg->offset);
++			return -EFAULT;
++		}
++	}
++
++	dev_dbg(dev, "pci_dma_map_sg trying to map %d ents\n", nents);
++	count  = dma_map_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
++	if (count <= 0) {
++		dev_err(dev, "pci_dma_map_sg %d ents failed\n", nents);
++		return 0;
++	}
++
++	dev_dbg(dev, "pci_dma_map_sg %d ents mapped\n", count);
++
++	for_each_sg(sglist, sg, count, i)
++		npages += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
++
++	iova = alloc_iova(&mmu->dmap->iovad, npages,
++			  PHYS_PFN(dma_get_mask(dev)), 0);
++	if (!iova)
++		return 0;
++
++	dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo,
++		iova->pfn_hi);
++
++	iova_addr = iova->pfn_lo;
++	for_each_sg(sglist, sg, count, i) {
++		int ret;
++
++		dev_dbg(dev, "mapping entry %d: iova 0x%llx phy %pad size %d\n",
++			i, PFN_PHYS(iova_addr), &sg_dma_address(sg),
++			sg_dma_len(sg));
++
++		ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
++				   sg_dma_address(sg),
++				   PAGE_ALIGN(sg_dma_len(sg)));
++		if (ret)
++			goto out_fail;
++
++		sg_dma_address(sg) = PFN_PHYS(iova_addr);
++
++		iova_addr += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
++	}
++
++	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
++		ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
++
++	return count;
++
++out_fail:
++	ipu6_dma_unmap_sg(dev, sglist, i, dir, attrs);
++
++	return 0;
++}
++
++/*
++ * Create scatter-list for the already allocated DMA buffer
++ */
++static int ipu6_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
++				void *cpu_addr, dma_addr_t handle, size_t size,
++				unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct vm_info *info;
++	int n_pages;
++	int ret = 0;
++
++	info = get_vm_info(mmu, handle);
++	if (!info)
++		return -EFAULT;
++
++	if (!info->vaddr)
++		return -EFAULT;
++
++	if (WARN_ON(!info->pages))
++		return -ENOMEM;
++
++	n_pages = PHYS_PFN(PAGE_ALIGN(size));
++
++	ret = sg_alloc_table_from_pages(sgt, info->pages, n_pages, 0, size,
++					GFP_KERNEL);
++	if (ret)
++		dev_warn(dev, "IPU6 get sgt table failed\n");
++
++	return ret;
++}
++
++const struct dma_map_ops ipu6_dma_ops = {
++	.alloc = ipu6_dma_alloc,
++	.free = ipu6_dma_free,
++	.mmap = ipu6_dma_mmap,
++	.map_sg = ipu6_dma_map_sg,
++	.unmap_sg = ipu6_dma_unmap_sg,
++	.sync_single_for_cpu = ipu6_dma_sync_single_for_cpu,
++	.sync_single_for_device = ipu6_dma_sync_single_for_cpu,
++	.sync_sg_for_cpu = ipu6_dma_sync_sg_for_cpu,
++	.sync_sg_for_device = ipu6_dma_sync_sg_for_cpu,
++	.get_sgtable = ipu6_dma_get_sgtable,
++};
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.h b/drivers/media/pci/intel/ipu6/ipu6-dma.h
+new file mode 100644
+index 000000000000..c75ad2462368
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-dma.h
+@@ -0,0 +1,19 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_DMA_H
++#define IPU6_DMA_H
++
++#include <linux/dma-map-ops.h>
++#include <linux/iova.h>
++
++struct ipu6_mmu_info;
++
++struct ipu6_dma_mapping {
++	struct ipu6_mmu_info *mmu_info;
++	struct iova_domain iovad;
++};
++
++extern const struct dma_map_ops ipu6_dma_ops;
++
++#endif /* IPU6_DMA_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.c b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
+new file mode 100644
+index 000000000000..dc16d45187a8
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
+@@ -0,0 +1,845 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++#include <asm/barrier.h>
++
++#include <linux/align.h>
++#include <linux/atomic.h>
++#include <linux/bitops.h>
++#include <linux/bits.h>
++#include <linux/bug.h>
++#include <linux/cacheflush.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/gfp.h>
++#include <linux/io.h>
++#include <linux/iova.h>
++#include <linux/math.h>
++#include <linux/minmax.h>
++#include <linux/mm.h>
++#include <linux/pfn.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++
++#include "ipu6.h"
++#include "ipu6-dma.h"
++#include "ipu6-mmu.h"
++#include "ipu6-platform-regs.h"
++
++#define ISP_PAGE_SHIFT		12
++#define ISP_PAGE_SIZE		BIT(ISP_PAGE_SHIFT)
++#define ISP_PAGE_MASK		(~(ISP_PAGE_SIZE - 1))
++
++#define ISP_L1PT_SHIFT		22
++#define ISP_L1PT_MASK		(~((1U << ISP_L1PT_SHIFT) - 1))
++
++#define ISP_L2PT_SHIFT		12
++#define ISP_L2PT_MASK		(~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK))))
++
++#define ISP_L1PT_PTES           1024
++#define ISP_L2PT_PTES           1024
++
++#define ISP_PADDR_SHIFT		12
++
++#define REG_TLB_INVALIDATE	0x0000
++
++#define REG_L1_PHYS		0x0004	/* 27-bit pfn */
++#define REG_INFO		0x0008
++
++#define TBL_PHYS_ADDR(a)	((phys_addr_t)(a) << ISP_PADDR_SHIFT)
++
++static void tlb_invalidate(struct ipu6_mmu *mmu)
++{
++	unsigned long flags;
++	unsigned int i;
++
++	spin_lock_irqsave(&mmu->ready_lock, flags);
++	if (!mmu->ready) {
++		spin_unlock_irqrestore(&mmu->ready_lock, flags);
++		return;
++	}
++
++	for (i = 0; i < mmu->nr_mmus; i++) {
++		/*
++		 * To avoid the HW bug induced dead lock in some of the IPU6
++		 * MMUs on successive invalidate calls, we need to first do a
++		 * read to the page table base before writing the invalidate
++		 * register. MMUs which need to implement this WA, will have
++		 * the insert_read_before_invalidate flags set as true.
++		 * Disregard the return value of the read.
++		 */
++		if (mmu->mmu_hw[i].insert_read_before_invalidate)
++			readl(mmu->mmu_hw[i].base + REG_L1_PHYS);
++
++		writel(0xffffffff, mmu->mmu_hw[i].base +
++		       REG_TLB_INVALIDATE);
++		/*
++		 * The TLB invalidation is a "single cycle" (IOMMU clock cycles)
++		 * When the actual MMIO write reaches the IPU6 TLB Invalidate
++		 * register, wmb() will force the TLB invalidate out if the CPU
++		 * attempts to update the IOMMU page table (or sooner).
++		 */
++		wmb();
++	}
++	spin_unlock_irqrestore(&mmu->ready_lock, flags);
++}
++
++#ifdef DEBUG
++static void page_table_dump(struct ipu6_mmu_info *mmu_info)
++{
++	u32 l1_idx;
++
++	dev_dbg(mmu_info->dev, "begin IOMMU page table dump\n");
++
++	for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) {
++		u32 l2_idx;
++		u32 iova = (phys_addr_t)l1_idx << ISP_L1PT_SHIFT;
++
++		if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval)
++			continue;
++		dev_dbg(mmu_info->dev,
++			"l1 entry %u; iovas 0x%8.8x-0x%8.8x, at %pa\n",
++			l1_idx, iova, iova + ISP_PAGE_SIZE,
++			TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]));
++
++		for (l2_idx = 0; l2_idx < ISP_L2PT_PTES; l2_idx++) {
++			u32 *l2_pt = mmu_info->l2_pts[l1_idx];
++			u32 iova2 = iova + (l2_idx << ISP_L2PT_SHIFT);
++
++			if (l2_pt[l2_idx] == mmu_info->dummy_page_pteval)
++				continue;
++
++			dev_dbg(mmu_info->dev,
++				"\tl2 entry %u; iova 0x%8.8x, phys %pa\n",
++				l2_idx, iova2,
++				TBL_PHYS_ADDR(l2_pt[l2_idx]));
++		}
++	}
++
++	dev_dbg(mmu_info->dev, "end IOMMU page table dump\n");
++}
++#endif /* DEBUG */
++
++static dma_addr_t map_single(struct ipu6_mmu_info *mmu_info, void *ptr)
++{
++	dma_addr_t dma;
++
++	dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL);
++	if (dma_mapping_error(mmu_info->dev, dma))
++		return 0;
++
++	return dma;
++}
++
++static int get_dummy_page(struct ipu6_mmu_info *mmu_info)
++{
++	void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
++	dma_addr_t dma;
++
++	if (!pt)
++		return -ENOMEM;
++
++	dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt);
++
++	dma = map_single(mmu_info, pt);
++	if (!dma) {
++		dev_err(mmu_info->dev, "Failed to map dummy page\n");
++		goto err_free_page;
++	}
++
++	mmu_info->dummy_page = pt;
++	mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT;
++
++	return 0;
++
++err_free_page:
++	free_page((unsigned long)pt);
++	return -ENOMEM;
++}
++
++static void free_dummy_page(struct ipu6_mmu_info *mmu_info)
++{
++	dma_unmap_single(mmu_info->dev,
++			 TBL_PHYS_ADDR(mmu_info->dummy_page_pteval),
++			 PAGE_SIZE, DMA_BIDIRECTIONAL);
++	free_page((unsigned long)mmu_info->dummy_page);
++}
++
++static int alloc_dummy_l2_pt(struct ipu6_mmu_info *mmu_info)
++{
++	u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
++	dma_addr_t dma;
++	unsigned int i;
++
++	if (!pt)
++		return -ENOMEM;
++
++	dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt);
++
++	dma = map_single(mmu_info, pt);
++	if (!dma) {
++		dev_err(mmu_info->dev, "Failed to map l2pt page\n");
++		goto err_free_page;
++	}
++
++	for (i = 0; i < ISP_L2PT_PTES; i++)
++		pt[i] = mmu_info->dummy_page_pteval;
++
++	mmu_info->dummy_l2_pt = pt;
++	mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT;
++
++	return 0;
++
++err_free_page:
++	free_page((unsigned long)pt);
++	return -ENOMEM;
++}
++
++static void free_dummy_l2_pt(struct ipu6_mmu_info *mmu_info)
++{
++	dma_unmap_single(mmu_info->dev,
++			 TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval),
++			 PAGE_SIZE, DMA_BIDIRECTIONAL);
++	free_page((unsigned long)mmu_info->dummy_l2_pt);
++}
++
++static u32 *alloc_l1_pt(struct ipu6_mmu_info *mmu_info)
++{
++	u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
++	dma_addr_t dma;
++	unsigned int i;
++
++	if (!pt)
++		return NULL;
++
++	dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt);
++
++	for (i = 0; i < ISP_L1PT_PTES; i++)
++		pt[i] = mmu_info->dummy_l2_pteval;
++
++	dma = map_single(mmu_info, pt);
++	if (!dma) {
++		dev_err(mmu_info->dev, "Failed to map l1pt page\n");
++		goto err_free_page;
++	}
++
++	mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT;
++	dev_dbg(mmu_info->dev, "l1 pt %p mapped at %llx\n", pt, dma);
++
++	return pt;
++
++err_free_page:
++	free_page((unsigned long)pt);
++	return NULL;
++}
++
++static u32 *alloc_l2_pt(struct ipu6_mmu_info *mmu_info)
++{
++	u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
++	unsigned int i;
++
++	if (!pt)
++		return NULL;
++
++	dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt);
++
++	for (i = 0; i < ISP_L1PT_PTES; i++)
++		pt[i] = mmu_info->dummy_page_pteval;
++
++	return pt;
++}
++
++static int l2_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		  phys_addr_t paddr, size_t size)
++{
++	u32 l1_idx = iova >> ISP_L1PT_SHIFT;
++	u32 iova_start = iova;
++	u32 *l2_pt, *l2_virt;
++	unsigned int l2_idx;
++	unsigned long flags;
++	dma_addr_t dma;
++	u32 l1_entry;
++
++	dev_dbg(mmu_info->dev,
++		"mapping l2 page table for l1 index %u (iova %8.8x)\n",
++		l1_idx, (u32)iova);
++
++	spin_lock_irqsave(&mmu_info->lock, flags);
++	l1_entry = mmu_info->l1_pt[l1_idx];
++	if (l1_entry == mmu_info->dummy_l2_pteval) {
++		l2_virt = mmu_info->l2_pts[l1_idx];
++		if (likely(!l2_virt)) {
++			l2_virt = alloc_l2_pt(mmu_info);
++			if (!l2_virt) {
++				spin_unlock_irqrestore(&mmu_info->lock, flags);
++				return -ENOMEM;
++			}
++		}
++
++		dma = map_single(mmu_info, l2_virt);
++		if (!dma) {
++			dev_err(mmu_info->dev, "Failed to map l2pt page\n");
++			free_page((unsigned long)l2_virt);
++			spin_unlock_irqrestore(&mmu_info->lock, flags);
++			return -EINVAL;
++		}
++
++		l1_entry = dma >> ISP_PADDR_SHIFT;
++
++		dev_dbg(mmu_info->dev, "page for l1_idx %u %p allocated\n",
++			l1_idx, l2_virt);
++		mmu_info->l1_pt[l1_idx] = l1_entry;
++		mmu_info->l2_pts[l1_idx] = l2_virt;
++		clflush_cache_range((void *)&mmu_info->l1_pt[l1_idx],
++				    sizeof(mmu_info->l1_pt[l1_idx]));
++	}
++
++	l2_pt = mmu_info->l2_pts[l1_idx];
++
++	dev_dbg(mmu_info->dev, "l2_pt at %p with dma 0x%x\n", l2_pt, l1_entry);
++
++	paddr = ALIGN(paddr, ISP_PAGE_SIZE);
++
++	l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
++
++	dev_dbg(mmu_info->dev, "l2_idx %u, phys 0x%8.8x\n", l2_idx,
++		l2_pt[l2_idx]);
++	if (l2_pt[l2_idx] != mmu_info->dummy_page_pteval) {
++		spin_unlock_irqrestore(&mmu_info->lock, flags);
++		return -EINVAL;
++	}
++
++	l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT;
++
++	clflush_cache_range((void *)&l2_pt[l2_idx], sizeof(l2_pt[l2_idx]));
++	spin_unlock_irqrestore(&mmu_info->lock, flags);
++
++	dev_dbg(mmu_info->dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx,
++		l2_pt[l2_idx]);
++
++	return 0;
++}
++
++static int __ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++			  phys_addr_t paddr, size_t size)
++{
++	u32 iova_start = round_down(iova, ISP_PAGE_SIZE);
++	u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE);
++
++	dev_dbg(mmu_info->dev,
++		"mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr 0x%10.10llx\n",
++		iova_start, iova_end, size, paddr);
++
++	return l2_map(mmu_info, iova_start, paddr, size);
++}
++
++static size_t l2_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		       phys_addr_t dummy, size_t size)
++{
++	u32 l1_idx = iova >> ISP_L1PT_SHIFT;
++	u32 iova_start = iova;
++	unsigned int l2_idx;
++	size_t unmapped = 0;
++	unsigned long flags;
++	u32 *l2_pt;
++
++	dev_dbg(mmu_info->dev, "unmapping l2 page table for l1 index %u (iova 0x%8.8lx)\n",
++		l1_idx, iova);
++
++	spin_lock_irqsave(&mmu_info->lock, flags);
++	if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) {
++		spin_unlock_irqrestore(&mmu_info->lock, flags);
++		dev_err(mmu_info->dev,
++			"unmap iova 0x%8.8lx l1 idx %u which was not mapped\n",
++			iova, l1_idx);
++		return 0;
++	}
++
++	for (l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
++	     (iova_start & ISP_L1PT_MASK) + (l2_idx << ISP_PAGE_SHIFT)
++		     < iova_start + size && l2_idx < ISP_L2PT_PTES; l2_idx++) {
++		l2_pt = mmu_info->l2_pts[l1_idx];
++		dev_dbg(mmu_info->dev,
++			"unmap l2 index %u with pteval 0x%10.10llx\n",
++			l2_idx, TBL_PHYS_ADDR(l2_pt[l2_idx]));
++		l2_pt[l2_idx] = mmu_info->dummy_page_pteval;
++
++		clflush_cache_range((void *)&l2_pt[l2_idx],
++				    sizeof(l2_pt[l2_idx]));
++		unmapped++;
++	}
++	spin_unlock_irqrestore(&mmu_info->lock, flags);
++
++	return unmapped << ISP_PAGE_SHIFT;
++}
++
++static size_t __ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info,
++			       unsigned long iova, size_t size)
++{
++	return l2_unmap(mmu_info, iova, 0, size);
++}
++
++static int allocate_trash_buffer(struct ipu6_mmu *mmu)
++{
++	unsigned int n_pages = PHYS_PFN(PAGE_ALIGN(IPU6_MMUV2_TRASH_RANGE));
++	struct iova *iova;
++	unsigned int i;
++	dma_addr_t dma;
++	unsigned long iova_addr;
++	int ret;
++
++	/* Allocate 8MB in iova range */
++	iova = alloc_iova(&mmu->dmap->iovad, n_pages,
++			  PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0);
++	if (!iova) {
++		dev_err(mmu->dev, "cannot allocate iova range for trash\n");
++		return -ENOMEM;
++	}
++
++	dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0,
++			   PAGE_SIZE, DMA_BIDIRECTIONAL);
++	if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) {
++		dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n");
++		ret = -ENOMEM;
++		goto out_free_iova;
++	}
++
++	mmu->pci_trash_page = dma;
++
++	/*
++	 * Map the 8MB iova address range to the same physical trash page
++	 * mmu->trash_page which is already reserved at the probe
++	 */
++	iova_addr = iova->pfn_lo;
++	for (i = 0; i < n_pages; i++) {
++		ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
++				   mmu->pci_trash_page, PAGE_SIZE);
++		if (ret) {
++			dev_err(mmu->dev,
++				"mapping trash buffer range failed\n");
++			goto out_unmap;
++		}
++
++		iova_addr++;
++	}
++
++	mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo);
++	dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n",
++		mmu->mmid, (unsigned int)mmu->iova_trash_page);
++	return 0;
++
++out_unmap:
++	ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
++		       PFN_PHYS(iova_size(iova)));
++	dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page,
++		       PAGE_SIZE, DMA_BIDIRECTIONAL);
++out_free_iova:
++	__free_iova(&mmu->dmap->iovad, iova);
++	return ret;
++}
++
++int ipu6_mmu_hw_init(struct ipu6_mmu *mmu)
++{
++	struct ipu6_mmu_info *mmu_info;
++	unsigned long flags;
++	unsigned int i;
++
++	mmu_info = mmu->dmap->mmu_info;
++
++	/* Initialise the each MMU HW block */
++	for (i = 0; i < mmu->nr_mmus; i++) {
++		struct ipu6_mmu_hw *mmu_hw = &mmu->mmu_hw[i];
++		unsigned int j;
++		u16 block_addr;
++
++		/* Write page table address per MMU */
++		writel((phys_addr_t)mmu_info->l1_pt_dma,
++		       mmu->mmu_hw[i].base + REG_L1_PHYS);
++
++		/* Set info bits per MMU */
++		writel(mmu->mmu_hw[i].info_bits,
++		       mmu->mmu_hw[i].base + REG_INFO);
++
++		/* Configure MMU TLB stream configuration for L1 */
++		for (j = 0, block_addr = 0; j < mmu_hw->nr_l1streams;
++		     block_addr += mmu->mmu_hw[i].l1_block_sz[j], j++) {
++			if (block_addr > IPU6_MAX_LI_BLOCK_ADDR) {
++				dev_err(mmu->dev, "invalid L1 configuration\n");
++				return -EINVAL;
++			}
++
++			/* Write block start address for each streams */
++			writel(block_addr, mmu_hw->base +
++			       mmu_hw->l1_stream_id_reg_offset + 4 * j);
++		}
++
++		/* Configure MMU TLB stream configuration for L2 */
++		for (j = 0, block_addr = 0; j < mmu_hw->nr_l2streams;
++		     block_addr += mmu->mmu_hw[i].l2_block_sz[j], j++) {
++			if (block_addr > IPU6_MAX_L2_BLOCK_ADDR) {
++				dev_err(mmu->dev, "invalid L2 configuration\n");
++				return -EINVAL;
++			}
++
++			writel(block_addr, mmu_hw->base +
++			       mmu_hw->l2_stream_id_reg_offset + 4 * j);
++		}
++	}
++
++	if (!mmu->trash_page) {
++		int ret;
++
++		mmu->trash_page = alloc_page(GFP_KERNEL);
++		if (!mmu->trash_page) {
++			dev_err(mmu->dev, "insufficient memory for trash buffer\n");
++			return -ENOMEM;
++		}
++
++		ret = allocate_trash_buffer(mmu);
++		if (ret) {
++			__free_page(mmu->trash_page);
++			mmu->trash_page = NULL;
++			dev_err(mmu->dev, "trash buffer allocation failed\n");
++			return ret;
++		}
++	}
++
++	spin_lock_irqsave(&mmu->ready_lock, flags);
++	mmu->ready = true;
++	spin_unlock_irqrestore(&mmu->ready_lock, flags);
++
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, INTEL_IPU6);
++
++static struct ipu6_mmu_info *ipu6_mmu_alloc(struct ipu6_device *isp)
++{
++	struct ipu6_mmu_info *mmu_info;
++	int ret;
++
++	mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL);
++	if (!mmu_info)
++		return NULL;
++
++	mmu_info->aperture_start = 0;
++	mmu_info->aperture_end = DMA_BIT_MASK(isp->secure_mode ?
++					      IPU6_MMU_ADDR_BITS :
++					      IPU6_MMU_ADDR_BITS_NON_SECURE);
++	mmu_info->pgsize_bitmap = SZ_4K;
++	mmu_info->dev = &isp->pdev->dev;
++
++	ret = get_dummy_page(mmu_info);
++	if (ret)
++		goto err_free_info;
++
++	ret = alloc_dummy_l2_pt(mmu_info);
++	if (ret)
++		goto err_free_dummy_page;
++
++	mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts));
++	if (!mmu_info->l2_pts)
++		goto err_free_dummy_l2_pt;
++
++	/*
++	 * We always map the L1 page table (a single page as well as
++	 * the L2 page tables).
++	 */
++	mmu_info->l1_pt = alloc_l1_pt(mmu_info);
++	if (!mmu_info->l1_pt)
++		goto err_free_l2_pts;
++
++	spin_lock_init(&mmu_info->lock);
++
++	dev_dbg(mmu_info->dev, "domain initialised\n");
++
++	return mmu_info;
++
++err_free_l2_pts:
++	vfree(mmu_info->l2_pts);
++err_free_dummy_l2_pt:
++	free_dummy_l2_pt(mmu_info);
++err_free_dummy_page:
++	free_dummy_page(mmu_info);
++err_free_info:
++	kfree(mmu_info);
++
++	return NULL;
++}
++
++void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&mmu->ready_lock, flags);
++	mmu->ready = false;
++	spin_unlock_irqrestore(&mmu->ready_lock, flags);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, INTEL_IPU6);
++
++static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp)
++{
++	struct ipu6_dma_mapping *dmap;
++
++	dmap = kzalloc(sizeof(*dmap), GFP_KERNEL);
++	if (!dmap)
++		return NULL;
++
++	dmap->mmu_info = ipu6_mmu_alloc(isp);
++	if (!dmap->mmu_info) {
++		kfree(dmap);
++		return NULL;
++	}
++
++	init_iova_domain(&dmap->iovad, SZ_4K, 1);
++	dmap->mmu_info->dmap = dmap;
++
++	dev_dbg(&isp->pdev->dev, "alloc mapping\n");
++
++	iova_cache_get();
++
++	return dmap;
++}
++
++phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info,
++				  dma_addr_t iova)
++{
++	phys_addr_t phy_addr;
++	unsigned long flags;
++	u32 *l2_pt;
++
++	spin_lock_irqsave(&mmu_info->lock, flags);
++	l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT];
++	phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT];
++	phy_addr <<= ISP_PAGE_SHIFT;
++	spin_unlock_irqrestore(&mmu_info->lock, flags);
++
++	return phy_addr;
++}
++
++static size_t ipu6_mmu_pgsize(unsigned long pgsize_bitmap,
++			      unsigned long addr_merge, size_t size)
++{
++	unsigned int pgsize_idx;
++	size_t pgsize;
++
++	/* Max page size that still fits into 'size' */
++	pgsize_idx = __fls(size);
++
++	if (likely(addr_merge)) {
++		/* Max page size allowed by address */
++		unsigned int align_pgsize_idx = __ffs(addr_merge);
++
++		pgsize_idx = min(pgsize_idx, align_pgsize_idx);
++	}
++
++	pgsize = (1UL << (pgsize_idx + 1)) - 1;
++	pgsize &= pgsize_bitmap;
++
++	WARN_ON(!pgsize);
++
++	/* pick the biggest page */
++	pgsize_idx = __fls(pgsize);
++	pgsize = 1UL << pgsize_idx;
++
++	return pgsize;
++}
++
++size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		      size_t size)
++{
++	size_t unmapped_page, unmapped = 0;
++	unsigned int min_pagesz;
++
++	/* find out the minimum page size supported */
++	min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap);
++
++	/*
++	 * The virtual address and the size of the mapping must be
++	 * aligned (at least) to the size of the smallest page supported
++	 * by the hardware
++	 */
++	if (!IS_ALIGNED(iova | size, min_pagesz)) {
++		dev_err(NULL, "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
++			iova, size, min_pagesz);
++		return -EINVAL;
++	}
++
++	/*
++	 * Keep iterating until we either unmap 'size' bytes (or more)
++	 * or we hit an area that isn't mapped.
++	 */
++	while (unmapped < size) {
++		size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap,
++						iova, size - unmapped);
++
++		unmapped_page = __ipu6_mmu_unmap(mmu_info, iova, pgsize);
++		if (!unmapped_page)
++			break;
++
++		dev_dbg(mmu_info->dev, "unmapped: iova 0x%lx size 0x%zx\n",
++			iova, unmapped_page);
++
++		iova += unmapped_page;
++		unmapped += unmapped_page;
++	}
++
++	return unmapped;
++}
++
++int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		 phys_addr_t paddr, size_t size)
++{
++	unsigned long orig_iova = iova;
++	unsigned int min_pagesz;
++	size_t orig_size = size;
++	int ret = 0;
++
++	if (mmu_info->pgsize_bitmap == 0UL)
++		return -ENODEV;
++
++	/* find out the minimum page size supported */
++	min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap);
++
++	/*
++	 * both the virtual address and the physical one, as well as
++	 * the size of the mapping, must be aligned (at least) to the
++	 * size of the smallest page supported by the hardware
++	 */
++	if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
++		dev_err(mmu_info->dev,
++			"unaligned: iova %lx pa %pa size %zx min_pagesz %x\n",
++			iova, &paddr, size, min_pagesz);
++		return -EINVAL;
++	}
++
++	dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n",
++		iova, &paddr, size);
++
++	while (size) {
++		size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap,
++						iova | paddr, size);
++
++		dev_dbg(mmu_info->dev,
++			"mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
++			iova, &paddr, pgsize);
++
++		ret = __ipu6_mmu_map(mmu_info, iova, paddr, pgsize);
++		if (ret)
++			break;
++
++		iova += pgsize;
++		paddr += pgsize;
++		size -= pgsize;
++	}
++
++	/* unroll mapping in case something went wrong */
++	if (ret)
++		ipu6_mmu_unmap(mmu_info, orig_iova, orig_size - size);
++
++	return ret;
++}
++
++static void ipu6_mmu_destroy(struct ipu6_mmu *mmu)
++{
++	struct ipu6_dma_mapping *dmap = mmu->dmap;
++	struct ipu6_mmu_info *mmu_info = dmap->mmu_info;
++	struct iova *iova;
++	u32 l1_idx;
++
++	if (mmu->iova_trash_page) {
++		iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page));
++		if (iova) {
++			/* unmap and free the trash buffer iova */
++			ipu6_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo),
++				       PFN_PHYS(iova_size(iova)));
++			__free_iova(&dmap->iovad, iova);
++		} else {
++			dev_err(mmu->dev, "trash buffer iova not found.\n");
++		}
++
++		mmu->iova_trash_page = 0;
++		dma_unmap_page(mmu_info->dev, mmu->pci_trash_page,
++			       PAGE_SIZE, DMA_BIDIRECTIONAL);
++		mmu->pci_trash_page = 0;
++		__free_page(mmu->trash_page);
++	}
++
++	for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) {
++		if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) {
++			dma_unmap_single(mmu_info->dev,
++					 TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]),
++					 PAGE_SIZE, DMA_BIDIRECTIONAL);
++			free_page((unsigned long)mmu_info->l2_pts[l1_idx]);
++		}
++	}
++
++	vfree(mmu_info->l2_pts);
++	free_dummy_page(mmu_info);
++	dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma),
++			 PAGE_SIZE, DMA_BIDIRECTIONAL);
++	free_page((unsigned long)mmu_info->dummy_l2_pt);
++	free_page((unsigned long)mmu_info->l1_pt);
++	kfree(mmu_info);
++}
++
++struct ipu6_mmu *ipu6_mmu_init(struct device *dev,
++			       void __iomem *base, int mmid,
++			       const struct ipu6_hw_variants *hw)
++{
++	struct ipu6_device *isp = pci_get_drvdata(to_pci_dev(dev));
++	struct ipu6_mmu_pdata *pdata;
++	struct ipu6_mmu *mmu;
++	unsigned int i;
++
++	if (hw->nr_mmus > IPU6_MMU_MAX_DEVICES)
++		return ERR_PTR(-EINVAL);
++
++	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
++	if (!pdata)
++		return ERR_PTR(-ENOMEM);
++
++	for (i = 0; i < hw->nr_mmus; i++) {
++		struct ipu6_mmu_hw *pdata_mmu = &pdata->mmu_hw[i];
++		const struct ipu6_mmu_hw *src_mmu = &hw->mmu_hw[i];
++
++		if (src_mmu->nr_l1streams > IPU6_MMU_MAX_TLB_L1_STREAMS ||
++		    src_mmu->nr_l2streams > IPU6_MMU_MAX_TLB_L2_STREAMS)
++			return ERR_PTR(-EINVAL);
++
++		*pdata_mmu = *src_mmu;
++		pdata_mmu->base = base + src_mmu->offset;
++	}
++
++	mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL);
++	if (!mmu)
++		return ERR_PTR(-ENOMEM);
++
++	mmu->mmid = mmid;
++	mmu->mmu_hw = pdata->mmu_hw;
++	mmu->nr_mmus = hw->nr_mmus;
++	mmu->tlb_invalidate = tlb_invalidate;
++	mmu->ready = false;
++	INIT_LIST_HEAD(&mmu->vma_list);
++	spin_lock_init(&mmu->ready_lock);
++
++	mmu->dmap = alloc_dma_mapping(isp);
++	if (!mmu->dmap) {
++		dev_err(dev, "can't alloc dma mapping\n");
++		return ERR_PTR(-ENOMEM);
++	}
++
++	return mmu;
++}
++
++void ipu6_mmu_cleanup(struct ipu6_mmu *mmu)
++{
++	struct ipu6_dma_mapping *dmap = mmu->dmap;
++
++	ipu6_mmu_destroy(mmu);
++	mmu->dmap = NULL;
++	iova_cache_put();
++	put_iova_domain(&dmap->iovad);
++	kfree(dmap);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.h b/drivers/media/pci/intel/ipu6/ipu6-mmu.h
+new file mode 100644
+index 000000000000..95df7931a2e5
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.h
+@@ -0,0 +1,73 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_MMU_H
++#define IPU6_MMU_H
++
++#define ISYS_MMID 1
++#define PSYS_MMID 0
++
++#include <linux/list.h>
++#include <linux/spinlock_types.h>
++#include <linux/types.h>
++
++struct device;
++struct page;
++struct ipu6_hw_variants;
++
++struct ipu6_mmu_info {
++	struct device *dev;
++
++	u32 *l1_pt;
++	u32 l1_pt_dma;
++	u32 **l2_pts;
++
++	u32 *dummy_l2_pt;
++	u32 dummy_l2_pteval;
++	void *dummy_page;
++	u32 dummy_page_pteval;
++
++	dma_addr_t aperture_start;
++	dma_addr_t aperture_end;
++	unsigned long pgsize_bitmap;
++
++	spinlock_t lock;	/* Serialize access to users */
++	struct ipu6_dma_mapping *dmap;
++};
++
++struct ipu6_mmu {
++	struct list_head node;
++
++	struct ipu6_mmu_hw *mmu_hw;
++	unsigned int nr_mmus;
++	unsigned int mmid;
++
++	phys_addr_t pgtbl;
++	struct device *dev;
++
++	struct ipu6_dma_mapping *dmap;
++	struct list_head vma_list;
++
++	struct page *trash_page;
++	dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */
++	dma_addr_t iova_trash_page; /* IOVA for IPU6 child nodes to use */
++
++	bool ready;
++	spinlock_t ready_lock;	/* Serialize access to bool ready */
++
++	void (*tlb_invalidate)(struct ipu6_mmu *mmu);
++};
++
++struct ipu6_mmu *ipu6_mmu_init(struct device *dev,
++			       void __iomem *base, int mmid,
++			       const struct ipu6_hw_variants *hw);
++void ipu6_mmu_cleanup(struct ipu6_mmu *mmu);
++int ipu6_mmu_hw_init(struct ipu6_mmu *mmu);
++void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu);
++int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		 phys_addr_t paddr, size_t size);
++size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		      size_t size);
++phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info,
++				  dma_addr_t iova);
++#endif
+-- 
+2.43.2
+
+
+From a4363013580d8a552e8084faedbb98bd2482c057 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:20 +0800
+Subject: [PATCH 13/33] media: intel/ipu6: add syscom interfaces between
+ firmware and driver
+
+Syscom is an inter-process(or) communication mechanism between an IPU
+and host. Syscom uses message queues for message exchange between IPU
+and host. Each message queue has its consumer and producer, host queue
+messages to firmware as the producer and then firmware to dequeue the
+messages as consumer and vice versa. IPU and host use shared registers
+or memory to reside the read and write indices which are updated by
+consumer and producer.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-fw-com.c | 413 +++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-fw-com.h |  47 +++
+ 2 files changed, 460 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-com.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-com.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-com.c b/drivers/media/pci/intel/ipu6/ipu6-fw-com.c
+new file mode 100644
+index 000000000000..0f893f44e04c
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-fw-com.c
+@@ -0,0 +1,413 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/io.h>
++#include <linux/math.h>
++#include <linux/overflow.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-fw-com.h"
++
++/*
++ * FWCOM layer is a shared resource between FW and driver. It consist
++ * of token queues to both send and receive directions. Queue is simply
++ * an array of structures with read and write indexes to the queue.
++ * There are 1...n queues to both directions. Queues locates in
++ * system RAM and are mapped to ISP MMU so that both CPU and ISP can
++ * see the same buffer. Indexes are located in ISP DMEM so that FW code
++ * can poll those with very low latency and cost. CPU access to indexes is
++ * more costly but that happens only at message sending time and
++ * interrupt triggered message handling. CPU doesn't need to poll indexes.
++ * wr_reg / rd_reg are offsets to those dmem location. They are not
++ * the indexes itself.
++ */
++
++/* Shared structure between driver and FW - do not modify */
++struct ipu6_fw_sys_queue {
++	u64 host_address;
++	u32 vied_address;
++	u32 size;
++	u32 token_size;
++	u32 wr_reg;	/* reg number in subsystem's regmem */
++	u32 rd_reg;
++	u32 _align;
++} __packed;
++
++struct ipu6_fw_sys_queue_res {
++	u64 host_address;
++	u32 vied_address;
++	u32 reg;
++} __packed;
++
++enum syscom_state {
++	/* Program load or explicit host setting should init to this */
++	SYSCOM_STATE_UNINIT = 0x57a7e000,
++	/* SP Syscom sets this when it is ready for use */
++	SYSCOM_STATE_READY = 0x57a7e001,
++	/* SP Syscom sets this when no more syscom accesses will happen */
++	SYSCOM_STATE_INACTIVE = 0x57a7e002,
++};
++
++enum syscom_cmd {
++	/* Program load or explicit host setting should init to this */
++	SYSCOM_COMMAND_UNINIT = 0x57a7f000,
++	/* Host Syscom requests syscom to become inactive */
++	SYSCOM_COMMAND_INACTIVE = 0x57a7f001,
++};
++
++/* firmware config: data that sent from the host to SP via DDR */
++/* Cell copies data into a context */
++
++struct ipu6_fw_syscom_config {
++	u32 firmware_address;
++
++	u32 num_input_queues;
++	u32 num_output_queues;
++
++	/* ISP pointers to an array of ipu6_fw_sys_queue structures */
++	u32 input_queue;
++	u32 output_queue;
++
++	/* ISYS / PSYS private data */
++	u32 specific_addr;
++	u32 specific_size;
++};
++
++struct ipu6_fw_com_context {
++	struct ipu6_bus_device *adev;
++	void __iomem *dmem_addr;
++	int (*cell_ready)(struct ipu6_bus_device *adev);
++	void (*cell_start)(struct ipu6_bus_device *adev);
++
++	void *dma_buffer;
++	dma_addr_t dma_addr;
++	unsigned int dma_size;
++	unsigned long attrs;
++
++	struct ipu6_fw_sys_queue *input_queue;	/* array of host to SP queues */
++	struct ipu6_fw_sys_queue *output_queue;	/* array of SP to host */
++
++	u32 config_vied_addr;
++
++	unsigned int buttress_boot_offset;
++	void __iomem *base_addr;
++};
++
++#define FW_COM_WR_REG 0
++#define FW_COM_RD_REG 4
++
++#define REGMEM_OFFSET 0
++#define TUNIT_MAGIC_PATTERN 0x5a5a5a5a
++
++enum regmem_id {
++	/* pass pkg_dir address to SPC in non-secure mode */
++	PKG_DIR_ADDR_REG = 0,
++	/* Tunit CFG blob for secure - provided by host.*/
++	TUNIT_CFG_DWR_REG = 1,
++	/* syscom commands - modified by the host */
++	SYSCOM_COMMAND_REG = 2,
++	/* Store interrupt status - updated by SP */
++	SYSCOM_IRQ_REG = 3,
++	/* first syscom queue pointer register */
++	SYSCOM_QPR_BASE_REG = 4
++};
++
++#define BUTTRESS_FW_BOOT_PARAMS_0 0x4000
++#define BUTTRESS_FW_BOOT_PARAM_REG(base, offset, id)			\
++	((base) + BUTTRESS_FW_BOOT_PARAMS_0 + ((offset) + (id)) * 4)
++
++enum buttress_syscom_id {
++	/* pass syscom configuration to SPC */
++	SYSCOM_CONFIG_ID		= 0,
++	/* syscom state - modified by SP */
++	SYSCOM_STATE_ID			= 1,
++	/* syscom vtl0 addr mask */
++	SYSCOM_VTL0_ADDR_MASK_ID	= 2,
++	SYSCOM_ID_MAX
++};
++
++static void ipu6_sys_queue_init(struct ipu6_fw_sys_queue *q, unsigned int size,
++				unsigned int token_size,
++				struct ipu6_fw_sys_queue_res *res)
++{
++	unsigned int buf_size = (size + 1) * token_size;
++
++	q->size = size + 1;
++	q->token_size = token_size;
++
++	/* acquire the shared buffer space */
++	q->host_address = res->host_address;
++	res->host_address += buf_size;
++	q->vied_address = res->vied_address;
++	res->vied_address += buf_size;
++
++	/* acquire the shared read and writer pointers */
++	q->wr_reg = res->reg;
++	res->reg++;
++	q->rd_reg = res->reg;
++	res->reg++;
++}
++
++void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
++			  struct ipu6_bus_device *adev, void __iomem *base)
++{
++	size_t conf_size, inq_size, outq_size, specific_size;
++	struct ipu6_fw_syscom_config *config_host_addr;
++	unsigned int sizeinput = 0, sizeoutput = 0;
++	struct ipu6_fw_sys_queue_res res;
++	struct ipu6_fw_com_context *ctx;
++	struct device *dev = &adev->auxdev.dev;
++	size_t sizeall, offset;
++	unsigned long attrs = 0;
++	void *specific_host_addr;
++	unsigned int i;
++
++	if (!cfg || !cfg->cell_start || !cfg->cell_ready)
++		return NULL;
++
++	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
++	if (!ctx)
++		return NULL;
++	ctx->dmem_addr = base + cfg->dmem_addr + REGMEM_OFFSET;
++	ctx->adev = adev;
++	ctx->cell_start = cfg->cell_start;
++	ctx->cell_ready = cfg->cell_ready;
++	ctx->buttress_boot_offset = cfg->buttress_boot_offset;
++	ctx->base_addr  = base;
++
++	/*
++	 * Allocate DMA mapped memory. Allocate one big chunk.
++	 */
++	/* Base cfg for FW */
++	conf_size = roundup(sizeof(struct ipu6_fw_syscom_config), 8);
++	/* Descriptions of the queues */
++	inq_size = size_mul(cfg->num_input_queues,
++			    sizeof(struct ipu6_fw_sys_queue));
++	outq_size = size_mul(cfg->num_output_queues,
++			     sizeof(struct ipu6_fw_sys_queue));
++	/* FW specific information structure */
++	specific_size = roundup(cfg->specific_size, 8);
++
++	sizeall = conf_size + inq_size + outq_size + specific_size;
++
++	for (i = 0; i < cfg->num_input_queues; i++)
++		sizeinput += size_mul(cfg->input[i].queue_size + 1,
++				      cfg->input[i].token_size);
++
++	for (i = 0; i < cfg->num_output_queues; i++)
++		sizeoutput += size_mul(cfg->output[i].queue_size + 1,
++				       cfg->output[i].token_size);
++
++	sizeall += sizeinput + sizeoutput;
++
++	ctx->dma_buffer = dma_alloc_attrs(dev, sizeall, &ctx->dma_addr,
++					  GFP_KERNEL, attrs);
++	ctx->attrs = attrs;
++	if (!ctx->dma_buffer) {
++		dev_err(dev, "failed to allocate dma memory\n");
++		kfree(ctx);
++		return NULL;
++	}
++
++	ctx->dma_size = sizeall;
++
++	config_host_addr = ctx->dma_buffer;
++	ctx->config_vied_addr = ctx->dma_addr;
++
++	offset = conf_size;
++	ctx->input_queue = ctx->dma_buffer + offset;
++	config_host_addr->input_queue = ctx->dma_addr + offset;
++	config_host_addr->num_input_queues = cfg->num_input_queues;
++
++	offset += inq_size;
++	ctx->output_queue = ctx->dma_buffer + offset;
++	config_host_addr->output_queue = ctx->dma_addr + offset;
++	config_host_addr->num_output_queues = cfg->num_output_queues;
++
++	/* copy firmware specific data */
++	offset += outq_size;
++	specific_host_addr = ctx->dma_buffer + offset;
++	config_host_addr->specific_addr = ctx->dma_addr + offset;
++	config_host_addr->specific_size = cfg->specific_size;
++	if (cfg->specific_addr && cfg->specific_size)
++		memcpy(specific_host_addr, cfg->specific_addr,
++		       cfg->specific_size);
++
++	/* initialize input queues */
++	offset += specific_size;
++	res.reg = SYSCOM_QPR_BASE_REG;
++	res.host_address = (u64)(ctx->dma_buffer + offset);
++	res.vied_address = ctx->dma_addr + offset;
++	for (i = 0; i < cfg->num_input_queues; i++)
++		ipu6_sys_queue_init(ctx->input_queue + i,
++				    cfg->input[i].queue_size,
++				    cfg->input[i].token_size, &res);
++
++	/* initialize output queues */
++	offset += sizeinput;
++	res.host_address = (u64)(ctx->dma_buffer + offset);
++	res.vied_address = ctx->dma_addr + offset;
++	for (i = 0; i < cfg->num_output_queues; i++) {
++		ipu6_sys_queue_init(ctx->output_queue + i,
++				    cfg->output[i].queue_size,
++				    cfg->output[i].token_size, &res);
++	}
++
++	return ctx;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6);
++
++int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx)
++{
++	/* write magic pattern to disable the tunit trace */
++	writel(TUNIT_MAGIC_PATTERN, ctx->dmem_addr + TUNIT_CFG_DWR_REG * 4);
++	/* Check if SP is in valid state */
++	if (!ctx->cell_ready(ctx->adev))
++		return -EIO;
++
++	/* store syscom uninitialized command */
++	writel(SYSCOM_COMMAND_UNINIT, ctx->dmem_addr + SYSCOM_COMMAND_REG * 4);
++
++	/* store syscom uninitialized state */
++	writel(SYSCOM_STATE_UNINIT,
++	       BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
++					  ctx->buttress_boot_offset,
++					  SYSCOM_STATE_ID));
++
++	/* store firmware configuration address */
++	writel(ctx->config_vied_addr,
++	       BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
++					  ctx->buttress_boot_offset,
++					  SYSCOM_CONFIG_ID));
++	ctx->cell_start(ctx->adev);
++
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6);
++
++int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx)
++{
++	int state;
++
++	state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
++						 ctx->buttress_boot_offset,
++						 SYSCOM_STATE_ID));
++	if (state != SYSCOM_STATE_READY)
++		return -EBUSY;
++
++	/* set close request flag */
++	writel(SYSCOM_COMMAND_INACTIVE, ctx->dmem_addr +
++	       SYSCOM_COMMAND_REG * 4);
++
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6);
++
++int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force)
++{
++	/* check if release is forced, an verify cell state if it is not */
++	if (!force && !ctx->cell_ready(ctx->adev))
++		return -EBUSY;
++
++	dma_free_attrs(&ctx->adev->auxdev.dev, ctx->dma_size,
++		       ctx->dma_buffer, ctx->dma_addr, ctx->attrs);
++	kfree(ctx);
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6);
++
++bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx)
++{
++	int state;
++
++	state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
++						 ctx->buttress_boot_offset,
++						 SYSCOM_STATE_ID));
++
++	return state == SYSCOM_STATE_READY;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6);
++
++void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
++{
++	struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
++	void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
++	unsigned int wr, rd;
++	unsigned int packets;
++	unsigned int index;
++
++	wr = readl(q_dmem + FW_COM_WR_REG);
++	rd = readl(q_dmem + FW_COM_RD_REG);
++
++	if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
++		return NULL;
++
++	if (wr < rd)
++		packets = rd - wr - 1;
++	else
++		packets = q->size - (wr - rd + 1);
++
++	if (!packets)
++		return NULL;
++
++	index = readl(q_dmem + FW_COM_WR_REG);
++
++	return (void *)(q->host_address + index * q->token_size);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6);
++
++void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
++{
++	struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
++	void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
++	unsigned int wr = readl(q_dmem + FW_COM_WR_REG) + 1;
++
++	if (wr >= q->size)
++		wr = 0;
++
++	writel(wr, q_dmem + FW_COM_WR_REG);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6);
++
++void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
++{
++	struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
++	void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
++	unsigned int wr, rd;
++	unsigned int packets;
++
++	wr = readl(q_dmem + FW_COM_WR_REG);
++	rd = readl(q_dmem + FW_COM_RD_REG);
++
++	if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
++		return NULL;
++
++	if (wr < rd)
++		wr += q->size;
++
++	packets = wr - rd;
++	if (!packets)
++		return NULL;
++
++	return (void *)(q->host_address + rd * q->token_size);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6);
++
++void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
++{
++	struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
++	void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
++	unsigned int rd = readl(q_dmem + FW_COM_RD_REG) + 1;
++
++	if (rd >= q->size)
++		rd = 0;
++
++	writel(rd, q_dmem + FW_COM_RD_REG);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6);
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-com.h b/drivers/media/pci/intel/ipu6/ipu6-fw-com.h
+new file mode 100644
+index 000000000000..660c406b3ac9
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-fw-com.h
+@@ -0,0 +1,47 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_FW_COM_H
++#define IPU6_FW_COM_H
++
++struct ipu6_fw_com_context;
++struct ipu6_bus_device;
++
++struct ipu6_fw_syscom_queue_config {
++	unsigned int queue_size;	/* tokens per queue */
++	unsigned int token_size;	/* bytes per token */
++};
++
++#define SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET	0
++
++struct ipu6_fw_com_cfg {
++	unsigned int num_input_queues;
++	unsigned int num_output_queues;
++	struct ipu6_fw_syscom_queue_config *input;
++	struct ipu6_fw_syscom_queue_config *output;
++
++	unsigned int dmem_addr;
++
++	/* firmware-specific configuration data */
++	void *specific_addr;
++	unsigned int specific_size;
++	int (*cell_ready)(struct ipu6_bus_device *adev);
++	void (*cell_start)(struct ipu6_bus_device *adev);
++
++	unsigned int buttress_boot_offset;
++};
++
++void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
++			  struct ipu6_bus_device *adev, void __iomem *base);
++
++int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx);
++bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx);
++int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx);
++int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force);
++
++void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
++void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
++void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
++void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
++
++#endif
+-- 
+2.43.2
+
+
+From e690b8a96eb4fbe1d948ad80047a9af7c601b761 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:21 +0800
+Subject: [PATCH 14/33] media: intel/ipu6: input system ABI between firmware
+ and driver
+
+Implement the input system firmware ABIs between the firmware and
+driver - include stream configuration, control command, capture
+request and response, etc.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-fw-isys.c | 487 +++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-fw-isys.h | 573 ++++++++++++++++++++
+ 2 files changed, 1060 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
+new file mode 100644
+index 000000000000..e06c1c955d38
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
+@@ -0,0 +1,487 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/cacheflush.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/io.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-fw-com.h"
++#include "ipu6-isys.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++#include "ipu6-platform-regs.h"
++
++static const char send_msg_types[N_IPU6_FW_ISYS_SEND_TYPE][32] = {
++	"STREAM_OPEN",
++	"STREAM_START",
++	"STREAM_START_AND_CAPTURE",
++	"STREAM_CAPTURE",
++	"STREAM_STOP",
++	"STREAM_FLUSH",
++	"STREAM_CLOSE"
++};
++
++static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_fw_isys_proxy_resp_info_abi *resp;
++	int ret;
++
++	resp = ipu6_recv_get_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
++	if (!resp)
++		return 1;
++
++	dev_dbg(dev, "Proxy response: id %u, error %u, details %u\n",
++		resp->request_id, resp->error_info.error,
++		resp->error_info.error_details);
++
++	ret = req_id == resp->request_id ? 0 : -EIO;
++
++	ipu6_recv_put_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
++
++	return ret;
++}
++
++int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
++				  unsigned int req_id,
++				  unsigned int index,
++				  unsigned int offset, u32 value)
++{
++	struct ipu6_fw_com_context *ctx = isys->fwcom;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_fw_proxy_send_queue_token *token;
++	unsigned int timeout = 1000;
++	int ret;
++
++	dev_dbg(dev,
++		"proxy send: req_id 0x%x, index %d, offset 0x%x, value 0x%x\n",
++		req_id, index, offset, value);
++
++	token = ipu6_send_get_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);
++	if (!token)
++		return -EBUSY;
++
++	token->request_id = req_id;
++	token->region_index = index;
++	token->offset = offset;
++	token->value = value;
++	ipu6_send_put_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);
++
++	do {
++		usleep_range(100, 110);
++		ret = handle_proxy_response(isys, req_id);
++		if (!ret)
++			break;
++		if (ret == -EIO) {
++			dev_err(dev, "Proxy respond with unexpected id\n");
++			break;
++		}
++		timeout--;
++	} while (ret && timeout);
++
++	if (!timeout)
++		dev_err(dev, "Proxy response timed out\n");
++
++	return ret;
++}
++
++int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
++			     const unsigned int stream_handle,
++			     void *cpu_mapped_buf,
++			     dma_addr_t dma_mapped_buf,
++			     size_t size, u16 send_type)
++{
++	struct ipu6_fw_com_context *ctx = isys->fwcom;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_fw_send_queue_token *token;
++
++	if (send_type >= N_IPU6_FW_ISYS_SEND_TYPE)
++		return -EINVAL;
++
++	dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]);
++
++	/*
++	 * Time to flush cache in case we have some payload. Not all messages
++	 * have that
++	 */
++	if (cpu_mapped_buf)
++		clflush_cache_range(cpu_mapped_buf, size);
++
++	token = ipu6_send_get_token(ctx,
++				    stream_handle + IPU6_BASE_MSG_SEND_QUEUES);
++	if (!token)
++		return -EBUSY;
++
++	token->payload = dma_mapped_buf;
++	token->buf_handle = (unsigned long)cpu_mapped_buf;
++	token->send_type = send_type;
++
++	ipu6_send_put_token(ctx, stream_handle + IPU6_BASE_MSG_SEND_QUEUES);
++
++	return 0;
++}
++
++int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys,
++			    const unsigned int stream_handle, u16 send_type)
++{
++	return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0,
++					send_type);
++}
++
++int ipu6_fw_isys_close(struct ipu6_isys *isys)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	int retry = IPU6_ISYS_CLOSE_RETRY;
++	unsigned long flags;
++	void *fwcom;
++	int ret;
++
++	/*
++	 * Stop the isys fw. Actual close takes
++	 * some time as the FW must stop its actions including code fetch
++	 * to SP icache.
++	 * spinlock to wait the interrupt handler to be finished
++	 */
++	spin_lock_irqsave(&isys->power_lock, flags);
++	ret = ipu6_fw_com_close(isys->fwcom);
++	fwcom = isys->fwcom;
++	isys->fwcom = NULL;
++	spin_unlock_irqrestore(&isys->power_lock, flags);
++	if (ret)
++		dev_err(dev, "Device close failure: %d\n", ret);
++
++	/* release probably fails if the close failed. Let's try still */
++	do {
++		usleep_range(400, 500);
++		ret = ipu6_fw_com_release(fwcom, 0);
++		retry--;
++	} while (ret && retry);
++
++	if (ret) {
++		dev_err(dev, "Device release time out %d\n", ret);
++		spin_lock_irqsave(&isys->power_lock, flags);
++		isys->fwcom = fwcom;
++		spin_unlock_irqrestore(&isys->power_lock, flags);
++	}
++
++	return ret;
++}
++
++void ipu6_fw_isys_cleanup(struct ipu6_isys *isys)
++{
++	int ret;
++
++	ret = ipu6_fw_com_release(isys->fwcom, 1);
++	if (ret < 0)
++		dev_warn(&isys->adev->auxdev.dev,
++			 "Device busy, fw_com release failed.");
++	isys->fwcom = NULL;
++}
++
++static void start_sp(struct ipu6_bus_device *adev)
++{
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	void __iomem *spc_regs_base = isys->pdata->base +
++		isys->pdata->ipdata->hw_variant.spc_offset;
++	u32 val = IPU6_ISYS_SPC_STATUS_START |
++		IPU6_ISYS_SPC_STATUS_RUN |
++		IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE;
++
++	val |= isys->icache_prefetch ? IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH : 0;
++
++	writel(val, spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);
++}
++
++static int query_sp(struct ipu6_bus_device *adev)
++{
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	void __iomem *spc_regs_base = isys->pdata->base +
++		isys->pdata->ipdata->hw_variant.spc_offset;
++	u32 val;
++
++	val = readl(spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);
++	/* return true when READY == 1, START == 0 */
++	val &= IPU6_ISYS_SPC_STATUS_READY | IPU6_ISYS_SPC_STATUS_START;
++
++	return val == IPU6_ISYS_SPC_STATUS_READY;
++}
++
++static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys,
++				    struct ipu6_fw_com_cfg *fwcom,
++				    unsigned int num_streams)
++{
++	unsigned int max_send_queues, max_sram_blocks, max_devq_size;
++	struct ipu6_fw_syscom_queue_config *input_queue_cfg;
++	struct ipu6_fw_syscom_queue_config *output_queue_cfg;
++	struct device *dev = &isys->adev->auxdev.dev;
++	int type_proxy = IPU6_FW_ISYS_QUEUE_TYPE_PROXY;
++	int type_dev = IPU6_FW_ISYS_QUEUE_TYPE_DEV;
++	int type_msg = IPU6_FW_ISYS_QUEUE_TYPE_MSG;
++	int base_dev_send = IPU6_BASE_DEV_SEND_QUEUES;
++	int base_msg_send = IPU6_BASE_MSG_SEND_QUEUES;
++	int base_msg_recv = IPU6_BASE_MSG_RECV_QUEUES;
++	struct ipu6_fw_isys_fw_config *isys_fw_cfg;
++	u32 num_in_message_queues;
++	unsigned int max_streams;
++	unsigned int size;
++	unsigned int i;
++
++	max_streams = isys->pdata->ipdata->max_streams;
++	max_send_queues = isys->pdata->ipdata->max_send_queues;
++	max_sram_blocks = isys->pdata->ipdata->max_sram_blocks;
++	max_devq_size = isys->pdata->ipdata->max_devq_size;
++	num_in_message_queues = clamp(num_streams, 1U, max_streams);
++	isys_fw_cfg = devm_kzalloc(dev, sizeof(*isys_fw_cfg), GFP_KERNEL);
++	if (!isys_fw_cfg)
++		return -ENOMEM;
++
++	isys_fw_cfg->num_send_queues[type_proxy] = IPU6_N_MAX_PROXY_SEND_QUEUES;
++	isys_fw_cfg->num_send_queues[type_dev] = IPU6_N_MAX_DEV_SEND_QUEUES;
++	isys_fw_cfg->num_send_queues[type_msg] = num_in_message_queues;
++	isys_fw_cfg->num_recv_queues[type_proxy] = IPU6_N_MAX_PROXY_RECV_QUEUES;
++	/* Common msg/dev return queue */
++	isys_fw_cfg->num_recv_queues[type_dev] = 0;
++	isys_fw_cfg->num_recv_queues[type_msg] = 1;
++
++	size = sizeof(*input_queue_cfg) * max_send_queues;
++	input_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);
++	if (!input_queue_cfg)
++		return -ENOMEM;
++
++	size = sizeof(*output_queue_cfg) * IPU6_N_MAX_RECV_QUEUES;
++	output_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);
++	if (!output_queue_cfg)
++		return -ENOMEM;
++
++	fwcom->input = input_queue_cfg;
++	fwcom->output = output_queue_cfg;
++
++	fwcom->num_input_queues = isys_fw_cfg->num_send_queues[type_proxy] +
++		isys_fw_cfg->num_send_queues[type_dev] +
++		isys_fw_cfg->num_send_queues[type_msg];
++
++	fwcom->num_output_queues = isys_fw_cfg->num_recv_queues[type_proxy] +
++		isys_fw_cfg->num_recv_queues[type_dev] +
++		isys_fw_cfg->num_recv_queues[type_msg];
++
++	/* SRAM partitioning. Equal partitioning is set. */
++	for (i = 0; i < max_sram_blocks; i++) {
++		if (i < num_in_message_queues)
++			isys_fw_cfg->buffer_partition.num_gda_pages[i] =
++				(IPU6_DEVICE_GDA_NR_PAGES *
++				 IPU6_DEVICE_GDA_VIRT_FACTOR) /
++				num_in_message_queues;
++		else
++			isys_fw_cfg->buffer_partition.num_gda_pages[i] = 0;
++	}
++
++	/* FW assumes proxy interface at fwcom queue 0 */
++	for (i = 0; i < isys_fw_cfg->num_send_queues[type_proxy]; i++) {
++		input_queue_cfg[i].token_size =
++			sizeof(struct ipu6_fw_proxy_send_queue_token);
++		input_queue_cfg[i].queue_size = IPU6_ISYS_SIZE_PROXY_SEND_QUEUE;
++	}
++
++	for (i = 0; i < isys_fw_cfg->num_send_queues[type_dev]; i++) {
++		input_queue_cfg[base_dev_send + i].token_size =
++			sizeof(struct ipu6_fw_send_queue_token);
++		input_queue_cfg[base_dev_send + i].queue_size = max_devq_size;
++	}
++
++	for (i = 0; i < isys_fw_cfg->num_send_queues[type_msg]; i++) {
++		input_queue_cfg[base_msg_send + i].token_size =
++			sizeof(struct ipu6_fw_send_queue_token);
++		input_queue_cfg[base_msg_send + i].queue_size =
++			IPU6_ISYS_SIZE_SEND_QUEUE;
++	}
++
++	for (i = 0; i < isys_fw_cfg->num_recv_queues[type_proxy]; i++) {
++		output_queue_cfg[i].token_size =
++			sizeof(struct ipu6_fw_proxy_resp_queue_token);
++		output_queue_cfg[i].queue_size =
++			IPU6_ISYS_SIZE_PROXY_RECV_QUEUE;
++	}
++	/* There is no recv DEV queue */
++	for (i = 0; i < isys_fw_cfg->num_recv_queues[type_msg]; i++) {
++		output_queue_cfg[base_msg_recv + i].token_size =
++			sizeof(struct ipu6_fw_resp_queue_token);
++		output_queue_cfg[base_msg_recv + i].queue_size =
++			IPU6_ISYS_SIZE_RECV_QUEUE;
++	}
++
++	fwcom->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset;
++	fwcom->specific_addr = isys_fw_cfg;
++	fwcom->specific_size = sizeof(*isys_fw_cfg);
++
++	return 0;
++}
++
++int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	int retry = IPU6_ISYS_OPEN_RETRY;
++	struct ipu6_fw_com_cfg fwcom = {
++		.cell_start = start_sp,
++		.cell_ready = query_sp,
++		.buttress_boot_offset = SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET,
++	};
++	int ret;
++
++	ipu6_isys_fwcom_cfg_init(isys, &fwcom, num_streams);
++
++	isys->fwcom = ipu6_fw_com_prepare(&fwcom, isys->adev,
++					  isys->pdata->base);
++	if (!isys->fwcom) {
++		dev_err(dev, "isys fw com prepare failed\n");
++		return -EIO;
++	}
++
++	ret = ipu6_fw_com_open(isys->fwcom);
++	if (ret) {
++		dev_err(dev, "isys fw com open failed %d\n", ret);
++		return ret;
++	}
++
++	do {
++		usleep_range(400, 500);
++		if (ipu6_fw_com_ready(isys->fwcom))
++			break;
++		retry--;
++	} while (retry > 0);
++
++	if (!retry) {
++		dev_err(dev, "isys port open ready failed %d\n", ret);
++		ipu6_fw_isys_close(isys);
++		ret = -EIO;
++	}
++
++	return ret;
++}
++
++struct ipu6_fw_isys_resp_info_abi *
++ipu6_fw_isys_get_resp(void *context, unsigned int queue)
++{
++	return ipu6_recv_get_token(context, queue);
++}
++
++void ipu6_fw_isys_put_resp(void *context, unsigned int queue)
++{
++	ipu6_recv_put_token(context, queue);
++}
++
++void ipu6_fw_isys_dump_stream_cfg(struct device *dev,
++				  struct ipu6_fw_isys_stream_cfg_data_abi *cfg)
++{
++	unsigned int i;
++
++	dev_dbg(dev, "-----------------------------------------------------\n");
++	dev_dbg(dev, "IPU6_FW_ISYS_STREAM_CFG_DATA\n");
++
++	dev_dbg(dev, "compfmt = %d\n", cfg->vc);
++	dev_dbg(dev, "src = %d\n", cfg->src);
++	dev_dbg(dev, "vc = %d\n", cfg->vc);
++	dev_dbg(dev, "isl_use = %d\n", cfg->isl_use);
++	dev_dbg(dev, "sensor_type = %d\n", cfg->sensor_type);
++
++	dev_dbg(dev, "send_irq_sof_discarded = %d\n",
++		cfg->send_irq_sof_discarded);
++	dev_dbg(dev, "send_irq_eof_discarded = %d\n",
++		cfg->send_irq_eof_discarded);
++	dev_dbg(dev, "send_resp_sof_discarded = %d\n",
++		cfg->send_resp_sof_discarded);
++	dev_dbg(dev, "send_resp_eof_discarded = %d\n",
++		cfg->send_resp_eof_discarded);
++
++	dev_dbg(dev, "crop:\n");
++	dev_dbg(dev, "\t.left_top = [%d, %d]\n", cfg->crop.left_offset,
++		cfg->crop.top_offset);
++	dev_dbg(dev, "\t.right_bottom = [%d, %d]\n", cfg->crop.right_offset,
++		cfg->crop.bottom_offset);
++
++	dev_dbg(dev, "nof_input_pins = %d\n", cfg->nof_input_pins);
++	for (i = 0; i < cfg->nof_input_pins; i++) {
++		dev_dbg(dev, "input pin[%d]:\n", i);
++		dev_dbg(dev, "\t.dt = 0x%0x\n", cfg->input_pins[i].dt);
++		dev_dbg(dev, "\t.mipi_store_mode = %d\n",
++			cfg->input_pins[i].mipi_store_mode);
++		dev_dbg(dev, "\t.bits_per_pix = %d\n",
++			cfg->input_pins[i].bits_per_pix);
++		dev_dbg(dev, "\t.mapped_dt = 0x%0x\n",
++			cfg->input_pins[i].mapped_dt);
++		dev_dbg(dev, "\t.input_res = %dx%d\n",
++			cfg->input_pins[i].input_res.width,
++			cfg->input_pins[i].input_res.height);
++		dev_dbg(dev, "\t.mipi_decompression = %d\n",
++			cfg->input_pins[i].mipi_decompression);
++		dev_dbg(dev, "\t.capture_mode = %d\n",
++			cfg->input_pins[i].capture_mode);
++	}
++
++	dev_dbg(dev, "nof_output_pins = %d\n", cfg->nof_output_pins);
++	for (i = 0; i < cfg->nof_output_pins; i++) {
++		dev_dbg(dev, "output_pin[%d]:\n", i);
++		dev_dbg(dev, "\t.input_pin_id = %d\n",
++			cfg->output_pins[i].input_pin_id);
++		dev_dbg(dev, "\t.output_res = %dx%d\n",
++			cfg->output_pins[i].output_res.width,
++			cfg->output_pins[i].output_res.height);
++		dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride);
++		dev_dbg(dev, "\t.pt = %d\n", cfg->output_pins[i].pt);
++		dev_dbg(dev, "\t.payload_buf_size = %d\n",
++			cfg->output_pins[i].payload_buf_size);
++		dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft);
++		dev_dbg(dev, "\t.watermark_in_lines = %d\n",
++			cfg->output_pins[i].watermark_in_lines);
++		dev_dbg(dev, "\t.send_irq = %d\n",
++			cfg->output_pins[i].send_irq);
++		dev_dbg(dev, "\t.reserve_compression = %d\n",
++			cfg->output_pins[i].reserve_compression);
++		dev_dbg(dev, "\t.snoopable = %d\n",
++			cfg->output_pins[i].snoopable);
++		dev_dbg(dev, "\t.error_handling_enable = %d\n",
++			cfg->output_pins[i].error_handling_enable);
++		dev_dbg(dev, "\t.sensor_type = %d\n",
++			cfg->output_pins[i].sensor_type);
++	}
++	dev_dbg(dev, "-----------------------------------------------------\n");
++}
++
++void
++ipu6_fw_isys_dump_frame_buff_set(struct device *dev,
++				 struct ipu6_fw_isys_frame_buff_set_abi *buf,
++				 unsigned int outputs)
++{
++	unsigned int i;
++
++	dev_dbg(dev, "-----------------------------------------------------\n");
++	dev_dbg(dev, "IPU6_FW_ISYS_FRAME_BUFF_SET\n");
++
++	for (i = 0; i < outputs; i++) {
++		dev_dbg(dev, "output_pin[%d]:\n", i);
++		dev_dbg(dev, "\t.out_buf_id = %llu\n",
++			buf->output_pins[i].out_buf_id);
++		dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr);
++		dev_dbg(dev, "\t.compress = %d\n",
++			buf->output_pins[i].compress);
++	}
++
++	dev_dbg(dev, "send_irq_sof = 0x%x\n", buf->send_irq_sof);
++	dev_dbg(dev, "send_irq_eof = 0x%x\n", buf->send_irq_eof);
++	dev_dbg(dev, "send_resp_sof = 0x%x\n", buf->send_resp_sof);
++	dev_dbg(dev, "send_resp_eof = 0x%x\n", buf->send_resp_eof);
++	dev_dbg(dev, "send_irq_capture_ack = 0x%x\n",
++		buf->send_irq_capture_ack);
++	dev_dbg(dev, "send_irq_capture_done = 0x%x\n",
++		buf->send_irq_capture_done);
++	dev_dbg(dev, "send_resp_capture_ack = 0x%x\n",
++		buf->send_resp_capture_ack);
++	dev_dbg(dev, "send_resp_capture_done = 0x%x\n",
++		buf->send_resp_capture_done);
++
++	dev_dbg(dev, "-----------------------------------------------------\n");
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
+new file mode 100644
+index 000000000000..a7ffa0e22bf0
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
+@@ -0,0 +1,573 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_FW_ISYS_H
++#define IPU6_FW_ISYS_H
++
++#include <linux/types.h>
++
++struct device;
++struct ipu6_isys;
++
++/* Max number of Input/Output Pins */
++#define IPU6_MAX_IPINS 4
++
++#define IPU6_MAX_OPINS ((IPU6_MAX_IPINS) + 1)
++
++#define IPU6_STREAM_ID_MAX 16
++#define IPU6_NONSECURE_STREAM_ID_MAX 12
++#define IPU6_DEV_SEND_QUEUE_SIZE (IPU6_STREAM_ID_MAX)
++#define IPU6_NOF_SRAM_BLOCKS_MAX (IPU6_STREAM_ID_MAX)
++#define IPU6_N_MAX_MSG_SEND_QUEUES (IPU6_STREAM_ID_MAX)
++#define IPU6SE_STREAM_ID_MAX 8
++#define IPU6SE_NONSECURE_STREAM_ID_MAX 4
++#define IPU6SE_DEV_SEND_QUEUE_SIZE (IPU6SE_STREAM_ID_MAX)
++#define IPU6SE_NOF_SRAM_BLOCKS_MAX (IPU6SE_STREAM_ID_MAX)
++#define IPU6SE_N_MAX_MSG_SEND_QUEUES (IPU6SE_STREAM_ID_MAX)
++
++/* Single return queue for all streams/commands type */
++#define IPU6_N_MAX_MSG_RECV_QUEUES 1
++/* Single device queue for high priority commands (bypass in-order queue) */
++#define IPU6_N_MAX_DEV_SEND_QUEUES 1
++/* Single dedicated send queue for proxy interface */
++#define IPU6_N_MAX_PROXY_SEND_QUEUES 1
++/* Single dedicated recv queue for proxy interface */
++#define IPU6_N_MAX_PROXY_RECV_QUEUES 1
++/* Send queues layout */
++#define IPU6_BASE_PROXY_SEND_QUEUES 0
++#define IPU6_BASE_DEV_SEND_QUEUES \
++	(IPU6_BASE_PROXY_SEND_QUEUES + IPU6_N_MAX_PROXY_SEND_QUEUES)
++#define IPU6_BASE_MSG_SEND_QUEUES \
++	(IPU6_BASE_DEV_SEND_QUEUES + IPU6_N_MAX_DEV_SEND_QUEUES)
++/* Recv queues layout */
++#define IPU6_BASE_PROXY_RECV_QUEUES 0
++#define IPU6_BASE_MSG_RECV_QUEUES \
++	(IPU6_BASE_PROXY_RECV_QUEUES + IPU6_N_MAX_PROXY_RECV_QUEUES)
++#define IPU6_N_MAX_RECV_QUEUES \
++	(IPU6_BASE_MSG_RECV_QUEUES + IPU6_N_MAX_MSG_RECV_QUEUES)
++
++#define IPU6_N_MAX_SEND_QUEUES \
++	(IPU6_BASE_MSG_SEND_QUEUES + IPU6_N_MAX_MSG_SEND_QUEUES)
++#define IPU6SE_N_MAX_SEND_QUEUES \
++	(IPU6_BASE_MSG_SEND_QUEUES + IPU6SE_N_MAX_MSG_SEND_QUEUES)
++
++/* Max number of supported input pins routed in ISL */
++#define IPU6_MAX_IPINS_IN_ISL 2
++
++/* Max number of planes for frame formats supported by the FW */
++#define IPU6_PIN_PLANES_MAX 4
++
++#define IPU6_FW_ISYS_SENSOR_TYPE_START 14
++#define IPU6_FW_ISYS_SENSOR_TYPE_END 19
++#define IPU6SE_FW_ISYS_SENSOR_TYPE_START 6
++#define IPU6SE_FW_ISYS_SENSOR_TYPE_END 11
++/*
++ * Device close takes some time from last ack message to actual stopping
++ * of the SP processor. As long as the SP processor runs we can't proceed with
++ * clean up of resources.
++ */
++#define IPU6_ISYS_OPEN_RETRY			2000
++#define IPU6_ISYS_CLOSE_RETRY			2000
++#define IPU6_FW_CALL_TIMEOUT_JIFFIES		msecs_to_jiffies(2000)
++
++enum ipu6_fw_isys_resp_type {
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE = 0,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY,
++	IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_WATERMARK,
++	IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF,
++	IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE,
++	IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_SKIPPED,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED,
++	IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED,
++	IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED,
++	IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY,
++	N_IPU6_FW_ISYS_RESP_TYPE
++};
++
++enum ipu6_fw_isys_send_type {
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN = 0,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_START,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_STOP,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE,
++	N_IPU6_FW_ISYS_SEND_TYPE
++};
++
++enum ipu6_fw_isys_queue_type {
++	IPU6_FW_ISYS_QUEUE_TYPE_PROXY = 0,
++	IPU6_FW_ISYS_QUEUE_TYPE_DEV,
++	IPU6_FW_ISYS_QUEUE_TYPE_MSG,
++	N_IPU6_FW_ISYS_QUEUE_TYPE
++};
++
++enum ipu6_fw_isys_stream_source {
++	IPU6_FW_ISYS_STREAM_SRC_PORT_0 = 0,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_1,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_2,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_3,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_4,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_5,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_6,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_7,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_8,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_9,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_10,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_11,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_12,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_13,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_14,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_15,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_2,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_3,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_4,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_5,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_6,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_7,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_8,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_9,
++	N_IPU6_FW_ISYS_STREAM_SRC
++};
++
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 IPU6_FW_ISYS_STREAM_SRC_PORT_0
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT1 IPU6_FW_ISYS_STREAM_SRC_PORT_1
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT2 IPU6_FW_ISYS_STREAM_SRC_PORT_2
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT3 IPU6_FW_ISYS_STREAM_SRC_PORT_3
++
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTA IPU6_FW_ISYS_STREAM_SRC_PORT_4
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTB IPU6_FW_ISYS_STREAM_SRC_PORT_5
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT0 \
++	IPU6_FW_ISYS_STREAM_SRC_PORT_6
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT1 \
++	IPU6_FW_ISYS_STREAM_SRC_PORT_7
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT2 \
++	IPU6_FW_ISYS_STREAM_SRC_PORT_8
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT3 \
++	IPU6_FW_ISYS_STREAM_SRC_PORT_9
++
++#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT0 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0
++#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT1 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1
++
++/**
++ * enum ipu6_fw_isys_mipi_vc: MIPI csi2 spec
++ * supports up to 4 virtual per physical channel
++ */
++enum ipu6_fw_isys_mipi_vc {
++	IPU6_FW_ISYS_MIPI_VC_0 = 0,
++	IPU6_FW_ISYS_MIPI_VC_1,
++	IPU6_FW_ISYS_MIPI_VC_2,
++	IPU6_FW_ISYS_MIPI_VC_3,
++	N_IPU6_FW_ISYS_MIPI_VC
++};
++
++enum ipu6_fw_isys_frame_format_type {
++	IPU6_FW_ISYS_FRAME_FORMAT_NV11 = 0, /* 12 bit YUV 411, Y, UV plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV12,	/* 12 bit YUV 420, Y, UV plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV12_16, /* 16 bit YUV 420, Y, UV plane */
++	/* 12 bit YUV 420, Intel proprietary tiled format */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV12_TILEY,
++
++	IPU6_FW_ISYS_FRAME_FORMAT_NV16,	/* 16 bit YUV 422, Y, UV plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV21,	/* 12 bit YUV 420, Y, VU plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV61,	/* 16 bit YUV 422, Y, VU plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YV12,	/* 12 bit YUV 420, Y, V, U plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YV16,	/* 16 bit YUV 422, Y, V, U plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420, /* 12 bit YUV 420, Y, U, V plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420_10, /* yuv420, 10 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420_12, /* yuv420, 12 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420_14, /* yuv420, 14 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420_16, /* yuv420, 16 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV422, /* 16 bit YUV 422, Y, U, V plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV422_16, /* yuv422, 16 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_UYVY,	/* 16 bit YUV 422, UYVY interleaved */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUYV,	/* 16 bit YUV 422, YUYV interleaved */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV444, /* 24 bit YUV 444, Y, U, V plane */
++	/* Internal format, 2 y lines followed by a uvinterleaved line */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV_LINE,
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW8,	/* RAW8, 1 plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW10, /* RAW10, 1 plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW12, /* RAW12, 1 plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW14, /* RAW14, 1 plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW16, /* RAW16, 1 plane */
++	/**
++	 * 16 bit RGB, 1 plane. Each 3 sub pixels are packed into one 16 bit
++	 * value, 5 bits for R, 6 bits for G and 5 bits for B.
++	 */
++	IPU6_FW_ISYS_FRAME_FORMAT_RGB565,
++	IPU6_FW_ISYS_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */
++	IPU6_FW_ISYS_FRAME_FORMAT_RGBA888, /* 32 bit RGBA, 1 plane, A=Alpha */
++	IPU6_FW_ISYS_FRAME_FORMAT_QPLANE6, /* Internal, for advanced ISP */
++	IPU6_FW_ISYS_FRAME_FORMAT_BINARY_8, /* byte stream, used for jpeg. */
++	N_IPU6_FW_ISYS_FRAME_FORMAT
++};
++
++#define IPU6_FW_ISYS_FRAME_FORMAT_RAW	(IPU6_FW_ISYS_FRAME_FORMAT_RAW16)
++
++enum ipu6_fw_isys_pin_type {
++	/* captured as MIPI packets */
++	IPU6_FW_ISYS_PIN_TYPE_MIPI = 0,
++	/* captured through the SoC path */
++	IPU6_FW_ISYS_PIN_TYPE_RAW_SOC = 3,
++};
++
++/**
++ * enum ipu6_fw_isys_mipi_store_mode. Describes if long MIPI packets reach
++ * MIPI SRAM with the long packet header or
++ * if not, then only option is to capture it with pin type MIPI.
++ */
++enum ipu6_fw_isys_mipi_store_mode {
++	IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL = 0,
++	IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER,
++	N_IPU6_FW_ISYS_MIPI_STORE_MODE
++};
++
++enum ipu6_fw_isys_capture_mode {
++	IPU6_FW_ISYS_CAPTURE_MODE_REGULAR = 0,
++	IPU6_FW_ISYS_CAPTURE_MODE_BURST,
++	N_IPU6_FW_ISYS_CAPTURE_MODE,
++};
++
++enum ipu6_fw_isys_sensor_mode {
++	IPU6_FW_ISYS_SENSOR_MODE_NORMAL = 0,
++	IPU6_FW_ISYS_SENSOR_MODE_TOBII,
++	N_IPU6_FW_ISYS_SENSOR_MODE,
++};
++
++enum ipu6_fw_isys_error {
++	IPU6_FW_ISYS_ERROR_NONE = 0,
++	IPU6_FW_ISYS_ERROR_FW_INTERNAL_CONSISTENCY,
++	IPU6_FW_ISYS_ERROR_HW_CONSISTENCY,
++	IPU6_FW_ISYS_ERROR_DRIVER_INVALID_COMMAND_SEQUENCE,
++	IPU6_FW_ISYS_ERROR_DRIVER_INVALID_DEVICE_CONFIGURATION,
++	IPU6_FW_ISYS_ERROR_DRIVER_INVALID_STREAM_CONFIGURATION,
++	IPU6_FW_ISYS_ERROR_DRIVER_INVALID_FRAME_CONFIGURATION,
++	IPU6_FW_ISYS_ERROR_INSUFFICIENT_RESOURCES,
++	IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO,
++	IPU6_FW_ISYS_ERROR_HW_REPORTED_SIG2CIO,
++	IPU6_FW_ISYS_ERROR_SENSOR_FW_SYNC,
++	IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION,
++	IPU6_FW_ISYS_ERROR_RESPONSE_QUEUE_FULL,
++	N_IPU6_FW_ISYS_ERROR
++};
++
++enum ipu6_fw_proxy_error {
++	IPU6_FW_PROXY_ERROR_NONE = 0,
++	IPU6_FW_PROXY_ERROR_INVALID_WRITE_REGION,
++	IPU6_FW_PROXY_ERROR_INVALID_WRITE_OFFSET,
++	N_IPU6_FW_PROXY_ERROR
++};
++
++/* firmware ABI structure below are aligned in firmware, no need pack */
++struct ipu6_fw_isys_buffer_partition_abi {
++	u32 num_gda_pages[IPU6_STREAM_ID_MAX];
++};
++
++struct ipu6_fw_isys_fw_config {
++	struct ipu6_fw_isys_buffer_partition_abi buffer_partition;
++	u32 num_send_queues[N_IPU6_FW_ISYS_QUEUE_TYPE];
++	u32 num_recv_queues[N_IPU6_FW_ISYS_QUEUE_TYPE];
++};
++
++/**
++ * struct ipu6_fw_isys_resolution_abi: Generic resolution structure.
++ */
++struct ipu6_fw_isys_resolution_abi {
++	u32 width;
++	u32 height;
++};
++
++/**
++ * struct ipu6_fw_isys_output_pin_payload_abi
++ * @out_buf_id: Points to output pin buffer - buffer identifier
++ * @addr: Points to output pin buffer - CSS Virtual Address
++ * @compress: Request frame compression (1), or  not (0)
++ */
++struct ipu6_fw_isys_output_pin_payload_abi {
++	u64 out_buf_id;
++	u32 addr;
++	u32 compress;
++};
++
++/**
++ * struct ipu6_fw_isys_output_pin_info_abi
++ * @output_res: output pin resolution
++ * @stride: output stride in Bytes (not valid for statistics)
++ * @watermark_in_lines: pin watermark level in lines
++ * @payload_buf_size: minimum size in Bytes of all buffers that will be
++ *			supplied for capture on this pin
++ * @send_irq: assert if pin event should trigger irq
++ * @pt: pin type -real format "enum ipu6_fw_isys_pin_type"
++ * @ft: frame format type -real format "enum ipu6_fw_isys_frame_format_type"
++ * @input_pin_id: related input pin id
++ * @reserve_compression: reserve compression resources for pin
++ */
++struct ipu6_fw_isys_output_pin_info_abi {
++	struct ipu6_fw_isys_resolution_abi output_res;
++	u32 stride;
++	u32 watermark_in_lines;
++	u32 payload_buf_size;
++	u32 ts_offsets[IPU6_PIN_PLANES_MAX];
++	u32 s2m_pixel_soc_pixel_remapping;
++	u32 csi_be_soc_pixel_remapping;
++	u8 send_irq;
++	u8 input_pin_id;
++	u8 pt;
++	u8 ft;
++	u8 reserved;
++	u8 reserve_compression;
++	u8 snoopable;
++	u8 error_handling_enable;
++	u32 sensor_type;
++};
++
++/**
++ * struct ipu6_fw_isys_input_pin_info_abi
++ * @input_res: input resolution
++ * @dt: mipi data type ((enum ipu6_fw_isys_mipi_data_type)
++ * @mipi_store_mode: defines if legacy long packet header will be stored or
++ *		     discarded if discarded, output pin type for this
++ *		     input pin can only be MIPI
++ *		     (enum ipu6_fw_isys_mipi_store_mode)
++ * @bits_per_pix: native bits per pixel
++ * @mapped_dt: actual data type from sensor
++ * @mipi_decompression: defines which compression will be in mipi backend
++ * @crop_first_and_last_lines    Control whether to crop the
++ *                              first and last line of the
++ *                              input image. Crop done by HW
++ *                              device.
++ * @capture_mode: mode of capture, regular or burst, default value is regular
++ */
++struct ipu6_fw_isys_input_pin_info_abi {
++	struct ipu6_fw_isys_resolution_abi input_res;
++	u8 dt;
++	u8 mipi_store_mode;
++	u8 bits_per_pix;
++	u8 mapped_dt;
++	u8 mipi_decompression;
++	u8 crop_first_and_last_lines;
++	u8 capture_mode;
++	u8 reserved;
++};
++
++/**
++ * struct ipu6_fw_isys_cropping_abi - cropping coordinates
++ */
++struct ipu6_fw_isys_cropping_abi {
++	s32 top_offset;
++	s32 left_offset;
++	s32 bottom_offset;
++	s32 right_offset;
++};
++
++/**
++ * struct ipu6_fw_isys_stream_cfg_data_abi
++ * ISYS stream configuration data structure
++ * @crop: for extended use and is not used in FW currently
++ * @input_pins: input pin descriptors
++ * @output_pins: output pin descriptors
++ * @compfmt: de-compression setting for User Defined Data
++ * @nof_input_pins: number of input pins
++ * @nof_output_pins: number of output pins
++ * @send_irq_sof_discarded: send irq on discarded frame sof response
++ *		- if '1' it will override the send_resp_sof_discarded
++ *		  and send the response
++ *		- if '0' the send_resp_sof_discarded will determine
++ *		  whether to send the response
++ * @send_irq_eof_discarded: send irq on discarded frame eof response
++ *		- if '1' it will override the send_resp_eof_discarded
++ *		  and send the response
++ *		- if '0' the send_resp_eof_discarded will determine
++ *		  whether to send the response
++ * @send_resp_sof_discarded: send response for discarded frame sof detected,
++ *			     used only when send_irq_sof_discarded is '0'
++ * @send_resp_eof_discarded: send response for discarded frame eof detected,
++ *			     used only when send_irq_eof_discarded is '0'
++ * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1
++ * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel)
++ * @isl_use: indicates whether stream requires ISL and how
++ * @sensor_type: type of connected sensor, tobii or others, default is 0
++ */
++struct ipu6_fw_isys_stream_cfg_data_abi {
++	struct ipu6_fw_isys_cropping_abi crop;
++	struct ipu6_fw_isys_input_pin_info_abi input_pins[IPU6_MAX_IPINS];
++	struct ipu6_fw_isys_output_pin_info_abi output_pins[IPU6_MAX_OPINS];
++	u32 compfmt;
++	u8 nof_input_pins;
++	u8 nof_output_pins;
++	u8 send_irq_sof_discarded;
++	u8 send_irq_eof_discarded;
++	u8 send_resp_sof_discarded;
++	u8 send_resp_eof_discarded;
++	u8 src;
++	u8 vc;
++	u8 isl_use;
++	u8 sensor_type;
++	u8 reserved;
++	u8 reserved2;
++};
++
++/**
++ * struct ipu6_fw_isys_frame_buff_set - frame buffer set
++ * @output_pins: output pin addresses
++ * @send_irq_sof: send irq on frame sof response
++ *		- if '1' it will override the send_resp_sof and
++ *		  send the response
++ *		- if '0' the send_resp_sof will determine whether to
++ *		  send the response
++ * @send_irq_eof: send irq on frame eof response
++ *		- if '1' it will override the send_resp_eof and
++ *		  send the response
++ *		- if '0' the send_resp_eof will determine whether to
++ *		  send the response
++ * @send_resp_sof: send response for frame sof detected,
++ *		   used only when send_irq_sof is '0'
++ * @send_resp_eof: send response for frame eof detected,
++ *		   used only when send_irq_eof is '0'
++ * @send_resp_capture_ack: send response for capture ack event
++ * @send_resp_capture_done: send response for capture done event
++ */
++struct ipu6_fw_isys_frame_buff_set_abi {
++	struct ipu6_fw_isys_output_pin_payload_abi output_pins[IPU6_MAX_OPINS];
++	u8 send_irq_sof;
++	u8 send_irq_eof;
++	u8 send_irq_capture_ack;
++	u8 send_irq_capture_done;
++	u8 send_resp_sof;
++	u8 send_resp_eof;
++	u8 send_resp_capture_ack;
++	u8 send_resp_capture_done;
++	u8 reserved[8];
++};
++
++/**
++ * struct ipu6_fw_isys_error_info_abi
++ * @error: error code if something went wrong
++ * @error_details: depending on error code, it may contain additional error info
++ */
++struct ipu6_fw_isys_error_info_abi {
++	u32 error;
++	u32 error_details;
++};
++
++/**
++ * struct ipu6_fw_isys_resp_info_comm
++ * @pin: this var is only valid for pin event related responses,
++ *     contains pin addresses
++ * @error_info: error information from the FW
++ * @timestamp: Time information for event if available
++ * @stream_handle: stream id the response corresponds to
++ * @type: response type (enum ipu6_fw_isys_resp_type)
++ * @pin_id: pin id that the pin payload corresponds to
++ */
++struct ipu6_fw_isys_resp_info_abi {
++	u64 buf_id;
++	struct ipu6_fw_isys_output_pin_payload_abi pin;
++	struct ipu6_fw_isys_error_info_abi error_info;
++	u32 timestamp[2];
++	u8 stream_handle;
++	u8 type;
++	u8 pin_id;
++	u8 reserved;
++	u32 reserved2;
++};
++
++/**
++ * struct ipu6_fw_isys_proxy_error_info_comm
++ * @proxy_error: error code if something went wrong
++ * @proxy_error_details: depending on error code, it may contain additional
++ *			error info
++ */
++struct ipu6_fw_isys_proxy_error_info_abi {
++	u32 error;
++	u32 error_details;
++};
++
++struct ipu6_fw_isys_proxy_resp_info_abi {
++	u32 request_id;
++	struct ipu6_fw_isys_proxy_error_info_abi error_info;
++};
++
++/**
++ * struct ipu6_fw_proxy_write_queue_token
++ * @request_id: update id for the specific proxy write request
++ * @region_index: Region id for the proxy write request
++ * @offset: Offset of the write request according to the base address
++ *	    of the region
++ * @value: Value that is requested to be written with the proxy write request
++ */
++struct ipu6_fw_proxy_write_queue_token {
++	u32 request_id;
++	u32 region_index;
++	u32 offset;
++	u32 value;
++};
++
++/**
++ * struct ipu6_fw_resp_queue_token
++ */
++struct ipu6_fw_resp_queue_token {
++	struct ipu6_fw_isys_resp_info_abi resp_info;
++};
++
++/**
++ * struct ipu6_fw_send_queue_token
++ */
++struct ipu6_fw_send_queue_token {
++	u64 buf_handle;
++	u32 payload;
++	u16 send_type;
++	u16 stream_id;
++};
++
++/**
++ * struct ipu6_fw_proxy_resp_queue_token
++ */
++struct ipu6_fw_proxy_resp_queue_token {
++	struct ipu6_fw_isys_proxy_resp_info_abi proxy_resp_info;
++};
++
++/**
++ * struct ipu6_fw_proxy_send_queue_token
++ */
++struct ipu6_fw_proxy_send_queue_token {
++	u32 request_id;
++	u32 region_index;
++	u32 offset;
++	u32 value;
++};
++
++void
++ipu6_fw_isys_dump_stream_cfg(struct device *dev,
++			     struct ipu6_fw_isys_stream_cfg_data_abi *cfg);
++void
++ipu6_fw_isys_dump_frame_buff_set(struct device *dev,
++				 struct ipu6_fw_isys_frame_buff_set_abi *buf,
++				 unsigned int outputs);
++int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams);
++int ipu6_fw_isys_close(struct ipu6_isys *isys);
++int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys,
++			    const unsigned int stream_handle, u16 send_type);
++int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
++			     const unsigned int stream_handle,
++			     void *cpu_mapped_buf, dma_addr_t dma_mapped_buf,
++			     size_t size, u16 send_type);
++int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
++				  unsigned int req_id,
++				  unsigned int index,
++				  unsigned int offset, u32 value);
++void ipu6_fw_isys_cleanup(struct ipu6_isys *isys);
++struct ipu6_fw_isys_resp_info_abi *
++ipu6_fw_isys_get_resp(void *context, unsigned int queue);
++void ipu6_fw_isys_put_resp(void *context, unsigned int queue);
++#endif
+-- 
+2.43.2
+
+
+From e69a245fa4db99e5983170aab73f962dc99d3149 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:22 +0800
+Subject: [PATCH 15/33] media: intel/ipu6: add IPU6 CSI2 receiver v4l2
+ sub-device
+
+Input system CSI2 receiver is exposed as a v4l2 sub-device.
+Each CSI2 sub-device represent one single CSI2 hardware port
+which be linked with external sub-device such camera sensor
+by linked with ISYS CSI2's sink pad. CSI2 source pad is linked
+to the sink pad of video capture device.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c | 666 ++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h |  81 +++
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.c   | 381 ++++++++++
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.h   |  61 ++
+ .../intel/ipu6/ipu6-platform-isys-csi2-reg.h  | 189 +++++
+ 5 files changed, 1378 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+new file mode 100644
+index 000000000000..ac9fa3e0d7ab
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+@@ -0,0 +1,666 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/atomic.h>
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/minmax.h>
++#include <linux/sprintf.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-subdev.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-isys-subdev.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++
++static const u32 csi2_supported_codes[] = {
++	MEDIA_BUS_FMT_RGB565_1X16,
++	MEDIA_BUS_FMT_RGB888_1X24,
++	MEDIA_BUS_FMT_UYVY8_1X16,
++	MEDIA_BUS_FMT_YUYV8_1X16,
++	MEDIA_BUS_FMT_SBGGR10_1X10,
++	MEDIA_BUS_FMT_SGBRG10_1X10,
++	MEDIA_BUS_FMT_SGRBG10_1X10,
++	MEDIA_BUS_FMT_SRGGB10_1X10,
++	MEDIA_BUS_FMT_SBGGR12_1X12,
++	MEDIA_BUS_FMT_SGBRG12_1X12,
++	MEDIA_BUS_FMT_SGRBG12_1X12,
++	MEDIA_BUS_FMT_SRGGB12_1X12,
++	MEDIA_BUS_FMT_SBGGR8_1X8,
++	MEDIA_BUS_FMT_SGBRG8_1X8,
++	MEDIA_BUS_FMT_SGRBG8_1X8,
++	MEDIA_BUS_FMT_SRGGB8_1X8,
++	0
++};
++
++/*
++ * Strings corresponding to CSI-2 receiver errors are here.
++ * Corresponding macros are defined in the header file.
++ */
++static const struct ipu6_csi2_error dphy_rx_errors[] = {
++	{ "Single packet header error corrected", true },
++	{ "Multiple packet header errors detected", true },
++	{ "Payload checksum (CRC) error", true },
++	{ "Transfer FIFO overflow", false },
++	{ "Reserved short packet data type detected", true },
++	{ "Reserved long packet data type detected", true },
++	{ "Incomplete long packet detected", false },
++	{ "Frame sync error", false },
++	{ "Line sync error", false },
++	{ "DPHY recoverable synchronization error", true },
++	{ "DPHY fatal error", false },
++	{ "DPHY elastic FIFO overflow", false },
++	{ "Inter-frame short packet discarded", true },
++	{ "Inter-frame long packet discarded", true },
++	{ "MIPI pktgen overflow", false },
++	{ "MIPI pktgen data loss", false },
++	{ "FIFO overflow", false },
++	{ "Lane deskew", false },
++	{ "SOT sync error", false },
++	{ "HSIDLE detected", false }
++};
++
++s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2)
++{
++	struct media_pad *src_pad;
++	struct v4l2_subdev *ext_sd;
++	struct device *dev;
++
++	if (!csi2)
++		return -EINVAL;
++
++	dev = &csi2->isys->adev->auxdev.dev;
++	src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
++	if (IS_ERR_OR_NULL(src_pad)) {
++		dev_err(dev, "can't get source pad of %s\n", csi2->asd.sd.name);
++		return -ENOLINK;
++	}
++
++	ext_sd = media_entity_to_v4l2_subdev(src_pad->entity);
++	if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name))
++		return -ENODEV;
++
++	return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0);
++}
++
++static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
++				struct v4l2_event_subscription *sub)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++
++	dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n",
++		sub->type, sub->id);
++
++	switch (sub->type) {
++	case V4L2_EVENT_FRAME_SYNC:
++		return v4l2_event_subscribe(fh, sub, 10, NULL);
++	case V4L2_EVENT_CTRL:
++		return v4l2_ctrl_subscribe_event(fh, sub);
++	default:
++		return -EINVAL;
++	}
++}
++
++static const struct v4l2_subdev_core_ops csi2_sd_core_ops = {
++	.subscribe_event = csi2_subscribe_event,
++	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++/*
++ * The input system CSI2+ receiver has several
++ * parameters affecting the receiver timings. These depend
++ * on the MIPI bus frequency F in Hz (sensor transmitter rate)
++ * as follows:
++ *	register value = (A/1e9 + B * UI) / COUNT_ACC
++ * where
++ *	UI = 1 / (2 * F) in seconds
++ *	COUNT_ACC = counter accuracy in seconds
++ *	COUNT_ACC = 0.125 ns = 1 / 8 ns, ACCINV = 8.
++ *
++ * A and B are coefficients from the table below,
++ * depending whether the register minimum or maximum value is
++ * calculated.
++ *				       Minimum     Maximum
++ * Clock lane			       A     B     A     B
++ * reg_rx_csi_dly_cnt_termen_clane     0     0    38     0
++ * reg_rx_csi_dly_cnt_settle_clane    95    -8   300   -16
++ * Data lanes
++ * reg_rx_csi_dly_cnt_termen_dlane0    0     0    35     4
++ * reg_rx_csi_dly_cnt_settle_dlane0   85    -2   145    -6
++ * reg_rx_csi_dly_cnt_termen_dlane1    0     0    35     4
++ * reg_rx_csi_dly_cnt_settle_dlane1   85    -2   145    -6
++ * reg_rx_csi_dly_cnt_termen_dlane2    0     0    35     4
++ * reg_rx_csi_dly_cnt_settle_dlane2   85    -2   145    -6
++ * reg_rx_csi_dly_cnt_termen_dlane3    0     0    35     4
++ * reg_rx_csi_dly_cnt_settle_dlane3   85    -2   145    -6
++ *
++ * We use the minimum values of both A and B.
++ */
++
++#define DIV_SHIFT	8
++#define CSI2_ACCINV	8
++
++static u32 calc_timing(s32 a, s32 b, s64 link_freq, s32 accinv)
++{
++	return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT)
++			     / (s32)(link_freq >> DIV_SHIFT));
++}
++
++static int
++ipu6_isys_csi2_calc_timing(struct ipu6_isys_csi2 *csi2,
++			   struct ipu6_isys_csi2_timing *timing, s32 accinv)
++{
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++	s64 link_freq;
++
++	link_freq = ipu6_isys_csi2_get_link_freq(csi2);
++	if (link_freq < 0)
++		return link_freq;
++
++	timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A,
++				      CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B,
++				      link_freq, accinv);
++	timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A,
++				      CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B,
++				      link_freq, accinv);
++	timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A,
++				      CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B,
++				      link_freq, accinv);
++	timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A,
++				      CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B,
++				      link_freq, accinv);
++
++	dev_dbg(dev, "ctermen %u csettle %u dtermen %u dsettle %u\n",
++		timing->ctermen, timing->csettle,
++		timing->dtermen, timing->dsettle);
++
++	return 0;
++}
++
++void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2)
++{
++	u32 irq = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++			CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
++	struct ipu6_isys *isys = csi2->isys;
++	u32 mask;
++
++	mask = isys->pdata->ipdata->csi2.irq_mask;
++	writel(irq & mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++	csi2->receiver_errors |= irq & mask;
++}
++
++void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2)
++{
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++	const struct ipu6_csi2_error *errors;
++	u32 status;
++	u32 i;
++
++	/* register errors once more in case of interrupts are disabled */
++	ipu6_isys_register_errors(csi2);
++	status = csi2->receiver_errors;
++	csi2->receiver_errors = 0;
++	errors = dphy_rx_errors;
++
++	for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) {
++		if (status & BIT(i))
++			dev_err_ratelimited(dev, "csi2-%i error: %s\n",
++					    csi2->port, errors[i].error_string);
++	}
++}
++
++static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd,
++				     const struct ipu6_isys_csi2_timing *timing,
++				     unsigned int nlanes, int enable)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
++	struct ipu6_isys *isys = csi2->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_isys_csi2_config cfg;
++	unsigned int nports;
++	int ret = 0;
++	u32 mask = 0;
++	u32 i;
++
++	dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off",
++		csi2->port, nlanes);
++
++	cfg.port = csi2->port;
++	cfg.nlanes = nlanes;
++
++	mask = isys->pdata->ipdata->csi2.irq_mask;
++	nports = isys->pdata->ipdata->csi2.nports;
++
++	if (!enable) {
++		writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE);
++		writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE);
++
++		writel(0,
++		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++		       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
++		writel(mask,
++		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++		       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++		writel(0,
++		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++		       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
++		writel(0xffffffff,
++		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++		       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++
++		isys->phy_set_power(isys, &cfg, timing, false);
++
++		writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
++		       (isys->pdata->ipdata->csi2.fw_access_port_ofs,
++			csi2->port));
++		writel(0, isys->pdata->base +
++		       CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port));
++
++		return ret;
++	}
++
++	/* reset port reset */
++	writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST);
++	usleep_range(100, 200);
++	writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST);
++
++	/* enable port clock */
++	for (i = 0; i < nports; i++) {
++		writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i));
++		writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
++		       (isys->pdata->ipdata->csi2.fw_access_port_ofs, i));
++	}
++
++	/* enable all error related irq */
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
++
++	/*
++	 * Using event from firmware instead of irq to handle CSI2 sync event
++	 * which can reduce system wakeups. If CSI2 sync irq enabled, we need
++	 * disable the firmware CSI2 sync event to avoid duplicate handling.
++	 */
++	writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
++	writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
++	writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++	writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
++	writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
++
++	/* configure to enable FE and PPI2CSI */
++	writel(0, csi2->base + CSI_REG_CSI_FE_MODE);
++	writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL);
++	writel(CSI_CNTR_SENSOR_LINE_ID | CSI_CNTR_SENSOR_FRAME_ID,
++	       csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL);
++	writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1),
++	       csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF);
++
++	writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE);
++	writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE);
++
++	ret = isys->phy_set_power(isys, &cfg, timing, true);
++	if (ret)
++		dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port,
++			ret);
++
++	return ret;
++}
++
++static int set_stream(struct v4l2_subdev *sd, int enable)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++	struct ipu6_isys_csi2_timing timing = { };
++	unsigned int nlanes;
++	int ret;
++
++	dev_dbg(dev, "csi2 stream %s callback\n", enable ? "on" : "off");
++
++	if (!enable) {
++		csi2->stream_count--;
++		if (csi2->stream_count)
++			return 0;
++
++		ipu6_isys_csi2_set_stream(sd, &timing, 0, enable);
++		return 0;
++	}
++
++	if (csi2->stream_count) {
++		csi2->stream_count++;
++		return 0;
++	}
++
++	nlanes = csi2->nlanes;
++
++	ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);
++	if (ret)
++		return ret;
++
++	ret = ipu6_isys_csi2_set_stream(sd, &timing, nlanes, enable);
++	if (ret)
++		return ret;
++
++	csi2->stream_count++;
++
++	return 0;
++}
++
++static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,
++				  struct v4l2_subdev_state *state,
++				  struct v4l2_subdev_selection *sel)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct device *dev = &asd->isys->adev->auxdev.dev;
++	struct v4l2_mbus_framefmt *sink_ffmt;
++	struct v4l2_mbus_framefmt *src_ffmt;
++	struct v4l2_rect *crop;
++
++	if (sel->pad == CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
++		return -EINVAL;
++
++	sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
++								 sel->pad,
++								 sel->stream);
++	if (!sink_ffmt)
++		return -EINVAL;
++
++	src_ffmt = v4l2_subdev_state_get_stream_format(state, sel->pad,
++						       sel->stream);
++	if (!src_ffmt)
++		return -EINVAL;
++
++	crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream);
++	if (!crop)
++		return -EINVAL;
++
++	/* Only vertical cropping is supported */
++	sel->r.left = 0;
++	sel->r.width = sink_ffmt->width;
++	/* Non-bayer formats can't be single line cropped */
++	if (!ipu6_isys_is_bayer_format(sink_ffmt->code))
++		sel->r.top &= ~1;
++	sel->r.height = clamp(sel->r.height & ~1, IPU6_ISYS_MIN_HEIGHT,
++			      sink_ffmt->height - sel->r.top);
++	*crop = sel->r;
++
++	/* update source pad format */
++	src_ffmt->width = sel->r.width;
++	src_ffmt->height = sel->r.height;
++	if (ipu6_isys_is_bayer_format(sink_ffmt->code))
++		src_ffmt->code = ipu6_isys_convert_bayer_order(sink_ffmt->code,
++							       sel->r.left,
++							       sel->r.top);
++	dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n",
++		sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height,
++		src_ffmt->code);
++
++	return 0;
++}
++
++static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd,
++				  struct v4l2_subdev_state *state,
++				  struct v4l2_subdev_selection *sel)
++{
++	struct v4l2_mbus_framefmt *sink_ffmt;
++	struct v4l2_rect *crop;
++	int ret = 0;
++
++	if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK)
++		return -EINVAL;
++
++	sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
++								 sel->pad,
++								 sel->stream);
++	if (!sink_ffmt)
++		return -EINVAL;
++
++	crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream);
++	if (!crop)
++		return -EINVAL;
++
++	switch (sel->target) {
++	case V4L2_SEL_TGT_CROP_DEFAULT:
++	case V4L2_SEL_TGT_CROP_BOUNDS:
++		sel->r.left = 0;
++		sel->r.top = 0;
++		sel->r.width = sink_ffmt->width;
++		sel->r.height = sink_ffmt->height;
++		break;
++	case V4L2_SEL_TGT_CROP:
++		sel->r = *crop;
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static const struct v4l2_subdev_video_ops csi2_sd_video_ops = {
++	.s_stream = set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
++	.init_cfg = ipu6_isys_subdev_init_cfg,
++	.get_fmt = v4l2_subdev_get_fmt,
++	.set_fmt = ipu6_isys_subdev_set_fmt,
++	.get_selection = ipu6_isys_csi2_get_sel,
++	.set_selection = ipu6_isys_csi2_set_sel,
++	.enum_mbus_code = ipu6_isys_subdev_enum_mbus_code,
++	.set_routing = ipu6_isys_subdev_set_routing,
++};
++
++static const struct v4l2_subdev_ops csi2_sd_ops = {
++	.core = &csi2_sd_core_ops,
++	.video = &csi2_sd_video_ops,
++	.pad = &csi2_sd_pad_ops,
++};
++
++static const struct media_entity_operations csi2_entity_ops = {
++	.link_validate = v4l2_subdev_link_validate,
++	.has_pad_interdep = v4l2_subdev_has_pad_interdep,
++};
++
++void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2)
++{
++	if (!csi2->isys)
++		return;
++
++	v4l2_device_unregister_subdev(&csi2->asd.sd);
++	v4l2_subdev_cleanup(&csi2->asd.sd);
++	ipu6_isys_subdev_cleanup(&csi2->asd);
++	csi2->isys = NULL;
++}
++
++int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2,
++			struct ipu6_isys *isys,
++			void __iomem *base, unsigned int index)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	int ret;
++
++	csi2->isys = isys;
++	csi2->base = base;
++	csi2->port = index;
++
++	csi2->asd.sd.entity.ops = &csi2_entity_ops;
++	csi2->asd.isys = isys;
++	ret = ipu6_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0,
++				    NR_OF_CSI2_SINK_PADS, NR_OF_CSI2_SRC_PADS);
++	if (ret)
++		goto fail;
++
++	csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index;
++	csi2->asd.supported_codes = csi2_supported_codes;
++	snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name),
++		 IPU6_ISYS_ENTITY_PREFIX " CSI2 %u", index);
++	v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd);
++	ret = v4l2_subdev_init_finalize(&csi2->asd.sd);
++	if (ret) {
++		dev_err(dev, "failed to init v4l2 subdev\n");
++		goto fail;
++	}
++
++	ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd);
++	if (ret) {
++		dev_err(dev, "failed to register v4l2 subdev\n");
++		goto fail;
++	}
++
++	return 0;
++
++fail:
++	ipu6_isys_csi2_cleanup(csi2);
++
++	return ret;
++}
++
++void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream)
++{
++	struct video_device *vdev = stream->asd->sd.devnode;
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
++	struct v4l2_event ev = {
++		.type = V4L2_EVENT_FRAME_SYNC,
++	};
++
++	ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence);
++	v4l2_event_queue(vdev, &ev);
++
++	dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",
++		csi2->port, ev.u.frame_sync.frame_sequence, stream->vc);
++}
++
++void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream)
++{
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
++	u32 frame_sequence = atomic_read(&stream->sequence);
++
++	dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n",
++		csi2->port, frame_sequence);
++}
++
++int ipu6_isys_csi2_get_remote_desc(u32 source_stream,
++				   struct ipu6_isys_csi2 *csi2,
++				   struct media_entity *source_entity,
++				   struct v4l2_mbus_frame_desc_entry *entry,
++				   int *nr_queues)
++{
++	struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++	struct v4l2_mbus_frame_desc desc;
++	struct v4l2_subdev *source;
++	struct media_pad *pad;
++	unsigned int i;
++	int count = 0;
++	int ret;
++
++	source = media_entity_to_v4l2_subdev(source_entity);
++	if (!source)
++		return -EPIPE;
++
++	pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]);
++	if (!pad)
++		return -EPIPE;
++
++	ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc);
++	if (ret)
++		return ret;
++
++	if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
++		dev_err(dev, "Unsupported frame descriptor type\n");
++		return -EINVAL;
++	}
++
++	for (i = 0; i < desc.num_entries; i++) {
++		if (source_stream == desc.entry[i].stream) {
++			desc_entry = &desc.entry[i];
++			break;
++		}
++	}
++
++	if (!desc_entry) {
++		dev_err(dev, "Failed to find stream %u from remote subdev\n",
++			source_stream);
++		return -EINVAL;
++	}
++
++	if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) {
++		dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc);
++		return -EINVAL;
++	}
++
++	*entry = *desc_entry;
++	for (i = 0; i < desc.num_entries; i++) {
++		if (entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc)
++			count++;
++	}
++
++	*nr_queues = count;
++	return 0;
++}
++
++void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status)
++{
++	struct ipu6_isys_stream *stream = av->stream;
++	struct v4l2_subdev *sd = &stream->asd->sd;
++	struct v4l2_subdev_state *state;
++	struct media_pad *r_pad;
++	unsigned int i;
++	u32 r_stream;
++
++	r_pad = media_pad_remote_pad_first(&av->pad);
++	r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
++
++	state = v4l2_subdev_lock_and_get_active_state(sd);
++
++	for (i = 0; i < state->stream_configs.num_configs; i++) {
++		struct v4l2_subdev_stream_config *cfg =
++			&state->stream_configs.configs[i];
++
++		if (cfg->pad == r_pad->index && r_stream == cfg->stream) {
++			dev_dbg(&av->isys->adev->auxdev.dev,
++				"%s: pad:%u, stream:%u, status:%u\n",
++				sd->entity.name, r_pad->index, r_stream,
++				status);
++			cfg->enabled = status;
++		}
++	}
++
++	v4l2_subdev_unlock_state(state);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
+new file mode 100644
+index 000000000000..d4765bae6112
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
+@@ -0,0 +1,81 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_CSI2_H
++#define IPU6_ISYS_CSI2_H
++
++#include <linux/container_of.h>
++
++#include "ipu6-isys-subdev.h"
++
++struct media_entity;
++struct v4l2_mbus_frame_desc_entry;
++
++struct ipu6_isys_video;
++struct ipu6_isys;
++struct ipu6_isys_csi2_pdata;
++struct ipu6_isys_stream;
++
++#define NR_OF_CSI2_VC			16
++#define INVALID_VC_ID			-1
++#define NR_OF_CSI2_SINK_PADS		1
++#define CSI2_PAD_SINK			0
++#define NR_OF_CSI2_SRC_PADS		8
++#define CSI2_PAD_SRC			1
++#define NR_OF_CSI2_PADS	(NR_OF_CSI2_SINK_PADS + NR_OF_CSI2_SRC_PADS)
++
++#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A		0
++#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B		0
++#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A		95
++#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B		-8
++
++#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A		0
++#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B		0
++#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A		85
++#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B		-2
++
++struct ipu6_isys_csi2 {
++	struct ipu6_isys_subdev asd;
++	struct ipu6_isys_csi2_pdata *pdata;
++	struct ipu6_isys *isys;
++
++	void __iomem *base;
++	u32 receiver_errors;
++	unsigned int nlanes;
++	unsigned int port;
++	unsigned int stream_count;
++};
++
++struct ipu6_isys_csi2_timing {
++	u32 ctermen;
++	u32 csettle;
++	u32 dtermen;
++	u32 dsettle;
++};
++
++struct ipu6_csi2_error {
++	const char *error_string;
++	bool is_info_only;
++};
++
++#define ipu6_isys_subdev_to_csi2(__sd) \
++	container_of(__sd, struct ipu6_isys_csi2, asd)
++
++#define to_ipu6_isys_csi2(__asd) container_of(__asd, struct ipu6_isys_csi2, asd)
++
++s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2);
++int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, struct ipu6_isys *isys,
++			void __iomem *base, unsigned int index);
++void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2);
++void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream);
++void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream);
++void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2);
++void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2);
++int ipu6_isys_csi2_get_remote_desc(u32 source_stream,
++				   struct ipu6_isys_csi2 *csi2,
++				   struct media_entity *source_entity,
++				   struct v4l2_mbus_frame_desc_entry *entry,
++				   int *nr_queues);
++void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status);
++
++#endif /* IPU6_ISYS_CSI2_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+new file mode 100644
+index 000000000000..510c5ca34f9f
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+@@ -0,0 +1,381 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bug.h>
++#include <linux/device.h>
++#include <linux/minmax.h>
++
++#include <media/media-entity.h>
++#include <media/mipi-csi2.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-subdev.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-subdev.h"
++
++unsigned int ipu6_isys_mbus_code_to_bpp(u32 code)
++{
++	switch (code) {
++	case MEDIA_BUS_FMT_RGB888_1X24:
++		return 24;
++	case MEDIA_BUS_FMT_RGB565_1X16:
++	case MEDIA_BUS_FMT_UYVY8_1X16:
++	case MEDIA_BUS_FMT_YUYV8_1X16:
++		return 16;
++	case MEDIA_BUS_FMT_SBGGR12_1X12:
++	case MEDIA_BUS_FMT_SGBRG12_1X12:
++	case MEDIA_BUS_FMT_SGRBG12_1X12:
++	case MEDIA_BUS_FMT_SRGGB12_1X12:
++		return 12;
++	case MEDIA_BUS_FMT_SBGGR10_1X10:
++	case MEDIA_BUS_FMT_SGBRG10_1X10:
++	case MEDIA_BUS_FMT_SGRBG10_1X10:
++	case MEDIA_BUS_FMT_SRGGB10_1X10:
++		return 10;
++	case MEDIA_BUS_FMT_SBGGR8_1X8:
++	case MEDIA_BUS_FMT_SGBRG8_1X8:
++	case MEDIA_BUS_FMT_SGRBG8_1X8:
++	case MEDIA_BUS_FMT_SRGGB8_1X8:
++		return 8;
++	default:
++		WARN_ON(1);
++		return 8;
++	}
++}
++
++unsigned int ipu6_isys_mbus_code_to_mipi(u32 code)
++{
++	switch (code) {
++	case MEDIA_BUS_FMT_RGB565_1X16:
++		return MIPI_CSI2_DT_RGB565;
++	case MEDIA_BUS_FMT_RGB888_1X24:
++		return MIPI_CSI2_DT_RGB888;
++	case MEDIA_BUS_FMT_UYVY8_1X16:
++	case MEDIA_BUS_FMT_YUYV8_1X16:
++		return MIPI_CSI2_DT_YUV422_8B;
++	case MEDIA_BUS_FMT_SBGGR12_1X12:
++	case MEDIA_BUS_FMT_SGBRG12_1X12:
++	case MEDIA_BUS_FMT_SGRBG12_1X12:
++	case MEDIA_BUS_FMT_SRGGB12_1X12:
++		return MIPI_CSI2_DT_RAW12;
++	case MEDIA_BUS_FMT_SBGGR10_1X10:
++	case MEDIA_BUS_FMT_SGBRG10_1X10:
++	case MEDIA_BUS_FMT_SGRBG10_1X10:
++	case MEDIA_BUS_FMT_SRGGB10_1X10:
++		return MIPI_CSI2_DT_RAW10;
++	case MEDIA_BUS_FMT_SBGGR8_1X8:
++	case MEDIA_BUS_FMT_SGBRG8_1X8:
++	case MEDIA_BUS_FMT_SGRBG8_1X8:
++	case MEDIA_BUS_FMT_SRGGB8_1X8:
++		return MIPI_CSI2_DT_RAW8;
++	default:
++		/* return unavailable MIPI data type - 0x3f */
++		WARN_ON(1);
++		return 0x3f;
++	}
++}
++
++bool ipu6_isys_is_bayer_format(u32 code)
++{
++	switch (ipu6_isys_mbus_code_to_mipi(code)) {
++	case MIPI_CSI2_DT_RAW8:
++	case MIPI_CSI2_DT_RAW10:
++	case MIPI_CSI2_DT_RAW12:
++		return true;
++	default:
++		return false;
++	}
++}
++
++u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y)
++{
++	static const u32 code_map[] = {
++		MEDIA_BUS_FMT_SRGGB8_1X8,
++		MEDIA_BUS_FMT_SGRBG8_1X8,
++		MEDIA_BUS_FMT_SGBRG8_1X8,
++		MEDIA_BUS_FMT_SBGGR8_1X8,
++		MEDIA_BUS_FMT_SRGGB10_1X10,
++		MEDIA_BUS_FMT_SGRBG10_1X10,
++		MEDIA_BUS_FMT_SGBRG10_1X10,
++		MEDIA_BUS_FMT_SBGGR10_1X10,
++		MEDIA_BUS_FMT_SRGGB12_1X12,
++		MEDIA_BUS_FMT_SGRBG12_1X12,
++		MEDIA_BUS_FMT_SGBRG12_1X12,
++		MEDIA_BUS_FMT_SBGGR12_1X12
++	};
++	u32 i;
++
++	for (i = 0; i < ARRAY_SIZE(code_map); i++)
++		if (code_map[i] == code)
++			break;
++
++	if (WARN_ON(i == ARRAY_SIZE(code_map)))
++		return code;
++
++	return code_map[i ^ (((y & 1) << 1) | (x & 1))];
++}
++
++int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
++			     struct v4l2_subdev_state *state,
++			     struct v4l2_subdev_format *format)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct v4l2_mbus_framefmt *fmt;
++	struct v4l2_rect *crop;
++	u32 code = asd->supported_codes[0];
++	u32 other_pad, other_stream;
++	unsigned int i;
++	int ret;
++
++	/* No transcoding, source and sink formats must match. */
++	if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) &&
++	    sd->entity.num_pads > 1)
++		return v4l2_subdev_get_fmt(sd, state, format);
++
++	format->format.width = clamp(format->format.width, IPU6_ISYS_MIN_WIDTH,
++				     IPU6_ISYS_MAX_WIDTH);
++	format->format.height = clamp(format->format.height,
++				      IPU6_ISYS_MIN_HEIGHT,
++				      IPU6_ISYS_MAX_HEIGHT);
++
++	for (i = 0; asd->supported_codes[i]; i++) {
++		if (asd->supported_codes[i] == format->format.code) {
++			code = asd->supported_codes[i];
++			break;
++		}
++	}
++	format->format.code = code;
++	format->format.field = V4L2_FIELD_NONE;
++
++	/* Store the format and propagate it to the source pad. */
++	fmt = v4l2_subdev_state_get_stream_format(state, format->pad,
++						  format->stream);
++	if (!fmt)
++		return -EINVAL;
++
++	*fmt = format->format;
++
++	if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK))
++		return 0;
++
++	/* propagate format to following source pad */
++	fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
++							   format->stream);
++	if (!fmt)
++		return -EINVAL;
++
++	*fmt = format->format;
++
++	ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
++						    format->pad,
++						    format->stream,
++						    &other_pad,
++						    &other_stream);
++	if (ret)
++		return -EINVAL;
++
++	crop = v4l2_subdev_state_get_stream_crop(state, other_pad,
++						 other_stream);
++	/* reset crop */
++	crop->left = 0;
++	crop->top = 0;
++	crop->width = fmt->width;
++	crop->height = fmt->height;
++
++	return 0;
++}
++
++int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
++				    struct v4l2_subdev_state *state,
++				    struct v4l2_subdev_mbus_code_enum *code)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	const u32 *supported_codes = asd->supported_codes;
++	u32 index;
++
++	for (index = 0; supported_codes[index]; index++) {
++		if (index == code->index) {
++			code->code = supported_codes[index];
++			return 0;
++		}
++	}
++
++	return -EINVAL;
++}
++
++static int subdev_set_routing(struct v4l2_subdev *sd,
++			      struct v4l2_subdev_state *state,
++			      struct v4l2_subdev_krouting *routing)
++{
++	static const struct v4l2_mbus_framefmt format = {
++		.width = 4096,
++		.height = 3072,
++		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
++		.field = V4L2_FIELD_NONE,
++	};
++	int ret;
++
++	ret = v4l2_subdev_routing_validate(sd, routing,
++					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
++	if (ret)
++		return ret;
++
++	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
++}
++
++int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
++				 struct v4l2_mbus_framefmt *format)
++{
++	struct v4l2_mbus_framefmt *fmt;
++	struct v4l2_subdev_state *state;
++
++	if (!sd || !format)
++		return -EINVAL;
++
++	state = v4l2_subdev_lock_and_get_active_state(sd);
++	fmt = v4l2_subdev_state_get_stream_format(state, pad, stream);
++	if (fmt)
++		*format = *fmt;
++	v4l2_subdev_unlock_state(state);
++
++	return fmt ? 0 : -EINVAL;
++}
++
++int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
++				  struct v4l2_rect *crop)
++{
++	struct v4l2_subdev_state *state;
++	struct v4l2_rect *rect;
++
++	if (!sd || !crop)
++		return -EINVAL;
++
++	state = v4l2_subdev_lock_and_get_active_state(sd);
++	rect = v4l2_subdev_state_get_stream_crop(state, pad, stream);
++	if (rect)
++		*crop = *rect;
++	v4l2_subdev_unlock_state(state);
++
++	return rect ? 0 : -EINVAL;
++}
++
++u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
++{
++	struct v4l2_subdev_state *state;
++	struct v4l2_subdev_route *routes;
++	unsigned int i;
++	u32 source_stream = 0;
++
++	state = v4l2_subdev_lock_and_get_active_state(sd);
++	if (!state)
++		return 0;
++
++	routes = state->routing.routes;
++	for (i = 0; i < state->routing.num_routes; i++) {
++		if (routes[i].source_pad == pad) {
++			source_stream = routes[i].source_stream;
++			break;
++		}
++	}
++
++	v4l2_subdev_unlock_state(state);
++
++	return source_stream;
++}
++
++int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd,
++			      struct v4l2_subdev_state *state)
++{
++	struct v4l2_subdev_route route = {
++		.sink_pad = 0,
++		.sink_stream = 0,
++		.source_pad = 1,
++		.source_stream = 0,
++		.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
++	};
++	struct v4l2_subdev_krouting routing = {
++		.num_routes = 1,
++		.routes = &route,
++	};
++
++	return subdev_set_routing(sd, state, &routing);
++}
++
++int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
++				 struct v4l2_subdev_state *state,
++				 enum v4l2_subdev_format_whence which,
++				 struct v4l2_subdev_krouting *routing)
++{
++	return subdev_set_routing(sd, state, routing);
++}
++
++int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
++			  const struct v4l2_subdev_ops *ops,
++			  unsigned int nr_ctrls,
++			  unsigned int num_sink_pads,
++			  unsigned int num_source_pads)
++{
++	unsigned int num_pads = num_sink_pads + num_source_pads;
++	unsigned int i;
++	int ret;
++
++	v4l2_subdev_init(&asd->sd, ops);
++
++	asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++			 V4L2_SUBDEV_FL_HAS_EVENTS |
++			 V4L2_SUBDEV_FL_STREAMS;
++	asd->sd.owner = THIS_MODULE;
++	asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
++
++	asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
++				sizeof(*asd->pad), GFP_KERNEL);
++
++	if (!asd->pad)
++		return -ENOMEM;
++
++	for (i = 0; i < num_sink_pads; i++)
++		asd->pad[i].flags = MEDIA_PAD_FL_SINK |
++				    MEDIA_PAD_FL_MUST_CONNECT;
++
++	for (i = num_sink_pads; i < num_pads; i++)
++		asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;
++
++	ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
++	if (ret)
++		return ret;
++
++	if (asd->ctrl_init) {
++		ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
++		if (ret)
++			goto out_media_entity_cleanup;
++
++		asd->ctrl_init(&asd->sd);
++		if (asd->ctrl_handler.error) {
++			ret = asd->ctrl_handler.error;
++			goto out_v4l2_ctrl_handler_free;
++		}
++
++		asd->sd.ctrl_handler = &asd->ctrl_handler;
++	}
++
++	asd->source = -1;
++
++	return 0;
++
++out_v4l2_ctrl_handler_free:
++	v4l2_ctrl_handler_free(&asd->ctrl_handler);
++
++out_media_entity_cleanup:
++	media_entity_cleanup(&asd->sd.entity);
++
++	return ret;
++}
++
++void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd)
++{
++	media_entity_cleanup(&asd->sd.entity);
++	v4l2_ctrl_handler_free(&asd->ctrl_handler);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+new file mode 100644
+index 000000000000..adea2a55761d
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+@@ -0,0 +1,61 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_SUBDEV_H
++#define IPU6_ISYS_SUBDEV_H
++
++#include <linux/container_of.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-subdev.h>
++
++struct ipu6_isys;
++
++struct ipu6_isys_subdev {
++	struct v4l2_subdev sd;
++	struct ipu6_isys *isys;
++	u32 const *supported_codes;
++	struct media_pad *pad;
++	struct v4l2_ctrl_handler ctrl_handler;
++	void (*ctrl_init)(struct v4l2_subdev *sd);
++	int source;	/* SSI stream source; -1 if unset */
++};
++
++#define to_ipu6_isys_subdev(__sd) \
++	container_of(__sd, struct ipu6_isys_subdev, sd)
++
++unsigned int ipu6_isys_mbus_code_to_bpp(u32 code);
++unsigned int ipu6_isys_mbus_code_to_mipi(u32 code);
++bool ipu6_isys_is_bayer_format(u32 code);
++u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y);
++
++int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
++			     struct v4l2_subdev_state *state,
++			     struct v4l2_subdev_format *fmt);
++int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
++				    struct v4l2_subdev_state *state,
++				    struct v4l2_subdev_mbus_code_enum
++				    *code);
++int ipu6_isys_subdev_link_validate(struct v4l2_subdev *sd,
++				   struct media_link *link,
++				   struct v4l2_subdev_format *source_fmt,
++				   struct v4l2_subdev_format *sink_fmt);
++u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad);
++int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
++				 struct v4l2_mbus_framefmt *format);
++int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
++				  struct v4l2_rect *crop);
++int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd,
++			      struct v4l2_subdev_state *state);
++int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
++				 struct v4l2_subdev_state *state,
++				 enum v4l2_subdev_format_whence which,
++				 struct v4l2_subdev_krouting *routing);
++int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
++			  const struct v4l2_subdev_ops *ops,
++			  unsigned int nr_ctrls,
++			  unsigned int num_sink_pads,
++			  unsigned int num_source_pads);
++void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd);
++#endif /* IPU6_ISYS_SUBDEV_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h b/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h
+new file mode 100644
+index 000000000000..2034e1109d98
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h
+@@ -0,0 +1,189 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2023 Intel Corporation */
++
++#ifndef IPU6_PLATFORM_ISYS_CSI2_REG_H
++#define IPU6_PLATFORM_ISYS_CSI2_REG_H
++
++#include <linux/bits.h>
++
++#define CSI_REG_BASE			0x220000
++#define CSI_REG_BASE_PORT(id)		((id) * 0x1000)
++
++#define IPU6_CSI_PORT_A_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(0))
++#define IPU6_CSI_PORT_B_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(1))
++#define IPU6_CSI_PORT_C_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(2))
++#define IPU6_CSI_PORT_D_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(3))
++#define IPU6_CSI_PORT_E_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(4))
++#define IPU6_CSI_PORT_F_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(5))
++#define IPU6_CSI_PORT_G_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(6))
++#define IPU6_CSI_PORT_H_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(7))
++
++/* CSI Port Genral Purpose Registers */
++#define CSI_REG_PORT_GPREG_SRST                 0x0
++#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST    0x4
++#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL    0x8
++
++/*
++ * Port IRQs mapping events:
++ * IRQ0 - CSI_FE event
++ * IRQ1 - CSI_SYNC
++ * IRQ2 - S2M_SIDS0TO7
++ * IRQ3 - S2M_SIDS8TO15
++ */
++#define CSI_PORT_REG_BASE_IRQ_CSI               0x80
++#define CSI_PORT_REG_BASE_IRQ_CSI_SYNC          0xA0
++#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS0TOS7     0xC0
++#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS8TOS15    0xE0
++
++#define CSI_PORT_REG_BASE_IRQ_EDGE_OFFSET	0x0
++#define CSI_PORT_REG_BASE_IRQ_MASK_OFFSET	0x4
++#define CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET	0x8
++#define CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET	0xc
++#define CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET	0x10
++#define CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET	0x14
++
++#define IPU6SE_CSI_RX_ERROR_IRQ_MASK		GENMASK(18, 0)
++#define IPU6_CSI_RX_ERROR_IRQ_MASK		GENMASK(19, 0)
++
++#define CSI_RX_NUM_ERRORS_IN_IRQ		20
++#define CSI_RX_NUM_IRQ				32
++
++#define IPU_CSI_RX_IRQ_FS_VC(chn)	(1 << ((chn) * 2))
++#define IPU_CSI_RX_IRQ_FE_VC(chn)	(2 << ((chn) * 2))
++
++/* PPI2CSI */
++#define CSI_REG_PPI2CSI_ENABLE				0x200
++#define CSI_REG_PPI2CSI_CONFIG_PPI_INTF			0x204
++#define PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK		GENMASK(4, 3)
++#define CSI_REG_PPI2CSI_CONFIG_CSI_FEATURE		0x208
++
++enum CSI_PPI2CSI_CTRL {
++	CSI_PPI2CSI_DISABLE = 0,
++	CSI_PPI2CSI_ENABLE = 1,
++};
++
++/* CSI_FE */
++#define CSI_REG_CSI_FE_ENABLE                   0x280
++#define CSI_REG_CSI_FE_MODE                     0x284
++#define CSI_REG_CSI_FE_MUX_CTRL                 0x288
++#define CSI_REG_CSI_FE_SYNC_CNTR_SEL            0x290
++
++enum CSI_FE_ENABLE_TYPE {
++	CSI_FE_DISABLE = 0,
++	CSI_FE_ENABLE = 1,
++};
++
++enum CSI_FE_MODE_TYPE {
++	CSI_FE_DPHY_MODE = 0,
++	CSI_FE_CPHY_MODE = 1,
++};
++
++enum CSI_FE_INPUT_SELECTOR {
++	CSI_SENSOR_INPUT = 0,
++	CSI_MIPIGEN_INPUT = 1,
++};
++
++enum CSI_FE_SYNC_CNTR_SEL_TYPE {
++	CSI_CNTR_SENSOR_LINE_ID = BIT(0),
++	CSI_CNTR_INT_LINE_PKT_ID = ~CSI_CNTR_SENSOR_LINE_ID,
++	CSI_CNTR_SENSOR_FRAME_ID = BIT(1),
++	CSI_CNTR_INT_FRAME_PKT_ID = ~CSI_CNTR_SENSOR_FRAME_ID,
++};
++
++/* CSI HUB General Purpose Registers */
++#define CSI_REG_HUB_GPREG_SRST			(CSI_REG_BASE + 0x18000)
++#define CSI_REG_HUB_GPREG_SLV_REG_SRST		(CSI_REG_BASE + 0x18004)
++
++#define CSI_REG_HUB_DRV_ACCESS_PORT(id)	(CSI_REG_BASE + 0x18018 + (id) * 4)
++#define CSI_REG_HUB_FW_ACCESS_PORT_OFS		0x17000
++#define CSI_REG_HUB_FW_ACCESS_PORT_V6OFS	0x16000
++#define CSI_REG_HUB_FW_ACCESS_PORT(ofs, id)	\
++					(CSI_REG_BASE + (ofs) + (id) * 4)
++
++enum CSI_PORT_CLK_GATING_SWITCH {
++	CSI_PORT_CLK_GATING_OFF = 0,
++	CSI_PORT_CLK_GATING_ON = 1,
++};
++
++#define CSI_REG_BASE_HUB_IRQ                        0x18200
++
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE			0x238200
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK			0x238204
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS			0x238208
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR			0x23820c
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE			0x238210
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE		0x238214
++
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE			0x238220
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK			0x238224
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS			0x238228
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR			0x23822c
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE			0x238230
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE		0x238234
++
++/* MTL IPU6V6 irq ctrl0 & ctrl1 */
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE			0x238700
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK			0x238704
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS		0x238708
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR			0x23870c
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE		0x238710
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE	0x238714
++
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE			0x238720
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK			0x238724
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS		0x238728
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR			0x23872c
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE		0x238730
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE	0x238734
++
++/*
++ * 3:0 CSI_PORT.irq_out[3:0] CSI_PORT_CTRL0 IRQ outputs (4bits)
++ * [0] CSI_PORT.IRQ_CTRL0_csi
++ * [1] CSI_PORT.IRQ_CTRL1_csi_sync
++ * [2] CSI_PORT.IRQ_CTRL2_s2m_sids0to7
++ * [3] CSI_PORT.IRQ_CTRL3_s2m_sids8to15
++ */
++#define IPU6_ISYS_UNISPART_IRQ_CSI2(port)		\
++				   (0x3 << ((port) * IPU6_CSI_IRQ_NUM_PER_PIPE))
++
++/*
++ * ipu6se support 2 front ends, 2 port per front end, 4 ports 0..3
++ * sip0 - 0, 1
++ * sip1 - 2, 3
++ * 0 and 2 support 4 data lanes, 1 and 3 support 2 data lanes
++ * all offset are base from isys base address
++ */
++
++#define CSI2_HUB_GPREG_SIP_SRST(sip)			(0x238038 + (sip) * 4)
++#define CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip)		(0x238050 + (sip) * 4)
++
++#define CSI2_HUB_GPREG_DPHY_TIMER_INCR			0x238040
++#define CSI2_HUB_GPREG_HPLL_FREQ			0x238044
++#define CSI2_HUB_GPREG_IS_CLK_RATIO			0x238048
++#define CSI2_HUB_GPREG_HPLL_FREQ_ISCLK_RATE_OVERRIDE	0x23804c
++#define CSI2_HUB_GPREG_PORT_CLKGATING_DISABLE		0x238058
++#define CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL		0x23805c
++#define CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL		0x238088
++#define CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL		0x2380a4
++#define CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL		0x2380d0
++
++#define CSI2_SIP_TOP_CSI_RX_BASE(sip)		(0x23805c + (sip) * 0x48)
++#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port)	(0x23805c + ((port) / 2) * 0x48)
++#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port)	(0x238088 + ((port) / 2) * 0x48)
++
++/* offset from port base */
++#define CSI2_SIP_TOP_CSI_RX_PORT_CONTROL		0x0
++#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE	0x4
++#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE	0x8
++#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(lane)	(0xc + (lane) * 8)
++#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(lane)	(0x10 + (lane) * 8)
++
++#endif /* IPU6_ISYS_CSI2_REG_H */
+-- 
+2.43.2
+
+
+From 71d3da5e8f1ea094e26030a12ceed4553cbe182a Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:23 +0800
+Subject: [PATCH 16/33] media: intel/ipu6: add the CSI2 DPHY implementation
+
+IPU6 CSI2 DPHY hardware varies on different platforms, current
+IPU6 has three DPHY hardware instance which maybe used on tigerlake,
+alderlake, metorlake and jasperlake. MCD DPHY is shipped on tigerlake
+and alderlake, DWC DPHY is shipped on metorlake.
+
+Each PHY has its own register space, input system driver call the
+DPHY callback which was set at isys_probe().
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ .../media/pci/intel/ipu6/ipu6-isys-dwc-phy.c  | 536 +++++++++++++
+ .../media/pci/intel/ipu6/ipu6-isys-jsl-phy.c  | 242 ++++++
+ .../media/pci/intel/ipu6/ipu6-isys-mcd-phy.c  | 720 ++++++++++++++++++
+ 3 files changed, 1498 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
+new file mode 100644
+index 000000000000..a4bb5ff51d4e
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
+@@ -0,0 +1,536 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/iopoll.h>
++#include <linux/math64.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++
++#define IPU6_DWC_DPHY_BASE(i)			(0x238038 + 0x34 * (i))
++#define IPU6_DWC_DPHY_RSTZ			0x00
++#define IPU6_DWC_DPHY_SHUTDOWNZ			0x04
++#define IPU6_DWC_DPHY_HSFREQRANGE		0x08
++#define IPU6_DWC_DPHY_CFGCLKFREQRANGE		0x0c
++#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE	0x10
++#define IPU6_DWC_DPHY_TEST_IFC_REQ		0x14
++#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION	0x18
++#define IPU6_DWC_DPHY_DFT_CTRL0			0x28
++#define IPU6_DWC_DPHY_DFT_CTRL1			0x2c
++#define IPU6_DWC_DPHY_DFT_CTRL2			0x30
++
++/*
++ * test IFC request definition:
++ * - req: 0 for read, 1 for write
++ * - 12 bits address
++ * - 8bits data (will ignore for read)
++ * --24----16------4-----0
++ * --|-data-|-addr-|-req-|
++ */
++#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \
++				  FIELD_PREP(GENMASK(15, 4), addr) | \
++				  FIELD_PREP(GENMASK(1, 0), req))
++
++#define TEST_IFC_REQ_READ	0
++#define TEST_IFC_REQ_WRITE	1
++#define TEST_IFC_REQ_RESET	2
++
++#define TEST_IFC_ACCESS_MODE_FSM	0
++#define TEST_IFC_ACCESS_MODE_IFC_CTL	1
++
++enum phy_fsm_state {
++	PHY_FSM_STATE_POWERON = 0,
++	PHY_FSM_STATE_BGPON = 1,
++	PHY_FSM_STATE_CAL_TYPE = 2,
++	PHY_FSM_STATE_BURNIN_CAL = 3,
++	PHY_FSM_STATE_TERMCAL = 4,
++	PHY_FSM_STATE_OFFSETCAL = 5,
++	PHY_FSM_STATE_OFFSET_LANE = 6,
++	PHY_FSM_STATE_IDLE = 7,
++	PHY_FSM_STATE_ULP = 8,
++	PHY_FSM_STATE_DDLTUNNING = 9,
++	PHY_FSM_STATE_SKEW_BACKWARD = 10,
++	PHY_FSM_STATE_INVALID,
++};
++
++static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++			   u32 data)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
++
++	dev_dbg(dev, "write: reg 0x%lx = data 0x%x", base + addr - isys_base,
++		data);
++	writel(data, base + addr);
++}
++
++static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
++	u32 data;
++
++	data = readl(base + addr);
++	dev_dbg(dev, "read: reg 0x%lx = data 0x%x", base + addr - isys_base,
++		data);
++
++	return data;
++}
++
++static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++				u32 data, u8 shift, u8 width)
++{
++	u32 temp;
++	u32 mask;
++
++	mask = (1 << width) - 1;
++	temp = dwc_dphy_read(isys, phy_id, addr);
++	temp &= ~(mask << shift);
++	temp |= (data & mask) << shift;
++	dwc_dphy_write(isys, phy_id, addr, temp);
++}
++
++static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id,
++					     u32 addr, u8 shift,  u8 width)
++{
++	u32 val;
++
++	val = dwc_dphy_read(isys, phy_id, addr) >> shift;
++	return val & ((1 << width) - 1);
++}
++
++#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC)
++static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++			     u32 *val)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
++	void __iomem *reg;
++	u32 completion;
++	int ret;
++
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
++		       IFC_REQ(TEST_IFC_REQ_READ, addr, 0));
++	reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
++	ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
++				 10, DWC_DPHY_TIMEOUT);
++	if (ret) {
++		dev_err(dev, "DWC ifc request read timeout\n");
++		return ret;
++	}
++
++	*val = completion >> 8 & 0xff;
++	*val = FIELD_GET(GENMASK(15, 8), completion);
++	dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val);
++
++	return 0;
++}
++
++static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++			      u32 data)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
++	void __iomem *reg;
++	u32 completion;
++	int ret;
++
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
++		       IFC_REQ(TEST_IFC_REQ_WRITE, addr, data));
++	completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION);
++	reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
++	ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
++				 10, DWC_DPHY_TIMEOUT);
++	if (ret)
++		dev_err(dev, "DWC ifc request write timeout\n");
++
++	return ret;
++}
++
++static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id,
++				    u32 addr, u32 data, u8 shift, u8 width)
++{
++	u32 temp, mask;
++	int ret;
++
++	ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp);
++	if (ret)
++		return;
++
++	mask = (1 << width) - 1;
++	temp &= ~(mask << shift);
++	temp |= (data & mask) << shift;
++	dwc_dphy_ifc_write(isys, phy_id, addr, temp);
++}
++
++static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++				  u8 shift, u8 width)
++{
++	int ret;
++	u32 val;
++
++	ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val);
++	if (ret)
++		return 0;
++
++	return ((val >> shift) & ((1 << width) - 1));
++}
++
++static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	u32 fsm_state;
++	int ret;
++
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1);
++	usleep_range(10, 20);
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1);
++
++	ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state,
++				(fsm_state == PHY_FSM_STATE_IDLE ||
++				 fsm_state == PHY_FSM_STATE_ULP),
++				100, DWC_DPHY_TIMEOUT, false, isys,
++				phy_id, 0x1e, 0, 4);
++
++	if (ret)
++		dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id,
++			fsm_state);
++
++	return ret;
++}
++
++struct dwc_dphy_freq_range {
++	u8 hsfreq;
++	u16 min;
++	u16 max;
++	u16 default_mbps;
++	u16 osc_freq_target;
++};
++
++#define DPHY_FREQ_RANGE_NUM		(63)
++#define DPHY_FREQ_RANGE_INVALID_INDEX	(0xff)
++static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {
++	{0x00,	80,	97,	80,	335},
++	{0x10,	80,	107,	90,	335},
++	{0x20,	84,	118,	100,	335},
++	{0x30,	93,	128,	110,	335},
++	{0x01,	103,	139,	120,	335},
++	{0x11,	112,	149,	130,	335},
++	{0x21,	122,	160,	140,	335},
++	{0x31,	131,	170,	150,	335},
++	{0x02,	141,	181,	160,	335},
++	{0x12,	150,	191,	170,	335},
++	{0x22,	160,	202,	180,	335},
++	{0x32,	169,	212,	190,	335},
++	{0x03,	183,	228,	205,	335},
++	{0x13,	198,	244,	220,	335},
++	{0x23,	212,	259,	235,	335},
++	{0x33,	226,	275,	250,	335},
++	{0x04,	250,	301,	275,	335},
++	{0x14,	274,	328,	300,	335},
++	{0x25,	297,	354,	325,	335},
++	{0x35,	321,	380,	350,	335},
++	{0x05,	369,	433,	400,	335},
++	{0x16,	416,	485,	450,	335},
++	{0x26,	464,	538,	500,	335},
++	{0x37,	511,	590,	550,	335},
++	{0x07,	559,	643,	600,	335},
++	{0x18,	606,	695,	650,	335},
++	{0x28,	654,	748,	700,	335},
++	{0x39,	701,	800,	750,	335},
++	{0x09,	749,	853,	800,	335},
++	{0x19,	796,	905,	850,	335},
++	{0x29,	844,	958,	900,	335},
++	{0x3a,	891,	1010,	950,	335},
++	{0x0a,	939,	1063,	1000,	335},
++	{0x1a,	986,	1115,	1050,	335},
++	{0x2a,	1034,	1168,	1100,	335},
++	{0x3b,	1081,	1220,	1150,	335},
++	{0x0b,	1129,	1273,	1200,	335},
++	{0x1b,	1176,	1325,	1250,	335},
++	{0x2b,	1224,	1378,	1300,	335},
++	{0x3c,	1271,	1430,	1350,	335},
++	{0x0c,	1319,	1483,	1400,	335},
++	{0x1c,	1366,	1535,	1450,	335},
++	{0x2c,	1414,	1588,	1500,	335},
++	{0x3d,	1461,	1640,	1550,	208},
++	{0x0d,	1509,	1693,	1600,	214},
++	{0x1d,	1556,	1745,	1650,	221},
++	{0x2e,	1604,	1798,	1700,	228},
++	{0x3e,	1651,	1850,	1750,	234},
++	{0x0e,	1699,	1903,	1800,	241},
++	{0x1e,	1746,	1955,	1850,	248},
++	{0x2f,	1794,	2008,	1900,	255},
++	{0x3f,	1841,	2060,	1950,	261},
++	{0x0f,	1889,	2113,	2000,	268},
++	{0x40,	1936,	2165,	2050,	275},
++	{0x41,	1984,	2218,	2100,	281},
++	{0x42,	2031,	2270,	2150,	288},
++	{0x43,	2079,	2323,	2200,	294},
++	{0x44,	2126,	2375,	2250,	302},
++	{0x45,	2174,	2428,	2300,	308},
++	{0x46,	2221,	2480,	2350,	315},
++	{0x47,	2269,	2500,	2400,	321},
++	{0x48,	2316,	2500,	2450,	328},
++	{0x49,	2364,	2500,	2500,	335}
++};
++
++static u16 get_hsfreq_by_mbps(u32 mbps)
++{
++	unsigned int i = DPHY_FREQ_RANGE_NUM;
++
++	while (i--) {
++		if (freqranges[i].default_mbps == mbps ||
++		    (mbps >= freqranges[i].min && mbps <= freqranges[i].max))
++			return i;
++	}
++
++	return DPHY_FREQ_RANGE_INVALID_INDEX;
++}
++
++static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
++				    u32 phy_id, u32 mbps)
++{
++	struct ipu6_bus_device *adev = isys->adev;
++	struct device *dev = &adev->auxdev.dev;
++	struct ipu6_device *isp = adev->isp;
++	u32 cfg_clk_freqrange;
++	u32 osc_freq_target;
++	u32 index;
++
++	dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps);
++
++	index = get_hsfreq_by_mbps(mbps);
++	if (index == DPHY_FREQ_RANGE_INVALID_INDEX) {
++		dev_err(dev, "link freq not found for mbps %u", mbps);
++		return -EINVAL;
++	}
++
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE,
++			    freqranges[index].hsfreq, 0, 7);
++
++	/* Force termination Calibration */
++	if (isys->phy_termcal_val) {
++		dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1);
++		dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2);
++		dwc_dphy_ifc_write_mask(isys, phy_id, 0x209,
++					isys->phy_termcal_val, 4, 4);
++	}
++
++	/*
++	 * Enable override to configure the DDL target oscillation
++	 * frequency on bit 0 of register 0xe4
++	 */
++	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1);
++	/*
++	 * configure registers 0xe2, 0xe3 with the
++	 * appropriate DDL target oscillation frequency
++	 * 0x1cc(460)
++	 */
++	osc_freq_target = freqranges[index].osc_freq_target;
++	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2,
++				osc_freq_target & 0xff, 0, 8);
++	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3,
++				(osc_freq_target >> 8) & 0xf, 0, 4);
++
++	if (mbps < 1500) {
++		/* deskew_polarity_rw, for < 1.5Gbps */
++		dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1);
++	}
++
++	/*
++	 * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4]
++	 * (38.4 - 17) * 4 = ~85 (0x55)
++	 */
++	cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10;
++	dev_dbg(dev, "ref_clk = %u clk_freqrange = %u",
++		isp->buttress.ref_clk, cfg_clk_freqrange);
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE,
++			    cfg_clk_freqrange, 0, 8);
++
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1);
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1);
++
++	return 0;
++}
++
++static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master,
++					 u32 slave, u32 mbps)
++{
++	/* Config mastermacro */
++	dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1);
++
++	/* Config master PHY clk lane to drive long channel clk */
++	dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1);
++
++	/* Config both PHYs data lanes to get clk from long channel */
++	dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1);
++	dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1);
++
++	/* Config slave PHY clk lane to bypass long channel clk to DDR clk */
++	dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1);
++
++	/* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */
++	dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2);
++
++	/* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */
++	dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1);
++
++	/* Turn off slave PHY LP-RX clk lane */
++	dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5);
++}
++
++#define PHY_E	4
++static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	u32 rescal_done;
++	int ret;
++
++	ret = dwc_dphy_pwr_up(isys, phy_id);
++	if (ret != 0) {
++		dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret);
++		return ret;
++	}
++
++	/* reset forcerxmode */
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1);
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1);
++
++	dev_dbg(dev, "Dphy %u is ready!", phy_id);
++
++	if (phy_id != PHY_E || isys->phy_termcal_val)
++		return 0;
++
++	usleep_range(100, 200);
++	rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1);
++	if (rescal_done) {
++		isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id,
++							       0x220, 2, 4);
++		dev_dbg(dev, "termcal done with value = %u",
++			isys->phy_termcal_val);
++	}
++
++	return 0;
++}
++
++static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id)
++{
++	dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id);
++
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0);
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0);
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE,
++		       TEST_IFC_ACCESS_MODE_FSM);
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
++		       TEST_IFC_REQ_RESET);
++}
++
++int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u32 phy_id, primary, secondary;
++	u32 nlanes, port, mbps;
++	s64 link_freq;
++	int ret;
++
++	port = cfg->port;
++
++	if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
++		dev_warn(dev, "invalid port ID %d\n", port);
++		return -EINVAL;
++	}
++
++	nlanes = cfg->nlanes;
++	/* only port 0, 2 and 4 support 4 lanes */
++	if (nlanes == 4 && port % 2) {
++		dev_err(dev, "invalid csi-port %u with %u lanes\n", port,
++			nlanes);
++		return -EINVAL;
++	}
++
++	link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]);
++	if (link_freq < 0) {
++		dev_err(dev, "get link freq failed(%lld).\n", link_freq);
++		return link_freq;
++	}
++
++	mbps = div_u64(link_freq, 500000);
++
++	phy_id = port;
++	primary = port & ~1;
++	secondary = primary + 1;
++	if (on) {
++		if (nlanes == 4) {
++			dev_dbg(dev, "config phy %u and %u in aggr mode\n",
++				primary, secondary);
++
++			ipu6_isys_dwc_phy_reset(isys, primary);
++			ipu6_isys_dwc_phy_reset(isys, secondary);
++			ipu6_isys_dwc_phy_aggr_setup(isys, primary,
++						     secondary, mbps);
++
++			ret = ipu6_isys_dwc_phy_config(isys, primary, mbps);
++			if (ret)
++				return ret;
++			ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps);
++			if (ret)
++				return ret;
++
++			ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary);
++			if (ret)
++				return ret;
++
++			ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary);
++			return ret;
++		}
++
++		dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n",
++			phy_id, nlanes);
++
++		ipu6_isys_dwc_phy_reset(isys, phy_id);
++		ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps);
++		if (ret)
++			return ret;
++
++		ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id);
++		return ret;
++	}
++
++	if (nlanes == 4) {
++		dev_dbg(dev, "Power down phy %u and phy %u for port %u\n",
++			primary, secondary, port);
++		ipu6_isys_dwc_phy_reset(isys, secondary);
++		ipu6_isys_dwc_phy_reset(isys, primary);
++
++		return 0;
++	}
++
++	dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes);
++
++	ipu6_isys_dwc_phy_reset(isys, phy_id);
++
++	return 0;
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c
+new file mode 100644
+index 000000000000..dcc7743e0cee
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c
+@@ -0,0 +1,242 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/device.h>
++#include <linux/io.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++
++/* only use BB0, BB2, BB4, and BB6 on PHY0 */
++#define IPU6SE_ISYS_PHY_BB_NUM		4
++#define IPU6SE_ISYS_PHY_0_BASE		0x10000
++
++#define PHY_CPHY_DLL_OVRD(x)		(0x100 + 0x100 * (x))
++#define PHY_CPHY_RX_CONTROL1(x)		(0x110 + 0x100 * (x))
++#define PHY_DPHY_CFG(x)			(0x148 + 0x100 * (x))
++#define PHY_BB_AFE_CONFIG(x)		(0x174 + 0x100 * (x))
++
++/*
++ * use port_cfg to configure that which data lanes used
++ * +---------+     +------+ +-----+
++ * | port0 x4<-----|      | |     |
++ * |         |     | port | |     |
++ * | port1 x2<-----|      | |     |
++ * |         |     |      <-| PHY |
++ * | port2 x4<-----|      | |     |
++ * |         |     |config| |     |
++ * | port3 x2<-----|      | |     |
++ * +---------+     +------+ +-----+
++ */
++static const unsigned int csi2_port_cfg[][3] = {
++	{0, 0, 0x1f}, /* no link */
++	{4, 0, 0x10}, /* x4 + x4 config */
++	{2, 0, 0x12}, /* x2 + x2 config */
++	{1, 0, 0x13}, /* x1 + x1 config */
++	{2, 1, 0x15}, /* x2x1 + x2x1 config */
++	{1, 1, 0x16}, /* x1x1 + x1x1 config */
++	{2, 2, 0x18}, /* x2x2 + x2x2 config */
++	{1, 2, 0x19} /* x1x2 + x1x2 config */
++};
++
++/* port, nlanes, bbindex, portcfg */
++static const unsigned int phy_port_cfg[][4] = {
++	/* sip0 */
++	{0, 1, 0, 0x15},
++	{0, 2, 0, 0x15},
++	{0, 4, 0, 0x15},
++	{0, 4, 2, 0x22},
++	/* sip1 */
++	{2, 1, 4, 0x15},
++	{2, 2, 4, 0x15},
++	{2, 4, 4, 0x15},
++	{2, 4, 6, 0x22}
++};
++
++static void ipu6_isys_csi2_phy_config_by_port(struct ipu6_isys *isys,
++					      unsigned int port,
++					      unsigned int nlanes)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *base = isys->adev->isp->base;
++	unsigned int bbnum;
++	u32 val, reg, i;
++
++	dev_dbg(dev, "port %u with %u lanes", port, nlanes);
++
++	/* only support <1.5Gbps */
++	for (i = 0; i < IPU6SE_ISYS_PHY_BB_NUM; i++) {
++		/* cphy_dll_ovrd.crcdc_fsm_dlane0 = 13 */
++		reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i);
++		val = readl(base + reg);
++		val |= FIELD_PREP(GENMASK(6, 1), 13);
++		writel(val, base + reg);
++
++		/* cphy_rx_control1.en_crc1 = 1 */
++		reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_RX_CONTROL1(i);
++		val = readl(base + reg);
++		val |= BIT(31);
++		writel(val, base + reg);
++
++		/* dphy_cfg.reserved = 1, .lden_from_dll_ovrd_0 = 1 */
++		reg = IPU6SE_ISYS_PHY_0_BASE + PHY_DPHY_CFG(i);
++		val = readl(base + reg);
++		val |= BIT(25) | BIT(26);
++		writel(val, base + reg);
++
++		/* cphy_dll_ovrd.lden_crcdc_fsm_dlane0 = 1 */
++		reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i);
++		val = readl(base + reg);
++		val |= BIT(0);
++		writel(val, base + reg);
++	}
++
++	/* Front end config, use minimal channel loss */
++	for (i = 0; i < ARRAY_SIZE(phy_port_cfg); i++) {
++		if (phy_port_cfg[i][0] == port &&
++		    phy_port_cfg[i][1] == nlanes) {
++			bbnum = phy_port_cfg[i][2] / 2;
++			reg = IPU6SE_ISYS_PHY_0_BASE + PHY_BB_AFE_CONFIG(bbnum);
++			val = readl(base + reg);
++			val |= phy_port_cfg[i][3];
++			writel(val, base + reg);
++		}
++	}
++}
++
++static void ipu6_isys_csi2_rx_control(struct ipu6_isys *isys)
++{
++	void __iomem *base = isys->adev->isp->base;
++	u32 val, reg;
++
++	reg = CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL;
++	val = readl(base + reg);
++	val |= BIT(0);
++	writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL);
++
++	reg = CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL;
++	val = readl(base + reg);
++	val |= BIT(0);
++	writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL);
++
++	reg = CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL;
++	val = readl(base + reg);
++	val |= BIT(0);
++	writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL);
++
++	reg = CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL;
++	val = readl(base + reg);
++	val |= BIT(0);
++	writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL);
++}
++
++static int ipu6_isys_csi2_set_port_cfg(struct ipu6_isys *isys,
++				       unsigned int port, unsigned int nlanes)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int sip = port / 2;
++	unsigned int index;
++
++	switch (nlanes) {
++	case 1:
++		index = 5;
++		break;
++	case 2:
++		index = 6;
++		break;
++	case 4:
++		index = 1;
++		break;
++	default:
++		dev_err(dev, "lanes nr %u is unsupported\n", nlanes);
++		return -EINVAL;
++	}
++
++	dev_dbg(dev, "port config for port %u with %u lanes\n",	port, nlanes);
++
++	writel(csi2_port_cfg[index][2],
++	       isys->pdata->base + CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip));
++
++	return 0;
++}
++
++static void
++ipu6_isys_csi2_set_timing(struct ipu6_isys *isys,
++			  const struct ipu6_isys_csi2_timing *timing,
++			  unsigned int port, unsigned int nlanes)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *reg;
++	u32 port_base;
++	u32 i;
++
++	port_base = (port % 2) ? CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) :
++		CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port);
++
++	dev_dbg(dev, "set timing for port %u with %u lanes\n", port, nlanes);
++
++	reg = isys->pdata->base + port_base;
++	reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE;
++
++	writel(timing->ctermen, reg);
++
++	reg = isys->pdata->base + port_base;
++	reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE;
++	writel(timing->csettle, reg);
++
++	for (i = 0; i < nlanes; i++) {
++		reg = isys->pdata->base + port_base;
++		reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(i);
++		writel(timing->dtermen, reg);
++
++		reg = isys->pdata->base + port_base;
++		reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(i);
++		writel(timing->dsettle, reg);
++	}
++}
++
++#define DPHY_TIMER_INCR	0x28
++int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	int ret = 0;
++	u32 nlanes;
++	u32 port;
++
++	if (!on)
++		return 0;
++
++	port = cfg->port;
++	nlanes = cfg->nlanes;
++
++	if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
++		dev_warn(dev, "invalid port ID %d\n", port);
++		return -EINVAL;
++	}
++
++	ipu6_isys_csi2_phy_config_by_port(isys, port, nlanes);
++
++	writel(DPHY_TIMER_INCR,
++	       isys->pdata->base + CSI2_HUB_GPREG_DPHY_TIMER_INCR);
++
++	/* set port cfg and rx timing */
++	ipu6_isys_csi2_set_timing(isys, timing, port, nlanes);
++
++	ret = ipu6_isys_csi2_set_port_cfg(isys, port, nlanes);
++	if (ret)
++		return ret;
++
++	ipu6_isys_csi2_rx_control(isys);
++
++	return 0;
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c
+new file mode 100644
+index 000000000000..9abf389a05f1
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c
+@@ -0,0 +1,720 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bits.h>
++#include <linux/container_of.h>
++#include <linux/device.h>
++#include <linux/iopoll.h>
++#include <linux/list.h>
++#include <linux/refcount.h>
++#include <linux/time64.h>
++
++#include <media/v4l2-async.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++
++#define CSI_REG_HUB_GPREG_PHY_CTL(id) (CSI_REG_BASE + 0x18008 + (id) * 0x8)
++#define CSI_REG_HUB_GPREG_PHY_CTL_RESET			BIT(4)
++#define CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN		BIT(0)
++#define CSI_REG_HUB_GPREG_PHY_STATUS(id) (CSI_REG_BASE + 0x1800c + (id) * 0x8)
++#define CSI_REG_HUB_GPREG_PHY_POWER_ACK			BIT(0)
++#define CSI_REG_HUB_GPREG_PHY_READY			BIT(4)
++
++#define MCD_PHY_POWER_STATUS_TIMEOUT			(200 * USEC_PER_MSEC)
++
++/*
++ * bridge to phy in buttress reg map, each phy has 16 kbytes
++ * only 2 phys for TGL U and Y
++ */
++#define IPU6_ISYS_MCD_PHY_BASE(i)			(0x10000 + (i) * 0x4000)
++
++/*
++ *  There are 2 MCD DPHY instances on TGL and 1 MCD DPHY instance on ADL.
++ *  Each MCD PHY has 12-lanes which has 8 data lanes and 4 clock lanes.
++ *  CSI port 1, 3 (5, 7) can support max 2 data lanes.
++ *  CSI port 0, 2 (4, 6) can support max 4 data lanes.
++ *  PHY configurations are PPI based instead of port.
++ *  Left:
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | PPI     | PPI5    | PPI4    | PPI3    | PPI2   | PPI1    | PPI0     |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x4      | unused  | D3      | D2      | C0     | D0      | D1       |
++ *  |---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x2x2    | C1      | D0      | D1      | C0     | D0      | D1       |
++ *  ----------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x2x1    | C1      | D0      | unused  | C0     | D0      | D1       |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x1x1    | C1      | D0      | unused  | C0     | D0      | unused   |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x1x2    | C1      | D0      | D1      | C0     | D0      | unused   |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *
++ *  Right:
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | PPI     | PPI6    | PPI7    | PPI8    | PPI9   | PPI10   | PPI11    |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x4      | D1      | D0      | C2      | D2     | D3      | unused   |
++ *  |---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x2x2    | D1      | D0      | C2      | D1     | D0      | C3       |
++ *  ----------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x2x1    | D1      | D0      | C2      | unused | D0      | C3       |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x1x1    | unused  | D0      | C2      | unused | D0      | C3       |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x1x2    | unused  | D0      | C2      | D1     | D0      | C3       |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *
++ * ppi mapping per phy :
++ *
++ * x4 + x4:
++ * Left : port0 - PPI range {0, 1, 2, 3, 4}
++ * Right: port2 - PPI range {6, 7, 8, 9, 10}
++ *
++ * x4 + x2x2:
++ * Left: port0 - PPI range {0, 1, 2, 3, 4}
++ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11}
++ *
++ * x2x2 + x4:
++ * Left: port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5}
++ * Right: port2 - PPI range {6, 7, 8, 9, 10}
++ *
++ * x2x2 + x2x2:
++ * Left : port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5}
++ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11}
++ */
++
++struct phy_reg {
++	u32 reg;
++	u32 val;
++};
++
++static const struct phy_reg common_init_regs[] = {
++	/* for TGL-U, use 0x80000000 */
++	{0x00000040, 0x80000000},
++	{0x00000044, 0x00a80880},
++	{0x00000044, 0x00b80880},
++	{0x00000010, 0x0000078c},
++	{0x00000344, 0x2f4401e2},
++	{0x00000544, 0x924401e2},
++	{0x00000744, 0x594401e2},
++	{0x00000944, 0x624401e2},
++	{0x00000b44, 0xfc4401e2},
++	{0x00000d44, 0xc54401e2},
++	{0x00000f44, 0x034401e2},
++	{0x00001144, 0x8f4401e2},
++	{0x00001344, 0x754401e2},
++	{0x00001544, 0xe94401e2},
++	{0x00001744, 0xcb4401e2},
++	{0x00001944, 0xfa4401e2}
++};
++
++static const struct phy_reg x1_port0_config_regs[] = {
++	{0x00000694, 0xc80060fa},
++	{0x00000680, 0x3d4f78ea},
++	{0x00000690, 0x10a0140b},
++	{0x000006a8, 0xdf04010a},
++	{0x00000700, 0x57050060},
++	{0x00000710, 0x0030001c},
++	{0x00000738, 0x5f004444},
++	{0x0000073c, 0x78464204},
++	{0x00000748, 0x7821f940},
++	{0x0000074c, 0xb2000433},
++	{0x00000494, 0xfe6030fa},
++	{0x00000480, 0x29ef5ed0},
++	{0x00000490, 0x10a0540b},
++	{0x000004a8, 0x7a01010a},
++	{0x00000500, 0xef053460},
++	{0x00000510, 0xe030101c},
++	{0x00000538, 0xdf808444},
++	{0x0000053c, 0xc8422204},
++	{0x00000540, 0x0180088c},
++	{0x00000574, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x1_port1_config_regs[] = {
++	{0x00000c94, 0xc80060fa},
++	{0x00000c80, 0xcf47abea},
++	{0x00000c90, 0x10a0840b},
++	{0x00000ca8, 0xdf04010a},
++	{0x00000d00, 0x57050060},
++	{0x00000d10, 0x0030001c},
++	{0x00000d38, 0x5f004444},
++	{0x00000d3c, 0x78464204},
++	{0x00000d48, 0x7821f940},
++	{0x00000d4c, 0xb2000433},
++	{0x00000a94, 0xc91030fa},
++	{0x00000a80, 0x5a166ed0},
++	{0x00000a90, 0x10a0540b},
++	{0x00000aa8, 0x5d060100},
++	{0x00000b00, 0xef053460},
++	{0x00000b10, 0xa030101c},
++	{0x00000b38, 0xdf808444},
++	{0x00000b3c, 0xc8422204},
++	{0x00000b40, 0x0180088c},
++	{0x00000b74, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x1_port2_config_regs[] = {
++	{0x00001294, 0x28f000fa},
++	{0x00001280, 0x08130cea},
++	{0x00001290, 0x10a0140b},
++	{0x000012a8, 0xd704010a},
++	{0x00001300, 0x8d050060},
++	{0x00001310, 0x0030001c},
++	{0x00001338, 0xdf008444},
++	{0x0000133c, 0x78422204},
++	{0x00001348, 0x7821f940},
++	{0x0000134c, 0x5a000433},
++	{0x00001094, 0x2d20b0fa},
++	{0x00001080, 0xade75dd0},
++	{0x00001090, 0x10a0540b},
++	{0x000010a8, 0xb101010a},
++	{0x00001100, 0x33053460},
++	{0x00001110, 0x0030101c},
++	{0x00001138, 0xdf808444},
++	{0x0000113c, 0xc8422204},
++	{0x00001140, 0x8180088c},
++	{0x00001174, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x1_port3_config_regs[] = {
++	{0x00001894, 0xc80060fa},
++	{0x00001880, 0x0f90fd6a},
++	{0x00001890, 0x10a0840b},
++	{0x000018a8, 0xdf04010a},
++	{0x00001900, 0x57050060},
++	{0x00001910, 0x0030001c},
++	{0x00001938, 0x5f004444},
++	{0x0000193c, 0x78464204},
++	{0x00001948, 0x7821f940},
++	{0x0000194c, 0xb2000433},
++	{0x00001694, 0x3050d0fa},
++	{0x00001680, 0x0ef6d050},
++	{0x00001690, 0x10a0540b},
++	{0x000016a8, 0xe301010a},
++	{0x00001700, 0x69053460},
++	{0x00001710, 0xa030101c},
++	{0x00001738, 0xdf808444},
++	{0x0000173c, 0xc8422204},
++	{0x00001740, 0x0180088c},
++	{0x00001774, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x2_port0_config_regs[] = {
++	{0x00000694, 0xc80060fa},
++	{0x00000680, 0x3d4f78ea},
++	{0x00000690, 0x10a0140b},
++	{0x000006a8, 0xdf04010a},
++	{0x00000700, 0x57050060},
++	{0x00000710, 0x0030001c},
++	{0x00000738, 0x5f004444},
++	{0x0000073c, 0x78464204},
++	{0x00000748, 0x7821f940},
++	{0x0000074c, 0xb2000433},
++	{0x00000494, 0xc80060fa},
++	{0x00000480, 0x29ef5ed8},
++	{0x00000490, 0x10a0540b},
++	{0x000004a8, 0x7a01010a},
++	{0x00000500, 0xef053460},
++	{0x00000510, 0xe030101c},
++	{0x00000538, 0xdf808444},
++	{0x0000053c, 0xc8422204},
++	{0x00000540, 0x0180088c},
++	{0x00000574, 0x00000000},
++	{0x00000294, 0xc80060fa},
++	{0x00000280, 0xcb45b950},
++	{0x00000290, 0x10a0540b},
++	{0x000002a8, 0x8c01010a},
++	{0x00000300, 0xef053460},
++	{0x00000310, 0x8030101c},
++	{0x00000338, 0x41808444},
++	{0x0000033c, 0x32422204},
++	{0x00000340, 0x0180088c},
++	{0x00000374, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x2_port1_config_regs[] = {
++	{0x00000c94, 0xc80060fa},
++	{0x00000c80, 0xcf47abea},
++	{0x00000c90, 0x10a0840b},
++	{0x00000ca8, 0xdf04010a},
++	{0x00000d00, 0x57050060},
++	{0x00000d10, 0x0030001c},
++	{0x00000d38, 0x5f004444},
++	{0x00000d3c, 0x78464204},
++	{0x00000d48, 0x7821f940},
++	{0x00000d4c, 0xb2000433},
++	{0x00000a94, 0xc80060fa},
++	{0x00000a80, 0x5a166ed8},
++	{0x00000a90, 0x10a0540b},
++	{0x00000aa8, 0x7a01010a},
++	{0x00000b00, 0xef053460},
++	{0x00000b10, 0xa030101c},
++	{0x00000b38, 0xdf808444},
++	{0x00000b3c, 0xc8422204},
++	{0x00000b40, 0x0180088c},
++	{0x00000b74, 0x00000000},
++	{0x00000894, 0xc80060fa},
++	{0x00000880, 0x4d4f21d0},
++	{0x00000890, 0x10a0540b},
++	{0x000008a8, 0x5601010a},
++	{0x00000900, 0xef053460},
++	{0x00000910, 0x8030101c},
++	{0x00000938, 0xdf808444},
++	{0x0000093c, 0xc8422204},
++	{0x00000940, 0x0180088c},
++	{0x00000974, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x2_port2_config_regs[] = {
++	{0x00001294, 0xc80060fa},
++	{0x00001280, 0x08130cea},
++	{0x00001290, 0x10a0140b},
++	{0x000012a8, 0xd704010a},
++	{0x00001300, 0x8d050060},
++	{0x00001310, 0x0030001c},
++	{0x00001338, 0xdf008444},
++	{0x0000133c, 0x78422204},
++	{0x00001348, 0x7821f940},
++	{0x0000134c, 0x5a000433},
++	{0x00001094, 0xc80060fa},
++	{0x00001080, 0xade75dd8},
++	{0x00001090, 0x10a0540b},
++	{0x000010a8, 0xb101010a},
++	{0x00001100, 0x33053460},
++	{0x00001110, 0x0030101c},
++	{0x00001138, 0xdf808444},
++	{0x0000113c, 0xc8422204},
++	{0x00001140, 0x8180088c},
++	{0x00001174, 0x00000000},
++	{0x00000e94, 0xc80060fa},
++	{0x00000e80, 0x0fbf16d0},
++	{0x00000e90, 0x10a0540b},
++	{0x00000ea8, 0x7a01010a},
++	{0x00000f00, 0xf5053460},
++	{0x00000f10, 0xc030101c},
++	{0x00000f38, 0xdf808444},
++	{0x00000f3c, 0xc8422204},
++	{0x00000f40, 0x8180088c},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x2_port3_config_regs[] = {
++	{0x00001894, 0xc80060fa},
++	{0x00001880, 0x0f90fd6a},
++	{0x00001890, 0x10a0840b},
++	{0x000018a8, 0xdf04010a},
++	{0x00001900, 0x57050060},
++	{0x00001910, 0x0030001c},
++	{0x00001938, 0x5f004444},
++	{0x0000193c, 0x78464204},
++	{0x00001948, 0x7821f940},
++	{0x0000194c, 0xb2000433},
++	{0x00001694, 0xc80060fa},
++	{0x00001680, 0x0ef6d058},
++	{0x00001690, 0x10a0540b},
++	{0x000016a8, 0x7a01010a},
++	{0x00001700, 0x69053460},
++	{0x00001710, 0xa030101c},
++	{0x00001738, 0xdf808444},
++	{0x0000173c, 0xc8422204},
++	{0x00001740, 0x0180088c},
++	{0x00001774, 0x00000000},
++	{0x00001494, 0xc80060fa},
++	{0x00001480, 0xf9d34bd0},
++	{0x00001490, 0x10a0540b},
++	{0x000014a8, 0x7a01010a},
++	{0x00001500, 0x1b053460},
++	{0x00001510, 0x0030101c},
++	{0x00001538, 0xdf808444},
++	{0x0000153c, 0xc8422204},
++	{0x00001540, 0x8180088c},
++	{0x00001574, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x4_port0_config_regs[] = {
++	{0x00000694, 0xc80060fa},
++	{0x00000680, 0x3d4f78fa},
++	{0x00000690, 0x10a0140b},
++	{0x000006a8, 0xdf04010a},
++	{0x00000700, 0x57050060},
++	{0x00000710, 0x0030001c},
++	{0x00000738, 0x5f004444},
++	{0x0000073c, 0x78464204},
++	{0x00000748, 0x7821f940},
++	{0x0000074c, 0xb2000433},
++	{0x00000494, 0xfe6030fa},
++	{0x00000480, 0x29ef5ed8},
++	{0x00000490, 0x10a0540b},
++	{0x000004a8, 0x7a01010a},
++	{0x00000500, 0xef053460},
++	{0x00000510, 0xe030101c},
++	{0x00000538, 0xdf808444},
++	{0x0000053c, 0xc8422204},
++	{0x00000540, 0x0180088c},
++	{0x00000574, 0x00000004},
++	{0x00000294, 0x23e030fa},
++	{0x00000280, 0xcb45b950},
++	{0x00000290, 0x10a0540b},
++	{0x000002a8, 0x8c01010a},
++	{0x00000300, 0xef053460},
++	{0x00000310, 0x8030101c},
++	{0x00000338, 0x41808444},
++	{0x0000033c, 0x32422204},
++	{0x00000340, 0x0180088c},
++	{0x00000374, 0x00000004},
++	{0x00000894, 0x5620b0fa},
++	{0x00000880, 0x4d4f21dc},
++	{0x00000890, 0x10a0540b},
++	{0x000008a8, 0x5601010a},
++	{0x00000900, 0xef053460},
++	{0x00000910, 0x8030101c},
++	{0x00000938, 0xdf808444},
++	{0x0000093c, 0xc8422204},
++	{0x00000940, 0x0180088c},
++	{0x00000974, 0x00000004},
++	{0x00000a94, 0xc91030fa},
++	{0x00000a80, 0x5a166ecc},
++	{0x00000a90, 0x10a0540b},
++	{0x00000aa8, 0x5d01010a},
++	{0x00000b00, 0xef053460},
++	{0x00000b10, 0xa030101c},
++	{0x00000b38, 0xdf808444},
++	{0x00000b3c, 0xc8422204},
++	{0x00000b40, 0x0180088c},
++	{0x00000b74, 0x00000004},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x4_port1_config_regs[] = {
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x4_port2_config_regs[] = {
++	{0x00001294, 0x28f000fa},
++	{0x00001280, 0x08130cfa},
++	{0x00001290, 0x10c0140b},
++	{0x000012a8, 0xd704010a},
++	{0x00001300, 0x8d050060},
++	{0x00001310, 0x0030001c},
++	{0x00001338, 0xdf008444},
++	{0x0000133c, 0x78422204},
++	{0x00001348, 0x7821f940},
++	{0x0000134c, 0x5a000433},
++	{0x00001094, 0x2d20b0fa},
++	{0x00001080, 0xade75dd8},
++	{0x00001090, 0x10a0540b},
++	{0x000010a8, 0xb101010a},
++	{0x00001100, 0x33053460},
++	{0x00001110, 0x0030101c},
++	{0x00001138, 0xdf808444},
++	{0x0000113c, 0xc8422204},
++	{0x00001140, 0x8180088c},
++	{0x00001174, 0x00000004},
++	{0x00000e94, 0xd308d0fa},
++	{0x00000e80, 0x0fbf16d0},
++	{0x00000e90, 0x10a0540b},
++	{0x00000ea8, 0x2c01010a},
++	{0x00000f00, 0xf5053460},
++	{0x00000f10, 0xc030101c},
++	{0x00000f38, 0xdf808444},
++	{0x00000f3c, 0xc8422204},
++	{0x00000f40, 0x8180088c},
++	{0x00000f74, 0x00000004},
++	{0x00001494, 0x136850fa},
++	{0x00001480, 0xf9d34bdc},
++	{0x00001490, 0x10a0540b},
++	{0x000014a8, 0x5a01010a},
++	{0x00001500, 0x1b053460},
++	{0x00001510, 0x0030101c},
++	{0x00001538, 0xdf808444},
++	{0x0000153c, 0xc8422204},
++	{0x00001540, 0x8180088c},
++	{0x00001574, 0x00000004},
++	{0x00001694, 0x3050d0fa},
++	{0x00001680, 0x0ef6d04c},
++	{0x00001690, 0x10a0540b},
++	{0x000016a8, 0xe301010a},
++	{0x00001700, 0x69053460},
++	{0x00001710, 0xa030101c},
++	{0x00001738, 0xdf808444},
++	{0x0000173c, 0xc8422204},
++	{0x00001740, 0x0180088c},
++	{0x00001774, 0x00000004},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x4_port3_config_regs[] = {
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg *x1_config_regs[4] = {
++	x1_port0_config_regs,
++	x1_port1_config_regs,
++	x1_port2_config_regs,
++	x1_port3_config_regs
++};
++
++static const struct phy_reg *x2_config_regs[4] = {
++	x2_port0_config_regs,
++	x2_port1_config_regs,
++	x2_port2_config_regs,
++	x2_port3_config_regs
++};
++
++static const struct phy_reg *x4_config_regs[4] = {
++	x4_port0_config_regs,
++	x4_port1_config_regs,
++	x4_port2_config_regs,
++	x4_port3_config_regs
++};
++
++static const struct phy_reg **config_regs[3] = {
++	x1_config_regs,
++	x2_config_regs,
++	x4_config_regs
++};
++
++static int ipu6_isys_mcd_phy_powerup_ack(struct ipu6_isys *isys, u8 id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u32 val;
++	int ret;
++
++	val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++	val |= CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN;
++	writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++
++	ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
++				 val, val & CSI_REG_HUB_GPREG_PHY_POWER_ACK,
++				 200, MCD_PHY_POWER_STATUS_TIMEOUT);
++	if (ret)
++		dev_err(dev, "PHY%d powerup ack timeout", id);
++
++	return ret;
++}
++
++static int ipu6_isys_mcd_phy_powerdown_ack(struct ipu6_isys *isys, u8 id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u32 val;
++	int ret;
++
++	writel(0, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++	ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
++				 val, !(val & CSI_REG_HUB_GPREG_PHY_POWER_ACK),
++				 200, MCD_PHY_POWER_STATUS_TIMEOUT);
++	if (ret)
++		dev_err(dev, "PHY%d powerdown ack timeout", id);
++
++	return ret;
++}
++
++static void ipu6_isys_mcd_phy_reset(struct ipu6_isys *isys, u8 id, bool assert)
++{
++	void __iomem *isys_base = isys->pdata->base;
++	u32 val;
++
++	val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++	if (assert)
++		val |= CSI_REG_HUB_GPREG_PHY_CTL_RESET;
++	else
++		val &= ~(CSI_REG_HUB_GPREG_PHY_CTL_RESET);
++
++	writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++}
++
++static int ipu6_isys_mcd_phy_ready(struct ipu6_isys *isys, u8 id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u32 val;
++	int ret;
++
++	ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
++				 val, val & CSI_REG_HUB_GPREG_PHY_READY,
++				 200, MCD_PHY_POWER_STATUS_TIMEOUT);
++	if (ret)
++		dev_err(dev, "PHY%d ready ack timeout", id);
++
++	return ret;
++}
++
++static void ipu6_isys_mcd_phy_common_init(struct ipu6_isys *isys)
++{
++	struct ipu6_bus_device *adev = isys->adev;
++	struct ipu6_device *isp = adev->isp;
++	void __iomem *isp_base = isp->base;
++	struct sensor_async_sd *s_asd;
++	struct v4l2_async_connection *asc;
++	void __iomem *phy_base;
++	unsigned int i;
++	u8 phy_id;
++
++	list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) {
++		s_asd = container_of(asc, struct sensor_async_sd, asc);
++		phy_id = s_asd->csi2.port / 4;
++		phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id);
++
++		for (i = 0; i < ARRAY_SIZE(common_init_regs); i++)
++			writel(common_init_regs[i].val,
++			       phy_base + common_init_regs[i].reg);
++	}
++}
++
++static int ipu6_isys_driver_port_to_phy_port(struct ipu6_isys_csi2_config *cfg)
++{
++	int phy_port;
++	int ret;
++
++	if (!(cfg->nlanes == 4 || cfg->nlanes == 2 || cfg->nlanes == 1))
++		return -EINVAL;
++
++	/* B,F -> C0 A,E -> C1 C,G -> C2 D,H -> C4 */
++	/* normalize driver port number */
++	phy_port = cfg->port % 4;
++
++	/* swap port number only for A and B */
++	if (phy_port == 0)
++		phy_port = 1;
++	else if (phy_port == 1)
++		phy_port = 0;
++
++	ret = phy_port;
++
++	/* check validity per lane configuration */
++	if (cfg->nlanes == 4 && !(phy_port == 0 || phy_port == 2))
++		ret = -EINVAL;
++	else if ((cfg->nlanes == 2 || cfg->nlanes == 1) &&
++		 !(phy_port >= 0 && phy_port <= 3))
++		ret = -EINVAL;
++
++	return ret;
++}
++
++static int ipu6_isys_mcd_phy_config(struct ipu6_isys *isys)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_bus_device *adev = isys->adev;
++	const struct phy_reg **phy_config_regs;
++	struct ipu6_device *isp = adev->isp;
++	void __iomem *isp_base = isp->base;
++	struct sensor_async_sd *s_asd;
++	struct ipu6_isys_csi2_config cfg;
++	struct v4l2_async_connection *asc;
++	u8 phy_port, phy_id;
++	unsigned int i;
++	void __iomem *phy_base;
++
++	list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) {
++		s_asd = container_of(asc, struct sensor_async_sd, asc);
++		cfg.port = s_asd->csi2.port;
++		cfg.nlanes = s_asd->csi2.nlanes;
++		phy_port = ipu6_isys_driver_port_to_phy_port(&cfg);
++		if (phy_port < 0) {
++			dev_err(dev, "invalid port %d for lane %d", cfg.port,
++				cfg.nlanes);
++			return -ENXIO;
++		}
++
++		phy_id = cfg.port / 4;
++		phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id);
++		dev_dbg(dev, "port%d PHY%u lanes %u\n", cfg.port, phy_id,
++			cfg.nlanes);
++
++		phy_config_regs = config_regs[cfg.nlanes / 2];
++		cfg.port = phy_port;
++		for (i = 0; phy_config_regs[cfg.port][i].reg; i++)
++			writel(phy_config_regs[cfg.port][i].val,
++			       phy_base + phy_config_regs[cfg.port][i].reg);
++	}
++
++	return 0;
++}
++
++#define CSI_MCD_PHY_NUM		2
++static refcount_t phy_power_ref_count[CSI_MCD_PHY_NUM];
++
++int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u8 port, phy_id;
++	refcount_t *ref;
++	int ret;
++
++	port = cfg->port;
++	phy_id = port / 4;
++
++	ref = &phy_power_ref_count[phy_id];
++
++	dev_dbg(dev, "for phy %d port %d, lanes: %d\n", phy_id, port,
++		cfg->nlanes);
++
++	if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
++		dev_warn(dev, "invalid port ID %d\n", port);
++		return -EINVAL;
++	}
++
++	if (on) {
++		if (refcount_read(ref)) {
++			dev_dbg(dev, "for phy %d is already UP", phy_id);
++			refcount_inc(ref);
++			return 0;
++		}
++
++		ret = ipu6_isys_mcd_phy_powerup_ack(isys, phy_id);
++		if (ret)
++			return ret;
++
++		ipu6_isys_mcd_phy_reset(isys, phy_id, 0);
++		ipu6_isys_mcd_phy_common_init(isys);
++
++		ret = ipu6_isys_mcd_phy_config(isys);
++		if (ret)
++			return ret;
++
++		ipu6_isys_mcd_phy_reset(isys, phy_id, 1);
++		ret = ipu6_isys_mcd_phy_ready(isys, phy_id);
++		if (ret)
++			return ret;
++
++		refcount_set(ref, 1);
++		return 0;
++	}
++
++	if (!refcount_dec_and_test(ref))
++		return 0;
++
++	return ipu6_isys_mcd_phy_powerdown_ack(isys, phy_id);
++}
+-- 
+2.43.2
+
+
+From 5d8544bd1d6dc7fce10a3bf01d10d926b8990b54 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:24 +0800
+Subject: [PATCH 17/33] media: intel/ipu6: add input system driver
+
+Input system driver do basic isys hardware setup and irq handling
+and work with fwnode and v4l2 to register the ISYS v4l2 devices.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-isys.c | 1353 ++++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-isys.h |  207 ++++
+ 2 files changed, 1560 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c
+new file mode 100644
+index 000000000000..e8983363a0da
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c
+@@ -0,0 +1,1353 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/auxiliary_bus.h>
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/completion.h>
++#include <linux/container_of.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/firmware.h>
++#include <linux/io.h>
++#include <linux/irqreturn.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/pci.h>
++#include <linux/pm_runtime.h>
++#include <linux/pm_qos.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/string.h>
++
++#include <media/ipu-bridge.h>
++#include <media/media-device.h>
++#include <media/media-entity.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-fwnode.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-cpd.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-mmu.h"
++#include "ipu6-platform-buttress-regs.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++#include "ipu6-platform-regs.h"
++
++#define IPU6_BUTTRESS_FABIC_CONTROL		0x68
++#define GDA_ENABLE_IWAKE_INDEX			2
++#define GDA_IWAKE_THRESHOLD_INDEX		1
++#define GDA_IRQ_CRITICAL_THRESHOLD_INDEX	0
++#define GDA_MEMOPEN_THRESHOLD_INDEX		3
++#define DEFAULT_DID_RATIO			90
++#define DEFAULT_IWAKE_THRESHOLD			0x42
++#define DEFAULT_MEM_OPEN_TIME			10
++#define ONE_THOUSAND_MICROSECOND		1000
++/* One page is 2KB, 8 x 16 x 16 = 2048B = 2KB */
++#define ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE	0x800
++
++/* LTR & DID value are 10 bit at most */
++#define LTR_DID_VAL_MAX				1023
++#define LTR_DEFAULT_VALUE			0x70503c19
++#define FILL_TIME_DEFAULT_VALUE			0xfff0783c
++#define LTR_DID_PKGC_2R				20
++#define LTR_SCALE_DEFAULT			5
++#define LTR_SCALE_1024NS			2
++#define DID_SCALE_1US				2
++#define DID_SCALE_32US				3
++#define REG_PKGC_PMON_CFG			0xb00
++
++#define VAL_PKGC_PMON_CFG_RESET			0x38
++#define VAL_PKGC_PMON_CFG_START			0x7
++
++#define IS_PIXEL_BUFFER_PAGES			0x80
++/*
++ * when iwake mode is disabled, the critical threshold is statically set
++ * to 75% of the IS pixel buffer, criticalThreshold = (128 * 3) / 4
++ */
++#define CRITICAL_THRESHOLD_IWAKE_DISABLE	(IS_PIXEL_BUFFER_PAGES * 3 / 4)
++
++union fabric_ctrl {
++	struct {
++		u16 ltr_val   : 10;
++		u16 ltr_scale : 3;
++		u16 reserved  : 3;
++		u16 did_val   : 10;
++		u16 did_scale : 3;
++		u16 reserved2 : 1;
++		u16 keep_power_in_D0   : 1;
++		u16 keep_power_override : 1;
++	} bits;
++	u32 value;
++};
++
++enum ltr_did_type {
++	LTR_IWAKE_ON,
++	LTR_IWAKE_OFF,
++	LTR_ISYS_ON,
++	LTR_ISYS_OFF,
++	LTR_ENHANNCE_IWAKE,
++	LTR_TYPE_MAX
++};
++
++#define ISYS_PM_QOS_VALUE	300
++
++static int isys_isr_one(struct ipu6_bus_device *adev);
++
++static int
++isys_complete_ext_device_registration(struct ipu6_isys *isys,
++				      struct v4l2_subdev *sd,
++				      struct ipu6_isys_csi2_config *csi2)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int i;
++	int ret;
++
++	for (i = 0; i < sd->entity.num_pads; i++) {
++		if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
++			break;
++	}
++
++	if (i == sd->entity.num_pads) {
++		dev_warn(dev, "no src pad in external entity\n");
++		ret = -ENOENT;
++		goto unregister_subdev;
++	}
++
++	ret = media_create_pad_link(&sd->entity, i,
++				    &isys->csi2[csi2->port].asd.sd.entity,
++				    0, 0);
++	if (ret) {
++		dev_warn(dev, "can't create link\n");
++		goto unregister_subdev;
++	}
++
++	isys->csi2[csi2->port].nlanes = csi2->nlanes;
++
++	return 0;
++
++unregister_subdev:
++	v4l2_device_unregister_subdev(sd);
++
++	return ret;
++}
++
++static void isys_stream_init(struct ipu6_isys *isys)
++{
++	u32 i;
++
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++		mutex_init(&isys->streams[i].mutex);
++		init_completion(&isys->streams[i].stream_open_completion);
++		init_completion(&isys->streams[i].stream_close_completion);
++		init_completion(&isys->streams[i].stream_start_completion);
++		init_completion(&isys->streams[i].stream_stop_completion);
++		INIT_LIST_HEAD(&isys->streams[i].queues);
++		isys->streams[i].isys = isys;
++		isys->streams[i].stream_handle = i;
++		isys->streams[i].vc = INVALID_VC_ID;
++	}
++}
++
++static void isys_csi2_unregister_subdevices(struct ipu6_isys *isys)
++{
++	const struct ipu6_isys_internal_csi2_pdata *csi2 =
++		&isys->pdata->ipdata->csi2;
++	unsigned int i;
++
++	for (i = 0; i < csi2->nports; i++)
++		ipu6_isys_csi2_cleanup(&isys->csi2[i]);
++}
++
++static int isys_csi2_register_subdevices(struct ipu6_isys *isys)
++{
++	const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
++		&isys->pdata->ipdata->csi2;
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int i;
++	int ret;
++
++	isys->csi2 = devm_kcalloc(dev, csi2_pdata->nports,
++				  sizeof(*isys->csi2), GFP_KERNEL);
++	if (!isys->csi2)
++		return -ENOMEM;
++
++	for (i = 0; i < csi2_pdata->nports; i++) {
++		ret = ipu6_isys_csi2_init(&isys->csi2[i], isys,
++					  isys->pdata->base +
++					  csi2_pdata->offsets[i], i);
++		if (ret)
++			goto fail;
++
++		isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
++	}
++
++	return 0;
++
++fail:
++	while (i--)
++		ipu6_isys_csi2_cleanup(&isys->csi2[i]);
++
++	return ret;
++}
++
++static int isys_csi2_create_media_links(struct ipu6_isys *isys)
++{
++	const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
++		&isys->pdata->ipdata->csi2;
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int i, j, k;
++	int ret;
++
++	for (i = 0; i < csi2_pdata->nports; i++) {
++		struct media_entity *sd = &isys->csi2[i].asd.sd.entity;
++
++		for (j = 0; j < NR_OF_VIDEO_DEVICE; j++) {
++			struct media_entity *v = &isys->av[j].vdev.entity;
++			u32 flag = MEDIA_LNK_FL_DYNAMIC;
++
++			for (k = CSI2_PAD_SRC; k < NR_OF_CSI2_PADS; k++) {
++				ret = media_create_pad_link(sd, k, v, 0, flag);
++				if (ret) {
++					dev_err(dev, "CSI2 can't create link\n");
++					return ret;
++				}
++			}
++		}
++	}
++
++	return 0;
++}
++
++static void isys_unregister_video_devices(struct ipu6_isys *isys)
++{
++	unsigned int i;
++
++	for (i = 0; i < NR_OF_VIDEO_DEVICE; i++)
++		ipu6_isys_video_cleanup(&isys->av[i]);
++}
++
++static int isys_register_video_devices(struct ipu6_isys *isys)
++{
++	unsigned int i;
++	int ret;
++
++	for (i = 0; i < NR_OF_VIDEO_DEVICE; i++) {
++		snprintf(isys->av[i].vdev.name, sizeof(isys->av[i].vdev.name),
++			 IPU6_ISYS_ENTITY_PREFIX " ISYS Capture %u", i);
++		isys->av[i].isys = isys;
++		isys->av[i].aq.vbq.buf_struct_size =
++			sizeof(struct ipu6_isys_video_buffer);
++		isys->av[i].pfmt = &ipu6_isys_pfmts[0];
++
++		ret = ipu6_isys_video_init(&isys->av[i]);
++		if (ret)
++			goto fail;
++	}
++
++	return 0;
++
++fail:
++	while (i--)
++		ipu6_isys_video_cleanup(&isys->av[i]);
++
++	return ret;
++}
++
++void isys_setup_hw(struct ipu6_isys *isys)
++{
++	void __iomem *base = isys->pdata->base;
++	const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold;
++	u32 irqs = 0;
++	unsigned int i, nports;
++
++	nports = isys->pdata->ipdata->csi2.nports;
++
++	/* Enable irqs for all MIPI ports */
++	for (i = 0; i < nports; i++)
++		irqs |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
++
++	writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_edge);
++	writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_lnp);
++	writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_mask);
++	writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_enable);
++	writel(GENMASK(19, 0),
++	       base + isys->pdata->ipdata->csi2.ctrl0_irq_clear);
++
++	irqs = ISYS_UNISPART_IRQS;
++	writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_EDGE);
++	writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE);
++	writel(GENMASK(28, 0), base + IPU6_REG_ISYS_UNISPART_IRQ_CLEAR);
++	writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
++	writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_ENABLE);
++
++	writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG);
++	writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG);
++
++	/* Write CDC FIFO threshold values for isys */
++	for (i = 0; i < isys->pdata->ipdata->hw_variant.cdc_fifos; i++)
++		writel(thd[i], base + IPU6_REG_ISYS_CDC_THRESHOLD(i));
++}
++
++static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2)
++{
++	struct ipu6_isys_stream *stream;
++	unsigned int i;
++	u32 status;
++	int source;
++
++	ipu6_isys_register_errors(csi2);
++
++	status = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++		       CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
++
++	writel(status, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++
++	source = csi2->asd.source;
++	for (i = 0; i < NR_OF_CSI2_VC; i++) {
++		if (status & IPU_CSI_RX_IRQ_FS_VC(i)) {
++			stream = ipu6_isys_query_stream_by_source(csi2->isys,
++								  source, i);
++			if (stream) {
++				ipu6_isys_csi2_sof_event_by_stream(stream);
++				ipu6_isys_put_stream(stream);
++			}
++		}
++
++		if (status & IPU_CSI_RX_IRQ_FE_VC(i)) {
++			stream = ipu6_isys_query_stream_by_source(csi2->isys,
++								  source, i);
++			if (stream) {
++				ipu6_isys_csi2_eof_event_by_stream(stream);
++				ipu6_isys_put_stream(stream);
++			}
++		}
++	}
++}
++
++irqreturn_t isys_isr(struct ipu6_bus_device *adev)
++{
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	void __iomem *base = isys->pdata->base;
++	u32 status_sw, status_csi;
++	u32 ctrl0_status, ctrl0_clear;
++
++	spin_lock(&isys->power_lock);
++	if (!isys->power) {
++		spin_unlock(&isys->power_lock);
++		return IRQ_NONE;
++	}
++
++	ctrl0_status = isys->pdata->ipdata->csi2.ctrl0_irq_status;
++	ctrl0_clear = isys->pdata->ipdata->csi2.ctrl0_irq_clear;
++
++	status_csi = readl(isys->pdata->base + ctrl0_status);
++	status_sw = readl(isys->pdata->base +
++			  IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
++
++	writel(ISYS_UNISPART_IRQS & ~IPU6_ISYS_UNISPART_IRQ_SW,
++	       base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
++
++	do {
++		writel(status_csi, isys->pdata->base + ctrl0_clear);
++
++		writel(status_sw, isys->pdata->base +
++		       IPU6_REG_ISYS_UNISPART_IRQ_CLEAR);
++
++		if (isys->isr_csi2_bits & status_csi) {
++			unsigned int i;
++
++			for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) {
++				/* irq from not enabled port */
++				if (!isys->csi2[i].base)
++					continue;
++				if (status_csi & IPU6_ISYS_UNISPART_IRQ_CSI2(i))
++					ipu6_isys_csi2_isr(&isys->csi2[i]);
++			}
++		}
++
++		writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG);
++
++		if (!isys_isr_one(adev))
++			status_sw = IPU6_ISYS_UNISPART_IRQ_SW;
++		else
++			status_sw = 0;
++
++		status_csi = readl(isys->pdata->base + ctrl0_status);
++		status_sw |= readl(isys->pdata->base +
++				   IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
++	} while ((status_csi & isys->isr_csi2_bits) ||
++		 (status_sw & IPU6_ISYS_UNISPART_IRQ_SW));
++
++	writel(ISYS_UNISPART_IRQS, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
++
++	spin_unlock(&isys->power_lock);
++
++	return IRQ_HANDLED;
++}
++
++static void get_lut_ltrdid(struct ipu6_isys *isys, struct ltr_did *pltr_did)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++	struct ltr_did ltrdid_default;
++
++	ltrdid_default.lut_ltr.value = LTR_DEFAULT_VALUE;
++	ltrdid_default.lut_fill_time.value = FILL_TIME_DEFAULT_VALUE;
++
++	if (iwake_watermark->ltrdid.lut_ltr.value)
++		*pltr_did = iwake_watermark->ltrdid;
++	else
++		*pltr_did = ltrdid_default;
++}
++
++static int set_iwake_register(struct ipu6_isys *isys, u32 index, u32 value)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	u32 req_id = index;
++	u32 offset = 0;
++	int ret;
++
++	ret = ipu6_fw_isys_send_proxy_token(isys, req_id, index, offset, value);
++	if (ret)
++		dev_err(dev, "write %d failed %d", index, ret);
++
++	return ret;
++}
++
++/*
++ * When input system is powered up and before enabling any new sensor capture,
++ * or after disabling any sensor capture the following values need to be set:
++ * LTR_value = LTR(usec) from calculation;
++ * LTR_scale = 2;
++ * DID_value = DID(usec) from calculation;
++ * DID_scale = 2;
++ *
++ * When input system is powered down, the LTR and DID values
++ * must be returned to the default values:
++ * LTR_value = 1023;
++ * LTR_scale = 5;
++ * DID_value = 1023;
++ * DID_scale = 2;
++ */
++static void set_iwake_ltrdid(struct ipu6_isys *isys, u16 ltr, u16 did,
++			     enum ltr_did_type use)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	u16 ltr_val, ltr_scale = LTR_SCALE_1024NS;
++	u16 did_val, did_scale = DID_SCALE_1US;
++	struct ipu6_device *isp = isys->adev->isp;
++	union fabric_ctrl fc;
++
++	switch (use) {
++	case LTR_IWAKE_ON:
++		ltr_val = min_t(u16, ltr, (u16)LTR_DID_VAL_MAX);
++		did_val = min_t(u16, did, (u16)LTR_DID_VAL_MAX);
++		ltr_scale = (ltr == LTR_DID_VAL_MAX &&
++			     did == LTR_DID_VAL_MAX) ?
++			LTR_SCALE_DEFAULT : LTR_SCALE_1024NS;
++		break;
++	case LTR_ISYS_ON:
++	case LTR_IWAKE_OFF:
++		ltr_val = LTR_DID_PKGC_2R;
++		did_val = LTR_DID_PKGC_2R;
++		break;
++	case LTR_ISYS_OFF:
++		ltr_val   = LTR_DID_VAL_MAX;
++		did_val   = LTR_DID_VAL_MAX;
++		ltr_scale = LTR_SCALE_DEFAULT;
++		break;
++	case LTR_ENHANNCE_IWAKE:
++		if (ltr == LTR_DID_VAL_MAX && did == LTR_DID_VAL_MAX) {
++			ltr_val = LTR_DID_VAL_MAX;
++			did_val = LTR_DID_VAL_MAX;
++			ltr_scale = LTR_SCALE_DEFAULT;
++		} else if (did < ONE_THOUSAND_MICROSECOND) {
++			ltr_val = ltr;
++			did_val = did;
++		} else {
++			ltr_val = ltr;
++			/* div 90% value by 32 to account for scale change */
++			did_val = did / 32;
++			did_scale = DID_SCALE_32US;
++		}
++		break;
++	default:
++		ltr_val   = LTR_DID_VAL_MAX;
++		did_val   = LTR_DID_VAL_MAX;
++		ltr_scale = LTR_SCALE_DEFAULT;
++		break;
++	}
++
++	fc.value = readl(isp->base + IPU6_BUTTRESS_FABIC_CONTROL);
++	fc.bits.ltr_val = ltr_val;
++	fc.bits.ltr_scale = ltr_scale;
++	fc.bits.did_val = did_val;
++	fc.bits.did_scale = did_scale;
++
++	dev_dbg(dev, "ltr: value %u scale %u, did: value %u scale %u\n",
++		ltr_val, ltr_scale, did_val, did_scale);
++	writel(fc.value, isp->base + IPU6_BUTTRESS_FABIC_CONTROL);
++}
++
++/*
++ * Driver may clear register GDA_ENABLE_IWAKE before FW configures the
++ * stream for debug purpose. Otherwise driver should not access this register.
++ */
++static void enable_iwake(struct ipu6_isys *isys, bool enable)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++	int ret;
++
++	mutex_lock(&iwake_watermark->mutex);
++
++	if (iwake_watermark->iwake_enabled == enable) {
++		mutex_unlock(&iwake_watermark->mutex);
++		return;
++	}
++
++	ret = set_iwake_register(isys, GDA_ENABLE_IWAKE_INDEX, enable);
++	if (!ret)
++		iwake_watermark->iwake_enabled = enable;
++
++	mutex_unlock(&iwake_watermark->mutex);
++}
++
++void update_watermark_setting(struct ipu6_isys *isys)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++	u32 iwake_threshold, iwake_critical_threshold, page_num;
++	struct device *dev = &isys->adev->auxdev.dev;
++	u32 calc_fill_time_us = 0, ltr = 0, did = 0;
++	struct video_stream_watermark *p_watermark;
++	enum ltr_did_type ltr_did_type;
++	struct list_head *stream_node;
++	u64 isys_pb_datarate_mbs = 0;
++	u32 mem_open_threshold = 0;
++	struct ltr_did ltrdid;
++	u64 threshold_bytes;
++	u32 max_sram_size;
++	u32 shift;
++
++	shift = isys->pdata->ipdata->sram_gran_shift;
++	max_sram_size = isys->pdata->ipdata->max_sram_size;
++
++	mutex_lock(&iwake_watermark->mutex);
++	if (iwake_watermark->force_iwake_disable) {
++		set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF);
++		set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
++				   CRITICAL_THRESHOLD_IWAKE_DISABLE);
++		goto unlock_exit;
++	}
++
++	if (list_empty(&iwake_watermark->video_list)) {
++		isys_pb_datarate_mbs = 0;
++	} else {
++		list_for_each(stream_node, &iwake_watermark->video_list) {
++			p_watermark = list_entry(stream_node,
++						 struct video_stream_watermark,
++						 stream_node);
++			isys_pb_datarate_mbs += p_watermark->stream_data_rate;
++		}
++	}
++	mutex_unlock(&iwake_watermark->mutex);
++
++	if (!isys_pb_datarate_mbs) {
++		enable_iwake(isys, false);
++		set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF);
++		mutex_lock(&iwake_watermark->mutex);
++		set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
++				   CRITICAL_THRESHOLD_IWAKE_DISABLE);
++		goto unlock_exit;
++	}
++
++	enable_iwake(isys, true);
++	calc_fill_time_us = max_sram_size / isys_pb_datarate_mbs;
++
++	if (isys->pdata->ipdata->enhanced_iwake) {
++		ltr = isys->pdata->ipdata->ltr;
++		did = calc_fill_time_us * DEFAULT_DID_RATIO / 100;
++		ltr_did_type = LTR_ENHANNCE_IWAKE;
++	} else {
++		get_lut_ltrdid(isys, &ltrdid);
++
++		if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th0)
++			ltr = 0;
++		else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th1)
++			ltr = ltrdid.lut_ltr.bits.val0;
++		else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th2)
++			ltr = ltrdid.lut_ltr.bits.val1;
++		else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th3)
++			ltr = ltrdid.lut_ltr.bits.val2;
++		else
++			ltr = ltrdid.lut_ltr.bits.val3;
++
++		did = calc_fill_time_us - ltr;
++		ltr_did_type = LTR_IWAKE_ON;
++	}
++
++	set_iwake_ltrdid(isys, ltr, did, ltr_did_type);
++
++	/* calculate iwake threshold with 2KB granularity pages */
++	threshold_bytes = did * isys_pb_datarate_mbs;
++	iwake_threshold = max_t(u32, 1, threshold_bytes >> shift);
++	iwake_threshold = min_t(u32, iwake_threshold, max_sram_size);
++
++	mutex_lock(&iwake_watermark->mutex);
++	if (isys->pdata->ipdata->enhanced_iwake) {
++		set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX,
++				   DEFAULT_IWAKE_THRESHOLD);
++		/* calculate number of pages that will be filled in 10 usec */
++		page_num = (DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) /
++			ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE;
++		page_num += ((DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) %
++			     ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE) ? 1 : 0;
++		mem_open_threshold = isys->pdata->ipdata->memopen_threshold;
++		mem_open_threshold = max_t(u32, mem_open_threshold, page_num);
++		dev_dbg(dev, "mem_open_threshold: %u\n", mem_open_threshold);
++		set_iwake_register(isys, GDA_MEMOPEN_THRESHOLD_INDEX,
++				   mem_open_threshold);
++	} else {
++		set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX,
++				   iwake_threshold);
++	}
++
++	iwake_critical_threshold = iwake_threshold +
++		(IS_PIXEL_BUFFER_PAGES - iwake_threshold) / 2;
++
++	dev_dbg(dev, "threshold: %u critical: %u\n", iwake_threshold,
++		iwake_critical_threshold);
++
++	set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
++			   iwake_critical_threshold);
++
++	writel(VAL_PKGC_PMON_CFG_RESET,
++	       isys->adev->isp->base + REG_PKGC_PMON_CFG);
++	writel(VAL_PKGC_PMON_CFG_START,
++	       isys->adev->isp->base + REG_PKGC_PMON_CFG);
++unlock_exit:
++	mutex_unlock(&iwake_watermark->mutex);
++}
++
++static void isys_iwake_watermark_init(struct ipu6_isys *isys)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++
++	INIT_LIST_HEAD(&iwake_watermark->video_list);
++	mutex_init(&iwake_watermark->mutex);
++
++	iwake_watermark->ltrdid.lut_ltr.value = 0;
++	iwake_watermark->isys = isys;
++	iwake_watermark->iwake_enabled = false;
++	iwake_watermark->force_iwake_disable = false;
++}
++
++static void isys_iwake_watermark_cleanup(struct ipu6_isys *isys)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++
++	mutex_lock(&iwake_watermark->mutex);
++	list_del(&iwake_watermark->video_list);
++	mutex_unlock(&iwake_watermark->mutex);
++
++	mutex_destroy(&iwake_watermark->mutex);
++}
++
++/* The .bound() notifier callback when a match is found */
++static int isys_notifier_bound(struct v4l2_async_notifier *notifier,
++			       struct v4l2_subdev *sd,
++			       struct v4l2_async_connection *asc)
++{
++	struct ipu6_isys *isys =
++		container_of(notifier, struct ipu6_isys, notifier);
++	struct sensor_async_sd *s_asd =
++		container_of(asc, struct sensor_async_sd, asc);
++	int ret;
++
++	ret = ipu_bridge_instantiate_vcm(sd->dev);
++	if (ret) {
++		dev_err(&isys->adev->auxdev.dev, "instantiate vcm failed\n");
++		return ret;
++	}
++
++	dev_dbg(&isys->adev->auxdev.dev, "bind %s nlanes is %d port is %d\n",
++		sd->name, s_asd->csi2.nlanes, s_asd->csi2.port);
++	ret = isys_complete_ext_device_registration(isys, sd, &s_asd->csi2);
++	if (ret)
++		return ret;
++
++	return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
++}
++
++static int isys_notifier_complete(struct v4l2_async_notifier *notifier)
++{
++	struct ipu6_isys *isys =
++		container_of(notifier, struct ipu6_isys, notifier);
++
++	return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
++}
++
++static const struct v4l2_async_notifier_operations isys_async_ops = {
++	.bound = isys_notifier_bound,
++	.complete = isys_notifier_complete,
++};
++
++#define ISYS_MAX_PORTS 8
++static int isys_notifier_init(struct ipu6_isys *isys)
++{
++	struct ipu6_device *isp = isys->adev->isp;
++	struct device *dev = &isp->pdev->dev;
++	unsigned int i;
++	int ret;
++
++	v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev);
++
++	for (i = 0; i < ISYS_MAX_PORTS; i++) {
++		struct v4l2_fwnode_endpoint vep = {
++			.bus_type = V4L2_MBUS_CSI2_DPHY
++		};
++		struct sensor_async_sd *s_asd;
++		struct fwnode_handle *ep;
++
++		ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0,
++						FWNODE_GRAPH_ENDPOINT_NEXT);
++		if (!ep)
++			continue;
++
++		ret = v4l2_fwnode_endpoint_parse(ep, &vep);
++		if (ret) {
++			dev_err(dev, "fwnode endpoint parse failed: %d\n", ret);
++			goto err_parse;
++		}
++
++		s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep,
++							struct sensor_async_sd);
++		if (IS_ERR(s_asd)) {
++			ret = PTR_ERR(s_asd);
++			dev_err(dev, "add remove fwnode failed: %d\n", ret);
++			goto err_parse;
++		}
++
++		s_asd->csi2.port = vep.base.port;
++		s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes;
++
++		dev_dbg(dev, "remote endpoint port %d with %d lanes added\n",
++			s_asd->csi2.port, s_asd->csi2.nlanes);
++
++		fwnode_handle_put(ep);
++
++		continue;
++
++err_parse:
++		fwnode_handle_put(ep);
++		return ret;
++	}
++
++	isys->notifier.ops = &isys_async_ops;
++	ret = v4l2_async_nf_register(&isys->notifier);
++	if (ret) {
++		dev_err(dev, "failed to register async notifier : %d\n", ret);
++		v4l2_async_nf_cleanup(&isys->notifier);
++	}
++
++	return ret;
++}
++
++static void isys_notifier_cleanup(struct ipu6_isys *isys)
++{
++	v4l2_async_nf_unregister(&isys->notifier);
++	v4l2_async_nf_cleanup(&isys->notifier);
++}
++
++static int isys_register_devices(struct ipu6_isys *isys)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct pci_dev *pdev = isys->adev->isp->pdev;
++	int ret;
++
++	isys->media_dev.dev = dev;
++	media_device_pci_init(&isys->media_dev,
++			      pdev, IPU6_MEDIA_DEV_MODEL_NAME);
++
++	strscpy(isys->v4l2_dev.name, isys->media_dev.model,
++		sizeof(isys->v4l2_dev.name));
++
++	ret = media_device_register(&isys->media_dev);
++	if (ret < 0)
++		goto out_media_device_unregister;
++
++	isys->v4l2_dev.mdev = &isys->media_dev;
++	isys->v4l2_dev.ctrl_handler = NULL;
++
++	ret = v4l2_device_register(dev->parent, &isys->v4l2_dev);
++	if (ret < 0)
++		goto out_media_device_unregister;
++
++	ret = isys_register_video_devices(isys);
++	if (ret)
++		goto out_v4l2_device_unregister;
++
++	ret = isys_csi2_register_subdevices(isys);
++	if (ret)
++		goto out_isys_unregister_video_device;
++
++	ret = isys_csi2_create_media_links(isys);
++	if (ret)
++		goto out_isys_unregister_subdevices;
++
++	ret = isys_notifier_init(isys);
++	if (ret)
++		goto out_isys_unregister_subdevices;
++
++	return 0;
++
++out_isys_unregister_subdevices:
++	isys_csi2_unregister_subdevices(isys);
++
++out_isys_unregister_video_device:
++	isys_unregister_video_devices(isys);
++
++out_v4l2_device_unregister:
++	v4l2_device_unregister(&isys->v4l2_dev);
++
++out_media_device_unregister:
++	media_device_unregister(&isys->media_dev);
++	media_device_cleanup(&isys->media_dev);
++
++	dev_err(dev, "failed to register isys devices\n");
++
++	return ret;
++}
++
++static void isys_unregister_devices(struct ipu6_isys *isys)
++{
++	isys_unregister_video_devices(isys);
++	isys_csi2_unregister_subdevices(isys);
++	v4l2_device_unregister(&isys->v4l2_dev);
++	media_device_unregister(&isys->media_dev);
++	media_device_cleanup(&isys->media_dev);
++}
++
++static int isys_runtime_pm_resume(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	struct ipu6_device *isp = adev->isp;
++	unsigned long flags;
++	int ret;
++
++	if (!isys)
++		return 0;
++
++	ret = ipu6_mmu_hw_init(adev->mmu);
++	if (ret)
++		return ret;
++
++	cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE);
++
++	ret = ipu6_buttress_start_tsc_sync(isp);
++	if (ret)
++		return ret;
++
++	spin_lock_irqsave(&isys->power_lock, flags);
++	isys->power = 1;
++	spin_unlock_irqrestore(&isys->power_lock, flags);
++
++	isys_setup_hw(isys);
++
++	set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON);
++
++	return 0;
++}
++
++static int isys_runtime_pm_suspend(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++	struct ipu6_isys *isys;
++	unsigned long flags;
++
++	isys = dev_get_drvdata(dev);
++	if (!isys)
++		return 0;
++
++	spin_lock_irqsave(&isys->power_lock, flags);
++	isys->power = 0;
++	spin_unlock_irqrestore(&isys->power_lock, flags);
++
++	mutex_lock(&isys->mutex);
++	isys->need_reset = false;
++	mutex_unlock(&isys->mutex);
++
++	isys->phy_termcal_val = 0;
++	cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
++
++	set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF);
++
++	ipu6_mmu_hw_cleanup(adev->mmu);
++
++	return 0;
++}
++
++static int isys_suspend(struct device *dev)
++{
++	struct ipu6_isys *isys = dev_get_drvdata(dev);
++
++	/* If stream is open, refuse to suspend */
++	if (isys->stream_opened)
++		return -EBUSY;
++
++	return 0;
++}
++
++static int isys_resume(struct device *dev)
++{
++	return 0;
++}
++
++static const struct dev_pm_ops isys_pm_ops = {
++	.runtime_suspend = isys_runtime_pm_suspend,
++	.runtime_resume = isys_runtime_pm_resume,
++	.suspend = isys_suspend,
++	.resume = isys_resume,
++};
++
++static void isys_remove(struct auxiliary_device *auxdev)
++{
++	struct ipu6_bus_device *adev = auxdev_to_adev(auxdev);
++	struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev);
++	struct ipu6_device *isp = adev->isp;
++	struct isys_fw_msgs *fwmsg, *safe;
++	unsigned int i;
++
++	list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head)
++		dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs),
++			       fwmsg, fwmsg->dma_addr, 0);
++
++	list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head)
++		dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs),
++			       fwmsg, fwmsg->dma_addr, 0);
++
++	isys_unregister_devices(isys);
++	isys_notifier_cleanup(isys);
++
++	cpu_latency_qos_remove_request(&isys->pm_qos);
++
++	if (!isp->secure_mode) {
++		ipu6_cpd_free_pkg_dir(adev);
++		ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt);
++		release_firmware(adev->fw);
++	}
++
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++)
++		mutex_destroy(&isys->streams[i].mutex);
++
++	isys_iwake_watermark_cleanup(isys);
++	mutex_destroy(&isys->stream_mutex);
++	mutex_destroy(&isys->mutex);
++}
++
++static int alloc_fw_msg_bufs(struct ipu6_isys *isys, int amount)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct isys_fw_msgs *addr;
++	dma_addr_t dma_addr;
++	unsigned long flags;
++	unsigned int i;
++
++	for (i = 0; i < amount; i++) {
++		addr = dma_alloc_attrs(dev, sizeof(struct isys_fw_msgs),
++				       &dma_addr, GFP_KERNEL, 0);
++		if (!addr)
++			break;
++		addr->dma_addr = dma_addr;
++
++		spin_lock_irqsave(&isys->listlock, flags);
++		list_add(&addr->head, &isys->framebuflist);
++		spin_unlock_irqrestore(&isys->listlock, flags);
++	}
++
++	if (i == amount)
++		return 0;
++
++	spin_lock_irqsave(&isys->listlock, flags);
++	while (!list_empty(&isys->framebuflist)) {
++		addr = list_first_entry(&isys->framebuflist,
++					struct isys_fw_msgs, head);
++		list_del(&addr->head);
++		spin_unlock_irqrestore(&isys->listlock, flags);
++		dma_free_attrs(dev, sizeof(struct isys_fw_msgs), addr,
++			       addr->dma_addr, 0);
++		spin_lock_irqsave(&isys->listlock, flags);
++	}
++	spin_unlock_irqrestore(&isys->listlock, flags);
++
++	return -ENOMEM;
++}
++
++struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream)
++{
++	struct ipu6_isys *isys = stream->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct isys_fw_msgs *msg;
++	unsigned long flags;
++	int ret;
++
++	spin_lock_irqsave(&isys->listlock, flags);
++	if (list_empty(&isys->framebuflist)) {
++		spin_unlock_irqrestore(&isys->listlock, flags);
++		dev_dbg(dev, "Frame list empty\n");
++
++		ret = alloc_fw_msg_bufs(isys, 5);
++		if (ret < 0)
++			return NULL;
++
++		spin_lock_irqsave(&isys->listlock, flags);
++		if (list_empty(&isys->framebuflist)) {
++			spin_unlock_irqrestore(&isys->listlock, flags);
++			dev_err(dev, "Frame list empty\n");
++			return NULL;
++		}
++	}
++	msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head);
++	list_move(&msg->head, &isys->framebuflist_fw);
++	spin_unlock_irqrestore(&isys->listlock, flags);
++	memset(&msg->fw_msg, 0, sizeof(msg->fw_msg));
++
++	return msg;
++}
++
++void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys)
++{
++	struct isys_fw_msgs *fwmsg, *fwmsg0;
++	unsigned long flags;
++
++	spin_lock_irqsave(&isys->listlock, flags);
++	list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head)
++		list_move(&fwmsg->head, &isys->framebuflist);
++	spin_unlock_irqrestore(&isys->listlock, flags);
++}
++
++void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data)
++{
++	struct isys_fw_msgs *msg;
++	unsigned long flags;
++	u64 *ptr = (u64 *)data;
++
++	if (!ptr)
++		return;
++
++	spin_lock_irqsave(&isys->listlock, flags);
++	msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy);
++	list_move(&msg->head, &isys->framebuflist);
++	spin_unlock_irqrestore(&isys->listlock, flags);
++}
++
++static int isys_probe(struct auxiliary_device *auxdev,
++		      const struct auxiliary_device_id *auxdev_id)
++{
++	struct ipu6_bus_device *adev = auxdev_to_adev(auxdev);
++	struct ipu6_device *isp = adev->isp;
++	const struct firmware *fw;
++	struct ipu6_isys *isys;
++	unsigned int i;
++	int ret;
++
++	if (!isp->bus_ready_to_probe)
++		return -EPROBE_DEFER;
++
++	isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL);
++	if (!isys)
++		return -ENOMEM;
++
++	ret = ipu6_mmu_hw_init(adev->mmu);
++	if (ret)
++		return ret;
++
++	adev->auxdrv_data =
++		(const struct ipu6_auxdrv_data *)auxdev_id->driver_data;
++	adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver);
++	isys->adev = adev;
++	isys->pdata = adev->pdata;
++
++	/* initial sensor type */
++	isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
++
++	spin_lock_init(&isys->streams_lock);
++	spin_lock_init(&isys->power_lock);
++	isys->power = 0;
++	isys->phy_termcal_val = 0;
++
++	mutex_init(&isys->mutex);
++	mutex_init(&isys->stream_mutex);
++
++	spin_lock_init(&isys->listlock);
++	INIT_LIST_HEAD(&isys->framebuflist);
++	INIT_LIST_HEAD(&isys->framebuflist_fw);
++
++	isys->line_align = IPU6_ISYS_2600_MEM_LINE_ALIGN;
++	isys->icache_prefetch = 0;
++
++	dev_set_drvdata(&auxdev->dev, isys);
++
++	isys_stream_init(isys);
++
++	if (!isp->secure_mode) {
++		fw = isp->cpd_fw;
++		ret = ipu6_buttress_map_fw_image(adev, fw, &adev->fw_sgt);
++		if (ret)
++			goto release_firmware;
++
++		ret = ipu6_cpd_create_pkg_dir(adev, isp->cpd_fw->data);
++		if (ret)
++			goto remove_shared_buffer;
++	}
++
++	cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
++
++	ret = alloc_fw_msg_bufs(isys, 20);
++	if (ret < 0)
++		goto out_remove_pkg_dir_shared_buffer;
++
++	isys_iwake_watermark_init(isys);
++
++	if (is_ipu6se(adev->isp->hw_ver))
++		isys->phy_set_power = ipu6_isys_jsl_phy_set_power;
++	else if (is_ipu6ep_mtl(adev->isp->hw_ver))
++		isys->phy_set_power = ipu6_isys_dwc_phy_set_power;
++	else
++		isys->phy_set_power = ipu6_isys_mcd_phy_set_power;
++
++	ret = isys_register_devices(isys);
++	if (ret)
++		goto out_remove_pkg_dir_shared_buffer;
++
++	ipu6_mmu_hw_cleanup(adev->mmu);
++
++	return 0;
++
++out_remove_pkg_dir_shared_buffer:
++	if (!isp->secure_mode)
++		ipu6_cpd_free_pkg_dir(adev);
++remove_shared_buffer:
++	if (!isp->secure_mode)
++		ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt);
++release_firmware:
++	if (!isp->secure_mode)
++		release_firmware(adev->fw);
++
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++)
++		mutex_destroy(&isys->streams[i].mutex);
++
++	mutex_destroy(&isys->mutex);
++	mutex_destroy(&isys->stream_mutex);
++
++	ipu6_mmu_hw_cleanup(adev->mmu);
++
++	return ret;
++}
++
++struct fwmsg {
++	int type;
++	char *msg;
++	bool valid_ts;
++};
++
++static const struct fwmsg fw_msg[] = {
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
++	 "STREAM_START_AND_CAPTURE_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
++	 "STREAM_START_AND_CAPTURE_DONE", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1},
++	{-1, "UNKNOWN MESSAGE", 0}
++};
++
++static u32 resp_type_to_index(int type)
++{
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(fw_msg); i++)
++		if (fw_msg[i].type == type)
++			return i;
++
++	return  ARRAY_SIZE(fw_msg) - 1;
++}
++
++static int isys_isr_one(struct ipu6_bus_device *adev)
++{
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	struct ipu6_fw_isys_resp_info_abi *resp;
++	struct ipu6_isys_stream *stream;
++	struct ipu6_isys_csi2 *csi2 = NULL;
++	u32 index;
++	u64 ts;
++
++	if (!isys->fwcom)
++		return 1;
++
++	resp = ipu6_fw_isys_get_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES);
++	if (!resp)
++		return 1;
++
++	ts = (u64)resp->timestamp[1] << 32 | resp->timestamp[0];
++
++	index = resp_type_to_index(resp->type);
++	dev_dbg(&adev->auxdev.dev,
++		"FW resp %02d %s, stream %u, ts 0x%16.16llx, pin %d\n",
++		resp->type, fw_msg[index].msg, resp->stream_handle,
++		fw_msg[index].valid_ts ? ts : 0, resp->pin_id);
++
++	if (resp->error_info.error == IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION)
++		/* Suspension is kind of special case: not enough buffers */
++		dev_dbg(&adev->auxdev.dev,
++			"FW error resp SUSPENSION, details %d\n",
++			resp->error_info.error_details);
++	else if (resp->error_info.error)
++		dev_dbg(&adev->auxdev.dev,
++			"FW error resp error %d, details %d\n",
++			resp->error_info.error, resp->error_info.error_details);
++
++	if (resp->stream_handle >= IPU6_ISYS_MAX_STREAMS) {
++		dev_err(&adev->auxdev.dev, "bad stream handle %u\n",
++			resp->stream_handle);
++		goto leave;
++	}
++
++	stream = ipu6_isys_query_stream_by_handle(isys, resp->stream_handle);
++	if (!stream) {
++		dev_err(&adev->auxdev.dev, "stream of stream_handle %u is unused\n",
++			resp->stream_handle);
++		goto leave;
++	}
++	stream->error = resp->error_info.error;
++
++	csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
++
++	switch (resp->type) {
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE:
++		complete(&stream->stream_open_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK:
++		complete(&stream->stream_close_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK:
++		complete(&stream->stream_start_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK:
++		complete(&stream->stream_start_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK:
++		complete(&stream->stream_stop_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK:
++		complete(&stream->stream_stop_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY:
++		/*
++		 * firmware only release the capture msg until software
++		 * get pin_data_ready event
++		 */
++		ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), resp->buf_id);
++		if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS &&
++		    stream->output_pins[resp->pin_id].pin_ready)
++			stream->output_pins[resp->pin_id].pin_ready(stream,
++								    resp);
++		else
++			dev_warn(&adev->auxdev.dev,
++				 "%d:No data pin ready handler for pin id %d\n",
++				 resp->stream_handle, resp->pin_id);
++		if (csi2)
++			ipu6_isys_csi2_error(csi2);
++
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK:
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE:
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE:
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF:
++
++		ipu6_isys_csi2_sof_event_by_stream(stream);
++		stream->seq[stream->seq_index].sequence =
++			atomic_read(&stream->sequence) - 1;
++		stream->seq[stream->seq_index].timestamp = ts;
++		dev_dbg(&adev->auxdev.dev,
++			"sof: handle %d: (index %u), timestamp 0x%16.16llx\n",
++			resp->stream_handle,
++			stream->seq[stream->seq_index].sequence, ts);
++		stream->seq_index = (stream->seq_index + 1)
++			% IPU6_ISYS_MAX_PARALLEL_SOF;
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF:
++		ipu6_isys_csi2_eof_event_by_stream(stream);
++		dev_dbg(&adev->auxdev.dev,
++			"eof: handle %d: (index %u), timestamp 0x%16.16llx\n",
++			resp->stream_handle,
++			stream->seq[stream->seq_index].sequence, ts);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY:
++		break;
++	default:
++		dev_err(&adev->auxdev.dev, "%d:unknown response type %u\n",
++			resp->stream_handle, resp->type);
++		break;
++	}
++
++	ipu6_isys_put_stream(stream);
++leave:
++	ipu6_fw_isys_put_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES);
++	return 0;
++}
++
++static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data = {
++	.isr = isys_isr,
++	.isr_threaded = NULL,
++	.wake_isr_thread = false,
++};
++
++static const struct auxiliary_device_id ipu6_isys_id_table[] = {
++	{
++		.name = "intel_ipu6.isys",
++		.driver_data = (kernel_ulong_t)&ipu6_isys_auxdrv_data,
++	},
++	{ }
++};
++MODULE_DEVICE_TABLE(auxiliary, ipu6_isys_id_table);
++
++static struct auxiliary_driver isys_driver = {
++	.name = IPU6_ISYS_NAME,
++	.probe = isys_probe,
++	.remove = isys_remove,
++	.id_table = ipu6_isys_id_table,
++	.driver = {
++		.pm = &isys_pm_ops,
++	},
++};
++
++module_auxiliary_driver(isys_driver);
++
++MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
++MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
++MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
++MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>");
++MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Intel IPU6 input system driver");
++MODULE_IMPORT_NS(INTEL_IPU6);
++MODULE_IMPORT_NS(INTEL_IPU_BRIDGE);
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h
+new file mode 100644
+index 000000000000..cf7a90bfedc9
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h
+@@ -0,0 +1,207 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_H
++#define IPU6_ISYS_H
++
++#include <linux/irqreturn.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++#include <linux/pm_qos.h>
++#include <linux/spinlock_types.h>
++#include <linux/types.h>
++
++#include <media/media-device.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-device.h>
++
++#include "ipu6.h"
++#include "ipu6-fw-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-isys-video.h"
++
++struct ipu6_bus_device;
++
++#define IPU6_ISYS_ENTITY_PREFIX		"Intel IPU6"
++/* FW support max 16 streams */
++#define IPU6_ISYS_MAX_STREAMS		16
++#define ISYS_UNISPART_IRQS	(IPU6_ISYS_UNISPART_IRQ_SW |	\
++				 IPU6_ISYS_UNISPART_IRQ_CSI0 |	\
++				 IPU6_ISYS_UNISPART_IRQ_CSI1)
++
++#define IPU6_ISYS_2600_MEM_LINE_ALIGN	64
++
++/*
++ * Current message queue configuration. These must be big enough
++ * so that they never gets full. Queues are located in system memory
++ */
++#define IPU6_ISYS_SIZE_RECV_QUEUE 40
++#define IPU6_ISYS_SIZE_SEND_QUEUE 40
++#define IPU6_ISYS_SIZE_PROXY_RECV_QUEUE 5
++#define IPU6_ISYS_SIZE_PROXY_SEND_QUEUE 5
++#define IPU6_ISYS_NUM_RECV_QUEUE 1
++
++#define IPU6_ISYS_MIN_WIDTH		1U
++#define IPU6_ISYS_MIN_HEIGHT		1U
++#define IPU6_ISYS_MAX_WIDTH		4672U
++#define IPU6_ISYS_MAX_HEIGHT		3416U
++
++/* the threshold granularity is 2KB on IPU6 */
++#define IPU6_SRAM_GRANULARITY_SHIFT	11
++#define IPU6_SRAM_GRANULARITY_SIZE	2048
++/* the threshold granularity is 1KB on IPU6SE */
++#define IPU6SE_SRAM_GRANULARITY_SHIFT	10
++#define IPU6SE_SRAM_GRANULARITY_SIZE	1024
++/* IS pixel buffer is 256KB, MaxSRAMSize is 200KB on IPU6 */
++#define IPU6_MAX_SRAM_SIZE			(200 << 10)
++/* IS pixel buffer is 128KB, MaxSRAMSize is 96KB on IPU6SE */
++#define IPU6SE_MAX_SRAM_SIZE			(96 << 10)
++
++#define IPU6EP_LTR_VALUE			200
++#define IPU6EP_MIN_MEMOPEN_TH			0x4
++#define IPU6EP_MTL_LTR_VALUE			1023
++#define IPU6EP_MTL_MIN_MEMOPEN_TH		0xc
++
++struct ltr_did {
++	union {
++		u32 value;
++		struct {
++			u8 val0;
++			u8 val1;
++			u8 val2;
++			u8 val3;
++		} bits;
++	} lut_ltr;
++	union {
++		u32 value;
++		struct {
++			u8 th0;
++			u8 th1;
++			u8 th2;
++			u8 th3;
++		} bits;
++	} lut_fill_time;
++};
++
++struct isys_iwake_watermark {
++	bool iwake_enabled;
++	bool force_iwake_disable;
++	u32 iwake_threshold;
++	u64 isys_pixelbuffer_datarate;
++	struct ltr_did ltrdid;
++	struct mutex mutex; /* protect whole struct */
++	struct ipu6_isys *isys;
++	struct list_head video_list;
++};
++
++struct ipu6_isys_csi2_config {
++	u32 nlanes;
++	u32 port;
++};
++
++struct sensor_async_sd {
++	struct v4l2_async_connection asc;
++	struct ipu6_isys_csi2_config csi2;
++};
++
++/*
++ * struct ipu6_isys
++ *
++ * @media_dev: Media device
++ * @v4l2_dev: V4L2 device
++ * @adev: ISYS bus device
++ * @power: Is ISYS powered on or not?
++ * @isr_bits: Which bits does the ISR handle?
++ * @power_lock: Serialise access to power (power state in general)
++ * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers
++ * @streams_lock: serialise access to streams
++ * @streams: streams per firmware stream ID
++ * @fwcom: fw communication layer private pointer
++ *         or optional external library private pointer
++ * @line_align: line alignment in memory
++ * @phy_termcal_val: the termination calibration value, only used for DWC PHY
++ * @need_reset: Isys requires d0i0->i3 transition
++ * @ref_count: total number of callers fw open
++ * @mutex: serialise access isys video open/release related operations
++ * @stream_mutex: serialise stream start and stop, queueing requests
++ * @pdata: platform data pointer
++ * @csi2: CSI-2 receivers
++ */
++struct ipu6_isys {
++	struct media_device media_dev;
++	struct v4l2_device v4l2_dev;
++	struct ipu6_bus_device *adev;
++
++	int power;
++	spinlock_t power_lock;
++	u32 isr_csi2_bits;
++	u32 csi2_rx_ctrl_cached;
++	spinlock_t streams_lock;
++	struct ipu6_isys_stream streams[IPU6_ISYS_MAX_STREAMS];
++	int streams_ref_count[IPU6_ISYS_MAX_STREAMS];
++	void *fwcom;
++	unsigned int line_align;
++	u32 phy_termcal_val;
++	bool need_reset;
++	bool icache_prefetch;
++	bool csi2_cse_ipc_not_supported;
++	unsigned int ref_count;
++	unsigned int stream_opened;
++	unsigned int sensor_type;
++
++	struct mutex mutex;
++	struct mutex stream_mutex;
++
++	struct ipu6_isys_pdata *pdata;
++
++	int (*phy_set_power)(struct ipu6_isys *isys,
++			     struct ipu6_isys_csi2_config *cfg,
++			     const struct ipu6_isys_csi2_timing *timing,
++			     bool on);
++
++	struct ipu6_isys_csi2 *csi2;
++	struct ipu6_isys_video av[NR_OF_VIDEO_DEVICE];
++
++	struct pm_qos_request pm_qos;
++	spinlock_t listlock;	/* Protect framebuflist */
++	struct list_head framebuflist;
++	struct list_head framebuflist_fw;
++	struct v4l2_async_notifier notifier;
++	struct isys_iwake_watermark iwake_watermark;
++};
++
++struct isys_fw_msgs {
++	union {
++		u64 dummy;
++		struct ipu6_fw_isys_frame_buff_set_abi frame;
++		struct ipu6_fw_isys_stream_cfg_data_abi stream;
++	} fw_msg;
++	struct list_head head;
++	dma_addr_t dma_addr;
++};
++
++struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream);
++void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data);
++void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys);
++
++extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops;
++
++void isys_setup_hw(struct ipu6_isys *isys);
++irqreturn_t isys_isr(struct ipu6_bus_device *adev);
++void update_watermark_setting(struct ipu6_isys *isys);
++
++int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on);
++
++int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on);
++
++int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on);
++#endif /* IPU6_ISYS_H */
+-- 
+2.43.2
+
+
+From 7cdb944c1cc8beb6f61268e2fe177d585fa5f415 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:25 +0800
+Subject: [PATCH 18/33] media: intel/ipu6: input system video capture nodes
+
+Register v4l2 video device and setup the vb2 queue to
+support basic video capture. Video streaming callback
+will trigger the input system driver to construct a
+input system stream configuration for firmware based on
+data type and stream ID and then queue buffers to firmware
+to do capture.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ .../media/pci/intel/ipu6/ipu6-isys-queue.c    |  825 +++++++++++
+ .../media/pci/intel/ipu6/ipu6-isys-queue.h    |   76 +
+ .../media/pci/intel/ipu6/ipu6-isys-video.c    | 1253 +++++++++++++++++
+ .../media/pci/intel/ipu6/ipu6-isys-video.h    |  136 ++
+ 4 files changed, 2290 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+new file mode 100644
+index 000000000000..735d2d642d87
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+@@ -0,0 +1,825 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++#include <linux/atomic.h>
++#include <linux/bug.h>
++#include <linux/device.h>
++#include <linux/list.h>
++#include <linux/lockdep.h>
++#include <linux/mutex.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-subdev.h>
++#include <media/videobuf2-dma-contig.h>
++#include <media/videobuf2-v4l2.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-fw-isys.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-video.h"
++
++static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
++		       unsigned int *num_planes, unsigned int sizes[],
++		       struct device *alloc_devs[])
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	bool use_fmt = false;
++	unsigned int i;
++	u32 size;
++
++	/* num_planes == 0: we're being called through VIDIOC_REQBUFS */
++	if (!*num_planes) {
++		use_fmt = true;
++		*num_planes = av->mpix.num_planes;
++	}
++
++	for (i = 0; i < *num_planes; i++) {
++		size = av->mpix.plane_fmt[i].sizeimage;
++		if (use_fmt) {
++			sizes[i] = size;
++		} else if (sizes[i] < size) {
++			dev_err(dev, "%s: queue setup: plane %d size %u < %u\n",
++				av->vdev.name, i, sizes[i], size);
++			return -EINVAL;
++		}
++
++		alloc_devs[i] = aq->dev;
++	}
++
++	return 0;
++}
++
++static int ipu6_isys_buf_prepare(struct vb2_buffer *vb)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++
++	dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n",
++		av->vdev.name, av->mpix.plane_fmt[0].sizeimage,
++		vb2_plane_size(vb, 0));
++
++	if (av->mpix.plane_fmt[0].sizeimage > vb2_plane_size(vb, 0))
++		return -EINVAL;
++
++	vb2_set_plane_payload(vb, 0, av->mpix.plane_fmt[0].bytesperline *
++			      av->mpix.height);
++	vb->planes[0].data_offset = 0;
++
++	return 0;
++}
++
++/*
++ * Queue a buffer list back to incoming or active queues. The buffers
++ * are removed from the buffer list.
++ */
++void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl,
++				 unsigned long op_flags,
++				 enum vb2_buffer_state state)
++{
++	struct ipu6_isys_buffer *ib, *ib_safe;
++	unsigned long flags;
++	bool first = true;
++
++	if (!bl)
++		return;
++
++	WARN_ON_ONCE(!bl->nbufs);
++	WARN_ON_ONCE(op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE &&
++		     op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING);
++
++	list_for_each_entry_safe(ib, ib_safe, &bl->head, head) {
++		struct ipu6_isys_video *av;
++		struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++		struct ipu6_isys_queue *aq =
++			vb2_queue_to_isys_queue(vb->vb2_queue);
++		struct device *dev;
++
++		av = ipu6_isys_queue_to_video(aq);
++		dev = &av->isys->adev->auxdev.dev;
++		spin_lock_irqsave(&aq->lock, flags);
++		list_del(&ib->head);
++		if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE)
++			list_add(&ib->head, &aq->active);
++		else if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING)
++			list_add_tail(&ib->head, &aq->incoming);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_SET_STATE)
++			vb2_buffer_done(vb, state);
++
++		if (first) {
++			dev_dbg(dev,
++				"queue buf list %p flags %lx, s %d, %d bufs\n",
++				bl, op_flags, state, bl->nbufs);
++			first = false;
++		}
++
++		bl->nbufs--;
++	}
++
++	WARN_ON(bl->nbufs);
++}
++
++/*
++ * flush_firmware_streamon_fail() - Flush in cases where requests may
++ * have been queued to firmware and the *firmware streamon fails for a
++ * reason or another.
++ */
++static void flush_firmware_streamon_fail(struct ipu6_isys_stream *stream)
++{
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_queue *aq;
++	unsigned long flags;
++
++	lockdep_assert_held(&stream->mutex);
++
++	list_for_each_entry(aq, &stream->queues, node) {
++		struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++		struct ipu6_isys_buffer *ib, *ib_safe;
++
++		spin_lock_irqsave(&aq->lock, flags);
++		list_for_each_entry_safe(ib, ib_safe, &aq->active, head) {
++			struct vb2_buffer *vb =
++				ipu6_isys_buffer_to_vb2_buffer(ib);
++
++			list_del(&ib->head);
++			if (av->streaming) {
++				dev_dbg(dev,
++					"%s: queue buffer %u back to incoming\n",
++					av->vdev.name, vb->index);
++				/* Queue already streaming, return to driver. */
++				list_add(&ib->head, &aq->incoming);
++				continue;
++			}
++			/* Queue not yet streaming, return to user. */
++			dev_dbg(dev, "%s: return %u back to videobuf2\n",
++				av->vdev.name, vb->index);
++			vb2_buffer_done(ipu6_isys_buffer_to_vb2_buffer(ib),
++					VB2_BUF_STATE_QUEUED);
++		}
++		spin_unlock_irqrestore(&aq->lock, flags);
++	}
++}
++
++/*
++ * Attempt obtaining a buffer list from the incoming queues, a list of buffers
++ * that contains one entry from each video buffer queue. If a buffer can't be
++ * obtained from every queue, the buffers are returned back to the queue.
++ */
++static int buffer_list_get(struct ipu6_isys_stream *stream,
++			   struct ipu6_isys_buffer_list *bl)
++{
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_queue *aq;
++	unsigned long flags;
++	unsigned long buf_flag = IPU6_ISYS_BUFFER_LIST_FL_INCOMING;
++
++	bl->nbufs = 0;
++	INIT_LIST_HEAD(&bl->head);
++
++	list_for_each_entry(aq, &stream->queues, node) {
++		struct ipu6_isys_buffer *ib;
++
++		spin_lock_irqsave(&aq->lock, flags);
++		if (list_empty(&aq->incoming)) {
++			spin_unlock_irqrestore(&aq->lock, flags);
++			if (!list_empty(&bl->head))
++				ipu6_isys_buffer_list_queue(bl, buf_flag, 0);
++			return -ENODATA;
++		}
++
++		ib = list_last_entry(&aq->incoming,
++				     struct ipu6_isys_buffer, head);
++
++		dev_dbg(dev, "buffer: %s: buffer %u\n",
++			ipu6_isys_queue_to_video(aq)->vdev.name,
++			ipu6_isys_buffer_to_vb2_buffer(ib)->index);
++		list_del(&ib->head);
++		list_add(&ib->head, &bl->head);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		bl->nbufs++;
++	}
++
++	dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs);
++
++	return 0;
++}
++
++static void
++ipu6_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb,
++				  struct ipu6_fw_isys_frame_buff_set_abi *set)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
++
++	set->output_pins[aq->fw_output].addr =
++		vb2_dma_contig_plane_dma_addr(vb, 0);
++	set->output_pins[aq->fw_output].out_buf_id = vb->index + 1;
++}
++
++/*
++ * Convert a buffer list to a isys fw ABI framebuffer set. The
++ * buffer list is not modified.
++ */
++#define IPU6_ISYS_FRAME_NUM_THRESHOLD  (30)
++void
++ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set,
++			      struct ipu6_isys_stream *stream,
++			      struct ipu6_isys_buffer_list *bl)
++{
++	struct ipu6_isys_buffer *ib;
++
++	WARN_ON(!bl->nbufs);
++
++	set->send_irq_sof = 1;
++	set->send_resp_sof = 1;
++	set->send_irq_eof = 0;
++	set->send_resp_eof = 0;
++
++	if (stream->streaming)
++		set->send_irq_capture_ack = 0;
++	else
++		set->send_irq_capture_ack = 1;
++	set->send_irq_capture_done = 0;
++
++	set->send_resp_capture_ack = 1;
++	set->send_resp_capture_done = 1;
++	if (atomic_read(&stream->sequence) >= IPU6_ISYS_FRAME_NUM_THRESHOLD) {
++		set->send_resp_capture_ack = 0;
++		set->send_resp_capture_done = 0;
++	}
++
++	list_for_each_entry(ib, &bl->head, head) {
++		struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++
++		ipu6_isys_buf_to_fw_frame_buf_pin(vb, set);
++	}
++}
++
++/* Start streaming for real. The buffer list must be available. */
++static int ipu6_isys_stream_start(struct ipu6_isys_video *av,
++				  struct ipu6_isys_buffer_list *bl, bool error)
++{
++	struct ipu6_isys_stream *stream = av->stream;
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_buffer_list __bl;
++	int ret;
++
++	mutex_lock(&stream->isys->stream_mutex);
++	ret = ipu6_isys_video_set_streaming(av, 1, bl);
++	mutex_unlock(&stream->isys->stream_mutex);
++	if (ret)
++		goto out_requeue;
++
++	stream->streaming = 1;
++
++	bl = &__bl;
++
++	do {
++		struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
++		struct isys_fw_msgs *msg;
++		u16 send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE;
++
++		ret = buffer_list_get(stream, bl);
++		if (ret < 0)
++			break;
++
++		msg = ipu6_get_fw_msg_buf(stream);
++		if (!msg)
++			return -ENOMEM;
++
++		buf = &msg->fw_msg.frame;
++		ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);
++		ipu6_fw_isys_dump_frame_buff_set(dev, buf,
++						 stream->nr_output_pins);
++		ipu6_isys_buffer_list_queue(bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE,
++					    0);
++		ret = ipu6_fw_isys_complex_cmd(stream->isys,
++					       stream->stream_handle, buf,
++					       msg->dma_addr, sizeof(*buf),
++					       send_type);
++	} while (!WARN_ON(ret));
++
++	return 0;
++
++out_requeue:
++	if (bl && bl->nbufs)
++		ipu6_isys_buffer_list_queue(bl,
++					    (IPU6_ISYS_BUFFER_LIST_FL_INCOMING |
++					     error) ?
++					    IPU6_ISYS_BUFFER_LIST_FL_SET_STATE :
++					    0, error ? VB2_BUF_STATE_ERROR :
++					    VB2_BUF_STATE_QUEUED);
++	flush_firmware_streamon_fail(stream);
++
++	return ret;
++}
++
++static void buf_queue(struct vb2_buffer *vb)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
++	struct ipu6_isys_video_buffer *ivb =
++		vb2_buffer_to_ipu6_isys_video_buffer(vvb);
++	struct ipu6_isys_buffer *ib = &ivb->ib;
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct media_pipeline *media_pipe =
++		media_entity_pipeline(&av->vdev.entity);
++	struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
++	struct ipu6_isys_stream *stream = av->stream;
++	struct ipu6_isys_buffer_list bl;
++	struct isys_fw_msgs *msg;
++	unsigned long flags;
++	dma_addr_t dma;
++	unsigned int i;
++	int ret;
++
++	dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name);
++
++	for (i = 0; i < vb->num_planes; i++) {
++		dma = vb2_dma_contig_plane_dma_addr(vb, i);
++		dev_dbg(dev, "iova: plane %u iova %pad\n", i, &dma);
++	}
++
++	spin_lock_irqsave(&aq->lock, flags);
++	list_add(&ib->head, &aq->incoming);
++	spin_unlock_irqrestore(&aq->lock, flags);
++
++	if (!media_pipe || !vb->vb2_queue->start_streaming_called) {
++		dev_dbg(dev, "media pipeline is not ready for %s\n",
++			av->vdev.name);
++		return;
++	}
++
++	mutex_lock(&stream->mutex);
++
++	if (stream->nr_streaming != stream->nr_queues) {
++		dev_dbg(dev, "not streaming yet, adding to incoming\n");
++		goto out;
++	}
++
++	/*
++	 * We just put one buffer to the incoming list of this queue
++	 * (above). Let's see whether all queues in the pipeline would
++	 * have a buffer.
++	 */
++	ret = buffer_list_get(stream, &bl);
++	if (ret < 0) {
++		dev_warn(dev, "No buffers available\n");
++		goto out;
++	}
++
++	msg = ipu6_get_fw_msg_buf(stream);
++	if (!msg) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	buf = &msg->fw_msg.frame;
++	ipu6_isys_buf_to_fw_frame_buf(buf, stream, &bl);
++	ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins);
++
++	if (!stream->streaming) {
++		ret = ipu6_isys_stream_start(av, &bl, true);
++		if (ret)
++			dev_err(dev, "stream start failed.\n");
++		goto out;
++	}
++
++	/*
++	 * We must queue the buffers in the buffer list to the
++	 * appropriate video buffer queues BEFORE passing them to the
++	 * firmware since we could get a buffer event back before we
++	 * have queued them ourselves to the active queue.
++	 */
++	ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
++
++	ret = ipu6_fw_isys_complex_cmd(stream->isys, stream->stream_handle,
++				       buf, msg->dma_addr, sizeof(*buf),
++				       IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE);
++	if (ret < 0)
++		dev_err(dev, "send stream capture failed\n");
++
++out:
++	mutex_unlock(&stream->mutex);
++}
++
++static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq)
++{
++	struct v4l2_mbus_framefmt format;
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct media_pad *remote_pad =
++		media_pad_remote_pad_first(av->vdev.entity.pads);
++	struct v4l2_subdev *sd;
++	u32 r_stream;
++	int ret;
++
++	if (!remote_pad)
++		return -ENOTCONN;
++
++	sd = media_entity_to_v4l2_subdev(remote_pad->entity);
++	r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, remote_pad->index);
++
++	ret = ipu6_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream,
++					   &format);
++
++	if (ret) {
++		dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n",
++			sd->entity.name, remote_pad->index, r_stream);
++		return ret;
++	}
++
++	if (format.width != av->mpix.width ||
++	    format.height != av->mpix.height) {
++		dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n",
++			av->mpix.width, av->mpix.height,
++			format.width, format.height);
++		return -EINVAL;
++	}
++
++	if (format.field != av->mpix.field) {
++		dev_dbg(dev, "wrong field value 0x%8.8x (0x%8.8x expected)\n",
++			av->mpix.field, format.field);
++		return -EINVAL;
++	}
++
++	if (format.code != av->pfmt->code) {
++		dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n",
++			av->pfmt->code, format.code);
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static void return_buffers(struct ipu6_isys_queue *aq,
++			   enum vb2_buffer_state state)
++{
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct ipu6_isys_buffer *ib;
++	bool need_reset = false;
++	unsigned long flags;
++
++	spin_lock_irqsave(&aq->lock, flags);
++	while (!list_empty(&aq->incoming)) {
++		struct vb2_buffer *vb;
++
++		ib = list_first_entry(&aq->incoming, struct ipu6_isys_buffer,
++				      head);
++		vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++		list_del(&ib->head);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		vb2_buffer_done(vb, state);
++
++		spin_lock_irqsave(&aq->lock, flags);
++	}
++
++	/*
++	 * Something went wrong (FW crash / HW hang / not all buffers
++	 * returned from isys) if there are still buffers queued in active
++	 * queue. We have to clean up places a bit.
++	 */
++	while (!list_empty(&aq->active)) {
++		struct vb2_buffer *vb;
++
++		ib = list_first_entry(&aq->active, struct ipu6_isys_buffer,
++				      head);
++		vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++
++		list_del(&ib->head);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		vb2_buffer_done(vb, state);
++
++		spin_lock_irqsave(&aq->lock, flags);
++		need_reset = true;
++	}
++
++	spin_unlock_irqrestore(&aq->lock, flags);
++
++	if (need_reset) {
++		mutex_lock(&av->isys->mutex);
++		av->isys->need_reset = true;
++		mutex_unlock(&av->isys->mutex);
++	}
++}
++
++static void ipu6_isys_stream_cleanup(struct ipu6_isys_video *av)
++{
++	video_device_pipeline_stop(&av->vdev);
++	ipu6_isys_put_stream(av->stream);
++	av->stream = NULL;
++}
++
++static int start_streaming(struct vb2_queue *q, unsigned int count)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct ipu6_isys_buffer_list __bl, *bl = NULL;
++	struct ipu6_isys_stream *stream;
++	struct media_entity *source_entity = NULL;
++	int nr_queues, ret;
++
++	dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
++		av->vdev.name, av->mpix.width, av->mpix.height,
++		av->pfmt->css_pixelformat);
++
++	ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues);
++	if (ret < 0) {
++		dev_err(dev, "failed to setup video\n");
++		goto out_return_buffers;
++	}
++
++	ret = ipu6_isys_link_fmt_validate(aq);
++	if (ret) {
++		dev_err(dev,
++			"%s: link format validation failed (%d)\n",
++			av->vdev.name, ret);
++		goto out_pipeline_stop;
++	}
++
++	ret = ipu6_isys_fw_open(av->isys);
++	if (ret)
++		goto out_pipeline_stop;
++
++	stream = av->stream;
++	mutex_lock(&stream->mutex);
++	if (!stream->nr_streaming) {
++		ret = ipu6_isys_video_prepare_stream(av, source_entity,
++						     nr_queues);
++		if (ret)
++			goto out_fw_close;
++	}
++
++	stream->nr_streaming++;
++	dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming,
++		stream->nr_queues);
++
++	list_add(&aq->node, &stream->queues);
++	ipu6_isys_set_csi2_streams_status(av, true);
++	ipu6_isys_configure_stream_watermark(av, true);
++	ipu6_isys_update_stream_watermark(av, true);
++
++	if (stream->nr_streaming != stream->nr_queues)
++		goto out;
++
++	bl = &__bl;
++	ret = buffer_list_get(stream, bl);
++	if (ret < 0) {
++		dev_dbg(dev,
++			"no buffer available, postponing streamon\n");
++		goto out;
++	}
++
++	ret = ipu6_isys_stream_start(av, bl, false);
++	if (ret)
++		goto out_stream_start;
++
++out:
++	mutex_unlock(&stream->mutex);
++
++	return 0;
++
++out_stream_start:
++	list_del(&aq->node);
++	stream->nr_streaming--;
++
++out_fw_close:
++	mutex_unlock(&stream->mutex);
++	ipu6_isys_fw_close(av->isys);
++
++out_pipeline_stop:
++	ipu6_isys_stream_cleanup(av);
++
++out_return_buffers:
++	return_buffers(aq, VB2_BUF_STATE_QUEUED);
++
++	return ret;
++}
++
++static void stop_streaming(struct vb2_queue *q)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct ipu6_isys_stream *stream = av->stream;
++
++	ipu6_isys_set_csi2_streams_status(av, false);
++
++	mutex_lock(&stream->mutex);
++
++	ipu6_isys_update_stream_watermark(av, false);
++
++	mutex_lock(&av->isys->stream_mutex);
++	if (stream->nr_streaming == stream->nr_queues && stream->streaming)
++		ipu6_isys_video_set_streaming(av, 0, NULL);
++	mutex_unlock(&av->isys->stream_mutex);
++
++	stream->nr_streaming--;
++	list_del(&aq->node);
++	stream->streaming = 0;
++	mutex_unlock(&stream->mutex);
++
++	ipu6_isys_stream_cleanup(av);
++
++	return_buffers(aq, VB2_BUF_STATE_ERROR);
++
++	ipu6_isys_fw_close(av->isys);
++}
++
++static unsigned int
++get_sof_sequence_by_timestamp(struct ipu6_isys_stream *stream,
++			      struct ipu6_fw_isys_resp_info_abi *info)
++{
++	u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0];
++	struct ipu6_isys *isys = stream->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int i;
++
++	/*
++	 * The timestamp is invalid as no TSC in some FPGA platform,
++	 * so get the sequence from pipeline directly in this case.
++	 */
++	if (time == 0)
++		return atomic_read(&stream->sequence) - 1;
++
++	for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++)
++		if (time == stream->seq[i].timestamp) {
++			dev_dbg(dev, "sof: using seq nr %u for ts %llu\n",
++				stream->seq[i].sequence, time);
++			return stream->seq[i].sequence;
++		}
++
++	for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++)
++		dev_dbg(dev, "sof: sequence %u, timestamp value %llu\n",
++			stream->seq[i].sequence, stream->seq[i].timestamp);
++
++	return 0;
++}
++
++static u64 get_sof_ns_delta(struct ipu6_isys_video *av,
++			    struct ipu6_fw_isys_resp_info_abi *info)
++{
++	struct ipu6_bus_device *adev = av->isys->adev;
++	struct ipu6_device *isp = adev->isp;
++	u64 delta, tsc_now;
++
++	ipu6_buttress_tsc_read(isp, &tsc_now);
++	if (!tsc_now)
++		return 0;
++
++	delta = tsc_now - ((u64)info->timestamp[1] << 32 | info->timestamp[0]);
++
++	return ipu6_buttress_tsc_ticks_to_ns(delta, isp);
++}
++
++void ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib,
++				      struct ipu6_fw_isys_resp_info_abi *info)
++{
++	struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct ipu6_isys_stream *stream = av->stream;
++	u64 ns;
++	u32 sequence;
++
++	ns = ktime_get_ns() - get_sof_ns_delta(av, info);
++	sequence = get_sof_sequence_by_timestamp(stream, info);
++
++	vbuf->vb2_buf.timestamp = ns;
++	vbuf->sequence = sequence;
++
++	dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n",
++		av->vdev.name, ktime_get_ns(), sequence);
++	dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index,
++		vbuf->vb2_buf.timestamp);
++}
++
++void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib)
++{
++	struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++
++	if (atomic_read(&ib->str2mmio_flag)) {
++		vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
++		/*
++		 * Operation on buffer is ended with error and will be reported
++		 * to the userspace when it is de-queued
++		 */
++		atomic_set(&ib->str2mmio_flag, 0);
++	} else {
++		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
++	}
++}
++
++void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream,
++			       struct ipu6_fw_isys_resp_info_abi *info)
++{
++	struct ipu6_isys_queue *aq = stream->output_pins[info->pin_id].aq;
++	struct ipu6_isys *isys = stream->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_isys_buffer *ib;
++	struct vb2_buffer *vb;
++	unsigned long flags;
++	bool first = true;
++	struct vb2_v4l2_buffer *buf;
++
++	spin_lock_irqsave(&aq->lock, flags);
++	if (list_empty(&aq->active)) {
++		spin_unlock_irqrestore(&aq->lock, flags);
++		dev_err(dev, "active queue empty\n");
++		return;
++	}
++
++	list_for_each_entry_reverse(ib, &aq->active, head) {
++		dma_addr_t addr;
++
++		vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++		addr = vb2_dma_contig_plane_dma_addr(vb, 0);
++
++		if (info->pin.addr != addr) {
++			if (first)
++				dev_err(dev, "Unexpected buffer address %pad\n",
++					&addr);
++			first = false;
++			continue;
++		}
++
++		if (info->error_info.error ==
++		    IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) {
++			/*
++			 * Check for error message:
++			 * 'IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO'
++			 */
++			atomic_set(&ib->str2mmio_flag, 1);
++		}
++		dev_dbg(dev, "buffer: found buffer %pad\n", &addr);
++
++		buf = to_vb2_v4l2_buffer(vb);
++		buf->field = V4L2_FIELD_NONE;
++
++		list_del(&ib->head);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		ipu6_isys_buf_calc_sequence_time(ib, info);
++
++		ipu6_isys_queue_buf_done(ib);
++
++		return;
++	}
++
++	dev_err(dev, "Failed to find a matching video buffer");
++
++	spin_unlock_irqrestore(&aq->lock, flags);
++}
++
++static const struct vb2_ops ipu6_isys_queue_ops = {
++	.queue_setup = queue_setup,
++	.wait_prepare = vb2_ops_wait_prepare,
++	.wait_finish = vb2_ops_wait_finish,
++	.buf_prepare = ipu6_isys_buf_prepare,
++	.start_streaming = start_streaming,
++	.stop_streaming = stop_streaming,
++	.buf_queue = buf_queue,
++};
++
++int ipu6_isys_queue_init(struct ipu6_isys_queue *aq)
++{
++	struct ipu6_isys *isys = ipu6_isys_queue_to_video(aq)->isys;
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	int ret;
++
++	/* no support for userptr */
++	if (!aq->vbq.io_modes)
++		aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF;
++
++	aq->vbq.drv_priv = aq;
++	aq->vbq.ops = &ipu6_isys_queue_ops;
++	aq->vbq.lock = &av->mutex;
++	aq->vbq.mem_ops = &vb2_dma_contig_memops;
++	aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
++	aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++
++	ret = vb2_queue_init(&aq->vbq);
++	if (ret)
++		return ret;
++
++	aq->dev = &isys->adev->auxdev.dev;
++	aq->vbq.dev = &isys->adev->auxdev.dev;
++	spin_lock_init(&aq->lock);
++	INIT_LIST_HEAD(&aq->active);
++	INIT_LIST_HEAD(&aq->incoming);
++
++	return 0;
++}
++
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
+new file mode 100644
+index 000000000000..9fb454577bb5
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
+@@ -0,0 +1,76 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_QUEUE_H
++#define IPU6_ISYS_QUEUE_H
++
++#include <linux/container_of.h>
++#include <linux/atomic.h>
++#include <linux/device.h>
++#include <linux/list.h>
++#include <linux/spinlock_types.h>
++
++#include <media/videobuf2-v4l2.h>
++
++#include "ipu6-fw-isys.h"
++#include "ipu6-isys-video.h"
++
++struct ipu6_isys_queue {
++	struct vb2_queue vbq;
++	struct list_head node;
++	struct device *dev;
++	/*
++	 * @lock: serialise access to queued and pre_streamon_queued
++	 */
++	spinlock_t lock;
++	struct list_head active;
++	struct list_head incoming;
++	unsigned int fw_output;
++};
++
++struct ipu6_isys_buffer {
++	struct list_head head;
++	atomic_t str2mmio_flag;
++};
++
++struct ipu6_isys_video_buffer {
++	struct vb2_v4l2_buffer vb_v4l2;
++	struct ipu6_isys_buffer ib;
++};
++
++#define IPU6_ISYS_BUFFER_LIST_FL_INCOMING	BIT(0)
++#define IPU6_ISYS_BUFFER_LIST_FL_ACTIVE	BIT(1)
++#define IPU6_ISYS_BUFFER_LIST_FL_SET_STATE	BIT(2)
++
++struct ipu6_isys_buffer_list {
++	struct list_head head;
++	unsigned int nbufs;
++};
++
++#define vb2_queue_to_isys_queue(__vb2) \
++	container_of(__vb2, struct ipu6_isys_queue, vbq)
++
++#define ipu6_isys_to_isys_video_buffer(__ib) \
++	container_of(__ib, struct ipu6_isys_video_buffer, ib)
++
++#define vb2_buffer_to_ipu6_isys_video_buffer(__vvb) \
++	container_of(__vvb, struct ipu6_isys_video_buffer, vb_v4l2)
++
++#define ipu6_isys_buffer_to_vb2_buffer(__ib) \
++	(&ipu6_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf)
++
++void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl,
++				 unsigned long op_flags,
++				 enum vb2_buffer_state state);
++void
++ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set,
++			      struct ipu6_isys_stream *stream,
++			      struct ipu6_isys_buffer_list *bl);
++void
++ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib,
++				 struct ipu6_fw_isys_resp_info_abi *info);
++void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib);
++void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream,
++			       struct ipu6_fw_isys_resp_info_abi *info);
++int ipu6_isys_queue_init(struct ipu6_isys_queue *aq);
++#endif /* IPU6_ISYS_QUEUE_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+new file mode 100644
+index 000000000000..847eac26bcd6
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+@@ -0,0 +1,1253 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/align.h>
++#include <linux/bits.h>
++#include <linux/bug.h>
++#include <linux/completion.h>
++#include <linux/container_of.h>
++#include <linux/device.h>
++#include <linux/list.h>
++#include <linux/math64.h>
++#include <linux/minmax.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/pm_runtime.h>
++#include <linux/spinlock.h>
++#include <linux/string.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-dev.h>
++#include <media/v4l2-fh.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-subdev.h>
++#include <media/videobuf2-v4l2.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-cpd.h"
++#include "ipu6-fw-isys.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-isys-queue.h"
++#include "ipu6-isys-video.h"
++#include "ipu6-platform-regs.h"
++
++const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
++	{V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
++	{V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
++	{V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
++	{V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
++	{V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
++	{V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
++	{V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
++	{V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
++	{V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
++	{V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_YUYV},
++	{V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RGB565},
++	{V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RGBA888},
++};
++
++static int video_open(struct file *file)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++	struct ipu6_isys *isys = av->isys;
++	struct ipu6_bus_device *adev = isys->adev;
++
++	mutex_lock(&isys->mutex);
++	if (isys->need_reset) {
++		mutex_unlock(&isys->mutex);
++		dev_warn(&adev->auxdev.dev, "isys power cycle required\n");
++		return -EIO;
++	}
++	mutex_unlock(&isys->mutex);
++
++	return v4l2_fh_open(file);
++}
++
++static int video_release(struct file *file)
++{
++	return vb2_fop_release(file);
++}
++
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_get_pixelformat(u32 pixelformat)
++{
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
++		const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i];
++
++		if (pfmt->pixelformat == pixelformat)
++			return pfmt;
++	}
++
++	return &ipu6_isys_pfmts[0];
++}
++
++int ipu6_isys_vidioc_querycap(struct file *file, void *fh,
++			      struct v4l2_capability *cap)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++
++	strscpy(cap->driver, IPU6_ISYS_NAME, sizeof(cap->driver));
++	strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card));
++
++	return 0;
++}
++
++int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,
++			      struct v4l2_fmtdesc *f)
++{
++	unsigned int i, found = 0;
++
++	if (f->index >= ARRAY_SIZE(ipu6_isys_pfmts))
++		return -EINVAL;
++
++	if (!f->mbus_code) {
++		f->flags = 0;
++		f->pixelformat = ipu6_isys_pfmts[f->index].pixelformat;
++		return 0;
++	}
++
++	for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
++		if (f->mbus_code != ipu6_isys_pfmts[i].code)
++			continue;
++
++		if (f->index == found) {
++			f->flags = 0;
++			f->pixelformat = ipu6_isys_pfmts[i].pixelformat;
++			return 0;
++		}
++		found++;
++	}
++
++	return -EINVAL;
++}
++
++static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh,
++					    struct v4l2_frmsizeenum *fsize)
++{
++	if (fsize->index > 0)
++		return -EINVAL;
++
++	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
++	fsize->stepwise.min_width = IPU6_ISYS_MIN_WIDTH;
++	fsize->stepwise.max_width = IPU6_ISYS_MAX_WIDTH;
++	fsize->stepwise.min_height = IPU6_ISYS_MIN_HEIGHT;
++	fsize->stepwise.max_height = IPU6_ISYS_MAX_HEIGHT;
++	fsize->stepwise.step_width = 2;
++	fsize->stepwise.step_height = 2;
++
++	return 0;
++}
++
++static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *fh,
++				       struct v4l2_format *fmt)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++
++	fmt->fmt.pix_mp = av->mpix;
++
++	return 0;
++}
++
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_video_try_fmt_vid_mplane(struct ipu6_isys_video *av,
++				   struct v4l2_pix_format_mplane *mpix)
++{
++	const struct ipu6_isys_pixelformat *pfmt =
++		ipu6_isys_get_pixelformat(mpix->pixelformat);
++
++	mpix->pixelformat = pfmt->pixelformat;
++	mpix->num_planes = 1;
++
++	mpix->width = clamp(mpix->width, IPU6_ISYS_MIN_WIDTH,
++			    IPU6_ISYS_MAX_WIDTH);
++	mpix->height = clamp(mpix->height, IPU6_ISYS_MIN_HEIGHT,
++			     IPU6_ISYS_MAX_HEIGHT);
++
++	if (pfmt->bpp != pfmt->bpp_packed)
++		mpix->plane_fmt[0].bytesperline =
++			mpix->width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);
++	else
++		mpix->plane_fmt[0].bytesperline =
++			DIV_ROUND_UP((unsigned int)mpix->width * pfmt->bpp,
++				     BITS_PER_BYTE);
++
++	mpix->plane_fmt[0].bytesperline = ALIGN(mpix->plane_fmt[0].bytesperline,
++						av->isys->line_align);
++
++	/*
++	 * (height + 1) * bytesperline due to a hardware issue: the DMA unit
++	 * is a power of two, and a line should be transferred as few units
++	 * as possible. The result is that up to line length more data than
++	 * the image size may be transferred to memory after the image.
++	 * Another limitation is the GDA allocation unit size. For low
++	 * resolution it gives a bigger number. Use larger one to avoid
++	 * memory corruption.
++	 */
++	mpix->plane_fmt[0].sizeimage =
++		max(mpix->plane_fmt[0].sizeimage,
++		    mpix->plane_fmt[0].bytesperline * mpix->height +
++		    max(mpix->plane_fmt[0].bytesperline,
++			av->isys->pdata->ipdata->isys_dma_overshoot));
++
++	memset(mpix->plane_fmt[0].reserved, 0,
++	       sizeof(mpix->plane_fmt[0].reserved));
++
++	mpix->field = V4L2_FIELD_NONE;
++
++	mpix->colorspace = V4L2_COLORSPACE_RAW;
++	mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
++	mpix->quantization = V4L2_QUANTIZATION_DEFAULT;
++	mpix->xfer_func = V4L2_XFER_FUNC_DEFAULT;
++
++	return pfmt;
++}
++
++static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *fh,
++				       struct v4l2_format *f)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++
++	if (av->aq.vbq.streaming)
++		return -EBUSY;
++
++	av->pfmt = ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
++	av->mpix = f->fmt.pix_mp;
++
++	return 0;
++}
++
++static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *fh,
++					 struct v4l2_format *f)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++
++	ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
++
++	return 0;
++}
++
++static int link_validate(struct media_link *link)
++{
++	struct ipu6_isys_video *av =
++		container_of(link->sink, struct ipu6_isys_video, pad);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct v4l2_subdev_state *s_state;
++	struct v4l2_subdev *s_sd;
++	struct v4l2_mbus_framefmt *s_fmt;
++	struct media_pad *s_pad;
++	u32 s_stream;
++	int ret = -EPIPE;
++
++	if (!link->source->entity)
++		return ret;
++
++	s_sd = media_entity_to_v4l2_subdev(link->source->entity);
++	s_state = v4l2_subdev_get_unlocked_active_state(s_sd);
++	if (!s_state)
++		return ret;
++
++	dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n",
++		link->source->entity->name, link->source->index,
++		link->sink->entity->name);
++
++	s_pad = media_pad_remote_pad_first(&av->pad);
++	s_stream = ipu6_isys_get_src_stream_by_src_pad(s_sd, s_pad->index);
++
++	v4l2_subdev_lock_state(s_state);
++
++	s_fmt = v4l2_subdev_state_get_stream_format(s_state, s_pad->index,
++						    s_stream);
++	if (!s_fmt) {
++		dev_err(dev, "failed to get source pad format\n");
++		goto unlock;
++	}
++
++	if (s_fmt->width != av->mpix.width ||
++	    s_fmt->height != av->mpix.height || s_fmt->code != av->pfmt->code) {
++		dev_err(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",
++			s_fmt->width, s_fmt->height, s_fmt->code,
++			av->mpix.width, av->mpix.height, av->pfmt->code);
++		goto unlock;
++	}
++
++	v4l2_subdev_unlock_state(s_state);
++
++	return 0;
++unlock:
++	v4l2_subdev_unlock_state(s_state);
++
++	return ret;
++}
++
++static void get_stream_opened(struct ipu6_isys_video *av)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&av->isys->streams_lock, flags);
++	av->isys->stream_opened++;
++	spin_unlock_irqrestore(&av->isys->streams_lock, flags);
++}
++
++static void put_stream_opened(struct ipu6_isys_video *av)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&av->isys->streams_lock, flags);
++	av->isys->stream_opened--;
++	spin_unlock_irqrestore(&av->isys->streams_lock, flags);
++}
++
++static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
++				struct ipu6_fw_isys_stream_cfg_data_abi *cfg)
++{
++	struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad);
++	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
++	struct ipu6_fw_isys_input_pin_info_abi *input_pin;
++	struct ipu6_fw_isys_output_pin_info_abi *output_pin;
++	struct ipu6_isys_stream *stream = av->stream;
++	struct ipu6_isys_queue *aq = &av->aq;
++	struct v4l2_mbus_framefmt fmt;
++	struct v4l2_rect v4l2_crop;
++	struct ipu6_isys *isys = av->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	int input_pins = cfg->nof_input_pins++;
++	int output_pins;
++	u32 src_stream;
++	int ret;
++
++	src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index);
++	ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream,
++					   &fmt);
++	if (ret < 0) {
++		dev_err(dev, "can't get stream format (%d)\n", ret);
++		return ret;
++	}
++
++	ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream,
++					    &v4l2_crop);
++	if (ret < 0) {
++		dev_err(dev, "can't get stream crop (%d)\n", ret);
++		return ret;
++	}
++
++	input_pin = &cfg->input_pins[input_pins];
++	input_pin->input_res.width = fmt.width;
++	input_pin->input_res.height = fmt.height;
++	input_pin->dt = av->dt;
++	input_pin->bits_per_pix = av->pfmt->bpp_packed;
++	input_pin->mapped_dt = 0x40; /* invalid mipi data type */
++	input_pin->mipi_decompression = 0;
++	input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR;
++	input_pin->mipi_store_mode = av->pfmt->bpp == av->pfmt->bpp_packed ?
++		IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER :
++		IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL;
++	input_pin->crop_first_and_last_lines = v4l2_crop.top & 1;
++
++	output_pins = cfg->nof_output_pins++;
++	aq->fw_output = output_pins;
++	stream->output_pins[output_pins].pin_ready = ipu6_isys_queue_buf_ready;
++	stream->output_pins[output_pins].aq = aq;
++
++	output_pin = &cfg->output_pins[output_pins];
++	output_pin->input_pin_id = input_pins;
++	output_pin->output_res.width = av->mpix.width;
++	output_pin->output_res.height = av->mpix.height;
++
++	output_pin->stride = av->mpix.plane_fmt[0].bytesperline;
++	if (av->pfmt->bpp != av->pfmt->bpp_packed)
++		output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC;
++	else
++		output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI;
++	output_pin->ft = av->pfmt->css_pixelformat;
++	output_pin->send_irq = 1;
++	memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets));
++	output_pin->s2m_pixel_soc_pixel_remapping =
++		S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
++	output_pin->csi_be_soc_pixel_remapping =
++		CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
++
++	output_pin->snoopable = true;
++	output_pin->error_handling_enable = false;
++	output_pin->sensor_type = isys->sensor_type++;
++	if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end)
++		isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
++
++	return 0;
++}
++
++static int start_stream_firmware(struct ipu6_isys_video *av,
++				 struct ipu6_isys_buffer_list *bl)
++{
++	struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg;
++	struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
++	struct ipu6_isys_stream *stream = av->stream;
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct isys_fw_msgs *msg = NULL;
++	struct ipu6_isys_queue *aq;
++	int ret, retout, tout;
++	u16 send_type;
++
++	msg = ipu6_get_fw_msg_buf(stream);
++	if (!msg)
++		return -ENOMEM;
++
++	stream_cfg = &msg->fw_msg.stream;
++	stream_cfg->src = stream->stream_source;
++	stream_cfg->vc = stream->vc;
++	stream_cfg->isl_use = 0;
++	stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL;
++
++	list_for_each_entry(aq, &stream->queues, node) {
++		struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq);
++
++		ret = ipu6_isys_fw_pin_cfg(__av, stream_cfg);
++		if (ret < 0) {
++			ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
++			return ret;
++		}
++	}
++
++	ipu6_fw_isys_dump_stream_cfg(dev, stream_cfg);
++
++	stream->nr_output_pins = stream_cfg->nof_output_pins;
++
++	reinit_completion(&stream->stream_open_completion);
++
++	ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
++				       stream_cfg, msg->dma_addr,
++				       sizeof(*stream_cfg),
++				       IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN);
++	if (ret < 0) {
++		dev_err(dev, "can't open stream (%d)\n", ret);
++		ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
++		return ret;
++	}
++
++	get_stream_opened(av);
++
++	tout = wait_for_completion_timeout(&stream->stream_open_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++
++	ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
++
++	if (!tout) {
++		dev_err(dev, "stream open time out\n");
++		ret = -ETIMEDOUT;
++		goto out_put_stream_opened;
++	}
++	if (stream->error) {
++		dev_err(dev, "stream open error: %d\n", stream->error);
++		ret = -EIO;
++		goto out_put_stream_opened;
++	}
++	dev_dbg(dev, "start stream: open complete\n");
++
++	if (bl) {
++		msg = ipu6_get_fw_msg_buf(stream);
++		if (!msg) {
++			ret = -ENOMEM;
++			goto out_put_stream_opened;
++		}
++		buf = &msg->fw_msg.frame;
++		ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);
++		ipu6_isys_buffer_list_queue(bl,
++					    IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
++	}
++
++	reinit_completion(&stream->stream_start_completion);
++
++	if (bl) {
++		send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE;
++		ipu6_fw_isys_dump_frame_buff_set(dev, buf,
++						 stream_cfg->nof_output_pins);
++		ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
++					       buf, msg->dma_addr,
++					       sizeof(*buf), send_type);
++	} else {
++		send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START;
++		ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
++					      send_type);
++	}
++
++	if (ret < 0) {
++		dev_err(dev, "can't start streaming (%d)\n", ret);
++		goto out_stream_close;
++	}
++
++	tout = wait_for_completion_timeout(&stream->stream_start_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++	if (!tout) {
++		dev_err(dev, "stream start time out\n");
++		ret = -ETIMEDOUT;
++		goto out_stream_close;
++	}
++	if (stream->error) {
++		dev_err(dev, "stream start error: %d\n", stream->error);
++		ret = -EIO;
++		goto out_stream_close;
++	}
++	dev_dbg(dev, "start stream: complete\n");
++
++	return 0;
++
++out_stream_close:
++	reinit_completion(&stream->stream_close_completion);
++
++	retout = ipu6_fw_isys_simple_cmd(av->isys,
++					 stream->stream_handle,
++					 IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
++	if (retout < 0) {
++		dev_dbg(dev, "can't close stream (%d)\n", retout);
++		goto out_put_stream_opened;
++	}
++
++	tout = wait_for_completion_timeout(&stream->stream_close_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++	if (!tout)
++		dev_err(dev, "stream close time out\n");
++	else if (stream->error)
++		dev_err(dev, "stream close error: %d\n", stream->error);
++	else
++		dev_dbg(dev, "stream close complete\n");
++
++out_put_stream_opened:
++	put_stream_opened(av);
++
++	return ret;
++}
++
++static void stop_streaming_firmware(struct ipu6_isys_video *av)
++{
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct ipu6_isys_stream *stream = av->stream;
++	int ret, tout;
++
++	reinit_completion(&stream->stream_stop_completion);
++
++	ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
++				      IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH);
++
++	if (ret < 0) {
++		dev_err(dev, "can't stop stream (%d)\n", ret);
++		return;
++	}
++
++	tout = wait_for_completion_timeout(&stream->stream_stop_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++	if (!tout)
++		dev_warn(dev, "stream stop time out\n");
++	else if (stream->error)
++		dev_warn(dev, "stream stop error: %d\n", stream->error);
++	else
++		dev_dbg(dev, "stop stream: complete\n");
++}
++
++static void close_streaming_firmware(struct ipu6_isys_video *av)
++{
++	struct ipu6_isys_stream *stream = av->stream;
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	int ret, tout;
++
++	reinit_completion(&stream->stream_close_completion);
++
++	ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
++				      IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
++	if (ret < 0) {
++		dev_err(dev, "can't close stream (%d)\n", ret);
++		return;
++	}
++
++	tout = wait_for_completion_timeout(&stream->stream_close_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++	if (!tout)
++		dev_warn(dev, "stream close time out\n");
++	else if (stream->error)
++		dev_warn(dev, "stream close error: %d\n", stream->error);
++	else
++		dev_dbg(dev, "close stream: complete\n");
++
++	put_stream_opened(av);
++}
++
++int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
++				   struct media_entity *source_entity,
++				   int nr_queues)
++{
++	struct ipu6_isys_stream *stream = av->stream;
++	struct ipu6_isys_csi2 *csi2;
++
++	if (WARN_ON(stream->nr_streaming))
++		return -EINVAL;
++
++	stream->nr_queues = nr_queues;
++	atomic_set(&stream->sequence, 0);
++
++	stream->seq_index = 0;
++	memset(stream->seq, 0, sizeof(stream->seq));
++
++	if (WARN_ON(!list_empty(&stream->queues)))
++		return -EINVAL;
++
++	stream->stream_source = stream->asd->source;
++	csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
++	csi2->receiver_errors = 0;
++	stream->source_entity = source_entity;
++
++	dev_dbg(&av->isys->adev->auxdev.dev,
++		"prepare stream: external entity %s\n",
++		stream->source_entity->name);
++
++	return 0;
++}
++
++void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
++					  bool state)
++{
++	struct ipu6_isys *isys = av->isys;
++	struct ipu6_isys_csi2 *csi2 = NULL;
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct v4l2_mbus_framefmt format;
++	struct v4l2_subdev *esd;
++	struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 };
++	unsigned int bpp, lanes;
++	s64 link_freq = 0;
++	u64 pixel_rate = 0;
++	int ret;
++
++	if (!state)
++		return;
++
++	esd = media_entity_to_v4l2_subdev(av->stream->source_entity);
++
++	av->watermark.width = av->mpix.width;
++	av->watermark.height = av->mpix.height;
++	av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift;
++	av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size;
++
++	ret = v4l2_g_ctrl(esd->ctrl_handler, &hb);
++	if (!ret && hb.value >= 0)
++		av->watermark.hblank = hb.value;
++	else
++		av->watermark.hblank = 0;
++
++	csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd);
++	link_freq = ipu6_isys_csi2_get_link_freq(csi2);
++	if (link_freq > 0) {
++		lanes = csi2->nlanes;
++		ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0,
++						   av->source_stream, &format);
++		if (!ret) {
++			bpp = ipu6_isys_mbus_code_to_bpp(format.code);
++			pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp);
++		}
++	}
++
++	av->watermark.pixel_rate = pixel_rate;
++
++	if (!pixel_rate) {
++		mutex_lock(&iwake_watermark->mutex);
++		iwake_watermark->force_iwake_disable = true;
++		mutex_unlock(&iwake_watermark->mutex);
++		dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n",
++			 av->stream->source_entity->name);
++	}
++}
++
++static void calculate_stream_datarate(struct ipu6_isys_video *av)
++{
++	struct video_stream_watermark *watermark = &av->watermark;
++	u32 bpp = av->pfmt->bpp;
++	u32 pages_per_line, pb_bytes_per_line, pixels_per_line, bytes_per_line;
++	u64 line_time_ns, stream_data_rate;
++	u16 shift, size;
++
++	shift = watermark->sram_gran_shift;
++	size = watermark->sram_gran_size;
++
++	pixels_per_line = watermark->width + watermark->hblank;
++	line_time_ns =  div_u64(pixels_per_line * NSEC_PER_SEC,
++				watermark->pixel_rate);
++	bytes_per_line = watermark->width * bpp / 8;
++	pages_per_line = DIV_ROUND_UP(bytes_per_line, size);
++	pb_bytes_per_line = pages_per_line << shift;
++	stream_data_rate = div64_u64(pb_bytes_per_line * 1000, line_time_ns);
++
++	watermark->stream_data_rate = stream_data_rate;
++}
++
++void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state)
++{
++	struct isys_iwake_watermark *iwake_watermark =
++		&av->isys->iwake_watermark;
++
++	if (!av->watermark.pixel_rate)
++		return;
++
++	if (state) {
++		calculate_stream_datarate(av);
++		mutex_lock(&iwake_watermark->mutex);
++		list_add(&av->watermark.stream_node,
++			 &iwake_watermark->video_list);
++		mutex_unlock(&iwake_watermark->mutex);
++	} else {
++		av->watermark.stream_data_rate = 0;
++		mutex_lock(&iwake_watermark->mutex);
++		list_del(&av->watermark.stream_node);
++		mutex_unlock(&iwake_watermark->mutex);
++	}
++
++	update_watermark_setting(av->isys);
++}
++
++void ipu6_isys_put_stream(struct ipu6_isys_stream *stream)
++{
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	unsigned int i;
++	unsigned long flags;
++
++	if (!stream) {
++		dev_err(dev, "no available stream\n");
++		return;
++	}
++
++	spin_lock_irqsave(&stream->isys->streams_lock, flags);
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++		if (&stream->isys->streams[i] == stream) {
++			if (stream->isys->streams_ref_count[i] > 0)
++				stream->isys->streams_ref_count[i]--;
++			else
++				dev_warn(dev, "invalid stream %d\n", i);
++
++			break;
++		}
++	}
++	spin_unlock_irqrestore(&stream->isys->streams_lock, flags);
++}
++
++static struct ipu6_isys_stream *
++ipu6_isys_get_stream(struct ipu6_isys_video *av, struct ipu6_isys_subdev *asd)
++{
++	struct ipu6_isys_stream *stream = NULL;
++	struct ipu6_isys *isys = av->isys;
++	unsigned long flags;
++	unsigned int i;
++	u8 vc = av->vc;
++
++	if (!isys)
++		return NULL;
++
++	spin_lock_irqsave(&isys->streams_lock, flags);
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++		if (isys->streams_ref_count[i] && isys->streams[i].vc == vc &&
++		    isys->streams[i].asd == asd) {
++			isys->streams_ref_count[i]++;
++			stream = &isys->streams[i];
++			break;
++		}
++	}
++
++	if (!stream) {
++		for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++			if (!isys->streams_ref_count[i]) {
++				isys->streams_ref_count[i]++;
++				stream = &isys->streams[i];
++				stream->vc = vc;
++				stream->asd = asd;
++				break;
++			}
++		}
++	}
++	spin_unlock_irqrestore(&isys->streams_lock, flags);
++
++	return stream;
++}
++
++struct ipu6_isys_stream *
++ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle)
++{
++	unsigned long flags;
++	struct ipu6_isys_stream *stream = NULL;
++
++	if (!isys)
++		return NULL;
++
++	if (stream_handle >= IPU6_ISYS_MAX_STREAMS) {
++		dev_err(&isys->adev->auxdev.dev,
++			"stream_handle %d is invalid\n", stream_handle);
++		return NULL;
++	}
++
++	spin_lock_irqsave(&isys->streams_lock, flags);
++	if (isys->streams_ref_count[stream_handle] > 0) {
++		isys->streams_ref_count[stream_handle]++;
++		stream = &isys->streams[stream_handle];
++	}
++	spin_unlock_irqrestore(&isys->streams_lock, flags);
++
++	return stream;
++}
++
++struct ipu6_isys_stream *
++ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc)
++{
++	struct ipu6_isys_stream *stream = NULL;
++	unsigned long flags;
++	unsigned int i;
++
++	if (!isys)
++		return NULL;
++
++	if (source < 0) {
++		dev_err(&stream->isys->adev->auxdev.dev,
++			"query stream with invalid port number\n");
++		return NULL;
++	}
++
++	spin_lock_irqsave(&isys->streams_lock, flags);
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++		if (!isys->streams_ref_count[i])
++			continue;
++
++		if (isys->streams[i].stream_source == source &&
++		    isys->streams[i].vc == vc) {
++			stream = &isys->streams[i];
++			isys->streams_ref_count[i]++;
++			break;
++		}
++	}
++	spin_unlock_irqrestore(&isys->streams_lock, flags);
++
++	return stream;
++}
++
++static u64 get_stream_mask_by_pipeline(struct ipu6_isys_video *av)
++{
++	struct media_pipeline *pipeline =
++		media_entity_pipeline(&av->vdev.entity);
++	struct media_entity *entity;
++	unsigned int i;
++	u64 stream_mask = 0;
++
++	for (i = 0; i < NR_OF_VIDEO_DEVICE; i++) {
++		entity = &av->isys->av[i].vdev.entity;
++		if (pipeline == media_entity_pipeline(entity))
++			stream_mask |= BIT_ULL(av->isys->av[i].source_stream);
++	}
++
++	return stream_mask;
++}
++
++int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
++				  struct ipu6_isys_buffer_list *bl)
++{
++	struct v4l2_subdev_krouting *routing;
++	struct ipu6_isys_stream *stream = av->stream;
++	struct v4l2_subdev_state *subdev_state;
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct v4l2_subdev *sd = NULL;
++	struct v4l2_subdev *ssd = NULL;
++	struct media_pad *r_pad;
++	struct media_pad *s_pad = NULL;
++	u32 sink_pad, sink_stream;
++	u64 r_stream;
++	u64 stream_mask = 0;
++	int ret = 0;
++
++	dev_dbg(dev, "set stream: %d\n", state);
++
++	if (WARN(!stream->source_entity, "No source entity for stream\n"))
++		return -ENODEV;
++
++	ssd = media_entity_to_v4l2_subdev(stream->source_entity);
++	sd = &stream->asd->sd;
++	r_pad = media_pad_remote_pad_first(&av->pad);
++	r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
++
++	subdev_state = v4l2_subdev_lock_and_get_active_state(sd);
++	routing = &subdev_state->routing;
++	ret = v4l2_subdev_routing_find_opposite_end(routing, r_pad->index,
++						    r_stream, &sink_pad,
++						    &sink_stream);
++	v4l2_subdev_unlock_state(subdev_state);
++	if (ret)
++		return ret;
++
++	s_pad = media_pad_remote_pad_first(&stream->asd->pad[sink_pad]);
++
++	stream_mask = get_stream_mask_by_pipeline(av);
++	if (!state) {
++		stop_streaming_firmware(av);
++
++		/* stop external sub-device now. */
++		dev_dbg(dev, "disable streams 0x%llx of %s\n", stream_mask,
++			ssd->name);
++		ret = v4l2_subdev_disable_streams(ssd, s_pad->index,
++						  stream_mask);
++		if (ret) {
++			dev_err(dev, "disable streams of %s failed with %d\n",
++				ssd->name, ret);
++			return ret;
++		}
++
++		/* stop sub-device which connects with video */
++		dev_dbg(dev, "stream off entity %s pad:%d\n", sd->name,
++			r_pad->index);
++		ret = v4l2_subdev_call(sd, video, s_stream, state);
++		if (ret) {
++			dev_err(dev, "stream off %s failed with %d\n", sd->name,
++				ret);
++			return ret;
++		}
++		close_streaming_firmware(av);
++	} else {
++		ret = start_stream_firmware(av, bl);
++		if (ret) {
++			dev_err(dev, "start stream of firmware failed\n");
++			goto out_clear_stream_watermark;
++		}
++
++		/* start sub-device which connects with video */
++		dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index);
++		ret = v4l2_subdev_call(sd, video, s_stream, state);
++		if (ret) {
++			dev_err(dev, "stream on %s failed with %d\n", sd->name,
++				ret);
++			goto out_media_entity_stop_streaming_firmware;
++		}
++
++		/* start external sub-device now. */
++		dev_dbg(dev, "enable streams 0x%llx of %s\n", stream_mask,
++			ssd->name);
++		ret = v4l2_subdev_enable_streams(ssd, s_pad->index,
++						 stream_mask);
++		if (ret) {
++			dev_err(dev,
++				"enable streams 0x%llx of %s failed with %d\n",
++				stream_mask, stream->source_entity->name, ret);
++			goto out_media_entity_stop_streaming;
++		}
++	}
++
++	av->streaming = state;
++
++	return 0;
++
++out_media_entity_stop_streaming:
++	v4l2_subdev_disable_streams(sd, r_pad->index, BIT(r_stream));
++
++out_media_entity_stop_streaming_firmware:
++	stop_streaming_firmware(av);
++
++out_clear_stream_watermark:
++	ipu6_isys_update_stream_watermark(av, 0);
++
++	return ret;
++}
++
++static const struct v4l2_ioctl_ops ioctl_ops_mplane = {
++	.vidioc_querycap = ipu6_isys_vidioc_querycap,
++	.vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt,
++	.vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes,
++	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane,
++	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane,
++	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane,
++	.vidioc_reqbufs = vb2_ioctl_reqbufs,
++	.vidioc_create_bufs = vb2_ioctl_create_bufs,
++	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++	.vidioc_querybuf = vb2_ioctl_querybuf,
++	.vidioc_qbuf = vb2_ioctl_qbuf,
++	.vidioc_dqbuf = vb2_ioctl_dqbuf,
++	.vidioc_streamon = vb2_ioctl_streamon,
++	.vidioc_streamoff = vb2_ioctl_streamoff,
++	.vidioc_expbuf = vb2_ioctl_expbuf,
++};
++
++static const struct media_entity_operations entity_ops = {
++	.link_validate = link_validate,
++};
++
++static const struct v4l2_file_operations isys_fops = {
++	.owner = THIS_MODULE,
++	.poll = vb2_fop_poll,
++	.unlocked_ioctl = video_ioctl2,
++	.mmap = vb2_fop_mmap,
++	.open = video_open,
++	.release = video_release,
++};
++
++int ipu6_isys_fw_open(struct ipu6_isys *isys)
++{
++	struct ipu6_bus_device *adev = isys->adev;
++	const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata;
++	int ret;
++
++	ret = pm_runtime_resume_and_get(&adev->auxdev.dev);
++	if (ret < 0)
++		return ret;
++
++	mutex_lock(&isys->mutex);
++
++	if (isys->ref_count++)
++		goto unlock;
++
++	ipu6_configure_spc(adev->isp, &ipdata->hw_variant,
++			   IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base,
++			   adev->pkg_dir, adev->pkg_dir_dma_addr);
++
++	/*
++	 * Buffers could have been left to wrong queue at last closure.
++	 * Move them now back to empty buffer queue.
++	 */
++	ipu6_cleanup_fw_msg_bufs(isys);
++
++	if (isys->fwcom) {
++		/*
++		 * Something went wrong in previous shutdown. As we are now
++		 * restarting isys we can safely delete old context.
++		 */
++		dev_warn(&adev->auxdev.dev, "clearing old context\n");
++		ipu6_fw_isys_cleanup(isys);
++	}
++
++	ret = ipu6_fw_isys_init(isys, ipdata->num_parallel_streams);
++	if (ret < 0)
++		goto out;
++
++unlock:
++	mutex_unlock(&isys->mutex);
++
++	return 0;
++
++out:
++	isys->ref_count--;
++	mutex_unlock(&isys->mutex);
++	pm_runtime_put(&adev->auxdev.dev);
++
++	return ret;
++}
++
++void ipu6_isys_fw_close(struct ipu6_isys *isys)
++{
++	mutex_lock(&isys->mutex);
++
++	isys->ref_count--;
++	if (!isys->ref_count) {
++		ipu6_fw_isys_close(isys);
++		if (isys->fwcom) {
++			isys->need_reset = true;
++			dev_warn(&isys->adev->auxdev.dev,
++				 "failed to close fw isys\n");
++		}
++	}
++
++	mutex_unlock(&isys->mutex);
++
++	if (isys->need_reset)
++		pm_runtime_put_sync(&isys->adev->auxdev.dev);
++	else
++		pm_runtime_put(&isys->adev->auxdev.dev);
++}
++
++int ipu6_isys_setup_video(struct ipu6_isys_video *av,
++			  struct media_entity **source_entity, int *nr_queues)
++{
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct v4l2_mbus_frame_desc_entry entry;
++	struct v4l2_subdev_route *route = NULL;
++	struct v4l2_subdev_route *r;
++	struct v4l2_subdev_state *state;
++	struct ipu6_isys_subdev *asd;
++	struct v4l2_subdev *remote_sd;
++	struct media_pipeline *pipeline;
++	struct media_pad *source_pad, *remote_pad;
++	int ret = -EINVAL;
++
++	remote_pad = media_pad_remote_pad_first(&av->pad);
++	if (!remote_pad) {
++		dev_dbg(dev, "failed to get remote pad\n");
++		return -ENODEV;
++	}
++
++	remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
++	asd = to_ipu6_isys_subdev(remote_sd);
++	source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]);
++	if (!source_pad) {
++		dev_dbg(dev, "No external source entity\n");
++		return -ENODEV;
++	}
++
++	*source_entity = source_pad->entity;
++
++	/* Find the root */
++	state = v4l2_subdev_lock_and_get_active_state(remote_sd);
++	for_each_active_route(&state->routing, r) {
++		if (r->source_pad != remote_pad->index)
++			continue;
++
++		route = r;
++		break;
++	}
++
++	if (!route) {
++		v4l2_subdev_unlock_state(state);
++		dev_dbg(dev, "Failed to find route\n");
++		return -ENODEV;
++	}
++	v4l2_subdev_unlock_state(state);
++	av->source_stream = route->sink_stream;
++
++	ret = ipu6_isys_csi2_get_remote_desc(av->source_stream,
++					     to_ipu6_isys_csi2(asd),
++					     *source_entity, &entry,
++					     nr_queues);
++	if (ret == -ENOIOCTLCMD) {
++		av->vc = 0;
++		av->dt = ipu6_isys_mbus_code_to_mipi(av->pfmt->code);
++		*nr_queues = 1;
++	} else if (!ret) {
++		dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",
++			entry.stream, entry.length, entry.bus.csi2.vc,
++			entry.bus.csi2.dt);
++
++		av->vc = entry.bus.csi2.vc;
++		av->dt = entry.bus.csi2.dt;
++	} else {
++		dev_err(dev, "failed to get remote frame desc\n");
++		return ret;
++	}
++
++	pipeline = media_entity_pipeline(&av->vdev.entity);
++	if (!pipeline)
++		ret = video_device_pipeline_alloc_start(&av->vdev);
++	else
++		ret = video_device_pipeline_start(&av->vdev, pipeline);
++	if (ret < 0) {
++		dev_dbg(dev, "media pipeline start failed\n");
++		return ret;
++	}
++
++	av->stream = ipu6_isys_get_stream(av, asd);
++	if (!av->stream) {
++		video_device_pipeline_stop(&av->vdev);
++		dev_err(dev, "no available stream for firmware\n");
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++/*
++ * Do everything that's needed to initialise things related to video
++ * buffer queue, video node, and the related media entity. The caller
++ * is expected to assign isys field and set the name of the video
++ * device.
++ */
++int ipu6_isys_video_init(struct ipu6_isys_video *av)
++{
++	struct v4l2_format format = {
++		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++		.fmt.pix_mp = {
++			.width = 1920,
++			.height = 1080,
++		},
++	};
++	int ret;
++
++	mutex_init(&av->mutex);
++	av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |
++			       V4L2_CAP_VIDEO_CAPTURE_MPLANE;
++	av->vdev.vfl_dir = VFL_DIR_RX;
++
++	ret = ipu6_isys_queue_init(&av->aq);
++	if (ret)
++		goto out_free_watermark;
++
++	av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
++	ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad);
++	if (ret)
++		goto out_vb2_queue_release;
++
++	av->vdev.entity.ops = &entity_ops;
++	av->vdev.release = video_device_release_empty;
++	av->vdev.fops = &isys_fops;
++	av->vdev.v4l2_dev = &av->isys->v4l2_dev;
++	if (!av->vdev.ioctl_ops)
++		av->vdev.ioctl_ops = &ioctl_ops_mplane;
++	av->vdev.queue = &av->aq.vbq;
++	av->vdev.lock = &av->mutex;
++
++	ipu6_isys_video_try_fmt_vid_mplane(av, &format.fmt.pix_mp);
++	av->mpix = format.fmt.pix_mp;
++
++	set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags);
++	video_set_drvdata(&av->vdev, av);
++
++	ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1);
++	if (ret)
++		goto out_media_entity_cleanup;
++
++	return ret;
++
++out_media_entity_cleanup:
++	vb2_video_unregister_device(&av->vdev);
++	media_entity_cleanup(&av->vdev.entity);
++
++out_vb2_queue_release:
++	vb2_queue_release(&av->aq.vbq);
++
++out_free_watermark:
++	mutex_destroy(&av->mutex);
++
++	return ret;
++}
++
++void ipu6_isys_video_cleanup(struct ipu6_isys_video *av)
++{
++	vb2_video_unregister_device(&av->vdev);
++	media_entity_cleanup(&av->vdev.entity);
++	mutex_destroy(&av->mutex);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+new file mode 100644
+index 000000000000..21cd33c7e277
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+@@ -0,0 +1,136 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_VIDEO_H
++#define IPU6_ISYS_VIDEO_H
++
++#include <linux/atomic.h>
++#include <linux/completion.h>
++#include <linux/container_of.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-dev.h>
++
++#include "ipu6-isys-queue.h"
++
++#define IPU6_ISYS_OUTPUT_PINS 11
++#define IPU6_ISYS_MAX_PARALLEL_SOF 2
++#define NR_OF_VIDEO_DEVICE 31
++
++struct file;
++struct ipu6_isys;
++struct ipu6_isys_subdev;
++
++struct ipu6_isys_pixelformat {
++	u32 pixelformat;
++	u32 bpp;
++	u32 bpp_packed;
++	u32 code;
++	u32 css_pixelformat;
++};
++
++struct sequence_info {
++	unsigned int sequence;
++	u64 timestamp;
++};
++
++struct output_pin_data {
++	void (*pin_ready)(struct ipu6_isys_stream *stream,
++			  struct ipu6_fw_isys_resp_info_abi *info);
++	struct ipu6_isys_queue *aq;
++};
++
++/*
++ * Align with firmware stream. Each stream represents a CSI virtual channel.
++ * May map to multiple video devices
++ */
++struct ipu6_isys_stream {
++	struct mutex mutex;
++	struct media_entity *source_entity;
++	atomic_t sequence;
++	unsigned int seq_index;
++	struct sequence_info seq[IPU6_ISYS_MAX_PARALLEL_SOF];
++	int stream_source;
++	int stream_handle;
++	unsigned int nr_output_pins;
++	struct ipu6_isys_subdev *asd;
++
++	int nr_queues;	/* Number of capture queues */
++	int nr_streaming;
++	int streaming;	/* Has streaming been really started? */
++	struct list_head queues;
++	struct completion stream_open_completion;
++	struct completion stream_close_completion;
++	struct completion stream_start_completion;
++	struct completion stream_stop_completion;
++	struct ipu6_isys *isys;
++
++	struct output_pin_data output_pins[IPU6_ISYS_OUTPUT_PINS];
++	int error;
++	u8 vc;
++};
++
++struct video_stream_watermark {
++	u32 width;
++	u32 height;
++	u32 hblank;
++	u32 frame_rate;
++	u64 pixel_rate;
++	u64 stream_data_rate;
++	u16 sram_gran_shift;
++	u16 sram_gran_size;
++	struct list_head stream_node;
++};
++
++struct ipu6_isys_video {
++	struct ipu6_isys_queue aq;
++	/* Serialise access to other fields in the struct. */
++	struct mutex mutex;
++	struct media_pad pad;
++	struct video_device vdev;
++	struct v4l2_pix_format_mplane mpix;
++	const struct ipu6_isys_pixelformat *pfmt;
++	struct ipu6_isys *isys;
++	struct ipu6_isys_stream *stream;
++	unsigned int streaming;
++	struct video_stream_watermark watermark;
++	u32 source_stream;
++	u8 vc;
++	u8 dt;
++};
++
++#define ipu6_isys_queue_to_video(__aq) \
++	container_of(__aq, struct ipu6_isys_video, aq)
++
++extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts[];
++extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[];
++
++int ipu6_isys_vidioc_querycap(struct file *file, void *fh,
++			      struct v4l2_capability *cap);
++
++int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,
++			      struct v4l2_fmtdesc *f);
++int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
++				   struct media_entity *source_entity,
++				   int nr_queues);
++int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
++				  struct ipu6_isys_buffer_list *bl);
++int ipu6_isys_fw_open(struct ipu6_isys *isys);
++void ipu6_isys_fw_close(struct ipu6_isys *isys);
++int ipu6_isys_setup_video(struct ipu6_isys_video *av,
++			  struct media_entity **source_entity, int *nr_queues);
++int ipu6_isys_video_init(struct ipu6_isys_video *av);
++void ipu6_isys_video_cleanup(struct ipu6_isys_video *av);
++void ipu6_isys_put_stream(struct ipu6_isys_stream *stream);
++struct ipu6_isys_stream *
++ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle);
++struct ipu6_isys_stream *
++ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc);
++
++void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
++					  bool state);
++void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state);
++
++#endif /* IPU6_ISYS_VIDEO_H */
+-- 
+2.43.2
+
+
+From cc79447bab87ce8c498b0e7a5f849c7d4f6262c0 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:26 +0800
+Subject: [PATCH 19/33] media: add Kconfig and Makefile for IPU6
+
+Add IPU6 support in Kconfig and Makefile, with this patch you can
+build the Intel IPU6 and input system modules by select the
+CONFIG_VIDEO_INTEL_IPU6 in config.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+Signed-off-by: Andreas Helbech Kleist <andreaskleist@gmail.com>
+---
+ drivers/media/pci/intel/Kconfig       |  1 +
+ drivers/media/pci/intel/Makefile      |  1 +
+ drivers/media/pci/intel/ipu6/Kconfig  | 17 +++++++++++++++++
+ drivers/media/pci/intel/ipu6/Makefile | 23 +++++++++++++++++++++++
+ 4 files changed, 42 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/Kconfig
+ create mode 100644 drivers/media/pci/intel/ipu6/Makefile
+
+diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig
+index ee4684159d3d..04cb3d253486 100644
+--- a/drivers/media/pci/intel/Kconfig
++++ b/drivers/media/pci/intel/Kconfig
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ 
+ source "drivers/media/pci/intel/ipu3/Kconfig"
++source "drivers/media/pci/intel/ipu6/Kconfig"
+ source "drivers/media/pci/intel/ivsc/Kconfig"
+ 
+ config IPU_BRIDGE
+diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile
+index f199a97e1d78..3a2cc6567159 100644
+--- a/drivers/media/pci/intel/Makefile
++++ b/drivers/media/pci/intel/Makefile
+@@ -5,3 +5,4 @@
+ obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o
+ obj-y	+= ipu3/
+ obj-y	+= ivsc/
++obj-$(CONFIG_VIDEO_INTEL_IPU6)	+= ipu6/
+diff --git a/drivers/media/pci/intel/ipu6/Kconfig b/drivers/media/pci/intel/ipu6/Kconfig
+new file mode 100644
+index 000000000000..5cb4f3c2d59f
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/Kconfig
+@@ -0,0 +1,17 @@
++config VIDEO_INTEL_IPU6
++	tristate "Intel IPU6 driver"
++	depends on ACPI || COMPILE_TEST
++	depends on MEDIA_SUPPORT
++	depends on MEDIA_PCI_SUPPORT
++	depends on X86 && X86_64
++	select IOMMU_IOVA
++	select VIDEO_V4L2_SUBDEV_API
++	select VIDEOBUF2_DMA_CONTIG
++	select V4L2_FWNODE
++	select IPU_BRIDGE
++	help
++	  This is the 6th Gen Intel Image Processing Unit, found in Intel SoCs
++	  and used for capturing images and video from camera sensors.
++
++	  To compile this driver, say Y here! It contains 2 modules -
++	  intel_ipu6 and intel_ipu6_isys.
+diff --git a/drivers/media/pci/intel/ipu6/Makefile b/drivers/media/pci/intel/ipu6/Makefile
+new file mode 100644
+index 000000000000..a821b0a1567f
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/Makefile
+@@ -0,0 +1,23 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++intel-ipu6-y			:= ipu6.o \
++				ipu6-bus.o \
++				ipu6-dma.o \
++				ipu6-mmu.o \
++				ipu6-buttress.o \
++				ipu6-cpd.o \
++				ipu6-fw-com.o
++
++obj-$(CONFIG_VIDEO_INTEL_IPU6)	+= intel-ipu6.o
++
++intel-ipu6-isys-y		:= ipu6-isys.o \
++				ipu6-isys-csi2.o \
++				ipu6-fw-isys.o \
++				ipu6-isys-video.o \
++				ipu6-isys-queue.o \
++				ipu6-isys-subdev.o \
++				ipu6-isys-mcd-phy.o \
++				ipu6-isys-jsl-phy.o \
++				ipu6-isys-dwc-phy.o
++
++obj-$(CONFIG_VIDEO_INTEL_IPU6)	+= intel-ipu6-isys.o
+-- 
+2.43.2
+
+
+From edc6bed6991727e64f1eb60c0392403c39b96ba4 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:27 +0800
+Subject: [PATCH 20/33] MAINTAINERS: add maintainers for Intel IPU6 input
+ system driver
+
+Update MAINTAINERS file for Intel IPU6 input system driver.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ MAINTAINERS | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 1aabf1c15bb3..5346d472cb0f 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -10899,6 +10899,16 @@ F:	Documentation/admin-guide/media/ipu3_rcb.svg
+ F:	Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst
+ F:	drivers/staging/media/ipu3/
+ 
++INTEL IPU6 INPUT SYSTEM DRIVER
++M:	Sakari Ailus <sakari.ailus@linux.intel.com>
++M:	Bingbu Cao <bingbu.cao@intel.com>
++R:	Tianshu Qiu <tian.shu.qiu@intel.com>
++L:	linux-media@vger.kernel.org
++S:	Maintained
++T:	git git://linuxtv.org/media_tree.git
++F:	Documentation/admin-guide/media/ipu6-isys.rst
++F:	drivers/media/pci/intel/ipu6/
++
+ INTEL ISHTP ECLITE DRIVER
+ M:	Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com>
+ L:	platform-driver-x86@vger.kernel.org
+-- 
+2.43.2
+
+
+From a12041e5f7fb32b93669f19b579bc1940a026bbe Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:28 +0800
+Subject: [PATCH 21/33] Documentation: add Intel IPU6 ISYS driver admin-guide
+ doc
+
+This document mainly describe the functionality of IPU6 and
+IPU6 isys driver, and gives an example that how user can do
+imaging capture with tools.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ Documentation/admin-guide/media/ipu6-isys.rst | 158 ++++++++++++++++
+ .../admin-guide/media/ipu6_isys_graph.svg     | 174 ++++++++++++++++++
+ .../admin-guide/media/v4l-drivers.rst         |   1 +
+ 3 files changed, 333 insertions(+)
+ create mode 100644 Documentation/admin-guide/media/ipu6-isys.rst
+ create mode 100644 Documentation/admin-guide/media/ipu6_isys_graph.svg
+
+diff --git a/Documentation/admin-guide/media/ipu6-isys.rst b/Documentation/admin-guide/media/ipu6-isys.rst
+new file mode 100644
+index 000000000000..5e78ab88c649
+--- /dev/null
++++ b/Documentation/admin-guide/media/ipu6-isys.rst
+@@ -0,0 +1,158 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++.. include:: <isonum.txt>
++
++========================================================
++Intel Image Processing Unit 6 (IPU6) Input System driver
++========================================================
++
++Copyright |copy| 2023 Intel Corporation
++
++Introduction
++============
++
++This file documents the Intel IPU6 (6th generation Image Processing Unit)
++Input System (MIPI CSI2 receiver) drivers located under
++drivers/media/pci/intel/ipu6.
++
++The Intel IPU6 can be found in certain Intel Chipsets but not in all SKUs:
++
++* TigerLake
++* JasperLake
++* AlderLake
++* RaptorLake
++* MeteorLake
++
++Intel IPU6 is made up of two components - Input System (ISYS) and Processing
++System (PSYS).
++
++The Input System mainly works as MIPI CSI2 receiver which receives and
++processes the imaging data from the sensors and outputs the frames to memory.
++
++There are 2 driver modules - intel_ipu6 and intel_ipu6_isys. intel_ipu6 is an
++IPU6 common driver which does PCI configuration, firmware loading and parsing,
++firmware authentication, DMA mapping and IPU-MMU (internal Memory mapping Unit)
++configuration. intel_ipu6_isys implements V4L2, Media Controller and V4L2
++sub-device interfaces. The IPU6 ISYS driver supports camera sensors connected
++to the IPU6 ISYS through V4L2 sub-device sensor drivers.
++
++.. Note:: See Documentation/driver-api/media/drivers/ipu6.rst for more
++	  information about the IPU6 hardware.
++
++
++Input system driver
++===================
++
++The input System driver mainly configures CSI2 DPHY, constructs the firmware
++stream configuration, sends commands to firmware, gets response from hardware
++and firmware and then returns buffers to user.
++The ISYS is represented as several V4L2 sub-devices - 'Intel IPU6 CSI2 $port',
++which provide V4L2 subdev interfaces to the user space, there are also several
++video nodes for each CSI-2 stream capture - 'Intel IPU6 ISYS capture $num' which
++provide interface to user to set formats, queue buffers and streaming.
++
++.. kernel-figure::  ipu6_isys_graph.svg
++   :alt: ipu6 isys media graph with multiple streams support
++
++   ipu6 isys media graph with multiple streams support
++
++Capturing frames by IPU6 ISYS
++-----------------------------
++
++IPU6 ISYS is used to capture frames from the camera sensors connected to the
++CSI2 ports. The supported input formats of ISYS are listed in table below:
++
++.. tabularcolumns:: |p{0.8cm}|p{4.0cm}|p{4.0cm}|
++
++.. flat-table::
++    :header-rows: 1
++
++    * - IPU6 ISYS supported input formats
++
++    * - RGB565, RGB888
++
++    * - UYVY8, YUYV8
++
++    * - RAW8, RAW10, RAW12
++
++.. _ipu6_isys_capture_examples:
++
++Examples
++~~~~~~~~
++Here is an example of IPU6 ISYS raw capture on Dell XPS 9315 laptop. On this
++machine, ov01a10 sensor is connected to IPU ISYS CSI2 port 2, which can
++generate images at sBGGR10 with resolution 1280x800.
++
++Using the media controller APIs, we can configure ov01a10 sensor by
++media-ctl [#f1]_ and yavta [#f2]_ to transmit frames to IPU6 ISYS.
++
++.. code-block:: none
++
++    # Example 1 capture frame from ov01a10 camera sensor
++    # This example assumes /dev/media0 as the IPU ISYS media device
++    export MDEV=/dev/media0
++
++    # Establish the link for the media devices using media-ctl
++    media-ctl -d $MDEV -l "\"ov01a10 3-0036\":0 -> \"Intel IPU6 CSI2 2\":0[1]"
++
++    # Set the format for the media devices
++    media-ctl -d $MDEV -V "ov01a10:0 [fmt:SBGGR10/1280x800]"
++    media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:0 [fmt:SBGGR10/1280x800]"
++    media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:1 [fmt:SBGGR10/1280x800]"
++
++Once the media pipeline is configured, desired sensor specific settings
++(such as exposure and gain settings) can be set, using the yavta tool.
++
++e.g
++
++.. code-block:: none
++
++    # and that ov01a10 sensor is connected to i2c bus 3 with address 0x36
++    export SDEV=$(media-ctl -d $MDEV -e "ov01a10 3-0036")
++
++    yavta -w 0x009e0903 400 $SDEV
++    yavta -w 0x009e0913 1000 $SDEV
++    yavta -w 0x009e0911 2000 $SDEV
++
++Once the desired sensor settings are set, frame captures can be done as below.
++
++e.g
++
++.. code-block:: none
++
++    yavta --data-prefix -u -c10 -n5 -I -s 1280x800 --file=/tmp/frame-#.bin \
++          -f SBGGR10 $(media-ctl -d $MDEV -e "Intel IPU6 ISYS Capture 0")
++
++With the above command, 10 frames are captured at 1280x800 resolution with
++sBGGR10 format. The captured frames are available as /tmp/frame-#.bin files.
++
++Here is another example of IPU6 ISYS RAW and metadata capture from camera
++sensor ov2740 on Lenovo X1 Yoga laptop.
++
++.. code-block:: none
++
++    media-ctl -l "\"ov2740 14-0036\":0 -> \"Intel IPU6 CSI2 1\":0[1]"
++    media-ctl -l "\"Intel IPU6 CSI2 1\":1 -> \"Intel IPU6 ISYS Capture 0\":0[5]"
++    media-ctl -l "\"Intel IPU6 CSI2 1\":2 -> \"Intel IPU6 ISYS Capture 1\":0[5]"
++
++    # set routing
++    media-ctl -v -R "\"Intel IPU6 CSI2 1\" [0/0->1/0[1],0/1->2/1[1]]"
++
++    media-ctl -v "\"Intel IPU6 CSI2 1\":0/0 [fmt:SGRBG10/1932x1092]"
++    media-ctl -v "\"Intel IPU6 CSI2 1\":0/1 [fmt:GENERIC_8/97x1]"
++    media-ctl -v "\"Intel IPU6 CSI2 1\":1/0 [fmt:SGRBG10/1932x1092]"
++    media-ctl -v "\"Intel IPU6 CSI2 1\":2/1 [fmt:GENERIC_8/97x1]"
++
++    CAPTURE_DEV=$(media-ctl -e "Intel IPU6 ISYS Capture 0")
++    ./yavta --data-prefix -c100 -n5 -I -s1932x1092 --file=/tmp/frame-#.bin \
++    -f SGRBG10 ${CAPTURE_DEV}
++
++    CAPTURE_META=$(media-ctl -e "Intel IPU6 ISYS Capture 1")
++    ./yavta --data-prefix -c100 -n5 -I -s97x1 -B meta-capture \
++    --file=/tmp/meta-#.bin -f GENERIC_8 ${CAPTURE_META}
++
++References
++==========
++
++.. [#f1] https://git.ideasonboard.org/?p=media-ctl.git;a=summary
++.. [#f2] https://git.ideasonboard.org/yavta.git
+diff --git a/Documentation/admin-guide/media/ipu6_isys_graph.svg b/Documentation/admin-guide/media/ipu6_isys_graph.svg
+new file mode 100644
+index 000000000000..707747c75280
+--- /dev/null
++++ b/Documentation/admin-guide/media/ipu6_isys_graph.svg
+@@ -0,0 +1,174 @@
++<?xml version="1.0" encoding="UTF-8" standalone="no"?>
++<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
++ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
++<!-- Generated by graphviz version 2.38.0 (20140413.2041)
++ -->
++<!-- Title: board Pages: 1 -->
++<svg width="559pt" height="810pt"
++ viewBox="0.00 0.00 559.00 809.50" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
++<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 805.5)">
++<title>board</title>
++<polygon fill="white" stroke="none" points="-4,4 -4,-805.5 555,-805.5 555,4 -4,4"/>
++<!-- n00000001 -->
++<g id="node1" class="node"><title>n00000001</title>
++<polygon fill="#66cd00" stroke="black" points="551,-192.5 387,-192.5 387,-154.5 551,-154.5 551,-192.5"/>
++<text text-anchor="middle" x="469" y="-177.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 0</text>
++<text text-anchor="middle" x="469" y="-162.3" font-family="Times,serif" font-size="14.00">/dev/video0</text>
++</g>
++<!-- n00000002 -->
++<g id="node2" class="node"><title>n00000002</title>
++<polygon fill="#66cd00" stroke="black" points="551,-395.5 387,-395.5 387,-357.5 551,-357.5 551,-395.5"/>
++<text text-anchor="middle" x="469" y="-380.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 1</text>
++<text text-anchor="middle" x="469" y="-365.3" font-family="Times,serif" font-size="14.00">/dev/video1</text>
++</g>
++<!-- n00000003 -->
++<g id="node3" class="node"><title>n00000003</title>
++<polygon fill="#66cd00" stroke="black" points="551,-598.5 387,-598.5 387,-560.5 551,-560.5 551,-598.5"/>
++<text text-anchor="middle" x="469" y="-583.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 2</text>
++<text text-anchor="middle" x="469" y="-568.3" font-family="Times,serif" font-size="14.00">/dev/video2</text>
++</g>
++<!-- n00000004 -->
++<g id="node4" class="node"><title>n00000004</title>
++<polygon fill="#66cd00" stroke="black" points="551,-801.5 387,-801.5 387,-763.5 551,-763.5 551,-801.5"/>
++<text text-anchor="middle" x="469" y="-786.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 3</text>
++<text text-anchor="middle" x="469" y="-771.3" font-family="Times,serif" font-size="14.00">/dev/video3</text>
++</g>
++<!-- n0000007d -->
++<g id="node5" class="node"><title>n0000007d</title>
++<path fill="#ffb90f" stroke="black" d="M201,-0.5C201,-0.5 339,-0.5 339,-0.5 345,-0.5 351,-6.5 351,-12.5 351,-12.5 351,-172.5 351,-172.5 351,-178.5 345,-184.5 339,-184.5 339,-184.5 201,-184.5 201,-184.5 195,-184.5 189,-178.5 189,-172.5 189,-172.5 189,-12.5 189,-12.5 189,-6.5 195,-0.5 201,-0.5"/>
++<text text-anchor="middle" x="200.5" y="-88.8" font-family="Times,serif" font-size="14.00">0</text>
++<polyline fill="none" stroke="black" points="212,-0.5 212,-184.5 "/>
++<text text-anchor="middle" x="270" y="-96.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 0</text>
++<text text-anchor="middle" x="270" y="-81.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev0</text>
++<polyline fill="none" stroke="black" points="328,-0.5 328,-184.5 "/>
++<text text-anchor="middle" x="339.5" y="-169.3" font-family="Times,serif" font-size="14.00">1</text>
++<polyline fill="none" stroke="black" points="328,-161.5 351,-161.5 "/>
++<text text-anchor="middle" x="339.5" y="-146.3" font-family="Times,serif" font-size="14.00">2</text>
++<polyline fill="none" stroke="black" points="328,-138.5 351,-138.5 "/>
++<text text-anchor="middle" x="339.5" y="-123.3" font-family="Times,serif" font-size="14.00">3</text>
++<polyline fill="none" stroke="black" points="328,-115.5 351,-115.5 "/>
++<text text-anchor="middle" x="339.5" y="-100.3" font-family="Times,serif" font-size="14.00">4</text>
++<polyline fill="none" stroke="black" points="328,-92.5 351,-92.5 "/>
++<text text-anchor="middle" x="339.5" y="-77.3" font-family="Times,serif" font-size="14.00">5</text>
++<polyline fill="none" stroke="black" points="328,-69.5 351,-69.5 "/>
++<text text-anchor="middle" x="339.5" y="-54.3" font-family="Times,serif" font-size="14.00">6</text>
++<polyline fill="none" stroke="black" points="328,-46.5 351,-46.5 "/>
++<text text-anchor="middle" x="339.5" y="-31.3" font-family="Times,serif" font-size="14.00">7</text>
++<polyline fill="none" stroke="black" points="328,-23.5 351,-23.5 "/>
++<text text-anchor="middle" x="339.5" y="-8.3" font-family="Times,serif" font-size="14.00">8</text>
++</g>
++<!-- n0000007d&#45;&gt;n00000001 -->
++<g id="edge1" class="edge"><title>n0000007d:port1&#45;&gt;n00000001</title>
++<path fill="none" stroke="black" stroke-dasharray="5,2" d="M351,-173.5C359.322,-173.5 367.976,-173.5 376.644,-173.5"/>
++<polygon fill="black" stroke="black" points="376.807,-177 386.807,-173.5 376.807,-170 376.807,-177"/>
++</g>
++<!-- n00000087 -->
++<g id="node6" class="node"><title>n00000087</title>
++<path fill="#ffb90f" stroke="black" d="M201,-203.5C201,-203.5 339,-203.5 339,-203.5 345,-203.5 351,-209.5 351,-215.5 351,-215.5 351,-375.5 351,-375.5 351,-381.5 345,-387.5 339,-387.5 339,-387.5 201,-387.5 201,-387.5 195,-387.5 189,-381.5 189,-375.5 189,-375.5 189,-215.5 189,-215.5 189,-209.5 195,-203.5 201,-203.5"/>
++<text text-anchor="middle" x="200.5" y="-291.8" font-family="Times,serif" font-size="14.00">0</text>
++<polyline fill="none" stroke="black" points="212,-203.5 212,-387.5 "/>
++<text text-anchor="middle" x="270" y="-299.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 1</text>
++<text text-anchor="middle" x="270" y="-284.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev1</text>
++<polyline fill="none" stroke="black" points="328,-203.5 328,-387.5 "/>
++<text text-anchor="middle" x="339.5" y="-372.3" font-family="Times,serif" font-size="14.00">1</text>
++<polyline fill="none" stroke="black" points="328,-364.5 351,-364.5 "/>
++<text text-anchor="middle" x="339.5" y="-349.3" font-family="Times,serif" font-size="14.00">2</text>
++<polyline fill="none" stroke="black" points="328,-341.5 351,-341.5 "/>
++<text text-anchor="middle" x="339.5" y="-326.3" font-family="Times,serif" font-size="14.00">3</text>
++<polyline fill="none" stroke="black" points="328,-318.5 351,-318.5 "/>
++<text text-anchor="middle" x="339.5" y="-303.3" font-family="Times,serif" font-size="14.00">4</text>
++<polyline fill="none" stroke="black" points="328,-295.5 351,-295.5 "/>
++<text text-anchor="middle" x="339.5" y="-280.3" font-family="Times,serif" font-size="14.00">5</text>
++<polyline fill="none" stroke="black" points="328,-272.5 351,-272.5 "/>
++<text text-anchor="middle" x="339.5" y="-257.3" font-family="Times,serif" font-size="14.00">6</text>
++<polyline fill="none" stroke="black" points="328,-249.5 351,-249.5 "/>
++<text text-anchor="middle" x="339.5" y="-234.3" font-family="Times,serif" font-size="14.00">7</text>
++<polyline fill="none" stroke="black" points="328,-226.5 351,-226.5 "/>
++<text text-anchor="middle" x="339.5" y="-211.3" font-family="Times,serif" font-size="14.00">8</text>
++</g>
++<!-- n00000087&#45;&gt;n00000002 -->
++<g id="edge2" class="edge"><title>n00000087:port1&#45;&gt;n00000002</title>
++<path fill="none" stroke="black" stroke-dasharray="5,2" d="M351,-376.5C359.322,-376.5 367.976,-376.5 376.644,-376.5"/>
++<polygon fill="black" stroke="black" points="376.807,-380 386.807,-376.5 376.807,-373 376.807,-380"/>
++</g>
++<!-- n00000091 -->
++<g id="node7" class="node"><title>n00000091</title>
++<path fill="#ffb90f" stroke="black" d="M201,-406.5C201,-406.5 339,-406.5 339,-406.5 345,-406.5 351,-412.5 351,-418.5 351,-418.5 351,-578.5 351,-578.5 351,-584.5 345,-590.5 339,-590.5 339,-590.5 201,-590.5 201,-590.5 195,-590.5 189,-584.5 189,-578.5 189,-578.5 189,-418.5 189,-418.5 189,-412.5 195,-406.5 201,-406.5"/>
++<text text-anchor="middle" x="200.5" y="-494.8" font-family="Times,serif" font-size="14.00">0</text>
++<polyline fill="none" stroke="black" points="212,-406.5 212,-590.5 "/>
++<text text-anchor="middle" x="270" y="-502.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 2</text>
++<text text-anchor="middle" x="270" y="-487.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev2</text>
++<polyline fill="none" stroke="black" points="328,-406.5 328,-590.5 "/>
++<text text-anchor="middle" x="339.5" y="-575.3" font-family="Times,serif" font-size="14.00">1</text>
++<polyline fill="none" stroke="black" points="328,-567.5 351,-567.5 "/>
++<text text-anchor="middle" x="339.5" y="-552.3" font-family="Times,serif" font-size="14.00">2</text>
++<polyline fill="none" stroke="black" points="328,-544.5 351,-544.5 "/>
++<text text-anchor="middle" x="339.5" y="-529.3" font-family="Times,serif" font-size="14.00">3</text>
++<polyline fill="none" stroke="black" points="328,-521.5 351,-521.5 "/>
++<text text-anchor="middle" x="339.5" y="-506.3" font-family="Times,serif" font-size="14.00">4</text>
++<polyline fill="none" stroke="black" points="328,-498.5 351,-498.5 "/>
++<text text-anchor="middle" x="339.5" y="-483.3" font-family="Times,serif" font-size="14.00">5</text>
++<polyline fill="none" stroke="black" points="328,-475.5 351,-475.5 "/>
++<text text-anchor="middle" x="339.5" y="-460.3" font-family="Times,serif" font-size="14.00">6</text>
++<polyline fill="none" stroke="black" points="328,-452.5 351,-452.5 "/>
++<text text-anchor="middle" x="339.5" y="-437.3" font-family="Times,serif" font-size="14.00">7</text>
++<polyline fill="none" stroke="black" points="328,-429.5 351,-429.5 "/>
++<text text-anchor="middle" x="339.5" y="-414.3" font-family="Times,serif" font-size="14.00">8</text>
++</g>
++<!-- n00000091&#45;&gt;n00000003 -->
++<g id="edge3" class="edge"><title>n00000091:port1&#45;&gt;n00000003</title>
++<path fill="none" stroke="black" d="M351,-579.5C359.322,-579.5 367.976,-579.5 376.644,-579.5"/>
++<polygon fill="black" stroke="black" points="376.807,-583 386.807,-579.5 376.807,-576 376.807,-583"/>
++</g>
++<!-- n0000009b -->
++<g id="node8" class="node"><title>n0000009b</title>
++<path fill="#ffb90f" stroke="black" d="M201,-609.5C201,-609.5 339,-609.5 339,-609.5 345,-609.5 351,-615.5 351,-621.5 351,-621.5 351,-781.5 351,-781.5 351,-787.5 345,-793.5 339,-793.5 339,-793.5 201,-793.5 201,-793.5 195,-793.5 189,-787.5 189,-781.5 189,-781.5 189,-621.5 189,-621.5 189,-615.5 195,-609.5 201,-609.5"/>
++<text text-anchor="middle" x="200.5" y="-697.8" font-family="Times,serif" font-size="14.00">0</text>
++<polyline fill="none" stroke="black" points="212,-609.5 212,-793.5 "/>
++<text text-anchor="middle" x="270" y="-705.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 3</text>
++<text text-anchor="middle" x="270" y="-690.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev3</text>
++<polyline fill="none" stroke="black" points="328,-609.5 328,-793.5 "/>
++<text text-anchor="middle" x="339.5" y="-778.3" font-family="Times,serif" font-size="14.00">1</text>
++<polyline fill="none" stroke="black" points="328,-770.5 351,-770.5 "/>
++<text text-anchor="middle" x="339.5" y="-755.3" font-family="Times,serif" font-size="14.00">2</text>
++<polyline fill="none" stroke="black" points="328,-747.5 351,-747.5 "/>
++<text text-anchor="middle" x="339.5" y="-732.3" font-family="Times,serif" font-size="14.00">3</text>
++<polyline fill="none" stroke="black" points="328,-724.5 351,-724.5 "/>
++<text text-anchor="middle" x="339.5" y="-709.3" font-family="Times,serif" font-size="14.00">4</text>
++<polyline fill="none" stroke="black" points="328,-701.5 351,-701.5 "/>
++<text text-anchor="middle" x="339.5" y="-686.3" font-family="Times,serif" font-size="14.00">5</text>
++<polyline fill="none" stroke="black" points="328,-678.5 351,-678.5 "/>
++<text text-anchor="middle" x="339.5" y="-663.3" font-family="Times,serif" font-size="14.00">6</text>
++<polyline fill="none" stroke="black" points="328,-655.5 351,-655.5 "/>
++<text text-anchor="middle" x="339.5" y="-640.3" font-family="Times,serif" font-size="14.00">7</text>
++<polyline fill="none" stroke="black" points="328,-632.5 351,-632.5 "/>
++<text text-anchor="middle" x="339.5" y="-617.3" font-family="Times,serif" font-size="14.00">8</text>
++</g>
++<!-- n0000009b&#45;&gt;n00000004 -->
++<g id="edge4" class="edge"><title>n0000009b:port1&#45;&gt;n00000004</title>
++<path fill="none" stroke="black" stroke-dasharray="5,2" d="M351,-782.5C359.322,-782.5 367.976,-782.5 376.644,-782.5"/>
++<polygon fill="black" stroke="black" points="376.807,-786 386.807,-782.5 376.807,-779 376.807,-786"/>
++</g>
++<!-- n00000865 -->
++<g id="node9" class="node"><title>n00000865</title>
++<path fill="cornflowerblue" stroke="black" d="M12,-479.5C12,-479.5 141,-479.5 141,-479.5 147,-479.5 153,-485.5 153,-491.5 153,-491.5 153,-505.5 153,-505.5 153,-511.5 147,-517.5 141,-517.5 141,-517.5 12,-517.5 12,-517.5 6,-517.5 0,-511.5 0,-505.5 0,-505.5 0,-491.5 0,-491.5 0,-485.5 6,-479.5 12,-479.5"/>
++<text text-anchor="middle" x="10" y="-494.8" font-family="Times,serif" font-size="14.00"> </text>
++<polyline fill="none" stroke="black" points="20,-479.5 20,-517.5 "/>
++<text text-anchor="middle" x="75" y="-502.3" font-family="Times,serif" font-size="14.00">ov01a10 3&#45;0036</text>
++<text text-anchor="middle" x="75" y="-487.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev4</text>
++<polyline fill="none" stroke="black" points="130,-479.5 130,-517.5 "/>
++<text text-anchor="middle" x="141.5" y="-494.8" font-family="Times,serif" font-size="14.00">0</text>
++</g>
++<!-- n00000865&#45;&gt;n00000091 -->
++<g id="edge5" class="edge"><title>n00000865:port0&#45;&gt;n00000091:port0</title>
++<path fill="none" stroke="black" d="M153,-498.5C165,-498.5 170.25,-498.5 178.875,-498.5"/>
++<polygon fill="black" stroke="black" points="179,-502 189,-498.5 179,-495 179,-502"/>
++</g>
++<!-- n00000866 -->
++<!-- n00000866&#45;&gt;n0000007d -->
++<!-- n00000867 -->
++<!-- n00000867&#45;&gt;n00000087 -->
++<!-- n00000868 -->
++<!-- n00000868&#45;&gt;n0000009b -->
++</g>
++</svg>
+diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst
+index f4bb2605f07e..4120eded9a13 100644
+--- a/Documentation/admin-guide/media/v4l-drivers.rst
++++ b/Documentation/admin-guide/media/v4l-drivers.rst
+@@ -16,6 +16,7 @@ Video4Linux (V4L) driver-specific documentation
+ 	imx
+ 	imx7
+ 	ipu3
++	ipu6-isys
+ 	ivtv
+ 	mgb4
+ 	omap3isp
+-- 
+2.43.2
+
+
+From 3e80683ecc9ffe38fdf6e6232089794b6019816b Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:29 +0800
+Subject: [PATCH 22/33] Documentation: add documentation of Intel IPU6 driver
+ and hardware overview
+
+Add a documentation for an overview of IPU6 hardware and describe the main
+the components of IPU6 driver.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ .../driver-api/media/drivers/index.rst        |   1 +
+ .../driver-api/media/drivers/ipu6.rst         | 205 ++++++++++++++++++
+ 2 files changed, 206 insertions(+)
+ create mode 100644 Documentation/driver-api/media/drivers/ipu6.rst
+
+diff --git a/Documentation/driver-api/media/drivers/index.rst b/Documentation/driver-api/media/drivers/index.rst
+index c4123a16b5f9..7f6f3dcd5c90 100644
+--- a/Documentation/driver-api/media/drivers/index.rst
++++ b/Documentation/driver-api/media/drivers/index.rst
+@@ -26,6 +26,7 @@ Video4Linux (V4L) drivers
+ 	vimc-devel
+ 	zoran
+ 	ccs/ccs
++	ipu6
+ 
+ 
+ Digital TV drivers
+diff --git a/Documentation/driver-api/media/drivers/ipu6.rst b/Documentation/driver-api/media/drivers/ipu6.rst
+new file mode 100644
+index 000000000000..b6357155c13b
+--- /dev/null
++++ b/Documentation/driver-api/media/drivers/ipu6.rst
+@@ -0,0 +1,205 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++==================
++Intel IPU6 Driver
++==================
++
++Author: Bingbu Cao <bingbu.cao@intel.com>
++
++Overview
++=========
++
++Intel IPU6 is the sixth generation of Intel Image Processing Unit used in some
++Intel Chipsets such as Tiger Lake, Jasper Lake, Alder Lake, Raptor Lake and
++Meteor Lake. IPU6 consists of two major systems: Input System (IS) and
++Processing System (PS). IPU6 are visible on the PCI bus as a single device,
++it can be found by ``lspci``:
++
++``0000:00:05.0 Multimedia controller: Intel Corporation Device xxxx (rev xx)``
++
++IPU6 has a 16 MB BAR in PCI configuration Space for MMIO registers which is
++visible for driver.
++
++Buttress
++=========
++
++The IPU6 is connecting to the system fabric with ``Buttress`` which is enabling
++host driver to control the IPU6, it also allows IPU6 access the system memory to
++store and load frame pixel streams and any other metadata.
++
++``Buttress`` mainly manages several system functionalities - power management,
++interrupt handling, firmware authentication and global timer sync.
++
++IS and PS Power flow
++---------------------------
++
++IPU6 driver initialize the IS and PS power up or down request by setting the
++Buttress frequency control register for IS and PS -
++``IPU6_BUTTRESS_REG_IS_FREQ_CTL`` and ``IPU6_BUTTRESS_REG_PS_FREQ_CTL`` in
++function:
++
++.. c:function:: int ipu6_buttress_power(..., bool on)
++
++Buttress forwards the request to Punit, after Punit execute the power up flow,
++buttress indicates driver that IS or PS is powered up by updating the power
++status registers.
++
++.. Note:: IS power up needs take place prior to PS power up, IS power down needs
++	  take place after PS power down due to hardware limitation.
++
++
++Interrupt
++------------
++
++IPU6 interrupt can be generated as MSI or INTA, interrupt will be triggered
++when IS, PS, Buttress event or error happen, driver can get the interrupt
++cause by reading the interrupt status register ``BUTTRESS_REG_ISR_STATUS``,
++driver firstly clear the irq status and then call specific IS or PS irq handler.
++
++.. c:function:: irqreturn_t ipu6_buttress_isr(int irq, ...)
++
++Security and firmware authentication
++-------------------------------------
++To address the IPU6 firmware security concerns, the IPU6 firmware needs to
++undergo an authentication process before it is allowed to executed on the IPU6
++internal processors. Driver will work with Converged Security Engine (CSE) to
++complete authentication process. CSE is responsible of authenticating the
++IPU6 firmware, the authenticated firmware binary is copied into an isolated
++memory region. Firmware authentication process is implemented by CSE following
++an IPC handshake with driver. There are some Buttress registers used by CSE and
++driver to communicate with each other as IPC messages.
++
++.. c:function:: int ipu6_buttress_authenticate(...)
++
++Global timer sync
++------------------
++IPU driver initiates a Hammock Harbor synchronization flow each time it starts
++camera operation. IPU will synchronizes an internal counter in the Buttress
++with a copy of SoC time, this counter keeps the updated time until camera
++operation is stopped. Driver can use this time counter to calibrate the
++timestamp based on the timestamp in response event from firmware.
++
++.. c:function:: int ipu6_buttress_start_tsc_sync(...)
++
++
++DMA and MMU
++============
++
++IPU6 has its own scalar processor where the firmware run at, it has
++an internal 32-bits virtual address space. IPU6 has MMU address translation
++hardware to allow that scalar process access the internal memory and external
++system memory through IPU6 virtual address. The address translation is
++based on two levels of page lookup tables stored in system memory which are
++maintained by IPU6 driver. IPU6 driver sets the level-1 page table base address
++to MMU register and allow MMU to lookup the page table.
++
++IPU6 driver exports its own DMA operations. Driver will update the page table
++entries for each DMA operation and invalidate the MMU TLB after each unmap and
++free.
++
++.. code-block:: none
++
++    const struct dma_map_ops ipu6_dma_ops = {
++	   .alloc = ipu6_dma_alloc,
++	   .free = ipu6_dma_free,
++	   .mmap = ipu6_dma_mmap,
++	   .map_sg = ipu6_dma_map_sg,
++	   .unmap_sg = ipu6_dma_unmap_sg,
++	   ...
++    };
++
++.. Note:: IPU6 MMU works behind IOMMU, so for each IPU6 DMA ops, driver will
++	  call generic PCI DMA ops to ask IOMMU to do the additional mapping
++	  if VT-d enabled.
++
++
++Firmware file format
++=====================
++
++IPU6 release the firmware in Code Partition Directory (CPD) file format. The
++CPD firmware contains a CPD header, several CPD entries and CPD components.
++CPD component includes 3 entries - manifest, metadata and module data. Manifest
++and metadata are defined by CSE and used by CSE for authentication. Module data
++is defined by IPU6 which holds the binary data of firmware called package
++directory. IPU6 driver (``ipu6-cpd.c``) parses and validates the CPD firmware
++file and get the package directory binary data of IPU6 firmware, copy it to
++specific DMA buffer and sets its base address to Buttress ``FW_SOURCE_BASE``
++register, CSE will do authentication for this firmware binary.
++
++
++Syscom interface
++================
++
++IPU6 driver communicates with firmware via syscom ABI. Syscom is an
++inter-processor communication mechanism between IPU scalar processor and CPU.
++There are a number of resources shared between firmware and software.
++A system memory region where the message queues reside, firmware can access the
++memory region via IPU MMU. Syscom queues are FIFO fixed depth queues with
++configurable elements ``token`` (message). There is also a common IPU MMIO
++registers where the queue read and write indices reside. Software and firmware
++work as producer and consumer of tokens in queue, and update the write and read
++indices separately when sending or receiving each message.
++
++IPU6 driver must prepare and configure the number of input and output queues,
++configure the count of tokens per queue and the size of per token before
++initiate and start the communication with firmware, firmware and software must
++use same configurations. IPU6 Buttress has a number of firmware boot parameter
++registers which can be used to store the address of configuration and initiate
++the Syscom state, then driver can request firmware to start and run via setting
++the scalar processor control status register.
++
++
++Input System
++==============
++
++IPU6 input system consists of MIPI D-PHY and several CSI receiver controllers,
++it can capture image pixel data from camera sensors or other MIPI CSI output
++devices.
++
++D-PHYs and CSI-2 ports lane mapping
++-----------------------------------
++
++IPU6 integrates different D-PHY IPs on different SoCs, on Tiger Lake and Alder
++Lake, IPU6 integrates MCD10 D-PHY, IPU6SE on Jasper Lake integrates JSL D-PHY
++and IPU6EP on Meteor Lake integrates a Synopsys DWC D-PHY. There is an adaption
++layer between D-PHY and CSI receiver controller which includes port
++configuration, PHY wrapper or private test interfaces for D-PHY. There are 3
++D-PHY drivers ``ipu6-isys-mcd-phy.c``, ``ipu6-isys-jsl-phy.c`` and
++``ipu6-isys-dwc-phy.c`` program the above 3 D-PHYs in IPU6.
++
++Different IPU6 version has different D-PHY lanes mappings, On Tiger Lake, there
++are 12 data lanes and 8 clock lanes, IPU6 support maximum 8 CSI-2 ports, see
++the ppi mmapping in ``ipu6-isys-mcd-phy.c`` for more information. On Jasper Lake
++and Alder Lake, D-PHY has 8 data lanes and 4 clock lanes, IPU6 support maximum 4
++CSI-2 ports. For Meteor Lake, D-PHY has 12 data lanes and 6 clock lanes, IPU6
++support maximum 6 CSI-2 ports.
++
++.. Note:: Each adjacent CSI ports work as a pair and share the data lanes.
++	  For example, for CSI port 0 and 1, CSI port 0 support maximum 4
++	  data lanes, CSI port 1 support maximum 2 data lanes, CSI port 0
++	  with 2 data lanes can work together with CSI port 1 with 2 data lanes.
++	  If trying to use CSI port 0 with 4 lanes, CSI port 1 will not be
++	  available as the 4 data lanes are shared by CSI port 0 and 1. Same
++	  scenario is also applied for CSI port 2/3, 4/5 and 7/8.
++
++IS firmware ABIs
++----------------
++
++IPU6 firmware define a series of ABIs to software. In general, software firstly
++prepare the stream configuration ``struct ipu6_fw_isys_stream_cfg_data_abi``
++and send the configuration to firmware via sending ``STREAM_OPEN`` command.
++Stream configuration includes input pins and output pins, input pin
++``struct ipu6_fw_isys_input_pin_info_abi`` defines the resolution and data type
++of input source, output pin ``struct ipu6_fw_isys_output_pin_info_abi``
++defines the output resolution, stride and frame format, etc. Once driver get the
++interrupt from firmware that indicates stream open successfully, driver will
++send the ``STREAM_START`` and ``STREAM_CAPTURE`` command to request firmware to
++start capturing image frames. ``STREAM_CAPTURE`` command queues the buffers to
++firmware with ``struct ipu6_fw_isys_frame_buff_set``, software then wait the
++interrupt and response from firmware, ``PIN_DATA_READY`` means data ready
++on specific output pin and then software return the buffers to user.
++
++.. Note:: See :ref:`Examples<ipu6_isys_capture_examples>` about how to do
++	  capture by IPU6 IS driver.
++
++
+-- 
+2.43.2
+
+
+From d883f3386e7185d9404cb25e32df986656a4e82a Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:30 +0800
+Subject: [PATCH 23/33] media: ipu6/isys: support line-based metadata capture
+ support
+
+Some camera sensor can output the embedded data in specific
+data type.  This patch add the support for embedded data capture
+in IPU6 IS driver.
+
+It's based on Sakari's line-based metadata capture support change:
+<URL:https://git.linuxtv.org/sailus/media_tree.git/log/?h=metadata>
+
+Signed-off-by: Hongju Wang <hongju.wang@intel.com>
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c |   5 +
+ .../media/pci/intel/ipu6/ipu6-isys-queue.c    |  44 ++--
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.c   |   5 +
+ .../media/pci/intel/ipu6/ipu6-isys-video.c    | 201 +++++++++++++++---
+ .../media/pci/intel/ipu6/ipu6-isys-video.h    |   7 +-
+ 5 files changed, 216 insertions(+), 46 deletions(-)
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+index ac9fa3e0d7ab..a6430d531129 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+@@ -42,6 +42,11 @@ static const u32 csi2_supported_codes[] = {
+ 	MEDIA_BUS_FMT_SGBRG8_1X8,
+ 	MEDIA_BUS_FMT_SGRBG8_1X8,
+ 	MEDIA_BUS_FMT_SRGGB8_1X8,
++	MEDIA_BUS_FMT_META_8,
++	MEDIA_BUS_FMT_META_10,
++	MEDIA_BUS_FMT_META_12,
++	MEDIA_BUS_FMT_META_16,
++	MEDIA_BUS_FMT_META_24,
+ 	0
+ };
+ 
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+index 735d2d642d87..15fa7ed22b2f 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+@@ -35,11 +35,14 @@ static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ 	/* num_planes == 0: we're being called through VIDIOC_REQBUFS */
+ 	if (!*num_planes) {
+ 		use_fmt = true;
+-		*num_planes = av->mpix.num_planes;
++		if (av->vfmt.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++			*num_planes = av->vfmt.fmt.pix_mp.num_planes;
++		else if (av->vfmt.type == V4L2_BUF_TYPE_META_CAPTURE)
++			*num_planes = 1;
+ 	}
+ 
+ 	for (i = 0; i < *num_planes; i++) {
+-		size = av->mpix.plane_fmt[i].sizeimage;
++		size = ipu6_get_data_size(&av->vfmt, i);
+ 		if (use_fmt) {
+ 			sizes[i] = size;
+ 		} else if (sizes[i] < size) {
+@@ -59,16 +62,17 @@ static int ipu6_isys_buf_prepare(struct vb2_buffer *vb)
+ 	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+ 	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ 	struct device *dev = &av->isys->adev->auxdev.dev;
++	u32 bytesperline = ipu6_get_bytes_per_line(&av->vfmt);
++	u32 height = ipu6_get_frame_height(&av->vfmt);
++	u32 size = ipu6_get_data_size(&av->vfmt, 0);
+ 
+ 	dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n",
+-		av->vdev.name, av->mpix.plane_fmt[0].sizeimage,
+-		vb2_plane_size(vb, 0));
++		av->vdev.name, size, vb2_plane_size(vb, 0));
+ 
+-	if (av->mpix.plane_fmt[0].sizeimage > vb2_plane_size(vb, 0))
++	if (size > vb2_plane_size(vb, 0))
+ 		return -EINVAL;
+ 
+-	vb2_set_plane_payload(vb, 0, av->mpix.plane_fmt[0].bytesperline *
+-			      av->mpix.height);
++	vb2_set_plane_payload(vb, 0, bytesperline * height);
+ 	vb->planes[0].data_offset = 0;
+ 
+ 	return 0;
+@@ -437,18 +441,22 @@ static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq)
+ 		return ret;
+ 	}
+ 
+-	if (format.width != av->mpix.width ||
+-	    format.height != av->mpix.height) {
+-		dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n",
+-			av->mpix.width, av->mpix.height,
+-			format.width, format.height);
++	if (format.width != ipu6_get_frame_width(&av->vfmt) ||
++	    format.height != ipu6_get_frame_height(&av->vfmt)) {
++		dev_err(dev, "wrong width or height %ux%u (%ux%u expected)\n",
++			ipu6_get_frame_width(&av->vfmt),
++			ipu6_get_frame_height(&av->vfmt), format.width,
++			format.height);
+ 		return -EINVAL;
+ 	}
+ 
+-	if (format.field != av->mpix.field) {
+-		dev_dbg(dev, "wrong field value 0x%8.8x (0x%8.8x expected)\n",
+-			av->mpix.field, format.field);
+-		return -EINVAL;
++	if (av->vfmt.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
++		if (format.field != av->vfmt.fmt.pix_mp.field) {
++			dev_dbg(dev,
++				"wrong field value 0x%8.8x (%8.8x expected)\n",
++				av->vfmt.fmt.pix_mp.field, format.field);
++			return -EINVAL;
++		}
+ 	}
+ 
+ 	if (format.code != av->pfmt->code) {
+@@ -531,8 +539,8 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
+ 	int nr_queues, ret;
+ 
+ 	dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
+-		av->vdev.name, av->mpix.width, av->mpix.height,
+-		av->pfmt->css_pixelformat);
++		av->vdev.name, ipu6_get_frame_width(&av->vfmt),
++		ipu6_get_frame_height(&av->vfmt), av->pfmt->css_pixelformat);
+ 
+ 	ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues);
+ 	if (ret < 0) {
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+index 510c5ca34f9f..3c9263ac02a3 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+@@ -20,25 +20,30 @@ unsigned int ipu6_isys_mbus_code_to_bpp(u32 code)
+ {
+ 	switch (code) {
+ 	case MEDIA_BUS_FMT_RGB888_1X24:
++	case MEDIA_BUS_FMT_META_24:
+ 		return 24;
+ 	case MEDIA_BUS_FMT_RGB565_1X16:
+ 	case MEDIA_BUS_FMT_UYVY8_1X16:
+ 	case MEDIA_BUS_FMT_YUYV8_1X16:
++	case MEDIA_BUS_FMT_META_16:
+ 		return 16;
+ 	case MEDIA_BUS_FMT_SBGGR12_1X12:
+ 	case MEDIA_BUS_FMT_SGBRG12_1X12:
+ 	case MEDIA_BUS_FMT_SGRBG12_1X12:
+ 	case MEDIA_BUS_FMT_SRGGB12_1X12:
++	case MEDIA_BUS_FMT_META_12:
+ 		return 12;
+ 	case MEDIA_BUS_FMT_SBGGR10_1X10:
+ 	case MEDIA_BUS_FMT_SGBRG10_1X10:
+ 	case MEDIA_BUS_FMT_SGRBG10_1X10:
+ 	case MEDIA_BUS_FMT_SRGGB10_1X10:
++	case MEDIA_BUS_FMT_META_10:
+ 		return 10;
+ 	case MEDIA_BUS_FMT_SBGGR8_1X8:
+ 	case MEDIA_BUS_FMT_SGBRG8_1X8:
+ 	case MEDIA_BUS_FMT_SGRBG8_1X8:
+ 	case MEDIA_BUS_FMT_SRGGB8_1X8:
++	case MEDIA_BUS_FMT_META_8:
+ 		return 8;
+ 	default:
+ 		WARN_ON(1);
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+index 847eac26bcd6..1a023bf1e1a6 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+@@ -85,6 +85,11 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RGB565},
+ 	{V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RGBA888},
++	{V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8, 0},
++	{V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10, 0},
++	{V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12, 0},
++	{V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16, 0},
++	{V4L2_META_FMT_GENERIC_CSI2_24, 24, 24, MEDIA_BUS_FMT_META_24, 0},
+ };
+ 
+ static int video_open(struct file *file)
+@@ -181,12 +186,12 @@ static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh,
+ 	return 0;
+ }
+ 
+-static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+-				       struct v4l2_format *fmt)
++static int vidioc_get_format(struct file *file, void *fh,
++			     struct v4l2_format *fmt)
+ {
+ 	struct ipu6_isys_video *av = video_drvdata(file);
+ 
+-	fmt->fmt.pix_mp = av->mpix;
++	*fmt = av->vfmt;
+ 
+ 	return 0;
+ }
+@@ -245,30 +250,114 @@ ipu6_isys_video_try_fmt_vid_mplane(struct ipu6_isys_video *av,
+ 	return pfmt;
+ }
+ 
+-static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *fh,
+-				       struct v4l2_format *f)
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_video_try_fmt_meta(struct ipu6_isys_video *av,
++			     struct v4l2_meta_format *meta)
++{
++	const struct ipu6_isys_pixelformat *pfmt =
++		ipu6_isys_get_pixelformat(meta->dataformat);
++
++	memset(&av->vfmt, 0, sizeof(av->vfmt));
++	av->vfmt.type = V4L2_BUF_TYPE_META_CAPTURE;
++	av->pfmt = pfmt;
++
++	meta->dataformat = pfmt->pixelformat;
++	meta->width = clamp(meta->width, IPU6_ISYS_MIN_WIDTH,
++			    IPU6_ISYS_MAX_WIDTH);
++	meta->height = clamp(meta->height, IPU6_ISYS_MIN_HEIGHT,
++			     IPU6_ISYS_MAX_HEIGHT);
++
++	if (pfmt->bpp != pfmt->bpp_packed)
++		meta->bytesperline = meta->width *
++				     DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);
++	else
++		meta->bytesperline =
++			DIV_ROUND_UP(meta->width * pfmt->bpp, BITS_PER_BYTE);
++
++	meta->bytesperline = ALIGN(meta->bytesperline, av->isys->line_align);
++	meta->buffersize =
++		max(max(meta->buffersize, meta->bytesperline * meta->height +
++			max(meta->bytesperline,
++			    av->isys->pdata->ipdata->isys_dma_overshoot)), 1U);
++
++	return pfmt;
++}
++
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_video_try_fmt(struct ipu6_isys_video *av, struct v4l2_format *f)
++{
++	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
++	else if (f->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return ipu6_isys_video_try_fmt_meta(av, &f->fmt.meta);
++	else
++		return &ipu6_isys_pfmts[0];
++}
++
++static int vidioc_set_format(struct file *file, void *fh,
++			     struct v4l2_format *f)
+ {
+ 	struct ipu6_isys_video *av = video_drvdata(file);
+ 
+ 	if (av->aq.vbq.streaming)
+ 		return -EBUSY;
+ 
+-	av->pfmt = ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
+-	av->mpix = f->fmt.pix_mp;
++	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
++	    f->type != V4L2_BUF_TYPE_META_CAPTURE)
++		return -EINVAL;
++
++	av->pfmt = ipu6_isys_video_try_fmt(av, f);
++	av->vfmt = *f;
+ 
+ 	return 0;
+ }
+ 
+-static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *fh,
+-					 struct v4l2_format *f)
++static int vidioc_try_format(struct file *file, void *fh,
++			     struct v4l2_format *f)
+ {
+ 	struct ipu6_isys_video *av = video_drvdata(file);
+ 
+-	ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
++	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
++	    f->type != V4L2_BUF_TYPE_META_CAPTURE)
++		return -EINVAL;
++
++	ipu6_isys_video_try_fmt(av, f);
+ 
+ 	return 0;
+ }
+ 
++static int vidioc_request_qbufs(struct file *file, void *priv,
++				struct v4l2_requestbuffers *p)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++	int ret;
++
++	av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type);
++	av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type);
++
++	ret = vb2_queue_change_type(&av->aq.vbq, p->type);
++	if (ret)
++		return ret;
++
++	return vb2_ioctl_reqbufs(file, priv, p);
++}
++
++static int vidioc_create_bufs(struct file *file, void *priv,
++			      struct v4l2_create_buffers *p)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++	int ret;
++
++	av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type);
++	av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type);
++
++	ret = vb2_queue_change_type(&av->aq.vbq, p->format.type);
++	if (ret)
++		return ret;
++
++	return vb2_ioctl_create_bufs(file, priv, p);
++}
++
+ static int link_validate(struct media_link *link)
+ {
+ 	struct ipu6_isys_video *av =
+@@ -279,6 +368,8 @@ static int link_validate(struct media_link *link)
+ 	struct v4l2_mbus_framefmt *s_fmt;
+ 	struct media_pad *s_pad;
+ 	u32 s_stream;
++	u32 height;
++	u32 width;
+ 	int ret = -EPIPE;
+ 
+ 	if (!link->source->entity)
+@@ -305,11 +396,13 @@ static int link_validate(struct media_link *link)
+ 		goto unlock;
+ 	}
+ 
+-	if (s_fmt->width != av->mpix.width ||
+-	    s_fmt->height != av->mpix.height || s_fmt->code != av->pfmt->code) {
++	height = ipu6_get_frame_height(&av->vfmt);
++	width = ipu6_get_frame_width(&av->vfmt);
++	if (s_fmt->width != width || s_fmt->height != height ||
++	    s_fmt->code != av->pfmt->code) {
+ 		dev_err(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",
+-			s_fmt->width, s_fmt->height, s_fmt->code,
+-			av->mpix.width, av->mpix.height, av->pfmt->code);
++			s_fmt->width, s_fmt->height, s_fmt->code, width, height,
++			av->pfmt->code);
+ 		goto unlock;
+ 	}
+ 
+@@ -393,10 +486,10 @@ static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
+ 
+ 	output_pin = &cfg->output_pins[output_pins];
+ 	output_pin->input_pin_id = input_pins;
+-	output_pin->output_res.width = av->mpix.width;
+-	output_pin->output_res.height = av->mpix.height;
++	output_pin->output_res.width = ipu6_get_frame_width(&av->vfmt);
++	output_pin->output_res.height = ipu6_get_frame_height(&av->vfmt);
+ 
+-	output_pin->stride = av->mpix.plane_fmt[0].bytesperline;
++	output_pin->stride = ipu6_get_bytes_per_line(&av->vfmt);
+ 	if (av->pfmt->bpp != av->pfmt->bpp_packed)
+ 		output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC;
+ 	else
+@@ -663,8 +756,8 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
+ 
+ 	esd = media_entity_to_v4l2_subdev(av->stream->source_entity);
+ 
+-	av->watermark.width = av->mpix.width;
+-	av->watermark.height = av->mpix.height;
++	av->watermark.width = ipu6_get_frame_width(&av->vfmt);
++	av->watermark.height = ipu6_get_frame_height(&av->vfmt);
+ 	av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift;
+ 	av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size;
+ 
+@@ -992,11 +1085,15 @@ static const struct v4l2_ioctl_ops ioctl_ops_mplane = {
+ 	.vidioc_querycap = ipu6_isys_vidioc_querycap,
+ 	.vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt,
+ 	.vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes,
+-	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane,
+-	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane,
+-	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane,
+-	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+-	.vidioc_create_bufs = vb2_ioctl_create_bufs,
++	.vidioc_g_fmt_vid_cap_mplane = vidioc_get_format,
++	.vidioc_s_fmt_vid_cap_mplane = vidioc_set_format,
++	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_format,
++	.vidioc_enum_fmt_meta_cap = ipu6_isys_vidioc_enum_fmt,
++	.vidioc_g_fmt_meta_cap = vidioc_get_format,
++	.vidioc_s_fmt_meta_cap = vidioc_set_format,
++	.vidioc_try_fmt_meta_cap = vidioc_try_format,
++	.vidioc_reqbufs = vidioc_request_qbufs,
++	.vidioc_create_bufs = vidioc_create_bufs,
+ 	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ 	.vidioc_querybuf = vb2_ioctl_querybuf,
+ 	.vidioc_qbuf = vb2_ioctl_qbuf,
+@@ -1199,7 +1296,8 @@ int ipu6_isys_video_init(struct ipu6_isys_video *av)
+ 
+ 	mutex_init(&av->mutex);
+ 	av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |
+-			       V4L2_CAP_VIDEO_CAPTURE_MPLANE;
++			       V4L2_CAP_VIDEO_CAPTURE_MPLANE |
++			       V4L2_CAP_META_CAPTURE;
+ 	av->vdev.vfl_dir = VFL_DIR_RX;
+ 
+ 	ret = ipu6_isys_queue_init(&av->aq);
+@@ -1220,8 +1318,8 @@ int ipu6_isys_video_init(struct ipu6_isys_video *av)
+ 	av->vdev.queue = &av->aq.vbq;
+ 	av->vdev.lock = &av->mutex;
+ 
+-	ipu6_isys_video_try_fmt_vid_mplane(av, &format.fmt.pix_mp);
+-	av->mpix = format.fmt.pix_mp;
++	ipu6_isys_video_try_fmt(av, &format);
++	av->vfmt = format;
+ 
+ 	set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags);
+ 	video_set_drvdata(&av->vdev, av);
+@@ -1251,3 +1349,52 @@ void ipu6_isys_video_cleanup(struct ipu6_isys_video *av)
+ 	media_entity_cleanup(&av->vdev.entity);
+ 	mutex_destroy(&av->mutex);
+ }
++
++u32 ipu6_get_data_size(struct v4l2_format *vfmt, int plane)
++{
++	if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return vfmt->fmt.pix_mp.plane_fmt[plane].sizeimage;
++	else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return vfmt->fmt.meta.buffersize;
++
++	WARN_ON_ONCE(1);
++
++	return 0;
++}
++
++u32 ipu6_get_bytes_per_line(struct v4l2_format *vfmt)
++{
++	if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return vfmt->fmt.pix_mp.plane_fmt[0].bytesperline;
++	else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return vfmt->fmt.meta.bytesperline;
++
++	WARN_ON_ONCE(1);
++
++	return 0;
++}
++
++u32 ipu6_get_frame_width(struct v4l2_format *vfmt)
++{
++	if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return vfmt->fmt.pix_mp.width;
++	else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return vfmt->fmt.meta.width;
++
++	WARN_ON_ONCE(1);
++
++	return 0;
++}
++
++u32 ipu6_get_frame_height(struct v4l2_format *vfmt)
++{
++	if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return vfmt->fmt.pix_mp.height;
++	else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return vfmt->fmt.meta.height;
++
++	WARN_ON_ONCE(1);
++
++	return 0;
++}
++
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+index 21cd33c7e277..2634ec0fd68b 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+@@ -90,7 +90,7 @@ struct ipu6_isys_video {
+ 	struct mutex mutex;
+ 	struct media_pad pad;
+ 	struct video_device vdev;
+-	struct v4l2_pix_format_mplane mpix;
++	struct v4l2_format vfmt;
+ 	const struct ipu6_isys_pixelformat *pfmt;
+ 	struct ipu6_isys *isys;
+ 	struct ipu6_isys_stream *stream;
+@@ -133,4 +133,9 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
+ 					  bool state);
+ void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state);
+ 
++u32 ipu6_get_data_size(struct v4l2_format *vfmt, int plane);
++u32 ipu6_get_bytes_per_line(struct v4l2_format *vfmt);
++u32 ipu6_get_frame_width(struct v4l2_format *vfmt);
++u32 ipu6_get_frame_height(struct v4l2_format *vfmt);
++
+ #endif /* IPU6_ISYS_VIDEO_H */
+-- 
+2.43.2
+
+
+From 9a6fb311b81433ebbd8e0769bed19958a6a5a5f6 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:31 +0800
+Subject: [PATCH 24/33] media: ipu6/isys: support new v4l2 subdev state APIs
+
+Add support for the upcoming v4l2-subdev API changes in kernel 6.8.
+This patch is based on Sakari's branch:
+<URL:https://git.linuxtv.org/sailus/media_tree.git/log/?h=metadata>
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c |  8 +++-----
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.c   | 19 +++++++++++--------
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.h   |  2 --
+ .../media/pci/intel/ipu6/ipu6-isys-video.c    |  3 +--
+ 4 files changed, 15 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+index a6430d531129..6f258cf92fc1 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+@@ -403,12 +403,11 @@ static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,
+ 	if (!sink_ffmt)
+ 		return -EINVAL;
+ 
+-	src_ffmt = v4l2_subdev_state_get_stream_format(state, sel->pad,
+-						       sel->stream);
++	src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
+ 	if (!src_ffmt)
+ 		return -EINVAL;
+ 
+-	crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream);
++	crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ 	if (!crop)
+ 		return -EINVAL;
+ 
+@@ -453,7 +452,7 @@ static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd,
+ 	if (!sink_ffmt)
+ 		return -EINVAL;
+ 
+-	crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream);
++	crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ 	if (!crop)
+ 		return -EINVAL;
+ 
+@@ -480,7 +479,6 @@ static const struct v4l2_subdev_video_ops csi2_sd_video_ops = {
+ };
+ 
+ static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
+-	.init_cfg = ipu6_isys_subdev_init_cfg,
+ 	.get_fmt = v4l2_subdev_get_fmt,
+ 	.set_fmt = ipu6_isys_subdev_set_fmt,
+ 	.get_selection = ipu6_isys_csi2_get_sel,
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+index 3c9263ac02a3..aeccd6f93986 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+@@ -156,8 +156,7 @@ int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ 	format->format.field = V4L2_FIELD_NONE;
+ 
+ 	/* Store the format and propagate it to the source pad. */
+-	fmt = v4l2_subdev_state_get_stream_format(state, format->pad,
+-						  format->stream);
++	fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ 	if (!fmt)
+ 		return -EINVAL;
+ 
+@@ -182,8 +181,7 @@ int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ 	if (ret)
+ 		return -EINVAL;
+ 
+-	crop = v4l2_subdev_state_get_stream_crop(state, other_pad,
+-						 other_stream);
++	crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
+ 	/* reset crop */
+ 	crop->left = 0;
+ 	crop->top = 0;
+@@ -241,7 +239,7 @@ int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ 		return -EINVAL;
+ 
+ 	state = v4l2_subdev_lock_and_get_active_state(sd);
+-	fmt = v4l2_subdev_state_get_stream_format(state, pad, stream);
++	fmt = v4l2_subdev_state_get_format(state, pad, stream);
+ 	if (fmt)
+ 		*format = *fmt;
+ 	v4l2_subdev_unlock_state(state);
+@@ -259,7 +257,7 @@ int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ 		return -EINVAL;
+ 
+ 	state = v4l2_subdev_lock_and_get_active_state(sd);
+-	rect = v4l2_subdev_state_get_stream_crop(state, pad, stream);
++	rect = v4l2_subdev_state_get_crop(state, pad, stream);
+ 	if (rect)
+ 		*crop = *rect;
+ 	v4l2_subdev_unlock_state(state);
+@@ -291,8 +289,8 @@ u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
+ 	return source_stream;
+ }
+ 
+-int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd,
+-			      struct v4l2_subdev_state *state)
++static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd,
++				       struct v4l2_subdev_state *state)
+ {
+ 	struct v4l2_subdev_route route = {
+ 		.sink_pad = 0,
+@@ -317,6 +315,10 @@ int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
+ 	return subdev_set_routing(sd, state, routing);
+ }
+ 
++static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = {
++	.init_state = ipu6_isys_subdev_init_state,
++};
++
+ int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
+ 			  const struct v4l2_subdev_ops *ops,
+ 			  unsigned int nr_ctrls,
+@@ -334,6 +336,7 @@ int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
+ 			 V4L2_SUBDEV_FL_STREAMS;
+ 	asd->sd.owner = THIS_MODULE;
+ 	asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
++	asd->sd.internal_ops = &ipu6_isys_subdev_internal_ops;
+ 
+ 	asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
+ 				sizeof(*asd->pad), GFP_KERNEL);
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+index adea2a55761d..f4e32b094b5b 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+@@ -46,8 +46,6 @@ int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ 				 struct v4l2_mbus_framefmt *format);
+ int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ 				  struct v4l2_rect *crop);
+-int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd,
+-			      struct v4l2_subdev_state *state);
+ int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
+ 				 struct v4l2_subdev_state *state,
+ 				 enum v4l2_subdev_format_whence which,
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+index 1a023bf1e1a6..62d4043fc2a1 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+@@ -389,8 +389,7 @@ static int link_validate(struct media_link *link)
+ 
+ 	v4l2_subdev_lock_state(s_state);
+ 
+-	s_fmt = v4l2_subdev_state_get_stream_format(s_state, s_pad->index,
+-						    s_stream);
++	s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream);
+ 	if (!s_fmt) {
+ 		dev_err(dev, "failed to get source pad format\n");
+ 		goto unlock;
+-- 
+2.43.2
+
+
+From 53ca77877d2cc7ecc39bb0ef26a1871a1c26afd1 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 15 Jan 2024 15:57:06 +0100
+Subject: [PATCH 25/33] media: intel/ipu6: Disable packed bayer v4l2-buffer
+ formats on TGL
+
+Using CSI2 packing to store 10bpp bayer data in the v4l2-buffers does not
+work on Tiger Lake when testing with an ov01a1s sensor.
+
+Disable packed bayer formats on Tiger Lake for now.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ .../media/pci/intel/ipu6/ipu6-isys-video.c    | 65 ++++++++++++-------
+ 1 file changed, 43 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+index 62d4043fc2a1..c971ffe0b948 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+@@ -61,6 +61,17 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
+ 	{V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
++	{V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_YUYV},
++	{V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RGB565},
++	{V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RGBA888},
++};
++
++const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[] = {
+ 	{V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
+ 	{V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
+@@ -77,19 +88,6 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
+ 	{V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
+-	{V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
+-	 IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
+-	{V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
+-	 IPU6_FW_ISYS_FRAME_FORMAT_YUYV},
+-	{V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
+-	 IPU6_FW_ISYS_FRAME_FORMAT_RGB565},
+-	{V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
+-	 IPU6_FW_ISYS_FRAME_FORMAT_RGBA888},
+-	{V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8, 0},
+-	{V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10, 0},
+-	{V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12, 0},
+-	{V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16, 0},
+-	{V4L2_META_FMT_GENERIC_CSI2_24, 24, 24, MEDIA_BUS_FMT_META_24, 0},
+ };
+ 
+ static int video_open(struct file *file)
+@@ -114,14 +112,27 @@ static int video_release(struct file *file)
+ 	return vb2_fop_release(file);
+ }
+ 
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_get_pixelformat_by_idx(unsigned int idx)
++{
++	if (idx < ARRAY_SIZE(ipu6_isys_pfmts))
++		return &ipu6_isys_pfmts[idx];
++
++	idx -= ARRAY_SIZE(ipu6_isys_pfmts);
++
++	if (idx < ARRAY_SIZE(ipu6_isys_pfmts_packed))
++		return &ipu6_isys_pfmts_packed[idx];
++
++	return NULL;
++}
++
+ static const struct ipu6_isys_pixelformat *
+ ipu6_isys_get_pixelformat(u32 pixelformat)
+ {
++	const struct ipu6_isys_pixelformat *pfmt;
+ 	unsigned int i;
+ 
+-	for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
+-		const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i];
+-
++	for (i = 0; (pfmt = ipu6_isys_get_pixelformat_by_idx(i)); i++) {
+ 		if (pfmt->pixelformat == pixelformat)
+ 			return pfmt;
+ 	}
+@@ -143,24 +154,34 @@ int ipu6_isys_vidioc_querycap(struct file *file, void *fh,
+ int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,
+ 			      struct v4l2_fmtdesc *f)
+ {
+-	unsigned int i, found = 0;
++	struct ipu6_isys_video *av = video_drvdata(file);
++	const struct ipu6_isys_pixelformat *fmt;
++	unsigned int i, nfmts, found = 0;
++
++	nfmts = ARRAY_SIZE(ipu6_isys_pfmts);
++	/* Disable packed formats on TGL for now, TGL has 8 CSI ports */
++	if (av->isys->pdata->ipdata->csi2.nports != 8)
++		nfmts += ARRAY_SIZE(ipu6_isys_pfmts_packed);
+ 
+-	if (f->index >= ARRAY_SIZE(ipu6_isys_pfmts))
++	if (f->index >= nfmts)
+ 		return -EINVAL;
+ 
+ 	if (!f->mbus_code) {
++		fmt = ipu6_isys_get_pixelformat_by_idx(f->index);
+ 		f->flags = 0;
+-		f->pixelformat = ipu6_isys_pfmts[f->index].pixelformat;
++		f->pixelformat = fmt->pixelformat;
+ 		return 0;
+ 	}
+ 
+-	for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
+-		if (f->mbus_code != ipu6_isys_pfmts[i].code)
++	for (i = 0; i < nfmts; i++) {
++		fmt = ipu6_isys_get_pixelformat_by_idx(i);
++
++		if (f->mbus_code != fmt->code)
+ 			continue;
+ 
+ 		if (f->index == found) {
+ 			f->flags = 0;
+-			f->pixelformat = ipu6_isys_pfmts[i].pixelformat;
++			f->pixelformat = fmt->pixelformat;
+ 			return 0;
+ 		}
+ 		found++;
+-- 
+2.43.2
+
+
+From ed407043f03e9af2b09ab8ad449c2716ce7fde01 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 6 Nov 2023 12:13:42 +0100
+Subject: [PATCH 26/33] media: Add ov01a1s driver
+
+Add ov01a1s driver from:
+https://github.com/intel/ipu6-drivers/
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/Kconfig   |    9 +
+ drivers/media/i2c/Makefile  |    1 +
+ drivers/media/i2c/ov01a1s.c | 1191 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 1201 insertions(+)
+ create mode 100644 drivers/media/i2c/ov01a1s.c
+
+diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
+index 4c3435921f19..08f934740980 100644
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -313,6 +313,15 @@ config VIDEO_OV01A10
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called ov01a10.
+ 
++config VIDEO_OV01A1S
++	tristate "OmniVision OV01A1S sensor support"
++	help
++	  This is a Video4Linux2 sensor driver for the OmniVision
++	  OV01A1S camera.
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called ov01a1s.
++
+ config VIDEO_OV02A10
+ 	tristate "OmniVision OV02A10 sensor support"
+ 	help
+diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
+index dfbe6448b549..53ea54c2cf01 100644
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -76,6 +76,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
+ obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
+ obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o
+ obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o
++obj-$(CONFIG_VIDEO_OV01A1S) += ov01a1s.o
+ obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o
+ obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o
+ obj-$(CONFIG_VIDEO_OV08X40) += ov08x40.o
+diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c
+new file mode 100644
+index 000000000000..0dcce8b492b4
+--- /dev/null
++++ b/drivers/media/i2c/ov01a1s.c
+@@ -0,0 +1,1191 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2020-2022 Intel Corporation.
++
++#include <asm/unaligned.h>
++#include <linux/acpi.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/version.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-fwnode.h>
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++#include <linux/clk.h>
++#include <linux/gpio/consumer.h>
++#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++#include "power_ctrl_logic.h"
++#endif
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++#include <linux/vsc.h>
++#endif
++
++#define OV01A1S_LINK_FREQ_400MHZ	400000000ULL
++#define OV01A1S_SCLK			40000000LL
++#define OV01A1S_MCLK			19200000
++#define OV01A1S_DATA_LANES		1
++#define OV01A1S_RGB_DEPTH		10
++
++#define OV01A1S_REG_CHIP_ID		0x300a
++#define OV01A1S_CHIP_ID			0x560141
++
++#define OV01A1S_REG_MODE_SELECT		0x0100
++#define OV01A1S_MODE_STANDBY		0x00
++#define OV01A1S_MODE_STREAMING		0x01
++
++/* vertical-timings from sensor */
++#define OV01A1S_REG_VTS			0x380e
++#define OV01A1S_VTS_DEF			0x0380
++#define OV01A1S_VTS_MIN			0x0380
++#define OV01A1S_VTS_MAX			0xffff
++
++/* Exposure controls from sensor */
++#define OV01A1S_REG_EXPOSURE		0x3501
++#define OV01A1S_EXPOSURE_MIN		4
++#define OV01A1S_EXPOSURE_MAX_MARGIN	8
++#define OV01A1S_EXPOSURE_STEP		1
++
++/* Analog gain controls from sensor */
++#define OV01A1S_REG_ANALOG_GAIN		0x3508
++#define OV01A1S_ANAL_GAIN_MIN		0x100
++#define OV01A1S_ANAL_GAIN_MAX		0xffff
++#define OV01A1S_ANAL_GAIN_STEP		1
++
++/* Digital gain controls from sensor */
++#define OV01A1S_REG_DIGILAL_GAIN_B	0x350A
++#define OV01A1S_REG_DIGITAL_GAIN_GB	0x3510
++#define OV01A1S_REG_DIGITAL_GAIN_GR	0x3513
++#define OV01A1S_REG_DIGITAL_GAIN_R	0x3516
++#define OV01A1S_DGTL_GAIN_MIN		0
++#define OV01A1S_DGTL_GAIN_MAX		0x3ffff
++#define OV01A1S_DGTL_GAIN_STEP		1
++#define OV01A1S_DGTL_GAIN_DEFAULT	1024
++
++/* Test Pattern Control */
++#define OV01A1S_REG_TEST_PATTERN		0x4503
++#define OV01A1S_TEST_PATTERN_ENABLE	BIT(7)
++#define OV01A1S_TEST_PATTERN_BAR_SHIFT	0
++
++enum {
++	OV01A1S_LINK_FREQ_400MHZ_INDEX,
++};
++
++struct ov01a1s_reg {
++	u16 address;
++	u8 val;
++};
++
++struct ov01a1s_reg_list {
++	u32 num_of_regs;
++	const struct ov01a1s_reg *regs;
++};
++
++struct ov01a1s_link_freq_config {
++	const struct ov01a1s_reg_list reg_list;
++};
++
++struct ov01a1s_mode {
++	/* Frame width in pixels */
++	u32 width;
++
++	/* Frame height in pixels */
++	u32 height;
++
++	/* Horizontal timining size */
++	u32 hts;
++
++	/* Default vertical timining size */
++	u32 vts_def;
++
++	/* Min vertical timining size */
++	u32 vts_min;
++
++	/* Link frequency needed for this resolution */
++	u32 link_freq_index;
++
++	/* Sensor register settings for this resolution */
++	const struct ov01a1s_reg_list reg_list;
++};
++
++static const struct ov01a1s_reg mipi_data_rate_720mbps[] = {
++};
++
++static const struct ov01a1s_reg sensor_1296x800_setting[] = {
++	{0x0103, 0x01},
++	{0x0302, 0x00},
++	{0x0303, 0x06},
++	{0x0304, 0x01},
++	{0x0305, 0x90},
++	{0x0306, 0x00},
++	{0x0308, 0x01},
++	{0x0309, 0x00},
++	{0x030c, 0x01},
++	{0x0322, 0x01},
++	{0x0323, 0x06},
++	{0x0324, 0x01},
++	{0x0325, 0x68},
++	{0x3002, 0xa1},
++	{0x301e, 0xf0},
++	{0x3022, 0x01},
++	{0x3501, 0x03},
++	{0x3502, 0x78},
++	{0x3504, 0x0c},
++	{0x3508, 0x01},
++	{0x3509, 0x00},
++	{0x3601, 0xc0},
++	{0x3603, 0x71},
++	{0x3610, 0x68},
++	{0x3611, 0x86},
++	{0x3640, 0x10},
++	{0x3641, 0x80},
++	{0x3642, 0xdc},
++	{0x3646, 0x55},
++	{0x3647, 0x57},
++	{0x364b, 0x00},
++	{0x3653, 0x10},
++	{0x3655, 0x00},
++	{0x3656, 0x00},
++	{0x365f, 0x0f},
++	{0x3661, 0x45},
++	{0x3662, 0x24},
++	{0x3663, 0x11},
++	{0x3664, 0x07},
++	{0x3709, 0x34},
++	{0x370b, 0x6f},
++	{0x3714, 0x22},
++	{0x371b, 0x27},
++	{0x371c, 0x67},
++	{0x371d, 0xa7},
++	{0x371e, 0xe7},
++	{0x3730, 0x81},
++	{0x3733, 0x10},
++	{0x3734, 0x40},
++	{0x3737, 0x04},
++	{0x3739, 0x1c},
++	{0x3767, 0x00},
++	{0x376c, 0x81},
++	{0x3772, 0x14},
++	{0x37c2, 0x04},
++	{0x37d8, 0x03},
++	{0x37d9, 0x0c},
++	{0x37e0, 0x00},
++	{0x37e1, 0x08},
++	{0x37e2, 0x10},
++	{0x37e3, 0x04},
++	{0x37e4, 0x04},
++	{0x37e5, 0x03},
++	{0x37e6, 0x04},
++	{0x3800, 0x00},
++	{0x3801, 0x00},
++	{0x3802, 0x00},
++	{0x3803, 0x00},
++	{0x3804, 0x05},
++	{0x3805, 0x0f},
++	{0x3806, 0x03},
++	{0x3807, 0x2f},
++	{0x3808, 0x05},
++	{0x3809, 0x00},
++	{0x380a, 0x03},
++	{0x380b, 0x1e},
++	{0x380c, 0x05},
++	{0x380d, 0xd0},
++	{0x380e, 0x03},
++	{0x380f, 0x80},
++	{0x3810, 0x00},
++	{0x3811, 0x09},
++	{0x3812, 0x00},
++	{0x3813, 0x08},
++	{0x3814, 0x01},
++	{0x3815, 0x01},
++	{0x3816, 0x01},
++	{0x3817, 0x01},
++	{0x3820, 0xa8},
++	{0x3822, 0x03},
++	{0x3832, 0x28},
++	{0x3833, 0x10},
++	{0x3b00, 0x00},
++	{0x3c80, 0x00},
++	{0x3c88, 0x02},
++	{0x3c8c, 0x07},
++	{0x3c8d, 0x40},
++	{0x3cc7, 0x80},
++	{0x4000, 0xc3},
++	{0x4001, 0xe0},
++	{0x4003, 0x40},
++	{0x4008, 0x02},
++	{0x4009, 0x19},
++	{0x400a, 0x01},
++	{0x400b, 0x6c},
++	{0x4011, 0x00},
++	{0x4041, 0x00},
++	{0x4300, 0xff},
++	{0x4301, 0x00},
++	{0x4302, 0x0f},
++	{0x4503, 0x00},
++	{0x4601, 0x50},
++	{0x481f, 0x34},
++	{0x4825, 0x33},
++	{0x4837, 0x14},
++	{0x4881, 0x40},
++	{0x4883, 0x01},
++	{0x4890, 0x00},
++	{0x4901, 0x00},
++	{0x4902, 0x00},
++	{0x4b00, 0x2a},
++	{0x4b0d, 0x00},
++	{0x450a, 0x04},
++	{0x450b, 0x00},
++	{0x5000, 0x65},
++	{0x5004, 0x00},
++	{0x5080, 0x40},
++	{0x5200, 0x18},
++	{0x4837, 0x14},
++	{0x0305, 0xf4},
++	{0x0325, 0xc2},
++	{0x3808, 0x05},
++	{0x3809, 0x10},
++	{0x380a, 0x03},
++	{0x380b, 0x1e},
++	{0x3810, 0x00},
++	{0x3811, 0x00},
++	{0x3812, 0x00},
++	{0x3813, 0x09},
++	{0x3820, 0x88},
++	{0x373d, 0x24},
++};
++
++static const char * const ov01a1s_test_pattern_menu[] = {
++	"Disabled",
++	"Color Bar",
++	"Top-Bottom Darker Color Bar",
++	"Right-Left Darker Color Bar",
++	"Color Bar type 4",
++};
++
++static const s64 link_freq_menu_items[] = {
++	OV01A1S_LINK_FREQ_400MHZ,
++};
++
++static const struct ov01a1s_link_freq_config link_freq_configs[] = {
++	[OV01A1S_LINK_FREQ_400MHZ_INDEX] = {
++		.reg_list = {
++			.num_of_regs = ARRAY_SIZE(mipi_data_rate_720mbps),
++			.regs = mipi_data_rate_720mbps,
++		}
++	},
++};
++
++static const struct ov01a1s_mode supported_modes[] = {
++	{
++		.width = 1296,
++		.height = 798,
++		.hts = 1488,
++		.vts_def = OV01A1S_VTS_DEF,
++		.vts_min = OV01A1S_VTS_MIN,
++		.reg_list = {
++			.num_of_regs = ARRAY_SIZE(sensor_1296x800_setting),
++			.regs = sensor_1296x800_setting,
++		},
++		.link_freq_index = OV01A1S_LINK_FREQ_400MHZ_INDEX,
++	},
++};
++
++struct ov01a1s {
++	struct v4l2_subdev sd;
++	struct media_pad pad;
++	struct v4l2_ctrl_handler ctrl_handler;
++
++	/* V4L2 Controls */
++	struct v4l2_ctrl *link_freq;
++	struct v4l2_ctrl *pixel_rate;
++	struct v4l2_ctrl *vblank;
++	struct v4l2_ctrl *hblank;
++	struct v4l2_ctrl *exposure;
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	struct v4l2_ctrl *privacy_status;
++
++	/* VSC settings */
++	struct vsc_mipi_config conf;
++	struct vsc_camera_status status;
++#endif
++
++	/* Current mode */
++	const struct ov01a1s_mode *cur_mode;
++
++	/* To serialize asynchronus callbacks */
++	struct mutex mutex;
++
++	/* i2c client */
++	struct i2c_client *client;
++
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	/* GPIO for reset */
++	struct gpio_desc *reset_gpio;
++	/* GPIO for powerdown */
++	struct gpio_desc *powerdown_gpio;
++	/* Power enable */
++	struct regulator *avdd;
++	/* Clock provider */
++	struct clk *clk;
++#endif
++
++	enum {
++		OV01A1S_USE_DEFAULT = 0,
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) || IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++		OV01A1S_USE_INT3472 = 1,
++#endif
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++		OV01A1S_USE_INTEL_VSC = 2,
++#endif
++	} power_type;
++
++	/* Streaming on/off */
++	bool streaming;
++};
++
++static inline struct ov01a1s *to_ov01a1s(struct v4l2_subdev *subdev)
++{
++	return container_of(subdev, struct ov01a1s, sd);
++}
++
++static int ov01a1s_read_reg(struct ov01a1s *ov01a1s, u16 reg, u16 len, u32 *val)
++{
++	struct i2c_client *client = ov01a1s->client;
++	struct i2c_msg msgs[2];
++	u8 addr_buf[2];
++	u8 data_buf[4] = {0};
++	int ret = 0;
++
++	if (len > sizeof(data_buf))
++		return -EINVAL;
++
++	put_unaligned_be16(reg, addr_buf);
++	msgs[0].addr = client->addr;
++	msgs[0].flags = 0;
++	msgs[0].len = sizeof(addr_buf);
++	msgs[0].buf = addr_buf;
++	msgs[1].addr = client->addr;
++	msgs[1].flags = I2C_M_RD;
++	msgs[1].len = len;
++	msgs[1].buf = &data_buf[sizeof(data_buf) - len];
++
++	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++	if (ret != ARRAY_SIZE(msgs))
++		return ret < 0 ? ret : -EIO;
++
++	*val = get_unaligned_be32(data_buf);
++
++	return 0;
++}
++
++static int ov01a1s_write_reg(struct ov01a1s *ov01a1s, u16 reg, u16 len, u32 val)
++{
++	struct i2c_client *client = ov01a1s->client;
++	u8 buf[6];
++	int ret = 0;
++
++	if (len > 4)
++		return -EINVAL;
++
++	put_unaligned_be16(reg, buf);
++	put_unaligned_be32(val << 8 * (4 - len), buf + 2);
++
++	ret = i2c_master_send(client, buf, len + 2);
++	if (ret != len + 2)
++		return ret < 0 ? ret : -EIO;
++
++	return 0;
++}
++
++static int ov01a1s_write_reg_list(struct ov01a1s *ov01a1s,
++				  const struct ov01a1s_reg_list *r_list)
++{
++	struct i2c_client *client = ov01a1s->client;
++	unsigned int i;
++	int ret = 0;
++
++	for (i = 0; i < r_list->num_of_regs; i++) {
++		ret = ov01a1s_write_reg(ov01a1s, r_list->regs[i].address, 1,
++					r_list->regs[i].val);
++		if (ret) {
++			dev_err_ratelimited(&client->dev,
++					    "write reg 0x%4.4x return err = %d",
++					    r_list->regs[i].address, ret);
++			return ret;
++		}
++	}
++
++	return 0;
++}
++
++static int ov01a1s_update_digital_gain(struct ov01a1s *ov01a1s, u32 d_gain)
++{
++	struct i2c_client *client = ov01a1s->client;
++	u32 real = d_gain << 6;
++	int ret = 0;
++
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_DIGILAL_GAIN_B, 3, real);
++	if (ret) {
++		dev_err(&client->dev, "failed to set OV01A1S_REG_DIGITAL_GAIN_B");
++		return ret;
++	}
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_DIGITAL_GAIN_GB, 3, real);
++	if (ret) {
++		dev_err(&client->dev, "failed to set OV01A1S_REG_DIGITAL_GAIN_GB");
++		return ret;
++	}
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_DIGITAL_GAIN_GR, 3, real);
++	if (ret) {
++		dev_err(&client->dev, "failed to set OV01A1S_REG_DIGITAL_GAIN_GR");
++		return ret;
++	}
++
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_DIGITAL_GAIN_R, 3, real);
++	if (ret) {
++		dev_err(&client->dev, "failed to set OV01A1S_REG_DIGITAL_GAIN_R");
++		return ret;
++	}
++	return ret;
++}
++
++static int ov01a1s_test_pattern(struct ov01a1s *ov01a1s, u32 pattern)
++{
++	if (pattern)
++		pattern = (pattern - 1) << OV01A1S_TEST_PATTERN_BAR_SHIFT |
++			  OV01A1S_TEST_PATTERN_ENABLE;
++
++	return ov01a1s_write_reg(ov01a1s, OV01A1S_REG_TEST_PATTERN, 1, pattern);
++}
++
++static int ov01a1s_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++	struct ov01a1s *ov01a1s = container_of(ctrl->handler,
++					     struct ov01a1s, ctrl_handler);
++	struct i2c_client *client = ov01a1s->client;
++	s64 exposure_max;
++	int ret = 0;
++
++	/* Propagate change of current control to all related controls */
++	if (ctrl->id == V4L2_CID_VBLANK) {
++		/* Update max exposure while meeting expected vblanking */
++		exposure_max = ov01a1s->cur_mode->height + ctrl->val -
++			       OV01A1S_EXPOSURE_MAX_MARGIN;
++		__v4l2_ctrl_modify_range(ov01a1s->exposure,
++					 ov01a1s->exposure->minimum,
++					 exposure_max, ov01a1s->exposure->step,
++					 exposure_max);
++	}
++
++	/* V4L2 controls values will be applied only when power is already up */
++	if (!pm_runtime_get_if_in_use(&client->dev))
++		return 0;
++
++	switch (ctrl->id) {
++	case V4L2_CID_ANALOGUE_GAIN:
++		ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_ANALOG_GAIN, 2,
++					ctrl->val);
++		break;
++
++	case V4L2_CID_DIGITAL_GAIN:
++		ret = ov01a1s_update_digital_gain(ov01a1s, ctrl->val);
++		break;
++
++	case V4L2_CID_EXPOSURE:
++		ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_EXPOSURE, 2,
++					ctrl->val);
++		break;
++
++	case V4L2_CID_VBLANK:
++		ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_VTS, 2,
++					ov01a1s->cur_mode->height + ctrl->val);
++		break;
++
++	case V4L2_CID_TEST_PATTERN:
++		ret = ov01a1s_test_pattern(ov01a1s, ctrl->val);
++		break;
++
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	case V4L2_CID_PRIVACY:
++		dev_dbg(&client->dev, "set privacy to %d", ctrl->val);
++		break;
++
++#endif
++	default:
++		ret = -EINVAL;
++		break;
++	}
++
++	pm_runtime_put(&client->dev);
++
++	return ret;
++}
++
++static const struct v4l2_ctrl_ops ov01a1s_ctrl_ops = {
++	.s_ctrl = ov01a1s_set_ctrl,
++};
++
++static int ov01a1s_init_controls(struct ov01a1s *ov01a1s)
++{
++	struct v4l2_ctrl_handler *ctrl_hdlr;
++	const struct ov01a1s_mode *cur_mode;
++	s64 exposure_max, h_blank;
++	u32 vblank_min, vblank_max, vblank_default;
++	int size;
++	int ret = 0;
++
++	ctrl_hdlr = &ov01a1s->ctrl_handler;
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
++#else
++	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
++#endif
++	if (ret)
++		return ret;
++
++	ctrl_hdlr->lock = &ov01a1s->mutex;
++	cur_mode = ov01a1s->cur_mode;
++	size = ARRAY_SIZE(link_freq_menu_items);
++
++	ov01a1s->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
++						    &ov01a1s_ctrl_ops,
++						    V4L2_CID_LINK_FREQ,
++						    size - 1, 0,
++						    link_freq_menu_items);
++	if (ov01a1s->link_freq)
++		ov01a1s->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
++	ov01a1s->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops,
++						V4L2_CID_PIXEL_RATE, 0,
++						OV01A1S_SCLK, 1, OV01A1S_SCLK);
++
++	vblank_min = cur_mode->vts_min - cur_mode->height;
++	vblank_max = OV01A1S_VTS_MAX - cur_mode->height;
++	vblank_default = cur_mode->vts_def - cur_mode->height;
++	ov01a1s->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops,
++					    V4L2_CID_VBLANK, vblank_min,
++					    vblank_max, 1, vblank_default);
++
++	h_blank = cur_mode->hts - cur_mode->width;
++	ov01a1s->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops,
++					    V4L2_CID_HBLANK, h_blank, h_blank,
++					    1, h_blank);
++	if (ov01a1s->hblank)
++		ov01a1s->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	ov01a1s->privacy_status = v4l2_ctrl_new_std(ctrl_hdlr,
++						    &ov01a1s_ctrl_ops,
++						    V4L2_CID_PRIVACY,
++						    0, 1, 1, 0);
++#endif
++
++	v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++			  OV01A1S_ANAL_GAIN_MIN, OV01A1S_ANAL_GAIN_MAX,
++			  OV01A1S_ANAL_GAIN_STEP, OV01A1S_ANAL_GAIN_MIN);
++	v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
++			  OV01A1S_DGTL_GAIN_MIN, OV01A1S_DGTL_GAIN_MAX,
++			  OV01A1S_DGTL_GAIN_STEP, OV01A1S_DGTL_GAIN_DEFAULT);
++	exposure_max = cur_mode->vts_def - OV01A1S_EXPOSURE_MAX_MARGIN;
++	ov01a1s->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops,
++					      V4L2_CID_EXPOSURE,
++					      OV01A1S_EXPOSURE_MIN,
++					      exposure_max,
++					      OV01A1S_EXPOSURE_STEP,
++					      exposure_max);
++	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov01a1s_ctrl_ops,
++				     V4L2_CID_TEST_PATTERN,
++				     ARRAY_SIZE(ov01a1s_test_pattern_menu) - 1,
++				     0, 0, ov01a1s_test_pattern_menu);
++	if (ctrl_hdlr->error)
++		return ctrl_hdlr->error;
++
++	ov01a1s->sd.ctrl_handler = ctrl_hdlr;
++
++	return 0;
++}
++
++static void ov01a1s_update_pad_format(const struct ov01a1s_mode *mode,
++				      struct v4l2_mbus_framefmt *fmt)
++{
++	fmt->width = mode->width;
++	fmt->height = mode->height;
++	fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
++	fmt->field = V4L2_FIELD_NONE;
++}
++
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++static void ov01a1s_vsc_privacy_callback(void *handle,
++				       enum vsc_privacy_status status)
++{
++	struct ov01a1s *ov01a1s = handle;
++
++	v4l2_ctrl_s_ctrl(ov01a1s->privacy_status, !status);
++}
++
++#endif
++static int ov01a1s_start_streaming(struct ov01a1s *ov01a1s)
++{
++	struct i2c_client *client = ov01a1s->client;
++	const struct ov01a1s_reg_list *reg_list;
++	int link_freq_index;
++	int ret = 0;
++
++	link_freq_index = ov01a1s->cur_mode->link_freq_index;
++	reg_list = &link_freq_configs[link_freq_index].reg_list;
++	ret = ov01a1s_write_reg_list(ov01a1s, reg_list);
++	if (ret) {
++		dev_err(&client->dev, "failed to set plls");
++		return ret;
++	}
++
++	reg_list = &ov01a1s->cur_mode->reg_list;
++	ret = ov01a1s_write_reg_list(ov01a1s, reg_list);
++	if (ret) {
++		dev_err(&client->dev, "failed to set mode");
++		return ret;
++	}
++
++	ret = __v4l2_ctrl_handler_setup(ov01a1s->sd.ctrl_handler);
++	if (ret)
++		return ret;
++
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_MODE_SELECT, 1,
++				OV01A1S_MODE_STREAMING);
++	if (ret)
++		dev_err(&client->dev, "failed to start streaming");
++
++	return ret;
++}
++
++static void ov01a1s_stop_streaming(struct ov01a1s *ov01a1s)
++{
++	struct i2c_client *client = ov01a1s->client;
++	int ret = 0;
++
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_MODE_SELECT, 1,
++				OV01A1S_MODE_STANDBY);
++	if (ret)
++		dev_err(&client->dev, "failed to stop streaming");
++}
++
++static int ov01a1s_set_stream(struct v4l2_subdev *sd, int enable)
++{
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	struct i2c_client *client = ov01a1s->client;
++	int ret = 0;
++
++	if (ov01a1s->streaming == enable)
++		return 0;
++
++	mutex_lock(&ov01a1s->mutex);
++	if (enable) {
++		ret = pm_runtime_get_sync(&client->dev);
++		if (ret < 0) {
++			pm_runtime_put_noidle(&client->dev);
++			mutex_unlock(&ov01a1s->mutex);
++			return ret;
++		}
++
++		ret = ov01a1s_start_streaming(ov01a1s);
++		if (ret) {
++			enable = 0;
++			ov01a1s_stop_streaming(ov01a1s);
++			pm_runtime_put(&client->dev);
++		}
++	} else {
++		ov01a1s_stop_streaming(ov01a1s);
++		pm_runtime_put(&client->dev);
++	}
++
++	ov01a1s->streaming = enable;
++	mutex_unlock(&ov01a1s->mutex);
++
++	return ret;
++}
++
++static int ov01a1s_power_off(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	int ret = 0;
++
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472) {
++		gpiod_set_value_cansleep(ov01a1s->reset_gpio, 1);
++		gpiod_set_value_cansleep(ov01a1s->powerdown_gpio, 1);
++		if (ov01a1s->avdd)
++			ret = regulator_disable(ov01a1s->avdd);
++		clk_disable_unprepare(ov01a1s->clk);
++		msleep(20);
++	}
++#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
++		ret = power_ctrl_logic_set_power(0);
++#endif
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) {
++		ret = vsc_release_camera_sensor(&ov01a1s->status);
++		if (ret && ret != -EAGAIN)
++			dev_err(dev, "Release VSC failed");
++	}
++#endif
++
++	return ret;
++}
++
++static int ov01a1s_power_on(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	int ret = 0;
++
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472) {
++		ret = clk_prepare_enable(ov01a1s->clk);
++		if (ret)
++			return ret;
++		if (ov01a1s->avdd)
++			ret = regulator_enable(ov01a1s->avdd);
++		if (ret)
++			return ret;
++		gpiod_set_value_cansleep(ov01a1s->powerdown_gpio, 0);
++		gpiod_set_value_cansleep(ov01a1s->reset_gpio, 0);
++		msleep(20);
++	}
++#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
++		ret = power_ctrl_logic_set_power(1);
++#endif
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) {
++		ret = vsc_acquire_camera_sensor(&ov01a1s->conf,
++						ov01a1s_vsc_privacy_callback,
++						ov01a1s, &ov01a1s->status);
++		if (ret && ret != -EAGAIN) {
++			dev_err(dev, "Acquire VSC failed");
++			return ret;
++		}
++		__v4l2_ctrl_s_ctrl(ov01a1s->privacy_status,
++				   !(ov01a1s->status.status));
++	}
++#endif
++
++	return ret;
++}
++
++static int __maybe_unused ov01a1s_suspend(struct device *dev)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct v4l2_subdev *sd = i2c_get_clientdata(client);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++
++	mutex_lock(&ov01a1s->mutex);
++	if (ov01a1s->streaming)
++		ov01a1s_stop_streaming(ov01a1s);
++
++	mutex_unlock(&ov01a1s->mutex);
++
++	return 0;
++}
++
++static int __maybe_unused ov01a1s_resume(struct device *dev)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct v4l2_subdev *sd = i2c_get_clientdata(client);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	int ret = 0;
++
++	mutex_lock(&ov01a1s->mutex);
++	if (!ov01a1s->streaming)
++		goto exit;
++
++	ret = ov01a1s_start_streaming(ov01a1s);
++	if (ret) {
++		ov01a1s->streaming = false;
++		ov01a1s_stop_streaming(ov01a1s);
++	}
++
++exit:
++	mutex_unlock(&ov01a1s->mutex);
++	return ret;
++}
++
++static int ov01a1s_set_format(struct v4l2_subdev *sd,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++			      struct v4l2_subdev_pad_config *cfg,
++#else
++			      struct v4l2_subdev_state *sd_state,
++#endif
++			      struct v4l2_subdev_format *fmt)
++{
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	const struct ov01a1s_mode *mode;
++	s32 vblank_def, h_blank;
++
++	mode = v4l2_find_nearest_size(supported_modes,
++				      ARRAY_SIZE(supported_modes), width,
++				      height, fmt->format.width,
++				      fmt->format.height);
++
++	mutex_lock(&ov01a1s->mutex);
++	ov01a1s_update_pad_format(mode, &fmt->format);
++	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
++#else
++		*v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format;
++#endif
++	} else {
++		ov01a1s->cur_mode = mode;
++		__v4l2_ctrl_s_ctrl(ov01a1s->link_freq, mode->link_freq_index);
++		__v4l2_ctrl_s_ctrl_int64(ov01a1s->pixel_rate, OV01A1S_SCLK);
++
++		/* Update limits and set FPS to default */
++		vblank_def = mode->vts_def - mode->height;
++		__v4l2_ctrl_modify_range(ov01a1s->vblank,
++					 mode->vts_min - mode->height,
++					 OV01A1S_VTS_MAX - mode->height, 1,
++					 vblank_def);
++		__v4l2_ctrl_s_ctrl(ov01a1s->vblank, vblank_def);
++		h_blank = mode->hts - mode->width;
++		__v4l2_ctrl_modify_range(ov01a1s->hblank, h_blank, h_blank, 1,
++					 h_blank);
++	}
++	mutex_unlock(&ov01a1s->mutex);
++
++	return 0;
++}
++
++static int ov01a1s_get_format(struct v4l2_subdev *sd,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++			      struct v4l2_subdev_pad_config *cfg,
++#else
++			      struct v4l2_subdev_state *sd_state,
++#endif
++			      struct v4l2_subdev_format *fmt)
++{
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++
++	mutex_lock(&ov01a1s->mutex);
++	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++		fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd, cfg,
++							  fmt->pad);
++#else
++		fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd,
++							  sd_state, fmt->pad);
++#endif
++	else
++		ov01a1s_update_pad_format(ov01a1s->cur_mode, &fmt->format);
++
++	mutex_unlock(&ov01a1s->mutex);
++
++	return 0;
++}
++
++static int ov01a1s_enum_mbus_code(struct v4l2_subdev *sd,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++				  struct v4l2_subdev_pad_config *cfg,
++#else
++				  struct v4l2_subdev_state *sd_state,
++#endif
++				  struct v4l2_subdev_mbus_code_enum *code)
++{
++	if (code->index > 0)
++		return -EINVAL;
++
++	code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
++
++	return 0;
++}
++
++static int ov01a1s_enum_frame_size(struct v4l2_subdev *sd,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++				   struct v4l2_subdev_pad_config *cfg,
++#else
++				   struct v4l2_subdev_state *sd_state,
++#endif
++				   struct v4l2_subdev_frame_size_enum *fse)
++{
++	if (fse->index >= ARRAY_SIZE(supported_modes))
++		return -EINVAL;
++
++	if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
++		return -EINVAL;
++
++	fse->min_width = supported_modes[fse->index].width;
++	fse->max_width = fse->min_width;
++	fse->min_height = supported_modes[fse->index].height;
++	fse->max_height = fse->min_height;
++
++	return 0;
++}
++
++static int ov01a1s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++
++	mutex_lock(&ov01a1s->mutex);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++	ov01a1s_update_pad_format(&supported_modes[0],
++				  v4l2_subdev_get_try_format(sd, fh->pad, 0));
++#else
++	ov01a1s_update_pad_format(&supported_modes[0],
++				  v4l2_subdev_get_try_format(sd, fh->state, 0));
++#endif
++	mutex_unlock(&ov01a1s->mutex);
++
++	return 0;
++}
++
++static const struct v4l2_subdev_video_ops ov01a1s_video_ops = {
++	.s_stream = ov01a1s_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops ov01a1s_pad_ops = {
++	.set_fmt = ov01a1s_set_format,
++	.get_fmt = ov01a1s_get_format,
++	.enum_mbus_code = ov01a1s_enum_mbus_code,
++	.enum_frame_size = ov01a1s_enum_frame_size,
++};
++
++static const struct v4l2_subdev_ops ov01a1s_subdev_ops = {
++	.video = &ov01a1s_video_ops,
++	.pad = &ov01a1s_pad_ops,
++};
++
++static const struct media_entity_operations ov01a1s_subdev_entity_ops = {
++	.link_validate = v4l2_subdev_link_validate,
++};
++
++static const struct v4l2_subdev_internal_ops ov01a1s_internal_ops = {
++	.open = ov01a1s_open,
++};
++
++static int ov01a1s_identify_module(struct ov01a1s *ov01a1s)
++{
++	struct i2c_client *client = ov01a1s->client;
++	int ret;
++	u32 val;
++
++	ret = ov01a1s_read_reg(ov01a1s, OV01A1S_REG_CHIP_ID, 3, &val);
++	if (ret)
++		return ret;
++
++	if (val != OV01A1S_CHIP_ID) {
++		dev_err(&client->dev, "chip id mismatch: %x!=%x",
++			OV01A1S_CHIP_ID, val);
++		return -ENXIO;
++	}
++
++	return 0;
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
++static int ov01a1s_remove(struct i2c_client *client)
++#else
++static void ov01a1s_remove(struct i2c_client *client)
++#endif
++{
++	struct v4l2_subdev *sd = i2c_get_clientdata(client);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++
++	v4l2_async_unregister_subdev(sd);
++	media_entity_cleanup(&sd->entity);
++	v4l2_ctrl_handler_free(sd->ctrl_handler);
++	pm_runtime_disable(&client->dev);
++	mutex_destroy(&ov01a1s->mutex);
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
++	return 0;
++#endif
++}
++
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++static int ov01a1s_parse_gpio(struct ov01a1s *ov01a1s)
++{
++	struct device *dev = &ov01a1s->client->dev;
++
++	ov01a1s->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
++	if (IS_ERR(ov01a1s->reset_gpio)) {
++		dev_warn(dev, "error while getting reset gpio: %ld\n",
++			 PTR_ERR(ov01a1s->reset_gpio));
++		ov01a1s->reset_gpio = NULL;
++		return -EPROBE_DEFER;
++	}
++
++	/* For optional, don't return or print warn if can't get it */
++	ov01a1s->powerdown_gpio =
++		devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW);
++	if (IS_ERR(ov01a1s->powerdown_gpio)) {
++		dev_dbg(dev, "no powerdown gpio: %ld\n",
++			PTR_ERR(ov01a1s->powerdown_gpio));
++		ov01a1s->powerdown_gpio = NULL;
++	}
++
++	ov01a1s->avdd = devm_regulator_get_optional(dev, "avdd");
++	if (IS_ERR(ov01a1s->avdd)) {
++		dev_dbg(dev, "no regulator avdd: %ld\n",
++			PTR_ERR(ov01a1s->avdd));
++		ov01a1s->avdd = NULL;
++	}
++
++	ov01a1s->clk = devm_clk_get_optional(dev, "clk");
++	if (IS_ERR(ov01a1s->clk)) {
++		dev_dbg(dev, "no clk: %ld\n", PTR_ERR(ov01a1s->clk));
++		ov01a1s->clk = NULL;
++	}
++
++	return 0;
++}
++#endif
++
++static int ov01a1s_parse_power(struct ov01a1s *ov01a1s)
++{
++	int ret = 0;
++
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	ov01a1s->conf.lane_num = OV01A1S_DATA_LANES;
++	/* frequency unit 100k */
++	ov01a1s->conf.freq = OV01A1S_LINK_FREQ_400MHZ / 100000;
++	ret = vsc_acquire_camera_sensor(&ov01a1s->conf, NULL, NULL, &ov01a1s->status);
++	if (!ret) {
++		ov01a1s->power_type = OV01A1S_USE_INTEL_VSC;
++		return 0;
++	} else if (ret != -EAGAIN) {
++		return ret;
++	}
++#endif
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	ret = ov01a1s_parse_gpio(ov01a1s);
++#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++	ret = power_ctrl_logic_set_power(1);
++#endif
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) || IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++	if (!ret) {
++		ov01a1s->power_type = OV01A1S_USE_INT3472;
++		return 0;
++	}
++#endif
++	if (ret == -EAGAIN)
++		return -EPROBE_DEFER;
++
++	return ret;
++}
++
++static int ov01a1s_probe(struct i2c_client *client)
++{
++	struct ov01a1s *ov01a1s;
++	int ret = 0;
++
++	ov01a1s = devm_kzalloc(&client->dev, sizeof(*ov01a1s), GFP_KERNEL);
++	if (!ov01a1s)
++		return -ENOMEM;
++
++	ov01a1s->client = client;
++	ret = ov01a1s_parse_power(ov01a1s);
++	if (ret)
++		return ret;
++
++	v4l2_i2c_subdev_init(&ov01a1s->sd, client, &ov01a1s_subdev_ops);
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	/* In other cases, power is up in ov01a1s_parse_power */
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
++		ov01a1s_power_on(&client->dev);
++#endif
++	ret = ov01a1s_identify_module(ov01a1s);
++	if (ret) {
++		dev_err(&client->dev, "failed to find sensor: %d", ret);
++		goto probe_error_power_off;
++	}
++
++	mutex_init(&ov01a1s->mutex);
++	ov01a1s->cur_mode = &supported_modes[0];
++	ret = ov01a1s_init_controls(ov01a1s);
++	if (ret) {
++		dev_err(&client->dev, "failed to init controls: %d", ret);
++		goto probe_error_v4l2_ctrl_handler_free;
++	}
++
++	ov01a1s->sd.internal_ops = &ov01a1s_internal_ops;
++	ov01a1s->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++	ov01a1s->sd.entity.ops = &ov01a1s_subdev_entity_ops;
++	ov01a1s->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++	ov01a1s->pad.flags = MEDIA_PAD_FL_SOURCE;
++	ret = media_entity_pads_init(&ov01a1s->sd.entity, 1, &ov01a1s->pad);
++	if (ret) {
++		dev_err(&client->dev, "failed to init entity pads: %d", ret);
++		goto probe_error_v4l2_ctrl_handler_free;
++	}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
++	ret = v4l2_async_register_subdev_sensor_common(&ov01a1s->sd);
++#else
++	ret = v4l2_async_register_subdev_sensor(&ov01a1s->sd);
++#endif
++	if (ret < 0) {
++		dev_err(&client->dev, "failed to register V4L2 subdev: %d",
++			ret);
++		goto probe_error_media_entity_cleanup;
++	}
++
++	/*
++	 * Device is already turned on by i2c-core with ACPI domain PM.
++	 * Enable runtime PM and turn off the device.
++	 */
++	pm_runtime_set_active(&client->dev);
++	pm_runtime_enable(&client->dev);
++	pm_runtime_idle(&client->dev);
++
++	return 0;
++
++probe_error_media_entity_cleanup:
++	media_entity_cleanup(&ov01a1s->sd.entity);
++
++probe_error_v4l2_ctrl_handler_free:
++	v4l2_ctrl_handler_free(ov01a1s->sd.ctrl_handler);
++	mutex_destroy(&ov01a1s->mutex);
++
++probe_error_power_off:
++	ov01a1s_power_off(&client->dev);
++
++	return ret;
++}
++
++static const struct dev_pm_ops ov01a1s_pm_ops = {
++	SET_SYSTEM_SLEEP_PM_OPS(ov01a1s_suspend, ov01a1s_resume)
++	SET_RUNTIME_PM_OPS(ov01a1s_power_off, ov01a1s_power_on, NULL)
++};
++
++#ifdef CONFIG_ACPI
++static const struct acpi_device_id ov01a1s_acpi_ids[] = {
++	{ "OVTI01AS" },
++	{}
++};
++
++MODULE_DEVICE_TABLE(acpi, ov01a1s_acpi_ids);
++#endif
++
++static struct i2c_driver ov01a1s_i2c_driver = {
++	.driver = {
++		.name = "ov01a1s",
++		.pm = &ov01a1s_pm_ops,
++		.acpi_match_table = ACPI_PTR(ov01a1s_acpi_ids),
++	},
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
++	.probe_new = ov01a1s_probe,
++#else
++	.probe = ov01a1s_probe,
++#endif
++	.remove = ov01a1s_remove,
++};
++
++module_i2c_driver(ov01a1s_i2c_driver);
++
++MODULE_AUTHOR("Xu, Chongyang <chongyang.xu@intel.com>");
++MODULE_AUTHOR("Lai, Jim <jim.lai@intel.com>");
++MODULE_AUTHOR("Qiu, Tianshu <tian.shu.qiu@intel.com>");
++MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>");
++MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
++MODULE_DESCRIPTION("OmniVision OV01A1S sensor driver");
++MODULE_LICENSE("GPL v2");
+-- 
+2.43.2
+
+
+From 9f58ae728245ad7ac604737ab16781d7ccb2006e Mon Sep 17 00:00:00 2001
+From: Florian Klink <flokli@flokli.de>
+Date: Sun, 17 Mar 2024 14:24:05 +0200
+Subject: [PATCH 27/33] ov01a1s.c: support Linux 6.8.0
+
+Used https://github.com/intel/ipu6-drivers/pull/213 as an inspiration.
+---
+ drivers/media/i2c/ov01a1s.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c
+index 0dcce8b492b4..923b12b2a948 100644
+--- a/drivers/media/i2c/ov01a1s.c
++++ b/drivers/media/i2c/ov01a1s.c
+@@ -832,8 +832,10 @@ static int ov01a1s_set_format(struct v4l2_subdev *sd,
+ 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
+ 		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+-#else
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0)
+ 		*v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format;
++#else
++		*v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
+ #endif
+ 	} else {
+ 		ov01a1s->cur_mode = mode;
+@@ -871,9 +873,11 @@ static int ov01a1s_get_format(struct v4l2_subdev *sd,
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
+ 		fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd, cfg,
+ 							  fmt->pad);
+-#else
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0)
+ 		fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd,
+ 							  sd_state, fmt->pad);
++#else
++		fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad);
+ #endif
+ 	else
+ 		ov01a1s_update_pad_format(ov01a1s->cur_mode, &fmt->format);
+@@ -929,9 +933,12 @@ static int ov01a1s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
+ 	ov01a1s_update_pad_format(&supported_modes[0],
+ 				  v4l2_subdev_get_try_format(sd, fh->pad, 0));
+-#else
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0)
+ 	ov01a1s_update_pad_format(&supported_modes[0],
+ 				  v4l2_subdev_get_try_format(sd, fh->state, 0));
++#else
++	ov01a1s_update_pad_format(&supported_modes[0],
++				  v4l2_subdev_state_get_format(fh->state, 0));
+ #endif
+ 	mutex_unlock(&ov01a1s->mutex);
+ 
+-- 
+2.43.2
+
+
+From 80bee1ca899ebfa4126d1e69ea821a2c30aba00c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 6 Nov 2023 12:33:56 +0100
+Subject: [PATCH 28/33] media: ov01a1s: Remove non upstream iVSC support
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/ov01a1s.c | 71 -------------------------------------
+ 1 file changed, 71 deletions(-)
+
+diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c
+index 923b12b2a948..22b406bdeae9 100644
+--- a/drivers/media/i2c/ov01a1s.c
++++ b/drivers/media/i2c/ov01a1s.c
+@@ -17,9 +17,6 @@
+ #elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
+ #include "power_ctrl_logic.h"
+ #endif
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-#include <linux/vsc.h>
+-#endif
+ 
+ #define OV01A1S_LINK_FREQ_400MHZ	400000000ULL
+ #define OV01A1S_SCLK			40000000LL
+@@ -302,13 +299,6 @@ struct ov01a1s {
+ 	struct v4l2_ctrl *vblank;
+ 	struct v4l2_ctrl *hblank;
+ 	struct v4l2_ctrl *exposure;
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	struct v4l2_ctrl *privacy_status;
+-
+-	/* VSC settings */
+-	struct vsc_mipi_config conf;
+-	struct vsc_camera_status status;
+-#endif
+ 
+ 	/* Current mode */
+ 	const struct ov01a1s_mode *cur_mode;
+@@ -334,9 +324,6 @@ struct ov01a1s {
+ 		OV01A1S_USE_DEFAULT = 0,
+ #if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) || IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
+ 		OV01A1S_USE_INT3472 = 1,
+-#endif
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-		OV01A1S_USE_INTEL_VSC = 2,
+ #endif
+ 	} power_type;
+ 
+@@ -505,12 +492,6 @@ static int ov01a1s_set_ctrl(struct v4l2_ctrl *ctrl)
+ 		ret = ov01a1s_test_pattern(ov01a1s, ctrl->val);
+ 		break;
+ 
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	case V4L2_CID_PRIVACY:
+-		dev_dbg(&client->dev, "set privacy to %d", ctrl->val);
+-		break;
+-
+-#endif
+ 	default:
+ 		ret = -EINVAL;
+ 		break;
+@@ -535,11 +516,7 @@ static int ov01a1s_init_controls(struct ov01a1s *ov01a1s)
+ 	int ret = 0;
+ 
+ 	ctrl_hdlr = &ov01a1s->ctrl_handler;
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+-#else
+ 	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+-#endif
+ 	if (ret)
+ 		return ret;
+ 
+@@ -572,12 +549,6 @@ static int ov01a1s_init_controls(struct ov01a1s *ov01a1s)
+ 					    1, h_blank);
+ 	if (ov01a1s->hblank)
+ 		ov01a1s->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	ov01a1s->privacy_status = v4l2_ctrl_new_std(ctrl_hdlr,
+-						    &ov01a1s_ctrl_ops,
+-						    V4L2_CID_PRIVACY,
+-						    0, 1, 1, 0);
+-#endif
+ 
+ 	v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ 			  OV01A1S_ANAL_GAIN_MIN, OV01A1S_ANAL_GAIN_MAX,
+@@ -613,16 +584,6 @@ static void ov01a1s_update_pad_format(const struct ov01a1s_mode *mode,
+ 	fmt->field = V4L2_FIELD_NONE;
+ }
+ 
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-static void ov01a1s_vsc_privacy_callback(void *handle,
+-				       enum vsc_privacy_status status)
+-{
+-	struct ov01a1s *ov01a1s = handle;
+-
+-	v4l2_ctrl_s_ctrl(ov01a1s->privacy_status, !status);
+-}
+-
+-#endif
+ static int ov01a1s_start_streaming(struct ov01a1s *ov01a1s)
+ {
+ 	struct i2c_client *client = ov01a1s->client;
+@@ -722,13 +683,6 @@ static int ov01a1s_power_off(struct device *dev)
+ 	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
+ 		ret = power_ctrl_logic_set_power(0);
+ #endif
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) {
+-		ret = vsc_release_camera_sensor(&ov01a1s->status);
+-		if (ret && ret != -EAGAIN)
+-			dev_err(dev, "Release VSC failed");
+-	}
+-#endif
+ 
+ 	return ret;
+ }
+@@ -756,19 +710,6 @@ static int ov01a1s_power_on(struct device *dev)
+ 	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
+ 		ret = power_ctrl_logic_set_power(1);
+ #endif
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) {
+-		ret = vsc_acquire_camera_sensor(&ov01a1s->conf,
+-						ov01a1s_vsc_privacy_callback,
+-						ov01a1s, &ov01a1s->status);
+-		if (ret && ret != -EAGAIN) {
+-			dev_err(dev, "Acquire VSC failed");
+-			return ret;
+-		}
+-		__v4l2_ctrl_s_ctrl(ov01a1s->privacy_status,
+-				   !(ov01a1s->status.status));
+-	}
+-#endif
+ 
+ 	return ret;
+ }
+@@ -1051,18 +992,6 @@ static int ov01a1s_parse_power(struct ov01a1s *ov01a1s)
+ {
+ 	int ret = 0;
+ 
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	ov01a1s->conf.lane_num = OV01A1S_DATA_LANES;
+-	/* frequency unit 100k */
+-	ov01a1s->conf.freq = OV01A1S_LINK_FREQ_400MHZ / 100000;
+-	ret = vsc_acquire_camera_sensor(&ov01a1s->conf, NULL, NULL, &ov01a1s->status);
+-	if (!ret) {
+-		ov01a1s->power_type = OV01A1S_USE_INTEL_VSC;
+-		return 0;
+-	} else if (ret != -EAGAIN) {
+-		return ret;
+-	}
+-#endif
+ #if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
+ 	ret = ov01a1s_parse_gpio(ov01a1s);
+ #elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
+-- 
+2.43.2
+
+
+From e624515c64d782b452a4676c1e117815267559ae Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 23 Jan 2024 14:58:35 +0100
+Subject: [PATCH 29/33] media: hi556: Return -EPROBE_DEFER if no endpoint is
+ found
+
+With ipu bridge, endpoints may only be created when ipu bridge has
+initialised. This may happen after the sensor driver has first probed.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/hi556.c | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
+index 38c77d515786..96bae9914d52 100644
+--- a/drivers/media/i2c/hi556.c
++++ b/drivers/media/i2c/hi556.c
+@@ -1206,8 +1206,13 @@ static int hi556_check_hwcfg(struct device *dev)
+ 	int ret = 0;
+ 	unsigned int i, j;
+ 
+-	if (!fwnode)
+-		return -ENXIO;
++	/*
++	 * Sometimes the fwnode graph is initialized by the bridge driver,
++	 * wait for this.
++	 */
++	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
++	if (!ep)
++		return -EPROBE_DEFER;
+ 
+ 	ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
+ 	if (ret) {
+@@ -1220,10 +1225,6 @@ static int hi556_check_hwcfg(struct device *dev)
+ 		return -EINVAL;
+ 	}
+ 
+-	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+-	if (!ep)
+-		return -ENXIO;
+-
+ 	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ 	fwnode_handle_put(ep);
+ 	if (ret)
+-- 
+2.43.2
+
+
+From b127d1003050fb894ea764b600d5f399af413b68 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 23 Jan 2024 14:48:26 +0100
+Subject: [PATCH 30/33] media: hi556: Add support for reset GPIO
+
+On some ACPI platforms, such as Chromebooks the ACPI methods to
+change the power-state (_PS0 and _PS3) fully take care of powering
+on/off the sensor.
+
+On other ACPI platforms, such as e.g. various HP models with IPU6 +
+hi556 sensor, the sensor driver must control the reset GPIO itself.
+
+Add support for having the driver control an optional reset GPIO.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/hi556.c | 45 ++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 44 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
+index 96bae9914d52..f5a39b83598b 100644
+--- a/drivers/media/i2c/hi556.c
++++ b/drivers/media/i2c/hi556.c
+@@ -4,6 +4,7 @@
+ #include <asm/unaligned.h>
+ #include <linux/acpi.h>
+ #include <linux/delay.h>
++#include <linux/gpio/consumer.h>
+ #include <linux/i2c.h>
+ #include <linux/module.h>
+ #include <linux/pm_runtime.h>
+@@ -633,6 +634,9 @@ struct hi556 {
+ 	struct v4l2_ctrl *hblank;
+ 	struct v4l2_ctrl *exposure;
+ 
++	/* GPIOs, clocks, etc. */
++	struct gpio_desc *reset_gpio;
++
+ 	/* Current mode */
+ 	const struct hi556_mode *cur_mode;
+ 
+@@ -1276,6 +1280,25 @@ static void hi556_remove(struct i2c_client *client)
+ 	mutex_destroy(&hi556->mutex);
+ }
+ 
++static int hi556_suspend(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct hi556 *hi556 = to_hi556(sd);
++
++	gpiod_set_value_cansleep(hi556->reset_gpio, 1);
++	return 0;
++}
++
++static int hi556_resume(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct hi556 *hi556 = to_hi556(sd);
++
++	gpiod_set_value_cansleep(hi556->reset_gpio, 0);
++	usleep_range(5000, 5500);
++	return 0;
++}
++
+ static int hi556_probe(struct i2c_client *client)
+ {
+ 	struct hi556 *hi556;
+@@ -1295,12 +1318,24 @@ static int hi556_probe(struct i2c_client *client)
+ 
+ 	v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops);
+ 
++	hi556->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
++						    GPIOD_OUT_HIGH);
++	if (IS_ERR(hi556->reset_gpio))
++		return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio),
++				     "failed to get reset GPIO\n");
++
+ 	full_power = acpi_dev_state_d0(&client->dev);
+ 	if (full_power) {
++		/* Ensure non ACPI managed resources are enabled */
++		ret = hi556_resume(&client->dev);
++		if (ret)
++			return dev_err_probe(&client->dev, ret,
++					     "failed to power on sensor\n");
++
+ 		ret = hi556_identify_module(hi556);
+ 		if (ret) {
+ 			dev_err(&client->dev, "failed to find sensor: %d", ret);
+-			return ret;
++			goto probe_error_power_off;
+ 		}
+ 	}
+ 
+@@ -1345,9 +1380,16 @@ static int hi556_probe(struct i2c_client *client)
+ 	v4l2_ctrl_handler_free(hi556->sd.ctrl_handler);
+ 	mutex_destroy(&hi556->mutex);
+ 
++probe_error_power_off:
++	if (full_power)
++		hi556_suspend(&client->dev);
++
+ 	return ret;
+ }
+ 
++static DEFINE_RUNTIME_DEV_PM_OPS(hi556_pm_ops, hi556_suspend, hi556_resume,
++				 NULL);
++
+ #ifdef CONFIG_ACPI
+ static const struct acpi_device_id hi556_acpi_ids[] = {
+ 	{"INT3537"},
+@@ -1361,6 +1403,7 @@ static struct i2c_driver hi556_i2c_driver = {
+ 	.driver = {
+ 		.name = "hi556",
+ 		.acpi_match_table = ACPI_PTR(hi556_acpi_ids),
++		.pm = pm_sleep_ptr(&hi556_pm_ops),
+ 	},
+ 	.probe = hi556_probe,
+ 	.remove = hi556_remove,
+-- 
+2.43.2
+
+
+From ee651202ba2ca38da067b5379edd7b4f339cf7a8 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 23 Jan 2024 14:54:22 +0100
+Subject: [PATCH 31/33] media: hi556: Add support for external clock
+
+On some ACPI platforms, such as Chromebooks the ACPI methods to
+change the power-state (_PS0 and _PS3) fully take care of powering
+on/off the sensor.
+
+On other ACPI platforms, such as e.g. various HP models with IPU6 +
+hi556 sensor, the sensor driver must control the sensor's clock itself.
+
+Add support for having the driver control an optional clock.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/hi556.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
+index f5a39b83598b..b783e0f56687 100644
+--- a/drivers/media/i2c/hi556.c
++++ b/drivers/media/i2c/hi556.c
+@@ -3,6 +3,7 @@
+ 
+ #include <asm/unaligned.h>
+ #include <linux/acpi.h>
++#include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/i2c.h>
+@@ -636,6 +637,7 @@ struct hi556 {
+ 
+ 	/* GPIOs, clocks, etc. */
+ 	struct gpio_desc *reset_gpio;
++	struct clk *clk;
+ 
+ 	/* Current mode */
+ 	const struct hi556_mode *cur_mode;
+@@ -1286,6 +1288,7 @@ static int hi556_suspend(struct device *dev)
+ 	struct hi556 *hi556 = to_hi556(sd);
+ 
+ 	gpiod_set_value_cansleep(hi556->reset_gpio, 1);
++	clk_disable_unprepare(hi556->clk);
+ 	return 0;
+ }
+ 
+@@ -1293,6 +1296,11 @@ static int hi556_resume(struct device *dev)
+ {
+ 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ 	struct hi556 *hi556 = to_hi556(sd);
++	int ret;
++
++	ret = clk_prepare_enable(hi556->clk);
++	if (ret)
++		return ret;
+ 
+ 	gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ 	usleep_range(5000, 5500);
+@@ -1324,6 +1332,11 @@ static int hi556_probe(struct i2c_client *client)
+ 		return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio),
+ 				     "failed to get reset GPIO\n");
+ 
++	hi556->clk = devm_clk_get_optional(&client->dev, "clk");
++	if (IS_ERR(hi556->clk))
++		return dev_err_probe(&client->dev, PTR_ERR(hi556->clk),
++				     "failed to get clock\n");
++
+ 	full_power = acpi_dev_state_d0(&client->dev);
+ 	if (full_power) {
+ 		/* Ensure non ACPI managed resources are enabled */
+-- 
+2.43.2
+
+
+From 16be71996d451b8137ba63070e760448814c11a1 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 24 Jan 2024 18:45:02 +0100
+Subject: [PATCH 32/33] media: hi556: Add support for avdd regulator
+
+On some ACPI platforms, such as Chromebooks the ACPI methods to
+change the power-state (_PS0 and _PS3) fully take care of powering
+on/off the sensor.
+
+On other ACPI platforms, such as e.g. various HP models with IPU6 +
+hi556 sensor, the sensor driver must control the avdd regulator itself.
+
+Add support for having the driver control the sensor's avdd regulator.
+Note this relies on the regulator-core providing a dummy regulator
+(which it does by default) on platforms where Linux is not aware of
+the avdd regulator.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/hi556.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
+index b783e0f56687..5641c249d4b1 100644
+--- a/drivers/media/i2c/hi556.c
++++ b/drivers/media/i2c/hi556.c
+@@ -9,6 +9,7 @@
+ #include <linux/i2c.h>
+ #include <linux/module.h>
+ #include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
+ #include <media/v4l2-ctrls.h>
+ #include <media/v4l2-device.h>
+ #include <media/v4l2-fwnode.h>
+@@ -638,6 +639,7 @@ struct hi556 {
+ 	/* GPIOs, clocks, etc. */
+ 	struct gpio_desc *reset_gpio;
+ 	struct clk *clk;
++	struct regulator *avdd;
+ 
+ 	/* Current mode */
+ 	const struct hi556_mode *cur_mode;
+@@ -1286,8 +1288,17 @@ static int hi556_suspend(struct device *dev)
+ {
+ 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ 	struct hi556 *hi556 = to_hi556(sd);
++	int ret;
+ 
+ 	gpiod_set_value_cansleep(hi556->reset_gpio, 1);
++
++	ret = regulator_disable(hi556->avdd);
++	if (ret) {
++		dev_err(dev, "failed to disable avdd: %d\n", ret);
++		gpiod_set_value_cansleep(hi556->reset_gpio, 0);
++		return ret;
++	}
++
+ 	clk_disable_unprepare(hi556->clk);
+ 	return 0;
+ }
+@@ -1302,6 +1313,13 @@ static int hi556_resume(struct device *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	ret = regulator_enable(hi556->avdd);
++	if (ret) {
++		dev_err(dev, "failed to enable avdd: %d\n", ret);
++		clk_disable_unprepare(hi556->clk);
++		return ret;
++	}
++
+ 	gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ 	usleep_range(5000, 5500);
+ 	return 0;
+@@ -1337,6 +1355,12 @@ static int hi556_probe(struct i2c_client *client)
+ 		return dev_err_probe(&client->dev, PTR_ERR(hi556->clk),
+ 				     "failed to get clock\n");
+ 
++	/* The regulator core will provide a "dummy" regulator if necessary */
++	hi556->avdd = devm_regulator_get(&client->dev, "avdd");
++	if (IS_ERR(hi556->avdd))
++		return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd),
++				     "failed to get avdd regulator\n");
++
+ 	full_power = acpi_dev_state_d0(&client->dev);
+ 	if (full_power) {
+ 		/* Ensure non ACPI managed resources are enabled */
+-- 
+2.43.2
+
+
+From 6bd6e73829cf264120f629c88c552c4eb59c7eee Mon Sep 17 00:00:00 2001
+From: Florian Klink <flokli@flokli.de>
+Date: Sun, 17 Mar 2024 17:07:53 +0200
+Subject: [PATCH 33/33] media: intel/ipu6: fix firmware paths
+
+linux-firmware ships them in intel/ipu, not intel/.
+---
+ drivers/media/pci/intel/ipu6/ipu6.h | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6.h b/drivers/media/pci/intel/ipu6/ipu6.h
+index 04e7e7e61ca5..da8a95a9edf8 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6.h
++++ b/drivers/media/pci/intel/ipu6/ipu6.h
+@@ -24,10 +24,10 @@ struct ipu6_bus_device;
+ #define IPU6_NAME			"intel-ipu6"
+ #define IPU6_MEDIA_DEV_MODEL_NAME	"ipu6"
+ 
+-#define IPU6SE_FIRMWARE_NAME		"intel/ipu6se_fw.bin"
+-#define IPU6EP_FIRMWARE_NAME		"intel/ipu6ep_fw.bin"
+-#define IPU6_FIRMWARE_NAME		"intel/ipu6_fw.bin"
+-#define IPU6EPMTL_FIRMWARE_NAME		"intel/ipu6epmtl_fw.bin"
++#define IPU6SE_FIRMWARE_NAME		"intel/ipu/ipu6se_fw.bin"
++#define IPU6EP_FIRMWARE_NAME		"intel/ipu/ipu6ep_fw.bin"
++#define IPU6_FIRMWARE_NAME		"intel/ipu/ipu6_fw.bin"
++#define IPU6EPMTL_FIRMWARE_NAME		"intel/ipu/ipu6epmtl_fw.bin"
+ 
+ enum ipu6_version {
+ 	IPU6_VER_INVALID = 0,
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/.skip-tree b/users/flokli/ipu6-softisp/libcamera/.skip-tree
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/.skip-tree
diff --git a/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch b/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch
new file mode 100644
index 0000000000..b640ddaa24
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch
@@ -0,0 +1,82 @@
+From d86746fc1739f678e4bafe43f5047cba9b6b053e Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:05 +0100
+Subject: [PATCH 01/21] libcamera: pipeline: simple: fix size adjustment in
+ validate()
+
+SimpleCameraConfiguration::validate() adjusts the configuration of its
+streams (if the size is not in the outputSizes) to the captureSize. But
+the captureSize itself can be not in the outputSizes, and then the
+adjusted configuration won't be valid resulting in camera configuration
+failure.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/pipeline/simple/simple.cpp | 37 ++++++++++++++++++++++--
+ 1 file changed, 35 insertions(+), 2 deletions(-)
+
+diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
+index 911051b2..a84f6760 100644
+--- a/src/libcamera/pipeline/simple/simple.cpp
++++ b/src/libcamera/pipeline/simple/simple.cpp
+@@ -881,6 +881,30 @@ SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera,
+ {
+ }
+ 
++namespace {
++
++static Size adjustSize(const Size &requestedSize, const SizeRange &supportedSizes)
++{
++	ASSERT(supportedSizes.min <= supportedSizes.max);
++
++	if (supportedSizes.min == supportedSizes.max)
++		return supportedSizes.max;
++
++	unsigned int hStep = supportedSizes.hStep;
++	unsigned int vStep = supportedSizes.vStep;
++
++	if (hStep == 0)
++		hStep = supportedSizes.max.width - supportedSizes.min.width;
++	if (vStep == 0)
++		vStep = supportedSizes.max.height - supportedSizes.min.height;
++
++	Size adjusted = requestedSize.boundedTo(supportedSizes.max).expandedTo(supportedSizes.min);
++
++	return adjusted.shrunkBy(supportedSizes.min).alignedDownTo(hStep, vStep).grownBy(supportedSizes.min);
++}
++
++} /* namespace */
++
+ CameraConfiguration::Status SimpleCameraConfiguration::validate()
+ {
+ 	const CameraSensor *sensor = data_->sensor_.get();
+@@ -997,10 +1021,19 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
+ 		}
+ 
+ 		if (!pipeConfig_->outputSizes.contains(cfg.size)) {
++			Size adjustedSize = pipeConfig_->captureSize;
++			/*
++			 * The converter (when present) may not be able to output
++			 * a size identical to its input size. The capture size is thus
++			 * not guaranteed to be a valid output size. In such cases, use
++			 * the smaller valid output size closest to the requested.
++			 */
++			if (!pipeConfig_->outputSizes.contains(adjustedSize))
++				adjustedSize = adjustSize(cfg.size, pipeConfig_->outputSizes);
+ 			LOG(SimplePipeline, Debug)
+ 				<< "Adjusting size from " << cfg.size
+-				<< " to " << pipeConfig_->captureSize;
+-			cfg.size = pipeConfig_->captureSize;
++				<< " to " << adjustedSize;
++			cfg.size = adjustedSize;
+ 			status = Adjusted;
+ 		}
+ 
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch b/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch
new file mode 100644
index 0000000000..450a0a21f1
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch
@@ -0,0 +1,350 @@
+From 96e50c6a43352a9cb81d558fea27e580f2b26585 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:06 +0100
+Subject: [PATCH 02/21] libcamera: internal: Move dma_heaps.[h, cpp] to common
+ directories
+
+DmaHeap class is useful outside the RPi pipeline handler too.
+
+Move dma_heaps.h and dma_heaps.cpp to common directories. Update
+the build files and RPi vc4 pipeline handler accordingly.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ .../libcamera/internal}/dma_heaps.h           |   4 -
+ include/libcamera/internal/meson.build        |   1 +
+ src/libcamera/dma_heaps.cpp                   | 127 ++++++++++++++++++
+ src/libcamera/meson.build                     |   1 +
+ src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp  |  90 -------------
+ src/libcamera/pipeline/rpi/vc4/meson.build    |   1 -
+ src/libcamera/pipeline/rpi/vc4/vc4.cpp        |   5 +-
+ 7 files changed, 131 insertions(+), 98 deletions(-)
+ rename {src/libcamera/pipeline/rpi/vc4 => include/libcamera/internal}/dma_heaps.h (92%)
+ create mode 100644 src/libcamera/dma_heaps.cpp
+ delete mode 100644 src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
+
+diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
+similarity index 92%
+rename from src/libcamera/pipeline/rpi/vc4/dma_heaps.h
+rename to include/libcamera/internal/dma_heaps.h
+index 0a4a8d86..cff8f140 100644
+--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h
++++ b/include/libcamera/internal/dma_heaps.h
+@@ -13,8 +13,6 @@
+ 
+ namespace libcamera {
+ 
+-namespace RPi {
+-
+ class DmaHeap
+ {
+ public:
+@@ -27,6 +25,4 @@ private:
+ 	UniqueFD dmaHeapHandle_;
+ };
+ 
+-} /* namespace RPi */
+-
+ } /* namespace libcamera */
+diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
+index 7f1f3440..33eb0fb3 100644
+--- a/include/libcamera/internal/meson.build
++++ b/include/libcamera/internal/meson.build
+@@ -25,6 +25,7 @@ libcamera_internal_headers = files([
+     'device_enumerator.h',
+     'device_enumerator_sysfs.h',
+     'device_enumerator_udev.h',
++    'dma_heaps.h',
+     'formats.h',
+     'framebuffer.h',
+     'ipa_manager.h',
+diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
+new file mode 100644
+index 00000000..38ef175a
+--- /dev/null
++++ b/src/libcamera/dma_heaps.cpp
+@@ -0,0 +1,127 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2020, Raspberry Pi Ltd
++ *
++ * dma_heaps.h - Helper class for dma-heap allocations.
++ */
++
++#include "libcamera/internal/dma_heaps.h"
++
++#include <array>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <unistd.h>
++
++#include <linux/dma-buf.h>
++#include <linux/dma-heap.h>
++
++#include <libcamera/base/log.h>
++
++/**
++ * \file dma_heaps.cpp
++ * \brief CMA dma-heap allocator
++ */
++
++/*
++ * /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
++ * to only have to worry about importing.
++ *
++ * Annoyingly, should the cma heap size be specified on the kernel command line
++ * instead of DT, the heap gets named "reserved" instead.
++ */
++static constexpr std::array<const char *, 2> heapNames = {
++	"/dev/dma_heap/linux,cma",
++	"/dev/dma_heap/reserved"
++};
++
++namespace libcamera {
++
++LOG_DEFINE_CATEGORY(DmaHeap)
++
++/**
++ * \class DmaHeap
++ * \brief Helper class for CMA dma-heap allocations
++ */
++
++/**
++ * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor
++ *
++ * Goes through the internal list of possible names of the CMA dma-heap devices
++ * until a CMA dma-heap device is successfully opened. If it fails to open any
++ * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap
++ * object owns a wrapped dma-heap file descriptor.
++ *
++ * Please check the new DmaHeap object with \ref DmaHeap::isValid before using it.
++ */
++DmaHeap::DmaHeap()
++{
++	for (const char *name : heapNames) {
++		int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
++		if (ret < 0) {
++			ret = errno;
++			LOG(DmaHeap, Debug)
++				<< "Failed to open " << name << ": "
++				<< strerror(ret);
++			continue;
++		}
++
++		dmaHeapHandle_ = UniqueFD(ret);
++		break;
++	}
++
++	if (!dmaHeapHandle_.isValid())
++		LOG(DmaHeap, Error) << "Could not open any dmaHeap device";
++}
++
++/**
++ * \brief Destroy the DmaHeap instance
++ *
++ * Destroying a DmaHeap instance which owns a wrapped dma-heap file descriptor
++ * closes the descriptor automatically.
++ */
++DmaHeap::~DmaHeap() = default;
++
++/**
++ * \fn DmaHeap::isValid()
++ * \brief Check if the DmaHeap instance is valid
++ * \return True if the DmaHeap is valid, false otherwise
++ */
++
++/**
++ * \brief Allocate a dma-buf from the DmaHeap
++ * \param [in] name The name to set for the allocated buffer
++ * \param [in] size The size of the buffer to allocate
++ * \return The \ref UniqueFD of the allocated buffer
++ *
++ * Allocates a dma-buf with read/write access.
++ * If the allocation fails returns invalid UniqueFD.
++ */
++UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
++{
++	int ret;
++
++	if (!name)
++		return {};
++
++	struct dma_heap_allocation_data alloc = {};
++
++	alloc.len = size;
++	alloc.fd_flags = O_CLOEXEC | O_RDWR;
++
++	ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
++	if (ret < 0) {
++		LOG(DmaHeap, Error) << "dmaHeap allocation failure for " << name;
++		return {};
++	}
++
++	UniqueFD allocFd(alloc.fd);
++	ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
++	if (ret < 0) {
++		LOG(DmaHeap, Error) << "dmaHeap naming failure for " << name;
++		return {};
++	}
++
++	return allocFd;
++}
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
+index 45f63e93..3c5e43df 100644
+--- a/src/libcamera/meson.build
++++ b/src/libcamera/meson.build
+@@ -17,6 +17,7 @@ libcamera_sources = files([
+     'delayed_controls.cpp',
+     'device_enumerator.cpp',
+     'device_enumerator_sysfs.cpp',
++    'dma_heaps.cpp',
+     'fence.cpp',
+     'formats.cpp',
+     'framebuffer.cpp',
+diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
+deleted file mode 100644
+index 317b1fc1..00000000
+--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
++++ /dev/null
+@@ -1,90 +0,0 @@
+-/* SPDX-License-Identifier: LGPL-2.1-or-later */
+-/*
+- * Copyright (C) 2020, Raspberry Pi Ltd
+- *
+- * dma_heaps.h - Helper class for dma-heap allocations.
+- */
+-
+-#include "dma_heaps.h"
+-
+-#include <array>
+-#include <fcntl.h>
+-#include <linux/dma-buf.h>
+-#include <linux/dma-heap.h>
+-#include <sys/ioctl.h>
+-#include <unistd.h>
+-
+-#include <libcamera/base/log.h>
+-
+-/*
+- * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
+- * to only have to worry about importing.
+- *
+- * Annoyingly, should the cma heap size be specified on the kernel command line
+- * instead of DT, the heap gets named "reserved" instead.
+- */
+-static constexpr std::array<const char *, 2> heapNames = {
+-	"/dev/dma_heap/linux,cma",
+-	"/dev/dma_heap/reserved"
+-};
+-
+-namespace libcamera {
+-
+-LOG_DECLARE_CATEGORY(RPI)
+-
+-namespace RPi {
+-
+-DmaHeap::DmaHeap()
+-{
+-	for (const char *name : heapNames) {
+-		int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
+-		if (ret < 0) {
+-			ret = errno;
+-			LOG(RPI, Debug) << "Failed to open " << name << ": "
+-					<< strerror(ret);
+-			continue;
+-		}
+-
+-		dmaHeapHandle_ = UniqueFD(ret);
+-		break;
+-	}
+-
+-	if (!dmaHeapHandle_.isValid())
+-		LOG(RPI, Error) << "Could not open any dmaHeap device";
+-}
+-
+-DmaHeap::~DmaHeap() = default;
+-
+-UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
+-{
+-	int ret;
+-
+-	if (!name)
+-		return {};
+-
+-	struct dma_heap_allocation_data alloc = {};
+-
+-	alloc.len = size;
+-	alloc.fd_flags = O_CLOEXEC | O_RDWR;
+-
+-	ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
+-	if (ret < 0) {
+-		LOG(RPI, Error) << "dmaHeap allocation failure for "
+-				<< name;
+-		return {};
+-	}
+-
+-	UniqueFD allocFd(alloc.fd);
+-	ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
+-	if (ret < 0) {
+-		LOG(RPI, Error) << "dmaHeap naming failure for "
+-				<< name;
+-		return {};
+-	}
+-
+-	return allocFd;
+-}
+-
+-} /* namespace RPi */
+-
+-} /* namespace libcamera */
+diff --git a/src/libcamera/pipeline/rpi/vc4/meson.build b/src/libcamera/pipeline/rpi/vc4/meson.build
+index cdb049c5..386e2296 100644
+--- a/src/libcamera/pipeline/rpi/vc4/meson.build
++++ b/src/libcamera/pipeline/rpi/vc4/meson.build
+@@ -1,7 +1,6 @@
+ # SPDX-License-Identifier: CC0-1.0
+ 
+ libcamera_sources += files([
+-    'dma_heaps.cpp',
+     'vc4.cpp',
+ ])
+ 
+diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+index 26102ea7..3a42e75e 100644
+--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
++++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+@@ -12,12 +12,11 @@
+ #include <libcamera/formats.h>
+ 
+ #include "libcamera/internal/device_enumerator.h"
++#include "libcamera/internal/dma_heaps.h"
+ 
+ #include "../common/pipeline_base.h"
+ #include "../common/rpi_stream.h"
+ 
+-#include "dma_heaps.h"
+-
+ using namespace std::chrono_literals;
+ 
+ namespace libcamera {
+@@ -87,7 +86,7 @@ public:
+ 	RPi::Device<Isp, 4> isp_;
+ 
+ 	/* DMAHEAP allocation helper. */
+-	RPi::DmaHeap dmaHeap_;
++	DmaHeap dmaHeap_;
+ 	SharedFD lsTable_;
+ 
+ 	struct Config {
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch b/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch
new file mode 100644
index 0000000000..6e5ef9445a
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch
@@ -0,0 +1,169 @@
+From 5df9bc3b2a3d86bcc8504896cc87d7fcb5aea3a4 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:07 +0100
+Subject: [PATCH 03/21] libcamera: dma_heaps: extend DmaHeap class to support
+ system heap
+
+Add an argument to the constructor to specify dma heaps type(s)
+to use. Can be DmaHeapFlag::Cma and/or DmaHeapFlag::System.
+By default DmaHeapFlag::Cma is used. If both DmaHeapFlag::Cma and
+DmaHeapFlag::System are set, CMA heap is tried first.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ include/libcamera/internal/dma_heaps.h | 12 ++++-
+ src/libcamera/dma_heaps.cpp            | 67 ++++++++++++++++++++------
+ 2 files changed, 63 insertions(+), 16 deletions(-)
+
+diff --git a/include/libcamera/internal/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
+index cff8f140..80bf29e7 100644
+--- a/include/libcamera/internal/dma_heaps.h
++++ b/include/libcamera/internal/dma_heaps.h
+@@ -9,6 +9,7 @@
+ 
+ #include <stddef.h>
+ 
++#include <libcamera/base/flags.h>
+ #include <libcamera/base/unique_fd.h>
+ 
+ namespace libcamera {
+@@ -16,7 +17,14 @@ namespace libcamera {
+ class DmaHeap
+ {
+ public:
+-	DmaHeap();
++	enum class DmaHeapFlag {
++		Cma = 1 << 0,
++		System = 1 << 1,
++	};
++
++	using DmaHeapFlags = Flags<DmaHeapFlag>;
++
++	DmaHeap(DmaHeapFlags flags = DmaHeapFlag::Cma);
+ 	~DmaHeap();
+ 	bool isValid() const { return dmaHeapHandle_.isValid(); }
+ 	UniqueFD alloc(const char *name, std::size_t size);
+@@ -25,4 +33,6 @@ private:
+ 	UniqueFD dmaHeapHandle_;
+ };
+ 
++LIBCAMERA_FLAGS_ENABLE_OPERATORS(DmaHeap::DmaHeapFlag)
++
+ } /* namespace libcamera */
+diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
+index 38ef175a..d0e33ce6 100644
+--- a/src/libcamera/dma_heaps.cpp
++++ b/src/libcamera/dma_heaps.cpp
+@@ -19,9 +19,11 @@
+ 
+ /**
+  * \file dma_heaps.cpp
+- * \brief CMA dma-heap allocator
++ * \brief dma-heap allocator
+  */
+ 
++namespace libcamera {
++
+ /*
+  * /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
+  * to only have to worry about importing.
+@@ -29,42 +31,77 @@
+  * Annoyingly, should the cma heap size be specified on the kernel command line
+  * instead of DT, the heap gets named "reserved" instead.
+  */
+-static constexpr std::array<const char *, 2> heapNames = {
+-	"/dev/dma_heap/linux,cma",
+-	"/dev/dma_heap/reserved"
++
++/**
++ * \struct DmaHeapInfo
++ * \brief Tells what type of dma-heap the dma-heap represented by the device node name is
++ * \var DmaHeapInfo::flag
++ * \brief The type of the dma-heap
++ * \var DmaHeapInfo::name
++ * \brief The dma-heap's device node name
++ */
++struct DmaHeapInfo {
++	DmaHeap::DmaHeapFlag flag;
++	const char *name;
+ };
+ 
+-namespace libcamera {
++static constexpr std::array<DmaHeapInfo, 3> heapInfos = {
++	{ /* CMA heap names first */
++	  { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/linux,cma" },
++	  { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/reserved" },
++	  { DmaHeap::DmaHeapFlag::System, "/dev/dma_heap/system" } }
++};
+ 
+ LOG_DEFINE_CATEGORY(DmaHeap)
+ 
+ /**
+  * \class DmaHeap
+- * \brief Helper class for CMA dma-heap allocations
++ * \brief Helper class for dma-heap allocations
+  */
+ 
+ /**
+- * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor
++ * \enum DmaHeap::DmaHeapFlag
++ * \brief Type of the dma-heap
++ * \var DmaHeap::Cma
++ * \brief Allocate from a CMA dma-heap
++ * \var DmaHeap::System
++ * \brief Allocate from the system dma-heap
++ */
++
++/**
++ * \typedef DmaHeap::DmaHeapFlags
++ * \brief A bitwise combination of DmaHeap::DmaHeapFlag values
++ */
++
++/**
++ * \brief Construct a DmaHeap that owns a CMA or system dma-heap file descriptor
++ * \param [in] flags The type(s) of the dma-heap(s) to allocate from
+  *
+- * Goes through the internal list of possible names of the CMA dma-heap devices
+- * until a CMA dma-heap device is successfully opened. If it fails to open any
+- * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap
+- * object owns a wrapped dma-heap file descriptor.
++ * By default \a flags are set to DmaHeap::DmaHeapFlag::Cma. The constructor goes
++ * through the internal list of possible names of the CMA and system dma-heap devices
++ * until the dma-heap device of the requested type is successfully opened. If more
++ * than one dma-heap type is specified in flags the CMA heap is tried first. If it
++ * fails to open any dma-heap device an invalid DmaHeap object is constructed.
++ * A valid DmaHeap object owns a wrapped dma-heap file descriptor.
+  *
+  * Please check the new DmaHeap object with \ref DmaHeap::isValid before using it.
+  */
+-DmaHeap::DmaHeap()
++DmaHeap::DmaHeap(DmaHeapFlags flags)
+ {
+-	for (const char *name : heapNames) {
+-		int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
++	for (const auto &info : heapInfos) {
++		if (!(flags & info.flag))
++			continue;
++
++		int ret = ::open(info.name, O_RDWR | O_CLOEXEC, 0);
+ 		if (ret < 0) {
+ 			ret = errno;
+ 			LOG(DmaHeap, Debug)
+-				<< "Failed to open " << name << ": "
++				<< "Failed to open " << info.name << ": "
+ 				<< strerror(ret);
+ 			continue;
+ 		}
+ 
++		LOG(DmaHeap, Debug) << "Using " << info.name;
+ 		dmaHeapHandle_ = UniqueFD(ret);
+ 		break;
+ 	}
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch b/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch
new file mode 100644
index 0000000000..48f10aa47a
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch
@@ -0,0 +1,69 @@
+From a6777760a2121f02808baecea504ac0e242f860b Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:08 +0100
+Subject: [PATCH 04/21] libcamera: internal: Move SharedMemObject class to a
+ common directory
+
+Move SharedMemObject class out of RPi namespace and put it into
+include/libcamera/internal so that everyone could use it.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ include/libcamera/internal/meson.build                      | 1 +
+ .../libcamera/internal}/shared_mem_object.h                 | 6 +-----
+ 2 files changed, 2 insertions(+), 5 deletions(-)
+ rename {src/libcamera/pipeline/rpi/common => include/libcamera/internal}/shared_mem_object.h (97%)
+
+diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
+index 33eb0fb3..5807dfd9 100644
+--- a/include/libcamera/internal/meson.build
++++ b/include/libcamera/internal/meson.build
+@@ -39,6 +39,7 @@ libcamera_internal_headers = files([
+     'process.h',
+     'pub_key.h',
+     'request.h',
++    'shared_mem_object.h',
+     'source_paths.h',
+     'sysfs.h',
+     'v4l2_device.h',
+diff --git a/src/libcamera/pipeline/rpi/common/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
+similarity index 97%
+rename from src/libcamera/pipeline/rpi/common/shared_mem_object.h
+rename to include/libcamera/internal/shared_mem_object.h
+index aa56c220..98636b44 100644
+--- a/src/libcamera/pipeline/rpi/common/shared_mem_object.h
++++ b/include/libcamera/internal/shared_mem_object.h
+@@ -6,8 +6,8 @@
+  */
+ #pragma once
+ 
+-#include <cstddef>
+ #include <fcntl.h>
++#include <stddef.h>
+ #include <string>
+ #include <sys/mman.h>
+ #include <sys/stat.h>
+@@ -19,8 +19,6 @@
+ 
+ namespace libcamera {
+ 
+-namespace RPi {
+-
+ template<class T>
+ class SharedMemObject
+ {
+@@ -123,6 +121,4 @@ private:
+ 	T *obj_;
+ };
+ 
+-} /* namespace RPi */
+-
+ } /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch b/users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch
new file mode 100644
index 0000000000..d2143febf7
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch
@@ -0,0 +1,403 @@
+From f94af21adc1889706127d07c5425f44c9cec9a95 Mon Sep 17 00:00:00 2001
+From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
+Date: Mon, 11 Mar 2024 15:15:09 +0100
+Subject: [PATCH 05/21] libcamera: shared_mem_object: reorganize the code and
+ document the SharedMemObject class
+
+Split the parts which doesn't otherwise depend on the type T or
+arguments Args out of the SharedMemObject class into a new
+SharedMem class.
+
+Doxygen documentation by Dennis Bonke and Andrei Konovalov.
+
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ .../libcamera/internal/shared_mem_object.h    | 101 ++++++----
+ src/libcamera/meson.build                     |   1 +
+ src/libcamera/shared_mem_object.cpp           | 190 ++++++++++++++++++
+ 3 files changed, 253 insertions(+), 39 deletions(-)
+ create mode 100644 src/libcamera/shared_mem_object.cpp
+
+diff --git a/include/libcamera/internal/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
+index 98636b44..43b07c9d 100644
+--- a/include/libcamera/internal/shared_mem_object.h
++++ b/include/libcamera/internal/shared_mem_object.h
+@@ -6,12 +6,9 @@
+  */
+ #pragma once
+ 
+-#include <fcntl.h>
+ #include <stddef.h>
+ #include <string>
+ #include <sys/mman.h>
+-#include <sys/stat.h>
+-#include <unistd.h>
+ #include <utility>
+ 
+ #include <libcamera/base/class.h>
+@@ -19,58 +16,92 @@
+ 
+ namespace libcamera {
+ 
++class SharedMem
++{
++public:
++	SharedMem()
++		: mem_(nullptr)
++	{
++	}
++
++	SharedMem(const std::string &name, std::size_t size);
++
++	SharedMem(SharedMem &&rhs)
++	{
++		this->name_ = std::move(rhs.name_);
++		this->fd_ = std::move(rhs.fd_);
++		this->mem_ = rhs.mem_;
++		rhs.mem_ = nullptr;
++	}
++
++	virtual ~SharedMem()
++	{
++		if (mem_)
++			munmap(mem_, size_);
++	}
++
++	/* Make SharedMem non-copyable for now. */
++	LIBCAMERA_DISABLE_COPY(SharedMem)
++
++	SharedMem &operator=(SharedMem &&rhs)
++	{
++		this->name_ = std::move(rhs.name_);
++		this->fd_ = std::move(rhs.fd_);
++		this->mem_ = rhs.mem_;
++		rhs.mem_ = nullptr;
++		return *this;
++	}
++
++	const SharedFD &fd() const
++	{
++		return fd_;
++	}
++
++	void *mem() const
++	{
++		return mem_;
++	}
++
++private:
++	std::string name_;
++	SharedFD fd_;
++	size_t size_;
++protected:
++	void *mem_;
++};
++
+ template<class T>
+-class SharedMemObject
++class SharedMemObject : public SharedMem
+ {
+ public:
+ 	static constexpr std::size_t SIZE = sizeof(T);
+ 
+ 	SharedMemObject()
+-		: obj_(nullptr)
++		: SharedMem(), obj_(nullptr)
+ 	{
+ 	}
+ 
+ 	template<class... Args>
+ 	SharedMemObject(const std::string &name, Args &&...args)
+-		: name_(name), obj_(nullptr)
++		: SharedMem(name, SIZE), obj_(nullptr)
+ 	{
+-		void *mem;
+-		int ret;
+-
+-		ret = memfd_create(name_.c_str(), MFD_CLOEXEC);
+-		if (ret < 0)
+-			return;
+-
+-		fd_ = SharedFD(std::move(ret));
+-		if (!fd_.isValid())
+-			return;
+-
+-		ret = ftruncate(fd_.get(), SIZE);
+-		if (ret < 0)
++		if (mem_ == nullptr)
+ 			return;
+ 
+-		mem = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+-			   fd_.get(), 0);
+-		if (mem == MAP_FAILED)
+-			return;
+-
+-		obj_ = new (mem) T(std::forward<Args>(args)...);
++		obj_ = new (mem_) T(std::forward<Args>(args)...);
+ 	}
+ 
+ 	SharedMemObject(SharedMemObject<T> &&rhs)
++		: SharedMem(std::move(rhs))
+ 	{
+-		this->name_ = std::move(rhs.name_);
+-		this->fd_ = std::move(rhs.fd_);
+ 		this->obj_ = rhs.obj_;
+ 		rhs.obj_ = nullptr;
+ 	}
+ 
+ 	~SharedMemObject()
+ 	{
+-		if (obj_) {
++		if (obj_)
+ 			obj_->~T();
+-			munmap(obj_, SIZE);
+-		}
+ 	}
+ 
+ 	/* Make SharedMemObject non-copyable for now. */
+@@ -78,8 +109,7 @@ public:
+ 
+ 	SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs)
+ 	{
+-		this->name_ = std::move(rhs.name_);
+-		this->fd_ = std::move(rhs.fd_);
++		SharedMem::operator=(std::move(rhs));
+ 		this->obj_ = rhs.obj_;
+ 		rhs.obj_ = nullptr;
+ 		return *this;
+@@ -105,19 +135,12 @@ public:
+ 		return *obj_;
+ 	}
+ 
+-	const SharedFD &fd() const
+-	{
+-		return fd_;
+-	}
+-
+ 	explicit operator bool() const
+ 	{
+ 		return !!obj_;
+ 	}
+ 
+ private:
+-	std::string name_;
+-	SharedFD fd_;
+ 	T *obj_;
+ };
+ 
+diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
+index 3c5e43df..94a95ae3 100644
+--- a/src/libcamera/meson.build
++++ b/src/libcamera/meson.build
+@@ -41,6 +41,7 @@ libcamera_sources = files([
+     'process.cpp',
+     'pub_key.cpp',
+     'request.cpp',
++    'shared_mem_object.cpp',
+     'source_paths.cpp',
+     'stream.cpp',
+     'sysfs.cpp',
+diff --git a/src/libcamera/shared_mem_object.cpp b/src/libcamera/shared_mem_object.cpp
+new file mode 100644
+index 00000000..44fe74c2
+--- /dev/null
++++ b/src/libcamera/shared_mem_object.cpp
+@@ -0,0 +1,190 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Raspberry Pi Ltd
++ *
++ * shared_mem_object.cpp - Helper class for shared memory allocations
++ */
++
++#include "libcamera/internal/shared_mem_object.h"
++
++#include <sys/types.h>
++#include <unistd.h>
++
++/**
++ * \file shared_mem_object.cpp
++ * \brief Helper class for shared memory allocations
++ */
++
++namespace libcamera {
++
++/**
++ * \class SharedMem
++ * \brief Helper class for allocating shared memory
++ *
++ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries.
++ *
++ * SharedMem allocates the shared memory of the given size and maps it.
++ * To check that the shared memory was allocated and mapped successfully, one
++ * needs to verify that the pointer to the shared memory returned by SharedMem::mem()
++ * is not nullptr.
++ *
++ * To access the shared memory from another process the SharedFD should be passed
++ * to that process, and then the shared memory should be mapped into that process
++ * address space by calling mmap().
++ *
++ * A single memfd is created for every SharedMem. If there is a need to allocate
++ * a large number of objects in shared memory, these objects should be grouped
++ * together and use the shared memory allocated by a single SharedMem object if
++ * possible. This will help to minimize the number of created memfd's.
++ */
++
++/**
++ * \fn SharedMem::SharedMem(const std::string &name, std::size_t size)
++ * \brief Constructor for the SharedMem
++ * \param[in] name Name of the SharedMem
++ * \param[in] size Size of the shared memory to allocate and map
++ */
++
++/**
++ * \fn SharedMem::SharedMem(SharedMem &&rhs)
++ * \brief Move constructor for SharedMem
++ * \param[in] rhs The object to move
++ */
++
++/**
++ * \fn SharedMem::~SharedMem()
++ * \brief SharedMem destructor
++ *
++ * Unmaps the allocated shared memory. Decrements the shared memory descriptor use
++ * count.
++ */
++
++/**
++ * \fn SharedMem &SharedMem::operator=(SharedMem &&rhs)
++ * \brief Move constructor for SharedMem
++ * \param[in] rhs The object to move
++ */
++
++/**
++ * \fn const SharedFD &SharedMem::fd() const
++ * \brief Gets the file descriptor for the underlying shared memory
++ * \return The file descriptor
++ */
++
++/**
++ * \fn void *SharedMem::mem() const
++ * \brief Gets the pointer to the underlying shared memory
++ * \return The pointer to the shared memory
++ */
++
++SharedMem::SharedMem(const std::string &name, std::size_t size)
++	: name_(name), size_(size), mem_(nullptr)
++{
++	int fd = memfd_create(name_.c_str(), MFD_CLOEXEC);
++	if (fd < 0)
++		return;
++
++	fd_ = SharedFD(std::move(fd));
++	if (!fd_.isValid())
++		return;
++
++	if (ftruncate(fd_.get(), size_) < 0)
++		return;
++
++	mem_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
++		    fd_.get(), 0);
++	if (mem_ == MAP_FAILED)
++		mem_ = nullptr;
++}
++
++/**
++ * \var SharedMem::mem_
++ * \brief Pointer to the shared memory allocated
++ */
++
++/**
++ * \class SharedMemObject
++ * \brief Helper class for allocating objects in shared memory
++ *
++ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries.
++ *
++ * Given the type of the object to be created in shared memory and the arguments
++ * to pass to this object's constructor, SharedMemObject allocates the shared memory
++ * of the size of the object and constructs the object in this memory. To ensure
++ * that the SharedMemObject was created successfully, one needs to verify that the
++ * overloaded bool() operator returns true. The object created in the shared memory
++ * can be accessed using the SharedMemObject::operator*() indirection operator. Its
++ * members can be accessed with the SharedMemObject::operator->() member of pointer
++ * operator.
++ *
++ * To access the object from another process the SharedFD should be passed to that
++ * process, and the shared memory should be mapped by calling mmap().
++ *
++ * A single memfd is created for every SharedMemObject. If there is a need to allocate
++ * a large number of objects in shared memory, these objects should be grouped into a
++ * single large object to keep the number of created memfd's reasonably small.
++ */
++
++/**
++ * \var SharedMemObject::SIZE
++ * \brief The size of the object that is going to be stored here
++ */
++
++/**
++ * \fn SharedMemObject< T >::SharedMemObject(const std::string &name, Args &&...args)
++ * \brief Constructor for the SharedMemObject
++ * \param[in] name Name of the SharedMemObject
++ * \param[in] args Args to pass to the constructor of the object in shared memory
++ */
++
++/**
++ * \fn SharedMemObject::SharedMemObject(SharedMemObject<T> &&rhs)
++ * \brief Move constructor for SharedMemObject
++ * \param[in] rhs The object to move
++ */
++
++/**
++ * \fn SharedMemObject::~SharedMemObject()
++ * \brief SharedMemObject destructor
++ *
++ * Destroys the object created in the shared memory and then unmaps the shared memory.
++ * Decrements the shared memory descriptor use count.
++ */
++
++/**
++ * \fn SharedMemObject::operator=(SharedMemObject<T> &&rhs)
++ * \brief Operator= for SharedMemObject
++ * \param[in] rhs The SharedMemObject object to take the data from
++ */
++
++/**
++ * \fn SharedMemObject::operator->()
++ * \brief Operator-> for SharedMemObject
++ * \return The pointer to the object
++ */
++
++/**
++ * \fn const T *SharedMemObject::operator->() const
++ * \brief Operator-> for SharedMemObject
++ * \return The pointer to the const object
++ */
++
++/**
++ * \fn SharedMemObject::operator*()
++ * \brief Operator* for SharedMemObject
++ * \return The reference to the object
++ */
++
++/**
++ * \fn const T &SharedMemObject::operator*() const
++ * \brief Operator* for SharedMemObject
++ * \return Const reference to the object
++ */
++
++/**
++ * \fn SharedMemObject::operator bool()
++ * \brief Operator bool() for SharedMemObject
++ * \return True if the object was created OK in the shared memory, false otherwise
++ */
++
++} // namespace libcamera
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch
new file mode 100644
index 0000000000..9f80b69f16
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch
@@ -0,0 +1,523 @@
+From 4259b01930333c6666a185d923e6e68ec915a4fd Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:10 +0100
+Subject: [PATCH 06/21] libcamera: software_isp: Add SwStatsCpu class
+
+Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use.
+
+This implementation offers a configure function + functions to gather
+statistics on a line by line basis. This allows CPU based software
+debayering to call into interlace debayering and statistics gathering
+on a line by line bases while the input data is still hot in the cache.
+
+This implementation also allows specifying a window over which to gather
+statistics instead of processing the whole frame.
+
+Doxygen documentation by Dennis Bonke.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Co-developed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Co-developed-by: Marttico <g.martti@gmail.com>
+Signed-off-by: Marttico <g.martti@gmail.com>
+Co-developed-by: Toon Langendam <t.langendam@gmail.com>
+Signed-off-by: Toon Langendam <t.langendam@gmail.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ include/libcamera/internal/meson.build        |   1 +
+ .../internal/software_isp/meson.build         |   5 +
+ .../internal/software_isp/swisp_stats.h       |  38 ++++
+ src/libcamera/meson.build                     |   1 +
+ src/libcamera/software_isp/meson.build        |  12 +
+ src/libcamera/software_isp/swstats_cpu.cpp    | 208 ++++++++++++++++++
+ src/libcamera/software_isp/swstats_cpu.h      | 159 +++++++++++++
+ 7 files changed, 424 insertions(+)
+ create mode 100644 include/libcamera/internal/software_isp/meson.build
+ create mode 100644 include/libcamera/internal/software_isp/swisp_stats.h
+ create mode 100644 src/libcamera/software_isp/meson.build
+ create mode 100644 src/libcamera/software_isp/swstats_cpu.cpp
+ create mode 100644 src/libcamera/software_isp/swstats_cpu.h
+
+diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
+index 5807dfd9..160fdc37 100644
+--- a/include/libcamera/internal/meson.build
++++ b/include/libcamera/internal/meson.build
+@@ -50,3 +50,4 @@ libcamera_internal_headers = files([
+ ])
+ 
+ subdir('converter')
++subdir('software_isp')
+diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
+new file mode 100644
+index 00000000..66c9c3fb
+--- /dev/null
++++ b/include/libcamera/internal/software_isp/meson.build
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: CC0-1.0
++
++libcamera_internal_headers += files([
++    'swisp_stats.h',
++])
+diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h
+new file mode 100644
+index 00000000..afe42c9a
+--- /dev/null
++++ b/include/libcamera/internal/software_isp/swisp_stats.h
+@@ -0,0 +1,38 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ *
++ * swisp_stats.h - Statistics data format used by the software ISP and software IPA
++ */
++
++#pragma once
++
++namespace libcamera {
++
++/**
++ * \brief Struct that holds the statistics for the Software ISP.
++ */
++struct SwIspStats {
++	/**
++	 * \brief Holds the sum of all sampled red pixels.
++	 */
++	unsigned long sumR_;
++	/**
++	 * \brief Holds the sum of all sampled green pixels.
++	 */
++	unsigned long sumG_;
++	/**
++	 * \brief Holds the sum of all sampled blue pixels.
++	 */
++	unsigned long sumB_;
++	/**
++	 * \brief Number of bins in the yHistogram.
++	 */
++	static constexpr unsigned int kYHistogramSize = 16;
++	/**
++	 * \brief A histogram of luminance values.
++	 */
++	std::array<unsigned int, kYHistogramSize> yHistogram;
++};
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
+index 94a95ae3..91e4cc60 100644
+--- a/src/libcamera/meson.build
++++ b/src/libcamera/meson.build
+@@ -71,6 +71,7 @@ subdir('converter')
+ subdir('ipa')
+ subdir('pipeline')
+ subdir('proxy')
++subdir('software_isp')
+ 
+ null_dep = dependency('', required : false)
+ 
+diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
+new file mode 100644
+index 00000000..fcfff74a
+--- /dev/null
++++ b/src/libcamera/software_isp/meson.build
+@@ -0,0 +1,12 @@
++# SPDX-License-Identifier: CC0-1.0
++
++softisp_enabled = pipelines.contains('simple')
++summary({'SoftISP support' : softisp_enabled}, section : 'Configuration')
++
++if not (softisp_enabled)
++    subdir_done()
++endif
++
++libcamera_sources += files([
++    'swstats_cpu.cpp',
++])
+diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
+new file mode 100644
+index 00000000..448d0e4c
+--- /dev/null
++++ b/src/libcamera/software_isp/swstats_cpu.cpp
+@@ -0,0 +1,208 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * swstats_cpu.cpp - CPU based software statistics implementation
++ */
++
++#include "swstats_cpu.h"
++
++#include <libcamera/base/log.h>
++
++#include <libcamera/stream.h>
++
++#include "libcamera/internal/bayer_format.h"
++
++namespace libcamera {
++
++/**
++ * \class SwStatsCpu
++ * \brief Class for gathering statistics on the CPU
++ *
++ * CPU based software ISP statistics implementation.
++ *
++ * This class offers a configure function + functions to gather statistics
++ * on a line by line basis. This allows CPU based software debayering to
++ * interlace debayering and statistics gathering on a line by line basis
++ * while the input data is still hot in the cache.
++ *
++ * It is also possible to specify a window over which to gather
++ * statistics instead of processing the whole frame.
++ */
++
++LOG_DEFINE_CATEGORY(SwStatsCpu)
++
++SwStatsCpu::SwStatsCpu()
++{
++	sharedStats_ = SharedMemObject<SwIspStats>("softIsp_stats");
++	if (!sharedStats_.fd().isValid())
++		LOG(SwStatsCpu, Error)
++			<< "Failed to create shared memory for statistics";
++}
++
++static const unsigned int kRedYMul = 77; /* 0.299 * 256 */
++static const unsigned int kGreenYMul = 150; /* 0.587 * 256 */
++static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */
++
++#define SWSTATS_START_LINE_STATS(pixel_t) \
++	pixel_t r, g, g2, b;              \
++	unsigned int yVal;                \
++                                          \
++	unsigned int sumR = 0;            \
++	unsigned int sumG = 0;            \
++	unsigned int sumB = 0;
++
++#define SWSTATS_ACCUMULATE_LINE_STATS(div) \
++	sumR += r;                         \
++	sumG += g;                         \
++	sumB += b;                         \
++                                           \
++	yVal = r * kRedYMul;               \
++	yVal += g * kGreenYMul;            \
++	yVal += b * kBlueYMul;             \
++	stats_.yHistogram[yVal * SwIspStats::kYHistogramSize / (256 * 256 * (div))]++;
++
++#define SWSTATS_FINISH_LINE_STATS() \
++	stats_.sumR_ += sumR;       \
++	stats_.sumG_ += sumG;       \
++	stats_.sumB_ += sumB;
++
++void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
++{
++	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
++	const uint8_t *src1 = src[2] + window_.x * 5 / 4;
++	const int widthInBytes = window_.width * 5 / 4;
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	SWSTATS_START_LINE_STATS(uint8_t)
++
++	/* x += 5 sample every other 2x2 block */
++	for (int x = 0; x < widthInBytes; x += 5) {
++		/* BGGR */
++		b = src0[x];
++		g = src0[x + 1];
++		g2 = src1[x];
++		r = src1[x + 1];
++		g = (g + g2) / 2;
++		/* Data is already 8 bits, divide by 1 */
++		SWSTATS_ACCUMULATE_LINE_STATS(1)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
++void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[])
++{
++	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
++	const uint8_t *src1 = src[2] + window_.x * 5 / 4;
++	const int widthInBytes = window_.width * 5 / 4;
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	SWSTATS_START_LINE_STATS(uint8_t)
++
++	/* x += 5 sample every other 2x2 block */
++	for (int x = 0; x < widthInBytes; x += 5) {
++		/* GBRG */
++		g = src0[x];
++		b = src0[x + 1];
++		r = src1[x];
++		g2 = src1[x + 1];
++		g = (g + g2) / 2;
++		/* Data is already 8 bits, divide by 1 */
++		SWSTATS_ACCUMULATE_LINE_STATS(1)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
++/**
++ * \brief Reset state to start statistics gathering for a new frame.
++ *
++ * This may only be called after a successful setWindow() call.
++ */
++void SwStatsCpu::startFrame(void)
++{
++	stats_.sumR_ = 0;
++	stats_.sumB_ = 0;
++	stats_.sumG_ = 0;
++	stats_.yHistogram.fill(0);
++}
++
++/**
++ * \brief Finish statistics calculation for the current frame.
++ *
++ * This may only be called after a successful setWindow() call.
++ */
++void SwStatsCpu::finishFrame(void)
++{
++	*sharedStats_ = stats_;
++	statsReady.emit(0);
++}
++
++/**
++ * \brief Configure the statistics object for the passed in input format.
++ * \param[in] inputCfg The input format
++ *
++ * \return 0 on success, a negative errno value on failure
++ */
++int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
++{
++	BayerFormat bayerFormat =
++		BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
++
++	if (bayerFormat.bitDepth == 10 &&
++	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
++		patternSize_.height = 2;
++		patternSize_.width = 4; /* 5 bytes per *4* pixels */
++		/* Skip every 3th and 4th line, sample every other 2x2 block */
++		ySkipMask_ = 0x02;
++		xShift_ = 0;
++
++		switch (bayerFormat.order) {
++		case BayerFormat::BGGR:
++		case BayerFormat::GRBG:
++			stats0_ = &SwStatsCpu::statsBGGR10PLine0;
++			swapLines_ = bayerFormat.order == BayerFormat::GRBG;
++			return 0;
++		case BayerFormat::GBRG:
++		case BayerFormat::RGGB:
++			stats0_ = &SwStatsCpu::statsGBRG10PLine0;
++			swapLines_ = bayerFormat.order == BayerFormat::RGGB;
++			return 0;
++		default:
++			break;
++		}
++	}
++
++	LOG(SwStatsCpu, Info)
++		<< "Unsupported input format " << inputCfg.pixelFormat.toString();
++	return -EINVAL;
++}
++
++/**
++ * \brief Specify window coordinates over which to gather statistics.
++ * \param[in] window The window object.
++ */
++void SwStatsCpu::setWindow(Rectangle window)
++{
++	window_ = window;
++
++	window_.x &= ~(patternSize_.width - 1);
++	window_.x += xShift_;
++	window_.y &= ~(patternSize_.height - 1);
++
++	/* width_ - xShift_ to make sure the window fits */
++	window_.width -= xShift_;
++	window_.width &= ~(patternSize_.width - 1);
++	window_.height &= ~(patternSize_.height - 1);
++}
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
+new file mode 100644
+index 00000000..0ac9ae71
+--- /dev/null
++++ b/src/libcamera/software_isp/swstats_cpu.h
+@@ -0,0 +1,159 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * swstats_cpu.h - CPU based software statistics implementation
++ */
++
++#pragma once
++
++#include <stdint.h>
++
++#include <libcamera/base/signal.h>
++
++#include <libcamera/geometry.h>
++
++#include "libcamera/internal/shared_mem_object.h"
++#include "libcamera/internal/software_isp/swisp_stats.h"
++
++namespace libcamera {
++
++class PixelFormat;
++struct StreamConfiguration;
++
++class SwStatsCpu
++{
++public:
++	SwStatsCpu();
++	~SwStatsCpu() = default;
++
++	/**
++	 * \brief Gets whether the statistics object is valid.
++	 *
++	 * \return true if it's valid, false otherwise
++	 */
++	bool isValid() const { return sharedStats_.fd().isValid(); }
++
++	/**
++	 * \brief Get the file descriptor for the statistics.
++	 *
++	 * \return the file descriptor
++	 */
++	const SharedFD &getStatsFD() { return sharedStats_.fd(); }
++
++	/**
++	 * \brief Get the pattern size.
++	 *
++	 * For some input-formats, e.g. Bayer data, processing is done multiple lines
++	 * and/or columns at a time. Get width and height at which the (bayer) pattern
++	 * repeats. Window values are rounded down to a multiple of this and the height
++	 * also indicates if processLine2() should be called or not.
++	 * This may only be called after a successful configure() call.
++	 *
++	 * \return the pattern size
++	 */
++	const Size &patternSize() { return patternSize_; }
++
++	int configure(const StreamConfiguration &inputCfg);
++	void setWindow(Rectangle window);
++	void startFrame();
++	void finishFrame();
++
++	/**
++	 * \brief Process line 0.
++	 * \param[in] y The y coordinate.
++	 * \param[in] src The input data.
++	 *
++	 * This function processes line 0 for input formats with patternSize height == 1.
++	 * It'll process line 0 and 1 for input formats with patternSize height >= 2.
++	 * This function may only be called after a successful setWindow() call.
++	 */
++	void processLine0(unsigned int y, const uint8_t *src[])
++	{
++		if ((y & ySkipMask_) || y < (unsigned int)window_.y ||
++		    y >= (window_.y + window_.height))
++			return;
++
++		(this->*stats0_)(src);
++	}
++
++	/**
++	 * \brief Process line 2 and 3.
++	 * \param[in] y The y coordinate.
++	 * \param[in] src The input data.
++	 *
++	 * This function processes line 2 and 3 for input formats with patternSize height == 4.
++	 * This function may only be called after a successful setWindow() call.
++	 */
++	void processLine2(unsigned int y, const uint8_t *src[])
++	{
++		if ((y & ySkipMask_) || y < (unsigned int)window_.y ||
++		    y >= (window_.y + window_.height))
++			return;
++
++		(this->*stats2_)(src);
++	}
++
++	/**
++	 * \brief Signals that the statistics are ready.
++	 *
++	 * The int parameter isn't actually used.
++	 */
++	Signal<int> statsReady;
++
++private:
++	/**
++	 * \brief Called when there is data to get statistics from.
++	 * \param[in] src The input data
++	 *
++	 * These functions take an array of (patternSize_.height + 1) src
++	 * pointers each pointing to a line in the source image. The middle
++	 * element of the array will point to the actual line being processed.
++	 * Earlier element(s) will point to the previous line(s) and later
++	 * element(s) to the next line(s).
++	 *
++	 * See the documentation of DebayerCpu::debayerFn for more details.
++	 */
++	using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
++
++	void statsBGGR10PLine0(const uint8_t *src[]);
++	void statsGBRG10PLine0(const uint8_t *src[]);
++
++	/* Variables set by configure(), used every line */
++	statsProcessFn stats0_;
++	statsProcessFn stats2_;
++	bool swapLines_;
++
++	/**
++	 * \brief Skip lines where this bitmask is set in y.
++	 */
++	unsigned int ySkipMask_;
++
++	/**
++	 * \brief Statistics window, set by setWindow(), used ever line.
++	 */
++	Rectangle window_;
++
++	/**
++	 * \brief The size of the bayer pattern.
++	 *
++	 * Valid sizes are: 2x2, 4x2 or 4x4.
++	 */
++	Size patternSize_;
++
++	/**
++	 * \brief The offset of x, applied to window_.x for bayer variants.
++	 *
++	 * This can either be 0 or 1.
++	 */
++	unsigned int xShift_;
++
++	SharedMemObject<SwIspStats> sharedStats_;
++	SwIspStats stats_;
++};
++
++} /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch b/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch
new file mode 100644
index 0000000000..7c71709896
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch
@@ -0,0 +1,255 @@
+From 25e6893e46bd2174f6913eea79817988d9280706 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:11 +0100
+Subject: [PATCH 07/21] libcamera: software_isp: Add Debayer base class
+
+Add a base class for debayer implementations. This is intended to be
+suitable for both GPU (or otherwise) accelerated debayer implementations
+as well as CPU based debayering.
+
+Doxygen documentation by Dennis Bonke.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ .../internal/software_isp/debayer_params.h    |  48 ++++++++
+ .../internal/software_isp/meson.build         |   1 +
+ src/libcamera/software_isp/debayer.cpp        |  29 +++++
+ src/libcamera/software_isp/debayer.h          | 104 ++++++++++++++++++
+ src/libcamera/software_isp/meson.build        |   1 +
+ 5 files changed, 183 insertions(+)
+ create mode 100644 include/libcamera/internal/software_isp/debayer_params.h
+ create mode 100644 src/libcamera/software_isp/debayer.cpp
+ create mode 100644 src/libcamera/software_isp/debayer.h
+
+diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
+new file mode 100644
+index 00000000..98965fa1
+--- /dev/null
++++ b/include/libcamera/internal/software_isp/debayer_params.h
+@@ -0,0 +1,48 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer_params.h - DebayerParams header
++ */
++
++#pragma once
++
++namespace libcamera {
++
++/**
++ * \brief Struct to hold the debayer parameters.
++ */
++struct DebayerParams {
++	/**
++	 * \brief const value for 1.0 gain
++	 */
++	static constexpr unsigned int kGain10 = 256;
++
++	/**
++	 * \brief Red Gain
++	 *
++	 * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
++	 */
++	unsigned int gainR;
++	/**
++	 * \brief Green Gain
++	 *
++	 * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
++	 */
++	unsigned int gainG;
++	/**
++	 * \brief Blue Gain
++	 *
++	 * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
++	 */
++	unsigned int gainB;
++	/**
++	 * \brief Gamma correction, 1.0 is no correction
++	 */
++	float gamma;
++};
++
++} /* namespace libcamera */
+diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
+index 66c9c3fb..a620e16d 100644
+--- a/include/libcamera/internal/software_isp/meson.build
++++ b/include/libcamera/internal/software_isp/meson.build
+@@ -1,5 +1,6 @@
+ # SPDX-License-Identifier: CC0-1.0
+ 
+ libcamera_internal_headers += files([
++    'debayer_params.h',
+     'swisp_stats.h',
+ ])
+diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
+new file mode 100644
+index 00000000..64f0b5a0
+--- /dev/null
++++ b/src/libcamera/software_isp/debayer.cpp
+@@ -0,0 +1,29 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer.cpp - debayer base class
++ */
++
++#include "debayer.h"
++
++namespace libcamera {
++
++/**
++ * \class Debayer
++ * \brief Base debayering class
++ *
++ * Base class that provides functions for setting up the debayering process.
++ */
++
++LOG_DEFINE_CATEGORY(Debayer)
++
++Debayer::~Debayer()
++{
++}
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h
+new file mode 100644
+index 00000000..8880ff99
+--- /dev/null
++++ b/src/libcamera/software_isp/debayer.h
+@@ -0,0 +1,104 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer.h - debayering base class
++ */
++
++#pragma once
++
++#include <stdint.h>
++
++#include <libcamera/base/log.h>
++#include <libcamera/base/signal.h>
++
++#include <libcamera/geometry.h>
++#include <libcamera/stream.h>
++
++#include "libcamera/internal/software_isp/debayer_params.h"
++
++namespace libcamera {
++
++class FrameBuffer;
++
++LOG_DECLARE_CATEGORY(Debayer)
++
++class Debayer
++{
++public:
++	virtual ~Debayer() = 0;
++
++	/**
++	 * \brief Configure the debayer object according to the passed in parameters.
++	 * \param[in] inputCfg The input configuration.
++	 * \param[in] outputCfgs The output configurations.
++	 *
++	 * \return 0 on success, a negative errno on failure.
++	 */
++	virtual int configure(const StreamConfiguration &inputCfg,
++			      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0;
++
++	/**
++	 * \brief Get the width and height at which the bayer pattern repeats.
++	 * \param[in] inputFormat The input format.
++	 *
++	 * Valid sizes are: 2x2, 4x2 or 4x4.
++	 *
++	 * \return pattern size or an empty size for unsupported inputFormats.
++	 */
++	virtual Size patternSize(PixelFormat inputFormat) = 0;
++
++	/**
++	 * \brief Get the supported output formats.
++	 * \param[in] inputFormat The input format.
++	 *
++	 * \return all supported output formats or an empty vector if there are none.
++	 */
++	virtual std::vector<PixelFormat> formats(PixelFormat inputFormat) = 0;
++
++	/**
++	 * \brief Get the stride and the frame size.
++	 * \param[in] outputFormat The output format.
++	 * \param[in] size The output size.
++	 *
++	 * \return a tuple of the stride and the frame size, or a tuple with 0,0 if there is no valid output config.
++	 */
++	virtual std::tuple<unsigned int, unsigned int>
++	strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0;
++
++	/**
++	 * \brief Process the bayer data into the requested format.
++	 * \param[in] input The input buffer.
++	 * \param[in] output The output buffer.
++	 * \param[in] params The parameters to be used in debayering.
++	 *
++	 * \note DebayerParams is passed by value deliberately so that a copy is passed
++	 * when this is run in another thread by invokeMethod().
++	 */
++	virtual void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params) = 0;
++
++	/**
++	 * \brief Get the supported output sizes for the given input format and size.
++	 * \param[in] inputFormat The input format.
++	 * \param[in] inputSize The input size.
++	 *
++	 * \return The valid size ranges or an empty range if there are none.
++	 */
++	virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0;
++
++	/**
++	 * \brief Signals when the input buffer is ready.
++	 */
++	Signal<FrameBuffer *> inputBufferReady;
++
++	/**
++	 * \brief Signals when the output buffer is ready.
++	 */
++	Signal<FrameBuffer *> outputBufferReady;
++};
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
+index fcfff74a..62095f61 100644
+--- a/src/libcamera/software_isp/meson.build
++++ b/src/libcamera/software_isp/meson.build
+@@ -8,5 +8,6 @@ if not (softisp_enabled)
+ endif
+ 
+ libcamera_sources += files([
++    'debayer.cpp',
+     'swstats_cpu.cpp',
+ ])
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch
new file mode 100644
index 0000000000..f549769f2f
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch
@@ -0,0 +1,825 @@
+From 5f57a52ea1054cac73344d83ff605cba0df0d279 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:12 +0100
+Subject: [PATCH 08/21] libcamera: software_isp: Add DebayerCpu class
+
+Add CPU based debayering implementation. This initial implementation
+only supports debayering packed 10 bits per pixel bayer data in
+the 4 standard bayer orders.
+
+Doxygen documentation by Dennis Bonke.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Co-developed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ src/libcamera/software_isp/debayer_cpu.cpp | 626 +++++++++++++++++++++
+ src/libcamera/software_isp/debayer_cpu.h   | 143 +++++
+ src/libcamera/software_isp/meson.build     |   1 +
+ 3 files changed, 770 insertions(+)
+ create mode 100644 src/libcamera/software_isp/debayer_cpu.cpp
+ create mode 100644 src/libcamera/software_isp/debayer_cpu.h
+
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+new file mode 100644
+index 00000000..f932362c
+--- /dev/null
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -0,0 +1,626 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer_cpu.cpp - CPU based debayering class
++ */
++
++#include "debayer_cpu.h"
++
++#include <math.h>
++#include <stdlib.h>
++#include <time.h>
++
++#include <libcamera/formats.h>
++
++#include "libcamera/internal/bayer_format.h"
++#include "libcamera/internal/framebuffer.h"
++#include "libcamera/internal/mapped_framebuffer.h"
++
++namespace libcamera {
++
++/**
++ * \class DebayerCpu
++ * \brief Class for debayering on the CPU
++ *
++ * Implementation for CPU based debayering
++ */
++
++/**
++ * \brief Constructs a DebayerCpu object.
++ * \param[in] stats Pointer to the stats object to use.
++ */
++DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
++	: stats_(std::move(stats)), gamma_correction_(1.0)
++{
++#ifdef __x86_64__
++	enableInputMemcpy_ = false;
++#else
++	enableInputMemcpy_ = true;
++#endif
++	/* Initialize gamma to 1.0 curve */
++	for (unsigned int i = 0; i < kGammaLookupSize; i++)
++		gamma_[i] = i / (kGammaLookupSize / kRGBLookupSize);
++
++	for (unsigned int i = 0; i < kMaxLineBuffers; i++)
++		lineBuffers_[i] = nullptr;
++}
++
++DebayerCpu::~DebayerCpu()
++{
++	for (unsigned int i = 0; i < kMaxLineBuffers; i++)
++		free(lineBuffers_[i]);
++}
++
++// RGR
++// GBG
++// RGR
++#define BGGR_BGR888(p, n, div)                                                                \
++	*dst++ = blue_[curr[x] / (div)];                                                      \
++	*dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))];       \
++	*dst++ = red_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \
++	x++;
++
++// GBG
++// RGR
++// GBG
++#define GRBG_BGR888(p, n, div)                                    \
++	*dst++ = blue_[(prev[x] + next[x]) / (2 * (div))];        \
++	*dst++ = green_[curr[x] / (div)];                         \
++	*dst++ = red_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \
++	x++;
++
++// GRG
++// BGB
++// GRG
++#define GBRG_BGR888(p, n, div)                                     \
++	*dst++ = blue_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \
++	*dst++ = green_[curr[x] / (div)];                          \
++	*dst++ = red_[(prev[x] + next[x]) / (2 * (div))];          \
++	x++;
++
++// BGB
++// GRG
++// BGB
++#define RGGB_BGR888(p, n, div)                                                                 \
++	*dst++ = blue_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \
++	*dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))];        \
++	*dst++ = red_[curr[x] / (div)];                                                        \
++	x++;
++
++void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	const int width_in_bytes = window_.width * 5 / 4;
++	const uint8_t *prev = (const uint8_t *)src[0];
++	const uint8_t *curr = (const uint8_t *)src[1];
++	const uint8_t *next = (const uint8_t *)src[2];
++
++	/*
++	 * For the first pixel getting a pixel from the previous column uses
++	 * x - 2 to skip the 5th byte with least-significant bits for 4 pixels.
++	 * Same for last pixel (uses x + 2) and looking at the next column.
++	 */
++	for (int x = 0; x < width_in_bytes;) {
++		/* First pixel */
++		BGGR_BGR888(2, 1, 1)
++		/* Second pixel BGGR -> GBRG */
++		GBRG_BGR888(1, 1, 1)
++		/* Same thing for third and fourth pixels */
++		BGGR_BGR888(1, 1, 1)
++		GBRG_BGR888(1, 2, 1)
++		/* Skip 5th src byte with 4 x 2 least-significant-bits */
++		x++;
++	}
++}
++
++void DebayerCpu::debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	const int width_in_bytes = window_.width * 5 / 4;
++	const uint8_t *prev = (const uint8_t *)src[0];
++	const uint8_t *curr = (const uint8_t *)src[1];
++	const uint8_t *next = (const uint8_t *)src[2];
++
++	for (int x = 0; x < width_in_bytes;) {
++		/* First pixel */
++		GRBG_BGR888(2, 1, 1)
++		/* Second pixel GRBG -> RGGB */
++		RGGB_BGR888(1, 1, 1)
++		/* Same thing for third and fourth pixels */
++		GRBG_BGR888(1, 1, 1)
++		RGGB_BGR888(1, 2, 1)
++		/* Skip 5th src byte with 4 x 2 least-significant-bits */
++		x++;
++	}
++}
++
++void DebayerCpu::debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	const int width_in_bytes = window_.width * 5 / 4;
++	const uint8_t *prev = (const uint8_t *)src[0];
++	const uint8_t *curr = (const uint8_t *)src[1];
++	const uint8_t *next = (const uint8_t *)src[2];
++
++	for (int x = 0; x < width_in_bytes;) {
++		/* Even pixel */
++		GBRG_BGR888(2, 1, 1)
++		/* Odd pixel GBGR -> BGGR */
++		BGGR_BGR888(1, 1, 1)
++		/* Same thing for next 2 pixels */
++		GBRG_BGR888(1, 1, 1)
++		BGGR_BGR888(1, 2, 1)
++		/* Skip 5th src byte with 4 x 2 least-significant-bits */
++		x++;
++	}
++}
++
++void DebayerCpu::debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	const int width_in_bytes = window_.width * 5 / 4;
++	const uint8_t *prev = (const uint8_t *)src[0];
++	const uint8_t *curr = (const uint8_t *)src[1];
++	const uint8_t *next = (const uint8_t *)src[2];
++
++	for (int x = 0; x < width_in_bytes;) {
++		/* Even pixel */
++		RGGB_BGR888(2, 1, 1)
++		/* Odd pixel RGGB -> GRBG */
++		GRBG_BGR888(1, 1, 1)
++		/* Same thing for next 2 pixels */
++		RGGB_BGR888(1, 1, 1)
++		GRBG_BGR888(1, 2, 1)
++		/* Skip 5th src byte with 4 x 2 least-significant-bits */
++		x++;
++	}
++}
++
++static bool isStandardBayerOrder(BayerFormat::Order order)
++{
++	return order == BayerFormat::BGGR || order == BayerFormat::GBRG ||
++	       order == BayerFormat::GRBG || order == BayerFormat::RGGB;
++}
++
++/*
++ * Setup the Debayer object according to the passed in parameters.
++ * Return 0 on success, a negative errno value on failure
++ * (unsupported parameters).
++ */
++int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)
++{
++	BayerFormat bayerFormat =
++		BayerFormat::fromPixelFormat(inputFormat);
++
++	if (bayerFormat.bitDepth == 10 &&
++	    bayerFormat.packing == BayerFormat::Packing::CSI2 &&
++	    isStandardBayerOrder(bayerFormat.order)) {
++		config.bpp = 10;
++		config.patternSize.width = 4; /* 5 bytes per *4* pixels */
++		config.patternSize.height = 2;
++		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
++		return 0;
++	}
++
++	LOG(Debayer, Info)
++		<< "Unsupported input format " << inputFormat.toString();
++	return -EINVAL;
++}
++
++int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
++{
++	if (outputFormat == formats::RGB888) {
++		config.bpp = 24;
++		return 0;
++	}
++
++	LOG(Debayer, Info)
++		<< "Unsupported output format " << outputFormat.toString();
++	return -EINVAL;
++}
++
++/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */
++int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat)
++{
++	BayerFormat bayerFormat =
++		BayerFormat::fromPixelFormat(inputFormat);
++
++	if (bayerFormat.bitDepth == 10 &&
++	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
++		switch (bayerFormat.order) {
++		case BayerFormat::BGGR:
++			debayer0_ = &DebayerCpu::debayer10P_BGBG_BGR888;
++			debayer1_ = &DebayerCpu::debayer10P_GRGR_BGR888;
++			return 0;
++		case BayerFormat::GBRG:
++			debayer0_ = &DebayerCpu::debayer10P_GBGB_BGR888;
++			debayer1_ = &DebayerCpu::debayer10P_RGRG_BGR888;
++			return 0;
++		case BayerFormat::GRBG:
++			debayer0_ = &DebayerCpu::debayer10P_GRGR_BGR888;
++			debayer1_ = &DebayerCpu::debayer10P_BGBG_BGR888;
++			return 0;
++		case BayerFormat::RGGB:
++			debayer0_ = &DebayerCpu::debayer10P_RGRG_BGR888;
++			debayer1_ = &DebayerCpu::debayer10P_GBGB_BGR888;
++			return 0;
++		default:
++			break;
++		}
++	}
++
++	LOG(Debayer, Error) << "Unsupported input output format combination";
++	return -EINVAL;
++}
++
++int DebayerCpu::configure(const StreamConfiguration &inputCfg,
++			  const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)
++{
++	if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)
++		return -EINVAL;
++
++	if (stats_->configure(inputCfg) != 0)
++		return -EINVAL;
++
++	const Size &stats_pattern_size = stats_->patternSize();
++	if (inputConfig_.patternSize.width != stats_pattern_size.width ||
++	    inputConfig_.patternSize.height != stats_pattern_size.height) {
++		LOG(Debayer, Error)
++			<< "mismatching stats and debayer pattern sizes for "
++			<< inputCfg.pixelFormat.toString();
++		return -EINVAL;
++	}
++
++	inputConfig_.stride = inputCfg.stride;
++
++	if (outputCfgs.size() != 1) {
++		LOG(Debayer, Error)
++			<< "Unsupported number of output streams: "
++			<< outputCfgs.size();
++		return -EINVAL;
++	}
++
++	const StreamConfiguration &outputCfg = outputCfgs[0];
++	SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
++	std::tie(outputConfig_.stride, outputConfig_.frameSize) =
++		strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);
++
++	if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
++		LOG(Debayer, Error)
++			<< "Invalid output size/stride: "
++			<< "\n  " << outputCfg.size << " (" << outSizeRange << ")"
++			<< "\n  " << outputCfg.stride << " (" << outputConfig_.stride << ")";
++		return -EINVAL;
++	}
++
++	if (setDebayerFunctions(inputCfg.pixelFormat, outputCfg.pixelFormat) != 0)
++		return -EINVAL;
++
++	window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &
++		    ~(inputConfig_.patternSize.width - 1);
++	window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &
++		    ~(inputConfig_.patternSize.height - 1);
++	window_.width = outputCfg.size.width;
++	window_.height = outputCfg.size.height;
++
++	/* Don't pass x,y since process() already adjusts src before passing it */
++	stats_->setWindow(Rectangle(window_.size()));
++
++	/* pad with patternSize.Width on both left and right side */
++	lineBufferPadding_ = inputConfig_.patternSize.width * inputConfig_.bpp / 8;
++	lineBufferLength_ = window_.width * inputConfig_.bpp / 8 +
++			    2 * lineBufferPadding_;
++	for (unsigned int i = 0;
++	     i < (inputConfig_.patternSize.height + 1) && enableInputMemcpy_;
++	     i++) {
++		free(lineBuffers_[i]);
++		lineBuffers_[i] = (uint8_t *)malloc(lineBufferLength_);
++		if (!lineBuffers_[i])
++			return -ENOMEM;
++	}
++
++	measuredFrames_ = 0;
++	frameProcessTime_ = 0;
++
++	return 0;
++}
++
++/*
++ * Get width and height at which the bayer-pattern repeats.
++ * Return pattern-size or an empty Size for an unsupported inputFormat.
++ */
++Size DebayerCpu::patternSize(PixelFormat inputFormat)
++{
++	DebayerCpu::DebayerInputConfig config;
++
++	if (getInputConfig(inputFormat, config) != 0)
++		return {};
++
++	return config.patternSize;
++}
++
++std::vector<PixelFormat> DebayerCpu::formats(PixelFormat inputFormat)
++{
++	DebayerCpu::DebayerInputConfig config;
++
++	if (getInputConfig(inputFormat, config) != 0)
++		return std::vector<PixelFormat>();
++
++	return config.outputFormats;
++}
++
++std::tuple<unsigned int, unsigned int>
++DebayerCpu::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
++{
++	DebayerCpu::DebayerOutputConfig config;
++
++	if (getOutputConfig(outputFormat, config) != 0)
++		return std::make_tuple(0, 0);
++
++	/* round up to multiple of 8 for 64 bits alignment */
++	unsigned int stride = (size.width * config.bpp / 8 + 7) & ~7;
++
++	return std::make_tuple(stride, stride * size.height);
++}
++
++void DebayerCpu::setupInputMemcpy(const uint8_t *linePointers[])
++{
++	const unsigned int patternHeight = inputConfig_.patternSize.height;
++
++	if (!enableInputMemcpy_)
++		return;
++
++	for (unsigned int i = 0; i < patternHeight; i++) {
++		memcpy(lineBuffers_[i], linePointers[i + 1] - lineBufferPadding_,
++		       lineBufferLength_);
++		linePointers[i + 1] = lineBuffers_[i] + lineBufferPadding_;
++	}
++
++	/* Point lineBufferIndex_ to first unused lineBuffer */
++	lineBufferIndex_ = patternHeight;
++}
++
++void DebayerCpu::shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src)
++{
++	const unsigned int patternHeight = inputConfig_.patternSize.height;
++
++	for (unsigned int i = 0; i < patternHeight; i++)
++		linePointers[i] = linePointers[i + 1];
++
++	linePointers[patternHeight] = src +
++				      (patternHeight / 2) * (int)inputConfig_.stride;
++}
++
++void DebayerCpu::memcpyNextLine(const uint8_t *linePointers[])
++{
++	const unsigned int patternHeight = inputConfig_.patternSize.height;
++
++	if (!enableInputMemcpy_)
++		return;
++
++	memcpy(lineBuffers_[lineBufferIndex_], linePointers[patternHeight] - lineBufferPadding_,
++	       lineBufferLength_);
++	linePointers[patternHeight] = lineBuffers_[lineBufferIndex_] + lineBufferPadding_;
++
++	lineBufferIndex_ = (lineBufferIndex_ + 1) % (patternHeight + 1);
++}
++
++void DebayerCpu::process2(const uint8_t *src, uint8_t *dst)
++{
++	unsigned int y_end = window_.y + window_.height;
++	/* Holds [0] previous- [1] current- [2] next-line */
++	const uint8_t *linePointers[3];
++
++	/* Adjust src to top left corner of the window */
++	src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
++
++	/* [x] becomes [x - 1] after initial shiftLinePointers() call */
++	if (window_.y) {
++		linePointers[1] = src - inputConfig_.stride; /* previous-line */
++		linePointers[2] = src;
++	} else {
++		/* window_.y == 0, use the next line as prev line */
++		linePointers[1] = src + inputConfig_.stride;
++		linePointers[2] = src;
++		/* Last 2 lines also need special handling */
++		y_end -= 2;
++	}
++
++	setupInputMemcpy(linePointers);
++
++	for (unsigned int y = window_.y; y < y_end; y += 2) {
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		stats_->processLine0(y, linePointers);
++		(this->*debayer0_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		(this->*debayer1_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++	}
++
++	if (window_.y == 0) {
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		stats_->processLine0(y_end, linePointers);
++		(this->*debayer0_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		/* next line may point outside of src, use prev. */
++		linePointers[2] = linePointers[0];
++		(this->*debayer1_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++	}
++}
++
++void DebayerCpu::process4(const uint8_t *src, uint8_t *dst)
++{
++	const unsigned int y_end = window_.y + window_.height;
++	/*
++	 * This holds pointers to [0] 2-lines-up [1] 1-line-up [2] current-line
++	 * [3] 1-line-down [4] 2-lines-down.
++	 */
++	const uint8_t *linePointers[5];
++
++	/* Adjust src to top left corner of the window */
++	src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
++
++	/* [x] becomes [x - 1] after initial shiftLinePointers() call */
++	linePointers[1] = src - 2 * inputConfig_.stride;
++	linePointers[2] = src - inputConfig_.stride;
++	linePointers[3] = src;
++	linePointers[4] = src + inputConfig_.stride;
++
++	setupInputMemcpy(linePointers);
++
++	for (unsigned int y = window_.y; y < y_end; y += 4) {
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		stats_->processLine0(y, linePointers);
++		(this->*debayer0_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		(this->*debayer1_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		stats_->processLine2(y, linePointers);
++		(this->*debayer2_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		(this->*debayer3_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++	}
++}
++
++static inline int64_t timeDiff(timespec &after, timespec &before)
++{
++	return (after.tv_sec - before.tv_sec) * 1000000000LL +
++	       (int64_t)after.tv_nsec - (int64_t)before.tv_nsec;
++}
++
++void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams params)
++{
++	timespec frameStartTime;
++
++	if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure) {
++		frameStartTime = {};
++		clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime);
++	}
++
++	/* Apply DebayerParams */
++	if (params.gamma != gamma_correction_) {
++		for (unsigned int i = 0; i < kGammaLookupSize; i++)
++			gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma);
++
++		gamma_correction_ = params.gamma;
++	}
++
++	for (unsigned int i = 0; i < kRGBLookupSize; i++) {
++		constexpr unsigned int div =
++			kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize;
++		unsigned int idx;
++
++		/* Apply gamma after gain! */
++		idx = std::min({ i * params.gainR / div, (kGammaLookupSize - 1) });
++		red_[i] = gamma_[idx];
++
++		idx = std::min({ i * params.gainG / div, (kGammaLookupSize - 1) });
++		green_[i] = gamma_[idx];
++
++		idx = std::min({ i * params.gainB / div, (kGammaLookupSize - 1) });
++		blue_[i] = gamma_[idx];
++	}
++
++	/* Copy metadata from the input buffer */
++	FrameMetadata &metadata = output->_d()->metadata();
++	metadata.status = input->metadata().status;
++	metadata.sequence = input->metadata().sequence;
++	metadata.timestamp = input->metadata().timestamp;
++
++	MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
++	MappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write);
++	if (!in.isValid() || !out.isValid()) {
++		LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
++		metadata.status = FrameMetadata::FrameError;
++		return;
++	}
++
++	stats_->startFrame();
++
++	if (inputConfig_.patternSize.height == 2)
++		process2(in.planes()[0].data(), out.planes()[0].data());
++	else
++		process4(in.planes()[0].data(), out.planes()[0].data());
++
++	metadata.planes()[0].bytesused = out.planes()[0].size();
++
++	/* Measure before emitting signals */
++	if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure &&
++	    ++measuredFrames_ > DebayerCpu::kFramesToSkip) {
++		timespec frameEndTime = {};
++		clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime);
++		frameProcessTime_ += timeDiff(frameEndTime, frameStartTime);
++		if (measuredFrames_ == DebayerCpu::kLastFrameToMeasure) {
++			const unsigned int measuredFrames = DebayerCpu::kLastFrameToMeasure -
++							    DebayerCpu::kFramesToSkip;
++			LOG(Debayer, Info)
++				<< "Processed " << measuredFrames
++				<< " frames in " << frameProcessTime_ / 1000 << "us, "
++				<< frameProcessTime_ / (1000 * measuredFrames)
++				<< " us/frame";
++		}
++	}
++
++	stats_->finishFrame();
++	outputBufferReady.emit(output);
++	inputBufferReady.emit(input);
++}
++
++SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
++{
++	Size pattern_size = patternSize(inputFormat);
++	unsigned int border_height = pattern_size.height;
++
++	if (pattern_size.isNull())
++		return {};
++
++	/* No need for top/bottom border with a pattern height of 2 */
++	if (pattern_size.height == 2)
++		border_height = 0;
++
++	/*
++	 * For debayer interpolation a border is kept around the entire image
++	 * and the minimum output size is pattern-height x pattern-width.
++	 */
++	if (inputSize.width < (3 * pattern_size.width) ||
++	    inputSize.height < (2 * border_height + pattern_size.height)) {
++		LOG(Debayer, Warning)
++			<< "Input format size too small: " << inputSize.toString();
++		return {};
++	}
++
++	return SizeRange(Size(pattern_size.width, pattern_size.height),
++			 Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
++			      (inputSize.height - 2 * border_height) & ~(pattern_size.height - 1)),
++			 pattern_size.width, pattern_size.height);
++}
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
+new file mode 100644
+index 00000000..8a51ed85
+--- /dev/null
++++ b/src/libcamera/software_isp/debayer_cpu.h
+@@ -0,0 +1,143 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer_cpu.h - CPU based debayering header
++ */
++
++#pragma once
++
++#include <memory>
++#include <stdint.h>
++#include <vector>
++
++#include <libcamera/base/object.h>
++
++#include "debayer.h"
++#include "swstats_cpu.h"
++
++namespace libcamera {
++
++class DebayerCpu : public Debayer, public Object
++{
++public:
++	DebayerCpu(std::unique_ptr<SwStatsCpu> stats);
++	~DebayerCpu();
++
++	int configure(const StreamConfiguration &inputCfg,
++		      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs);
++	Size patternSize(PixelFormat inputFormat);
++	std::vector<PixelFormat> formats(PixelFormat input);
++	std::tuple<unsigned int, unsigned int>
++	strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
++	void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params);
++	SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
++
++	/**
++	 * \brief Get the file descriptor for the statistics.
++	 *
++	 * \return the file descriptor pointing to the statistics.
++	 */
++	const SharedFD &getStatsFD() { return stats_->getStatsFD(); }
++
++	/**
++	 * \brief Get the output frame size.
++	 *
++	 * \return The output frame size.
++	 */
++	unsigned int frameSize() { return outputConfig_.frameSize; }
++
++private:
++	/**
++	 * \brief Called to debayer 1 line of Bayer input data to output format
++	 * \param[out] dst Pointer to the start of the output line to write
++	 * \param[in] src The input data
++	 *
++	 * Input data is an array of (patternSize_.height + 1) src
++	 * pointers each pointing to a line in the Bayer source. The middle
++	 * element of the array will point to the actual line being processed.
++	 * Earlier element(s) will point to the previous line(s) and later
++	 * element(s) to the next line(s).
++	 *
++	 * These functions take an array of src pointers, rather than
++	 * a single src pointer + a stride for the source, so that when the src
++	 * is slow uncached memory it can be copied to faster memory before
++	 * debayering. Debayering a standard 2x2 Bayer pattern requires access
++	 * to the previous and next src lines for interpolating the missing
++	 * colors. To allow copying the src lines only once 3 temporary buffers
++	 * each holding a single line are used, re-using the oldest buffer for
++	 * the next line and the pointers are swizzled so that:
++	 * src[0] = previous-line, src[1] = currrent-line, src[2] = next-line.
++	 * This way the 3 pointers passed to the debayer functions form
++	 * a sliding window over the src avoiding the need to copy each
++	 * line more than once.
++	 *
++	 * Similarly for bayer patterns which repeat every 4 lines, 5 src
++	 * pointers are passed holding: src[0] = 2-lines-up, src[1] = 1-line-up
++	 * src[2] = current-line, src[3] = 1-line-down, src[4] = 2-lines-down.
++	 */
++	using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]);
++
++	/* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
++	void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
++
++	struct DebayerInputConfig {
++		Size patternSize;
++		unsigned int bpp; /* Memory used per pixel, not precision */
++		unsigned int stride;
++		std::vector<PixelFormat> outputFormats;
++	};
++
++	struct DebayerOutputConfig {
++		unsigned int bpp; /* Memory used per pixel, not precision */
++		unsigned int stride;
++		unsigned int frameSize;
++	};
++
++	int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
++	int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
++	int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
++	void setupInputMemcpy(const uint8_t *linePointers[]);
++	void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
++	void memcpyNextLine(const uint8_t *linePointers[]);
++	void process2(const uint8_t *src, uint8_t *dst);
++	void process4(const uint8_t *src, uint8_t *dst);
++
++	static constexpr unsigned int kGammaLookupSize = 1024;
++	static constexpr unsigned int kRGBLookupSize = 256;
++	/* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */
++	static constexpr unsigned int kMaxLineBuffers = 5;
++
++	std::array<uint8_t, kGammaLookupSize> gamma_;
++	std::array<uint8_t, kRGBLookupSize> red_;
++	std::array<uint8_t, kRGBLookupSize> green_;
++	std::array<uint8_t, kRGBLookupSize> blue_;
++	debayerFn debayer0_;
++	debayerFn debayer1_;
++	debayerFn debayer2_;
++	debayerFn debayer3_;
++	Rectangle window_;
++	DebayerInputConfig inputConfig_;
++	DebayerOutputConfig outputConfig_;
++	std::unique_ptr<SwStatsCpu> stats_;
++	uint8_t *lineBuffers_[kMaxLineBuffers];
++	unsigned int lineBufferLength_;
++	unsigned int lineBufferPadding_;
++	unsigned int lineBufferIndex_;
++	bool enableInputMemcpy_;
++	float gamma_correction_;
++	unsigned int measuredFrames_;
++	int64_t frameProcessTime_;
++	/* Skip 30 frames for things to stabilize then measure 30 frames */
++	static constexpr unsigned int kFramesToSkip = 30;
++	static constexpr unsigned int kLastFrameToMeasure = 60;
++};
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
+index 62095f61..71b46539 100644
+--- a/src/libcamera/software_isp/meson.build
++++ b/src/libcamera/software_isp/meson.build
+@@ -9,5 +9,6 @@ endif
+ 
+ libcamera_sources += files([
+     'debayer.cpp',
++    'debayer_cpu.cpp',
+     'swstats_cpu.cpp',
+ ])
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch b/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch
new file mode 100644
index 0000000000..40f9403ba9
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch
@@ -0,0 +1,506 @@
+From 5261c801d8425fa82bcbd3da0199d06153eb5bd7 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:13 +0100
+Subject: [PATCH 09/21] libcamera: ipa: add Soft IPA
+
+Define the Soft IPA main and event interfaces, add the Soft IPA
+implementation.
+
+The current src/ipa/meson.build assumes the IPA name to match the
+pipeline name. For this reason "-Dipas=simple" is used for the
+Soft IPA module.
+
+Auto exposure/gain and AWB implementation by Dennis, Toon and Martti.
+
+Auto exposure/gain targets a Mean Sample Value of 2.5 following
+the MSV calculation algorithm from:
+https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Co-developed-by: Marttico <g.martti@gmail.com>
+Signed-off-by: Marttico <g.martti@gmail.com>
+Co-developed-by: Toon Langendam <t.langendam@gmail.com>
+Signed-off-by: Toon Langendam <t.langendam@gmail.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ Documentation/Doxyfile.in         |   1 +
+ include/libcamera/ipa/meson.build |   1 +
+ include/libcamera/ipa/soft.mojom  |  28 +++
+ meson_options.txt                 |   2 +-
+ src/ipa/simple/data/meson.build   |   9 +
+ src/ipa/simple/data/soft.conf     |   3 +
+ src/ipa/simple/meson.build        |  25 +++
+ src/ipa/simple/soft_simple.cpp    | 326 ++++++++++++++++++++++++++++++
+ 8 files changed, 394 insertions(+), 1 deletion(-)
+ create mode 100644 include/libcamera/ipa/soft.mojom
+ create mode 100644 src/ipa/simple/data/meson.build
+ create mode 100644 src/ipa/simple/data/soft.conf
+ create mode 100644 src/ipa/simple/meson.build
+ create mode 100644 src/ipa/simple/soft_simple.cpp
+
+diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
+index a86ea6c1..2be8d47b 100644
+--- a/Documentation/Doxyfile.in
++++ b/Documentation/Doxyfile.in
+@@ -44,6 +44,7 @@ EXCLUDE                = @TOP_SRCDIR@/include/libcamera/base/span.h \
+                          @TOP_SRCDIR@/src/libcamera/pipeline/ \
+                          @TOP_SRCDIR@/src/libcamera/tracepoints.cpp \
+                          @TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \
++                         @TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \
+                          @TOP_BUILDDIR@/src/libcamera/proxy/
+ 
+ EXCLUDE_PATTERNS       = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \
+diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build
+index f3b4881c..3352d08f 100644
+--- a/include/libcamera/ipa/meson.build
++++ b/include/libcamera/ipa/meson.build
+@@ -65,6 +65,7 @@ pipeline_ipa_mojom_mapping = {
+     'ipu3': 'ipu3.mojom',
+     'rkisp1': 'rkisp1.mojom',
+     'rpi/vc4': 'raspberrypi.mojom',
++    'simple': 'soft.mojom',
+     'vimc': 'vimc.mojom',
+ }
+ 
+diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
+new file mode 100644
+index 00000000..c249bd75
+--- /dev/null
++++ b/include/libcamera/ipa/soft.mojom
+@@ -0,0 +1,28 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++
++/*
++ * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry.
++ */
++
++module ipa.soft;
++
++import "include/libcamera/ipa/core.mojom";
++
++interface IPASoftInterface {
++	init(libcamera.IPASettings settings,
++	     libcamera.SharedFD fdStats,
++	     libcamera.SharedFD fdParams,
++	     libcamera.ControlInfoMap sensorCtrlInfoMap)
++		=> (int32 ret);
++	start() => (int32 ret);
++	stop();
++	configure(libcamera.ControlInfoMap sensorCtrlInfoMap)
++		=> (int32 ret);
++
++	[async] processStats(libcamera.ControlList sensorControls);
++};
++
++interface IPASoftEventInterface {
++	setSensorControls(libcamera.ControlList sensorControls);
++	setIspParams(int32 dummy);
++};
+diff --git a/meson_options.txt b/meson_options.txt
+index 5fdc7be8..94372e47 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -27,7 +27,7 @@ option('gstreamer',
+ 
+ option('ipas',
+         type : 'array',
+-        choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'],
++        choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],
+         description : 'Select which IPA modules to build')
+ 
+ option('lc-compliance',
+diff --git a/src/ipa/simple/data/meson.build b/src/ipa/simple/data/meson.build
+new file mode 100644
+index 00000000..33548cc6
+--- /dev/null
++++ b/src/ipa/simple/data/meson.build
+@@ -0,0 +1,9 @@
++# SPDX-License-Identifier: CC0-1.0
++
++conf_files = files([
++    'soft.conf',
++])
++
++install_data(conf_files,
++             install_dir : ipa_data_dir / 'soft',
++             install_tag : 'runtime')
+diff --git a/src/ipa/simple/data/soft.conf b/src/ipa/simple/data/soft.conf
+new file mode 100644
+index 00000000..0c70e7c0
+--- /dev/null
++++ b/src/ipa/simple/data/soft.conf
+@@ -0,0 +1,3 @@
++# SPDX-License-Identifier: LGPL-2.1-or-later
++#
++# Dummy configuration file for the soft IPA.
+diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
+new file mode 100644
+index 00000000..3e863db7
+--- /dev/null
++++ b/src/ipa/simple/meson.build
+@@ -0,0 +1,25 @@
++# SPDX-License-Identifier: CC0-1.0
++
++ipa_name = 'ipa_soft_simple'
++
++mod = shared_module(ipa_name,
++                    ['soft_simple.cpp', libcamera_generated_ipa_headers],
++                    name_prefix : '',
++                    include_directories : [ipa_includes, libipa_includes],
++                    dependencies : libcamera_private,
++                    link_with : libipa,
++                    install : true,
++                    install_dir : ipa_install_dir)
++
++if ipa_sign_module
++    custom_target(ipa_name + '.so.sign',
++                  input : mod,
++                  output : ipa_name + '.so.sign',
++                  command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],
++                  install : false,
++                  build_by_default : true)
++endif
++
++subdir('data')
++
++ipa_names += ipa_name
+diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
+new file mode 100644
+index 00000000..312df4ba
+--- /dev/null
++++ b/src/ipa/simple/soft_simple.cpp
+@@ -0,0 +1,326 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ *
++ * soft_simple.cpp - Simple Software Image Processing Algorithm module
++ */
++
++#include <sys/mman.h>
++
++#include <libcamera/base/file.h>
++#include <libcamera/base/log.h>
++#include <libcamera/base/shared_fd.h>
++
++#include <libcamera/control_ids.h>
++#include <libcamera/controls.h>
++
++#include <libcamera/ipa/ipa_interface.h>
++#include <libcamera/ipa/ipa_module_info.h>
++#include <libcamera/ipa/soft_ipa_interface.h>
++
++#include "libcamera/internal/camera_sensor.h"
++#include "libcamera/internal/software_isp/debayer_params.h"
++#include "libcamera/internal/software_isp/swisp_stats.h"
++
++namespace libcamera {
++
++LOG_DEFINE_CATEGORY(IPASoft)
++
++namespace ipa::soft {
++
++class IPASoftSimple : public ipa::soft::IPASoftInterface
++{
++public:
++	IPASoftSimple()
++		: params_(static_cast<DebayerParams *>(MAP_FAILED)),
++		  stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0)
++	{
++	}
++
++	~IPASoftSimple()
++	{
++		if (stats_ != MAP_FAILED)
++			munmap(stats_, sizeof(SwIspStats));
++		if (params_ != MAP_FAILED)
++			munmap(params_, sizeof(DebayerParams));
++	}
++
++	int init(const IPASettings &settings,
++		 const SharedFD &fdStats,
++		 const SharedFD &fdParams,
++		 const ControlInfoMap &sensorInfoMap) override;
++	int configure(const ControlInfoMap &sensorInfoMap) override;
++
++	int start() override;
++	void stop() override;
++
++	void processStats(const ControlList &sensorControls) override;
++
++private:
++	void updateExposure(double exposureMSV);
++
++	SharedFD fdStats_;
++	SharedFD fdParams_;
++	DebayerParams *params_;
++	SwIspStats *stats_;
++
++	int32_t exposure_min_, exposure_max_;
++	int32_t again_min_, again_max_;
++	int32_t again_, exposure_;
++	unsigned int ignore_updates_;
++};
++
++int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,
++			const SharedFD &fdStats,
++			const SharedFD &fdParams,
++			const ControlInfoMap &sensorInfoMap)
++{
++	fdStats_ = fdStats;
++	if (!fdStats_.isValid()) {
++		LOG(IPASoft, Error) << "Invalid Statistics handle";
++		return -ENODEV;
++	}
++
++	fdParams_ = fdParams;
++	if (!fdParams_.isValid()) {
++		LOG(IPASoft, Error) << "Invalid Parameters handle";
++		return -ENODEV;
++	}
++
++	params_ = static_cast<DebayerParams *>(mmap(nullptr, sizeof(DebayerParams),
++						    PROT_WRITE, MAP_SHARED,
++						    fdParams_.get(), 0));
++	if (params_ == MAP_FAILED) {
++		LOG(IPASoft, Error) << "Unable to map Parameters";
++		return -errno;
++	}
++
++	stats_ = static_cast<SwIspStats *>(mmap(nullptr, sizeof(SwIspStats),
++						PROT_READ, MAP_SHARED,
++						fdStats_.get(), 0));
++	if (stats_ == MAP_FAILED) {
++		LOG(IPASoft, Error) << "Unable to map Statistics";
++		return -errno;
++	}
++
++	if (sensorInfoMap.find(V4L2_CID_EXPOSURE) == sensorInfoMap.end()) {
++		LOG(IPASoft, Error) << "Don't have exposure control";
++		return -EINVAL;
++	}
++
++	if (sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN) == sensorInfoMap.end()) {
++		LOG(IPASoft, Error) << "Don't have gain control";
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap)
++{
++	const ControlInfo &exposure_info = sensorInfoMap.find(V4L2_CID_EXPOSURE)->second;
++	const ControlInfo &gain_info = sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN)->second;
++
++	exposure_min_ = exposure_info.min().get<int32_t>();
++	exposure_max_ = exposure_info.max().get<int32_t>();
++	if (!exposure_min_) {
++		LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
++		exposure_min_ = 1;
++	}
++
++	again_min_ = gain_info.min().get<int32_t>();
++	again_max_ = gain_info.max().get<int32_t>();
++	/*
++	 * The camera sensor gain (g) is usually not equal to the value written
++	 * into the gain register (x). But the way how the AGC algorithm changes
++	 * the gain value to make the total exposure closer to the optimum assumes
++	 * that g(x) is not too far from linear function. If the minimal gain is 0,
++	 * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
++	 * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
++	 * one edge, and very small near the other) we limit the range of the gain
++	 * values used.
++	 */
++	if (!again_min_) {
++		LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
++		again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);
++	}
++
++	LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
++			   << ", gain " << again_min_ << "-" << again_max_;
++
++	return 0;
++}
++
++int IPASoftSimple::start()
++{
++	return 0;
++}
++
++void IPASoftSimple::stop()
++{
++}
++
++/*
++ * The number of bins to use for the optimal exposure calculations.
++ */
++static constexpr unsigned int kExposureBinsCount = 5;
++/*
++ * The exposure is optimal when the mean sample value of the histogram is
++ * in the middle of the range.
++ */
++static constexpr float kExposureOptimal = kExposureBinsCount / 2.0;
++/*
++ * The below value implements the hysteresis for the exposure adjustment.
++ * It is small enough to have the exposure close to the optimal, and is big
++ * enough to prevent the exposure from wobbling around the optimal value.
++ */
++static constexpr float kExposureSatisfactory = 0.2;
++
++void IPASoftSimple::processStats(const ControlList &sensorControls)
++{
++	/*
++	 * Calculate red and blue gains for AWB.
++	 * Clamp max gain at 4.0, this also avoids 0 division.
++	 */
++	if (stats_->sumR_ <= stats_->sumG_ / 4)
++		params_->gainR = 1024;
++	else
++		params_->gainR = 256 * stats_->sumG_ / stats_->sumR_;
++
++	if (stats_->sumB_ <= stats_->sumG_ / 4)
++		params_->gainB = 1024;
++	else
++		params_->gainB = 256 * stats_->sumG_ / stats_->sumB_;
++
++	/* Green gain and gamma values are fixed */
++	params_->gainG = 256;
++	params_->gamma = 0.5;
++
++	setIspParams.emit(0);
++
++	/*
++	 * AE / AGC, use 2 frames delay to make sure that the exposure and
++	 * the gain set have applied to the camera sensor.
++	 */
++	if (ignore_updates_ > 0) {
++		--ignore_updates_;
++		return;
++	}
++
++	/*
++	 * Calculate Mean Sample Value (MSV) according to formula from:
++	 * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
++	 */
++	constexpr unsigned int yHistValsPerBin =
++		SwIspStats::kYHistogramSize / kExposureBinsCount;
++	constexpr unsigned int yHistValsPerBinMod =
++		SwIspStats::kYHistogramSize /
++		(SwIspStats::kYHistogramSize % kExposureBinsCount + 1);
++	int ExposureBins[kExposureBinsCount] = {};
++	unsigned int denom = 0;
++	unsigned int num = 0;
++
++	for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) {
++		unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;
++		ExposureBins[idx] += stats_->yHistogram[i];
++	}
++
++	for (unsigned int i = 0; i < kExposureBinsCount; i++) {
++		LOG(IPASoft, Debug) << i << ": " << ExposureBins[i];
++		denom += ExposureBins[i];
++		num += ExposureBins[i] * (i + 1);
++	}
++
++	float exposureMSV = (float)num / denom;
++
++	/* sanity check */
++	if (!sensorControls.contains(V4L2_CID_EXPOSURE) ||
++	    !sensorControls.contains(V4L2_CID_ANALOGUE_GAIN)) {
++		LOG(IPASoft, Error) << "Control(s) missing";
++		return;
++	}
++
++	ControlList ctrls(sensorControls);
++
++	exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>();
++	again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
++
++	updateExposure(exposureMSV);
++
++	ctrls.set(V4L2_CID_EXPOSURE, exposure_);
++	ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
++
++	ignore_updates_ = 2;
++
++	setSensorControls.emit(ctrls);
++
++	LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV
++			    << " exp " << exposure_ << " again " << again_
++			    << " gain R/B " << params_->gainR << "/" << params_->gainB;
++}
++
++void IPASoftSimple::updateExposure(double exposureMSV)
++{
++	/* DENOMINATOR of 10 gives ~10% increment/decrement; DENOMINATOR of 5 - about ~20% */
++	static constexpr uint8_t kExpDenominator = 10;
++	static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
++	static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
++
++	int next;
++
++	if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
++		next = exposure_ * kExpNumeratorUp / kExpDenominator;
++		if (next - exposure_ < 1)
++			exposure_ += 1;
++		else
++			exposure_ = next;
++		if (exposure_ >= exposure_max_) {
++			next = again_ * kExpNumeratorUp / kExpDenominator;
++			if (next - again_ < 1)
++				again_ += 1;
++			else
++				again_ = next;
++		}
++	}
++
++	if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
++		if (exposure_ == exposure_max_ && again_ != again_min_) {
++			next = again_ * kExpNumeratorDown / kExpDenominator;
++			if (again_ - next < 1)
++				again_ -= 1;
++			else
++				again_ = next;
++		} else {
++			next = exposure_ * kExpNumeratorDown / kExpDenominator;
++			if (exposure_ - next < 1)
++				exposure_ -= 1;
++			else
++				exposure_ = next;
++		}
++	}
++
++	exposure_ = std::clamp(exposure_, exposure_min_, exposure_max_);
++	again_ = std::clamp(again_, again_min_, again_max_);
++}
++
++} /* namespace ipa::soft */
++
++/*
++ * External IPA module interface
++ */
++extern "C" {
++const struct IPAModuleInfo ipaModuleInfo = {
++	IPA_MODULE_API_VERSION,
++	0,
++	"SimplePipelineHandler",
++	"simple",
++};
++
++IPAInterface *ipaCreate()
++{
++	return new ipa::soft::IPASoftSimple();
++}
++
++} /* extern "C" */
++
++} /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch b/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch
new file mode 100644
index 0000000000..9f2d66c2f8
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch
@@ -0,0 +1,507 @@
+From ad41ea12fe4b8ca0ace20781c775a63ed0d66f4c Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:14 +0100
+Subject: [PATCH 10/21] libcamera: introduce SoftwareIsp
+
+Doxygen documentation by Dennis Bonke.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ .../internal/software_isp/meson.build         |   1 +
+ .../internal/software_isp/software_isp.h      |  98 +++++
+ src/libcamera/software_isp/meson.build        |   1 +
+ src/libcamera/software_isp/software_isp.cpp   | 349 ++++++++++++++++++
+ 4 files changed, 449 insertions(+)
+ create mode 100644 include/libcamera/internal/software_isp/software_isp.h
+ create mode 100644 src/libcamera/software_isp/software_isp.cpp
+
+diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
+index a620e16d..508ddddc 100644
+--- a/include/libcamera/internal/software_isp/meson.build
++++ b/include/libcamera/internal/software_isp/meson.build
+@@ -2,5 +2,6 @@
+ 
+ libcamera_internal_headers += files([
+     'debayer_params.h',
++    'software_isp.h',
+     'swisp_stats.h',
+ ])
+diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
+new file mode 100644
+index 00000000..8d25e979
+--- /dev/null
++++ b/include/libcamera/internal/software_isp/software_isp.h
+@@ -0,0 +1,98 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ *
++ * software_isp.h - Simple software ISP implementation
++ */
++
++#pragma once
++
++#include <functional>
++#include <initializer_list>
++#include <map>
++#include <memory>
++#include <string>
++#include <tuple>
++#include <vector>
++
++#include <libcamera/base/class.h>
++#include <libcamera/base/log.h>
++#include <libcamera/base/signal.h>
++#include <libcamera/base/thread.h>
++
++#include <libcamera/geometry.h>
++#include <libcamera/pixel_format.h>
++
++#include <libcamera/ipa/soft_ipa_interface.h>
++#include <libcamera/ipa/soft_ipa_proxy.h>
++
++#include "libcamera/internal/dma_heaps.h"
++#include "libcamera/internal/pipeline_handler.h"
++#include "libcamera/internal/shared_mem_object.h"
++#include "libcamera/internal/software_isp/debayer_params.h"
++
++namespace libcamera {
++
++class DebayerCpu;
++class FrameBuffer;
++class PixelFormat;
++struct StreamConfiguration;
++
++LOG_DECLARE_CATEGORY(SoftwareIsp)
++
++class SoftwareIsp
++{
++public:
++	SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
++	~SoftwareIsp();
++
++	int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
++
++	bool isValid() const;
++
++	std::vector<PixelFormat> formats(PixelFormat input);
++
++	SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
++
++	std::tuple<unsigned int, unsigned int>
++	strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
++
++	int configure(const StreamConfiguration &inputCfg,
++		      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
++		      const ControlInfoMap &sensorControls);
++
++	int exportBuffers(unsigned int output, unsigned int count,
++			  std::vector<std::unique_ptr<FrameBuffer>> *buffers);
++
++	void processStats(const ControlList &sensorControls);
++
++	int start();
++	void stop();
++
++	int queueBuffers(FrameBuffer *input,
++			 const std::map<unsigned int, FrameBuffer *> &outputs);
++
++	void process(FrameBuffer *input, FrameBuffer *output);
++
++	Signal<FrameBuffer *> inputBufferReady;
++	Signal<FrameBuffer *> outputBufferReady;
++	Signal<int> ispStatsReady;
++	Signal<const ControlList &> setSensorControls;
++
++private:
++	void saveIspParams(int dummy);
++	void setSensorCtrls(const ControlList &sensorControls);
++	void statsReady(int dummy);
++	void inputReady(FrameBuffer *input);
++	void outputReady(FrameBuffer *output);
++
++	std::unique_ptr<DebayerCpu> debayer_;
++	Thread ispWorkerThread_;
++	SharedMemObject<DebayerParams> sharedParams_;
++	DebayerParams debayerParams_;
++	DmaHeap dmaHeap_;
++
++	std::unique_ptr<ipa::soft::IPAProxySoft> ipa_;
++};
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
+index 71b46539..e9266e54 100644
+--- a/src/libcamera/software_isp/meson.build
++++ b/src/libcamera/software_isp/meson.build
+@@ -10,5 +10,6 @@ endif
+ libcamera_sources += files([
+     'debayer.cpp',
+     'debayer_cpu.cpp',
++    'software_isp.cpp',
+     'swstats_cpu.cpp',
+ ])
+diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
+new file mode 100644
+index 00000000..388b4496
+--- /dev/null
++++ b/src/libcamera/software_isp/software_isp.cpp
+@@ -0,0 +1,349 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ *
++ * software_isp.cpp - Simple software ISP implementation
++ */
++
++#include "libcamera/internal/software_isp/software_isp.h"
++
++#include <sys/mman.h>
++#include <sys/types.h>
++#include <unistd.h>
++
++#include <libcamera/formats.h>
++#include <libcamera/stream.h>
++
++#include "libcamera/internal/bayer_format.h"
++#include "libcamera/internal/framebuffer.h"
++#include "libcamera/internal/ipa_manager.h"
++#include "libcamera/internal/mapped_framebuffer.h"
++
++#include "debayer_cpu.h"
++
++/**
++ * \file software_isp.cpp
++ * \brief Simple software ISP implementation
++ */
++
++namespace libcamera {
++
++LOG_DEFINE_CATEGORY(SoftwareIsp)
++
++/**
++ * \class SoftwareIsp
++ * \brief Class for the Software ISP
++ */
++
++/**
++ * \var SoftwareIsp::inputBufferReady
++ * \brief A signal emitted when the input frame buffer completes
++ */
++
++/**
++ * \var SoftwareIsp::outputBufferReady
++ * \brief A signal emitted when the output frame buffer completes
++ */
++
++/**
++ * \var SoftwareIsp::ispStatsReady
++ * \brief A signal emitted when the statistics for IPA are ready
++ *
++ * The int parameter isn't actually used.
++ */
++
++/**
++ * \var SoftwareIsp::setSensorControls
++ * \brief A signal emitted when the values to write to the sensor controls are ready
++ */
++
++/**
++ * \brief Constructs SoftwareIsp object
++ * \param[in] pipe The pipeline handler in use
++ * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor
++ */
++SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
++	: debayer_(nullptr),
++	  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f },
++	  dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
++{
++	if (!dmaHeap_.isValid()) {
++		LOG(SoftwareIsp, Error) << "Failed to create DmaHeap object";
++		return;
++	}
++
++	sharedParams_ = SharedMemObject<DebayerParams>("softIsp_params");
++	if (!sharedParams_) {
++		LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters";
++		return;
++	}
++
++	auto stats = std::make_unique<SwStatsCpu>();
++	if (!stats->isValid()) {
++		LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
++		return;
++	}
++	stats->statsReady.connect(this, &SoftwareIsp::statsReady);
++
++	debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
++	debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady);
++	debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady);
++
++	ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
++	if (!ipa_) {
++		LOG(SoftwareIsp, Error)
++			<< "Creating IPA for software ISP failed";
++		debayer_.reset();
++		return;
++	}
++
++	int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
++			     debayer_->getStatsFD(),
++			     sharedParams_.fd(),
++			     sensorControls);
++	if (ret) {
++		LOG(SoftwareIsp, Error) << "IPA init failed";
++		debayer_.reset();
++		return;
++	}
++
++	ipa_->setIspParams.connect(this, &SoftwareIsp::saveIspParams);
++	ipa_->setSensorControls.connect(this, &SoftwareIsp::setSensorCtrls);
++
++	debayer_->moveToThread(&ispWorkerThread_);
++}
++
++SoftwareIsp::~SoftwareIsp()
++{
++	/* make sure to destroy the DebayerCpu before the ispWorkerThread_ is gone */
++	debayer_.reset();
++}
++
++/**
++ * \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename)
++ * \brief Load a configuration from a file
++ * \param[in] filename The file to load the configuration data from
++ *
++ * Currently is a stub doing nothing and always returning "success".
++ *
++ * \return 0 on success
++ */
++
++/**
++ * \brief Process the statistics gathered
++ * \param[in] sensorControls The sensor controls
++ *
++ * Requests the IPA to calculate new parameters for ISP and new control
++ * values for the sensor.
++ */
++void SoftwareIsp::processStats(const ControlList &sensorControls)
++{
++	ASSERT(ipa_);
++	ipa_->processStats(sensorControls);
++}
++
++/**
++ * \brief Check the validity of Software Isp object
++ * \return True if Software Isp is valid, false otherwise
++ */
++bool SoftwareIsp::isValid() const
++{
++	return !!debayer_;
++}
++
++/**
++  * \brief Get the output formats supported for the given input format
++  * \param[in] inputFormat The input format
++  * \return All the supported output formats or an empty vector if there are none
++  */
++std::vector<PixelFormat> SoftwareIsp::formats(PixelFormat inputFormat)
++{
++	ASSERT(debayer_ != nullptr);
++
++	return debayer_->formats(inputFormat);
++}
++
++/**
++ * \brief Get the supported output sizes for the given input format and size
++ * \param[in] inputFormat The input format
++ * \param[in] inputSize The input frame size
++ * \return The valid size range or an empty range if there are none
++ */
++SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize)
++{
++	ASSERT(debayer_ != nullptr);
++
++	return debayer_->sizes(inputFormat, inputSize);
++}
++
++/**
++ * Get the output stride and the frame size in bytes for the given output format and size
++ * \param[in] outputFormat The output format
++ * \param[in] size The output size (width and height in pixels)
++ * \return A tuple of the stride and the frame size in bytes, or a tuple of 0,0
++ * if there is no valid output config
++ */
++std::tuple<unsigned int, unsigned int>
++SoftwareIsp::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
++{
++	ASSERT(debayer_ != nullptr);
++
++	return debayer_->strideAndFrameSize(outputFormat, size);
++}
++
++/**
++ * \brief Configure the SoftwareIsp object according to the passed in parameters
++ * \param[in] inputCfg The input configuration
++ * \param[in] outputCfgs The output configurations
++ * \param[in] sensorControls ControlInfoMap of the controls supported by the sensor
++ * \return 0 on success, a negative errno on failure
++ */
++int SoftwareIsp::configure(const StreamConfiguration &inputCfg,
++			   const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
++			   const ControlInfoMap &sensorControls)
++{
++	ASSERT(ipa_ != nullptr && debayer_ != nullptr);
++
++	int ret = ipa_->configure(sensorControls);
++	if (ret < 0)
++		return ret;
++
++	return debayer_->configure(inputCfg, outputCfgs);
++}
++
++/**
++ * \brief Export the buffers from the Software ISP
++ * \param[in] output Output stream index exporting the buffers
++ * \param[in] count Number of buffers to allocate
++ * \param[out] buffers Vector to store the allocated buffers
++ * \return The number of allocated buffers on success or a negative error code
++ * otherwise
++ */
++int SoftwareIsp::exportBuffers(unsigned int output, unsigned int count,
++			       std::vector<std::unique_ptr<FrameBuffer>> *buffers)
++{
++	ASSERT(debayer_ != nullptr);
++
++	/* single output for now */
++	if (output >= 1)
++		return -EINVAL;
++
++	for (unsigned int i = 0; i < count; i++) {
++		const std::string name = "frame-" + std::to_string(i);
++		const size_t frameSize = debayer_->frameSize();
++
++		FrameBuffer::Plane outPlane;
++		outPlane.fd = SharedFD(dmaHeap_.alloc(name.c_str(), frameSize));
++		if (!outPlane.fd.isValid()) {
++			LOG(SoftwareIsp, Error)
++				<< "failed to allocate a dma_buf";
++			return -ENOMEM;
++		}
++		outPlane.offset = 0;
++		outPlane.length = frameSize;
++
++		std::vector<FrameBuffer::Plane> planes{ outPlane };
++		buffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes)));
++	}
++
++	return count;
++}
++
++/**
++ * \brief Queue buffers to Software ISP
++ * \param[in] input The input framebuffer
++ * \param[in] outputs The container holding the output stream indexes and
++ * their respective frame buffer outputs
++ * \return 0 on success, a negative errno on failure
++ */
++int SoftwareIsp::queueBuffers(FrameBuffer *input,
++			      const std::map<unsigned int, FrameBuffer *> &outputs)
++{
++	unsigned int mask = 0;
++
++	/*
++	 * Validate the outputs as a sanity check: at least one output is
++	 * required, all outputs must reference a valid stream and no two
++	 * outputs can reference the same stream.
++	 */
++	if (outputs.empty())
++		return -EINVAL;
++
++	for (auto [index, buffer] : outputs) {
++		if (!buffer)
++			return -EINVAL;
++		if (index >= 1) /* only single stream atm */
++			return -EINVAL;
++		if (mask & (1 << index))
++			return -EINVAL;
++
++		mask |= 1 << index;
++	}
++
++	process(input, outputs.at(0));
++
++	return 0;
++}
++
++/**
++ * \brief Starts the Software ISP streaming operation
++ * \return 0 on success, any other value indicates an error
++ */
++int SoftwareIsp::start()
++{
++	int ret = ipa_->start();
++	if (ret)
++		return ret;
++
++	ispWorkerThread_.start();
++	return 0;
++}
++
++/**
++ * \brief Stops the Software ISP streaming operation
++ */
++void SoftwareIsp::stop()
++{
++	ispWorkerThread_.exit();
++	ispWorkerThread_.wait();
++
++	ipa_->stop();
++}
++
++/**
++ * \brief Passes the input framebuffer to the ISP worker to process
++ * \param[in] input The input framebuffer
++ * \param[out] output The framebuffer to write the processed frame to
++ */
++void SoftwareIsp::process(FrameBuffer *input, FrameBuffer *output)
++{
++	debayer_->invokeMethod(&DebayerCpu::process,
++			       ConnectionTypeQueued, input, output, debayerParams_);
++}
++
++void SoftwareIsp::saveIspParams([[maybe_unused]] int dummy)
++{
++	debayerParams_ = *sharedParams_;
++}
++
++void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
++{
++	setSensorControls.emit(sensorControls);
++}
++
++void SoftwareIsp::statsReady(int dummy)
++{
++	ispStatsReady.emit(dummy);
++}
++
++void SoftwareIsp::inputReady(FrameBuffer *input)
++{
++	inputBufferReady.emit(input);
++}
++
++void SoftwareIsp::outputReady(FrameBuffer *output)
++{
++	outputBufferReady.emit(output);
++}
++
++} /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch b/users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
new file mode 100644
index 0000000000..5c2237a8eb
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
@@ -0,0 +1,240 @@
+From 050440eed6ab90686df217f5ff7dea0b241e3898 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:15 +0100
+Subject: [PATCH 11/21] libcamera: pipeline: simple: rename converterBuffers_
+ and related vars
+
+The converterBuffers_ and the converterQueue_ are not that specific
+to the Converter, and could be used by another entity doing the format
+conversion.
+
+Rename converterBuffers_, converterQueue_, and useConverter_ to
+conversionBuffers_, conversionQueue_ and useConversion_ to
+disassociate them from the Converter.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/pipeline/simple/simple.cpp | 63 ++++++++++++------------
+ 1 file changed, 32 insertions(+), 31 deletions(-)
+
+diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
+index a84f6760..78854ef8 100644
+--- a/src/libcamera/pipeline/simple/simple.cpp
++++ b/src/libcamera/pipeline/simple/simple.cpp
+@@ -269,17 +269,18 @@ public:
+ 	std::vector<Configuration> configs_;
+ 	std::map<PixelFormat, std::vector<const Configuration *>> formats_;
+ 
++	std::vector<std::unique_ptr<FrameBuffer>> conversionBuffers_;
++	std::queue<std::map<unsigned int, FrameBuffer *>> conversionQueue_;
++	bool useConversion_;
++
+ 	std::unique_ptr<Converter> converter_;
+-	std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;
+-	bool useConverter_;
+-	std::queue<std::map<unsigned int, FrameBuffer *>> converterQueue_;
+ 
+ private:
+ 	void tryPipeline(unsigned int code, const Size &size);
+ 	static std::vector<const MediaPad *> routedSourcePads(MediaPad *sink);
+ 
+-	void converterInputDone(FrameBuffer *buffer);
+-	void converterOutputDone(FrameBuffer *buffer);
++	void conversionInputDone(FrameBuffer *buffer);
++	void conversionOutputDone(FrameBuffer *buffer);
+ };
+ 
+ class SimpleCameraConfiguration : public CameraConfiguration
+@@ -503,8 +504,8 @@ int SimpleCameraData::init()
+ 				<< "Failed to create converter, disabling format conversion";
+ 			converter_.reset();
+ 		} else {
+-			converter_->inputBufferReady.connect(this, &SimpleCameraData::converterInputDone);
+-			converter_->outputBufferReady.connect(this, &SimpleCameraData::converterOutputDone);
++			converter_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone);
++			converter_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
+ 		}
+ 	}
+ 
+@@ -740,7 +741,7 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 	 * point converting an erroneous buffer.
+ 	 */
+ 	if (buffer->metadata().status != FrameMetadata::FrameSuccess) {
+-		if (!useConverter_) {
++		if (!useConversion_) {
+ 			/* No conversion, just complete the request. */
+ 			Request *request = buffer->request();
+ 			pipe->completeBuffer(request, buffer);
+@@ -756,16 +757,16 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 		if (buffer->metadata().status != FrameMetadata::FrameCancelled)
+ 			video_->queueBuffer(buffer);
+ 
+-		if (converterQueue_.empty())
++		if (conversionQueue_.empty())
+ 			return;
+ 
+ 		Request *request = nullptr;
+-		for (auto &item : converterQueue_.front()) {
++		for (auto &item : conversionQueue_.front()) {
+ 			FrameBuffer *outputBuffer = item.second;
+ 			request = outputBuffer->request();
+ 			pipe->completeBuffer(request, outputBuffer);
+ 		}
+-		converterQueue_.pop();
++		conversionQueue_.pop();
+ 
+ 		if (request)
+ 			pipe->completeRequest(request);
+@@ -782,9 +783,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 	 */
+ 	Request *request = buffer->request();
+ 
+-	if (useConverter_ && !converterQueue_.empty()) {
++	if (useConversion_ && !conversionQueue_.empty()) {
+ 		const std::map<unsigned int, FrameBuffer *> &outputs =
+-			converterQueue_.front();
++			conversionQueue_.front();
+ 		if (!outputs.empty()) {
+ 			FrameBuffer *outputBuffer = outputs.begin()->second;
+ 			if (outputBuffer)
+@@ -801,14 +802,14 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 	 * conversion is needed. If there's no queued request, just requeue the
+ 	 * captured buffer for capture.
+ 	 */
+-	if (useConverter_) {
+-		if (converterQueue_.empty()) {
++	if (useConversion_) {
++		if (conversionQueue_.empty()) {
+ 			video_->queueBuffer(buffer);
+ 			return;
+ 		}
+ 
+-		converter_->queueBuffers(buffer, converterQueue_.front());
+-		converterQueue_.pop();
++		converter_->queueBuffers(buffer, conversionQueue_.front());
++		conversionQueue_.pop();
+ 		return;
+ 	}
+ 
+@@ -817,13 +818,13 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 	pipe->completeRequest(request);
+ }
+ 
+-void SimpleCameraData::converterInputDone(FrameBuffer *buffer)
++void SimpleCameraData::conversionInputDone(FrameBuffer *buffer)
+ {
+ 	/* Queue the input buffer back for capture. */
+ 	video_->queueBuffer(buffer);
+ }
+ 
+-void SimpleCameraData::converterOutputDone(FrameBuffer *buffer)
++void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
+ {
+ 	SimplePipelineHandler *pipe = SimpleCameraData::pipe();
+ 
+@@ -1189,14 +1190,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
+ 
+ 	/* Configure the converter if needed. */
+ 	std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
+-	data->useConverter_ = config->needConversion();
++	data->useConversion_ = config->needConversion();
+ 
+ 	for (unsigned int i = 0; i < config->size(); ++i) {
+ 		StreamConfiguration &cfg = config->at(i);
+ 
+ 		cfg.setStream(&data->streams_[i]);
+ 
+-		if (data->useConverter_)
++		if (data->useConversion_)
+ 			outputCfgs.push_back(cfg);
+ 	}
+ 
+@@ -1222,7 +1223,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
+ 	 * Export buffers on the converter or capture video node, depending on
+ 	 * whether the converter is used or not.
+ 	 */
+-	if (data->useConverter_)
++	if (data->useConversion_)
+ 		return data->converter_->exportBuffers(data->streamIndex(stream),
+ 						       count, buffers);
+ 	else
+@@ -1243,13 +1244,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
+ 		return -EBUSY;
+ 	}
+ 
+-	if (data->useConverter_) {
++	if (data->useConversion_) {
+ 		/*
+ 		 * When using the converter allocate a fixed number of internal
+ 		 * buffers.
+ 		 */
+ 		ret = video->allocateBuffers(kNumInternalBuffers,
+-					     &data->converterBuffers_);
++					     &data->conversionBuffers_);
+ 	} else {
+ 		/* Otherwise, prepare for using buffers from the only stream. */
+ 		Stream *stream = &data->streams_[0];
+@@ -1268,7 +1269,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
+ 		return ret;
+ 	}
+ 
+-	if (data->useConverter_) {
++	if (data->useConversion_) {
+ 		ret = data->converter_->start();
+ 		if (ret < 0) {
+ 			stop(camera);
+@@ -1276,7 +1277,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
+ 		}
+ 
+ 		/* Queue all internal buffers for capture. */
+-		for (std::unique_ptr<FrameBuffer> &buffer : data->converterBuffers_)
++		for (std::unique_ptr<FrameBuffer> &buffer : data->conversionBuffers_)
+ 			video->queueBuffer(buffer.get());
+ 	}
+ 
+@@ -1288,7 +1289,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
+ 	SimpleCameraData *data = cameraData(camera);
+ 	V4L2VideoDevice *video = data->video_;
+ 
+-	if (data->useConverter_)
++	if (data->useConversion_)
+ 		data->converter_->stop();
+ 
+ 	video->streamOff();
+@@ -1296,7 +1297,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
+ 
+ 	video->bufferReady.disconnect(data, &SimpleCameraData::bufferReady);
+ 
+-	data->converterBuffers_.clear();
++	data->conversionBuffers_.clear();
+ 
+ 	releasePipeline(data);
+ }
+@@ -1314,7 +1315,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
+ 		 * queue, it will be handed to the converter in the capture
+ 		 * completion handler.
+ 		 */
+-		if (data->useConverter_) {
++		if (data->useConversion_) {
+ 			buffers.emplace(data->streamIndex(stream), buffer);
+ 		} else {
+ 			ret = data->video_->queueBuffer(buffer);
+@@ -1323,8 +1324,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
+ 		}
+ 	}
+ 
+-	if (data->useConverter_)
+-		data->converterQueue_.push(std::move(buffers));
++	if (data->useConversion_)
++		data->conversionQueue_.push(std::move(buffers));
+ 
+ 	return 0;
+ }
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch b/users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
new file mode 100644
index 0000000000..378a43604f
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
@@ -0,0 +1,302 @@
+From d64b0fca22ef25b8a14d7fc97dfab64eb1c4f21a Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:16 +0100
+Subject: [PATCH 12/21] libcamera: pipeline: simple: enable use of Soft ISP and
+ Soft IPA
+
+To enable the Simple Soft ISP and Soft IPA for simple pipeline handler
+configure the build with:
+  -Dpipelines=simple -Dipas=simple
+
+Also using the Soft ISP for the particular hardware platform must
+be enabled in the supportedDevices[] table.
+
+If the pipeline uses Converter, Soft ISP and Soft IPA aren't
+available.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/pipeline/simple/simple.cpp | 137 ++++++++++++++++++-----
+ 1 file changed, 109 insertions(+), 28 deletions(-)
+
+diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
+index 78854ef8..c3ebb7b7 100644
+--- a/src/libcamera/pipeline/simple/simple.cpp
++++ b/src/libcamera/pipeline/simple/simple.cpp
+@@ -34,6 +34,7 @@
+ #include "libcamera/internal/device_enumerator.h"
+ #include "libcamera/internal/media_device.h"
+ #include "libcamera/internal/pipeline_handler.h"
++#include "libcamera/internal/software_isp/software_isp.h"
+ #include "libcamera/internal/v4l2_subdevice.h"
+ #include "libcamera/internal/v4l2_videodevice.h"
+ 
+@@ -185,17 +186,22 @@ struct SimplePipelineInfo {
+ 	 * and the number of streams it supports.
+ 	 */
+ 	std::vector<std::pair<const char *, unsigned int>> converters;
++	/*
++	 * Using Software ISP is to be enabled per driver.
++	 * The Software ISP can't be used together with the converters.
++	 */
++	bool swIspEnabled;
+ };
+ 
+ namespace {
+ 
+ static const SimplePipelineInfo supportedDevices[] = {
+-	{ "dcmipp", {} },
+-	{ "imx7-csi", { { "pxp", 1 } } },
+-	{ "j721e-csi2rx", {} },
+-	{ "mxc-isi", {} },
+-	{ "qcom-camss", {} },
+-	{ "sun6i-csi", {} },
++	{ "dcmipp", {}, false },
++	{ "imx7-csi", { { "pxp", 1 } }, false },
++	{ "j721e-csi2rx", {}, false },
++	{ "mxc-isi", {}, false },
++	{ "qcom-camss", {}, true },
++	{ "sun6i-csi", {}, false },
+ };
+ 
+ } /* namespace */
+@@ -274,6 +280,7 @@ public:
+ 	bool useConversion_;
+ 
+ 	std::unique_ptr<Converter> converter_;
++	std::unique_ptr<SoftwareIsp> swIsp_;
+ 
+ private:
+ 	void tryPipeline(unsigned int code, const Size &size);
+@@ -281,6 +288,9 @@ private:
+ 
+ 	void conversionInputDone(FrameBuffer *buffer);
+ 	void conversionOutputDone(FrameBuffer *buffer);
++
++	void ispStatsReady(int dummy);
++	void setSensorControls(const ControlList &sensorControls);
+ };
+ 
+ class SimpleCameraConfiguration : public CameraConfiguration
+@@ -332,6 +342,7 @@ public:
+ 	V4L2VideoDevice *video(const MediaEntity *entity);
+ 	V4L2Subdevice *subdev(const MediaEntity *entity);
+ 	MediaDevice *converter() { return converter_; }
++	bool swIspEnabled() { return swIspEnabled_; }
+ 
+ protected:
+ 	int queueRequestDevice(Camera *camera, Request *request) override;
+@@ -360,6 +371,7 @@ private:
+ 	std::map<const MediaEntity *, EntityData> entities_;
+ 
+ 	MediaDevice *converter_;
++	bool swIspEnabled_;
+ };
+ 
+ /* -----------------------------------------------------------------------------
+@@ -509,6 +521,29 @@ int SimpleCameraData::init()
+ 		}
+ 	}
+ 
++	/*
++	 * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
++	 */
++	if (!converter_ && pipe->swIspEnabled()) {
++		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());
++		if (!swIsp_->isValid()) {
++			LOG(SimplePipeline, Warning)
++				<< "Failed to create software ISP, disabling software debayering";
++			swIsp_.reset();
++		} else {
++			/*
++			 * \todo explain why SimpleCameraData::conversionInputDone() can't be directly
++			 * connected to inputBufferReady signal.
++			 */
++			swIsp_->inputBufferReady.connect(pipe, [this](FrameBuffer *buffer) {
++				this->conversionInputDone(buffer);
++			});
++			swIsp_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
++			swIsp_->ispStatsReady.connect(this, &SimpleCameraData::ispStatsReady);
++			swIsp_->setSensorControls.connect(this, &SimpleCameraData::setSensorControls);
++		}
++	}
++
+ 	video_ = pipe->video(entities_.back().entity);
+ 	ASSERT(video_);
+ 
+@@ -599,12 +634,21 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)
+ 		config.captureFormat = pixelFormat;
+ 		config.captureSize = format.size;
+ 
+-		if (!converter_) {
++
++		if (converter_) {
++ 			config.outputFormats = converter_->formats(pixelFormat);
++ 			config.outputSizes = converter_->sizes(format.size);
++		} else if (swIsp_) {
++			config.outputFormats = swIsp_->formats(pixelFormat);
++			config.outputSizes = swIsp_->sizes(pixelFormat, format.size);
++			if (config.outputFormats.empty()) {
++				/* Do not use swIsp for unsupported pixelFormat's: */
++				config.outputFormats = { pixelFormat };
++				config.outputSizes = config.captureSize;
++			}
++		} else {
+ 			config.outputFormats = { pixelFormat };
+ 			config.outputSizes = config.captureSize;
+-		} else {
+-			config.outputFormats = converter_->formats(pixelFormat);
+-			config.outputSizes = converter_->sizes(format.size);
+ 		}
+ 
+ 		configs_.push_back(config);
+@@ -750,9 +794,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 		}
+ 
+ 		/*
+-		 * The converter is in use. Requeue the internal buffer for
+-		 * capture (unless the stream is being stopped), and complete
+-		 * the request with all the user-facing buffers.
++		 * The converter or Software ISP is in use. Requeue the internal
++		 * buffer for capture (unless the stream is being stopped), and
++		 * complete the request with all the user-facing buffers.
+ 		 */
+ 		if (buffer->metadata().status != FrameMetadata::FrameCancelled)
+ 			video_->queueBuffer(buffer);
+@@ -798,9 +842,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 					buffer->metadata().timestamp);
+ 
+ 	/*
+-	 * Queue the captured and the request buffer to the converter if format
+-	 * conversion is needed. If there's no queued request, just requeue the
+-	 * captured buffer for capture.
++	 * Queue the captured and the request buffer to the converter or Software
++	 * ISP if format conversion is needed. If there's no queued request, just
++	 * requeue the captured buffer for capture.
+ 	 */
+ 	if (useConversion_) {
+ 		if (conversionQueue_.empty()) {
+@@ -808,7 +852,11 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 			return;
+ 		}
+ 
+-		converter_->queueBuffers(buffer, conversionQueue_.front());
++		if (converter_)
++			converter_->queueBuffers(buffer, conversionQueue_.front());
++		else
++			swIsp_->queueBuffers(buffer, conversionQueue_.front());
++
+ 		conversionQueue_.pop();
+ 		return;
+ 	}
+@@ -834,6 +882,18 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
+ 		pipe->completeRequest(request);
+ }
+ 
++void SimpleCameraData::ispStatsReady([[maybe_unused]] int dummy)
++{
++	swIsp_->processStats(sensor_->getControls({ V4L2_CID_ANALOGUE_GAIN,
++						    V4L2_CID_EXPOSURE }));
++}
++
++void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
++{
++	ControlList ctrls(sensorControls);
++	sensor_->setControls(&ctrls);
++}
++
+ /* Retrieve all source pads connected to a sink pad through active routes. */
+ std::vector<const MediaPad *> SimpleCameraData::routedSourcePads(MediaPad *sink)
+ {
+@@ -1046,8 +1106,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
+ 		/* Set the stride, frameSize and bufferCount. */
+ 		if (needConversion_) {
+ 			std::tie(cfg.stride, cfg.frameSize) =
+-				data_->converter_->strideAndFrameSize(cfg.pixelFormat,
+-								      cfg.size);
++				(data_->converter_) ? data_->converter_->strideAndFrameSize(cfg.pixelFormat,
++											    cfg.size)
++						    : data_->swIsp_->strideAndFrameSize(cfg.pixelFormat,
++											cfg.size);
+ 			if (cfg.stride == 0)
+ 				return Invalid;
+ 		} else {
+@@ -1210,7 +1272,9 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
+ 	inputCfg.stride = captureFormat.planes[0].bpl;
+ 	inputCfg.bufferCount = kNumInternalBuffers;
+ 
+-	return data->converter_->configure(inputCfg, outputCfgs);
++	return (data->converter_) ? data->converter_->configure(inputCfg, outputCfgs)
++				  : data->swIsp_->configure(inputCfg, outputCfgs,
++							    data->sensor_->controls());
+ }
+ 
+ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
+@@ -1224,8 +1288,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
+ 	 * whether the converter is used or not.
+ 	 */
+ 	if (data->useConversion_)
+-		return data->converter_->exportBuffers(data->streamIndex(stream),
+-						       count, buffers);
++		return (data->converter_) ? data->converter_->exportBuffers(data->streamIndex(stream),
++									    count, buffers)
++					  : data->swIsp_->exportBuffers(data->streamIndex(stream),
++									count, buffers);
+ 	else
+ 		return data->video_->exportBuffers(count, buffers);
+ }
+@@ -1270,10 +1336,18 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
+ 	}
+ 
+ 	if (data->useConversion_) {
+-		ret = data->converter_->start();
+-		if (ret < 0) {
+-			stop(camera);
+-			return ret;
++		if (data->converter_) {
++			ret = data->converter_->start();
++			if (ret < 0) {
++				stop(camera);
++				return ret;
++			}
++		} else if (data->swIsp_) {
++			ret = data->swIsp_->start();
++			if (ret < 0) {
++				stop(camera);
++				return ret;
++			}
+ 		}
+ 
+ 		/* Queue all internal buffers for capture. */
+@@ -1289,8 +1363,13 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
+ 	SimpleCameraData *data = cameraData(camera);
+ 	V4L2VideoDevice *video = data->video_;
+ 
+-	if (data->useConversion_)
+-		data->converter_->stop();
++	if (data->useConversion_) {
++		if (data->converter_)
++			data->converter_->stop();
++		else if (data->swIsp_) {
++			data->swIsp_->stop();
++		}
++	}
+ 
+ 	video->streamOff();
+ 	video->releaseBuffers();
+@@ -1452,6 +1531,8 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
+ 		}
+ 	}
+ 
++  swIspEnabled_ = info->swIspEnabled;
++
+ 	/* Locate the sensors. */
+ 	std::vector<MediaEntity *> sensors = locateSensors();
+ 	if (sensors.empty()) {
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch b/users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
new file mode 100644
index 0000000000..1a57d690ff
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
@@ -0,0 +1,203 @@
+From aabc53453d542495d9da25411f57308c01f2bc28 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:17 +0100
+Subject: [PATCH 13/21] libcamera: swstats_cpu: Add support for 8, 10 and 12
+ bpp unpacked bayer input
+
+Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard
+bayer orders.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/software_isp/swstats_cpu.cpp | 128 +++++++++++++++++++++
+ src/libcamera/software_isp/swstats_cpu.h   |   9 ++
+ 2 files changed, 137 insertions(+)
+
+diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
+index 448d0e4c..be310f56 100644
+--- a/src/libcamera/software_isp/swstats_cpu.cpp
++++ b/src/libcamera/software_isp/swstats_cpu.cpp
+@@ -71,6 +71,83 @@ static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */
+ 	stats_.sumG_ += sumG;       \
+ 	stats_.sumB_ += sumB;
+ 
++void SwStatsCpu::statsBGGR8Line0(const uint8_t *src[])
++{
++	const uint8_t *src0 = src[1] + window_.x;
++	const uint8_t *src1 = src[2] + window_.x;
++
++	SWSTATS_START_LINE_STATS(uint8_t)
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	/* x += 4 sample every other 2x2 block */
++	for (int x = 0; x < (int)window_.width; x += 4) {
++		b = src0[x];
++		g = src0[x + 1];
++		g2 = src1[x];
++		r = src1[x + 1];
++
++		g = (g + g2) / 2;
++
++		SWSTATS_ACCUMULATE_LINE_STATS(1)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
++void SwStatsCpu::statsBGGR10Line0(const uint8_t *src[])
++{
++	const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
++	const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
++
++	SWSTATS_START_LINE_STATS(uint16_t)
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	/* x += 4 sample every other 2x2 block */
++	for (int x = 0; x < (int)window_.width; x += 4) {
++		b = src0[x];
++		g = src0[x + 1];
++		g2 = src1[x];
++		r = src1[x + 1];
++
++		g = (g + g2) / 2;
++
++		/* divide Y by 4 for 10 -> 8 bpp value */
++		SWSTATS_ACCUMULATE_LINE_STATS(4)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
++void SwStatsCpu::statsBGGR12Line0(const uint8_t *src[])
++{
++	const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
++	const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
++
++	SWSTATS_START_LINE_STATS(uint16_t)
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	/* x += 4 sample every other 2x2 block */
++	for (int x = 0; x < (int)window_.width; x += 4) {
++		b = src0[x];
++		g = src0[x + 1];
++		g2 = src1[x];
++		r = src1[x + 1];
++
++		g = (g + g2) / 2;
++
++		/* divide Y by 16 for 12 -> 8 bpp value */
++		SWSTATS_ACCUMULATE_LINE_STATS(16)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
+ void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
+ {
+ 	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
+@@ -147,6 +224,42 @@ void SwStatsCpu::finishFrame(void)
+ 	statsReady.emit(0);
+ }
+ 
++/**
++ * \brief Setup SwStatsCpu object for standard Bayer orders
++ * \param[in] order The Bayer order
++ *
++ * Check if order is a standard Bayer order and setup xShift_ and swapLines_
++ * so that a single BGGR stats function can be used for all 4 standard orders.
++ */
++int SwStatsCpu::setupStandardBayerOrder(BayerFormat::Order order)
++{
++	switch (order) {
++	case BayerFormat::BGGR:
++		xShift_ = 0;
++		swapLines_ = false;
++		break;
++	case BayerFormat::GBRG:
++		xShift_ = 1; /* BGGR -> GBRG */
++		swapLines_ = false;
++		break;
++	case BayerFormat::GRBG:
++		xShift_ = 0;
++		swapLines_ = true; /* BGGR -> GRBG */
++		break;
++	case BayerFormat::RGGB:
++		xShift_ = 1; /* BGGR -> GBRG */
++		swapLines_ = true; /* GBRG -> RGGB */
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	patternSize_.height = 2;
++	patternSize_.width = 2;
++	ySkipMask_ = 0x02; /* Skip every 3th and 4th line */
++	return 0;
++}
++
+ /**
+  * \brief Configure the statistics object for the passed in input format.
+  * \param[in] inputCfg The input format
+@@ -158,6 +271,21 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
+ 
++	if (bayerFormat.packing == BayerFormat::Packing::None &&
++	    setupStandardBayerOrder(bayerFormat.order) == 0) {
++		switch (bayerFormat.bitDepth) {
++		case 8:
++			stats0_ = &SwStatsCpu::statsBGGR8Line0;
++			return 0;
++		case 10:
++			stats0_ = &SwStatsCpu::statsBGGR10Line0;
++			return 0;
++		case 12:
++			stats0_ = &SwStatsCpu::statsBGGR12Line0;
++			return 0;
++		}
++	}
++
+ 	if (bayerFormat.bitDepth == 10 &&
+ 	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ 		patternSize_.height = 2;
+diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
+index 0ac9ae71..bbbcf69b 100644
+--- a/src/libcamera/software_isp/swstats_cpu.h
++++ b/src/libcamera/software_isp/swstats_cpu.h
+@@ -17,6 +17,7 @@
+ 
+ #include <libcamera/geometry.h>
+ 
++#include "libcamera/internal/bayer_format.h"
+ #include "libcamera/internal/shared_mem_object.h"
+ #include "libcamera/internal/software_isp/swisp_stats.h"
+ 
+@@ -120,6 +121,14 @@ private:
+ 	 */
+ 	using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
+ 
++	int setupStandardBayerOrder(BayerFormat::Order order);
++	/* Bayer 8 bpp unpacked */
++	void statsBGGR8Line0(const uint8_t *src[]);
++	/* Bayer 10 bpp unpacked */
++	void statsBGGR10Line0(const uint8_t *src[]);
++	/* Bayer 12 bpp unpacked */
++	void statsBGGR12Line0(const uint8_t *src[]);
++	/* Bayer 10 bpp packed */
+ 	void statsBGGR10PLine0(const uint8_t *src[]);
+ 	void statsGBRG10PLine0(const uint8_t *src[]);
+ 
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch b/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
new file mode 100644
index 0000000000..c7edf49828
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
@@ -0,0 +1,234 @@
+From 5f3647bd4f12dd62256a425c49fd18a0f5990930 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:18 +0100
+Subject: [PATCH 14/21] libcamera: debayer_cpu: Add support for 8, 10 and 12
+ bpp unpacked bayer input
+
+Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard
+bayer orders.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/software_isp/debayer_cpu.cpp | 128 +++++++++++++++++++++
+ src/libcamera/software_isp/debayer_cpu.h   |  13 +++
+ 2 files changed, 141 insertions(+)
+
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index f932362c..eb1c2718 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -56,6 +56,11 @@ DebayerCpu::~DebayerCpu()
+ 		free(lineBuffers_[i]);
+ }
+ 
++#define DECLARE_SRC_POINTERS(pixel_t)                            \
++	const pixel_t *prev = (const pixel_t *)src[0] + xShift_; \
++	const pixel_t *curr = (const pixel_t *)src[1] + xShift_; \
++	const pixel_t *next = (const pixel_t *)src[2] + xShift_;
++
+ // RGR
+ // GBG
+ // RGR
+@@ -92,6 +97,70 @@ DebayerCpu::~DebayerCpu()
+ 	*dst++ = red_[curr[x] / (div)];                                                        \
+ 	x++;
+ 
++void DebayerCpu::debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint8_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		BGGR_BGR888(1, 1, 1)
++		GBRG_BGR888(1, 1, 1)
++	}
++}
++
++void DebayerCpu::debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint8_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		GRBG_BGR888(1, 1, 1)
++		RGGB_BGR888(1, 1, 1)
++	}
++}
++
++void DebayerCpu::debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint16_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		/* divide values by 4 for 10 -> 8 bpp value */
++		BGGR_BGR888(1, 1, 4)
++		GBRG_BGR888(1, 1, 4)
++	}
++}
++
++void DebayerCpu::debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint16_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		/* divide values by 4 for 10 -> 8 bpp value */
++		GRBG_BGR888(1, 1, 4)
++		RGGB_BGR888(1, 1, 4)
++	}
++}
++
++void DebayerCpu::debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint16_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		/* divide values by 16 for 12 -> 8 bpp value */
++		BGGR_BGR888(1, 1, 16)
++		GBRG_BGR888(1, 1, 16)
++	}
++}
++
++void DebayerCpu::debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint16_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		/* divide values by 16 for 12 -> 8 bpp value */
++		GRBG_BGR888(1, 1, 16)
++		RGGB_BGR888(1, 1, 16)
++	}
++}
++
+ void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
+ {
+ 	const int width_in_bytes = window_.width * 5 / 4;
+@@ -193,6 +262,16 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
++	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
++	    bayerFormat.packing == BayerFormat::Packing::None &&
++	    isStandardBayerOrder(bayerFormat.order)) {
++		config.bpp = (bayerFormat.bitDepth + 7) & ~7;
++		config.patternSize.width = 2;
++		config.patternSize.height = 2;
++		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
++		return 0;
++	}
++
+ 	if (bayerFormat.bitDepth == 10 &&
+ 	    bayerFormat.packing == BayerFormat::Packing::CSI2 &&
+ 	    isStandardBayerOrder(bayerFormat.order)) {
+@@ -220,12 +299,61 @@ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &c
+ 	return -EINVAL;
+ }
+ 
++/*
++ * Check for standard Bayer orders and set xShift_ and swap debayer0/1, so that
++ * a single pair of BGGR debayer functions can be used for all 4 standard orders.
++ */
++int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
++{
++	switch (order) {
++	case BayerFormat::BGGR:
++		break;
++	case BayerFormat::GBRG:
++		xShift_ = 1; /* BGGR -> GBRG */
++		break;
++	case BayerFormat::GRBG:
++		std::swap(debayer0_, debayer1_); /* BGGR -> GRBG */
++		break;
++	case BayerFormat::RGGB:
++		xShift_ = 1; /* BGGR -> GBRG */
++		std::swap(debayer0_, debayer1_); /* GBRG -> RGGB */
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
+ /* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */
+ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat)
+ {
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
++	xShift_ = 0;
++
++	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
++	    bayerFormat.packing == BayerFormat::Packing::None &&
++	    isStandardBayerOrder(bayerFormat.order)) {
++		switch (bayerFormat.bitDepth) {
++		case 8:
++			debayer0_ = &DebayerCpu::debayer8_BGBG_BGR888;
++			debayer1_ = &DebayerCpu::debayer8_GRGR_BGR888;
++			break;
++		case 10:
++			debayer0_ = &DebayerCpu::debayer10_BGBG_BGR888;
++			debayer1_ = &DebayerCpu::debayer10_GRGR_BGR888;
++			break;
++		case 12:
++			debayer0_ = &DebayerCpu::debayer12_BGBG_BGR888;
++			debayer1_ = &DebayerCpu::debayer12_GRGR_BGR888;
++			break;
++		}
++		setupStandardBayerOrder(bayerFormat.order);
++		return 0;
++	}
++
+ 	if (bayerFormat.bitDepth == 10 &&
+ 	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ 		switch (bayerFormat.order) {
+diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
+index 8a51ed85..fd1fa180 100644
+--- a/src/libcamera/software_isp/debayer_cpu.h
++++ b/src/libcamera/software_isp/debayer_cpu.h
+@@ -17,6 +17,8 @@
+ 
+ #include <libcamera/base/object.h>
+ 
++#include "libcamera/internal/bayer_format.h"
++
+ #include "debayer.h"
+ #include "swstats_cpu.h"
+ 
+@@ -82,6 +84,15 @@ private:
+ 	 */
+ 	using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]);
+ 
++	/* 8-bit raw bayer format */
++	void debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
++	/* unpacked 10-bit raw bayer format */
++	void debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
++	/* unpacked 12-bit raw bayer format */
++	void debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
+ 	/* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
+ 	void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
+ 	void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
+@@ -103,6 +114,7 @@ private:
+ 
+ 	int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
+ 	int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
++	int setupStandardBayerOrder(BayerFormat::Order order);
+ 	int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
+ 	void setupInputMemcpy(const uint8_t *linePointers[]);
+ 	void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
+@@ -131,6 +143,7 @@ private:
+ 	unsigned int lineBufferLength_;
+ 	unsigned int lineBufferPadding_;
+ 	unsigned int lineBufferIndex_;
++	unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
+ 	bool enableInputMemcpy_;
+ 	float gamma_correction_;
+ 	unsigned int measuredFrames_;
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch b/users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch
new file mode 100644
index 0000000000..0abca2ea82
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch
@@ -0,0 +1,127 @@
+From 186db51d54bcbd4d5096bea1e4396966c2dad001 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:19 +0100
+Subject: [PATCH 15/21] libcamera: debayer_cpu: Add BGR888 output support
+
+BGR888 is RGB888 with the red and blue pixels swapped, adjust
+the debayering to swap the red and blue pixels in the bayer pattern
+to add support for writing formats::BGR888.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
+---
+ src/libcamera/software_isp/debayer_cpu.cpp | 42 +++++++++++++++++++---
+ src/libcamera/software_isp/debayer_cpu.h   |  1 +
+ 2 files changed, 38 insertions(+), 5 deletions(-)
+
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index eb1c2718..a1692693 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -268,7 +268,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 		config.bpp = (bayerFormat.bitDepth + 7) & ~7;
+ 		config.patternSize.width = 2;
+ 		config.patternSize.height = 2;
+-		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
++		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888, formats::BGR888 });
+ 		return 0;
+ 	}
+ 
+@@ -278,7 +278,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 		config.bpp = 10;
+ 		config.patternSize.width = 4; /* 5 bytes per *4* pixels */
+ 		config.patternSize.height = 2;
+-		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
++		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888, formats::BGR888 });
+ 		return 0;
+ 	}
+ 
+@@ -289,7 +289,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 
+ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
+ {
+-	if (outputFormat == formats::RGB888) {
++	if (outputFormat == formats::RGB888 || outputFormat == formats::BGR888) {
+ 		config.bpp = 24;
+ 		return 0;
+ 	}
+@@ -325,13 +325,41 @@ int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
+ 	return 0;
+ }
+ 
+-/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */
+-int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat)
++int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat)
+ {
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
+ 	xShift_ = 0;
++	swapRedBlueGains_ = false;
++
++	switch (outputFormat) {
++	case formats::RGB888:
++		break;
++	case formats::BGR888:
++		/* Swap R and B in bayer order to generate BGR888 instead of RGB888 */
++		swapRedBlueGains_ = true;
++
++		switch (bayerFormat.order) {
++		case BayerFormat::BGGR:
++			bayerFormat.order = BayerFormat::RGGB;
++			break;
++		case BayerFormat::GBRG:
++			bayerFormat.order = BayerFormat::GRBG;
++			break;
++		case BayerFormat::GRBG:
++			bayerFormat.order = BayerFormat::GBRG;
++			break;
++		case BayerFormat::RGGB:
++			bayerFormat.order = BayerFormat::BGGR;
++			break;
++		default:
++			goto invalid_fmt;
++		}
++		break;
++	default:
++		goto invalid_fmt;
++	}
+ 
+ 	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
+ 	    bayerFormat.packing == BayerFormat::Packing::None &&
+@@ -378,6 +406,7 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] Pi
+ 		}
+ 	}
+ 
++invalid_fmt:
+ 	LOG(Debayer, Error) << "Unsupported input output format combination";
+ 	return -EINVAL;
+ }
+@@ -661,6 +690,9 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
+ 		gamma_correction_ = params.gamma;
+ 	}
+ 
++	if (swapRedBlueGains_)
++		std::swap(params.gainR, params.gainB);
++
+ 	for (unsigned int i = 0; i < kRGBLookupSize; i++) {
+ 		constexpr unsigned int div =
+ 			kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize;
+diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
+index fd1fa180..5f44fc65 100644
+--- a/src/libcamera/software_isp/debayer_cpu.h
++++ b/src/libcamera/software_isp/debayer_cpu.h
+@@ -145,6 +145,7 @@ private:
+ 	unsigned int lineBufferIndex_;
+ 	unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
+ 	bool enableInputMemcpy_;
++	bool swapRedBlueGains_;
+ 	float gamma_correction_;
+ 	unsigned int measuredFrames_;
+ 	int64_t frameProcessTime_;
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch b/users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
new file mode 100644
index 0000000000..724b67033f
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
@@ -0,0 +1,237 @@
+From e9580d30a1a79fce1ebd72ae74ceb4a3d1cf8fbb Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 19 Dec 2023 11:16:26 +0100
+Subject: [PATCH 16/21] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer
+ order DNU
+
+The ov01a1s sensor has the following bayer pattern (4x4 tile repeating):
+
+IGIG
+GBGR
+IGIG
+GRGB
+
+Add support for this PixelFormat to libcamera.
+
+Do Not Upstream, first the include/linux/media-bus-format.h and
+include/linux/videodev2.h changes need to land in the upstream kernel.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ include/libcamera/internal/bayer_format.h |  3 ++-
+ include/linux/drm_fourcc.h                |  2 ++
+ include/linux/media-bus-format.h          |  4 +++-
+ include/linux/videodev2.h                 |  3 +++
+ src/libcamera/bayer_format.cpp            |  5 +++++
+ src/libcamera/camera_sensor.cpp           |  3 +++
+ src/libcamera/formats.cpp                 | 20 ++++++++++++++++++++
+ src/libcamera/formats.yaml                |  5 +++++
+ src/libcamera/v4l2_pixelformat.cpp        |  4 ++++
+ src/libcamera/v4l2_subdevice.cpp          |  1 +
+ 10 files changed, 48 insertions(+), 2 deletions(-)
+
+diff --git a/include/libcamera/internal/bayer_format.h b/include/libcamera/internal/bayer_format.h
+index 78ba3969..e77106c3 100644
+--- a/include/libcamera/internal/bayer_format.h
++++ b/include/libcamera/internal/bayer_format.h
+@@ -27,7 +27,8 @@ public:
+ 		GBRG = 1,
+ 		GRBG = 2,
+ 		RGGB = 3,
+-		MONO = 4
++		MONO = 4,
++		IGIG_GBGR_IGIG_GRGB = 5,
+ 	};
+ 
+ 	enum class Packing : uint16_t {
+diff --git a/include/linux/drm_fourcc.h b/include/linux/drm_fourcc.h
+index 1496e097..750ae8c9 100644
+--- a/include/linux/drm_fourcc.h
++++ b/include/linux/drm_fourcc.h
+@@ -405,6 +405,8 @@ extern "C" {
+ #define DRM_FORMAT_SGRBG10	fourcc_code('B', 'A', '1', '0')
+ #define DRM_FORMAT_SGBRG10	fourcc_code('G', 'B', '1', '0')
+ #define DRM_FORMAT_SBGGR10	fourcc_code('B', 'G', '1', '0')
++/* Mixed 10 bit bayer + ir pixel pattern found on Omnivision ov01a1s */
++#define DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10 fourcc_code('O', 'V', '1', 'S')
+ 
+ /* 12-bit Bayer formats */
+ #define DRM_FORMAT_SRGGB12	fourcc_code('R', 'G', '1', '2')
+diff --git a/include/linux/media-bus-format.h b/include/linux/media-bus-format.h
+index 0dfc11ee..c5fbda0e 100644
+--- a/include/linux/media-bus-format.h
++++ b/include/linux/media-bus-format.h
+@@ -112,7 +112,7 @@
+ #define MEDIA_BUS_FMT_YUV16_1X48		0x202a
+ #define MEDIA_BUS_FMT_UYYVYY16_0_5X48		0x202b
+ 
+-/* Bayer - next is	0x3021 */
++/* Bayer - next is 0x3022 */
+ #define MEDIA_BUS_FMT_SBGGR8_1X8		0x3001
+ #define MEDIA_BUS_FMT_SGBRG8_1X8		0x3013
+ #define MEDIA_BUS_FMT_SGRBG8_1X8		0x3002
+@@ -145,6 +145,8 @@
+ #define MEDIA_BUS_FMT_SGBRG16_1X16		0x301e
+ #define MEDIA_BUS_FMT_SGRBG16_1X16		0x301f
+ #define MEDIA_BUS_FMT_SRGGB16_1X16		0x3020
++/* Mixed bayer + ir pixel pattern found on ov01a1s */
++#define MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10 0x3021
+ 
+ /* JPEG compressed formats - next is	0x4002 */
+ #define MEDIA_BUS_FMT_JPEG_1X8			0x4001
+diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
+index bfb315d6..13c6c9d3 100644
+--- a/include/linux/videodev2.h
++++ b/include/linux/videodev2.h
+@@ -678,6 +678,9 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_SGBRG16 v4l2_fourcc('G', 'B', '1', '6') /* 16  GBGB.. RGRG.. */
+ #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16  GRGR.. BGBG.. */
+ #define V4L2_PIX_FMT_SRGGB16 v4l2_fourcc('R', 'G', '1', '6') /* 16  RGRG.. GBGB.. */
++	/* 10bit mixed bayer + ir pixel pattern found on ov01a1s */
++#define V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10  v4l2_fourcc('O', 'V', '1', 'S') /* unpacked */
++#define V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P v4l2_fourcc('O', 'V', '1', 'P') /* packed */
+ 
+ /* HSV formats */
+ #define V4L2_PIX_FMT_HSV24 v4l2_fourcc('H', 'S', 'V', '3')
+diff --git a/src/libcamera/bayer_format.cpp b/src/libcamera/bayer_format.cpp
+index 3bf15fb4..ae227540 100644
+--- a/src/libcamera/bayer_format.cpp
++++ b/src/libcamera/bayer_format.cpp
+@@ -108,6 +108,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{
+ 		{ formats::SGRBG10, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10) } },
+ 	{ { BayerFormat::RGGB, 10, BayerFormat::Packing::None },
+ 		{ formats::SRGGB10, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10) } },
++	{ { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::None },
++		{ formats::SIGIG_GBGR_IGIG_GRGB10, V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10) } },
+ 	{ { BayerFormat::BGGR, 10, BayerFormat::Packing::CSI2 },
+ 		{ formats::SBGGR10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P) } },
+ 	{ { BayerFormat::GBRG, 10, BayerFormat::Packing::CSI2 },
+@@ -116,6 +118,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{
+ 		{ formats::SGRBG10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P) } },
+ 	{ { BayerFormat::RGGB, 10, BayerFormat::Packing::CSI2 },
+ 		{ formats::SRGGB10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P) } },
++	{ { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::CSI2 },
++		{ formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P) } },
+ 	{ { BayerFormat::BGGR, 10, BayerFormat::Packing::IPU3 },
+ 		{ formats::SBGGR10_IPU3, V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10) } },
+ 	{ { BayerFormat::GBRG, 10, BayerFormat::Packing::IPU3 },
+@@ -193,6 +197,7 @@ const std::unordered_map<unsigned int, BayerFormat> mbusCodeToBayer{
+ 	{ MEDIA_BUS_FMT_SGBRG10_1X10, { BayerFormat::GBRG, 10, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SGRBG10_1X10, { BayerFormat::GRBG, 10, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SRGGB10_1X10, { BayerFormat::RGGB, 10, BayerFormat::Packing::None } },
++	{ MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SBGGR12_1X12, { BayerFormat::BGGR, 12, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SGBRG12_1X12, { BayerFormat::GBRG, 12, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SGRBG12_1X12, { BayerFormat::GRBG, 12, BayerFormat::Packing::None } },
+diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
+index 0ef78d9c..f19f72ea 100644
+--- a/src/libcamera/camera_sensor.cpp
++++ b/src/libcamera/camera_sensor.cpp
+@@ -510,6 +510,9 @@ int CameraSensor::initProperties()
+ 		case BayerFormat::MONO:
+ 			cfa = properties::draft::MONO;
+ 			break;
++		case BayerFormat::IGIG_GBGR_IGIG_GRGB:
++			cfa = properties::draft::RGB;
++			break;
+ 		}
+ 
+ 		properties_.set(properties::draft::ColorFilterArrangement, cfa);
+diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp
+index 447e6238..aef7d598 100644
+--- a/src/libcamera/formats.cpp
++++ b/src/libcamera/formats.cpp
+@@ -599,6 +599,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
+ 		.pixelsPerGroup = 2,
+ 		.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
+ 	} },
++	{ formats::SIGIG_GBGR_IGIG_GRGB10, {
++		.name = "SIGIG_GBGR_IGIG_GRGB10",
++		.format = formats::SIGIG_GBGR_IGIG_GRGB10,
++		.v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10), },
++		.bitsPerPixel = 10,
++		.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
++		.packed = false,
++		.pixelsPerGroup = 4,
++		.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
++	} },
+ 	{ formats::SBGGR10_CSI2P, {
+ 		.name = "SBGGR10_CSI2P",
+ 		.format = formats::SBGGR10_CSI2P,
+@@ -639,6 +649,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
+ 		.pixelsPerGroup = 4,
+ 		.planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
+ 	} },
++	{ formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, {
++		.name = "SIGIG_GBGR_IGIG_GRGB10_CSI2P",
++		.format = formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P,
++		.v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P), },
++		.bitsPerPixel = 10,
++		.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
++		.packed = true,
++		.pixelsPerGroup = 4,
++		.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
++	} },
+ 	{ formats::SBGGR12, {
+ 		.name = "SBGGR12",
+ 		.format = formats::SBGGR12,
+diff --git a/src/libcamera/formats.yaml b/src/libcamera/formats.yaml
+index 539ac0b3..0786a900 100644
+--- a/src/libcamera/formats.yaml
++++ b/src/libcamera/formats.yaml
+@@ -100,6 +100,8 @@ formats:
+       fourcc: DRM_FORMAT_SGBRG10
+   - SBGGR10:
+       fourcc: DRM_FORMAT_SBGGR10
++  - SIGIG_GBGR_IGIG_GRGB10:
++      fourcc: DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10
+ 
+   - SRGGB12:
+       fourcc: DRM_FORMAT_SRGGB12
+@@ -144,6 +146,9 @@ formats:
+   - SBGGR10_CSI2P:
+       fourcc: DRM_FORMAT_SBGGR10
+       mod: MIPI_FORMAT_MOD_CSI2_PACKED
++  - SIGIG_GBGR_IGIG_GRGB10_CSI2P:
++      fourcc: DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10
++      mod: MIPI_FORMAT_MOD_CSI2_PACKED
+ 
+   - SRGGB12_CSI2P:
+       fourcc: DRM_FORMAT_SRGGB12
+diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp
+index 5551c62e..53078d99 100644
+--- a/src/libcamera/v4l2_pixelformat.cpp
++++ b/src/libcamera/v4l2_pixelformat.cpp
+@@ -153,6 +153,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{
+ 		{ formats::SGRBG10, "10-bit Bayer GRGR/BGBG" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),
+ 		{ formats::SRGGB10, "10-bit Bayer RGRG/GBGB" } },
++	{ V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10),
++		{ formats::SIGIG_GBGR_IGIG_GRGB10, "10-bit Bayer GRGB/IGIG/GBGR/IGIG" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),
+ 		{ formats::SBGGR10_CSI2P, "10-bit Bayer BGBG/GRGR Packed" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),
+@@ -161,6 +163,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{
+ 		{ formats::SGRBG10_CSI2P, "10-bit Bayer GRGR/BGBG Packed" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),
+ 		{ formats::SRGGB10_CSI2P, "10-bit Bayer RGRG/GBGB Packed" } },
++	{ V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P),
++		{ formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, "10-bit Bayer GRGB/IGIG/GBGR/IGIG Packed" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),
+ 		{ formats::SBGGR12, "12-bit Bayer BGBG/GRGR" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),
+diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
+index 15e8206a..4ad37aaf 100644
+--- a/src/libcamera/v4l2_subdevice.cpp
++++ b/src/libcamera/v4l2_subdevice.cpp
+@@ -128,6 +128,7 @@ const std::map<uint32_t, V4L2SubdeviceFormatInfo> formatInfoMap = {
+ 	{ MEDIA_BUS_FMT_SGBRG10_1X10, { 10, "SGBRG10_1X10", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SGRBG10_1X10, { 10, "SGRBG10_1X10", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SRGGB10_1X10, { 10, "SRGGB10_1X10", PixelFormatInfo::ColourEncodingRAW } },
++	{ MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { 10, "SIGIG_GBGR_IGIG_GRGB10_1X10", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SBGGR12_1X12, { 12, "SBGGR12_1X12", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SGBRG12_1X12, { 12, "SGBRG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SGRBG12_1X12, { 12, "SGRBG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch b/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
new file mode 100644
index 0000000000..2343e9c46f
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
@@ -0,0 +1,132 @@
+From 6c509a3d144d46a11454d32d128d16e16602b50f Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:20 +0100
+Subject: [PATCH 17/21] libcamera: Add "Software ISP benchmarking"
+ documentation
+
+Add a "Software ISP benchmarking" documentation section which describes
+the performance/power consumption measurements used during
+the Software ISP's development.
+
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
+---
+ Documentation/index.rst                     |  1 +
+ Documentation/meson.build                   |  1 +
+ Documentation/software-isp-benchmarking.rst | 82 +++++++++++++++++++++
+ 3 files changed, 84 insertions(+)
+ create mode 100644 Documentation/software-isp-benchmarking.rst
+
+diff --git a/Documentation/index.rst b/Documentation/index.rst
+index 63fac72d..5442ae75 100644
+--- a/Documentation/index.rst
++++ b/Documentation/index.rst
+@@ -24,3 +24,4 @@
+    Lens driver requirements <lens_driver_requirements>
+    Python Bindings <python-bindings>
+    Camera Sensor Model <camera-sensor-model>
++   SoftwareISP Benchmarking <software-isp-benchmarking>
+diff --git a/Documentation/meson.build b/Documentation/meson.build
+index 7a58fec8..3872e0a8 100644
+--- a/Documentation/meson.build
++++ b/Documentation/meson.build
+@@ -80,6 +80,7 @@ if sphinx.found()
+         'lens_driver_requirements.rst',
+         'python-bindings.rst',
+         'sensor_driver_requirements.rst',
++        'software-isp-benchmarking.rst',
+        '../README.rst',
+     ]
+ 
+diff --git a/Documentation/software-isp-benchmarking.rst b/Documentation/software-isp-benchmarking.rst
+new file mode 100644
+index 00000000..b2803953
+--- /dev/null
++++ b/Documentation/software-isp-benchmarking.rst
+@@ -0,0 +1,82 @@
++.. SPDX-License-Identifier: CC-BY-SA-4.0
++
++.. _software-isp-benchmarking:
++
++Software ISP benchmarking
++=========================
++
++The Software ISP is particularly sensitive to performance regressions
++therefore it is a good idea to always benchmark the Software ISP
++before and after making changes to it and ensure that there are
++no performance regressions.
++
++DebayerCpu class builtin benchmark
++----------------------------------
++
++The DebayerCpu class has a builtin benchmark. This benchmark
++measures the time spent on processing (collecting statistics
++and debayering) only, it does not measure the time spent on
++capturing or outputting the frames.
++
++The builtin benchmark always runs. So this can be used by simply
++running "cam" or "qcam" with a pipeline using the Software ISP.
++
++When it runs it will skip measuring the first 30 frames to
++allow the caches and the CPU temperature (turbo-ing) to warm-up
++and then it measures 30 fps and shows the total and per frame
++processing time using an info level log message:
++
++.. code-block:: text
++
++   INFO Debayer debayer_cpu.cpp:907 Processed 30 frames in 244317us, 8143 us/frame
++
++To get stable measurements it is advised to disable any other processes which
++may cause significant CPU usage (e.g. disable wifi, bluetooth and browsers).
++When possible it is also advisable to disable CPU turbo-ing and
++frequency-scaling.
++
++For example when benchmarking on a Lenovo ThinkPad X1 Yoga Gen 8, with
++the charger plugged in, the CPU can be fixed to run at 2 GHz using:
++
++.. code-block:: shell
++
++   sudo x86_energy_perf_policy --turbo-enable 0
++   sudo cpupower frequency-set -d 2GHz -u 2GHz
++
++with these settings the builtin bench reports a processing time of ~7.8ms/frame
++on this laptop for FHD SGRBG10 (unpacked) bayer data.
++
++Measuring power consumption
++---------------------------
++
++Since the Software ISP is often used on mobile devices it is also
++important to measure power consumption and ensure that that does
++not regress.
++
++For example to measure power consumption on a Lenovo ThinkPad X1 Yoga Gen 8
++it needs to be running on battery and it should be configured with its
++platform-profile (/sys/firmware/acpi/platform_profile) set to balanced and
++with its default turbo and frequency-scaling behavior to match real world usage.
++
++Then start qcam to capture a FHD picture at 30 fps and position the qcam window
++so that it is fully visible. After this run the following command to monitor
++the power consumption:
++
++.. code-block:: shell
++
++   watch -n 10 cat /sys/class/power_supply/BAT0/power_now /sys/class/hwmon/hwmon6/fan?_input
++
++Note this not only measures the power consumption in Β΅W it also monitors
++the speed of this laptop's 2 fans. This is important because depending on
++the ambient temperature the 2 fans may spin up while testing and this
++will cause an additional power consumption of approx. 0.5 W messing up
++the measurement.
++
++After starting qcam + the watch command let the laptop sit without using
++it for 2 minutes for the readings to stabilize. Then check that the fans
++have not turned on and manually take a couple of consecutive power readings
++and avarage these.
++
++On the example Lenovo ThinkPad X1 Yoga Gen 8 laptop this results in
++a measured power consumption of approx. 13 W while running qcam versus
++approx. 4-5 W while setting idle with its OLED panel on.
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch b/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch
new file mode 100644
index 0000000000..c746b74dba
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch
@@ -0,0 +1,396 @@
+From bb608d177135d74e3c98b8a61fb459ebe254bca5 Mon Sep 17 00:00:00 2001
+From: Milan Zamazal <mzamazal@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:21 +0100
+Subject: [PATCH 18/21] libcamera: software_isp: Apply black level compensation
+
+Black may not be represented as 0 pixel value for given hardware, it may be
+higher.  If this is not compensated then various problems may occur such as low
+contrast or suboptimal exposure.
+
+The black pixel value can be either retrieved from a tuning file for the given
+hardware, or automatically on fly.  The former is the right and correct method,
+while the latter can be used when a tuning file is not available for the given
+hardware.  Since there is currently no support for tuning files in software ISP,
+the automatic, hardware independent way, is always used.  Support for tuning
+files should be added in future but it will require more work than this patch.
+
+The patch looks at the image histogram and assumes that black starts when pixel
+values start occurring on the left.  A certain amount of the darkest pixels is
+ignored; it doesn't matter whether they represent various kinds of noise or are
+real, they are better to omit in any case to make the image looking better.  It
+also doesn't matter whether the darkest pixels occur around the supposed black
+level or are spread between 0 and the black level, the difference is not
+important.
+
+An arbitrary threshold of 2% darkest pixels is applied; there is no magic about
+that value.
+
+The patch assumes that the black values for different colors are the same and
+doesn't attempt any other non-primitive enhancements.  It cannot completely
+replace tuning files and simplicity, while providing visible benefit, is its
+goal.  Anything more sophisticated is left for future patches.
+
+A possible cheap enhancement, if needed, could be setting exposure + gain to
+minimum values temporarily, before setting the black level.  In theory, the
+black level should be fixed but it may not be reached in all images.  For this
+reason, the patch updates black level only if the observed value is lower than
+the current one; it should be never increased.
+
+The purpose of the patch is to compensate for hardware properties.  General
+image contrast enhancements are out of scope of this patch.
+
+Stats are still gathered as an uncorrected histogram, to avoid any confusion and
+to represent the raw image data.  Exposure must be determined after the black
+level correction -- it has no influence on the sub-black area and must be
+correct after applying the black level correction.  The granularity of the
+histogram is increased from 16 to 64 to provide a better precision (there is no
+theory behind either of those numbers).
+
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ .../internal/software_isp/debayer_params.h    |  4 +
+ .../internal/software_isp/swisp_stats.h       | 10 ++-
+ src/ipa/simple/black_level.cpp                | 86 +++++++++++++++++++
+ src/ipa/simple/black_level.h                  | 28 ++++++
+ src/ipa/simple/meson.build                    |  7 +-
+ src/ipa/simple/soft_simple.cpp                | 28 ++++--
+ src/libcamera/software_isp/debayer_cpu.cpp    | 13 ++-
+ src/libcamera/software_isp/debayer_cpu.h      |  1 +
+ src/libcamera/software_isp/software_isp.cpp   |  2 +-
+ 9 files changed, 162 insertions(+), 17 deletions(-)
+ create mode 100644 src/ipa/simple/black_level.cpp
+ create mode 100644 src/ipa/simple/black_level.h
+
+diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
+index 98965fa1..5e38e08b 100644
+--- a/include/libcamera/internal/software_isp/debayer_params.h
++++ b/include/libcamera/internal/software_isp/debayer_params.h
+@@ -43,6 +43,10 @@ struct DebayerParams {
+ 	 * \brief Gamma correction, 1.0 is no correction
+ 	 */
+ 	float gamma;
++	/**
++	 * \brief Level of the black point, 0..255, 0 is no correction.
++	 */
++	unsigned int blackLevel;
+ };
+ 
+ } /* namespace libcamera */
+diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h
+index afe42c9a..25cd5abd 100644
+--- a/include/libcamera/internal/software_isp/swisp_stats.h
++++ b/include/libcamera/internal/software_isp/swisp_stats.h
+@@ -7,6 +7,8 @@
+ 
+ #pragma once
+ 
++#include <array>
++
+ namespace libcamera {
+ 
+ /**
+@@ -28,11 +30,15 @@ struct SwIspStats {
+ 	/**
+ 	 * \brief Number of bins in the yHistogram.
+ 	 */
+-	static constexpr unsigned int kYHistogramSize = 16;
++	static constexpr unsigned int kYHistogramSize = 64;
++	/**
++	 * \brief Type of the histogram.
++	 */
++	using histogram = std::array<unsigned int, kYHistogramSize>;
+ 	/**
+ 	 * \brief A histogram of luminance values.
+ 	 */
+-	std::array<unsigned int, kYHistogramSize> yHistogram;
++	histogram yHistogram;
+ };
+ 
+ } /* namespace libcamera */
+diff --git a/src/ipa/simple/black_level.cpp b/src/ipa/simple/black_level.cpp
+new file mode 100644
+index 00000000..8d52201b
+--- /dev/null
++++ b/src/ipa/simple/black_level.cpp
+@@ -0,0 +1,86 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2024, Red Hat Inc.
++ *
++ * black_level.cpp - black level handling
++ */
++
++#include "black_level.h"
++
++#include <numeric>
++
++#include <libcamera/base/log.h>
++
++namespace libcamera {
++
++LOG_DEFINE_CATEGORY(IPASoftBL)
++
++/**
++ * \class BlackLevel
++ * \brief Object providing black point level for software ISP
++ *
++ * Black level can be provided in hardware tuning files or, if no tuning file is
++ * available for the given hardware, guessed automatically, with less accuracy.
++ * As tuning files are not yet implemented for software ISP, BlackLevel
++ * currently provides only guessed black levels.
++ *
++ * This class serves for tracking black level as a property of the underlying
++ * hardware, not as means of enhancing a particular scene or image.
++ *
++ * The class is supposed to be instantiated for the given camera stream.
++ * The black level can be retrieved using BlackLevel::get() method. It is
++ * initially 0 and may change when updated using BlackLevel::update() method.
++ */
++
++BlackLevel::BlackLevel()
++	: blackLevel_(255), blackLevelSet_(false)
++{
++}
++
++/**
++ * \brief Return the current black level
++ *
++ * \return The black level, in the range from 0 (minimum) to 255 (maximum).
++ * If the black level couldn't be determined yet, return 0.
++ */
++unsigned int BlackLevel::get() const
++{
++	return blackLevelSet_ ? blackLevel_ : 0;
++}
++
++/**
++ * \brief Update black level from the provided histogram
++ * \param[in] yHistogram The histogram to be used for updating black level
++ *
++ * The black level is property of the given hardware, not image. It is updated
++ * only if it has not been yet set or if it is lower than the lowest value seen
++ * so far.
++ */
++void BlackLevel::update(SwIspStats::histogram &yHistogram)
++{
++	// The constant is selected to be "good enough", not overly conservative or
++	// aggressive. There is no magic about the given value.
++	constexpr float ignoredPercentage_ = 0.02;
++	const unsigned int total =
++		std::accumulate(begin(yHistogram), end(yHistogram), 0);
++	const unsigned int pixelThreshold = ignoredPercentage_ * total;
++	const unsigned int currentBlackIdx =
++		blackLevel_ / (256 / SwIspStats::kYHistogramSize);
++
++	for (unsigned int i = 0, seen = 0;
++	     i < currentBlackIdx && i < SwIspStats::kYHistogramSize;
++	     i++) {
++		seen += yHistogram[i];
++		if (seen >= pixelThreshold) {
++			blackLevel_ = i * (256 / SwIspStats::kYHistogramSize);
++			blackLevelSet_ = true;
++			LOG(IPASoftBL, Debug)
++				<< "Auto-set black level: "
++				<< i << "/" << SwIspStats::kYHistogramSize
++				<< " (" << 100 * (seen - yHistogram[i]) / total << "% below, "
++				<< 100 * seen / total << "% at or below)";
++			break;
++		}
++	};
++}
++} // namespace libcamera
+diff --git a/src/ipa/simple/black_level.h b/src/ipa/simple/black_level.h
+new file mode 100644
+index 00000000..b3785db0
+--- /dev/null
++++ b/src/ipa/simple/black_level.h
+@@ -0,0 +1,28 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2024, Red Hat Inc.
++ *
++ * black_level.h - black level handling
++ */
++
++#pragma once
++
++#include <array>
++
++#include "libcamera/internal/software_isp/swisp_stats.h"
++
++namespace libcamera {
++
++class BlackLevel
++{
++public:
++	BlackLevel();
++	unsigned int get() const;
++	void update(std::array<unsigned int, SwIspStats::kYHistogramSize> &yHistogram);
++
++private:
++	unsigned int blackLevel_;
++	bool blackLevelSet_;
++};
++
++} // namespace libcamera
+diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
+index 3e863db7..44b5f1d7 100644
+--- a/src/ipa/simple/meson.build
++++ b/src/ipa/simple/meson.build
+@@ -2,8 +2,13 @@
+ 
+ ipa_name = 'ipa_soft_simple'
+ 
++soft_simple_sources = files([
++    'soft_simple.cpp',
++    'black_level.cpp',
++])
++
+ mod = shared_module(ipa_name,
+-                    ['soft_simple.cpp', libcamera_generated_ipa_headers],
++                    [soft_simple_sources, libcamera_generated_ipa_headers],
+                     name_prefix : '',
+                     include_directories : [ipa_includes, libipa_includes],
+                     dependencies : libcamera_private,
+diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
+index 312df4ba..ac027568 100644
+--- a/src/ipa/simple/soft_simple.cpp
++++ b/src/ipa/simple/soft_simple.cpp
+@@ -22,6 +22,8 @@
+ #include "libcamera/internal/software_isp/debayer_params.h"
+ #include "libcamera/internal/software_isp/swisp_stats.h"
+ 
++#include "black_level.h"
++
+ namespace libcamera {
+ 
+ LOG_DEFINE_CATEGORY(IPASoft)
+@@ -33,7 +35,8 @@ class IPASoftSimple : public ipa::soft::IPASoftInterface
+ public:
+ 	IPASoftSimple()
+ 		: params_(static_cast<DebayerParams *>(MAP_FAILED)),
+-		  stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0)
++		  stats_(static_cast<SwIspStats *>(MAP_FAILED)),
++		  blackLevel_(BlackLevel()), ignore_updates_(0)
+ 	{
+ 	}
+ 
+@@ -63,6 +66,7 @@ private:
+ 	SharedFD fdParams_;
+ 	DebayerParams *params_;
+ 	SwIspStats *stats_;
++	BlackLevel blackLevel_;
+ 
+ 	int32_t exposure_min_, exposure_max_;
+ 	int32_t again_min_, again_max_;
+@@ -196,6 +200,10 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
+ 	params_->gainG = 256;
+ 	params_->gamma = 0.5;
+ 
++	if (ignore_updates_ > 0)
++		blackLevel_.update(stats_->yHistogram);
++	params_->blackLevel = blackLevel_.get();
++
+ 	setIspParams.emit(0);
+ 
+ 	/*
+@@ -211,18 +219,19 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
+ 	 * Calculate Mean Sample Value (MSV) according to formula from:
+ 	 * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
+ 	 */
+-	constexpr unsigned int yHistValsPerBin =
+-		SwIspStats::kYHistogramSize / kExposureBinsCount;
+-	constexpr unsigned int yHistValsPerBinMod =
+-		SwIspStats::kYHistogramSize /
+-		(SwIspStats::kYHistogramSize % kExposureBinsCount + 1);
++	const unsigned int blackLevelHistIdx =
++		params_->blackLevel / (256 / SwIspStats::kYHistogramSize);
++	const unsigned int histogramSize = SwIspStats::kYHistogramSize - blackLevelHistIdx;
++	const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount;
++	const unsigned int yHistValsPerBinMod =
++		histogramSize / (histogramSize % kExposureBinsCount + 1);
+ 	int ExposureBins[kExposureBinsCount] = {};
+ 	unsigned int denom = 0;
+ 	unsigned int num = 0;
+ 
+-	for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) {
++	for (unsigned int i = 0; i < histogramSize; i++) {
+ 		unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;
+-		ExposureBins[idx] += stats_->yHistogram[i];
++		ExposureBins[idx] += stats_->yHistogram[blackLevelHistIdx + i];
+ 	}
+ 
+ 	for (unsigned int i = 0; i < kExposureBinsCount; i++) {
+@@ -256,7 +265,8 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
+ 
+ 	LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV
+ 			    << " exp " << exposure_ << " again " << again_
+-			    << " gain R/B " << params_->gainR << "/" << params_->gainB;
++			    << " gain R/B " << params_->gainR << "/" << params_->gainB
++			    << " black level " << params_->blackLevel;
+ }
+ 
+ void IPASoftSimple::updateExposure(double exposureMSV)
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index a1692693..3be3cdfe 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -35,7 +35,7 @@ namespace libcamera {
+  * \param[in] stats Pointer to the stats object to use.
+  */
+ DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
+-	: stats_(std::move(stats)), gamma_correction_(1.0)
++	: stats_(std::move(stats)), gamma_correction_(1.0), blackLevel_(0)
+ {
+ #ifdef __x86_64__
+ 	enableInputMemcpy_ = false;
+@@ -683,11 +683,16 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
+ 	}
+ 
+ 	/* Apply DebayerParams */
+-	if (params.gamma != gamma_correction_) {
+-		for (unsigned int i = 0; i < kGammaLookupSize; i++)
+-			gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma);
++	if (params.gamma != gamma_correction_ || params.blackLevel != blackLevel_) {
++		const unsigned int blackIndex =
++			params.blackLevel * kGammaLookupSize / 256;
++		std::fill(gamma_.begin(), gamma_.begin() + blackIndex, 0);
++		const float divisor = kGammaLookupSize - blackIndex - 1.0;
++		for (unsigned int i = blackIndex; i < kGammaLookupSize; i++)
++			gamma_[i] = UINT8_MAX * powf((i - blackIndex) / divisor, params.gamma);
+ 
+ 		gamma_correction_ = params.gamma;
++		blackLevel_ = params.blackLevel;
+ 	}
+ 
+ 	if (swapRedBlueGains_)
+diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
+index 5f44fc65..ea02f909 100644
+--- a/src/libcamera/software_isp/debayer_cpu.h
++++ b/src/libcamera/software_isp/debayer_cpu.h
+@@ -147,6 +147,7 @@ private:
+ 	bool enableInputMemcpy_;
+ 	bool swapRedBlueGains_;
+ 	float gamma_correction_;
++	unsigned int blackLevel_;
+ 	unsigned int measuredFrames_;
+ 	int64_t frameProcessTime_;
+ 	/* Skip 30 frames for things to stabilize then measure 30 frames */
+diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
+index 388b4496..9b49be41 100644
+--- a/src/libcamera/software_isp/software_isp.cpp
++++ b/src/libcamera/software_isp/software_isp.cpp
+@@ -64,7 +64,7 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
+  */
+ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
+ 	: debayer_(nullptr),
+-	  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f },
++	  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },
+ 	  dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
+ {
+ 	if (!dmaHeap_.isValid()) {
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch b/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch
new file mode 100644
index 0000000000..5b562c603c
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch
@@ -0,0 +1,239 @@
+From b0c07674abecb05dc0af93a4b749971f057bc3c6 Mon Sep 17 00:00:00 2001
+From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
+Date: Mon, 11 Mar 2024 15:15:22 +0100
+Subject: [PATCH 19/21] libcamera: Soft IPA: use CameraSensorHelper for
+ analogue gain
+
+Use CameraSensorHelper to convert the analogue gain code read from the
+camera sensor into real analogue gain value. In the future this makes
+it possible to use faster AE/AGC algorithm. For now the same AE/AGC
+algorithm is used, but even then the CameraSensorHelper lets us use the
+full range of analogue gain values.
+
+If there is no CameraSensorHelper for the camera sensor in use, a
+warning log message is printed, and the AE/AGC works exactly as before
+this change.
+
+Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ .../internal/software_isp/software_isp.h      |  3 +-
+ src/ipa/simple/soft_simple.cpp                | 77 ++++++++++++-------
+ src/libcamera/pipeline/simple/simple.cpp      |  2 +-
+ src/libcamera/software_isp/software_isp.cpp   |  8 +-
+ 4 files changed, 57 insertions(+), 33 deletions(-)
+
+diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
+index 8d25e979..2a6db7ba 100644
+--- a/include/libcamera/internal/software_isp/software_isp.h
++++ b/include/libcamera/internal/software_isp/software_isp.h
+@@ -26,6 +26,7 @@
+ #include <libcamera/ipa/soft_ipa_interface.h>
+ #include <libcamera/ipa/soft_ipa_proxy.h>
+ 
++#include "libcamera/internal/camera_sensor.h"
+ #include "libcamera/internal/dma_heaps.h"
+ #include "libcamera/internal/pipeline_handler.h"
+ #include "libcamera/internal/shared_mem_object.h"
+@@ -43,7 +44,7 @@ LOG_DECLARE_CATEGORY(SoftwareIsp)
+ class SoftwareIsp
+ {
+ public:
+-	SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
++	SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor);
+ 	~SoftwareIsp();
+ 
+ 	int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
+diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
+index ac027568..e4d64762 100644
+--- a/src/ipa/simple/soft_simple.cpp
++++ b/src/ipa/simple/soft_simple.cpp
+@@ -22,6 +22,8 @@
+ #include "libcamera/internal/software_isp/debayer_params.h"
+ #include "libcamera/internal/software_isp/swisp_stats.h"
+ 
++#include "libipa/camera_sensor_helper.h"
++
+ #include "black_level.h"
+ 
+ namespace libcamera {
+@@ -67,18 +69,27 @@ private:
+ 	DebayerParams *params_;
+ 	SwIspStats *stats_;
+ 	BlackLevel blackLevel_;
++	std::unique_ptr<CameraSensorHelper> camHelper_;
+ 
+ 	int32_t exposure_min_, exposure_max_;
+-	int32_t again_min_, again_max_;
+-	int32_t again_, exposure_;
++	int32_t exposure_;
++	double again_min_, again_max_, againMinStep_;
++	double again_;
+ 	unsigned int ignore_updates_;
+ };
+ 
+-int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,
++int IPASoftSimple::init(const IPASettings &settings,
+ 			const SharedFD &fdStats,
+ 			const SharedFD &fdParams,
+ 			const ControlInfoMap &sensorInfoMap)
+ {
++	camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
++	if (camHelper_ == nullptr) {
++		LOG(IPASoft, Warning)
++			<< "Failed to create camera sensor helper for "
++			<< settings.sensorModel;
++	}
++
+ 	fdStats_ = fdStats;
+ 	if (!fdStats_.isValid()) {
+ 		LOG(IPASoft, Error) << "Invalid Statistics handle";
+@@ -132,25 +143,35 @@ int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap)
+ 		exposure_min_ = 1;
+ 	}
+ 
+-	again_min_ = gain_info.min().get<int32_t>();
+-	again_max_ = gain_info.max().get<int32_t>();
+-	/*
+-	 * The camera sensor gain (g) is usually not equal to the value written
+-	 * into the gain register (x). But the way how the AGC algorithm changes
+-	 * the gain value to make the total exposure closer to the optimum assumes
+-	 * that g(x) is not too far from linear function. If the minimal gain is 0,
+-	 * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
+-	 * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
+-	 * one edge, and very small near the other) we limit the range of the gain
+-	 * values used.
+-	 */
+-	if (!again_min_) {
+-		LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
+-		again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);
++	int32_t again_min = gain_info.min().get<int32_t>();
++	int32_t again_max = gain_info.max().get<int32_t>();
++
++	if (camHelper_) {
++		again_min_ = camHelper_->gain(again_min);
++		again_max_ = camHelper_->gain(again_max);
++		againMinStep_ = (again_max_ - again_min_) / 100.0;
++	} else {
++		/*
++		 * The camera sensor gain (g) is usually not equal to the value written
++		 * into the gain register (x). But the way how the AGC algorithm changes
++		 * the gain value to make the total exposure closer to the optimum assumes
++		 * that g(x) is not too far from linear function. If the minimal gain is 0,
++		 * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
++		 * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
++		 * one edge, and very small near the other) we limit the range of the gain
++		 * values used.
++		 */
++		again_max_ = again_max;
++		if (!again_min) {
++			LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
++			again_min_ = std::min(100, again_min / 2 + again_max / 2);
++		}
++		againMinStep_ = 1.0;
+ 	}
+ 
+ 	LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
+-			   << ", gain " << again_min_ << "-" << again_max_;
++			   << ", gain " << again_min_ << "-" << again_max_
++			   << " (" << againMinStep_ << ")";
+ 
+ 	return 0;
+ }
+@@ -252,12 +273,14 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
+ 	ControlList ctrls(sensorControls);
+ 
+ 	exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>();
+-	again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
++	int32_t again = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
++	again_ = camHelper_ ? camHelper_->gain(again) : again;
+ 
+ 	updateExposure(exposureMSV);
+ 
+ 	ctrls.set(V4L2_CID_EXPOSURE, exposure_);
+-	ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
++	ctrls.set(V4L2_CID_ANALOGUE_GAIN,
++		  static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(again_) : again_));
+ 
+ 	ignore_updates_ = 2;
+ 
+@@ -276,7 +299,7 @@ void IPASoftSimple::updateExposure(double exposureMSV)
+ 	static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
+ 	static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
+ 
+-	int next;
++	double next;
+ 
+ 	if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
+ 		next = exposure_ * kExpNumeratorUp / kExpDenominator;
+@@ -286,18 +309,18 @@ void IPASoftSimple::updateExposure(double exposureMSV)
+ 			exposure_ = next;
+ 		if (exposure_ >= exposure_max_) {
+ 			next = again_ * kExpNumeratorUp / kExpDenominator;
+-			if (next - again_ < 1)
+-				again_ += 1;
++			if (next - again_ < againMinStep_)
++				again_ += againMinStep_;
+ 			else
+ 				again_ = next;
+ 		}
+ 	}
+ 
+ 	if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
+-		if (exposure_ == exposure_max_ && again_ != again_min_) {
++		if (exposure_ == exposure_max_ && again_ > again_min_) {
+ 			next = again_ * kExpNumeratorDown / kExpDenominator;
+-			if (again_ - next < 1)
+-				again_ -= 1;
++			if (again_ - next < againMinStep_)
++				again_ -= againMinStep_;
+ 			else
+ 				again_ = next;
+ 		} else {
+diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
+index c3ebb7b7..7e932a14 100644
+--- a/src/libcamera/pipeline/simple/simple.cpp
++++ b/src/libcamera/pipeline/simple/simple.cpp
+@@ -525,7 +525,7 @@ int SimpleCameraData::init()
+ 	 * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
+ 	 */
+ 	if (!converter_ && pipe->swIspEnabled()) {
+-		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());
++		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get());
+ 		if (!swIsp_->isValid()) {
+ 			LOG(SimplePipeline, Warning)
+ 				<< "Failed to create software ISP, disabling software debayering";
+diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
+index 9b49be41..ea4d96e4 100644
+--- a/src/libcamera/software_isp/software_isp.cpp
++++ b/src/libcamera/software_isp/software_isp.cpp
+@@ -60,9 +60,9 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
+ /**
+  * \brief Constructs SoftwareIsp object
+  * \param[in] pipe The pipeline handler in use
+- * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor
++ * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline handler
+  */
+-SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
++SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)
+ 	: debayer_(nullptr),
+ 	  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },
+ 	  dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
+@@ -97,10 +97,10 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorCont
+ 		return;
+ 	}
+ 
+-	int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
++	int ret = ipa_->init(IPASettings{ "No cfg file", sensor->model() },
+ 			     debayer_->getStatsFD(),
+ 			     sharedParams_.fd(),
+-			     sensorControls);
++			     sensor->controls());
+ 	if (ret) {
+ 		LOG(SoftwareIsp, Error) << "IPA init failed";
+ 		debayer_.reset();
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch b/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch
new file mode 100644
index 0000000000..343f04c850
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch
@@ -0,0 +1,95 @@
+From 2bde6e420571c6dc0ff25246620b4c987987f6be Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 19 Dec 2023 15:45:51 +0100
+Subject: [PATCH 20/21] ov01a1s HACK
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/camera_sensor.cpp            | 6 ++++++
+ src/libcamera/software_isp/debayer_cpu.cpp | 8 ++++++++
+ src/libcamera/software_isp/swstats_cpu.cpp | 4 ++++
+ 3 files changed, 18 insertions(+)
+
+diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
+index f19f72ea..7ad4b9ef 100644
+--- a/src/libcamera/camera_sensor.cpp
++++ b/src/libcamera/camera_sensor.cpp
+@@ -34,6 +34,9 @@
+ 
+ namespace libcamera {
+ 
++// HACK HACK
++bool is_ov01a1s = false;
++
+ LOG_DEFINE_CATEGORY(CameraSensor)
+ 
+ /**
+@@ -426,6 +429,9 @@ int CameraSensor::initProperties()
+ 	model_ = subdev_->model();
+ 	properties_.set(properties::Model, utils::toAscii(model_));
+ 
++	if (model_ == "ov01a1s")
++		is_ov01a1s = true;
++
+ 	/* Generate a unique ID for the sensor. */
+ 	int ret = generateId();
+ 	if (ret)
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index 3be3cdfe..d6599805 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -23,6 +23,7 @@
+ 
+ namespace libcamera {
+ 
++extern bool is_ov01a1s;
+ /**
+  * \class DebayerCpu
+  * \brief Class for debayering on the CPU
+@@ -262,6 +263,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
++	if (is_ov01a1s)
++		bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
++
+ 	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
+ 	    bayerFormat.packing == BayerFormat::Packing::None &&
+ 	    isStandardBayerOrder(bayerFormat.order)) {
+@@ -330,7 +334,11 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
++	if (is_ov01a1s)
++		bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
++
+ 	xShift_ = 0;
++
+ 	swapRedBlueGains_ = false;
+ 
+ 	switch (outputFormat) {
+diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
+index be310f56..cda1894a 100644
+--- a/src/libcamera/software_isp/swstats_cpu.cpp
++++ b/src/libcamera/software_isp/swstats_cpu.cpp
+@@ -19,6 +19,7 @@
+ 
+ namespace libcamera {
+ 
++extern bool is_ov01a1s;
+ /**
+  * \class SwStatsCpu
+  * \brief Class for gathering statistics on the CPU
+@@ -271,6 +272,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
+ 
++	if (is_ov01a1s)
++		bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
++
+ 	if (bayerFormat.packing == BayerFormat::Packing::None &&
+ 	    setupStandardBayerOrder(bayerFormat.order) == 0) {
+ 		switch (bayerFormat.bitDepth) {
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch b/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
new file mode 100644
index 0000000000..a3af38c93c
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
@@ -0,0 +1,42 @@
+From a21bb26dcfcc00425f031421b87576f9c81e4824 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 24 Jan 2024 20:44:29 +0100
+Subject: [PATCH 21/21] libcamera: debayer_cpu: Make the minimum size 1280x720
+
+pipewire + firefox default to what looks like 640x480 if we export
+the entire supported cropping range. Hardcode 720p as minsize for now.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/software_isp/debayer_cpu.cpp | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index d6599805..5a06b191 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -790,10 +790,17 @@ SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
+ 		return {};
+ 	}
+ 
+-	return SizeRange(Size(pattern_size.width, pattern_size.height),
+-			 Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
+-			      (inputSize.height - 2 * border_height) & ~(pattern_size.height - 1)),
+-			 pattern_size.width, pattern_size.height);
++	/*
++	 * pipewire + firefox default to what looks like 640x480
++	 * if we export the entire supported cropping range.
++	 * Hardcode 720p as minsize for now. Minsize should be
++	 * Size(pattern_size.width, pattern_size.height)
++	 */
++	unsigned int w = (inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1);
++	unsigned int h = (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1);
++	return SizeRange(Size(std::min(w, 1280u), std::min(h, 720u)),
++	                 Size(w, h),
++	                 pattern_size.width, pattern_size.height);
+ }
+ 
+ } /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/keyboards/dilemma/default.nix b/users/flokli/keyboards/dilemma/default.nix
new file mode 100644
index 0000000000..265f8e56db
--- /dev/null
+++ b/users/flokli/keyboards/dilemma/default.nix
@@ -0,0 +1,45 @@
+{ depot, pkgs, ... }:
+
+rec {
+  firmware = pkgs.stdenv.mkDerivation {
+    name = "keychron-bastardkb-dilemma-firmware";
+
+    src = pkgs.fetchFromGitHub {
+      owner = "qmk";
+      repo = "qmk_firmware";
+      rev = "728aa576b0cd65c6fb7cf77132fdcd06fcedb643"; # develop branch
+      hash = "sha256-YmdX8nEsB1R8d265HAmvwejPjEHJdoTnm4QNigzrcyw=";
+      fetchSubmodules = true;
+    };
+
+    patches = [ ./enable-taps.patch ];
+
+    postPatch = ''
+      patchShebangs util/uf2conv.py
+    '';
+
+    nativeBuildInputs = [
+      pkgs.python3
+      pkgs.qmk
+    ];
+
+    buildPhase = ''
+      mkdir -p keyboards/bastardkb/dilemma/3x5_3/keymaps/flokli
+      cp ${./keymap.c} keyboards/bastardkb/dilemma/3x5_3/keymaps/flokli/keymap.c
+      cp ${./rules.mk} keyboards/bastardkb/dilemma/3x5_3/keymaps/flokli/rules.mk
+
+      make bastardkb/dilemma/3x5_3:flokli
+    '';
+
+    installPhase = ''
+      mkdir -p $out
+      cp bastardkb_dilemma_3x5_3_flokli.uf2 $out/
+    '';
+  };
+
+  flash = pkgs.writeShellScript "flash.sh" ''
+    ${pkgs.qmk}/bin/qmk flash ${firmware}/bastardkb_dilemma_3x5_3_flokli.uf2
+  '';
+
+  meta.ci.targets = [ "firmware" ];
+}
diff --git a/users/flokli/keyboards/dilemma/enable-taps.patch b/users/flokli/keyboards/dilemma/enable-taps.patch
new file mode 100644
index 0000000000..afded85492
--- /dev/null
+++ b/users/flokli/keyboards/dilemma/enable-taps.patch
@@ -0,0 +1,24 @@
+From 32a1b9a189c13bec03c6b0f258121c47185db0ad Mon Sep 17 00:00:00 2001
+From: Florian Klink <flokli@flokli.de>
+Date: Tue, 23 Jan 2024 11:26:10 +0200
+Subject: [PATCH] bastardkb dilemma: enable taps
+
+---
+ keyboards/bastardkb/dilemma/3x5_3/config.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/keyboards/bastardkb/dilemma/3x5_3/config.h b/keyboards/bastardkb/dilemma/3x5_3/config.h
+index ccbc4e2f58..bf17dc7e02 100644
+--- a/keyboards/bastardkb/dilemma/3x5_3/config.h
++++ b/keyboards/bastardkb/dilemma/3x5_3/config.h
+@@ -42,6 +42,7 @@
+ #define POINTING_DEVICE_CS_PIN GP21
+ #undef CIRQUE_PINNACLE_DIAMETER_MM
+ #define CIRQUE_PINNACLE_DIAMETER_MM 40
++#define CIRQUE_PINNACLE_TAP_ENABLE 1
+ 
+ /* Reset. */
+ #define RP2040_BOOTLOADER_DOUBLE_TAP_RESET
+-- 
+2.43.0
+
diff --git a/users/flokli/keyboards/dilemma/keymap.c b/users/flokli/keyboards/dilemma/keymap.c
new file mode 100644
index 0000000000..2c21ef6c9e
--- /dev/null
+++ b/users/flokli/keyboards/dilemma/keymap.c
@@ -0,0 +1,220 @@
+/**
+ * Copyright 2022 Charly Delay <charly@codesink.dev> (@0xcharly)
+ * Copyright 2023 casuanoob <casuanoob@hotmail.com> (@casuanoob)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include QMK_KEYBOARD_H
+
+enum dilemma_keymap_layers {
+    LAYER_BASE = 0,
+    LAYER_NAVIGATION,
+    LAYER_MOUSE,
+    LAYER_MEDIA,
+    LAYER_NUMERAL,
+    LAYER_SYMBOLS,
+    LAYER_FUNCTION,
+};
+
+// Automatically enable sniping-mode on the pointer layer.
+#define DILEMMA_AUTO_SNIPING_ON_LAYER LAYER_MOUSE
+#define ESC_MED LT(LAYER_MEDIA, KC_ESC)
+#define SPC_NAV LT(LAYER_NAVIGATION, KC_SPC)
+#define TAB_MOU LT(LAYER_MOUSE, KC_TAB)
+#define ENT_SYM LT(LAYER_SYMBOLS, KC_ENT)
+#define BSP_NUM LT(LAYER_NUMERAL, KC_BSPC)
+#define DEL_FUN LT(LAYER_FUNCTION, KC_DEL)
+
+#ifndef POINTING_DEVICE_ENABLE
+#    define DRGSCRL KC_NO
+#    define DPI_MOD KC_NO
+#    define S_D_MOD KC_NO
+#    define SNIPING KC_NO
+#endif // !POINTING_DEVICE_ENABLE
+
+// clang-format off
+/** \brief COLEMAK-DH layout (3 rows, 10 columns). */
+#define LAYOUT_LAYER_BASE                                                                     \
+       KC_Q,    KC_W,    KC_F,    KC_P,    KC_B,    KC_J,    KC_L,    KC_U,    KC_Y, KC_QUOT, \
+       KC_A,    KC_R,    KC_S,    KC_T,    KC_G,    KC_M,    KC_N,    KC_E,    KC_I,    KC_O, \
+       KC_Z,    KC_X,    KC_C,    KC_D,    KC_V,    KC_K,    KC_H, KC_COMMA, KC_DOT, KC_SLSH, \
+                      ESC_MED, SPC_NAV, TAB_MOU, ENT_SYM, BSP_NUM, DEL_FUN
+
+/** Convenience row shorthands. */
+#define _______________DEAD_HALF_ROW_______________ XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX
+#define ______________HOME_ROW_GACS_L______________ KC_LGUI, KC_LALT, KC_LCTL, KC_LSFT, XXXXXXX
+#define ______________HOME_ROW_GACS_R______________ XXXXXXX, KC_LSFT, KC_LCTL, KC_RALT, KC_LGUI
+
+/*
+ * Layers used on the Dilemma.
+ *
+ * These layers started off heavily inspired by the Miryoku layout, but trimmed
+ * down and tailored for a stock experience that is meant to be fundation for
+ * further personalization.
+ *
+ * See https://github.com/manna-harbour/miryoku for the original layout.
+ */
+
+/**
+ * \brief Navigation layer.
+ *
+ * Primary left-hand layer (left home thumb) is navigation and editing. Cursor
+ * keys are on the home position, line and page movement below, clipboard
+ * above, caps lock and insert on the inner column. Thumb keys are duplicated
+ * from the base layer to avoid having to layer change mid edit and to enable
+ * auto-repeat.
+*/
+#define LAYOUT_LAYER_NAVIGATION                                                               \
+    _______________DEAD_HALF_ROW_______________, KC_AGAIN,LCTL(KC_V), LCTL(KC_C),  KC_CUT, KC_UNDO, \
+    ______________HOME_ROW_GACS_L______________, KC_CAPS, KC_LEFT,    KC_DOWN,   KC_UP, KC_RGHT, \
+    _______________DEAD_HALF_ROW_______________,  KC_INS, KC_HOME,    KC_PGDN, KC_PGUP,  KC_END, \
+                      XXXXXXX, _______, XXXXXXX,  KC_ENT, KC_BSPC,    KC_DEL
+
+/**
+ * \brief Mouse layer
+ *
+ * Secondary left-hand layer is mouse emulation. Mouse movement mirrors cursor
+ * navigation on home and wheel mirrors line / page movement below. Mouse
+ * buttons are on the thumbs. Left, right, and middle mouse buttons are on the
+ * primary, secondary, and tertiary thumb keys, respectively. Mouse movement,
+ * click, and drag, with modifiers, can be performed from the home position.
+ * Clipboard keys are duplicated from the Nav layer.
+*/
+#define LAYOUT_LAYER_MOUSE                                                                    \
+    _______________DEAD_HALF_ROW_______________, KC_AGAIN,KC_PSTE, KC_COPY,  KC_CUT, KC_UNDO, \
+    ______________HOME_ROW_GACS_L______________, _______, KC_MS_L, KC_MS_D, KC_MS_U, KC_MS_R, \
+    _______________DEAD_HALF_ROW_______________, _______, KC_WH_L, KC_WH_D, KC_WH_U, KC_WH_R, \
+                      XXXXXXX, XXXXXXX, _______, KC_BTN2, KC_BTN1, KC_BTN3
+
+/**
+ * \brief Media layer
+ *
+ * Tertiary left-hand layer is media control, with volume up / volume down and
+ * next / prev mirroring the navigation keys. Pause, stop and mute are on the
+ * primary, secondary, and tertiary thumbs, respectively.
+ *
+ * Keyboard hardware controls are also present, and depend on hardware and
+ * firmware support.
+ *
+ * RGB control is on the top row. RGB Toggle is on the inner index column key.
+ * Combine with Shift for RGB Off. RGB Mode, RGB Hue, RGB Saturation, and RGB
+ * Value are on index, middle, ring, and pinkie column keys, respectively.
+ * Tapping will increase the corresponding value. Combine with Shift to
+ * decrease.
+*/
+#define LAYOUT_LAYER_MEDIA                                                                    \
+    _______________DEAD_HALF_ROW_______________, RGB_TOG, RGB_MOD, RGB_HUI, RGB_SAI, RGB_VAI, \
+    ______________HOME_ROW_GACS_L______________, _______, KC_MPRV, KC_VOLD, KC_VOLU, KC_MNXT, \
+    _______________DEAD_HALF_ROW_______________, _______, _______, _______, _______, _______, \
+                      _______, XXXXXXX, XXXXXXX, KC_MSTP, KC_MPLY, KC_MUTE
+
+/**
+ * \brief Numeral layout.
+ *
+ * Primary right-hand layer (right home thumb) is numerals and symbols. Numerals
+ * are in the standard numpad locations with symbols in the remaining positions.
+ */
+#define LAYOUT_LAYER_NUMERAL                                                                  \
+    KC_LBRC,    KC_7,    KC_8,    KC_9, KC_RBRC, _______________DEAD_HALF_ROW_______________, \
+    KC_SCLN,    KC_4,    KC_5,    KC_6,  KC_EQL, ______________HOME_ROW_GACS_R______________, \
+    KC_GRAVE,   KC_1,    KC_2,    KC_3, KC_BSLS, _______________DEAD_HALF_ROW_______________, \
+                       KC_DOT, KC_0, KC_MINS, XXXXXXX, _______, XXXXXXX
+
+/**
+ * \brief Symbols layer.
+ *
+ * Secondary right-hand layer has shifted symbols in the same locations to reduce
+ * chording when using mods with shifted symbols. `KC_LPRN` is duplicated next to
+ * `KC_RPRN`.
+ */
+#define LAYOUT_LAYER_SYMBOLS                                                                  \
+    KC_LCBR, KC_AMPR, KC_ASTR, KC_LPRN, KC_RCBR, _______________DEAD_HALF_ROW_______________, \
+    KC_COLN,  KC_DLR, KC_PERC, KC_CIRC, KC_PLUS, ______________HOME_ROW_GACS_R______________, \
+    KC_TILD, KC_EXLM,   KC_AT, KC_HASH, KC_PIPE, _______________DEAD_HALF_ROW_______________, \
+                      KC_LPRN, KC_RPRN, KC_UNDS, _______, XXXXXXX, XXXXXXX
+
+/**
+ * \brief Function layer.
+ *
+ * Tertiary right-hand layer has function keys mirroring the numerals on the
+ * primary layer with system keys on the inner column. App is on the tertiary
+ * thumb key and other thumb keys are duplicated from the base layer to enable
+ * auto-repeat.
+ */
+#define LAYOUT_LAYER_FUNCTION                                                                 \
+     KC_F12,   KC_F7,   KC_F8,   KC_F9, KC_PSCR, _______________DEAD_HALF_ROW_______________, \
+     KC_F11,   KC_F4,   KC_F5,   KC_F6, KC_SCRL, ______________HOME_ROW_GACS_R______________, \
+     KC_F10,   KC_F1,   KC_F2,   KC_F3, KC_PAUS, _______________DEAD_HALF_ROW_______________, \
+                      KC_APP, KC_SPC, KC_TAB, XXXXXXX, XXXXXXX, _______
+
+/**
+ * \brief Add Home Row mod to a layout.
+ *
+ * Expects a 10-key per row layout.  Adds support for GACS (Gui, Alt, Ctl, Shift)
+ * home row.  The layout passed in parameter must contain at least 20 keycodes.
+ *
+ * This is meant to be used with `LAYER_BASE` defined above, eg.:
+ *
+ *     HOME_ROW_MOD_GACS(LAYER_BASE)
+ */
+#define _HOME_ROW_MOD_GACS(                                            \
+    L00, L01, L02, L03, L04, R05, R06, R07, R08, R09,                  \
+    L10, L11, L12, L13, L14, R15, R16, R17, R18, R19,                  \
+    ...)                                                               \
+             L00,         L01,         L02,         L03,         L04,  \
+             R05,         R06,         R07,         R08,         R09,  \
+      LGUI_T(L10), LALT_T(L11), LCTL_T(L12), LSFT_T(L13),        L14,  \
+             R15,  RSFT_T(R16), RCTL_T(R17), RALT_T(R18), RGUI_T(R19), \
+      __VA_ARGS__
+#define HOME_ROW_MOD_GACS(...) _HOME_ROW_MOD_GACS(__VA_ARGS__)
+
+
+#define LAYOUT_wrapper(...) LAYOUT_split_3x5_3(__VA_ARGS__)
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+  [LAYER_BASE] = LAYOUT_wrapper(
+    HOME_ROW_MOD_GACS(LAYOUT_LAYER_BASE)
+  ),
+  [LAYER_NAVIGATION] = LAYOUT_wrapper(LAYOUT_LAYER_NAVIGATION),
+  [LAYER_MOUSE] = LAYOUT_wrapper(LAYOUT_LAYER_MOUSE),
+  [LAYER_MEDIA] = LAYOUT_wrapper(LAYOUT_LAYER_MEDIA),
+  [LAYER_NUMERAL] = LAYOUT_wrapper(LAYOUT_LAYER_NUMERAL),
+  [LAYER_SYMBOLS] = LAYOUT_wrapper(LAYOUT_LAYER_SYMBOLS),
+  [LAYER_FUNCTION] = LAYOUT_wrapper(LAYOUT_LAYER_FUNCTION),
+};
+// clang-format on
+
+#ifdef POINTING_DEVICE_ENABLE
+#    ifdef DILEMMA_AUTO_SNIPING_ON_LAYER
+layer_state_t layer_state_set_user(layer_state_t state) {
+    dilemma_set_pointer_sniping_enabled(layer_state_cmp(state, DILEMMA_AUTO_SNIPING_ON_LAYER));
+    return state;
+}
+#    endif // DILEMMA_AUTO_SNIPING_ON_LAYER
+#endif     // POINTING_DEVICE_ENABLE
+
+#ifdef ENCODER_MAP_ENABLE
+// clang-format off
+const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
+    [LAYER_BASE]       = {ENCODER_CCW_CW(KC_WH_D, KC_WH_U),  ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
+    [LAYER_NAVIGATION] = {ENCODER_CCW_CW(KC_PGDN, KC_PGUP),  ENCODER_CCW_CW(KC_VOLU, KC_VOLD)},
+    [LAYER_MOUSE]      = {ENCODER_CCW_CW(RGB_HUD, RGB_HUI),  ENCODER_CCW_CW(RGB_SAD, RGB_SAI)},
+    [LAYER_MEDIA]      = {ENCODER_CCW_CW(KC_PGDN, KC_PGUP),  ENCODER_CCW_CW(KC_VOLU, KC_VOLD)},
+    [LAYER_NUMERAL]    = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI),  ENCODER_CCW_CW(RGB_SPD, RGB_SPI)},
+    [LAYER_SYMBOLS]    = {ENCODER_CCW_CW(RGB_RMOD, RGB_MOD), ENCODER_CCW_CW(KC_LEFT, KC_RGHT)},
+    [LAYER_FUNCTION]   = {ENCODER_CCW_CW(KC_DOWN, KC_UP),    ENCODER_CCW_CW(KC_LEFT, KC_RGHT)},
+};
+// clang-format on
+#endif // ENCODER_MAP_ENABLE
diff --git a/users/flokli/keyboards/dilemma/rules.mk b/users/flokli/keyboards/dilemma/rules.mk
new file mode 100644
index 0000000000..5a090013dc
--- /dev/null
+++ b/users/flokli/keyboards/dilemma/rules.mk
@@ -0,0 +1,2 @@
+ENCODER_MAP_ENABLE = yes
+OPT_DEFS += -DMK_KINETIC_SPEED=1
diff --git a/users/flokli/keyboards/k6_pro/default.nix b/users/flokli/keyboards/k6_pro/default.nix
new file mode 100644
index 0000000000..708bec7313
--- /dev/null
+++ b/users/flokli/keyboards/k6_pro/default.nix
@@ -0,0 +1,39 @@
+{ depot, pkgs, ... }:
+
+rec {
+  firmware = pkgs.stdenv.mkDerivation {
+    name = "keychron-k6_pro-firmware";
+
+    src = pkgs.fetchFromGitHub {
+      owner = "Keychron"; # the Keychron fork of qmk/qmk_firmware
+      repo = "qmk_firmware";
+      rev = "e0a48783e7cde92d1edfc53a8fff511c45e869d4"; # bluetooth_playground branch
+      hash = "sha256-Pk9kXktmej9JyvSt7UMEW2FDrBg7k1lOssh6HjrP5ro=";
+      fetchSubmodules = true;
+    };
+
+    nativeBuildInputs = [
+      pkgs.qmk
+    ];
+
+    buildPhase = ''
+      mkdir -p keyboards/keychron/k6_pro/ansi/rgb/keymaps/flokli
+      cp ${./keymap.c} keyboards/keychron/k6_pro/ansi/rgb/keymaps/flokli/keymap.c
+      cp ${./rules.mk} keyboards/keychron/k6_pro/ansi/rgb/keymaps/flokli/rules.mk
+
+      make keychron/k6_pro/ansi/rgb:flokli
+    '';
+
+    installPhase = ''
+      mkdir -p $out
+
+      cp keychron_k6_pro_ansi_rgb_flokli.bin $out/
+    '';
+  };
+
+  flash = pkgs.writeShellScript "flash.sh" ''
+    ${pkgs.qmk}/bin/qmk flash ${firmware}/keychron_k6_pro_ansi_rgb_flokli.bin
+  '';
+
+  meta.ci.targets = [ "firmware" ];
+}
diff --git a/users/flokli/keyboards/k6_pro/keymap.c b/users/flokli/keyboards/k6_pro/keymap.c
new file mode 100644
index 0000000000..1aa406eeac
--- /dev/null
+++ b/users/flokli/keyboards/k6_pro/keymap.c
@@ -0,0 +1,76 @@
+/* Copyright 2021 @ Keychron (https://www.keychron.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include QMK_KEYBOARD_H
+
+// clang-format off
+enum layers{
+  MAC_BASE,
+  WIN_BASE,
+  MAC_FN1,
+  WIN_FN1,
+  FN2
+};
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+[MAC_BASE] = LAYOUT_ansi_68(
+     KC_ESC,   KC_1,     KC_2,     KC_3,     KC_4,     KC_5,     KC_6,     KC_7,     KC_8,     KC_9,     KC_0,     KC_MINS,  KC_EQL,   KC_BSPC, KC_DEL,
+     KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_LBRC,  KC_RBRC,  KC_BSLS, KC_HOME,
+     KC_CAPS,  KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_QUOT,            KC_ENT,  KC_PGUP,
+     KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,   KC_SLSH,  KC_RSFT,  KC_UP,    KC_PGDN,
+     KC_LCTL,  KC_LOPTN, KC_LCMMD,                               KC_SPC,                       KC_RCMMD,MO(MAC_FN1),MO(FN2), KC_LEFT,  KC_DOWN, KC_RGHT),
+
+[WIN_BASE] = LAYOUT_ansi_68(
+     KC_ESC,   KC_1,     KC_2,     KC_3,     KC_4,     KC_5,     KC_6,     KC_7,     KC_8,     KC_9,     KC_0,     KC_MINS,  KC_EQL,   KC_BSPC, KC_DEL,
+     KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_LBRC,  KC_RBRC,  KC_BSLS, KC_HOME,
+     KC_CAPS,  KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_QUOT,            KC_ENT,  KC_PGUP,
+     KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,   KC_SLSH,  KC_RSFT,            KC_UP,   KC_PGDN,
+     KC_LCTL,  KC_LGUI,  KC_LALT,                                KC_SPC,                       KC_RALT, MO(WIN_FN1),MO(FN2), KC_LEFT,  KC_DOWN, KC_RGHT),
+
+[MAC_FN1] = LAYOUT_ansi_68(
+     KC_GRV,   KC_BRID,  KC_BRIU,  KC_MCTL,  KC_LPAD,  RGB_VAD,  RGB_VAI,  KC_MPRV,  KC_MPLY,  KC_MNXT,  KC_MUTE,  KC_VOLD,  KC_VOLU,  _______,  RGB_TOG,
+     _______,  BT_HST1,  BT_HST2,  BT_HST3,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     RGB_TOG,  RGB_MOD,  RGB_VAI,  RGB_HUI,  RGB_SAI,  RGB_SPI,  _______,  _______,  _______,  _______,  _______,  _______,            _______,  _______,
+     _______,  RGB_RMOD, RGB_VAD,  RGB_HUD,  RGB_SAD,  RGB_SPD,  NK_TOGG,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     _______,  _______,  _______,                                _______,                      _______,  _______,  _______,  _______,  _______,  _______),
+
+[WIN_FN1] = LAYOUT_ansi_68(
+//                                           mic mute                      webcam    wifi
+     KC_GRV,   KC_MUTE,  KC_VOLD,  KC_VOLU,  _______,  KC_BRID,  KC_BRIU,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  RGB_TOG,
+     _______,  BT_HST1,  BT_HST2,  BT_HST3,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     RGB_TOG,  RGB_MOD,  RGB_VAI,  RGB_HUI,  RGB_SAI,  RGB_SPI,  _______,  _______,  _______,  _______,  _______,  _______,            _______,  _______,
+     _______,  RGB_RMOD, RGB_VAD,  RGB_HUD,  RGB_SAD,  RGB_SPD,  NK_TOGG,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     _______,  _______,  _______,                                _______,                      KC_PSCR,  _______,  _______,  _______,  _______,  _______),
+
+[FN2] = LAYOUT_ansi_68(
+     KC_TILD,  KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_F9,    KC_F10,   KC_F11,   KC_F12,   _______,  KC_SLEP,
+     _______,  KC_BTN1,  KC_MS_U,  KC_BTN2,  KC_WH_U,  KC_VOLU,  KC_MUTE,  KC_MPLY,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     _______,  KC_MS_L,  KC_MS_D,  KC_MS_R,  KC_WH_D,  KC_VOLD,  KC_MPRV,  KC_MNXT,  _______,  _______,  _______,  _______,            _______,  _______,
+     _______,  _______,  _______,  _______,  _______,  BAT_LVL,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     _______,  _______,  _______,                                _______,                      _______,  _______,  _______,  _______,  _______,  _______),
+};
+
+// Shift+Del -> middle mouse button
+const key_override_t insert_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_DEL, KC_BTN3);
+// Shift+Home -> End
+const key_override_t end_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_HOME, KC_END);
+
+// This globally defines all key overrides to be used
+const key_override_t **key_overrides = (const key_override_t *[]) {
+     &insert_key_override,
+     &end_key_override,
+     NULL // end of array
+};
diff --git a/users/flokli/keyboards/k6_pro/rules.mk b/users/flokli/keyboards/k6_pro/rules.mk
new file mode 100644
index 0000000000..35725756d4
--- /dev/null
+++ b/users/flokli/keyboards/k6_pro/rules.mk
@@ -0,0 +1,2 @@
+KEY_OVERRIDE_ENABLE = yes
+OPT_DEFS += -DDYNAMIC_KEYMAP_LAYER_COUNT=5 -DMK_KINETIC_SPEED=1
diff --git a/users/flokli/nixos/.envrc b/users/flokli/nixos/.envrc
new file mode 100644
index 0000000000..ccf3cb847a
--- /dev/null
+++ b/users/flokli/nixos/.envrc
@@ -0,0 +1 @@
+PATH_add $(nix-build ../../.. -A users.flokli.nixos.deps --no-out-link)/bin
diff --git a/users/flokli/nixos/.skip-subtree b/users/flokli/nixos/.skip-subtree
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/users/flokli/nixos/.skip-subtree
diff --git a/users/flokli/nixos/archeology-ec2/OWNERS b/users/flokli/nixos/archeology-ec2/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/users/flokli/nixos/archeology-ec2/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/users/flokli/nixos/archeology-ec2/configuration.nix b/users/flokli/nixos/archeology-ec2/configuration.nix
new file mode 100644
index 0000000000..f0fc0c5d09
--- /dev/null
+++ b/users/flokli/nixos/archeology-ec2/configuration.nix
@@ -0,0 +1,35 @@
+{ depot, pkgs, modulesPath, ... }:
+
+{
+  imports = [
+    "${modulesPath}/virtualisation/amazon-image.nix"
+    ../profiles/archeology.nix
+  ];
+
+  systemd.timers.parse-bucket-logs = {
+    wantedBy = [ "multi-user.target" ];
+    timerConfig.OnCalendar = "*-*-* 03:00:00 UTC";
+  };
+
+  systemd.services.parse-bucket-logs = {
+    path = [ depot.users.flokli.archeology.parse-bucket-logs ];
+    serviceConfig = {
+      Type = "oneshot";
+      ExecStart = (pkgs.writers.writePython3 "parse-bucket-logs-continuously"
+        {
+          libraries = [ pkgs.python3Packages.boto3 ];
+        } ./parse-bucket-logs-continuously.py);
+      DynamicUser = "yes";
+      StateDirectory = "parse-bucket-logs";
+    };
+  };
+
+  environment.systemPackages = [
+    depot.users.flokli.archeology.parse-bucket-logs
+  ];
+
+  networking.hostName = "archeology-ec2";
+
+  system.stateVersion = "23.05"; # Did you read the comment?
+}
+
diff --git a/users/flokli/nixos/archeology-ec2/hardware-configuration.nix b/users/flokli/nixos/archeology-ec2/hardware-configuration.nix
new file mode 100644
index 0000000000..7b3d79d70a
--- /dev/null
+++ b/users/flokli/nixos/archeology-ec2/hardware-configuration.nix
@@ -0,0 +1,36 @@
+{ lib, modulesPath, ... }:
+
+{
+  imports =
+    [
+      (modulesPath + "/profiles/qemu-guest.nix")
+    ];
+
+  boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "sr_mod" "virtio_blk" ];
+  boot.initrd.kernelModules = [ ];
+  boot.kernelModules = [ "kvm-amd" ];
+  boot.extraModulePackages = [ ];
+
+  fileSystems."/" =
+    {
+      device = "/dev/disk/by-partlabel/root";
+      fsType = "xfs";
+    };
+
+  fileSystems."/boot" =
+    {
+      device = "/dev/disk/by-partlabel/boot";
+      fsType = "vfat";
+    };
+
+  swapDevices = [ ];
+
+  # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+  # (the default) this is the recommended approach. When using systemd-networkd it's
+  # still possible to use this option, but it's recommended to use it in conjunction
+  # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+  networking.useDHCP = lib.mkDefault true;
+  # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true;
+
+  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+}
diff --git a/users/flokli/nixos/archeology-ec2/parse-bucket-logs-continuously.py b/users/flokli/nixos/archeology-ec2/parse-bucket-logs-continuously.py
new file mode 100644
index 0000000000..f6ec8fb77c
--- /dev/null
+++ b/users/flokli/nixos/archeology-ec2/parse-bucket-logs-continuously.py
@@ -0,0 +1,62 @@
+import boto3
+import datetime
+import os
+import re
+import subprocess
+import tempfile
+
+s3 = boto3.resource('s3')
+bucket_name = "nix-archeologist"
+prefix = "nix-cache-bucket-logs/"
+
+bucket = s3.Bucket(bucket_name)
+
+key_pattern = re.compile(r'.*\/(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})\.parquet$')  # noqa: E501
+
+# get a listing (which is sorted), grab the most recent key
+last_elem = list(
+    o for o in bucket.objects.filter(Prefix=prefix)
+    if key_pattern.match(o.key)
+).pop()
+
+# extract the date of that key.
+m = key_pattern.search(last_elem.key)
+last_elem_date = datetime.date(int(m.group("y")), int(m.group("m")), int(m.group("d")))  # noqa: E501
+
+# get the current date (UTC)
+now = datetime.datetime.now(tz=datetime.UTC)
+now_date = datetime.date(now.year, now.month, now.day)
+
+while True:
+    # Calculate what date would be processed next.
+    next_elem_date = last_elem_date + datetime.timedelta(days=1)
+
+    # If that's today, we don't want to process it.
+    if next_elem_date == now_date:
+        print("Caught up, would process data from today.")
+        break
+
+    # If we'd be processing data from yesterday, but it's right after midnight,
+    # also don't process - data might still be flushed.
+    if (next_elem_date + datetime.timedelta(days=1) == now_date) and now.hour == 0:  # noqa: E501
+        print("Not processing data from previous day right after midnight")
+        break
+
+    src = f"http://nix-cache-log.s3.amazonaws.com/log/{next_elem_date.isoformat()}-*"  # noqa: E501
+
+    # Invoke parse-bucket-logs script inside a tempdir and upload on success.
+    with tempfile.TemporaryDirectory() as td:
+        work_file_name = os.path.join(td, "output.parquet")
+        args = ["archeology-parse-bucket-logs", src, work_file_name]
+        subprocess.run(
+            args,
+            check=True  # throw exception if nonzero exit code
+        )
+
+        dest_key = f"{prefix}{next_elem_date.isoformat()}.parquet"
+
+        # Upload the file
+        print(f"uploading to s3://{bucket_name}{dest_key}")
+        bucket.upload_file(work_file_name, dest_key)
+
+    last_elem_date = next_elem_date
diff --git a/users/flokli/nixos/default.nix b/users/flokli/nixos/default.nix
new file mode 100644
index 0000000000..9ed223a908
--- /dev/null
+++ b/users/flokli/nixos/default.nix
@@ -0,0 +1,32 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
+
+  # assumes `name` is configured appropriately in your .ssh/config
+  deployScript = name: sys: pkgs.writeShellScriptBin "deploy-${name}" ''
+    set -eo pipefail
+    nix-copy-closure --to ${name} --gzip --use-substitutes ${sys}
+    ssh ${name} nix-env --profile /nix/var/nix/profiles/system --set ${sys}
+    ssh ${name} ${sys}/bin/switch-to-configuration switch
+  '';
+
+in
+depot.nix.readTree.drvTargets rec {
+  archeologyEc2System = (depot.ops.nixos.nixosFor ({ ... }: {
+    imports = [
+      ./archeology-ec2/configuration.nix
+    ];
+  })).config.system.build.toplevel;
+
+  deploy-archeology-ec2 = (deployScript "archeology-ec2" archeologyEc2System);
+
+  deps = (depot.nix.lazy-deps {
+    deploy-archeology-ec2.attr = "users.flokli.nixos.deploy-archeology-ec2";
+  });
+
+  shell = pkgs.mkShell {
+    name = "flokli-nixos-shell";
+    packages = [ deps ];
+  };
+}
diff --git a/users/flokli/nixos/profiles/archeology.nix b/users/flokli/nixos/profiles/archeology.nix
new file mode 100644
index 0000000000..c87d6bcf30
--- /dev/null
+++ b/users/flokli/nixos/profiles/archeology.nix
@@ -0,0 +1,37 @@
+# Set of unconditional config options applicable to all archeology machines.
+
+{ depot, pkgs, ... }:
+
+{
+  # Use the TVL binary cache
+  tvl.cache.enable = true;
+
+  # Start clickhose as a system service.
+  services.clickhouse.enable = true;
+
+  # for ClickHouse
+  # We're keeping this here rather than in the NixOS module, because I suspect
+  # this opens up timing side channels. This is a single-user, single-purpose
+  # machine, so that isn't a concern here.
+  boot.kernel.sysctl."kernel.task_delayacct" = 1;
+
+  # Enable SSH and let edef and flokli in
+  services.openssh.enable = true;
+
+  users.users.root.openssh.authorizedKeys.keys = [
+    "cert-authority ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCvb/7ojfcbKvHIyjnrNUOOgzy44tCkgXY9HLuyFta1jQOE9pFIK19B4dR9bOglPKf145CCL0mSFJNNqmNwwavU2uRn+TQrW+U1dQAk8Gt+gh3O49YE854hwwyMU+xD6bIuUdfxPr+r5al/Ov5Km28ZMlHOs3FoAP0hInK+eAibioxL5rVJOtgicrOVCkGoXEgnuG+LRbOYTwzdClhRUxiPjK8alCbcJQ53AeZHO4G6w9wTr+W5ILCfvW4OmUXCX01sKzaBiQuuFCF6M/H4LlnsPWLMra2twXxkOIhZblwC+lncps9lQaUgiD4koZeOCORvHW00G0L39ilFbbnVcL6Itp/m8RRWm/xRxS4RMnsdV/AhvpRLrhL3lfQ7E2oCeSM36v1S9rdg6a47zcnpL+ahG76Gz39Y7KmVRQciNx7ezbwxj3Q5lZtFykgdfGIAN+bT8ijXMO6m68g60i9Bz4IoMZGkiJGqMYLTxMQ+oRgR3Ro5lbj7E11YBHyeimoBYXYGHMkiuxopQZ7lIj3plxIzhmUlXJBA4jMw9KGHdYaLhaicIYhvQmCTAjrkt2HvxEe6lU8iws2Qv+pB6tAGundN36RVVWAckeQPZ4ZsgDP8V2FfibZ1nsrQ+zBKqaslYMAHs01Cf0Hm0PnCqagf230xaobu0iooNuXx44QKoDnB+w== edef"
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTVTXOutUZZjXLB0lUSgeKcSY/8mxKkC0ingGK1whD2 flokli"
+  ];
+
+  # Get a bunch of text editors and CLI tools.
+  environment.systemPackages = [
+    pkgs.awscli
+    pkgs.duckdb
+    pkgs.parquet-tools
+    pkgs.helix
+    pkgs.htop
+    pkgs.kakoune
+    pkgs.kitty.terminfo
+    pkgs.tmux
+  ];
+}
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/.gitignore b/users/flokli/presentations/2023-09-09-nixcon-tvix/.gitignore
new file mode 100644
index 0000000000..397b4a7624
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/.gitignore
@@ -0,0 +1 @@
+*.log
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/architecture.dot b/users/flokli/presentations/2023-09-09-nixcon-tvix/architecture.dot
new file mode 100644
index 0000000000..a6ea0460ef
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/architecture.dot
@@ -0,0 +1,5 @@
+digraph {
+    Builder
+    Store
+    Evaluator
+}
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/cppnix-example-lexer.cpp b/users/flokli/presentations/2023-09-09-nixcon-tvix/cppnix-example-lexer.cpp
new file mode 100644
index 0000000000..7c52bce8b6
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/cppnix-example-lexer.cpp
@@ -0,0 +1,13 @@
+attrpath
+  : attrpath '.' attr {
+    $$ = $1; $1->push_back(AttrName(data->symbols.create($3)));
+  }
+  | attrpath '.' string_attr
+    { $$ = $1;
+      ExprString * str = dynamic_cast<ExprString *>($3);
+      if (str) {
+          $$->push_back(AttrName(data->symbols.create(str->s)));
+          delete str;
+      } else
+          $$->push_back(AttrName($3));
+    }
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/crate-deps.dot b/users/flokli/presentations/2023-09-09-nixcon-tvix/crate-deps.dot
new file mode 100644
index 0000000000..66ead74b1e
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/crate-deps.dot
@@ -0,0 +1,19 @@
+digraph {
+    bgcolor="transparent"
+    node [fillcolor="lightgrey",style="filled"]
+
+    tvix_cli
+    tvix_eval
+    nix_compat
+    tvix_serde
+    tvix_store
+
+    tvix_cli -> tvix_store
+    tvix_cli -> nix_compat
+    tvix_cli -> tvix_eval
+
+    tvix_store -> nix_compat
+    tvix_eval -> nix_compat
+
+    tvix_serde -> tvix_eval
+}
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/default.nix b/users/flokli/presentations/2023-09-09-nixcon-tvix/default.nix
new file mode 100644
index 0000000000..1ec0a0bd0e
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/default.nix
@@ -0,0 +1,37 @@
+{ depot, pkgs, ... }:
+
+let
+  inherit (pkgs)
+    fontconfig qrencode runCommand stdenv;
+  mkQr = url: runCommand "qrcode.png" { } ''
+    ${qrencode}/bin/qrencode -o $out -t SVG -s 5 \
+      --background=fafafa \
+      --foreground=000000 \
+      ${url}
+  '';
+in
+stdenv.mkDerivation {
+  name = "2023-nixcon-tvix";
+  src = ./.;
+
+  FONTCONFIG_FILE = pkgs.makeFontsConf {
+    fontDirectories = with pkgs; [ jetbrains-mono fira fira-code fira-mono lato ];
+  };
+
+  PUPPETEER_EXECUTABLE_PATH = "${pkgs.chromium}/bin/chromium";
+  PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "1";
+
+  nativeBuildInputs = [ pkgs.reveal-md pkgs.graphviz ];
+
+  buildPhase = ''
+    cp ${depot.tvix.logo}/logo.png tvix-logo.png
+    dot -Tsvg crate-deps.dot > crate-deps.svg
+    cp ${mkQr "https://flokli.de"} qrcode-flokli.svg
+    cp ${mkQr "https://tvix.dev"} qrcode-tvix.svg
+
+    mkdir -p $out
+    reveal-md --static $out presentation.md
+    reveal-md --print $out/slides.pdf presentation.md
+    cp tvixbolt.webm $out
+  '';
+}
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/presentation.md b/users/flokli/presentations/2023-09-09-nixcon-tvix/presentation.md
new file mode 100644
index 0000000000..b37554e188
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/presentation.md
@@ -0,0 +1,294 @@
+---
+author:
+- Florian Klink
+date: 2023-09-09
+title: "Tvix: Status update"
+theme: moon
+revealOptions:
+  transition: 'fade'
+---
+
+# Tvix: Status update
+
+![Tvix Logo](tvix-logo.png)
+
+2023-09-09
+
+Florian Klink
+
+---
+
+## Whoami
+
+- flokli
+- nixpkgs contributor since 2018, maintaining systemd, nsncd and some
+  more stuff
+- Freelance Nix/DevOps consultant
+- I spend too much time on computers :-)
+
+---
+
+## What is Tvix?
+
+- A new implementation of Nix
+- modular
+- written in Rust
+- developed in the [TVL](https://tvl.fyi) monorepo
+- subtree exported to [github:tvlfyi/tvix](https://github.com/tvlfyi/tvix)
+
+---
+
+## Structure
+
+- strong separation between **Evaluator**, **Store** and **Builder**
+- Defined interfaces between different components (Protobuf/gRPC) <!-- .element: class="fragment" -->
+  - Allows adding to/combining with your own components <!-- .element: class="fragment" -->
+- <!-- .element: class="fragment" --> A lot of helper code for some of the Nix internals in the `nix-compat` crate
+
+Note: Derivation types, serializers. NAR writers, nixbase32 enc/dec, Nix Hash function, stringparsing etc.
+
+----
+
+![crate-deps.svg](crate-deps.svg)
+
+---
+
+## Evaluator: Design
+
+- <!-- .element: class="fragment" --> 
+  Nix code is parsed via [rnix](https://github.com/nix-community/rnix-parser)
+- <!-- .element: class="fragment" -->
+  AST traversal, generate bytecode (with some transformations)
+- <!-- .element: class="fragment" -->
+  Bytecode is executed by an abstract machine with a Nix-specific instruction set
+
+----
+
+## Evaluator: Design
+
+- <!-- .element: class="fragment" -->
+  Builtins are separate from the "evaluator core"
+  - <!-- .element: class="fragment" -->
+    inject your own builtins
+  - <!-- .element: class="fragment" -->
+    this includes `builtins.derivation`!
+- <!-- .element: class="fragment" -->
+  IO is nicely abstracted away
+  - <!-- .element: class="fragment" -->
+    We can run a Nixlang subset without IO in wasm (see [tvixbolt](https://bolt.tvix.dev/)),
+    or parse Nix into a config struct with `tvix-serde`
+
+----
+
+<!-- <video class="r-stretch" src="./tvixbolt.webm"></video> -->
+<a href="./tvixbolt.webm">Tvixbolt Demo</a>
+
+----
+
+### Evaluator: Current Work
+
+- <!-- .element: class="fragment" -->
+  Current goal: **evaluate nixpkgs the same way as Nix does**
+- <!-- .element: class="fragment" -->
+  Checked by comparing the calculated output paths, which checks correctness of all \"parent\" output paths too.
+- <!-- .element: class="fragment" -->
+  Required implementing a lot of Nix internals in `nix-compat`, and `tvix-store` (A-Term, hash modulo, NAR writer/hasher)
+
+Note: Getting output hashing correct, and exposing this in a re-usable fashion took quite some iterations to get right.
+
+----
+
+### Evaluator: Current Work (cont.)
+- <!-- .element: class="fragment" -->
+  πŸŽ‰ Already correct for (and continously checked by CI on every commit):
+  - <!-- .element: class="fragment" -->
+  `stdenv`, `hello`
+  - <!-- .element: class="fragment" -->
+  `pkgsCross.aarch64-multiplatform.stdenv`, `pkgsCross.aarch64-multiplatform.hello`
+- <!-- .element: class="fragment" -->
+  Some work left for more complicated expressions
+  - <!-- .element: class="fragment" -->
+    infinite recursion [when inheriting from a `builtins.tryEval` multiple times](https://b.tvl.fyi/281)
+  - <!-- .element: class="fragment" -->
+    small details around file imports
+- <!-- .element: class="fragment" -->
+  Not too much performance finetuning until we're correct first.
+
+----
+
+### Evaluator: Demo
+
+[![asciicast](https://asciinema.org/a/MH4tuVPLsKewJSGJUYEyIKUpj.svg)](https://asciinema.org/a/MH4tuVPLsKewJSGJUYEyIKUpj)
+
+---
+
+## Store: Design
+
+- <!-- .element: class="fragment" -->
+  Uses a very different underlying data model:
+  - <!-- .element: class="fragment" -->
+    Nix stores on a per- `StorePath` granularity
+  - <!-- .element: class="fragment" -->
+    tvix-store uses a Merkle DAG of directories, similar to git trees, but with [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) digests as pointers.
+  - <!-- .element: class="fragment" -->
+    Compat layer in front to still render/calculate NAR on demand where needed
+  - <!-- .element: class="fragment" -->
+    Substitution, caching, ... possible to describe via composition/layering
+
+----
+
+![tvix-store graph](tvix-store-graph.svg)
+
+----
+
+### Store: Advantages
+
+- <!-- .element: class="fragment" -->
+  Less downloading of data that didn't change
+- <!-- .element: class="fragment" -->
+  Less duplicate data on disk/storage
+- <!-- .element: class="fragment" -->
+  Inherently content-addressed, so P2P substitution possible
+- <!-- .element: class="fragment" -->
+  Allows doing verified blob streaming ([BAO](https://github.com/oconnor663/bao), [bao-tree](https://github.com/n0-computer/bao-tree))
+- <!-- .element: class="fragment" -->
+  Protocol has some \"smarter\" methods to avoid roundtrips, but could all be statically pre-rendered
+- <!-- .element: class="fragment" -->
+  Very little data that needs to be fetched from a trusted party (or be signed)
+
+Note: Our way of addressing blobs by their raw blake3 digest is natively compatible with iroh, the IPFS Re-implementation in Rust
+
+----
+
+### Store: Status
+
+- <!-- .element: class="fragment" -->
+  Whole Merkle-based store implementation (and Nix NAR compat layer) is there
+  - <!-- .element: class="fragment" -->
+    exercised by the output path CI tests, and a test suite.
+  - <!-- .element: class="fragment" -->
+    three backends (Sled, in-memory, gRPC client)
+  - <!-- .element: class="fragment" -->
+    more backends and more test suites planned.
+- <!-- .element: class="fragment" -->
+  FUSE filesystem to expose the store (to Tvix Builders, \"appliances\") <!-- .element: class="fragment" -->
+
+Note: backends: RocksDB, sqlite, s3. fuse: lazy fetching of build input files | think about a minimal initrd to bring up network and mount the store, then run any closure.
+
+----
+
+### Store: Demo
+
+[![asciicast](https://asciinema.org/a/YFB9yycHdx0OUH9N0WdAkIYua.svg)](https://asciinema.org/a/YFB9yycHdx0OUH9N0WdAkIYua)
+
+----
+
+### Store: Status (cont.)
+- <!-- .element: class="fragment" -->
+  More work on store composition needed (necessary for substition and caching)
+- <!-- .element: class="fragment" -->
+  More work on more granular blob substititon needed.
+- <!-- .element: class="fragment" -->
+  More work on bridges with Nix needed
+  - <!-- .element: class="fragment" -->
+    Get Nix to talk to a tvix-store
+  - <!-- .element: class="fragment" -->
+    Expose existing binary caches to tvix-store
+
+---
+
+### Builder: Design
+
+- <!-- .element: class="fragment" -->
+  Build requests/Build protocol is less Nix-specific
+  - <!-- .element: class="fragment" -->
+    allows reusing builders for other usages (non-sandboxed builds, other build systems, playing with other addressing mechanisms)
+- <!-- .element: class="fragment" -->
+  Distinction between a **specific build attempt** and the **general build recipe**
+  - <!-- .element: class="fragment" -->
+    allows keeping metadata about failed builds
+  - <!-- .element: class="fragment" -->
+    stats (memory/cpu consumption)
+  - <!-- .element: class="fragment" -->
+    comparing different produced binary outputs (r11y)
+
+----
+
+### Builder: Design
+
+- <!-- .element: class="fragment" -->
+  Invididual builds can be run in your desired container/virt engine/scheduler, as long as it speaks the same Build API
+- <!-- .element: class="fragment" -->
+  Build API composition/proxying, similar to Store composition
+- <!-- .element: class="fragment" -->
+  allows "unattended building" (evaluate nixpkgs locally and send all build requests to a remote builder)
+- <!-- .element: class="fragment" -->
+  allows tailing logs from currently/already running builds
+
+----
+
+### Builder: Status
+
+- <!-- .element: class="fragment" -->
+  Dummy Builder implementation in `go-nix` (using OCI)
+- <!-- .element: class="fragment" -->
+  Some scribble notes on the Build Protocol
+- <!-- .element: class="fragment" -->
+  Glue code to trigger builds from inside `builtins.derivation` needs to be written
+- <!-- .element: class="fragment" -->
+  Builder implementation (using `systemd-nspwan` or some container engine needs to be written.
+- <!-- .element: class="fragment" -->
+  Web interface to visualize store contents and build graphs/builds/logs
+
+---
+
+## Contributing
+
+- <!-- .element: class="fragment" -->
+  Join the IRC channel (`#tvl` on `hackint`), bridged to Matrix and XMPP
+- <!-- .element: class="fragment" -->
+  Check our issue tracker
+- <!-- .element: class="fragment" -->
+  Try to use it and tell us how you broke it!
+- <!-- .element: class="fragment" -->
+  Add various Nix bits to `nix-compat`
+
+Note: or if you like what you seeing and want to sponsor parts, that's also cool :-)
+
+---
+
+# Thanks to...
+
+- <!-- .element: class="fragment" -->
+  all TVL contributors (some drive-by, some long-term contributors)
+- <!-- .element: class="fragment" -->
+  countless Nix community members for their input on the architecture and rubberducking
+- <!-- .element: class="fragment" -->
+  NLNET and others to sponsor parts of this
+
+----
+
+# Questions?
+
+<style>
+.container{
+    display: flex;
+}
+.col{
+    flex: 1;
+}
+</style>
+
+<div class="container">
+
+<div class="col">
+Florian Klink / <a href="https://flokli.de">flokli.de</a><br />
+<img src="qrcode-flokli.svg" />
+</div>
+
+<div class="col">
+Tvix / <a href="https://tvix.dev">tvix.dev</a><br />
+<img src="qrcode-tvix.svg" />
+</div>
+
+</div>
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/tvix-store-graph.svg b/users/flokli/presentations/2023-09-09-nixcon-tvix/tvix-store-graph.svg
new file mode 100644
index 0000000000..56338b587e
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/tvix-store-graph.svg
@@ -0,0 +1,17 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1297.1484375 836.19140625" width="1297.1484375" height="836.19140625">
+  <!-- svg-source:excalidraw -->
+  
+  <defs>
+    <style class="style-fonts">
+      @font-face {
+        font-family: "Virgil";
+        src: url("https://excalidraw.com/Virgil.woff2");
+      }
+      @font-face {
+        font-family: "Cascadia";
+        src: url("https://excalidraw.com/Cascadia.woff2");
+      }
+    </style>
+    
+  </defs>
+  <rect x="0" y="0" width="1297.1484375" height="836.19140625" fill="#ffffff"></rect><g stroke-linecap="round" transform="translate(10 64.05078125) rotate(0 71.10546875 23.685546875)"><path d="M3.15 2.74 C3.15 2.74, 3.15 2.74, 3.15 2.74 M3.15 2.74 C3.15 2.74, 3.15 2.74, 3.15 2.74 M1.58 10.65 C3.55 6.56, 7.08 5.83, 10.11 0.84 M1.58 10.65 C4.54 7.52, 5.83 5.33, 10.11 0.84 M1.97 16.29 C5.97 12.52, 7.97 9.89, 14.44 1.95 M1.97 16.29 C5.39 12.84, 8.96 8.01, 14.44 1.95 M1.71 22.69 C5.44 18.07, 11.5 12.49, 19.43 2.31 M1.71 22.69 C6.47 16.78, 11.69 12.31, 19.43 2.31 M1.45 29.09 C9.97 18.06, 21.19 8.77, 25.07 1.92 M1.45 29.09 C9.61 17.76, 19.35 7.56, 25.07 1.92 M1.85 34.73 C6.66 29.67, 14.83 20.48, 30.06 2.28 M1.85 34.73 C8.29 27.25, 17.05 18.03, 30.06 2.28 M2.24 40.37 C10.77 28.82, 20.92 18.16, 35.7 1.88 M2.24 40.37 C14.65 26.46, 25.75 13.13, 35.7 1.88 M3.29 45.26 C19.71 28.3, 32.79 9.79, 40.69 2.24 M3.29 45.26 C14.19 32.96, 23.82 22.41, 40.69 2.24 M6.97 47.13 C21.76 30.34, 38.17 12.66, 45.67 2.6 M6.97 47.13 C19.37 32.8, 32.58 18.66, 45.67 2.6 M12.61 46.74 C23.7 34.53, 32.71 23.85, 51.32 2.21 M12.61 46.74 C23.04 33.52, 34.64 21.81, 51.32 2.21 M17.6 47.1 C32.92 32.2, 44.82 17.61, 56.3 2.57 M17.6 47.1 C26.86 36.54, 34.43 27.02, 56.3 2.57 M22.58 47.46 C36.55 31.33, 51.38 11.55, 61.95 2.17 M22.58 47.46 C35.33 33.95, 46.66 20.15, 61.95 2.17 M28.23 47.06 C40.8 33.63, 55.34 16.98, 66.93 2.53 M28.23 47.06 C36.54 38.37, 43.25 29.02, 66.93 2.53 M33.21 47.42 C48.21 29.57, 61.74 14.8, 72.58 2.14 M33.21 47.42 C44.03 35.16, 55.8 21.76, 72.58 2.14 M38.86 47.03 C49.09 34.45, 58.15 26.51, 77.56 2.5 M38.86 47.03 C53.73 30.24, 67.18 15.29, 77.56 2.5 M43.84 47.39 C57.75 33, 73.31 14.12, 82.55 2.86 M43.84 47.39 C53.3 37.24, 61.17 27.55, 82.55 2.86 M49.49 46.99 C62.68 35.47, 70.45 21.6, 88.19 2.46 M49.49 46.99 C61.84 34.86, 73.11 21.46, 88.19 2.46 M54.47 47.35 C61.1 38.29, 69.25 27.83, 93.18 2.82 M54.47 47.35 C66.26 32.61, 76.99 20.02, 93.18 2.82 M60.12 46.96 C73.27 30.81, 84.64 16.32, 98.82 2.43 M60.12 46.96 C69.94 34.83, 80.66 21.92, 98.82 2.43 M65.1 47.32 C73.48 35.83, 86.65 26.47, 103.81 2.79 M65.1 47.32 C78.64 32.14, 91.97 18.02, 103.81 2.79 M70.09 47.68 C85.6 30.67, 98.3 12.05, 109.45 2.4 M70.09 47.68 C81.34 34.02, 94.32 21.42, 109.45 2.4 M75.73 47.28 C89.43 31.61, 102.84 18.22, 114.44 2.76 M75.73 47.28 C85.67 35.55, 96.35 22.65, 114.44 2.76 M80.72 47.64 C91.93 34.98, 103.63 17.52, 119.43 3.12 M80.72 47.64 C90.36 36.8, 98.88 24.6, 119.43 3.12 M86.36 47.25 C101.83 29.55, 117.32 12.7, 125.07 2.72 M86.36 47.25 C99.66 32.13, 111.08 17.18, 125.07 2.72 M91.35 47.61 C104.35 33.22, 117.8 19.95, 130.06 3.08 M91.35 47.61 C103.56 34.45, 114.2 20.27, 130.06 3.08 M96.99 47.21 C103.59 38.71, 113.94 28.96, 136.36 1.93 M96.99 47.21 C106.26 36.64, 117.24 24.44, 136.36 1.93 M101.98 47.57 C111.9 34.49, 126.09 20.88, 140.03 3.8 M101.98 47.57 C114.1 33.1, 128.09 16.65, 140.03 3.8 M106.97 47.93 C114.85 39.44, 121.62 28.8, 141.74 7.93 M106.97 47.93 C117.52 37.38, 125.94 26.56, 141.74 7.93 M112.61 47.54 C120.79 38.54, 126.18 28.87, 144.1 11.31 M112.61 47.54 C125.62 33.32, 137.05 19.84, 144.1 11.31 M117.6 47.9 C122.32 42.89, 130.44 32.85, 144.5 16.96 M117.6 47.9 C126.99 36.5, 135.5 27.33, 144.5 16.96 M123.24 47.51 C127.95 43.64, 132.17 36.33, 144.23 23.35 M123.24 47.51 C129.47 41.35, 136.09 32.69, 144.23 23.35 M128.23 47.87 C131.28 40.54, 137.28 36.59, 143.97 29.75 M128.23 47.87 C132.3 43.29, 136.94 37.23, 143.97 29.75 M132.56 48.98 C136.69 46.03, 136.69 42.77, 143.71 36.15 M132.56 48.98 C136.02 46.4, 138.73 42.29, 143.71 36.15" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M11.84 0 M11.84 0 C38.02 2.65, 61.84 0.28, 130.37 0 M11.84 0 C38.21 1.94, 65.76 1.5, 130.37 0 M130.37 0 C139.47 1, 143.75 5.88, 142.21 11.84 M130.37 0 C137.21 0.92, 139.99 4.87, 142.21 11.84 M142.21 11.84 C140.42 17.68, 141.72 25.2, 142.21 35.53 M142.21 11.84 C142.2 20.55, 141.47 28.9, 142.21 35.53 M142.21 35.53 C143.14 42.65, 138.92 45.76, 130.37 47.37 M142.21 35.53 C141.26 45.41, 136.07 48.58, 130.37 47.37 M130.37 47.37 C100.03 46.91, 70.33 46.17, 11.84 47.37 M130.37 47.37 C93.27 45.68, 57.65 45.47, 11.84 47.37 M11.84 47.37 C3.83 47.21, 0.04 43.58, 0 35.53 M11.84 47.37 C3.58 49.37, 2.09 44.62, 0 35.53 M0 35.53 C-0.11 25.56, 1.74 17.16, 0 11.84 M0 35.53 C-0.73 27.14, 0.25 16.41, 0 11.84 M0 11.84 C1.26 4.93, 5.18 0.97, 11.84 0 M0 11.84 C0.62 1.9, 3.42 2.05, 11.84 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(15 78.13632812499998) rotate(0 65.625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">0x01 0x02 0x03</text></g><g stroke-linecap="round" transform="translate(11.80078125 149.404296875) rotate(0 51.5 24.5)"><path d="M3.26 2.83 C3.26 2.83, 3.26 2.83, 3.26 2.83 M3.26 2.83 C3.26 2.83, 3.26 2.83, 3.26 2.83 M1.69 10.74 C4.79 9.22, 6.14 6.4, 10.22 0.93 M1.69 10.74 C4.82 7.44, 8.36 3.46, 10.22 0.93 M1.43 17.14 C5.08 10.94, 12.94 4.63, 14.55 2.05 M1.43 17.14 C7.42 12.34, 11.48 5.83, 14.55 2.05 M1.82 22.78 C7.79 17.76, 9.92 11.78, 20.19 1.65 M1.82 22.78 C6.91 17.54, 10.69 12.89, 20.19 1.65 M1.56 29.18 C10.68 19.58, 16.05 8.86, 25.18 2.01 M1.56 29.18 C6.66 24.1, 10.7 18.88, 25.18 2.01 M1.95 34.82 C14.06 20.7, 22.56 11.87, 30.16 2.37 M1.95 34.82 C12.05 23.95, 21.94 11.12, 30.16 2.37 M1.69 41.22 C7.98 31.97, 17.66 24.23, 35.81 1.98 M1.69 41.22 C14.67 26.87, 25.96 11.6, 35.81 1.98 M3.4 45.35 C12.42 31.5, 21.88 20.01, 40.79 2.34 M3.4 45.35 C14.79 32.81, 25.98 18.39, 40.79 2.34 M5.76 48.73 C19.03 33.54, 30.13 20.75, 46.44 1.94 M5.76 48.73 C15.95 35.19, 26.77 22.13, 46.44 1.94 M10.75 49.09 C25.08 35.85, 34.98 19.45, 51.42 2.3 M10.75 49.09 C25.36 34.29, 38.07 17.14, 51.42 2.3 M14.42 50.96 C25.24 34.64, 40.8 20.23, 57.07 1.91 M14.42 50.96 C31.56 31.52, 47.54 13.07, 57.07 1.91 M20.07 50.57 C36.58 31.78, 51.41 11.57, 62.05 2.27 M20.07 50.57 C35.27 32.37, 49.62 15.57, 62.05 2.27 M25.05 50.93 C33.69 40.39, 45.49 30.76, 67.7 1.87 M25.05 50.93 C39.57 35.58, 52.89 19.97, 67.7 1.87 M30.04 51.29 C42.51 36.46, 56.98 20.61, 72.68 2.23 M30.04 51.29 C39.08 40.34, 49.2 29.9, 72.68 2.23 M35.68 50.89 C47.44 39.9, 56.68 27.08, 78.33 1.84 M35.68 50.89 C52.05 33.69, 66.26 15.86, 78.33 1.84 M40.67 51.25 C53.32 36.02, 69.16 19.85, 83.31 2.2 M40.67 51.25 C53.19 37.93, 64.14 24.78, 83.31 2.2 M46.31 50.86 C62.11 32.31, 78.52 15.58, 88.96 1.8 M46.31 50.86 C57.68 37.06, 69.81 23.8, 88.96 1.8 M51.3 51.22 C59.29 38.98, 72.37 29.04, 93.94 2.16 M51.3 51.22 C64.73 35.51, 79.44 17.72, 93.94 2.16 M56.94 50.83 C69.81 34.76, 86.63 19.59, 98.93 2.52 M56.94 50.83 C72.69 32.66, 90.36 14.37, 98.93 2.52 M61.93 51.19 C77.07 35.49, 89.58 18.3, 101.95 5.15 M61.93 51.19 C76.05 33.49, 92.37 16.74, 101.95 5.15 M67.57 50.79 C76.42 41.59, 82.64 33.63, 103 10.04 M67.57 50.79 C76.25 40.25, 84.99 30.22, 103 10.04 M72.56 51.15 C79.09 44.86, 86.76 36.01, 103.4 15.68 M72.56 51.15 C78.4 43.41, 84.37 36.12, 103.4 15.68 M78.2 50.76 C84.15 41.95, 92.56 33.07, 103.13 22.08 M78.2 50.76 C88.29 39.34, 96.97 28.67, 103.13 22.08 M83.19 51.12 C88.53 45.31, 96.1 36.68, 103.53 27.72 M83.19 51.12 C91.24 41.6, 98.32 33.94, 103.53 27.72 M88.83 50.72 C90.5 47.29, 97.87 43.07, 103.27 34.12 M88.83 50.72 C94.68 44.06, 98.18 39.73, 103.27 34.12 M93.82 51.08 C96.56 47.12, 100.38 44.41, 104.97 38.25 M93.82 51.08 C96.61 47.17, 101.29 42.48, 104.97 38.25" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M12.25 0 M12.25 0 C28.51 0, 42.5 2.25, 90.75 0 M12.25 0 C32.14 -0.62, 52.39 1.04, 90.75 0 M90.75 0 C100.71 0.09, 101.8 3.77, 103 12.25 M90.75 0 C97.64 -0.83, 104 5.94, 103 12.25 M103 12.25 C101.33 19.82, 101.63 32.15, 103 36.75 M103 12.25 C102.35 21.13, 102.15 31.06, 103 36.75 M103 36.75 C104.56 46.87, 99.61 50.21, 90.75 49 M103 36.75 C104.15 46.69, 101.13 47.94, 90.75 49 M90.75 49 C62.94 48.31, 35.39 47.65, 12.25 49 M90.75 49 C67.73 49.76, 43.51 49.69, 12.25 49 M12.25 49 C3.6 47.48, -1.71 45.84, 0 36.75 M12.25 49 C3.19 49.75, -1.85 43.96, 0 36.75 M0 36.75 C-0.6 25.33, 0.5 18.11, 0 12.25 M0 36.75 C-0.07 30.3, -1.01 22.9, 0 12.25 M0 12.25 C-1.34 4.43, 2.32 -0.12, 12.25 0 M0 12.25 C-0.18 4.12, 4.26 -0.37, 12.25 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(16.80078125 164.30429687499998) rotate(0 42.1875 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">0x04 0x05</text></g><g stroke-linecap="round" transform="translate(14.93359375 236.67578125) rotate(0 4.138671875 25.552734375)"><path d="M-0.29 2.4 C-0.29 2.4, -0.29 2.4, -0.29 2.4 M-0.29 2.4 C-0.29 2.4, -0.29 2.4, -0.29 2.4 M0.11 8.04 C1.82 6.13, 2.96 4.61, 4.7 2.76 M0.11 8.04 C1.05 6.92, 2.71 5.33, 4.7 2.76 M-0.15 14.44 C2.61 10.36, 4.52 9.27, 9.69 3.12 M-0.15 14.44 C3 10.45, 7.79 5.81, 9.69 3.12 M0.24 20.08 C1.78 17.33, 6.21 13.86, 9.43 9.52 M0.24 20.08 C3.9 16.52, 6.5 12.39, 9.43 9.52 M-0.02 26.48 C2.46 23.23, 7.19 20.21, 9.82 15.16 M-0.02 26.48 C2.52 23.58, 5.92 19.96, 9.82 15.16 M-0.28 32.88 C3.43 29.78, 4.96 26.51, 9.56 21.56 M-0.28 32.88 C1.68 30.37, 4.75 26.47, 9.56 21.56 M0.11 38.52 C2.43 35.55, 6.84 32.06, 9.3 27.96 M0.11 38.52 C3.34 34.69, 7.79 30.27, 9.3 27.96 M-0.15 44.92 C4.41 39.72, 7.29 36.44, 9.69 33.6 M-0.15 44.92 C2.92 41.4, 5.54 37.99, 9.69 33.6 M0.9 49.81 C2.58 47.91, 5.61 43.76, 9.43 40 M0.9 49.81 C3.14 46.67, 5.91 43.78, 9.43 40 M3.92 52.43 C5.01 50.49, 6.18 49.02, 9.82 45.64 M3.92 52.43 C5.89 50.77, 7.52 48.28, 9.82 45.64" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M2.07 0 M2.07 0 C2.97 -0.21, 4.11 0.12, 6.21 0 M2.07 0 C3.19 -0.16, 4.23 0.11, 6.21 0 M6.21 0 C8.97 0.45, 9.01 1.39, 8.28 2.07 M6.21 0 C9.57 -1.98, 9.89 2.6, 8.28 2.07 M8.28 2.07 C8.66 12.58, 9.73 22.31, 8.28 49.04 M8.28 2.07 C8.53 15.26, 7.49 28.86, 8.28 49.04 M8.28 49.04 C6.95 49.38, 7.58 52.75, 6.21 51.11 M8.28 49.04 C10.35 52.7, 9.64 53.24, 6.21 51.11 M6.21 51.11 C5.12 51.08, 3.76 50.9, 2.07 51.11 M6.21 51.11 C5.14 51.07, 4.25 51.01, 2.07 51.11 M2.07 51.11 C2.45 52.5, -1.17 52.21, 0 49.04 M2.07 51.11 C-1.47 50.3, 2.23 51.76, 0 49.04 M0 49.04 C-0.13 38.39, -0.48 26.48, 0 2.07 M0 49.04 C-1.31 32.15, -0.35 13.45, 0 2.07 M0 2.07 C0.41 0.36, -0.67 -0.56, 2.07 0 M0 2.07 C-0.35 -0.58, -1.03 -1.54, 2.07 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(10.23046875 10) rotate(0 23.4375 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">Blobs</text></g><g transform="translate(279.12890625 12.759374999999977) rotate(0 51.5625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">Directories</text></g><g stroke-linecap="round" transform="translate(283.875 68.3828125) rotate(0 197 72.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C8.28 14.49, 8.49 11.04, 18.1 2.48 M3.66 19.09 C7.7 14.57, 11.52 11.66, 18.1 2.48 M2.75 26.24 C11.51 19.05, 17.92 11.98, 24.4 1.33 M2.75 26.24 C7.23 20.55, 12.55 15.89, 24.4 1.33 M2.48 32.64 C12.12 24.05, 19.28 13.72, 30.7 0.18 M2.48 32.64 C9.16 24.56, 14.83 17.39, 30.7 0.18 M2.88 38.28 C11.43 28.28, 22.31 16.44, 34.37 2.05 M2.88 38.28 C14.6 24.25, 27.89 9.94, 34.37 2.05 M2.62 44.68 C9.32 34.89, 17.39 26.88, 39.36 2.41 M2.62 44.68 C11.95 33.82, 21.66 21.02, 39.36 2.41 M2.36 51.07 C12.81 41.21, 19.43 27.76, 45 2.02 M2.36 51.07 C16.68 33.29, 30.46 16.46, 45 2.02 M2.75 56.72 C16.64 41.98, 28.98 26.21, 49.99 2.38 M2.75 56.72 C21 35.95, 39.52 14.81, 49.99 2.38 M2.49 63.11 C19.63 44.26, 39.08 21.21, 55.63 1.98 M2.49 63.11 C17.01 46.73, 31.61 30.35, 55.63 1.98 M2.88 68.76 C22.9 46.33, 43.08 21.26, 60.62 2.34 M2.88 68.76 C23.19 45.39, 41.88 24.57, 60.62 2.34 M2.62 75.16 C23.83 54.32, 39.89 29.17, 66.26 1.95 M2.62 75.16 C18.47 56.13, 34.46 38.2, 66.26 1.95 M2.36 81.55 C29.87 50.88, 54.51 22.49, 71.25 2.31 M2.36 81.55 C26.5 54.38, 49.03 26.57, 71.25 2.31 M2.76 87.2 C25.76 61.24, 52.69 29.56, 76.89 1.91 M2.76 87.2 C22.68 64.01, 42.85 40.22, 76.89 1.91 M2.49 93.59 C30.49 61.54, 57.87 26.06, 81.88 2.27 M2.49 93.59 C31.33 60.52, 59.32 27.03, 81.88 2.27 M2.89 99.24 C28.16 70.88, 49.29 44.38, 87.52 1.88 M2.89 99.24 C22.19 77.54, 40.48 55.68, 87.52 1.88 M2.63 105.64 C27.42 78.52, 50.89 50.01, 92.51 2.24 M2.63 105.64 C38.6 64.43, 74.48 23.22, 92.51 2.24 M2.37 112.03 C35.69 71.87, 67.49 34.66, 98.15 1.85 M2.37 112.03 C29.87 80.37, 57.31 48.8, 98.15 1.85 M2.76 117.68 C35.35 78.95, 70.62 41.89, 103.14 2.21 M2.76 117.68 C24.16 94.43, 44.43 69.26, 103.14 2.21 M3.16 123.32 C27.31 95.67, 53.64 64.97, 108.78 1.81 M3.16 123.32 C40.3 79.27, 78.36 37.25, 108.78 1.81 M4.86 127.45 C35.22 90.31, 66.42 52.47, 113.77 2.17 M4.86 127.45 C27.89 100.07, 53.15 74.47, 113.77 2.17 M5.26 133.1 C41.54 89.35, 82.58 48.1, 119.41 1.78 M5.26 133.1 C50.78 80.9, 96.57 28.36, 119.41 1.78 M7.62 136.47 C50.54 85.68, 96.45 32.28, 124.4 2.14 M7.62 136.47 C50.54 87.26, 95.13 37.29, 124.4 2.14 M10.64 139.1 C44.47 96.28, 83.89 53.02, 130.04 1.74 M10.64 139.1 C49.89 96.71, 86.98 53.36, 130.04 1.74 M14.31 140.97 C55.74 92.08, 97.64 42.48, 135.03 2.1 M14.31 140.97 C49.36 97.94, 86.03 55.71, 135.03 2.1 M17.33 143.59 C62.88 90.12, 107.49 38.88, 140.67 1.71 M17.33 143.59 C52.29 102.09, 87.68 60.93, 140.67 1.71 M21.01 145.46 C48.28 114.13, 76.95 81.48, 145.66 2.07 M21.01 145.46 C64.61 95.84, 107.71 47.87, 145.66 2.07 M25.34 146.58 C73.23 89.86, 125.31 35.24, 150.65 2.43 M25.34 146.58 C51.9 117.94, 78.32 86.29, 150.65 2.43 M32.29 144.67 C78.07 95.21, 118.24 44.56, 156.29 2.03 M32.29 144.67 C74.54 98.13, 114.59 49.94, 156.29 2.03 M37.28 145.03 C85.09 88.58, 133.69 36.74, 161.28 2.39 M37.28 145.03 C69.44 108.12, 100.26 71.2, 161.28 2.39 M42.92 144.64 C90.72 91.41, 136.95 40.49, 166.92 2 M42.92 144.64 C66.95 114.58, 93.7 85.42, 166.92 2 M47.91 145 C75.02 114.35, 102.25 80.22, 171.91 2.36 M47.91 145 C87.4 99.39, 126.35 54.03, 171.91 2.36 M52.9 145.36 C79.04 113.6, 107.32 79.81, 177.55 1.96 M52.9 145.36 C77.61 115.94, 103.48 86, 177.55 1.96 M58.54 144.96 C92.12 105.3, 128.8 63.9, 182.54 2.32 M58.54 144.96 C106.61 90.51, 152.9 36.35, 182.54 2.32 M63.53 145.32 C107.47 99.27, 149.03 48.65, 188.18 1.93 M63.53 145.32 C96.13 109.61, 126.87 72.99, 188.18 1.93 M69.17 144.93 C102.47 104.11, 137.86 61.81, 193.17 2.29 M69.17 144.93 C110.04 98.38, 152.23 51.84, 193.17 2.29 M74.16 145.29 C104.78 108.19, 138.63 71.58, 198.81 1.9 M74.16 145.29 C99.31 117.03, 123.84 87.71, 198.81 1.9 M79.8 144.9 C130.71 87.32, 178.54 30.53, 203.8 2.26 M79.8 144.9 C124.29 94.42, 167.51 43.91, 203.8 2.26 M84.79 145.26 C111.41 111.48, 143.37 80.45, 209.44 1.86 M84.79 145.26 C130.36 93.05, 176.97 39.33, 209.44 1.86 M90.43 144.86 C128.08 97.62, 168.49 56.08, 214.43 2.22 M90.43 144.86 C130.35 96.04, 171.08 48.72, 214.43 2.22 M95.42 145.22 C146.19 90.39, 192.4 31.56, 220.07 1.83 M95.42 145.22 C120.72 114.85, 146.9 84.4, 220.07 1.83 M101.06 144.83 C127.22 111.52, 152.8 83.04, 225.06 2.19 M101.06 144.83 C132.86 108.13, 163.34 71.68, 225.06 2.19 M106.05 145.19 C154.17 92.56, 198.8 39.31, 230.7 1.79 M106.05 145.19 C136.53 110.59, 166.08 76.75, 230.7 1.79 M111.69 144.79 C138.65 117.15, 162.06 85.81, 235.69 2.15 M111.69 144.79 C137.61 113.65, 164.22 81.98, 235.69 2.15 M116.68 145.15 C157.06 99.25, 194.58 56.02, 241.33 1.76 M116.68 145.15 C155.1 99.61, 194.87 53.74, 241.33 1.76 M122.32 144.76 C155.38 104.12, 188.9 66.56, 246.32 2.12 M122.32 144.76 C171.19 89.94, 218.89 34.94, 246.32 2.12 M127.31 145.12 C160.63 107.75, 195.41 68.39, 251.96 1.72 M127.31 145.12 C171.79 94.11, 216.13 43.25, 251.96 1.72 M132.95 144.72 C167.14 104.52, 206.3 64.21, 256.95 2.08 M132.95 144.72 C164.99 105.52, 198.48 68.92, 256.95 2.08 M137.94 145.08 C183.71 92.31, 226.45 45.7, 262.59 1.69 M137.94 145.08 C163.89 115.91, 190.22 85.14, 262.59 1.69 M143.58 144.69 C174.92 109.48, 206.89 70.46, 267.58 2.05 M143.58 144.69 C170.4 113.21, 199.74 82.17, 267.58 2.05 M148.57 145.05 C178.08 111.76, 203.33 81.73, 272.56 2.41 M148.57 145.05 C173.64 115.53, 198.59 86.39, 272.56 2.41 M154.21 144.65 C187.55 108.07, 219.55 68.66, 278.21 2.01 M154.21 144.65 C200.06 91.97, 243.47 39.89, 278.21 2.01 M159.2 145.01 C186.66 112.8, 212.53 85.21, 283.19 2.37 M159.2 145.01 C207.12 89.92, 255.48 37.14, 283.19 2.37 M164.19 145.37 C202.12 104.92, 237.2 59.79, 288.84 1.98 M164.19 145.37 C194.82 106.77, 226.84 70.45, 288.84 1.98 M169.83 144.98 C212.12 93.05, 258.35 43.46, 293.82 2.34 M169.83 144.98 C201.44 106.46, 235.81 67.61, 293.82 2.34 M174.82 145.34 C207.54 111.65, 235.58 74.76, 299.47 1.94 M174.82 145.34 C199.92 118.44, 225.68 88.79, 299.47 1.94 M180.46 144.94 C208.5 116.42, 230.68 85.13, 304.45 2.3 M180.46 144.94 C218.97 100.78, 259.51 55.75, 304.45 2.3 M185.45 145.31 C221.24 102.72, 256.86 59.74, 310.1 1.91 M185.45 145.31 C232.8 92.21, 280.33 37.72, 310.1 1.91 M191.09 144.91 C238.27 88.19, 288.2 35.33, 315.08 2.27 M191.09 144.91 C220.59 109.55, 252.22 74.41, 315.08 2.27 M196.08 145.27 C239.76 94.85, 284.75 46.35, 320.73 1.88 M196.08 145.27 C230.49 104.94, 265.25 68.33, 320.73 1.88 M201.72 144.88 C234.66 108.79, 263.22 71.79, 325.71 2.24 M201.72 144.88 C237.32 101.42, 273.7 59.07, 325.71 2.24 M206.71 145.24 C253.21 90.64, 300.99 37.76, 331.36 1.84 M206.71 145.24 C240.65 108.22, 273.71 69.33, 331.36 1.84 M212.35 144.84 C242.19 114.5, 267.67 79.17, 336.34 2.2 M212.35 144.84 C236.6 115.07, 262.16 86.57, 336.34 2.2 M217.34 145.2 C251.25 105.29, 286.42 64.08, 341.99 1.81 M217.34 145.2 C249.04 108.05, 278.75 71.6, 341.99 1.81 M222.98 144.81 C254.78 109.71, 284.95 73.51, 346.97 2.17 M222.98 144.81 C251.44 112.17, 277.38 81.44, 346.97 2.17 M227.97 145.17 C258.67 109.25, 290.39 71.64, 352.62 1.77 M227.97 145.17 C253.46 115.85, 279.91 85.09, 352.62 1.77 M233.61 144.77 C279.92 90.22, 328.36 36.09, 357.6 2.13 M233.61 144.77 C263.54 109.84, 296.58 72.44, 357.6 2.13 M238.6 145.13 C284.33 94.02, 329.86 38.18, 363.25 1.74 M238.6 145.13 C270.6 110.97, 300.1 77.3, 363.25 1.74 M244.24 144.74 C285.03 98.47, 325.99 49.52, 368.23 2.1 M244.24 144.74 C288.81 94.28, 333.65 43.13, 368.23 2.1 M249.23 145.1 C274.07 113.24, 304.32 83.42, 373.22 2.46 M249.23 145.1 C288.32 102.57, 326.71 58.15, 373.22 2.46 M254.87 144.7 C297.9 94.29, 343.78 43.11, 377.55 3.57 M254.87 144.7 C286.66 106.11, 320.39 66.72, 377.55 3.57 M259.86 145.06 C295.8 103.74, 331.06 61.72, 381.23 5.44 M259.86 145.06 C298.82 101.67, 336.82 56.01, 381.23 5.44 M265.5 144.67 C313.19 90.52, 361.44 33.85, 384.9 7.31 M265.5 144.67 C292.92 114.87, 319.46 83.51, 384.9 7.31 M270.49 145.03 C293.76 118.67, 317.19 90.91, 387.92 9.94 M270.49 145.03 C298.6 112.33, 327.42 78.58, 387.92 9.94 M276.13 144.63 C309.18 107.62, 339.56 70.71, 390.28 13.32 M276.13 144.63 C314.84 98.86, 353.06 54.55, 390.28 13.32 M281.12 144.99 C305.94 116.7, 328.37 88.76, 391.99 17.45 M281.12 144.99 C321.03 97.29, 362.22 51.33, 391.99 17.45 M286.1 145.35 C324.81 103.13, 365.35 57.02, 393.04 22.34 M286.1 145.35 C313.97 112.26, 343.79 77.31, 393.04 22.34 M291.75 144.96 C324.16 107.97, 358.78 71.44, 395.4 25.72 M291.75 144.96 C322.28 110.33, 352.3 74.13, 395.4 25.72 M296.73 145.32 C322.53 113.82, 350.61 86.17, 395.14 32.11 M296.73 145.32 C330.9 104.68, 367.32 64.68, 395.14 32.11 M302.38 144.93 C320.38 122.95, 340.8 101.95, 395.54 37.76 M302.38 144.93 C328.33 112.21, 356.43 82.25, 395.54 37.76 M307.36 145.29 C337.92 110.64, 366.55 76.31, 395.28 44.15 M307.36 145.29 C325.37 123.43, 344.42 100.44, 395.28 44.15 M313.01 144.89 C335.45 120.95, 355.71 93.18, 395.01 50.55 M313.01 144.89 C342.22 110.33, 372.53 76.59, 395.01 50.55 M317.99 145.25 C335.46 125.76, 350.14 107.98, 395.41 56.2 M317.99 145.25 C334.45 126.54, 349.07 108.69, 395.41 56.2 M323.64 144.86 C350.88 112.04, 380.82 81.41, 395.15 62.59 M323.64 144.86 C349.73 114.44, 376.12 84.65, 395.15 62.59 M328.62 145.22 C342.07 126.15, 358.11 110, 395.54 68.24 M328.62 145.22 C352.38 118.21, 373.64 91.69, 395.54 68.24 M334.27 144.82 C356.58 116.53, 380.06 91.2, 395.28 74.63 M334.27 144.82 C352.85 123.85, 373.24 101.61, 395.28 74.63 M339.25 145.18 C350.58 130.51, 366.65 115.79, 395.67 80.28 M339.25 145.18 C360.84 120.94, 381.57 97.17, 395.67 80.28 M344.9 144.79 C359.95 129.93, 374.97 110.51, 395.41 86.68 M344.9 144.79 C359.66 128.95, 373.84 112.99, 395.41 86.68 M349.88 145.15 C365.74 124.35, 384.39 105.92, 395.81 92.32 M349.88 145.15 C365.01 128.41, 378.61 111.44, 395.81 92.32 M355.53 144.75 C366.83 128.33, 381.85 115.58, 395.55 98.72 M355.53 144.75 C366.51 134.09, 376.57 121.06, 395.55 98.72 M360.51 145.11 C371.57 133.46, 379.69 124.34, 395.94 104.36 M360.51 145.11 C371.82 131.23, 384.33 117.41, 395.94 104.36 M364.19 146.98 C372.36 139.23, 375.92 131.75, 395.68 110.76 M364.19 146.98 C375.25 134.47, 383.71 124.14, 395.68 110.76 M370.49 145.83 C377.43 139.68, 383.18 129.21, 396.73 115.65 M370.49 145.83 C377.33 137.24, 382.58 130.01, 396.73 115.65 M377.44 143.93 C380.98 139.22, 385.26 134.38, 391.88 127.33 M377.44 143.93 C381.01 139.93, 385.77 134.2, 391.88 127.33" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C158.41 -0.65, 283.84 1.37, 362 0 M32 0 C99.31 -2.94, 165.01 -1.66, 362 0 M362 0 C382.37 -1.19, 392.06 11.27, 394 32 M362 0 C383.53 0.55, 395.84 12.5, 394 32 M394 32 C396.17 49.29, 394.95 72.1, 394 113 M394 32 C394.74 57.51, 393.79 80.56, 394 113 M394 113 C393.49 135.83, 381.87 143.9, 362 145 M394 113 C391.98 135.04, 383.6 143.19, 362 145 M362 145 C291.31 144.27, 221.15 142.78, 32 145 M362 145 C249.93 143.31, 138.97 142.96, 32 145 M32 145 C8.71 146.69, -1.86 135.95, 0 113 M32 145 C10.21 143.8, 1.65 132.18, 0 113 M0 113 C0.55 94.72, -1.35 75.26, 0 32 M0 113 C-0.88 90.36, -1.5 66.27, 0 32 M0 32 C1.9 12.2, 9.12 -1.48, 32 0 M0 32 C0.22 11.69, 10.1 1.96, 32 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(288.875 73.3828125) rotate(0 140.625 67.19999999999999)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories: []</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: .keep</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;empty-blob-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g stroke-linecap="round" transform="translate(279.59193843887 271.159696266393) rotate(0 198.99999999999994 130)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C7.06 13.81, 9.24 9.34, 18.1 2.48 M3.66 19.09 C8.44 11.73, 13.76 5.27, 18.1 2.48 M2.75 26.24 C10 16.18, 18.61 6.13, 24.4 1.33 M2.75 26.24 C9.44 19.31, 14.57 12.4, 24.4 1.33 M2.48 32.64 C10.34 21.03, 17.74 12.3, 30.7 0.18 M2.48 32.64 C13.49 21.44, 21.83 9.48, 30.7 0.18 M2.88 38.28 C12.91 25, 26.86 13.76, 34.37 2.05 M2.88 38.28 C12.64 28.15, 21.63 16.63, 34.37 2.05 M2.62 44.68 C14.17 33.27, 22.89 18.23, 39.36 2.41 M2.62 44.68 C12.44 34.48, 21.24 23.55, 39.36 2.41 M2.36 51.07 C18.27 36.19, 31.27 19.8, 45 2.02 M2.36 51.07 C15.34 37.29, 27.12 23.41, 45 2.02 M2.75 56.72 C18.22 37.56, 35.93 17.94, 49.99 2.38 M2.75 56.72 C20.57 34.56, 37.8 14.24, 49.99 2.38 M2.49 63.11 C18.51 41.78, 36.66 23.69, 55.63 1.98 M2.49 63.11 C16.06 46.84, 30.13 32.58, 55.63 1.98 M2.23 69.51 C14.08 54.4, 27.81 42.3, 60.62 2.34 M2.23 69.51 C14.8 54.54, 28.11 38.47, 60.62 2.34 M2.62 75.16 C23.22 53.14, 45.11 28.68, 66.26 1.95 M2.62 75.16 C18.68 55.49, 35.76 36.87, 66.26 1.95 M2.36 81.55 C29.49 51.06, 52.67 20.07, 71.25 2.31 M2.36 81.55 C23.35 59.17, 43.86 35.46, 71.25 2.31 M2.76 87.2 C29.08 59.14, 54.26 30.38, 76.89 1.91 M2.76 87.2 C25.2 61.93, 47.39 35.45, 76.89 1.91 M2.49 93.59 C22.97 73.16, 41.24 50.49, 81.88 2.27 M2.49 93.59 C22.85 70.13, 41.2 48.51, 81.88 2.27 M2.23 99.99 C26.73 70.85, 53 42.34, 87.52 1.88 M2.23 99.99 C26.11 70.65, 52.18 43.37, 87.52 1.88 M2.63 105.64 C29.25 73.43, 57.28 44.11, 92.51 2.24 M2.63 105.64 C22.21 84.02, 40.09 61.9, 92.51 2.24 M2.37 112.03 C24.78 84.92, 46.32 60.61, 98.15 1.85 M2.37 112.03 C40.71 68.73, 77.43 25.35, 98.15 1.85 M2.1 118.43 C32.45 81.76, 64.34 45.79, 103.14 2.21 M2.1 118.43 C26.04 90.13, 50.16 63.23, 103.14 2.21 M2.5 124.07 C34.35 88.85, 66.58 53.31, 108.78 1.81 M2.5 124.07 C35.8 86.3, 67.38 48.42, 108.78 1.81 M2.24 130.47 C44.13 79.64, 85.07 32.68, 113.77 2.17 M2.24 130.47 C28.26 100.65, 54.41 68.41, 113.77 2.17 M2.63 136.11 C39.83 96.08, 73.86 52.5, 119.41 1.78 M2.63 136.11 C27.92 105.97, 55.2 76.2, 119.41 1.78 M2.37 142.51 C38.64 103.62, 72.54 62.27, 124.4 2.14 M2.37 142.51 C38.27 104.7, 71.42 64.68, 124.4 2.14 M2.11 148.91 C31.62 115.14, 60.21 82.46, 130.04 1.74 M2.11 148.91 C30.64 115.22, 60.37 82.49, 130.04 1.74 M2.5 154.55 C45.43 105.72, 87.93 57.79, 135.03 2.1 M2.5 154.55 C39.77 111.59, 78.21 68.92, 135.03 2.1 M2.24 160.95 C40.01 120.72, 75.93 77.72, 140.67 1.71 M2.24 160.95 C44.66 112.93, 87.03 64.56, 140.67 1.71 M1.98 167.35 C39.14 124.53, 74.75 81.57, 145.66 2.07 M1.98 167.35 C47.85 117.91, 91.41 66.21, 145.66 2.07 M2.38 172.99 C58.38 106.95, 115.31 43.43, 150.65 2.43 M2.38 172.99 C53.11 115.34, 104.81 56.32, 150.65 2.43 M2.11 179.39 C64.27 108.42, 124.58 39.52, 156.29 2.03 M2.11 179.39 C42.26 131.15, 83.61 83.43, 156.29 2.03 M2.51 185.03 C62.42 116.85, 122.68 43.77, 161.28 2.39 M2.51 185.03 C56.66 122.43, 112.32 58.31, 161.28 2.39 M2.25 191.43 C51.58 133.03, 102.03 75.72, 166.92 2 M2.25 191.43 C38.3 149.45, 75.45 107.32, 166.92 2 M1.99 197.83 C42.68 152.88, 80.66 108.12, 171.91 2.36 M1.99 197.83 C66.15 123.85, 132.83 47.33, 171.91 2.36 M2.38 203.47 C55.91 143.98, 107.7 83.76, 177.55 1.96 M2.38 203.47 C56.81 142.73, 110.46 81.58, 177.55 1.96 M2.12 209.87 C66.55 137, 131.82 62.14, 182.54 2.32 M2.12 209.87 C50.61 157.03, 98.73 101.44, 182.54 2.32 M1.86 216.27 C63.05 142.96, 126.25 69.69, 188.18 1.93 M1.86 216.27 C65.44 140.95, 131.03 66.87, 188.18 1.93 M2.25 221.91 C59.05 161.57, 111.38 97.22, 193.17 2.29 M2.25 221.91 C52.49 164.23, 101.5 106.36, 193.17 2.29 M1.33 229.06 C64.26 156.57, 124.34 84.79, 198.81 1.9 M1.33 229.06 C60.97 162.1, 120.98 94.62, 198.81 1.9 M1.73 234.71 C54.24 174.7, 106.54 116.97, 203.8 2.26 M1.73 234.71 C76.5 149.79, 152.59 63.83, 203.8 2.26 M2.78 239.59 C49.77 186.82, 94.95 134.94, 209.44 1.86 M2.78 239.59 C83.35 146.44, 163.63 53.44, 209.44 1.86 M4.49 243.73 C48.55 190.47, 93.86 139.53, 214.43 2.22 M4.49 243.73 C63.53 174.61, 122.23 106.2, 214.43 2.22 M6.19 247.86 C77.48 165.73, 149.04 81.52, 220.07 1.83 M6.19 247.86 C85.41 157.55, 164.08 66.86, 220.07 1.83 M8.56 251.24 C90.22 155.68, 175.16 56.51, 225.06 2.19 M8.56 251.24 C89.12 157.29, 172.43 62.13, 225.06 2.19 M10.92 254.62 C69.41 191.54, 124.13 126, 230.7 1.79 M10.92 254.62 C69.41 187.27, 127.03 120.42, 230.7 1.79 M13.94 257.24 C82.45 176.73, 153.61 97.39, 235.69 2.15 M13.94 257.24 C93.45 166.82, 173.91 75.69, 235.69 2.15 M18.27 258.36 C76.98 194.1, 131.53 129.14, 241.33 1.76 M18.27 258.36 C66.67 201.61, 115.46 145.37, 241.33 1.76 M22.6 259.47 C75.95 201, 128.47 140.06, 246.32 2.12 M22.6 259.47 C70.35 207.82, 116.17 155.22, 246.32 2.12 M26.27 261.34 C95.93 180.59, 169.95 95.39, 251.96 1.72 M26.27 261.34 C89.37 190.53, 152.1 118.22, 251.96 1.72 M32.57 260.19 C96.06 189.84, 155.98 120.97, 256.95 2.08 M32.57 260.19 C78.18 205.29, 124.7 151.48, 256.95 2.08 M37.56 260.55 C117 171.22, 192.42 83.99, 262.59 1.69 M37.56 260.55 C121.96 163.6, 206.2 67.19, 262.59 1.69 M43.2 260.16 C105.28 189.09, 169.45 117.92, 267.58 2.05 M43.2 260.16 C123.84 168.01, 203.45 76.43, 267.58 2.05 M48.19 260.52 C117.81 178.99, 186.9 100.6, 272.56 2.41 M48.19 260.52 C125.53 171.84, 202.4 82.45, 272.56 2.41 M53.83 260.12 C138.08 162.28, 222.76 67.81, 278.21 2.01 M53.83 260.12 C128.55 171.85, 204.35 84.41, 278.21 2.01 M58.82 260.48 C127.06 184.42, 193 106.58, 283.19 2.37 M58.82 260.48 C135.91 173.81, 211.1 87.23, 283.19 2.37 M64.46 260.09 C109.71 206.88, 156.75 154.77, 288.84 1.98 M64.46 260.09 C142.84 169.53, 220.7 80.3, 288.84 1.98 M69.45 260.45 C138.09 183.73, 208 105.35, 293.82 2.34 M69.45 260.45 C156.6 162.77, 241.99 64.65, 293.82 2.34 M75.09 260.06 C143.01 182.6, 211.21 103.49, 299.47 1.94 M75.09 260.06 C136 192, 195.87 123.37, 299.47 1.94 M80.08 260.42 C126.17 206.79, 172.95 151.57, 304.45 2.3 M80.08 260.42 C129.25 204.69, 179.83 146.37, 304.45 2.3 M85.72 260.02 C157.76 178.28, 226.22 97.47, 310.1 1.91 M85.72 260.02 C149.05 184.68, 213.67 111.24, 310.1 1.91 M90.71 260.38 C181.48 157.9, 269.25 55.91, 315.08 2.27 M90.71 260.38 C157.59 183.34, 224.55 106.5, 315.08 2.27 M95.7 260.74 C174.5 168.64, 255.03 76.7, 320.73 1.88 M95.7 260.74 C164.52 183.36, 232.23 105.12, 320.73 1.88 M101.34 260.35 C181.6 167.21, 261.15 73.9, 325.71 2.24 M101.34 260.35 C177.77 172.29, 255.39 82.95, 325.71 2.24 M106.33 260.71 C168.93 189.98, 230.06 115.81, 331.36 1.84 M106.33 260.71 C170.53 184.03, 237.17 107.92, 331.36 1.84 M111.97 260.31 C176.35 190.63, 236.85 119.43, 336.34 2.2 M111.97 260.31 C157.57 208.21, 204.38 154.93, 336.34 2.2 M116.96 260.67 C164.3 205.68, 212.63 148.72, 341.99 1.81 M116.96 260.67 C197.94 169.46, 278.58 77.5, 341.99 1.81 M122.6 260.28 C175.27 200.79, 229.84 138.31, 346.97 2.17 M122.6 260.28 C172.5 202.83, 220.76 146.35, 346.97 2.17 M127.59 260.64 C215.15 158.17, 304.42 54.77, 352.62 1.77 M127.59 260.64 C188.96 189.82, 250.22 118.28, 352.62 1.77 M133.23 260.24 C183.32 201.93, 235.94 141.72, 357.6 2.13 M133.23 260.24 C184.28 203.09, 233.15 145.34, 357.6 2.13 M138.22 260.6 C220.44 165.6, 301.95 72.93, 363.25 1.74 M138.22 260.6 C215.16 172.58, 291.69 83.91, 363.25 1.74 M143.86 260.21 C216.13 172.96, 292.05 87.04, 368.23 2.1 M143.86 260.21 C223.54 169.38, 302.36 80.03, 368.23 2.1 M148.85 260.57 C228.87 169.81, 309.22 79.04, 374.53 0.95 M148.85 260.57 C225.85 169.3, 304.04 78.63, 374.53 0.95 M154.49 260.17 C230.89 172.65, 306.45 86.57, 378.21 2.82 M154.49 260.17 C210.94 195.8, 265.6 130.92, 378.21 2.82 M159.48 260.53 C223.94 184.22, 290.75 110.57, 382.54 3.93 M159.48 260.53 C244.05 163.85, 329.27 67.22, 382.54 3.93 M165.12 260.14 C236.95 176.83, 309.56 92.2, 386.87 5.05 M165.12 260.14 C221.23 195.95, 279.16 130.62, 386.87 5.05 M170.11 260.5 C221.21 201.49, 273.24 142.28, 389.23 8.43 M170.11 260.5 C218.37 205.87, 266.44 150.41, 389.23 8.43 M175.75 260.11 C241.58 187.82, 304.45 113.61, 392.25 11.05 M175.75 260.11 C222.69 207.98, 267.39 155.95, 392.25 11.05 M180.74 260.47 C245.33 188.58, 307.54 115.78, 395.27 13.68 M180.74 260.47 C261.6 168.2, 340.34 76.88, 395.27 13.68 M186.38 260.07 C251.57 186.65, 319.28 107.6, 396.32 18.56 M186.38 260.07 C236.65 200.09, 289.45 140.81, 396.32 18.56 M191.37 260.43 C255.8 183.29, 323.42 107.08, 397.37 23.45 M191.37 260.43 C233.86 209.7, 277.09 160.88, 397.37 23.45 M197.01 260.04 C273.9 173.76, 350.14 83.39, 397.77 29.09 M197.01 260.04 C238.49 210.28, 280.91 161.3, 397.77 29.09 M202 260.4 C249.95 204.58, 301.21 145.35, 399.47 33.23 M202 260.4 C246.27 207.11, 292.1 154.9, 399.47 33.23 M207.64 260 C282.04 175.37, 353.03 93.88, 399.21 39.63 M207.64 260 C252.23 206.89, 298.22 154.54, 399.21 39.63 M212.63 260.36 C270.79 190.87, 332.59 125.04, 399.61 45.27 M212.63 260.36 C255.76 212.2, 297.66 164.88, 399.61 45.27 M217.62 260.72 C270.87 200.01, 324.39 137.32, 399.34 51.67 M217.62 260.72 C286.2 182.97, 352.23 105.9, 399.34 51.67 M223.26 260.33 C266.88 209.47, 311.98 157.05, 399.74 57.31 M223.26 260.33 C270.48 203.33, 319.36 147.73, 399.74 57.31 M228.25 260.69 C264.48 217.85, 298.81 176.68, 399.48 63.71 M228.25 260.69 C287.06 192.75, 345.09 124.31, 399.48 63.71 M233.89 260.29 C283.82 202.81, 333.35 147.36, 399.22 70.11 M233.89 260.29 C296.32 189.36, 357.95 117.97, 399.22 70.11 M238.88 260.65 C288.24 201.51, 340.4 144.75, 399.61 75.75 M238.88 260.65 C302.98 187.65, 365.83 115.51, 399.61 75.75 M244.52 260.26 C301.41 192.57, 362.82 123.97, 399.35 82.15 M244.52 260.26 C276.87 221.94, 309.71 183.55, 399.35 82.15 M249.51 260.62 C294.86 210.74, 338.19 159.76, 399.74 87.79 M249.51 260.62 C288.68 213.68, 329.48 168.33, 399.74 87.79 M255.15 260.22 C306.36 203.12, 357.73 144.26, 399.48 94.19 M255.15 260.22 C310.1 197.2, 364.53 133.07, 399.48 94.19 M260.14 260.58 C306.6 208.36, 353.31 154.47, 399.22 100.59 M260.14 260.58 C314.84 198.11, 370.53 134.35, 399.22 100.59 M265.78 260.19 C310.07 209.18, 353.85 155.07, 399.62 106.23 M265.78 260.19 C313.52 203.42, 361.67 148.41, 399.62 106.23 M270.77 260.55 C312.94 210.71, 350.92 165.03, 399.35 112.63 M270.77 260.55 C313.74 212.09, 354.22 165.25, 399.35 112.63 M276.41 260.15 C322.47 205.94, 367.79 155.3, 399.75 118.27 M276.41 260.15 C319.24 210.97, 362.85 162.61, 399.75 118.27 M281.4 260.51 C309.82 228.04, 337.57 195, 399.49 124.67 M281.4 260.51 C315.37 218.59, 351.47 180.05, 399.49 124.67 M287.04 260.12 C329.88 210.1, 376.19 158.03, 399.23 131.06 M287.04 260.12 C314.45 229.24, 339.01 200.91, 399.23 131.06 M292.03 260.48 C332.66 211.87, 372.61 167.01, 399.62 136.71 M292.03 260.48 C317.01 231.17, 342.37 202.7, 399.62 136.71 M297.67 260.09 C320.79 233.82, 342.65 206.24, 399.36 143.11 M297.67 260.09 C337.01 212.55, 377.78 167.34, 399.36 143.11 M302.66 260.45 C335.5 224.45, 365.99 190.46, 399.75 148.75 M302.66 260.45 C341.03 214.95, 379.75 172.24, 399.75 148.75 M308.3 260.05 C331.34 234.81, 350.27 209.28, 399.49 155.15 M308.3 260.05 C337.96 226.28, 369 190.93, 399.49 155.15 M313.29 260.41 C344.16 226.42, 373.26 191.65, 399.23 161.54 M313.29 260.41 C340.87 229.56, 366.67 199.76, 399.23 161.54 M318.93 260.02 C339.08 235.8, 357.34 215.88, 399.63 167.19 M318.93 260.02 C338.18 238.19, 356.44 217.12, 399.63 167.19 M323.92 260.38 C343.38 240.84, 359.55 216.05, 399.36 173.59 M323.92 260.38 C339.58 242.35, 357.05 223.74, 399.36 173.59 M328.9 260.74 C356.6 230.93, 381.38 201.75, 399.76 179.23 M328.9 260.74 C355.35 229.48, 382.07 199.6, 399.76 179.23 M334.55 260.34 C353.55 235.84, 374.53 215.15, 399.5 185.63 M334.55 260.34 C350.96 240.58, 366.19 222.61, 399.5 185.63 M339.53 260.7 C355.36 243.43, 370.27 221.73, 399.24 192.02 M339.53 260.7 C361.66 234.82, 383.39 210.67, 399.24 192.02 M345.18 260.31 C362.54 238.6, 380.33 220.43, 399.63 197.67 M345.18 260.31 C359.44 243.81, 374.08 228.32, 399.63 197.67 M350.16 260.67 C364.5 243.76, 376.71 230.97, 399.37 204.06 M350.16 260.67 C363.93 245.32, 377.91 229.24, 399.37 204.06 M355.81 260.27 C372.91 242.23, 387.92 223.73, 399.76 209.71 M355.81 260.27 C373.85 240.57, 389.06 220.82, 399.76 209.71 M360.79 260.63 C368.09 252.39, 377.96 242.27, 399.5 216.11 M360.79 260.63 C372.53 247.08, 385.83 232.19, 399.5 216.11 M366.44 260.24 C372.92 252.64, 384.19 241.59, 399.24 222.5 M366.44 260.24 C374.31 251.44, 384.03 240.28, 399.24 222.5 M372.74 259.09 C379.45 251.45, 383.63 244.65, 398.98 228.9 M372.74 259.09 C383.24 247.41, 391.87 236.99, 398.98 228.9 M378.38 258.69 C382.28 255.84, 387.86 247.21, 398.06 236.05 M378.38 258.69 C382.66 254.99, 386.79 248.97, 398.06 236.05 M385.99 256.04 C389.48 251.84, 392.18 249.55, 394.52 246.23 M385.99 256.04 C389.44 252.25, 392.29 247.67, 394.52 246.23" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C123.14 1.36, 217.94 1.27, 366 0 M32 0 C131.29 -1.79, 231.25 -1.6, 366 0 M366 0 C387.8 0.82, 396.14 9, 398 32 M366 0 C388.4 -1.28, 396.3 11.05, 398 32 M398 32 C396.68 77, 397.3 120.08, 398 228 M398 32 C397.91 99.78, 396.94 166.68, 398 228 M398 228 C399.84 247.36, 385.48 260.38, 366 260 M398 228 C399.48 250.74, 389.16 259.77, 366 260 M366 260 C278.41 260.83, 191.33 259.2, 32 260 M366 260 C254.85 261.48, 142.72 261.51, 32 260 M32 260 C9.06 261.46, -1.27 247.64, 0 228 M32 260 C8.96 260.25, 0.79 249.13, 0 228 M0 228 C-2.62 187.23, -0.98 150.02, 0 32 M0 228 C-1.12 169.92, -1.74 111.79, 0 32 M0 32 C-0.23 11.83, 11.85 0.53, 32 0 M0 32 C1.74 9.13, 11.06 1.81, 32 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(284.59193843887 276.159696266393) rotate(0 182.81249999999994 124.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: keep</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;directory-with-keep-digest&gt;</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 1</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files:</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: .keep</text><text x="0" y="134.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;empty-blob-digest&gt;</text><text x="0" y="153.6" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="172.79999999999998" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="192" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks:</text><text x="0" y="211.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: aa</text><text x="0" y="230.39999999999998" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   target: /nix/store/somewhereelse</text></g><g stroke-linecap="round" transform="translate(292.58984375 581.578125) rotate(0 192 36.5)"><path d="M4.86 4.22 C4.86 4.22, 4.86 4.22, 4.86 4.22 M4.86 4.22 C4.86 4.22, 4.86 4.22, 4.86 4.22 M3.28 12.13 C5.02 8.82, 8.25 4.62, 11.81 2.32 M3.28 12.13 C6.19 8.3, 9.57 5.6, 11.81 2.32 M1.71 20.04 C8.34 16.13, 10.93 7.61, 18.77 0.41 M1.71 20.04 C6.51 15.46, 9.85 9.62, 18.77 0.41 M2.11 25.68 C10.79 16.53, 14.3 8.85, 23.76 0.77 M2.11 25.68 C8.12 18.52, 15.95 11.63, 23.76 0.77 M1.84 32.08 C8.84 22.24, 16.07 17.29, 29.4 0.38 M1.84 32.08 C11.36 20.47, 23.16 9.07, 29.4 0.38 M2.24 37.72 C9.27 29.77, 15.4 22.85, 34.39 0.74 M2.24 37.72 C15.42 23.1, 27.12 8.74, 34.39 0.74 M2.63 43.36 C8.76 33.58, 17 25.79, 40.03 0.35 M2.63 43.36 C13.14 31.06, 23.17 18.26, 40.03 0.35 M2.37 49.76 C16.36 34.07, 29.4 16.32, 45.02 0.71 M2.37 49.76 C17.93 32.43, 33.93 13.74, 45.02 0.71 M2.77 55.41 C19.05 35.42, 38.7 10.68, 50.66 0.31 M2.77 55.41 C19.96 34.91, 39.43 13.51, 50.66 0.31 M1.19 63.31 C17.21 49, 29.42 31.12, 55.65 0.67 M1.19 63.31 C15.9 46.95, 29.88 30.05, 55.65 0.67 M4.21 65.94 C21.41 44.81, 41.02 26.02, 61.29 0.28 M4.21 65.94 C24.28 42.91, 45.96 20.08, 61.29 0.28 M6.57 69.32 C23.78 51.2, 36.27 33.53, 66.28 0.64 M6.57 69.32 C19.35 53.63, 31.93 38.99, 66.28 0.64 M9.59 71.94 C25.17 56.48, 40.49 38.06, 71.92 0.24 M9.59 71.94 C23.84 58.2, 36.53 43.78, 71.92 0.24 M13.92 73.05 C31.98 52.05, 54.85 25.68, 76.91 0.6 M13.92 73.05 C30.95 53.92, 48.62 33.36, 76.91 0.6 M19.57 72.66 C38.35 51.77, 53.43 34.49, 82.55 0.21 M19.57 72.66 C32.02 56.9, 44.97 41.55, 82.55 0.21 M24.55 73.02 C48.11 46.73, 66.96 24.34, 87.54 0.57 M24.55 73.02 C47.62 46.37, 71.59 19.54, 87.54 0.57 M30.2 72.63 C46.25 51.67, 67.22 31.55, 93.18 0.17 M30.2 72.63 C53.38 46.94, 75.41 21.61, 93.18 0.17 M35.18 72.99 C55.38 48.59, 74.75 27.94, 98.17 0.53 M35.18 72.99 C57.1 47.59, 77.7 22.46, 98.17 0.53 M40.83 72.59 C64.45 43.72, 89.12 19.76, 103.81 0.14 M40.83 72.59 C61.26 47.84, 82.21 23.37, 103.81 0.14 M45.81 72.95 C66.24 52.79, 83.08 30.43, 108.8 0.5 M45.81 72.95 C67.8 48.81, 88.58 24.77, 108.8 0.5 M51.46 72.56 C64.09 57.9, 78.25 44.63, 113.78 0.86 M51.46 72.56 C72.83 47.42, 94.74 22.72, 113.78 0.86 M56.44 72.92 C74.65 52.58, 96.04 31.05, 119.43 0.46 M56.44 72.92 C81.73 45.16, 105.23 18.3, 119.43 0.46 M62.09 72.52 C80.07 51.93, 98.45 29.82, 124.41 0.82 M62.09 72.52 C79.04 54.05, 95.96 34.96, 124.41 0.82 M67.07 72.88 C80.12 59.08, 92.94 42.21, 130.06 0.43 M67.07 72.88 C80.66 58.14, 95.45 40.91, 130.06 0.43 M72.06 73.24 C93.79 49.66, 111.38 26.32, 135.04 0.79 M72.06 73.24 C89.58 51.72, 107.82 32.09, 135.04 0.79 M77.7 72.85 C104.72 44.35, 127.17 17, 140.69 0.4 M77.7 72.85 C96.13 51.38, 114.98 29.95, 140.69 0.4 M82.69 73.21 C104.71 47.57, 127.59 22.31, 145.67 0.76 M82.69 73.21 C101.71 51.47, 120.77 29.01, 145.67 0.76 M88.33 72.81 C110.76 47.24, 131.64 20.5, 151.32 0.36 M88.33 72.81 C109 48.14, 130.72 23.09, 151.32 0.36 M93.32 73.17 C111.15 55.01, 126.29 31.78, 156.3 0.72 M93.32 73.17 C110.49 51.39, 129.9 29.91, 156.3 0.72 M98.96 72.78 C118.26 54.28, 133.1 34.79, 161.95 0.33 M98.96 72.78 C111.06 59.03, 124.81 44.07, 161.95 0.33 M103.95 73.14 C115.72 58.92, 129.64 40.85, 166.93 0.69 M103.95 73.14 C126.22 47.2, 149.58 21.56, 166.93 0.69 M109.59 72.74 C123.31 56.85, 140.06 38.01, 172.58 0.29 M109.59 72.74 C124.38 56.56, 137.2 40.37, 172.58 0.29 M114.58 73.1 C138.07 45.66, 163.14 15.75, 177.56 0.65 M114.58 73.1 C131.86 52.94, 147.99 32.77, 177.56 0.65 M120.22 72.71 C133.34 57.46, 150.05 38.7, 183.21 0.26 M120.22 72.71 C135.3 57.12, 148.59 39.51, 183.21 0.26 M125.21 73.07 C147.35 45.64, 169.73 21.54, 188.19 0.62 M125.21 73.07 C146.83 48.56, 167.28 24.07, 188.19 0.62 M130.85 72.68 C149.9 47.27, 173.04 22.78, 193.84 0.22 M130.85 72.68 C153.31 46.45, 175.71 22.68, 193.84 0.22 M135.84 73.04 C157.75 47.97, 181.46 23.22, 198.82 0.58 M135.84 73.04 C157.01 47.37, 178.52 21.52, 198.82 0.58 M141.49 72.64 C163.6 47.54, 184.92 24.28, 203.81 0.94 M141.49 72.64 C157.97 55.11, 171.75 36.25, 203.81 0.94 M146.47 73 C164.74 50.07, 184.75 31.86, 209.45 0.55 M146.47 73 C169.77 45.07, 195.2 17.9, 209.45 0.55 M152.12 72.61 C170.98 50.38, 191.11 25.57, 214.44 0.91 M152.12 72.61 C167.16 54.48, 184.74 36.2, 214.44 0.91 M157.1 72.97 C171.7 55.53, 188.24 37.45, 220.08 0.51 M157.1 72.97 C171.53 57.7, 184.96 42.02, 220.08 0.51 M162.09 73.33 C182.79 52.34, 200.46 29.24, 225.07 0.87 M162.09 73.33 C176.54 57.94, 188.74 43.02, 225.07 0.87 M167.73 72.93 C187.78 52.57, 204.67 31.34, 230.71 0.48 M167.73 72.93 C192.31 45.48, 215.11 18.16, 230.71 0.48 M172.72 73.29 C192.03 53.03, 213.93 26.16, 235.7 0.84 M172.72 73.29 C187.14 54.62, 203.69 37.67, 235.7 0.84 M178.36 72.9 C196.89 49.98, 218.6 27.24, 241.34 0.45 M178.36 72.9 C191.33 57.21, 204.9 42.89, 241.34 0.45 M183.35 73.26 C207.11 47.48, 230.29 17.13, 246.33 0.81 M183.35 73.26 C195.69 56.95, 208.85 41.55, 246.33 0.81 M188.99 72.86 C203.94 55.74, 221.89 34.74, 251.97 0.41 M188.99 72.86 C203.18 55.08, 218.22 38.46, 251.97 0.41 M193.98 73.22 C219.55 44.07, 240.87 19.79, 256.96 0.77 M193.98 73.22 C208.25 55.94, 223.54 39.09, 256.96 0.77 M199.62 72.83 C217.99 48.42, 241.49 28.33, 262.6 0.38 M199.62 72.83 C214.61 56.41, 229.03 40.98, 262.6 0.38 M204.61 73.19 C223.33 53.31, 241.74 30.68, 267.59 0.74 M204.61 73.19 C229.17 46.22, 251.07 19.59, 267.59 0.74 M210.25 72.79 C224.49 54.46, 241.18 34.58, 273.23 0.34 M210.25 72.79 C227.21 52.37, 245.03 32.68, 273.23 0.34 M215.24 73.15 C229.43 57.1, 240.31 42.47, 278.22 0.7 M215.24 73.15 C237.32 48.52, 258.37 22.2, 278.22 0.7 M220.88 72.76 C240.5 49.7, 259.92 29.24, 283.86 0.31 M220.88 72.76 C245.23 46.31, 268.29 19.17, 283.86 0.31 M225.87 73.12 C244.42 48.72, 265.88 27.95, 288.85 0.67 M225.87 73.12 C251.66 44.25, 275.97 16.49, 288.85 0.67 M231.51 72.72 C253.24 45.13, 279.63 17.21, 293.84 1.03 M231.51 72.72 C244.39 57.29, 257.3 41.73, 293.84 1.03 M236.5 73.08 C256.68 52.52, 274.01 31.26, 299.48 0.63 M236.5 73.08 C252.49 52.88, 270.22 34.34, 299.48 0.63 M242.14 72.69 C264.56 48.95, 287.34 23.02, 304.47 0.99 M242.14 72.69 C265.99 45.72, 289.13 17.36, 304.47 0.99 M247.13 73.05 C268.76 49.79, 290 25.19, 310.11 0.6 M247.13 73.05 C271.93 45.31, 297.4 16.35, 310.11 0.6 M252.77 72.66 C272.81 49.72, 292.69 22.65, 315.1 0.96 M252.77 72.66 C274.82 46.14, 296.99 21.06, 315.1 0.96 M257.76 73.02 C279.06 47.65, 296.04 26.13, 320.74 0.56 M257.76 73.02 C279.13 48.78, 298.14 26.64, 320.74 0.56 M262.75 73.38 C286.03 44.74, 309.2 19.59, 325.73 0.92 M262.75 73.38 C284.29 48.03, 306.79 23.96, 325.73 0.92 M268.39 72.98 C284.12 55.13, 298.79 37.14, 331.37 0.53 M268.39 72.98 C286.16 50.17, 306.05 30.27, 331.37 0.53 M273.38 73.34 C296.56 45.22, 323.75 15.16, 336.36 0.89 M273.38 73.34 C289.13 55.59, 302.31 40.33, 336.36 0.89 M279.02 72.95 C303.53 43.87, 326.85 18.13, 342 0.49 M279.02 72.95 C293.37 55.49, 308.15 39.18, 342 0.49 M284.01 73.31 C298.42 57.71, 311.23 40.55, 346.99 0.85 M284.01 73.31 C308.02 43.63, 333.49 16.03, 346.99 0.85 M289.65 72.91 C311.51 49.12, 331.37 27.36, 352.63 0.46 M289.65 72.91 C314.28 43.1, 339.52 15.91, 352.63 0.46 M294.64 73.27 C311.15 55.94, 323.35 38.14, 357.62 0.82 M294.64 73.27 C315.12 50.08, 336.79 25.51, 357.62 0.82 M300.28 72.88 C323.32 47.78, 344.6 22, 363.26 0.43 M300.28 72.88 C320.59 50.02, 339.32 28.35, 363.26 0.43 M305.27 73.24 C321.19 53.97, 335.23 38.9, 368.25 0.79 M305.27 73.24 C320.38 55.99, 334.56 39.62, 368.25 0.79 M310.91 72.84 C326.89 57.23, 339.69 36.32, 372.58 1.9 M310.91 72.84 C323.53 58.18, 338.06 42.96, 372.58 1.9 M315.9 73.2 C339.86 47.6, 361.01 22.6, 376.91 3.02 M315.9 73.2 C338.69 46.17, 361.68 20.58, 376.91 3.02 M321.54 72.81 C338.51 50.61, 357.46 32.27, 379.93 5.64 M321.54 72.81 C336.35 54.99, 349.93 38.93, 379.93 5.64 M326.53 73.17 C341.29 57.14, 355.14 36.64, 382.29 9.02 M326.53 73.17 C347.2 48.97, 367.48 26.48, 382.29 9.02 M332.17 72.77 C348.91 51.78, 366.05 34.35, 384.66 12.4 M332.17 72.77 C345.89 56.87, 360.02 41.97, 384.66 12.4 M337.16 73.13 C350.96 56.83, 362.64 44.64, 384.39 18.8 M337.16 73.13 C350.38 58.41, 363.8 42.97, 384.39 18.8 M342.8 72.74 C358.9 55.85, 372.92 38.47, 384.13 25.19 M342.8 72.74 C359.81 54.2, 374.02 35.6, 384.13 25.19 M347.79 73.1 C354.66 65.33, 364.11 55.68, 384.53 30.84 M347.79 73.1 C358.88 60.26, 371.55 46.08, 384.53 30.84 M352.77 73.46 C358.94 66.21, 369.89 55.52, 384.27 37.23 M352.77 73.46 C360.31 65.04, 369.69 54.26, 384.27 37.23 M358.42 73.07 C364.98 65.59, 369.02 58.96, 384 43.63 M358.42 73.07 C368.68 61.66, 377.08 51.51, 384 43.63 M363.4 73.43 C367.59 70.26, 373.45 61.3, 384.4 49.27 M363.4 73.43 C367.97 69.41, 372.38 63.07, 384.4 49.27 M367.74 74.54 C374.28 66.78, 379.58 61.94, 384.14 55.67 M367.74 74.54 C374.31 67.19, 380 58.66, 384.14 55.67 M374.69 72.64 C376.19 69.99, 382.01 66.54, 385.19 60.56 M374.69 72.64 C377.37 68.95, 380.91 65.58, 385.19 60.56" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M18.25 0 M18.25 0 C129.54 2.72, 242.28 1.14, 365.75 0 M18.25 0 C127.66 -1.41, 238.58 -1.21, 365.75 0 M365.75 0 C378.27 0.9, 382.95 5.36, 384 18.25 M365.75 0 C380.07 0.47, 383.93 4.97, 384 18.25 M384 18.25 C384.02 33.84, 384.87 48.45, 384 54.75 M384 18.25 C384.01 28.7, 384.22 39.53, 384 54.75 M384 54.75 C382.32 67.61, 378.7 73.98, 365.75 73 M384 54.75 C385.93 68.19, 378.42 74.36, 365.75 73 M365.75 73 C288.24 70.59, 213.1 72.01, 18.25 73 M365.75 73 C265.08 74.84, 163.23 75.38, 18.25 73 M18.25 73 C7.8 72.9, 1.58 68.54, 0 54.75 M18.25 73 C7.06 72.31, -0.46 65.42, 0 54.75 M0 54.75 C0.31 45.73, -1.55 34.03, 0 18.25 M0 54.75 C0.72 40.09, -0.05 27.49, 0 18.25 M0 18.25 C1.55 7.28, 6.06 -1.74, 18.25 0 M0 18.25 C1.91 4.6, 5.29 1.6, 18.25 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(297.58984375 586.578125) rotate(0 70.3125 28.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories: []</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files: []</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g stroke-linecap="round" transform="translate(297.01171875 700.19140625) rotate(0 192 63)"><path d="M4.68 12.02 C4.68 12.02, 4.68 12.02, 4.68 12.02 M4.68 12.02 C4.68 12.02, 4.68 12.02, 4.68 12.02 M1.14 22.19 C5.68 16.12, 10.34 11.35, 18.19 2.57 M1.14 22.19 C5.63 15.12, 11.63 10.54, 18.19 2.57 M2.84 26.32 C9.4 19.34, 14.38 14.47, 24.49 1.42 M2.84 26.32 C7.02 21.88, 12.43 16.24, 24.49 1.42 M3.24 31.97 C12.37 22.32, 23.31 9.97, 30.14 1.02 M3.24 31.97 C8.75 26.69, 14.07 19.43, 30.14 1.02 M2.98 38.36 C11.61 29.09, 22.34 16.73, 34.47 2.14 M2.98 38.36 C14.61 26.7, 24.62 13.57, 34.47 2.14 M2.72 44.76 C8.44 35.48, 20.15 28.58, 39.46 2.5 M2.72 44.76 C9.81 35.38, 16.68 27.81, 39.46 2.5 M3.11 50.4 C17.54 33.89, 29.94 20.31, 45.1 2.1 M3.11 50.4 C20.74 31.63, 36.06 13.37, 45.1 2.1 M2.85 56.8 C18.58 35.97, 39.38 14.61, 50.09 2.46 M2.85 56.8 C15.02 42.55, 25.62 29.03, 50.09 2.46 M3.24 62.45 C13.28 49.17, 26.13 38.7, 55.73 2.07 M3.24 62.45 C14.41 50.21, 23.72 38.66, 55.73 2.07 M2.98 68.84 C13.36 56.63, 24.49 40.18, 60.72 2.43 M2.98 68.84 C25.38 43.36, 46.78 18.82, 60.72 2.43 M2.72 75.24 C27.68 45.78, 49.87 21.46, 66.36 2.03 M2.72 75.24 C27.36 48.58, 50.99 21.41, 66.36 2.03 M3.12 80.88 C26.98 53.98, 51.79 26.74, 71.35 2.39 M3.12 80.88 C24.82 57.11, 46.21 30.7, 71.35 2.39 M2.85 87.28 C25.83 58.82, 49.6 32.81, 76.33 2.75 M2.85 87.28 C18.86 69.84, 32.85 53.27, 76.33 2.75 M3.25 92.92 C31.02 64.11, 55.21 32.05, 81.98 2.36 M3.25 92.92 C29.09 64.51, 54.01 35.08, 81.98 2.36 M0.36 102.34 C18.06 82.88, 34.35 63.19, 86.96 2.72 M0.36 102.34 C22.5 76.81, 45.99 50.02, 86.96 2.72 M2.07 106.47 C35.94 64.04, 71.16 24.59, 92.61 2.33 M2.07 106.47 C29.3 74.98, 57.21 42, 92.61 2.33 M3.78 110.61 C32.64 72.16, 65.08 38.52, 97.59 2.69 M3.78 110.61 C27.71 83.59, 50.59 56.73, 97.59 2.69 M5.48 114.74 C31.81 84.38, 61.24 51.3, 103.24 2.29 M5.48 114.74 C38.37 76.78, 69.65 40.61, 103.24 2.29 M7.85 118.12 C37.24 84.78, 67.86 51.75, 108.22 2.65 M7.85 118.12 C40.51 79.72, 74.23 40.65, 108.22 2.65 M10.86 120.75 C33.15 93.28, 59.27 65.67, 113.87 2.26 M10.86 120.75 C41.44 83.49, 74.67 47.06, 113.87 2.26 M14.54 122.62 C54.9 77.33, 93.45 31.72, 118.85 2.62 M14.54 122.62 C36.96 96.13, 58.59 69.9, 118.85 2.62 M18.21 124.48 C60.03 78.24, 98.36 27.73, 124.5 2.22 M18.21 124.48 C47.99 89.82, 78.58 52.73, 124.5 2.22 M23.2 124.84 C60.93 81.34, 96.9 37.41, 129.48 2.58 M23.2 124.84 C51.15 90.76, 80.7 57.55, 129.48 2.58 M27.53 125.96 C67.64 79.48, 108.95 32.16, 135.13 2.19 M27.53 125.96 C50.35 100, 70.45 76.16, 135.13 2.19 M32.52 126.32 C67.96 82.8, 105.29 42.95, 140.11 2.55 M32.52 126.32 C55.09 102.84, 75.65 76.61, 140.11 2.55 M37.51 126.68 C76.58 83.49, 111.39 39.46, 145.76 2.15 M37.51 126.68 C68.68 90.35, 99.78 54.4, 145.76 2.15 M43.15 126.29 C79.05 86.81, 113.32 46.01, 150.74 2.51 M43.15 126.29 C83.16 78.74, 122.47 32.63, 150.74 2.51 M48.14 126.65 C84.95 86.46, 121.01 43.96, 156.39 2.12 M48.14 126.65 C77.47 92.07, 106.15 57.86, 156.39 2.12 M53.12 127.01 C94.06 83.46, 132.13 35.83, 161.37 2.48 M53.12 127.01 C83.35 90.25, 115.88 54.9, 161.37 2.48 M58.77 126.61 C88.9 91.94, 120.62 55.5, 167.02 2.08 M58.77 126.61 C80.71 101.71, 103.08 74.94, 167.02 2.08 M63.75 126.97 C94.83 94.26, 121.35 59.65, 172 2.44 M63.75 126.97 C99.19 87.16, 134.08 46.33, 172 2.44 M69.4 126.58 C96.06 98.29, 123.08 66.58, 177.65 2.05 M69.4 126.58 C94.05 98.86, 118.43 72.82, 177.65 2.05 M74.38 126.94 C110.22 87.09, 146.32 44.12, 182.63 2.41 M74.38 126.94 C112.7 80.36, 152.52 34.24, 182.63 2.41 M80.03 126.54 C112.63 85.84, 146.64 47.21, 188.28 2.01 M80.03 126.54 C118.52 83.47, 154.62 39.47, 188.28 2.01 M85.01 126.9 C123.66 84.49, 160.64 41.03, 193.26 2.37 M85.01 126.9 C107.24 101.3, 128.84 75.24, 193.26 2.37 M90.66 126.51 C128.32 79.98, 168.35 34.11, 198.25 2.73 M90.66 126.51 C125.23 87.51, 159.47 48.47, 198.25 2.73 M95.64 126.87 C136.32 80.89, 176.13 32.73, 203.89 2.34 M95.64 126.87 C130.67 85.7, 167.03 45.03, 203.89 2.34 M101.29 126.47 C138.72 85.68, 174.7 42.24, 208.88 2.7 M101.29 126.47 C141.27 81.26, 181.48 32.8, 208.88 2.7 M106.27 126.83 C133.13 96.99, 159.87 69.21, 214.52 2.31 M106.27 126.83 C139.22 87.92, 173.99 50.04, 214.52 2.31 M111.92 126.44 C139.17 92.95, 170.03 64.03, 219.51 2.67 M111.92 126.44 C140.35 94.34, 169.24 63.52, 219.51 2.67 M116.9 126.8 C139.41 102.01, 161.49 74.65, 225.15 2.27 M116.9 126.8 C143.96 97.47, 169.6 65.58, 225.15 2.27 M122.55 126.4 C151.81 92.7, 184.46 60.16, 230.14 2.63 M122.55 126.4 C144.55 101.98, 167.71 77.15, 230.14 2.63 M127.53 126.76 C155.24 93.9, 184.87 59.39, 235.78 2.24 M127.53 126.76 C162.55 85.69, 197.14 44.32, 235.78 2.24 M133.18 126.37 C156.24 97.59, 181.25 74.22, 240.77 2.6 M133.18 126.37 C169.91 83.87, 205.04 43.06, 240.77 2.6 M138.16 126.73 C171.62 83.67, 210.01 43.26, 246.41 2.2 M138.16 126.73 C162.75 98.55, 185.88 71.86, 246.41 2.2 M143.81 126.33 C167.53 96.38, 195.44 68.77, 251.4 2.56 M143.81 126.33 C175.38 87.65, 208.52 52.45, 251.4 2.56 M148.79 126.69 C186.15 81.29, 226.32 36.57, 257.04 2.17 M148.79 126.69 C185.38 86.13, 221.92 44.72, 257.04 2.17 M154.44 126.3 C190.78 83.15, 229.34 36.03, 262.03 2.53 M154.44 126.3 C178.51 97.01, 203.83 70.69, 262.03 2.53 M159.42 126.66 C189.83 93.77, 217.8 63.16, 267.67 2.13 M159.42 126.66 C196.22 84.11, 234.53 42.17, 267.67 2.13 M165.07 126.27 C199.58 88.17, 233.46 45.58, 272.66 2.49 M165.07 126.27 C204.81 83.18, 243.36 38.31, 272.66 2.49 M170.05 126.63 C191.4 102.17, 214.65 75.55, 278.3 2.1 M170.05 126.63 C194.79 98.87, 219.21 68.85, 278.3 2.1 M175.04 126.99 C218.62 76.89, 259.45 31.99, 283.29 2.46 M175.04 126.99 C200.78 100.7, 225.33 71.6, 283.29 2.46 M180.68 126.59 C214.02 88.6, 246.67 51.74, 288.93 2.06 M180.68 126.59 C212.29 91.19, 241.62 56.43, 288.93 2.06 M185.67 126.95 C215.71 95.07, 243.49 60.99, 293.92 2.42 M185.67 126.95 C216.01 94.84, 243.45 62.69, 293.92 2.42 M191.31 126.56 C229.63 85.76, 263.97 41.71, 299.56 2.03 M191.31 126.56 C222.59 88.19, 255.96 51.19, 299.56 2.03 M196.3 126.92 C217.19 100.03, 243 74.87, 304.55 2.39 M196.3 126.92 C218.88 99.48, 243.28 72.58, 304.55 2.39 M201.94 126.52 C245.64 78.21, 284.33 30.24, 309.54 2.75 M201.94 126.52 C240.53 84.15, 277.64 41.48, 309.54 2.75 M206.93 126.88 C248.75 80.42, 287.71 35.97, 315.18 2.36 M206.93 126.88 C240.59 87.82, 273.05 49.59, 315.18 2.36 M212.57 126.49 C242.06 93.98, 270.55 61.9, 320.17 2.72 M212.57 126.49 C238.53 99.42, 261.94 72.17, 320.17 2.72 M217.56 126.85 C255.21 80.1, 296.4 36.1, 325.81 2.32 M217.56 126.85 C248.46 91.27, 280.97 56.5, 325.81 2.32 M223.2 126.45 C245.32 96.89, 272.92 68.02, 330.8 2.68 M223.2 126.45 C246.93 99.22, 269.26 73.84, 330.8 2.68 M228.19 126.81 C266.27 81.56, 302.8 39.44, 336.44 2.29 M228.19 126.81 C268.68 80.87, 310.31 31.49, 336.44 2.29 M233.83 126.42 C261.99 92.9, 293.86 58.89, 341.43 2.65 M233.83 126.42 C265.72 90.09, 297.62 53.24, 341.43 2.65 M238.82 126.78 C276.56 81.01, 314.76 40.31, 347.07 2.25 M238.82 126.78 C275.07 83.85, 309.78 43.16, 347.07 2.25 M244.47 126.38 C281.08 86.31, 311.83 47.08, 353.37 1.1 M244.47 126.38 C284.42 79.98, 324.2 34.41, 353.37 1.1 M249.45 126.74 C283.83 89.61, 316.66 48.25, 358.36 1.46 M249.45 126.74 C288.06 81.49, 329.09 35.62, 358.36 1.46 M255.1 126.35 C286.95 88.5, 321.78 49.74, 362.69 2.58 M255.1 126.35 C278.54 101.51, 299.57 76.6, 362.69 2.58 M260.08 126.71 C289.9 92.16, 318.76 59.09, 367.68 2.94 M260.08 126.71 C286.6 97.68, 309.24 69.16, 367.68 2.94 M265.73 126.32 C304.67 81.34, 343.89 36.13, 371.35 4.81 M265.73 126.32 C296.05 89.95, 328.32 54.46, 371.35 4.81 M270.71 126.68 C312.11 79.85, 350.1 38.08, 375.03 6.68 M270.71 126.68 C308.29 85.27, 344.32 43.1, 375.03 6.68 M276.36 126.28 C302.37 95.05, 332.55 61.6, 377.39 10.06 M276.36 126.28 C300.17 99.46, 322.39 72.19, 377.39 10.06 M281.34 126.64 C304.76 96.82, 332.3 67.75, 380.41 12.68 M281.34 126.64 C307.49 95.18, 332.91 65.5, 380.41 12.68 M286.33 127 C314.42 93.01, 345.57 57.56, 381.46 17.57 M286.33 127 C313.51 94.85, 341.77 62.68, 381.46 17.57 M291.97 126.61 C313.48 100.45, 339.17 75.97, 383.16 21.7 M291.97 126.61 C311.58 103.09, 333.11 79.56, 383.16 21.7 M296.96 126.97 C320.59 100.34, 342.62 72.36, 384.87 25.84 M296.96 126.97 C321.66 99.71, 344.27 72.56, 384.87 25.84 M302.6 126.57 C334.11 86.35, 368.78 51.97, 385.27 31.48 M302.6 126.57 C330.75 94.29, 357.85 62.13, 385.27 31.48 M307.59 126.93 C326.81 105.47, 343.03 83.78, 385 37.88 M307.59 126.93 C327.54 105.51, 344.6 84.21, 385 37.88 M313.23 126.54 C332.05 106.16, 349.05 86.64, 385.4 43.52 M313.23 126.54 C332.89 103.96, 352.85 83.32, 385.4 43.52 M318.22 126.9 C331.98 110.58, 346.57 93.14, 385.14 49.92 M318.22 126.9 C331.03 111.1, 344.19 94.95, 385.14 49.92 M323.86 126.5 C335.72 108.89, 350.56 93.22, 385.53 55.56 M323.86 126.5 C345.11 101.47, 364.04 79.57, 385.53 55.56 M328.85 126.86 C346.1 108.02, 361.21 90.1, 385.27 61.96 M328.85 126.86 C342.2 112.06, 355.94 96.74, 385.27 61.96 M334.49 126.47 C350.84 109.97, 364.71 94.6, 385.67 67.6 M334.49 126.47 C353.49 104.81, 372.17 82.14, 385.67 67.6 M339.48 126.83 C348.96 114.71, 363.17 101.01, 385.4 74 M339.48 126.83 C354.75 110.09, 368.15 93.09, 385.4 74 M345.12 126.43 C354.84 117.22, 365.07 103.77, 385.8 79.64 M345.12 126.43 C358.46 110.23, 371.31 95.91, 385.8 79.64 M350.11 126.79 C364.3 111.08, 375.22 97.18, 385.54 86.04 M350.11 126.79 C363.14 112.28, 374.94 98.31, 385.54 86.04 M354.44 127.91 C362.81 117.31, 370.87 110.18, 385.28 92.44 M354.44 127.91 C362.9 118.85, 369.83 111.91, 385.28 92.44 M360.74 126.76 C367.63 119.85, 374.61 113.88, 386.98 96.57 M360.74 126.76 C369 116.72, 377.31 108.85, 386.98 96.57 M367.7 124.86 C371.04 118.25, 376.15 112.61, 382.13 108.25 M367.7 124.86 C371.11 120.48, 374.22 117.39, 382.13 108.25" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M31.5 0 M31.5 0 C144.73 2.16, 257.89 1.55, 352.5 0 M31.5 0 C123.21 -0.03, 215.81 -0.04, 352.5 0 M352.5 0 C373.48 -1.54, 384.54 11.54, 384 31.5 M352.5 0 C371.52 1.89, 382.06 11.18, 384 31.5 M384 31.5 C384.2 57.65, 382.83 81.45, 384 94.5 M384 31.5 C384.94 50.55, 383.94 67.7, 384 94.5 M384 94.5 C385.07 116.86, 374.35 127.03, 352.5 126 M384 94.5 C385.17 117.04, 373.57 125.11, 352.5 126 M352.5 126 C247.11 128.47, 140.42 127.09, 31.5 126 M352.5 126 C271.97 126.76, 193.29 127.03, 31.5 126 M31.5 126 C11.34 124.76, -1.26 114.07, 0 94.5 M31.5 126 C9.77 126.57, -1.64 113.31, 0 94.5 M0 94.5 C0.36 74.76, 1.16 53.5, 0 31.5 M0 94.5 C-0.19 81.51, -0.09 68.38, 0 31.5 M0 31.5 C-0.91 8.69, 10.9 0.79, 31.5 0 M0 31.5 C2.16 12.64, 11.39 -0.47, 31.5 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(302.01171875 705.19140625) rotate(0 145.3125 57.60000000000002)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: a</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;directory-a-digest&gt;</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files: []</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g transform="translate(303.75390625 45.62890625) rotate(0 89.0625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_WITH_KEEP</text></g><g transform="translate(308.25390625 240.4765625) rotate(0 98.4375 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_COMPLICATED</text></g><g transform="translate(307.28125 554.6148437500001) rotate(0 51.5625 9.600000000000023)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_A</text></g><g transform="translate(310.6875 674.4585937500001) rotate(0 51.5625 9.600000000000023)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_B</text></g><g transform="translate(18.95703125 42.53671874999998) rotate(0 28.125 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_A</text></g><g transform="translate(22.55078125 130.24374999999998) rotate(0 28.125 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_B</text></g><g transform="translate(13.5546875 210.27109374999998) rotate(0 46.875 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_EMPTY</text></g><g stroke-linecap="round"><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-0.52 1.13 C-60.08 19.9, -297.46 94.83, -357.2 113.61 M1.4 0.68 C-58.25 18.98, -298.08 93.09, -357.74 111.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-331.96 94.73 C-340.78 97.36, -347.89 102.93, -357.37 112.7 M-333.95 93.43 C-339.75 97.47, -345.42 102.99, -358.69 111" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-325.89 114.33 C-336.76 110.86, -345.74 110.35, -357.37 112.7 M-327.89 113.04 C-335.08 112.25, -342.26 112.89, -358.69 111" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M-0.42 1.04 C43.76 -38.95, 220.7 -199.19, 264.79 -239.05 M1.55 0.54 C45.63 -39.42, 220.36 -198.5, 263.94 -238.07" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M251.72 -211.19 C251.81 -219.53, 257.18 -225.36, 265.07 -236.65 M250.73 -212.1 C253.06 -218.02, 257.3 -223.31, 264.08 -238.4" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M237.91 -226.37 C241.88 -230.52, 251.02 -232.22, 265.07 -236.65 M236.92 -227.29 C242.47 -229.5, 250.03 -231.14, 264.08 -238.4" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M0.21 0.21 C-60.17 -24.98, -301.37 -126.03, -361.5 -151.72 M-1.14 -0.73 C-61.8 -26.18, -302.73 -127.84, -362.74 -153.25" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M-332.43 -152.64 C-341.83 -151.66, -356.48 -151.02, -362.88 -151.77 M-332.23 -151.12 C-344.25 -151.99, -356 -152.45, -362.93 -152.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M-340.42 -133.74 C-346.81 -139.68, -358.54 -145.95, -362.88 -151.77 M-340.22 -132.22 C-349.22 -140.26, -357.91 -147.96, -362.93 -152.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M-0.84 0.01 C13.92 -16, 74.3 -80.87, 89.45 -97.18 M0.92 -1.04 C15.95 -16.73, 76.9 -79.62, 91.58 -95.58" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M79.65 -69.91 C84.59 -79.16, 87.99 -87.88, 90.6 -97.23 M78.91 -67.92 C83.62 -76.08, 87.25 -85.83, 90.62 -96.45" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M64.75 -84.03 C75.18 -87.85, 84.2 -91.25, 90.6 -97.23 M64.01 -82.04 C73.25 -85.89, 81.47 -91.29, 90.62 -96.45" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g transform="translate(796.08984375 10.884374999999977) rotate(0 37.5 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">PathInfo</text></g><g stroke-linecap="round" transform="translate(800.94921875 70.3515625) rotate(0 238.5 66.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C10.39 14.59, 12.36 7.63, 18.1 2.48 M3.66 19.09 C7.72 12.99, 12.69 8.45, 18.1 2.48 M2.75 26.24 C9.54 18.05, 20.15 8.22, 24.4 1.33 M2.75 26.24 C10.63 16.6, 19.07 7.71, 24.4 1.33 M2.48 32.64 C10.84 25.1, 18.62 16.99, 30.7 0.18 M2.48 32.64 C10.3 22, 19.4 12.2, 30.7 0.18 M2.88 38.28 C13.73 28.82, 21.58 17.53, 34.37 2.05 M2.88 38.28 C11.28 29.81, 18.49 19.2, 34.37 2.05 M2.62 44.68 C15.27 32.46, 26.63 18.87, 39.36 2.41 M2.62 44.68 C12.58 32.64, 23.98 18.88, 39.36 2.41 M2.36 51.07 C15.78 39.12, 25.44 23.33, 45 2.02 M2.36 51.07 C15.86 35.07, 30.03 18.81, 45 2.02 M2.75 56.72 C21.32 37.62, 39.51 16.97, 49.99 2.38 M2.75 56.72 C15.6 42.48, 27.62 27.63, 49.99 2.38 M2.49 63.11 C19.73 43.61, 35.49 24.47, 55.63 1.98 M2.49 63.11 C14.04 50.89, 25.8 36.73, 55.63 1.98 M2.88 68.76 C20.08 47.93, 38.2 25.58, 60.62 2.34 M2.88 68.76 C19.66 48.91, 37.22 28.6, 60.62 2.34 M2.62 75.16 C19.27 53.2, 38.94 35.17, 66.26 1.95 M2.62 75.16 C17.89 55.5, 34.42 36.25, 66.26 1.95 M2.36 81.55 C30.5 51, 58.03 17.88, 71.25 2.31 M2.36 81.55 C16.96 64.32, 31.61 47.37, 71.25 2.31 M2.76 87.2 C31.87 55.75, 56.06 22.7, 76.89 1.91 M2.76 87.2 C24.31 61.65, 46.29 35.63, 76.89 1.91 M2.49 93.59 C32.62 61.39, 61.22 28.82, 81.88 2.27 M2.49 93.59 C23.69 67.97, 45.72 43.7, 81.88 2.27 M2.89 99.24 C23.68 73.49, 45.29 48.44, 87.52 1.88 M2.89 99.24 C26.99 71.79, 50.36 44.89, 87.52 1.88 M2.63 105.64 C23.82 79.99, 47.73 56.76, 92.51 2.24 M2.63 105.64 C29.41 76.09, 53.71 47.33, 92.51 2.24 M3.02 111.28 C35.76 74.46, 69.37 34.13, 98.15 1.85 M3.02 111.28 C24.1 86.82, 46.96 62.48, 98.15 1.85 M4.73 115.41 C41.64 74.79, 76.39 31.84, 103.14 2.21 M4.73 115.41 C35.15 80.63, 65.61 44.22, 103.14 2.21 M5.12 121.06 C38.67 80.99, 74.17 42.93, 108.78 1.81 M5.12 121.06 C37.52 87.2, 67.81 51.07, 108.78 1.81 M8.14 123.68 C45.61 75.63, 85.4 30.08, 113.77 2.17 M8.14 123.68 C46.84 78.16, 85.53 34.79, 113.77 2.17 M10.51 127.06 C51.38 83, 90.78 34.09, 119.41 1.78 M10.51 127.06 C44.83 86.58, 79.34 47.77, 119.41 1.78 M13.52 129.68 C38.07 99.79, 58.32 73.05, 124.4 2.14 M13.52 129.68 C50.9 87.61, 88.06 45.68, 124.4 2.14 M17.2 131.55 C58.64 84.2, 95.67 41.29, 130.04 1.74 M17.2 131.55 C41.93 103.22, 66.7 72.66, 130.04 1.74 M20.87 133.42 C57.62 92.2, 92.65 51.53, 135.03 2.1 M20.87 133.42 C65.94 80.91, 110.67 28.91, 135.03 2.1 M25.86 133.78 C57.72 93.6, 90.77 57.22, 140.67 1.71 M25.86 133.78 C56.65 99.16, 86.75 62.82, 140.67 1.71 M30.19 134.9 C76.39 84.32, 123.04 30.78, 145.66 2.07 M30.19 134.9 C59.94 102.4, 88.51 68.91, 145.66 2.07 M35.18 135.26 C63.65 103.83, 88.19 71.92, 150.65 2.43 M35.18 135.26 C77.05 84.73, 121.83 35.2, 150.65 2.43 M40.17 135.62 C76.62 93.15, 111.75 54.6, 156.29 2.03 M40.17 135.62 C72.44 97.03, 106.71 60.31, 156.29 2.03 M45.81 135.22 C68.12 108.89, 93.36 81.85, 161.28 2.39 M45.81 135.22 C93.12 81.98, 137.9 28.67, 161.28 2.39 M50.8 135.58 C99.08 85.21, 141.4 29.85, 166.92 2 M50.8 135.58 C95.54 85.04, 137.27 35.33, 166.92 2 M56.44 135.19 C86.16 100.81, 118.44 64.39, 171.91 2.36 M56.44 135.19 C85.59 100.01, 117.34 65.71, 171.91 2.36 M61.43 135.55 C93.02 99.32, 126.23 60.67, 177.55 1.96 M61.43 135.55 C98.63 92.46, 135.75 49.47, 177.55 1.96 M66.41 135.91 C88.8 108.25, 112.86 81.48, 182.54 2.32 M66.41 135.91 C91.41 106.52, 117.33 75.61, 182.54 2.32 M72.06 135.51 C104.73 95.48, 142.51 55.2, 188.18 1.93 M72.06 135.51 C99.45 104.83, 124.74 75.89, 188.18 1.93 M77.04 135.87 C115.11 94.08, 147.51 55.24, 193.17 2.29 M77.04 135.87 C100.11 110.76, 122.58 83.57, 193.17 2.29 M82.69 135.48 C116.4 97.33, 152.41 55.98, 198.81 1.9 M82.69 135.48 C110.99 102.8, 139.68 69.49, 198.81 1.9 M87.67 135.84 C132 84.75, 175.61 37.52, 203.8 2.26 M87.67 135.84 C116.95 102.95, 145.57 69.87, 203.8 2.26 M93.32 135.44 C137.75 81.91, 184.41 30.9, 209.44 1.86 M93.32 135.44 C125.76 94, 160.68 54.19, 209.44 1.86 M98.3 135.8 C129.81 99.97, 161.28 60.26, 214.43 2.22 M98.3 135.8 C133.08 96.91, 166.85 57.98, 214.43 2.22 M103.95 135.41 C130.43 101.26, 160.91 69.64, 220.07 1.83 M103.95 135.41 C145.52 86.13, 188.77 38.68, 220.07 1.83 M108.93 135.77 C131.74 110.52, 156.29 82.4, 225.06 2.19 M108.93 135.77 C155.58 84.93, 200.54 32.47, 225.06 2.19 M114.58 135.38 C157.28 86.99, 201.96 37.19, 230.7 1.79 M114.58 135.38 C152.78 93.11, 190.04 49.2, 230.7 1.79 M119.56 135.74 C147.35 100.73, 175.19 69.8, 235.69 2.15 M119.56 135.74 C152.35 99.22, 182.76 63.91, 235.69 2.15 M125.21 135.34 C151.37 104.17, 174.15 75, 241.33 1.76 M125.21 135.34 C170.53 85.3, 211.95 36.33, 241.33 1.76 M130.19 135.7 C173.94 86.44, 220.64 33.18, 246.32 2.12 M130.19 135.7 C172.93 86.98, 216.51 38.35, 246.32 2.12 M135.84 135.31 C174.21 90.08, 208.21 49.75, 251.96 1.72 M135.84 135.31 C158.72 109.66, 182.85 82.09, 251.96 1.72 M140.82 135.67 C177.31 93.41, 212.97 53.24, 256.95 2.08 M140.82 135.67 C166.68 105.14, 194.13 75.79, 256.95 2.08 M146.47 135.27 C182.12 93.41, 217.19 51.75, 262.59 1.69 M146.47 135.27 C192.8 83.11, 239.07 30.19, 262.59 1.69 M151.45 135.63 C195.86 84.04, 242.39 29.83, 267.58 2.05 M151.45 135.63 C182.45 96.24, 215.98 59.61, 267.58 2.05 M157.1 135.24 C193.93 90.8, 230.49 48.73, 272.56 2.41 M157.1 135.24 C186.1 101.92, 213.74 67.58, 272.56 2.41 M162.08 135.6 C204.71 88.09, 242.57 43.67, 278.21 2.01 M162.08 135.6 C195.51 97.13, 227.49 59.04, 278.21 2.01 M167.73 135.2 C191.02 109.31, 213.95 82.1, 283.19 2.37 M167.73 135.2 C208.32 87.02, 247.72 39.69, 283.19 2.37 M172.71 135.56 C211.05 92.35, 249.27 45.71, 288.84 1.98 M172.71 135.56 C204.27 99.8, 232.12 66.33, 288.84 1.98 M178.36 135.17 C208.57 98.05, 240.33 61.56, 293.82 2.34 M178.36 135.17 C224.01 83.75, 267.9 32.95, 293.82 2.34 M183.34 135.53 C211.9 102.21, 241.28 69.57, 299.47 1.94 M183.34 135.53 C225.07 89.34, 264.88 43.51, 299.47 1.94 M188.33 135.89 C232.49 86.72, 275.85 35.32, 304.45 2.3 M188.33 135.89 C226.55 88.72, 267.12 42.68, 304.45 2.3 M193.97 135.49 C226.44 96.44, 258.36 62.18, 310.1 1.91 M193.97 135.49 C239.79 82.46, 285.44 30.27, 310.1 1.91 M198.96 135.85 C228.28 100.09, 257.95 68.9, 315.08 2.27 M198.96 135.85 C235.09 93.52, 272.84 52.67, 315.08 2.27 M204.6 135.46 C239.68 94.74, 275.29 55.5, 320.73 1.88 M204.6 135.46 C244.23 91.28, 283.02 46.4, 320.73 1.88 M209.59 135.82 C250.03 93.16, 286.49 47.86, 325.71 2.24 M209.59 135.82 C241.67 97.03, 276.6 58.48, 325.71 2.24 M215.23 135.42 C252.93 93.98, 290.73 49.25, 331.36 1.84 M215.23 135.42 C239.46 107.48, 265.75 77.82, 331.36 1.84 M220.22 135.78 C258.31 87.12, 298.41 41.67, 336.34 2.2 M220.22 135.78 C261.35 88.79, 302.28 41.23, 336.34 2.2 M225.86 135.39 C247.19 109.85, 271.45 83.37, 341.99 1.81 M225.86 135.39 C269.09 87.75, 311.09 39.46, 341.99 1.81 M230.85 135.75 C271.9 89.87, 311.65 42.41, 346.97 2.17 M230.85 135.75 C266.69 94.9, 300.46 56.72, 346.97 2.17 M236.49 135.36 C279.54 90.41, 318.34 43.37, 352.62 1.77 M236.49 135.36 C262.51 106.22, 289.8 75.87, 352.62 1.77 M241.48 135.72 C270.62 102.2, 297.59 68.43, 357.6 2.13 M241.48 135.72 C267.4 106.57, 291.68 77.77, 357.6 2.13 M247.12 135.32 C272.55 104.17, 301.27 75.42, 363.25 1.74 M247.12 135.32 C286.54 88.24, 325.79 43.89, 363.25 1.74 M252.11 135.68 C285.8 94.95, 321.96 52.06, 368.23 2.1 M252.11 135.68 C286.48 98.48, 319.42 59.27, 368.23 2.1 M257.75 135.29 C287.3 103.83, 313.13 73.67, 373.88 1.7 M257.75 135.29 C296.47 90.19, 336.96 43.61, 373.88 1.7 M262.74 135.65 C294.04 101.96, 321.68 67.42, 378.86 2.06 M262.74 135.65 C285.88 109.26, 307.99 80.92, 378.86 2.06 M268.38 135.25 C301.94 102.32, 329.68 63.48, 383.85 2.42 M268.38 135.25 C312.43 84.8, 355.81 34.99, 383.85 2.42 M273.37 135.61 C307.98 93.43, 343.82 50.19, 389.49 2.03 M273.37 135.61 C314.49 86.55, 355.98 39.87, 389.49 2.03 M279.01 135.22 C315.47 96.77, 347.58 56.5, 394.48 2.39 M279.01 135.22 C310.17 98.46, 342.48 62.75, 394.48 2.39 M284 135.58 C309.56 103.84, 338.63 75.13, 400.12 1.99 M284 135.58 C310.36 104.37, 338.59 71.89, 400.12 1.99 M289.64 135.18 C312.79 106.98, 337.43 80.83, 405.11 2.35 M289.64 135.18 C334.14 86.06, 378.85 34.51, 405.11 2.35 M294.63 135.54 C322.11 103.4, 351.85 69.91, 410.75 1.96 M294.63 135.54 C321.72 103.68, 349.75 71.49, 410.75 1.96 M299.62 135.9 C347.67 83.43, 392.82 28.41, 415.74 2.32 M299.62 135.9 C340.13 91.24, 379.75 47.11, 415.74 2.32 M305.26 135.51 C337.44 97.55, 370.85 61.54, 421.38 1.93 M305.26 135.51 C331.72 105.39, 358.21 73.88, 421.38 1.93 M310.25 135.87 C347.77 87.67, 388.48 42.7, 426.37 2.29 M310.25 135.87 C343.24 98.26, 374.33 62.37, 426.37 2.29 M315.89 135.47 C358.33 83.2, 405.58 35.13, 432.01 1.89 M315.89 135.47 C342.14 103.92, 367.01 74.44, 432.01 1.89 M320.88 135.83 C349.09 105.42, 377.17 73.43, 437 2.25 M320.88 135.83 C348.8 103.87, 377.25 71.06, 437 2.25 M326.52 135.44 C357.37 98.83, 392.96 61.44, 442.64 1.86 M326.52 135.44 C359.38 95.45, 395.35 54.96, 442.64 1.86 M331.51 135.8 C371.1 90.29, 414.52 41.02, 447.63 2.22 M331.51 135.8 C375.19 84.86, 419.5 34.87, 447.63 2.22 M337.15 135.41 C360.41 108.49, 389.11 78.17, 452.62 2.58 M337.15 135.41 C365.83 101.31, 393.5 68.62, 452.62 2.58 M342.14 135.77 C374.92 95.35, 408.22 55.28, 456.95 3.69 M342.14 135.77 C375.97 95.74, 410.85 54.51, 456.95 3.69 M347.78 135.37 C386.27 93.45, 423.41 49.67, 461.94 4.05 M347.78 135.37 C388.88 89.16, 428.69 44.23, 461.94 4.05 M352.77 135.73 C391.81 93.38, 429.55 47.99, 464.96 6.68 M352.77 135.73 C395.76 88.88, 437.51 38.86, 464.96 6.68 M358.41 135.34 C384.9 101.59, 410.76 73.18, 469.29 7.79 M358.41 135.34 C381.72 110.3, 403.42 84.85, 469.29 7.79 M363.4 135.7 C391.38 101.39, 419.01 68.91, 471.65 11.17 M363.4 135.7 C388 108.1, 412.92 77.82, 471.65 11.17 M369.04 135.3 C402.56 98.71, 434.28 59.98, 474.67 13.79 M369.04 135.3 C400.06 99.18, 430.91 65.19, 474.67 13.79 M374.03 135.66 C409.08 94.39, 445.61 50.9, 475.72 18.68 M374.03 135.66 C395.27 111.63, 415.98 87.61, 475.72 18.68 M379.67 135.27 C413.73 94.24, 448.47 58.31, 477.43 22.82 M379.67 135.27 C399.49 110.9, 420.37 87.43, 477.43 22.82 M384.66 135.63 C405.61 110.26, 426.94 85.23, 479.13 26.95 M384.66 135.63 C415.84 98.51, 447.61 62.33, 479.13 26.95 M390.3 135.23 C419.31 104.53, 444.38 75.12, 479.53 32.59 M390.3 135.23 C411.06 109.85, 433.81 86.46, 479.53 32.59 M395.29 135.59 C423.34 101.52, 451.07 71.77, 479.27 38.99 M395.29 135.59 C425.27 99.57, 456.57 63.69, 479.27 38.99 M400.93 135.2 C416.38 116.25, 434.8 96.63, 479 45.39 M400.93 135.2 C430.56 100.88, 460.84 68, 479 45.39 M405.92 135.56 C424.18 114.12, 445.92 87.99, 479.4 51.03 M405.92 135.56 C430.48 106.84, 457.04 77.45, 479.4 51.03 M410.91 135.92 C425.56 119.11, 439.25 103.89, 479.14 57.43 M410.91 135.92 C432.34 111.55, 454.95 85.96, 479.14 57.43 M416.55 135.52 C437.03 112.31, 458.69 88.33, 479.53 63.07 M416.55 135.52 C441.28 108.64, 464.07 81.07, 479.53 63.07 M421.54 135.88 C439.7 114.89, 456.47 92.93, 479.27 69.47 M421.54 135.88 C433.45 119.98, 448.34 104.83, 479.27 69.47 M427.18 135.49 C444.19 117.51, 459.72 100.42, 479.66 75.11 M427.18 135.49 C444.66 114.13, 462.3 93.68, 479.66 75.11 M432.17 135.85 C446.83 119.86, 461.29 103.69, 479.4 81.51 M432.17 135.85 C442.13 123.55, 452.74 111.76, 479.4 81.51 M437.81 135.46 C448.92 124.72, 457.92 112.62, 479.8 87.15 M437.81 135.46 C453.55 117.76, 467.68 101.47, 479.8 87.15 M445.42 132.8 C459.39 120.23, 469.27 105.41, 479.54 93.55 M445.42 132.8 C454.06 123.31, 462.94 111.17, 479.54 93.55 M451.06 132.4 C463.15 119.27, 472.64 110.16, 477.96 101.46 M451.06 132.4 C458.03 124.07, 464.8 115.65, 477.96 101.46 M457.36 131.25 C462.65 126.55, 467.85 119.82, 477.05 108.61 M457.36 131.25 C462.56 125.74, 468.27 120.23, 477.05 108.61 M464.97 128.59 C469.23 123.21, 474.5 119.94, 476.13 115.76 M464.97 128.59 C466.4 126.62, 470.09 122.46, 476.13 115.76" stroke="#b2f2bb" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C134.76 -0.82, 237.15 -2.84, 445 0 M32 0 C117.14 0.77, 203.3 0.82, 445 0 M445 0 C464.46 1.31, 477.82 10, 477 32 M445 0 C468.12 -1.15, 476.89 8.73, 477 32 M477 32 C477.29 57.75, 476.77 81.02, 477 101 M477 32 C477.02 52.99, 476.55 76.44, 477 101 M477 101 C476.76 123.18, 464.89 132.41, 445 133 M477 101 C479.01 122.87, 468.12 132.27, 445 133 M445 133 C321.26 133.45, 195.94 134.33, 32 133 M445 133 C294.67 133.48, 144.05 133.68, 32 133 M32 133 C9.9 132.01, -1.58 121.23, 0 101 M32 133 C9.79 134.41, -1.71 120.88, 0 101 M0 101 C-1.18 83.33, -0.4 65.06, 0 32 M0 101 C0 80.69, 0.41 59.87, 0 32 M0 32 C1.82 10.89, 11.97 0.59, 32 0 M0 32 C-1.7 8.91, 9.45 -0.01, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(805.94921875 75.3515625) rotate(0 220.3125 57.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">node:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">  symlink:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    name: 00000000000000000000000000000000-test</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    target: /foo</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">references: []</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">narinfo: …</text></g><g stroke-linecap="round" transform="translate(806.46875 225.2578125) rotate(0 238.5 72.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C8.68 15.22, 9.53 10.88, 18.1 2.48 M3.66 19.09 C8.81 14.12, 11.51 9.97, 18.1 2.48 M2.75 26.24 C6.81 18.96, 11.51 16.19, 24.4 1.33 M2.75 26.24 C8.1 20.93, 12.69 14.7, 24.4 1.33 M2.48 32.64 C12.09 22.75, 21.05 8.83, 30.7 0.18 M2.48 32.64 C12.24 21.36, 23.11 8.33, 30.7 0.18 M2.88 38.28 C14.38 23.94, 25.95 11.59, 34.37 2.05 M2.88 38.28 C9.83 28.87, 18.57 19.97, 34.37 2.05 M2.62 44.68 C11.2 34.98, 16.8 26.09, 39.36 2.41 M2.62 44.68 C9.89 35.67, 19.19 25.97, 39.36 2.41 M2.36 51.07 C16.6 33.87, 30.06 19.6, 45 2.02 M2.36 51.07 C14.11 36.43, 26.4 24.25, 45 2.02 M2.75 56.72 C11.38 44.5, 23.52 31.72, 49.99 2.38 M2.75 56.72 C12.11 44.16, 21.93 34.17, 49.99 2.38 M2.49 63.11 C22.69 39.21, 44.13 13.8, 55.63 1.98 M2.49 63.11 C24.61 38.46, 45.21 14.7, 55.63 1.98 M2.88 68.76 C22.62 46.66, 38.87 25.27, 60.62 2.34 M2.88 68.76 C15.25 52.49, 29.63 37.02, 60.62 2.34 M2.62 75.16 C19.95 55.49, 39.17 32.32, 66.26 1.95 M2.62 75.16 C24.54 50.58, 44.69 26.49, 66.26 1.95 M2.36 81.55 C27.08 53.26, 56.1 20.67, 71.25 2.31 M2.36 81.55 C22.38 59.12, 42.38 37.91, 71.25 2.31 M2.76 87.2 C16.85 69.7, 34.99 53.54, 76.89 1.91 M2.76 87.2 C24.69 60.73, 48.08 33.35, 76.89 1.91 M2.49 93.59 C26.83 67.24, 48.57 42.72, 81.88 2.27 M2.49 93.59 C22.01 71.75, 40.83 49.63, 81.88 2.27 M2.89 99.24 C21.26 75.44, 42.16 55.2, 87.52 1.88 M2.89 99.24 C27.49 71.35, 50.92 41.12, 87.52 1.88 M2.63 105.64 C30.05 76.71, 56.77 44.63, 92.51 2.24 M2.63 105.64 C29.93 73.84, 55.66 42.59, 92.51 2.24 M2.37 112.03 C31.19 75.8, 63.69 43.11, 98.15 1.85 M2.37 112.03 C33.09 77.93, 61.62 44.45, 98.15 1.85 M2.76 117.68 C39.28 75.49, 75.08 36.84, 103.14 2.21 M2.76 117.68 C38.87 78.04, 74.17 36.06, 103.14 2.21 M3.16 123.32 C44.92 78.9, 86.85 29.88, 108.78 1.81 M3.16 123.32 C33.5 86.75, 64.72 49.98, 108.78 1.81 M4.86 127.45 C37.1 91.22, 67.62 56.08, 113.77 2.17 M4.86 127.45 C48.91 78.36, 90.69 30.13, 113.77 2.17 M5.26 133.1 C28.92 105.27, 51.55 78.45, 119.41 1.78 M5.26 133.1 C39.84 93.11, 73.53 54.29, 119.41 1.78 M7.62 136.47 C35.56 105.24, 62.69 73.44, 124.4 2.14 M7.62 136.47 C41.94 98.75, 76.48 57.38, 124.4 2.14 M10.64 139.1 C45.2 102.3, 79.7 60.38, 130.04 1.74 M10.64 139.1 C39.87 104.89, 70.76 71.45, 130.04 1.74 M14.31 140.97 C43.51 110.97, 68.26 78.62, 135.03 2.1 M14.31 140.97 C45.94 106.35, 77.72 71.79, 135.03 2.1 M17.33 143.59 C63.36 91.09, 109.79 35.17, 140.67 1.71 M17.33 143.59 C67.02 87.83, 115.79 32.77, 140.67 1.71 M21.01 145.46 C69.52 85.94, 122.53 32.68, 145.66 2.07 M21.01 145.46 C65.52 93.39, 110.85 41.72, 145.66 2.07 M25.34 146.58 C73.82 90.55, 124.58 31.62, 150.65 2.43 M25.34 146.58 C57.27 108.71, 90.6 71.59, 150.65 2.43 M32.29 144.67 C82.37 88.85, 130.86 32.77, 156.29 2.03 M32.29 144.67 C58.07 117.01, 83.71 85.72, 156.29 2.03 M37.28 145.03 C76.55 100.57, 116.26 57.79, 161.28 2.39 M37.28 145.03 C75.27 101.67, 112.08 57.69, 161.28 2.39 M42.92 144.64 C87.59 96.54, 128.11 43.6, 166.92 2 M42.92 144.64 C76.77 106.18, 112.16 66.71, 166.92 2 M47.91 145 C74.27 114.89, 99.04 86.08, 171.91 2.36 M47.91 145 C80.84 110.75, 111.51 74.33, 171.91 2.36 M52.9 145.36 C91.05 102.58, 127.57 62.7, 177.55 1.96 M52.9 145.36 C99.51 92.64, 144.44 40.01, 177.55 1.96 M58.54 144.96 C96.62 103.36, 132.1 61.4, 182.54 2.32 M58.54 144.96 C102.3 95.31, 145 44.66, 182.54 2.32 M63.53 145.32 C109.82 92.88, 153.81 37.96, 188.18 1.93 M63.53 145.32 C106.42 95.64, 150.95 44.77, 188.18 1.93 M69.17 144.93 C96.18 113.75, 124.05 81.34, 193.17 2.29 M69.17 144.93 C108.27 96.79, 149.34 50.87, 193.17 2.29 M74.16 145.29 C97.88 116.26, 126.66 87.16, 198.81 1.9 M74.16 145.29 C103.75 113.96, 133.25 80.61, 198.81 1.9 M79.8 144.9 C128.87 85.82, 177.7 31.59, 203.8 2.26 M79.8 144.9 C108.17 113.17, 135.25 78.83, 203.8 2.26 M84.79 145.26 C128.13 96.77, 168.82 46.48, 209.44 1.86 M84.79 145.26 C120.57 103.55, 156.93 61.55, 209.44 1.86 M90.43 144.86 C131.03 103.87, 164.62 58.8, 214.43 2.22 M90.43 144.86 C139.02 86.88, 188.33 30.13, 214.43 2.22 M95.42 145.22 C122.03 112.94, 150.45 84.35, 220.07 1.83 M95.42 145.22 C135.79 98.82, 175.14 53.71, 220.07 1.83 M101.06 144.83 C140.89 97.19, 181.91 54.17, 225.06 2.19 M101.06 144.83 C134.98 105.95, 167.66 68.29, 225.06 2.19 M106.05 145.19 C152.49 92.82, 198.26 40.96, 230.7 1.79 M106.05 145.19 C132.37 114.56, 159.47 83.34, 230.7 1.79 M111.69 144.79 C153.52 93.02, 197.5 42.26, 235.69 2.15 M111.69 144.79 C159.86 91.23, 206.41 36.81, 235.69 2.15 M116.68 145.15 C142.1 113.42, 167.54 83.21, 241.33 1.76 M116.68 145.15 C161.14 92.66, 207.79 41.99, 241.33 1.76 M122.32 144.76 C166.13 93.13, 209.82 39.8, 246.32 2.12 M122.32 144.76 C166.64 93.28, 213.29 39.18, 246.32 2.12 M127.31 145.12 C165.51 100.3, 206.72 56.81, 251.96 1.72 M127.31 145.12 C156.34 112.26, 187.1 77.6, 251.96 1.72 M132.95 144.72 C166.91 109.34, 195.98 71.92, 256.95 2.08 M132.95 144.72 C180.38 89.53, 229.12 34.54, 256.95 2.08 M137.94 145.08 C175.38 101.81, 210.1 63.31, 262.59 1.69 M137.94 145.08 C169.66 108.24, 200.14 72.75, 262.59 1.69 M143.58 144.69 C168.18 114.88, 194.29 86.3, 267.58 2.05 M143.58 144.69 C176.83 108.94, 208.19 70.29, 267.58 2.05 M148.57 145.05 C182.56 103.55, 216.29 66.03, 272.56 2.41 M148.57 145.05 C181.02 105.11, 215.28 67.56, 272.56 2.41 M154.21 144.65 C189.78 103.53, 226.3 57.46, 278.21 2.01 M154.21 144.65 C194.19 98.45, 235.22 50.5, 278.21 2.01 M159.2 145.01 C201.39 97.64, 239.44 52.54, 283.19 2.37 M159.2 145.01 C196.58 101.2, 234.19 59.04, 283.19 2.37 M164.19 145.37 C204.83 100.56, 245.77 53.99, 288.84 1.98 M164.19 145.37 C194.6 111.64, 224.28 77.56, 288.84 1.98 M169.83 144.98 C216.09 96.8, 258.65 45.93, 293.82 2.34 M169.83 144.98 C198.36 111.93, 225.45 82.13, 293.82 2.34 M174.82 145.34 C222.13 92.22, 266.76 39.3, 299.47 1.94 M174.82 145.34 C214.43 98.2, 254.01 52.59, 299.47 1.94 M180.46 144.94 C206.13 115.61, 232.02 85.17, 304.45 2.3 M180.46 144.94 C208.14 111.98, 236.78 80.02, 304.45 2.3 M185.45 145.31 C211.73 114.99, 239.84 81.36, 310.1 1.91 M185.45 145.31 C211.68 114.43, 238.14 82.89, 310.1 1.91 M191.09 144.91 C218.08 111.04, 246.8 80.32, 315.08 2.27 M191.09 144.91 C214.94 114.57, 239.96 87.29, 315.08 2.27 M196.08 145.27 C239.46 94.21, 286.41 44.83, 320.73 1.88 M196.08 145.27 C229.6 106.98, 264.69 68.01, 320.73 1.88 M201.72 144.88 C228.54 113.1, 258.25 77.79, 325.71 2.24 M201.72 144.88 C243.4 96.39, 285.56 47.13, 325.71 2.24 M206.71 145.24 C253.76 92.02, 298.3 40.23, 331.36 1.84 M206.71 145.24 C244.96 99.96, 284.86 52.62, 331.36 1.84 M212.35 144.84 C256.46 90.35, 305.16 39.74, 336.34 2.2 M212.35 144.84 C254.88 97.2, 294.9 51.16, 336.34 2.2 M217.34 145.2 C260.85 95.12, 303.44 46.91, 341.99 1.81 M217.34 145.2 C254.9 102.38, 292.7 59.98, 341.99 1.81 M222.98 144.81 C255.34 105.33, 290.89 65.64, 346.97 2.17 M222.98 144.81 C266.33 93.19, 310.85 42.12, 346.97 2.17 M227.97 145.17 C263.66 108.29, 298 68.71, 352.62 1.77 M227.97 145.17 C258.23 109.67, 288.39 75.4, 352.62 1.77 M233.61 144.77 C270.05 104.52, 304.97 63.07, 357.6 2.13 M233.61 144.77 C274.47 98.07, 312.89 55.02, 357.6 2.13 M238.6 145.13 C266.67 111.74, 296.18 82.47, 363.25 1.74 M238.6 145.13 C277.04 101.17, 315.84 55.8, 363.25 1.74 M244.24 144.74 C274.9 111.46, 304.37 75.97, 368.23 2.1 M244.24 144.74 C293.37 88.52, 341.04 33.09, 368.23 2.1 M249.23 145.1 C276.79 116.75, 302.14 86.51, 373.88 1.7 M249.23 145.1 C290.94 99.98, 331.11 52.85, 373.88 1.7 M254.87 144.7 C287.13 105.19, 322.18 68.96, 378.86 2.06 M254.87 144.7 C288.12 105.83, 322.32 65.46, 378.86 2.06 M259.86 145.06 C297.18 103.73, 332.56 62.14, 383.85 2.42 M259.86 145.06 C285.47 114.28, 312.5 83.14, 383.85 2.42 M265.5 144.67 C289.04 113.22, 315.35 85.99, 389.49 2.03 M265.5 144.67 C309.3 94.62, 352.05 44.75, 389.49 2.03 M270.49 145.03 C300.51 108.19, 332.82 72.1, 394.48 2.39 M270.49 145.03 C300.97 111.01, 330.93 74.5, 394.48 2.39 M276.13 144.63 C324.36 91.02, 372.28 35.59, 400.12 1.99 M276.13 144.63 C314.28 100.64, 353.55 56.58, 400.12 1.99 M281.12 144.99 C306.5 114.55, 332.17 85.71, 405.11 2.35 M281.12 144.99 C323.82 96.38, 365.49 47.59, 405.11 2.35 M286.1 145.35 C317.3 110.79, 350.4 75.24, 410.75 1.96 M286.1 145.35 C329.26 94.11, 373.2 45.44, 410.75 1.96 M291.75 144.96 C331.46 97.55, 372.96 52.2, 415.74 2.32 M291.75 144.96 C333.75 97.37, 374.48 47.84, 415.74 2.32 M296.73 145.32 C339.74 99.77, 378.75 49.03, 421.38 1.93 M296.73 145.32 C339.27 96.73, 382.31 46.86, 421.38 1.93 M302.38 144.93 C334.79 106.51, 370.65 62.67, 426.37 2.29 M302.38 144.93 C351.86 88.93, 398.72 33.88, 426.37 2.29 M307.36 145.29 C340.53 106.77, 376.08 66.7, 432.01 1.89 M307.36 145.29 C343.7 102.77, 379.62 59.95, 432.01 1.89 M313.01 144.89 C349.35 105.7, 384.17 63.87, 437 2.25 M313.01 144.89 C353.04 98.04, 391.67 55.11, 437 2.25 M317.99 145.25 C339.84 114.7, 366.5 88.66, 442.64 1.86 M317.99 145.25 C351.57 103.84, 387.73 63.05, 442.64 1.86 M323.64 144.86 C348.5 114.38, 375.72 88.33, 447.63 2.22 M323.64 144.86 C355.53 106.62, 389.28 69.15, 447.63 2.22 M328.62 145.22 C372.38 92.62, 416.87 43.42, 452.62 2.58 M328.62 145.22 C366.51 102.9, 405.23 58.43, 452.62 2.58 M334.27 144.82 C363.07 113.17, 389.9 79.51, 456.95 3.69 M334.27 144.82 C367.21 104.1, 402.44 65.76, 456.95 3.69 M339.25 145.18 C372.8 105.31, 407.19 67.1, 461.94 4.05 M339.25 145.18 C370.71 111.13, 401.08 74.54, 461.94 4.05 M344.9 144.79 C373.24 109.42, 403.66 74.09, 464.96 6.68 M344.9 144.79 C380.58 103.48, 416.5 62.22, 464.96 6.68 M349.88 145.15 C396.98 89.22, 441.52 35.96, 469.29 7.79 M349.88 145.15 C384.63 103.87, 421.1 63.92, 469.29 7.79 M355.53 144.75 C384.31 114.2, 411.66 82.06, 471.65 11.17 M355.53 144.75 C395.88 99.08, 436.7 51.19, 471.65 11.17 M360.51 145.11 C388.54 111.66, 414.89 82.27, 474.67 13.79 M360.51 145.11 C398.58 98.72, 438.1 53.79, 474.67 13.79 M366.16 144.72 C399.89 104.57, 433.88 66.68, 475.72 18.68 M366.16 144.72 C408.72 95.09, 451.17 47.53, 475.72 18.68 M371.14 145.08 C397.64 114.94, 427.94 82.11, 477.43 22.82 M371.14 145.08 C410.48 97.21, 450.83 51.08, 477.43 22.82 M376.79 144.68 C397.46 116.7, 419.98 91.38, 479.13 26.95 M376.79 144.68 C400.93 117.38, 426.7 88.5, 479.13 26.95 M381.77 145.04 C417.98 100.88, 455.83 57.26, 479.53 32.59 M381.77 145.04 C410.94 114.25, 438.41 81.7, 479.53 32.59 M387.42 144.65 C412.93 114.6, 438.57 87, 479.27 38.99 M387.42 144.65 C411.38 118.3, 434.26 90.98, 479.27 38.99 M392.4 145.01 C416.3 120.47, 436.57 92.84, 479 45.39 M392.4 145.01 C415.9 120.64, 438.88 93.42, 479 45.39 M397.39 145.37 C415.88 120.12, 437.69 100.01, 479.4 51.03 M397.39 145.37 C419.06 121.26, 438.71 97.02, 479.4 51.03 M403.03 144.98 C418.65 128.5, 434.02 108.59, 479.14 57.43 M403.03 144.98 C431.82 109.79, 463.03 76.44, 479.14 57.43 M408.02 145.34 C423.72 128.07, 441.93 108.83, 479.53 63.07 M408.02 145.34 C423.27 129.37, 437.27 112.48, 479.53 63.07 M413.66 144.94 C430.89 123.63, 448.67 104.59, 479.27 69.47 M413.66 144.94 C429.3 124.99, 447.94 106.36, 479.27 69.47 M418.65 145.3 C429.88 130.69, 442.79 115.76, 479.66 75.11 M418.65 145.3 C438.44 123.35, 457.66 100.29, 479.66 75.11 M424.29 144.91 C443.94 120.3, 467.31 96.47, 479.4 81.51 M424.29 144.91 C443.54 122.53, 464.39 98.64, 479.4 81.51 M429.28 145.27 C440.46 128.53, 453.86 117.18, 479.8 87.15 M429.28 145.27 C446.57 125.43, 465.7 103.67, 479.8 87.15 M434.92 144.87 C452.62 126.78, 470.9 107.72, 479.54 93.55 M434.92 144.87 C446.21 130.49, 458.92 116.32, 479.54 93.55 M439.91 145.23 C454.92 126.59, 470.52 110.64, 479.93 99.19 M439.91 145.23 C454.48 130.43, 466.98 114.01, 479.93 99.19 M445.55 144.84 C459.7 129.63, 473.4 112.45, 479.67 105.59 M445.55 144.84 C456.2 132.48, 466.43 120.71, 479.67 105.59 M451.2 144.44 C457.4 137.27, 463.87 127.4, 478.1 113.5 M451.2 144.44 C457.13 137.53, 462.11 130.98, 478.1 113.5 M457.5 143.29 C464.19 138.07, 467.02 131.58, 476.52 121.41 M457.5 143.29 C461.1 138.38, 465.47 132.12, 476.52 121.41 M465.11 140.63 C469.09 135.97, 469.57 134.23, 476.26 127.8 M465.11 140.63 C467.57 137.52, 470.45 135.62, 476.26 127.8" stroke="#b2f2bb" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C176.04 -2.14, 317.57 -1.78, 445 0 M32 0 C179.36 0.19, 326.13 -0.1, 445 0 M445 0 C466.49 -1.62, 476.74 11.76, 477 32 M445 0 C465.33 0.95, 478.9 10.46, 477 32 M477 32 C478.38 59.54, 477.89 83.51, 477 113 M477 32 C477.4 63.12, 476.78 93.95, 477 113 M477 113 C476.1 134.31, 466.7 143.02, 445 145 M477 113 C478.22 135.1, 467.61 144.46, 445 145 M445 145 C356.12 145.98, 265.85 145.1, 32 145 M445 145 C280.4 145.67, 115.39 145.75, 32 145 M32 145 C8.74 143.12, -0.92 132.86, 0 113 M32 145 C12.94 142.98, -0.76 132.33, 0 113 M0 113 C-0.07 82.2, 0.07 49.78, 0 32 M0 113 C0.34 82.21, 0.32 51.58, 0 32 M0 32 C1.6 12.66, 10.66 -1.83, 32 0 M0 32 C1.88 10.07, 10.34 0.34, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(811.46875 230.2578125) rotate(0 220.3125 67.19999999999999)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">node:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">  directory:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    name: 11111111111111111111111111111111-test</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    digest: &lt;directory-complicated-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    size: 4</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">references: []</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">narinfo: …</text></g><g stroke-linecap="round" transform="translate(810.1484375 394.59375) rotate(0 238.5 72.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C8.91 13.37, 11.17 9.44, 18.1 2.48 M3.66 19.09 C9.16 12.46, 14.4 7.69, 18.1 2.48 M2.75 26.24 C9.7 18.96, 16.37 8.53, 24.4 1.33 M2.75 26.24 C11.39 17.63, 20.3 6.99, 24.4 1.33 M2.48 32.64 C10.71 24.03, 18.59 13.26, 30.7 0.18 M2.48 32.64 C11.25 23.35, 19.02 14.4, 30.7 0.18 M2.88 38.28 C16.41 22.39, 27.21 9.65, 34.37 2.05 M2.88 38.28 C9.5 30.63, 15.35 23.52, 34.37 2.05 M2.62 44.68 C14.02 30.56, 24.17 18.77, 39.36 2.41 M2.62 44.68 C11.44 34.89, 20.03 24.71, 39.36 2.41 M2.36 51.07 C14.17 38.91, 27.26 20.6, 45 2.02 M2.36 51.07 C14.79 38.21, 27.2 22.82, 45 2.02 M2.75 56.72 C14.28 42.93, 28 31.31, 49.99 2.38 M2.75 56.72 C14.37 45.04, 23.83 32.21, 49.99 2.38 M2.49 63.11 C15.91 47.14, 31.6 33.09, 55.63 1.98 M2.49 63.11 C22.3 40.56, 42.25 16.35, 55.63 1.98 M2.88 68.76 C25.94 42.37, 49.23 17.68, 60.62 2.34 M2.88 68.76 C25.29 41.12, 49.97 16.59, 60.62 2.34 M2.62 75.16 C26.36 49.35, 49.71 23.45, 66.26 1.95 M2.62 75.16 C27.26 46.7, 53.03 16.79, 66.26 1.95 M2.36 81.55 C18.75 60.1, 37.31 41.18, 71.25 2.31 M2.36 81.55 C30.19 50.57, 57.16 19.4, 71.25 2.31 M2.76 87.2 C17.36 71.88, 32.62 50.73, 76.89 1.91 M2.76 87.2 C26.3 60.71, 49.92 34.99, 76.89 1.91 M2.49 93.59 C27.25 66.48, 49.52 37.6, 81.88 2.27 M2.49 93.59 C30.85 62.58, 57.18 29.13, 81.88 2.27 M2.89 99.24 C25.68 73.59, 51.19 46.74, 87.52 1.88 M2.89 99.24 C20.96 78.77, 38.14 58.85, 87.52 1.88 M2.63 105.64 C27.26 82.03, 48.77 55.01, 92.51 2.24 M2.63 105.64 C29.71 75, 56.08 45.73, 92.51 2.24 M2.37 112.03 C38.89 71.73, 73.23 30.36, 98.15 1.85 M2.37 112.03 C31.31 79.87, 58.69 47.79, 98.15 1.85 M2.76 117.68 C38.65 77.93, 73.02 35.37, 103.14 2.21 M2.76 117.68 C39.96 75.26, 75.95 31.69, 103.14 2.21 M3.16 123.32 C39.16 81.11, 77.97 37.18, 108.78 1.81 M3.16 123.32 C26.62 96.63, 50.07 69.49, 108.78 1.81 M4.86 127.45 C38.16 84.21, 75.04 44.41, 113.77 2.17 M4.86 127.45 C26.16 101.72, 50.12 76.16, 113.77 2.17 M5.26 133.1 C32.36 105, 60.22 74.13, 119.41 1.78 M5.26 133.1 C49.95 79.53, 94.95 28.74, 119.41 1.78 M7.62 136.47 C35.36 107.28, 59.43 73.22, 124.4 2.14 M7.62 136.47 C48.07 90.66, 87.09 44.03, 124.4 2.14 M10.64 139.1 C44.23 99.15, 78.94 58.88, 130.04 1.74 M10.64 139.1 C48.85 99.23, 82.98 56.76, 130.04 1.74 M14.31 140.97 C61.07 83.76, 109.13 28.41, 135.03 2.1 M14.31 140.97 C40.23 110.15, 67.08 81.31, 135.03 2.1 M17.33 143.59 C57.88 96.8, 95.53 53.8, 140.67 1.71 M17.33 143.59 C57.08 96.93, 97.56 52.45, 140.67 1.71 M21.01 145.46 C56.01 106.76, 88.35 69.45, 145.66 2.07 M21.01 145.46 C67.32 93, 112.76 41.13, 145.66 2.07 M25.34 146.58 C51.39 115.65, 78.49 84.38, 150.65 2.43 M25.34 146.58 C67.89 94.87, 112.13 43.89, 150.65 2.43 M32.29 144.67 C81.15 91.53, 127.11 36.9, 156.29 2.03 M32.29 144.67 C57.68 113.6, 83.65 83.25, 156.29 2.03 M37.28 145.03 C80.72 91.85, 128.85 42.46, 161.28 2.39 M37.28 145.03 C81.17 93.73, 125.7 40.99, 161.28 2.39 M42.92 144.64 C86.35 93.9, 133.36 38.98, 166.92 2 M42.92 144.64 C81.83 99.95, 121.66 56.09, 166.92 2 M47.91 145 C76.08 112.91, 107.55 78.18, 171.91 2.36 M47.91 145 C81.06 109.66, 111.35 72.82, 171.91 2.36 M52.9 145.36 C100.16 89.62, 149.95 34.51, 177.55 1.96 M52.9 145.36 C89.79 102.8, 125.11 62.89, 177.55 1.96 M58.54 144.96 C90.83 107.72, 120.89 72.28, 182.54 2.32 M58.54 144.96 C83.32 116.05, 108.95 87.3, 182.54 2.32 M63.53 145.32 C97.39 110.22, 128.17 69.65, 188.18 1.93 M63.53 145.32 C97.52 104.23, 131.94 65.28, 188.18 1.93 M69.17 144.93 C100.97 104.36, 136.13 67.64, 193.17 2.29 M69.17 144.93 C105.44 102.82, 142.12 58.58, 193.17 2.29 M74.16 145.29 C113.39 99.23, 154.87 50.02, 198.81 1.9 M74.16 145.29 C115.68 97.91, 154.99 52.02, 198.81 1.9 M79.8 144.9 C117.22 100.44, 155.63 58.46, 203.8 2.26 M79.8 144.9 C119.55 99.87, 159.99 53.61, 203.8 2.26 M84.79 145.26 C114.96 111.31, 144.84 77.07, 209.44 1.86 M84.79 145.26 C130.74 96.04, 174.26 45.02, 209.44 1.86 M90.43 144.86 C119.59 111.08, 146.64 82.67, 214.43 2.22 M90.43 144.86 C137.14 92.65, 182.18 40.05, 214.43 2.22 M95.42 145.22 C134.97 97.52, 174.23 52.21, 220.07 1.83 M95.42 145.22 C121.16 115.94, 147.23 85.62, 220.07 1.83 M101.06 144.83 C128.73 111.86, 158.26 79.88, 225.06 2.19 M101.06 144.83 C127.52 114.11, 154.95 81.91, 225.06 2.19 M106.05 145.19 C132.23 115.04, 157.86 83.38, 230.7 1.79 M106.05 145.19 C133.88 111.98, 162.23 80.52, 230.7 1.79 M111.69 144.79 C135.52 113.89, 160.61 88.04, 235.69 2.15 M111.69 144.79 C155.76 94.05, 201.14 44.16, 235.69 2.15 M116.68 145.15 C149.35 106.9, 185.6 68, 241.33 1.76 M116.68 145.15 C144.46 112.95, 173.88 78.54, 241.33 1.76 M122.32 144.76 C163.47 96.75, 205.64 46.73, 246.32 2.12 M122.32 144.76 C169.21 91.48, 214.25 39.39, 246.32 2.12 M127.31 145.12 C165.46 100.62, 205.52 51.66, 251.96 1.72 M127.31 145.12 C172.07 90.92, 219.78 38.74, 251.96 1.72 M132.95 144.72 C175.8 96.08, 215.01 50.98, 256.95 2.08 M132.95 144.72 C175.65 95.27, 218.21 46.7, 256.95 2.08 M137.94 145.08 C175.05 101.58, 213.52 59.48, 262.59 1.69 M137.94 145.08 C171.08 105.35, 206.42 65.3, 262.59 1.69 M143.58 144.69 C186.3 92.98, 230.97 41.88, 267.58 2.05 M143.58 144.69 C178.59 107.36, 212.3 68.54, 267.58 2.05 M148.57 145.05 C179.15 108.79, 208.33 76.07, 272.56 2.41 M148.57 145.05 C184.5 104.35, 220.01 62.87, 272.56 2.41 M154.21 144.65 C195.56 97.04, 233.34 55.88, 278.21 2.01 M154.21 144.65 C182.1 112.15, 210.74 81.54, 278.21 2.01 M159.2 145.01 C197.45 101.68, 236.26 55.56, 283.19 2.37 M159.2 145.01 C190.06 111.73, 219.71 76.82, 283.19 2.37 M164.19 145.37 C214.12 88.29, 261.36 32.74, 288.84 1.98 M164.19 145.37 C191.17 116.65, 216.4 87.09, 288.84 1.98 M169.83 144.98 C211.86 100.8, 252.07 52.72, 293.82 2.34 M169.83 144.98 C202.9 106.38, 236.88 69.34, 293.82 2.34 M174.82 145.34 C207.63 107.06, 242.55 64.81, 299.47 1.94 M174.82 145.34 C211.8 104.01, 247.25 62.79, 299.47 1.94 M180.46 144.94 C205.47 114.55, 232.3 83.6, 304.45 2.3 M180.46 144.94 C204.45 114.46, 230.06 86.52, 304.45 2.3 M185.45 145.31 C229.35 95.12, 271.86 44.84, 310.1 1.91 M185.45 145.31 C216.37 107.9, 248.61 71.36, 310.1 1.91 M191.09 144.91 C222.55 111.72, 251.85 73.92, 315.08 2.27 M191.09 144.91 C238.55 91.08, 286.27 36.02, 315.08 2.27 M196.08 145.27 C234.29 100.34, 274.54 56.28, 320.73 1.88 M196.08 145.27 C222.42 114.67, 248.24 85.32, 320.73 1.88 M201.72 144.88 C245.02 96.69, 286.1 47.75, 325.71 2.24 M201.72 144.88 C233.01 110.5, 264.89 75.09, 325.71 2.24 M206.71 145.24 C250.09 93.17, 294.8 45.48, 331.36 1.84 M206.71 145.24 C246.95 98.07, 287.81 52.25, 331.36 1.84 M212.35 144.84 C255.02 97.82, 294.21 47.38, 336.34 2.2 M212.35 144.84 C254.78 98.81, 294.78 49.85, 336.34 2.2 M217.34 145.2 C260.68 96.29, 303.76 46.02, 341.99 1.81 M217.34 145.2 C250.78 105.78, 286.51 63.39, 341.99 1.81 M222.98 144.81 C272.96 88.46, 318.86 33.36, 346.97 2.17 M222.98 144.81 C256.46 106.72, 290.95 67.45, 346.97 2.17 M227.97 145.17 C264.79 102.33, 299.75 59.1, 352.62 1.77 M227.97 145.17 C263.52 105.07, 298.61 63.81, 352.62 1.77 M233.61 144.77 C274.12 96.97, 312.37 55.98, 357.6 2.13 M233.61 144.77 C256.24 114.88, 281.92 87.66, 357.6 2.13 M238.6 145.13 C271.28 102.8, 307.76 62.45, 363.25 1.74 M238.6 145.13 C264.06 115.44, 290.29 87.89, 363.25 1.74 M244.24 144.74 C276.08 105.66, 310.55 68.73, 368.23 2.1 M244.24 144.74 C288.01 92.91, 332.32 42.92, 368.23 2.1 M249.23 145.1 C286.85 103.31, 326.12 58.27, 373.88 1.7 M249.23 145.1 C278.25 112.14, 306.08 78.73, 373.88 1.7 M254.87 144.7 C287.46 102.93, 323.86 65.43, 378.86 2.06 M254.87 144.7 C288.68 105.04, 323.05 66.18, 378.86 2.06 M259.86 145.06 C291.28 111.25, 321.61 73.04, 383.85 2.42 M259.86 145.06 C289.7 108.87, 320.7 73.05, 383.85 2.42 M265.5 144.67 C302.2 102.1, 339.7 59.09, 389.49 2.03 M265.5 144.67 C314.09 87.39, 361.48 31.86, 389.49 2.03 M270.49 145.03 C306.79 101.72, 345.32 61.4, 394.48 2.39 M270.49 145.03 C300.53 111.68, 330.35 77.04, 394.48 2.39 M276.13 144.63 C319.2 96.52, 362.81 44.49, 400.12 1.99 M276.13 144.63 C306.01 109.16, 335.36 75.86, 400.12 1.99 M281.12 144.99 C321.76 94.08, 364.67 45.75, 405.11 2.35 M281.12 144.99 C319.22 100.17, 357.51 56.73, 405.11 2.35 M286.1 145.35 C333.71 88.53, 382.13 35.38, 410.75 1.96 M286.1 145.35 C317.78 109.04, 351.73 70.98, 410.75 1.96 M291.75 144.96 C337.19 88.4, 384.3 34.77, 415.74 2.32 M291.75 144.96 C317.91 112.26, 345.28 81.05, 415.74 2.32 M296.73 145.32 C325.68 112.17, 358.06 76.44, 421.38 1.93 M296.73 145.32 C343.84 88.98, 392.35 33.13, 421.38 1.93 M302.38 144.93 C339.95 106.31, 374.33 64.87, 426.37 2.29 M302.38 144.93 C336.03 105.32, 370.06 67.12, 426.37 2.29 M307.36 145.29 C340.16 110, 371.15 72.35, 432.01 1.89 M307.36 145.29 C340.08 109.03, 371.38 70.88, 432.01 1.89 M313.01 144.89 C346.7 110.4, 379.86 70.69, 437 2.25 M313.01 144.89 C342.6 108.31, 373.8 74.9, 437 2.25 M317.99 145.25 C351.16 108.99, 380.33 72.15, 442.64 1.86 M317.99 145.25 C343.7 117.08, 369.38 86.42, 442.64 1.86 M323.64 144.86 C370.25 87.28, 421.75 33.13, 447.63 2.22 M323.64 144.86 C351.37 114.11, 380.44 81.53, 447.63 2.22 M328.62 145.22 C355.29 117.71, 379.5 88.31, 452.62 2.58 M328.62 145.22 C362.76 106.45, 396.24 68.64, 452.62 2.58 M334.27 144.82 C363.38 107.43, 398.48 72.68, 456.95 3.69 M334.27 144.82 C357.91 116.46, 383 87.56, 456.95 3.69 M339.25 145.18 C379.04 101.04, 417.7 54.68, 461.94 4.05 M339.25 145.18 C385.54 91.6, 433.51 37.95, 461.94 4.05 M344.9 144.79 C386.94 95.92, 432.15 44.1, 464.96 6.68 M344.9 144.79 C374.61 108.52, 405.83 74.64, 464.96 6.68 M349.88 145.15 C391 98.05, 435.67 47.17, 469.29 7.79 M349.88 145.15 C395.91 94.65, 441.86 42.78, 469.29 7.79 M355.53 144.75 C385.45 107.18, 418.33 70.28, 471.65 11.17 M355.53 144.75 C400.44 93.54, 444.91 43.39, 471.65 11.17 M360.51 145.11 C401.24 102.6, 437.59 56.72, 474.67 13.79 M360.51 145.11 C405.18 92.61, 450.73 39.5, 474.67 13.79 M366.16 144.72 C400.27 105.26, 433.48 67.04, 475.72 18.68 M366.16 144.72 C393.53 111.8, 421.43 78.48, 475.72 18.68 M371.14 145.08 C393.65 118.74, 414.53 93.08, 477.43 22.82 M371.14 145.08 C402.07 111.49, 430.19 77.53, 477.43 22.82 M376.79 144.68 C398.96 116.96, 423.02 86.8, 479.13 26.95 M376.79 144.68 C405.71 112.24, 431.87 81.46, 479.13 26.95 M381.77 145.04 C402.53 120.66, 424.65 98.55, 479.53 32.59 M381.77 145.04 C415.6 104.73, 447.91 66.04, 479.53 32.59 M387.42 144.65 C421.09 106.74, 452.24 68, 479.27 38.99 M387.42 144.65 C417.09 111.82, 445.22 80.57, 479.27 38.99 M392.4 145.01 C416.08 117.03, 443.83 87.54, 479 45.39 M392.4 145.01 C412.54 120.34, 434.35 95.05, 479 45.39 M397.39 145.37 C418.8 119.7, 440.1 98.63, 479.4 51.03 M397.39 145.37 C425.95 112.73, 456.43 79.45, 479.4 51.03 M403.03 144.98 C425.28 120.65, 446.29 95.66, 479.14 57.43 M403.03 144.98 C425.92 117.76, 450.02 91.73, 479.14 57.43 M408.02 145.34 C425.07 128.2, 439.73 110.85, 479.53 63.07 M408.02 145.34 C430.26 120.38, 450.08 96.27, 479.53 63.07 M413.66 144.94 C435.93 123.3, 454.67 96.07, 479.27 69.47 M413.66 144.94 C439.06 116.88, 463.51 87.09, 479.27 69.47 M418.65 145.3 C434.28 127.3, 448 113.58, 479.66 75.11 M418.65 145.3 C439.53 121.35, 457.99 99.06, 479.66 75.11 M424.29 144.91 C438.64 130.23, 450.19 116.95, 479.4 81.51 M424.29 144.91 C446.16 119.97, 466.98 96.04, 479.4 81.51 M429.28 145.27 C449.48 121.21, 467.73 98.87, 479.8 87.15 M429.28 145.27 C445.11 126.77, 460.67 109.04, 479.8 87.15 M434.92 144.87 C451.5 125.37, 470.64 107.76, 479.54 93.55 M434.92 144.87 C450.33 126.77, 466.57 106.69, 479.54 93.55 M439.91 145.23 C452.13 132.68, 464.37 120.59, 479.93 99.19 M439.91 145.23 C451.04 133.55, 462.09 121.11, 479.93 99.19 M445.55 144.84 C458.79 129.03, 473.86 114.16, 479.67 105.59 M445.55 144.84 C454.8 134.14, 463.69 123.97, 479.67 105.59 M451.2 144.44 C462.63 132.85, 473.14 119.83, 478.1 113.5 M451.2 144.44 C458.17 135.43, 463.94 127.69, 478.1 113.5 M457.5 143.29 C463.11 139.03, 465.29 134.84, 476.52 121.41 M457.5 143.29 C464 136.22, 469.37 130.32, 476.52 121.41 M465.11 140.63 C467.6 137.97, 471.38 132.46, 476.26 127.8 M465.11 140.63 C467.66 137.17, 471.15 133.88, 476.26 127.8" stroke="#b2f2bb" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C142.9 2.12, 251.08 2.36, 445 0 M32 0 C115.5 -0.6, 199.1 -1.14, 445 0 M445 0 C467.37 0.9, 475.89 9.1, 477 32 M445 0 C464.69 1.75, 476.83 12.79, 477 32 M477 32 C475.75 51.18, 476.7 69.4, 477 113 M477 32 C477.02 52.65, 476.92 71.83, 477 113 M477 113 C475.11 133.14, 465.24 146.78, 445 145 M477 113 C478.86 132.08, 465.62 144.87, 445 145 M445 145 C290.97 146.31, 137.09 146.13, 32 145 M445 145 C337.93 143.89, 230.99 144.81, 32 145 M32 145 C9.61 145.84, -0.8 134.99, 0 113 M32 145 C11.35 145.42, 1.86 135.37, 0 113 M0 113 C1.15 86.4, 0.56 57.51, 0 32 M0 113 C-0.77 88.92, -1.37 66.73, 0 32 M0 32 C1.21 9.29, 10.51 -1.67, 32 0 M0 32 C2.23 9.98, 9.94 -0.68, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(815.1484375 399.59375) rotate(0 220.3125 67.19999999999999)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">node:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">  directory:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    name: 22222222222222222222222222222222-test</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    digest: &lt;directory-with-keep-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    size: 1</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">references: []</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">narinfo: …</text></g><g stroke-linecap="round"><g transform="translate(935.83984375 297.9609375) rotate(0 -127.17742444525584 -6.3835650656164376)"><path d="M-0.07 0.59 C-42.41 -1.51, -211.36 -11.21, -253.47 -13.35 M-1.57 -0.15 C-44.1 -1.99, -212.39 -9.54, -254.28 -11.88" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(935.83984375 297.9609375) rotate(0 -127.17742444525584 -6.3835650656164376)"><path d="M-224.64 -20.31 C-235.83 -15.98, -244.05 -15.65, -255.88 -12.33 M-225.16 -19.94 C-234.34 -18.84, -244.43 -15.58, -254.5 -12.11" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(935.83984375 297.9609375) rotate(0 -127.17742444525584 -6.3835650656164376)"><path d="M-225.65 0.18 C-236.36 -2.61, -244.23 -9.41, -255.88 -12.33 M-226.18 0.55 C-234.92 -4.92, -244.69 -8.23, -254.5 -12.11" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(934.54296875 464.29296875) rotate(0 -122.96248760393826 -133.71583716869355)"><path d="M-0.84 -0.49 C-41.65 -45.05, -205 -222.75, -245.8 -266.94 M0.91 -1.8 C-39.98 -46.25, -205.61 -221.44, -246.84 -265.75" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(934.54296875 464.29296875) rotate(0 -122.96248760393826 -133.71583716869355)"><path d="M-220.88 -252.52 C-227.08 -255.42, -230.79 -256.62, -246.15 -266.29 M-219.26 -252.47 C-226.67 -255.03, -232.47 -258.86, -246.62 -266.61" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(934.54296875 464.29296875) rotate(0 -122.96248760393826 -133.71583716869355)"><path d="M-235.85 -238.47 C-238.83 -244.34, -239.26 -248.61, -246.15 -266.29 M-234.23 -238.42 C-238.04 -244.21, -240.25 -251.41, -246.62 -266.61" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round" transform="translate(808.1090530561523 569.6183182417908) rotate(0 238.5 82)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C6.81 13.38, 14.57 7.61, 18.1 2.48 M3.66 19.09 C7.86 14.77, 10.94 9.38, 18.1 2.48 M2.75 26.24 C8.42 17.74, 14.61 10.75, 24.4 1.33 M2.75 26.24 C7.35 20.79, 11.06 16.63, 24.4 1.33 M2.48 32.64 C8.41 23.44, 17.32 15.14, 30.7 0.18 M2.48 32.64 C8.74 25.77, 13.03 20.51, 30.7 0.18 M2.88 38.28 C8.15 29.11, 19.49 21.57, 34.37 2.05 M2.88 38.28 C13.52 25.23, 25.55 13.16, 34.37 2.05 M2.62 44.68 C16 33.11, 26.07 16.01, 39.36 2.41 M2.62 44.68 C14.25 32.19, 24.76 18.26, 39.36 2.41 M2.36 51.07 C18.74 34.45, 31.43 18.56, 45 2.02 M2.36 51.07 C17.04 34.98, 31.27 17.88, 45 2.02 M2.75 56.72 C12.45 43.26, 26.24 32.38, 49.99 2.38 M2.75 56.72 C13.08 44.38, 23.25 35.02, 49.99 2.38 M2.49 63.11 C13.22 50.48, 25.3 38, 55.63 1.98 M2.49 63.11 C20.59 44.59, 36.16 24.18, 55.63 1.98 M2.88 68.76 C22.9 48.96, 39.83 27.4, 60.62 2.34 M2.88 68.76 C24.33 43.11, 46.62 19.91, 60.62 2.34 M2.62 75.16 C24.58 48.11, 49.17 22.73, 66.26 1.95 M2.62 75.16 C17.85 56.46, 36.34 37, 66.26 1.95 M2.36 81.55 C22.67 56.14, 44.58 32.75, 71.25 2.31 M2.36 81.55 C28.06 52.26, 52.4 23.27, 71.25 2.31 M2.76 87.2 C23.08 61.22, 45.46 37.01, 76.89 1.91 M2.76 87.2 C19.7 68.52, 35.9 49.32, 76.89 1.91 M2.49 93.59 C20.89 69.28, 40.59 49.24, 81.88 2.27 M2.49 93.59 C22.48 70.61, 42.67 48.73, 81.88 2.27 M2.89 99.24 C34.82 63.55, 63.23 31.57, 87.52 1.88 M2.89 99.24 C20.63 78.39, 39.49 56.84, 87.52 1.88 M2.63 105.64 C35.22 68.03, 69.69 27.76, 92.51 2.24 M2.63 105.64 C33.18 70.17, 62.24 35.99, 92.51 2.24 M2.37 112.03 C26.3 85.12, 50.21 57.23, 98.15 1.85 M2.37 112.03 C36.74 71.78, 71.12 32.15, 98.15 1.85 M2.76 117.68 C37.79 74.87, 77.39 32.27, 103.14 2.21 M2.76 117.68 C28.95 87.13, 55.3 54.97, 103.14 2.21 M2.5 124.07 C29.71 93.79, 56.39 67.33, 108.78 1.81 M2.5 124.07 C39.16 80.77, 76.22 37.93, 108.78 1.81 M2.89 129.72 C46.1 83.46, 86.14 32.26, 113.77 2.17 M2.89 129.72 C38.33 90.23, 73.3 50.35, 113.77 2.17 M2.63 136.11 C32.42 101.51, 64.36 66.19, 119.41 1.78 M2.63 136.11 C42.9 88.38, 84.51 40.74, 119.41 1.78 M3.03 141.76 C47.01 92.79, 88.14 44.94, 124.4 2.14 M3.03 141.76 C36.31 103.57, 71.35 65.73, 124.4 2.14 M4.73 145.89 C51.61 90.8, 99.17 38.12, 130.04 1.74 M4.73 145.89 C47.88 98.67, 90.06 49.91, 130.04 1.74 M5.13 151.53 C50.72 98.34, 95.33 50.22, 135.03 2.1 M5.13 151.53 C54.14 93.61, 105.46 35.23, 135.03 2.1 M7.49 154.91 C46.02 110.99, 81.47 70.85, 140.67 1.71 M7.49 154.91 C52.42 103.68, 96.67 51.91, 140.67 1.71 M10.51 157.54 C60.56 98.74, 112.31 43.05, 145.66 2.07 M10.51 157.54 C41.53 122.75, 73.01 84.79, 145.66 2.07 M13.53 160.16 C39.75 129.1, 69.49 95.86, 150.65 2.43 M13.53 160.16 C66.46 100.75, 117.42 41, 150.65 2.43 M17.2 162.03 C70.82 99.54, 124.51 39.14, 156.29 2.03 M17.2 162.03 C45.6 127.78, 75.59 94.57, 156.29 2.03 M20.88 163.9 C56.65 126.92, 91.29 87.42, 161.28 2.39 M20.88 163.9 C71.96 105.59, 125.36 44.09, 161.28 2.39 M25.21 165.02 C65.92 117.85, 106.95 72.45, 166.92 2 M25.21 165.02 C60.62 122.61, 96.68 82.03, 166.92 2 M30.2 165.38 C61.83 128.51, 95.39 92.16, 171.91 2.36 M30.2 165.38 C61.34 132.24, 90.7 98.68, 171.91 2.36 M34.53 166.49 C75.36 119.62, 116.91 72.06, 177.55 1.96 M34.53 166.49 C82.24 112.82, 128.51 59.11, 177.55 1.96 M40.17 166.1 C71.9 131.05, 100.75 95.32, 182.54 2.32 M40.17 166.1 C75.59 123.88, 112.97 81.96, 182.54 2.32 M45.16 166.46 C101.91 101.55, 158.7 35.39, 188.18 1.93 M45.16 166.46 C92.93 108.21, 142.16 51.42, 188.18 1.93 M50.8 166.06 C83.66 126.11, 118.37 86.9, 193.17 2.29 M50.8 166.06 C90.57 121.95, 130.52 76.87, 193.17 2.29 M55.79 166.42 C98.61 114.56, 145.49 65.61, 198.81 1.9 M55.79 166.42 C89.92 128.89, 122.15 90.21, 198.81 1.9 M61.43 166.03 C108.14 112.25, 154.28 57.71, 203.8 2.26 M61.43 166.03 C102.94 116.29, 146.47 66.47, 203.8 2.26 M66.42 166.39 C110.46 115.14, 155.39 64.89, 209.44 1.86 M66.42 166.39 C103.96 123.93, 141.14 82.62, 209.44 1.86 M72.06 165.99 C116.55 115.54, 157.91 65.86, 214.43 2.22 M72.06 165.99 C122.43 107.95, 174.9 47.56, 214.43 2.22 M77.05 166.35 C130.59 105.53, 183.67 43.12, 220.07 1.83 M77.05 166.35 C125.26 111.87, 172.24 58.31, 220.07 1.83 M82.69 165.96 C137.84 105.94, 191.52 41.79, 225.06 2.19 M82.69 165.96 C133.74 107.43, 183.36 50.36, 225.06 2.19 M87.68 166.32 C128.09 119.78, 167.53 73.55, 230.7 1.79 M87.68 166.32 C125.69 123.08, 162.79 79.78, 230.7 1.79 M92.67 166.68 C142.83 109.77, 193.44 51.39, 235.69 2.15 M92.67 166.68 C144.44 108.24, 195.97 47.04, 235.69 2.15 M98.31 166.28 C138.71 115.99, 180.28 69.93, 241.33 1.76 M98.31 166.28 C147.53 110.62, 196.35 53.63, 241.33 1.76 M103.3 166.64 C137.9 125.59, 173.89 87.87, 246.32 2.12 M103.3 166.64 C156.08 106.69, 208.6 46.04, 246.32 2.12 M108.94 166.25 C148.42 122.93, 186.31 81.16, 251.96 1.72 M108.94 166.25 C161 105.15, 212.81 45.73, 251.96 1.72 M113.93 166.61 C141.79 133.21, 172.21 98.82, 256.95 2.08 M113.93 166.61 C156.91 116.88, 202.51 64.75, 256.95 2.08 M119.57 166.21 C168.54 109.97, 219.75 52.97, 262.59 1.69 M119.57 166.21 C167.8 108.87, 219.02 51.26, 262.59 1.69 M124.56 166.57 C161.75 123.88, 199.53 80.79, 267.58 2.05 M124.56 166.57 C156.23 128.01, 190.3 88.85, 267.58 2.05 M130.2 166.18 C169.64 122.3, 211.11 75.14, 272.56 2.41 M130.2 166.18 C168.9 120.31, 208.25 76.02, 272.56 2.41 M135.19 166.54 C184.84 111.09, 234.19 52.53, 278.21 2.01 M135.19 166.54 C187.36 108.27, 238.69 50.75, 278.21 2.01 M140.83 166.15 C183.77 117.65, 225.92 69.25, 283.19 2.37 M140.83 166.15 C187.53 113.68, 233.77 60.45, 283.19 2.37 M145.82 166.51 C182.78 122.72, 219.21 80.97, 288.84 1.98 M145.82 166.51 C200.67 105.62, 254.4 42.05, 288.84 1.98 M151.46 166.11 C182.26 131.38, 212.83 96.05, 293.82 2.34 M151.46 166.11 C186.45 126.29, 219.87 88.44, 293.82 2.34 M156.45 166.47 C204.97 108.71, 255.45 52.85, 299.47 1.94 M156.45 166.47 C211.25 105.36, 265.8 42.21, 299.47 1.94 M162.09 166.08 C213.47 106.17, 266.91 43.59, 304.45 2.3 M162.09 166.08 C216.39 105.62, 269.56 44.48, 304.45 2.3 M167.08 166.44 C219.65 106.64, 271.52 49.11, 310.1 1.91 M167.08 166.44 C214.13 111.77, 261.4 58.06, 310.1 1.91 M172.72 166.04 C208.84 124.53, 247 83.41, 315.08 2.27 M172.72 166.04 C205.15 129.49, 236.42 90.2, 315.08 2.27 M177.71 166.4 C209.21 130.02, 243.57 90.18, 320.73 1.88 M177.71 166.4 C231.87 105.57, 287.31 42.39, 320.73 1.88 M183.35 166.01 C220.85 120.46, 259.07 75.83, 325.71 2.24 M183.35 166.01 C230.79 111.91, 277 57.33, 325.71 2.24 M188.34 166.37 C233.45 112.01, 280.56 61.66, 331.36 1.84 M188.34 166.37 C240.12 108.18, 290.83 49.09, 331.36 1.84 M193.98 165.97 C226.25 131.55, 256.45 92.17, 336.34 2.2 M193.98 165.97 C244.44 109.17, 294.38 52.76, 336.34 2.2 M198.97 166.33 C247.81 112.21, 292.15 60.33, 341.99 1.81 M198.97 166.33 C239.08 119.2, 279.09 71.05, 341.99 1.81 M203.95 166.69 C258.46 101.41, 315.11 40.21, 346.97 2.17 M203.95 166.69 C257.24 106.78, 308.75 46.24, 346.97 2.17 M209.6 166.3 C264.45 102.42, 321.03 37.17, 352.62 1.77 M209.6 166.3 C247.96 124.81, 283.55 84.13, 352.62 1.77 M214.58 166.66 C251.99 125.65, 288.37 82.49, 357.6 2.13 M214.58 166.66 C245.51 132.68, 277.5 95.94, 357.6 2.13 M220.23 166.26 C250.91 130.54, 281.47 95.54, 363.25 1.74 M220.23 166.26 C270.46 109.08, 319.88 50.52, 363.25 1.74 M225.21 166.62 C276.61 104.17, 331.06 44.88, 368.23 2.1 M225.21 166.62 C275.98 109.32, 326.13 50.94, 368.23 2.1 M230.86 166.23 C272.47 114.76, 315.56 64.33, 373.88 1.7 M230.86 166.23 C278.29 112.46, 325.09 57.98, 373.88 1.7 M235.84 166.59 C282.63 113.09, 329.86 58.03, 378.86 2.06 M235.84 166.59 C289.28 107.3, 342.41 45.74, 378.86 2.06 M241.49 166.2 C289.5 107.98, 340.13 49.49, 383.85 2.42 M241.49 166.2 C291 108.5, 340.73 52, 383.85 2.42 M246.47 166.56 C299.38 107.83, 352.11 46.7, 389.49 2.03 M246.47 166.56 C290.21 118.16, 331.91 67.65, 389.49 2.03 M252.12 166.16 C287.75 124.26, 324.97 81.73, 394.48 2.39 M252.12 166.16 C285.46 128.24, 318.01 91.66, 394.48 2.39 M257.1 166.52 C303.62 111.42, 349.89 60.83, 400.12 1.99 M257.1 166.52 C297.83 119.09, 339.11 70.77, 400.12 1.99 M262.75 166.13 C302.79 119.22, 343.84 71.97, 405.11 2.35 M262.75 166.13 C318.14 102.81, 374.01 39.26, 405.11 2.35 M267.73 166.49 C316.44 106.41, 370.98 48.7, 410.75 1.96 M267.73 166.49 C314.68 110.39, 365.02 54.21, 410.75 1.96 M273.38 166.09 C319.98 112.15, 368.66 57.51, 415.74 2.32 M273.38 166.09 C318.87 114.27, 364 62.75, 415.74 2.32 M278.36 166.45 C334.32 101.65, 390.55 37.39, 421.38 1.93 M278.36 166.45 C333.03 105.94, 385.98 44.74, 421.38 1.93 M284.01 166.06 C317.55 130.16, 348.17 93.82, 426.37 2.29 M284.01 166.06 C326.17 119.01, 367.32 70.57, 426.37 2.29 M288.99 166.42 C322.48 126.3, 360.56 85.92, 432.01 1.89 M288.99 166.42 C335.34 112.09, 384.1 55.77, 432.01 1.89 M294.64 166.02 C329.61 122.3, 366.47 79.8, 437 2.25 M294.64 166.02 C335.35 119.83, 373.64 74.32, 437 2.25 M299.62 166.38 C347.99 112.19, 397.82 52.47, 442.64 1.86 M299.62 166.38 C346.57 111.86, 394.7 58.56, 442.64 1.86 M305.27 165.99 C340.79 125.39, 378.2 81.4, 447.63 2.22 M305.27 165.99 C336.56 132.13, 367.18 97.05, 447.63 2.22 M310.25 166.35 C364.09 102.48, 420.3 41.58, 452.62 2.58 M310.25 166.35 C350.18 121.57, 389.7 76.26, 452.62 2.58 M315.9 165.95 C355.72 120.42, 398.24 73.94, 456.95 3.69 M315.9 165.95 C361.7 110.74, 408.98 56.77, 456.95 3.69 M320.88 166.31 C359.3 121.21, 397.93 79.49, 461.94 4.05 M320.88 166.31 C351.82 129.41, 382.91 93.48, 461.94 4.05 M325.87 166.67 C377.05 109.95, 427.19 53.77, 464.96 6.68 M325.87 166.67 C359.4 128.68, 392.04 90.92, 464.96 6.68 M331.51 166.28 C388.57 102.78, 442.05 38.29, 469.29 7.79 M331.51 166.28 C365.47 127.01, 402.68 86.23, 469.29 7.79 M336.5 166.64 C378.68 118.85, 420.7 69.51, 471.65 11.17 M336.5 166.64 C367.77 133.94, 395.41 99.18, 471.65 11.17 M342.14 166.25 C389.11 113.09, 435.96 59.54, 474.67 13.79 M342.14 166.25 C370.01 136.08, 397.01 102.54, 474.67 13.79 M347.13 166.61 C393.05 113.37, 435.84 64.96, 475.72 18.68 M347.13 166.61 C393.9 112.62, 438.68 61.59, 475.72 18.68 M352.77 166.21 C380.68 136.97, 404.58 109.35, 477.43 22.82 M352.77 166.21 C393.66 118.4, 432.23 72.18, 477.43 22.82 M357.76 166.57 C393.89 123.75, 430.73 84.47, 479.13 26.95 M357.76 166.57 C407.4 110.77, 454.79 55.34, 479.13 26.95 M363.4 166.18 C388.23 138.89, 413.94 108.18, 479.53 32.59 M363.4 166.18 C409.58 114.51, 453.68 63.43, 479.53 32.59 M368.39 166.54 C408.72 121.43, 446.29 77.48, 479.27 38.99 M368.39 166.54 C401.91 127.96, 434.91 89.62, 479.27 38.99 M374.03 166.14 C412.5 122.98, 449.36 75.75, 479 45.39 M374.03 166.14 C396.44 139.53, 422.02 110.62, 479 45.39 M379.02 166.5 C409.99 128.35, 442.35 96.32, 479.4 51.03 M379.02 166.5 C406.86 135.7, 432.68 106.1, 479.4 51.03 M384.66 166.11 C402.24 145.5, 422.81 122.74, 479.14 57.43 M384.66 166.11 C422.18 124.54, 458.93 82.58, 479.14 57.43 M389.65 166.47 C419.28 131.51, 452.05 96.59, 479.53 63.07 M389.65 166.47 C419.89 132.79, 448.41 99.82, 479.53 63.07 M395.29 166.07 C426.82 132.48, 455.45 96.46, 479.27 69.47 M395.29 166.07 C416.92 140.62, 438.2 116.72, 479.27 69.47 M400.28 166.43 C420.47 143.05, 442.61 120.52, 479.66 75.11 M400.28 166.43 C426.13 136.5, 452.84 104.75, 479.66 75.11 M405.92 166.04 C427.91 140.97, 448.1 117.36, 479.4 81.51 M405.92 166.04 C431.73 136.16, 458.33 107.47, 479.4 81.51 M410.91 166.4 C439.08 133.08, 467.49 104.88, 479.8 87.15 M410.91 166.4 C439.78 134.31, 465.58 102.73, 479.8 87.15 M416.55 166 C435.71 144.16, 456.89 119.23, 479.54 93.55 M416.55 166 C429.32 151.02, 443.37 135.34, 479.54 93.55 M421.54 166.36 C441.2 143.55, 460.71 119.18, 479.28 99.95 M421.54 166.36 C436.83 150.09, 450.82 132.4, 479.28 99.95 M427.18 165.97 C444.74 144.68, 466.03 122.82, 479.67 105.59 M427.18 165.97 C445.95 144.28, 463.23 123.49, 479.67 105.59 M432.17 166.33 C443.24 153.78, 457.62 140.63, 479.41 111.99 M432.17 166.33 C444.12 153.05, 454.84 140.71, 479.41 111.99 M437.16 166.69 C445.39 157.04, 457.46 143.05, 479.8 117.63 M437.16 166.69 C448.39 153.72, 458.12 143.15, 479.8 117.63 M442.8 166.29 C452.3 154.17, 462.42 144.1, 479.54 124.03 M442.8 166.29 C455.94 150.05, 469.26 134.36, 479.54 124.03 M447.79 166.65 C456.94 155.72, 466.27 142.82, 477.31 132.69 M447.79 166.65 C458.81 155.08, 470.45 141.43, 477.31 132.69 M454.09 165.51 C459.1 157.63, 466.97 149.2, 476.39 139.85 M454.09 165.51 C460.38 158.66, 465.45 152.71, 476.39 139.85 M464.32 159.83 C469.65 156.05, 473.41 148.37, 475.48 147 M464.32 159.83 C466.17 157.25, 468.66 154.88, 475.48 147" stroke="#b2f2bb" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C179.89 -1.74, 328.07 0.08, 445 0 M32 0 C192.02 0.21, 350.62 -0.17, 445 0 M445 0 C465.52 0.25, 477.14 11.05, 477 32 M445 0 C467.31 -0.47, 475.37 11.81, 477 32 M477 32 C475.83 66.77, 477.32 98.5, 477 132 M477 32 C477.31 66.84, 477.55 102.41, 477 132 M477 132 C477.4 152.02, 464.97 162.99, 445 164 M477 132 C478.29 151.95, 467.37 166.29, 445 164 M445 164 C309.65 164.67, 175.86 163.51, 32 164 M445 164 C339.17 163.73, 232.87 164.46, 32 164 M32 164 C9.7 164.07, -1.59 152.38, 0 132 M32 164 C9.56 164.33, 1.77 155.2, 0 132 M0 132 C1.13 92.98, -0.18 57.12, 0 32 M0 132 C0.33 111.2, 1.53 90.02, 0 32 M0 32 C-1.62 11.04, 10.67 1.54, 32 0 M0 32 C0.37 11.97, 12.23 -2.15, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(813.1090530561523 574.6183182417908) rotate(0 220.3125 76.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">node:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">  file:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    name: 33333333333333333333333333333333-test</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    digest: &lt;blob-a-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    size: 3</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    executable: false</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">references: []</text><text x="0" y="134.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">narinfo: …</text></g><g stroke-linecap="round"><g transform="translate(942.4901398439579 641.0079592022902) rotate(0 -391.38489601802087 -282.46266052120785)"><path d="M-0.12 0.36 C-130.24 -93.43, -651.31 -469.22, -781.67 -563.32 M-1.64 -0.49 C-131.84 -94.62, -652.36 -471.5, -782.65 -565.29" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(942.4901398439579 641.0079592022902) rotate(0 -391.38489601802087 -282.46266052120785)"><path d="M-753.18 -557.22 C-759.58 -560.05, -767.44 -560.17, -782.06 -566.37 M-754.28 -557.06 C-760.35 -558.69, -767.09 -560.3, -781.67 -564.75" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(942.4901398439579 641.0079592022902) rotate(0 -391.38489601802087 -282.46266052120785)"><path d="M-765.2 -540.58 C-768.89 -547.05, -774.11 -550.81, -782.06 -566.37 M-766.3 -540.42 C-769.51 -545.96, -773.48 -551.41, -781.67 -564.75" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask></svg>
\ No newline at end of file
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/tvixbolt.webm b/users/flokli/presentations/2023-09-09-nixcon-tvix/tvixbolt.webm
new file mode 100644
index 0000000000..69bd20f193
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/tvixbolt.webm
Binary files differdiff --git a/users/flokli/presentations/2023-09-13-asg-tvix-store/default.nix b/users/flokli/presentations/2023-09-13-asg-tvix-store/default.nix
new file mode 100644
index 0000000000..840f21de81
--- /dev/null
+++ b/users/flokli/presentations/2023-09-13-asg-tvix-store/default.nix
@@ -0,0 +1,32 @@
+{ depot, pkgs, ... }:
+
+let
+  inherit (pkgs)
+    fontconfig qrencode runCommand stdenv;
+  mkQr = url: runCommand "qrcode.png" { } ''
+    ${qrencode}/bin/qrencode -o $out -t SVG -s 5 \
+      --background=fafafa \
+      --foreground=000000 \
+      ${url}
+  '';
+in
+stdenv.mkDerivation {
+  name = "2023-asg-tvix-store";
+  src = ./.;
+
+  FONTCONFIG_FILE = pkgs.makeFontsConf {
+    fontDirectories = with pkgs; [ jetbrains-mono fira fira-code fira-mono lato ];
+  };
+
+  nativeBuildInputs = [ pkgs.reveal-md pkgs.graphviz ];
+
+  buildPhase = ''
+    cp ${depot.tvix.logo}/logo.png tvix-logo.png
+    cp ${mkQr "https://flokli.de"} qrcode-flokli.svg
+    cp ${mkQr "https://tvix.dev"} qrcode-tvix.svg
+
+    mkdir -p $out
+    cp tvix-store-graph-blob-directory.svg $out/
+    reveal-md --static $out presentation.md
+  '';
+}
diff --git a/users/flokli/presentations/2023-09-13-asg-tvix-store/presentation.md b/users/flokli/presentations/2023-09-13-asg-tvix-store/presentation.md
new file mode 100644
index 0000000000..978934f9a4
--- /dev/null
+++ b/users/flokli/presentations/2023-09-13-asg-tvix-store/presentation.md
@@ -0,0 +1,138 @@
+---
+author:
+- Florian Klink
+date: 2023-09-09
+title: "tvix-store: A content-addressed file system and sync protocol"
+theme: moon
+revealOptions:
+  transition: 'fade'
+---
+
+## tvix-store
+### A content-addressed file system and sync protocol
+
+2023-09-13
+
+Florian Klink / flokli
+
+---
+
+## Whoami
+
+- <!-- .element: class="fragment" -->
+  flokli
+- <!-- .element: class="fragment" -->
+  Nix/NixOS contributor
+  - maintain systemd, nss and more low-level stuff there
+- <!-- .element: class="fragment" -->
+  Freelance Nix/DevOps consultant
+
+Note: more Kubernetes/DevOps exposure with work
+
+---
+
+## What is tvix-store?
+- <!-- .element: class="fragment" -->
+  A new implementation of a content-addressed "storage system"
+  - <!-- .element: class="fragment" -->
+    part of the Tvix Project, a (WIP) reimplementation of Nix and auxillary components in Rust
+  - <!-- .element: class="fragment" -->
+    Storage model: think about git trees and its Merkle DAG…
+  - <!-- .element: class="fragment" -->
+    … but with nicer wire format (`.proto`) and hash function (blake3)
+
+---
+
+## Storage model
+- <!-- .element: class="fragment" -->
+  Once you know the root: everything else is content-addressed
+   - <!-- .element: class="fragment" -->
+     No timestamps, no uid/gid, no xattrs, only one way to represent the same tree
+- <!-- .element: class="fragment" -->
+  Automatic dedup of identical subtrees in different file system trees
+- <!-- .element: class="fragment" -->
+  Automatic dedup of identical blobs (and you can do more chunking underneath too)
+
+---
+
+## Storage model (cont.)
+- <!-- .element: class="fragment" -->
+  Granular seekable access into blobs
+- <!-- .element: class="fragment" -->
+  verified streaming thanks to BLAKE3 and Bao, faulty data is detected early on
+- <!-- .element: class="fragment" -->
+  Everything below can be retrieved from anyone without having to trust (P2P substitution, CDNs, …)
+
+---
+
+## Usecases
+- <!-- .element: class="fragment" -->
+  File system tree delivery
+- <!-- .element: class="fragment" -->
+  Container image delivery
+- <!-- .element: class="fragment" -->
+  Backing store for VCS
+- <!-- .element: class="fragment" -->
+  Granular access into large datasets
+
+---
+
+## Status
+- <!-- .element: class="fragment" -->
+  In-memory backend, a local K/V backend (Sled)
+- <!-- .element: class="fragment" -->
+  FUSE filesystem
+- <!-- .element: class="fragment" -->
+  A gRPC API to transfer things, bindings for golang and rust
+- <!-- .element: class="fragment" -->
+  some object storage backends in development (GCS, NATS)
+- <!-- .element: class="fragment" -->
+  FUTUREWORK: more storage backends / store composition / in-kernel module?
+
+Notes: of course you can use your own network protocol too, like HTTP CAS or iroh....plug different stores together to represent caches, blobfs
+
+---
+
+## Contributing
+
+- <!-- .element: class="fragment" -->
+  Join the IRC channel (`#tvl` on `hackint`), bridged to Matrix and XMPP
+- <!-- .element: class="fragment" -->
+  Check our issue tracker
+- <!-- .element: class="fragment" -->
+  Try to use it and tell us how you broke it!
+
+Note: if this sounds useful to you, reach out!
+
+---
+
+# Thanks!
+
+<style>
+.container{
+    display: flex;
+}
+.col{
+    flex: 1;
+}
+</style>
+
+<div class="container">
+
+<div class="col">
+Florian Klink / <a href="https://flokli.de">flokli.de</a><br />
+<img src="qrcode-flokli.svg" />
+</div>
+
+<div class="col">
+Tvix / <a href="https://tvix.dev">tvix.dev</a><br />
+<img src="qrcode-tvix.svg" />
+</div>
+
+</div>
+
+---
+
+## Structure
+
+[tvix-store graph](tvix-store-graph-blob-directory.svg)
diff --git a/users/flokli/presentations/2023-09-13-asg-tvix-store/tvix-store-graph-blob-directory.svg b/users/flokli/presentations/2023-09-13-asg-tvix-store/tvix-store-graph-blob-directory.svg
new file mode 100644
index 0000000000..2c87350d5b
--- /dev/null
+++ b/users/flokli/presentations/2023-09-13-asg-tvix-store/tvix-store-graph-blob-directory.svg
@@ -0,0 +1,17 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 691.01171875 836.19140625" width="691.01171875" height="836.19140625">
+  <!-- svg-source:excalidraw -->
+  
+  <defs>
+    <style class="style-fonts">
+      @font-face {
+        font-family: "Virgil";
+        src: url("https://excalidraw.com/Virgil.woff2");
+      }
+      @font-face {
+        font-family: "Cascadia";
+        src: url("https://excalidraw.com/Cascadia.woff2");
+      }
+    </style>
+    
+  </defs>
+  <rect x="0" y="0" width="691.01171875" height="836.19140625" fill="#ffffff"></rect><g stroke-linecap="round" transform="translate(10 64.05078125) rotate(0 71.10546875 23.685546875)"><path d="M3.15 2.74 C3.15 2.74, 3.15 2.74, 3.15 2.74 M3.15 2.74 C3.15 2.74, 3.15 2.74, 3.15 2.74 M1.58 10.65 C3.55 6.56, 7.08 5.83, 10.11 0.84 M1.58 10.65 C4.54 7.52, 5.83 5.33, 10.11 0.84 M1.97 16.29 C5.97 12.52, 7.97 9.89, 14.44 1.95 M1.97 16.29 C5.39 12.84, 8.96 8.01, 14.44 1.95 M1.71 22.69 C5.44 18.07, 11.5 12.49, 19.43 2.31 M1.71 22.69 C6.47 16.78, 11.69 12.31, 19.43 2.31 M1.45 29.09 C9.97 18.06, 21.19 8.77, 25.07 1.92 M1.45 29.09 C9.61 17.76, 19.35 7.56, 25.07 1.92 M1.85 34.73 C6.66 29.67, 14.83 20.48, 30.06 2.28 M1.85 34.73 C8.29 27.25, 17.05 18.03, 30.06 2.28 M2.24 40.37 C10.77 28.82, 20.92 18.16, 35.7 1.88 M2.24 40.37 C14.65 26.46, 25.75 13.13, 35.7 1.88 M3.29 45.26 C19.71 28.3, 32.79 9.79, 40.69 2.24 M3.29 45.26 C14.19 32.96, 23.82 22.41, 40.69 2.24 M6.97 47.13 C21.76 30.34, 38.17 12.66, 45.67 2.6 M6.97 47.13 C19.37 32.8, 32.58 18.66, 45.67 2.6 M12.61 46.74 C23.7 34.53, 32.71 23.85, 51.32 2.21 M12.61 46.74 C23.04 33.52, 34.64 21.81, 51.32 2.21 M17.6 47.1 C32.92 32.2, 44.82 17.61, 56.3 2.57 M17.6 47.1 C26.86 36.54, 34.43 27.02, 56.3 2.57 M22.58 47.46 C36.55 31.33, 51.38 11.55, 61.95 2.17 M22.58 47.46 C35.33 33.95, 46.66 20.15, 61.95 2.17 M28.23 47.06 C40.8 33.63, 55.34 16.98, 66.93 2.53 M28.23 47.06 C36.54 38.37, 43.25 29.02, 66.93 2.53 M33.21 47.42 C48.21 29.57, 61.74 14.8, 72.58 2.14 M33.21 47.42 C44.03 35.16, 55.8 21.76, 72.58 2.14 M38.86 47.03 C49.09 34.45, 58.15 26.51, 77.56 2.5 M38.86 47.03 C53.73 30.24, 67.18 15.29, 77.56 2.5 M43.84 47.39 C57.75 33, 73.31 14.12, 82.55 2.86 M43.84 47.39 C53.3 37.24, 61.17 27.55, 82.55 2.86 M49.49 46.99 C62.68 35.47, 70.45 21.6, 88.19 2.46 M49.49 46.99 C61.84 34.86, 73.11 21.46, 88.19 2.46 M54.47 47.35 C61.1 38.29, 69.25 27.83, 93.18 2.82 M54.47 47.35 C66.26 32.61, 76.99 20.02, 93.18 2.82 M60.12 46.96 C73.27 30.81, 84.64 16.32, 98.82 2.43 M60.12 46.96 C69.94 34.83, 80.66 21.92, 98.82 2.43 M65.1 47.32 C73.48 35.83, 86.65 26.47, 103.81 2.79 M65.1 47.32 C78.64 32.14, 91.97 18.02, 103.81 2.79 M70.09 47.68 C85.6 30.67, 98.3 12.05, 109.45 2.4 M70.09 47.68 C81.34 34.02, 94.32 21.42, 109.45 2.4 M75.73 47.28 C89.43 31.61, 102.84 18.22, 114.44 2.76 M75.73 47.28 C85.67 35.55, 96.35 22.65, 114.44 2.76 M80.72 47.64 C91.93 34.98, 103.63 17.52, 119.43 3.12 M80.72 47.64 C90.36 36.8, 98.88 24.6, 119.43 3.12 M86.36 47.25 C101.83 29.55, 117.32 12.7, 125.07 2.72 M86.36 47.25 C99.66 32.13, 111.08 17.18, 125.07 2.72 M91.35 47.61 C104.35 33.22, 117.8 19.95, 130.06 3.08 M91.35 47.61 C103.56 34.45, 114.2 20.27, 130.06 3.08 M96.99 47.21 C103.59 38.71, 113.94 28.96, 136.36 1.93 M96.99 47.21 C106.26 36.64, 117.24 24.44, 136.36 1.93 M101.98 47.57 C111.9 34.49, 126.09 20.88, 140.03 3.8 M101.98 47.57 C114.1 33.1, 128.09 16.65, 140.03 3.8 M106.97 47.93 C114.85 39.44, 121.62 28.8, 141.74 7.93 M106.97 47.93 C117.52 37.38, 125.94 26.56, 141.74 7.93 M112.61 47.54 C120.79 38.54, 126.18 28.87, 144.1 11.31 M112.61 47.54 C125.62 33.32, 137.05 19.84, 144.1 11.31 M117.6 47.9 C122.32 42.89, 130.44 32.85, 144.5 16.96 M117.6 47.9 C126.99 36.5, 135.5 27.33, 144.5 16.96 M123.24 47.51 C127.95 43.64, 132.17 36.33, 144.23 23.35 M123.24 47.51 C129.47 41.35, 136.09 32.69, 144.23 23.35 M128.23 47.87 C131.28 40.54, 137.28 36.59, 143.97 29.75 M128.23 47.87 C132.3 43.29, 136.94 37.23, 143.97 29.75 M132.56 48.98 C136.69 46.03, 136.69 42.77, 143.71 36.15 M132.56 48.98 C136.02 46.4, 138.73 42.29, 143.71 36.15" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M11.84 0 M11.84 0 C38.02 2.65, 61.84 0.28, 130.37 0 M11.84 0 C38.21 1.94, 65.76 1.5, 130.37 0 M130.37 0 C139.47 1, 143.75 5.88, 142.21 11.84 M130.37 0 C137.21 0.92, 139.99 4.87, 142.21 11.84 M142.21 11.84 C140.42 17.68, 141.72 25.2, 142.21 35.53 M142.21 11.84 C142.2 20.55, 141.47 28.9, 142.21 35.53 M142.21 35.53 C143.14 42.65, 138.92 45.76, 130.37 47.37 M142.21 35.53 C141.26 45.41, 136.07 48.58, 130.37 47.37 M130.37 47.37 C100.03 46.91, 70.33 46.17, 11.84 47.37 M130.37 47.37 C93.27 45.68, 57.65 45.47, 11.84 47.37 M11.84 47.37 C3.83 47.21, 0.04 43.58, 0 35.53 M11.84 47.37 C3.58 49.37, 2.09 44.62, 0 35.53 M0 35.53 C-0.11 25.56, 1.74 17.16, 0 11.84 M0 35.53 C-0.73 27.14, 0.25 16.41, 0 11.84 M0 11.84 C1.26 4.93, 5.18 0.97, 11.84 0 M0 11.84 C0.62 1.9, 3.42 2.05, 11.84 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(15 78.13632812499998) rotate(0 65.625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">0x01 0x02 0x03</text></g><g stroke-linecap="round" transform="translate(11.80078125 149.404296875) rotate(0 51.5 24.5)"><path d="M3.26 2.83 C3.26 2.83, 3.26 2.83, 3.26 2.83 M3.26 2.83 C3.26 2.83, 3.26 2.83, 3.26 2.83 M1.69 10.74 C4.79 9.22, 6.14 6.4, 10.22 0.93 M1.69 10.74 C4.82 7.44, 8.36 3.46, 10.22 0.93 M1.43 17.14 C5.08 10.94, 12.94 4.63, 14.55 2.05 M1.43 17.14 C7.42 12.34, 11.48 5.83, 14.55 2.05 M1.82 22.78 C7.79 17.76, 9.92 11.78, 20.19 1.65 M1.82 22.78 C6.91 17.54, 10.69 12.89, 20.19 1.65 M1.56 29.18 C10.68 19.58, 16.05 8.86, 25.18 2.01 M1.56 29.18 C6.66 24.1, 10.7 18.88, 25.18 2.01 M1.95 34.82 C14.06 20.7, 22.56 11.87, 30.16 2.37 M1.95 34.82 C12.05 23.95, 21.94 11.12, 30.16 2.37 M1.69 41.22 C7.98 31.97, 17.66 24.23, 35.81 1.98 M1.69 41.22 C14.67 26.87, 25.96 11.6, 35.81 1.98 M3.4 45.35 C12.42 31.5, 21.88 20.01, 40.79 2.34 M3.4 45.35 C14.79 32.81, 25.98 18.39, 40.79 2.34 M5.76 48.73 C19.03 33.54, 30.13 20.75, 46.44 1.94 M5.76 48.73 C15.95 35.19, 26.77 22.13, 46.44 1.94 M10.75 49.09 C25.08 35.85, 34.98 19.45, 51.42 2.3 M10.75 49.09 C25.36 34.29, 38.07 17.14, 51.42 2.3 M14.42 50.96 C25.24 34.64, 40.8 20.23, 57.07 1.91 M14.42 50.96 C31.56 31.52, 47.54 13.07, 57.07 1.91 M20.07 50.57 C36.58 31.78, 51.41 11.57, 62.05 2.27 M20.07 50.57 C35.27 32.37, 49.62 15.57, 62.05 2.27 M25.05 50.93 C33.69 40.39, 45.49 30.76, 67.7 1.87 M25.05 50.93 C39.57 35.58, 52.89 19.97, 67.7 1.87 M30.04 51.29 C42.51 36.46, 56.98 20.61, 72.68 2.23 M30.04 51.29 C39.08 40.34, 49.2 29.9, 72.68 2.23 M35.68 50.89 C47.44 39.9, 56.68 27.08, 78.33 1.84 M35.68 50.89 C52.05 33.69, 66.26 15.86, 78.33 1.84 M40.67 51.25 C53.32 36.02, 69.16 19.85, 83.31 2.2 M40.67 51.25 C53.19 37.93, 64.14 24.78, 83.31 2.2 M46.31 50.86 C62.11 32.31, 78.52 15.58, 88.96 1.8 M46.31 50.86 C57.68 37.06, 69.81 23.8, 88.96 1.8 M51.3 51.22 C59.29 38.98, 72.37 29.04, 93.94 2.16 M51.3 51.22 C64.73 35.51, 79.44 17.72, 93.94 2.16 M56.94 50.83 C69.81 34.76, 86.63 19.59, 98.93 2.52 M56.94 50.83 C72.69 32.66, 90.36 14.37, 98.93 2.52 M61.93 51.19 C77.07 35.49, 89.58 18.3, 101.95 5.15 M61.93 51.19 C76.05 33.49, 92.37 16.74, 101.95 5.15 M67.57 50.79 C76.42 41.59, 82.64 33.63, 103 10.04 M67.57 50.79 C76.25 40.25, 84.99 30.22, 103 10.04 M72.56 51.15 C79.09 44.86, 86.76 36.01, 103.4 15.68 M72.56 51.15 C78.4 43.41, 84.37 36.12, 103.4 15.68 M78.2 50.76 C84.15 41.95, 92.56 33.07, 103.13 22.08 M78.2 50.76 C88.29 39.34, 96.97 28.67, 103.13 22.08 M83.19 51.12 C88.53 45.31, 96.1 36.68, 103.53 27.72 M83.19 51.12 C91.24 41.6, 98.32 33.94, 103.53 27.72 M88.83 50.72 C90.5 47.29, 97.87 43.07, 103.27 34.12 M88.83 50.72 C94.68 44.06, 98.18 39.73, 103.27 34.12 M93.82 51.08 C96.56 47.12, 100.38 44.41, 104.97 38.25 M93.82 51.08 C96.61 47.17, 101.29 42.48, 104.97 38.25" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M12.25 0 M12.25 0 C28.51 0, 42.5 2.25, 90.75 0 M12.25 0 C32.14 -0.62, 52.39 1.04, 90.75 0 M90.75 0 C100.71 0.09, 101.8 3.77, 103 12.25 M90.75 0 C97.64 -0.83, 104 5.94, 103 12.25 M103 12.25 C101.33 19.82, 101.63 32.15, 103 36.75 M103 12.25 C102.35 21.13, 102.15 31.06, 103 36.75 M103 36.75 C104.56 46.87, 99.61 50.21, 90.75 49 M103 36.75 C104.15 46.69, 101.13 47.94, 90.75 49 M90.75 49 C62.94 48.31, 35.39 47.65, 12.25 49 M90.75 49 C67.73 49.76, 43.51 49.69, 12.25 49 M12.25 49 C3.6 47.48, -1.71 45.84, 0 36.75 M12.25 49 C3.19 49.75, -1.85 43.96, 0 36.75 M0 36.75 C-0.6 25.33, 0.5 18.11, 0 12.25 M0 36.75 C-0.07 30.3, -1.01 22.9, 0 12.25 M0 12.25 C-1.34 4.43, 2.32 -0.12, 12.25 0 M0 12.25 C-0.18 4.12, 4.26 -0.37, 12.25 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(16.80078125 164.30429687499998) rotate(0 42.1875 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">0x04 0x05</text></g><g stroke-linecap="round" transform="translate(14.93359375 236.67578125) rotate(0 4.138671875 25.552734375)"><path d="M-0.29 2.4 C-0.29 2.4, -0.29 2.4, -0.29 2.4 M-0.29 2.4 C-0.29 2.4, -0.29 2.4, -0.29 2.4 M0.11 8.04 C1.82 6.13, 2.96 4.61, 4.7 2.76 M0.11 8.04 C1.05 6.92, 2.71 5.33, 4.7 2.76 M-0.15 14.44 C2.61 10.36, 4.52 9.27, 9.69 3.12 M-0.15 14.44 C3 10.45, 7.79 5.81, 9.69 3.12 M0.24 20.08 C1.78 17.33, 6.21 13.86, 9.43 9.52 M0.24 20.08 C3.9 16.52, 6.5 12.39, 9.43 9.52 M-0.02 26.48 C2.46 23.23, 7.19 20.21, 9.82 15.16 M-0.02 26.48 C2.52 23.58, 5.92 19.96, 9.82 15.16 M-0.28 32.88 C3.43 29.78, 4.96 26.51, 9.56 21.56 M-0.28 32.88 C1.68 30.37, 4.75 26.47, 9.56 21.56 M0.11 38.52 C2.43 35.55, 6.84 32.06, 9.3 27.96 M0.11 38.52 C3.34 34.69, 7.79 30.27, 9.3 27.96 M-0.15 44.92 C4.41 39.72, 7.29 36.44, 9.69 33.6 M-0.15 44.92 C2.92 41.4, 5.54 37.99, 9.69 33.6 M0.9 49.81 C2.58 47.91, 5.61 43.76, 9.43 40 M0.9 49.81 C3.14 46.67, 5.91 43.78, 9.43 40 M3.92 52.43 C5.01 50.49, 6.18 49.02, 9.82 45.64 M3.92 52.43 C5.89 50.77, 7.52 48.28, 9.82 45.64" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M2.07 0 M2.07 0 C2.97 -0.21, 4.11 0.12, 6.21 0 M2.07 0 C3.19 -0.16, 4.23 0.11, 6.21 0 M6.21 0 C8.97 0.45, 9.01 1.39, 8.28 2.07 M6.21 0 C9.57 -1.98, 9.89 2.6, 8.28 2.07 M8.28 2.07 C8.66 12.58, 9.73 22.31, 8.28 49.04 M8.28 2.07 C8.53 15.26, 7.49 28.86, 8.28 49.04 M8.28 49.04 C6.95 49.38, 7.58 52.75, 6.21 51.11 M8.28 49.04 C10.35 52.7, 9.64 53.24, 6.21 51.11 M6.21 51.11 C5.12 51.08, 3.76 50.9, 2.07 51.11 M6.21 51.11 C5.14 51.07, 4.25 51.01, 2.07 51.11 M2.07 51.11 C2.45 52.5, -1.17 52.21, 0 49.04 M2.07 51.11 C-1.47 50.3, 2.23 51.76, 0 49.04 M0 49.04 C-0.13 38.39, -0.48 26.48, 0 2.07 M0 49.04 C-1.31 32.15, -0.35 13.45, 0 2.07 M0 2.07 C0.41 0.36, -0.67 -0.56, 2.07 0 M0 2.07 C-0.35 -0.58, -1.03 -1.54, 2.07 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(10.23046875 10) rotate(0 23.4375 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">Blobs</text></g><g transform="translate(279.12890625 12.759374999999977) rotate(0 51.5625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">Directories</text></g><g stroke-linecap="round" transform="translate(283.875 68.3828125) rotate(0 197 72.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C8.28 14.49, 8.49 11.04, 18.1 2.48 M3.66 19.09 C7.7 14.57, 11.52 11.66, 18.1 2.48 M2.75 26.24 C11.51 19.05, 17.92 11.98, 24.4 1.33 M2.75 26.24 C7.23 20.55, 12.55 15.89, 24.4 1.33 M2.48 32.64 C12.12 24.05, 19.28 13.72, 30.7 0.18 M2.48 32.64 C9.16 24.56, 14.83 17.39, 30.7 0.18 M2.88 38.28 C11.43 28.28, 22.31 16.44, 34.37 2.05 M2.88 38.28 C14.6 24.25, 27.89 9.94, 34.37 2.05 M2.62 44.68 C9.32 34.89, 17.39 26.88, 39.36 2.41 M2.62 44.68 C11.95 33.82, 21.66 21.02, 39.36 2.41 M2.36 51.07 C12.81 41.21, 19.43 27.76, 45 2.02 M2.36 51.07 C16.68 33.29, 30.46 16.46, 45 2.02 M2.75 56.72 C16.64 41.98, 28.98 26.21, 49.99 2.38 M2.75 56.72 C21 35.95, 39.52 14.81, 49.99 2.38 M2.49 63.11 C19.63 44.26, 39.08 21.21, 55.63 1.98 M2.49 63.11 C17.01 46.73, 31.61 30.35, 55.63 1.98 M2.88 68.76 C22.9 46.33, 43.08 21.26, 60.62 2.34 M2.88 68.76 C23.19 45.39, 41.88 24.57, 60.62 2.34 M2.62 75.16 C23.83 54.32, 39.89 29.17, 66.26 1.95 M2.62 75.16 C18.47 56.13, 34.46 38.2, 66.26 1.95 M2.36 81.55 C29.87 50.88, 54.51 22.49, 71.25 2.31 M2.36 81.55 C26.5 54.38, 49.03 26.57, 71.25 2.31 M2.76 87.2 C25.76 61.24, 52.69 29.56, 76.89 1.91 M2.76 87.2 C22.68 64.01, 42.85 40.22, 76.89 1.91 M2.49 93.59 C30.49 61.54, 57.87 26.06, 81.88 2.27 M2.49 93.59 C31.33 60.52, 59.32 27.03, 81.88 2.27 M2.89 99.24 C28.16 70.88, 49.29 44.38, 87.52 1.88 M2.89 99.24 C22.19 77.54, 40.48 55.68, 87.52 1.88 M2.63 105.64 C27.42 78.52, 50.89 50.01, 92.51 2.24 M2.63 105.64 C38.6 64.43, 74.48 23.22, 92.51 2.24 M2.37 112.03 C35.69 71.87, 67.49 34.66, 98.15 1.85 M2.37 112.03 C29.87 80.37, 57.31 48.8, 98.15 1.85 M2.76 117.68 C35.35 78.95, 70.62 41.89, 103.14 2.21 M2.76 117.68 C24.16 94.43, 44.43 69.26, 103.14 2.21 M3.16 123.32 C27.31 95.67, 53.64 64.97, 108.78 1.81 M3.16 123.32 C40.3 79.27, 78.36 37.25, 108.78 1.81 M4.86 127.45 C35.22 90.31, 66.42 52.47, 113.77 2.17 M4.86 127.45 C27.89 100.07, 53.15 74.47, 113.77 2.17 M5.26 133.1 C41.54 89.35, 82.58 48.1, 119.41 1.78 M5.26 133.1 C50.78 80.9, 96.57 28.36, 119.41 1.78 M7.62 136.47 C50.54 85.68, 96.45 32.28, 124.4 2.14 M7.62 136.47 C50.54 87.26, 95.13 37.29, 124.4 2.14 M10.64 139.1 C44.47 96.28, 83.89 53.02, 130.04 1.74 M10.64 139.1 C49.89 96.71, 86.98 53.36, 130.04 1.74 M14.31 140.97 C55.74 92.08, 97.64 42.48, 135.03 2.1 M14.31 140.97 C49.36 97.94, 86.03 55.71, 135.03 2.1 M17.33 143.59 C62.88 90.12, 107.49 38.88, 140.67 1.71 M17.33 143.59 C52.29 102.09, 87.68 60.93, 140.67 1.71 M21.01 145.46 C48.28 114.13, 76.95 81.48, 145.66 2.07 M21.01 145.46 C64.61 95.84, 107.71 47.87, 145.66 2.07 M25.34 146.58 C73.23 89.86, 125.31 35.24, 150.65 2.43 M25.34 146.58 C51.9 117.94, 78.32 86.29, 150.65 2.43 M32.29 144.67 C78.07 95.21, 118.24 44.56, 156.29 2.03 M32.29 144.67 C74.54 98.13, 114.59 49.94, 156.29 2.03 M37.28 145.03 C85.09 88.58, 133.69 36.74, 161.28 2.39 M37.28 145.03 C69.44 108.12, 100.26 71.2, 161.28 2.39 M42.92 144.64 C90.72 91.41, 136.95 40.49, 166.92 2 M42.92 144.64 C66.95 114.58, 93.7 85.42, 166.92 2 M47.91 145 C75.02 114.35, 102.25 80.22, 171.91 2.36 M47.91 145 C87.4 99.39, 126.35 54.03, 171.91 2.36 M52.9 145.36 C79.04 113.6, 107.32 79.81, 177.55 1.96 M52.9 145.36 C77.61 115.94, 103.48 86, 177.55 1.96 M58.54 144.96 C92.12 105.3, 128.8 63.9, 182.54 2.32 M58.54 144.96 C106.61 90.51, 152.9 36.35, 182.54 2.32 M63.53 145.32 C107.47 99.27, 149.03 48.65, 188.18 1.93 M63.53 145.32 C96.13 109.61, 126.87 72.99, 188.18 1.93 M69.17 144.93 C102.47 104.11, 137.86 61.81, 193.17 2.29 M69.17 144.93 C110.04 98.38, 152.23 51.84, 193.17 2.29 M74.16 145.29 C104.78 108.19, 138.63 71.58, 198.81 1.9 M74.16 145.29 C99.31 117.03, 123.84 87.71, 198.81 1.9 M79.8 144.9 C130.71 87.32, 178.54 30.53, 203.8 2.26 M79.8 144.9 C124.29 94.42, 167.51 43.91, 203.8 2.26 M84.79 145.26 C111.41 111.48, 143.37 80.45, 209.44 1.86 M84.79 145.26 C130.36 93.05, 176.97 39.33, 209.44 1.86 M90.43 144.86 C128.08 97.62, 168.49 56.08, 214.43 2.22 M90.43 144.86 C130.35 96.04, 171.08 48.72, 214.43 2.22 M95.42 145.22 C146.19 90.39, 192.4 31.56, 220.07 1.83 M95.42 145.22 C120.72 114.85, 146.9 84.4, 220.07 1.83 M101.06 144.83 C127.22 111.52, 152.8 83.04, 225.06 2.19 M101.06 144.83 C132.86 108.13, 163.34 71.68, 225.06 2.19 M106.05 145.19 C154.17 92.56, 198.8 39.31, 230.7 1.79 M106.05 145.19 C136.53 110.59, 166.08 76.75, 230.7 1.79 M111.69 144.79 C138.65 117.15, 162.06 85.81, 235.69 2.15 M111.69 144.79 C137.61 113.65, 164.22 81.98, 235.69 2.15 M116.68 145.15 C157.06 99.25, 194.58 56.02, 241.33 1.76 M116.68 145.15 C155.1 99.61, 194.87 53.74, 241.33 1.76 M122.32 144.76 C155.38 104.12, 188.9 66.56, 246.32 2.12 M122.32 144.76 C171.19 89.94, 218.89 34.94, 246.32 2.12 M127.31 145.12 C160.63 107.75, 195.41 68.39, 251.96 1.72 M127.31 145.12 C171.79 94.11, 216.13 43.25, 251.96 1.72 M132.95 144.72 C167.14 104.52, 206.3 64.21, 256.95 2.08 M132.95 144.72 C164.99 105.52, 198.48 68.92, 256.95 2.08 M137.94 145.08 C183.71 92.31, 226.45 45.7, 262.59 1.69 M137.94 145.08 C163.89 115.91, 190.22 85.14, 262.59 1.69 M143.58 144.69 C174.92 109.48, 206.89 70.46, 267.58 2.05 M143.58 144.69 C170.4 113.21, 199.74 82.17, 267.58 2.05 M148.57 145.05 C178.08 111.76, 203.33 81.73, 272.56 2.41 M148.57 145.05 C173.64 115.53, 198.59 86.39, 272.56 2.41 M154.21 144.65 C187.55 108.07, 219.55 68.66, 278.21 2.01 M154.21 144.65 C200.06 91.97, 243.47 39.89, 278.21 2.01 M159.2 145.01 C186.66 112.8, 212.53 85.21, 283.19 2.37 M159.2 145.01 C207.12 89.92, 255.48 37.14, 283.19 2.37 M164.19 145.37 C202.12 104.92, 237.2 59.79, 288.84 1.98 M164.19 145.37 C194.82 106.77, 226.84 70.45, 288.84 1.98 M169.83 144.98 C212.12 93.05, 258.35 43.46, 293.82 2.34 M169.83 144.98 C201.44 106.46, 235.81 67.61, 293.82 2.34 M174.82 145.34 C207.54 111.65, 235.58 74.76, 299.47 1.94 M174.82 145.34 C199.92 118.44, 225.68 88.79, 299.47 1.94 M180.46 144.94 C208.5 116.42, 230.68 85.13, 304.45 2.3 M180.46 144.94 C218.97 100.78, 259.51 55.75, 304.45 2.3 M185.45 145.31 C221.24 102.72, 256.86 59.74, 310.1 1.91 M185.45 145.31 C232.8 92.21, 280.33 37.72, 310.1 1.91 M191.09 144.91 C238.27 88.19, 288.2 35.33, 315.08 2.27 M191.09 144.91 C220.59 109.55, 252.22 74.41, 315.08 2.27 M196.08 145.27 C239.76 94.85, 284.75 46.35, 320.73 1.88 M196.08 145.27 C230.49 104.94, 265.25 68.33, 320.73 1.88 M201.72 144.88 C234.66 108.79, 263.22 71.79, 325.71 2.24 M201.72 144.88 C237.32 101.42, 273.7 59.07, 325.71 2.24 M206.71 145.24 C253.21 90.64, 300.99 37.76, 331.36 1.84 M206.71 145.24 C240.65 108.22, 273.71 69.33, 331.36 1.84 M212.35 144.84 C242.19 114.5, 267.67 79.17, 336.34 2.2 M212.35 144.84 C236.6 115.07, 262.16 86.57, 336.34 2.2 M217.34 145.2 C251.25 105.29, 286.42 64.08, 341.99 1.81 M217.34 145.2 C249.04 108.05, 278.75 71.6, 341.99 1.81 M222.98 144.81 C254.78 109.71, 284.95 73.51, 346.97 2.17 M222.98 144.81 C251.44 112.17, 277.38 81.44, 346.97 2.17 M227.97 145.17 C258.67 109.25, 290.39 71.64, 352.62 1.77 M227.97 145.17 C253.46 115.85, 279.91 85.09, 352.62 1.77 M233.61 144.77 C279.92 90.22, 328.36 36.09, 357.6 2.13 M233.61 144.77 C263.54 109.84, 296.58 72.44, 357.6 2.13 M238.6 145.13 C284.33 94.02, 329.86 38.18, 363.25 1.74 M238.6 145.13 C270.6 110.97, 300.1 77.3, 363.25 1.74 M244.24 144.74 C285.03 98.47, 325.99 49.52, 368.23 2.1 M244.24 144.74 C288.81 94.28, 333.65 43.13, 368.23 2.1 M249.23 145.1 C274.07 113.24, 304.32 83.42, 373.22 2.46 M249.23 145.1 C288.32 102.57, 326.71 58.15, 373.22 2.46 M254.87 144.7 C297.9 94.29, 343.78 43.11, 377.55 3.57 M254.87 144.7 C286.66 106.11, 320.39 66.72, 377.55 3.57 M259.86 145.06 C295.8 103.74, 331.06 61.72, 381.23 5.44 M259.86 145.06 C298.82 101.67, 336.82 56.01, 381.23 5.44 M265.5 144.67 C313.19 90.52, 361.44 33.85, 384.9 7.31 M265.5 144.67 C292.92 114.87, 319.46 83.51, 384.9 7.31 M270.49 145.03 C293.76 118.67, 317.19 90.91, 387.92 9.94 M270.49 145.03 C298.6 112.33, 327.42 78.58, 387.92 9.94 M276.13 144.63 C309.18 107.62, 339.56 70.71, 390.28 13.32 M276.13 144.63 C314.84 98.86, 353.06 54.55, 390.28 13.32 M281.12 144.99 C305.94 116.7, 328.37 88.76, 391.99 17.45 M281.12 144.99 C321.03 97.29, 362.22 51.33, 391.99 17.45 M286.1 145.35 C324.81 103.13, 365.35 57.02, 393.04 22.34 M286.1 145.35 C313.97 112.26, 343.79 77.31, 393.04 22.34 M291.75 144.96 C324.16 107.97, 358.78 71.44, 395.4 25.72 M291.75 144.96 C322.28 110.33, 352.3 74.13, 395.4 25.72 M296.73 145.32 C322.53 113.82, 350.61 86.17, 395.14 32.11 M296.73 145.32 C330.9 104.68, 367.32 64.68, 395.14 32.11 M302.38 144.93 C320.38 122.95, 340.8 101.95, 395.54 37.76 M302.38 144.93 C328.33 112.21, 356.43 82.25, 395.54 37.76 M307.36 145.29 C337.92 110.64, 366.55 76.31, 395.28 44.15 M307.36 145.29 C325.37 123.43, 344.42 100.44, 395.28 44.15 M313.01 144.89 C335.45 120.95, 355.71 93.18, 395.01 50.55 M313.01 144.89 C342.22 110.33, 372.53 76.59, 395.01 50.55 M317.99 145.25 C335.46 125.76, 350.14 107.98, 395.41 56.2 M317.99 145.25 C334.45 126.54, 349.07 108.69, 395.41 56.2 M323.64 144.86 C350.88 112.04, 380.82 81.41, 395.15 62.59 M323.64 144.86 C349.73 114.44, 376.12 84.65, 395.15 62.59 M328.62 145.22 C342.07 126.15, 358.11 110, 395.54 68.24 M328.62 145.22 C352.38 118.21, 373.64 91.69, 395.54 68.24 M334.27 144.82 C356.58 116.53, 380.06 91.2, 395.28 74.63 M334.27 144.82 C352.85 123.85, 373.24 101.61, 395.28 74.63 M339.25 145.18 C350.58 130.51, 366.65 115.79, 395.67 80.28 M339.25 145.18 C360.84 120.94, 381.57 97.17, 395.67 80.28 M344.9 144.79 C359.95 129.93, 374.97 110.51, 395.41 86.68 M344.9 144.79 C359.66 128.95, 373.84 112.99, 395.41 86.68 M349.88 145.15 C365.74 124.35, 384.39 105.92, 395.81 92.32 M349.88 145.15 C365.01 128.41, 378.61 111.44, 395.81 92.32 M355.53 144.75 C366.83 128.33, 381.85 115.58, 395.55 98.72 M355.53 144.75 C366.51 134.09, 376.57 121.06, 395.55 98.72 M360.51 145.11 C371.57 133.46, 379.69 124.34, 395.94 104.36 M360.51 145.11 C371.82 131.23, 384.33 117.41, 395.94 104.36 M364.19 146.98 C372.36 139.23, 375.92 131.75, 395.68 110.76 M364.19 146.98 C375.25 134.47, 383.71 124.14, 395.68 110.76 M370.49 145.83 C377.43 139.68, 383.18 129.21, 396.73 115.65 M370.49 145.83 C377.33 137.24, 382.58 130.01, 396.73 115.65 M377.44 143.93 C380.98 139.22, 385.26 134.38, 391.88 127.33 M377.44 143.93 C381.01 139.93, 385.77 134.2, 391.88 127.33" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C158.41 -0.65, 283.84 1.37, 362 0 M32 0 C99.31 -2.94, 165.01 -1.66, 362 0 M362 0 C382.37 -1.19, 392.06 11.27, 394 32 M362 0 C383.53 0.55, 395.84 12.5, 394 32 M394 32 C396.17 49.29, 394.95 72.1, 394 113 M394 32 C394.74 57.51, 393.79 80.56, 394 113 M394 113 C393.49 135.83, 381.87 143.9, 362 145 M394 113 C391.98 135.04, 383.6 143.19, 362 145 M362 145 C291.31 144.27, 221.15 142.78, 32 145 M362 145 C249.93 143.31, 138.97 142.96, 32 145 M32 145 C8.71 146.69, -1.86 135.95, 0 113 M32 145 C10.21 143.8, 1.65 132.18, 0 113 M0 113 C0.55 94.72, -1.35 75.26, 0 32 M0 113 C-0.88 90.36, -1.5 66.27, 0 32 M0 32 C1.9 12.2, 9.12 -1.48, 32 0 M0 32 C0.22 11.69, 10.1 1.96, 32 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(288.875 73.3828125) rotate(0 140.625 67.19999999999999)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories: []</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: .keep</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;empty-blob-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g stroke-linecap="round" transform="translate(279.59193843887 271.159696266393) rotate(0 198.99999999999994 130)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C7.06 13.81, 9.24 9.34, 18.1 2.48 M3.66 19.09 C8.44 11.73, 13.76 5.27, 18.1 2.48 M2.75 26.24 C10 16.18, 18.61 6.13, 24.4 1.33 M2.75 26.24 C9.44 19.31, 14.57 12.4, 24.4 1.33 M2.48 32.64 C10.34 21.03, 17.74 12.3, 30.7 0.18 M2.48 32.64 C13.49 21.44, 21.83 9.48, 30.7 0.18 M2.88 38.28 C12.91 25, 26.86 13.76, 34.37 2.05 M2.88 38.28 C12.64 28.15, 21.63 16.63, 34.37 2.05 M2.62 44.68 C14.17 33.27, 22.89 18.23, 39.36 2.41 M2.62 44.68 C12.44 34.48, 21.24 23.55, 39.36 2.41 M2.36 51.07 C18.27 36.19, 31.27 19.8, 45 2.02 M2.36 51.07 C15.34 37.29, 27.12 23.41, 45 2.02 M2.75 56.72 C18.22 37.56, 35.93 17.94, 49.99 2.38 M2.75 56.72 C20.57 34.56, 37.8 14.24, 49.99 2.38 M2.49 63.11 C18.51 41.78, 36.66 23.69, 55.63 1.98 M2.49 63.11 C16.06 46.84, 30.13 32.58, 55.63 1.98 M2.23 69.51 C14.08 54.4, 27.81 42.3, 60.62 2.34 M2.23 69.51 C14.8 54.54, 28.11 38.47, 60.62 2.34 M2.62 75.16 C23.22 53.14, 45.11 28.68, 66.26 1.95 M2.62 75.16 C18.68 55.49, 35.76 36.87, 66.26 1.95 M2.36 81.55 C29.49 51.06, 52.67 20.07, 71.25 2.31 M2.36 81.55 C23.35 59.17, 43.86 35.46, 71.25 2.31 M2.76 87.2 C29.08 59.14, 54.26 30.38, 76.89 1.91 M2.76 87.2 C25.2 61.93, 47.39 35.45, 76.89 1.91 M2.49 93.59 C22.97 73.16, 41.24 50.49, 81.88 2.27 M2.49 93.59 C22.85 70.13, 41.2 48.51, 81.88 2.27 M2.23 99.99 C26.73 70.85, 53 42.34, 87.52 1.88 M2.23 99.99 C26.11 70.65, 52.18 43.37, 87.52 1.88 M2.63 105.64 C29.25 73.43, 57.28 44.11, 92.51 2.24 M2.63 105.64 C22.21 84.02, 40.09 61.9, 92.51 2.24 M2.37 112.03 C24.78 84.92, 46.32 60.61, 98.15 1.85 M2.37 112.03 C40.71 68.73, 77.43 25.35, 98.15 1.85 M2.1 118.43 C32.45 81.76, 64.34 45.79, 103.14 2.21 M2.1 118.43 C26.04 90.13, 50.16 63.23, 103.14 2.21 M2.5 124.07 C34.35 88.85, 66.58 53.31, 108.78 1.81 M2.5 124.07 C35.8 86.3, 67.38 48.42, 108.78 1.81 M2.24 130.47 C44.13 79.64, 85.07 32.68, 113.77 2.17 M2.24 130.47 C28.26 100.65, 54.41 68.41, 113.77 2.17 M2.63 136.11 C39.83 96.08, 73.86 52.5, 119.41 1.78 M2.63 136.11 C27.92 105.97, 55.2 76.2, 119.41 1.78 M2.37 142.51 C38.64 103.62, 72.54 62.27, 124.4 2.14 M2.37 142.51 C38.27 104.7, 71.42 64.68, 124.4 2.14 M2.11 148.91 C31.62 115.14, 60.21 82.46, 130.04 1.74 M2.11 148.91 C30.64 115.22, 60.37 82.49, 130.04 1.74 M2.5 154.55 C45.43 105.72, 87.93 57.79, 135.03 2.1 M2.5 154.55 C39.77 111.59, 78.21 68.92, 135.03 2.1 M2.24 160.95 C40.01 120.72, 75.93 77.72, 140.67 1.71 M2.24 160.95 C44.66 112.93, 87.03 64.56, 140.67 1.71 M1.98 167.35 C39.14 124.53, 74.75 81.57, 145.66 2.07 M1.98 167.35 C47.85 117.91, 91.41 66.21, 145.66 2.07 M2.38 172.99 C58.38 106.95, 115.31 43.43, 150.65 2.43 M2.38 172.99 C53.11 115.34, 104.81 56.32, 150.65 2.43 M2.11 179.39 C64.27 108.42, 124.58 39.52, 156.29 2.03 M2.11 179.39 C42.26 131.15, 83.61 83.43, 156.29 2.03 M2.51 185.03 C62.42 116.85, 122.68 43.77, 161.28 2.39 M2.51 185.03 C56.66 122.43, 112.32 58.31, 161.28 2.39 M2.25 191.43 C51.58 133.03, 102.03 75.72, 166.92 2 M2.25 191.43 C38.3 149.45, 75.45 107.32, 166.92 2 M1.99 197.83 C42.68 152.88, 80.66 108.12, 171.91 2.36 M1.99 197.83 C66.15 123.85, 132.83 47.33, 171.91 2.36 M2.38 203.47 C55.91 143.98, 107.7 83.76, 177.55 1.96 M2.38 203.47 C56.81 142.73, 110.46 81.58, 177.55 1.96 M2.12 209.87 C66.55 137, 131.82 62.14, 182.54 2.32 M2.12 209.87 C50.61 157.03, 98.73 101.44, 182.54 2.32 M1.86 216.27 C63.05 142.96, 126.25 69.69, 188.18 1.93 M1.86 216.27 C65.44 140.95, 131.03 66.87, 188.18 1.93 M2.25 221.91 C59.05 161.57, 111.38 97.22, 193.17 2.29 M2.25 221.91 C52.49 164.23, 101.5 106.36, 193.17 2.29 M1.33 229.06 C64.26 156.57, 124.34 84.79, 198.81 1.9 M1.33 229.06 C60.97 162.1, 120.98 94.62, 198.81 1.9 M1.73 234.71 C54.24 174.7, 106.54 116.97, 203.8 2.26 M1.73 234.71 C76.5 149.79, 152.59 63.83, 203.8 2.26 M2.78 239.59 C49.77 186.82, 94.95 134.94, 209.44 1.86 M2.78 239.59 C83.35 146.44, 163.63 53.44, 209.44 1.86 M4.49 243.73 C48.55 190.47, 93.86 139.53, 214.43 2.22 M4.49 243.73 C63.53 174.61, 122.23 106.2, 214.43 2.22 M6.19 247.86 C77.48 165.73, 149.04 81.52, 220.07 1.83 M6.19 247.86 C85.41 157.55, 164.08 66.86, 220.07 1.83 M8.56 251.24 C90.22 155.68, 175.16 56.51, 225.06 2.19 M8.56 251.24 C89.12 157.29, 172.43 62.13, 225.06 2.19 M10.92 254.62 C69.41 191.54, 124.13 126, 230.7 1.79 M10.92 254.62 C69.41 187.27, 127.03 120.42, 230.7 1.79 M13.94 257.24 C82.45 176.73, 153.61 97.39, 235.69 2.15 M13.94 257.24 C93.45 166.82, 173.91 75.69, 235.69 2.15 M18.27 258.36 C76.98 194.1, 131.53 129.14, 241.33 1.76 M18.27 258.36 C66.67 201.61, 115.46 145.37, 241.33 1.76 M22.6 259.47 C75.95 201, 128.47 140.06, 246.32 2.12 M22.6 259.47 C70.35 207.82, 116.17 155.22, 246.32 2.12 M26.27 261.34 C95.93 180.59, 169.95 95.39, 251.96 1.72 M26.27 261.34 C89.37 190.53, 152.1 118.22, 251.96 1.72 M32.57 260.19 C96.06 189.84, 155.98 120.97, 256.95 2.08 M32.57 260.19 C78.18 205.29, 124.7 151.48, 256.95 2.08 M37.56 260.55 C117 171.22, 192.42 83.99, 262.59 1.69 M37.56 260.55 C121.96 163.6, 206.2 67.19, 262.59 1.69 M43.2 260.16 C105.28 189.09, 169.45 117.92, 267.58 2.05 M43.2 260.16 C123.84 168.01, 203.45 76.43, 267.58 2.05 M48.19 260.52 C117.81 178.99, 186.9 100.6, 272.56 2.41 M48.19 260.52 C125.53 171.84, 202.4 82.45, 272.56 2.41 M53.83 260.12 C138.08 162.28, 222.76 67.81, 278.21 2.01 M53.83 260.12 C128.55 171.85, 204.35 84.41, 278.21 2.01 M58.82 260.48 C127.06 184.42, 193 106.58, 283.19 2.37 M58.82 260.48 C135.91 173.81, 211.1 87.23, 283.19 2.37 M64.46 260.09 C109.71 206.88, 156.75 154.77, 288.84 1.98 M64.46 260.09 C142.84 169.53, 220.7 80.3, 288.84 1.98 M69.45 260.45 C138.09 183.73, 208 105.35, 293.82 2.34 M69.45 260.45 C156.6 162.77, 241.99 64.65, 293.82 2.34 M75.09 260.06 C143.01 182.6, 211.21 103.49, 299.47 1.94 M75.09 260.06 C136 192, 195.87 123.37, 299.47 1.94 M80.08 260.42 C126.17 206.79, 172.95 151.57, 304.45 2.3 M80.08 260.42 C129.25 204.69, 179.83 146.37, 304.45 2.3 M85.72 260.02 C157.76 178.28, 226.22 97.47, 310.1 1.91 M85.72 260.02 C149.05 184.68, 213.67 111.24, 310.1 1.91 M90.71 260.38 C181.48 157.9, 269.25 55.91, 315.08 2.27 M90.71 260.38 C157.59 183.34, 224.55 106.5, 315.08 2.27 M95.7 260.74 C174.5 168.64, 255.03 76.7, 320.73 1.88 M95.7 260.74 C164.52 183.36, 232.23 105.12, 320.73 1.88 M101.34 260.35 C181.6 167.21, 261.15 73.9, 325.71 2.24 M101.34 260.35 C177.77 172.29, 255.39 82.95, 325.71 2.24 M106.33 260.71 C168.93 189.98, 230.06 115.81, 331.36 1.84 M106.33 260.71 C170.53 184.03, 237.17 107.92, 331.36 1.84 M111.97 260.31 C176.35 190.63, 236.85 119.43, 336.34 2.2 M111.97 260.31 C157.57 208.21, 204.38 154.93, 336.34 2.2 M116.96 260.67 C164.3 205.68, 212.63 148.72, 341.99 1.81 M116.96 260.67 C197.94 169.46, 278.58 77.5, 341.99 1.81 M122.6 260.28 C175.27 200.79, 229.84 138.31, 346.97 2.17 M122.6 260.28 C172.5 202.83, 220.76 146.35, 346.97 2.17 M127.59 260.64 C215.15 158.17, 304.42 54.77, 352.62 1.77 M127.59 260.64 C188.96 189.82, 250.22 118.28, 352.62 1.77 M133.23 260.24 C183.32 201.93, 235.94 141.72, 357.6 2.13 M133.23 260.24 C184.28 203.09, 233.15 145.34, 357.6 2.13 M138.22 260.6 C220.44 165.6, 301.95 72.93, 363.25 1.74 M138.22 260.6 C215.16 172.58, 291.69 83.91, 363.25 1.74 M143.86 260.21 C216.13 172.96, 292.05 87.04, 368.23 2.1 M143.86 260.21 C223.54 169.38, 302.36 80.03, 368.23 2.1 M148.85 260.57 C228.87 169.81, 309.22 79.04, 374.53 0.95 M148.85 260.57 C225.85 169.3, 304.04 78.63, 374.53 0.95 M154.49 260.17 C230.89 172.65, 306.45 86.57, 378.21 2.82 M154.49 260.17 C210.94 195.8, 265.6 130.92, 378.21 2.82 M159.48 260.53 C223.94 184.22, 290.75 110.57, 382.54 3.93 M159.48 260.53 C244.05 163.85, 329.27 67.22, 382.54 3.93 M165.12 260.14 C236.95 176.83, 309.56 92.2, 386.87 5.05 M165.12 260.14 C221.23 195.95, 279.16 130.62, 386.87 5.05 M170.11 260.5 C221.21 201.49, 273.24 142.28, 389.23 8.43 M170.11 260.5 C218.37 205.87, 266.44 150.41, 389.23 8.43 M175.75 260.11 C241.58 187.82, 304.45 113.61, 392.25 11.05 M175.75 260.11 C222.69 207.98, 267.39 155.95, 392.25 11.05 M180.74 260.47 C245.33 188.58, 307.54 115.78, 395.27 13.68 M180.74 260.47 C261.6 168.2, 340.34 76.88, 395.27 13.68 M186.38 260.07 C251.57 186.65, 319.28 107.6, 396.32 18.56 M186.38 260.07 C236.65 200.09, 289.45 140.81, 396.32 18.56 M191.37 260.43 C255.8 183.29, 323.42 107.08, 397.37 23.45 M191.37 260.43 C233.86 209.7, 277.09 160.88, 397.37 23.45 M197.01 260.04 C273.9 173.76, 350.14 83.39, 397.77 29.09 M197.01 260.04 C238.49 210.28, 280.91 161.3, 397.77 29.09 M202 260.4 C249.95 204.58, 301.21 145.35, 399.47 33.23 M202 260.4 C246.27 207.11, 292.1 154.9, 399.47 33.23 M207.64 260 C282.04 175.37, 353.03 93.88, 399.21 39.63 M207.64 260 C252.23 206.89, 298.22 154.54, 399.21 39.63 M212.63 260.36 C270.79 190.87, 332.59 125.04, 399.61 45.27 M212.63 260.36 C255.76 212.2, 297.66 164.88, 399.61 45.27 M217.62 260.72 C270.87 200.01, 324.39 137.32, 399.34 51.67 M217.62 260.72 C286.2 182.97, 352.23 105.9, 399.34 51.67 M223.26 260.33 C266.88 209.47, 311.98 157.05, 399.74 57.31 M223.26 260.33 C270.48 203.33, 319.36 147.73, 399.74 57.31 M228.25 260.69 C264.48 217.85, 298.81 176.68, 399.48 63.71 M228.25 260.69 C287.06 192.75, 345.09 124.31, 399.48 63.71 M233.89 260.29 C283.82 202.81, 333.35 147.36, 399.22 70.11 M233.89 260.29 C296.32 189.36, 357.95 117.97, 399.22 70.11 M238.88 260.65 C288.24 201.51, 340.4 144.75, 399.61 75.75 M238.88 260.65 C302.98 187.65, 365.83 115.51, 399.61 75.75 M244.52 260.26 C301.41 192.57, 362.82 123.97, 399.35 82.15 M244.52 260.26 C276.87 221.94, 309.71 183.55, 399.35 82.15 M249.51 260.62 C294.86 210.74, 338.19 159.76, 399.74 87.79 M249.51 260.62 C288.68 213.68, 329.48 168.33, 399.74 87.79 M255.15 260.22 C306.36 203.12, 357.73 144.26, 399.48 94.19 M255.15 260.22 C310.1 197.2, 364.53 133.07, 399.48 94.19 M260.14 260.58 C306.6 208.36, 353.31 154.47, 399.22 100.59 M260.14 260.58 C314.84 198.11, 370.53 134.35, 399.22 100.59 M265.78 260.19 C310.07 209.18, 353.85 155.07, 399.62 106.23 M265.78 260.19 C313.52 203.42, 361.67 148.41, 399.62 106.23 M270.77 260.55 C312.94 210.71, 350.92 165.03, 399.35 112.63 M270.77 260.55 C313.74 212.09, 354.22 165.25, 399.35 112.63 M276.41 260.15 C322.47 205.94, 367.79 155.3, 399.75 118.27 M276.41 260.15 C319.24 210.97, 362.85 162.61, 399.75 118.27 M281.4 260.51 C309.82 228.04, 337.57 195, 399.49 124.67 M281.4 260.51 C315.37 218.59, 351.47 180.05, 399.49 124.67 M287.04 260.12 C329.88 210.1, 376.19 158.03, 399.23 131.06 M287.04 260.12 C314.45 229.24, 339.01 200.91, 399.23 131.06 M292.03 260.48 C332.66 211.87, 372.61 167.01, 399.62 136.71 M292.03 260.48 C317.01 231.17, 342.37 202.7, 399.62 136.71 M297.67 260.09 C320.79 233.82, 342.65 206.24, 399.36 143.11 M297.67 260.09 C337.01 212.55, 377.78 167.34, 399.36 143.11 M302.66 260.45 C335.5 224.45, 365.99 190.46, 399.75 148.75 M302.66 260.45 C341.03 214.95, 379.75 172.24, 399.75 148.75 M308.3 260.05 C331.34 234.81, 350.27 209.28, 399.49 155.15 M308.3 260.05 C337.96 226.28, 369 190.93, 399.49 155.15 M313.29 260.41 C344.16 226.42, 373.26 191.65, 399.23 161.54 M313.29 260.41 C340.87 229.56, 366.67 199.76, 399.23 161.54 M318.93 260.02 C339.08 235.8, 357.34 215.88, 399.63 167.19 M318.93 260.02 C338.18 238.19, 356.44 217.12, 399.63 167.19 M323.92 260.38 C343.38 240.84, 359.55 216.05, 399.36 173.59 M323.92 260.38 C339.58 242.35, 357.05 223.74, 399.36 173.59 M328.9 260.74 C356.6 230.93, 381.38 201.75, 399.76 179.23 M328.9 260.74 C355.35 229.48, 382.07 199.6, 399.76 179.23 M334.55 260.34 C353.55 235.84, 374.53 215.15, 399.5 185.63 M334.55 260.34 C350.96 240.58, 366.19 222.61, 399.5 185.63 M339.53 260.7 C355.36 243.43, 370.27 221.73, 399.24 192.02 M339.53 260.7 C361.66 234.82, 383.39 210.67, 399.24 192.02 M345.18 260.31 C362.54 238.6, 380.33 220.43, 399.63 197.67 M345.18 260.31 C359.44 243.81, 374.08 228.32, 399.63 197.67 M350.16 260.67 C364.5 243.76, 376.71 230.97, 399.37 204.06 M350.16 260.67 C363.93 245.32, 377.91 229.24, 399.37 204.06 M355.81 260.27 C372.91 242.23, 387.92 223.73, 399.76 209.71 M355.81 260.27 C373.85 240.57, 389.06 220.82, 399.76 209.71 M360.79 260.63 C368.09 252.39, 377.96 242.27, 399.5 216.11 M360.79 260.63 C372.53 247.08, 385.83 232.19, 399.5 216.11 M366.44 260.24 C372.92 252.64, 384.19 241.59, 399.24 222.5 M366.44 260.24 C374.31 251.44, 384.03 240.28, 399.24 222.5 M372.74 259.09 C379.45 251.45, 383.63 244.65, 398.98 228.9 M372.74 259.09 C383.24 247.41, 391.87 236.99, 398.98 228.9 M378.38 258.69 C382.28 255.84, 387.86 247.21, 398.06 236.05 M378.38 258.69 C382.66 254.99, 386.79 248.97, 398.06 236.05 M385.99 256.04 C389.48 251.84, 392.18 249.55, 394.52 246.23 M385.99 256.04 C389.44 252.25, 392.29 247.67, 394.52 246.23" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C123.14 1.36, 217.94 1.27, 366 0 M32 0 C131.29 -1.79, 231.25 -1.6, 366 0 M366 0 C387.8 0.82, 396.14 9, 398 32 M366 0 C388.4 -1.28, 396.3 11.05, 398 32 M398 32 C396.68 77, 397.3 120.08, 398 228 M398 32 C397.91 99.78, 396.94 166.68, 398 228 M398 228 C399.84 247.36, 385.48 260.38, 366 260 M398 228 C399.48 250.74, 389.16 259.77, 366 260 M366 260 C278.41 260.83, 191.33 259.2, 32 260 M366 260 C254.85 261.48, 142.72 261.51, 32 260 M32 260 C9.06 261.46, -1.27 247.64, 0 228 M32 260 C8.96 260.25, 0.79 249.13, 0 228 M0 228 C-2.62 187.23, -0.98 150.02, 0 32 M0 228 C-1.12 169.92, -1.74 111.79, 0 32 M0 32 C-0.23 11.83, 11.85 0.53, 32 0 M0 32 C1.74 9.13, 11.06 1.81, 32 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(284.59193843887 276.159696266393) rotate(0 182.81249999999994 124.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: keep</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;directory-with-keep-digest&gt;</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 1</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files:</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: .keep</text><text x="0" y="134.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;empty-blob-digest&gt;</text><text x="0" y="153.6" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="172.79999999999998" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="192" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks:</text><text x="0" y="211.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: aa</text><text x="0" y="230.39999999999998" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   target: /nix/store/somewhereelse</text></g><g stroke-linecap="round" transform="translate(292.58984375 581.578125) rotate(0 192 36.5)"><path d="M4.86 4.22 C4.86 4.22, 4.86 4.22, 4.86 4.22 M4.86 4.22 C4.86 4.22, 4.86 4.22, 4.86 4.22 M3.28 12.13 C5.02 8.82, 8.25 4.62, 11.81 2.32 M3.28 12.13 C6.19 8.3, 9.57 5.6, 11.81 2.32 M1.71 20.04 C8.34 16.13, 10.93 7.61, 18.77 0.41 M1.71 20.04 C6.51 15.46, 9.85 9.62, 18.77 0.41 M2.11 25.68 C10.79 16.53, 14.3 8.85, 23.76 0.77 M2.11 25.68 C8.12 18.52, 15.95 11.63, 23.76 0.77 M1.84 32.08 C8.84 22.24, 16.07 17.29, 29.4 0.38 M1.84 32.08 C11.36 20.47, 23.16 9.07, 29.4 0.38 M2.24 37.72 C9.27 29.77, 15.4 22.85, 34.39 0.74 M2.24 37.72 C15.42 23.1, 27.12 8.74, 34.39 0.74 M2.63 43.36 C8.76 33.58, 17 25.79, 40.03 0.35 M2.63 43.36 C13.14 31.06, 23.17 18.26, 40.03 0.35 M2.37 49.76 C16.36 34.07, 29.4 16.32, 45.02 0.71 M2.37 49.76 C17.93 32.43, 33.93 13.74, 45.02 0.71 M2.77 55.41 C19.05 35.42, 38.7 10.68, 50.66 0.31 M2.77 55.41 C19.96 34.91, 39.43 13.51, 50.66 0.31 M1.19 63.31 C17.21 49, 29.42 31.12, 55.65 0.67 M1.19 63.31 C15.9 46.95, 29.88 30.05, 55.65 0.67 M4.21 65.94 C21.41 44.81, 41.02 26.02, 61.29 0.28 M4.21 65.94 C24.28 42.91, 45.96 20.08, 61.29 0.28 M6.57 69.32 C23.78 51.2, 36.27 33.53, 66.28 0.64 M6.57 69.32 C19.35 53.63, 31.93 38.99, 66.28 0.64 M9.59 71.94 C25.17 56.48, 40.49 38.06, 71.92 0.24 M9.59 71.94 C23.84 58.2, 36.53 43.78, 71.92 0.24 M13.92 73.05 C31.98 52.05, 54.85 25.68, 76.91 0.6 M13.92 73.05 C30.95 53.92, 48.62 33.36, 76.91 0.6 M19.57 72.66 C38.35 51.77, 53.43 34.49, 82.55 0.21 M19.57 72.66 C32.02 56.9, 44.97 41.55, 82.55 0.21 M24.55 73.02 C48.11 46.73, 66.96 24.34, 87.54 0.57 M24.55 73.02 C47.62 46.37, 71.59 19.54, 87.54 0.57 M30.2 72.63 C46.25 51.67, 67.22 31.55, 93.18 0.17 M30.2 72.63 C53.38 46.94, 75.41 21.61, 93.18 0.17 M35.18 72.99 C55.38 48.59, 74.75 27.94, 98.17 0.53 M35.18 72.99 C57.1 47.59, 77.7 22.46, 98.17 0.53 M40.83 72.59 C64.45 43.72, 89.12 19.76, 103.81 0.14 M40.83 72.59 C61.26 47.84, 82.21 23.37, 103.81 0.14 M45.81 72.95 C66.24 52.79, 83.08 30.43, 108.8 0.5 M45.81 72.95 C67.8 48.81, 88.58 24.77, 108.8 0.5 M51.46 72.56 C64.09 57.9, 78.25 44.63, 113.78 0.86 M51.46 72.56 C72.83 47.42, 94.74 22.72, 113.78 0.86 M56.44 72.92 C74.65 52.58, 96.04 31.05, 119.43 0.46 M56.44 72.92 C81.73 45.16, 105.23 18.3, 119.43 0.46 M62.09 72.52 C80.07 51.93, 98.45 29.82, 124.41 0.82 M62.09 72.52 C79.04 54.05, 95.96 34.96, 124.41 0.82 M67.07 72.88 C80.12 59.08, 92.94 42.21, 130.06 0.43 M67.07 72.88 C80.66 58.14, 95.45 40.91, 130.06 0.43 M72.06 73.24 C93.79 49.66, 111.38 26.32, 135.04 0.79 M72.06 73.24 C89.58 51.72, 107.82 32.09, 135.04 0.79 M77.7 72.85 C104.72 44.35, 127.17 17, 140.69 0.4 M77.7 72.85 C96.13 51.38, 114.98 29.95, 140.69 0.4 M82.69 73.21 C104.71 47.57, 127.59 22.31, 145.67 0.76 M82.69 73.21 C101.71 51.47, 120.77 29.01, 145.67 0.76 M88.33 72.81 C110.76 47.24, 131.64 20.5, 151.32 0.36 M88.33 72.81 C109 48.14, 130.72 23.09, 151.32 0.36 M93.32 73.17 C111.15 55.01, 126.29 31.78, 156.3 0.72 M93.32 73.17 C110.49 51.39, 129.9 29.91, 156.3 0.72 M98.96 72.78 C118.26 54.28, 133.1 34.79, 161.95 0.33 M98.96 72.78 C111.06 59.03, 124.81 44.07, 161.95 0.33 M103.95 73.14 C115.72 58.92, 129.64 40.85, 166.93 0.69 M103.95 73.14 C126.22 47.2, 149.58 21.56, 166.93 0.69 M109.59 72.74 C123.31 56.85, 140.06 38.01, 172.58 0.29 M109.59 72.74 C124.38 56.56, 137.2 40.37, 172.58 0.29 M114.58 73.1 C138.07 45.66, 163.14 15.75, 177.56 0.65 M114.58 73.1 C131.86 52.94, 147.99 32.77, 177.56 0.65 M120.22 72.71 C133.34 57.46, 150.05 38.7, 183.21 0.26 M120.22 72.71 C135.3 57.12, 148.59 39.51, 183.21 0.26 M125.21 73.07 C147.35 45.64, 169.73 21.54, 188.19 0.62 M125.21 73.07 C146.83 48.56, 167.28 24.07, 188.19 0.62 M130.85 72.68 C149.9 47.27, 173.04 22.78, 193.84 0.22 M130.85 72.68 C153.31 46.45, 175.71 22.68, 193.84 0.22 M135.84 73.04 C157.75 47.97, 181.46 23.22, 198.82 0.58 M135.84 73.04 C157.01 47.37, 178.52 21.52, 198.82 0.58 M141.49 72.64 C163.6 47.54, 184.92 24.28, 203.81 0.94 M141.49 72.64 C157.97 55.11, 171.75 36.25, 203.81 0.94 M146.47 73 C164.74 50.07, 184.75 31.86, 209.45 0.55 M146.47 73 C169.77 45.07, 195.2 17.9, 209.45 0.55 M152.12 72.61 C170.98 50.38, 191.11 25.57, 214.44 0.91 M152.12 72.61 C167.16 54.48, 184.74 36.2, 214.44 0.91 M157.1 72.97 C171.7 55.53, 188.24 37.45, 220.08 0.51 M157.1 72.97 C171.53 57.7, 184.96 42.02, 220.08 0.51 M162.09 73.33 C182.79 52.34, 200.46 29.24, 225.07 0.87 M162.09 73.33 C176.54 57.94, 188.74 43.02, 225.07 0.87 M167.73 72.93 C187.78 52.57, 204.67 31.34, 230.71 0.48 M167.73 72.93 C192.31 45.48, 215.11 18.16, 230.71 0.48 M172.72 73.29 C192.03 53.03, 213.93 26.16, 235.7 0.84 M172.72 73.29 C187.14 54.62, 203.69 37.67, 235.7 0.84 M178.36 72.9 C196.89 49.98, 218.6 27.24, 241.34 0.45 M178.36 72.9 C191.33 57.21, 204.9 42.89, 241.34 0.45 M183.35 73.26 C207.11 47.48, 230.29 17.13, 246.33 0.81 M183.35 73.26 C195.69 56.95, 208.85 41.55, 246.33 0.81 M188.99 72.86 C203.94 55.74, 221.89 34.74, 251.97 0.41 M188.99 72.86 C203.18 55.08, 218.22 38.46, 251.97 0.41 M193.98 73.22 C219.55 44.07, 240.87 19.79, 256.96 0.77 M193.98 73.22 C208.25 55.94, 223.54 39.09, 256.96 0.77 M199.62 72.83 C217.99 48.42, 241.49 28.33, 262.6 0.38 M199.62 72.83 C214.61 56.41, 229.03 40.98, 262.6 0.38 M204.61 73.19 C223.33 53.31, 241.74 30.68, 267.59 0.74 M204.61 73.19 C229.17 46.22, 251.07 19.59, 267.59 0.74 M210.25 72.79 C224.49 54.46, 241.18 34.58, 273.23 0.34 M210.25 72.79 C227.21 52.37, 245.03 32.68, 273.23 0.34 M215.24 73.15 C229.43 57.1, 240.31 42.47, 278.22 0.7 M215.24 73.15 C237.32 48.52, 258.37 22.2, 278.22 0.7 M220.88 72.76 C240.5 49.7, 259.92 29.24, 283.86 0.31 M220.88 72.76 C245.23 46.31, 268.29 19.17, 283.86 0.31 M225.87 73.12 C244.42 48.72, 265.88 27.95, 288.85 0.67 M225.87 73.12 C251.66 44.25, 275.97 16.49, 288.85 0.67 M231.51 72.72 C253.24 45.13, 279.63 17.21, 293.84 1.03 M231.51 72.72 C244.39 57.29, 257.3 41.73, 293.84 1.03 M236.5 73.08 C256.68 52.52, 274.01 31.26, 299.48 0.63 M236.5 73.08 C252.49 52.88, 270.22 34.34, 299.48 0.63 M242.14 72.69 C264.56 48.95, 287.34 23.02, 304.47 0.99 M242.14 72.69 C265.99 45.72, 289.13 17.36, 304.47 0.99 M247.13 73.05 C268.76 49.79, 290 25.19, 310.11 0.6 M247.13 73.05 C271.93 45.31, 297.4 16.35, 310.11 0.6 M252.77 72.66 C272.81 49.72, 292.69 22.65, 315.1 0.96 M252.77 72.66 C274.82 46.14, 296.99 21.06, 315.1 0.96 M257.76 73.02 C279.06 47.65, 296.04 26.13, 320.74 0.56 M257.76 73.02 C279.13 48.78, 298.14 26.64, 320.74 0.56 M262.75 73.38 C286.03 44.74, 309.2 19.59, 325.73 0.92 M262.75 73.38 C284.29 48.03, 306.79 23.96, 325.73 0.92 M268.39 72.98 C284.12 55.13, 298.79 37.14, 331.37 0.53 M268.39 72.98 C286.16 50.17, 306.05 30.27, 331.37 0.53 M273.38 73.34 C296.56 45.22, 323.75 15.16, 336.36 0.89 M273.38 73.34 C289.13 55.59, 302.31 40.33, 336.36 0.89 M279.02 72.95 C303.53 43.87, 326.85 18.13, 342 0.49 M279.02 72.95 C293.37 55.49, 308.15 39.18, 342 0.49 M284.01 73.31 C298.42 57.71, 311.23 40.55, 346.99 0.85 M284.01 73.31 C308.02 43.63, 333.49 16.03, 346.99 0.85 M289.65 72.91 C311.51 49.12, 331.37 27.36, 352.63 0.46 M289.65 72.91 C314.28 43.1, 339.52 15.91, 352.63 0.46 M294.64 73.27 C311.15 55.94, 323.35 38.14, 357.62 0.82 M294.64 73.27 C315.12 50.08, 336.79 25.51, 357.62 0.82 M300.28 72.88 C323.32 47.78, 344.6 22, 363.26 0.43 M300.28 72.88 C320.59 50.02, 339.32 28.35, 363.26 0.43 M305.27 73.24 C321.19 53.97, 335.23 38.9, 368.25 0.79 M305.27 73.24 C320.38 55.99, 334.56 39.62, 368.25 0.79 M310.91 72.84 C326.89 57.23, 339.69 36.32, 372.58 1.9 M310.91 72.84 C323.53 58.18, 338.06 42.96, 372.58 1.9 M315.9 73.2 C339.86 47.6, 361.01 22.6, 376.91 3.02 M315.9 73.2 C338.69 46.17, 361.68 20.58, 376.91 3.02 M321.54 72.81 C338.51 50.61, 357.46 32.27, 379.93 5.64 M321.54 72.81 C336.35 54.99, 349.93 38.93, 379.93 5.64 M326.53 73.17 C341.29 57.14, 355.14 36.64, 382.29 9.02 M326.53 73.17 C347.2 48.97, 367.48 26.48, 382.29 9.02 M332.17 72.77 C348.91 51.78, 366.05 34.35, 384.66 12.4 M332.17 72.77 C345.89 56.87, 360.02 41.97, 384.66 12.4 M337.16 73.13 C350.96 56.83, 362.64 44.64, 384.39 18.8 M337.16 73.13 C350.38 58.41, 363.8 42.97, 384.39 18.8 M342.8 72.74 C358.9 55.85, 372.92 38.47, 384.13 25.19 M342.8 72.74 C359.81 54.2, 374.02 35.6, 384.13 25.19 M347.79 73.1 C354.66 65.33, 364.11 55.68, 384.53 30.84 M347.79 73.1 C358.88 60.26, 371.55 46.08, 384.53 30.84 M352.77 73.46 C358.94 66.21, 369.89 55.52, 384.27 37.23 M352.77 73.46 C360.31 65.04, 369.69 54.26, 384.27 37.23 M358.42 73.07 C364.98 65.59, 369.02 58.96, 384 43.63 M358.42 73.07 C368.68 61.66, 377.08 51.51, 384 43.63 M363.4 73.43 C367.59 70.26, 373.45 61.3, 384.4 49.27 M363.4 73.43 C367.97 69.41, 372.38 63.07, 384.4 49.27 M367.74 74.54 C374.28 66.78, 379.58 61.94, 384.14 55.67 M367.74 74.54 C374.31 67.19, 380 58.66, 384.14 55.67 M374.69 72.64 C376.19 69.99, 382.01 66.54, 385.19 60.56 M374.69 72.64 C377.37 68.95, 380.91 65.58, 385.19 60.56" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M18.25 0 M18.25 0 C129.54 2.72, 242.28 1.14, 365.75 0 M18.25 0 C127.66 -1.41, 238.58 -1.21, 365.75 0 M365.75 0 C378.27 0.9, 382.95 5.36, 384 18.25 M365.75 0 C380.07 0.47, 383.93 4.97, 384 18.25 M384 18.25 C384.02 33.84, 384.87 48.45, 384 54.75 M384 18.25 C384.01 28.7, 384.22 39.53, 384 54.75 M384 54.75 C382.32 67.61, 378.7 73.98, 365.75 73 M384 54.75 C385.93 68.19, 378.42 74.36, 365.75 73 M365.75 73 C288.24 70.59, 213.1 72.01, 18.25 73 M365.75 73 C265.08 74.84, 163.23 75.38, 18.25 73 M18.25 73 C7.8 72.9, 1.58 68.54, 0 54.75 M18.25 73 C7.06 72.31, -0.46 65.42, 0 54.75 M0 54.75 C0.31 45.73, -1.55 34.03, 0 18.25 M0 54.75 C0.72 40.09, -0.05 27.49, 0 18.25 M0 18.25 C1.55 7.28, 6.06 -1.74, 18.25 0 M0 18.25 C1.91 4.6, 5.29 1.6, 18.25 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(297.58984375 586.578125) rotate(0 70.3125 28.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories: []</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files: []</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g stroke-linecap="round" transform="translate(297.01171875 700.19140625) rotate(0 192 63)"><path d="M4.68 12.02 C4.68 12.02, 4.68 12.02, 4.68 12.02 M4.68 12.02 C4.68 12.02, 4.68 12.02, 4.68 12.02 M1.14 22.19 C5.68 16.12, 10.34 11.35, 18.19 2.57 M1.14 22.19 C5.63 15.12, 11.63 10.54, 18.19 2.57 M2.84 26.32 C9.4 19.34, 14.38 14.47, 24.49 1.42 M2.84 26.32 C7.02 21.88, 12.43 16.24, 24.49 1.42 M3.24 31.97 C12.37 22.32, 23.31 9.97, 30.14 1.02 M3.24 31.97 C8.75 26.69, 14.07 19.43, 30.14 1.02 M2.98 38.36 C11.61 29.09, 22.34 16.73, 34.47 2.14 M2.98 38.36 C14.61 26.7, 24.62 13.57, 34.47 2.14 M2.72 44.76 C8.44 35.48, 20.15 28.58, 39.46 2.5 M2.72 44.76 C9.81 35.38, 16.68 27.81, 39.46 2.5 M3.11 50.4 C17.54 33.89, 29.94 20.31, 45.1 2.1 M3.11 50.4 C20.74 31.63, 36.06 13.37, 45.1 2.1 M2.85 56.8 C18.58 35.97, 39.38 14.61, 50.09 2.46 M2.85 56.8 C15.02 42.55, 25.62 29.03, 50.09 2.46 M3.24 62.45 C13.28 49.17, 26.13 38.7, 55.73 2.07 M3.24 62.45 C14.41 50.21, 23.72 38.66, 55.73 2.07 M2.98 68.84 C13.36 56.63, 24.49 40.18, 60.72 2.43 M2.98 68.84 C25.38 43.36, 46.78 18.82, 60.72 2.43 M2.72 75.24 C27.68 45.78, 49.87 21.46, 66.36 2.03 M2.72 75.24 C27.36 48.58, 50.99 21.41, 66.36 2.03 M3.12 80.88 C26.98 53.98, 51.79 26.74, 71.35 2.39 M3.12 80.88 C24.82 57.11, 46.21 30.7, 71.35 2.39 M2.85 87.28 C25.83 58.82, 49.6 32.81, 76.33 2.75 M2.85 87.28 C18.86 69.84, 32.85 53.27, 76.33 2.75 M3.25 92.92 C31.02 64.11, 55.21 32.05, 81.98 2.36 M3.25 92.92 C29.09 64.51, 54.01 35.08, 81.98 2.36 M0.36 102.34 C18.06 82.88, 34.35 63.19, 86.96 2.72 M0.36 102.34 C22.5 76.81, 45.99 50.02, 86.96 2.72 M2.07 106.47 C35.94 64.04, 71.16 24.59, 92.61 2.33 M2.07 106.47 C29.3 74.98, 57.21 42, 92.61 2.33 M3.78 110.61 C32.64 72.16, 65.08 38.52, 97.59 2.69 M3.78 110.61 C27.71 83.59, 50.59 56.73, 97.59 2.69 M5.48 114.74 C31.81 84.38, 61.24 51.3, 103.24 2.29 M5.48 114.74 C38.37 76.78, 69.65 40.61, 103.24 2.29 M7.85 118.12 C37.24 84.78, 67.86 51.75, 108.22 2.65 M7.85 118.12 C40.51 79.72, 74.23 40.65, 108.22 2.65 M10.86 120.75 C33.15 93.28, 59.27 65.67, 113.87 2.26 M10.86 120.75 C41.44 83.49, 74.67 47.06, 113.87 2.26 M14.54 122.62 C54.9 77.33, 93.45 31.72, 118.85 2.62 M14.54 122.62 C36.96 96.13, 58.59 69.9, 118.85 2.62 M18.21 124.48 C60.03 78.24, 98.36 27.73, 124.5 2.22 M18.21 124.48 C47.99 89.82, 78.58 52.73, 124.5 2.22 M23.2 124.84 C60.93 81.34, 96.9 37.41, 129.48 2.58 M23.2 124.84 C51.15 90.76, 80.7 57.55, 129.48 2.58 M27.53 125.96 C67.64 79.48, 108.95 32.16, 135.13 2.19 M27.53 125.96 C50.35 100, 70.45 76.16, 135.13 2.19 M32.52 126.32 C67.96 82.8, 105.29 42.95, 140.11 2.55 M32.52 126.32 C55.09 102.84, 75.65 76.61, 140.11 2.55 M37.51 126.68 C76.58 83.49, 111.39 39.46, 145.76 2.15 M37.51 126.68 C68.68 90.35, 99.78 54.4, 145.76 2.15 M43.15 126.29 C79.05 86.81, 113.32 46.01, 150.74 2.51 M43.15 126.29 C83.16 78.74, 122.47 32.63, 150.74 2.51 M48.14 126.65 C84.95 86.46, 121.01 43.96, 156.39 2.12 M48.14 126.65 C77.47 92.07, 106.15 57.86, 156.39 2.12 M53.12 127.01 C94.06 83.46, 132.13 35.83, 161.37 2.48 M53.12 127.01 C83.35 90.25, 115.88 54.9, 161.37 2.48 M58.77 126.61 C88.9 91.94, 120.62 55.5, 167.02 2.08 M58.77 126.61 C80.71 101.71, 103.08 74.94, 167.02 2.08 M63.75 126.97 C94.83 94.26, 121.35 59.65, 172 2.44 M63.75 126.97 C99.19 87.16, 134.08 46.33, 172 2.44 M69.4 126.58 C96.06 98.29, 123.08 66.58, 177.65 2.05 M69.4 126.58 C94.05 98.86, 118.43 72.82, 177.65 2.05 M74.38 126.94 C110.22 87.09, 146.32 44.12, 182.63 2.41 M74.38 126.94 C112.7 80.36, 152.52 34.24, 182.63 2.41 M80.03 126.54 C112.63 85.84, 146.64 47.21, 188.28 2.01 M80.03 126.54 C118.52 83.47, 154.62 39.47, 188.28 2.01 M85.01 126.9 C123.66 84.49, 160.64 41.03, 193.26 2.37 M85.01 126.9 C107.24 101.3, 128.84 75.24, 193.26 2.37 M90.66 126.51 C128.32 79.98, 168.35 34.11, 198.25 2.73 M90.66 126.51 C125.23 87.51, 159.47 48.47, 198.25 2.73 M95.64 126.87 C136.32 80.89, 176.13 32.73, 203.89 2.34 M95.64 126.87 C130.67 85.7, 167.03 45.03, 203.89 2.34 M101.29 126.47 C138.72 85.68, 174.7 42.24, 208.88 2.7 M101.29 126.47 C141.27 81.26, 181.48 32.8, 208.88 2.7 M106.27 126.83 C133.13 96.99, 159.87 69.21, 214.52 2.31 M106.27 126.83 C139.22 87.92, 173.99 50.04, 214.52 2.31 M111.92 126.44 C139.17 92.95, 170.03 64.03, 219.51 2.67 M111.92 126.44 C140.35 94.34, 169.24 63.52, 219.51 2.67 M116.9 126.8 C139.41 102.01, 161.49 74.65, 225.15 2.27 M116.9 126.8 C143.96 97.47, 169.6 65.58, 225.15 2.27 M122.55 126.4 C151.81 92.7, 184.46 60.16, 230.14 2.63 M122.55 126.4 C144.55 101.98, 167.71 77.15, 230.14 2.63 M127.53 126.76 C155.24 93.9, 184.87 59.39, 235.78 2.24 M127.53 126.76 C162.55 85.69, 197.14 44.32, 235.78 2.24 M133.18 126.37 C156.24 97.59, 181.25 74.22, 240.77 2.6 M133.18 126.37 C169.91 83.87, 205.04 43.06, 240.77 2.6 M138.16 126.73 C171.62 83.67, 210.01 43.26, 246.41 2.2 M138.16 126.73 C162.75 98.55, 185.88 71.86, 246.41 2.2 M143.81 126.33 C167.53 96.38, 195.44 68.77, 251.4 2.56 M143.81 126.33 C175.38 87.65, 208.52 52.45, 251.4 2.56 M148.79 126.69 C186.15 81.29, 226.32 36.57, 257.04 2.17 M148.79 126.69 C185.38 86.13, 221.92 44.72, 257.04 2.17 M154.44 126.3 C190.78 83.15, 229.34 36.03, 262.03 2.53 M154.44 126.3 C178.51 97.01, 203.83 70.69, 262.03 2.53 M159.42 126.66 C189.83 93.77, 217.8 63.16, 267.67 2.13 M159.42 126.66 C196.22 84.11, 234.53 42.17, 267.67 2.13 M165.07 126.27 C199.58 88.17, 233.46 45.58, 272.66 2.49 M165.07 126.27 C204.81 83.18, 243.36 38.31, 272.66 2.49 M170.05 126.63 C191.4 102.17, 214.65 75.55, 278.3 2.1 M170.05 126.63 C194.79 98.87, 219.21 68.85, 278.3 2.1 M175.04 126.99 C218.62 76.89, 259.45 31.99, 283.29 2.46 M175.04 126.99 C200.78 100.7, 225.33 71.6, 283.29 2.46 M180.68 126.59 C214.02 88.6, 246.67 51.74, 288.93 2.06 M180.68 126.59 C212.29 91.19, 241.62 56.43, 288.93 2.06 M185.67 126.95 C215.71 95.07, 243.49 60.99, 293.92 2.42 M185.67 126.95 C216.01 94.84, 243.45 62.69, 293.92 2.42 M191.31 126.56 C229.63 85.76, 263.97 41.71, 299.56 2.03 M191.31 126.56 C222.59 88.19, 255.96 51.19, 299.56 2.03 M196.3 126.92 C217.19 100.03, 243 74.87, 304.55 2.39 M196.3 126.92 C218.88 99.48, 243.28 72.58, 304.55 2.39 M201.94 126.52 C245.64 78.21, 284.33 30.24, 309.54 2.75 M201.94 126.52 C240.53 84.15, 277.64 41.48, 309.54 2.75 M206.93 126.88 C248.75 80.42, 287.71 35.97, 315.18 2.36 M206.93 126.88 C240.59 87.82, 273.05 49.59, 315.18 2.36 M212.57 126.49 C242.06 93.98, 270.55 61.9, 320.17 2.72 M212.57 126.49 C238.53 99.42, 261.94 72.17, 320.17 2.72 M217.56 126.85 C255.21 80.1, 296.4 36.1, 325.81 2.32 M217.56 126.85 C248.46 91.27, 280.97 56.5, 325.81 2.32 M223.2 126.45 C245.32 96.89, 272.92 68.02, 330.8 2.68 M223.2 126.45 C246.93 99.22, 269.26 73.84, 330.8 2.68 M228.19 126.81 C266.27 81.56, 302.8 39.44, 336.44 2.29 M228.19 126.81 C268.68 80.87, 310.31 31.49, 336.44 2.29 M233.83 126.42 C261.99 92.9, 293.86 58.89, 341.43 2.65 M233.83 126.42 C265.72 90.09, 297.62 53.24, 341.43 2.65 M238.82 126.78 C276.56 81.01, 314.76 40.31, 347.07 2.25 M238.82 126.78 C275.07 83.85, 309.78 43.16, 347.07 2.25 M244.47 126.38 C281.08 86.31, 311.83 47.08, 353.37 1.1 M244.47 126.38 C284.42 79.98, 324.2 34.41, 353.37 1.1 M249.45 126.74 C283.83 89.61, 316.66 48.25, 358.36 1.46 M249.45 126.74 C288.06 81.49, 329.09 35.62, 358.36 1.46 M255.1 126.35 C286.95 88.5, 321.78 49.74, 362.69 2.58 M255.1 126.35 C278.54 101.51, 299.57 76.6, 362.69 2.58 M260.08 126.71 C289.9 92.16, 318.76 59.09, 367.68 2.94 M260.08 126.71 C286.6 97.68, 309.24 69.16, 367.68 2.94 M265.73 126.32 C304.67 81.34, 343.89 36.13, 371.35 4.81 M265.73 126.32 C296.05 89.95, 328.32 54.46, 371.35 4.81 M270.71 126.68 C312.11 79.85, 350.1 38.08, 375.03 6.68 M270.71 126.68 C308.29 85.27, 344.32 43.1, 375.03 6.68 M276.36 126.28 C302.37 95.05, 332.55 61.6, 377.39 10.06 M276.36 126.28 C300.17 99.46, 322.39 72.19, 377.39 10.06 M281.34 126.64 C304.76 96.82, 332.3 67.75, 380.41 12.68 M281.34 126.64 C307.49 95.18, 332.91 65.5, 380.41 12.68 M286.33 127 C314.42 93.01, 345.57 57.56, 381.46 17.57 M286.33 127 C313.51 94.85, 341.77 62.68, 381.46 17.57 M291.97 126.61 C313.48 100.45, 339.17 75.97, 383.16 21.7 M291.97 126.61 C311.58 103.09, 333.11 79.56, 383.16 21.7 M296.96 126.97 C320.59 100.34, 342.62 72.36, 384.87 25.84 M296.96 126.97 C321.66 99.71, 344.27 72.56, 384.87 25.84 M302.6 126.57 C334.11 86.35, 368.78 51.97, 385.27 31.48 M302.6 126.57 C330.75 94.29, 357.85 62.13, 385.27 31.48 M307.59 126.93 C326.81 105.47, 343.03 83.78, 385 37.88 M307.59 126.93 C327.54 105.51, 344.6 84.21, 385 37.88 M313.23 126.54 C332.05 106.16, 349.05 86.64, 385.4 43.52 M313.23 126.54 C332.89 103.96, 352.85 83.32, 385.4 43.52 M318.22 126.9 C331.98 110.58, 346.57 93.14, 385.14 49.92 M318.22 126.9 C331.03 111.1, 344.19 94.95, 385.14 49.92 M323.86 126.5 C335.72 108.89, 350.56 93.22, 385.53 55.56 M323.86 126.5 C345.11 101.47, 364.04 79.57, 385.53 55.56 M328.85 126.86 C346.1 108.02, 361.21 90.1, 385.27 61.96 M328.85 126.86 C342.2 112.06, 355.94 96.74, 385.27 61.96 M334.49 126.47 C350.84 109.97, 364.71 94.6, 385.67 67.6 M334.49 126.47 C353.49 104.81, 372.17 82.14, 385.67 67.6 M339.48 126.83 C348.96 114.71, 363.17 101.01, 385.4 74 M339.48 126.83 C354.75 110.09, 368.15 93.09, 385.4 74 M345.12 126.43 C354.84 117.22, 365.07 103.77, 385.8 79.64 M345.12 126.43 C358.46 110.23, 371.31 95.91, 385.8 79.64 M350.11 126.79 C364.3 111.08, 375.22 97.18, 385.54 86.04 M350.11 126.79 C363.14 112.28, 374.94 98.31, 385.54 86.04 M354.44 127.91 C362.81 117.31, 370.87 110.18, 385.28 92.44 M354.44 127.91 C362.9 118.85, 369.83 111.91, 385.28 92.44 M360.74 126.76 C367.63 119.85, 374.61 113.88, 386.98 96.57 M360.74 126.76 C369 116.72, 377.31 108.85, 386.98 96.57 M367.7 124.86 C371.04 118.25, 376.15 112.61, 382.13 108.25 M367.7 124.86 C371.11 120.48, 374.22 117.39, 382.13 108.25" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M31.5 0 M31.5 0 C144.73 2.16, 257.89 1.55, 352.5 0 M31.5 0 C123.21 -0.03, 215.81 -0.04, 352.5 0 M352.5 0 C373.48 -1.54, 384.54 11.54, 384 31.5 M352.5 0 C371.52 1.89, 382.06 11.18, 384 31.5 M384 31.5 C384.2 57.65, 382.83 81.45, 384 94.5 M384 31.5 C384.94 50.55, 383.94 67.7, 384 94.5 M384 94.5 C385.07 116.86, 374.35 127.03, 352.5 126 M384 94.5 C385.17 117.04, 373.57 125.11, 352.5 126 M352.5 126 C247.11 128.47, 140.42 127.09, 31.5 126 M352.5 126 C271.97 126.76, 193.29 127.03, 31.5 126 M31.5 126 C11.34 124.76, -1.26 114.07, 0 94.5 M31.5 126 C9.77 126.57, -1.64 113.31, 0 94.5 M0 94.5 C0.36 74.76, 1.16 53.5, 0 31.5 M0 94.5 C-0.19 81.51, -0.09 68.38, 0 31.5 M0 31.5 C-0.91 8.69, 10.9 0.79, 31.5 0 M0 31.5 C2.16 12.64, 11.39 -0.47, 31.5 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(302.01171875 705.19140625) rotate(0 145.3125 57.60000000000002)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: a</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;directory-a-digest&gt;</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files: []</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g transform="translate(303.75390625 45.62890625) rotate(0 89.0625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_WITH_KEEP</text></g><g transform="translate(308.25390625 240.4765625) rotate(0 98.4375 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_COMPLICATED</text></g><g transform="translate(307.28125 554.6148437500001) rotate(0 51.5625 9.600000000000023)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_A</text></g><g transform="translate(310.6875 674.4585937500001) rotate(0 51.5625 9.600000000000023)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_B</text></g><g transform="translate(18.95703125 42.53671874999998) rotate(0 28.125 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_A</text></g><g transform="translate(22.55078125 130.24374999999998) rotate(0 28.125 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_B</text></g><g transform="translate(13.5546875 210.27109374999998) rotate(0 46.875 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_EMPTY</text></g><g stroke-linecap="round"><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-0.52 1.13 C-60.08 19.9, -297.46 94.83, -357.2 113.61 M1.4 0.68 C-58.25 18.98, -298.08 93.09, -357.74 111.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-331.96 94.73 C-340.78 97.36, -347.89 102.93, -357.37 112.7 M-333.95 93.43 C-339.75 97.47, -345.42 102.99, -358.69 111" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-325.89 114.33 C-336.76 110.86, -345.74 110.35, -357.37 112.7 M-327.89 113.04 C-335.08 112.25, -342.26 112.89, -358.69 111" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M-0.42 1.04 C43.76 -38.95, 220.7 -199.19, 264.79 -239.05 M1.55 0.54 C45.63 -39.42, 220.36 -198.5, 263.94 -238.07" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M251.72 -211.19 C251.81 -219.53, 257.18 -225.36, 265.07 -236.65 M250.73 -212.1 C253.06 -218.02, 257.3 -223.31, 264.08 -238.4" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M237.91 -226.37 C241.88 -230.52, 251.02 -232.22, 265.07 -236.65 M236.92 -227.29 C242.47 -229.5, 250.03 -231.14, 264.08 -238.4" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M0.21 0.21 C-60.17 -24.98, -301.37 -126.03, -361.5 -151.72 M-1.14 -0.73 C-61.8 -26.18, -302.73 -127.84, -362.74 -153.25" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M-332.43 -152.64 C-341.83 -151.66, -356.48 -151.02, -362.88 -151.77 M-332.23 -151.12 C-344.25 -151.99, -356 -152.45, -362.93 -152.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M-340.42 -133.74 C-346.81 -139.68, -358.54 -145.95, -362.88 -151.77 M-340.22 -132.22 C-349.22 -140.26, -357.91 -147.96, -362.93 -152.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M-0.84 0.01 C13.92 -16, 74.3 -80.87, 89.45 -97.18 M0.92 -1.04 C15.95 -16.73, 76.9 -79.62, 91.58 -95.58" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M79.65 -69.91 C84.59 -79.16, 87.99 -87.88, 90.6 -97.23 M78.91 -67.92 C83.62 -76.08, 87.25 -85.83, 90.62 -96.45" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M64.75 -84.03 C75.18 -87.85, 84.2 -91.25, 90.6 -97.23 M64.01 -82.04 C73.25 -85.89, 81.47 -91.29, 90.62 -96.45" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask></svg>
\ No newline at end of file
diff --git a/users/zseri/.gitignore b/users/fogti/.gitignore
index b8553ace55..b8553ace55 100644
--- a/users/zseri/.gitignore
+++ b/users/fogti/.gitignore
diff --git a/users/fogti/OWNERS b/users/fogti/OWNERS
new file mode 100644
index 0000000000..fb396265ca
--- /dev/null
+++ b/users/fogti/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+fogti
diff --git a/users/zseri/dbwospof.md b/users/fogti/dbwospof.md
index f1d68cde06..f1d68cde06 100644
--- a/users/zseri/dbwospof.md
+++ b/users/fogti/dbwospof.md
diff --git a/users/zseri/store-ref-scanner/.gitignore b/users/fogti/store-ref-scanner/.gitignore
index 5a44eef09a..5a44eef09a 100644
--- a/users/zseri/store-ref-scanner/.gitignore
+++ b/users/fogti/store-ref-scanner/.gitignore
diff --git a/users/fogti/store-ref-scanner/Cargo.toml b/users/fogti/store-ref-scanner/Cargo.toml
new file mode 100644
index 0000000000..0ed0c20a39
--- /dev/null
+++ b/users/fogti/store-ref-scanner/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "store-ref-scanner"
+version = "0.1.0"
+description = "scanner/extractor of Nix-like store paths from byte arrays/streams"
+license = "MIT OR Apache-2.0"
+categories = ["no-std", "parsing"]
+edition = "2021"
+homepage = "https://cs.tvl.fyi/depot/-/tree/users/fogti/store-ref-scanner"
+include = ["/src"]
+
+[dependencies]
diff --git a/users/zseri/store-ref-scanner/default.nix b/users/fogti/store-ref-scanner/default.nix
index 38f3fd64ec..38f3fd64ec 100644
--- a/users/zseri/store-ref-scanner/default.nix
+++ b/users/fogti/store-ref-scanner/default.nix
diff --git a/users/zseri/store-ref-scanner/fuzz/.gitignore b/users/fogti/store-ref-scanner/fuzz/.gitignore
index b400c27826..b400c27826 100644
--- a/users/zseri/store-ref-scanner/fuzz/.gitignore
+++ b/users/fogti/store-ref-scanner/fuzz/.gitignore
diff --git a/users/zseri/store-ref-scanner/fuzz/Cargo.lock b/users/fogti/store-ref-scanner/fuzz/Cargo.lock
index 7395dec05e..7395dec05e 100644
--- a/users/zseri/store-ref-scanner/fuzz/Cargo.lock
+++ b/users/fogti/store-ref-scanner/fuzz/Cargo.lock
diff --git a/users/zseri/store-ref-scanner/fuzz/Cargo.toml b/users/fogti/store-ref-scanner/fuzz/Cargo.toml
index 1832be0032..1832be0032 100644
--- a/users/zseri/store-ref-scanner/fuzz/Cargo.toml
+++ b/users/fogti/store-ref-scanner/fuzz/Cargo.toml
diff --git a/users/zseri/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs b/users/fogti/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs
index 9e21a7738a..9e21a7738a 100644
--- a/users/zseri/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs
+++ b/users/fogti/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs
diff --git a/users/zseri/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs b/users/fogti/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs
index 48100a628d..48100a628d 100644
--- a/users/zseri/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs
+++ b/users/fogti/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs
diff --git a/users/zseri/store-ref-scanner/src/hbm.rs b/users/fogti/store-ref-scanner/src/hbm.rs
index 2520efd836..2520efd836 100644
--- a/users/zseri/store-ref-scanner/src/hbm.rs
+++ b/users/fogti/store-ref-scanner/src/hbm.rs
diff --git a/users/zseri/store-ref-scanner/src/lib.rs b/users/fogti/store-ref-scanner/src/lib.rs
index 0f86a769fe..0f86a769fe 100644
--- a/users/zseri/store-ref-scanner/src/lib.rs
+++ b/users/fogti/store-ref-scanner/src/lib.rs
diff --git a/users/zseri/store-ref-scanner/src/spec.rs b/users/fogti/store-ref-scanner/src/spec.rs
index 79da0842c5..79da0842c5 100644
--- a/users/zseri/store-ref-scanner/src/spec.rs
+++ b/users/fogti/store-ref-scanner/src/spec.rs
diff --git a/users/grfn/OWNERS b/users/grfn/OWNERS
deleted file mode 100644
index da7ac5cb9e..0000000000
--- a/users/grfn/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherited: false
-owners:
-  - grfn
diff --git a/users/grfn/achilles/Cargo.lock b/users/grfn/achilles/Cargo.lock
deleted file mode 100644
index 30e5e021f1..0000000000
--- a/users/grfn/achilles/Cargo.lock
+++ /dev/null
@@ -1,868 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-[[package]]
-name = "achilles"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "bimap",
- "clap",
- "crate-root",
- "derive_more",
- "inkwell",
- "itertools",
- "lazy_static",
- "llvm-sys",
- "nom",
- "nom-trace",
- "pratt",
- "pretty_assertions",
- "proptest",
- "test-strategy",
- "thiserror",
- "void",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "0.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "anyhow"
-version = "1.0.38"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
-
-[[package]]
-name = "arrayvec"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
-
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi",
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
-
-[[package]]
-name = "bimap"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f92b72b8f03128773278bf74418b9205f3d2a12c39a61f92395f47af390c32bf"
-
-[[package]]
-name = "bit-set"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
-dependencies = [
- "bit-vec",
-]
-
-[[package]]
-name = "bit-vec"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
-
-[[package]]
-name = "bitflags"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
-
-[[package]]
-name = "bitvec"
-version = "0.19.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
-dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
-]
-
-[[package]]
-name = "byteorder"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
-
-[[package]]
-name = "cc"
-version = "1.0.67"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "clap"
-version = "3.0.0-beta.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
-dependencies = [
- "atty",
- "bitflags",
- "clap_derive",
- "indexmap",
- "lazy_static",
- "os_str_bytes",
- "strsim",
- "termcolor",
- "textwrap",
- "unicode-width",
- "vec_map",
-]
-
-[[package]]
-name = "clap_derive"
-version = "3.0.0-beta.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
-dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "crate-root"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59c6fe4622b269032d2c5140a592d67a9c409031d286174fcde172fbed86f0d3"
-
-[[package]]
-name = "ctor"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
-dependencies = [
- "quote",
- "syn",
-]
-
-[[package]]
-name = "derive_more"
-version = "0.99.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "diff"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
-
-[[package]]
-name = "either"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "funty"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
-
-[[package]]
-name = "getrandom"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
-
-[[package]]
-name = "heck"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
-dependencies = [
- "unicode-segmentation",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.1.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "indexmap"
-version = "1.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
-dependencies = [
- "autocfg",
- "hashbrown",
-]
-
-[[package]]
-name = "inkwell"
-version = "0.1.0"
-source = "git+https://github.com/TheDan64/inkwell?branch=master#a2db15b0bd1c06d71763585ae10d9ea4e775da0c"
-dependencies = [
- "either",
- "inkwell_internals",
- "libc",
- "llvm-sys",
- "once_cell",
- "parking_lot",
- "regex",
-]
-
-[[package]]
-name = "inkwell_internals"
-version = "0.3.0"
-source = "git+https://github.com/TheDan64/inkwell?branch=master#a2db15b0bd1c06d71763585ae10d9ea4e775da0c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "instant"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "itertools"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
-name = "lexical-core"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
-dependencies = [
- "arrayvec",
- "bitflags",
- "cfg-if",
- "ryu",
- "static_assertions",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.88"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
-
-[[package]]
-name = "llvm-sys"
-version = "110.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21ede189444b8c78907e5d36da5dabcf153170fcff9c1dba48afc4b33c7e19f0"
-dependencies = [
- "cc",
- "lazy_static",
- "libc",
- "regex",
- "semver",
-]
-
-[[package]]
-name = "lock_api"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
-dependencies = [
- "scopeguard",
-]
-
-[[package]]
-name = "memchr"
-version = "2.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
-
-[[package]]
-name = "nom"
-version = "6.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
-dependencies = [
- "bitvec",
- "funty",
- "lexical-core",
- "memchr",
- "version_check",
-]
-
-[[package]]
-name = "nom-trace"
-version = "0.2.1"
-source = "git+https://github.com/glittershark/nom-trace?branch=nom-6#6168d2e15cc51efd12d80260159b76a764dba138"
-dependencies = [
- "nom",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
-
-[[package]]
-name = "os_str_bytes"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
-
-[[package]]
-name = "output_vt100"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "parking_lot"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
-dependencies = [
- "instant",
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
-dependencies = [
- "cfg-if",
- "instant",
- "libc",
- "redox_syscall",
- "smallvec",
- "winapi",
-]
-
-[[package]]
-name = "pest"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
-dependencies = [
- "ucd-trie",
-]
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
-
-[[package]]
-name = "pratt"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e31bbc12f7936a7b195790dd6d9b982b66c54f45ff6766decf25c44cac302dce"
-
-[[package]]
-name = "pretty_assertions"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f297542c27a7df8d45de2b0e620308ab883ad232d06c14b76ac3e144bda50184"
-dependencies = [
- "ansi_term",
- "ctor",
- "diff",
- "output_vt100",
-]
-
-[[package]]
-name = "proc-macro-error"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
-dependencies = [
- "unicode-xid",
-]
-
-[[package]]
-name = "proptest"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
-dependencies = [
- "bit-set",
- "bitflags",
- "byteorder",
- "lazy_static",
- "num-traits",
- "quick-error 2.0.0",
- "rand",
- "rand_chacha",
- "rand_xorshift",
- "regex-syntax",
- "rusty-fork",
- "tempfile",
-]
-
-[[package]]
-name = "quick-error"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-
-[[package]]
-name = "quick-error"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
-
-[[package]]
-name = "quote"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "radium"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
-
-[[package]]
-name = "rand"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
- "rand_hc",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "rand_xorshift"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "regex"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
- "thread_local",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
-
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "rusty-fork"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
-dependencies = [
- "fnv",
- "quick-error 1.2.3",
- "tempfile",
- "wait-timeout",
-]
-
-[[package]]
-name = "ryu"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
-
-[[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
-name = "semver"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
-dependencies = [
- "semver-parser",
-]
-
-[[package]]
-name = "semver-parser"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
-dependencies = [
- "pest",
-]
-
-[[package]]
-name = "smallvec"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
-
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
-name = "strsim"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
-
-[[package]]
-name = "syn"
-version = "1.0.61"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed22b90a0e734a23a7610f4283ac9e5acfb96cbb30dfefa540d66f866f1c09c5"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-xid",
-]
-
-[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
-[[package]]
-name = "tempfile"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
-dependencies = [
- "cfg-if",
- "libc",
- "rand",
- "redox_syscall",
- "remove_dir_all",
- "winapi",
-]
-
-[[package]]
-name = "termcolor"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "test-strategy"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2328963c69243416e811c88066d18f670792b2e36e17fa57f4b1a124f85d18a8"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "thiserror"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "thread_local"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
-dependencies = [
- "once_cell",
-]
-
-[[package]]
-name = "ucd-trie"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
-
-[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
-
-[[package]]
-name = "version_check"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
-
-[[package]]
-name = "void"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-
-[[package]]
-name = "wait-timeout"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-util"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "wyz"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
diff --git a/users/grfn/achilles/default.nix b/users/grfn/achilles/default.nix
deleted file mode 100644
index 5245049d4a..0000000000
--- a/users/grfn/achilles/default.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-{ depot, pkgs, ... }:
-
-let
-  llvmPackages = pkgs.llvmPackages_11;
-in
-
-depot.third_party.naersk.buildPackage {
-  src = ./.;
-
-  buildInputs = [
-    llvmPackages.clang
-    llvmPackages.llvm
-    llvmPackages.bintools
-    llvmPackages.libclang.lib
-  ] ++ (with pkgs; [
-    zlib
-    ncurses
-    libxml2
-    libffi
-    pkgconfig
-  ]);
-
-  doCheck = true;
-}
diff --git a/users/grfn/achilles/shell.nix b/users/grfn/achilles/shell.nix
deleted file mode 100644
index f32dce3ba3..0000000000
--- a/users/grfn/achilles/shell.nix
+++ /dev/null
@@ -1,18 +0,0 @@
-with (import ../../.. {}).third_party.nixpkgs;
-
-mkShell {
-  buildInputs = [
-    clang_11
-    llvm_11.lib
-    llvmPackages_11.bintools
-    llvmPackages_11.clang
-    llvmPackages_11.libclang.lib
-    zlib
-    ncurses
-    libxml2
-    libffi
-    pkg-config
-  ];
-
-  LLVM_SYS_110_PREFIX = llvmPackages_11.bintools;
-}
diff --git a/users/grfn/achilles/src/commands/eval.rs b/users/grfn/achilles/src/commands/eval.rs
deleted file mode 100644
index 61a712c08a..0000000000
--- a/users/grfn/achilles/src/commands/eval.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use clap::Clap;
-
-use crate::codegen;
-use crate::interpreter;
-use crate::parser;
-use crate::tc;
-use crate::Result;
-
-/// Evaluate an expression and print its result
-#[derive(Clap)]
-pub struct Eval {
-    /// JIT-compile with LLVM instead of interpreting
-    #[clap(long)]
-    jit: bool,
-
-    /// Expression to evaluate
-    expr: String,
-}
-
-impl Eval {
-    pub fn run(self) -> Result<()> {
-        let (_, parsed) = parser::expr(&self.expr)?;
-        let hir = tc::typecheck_expr(parsed)?;
-        let result = if self.jit {
-            codegen::jit_eval::<i64>(&hir)?.into()
-        } else {
-            interpreter::eval(&hir)?
-        };
-        println!("{}", result);
-        Ok(())
-    }
-}
diff --git a/users/grfn/bbbg/arion-pkgs.nix b/users/grfn/bbbg/arion-pkgs.nix
deleted file mode 100644
index 66c016c283..0000000000
--- a/users/grfn/bbbg/arion-pkgs.nix
+++ /dev/null
@@ -1,2 +0,0 @@
-let depot = import ../../.. {};
-in depot.third_party.nixpkgs
diff --git a/users/grfn/bbbg/default.nix b/users/grfn/bbbg/default.nix
deleted file mode 100644
index 5b5b4badbf..0000000000
--- a/users/grfn/bbbg/default.nix
+++ /dev/null
@@ -1,81 +0,0 @@
-args@{ depot, pkgs, ... }:
-
-with pkgs.lib;
-
-let
-  inherit (depot.third_party) gitignoreSource;
-
-  deps = import ./deps.nix {
-    inherit (pkgs) fetchMavenArtifact fetchgit lib;
-  };
-in rec {
-  meta.targets = [
-    "db-util"
-    "server"
-    "tf"
-  ];
-
-  depsPaths = deps.makePaths {};
-
-  resources = builtins.filterSource (_: type: type != "symlink") ./resources;
-
-  classpath.dev = concatStringsSep ":" (
-    (map gitignoreSource [./src ./test ./env/dev]) ++ [resources] ++ depsPaths
-  );
-
-  classpath.test = concatStringsSep ":" (
-    (map gitignoreSource [./src ./test ./env/test]) ++ [resources] ++ depsPaths
-  );
-
-  classpath.prod = concatStringsSep ":" (
-    (map gitignoreSource [./src ./env/prod]) ++ [resources] ++ depsPaths
-  );
-
-  testClojure = pkgs.writeShellScript "test-clojure" ''
-    export HOME=$(pwd)
-    ${pkgs.clojure}/bin/clojure -Scp ${depsPaths}
-  '';
-
-  mkJar = name: opts:
-    with pkgs;
-    assert (hasSuffix ".jar" name);
-    stdenv.mkDerivation rec {
-      inherit name;
-      dontUnpack = true;
-      buildPhase = ''
-        export HOME=$(pwd)
-        cp ${./pom.xml} pom.xml
-        cp ${./deps.edn} deps.edn
-        ${clojure}/bin/clojure \
-          -Scp ${classpath.prod} \
-          -A:uberjar \
-          ${name} \
-          -C ${opts}
-      '';
-
-      doCheck = true;
-
-      checkPhase = ''
-        echo "checking for existence of ${name}"
-        [ -f ${name} ]
-      '';
-
-      installPhase = ''
-        cp ${name} $out
-      '';
-    };
-
-  db-util-jar = mkJar "bbbg-db-util.jar" "-m bbbg.db";
-
-  db-util = pkgs.writeShellScriptBin "bbbg-db-util" ''
-    exec ${pkgs.openjdk17_headless}/bin/java -jar ${db-util-jar} "$@"
-  '';
-
-  server-jar = mkJar "bbbg-server.jar" "-m bbbg.core";
-
-  server = pkgs.writeShellScriptBin "bbbg-server" ''
-    exec ${pkgs.openjdk17_headless}/bin/java -jar ${server-jar} "$@"
-  '';
-
-  tf = import ./tf.nix args;
-}
diff --git a/users/grfn/bbbg/deps.nix b/users/grfn/bbbg/deps.nix
deleted file mode 100644
index 0d014cf373..0000000000
--- a/users/grfn/bbbg/deps.nix
+++ /dev/null
@@ -1,1489 +0,0 @@
-# generated by clj2nix-1.1.0-rc
-{ fetchMavenArtifact, fetchgit, lib }:
-
-let repos = [
-        "https://repo1.maven.org/maven2/"
-        "https://repo.clojars.org/" ];
-
-  in rec {
-      makePaths = {extraClasspaths ? []}:
-        if (builtins.typeOf extraClasspaths != "list")
-        then builtins.throw "extraClasspaths must be of type 'list'!"
-        else (lib.concatMap (dep:
-          builtins.map (path:
-            if builtins.isString path then
-              path
-            else if builtins.hasAttr "jar" path then
-              path.jar
-            else if builtins.hasAttr "outPath" path then
-              path.outPath
-            else
-              path
-            )
-          dep.paths)
-        packages) ++ extraClasspaths;
-      makeClasspaths = {extraClasspaths ? []}:
-       if (builtins.typeOf extraClasspaths != "list")
-       then builtins.throw "extraClasspaths must be of type 'list'!"
-       else builtins.concatStringsSep ":" (makePaths {inherit extraClasspaths;});
-      packageSources = builtins.map (dep: dep.src) packages;
-      packages = [
-  rec {
-    name = "cambium.logback.json/cambium";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "cambium.logback.json";
-      groupId = "cambium";
-      sha512 = "8e3f32bc1e11071ddc8700204333ba653585de7985c03d14c351950a7896975092e9deffd658bfec7b0b8b9cc72dc025d8e5179a185bd25da26e500218ec37a5";
-      version = "0.4.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "clojure/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "clojure";
-      groupId = "org.clojure";
-      sha512 = "a242514f623a17601b360886563c4a4fe09335e4e16522ac42bbcacda073ae77651cfed446daae7fe74061bb7dff5adc454769c0edc0ded350136c3c707e75b9";
-      version = "1.11.0-alpha3";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "joda-time/joda-time";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "joda-time";
-      groupId = "joda-time";
-      sha512 = "012fb9aa9b00b456f72a92374855a7f062f8617c026c436eee2cda67dffa2f8622201909c0f4f454bb346ff5a3ed6f60c236fafb19fa66f612d9861f27b38d3a";
-      version = "2.10";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "commons-codec/commons-codec";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "commons-codec";
-      groupId = "commons-codec";
-      sha512 = "da30a716770795fce390e4dd340a8b728f220c6572383ffef55bd5839655d5611fcc06128b2144f6cdcb36f53072a12ec80b04afee787665e7ad0b6e888a6787";
-      version = "1.15";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "HikariCP/com.zaxxer";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "HikariCP";
-      groupId = "com.zaxxer";
-      sha512 = "a41b6d8b1c4656e633459824f10320965976eeead01bd5cb24911040073181730e61feb797aef89d9e01c922e89cb58654f364df0a6b1bf62ab3e6f9cc367d77";
-      version = "5.0.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "ring-devel/ring";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "ring-devel";
-      groupId = "ring";
-      sha512 = "79a1ec9f9d03aa4fa0426353970b13468ee65ce314b51ab7a2682212a196a9b5c985eacdee5dbc6ff2f1b536a4e06d0e85e9dd7cc9a49958735c9c4e6d427fd5";
-      version = "1.9.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "simpleclient/io.prometheus";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "simpleclient";
-      groupId = "io.prometheus";
-      sha512 = "60af1cefff04e7036467eae54f5930d5677e4ab066f8ed38a391b54df17733acfefac45e19ee53cef289347bddce5fc69a2766f4e580d21a22cfd9e2348e2723";
-      version = "0.12.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "commons-lang3/org.apache.commons";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "commons-lang3";
-      groupId = "org.apache.commons";
-      sha512 = "fbdbc0943cb3498b0148e86a39b773f97c8e6013740f72dbc727faeabea402073e2cc8c4d68198e5fc6b08a13b7700236292e99d4785f2c9989f2e5fac11fd81";
-      version = "3.12.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "tools.logging/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "tools.logging";
-      groupId = "org.clojure";
-      sha512 = "b7a9680f1156fc7c1574a4364ca550d47668ba727fc80110fdd00c159bedb45c5be82f09cdfb8e8e988e3381e2cf8881ea70651e38001e3eaa4ece31ad0bf0c5";
-      version = "1.2.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "core.specs.alpha/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "core.specs.alpha";
-      groupId = "org.clojure";
-      sha512 = "f521f95b362a47bb35f7c85528c34537f905fb3dd24f2284201e445635a0df701b35d8419d53c6507cc78d3717c1f83cda35ea4c82abd8943cd2ab3de3fcad70";
-      version = "0.2.62";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "netty-common/io.netty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "netty-common";
-      groupId = "io.netty";
-      sha512 = "7efc2f6774a3dbe8408fe182e19830b5b7a994a0d1b0eb50699df691c2450befa05ac205bbf341ad57bef3a04281ce435031e97e725c5c4edfc705a418828ce8";
-      version = "4.1.63.Final";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jackson-databind/com.fasterxml.jackson.core";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jackson-databind";
-      groupId = "com.fasterxml.jackson.core";
-      sha512 = "9f771e78af669b1e1683d6c5903bbf4790aaa88b6b420c2018437da318c3fa4220cd7fa726f3e42a1b8075def1fdbd3744937c15f3bcedfca3050199247363e8";
-      version = "2.12.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "expound/expound";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "expound";
-      groupId = "expound";
-      sha512 = "ca0a57cfd215cff6be36d1f83461ec2d0559c0eae172c8a8bd6e1676d49933d3c30a71192889bd75d813581707d5eda0ec05de03326396bc0cedebf2d71811e5";
-      version = "0.8.10";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "spec.alpha/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "spec.alpha";
-      groupId = "org.clojure";
-      sha512 = "ddfe4fa84622abd8ac56e2aa565a56e6bdc0bf330f377ff3e269ddc241bb9dbcac332c13502dfd4c09c2c08fe24d8d2e8cf3d04a1bc819ca5657b4e41feaa7c2";
-      version = "0.3.218";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "tools.cli/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "tools.cli";
-      groupId = "org.clojure";
-      sha512 = "1d88aa03eb6a664bf2c0ce22c45e7296d54d716e29b11904115be80ea1661623cf3e81fc222d164047058239010eb678af92ffedc7c3006475cceb59f3b21265";
-      version = "1.0.206";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "compojure/compojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "compojure";
-      groupId = "compojure";
-      sha512 = "1f4ba1354bd95772963a4ef0e129dde59d16f4f9fac0f89f2505a1d5de3b4527e45073219c0478e0b3285da46793e7c145ec5a55a9dae2fca6b77dc8d67b4db6";
-      version = "1.6.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "commons-fileupload/commons-fileupload";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "commons-fileupload";
-      groupId = "commons-fileupload";
-      sha512 = "a8780b7dd7ab68f9e1df38e77a5207c45ff50ec53d8b1476570d069edc8f59e52fb1d0fc534d7e513ac5a01b385ba73c320794c82369a72bd6d817a3b3b21f39";
-      version = "1.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jetty-http/org.eclipse.jetty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jetty-http";
-      groupId = "org.eclipse.jetty";
-      sha512 = "60422ff3ef311f1d9d7340c2accdf611d40e738a39e9128967175ede4990439f4725995988849957742d488f749dd2e0740f74dc5bd9b3364e32fbaa66689308";
-      version = "9.4.42.v20210604";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jetty-util/org.eclipse.jetty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jetty-util";
-      groupId = "org.eclipse.jetty";
-      sha512 = "d69084e2cfe0c3af1dc7ee2745d563549a4068b6e8aed5cd2b9f31167168fb64d418c4134a6dfb811b627ec0051d7ff71e0a02e4e775d18a53543d0871c44730";
-      version = "9.4.42.v20210604";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "janino/org.codehaus.janino";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "janino";
-      groupId = "org.codehaus.janino";
-      sha512 = "6853d7d53d3629df43a3a17ff5c989f59ec14e9030be5f67426deb9d0797fa3996b0609d582c65f22a4f7680c941b39ab6d466c480b2fea4bf92218a9b89651d";
-      version = "3.1.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jcl-over-slf4j/org.slf4j";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jcl-over-slf4j";
-      groupId = "org.slf4j";
-      sha512 = "23662fe407fcdbcba8865a8cd3f8bb09d4eb178a2a6511a32e35b995722b345e73f5dc1dd85d2d0a5c707db05aa57e0b3d0b96b59e55403fc486343d5ca4c0d6";
-      version = "2.0.0-alpha4";
-      
-    };
-    paths = [ src ];
-  }
-
-  (rec {
-    name = "io.github.cognitect-labs/test-runner";
-    src = fetchgit {
-      name = "test-runner";
-      url = "https://github.com/cognitect-labs/test-runner";
-      rev = "cc75980b43011773162b485f46f939dc5fba91e4";
-      sha256 = "1661ddmmqva1yiz9p09i5l32lfpi0a99h56022zgvz03nca2ksbg";
-    };
-    paths = map (path: src + path) [
-      "/src"
-    ];
-  })
-
-  rec {
-    name = "cambium.logback.core/cambium";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "cambium.logback.core";
-      groupId = "cambium";
-      sha512 = "83ee9a583dd8a7b2e82e0981b4e51b005095a27257eb1b07165d9701645609060c466ae67fb9431f524a544d52b71fa00009b8acf05aadbeb549043515f9b382";
-      version = "0.4.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "httpasyncclient/org.apache.httpcomponents";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "httpasyncclient";
-      groupId = "org.apache.httpcomponents";
-      sha512 = "0a80db5dbf772f02d02ba6c7c163e8da9517dd7195714b495acb845c429580c1fc926d3e71c115e75be8c145651dce2fdfa0dc380132f7809c14b3ad95492aee";
-      version = "4.1.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "logback-jackson/ch.qos.logback.contrib";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "logback-jackson";
-      groupId = "ch.qos.logback.contrib";
-      sha512 = "d9a3d4cb6cf4eda6fc18e2d374007d27c6ddba98e989a8d8a01b49859b280450113f685df6e16c5fbe0472bc9e26308bc7e8b7e0aedab9404cf0b492d7511685";
-      version = "0.1.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "simpleclient_tracer_otel/io.prometheus";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "simpleclient_tracer_otel";
-      groupId = "io.prometheus";
-      sha512 = "bce192e6162cb3ada7dd6c2d10456e78bce71c170faa09bad2896272fa1bd4a036288d707f3d47747991d8946c74fe21c565713fb15c7052305eb753c94dd939";
-      version = "0.12.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "netty-codec/io.netty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "netty-codec";
-      groupId = "io.netty";
-      sha512 = "f6d9c4a5b508ca0d5f0e213473088f5d7b2e184e447dc092e69227109e28da9b8e68b2238ca6ab4e9915bacacf59cc0dce6ebcbbb05dad34a03b7976d9670c51";
-      version = "4.1.63.Final";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "ring-oauth2/ring-oauth2";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "ring-oauth2";
-      groupId = "ring-oauth2";
-      sha512 = "3ed765b4bbb5749fcdcdb501b93ab656a413ade5af24c7aa34639718ed1fd0a5f325b05bd135540d56e55cbb456a2cb7852ba0e45bc5233e28229986eef75bb9";
-      version = "0.2.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "tools.macro/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "tools.macro";
-      groupId = "org.clojure";
-      sha512 = "65ce5e29379620ac458274c53cd9926e4b764fcaebb1a2b3bc8aef86bbe10c79e654b028bc4328905d2495a680fa90f5002cf5c47885f6449fad43a04a594b26";
-      version = "0.1.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jackson-dataformat-cbor/com.fasterxml.jackson.dataformat";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jackson-dataformat-cbor";
-      groupId = "com.fasterxml.jackson.dataformat";
-      sha512 = "ea5d049eac1b94666479c5e36de14d8fa4b7f24cb92f0f310d2ec2b4de66ef9023161060e67228ef2d7420a002ef861db12a29cad0864638c21612da49686f4f";
-      version = "2.12.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "depstar/seancorfield";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "depstar";
-      groupId = "seancorfield";
-      sha512 = "0f4458b39b8b1949755bc2fe64b239673a9efa3a0140998464bbbcab216ec847344c1b8920611f7c9ca07261850f3a08144ae221cc2c41813a080189e32f9c10";
-      version = "1.0.94";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "logback-core/ch.qos.logback";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "logback-core";
-      groupId = "ch.qos.logback";
-      sha512 = "fc554548f499e284007eeecf76bf4e1995effb6ac8a6262aa96118f623bf9085a9d5bec3741833dd3cae6a76b2ff78c6d0a1fe68bc01213207c93d8e2da345ca";
-      version = "1.3.0-alpha12";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "honeysql/honeysql";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "honeysql";
-      groupId = "honeysql";
-      sha512 = "74d1d93c968b33686848e3bf8934f3b5f002c2b69b1b55a3a3b172c952e9991324e6e95e3a0ce2fecf1de0d3a036f4dff7286df689f0733f253909464e0269f6";
-      version = "1.0.461";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "netty-buffer/io.netty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "netty-buffer";
-      groupId = "io.netty";
-      sha512 = "181b55d99d8d46bbf5f67f05bdccb0381af23a9fca3e6d935e6cde727b132c67133de1c3d81ed19b04c1a5b232be0de16ec1de7e81b532878bc69564237c15dc";
-      version = "4.1.63.Final";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "slingshot/slingshot";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "slingshot";
-      groupId = "slingshot";
-      sha512 = "ff2b2a27b441d230261c7f3ec8c38aa551865e05ab6438a74bd12bfcbc5f6bdc88199d42aaf5932b47df84f3d2700c8f514b9f4e9b5da28d29da7ff6b09a7fb5";
-      version = "0.12.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "httpcore-nio/org.apache.httpcomponents";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "httpcore-nio";
-      groupId = "org.apache.httpcomponents";
-      sha512 = "002af5f72b68a4ff1b1ff46b788013283d195e1d62ee1d7b102aa930b30f77f7e215a6d18edbea0fccd18fb1fa3a66cc4aef6070d72d6d1886f0044dfe0e16c7";
-      version = "4.4.10";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "ring-jetty-adapter/ring";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "ring-jetty-adapter";
-      groupId = "ring";
-      sha512 = "93075903ad73a8b73cb77ee9f53ed33594f40a5dafe8129089adb4cfa333e37468764203c00244568f02abf0c0eee9f5d9a9f96c420919027cf2746a41ec38e3";
-      version = "1.9.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "simpleclient_tracer_common/io.prometheus";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "simpleclient_tracer_common";
-      groupId = "io.prometheus";
-      sha512 = "6f717af63340efd84c5467ae752be7e66f586f0e8b57adb5b7a8ef99b223203ed829aad6797f6ef1811d6d861b00a621a1288c9271ec2ba77018d6d9eb9e7987";
-      version = "0.12.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "component/com.stuartsierra";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "component";
-      groupId = "com.stuartsierra";
-      sha512 = "108b02f51165ad07c2cf5232fbd954d052880c2456e6fb6db3342bda6851c76b73bf9145f03fb0df2b5782fe39f368b2868780c1e8e2dfa2ab2c68dd97f34ab7";
-      version = "1.0.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "netty-handler/io.netty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "netty-handler";
-      groupId = "io.netty";
-      sha512 = "48874727553dd7084f5c48d90de123704ae334837c3a103f598887bb21405dd62c57603b59300ac2fcdd936f0af99ed0730487fb9fb8917d236b8fe3f78f3c02";
-      version = "4.1.63.Final";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "yuicompressor/com.yahoo.platform.yui";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "yuicompressor";
-      groupId = "com.yahoo.platform.yui";
-      sha512 = "ba2588bd50eaa3005b1919daad9f9c86a33351ceb9b7b5f0a9a498a548cc523e99f9345917a64303f8e23925feea226386d3eac01f640f788d1be4c7cf0315e0";
-      version = "2.4.8";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "commons-io/commons-io";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "commons-io";
-      groupId = "commons-io";
-      sha512 = "6af22dffaaecd1553147e788b5cf50368582318f396e456fe9ff33f5175836713a5d700e51720465c932c2b1987daa83027358005812d6a95d5755432de3a79d";
-      version = "2.10.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "tools.namespace/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "tools.namespace";
-      groupId = "org.clojure";
-      sha512 = "2cdb9c5d9bc4fd01dae182e9ad4b91eeaa2487003a977e7d8d5e66f562a9544b59f558710eccf421ea63cbbfa953ac8944fe9b9a76049fb82a47eb2bdcb3a4d7";
-      version = "1.1.1";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "honeysql/com.github.seancorfield";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "honeysql";
-      groupId = "com.github.seancorfield";
-      sha512 = "a0e5ebbf922aaf170c2d74ec0efc0df7e3bda92d0b8cc5f40ee4c8ddcb8c7e0e46556fac381513e0ac76b10f681c14c2d2569010c2f8eab4ff04f6373c2bf229";
-      version = "2.2.840";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jackson-core/com.fasterxml.jackson.core";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jackson-core";
-      groupId = "com.fasterxml.jackson.core";
-      sha512 = "428e0ebb16dd4c74ab0adf712058fd0dc0cd788f6e6f90c60c627da6577b345fac60a30694e111f1cd4e3e8bf79a1f1b820d30ada114984b26c28e299e326eaa";
-      version = "2.12.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "clj-time/clj-time";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "clj-time";
-      groupId = "clj-time";
-      sha512 = "cfeb46af59fd4112aa5a5d0087a39355f0fc19514b4c02bc6c3d9f81c9bda40491686207836e9a7943aebeb82a3b36f4e8b7407a8908c5ef151122644b278d75";
-      version = "0.15.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "clj-http/clj-http";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "clj-http";
-      groupId = "clj-http";
-      sha512 = "9884557d4f38068cb3234aec80acc0de8f9716645529693ffd9bd6db8221f5d1cf9e2d1b8bf7c7df4215d71372b02d83043ebf8fc27dc422552b32c9bdba1602";
-      version = "3.12.3";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jul-to-slf4j/org.slf4j";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jul-to-slf4j";
-      groupId = "org.slf4j";
-      sha512 = "350cfb889248d724b27dce697f635f12d9db463f107830b9518ce184dc4cc1ab3933eb5bdab08515e69766c3d5be24547dac289d6406c44eca90717230714b91";
-      version = "2.0.0-alpha4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "migratus/migratus";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "migratus";
-      groupId = "migratus";
-      sha512 = "ee5ce8601930d063e0d9d90fc8e165b78fc1587bfd7e0fc9922735bc2f9fc27f8cf8bf10d49d6fd57b899ac4b250145bd653915ed770424416e026ba37d1b604";
-      version = "1.3.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "httpcore/org.apache.httpcomponents";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "httpcore";
-      groupId = "org.apache.httpcomponents";
-      sha512 = "f16a652f4a7b87dbf7cb16f8590d54a3f719c4c7b2f8883ce59db2d73be4701b64f2ca8a2c45aca6a5dbeaddeedff0c280a03722f70c076e239b645faa54eff9";
-      version = "4.4.14";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "httpclient-cache/org.apache.httpcomponents";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "httpclient-cache";
-      groupId = "org.apache.httpcomponents";
-      sha512 = "e150e8dc49c8c9972d8b324b56bb292b15e2f0e686f1292c4edac975615dfb16e5edb8ab325e614732a7d43a03061ca4fe93fe1e1f7487851a4d4d3af50a61f9";
-      version = "4.5.13";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "instaparse/instaparse";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "instaparse";
-      groupId = "instaparse";
-      sha512 = "ec2fcf4a09319a8fa9489b08fd9c9a5fe6e63155dde74d096f947fabc4f68d3d1bf68faf21e175e80eaee785f563a1903d30c550f93fb13a16a240609e3dfa2e";
-      version = "1.4.8";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "honeysql-postgres/nilenso";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "honeysql-postgres";
-      groupId = "nilenso";
-      sha512 = "d4accd3b8819cf715ecdb29496cf5a6a5ad3871fd579e55c7148d4e05774cb896c681b0c6f84df88aa9cd8e6ef9bfd65788ede9a49ba365ad0e32ee350091879";
-      version = "0.4.112";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "clj-tuple/clj-tuple";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "clj-tuple";
-      groupId = "clj-tuple";
-      sha512 = "dd626944d0aba679a21b164ed0c77ea84449359361496cba810f83b9fdeab751e5889963888098ce4bf8afa112dbda0a46ed60348a9c01ad36a2e255deb7ab6d";
-      version = "0.2.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jackson-annotations/com.fasterxml.jackson.core";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jackson-annotations";
-      groupId = "com.fasterxml.jackson.core";
-      sha512 = "6fdad6c5bb71a97331a662fe26265aacab6869f3307a710697d5c2f256fd48935764bfb0b3505a2cbb1605daf0b7350abdf84a1b1cf2bb1e91d9184565243c8e";
-      version = "2.12.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "hiccup/hiccup";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "hiccup";
-      groupId = "hiccup";
-      sha512 = "034f15be46c35029f41869c912f82cb2929fbbb0524ea64bd98dcdb9cf09875b28c75e926fa5fff53942b0f9e543e85a73a2d03c3f2112eecae30fcef8b148f4";
-      version = "1.0.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "riddley/riddley";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "riddley";
-      groupId = "riddley";
-      sha512 = "b478ecba9d1ab9d38c84a42354586fcece763000907b40c97bc43c0f16dc560b0860144efe410193cb3b7cb0149fbc1724fdd737cc3ba53de23618f5b30e6f9f";
-      version = "0.1.12";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "java.classpath/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "java.classpath";
-      groupId = "org.clojure";
-      sha512 = "90cd8edeaea02bd908d8cfb0cf5b1cf901aeb38ea3f4971c4b813d33210438aae6fff8e724a8272d2ea9441d373e7d936fa5870e309c1e9721299f662dbbdb9a";
-      version = "1.0.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "simpleclient_pushgateway/io.prometheus";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "simpleclient_pushgateway";
-      groupId = "io.prometheus";
-      sha512 = "31c8878929f516ba7030cc9ec4ac4cbcb09955a9fdae23c6904bc481e40e70e1b3e05619c49b646119077ef6f57c430cc7944f6bafdbca24c9efa8145474fcf7";
-      version = "0.12.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "ns-tracker/ns-tracker";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "ns-tracker";
-      groupId = "ns-tracker";
-      sha512 = "cfb6c2c9f899b43d1284acdc572b34b977936c4df734b38137dfea045421b74d529509cde23695f1dc5ee06d046c2f6b61a2cd98058da1c7220c21dd0361964f";
-      version = "0.4.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "clout/clout";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "clout";
-      groupId = "clout";
-      sha512 = "99d6e1a8c5726ca4e5d12b280a39e6d1182d734922600f27d588d3d65fbc830c5e03f9e0421ff25c819deee4d1f389fd3906222716ace1eb17ce70ef9c5e8f4b";
-      version = "2.2.1";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "commons-logging/commons-logging";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "commons-logging";
-      groupId = "commons-logging";
-      sha512 = "ed00dbfabd9ae00efa26dd400983601d076fe36408b7d6520084b447e5d1fa527ce65bd6afdcb58506c3a808323d28e88f26cb99c6f5db9ff64f6525ecdfa557";
-      version = "1.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "clojure.java-time/clojure.java-time";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "clojure.java-time";
-      groupId = "clojure.java-time";
-      sha512 = "62d8a286ec3393594e7f84eba22dbb02c1305a80a18b2574058ae963d3f3e829ff960c8b66e89069e6c071a11f869203134c6c4cdec6f8e516c9b314796c8108";
-      version = "0.3.3";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "data.csv/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "data.csv";
-      groupId = "org.clojure";
-      sha512 = "b039775a859ed27eca8f8ae74ccb6afde3ad1fe2b3cbe542240c324d60fe1237e495eb1300ee9eb4ff4ef59f01faf7aec6ef1dd6a025ee4fe556c1d91acfcf1b";
-      version = "1.0.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "simpleclient_tracer_otel_agent/io.prometheus";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "simpleclient_tracer_otel_agent";
-      groupId = "io.prometheus";
-      sha512 = "97694210d9a5b48a7cb9dda2a187432c4813edb3051edfa5832a0a471e0b2d5988dab92b70c292e78f59b169345deb5c1c706361fd726f3dc2480766dedfdcec";
-      version = "0.12.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "next.jdbc/com.github.seancorfield";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "next.jdbc";
-      groupId = "com.github.seancorfield";
-      sha512 = "0b4b01ba126bb8b1e2c14262db9fca75456b274d09535d9a7bb386699bf20dc9ac11590d210769e7429ca59ebfdfbb06916b3ff275cc817d74eac5bbabdab8f2";
-      version = "1.2.761";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "java.jdbc/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "java.jdbc";
-      groupId = "org.clojure";
-      sha512 = "6162b7774dca58b62a94bc5a04ba845e4c7065c9c589cc3bb802becfec0baf0989a338c1bf9a5db7c3128873702840d5f2451628f3aac977245975d65a683b7d";
-      version = "0.7.11";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "netty-transport/io.netty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "netty-transport";
-      groupId = "io.netty";
-      sha512 = "c11d690ffeaf3267b2166f73a43108fb89d588fcef3f6d3053bf4b6f6669483baa618fd97438010692a6fa28334372d5a31b7c0996961d4eabb60cbdc358a536";
-      version = "4.1.63.Final";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "crypto-random/crypto-random";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "crypto-random";
-      groupId = "crypto-random";
-      sha512 = "3520df744f250dbe061d1a5d7a05b7143f3a67a4c3f9ad87b8044ee68a36a702a0bcb3a203e35d380899dd01c28e01988b0a7af914b942ccbe0c35c9bdb22e11";
-      version = "1.2.1";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "netty-transport-native-unix-common/io.netty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "netty-transport-native-unix-common";
-      groupId = "io.netty";
-      sha512 = "b63e5f8a44b7f37f3dba378bd06af64dd1d7be3f0b1a7d47ad139ff06e0212b4c7081275b1b5b12183aeb72eb5f9bf9ef03ed8c78bc302aeb4817dca7bd89f3a";
-      version = "4.1.63.Final";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "ring-codec/ring";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "ring-codec";
-      groupId = "ring";
-      sha512 = "38b9775a794831b8afd8d66991a75aa5910cd50952c9035866bf9cc01353810aedafbc3f35d8f9e56981ebf9e5c37c00b968759ed087d2855348b3f46d8d0487";
-      version = "1.1.3";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "spy/com.impossibl.pgjdbc-ng";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "spy";
-      groupId = "com.impossibl.pgjdbc-ng";
-      sha512 = "173615c39aa6015a732e329217b40e3ea1c304c9c168d2764d6ef23ab8775e2f4432339bc22d049662561f09d3fd890b5415738620d64dcedb762d5da26b4ebb";
-      version = "0.8.9";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "logback-json-core/ch.qos.logback.contrib";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "logback-json-core";
-      groupId = "ch.qos.logback.contrib";
-      sha512 = "2a826036f21997e2979fda83ae3e33cf62f3b2b2df15a7b11d1fd8a52163b09f0f2f8d72f5fdcea0ec1289b3d27727ed5e6b0bcdf4c5d741f4bac07b7b6139e8";
-      version = "0.1.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "httpclient/org.apache.httpcomponents";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "httpclient";
-      groupId = "org.apache.httpcomponents";
-      sha512 = "3567739186e551f84cad3e4b6b270c5b8b19aba297675a96bcdff3663ff7d20d188611d21f675fe5ff1bfd7d8ca31362070910d7b92ab1b699872a120aa6f089";
-      version = "4.5.13";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "crypto-equality/crypto-equality";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "crypto-equality";
-      groupId = "crypto-equality";
-      sha512 = "54cf3bd28f633665962bf6b41f5ccbf2634d0db210a739e10a7b12f635e13c7ef532efe1d5d8c0120bb46478bbd08000b179f4c2dd52123242dab79fea97d6a6";
-      version = "1.0.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "cheshire/cheshire";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "cheshire";
-      groupId = "cheshire";
-      sha512 = "855e9c42a8d1c64f4db5cda45e31e914eb5ed99a715e8d7a5759a9c4ab6c69a82353635ca7b0837880c6cf9b41b11184ae11e09cbf2c07aa13db32c539e5dfd4";
-      version = "5.10.1";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "tigris/tigris";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "tigris";
-      groupId = "tigris";
-      sha512 = "fdff4ef5e7175a973aaef98de4f37dee8e125fc711c495382e280aaf3e11341fe8925d52567ca60f3f1795511ade11bc23461c88959632dfae3cf50374d02bf6";
-      version = "0.1.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "config/yogthos";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "config";
-      groupId = "yogthos";
-      sha512 = "3437992d192465edc74aec5259d5e0c0ad7e631dff860b2ee14cef27f13cee7c60487202cf00fc160a95fb0b85ce1ddf56cbdd0c008b47ac598061bf115f6a23";
-      version = "1.1.9";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jetty-io/org.eclipse.jetty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jetty-io";
-      groupId = "org.eclipse.jetty";
-      sha512 = "a8c5f73089daa0c8b27f836acddf40bcbf07bbb2571a4d73653be8aac3fb339022f546326722f216bad78a68886934d24db9bec54235124592dd29dbeab69051";
-      version = "9.4.42.v20210604";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "logback-json-classic/ch.qos.logback.contrib";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "logback-json-classic";
-      groupId = "ch.qos.logback.contrib";
-      sha512 = "d30bf70217d316914d83d46cc15783f656354084087d59cbc0620a746f10b4a42e56d33b3e50a8b3596a64ec8314730bf5ff9a3f7dc3417bdd0582665be009ec";
-      version = "0.1.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "tools.reader/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "tools.reader";
-      groupId = "org.clojure";
-      sha512 = "3481259c7a1eac719db2921e60173686726a0c2b65879d51a64d516a37f6120db8ffbb74b8bd273404285d7b25143ab5c7ced37e7c0eaf4ab1e44586ccd3c651";
-      version = "1.3.6";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "simpleclient_common/io.prometheus";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "simpleclient_common";
-      groupId = "io.prometheus";
-      sha512 = "dedd003638eb3651c112e2d697ac94eb4e3b3e32c94fa41bb1efe2c889a347cdc7bd13256e05423f3370592d4fd65faf8db57f0387ab75814d7fa77b14cbbadf";
-      version = "0.12.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "commons-compiler/org.codehaus.janino";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "commons-compiler";
-      groupId = "org.codehaus.janino";
-      sha512 = "f0778b891ef14d8ee6776747eab0b25da716cdc530752a81aedec2a77570e2f66402179b9408a6efde8125c808eb060a720d2f4977c1f1d022bdaae7eac8d011";
-      version = "3.1.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "servlet-api/javax.servlet";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "servlet-api";
-      groupId = "javax.servlet";
-      sha512 = "363ba5590436ab82067b7a2e14b481aeb2b12ca4048d7a1519a2e549b2d3c09ddf718ac64dc2be6c2fc24c51fdc9c8160261329403113369588ce27d87771db6";
-      version = "2.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "iapetos/clj-commons";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "iapetos";
-      groupId = "clj-commons";
-      sha512 = "d17f36c0cf0ec78db5e893e5c033f8562b31650bda6f5ee582e68f84a07a3631d04d6f69e4e18b1ca64e732c180fa669dfb69a78849e13f601cd563a7a8aab94";
-      version = "0.1.12";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "javax.servlet-api/javax.servlet";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "javax.servlet-api";
-      groupId = "javax.servlet";
-      sha512 = "32f7e3565c6cdf3d9a562f8fd597fe5059af0cf6b05b772a144a74bbc95927ac275eb38374538ec1c72adcce4c8e1e2c9f774a7b545db56b8085af0065e4a1e5";
-      version = "3.1.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "potemkin/potemkin";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "potemkin";
-      groupId = "potemkin";
-      sha512 = "5abc050bf7ff0b27d8c45aaa5e378201980815b711b2db99735db73304576c17e285026ea48a714bf0b0df7ad7a008de38b7d182cdc0e8989f4be1e6b3afa8aa";
-      version = "0.4.5";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "netty-resolver/io.netty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "netty-resolver";
-      groupId = "io.netty";
-      sha512 = "fabf893de74264caa1799c15d184ed8f20b7bf9b1c41abb29f29adf728a934951f97892a4924634f9efbda17c8cf74ea3ff97bafca616711e3c5f79b8ed9ef3e";
-      version = "4.1.63.Final";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "netty-transport-native-epoll/io.netty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "netty-transport-native-epoll";
-      groupId = "io.netty";
-      sha512 = "6fbc2dd2622699f3fc1f329acbd94baf7f1d8923c5cfcae262e6f2d64b4fd71b606561bce5e2b511dff8e052cdade930091fab683fd98713f6b62a622a2c6254";
-      version = "4.1.63.Final";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "clj-stacktrace/clj-stacktrace";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "clj-stacktrace";
-      groupId = "clj-stacktrace";
-      sha512 = "993f8a544203801fc074eefacee8e553e426422b3492d47b857d87ac73cde72c91e29f629382b9eae8cf9600bc2c4c29d2e7169e509c46302ab973c86e73af0c";
-      version = "0.2.8";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "cambium.codec-cheshire/cambium";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "cambium.codec-cheshire";
-      groupId = "cambium";
-      sha512 = "614491cf752a597f29ae29885db6c1ed191341303d89183bee52e4e2c76eb8eb14693562ad09484f379a074b36d97085e848ec3845e069440e6422506c1636f1";
-      version = "1.0.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "slf4j-api/org.slf4j";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "slf4j-api";
-      groupId = "org.slf4j";
-      sha512 = "ad705ab6fd5cd904ef6861c0adf08af19593cf6a486b18de548fe3d68e57b1baa7e02947584fd4dcc350ddcddcf906c01e8d9ba7943a202690d0d788627696b5";
-      version = "2.0.0-alpha4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "test.check/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "test.check";
-      groupId = "org.clojure";
-      sha512 = "b8d7a330b0b5514cd6a00c4382052fab51c3c9d3bc53133f8506791fa670e7c5ecd65094977ea5ced91f59623b0abd1ab8feeec96d63c5c6e459b265a655c577";
-      version = "1.1.1";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "ring-logger/ring-logger";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "ring-logger";
-      groupId = "ring-logger";
-      sha512 = "b675a61c173289fc610d84920ba40178bf62b3bc680923cb66866d78ee2a508296b27a1ab14b66bfbe0304a64166a7e3c3ddee36564dd4a2f988861bce455a3a";
-      version = "1.0.1";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "ring-servlet/ring";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "ring-servlet";
-      groupId = "ring";
-      sha512 = "3d8e6ec224e13d54810a945c0b6c0d2d863736a48d8c4bfc8fadb96b6b0fa9baa638644d0d92d8a53650b188e6e75d391731b08b26eb0f551e90a7504e7f4267";
-      version = "1.9.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "logback-classic/ch.qos.logback";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "logback-classic";
-      groupId = "ch.qos.logback";
-      sha512 = "f9fe0f126061f4abe3973b631b8d8244ba9e9d77783479a6500d629d772050dee508a001fc14d2131407fbdd0d33dd6b8aeb9b1ea9125b471bb8412e8de659e6";
-      version = "1.3.0-alpha12";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "dependency/com.stuartsierra";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "dependency";
-      groupId = "com.stuartsierra";
-      sha512 = "d32fbc4813bd16f2ed8c82e2915e1fb564e88422159bd3580a85c8cd969d1bbbe315bdc13d29c2f0eaceeeafcf649ee712c8df4532464d560aaeae4ae5953866";
-      version = "1.0.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "camel-snake-kebab/camel-snake-kebab";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "camel-snake-kebab";
-      groupId = "camel-snake-kebab";
-      sha512 = "589d34b500560b7113760a16bfb6f0ccd8f162a1ce8c9bc829495432159ba9c95aebf6bc43aa126237a0525806a205a05f9910122074902b659e7fd151d176b1";
-      version = "0.4.2";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "ring/ring";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "ring";
-      groupId = "ring";
-      sha512 = "93c48fb670736b91fb41d8076e1e9c4f53c67693d15e75290da319e7d7881b829a24180029b3a0fa051473c6c77ac3c97b519254ebf2b2c9538b185e79b69162";
-      version = "1.9.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "netty-transport-native-kqueue/io.netty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "netty-transport-native-kqueue";
-      groupId = "io.netty";
-      sha512 = "87e10c06e394a1698d65381d3be8336f753c55e3e899e297510161d0c72540023f30f9032322957e035ead793204a084b988bc21a2bc312fcf7567a22d02a3c4";
-      version = "4.1.63.Final";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "java.data/org.clojure";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "java.data";
-      groupId = "org.clojure";
-      sha512 = "225e1eafd1a659278212d831f7cd8609359f8c880ef3d69b4ade6301ce3c511307ce31d94cb82d5407314b990bd04714ec26273bb3036b248116a7a75fa75e1f";
-      version = "1.0.95";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jetty-server/org.eclipse.jetty";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jetty-server";
-      groupId = "org.eclipse.jetty";
-      sha512 = "b347f8a6e5b84e0f460037027e238a61edec710ade768c95e7be13dcea498abe43d5e622ee69ac7494138d1a8fcf92e07b7deab569c554831c57baad71c53b9b";
-      version = "9.4.42.v20210604";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "httpmime/org.apache.httpcomponents";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "httpmime";
-      groupId = "org.apache.httpcomponents";
-      sha512 = "e1b0ee84bce78576074dc1b6836a69d8f5518eade38562e6890e3ddaa72b7f54bf735c8e2286142c58cddf45f745da31261e5d73b7d8092eb6ecfb20946eb36c";
-      version = "4.5.13";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "log4j-over-slf4j/org.slf4j";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "log4j-over-slf4j";
-      groupId = "org.slf4j";
-      sha512 = "48fa023c57294b73b9bd2f53e3dd3169e03426e5b3aa9d80e1bb1a9abf927fc26ef9f64d02b9769d5577d83094d0f41f044d35bb3b4f6037d66d6b2f19b484a1";
-      version = "2.0.0-alpha4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "ring-core/ring";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "ring-core";
-      groupId = "ring";
-      sha512 = "38d7214a3fc1b80ab55999036638dd1971272e01bec4cb8e0ee0a4aa83f51b8c41ba8a5850b0660227f067d2f9c6d75c0c0737725ea02762bbf8d192dc72febe";
-      version = "1.9.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "cambium.core/cambium";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "cambium.core";
-      groupId = "cambium";
-      sha512 = "0e1fe626c6d0b31aad84ea2e4466273065925548ee5915f442b7997ebfe795faea36dbeac50a0f8c16bbd20d877511e3f8c4ff4f2b916a4538513aaa5cc20112";
-      version = "1.1.1";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "medley/medley";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "medley";
-      groupId = "medley";
-      sha512 = "749ef43b5ea2cae7dc96db871cdd15c7b3c9cfbd96828c20ab08e67d39a5e938357d15994d8d413bc68678285d6c666f2a7296fbf305706d03b3007254e3c55c";
-      version = "1.3.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "garden/garden";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "garden";
-      groupId = "garden";
-      sha512 = "2cc29f071b68bf451835f76de351ac2efb930b5df9ca7237fdca439d3c4d797d7fa207a147886efe1738ab1c50b76c1e366bf9ffcd6f286b0b211260aedd0b25";
-      version = "1.3.10";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jackson-dataformat-smile/com.fasterxml.jackson.dataformat";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jackson-dataformat-smile";
-      groupId = "com.fasterxml.jackson.dataformat";
-      sha512 = "69676964a2b09516b8ffd0d847b6f9a9b843424185453731b548c25e7e9ce30e808c56d66923f9183e2b5c1ba007421b146a6806e768b8e6b07470d60227f1dd";
-      version = "2.12.4";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "jaxb-api/javax.xml.bind";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "jaxb-api";
-      groupId = "javax.xml.bind";
-      sha512 = "0c5bfc2c9f655bf5e6d596e0c196dcb9344d6dc78bf774207c8f8b6be59f69addf2b3121e81491983eff648dfbd55002b9878132de190825dad3ef3a1265b367";
-      version = "2.3.0";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "pgjdbc-ng/com.impossibl.pgjdbc-ng";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "pgjdbc-ng";
-      groupId = "com.impossibl.pgjdbc-ng";
-      sha512 = "a34ac9146257329f6e9b354f13f564c65dbea6463addae383e3918d3a64c90c67f5f7fda6b5c3866de991a568d6690edb3fb09f2507593390a6e30ec0c79e02c";
-      version = "0.8.9";
-      
-    };
-    paths = [ src ];
-  }
-
-  rec {
-    name = "http-kit/http-kit";
-    src = fetchMavenArtifact {
-      inherit repos;
-      artifactId = "http-kit";
-      groupId = "http-kit";
-      sha512 = "4186a2429984745e18730aa8fd545f1fc1812083819ebf77aecfc04e0d31585358a5e25a308c7f21d81359418bbc72390c281f5ed91ae116cf1af79860ba22c3";
-      version = "2.5.3";
-      
-    };
-    paths = [ src ];
-  }
-
-  ];
-  }
-  
\ No newline at end of file
diff --git a/users/grfn/bbbg/module.nix b/users/grfn/bbbg/module.nix
deleted file mode 100644
index 297d846f22..0000000000
--- a/users/grfn/bbbg/module.nix
+++ /dev/null
@@ -1,136 +0,0 @@
-{ config, lib, pkgs, depot, ... }:
-
-let
-  bbbg = depot.users.grfn.bbbg;
-  cfg = config.services.bbbg;
-in {
-  options = with lib; {
-    services.bbbg = {
-      enable = mkEnableOption "BBBG Server";
-
-      port = mkOption {
-        type = types.int;
-        default = 7222;
-        description = "Port to listen to for the HTTP server";
-      };
-
-      domain = mkOption {
-        type = types.str;
-        default = "bbbg.gws.fyi";
-        description = "Domain to host under";
-      };
-
-      proxy = {
-        enable = mkEnableOption "NGINX reverse proxy";
-      };
-
-      database = {
-        enable = mkEnableOption "BBBG Database Server";
-
-        user = mkOption {
-          type = types.str;
-          default = "bbbg";
-          description = "Database username";
-        };
-
-        host = mkOption {
-          type = types.str;
-          default = "localhost";
-          description = "Database host";
-        };
-
-        name = mkOption {
-          type = types.str;
-          default = "bbbg";
-          description = "Database name";
-        };
-
-        port = mkOption {
-          type = types.int;
-          default = 5432;
-          description = "Database host";
-        };
-      };
-    };
-  };
-
-  config = lib.mkMerge [
-    (lib.mkIf cfg.enable {
-      systemd.services.bbbg-server = {
-        wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" ];
-
-        serviceConfig = {
-          DynamicUser = true;
-          Restart = "always";
-          EnvironmentFile = "/run/agenix/bbbg";
-        };
-
-        environment = {
-          PGHOST = cfg.database.host;
-          PGUSER = cfg.database.user;
-          PGDATABASE = cfg.database.name;
-          PORT = toString cfg.port;
-          BASE_URL = "https://${cfg.domain}";
-        };
-
-        script = "${bbbg.server}/bin/bbbg-server";
-      };
-
-      systemd.services.migrate-bbbg = {
-        description = "Run database migrations for BBBG";
-        wantedBy = [ "bbbg-server.service" ];
-        after = ([ "network.target" ]
-                 ++ (if cfg.database.enable
-                     then ["postgresql.service"]
-                     else []));
-
-        serviceConfig = {
-          Type = "oneshot";
-          EnvironmentFile = "/run/agenix/bbbg";
-        };
-
-        environment = {
-          PGHOST = cfg.database.host;
-          PGUSER = cfg.database.user;
-          PGDATABASE = cfg.database.name;
-        };
-
-        script = "${bbbg.db-util}/bin/bbbg-db-util migrate";
-      };
-    })
-    (lib.mkIf cfg.database.enable {
-      services.postgresql = {
-        enable = true;
-        authentication = lib.mkForce ''
-          local all all trust
-          host all all 127.0.0.1/32 password
-          host all all ::1/128 password
-          hostnossl all all 127.0.0.1/32 password
-          hostnossl all all ::1/128  password
-        '';
-
-        ensureDatabases = [
-          cfg.database.name
-        ];
-
-        ensureUsers = [{
-          name = cfg.database.user;
-          ensurePermissions = {
-            "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES";
-          };
-        }];
-      };
-    })
-    (lib.mkIf cfg.proxy.enable {
-      services.nginx = {
-        enable = true;
-        virtualHosts."${cfg.domain}" = {
-          enableACME = true;
-          forceSSL = true;
-          locations."/".proxyPass = "http://localhost:${toString cfg.port}";
-        };
-      };
-    })
-  ];
-}
diff --git a/users/grfn/bbbg/shell.nix b/users/grfn/bbbg/shell.nix
deleted file mode 100644
index 48bcd73759..0000000000
--- a/users/grfn/bbbg/shell.nix
+++ /dev/null
@@ -1,29 +0,0 @@
-let
- depot = import ../../.. {};
-in
-with depot.third_party.nixpkgs;
-
-mkShell {
-  buildInputs = [
-    arion
-    depot.third_party.clj2nix
-    clojure
-    openjdk11_headless
-    postgresql_12
-    nix-prefetch-git
-    (writeShellScriptBin "terraform" ''
-      set -e
-      module=$(nix-build ~/code/depot -A users.grfn.bbbg.tf.module)
-      rm -f ~/tfstate/bbbg/*.json
-      cp ''${module}/*.json ~/tfstate/bbbg
-      exec ${depot.users.grfn.bbbg.tf.terraform}/bin/terraform \
-        -chdir=/home/grfn/tfstate/bbbg \
-        "$@"
-    '')
-  ];
-
-  PGHOST = "localhost";
-  PGUSER = "bbbg";
-  PGDATABASE = "bbbg";
-  PGPASSWORD = "password";
-}
diff --git a/users/grfn/bbbg/src/bbbg/core.clj b/users/grfn/bbbg/src/bbbg/core.clj
deleted file mode 100644
index 70c7da50d5..0000000000
--- a/users/grfn/bbbg/src/bbbg/core.clj
+++ /dev/null
@@ -1,58 +0,0 @@
-(ns bbbg.core
-  (:gen-class)
-  (:require
-   [bbbg.db :as db]
-   [bbbg.web :as web]
-   [clojure.spec.alpha :as s]
-   [clojure.spec.test.alpha :as stest]
-   [com.stuartsierra.component :as component]
-   [expound.alpha :as exp]))
-
-(s/def ::config
-  (s/merge
-   ::db/config
-   ::web/config))
-
-(defn make-system [config]
-  (component/system-map
-   :db (db/make-database config)
-   :web (web/make-server config)))
-
-(defn env->config []
-  (s/assert
-   ::config
-   (merge
-    (db/env->config)
-    (web/env->config))))
-
-(defn dev-config []
-  (s/assert
-   ::config
-   (merge
-    (db/dev-config)
-    (web/dev-config))))
-
-(defonce system nil)
-
-(defn init-dev []
-  (s/check-asserts true)
-  (set! s/*explain-out* exp/printer)
-  (stest/instrument))
-
-(defn run-dev []
-  (init-dev)
-  (alter-var-root
-   #'system
-   (fn [sys]
-     (when sys
-       (component/start sys))
-     (component/start (make-system (dev-config))))))
-
-(defn -main [& _args]
-  (alter-var-root
-   #'system
-   (constantly (component/start (make-system (env->config))))))
-
-(comment
-  (run-dev)
-  )
diff --git a/users/grfn/emacs.d/cpp.el b/users/grfn/emacs.d/cpp.el
deleted file mode 100644
index 5b5dc8ead6..0000000000
--- a/users/grfn/emacs.d/cpp.el
+++ /dev/null
@@ -1,39 +0,0 @@
-;;; -*- lexical-binding: t; -*-
-
-
-(load! "google-c-style")
-
-(after! flycheck
-  (add-to-list 'flycheck-disabled-checkers 'c/c++-gcc)
-  (add-to-list 'flycheck-disabled-checkers 'c/c++-clang))
-
-(defun +grfn/cpp-setup ()
-  (when (s-starts-with?
-         "/home/grfn/code/depot/third_party/nix"
-         (buffer-file-name))
-    (setq lsp-clients-clangd-executable "/home/grfn/code/depot/users/grfn/emacs.d/nix-clangd.sh"
-          lsp-clients-clangd-args nil)
-    (google-set-c-style)
-    (lsp)
-    (add-to-list 'flycheck-disabled-checkers 'c/c++-gcc)
-    (add-to-list 'flycheck-disabled-checkers 'c/c++-clang)))
-
-(add-hook 'c++-mode-hook #'+grfn/cpp-setup)
-
-(use-package! protobuf-mode)
-
-(use-package! clang-format+
-  :config
-  (add-hook 'c-mode-common-hook #'clang-format+-mode))
-
-(map!
- (:map c++-mode-map
-  :leader
-  (:n "/ i" #'counsel-semantic-or-imenu)))
-
-(comment
- (setq
-  lsp-clients-clangd-executable
-  "/home/grfn/code/depot/third_party/nix/clangd.sh"
-  lsp-clients-clangd-args nil)
- )
diff --git a/users/grfn/emacs.d/init.el b/users/grfn/emacs.d/init.el
deleted file mode 100644
index 2518f2f798..0000000000
--- a/users/grfn/emacs.d/init.el
+++ /dev/null
@@ -1,172 +0,0 @@
-;;; -*- lexical-binding: t; -*-
-
-(doom! :completion
-       company           ; the ultimate code completion backend
-       (ivy +fuzzy
-            +prescient)               ; a search engine for love and life
-
-       :ui
-       ;;deft              ; notational velocity for Emacs
-       doom              ; what makes DOOM look the way it does
-       ;doom-dashboard    ; a nifty splash screen for Emacs
-       doom-quit         ; DOOM quit-message prompts when you quit Emacs
-       ;fill-column       ; a `fill-column' indicator
-       hl-todo           ; highlight TODO/FIXME/NOTE tags
-       ;;indent-guides     ; highlighted indent columns
-       modeline          ; snazzy, Atom-inspired modeline, plus API
-       nav-flash         ; blink the current line after jumping
-       ;;neotree           ; a project drawer, like NERDTree for vim
-       ophints           ; highlight the region an operation acts on
-       (popup            ; tame sudden yet inevitable temporary windows
-        +all             ; catch all popups that start with an asterix
-        +defaults)       ; default popup rules
-       ;; ligatures         ; replace bits of code with pretty symbols
-       ;; tabbar            ; FIXME an (incomplete) tab bar for Emacs
-       ;; treemacs          ; a project drawer, like neotree but cooler
-       unicode           ; extended unicode support for various languages
-       vc-gutter         ; vcs diff in the fringe
-       vi-tilde-fringe   ; fringe tildes to mark beyond EOB
-       window-select     ; visually switch windows
-       workspaces        ; tab emulation, persistence & separate workspaces
-
-       :editor
-       (evil +everywhere); come to the dark side, we have cookies
-       file-templates    ; auto-snippets for empty files
-       fold              ; (nigh) universal code folding
-       ;;(format +onsave)  ; automated prettiness
-       ;;lispy             ; vim for lisp, for people who dont like vim
-       multiple-cursors  ; editing in many places at once
-       ;;parinfer          ; turn lisp into python, sort of
-       rotate-text       ; cycle region at point between text candidates
-       snippets          ; my elves. They type so I don't have to
-       word-wrap
-
-       :emacs
-       (dired            ; making dired pretty [functional]
-       ;;+ranger         ; bringing the goodness of ranger to dired
-       ;;+icons          ; colorful icons for dired-mode
-        )
-       electric          ; smarter, keyword-based electric-indent
-       ;;eshell            ; a consistent, cross-platform shell (WIP)
-       ;;term              ; terminals in Emacs
-       vc                ; version-control and Emacs, sitting in a tree
-       (undo +tree)
-
-       :tools
-       ;;ansible
-       ;;debugger          ; FIXME stepping through code, to help you add bugs
-       ;;direnv
-       docker
-       ;;editorconfig      ; let someone else argue about tabs vs spaces
-       ;; ein               ; tame Jupyter notebooks with emacs
-       (eval +overlay)              ; run code, run (also, repls)
-       gist              ; interacting with github gists
-       (lookup           ; helps you navigate your code and documentation
-        +docsets)        ; ...or in Dash docsets locally
-       lsp
-       ;;macos             ; MacOS-specific commands
-       magit             ; a git porcelain for Emacs
-       make              ; run make tasks from Emacs
-       pass              ; password manager for nerds
-       pdf               ; pdf enhancements
-       ;;prodigy           ; FIXME managing external services & code builders
-       ;;rgb               ; creating color strings
-       ;;terraform         ; infrastructure as code
-       ;;tmux              ; an API for interacting with tmux
-       ;;upload            ; map local to remote projects via ssh/ftp
-       ;;wakatime
-       ;;vterm             ; another terminals in Emacs
-
-       :checkers
-       syntax          ; tasing you for every semicolon you forget
-       ; spell           ; tasing you for misspelling mispelling
-
-       :lang
-       agda              ; types of types of types of types...
-       ;;assembly          ; assembly for fun or debugging
-       cc                ; C/C++/Obj-C madness
-       clojure           ; java with a lisp
-       common-lisp       ; if you've seen one lisp, you've seen them all
-       ; coq               ; proofs-as-programs
-       ;;crystal           ; ruby at the speed of c
-       ;;csharp            ; unity, .NET, and mono shenanigans
-       data              ; config/data formats
-       erlang            ; an elegant language for a more civilized age
-       elixir            ; erlang done right
-       ;;elm               ; care for a cup of TEA?
-       emacs-lisp        ; drown in parentheses
-       ;;ess               ; emacs speaks statistics
-       ;;go                ; the hipster dialect
-       ;; (haskell +intero) ; a language that's lazier than I am
-       haskell ; a language that's lazier than I am
-       ;;hy                ; readability of scheme w/ speed of python
-       ;; idris             ;
-       ;;(java +meghanada) ; the poster child for carpal tunnel syndrome
-       javascript        ; all(hope(abandon(ye(who(enter(here))))))
-       julia             ; a better, faster MATLAB
-       ;;kotlin            ; a better, slicker Java(Script)
-       latex             ; writing papers in Emacs has never been so fun
-       ;;ledger            ; an accounting system in Emacs
-       lua               ; one-based indices? one-based indices
-       markdown          ; writing docs for people to ignore
-       ;;nim               ; python + lisp at the speed of c
-       nix               ; I hereby declare "nix geht mehr!"
-       ;;ocaml             ; an objective camel
-       (org              ; organize your plain life in plain text
-        +dragndrop       ; drag & drop files/images into org buffers
-        +attach          ; custom attachment system
-        +babel           ; running code in org
-        +capture         ; org-capture in and outside of Emacs
-        +export          ; Exporting org to whatever you want
-        ;; +habit           ; Keep track of your habits
-        +present         ; Emacs for presentations
-        +pretty
-        +brain
-        +protocol)       ; Support for org-protocol:// links
-       ;;perl              ; write code no one else can comprehend
-       ;;php               ; perl's insecure younger brother
-       ;;plantuml          ; diagrams for confusing people more
-       purescript        ; javascript, but functional
-       (python +lsp)            ; beautiful is better than ugly
-       ;;qt                ; the 'cutest' gui framework ever
-       racket            ; a DSL for DSLs
-       rest              ; Emacs as a REST client
-       ;;ruby              ; 1.step do {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
-       rust              ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
-       ;;scala             ; java, but good
-       (sh +fish)        ; she sells (ba|z|fi)sh shells on the C xor
-       ;;solidity          ; do you need a blockchain? No.
-       ;;swift             ; who asked for emoji variables?
-       ;;terra             ; Earth and Moon in alignment for performance.
-       ;;web               ; the tubes
-       ;;vala              ; GObjective-C
-
-       ;; Applications are complex and opinionated modules that transform Emacs
-       ;; toward a specific purpose. They may have additional dependencies and
-       ;; should be loaded late.
-       :app
-       ;;(email +gmail)    ; emacs as an email client
-       irc               ; how neckbeards socialize
-       ;;(rss +org)        ; emacs as an RSS reader
-       twitter           ; twitter client https://twitter.com/vnought
-       ;;(write            ; emacs as a word processor (latex + org + markdown)
-       ;; +wordnut         ; wordnet (wn) search
-       ;; +langtool)       ; a proofreader (grammar/style check) for Emacs
-
-       :email
-       ;; (mu4e +gmail)
-       notmuch
-
-       :collab
-       ;;floobits          ; peer programming for a price
-       ;;impatient-mode    ; show off code over HTTP
-
-       :config
-       ;; For literate config users. This will tangle+compile a config.org
-       ;; literate config in your `doom-private-dir' whenever it changes.
-       ;;literate
-
-       ;; The default module sets reasonable defaults for Emacs. It also
-       ;; provides a Spacemacs-inspired keybinding scheme and a smartparens
-       ;; config. Use it as a reference for your own modules.
-       (default +bindings +smartparens))
diff --git a/users/grfn/emacs.d/org-config.el b/users/grfn/emacs.d/org-config.el
deleted file mode 100644
index 81fb35a1cb..0000000000
--- a/users/grfn/emacs.d/org-config.el
+++ /dev/null
@@ -1,193 +0,0 @@
-;;; -*- lexical-binding: t; -*-
-
-(defun notes-file (f)
-  (concat org-directory (if (string-prefix-p "/" f) "" "/") f))
-
-(defun grfn/org-project-tag->key (tag)
-  (s-replace-regexp "^project__" "" tag))
-
-(defun grfn/org-project-tag->name (tag)
-  (s-titleized-words
-   (s-join " " (s-split "_" (grfn/org-project-tag->key tag)))))
-
-(defun grfn/org-project-tag->keys (tag)
-  (s-join "" (cons "p"
-                   (-map (lambda (s) (substring-no-properties s 0 1))
-                         (s-split "_" (grfn/org-project-tag->key tag))))))
-
-(defun grfn/org-projects->agenda-commands (project-tags)
-  (loop for tag in project-tags
-        collect `(,(grfn/org-project-tag->keys tag)
-                  ,(grfn/org-project-tag->name tag)
-                  tags-todo
-                  ,tag)))
-
-(defun grfn/org-projects ()
-  (loop for (tag) in
-        (org-global-tags-completion-table
-         (directory-files-recursively "~/notes" "\\.org$"))
-        when (s-starts-with-p "project__" tag)
-        collect tag))
-
-(comment
- (grfn/org-projects->agenda-commands (grfn/org-projects))
- )
-
-(setq
- org-directory (expand-file-name "~/notes")
- +org-dir (expand-file-name "~/notes")
- org-default-notes-file (concat org-directory "/inbox.org")
- +org-default-todo-file (concat org-directory "/inbox.org")
- org-agenda-files (directory-files-recursively
-                   "~/notes" "\\.org$")
- org-refile-targets '((org-agenda-files :maxlevel . 3))
- org-outline-path-complete-in-steps nil
- org-refile-use-outline-path t
- org-file-apps `((auto-mode . emacs)
-                 (,(rx (or (and "." (optional "x") (optional "htm") (optional "l") buffer-end)
-                           (and buffer-start "http" (optional "s") "://")))
-                  . "firefox %s")
-                 (,(rx ".pdf" buffer-end) . "apvlv %s")
-                 (,(rx "." (or "png"
-                               "jpg"
-                               "jpeg"
-                               "gif"
-                               "tif"
-                               "tiff")
-                       buffer-end)
-                  . "feh %s"))
- org-log-done 'time
- org-archive-location "~/notes/trash::* From %s"
- org-cycle-separator-lines 2
- org-hidden-keywords '(title)
- org-tags-column -130
- org-ellipsis "…"
- org-imenu-depth 9
- org-capture-templates
- `(("t" "Todo" entry
-    (file +org-default-todo-file)
-    "* TODO %?\n%i"
-    :kill-buffer t)
-
-   ("m" "Email" entry
-    (file +org-default-todo-file)
-    "* TODO [[%L][%:subject]] :email:\n%i")
-
-   ("n" "Notes" entry
-    (file +org-default-todo-file)
-    "* %U %?\n%i"
-    :prepend t
-    :kill-buffer t)
-
-   ("c" "Task note" entry
-    (clock)
-    "* %U %?\n%i[%l[Context]]\n"
-    :kill-buffer t
-    :unnarrowed t)
-
-   ("p" "Projects")
-   ("px" "Xanthous" entry
-    (file+headline ,(notes-file "xanthous.org") "Backlog")
-    "* TODO %?\nContext %a\nIn task: %K")
-   ("pt" "Tvix" entry
-    (file+headline ,(notes-file "tvix.org") "Tvix TODO")
-    "* TODO %?\nContext %a\nIn task: %K")
-   ("pw" "Windtunnel" entry
-    (file+headline ,(notes-file "windtunnel.org") "Tasks")
-    "* TODO %i%?\nContext: %a\nIn task: %K")
-
-   ("d" "Data recording")
-   )
-
- org-capture-templates-contexts
- `(("px" ((in-file . "/home/grfn/code/depot/users/grfn/xanthous/.*"))))
-
- org-deadline-warning-days 1
- org-agenda-skip-scheduled-if-deadline-is-shown 'todo
- org-todo-keywords '((sequence "TODO(t)" "ACTIVE(a)" "|" "DONE(d)" "RUNNING(r)")
-                     (sequence "NEXT(n)" "WAITING(w)" "LATER(l)" "|" "CANCELLED(c)"))
- org-agenda-custom-commands
- `(("S" "Sprint Tasks" tags-todo "sprint")
-   ("i" "Inbox" tags "inbox")
-   ("r" "Running jobs" todo "RUNNING")
-   ("w" "@Work" tags-todo "@work")
-   ("n" . "Next...")
-   ("np" "Next Sprint" tags-todo "next_sprint|sprint_planning")
-
-   ("p" . "Project...")
-   ,@(grfn/org-projects->agenda-commands (grfn/org-projects)))
-
- org-agenda-dim-blocked-tasks nil
- org-enforce-todo-dependencies nil
-
- org-babel-clojure-backend 'cider)
-
-
-(defun +grfn/insert-work-template ()
-  (interactive)
-  (goto-char (point-min))
-  (forward-line)
-  (insert "#+TODO: TODO(t) NEXT(n) ACTIVE(a) | DONE(d) PR(p) RUNNING(r) TESTING(D)
-#+TODO: BLOCKED(b) BACKLOG(l) PROPOSED(o) | CANCELLED(c)
-#+FILETAGS: @work
-#+FILETAGS: @work
-#+PROPERTY: Effort_ALL 0 4:00 8:00 12:00 20:00 32:00
-#+PROPERTY: ESTIMATE_ALL 0 1 2 3 5 8
-#+PROPERTY: STORY-TYPE_ALL Feature Bug Chore
-#+PROPERTY: NOBLOCKING t
-#+COLUMNS: %TODO %40ITEM(Task) %17EFFORT(Estimated){:} %CLOCKSUM(Time Spent) %17STORY-TYPE(Type) %TAGS"))
-
-(defun +grfn/insert-org-template ()
-  (interactive)
-  (pcase (buffer-file-name)
-    ((s-contains "/work/") (+grfn/insert-work-template))))
-
-;;; TODO: this doesn't work?
-(define-auto-insert "\\.org?$" #'grfn/insert-org-template t)
-
-(defun forge--post-submit-around---link-pr-to-org-item
-    (orig)
-  (let ((cb (funcall orig)))
-    (lambda (value headers status req)
-      (prog1 (funcall cb value headers status req)
-        (grfn/at-org-clocked-in-item
-         (let ((url (alist-get 'html_url value))
-               (number (alist-get 'number value)))
-           (org-set-property
-            "pull-request"
-            (org-make-link-string
-             url
-             (format "%s/%s/%d"
-                     (->> value
-                          (alist-get 'base)
-                          (alist-get 'repo)
-                          (alist-get 'name))
-                     (->> value
-                          (alist-get 'base)
-                          (alist-get 'repo)
-                          (alist-get 'owner)
-                          (alist-get 'login))
-                     number)))))))))
-
-(advice-add
- #'forge--post-submit-callback
- :around #'forge--post-submit-around---link-pr-to-org-item)
-
-(defun +grfn/org-setup ()
-  (setq-local truncate-lines -1)
-  (display-line-numbers-mode -1)
-  (line-number-mode -1))
-
-(add-hook 'org-mode-hook #'+grfn/org-setup)
-
-(set-face-foreground 'org-block +solarized-s-base00)
-(setq whitespace-global-modes '(not org-mode magit-mode vterm-mode))
-(setf (alist-get 'file org-link-frame-setup) 'find-file-other-window)
-(set-face-foreground 'org-block +solarized-s-base00)
-
-;; (add-hook! org-mode
-;;   (set-company-backend! 'org-mode
-;;     '(:separate company-ob-postgresql
-;;                 company-dabbrev
-;;                 company-yasnippet
-;;                 company-ispell)))
diff --git a/users/grfn/emacs.d/packages.el b/users/grfn/emacs.d/packages.el
deleted file mode 100644
index 5a580cad17..0000000000
--- a/users/grfn/emacs.d/packages.el
+++ /dev/null
@@ -1,152 +0,0 @@
-;; -*- no-byte-compile: t; -*-
-;;; private/grfn/packages.el
-
-(package! moody)
-
-;; Editor
-(package! solarized-theme)
-(package! fill-column-indicator)
-(package! flx)
-(package! general
-  :recipe (:host github :repo "noctuid/general.el"))
-(package! fill-column-indicator)
-(package! writeroom-mode)
-(package! dash)
-(package! w3m)
-(package! rainbow-mode)
-(package! string-inflection)
-
-;;; Org
-(package! org-tracker
-  :recipe (:host file
-           :local-repo "~/code/org-tracker"))
-(package! org-alert)
-(package! ob-http)
-(package! ob-ipython)
-(package! ob-async)
-(package! org-recent-headings)
-(package! org-sticky-header)
-(package! gnuplot)
-(package! gnuplot-mode)
-
-;; Presentation
-(package! epresent)
-(package! org-tree-slide)
-(package! ox-reveal)
-
-;; Slack etc
-(package! slack)
-(package! alert)
-
-;; Git
-(package! evil-magit)
-(package! marshal)
-(package! forge)
-(package!
-  github-review
-  :recipe
-  (:host github
-         :repo "charignon/github-review"
-         :files ("github-review.el")))
-
-;; Elisp
-(package! dash)
-(package! dash-functional)
-(package! s)
-(package! request)
-(package! predd
-  :recipe (:host github :repo "skeeto/predd"))
-(package! aio)
-
-;; Haskell
-(package! lsp-haskell)
-(package! counsel-etags)
-
-;;; LSP
-(package! lsp-mode)
-(package! lsp-ui :recipe (:host github :repo "emacs-lsp/lsp-ui"))
-(package! company-lsp)
-(package! lsp-treemacs)
-
-;; Rust
-(package! rustic :disable t)
-;; (package! racer :disable t)
-(package! cargo)
-
-;; Lisp
-(package! paxedit)
-
-;; Javascript
-(package! flow-minor-mode)
-(package! flycheck-flow)
-(package! company-flow)
-(package! prettier-js)
-
-;; GraphQL
-(package! graphql-mode)
-
-;; Haskell
-(package! lsp-mode)
-(package! lsp-ui)
-(package! lsp-haskell)
-(package! company-lsp)
-;; (package! lsp-imenu)
-
-;; Clojure
-(package! flycheck-clojure)
-
-;; SQL
-(package! sqlup-mode)
-(package! emacsql)
-(package! emacsql-psql)
-
-;;; Python
-(package! pyimport)
-;; (package! yapfify)
-(package! blacken)
-
-
-;;; Desktop interaction
-(package! counsel-spotify)
-
-;;; Dhall
-(package! dhall-mode)
-
-;;; Kubernetes
-(package! kubernetes)
-(package! kubernetes-evil)
-(package! k8s-mode)
-
-;;; Stack Exchange
-(package! sx)
-
-;;; Nix
-(package! nix-update
-  :recipe (:host github
-           :repo "glittershark/nix-update-el"))
-(package! direnv)
-
-;;; Sequence diagrams
-(package! wsd-mode
-  :recipe (:host github
-           :repo "josteink/wsd-mode"))
-
-;;; logic?
-(package! metal-mercury-mode
-  :recipe (:host github
-                 :repo "ahungry/metal-mercury-mode"))
-(package! flycheck-mercury)
-
-(package! terraform-mode)
-(package! company-terraform)
-
-(package! jsonnet-mode)
-
-;;;
-(package! znc
-  :recipe (:host github
-                 :repo "sshirokov/ZNC.el"))
-
-;;; cpp
-(package! protobuf-mode)
-(package! clang-format+)
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/hlint b/users/grfn/emacs.d/snippets/haskell-mode/hlint
deleted file mode 100644
index 74b63dc672..0000000000
--- a/users/grfn/emacs.d/snippets/haskell-mode/hlint
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- mode: snippet -*-
-# name: hlint
-# uuid:
-# expand-env: ((yas-indent-line 'fixed))
-# key: hlint
-# condition: t
-# --
-{-# ANN module ("Hlint: ignore $1" :: String) #- }
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/test-group b/users/grfn/emacs.d/snippets/haskell-mode/test-group
deleted file mode 100644
index 948e90d9e0..0000000000
--- a/users/grfn/emacs.d/snippets/haskell-mode/test-group
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- mode: snippet -*-
-# name: test-group
-# uuid:
-# key: testGroup
-# condition: t
-# --
-testGroup "${1:name}"
-[ $0
-]
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/nix-mode/fetchFromGitHub b/users/grfn/emacs.d/snippets/nix-mode/fetchFromGitHub
deleted file mode 100644
index 9b93735730..0000000000
--- a/users/grfn/emacs.d/snippets/nix-mode/fetchFromGitHub
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- mode: snippet -*-
-# name: fetchFromGitHub
-# uuid:
-# key: fetchFromGitHub
-# condition: t
-# --
-fetchFromGitHub {
-                owner = "$1";
-                repo = "$2";
-                rev = "$3";
-                sha256 = "0000000000000000000000000000000000000000000000000000";
-}
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/nix-mode/sha256 b/users/grfn/emacs.d/snippets/nix-mode/sha256
deleted file mode 100644
index e3d52e1c02..0000000000
--- a/users/grfn/emacs.d/snippets/nix-mode/sha256
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- mode: snippet -*-
-# name: sha256
-# uuid:
-# key: sha256
-# condition: t
-# --
-sha256 = "0000000000000000000000000000000000000000000000000000";
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/org-mode/combat b/users/grfn/emacs.d/snippets/org-mode/combat
deleted file mode 100644
index ef46062d09..0000000000
--- a/users/grfn/emacs.d/snippets/org-mode/combat
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- mode: snippet -*-
-# name: combat
-# uuid:
-# key: combat
-# condition: t
-# --
-|             | initiative | max hp | current hp | status |      |
-|-------------+------------+--------+------------+--------+------|
-| Barty Barty |            |        |            |        | <--- |
-| Hectoroth   |            |        |            |        |      |
-| Xanadu      |            |        |            |        |      |
-| Aurora      |            |        |            |        |      |
-| EFB         |            |        |            |        |      |
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/prolog-mode/use-module b/users/grfn/emacs.d/snippets/prolog-mode/use-module
deleted file mode 100644
index 970391f936..0000000000
--- a/users/grfn/emacs.d/snippets/prolog-mode/use-module
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- mode: snippet -*-
-# name: use-module
-# uuid:
-# key: use
-# condition: t
-# --
-:- use_module(${1:library($2)}${3:, [$4]}).
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/python-mode/decorate b/users/grfn/emacs.d/snippets/python-mode/decorate
deleted file mode 100644
index 9448b45c96..0000000000
--- a/users/grfn/emacs.d/snippets/python-mode/decorate
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- mode: snippet -*-
-# name: decorate
-# uuid:
-# key: decorate
-# condition: t
-# --
-def wrap(inner):
-    @wraps(inner)
-    def wrapped(*args, **kwargs):
-        ret = inner(*args, **kwargs)
-        return ret
-
-    return wrapped
-
-return wrap
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/python-mode/dunder b/users/grfn/emacs.d/snippets/python-mode/dunder
deleted file mode 100644
index c49ec40a15..0000000000
--- a/users/grfn/emacs.d/snippets/python-mode/dunder
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- mode: snippet -*-
-# name: dunder
-# uuid:
-# key: du
-# condition: t
-# --
-__$1__$0
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/python-mode/name b/users/grfn/emacs.d/snippets/python-mode/name
deleted file mode 100644
index eca6d60b48..0000000000
--- a/users/grfn/emacs.d/snippets/python-mode/name
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- mode: snippet -*-
-# name: name
-# uuid:
-# key: name
-# condition: t
-# --
-__name__
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/python-mode/pdb b/users/grfn/emacs.d/snippets/python-mode/pdb
deleted file mode 100644
index 6b5c0bbc0a..0000000000
--- a/users/grfn/emacs.d/snippets/python-mode/pdb
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- mode: snippet -*-
-# name: pdb
-# uuid:
-# key: pdb
-# condition: t
-# --
-import pdb; pdb.set_trace()
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/rust-mode/async test b/users/grfn/emacs.d/snippets/rust-mode/async test
deleted file mode 100644
index 2741075474..0000000000
--- a/users/grfn/emacs.d/snippets/rust-mode/async test
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- mode: snippet -*-
-# name: async test
-# uuid:
-# key: atest
-# condition: t
-# --
-#[tokio::test${1:(flavor = "multi_thread")}]
-async fn ${2:test_name}() {
-   `%`$0
-}
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/rust-mode/benchmark b/users/grfn/emacs.d/snippets/rust-mode/benchmark
deleted file mode 100644
index f1446923a0..0000000000
--- a/users/grfn/emacs.d/snippets/rust-mode/benchmark
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- mode: snippet -*-
-# name: benchmark
-# uuid:
-# key: bench
-# condition: t
-# --
-#[bench]
-fn ${1:benchmark_name}(b: &mut Bencher) {
-   `%`b.iter(|| $0);
-}
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/rust-mode/proptest b/users/grfn/emacs.d/snippets/rust-mode/proptest
deleted file mode 100644
index 377b3cfcf6..0000000000
--- a/users/grfn/emacs.d/snippets/rust-mode/proptest
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- mode: snippet -*-
-# name: proptest
-# uuid:
-# key: proptest
-# condition: t
-# --
-#[proptest]
-fn ${1:test_name}($2) {
-   `%`$0
-}
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/terraform-mode/variable b/users/grfn/emacs.d/snippets/terraform-mode/variable
deleted file mode 100644
index e64175200f..0000000000
--- a/users/grfn/emacs.d/snippets/terraform-mode/variable
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- mode: snippet -*-
-# name: variable
-# uuid:
-# key: var
-# condition: t
-# --
-variable "${1:name}" {
-  type = ${2:string}
-  ${3:default = ${4:default}}
-}
-$0
\ No newline at end of file
diff --git a/users/grfn/gws.fyi/Makefile b/users/grfn/gws.fyi/Makefile
deleted file mode 100644
index d6c9f40c95..0000000000
--- a/users/grfn/gws.fyi/Makefile
+++ /dev/null
@@ -1,31 +0,0 @@
-.PHONY: deploy
-
-deploy:
-	@$(shell nix-build `git rev-parse --show-toplevel` -A 'users.grfn."gws.fyi"')
-
-renew:
-	@echo Renewing...
-	@certbot certonly \
-		--manual \
-		--domain www.gws.fyi \
-		--preferred-challenges dns \
-		--server https://acme-v02.api.letsencrypt.org/directory \
-		--agree-tos \
-		--work-dir $(shell pwd)/letsencrypt/work \
-		--logs-dir $(shell pwd)/letsencrypt/logs \
-		--config-dir $(shell pwd)/letsencrypt/config
-	@echo "Reimporting certificate"
-	@aws acm import-certificate \
-	    --profile personal \
-	    --certificate file://letsencrypt/config/live/www.gws.fyi/cert.pem \
-	    --certificate-chain file://letsencrypt/config/live/www.gws.fyi/fullchain.pem \
-	    --private-key file://letsencrypt/config/live/www.gws.fyi/privkey.pem \
-	    --certificate-arn arn:aws:acm:us-east-1:797089351721:certificate/628e54f3-55f9-49c0-811a-eba516b68e30 \
-		--region us-east-1
-
-backup:
-	@tarsnap -cf $(shell uname -n)-letsencrypt-$(shell date +%Y-%m-%d_%H-%M-%S) \
-		letsencrypt/
-
-open:
-	$$BROWSER "https://www.gws.fyi"
diff --git a/users/grfn/gws.fyi/default.nix b/users/grfn/gws.fyi/default.nix
deleted file mode 100644
index 5b7d8fc0e1..0000000000
--- a/users/grfn/gws.fyi/default.nix
+++ /dev/null
@@ -1,35 +0,0 @@
-args@{ pkgs, depot, ... }:
-with pkgs;
-let
-  site = import ./site.nix args;
-  resume = import ../resume args;
-  bucket = "s3://gws.fyi";
-  distributionID = "E2ST43JNBH8C64";
-
-  css = runCommand "main.css" {
-    buildInputs = [ pkgs.minify ];
-  } ''
-    minify --type css < ${./main.css} > $out
-  '';
-
-  keys = runCommand "ssh-keys" {} ''
-    touch $out
-    echo "${depot.users.grfn.keys.main}" >> $out
-  '';
-
-  website =
-    runCommand "gws.fyi" { } ''
-      mkdir -p $out
-      cp ${css} $out/main.css
-      cp ${site.index} $out/index.html
-      cp -r ${site.recipes} $out/recipes
-      cp ${resume} $out/resume.pdf
-      cp ${keys} $out/keys
-    '';
-
-in (writeShellScript "deploy.sh" ''
-  ${awscli2}/bin/aws --profile personal s3 sync ${website}/ ${bucket}
-  echo "Deployed to http://gws.fyi"
-'') // {
-  inherit website site;
-}
diff --git a/users/grfn/gws.fyi/index.org b/users/grfn/gws.fyi/index.org
deleted file mode 100644
index fa47b7c036..0000000000
--- a/users/grfn/gws.fyi/index.org
+++ /dev/null
@@ -1,39 +0,0 @@
-#+OPTIONS: title:nil toc:nil num:nil
-#+HTML_HEAD: <title>griffin smith</title>
-#+HTML_HEAD: <link rel="stylesheet" href="./main.css">
-
-my name is griffin ward smith (aka grfn, glittershark, gws) and i'm a software
-engineer and musician
-
-* code
-
-- [[https://github.com/glittershark/][github]]
-- [[https://cs.tvl.fyi/depot/-/tree/users/grfn][my directory in the tvl monorepo]]
-
-* work
-
-i'm currently working for a relatively new stealth-mode startup. i can't talk much
-about it yet, but if you're interested in rust, distributed systems, and
-databases please reach out
-
-* projects
-
-- [[https://windtunnel.ci/][windtunnel]], a continuous benchmarking software-as-a-service currently accepting early alpha users (send me an email if you want to try it out!)
-- [[https://cs.tvl.fyi/depot/-/tree/users/grfn/achilles][achilles]], a compiler for (what I plan to become) a dependently typed, low-level functional programming language targeting LLVM
-- [[https://github.com/glittershark/org-clubhouse][org-clubhouse]], an emacs package for lightweight integration between [[https://orgmode.org/][org-mode]] and [[https://clubhouse.io/][the clubhouse project management tool]]
-- [[https://cs.tvl.fyi/depot/-/tree/users/grfn/xanthous][xanthous]], a terminal roguelike in haskell that I work on intermittently and exclusively for fun \\
-  you can now try xanthous out over ssh by running ~ssh -p 2222 xanthous.gws.fyi~. if you do so I'd love if you send me an email about it
-
-* music
-
-- https://sacrosanct.bandcamp.com/, a post-rock project with a [[https://bandcamp.com/h34rken][friend of mine]]
-- [[https://soundcloud.com/missingggg][my current soundcloud]], releasing instrumental hip-hop under the name *missing*
-- you can also find a log of all the music I listen to [[https://www.last.fm/user/wildgriffin45][on last.fm]]
-
-* contact
-
-- [[mailto:web@gws.fyi][web@gws.fyi]]
-- [[https://twitter.com/glittershark1][twitter]]
-- https://keybase.io/glittershark
-- grfn on IRC (hackint or libera.chat)
-- [[http://keys.gnupg.net/pks/lookup?op=get&search=0x44EF5B5E861C09A7][gpg key: 0F11A989879E8BBBFDC1E23644EF5B5E861C09A7]]
diff --git a/users/grfn/gws.fyi/orgExportHTML.nix b/users/grfn/gws.fyi/orgExportHTML.nix
deleted file mode 100644
index ac28580a59..0000000000
--- a/users/grfn/gws.fyi/orgExportHTML.nix
+++ /dev/null
@@ -1,70 +0,0 @@
-{ pkgs, depot, ... }:
-
-with pkgs;
-with lib;
-
-let
-
-  emacsWithPackages = (pkgs.emacsPackagesGen pkgs.emacs27).emacsWithPackages;
-
-  emacs = emacsWithPackages (p: with p; [
-    org
-  ]);
-
-in
-
-opts:
-
-let
-  src = if isAttrs opts then opts.src else opts;
-  headline = if isAttrs opts then opts.headline else null;
-
-  bn = builtins.baseNameOf src;
-  filename = elemAt (splitString "." bn) 0;
-
-  outName =
-    if isNull headline
-    then
-      let bn = builtins.baseNameOf src;
-          filename = elemAt (splitString "." bn) 0;
-      in
-        if depot.nix.utils.isDirectory src
-        then filename
-        else filename + ".html"
-    else "${filename}-${replaceStrings [" "] ["-"] filename}.html";
-
-  escapeDoubleQuotes = replaceStrings ["\""] ["\\\""];
-
-  navToHeadline = optionalString (! isNull headline) ''
-    (search-forward "${escapeDoubleQuotes headline}")
-    (org-narrow-to-subtree)
-  '';
-
-in
-
-runCommand outName { inherit src; } ''
-  buildFile() {
-    cp "$1" file.org
-    ${emacs}/bin/emacs --batch \
-      --load ${./config.el} \
-      --visit file.org \
-      --eval "(progn
-        ${escapeDoubleQuotes navToHeadline}
-        (org-html-export-to-html))" \
-      --kill
-    rm file.org
-    substitute file.html "$2" \
-      --replace '<title>&lrm;</title>' ""
-    rm file.html
-  }
-
-  if [ -d $src ]; then
-    for file in $src/*; do
-      result=''${file/$src/$out}
-      mkdir -p $(dirname $result)
-      buildFile $file ''${result/.org/.html}
-    done
-  else
-    buildFile $src $out
-  fi
-''
diff --git a/users/grfn/gws.fyi/shell.nix b/users/grfn/gws.fyi/shell.nix
deleted file mode 100644
index 846bdb6677..0000000000
--- a/users/grfn/gws.fyi/shell.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-with import <nixpkgs> { config.allowUnfree = true; };
-mkShell {
-  buildInputs = [
-    awscli
-    gnumake
-    letsencrypt
-    tarsnap
-  ];
-}
diff --git a/users/grfn/keyboard/default.nix b/users/grfn/keyboard/default.nix
deleted file mode 100644
index dda1d9682c..0000000000
--- a/users/grfn/keyboard/default.nix
+++ /dev/null
@@ -1,63 +0,0 @@
-{ pkgs, ... }:
-
-with pkgs;
-
-let avrlibc = pkgsCross.avr.libcCross; in
-
-rec {
-  qmkSource = fetchgit {
-    url = "https://github.com/qmk/qmk_firmware";
-    rev = "ab1650606c36f85018257aba65d9c3ff8ec42e71";
-    sha256 = "1k59flkvhjzmfl0yz9z37lqhvad7m9r5wy1p1sjk5274rsmylh79";
-    fetchSubmodules = true;
-  };
-
-  layout = stdenv.mkDerivation rec {
-    name = "ergodox_ez_grfn.hex";
-
-    src = qmkSource;
-
-    buildInputs = [
-      dfu-programmer
-      dfu-util
-      diffutils
-      git
-      python3
-      pkgsCross.avr.buildPackages.binutils
-      pkgsCross.avr.buildPackages.gcc
-      avrlibc
-      avrdude
-    ];
-
-    AVR_CFLAGS = [
-      "-isystem ${avrlibc}/avr/include"
-      "-L${avrlibc}/avr/lib/avr5"
-    ];
-
-    AVR_ASFLAGS = AVR_CFLAGS;
-
-    patches = [ ./increase-tapping-delay.patch ];
-
-    postPatch = ''
-      mkdir keyboards/ergodox_ez/keymaps/grfn
-      cp ${./keymap.c} keyboards/ergodox_ez/keymaps/grfn/keymap.c
-    '';
-
-    buildPhase = ''
-      make ergodox_ez:grfn
-    '';
-
-    installPhase = ''
-      cp ergodox_ez_grfn.hex $out
-    '';
-  };
-
-  flash = writeShellScript "flash.sh" ''
-    ${teensy-loader-cli}/bin/teensy-loader-cli \
-      -v \
-      --mcu=atmega32u4 \
-      -w ${layout}
-  '';
-
-  meta.targets = [ "layout" ];
-}
diff --git a/users/grfn/keyboard/flash b/users/grfn/keyboard/flash
deleted file mode 100755
index 76def36f9c..0000000000
--- a/users/grfn/keyboard/flash
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env bash
-exec "$(nix-build --no-out-link ../../.. -A users.grfn.keyboard.flash)"
diff --git a/users/grfn/keyboard/keymap.c b/users/grfn/keyboard/keymap.c
deleted file mode 100644
index 94a399a1ae..0000000000
--- a/users/grfn/keyboard/keymap.c
+++ /dev/null
@@ -1,206 +0,0 @@
-#include QMK_KEYBOARD_H
-#include "debug.h"
-#include "action_layer.h"
-#include "version.h"
-
-
-#include "keymap_german.h"
-
-#include "keymap_nordic.h"
-
-
-
-enum custom_keycodes {
-  PLACEHOLDER = SAFE_RANGE, // can always be here
-  EPRM,
-  VRSN,
-  RGB_SLD,
-
-  EX_PIPE, // |>
-  THIN_ARROW, // ->
-  FAT_ARROW, // =>
-};
-
-
-
-#define LAMBDA UC(0x03BB)
-
-const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
-
-  [0] = LAYOUT_ergodox(
-      KC_EQUAL,       KC_1,           KC_2,   KC_3,   KC_4,   KC_5,   KC_LEFT,
-      KC_TAB,         KC_Q,           KC_W,   KC_E,   KC_R,   KC_T,   KC_LALT,
-      KC_ESCAPE,      KC_A,           KC_S,   KC_D,   KC_F,   KC_G,
-      KC_RSFT,        CTL_T(KC_Z),    KC_X,   KC_C,   KC_V,   KC_B,   KC_TAB,
-      LT(1,KC_GRAVE), KC_QUOTE,       LALT(KC_LSHIFT),KC_LEFT,KC_RIGHT,
-                                        ALT_T(KC_APPLICATION),      LAMBDA,
-                                                                    KC_LBRACKET,
-                                        GUI_T(KC_NO), LSFT_T(KC_BSPACE),    KC_COLN,
-
-      KC_MY_COMPUTER, KC_6,   KC_7,   KC_8,       KC_9,       KC_0,               KC_MINUS,
-      KC_RALT,      KC_Y,   KC_U,   KC_I,       KC_O,       KC_P,               KC_BSLASH,
-                    KC_H,   KC_J,   KC_K,       KC_L,       LT(2,KC_SCOLON),    LT(1,KC_QUOTE),
-      KC_MINUS,     KC_N,   KC_M,   KC_COMMA,   KC_DOT,     CTL_T(KC_SLASH),    KC_RSFT,
-                    KC_DOWN,KC_UP,  KC_LBRACKET,KC_RBRACKET,MO(1),
-
-      KC_PAUSE,  TG(3),
-      KC_RBRACKET,
-      KC_COLN,  RSFT_T(KC_ENTER),   KC_SPACE
-   ),
-
-  [1] = LAYOUT_ergodox(
-      KC_ESCAPE,        KC_F1,          KC_F2,          KC_F3,          KC_F4,      KC_F5,          KC_TRANSPARENT,
-      KC_TRANSPARENT,   KC_EXLM,        KC_AT,          KC_LCBR,        KC_RCBR,    KC_PIPE,        KC_RABK,
-      KC_TRANSPARENT,   KC_HASH,        KC_DLR,         KC_LPRN,        KC_RPRN,    KC_UNDERSCORE,
-      KC_LABK,          KC_PERC,          KC_CIRC,        KC_LBRACKET,    KC_RBRACKET,    KC_TILD,    KC_TRANSPARENT,
-      KC_TRANSPARENT,   KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
-                                                        RGB_MOD,  KC_TRANSPARENT,
-                                                                  KC_TRANSPARENT,
-                                                        RGB_VAD,    RGB_VAI, EX_PIPE,
-
-      KC_TRANSPARENT,   KC_F6,          KC_F7,          KC_F8,          KC_F9,      KC_F10,         KC_F11,
-      KC_PGUP,          KC_UP,          KC_7,           KC_8,           KC_9,       KC_ASTR,        KC_F12,
-                        KC_DOWN,        KC_4,           KC_5,           KC_6,       KC_PLUS,        KC_TRANSPARENT,
-      KC_PGDOWN,        KC_AMPR,        KC_1,           KC_2,           KC_3,       KC_BSLASH,      KC_TRANSPARENT,
-                                        KC_TRANSPARENT, KC_DOT,         KC_0,       KC_EQUAL,       KC_TRANSPARENT,
-      RGB_TOG,          RGB_SLD,
-      THIN_ARROW,
-      EX_PIPE,          RGB_HUD,    RGB_HUI
-  ),
-
-  [2] = LAYOUT_ergodox(
-      KC_SCROLLLOCK,  KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_UP,       KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_LEFT,     KC_MS_DOWN,     KC_MS_RIGHT,    KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_BTN1,     KC_MS_BTN2,
-                                                       KC_TRANSPARENT,                 KC_TRANSPARENT,
-                                                                                       KC_TRANSPARENT,
-                                                       KC_MS_BTN1,     KC_MS_BTN2,     KC_TRANSPARENT,
-
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,    KC_TRANSPARENT,      KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,    KC_TRANSPARENT,      KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
-                      KC_TRANSPARENT, KC_MS_WH_DOWN,     KC_MS_WH_UP,         KC_TRANSPARENT,      KC_TRANSPARENT, KC_MEDIA_PLAY_PAUSE,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,    KC_MEDIA_PREV_TRACK, KC_MEDIA_NEXT_TRACK, KC_TRANSPARENT, KC_TRANSPARENT,
-                                      KC_AUDIO_VOL_DOWN, KC_AUDIO_VOL_UP,     KC_AUDIO_MUTE,       KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_WWW_BACK),
-
-  // FPS layout
-  [3] = LAYOUT_ergodox(
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
-                                                      KC_TRANSPARENT,           KC_TRANSPARENT,
-                                                                                KC_TRANSPARENT,
-                                                      KC_SPACE, KC_TRANSPARENT, KC_TRANSPARENT,
-
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
-                      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
-                                      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,      KC_TRANSPARENT, KC_TRANSPARENT,
-      KC_TRANSPARENT, TG(3),
-      KC_TRANSPARENT,
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT),
-};
-
-const uint16_t PROGMEM fn_actions[] = {
-  [1] = ACTION_LAYER_TAP_TOGGLE(1)
-};
-
-// leaving this in place for compatibilty with old keymaps cloned and re-compiled.
-const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
-{
-      switch(id) {
-        case 0:
-        if (record->event.pressed) {
-          SEND_STRING (QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION);
-        }
-        break;
-      }
-    return MACRO_NONE;
-};
-
-bool process_record_user(uint16_t keycode, keyrecord_t *record) {
-  switch (keycode) {
-    // dynamically generate these.
-    case EPRM:
-      if (record->event.pressed) {
-        eeconfig_init();
-      }
-      return false;
-      break;
-    case VRSN:
-      if (record->event.pressed) {
-        SEND_STRING (QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION);
-      }
-      return false;
-      break;
-    case RGB_SLD:
-      if (record->event.pressed) {
-        rgblight_mode(1);
-      }
-      return false;
-      break;
-    case EX_PIPE:
-      if (record->event.pressed) {
-        SEND_STRING ( "|> " );
-      }
-      return false;
-      break;
-    case THIN_ARROW:
-      if (record->event.pressed) {
-        SEND_STRING ( "-> " );
-      }
-      return false;
-      break;
-
-
-  }
-  return true;
-}
-
-void matrix_scan_user(void) {
-
-    uint8_t layer = biton32(layer_state);
-
-    ergodox_board_led_off();
-    ergodox_right_led_1_off();
-    ergodox_right_led_2_off();
-    ergodox_right_led_3_off();
-    switch (layer) {
-        case 1:
-            ergodox_right_led_1_on();
-            break;
-        case 2:
-            ergodox_right_led_2_on();
-            break;
-        case 3:
-            ergodox_right_led_3_on();
-            break;
-        case 4:
-            ergodox_right_led_1_on();
-            ergodox_right_led_2_on();
-            break;
-        case 5:
-            ergodox_right_led_1_on();
-            ergodox_right_led_3_on();
-            break;
-        case 6:
-            ergodox_right_led_2_on();
-            ergodox_right_led_3_on();
-            break;
-        case 7:
-            ergodox_right_led_1_on();
-            ergodox_right_led_2_on();
-            ergodox_right_led_3_on();
-            break;
-        default:
-            break;
-    }
-
-};
diff --git a/users/grfn/resume/default.nix b/users/grfn/resume/default.nix
deleted file mode 100644
index 2db6a650bc..0000000000
--- a/users/grfn/resume/default.nix
+++ /dev/null
@@ -1,37 +0,0 @@
-{ pkgs, ... }:
-
-with pkgs.lib;
-
-pkgs.runCommandNoCC "resume.pdf" {
-  buildInputs = [(pkgs.texlive.combine {
-    inherit (pkgs.texlive)
-      capt-of
-      collection-fontsrecommended
-      enumitem
-      etoolbox
-      fancyvrb
-      float
-      fncychap
-      framed
-      l3packages
-      microtype
-      needspace
-      parskip
-      scheme-basic
-      tabulary
-      titlesec
-      ulem
-      upquote
-      varwidth
-      wrapfig
-      xcolor
-      ;
-  })];
-} ''
-  cp ${builtins.filterSource (path: type:
-    type == "regular" &&
-    any (ext: hasSuffix ext path) [".sty" ".cls" ".tex" ".png"]
-  ) ./.}/* .
-  pdflatex ./resume.tex
-  cp resume.pdf $out
-''
diff --git a/users/grfn/resume/resume.tex b/users/grfn/resume/resume.tex
deleted file mode 100644
index 933558d570..0000000000
--- a/users/grfn/resume/resume.tex
+++ /dev/null
@@ -1,212 +0,0 @@
-%% start of file `template.tex'.
-%% Copyright 2006-2013 Xavier Danaux (xdanaux@gmail.com).
-%% Copyright 2014-2020 Griffin Smith (wildgriffin45@gmail.com).
-%
-% This work may be distributed and/or modified under the
-% conditions of the LaTeX Project Public License version 1.3c,
-% available at http://www.latex-project.org/lppl/.
-
-
-\documentclass[10pt,a4paper,sans]{moderncv}        % possible options include font size ('10pt', '11pt' and '12pt'), paper size ('a4paper', 'letterpaper', 'a5paper', 'legalpaper', 'executivepaper' and 'landscape') and font family ('sans' and 'roman')
-
-\usepackage[inline]{enumitem}
-
-
-% moderncv themes
-% style options are 'casual' (default), 'classic', 'oldstyle' and 'banking'
-\moderncvstyle{casual}
-% color options 'blue' (default), 'orange', 'green', 'red', 'purple', 'grey' and 'black'
-\moderncvcolor{black}
-% to set the default font; use '\sfdefault' for the default sans serif font,
-% '\rmdefault' for the default roman one, or any tex font name
-%\renewcommand{\familydefault}{\sfdefault}
-\nopagenumbers{}
-
-\usepackage[utf8]{inputenc}
-
-\usepackage[scale=0.8, margin=0.65in]{geometry}
-\setlength{\hintscolumnwidth}{2.6cm}
-
-\name{Griffin}{Smith}
-\title{Software Engineer}
-\phone[mobile]{(720) 206-7218}
-\email{grfn@gws.fyi}
-\homepage{https://www.gws.fyi}
-\extrainfo{References available upon request}
-
-\begin{document}
-\makecvtitle{}
-\section{Skills}
-\cvitem{Clojure}{Extensive experience architecting, deploying, and building
-complex web applications in Clojure and Clojurescript, with a focus on
-Re-Frame and Reagent.}
-\cvitem{Haskell}{Passionate love for pure functional programming as a hobbyist
-pursuit, but also practical experience building production systems in Haskell at
-scale, and using Haskell's advanced type system extensions where appropriate to
-deliver increased ergonomics and safety.}
-\cvitem{Nix}{Experience with adopting and teaching nix at scale in a production
-stack both for local development dependencies and for configuring and building
-production software. Core contributer to a fork of the nix implementation itself
-(tvix) aimed at providing increased safety, performance, and flexibility.}
-\cvitem{Scala}{Understanding of Scala from the perspective of a functional
-programmer rather than a Java programmer. Experience building production
-big-data processing systems using Akka, and deep programming with Scala's type
-system using Shapeless.}
-\cvitem{Unix/Linux}{Experience with administrating highly available distributed
-systems. Passion for the Unix philosophy of discrete, composable units of
-functionality.}
-\cvitem{Ruby}{Experience building both full-stack applications with Ruby on
-Rails in addition to smaller microservices and custom frameworks. Deep
-understanding of the internals of the Ruby interpreter and object system.}
-\cvitem{Javascript}{Experience developing real-time responsive single-page web
-applications using React, in addition to significant contributions to the React
-open-source community.}
-\cvitem{SQL}{Deep understanding of relational databases, including experience
-designing the database schema in Postgres for an application with over a decade
-of usage, hundreds of gigabytes of data, complex, multi-tiered hierarchical data
-structures, as well as experience writing and optimizing large, complex queries
-against that database.}
-
-\subsection{Additional Tools}
-\cvitem{}{\footnotesize
-    \begin{itemize*}
-        \item Vim
-        \item Kubernetes
-        \item Git
-        \item Puppet
-        \item AWS
-        \item Reagent
-        \item Datomic
-        \item Elasticsearch
-        \item Redis
-        \item DynamoDB
-        \item Docker
-        \item JIRA
-        \item Java
-        \item QuickCheck (and similar tools)
-        \item Python
-        \item Elixir
-    \end{itemize*}
-    \newline
-    \textbf{Novice Level:}
-    \begin{itemize*}
-        \item Rust
-        \item C++
-        \item Erlang
-        \item Prolog
-        \item Idris
-        \item Agda
-        \item Tensorflow
-    \end{itemize*}}
-
-\section{Experience}
-\subsection{Employment}
-\cventry{2019-present}{Engineering Manager}{Urbint}{New York, NY}{}
-{\begin{itemize}
-   \item Lead of the platform team with two direct reports - a senior SRE and
-     a senior software engineer.
-   \item Performed user research on developers, project managers, product
-     managers, and other internal stakeholders to build the roadmap for the
-     platform team.
-   \item Built and maintained a system to deploy one-off full stack
-     application instances from pull requests to enable easier testing.
-   \item Led a large, multi-project migration between CI systems that resulted
-     in a decrease of average build times from 2 hours to less than 10 minutes.
-   \item Maintained and extended Nix-based build and development
-     infrastructure for both software engineers and machine learning engineers.
- \end{itemize}}
-\cventry{2018--2019}{Senior Software Engineer}{Urbint}{New York, NY}{}
-{\begin{itemize}
-   \item Built, trained, and maintained a large, deep-learning-based
-     image-detection model for semi-automated (human-in-the-loop) video
-     classification.
-   \item Designed, built, and maintained a novel in-house tool for collection of
-     training data.
-   \item Maintained and guaranteed reliability of a large data pipeline for
-     video processing and classification.
- \end{itemize}}
-\cventry{2017--2018}{Senior Software Engineer}{Urbint}{New York, NY}{}
-{\begin{itemize}
-   \item Integral in the architecture of a novel, serializable ACID
-     transactional graph database built on RocksDB, first in Elixir then in
-     Haskell.
-   \item Helped ship customer deliverables involving multi-day data
-     processing jobs for disparate data sources.
-   \item Instructed other developers in the use of and theory behind Haskell
-   \item Brought computational graph theory to bear on the problem of unifying
-     disparate, highly heterogeneous data sources across the world of open data.
- \end{itemize}}
-\cventry{2016--2017}{Senior Software Engineer}{SecurityScorecard, Inc.}{New York, NY}{}
-{Lead frontend developer for a rapidly-moving and growing security software startup.
-  \begin{itemize}
-    \item Took part in collaborative product design meetings to make UX
-      tradeoffs with product designers and managers.
-    \item Drove application architecture for a large, complex, data-driven frontend
-      application.
-    \item Championed increased use of production monitoring and alerting.
-    \item Worked with business stakeholders to set long- and short-term priorities for
-      application development.
-    \item Mentored junior team members.
-  \end{itemize}}
-\cventry{2015--2016}{Lead Developer}{Nomi, Inc.}{New York, NY}{}
-{Lead web services developer transitioning to a full-stack role implementing
-  shared software components and architecting a large, complex microservices
-  application ingesting hundreds of gigabytes of IoT data per week.
-  \begin{itemize}
-    \item Lead application architecture of the majority of the backend services to
-      encourage consistent REST API design and code sharing.
-    \item Championed the use of Haskell for rapid, safe development of the API Gateway
-      service.
-    \item Took ownership of operations and server maintenance of a >100-instance AWS
-      account using Puppet.
-  \end{itemize}}
-\cventry{2014--2015}{Lead Developer}{LandlordsNY, LLC}{New York, NY}{}
-{Sole engineer for a small startup connecting landlords and property managers and
-  facilitating the online sharing of information in a historically technology-averse
-  industry.
-  \begin{itemize}
-    \item Drove product design, visual design, and UX architecture for a major revamping
-      of the core product.
-    \item Interfaced with customers to set priorities for new feature development.
-    \item Conducted hiring and recruiting to build out an engineering team.
-  \end{itemize}}
-\cventry{2012--2014}{Associate Developer}{Visionlink Inc.}{Boulder, CO}{}
-{Integral member of an agile development team building the nation's most-used Information
-  and Referral platform for organizations such as United Way Worldwide and the American Red
-  Cross.
-  \begin{itemize}
-    \item Refactored and revamped legacy code to increase performance and long-term
-      maintainablity.
-    \item Worked on several triage-teams to rapidly fix production bugs with strict deadlines.
-    \item Built a complex, yet highly-performant tool for searching human services by category.
-    \item Acted as a core designer and developer of a major product revamp.
-      \begin{itemize}
-        \item Drove a complete rethinking of the data model in the product, leading to greater
-          unification, simplicity, and consistency;
-        \item Championed the adoption of a test-driven-development model;
-        \item Drove product documentation and code standardization.
-      \end{itemize}
-  \end{itemize}}
-
-\section{Project Highlights}
-\newcommand{\project}[3]{\item \textbf{#1} -- \textit{#2}\newline{}#3}
-\cvitem{}{\begin{itemize}
-  \project{Github Bug Bounty}{https://bounty.github.com/researchers/glittershark.html}{
-    Discovered and responsibly disclosed a persistent XSS on Github's main
-    website}
-  \project{Tvix}{https://cs.tvl.fyi/depot/-/blob/third\_party/nix/README.md}{
-    Fork of the Nix build tool delivering increased reliability, code
-    quality, and pluggability}
-  \project{Panettone}{https://cs.tvl.fyi/depot/-/tree/web/panettone}{
-    Aggressively simple bug-tracker developed in Common Lisp for the community
-    involved in the development of Tvix. Hosted at https://b.tvl.fyi}
-  \project{Org-Clubhouse}{https://github.com/glittershark/org-clubhouse}{
-    Emacs library for integration between org-mode and the Clubhouse issue
-    tracker}
-  \project{core-async-storage}{https://github.com/glittershark/core-async-storage}{
-    Simple Clojurescript wrapper around React Native's AsyncStorage using
-    core.async}
-\end{itemize}}
-
-\end{document}
-% vim: set tw=95 colorcolumn=-1:
diff --git a/users/grfn/secrets/bbbg.age b/users/grfn/secrets/bbbg.age
deleted file mode 100644
index 6c15dcdf73..0000000000
--- a/users/grfn/secrets/bbbg.age
+++ /dev/null
@@ -1,12 +0,0 @@
-age-encryption.org/v1
--> ssh-ed25519 CpJBgQ 6vLlq2WEcn6TE0rgahQyl7CYhCF3uiBD3hOnZkHswmM
-BSUiKPdDWMhYbi/+j9Kw5YDEOvjaickYQuhpWkhLktQ
--> ssh-ed25519 LfBFbQ mQJfyk35Ghd7UWouPlq4kTIFFwlRGh24r0kvJUgUbBw
-eYpBJEG9Cdc2qHI0maFpp9/2o30R0KGLRSQ7DzsVaZ0
--> ssh-ed25519 lZtaEQ npyXpqTMWITvRVfPwEQ1rXJ0sxnJvurLOfeiE07m92E
-oCXVRGOegBgQUJof8UHJsDdMyNsx6X575Rd4mWZ9LRk
--> ;^O0_l-grease
-sseb4RnQz93Wlgs5B0PE+j7AzFyMkzHjFbn9sCn0UA
---- Bqq0uedob5/kJOSoavN7Aq1fH7QVNW134M3uS6u2lFA
-Šr3?uΪjΎ§ΛF !ω_ΏΐRΨ/°#BQωΓΑ
-αNΧ”Γ"·ςV1ΝmwΊlΊΝyμφ's³ ˜λΘP’·ε^―6τνK{‡πΝΚt²3m ¬%‘Ν'zOoιμ8^SJϋ€νxκ¨―R=Ψήφ«€Ε•zEz*Ρ€ϊ€Β>¦g뻐‹%>\)Lj0Κ¬«5VQ8˜/H…XOρG΅‰s7gΘζDGIΙs·ήNόpiuYFj?x;]
\ No newline at end of file
diff --git a/users/grfn/secrets/buildkite-ssh-key.age b/users/grfn/secrets/buildkite-ssh-key.age
deleted file mode 100644
index 0ae5aa5502..0000000000
--- a/users/grfn/secrets/buildkite-ssh-key.age
+++ /dev/null
Binary files differdiff --git a/users/grfn/secrets/buildkite-token.age b/users/grfn/secrets/buildkite-token.age
deleted file mode 100644
index 9e9e370f1b..0000000000
--- a/users/grfn/secrets/buildkite-token.age
+++ /dev/null
@@ -1,12 +0,0 @@
-age-encryption.org/v1
--> ssh-ed25519 CpJBgQ tz7tudrJYQw2Ftnk7iNbSd/De2UJ0GAafFJjPwUo8xM
-bUBNO94Pjf79FErPxv92XnpXWFEgethREU+/U+xjWBc
--> ssh-ed25519 LfBFbQ yPjXk6XlJoGyVaCWMcPzfNXzb1cBNZhjYy+wsQtMhTI
-qk6hZMl1oeKLniGb/bKIxSb6ocVRCQsmQPcwxnlYfno
--> \'q-grease
-nYvpKokvFbVXfATzlQ7SPQa9Gw99E84SPRFdR7ey+HSCB705Q9uYwBpr9hjpiIod
-9PJIi88ENWf9/XAmm2d7daE+YPRYhln4U6w
---- EuyCLA6GvtbGI+EoC1z2dbpfyxo4ebXX1nY+9rsgUVY
-ϋ[°σίΥhΦΎΞ©ώΠͺτ`1?NCψΠΜ@Ε™Šu³–BlΣ8*ՈΝs₯¦‰Z~P―А―ό?8ώ
-
-ΥOΐ~{G}ˆ0q.ζυAŸ₯WΖ
\ No newline at end of file
diff --git a/users/grfn/secrets/cloudflare.age b/users/grfn/secrets/cloudflare.age
deleted file mode 100644
index e2f6e93603..0000000000
--- a/users/grfn/secrets/cloudflare.age
+++ /dev/null
@@ -1,9 +0,0 @@
-age-encryption.org/v1
--> ssh-ed25519 CpJBgQ tWx7wXCFjOOfD0wKRHHvLUdR+SF0i43xvnQG9GKurnk
-NRh7kSn7wqw80Y9EFr9Ccft+zYMadXZhYNPEaQlQXtQ
--> ssh-ed25519 LfBFbQ SPQMLC3Ehw00IG1CcbcLFZI2tHy89fjRgVgH4Iw2iBM
-oo2gT9472/DFRoZ6TYxhnM9ylRUNzoS8mLQYvn+4OSM
--> D[7+*-grease `>j ~Jk Dz%o vaKET3
-TkKVm8IpqfiVzETAi9+zuUtCdkReB+lHtthwNw
---- 3iOmY4TNICMi/Fz7k8pmoZlFym9uQBWNtHNlizoAMaM
-ZPzQ6ΚδžΒ5Ύ½ΌATΐΈI―·©;Ύ;Π—y5]­œk^!Ά`¬ƒt™—$R’Φ‚tήλK)ς<έγ§kΎϋϋ_°#XmASŠpU1©όŽ@šΒ)ϋβc©ΦΊέqœj1zΡ,HΔΒήgΘί:θ‘
\ No newline at end of file
diff --git a/users/grfn/secrets/ddclient-password.age b/users/grfn/secrets/ddclient-password.age
deleted file mode 100644
index 0de8707105..0000000000
--- a/users/grfn/secrets/ddclient-password.age
+++ /dev/null
@@ -1,9 +0,0 @@
-age-encryption.org/v1
--> ssh-ed25519 CpJBgQ 1Yw1EllkiG38qEQ03eN1p8WzC74zKb6YIuZMb3RD0ik
-P7iAo2rCex8XkCyWKjTSecAZDg5wokrfBLRk/Nl479w
--> ssh-ed25519 LfBFbQ /cMWDtN+SlCs7WsomkngPpPK/4RHpCDZr2bg+jyqHEs
-bgnNTXhxYtW84twA7ty9RWgycABW0MI9OEk69TRT2ro
--> Ujl-grease l_8cO.F
-pw/kCMvXCg4my4M
---- m3jMrTCJFA1bGgKERiAhAYvXt/++wWzva0CVdtz3cgQ
-]>'C[›*€*ϊj₯!³Ά`™7»<όŸQδƒψ mνIΡ7tGW ŒΟ³;η{Ja‘Υnˆpϊςπ–Χ©`b‡tΣΦ
\ No newline at end of file
diff --git a/users/grfn/secrets/secrets.nix b/users/grfn/secrets/secrets.nix
deleted file mode 100644
index 986ad181b8..0000000000
--- a/users/grfn/secrets/secrets.nix
+++ /dev/null
@@ -1,13 +0,0 @@
-let
-  grfn = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMcBGBoWd5pPIIQQP52rcFOQN3wAY0J/+K2fuU6SffjA";
-  mugwump = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFE2fxPgWO+zeQoLBTgsgxP7Vg7QNHlrQ+Rb3fHFTomB";
-  bbbg = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL/VzrNEY47KPTce3dgfORkAbweWkr4BI8j54BAIs7bG";
-in
-
-{
-  "bbbg.age".publicKeys = [ grfn mugwump bbbg ];
-  "cloudflare.age".publicKeys = [ grfn mugwump ];
-  "ddclient-password.age".publicKeys = [ grfn mugwump ];
-  "buildkite-ssh-key.age".publicKeys = [ grfn mugwump ];
-  "buildkite-token.age".publicKeys = [ grfn mugwump ];
-}
diff --git a/users/grfn/secrets/shell.nix b/users/grfn/secrets/shell.nix
deleted file mode 100644
index fe912fe791..0000000000
--- a/users/grfn/secrets/shell.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-let
-  depot = import ../../.. {};
-in
-depot.third_party.nixpkgs.mkShell {
-  buildInputs = [
-    depot.third_party.agenix.cli
-  ];
-}
diff --git a/users/grfn/system/home/common/solarized.nix b/users/grfn/system/home/common/solarized.nix
deleted file mode 100644
index e94693edc5..0000000000
--- a/users/grfn/system/home/common/solarized.nix
+++ /dev/null
@@ -1,18 +0,0 @@
-rec {
-  base03  = "#002B36";
-  base02  = "#073642";
-  base01  = "#586e75";
-  base00  = "#657b83";
-  base0   = "#839496";
-  base1   = "#93a1a1";
-  base2   = "#eee8d5";
-  base3   = "#fdf6e3";
-  yellow  = "#b58900";
-  orange  = "#cb4b16";
-  red     = "#dc322f";
-  magenta = "#d33682";
-  violet  = "#6c71c4";
-  blue    = "#268bd2";
-  cyan    = "#2aa198";
-  green   = "#859900";
-}
diff --git a/users/grfn/system/home/default.nix b/users/grfn/system/home/default.nix
deleted file mode 100644
index f62704152b..0000000000
--- a/users/grfn/system/home/default.nix
+++ /dev/null
@@ -1,32 +0,0 @@
-{ pkgs, depot, lib, ... }:
-
-with lib;
-
-rec {
-  home = confPath: (import "${pkgs.home-manager.src}/modules" {
-    inherit pkgs;
-
-    configuration = { config, lib, ... }: {
-      imports = [ confPath ];
-      lib.depot = depot;
-
-      # home-manager exposes no API to override the package set that
-      # is used, unless called from the NixOS module.
-      #
-      # To get around it, the module argument is overridden here.
-      _module.args.pkgs = mkForce pkgs;
-    };
-  });
-
-  dobharchu = home ./machines/dobharchu.nix;
-
-  dobharchuHome = dobharchu.activation-script;
-
-  yeren = home ./machines/yeren.nix;
-
-  yerenHome = yeren.activation-script;
-
-  meta.targets = [
-    "yerenHome"
-  ];
-}
diff --git a/users/grfn/system/home/machines/dobharchu.nix b/users/grfn/system/home/machines/dobharchu.nix
deleted file mode 100644
index 0b8503a00e..0000000000
--- a/users/grfn/system/home/machines/dobharchu.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  imports = [
-    ../platforms/darwin.nix
-    ../modules/common.nix
-    ../modules/games.nix
-  ];
-
-  home.packages = with pkgs; [
-    coreutils
-    gnupg
-    nix-prefetch-github
-    pass
-    pinentry_mac
-  ];
-}
diff --git a/users/grfn/system/home/machines/roswell.nix b/users/grfn/system/home/machines/roswell.nix
deleted file mode 100644
index f68f3a7be3..0000000000
--- a/users/grfn/system/home/machines/roswell.nix
+++ /dev/null
@@ -1,54 +0,0 @@
-{ pkgs, lib, config, ... }:
-
-let
-  laptopKeyboardId = "5";
-in
-
-{
-  imports = [
-    ../platforms/linux.nix
-    ../modules/shell.nix
-    ../modules/development.nix
-    ../modules/emacs.nix
-    ../modules/vim.nix
-  ];
-
-  home.packages = with pkgs; [
-    # System utilities
-    bat
-    htop
-    killall
-    bind
-    zip unzip
-    tree
-    ncat
-    bc
-    pv
-
-    # Security
-    gnupg
-    keybase
-    openssl
-
-    # Nix things
-    nixfmt
-    nix-prefetch-github
-    nix-review
-    cachix
-  ];
-
-  programs.password-store.enable = true;
-
-  programs.home-manager.enable = true;
-  home.stateVersion = "20.03";
-
-  xsession.enable = lib.mkForce false;
-
-  services.lorri.enable = true;
-
-  programs.direnv = {
-    enable = true;
-    enableBashIntegration = true;
-    enableZshIntegration = true;
-  };
-}
diff --git a/users/grfn/system/home/machines/yeren.nix b/users/grfn/system/home/machines/yeren.nix
deleted file mode 100644
index 0c89e9d123..0000000000
--- a/users/grfn/system/home/machines/yeren.nix
+++ /dev/null
@@ -1,77 +0,0 @@
-{ pkgs, lib, config, ... }:
-
-let
-  inherit (builtins) pathExists;
-  laptopKeyboardId = "5";
-in
-
-{
-  imports = [
-    ../platforms/linux.nix
-    ../modules/common.nix
-    ../modules/development/readyset.nix
-  ] ++ (lib.optional (pathExists ../modules/private.nix) ../modules/private.nix);
-
-  # for when hacking
-  programs.home-manager.enable = true;
-  home.stateVersion = "20.03";
-
-  system.machine = {
-    wirelessInterface = "wlp0s20f3";
-    i3FontSize = 9;
-  };
-
-  home.packages = with pkgs; [
-    zoom-us
-    slack
-    mysql
-    graphviz
-    mypaint
-    xdot
-    tdesktop
-    subsurface
-
-    (discord.override rec {
-      version = "0.0.16";
-      src = fetchurl {
-        url = "https://dl.discordapp.net/apps/linux/${version}/discord-${version}.tar.gz";
-        sha256 = "1s9qym58cjm8m8kg3zywvwai2i3adiq6sdayygk2zv72ry74ldai";
-      };
-    })
-
-    steam
-
-    awscli2
-  ];
-
-  systemd.user.services.laptop-keyboard = {
-    Unit = {
-      Description = "Swap caps+escape and alt+super, but only on the built-in laptop keyboard";
-      After = [ "graphical-session-pre.target" ];
-      PartOf = [ "graphical-session.target" ];
-    };
-
-    Install = { WantedBy = [ "graphical-session.target" ]; };
-
-    Service = {
-      Type = "oneshot";
-      RemainAfterExit = true;
-      ExecStart = (
-        "${pkgs.xorg.setxkbmap}/bin/setxkbmap "
-          + "-device ${laptopKeyboardId} "
-          + "-option caps:swapescape "
-          + "-option compose:ralt "
-          + "-option altwin:swap_alt_win"
-      );
-    };
-  };
-
-  xsession.windowManager.i3.config.keybindings.F9 = "exec lock";
-
-  xdg.mimeApps.defaultApplications."x-scheme-handler/tg" =
-    "telegramdesktop.desktop";
-
-  programs.zsh.shellAliases = {
-    "graph" = "curl -s localhost:6033/graph | dot -Tpng | feh -";
-  };
-}
diff --git a/users/grfn/system/home/modules/alacritty.nix b/users/grfn/system/home/modules/alacritty.nix
deleted file mode 100644
index 67d6638a31..0000000000
--- a/users/grfn/system/home/modules/alacritty.nix
+++ /dev/null
@@ -1,56 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  programs.alacritty = {
-    enable = true;
-    settings = {
-      font.size = 6;
-      font.normal.family = "Meslo LGSDZ Nerd Font";
-
-      draw_bold_text_with_bright_colors = false;
-
-      key_bindings = [
-        {
-          key = "Escape";
-          mods = "Control";
-          action = "ToggleViMode";
-        }
-      ];
-
-      colors = with import ../common/solarized.nix; rec {
-        # Default colors
-        primary = {
-          background = base3;
-          foreground = base00;
-        };
-
-        cursor = {
-          text = base3;
-          cursor = base00;
-        };
-
-        # Normal colors
-        normal = {
-          inherit red green yellow blue magenta cyan;
-          black = base02;
-          white = base2;
-        };
-
-        # Bright colors
-        # bright = normal;
-        bright = {
-          black = base03;
-          red = orange;
-          green = base01;
-          yellow = base00;
-          blue = base0;
-          magenta = violet;
-          cyan = base1;
-          white = base3;
-        };
-
-        vi_mode_cursor.cursor = red;
-      };
-    };
-  };
-}
diff --git a/users/grfn/system/home/modules/alsi.nix b/users/grfn/system/home/modules/alsi.nix
deleted file mode 100644
index c4a14e683c..0000000000
--- a/users/grfn/system/home/modules/alsi.nix
+++ /dev/null
@@ -1,58 +0,0 @@
-{ config, lib, pkgs, ... }:
-{
-  home.packages = [ config.lib.depot.third_party.alsi ];
-
-  xdg.configFile."alsi/alsi.logo" = {
-    source = ./nixos-logo.txt;
-    force = true;
-  };
-
-  xdg.configFile."alsi/alsi.conf" = {
-    force = true;
-    text = ''
-    #!${pkgs.perl}/bin/perl
-
-    scalar {
-      ALSI_VERSION         => "0.4.8",
-      COLORS_FILE          => "/home/grfn/.config/alsi/alsi.colors",
-      DE_FILE              => "/home/grfn/.config/alsi/alsi.de",
-      DEFAULT_COLOR_BOLD   => "blue",
-      DEFAULT_COLOR_NORMAL => "blue",
-      DF_COMMAND           => "df -Th -x sys -x tmpfs -x devtmpfs &>/dev/stdout",
-      GTK2_RC_FILE         => "/home/grfn/.gtkrc-2.0",
-      GTK3_RC_FILE         => "/home/grfn/.config/gtk-3.0/settings.ini",
-      LOGO_FILE            => "/home/grfn/.config/alsi/alsi.logo",
-      OUTPUT_FILE          => "/home/grfn/.config/alsi/alsi.output",
-      # PACKAGES_PATH      => "/var/lib/pacman/local/",
-      PS_COMMAND           => "ps -A",
-      USAGE_COLORS         => 0,
-      USAGE_COLORS_BOLD    => 0,
-      USAGE_PRECENT_GREEN  => 50,
-      USAGE_PRECENT_RED    => 100,
-      USAGE_PRECENT_YELLOW => 85,
-      USE_LOGO_FROM_FILE   => 1,
-      USE_VALUES_COLOR     => 0,
-      WM_FILE              => "/home/grfn/.config/alsi/alsi.wm",
-    }
-    '';
-  };
-
-  xdg.configFile."alsi/alsi.colors".text = ''
-    #!${pkgs.perl}/bin/perl
-
-    # Colors for alsi
-
-    scalar {
-       black   => {normal => "\e[0;30m", bold => "\e[1;30m"},
-       red     => {normal => "\e[0;31m", bold => "\e[1;31m"},
-       green   => {normal => "\e[0;32m", bold => "\e[1;32m"},
-       yellow  => {normal => "\e[0;33m", bold => "\e[1;33m"},
-       default => {normal => "\e[0;34m", bold => "\e[1;34m"},
-       blue    => {normal => "\e[0;34m", bold => "\e[1;34m"},
-       purple  => {normal => "\e[0;35m", bold => "\e[1;35m"},
-       cyan    => {normal => "\e[0;36m", bold => "\e[1;36m"},
-       white   => {normal => "\e[0;37m", bold => "\e[1;37m"},
-       reset   => "\e[0m",
-    }
-  '';
-}
diff --git a/users/grfn/system/home/modules/common.nix b/users/grfn/system/home/modules/common.nix
deleted file mode 100644
index 86e4ba400e..0000000000
--- a/users/grfn/system/home/modules/common.nix
+++ /dev/null
@@ -1,105 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-# Everything in here needs to work on linux or darwin
-
-{
-  imports = [
-    ../modules/shell.nix
-    ../modules/development.nix
-    ../modules/emacs.nix
-    ../modules/vim.nix
-    ../modules/tarsnap.nix
-    ../modules/twitter.nix
-    ../modules/lib/cloneRepo.nix
-  ];
-
-  programs.password-store.enable = true;
-
-  grfn.impure.clonedRepos.passwordStore = {
-    github = "glittershark/pass";
-    path = ".local/share/password-store";
-  };
-
-  home.packages = with pkgs; [
-    # System utilities
-    bat
-    htop
-    killall
-    bind
-    zip unzip
-    tree
-    ncat
-    bc
-    pv
-
-    # Security
-    gnupg
-    keybase
-    openssl
-
-    # Nix things
-    nixfmt
-    nix-prefetch-github
-    nix-review
-    cachix
-    (writeShellScriptBin "rebuild-mugwump" ''
-      set -eo pipefail
-      cd ~/code/depot
-      nix build -f . users.grfn.system.system.mugwumpSystem -o /tmp/mugwump
-      nix copy -f . users.grfn.system.system.mugwumpSystem \
-        --to ssh://mugwump
-      system=$(readlink -ef /tmp/mugwump)
-      ssh mugwump sudo nix-env -p /nix/var/nix/profiles/system --set $system
-      ssh mugwump sudo $system/bin/switch-to-configuration switch
-    '')
-    (writeShellScriptBin "rebuild-home" ''
-      set -eo pipefail
-      cd ~/code/depot
-      nix build -f . users.grfn.system.home.$(hostname)Home -o /tmp/home
-      /tmp/home/activate
-    '')
-  ];
-
-  programs.ssh = {
-    enable = true;
-
-    matchBlocks = {
-      "home" = {
-        host = "home.gws.fyi";
-        forwardAgent = true;
-      };
-
-      "dobharchu" = {
-        host = "dobharchu";
-        hostname = "172.16.0.4";
-        forwardAgent = true;
-        user = "griffin";
-      };
-
-      "cerberus" = {
-        host = "cerberus";
-        hostname = "172.16.0.3";
-        forwardAgent = true;
-        user = "griffin";
-      };
-
-      "mugwump" = {
-        host = "mugwump";
-        hostname = "172.16.0.5";
-        forwardAgent = true;
-      };
-
-      "roswell" = {
-        host = "roswell";
-        hostname = "18.223.118.13";
-        forwardAgent = true;
-      };
-    };
-  };
-
-  programs.direnv = {
-    enable = true;
-    enableBashIntegration = true;
-    enableZshIntegration = true;
-  };
-}
diff --git a/users/grfn/system/home/modules/development.nix b/users/grfn/system/home/modules/development.nix
deleted file mode 100644
index 653ce5d83a..0000000000
--- a/users/grfn/system/home/modules/development.nix
+++ /dev/null
@@ -1,213 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-
-  clj2nix = pkgs.callPackage (pkgs.fetchFromGitHub {
-    owner = "hlolli";
-    repo = "clj2nix";
-    rev = "3ab3480a25e850b35d1f532a5e4e7b3202232383";
-    sha256 = "1lry026mlpxp1j563qs13nhxf37i2zpl7lh0lgfdwc44afybqka6";
-  }) {};
-
-  pg-dump-upsert = pkgs.buildGoModule rec {
-    pname = "pg-dump-upsert";
-    version = "165258deaebded5e9b88f7a0acf3a4b7350e7bf4";
-
-    src = pkgs.fetchFromGitHub {
-      owner = "tomyl";
-      repo = "pg-dump-upsert";
-      rev = version;
-      sha256 = "1an4h8jjbj3r618ykjwk9brii4h9cxjqy47c4c8rivnvhimgf4wm";
-    };
-
-    vendorSha256 = "1a5fx6mrv30cl46kswicd8lf5i5shn1fykchvbnbhdpgxhbz6qi4";
-  };
-
-in
-
-with lib;
-
-{
-  imports = [
-    ./lib/zshFunctions.nix
-    ./development/kube.nix
-    # TODO(grfn): agda build is broken in the nixpkgs checkout
-    # ./development/agda.nix
-    ./development/rust.nix
-  ];
-
-  home.packages = with pkgs; [
-    jq
-    yq
-    gron
-    gitAndTools.hub
-    gitAndTools.tig
-    gitAndTools.gh
-    shellcheck
-    httpie
-    entr
-    gnumake
-    inetutils
-    tokei
-    jsonnet
-    ngrok
-    amber
-
-    gdb
-    lldb
-    hyperfine
-    clang-tools_11
-    (rr.overrideAttrs (_: rec {
-      version = "f25671d094edac8059cec56b98d7f10f2c740697";
-      src = pkgs.fetchFromGitHub {
-        owner = "rr-debugger";
-        repo = "rr";
-        rev = version;
-        sha256 = "149s4mw8vl8d3nx15sfp62z0izp2dibz99k720j75rjnkwk2bq2z";
-        fetchSubmodules = true;
-      };
-    }))
-
-    clj2nix
-    clojure
-    leiningen
-    clj-kondo
-
-    pg-dump-upsert
-
-    nodePackages.prettier
-  ] ++ optionals (stdenv.isLinux) [
-    # TODO(grfn): replace with stable again once the current julia debacle
-    # is resolved upstream, see https://github.com/NixOS/nixpkgs/pull/121114
-    julia_16-bin
-    valgrind
-  ];
-
-  programs.git = {
-    enable = true;
-    package = pkgs.gitFull;
-    userEmail = "root@gws.fyi";
-    userName  = "Griffin Smith";
-    ignores = [
-      "*.sw*"
-      ".classpath"
-      ".project"
-      ".settings/"
-      ".dir-locals.el"
-      ".stack-work-profiling"
-      ".projectile"
-    ];
-    extraConfig = {
-      github.user = "glittershark";
-      merge.conflictstyle = "diff3";
-      rerere.enabled = "true";
-      advice.skippedCherryPicks = "false";
-    };
-
-    delta = {
-      enable = true;
-      options = {
-        syntax-theme = "Solarized (light)";
-        hunk-style = "plain";
-        commit-style = "box";
-      };
-    };
-  };
-
-  home.file.".gdbinit".text = ''
-    set history filename ~/.gdb_history
-    set history save on
-    set history size unlimited
-    set history remove-duplicates unlimited
-    set history expansion on
-  '';
-
-  home.file.".psqlrc".text = ''
-    \set QUIET 1
-    \timing
-    \set ON_ERROR_ROLLBACK interactive
-    \set VERBOSITY verbose
-    \x auto
-    \set PROMPT1 '%[%033[1m%]%M/%/%R%[%033[0m%]%# '
-    \set PROMPT2 '...%# '
-    \set HISTFILE ~/.psql_history- :DBNAME
-    \set HISTCONTROL ignoredups
-    \pset null [null]
-    \unset QUIET
-  '';
-
-  programs.readline = {
-    enable = true;
-    extraConfig = ''
-      set editing-mode vi
-    '';
-  };
-
-  programs.zsh = {
-    shellAliases = {
-      # Git
-      "gwip" = "git add . && git commit -am wip";
-      "gpr" = "g pull-request";
-      "gcl" = "git clone";
-      "grs" = "gr --soft";
-      "grhh" = "grh HEAD";
-      "grh" = "gr --hard";
-      "gr" = "git reset";
-      "gcb" = "gc -b";
-      "gco" = "gc";
-      "gcd" = "gc development";
-      "gcm" = "gc master";
-      "gcc" = "gc canon";
-      "gc" = "git checkout";
-      "gbg" = "git branch | grep";
-      "gba" = "git branch -a";
-      "gb" = "git branch";
-      "gcv" = "git commit --verbose";
-      "gci" = "git commit";
-      "gm" = "git merge";
-      "gdc" = "gd --cached";
-      "gd" = "git diff";
-      "gsl" = "git stash list";
-      "gss" = "git show stash";
-      "gsad" = "git stash drop";
-      "gsa" = "git stash";
-      "gst" = "gs";
-      "gs" = "git status";
-      "gg" = "gl --decorate --oneline --graph --date-order --all";
-      "gl" = "git log";
-      "gf" = "git fetch";
-      "gur" = "gu --rebase";
-      "gu" = "git pull";
-      "gpf" = "gp -f";
-      "gpa" = "gp --all";
-      "gpu" = "git push -u origin \"$(git symbolic-ref --short HEAD)\"";
-      "gp" = "git push";
-      "ganw" = "git diff -w --no-color | git apply --cached --ignore-whitespace";
-      "ga" = "git add";
-      "gnp" = "git --no-pager";
-      "g" = "git";
-      "git" = "hub";
-      "grim" = "git fetch && git rebase -i --autostash origin/master";
-      "grom" = "git fetch && git rebase --autostash origin/master";
-      "groc" = "git fetch && git rebase --autostash origin/canon";
-      "grc" = "git rebase --continue";
-      "gcan" = "git commit --amend --no-edit";
-      "grl" = "git reflog";
-
-      # Haskell
-      "crl" = "cabal repl";
-      "cr" = "cabal run";
-      "cnb" = "cabal new-build";
-      "cob" = "cabal old-build";
-      "cnr" = "cabal new-run";
-      "cor" = "cabal old-run";
-      "ho" = "hoogle";
-    };
-
-    functions = {
-      gdelmerged = ''
-      git branch --merged | egrep -v 'master' | tr -d '+ ' | xargs git branch -d
-      '';
-    };
-  };
-}
diff --git a/users/grfn/system/home/modules/development/readyset.nix b/users/grfn/system/home/modules/development/readyset.nix
deleted file mode 100644
index 19f44b7703..0000000000
--- a/users/grfn/system/home/modules/development/readyset.nix
+++ /dev/null
@@ -1,16 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  imports = [
-    ./rust.nix
-  ];
-
-  home.packages = with pkgs; [
-    # This goes in $PATH so I can run it from rofi and parent to my WM
-    (writeShellScriptBin "dotclip" "xclip -out -selection clipboard | dot -Tpng | feh -")
-  ];
-
-  programs.zsh.shellAliases = {
-    "tf" = "terraform";
-  };
-}
diff --git a/users/grfn/system/home/modules/development/rust.nix b/users/grfn/system/home/modules/development/rust.nix
deleted file mode 100644
index 4ae5bc3bcc..0000000000
--- a/users/grfn/system/home/modules/development/rust.nix
+++ /dev/null
@@ -1,34 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-
-{
-  home.packages = with pkgs; [
-    rustup
-    rust-analyzer
-    cargo-edit
-    cargo-expand
-    cargo-flamegraph
-    cargo-rr
-    cargo-udeps
-    cargo-bloat
-    sccache
-    evcxr
-  ];
-
-  programs.zsh.shellAliases = {
-    "cg" = "cargo";
-    "cb" = "cargo build";
-    "ct" = "cargo test";
-    "ctw" = "fd -e rs | entr cargo test";
-    "cch" = "cargo check";
-  };
-
-  home.file.".cargo/config".text = ''
-    [build]
-    rustc-wrapper = "${pkgs.sccache}/bin/sccache"
-
-    [target.x86_64-unknown-linux-gnu]
-    linker = "clang"
-    rustflags = ["-C", "link-arg=-fuse-ld=${pkgs.mold}/bin/mold"]
-  '';
-}
diff --git a/users/grfn/system/home/modules/emacs.nix b/users/grfn/system/home/modules/emacs.nix
deleted file mode 100644
index 6cc38bc7ab..0000000000
--- a/users/grfn/system/home/modules/emacs.nix
+++ /dev/null
@@ -1,108 +0,0 @@
-{ pkgs, lib, config, ... }:
-
-with lib;
-
-let
- # doom-emacs = pkgs.callPackage (builtins.fetchTarball {
- #   url = https://github.com/vlaci/nix-doom-emacs/archive/master.tar.gz;
- # }) {
- #   doomPrivateDir = ./doom.d;  # Directory containing your config.el init.el
- #                               # and packages.el files
- # };
-
-  depot = config.lib.depot;
-
-in {
-  imports = [
-    ./lib/cloneRepo.nix
-  ];
-
-  # home.packages = [ doom-emacs ];
-  # home.file.".emacs.d/init.el".text = ''
-  #     (load "default.el")
-  # '';
-  #
-
-  config = mkMerge [
-    {
-      home.packages = with pkgs; [
-        # LaTeX (for org export)
-        (pkgs.texlive.combine {
-          inherit (pkgs.texlive)
-            capt-of
-            collection-fontsrecommended
-            dvipng
-            fancyvrb
-            float
-            fncychap
-            framed
-            mathpartir
-            needspace
-            parskip
-            scheme-basic
-            semantic
-            tabulary
-            titlesec
-            ulem
-            upquote
-            varwidth
-            wrapfig
-          ;
-        })
-
-        ispell
-
-        ripgrep
-        coreutils
-        fd
-        clang
-        gnutls
-        emacsPackages.telega
-      ];
-
-      programs.emacs = {
-        enable = true;
-        package = pkgs.emacsUnstable;
-        extraPackages = (epkgs:
-          (with epkgs; [
-            tvlPackages.dottime
-            tvlPackages.tvl
-            vterm
-            telega
-          ])
-        );
-      };
-
-      grfn.impure.clonedRepos = {
-        orgClubhouse = {
-          github = "glittershark/org-clubhouse";
-          path = "code/org-clubhouse";
-        };
-
-        doomEmacs = {
-          github = "hlissner/doom-emacs";
-          path = ".emacs.d";
-          after = ["emacs.d"];
-          onClone = "bin/doom install";
-        };
-
-        "emacs.d" = {
-          github = "glittershark/emacs.d";
-          path = ".doom.d";
-          after = ["orgClubhouse"];
-        };
-      };
-
-      programs.zsh.shellAliases = {
-        "ec" = "emacsclient";
-      };
-    }
-    (mkIf pkgs.stdenv.isLinux {
-      # Notes
-      services.syncthing = {
-        enable = true;
-        tray = true;
-      };
-    })
-  ];
-}
diff --git a/users/grfn/system/home/modules/email.nix b/users/grfn/system/home/modules/email.nix
deleted file mode 100644
index 0a3e58205b..0000000000
--- a/users/grfn/system/home/modules/email.nix
+++ /dev/null
@@ -1,87 +0,0 @@
-{ lib, pkgs, config, ... }:
-
-with lib;
-
-let
-
-  # from home-manager/modules/services/lieer.nix
-  escapeUnitName = name:
-    let
-      good = upperChars ++ lowerChars ++ stringToCharacters "0123456789-_";
-      subst = c: if any (x: x == c) good then c else "-";
-    in stringAsChars subst name;
-
-  accounts = {
-    personal = {
-      primary = true;
-      address = "root@gws.fyi";
-      aliases = [ "grfn@gws.fyi" ];
-      passEntry = "root-gws-msmtp";
-    };
-
-    work = {
-      address = "griffin@readyset.io";
-      passEntry = "readyset/msmtp";
-    };
-
-  };
-
-in {
-  programs.lieer.enable = true;
-  programs.notmuch.enable = true;
-  services.lieer.enable = true;
-  programs.msmtp.enable = true;
-
-  home.packages = with pkgs; [
-    mu
-    msmtp
-  ];
-
-  systemd.user.services = mapAttrs' (name: account: {
-    name = escapeUnitName "lieer-${name}";
-    value.Service = {
-      ExecStart = mkForce "${pkgs.writeShellScript "sync-${name}" ''
-        ${pkgs.gmailieer}/bin/gmi sync --path ~/mail/${name}
-      ''}";
-      Environment = "NOTMUCH_CONFIG=${config.home.sessionVariables.NOTMUCH_CONFIG}";
-    };
-
-  }) accounts;
-
-  # xdg.configFile."notifymuch/notifymuch.cfg".text = generators.toINI {} {
-  #   notifymuch = {
-  #     query = "is:unread and is:important";
-  #     mail_client = "";
-  #     recency_interval_hours = "48";
-  #     hidden_tags = "inbox unread attachment replied sent encrypted signed";
-  #   };
-  # };
-
-  accounts.email.maildirBasePath = "mail";
-  accounts.email.accounts = mapAttrs (_: params@{ passEntry, ... }: {
-    realName = "Griffin Smith";
-    passwordCommand = "pass ${passEntry}";
-
-    flavor = "gmail.com";
-
-    imapnotify = {
-      enable = true;
-      boxes = [ "Inbox" ];
-    };
-
-    gpg = {
-      key = "0F11A989879E8BBBFDC1E23644EF5B5E861C09A7";
-      signByDefault = true;
-    };
-
-    notmuch.enable = true;
-    lieer = {
-      enable = true;
-      sync = {
-        enable = true;
-        frequency = "*:*";
-      };
-    };
-    msmtp.enable = true;
-  } // builtins.removeAttrs params ["passEntry"]) accounts;
-}
diff --git a/users/grfn/system/home/modules/games.nix b/users/grfn/system/home/modules/games.nix
deleted file mode 100644
index 26dc9d31f3..0000000000
--- a/users/grfn/system/home/modules/games.nix
+++ /dev/null
@@ -1,59 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with pkgs;
-with lib;
-
-let
-
-  df-orig = dwarf-fortress-packages.dwarf-fortress-original;
-
-  df-full = (dwarf-fortress-packages.dwarf-fortress-full.override {
-    theme = null;
-    enableIntro = false;
-    enableFPS = true;
-  });
-
-  init = runCommand "init.txt" {} ''
-    substitute "${df-orig}/data/init/init.txt" $out \
-      --replace "[INTRO:YES]" "[INTRO:NO]" \
-      --replace "[VOLUME:255]" "[VOLUME:0]" \
-      --replace "[FPS:NO]" "[FPS:YES]"
-  '';
-
-  d_init = runCommand "d_init.txt" {} ''
-    substitute "${df-orig}/data/init/d_init.txt" $out \
-      --replace "[AUTOSAVE:NONE]" "[AUTOSAVE:SEASONAL]" \
-      --replace "[AUTOSAVE_PAUSE:NO]" "[AUTOSAVE_PAUSE:YES]" \
-      --replace "[INITIAL_SAVE:NO]" "[INITIAL_SAVE:YES]" \
-      --replace "[EMBARK_WARNING_ALWAYS:NO]" "[EMBARK_WARNING_ALWAYS:YES]" \
-      --replace "[VARIED_GROUND_TILES:YES]" "[VARIED_GROUND_TILES:NO]" \
-      --replace "[SHOW_FLOW_AMOUNTS:NO]" "[SHOW_FLOW_AMOUNTS:YES]"
-  '';
-
-  df = runCommand "dwarf-fortress" {} ''
-    mkdir -p $out/bin
-    sed \
-      -e '4icp -f ${init} "$DF_DIR/data/init/init.txt"' \
-      -e '4icp -f ${d_init} "$DF_DIR/data/init/d_init.txt"' \
-      < "${df-full}/bin/dwarf-fortress" >"$out/bin/dwarf-fortress"
-
-    shopt -s extglob
-    ln -s ${df-full}/bin/!(dwarf-fortress) $out/bin
-
-    chmod +x $out/bin/dwarf-fortress
-  '';
-
-in mkMerge [
-  {
-    home.packages = [
-      crawl
-      xonotic
-    ];
-  }
-  (mkIf stdenv.isLinux {
-    home.packages = [
-      df
-      polymc
-    ];
-  })
-]
diff --git a/users/grfn/system/home/modules/i3.nix b/users/grfn/system/home/modules/i3.nix
deleted file mode 100644
index 111f2a08c2..0000000000
--- a/users/grfn/system/home/modules/i3.nix
+++ /dev/null
@@ -1,366 +0,0 @@
-{ config, lib, pkgs, ... }:
-let
-  mod = "Mod4";
-  solarized = import ../common/solarized.nix;
-  # TODO pull this out into lib
-  emacsclient = eval: pkgs.writeShellScript "emacsclient-eval" ''
-    msg=$(emacsclient --eval '${eval}' 2>&1)
-    echo "''${msg:1:-1}"
-  '';
-  screenlayout = {
-    home = pkgs.writeShellScript "screenlayout_home.sh" ''
-      xrandr \
-        --output eDP-1 --mode 1920x1200 --pos 0x960 --rotate normal \
-        --output DP-3 --primary --mode 3840x2160 --pos 1920x0 --rotate normal \
-        --output DP-1 --off \
-        --output DP-2 --off \
-        --output DP-4 --off
-    '';
-  };
-
-  inherit (builtins) map;
-  inherit (lib) mkMerge range;
-in {
-  options = with lib; {
-    system.machine.wirelessInterface = mkOption {
-      description = ''
-        Name of the primary wireless interface. Used by i3status, etc.
-      '';
-      default = "wlp3s0";
-      type = types.str;
-    };
-
-    system.machine.i3FontSize = mkOption {
-      description = "Font size to use in i3 window decorations etc.";
-      default = 6;
-      type = types.int;
-    };
-  };
-
-  config =
-    let decorationFont = "MesloLGSDZ ${toString config.system.machine.i3FontSize}"; in
-    {
-      home.packages = with pkgs; [
-        rofi
-        rofi-pass
-        python38Packages.py3status
-        i3lock
-        i3status
-        dconf # for gtk
-
-        # Screenshots
-        maim
-
-        # GIFs
-        picom
-        peek
-
-        (pkgs.writeShellScriptBin "lock" ''
-          playerctl pause
-          ${pkgs.i3lock}/bin/i3lock -c 222222
-        '')
-      ];
-
-      xsession.scriptPath = ".xsession";
-
-      xsession.windowManager.i3 = {
-        enable = true;
-        config = {
-          modifier = mod;
-          keybindings =
-            mkMerge (
-              (map
-                (n: {
-                  "${mod}+${toString n}" =
-                    "workspace ${toString n}";
-                  "${mod}+Shift+${toString n}" =
-                    "move container to workspace ${toString n}";
-                })
-                (range 0 9))
-            ++ [(rec {
-              "${mod}+h" = "focus left";
-              "${mod}+j" = "focus down";
-              "${mod}+k" = "focus up";
-              "${mod}+l" = "focus right";
-              "${mod}+semicolon" = "focus parent";
-
-              "${mod}+Shift+h" = "move left";
-              "${mod}+Shift+j" = "move down";
-              "${mod}+Shift+k" = "move up";
-              "${mod}+Shift+l" = "move right";
-
-              "${mod}+Shift+x" = "kill";
-
-              "${mod}+Return" = "exec alacritty";
-
-              "${mod}+Shift+s" = "split h";
-              "${mod}+Shift+v" = "split v";
-              "${mod}+e" = "layout toggle split";
-              "${mod}+w" = "layout tabbed";
-              "${mod}+s" = "layout stacking";
-
-              "${mod}+f" = "fullscreen";
-
-              "${mod}+Shift+r" = "restart";
-
-              "${mod}+r" = "mode resize";
-
-              # Marks
-              "${mod}+Shift+m" = ''exec i3-input -F "mark %s" -l 1 -P 'Mark: ' '';
-              "${mod}+m" = ''exec i3-input -F '[con_mark="%s"] focus' -l 1 -P 'Go to: ' '';
-
-              # Screenshots
-              "${mod}+q" = "exec \"maim | xclip -selection clipboard -t image/png\"";
-              "${mod}+Shift+q" = "exec \"maim -s | xclip -selection clipboard -t image/png\"";
-              "${mod}+Ctrl+q" = "exec ${pkgs.writeShellScript "peek.sh" ''
-              ${pkgs.picom}/bin/picom &
-              picom_pid=$!
-              ${pkgs.peek}/bin/peek || true
-              kill -SIGINT $picom_pid
-            ''}";
-
-              # Launching applications
-              "${mod}+u" = "exec ${pkgs.writeShellScript "rofi" ''
-              rofi \
-                -modi 'combi' \
-                -combi-modi "window,drun,ssh,run" \
-                -font '${decorationFont}' \
-                -show combi
-            ''}";
-
-              # Passwords
-              "${mod}+p" = "exec rofi-pass -font '${decorationFont}'";
-
-              # Media
-              "XF86AudioPlay" = "exec playerctl play-pause";
-              "XF86AudioNext" = "exec playerctl next";
-              "XF86AudioPrev" = "exec playerctl previous";
-              "XF86AudioRaiseVolume" = "exec pulseaudio-ctl up";
-              "XF86AudioLowerVolume" = "exec pulseaudio-ctl down";
-              "XF86AudioMute" = "exec pulseaudio-ctl mute";
-
-              # Lock
-              Pause = "exec lock";
-
-              # Brightness
-              "XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl -q s 5%-";
-              "XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl -q s 5%+";
-
-              # Sleep/hibernate
-              # "${mod}+Escape" = "exec systemctl suspend";
-              # "${mod}+Shift+Escape" = "exec systemctl hibernate";
-
-              # Scratch buffer
-              "${mod}+minus" = "scratchpad show";
-              "${mod}+Shift+minus" = "move scratchpad";
-              "${mod}+space" = "focus mode_toggle";
-              "${mod}+Shift+space" = "floating toggle";
-
-              # Screen Layout
-              "${mod}+Shift+t" = "exec xrandr --auto";
-              "${mod}+t" = "exec ${screenlayout.home}";
-              "${mod}+Ctrl+t" = "exec ${pkgs.writeShellScript "fix_term.sh" ''
-              xrandr --output eDP-1 --off && ${screenlayout.home}
-            ''}";
-
-              # Notifications
-              "${mod}+Shift+n" = "exec killall -SIGUSR1 .dunst-wrapped";
-              "${mod}+n" = "exec killall -SIGUSR2 .dunst-wrapped";
-              "Control+space" = "exec ${pkgs.dunst}/bin/dunstctl close";
-              "Control+Shift+space" = "exec ${pkgs.dunst}/bin/dunstctl close-all";
-              "Control+grave" = "exec ${pkgs.dunst}/bin/dunstctl history-pop";
-              "Control+Shift+period" = "exec ${pkgs.dunst}/bin/dunstctl action";
-            })]);
-
-          fonts = [ decorationFont ];
-
-          colors = with solarized; rec {
-            focused = {
-              border = base01;
-              background = base01;
-              text = base3;
-              indicator = red;
-              childBorder = base02;
-            };
-            focusedInactive = focused // {
-              border = base03;
-              background = base03;
-              # text = base1;
-            };
-            unfocused = focusedInactive;
-            background = base03;
-          };
-
-          modes.resize = {
-            l = "resize shrink width 5 px or 5 ppt";
-            k = "resize grow height 5 px or 5 ppt";
-            j = "resize shrink height 5 px or 5 ppt";
-            h = "resize grow width 5 px or 5 ppt";
-
-            Return = "mode \"default\"";
-          };
-
-          bars = [{
-            statusCommand =
-              let i3status-conf = pkgs.writeText "i3status.conf" ''
-              general {
-                  output_format = i3bar
-                  colors = true
-                  color_good = "#859900"
-
-                  interval = 1
-              }
-
-              order += "external_script current_task"
-              order += "external_script inbox"
-              order += "spotify"
-              order += "volume_status"
-              order += "wireless ${config.system.machine.wirelessInterface}"
-              # order += "ethernet enp3s0f0"
-              order += "cpu_usage"
-              order += "battery 0"
-              # order += "volume master"
-              order += "time"
-              order += "tztime utc"
-
-              mpd {
-                  format = "%artist - %album - %title"
-              }
-
-              wireless ${config.system.machine.wirelessInterface} {
-                  format_up = "W: (%quality - %essid - %bitrate) %ip"
-                  format_down = "W: -"
-              }
-
-              ethernet enp3s0f0 {
-                  format_up = "E: %ip"
-                  format_down = "E: -"
-              }
-
-              battery 0 {
-                  format = "%status %percentage"
-                  path = "/sys/class/power_supply/BAT%d/uevent"
-                  low_threshold = 10
-              }
-
-              cpu_usage {
-                  format = "CPU: %usage"
-              }
-
-              load {
-                  format = "%5min"
-              }
-
-              time {
-                  format = "    %a %h %d ⌚   %I:%M     "
-              }
-
-              spotify {
-                  color_playing = "#fdf6e3"
-                  color_paused = "#93a1a1"
-                  format_stopped = ""
-                  format_down = ""
-                  format = "{title} - {artist} ({album})"
-              }
-
-              external_script inbox {
-                  script_path = '${emacsclient "(grfn/num-inbox-items-message)"}'
-                  format = 'Inbox: {output}'
-                  cache_timeout = 120
-                  color = "#93a1a1"
-              }
-
-              external_script current_task {
-                  script_path = '${emacsclient "(grfn/org-current-clocked-in-task-message)"}'
-                  # format = '{output}'
-                  cache_timeout = 60
-                  color = "#93a1a1"
-              }
-
-              tztime utc {
-                  timezone = "UTC"
-                  format = "    %HΒ·%M    "
-              }
-
-              volume_status {
-                  format = "☊ {percentage}"
-                  format_muted = "☊ X"
-                  # device = "default"
-                  # mixer_idx = 0
-              }
-            '';
-              in "py3status -c ${i3status-conf}";
-            fonts = [ decorationFont ];
-            position = "top";
-            colors = with solarized; rec {
-              background = base03;
-              statusline = base3;
-              separator = base1;
-              activeWorkspace = {
-                border = base03;
-                background = base1;
-                text = base3;
-              };
-              focusedWorkspace = activeWorkspace;
-              inactiveWorkspace = activeWorkspace // {
-                background = base01;
-              };
-              urgentWorkspace = activeWorkspace // {
-                background = red;
-              };
-            };
-          }];
-        };
-      };
-
-      services.dunst = {
-        enable = true;
-        settings = with solarized; {
-          global = {
-            font = "MesloLGSDZ ${toString (config.system.machine.i3FontSize * 1.5)}";
-            allow_markup = true;
-            format = "<b>%s</b>\n%b";
-            sort = true;
-            alignment = "left";
-            geometry = "600x15-40+40";
-            idle_threshold = 120;
-            separator_color = "frame";
-            separator_height = 1;
-            word_wrap = true;
-            padding = 8;
-            horizontal_padding = 8;
-            max_icon_size = 45;
-          };
-
-          frame = {
-            width = 0;
-            color = "#aaaaaa";
-          };
-
-          urgency_low = {
-            background = base03;
-            foreground = base3;
-            timeout = 5;
-          };
-
-          urgency_normal = {
-            background = base02;
-            foreground = base3;
-            timeout = 7;
-          };
-
-          urgency_critical = {
-            background = red;
-            foreground = base3;
-            timeout = 0;
-          };
-        };
-      };
-
-      gtk = {
-        enable = true;
-        iconTheme.name = "Adwaita";
-        theme.name = "Adwaita";
-      };
-  };
-}
diff --git a/users/grfn/system/home/modules/lib/cloneRepo.nix b/users/grfn/system/home/modules/lib/cloneRepo.nix
deleted file mode 100644
index dc487dc6bd..0000000000
--- a/users/grfn/system/home/modules/lib/cloneRepo.nix
+++ /dev/null
@@ -1,67 +0,0 @@
-{ lib, config, ... }:
-with lib;
-{
-  options = {
-    grfn.impure.clonedRepos = mkOption {
-      description = "Repositories to clone";
-      default = {};
-      type = with types; loaOf (
-        let sm = submodule {
-          options = {
-            url = mkOption {
-              type = nullOr str;
-              description = "URL of repository to clone";
-              default = null;
-            };
-
-            github = mkOption {
-              type = nullOr str;
-              description = "Github owner/repo of repository to clone";
-              default = null;
-            };
-
-            path = mkOption {
-              type = str;
-              description = "Path to clone to";
-            };
-
-            onClone = mkOption {
-              type = str;
-              description = ''
-                Shell command to run after cloning the repo for the first time.
-                Runs inside the repo itself.
-              '';
-              default = "";
-            };
-
-            after = mkOption {
-              type = listOf str;
-              description = "Activation hooks that this repository must be cloned after";
-              default = [];
-            };
-          };
-        };
-        in addCheck sm (cr: (! isNull cr.url || ! isNull cr.github))
-      );
-    };
-  };
-
-  config = {
-    home.activation =
-      mapAttrs
-      (_: {
-        url, path, github, onClone, after, ...
-      }:
-        let repoURL = if isNull url then "git@github.com:${github}" else url;
-        in hm.dag.entryAfter (["writeBoundary"] ++ after) ''
-          $DRY_RUN_CMD mkdir -p $(dirname "${path}")
-          if [[ ! -d ${path} ]]; then
-            $DRY_RUN_CMD git clone "${repoURL}" "${path}"
-            pushd ${path}
-            $DRY_RUN_CMD ${onClone}
-            popd
-          fi
-        '')
-      config.grfn.impure.clonedRepos;
-  };
-}
diff --git a/users/grfn/system/home/modules/lib/zshFunctions.nix b/users/grfn/system/home/modules/lib/zshFunctions.nix
deleted file mode 100644
index 7c39b3478c..0000000000
--- a/users/grfn/system/home/modules/lib/zshFunctions.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-{
-  options = {
-    programs.zsh.functions = mkOption {
-      description = "An attribute set that maps function names to their source";
-      default = {};
-      type = with types; attrsOf (either str path);
-    };
-  };
-
-  config.programs.zsh.initExtra = concatStringsSep "\n" (
-    mapAttrsToList (name: funSrc: ''
-      function ${name}() {
-        ${funSrc}
-      }
-    '') config.programs.zsh.functions
-  );
-}
diff --git a/users/grfn/system/home/modules/nixos-logo.txt b/users/grfn/system/home/modules/nixos-logo.txt
deleted file mode 100644
index d4b16b44f0..0000000000
--- a/users/grfn/system/home/modules/nixos-logo.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-                 ((((((          ###%######       ##%###/
-               ,(((((((/(          #%#%#%#%#    .#%#%#%#%#
-                 ((((((///          %#######%. #####%###/
-                  (((((/(//,         /##%###%###%######
-                    (((//////          #####%########(
-         .(((((((((((((((///////////////#%%%########          ((
-        (((((((((((((((///////////////////#########         .((((
-       ((((((((((((((((/(//////////////////##########      ((((((((
-                   (#########                #########    (((((((((
-                  #########                   #########/((((((((((
-                *#########                     .#######(((((((((
- ###%###################                         ####(//((((((((((((((((
-####%##################                           .#////////((((((((((((((
-%%%%%%%%%%%%%%#######((                           ////////////((((((((((((
- ###%#######%#######////.                        ///////////////////((((
-         ###%###%#///////(                      /////////
-       .####%#### /////////                   /////////,
-      %#%#%#%#%*   /////////(                /////////
-      .#####%#       ////////(######################%#######%#####,
-        %####         (////////#####################%###%###%###%
-         .#          (//////(//((###################%#######%##%
-                    (//(((((((((((          #####%%%%(
-                  //(/((((((((((((((          ######%##
-                 (((((((((  (((((((((          #####%###/
-                (((((((((    /(((((((((         .###%####%
-                 ((((((        (((((((((          %#%#%#/
diff --git a/users/grfn/system/home/modules/obs.nix b/users/grfn/system/home/modules/obs.nix
deleted file mode 100644
index d1dade477c..0000000000
--- a/users/grfn/system/home/modules/obs.nix
+++ /dev/null
@@ -1,66 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with pkgs;
-
-let
-  libuiohook = stdenv.mkDerivation rec {
-    pname = "libuiohook";
-    version = "1.1";
-    src = fetchFromGitHub {
-      owner = "kwhat";
-      repo = "libuiohook";
-      rev = version;
-      sha256 = "1isfxn3cfrdqq22d3mlz2lzm4asf9gprs7ww2xy9c3j3srk9kd7r";
-    };
-
-    preConfigure = ''
-      ./bootstrap.sh
-    '';
-
-    nativeBuildInputs = [ pkg-config ];
-    buildInputs = [
-      libtool autoconf automake
-      x11
-      xorg.libXtst
-      xorg.libXinerama
-      xorg.libxkbfile
-      libxkbcommon
-    ];
-  };
-
-  obs-input-overlay = stdenv.mkDerivation rec {
-    pname = "obs-input-overlay";
-    version = "4.8";
-    src = fetchFromGitHub {
-      owner = "univrsal";
-      repo = "input-overlay";
-      rev = "v${version}";
-      sha256 = "1dklg0dx9ijwyhgwcaqz859rbpaivmqxqvh9w3h4byrh5pnkz8bf";
-      fetchSubmodules = true;
-    };
-
-    nativeBuildInputs = [ cmake ];
-    buildInputs = [ obs-studio libuiohook ];
-
-    postPatch = ''
-      sed -i CMakeLists.txt \
-        -e '2iinclude(${obs-studio.src}/cmake/Modules/ObsHelpers.cmake)' \
-        -e '2ifind_package(LibObs REQUIRED)'
-    '';
-
-    cmakeFlags = [
-      "-Wno-dev"
-    ];
-  };
-in
-{
-  home.packages = [
-    obs-studio
-    obs-input-overlay
-  ];
-
-  xdg.configFile."obs-studio/plugins/input-overlay/bin/64bit/input-overlay.so".source =
-    "${obs-input-overlay}/lib/obs-plugins/input-overlay.so";
-  xdg.configFile."obs-studio/plugins/input-overlay/data".source =
-    "${obs-input-overlay}/share/obs/obs-plugins/input-overlay";
-}
diff --git a/users/grfn/system/home/modules/rtlsdr.nix b/users/grfn/system/home/modules/rtlsdr.nix
deleted file mode 100644
index a1c717617a..0000000000
--- a/users/grfn/system/home/modules/rtlsdr.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-
-  nixpkgs-gnuradio = import (pkgs.fetchFromGitHub {
-    owner = "doronbehar";
-    repo = "nixpkgs";
-    rev = "712561aa5f10bfe6112a1726a912585612a70d1f";
-    sha256 = "04yqflbwjcfl9vlplphpj82csqqz9k6m3nj1ybhwgmsc4by7vivl";
-  }) {};
-
-in
-
-{
-  home.packages = with pkgs; [
-    rtl-sdr
-    nixpkgs-gnuradio.gnuradio
-    nixpkgs-gnuradio.gnuradio.plugins.osmosdr
-    nixpkgs-gnuradio.gqrx
-  ];
-}
diff --git a/users/grfn/system/home/modules/shell.nix b/users/grfn/system/home/modules/shell.nix
deleted file mode 100644
index 088fe9238c..0000000000
--- a/users/grfn/system/home/modules/shell.nix
+++ /dev/null
@@ -1,184 +0,0 @@
-{ config, lib, pkgs, ... }:
-let
-  shellAliases = rec {
-    # NixOS stuff
-    hms = "home-manager switch";
-    nor = "sudo nixos-rebuild switch";
-    nrs = nor;
-    nrb = "sudo nixos-rebuild boot";
-    ncg = "nix-collect-garbage";
-    vihome = "vim ~/.config/nixpkgs/home.nix && home-manager switch";
-    virc = "vim ~/code/system/home/modules/shell.nix && home-manager switch && source ~/.zshrc";
-    visystem = "sudo vim /etc/nixos/configuration.nix && sudo nixos-rebuild switch";
-
-    # Nix
-    ns = "nix-shell";
-    nb = "nix build -f .";
-    nbl = "nix build -f . --builders ''"; # nix build local
-    lwo = "lorri watch --once";
-
-    # Docker and friends
-    "dcu" = "docker-compose up";
-    "dcud" = "docker-compose up -d";
-    "dc" = "docker-compose";
-    "dcr" = "docker-compose restart";
-    "dclf" = "docker-compose logs -f";
-    "dck" = "docker";
-    "dockerclean" = "dockercleancontainers && dockercleanimages";
-    "dockercleanimages" = "docker images -a --no-trunc | grep none | awk '{print \$$3}' | xargs -L 1 -r docker rmi";
-    "dockercleancontainers" = "docker ps -a --no-trunc| grep 'Exit' | awk '{print \$$1}' | xargs -L 1 -r docker rm";
-
-    # Directories
-    stck = "dirs -v";
-    b= "cd ~1";
-    ".." = "cd ..";
-    "..." = "cd ../..";
-    "...." = "cd ../../..";
-    "....." = "cd ../../../..";
-
-    # Aliases from old config
-    "http" = "http --style solarized";
-    "grep" = "grep $GREP_OPTIONS";
-    "bak" = "~/bin/backup.sh";
-    "xmm" = "xmodmap ~/.Xmodmap";
-    "asdflkj" = "asdf";
-    "asdf" = "asdfghjkl";
-    "asdfghjkl" = "echo \"Having some trouble?\"";
-    "ift" = "sudo iftop -i wlp3s0";
-    "first" = "awk '{print \$$1}'";
-    "cmt" = "git log --oneline | fzf-tmux | awk '{print \$$1}'";
-    "workmon" = "xrandr --output DP-2 --pos 1440x900 --primary";
-    "vi" = "vim";
-    "adbdev" = "adb devices";
-    "adbcon" = "adb connect $GNEX_IP";
-    "mpalb" = "mpc search album";
-    "mpart" = "mpc search artist";
-    "mps" = "mpc search";
-    "mpa" = "mpc add";
-    "mpt" = "mpc toggle";
-    "mpl" = "mpc playlist";
-    "dsstore" = "find . -name '*.DS_Store' -type f -ls -delete";
-    "df" = "df -h";
-    "fs" = "stat -f '%z bytes'";
-    "ll" = "ls -al";
-    "la" = "ls -a";
-  };
-in {
-  home.packages = with pkgs; [
-    zsh
-    autojump
-    ntfy
-  ];
-
-  home.sessionVariables = {
-    EDITOR = "vim";
-    LS_COLORS = "no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.ogg=01;35:*.mp3=01;35:*.wav=01;35:";
-    BROWSER = "firefox";
-    BAT_THEME = "ansi-light";
-  };
-
-  programs.bash = {
-    enable = true;
-    inherit shellAliases;
-  };
-
-  programs.zsh = {
-    enable = true;
-    enableAutosuggestions = true;
-    autocd = true;
-
-    inherit shellAliases;
-
-    history = rec {
-      save = 100000;
-      size = save;
-    };
-
-    oh-my-zsh = {
-      enable = true;
-
-      plugins = [
-        "battery"
-        "colorize"
-        "command-not-found"
-        "github"
-        "gitignore"
-        "postgres"
-        "systemd"
-        "themes"
-        "vi-mode"
-      ];
-
-      custom = "${pkgs.stdenv.mkDerivation {
-        name = "oh-my-zsh-custom";
-        unpackPhase = ":";
-        installPhase = ''
-          mkdir -p $out/themes
-          mkdir -p $out/custom/plugins
-          ln -s ${./pure.zsh-theme} $out/themes/pure.zsh-theme
-        '';
-      }}";
-
-      theme = "pure";
-    };
-
-    plugins = [{
-      name = "pure-theme";
-      src = pkgs.fetchFromGitHub {
-        owner = "sindresorhus";
-        repo = "pure";
-        rev = "0a92b02dd4172f6c64fdc9b81fe6cd4bddb0a23b";
-        sha256 = "0l8jqhmmjn7p32hdjnv121xsjnqd2c0plhzgydv2yzrmqgyvx7cc";
-      };
-    }];
-
-    initExtraBeforeCompInit = ''
-      zstyle ':completion:*' completer _complete _ignored _correct _approximate
-      zstyle ':completion:*' matcher-list \'\' 'm:{[:lower:]}={[:upper:]} m:{[:lower:][:upper:]}={[:upper:][:lower:]} r:|[._- :]=** r:|=**' 'l:|=* r:|=*'
-      zstyle ':completion:*' max-errors 5
-      zstyle ':completion:*' use-cache yes
-      zstyle ':completion::complete:grunt::options:' expire 1
-      zstyle ':completion:*' prompt '%e errors'
-      # zstyle :compinstall filename '~/.zshrc'
-      autoload -Uz compinit
-    '';
-
-    initExtra = ''
-      source ${./zshrc}
-      source ${pkgs.fetchFromGitHub {
-        owner = "zsh-users";
-        repo = "zsh-syntax-highlighting";
-        rev = "7678a8a22780141617f809002eeccf054bf8f448";
-        sha256 = "0xh4fbd54kvwwpqvabk8lpw7m80phxdzrd75q3y874jw0xx1a9q6";
-      }}/zsh-syntax-highlighting.zsh
-      source ${pkgs.autojump}/share/autojump/autojump.zsh
-      source ${pkgs.fetchFromGitHub {
-        owner = "chisui";
-        repo = "zsh-nix-shell";
-        rev = "a65382a353eaee5a98f068c330947c032a1263bb";
-        sha256 = "0l41ac5b7p8yyjvpfp438kw7zl9dblrpd7icjg1v3ig3xy87zv0n";
-      }}/nix-shell.plugin.zsh
-
-      eval "$(${pkgs.ntfy}/bin/ntfy shell-integration)"
-
-      export RPS1=""
-      autoload -U promptinit; promptinit
-      prompt pure
-
-      if [[ "$TERM" == "dumb" ]]; then
-        unsetopt zle
-        unsetopt prompt_cr
-        unsetopt prompt_subst
-        unfunction precmd
-        unfunction preexec
-        export PS1='$ '
-      fi
-    '';
-  };
-
-  programs.fzf = {
-    enable = true;
-    enableBashIntegration = true;
-    enableZshIntegration = true;
-  };
-}
diff --git a/users/grfn/system/home/modules/tarsnap.nix b/users/grfn/system/home/modules/tarsnap.nix
deleted file mode 100644
index 4bff19910f..0000000000
--- a/users/grfn/system/home/modules/tarsnap.nix
+++ /dev/null
@@ -1,64 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  home.packages = with pkgs; [
-    tarsnap
-  ];
-
-  home.file.".tarsnaprc".text = ''
-  ### Recommended options
-
-  # Tarsnap cache directory
-  cachedir /home/grfn/.cache/tarsnap
-
-  # Tarsnap key file
-  keyfile /home/grfn/.private/tarsnap.key
-
-  # Don't archive files which have the nodump flag set.
-  nodump
-
-  # Print statistics when creating or deleting archives.
-  print-stats
-
-  # Create a checkpoint once per GB of uploaded data.
-  checkpoint-bytes 1G
-
-  ### Commonly useful options
-
-  # Use SI prefixes to make numbers printed by --print-stats more readable.
-  humanize-numbers
-
-  ### Other options, not applicable to most systems
-
-  # Aggressive network behaviour: Use multiple TCP connections when
-  # writing archives.  Use of this option is recommended only in
-  # cases where TCP congestion control is known to be the limiting
-  # factor in upload performance.
-  #aggressive-networking
-
-  # Exclude files and directories matching specified patterns.
-  # Only one file or directory per command; multiple "exclude"
-  # commands may be given.
-  #exclude
-
-  # Include only files and directories matching specified patterns.
-  # Only one file or directory per command; multiple "include"
-  # commands may be given.
-  #include
-
-  # Attempt to reduce tarsnap memory consumption.  This option
-  # will slow down the process of creating archives, but may help
-  # on systems where the average size of files being backed up is
-  # less than 1 MB.
-  #lowmem
-
-  # Try even harder to reduce tarsnap memory consumption.  This can
-  # significantly slow down tarsnap, but reduces its memory usage
-  # by an additional factor of 2 beyond what the lowmem option does.
-  #verylowmem
-
-  # Snapshot time.  Use this option if you are backing up files
-  # from a filesystem snapshot rather than from a "live" filesystem.
-  #snaptime <file>
-  '';
-}
diff --git a/users/grfn/system/home/platforms/linux.nix b/users/grfn/system/home/platforms/linux.nix
deleted file mode 100644
index 98223980e3..0000000000
--- a/users/grfn/system/home/platforms/linux.nix
+++ /dev/null
@@ -1,92 +0,0 @@
-{ config, pkgs, ... }:
-
-let
-
-  depot = config.lib.depot;
-
-in
-
-{
-  imports = [
-    ../modules/alacritty.nix
-    ../modules/alsi.nix
-    ../modules/development.nix
-    ../modules/emacs.nix
-    ../modules/email.nix
-    ../modules/firefox.nix
-    ../modules/games.nix
-    ../modules/obs.nix
-    ../modules/i3.nix
-    ../modules/shell.nix
-    ../modules/tarsnap.nix
-    ../modules/vim.nix
-  ];
-
-  xsession.enable = true;
-
-  home.packages = with pkgs; [
-    # Desktop stuff
-    arandr
-    firefox
-    feh
-    chromium
-    xclip
-    xorg.xev
-    picom
-    peek
-    signal-desktop
-    apvlv # pdf viewer
-    vlc
-    irssi
-    gnutls
-    pandoc
-    barrier
-    depot.tools.nsfv-setup
-    glimpse # fork of gimp
-
-    # System utilities
-    powertop
-    usbutils
-    pciutils
-    gdmap
-    lsof
-    tree
-    ncat
-    iftop
-
-    # Security
-    gnupg
-    keybase
-    openssl
-    yubikey-manager
-    yubikey-manager-qt
-
-    # Spotify...etc
-    spotify
-    playerctl
-  ];
-
-  services.redshift = {
-    enable = true;
-    provider = "geoclue2";
-  };
-
-  services.pasystray.enable = true;
-
-  services.gpg-agent = {
-    enable = true;
-  };
-
-  gtk = {
-    enable = true;
-    gtk3.bookmarks = [
-      "file:///home/grfn/code"
-    ];
-  };
-
-  programs.zsh.initExtra = ''
-    [[ ! $IN_NIX_SHELL ]] && alsi -l
-  '';
-
-  services.lorri.enable = true;
-}
diff --git a/users/grfn/system/system/default.nix b/users/grfn/system/system/default.nix
deleted file mode 100644
index 445d4ad243..0000000000
--- a/users/grfn/system/system/default.nix
+++ /dev/null
@@ -1,38 +0,0 @@
-args @ { depot, pkgs, ... }:
-
-rec {
-  mugwump = import ./machines/mugwump.nix;
-
-  mugwumpSystem = (depot.ops.nixos.nixosFor mugwump).system;
-
-  roswell = import ./machines/roswell.nix;
-
-  roswellSystem = (depot.ops.nixos.nixosFor ({ ... }: {
-    imports = [
-      ./machines/roswell.nix
-      "${pkgs.home-manager.src}/nixos"
-    ];
-
-    # Use the same nixpkgs as everything else
-    home-manager.useGlobalPkgs = true;
-
-    home-manager.users.grfn = { config, lib, ... }: {
-      imports = [ ../home/machines/roswell.nix ];
-      lib.depot = depot;
-    };
-  })).system;
-
-  yeren = import ./machines/yeren.nix;
-
-  yerenSystem = (depot.ops.nixos.nixosFor yeren).system;
-
-  iso = import ./iso.nix args;
-
-  meta.targets = [
-    "mugwumpSystem"
-    "roswellSystem"
-    "yerenSystem"
-
-    "iso"
-  ];
-}
diff --git a/users/grfn/system/system/iso.nix b/users/grfn/system/system/iso.nix
deleted file mode 100644
index 4adccebfb8..0000000000
--- a/users/grfn/system/system/iso.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-{ depot, lib, pkgs, ... }:
-
-let
-  configuration = { ... }: {
-    imports = [
-      "${pkgs.path}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix"
-      "${pkgs.path}/nixos/modules/installer/cd-dvd/channel.nix"
-    ];
-
-    networking.networkmanager.enable = true;
-    networking.useDHCP = false;
-    networking.firewall.enable = false;
-    networking.wireless.enable = lib.mkForce false;
-  };
-in (depot.third_party.nixos {
-  inherit configuration;
-}).config.system.build.isoImage
diff --git a/users/grfn/system/system/machines/mugwump.nix b/users/grfn/system/system/machines/mugwump.nix
deleted file mode 100644
index 7de6555878..0000000000
--- a/users/grfn/system/system/machines/mugwump.nix
+++ /dev/null
@@ -1,279 +0,0 @@
-{ config, lib, pkgs, modulesPath, depot, ... }:
-
-with lib;
-
-{
-  imports = [
-    ../modules/common.nix
-    (modulesPath + "/installer/scan/not-detected.nix")
-    "${depot.path}/ops/modules/prometheus-fail2ban-exporter.nix"
-    "${depot.path}/users/grfn/xanthous/server/module.nix"
-    "${depot.third_party.agenix.src}/modules/age.nix"
-    "${depot.path}/users/grfn/bbbg/module.nix"
-  ];
-
-  networking.hostName = "mugwump";
-
-  boot = {
-    loader.systemd-boot.enable = true;
-
-    kernelModules = [ "kvm-intel" ];
-    extraModulePackages = [ ];
-
-    initrd = {
-      availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
-      kernelModules = [
-        "uas" "usbcore" "usb_storage" "vfat" "nls_cp437" "nls_iso8859_1"
-      ];
-
-      postDeviceCommands = pkgs.lib.mkBefore ''
-        mkdir -m 0755 -p /key
-        sleep 2
-        mount -n -t vfat -o ro `findfs UUID=9048-A9D5` /key
-      '';
-
-      luks.devices."cryptroot" = {
-        device = "/dev/disk/by-uuid/803a9028-339c-4617-a213-4fe138161f6d";
-        keyFile = "/key/keyfile";
-        preLVM = false;
-      };
-    };
-  };
-
-  fileSystems = {
-    "/" = {
-      device = "/dev/mapper/cryptroot";
-      fsType = "btrfs";
-    };
-    "/boot" = {
-      device = "/dev/disk/by-uuid/7D74-0E4B";
-      fsType = "vfat";
-    };
-  };
-
-  networking.interfaces = {
-    enp0s25.useDHCP = false;
-    wlp2s0.useDHCP = false;
-  };
-
-  networking.firewall.enable = true;
-  networking.firewall.allowedTCPPorts = [ 22 80 443 ];
-
-  security.sudo.extraRules = [{
-    groups = ["wheel"];
-    commands = [{ command = "ALL"; options = ["NOPASSWD"]; }];
-  }];
-
-  nix.gc.dates = "monthly";
-
-  age.secrets = let
-    secret = name: depot.users.grfn.secrets."${name}.age";
-  in {
-    bbbg.file = secret "bbbg";
-    cloudflare.file = secret "cloudflare";
-    ddclient-password.file = secret "ddclient-password";
-
-    buildkite-ssh-key = {
-      file = secret "buildkite-ssh-key";
-      group = "keys";
-      mode = "0440";
-    };
-
-    buildkite-token = {
-      file = secret "buildkite-token";
-      group = "keys";
-      mode = "0440";
-    };
-  };
-
-  services.depot.auto-deploy = {
-    enable = true;
-    interval = "1d";
-  };
-
-  services.fail2ban = {
-    enable = true;
-    ignoreIP = [
-      "172.16.0.0/16"
-    ];
-  };
-
-  services.openssh = {
-    allowSFTP = false;
-    passwordAuthentication = false;
-    permitRootLogin = "no";
-  };
-
-  services.grafana = {
-    enable = true;
-    port = 3000;
-    domain = "metrics.gws.fyi";
-    rootUrl = "https://metrics.gws.fyi";
-    dataDir = "/var/lib/grafana";
-    analytics.reporting.enable = false;
-
-    provision = {
-      enable = true;
-      datasources = [{
-        name = "Prometheus";
-        type = "prometheus";
-        url = "http://localhost:9090";
-      }];
-    };
-  };
-
-  security.acme.defaults.email = "root@gws.fyi";
-  security.acme.acceptTerms = true;
-
-  services.nginx = {
-    enable = true;
-    statusPage = true;
-    recommendedGzipSettings = true;
-    recommendedOptimisation = true;
-    recommendedTlsSettings = true;
-
-    virtualHosts = {
-      "metrics.gws.fyi" = {
-        enableACME = true;
-        forceSSL = true;
-        locations."/" = {
-          proxyPass = "http://localhost:${toString config.services.grafana.port}";
-        };
-      };
-    };
-  };
-
-  services.ddclient = {
-    enable = true;
-    domains = [ "home.gws.fyi" ];
-    interval = "1d";
-    zone = "gws.fyi";
-    protocol = "cloudflare";
-    username = "root@gws.fyi";
-    passwordFile = "/run/agenix/ddclient-password";
-    quiet = true;
-  };
-
-  systemd.services.ddclient.serviceConfig.DynamicUser = lib.mkForce false;
-
-  security.acme.certs."metrics.gws.fyi" = {
-    dnsProvider = "cloudflare";
-    credentialsFile = "/run/agenix/cloudflare";
-    webroot = mkForce null;
-  };
-
-  services.prometheus = {
-    enable = true;
-    exporters = {
-      node = {
-        enable = true;
-        openFirewall = false;
-
-        enabledCollectors = [
-          "processes"
-          "systemd"
-          "tcpstat"
-          "wifi"
-        ];
-      };
-
-      nginx = {
-        enable = true;
-        openFirewall = true;
-        sslVerify = false;
-        constLabels = [ "host=mugwump" ];
-      };
-
-      blackbox = {
-        enable = true;
-        openFirewall = true;
-        configFile = pkgs.writeText "blackbox-exporter.yaml" (builtins.toJSON {
-          modules = {
-            https_2xx = {
-              prober = "http";
-              http = {
-                method = "GET";
-                fail_if_ssl = false;
-                fail_if_not_ssl = true;
-                preferred_ip_protocol = "ip4";
-              };
-            };
-          };
-        });
-      };
-    };
-
-    scrapeConfigs = [{
-      job_name = "node";
-      scrape_interval = "5s";
-      static_configs = [{
-        targets = ["localhost:${toString config.services.prometheus.exporters.node.port}"];
-      }];
-    } {
-      job_name = "nginx";
-      scrape_interval = "5s";
-      static_configs = [{
-        targets = ["localhost:${toString config.services.prometheus.exporters.nginx.port}"];
-      }];
-    } {
-      job_name = "xanthous_server";
-      scrape_interval = "1s";
-      static_configs = [{
-        targets = ["localhost:${toString config.services.xanthous-server.metricsPort}"];
-      }];
-    } {
-      job_name = "blackbox";
-      metrics_path = "/probe";
-      params.module = ["https_2xx"];
-      scrape_interval = "5s";
-      static_configs = [{
-        targets = [
-          "https://gws.fyi"
-          "https://windtunnel.ci"
-          "https://app.windtunnel.ci"
-          "https://metrics.gws.fyi"
-        ];
-      }];
-      relabel_configs = [{
-        source_labels = ["__address__"];
-        target_label = "__param_target";
-      } {
-        source_labels = ["__param_target"];
-        target_label = "instance";
-      } {
-        target_label = "__address__";
-        replacement = "localhost:${toString config.services.prometheus.exporters.blackbox.port}";
-      }];
-    }];
-  };
-
-  services.xanthous-server.enable = true;
-
-  services.bbbg.enable = true;
-  services.bbbg.domain = "staging.bbbg.gws.fyi";
-  services.bbbg.database.enable = true;
-  services.bbbg.proxy.enable = true;
-
-  virtualisation.docker.enable = true;
-
-  services.buildkite-agents = listToAttrs (map (n: rec {
-    name = "mugwump-${toString n}";
-    value = {
-      inherit name;
-      enable = true;
-      tokenPath = "/run/agenix/buildkite-agent-token";
-      privateSshKeyPath = "/run/agenix/buildkite-ssh-key";
-      runtimePackages = with pkgs; [
-        docker
-        nix
-        gnutar
-        gzip
-      ];
-    };
-  }) (range 1 1));
-
-  users.users."buildkite-agent-mugwump-1" = {
-    isSystemUser = true;
-    extraGroups = [ "docker" ];
-  };
-}
diff --git a/users/grfn/system/system/machines/roswell.nix b/users/grfn/system/system/machines/roswell.nix
deleted file mode 100644
index 6eb4a510b8..0000000000
--- a/users/grfn/system/system/machines/roswell.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-{ depot, config, lib, pkgs, modulesPath, ... }:
-
-{
-  imports = [
-    ../modules/common.nix
-    "${modulesPath}/installer/scan/not-detected.nix"
-    "${modulesPath}/virtualisation/amazon-image.nix"
-  ];
-
-  ec2.hvm = true;
-
-  networking.hostName = "roswell";
-
-  users.users.grfn.openssh.authorizedKeys.keys = [
-    depot.users.grfn.keys.main
-  ];
-}
diff --git a/users/grfn/system/system/machines/yeren.nix b/users/grfn/system/system/machines/yeren.nix
deleted file mode 100644
index 5bdb8eab0c..0000000000
--- a/users/grfn/system/system/machines/yeren.nix
+++ /dev/null
@@ -1,132 +0,0 @@
-{ depot, modulesPath, config, lib, pkgs, ... }:
-
-{
-  imports = [
-    (modulesPath + "/installer/scan/not-detected.nix")
-    ../modules/common.nix
-    ../modules/laptop.nix
-    ../modules/xserver.nix
-    ../modules/fonts.nix
-    ../modules/sound.nix
-    ../modules/tvl.nix
-    ../modules/development.nix
-    ../modules/work/kolide.nix
-  ];
-
-  networking.hostName = "yeren";
-
-  system.stateVersion = "21.03";
-
-  boot = {
-    initrd = {
-      availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
-      kernelModules = [ ];
-
-      luks.devices = {
-        "cryptroot".device = "/dev/disk/by-uuid/dcfbc22d-e0d2-411b-8dd3-96704d3aae2e";
-      };
-    };
-
-    kernelPackages = pkgs.linuxPackages_5_15;
-
-    kernelModules = [ "kvm-intel" ];
-    blacklistedKernelModules = [ "psmouse" ];
-    extraModulePackages = [
-      config.boot.kernelPackages.digimend
-    ];
-    kernelParams = [
-      "i915.preliminary_hw_support=1"
-      "pcie_aspm=force"
-    ];
-
-    # https://bbs.archlinux.org/viewtopic.php?pid=1933643#p1933643
-    extraModprobeConfig = ''
-      options snd-intel-dspcfg dsp_driver=1
-    '';
-
-    kernel.sysctl = {
-      "kernel.perf_event_paranoid" = -1;
-    };
-  };
-
-  fileSystems = {
-    "/" = {
-      device = "/dev/mapper/cryptroot";
-      fsType = "btrfs";
-    };
-
-    "/boot" = {
-      device = "/dev/disk/by-uuid/53A9-248B";
-      fsType = "vfat";
-    };
-  };
-
-  swapDevices = [{
-    device = "/dev/disk/by-uuid/b627cb0e-0451-4f25-94d0-6497e01f0da4";
-  }];
-
-  services.xserver = {
-    exportConfiguration = true;
-    extraConfig = ''
-      Section "Device"
-        Identifier  "Intel Graphics"
-        Driver      "intel"
-        Option      "TripleBuffer" "true"
-        Option      "TearFree"     "true"
-        Option      "DRI"          "true"
-        Option      "AccelMethod"  "sna"
-      EndSection
-    '';
-  };
-
-  hardware.firmware = with pkgs; [
-    alsa-firmware
-    sof-firmware
-  ];
-
-  hardware.opengl.extraPackages = with pkgs; [
-    vaapiIntel
-    vaapiVdpau
-    libvdpau-va-gl
-    intel-media-driver
-  ];
-
-  # Disabled for now until libfprint-tod can get a version bump
-  # services.fprintd = {
-  #   enable = true;
-  #   package = pkgs.fprintd-tod;
-  # };
-
-  systemd.services.fprintd.environment.FP_TOD_DRIVERS_DIR =
-    "${pkgs.libfprint-2-tod1-goodix}/usr/lib/libfprint-2/tod-1";
-
-  security.pam.loginLimits = [
-    {
-      domain = "grfn";
-      type = "soft";
-      item = "nofile";
-      value = "65535";
-    }
-  ];
-
-  security.pam.services = {
-    login.fprintAuth = true;
-    sudo.fprintAuth = true;
-    i3lock.fprintAuth = false;
-    i3lock-color.fprintAuth = false;
-    lightdm.fprintAuth = true;
-    lightdm-greeter.fprintAuth = true;
-  };
-
-  hardware.opengl.driSupport32Bit = true;
-
-  hardware.pulseaudio.extraConfig = ''
-    load-module module-remap-source source_name=KompleteAudio6_1 source_properties=device.description=KompleteAudio6Input1 master=alsa_input.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.multichannel-input remix=no channels=1 master_channel_map=front-left channel_map=mono
-    load-module module-remap-source source_name=KompleteAudio6_2 source_properties=device.description=KompleteAudio6Input2 master=alsa_input.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.multichannel-input remix=no channels=1 master_channel_map=front-right channel_map=mono
-    load-module module-remap-sink sink_name=KompleteAudio6_12 sink_properties=device.description=KompleteAudio6_12 remix=no master=alsa_output.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.analog-surround-21 channels=2 master_channel_map=front-left,front-right channel_map=front-left,front-right
-  '';
-
-  services.fwupd.enable = true;
-
-  services.tailscale.enable = true;
-}
diff --git a/users/grfn/system/system/modules/common.nix b/users/grfn/system/system/modules/common.nix
deleted file mode 100644
index 91723973f5..0000000000
--- a/users/grfn/system/system/modules/common.nix
+++ /dev/null
@@ -1,87 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-
-  depot = import ../../../../.. {};
-
-in
-
-with lib;
-
-{
-  boot.loader.systemd-boot.enable = true;
-  boot.loader.efi.canTouchEfiVariables = true;
-
-  networking.useDHCP = false;
-  networking.networkmanager.enable = true;
-
-  i18n = {
-    defaultLocale = "en_US.UTF-8";
-  };
-
-  time.timeZone = lib.mkDefault "America/New_York";
-
-  environment.systemPackages = with pkgs; [
-    wget
-    vim
-    zsh
-    git
-    w3m
-    libnotify
-    file
-    lm_sensors
-    dnsutils
-    htop
-    man-pages
-    man-pages-posix
-  ];
-
-  documentation.dev.enable = true;
-  documentation.man.generateCaches = true;
-
-  services.openssh.enable = true;
-
-  programs.ssh.startAgent = true;
-
-  networking.firewall.enable = mkDefault false;
-
-  users.mutableUsers = true;
-  programs.zsh.enable = true;
-  environment.pathsToLink = [ "/share/zsh" ];
-  users.users.grfn = {
-    isNormalUser = true;
-    initialPassword = "password";
-    extraGroups = [
-      "wheel"
-      "networkmanager"
-      "audio"
-      "docker"
-    ];
-    shell = pkgs.zsh;
-  };
-
-  nix = {
-    trustedUsers = [ "grfn" ];
-    distributedBuilds = true;
-
-    gc = {
-      automatic = true;
-      dates = mkDefault "weekly";
-      options = "--delete-older-than 30d";
-    };
-  };
-
-  services.udev.packages = with pkgs; [
-    yubikey-personalization
-  ];
-
-  services.pcscd.enable = true;
-
-  services.udev.extraRules = ''
-    # UDEV rules for Teensy USB devices
-    ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
-    ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
-    SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", MODE:="0666"
-    KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", MODE:="0666"
-  '';
-}
diff --git a/users/grfn/system/system/modules/desktop.nix b/users/grfn/system/system/modules/desktop.nix
deleted file mode 100644
index 3adbd9d9b0..0000000000
--- a/users/grfn/system/system/modules/desktop.nix
+++ /dev/null
@@ -1,19 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  imports = [
-    ./xserver.nix
-    ./fonts.nix
-    ./sound.nix
-    ./kernel.nix
-  ];
-
-  programs.nm-applet.enable = true;
-
-  users.users.grfn.extraGroups = [
-    "audio"
-    "video"
-  ];
-
-  services.geoclue2.enable = true;
-}
diff --git a/users/grfn/system/system/modules/development.nix b/users/grfn/system/system/modules/development.nix
deleted file mode 100644
index bfa0e22cff..0000000000
--- a/users/grfn/system/system/modules/development.nix
+++ /dev/null
@@ -1,6 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  virtualisation.docker.enable = true;
-  users.users.grfn.extraGroups = [ "docker" ];
-}
diff --git a/users/grfn/system/system/modules/fonts.nix b/users/grfn/system/system/modules/fonts.nix
deleted file mode 100644
index babe30d427..0000000000
--- a/users/grfn/system/system/modules/fonts.nix
+++ /dev/null
@@ -1,12 +0,0 @@
-{ config, lib, pkgs, ... }:
-{
-  fonts = {
-    fonts = with pkgs; [
-      nerdfonts
-      noto-fonts-emoji
-      twitter-color-emoji
-    ];
-
-    fontconfig.defaultFonts.emoji = ["Twitter Color Emoji"];
-  };
-}
diff --git a/users/grfn/system/system/modules/laptop.nix b/users/grfn/system/system/modules/laptop.nix
deleted file mode 100644
index 05c5333e51..0000000000
--- a/users/grfn/system/system/modules/laptop.nix
+++ /dev/null
@@ -1,15 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  imports = [
-    ./reusable/battery.nix
-  ];
-
-  laptop.onLowBattery.enable = true;
-
-  services.logind.extraConfig = ''
-    HandlePowerKey=hibernate
-  '';
-
-  services.tlp.enable = true;
-}
diff --git a/users/grfn/system/system/modules/reusable/battery.nix b/users/grfn/system/system/modules/reusable/battery.nix
deleted file mode 100644
index ca92e0c3f6..0000000000
--- a/users/grfn/system/system/modules/reusable/battery.nix
+++ /dev/null
@@ -1,32 +0,0 @@
-{ config, lib, pkgs, ... }:
-with lib;
-{
-  options = {
-    laptop.onLowBattery = {
-      enable = mkEnableOption "Perform action on low battery";
-
-      thresholdPercentage = mkOption {
-        description = "Threshold battery percentage on which to perform the action";
-        default = 8;
-        type = types.int;
-      };
-
-      action = mkOption {
-        description = "Action to perform on low battery";
-        default = "hibernate";
-        type = types.enum [ "hibernate" "suspend" "suspend-then-hibernate" ];
-      };
-    };
-  };
-
-  config =
-    let cfg = config.laptop.onLowBattery;
-    in mkIf cfg.enable {
-    services.udev.extraRules = concatStrings [
-      ''SUBSYSTEM=="power_supply", ''
-      ''ATTR{status}=="Discharging", ''
-      ''ATTR{capacity}=="[0-${toString cfg.thresholdPercentage}]", ''
-      ''RUN+="${pkgs.systemd}/bin/systemctl ${cfg.action}"''
-    ];
-  };
-}
diff --git a/users/grfn/system/system/modules/tvl.nix b/users/grfn/system/system/modules/tvl.nix
deleted file mode 100644
index 905ec8ced5..0000000000
--- a/users/grfn/system/system/modules/tvl.nix
+++ /dev/null
@@ -1,37 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  nix = {
-    buildMachines = [{
-      hostName = "whitby.tvl.fyi";
-      sshUser = "grfn";
-      sshKey = "/root/.ssh/id_rsa";
-      system = "x86_64-linux";
-      maxJobs = 64;
-      supportedFeatures = ["big-parallel" "kvm" "nixos-test" "benchmark"];
-    }];
-
-    extraOptions = ''
-      builders-use-substitutes = true
-    '';
-
-    binaryCaches = [
-      "https://cache.nixos.org"
-      "ssh://nix-ssh@whitby.tvl.fyi"
-    ];
-    trustedBinaryCaches = [
-      "https://cache.nixos.org"
-      "ssh://nix-ssh@whitby.tvl.fyi"
-    ];
-    binaryCachePublicKeys = [
-      "cache.tvl.fyi:fd+9d1ceCPvDX/xVhcfv8nAa6njEhAGAEe+oGJDEeoc="
-    ];
-  };
-
-  programs.ssh.knownHosts.whitby = {
-    hostNames = [ "whitby" "whitby.tvl.fyi" "49.12.129.211"];
-    publicKeyFile = pkgs.writeText "whitby.pub" ''
-      ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILNh/w4BSKov0jdz3gKBc98tpoLta5bb87fQXWBhAl2I
-    '';
-  };
-}
diff --git a/users/grfn/system/system/modules/work/kolide.deb b/users/grfn/system/system/modules/work/kolide.deb
deleted file mode 100644
index a319a5806f..0000000000
--- a/users/grfn/system/system/modules/work/kolide.deb
+++ /dev/null
Binary files differdiff --git a/users/grfn/system/system/modules/work/kolide.nix b/users/grfn/system/system/modules/work/kolide.nix
deleted file mode 100644
index 29ee0a0d7c..0000000000
--- a/users/grfn/system/system/modules/work/kolide.nix
+++ /dev/null
@@ -1,49 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-  deb = ./kolide.deb;
-
-  kolide = pkgs.runCommand "kolide-data" {
-    buildInputs = [ pkgs.binutils-unwrapped ];
-  } ''
-    cp ${deb} ./kolide.deb
-    ar x kolide.deb
-    mkdir result
-    tar xzf data.tar.gz -C result
-    patchelf \
-      --set-interpreter ${pkgs.glibc}/lib/ld-linux-x86-64.so.2 \
-      --set-rpath "${lib.makeLibraryPath (with pkgs; [
-        zlib
-      ])}" \
-      result/usr/local/kolide-k2/bin/osqueryd
-    mv result $out
-  '';
-
-in {
-  systemd.services."launcher.kolide-k2" = {
-    wantedBy = [ "multi-user.target" ];
-    after = [ "network.target" "syslog.service" ];
-    description = "The Kolide Launcher";
-    serviceConfig = {
-      ExecStart = ''
-        ${kolide}/usr/local/kolide-k2/bin/launcher \
-          -config \
-          ${pkgs.writeText "launcher.flags" ''
-            with_initial_runner
-            control
-            autoupdate
-            root_directory /var/lib/kolide
-            osqueryd_path ${kolide}/usr/local/kolide-k2/bin/osqueryd
-            enroll_secret_path ${kolide}/etc/kolide-k2/secret
-            control_hostname k2control.kolide.com
-            update_channel stable
-            transport jsonrpc
-            hostname k2device.kolide.com
-          ''}
-      '';
-      StateDirectory = "kolide";
-      Restart = "on-failure";
-      RestartSec = 3;
-    };
-  };
-}
diff --git a/users/grfn/terraform/globals.nix b/users/grfn/terraform/globals.nix
deleted file mode 100644
index 5f373c6646..0000000000
--- a/users/grfn/terraform/globals.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-{ pkgs, ... }:
-
-{
-  provider.aws = map (region: {
-    inherit region;
-    alias = region;
-    profile = "personal";
-  }) [
-    "us-east-1"
-    "us-east-2"
-    "us-west-2"
-  ];
-
-  data.external.cloudflare_api_key = {
-    program = [(pkgs.writeShellScript "cloudflare_api_key" ''
-      jq -n --arg api_key "$(pass cloudflare-api-key)" '{"api_key":$api_key}'
-    '')];
-  };
-
-  provider.cloudflare = {
-    email = "root@gws.fyi";
-    api_key = "\${data.external.cloudflare_api_key.result.api_key}";
-  };
-}
diff --git a/users/grfn/xanthous/.envrc b/users/grfn/xanthous/.envrc
deleted file mode 100644
index be81feddb1..0000000000
--- a/users/grfn/xanthous/.envrc
+++ /dev/null
@@ -1 +0,0 @@
-eval "$(lorri direnv)"
\ No newline at end of file
diff --git a/users/grfn/xanthous/build/generic-arbitrary-export-garbitrary.patch b/users/grfn/xanthous/build/generic-arbitrary-export-garbitrary.patch
deleted file mode 100644
index f0c936bfca..0000000000
--- a/users/grfn/xanthous/build/generic-arbitrary-export-garbitrary.patch
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git a/src/Test/QuickCheck/Arbitrary/Generic.hs b/src/Test/QuickCheck/Arbitrary/Generic.hs
-index fed6ab3..91f59f1 100644
---- a/src/Test/QuickCheck/Arbitrary/Generic.hs
-+++ b/src/Test/QuickCheck/Arbitrary/Generic.hs
-@@ -23,6 +23,7 @@ The generated 'arbitrary' method is equivalent to
- 
- module Test.QuickCheck.Arbitrary.Generic
-   ( Arbitrary(..)
-+  , GArbitrary
-   , genericArbitrary
-   , genericShrink
-   ) where
diff --git a/users/grfn/xanthous/default.nix b/users/grfn/xanthous/default.nix
deleted file mode 100644
index c0eca446c9..0000000000
--- a/users/grfn/xanthous/default.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-{ depot ? (import ../../../. {})
-, pkgs ? depot.third_party.nixpkgs
-, ... }:
-
-let
-  ignore = depot.third_party.gitignoreSource.gitignoreFilter ./.;
-  src = builtins.path {
-    name = "xanthous-source";
-    path = ./.;
-    filter = path: type:
-      !(type == "directory" && builtins.baseNameOf path == "server")
-      && !(type == "directory" && builtins.baseNameOf path == "docs")
-      && (ignore path type
-          || builtins.baseNameOf path == "package.yaml");
-  };
-  # generated by cabal2nix
-  basePkg = pkgs.haskellPackages.callPackage ./pkg.nix { };
-in
-
-pkgs.haskell.lib.overrideCabal basePkg (default: {
-  inherit src;
-  version = "canon";
-  configureFlags = [
-    "--ghc-option=-Wall --ghc-option=-Werror"
-  ] ++ (default.configureFlags or []);
-})
diff --git a/users/grfn/xanthous/pkg.nix b/users/grfn/xanthous/pkg.nix
deleted file mode 100644
index 0f0dbfc982..0000000000
--- a/users/grfn/xanthous/pkg.nix
+++ /dev/null
@@ -1,80 +0,0 @@
-{ mkDerivation, aeson, array, async, base, bifunctors, brick
-, checkers, classy-prelude, comonad, comonad-extras, constraints
-, containers, criterion, data-default, data-interval, deepseq
-, directory, fgl, fgl-arbitrary, file-embed, filepath
-, generic-arbitrary, generic-lens, groups, hgeometry
-, hgeometry-combinatorial, hpack, JuicyPixels, lens
-, lens-properties, lib, lifted-async, linear, megaparsec, mmorph
-, monad-control, MonadRandom, mtl, optparse-applicative, parallel
-, parser-combinators, pointed, QuickCheck, quickcheck-instances
-, quickcheck-text, random, random-extras, random-fu, random-source
-, Rasterific, raw-strings-qq, reflection, semigroupoids, semigroups
-, splitmix, stache, streams, tasty, tasty-hunit, tasty-quickcheck
-, tasty-rerun, text, text-zipper, tomland, transformers, vector
-, vty, witherable, yaml, zlib
-}:
-mkDerivation {
-  pname = "xanthous";
-  version = "0.1.0.0";
-  src = ./.;
-  isLibrary = true;
-  isExecutable = true;
-  libraryHaskellDepends = [
-    aeson array async base bifunctors brick checkers classy-prelude
-    comonad comonad-extras constraints containers criterion
-    data-default data-interval deepseq directory fgl fgl-arbitrary
-    file-embed filepath generic-arbitrary generic-lens groups hgeometry
-    hgeometry-combinatorial JuicyPixels lens lifted-async linear
-    megaparsec mmorph monad-control MonadRandom mtl
-    optparse-applicative parallel parser-combinators pointed QuickCheck
-    quickcheck-instances quickcheck-text random random-extras random-fu
-    random-source Rasterific raw-strings-qq reflection semigroupoids
-    semigroups splitmix stache streams text text-zipper tomland
-    transformers vector vty witherable yaml zlib
-  ];
-  libraryToolDepends = [ hpack ];
-  executableHaskellDepends = [
-    aeson array async base bifunctors brick checkers classy-prelude
-    comonad comonad-extras constraints containers criterion
-    data-default data-interval deepseq directory fgl fgl-arbitrary
-    file-embed filepath generic-arbitrary generic-lens groups hgeometry
-    hgeometry-combinatorial JuicyPixels lens lifted-async linear
-    megaparsec mmorph monad-control MonadRandom mtl
-    optparse-applicative parallel parser-combinators pointed QuickCheck
-    quickcheck-instances quickcheck-text random random-extras random-fu
-    random-source Rasterific raw-strings-qq reflection semigroupoids
-    semigroups splitmix stache streams text text-zipper tomland
-    transformers vector vty witherable yaml zlib
-  ];
-  testHaskellDepends = [
-    aeson array async base bifunctors brick checkers classy-prelude
-    comonad comonad-extras constraints containers criterion
-    data-default data-interval deepseq directory fgl fgl-arbitrary
-    file-embed filepath generic-arbitrary generic-lens groups hgeometry
-    hgeometry-combinatorial JuicyPixels lens lens-properties
-    lifted-async linear megaparsec mmorph monad-control MonadRandom mtl
-    optparse-applicative parallel parser-combinators pointed QuickCheck
-    quickcheck-instances quickcheck-text random random-extras random-fu
-    random-source Rasterific raw-strings-qq reflection semigroupoids
-    semigroups splitmix stache streams tasty tasty-hunit
-    tasty-quickcheck tasty-rerun text text-zipper tomland transformers
-    vector vty witherable yaml zlib
-  ];
-  benchmarkHaskellDepends = [
-    aeson array async base bifunctors brick checkers classy-prelude
-    comonad comonad-extras constraints containers criterion
-    data-default data-interval deepseq directory fgl fgl-arbitrary
-    file-embed filepath generic-arbitrary generic-lens groups hgeometry
-    hgeometry-combinatorial JuicyPixels lens lifted-async linear
-    megaparsec mmorph monad-control MonadRandom mtl
-    optparse-applicative parallel parser-combinators pointed QuickCheck
-    quickcheck-instances quickcheck-text random random-extras random-fu
-    random-source Rasterific raw-strings-qq reflection semigroupoids
-    semigroups splitmix stache streams text text-zipper tomland
-    transformers vector vty witherable yaml zlib
-  ];
-  prePatch = "hpack";
-  homepage = "https://github.com/glittershark/xanthous#readme";
-  description = "A WIP TUI RPG";
-  license = lib.licenses.gpl3Only;
-}
diff --git a/users/grfn/xanthous/server/.envrc b/users/grfn/xanthous/server/.envrc
deleted file mode 100644
index 051d09d292..0000000000
--- a/users/grfn/xanthous/server/.envrc
+++ /dev/null
@@ -1 +0,0 @@
-eval "$(lorri direnv)"
diff --git a/users/grfn/xanthous/server/Cargo.lock b/users/grfn/xanthous/server/Cargo.lock
deleted file mode 100644
index 46488d4575..0000000000
--- a/users/grfn/xanthous/server/Cargo.lock
+++ /dev/null
@@ -1,1937 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "addr2line"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-
-[[package]]
-name = "aes"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
-dependencies = [
- "cfg-if 1.0.0",
- "cipher",
- "cpufeatures",
- "ctr",
- "opaque-debug",
-]
-
-[[package]]
-name = "ahash"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
-dependencies = [
- "getrandom",
- "once_cell",
- "version_check",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "0.7.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "atomic-shim"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d20fdac7156779a1a30d970e838195558b4810dd06aa69e7c7461bdc518edf9b"
-dependencies = [
- "crossbeam",
-]
-
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi",
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
-
-[[package]]
-name = "backtrace"
-version = "0.3.63"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6"
-dependencies = [
- "addr2line",
- "cc",
- "cfg-if 1.0.0",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
-]
-
-[[package]]
-name = "base64ct"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6b4d9b1225d28d360ec6a231d65af1fd99a2a095154c8040689617290569c5c"
-
-[[package]]
-name = "bcrypt-pbkdf"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c38c03b9506bd92bf1ef50665a81eda156f615438f7654bffba58907e6149d7"
-dependencies = [
- "blowfish",
- "crypto-mac",
- "pbkdf2",
- "sha2",
- "zeroize",
-]
-
-[[package]]
-name = "bit-vec"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "block-buffer"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "block-modes"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e"
-dependencies = [
- "block-padding",
- "cipher",
-]
-
-[[package]]
-name = "block-padding"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
-
-[[package]]
-name = "blowfish"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe3ff3fc1de48c1ac2e3341c4df38b0d1bfb8fdf04632a187c8b75aaa319a7ab"
-dependencies = [
- "byteorder",
- "cipher",
- "opaque-debug",
-]
-
-[[package]]
-name = "bumpalo"
-version = "3.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
-
-[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
-
-[[package]]
-name = "bytes"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
-
-[[package]]
-name = "cc"
-version = "1.0.71"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
-
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "chrono"
-version = "0.4.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
-dependencies = [
- "libc",
- "num-integer",
- "num-traits",
- "winapi",
-]
-
-[[package]]
-name = "cipher"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "clap"
-version = "3.0.0-beta.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "feff3878564edb93745d58cf63e17b63f24142506e7a20c87a5521ed7bfb1d63"
-dependencies = [
- "atty",
- "bitflags",
- "clap_derive",
- "indexmap",
- "lazy_static",
- "os_str_bytes",
- "strsim",
- "termcolor",
- "textwrap",
- "unicase",
-]
-
-[[package]]
-name = "clap_derive"
-version = "3.0.0-beta.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b15c6b4f786ffb6192ffe65a36855bc1fc2444bcd0945ae16748dcd6ed7d0d3"
-dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "color-eyre"
-version = "0.5.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7"
-dependencies = [
- "backtrace",
- "color-spantrace",
- "eyre",
- "indenter",
- "once_cell",
- "owo-colors",
- "tracing-error",
-]
-
-[[package]]
-name = "color-spantrace"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1"
-dependencies = [
- "once_cell",
- "owo-colors",
- "tracing-core",
- "tracing-error",
-]
-
-[[package]]
-name = "cpufeatures"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "crc32fast"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
-dependencies = [
- "cfg-if 1.0.0",
-]
-
-[[package]]
-name = "crossbeam"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
-dependencies = [
- "cfg-if 0.1.10",
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-epoch 0.8.2",
- "crossbeam-queue",
- "crossbeam-utils 0.7.2",
-]
-
-[[package]]
-name = "crossbeam-channel"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
-dependencies = [
- "crossbeam-utils 0.7.2",
- "maybe-uninit",
-]
-
-[[package]]
-name = "crossbeam-deque"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed"
-dependencies = [
- "crossbeam-epoch 0.8.2",
- "crossbeam-utils 0.7.2",
- "maybe-uninit",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
-dependencies = [
- "autocfg",
- "cfg-if 0.1.10",
- "crossbeam-utils 0.7.2",
- "lazy_static",
- "maybe-uninit",
- "memoffset 0.5.6",
- "scopeguard",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
-dependencies = [
- "cfg-if 1.0.0",
- "crossbeam-utils 0.8.5",
- "lazy_static",
- "memoffset 0.6.4",
- "scopeguard",
-]
-
-[[package]]
-name = "crossbeam-queue"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
-dependencies = [
- "cfg-if 0.1.10",
- "crossbeam-utils 0.7.2",
- "maybe-uninit",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
-dependencies = [
- "autocfg",
- "cfg-if 0.1.10",
- "lazy_static",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
-dependencies = [
- "cfg-if 1.0.0",
- "lazy_static",
-]
-
-[[package]]
-name = "crypto-mac"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
-dependencies = [
- "generic-array",
- "subtle",
-]
-
-[[package]]
-name = "cryptovec"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccc7fa13a6bbb2322d325292c57f4c8e7291595506f8289968a0eb61c3130bdf"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "ctr"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
-dependencies = [
- "cipher",
-]
-
-[[package]]
-name = "dashmap"
-version = "4.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
-dependencies = [
- "cfg-if 1.0.0",
- "num_cpus",
-]
-
-[[package]]
-name = "data-encoding"
-version = "2.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
-
-[[package]]
-name = "digest"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "dirs"
-version = "3.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
-dependencies = [
- "dirs-sys",
-]
-
-[[package]]
-name = "dirs-sys"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
-dependencies = [
- "libc",
- "redox_users",
- "winapi",
-]
-
-[[package]]
-name = "endian-type"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
-
-[[package]]
-name = "eyre"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b"
-dependencies = [
- "indenter",
- "once_cell",
-]
-
-[[package]]
-name = "flate2"
-version = "1.0.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
-dependencies = [
- "cfg-if 1.0.0",
- "crc32fast",
- "libc",
- "miniz_oxide",
-]
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "futures"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
-
-[[package]]
-name = "futures-executor"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c"
-dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-io"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
-
-[[package]]
-name = "futures-macro"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
-dependencies = [
- "autocfg",
- "proc-macro-hack",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "futures-sink"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11"
-
-[[package]]
-name = "futures-task"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
-
-[[package]]
-name = "futures-util"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
-dependencies = [
- "autocfg",
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-macro",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "proc-macro-hack",
- "proc-macro-nested",
- "slab",
-]
-
-[[package]]
-name = "generic-array"
-version = "0.14.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
-dependencies = [
- "typenum",
- "version_check",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "gimli"
-version = "0.26.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
-
-[[package]]
-name = "hashbrown"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
-dependencies = [
- "ahash",
-]
-
-[[package]]
-name = "heck"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hmac"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
-dependencies = [
- "crypto-mac",
- "digest",
-]
-
-[[package]]
-name = "http"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
-dependencies = [
- "bytes",
- "http",
- "pin-project-lite",
-]
-
-[[package]]
-name = "httparse"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
-
-[[package]]
-name = "httpdate"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440"
-
-[[package]]
-name = "hyper"
-version = "0.14.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b91bb1f221b6ea1f1e4371216b70f40748774c2fb5971b450c07773fb92d26b"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-core",
- "futures-util",
- "http",
- "http-body",
- "httparse",
- "httpdate",
- "itoa",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
- "want",
-]
-
-[[package]]
-name = "indenter"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
-
-[[package]]
-name = "indexmap"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
-dependencies = [
- "autocfg",
- "hashbrown",
-]
-
-[[package]]
-name = "instant"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
-dependencies = [
- "cfg-if 1.0.0",
-]
-
-[[package]]
-name = "ipnet"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
-
-[[package]]
-name = "itoa"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
-
-[[package]]
-name = "js-sys"
-version = "0.3.55"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
-dependencies = [
- "wasm-bindgen",
-]
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
-name = "libc"
-version = "0.2.107"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
-
-[[package]]
-name = "libsodium-sys"
-version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd"
-dependencies = [
- "cc",
- "libc",
- "pkg-config",
- "walkdir",
-]
-
-[[package]]
-name = "lock_api"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
-dependencies = [
- "scopeguard",
-]
-
-[[package]]
-name = "log"
-version = "0.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
-dependencies = [
- "cfg-if 1.0.0",
-]
-
-[[package]]
-name = "mach"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "matchers"
-version = "0.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
-dependencies = [
- "regex-automata",
-]
-
-[[package]]
-name = "maybe-uninit"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
-
-[[package]]
-name = "md5"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
-
-[[package]]
-name = "memchr"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
-
-[[package]]
-name = "memoffset"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "memoffset"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "metrics"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a00f42f354a2ed4894db863b3a4db47aef2d2e4435b937221749bd37a8a7aaa8"
-dependencies = [
- "ahash",
- "metrics-macros",
- "proc-macro-hack",
-]
-
-[[package]]
-name = "metrics-exporter-prometheus"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "343a5ceb38235928e7a5687412590f07e6d281522dcd9ff51246f8856eef5fe5"
-dependencies = [
- "hyper",
- "ipnet",
- "metrics",
- "metrics-util",
- "parking_lot",
- "quanta",
- "thiserror",
- "tokio",
-]
-
-[[package]]
-name = "metrics-macros"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caa72e4a3d157986dd2565c82ecbddcc23941513669a3766b938f6b72eb87f3f"
-dependencies = [
- "lazy_static",
- "proc-macro-hack",
- "proc-macro2",
- "quote",
- "regex",
- "syn",
-]
-
-[[package]]
-name = "metrics-util"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74c9b6aee519e1461b678952d3671652bb341d0664b1188f895a436a4e2e6ffa"
-dependencies = [
- "ahash",
- "aho-corasick",
- "atomic-shim",
- "crossbeam-epoch 0.9.5",
- "crossbeam-utils 0.8.5",
- "dashmap",
- "hashbrown",
- "indexmap",
- "metrics",
- "num_cpus",
- "ordered-float",
- "parking_lot",
- "quanta",
- "radix_trie",
- "sketches-ddsketch",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
-dependencies = [
- "adler",
- "autocfg",
-]
-
-[[package]]
-name = "mio"
-version = "0.7.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
-dependencies = [
- "libc",
- "log",
- "miow",
- "ntapi",
- "winapi",
-]
-
-[[package]]
-name = "miow"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "nibble_vec"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
-dependencies = [
- "smallvec",
-]
-
-[[package]]
-name = "nix"
-version = "0.23.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
-dependencies = [
- "bitflags",
- "cc",
- "cfg-if 1.0.0",
- "libc",
- "memoffset 0.6.4",
-]
-
-[[package]]
-name = "ntapi"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "num-bigint"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
-dependencies = [
- "autocfg",
- "num-traits",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "num_cpus"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
-[[package]]
-name = "object"
-version = "0.27.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
-
-[[package]]
-name = "opaque-debug"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
-
-[[package]]
-name = "ordered-float"
-version = "2.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97c9d06878b3a851e8026ef94bf7fef9ba93062cd412601da4d9cf369b1cc62d"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "os_str_bytes"
-version = "4.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "addaa943333a514159c80c97ff4a93306530d965d27e139188283cd13e06a799"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "owo-colors"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
-
-[[package]]
-name = "parking_lot"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
-dependencies = [
- "instant",
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
-dependencies = [
- "cfg-if 1.0.0",
- "instant",
- "libc",
- "redox_syscall",
- "smallvec",
- "winapi",
-]
-
-[[package]]
-name = "password-hash"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7"
-dependencies = [
- "base64ct",
- "rand_core",
- "subtle",
-]
-
-[[package]]
-name = "pbkdf2"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa"
-dependencies = [
- "base64ct",
- "crypto-mac",
- "hmac",
- "password-hash",
- "sha2",
-]
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
-
-[[package]]
-name = "pin-utils"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
-
-[[package]]
-name = "pkg-config"
-version = "0.3.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
-
-[[package]]
-name = "proc-macro-error"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-hack"
-version = "0.5.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
-
-[[package]]
-name = "proc-macro-nested"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
-dependencies = [
- "unicode-xid",
-]
-
-[[package]]
-name = "quanta"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8"
-dependencies = [
- "crossbeam-utils 0.8.5",
- "libc",
- "mach",
- "once_cell",
- "raw-cpuid",
- "wasi",
- "web-sys",
- "winapi",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "radix_trie"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
-dependencies = [
- "endian-type",
- "nibble_vec",
-]
-
-[[package]]
-name = "rand"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
- "rand_hc",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "raw-cpuid"
-version = "10.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "929f54e29691d4e6a9cc558479de70db7aa3d98cd6fe7ab86d7507aa2886b9d2"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "redox_users"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
-dependencies = [
- "getrandom",
- "redox_syscall",
-]
-
-[[package]]
-name = "regex"
-version = "1.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
-
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "rustc-demangle"
-version = "0.1.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
-
-[[package]]
-name = "ryu"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
-
-[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
-name = "serde"
-version = "1.0.130"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
-
-[[package]]
-name = "serde_derive"
-version = "1.0.130"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8"
-dependencies = [
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "sha2"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
-dependencies = [
- "block-buffer",
- "cfg-if 1.0.0",
- "cpufeatures",
- "digest",
- "opaque-debug",
-]
-
-[[package]]
-name = "sharded-slab"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "sketches-ddsketch"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76a77a8fd93886010f05e7ea0720e569d6d16c65329dbe3ec033bbbccccb017b"
-
-[[package]]
-name = "slab"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
-
-[[package]]
-name = "smallvec"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
-
-[[package]]
-name = "socket2"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "strsim"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
-
-[[package]]
-name = "subtle"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
-
-[[package]]
-name = "syn"
-version = "1.0.81"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-xid",
-]
-
-[[package]]
-name = "tempfile"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "rand",
- "redox_syscall",
- "remove_dir_all",
- "winapi",
-]
-
-[[package]]
-name = "termcolor"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.14.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "thiserror"
-version = "1.0.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "thread_local"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
-dependencies = [
- "once_cell",
-]
-
-[[package]]
-name = "thrussh"
-version = "0.33.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e6540238a9adf83df6e66541c182a52acf892ab335595ca965c229ade8536f8"
-dependencies = [
- "bitflags",
- "byteorder",
- "cryptovec",
- "digest",
- "flate2",
- "futures",
- "generic-array",
- "log",
- "rand",
- "sha2",
- "thiserror",
- "thrussh-keys",
- "thrussh-libsodium",
- "tokio",
-]
-
-[[package]]
-name = "thrussh-keys"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a72cc51a2932b18d92f7289332d8564cec4a5014063722a9d3fdca52c5d8f5ab"
-dependencies = [
- "aes",
- "bcrypt-pbkdf",
- "bit-vec",
- "block-modes",
- "byteorder",
- "cryptovec",
- "data-encoding",
- "dirs",
- "futures",
- "hmac",
- "log",
- "md5",
- "num-bigint",
- "num-integer",
- "pbkdf2",
- "rand",
- "serde",
- "serde_derive",
- "sha2",
- "thiserror",
- "thrussh-libsodium",
- "tokio",
- "tokio-stream",
- "yasna",
-]
-
-[[package]]
-name = "thrussh-libsodium"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfe89c70d27b1cb92e13bc8af63493e890d0de46dae4df0e28233f62b4ed9500"
-dependencies = [
- "lazy_static",
- "libc",
- "libsodium-sys",
- "pkg-config",
- "vcpkg",
-]
-
-[[package]]
-name = "tokio"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee"
-dependencies = [
- "autocfg",
- "bytes",
- "libc",
- "memchr",
- "mio",
- "num_cpus",
- "once_cell",
- "pin-project-lite",
- "signal-hook-registry",
- "tokio-macros",
- "winapi",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "114383b041aa6212c579467afa0075fbbdd0718de036100bc0ba7961d8cb9095"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "tokio-stream"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
-dependencies = [
- "futures-core",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "tower-service"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
-
-[[package]]
-name = "tracing"
-version = "0.1.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
-dependencies = [
- "cfg-if 1.0.0",
- "pin-project-lite",
- "tracing-attributes",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-attributes"
-version = "0.1.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "tracing-error"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
-dependencies = [
- "tracing",
- "tracing-subscriber",
-]
-
-[[package]]
-name = "tracing-log"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3"
-dependencies = [
- "lazy_static",
- "log",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-serde"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"
-dependencies = [
- "serde",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-subscriber"
-version = "0.2.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
-dependencies = [
- "ansi_term",
- "chrono",
- "lazy_static",
- "matchers",
- "regex",
- "serde",
- "serde_json",
- "sharded-slab",
- "smallvec",
- "thread_local",
- "tracing",
- "tracing-core",
- "tracing-log",
- "tracing-serde",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
-
-[[package]]
-name = "typenum"
-version = "1.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
-
-[[package]]
-name = "unicase"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
-dependencies = [
- "version_check",
-]
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
-[[package]]
-name = "vcpkg"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
-
-[[package]]
-name = "version_check"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
-
-[[package]]
-name = "walkdir"
-version = "2.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
-dependencies = [
- "same-file",
- "winapi",
- "winapi-util",
-]
-
-[[package]]
-name = "want"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
-dependencies = [
- "log",
- "try-lock",
-]
-
-[[package]]
-name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.78"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
-dependencies = [
- "cfg-if 1.0.0",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.78"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
-dependencies = [
- "bumpalo",
- "lazy_static",
- "log",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.78"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.78"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.78"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
-
-[[package]]
-name = "web-sys"
-version = "0.3.55"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-util"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "xanthous-server"
-version = "0.1.0"
-dependencies = [
- "base64ct",
- "clap",
- "color-eyre",
- "eyre",
- "futures",
- "libc",
- "metrics",
- "metrics-exporter-prometheus",
- "nix",
- "pbkdf2",
- "tempfile",
- "thrussh",
- "thrussh-keys",
- "tokio",
- "tracing",
- "tracing-subscriber",
-]
-
-[[package]]
-name = "yasna"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75"
-dependencies = [
- "bit-vec",
- "num-bigint",
-]
-
-[[package]]
-name = "zeroize"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
diff --git a/users/grfn/xanthous/server/Cargo.toml b/users/grfn/xanthous/server/Cargo.toml
deleted file mode 100644
index adb2a02391..0000000000
--- a/users/grfn/xanthous/server/Cargo.toml
+++ /dev/null
@@ -1,29 +0,0 @@
-[package]
-name = "xanthous-server"
-version = "0.1.0"
-edition = "2018"
-
-[dependencies]
-clap = "3.0.0-beta.5"
-color-eyre = "0.5.11"
-eyre = "0.6.5"
-thrussh = "0.33.5"
-thrussh-keys = "0.21.0"
-tracing = "0.1.29"
-tracing-subscriber = "0.2.25"
-metrics = "0.17.0"
-metrics-exporter-prometheus = "0.6.1"
-futures = "0.3.17"
-libc = "0.2.103"
-nix = "0.23.0"
-
-# Pins for rust 1.55 (2018 edition) until we have 1.56 in nixpkgs-unstable
-pbkdf2 = "<0.9"
-base64ct = "<1.2"
-
-[dependencies.tokio]
-version = "1.13"
-features = ["rt", "rt-multi-thread", "macros", "net", "process", "fs", "signal"]
-
-[dev-dependencies]
-tempfile = "3.2.0"
diff --git a/users/grfn/xanthous/server/default.nix b/users/grfn/xanthous/server/default.nix
deleted file mode 100644
index 0b3900e4d5..0000000000
--- a/users/grfn/xanthous/server/default.nix
+++ /dev/null
@@ -1,14 +0,0 @@
-args@{
-  depot ? import ../../../.. {}
-, pkgs ? depot.third_party.nixpkgs
-, ...
-}:
-
-depot.third_party.naersk.buildPackage {
-  name = "xanthous-server";
-  version = "0.0.1";
-  src = depot.third_party.gitignoreSource ./.;
-  passthru = {
-    docker = import ./docker.nix args;
-  };
-}
diff --git a/users/grfn/xanthous/server/docker.nix b/users/grfn/xanthous/server/docker.nix
deleted file mode 100644
index a62943c2b0..0000000000
--- a/users/grfn/xanthous/server/docker.nix
+++ /dev/null
@@ -1,19 +0,0 @@
-{ depot ? import ../../../.. {}
-, pkgs ? depot.third_party.nixpkgs
-, ...
-}:
-
-let
-  inherit (depot.users.grfn) xanthous;
-  xanthous-server = xanthous.server;
-in pkgs.dockerTools.buildLayeredImage {
-  name = "xanthous-server";
-  tag = "latest";
-  contents = [ xanthous xanthous-server ];
-  config = {
-    Cmd = [
-      "${xanthous-server}/bin/xanthous-server"
-      "--xanthous-binary-path" "${xanthous}/bin/xanthous"
-    ];
-  };
-}
diff --git a/users/grfn/xanthous/server/module.nix b/users/grfn/xanthous/server/module.nix
deleted file mode 100644
index 73ac276caf..0000000000
--- a/users/grfn/xanthous/server/module.nix
+++ /dev/null
@@ -1,48 +0,0 @@
-{ config, lib, pkgs, depot, ... }:
-
-let
-  cfg = config.services.xanthous-server;
-in {
-  options = with lib; {
-    services.xanthous-server = {
-      enable = mkEnableOption "xanthous server";
-
-      port = mkOption {
-        type = types.int;
-        default = 2222;
-        description = "Port to listen to for SSH connections";
-      };
-
-      metricsPort = mkOption {
-        type = types.int;
-        default = 9000;
-        description = "Port to listen to for prometheus metrics";
-      };
-
-      image = mkOption {
-        type = types.package;
-        default = depot.users.grfn.xanthous.server.docker;
-        description = "OCI image file to run";
-      };
-
-      ed25519SecretKeyFile = mkOption {
-        type = with types; uniq string;
-        description = "Path to the ed25519 secret key for the server";
-      };
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    virtualisation.oci-containers.containers."xanthous-server" = {
-      autoStart = true;
-      image = "${cfg.image.imageName}:${cfg.image.imageTag}";
-      imageFile = cfg.image;
-      ports = [
-        "${toString cfg.port}:22"
-        "${toString cfg.metricsPort}:9000"
-      ];
-      environment.SECRET_KEY_FILE = "/secret-key";
-      volumes = [ "/etc/secrets/xanthous-server-secret-key:/secret-key" ];
-    };
-  };
-}
diff --git a/users/grfn/xanthous/server/shell.nix b/users/grfn/xanthous/server/shell.nix
deleted file mode 100644
index a6747175f1..0000000000
--- a/users/grfn/xanthous/server/shell.nix
+++ /dev/null
@@ -1,11 +0,0 @@
-let
-  depot = import ../../../.. {};
-  pkgs = depot.third_party.nixpkgs;
-in
-
-pkgs.mkShell {
-  buildInputs = with pkgs; [
-    rustup
-    rust-analyzer
-  ];
-}
diff --git a/users/grfn/xanthous/server/src/main.rs b/users/grfn/xanthous/server/src/main.rs
deleted file mode 100644
index ed8f831c7d..0000000000
--- a/users/grfn/xanthous/server/src/main.rs
+++ /dev/null
@@ -1,388 +0,0 @@
-use std::net::SocketAddr;
-use std::path::PathBuf;
-use std::pin::Pin;
-use std::process::Command;
-use std::str;
-use std::sync::Arc;
-
-use clap::Parser;
-use color_eyre::eyre::Result;
-use eyre::{bail, Context};
-use futures::future::{ready, Ready};
-use futures::Future;
-use metrics_exporter_prometheus::PrometheusBuilder;
-use nix::pty::Winsize;
-use pty::ChildHandle;
-use thrussh::ChannelId;
-use thrussh::{
-    server::{self, Auth, Session},
-    CryptoVec,
-};
-use thrussh_keys::decode_secret_key;
-use thrussh_keys::key::KeyPair;
-use tokio::fs::File;
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
-use tokio::net::TcpListener;
-use tokio::select;
-use tokio::time::Instant;
-use tracing::{debug, error, info, info_span, trace, warn, Instrument};
-use tracing_subscriber::EnvFilter;
-
-use crate::pty::WaitPid;
-
-mod metrics;
-mod pty;
-
-use crate::metrics::reported::*;
-use crate::metrics::{decrement_gauge, histogram, increment_counter, increment_gauge};
-
-/// SSH-compatible server for playing Xanthous
-#[derive(Parser, Debug)]
-struct Opts {
-    /// Address to bind to
-    #[clap(long, short = 'a', default_value = "0.0.0.0:22")]
-    address: String,
-
-    /// Address to listen to for metrics
-    #[clap(long, default_value = "0.0.0.0:9000")]
-    metrics_address: SocketAddr,
-
-    /// Format to use when emitting log events
-    #[clap(
-        long,
-        env = "LOG_FORMAT",
-        default_value = "full",
-        possible_values = &["compact", "full", "pretty", "json"]
-    )]
-    log_format: String,
-
-    /// Full path to the xanthous binary
-    #[clap(long, env = "XANTHOUS_BINARY_PATH")]
-    xanthous_binary_path: String,
-
-    /// Path to a file containing the ed25519 secret key for the server
-    #[clap(long, env = "SECRET_KEY_FILE")]
-    secret_key_file: PathBuf,
-
-    /// Level to log at
-    #[clap(long, env = "LOG_LEVEL", default_value = "info")]
-    log_level: String,
-}
-
-impl Opts {
-    async fn read_secret_key(&self) -> Result<KeyPair> {
-        let mut file = File::open(&self.secret_key_file)
-            .await
-            .context("Reading secret key file")?;
-        let mut secret_key = Vec::with_capacity(464);
-        file.read_to_end(&mut secret_key).await?;
-        Ok(decode_secret_key(str::from_utf8(&secret_key)?, None)?)
-    }
-
-    async fn ssh_server_config(&self) -> Result<server::Config> {
-        let key_pair = self.read_secret_key().await?;
-
-        Ok(server::Config {
-            server_id: "SSH-2.0-xanthous".to_owned(),
-            keys: vec![key_pair],
-            ..Default::default()
-        })
-    }
-
-    fn init_logging(&self) -> Result<()> {
-        let filter = EnvFilter::try_new(&self.log_level)?;
-        let s = tracing_subscriber::fmt().with_env_filter(filter);
-
-        match self.log_format.as_str() {
-            "compact" => s.compact().init(),
-            "full" => s.init(),
-            "pretty" => s.pretty().init(),
-            "json" => s.json().with_current_span(true).init(),
-            _ => bail!("Invalid log format `{}`"),
-        }
-
-        Ok(())
-    }
-}
-
-struct Handler {
-    address: SocketAddr,
-    xanthous_binary_path: &'static str,
-    username: Option<String>,
-    child: Option<ChildHandle>,
-}
-
-async fn run_child(
-    mut child: pty::Child,
-    mut server_handle: server::Handle,
-    channel_id: ChannelId,
-) -> Result<()> {
-    let mut buf = [0; 2048];
-    loop {
-        select! {
-            r = child.tty.read(&mut buf)  => {
-                let read_bytes = r?;
-                if read_bytes == 0 {
-                    info!("EOF received from process");
-                    let _ = server_handle.close(channel_id).await;
-                    return Ok(())
-                } else {
-                    trace!(?read_bytes, "read bytes from child");
-                    let _ = server_handle.data(channel_id, CryptoVec::from_slice(&buf[..read_bytes])).await;
-                }
-            }
-            status = WaitPid::new(child.pid) => {
-                match status {
-                    Ok(_status) => info!("Child exited"),
-                    Err(error) => error!(%error, "Child failed"),
-                }
-                let _ = server_handle.close(channel_id).await;
-                return Ok(())
-            }
-        }
-    }
-}
-
-impl Handler {
-    async fn spawn_shell(
-        &mut self,
-        mut handle: server::Handle,
-        channel_id: ChannelId,
-        term: String,
-        winsize: Winsize,
-    ) -> Result<()> {
-        let mut cmd = Command::new(self.xanthous_binary_path);
-        cmd.env("TERM", term);
-        if let Some(username) = &self.username {
-            cmd.args(["--name", username]);
-        }
-        cmd.arg("--disable-saving");
-
-        let child = pty::spawn(cmd, Some(winsize), None).await?;
-        info!(pid = %child.pid, "Spawned child");
-        increment_gauge!(RUNNING_PROCESSES, 1.0);
-        self.child = Some(child.handle().await?);
-        tokio::spawn(
-            async move {
-                let span = info_span!("child", pid = %child.pid);
-                if let Err(error) = run_child(child, handle.clone(), channel_id)
-                    .instrument(span.clone())
-                    .await
-                {
-                    span.in_scope(|| error!(%error, "Error running child"));
-                    let _ = handle.close(channel_id).await;
-                }
-                decrement_gauge!(RUNNING_PROCESSES, 1.0);
-            }
-            .in_current_span(),
-        );
-        Ok(())
-    }
-}
-
-#[allow(clippy::type_complexity)]
-impl server::Handler for Handler {
-    type Error = eyre::Error;
-    type FutureAuth = Ready<Result<(Self, Auth)>>;
-    type FutureUnit = Pin<Box<dyn Future<Output = Result<(Self, Session)>> + Send + 'static>>;
-    type FutureBool = Ready<Result<(Self, Session, bool)>>;
-
-    fn finished_auth(self, auth: Auth) -> Self::FutureAuth {
-        ready(Ok((self, auth)))
-    }
-
-    fn finished_bool(self, b: bool, session: Session) -> Self::FutureBool {
-        ready(Ok((self, session, b)))
-    }
-
-    fn finished(self, session: Session) -> Self::FutureUnit {
-        Box::pin(ready(Ok((self, session))))
-    }
-
-    fn auth_none(mut self, username: &str) -> Self::FutureAuth {
-        info!(%username, "Accepted new connection");
-        self.username = Some(username.to_owned());
-        self.finished_auth(Auth::Accept)
-    }
-
-    fn auth_password(mut self, username: &str, _password: &str) -> Self::FutureAuth {
-        info!(%username, "Accepted new connection");
-        self.username = Some(username.to_owned());
-        self.finished_auth(Auth::Accept)
-    }
-
-    fn auth_publickey(
-        mut self,
-        username: &str,
-        _: &thrussh_keys::key::PublicKey,
-    ) -> Self::FutureAuth {
-        info!(%username, "Accepted new connection");
-        self.username = Some(username.to_owned());
-        self.finished_auth(Auth::Accept)
-    }
-
-    fn pty_request(
-        mut self,
-        channel: thrussh::ChannelId,
-        term: &str,
-        col_width: u32,
-        row_height: u32,
-        pix_width: u32,
-        pix_height: u32,
-        modes: &[(thrussh::Pty, u32)],
-        session: Session,
-    ) -> Self::FutureUnit {
-        let term = term.to_owned();
-        let modes = modes.to_vec();
-        Box::pin(async move {
-            debug!(
-                %term,
-                %col_width,
-                %row_height,
-                %pix_width,
-                %pix_height,
-                ?modes,
-                "PTY Requested"
-            );
-
-            self.spawn_shell(
-                session.handle(),
-                channel,
-                term,
-                Winsize {
-                    ws_row: row_height as _,
-                    ws_col: col_width as _,
-                    ws_xpixel: pix_width as _,
-                    ws_ypixel: pix_height as _,
-                },
-            )
-            .await?;
-
-            Ok((self, session))
-        })
-    }
-
-    fn window_change_request(
-        mut self,
-        _channel: ChannelId,
-        col_width: u32,
-        row_height: u32,
-        pix_width: u32,
-        pix_height: u32,
-        session: Session,
-    ) -> Self::FutureUnit {
-        Box::pin(async move {
-            if let Some(child) = self.child.as_mut() {
-                trace!(%row_height, %col_width, "Window resize request received");
-                child
-                    .resize_window(Winsize {
-                        ws_row: row_height as _,
-                        ws_col: col_width as _,
-                        ws_xpixel: pix_width as _,
-                        ws_ypixel: pix_height as _,
-                    })
-                    .await?;
-            } else {
-                warn!("Resize request received without child process; ignoring");
-            }
-
-            Ok((self, session))
-        })
-    }
-
-    fn data(
-        mut self,
-        _channel: thrussh::ChannelId,
-        data: &[u8],
-        session: Session,
-    ) -> Self::FutureUnit {
-        trace!(data = %String::from_utf8_lossy(data), raw_data = ?data);
-        let data = data.to_owned();
-        Box::pin(async move {
-            if let Some(child) = self.child.as_mut() {
-                child.write_all(&data).await?;
-            } else {
-                warn!("Data received without child process; ignoring");
-            }
-
-            Ok((self, session))
-        })
-    }
-}
-
-#[tokio::main]
-async fn main() -> Result<()> {
-    color_eyre::install()?;
-    let opts = Box::leak::<'static>(Box::new(Opts::parse()));
-    opts.init_logging()?;
-    PrometheusBuilder::new()
-        .listen_address(opts.metrics_address)
-        .install()?;
-    metrics::register();
-
-    let config = Arc::new(opts.ssh_server_config().await?);
-    info!(address = %opts.address, "Listening for new SSH connections");
-    let listener = TcpListener::bind(&opts.address).await?;
-
-    loop {
-        let (stream, address) = listener.accept().await?;
-        increment_counter!(CONNECTIONS_ACCEPTED);
-        increment_gauge!(ACTIVE_CONNECTIONS, 1.0);
-        let config = config.clone();
-        let handler = Handler {
-            xanthous_binary_path: &opts.xanthous_binary_path,
-            address,
-            username: None,
-            child: None,
-        };
-        tokio::spawn(async move {
-            let span = info_span!("client", address = %handler.address);
-            let start = Instant::now();
-            if let Err(error) = server::run_stream(config, stream, handler)
-                .instrument(span.clone())
-                .await
-            {
-                span.in_scope(|| error!(%error));
-            }
-            let duration = start.elapsed();
-            span.in_scope(|| info!(duration_ms = %duration.as_millis(), "Client disconnected"));
-            histogram!(CONNECTION_DURATION, duration);
-            decrement_gauge!(ACTIVE_CONNECTIONS, 1.0);
-        });
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use tempfile::NamedTempFile;
-
-    use super::*;
-
-    #[tokio::test]
-    async fn read_secret_key() {
-        use std::io::Write;
-
-        let mut file = NamedTempFile::new().unwrap();
-        file.write_all(
-            b"
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
-QyNTUxOQAAACAYz80xcK7jYxZMAl6apIHKRtB0Z2U78gG39c1QaIhgMwAAAJB9vxK9fb8S
-vQAAAAtzc2gtZWQyNTUxOQAAACAYz80xcK7jYxZMAl6apIHKRtB0Z2U78gG39c1QaIhgMw
-AAAEDNZ0d3lLNBGU6Im4JOpr490TOjm+cB7kMVXjVg3iCowBjPzTFwruNjFkwCXpqkgcpG
-0HRnZTvyAbf1zVBoiGAzAAAACHRlc3Qta2V5AQIDBAU=
------END OPENSSH PRIVATE KEY-----
-",
-        )
-        .unwrap();
-
-        let opts: Opts = Opts::parse_from(&[
-            "xanthous-server".as_ref(),
-            "--xanthous-binary-path".as_ref(),
-            "/bin/xanthous".as_ref(),
-            "--secret-key-file".as_ref(),
-            file.path().as_os_str(),
-        ]);
-        opts.read_secret_key().await.unwrap();
-    }
-}
diff --git a/users/grfn/xanthous/shell.nix b/users/grfn/xanthous/shell.nix
deleted file mode 100644
index 572ed211bc..0000000000
--- a/users/grfn/xanthous/shell.nix
+++ /dev/null
@@ -1,23 +0,0 @@
-let
-  depot = import ../../../. {};
-  inherit (depot) third_party;
-  pkgs = third_party.nixpkgs;
-in
-
-(pkgs.haskellPackages.extend (pkgs.haskell.lib.packageSourceOverrides {
-  xanthous = third_party.gitignoreSource ./.;
-})).shellFor {
-  packages = p: [p.xanthous];
-  withHoogle = true;
-  doBenchmark = true;
-  buildInputs = (with pkgs.haskellPackages; [
-    cabal-install
-    ghc-prof-flamegraph
-    hp2pretty
-    hlint
-    haskell-language-server
-    cabal2nix
-  ]) ++ (with pkgs; [
-    qpdf
-  ]);
-}
diff --git a/users/grfn/xanthous/src/Xanthous/App.hs b/users/grfn/xanthous/src/Xanthous/App.hs
deleted file mode 100644
index a251833955..0000000000
--- a/users/grfn/xanthous/src/Xanthous/App.hs
+++ /dev/null
@@ -1,607 +0,0 @@
-{-# LANGUAGE UndecidableInstances #-}
-{-# LANGUAGE RecordWildCards      #-}
---------------------------------------------------------------------------------
-{-# OPTIONS_GHC -Wno-deferred-type-errors #-}
-module Xanthous.App
-  ( makeApp
-  , RunType(..)
-  ) where
---------------------------------------------------------------------------------
-import           Xanthous.Prelude
-import           Brick hiding (App, halt, continue, raw)
-import qualified Brick
-import           Graphics.Vty.Attributes (defAttr)
-import           Graphics.Vty.Input.Events (Event(EvKey))
-import           Control.Monad.State (get, gets)
-import           Control.Monad.State.Class (modify)
-import           Data.Aeson (object, ToJSON)
-import qualified Data.Aeson as A
-import qualified Data.Vector as V
-import           System.Exit
-import           System.Directory (doesFileExist)
-import           Data.List.NonEmpty (NonEmpty(..))
-import           Data.Vector.Lens (toVectorOf)
---------------------------------------------------------------------------------
-import           Xanthous.App.Common
-import           Xanthous.App.Time
-import           Xanthous.App.Prompt
-import           Xanthous.App.Autocommands
-import           Xanthous.Command
-import           Xanthous.Data
-                 ( move
-                 , Dimensions'(Dimensions)
-                 , positioned
-                 , position
-                 , Position
-                 , (|*|)
-                 , Tiles(..), Hitpoints, fromScalar
-                 )
-import           Xanthous.Data.App (ResourceName, Panel(..), AppEvent(..))
-import qualified Xanthous.Data.EntityMap as EntityMap
-import           Xanthous.Data.Levels (prevLevel, nextLevel)
-import qualified Xanthous.Data.Levels as Levels
-import           Xanthous.Data.Entities (blocksObject)
-import           Xanthous.Game
-import           Xanthous.Game.State
-import           Xanthous.Game.Env
-import           Xanthous.Game.Draw (drawGame)
-import           Xanthous.Game.Prompt hiding (Fire)
-import qualified Xanthous.Messages as Messages
-import           Xanthous.Random
-import           Xanthous.Util (removeVectorIndex, useListOf)
-import           Xanthous.Util.Inflection (toSentence)
-import           Xanthous.Physics (throwDistance, bluntThrowDamage)
-import           Xanthous.Data.EntityMap.Graphics (lineOfSight)
-import           Xanthous.Data.EntityMap (EntityID)
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
-import           Xanthous.Entities.Common
-                 ( InventoryPosition, describeInventoryPosition, backpack
-                 , wieldableItem, wieldedItems, wielded, itemsWithPosition
-                 , removeItemFromPosition, asWieldedItem, inRightHand
-                 , wieldedItem, items
-                 )
-import qualified Xanthous.Entities.Character as Character
-import           Xanthous.Entities.Character hiding (pickUpItem)
-import           Xanthous.Entities.Item (Item, weight)
-import qualified Xanthous.Entities.Item as Item
-import           Xanthous.Entities.Creature (Creature)
-import qualified Xanthous.Entities.Creature as Creature
-import           Xanthous.Entities.Environment
-                 (Door, open, closed, locked, GroundMessage(..), Staircase(..))
-import           Xanthous.Entities.RawTypes
-                 ( edible, eatMessage, hitpointsHealed
-                 , attackMessage
-                 )
-import           Xanthous.Generators.Level
-import qualified Xanthous.Generators.Level.CaveAutomata as CaveAutomata
-import qualified Xanthous.Generators.Level.Dungeon as Dungeon
---------------------------------------------------------------------------------
-
-type App = Brick.App GameState AppEvent ResourceName
-
-data RunType = NewGame | LoadGame FilePath
-  deriving stock (Eq)
-
-makeApp :: GameEnv -> RunType -> IO App
-makeApp env rt = pure $ Brick.App
-  { appDraw = drawGame
-  , appChooseCursor = const headMay
-  , appHandleEvent = \game event -> runAppM (handleEvent event) env game
-  , appStartEvent = case rt of
-      NewGame -> runAppM (startEvent >> get) env
-      LoadGame save -> pure . (savefile ?~ save)
-  , appAttrMap = const $ attrMap defAttr []
-  }
-
-runAppM :: AppM a -> GameEnv -> GameState -> EventM ResourceName a
-runAppM appm ge = fmap fst . runAppT appm ge
-
-startEvent :: AppM ()
-startEvent = do
-  initLevel
-  modify updateCharacterVision
-  use (character . characterName) >>= \case
-    Nothing -> prompt_ @'StringPrompt ["character", "namePrompt"] Uncancellable
-      $ \(StringResult s) -> do
-        character . characterName ?= s
-        say ["welcome"] =<< use character
-    Just n -> say ["welcome"] $ object [ "characterName" A..= n ]
-
-initLevel :: AppM ()
-initLevel = do
-  level <- genLevel 0
-  entities <>= levelToEntityMap level
-  characterPosition .= level ^. levelCharacterPosition
-
---------------------------------------------------------------------------------
-
-handleEvent :: BrickEvent ResourceName AppEvent -> AppM (Next GameState)
-handleEvent ev = use promptState >>= \case
-  NoPrompt -> handleNoPromptEvent ev
-  WaitingPrompt msg pr -> handlePromptEvent msg pr ev
-
-
-handleNoPromptEvent :: BrickEvent ResourceName AppEvent -> AppM (Next GameState)
-handleNoPromptEvent (VtyEvent (EvKey k mods))
-  | Just command <- commandFromKey k mods
-  = do messageHistory %= nextTurn
-       cancelAutocommand
-       handleCommand command
-handleNoPromptEvent (AppEvent AutoContinue) = do
-  preuse (autocommand . _ActiveAutocommand . _1) >>= traverse_ autoStep
-  continue
-handleNoPromptEvent _ = continue
-
-handleCommand :: Command -> AppM (Next GameState)
-handleCommand Quit = confirm_ ["quit", "confirm"] (liftIO exitSuccess) >> continue
-handleCommand (Move dir) = do
-  newPos <- uses characterPosition $ move dir
-  collisionAt newPos >>= \case
-    Nothing -> do
-      characterPosition .= newPos
-      stepGameBy =<< uses (character . speed) (|*| Tiles 1)
-      describeEntitiesAt newPos
-    Just Combat -> attackAt newPos
-    Just Stop -> pure ()
-  continue
-
-handleCommand PickUp = do
-  pos <- use characterPosition
-  uses entities (entitiesAtPositionWithType @Item pos) >>= \case
-    [] -> say_ ["pickUp", "nothingToPickUp"]
-    [item] -> pickUpItem item
-    items' ->
-      menu_ ["pickUp", "menu"] Cancellable (entityMenu_ items')
-      $ \(MenuResult item) -> pickUpItem item
-  continue
-  where
-    pickUpItem (itemID, item) = do
-      character %= Character.pickUpItem item
-      entities . at itemID .= Nothing
-      say ["pickUp", "pickUp"] $ object [ "item" A..= item ]
-      stepGameBy 100 -- TODO
-
-handleCommand Drop = do
-  takeItemFromInventory_ ["drop", "menu"] Cancellable id
-    (say_ ["drop", "nothing"])
-    $ \(MenuResult item) -> do
-      entitiesAtCharacter %= (SomeEntity item <|)
-      say ["drop", "dropped"] $ object [ "item" A..= item ]
-  continue
-
-handleCommand PreviousMessage = do
-  messageHistory %= previousMessage
-  continue
-
-handleCommand Open = do
-  prompt_ @'DirectionPrompt ["open", "prompt"] Cancellable
-    $ \(DirectionResult dir) -> do
-      pos <- move dir <$> use characterPosition
-      doors <- uses entities $ entitiesAtPositionWithType @Door pos
-      if | null doors -> say_ ["open", "nothingToOpen"]
-         | any (view $ _2 . locked) doors -> say_ ["open", "locked"]
-         | all (view $ _2 . open) doors   -> say_ ["open", "alreadyOpen"]
-         | otherwise -> do
-             for_ doors $ \(eid, _) ->
-               entities . ix eid . positioned . _SomeEntity . open .= True
-             say_ ["open", "success"]
-      pure ()
-  stepGame -- TODO
-  continue
-
-handleCommand Close = do
-  prompt_ @'DirectionPrompt ["close", "prompt"] Cancellable
-    $ \(DirectionResult dir) -> do
-      pos <- move dir <$> use characterPosition
-      (nonDoors, doors) <- uses entities
-        $ partitionEithers
-        . toList
-        . map ( (matching . aside $ _SomeEntity @Door)
-              . over _2 (view positioned)
-              )
-        . EntityMap.atPositionWithIDs pos
-      if | null doors -> say_ ["close", "nothingToClose"]
-         | all (view $ _2 . closed) doors -> say_ ["close", "alreadyClosed"]
-         | any (view blocksObject . entityAttributes . snd) nonDoors ->
-           say ["close", "blocked"]
-           $ object [ "entityDescriptions"
-                      A..= ( toSentence
-                           . map description
-                           . filter (view blocksObject . entityAttributes)
-                           . map snd
-                           ) nonDoors
-                    , "blockOrBlocks"
-                      A..= ( if length nonDoors == 1
-                             then "blocks"
-                             else "block"
-                           :: Text)
-                    ]
-         | otherwise -> do
-             for_ doors $ \(eid, _) ->
-               entities . ix eid . positioned . _SomeEntity . closed .= True
-             for_ nonDoors $ \(eid, _) ->
-               entities . ix eid . position %= move dir
-             say_ ["close", "success"]
-      pure ()
-  stepGame -- TODO
-  continue
-
-handleCommand Look = do
-  prompt_ @'PointOnMap ["look", "prompt"] Cancellable
-    $ \(PointOnMapResult pos) -> revealedEntitiesAtPosition pos >>= \case
-        Empty -> say_ ["look", "nothing"]
-        ents -> describeEntities ents
-  continue
-
-handleCommand Wait = stepGame >> continue
-
-handleCommand Eat = do
-  uses (character . inventory . backpack)
-       (V.mapMaybe (\item -> (item,) <$> item ^. Item.itemType . edible))
-    >>= \case
-      Empty -> say_ ["eat", "noFood"]
-      food ->
-        let foodMenuItem idx (item, edibleItem)
-              = ( item ^. Item.itemType . char . char
-                , MenuOption (description item) (idx, item, edibleItem))
-                -- TODO refactor to use entityMenu_
-            menuItems = mkMenuItems $ imap foodMenuItem food
-        in menu_ ["eat", "menuPrompt"] Cancellable menuItems
-          $ \(MenuResult (idx, item, edibleItem)) -> do
-            character . inventory . backpack %= removeVectorIndex idx
-            let msg = fromMaybe (Messages.lookup ["eat", "eat"])
-                      $ edibleItem ^. eatMessage
-            character . characterHitpoints' +=
-              edibleItem ^. hitpointsHealed . to fromIntegral
-            message msg $ object ["item" A..= item]
-            stepGame -- TODO
-  continue
-
-handleCommand Read = do
-  -- TODO allow reading things in the inventory (combo direction+menu prompt?)
-  prompt_ @'DirectionPrompt ["read", "prompt"] Cancellable
-    $ \(DirectionResult dir) -> do
-      pos <- uses characterPosition $ move dir
-      uses entities
-        (fmap snd . entitiesAtPositionWithType @GroundMessage pos) >>= \case
-          Empty -> say_ ["read", "nothing"]
-          GroundMessage msg :< Empty ->
-            say ["read", "result"] $ object ["message" A..= msg]
-          msgs ->
-            let readAndContinue Empty = pure ()
-                readAndContinue (msg :< msgs') =
-                  prompt @'Continue
-                    ["read", "result"]
-                    (object ["message" A..= msg])
-                    Cancellable
-                  . const
-                  $ readAndContinue msgs'
-                readAndContinue _ = error "this is total"
-            in readAndContinue msgs
-  continue
-
-handleCommand ShowInventory = showPanel InventoryPanel >> continue
-
-handleCommand DescribeInventory = do
-  selectItemFromInventory_ ["inventory", "describe", "select"] Cancellable id
-    (say_ ["inventory", "describe", "nothing"])
-    $ \(MenuResult (invPos, item)) -> showPanel . ItemDescriptionPanel
-        $ Item.fullDescription item
-        <> "\n\n" <> describeInventoryPosition invPos
-  continue
-
-
-handleCommand Wield = do
-  takeItemFromInventory_ ["wield", "menu"] Cancellable asWieldedItem
-    (say_ ["wield", "nothing"])
-    $ \(MenuResult item) -> do
-      prevItems <- character . inventory . wielded <<.= inRightHand item
-      character . inventory . backpack
-        <>= fromList (prevItems ^.. wieldedItems . wieldedItem)
-      say ["wield", "wielded"] item
-  continue
-
-handleCommand Fire = do
-  selectItemFromInventory_ ["fire", "menu"] Cancellable id
-    (say_ ["fire", "nothing"])
-    $ \(MenuResult (invPos, item)) ->
-      let wt = weight item
-          dist = throwDistance wt
-          dam = bluntThrowDamage wt
-      in if dist < fromScalar 1
-         then say_ ["fire", "zeroRange"]
-         else firePrompt_ ["fire", "target"] Cancellable dist $
-          \(FireResult targetPos) -> do
-              charPos <- use characterPosition
-              mTarget <- uses entities $ firstEnemy . lineOfSight charPos targetPos
-              case mTarget of
-                Just target -> do
-                  creature' <- damageCreature target dam
-                  unless (Creature.isDead creature') $
-                    let msgPath = ["fire", "fired"] <> [if dam == 0
-                                                        then "noDamage"
-                                                        else "someDamage"]
-                    in say msgPath $ object [ "item" A..= item
-                                            , "creature" A..= creature'
-                                            ]
-                Nothing ->
-                  say ["fire", "fired", "noTarget"] $ object [ "item" A..= item ]
-              character . inventory %= removeItemFromPosition invPos item
-              entities . EntityMap.atPosition targetPos %= (SomeEntity item <|)
-              stepGame -- TODO(grfn): should this be based on distance?
-  continue
-  where
-    firstEnemy
-      :: [(Position, Vector (EntityID, SomeEntity))]
-      -> Maybe (EntityID, Creature)
-    firstEnemy los =
-      let enemies = los >>= \(_, es) -> toList $ headMay es
-      in enemies ^? folded . below _SomeEntity
-
-handleCommand Save =
-  view (config . disableSaving) >>= \case
-    True -> say_ ["save", "disabled"] >> continue
-    False -> do
-      -- TODO default save locations / config file?
-      use savefile >>= \case
-        Just filepath ->
-          stringPromptWithDefault_
-            ["save", "location"]
-            Cancellable
-            (pack filepath)
-            promptCallback
-        Nothing -> prompt_ @'StringPrompt ["save", "location"] Cancellable promptCallback
-      continue
-      where
-        promptCallback :: PromptResult 'StringPrompt -> AppM ()
-        promptCallback (StringResult filename) = do
-          sf <- use savefile
-          exists <- liftIO . doesFileExist $ unpack filename
-          if exists && sf /= Just (unpack filename)
-          then confirm ["save", "overwrite"] (object ["filename" A..= filename])
-              $ doSave filename
-          else doSave filename
-        doSave filename = do
-          src <- gets saveGame
-          lift . liftIO $ do
-            writeFile (unpack filename) $ toStrict src
-            exitSuccess
-
-handleCommand GoUp = do
-  hasStairs <- uses entitiesAtCharacter $ elem (SomeEntity UpStaircase)
-  if hasStairs
-  then uses levels prevLevel >>= \case
-    Just levs' -> do
-      cEID <- use characterEntityID
-      pCharacter <- entities . at cEID <<.= Nothing
-      levels .= levs'
-      charPos <- use characterPosition
-      entities . at cEID .= pCharacter
-      characterPosition .= charPos
-    Nothing ->
-      -- TODO in nethack, this leaves the game. Maybe something similar here?
-      say_ ["cant", "goUp"]
-  else say_ ["cant", "goUp"]
-
-  continue
-
-handleCommand GoDown = do
-  hasStairs <- uses entitiesAtCharacter $ elem (SomeEntity DownStaircase)
-
-  if hasStairs
-  then do
-    levs <- use levels
-    let newLevelNum = Levels.pos levs + 1
-    levs' <- nextLevel (levelToGameLevel <$> genLevel newLevelNum) levs
-    cEID <- use characterEntityID
-    pCharacter <- entities . at cEID <<.= Nothing
-    levels .= levs'
-    entities . at cEID .= pCharacter
-    characterPosition .= extract levs' ^. upStaircasePosition
-  else say_ ["cant", "goDown"]
-
-  continue
-
-handleCommand (StartAutoMove dir) = do
-  runAutocommand $ AutoMove dir
-  continue
-
-handleCommand Rest = do
-  say_ ["autocommands", "resting"]
-  runAutocommand AutoRest
-  continue
-
---
-
-handleCommand ToggleRevealAll = do
-  val <- debugState . allRevealed <%= not
-  say ["debug", "toggleRevealAll"] $ object [ "revealAll" A..= val ]
-  continue
-
---------------------------------------------------------------------------------
-attackAt :: Position -> AppM ()
-attackAt pos =
-  uses entities (entitiesAtPositionWithType @Creature pos) >>= \case
-    Empty               -> say_ ["combat", "nothingToAttack"]
-    (creature :< Empty) -> attackCreature creature
-    creatures ->
-      menu_ ["combat", "menu"] Cancellable (entityMenu_ creatures)
-      $ \(MenuResult creature) -> attackCreature creature
- where
-  attackCreature creature = do
-    charDamage <- uses character characterDamage
-    creature' <- damageCreature creature charDamage
-    msg <- uses character getAttackMessage
-    unless (Creature.isDead creature')
-      . message msg $ object ["creature" A..= creature']
-    whenM (uses character $ isNothing . weapon) handleFists
-    stepGame
-  weapon chr = chr ^? inventory . wielded . wieldedItems . wieldableItem
-  getAttackMessage chr =
-    case weapon chr of
-      Just wi ->
-        fromMaybe (Messages.lookup ["combat", "hit", "generic"])
-        $ wi ^. attackMessage
-      Nothing ->
-        Messages.lookup ["combat", "hit", "fists"]
-
-  handleFists = do
-    damageChance <- use $ character . body . knuckles . to fistDamageChance
-    whenM (chance damageChance) $ do
-      damageAmount <- use $ character . body . knuckles . to fistfightingDamage
-      say_ [ "combat" , if damageAmount > 1
-                        then "fistExtraSelfDamage"
-                        else "fistSelfDamage" ]
-      character %= Character.damage damageAmount
-      character . body . knuckles %= damageKnuckles
-
-damageCreature :: (EntityID, Creature) -> Hitpoints -> AppM Creature
-damageCreature (creatureID, creature) dam = do
-  let creature' = Creature.damage dam creature
-      msgParams = object ["creature" A..= creature']
-  if Creature.isDead creature'
-    then do
-      say ["combat", "killed"] msgParams
-      floorItems <- useListOf
-                   $ entities
-                   . ix creatureID
-                   . positioned
-                   . _SomeEntity @Creature
-                   . inventory
-                   . items
-      mCreaturePos <- preuse $ entities . ix creatureID . position
-      entities . at creatureID .= Nothing
-      for_ mCreaturePos $ \creaturePos ->
-        entities . EntityMap.atPosition creaturePos
-          %= (<> fromList (SomeEntity <$> floorItems))
-    else entities . ix creatureID . positioned .= SomeEntity creature'
-  pure creature'
-
-
-entityMenu_
-  :: (Comonad w, Entity entity)
-  => [w entity]
-  -> Map Char (MenuOption (w entity))
-entityMenu_ = mkMenuItems @[_] . map entityMenuItem
-  where
-    entityMenuItem wentity
-      = let entity = extract wentity
-      in (entityMenuChar entity, MenuOption (description entity) wentity)
-
-
-entityMenuChar :: Entity a => a -> Char
-entityMenuChar entity
-  = let ec = entityChar entity ^. char
-    in if ec `elem` (['a'..'z'] ++ ['A'..'Z'])
-        then ec
-        else 'a'
-
--- | Prompt with an item to select out of the inventory and call callback with
--- it
-selectItemFromInventory
-  :: forall item params.
-    (ToJSON params)
-  => [Text]            -- ^ Menu message
-  -> params            -- ^ Menu message params
-  -> PromptCancellable -- ^ Is the menu cancellable?
-  -> Prism' Item item  -- ^ Attach some extra information to the item, in a
-                      --   recoverable fashion. Prism vs iso so we can discard
-                      --   items.
-  -> AppM ()            -- ^ Action to take if there are no items matching
-  -> (PromptResult ('Menu (InventoryPosition, item)) -> AppM ())
-  -> AppM ()
-selectItemFromInventory msgPath msgParams cancellable extraInfo onEmpty cb = do
-  uses (character . inventory)
-       (V.mapMaybe (_2 $ preview extraInfo) . toVectorOf itemsWithPosition)
-    >>= \case
-      Empty -> onEmpty
-      items' -> menu msgPath msgParams cancellable (itemMenu items') cb
-  where
-    itemMenu = mkMenuItems . map itemMenuItem
-    itemMenuItem (invPos, extraInfoItem) =
-      let item = extraInfo # extraInfoItem
-      in ( entityMenuChar item
-         , MenuOption
-           (description item <> " (" <> describeInventoryPosition invPos <> ")")
-           (invPos, extraInfoItem)
-         )
-
--- | Prompt with an item to select out of the inventory and call callback with
--- it
-selectItemFromInventory_
-  :: forall item.
-    [Text]            -- ^ Menu message
-  -> PromptCancellable -- ^ Is the menu cancellable?
-  -> Prism' Item item  -- ^ Attach some extra information to the item, in a
-                      --   recoverable fashion. Prism vs iso so we can discard
-                      --   items.
-  -> AppM ()            -- ^ Action to take if there are no items matching
-  -> (PromptResult ('Menu (InventoryPosition, item)) -> AppM ())
-  -> AppM ()
-selectItemFromInventory_ msgPath = selectItemFromInventory msgPath ()
-
--- | Prompt with an item to select out of the inventory, remove it from the
--- inventory, and call callback with it
-takeItemFromInventory
-  :: forall item params.
-    (ToJSON params)
-  => [Text]            -- ^ Menu message
-  -> params            -- ^ Menu message params
-  -> PromptCancellable -- ^ Is the menu cancellable?
-  -> Prism' Item item  -- ^ Attach some extra information to the item, in a
-                      --   recoverable fashion. Prism vs iso so we can discard
-                      --   items.
-  -> AppM ()            -- ^ Action to take if there are no items matching
-  -> (PromptResult ('Menu item) -> AppM ())
-  -> AppM ()
-takeItemFromInventory msgPath msgParams cancellable extraInfo onEmpty cb =
-  selectItemFromInventory msgPath msgParams cancellable extraInfo onEmpty
-    $ \(MenuResult (invPos, item)) -> do
-      character . inventory
-        %= removeItemFromPosition invPos (item ^. re extraInfo)
-      cb $ MenuResult item
-
-takeItemFromInventory_
-  :: forall item.
-    [Text]            -- ^ Menu message
-  -> PromptCancellable -- ^ Is the menu cancellable?
-  -> Prism' Item item  -- ^ Attach some extra information to the item, in a
-                      --   recoverable fashion. Prism vs iso so we can discard
-                      --   items.
-  -> AppM ()            -- ^ Action to take if there are no items matching
-  -> (PromptResult ('Menu item) -> AppM ())
-  -> AppM ()
-takeItemFromInventory_ msgPath = takeItemFromInventory msgPath ()
-
--- entityMenu :: Entity entity => [entity] -> Map Char (MenuOption entity)
--- entityMenu = map (map runIdentity) . entityMenu_ . fmap Identity
-
-showPanel :: Panel -> AppM ()
-showPanel panel = do
-  activePanel ?= panel
-  prompt_ @'Continue ["generic", "continue"] Uncancellable
-    . const
-    $ activePanel .= Nothing
-
---------------------------------------------------------------------------------
-
-genLevel
-  :: Word -- ^ Level number, starting at 0
-  -> AppM Level
-genLevel num = do
-  let dims = Dimensions 80 80
-  generator <- choose $ CaveAutomata :| [Dungeon]
-  let
-    doGen = case generator of
-      CaveAutomata -> generateLevel SCaveAutomata CaveAutomata.defaultParams
-      Dungeon -> generateLevel SDungeon Dungeon.defaultParams
-  level <- doGen dims num
-  pure $!! level
-
-levelToGameLevel :: Level -> GameLevel
-levelToGameLevel level =
-  let _levelEntities = levelToEntityMap level
-      _upStaircasePosition = level ^. levelCharacterPosition
-      _levelRevealedPositions = mempty
-  in GameLevel {..}
diff --git a/users/grfn/xanthous/src/Xanthous/Command.hs b/users/grfn/xanthous/src/Xanthous/Command.hs
deleted file mode 100644
index 187e5c16d7..0000000000
--- a/users/grfn/xanthous/src/Xanthous/Command.hs
+++ /dev/null
@@ -1,84 +0,0 @@
---------------------------------------------------------------------------------
-module Xanthous.Command where
---------------------------------------------------------------------------------
-import Xanthous.Prelude hiding (Left, Right, Down)
---------------------------------------------------------------------------------
-import Graphics.Vty.Input (Key(..), Modifier(..))
-import qualified Data.Char as Char
---------------------------------------------------------------------------------
-import Xanthous.Data (Direction(..))
---------------------------------------------------------------------------------
-
-data Command
-  = Quit
-  | Move Direction
-  | StartAutoMove Direction
-  | PreviousMessage
-  | PickUp
-  | Drop
-  | Open
-  | Close
-  | Wait
-  | Eat
-  | Look
-  | Save
-  | Read
-  | ShowInventory
-  | DescribeInventory
-  | Wield
-  | Fire
-  | GoUp
-  | GoDown
-  | Rest
-
-    -- | TODO replace with `:` commands
-  | ToggleRevealAll
-
-commandFromKey :: Key -> [Modifier] -> Maybe Command
-commandFromKey (KChar 'q') [] = Just Quit
-commandFromKey (KChar '.') [] = Just Wait
-commandFromKey (KChar (directionFromChar -> Just dir)) [] = Just $ Move dir
-commandFromKey (KChar c) []
-  | Char.isUpper c
-  , Just dir <- directionFromChar $ Char.toLower c
-  = Just $ StartAutoMove dir
-commandFromKey (KChar 'p') [MCtrl] = Just PreviousMessage
-commandFromKey (KChar ',') [] = Just PickUp
-commandFromKey (KChar 'd') [] = Just Drop
-commandFromKey (KChar 'o') [] = Just Open
-commandFromKey (KChar 'c') [] = Just Close
-commandFromKey (KChar ';') [] = Just Look
-commandFromKey (KChar 'e') [] = Just Eat
-commandFromKey (KChar 'S') [] = Just Save
-commandFromKey (KChar 'r') [] = Just Read
-commandFromKey (KChar 'i') [] = Just ShowInventory
-commandFromKey (KChar 'I') [] = Just DescribeInventory
-commandFromKey (KChar 'w') [] = Just Wield
-commandFromKey (KChar 'f') [] = Just Fire
-commandFromKey (KChar '<') [] = Just GoUp
-commandFromKey (KChar '>') [] = Just GoDown
-commandFromKey (KChar 'R') [] = Just Rest
-
-commandFromKey KUp [] = Just $ Move Up
-commandFromKey KDown [] = Just $ Move Down
-commandFromKey KLeft [] = Just $ Move Left
-commandFromKey KRight [] = Just $ Move Right
-
--- DEBUG COMMANDS --
-commandFromKey (KChar 'r') [MMeta] = Just ToggleRevealAll
-
-commandFromKey _ _ = Nothing
-
---------------------------------------------------------------------------------
-
-directionFromChar :: Char -> Maybe Direction
-directionFromChar 'h' = Just Left
-directionFromChar 'j' = Just Down
-directionFromChar 'k' = Just Up
-directionFromChar 'l' = Just Right
-directionFromChar 'y' = Just UpLeft
-directionFromChar 'u' = Just UpRight
-directionFromChar 'b' = Just DownLeft
-directionFromChar 'n' = Just DownRight
-directionFromChar '.' = Just Here
-directionFromChar _   = Nothing
diff --git a/users/grfn/xanthous/src/Xanthous/Data/App.hs b/users/grfn/xanthous/src/Xanthous/Data/App.hs
deleted file mode 100644
index a2cfcb8001..0000000000
--- a/users/grfn/xanthous/src/Xanthous/Data/App.hs
+++ /dev/null
@@ -1,45 +0,0 @@
---------------------------------------------------------------------------------
-module Xanthous.Data.App
-  ( Panel(..)
-  , ResourceName(..)
-  , AppEvent(..)
-  ) where
---------------------------------------------------------------------------------
-import Xanthous.Prelude
---------------------------------------------------------------------------------
-import Test.QuickCheck
-import Test.QuickCheck.Instances.Text ()
-import Data.Aeson (ToJSON, FromJSON)
---------------------------------------------------------------------------------
-import Xanthous.Util.QuickCheck
---------------------------------------------------------------------------------
-
--- | Enum for "panels" displayed in the game's UI.
-data Panel
-  = -- | A panel displaying the character's inventory
-    InventoryPanel
-  | -- | A panel describing an item in the inventory in detail
-    --
-    -- The argument is the full description of the item
-    ItemDescriptionPanel Text
-  deriving stock (Show, Eq, Ord, Generic)
-  deriving anyclass (NFData, CoArbitrary, Function, ToJSON, FromJSON)
-  deriving Arbitrary via GenericArbitrary Panel
-
-
-data ResourceName
-  = MapViewport -- ^ The main viewport where we display the game content
-  | Character   -- ^ The character
-  | MessageBox  -- ^ The box where we display messages to the user
-  | Prompt      -- ^ The game's prompt
-  | Panel Panel -- ^ A panel in the game
-  deriving stock (Show, Eq, Ord, Generic)
-  deriving anyclass (NFData, CoArbitrary, Function, ToJSON, FromJSON)
-  deriving Arbitrary via GenericArbitrary ResourceName
-
-data AppEvent
-  = AutoContinue -- ^ Continue whatever autocommand has been requested by the
-                 --   user
-  deriving stock (Show, Eq, Ord, Generic)
-  deriving anyclass (NFData, CoArbitrary, Function, ToJSON, FromJSON)
-  deriving Arbitrary via GenericArbitrary AppEvent
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/rock.yaml b/users/grfn/xanthous/src/Xanthous/Entities/Raws/rock.yaml
deleted file mode 100644
index e7492bf5fb..0000000000
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/rock.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-Item:
-  name: rock
-  description: a rock
-  longDescription: a medium-sized rock made out of some unknown stone
-  char: .
-  wieldable:
-    damage: 1
-    attackMessage: you hit the {{creature.creatureType.name}} in the head with your rock.
-  density: [ 1500000, 2500000 ]
-  volume: [ 0.000125, 0.001 ]
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Draw.hs b/users/grfn/xanthous/src/Xanthous/Game/Draw.hs
deleted file mode 100644
index 53ea1c96f8..0000000000
--- a/users/grfn/xanthous/src/Xanthous/Game/Draw.hs
+++ /dev/null
@@ -1,151 +0,0 @@
---------------------------------------------------------------------------------
-module Xanthous.Game.Draw
-  ( drawGame
-  ) where
---------------------------------------------------------------------------------
-import           Xanthous.Prelude
---------------------------------------------------------------------------------
-import           Brick hiding (loc, on)
-import           Brick.Widgets.Border
-import           Brick.Widgets.Border.Style
-import           Brick.Widgets.Edit
-import           Control.Monad.State.Lazy (evalState)
-import           Control.Monad.State.Class ( get, MonadState, gets )
---------------------------------------------------------------------------------
-import           Xanthous.Data
-import           Xanthous.Data.App (ResourceName, Panel(..))
-import qualified Xanthous.Data.App as Resource
-import qualified Xanthous.Data.EntityMap as EntityMap
-import           Xanthous.Game.State
-import           Xanthous.Entities.Common (Wielded(..), wielded, backpack)
-import           Xanthous.Entities.Character
-import           Xanthous.Entities.Item (Item)
-import           Xanthous.Game
-                 ( characterPosition
-                 , character
-                 , revealedEntitiesAtPosition
-                 )
-import           Xanthous.Game.Prompt
-import           Xanthous.Orphans ()
---------------------------------------------------------------------------------
-
-cursorPosition :: GameState -> Widget ResourceName -> Widget ResourceName
-cursorPosition game
-  | WaitingPrompt _ (Prompt _ _ (preview promptStatePosition -> Just pos) _ _)
-    <- game ^. promptState
-  = showCursor Resource.Prompt (pos ^. loc)
-  | otherwise
-  = showCursor Resource.Character (game ^. characterPosition . loc)
-
-drawMessages :: MessageHistory -> Widget ResourceName
-drawMessages = txtWrap . (<> " ") . unwords . reverse . oextract
-
-drawPromptState :: GamePromptState m -> Widget ResourceName
-drawPromptState NoPrompt = emptyWidget
-drawPromptState (WaitingPrompt msg (Prompt _ pt ps pri _)) =
-  case (pt, ps, pri) of
-    (SStringPrompt, StringPromptState edit, mDef) ->
-      txt msg
-      <+> txt (maybe "" (\def -> "(default: " <> def <> ") ") mDef)
-      <+> renderEditor (txt . fold) True edit
-    (SDirectionPrompt, DirectionPromptState, _) -> txtWrap msg
-    (SMenu, _, menuItems) ->
-      txtWrap msg
-      <=> foldl' (<=>) emptyWidget (map drawMenuItem $ itoList menuItems)
-    _ -> txtWrap msg
-  where
-    drawMenuItem (chr, MenuOption m _) =
-      str ("[" <> pure chr <> "] ") <+> txtWrap m
-
-drawEntities
-  :: forall m. MonadState GameState m
-  => m (Widget ResourceName)
-drawEntities = do
-  allEnts <- use entities
-  let entityPositions = EntityMap.positions allEnts
-      maxY = fromMaybe 0 $ maximumOf (folded . y) entityPositions
-      maxX = fromMaybe 0 $ maximumOf (folded . x) entityPositions
-      rows = traverse mkRow [0..maxY]
-      mkRow rowY = hBox <$> traverse (renderEntityAt . flip Position rowY) [0..maxX]
-      renderEntityAt pos
-        = renderTopEntity pos <$> revealedEntitiesAtPosition pos
-      renderTopEntity pos ents
-        = let neighbors = EntityMap.neighbors pos allEnts
-          in maybe (str " ") (drawWithNeighbors neighbors)
-             $ maximumBy (compare `on` drawPriority)
-             <$> fromNullable ents
-  vBox <$> rows
-
-drawMap :: MonadState GameState m => m (Widget ResourceName)
-drawMap = do
-  cursorPos <- gets cursorPosition
-  viewport Resource.MapViewport Both . cursorPos <$> drawEntities
-
-bullet :: Char
-bullet = 'β€’'
-
-drawInventoryPanel :: GameState -> Widget ResourceName
-drawInventoryPanel game
-  =   drawWielded  (game ^. character . inventory . wielded)
-  <=> drawBackpack (game ^. character . inventory . backpack)
-  where
-    drawWielded (Hands Nothing Nothing) = emptyWidget
-    drawWielded (DoubleHanded i) =
-      txtWrap $ "You are holding " <> description i <> " in both hands"
-    drawWielded (Hands l r) = drawHand "left" l <=> drawHand "right" r
-    drawHand side = maybe emptyWidget $ \i ->
-      txtWrap ( "You are holding "
-              <> description i
-              <> " in your " <> side <> " hand"
-              )
-      <=> txt " "
-
-    drawBackpack :: Vector Item -> Widget ResourceName
-    drawBackpack Empty = txtWrap "Your backpack is empty right now."
-    drawBackpack backpackItems
-      = txtWrap ( "You are currently carrying the following items in your "
-                <> "backpack:")
-        <=> txt " "
-        <=> foldl' (<=>) emptyWidget
-            (map
-              (txtWrap . ((bullet <| " ") <>) . description)
-              backpackItems)
-
-
-drawPanel :: GameState -> Panel -> Widget ResourceName
-drawPanel game panel
-  = border
-  . hLimit 35
-  . viewport (Resource.Panel panel) Vertical
-  . case panel of
-      InventoryPanel -> drawInventoryPanel
-      ItemDescriptionPanel desc -> const $ txtWrap desc
-  $ game
-
-drawCharacterInfo :: Character -> Widget ResourceName
-drawCharacterInfo ch = txt " " <+> charName <+> charHitpoints
-  where
-    charName | Just n <- ch ^. characterName
-             = txt $ n <> " "
-             | otherwise
-             = emptyWidget
-    charHitpoints
-        = txt "Hitpoints: "
-      <+> txt (tshow $ let Hitpoints hp = characterHitpoints ch in hp)
-
-drawGame :: GameState -> [Widget ResourceName]
-drawGame = evalState $ do
-  game <- get
-  drawnMap <- drawMap
-  pure
-    . pure
-    . withBorderStyle unicode
-    $ case game ^. promptState of
-        NoPrompt -> drawMessages (game ^. messageHistory)
-        _ -> emptyWidget
-    <=> drawPromptState (game ^. promptState)
-    <=>
-    (maybe emptyWidget (drawPanel game) (game ^. activePanel)
-    <+> border drawnMap
-    )
-    <=> drawCharacterInfo (game ^. character)
diff --git a/users/grfn/xanthous/test/Xanthous/Entities/CommonSpec.hs b/users/grfn/xanthous/test/Xanthous/Entities/CommonSpec.hs
deleted file mode 100644
index ba27e3cbca..0000000000
--- a/users/grfn/xanthous/test/Xanthous/Entities/CommonSpec.hs
+++ /dev/null
@@ -1,32 +0,0 @@
---------------------------------------------------------------------------------
-module Xanthous.Entities.CommonSpec (main, test) where
---------------------------------------------------------------------------------
-import           Test.Prelude
-import           Data.Vector.Lens (toVectorOf)
---------------------------------------------------------------------------------
-import           Xanthous.Entities.Common
---------------------------------------------------------------------------------
-
-main :: IO ()
-main = defaultMain test
-
-test :: TestTree
-test = testGroup "Xanthous.Entities.CommonSpec"
-  [ testGroup "Inventory"
-    [ testProperty "items === itemsWithPosition . _2" $ \inv ->
-        inv ^.. items === inv ^.. itemsWithPosition . _2
-    , testGroup "removeItemFromPosition" $
-      let rewield w inv =
-            let (old, inv') = inv & wielded <<.~ w
-            in inv' & backpack <>~ toVectorOf (wieldedItems . wieldedItem) old
-      in [ (Backpack, \item -> backpack %~ (item ^. wieldedItem <|))
-         , (LeftHand, rewield . inLeftHand)
-         , (RightHand, rewield . inRightHand)
-         , (BothHands, rewield . review doubleHanded)
-         ] <&> \(pos, addItem) ->
-           testProperty (show pos) $ \inv item ->
-             let inv' = addItem item inv
-                 inv'' = removeItemFromPosition pos (item ^. wieldedItem) inv'
-             in inv'' ^.. items === inv ^.. items
-    ]
-  ]
diff --git a/users/isomer/OWNERS b/users/isomer/OWNERS
deleted file mode 100644
index 6997cd391d..0000000000
--- a/users/isomer/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherited: false
-owners:
-  - isomer
diff --git a/users/isomer/keys.nix b/users/isomer/keys.nix
deleted file mode 100644
index 8c29e27895..0000000000
--- a/users/isomer/keys.nix
+++ /dev/null
@@ -1,7 +0,0 @@
-# SSH public keys
-{ ... }:
-
-rec {
-  perry = "cert-authority,principals=perry ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCXWKN+FXlQAQ36R4+FHJ9f15Tz/48xLK1f85Yf9eBrvJJVMn6ge3Cy8AJ2nymBtVvCC86q616yl4Mn+CrKBH/vHr4jY9nxJ7HHgKI8ERr+7KpLIAiiaeIBljWwCy918lK3MijRCuj0P0d3v8CEFJjyCsiyglDVcNhsW87VqqZE6lUg4Alw1CGAmNjamxdoIZxjZAM9vJtZrlYnUiu+X7vTl5ttTaZkLCCfu+/bJAKFBWPG5BPaNjjfGVuTKqEc4plkI3JeZBu3Or3LzlYxcvp71i+eKGJ8F/nMBlo25iQsQpi8ZS7JYAhj3mYVrstw7j+nkgbordvDOK5NbDMi6GzX";
-  all = [ perry ];
-}
diff --git a/users/j4m3s/OWNERS b/users/j4m3s/OWNERS
new file mode 100644
index 0000000000..9d95afbeaa
--- /dev/null
+++ b/users/j4m3s/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+j4m3s
diff --git a/users/j4m3s/keys.nix b/users/j4m3s/keys.nix
new file mode 100644
index 0000000000..e5aaa30737
--- /dev/null
+++ b/users/j4m3s/keys.nix
@@ -0,0 +1,7 @@
+{ ... }:
+
+{
+  all = [
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH77KDNivaadAX0byGQgmel9hA7VcmFnL+IKYHgBVNVp tvl"
+  ];
+}
diff --git a/users/lukegb/OWNERS b/users/lukegb/OWNERS
index 676fbf1856..4ff54b467e 100644
--- a/users/lukegb/OWNERS
+++ b/users/lukegb/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - lukegb
+set noparent
+
+lukegb
diff --git a/users/lukegb/keys.nix b/users/lukegb/keys.nix
index e54009122f..4745df550c 100644
--- a/users/lukegb/keys.nix
+++ b/users/lukegb/keys.nix
@@ -3,8 +3,9 @@
 
 rec {
   termius = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINytpHct7PLdLNp6MoaOPP7ccBPUQKymVNMqix//Wt1f";
-  porcorosso-wsl = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMhQ3yjf59eQjOfVXzXz5u8BS5c6hdL1yY8GqccaIjx3";
   porcorosso-nixos = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILid+1rq3k3k7Kbaw8X63vrPrQdanH55TucQwp3ZWfo+";
   clouvider-lon01-nix = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINQU7Y+Ha5m0ebwUjA55xXT/xbWZAWx1fVNFufle+vQj";
-  all = [ termius porcorosso-wsl porcorosso-nixos clouvider-lon01-nix ];
+  lukegb-build = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICESF0H+OCxY/CfyG9VjM6iJe+VbYc4NmGjRrwPCHaD9";
+  lukegb-ca = "cert-authority,principals=\"lukegb\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEqNOwlR7Qa8cbGpDfSCOweDPbAGQOZIcoRgh6s/J8DR";
+  all = [ termius porcorosso-nixos clouvider-lon01-nix lukegb-build lukegb-ca ];
 }
diff --git a/users/padraic-o-mhuiris/OWNERS b/users/padraic-o-mhuiris/OWNERS
new file mode 100644
index 0000000000..ee6715b160
--- /dev/null
+++ b/users/padraic-o-mhuiris/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+padraic-o-mhuiris
diff --git a/users/picnoir/tvix-daemon/.gitignore b/users/picnoir/tvix-daemon/.gitignore
new file mode 100644
index 0000000000..ea8c4bf7f3
--- /dev/null
+++ b/users/picnoir/tvix-daemon/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/users/picnoir/tvix-daemon/Cargo.lock b/users/picnoir/tvix-daemon/Cargo.lock
new file mode 100644
index 0000000000..683203f5ca
--- /dev/null
+++ b/users/picnoir/tvix-daemon/Cargo.lock
@@ -0,0 +1,1541 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "anstream"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "axum"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bstr"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+dependencies = [
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cc"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "platforms",
+ "rustc_version",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
+
+[[package]]
+name = "der"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "document-features"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "enum-primitive-derive"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c"
+dependencies = [
+ "num-traits",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-core",
+ "futures-macro",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "h2"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "nix-compat"
+version = "0.1.0"
+dependencies = [
+ "bitflags 2.4.2",
+ "bstr",
+ "data-encoding",
+ "ed25519",
+ "ed25519-dalek",
+ "enum-primitive-derive",
+ "glob",
+ "nom",
+ "num-traits",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "platforms"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+
+[[package]]
+name = "serde"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
+
+[[package]]
+name = "socket2"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "2.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "thiserror"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tokio"
+version = "1.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-listener"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96367e127b4cf47b92592a5154a563435fe28fe3fccf25917d4a34ee59c87303"
+dependencies = [
+ "axum",
+ "document-features",
+ "futures-core",
+ "futures-util",
+ "nix",
+ "pin-project",
+ "socket2",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-test"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7"
+dependencies = [
+ "async-stream",
+ "bytes",
+ "futures-core",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "nu-ansi-term",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "tvix-daemon"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "nix-compat",
+ "tokio",
+ "tokio-listener",
+ "tokio-test",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.4",
+ "windows_aarch64_msvc 0.52.4",
+ "windows_i686_gnu 0.52.4",
+ "windows_i686_msvc 0.52.4",
+ "windows_x86_64_gnu 0.52.4",
+ "windows_x86_64_gnullvm 0.52.4",
+ "windows_x86_64_msvc 0.52.4",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
diff --git a/users/picnoir/tvix-daemon/Cargo.nix b/users/picnoir/tvix-daemon/Cargo.nix
new file mode 100644
index 0000000000..2382027f9b
--- /dev/null
+++ b/users/picnoir/tvix-daemon/Cargo.nix
@@ -0,0 +1,5754 @@
+# This file was @generated by crate2nix 0.13.0 with the command:
+#   "generate" "--all-features"
+# See https://github.com/kolloch/crate2nix for more info.
+
+{ nixpkgs ? <nixpkgs>
+, pkgs ? import nixpkgs { config = { }; }
+, lib ? pkgs.lib
+, stdenv ? pkgs.stdenv
+, buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate
+  # This is used as the `crateOverrides` argument for `buildRustCrate`.
+, defaultCrateOverrides ? pkgs.defaultCrateOverrides
+  # The features to enable for the root_crate or the workspace_members.
+, rootFeatures ? [ "default" ]
+  # If true, throw errors instead of issueing deprecation warnings.
+, strictDeprecation ? false
+  # Used for conditional compilation based on CPU feature detection.
+, targetFeatures ? [ ]
+  # Whether to perform release builds: longer compile times, faster binaries.
+, release ? true
+  # Additional crate2nix configuration if it exists.
+, crateConfig ? if builtins.pathExists ./crate-config.nix
+  then pkgs.callPackage ./crate-config.nix { }
+  else { }
+}:
+
+rec {
+  #
+  # "public" attributes that we attempt to keep stable with new versions of crate2nix.
+  #
+
+  rootCrate = rec {
+    packageId = "tvix-daemon";
+
+    # Use this attribute to refer to the derivation building your root crate package.
+    # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }.
+    build = internal.buildRustCrateWithFeatures {
+      inherit packageId;
+    };
+
+    # Debug support which might change between releases.
+    # File a bug if you depend on any for non-debug work!
+    debug = internal.debugCrate { inherit packageId; };
+  };
+  # Refer your crate build derivation by name here.
+  # You can override the features with
+  # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }.
+  workspaceMembers = {
+    "tvix-daemon" = rec {
+      packageId = "tvix-daemon";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-daemon";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+  };
+
+  # A derivation that joins the outputs of all workspace members together.
+  allWorkspaceMembers = pkgs.symlinkJoin {
+    name = "all-workspace-members";
+    paths =
+      let members = builtins.attrValues workspaceMembers;
+      in builtins.map (m: m.build) members;
+  };
+
+  #
+  # "internal" ("private") attributes that may change in every new version of crate2nix.
+  #
+
+  internal = rec {
+    # Build and dependency information for crates.
+    # Many of the fields are passed one-to-one to buildRustCrate.
+    #
+    # Noteworthy:
+    # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate.
+    #   but with additional information which is used during dependency/feature resolution.
+    # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging.
+    # * `devDependencies` as of now not used by `buildRustCrate` but used to
+    #   inject test dependencies into the build
+
+    crates = {
+      "addr2line" = rec {
+        crateName = "addr2line";
+        version = "0.21.0";
+        edition = "2018";
+        sha256 = "1jx0k3iwyqr8klqbzk6kjvr496yd94aspis10vwsj5wy7gib4c4a";
+        dependencies = [
+          {
+            name = "gimli";
+            packageId = "gimli";
+            usesDefaultFeatures = false;
+            features = [ "read" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "rustc-demangle" "cpp_demangle" "std-object" "fallible-iterator" "smallvec" "memmap2" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "memmap2" = [ "dep:memmap2" ];
+          "object" = [ "dep:object" ];
+          "rustc-demangle" = [ "dep:rustc-demangle" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "gimli/rustc-dep-of-std" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "gimli/std" ];
+          "std-object" = [ "std" "object" "object/std" "object/compression" "gimli/endian-reader" ];
+        };
+      };
+      "adler" = rec {
+        crateName = "adler";
+        version = "1.0.2";
+        edition = "2015";
+        sha256 = "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj";
+        authors = [
+          "Jonas Schievink <jonasschievink@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "anstream" = rec {
+        crateName = "anstream";
+        version = "0.6.13";
+        edition = "2021";
+        sha256 = "1yv2idkyf9mp9xwc684v0ywqiy86lwc9gvllwdishl7y6czx0syr";
+        dependencies = [
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "anstyle-parse";
+            packageId = "anstyle-parse";
+          }
+          {
+            name = "anstyle-query";
+            packageId = "anstyle-query";
+            optional = true;
+          }
+          {
+            name = "anstyle-wincon";
+            packageId = "anstyle-wincon";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "colorchoice";
+            packageId = "colorchoice";
+          }
+          {
+            name = "utf8parse";
+            packageId = "utf8parse";
+          }
+        ];
+        features = {
+          "auto" = [ "dep:anstyle-query" ];
+          "default" = [ "auto" "wincon" ];
+          "wincon" = [ "dep:anstyle-wincon" ];
+        };
+        resolvedDefaultFeatures = [ "auto" "default" "wincon" ];
+      };
+      "anstyle" = rec {
+        crateName = "anstyle";
+        version = "1.0.6";
+        edition = "2021";
+        sha256 = "1g1ngvxrz9d6xsymxzzzg581jzyz1sn8d0jpjcwxks07cff2c0c9";
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "anstyle-parse" = rec {
+        crateName = "anstyle-parse";
+        version = "0.2.3";
+        edition = "2021";
+        sha256 = "134jhzrz89labrdwxxnjxqjdg06qvaflj1wkfnmyapwyldfwcnn7";
+        dependencies = [
+          {
+            name = "utf8parse";
+            packageId = "utf8parse";
+            optional = true;
+          }
+        ];
+        features = {
+          "core" = [ "dep:arrayvec" ];
+          "default" = [ "utf8" ];
+          "utf8" = [ "dep:utf8parse" ];
+        };
+        resolvedDefaultFeatures = [ "default" "utf8" ];
+      };
+      "anstyle-query" = rec {
+        crateName = "anstyle-query";
+        version = "1.0.2";
+        edition = "2021";
+        sha256 = "0j3na4b1nma39g4x7cwvj009awxckjf3z2vkwhldgka44hqj72g2";
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_System_Console" "Win32_Foundation" ];
+          }
+        ];
+
+      };
+      "anstyle-wincon" = rec {
+        crateName = "anstyle-wincon";
+        version = "3.0.2";
+        edition = "2021";
+        sha256 = "19v0fv400bmp4niqpzxnhg83vz12mmqv7l2l8vi80qcdxj0lpm8w";
+        dependencies = [
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_System_Console" "Win32_Foundation" ];
+          }
+        ];
+
+      };
+      "async-stream" = rec {
+        crateName = "async-stream";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream-impl";
+            packageId = "async-stream-impl";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "async-stream-impl" = rec {
+        crateName = "async-stream-impl";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
+        procMacro = true;
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "async-trait" = rec {
+        crateName = "async-trait";
+        version = "0.1.77";
+        edition = "2021";
+        sha256 = "1adf1jh2yg39rkpmqjqyr9xyd6849p0d95425i6imgbhx0syx069";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "autocfg" = rec {
+        crateName = "autocfg";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "1ylp3cb47ylzabimazvbz9ms6ap784zhb6syaz6c1jqpmcmq0s6l";
+        authors = [
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+
+      };
+      "axum" = rec {
+        crateName = "axum";
+        version = "0.7.4";
+        edition = "2021";
+        sha256 = "17kv7v8m981cqmfbv5m538fzxhw51l9bajv06kfddi7njarb8dhj";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "axum-core";
+            packageId = "axum-core";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "http-body-util";
+            packageId = "http-body-util";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            optional = true;
+          }
+          {
+            name = "hyper-util";
+            packageId = "hyper-util";
+            optional = true;
+            features = [ "tokio" "server" "server-auto" ];
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "matchit";
+            packageId = "matchit";
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "mime";
+            packageId = "mime";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+            features = [ "raw_value" ];
+          }
+          {
+            name = "serde_path_to_error";
+            packageId = "serde_path_to_error";
+            optional = true;
+          }
+          {
+            name = "serde_urlencoded";
+            packageId = "serde_urlencoded";
+            optional = true;
+          }
+          {
+            name = "sync_wrapper";
+            packageId = "sync_wrapper";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            rename = "tokio";
+            optional = true;
+            features = [ "time" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            usesDefaultFeatures = false;
+            features = [ "util" ];
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            rename = "tokio";
+            features = [ "macros" "rt" "rt-multi-thread" "net" "test-util" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            rename = "tower";
+            features = [ "util" "timeout" "limit" "load-shed" "steer" "filter" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+        ];
+        features = {
+          "__private_docs" = [ "tower/full" "dep:tower-http" ];
+          "default" = [ "form" "http1" "json" "matched-path" "original-uri" "query" "tokio" "tower-log" "tracing" ];
+          "form" = [ "dep:serde_urlencoded" ];
+          "http1" = [ "dep:hyper" "hyper?/http1" ];
+          "http2" = [ "dep:hyper" "hyper?/http2" ];
+          "json" = [ "dep:serde_json" "dep:serde_path_to_error" ];
+          "macros" = [ "dep:axum-macros" ];
+          "multipart" = [ "dep:multer" ];
+          "query" = [ "dep:serde_urlencoded" ];
+          "tokio" = [ "dep:hyper-util" "dep:tokio" "tokio/net" "tokio/rt" "tower/make" "tokio/macros" ];
+          "tower-log" = [ "tower/log" ];
+          "tracing" = [ "dep:tracing" "axum-core/tracing" ];
+          "ws" = [ "dep:hyper" "tokio" "dep:tokio-tungstenite" "dep:sha1" "dep:base64" ];
+        };
+        resolvedDefaultFeatures = [ "default" "form" "http1" "json" "matched-path" "original-uri" "query" "tokio" "tower-log" "tracing" ];
+      };
+      "axum-core" = rec {
+        crateName = "axum-core";
+        version = "0.4.3";
+        edition = "2021";
+        sha256 = "1qx28wg4j6qdcdrisqwyaavlzc0zvbsrcwa99zf9456lfbyn6p51";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "http-body-util";
+            packageId = "http-body-util";
+          }
+          {
+            name = "mime";
+            packageId = "mime";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "sync_wrapper";
+            packageId = "sync_wrapper";
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+        ];
+        features = {
+          "__private_docs" = [ "dep:tower-http" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "tracing" ];
+      };
+      "backtrace" = rec {
+        crateName = "backtrace";
+        version = "0.3.69";
+        edition = "2018";
+        sha256 = "0dsq23dhw4pfndkx2nsa1ml2g31idm7ss7ljxp8d57avygivg290";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "addr2line";
+            packageId = "addr2line";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "object";
+            packageId = "object";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+            features = [ "read_core" "elf" "macho" "pe" "unaligned" "archive" ];
+          }
+          {
+            name = "rustc-demangle";
+            packageId = "rustc-demangle";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "std" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "serialize-rustc" = [ "rustc-serialize" ];
+          "serialize-serde" = [ "serde" ];
+          "verify-winapi" = [ "winapi/dbghelp" "winapi/handleapi" "winapi/libloaderapi" "winapi/memoryapi" "winapi/minwindef" "winapi/processthreadsapi" "winapi/synchapi" "winapi/tlhelp32" "winapi/winbase" "winapi/winnt" ];
+          "winapi" = [ "dep:winapi" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64ct" = rec {
+        crateName = "base64ct";
+        version = "1.6.0";
+        edition = "2021";
+        sha256 = "0nvdba4jb8aikv60az40x2w1y96sjdq8z3yp09rwzmkhiwv1lg4c";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "bitflags 1.3.2" = rec {
+        crateName = "bitflags";
+        version = "1.3.2";
+        edition = "2018";
+        sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bitflags 2.4.2" = rec {
+        crateName = "bitflags";
+        version = "2.4.2";
+        edition = "2021";
+        sha256 = "1pqd142hyqlzr7p9djxq2ff0jx07a2sb2xp9lhw69cbf80s0jmzd";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "block-buffer" = rec {
+        crateName = "block-buffer";
+        version = "0.10.4";
+        edition = "2018";
+        sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+
+      };
+      "bstr" = rec {
+        crateName = "bstr";
+        version = "1.9.1";
+        edition = "2021";
+        sha256 = "01ipr5rncw3kf4dyc1p2g00njn1df2b0xpviwhb8830iv77wbvq5";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "dfa-search" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "memchr/alloc" "serde?/alloc" ];
+          "default" = [ "std" "unicode" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" "memchr/std" "serde?/std" ];
+          "unicode" = [ "dep:regex-automata" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "serde" "std" "unicode" ];
+      };
+      "bytes" = rec {
+        crateName = "bytes";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "cc" = rec {
+        crateName = "cc";
+        version = "1.0.90";
+        edition = "2018";
+        sha256 = "1xg1bqnq50dpf6g1hl90caxgz4afnf74pxa426gh7wxch9561mlc";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "jobserver" = [ "dep:jobserver" ];
+          "libc" = [ "dep:libc" ];
+          "parallel" = [ "libc" "jobserver" ];
+        };
+      };
+      "cfg-if" = rec {
+        crateName = "cfg-if";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "clap" = rec {
+        crateName = "clap";
+        version = "4.5.3";
+        edition = "2021";
+        crateBin = [ ];
+        sha256 = "04w8fx68hzjzk45ir4b9jzwk4m7bki0k5afwns9zqgh61v82d5ll";
+        dependencies = [
+          {
+            name = "clap_builder";
+            packageId = "clap_builder";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "clap_derive";
+            packageId = "clap_derive";
+            optional = true;
+          }
+        ];
+        features = {
+          "cargo" = [ "clap_builder/cargo" ];
+          "color" = [ "clap_builder/color" ];
+          "debug" = [ "clap_builder/debug" "clap_derive?/debug" ];
+          "default" = [ "std" "color" "help" "usage" "error-context" "suggestions" ];
+          "deprecated" = [ "clap_builder/deprecated" "clap_derive?/deprecated" ];
+          "derive" = [ "dep:clap_derive" ];
+          "env" = [ "clap_builder/env" ];
+          "error-context" = [ "clap_builder/error-context" ];
+          "help" = [ "clap_builder/help" ];
+          "std" = [ "clap_builder/std" ];
+          "string" = [ "clap_builder/string" ];
+          "suggestions" = [ "clap_builder/suggestions" ];
+          "unicode" = [ "clap_builder/unicode" ];
+          "unstable-doc" = [ "clap_builder/unstable-doc" "derive" ];
+          "unstable-styles" = [ "clap_builder/unstable-styles" ];
+          "unstable-v5" = [ "clap_builder/unstable-v5" "clap_derive?/unstable-v5" "deprecated" ];
+          "usage" = [ "clap_builder/usage" ];
+          "wrap_help" = [ "clap_builder/wrap_help" ];
+        };
+        resolvedDefaultFeatures = [ "color" "default" "derive" "env" "error-context" "help" "std" "suggestions" "usage" ];
+      };
+      "clap_builder" = rec {
+        crateName = "clap_builder";
+        version = "4.5.2";
+        edition = "2021";
+        sha256 = "1d7p4hph4fyhaphkf0v5zv0kq4lz25a9jq2f901yrq3afqp9w4mf";
+        dependencies = [
+          {
+            name = "anstream";
+            packageId = "anstream";
+            optional = true;
+          }
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "clap_lex";
+            packageId = "clap_lex";
+          }
+          {
+            name = "strsim";
+            packageId = "strsim";
+            optional = true;
+          }
+        ];
+        features = {
+          "color" = [ "dep:anstream" ];
+          "debug" = [ "dep:backtrace" ];
+          "default" = [ "std" "color" "help" "usage" "error-context" "suggestions" ];
+          "std" = [ "anstyle/std" ];
+          "suggestions" = [ "dep:strsim" "error-context" ];
+          "unicode" = [ "dep:unicode-width" "dep:unicase" ];
+          "unstable-doc" = [ "cargo" "wrap_help" "env" "unicode" "string" ];
+          "unstable-styles" = [ "color" ];
+          "unstable-v5" = [ "deprecated" ];
+          "wrap_help" = [ "help" "dep:terminal_size" ];
+        };
+        resolvedDefaultFeatures = [ "color" "env" "error-context" "help" "std" "suggestions" "usage" ];
+      };
+      "clap_derive" = rec {
+        crateName = "clap_derive";
+        version = "4.5.3";
+        edition = "2021";
+        sha256 = "0byp6k5kyvi9jcbnjjbyw7ak7avn87f2s4ya154f3xc01h29l8wh";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "raw-deprecated" = [ "deprecated" ];
+          "unstable-v5" = [ "deprecated" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "clap_lex" = rec {
+        crateName = "clap_lex";
+        version = "0.7.0";
+        edition = "2021";
+        sha256 = "1kh1sckgq71kay2rrr149pl9gbsrvyccsq6xm5xpnq0cxnyqzk4q";
+
+      };
+      "colorchoice" = rec {
+        crateName = "colorchoice";
+        version = "1.0.0";
+        edition = "2021";
+        sha256 = "1ix7w85kwvyybwi2jdkl3yva2r2bvdcc3ka2grjfzfgrapqimgxc";
+
+      };
+      "const-oid" = rec {
+        crateName = "const-oid";
+        version = "0.9.6";
+        edition = "2021";
+        sha256 = "1y0jnqaq7p2wvspnx7qj76m7hjcqpz73qzvr9l2p9n2s51vr6if2";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+        };
+      };
+      "cpufeatures" = rec {
+        crateName = "cpufeatures";
+        version = "0.2.12";
+        edition = "2018";
+        sha256 = "012m7rrak4girqlii3jnqwrr73gv1i980q4wra5yyyhvzwk5xzjk";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-linux-android");
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("apple" == target."vendor" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("loongarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+        ];
+
+      };
+      "crypto-common" = rec {
+        crateName = "crypto-common";
+        version = "0.1.6";
+        edition = "2018";
+        sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+            features = [ "more_lengths" ];
+          }
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        features = {
+          "getrandom" = [ "rand_core/getrandom" ];
+          "rand_core" = [ "dep:rand_core" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "curve25519-dalek" = rec {
+        crateName = "curve25519-dalek";
+        version = "4.1.2";
+        edition = "2021";
+        sha256 = "0j7kqchcgycs4a11gvlda93h9w2jr05nn4hjpfyh2kn94a4pnrqa";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: ("x86_64" == target."arch" or null);
+          }
+          {
+            name = "curve25519-dalek-derive";
+            packageId = "curve25519-dalek-derive";
+            target = { target, features }: ((!("fiat" == target."curve25519_dalek_backend" or null)) && (!("serial" == target."curve25519_dalek_backend" or null)) && ("x86_64" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "fiat-crypto";
+            packageId = "fiat-crypto";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("fiat" == target."curve25519_dalek_backend" or null);
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "platforms";
+            packageId = "platforms";
+          }
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "default" = [ "alloc" "precomputed-tables" "zeroize" ];
+          "digest" = [ "dep:digest" ];
+          "ff" = [ "dep:ff" ];
+          "group" = [ "dep:group" "rand_core" ];
+          "group-bits" = [ "group" "ff/bits" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "digest" "precomputed-tables" "zeroize" ];
+      };
+      "curve25519-dalek-derive" = rec {
+        crateName = "curve25519-dalek-derive";
+        version = "0.1.1";
+        edition = "2021";
+        sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "data-encoding" = rec {
+        crateName = "data-encoding";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "1rcbnwfmfxhlshzbn3r7srm3azqha3mn33yxyqxkzz2wpqcjm5ky";
+        authors = [
+          "Julien Cretin <git@ia0.eu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "der" = rec {
+        crateName = "der";
+        version = "0.7.8";
+        edition = "2021";
+        sha256 = "070bwiyr80800h31c5zd96ckkgagfjgnrrdmz3dzg2lccsd3dypz";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "const-oid";
+            packageId = "const-oid";
+            optional = true;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "arbitrary" = [ "dep:arbitrary" "const-oid?/arbitrary" "std" ];
+          "bytes" = [ "dep:bytes" "alloc" ];
+          "derive" = [ "dep:der_derive" ];
+          "flagset" = [ "dep:flagset" ];
+          "oid" = [ "dep:const-oid" ];
+          "pem" = [ "dep:pem-rfc7468" "alloc" "zeroize" ];
+          "std" = [ "alloc" ];
+          "time" = [ "dep:time" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "oid" "std" "zeroize" ];
+      };
+      "digest" = rec {
+        crateName = "digest";
+        version = "0.10.7";
+        edition = "2018";
+        sha256 = "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer";
+            optional = true;
+          }
+          {
+            name = "crypto-common";
+            packageId = "crypto-common";
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "block-buffer" = [ "dep:block-buffer" ];
+          "const-oid" = [ "dep:const-oid" ];
+          "core-api" = [ "block-buffer" ];
+          "default" = [ "core-api" ];
+          "dev" = [ "blobby" ];
+          "mac" = [ "subtle" ];
+          "oid" = [ "const-oid" ];
+          "rand_core" = [ "crypto-common/rand_core" ];
+          "std" = [ "alloc" "crypto-common/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "block-buffer" "core-api" "default" "std" ];
+      };
+      "document-features" = rec {
+        crateName = "document-features";
+        version = "0.2.8";
+        edition = "2018";
+        sha256 = "15cvgxqngxslgllz15m8aban6wqfgsi6nlhr0g25yfsnd6nq4lpg";
+        procMacro = true;
+        libPath = "lib.rs";
+        authors = [
+          "Slint Developers <info@slint-ui.com>"
+        ];
+        dependencies = [
+          {
+            name = "litrs";
+            packageId = "litrs";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "ed25519" = rec {
+        crateName = "ed25519";
+        version = "2.2.3";
+        edition = "2021";
+        sha256 = "0lydzdf26zbn82g7xfczcac9d7mzm3qgx934ijjrd5hjpjx32m8i";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "pkcs8";
+            packageId = "pkcs8";
+            optional = true;
+          }
+          {
+            name = "signature";
+            packageId = "signature";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "pkcs8?/alloc" ];
+          "default" = [ "std" ];
+          "pem" = [ "alloc" "pkcs8/pem" ];
+          "pkcs8" = [ "dep:pkcs8" ];
+          "serde" = [ "dep:serde" ];
+          "serde_bytes" = [ "serde" "dep:serde_bytes" ];
+          "std" = [ "pkcs8?/std" "signature/std" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "ed25519-dalek" = rec {
+        crateName = "ed25519-dalek";
+        version = "2.1.1";
+        edition = "2021";
+        sha256 = "0w88cafwglg9hjizldbmlza0ns3hls81zk1bcih3m5m3h67algaa";
+        authors = [
+          "isis lovecruft <isis@patternsinthevoid.net>"
+          "Tony Arcieri <bascule@gmail.com>"
+          "Michael Rosenberg <michael@mrosenberg.pub>"
+        ];
+        dependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" ];
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" "rand_core" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "curve25519-dalek/alloc" "ed25519/alloc" "serde?/alloc" "zeroize/alloc" ];
+          "asm" = [ "sha2/asm" ];
+          "batch" = [ "alloc" "merlin" "rand_core" ];
+          "default" = [ "fast" "std" "zeroize" ];
+          "digest" = [ "signature/digest" ];
+          "fast" = [ "curve25519-dalek/precomputed-tables" ];
+          "legacy_compatibility" = [ "curve25519-dalek/legacy_compatibility" ];
+          "merlin" = [ "dep:merlin" ];
+          "pem" = [ "alloc" "ed25519/pem" "pkcs8" ];
+          "pkcs8" = [ "ed25519/pkcs8" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" "ed25519/serde" ];
+          "signature" = [ "dep:signature" ];
+          "std" = [ "alloc" "ed25519/std" "serde?/std" "sha2/std" ];
+          "zeroize" = [ "dep:zeroize" "curve25519-dalek/zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fast" "std" "zeroize" ];
+      };
+      "enum-primitive-derive" = rec {
+        crateName = "enum-primitive-derive";
+        version = "0.3.0";
+        edition = "2018";
+        sha256 = "0k6wcf58h5kh64yq5nfq71va53kaya0kzxwsjwbgwm2n2zd9axxs";
+        procMacro = true;
+        authors = [
+          "Doug Goldstein <cardoe@cardoe.com>"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+          }
+        ];
+
+      };
+      "equivalent" = rec {
+        crateName = "equivalent";
+        version = "1.0.1";
+        edition = "2015";
+        sha256 = "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl";
+
+      };
+      "fiat-crypto" = rec {
+        crateName = "fiat-crypto";
+        version = "0.2.6";
+        edition = "2018";
+        sha256 = "10hkkkjynhibvchznkxx81gwxqarn9i5sgz40d6xxb8xzhsz8xhn";
+        authors = [
+          "Fiat Crypto library authors <jgross@mit.edu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+      };
+      "fnv" = rec {
+        crateName = "fnv";
+        version = "1.0.7";
+        edition = "2015";
+        sha256 = "1hc2mcqha06aibcaza94vbi81j6pr9a1bbxrxjfhc91zin8yr7iz";
+        libPath = "lib.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "form_urlencoded" = rec {
+        crateName = "form_urlencoded";
+        version = "1.2.1";
+        edition = "2018";
+        sha256 = "0milh8x7nl4f450s3ddhg57a3flcv6yq8hlkyk6fyr3mcb128dp1";
+        authors = [
+          "The rust-url developers"
+        ];
+        dependencies = [
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "percent-encoding/alloc" ];
+          "default" = [ "std" ];
+          "std" = [ "alloc" "percent-encoding/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-channel" = rec {
+        crateName = "futures-channel";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" ];
+          "default" = [ "std" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "sink" = [ "futures-sink" ];
+          "std" = [ "alloc" "futures-core/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-core" = rec {
+        crateName = "futures-core";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        features = {
+          "default" = [ "std" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-macro" = rec {
+        crateName = "futures-macro";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "futures-sink" = rec {
+        crateName = "futures-sink";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-task" = rec {
+        crateName = "futures-task";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "futures-util" = rec {
+        crateName = "futures-util";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-macro";
+            packageId = "futures-macro";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "pin-utils";
+            packageId = "pin-utils";
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" ];
+          "async-await-macro" = [ "async-await" "futures-macro" ];
+          "channel" = [ "std" "futures-channel" ];
+          "compat" = [ "std" "futures_01" ];
+          "default" = [ "std" "async-await" "async-await-macro" ];
+          "futures-channel" = [ "dep:futures-channel" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-macro" = [ "dep:futures-macro" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "futures_01" = [ "dep:futures_01" ];
+          "io" = [ "std" "futures-io" "memchr" ];
+          "io-compat" = [ "io" "compat" "tokio-io" ];
+          "memchr" = [ "dep:memchr" ];
+          "portable-atomic" = [ "futures-core/portable-atomic" ];
+          "sink" = [ "futures-sink" ];
+          "slab" = [ "dep:slab" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "slab" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" ];
+          "write-all-vectored" = [ "io" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "async-await-macro" "default" "futures-macro" "slab" "std" ];
+      };
+      "generic-array" = rec {
+        crateName = "generic-array";
+        version = "0.14.7";
+        edition = "2015";
+        sha256 = "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45";
+        libName = "generic_array";
+        authors = [
+          "BartΕ‚omiej KamiΕ„ski <fizyk20@gmail.com>"
+          "Aaron Trent <novacrazy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "more_lengths" ];
+      };
+      "getrandom" = rec {
+        crateName = "getrandom";
+        version = "0.2.12";
+        edition = "2018";
+        sha256 = "1d8jb9bv38nkwlqqdjcav6gxckgwc9g30pm3qq506rvncpm9400r";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "js" = [ "wasm-bindgen" "js-sys" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "libc/rustc-dep-of-std" "wasi/rustc-dep-of-std" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "gimli" = rec {
+        crateName = "gimli";
+        version = "0.28.1";
+        edition = "2018";
+        sha256 = "0lv23wc8rxvmjia3mcxc6hj9vkqnv1bqq0h8nzjcgf71mrxx6wa2";
+        features = {
+          "default" = [ "read-all" "write" ];
+          "endian-reader" = [ "read" "dep:stable_deref_trait" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "read" = [ "read-core" ];
+          "read-all" = [ "read" "std" "fallible-iterator" "endian-reader" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" ];
+          "std" = [ "fallible-iterator?/std" "stable_deref_trait?/std" ];
+          "write" = [ "dep:indexmap" ];
+        };
+        resolvedDefaultFeatures = [ "read" "read-core" ];
+      };
+      "glob" = rec {
+        crateName = "glob";
+        version = "0.3.1";
+        edition = "2015";
+        sha256 = "16zca52nglanv23q5qrwd5jinw3d3as5ylya6y1pbx47vkxvrynj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+
+      };
+      "h2" = rec {
+        crateName = "h2";
+        version = "0.4.2";
+        edition = "2018";
+        sha256 = "0hqr2l7kl9zqjcjdv69v9jx6v65mlbsavsyff8mr6lgqkbjk1l1i";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            features = [ "codec" "io" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt-multi-thread" "macros" "sync" "net" ];
+          }
+        ];
+        features = { };
+      };
+      "hashbrown" = rec {
+        crateName = "hashbrown";
+        version = "0.14.3";
+        edition = "2021";
+        sha256 = "012nywlg0lj9kwanh69my5x67vjlfmzfi9a0rq4qvis2j8fil3r9";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "alloc" = [ "dep:alloc" ];
+          "allocator-api2" = [ "dep:allocator-api2" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "ahash" "inline-more" "allocator-api2" ];
+          "equivalent" = [ "dep:equivalent" ];
+          "nightly" = [ "allocator-api2?/nightly" "bumpalo/allocator_api" ];
+          "rayon" = [ "dep:rayon" ];
+          "rkyv" = [ "dep:rkyv" ];
+          "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "raw" ];
+      };
+      "heck" = rec {
+        crateName = "heck";
+        version = "0.5.0";
+        edition = "2021";
+        sha256 = "1sjmpsdl8czyh9ywl3qcsfsq9a307dg4ni2vnlwgnzzqhc4y0113";
+
+      };
+      "hermit-abi" = rec {
+        crateName = "hermit-abi";
+        version = "0.3.9";
+        edition = "2021";
+        sha256 = "092hxjbjnq5fmz66grd9plxd0sh6ssg5fhgwwwqbrzgzkjwdycfj";
+        authors = [
+          "Stefan Lankes"
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins/rustc-dep-of-std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "http" = rec {
+        crateName = "http";
+        version = "1.1.0";
+        edition = "2018";
+        sha256 = "0n426lmcxas6h75c2cp25m933pswlrfjz10v91vc62vib2sdvf91";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "http-body" = rec {
+        crateName = "http-body";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "0hyn8n3iadrbwq8y0p1rl1275s4nm49bllw5wji29g4aa3dqbb0w";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+        ];
+
+      };
+      "http-body-util" = rec {
+        crateName = "http-body-util";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "07agldas2qgcfc05ckiarlmf9vzragbda823nqhrqrc6mjrghx84";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "httparse" = rec {
+        crateName = "httparse";
+        version = "1.8.0";
+        edition = "2018";
+        sha256 = "010rrfahm1jss3p022fqf3j3jmm72vhn4iqhykahb9ynpaag75yq";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "httpdate" = rec {
+        crateName = "httpdate";
+        version = "1.0.3";
+        edition = "2021";
+        sha256 = "1aa9rd2sac0zhjqh24c9xvir96g188zldkx0hr6dnnlx5904cfyz";
+        authors = [
+          "Pyfisch <pyfisch@posteo.org>"
+        ];
+
+      };
+      "hyper" = rec {
+        crateName = "hyper";
+        version = "1.2.0";
+        edition = "2021";
+        sha256 = "0fi6k7hz5fmdph0a5r8hw50d7h2n9zxkizmafcmb65f67bblhr8q";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            optional = true;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "h2";
+            packageId = "h2";
+            optional = true;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "httparse";
+            packageId = "httparse";
+            optional = true;
+          }
+          {
+            name = "httpdate";
+            packageId = "httpdate";
+            optional = true;
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+            optional = true;
+            features = [ "const_generics" "const_new" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            features = [ "sink" ];
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "macros" "net" "io-std" "io-util" "rt" "rt-multi-thread" "sync" "time" "test-util" ];
+          }
+        ];
+        features = {
+          "client" = [ "dep:want" "dep:pin-project-lite" "dep:smallvec" ];
+          "ffi" = [ "dep:libc" "dep:http-body-util" ];
+          "full" = [ "client" "http1" "http2" "server" ];
+          "http1" = [ "dep:futures-channel" "dep:futures-util" "dep:httparse" "dep:itoa" ];
+          "http2" = [ "dep:futures-channel" "dep:futures-util" "dep:h2" ];
+          "server" = [ "dep:httpdate" "dep:pin-project-lite" "dep:smallvec" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "default" "http1" "http2" "server" ];
+      };
+      "hyper-util" = rec {
+        crateName = "hyper-util";
+        version = "0.1.3";
+        edition = "2021";
+        sha256 = "1akngan7j0n2n0wd25c6952mvqbkj9gp1lcwzyxjc0d37l8yyf6a";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            features = [ "all" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "net" "rt" "time" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "full" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "test-util" ];
+          }
+        ];
+        features = {
+          "client" = [ "hyper/client" "dep:tracing" "dep:futures-channel" "dep:tower" "dep:tower-service" ];
+          "client-legacy" = [ "client" ];
+          "full" = [ "client" "client-legacy" "server" "server-auto" "service" "http1" "http2" "tokio" ];
+          "http1" = [ "hyper/http1" ];
+          "http2" = [ "hyper/http2" ];
+          "server" = [ "hyper/server" ];
+          "server-auto" = [ "server" "http1" "http2" ];
+          "service" = [ "dep:tower" "dep:tower-service" ];
+          "tokio" = [ "dep:tokio" "dep:socket2" ];
+        };
+        resolvedDefaultFeatures = [ "default" "http1" "http2" "server" "server-auto" "tokio" ];
+      };
+      "indexmap" = rec {
+        crateName = "indexmap";
+        version = "2.2.5";
+        edition = "2021";
+        sha256 = "1x4x9zdqvlkfks3y84dsynh1p8na3nn48nn454s26rqla6fr42vv";
+        dependencies = [
+          {
+            name = "equivalent";
+            packageId = "equivalent";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            usesDefaultFeatures = false;
+            features = [ "raw" ];
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "borsh" = [ "dep:borsh" ];
+          "default" = [ "std" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-rayon" = [ "dep:rustc-rayon" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "itoa" = rec {
+        crateName = "itoa";
+        version = "1.0.10";
+        edition = "2018";
+        sha256 = "0k7xjfki7mnv6yzjrbnbnjllg86acmbnk4izz2jmm1hx2wd6v95i";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "lazy_static" = rec {
+        crateName = "lazy_static";
+        version = "1.4.0";
+        edition = "2015";
+        sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2";
+        authors = [
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        features = {
+          "spin" = [ "dep:spin" ];
+          "spin_no_std" = [ "spin" ];
+        };
+      };
+      "libc" = rec {
+        crateName = "libc";
+        version = "0.2.153";
+        edition = "2015";
+        sha256 = "1gg7m1ils5dms5miq9fyllrcp0jxnbpgkx71chd2i0lafa8qy6cw";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ];
+          "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "extra_traits" "std" ];
+      };
+      "litrs" = rec {
+        crateName = "litrs";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "19cssch9gc0x2snd9089nvwzz79zx6nzsi3icffpx25p4hck1kml";
+        authors = [
+          "Lukas Kalbertodt <lukas.kalbertodt@gmail.com>"
+        ];
+        features = {
+          "check_suffix" = [ "unicode-xid" ];
+          "default" = [ "proc-macro2" ];
+          "proc-macro2" = [ "dep:proc-macro2" ];
+          "unicode-xid" = [ "dep:unicode-xid" ];
+        };
+      };
+      "lock_api" = rec {
+        crateName = "lock_api";
+        version = "0.4.11";
+        edition = "2018";
+        sha256 = "0iggx0h4jx63xm35861106af3jkxq06fpqhpkhgw0axi2n38y5iw";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "atomic_usize" ];
+          "owning_ref" = [ "dep:owning_ref" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "atomic_usize" "default" ];
+      };
+      "log" = rec {
+        crateName = "log";
+        version = "0.4.21";
+        edition = "2021";
+        sha256 = "074hldq1q8rlzq2s2qa8f25hj4s3gpw71w64vdwzjd01a4g8rvch";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "kv_serde" = [ "kv_std" "value-bag/serde" "serde" ];
+          "kv_std" = [ "std" "kv" "value-bag/error" ];
+          "kv_sval" = [ "kv" "value-bag/sval" "sval" "sval_ref" ];
+          "kv_unstable" = [ "kv" "value-bag" ];
+          "kv_unstable_serde" = [ "kv_serde" "kv_unstable_std" ];
+          "kv_unstable_std" = [ "kv_std" "kv_unstable" ];
+          "kv_unstable_sval" = [ "kv_sval" "kv_unstable" ];
+          "serde" = [ "dep:serde" ];
+          "sval" = [ "dep:sval" ];
+          "sval_ref" = [ "dep:sval_ref" ];
+          "value-bag" = [ "dep:value-bag" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "matchit" = rec {
+        crateName = "matchit";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "156bgdmmlv4crib31qhgg49nsjk88dxkdqp80ha2pk2rk6n6ax0f";
+        authors = [
+          "Ibraheem Ahmed <ibraheem@ibraheem.ca>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "memchr" = rec {
+        crateName = "memchr";
+        version = "2.7.1";
+        edition = "2021";
+        sha256 = "0jf1kicqa4vs9lyzj4v4y1p90q0dh87hvhsdd5xvhnp527sw8gaj";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+          "bluss"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "logging" = [ "dep:log" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "std" = [ "alloc" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "mime" = rec {
+        crateName = "mime";
+        version = "0.3.17";
+        edition = "2015";
+        sha256 = "16hkibgvb9klh0w0jk5crr5xv90l3wlf77ggymzjmvl1818vnxv8";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+
+      };
+      "minimal-lexical" = rec {
+        crateName = "minimal-lexical";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "miniz_oxide" = rec {
+        crateName = "miniz_oxide";
+        version = "0.7.2";
+        edition = "2018";
+        sha256 = "19qlxb21s6kabgqq61mk7kd1qk2invyygj076jz6i1gj2lz1z0cx";
+        authors = [
+          "Frommi <daniil.liferenko@gmail.com>"
+          "oyvindln <oyvindln@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "adler";
+            packageId = "adler";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "with-alloc" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "adler/rustc-dep-of-std" ];
+          "simd" = [ "simd-adler32" ];
+          "simd-adler32" = [ "dep:simd-adler32" ];
+        };
+      };
+      "mio" = rec {
+        crateName = "mio";
+        version = "0.8.11";
+        edition = "2018";
+        sha256 = "034byyl0ardml5yliy1hmvx8arkmn9rv479pid794sm07ia519m4";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_Storage_FileSystem" "Win32_System_IO" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = {
+          "default" = [ "log" ];
+          "log" = [ "dep:log" ];
+          "os-ext" = [ "os-poll" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_Security" ];
+        };
+        resolvedDefaultFeatures = [ "net" "os-ext" "os-poll" ];
+      };
+      "nix" = rec {
+        crateName = "nix";
+        version = "0.26.4";
+        edition = "2018";
+        sha256 = "06xgl4ybb8pvjrbmc3xggbgk3kbs1j0c4c0nzdfrmpbgrkrym2sr";
+        authors = [
+          "The nix-rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            features = [ "extra_traits" ];
+          }
+        ];
+        features = {
+          "aio" = [ "pin-utils" ];
+          "default" = [ "acct" "aio" "dir" "env" "event" "feature" "fs" "hostname" "inotify" "ioctl" "kmod" "mman" "mount" "mqueue" "net" "personality" "poll" "process" "pthread" "ptrace" "quota" "reboot" "resource" "sched" "signal" "socket" "term" "time" "ucontext" "uio" "user" "zerocopy" ];
+          "dir" = [ "fs" ];
+          "memoffset" = [ "dep:memoffset" ];
+          "mount" = [ "uio" ];
+          "mqueue" = [ "fs" ];
+          "net" = [ "socket" ];
+          "pin-utils" = [ "dep:pin-utils" ];
+          "ptrace" = [ "process" ];
+          "sched" = [ "process" ];
+          "signal" = [ "process" ];
+          "socket" = [ "memoffset" ];
+          "ucontext" = [ "signal" ];
+          "user" = [ "feature" ];
+          "zerocopy" = [ "fs" "uio" ];
+        };
+        resolvedDefaultFeatures = [ "feature" "fs" "user" ];
+      };
+      "nix-compat" = rec {
+        crateName = "nix-compat";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [ ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ../../../tvix/nix-compat; }
+          else ../../../tvix/nix-compat;
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+            features = [ "alloc" "unicode" "serde" ];
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+          }
+          {
+            name = "ed25519-dalek";
+            packageId = "ed25519-dalek";
+          }
+          {
+            name = "enum-primitive-derive";
+            packageId = "enum-primitive-derive";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "nom";
+            packageId = "nom";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "io-util" "macros" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+        ];
+        features = {
+          "async" = [ "tokio" ];
+          "pin-project-lite" = [ "dep:pin-project-lite" ];
+          "tokio" = [ "dep:tokio" ];
+          "wire" = [ "tokio" "pin-project-lite" ];
+        };
+        resolvedDefaultFeatures = [ "pin-project-lite" "tokio" "wire" ];
+      };
+      "nom" = rec {
+        crateName = "nom";
+        version = "7.1.3";
+        edition = "2018";
+        sha256 = "0jha9901wxam390jcf5pfa0qqfrgh8li787jx2ip0yk5b8y9hwyj";
+        authors = [
+          "contact@geoffroycouprie.com"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "minimal-lexical";
+            packageId = "minimal-lexical";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" "memchr/std" "minimal-lexical/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "nu-ansi-term" = rec {
+        crateName = "nu-ansi-term";
+        version = "0.46.0";
+        edition = "2018";
+        sha256 = "115sywxh53p190lyw97alm14nc004qj5jm5lvdj608z84rbida3p";
+        authors = [
+          "ogham@bsago.me"
+          "Ryan Scheel (Havvy) <ryan.havvy@gmail.com>"
+          "Josh Triplett <josh@joshtriplett.org>"
+          "The Nushell Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "overload";
+            packageId = "overload";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: ("windows" == target."os" or null);
+            features = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "processenv" ];
+          }
+        ];
+        features = {
+          "derive_serde_style" = [ "serde" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "num-traits" = rec {
+        crateName = "num-traits";
+        version = "0.2.18";
+        edition = "2018";
+        sha256 = "0yjib8p2p9kzmaz48xwhs69w5dh1wipph9jgnillzd2x33jz03fs";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "libm" = [ "dep:libm" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "num_cpus" = rec {
+        crateName = "num_cpus";
+        version = "1.16.0";
+        edition = "2015";
+        sha256 = "0hra6ihpnh06dvfvz9ipscys0xfqa9ca9hzp384d5m02ssvgqqa1";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(target."windows" or false));
+          }
+        ];
+
+      };
+      "object" = rec {
+        crateName = "object";
+        version = "0.32.2";
+        edition = "2018";
+        sha256 = "0hc4cjwyngiy6k51hlzrlsxgv5z25vv7c2cp0ky1lckfic0259m6";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "all" = [ "read" "write" "std" "compression" "wasm" ];
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "compression" = [ "dep:flate2" "dep:ruzstd" "std" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "read" "compression" ];
+          "doc" = [ "read_core" "write_std" "std" "compression" "archive" "coff" "elf" "macho" "pe" "wasm" "xcoff" ];
+          "pe" = [ "coff" ];
+          "read" = [ "read_core" "archive" "coff" "elf" "macho" "pe" "xcoff" "unaligned" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "alloc" "memchr/rustc-dep-of-std" ];
+          "std" = [ "memchr/std" ];
+          "unstable-all" = [ "all" "unstable" ];
+          "wasm" = [ "dep:wasmparser" ];
+          "write" = [ "write_std" "coff" "elf" "macho" "pe" "xcoff" ];
+          "write_core" = [ "dep:crc32fast" "dep:indexmap" "dep:hashbrown" ];
+          "write_std" = [ "write_core" "std" "indexmap?/std" "crc32fast?/std" ];
+        };
+        resolvedDefaultFeatures = [ "archive" "coff" "elf" "macho" "pe" "read_core" "unaligned" ];
+      };
+      "once_cell" = rec {
+        crateName = "once_cell";
+        version = "1.19.0";
+        edition = "2021";
+        sha256 = "14kvw7px5z96dk4dwdm1r9cqhhy2cyj1l5n5b29mynbb8yr15nrz";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        features = {
+          "alloc" = [ "race" ];
+          "atomic-polyfill" = [ "critical-section" ];
+          "critical-section" = [ "dep:critical-section" "portable-atomic" ];
+          "default" = [ "std" ];
+          "parking_lot" = [ "dep:parking_lot_core" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "race" "std" ];
+      };
+      "overload" = rec {
+        crateName = "overload";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "0fdgbaqwknillagy1xq7xfgv60qdbk010diwl7s1p0qx7hb16n5i";
+        authors = [
+          "Daniel Salvadori <danaugrs@gmail.com>"
+        ];
+
+      };
+      "parking_lot" = rec {
+        crateName = "parking_lot";
+        version = "0.12.1";
+        edition = "2018";
+        sha256 = "13r2xk7mnxfc5g0g6dkdxqdqad99j7s7z8zhzz4npw5r0g0v4hip";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lock_api";
+            packageId = "lock_api";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core";
+          }
+        ];
+        features = {
+          "arc_lock" = [ "lock_api/arc_lock" ];
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot_core" = rec {
+        crateName = "parking_lot_core";
+        version = "0.9.9";
+        edition = "2018";
+        sha256 = "13h0imw1aq86wj28gxkblhkzx6z1gk8q18n0v76qmmj6cliajhjc";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "percent-encoding" = rec {
+        crateName = "percent-encoding";
+        version = "2.3.1";
+        edition = "2018";
+        sha256 = "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573";
+        authors = [
+          "The rust-url developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "pin-project" = rec {
+        crateName = "pin-project";
+        version = "1.1.5";
+        edition = "2021";
+        sha256 = "1cxl146x0q7lawp0m1826wsgj8mmmfs6ja8q7m6f7ff5j6vl7gxn";
+        dependencies = [
+          {
+            name = "pin-project-internal";
+            packageId = "pin-project-internal";
+          }
+        ];
+
+      };
+      "pin-project-internal" = rec {
+        crateName = "pin-project-internal";
+        version = "1.1.5";
+        edition = "2021";
+        sha256 = "0r9r4ivwiyqf45sv6b30l1dx282lxaax2f6gl84jwa3q590s8f1g";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "pin-project-lite" = rec {
+        crateName = "pin-project-lite";
+        version = "0.2.13";
+        edition = "2018";
+        sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+
+      };
+      "pin-utils" = rec {
+        crateName = "pin-utils";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        authors = [
+          "Josef Brandl <mail@josefbrandl.de>"
+        ];
+
+      };
+      "pkcs8" = rec {
+        crateName = "pkcs8";
+        version = "0.10.2";
+        edition = "2021";
+        sha256 = "1dx7w21gvn07azszgqd3ryjhyphsrjrmq5mmz1fbxkj5g0vv4l7r";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+          {
+            name = "spki";
+            packageId = "spki";
+          }
+        ];
+        features = {
+          "3des" = [ "encryption" "pkcs5/3des" ];
+          "alloc" = [ "der/alloc" "der/zeroize" "spki/alloc" ];
+          "des-insecure" = [ "encryption" "pkcs5/des-insecure" ];
+          "encryption" = [ "alloc" "pkcs5/alloc" "pkcs5/pbes2" "rand_core" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "pem" = [ "alloc" "der/pem" "spki/pem" ];
+          "pkcs5" = [ "dep:pkcs5" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "sha1-insecure" = [ "encryption" "pkcs5/sha1-insecure" ];
+          "std" = [ "alloc" "der/std" "spki/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "platforms" = rec {
+        crateName = "platforms";
+        version = "3.3.0";
+        edition = "2018";
+        sha256 = "0k7q6pigmnvgpfasvssb12m2pv3pc94zrhrfg9by3h3wmhyfqvb2";
+        authors = [
+          "Tony Arcieri <bascule@gmail.com>"
+          "Sergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "proc-macro2" = rec {
+        crateName = "proc-macro2";
+        version = "1.0.79";
+        edition = "2021";
+        sha256 = "0bn004ybzdqid81cqppr5c9jrvqsxv50x60sxc41cwpmk0igydg8";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "quote" = rec {
+        crateName = "quote";
+        version = "1.0.35";
+        edition = "2018";
+        sha256 = "1vv8r2ncaz4pqdr78x7f138ka595sp2ncr1sa2plm4zxbsmwj7i9";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "rand_core" = rec {
+        crateName = "rand_core";
+        version = "0.6.4";
+        edition = "2018";
+        sha256 = "0b4j2v4cb5krak1pv6kakv4sz6xcwbrmy2zckc32hsigbrwy82zc";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+        ];
+        features = {
+          "getrandom" = [ "dep:getrandom" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "alloc" "getrandom" "getrandom/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "getrandom" "std" ];
+      };
+      "redox_syscall" = rec {
+        crateName = "redox_syscall";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1aiifyz5dnybfvkk4cdab9p2kmphag1yad6iknc7aszlxxldf8j7";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+        features = {
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "bitflags/rustc-dep-of-std" ];
+        };
+      };
+      "regex-automata" = rec {
+        crateName = "regex-automata";
+        version = "0.4.6";
+        edition = "2021";
+        sha256 = "1spaq7y4im7s56d1gxa2hi4hzf6dwswb1bv8xyavzya7k25kpf46";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" "syntax" "perf" "unicode" "meta" "nfa" "dfa" "hybrid" ];
+          "dfa" = [ "dfa-build" "dfa-search" "dfa-onepass" ];
+          "dfa-build" = [ "nfa-thompson" "dfa-search" ];
+          "dfa-onepass" = [ "nfa-thompson" ];
+          "hybrid" = [ "alloc" "nfa-thompson" ];
+          "internal-instrument" = [ "internal-instrument-pikevm" ];
+          "internal-instrument-pikevm" = [ "logging" "std" ];
+          "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ];
+          "meta" = [ "syntax" "nfa-pikevm" ];
+          "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ];
+          "nfa-backtrack" = [ "nfa-thompson" ];
+          "nfa-pikevm" = [ "nfa-thompson" ];
+          "nfa-thompson" = [ "alloc" ];
+          "perf" = [ "perf-inline" "perf-literal" ];
+          "perf-literal" = [ "perf-literal-substring" "perf-literal-multisubstring" ];
+          "perf-literal-multisubstring" = [ "std" "dep:aho-corasick" ];
+          "perf-literal-substring" = [ "aho-corasick?/perf-literal" "dep:memchr" ];
+          "std" = [ "regex-syntax?/std" "memchr?/std" "aho-corasick?/std" "alloc" ];
+          "syntax" = [ "dep:regex-syntax" "alloc" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" "regex-syntax?/unicode" ];
+          "unicode-age" = [ "regex-syntax?/unicode-age" ];
+          "unicode-bool" = [ "regex-syntax?/unicode-bool" ];
+          "unicode-case" = [ "regex-syntax?/unicode-case" ];
+          "unicode-gencat" = [ "regex-syntax?/unicode-gencat" ];
+          "unicode-perl" = [ "regex-syntax?/unicode-perl" ];
+          "unicode-script" = [ "regex-syntax?/unicode-script" ];
+          "unicode-segment" = [ "regex-syntax?/unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "dfa-search" ];
+      };
+      "rustc-demangle" = rec {
+        crateName = "rustc-demangle";
+        version = "0.1.23";
+        edition = "2015";
+        sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "rustc_version" = rec {
+        crateName = "rustc_version";
+        version = "0.4.0";
+        edition = "2018";
+        sha256 = "0rpk9rcdk405xhbmgclsh4pai0svn49x35aggl4nhbkd4a2zb85z";
+        authors = [
+          "Dirkjan Ochtman <dirkjan@ochtman.nl>"
+          "Marvin LΓΆbel <loebel.marvin@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "semver";
+            packageId = "semver";
+          }
+        ];
+
+      };
+      "rustversion" = rec {
+        crateName = "rustversion";
+        version = "1.0.14";
+        edition = "2018";
+        sha256 = "1x1pz1yynk5xzzrazk2svmidj69jhz89dz5vrc28sixl20x1iz3z";
+        procMacro = true;
+        build = "build/build.rs";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "ryu" = rec {
+        crateName = "ryu";
+        version = "1.0.17";
+        edition = "2018";
+        sha256 = "188vrsh3zlnl5xl7lw0rp2sc0knpx8yaqpwvr648b6h12v4rfrp8";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "scopeguard" = rec {
+        crateName = "scopeguard";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "0jcz9sd47zlsgcnm1hdw0664krxwb5gczlif4qngj2aif8vky54l";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+        };
+      };
+      "semver" = rec {
+        crateName = "semver";
+        version = "1.0.22";
+        edition = "2018";
+        sha256 = "1jir6q2ps4s5v52bqxpvwj35p0m0ahl5pf62ppwksbv5kvk3zm4j";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "serde" = rec {
+        crateName = "serde";
+        version = "1.0.197";
+        edition = "2018";
+        sha256 = "1qjcxqd3p4yh5cmmax9q4ics1zy34j5ij32cvjj5dc5rw5rwic9z";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            optional = true;
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "derive" "serde_derive" "std" ];
+      };
+      "serde_derive" = rec {
+        crateName = "serde_derive";
+        version = "1.0.197";
+        edition = "2015";
+        sha256 = "02v1x0sdv8qy06lpr6by4ar1n3jz3hmab15cgimpzhgd895v7c3y";
+        procMacro = true;
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            usesDefaultFeatures = false;
+            features = [ "clone-impls" "derive" "parsing" "printing" "proc-macro" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "serde_json" = rec {
+        crateName = "serde_json";
+        version = "1.0.114";
+        edition = "2021";
+        sha256 = "1q4saigxwkf8bw4y5kp6k33dnavlvvwa2q4zmag59vrjsqdrpw65";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" ];
+          "default" = [ "std" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "preserve_order" = [ "indexmap" "std" ];
+          "std" = [ "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "raw_value" "std" ];
+      };
+      "serde_path_to_error" = rec {
+        crateName = "serde_path_to_error";
+        version = "0.1.16";
+        edition = "2021";
+        sha256 = "19hlz2359l37ifirskpcds7sxg0gzpqvfilibs7whdys0128i6dg";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+        ];
+
+      };
+      "serde_urlencoded" = rec {
+        crateName = "serde_urlencoded";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1zgklbdaysj3230xivihs30qi5vkhigg323a9m62k8jwf4a1qjfk";
+        authors = [
+          "Anthony Ramine <n.oxyde@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "form_urlencoded";
+            packageId = "form_urlencoded";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+        ];
+
+      };
+      "sha2" = rec {
+        crateName = "sha2";
+        version = "0.10.8";
+        edition = "2018";
+        sha256 = "1j1x78zk9il95w9iv46dh9wm73r6xrgj32y6lzzw7bxws9dbfgbr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha2-asm" ];
+          "asm-aarch64" = [ "asm" ];
+          "default" = [ "std" ];
+          "oid" = [ "digest/oid" ];
+          "sha2-asm" = [ "dep:sha2-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sharded-slab" = rec {
+        crateName = "sharded-slab";
+        version = "0.1.7";
+        edition = "2018";
+        sha256 = "1xipjr4nqsgw34k7a2cgj9zaasl2ds6jwn89886kww93d32a637l";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+        ];
+        dependencies = [
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+        ];
+        features = {
+          "loom" = [ "dep:loom" ];
+        };
+      };
+      "signal-hook-registry" = rec {
+        crateName = "signal-hook-registry";
+        version = "1.4.1";
+        edition = "2015";
+        sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        authors = [
+          "Michal 'vorner' Vaner <vorner@vorner.cz>"
+          "Masaki Hara <ackie.h.gmai@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "signature" = rec {
+        crateName = "signature";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "1pi9hd5vqfr3q3k49k37z06p7gs5si0in32qia4mmr1dancr6m3p";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "derive" = [ "dep:derive" ];
+          "digest" = [ "dep:digest" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "std" = [ "alloc" "rand_core?/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "slab" = rec {
+        crateName = "slab";
+        version = "0.4.9";
+        edition = "2018";
+        sha256 = "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "smallvec" = rec {
+        crateName = "smallvec";
+        version = "1.13.1";
+        edition = "2018";
+        sha256 = "1mzk9j117pn3k1gabys0b7nz8cdjsx5xc6q7fwnm8r0an62d7v76";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "const_new" = [ "const_generics" ];
+          "drain_keep_rest" = [ "drain_filter" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "const_generics" "const_new" ];
+      };
+      "socket2" = rec {
+        crateName = "socket2";
+        version = "0.5.6";
+        edition = "2021";
+        sha256 = "0w98g7dh9m74vpxln401hl4knpjzrx7jhng7cbh46x9vm70dkzq5";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "all" ];
+      };
+      "spki" = rec {
+        crateName = "spki";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "17fj8k5fmx4w9mp27l970clrh5qa7r5sjdvbsln987xhb34dc7nr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "base64ct";
+            packageId = "base64ct";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "base64ct?/alloc" "der/alloc" ];
+          "arbitrary" = [ "std" "dep:arbitrary" "der/arbitrary" ];
+          "base64" = [ "dep:base64ct" ];
+          "fingerprint" = [ "sha2" ];
+          "pem" = [ "alloc" "der/pem" ];
+          "sha2" = [ "dep:sha2" ];
+          "std" = [ "der/std" "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "strsim" = rec {
+        crateName = "strsim";
+        version = "0.11.0";
+        edition = "2015";
+        sha256 = "00gsdp2x1gkkxsbjxgrjyil2hsbdg49bwv8q2y1f406dwk4p7q2y";
+        authors = [
+          "Danny Guo <danny@dannyguo.com>"
+          "maxbachmann <oss@maxbachmann.de>"
+        ];
+
+      };
+      "subtle" = rec {
+        crateName = "subtle";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "1g2yjs7gffgmdvkkq0wrrh0pxds3q0dv6dhkw9cdpbib656xdkc1";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        features = {
+          "default" = [ "std" "i128" ];
+        };
+      };
+      "syn" = rec {
+        crateName = "syn";
+        version = "2.0.52";
+        edition = "2021";
+        sha256 = "01saay6pi9x19f6lin3mw3xawdyyagpzzy39ghz2rw6i6rdx36dn";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit-mut" ];
+      };
+      "sync_wrapper" = rec {
+        crateName = "sync_wrapper";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "0q01lyj0gr9a93n10nxsn8lwbzq97jqd6b768x17c8f7v7gccir0";
+        authors = [
+          "Actyx AG <developer@actyx.io>"
+        ];
+        features = {
+          "futures" = [ "futures-core" ];
+          "futures-core" = [ "dep:futures-core" ];
+        };
+      };
+      "thiserror" = rec {
+        crateName = "thiserror";
+        version = "1.0.58";
+        edition = "2021";
+        sha256 = "15rjgd1abi2mzjgzfhrvmsxf9h65n95h6sp8f4s52q4i00wqhih3";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "thiserror-impl";
+            packageId = "thiserror-impl";
+          }
+        ];
+
+      };
+      "thiserror-impl" = rec {
+        crateName = "thiserror-impl";
+        version = "1.0.58";
+        edition = "2021";
+        sha256 = "1xylyqcb8rv5yh2yf97hg4n4kg27qccc0ijafr1zqklrhahkn7y6";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+          }
+        ];
+
+      };
+      "thread_local" = rec {
+        crateName = "thread_local";
+        version = "1.1.8";
+        edition = "2021";
+        sha256 = "173i5lyjh011gsimk21np9jn8al18rxsrkjli20a7b8ks2xgk7lb";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+        ];
+        features = { };
+      };
+      "tokio" = rec {
+        crateName = "tokio";
+        version = "1.36.0";
+        edition = "2021";
+        sha256 = "0c89p36zbd4abr1z3l5mipp43x7z4c9b4vp4s6r8y0gs2mjmya31";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            target = { target, features }: (target."tokio_taskdump" or false);
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+            optional = true;
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "signal-hook-registry";
+            packageId = "signal-hook-registry";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+            features = [ "all" ];
+          }
+          {
+            name = "tokio-macros";
+            packageId = "tokio-macros";
+            optional = true;
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Security_Authorization" ];
+          }
+        ];
+        features = {
+          "bytes" = [ "dep:bytes" ];
+          "full" = [ "fs" "io-util" "io-std" "macros" "net" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "sync" "time" ];
+          "io-util" = [ "bytes" ];
+          "libc" = [ "dep:libc" ];
+          "macros" = [ "tokio-macros" ];
+          "mio" = [ "dep:mio" ];
+          "net" = [ "libc" "mio/os-poll" "mio/os-ext" "mio/net" "socket2" "windows-sys/Win32_Foundation" "windows-sys/Win32_Security" "windows-sys/Win32_Storage_FileSystem" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_System_SystemServices" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "process" = [ "bytes" "libc" "mio/os-poll" "mio/os-ext" "mio/net" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Threading" "windows-sys/Win32_System_WindowsProgramming" ];
+          "rt-multi-thread" = [ "num_cpus" "rt" ];
+          "signal" = [ "libc" "mio/os-poll" "mio/net" "mio/os-ext" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Console" ];
+          "signal-hook-registry" = [ "dep:signal-hook-registry" ];
+          "socket2" = [ "dep:socket2" ];
+          "test-util" = [ "rt" "sync" "time" ];
+          "tokio-macros" = [ "dep:tokio-macros" ];
+          "tracing" = [ "dep:tracing" ];
+          "windows-sys" = [ "dep:windows-sys" ];
+        };
+        resolvedDefaultFeatures = [ "bytes" "default" "fs" "full" "io-std" "io-util" "libc" "macros" "mio" "net" "num_cpus" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "test-util" "time" "tokio-macros" "windows-sys" ];
+      };
+      "tokio-listener" = rec {
+        crateName = "tokio-listener";
+        version = "0.3.2";
+        edition = "2021";
+        sha256 = "00vkr1cywd2agn8jbkzwwf7y4ps3cfjm8l9ab697px2cgc97wdln";
+        dependencies = [
+          {
+            name = "axum";
+            packageId = "axum";
+            rename = "axum07";
+          }
+          {
+            name = "document-features";
+            packageId = "document-features";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+          }
+          {
+            name = "nix";
+            packageId = "nix";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+            features = [ "user" "fs" ];
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            features = [ "all" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "net" "io-std" "time" "sync" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            optional = true;
+            features = [ "net" "codec" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "rt" "io-util" ];
+          }
+        ];
+        features = {
+          "axum07" = [ "dep:hyper1" "dep:hyper-util" "dep:futures-util" "dep:tower-service" "dep:tower" ];
+          "clap" = [ "dep:clap" ];
+          "default" = [ "user_facing_default" "tokio-util" ];
+          "hyper014" = [ "dep:hyper014" ];
+          "inetd" = [ "dep:futures-util" ];
+          "nix" = [ "dep:nix" ];
+          "serde" = [ "dep:serde" "serde_with" ];
+          "serde_with" = [ "dep:serde_with" ];
+          "socket2" = [ "dep:socket2" ];
+          "socket_options" = [ "socket2" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "tonic010" = [ "dep:tonic_010" ];
+          "tonic011" = [ "dep:tonic" ];
+          "unix_path_tools" = [ "nix" ];
+          "user_facing_default" = [ "inetd" "unix" "unix_path_tools" "sd_listen" "socket_options" ];
+        };
+        resolvedDefaultFeatures = [ "default" "inetd" "nix" "sd_listen" "socket2" "socket_options" "tokio-util" "unix" "unix_path_tools" "user_facing_default" ];
+      };
+      "tokio-macros" = rec {
+        crateName = "tokio-macros";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-stream" = rec {
+        crateName = "tokio-stream";
+        version = "0.1.14";
+        edition = "2021";
+        sha256 = "0hi8hcwavh5sdi1ivc9qc4yvyr32f153c212dpd7sb366y6rhz1r";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" "test-util" ];
+          }
+        ];
+        features = {
+          "default" = [ "time" ];
+          "fs" = [ "tokio/fs" ];
+          "full" = [ "time" "net" "io-util" "fs" "sync" "signal" ];
+          "io-util" = [ "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "signal" = [ "tokio/signal" ];
+          "sync" = [ "tokio/sync" "tokio-util" ];
+          "time" = [ "tokio/time" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+        };
+        resolvedDefaultFeatures = [ "default" "time" ];
+      };
+      "tokio-test" = rec {
+        crateName = "tokio-test";
+        version = "0.4.4";
+        edition = "2021";
+        sha256 = "1xzri2m3dg8nzdyznm77nymvil9cyh1gfdfrbnska51iqfmvls14";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt" "sync" "time" "test-util" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-util" = rec {
+        crateName = "tokio-util";
+        version = "0.7.10";
+        edition = "2021";
+        sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "__docs_rs" = [ "futures-util" ];
+          "codec" = [ "tracing" ];
+          "compat" = [ "futures-io" ];
+          "full" = [ "codec" "compat" "io-util" "time" "net" "rt" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hashbrown" = [ "dep:hashbrown" ];
+          "io-util" = [ "io" "tokio/rt" "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "rt" = [ "tokio/rt" "tokio/sync" "futures-util" "hashbrown" ];
+          "slab" = [ "dep:slab" ];
+          "time" = [ "tokio/time" "slab" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "codec" "default" "io" "net" "tracing" ];
+      };
+      "tower" = rec {
+        crateName = "tower";
+        version = "0.4.13";
+        edition = "2018";
+        sha256 = "073wncyqav4sak1p755hf6vl66njgfc1z1g1di9rxx3cvvh9pymq";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            optional = true;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "sync" ];
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "sync" "test-util" "rt-multi-thread" ];
+          }
+        ];
+        features = {
+          "__common" = [ "futures-core" "pin-project-lite" ];
+          "balance" = [ "discover" "load" "ready-cache" "make" "rand" "slab" ];
+          "buffer" = [ "__common" "tokio/sync" "tokio/rt" "tokio-util" "tracing" ];
+          "default" = [ "log" ];
+          "discover" = [ "__common" ];
+          "filter" = [ "__common" "futures-util" ];
+          "full" = [ "balance" "buffer" "discover" "filter" "hedge" "limit" "load" "load-shed" "make" "ready-cache" "reconnect" "retry" "spawn-ready" "steer" "timeout" "util" ];
+          "futures-core" = [ "dep:futures-core" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hdrhistogram" = [ "dep:hdrhistogram" ];
+          "hedge" = [ "util" "filter" "futures-util" "hdrhistogram" "tokio/time" "tracing" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "limit" = [ "__common" "tokio/time" "tokio/sync" "tokio-util" "tracing" ];
+          "load" = [ "__common" "tokio/time" "tracing" ];
+          "load-shed" = [ "__common" ];
+          "log" = [ "tracing/log" ];
+          "make" = [ "futures-util" "pin-project-lite" "tokio/io-std" ];
+          "pin-project" = [ "dep:pin-project" ];
+          "pin-project-lite" = [ "dep:pin-project-lite" ];
+          "rand" = [ "dep:rand" ];
+          "ready-cache" = [ "futures-core" "futures-util" "indexmap" "tokio/sync" "tracing" "pin-project-lite" ];
+          "reconnect" = [ "make" "tokio/io-std" "tracing" ];
+          "retry" = [ "__common" "tokio/time" ];
+          "slab" = [ "dep:slab" ];
+          "spawn-ready" = [ "__common" "futures-util" "tokio/sync" "tokio/rt" "util" "tracing" ];
+          "timeout" = [ "pin-project-lite" "tokio/time" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-stream" = [ "dep:tokio-stream" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "tracing" = [ "dep:tracing" ];
+          "util" = [ "__common" "futures-util" "pin-project" ];
+        };
+        resolvedDefaultFeatures = [ "__common" "futures-core" "futures-util" "log" "make" "pin-project" "pin-project-lite" "tokio" "tracing" "util" ];
+      };
+      "tower-layer" = rec {
+        crateName = "tower-layer";
+        version = "0.3.2";
+        edition = "2018";
+        sha256 = "1l7i17k9vlssrdg4s3b0ia5jjkmmxsvv8s9y9ih0jfi8ssz8s362";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+
+      };
+      "tower-service" = rec {
+        crateName = "tower-service";
+        version = "0.3.2";
+        edition = "2018";
+        sha256 = "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+
+      };
+      "tracing" = rec {
+        crateName = "tracing";
+        version = "0.1.40";
+        edition = "2018";
+        sha256 = "1vv48dac9zgj9650pg2b4d0j3w6f3x9gbggf43scq5hrlysklln3";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tracing-attributes";
+            packageId = "tracing-attributes";
+            optional = true;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "attributes" = [ "tracing-attributes" ];
+          "default" = [ "std" "attributes" ];
+          "log" = [ "dep:log" ];
+          "log-always" = [ "log" ];
+          "std" = [ "tracing-core/std" ];
+          "tracing-attributes" = [ "dep:tracing-attributes" ];
+          "valuable" = [ "tracing-core/valuable" ];
+        };
+        resolvedDefaultFeatures = [ "attributes" "default" "log" "std" "tracing-attributes" ];
+      };
+      "tracing-attributes" = rec {
+        crateName = "tracing-attributes";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <dbarsky@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            usesDefaultFeatures = false;
+            features = [ "full" "parsing" "printing" "visit-mut" "clone-impls" "extra-traits" "proc-macro" ];
+          }
+        ];
+        features = { };
+      };
+      "tracing-core" = rec {
+        crateName = "tracing-core";
+        version = "0.1.32";
+        edition = "2018";
+        sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+          }
+          {
+            name = "valuable";
+            packageId = "valuable";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."tracing_unstable" or false);
+          }
+        ];
+        features = {
+          "default" = [ "std" "valuable/std" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "std" = [ "once_cell" ];
+          "valuable" = [ "dep:valuable" ];
+        };
+        resolvedDefaultFeatures = [ "default" "once_cell" "std" "valuable" ];
+      };
+      "tracing-log" = rec {
+        crateName = "tracing-log";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "1hs77z026k730ij1a9dhahzrl0s073gfa2hm5p0fbl0b80gmz1gf";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+          }
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "default" = [ "log-tracer" "std" ];
+          "interest-cache" = [ "lru" "ahash" ];
+          "lru" = [ "dep:lru" ];
+          "std" = [ "log/std" ];
+        };
+        resolvedDefaultFeatures = [ "log-tracer" "std" ];
+      };
+      "tracing-subscriber" = rec {
+        crateName = "tracing-subscriber";
+        version = "0.3.18";
+        edition = "2018";
+        sha256 = "12vs1bwk4kig1l2qqjbbn2nm5amwiqmkcmnznylzmnfvjy6083xd";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <me@davidbarsky.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "nu-ansi-term";
+            packageId = "nu-ansi-term";
+            optional = true;
+          }
+          {
+            name = "sharded-slab";
+            packageId = "sharded-slab";
+            optional = true;
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+            optional = true;
+          }
+          {
+            name = "thread_local";
+            packageId = "thread_local";
+            optional = true;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tracing-log";
+            packageId = "tracing-log";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "log-tracer" "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tracing-log";
+            packageId = "tracing-log";
+          }
+        ];
+        features = {
+          "ansi" = [ "fmt" "nu-ansi-term" ];
+          "chrono" = [ "dep:chrono" ];
+          "default" = [ "smallvec" "fmt" "ansi" "tracing-log" "std" ];
+          "env-filter" = [ "matchers" "regex" "once_cell" "tracing" "std" "thread_local" ];
+          "fmt" = [ "registry" "std" ];
+          "json" = [ "tracing-serde" "serde" "serde_json" ];
+          "local-time" = [ "time/local-offset" ];
+          "matchers" = [ "dep:matchers" ];
+          "nu-ansi-term" = [ "dep:nu-ansi-term" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "regex" = [ "dep:regex" ];
+          "registry" = [ "sharded-slab" "thread_local" "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "sharded-slab" = [ "dep:sharded-slab" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "alloc" "tracing-core/std" ];
+          "thread_local" = [ "dep:thread_local" ];
+          "time" = [ "dep:time" ];
+          "tracing" = [ "dep:tracing" ];
+          "tracing-log" = [ "dep:tracing-log" ];
+          "tracing-serde" = [ "dep:tracing-serde" ];
+          "valuable" = [ "tracing-core/valuable" "valuable_crate" "valuable-serde" "tracing-serde/valuable" ];
+          "valuable-serde" = [ "dep:valuable-serde" ];
+          "valuable_crate" = [ "dep:valuable_crate" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "ansi" "default" "fmt" "nu-ansi-term" "registry" "sharded-slab" "smallvec" "std" "thread_local" "tracing-log" ];
+      };
+      "tvix-daemon" = rec {
+        crateName = "tvix-daemon";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "tvix-daemon";
+            path = "src/main.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./.; }
+          else ./.;
+        dependencies = [
+          {
+            name = "clap";
+            packageId = "clap";
+            features = [ "derive" "env" ];
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+            features = [ "wire" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+          {
+            name = "tokio-listener";
+            packageId = "tokio-listener";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "tracing-subscriber";
+            packageId = "tracing-subscriber";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio-test";
+            packageId = "tokio-test";
+          }
+        ];
+
+      };
+      "typenum" = rec {
+        crateName = "typenum";
+        version = "1.17.0";
+        edition = "2018";
+        sha256 = "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2";
+        build = "build/main.rs";
+        authors = [
+          "Paho Lurie-Gregg <paho@paholg.com>"
+          "Andre Bogus <bogusandre@gmail.com>"
+        ];
+        features = {
+          "scale-info" = [ "dep:scale-info" ];
+          "scale_info" = [ "scale-info/derive" ];
+        };
+      };
+      "unicode-ident" = rec {
+        crateName = "unicode-ident";
+        version = "1.0.12";
+        edition = "2018";
+        sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "utf8parse" = rec {
+        crateName = "utf8parse";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "02ip1a0az0qmc2786vxk2nqwsgcwf17d3a38fkf0q7hrmwh9c6vi";
+        authors = [
+          "Joe Wilm <joe@jwilm.com>"
+          "Christian Duerr <contact@christianduerr.com>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "valuable" = rec {
+        crateName = "valuable";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "0v9gp3nkjbl30z0fd56d8mx7w1csk86wwjhfjhr400wh9mfpw2w3";
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "valuable-derive" ];
+          "std" = [ "alloc" ];
+          "valuable-derive" = [ "dep:valuable-derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "version_check" = rec {
+        crateName = "version_check";
+        version = "0.9.4";
+        edition = "2015";
+        sha256 = "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9";
+        authors = [
+          "Sergio Benitez <sb@sergio.bz>"
+        ];
+
+      };
+      "wasi" = rec {
+        crateName = "wasi";
+        version = "0.11.0+wasi-snapshot-preview1";
+        edition = "2018";
+        sha256 = "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw";
+        authors = [
+          "The Cranelift Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "rustc-std-workspace-alloc" ];
+          "rustc-std-workspace-alloc" = [ "dep:rustc-std-workspace-alloc" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "winapi" = rec {
+        crateName = "winapi";
+        version = "0.3.9";
+        edition = "2015";
+        sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-i686-pc-windows-gnu";
+            packageId = "winapi-i686-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+          }
+          {
+            name = "winapi-x86_64-pc-windows-gnu";
+            packageId = "winapi-x86_64-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+          }
+        ];
+        features = {
+          "debug" = [ "impl-debug" ];
+        };
+        resolvedDefaultFeatures = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "processenv" ];
+      };
+      "winapi-i686-pc-windows-gnu" = rec {
+        crateName = "winapi-i686-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi-x86_64-pc-windows-gnu" = rec {
+        crateName = "winapi-x86_64-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "windows-sys 0.48.0" = rec {
+        crateName = "windows-sys";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+          }
+        ];
+        features = {
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_IO" "Win32_System_Pipes" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_WindowsProgramming" "default" ];
+      };
+      "windows-sys 0.52.0" = rec {
+        crateName = "windows-sys";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.4";
+          }
+        ];
+        features = {
+          "Wdk_Foundation" = [ "Wdk" ];
+          "Wdk_Graphics" = [ "Wdk" ];
+          "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ];
+          "Wdk_Storage" = [ "Wdk" ];
+          "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ];
+          "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ];
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_IO" = [ "Wdk_System" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Wdk_System_Registry" = [ "Wdk_System" ];
+          "Wdk_System_SystemInformation" = [ "Wdk_System" ];
+          "Wdk_System_SystemServices" = [ "Wdk_System" ];
+          "Wdk_System_Threading" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_Nvme" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_Variant" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_System" "Win32_System_Console" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" "default" ];
+      };
+      "windows-targets 0.48.5" = rec {
+        crateName = "windows-targets";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.48.5";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows-targets 0.52.4" = rec {
+        crateName = "windows-targets";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "06sdd7fin3dj9cmlg6n1dw0n1l10jhn9b8ckz1cqf0drb9z7plvx";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.52.4";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.52.4";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.52.4";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.52.4";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.52.4";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.52.4";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.52.4";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.48.5" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.52.4" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "1jfam5qfngg8v1syxklnvy8la94b5igm7klkrk8z5ik5qgs6rx5w";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.48.5" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.52.4" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "0xdn6db0rk8idn7dxsyflixq2dbj9x60kzdzal5rkxmwsffjb7ys";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.48.5" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.52.4" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "1lq1g35sbj55ms86by4c080jcqrlfjy9bw5r4mgrkq4riwkdhx5l";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.48.5" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.52.4" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "00lfzw88dkf3fdcf2hpfhp74i9pwbp7rwnj1nhy79vavksifj58m";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.48.5" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.52.4" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "00qs6x33bf9lai2q68faxl56cszbv7mf7zqlslmc1778j0ahkvjy";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.48.5" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.52.4" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "0xr13xxakp14hs4v4hg2ynjcv7wrzr3hg7zk5agglj8v8pr7kjkp";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.48.5" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.52.4" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "1n0yc7xiv9iki1j3xl8nxlwwkr7dzsnwwvycvgxxv81d5bjm5drj";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "zeroize" = rec {
+        crateName = "zeroize";
+        version = "1.7.0";
+        edition = "2021";
+        sha256 = "0bfvby7k9pdp6623p98yz2irqnamcyzpn7zh20nqmdn68b0lwnsj";
+        authors = [
+          "The RustCrypto Project Developers"
+        ];
+        features = {
+          "default" = [ "alloc" ];
+          "derive" = [ "zeroize_derive" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "zeroize_derive" = [ "dep:zeroize_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+    };
+
+    #
+    # crate2nix/default.nix (excerpt start)
+    #
+
+    /* Target (platform) data for conditional dependencies.
+      This corresponds roughly to what buildRustCrate is setting.
+    */
+    makeDefaultTarget = platform: {
+      unix = platform.isUnix;
+      windows = platform.isWindows;
+      fuchsia = true;
+      test = false;
+
+      /* We are choosing an arbitrary rust version to grab `lib` from,
+      which is unfortunate, but `lib` has been version-agnostic the
+      whole time so this is good enough for now.
+      */
+      os = pkgs.rust.lib.toTargetOs platform;
+      arch = pkgs.rust.lib.toTargetArch platform;
+      family = pkgs.rust.lib.toTargetFamily platform;
+      vendor = pkgs.rust.lib.toTargetVendor platform;
+      env = "gnu";
+      endian =
+        if platform.parsed.cpu.significantByte.name == "littleEndian"
+        then "little" else "big";
+      pointer_width = toString platform.parsed.cpu.bits;
+      debug_assertions = false;
+    };
+
+    /* Filters common temp files and build files. */
+    # TODO(pkolloch): Substitute with gitignore filter
+    sourceFilter = name: type:
+      let
+        baseName = builtins.baseNameOf (builtins.toString name);
+      in
+        ! (
+          # Filter out git
+          baseName == ".gitignore"
+          || (type == "directory" && baseName == ".git")
+
+          # Filter out build results
+          || (
+            type == "directory" && (
+              baseName == "target"
+              || baseName == "_site"
+              || baseName == ".sass-cache"
+              || baseName == ".jekyll-metadata"
+              || baseName == "build-artifacts"
+            )
+          )
+
+          # Filter out nix-build result symlinks
+          || (
+            type == "symlink" && lib.hasPrefix "result" baseName
+          )
+
+          # Filter out IDE config
+          || (
+            type == "directory" && (
+              baseName == ".idea" || baseName == ".vscode"
+            )
+          ) || lib.hasSuffix ".iml" baseName
+
+          # Filter out nix build files
+          || baseName == "Cargo.nix"
+
+          # Filter out editor backup / swap files.
+          || lib.hasSuffix "~" baseName
+          || builtins.match "^\\.sw[a-z]$$" baseName != null
+          || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null
+          || lib.hasSuffix ".tmp" baseName
+          || lib.hasSuffix ".bak" baseName
+          || baseName == "tests.nix"
+        );
+
+    /* Returns a crate which depends on successful test execution
+      of crate given as the second argument.
+
+      testCrateFlags: list of flags to pass to the test exectuable
+      testInputs: list of packages that should be available during test execution
+    */
+    crateWithTest = { crate, testCrate, testCrateFlags, testInputs, testPreRun, testPostRun }:
+      assert builtins.typeOf testCrateFlags == "list";
+      assert builtins.typeOf testInputs == "list";
+      assert builtins.typeOf testPreRun == "string";
+      assert builtins.typeOf testPostRun == "string";
+      let
+        # override the `crate` so that it will build and execute tests instead of
+        # building the actual lib and bin targets We just have to pass `--test`
+        # to rustc and it will do the right thing.  We execute the tests and copy
+        # their log and the test executables to $out for later inspection.
+        test =
+          let
+            drv = testCrate.override
+              (
+                _: {
+                  buildTests = true;
+                  release = false;
+                }
+              );
+            # If the user hasn't set any pre/post commands, we don't want to
+            # insert empty lines. This means that any existing users of crate2nix
+            # don't get a spurious rebuild unless they set these explicitly.
+            testCommand = pkgs.lib.concatStringsSep "\n"
+              (pkgs.lib.filter (s: s != "") [
+                testPreRun
+                "$f $testCrateFlags 2>&1 | tee -a $out"
+                testPostRun
+              ]);
+          in
+          pkgs.runCommand "run-tests-${testCrate.name}"
+            {
+              inherit testCrateFlags;
+              buildInputs = testInputs;
+            } ''
+            set -e
+
+            export RUST_BACKTRACE=1
+
+            # recreate a file hierarchy as when running tests with cargo
+
+            # the source for test data
+            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
+            # instead of $NIX_BUILD_TOP/
+            # because we compiled those test binaries in the former and not the latter.
+            # So all paths will expect source tree to be there and not in the build top directly.
+            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
+            # TODO(raitobezarius): I believe there could be more edge cases if `crate.sourceRoot`
+            # do exist but it's very hard to reason about them, so let's wait until the first bug report.
+            mkdir -p source/
+            cd source/
+
+            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+
+            # build outputs
+            testRoot=target/debug
+            mkdir -p $testRoot
+
+            # executables of the crate
+            # we copy to prevent std::env::current_exe() to resolve to a store location
+            for i in ${crate}/bin/*; do
+              cp "$i" "$testRoot"
+            done
+            chmod +w -R .
+
+            # test harness executables are suffixed with a hash, like cargo does
+            # this allows to prevent name collision with the main
+            # executables of the crate
+            hash=$(basename $out)
+            for file in ${drv}/tests/*; do
+              f=$testRoot/$(basename $file)-$hash
+              cp $file $f
+              ${testCommand}
+            done
+          '';
+      in
+      pkgs.runCommand "${crate.name}-linked"
+        {
+          inherit (crate) outputs crateName;
+          passthru = (crate.passthru or { }) // {
+            inherit test;
+          };
+        }
+        (lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+          echo tested by ${test}
+        '' + ''
+          ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs}
+        '');
+
+    /* A restricted overridable version of builtRustCratesWithFeatures. */
+    buildRustCrateWithFeatures =
+      { packageId
+      , features ? rootFeatures
+      , crateOverrides ? defaultCrateOverrides
+      , buildRustCrateForPkgsFunc ? null
+      , runTests ? false
+      , testCrateFlags ? [ ]
+      , testInputs ? [ ]
+        # Any command to run immediatelly before a test is executed.
+      , testPreRun ? ""
+        # Any command run immediatelly after a test is executed.
+      , testPostRun ? ""
+      }:
+      lib.makeOverridable
+        (
+          { features
+          , crateOverrides
+          , runTests
+          , testCrateFlags
+          , testInputs
+          , testPreRun
+          , testPostRun
+          }:
+          let
+            buildRustCrateForPkgsFuncOverriden =
+              if buildRustCrateForPkgsFunc != null
+              then buildRustCrateForPkgsFunc
+              else
+                (
+                  if crateOverrides == pkgs.defaultCrateOverrides
+                  then buildRustCrateForPkgs
+                  else
+                    pkgs: (buildRustCrateForPkgs pkgs).override {
+                      defaultCrateOverrides = crateOverrides;
+                    }
+                );
+            builtRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = false;
+            };
+            builtTestRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = true;
+            };
+            drv = builtRustCrates.crates.${packageId};
+            testDrv = builtTestRustCrates.crates.${packageId};
+            derivation =
+              if runTests then
+                crateWithTest
+                  {
+                    crate = drv;
+                    testCrate = testDrv;
+                    inherit testCrateFlags testInputs testPreRun testPostRun;
+                  }
+              else drv;
+          in
+          derivation
+        )
+        { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun; };
+
+    /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc
+      for the corresponding crate.
+    */
+    builtRustCratesWithFeatures =
+      { packageId
+      , features
+      , crateConfigs ? crates
+      , buildRustCrateForPkgsFunc
+      , runTests
+      , makeTarget ? makeDefaultTarget
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isList features);
+        assert (builtins.isAttrs (makeTarget stdenv.hostPlatform));
+        assert (builtins.isBool runTests);
+        let
+          rootPackageId = packageId;
+          mergedFeatures = mergePackageFeatures
+            (
+              args // {
+                inherit rootPackageId;
+                target = makeTarget stdenv.hostPlatform // { test = runTests; };
+              }
+            );
+          # Memoize built packages so that reappearing packages are only built once.
+          builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs;
+          mkBuiltByPackageIdByPkgs = pkgs:
+            let
+              self = {
+                crates = lib.mapAttrs (packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId) crateConfigs;
+                target = makeTarget pkgs.stdenv.hostPlatform;
+                build = mkBuiltByPackageIdByPkgs pkgs.buildPackages;
+              };
+            in
+            self;
+          buildByPackageIdForPkgsImpl = self: pkgs: packageId:
+            let
+              features = mergedFeatures."${packageId}" or [ ];
+              crateConfig' = crateConfigs."${packageId}";
+              crateConfig =
+                builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ];
+              devDependencies =
+                lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig'.devDependencies or [ ]);
+              dependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self) target;
+                  buildByPackageId = depPackageId:
+                    # proc_macro crates must be compiled for the build architecture
+                    if crateConfigs.${depPackageId}.procMacro or false
+                    then self.build.crates.${depPackageId}
+                    else self.crates.${depPackageId};
+                  dependencies =
+                    (crateConfig.dependencies or [ ])
+                    ++ devDependencies;
+                };
+              buildDependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self.build) target;
+                  buildByPackageId = depPackageId:
+                    self.build.crates.${depPackageId};
+                  dependencies = crateConfig.buildDependencies or [ ];
+                };
+              dependenciesWithRenames =
+                let
+                  buildDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self) target;
+                    dependencies = crateConfig.dependencies or [ ] ++ devDependencies;
+                  };
+                  hostDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self.build) target;
+                    dependencies = crateConfig.buildDependencies or [ ];
+                  };
+                in
+                lib.filter (d: d ? "rename") (hostDeps ++ buildDeps);
+              # Crate renames have the form:
+              #
+              # {
+              #    crate_name = [
+              #       { version = "1.2.3"; rename = "crate_name01"; }
+              #    ];
+              #    # ...
+              # }
+              crateRenames =
+                let
+                  grouped =
+                    lib.groupBy
+                      (dependency: dependency.name)
+                      dependenciesWithRenames;
+                  versionAndRename = dep:
+                    let
+                      package = crateConfigs."${dep.packageId}";
+                    in
+                    { inherit (dep) rename; inherit (package) version; };
+                in
+                lib.mapAttrs (name: builtins.map versionAndRename) grouped;
+            in
+            buildRustCrateForPkgsFunc pkgs
+              (
+                crateConfig // {
+                  # https://github.com/NixOS/nixpkgs/issues/218712
+                  dontStrip = stdenv.hostPlatform.isDarwin;
+                  src = crateConfig.src or (
+                    pkgs.fetchurl rec {
+                      name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz";
+                      # https://www.pietroalbini.org/blog/downloading-crates-io/
+                      # Not rate-limited, CDN URL.
+                      url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate";
+                      sha256 =
+                        assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}");
+                        crateConfig.sha256;
+                    }
+                  );
+                  extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}";
+                  inherit features dependencies buildDependencies crateRenames release;
+                }
+              );
+        in
+        builtByPackageIdByPkgs;
+
+    /* Returns the actual derivations for the given dependencies. */
+    dependencyDerivations =
+      { buildByPackageId
+      , features
+      , dependencies
+      , target
+      }:
+        assert (builtins.isList features);
+        assert (builtins.isList dependencies);
+        assert (builtins.isAttrs target);
+        let
+          enabledDependencies = filterEnabledDependencies {
+            inherit dependencies features target;
+          };
+          depDerivation = dependency: buildByPackageId dependency.packageId;
+        in
+        map depDerivation enabledDependencies;
+
+    /* Returns a sanitized version of val with all values substituted that cannot
+      be serialized as JSON.
+    */
+    sanitizeForJson = val:
+      if builtins.isAttrs val
+      then lib.mapAttrs (n: sanitizeForJson) val
+      else if builtins.isList val
+      then builtins.map sanitizeForJson val
+      else if builtins.isFunction val
+      then "function"
+      else val;
+
+    /* Returns various tools to debug a crate. */
+    debugCrate = { packageId, target ? makeDefaultTarget stdenv.hostPlatform }:
+      assert (builtins.isString packageId);
+      let
+        debug = rec {
+          # The built tree as passed to buildRustCrate.
+          buildTree = buildRustCrateWithFeatures {
+            buildRustCrateForPkgsFunc = _: lib.id;
+            inherit packageId;
+          };
+          sanitizedBuildTree = sanitizeForJson buildTree;
+          dependencyTree = sanitizeForJson
+            (
+              buildRustCrateWithFeatures {
+                buildRustCrateForPkgsFunc = _: crate: {
+                  "01_crateName" = crate.crateName or false;
+                  "02_features" = crate.features or [ ];
+                  "03_dependencies" = crate.dependencies or [ ];
+                };
+                inherit packageId;
+              }
+            );
+          mergedPackageFeatures = mergePackageFeatures {
+            features = rootFeatures;
+            inherit packageId target;
+          };
+          diffedDefaultPackageFeatures = diffDefaultPackageFeatures {
+            inherit packageId target;
+          };
+        };
+      in
+      { internal = debug; };
+
+    /* Returns differences between cargo default features and crate2nix default
+      features.
+
+      This is useful for verifying the feature resolution in crate2nix.
+    */
+    diffDefaultPackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , target
+      }:
+        assert (builtins.isAttrs crateConfigs);
+        let
+          prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; });
+          mergedFeatures =
+            prefixValues
+              "crate2nix"
+              (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; });
+          configs = prefixValues "cargo" crateConfigs;
+          combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ];
+          onlyInCargo =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined);
+          onlyInCrate2Nix =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined);
+          differentFeatures = lib.filterAttrs
+            (
+              n: v:
+                (v ? "crate2nix")
+                && (v ? "cargo")
+                && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ])
+            )
+            combined;
+        in
+        builtins.toJSON {
+          inherit onlyInCargo onlyInCrate2Nix differentFeatures;
+        };
+
+    /* Returns an attrset mapping packageId to the list of enabled features.
+
+      If multiple paths to a dependency enable different features, the
+      corresponding feature sets are merged. Features in rust are additive.
+    */
+    mergePackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , rootPackageId ? packageId
+      , features ? rootFeatures
+      , dependencyPath ? [ crates.${packageId}.crateName ]
+      , featuresByPackageId ? { }
+      , target
+        # Adds devDependencies to the crate with rootPackageId.
+      , runTests ? false
+      , ...
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isString rootPackageId);
+        assert (builtins.isList features);
+        assert (builtins.isList dependencyPath);
+        assert (builtins.isAttrs featuresByPackageId);
+        assert (builtins.isAttrs target);
+        assert (builtins.isBool runTests);
+        let
+          crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
+          expandedFeatures = expandFeatures (crateConfig.features or { }) features;
+          enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
+          depWithResolvedFeatures = dependency:
+            let
+              inherit (dependency) packageId;
+              features = dependencyFeatures enabledFeatures dependency;
+            in
+            { inherit packageId features; };
+          resolveDependencies = cache: path: dependencies:
+            assert (builtins.isAttrs cache);
+            assert (builtins.isList dependencies);
+            let
+              enabledDependencies = filterEnabledDependencies {
+                inherit dependencies target;
+                features = enabledFeatures;
+              };
+              directDependencies = map depWithResolvedFeatures enabledDependencies;
+              foldOverCache = op: lib.foldl op cache directDependencies;
+            in
+            foldOverCache
+              (
+                cache: { packageId, features }:
+                  let
+                    cacheFeatures = cache.${packageId} or [ ];
+                    combinedFeatures = sortedUnique (cacheFeatures ++ features);
+                  in
+                  if cache ? ${packageId} && cache.${packageId} == combinedFeatures
+                  then cache
+                  else
+                    mergePackageFeatures {
+                      features = combinedFeatures;
+                      featuresByPackageId = cache;
+                      inherit crateConfigs packageId target runTests rootPackageId;
+                    }
+              );
+          cacheWithSelf =
+            let
+              cacheFeatures = featuresByPackageId.${packageId} or [ ];
+              combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
+            in
+            featuresByPackageId // {
+              "${packageId}" = combinedFeatures;
+            };
+          cacheWithDependencies =
+            resolveDependencies cacheWithSelf "dep"
+              (
+                crateConfig.dependencies or [ ]
+                ++ lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig.devDependencies or [ ])
+              );
+          cacheWithAll =
+            resolveDependencies
+              cacheWithDependencies "build"
+              (crateConfig.buildDependencies or [ ]);
+        in
+        cacheWithAll;
+
+    /* Returns the enabled dependencies given the enabled features. */
+    filterEnabledDependencies = { dependencies, features, target }:
+      assert (builtins.isList dependencies);
+      assert (builtins.isList features);
+      assert (builtins.isAttrs target);
+
+      lib.filter
+        (
+          dep:
+          let
+            targetFunc = dep.target or (features: true);
+          in
+          targetFunc { inherit features target; }
+          && (
+            !(dep.optional or false)
+            || builtins.any (doesFeatureEnableDependency dep) features
+          )
+        )
+        dependencies;
+
+    /* Returns whether the given feature should enable the given dependency. */
+    doesFeatureEnableDependency = dependency: feature:
+      let
+        name = dependency.rename or dependency.name;
+        prefix = "${name}/";
+        len = builtins.stringLength prefix;
+        startsWithPrefix = builtins.substring 0 len feature == prefix;
+      in
+      feature == name || feature == "dep:" + name || startsWithPrefix;
+
+    /* Returns the expanded features for the given inputFeatures by applying the
+      rules in featureMap.
+
+      featureMap is an attribute set which maps feature names to lists of further
+      feature names to enable in case this feature is selected.
+    */
+    expandFeatures = featureMap: inputFeatures:
+      assert (builtins.isAttrs featureMap);
+      assert (builtins.isList inputFeatures);
+      let
+        expandFeaturesNoCycle = oldSeen: inputFeatures:
+          if inputFeatures != [ ]
+          then
+            let
+              # The feature we're currently expanding.
+              feature = builtins.head inputFeatures;
+              # All the features we've seen/expanded so far, including the one
+              # we're currently processing.
+              seen = oldSeen // { ${feature} = 1; };
+              # Expand the feature but be careful to not re-introduce a feature
+              # that we've already seen: this can easily cause a cycle, see issue
+              # #209.
+              enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]);
+            in
+            [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
+          # No more features left, nothing to expand to.
+          else [ ];
+        outFeatures = expandFeaturesNoCycle { } inputFeatures;
+      in
+      sortedUnique outFeatures;
+
+    /* This function adds optional dependencies as features if they are enabled
+      indirectly by dependency features. This function mimics Cargo's behavior
+      described in a note at:
+      https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
+    */
+    enableFeatures = dependencies: features:
+      assert (builtins.isList features);
+      assert (builtins.isList dependencies);
+      let
+        additionalFeatures = lib.concatMap
+          (
+            dependency:
+              assert (builtins.isAttrs dependency);
+              let
+                enabled = builtins.any (doesFeatureEnableDependency dependency) features;
+              in
+              if (dependency.optional or false) && enabled
+              then [ (dependency.rename or dependency.name) ]
+              else [ ]
+          )
+          dependencies;
+      in
+      sortedUnique (features ++ additionalFeatures);
+
+    /*
+      Returns the actual features for the given dependency.
+
+      features: The features of the crate that refers this dependency.
+    */
+    dependencyFeatures = features: dependency:
+      assert (builtins.isList features);
+      assert (builtins.isAttrs dependency);
+      let
+        defaultOrNil =
+          if dependency.usesDefaultFeatures or true
+          then [ "default" ]
+          else [ ];
+        explicitFeatures = dependency.features or [ ];
+        additionalDependencyFeatures =
+          let
+            name = dependency.rename or dependency.name;
+            stripPrefixMatch = prefix: s:
+              if lib.hasPrefix prefix s
+              then lib.removePrefix prefix s
+              else null;
+            extractFeature = feature: lib.findFirst
+              (f: f != null)
+              null
+              (map (prefix: stripPrefixMatch prefix feature) [
+                (name + "/")
+                (name + "?/")
+              ]);
+            dependencyFeatures = lib.filter (f: f != null) (map extractFeature features);
+          in
+          dependencyFeatures;
+      in
+      defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures;
+
+    /* Sorts and removes duplicates from a list of strings. */
+    sortedUnique = features:
+      assert (builtins.isList features);
+      assert (builtins.all builtins.isString features);
+      let
+        outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features;
+        outFeaturesUnique = builtins.attrNames outFeaturesSet;
+      in
+      builtins.sort (a: b: a < b) outFeaturesUnique;
+
+    deprecationWarning = message: value:
+      if strictDeprecation
+      then builtins.throw "strictDeprecation enabled, aborting: ${message}"
+      else builtins.trace message value;
+
+    #
+    # crate2nix/default.nix (excerpt end)
+    #
+  };
+}
+
diff --git a/users/picnoir/tvix-daemon/Cargo.toml b/users/picnoir/tvix-daemon/Cargo.toml
new file mode 100644
index 0000000000..2aca99f201
--- /dev/null
+++ b/users/picnoir/tvix-daemon/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "tvix-daemon"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+nix-compat = { path = "../../../tvix/nix-compat", features = ["wire"] }
+tokio-listener = "0.3.1"
+tokio = { version = "1.36.0", features = ["full"] }
+tracing-subscriber = "0.3.18"
+tracing = "0.1.40"
+clap = { version = "4.5.3", features = ["derive", "env"] }
+
+[dev-dependencies]
+tokio-test = "0.4.4"
diff --git a/users/picnoir/tvix-daemon/README.md b/users/picnoir/tvix-daemon/README.md
new file mode 100644
index 0000000000..1c65790a22
--- /dev/null
+++ b/users/picnoir/tvix-daemon/README.md
@@ -0,0 +1,16 @@
+# Tvix-daemon
+
+A **super** incomplete implementation of a Nix-compatible daemon. Same as the original except it's backed by Tvix-Store.
+
+For now, this is mostly used as a playground to implement the Nix daemon wire format in nix-compat.
+
+On the long run, I hope this to be useful to get some real-world usage experience of tvix-store.
+
+## Build
+
+When inside this directory:
+
+```sh
+mg shell :shell
+cargo build
+```
diff --git a/users/picnoir/tvix-daemon/default.nix b/users/picnoir/tvix-daemon/default.nix
new file mode 100644
index 0000000000..78b9aa9a1d
--- /dev/null
+++ b/users/picnoir/tvix-daemon/default.nix
@@ -0,0 +1,43 @@
+{ depot, pkgs, ... }:
+
+let
+  crate2nix = pkgs.callPackage ./Cargo.nix {
+    defaultCrateOverrides = {
+      tvix-castore = prev: {
+        PROTO_ROOT = depot.tvix.castore.protos.protos;
+        nativeBuildInputs = protobufDep prev;
+      };
+
+      tvix-store = prev: {
+        PROTO_ROOT = depot.tvix.store.protos.protos;
+        nativeBuildInputs = protobufDep prev;
+      };
+    };
+  };
+  protobufDep = prev: (prev.nativeBuildInputs or [ ]) ++ [ pkgs.buildPackages.protobuf ];
+in
+{
+  shell = (import ./shell.nix { inherit pkgs; });
+  tvix-daemon = crate2nix.rootCrate.build;
+  clippy = pkgs.stdenv.mkDerivation {
+    name = "tvix-daemon-clippy";
+    # The cleaned sources.
+    src = depot.third_party.gitignoreSource ./.;
+    cargoDeps = crate2nix.allWorkspaceMembers;
+
+    nativeBuildInputs = with pkgs; [
+      cargo
+      clippy
+      pkg-config
+      protobuf
+      rustc
+      rustPlatform.cargoSetupHook
+    ];
+
+    buildPhase = "cargo clippy --tests --all-features --benches --examples | tee $out";
+  };
+  meta.ci.targets = [
+    "tvix-daemon"
+    "shell"
+  ];
+}
diff --git a/users/picnoir/tvix-daemon/shell.nix b/users/picnoir/tvix-daemon/shell.nix
new file mode 100644
index 0000000000..6ec6b961fa
--- /dev/null
+++ b/users/picnoir/tvix-daemon/shell.nix
@@ -0,0 +1,21 @@
+{ pkgs, ... }:
+pkgs.mkShell {
+  name = "tvix-daemon";
+  packages = [
+    pkgs.cargo
+    pkgs.cargo-machete
+    pkgs.cargo-expand
+    pkgs.clippy
+    pkgs.evans
+    pkgs.fuse
+    pkgs.go
+    pkgs.grpcurl
+    pkgs.hyperfine
+    pkgs.nix_2_3 # b/313
+    pkgs.pkg-config
+    pkgs.rust-analyzer
+    pkgs.rustc
+    pkgs.rustfmt
+    pkgs.protobuf
+  ];
+}
diff --git a/users/picnoir/tvix-daemon/src/main.rs b/users/picnoir/tvix-daemon/src/main.rs
new file mode 100644
index 0000000000..dc49b209e0
--- /dev/null
+++ b/users/picnoir/tvix-daemon/src/main.rs
@@ -0,0 +1,116 @@
+use clap::Parser;
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio_listener::{self, SystemOptions, UserOptions};
+use tracing::{debug, error, info, instrument, Level};
+
+use nix_compat::worker_protocol::{self, server_handshake_client, ClientSettings, Trust};
+use nix_compat::ProtocolVersion;
+
+#[derive(Parser, Debug)]
+struct Cli {
+    /// Listening unix socket path
+    #[arg(short, long)]
+    socket: Option<String>,
+    /// Log verbosity level. Can be "error", "warn", "info", "debug", "trace", or a number 1-5
+    #[arg(short, long, env)]
+    verbosity: Option<Level>,
+}
+
+#[tokio::main]
+#[instrument()]
+async fn main() {
+    let args = Cli::parse();
+    tracing_subscriber::fmt()
+        .compact()
+        .with_max_level(
+            args.verbosity
+                .unwrap_or_else(|| panic!("Can't parse log verbosity")),
+        )
+        .try_init()
+        .unwrap();
+    info!("Started Tvix daemon");
+    let addr = args
+        .socket
+        .unwrap_or_else(|| "sd_listen_unix".to_string())
+        .parse()
+        .expect("Invalid listening socket address");
+    let system_options: SystemOptions = Default::default();
+    let mut user_options: UserOptions = Default::default();
+    user_options.recv_buffer_size = Some(1024);
+    user_options.send_buffer_size = Some(1024);
+    info!(user_options.send_buffer_size);
+    info!(user_options.recv_buffer_size);
+    let mut listener = tokio_listener::Listener::bind(&addr, &system_options, &user_options)
+        .await
+        .unwrap();
+    info!(listener_address = ?listener, "Listening for incoming connections");
+    while let Ok((conn, addr)) = listener.accept().await {
+        info!(addr = %addr, "Incoming connection");
+        tokio::spawn(async move { worker(conn).await });
+    }
+}
+
+/// Structure used to hold the client socket connection and some
+/// metadata about the connection.
+#[derive(Debug)]
+struct ClientConnection<R: AsyncReadExt + AsyncWriteExt + Unpin> {
+    pub conn: R,
+    pub version: ProtocolVersion,
+    pub client_settings: Option<ClientSettings>,
+}
+
+/// Worker in charge to respond a Nix client using the Nix wire
+/// protocol.
+#[instrument()]
+async fn worker<R>(mut conn: R)
+where
+    R: AsyncReadExt + AsyncWriteExt + Unpin + std::fmt::Debug,
+{
+    match server_handshake_client(&mut conn, "2.18.2", Trust::Trusted).await {
+        Ok(client_protocol_version) => {
+            let mut client_connection = ClientConnection {
+                conn,
+                version: client_protocol_version,
+                client_settings: None,
+            };
+            debug!("Client hanshake succeeded");
+            debug!(client_protocol_version = ?client_protocol_version);
+            // TODO: implement logging. For now, we'll just send
+            // STDERR_LAST, which is good enough to get Nix respond to
+            // us.
+            client_connection
+                .conn
+                .write_u64_le(worker_protocol::STDERR_LAST)
+                .await
+                .unwrap();
+            loop {
+                let op = worker_protocol::read_op(&mut client_connection.conn)
+                    .await
+                    .unwrap();
+                match op {
+                    worker_protocol::Operation::SetOptions => {
+                        let settings = op_set_options(&mut client_connection).await.unwrap();
+                        client_connection.client_settings = Some(settings);
+                        debug!(settings = ?client_connection.client_settings, "Received client settings");
+                    }
+                    _ => {
+                        error!(op = ?op, "Unimplemented operation");
+                        break;
+                    }
+                }
+            }
+        }
+        Err(e) => error!("Client handshake failed: {}", e),
+    }
+}
+
+async fn op_set_options<R>(conn: &mut ClientConnection<R>) -> std::io::Result<ClientSettings>
+where
+    R: AsyncReadExt + AsyncWriteExt + Unpin + std::fmt::Debug,
+{
+    let settings = worker_protocol::read_client_settings(&mut conn.conn, conn.version).await?;
+    // The client expects us to send some logs when we're processing
+    // the settings. Sending STDERR_LAST signal we're done processing.
+    conn.conn.write_u64_le(worker_protocol::STDERR_LAST).await?;
+    Ok(settings)
+}
diff --git a/users/picnoir/tvix-daemon/vm-test/README.md b/users/picnoir/tvix-daemon/vm-test/README.md
new file mode 100644
index 0000000000..bd7f14f7ef
--- /dev/null
+++ b/users/picnoir/tvix-daemon/vm-test/README.md
@@ -0,0 +1,5 @@
+# Integration VM Test
+
+This VM test fails for now. We use it to conveniently test our implementation on a real world Nix setup.
+
+For now, it only adds a new path to the store. It'll likely do more in the future.
diff --git a/users/picnoir/tvix-daemon/vm-test/default.nix b/users/picnoir/tvix-daemon/vm-test/default.nix
new file mode 100644
index 0000000000..e70690ee02
--- /dev/null
+++ b/users/picnoir/tvix-daemon/vm-test/default.nix
@@ -0,0 +1,28 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  nixosTestDrv = pkgs.nixosTest {
+    name = "tvix-daemon-vm-test";
+    nodes.machine = { config, pkgs, ... }: {
+      environment.systemPackages = [
+        (pkgs.writers.writeBashBin "poke-daemon" ''
+          NIX_REMOTE=unix:///nix/var/nix/daemon-socket/socket nix-instantiate -E '"''${/etc/nscd.conf}"'
+        '')
+      ];
+      systemd.services.nix-daemon.serviceConfig.ExecStart = [
+        ""
+        "${depot.users.picnoir.tvix-daemon.tvix-daemon}/bin/tvix-daemon"
+      ];
+
+    };
+    testScript = ''
+      machine.wait_for_unit("multi-user.target")
+      machine.succeed("poke-daemon")
+    '';
+  };
+in
+nixosTestDrv // {
+  # The test fails for now. TOREMOVE when we reach the stage where we
+  # can add stuff to the store.
+  meta.ci.skip = true;
+}
diff --git a/users/qyliss/OWNERS b/users/qyliss/OWNERS
index d54ea3622d..68724206af 100644
--- a/users/qyliss/OWNERS
+++ b/users/qyliss/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - qyliss
+set noparent
+
+qyliss
diff --git a/users/riking/OWNERS b/users/riking/OWNERS
deleted file mode 100644
index a39f4cd9f0..0000000000
--- a/users/riking/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherit: false
-owners:
- - riking
diff --git a/users/riking/adventofcode-2020/.gitignore b/users/riking/adventofcode-2020/.gitignore
deleted file mode 100644
index 076ff41215..0000000000
--- a/users/riking/adventofcode-2020/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*/target
-*/input.txt
diff --git a/users/riking/adventofcode-2020/day01/Cargo.lock b/users/riking/adventofcode-2020/day01/Cargo.lock
deleted file mode 100644
index a1a18948a7..0000000000
--- a/users/riking/adventofcode-2020/day01/Cargo.lock
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-[[package]]
-name = "anyhow"
-version = "1.0.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7"
-
-[[package]]
-name = "day01"
-version = "0.1.0"
-dependencies = [
- "anyhow",
-]
diff --git a/users/riking/adventofcode-2020/day01/Cargo.toml b/users/riking/adventofcode-2020/day01/Cargo.toml
deleted file mode 100644
index d90ab548bb..0000000000
--- a/users/riking/adventofcode-2020/day01/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[package]
-name = "day01"
-version = "0.1.0"
-authors = ["Kane York <kanepyork@gmail.com>"]
-edition = "2018"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-anyhow = "1.0.34"
diff --git a/users/riking/adventofcode-2020/day01/default.nix b/users/riking/adventofcode-2020/day01/default.nix
deleted file mode 100644
index 0648a05af6..0000000000
--- a/users/riking/adventofcode-2020/day01/default.nix
+++ /dev/null
@@ -1,10 +0,0 @@
-{ depot, ... }:
-
-with depot.third_party;
-
-naersk.buildPackage {
-  src = ./.;
-
-  buildInputs = [];
-  doCheck = true;
-}
diff --git a/users/riking/adventofcode-2020/day01/src/main.rs b/users/riking/adventofcode-2020/day01/src/main.rs
deleted file mode 100644
index 3e6b339d7c..0000000000
--- a/users/riking/adventofcode-2020/day01/src/main.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use anyhow::anyhow;
-use std::fs::File;
-use std::io::prelude::*;
-use std::io::BufReader;
-
-const PART_2: bool = true;
-
-fn day01(is_part2: bool, numbers: &Vec<i64>) -> Result<String, anyhow::Error> {
-    //println!("{:?}", numbers);
-
-    for n1 in numbers.iter() {
-        for n2 in numbers.iter() {
-            if is_part2 {
-                for n3 in numbers.iter() {
-                    if n1 + n2 + n3 == 2020 {
-                        return Ok((n1 * n2 * n3).to_string());
-                    }
-                }
-            } else {
-                if n1 + n2 == 2020 {
-                    return Ok((n1 * n2).to_string());
-                }
-            }
-        }
-    }
-
-    Err(anyhow!("no solution found"))
-}
-
-fn parse(filename: &str) -> Result<Vec<i64>, anyhow::Error> {
-    let f = File::open(filename)?;
-    let mut reader = BufReader::new(f);
-
-    let mut values = Vec::<i64>::new();
-
-    let mut line = String::new();
-    loop {
-        line.clear();
-        reader.read_line(&mut line)?;
-        let trimmed_line = line.trim();
-        if trimmed_line.is_empty() {
-            break;
-        }
-
-        values.push(trimmed_line.parse()?);
-    }
-    Ok(values)
-}
-
-fn main() -> anyhow::Result<()> {
-    let args: Vec<String> = std::env::args().collect();
-
-    //println!("{:?}", args);
-    if args.len() != 2 {
-        return Err(anyhow!("usage: day01 input_file"));
-    }
-    let filename = args.into_iter().skip(1).next().expect("args len == 1");
-
-    let numbers = parse(&filename)?;
-
-    println!("{}", day01(PART_2, &numbers)?);
-
-    Ok(())
-}
-
-#[cfg(test)]
-mod tests {
-    use super::day01;
-
-    #[test]
-    fn test_part1() {
-        let vec = vec![1721, 979, 366, 299, 675, 1456];
-        let result = day01(false, &vec).unwrap();
-
-        assert_eq!(result, 514579.to_string());
-    }
-
-    #[test]
-    fn test_part2() {
-        let vec = vec![1721, 979, 366, 299, 675, 1456];
-        let result = day01(true, &vec).unwrap();
-
-        assert_eq!(result, 241861950.to_string());
-    }
-}
diff --git a/users/riking/dotfiles/.mybashrc b/users/riking/dotfiles/.mybashrc
deleted file mode 100644
index c5ebc34a1f..0000000000
--- a/users/riking/dotfiles/.mybashrc
+++ /dev/null
@@ -1,53 +0,0 @@
-
-# BEGIN: __USER_FUNCTIONS__
-function gh-clone() {
-	if [[ "x$2" == "x" ]]; then
-		IFS='/' read -ra PARTS <<< "$1"
-		user="${PARTS[0]}"
-		repo="${PARTS[1]}"
-	else
-		user="$1"
-		repo="$2"
-	fi
-	if [[ -d ~/go/src/github.com/"$user"/"$repo" ]]; then
-		cd ~/go/src/github.com/"${user}"/"${repo}"
-		return 0
-	fi
-	mkdir -p ~/go/src/github.com/"${user}"
-	cd ~/go/src/github.com/"${user}"
-	git clone git@github.com:"${user}"/"${repo}".git
-	cd ~/go/src/github.com/"${user}"/"${repo}"
-}
-
-function download() {
-	cd "${HOME}/Downloads"
-	wget "$@"
-}
-
-# todo: only one password pls
-function prodaccess() {
-	(ssh-add -L | grep -q 'ZgEu6S3SLatYN') || ssh-add "$HOME"/.ssh/id_ed25519
-	(ssh-add -L | grep -q 'Gfh2S3kUwZ8A6') || ssh-add "$HOME"/.ssh/id_rsa.discourse
-	echo "signing test" | gpg --clearsign > /dev/null
-}
-
-function reset-audio() {
-	pulseaudio -k && sudo alsa force-reload
-}
-
-function tvl-push() {
-	git push origin HEAD:refs/for/canon
-}
-
-# END: __USER_FUNCTIONS__
-
-# BEGIN: __USER_ENV__
-GOPATH=$HOME/go
-CDPATH=$HOME/go/src
-export GPG_TTY="$(tty)"
-
-export PATH="/usr/local/go/bin:$HOME/go/bin:$HOME/.rbenv/bin:$PATH"
-
-eval "$(rbenv init -)"
-# END: __USER_ENV__
-
diff --git a/users/riking/dotfiles/fish/conf.d/nix-env.fish b/users/riking/dotfiles/fish/conf.d/nix-env.fish
deleted file mode 100644
index 6f79f97528..0000000000
--- a/users/riking/dotfiles/fish/conf.d/nix-env.fish
+++ /dev/null
@@ -1,141 +0,0 @@
-# SPDX-License-Identifier: Unlicense
-# https://raw.githubusercontent.com/lilyball/nix-env.fish/master/conf.d/nix-env.fish
-
-# Setup Nix
-
-# We need to distinguish between single-user and multi-user installs.
-# This is difficult because there's no official way to do this.
-# We could look for the presence of /nix/var/nix/daemon-socket/socket but this will fail if the
-# daemon hasn't started yet. /nix/var/nix/daemon-socket will exist if the daemon has ever run, but
-# I don't think there's any protection against accidentally running `nix-daemon` as a user.
-# We also can't just look for /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh because
-# older single-user installs used the default profile instead of a per-user profile.
-# We can still check for it first, because all multi-user installs should have it, and so if it's
-# not present that's a pretty big indicator that this is a single-user install. If it does exist,
-# we still need to verify the install type. To that end we'll look for a root owner and sticky bit
-# on /nix/store. Multi-user installs set both, single-user installs don't. It's certainly possible
-# someone could do a single-user install as root and then manually set the sticky bit but that
-# would be extremely unusual.
-
-set -l nix_profile_path /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
-set -l single_user_profile_path ~/.nix-profile/etc/profile.d/nix.sh
-if test -e $nix_profile_path
-  # The path exists. Double-check that this is a multi-user install.
-  # We can't just check for ~/.nix-profile/… because this may be a single-user install running as
-  # the wrong user.
-
-  # stat is not portable. Splitting the output of ls -nd is reliable on most platforms.
-  set -l owner (string split -n ' ' (ls -nd /nix/store 2>/dev/null))[3]
-  if not test -k /nix/store -a $owner -eq 0
-    # /nix/store is either not owned by root or not sticky. Assume single-user.
-    set nix_profile_path $single_user_profile_path
-  end
-else
-  # The path doesn't exist. Assume single-user
-  set nix_profile_path $single_user_profile_path
-end
-
-if test -e $nix_profile_path
-  # Source the nix setup script
-  # We're going to run the regular Nix profile under bash and then print out a few variables
-  for line in (env -u BASH_ENV bash -c '. "$0"; for name in PATH "${!NIX_@}"; do printf "%s=%s\0" "$name" "${!name}"; done' $nix_profile_path | string split0)
-    set -xg (string split -m 1 = $line)
-  end
-
-  # Insert Nix's fish share directories into fish's special variables.
-  # nixpkgs-installed fish tries to set these up already if NIX_PROFILES is defined, which won't
-  # be the case when sourcing $__fish_data_dir/share/config.fish normally, but might be for a
-  # recursive invocation. To guard against that, we'll only insert paths that don't already exit.
-  # Furthermore, for the vendor_conf.d sourcing, we'll use the pre-existing presence of a path in
-  # $fish_function_path to determine whether we want to source the relevant vendor_conf.d folder.
-
-  # To start, let's locally define NIX_PROFILES if it doesn't already exist.
-  set -al NIX_PROFILES
-  if test (count $NIX_PROFILES) -eq 0
-    set -a NIX_PROFILES $HOME/.nix-profile
-  end
-  # Replicate the logic from nixpkgs version of $__fish_data_dir/__fish_build_paths.fish.
-  set -l __nix_profile_paths (string split ' ' -- $NIX_PROFILES)[-1..1]
-  set -l __extra_completionsdir \
-    $__nix_profile_paths/etc/fish/completions \
-    $__nix_profile_paths/share/fish/vendor_completions.d
-  set -l __extra_functionsdir \
-    $__nix_profile_paths/etc/fish/functions \
-    $__nix_profile_paths/share/fish/vendor_functions.d
-  set -l __extra_confdir \
-    $__nix_profile_paths/etc/fish/conf.d \
-    $__nix_profile_paths/share/fish/vendor_conf.d \
-
-  ### Configure fish_function_path ###
-  # Remove any of our extra paths that may already exist.
-  # Record the equivalent __extra_confdir path for any function path that exists.
-  set -l existing_conf_paths
-  for path in $__extra_functionsdir
-    if set -l idx (contains --index -- $path $fish_function_path)
-      set -e fish_function_path[$idx]
-      set -a existing_conf_paths $__extra_confdir[(contains --index -- $path $__extra_functionsdir)]
-    end
-  end
-  # Insert the paths before $__fish_data_dir.
-  if set -l idx (contains --index -- $__fish_data_dir/functions $fish_function_path)
-    # Fish has no way to simply insert into the middle of an array.
-    set -l new_path $fish_function_path[1..$idx]
-    set -e new_path[$idx]
-    set -a new_path $__extra_functionsdir
-    set fish_function_path $new_path $fish_function_path[$idx..-1]
-  else
-    set -a fish_function_path $__extra_functionsdir
-  end
-
-  ### Configure fish_complete_path ###
-  # Remove any of our extra paths that may already exist.
-  for path in $__extra_completionsdir
-    if set -l idx (contains --index -- $path $fish_complete_path)
-      set -e fish_complete_path[$idx]
-    end
-  end
-  # Insert the paths before $__fish_data_dir.
-  if set -l idx (contains --index -- $__fish_data_dir/completions $fish_complete_path)
-    set -l new_path $fish_complete_path[1..$idx]
-    set -e new_path[$idx]
-    set -a new_path $__extra_completionsdir
-    set fish_complete_path $new_path $fish_complete_path[$idx..-1]
-  else
-    set -a fish_complete_path $__extra_completionsdir
-  end
-
-  ### Source conf directories ###
-  # The built-in directories were already sourced during shell initialization.
-  # Any __extra_confdir that came from $__fish_data_dir/__fish_build_paths.fish was also sourced.
-  # As explained above, we're using the presence of pre-existing paths in $fish_function_path as a
-  # signal that the corresponding conf dir has also already been sourced.
-  # In order to simulate this, we'll run through the same algorithm as found in
-  # $__fish_data_dir/config.fish except we'll avoid sourcing the file if it comes from an
-  # already-sourced location.
-  # Caveats:
-  # * Files will be sourced in a different order than we'd ideally do (because we're coming in
-  #   after the fact to source them).
-  # * If there are existing extra conf paths, files in them may have been sourced that should have
-  #   been suppressed by paths we're inserting in front.
-  # * Similarly any files in $__fish_data_dir/vendor_conf.d that should have been suppressed won't
-  #   have been.
-  set -l sourcelist
-  for file in $__fish_config_dir/conf.d/*.fish $__fish_sysconf_dir/conf.d/*.fish
-    # We know these paths were sourced already. Just record them.
-    set -l basename (string replace -r '^.*/' '' -- $file)
-    contains -- $basename $sourcelist
-    or set -a sourcelist $basename
-  end
-  for root in $__extra_confdir
-    for file in $root/*.fish
-      set -l basename (string replace -r '^.*/' '' -- $file)
-      contains -- $basename $sourcelist
-      and continue
-      set -a sourcelist $basename
-      contains -- $root $existing_conf_paths
-      and continue # this is a pre-existing path, it will have been sourced already
-      [ -f $file -a -r $file ]
-      and source $file
-    end
-  end
-end
diff --git a/users/riking/dotfiles/fish/config.fish b/users/riking/dotfiles/fish/config.fish
deleted file mode 100644
index c2454762bd..0000000000
--- a/users/riking/dotfiles/fish/config.fish
+++ /dev/null
@@ -1,8 +0,0 @@
-set -gx GOPATH "$HOME/go"
-set -gx GPG_TTY (tty)
-set -gx DEPOT_ROOT "$GOPATH/src/code.tvl.fyi"
-
-set -gx PATH '/usr/local/go/bin' "$HOME/.cargo/bin" "$HOME/.rbenv/bin" $PATH
-status --is-interactive; and rbenv init - | source
-source ~/.opsrc.fish # work
-set -gx PATH "$HOME/go/bin" $PATH
diff --git a/users/riking/dotfiles/fish/fish_variables b/users/riking/dotfiles/fish/fish_variables
deleted file mode 100644
index fa8bff919f..0000000000
--- a/users/riking/dotfiles/fish/fish_variables
+++ /dev/null
@@ -1,32 +0,0 @@
-# This file contains fish universal variable definitions.
-# VERSION: 3.0
-SETUVAR __fish_initialized:3100
-SETUVAR fish_color_autosuggestion:555\x1ebrblack
-SETUVAR fish_color_cancel:\x2dr
-SETUVAR fish_color_command:005fd7
-SETUVAR fish_color_comment:990000
-SETUVAR fish_color_cwd:green
-SETUVAR fish_color_cwd_root:red
-SETUVAR fish_color_end:009900
-SETUVAR fish_color_error:ff0000
-SETUVAR fish_color_escape:00a6b2
-SETUVAR fish_color_history_current:\x2d\x2dbold
-SETUVAR fish_color_host:normal
-SETUVAR fish_color_host_remote:yellow
-SETUVAR fish_color_match:\x2d\x2dbackground\x3dbrblue
-SETUVAR fish_color_normal:normal
-SETUVAR fish_color_operator:00a6b2
-SETUVAR fish_color_param:00afff
-SETUVAR fish_color_quote:999900
-SETUVAR fish_color_redirection:00afff
-SETUVAR fish_color_search_match:bryellow\x1e\x2d\x2dbackground\x3dbrblack
-SETUVAR fish_color_selection:white\x1e\x2d\x2dbold\x1e\x2d\x2dbackground\x3dbrblack
-SETUVAR fish_color_status:red
-SETUVAR fish_color_user:brgreen
-SETUVAR fish_color_valid_path:\x2d\x2dunderline
-SETUVAR fish_greeting:Welcome\x20to\x20fish\x2c\x20the\x20friendly\x20interactive\x20shell\x0aType\x20\x60help\x60\x20for\x20instructions\x20on\x20how\x20to\x20use\x20fish
-SETUVAR fish_key_bindings:fish_default_key_bindings
-SETUVAR fish_pager_color_completion:\x1d
-SETUVAR fish_pager_color_description:B3A06D\x1eyellow
-SETUVAR fish_pager_color_prefix:white\x1e\x2d\x2dbold\x1e\x2d\x2dunderline
-SETUVAR fish_pager_color_progress:brwhite\x1e\x2d\x2dbackground\x3dcyan
diff --git a/users/riking/dotfiles/fish/functions/ddate.fish b/users/riking/dotfiles/fish/functions/ddate.fish
deleted file mode 100644
index 8152d31680..0000000000
--- a/users/riking/dotfiles/fish/functions/ddate.fish
+++ /dev/null
@@ -1,3 +0,0 @@
-function ddate --description 'current date in Discourse format'
-    TZ=UTC date '+[date=%Y-%m-%d time=%H:%M:%S timezone=\"%Z\"]'
-end
diff --git a/users/riking/dotfiles/fish/functions/gh-clone.fish b/users/riking/dotfiles/fish/functions/gh-clone.fish
deleted file mode 100644
index 109ec353f6..0000000000
--- a/users/riking/dotfiles/fish/functions/gh-clone.fish
+++ /dev/null
@@ -1,18 +0,0 @@
-function gh-clone --description 'Clone and CD to a github repository'
-    if test (count $argv) -eq 1
-        set user (string split "/" -- $argv[1])[1]
-        set repo (string split "/" -- $argv[1])[2]
-    else
-        set user $argv[1]
-        set repo $argv[2]
-    end
-
-    if test -d "$HOME/go/src/github.com/$user/$repo"
-        cd "$HOME/go/src/github.com/$user/$repo"
-        return 0
-    end
-    mkdir -p "$HOME/go/src/github.com/$user"
-    cd "$HOME/go/src/github.com/$user"
-    git clone "git@github.com:$user/$repo.git"
-    cd "$HOME/go/src/github.com/$user/$repo"
-end
diff --git a/users/riking/dotfiles/fish/functions/prodaccess.fish b/users/riking/dotfiles/fish/functions/prodaccess.fish
deleted file mode 100644
index 876c14c5e3..0000000000
--- a/users/riking/dotfiles/fish/functions/prodaccess.fish
+++ /dev/null
@@ -1,6 +0,0 @@
-function prodaccess
-    ssh-add "$HOME/.ssh/id_ecdsa_sk"
-    begin; ssh-add -L | grep -q 'ZgEu6S3SLatYN'; end || ssh-add "$HOME"/.ssh/id_ed25519
-    begin; ssh-add -L | grep -q 'Gfh2S3kUwZ8A6'; end || ssh-add "$HOME"/.ssh/id_rsa.discourse
-    echo "signing test" | gpg --clearsign > /dev/null
-end
diff --git a/users/riking/dotfiles/fish/functions/reset-audio.fish b/users/riking/dotfiles/fish/functions/reset-audio.fish
deleted file mode 100644
index eb48578a52..0000000000
--- a/users/riking/dotfiles/fish/functions/reset-audio.fish
+++ /dev/null
@@ -1,4 +0,0 @@
-function reset-audio --description "Resets pulse and alsa"
-    pulseaudio -k
-    sudo alsa force-reload
-end
diff --git a/users/riking/dotfiles/fish/functions/tvl-push.fish b/users/riking/dotfiles/fish/functions/tvl-push.fish
deleted file mode 100644
index f04ac830c0..0000000000
--- a/users/riking/dotfiles/fish/functions/tvl-push.fish
+++ /dev/null
@@ -1,3 +0,0 @@
-function tvl-push
-    git push origin HEAD:refs/for/canon
-end
diff --git a/users/riking/dotfiles/regolith/Xresources b/users/riking/dotfiles/regolith/Xresources
deleted file mode 100644
index f47b93511a..0000000000
--- a/users/riking/dotfiles/regolith/Xresources
+++ /dev/null
@@ -1,5 +0,0 @@
-#include "/etc/regolith/styles/ubuntu/root"
-
-i3-wm.program.lock: xset s activate
-i3-wm.program.1: /bin/sh $HOME/.config/regolith/initrc
-
diff --git a/users/riking/dotfiles/regolith/flags/ui-fingerprint b/users/riking/dotfiles/regolith/flags/ui-fingerprint
deleted file mode 100644
index b35aedd2dc..0000000000
--- a/users/riking/dotfiles/regolith/flags/ui-fingerprint
+++ /dev/null
@@ -1 +0,0 @@
-ec33ee15ff705ac4b167ba6b7f6df3c2
diff --git a/users/riking/dotfiles/regolith/initrc b/users/riking/dotfiles/regolith/initrc
deleted file mode 100755
index 9b14613cd4..0000000000
--- a/users/riking/dotfiles/regolith/initrc
+++ /dev/null
@@ -1,3 +0,0 @@
-
-xset s 900 5
-( xss-lock -n /usr/lib/xsecurelock/dimmer -l -- sh -c "XSECURELOCK_PASSWORD_PROMPT=time_hex XSECURELOCK_SHOW_DATETIME=1 XSECURELOCK_SAVER=saver_mpv XSECURELOCK_IMAGE_DURATION_SECONDS=10 XSECURELOCK_LIST_VIDEOS_COMMAND='find ~/Videos/Screensaver -type f' xsecurelock" )&
diff --git a/users/riking/dotfiles/tmux.conf b/users/riking/dotfiles/tmux.conf
deleted file mode 100644
index 1f253cb27f..0000000000
--- a/users/riking/dotfiles/tmux.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-
-set -g mouse on
-set-option -g prefix C-a
-bind-key C-a send-prefix
-bind | split-window -h
-bind - split-window -v
diff --git a/users/riking/keys.nix b/users/riking/keys.nix
deleted file mode 100644
index 6dd2ff18a3..0000000000
--- a/users/riking/keys.nix
+++ /dev/null
@@ -1,20 +0,0 @@
-# SSH public keys
-{ ... }:
-
-rec {
-  sk-ecljg09 = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBBwJ7dJJUkvIK+bDsVsCsCZSlbs90aOLsHN7XesC8/AmLA5rIRLO8I5ADoOjsWAXl/WAgxqOMmB4LxZjoXWa1a0AAAAEc3NoOg== riking@sk-ECLJG09";
-  sk-portable1 = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBCfA8/0nKk4jXclWHjRZIuicPeyIo9oDwahpnWjEATr7YaFDAo632KTSgqlW0lpx8lX9alLsJRhFV2XaSurYw/EAAAAEc3NoOg== riking@sk-portable1";
-  sk-portable2 = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBEX3DXreQR93SR68QZHTdaVd5RjlRM8C0jcZ4kI4OZwqk7xuk68w3g22q2OM7O+chj+n1N3u0hLxi82QfRnwyasAAAAEc3NoOg== riking@sk-portable2";
-  sk-desktop = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBB+JvN8nAxD+yo49Ohf/UDq7Z049yvkURJIA1XNbvKaAkvfWnCN5m9vTC1FyGxTyCwy4QpD1pFP5fIn0X/kvvfgAAAAEc3NoOg== riking@sk-kane-DAN-A4";
-
-  u2f = [sk-ecljg09 sk-portable1 sk-portable2 sk-desktop];
-
-  ed1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAjWIfFH2bAWMZG+HudV1MVHWUl83M/ZgEu6S3SLatYN riking@kane-DAN-A4";
-  ed2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICBblB4C9IgAijv+qN6Zs8TM2Sz7phQvVmRrcDn4VYNo riking@ECLJG09";
-
-  passworded = [ed1 ed2];
-
-  unprotected = [];
-
-  all = u2f ++ passworded ++ unprotected;
-}
diff --git a/users/sterni/OWNERS b/users/sterni/OWNERS
index cace4d0f37..6434d4ca30 100644
--- a/users/sterni/OWNERS
+++ b/users/sterni/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - sterni
+set noparent
+
+sterni
diff --git a/users/sterni/clhs-lookup/default.nix b/users/sterni/clhs-lookup/default.nix
index b6a0bd0679..1cde38e8ce 100644
--- a/users/sterni/clhs-lookup/default.nix
+++ b/users/sterni/clhs-lookup/default.nix
@@ -36,4 +36,4 @@ let
     ];
   };
 in
-  clhs-lookup
+clhs-lookup
diff --git a/users/sterni/dot-time-man-pages/OWNERS b/users/sterni/dot-time-man-pages/OWNERS
index 980c17b424..b9bc074a80 100644
--- a/users/sterni/dot-time-man-pages/OWNERS
+++ b/users/sterni/dot-time-man-pages/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - edef
+edef
diff --git a/users/sterni/dot-time-man-pages/default.nix b/users/sterni/dot-time-man-pages/default.nix
index bf7d63dbd7..c449cde613 100644
--- a/users/sterni/dot-time-man-pages/default.nix
+++ b/users/sterni/dot-time-man-pages/default.nix
@@ -9,9 +9,9 @@ let
       res = builtins.filter ({ username, ... }: username == user) depot.ops.users;
       len = builtins.length res;
     in
-      if len == 1
-      then (builtins.head res).email
-      else builtins.throw "findEmail: got ${toString len} results instead of 1";
+    if len == 1
+    then (builtins.head res).email
+    else builtins.throw "findEmail: got ${toString len} results instead of 1";
 
   # dot-time(7) man page, ported from dotti.me
   dot-time = rec {
@@ -65,6 +65,6 @@ let
   };
 
 in
-  depot.nix.buildManPages "dot-time" {} [
-    dot-time
-  ]
+depot.nix.buildManPages "dot-time" { } [
+  dot-time
+]
diff --git a/users/sterni/emacs/default.nix b/users/sterni/emacs/default.nix
index 0a03c15586..9d057fef63 100644
--- a/users/sterni/emacs/default.nix
+++ b/users/sterni/emacs/default.nix
@@ -1,37 +1,56 @@
-{ depot, pkgs, ... }:
+{ depot, pkgs, lib, ... }:
 
 let
-  inherit (pkgs.emacsGcc.pkgs) withPackages;
+  inherit (pkgs.stdenv.hostPlatform) is64bit;
 
-  emacs = withPackages (epkgs: [
-    # basic setup
-    epkgs.elpaPackages.undo-tree
-    epkgs.melpaPackages.evil
-    epkgs.melpaPackages.evil-collection
-    epkgs.melpaPackages.use-package
-    # languages
+  # Wrap chktex(1) with the flags we want because the chktex flycheck checker
+  # ignores tex-chktex-extra-flags and has no other way to set flags. I did
+  # not want to mess around with chktexrc because that seems to involve copying
+  # around a lot of rules (that would need to be updated?).
+  #
+  # Warning 8 is about correct dash length. This is really annoying because it'll
+  # light up everywhere if you use typographically correct dashes in German text.
+  chktexLessWarnings = pkgs.writeShellScript "chktex-less-warnings" ''
+    exec chktex -n8 "$@"
+  '';
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs29-pgtk).withPackages (epkgs: [
     epkgs.bqn-mode
-    epkgs.elpaPackages.ada-mode
+    #epkgs.elpaPackages.ada-mode
+    epkgs.elpaPackages.rainbow-mode
+    epkgs.elpaPackages.undo-tree
+    epkgs.elpaPackages.which-key
     epkgs.melpaPackages.adoc-mode
+    epkgs.melpaPackages.cmake-mode
+    epkgs.melpaPackages.deft
+    epkgs.melpaPackages.direnv
     epkgs.melpaPackages.dockerfile-mode
+    epkgs.melpaPackages.editorconfig
+    epkgs.melpaPackages.elfeed
+    epkgs.melpaPackages.evil
+    epkgs.melpaPackages.evil-collection
+    epkgs.melpaPackages.flycheck
     epkgs.melpaPackages.haskell-mode
+    epkgs.melpaPackages.hl-todo
     epkgs.melpaPackages.jq-mode
-    epkgs.melpaPackages.languagetool
+    epkgs.melpaPackages.lsp-haskell
+    epkgs.melpaPackages.lsp-mode
+    epkgs.melpaPackages.lsp-ui
+    epkgs.melpaPackages.magit
     epkgs.melpaPackages.markdown-mode
+    epkgs.melpaPackages.meson-mode
     epkgs.melpaPackages.nix-mode
+    epkgs.melpaPackages.org-clock-csv
+    epkgs.melpaPackages.paredit
+    epkgs.melpaPackages.rainbow-delimiters
     epkgs.melpaPackages.sly
+    epkgs.melpaPackages.use-package
     epkgs.melpaPackages.yaml-mode
     epkgs.rust-mode
-    epkgs.urweb-mode
-    # misc
-    epkgs.melpaPackages.hl-todo
-    epkgs.melpaPackages.paredit
-    epkgs.elpaPackages.rainbow-mode
-    epkgs.melpaPackages.rainbow-delimiters
-    # beyond text editing
-    epkgs.melpaPackages.elfeed
-    epkgs.melpaPackages.magit
     epkgs.tvlPackages.tvl
+    epkgs.urweb-mode
+  ] ++ lib.optionals is64bit [
+    epkgs.melpaPackages.languagetool
   ]);
 
   configDirectory = pkgs.symlinkJoin {
@@ -41,15 +60,31 @@ let
       (pkgs.writeTextFile {
         name = "injected-emacs.d";
         destination = "/nix-inject.el";
-        text = ''
-          ;; bqn-mode
-          (setq bqn-interpreter-path "${pkgs.cbqn}/bin/BQN")
+        text =
+          # Java doesn't seem to be available for non 64bit platforms in nixpkgs
+          # CBQN doesn't seem to support i686 at least
+          lib.optionalString is64bit ''
+            ;; bqn-mode
+            (setq bqn-interpreter-path "${pkgs.cbqn}/bin/BQN")
 
-          ;; languagetool
-          (setq languagetool-java-bin "${pkgs.jre}/bin/java")
-          (setq languagetool-language-tool-jar "${pkgs.languagetool}/share/languagetool-commandline.jar")
+            ;; languagetool
+            (setq languagetool-java-bin "${pkgs.jre}/bin/java"
+                  languagetool-console-command "${pkgs.languagetool}/share/languagetool-commandline.jar"
+                  languagetool-server-command "${pkgs.languagetool}/share/languagetool-server.jar")
+          '' + ''
 
-          (provide 'nix-inject)
+            ;; use bash instead of fish from SHELL for some things, as it plays
+            ;; nicer with TERM=dumb, as I don't need/want vterm anyways.
+            ;; We want it to source /etc/profile for some extra setup that
+            ;; kicks in if TERM=dumb, meaning we can't use dash/sh mode.
+            (setq shell-file-name "${pkgs.bash}/bin/bash"
+                  explicit-bash-args '("-l"))
+
+            ;; chktex wrapper that disables warnings I don't want
+            (setq flycheck-tex-chktex-executable "${chktexLessWarnings}")
+            (setq tex-chktex-program "${chktexLessWarnings}")
+
+            (provide 'nix-inject)
         '';
       })
     ];
@@ -60,10 +95,16 @@ let
 in
 
 # sadly we can't give an init-file via the command line
-pkgs.writeShellScriptBin "emacs" ''
+(pkgs.writeShellScriptBin "emacs" ''
   exec ${emacs}/bin/emacs          \
     --no-init-file                 \
     --directory ${configDirectory} \
     --eval "(require 'init)"       \
     "$@"
-''
+'').overrideAttrs (super: {
+  buildCommand = ''
+    ${super.buildCommand}
+
+    ln -s "${emacs}/bin/emacsclient" "$out/bin/emacsclient"
+  '';
+})
diff --git a/users/sterni/emacs/init.el b/users/sterni/emacs/init.el
index 926b413663..4cb741f62d 100644
--- a/users/sterni/emacs/init.el
+++ b/users/sterni/emacs/init.el
@@ -1,5 +1,9 @@
+;; set up package infrastructure
+
+(require 'use-package)
+(package-initialize)
+
 ;; Set default font and fallback font via set-fontset-font
-;; TODO(sterni): Investigate non-emoji representation of some glyphs
 (let ((mono-font "Bitstream Vera Sans Mono-12")
       (emoji-font "Noto Color Emoji-12"))
   (setq default-frame-alist `((font . ,mono-font)))
@@ -7,7 +11,6 @@
   (set-fontset-font t nil emoji-font))
 
 (setq inhibit-startup-message t
-      initial-buffer-choice (concat (getenv "HOME") "/files/sync/org/inbox.org")
       display-time-24hr-format t
       select-enable-clipboard t)
 
@@ -16,7 +19,8 @@
 
 ;; Indent
 (setq-default indent-tabs-mode nil)
-(setq tab-width 2)
+(setq tab-width 2
+      css-indent-offset tab-width)
 
 ;; UTF-8
 (setq locale-coding-system 'utf-8)
@@ -63,6 +67,10 @@
 (setq savehist-additional-variables '(search-ring regexp-search-ring magit-cl-history))
 
 ;; buffers
+
+;; performance migitations
+(global-so-long-mode)
+
 ;; unique component should come first for better completion
 (setq uniquify-buffer-name-style 'forward)
 
@@ -93,11 +101,59 @@
 (setq perl-continued-statement-offset 0)
 (setq perl-continued-brace-offset 0)
 
-;;; Configure packages
-(require 'use-package)
+;; org mode
 
-(package-initialize)
+(setq org-clock-persist 'history)
+(org-clock-persistence-insinuate)
+
+(let ((org-folder (concat (getenv "HOME") "/files/sync/org")))
+  (setq org-agenda-files (directory-files-recursively org-folder "\\.org$")
+        org-default-notes-file (concat org-folder "/inbox.org")
+        initial-buffer-choice org-default-notes-file
+        org-refile-targets '((org-agenda-files . (:maxlevel . 2)))))
+
+;; latex
+
+(defun latex-word-count ()
+  "Calls texcount on the file the current buffer points to and displays the result."
+  (interactive)
+  (save-buffer)
+  (let* ((file (buffer-file-name)) ; needs to happen outside with-current-buffer
+         (word-count
+             (with-output-to-string
+               (with-current-buffer standard-output
+                 (call-process "texcount" nil t nil "-brief" "-utf8" file)))))
+      (message (string-trim-right word-count))))
+
+;; ediff
+;; doesn't create new window for ediff controls which I always open accidentally
+(setq ediff-window-setup-function 'ediff-setup-windows-plain)
+
+;; man
+(setq Man-notify-method 'pushy) ; display man page in current window
+
+;; shell
+
+; default, but allows ';' as prompt
+(setq shell-prompt-pattern "^[^#$%>;\n]*[#$%>;] *")
+
+;; projects (see also evil config)
 
+(defun project-magit ()
+  "Run magit in the current project dir"
+  (interactive)
+  (magit (project-root (project-current t))))
+
+(define-key project-prefix-map (kbd "G") 'project-magit)
+
+(setq project-switch-commands
+      '((project-find-file "Find file")
+        (project-find-regexp "Find regexp")
+        (project-dired "Dired")
+        (project-shell "Shell")
+        (project-magit "Magit")))
+
+;;; Configure packages
 (use-package undo-tree
   :config
   (global-undo-tree-mode)
@@ -131,33 +187,39 @@
   ;; buffer management
   (evil-define-key 'normal 'global (kbd "<leader>bk") 'kill-buffer)
   (evil-define-key 'normal 'global (kbd "<leader>bb") 'switch-to-buffer)
+  (evil-define-key 'normal 'global (kbd "<leader>bo") 'switch-to-buffer-other-window)
   (evil-define-key 'normal 'global (kbd "<leader>bl") 'list-buffers)
-  ;; window management
-  (evil-define-key 'normal 'global (kbd "<leader>wk") 'delete-window)
-  (evil-define-key 'normal 'global (kbd "<leader>wo") 'delete-other-window)
-  (evil-define-key 'normal 'global (kbd "<leader>wh") 'split-window-below)
-  (evil-define-key 'normal 'global (kbd "<leader>wv") 'split-window-right)
-  (evil-define-key 'normal 'global (kbd "<leader>ww") 'other-window)
+  (evil-define-key 'normal 'global (kbd "<leader>br") 'revert-buffer)
+  ;; window management: C-w hjkl is annoying in neo
+  (define-key evil-window-map (kbd "<left>") 'evil-window-left)
+  (define-key evil-window-map (kbd "<right>") 'evil-window-right)
+  (define-key evil-window-map (kbd "<up>") 'evil-window-up)
+  (define-key evil-window-map (kbd "<down>") 'evil-window-down)
+  ;; projects
+  (evil-define-key 'normal 'global (kbd "<leader>pf") 'project-find-file)
+  (evil-define-key 'normal 'global (kbd "<leader>pg") 'project-find-regexp)
+  (evil-define-key 'normal 'global (kbd "<leader>pd") 'project-dired)
+  (evil-define-key 'normal 'global (kbd "<leader>ps") 'project-shell)
+  (evil-define-key 'normal 'global (kbd "<leader>pR") 'project-query-replace-regexp)
+  (evil-define-key 'normal 'global (kbd "<leader>pK") 'project-kill-buffers)
+  (evil-define-key 'normal 'global (kbd "<leader>pp") 'project-switch-project)
   ;; emacs
   (evil-define-key 'visual 'global (kbd "<leader>ee") 'eval-region)
   (evil-define-key 'normal 'global (kbd "<leader>ee") 'eval-last-sexp)
   (evil-define-key 'normal 'global (kbd "<leader>ep") 'eval-print-last-sexp)
   (evil-define-key 'normal 'global (kbd "<leader>eh") 'help)
+  (evil-define-key 'normal 'global (kbd "<leader>em") 'man)
+  (evil-define-key '(normal visual) 'global (kbd "<leader>eu") 'browse-url-at-point)
+  (evil-define-key '(normal visual) 'global (kbd "<leader>ef") 'ffap)
   ;; modify what is displayed
   (evil-define-key 'normal 'global (kbd "<leader>dw")
     (lambda ()
       (interactive)
       (whitespace-mode 'toggle)
       (display-fill-column-indicator-mode 'toggle)))
-  ;; elfeed bindings for evil (can't use-package elfeed apparently)
-  (evil-define-key 'normal 'global (kbd "<leader>ff") 'elfeed)
-  (evil-define-key '(normal visual) elfeed-search-mode-map
-    (kbd "o") 'elfeed-search-browse-url
-    (kbd "r") 'elfeed-search-untag-all-unread
-    (kbd "u") 'elfeed-search-tag-all-unread
-    (kbd "<leader>ff") 'elfeed-search-fetch
-    (kbd "<leader>fc") 'elfeed-db-compact
-    (kbd "<leader>fr") 'elfeed-search-update--force))
+  ;; org-mode
+  (evil-define-key 'normal 'global (kbd "<leader>oa") 'org-agenda)
+  (evil-define-key 'normal 'global (kbd "<leader>oc") 'org-capture))
 
 (use-package evil-collection
   :after evil
@@ -177,10 +239,55 @@
          (ielm-mode . paredit-mode)
          (lisp-interaction-mode . paredit-mode)))
 
+(use-package which-key :config (which-key-mode t))
+
 (use-package nix-mode :mode "\\.nix\\'")
 (use-package nix-drv-mode :mode "\\.drv\\'")
 
+(use-package direnv
+  :config (direnv-mode))
+
+(use-package editorconfig
+  :config (editorconfig-mode 1))
+
 (use-package haskell-mode)
+(use-package flycheck
+  :init (global-flycheck-mode)
+  :custom flycheck-keymap-prefix (kbd "<leader>!"))
+(use-package lsp-mode
+  :hook ((haskell-mode . lsp-deferred))
+  :commands (lsp lsp-deferred)
+  :custom
+  lsp-modeline-code-actions-segments '() ; using lsp-ui-sideline instead
+  :config
+  (evil-define-key 'normal 'global
+    (kbd "<leader>lwr") 'lsp-workspace-restart
+    (kbd "<leader>lwq") 'lsp-workspace-shutdown
+    (kbd "<leader>la=") 'lsp-format-buffer
+    (kbd "<leader>lar") 'lsp-rename
+    (kbd "<leader>laa") 'lsp-execute-code-action))
+(use-package lsp-ui
+  :after lsp-mode
+  :custom
+  lsp-ui-doc-enable t
+  lsp-ui-doc-border "DimGray"
+  lsp-ui-doc-delay 0.5
+  :config
+  (set-face-background 'lsp-ui-doc-background "WhiteSmoke")
+  (set-face-foreground 'lsp-ui-sideline-code-action "SaddleBrown")
+  (setq lsp-ui-sideline-code-actions-prefix "πŸ”¨ "
+        lsp-ui-sideline-show-diagnostics nil
+        lsp-ui-sideline-show-code-actions t) ; is :custom, but won't take effect?
+  (evil-define-key 'normal lsp-ui-mode-map
+    ;; TODO(sterni): emulate using xref for non-lsp?
+    (kbd "<leader>lgr") 'lsp-ui-peek-find-references
+    (kbd "<leader>lgd") 'lsp-ui-peek-find-definitions
+    (kbd "<leader>lc") 'lsp-ui-flycheck-list))
+(use-package lsp-haskell
+  :after lsp-mode
+  :custom
+  lsp-haskell-formatting-provider "ormolu")
+
 (use-package urweb-mode)
 (use-package bqn-mode
   :mode "\\.bqn\\'"
@@ -198,7 +305,8 @@
   :config
   (evil-define-key '(normal insert) sly-mrepl-mode-map (kbd "C-r") 'isearch-backward))
 
-(use-package ada-mode)
+; TODO(sterni): https://github.com/NixOS/nixpkgs/pull/173893/files
+; (use-package ada-mode)
 
 (use-package rainbow-mode)
 (use-package hl-todo
@@ -222,12 +330,28 @@
   languagetool-default-language "en-GB"
   languagetool-mother-tongue "de-DE"
   :config
-  (evil-define-key 'normal 'global (kbd "<leader>ll") 'languagetool-check)
-  (evil-define-key 'normal 'global (kbd "<leader>lc") 'languagetool-correct-at-point)
-  (evil-define-key 'normal 'global (kbd "<leader>ls") 'languagetool-set-language)
-  (evil-define-key 'normal 'global (kbd "<leader>lr") 'languagetool-clear-buffer))
+  (evil-define-key 'normal 'global (kbd "<leader>mll") 'languagetool-check)
+  (evil-define-key 'normal 'global (kbd "<leader>mlc") 'languagetool-correct-at-point)
+  (evil-define-key 'normal 'global (kbd "<leader>mls") 'languagetool-set-language)
+  (evil-define-key 'normal 'global (kbd "<leader>mlr") 'languagetool-clear-suggestions)
+  ;; Fill background of issues instead of just underlining to make it easier to read
+  (set-face-background 'languagetool-issue-default "yellow")
+  (set-face-background 'languagetool-issue-misspelling "red"))
+
+(use-package deft
+  :config
+  ;; This is based on (car deft-extensions), but unfortunately the variable is
+  ;; not re-bound in the hook defined by defcustom, so it is always "txt".
+  (setq deft-default-extension "org")
+  (evil-define-key 'normal 'global (kbd "<leader>mn") 'deft)
+  :custom
+  deft-directory (expand-file-name "~/files/sync/org/notes")
+  deft-extensions '("org" "md" "txt" "tex"))
+
+(unless (server-running-p)
+  (server-start))
 
-(require 'subscriptions)
+(require 'subscriptions) ; elfeed config
 (require 'nix-inject)
 
 (provide 'init)
diff --git a/users/sterni/emacs/subscriptions.el b/users/sterni/emacs/subscriptions.el
index 50bfff81f6..ba63da3063 100644
--- a/users/sterni/emacs/subscriptions.el
+++ b/users/sterni/emacs/subscriptions.el
@@ -1,84 +1,71 @@
-;;; elfeed subscriptions
+;;; elfeed
+(use-package elfeed
+  :after evil
+  :config
+  ;; elfeed bindings for evil
+  (evil-define-key 'normal 'global (kbd "<leader>mf") 'elfeed)
+  (evil-define-key '(normal visual) elfeed-search-mode-map
+    (kbd "o") 'elfeed-search-browse-url
+    (kbd "r") 'elfeed-search-untag-all-unread
+    (kbd "u") 'elfeed-search-tag-all-unread
+    (kbd "ff") 'elfeed-search-fetch
+    (kbd "fc") 'elfeed-db-compact)
+  ;; elfeed subscriptions
+  (setq elfeed-feeds
+        (append
+         ;; immutable subscriptions tracked in git
+         '(("https://repology.org/maintainer/sternenseemann%40systemli.org/feed-for-repo/nix_unstable/atom" dashboard releases)
+           ("https://www.stackage.org/feed" dashboard releases)
+           ("http://hundimbuero.blogspot.com/feeds/posts/default?alt=rss" blog cool-and-nice)
+           ("https://text.causal.agency/feed.atom" blog)
+           ("http://xsteadfastx.org/feed/" blog cool-and-nice)
+           ("https://tvl.fyi/feed.atom" blog cool-and-nice)
+           ("https://hannes.robur.coop/atom" blog)
+           ("https://stevelosh.com/rss.xml" blog)
+           ("https://blog.benjojo.co.uk/rss.xml" blog)
+           ("https://leahneukirchen.org/blog/index.atom" blog cool-and-nice)
+           ("https://leahneukirchen.org/trivium/index.atom" blog links cool-and-nice)
+           ("https://firefly.nu/feeds/all.atom.xml" blog cool-and-nice)
+           ("https://tazj.in/feed.atom" blog cool-and-nice)
+           ("https://alyssa.is/feed.xml" blog cool-and-nice)
+           ("https://eta.st/feed.xml" blog cool-and-nice)
+           ("https://spectrum-os.org/git/www/atom/bibliography.html" links blog)
+           ("https://vulns.xyz/feed.xml" blog)
+           ("https://www.german-foreign-policy.com/?type=9818" news)
+           ("https://niedzejkob.p4.team/rss.xml" blog)
+           ("https://grahamc.com/feed/" blog)
+           ("http://blog.nullspace.io/feed.xml" blog)
+           ("https://blog.kingcons.io/rss.xml" blog)
+           ("https://www.imperialviolet.org/iv-rss.xml" blog)
+           ("https://22gato.tumblr.com/rss" pictures cool-and-nice)
+           ("https://theprofoundprogrammer.com/rss" blog)
+           ("http://shitopenlabsays.tumblr.com/rss" openlab)
+           ("https://kristaps.bsd.lv/lowdown/atom.xml" releases)
+           ("http://0pointer.net/blog/index.atom" blog)
+           ("https://emacsninja.com/feed.atom" blog)
+           ("https://emacshorrors.com/feed.atom" blog)
+           ("http://therealmntmn.tumblr.com/rss" blog)
+           ("http://blog.duangle.com/feeds/posts/default" blog)
+           ("http://ccc.de/de/rss/updates.xml" news)
+           ("http://ffaaaffaffaffaa.tumblr.com/rss" pictures)
+           ("http://fotografiona.tumblr.com/rss" pictures)
+           ("http://guteaussicht.org/rss" pictures)
+           ("http://konvergenzfehler.de/feed/" blog)
+           ("https://markuscisler.com/feed.xml" blog)
+           ("http://www.plomlompom.de/PlomRogue/plomwiki.php?action=Blog_Atom" blog)
+           ("http://www.whvrt.de/rss" pictures)
+           ("https://echtsuppe.wordpress.com/feed/" blog defunct)
+           ("https://mgsloan.com/feed.xml" blog)
+           ("http://beza1e1.tuxen.de/blog_en.atom" blog)
+           ("https://anchor.fm/s/94bb000/podcast/rss" podcast))
+           ;; http://www.wollenzin.de/feed/ ;_;
 
-(setq elfeed-feeds
-      (append
-       ;; immutable subscriptions tracked in git
-       '(("https://repology.org/maintainer/sternenseemann%40systemli.org/feed-for-repo/nix_unstable/atom" dashboard releases)
-         ("http://hundimbuero.blogspot.com/feeds/posts/default?alt=rss" blog cool-and-nice)
-         ("gopher://text.causal.agency/0feed.atom" blog)
-         ("http://xsteadfastx.org/feed/" blog cool-and-nice)
-         ("https://tvl.fyi/feed.atom" blog cool-and-nice)
-         ("https://hannes.robur.coop/atom" blog)
-         ("https://stevelosh.com/rss.xml" blog)
-         ("https://planet.lisp.org/rss20.xml" blog)
-         ("https://hyperthings.garden/rss/all-posts.xml" blog)
-         ("https://blog.benjojo.co.uk/rss.xml" blog)
-         ("https://leahneukirchen.org/blog/index.atom" blog cool-and-nice)
-         ("https://leahneukirchen.org/trivium/index.atom" blog links cool-and-nice)
-         ("https://firefly.nu/feeds/all.atom.xml" blog cool-and-nice)
-         ("https://tazj.in/feed.atom" blog cool-and-nice)
-         ("https://alyssa.is/feed.xml" blog cool-and-nice)
-         ("https://eta.st/feed.xml" blog cool-and-nice)
-         ("https://spectrum-os.org/git/www/atom/bibliography.html" links blog)
-         ("https://rachelbythebay.com/w/atom.xml" blog)
-         ("http://evrl.com/feed.xml" blog)
-         ("https://vulns.xyz/feed.xml" blog)
-         ("https://www.german-foreign-policy.com/?type=9818" news)
-         ("https://niedzejkob.p4.team/rss.xml" blog)
-         ("https://grahamc.com/feed/" blog)
-         ("https://michael.stapelberg.ch/feed.xml" blog)
-         ("https://kazu-yamamoto.hatenablog.jp/feed" blog)
-         ("https://ariadne.space/feed/" blog)
-         ("https://bodil.lol/rss.xml" blog)
-         ("http://blog.nullspace.io/feed.xml" blog)
-         ("https://blog.kingcons.io/rss.xml" blog)
-         ("http://jaspervdj.be/rss.xml" blog)
-         ("https://www.imperialviolet.org/iv-rss.xml" blog)
-         ("https://latacora.micro.blog/feed.xml" blog)
-         ("https://22gato.tumblr.com/rss" pictures cool-and-nice)
-         ("https://theprofoundprogrammer.com/rss" blog)
-         ("https://wiki.openlab-augsburg.de/_feed" openlab)
-         ("http://shitopenlabsays.tumblr.com/rss" openlab)
-         ("http://suckless.org/atom.xml" releases)
-         ("https://kristaps.bsd.lv/lowdown/atom.xml" releases)
-         ("http://0pointer.net/blog/index.atom" blog)
-         ("https://emacsninja.com/feed.atom" blog)
-         ("https://emacshorrors.com/feed.atom" blog)
-         ("http://therealmntmn.tumblr.com/rss" blog)
-         ("http://blog.duangle.com/feeds/posts/default" blog)
-         ("http://blog.johl.io/atom.xml" blog)
-         ("http://blog.z3bra.org/rss/feed.xml" blog)
-         ("http://ccc.de/de/rss/updates.xml" news)
-         ;; ("http://fabienne.us/feed/" blog) ; database error
-         ("http://feeds.feedburner.com/baschtcom" blog)
-         ("http://ffaaaffaffaffaa.tumblr.com/rss" pictures)
-         ("http://fnordig.de/feed.xml" blog)
-         ("http://fotografiona.tumblr.com/rss" pictures)
-         ("https://grandhotel-cosmopolis.org/de/feed" news)
-         ("http://guteaussicht.org/rss" pictures)
-         ("http://konvergenzfehler.de/feed/" blog)
-         ("https://markuscisler.com/feed.xml" blog)
-         ("http://n00bcore.de/feed/" podcast)
-         ("http://spacethatneverwas.tumblr.com/rss" pictures)
-         ("http://theresa.someserver.de/blog/?feed=rss2" blog)
-         ("http://www.frumble.de/blog/feed/" blog)
-         ("http://www.plomlompom.de/PlomRogue/plomwiki.php?action=Blog_Atom" blog)
-         ("http://www.whvrt.de/rss" pictures)
-         ("http://www.windytan.com/feeds/posts/default" blog)
-         ("https://echtsuppe.wordpress.com/feed/" blog defunct)
-         ("https://mgsloan.com/feed.xml" blog)
-         ("https://notes.sterni.lv/atom.xml" me)
-         ("http://arduina.net/feed/" defunct blog)
-         ("http://beza1e1.tuxen.de/blog_en.atom" blog)
-         ("https://anchor.fm/s/94bb000/podcast/rss" podcast))
-         ;; http://www.wollenzin.de/feed/ ;_;
-
-       ;; add more feeds from an untracked file in $HOME
-       (let ((file (concat (getenv "HOME")
-                           "/.config/emacs-custom/mutable-subscriptions.el")))
-         (when (file-exists-p file)
-           (read (with-temp-buffer
-                   (insert-file-contents file)
-                   (buffer-string)))))))
+         ;; add more feeds from an untracked file in $HOME
+         (let ((file (concat (getenv "HOME")
+                             "/.config/emacs-custom/mutable-subscriptions.el")))
+           (when (file-exists-p file)
+             (read (with-temp-buffer
+                     (insert-file-contents file)
+                     (buffer-string))))))))
 
 (provide 'subscriptions)
diff --git a/users/sterni/exercises/aoc/.gitignore b/users/sterni/exercises/aoc/.gitignore
index de53cfc531..b9720d7451 100644
--- a/users/sterni/exercises/aoc/.gitignore
+++ b/users/sterni/exercises/aoc/.gitignore
@@ -1 +1,2 @@
-/*/input
\ No newline at end of file
+/*/input
+/*/*/input
\ No newline at end of file
diff --git a/users/sterni/exercises/aoc/2021/default.nix b/users/sterni/exercises/aoc/2021/default.nix
index 33d3a92ac7..d3ed563ec6 100644
--- a/users/sterni/exercises/aoc/2021/default.nix
+++ b/users/sterni/exercises/aoc/2021/default.nix
@@ -1,4 +1,4 @@
-{ depot ? import ../../../../.. {}
+{ depot ? import ../../../../.. { }
 , pkgs ? depot.third_party.nixpkgs
 , ...
 }:
diff --git a/users/sterni/exercises/aoc/2021/solutions.bqn b/users/sterni/exercises/aoc/2021/solutions.bqn
index 4cedb567f9..755c944046 100755
--- a/users/sterni/exercises/aoc/2021/solutions.bqn
+++ b/users/sterni/exercises/aoc/2021/solutions.bqn
@@ -1,20 +1,15 @@
 #!/usr/bin/env BQN
 
+⟨Xor⟩ ← β€’Import "../lib.bqn"
+
 #
 # Utilities
 #
 
-IsAsciiNum ← ('0'βŠΈβ‰€βˆ§β‰€βŸœ'9')
-
-ReadInt ← {(π•¨βŠΈΓ—+⊣)´∘⌽-⟜'0'𝕩} # stolen from leah2
-ReadDec ← 10⊸ReadInt
+⟨IsAsciiNum,ReadInt,ReadDec,SplitOn,_fix⟩ ← β€’Import β€’path∾"/../lib.bqn"
 
 ReadInput ← {β€’file.Lines ∾ β€’pathβ€Ώ"/input/day"β€Ώ(β€’Fmt 𝕩)}
 
-SplitOn ← ((⊒ (-1Λ™)⍟⊣¨ +`∘(1⊸»<⊒))∘(≑¨)βŠ”βŠ’)
-
-_fix ← {𝕩 π•Šβˆ˜βŠ’βŸβ‰’ 𝔽 𝕩}
-
 #
 # 2021-12-01
 #
@@ -476,7 +471,6 @@ _EnhancedPixelCount ← {+Β΄β₯ŠβŠ‘ (π•¨βŠΈEnhance)βŸπ•— 𝕩}
 day25Input ← ".>v" ⊐ > ReadInput 25
 day25ExampleInput ← ".>v"βŠβˆ˜β€Ώ10β₯Š"v...>>.vv>.vv>>.vv..>>.>v>...v>>v>>.>.v.v>v.vv.v..>.>>..v....vv..>.>v.v.v..>>v.v....v..v.>"
 
-Xor ← (¬⊸∧∨∧⟜¬)
 MoveHerd ← {(π•©βˆ§π•©β‰ π•¨)+𝕨× (𝕨=𝕩) (Xor⟜(1⊸⌽)∨⊒) (0=𝕩)∧(-1)βŒ½π•¨=𝕩}
 
 _fixCount ← {
diff --git a/users/sterni/exercises/aoc/2022/.skip-subtree b/users/sterni/exercises/aoc/2022/.skip-subtree
new file mode 100644
index 0000000000..39d1894495
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/.skip-subtree
@@ -0,0 +1 @@
+nix solutions don't use readTree and the rest is non-nix
diff --git a/users/sterni/exercises/aoc/2022/01/1.bqn b/users/sterni/exercises/aoc/2022/01/1.bqn
new file mode 100644
index 0000000000..022b476aa9
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/01/1.bqn
@@ -0,0 +1,7 @@
+lib ← β€’Import β€’path∾"/../../lib.bqn"
+input ← lib.ReadDec¨¨ (<"") lib.SplitOn β€’FLines β€’path∾"/input"
+
+aβ€ΏΒ·β€Ώb ← +`3β†‘βˆ¨+´¨ input
+
+β€’Out "day01.1: "βˆΎβ€’Fmt a
+β€’Out "day01.2: "βˆΎβ€’Fmt b
diff --git a/users/sterni/exercises/aoc/2022/01/1.k b/users/sterni/exercises/aoc/2022/01/1.k
new file mode 100644
index 0000000000..42d64dfb6c
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/01/1.k
@@ -0,0 +1 @@
+(+\e@3#>e:(+/.'1_)'(&0=#'i)_i:0:"input")_1
diff --git a/users/sterni/exercises/aoc/2022/02/2.bqn b/users/sterni/exercises/aoc/2022/02/2.bqn
new file mode 100644
index 0000000000..65e3c817bb
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/02/2.bqn
@@ -0,0 +1,7 @@
+lib ← β€’Import β€’path∾"/../../lib.bqn"
+i ← 3|"ABCXYZ"⊸⊐¨ ' ' βŠ‘Β¨βˆ˜lib.SplitOnΒ¨ β€’FLines β€’path∾"/input"
+S1 ← {1+𝕩+3Γ—3|1+𝕩-𝕨}
+S2 ← {𝕨 S1 3|𝕨+𝕩-1}
+
+β€’Out "day02.1: "βˆΎβ€’Fmt +Β΄S1´¨i
+β€’Out "day02.2: "βˆΎβ€’Fmt +Β΄S2´¨i
diff --git a/users/sterni/exercises/aoc/2022/02/2.k b/users/sterni/exercises/aoc/2022/02/2.k
new file mode 100644
index 0000000000..9b6d10058d
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/02/2.k
@@ -0,0 +1 @@
++/{{y+1+3*3!1+y-x}[x]'y,3!x+y-1}.'3!"ABCXYZ"?(0:"input")_'1
diff --git a/users/sterni/exercises/aoc/2022/03/3.bqn b/users/sterni/exercises/aoc/2022/03/3.bqn
new file mode 100644
index 0000000000..642fccd450
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/03/3.bqn
@@ -0,0 +1,8 @@
+i ← β€’FLines β€’path∾"/input"
+c ← ∾(↕26)⊸+Β¨"aA"
+P ← {1+βŠ‘cβŠβŠ‘π•©}
+S ← (∊/⊣)
+G ← ((⊣/(β†•Γ·ΛœβŸœβ‰ ))βŠ”βŠ’)
+
+β€’Out "day03.1: "βˆΎβ€’Fmt +Β΄(P S˝)Β¨2β€Ώβˆ˜βŠΈβ₯ŠΒ¨i
+β€’Out "day03.2: "βˆΎβ€’Fmt +Β΄3(P SΒ΄)¨∘G i
diff --git a/users/sterni/exercises/aoc/2022/03/3.k b/users/sterni/exercises/aoc/2022/03/3.k
new file mode 100644
index 0000000000..3e31f5f32c
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/03/3.k
@@ -0,0 +1 @@
++/'(58*r<1)+r:-96+(,/{?y@&~^x?y}/')'((2 0N#)';0N 3#)@\:0:"input"
diff --git a/users/sterni/exercises/aoc/2022/04/4.bqn b/users/sterni/exercises/aoc/2022/04/4.bqn
new file mode 100644
index 0000000000..0b8f1b4500
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/04/4.bqn
@@ -0,0 +1,11 @@
+⟨SplitOn, ReadDec⟩ ← β€’Import "../../lib.bqn"
+
+Sections ← {
+  aβ€Ώb ← ReadDecΒ¨ (<'-') SplitOn 𝕩
+  β†•βŒΎ(-⟜a) 1+b
+}
+i ← βˆ˜β€Ώ2β₯ŠSectionsΒ¨ ∾(<',') SplitOnΒ¨ β€’FLines "input"
+Is ← ∊´∘((⍋≠¨)⊏⊒)
+
+β€’Out "day04.1: "βˆΎβ€’Fmt +Β΄(∧´Is)˘ i
+β€’Out "day04.2: "βˆΎβ€’Fmt +Β΄(∨´Is)˘ i
diff --git a/users/sterni/exercises/aoc/2022/05/5.bqn b/users/sterni/exercises/aoc/2022/05/5.bqn
new file mode 100644
index 0000000000..15b0dfc805
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/05/5.bqn
@@ -0,0 +1,18 @@
+⟨ReadDec, SplitOn, IsAsciiNum⟩ ← β€’Import "../../lib.bqn"
+rsβ€Ώrc ← (<"") SplitOn β€’FLines "../05/input"
+
+stacks ← {
+  count ← '0'-ΛœβŠ‘βŒ½' ' (β‰ /⊒) βŠ‘βŒ½rs
+  ' ' (β‰ /⊒)Β¨<˘ (countΓ—4) ((»∘(0⊸=)∘(4⊸|)βˆ˜β†•βŠ£)/↑) ⍉> (-1)↓rs
+}
+
+cmds ← {0β€Ώ1β€Ώ1-˜ ReadDecΒ¨ ((∧´IsAsciiNum)Β¨/⊒) (<' ') SplitOn 𝕩}Β¨ rc
+
+_ApplyCmd ← {
+  s Fn _self cβ€Ώfβ€Ώt :
+  mβ€Ώk ← 2↑ c ((β‰€βŸœ(↕≠))βŠ”βŠ’) fβŠ‘s
+  (Fn m)⊸∾⌾(tβŠΈβŠ‘) kΛ™βŒΎ(fβŠΈβŠ‘) s
+}
+
+β€’Out "day05.1: "βˆΎβŠ‘Β¨stacks ⌽_ApplyCmd˜´ ⌽ cmds
+β€’Out "day05.2: "βˆΎβŠ‘Β¨stacks ⊒_ApplyCmd˜´ ⌽ cmds
diff --git a/users/sterni/exercises/aoc/2022/06/6.bqn b/users/sterni/exercises/aoc/2022/06/6.bqn
new file mode 100644
index 0000000000..041a2e9100
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/06/6.bqn
@@ -0,0 +1,4 @@
+i ← βŠ‘β€’FLines "input"
+FirstMarker ← {𝕩+βŠ‘/(βˆ§Β΄βˆ˜Β¬βŠ’)Λ˜π•©β†•i}
+β€’Out "day06.1: "βˆΎβ€’Fmt FirstMarker 4
+β€’Out "day06.2: "βˆΎβ€’Fmt FirstMarker 14
diff --git a/users/sterni/exercises/aoc/2022/06/6.k b/users/sterni/exercises/aoc/2022/06/6.k
new file mode 100644
index 0000000000..3dc0de0a3e
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/06/6.k
@@ -0,0 +1 @@
+4 14{x+*&x=#'?'x':y}\:1:"input"
diff --git a/users/sterni/exercises/aoc/2022/07/7.bqn b/users/sterni/exercises/aoc/2022/07/7.bqn
new file mode 100644
index 0000000000..2fc387f340
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/07/7.bqn
@@ -0,0 +1,24 @@
+lib ← β€’Import "../../lib.bqn"
+cmds ← 1↓ '$' ((+`= ⟜(βŠ‘Β¨))βŠ”βŠ’) β€’FLines "input"
+paths ← (<⟨⟩) {
+  𝕨 π•Š "$ ls": 𝕨;
+  𝕨 π•Š "$ cd /": ⟨⟩;
+  𝕨 π•Š "$ cd ..": (-1)↓𝕨;
+  𝕨 π•Š 𝕩: π•¨βˆΎ<5↓𝕩 # "$ cd …"
+}` βŠ‘Β¨cmds
+ParseLs ← {
+  dirsβ€Ώfiles ← 2↑((lib.IsAsciiNumβˆ˜βŠ‘βˆ˜βŠ‘)Β¨βŠ”βŠ’) ((<' ')⊸lib.SplitOn)Β¨ 1↓𝕩
+  (1βŠ‘Β¨dirs)β‹ˆ(lib.ReadDec 0βŠΈβŠ‘)Β¨files
+}
+dirlists ← ParseLs⌾(1βŠΈβŠ‘)Β¨β₯Šβ‹ˆΛ˜(("$ cd"βŠΈβ‰’βŸœ(4βŠΈβ†‘)βˆ˜βŠ‘Β¨)∘(1⊸⊏)˘/⊒) (⍒≠¨paths)βŠβ‰paths≍cmds
+DirSize ← {βŠ‘π•¨ (βŠ‘βˆ˜(1βŠΈβŠ‘Β¨βˆ˜βŠ£βŠβŠ’)βŠ‘βŠ£) <𝕩}
+DirName ← ∾'/'⊸∾¨
+dirsizes ← βŠ‘Β¨ ⟨⟩ {
+  szs π•Š ⟨dir, subdirsβ€Ώfiles⟩:
+  Canon ← DirName dirβŠΈβˆΎβŸœβ‹ˆ
+  sz ← +Β΄files∾szs⊸DirSize∘CanonΒ¨ subdirs
+  szs∾<szβ‹ˆDirName dir
+}˜´ ⌽dirlists
+
+β€’Out "day07.1: "βˆΎβ€’Fmt +Β΄ 100000 (β‰₯/⊒) dirsizes
+β€’Out "day07.2: "βˆΎβ€’Fmt (30000000-70000000-⌈´dirsizes) ⌊´∘(≀/⊒) dirsizes
diff --git a/users/sterni/exercises/aoc/2022/08/8.bqn b/users/sterni/exercises/aoc/2022/08/8.bqn
new file mode 100644
index 0000000000..91a16d9573
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/08/8.bqn
@@ -0,0 +1,15 @@
+i ← >'0'-Λœβ€’FLines "input"
+Visible ← {
+  _vis ← {(⌈`∘(Β―1βŠΈΒ»Λ˜βŒΎβ‰)<⊒)βŒΎπ• 𝕗}
+  βˆ¨Β΄π•© _visΒ¨ ⟨⊒,⌽,⍉,βŒ½β‰βŸ©
+}
+
+β€’Out "day08.1: "βˆΎβ€’Fmt +Β΄β₯ŠVisible i
+
+ViewingDistances ← {
+  DirView ← {β‰ 1(»⟜(∧`(βŠ‘π•©)⊸>)/⊒) 1↓𝕩}
+  _spliceDir ← {! =´≒𝕗 β‹„ 𝕏⁼(βŠ’β†“(⊏⟜(𝕏𝕗))∘⊣)´¨ β‹ˆβŒœΛœβ†•β‰ π•—}
+  Γ—Β΄ DirView¨¨ 𝕩 _spliceDirΒ¨ ⟨⊒, ⌽˘, ⍉, βŒ½Λ˜β‰βŸ©
+}
+
+β€’Out "day08.2: "βˆΎβ€’Fmt ⌈´β₯ŠViewingDistances i
diff --git a/users/sterni/exercises/aoc/2022/09/9.bqn b/users/sterni/exercises/aoc/2022/09/9.bqn
new file mode 100644
index 0000000000..fff38b5913
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/09/9.bqn
@@ -0,0 +1,17 @@
+⟨SplitOn,ReadDec⟩ ← β€’Import "../../lib.bqn"
+i ← ReadDec⌾(1βŠΈβŠ‘)Β¨ (<' ')⊸SplitOnΒ¨ β€’FLines "input"
+
+UnitDelta ← (⊒÷(|+0⊸=))
+ExpandStep ← {
+  π•Š "L"β€Ώl: π•Š (-l)β€Ώ0;
+  π•Š "R"β€Ώr: π•Š rβ€Ώ0;
+  π•Š "U"β€Ώu: π•Š 0β€Ώu;
+  π•Š "D"β€Ώd: π•Š 0β€Ώ(-d);
+  π•Š delta: ((⌈´|)β₯Š<∘UnitDelta) delta
+}
+
+Step ← {knots π•Š delta: {h π•Š t: (UnitDelta h-t) +⍟(1<⌈´|h-t) t}` (delta⊸+)βŒΎβŠ‘ knots}
+Visited ← {+Β΄0=βŠ’(Β―1βŠΈβŠ‘)Β¨(<𝕨β₯Š<0β€Ώ0) Step` ∾ExpandStepΒ¨ 𝕩}
+
+β€’Out "day09.1: "βˆΎβ€’Fmt  2 Visited i
+β€’Out "day09.2: "βˆΎβ€’Fmt 10 Visited i
diff --git a/users/sterni/exercises/aoc/2022/10/10.bqn b/users/sterni/exercises/aoc/2022/10/10.bqn
new file mode 100644
index 0000000000..04e3d6a8e5
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/10/10.bqn
@@ -0,0 +1,25 @@
+⟨SplitOn,ReadDec⟩ ← β€’Import "../../lib.bqn"
+# Instead of implementing the VM described in the problem, translate the
+# program to instructions with equivalent timing for a similar VM that
+# only needs 1 cycle for every instruction.
+is ← ∾{"noop": <"noop"; 𝕩: (<"noop")∾<ReadDec⌾(1βŠΈβŠ‘) (<' ') SplitOn 𝕩}Β¨ β€’FLines "input"
+
+Op ← {x π•Š "noop": x;x π•Š "addx"β€Ώi: x+i}
+Draw ← {π•Š cβ€Ώxβ€Ώpic: pic∨(↕240)((c-1)⊸=∘⊣∧∊)(⌊⌾(÷⟜40)c)+Β―1+x+↕3}
+_vm ← {
+  is _self s: (βŠ‘s)β‰₯β‰ is? s;
+  is _self prevβ€Ώsumβ€Ώxβ€Ώpic:
+  cycle ← prev+1
+  is _self ⟨
+    cycle,
+    sum+xΓ—cycleΓ—βŠ‘cycle∊20β€Ώ60β€Ώ100β€Ώ140β€Ώ180β€Ώ220,
+    x Op (Β―1+cycle)βŠ‘is,
+    Draw cycleβ€Ώxβ€Ώpic
+  ⟩
+}
+
+Β·β€Ώsumβ€ΏΒ·β€Ώpic ← is _vm 1β€Ώ0β€Ώ1β€Ώ(240β₯Š0)
+
+β€’Out "day10.1: "βˆΎβ€’Fmt sum
+β€’Out "day10.2:"
+β€’Show ".#" ⊏˜ βˆ˜β€Ώ40β₯Špic
diff --git a/users/sterni/exercises/aoc/2022/11/11.bqn b/users/sterni/exercises/aoc/2022/11/11.bqn
new file mode 100644
index 0000000000..12b9b5097a
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/11/11.bqn
@@ -0,0 +1,41 @@
+# needs export BQNLIBS=/path/to/mlochbaum/bqn-libs
+⟨ReadDec,ImportBqnLibs⟩ ← β€’Import "../../lib.bqn"
+⟨Split⟩ ← ImportBqnLibs "strings.bqn"
+MakeOp ← {
+  π•Š aβ€Ώ"+"β€Ώb: π•Š aβ€Ώ+β€Ώb;
+  π•Š aβ€Ώ"*"β€Ώb: π•Š aβ€ΏΓ—β€Ώb;
+  π•Š aβ€Ώopβ€Ώb:
+  isβ€Ώxs ← (<"old") (β‰‘Β¨βŠ”βŠ’) aβ€Ώb
+  {opΒ΄ (𝕩⋆≠xs) ∾ReadDecΒ¨ is}
+}
+ParseMonkey ← {
+  Β·β€Ώitemsβ€Ώopβ€Ώifβ€Ώthenβ€Ώelse:
+  {
+    initial ⇐ ReadDecΒ¨ ", " Split 18↓items
+    op ⇐ MakeOp " " Split 19↓op
+    if ⇐ ReadDec 21↓if
+    then ⇐ ReadDec 29↓then
+    else ⇐ ReadDec 30↓else
+  }
+}
+monkeys ← ParseMonkeyΒ¨ 1↓' '((+`(β‰ βŸœβŠ‘)Β¨)βŠ”βŠ’)0(β‰ βŸœβ‰ Β¨/⊒)β€’FLines "input"
+items ← {𝕩.initial}Β¨ monkeys
+lim ← Γ—Β΄{𝕩.if}Β¨ monkeys
+
+Sim ← {
+  div π•Š len:
+  Turn ← {
+    items π•Š turnidx:
+    i ← (β‰ monkeys)|turnidx
+    m ← iβŠ‘monkeys
+
+    worry ← lim|⌊div÷˜ m.OpΒ¨ iβŠ‘items
+    elseβ€Ώthen ← 2↑0 (=⟜(m.if⊸|)βŠ”βŠ’) worry
+
+    ⟨then, else⟩⊸(∾˜¨)⌾(m.thenβ€Ώm.else⊸⊏) βŸ¨βŸ©Λ™βŒΎ(iβŠΈβŠ‘) items
+  }
+  Γ—Β΄2β†‘βˆ¨+˝(<items) ((β‰ βŠ‘)⊸(>((β†•βŠ£)=|)Β¨)Γ—(β‰ Β¨Λ˜)∘>∘(⊣»Turn`)) ↕lenΓ—β‰ items
+}
+
+β€’Out "day11.1: "βˆΎβ€’Fmt 3 Sim 20
+β€’Out "day11.2: "βˆΎβ€’Fmt 1 Sim 10000
diff --git a/users/sterni/exercises/aoc/2022/12/12.bqn b/users/sterni/exercises/aoc/2022/12/12.bqn
new file mode 100644
index 0000000000..cf42f6f899
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/12/12.bqn
@@ -0,0 +1,16 @@
+⟨ImportBqnLibs,_fix⟩ ← β€’Import "../../lib.bqn"
+⟨ReplaceAll⟩ ← ImportBqnLibs "strings.bqn"
+i ← >β€’FLines "input"
+
+elevation ← 'a'-˜⟨"S","E"βŸ©β€ΏβŸ¨"a","z"⟩ ReplaceAll⌾β₯Š i
+starts ← (βŠβŸœβˆžβ€Ώ0)¨⟨'S'=i,0=elevation⟩
+end ← 'E'=i
+
+Step ← {
+  π•Š steps:
+  Go ← {𝕏⁼((βŠ’βˆΎΒ¨β†•βˆ˜β‰’)(β‰€βŸœ(∞⊸»˘∘+⟜1))Λœπ•elevation)βŠ‘>((β₯ŠβŸœβˆž)βˆ˜β‰’βŠΈβ‹ˆ)˜∞⊸»˘1+𝕏steps}
+  steps⌊´Go¨⟨⊒,⌽˘,⍉,β‰βŒ½βŸ©
+}
+Shortest ← {βŠ‘end/βŠΈβŠβ—‹β₯ŠStep _fix 𝕩}
+
+β€’OutΒ¨ "day12.1: "β€Ώ"day12.2: "∾¨ β€’Fmt∘ShortestΒ¨ starts
diff --git a/users/sterni/exercises/aoc/2022/13/13.bqn b/users/sterni/exercises/aoc/2022/13/13.bqn
new file mode 100644
index 0000000000..0242cc5093
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/13/13.bqn
@@ -0,0 +1,14 @@
+lib ← β€’Import "../../lib.bqn"
+str ← lib.ImportBqnLibs "strings.bqn"
+i ← >⟨"[","]"βŸ©β€ΏβŸ¨"⟨","⟩"⟩⊸(β€’BQN str.ReplaceAll)¨¨0((βŸ¨βŸ©βŠΈβ‰‘Β¨Β―1Λ™βŸβŠ£Β¨(+`(=βŸœβ‰ )Β¨))βŠ”βŠ’)β€’FLines "input"
+
+Ord ← {
+  i1 π•Š i2: 1β€Ώ1≑‒TypeΒ¨ i1β€Ώi2? Β―1β€Ώ1β€Ώ0βŠ‘Λœi1(=+≀)i2;
+  i1 π•Š l2: 1β€Ώ0≑‒TypeΒ¨ i1β€Ώl2? l2 Ord˜ β‹ˆi1;
+  l1 π•Š i2: 0β€Ώ1≑‒TypeΒ¨ l1β€Ώi2? l1 Ord β‹ˆi2;
+  l1 π•Š l2: 0β€Ώ0≑‒TypeΒ¨ l1β€Ώl2?
+  βŠ‘1↑0(β‰ /⊒)l1 OrdΒ¨β—‹((l1βŒˆβ—‹β‰ l2)⊸(β†‘βŒΎ(+⟜1))) l2
+}
+
+β€’Out "day13.1: "βˆΎβ€’Fmt +Β΄1+/(1⊸=OrdΒ΄)˘i
+β€’Out "day13.2: "βˆΎβ€’Fmt Γ—Β΄1β€Ώ2++´˘¯1=⟨⟨2βŸ©βŸ©β€ΏβŸ¨βŸ¨6⟩⟩Ord⌜β₯Ši
diff --git a/users/sterni/exercises/aoc/2022/15/15.bqn b/users/sterni/exercises/aoc/2022/15/15.bqn
new file mode 100644
index 0000000000..e47355856b
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/15/15.bqn
@@ -0,0 +1,18 @@
+lib ← β€’Import "../../lib.bqn"
+
+F ← ¬∘('-'⊸=∨lib.IsAsciiNum)
+i ← βŒ½Λ˜Λ˜βˆ˜β€Ώ2β€Ώ2β₯Šlib.ReadDecΒ¨>(0⊸<βŸœβ‰ Β¨/⊒)∘((F Β―1Λ™βŸβŠ£Β¨(+`F))βŠ”βŠ’)Β¨ β€’FLines "input"
+
+ssp ← 4000000
+
+sds ← (⊏˘∾˘(+´˘(|(-˝))˘)) i
+
+# _fix is needed to deal with e.g. ⟨0β€Ώ15, 5β€Ώ8, 12β€Ώ23⟩
+MergeRanges ← ((βŠ‘βˆΎβŠ‘βˆ˜βŒ½)∘∧∘∾)¨∘(+`∘((<βˆžβ€Ώβˆž)⊸»{<Β΄1β€Ώ2βŠπ•¨βˆΎπ•©}¨⊒)βŠ”βŠ’) lib._fix
+
+Range ← {cky π•Š yβ€Ώxβ€Ώd: x+Β―1β€Ώ1Γ—d-|cky-y}
+RangesY ← {<Λ˜βˆ§π•©(⊣Range˘({cky π•Š yβ€ΏΒ·β€Ώd: dβ‰₯|y-cky}˘/⊒))sds}
+OutRangeY ← {(1<β‰ )β—ΆβŸ¨0Λ™,π•©βŠΈ+∘(sspβŠΈΓ—βŸœ(+⟜1))∘(1βŠΈβŠ‘)∘∾⟩ MergeRanges ssp⌊0⌈RangesY 𝕩}
+
+β€’Out "day15.1: "βˆΎβ€’Fmt +Β΄-˜´¨MergeRanges RangesY 2÷˜ssp
+β€’Out "day15.2: "βˆΎβ€’Fmt +Β΄OutRangeY¨↕ssp
diff --git a/users/sterni/exercises/aoc/2022/16/16.k b/users/sterni/exercises/aoc/2022/16/16.k
new file mode 100644
index 0000000000..40d5ace60e
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/16/16.k
@@ -0,0 +1,21 @@
+/ parsing
+(f;r;t):+{x[1 4],,9_x^'","}'" "\' 0:"input"
+(f;t):`s$''(f;t)
+r:f!{`I$x[&(x<58)&47<x]}' r
+g:f!t
+
+/ total flow scoring
+tf: {+/+\{x,(30-#x)#0}((r.)'x)*`XX=':x}
+
+/ valves to open
+vto: f^(=r).0;
+
+/ paths to keep after each step
+best: {x[(1000&#x)#>tf'x]}
+
+p:{[n;ps]
+  ms:ps[&~fin:(#vto)={#?x[&`X=':x]}'ps];
+  rt: best[ps[&fin],(ms[w],'(,*|)'ms[w:&{(0|/vto=l)&~|/0&':x=l:*|x}'ms]),,/{x,/:,'g[*|x]}' ms];
+  $[n>1;o[n-1;rt];rt]}
+
+*tf'p[29;,,`AA]
diff --git a/users/sterni/exercises/aoc/2022/17/17.bqn b/users/sterni/exercises/aoc/2022/17/17.bqn
new file mode 100644
index 0000000000..21b94221aa
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/17/17.bqn
@@ -0,0 +1,51 @@
+jets ← '>'= "<>" (∊˜/⊒) β€’FChars "input"
+pieces ← >¨⟨1β€Ώ1β€Ώ1β€Ώ1βŸ©β€ΏβŸ¨0β€Ώ1β€Ώ0,1β€Ώ1β€Ώ1,0β€Ώ1β€Ώ0βŸ©β€ΏβŸ¨0β€Ώ0β€Ώ1,0β€Ώ0β€Ώ1,1β€Ώ1β€Ώ1βŸ©β€ΏβŸ¨β‹ˆ1,β‹ˆ1,β‹ˆ1,β‹ˆ1βŸ©β€ΏβŸ¨1β€Ώ1,1β€Ώ1⟩
+w ← 7
+initial ← 0β€Ώwβ₯Š0
+
+# Warning: mutated global!
+ji ← 0
+_try ← {(⊒ π•©Λ™βŸ(β‰ β—‹(+´∘β₯Šβˆ˜βˆ¨βŸœπ•¨)) 𝔽) 𝕩}
+Fall ← {
+  pushed ← 𝕨 ((jiβŠ‘jets)β—ΆΒ«β€ΏΒ»)˘ _try 𝕩
+  ji ↩ (β‰ jets)|ji+1
+  fallen ← 𝕨 Β» _try pushed
+  𝕨 π•ŠβŸ(pushedβ‰’fallen) fallen
+}
+Height ← β‰ βˆ˜(∨´˘/⊒)
+ThrowPiece ← {
+  piece ← 𝕩 (|ΛœβŸœβ‰ βŠ‘βŠ’) pieces
+  chamber ← (((3+β‰ piece)⊸+βˆ˜βŠ‘βˆ˜(1βŠΈβ†‘)∘⌽∘(1⊸+)∘/∨´˘)β†‘βŠ’)βŒΎβŒ½π•¨
+  falling ← (β‰ chamber)↑(»⍟2 wβŠΈβ†‘)˘piece
+  chamber (⊣∨Fall) falling
+}
+
+β€’Out "day17.1: "βˆΎβ€’Fmt Height initial ThrowPiece˜´ βŒ½β†•2022
+
+# https://mlochbaum.github.io/BQN/doc/control.html#while
+While ← {𝕩{π”½βŸπ”Ύβˆ˜π”½_𝕣_π”Ύβˆ˜π”½βŸπ”Ύπ•©}𝕨@}Β΄
+{
+  target ← 1000000000000
+  ji ↩ 0 β‹„ i ← 0 β‹„ res ← @
+
+  chamber ← initial
+  cycles ← βŸ¨β‰ pieces,β‰ jets⟩β₯Š<⟨⟩
+
+  While {𝕀⋄res=@}β€Ώ{𝕀
+    chamber ↩ chamber ThrowPiece i
+    i +↩ 1
+
+    t ← i|Λœβ‰ pieces
+    cycles ↩ {
+      new ← π•©βˆΎ<iβ‹ˆHeight chamber
+      res ↩ {π•Š 𝕩:
+        ⟨pl,hlβŸ©β€ΏΒ· ← chk ← Β―2↑new
+        pdβ€Ώhd ← -´⌽chk
+        @Λ™βŸ(0β‰ pd|target-pl) hl+hdΓ—pd÷˜target-pl
+      }⍟(1<β‰ new) @
+      new
+    }⌾(tβ€ΏjiβŠΈβŠ‘) cycles
+  }
+
+  β€’Out "day17.2: "βˆΎβ€’Fmt res
+}
diff --git a/users/sterni/exercises/aoc/2022/18/18.bqn b/users/sterni/exercises/aoc/2022/18/18.bqn
new file mode 100644
index 0000000000..76ec569fed
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/18/18.bqn
@@ -0,0 +1,14 @@
+lib ← β€’Import "../../lib.bqn"
+
+i ← (lib.ReadDecΒ¨(<',')⊸lib.SplitOn)Β¨ β€’FLines "input"
+dim ← 1+⌈´i
+cubes ← iβˆŠΛœβ†•dim
+
+views ← ⟨0β€Ώ1β€Ώ2, 1β€Ώ2β€Ώ0, 2β€Ώ0β€Ώ1⟩
+Exposed ← {(6Γ—+Β΄β₯Šπ•©)-2Γ—+Β΄views{+Β΄β₯Š(∧˝˘)2↕𝕨⍉𝕩}Β¨<𝕩}
+Interior ← {(¬𝕩)∧´views{((lib.Xor`∘((∊∧⊒)∨»∘(∊⌾⌽∧⊒)))βŽ‰1)⌾(π•¨βŠΈβ‰)𝕩}Β¨<𝕩}
+Displace ← {⌈´(β₯ŠβŠ’β€ΏβŒ½β‹ˆβŒœviews){Fβ€Ώa π•Š 𝕩:((-∘¬∘(Β»((0⊸=⊣)∧>)⊒)⌈⊒)βŽ‰1)⌾(F aβŠΈβ‰)𝕩}Β¨<𝕩}
+Exterior ← (⊒-β—‹Exposed Β―1⊸=∘(Displace lib._fix)∘(-∘Interior+⊒))
+
+β€’Out "day18.1: "βˆΎβ€’Fmt Exposed cubes
+β€’Out "day18.2: "βˆΎβ€’Fmt Exterior cubes
diff --git a/users/sterni/exercises/aoc/2022/20/20.bqn b/users/sterni/exercises/aoc/2022/20/20.bqn
new file mode 100644
index 0000000000..8d4c905e87
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/20/20.bqn
@@ -0,0 +1,13 @@
+⟨ReadDec⟩ ← β€’Import "../../lib.bqn"
+enc ← ReadDecΒ¨ β€’FLines "input"
+
+CoordSum ← +´∘(1000β€Ώ2000β€Ώ3000⊸((βŠ’β‰ βŠΈ|+⟜(βŠ‘βˆ˜(/=⟜0)∘⊒))⊏⊒))
+Mix ← {
+  M ← {m π•Š i:
+    l ← β‰ m
+    i {n ← (l-1)|(π•©βŠ‘m)+βŠ‘/𝕩=𝕨 β‹„ (nβŠΈβ†‘(βˆΎβŸœπ•©)⊸∾nβŠΈβ†“) 𝕩(β‰ /⊒)𝕨}˜´ βŒ½β†•l
+  }
+  CoordSum ((⊒MβŸπ•¨β†•βˆ˜β‰ )⊏⊒) 𝕩
+}
+β€’Out "day20.1: "βˆΎβ€’Fmt 1 Mix enc
+β€’Out "day20.2: "βˆΎβ€’Fmt 10 Mix 811589153Γ—enc
diff --git a/users/sterni/exercises/aoc/2022/21/21.bqn b/users/sterni/exercises/aoc/2022/21/21.bqn
new file mode 100644
index 0000000000..2f91f55d44
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/21/21.bqn
@@ -0,0 +1,25 @@
+⟨ImportBqnLibs, IsAsciiNum, ReadDec⟩ ← β€’Import "../../lib.bqn"
+⟨ReplaceAll, Split⟩ ← ImportBqnLibs "strings.bqn"
+
+i ← ": "⊸SplitΒ¨ β€’FLines "input"
+ReplaceInts ← {
+  π•Š 𝕩: π•ŠΒ΄ 2↑(¬∘(∧´IsAsciiNumβˆ˜βŠ‘βˆ˜βŒ½)Β¨βŠ”βŠ’) 𝕩;
+  # TODO: Efficient replace on tokens
+  is π•Š es: (((β€’Fmt⍟(0βŠΈβ‰ β€’Type))¨⌾(1βŠΈβŠ‘) <Λ˜β‰>is)⊸ReplaceAll⌾(1βŠΈβŠ‘))Β¨ es
+}
+
+c ← 0
+CanEval ← (IsAsciiNum∨∊⟜"+-/* ")
+Eval ← {
+  aβ€Ώsβ€Ώb ← " " Split 𝕩
+  f ← βŠ‘+β€Ώ-β€ΏΓ—β€ΏΓ·βŠΛœ"+-*/"⊐s
+  a Fβ—‹ReadDec b
+}
+EvalExprs ← {
+  pβ€Ώe ← 2↑((∧´CanEvalβˆ˜βŠ‘βˆ˜βŒ½)Β¨βŠ”βŠ’) 𝕩
+  ev ← (Eval⌾(βŠ‘βŒ½))Β¨ e
+  c +↩1
+  (βŠ‘(βŠ‘Β¨ev)∊˜<"root")β—ΆβŸ¨EvalExprs∘(ReplaceInts⟜p),1βŠΈβŠ‘βŠ‘βŸ© ev
+}
+
+β€’Show EvalExprs ReplaceInts i
diff --git a/users/sterni/exercises/aoc/2022/25/25.bqn b/users/sterni/exercises/aoc/2022/25/25.bqn
new file mode 100644
index 0000000000..921099141f
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/25/25.bqn
@@ -0,0 +1,4 @@
+c ← "=-012"
+F ← +´∘(βŠ’Γ—(5βŠΈβ‹†)βˆ˜βŒ½βˆ˜β†•βˆ˜β‰ )∘(-⟜2)∘(c⊸⊐)
+T ← {c⊏˜5|2+𝕩 {(⌊5Γ·Λœπ•¨+2) (π•ŠβŸœ(π•¨βŠΈβˆΎ))⍟(0<𝕨) 𝕩} ⟨⟩}
+β€’Out "day25.1: "∾T +Β΄FΒ¨ β€’FLines "input"
diff --git a/users/sterni/exercises/aoc/2022/25/25.k b/users/sterni/exercises/aoc/2022/25/25.k
new file mode 100644
index 0000000000..df956f002f
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/25/25.k
@@ -0,0 +1 @@
+c@2+{(1_o,0)+x+-5*o:2<x}/5\+/{5/x-2}'(c:"=-012")?0:"input"
diff --git a/users/sterni/exercises/aoc/2022/README.md b/users/sterni/exercises/aoc/2022/README.md
new file mode 100644
index 0000000000..65d51dd21f
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/README.md
@@ -0,0 +1,8 @@
+# sterni's [Advent of Code 2022](adventofcode.com/2022)
+
+I'm trying to do it in BQN again to redeem myself for my unfinished [AoC 2021](../2021),
+but will allow myself falling back to another language if I get stuck, so I actually
+complete this one.
+~~I also plan to write additional solutions in Nix (when I have the time) in order to
+throw `//tvix/eval` against some new problems.~~
+We'll see how it goes, as my December promises to be quite busy.
diff --git a/users/sterni/exercises/aoc/2022/default.nix b/users/sterni/exercises/aoc/2022/default.nix
new file mode 100644
index 0000000000..01134d1306
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/default.nix
@@ -0,0 +1,53 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  inherit (pkgs.buildPackages) cbqn ngn-k;
+
+  # input files are not checked in
+  meta.ci.skip = true;
+
+  BQNLIBS = pkgs.fetchFromGitHub {
+    owner = "mlochbaum";
+    repo = "bqn-libs";
+    rev = "d56d8ea0b8c294fac7274678d9ab112553a03f42";
+    sha256 = "1c1bkqj62v8m13jgaa32ridy0fk5iqysq5b2qwxbqxhky5zwnk9h";
+  };
+in
+
+depot.nix.readTree.drvTargets {
+  shell = pkgs.mkShell {
+    name = "aoc-2022-shell";
+    packages = [
+      cbqn
+      ngn-k
+    ];
+
+    inherit BQNLIBS;
+  };
+
+  bqn = pkgs.runCommand "bqn-aoc-2022"
+    {
+      nativeBuildInputs = [
+        cbqn
+      ];
+
+      aoc = builtins.path {
+        name = "bqn-aoc-2022";
+        path = ./../.;
+        # Need lib.bqn from ../ and all inputs as well as bqn files from ./*
+        filter = path: type:
+          lib.hasSuffix ".bqn" path || (
+            lib.hasPrefix (toString ./.) path
+            && (
+              type == "directory"
+              || lib.hasSuffix "/input" path
+            )
+          );
+      };
+
+      inherit meta BQNLIBS;
+    }
+    ''
+      find "$aoc/2022" -name '*.bqn' -exec BQN {} \; | tee "$out"
+    '';
+}
diff --git a/users/sterni/exercises/aoc/lib.bqn b/users/sterni/exercises/aoc/lib.bqn
new file mode 100644
index 0000000000..e870a5dfa4
--- /dev/null
+++ b/users/sterni/exercises/aoc/lib.bqn
@@ -0,0 +1,18 @@
+IsAsciiNum ⇐ ('0'βŠΈβ‰€βˆ§β‰€βŸœ'9')
+IsAlpha ⇐ (('a'βŠΈβ‰€βˆ§β‰€βŸœ'z')∨('A'βŠΈβ‰€βˆ§β‰€βŸœ'Z'))
+
+# based on leah2's function
+ReadInt ⇐ {
+  𝕨 π•Š 𝕩: '-'=βŠ‘π•©? -𝕨 π•Š 1↓𝕩;
+  𝕨 π•Š 𝕩: (π•¨βŠΈΓ—+⊣)´∘⌽-⟜'0'𝕩
+}
+ReadDec ⇐ 10⊸ReadInt
+
+SplitOn ⇐ ((⊒ (-1Λ™)⍟⊣¨ +`∘(1⊸»<⊒))∘(≑¨)βŠ”βŠ’)
+SplitAt ← ((βŠ£β‰€β†•βˆ˜β‰ βˆ˜βŠ’)βŠ”βŠ’)
+
+_fix ⇐ {𝕩 π•Šβˆ˜βŠ’βŸβ‰’ 𝔽 𝕩}
+
+ImportBqnLibs ⇐ {β€’Import π•©βˆΎΛœ"/"∾˜¯1↓1βŠ‘β€’SH "printenv"β€Ώ"BQNLIBS"}
+
+Xor ⇐ (¬⊸∧∨∧⟜¬)
diff --git a/users/sterni/external/flipdot-gschichtler.nix b/users/sterni/external/flipdot-gschichtler.nix
new file mode 100644
index 0000000000..58f4fe1e7c
--- /dev/null
+++ b/users/sterni/external/flipdot-gschichtler.nix
@@ -0,0 +1,9 @@
+{ pkgs, depot, ... }:
+
+import depot.users.sterni.external.sources.flipdot-gschichtler { inherit pkgs; } // {
+  # all targets we care about for depot
+  meta.ci.targets = [
+    "bahnhofshalle"
+    "warteraum"
+  ];
+}
diff --git a/users/sterni/external/likely-music.nix b/users/sterni/external/likely-music.nix
new file mode 100644
index 0000000000..cfb6d120bd
--- /dev/null
+++ b/users/sterni/external/likely-music.nix
@@ -0,0 +1,11 @@
+{ depot, pkgs, ... }:
+
+import depot.users.sterni.external.sources.likely-music
+  {
+    inherit pkgs;
+    inherit (depot.third_party) napalm;
+  } // {
+  meta.ci.targets = [
+    "likely-music"
+  ];
+}
diff --git a/users/sterni/external/sources.json b/users/sterni/external/sources.json
new file mode 100644
index 0000000000..5233aed23e
--- /dev/null
+++ b/users/sterni/external/sources.json
@@ -0,0 +1,26 @@
+{
+    "flipdot-gschichtler": {
+        "branch": "master",
+        "description": "send text to the flipdots, orderly queued",
+        "homepage": "https://flipdot.openlab-augsburg.de",
+        "owner": "openlab-aux",
+        "repo": "flipdot-gschichtler",
+        "rev": "93683a7fff04e167963b70a8906f982567646501",
+        "sha256": "134kgmlv63vzdvc3lr0rys55klmzip7qpfnyzssahihp4mjyyq16",
+        "type": "tarball",
+        "url": "https://github.com/openlab-aux/flipdot-gschichtler/archive/93683a7fff04e167963b70a8906f982567646501.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "likely-music": {
+        "branch": "master",
+        "description": "experimental application for probabilistic music composition",
+        "homepage": "",
+        "owner": "sternenseemann",
+        "repo": "likely-music",
+        "rev": "c9bef141d846c493a045385ab8146aa28fc8ef33",
+        "sha256": "1wqgxx8wk7lrvyn9h66gga2wf7dcq7si8wq1w5gfhjnwnsrnvs6y",
+        "type": "tarball",
+        "url": "https://github.com/sternenseemann/likely-music/archive/c9bef141d846c493a045385ab8146aa28fc8ef33.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    }
+}
diff --git a/users/sterni/external/sources.nix b/users/sterni/external/sources.nix
new file mode 100644
index 0000000000..cdcde6da5c
--- /dev/null
+++ b/users/sterni/external/sources.nix
@@ -0,0 +1,197 @@
+# This file has been generated by Niv.
+_:
+let
+
+  #
+  # The fetchers. fetch_<type> fetches specs of type <type>.
+  #
+
+  fetch_file = pkgs: name: spec:
+    let
+      name' = sanitizeName name + "-src";
+    in
+    if spec.builtin or true then
+      builtins_fetchurl { inherit (spec) url sha256; name = name'; }
+    else
+      pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
+
+  fetch_tarball = pkgs: name: spec:
+    let
+      name' = sanitizeName name + "-src";
+    in
+    if spec.builtin or true then
+      builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
+    else
+      pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
+
+  fetch_git = name: spec:
+    let
+      ref =
+        if spec ? ref then spec.ref else
+        if spec ? branch then "refs/heads/${spec.branch}" else
+        if spec ? tag then "refs/tags/${spec.tag}" else
+        abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
+      submodules = if spec ? submodules then spec.submodules else false;
+      submoduleArg =
+        let
+          nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0;
+          emptyArgWithWarning =
+            if submodules == true
+            then
+              builtins.trace
+                (
+                  "The niv input \"${name}\" uses submodules "
+                  + "but your nix's (${builtins.nixVersion}) builtins.fetchGit "
+                  + "does not support them"
+                )
+                { }
+            else { };
+        in
+        if nixSupportsSubmodules
+        then { inherit submodules; }
+        else emptyArgWithWarning;
+    in
+    builtins.fetchGit
+      ({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg);
+
+  fetch_local = spec: spec.path;
+
+  fetch_builtin-tarball = name: throw
+    ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
+        $ niv modify ${name} -a type=tarball -a builtin=true'';
+
+  fetch_builtin-url = name: throw
+    ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
+        $ niv modify ${name} -a type=file -a builtin=true'';
+
+  #
+  # Various helpers
+  #
+
+  # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
+  sanitizeName = name:
+    (
+      concatMapStrings (s: if builtins.isList s then "-" else s)
+        (
+          builtins.split "[^[:alnum:]+._?=-]+"
+            ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
+        )
+    );
+
+  # The set of packages used when specs are fetched using non-builtins.
+  mkPkgs = sources: system:
+    let
+      sourcesNixpkgs =
+        import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
+      hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
+      hasThisAsNixpkgsPath = <nixpkgs> == ./.;
+    in
+    if builtins.hasAttr "nixpkgs" sources
+    then sourcesNixpkgs
+    else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
+      import <nixpkgs> { }
+    else
+      abort
+        ''
+          Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
+          add a package called "nixpkgs" to your sources.json.
+        '';
+
+  # The actual fetching function.
+  fetch = pkgs: name: spec:
+
+    if ! builtins.hasAttr "type" spec then
+      abort "ERROR: niv spec ${name} does not have a 'type' attribute"
+    else if spec.type == "file" then fetch_file pkgs name spec
+    else if spec.type == "tarball" then fetch_tarball pkgs name spec
+    else if spec.type == "git" then fetch_git name spec
+    else if spec.type == "local" then fetch_local spec
+    else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
+    else if spec.type == "builtin-url" then fetch_builtin-url name
+    else
+      abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
+
+  # If the environment variable NIV_OVERRIDE_${name} is set, then use
+  # the path directly as opposed to the fetched source.
+  replace = name: drv:
+    let
+      saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
+      ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
+    in
+    if ersatz == "" then drv else
+      # this turns the string into an actual Nix path (for both absolute and
+      # relative paths)
+    if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
+
+  # Ports of functions for older nix versions
+
+  # a Nix version of mapAttrs if the built-in doesn't exist
+  mapAttrs = builtins.mapAttrs or (
+    f: set: with builtins;
+    listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
+  );
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
+  range = first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1);
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
+  stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
+  stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
+  concatMapStrings = f: list: concatStrings (map f list);
+  concatStrings = builtins.concatStringsSep "";
+
+  # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
+  optionalAttrs = cond: as: if cond then as else { };
+
+  # fetchTarball version that is compatible between all the versions of Nix
+  builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
+    let
+      inherit (builtins) lessThan nixVersion fetchTarball;
+    in
+    if lessThan nixVersion "1.12" then
+      fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
+    else
+      fetchTarball attrs;
+
+  # fetchurl version that is compatible between all the versions of Nix
+  builtins_fetchurl = { url, name ? null, sha256 }@attrs:
+    let
+      inherit (builtins) lessThan nixVersion fetchurl;
+    in
+    if lessThan nixVersion "1.12" then
+      fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
+    else
+      fetchurl attrs;
+
+  # Create the final "sources" from the config
+  mkSources = config:
+    mapAttrs
+      (
+        name: spec:
+          if builtins.hasAttr "outPath" spec
+          then
+            abort
+              "The values in sources.json should not have an 'outPath' attribute"
+          else
+            spec // { outPath = replace name (fetch config.pkgs name spec); }
+      )
+      config.sources;
+
+  # The "config" used by the fetchers
+  mkConfig =
+    { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
+    , sources ? if isNull sourcesFile then { } else builtins.fromJSON (builtins.readFile sourcesFile)
+    , system ? builtins.currentSystem
+    , pkgs ? mkPkgs sources system
+    }: rec {
+      # The sources, i.e. the attribute set of spec name to spec
+      inherit sources;
+
+      # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
+      inherit pkgs;
+    };
+
+in
+mkSources (mkConfig { }) // { __functor = _: settings: mkSources (mkConfig settings); }
diff --git a/users/sterni/htmlman/default.nix b/users/sterni/htmlman/default.nix
index b88bc26410..6bf21ce2db 100644
--- a/users/sterni/htmlman/default.nix
+++ b/users/sterni/htmlman/default.nix
@@ -19,9 +19,9 @@ let
     ;
 
   bins = getBins cheddar [ "cheddar" ]
-      // getBins mandoc [ "mandoc" ]
-      // getBins coreutils [ "cat" "mv" "mkdir" ]
-      ;
+    // getBins mandoc [ "mandoc" ]
+    // getBins coreutils [ "cat" "mv" "mkdir" ]
+  ;
 
   normalizeDrv = fetchurl {
     url = "https://necolas.github.io/normalize.css/8.0.1/normalize.css";
@@ -29,7 +29,10 @@ let
   };
 
   execlineStdoutInto = target: line: [
-    "redirfd" "-w" "1" target
+    "redirfd"
+    "-w"
+    "1"
+    target
   ] ++ line;
 
   # I will not write a pure nix markdown renderer
@@ -39,16 +42,24 @@ let
   # I will not write a pure nix markdown renderer
   markdown = md:
     let
-      html = runExecline.local "rendered-markdown" {
-        stdin = md;
-      } ([
-        "importas" "-iu" "out" "out"
-      ] ++ execlineStdoutInto "$out" [
-        bins.cheddar "--about-filter" "description.md"
-      ]);
-    in builtins.readFile html;
-
-  indexTemplate = { title, description, pages ? [] }: ''
+      html = runExecline.local "rendered-markdown"
+        {
+          stdin = md;
+        }
+        ([
+          "importas"
+          "-iu"
+          "out"
+          "out"
+        ] ++ execlineStdoutInto "$out" [
+          bins.cheddar
+          "--about-filter"
+          "description.md"
+        ]);
+    in
+    builtins.readFile html;
+
+  indexTemplate = { title, description, pages ? [ ] }: ''
     <!doctype html>
     <html>
       <head>
@@ -137,40 +148,40 @@ let
 
   htmlman =
     { title
-    # title of the index page
+      # title of the index page
     , description ? ""
-    # description which is displayed after
-    # the main heading on the index page
-    , pages ? []
-    # man pages of the following structure:
-    # {
-    #   name : string;
-    #   section : int;
-    #   path : either path string;
-    # }
-    # path is optional, if it is not given,
-    # the man page source must be located at
-    # "${manDir}/${name}.${toString section}"
+      # description which is displayed after
+      # the main heading on the index page
+    , pages ? [ ]
+      # man pages of the following structure:
+      # {
+      #   name : string;
+      #   section : int;
+      #   path : either path string;
+      # }
+      # path is optional, if it is not given,
+      # the man page source must be located at
+      # "${manDir}/${name}.${toString section}"
     , manDir ? null
-    # directory in which man page sources are located
+      # directory in which man page sources are located
     , style ? defaultStyle
-    # CSS to use as a string
+      # CSS to use as a string
     , normalizeCss ? true
-    # whether to include normalize.css before the custom CSS
+      # whether to include normalize.css before the custom CSS
     , linkXr ? "all"
-    # How to handle cross references in the html output:
-    #
-    # * none:     don't convert cross references into hyperlinks
-    # * all:      link all cross references as if they were
-    #             rendered into $out by htmlman
-    # * inManDir: link to all man pages which have their source
-    #             in `manDir` and use the format string defined
-    #             in linkXrFallback for all other cross references.
+      # How to handle cross references in the html output:
+      #
+      # * none:     don't convert cross references into hyperlinks
+      # * all:      link all cross references as if they were
+      #             rendered into $out by htmlman
+      # * inManDir: link to all man pages which have their source
+      #             in `manDir` and use the format string defined
+      #             in linkXrFallback for all other cross references.
     , linkXrFallback ? "https://manpages.debian.org/unstable/%N.%S.en.html"
-    # fallback link to use if linkXr == "inManDir" and the man
-    # page is not in ${manDir}. Placeholders %N (name of page)
-    # and %S (section of page) can be used. See mandoc(1) for
-    # more information.
+      # fallback link to use if linkXr == "inManDir" and the man
+      # page is not in ${manDir}. Placeholders %N (name of page)
+      # and %S (section of page) can be used. See mandoc(1) for
+      # more information.
     }:
 
     let
@@ -188,47 +199,70 @@ let
       mandocOpts = lib.concatStringsSep "," ([
         "style=style.css"
       ] ++ linkXrEnum.match linkXr {
-        all      = [ "man=./%N.%S.html" ];
+        all = [ "man=./%N.%S.html" ];
         inManDir = [ "man=./%N.%S.html;${linkXrFallback}" ];
-        none     = [ ];
+        none = [ ];
       });
 
       html =
-        runExecline.local "htmlman-${title}" {
-          derivationArgs = {
-            inherit index style;
-            passAsFile = [ "index" "style" ];
-          };
-        } ([
-          "multisubstitute" [
-            "importas" "-iu" "out" "out"
-            "importas" "-iu" "index" "indexPath"
-            "importas" "-iu" "style" "stylePath"
-          ]
-          "if" [ bins.mkdir "-p" "$out" ]
-          "if" [ bins.mv "$index" "\${out}/index.html" ]
-          "if" (execlineStdoutInto "\${out}/style.css" [
-            "if" ([
-              bins.cat
-            ] ++ lib.optional normalizeCss normalizeDrv
+        runExecline.local "htmlman-${title}"
+          {
+            derivationArgs = {
+              inherit index style;
+              passAsFile = [ "index" "style" ];
+            };
+          }
+          ([
+            "multisubstitute"
+            [
+              "importas"
+              "-iu"
+              "out"
+              "out"
+              "importas"
+              "-iu"
+              "index"
+              "indexPath"
+              "importas"
+              "-iu"
+              "style"
+              "stylePath"
+            ]
+            "if"
+            [ bins.mkdir "-p" "$out" ]
+            "if"
+            [ bins.mv "$index" "\${out}/index.html" ]
+            "if"
+            (execlineStdoutInto "\${out}/style.css" [
+              "if"
+              ([
+                bins.cat
+              ] ++ lib.optional normalizeCss normalizeDrv
               ++ [
-              "$style"
+                "$style"
+              ])
             ])
-          ])
-          # let mandoc check for available man pages
-          "execline-cd" "${manDir}"
-        ] ++ lib.concatMap ({ name, section, ... }@p:
-          execlineStdoutInto "\${out}/${name}.${toString section}.html" [
-          "if" [
-            bins.mandoc
-            "-mdoc"
-            "-T" "html"
-            "-O" mandocOpts
-            (resolvePath p)
-          ]
-        ]) pages);
-    in html // {
+            # let mandoc check for available man pages
+            "execline-cd"
+            "${manDir}"
+          ] ++ lib.concatMap
+            ({ name, section, ... }@p:
+              execlineStdoutInto "\${out}/${name}.${toString section}.html" [
+                "if"
+                [
+                  bins.mandoc
+                  "-mdoc"
+                  "-T"
+                  "html"
+                  "-O"
+                  mandocOpts
+                  (resolvePath p)
+                ]
+              ])
+            pages);
+    in
+    html // {
       deploy = deployScript title html;
     };
 in
-  htmlman
+htmlman
diff --git a/users/sterni/keys.nix b/users/sterni/keys.nix
index 815f62ee08..0a422bc0d1 100644
--- a/users/sterni/keys.nix
+++ b/users/sterni/keys.nix
@@ -3,5 +3,6 @@
 {
   all = [
     "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJk+KvgvI2oJTppMASNUfMcMkA2G5ZNt+HnWDzaXKLlo lukas@wolfgang"
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIZTrefeqOlXDz7nnDWw820+29vLgn6R3o4N1G3lRWrr lukas@ludwig"
   ];
 }
diff --git a/users/sterni/lv/gopher/default.nix b/users/sterni/lv/gopher/default.nix
new file mode 100644
index 0000000000..f8f42c82d5
--- /dev/null
+++ b/users/sterni/lv/gopher/default.nix
@@ -0,0 +1,8 @@
+{ depot, ... }:
+
+depot.users.sterni.nix.build.buildGopherHole {
+  name = "gopher-sterni.lv";
+  dir = [
+    "🚧 closed for construction 🚧"
+  ];
+}
diff --git a/users/sterni/machines/.skip-subtree b/users/sterni/machines/.skip-subtree
new file mode 100644
index 0000000000..a79762853e
--- /dev/null
+++ b/users/sterni/machines/.skip-subtree
@@ -0,0 +1 @@
+Subdirectories are manually reexposed by default.nix as the contain NixOS modules
diff --git a/users/sterni/machines/default.nix b/users/sterni/machines/default.nix
new file mode 100644
index 0000000000..291d9756c7
--- /dev/null
+++ b/users/sterni/machines/default.nix
@@ -0,0 +1,81 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.nq [ "fq" "nq" ];
+
+  machines = lib.mapAttrs
+    (name: _:
+      depot.ops.nixos.nixosFor (import (./. + ("/" + name)))
+    )
+    (lib.filterAttrs (_: type: type == "directory") (builtins.readDir ./.));
+
+  # TODO(sterni): share code with rebuild-system
+  localDeployScriptFor = { system, config, ... }:
+    pkgs.writeShellScript "local-deploy-${system.name}" ''
+      set -eu
+      if [[ "$(hostname)" != "${config.networking.hostName}" ]]; then
+        echo "$0: unexpected hostname: $(hostname). Are you deploying on the right machine?"
+        exit 1
+      fi
+      nix-env -p /nix/var/nix/profiles/system --set "${system}"
+      "${system}/bin/switch-to-configuration" switch
+    '';
+
+  # Builds the system on the remote machine
+  deployScriptFor = { system, ... }@machine:
+    pkgs.writeShellScript "remote-deploy-${system.name}" ''
+      set -eu
+
+      if [ $# != 1 ]; then
+        printf 'usage: %s [USER@]HOST' "$0"
+        exit 100
+      fi
+
+      readonly TARGET_HOST="$1"
+      readonly DEPLOY_DRV="${
+        builtins.unsafeDiscardOutputDependency (
+          # Wrapper script around localDeployScriptFor that merely starts the
+          # local deploy script using and nq and then waits using fq. This means
+          # we can't Ctrl-C the deploy and it won't be terminated by a lost
+          # connection.
+          pkgs.writeShellScript "queue-deploy-${system.name}" ''
+            readonly STATE_DIR="''${XDG_STATE_HOME:-$HOME/.local/state}/sterni-deploy"
+            mkdir -p "$STATE_DIR"
+
+            export NQDIR="$STATE_DIR"
+
+            "${bins.nq}" "${localDeployScriptFor machine}"
+            "${bins.fq}"
+          ''
+        ).drvPath
+      }"
+
+      nix-copy-closure -s --gzip --to "$TARGET_HOST" "$DEPLOY_DRV"
+
+      readonly DEPLOY_OUT="$(ssh "$TARGET_HOST" "nix-store -r '$DEPLOY_DRV'")"
+
+      ssh "$TARGET_HOST" "$DEPLOY_OUT"
+    '';
+
+in
+
+depot.nix.readTree.drvTargets (
+  # this somehow becomes necessarily ugly with nixpkgs-fmt
+  machines // { inherit deployScriptFor; } //
+
+  lib.mapAttrs'
+    (name: _: {
+      name = "${name}System";
+      value = machines.${name}.system;
+    })
+    machines
+
+    //
+
+  lib.mapAttrs'
+    (name: _: {
+      name = "${name}Deploy";
+      value = deployScriptFor machines.${name};
+    })
+    machines
+)
diff --git a/users/sterni/machines/edwin/default.nix b/users/sterni/machines/edwin/default.nix
new file mode 100644
index 0000000000..68f20787a9
--- /dev/null
+++ b/users/sterni/machines/edwin/default.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, depot, ... }:
+
+{
+  imports = [
+    # Third party modules we use
+    "${depot.third_party.agenix.src}/modules/age.nix"
+    # Basic settings
+    ../../modules/common.nix
+    # These modules touch things related to booting (filesystems, initrd network…)
+    ./hardware.nix
+    ./network.nix
+    # These modules configure services, websites etc.
+    (depot.path.origSrc + "/ops/modules/btrfs-auto-scrub.nix")
+  ];
+
+  config = {
+    system.stateVersion = "20.09";
+  };
+}
diff --git a/users/sterni/machines/edwin/hardware.nix b/users/sterni/machines/edwin/hardware.nix
new file mode 100644
index 0000000000..0e33de753a
--- /dev/null
+++ b/users/sterni/machines/edwin/hardware.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, depot, ... }:
+
+{
+  config = {
+    boot = {
+      loader.grub = {
+        enable = true;
+        # TODO(sterni): use /dev/disk/by-id ?
+        devices = [
+          "/dev/sda"
+          "/dev/sdb"
+        ];
+      };
+
+      kernelModules = [
+        "kvm-intel"
+      ];
+
+      initrd.availableKernelModules = [
+        "ahci"
+        "sd_mod"
+        "btrfs"
+        "realtek"
+        "r8169"
+      ];
+    };
+
+    boot.initrd.luks.devices = {
+      "crypt1".device = "/dev/disk/by-uuid/02ac34ee-be10-401b-90c2-1c6aa54c4d5f";
+      "crypt2".device = "/dev/disk/by-uuid/7ce07191-e704-4aed-a60f-dfa3ce386b26";
+      "crypt-swap1".device = "/dev/disk/by-uuid/fec7155c-6a65-4f25-b271-43763e4c31eb";
+      "crypt-swap2".device = "/dev/disk/by-uuid/7b0a03fc-51de-4578-9811-94b00df09d88";
+    };
+
+    fileSystems = {
+      "/" = {
+        device = "/dev/disk/by-label/root";
+        fsType = "btrfs";
+      };
+
+      "/boot" = {
+        device = "/dev/disk/by-label/boot";
+        fsType = "btrfs";
+      };
+    };
+
+    swapDevices = [
+      { device = "/dev/disk/by-label/swap1"; }
+      { device = "/dev/disk/by-label/swap2"; }
+    ];
+
+    powerManagement.cpuFreqGovernor = "performance";
+    hardware = {
+      enableRedistributableFirmware = true;
+      cpu.intel.updateMicrocode = true;
+    };
+
+    nix.settings = {
+      max-jobs = 2;
+      cores = 4;
+    };
+  };
+}
diff --git a/users/sterni/machines/edwin/network.nix b/users/sterni/machines/edwin/network.nix
new file mode 100644
index 0000000000..1e3d4e76f0
--- /dev/null
+++ b/users/sterni/machines/edwin/network.nix
@@ -0,0 +1,62 @@
+{ config, pkgs, lib, depot, ... }:
+
+let
+  ipv6 = "2a01:4f8:151:54d0::/64";
+
+  ipv4 = "176.9.107.207";
+  gatewayv4 = "176.9.107.193";
+  netmaskv4 = "255.255.255.224";
+in
+
+{
+  config = {
+    boot = {
+      kernelParams = [
+        "ip=${ipv4}::${gatewayv4}:${netmaskv4}::eth0:none"
+      ];
+
+      initrd.network = {
+        enable = true;
+        ssh = {
+          enable = true;
+          authorizedKeys = depot.users.sterni.keys.all;
+          hostKeys = [
+            "/etc/nixos/unlock_rsa_key_openssh"
+            "/etc/nixos/unlock_ed25519_key_openssh"
+          ];
+        };
+        postCommands = ''
+          echo 'cryptsetup-askpass' >> /root/.profile
+        '';
+      };
+    };
+
+    networking = {
+      usePredictableInterfaceNames = false;
+      useDHCP = false;
+      interfaces."eth0".useDHCP = false;
+
+      hostName = "edwin";
+
+      firewall = {
+        enable = true;
+        allowPing = true;
+        allowedTCPPorts = [ 22 80 443 ];
+      };
+    };
+
+    systemd.network = {
+      enable = true;
+      networks."eth0".extraConfig = ''
+        [Match]
+        Name = eth0
+
+        [Network]
+        Address = ${ipv6}
+        Gateway = fe80::1
+        Address = ${ipv4}/27
+        Gateway = ${gatewayv4}
+      '';
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/default.nix b/users/sterni/machines/ingeborg/default.nix
new file mode 100644
index 0000000000..2d026ae05b
--- /dev/null
+++ b/users/sterni/machines/ingeborg/default.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, depot, ... }:
+
+{
+  imports = [
+    # Third party modules
+    "${depot.third_party.agenix.src}/modules/age.nix"
+    # Basic settings
+    ../../modules/common.nix
+    # These modules touch things related to booting (filesystems, initrd network…)
+    ./hardware.nix
+    ./network.nix
+    # (More or less) pluggable service configuration
+    (depot.path.origSrc + "/ops/modules/btrfs-auto-scrub.nix")
+    ./monitoring.nix
+    ./minecraft.nix
+    ./http/sterni.lv.nix
+    ./http/code.sterni.lv.nix
+    ./http/flipdot.openlab-augsburg.de.nix
+    ./tv.nix
+    ./quassel.nix
+
+    # Inactive:
+    # ./http/likely-music.sterni.lv.nix
+    # ./gopher.nix
+
+    # TODO(sterni): fail2ban
+    # TODO(sterni): automatic backups for full recovery
+  ];
+
+  config = {
+    system.stateVersion = "24.05";
+  };
+}
diff --git a/users/sterni/machines/ingeborg/gopher.nix b/users/sterni/machines/ingeborg/gopher.nix
new file mode 100644
index 0000000000..57275e13a5
--- /dev/null
+++ b/users/sterni/machines/ingeborg/gopher.nix
@@ -0,0 +1,19 @@
+{ depot, ... }:
+
+{
+  config = {
+    services.spacecookie = {
+      enable = true;
+      openFirewall = true;
+      settings = {
+        hostname = "sterni.lv";
+        root = depot.users.sterni.lv.gopher;
+        log = {
+          enable = true;
+          hide-ips = true;
+          hide-time = true;
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/hardware.nix b/users/sterni/machines/ingeborg/hardware.nix
new file mode 100644
index 0000000000..982598131e
--- /dev/null
+++ b/users/sterni/machines/ingeborg/hardware.nix
@@ -0,0 +1,76 @@
+{ config, lib, pkgs, depot, ... }:
+
+{
+  # Booting / Kernel
+  boot = {
+    loader.grub = {
+      enable = true;
+      devices = [
+        "/dev/disk/by-id/wwn-0x5000c500a4859731"
+        "/dev/disk/by-id/wwn-0x5000c500a485c1b5"
+      ];
+    };
+
+    initrd = {
+      availableKernelModules = [
+        "ahci"
+        "btrfs"
+        "sd_mod"
+        "xhci_pci"
+        "e1000e"
+      ];
+      kernelModules = [
+        "dm-snapshot"
+      ];
+    };
+
+    swraid = {
+      enable = true;
+      mdadmConf = ''
+        ARRAY /dev/md/boot-raid metadata=1.2 name=nixos:boot-raid UUID=13007b9d:ab7a1129:c45ec40f:3c9f2111
+        ARRAY /dev/md/encrypted-container-raid metadata=1.2 name=nixos:encrypted-container-raid UUID=38dfa683:a6d30690:32a5de6f:fb7980fe
+      '';
+    };
+
+    kernelModules = [
+      "kvm-intel"
+    ];
+  };
+
+  # Filesystems
+  services.lvm.enable = true;
+
+  boot.initrd.luks.devices."container" = {
+    device = "/dev/md/encrypted-container-raid";
+    preLVM = true;
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/mainvg/root";
+      fsType = "btrfs";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-label/boot";
+      fsType = "ext4";
+    };
+  };
+
+  swapDevices = [
+    { device = "/dev/mainvg/swap"; }
+  ];
+
+  # CPU
+  hardware = {
+    cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+    enableRedistributableFirmware = true;
+  };
+
+  nix.settings = {
+    max-jobs = 2;
+    cores = 4;
+  };
+
+  powerManagement.cpuFreqGovernor = "performance";
+}
diff --git a/users/sterni/machines/ingeborg/http/code.sterni.lv.nix b/users/sterni/machines/ingeborg/http/code.sterni.lv.nix
new file mode 100644
index 0000000000..fd4975ed1d
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/code.sterni.lv.nix
@@ -0,0 +1,263 @@
+{ depot, pkgs, lib, config, ... }:
+
+let
+  virtualHost = "code.sterni.lv";
+
+  repoSections = [
+    {
+      section = "active";
+      repos = {
+        spacecookie = {
+          description = "gopher server (and library for Haskell)";
+          upstream = "https://github.com/sternenseemann/spacecookie.git";
+        };
+        "mirror/depot" = {
+          description = "monorepo for the virus lounge";
+          upstream = "https://code.tvl.fyi/depot.git";
+          cgit.defbranch = "canon";
+        };
+        "mirror/flipdot-gschichtler" = {
+          description = "message queue system for OpenLab's flipdot display";
+          upstream = "https://github.com/openlab-aux/flipdot-gschichtler.git";
+        };
+        "mirror/nixpkgs" = {
+          description = "Nix packages collection";
+          upstream = "https://github.com/nixos/nixpkgs.git";
+          cgit.enable-commit-graph = "0"; # too slow
+        };
+        "mirror/vuizvui" = {
+          description = "Nix(OS) expressions used by the OpenLab and its members";
+          upstream = "https://github.com/openlab-aux/vuizvui.git";
+        };
+      };
+    }
+    {
+      section = "poc";
+      repos = {
+        emoji-generic = {
+          description = "generic emoji library for Haskell";
+          upstream = "https://github.com/sternenseemann/emoji-generic.git";
+        };
+        grav2ty = {
+          description = "β€œrealistic” 2d space game";
+          upstream = "https://github.com/sternenseemann/grav2ty.git";
+        };
+        haskell-dot-time = {
+          description = "UTC-centric time library for haskell with dot time support";
+          cgit.defbranch = "main";
+        };
+        buchstabensuppe = {
+          description = "toy font rendering for low pixelcount, high contrast displays";
+          upstream = "https://github.com/sternenseemann/buchstabensuppe.git";
+          cgit.defbranch = "main";
+        };
+        "mirror/saneterm" = {
+          description = "modern line-oriented terminal emulator without support for TUIs";
+          upstream = "https://git.8pit.net/saneterm.git";
+        };
+      };
+    }
+    {
+      # TODO(sterni): resisort, klammeraffe, cl-ca, ponify, tinyrl
+      section = "archive";
+      repos = {
+        gopher-proxy = {
+          description = "Gopher over HTTP proxy";
+          upstream = "https://github.com/sternenseemann/gopher-proxy.git";
+        };
+        likely-music = {
+          description = "experimental application for probabilistic music composition";
+          upstream = "https://github.com/sternenseemann/likely-music.git";
+        };
+        logbook = {
+          description = "file format for keeping a personal log";
+          upstream = "https://github.com/sternenseemann/logbook.git";
+        };
+        sternenblog = {
+          description = "file based cgi blog software";
+          upstream = "https://github.com/sternenseemann/sternenblog.git";
+        };
+      };
+    }
+  ];
+
+  repoPath = name: repo: repo.path or "/srv/git/${name}.git";
+
+  cgitRepoEntry = name: repo:
+    lib.concatStringsSep "\n" (
+      [
+        "repo.url=${name}"
+        "repo.path=${repoPath name repo}"
+      ]
+      ++ lib.optional (repo ? description) "repo.desc=${repo.description}"
+      ++ lib.mapAttrsToList (n: v: "repo.${n}=${v}") repo.cgit or { }
+    );
+
+  cgitHead = pkgs.writeText "cgit-head.html" ''
+    <style>
+    #summary {
+      max-width: 80em;
+    }
+
+    #summary * {
+      max-width: 100%;
+    }
+    </style>
+  '';
+
+  cgitConfig = pkgs.writeText "cgitrc" ''
+    virtual-root=/
+
+    enable-http-clone=1
+    clone-url=https://${virtualHost}/$CGIT_REPO_URL
+
+    enable-blame=1
+    enable-log-filecount=1
+    enable-log-linecount=1
+    enable-index-owner=0
+    enable-blame=1
+    enable-commit-graph=1
+
+    root-title=code.sterni.lv
+    css=/cgit.css
+    head-include=${cgitHead}
+
+    mimetype-file=${pkgs.mime-types}/etc/mime.types
+
+    about-filter=${depot.tools.cheddar.about-filter}/bin/cheddar-about
+    source-filter=${depot.tools.cheddar}/bin/cheddar
+    readme=:README.md
+    readme=:readme.md
+
+    section-sort=0
+    ${
+      lib.concatMapStringsSep "\n" (section:
+        ''
+          section=${section.section}
+
+        ''
+        + builtins.concatStringsSep "\n\n" (lib.mapAttrsToList cgitRepoEntry section.repos)
+      ) repoSections
+    }
+  '';
+
+  /* Merge a list of attrs, but fail when the same attribute occurs twice.
+
+     Type: [ attrs ] -> attrs
+  */
+  mergeManyDistinctAttrs = lib.foldAttrs
+    (
+      val: nul:
+        if nul == null then val else throw "Every attribute name may occur only once"
+    )
+    null;
+
+  flatRepos = mergeManyDistinctAttrs
+    (builtins.map (section: section.repos) repoSections);
+
+  reposToMirror = lib.filterAttrs (_: repo: repo ? upstream) flatRepos;
+
+  # User and group name used for running the mirror scripts
+  mirroredReposOwner = "git";
+
+  # Make repo name suitable for systemd unit/timer
+  unitName = name: "mirror-${lib.strings.sanitizeDerivationName name}";
+in
+
+{
+  imports = [
+    ./nginx.nix
+    ./fcgiwrap.nix
+  ];
+
+  config = {
+    services.nginx.virtualHosts."${virtualHost}" = {
+      enableACME = true;
+      forceSSL = true;
+      root = "${pkgs.cgit-pink}/cgit/";
+      extraConfig = ''
+        try_files $uri @cgit;
+
+        location @cgit {
+          include ${pkgs.nginx}/conf/fastcgi_params;
+          fastcgi_param    SCRIPT_FILENAME ${pkgs.cgit-pink}/cgit/cgit.cgi;
+          fastcgi_param    PATH_INFO       $uri;
+          fastcgi_param    QUERY_STRING    $args;
+          fastcgi_param    HTTP_HOST       $server_name;
+          fastcgi_param    CGIT_CONFIG     ${cgitConfig};
+          fastcgi_pass     unix:${toString config.services.fcgiwrap.socketAddress};
+        }
+      '';
+    };
+
+    users = {
+      users.${mirroredReposOwner} = {
+        group = mirroredReposOwner;
+        isSystemUser = true;
+      };
+
+      groups.${mirroredReposOwner} = { };
+    };
+
+
+    systemd.timers = lib.mapAttrs'
+      (
+        name: repo:
+          {
+            name = unitName name;
+            value = {
+              description = "regularly update mirror git repository ${name}";
+              wantedBy = [ "timers.target" ];
+              enable = true;
+              timerConfig = {
+                # Fire every 6h and distribute the workload over next 6h randomly
+                OnCalendar = "*-*-* 00/6:00:00";
+                RandomizedDelaySec = "6h";
+                Persistent = true;
+              };
+            };
+          }
+      )
+      reposToMirror;
+
+    systemd.services = lib.mapAttrs'
+      (
+        name: repo:
+          {
+            name = unitName name;
+            value = {
+              description = "mirror git repository ${name}";
+              requires = [ "network-online.target" ];
+              after = [ "network-online.target" ];
+
+              script =
+                let
+                  path = repoPath name repo;
+                in
+                ''
+                  set -euo pipefail
+
+                  export PATH="${lib.makeBinPath [ pkgs.coreutils pkgs.git ]}"
+
+                  if test ! -d "${path}"; then
+                    mkdir -p "$(dirname "${path}")"
+                    git clone --mirror "${repo.upstream}" "${path}"
+                    exit 0
+                  fi
+
+                  cd "${path}"
+
+                  git fetch "${repo.upstream}" '+refs/*:refs/*' --prune
+                '';
+
+              serviceConfig = {
+                Type = "oneshot";
+                User = mirroredReposOwner;
+                Group = mirroredReposOwner;
+              };
+            };
+          }
+      )
+      reposToMirror;
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/fcgiwrap.nix b/users/sterni/machines/ingeborg/http/fcgiwrap.nix
new file mode 100644
index 0000000000..19696d85d4
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/fcgiwrap.nix
@@ -0,0 +1,15 @@
+{ ... }:
+
+{
+  imports = [
+    ./nginx.nix
+  ];
+
+  config.services.fcgiwrap = {
+    enable = true;
+    socketType = "unix";
+    socketAddress = "/run/fcgiwrap.sock";
+    user = "http";
+    group = "http";
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/flipdot.openlab-augsburg.de.nix b/users/sterni/machines/ingeborg/http/flipdot.openlab-augsburg.de.nix
new file mode 100644
index 0000000000..c86956a0a4
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/flipdot.openlab-augsburg.de.nix
@@ -0,0 +1,36 @@
+{ depot, lib, config, ... }:
+
+let
+  inherit (depot.users.sterni.external.flipdot-gschichtler)
+    bahnhofshalle
+    warteraum
+    nixosModule
+    ;
+in
+
+{
+  imports = [
+    nixosModule
+    ./nginx.nix
+  ];
+
+  config = {
+    age.secrets = lib.genAttrs [
+      "warteraum-salt"
+      "warteraum-tokens"
+    ]
+      (name: {
+        file = depot.users.sterni.secrets."${name}.age";
+      });
+
+    services.flipdot-gschichtler = {
+      enable = true;
+      virtualHost = "flipdot.openlab-augsburg.de";
+      packages = {
+        inherit bahnhofshalle warteraum;
+      };
+      saltFile = config.age.secretsDir + "/warteraum-salt";
+      tokensFile = config.age.secretsDir + "/warteraum-tokens";
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/likely-music.sterni.lv.nix b/users/sterni/machines/ingeborg/http/likely-music.sterni.lv.nix
new file mode 100644
index 0000000000..8da03ac5e6
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/likely-music.sterni.lv.nix
@@ -0,0 +1,23 @@
+{ depot, ... }:
+
+let
+  inherit (depot.users.sterni.external.likely-music)
+    nixosModule
+    likely-music
+    ;
+in
+
+{
+  imports = [
+    ./nginx.nix
+    nixosModule
+  ];
+
+  config = {
+    services.likely-music = {
+      enable = true;
+      virtualHost = "likely-music.sterni.lv";
+      package = likely-music;
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/nginx.nix b/users/sterni/machines/ingeborg/http/nginx.nix
new file mode 100644
index 0000000000..d551b8391d
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/nginx.nix
@@ -0,0 +1,30 @@
+{ ... }:
+
+{
+  config = {
+    users = {
+      users.http = {
+        isSystemUser = true;
+        group = "http";
+      };
+
+      groups.http = { };
+    };
+
+    services.nginx = {
+      enable = true;
+      recommendedTlsSettings = true;
+      recommendedGzipSettings = true;
+      recommendedProxySettings = true;
+
+      user = "http";
+      group = "http";
+
+      appendHttpConfig = ''
+        charset utf-8;
+      '';
+    };
+
+    networking.firewall.allowedTCPPorts = [ 80 443 ];
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/sterni.lv.nix b/users/sterni/machines/ingeborg/http/sterni.lv.nix
new file mode 100644
index 0000000000..50c1bac293
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/sterni.lv.nix
@@ -0,0 +1,34 @@
+{ pkgs, depot, ... }:
+
+let
+  inherit (depot.users.sterni.nix.html)
+    __findFile
+    withDoctype
+    ;
+in
+
+{
+  imports = [
+    ./nginx.nix
+  ];
+
+  config = {
+    services.nginx.virtualHosts."sterni.lv" = {
+      enableACME = true;
+      forceSSL = true;
+      root = pkgs.writeTextFile {
+        name = "sterni.lv-http-root";
+        destination = "/index.html";
+        text = withDoctype (<html> { } [
+          (<head> { } [
+            (<meta> { charset = "utf-8"; } null)
+            (<title> { } "no thoughts")
+          ])
+          (<body> { } "🦩")
+        ]);
+      };
+      # TODO(sterni): tmp.sterni.lv
+      locations."/tmp/".root = toString /srv/http;
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/irccat.nix b/users/sterni/machines/ingeborg/irccat.nix
new file mode 100644
index 0000000000..0c40f15e33
--- /dev/null
+++ b/users/sterni/machines/ingeborg/irccat.nix
@@ -0,0 +1,23 @@
+{ depot, config, pkgs, lib, ... }:
+
+{
+  imports = [
+    (depot.path.origSrc + "/ops/modules/irccat.nix")
+  ];
+
+  config = {
+    services.depot.irccat = {
+      enable = true;
+      secretsFile = builtins.toFile "empty.json" "{}"; # TODO(sterni): register
+      config = {
+        tcp.listen = ":4722"; # ircc
+        irc = {
+          server = "irc.hackint.org:6697";
+          tls = true;
+          nick = config.networking.hostName;
+          realname = "irccat";
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/minecraft.nix b/users/sterni/machines/ingeborg/minecraft.nix
new file mode 100644
index 0000000000..383fee8ca0
--- /dev/null
+++ b/users/sterni/machines/ingeborg/minecraft.nix
@@ -0,0 +1,125 @@
+{ pkgs, depot, config, ... }:
+
+let
+  carpet = pkgs.fetchurl {
+    url = "https://github.com/gnembon/fabric-carpet/releases/download/1.4.128/fabric-carpet-1.20.3-1.4.128+v231205.jar";
+    sha256 = "1jh2pb9pjwyfv1ianzykmja21nqlv175a8rg926xg3w4hhhwzrfq";
+  };
+
+  carpet-extra = pkgs.fetchurl {
+    url = "https://github.com/gnembon/carpet-extra/releases/download/1.4.128/carpet-extra-1.20.3-1.4.128.jar";
+    sha256 = "0gxwm5ayr0y5dri0kxlnrrgy9pyaim34rl6km1j42fkyvc4r8p6x";
+  };
+
+  userGroup = "minecraft";
+
+  makeJvmOpts = megs: [
+    "-Xms${toString megs}M"
+    "-Xmx${toString megs}M"
+  ];
+
+  whitelist = {
+    spreadwasser = "242a66eb-2df2-4585-9a28-ac763ad0d0f9";
+    sternenseemann = "d8e48069-1905-4886-a5da-a4ee917ee254";
+  };
+
+  rconPasswordFile = config.age.secretsDir + "/minecraft-rcon";
+
+  baseProperties = {
+    white-list = true;
+    allow-flight = true;
+    difficulty = "hard";
+    function-permission-level = 4;
+    snooper-enabled = false;
+    view-distance = 12;
+    sync-chunk-writes = "false"; # the single biggest performance fix
+    max-tick-time = 6000000; # TODO(sterni): disable watchdog via carpet
+    enforce-secure-profile = false;
+  };
+in
+
+{
+  imports = [
+    ../../modules/minecraft-fabric.nix
+    ../../modules/backup-minecraft-fabric.nix
+  ];
+
+  config = {
+    environment.systemPackages = [
+      pkgs.mcrcon
+      pkgs.jre
+    ];
+
+    users = {
+      users."${userGroup}" = {
+        isNormalUser = true;
+        openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
+        shell = "${pkgs.fish}/bin/fish";
+      };
+
+      groups."${userGroup}" = { };
+    };
+
+    age.secrets = {
+      minecraft-rcon.file = depot.users.sterni.secrets."minecraft-rcon.age";
+    };
+
+    services.backup-minecraft-fabric-servers = {
+      enable = true;
+      repository = "/srv/backup/from-local/minecraft";
+    };
+
+    services.minecraft-fabric-server = {
+      creative = {
+        enable = false; # not actively used
+        version = "1.20.4";
+        mods = [
+          carpet
+          carpet-extra
+        ];
+        world = config.users.users.${userGroup}.home + "/worlds/creative";
+
+        jvmOpts = makeJvmOpts 2048;
+        user = userGroup;
+        group = userGroup;
+
+        inherit whitelist rconPasswordFile;
+        ops = whitelist;
+
+        serverProperties = baseProperties // {
+          server-port = 25566;
+          "rcon.port" = 25576;
+          gamemode = "creative";
+          enable-command-block = true;
+          motd = "storage design server";
+          spawn-protection = 2;
+        };
+      };
+
+      carpet = {
+        enable = true;
+        version = "1.20.4";
+        mods = [
+          carpet
+          carpet-extra
+        ];
+        world = config.users.users.${userGroup}.home + "/worlds/carpet";
+
+        jvmOpts = makeJvmOpts 4096;
+        user = userGroup;
+        group = userGroup;
+
+        inherit whitelist rconPasswordFile;
+        ops = whitelist;
+
+        serverProperties = baseProperties // {
+          server-port = 25565;
+          "rcon.port" = 25575;
+          motd = "ich tu fleissig hustlen nenn mich bob der baumeister";
+
+          level-seed = 7240251176989694927; # for posterity
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/monitoring.nix b/users/sterni/machines/ingeborg/monitoring.nix
new file mode 100644
index 0000000000..6244bc5e88
--- /dev/null
+++ b/users/sterni/machines/ingeborg/monitoring.nix
@@ -0,0 +1,152 @@
+{ pkgs, lib, config, ... }:
+
+let
+  ircChannel = "#sterni.lv";
+  irccatPort =
+    builtins.replaceStrings [ ":" ] [ "" ]
+      config.services.depot.irccat.config.tcp.listen;
+
+  mkIrcMessager =
+    { name
+    , msgExpr
+    }:
+    pkgs.writeShellScript name ''
+      set -euo pipefail
+      printf '%s %s\n' ${lib.escapeShellArg ircChannel} ${msgExpr} | \
+        ${lib.getBin pkgs.netcat-openbsd}/bin/nc -N localhost ${irccatPort}
+    '';
+
+  netdataPort = 19999;
+in
+
+{
+  imports = [
+    ./irccat.nix
+  ];
+
+  config = {
+    services.depot.irccat.config.irc.channels = [
+      ircChannel
+    ];
+
+    # Since we have irccat we can wire up mdadm --monitor
+    boot.swraid.mdadmConf = ''
+      PROGRAM ${
+        mkIrcMessager {
+          name = "mdmonitor-to-irc";
+          # prog EVENT MD_DEVICE COMPONENT_DEVICE
+          msgExpr = ''"mdmonitor: $1($2''${3:+, $3})"'';
+        }
+      }
+    '';
+
+    # TODO(sterni): irc notifications (?)
+    services = {
+      smartd = {
+        enable = true;
+        autodetect = true;
+        # Short self test every day 03:00
+        # Long self test every tuesday 05:00
+        defaults.autodetected = "-a -o on -s (S/../.././03|L/../../2/05)";
+        extraOptions = [
+          "-A"
+          "/var/log/smartd/"
+        ];
+      };
+
+      netdata = {
+        enable = true;
+        config = {
+          logs = {
+            access = "syslog";
+            error = "syslog";
+            debug = "syslog";
+            health = "syslog";
+            collector = "syslog";
+          };
+          web = {
+            "default port" = toString netdataPort;
+            "bind to" = "localhost:${toString netdataPort}";
+          };
+          health = {
+            "script to execute on alarm" = pkgs.writeShellScript "simple-alarm-notify" ''
+              set -euo pipefail
+
+              # This humongous list is copied over from netdata's alarm-notify.sh
+              roles="''${1}"               # the roles that should be notified for this event
+              args_host="''${2}"           # the host generated this event
+              unique_id="''${3}"           # the unique id of this event
+              alarm_id="''${4}"            # the unique id of the alarm that generated this event
+              event_id="''${5}"            # the incremental id of the event, for this alarm id
+              when="''${6}"                # the timestamp this event occurred
+              name="''${7}"                # the name of the alarm, as given in netdata health.d entries
+              chart="''${8}"               # the name of the chart (type.id)
+              status="''${9}"              # the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
+              old_status="''${10}"         # the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
+              value="''${11}"              # the current value of the alarm
+              old_value="''${12}"          # the previous value of the alarm
+              src="''${13}"                # the line number and file the alarm has been configured
+              duration="''${14}"           # the duration in seconds of the previous alarm state
+              non_clear_duration="''${15}" # the total duration in seconds this is/was non-clear
+              units="''${16}"              # the units of the value
+              info="''${17}"               # a short description of the alarm
+              value_string="''${18}"       # friendly value (with units)
+              # shellcheck disable=SC2034
+              # variable is unused, but https://github.com/netdata/netdata/pull/5164#discussion_r255572947
+              old_value_string="''${19}"   # friendly old value (with units), previously named "old_value_string"
+              calc_expression="''${20}"    # contains the expression that was evaluated to trigger the alarm
+              calc_param_values="''${21}"  # the values of the parameters in the expression, at the time of the evaluation
+              total_warnings="''${22}"     # Total number of alarms in WARNING state
+              total_critical="''${23}"     # Total number of alarms in CRITICAL state
+              total_warn_alarms="''${24}"  # List of alarms in warning state
+              total_crit_alarms="''${25}"  # List of alarms in critical state
+              classification="''${26}"     # The class field from .conf files
+              edit_command_line="''${27}"  # The command to edit the alarm, with the line number
+              child_machine_guid="''${28}" # the machine_guid of the child
+              transition_id="''${29}"      # the transition_id of the alert
+              summary="''${30}"            # the summary text field of the alert
+
+              # Verify that they haven't extended the arg list
+              ARG_COUNT_EXPECTED=30
+
+              if [[ "$#" != "$ARG_COUNT_EXPECTED" ]]; then
+                echo "$0: WARNING: unexpected number of arguments: $#. Did netdata add more?" >&2
+              fi
+
+              MSG="netdata: $status ''${name//_/ } ($chart): ''${summary//_/ } = $value_string"
+
+              # Filter rules by chart name. This is necessary, since the "enabled alarms"
+              # filter only allows for filtering alarm types, not specific alarms
+              # belonging to that alarm.
+              case "$chart" in
+                # netdata prefers the automatically assigned names (dm-<n>, md<n>,
+                # sd<c>) over ids for alerts, so this configuration assumes that
+                # we have two physical disks which we kind of assert using the
+                # grub configuration (it is more difficult with the soft raid
+                # config).
+                # ${assert builtins.length config.boot.loader.grub.devices == 2; ""}
+                disk_util.sda | disk_util.sdb | disk_backlog.sda | disk_backlog.sdb)
+
+                  ;;
+                disk_util.* | disk_backlog.*)
+                  echo "$0: INFO: DISCARDING message: $MSG" >&2
+                  exit 0
+                  ;;
+                *)
+                  ;;
+              esac
+
+              echo "$0: INFO: sending message: $MSG" >&2
+              ${
+                mkIrcMessager {
+                  name = "trivial-send-to-irc";
+                  msgExpr = "\"$1\"";
+                }
+              } "$MSG"
+            '';
+          };
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/network.nix b/users/sterni/machines/ingeborg/network.nix
new file mode 100644
index 0000000000..fceb530d55
--- /dev/null
+++ b/users/sterni/machines/ingeborg/network.nix
@@ -0,0 +1,62 @@
+{ config, pkgs, lib, depot, ... }:
+
+let
+  ipv6 = "2a01:4f9:2a:1bc6::/64";
+
+  ipv4 = "95.216.27.158";
+  gatewayv4 = "95.216.27.129";
+  netmaskv4 = "255.255.255.192";
+in
+
+{
+  config = {
+    boot = {
+      kernelParams = [
+        "ip=${ipv4}::${gatewayv4}:${netmaskv4}::eth0:none"
+      ];
+
+      initrd.network = {
+        enable = true;
+        ssh = {
+          enable = true;
+          authorizedKeys = depot.users.sterni.keys.all;
+          hostKeys = [
+            "/etc/nixos/unlock_rsa_key_openssh"
+            "/etc/nixos/unlock_ed25519_key_openssh"
+          ];
+        };
+        postCommands = ''
+          echo 'cryptsetup-askpass' >> /root/.profile
+        '';
+      };
+    };
+
+    networking = {
+      usePredictableInterfaceNames = false;
+      useDHCP = false;
+      interfaces."eth0".useDHCP = false;
+
+      hostName = "ingeborg";
+
+      firewall = {
+        enable = true;
+        allowPing = true;
+        allowedTCPPorts = [ 22 ];
+      };
+    };
+
+    systemd.network = {
+      enable = true;
+      networks."eth0".extraConfig = ''
+        [Match]
+        Name = eth0
+
+        [Network]
+        Address = ${ipv6}
+        Gateway = fe80::1
+        Address = ${ipv4}/27
+        Gateway = ${gatewayv4}
+      '';
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/quassel.nix b/users/sterni/machines/ingeborg/quassel.nix
new file mode 100644
index 0000000000..cd8dacc917
--- /dev/null
+++ b/users/sterni/machines/ingeborg/quassel.nix
@@ -0,0 +1,18 @@
+{ depot, ... }:
+
+{
+  imports = [
+    (depot.path.origSrc + "/ops/modules/quassel.nix")
+  ];
+
+  config = {
+    services.depot.quassel = {
+      enable = true;
+      acmeHost = "sterni.lv";
+      bindAddresses = [
+        "0.0.0.0"
+        "::"
+      ];
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/tv.nix b/users/sterni/machines/ingeborg/tv.nix
new file mode 100644
index 0000000000..016ad256ef
--- /dev/null
+++ b/users/sterni/machines/ingeborg/tv.nix
@@ -0,0 +1,13 @@
+{ pkgs, ... }:
+
+{
+  config = {
+    # TODO(sterni): smb or nfs may be a faster alternative?
+    services.openssh.allowSFTP = true;
+
+    users.users.tv = {
+      group = "users";
+      isNormalUser = true;
+    };
+  };
+}
diff --git a/users/sterni/mblog/.gitignore b/users/sterni/mblog/.gitignore
new file mode 100644
index 0000000000..ae957fcad0
--- /dev/null
+++ b/users/sterni/mblog/.gitignore
@@ -0,0 +1,5 @@
+# local test data
+test-msg
+
+# sly C-c C-k
+*.fasl
diff --git a/users/sterni/mblog/LICENSE b/users/sterni/mblog/LICENSE
new file mode 100644
index 0000000000..f288702d2f
--- /dev/null
+++ b/users/sterni/mblog/LICENSE
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/users/sterni/mblog/cli.lisp b/users/sterni/mblog/cli.lisp
index 93be7e8b8e..555f8def53 100644
--- a/users/sterni/mblog/cli.lisp
+++ b/users/sterni/mblog/cli.lisp
@@ -1,17 +1,75 @@
-(in-package :mblog)
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
+
+(in-package :cli)
 (declaim (optimize (safety 3)))
 
-(defparameter +synopsis+ "mnote-html FILE [FILE [ ... ]]")
+;; TODO(sterni): nicer messages for various errors signaled?
+
+(defun partition-by (f seq)
+  "Split SEQ into two lists, returned as multiple values. The first list
+  contains all elements for which F returns T, the second one the remaining
+  elements."
+  (loop for x in seq
+        if (funcall f x)
+          collecting x into yes
+        else
+          collecting x into no
+        finally (return (values yes no))))
+
+(defparameter +help+ '(("mnote-html" . "FILE [FILE [ ... ]]")
+                       ("mblog"      . "MAILDIR OUT")))
+
+(defun mnote-html (name flags &rest args)
+  "Convert all note mime messages given as ARGS to HTML fragments."
+  (declare (ignore name flags))
+  (loop for arg in args
+        do (note:apple-note-html-fragment
+            (note:make-apple-note (mime:mime-message (pathname arg)))
+            *standard-output*)))
+
+(defun mblog (name flags maildir outdir)
+  "Read a MAILDIR and build an mblog in OUTDIR "
+  (declare (ignore name flags))
+  (build-mblog (pathname maildir) (pathname outdir)))
+
+(defun display-help (name flags &rest args)
+  "Print help message for current executable."
+  (declare (ignore args flags))
+  (format *error-output* "Usage: ~A ~A~%"
+          name
+          (or (cdr (assoc name +help+ :test #'string=))
+              (concatenate 'string "Unknown executable: " name))))
+
+(defun usage-error (name flags &rest args)
+  "Print help and exit with a non-zero exit code."
+  (format *error-output* "~A: usage error~%" name)
+  (display-help name args flags)
+  (uiop:quit 100))
 
-;; TODO(sterni): handle relevant conditions
 (defun main ()
-  (let* ((args (uiop:command-line-arguments))
-         (help-p (or (not args)
-                     (find-if (lambda (x)
-                                (member x '("-h" "--help" "--usage")
-                                        :test #'string=))
-                              args))))
-    (if help-p (format *error-output* "Usage: ~A~%" +synopsis+)
-      (loop for arg in args
-            do (apple-note-html-fragment
-                (mime:mime-message (pathname arg)) *standard-output*)))))
+  "Dispatch to correct main function based on arguments and UIOP:ARGV0."
+  (config:init-from-env)
+  (multiple-value-bind (flags args)
+      (partition-by (lambda (x) (starts-with #\- x))
+                    (uiop:command-line-arguments))
+
+    (let ((prog-name (pathname-name (pathname (uiop:argv0))))
+          (help-requested-p (find-if (lambda (x)
+                                       (member x '("-h" "--help" "--usage")
+                                               :test #'string=))
+                                     args)))
+      (apply
+       (if help-requested-p
+           #'display-help
+           (cond
+             ((and (string= prog-name "mnote-html")
+                   (null flags))
+              #'mnote-html)
+             ((and (string= prog-name "mblog")
+                   (null flags)
+                   (= 2 (length args)))
+              #'mblog)
+             (t #'usage-error)))
+       (append (list prog-name flags)
+               args)))))
diff --git a/users/sterni/mblog/config.lisp b/users/sterni/mblog/config.lisp
new file mode 100644
index 0000000000..0d4cbfe8ae
--- /dev/null
+++ b/users/sterni/mblog/config.lisp
@@ -0,0 +1,31 @@
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2023 by sterni
+
+(in-package :config)
+
+(eval-when (:compile-toplevel :load-toplevel)
+  (defun plist-to-alist (lst)
+    (loop for (name . (default . (parser . nil))) on lst by #'cdddr
+          collect (cons name (list default parser))))
+
+  (defun symbol-to-env-var-name (symbol)
+    (concatenate 'string
+                 "MBLOG_"
+                 (string-upcase
+                  (remove #\* (substitute #\_ #\- (string symbol)))))))
+
+(defmacro define-configuration-variables (&rest args)
+  (let ((vars (plist-to-alist args))
+        (val-var-sym (gensym)))
+    `(progn
+       ,@(loop for (name . (default nil)) in vars
+              collect `(defvar ,name ,default))
+
+       (defun init-from-env ()
+         ,@(loop for (name . (nil parser)) in vars
+                 collect
+                 `(when-let ((,val-var-sym (getenv ,(symbol-to-env-var-name name))))
+                    (setf ,name (funcall ,parser ,val-var-sym))))))))
+
+(define-configuration-variables
+  *general-buffer-size* (min 4096 qbase64:+max-bytes-length+) #'parse-integer)
diff --git a/users/sterni/mblog/default.nix b/users/sterni/mblog/default.nix
index 16ae573ba7..6ad8a10ce3 100644
--- a/users/sterni/mblog/default.nix
+++ b/users/sterni/mblog/default.nix
@@ -1,12 +1,17 @@
+# SPDX-License-Identifier: GPL-3.0-only
+# SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
 { depot, pkgs, ... }:
 
-depot.nix.buildLisp.program {
-  name = "mnote-html";
+(depot.nix.buildLisp.program {
+  name = "mblog";
 
   srcs = [
     ./packages.lisp
+    ./config.lisp
+    ./maildir.lisp
     ./transformer.lisp
     ./note.lisp
+    ./mblog.lisp
     ./cli.lisp
   ];
 
@@ -15,17 +20,28 @@ depot.nix.buildLisp.program {
       sbcl = depot.nix.buildLisp.bundled "uiop";
       default = depot.nix.buildLisp.bundled "asdf";
     }
+    depot.lisp.klatre
     depot.third_party.lisp.alexandria
     depot.third_party.lisp.closure-html
+    depot.third_party.lisp.cl-date-time-parser
     depot.third_party.lisp.cl-who
+    depot.third_party.lisp.local-time
     depot.third_party.lisp.mime4cl
   ];
 
-  main = "mblog:main";
+  main = "cli:main";
 
   # due to sclf
   brokenOn = [
     "ccl"
     "ecl"
   ];
-}
+}).overrideAttrs (super: {
+  # The built binary dispatches based on argv[0]. Building two executables would
+  # waste a lot of space.
+  buildCommand = ''
+    ${super.buildCommand}
+
+    ln -s "$out/bin/mblog" "$out/bin/mnote-html"
+  '';
+})
diff --git a/users/sterni/mblog/maildir.lisp b/users/sterni/mblog/maildir.lisp
new file mode 100644
index 0000000000..42f18c619d
--- /dev/null
+++ b/users/sterni/mblog/maildir.lisp
@@ -0,0 +1,20 @@
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022 by sterni
+
+(in-package :maildir)
+(declaim (optimize (safety 3)))
+
+(defun list (dir)
+  "Returns a list of pathnames to messages in a maildir. The messages are
+  returned in no guaranteed order. Note that this function doesn't fully
+  implement the behavior prescribed by maildir(5): It only looks at `cur`
+  and `new` and won't clean up `tmp` nor move files from `new` to `cur`,
+  since it is strictly read-only."
+  (flet ((subdir-contents (subdir)
+           (directory
+            (merge-pathnames
+             (make-pathname :directory `(:relative ,subdir)
+                            :name :wild :type :wild)
+             dir))))
+    (mapcan #'subdir-contents '("cur" "new"))))
+
diff --git a/users/sterni/mblog/mblog.lisp b/users/sterni/mblog/mblog.lisp
new file mode 100644
index 0000000000..7823bde203
--- /dev/null
+++ b/users/sterni/mblog/mblog.lisp
@@ -0,0 +1,147 @@
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
+;; SPDX-FileCopyrightText: Copyright (C) 2006-2010 by Walter C. Pelissero
+
+(in-package :mblog)
+
+;; util
+
+;; Taken from SCLF, written by Walter C. Pelissero
+(defun pathname-as-directory (pathname)
+  "Converts PATHNAME to directory form and return it."
+  (setf pathname (pathname pathname))
+  (if (pathname-name pathname)
+      (make-pathname :directory (append (or (pathname-directory pathname)
+                                            '(:relative))
+                                        (list (file-namestring pathname)))
+                     :name nil
+                     :type nil
+                     :defaults pathname)
+      pathname))
+
+(defmacro with-overwrite-file ((&rest args) &body body)
+  "Like WITH-OPEN-FILE, but creates/supersedes the given file for writing."
+  `(with-open-file (,@args :direction :output
+                           :if-exists :supersede
+                           :if-does-not-exist :create)
+     ,@body))
+
+;; CSS
+
+(defvar *style* "
+header, main {
+  width: 100%;
+  max-width: 800px;
+}
+
+main img {
+  max-width: 100%;
+}
+
+a:link, a:visited {
+  color: blue;
+}
+")
+
+;; Templating
+
+(eval-when (:compile-toplevel :load-toplevel)
+  (setf (who:html-mode) :html5))
+
+(defmacro render-page ((stream title &key root) &body body)
+  "Surround BODY with standard mblog document skeleton and render it to STREAM
+  using CL-WHO. If :ROOT is T, assume that the page is the top level index page.
+  Otherwise it is assumed to be one level below the index page."
+  `(who:with-html-output (,stream nil :prologue t)
+    (:html
+     (:head
+      (:meta :charset "utf-8")
+      (:meta :viewport "width=device-width")
+      (:title (who:esc ,title))
+      (:link :rel "stylesheet"
+             :type "text/css"
+             :href ,(concatenate 'string (if root "" "../") "style.css")))
+     (:body
+      (:header
+       (:nav
+        (:a :href ,(who:escape-string (if root "" "..")) "index")))
+      (:main ,@body)))))
+
+;; Build Logic
+
+(defun build-note-page (note note-dir)
+  "Convert NOTE to HTML and write it to index.html in NOTE-DIR alongside any
+  extra attachments NOTE contains."
+  (with-overwrite-file (html-stream (merge-pathnames "index.html" note-dir))
+    (render-page (html-stream (apple-note-subject note))
+      (:article
+       (apple-note-html-fragment note html-stream))))
+
+  (mime:do-parts (part note)
+    (unless (string= (mime:mime-id part)
+                     (mime:mime-id (note:apple-note-text-part note)))
+      (let ((attachment-in (mime:mime-body-stream part))
+            (attachment-dst (merge-pathnames
+                             (mime:mime-part-file-name part)
+                             note-dir)))
+
+        (format *error-output* "Writing attachment ~A~%" attachment-dst)
+
+        (with-overwrite-file (attachment-out attachment-dst
+                              :element-type
+                              (stream-element-type attachment-in))
+          (redirect-stream attachment-in attachment-out
+                           :buffer-size *general-buffer-size*)))))
+
+  (values))
+
+(defun build-index-page (notes-list destination)
+  "Write an overview page linking all notes in NOTE-LIST in the given order to
+  DESTINATION. The notes are assumed to be in a sibling directory named like the
+  each note's UUID."
+  (with-overwrite-file (listing-stream destination)
+    (render-page (listing-stream "mblog" :root t)
+      (:h1 "mblog")
+      (:table
+       (dolist (note notes-list)
+         (who:htm
+          (:tr
+           (:td (:a :href (who:escape-string (apple-note-uuid note))
+                    (who:esc (apple-note-subject note))))
+           (:td (who:esc
+                 (klatre:format-dottime
+                  (universal-to-timestamp (apple-note-time note)))))))))))
+  (values))
+
+(defun build-mblog (notes-dir html-dir)
+  "Take MIME messages from maildir NOTES-DIR and build a complete mblog in HTML-DIR."
+  (setf notes-dir (pathname-as-directory notes-dir))
+  (setf html-dir (pathname-as-directory html-dir))
+
+  ;; TODO(sterni): avoid rewriting if nothing was updated
+  ;; TODO(sterni): clean up deleted things
+  ;; TODO(sterni): atom feed
+
+  (let ((all-notes '()))
+    (dolist (message-path (maildir:list notes-dir))
+      (let* ((note (make-apple-note (mime:mime-message message-path)))
+             (note-dir  (merge-pathnames (make-pathname
+                                          :directory
+                                          `(:relative ,(apple-note-uuid note)))
+                                         html-dir)))
+
+        (format *error-output* "Writing note message ~A to ~A~%"
+                message-path note-dir)
+        (ensure-directories-exist note-dir)
+        (build-note-page note note-dir)
+        (push note all-notes)))
+
+    ;; reverse sort the entries by time for the index page
+    (setf all-notes (sort all-notes #'> :key #'apple-note-time))
+
+    (build-index-page all-notes (merge-pathnames "index.html" html-dir))
+
+    (with-overwrite-file (css-stream (merge-pathnames "style.css" html-dir))
+      (write-string *style* css-stream))
+
+    (values)))
diff --git a/users/sterni/mblog/note.lisp b/users/sterni/mblog/note.lisp
index fa4de0956f..f056aaa72d 100644
--- a/users/sterni/mblog/note.lisp
+++ b/users/sterni/mblog/note.lisp
@@ -1,60 +1,118 @@
-(in-package :mblog)
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
+
+(in-package :note)
 (declaim (optimize (safety 3)))
 
 ;;; util
 
 (defun html-escape-stream (in out)
   "Escape characters read from stream IN and write them to
-  stream OUT escaped using WHO:ESCAPE-CHAR-MINIMAL."
-  (loop for char = (read-char in nil nil)
-        while char
-        do (write-string (who:escape-char-minimal char) out)))
+  stream OUT escaped using WHO:ESCAPE-STRING-MINIMAL."
+  (let ((buf (make-string config:*general-buffer-size*)))
+    (loop for len = (read-sequence buf in)
+          while (> len 0)
+          do (write-string (who:escape-string-minimal (subseq buf 0 len)) out))))
 
 (defun cid-header-value (cid)
   "Takes a Content-ID as present in Apple Notes' <object> tags and properly
   surrounds them with angle brackets for a MIME header"
   (concatenate 'string "<" cid ">"))
 
+(defun find-mime-message-date (message)
+  (when-let ((date-string (car (mime:mime-message-header-values "Date" message))))
+    (date-time-parser:parse-date-time date-string)))
+
 ;;; main implementation
 
-;; TODO(sterni): make this a β€œparser” instead of a predicate
+(defun apple-note-mime-subtype-p (x)
+  (member x '("plain" "html") :test #'string-equal))
+
+(deftype apple-note-mime-subtype ()
+  '(satisfies apple-note-mime-subtype-p))
+
+(defclass apple-note (mime:mime-message)
+  ((text-part
+    :type mime:mime-text
+    :initarg :text-part
+    :reader apple-note-text-part)
+   (subject
+    :type string
+    :initarg :subject
+    :reader apple-note-subject)
+   (uuid
+    :type string
+    :initarg :uuid
+    :reader apple-note-uuid)
+   (time
+    :type integer
+    :initarg :time
+    :reader apple-note-time)
+   (mime-subtype
+    :type apple-note-mime-subtype
+    :initarg :mime-subtype
+    :reader apple-note-mime-subtype))
+  (:documentation
+   "Representation of a Note created using Apple's Notes via the IMAP backend"))
+
 (defun apple-note-p (msg)
   "Checks X-Uniform-Type-Identifier of a MIME:MIME-MESSAGE
-  to determine if a given mime message is an Apple Note."
-  (when-let (uniform-id (assoc "X-Uniform-Type-Identifier"
-                               (mime:mime-message-headers msg)
-                               :test #'string=))
-    (string= (cdr uniform-id) "com.apple.mail-note")))
-
-(defun apple-note-html-fragment (msg out)
-  "Takes a MIME:MIME-MESSAGE and writes its text content as HTML to
-  the OUT stream. The <object> tags are resolved to <img> which
-  refer to the respective attachment's filename as a relative path,
-  but extraction of the attachments must be done separately. The
-  surrounding <html> and <body> tags are stripped and <head>
-  discarded completely, so only a fragment which can be included
-  in custom templates remains."
-  (let ((text (find-mime-text-part msg)))
+  to determine if a given mime message claims to be an Apple Note."
+  (when-let (uniform-id (car (mime:mime-message-header-values
+                              "X-Uniform-Type-Identifier"
+                              msg)))
+    (string-equal uniform-id "com.apple.mail-note")))
+
+(defun make-apple-note (msg)
+  (check-type msg mime-message)
+
+  (unless (apple-note-p msg)
+    (error "Passed message is not an Apple Note according to headers"))
+
+  (let ((text-part (mime:find-mime-text-part msg))
+        (subject (car (mime:mime-message-header-values "Subject" msg :decode t)))
+        (uuid (when-let ((val (car (mime:mime-message-header-values
+                                    "X-Universally-Unique-Identifier"
+                                    msg))))
+                (string-downcase val)))
+        (time (find-mime-message-date msg)))
+    ;; The idea here is that we don't need to check a lot manually, instead
+    ;; the type annotation are going to do this for us (with sufficient safety?)
+    (change-class msg 'apple-note
+                  :text-part text-part
+                  :subject subject
+                  :uuid uuid
+                  :time time
+                  :mime-subtype (mime:mime-subtype text-part))))
+
+(defgeneric apple-note-html-fragment (note out)
+  (:documentation
+   "Takes an APPLE-NOTE and writes its text content as HTML to
+   the OUT stream. The <object> tags are resolved to <img> which
+   refer to the respective attachment's filename as a relative path,
+   but extraction of the attachments must be done separately. The
+   surrounding <html> and <body> tags are stripped and <head>
+   discarded completely, so only a fragment which can be included
+   in custom templates remains."))
+
+(defmethod apple-note-html-fragment ((note apple-note) (out stream))
+  (let ((text (apple-note-text-part note)))
     (cond
-      ;; Sanity checking of the note
-      ((not (apple-note-p msg))
-       (error "Unsupported or missing X-Uniform-Type-Identifier"))
-      ((not text) (error "Malformed Apple Note: no text part"))
       ;; notemap creates text/plain notes we need to handle properly.
       ;; Additionally we *could* check X-Mailer which notemap sets
-      ((string= (mime:mime-subtype text) "plain")
-       (html-escape-stream (mime:mime-body-stream text :binary nil) out))
+      ((string-equal (apple-note-mime-subtype note) "plain")
+       (html-escape-stream (mime:mime-body-stream text) out))
       ;; Notes.app creates text/html parts
-      ((string= (mime:mime-subtype text) "html")
+      ((string-equal (apple-note-mime-subtype note) "html")
        (closure-html:parse
         (mime:mime-body-stream text)
         (make-instance
          'apple-note-transformer
          :cid-lookup
          (lambda (cid)
-           (when-let* ((part (mime:find-mime-part-by-id msg (cid-header-value cid)))
+           (when-let* ((part (mime:find-mime-part-by-id note (cid-header-value cid)))
                        (file (mime:mime-part-file-name part)))
              file))
          :next-handler
          (closure-html:make-character-stream-sink out))))
-      (t (error "Malformed Apple Note: unknown mime type")))))
+      (t (error "Internal error: unexpected MIME subtype")))))
diff --git a/users/sterni/mblog/packages.lisp b/users/sterni/mblog/packages.lisp
index ca2e41b682..d6e33955d3 100644
--- a/users/sterni/mblog/packages.lisp
+++ b/users/sterni/mblog/packages.lisp
@@ -1,15 +1,64 @@
-(defpackage :mblog
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
+
+(defpackage :maildir
+  (:use :common-lisp)
+  (:shadow :list)
+  (:export :list)
+  (:documentation
+   "Very incomplete package for dealing with maildir(5)."))
+
+(defpackage :config
+  (:use
+   :common-lisp)
+  (:import-from :uiop :getenv)
+  (:import-from :alexandria :when-let)
+  (:export
+   :init-from-env
+   :*general-buffer-size*))
+
+(defpackage :note
   (:use
    :common-lisp
-   :mime4cl
    :closure-html
-   :who
-   :uiop)
-  (:shadow :with-html-output) ; conflict between closure-html and who
+   :cl-date-time-parser
+   :mime4cl
+   :config)
   (:import-from
    :alexandria
    :when-let*
    :when-let
    :starts-with-subseq
    :ends-with-subseq)
+  (:import-from :who :escape-string-minimal)
+  (:export
+   :apple-note
+   :apple-note-uuid
+   :apple-note-subject
+   :apple-note-time
+   :apple-note-text-part
+   :make-apple-note
+   :apple-note-html-fragment))
+
+(defpackage :mblog
+  (:use
+   :common-lisp
+   :klatre
+   :who
+   :maildir
+   :note
+   :config)
+  (:export :build-mblog)
+  (:import-from :local-time :universal-to-timestamp)
+  (:import-from :mime4cl :redirect-stream)
+  (:shadowing-import-from :common-lisp :list))
+
+(defpackage :cli
+  (:use
+   :common-lisp
+   :uiop
+   :note
+   :config
+   :mblog)
+  (:import-from :alexandria :starts-with)
   (:export :main))
diff --git a/users/sterni/mblog/transformer.lisp b/users/sterni/mblog/transformer.lisp
index f26c5652a2..c499eafbec 100644
--- a/users/sterni/mblog/transformer.lisp
+++ b/users/sterni/mblog/transformer.lisp
@@ -1,4 +1,7 @@
-(in-package :mblog)
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022 by sterni
+
+(in-package :note)
 (declaim (optimize (safety 3)))
 
 ;; Throw away these tags and all of their children
@@ -60,7 +63,7 @@
 
 (defun parse-content-id (attrlist)
   (when-let (data (find-if (lambda (x)
-                             (string= (hax:attribute-name x) "DATA"))
+                             (string-equal (hax:attribute-name x) "DATA"))
                            attrlist))
     (multiple-value-bind (starts-with-cid-p suffix)
         (starts-with-subseq "cid:" (hax:attribute-value data)
@@ -81,16 +84,16 @@
       ;; If we are not discarding any outer elements, we can set
       ;; up a new discard condition if we encounter an appropriate
       ;; element.
-      ((member name +discard-tags-with-children+ :test #'string=)
+      ((member name +discard-tags-with-children+ :test #'string-equal)
        (setf discard-until (cons name depth)))
       ;; Only drop this event, must be mirrored in END-ELEMENT to
       ;; avoid invalidly nested HTML.
-      ((member name +discard-tags-only+ :test #'string=) nil)
+      ((member name +discard-tags-only+ :test #'string-equal) nil)
       ;; If we encounter an object tag, we drop it and its contents,
       ;; but only after inspecting its attributes and emitting new
       ;; events representing an img tag which includes the respective
       ;; attachment via its filename.
-      ((string= name "OBJECT")
+      ((string-equal name "OBJECT")
        (progn
          (setf discard-until (cons "OBJECT" depth))
          ;; TODO(sterni): check type and only resolve images, raise error
@@ -116,12 +119,12 @@
       ;; If we are discarding and encounter the same tag again at the same
       ;; depth, we can stop, but still have to discard the current tag.
       ((and discard-until
-            (string= (car discard-until) name)
+            (string-equal (car discard-until) name)
             (= (cdr discard-until) depth))
        (setf discard-until nil))
       ;; In all other cases, we drop properly.
       (discard-until nil)
       ;; Mirrored tag stripping as in START-ELEMENT
-      ((member name +discard-tags-only+ :test #'string=) nil)
+      ((member name +discard-tags-only+ :test #'string-equal) nil)
       ;; In all other cases, we use HAX-PROXY-HANDLER to pass the event on.
       (t (call-next-method)))))
diff --git a/users/sterni/modules/backup-minecraft-fabric.nix b/users/sterni/modules/backup-minecraft-fabric.nix
new file mode 100644
index 0000000000..a80a7f51a9
--- /dev/null
+++ b/users/sterni/modules/backup-minecraft-fabric.nix
@@ -0,0 +1,125 @@
+# Companion module to minecraft-fabric.nix which automatically and regularly
+# creates backups of all minecraft servers' worlds to a shared borg(1)
+# repository.
+#
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2023 sterni <sternenseemann@systemli.org>
+{ pkgs, depot, config, lib, ... }:
+
+let
+  inherit (depot.nix) getBins;
+
+  bins = getBins pkgs.borgbackup [ "borg" ]
+    // getBins pkgs.mcrcon [ "mcrcon" ];
+
+  unvaried = ls: builtins.all (l: l == builtins.head ls) ls;
+
+  cfg = config.services.backup-minecraft-fabric-servers;
+
+  instances = lib.filterAttrs (_: i: i.enable) config.services.minecraft-fabric-server;
+  users = lib.mapAttrsToList (_: i: i.user) instances;
+  groups = lib.mapAttrsToList (_: i: i.group) instances;
+
+  mkBackupScript = instanceName: instanceCfg:
+    let
+      archivePrefix = "minecraft-fabric-${instanceName}-world-${builtins.baseNameOf instanceCfg.world}-";
+    in
+
+    pkgs.writeShellScript "backup-minecraft-fabric-${instanceName}" ''
+      export MCRCON_HOST="localhost"
+      export MCRCON_PORT="${toString instanceCfg.serverProperties."rcon.port"}"
+      # Unfortunately, mcrcon can't read the password from a file
+      export MCRCON_PASS="$(cat "''${CREDENTIALS_DIRECTORY}/${instanceName}-rcon-password")"
+
+      ${bins.mcrcon} save-all
+      unset MCRCON_PASS
+
+      # Give the server plenty of time to save
+      sleep 60
+
+      ${bins.borg} ${lib.escapeShellArgs [
+        "create"
+        "--verbose" "--filter" "AMEU" "--list"
+        "--stats" "--show-rc"
+        "--compression" "zlib"
+        "${cfg.repository}::${archivePrefix}{now}"
+        instanceCfg.world
+      ]}
+
+      ${bins.borg} ${lib.escapeShellArgs [
+        "prune"
+        "--list"
+        "--show-rc"
+        "--glob-archives" "${archivePrefix}*"
+        "--keep-hourly" "168"
+        "--keep-daily" "31"
+        "--keep-monthly" "6"
+        "--keep-yearly" "2"
+        cfg.repository
+      ]}
+
+      ${bins.borg} compact ${lib.escapeShellArg cfg.repository}
+    '';
+in
+
+{
+  imports = [
+    ./minecraft-fabric.nix
+  ];
+
+  options = {
+    services.backup-minecraft-fabric-servers = {
+      enable = lib.mkEnableOption "backups of all Minecraft fabric servers";
+
+      repository = lib.mkOption {
+        type = lib.types.path;
+        description = "Path to the borg(1) repository to use for all backups.";
+        default = "/var/lib/backup/minecraft-fabric";
+      };
+    };
+  };
+
+  config = lib.mkIf (cfg.enable && builtins.length (builtins.attrNames instances) > 0) {
+    assertions = [
+      {
+        assertion = unvaried users && unvaried groups;
+        message = "all instances under services.minecraft-fabric-server must use the same user and group";
+      }
+    ];
+
+    environment.systemPackages = [
+      pkgs.borgbackup
+    ];
+
+    systemd = {
+      services.backup-minecraft-fabric-servers = {
+        description = "Backup world of all fabric based Minecraft servers";
+        wantedBy = [ ];
+        after = builtins.map
+          (name: "minecraft-fabric-${name}.service")
+          (builtins.attrNames instances);
+
+        script = lib.concatStrings (lib.mapAttrsToList mkBackupScript instances);
+
+        serviceConfig = {
+          Type = "oneshot";
+          User = builtins.head users;
+          Group = builtins.head groups;
+          LoadCredential = lib.mapAttrsToList
+            (instanceName: instanceCfg: "${instanceName}-rcon-password:${instanceCfg.rconPasswordFile}")
+            instances;
+        };
+      };
+
+      timers.backup-minecraft-fabric-servers = {
+        description = "Regularly backup Minecraft fabric servers";
+        wantedBy = [ "timers.target" ];
+        timerConfig = {
+          OnCalendar = "*-*-* 00/3:00:00";
+          Persistent = true;
+          RandomizedDelaySec = "1h";
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/modules/common.nix b/users/sterni/modules/common.nix
new file mode 100644
index 0000000000..2c513acad3
--- /dev/null
+++ b/users/sterni/modules/common.nix
@@ -0,0 +1,79 @@
+# This module is common in the weakest sense, i.e. contains common settings to
+# all my machines contained in depotβ€”as opposed to common to all my potential
+# machines. Consequently, this module is currently very server-centric.
+{ pkgs, lib, depot, config, ... }:
+
+let
+  me = "lukas";
+in
+
+{
+  config = {
+
+    # More common
+
+    time.timeZone = "Europe/Berlin";
+
+    nix = {
+      package = pkgs.nix_2_3;
+      settings = {
+        trusted-public-keys = lib.mkAfter [
+          "headcounter.org:/7YANMvnQnyvcVB6rgFTdb8p5LG1OTXaO+21CaOSBzg="
+        ];
+        substituters = lib.mkAfter [
+          "https://hydra.build"
+        ];
+        trusted-users = [ me ];
+      };
+    };
+    tvl.cache.enable = true;
+
+    programs.fish.enable = true;
+
+    users = {
+      users = {
+        root.openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
+        ${me} = {
+          isNormalUser = true;
+          extraGroups = [ "wheel" "http" "git" ];
+          openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
+          shell = pkgs.fish;
+        };
+      };
+    };
+
+    # Less common
+
+    services = {
+      journald.extraConfig = ''
+        SystemMaxUse=10G
+      '';
+
+      openssh.enable = true;
+    };
+
+    programs = {
+      mosh.enable = true;
+      tmux.enable = true;
+    };
+
+    environment.systemPackages = [
+      pkgs.wget
+      pkgs.git
+      pkgs.stow
+      pkgs.htop
+      pkgs.foot.terminfo
+      pkgs.vim
+      pkgs.smartmontools
+    ];
+
+    security.acme = {
+      defaults.email = builtins.getAttr "email" (
+        builtins.head (
+          builtins.filter (attrs: attrs.username == "sterni") depot.ops.users
+        )
+      );
+      acceptTerms = true;
+    };
+  };
+}
diff --git a/users/sterni/modules/default.nix b/users/sterni/modules/default.nix
new file mode 100644
index 0000000000..5cc8be3cc6
--- /dev/null
+++ b/users/sterni/modules/default.nix
@@ -0,0 +1,2 @@
+# Stop readTree from looking at this directory
+_: { }
diff --git a/users/sterni/modules/minecraft-fabric.nix b/users/sterni/modules/minecraft-fabric.nix
new file mode 100644
index 0000000000..6cc32cd205
--- /dev/null
+++ b/users/sterni/modules/minecraft-fabric.nix
@@ -0,0 +1,532 @@
+# Declarative, but low Nix module for a modded minecraft server using the
+# fabric mod loader. That is to say, the build of the final server JAR
+# is not encapsulated in a derivation.
+#
+# The module has the following interesting properties:
+#
+#   * The fabric installer is executed on each server startup to assemble the
+#     patched server.jar. This is unfortunately necessary, as it seems to be
+#     difficult to do so in a derivation (fabric-installer accesses the network,
+#     the build doesn't seem to be reproducible). At least this avoids the
+#     question of the patched jar's redistributability.
+#   * RCON is used for starting and stopping which should prevent data loss,
+#     since we can issue a manual save command.
+#   * The entire runtime directory of the server is assembled from scratch on
+#     each start, so only blessed state (like the world) and declarative
+#     configuration (whitelist.json, server.properties, ...) survive.
+#   * It supports more than one server running on the same machine.
+#
+# Missing features:
+#
+#   * Support for bans
+#   * Support for mutable whitelist, ops, …
+#   * Op levels
+#
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2022-2024 sterni <sternenseemann@systemli.org>
+
+{ lib, pkgs, config, depot, ... }:
+
+let
+  #
+  # Dependencies
+  #
+  inherit (depot.nix.utils) storePathName;
+  inherit (depot.nix) getBins;
+
+  bins = getBins pkgs.mcrcon [ "mcrcon" ]
+    // getBins pkgs.jre [ "java" ]
+    // getBins pkgs.diffutils [ "diff" ]
+    // getBins pkgs.moreutils [ "sponge" ]
+    // getBins pkgs.extrace [ "pwait" ]
+    // getBins pkgs.util-linux [ "flock" ];
+
+  #
+  # Needed JARs
+  #
+  fetchJar = { pname, version, url, sha256, passthru ? { } }:
+    pkgs.fetchurl {
+      name = "${pname}-${version}.jar";
+      inherit url sha256;
+      passthru = passthru // { inherit version; };
+    };
+
+  fabricInstallerJar =
+    fetchJar rec {
+      pname = "fabric-installer";
+      version = "1.0.0";
+      url = "https://maven.fabricmc.net/net/fabricmc/fabric-installer/${version}/fabric-installer-${version}.jar";
+      sha256 = "0yrlzly1g5a80df27jvrbhxbp10xqxfyk64q0s0j13kz78fmnzkx";
+    };
+
+  # log4j workaround for Minecraft Server >= 1.12 && < 1.17
+  log4jFix_112_116 = pkgs.fetchurl {
+    url = "https://launcher.mojang.com/v1/objects/02937d122c86ce73319ef9975b58896fc1b491d1/log4j2_112-116.xml";
+    sha256 = "1paha357xbaffl38ckzgdh4l5iib2ydqbv7jsg67nj31nlalclr9";
+  };
+
+  serverJars = {
+    # Manually updated list of known minecraft `server.jar`s for now.
+    # Making this comprehensive isn't that interesting for now, since the module
+    # is annoying to use outside of depot anyways as it uses //nix.
+    "1.16.5" = fetchJar {
+      pname = "server";
+      version = "1.16.5";
+      url = "https://launcher.mojang.com/v1/objects/1b557e7b033b583cd9f66746b7a9ab1ec1673ced/server.jar";
+      sha256 = "19ix6x5ij4jcwqam1dscnqwm0m251gysc2j793wjcrb9sb3jkwsq";
+      passthru = {
+        baseJvmOpts = [
+          "-Dlog4j.configurationFile=${log4jFix_112_116}"
+        ];
+      };
+    };
+    "1.17" = fetchJar {
+      pname = "server";
+      version = "1.17";
+      url = "https://launcher.mojang.com/v1/objects/0a269b5f2c5b93b1712d0f5dc43b6182b9ab254e/server.jar";
+      sha256 = "0jqz7hpx7zvjj2n5rfrh8jmdj6ziqyp8c9nq4sr4jmkbky6hsfbv";
+      passthru.baseJvmOpts = [
+        "-Dlog4j2.formatMsgNoLookups=true"
+      ];
+    };
+    "1.17.1" = fetchJar {
+      pname = "server";
+      version = "1.17.1";
+      url = "https://launcher.mojang.com/v1/objects/a16d67e5807f57fc4e550299cf20226194497dc2/server.jar";
+      sha256 = "0pzmzagvrrapjsnd8xg4lqwynwnb5rcqk2n9h2kzba8p2fs13hp8";
+      passthru.baseJvmOpts = [
+        "-Dlog4j2.formatMsgNoLookups=true"
+      ];
+    };
+    "1.18" = fetchJar {
+      pname = "server";
+      version = "1.18";
+      url = "https://launcher.mojang.com/v1/objects/3cf24a8694aca6267883b17d934efacc5e44440d/server.jar";
+      sha256 = "0vvycjcfq96z7cl5dsrq98k9b7j7l4x0y9nflrcqmcvink7fs5w4";
+      passthru.baseJvmOpts = [
+        "-Dlog4j2.formatMsgNoLookups=true"
+      ];
+    };
+    "1.18.1" = fetchJar {
+      pname = "server";
+      version = "1.18.1";
+      url = "https://launcher.mojang.com/v1/objects/125e5adf40c659fd3bce3e66e67a16bb49ecc1b9/server.jar";
+      sha256 = "1pyvym6xzjb1siizzj4ma7lpb05qhgxnzps8lmlbk00lv0515kgb";
+    };
+    "1.18.2" = fetchJar {
+      pname = "server";
+      version = "1.18.2";
+      url = "https://launcher.mojang.com/v1/objects/c8f83c5655308435b3dcf03c06d9fe8740a77469/server.jar";
+      sha256 = "0hx330bnadixph44sip0h5h986m11qxbdba6lbgwz4da6lg9vgjp";
+    };
+    "1.19" = fetchJar {
+      pname = "server";
+      version = "1.19";
+      url = "https://launcher.mojang.com/v1/objects/e00c4052dac1d59a1188b2aa9d5a87113aaf1122/server.jar";
+      sha256 = "1cnjrqr2vn8gppd1y1lcdrc46fd7m1b3zl28zpbw72fgy1bd1vyy";
+    };
+    "1.19.1" = fetchJar {
+      pname = "server";
+      version = "1.19.1";
+      url = "https://piston-data.mojang.com/v1/objects/8399e1211e95faa421c1507b322dbeae86d604df/server.jar";
+      sha256 = "0jnlb5z8a7qi6p6bbwnmdl77b8kq83ryfdp58dhx8kg2hf6lbfx8";
+    };
+    "1.19.2" = fetchJar {
+      pname = "server";
+      version = "1.19.2";
+      url = "https://piston-data.mojang.com/v1/objects/f69c284232d7c7580bd89a5a4931c3581eae1378/server.jar";
+      sha256 = "15jdxh5zvsgvvk9hnv47swgjfg8fr653g6nx99q1rxpmkq32frxj";
+    };
+    "1.19.3" = fetchJar {
+      pname = "server";
+      version = "1.19.3";
+      url = "https://piston-data.mojang.com/v1/objects/c9df48efed58511cdd0213c56b9013a7b5c9ac1f/server.jar";
+      sha256 = "06qykz3nq7qmfw4phs3wvq3nk28clg8s3qrs37856aai8b8kmgaf";
+    };
+    # Starting with 1.19.4 we could use --pidFile for systemd's PIDFile=, but as
+    # the service doesn't fork, there seems to be no point.
+    "1.19.4" = fetchJar {
+      pname = "server";
+      version = "1.19.4";
+      url = "https://piston-data.mojang.com/v1/objects/8f3112a1049751cc472ec13e397eade5336ca7ae/server.jar";
+      sha256 = "0lrzpqd6zjvqh9g2byicgh66n43z0hwzp863r22ifx2hll6s2955";
+    };
+    # https://feedback.minecraft.net/hc/en-us/articles/16499677456781-Minecraft-Java-Edition-1-20-Trails-Tales
+    "1.20" = fetchJar {
+      name = "server";
+      version = "1.20";
+      url = "https://piston-data.mojang.com/v1/objects/15c777e2cfe0556eef19aab534b186c0c6f277e1/server.jar";
+      sha256 = "0sym07vqrlbhyxxhlpz73ls0jh0g9qcl4plaa1scx0n1rr1cahgz";
+    };
+    # https://www.minecraft.net/en-us/article/minecraft--java-edition-1-20-1
+    "1.20.1" = fetchJar {
+      pname = "server";
+      version = "1.20.1";
+      url = "https://piston-data.mojang.com/v1/objects/84194a2f286ef7c14ed7ce0090dba59902951553/server.jar";
+      sha256 = "1q3r3c95vkai477r3gsmf2p0pmyl4zfn0qwl8y0y60m1qnfkmxrs";
+    };
+    # https://www.minecraft.net/en-us/article/minecraft-java-edition-1-20-2
+    "1.20.2" = fetchJar {
+      pname = "server";
+      version = "1.20.2";
+      url = "https://piston-data.mojang.com/v1/objects/5b868151bd02b41319f54c8d4061b8cae84e665c/server.jar";
+      sha256 = "1s7ag1p8v0vyzc6a8mjkd3rcf065hjb4avqa3zj4dbb9hn1y9bhx";
+    };
+    # https://www.minecraft.net/en-us/article/minecraft-java-edition-1-20-3
+    "1.20.3" = fetchJar {
+      pname = "server";
+      version = "1.20.3";
+      url = "https://piston-data.mojang.com/v1/objects/4fb536bfd4a83d61cdbaf684b8d311e66e7d4c49/server.jar";
+      sha256 = "1blb2cp1nlm0yr7yjhazj33g0hjlgfawx2v7y16h70pijfz8kv9n";
+    };
+    # https://www.minecraft.net/en-us/article/minecraft-java-edition-1-20-4
+    "1.20.4" = fetchJar {
+      pname = "server";
+      version = "1.20.4";
+      url = "https://piston-data.mojang.com/v1/objects/8dd1a28015f51b1803213892b50b7b4fc76e594d/server.jar";
+      sha256 = "0qykf9a3nacklqsyb30kg9m79nw462la6rf92gsdssdakprscgy0";
+    };
+  };
+
+  #
+  # mods directory for fabric
+  #
+  makeModFolder = name: mods:
+    pkgs.runCommand "${name}-fabric-mod-folder" { } (
+      ''
+        mkdir -p "$out"
+      '' + lib.concatMapStrings
+        (mod: ''
+          test -f "${mod}" || {
+              printf 'Not a regular file: %s\n' "${mod}" >&2
+              exit 1
+          }
+          ln -s "${mod}" "$out/${storePathName mod}"
+        '')
+        mods
+    );
+
+  #
+  # Create a server.properties file
+  #
+  propertyValue = v:
+    if builtins.isBool v
+    then lib.boolToString v
+    else toString v;
+
+  serverPropertiesFile = name: instanceCfg:
+    let
+      serverProperties' =
+        builtins.removeAttrs instanceCfg.serverProperties [
+          "rcon.password"
+        ] // {
+          enable-rcon = true;
+        };
+    in
+    pkgs.writeText "${name}-server.properties" (''
+      # created by minecraft-fabric.nix
+    '' + lib.concatStrings (lib.mapAttrsToList
+      (key: value: ''
+        ${key}=${propertyValue value}
+      '')
+      serverProperties'));
+
+  #
+  # Create JSON β€œstate” files
+  #
+  writeJson = name: data: pkgs.writeText "${name}.json" (builtins.toJSON data);
+
+  toWhitelist = name: uuid: { inherit name uuid; };
+
+  whitelistFile = name: instanceCfg:
+    writeJson "${name}-whitelist" (
+      lib.mapAttrsToList toWhitelist instanceCfg.whitelist
+    );
+
+  opsFile = name: instanceCfg:
+    writeJson "${name}-ops" (
+      lib.mapAttrsToList
+        (name: value:
+          toWhitelist name value // {
+            level = 4;
+            bypassesPlayerLimit = true;
+          }
+        )
+        instanceCfg.ops
+    );
+
+  #
+  # Service start and stop scripts
+  #
+  stopScript = name: instanceCfg:
+    pkgs.writeShellScript "minecraft-fabric-${name}-stop" ''
+      set -eu
+
+      # Before shutting down, display the diff between prescribed and used
+      # server.properties file for debugging purposes; filter out credential
+      actualProperties="''${RUNTIME_DIRECTORY}/server.properties"
+      sort "$actualProperties" | ${bins.sponge} "$actualProperties"
+      ( ${bins.diff} -u "${serverPropertiesFile name instanceCfg}" \
+          "$actualProperties" \
+          || true ) | grep -v rcon.password
+
+      export MCRCON_HOST=localhost
+      export MCRCON_PORT=${lib.escapeShellArg instanceCfg.serverProperties."rcon.port"}
+      # Unfortunately, mcrcon can't read the password from a file
+      export MCRCON_PASS="$(cat "''${CREDENTIALS_DIRECTORY}/rcon-password")"
+
+      # Send stop request
+      "${bins.mcrcon}" 'say Server is stopping' save-all stop
+
+      # Wait for service to come down (systemd SIGTERMs right after ExecStop)
+      "${bins.flock}" "''${RUNTIME_DIRECTORY}" true
+    '';
+
+  startScript = name: instanceCfg:
+    let
+      serverJar = serverJars.${instanceCfg.version} or
+        (throw "Don't have server.jar for Minecraft Server ${instanceCfg.version}");
+
+    in
+
+    pkgs.writeShellScript "minecraft-fabric-${name}-start" ''
+      set -eu
+
+      cd "''${RUNTIME_DIRECTORY}"
+
+      copyFromStore() {
+          install -m600 "$1" "$2"
+      }
+
+      # Check if world is available
+      if test ! -d "${instanceCfg.world}"; then
+          echo "Could not find world, generating new one" >&2
+          mkdir -p "${instanceCfg.world}"
+      fi
+
+      # Put required files into place
+      echo eula=true > eula.txt
+      ln -s "${instanceCfg.world}" "${instanceCfg.level-name or "world"}"
+      copyFromStore "${serverJar}" server.jar
+      copyFromStore "${whitelistFile name instanceCfg}" whitelist.json
+      copyFromStore "${opsFile name instanceCfg}" ops.json
+      ln -s "${makeModFolder name instanceCfg.mods}" mods
+
+      # Create config and set password from credentials (echo hopefully doesn't leak)
+      copyFromStore "${serverPropertiesFile name instanceCfg}" server.properties
+      echo "rcon.password=$(cat "$CREDENTIALS_DIRECTORY/rcon-password")" >> server.properties
+
+      # Build patched jar
+      "${bins.java}" -jar "${fabricInstallerJar}" \
+          server -mcversion "${instanceCfg.version}"
+
+      # Lock is held as long as the server is running, so that we can wait for
+      # the actual shutdown in the stop script without relying on $MAINPID.
+      exec "${bins.flock}" "''${RUNTIME_DIRECTORY}" \
+          "${bins.java}" \
+          ${lib.escapeShellArgs (serverJar.baseJvmOpts or [ ] ++ instanceCfg.jvmOpts)} \
+          -jar fabric-server-launch.jar nogui
+    '';
+
+  #
+  # Option types
+  #
+  impurePath = lib.types.path // {
+    name = "impurePath";
+    check = x:
+      lib.types.path.check x
+        && !(builtins.isPath x)
+        && !(lib.hasPrefix builtins.storeDir (toString x));
+  };
+
+
+  instanceType = lib.types.submodule {
+    options = {
+      enable = lib.mkEnableOption "Minecraft server instance with the fabric mod loader";
+
+      version = lib.mkOption {
+        type = lib.types.str;
+        description = "Minecraft Server version to use.";
+        example = "1.16.5";
+      };
+
+      mods = lib.mkOption {
+        type = with lib.types; listOf package;
+        description = "List of fabric mod JARs to load.";
+        default = [ ];
+      };
+
+      world = lib.mkOption {
+        type = impurePath;
+        description = "Path to the Minecraft world folder to use.";
+        example = "/var/minecraft/world";
+      };
+
+      jvmOpts = lib.mkOption {
+        type = with lib.types; listOf str;
+        default = [ ];
+        example = [
+          "-Xmx2048M"
+          "-Xms2048M"
+        ];
+        description = ''
+          Options to pass to
+          <citerefentry>
+            <refentrytitle>java</refentrytitle>
+            <manvolnum>1</manvolnum>
+          </citerefentry>
+          in order to tweak the runtime of the JVM.
+        '';
+      };
+
+      user = lib.mkOption {
+        type = lib.types.str;
+        default = "minecraft";
+        description = ''
+          Name of an existing user to run the server as. Needs to have write
+          access to the specified world.
+        '';
+      };
+
+      group = lib.mkOption {
+        type = lib.types.str;
+        default = "users";
+        description = ''
+          Name of an existing group to run the server under.
+        '';
+      };
+
+      rconPasswordFile = lib.mkOption {
+        type = impurePath;
+        description = ''
+          File (outised the store) that stores the password to use for Minecraft's
+          RCON interface.
+        '';
+        example = "/var/secrets/minecraft-rcon";
+      };
+
+      whitelist = lib.mkOption {
+        type = with lib.types; attrsOf str;
+        description = ''
+          Attribute set mapping whitelisted user names to their user ids.
+        '';
+        example = {
+          sternenseemann = "d8e48069-1905-4886-a5da-a4ee917ee254";
+        };
+      };
+
+      ops = lib.mkOption {
+        type = with lib.types; attrsOf str;
+        description = ''
+          Attribute set mapping op-ed user names to their user ids.
+          Setting permission levels is not possible at the moment,
+          set to 4 by default.
+        '';
+        example = {
+          sternenseemann = "d8e48069-1905-4886-a5da-a4ee917ee254";
+        };
+      };
+
+      serverProperties = lib.mkOption {
+        type = lib.types.submodule {
+          freeformType = lib.types.attrs;
+
+          # Only options the module needs to access are declared explicitly
+          options = {
+            server-port = lib.mkOption {
+              type = lib.types.port;
+              default = 25565;
+              description = ''
+                Port to listen on.
+              '';
+            };
+
+            "rcon.port" = lib.mkOption {
+              type = lib.types.port;
+              default = 25575;
+              description = ''
+                Port to use for the RCON control mechanism.
+              '';
+            };
+          };
+        };
+      };
+    };
+  };
+
+  cfg = config.services.minecraft-fabric-server;
+
+  serverPorts = lib.mapAttrsToList
+    (_: instanceCfg:
+      instanceCfg.serverProperties.server-port
+    )
+    cfg;
+
+  rconPorts = lib.mapAttrsToList
+    (_: instanceCfg:
+      instanceCfg.serverProperties."rcon.port"
+    )
+    cfg;
+in
+
+{
+  options = {
+    services.minecraft-fabric-server = lib.mkOption {
+      type = with lib.types; attrsOf instanceType;
+      default = { };
+      description = "Minecraft server instances with the fabric mod loader";
+    };
+  };
+
+  config = {
+    assertions = [
+      {
+        assertion = builtins.all (instance: !instance.enable) (builtins.attrValues cfg)
+          || pkgs.config.allowUnfreeRedistributable or false
+          || pkgs.config.allowUnfree or false;
+        message = lib.concatStringsSep " " [
+          "You need to allow unfree software for minecraft,"
+          "as you'll implicitly agree to Mojang's EULA."
+        ];
+      }
+      {
+        assertion =
+          let
+            allPorts = serverPorts ++ rconPorts;
+          in
+          lib.unique allPorts == allPorts;
+        message = "All assigned ports need to be unique.";
+      }
+    ];
+
+    systemd.services = lib.mapAttrs'
+      (name: instanceCfg:
+        {
+          name = "minecraft-fabric-${name}";
+          value = {
+            description = "Minecraft server ${name} with the fabric mod loader";
+            wantedBy = [ "multi-user.target" ];
+            after = [ "network.target" ];
+            inherit (instanceCfg) enable;
+
+            serviceConfig = {
+              Type = "simple";
+              User = instanceCfg.user;
+              Group = instanceCfg.group;
+              ExecStart = startScript name instanceCfg;
+              ExecStop = stopScript name instanceCfg;
+              RuntimeDirectory = "minecraft-fabric-${name}";
+              LoadCredential = "rcon-password:${instanceCfg.rconPasswordFile}";
+              RestartSec = "40s";
+            };
+          };
+        }
+      )
+      cfg;
+
+    networking.firewall = {
+      allowedTCPPorts = serverPorts;
+      allowedUDPPorts = serverPorts;
+    };
+  };
+}
diff --git a/users/sterni/nix/build/buildGopherHole/default.nix b/users/sterni/nix/build/buildGopherHole/default.nix
new file mode 100644
index 0000000000..eec13a8654
--- /dev/null
+++ b/users/sterni/nix/build/buildGopherHole/default.nix
@@ -0,0 +1,109 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  inherit (pkgs)
+    runCommand
+    writeText
+    ;
+
+  inherit (depot.users.sterni.nix.build)
+    buildGopherHole
+    ;
+
+  fileTypes = {
+    # RFC1436
+    text = "0";
+    menu = "1";
+    cso = "2";
+    error = "3";
+    binhex = "4";
+    dos = "5";
+    uuencoded = "6";
+    index-server = "7";
+    telnet = "8";
+    binary = "9";
+    mirror = "+";
+    gif = "g";
+    image = "I";
+    tn3270 = "T";
+    # non-standard
+    info = "i";
+    html = "h";
+  };
+
+  buildFile = { file, name, fileType ? fileTypes.text }:
+    runCommand name
+      {
+        passthru = {
+          # respect the file type the file derivation passes
+          # through. otherwise use explicitly set type or
+          # default value.
+          fileType = file.fileType or fileType;
+        };
+      } ''
+      ln -s ${file} "$out"
+    '';
+
+  buildGopherMap = dir:
+    let
+      /* strings constitute an info line or an empty line
+         if their length is zero. sets that contain a menu
+         value have that added to the gophermap as-is.
+
+         all other entries should be a set which can be built using
+         buildGopherHole and is linked by their name. The resulting
+         derivation is expected to passthru a fileType containing the
+         gopher file type char of themselves.
+      */
+      gopherMapLine = e:
+        if builtins.isString e
+        then e
+        else if e ? menu
+        then e.menu
+        else
+          let
+            drv = buildGopherHole e;
+            title = e.title or e.name;
+          in
+          "${drv.fileType}${title}\t${drv.name}";
+    in
+    writeText ".gophermap" (lib.concatMapStringsSep "\n" gopherMapLine dir);
+
+  buildDir =
+    { dir, name, ... }:
+
+    let
+      # filter all entries out that have to be symlinked:
+      # sets with the file or dir attribute
+      drvOnly = builtins.map buildGopherHole (builtins.filter
+        (x: !(builtins.isString x) && (x ? dir || x ? file))
+        dir);
+      gopherMap = buildGopherMap dir;
+    in
+    runCommand name
+      {
+        passthru = {
+          fileType = fileTypes.dir;
+        };
+      }
+      (''
+        mkdir -p "$out"
+        ln -s "${gopherMap}" "$out/.gophermap"
+      '' + lib.concatMapStrings
+        (drv: ''
+          ln -s "${drv}" "$out/${drv.name}"
+        '')
+        drvOnly);
+in
+
+{
+  # Dispatch into different file / dir handling code
+  # which is mutually recursive with this function.
+  __functor = _: args:
+    if args ? file then buildFile args
+    else if args ? dir then buildDir args
+    else builtins.throw "Unrecognized gopher hole item type: "
+      + lib.generators.toPretty { } args;
+
+  inherit fileTypes;
+}
diff --git a/users/sterni/nix/char/default.nix b/users/sterni/nix/char/default.nix
index aacfc9dcbe..9c6ce2fb25 100644
--- a/users/sterni/nix/char/default.nix
+++ b/users/sterni/nix/char/default.nix
@@ -53,17 +53,20 @@ let
   asciiAlpha = c:
     let
       v = ord c;
-    in (v >= 65 && v <= 90)
+    in
+    (v >= 65 && v <= 90)
     || (v >= 97 && v <= 122);
 
   asciiNum = c:
     let
       v = ord c;
-    in v >= 48 && v <= 57;
+    in
+    v >= 48 && v <= 57;
 
   asciiAlphaNum = c: asciiAlpha c || asciiNum c;
 
-in {
+in
+{
   inherit
     allChars
     char
@@ -78,18 +81,19 @@ in {
   # originally I generated a nix file containing a list of
   # characters, but infinisil uses a better way which I adapt
   # which is using builtins.readFile instead of import.
-  __generateAllChars = pkgs.runCommandCC "generate-all-chars" {
-    source = ''
-      #include <stdio.h>
-
-      int main(void) {
-        for(int i = 1; i <= 0xff; i++) {
-          putchar(i);
+  __generateAllChars = pkgs.runCommandCC "generate-all-chars"
+    {
+      source = ''
+        #include <stdio.h>
+
+        int main(void) {
+          for(int i = 1; i <= 0xff; i++) {
+            putchar(i);
+          }
         }
-      }
-    '';
-    passAsFile = [ "source" ];
-  } ''
+      '';
+      passAsFile = [ "source" ];
+    } ''
     $CC -o "$out" -x c "$sourcePath"
   '';
 }
diff --git a/users/sterni/nix/char/tests/default.nix b/users/sterni/nix/char/tests/default.nix
index 49b439adbb..cb17b74c57 100644
--- a/users/sterni/nix/char/tests/default.nix
+++ b/users/sterni/nix/char/tests/default.nix
@@ -10,7 +10,7 @@ let
   inherit (depot.users.sterni.nix)
     char
     string
-    int
+    num
     fun
     ;
 
@@ -18,14 +18,14 @@ let
 
   testAllCharConversion = it "tests conversion of all chars" [
     (assertEq "char.chr converts to char.allChars"
-      (builtins.genList (fun.rl char.chr (int.add 1)) 255)
+      (builtins.genList (fun.rl char.chr (num.add 1)) 255)
       charList)
     (assertEq "char.ord converts from char.allChars"
-      (builtins.genList (int.add 1) 255)
+      (builtins.genList (num.add 1) 255)
       (builtins.map char.ord charList))
   ];
 
 in
-  runTestsuite "char" [
-    testAllCharConversion
-  ]
+runTestsuite "char" [
+  testAllCharConversion
+]
diff --git a/users/sterni/nix/float/default.nix b/users/sterni/nix/float/default.nix
new file mode 100644
index 0000000000..ecb6465c88
--- /dev/null
+++ b/users/sterni/nix/float/default.nix
@@ -0,0 +1,23 @@
+{ depot, ... }:
+
+let
+  inherit (depot.users.sterni.nix)
+    num
+    ;
+in
+
+rec {
+  # In C++ Nix, the required builtins have been added in version 2.4
+  ceil = builtins.ceil or (throw "Nix implementation is missing builtins.ceil");
+  floor = builtins.floor or (throw "Nix implementation is missing builtins.floor");
+
+  truncate = f: if f >= 0 then floor f else ceil f;
+  round = f:
+    let
+      s = num.sign f;
+      a = s * f;
+    in
+    s * (if a >= floor a + 0.5 then ceil a else floor a);
+
+  intToFloat = i: i * 1.0;
+}
diff --git a/users/sterni/nix/float/tests/default.nix b/users/sterni/nix/float/tests/default.nix
new file mode 100644
index 0000000000..75e2a1bfa0
--- /dev/null
+++ b/users/sterni/nix/float/tests/default.nix
@@ -0,0 +1,49 @@
+{ depot, lib, ... }:
+
+let
+
+  inherit (depot.nix.runTestsuite)
+    runTestsuite
+    it
+    assertEq
+    ;
+
+  inherit (depot.users.sterni.nix)
+    float
+    ;
+
+  testsBuiltins = it "tests builtin operations" [
+    (assertEq "ceil pos" (float.ceil 1.5) 2)
+    (assertEq "ceil neg" (float.ceil (-1.5)) (-1))
+    (assertEq "floor pos" (float.floor 1.5) 1)
+    (assertEq "floor neg" (float.floor (-1.5)) (-2))
+  ];
+
+  testsConversionFrom = it "tests integer to float conversion" [
+    (assertEq "float.intToFloat is identity for floats" (float.intToFloat 1.3) 1.3)
+    (assertEq "float.intToFloat converts ints"
+      (builtins.all
+        (val: builtins.isFloat val)
+        (builtins.map float.intToFloat (builtins.genList (i: i - 500) 1000)))
+      true)
+  ];
+
+  exampleFloats = [ 0.5 0.45 0.3 0.1 200 203.457847 204.65547 (-1.5) (-2) (-1.3) (-0.45) ];
+  testsConversionTo = it "tests float to integer conversion" [
+    (assertEq "round"
+      (builtins.map float.round exampleFloats)
+      [ 1 0 0 0 200 203 205 (-2) (-2) (-1) 0 ])
+    (assertEq "truncate towards zero"
+      (builtins.map float.truncate exampleFloats)
+      [ 0 0 0 0 200 203 204 (-1) (-2) (-1) 0 ])
+  ];
+in
+
+runTestsuite "nix.num" ([
+  testsConversionFrom
+]
+  # Skip for e.g. C++ Nix < 2.4
+++ lib.optionals (builtins ? ceil && builtins ? floor) [
+  testsConversionTo
+  testsBuiltins
+])
diff --git a/users/sterni/nix/flow/default.nix b/users/sterni/nix/flow/default.nix
index b5783bd86d..4bef0abb91 100644
--- a/users/sterni/nix/flow/default.nix
+++ b/users/sterni/nix/flow/default.nix
@@ -68,13 +68,14 @@ let
           then s x
           else x == s;
       in
-        if b
-        then builtins.elemAt c 1
-        else switch x (builtins.tail conds);
+      if b
+      then builtins.elemAt c 1
+      else switch x (builtins.tail conds);
 
 
 
-in {
+in
+{
   inherit
     cond
     switch
diff --git a/users/sterni/nix/flow/tests/default.nix b/users/sterni/nix/flow/tests/default.nix
index 54cea01858..9f974a61c7 100644
--- a/users/sterni/nix/flow/tests/default.nix
+++ b/users/sterni/nix/flow/tests/default.nix
@@ -21,7 +21,7 @@ let
       (cond [ [ true 1 2 ] [ false 1 ] ]))
     (assertEq "last is true" "last"
       (cond [
-        [ false dontEval]
+        [ false dontEval ]
         [ false dontEval ]
         [ true "last" ]
       ]))
@@ -34,6 +34,6 @@ let
   ];
 
 in
-  runTestsuite "nix.flow" [
-    testCond
-  ]
+runTestsuite "nix.flow" [
+  testCond
+]
diff --git a/users/sterni/nix/fun/default.nix b/users/sterni/nix/fun/default.nix
index 6b3541ed4c..824cebfed2 100644
--- a/users/sterni/nix/fun/default.nix
+++ b/users/sterni/nix/fun/default.nix
@@ -39,6 +39,198 @@ let
     builtins.match ".*<attrspat ellipsis=\"1\">.*"
       (builtins.toXML f) != null;
 
+  /* Return the number of arguments the given function accepts or 0 if the value
+     is not a function.
+
+     Example:
+
+       argCount argCount
+       => 1
+
+       argCount builtins.add
+       => 2
+
+       argCount pkgs.stdenv.mkDerivation
+       => 1
+  */
+  argCount = f:
+    let
+      # N.B. since we are only interested if the result of calling is a function
+      # as opposed to a normal value or evaluation failure, we never need to
+      # check success, as value will be false (i.e. not a function) in the
+      # failure case.
+      called = builtins.tryEval (
+        f (builtins.throw "You should never see this error message")
+      );
+    in
+    if !(builtins.isFunction f || builtins.isFunction (f.__functor or null))
+    then 0
+    else 1 + argCount called.value;
+
+  /* Call a given function with a given list of arguments.
+
+     Example:
+
+       apply builtins.sub [ 20 10 ]
+       => 10
+  */
+  apply = f: args:
+    builtins.foldl' (f: x: f x) f args;
+
+  # TODO(sterni): think of a better name for unapply
+  /* Collect n arguments into a list and pass them to the given function.
+     Allows calling a function that expects a list by feeding it the list
+     elements individually as function arguments - the limitation is
+     that the list must be of constant length.
+
+     This is mainly useful for functions that wrap other, arbitrary functions
+     in conjunction with argCount and apply, since lists of arguments are
+     easier to deal with usually.
+
+     Example:
+
+       (unapply 3 lib.id) 1 2 3
+       => [ 1 2 3 ]
+
+       (unapply 5 lib.reverse) 1 2 null 4 5
+       => [ 5 4 null 2 1 ]
+
+       # unapply and apply compose the identity relation together
+
+       unapply (argCount f) (apply f)
+       # is equivalent to f (if the function has a constant number of arguments)
+
+       (unapply 2 (apply builtins.sub)) 20 10
+       => 10
+  */
+  unapply =
+    let
+      unapply' = acc: n: f: x:
+        if n == 1
+        then f (acc ++ [ x ])
+        else unapply' (acc ++ [ x ]) (n - 1) f;
+    in
+    unapply' [ ];
+
+  /* Optimize a tail recursive Nix function by intercepting the recursive
+     function application and expressing it in terms of builtins.genericClosure
+     instead. The main benefit of this optimization is that even a naively
+     written recursive algorithm won't overflow the stack.
+
+     For this to work the following things prerequisites are necessary:
+
+     - The passed function needs to be a fix point for its self reference,
+       i. e. the argument to tailCallOpt needs to be of the form
+       `self: # function body that uses self to call itself`.
+       This is because tailCallOpt needs to manipulate the call to self
+       which otherwise wouldn't be possible due to Nix's lexical scoping.
+
+     - The passed function may only call itself as a tail call, all other
+       forms of recursions will fail evaluation.
+
+     This function was mainly written to prove that builtins.genericClosure
+     can be used to express any (tail) recursive algorithm. It can be used
+     to avoid stack overflows for deeply recursive, but naively written
+     functions (in the context of Nix this mainly means using recursion
+     instead of (ab)using more performant and less limited builtins).
+     A better alternative to using this function is probably translating
+     the algorithm to builtins.genericClosure manually. Also note that
+     using tailCallOpt doesn't mean that the stack won't ever overflow:
+     Data structures, especially lazy ones, can still cause all the
+     available stack space to be consumed.
+
+     The optimization also only concerns avoiding stack overflows,
+     tailCallOpt will make functions slower if anything.
+
+     Type: (F -> F) -> F where F is any tail recursive function.
+
+     Example:
+
+     let
+       label' = self: acc: n:
+         if n == 0
+         then "This is " + acc + "cursed."
+         else self (acc + "very ") (n - 1);
+
+       # Equivalent to a naive recursive implementation in Nix
+       label = (lib.fix label') "";
+
+       labelOpt = (tailCallOpt label') "";
+     in
+
+     label 5
+     => "This is very very very very very cursed."
+
+     labelOpt 5
+     => "This is very very very very very cursed."
+
+     label 10000
+     => error: stack overflow (possible infinite recursion)
+
+     labelOpt 10000
+     => "This is very very very very very very very very very…
+  */
+  tailCallOpt = f:
+    let
+      argc = argCount (lib.fix f);
+
+      # This function simulates being f for f's self reference. Instead of
+      # recursing, it will just return the arguments received as a specially
+      # tagged set, so the recursion step can be performed later.
+      fakef = unapply argc (args: {
+        __tailCall = true;
+        inherit args;
+      });
+      # Pass fakef to f so that it'll be called instead of recursing, ensuring
+      # only one recursion step is performed at a time.
+      encodedf = f fakef;
+
+      opt = args:
+        let
+          steps = builtins.genericClosure {
+            # This is how we encode a (tail) call: A set with final == false
+            # and the list of arguments to pass to be found in args.
+            startSet = [
+              {
+                key = 0;
+                final = false;
+                inherit args;
+              }
+            ];
+
+            operator =
+              { key, final, ... }@state:
+              let
+                # Plumbing to make genericClosure happy
+                newId = {
+                  key = key + 1;
+                };
+
+                # Perform recursion step
+                call = apply encodedf state.args;
+
+                # If call encodes a new call, return the new encoded call,
+                # otherwise signal that we're done.
+                newState =
+                  if builtins.isAttrs call && call.__tailCall or false
+                  then newId // {
+                    final = false;
+                    inherit (call) args;
+                  } else newId // {
+                    final = true;
+                    value = call;
+                  };
+              in
+
+              if final
+              then [ ] # end condition for genericClosure
+              else [ newState ];
+          };
+        in
+        # The returned list contains intermediate steps we ignore.
+        (builtins.head (builtins.filter (x: x.final) steps)).value;
+    in
+    unapply argc opt;
 in
 
 {
@@ -55,5 +247,9 @@ in
     lr
     lrs
     hasEllipsis
+    argCount
+    tailCallOpt
+    apply
+    unapply
     ;
 }
diff --git a/users/sterni/nix/fun/tests/default.nix b/users/sterni/nix/fun/tests/default.nix
index 6492554306..6b1e6fcc7b 100644
--- a/users/sterni/nix/fun/tests/default.nix
+++ b/users/sterni/nix/fun/tests/default.nix
@@ -7,6 +7,8 @@ let
     assertEq
     ;
 
+  inherit (depot.nix) escapeExecline;
+
   inherit (depot.users.sterni.nix)
     fun
     ;
@@ -23,7 +25,58 @@ let
     (assertEq "Ellipsis" true
       (fun.hasEllipsis ({ depot, pkgs, ... }: 42)))
   ];
+
+  argCountTests = it "checks fun.argCount" [
+    (assertEq "builtins.sub has two arguments" 2
+      (fun.argCount builtins.sub))
+    (assertEq "fun.argCount has one argument" 1
+      (fun.argCount fun.argCount))
+    (assertEq "runTestsuite has two arguments" 2
+      (fun.argCount runTestsuite))
+  ];
+
+  applyTests = it "checks that fun.apply is equivalent to calling" [
+    (assertEq "fun.apply builtins.sub" (builtins.sub 23 42)
+      (fun.apply builtins.sub [ 23 42 ]))
+    (assertEq "fun.apply escapeExecline" (escapeExecline [ "foo" [ "bar" ] ])
+      (fun.apply escapeExecline [ [ "foo" [ "bar" ] ] ]))
+  ];
+
+  unapplyTests = it "checks fun.unapply" [
+    (assertEq "fun.unapply 3 accepts 3 args" 3
+      (fun.argCount (fun.unapply 3 fun.id)))
+    (assertEq "fun.unapply 73 accepts 73 args" 73
+      (fun.argCount (fun.unapply 73 fun.id)))
+    (assertEq "fun.unapply 1 accepts 73 args" 1
+      (fun.argCount (fun.unapply 1 fun.id)))
+    (assertEq "fun.unapply collects arguments correctly"
+      (fun.unapply 5 fun.id 1 2 3 4 5)
+      [ 1 2 3 4 5 ])
+    (assertEq "fun.unapply calls the given function correctly" 1
+      (fun.unapply 1 builtins.head 1))
+  ];
+
+  fac' = self: acc: n: if n == 0 then acc else self (n * acc) (n - 1);
+
+  facPlain = fun.fix fac' 1;
+  facOpt = fun.tailCallOpt fac' 1;
+
+  tailCallOptTests = it "checks fun.tailCallOpt" [
+    (assertEq "optimized and unoptimized factorial have the same base case"
+      (facPlain 0)
+      (facOpt 0))
+    (assertEq "optimized and unoptimized factorial have same value for 1"
+      (facPlain 1)
+      (facOpt 1))
+    (assertEq "optimized and unoptimized factorial have same value for 100"
+      (facPlain 100)
+      (facOpt 100))
+  ];
 in
-  runTestsuite "nix.fun" [
-    hasEllipsisTests
-  ]
+runTestsuite "nix.fun" [
+  hasEllipsisTests
+  argCountTests
+  applyTests
+  unapplyTests
+  tailCallOptTests
+]
diff --git a/users/sterni/nix/html/default.nix b/users/sterni/nix/html/default.nix
index 2498d832aa..d25a7ab8da 100644
--- a/users/sterni/nix/html/default.nix
+++ b/users/sterni/nix/html/default.nix
@@ -20,7 +20,7 @@ let
      => "&lt;hello&gt;"
   */
   escapeMinimal = builtins.replaceStrings
-    [ "<"    ">"    "&"     "\""     "'"      ]
+    [ "<" ">" "&" "\"" "'" ]
     [ "&lt;" "&gt;" "&amp;" "&quot;" "&#039;" ];
 
   /* Return a string with a correctly rendered tag of the given name,
@@ -87,18 +87,20 @@ let
   renderTag = tag: attrs: content:
     let
       attrs' = builtins.concatStringsSep "" (
-        builtins.map (n:
-          " ${escapeMinimal n}=\"${escapeMinimal (toString attrs.${n})}\""
-        ) (builtins.attrNames attrs)
+        builtins.map
+          (n:
+            " ${escapeMinimal n}=\"${escapeMinimal (toString attrs.${n})}\""
+          )
+          (builtins.attrNames attrs)
       );
       content' =
         if builtins.isList content
         then builtins.concatStringsSep "" content
         else content;
     in
-      if content == null
-      then "<${tag}${attrs'}/>"
-      else "<${tag}${attrs'}>${content'}</${tag}>";
+    if content == null
+    then "<${tag}${attrs'}/>"
+    else "<${tag}${attrs'}>${content'}</${tag}>";
 
   /* Prepend "<!DOCTYPE html>" to a string.
 
@@ -111,7 +113,8 @@ let
   */
   withDoctype = doc: "<!DOCTYPE html>" + doc;
 
-in {
+in
+{
   inherit escapeMinimal renderTag withDoctype;
 
   __findFile = _: renderTag;
diff --git a/users/sterni/nix/html/tests/default.nix b/users/sterni/nix/html/tests/default.nix
index 8688b69371..ed520675c5 100644
--- a/users/sterni/nix/html/tests/default.nix
+++ b/users/sterni/nix/html/tests/default.nix
@@ -8,15 +8,17 @@ let
     ;
 
   exampleDocument = withDoctype (<html> { lang = "en"; } [
-    (<head> {} [
+    (<head> { } [
       (<meta> { charset = "utf-8"; } null)
-      (<title> {} "html.nix example document")
-      (<link> {
-        rel = "license";
-        href = "https://code.tvl.fyi/about/LICENSE";
-        type = "text/html";
-      } null)
-      (<style> {}  (esc ''
+      (<title> { } "html.nix example document")
+      (<link>
+        {
+          rel = "license";
+          href = "https://code.tvl.fyi/about/LICENSE";
+          type = "text/html";
+        }
+        null)
+      (<style> { } (esc ''
         hgroup h2 {
           font-weight: normal;
         }
@@ -26,39 +28,45 @@ let
         }
       ''))
     ])
-    (<body> {} [
-      (<main> {} [
-        (<hgroup> {} [
-          (<h1> {} (esc "html.nix"))
-          (<h2> {} [
-            (<em> {} "the")
+    (<body> { } [
+      (<main> { } [
+        (<hgroup> { } [
+          (<h1> { } (esc "html.nix"))
+          (<h2> { } [
+            (<em> { } "the")
             (esc " most cursed HTML DSL ever!")
           ])
         ])
-        (<dl> {} [
-          (<dt> {} [
+        (<dl> { } [
+          (<dt> { } [
             (esc "Q: Wait, it's all ")
-            (<a> {
-              href = "https://cl.tvl.fyi/q/hashtag:cursed";
-            } (esc "cursed"))
+            (<a>
+              {
+                href = "https://cl.tvl.fyi/q/hashtag:cursed";
+              }
+              (esc "cursed"))
             (esc " nix hacks?")
           ])
-          (<dd> {} (esc "A: Always has been. πŸ”«"))
-          (<dt> {} (esc "Q: Why does this work?"))
-          (<dd> {} [
+          (<dd> { } (esc "A: Always has been. πŸ”«"))
+          (<dt> { } (esc "Q: Why does this work?"))
+          (<dd> { } [
             (esc "Because nix ")
-            (<a> {
-              href = "https://github.com/NixOS/nix/blob/293220bed5a75efc963e33c183787e87e55e28d9/src/libexpr/parser.y#L410-L416";
-            } (esc "translates "))
-            (<a> {
-              href = "https://github.com/NixOS/nix/blob/293220bed5a75efc963e33c183787e87e55e28d9/src/libexpr/lexer.l#L100";
-            } (esc "SPATH tokens"))
+            (<a>
+              {
+                href = "https://github.com/NixOS/nix/blob/293220bed5a75efc963e33c183787e87e55e28d9/src/libexpr/parser.y#L410-L416";
+              }
+              (esc "translates "))
+            (<a>
+              {
+                href = "https://github.com/NixOS/nix/blob/293220bed5a75efc963e33c183787e87e55e28d9/src/libexpr/lexer.l#L100";
+              }
+              (esc "SPATH tokens"))
             (esc " like ")
-            (<code> {} (esc "<nixpkgs>"))
+            (<code> { } (esc "<nixpkgs>"))
             (esc " into calls to ")
-            (<code> {} (esc "__findFile"))
+            (<code> { } (esc "__findFile"))
             (esc " in the ")
-            (<em> {} (esc "current"))
+            (<em> { } (esc "current"))
             (esc " scope.")
           ])
         ])
@@ -67,7 +75,8 @@ let
   ]);
 in
 
-pkgs.runCommandNoCC "html.nix.html" {
+pkgs.runCommand "html.nix.html"
+{
   passAsFile = [ "exampleDocument" ];
   inherit exampleDocument;
   nativeBuildInputs = [ pkgs.html5validator ];
diff --git a/users/sterni/nix/int/default.nix b/users/sterni/nix/int/default.nix
index b315757127..8707445223 100644
--- a/users/sterni/nix/int/default.nix
+++ b/users/sterni/nix/int/default.nix
@@ -2,37 +2,28 @@
 
 let
 
-  # TODO(sterni): implement nix.float and figure out which of these
-  #               functions can be split out into a common nix.num
-  #               library.
-
   inherit (depot.users.sterni.nix)
     string
+    num
     ;
 
   inherit (builtins)
     bitOr
     bitAnd
     bitXor
-    mul
-    div
-    add
-    sub
     ;
 
-  abs = i: if i < 0 then -i else i;
-
   exp = base: pow:
     if pow > 0
     then base * (exp base (pow - 1))
     else if pow < 0
-    then 1.0 / exp base (abs pow)
+    then 1.0 / exp base (num.abs pow)
     else 1;
 
   bitShiftR = bit: count:
     if count == 0
     then bit
-    else div (bitShiftR bit (count - 1)) 2;
+    else (bitShiftR bit (count - 1)) / 2;
 
   bitShiftL = bit: count:
     if count == 0
@@ -47,12 +38,12 @@ let
         if i == 0
         then ""
         else go (bitShiftR i 4)
-           + string.charAt (bitAnd i 15) hexdigits;
+          + string.charAt (bitAnd i 15) hexdigits;
       sign = lib.optionalString (int < 0) "-";
     in
-      if int == 0
-      then "0"
-      else "${sign}${go (abs int)}";
+    if int == 0
+    then "0"
+    else "${sign}${go (num.abs int)}";
 
   fromHexMap = builtins.listToAttrs
     (lib.imap0 (i: c: { name = c; value = i; })
@@ -72,11 +63,12 @@ let
           val = v.val + (fromHexMap."${d}" * v.mul);
           mul = v.mul * 16;
         })
-        { val = 0; mul = 1; } digits;
+        { val = 0; mul = 1; }
+        digits;
     in
-      if negative
-      then -parsed.val
-      else parsed.val;
+    if negative
+    then -parsed.val
+    else parsed.val;
 
   # A nix integer is a 64bit signed integer
   maxBound = 9223372036854775807;
@@ -93,25 +85,24 @@ let
   odd = x: bitAnd x 1 == 1;
   even = x: bitAnd x 1 == 0;
 
-  # div and mod behave like quot and rem in Haskell,
-  # i. e. they truncate towards 0
-  mod = a: b: let res = a / b; in a - (res * b);
-
-  inRange = a: b: x: x >= a && x <= b;
+  quot' = builtins.div; # no typecheck
+  rem = a: b:
+    assert builtins.isInt a && builtins.isInt b;
+    let res = quot' a b; in a - (res * b);
+  quot = a: b:
+    assert builtins.isInt a && builtins.isInt b;
+    quot' a b;
 
-in {
+in
+{
   inherit
     maxBound
     minBound
-    abs
     exp
     odd
     even
-    add
-    sub
-    mul
-    div
-    mod
+    quot
+    rem
     bitShiftR
     bitShiftL
     bitOr
@@ -119,6 +110,5 @@ in {
     bitXor
     toHex
     fromHex
-    inRange
     ;
 }
diff --git a/users/sterni/nix/int/tests/default.nix b/users/sterni/nix/int/tests/default.nix
index fac45dd251..80bd05b6b5 100644
--- a/users/sterni/nix/int/tests/default.nix
+++ b/users/sterni/nix/int/tests/default.nix
@@ -15,9 +15,6 @@ let
     ;
 
   testBounds = it "checks minBound and maxBound" [
-    # this is gonna blow up in my face because
-    # integer overflow is undefined behavior in
-    # C++, so most likely anything could happen?
     (assertEq "maxBound is the maxBound" true
       (int.maxBound + 1 < int.maxBound))
     (assertEq "minBound is the minBound" true
@@ -31,22 +28,262 @@ let
   ];
 
   expectedBytes = [
-    "00" "01" "02" "03" "04" "05" "06" "07" "08" "09" "0A" "0B" "0C" "0D" "0E" "0F"
-    "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "1A" "1B" "1C" "1D" "1E" "1F"
-    "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" "2A" "2B" "2C" "2D" "2E" "2F"
-    "30" "31" "32" "33" "34" "35" "36" "37" "38" "39" "3A" "3B" "3C" "3D" "3E" "3F"
-    "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" "4A" "4B" "4C" "4D" "4E" "4F"
-    "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" "5A" "5B" "5C" "5D" "5E" "5F"
-    "60" "61" "62" "63" "64" "65" "66" "67" "68" "69" "6A" "6B" "6C" "6D" "6E" "6F"
-    "70" "71" "72" "73" "74" "75" "76" "77" "78" "79" "7A" "7B" "7C" "7D" "7E" "7F"
-    "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" "8A" "8B" "8C" "8D" "8E" "8F"
-    "90" "91" "92" "93" "94" "95" "96" "97" "98" "99" "9A" "9B" "9C" "9D" "9E" "9F"
-    "A0" "A1" "A2" "A3" "A4" "A5" "A6" "A7" "A8" "A9" "AA" "AB" "AC" "AD" "AE" "AF"
-    "B0" "B1" "B2" "B3" "B4" "B5" "B6" "B7" "B8" "B9" "BA" "BB" "BC" "BD" "BE" "BF"
-    "C0" "C1" "C2" "C3" "C4" "C5" "C6" "C7" "C8" "C9" "CA" "CB" "CC" "CD" "CE" "CF"
-    "D0" "D1" "D2" "D3" "D4" "D5" "D6" "D7" "D8" "D9" "DA" "DB" "DC" "DD" "DE" "DF"
-    "E0" "E1" "E2" "E3" "E4" "E5" "E6" "E7" "E8" "E9" "EA" "EB" "EC" "ED" "EE" "EF"
-    "F0" "F1" "F2" "F3" "F4" "F5" "F6" "F7" "F8" "F9" "FA" "FB" "FC" "FD" "FE" "FF"
+    "00"
+    "01"
+    "02"
+    "03"
+    "04"
+    "05"
+    "06"
+    "07"
+    "08"
+    "09"
+    "0A"
+    "0B"
+    "0C"
+    "0D"
+    "0E"
+    "0F"
+    "10"
+    "11"
+    "12"
+    "13"
+    "14"
+    "15"
+    "16"
+    "17"
+    "18"
+    "19"
+    "1A"
+    "1B"
+    "1C"
+    "1D"
+    "1E"
+    "1F"
+    "20"
+    "21"
+    "22"
+    "23"
+    "24"
+    "25"
+    "26"
+    "27"
+    "28"
+    "29"
+    "2A"
+    "2B"
+    "2C"
+    "2D"
+    "2E"
+    "2F"
+    "30"
+    "31"
+    "32"
+    "33"
+    "34"
+    "35"
+    "36"
+    "37"
+    "38"
+    "39"
+    "3A"
+    "3B"
+    "3C"
+    "3D"
+    "3E"
+    "3F"
+    "40"
+    "41"
+    "42"
+    "43"
+    "44"
+    "45"
+    "46"
+    "47"
+    "48"
+    "49"
+    "4A"
+    "4B"
+    "4C"
+    "4D"
+    "4E"
+    "4F"
+    "50"
+    "51"
+    "52"
+    "53"
+    "54"
+    "55"
+    "56"
+    "57"
+    "58"
+    "59"
+    "5A"
+    "5B"
+    "5C"
+    "5D"
+    "5E"
+    "5F"
+    "60"
+    "61"
+    "62"
+    "63"
+    "64"
+    "65"
+    "66"
+    "67"
+    "68"
+    "69"
+    "6A"
+    "6B"
+    "6C"
+    "6D"
+    "6E"
+    "6F"
+    "70"
+    "71"
+    "72"
+    "73"
+    "74"
+    "75"
+    "76"
+    "77"
+    "78"
+    "79"
+    "7A"
+    "7B"
+    "7C"
+    "7D"
+    "7E"
+    "7F"
+    "80"
+    "81"
+    "82"
+    "83"
+    "84"
+    "85"
+    "86"
+    "87"
+    "88"
+    "89"
+    "8A"
+    "8B"
+    "8C"
+    "8D"
+    "8E"
+    "8F"
+    "90"
+    "91"
+    "92"
+    "93"
+    "94"
+    "95"
+    "96"
+    "97"
+    "98"
+    "99"
+    "9A"
+    "9B"
+    "9C"
+    "9D"
+    "9E"
+    "9F"
+    "A0"
+    "A1"
+    "A2"
+    "A3"
+    "A4"
+    "A5"
+    "A6"
+    "A7"
+    "A8"
+    "A9"
+    "AA"
+    "AB"
+    "AC"
+    "AD"
+    "AE"
+    "AF"
+    "B0"
+    "B1"
+    "B2"
+    "B3"
+    "B4"
+    "B5"
+    "B6"
+    "B7"
+    "B8"
+    "B9"
+    "BA"
+    "BB"
+    "BC"
+    "BD"
+    "BE"
+    "BF"
+    "C0"
+    "C1"
+    "C2"
+    "C3"
+    "C4"
+    "C5"
+    "C6"
+    "C7"
+    "C8"
+    "C9"
+    "CA"
+    "CB"
+    "CC"
+    "CD"
+    "CE"
+    "CF"
+    "D0"
+    "D1"
+    "D2"
+    "D3"
+    "D4"
+    "D5"
+    "D6"
+    "D7"
+    "D8"
+    "D9"
+    "DA"
+    "DB"
+    "DC"
+    "DD"
+    "DE"
+    "DF"
+    "E0"
+    "E1"
+    "E2"
+    "E3"
+    "E4"
+    "E5"
+    "E6"
+    "E7"
+    "E8"
+    "E9"
+    "EA"
+    "EB"
+    "EC"
+    "ED"
+    "EE"
+    "EF"
+    "F0"
+    "F1"
+    "F2"
+    "F3"
+    "F4"
+    "F5"
+    "F6"
+    "F7"
+    "F8"
+    "F9"
+    "FA"
+    "FB"
+    "FC"
+    "FD"
+    "FE"
+    "FF"
   ];
 
   hexByte = i: string.fit { width = 2; char = "0"; } (int.toHex i);
@@ -64,20 +301,23 @@ let
   ];
 
   testHex = it "checks conversion to hex" (lib.flatten [
-    (lib.imap0 (i: hex: [
-      (assertEq "hexByte ${toString i} == ${hex}" (hexByte i) hex)
-      (assertEq "${toString i} == fromHex ${hex}" i (int.fromHex hex))
-    ]) expectedBytes)
-    (builtins.map ({ left, right }: [
-      (assertEq "toHex ${toString left} == ${right}" (int.toHex left) right)
-      (assertEq "${toString left} == fromHex ${right}" left (int.fromHex right))
-    ]) hexInts)
+    (lib.imap0
+      (i: hex: [
+        (assertEq "hexByte ${toString i} == ${hex}" (hexByte i) hex)
+        (assertEq "${toString i} == fromHex ${hex}" i (int.fromHex hex))
+      ])
+      expectedBytes)
+    (builtins.map
+      ({ left, right }: [
+        (assertEq "toHex ${toString left} == ${right}" (int.toHex left) right)
+        (assertEq "${toString left} == fromHex ${right}" left (int.fromHex right))
+      ])
+      hexInts)
   ]);
 
   testBasic = it "checks basic int operations" [
     (assertEq "122 is even" (int.even 122 && !(int.odd 122)) true)
     (assertEq "123 is odd" (int.odd 123 && !(int.even 123)) true)
-    (assertEq "abs -4959" (int.abs (-4959)) 4959)
   ];
 
   expNumbers = [
@@ -94,20 +334,23 @@ let
   ];
 
   testExp = it "checks exponentiation"
-    (builtins.map ({ left, right }:
-      assertEq
-        "2 ^ ${toString left} == ${toString right}"
-        (int.exp 2 left) right) expNumbers);
+    (builtins.map
+      ({ left, right }:
+        assertEq
+          "2 ^ ${toString left} == ${toString right}"
+          (int.exp 2 left)
+          right)
+      expNumbers);
 
   shifts = [
-    { a =   2; b = 5; c =   64; op = "<<"; }
-    { a =  -2; b = 5; c =  -64; op = "<<"; }
+    { a = 2; b = 5; c = 64; op = "<<"; }
+    { a = -2; b = 5; c = -64; op = "<<"; }
     { a = 123; b = 4; c = 1968; op = "<<"; }
-    { a =   1; b = 8; c =  256; op = "<<"; }
-    { a = 256; b = 8; c =    1; op = ">>"; }
-    { a = 374; b = 2; c =   93; op = ">>"; }
-    { a =   2; b = 2; c =    0; op = ">>"; }
-    { a =  99; b = 9; c =    0; op = ">>"; }
+    { a = 1; b = 8; c = 256; op = "<<"; }
+    { a = 256; b = 8; c = 1; op = ">>"; }
+    { a = 374; b = 2; c = 93; op = ">>"; }
+    { a = 2; b = 2; c = 0; op = ">>"; }
+    { a = 99; b = 9; c = 0; op = ">>"; }
   ];
 
   checkShift = { a, b, c, op }@args:
@@ -116,15 +359,18 @@ let
         "<<" = int.bitShiftL;
         ">>" = int.bitShiftR;
       };
-    in assertEq "${toString a} ${op} ${toString b} == ${toString c}" (f a b) c;
+    in
+    assertEq "${toString a} ${op} ${toString b} == ${toString c}" (f a b) c;
 
   checkShiftRDivExp = n:
     assertEq "${toString n} >> 5 == ${toString n} / 2 ^ 5"
-      (int.bitShiftR n 5) (int.div n (int.exp 2 5));
+      (int.bitShiftR n 5)
+      (n / (int.exp 2 5));
 
   checkShiftLMulExp = n:
     assertEq "${toString n} >> 6 == ${toString n} * 2 ^ 6"
-      (int.bitShiftL n 5) (int.mul n (int.exp 2 5));
+      (int.bitShiftL n 5)
+      (n * (int.exp 2 5));
 
   testBit = it "checks bitwise operations" (lib.flatten [
     (builtins.map checkShift shifts)
@@ -160,44 +406,50 @@ let
   ]);
 
   divisions = [
-    { a =  2; b =  1; c = 2; mod = 0;}
-    { a =  2; b =  2; c = 1; mod = 0;}
-    { a = 20; b = 10; c = 2; mod = 0;}
-    { a = 12; b =  5; c = 2; mod = 2;}
-    { a = 23; b =  4; c = 5; mod = 3;}
+    { a = 2; b = 1; c = 2; rem = 0; }
+    { a = 2; b = 2; c = 1; rem = 0; }
+    { a = 20; b = 10; c = 2; rem = 0; }
+    { a = 12; b = 5; c = 2; rem = 2; }
+    { a = 23; b = 4; c = 5; rem = 3; }
   ];
 
-  checkDiv = n: { a, b, c, mod }: [
-    (assertEq "${n}: div result" (int.div a b) c)
-    (assertEq "${n}: mod result" (int.mod a b) mod)
-    (assertEq "${n}: divMod law" ((int.div a b) * b + (int.mod a b)) a)
+  checkQuot = n: { a, b, c, rem }: [
+    (assertEq "${n}: quot result" (int.quot a b) c)
+    (assertEq "${n}: rem result" (int.rem a b) rem)
+    (assertEq "${n}: quotRem law" ((int.quot a b) * b + (int.rem a b)) a)
   ];
 
-  testDivMod = it "checks integer division and modulo"
+  testQuotRem = it "checks integer quotient and remainder"
     (lib.flatten [
-      (builtins.map (checkDiv "+a / +b") divisions)
-      (builtins.map (fun.rl (checkDiv "-a / +b") (x: x // {
-        a = -x.a;
-        c = -x.c;
-        mod = -x.mod;
-      })) divisions)
-      (builtins.map (fun.rl (checkDiv "+a / -b") (x: x // {
-        b = -x.b;
-        c = -x.c;
-      })) divisions)
-      (builtins.map (fun.rl (checkDiv "-a / -b") (x: x // {
-        a = -x.a;
-        b = -x.b;
-        mod = -x.mod;
-      })) divisions)
+      (builtins.map (checkQuot "+a / +b") divisions)
+      (builtins.map
+        (fun.rl (checkQuot "-a / +b") (x: x // {
+          a = -x.a;
+          c = -x.c;
+          rem = -x.rem;
+        }))
+        divisions)
+      (builtins.map
+        (fun.rl (checkQuot "+a / -b") (x: x // {
+          b = -x.b;
+          c = -x.c;
+        }))
+        divisions)
+      (builtins.map
+        (fun.rl (checkQuot "-a / -b") (x: x // {
+          a = -x.a;
+          b = -x.b;
+          rem = -x.rem;
+        }))
+        divisions)
     ]);
 
 in
-  runTestsuite "nix.int" [
-    testBounds
-    testHex
-    testBasic
-    testExp
-    testBit
-    testDivMod
-  ]
+runTestsuite "nix.int" [
+  testBounds
+  testHex
+  testBasic
+  testExp
+  testBit
+  testQuotRem
+]
diff --git a/users/sterni/nix/list/default.nix b/users/sterni/nix/list/default.nix
new file mode 100644
index 0000000000..568a76d637
--- /dev/null
+++ b/users/sterni/nix/list/default.nix
@@ -0,0 +1,30 @@
+{ ... }:
+
+{
+  /* For a list of length n that consists of lists of length m,
+     return a list of length m containing lists of length n
+     so that
+
+         builtins.elemAt (builtins.elemAt orig a) b
+         == builtins.elemAt (builtins.elemAt transposed b) a
+
+     Essentially, if you think of the nested list as an array with two
+     dimensions, the two index axes are swapped.
+
+     The length of the inner lists m is determined based on the first element
+     and assumed to be used for all other lists. Malformed input data may
+     cause the function to crash or lose data.
+
+     Type: <n>[ <m>[ ] ] -> <m>[ <n>[ ] ]
+  */
+  transpose = list:
+    let
+      innerLength = builtins.length (builtins.head list);
+      outerLength = builtins.length list;
+    in
+    builtins.genList
+      (inner: builtins.genList
+        (outer: builtins.elemAt (builtins.elemAt list outer) inner)
+        outerLength)
+      innerLength;
+}
diff --git a/users/sterni/nix/misc/default.nix b/users/sterni/nix/misc/default.nix
new file mode 100644
index 0000000000..1de9c973ec
--- /dev/null
+++ b/users/sterni/nix/misc/default.nix
@@ -0,0 +1,18 @@
+{ ... }:
+
+let
+  /* Returns true if it is being evaluated using restrict-eval, false if not.
+     It's more robust than using `builtins.getEnv` since it isn't fooled by
+     `env -i`.
+
+     See https://github.com/NixOS/nix/issues/6579 for a description of the
+     behavior. Precise cause in the evaluator / store implementation is unclear.
+
+     Type: bool
+  */
+  inRestrictedEval = builtins.pathExists (toString ./guinea-pig + "/.");
+in
+
+{
+  inherit inRestrictedEval;
+}
diff --git a/users/sterni/nix/misc/guinea-pig b/users/sterni/nix/misc/guinea-pig
new file mode 120000
index 0000000000..73537e478e
--- /dev/null
+++ b/users/sterni/nix/misc/guinea-pig
@@ -0,0 +1 @@
+default.nix
\ No newline at end of file
diff --git a/users/sterni/nix/num/default.nix b/users/sterni/nix/num/default.nix
new file mode 100644
index 0000000000..81e2f8377f
--- /dev/null
+++ b/users/sterni/nix/num/default.nix
@@ -0,0 +1,17 @@
+{ ... }:
+
+rec {
+  inherit (builtins)
+    mul
+    div
+    add
+    sub
+    ;
+
+  sign = i: if i < 0 then -1 else 1;
+  abs = i: if i < 0 then -i else i;
+
+  inRange = a: b: x: x >= a && x <= b;
+
+  sum = builtins.foldl' (a: b: a + b) 0;
+}
diff --git a/users/sterni/nix/num/tests/default.nix b/users/sterni/nix/num/tests/default.nix
new file mode 100644
index 0000000000..ca5f861deb
--- /dev/null
+++ b/users/sterni/nix/num/tests/default.nix
@@ -0,0 +1,26 @@
+{ depot, ... }:
+
+let
+
+  inherit (depot.nix.runTestsuite)
+    runTestsuite
+    it
+    assertEq
+    ;
+
+  inherit (depot.users.sterni.nix)
+    num
+    ;
+
+  testsBasic = it "tests basic operations" [
+    (assertEq "abs -4959" (num.abs (-4959)) 4959)
+    (assertEq "sum" (num.sum [ 123 321 1.5 ]) (123 + 321 + 1.5))
+    (assertEq "inRange"
+      (builtins.map (num.inRange 1.0 5) [ 0 0.5 3 4 4.5 5.5 5 6 ])
+      [ false false true true true false true false ])
+  ];
+in
+
+runTestsuite "nix.num" [
+  testsBasic
+]
diff --git a/users/sterni/nix/string/default.nix b/users/sterni/nix/string/default.nix
index 19d2cec243..381c8ddff7 100644
--- a/users/sterni/nix/string/default.nix
+++ b/users/sterni/nix/string/default.nix
@@ -21,7 +21,8 @@ let
   charAt = i: s:
     let
       r = builtins.substring i 1 s;
-    in if r == "" then null else r;
+    in
+    if r == "" then null else r;
 
   charIndex = char: s:
     let
@@ -32,7 +33,8 @@ let
           [ (charAt i s == char) i ]
           [ true (go (i + 1)) ]
         ];
-    in go 0;
+    in
+    go 0;
 
   toChars = lib.stringToCharacters;
   fromChars = lib.concatStrings;
@@ -46,15 +48,16 @@ let
     let
       leftS = fromChars (builtins.genList (_: char) left);
       rightS = fromChars (builtins.genList (_: char) right);
-    in "${leftS}${s}${rightS}";
+    in
+    "${leftS}${s}${rightS}";
 
   fit = { char ? " ", width, side ? "left" }: s:
     let
       diff = width - builtins.stringLength s;
     in
-      if diff <= 0
-      then s
-      else pad { inherit char; "${side}" = diff; } s;
+    if diff <= 0
+    then s
+    else pad { inherit char; "${side}" = diff; } s;
 
   # pattern matching for strings only
   match = val: matcher: matcher."${val}";
@@ -80,23 +83,27 @@ let
       tokens = lib.flatten (builtins.split "(%.)" formatString);
       argsNeeded = builtins.length (builtins.filter specifierWithArg tokens);
 
-      format = args: (builtins.foldl' ({ out ? "", argIndex ? 0 }: token: {
-        argIndex = argIndex + (if specifierWithArg token then 1 else 0);
-        out =
-          /**/ if token == "%s" then out + builtins.elemAt args argIndex
-          else if token == "%%" then out + "%"
-          else if isSpecifier token then throw "Unsupported format specifier ${token}"
-          else out + token;
-      }) {} tokens).out;
+      format = args: (builtins.foldl'
+        ({ out ? "", argIndex ? 0 }: token: {
+          argIndex = argIndex + (if specifierWithArg token then 1 else 0);
+          out =
+            if token == "%s" then out + builtins.elemAt args argIndex
+            else if token == "%%" then out + "%"
+            else if isSpecifier token then throw "Unsupported format specifier ${token}"
+            else out + token;
+        })
+        { }
+        tokens).out;
 
       accumulateArgs = argCount: args:
         if argCount > 0
         then arg: accumulateArgs (argCount - 1) (args ++ [ arg ])
         else format args;
     in
-      accumulateArgs argsNeeded [];
+    accumulateArgs argsNeeded [ ];
 
-in {
+in
+{
   inherit
     take
     drop
diff --git a/users/sterni/nix/string/tests/default.nix b/users/sterni/nix/string/tests/default.nix
index c8aec94640..e9015e95dc 100644
--- a/users/sterni/nix/string/tests/default.nix
+++ b/users/sterni/nix/string/tests/default.nix
@@ -63,10 +63,10 @@ let
   ];
 
 in
-  runTestsuite "nix.string" [
-    testTakeDrop
-    testIndexing
-    testFinding
-    testMatch
-    testPrintf
-  ]
+runTestsuite "nix.string" [
+  testTakeDrop
+  testIndexing
+  testFinding
+  testMatch
+  testPrintf
+]
diff --git a/users/sterni/nix/url/default.nix b/users/sterni/nix/url/default.nix
index 37bd0de66a..4a401873a1 100644
--- a/users/sterni/nix/url/default.nix
+++ b/users/sterni/nix/url/default.nix
@@ -10,9 +10,24 @@ let
     ;
 
   reserved = c: builtins.elem c [
-    "!" "#" "$" "&" "'" "(" ")"
-    "*" "+" "," "/" ":" ";" "="
-    "?" "@" "[" "]"
+    "!"
+    "#"
+    "$"
+    "&"
+    "'"
+    "("
+    ")"
+    "*"
+    "+"
+    ","
+    "/"
+    ":"
+    ";"
+    "="
+    "?"
+    "@"
+    "["
+    "]"
   ];
 
   unreserved = c: char.asciiAlphaNum c
@@ -21,11 +36,13 @@ let
   percentEncode = c:
     if unreserved c
     then c
-    else "%" + (string.fit {
-      width = 2;
-      char = "0";
-      side = "left";
-    } (int.toHex (char.ord c)));
+    else "%" + (string.fit
+      {
+        width = 2;
+        char = "0";
+        side = "left";
+      }
+      (int.toHex (char.ord c)));
 
   encode = { leaveReserved ? false }: s:
     let
@@ -34,7 +51,8 @@ let
         if leaveReserved && reserved c
         then c
         else percentEncode c;
-    in lib.concatStrings (builtins.map tr chars);
+    in
+    lib.concatStrings (builtins.map tr chars);
 
   decode = s:
     let
@@ -71,9 +89,10 @@ let
         ];
 
     in
-      (builtins.foldl' decodeStep {} tokens).result;
+    (builtins.foldl' decodeStep { } tokens).result;
 
-in {
+in
+{
   inherit
     encode
     decode
diff --git a/users/sterni/nix/url/tests/default.nix b/users/sterni/nix/url/tests/default.nix
index 7cf53cde15..4eb6f95ccd 100644
--- a/users/sterni/nix/url/tests/default.nix
+++ b/users/sterni/nix/url/tests/default.nix
@@ -14,11 +14,13 @@ let
 
   checkEncoding = args: { left, right }:
     assertEq "encode ${builtins.toJSON left} == ${builtins.toJSON right}"
-      (url.encode args left) right;
+      (url.encode args left)
+      right;
 
   checkDecoding = { left, right }:
-  assertEq "${builtins.toJSON left} == decode ${builtins.toJSON right}"
-    (url.decode left) right;
+    assertEq "${builtins.toJSON left} == decode ${builtins.toJSON right}"
+      (url.decode left)
+      right;
 
   unreserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_~";
 
@@ -33,7 +35,7 @@ let
   ];
 
   testEncode = it "checks url.encode"
-    (builtins.map (checkEncoding {}) encodeExpected);
+    (builtins.map (checkEncoding { }) encodeExpected);
 
   testDecode = it "checks url.decode"
     (builtins.map checkDecoding encodeExpected);
@@ -50,7 +52,7 @@ let
       "urn:oasis:names:specification:docbook:dtd:xml:4.1.2"
     ]);
 in
-  runTestsuite "nix.url" [
-    testEncode
-    testLeaveReserved
-  ]
+runTestsuite "nix.url" [
+  testEncode
+  testLeaveReserved
+]
diff --git a/users/sterni/nix/utf8/default.nix b/users/sterni/nix/utf8/default.nix
index 270da934b6..e76695f128 100644
--- a/users/sterni/nix/utf8/default.nix
+++ b/users/sterni/nix/utf8/default.nix
@@ -6,6 +6,7 @@ let
     char
     flow
     fun
+    num
     int
     string
     util
@@ -25,7 +26,7 @@ let
      Type: integer -> integer
   */
   byteCount = i: flow.cond [
-    [ (int.bitAnd i 128 == 0)   1 ]
+    [ (int.bitAnd i 128 == 0) 1 ]
     [ (int.bitAnd i 224 == 192) 2 ]
     [ (int.bitAnd i 240 == 224) 3 ]
     [ (int.bitAnd i 248 == 240) 4 ]
@@ -45,30 +46,30 @@ let
     first:
     # byte position as an index starting with 0
     pos:
-      let
-        defaultRange = int.inRange 128 191;
-
-        secondBytePredicate = flow.switch first [
-          [ (int.inRange 194 223) defaultRange          ] # C2..DF
-          [ 224                   (int.inRange 160 191) ] # E0
-          [ (int.inRange 225 236) defaultRange          ] # E1..EC
-          [ 237                   (int.inRange 128 159) ] # ED
-          [ (int.inRange 238 239) defaultRange          ] # EE..EF
-          [ 240                   (int.inRange 144 191) ] # F0
-          [ (int.inRange 241 243) defaultRange          ] # F1..F3
-          [ 244                   (int.inRange 128 143) ] # F4
-          [ (fun.const true)      null                  ]
-        ];
+    let
+      defaultRange = num.inRange 128 191;
+
+      secondBytePredicate = flow.switch first [
+        [ (num.inRange 194 223) defaultRange ] # C2..DF
+        [ 224 (num.inRange 160 191) ] # E0
+        [ (num.inRange 225 236) defaultRange ] # E1..EC
+        [ 237 (num.inRange 128 159) ] # ED
+        [ (num.inRange 238 239) defaultRange ] # EE..EF
+        [ 240 (num.inRange 144 191) ] # F0
+        [ (num.inRange 241 243) defaultRange ] # F1..F3
+        [ 244 (num.inRange 128 143) ] # F4
+        [ (fun.const true) null ]
+      ];
 
-        firstBytePredicate = byte: assert first == byte;
-          first < 128 || secondBytePredicate != null;
-      in
-        # Either ASCII or in one of the byte ranges of Table 3-6.
-        if pos == 0 then firstBytePredicate
-        # return predicate according to Table 3-6.
-        else if pos == 1 then assert secondBytePredicate != null; secondBytePredicate
-        # 3rd and 4th byte have only one validity rule
-        else defaultRange;
+      firstBytePredicate = byte: assert first == byte;
+        first < 128 || secondBytePredicate != null;
+    in
+    # Either ASCII or in one of the byte ranges of Table 3-6.
+    if pos == 0 then firstBytePredicate
+    # return predicate according to Table 3-6.
+    else if pos == 1 then assert secondBytePredicate != null; secondBytePredicate
+    # 3rd and 4th byte have only one validity rule
+    else defaultRange;
 
   /* Iteration step for decoding an UTF-8 byte sequence.
      It decodes incrementally, i. e. it has to be fed
@@ -128,23 +129,24 @@ let
             # the current value by the amount of bytes left.
             offset = (count - (pos + 1)) * 6;
           in
-            code + (int.bitShiftL (int.bitAnd mask value) offset);
+          code + (int.bitShiftL (int.bitAnd mask value) offset);
       illFormedMsg =
         "Ill-formed byte ${int.toHex value} at position ${toString pos} in ${toString count} byte UTF-8 sequence";
     in
-      if !(wellFormedByte first pos value) then builtins.throw illFormedMsg
-      else if pos + 1 == count
-      then (builtins.removeAttrs args [ # allow extra state being passed through
-        "count"
-        "code"
-        "pos"
-        "first"
-      ]) // { result = newCode; }
-      else (builtins.removeAttrs args [ "result" ]) // {
-        inherit count first;
-        code = newCode;
-        pos  = pos + 1;
-      };
+    if !(wellFormedByte first pos value) then builtins.throw illFormedMsg
+    else if pos + 1 == count
+    then (builtins.removeAttrs args [
+      # allow extra state being passed through
+      "count"
+      "code"
+      "pos"
+      "first"
+    ]) // { result = newCode; }
+    else (builtins.removeAttrs args [ "result" ]) // {
+      inherit count first;
+      code = newCode;
+      pos = pos + 1;
+    };
 
   /* Decode an UTF-8 string into a list of codepoints.
 
@@ -161,7 +163,7 @@ let
           {
             key = "start";
             stringIndex = -1;
-            state = {};
+            state = { };
             codepoint = null;
           }
         ];
@@ -170,7 +172,8 @@ let
             # updated values for current iteration step
             newIndex = stringIndex + 1;
             newState = step state (builtins.substring newIndex 1 s);
-          in lib.optional (newIndex < stringLength) {
+          in
+          lib.optional (newIndex < stringLength) {
             # unique keys to make genericClosure happy
             key = toString newIndex;
             # carryover state for the next step
@@ -183,35 +186,39 @@ let
     in
     # extract all steps that yield a code point into a list
     builtins.map (v: v.codepoint) (
-      builtins.filter (
-        { codepoint, stringIndex, state, ... }:
-
-        let
-          # error message in case we are missing bytes at the end of input
-          earlyEndMsg =
-            if state ? count && state ? pos
-            then "Missing ${toString (with state; count - pos)} bytes at end of input"
-            else "Unexpected end of input";
-        in
-
-        # filter out all iteration steps without a codepoint value
-        codepoint != null
+      builtins.filter
+        (
+          { codepoint, stringIndex, state, ... }:
+
+          let
+            # error message in case we are missing bytes at the end of input
+            earlyEndMsg =
+              if state ? count && state ? pos
+              then "Missing ${toString (with state; count - pos)} bytes at end of input"
+              else "Unexpected end of input";
+          in
+
+          # filter out all iteration steps without a codepoint value
+          codepoint != null
           # if we are at the iteration step of a non-empty input string, throw
           # an error if no codepoint was returned, as it indicates an incomplete
           # UTF-8 sequence.
           || (stringLength > 0 && stringIndex == stringLength - 1 && throw earlyEndMsg)
 
-      ) iterResult
+        )
+        iterResult
     );
 
   /* Pretty prints a Unicode codepoint in the U+<HEX> notation.
 
      Type: integer -> string
   */
-  formatCodepoint = cp: "U+" + string.fit {
-    width = 4;
-    char = "0";
-  } (int.toHex cp);
+  formatCodepoint = cp: "U+" + string.fit
+    {
+      width = 4;
+      char = "0";
+    }
+    (int.toHex cp);
 
   encodeCodepoint = cp:
     let
@@ -219,11 +226,11 @@ let
       # Note that this doesn't check if the Unicode codepoint is allowed,
       # but rather allows all theoretically UTF-8-encodeable ones.
       count = flow.switch cp [
-        [ (int.inRange 0 127)         1 ] # 00000000 0xxxxxxx
-        [ (int.inRange 128 2047)      2 ] # 00000yyy yyxxxxxx
-        [ (int.inRange 2048 65535)    3 ] # zzzzyyyy yyxxxxxx
-        [ (int.inRange 65536 1114111) 4 ] # 000uuuuu zzzzyyyy yyxxxxxx,
-                                          # capped at U+10FFFF
+        [ (num.inRange 0 127) 1 ] # 00000000 0xxxxxxx
+        [ (num.inRange 128 2047) 2 ] # 00000yyy yyxxxxxx
+        [ (num.inRange 2048 65535) 3 ] # zzzzyyyy yyxxxxxx
+        [ (num.inRange 65536 1114111) 4 ] # 000uuuuu zzzzyyyy yyxxxxxx,
+        # capped at U+10FFFF
 
         [ (fun.const true) (builtins.throw invalidCodepointMsg) ]
       ];
@@ -234,32 +241,34 @@ let
       # according to Table 3-6. from The Unicode Standard, Version 13.0,
       # section 3.9. u is split into uh and ul since they are used in
       # different bytes in the end.
-      components = lib.mapAttrs (_: { mask, offset }:
-        int.bitAnd (int.bitShiftR cp offset) mask
-      ) {
-        x = {
-          mask = if count > 1 then 63 else 127;
-          offset = 0;
-        };
-        y = {
-          mask = if count > 2 then 63 else 31;
-          offset = 6;
-        };
-        z = {
-          mask = 15;
-          offset = 12;
-        };
-        # u which belongs into the second byte
-        ul = {
-          mask = 3;
-          offset = 16;
-        };
-        # u which belongs into the first byte
-        uh = {
-          mask = 7;
-          offset = 18;
+      components = lib.mapAttrs
+        (_: { mask, offset }:
+          int.bitAnd (int.bitShiftR cp offset) mask
+        )
+        {
+          x = {
+            mask = if count > 1 then 63 else 127;
+            offset = 0;
+          };
+          y = {
+            mask = if count > 2 then 63 else 31;
+            offset = 6;
+          };
+          z = {
+            mask = 15;
+            offset = 12;
+          };
+          # u which belongs into the second byte
+          ul = {
+            mask = 3;
+            offset = 16;
+          };
+          # u which belongs into the first byte
+          uh = {
+            mask = 7;
+            offset = 18;
+          };
         };
-      };
       inherit (components) x y z ul uh;
 
       # Finally construct the byte sequence for the given codepoint. This is
@@ -286,15 +295,18 @@ let
 
       unableToEncodeMessage = "Can't encode ${formatCodepoint cp} as UTF-8";
 
-    in string.fromBytes (
-      builtins.genList (i:
-        let
-          byte = builtins.elemAt bytes i;
-        in
+    in
+    string.fromBytes (
+      builtins.genList
+        (i:
+          let
+            byte = builtins.elemAt bytes i;
+          in
           if wellFormedByte firstByte i byte
           then byte
           else builtins.throw unableToEncodeMessage
-      ) count
+        )
+        count
     );
 
   /* Encode a list of Unicode codepoints into an UTF-8 string.
@@ -303,7 +315,8 @@ let
   */
   encode = lib.concatMapStrings encodeCodepoint;
 
-in {
+in
+{
   inherit
     encode
     decode
diff --git a/users/sterni/nix/utf8/tests/default.nix b/users/sterni/nix/utf8/tests/default.nix
index ddcd34208a..40783eab24 100644
--- a/users/sterni/nix/utf8/tests/default.nix
+++ b/users/sterni/nix/utf8/tests/default.nix
@@ -25,9 +25,10 @@ let
     char
     ;
 
-  rustDecoder = rustSimple {
-    name = "utf8-decode";
-  } ''
+  rustDecoder = rustSimple
+    {
+      name = "utf8-decode";
+    } ''
     use std::io::{self, Read};
     fn main() -> std::io::Result<()> {
       let mut buffer = String::new();
@@ -47,10 +48,11 @@ let
 
   rustDecode = s:
     let
-      expr = runCommandLocal "${s}-decoded" {} ''
+      expr = runCommandLocal "${s}-decoded" { } ''
         printf '%s' ${lib.escapeShellArg s} | ${rustDecoder} > $out
       '';
-    in import expr;
+    in
+    import expr;
 
   hexDecode = l:
     utf8.decode (string.fromBytes (builtins.map int.fromHex l));
@@ -65,23 +67,27 @@ let
     (assertEq "well-formed: F4 80 83 92" (hexDecode [ "F4" "80" "83" "92" ]) [ 1048786 ])
     (assertThrows "Codepoint out of range: 0xFFFFFF" (hexEncode [ "FFFFFF" ]))
     (assertThrows "Codepoint out of range: -0x02" (hexEncode [ "-02" ]))
-  ] ++ builtins.genList (i:
-    let
-      cp = i + int.fromHex "D800";
-    in
+  ] ++ builtins.genList
+    (i:
+      let
+        cp = i + int.fromHex "D800";
+      in
       assertThrows "Can't encode UTF-16 reserved characters: ${utf8.formatCodepoint cp}"
         (utf8.encode [ cp ])
-  ) (int.fromHex "07FF"));
+    )
+    (int.fromHex "07FF"));
 
   testAscii = it "checks decoding of ascii strings"
-    (builtins.map (s: assertEq "ASCII decoding is equal to UTF-8 decoding for \"${s}\""
-      (string.toBytes s) (utf8.decode s)) [
-        "foo bar"
-        "hello\nworld"
-        "carriage\r\nreturn"
-        "1238398494829304 []<><>({})[]!!)"
-        (string.take 127 char.allChars)
-      ]);
+    (builtins.map
+      (s: assertEq "ASCII decoding is equal to UTF-8 decoding for \"${s}\""
+        (string.toBytes s)
+        (utf8.decode s)) [
+      "foo bar"
+      "hello\nworld"
+      "carriage\r\nreturn"
+      "1238398494829304 []<><>({})[]!!)"
+      (string.take 127 char.allChars)
+    ]);
 
   randomUnicode = [
     "" # empty string should yield empty list
@@ -126,16 +132,17 @@ let
   testDecodingEncoding = it "checks that decoding and then encoding forms an identity"
     (builtins.map
       (s: assertEq "Decoding and then encoding β€œ${s}” yields itself"
-        (utf8.encode (utf8.decode s)) s)
+        (utf8.encode (utf8.decode s))
+        s)
       (lib.flatten [
         glassSentences
         randomUnicode
       ]));
 
 in
-  runTestsuite "nix.utf8" [
-    testFailures
-    testAscii
-    testDecoding
-    testDecodingEncoding
-  ]
+runTestsuite "nix.utf8" [
+  testFailures
+  testAscii
+  testDecoding
+  testDecodingEncoding
+]
diff --git a/users/sterni/nixpkgs-crate-holes/default.nix b/users/sterni/nixpkgs-crate-holes/default.nix
index a022568dc9..1630ecb8f1 100644
--- a/users/sterni/nixpkgs-crate-holes/default.nix
+++ b/users/sterni/nixpkgs-crate-holes/default.nix
@@ -33,7 +33,7 @@ let
       symphorien
       erictapen
       expipiplus1
-    ;
+      ;
   };
 
   # buildRustPackage handling
@@ -55,24 +55,25 @@ let
   extractCargoLock = drv:
     if !(drv ? cargoDeps.outPath)
     then null
-    else pkgs.runCommandNoCC "${drv.name}-Cargo.lock" {} ''
-      if test -d "${drv.cargoDeps}"; then
-        cp "${drv.cargoDeps}/Cargo.lock" "$out"
-      fi
-
-      if test -f "${drv.cargoDeps}"; then
-        tar -xO \
-          --no-wildcards-match-slash --wildcards \
-          -f "${drv.cargoDeps}" \
-          '*/Cargo.lock' \
-          > "$out"
-      fi
-    '';
+    else
+      pkgs.runCommand "${drv.name}-Cargo.lock" { } ''
+        if test -d "${drv.cargoDeps}"; then
+          cp "${drv.cargoDeps}/Cargo.lock" "$out"
+        fi
+
+        if test -f "${drv.cargoDeps}"; then
+          tar -xO \
+            --no-wildcards-match-slash --wildcards \
+            -f "${drv.cargoDeps}" \
+            '*/Cargo.lock' \
+            > "$out"
+        fi
+      '';
 
   # nixpkgs traversal
 
   # Condition for us to recurse: Either at top-level or recurseForDerivation.
-  recurseInto = path: x: path == [] ||
+  recurseInto = path: x: path == [ ] ||
     (lib.isAttrs x && (x.recurseForDerivations or false));
 
   # Returns the value or false if an eval error occurs.
@@ -97,46 +98,59 @@ let
           doRec = tryEvalOrFalse (recurseInto path x);
           isRust = tryEvalOrFalse (isRustPackage x);
         in
-          if doRec then lib.concatLists (
-            lib.mapAttrsToList (n: go (path ++ [ n ])) x
-          ) else if isDrv && isRust then [
-            {
-              attr = path;
-              lock = extractCargoLock x;
-              maintainers = x.meta.maintainers or [];
-            }
-          ] else [];
-    in go [];
+        if doRec then
+          lib.concatLists
+            (
+              lib.mapAttrsToList (n: go (path ++ [ n ])) x
+            ) else if isDrv && isRust then [
+          {
+            attr = path;
+            lock = extractCargoLock x;
+            maintainers = x.meta.maintainers or [ ];
+          }
+        ] else [ ];
+    in
+    go [ ];
 
   # Report generation and formatting
 
-  reportFor = { attr, lock, maintainers ? [] }: let
-    # naΓ―ve attribute path to Nix syntax conversion
-    strAttr = lib.concatStringsSep "." attr;
-    strMaintainers = lib.concatMapStringsSep " " (m: "@${m.github}") (
-      builtins.filter (x: builtins.elem x maintainerWhitelist) maintainers
-    );
-  in
+  reportFor = { attr, lock, maintainers ? [ ] }:
+    let
+      # naΓ―ve attribute path to Nix syntax conversion
+      strAttr = lib.concatStringsSep "." attr;
+      strMaintainers = lib.concatMapStringsSep " " (m: "@${m.github}") (
+        builtins.filter (x: builtins.elem x maintainerWhitelist) maintainers
+      );
+    in
     if lock == null
     then pkgs.emptyFile
-    else depot.nix.runExecline "${strAttr}-vulnerability-report" {} [
-      "pipeline" [
-        bins.cargo-audit
-        "audit" "--json"
-        "-n" "--db" rustsec-advisory-db
-        "-f" lock
-      ]
-      "importas" "out" "out"
-      "redirfd" "-w" "1" "$out"
-      bins.jq "-rj" "-f" ./format-audit-result.jq
-      "--arg" "attr" strAttr
-      "--arg" "maintainers" strMaintainers
-    ];
+    else
+      depot.nix.runExecline "${strAttr}-vulnerability-report" { } [
+        "foreground"
+        [
+          "importas"
+          "out"
+          "out"
+          "redirfd"
+          "-w"
+          "1"
+          "$out"
+          depot.tools.rust-crates-advisory.lock-file-report
+          strAttr
+          lock
+          "true"
+          strMaintainers
+        ]
+        # ignore exit status of report
+        "exit"
+        "0"
+      ];
 
   # GHMF in issues splits paragraphs on newlines
-  description = lib.concatMapStringsSep "\n\n" (
-    builtins.replaceStrings [ "\n" ] [ " " ]
-  ) [
+  description = lib.concatMapStringsSep "\n\n"
+    (
+      builtins.replaceStrings [ "\n" ] [ " " ]
+    ) [
     ''
       The vulnerability report below was generated by
       [nixpkgs-crate-holes](https://code.tvl.fyi/tree/users/sterni/nixpkgs-crate-holes)
@@ -194,39 +208,63 @@ let
       );
     in
 
-    depot.nix.runExecline "nixpkgs-rust-pkgs-vulnerability-report.md" {
-      stdin = lib.concatMapStrings (report: "${report}\n") reports;
-    } [
-      "importas" "out" "out"
-      "redirfd" "-w" "1" "$out"
+    depot.nix.runExecline "nixpkgs-rust-pkgs-vulnerability-report.md"
+      {
+        stdin = lib.concatMapStrings (report: "${report}\n") reports;
+      } [
+      "importas"
+      "out"
+      "out"
+      "redirfd"
+      "-w"
+      "1"
+      "$out"
       # Print introduction paragraph for the issue
-      "if" [ bins.printf "%s\n\n" description ]
+      "if"
+      [ bins.printf "%s\n\n" description ]
       # Print all reports
-      "foreground" [
-        "forstdin" "-E" "report" bins.cat "$report"
+      "foreground"
+      [
+        "forstdin"
+        "-E"
+        "report"
+        bins.cat
+        "$report"
       ]
       # Print stats at the end (mostly as a gimmick), we already know how many
       # attributes there are and count the attributes with vulnerability by
       # finding the number of checkable list entries in the output.
-      "backtick" "-E" "vulnerableCount" [
-        "pipeline" [
-          bins.grep "^- \\[ \\]" "$out"
+      "backtick"
+      "-E"
+      "vulnerableCount"
+      [
+        "pipeline"
+        [
+          bins.grep
+          "^- \\[ \\]"
+          "$out"
         ]
-        bins.wc "-l"
+        bins.wc
+        "-l"
       ]
-      "if" [
+      "if"
+      [
         bins.printf
         "\n%s of %s checked attributes have vulnerable dependencies.\n\n"
         "$vulnerableCount"
         (toString (builtins.length reports))
       ]
-      "if" [
-        bins.printf "%s\n\n" runInstructions
+      "if"
+      [
+        bins.printf
+        "%s\n\n"
+        runInstructions
       ]
     ];
 
   singleReport =
-    { # Attribute to check: string or list of strings (attr path)
+    {
+      # Attribute to check: string or list of strings (attr path)
       attr
       # Path to importable nixpkgs checkout
     , nixpkgsPath
@@ -241,37 +279,63 @@ let
       strAttr = lib.concatStringsSep "." attr';
     in
 
-    depot.nix.runExecline "${strAttr}-report.html" {} [
-      "importas" "out" "out"
-      "backtick" "-I" "-E" "-N" "report" [
-        bins.cargo-audit "audit"
+    depot.nix.runExecline "${strAttr}-report.html" { } [
+      "importas"
+      "out"
+      "out"
+      "backtick"
+      "-I"
+      "-E"
+      "-N"
+      "report"
+      [
+        bins.cargo-audit
+        "audit"
         "--quiet"
-        "-n" "--db" rustsec-advisory-db
-        "-f" lockFile
+        "-n"
+        "--db"
+        rustsec-advisory-db
+        "-f"
+        lockFile
       ]
-      "pipeline" [
-        "ifte" [
-          bins.printf "%s" "$report"
-        ] [
-          bins.printf "%s\n" "No vulnerabilities found"
+      "pipeline"
+      [
+        "ifte"
+        [
+          bins.printf
+          "%s"
+          "$report"
+        ]
+        [
+          bins.printf
+          "%s\n"
+          "No vulnerabilities found"
         ]
-        bins.test "-n" "$report"
+        bins.test
+        "-n"
+        "$report"
       ]
-      "pipeline" [
-        bins.tee "/dev/stderr"
+      "pipeline"
+      [
+        bins.tee
+        "/dev/stderr"
       ]
-      "redirfd" "-w" "1" "$out"
+      "redirfd"
+      "-w"
+      "1"
+      "$out"
       bins.ansi2html
     ];
 
-in {
+in
+{
   full = reportForNixpkgs;
   single = singleReport;
 
   inherit
     extractCargoLock
     allLockFiles
-  ;
+    ;
 
   # simple sanity check, doesn't cover everything, but testing the full report
   # is quite expensive in terms of evaluation.
@@ -280,5 +344,5 @@ in {
     attr = [ "ripgrep" ];
   };
 
-  meta.targets = [ "testSingle" ];
+  meta.ci.targets = [ "testSingle" ];
 }
diff --git a/users/sterni/nixpkgs-crate-holes/format-audit-result.jq b/users/sterni/nixpkgs-crate-holes/format-audit-result.jq
deleted file mode 100644
index e3147b8016..0000000000
--- a/users/sterni/nixpkgs-crate-holes/format-audit-result.jq
+++ /dev/null
@@ -1,61 +0,0 @@
-# Link to human-readable advisory info for a given vulnerability
-def link:
-  [ "https://rustsec.org/advisories/", .advisory.id, ".html" ] | add;
-
-# Format a list of version constraints
-def version_list:
-  [ .[] | "`" + . + "`" ] | join("; ");
-
-# show paths to fixing this vulnerability:
-#
-# - if there are patched releases, show them (the version we are using presumably
-#   predates the vulnerability discovery, so we likely want to upgrade to a
-#   patched release).
-# - if there are no patched releases, show the unaffected versions (in case we
-#   want to downgrade).
-# - otherwise we state that no unaffected versions are available at this time.
-#
-# This logic should be useful, but is slightly dumber than cargo-audit's
-# suggestion when using the non-JSON output.
-def patched:
-  if .versions.patched == [] then
-    if .versions.unaffected != [] then
-       "unaffected: " + (.versions.unaffected | version_list)
-    else
-      "no unaffected version available"
-    end
-  else
-    "patched: " + (.versions.patched | version_list)
-  end;
-
-# if the vulnerability has aliases (like CVE-*) emit them in parens
-def aliases:
-  if .advisory.aliases == [] then
-    ""
-  else
-    [ " (", (.advisory.aliases | join(", ")), ")" ] | add
-  end;
-
-# each vulnerability is rendered as a (normal) sublist item
-def format_vulnerability:
-  [ "  - "
-  , .package.name, " ", .package.version, ": "
-  , "[", .advisory.id, "](", link, ")"
-  , aliases
-  , ", ", patched
-  , "\n"
-  ] | add;
-
-# be quiet if no found vulnerabilities, otherwise render a GHFM checklist item
-if .vulnerabilities.found | not then
-  ""
-else
-  ([ "- [ ] "
-   , "`", $attr, "`: "
-   , (.vulnerabilities.count | tostring)
-   , " vulnerabilities in Cargo.lock"
-   , if $maintainers != "" then " (cc " + $maintainers + ")" else "" end
-   , "\n"
-   ] + (.vulnerabilities.list | map(format_vulnerability))
-  ) | add
-end
diff --git a/users/sterni/secrets/default.nix b/users/sterni/secrets/default.nix
new file mode 100644
index 0000000000..5550103c5a
--- /dev/null
+++ b/users/sterni/secrets/default.nix
@@ -0,0 +1,3 @@
+{ depot, ... }:
+
+depot.ops.secrets.mkSecrets ./. (import ./secrets.nix)
diff --git a/users/sterni/secrets/minecraft-rcon.age b/users/sterni/secrets/minecraft-rcon.age
new file mode 100644
index 0000000000..6531a74b88
--- /dev/null
+++ b/users/sterni/secrets/minecraft-rcon.age
Binary files differdiff --git a/users/sterni/secrets/secrets.nix b/users/sterni/secrets/secrets.nix
new file mode 100644
index 0000000000..7132fbf8f3
--- /dev/null
+++ b/users/sterni/secrets/secrets.nix
@@ -0,0 +1,15 @@
+let
+  nonremote = [
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJk+KvgvI2oJTppMASNUfMcMkA2G5ZNt+HnWDzaXKLlo"
+  ];
+
+  ingeborg = [
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAHQn/j6NCYucpM7qIEIslVJxiFeUEKa0hi+HobTz/12"
+  ];
+in
+
+{
+  "warteraum-salt.age".publicKeys = nonremote ++ ingeborg;
+  "warteraum-tokens.age".publicKeys = nonremote ++ ingeborg;
+  "minecraft-rcon.age".publicKeys = nonremote ++ ingeborg;
+}
diff --git a/users/sterni/secrets/warteraum-salt.age b/users/sterni/secrets/warteraum-salt.age
new file mode 100644
index 0000000000..61fa5e6161
--- /dev/null
+++ b/users/sterni/secrets/warteraum-salt.age
Binary files differdiff --git a/users/sterni/secrets/warteraum-tokens.age b/users/sterni/secrets/warteraum-tokens.age
new file mode 100644
index 0000000000..e1bf32269f
--- /dev/null
+++ b/users/sterni/secrets/warteraum-tokens.age
@@ -0,0 +1,11 @@
+age-encryption.org/v1
+-> ssh-ed25519 aXKGcg G96nS/CgSFqyum5QtOwyCo2d7PRIx7pcQBVyFjtErUE
+gkQuhegobZ68Z76h93G57/trz7ixSkpa7Dz+OYMzAIw
+-> ssh-ed25519 OaL1CA u9p+ejyLs4cWgB/LjR8XIIE3tRPf+a5Kqwl0nA8pDio
+ZfPVZIcqgyep7C68sTybGFa+7HFDwwoDQwAmoDszua4
+-> |WcGV<-grease >a*ke{l }9Iv) ]qz
+Ehf2eOTQe0t7mnbgNEjJBtRSNRl+MlgEIiziu9YU206yMQXSLrm04PPo9ycw5x/k
+N/5r/M36qnKJfZUVbtcFom85+UYOQDRnfXXvPyTrsA
+--- hRzM1BnEG2VPMV6DTZF2j4WZk/2uM65yAFDK3F0rSQc
+(«iΉε™ΩΊβ)QZρq(πgl:τSœΩΊ•¦Η’@+fΫ£Ϋ
+RόχΤNoͺήζI}°{Œΐf
\ No newline at end of file
diff --git a/users/tazjin/OWNERS b/users/tazjin/OWNERS
index c86f6eaa6a..ba1c065348 100644
--- a/users/tazjin/OWNERS
+++ b/users/tazjin/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - tazjin
+set noparent
+
+tazjin
diff --git a/users/tazjin/aoc2019/default.nix b/users/tazjin/aoc2019/default.nix
index ce3146d1f7..a1798f4001 100644
--- a/users/tazjin/aoc2019/default.nix
+++ b/users/tazjin/aoc2019/default.nix
@@ -11,12 +11,16 @@ let
   getDay = f: head (matchSolution f);
 
   solutionFiles = filter (e: dir."${e}" == "regular" && isSolution e) (attrNames dir);
-  solutions = map (f: let day = getDay f; in {
-    name = day;
-    value = depot.nix.writeElispBin {
-      name = "aoc2019";
-      deps = p: with p; [ dash s ht ];
-      src = ./. + ("/" + f);
-    };
-  }) solutionFiles;
-in listToAttrs solutions
+  solutions = map
+    (f:
+      let day = getDay f; in {
+        name = day;
+        value = depot.nix.writeElispBin {
+          name = "aoc2019";
+          deps = p: with p; [ dash s ht ];
+          src = ./. + ("/" + f);
+        };
+      })
+    solutionFiles;
+in
+listToAttrs solutions
diff --git a/users/tazjin/aoc2020/default.nix b/users/tazjin/aoc2020/default.nix
index 7a7309ac5a..cd89da7de4 100644
--- a/users/tazjin/aoc2020/default.nix
+++ b/users/tazjin/aoc2020/default.nix
@@ -11,12 +11,16 @@ let
   getDay = f: head (matchSolution f);
 
   solutionFiles = filter (e: dir."${e}" == "regular" && isSolution e) (attrNames dir);
-  solutions = map (f: let day = getDay f; in depot.nix.writeElispBin {
-      name = day;
-      deps = p: with p; [ dash s ht p.f ];
-      src = ./. + ("/" + f);
-  }) solutionFiles;
-in pkgs.symlinkJoin {
+  solutions = map
+    (f:
+      let day = getDay f; in depot.nix.writeElispBin {
+        name = day;
+        deps = p: with p; [ dash s ht p.f ];
+        src = ./. + ("/" + f);
+      })
+    solutionFiles;
+in
+pkgs.symlinkJoin {
   name = "aoc2020";
   paths = solutions;
 }
diff --git a/users/tazjin/aoc2022/day1.rs b/users/tazjin/aoc2022/day1.rs
new file mode 100644
index 0000000000..078eb25f03
--- /dev/null
+++ b/users/tazjin/aoc2022/day1.rs
@@ -0,0 +1,27 @@
+// AoC 2022 - day 1.
+
+fn sum_elf(elf: &str) -> usize {
+    elf.lines()
+        .map(|s| s.parse::<usize>().expect("invalid input"))
+        .sum()
+}
+
+fn group_by_elf(input: &str) -> Vec<usize> {
+    input.rsplit("\n\n").map(sum_elf).collect()
+}
+
+fn top_elf(input: &str) -> usize {
+    group_by_elf(&input).into_iter().max().unwrap()
+}
+
+fn top_n_elves(n: usize, input: &str) -> usize {
+    let mut by_elf = group_by_elf(input);
+    by_elf.sort_by(|a, b| b.cmp(a)); // high->low
+    (by_elf[..n]).iter().sum()
+}
+
+fn main() {
+    let input = std::fs::read_to_string("input").expect("input should be in file named 'input'");
+    println!("top elf: {}", top_elf(&input));
+    println!("top 3 elves: {}", top_n_elves(3, &input));
+}
diff --git a/users/tazjin/aoc2023/day1.el b/users/tazjin/aoc2023/day1.el
new file mode 100644
index 0000000000..b1a7faff02
--- /dev/null
+++ b/users/tazjin/aoc2023/day1.el
@@ -0,0 +1,52 @@
+(require 's)
+(require 'f)
+
+;; task 1
+
+(defun digit-p (c)
+  (and (> c ?0)
+       (<= c ?9)))
+
+(defun aocd1-sum-values (lines)
+  (-sum
+   (-map (lambda (line)
+           (let ((digits (-filter #'digit-p (string-to-list line))))
+             (string-to-number (string (-first-item digits) (-last-item digits)))))
+         lines)))
+
+(let ((lines (s-lines (s-trim (f-read "~/Downloads/input.txt")))))
+  (aocd1-sum-values lines))
+
+;; task 2
+
+(defun replace-written-numbers (input)
+  (with-temp-buffer
+    (insert input)
+    (let ((start 1))
+      (while (< start (point-max))
+        (format-replace-strings
+         '(("oneight" . "18")
+           ("twone" . "21")
+           ("threeight" . "38")
+           ("fiveight" . "58")
+           ("sevenine" . "79")
+           ("eightwo" . "82")
+           ("eighthree" . "83")
+           ("nineight" . "98"))
+         nil start (min (+ 10 start) (point-max)))
+        (format-replace-strings
+         '(("one" . "1")
+           ("two" . "2")
+           ("three" . "3")
+           ("four" . "4")
+           ("five" . "5")
+           ("six" . "6")
+           ("seven" . "7")
+           ("eight" . "8")
+           ("nine" . "9"))
+         nil start (min (+ 5 start) (point-max)))
+        (setq start (1+ start))))
+    (buffer-string)))
+
+(let ((lines (s-lines (s-trim (f-read "~/Downloads/input.txt")))))
+  (aocd1-sum-values (-map #'replace-written-numbers lines)))
diff --git a/users/tazjin/aoc2023/day2.el b/users/tazjin/aoc2023/day2.el
new file mode 100644
index 0000000000..9374d7862c
--- /dev/null
+++ b/users/tazjin/aoc2023/day2.el
@@ -0,0 +1,64 @@
+(require 'dash)
+(require 's)
+(require 'f)
+
+(defvar aoc23-day2-example
+
+  "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
+Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
+Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
+Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
+Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green")
+
+;; part 1
+
+(cl-defstruct aoc23d2-set red green blue)
+
+(defun aoc23d2-parse-set (input)
+  (let ((set (make-aoc23d2-set :red 0 :green 0 :blue 0))
+        (colours (-map #'s-trim (s-split "," input))))
+    (cl-loop for colour in colours
+             do (pcase (s-split " " colour t)
+                  (`(,num "red") (setf (aoc23d2-set-red set) (string-to-number num)))
+                  (`(,num "green") (setf (aoc23d2-set-green set) (string-to-number num)))
+                  (`(,num "blue") (setf (aoc23d2-set-blue set) (string-to-number num)))))
+    set))
+
+(cl-defstruct aoc23d2-game id sets)
+
+(defun aoc23d2-parse-game (input)
+  (pcase-let* ((`(,id-str ,sets-str) (s-split-up-to ":" input 1 t))
+               (game-id (string-to-number (s-chop-left (length "Game ") id-str)))
+               (sets (-map #'aoc23d2-parse-set (s-split ";" sets-str t))))
+    (make-aoc23d2-game :id game-id :sets sets)))
+
+(defun aoc23d2-game-possible-p (game r g b)
+  (cl-every (lambda (set)
+              (and (<= (aoc23d2-set-red set) r)
+                   (<= (aoc23d2-set-green set) g)
+                   (<= (aoc23d2-set-blue set) b)))
+            (aoc23d2-game-sets game)))
+
+(let ((input (f-read "~/Downloads/input.txt")))
+  (-sum
+   (-map #'aoc23d2-game-id
+         (-filter (lambda (g) (aoc23d2-game-possible-p g 12 13 14))
+                  (-map #'aoc23d2-parse-game (s-lines (s-trim input)))))))
+
+;; part 2
+
+(defun aoc23d2-game-min-cubes-power (game)
+  (let ((r 0)
+        (g 0)
+        (b 0))
+    (-each (aoc23d2-game-sets game)
+      (lambda (set)
+        (setq r (max r (aoc23d2-set-red set)))
+        (setq g (max g (aoc23d2-set-green set)))
+        (setq b (max b (aoc23d2-set-blue set)))))
+    (* (max 1 r) (max 1 g) (max 1 b))))
+
+(let ((input (f-read "~/Downloads/input.txt")))
+  (-sum
+   (-map #'aoc23d2-game-min-cubes-power
+         (-map #'aoc23d2-parse-game (s-lines (s-trim input))))))
diff --git a/users/tazjin/aoc2023/day3.el b/users/tazjin/aoc2023/day3.el
new file mode 100644
index 0000000000..dd39c1b836
--- /dev/null
+++ b/users/tazjin/aoc2023/day3.el
@@ -0,0 +1,110 @@
+(defun aoc23d3-symbol-p (c)
+  (not (or (= c ? )
+           (and (>= c ?0)
+                (<= c ?9)))))
+
+(defun rectangle-for-bounds (bounds)
+  (let* ((start (save-excursion
+                     (goto-char (car bounds))
+                     (let ((col (current-column)))
+                       (forward-line -1)
+                       (move-to-column (max 0 (1- col))))
+                     (point)))
+         (end (save-excursion
+                (goto-char (cdr bounds))
+                (let ((col (current-column)))
+                  (forward-line 1)
+                  (move-to-column (1+ col)))
+                (point))))
+    (list start end)))
+
+(defun get-machine-part ()
+  (interactive)
+  (when-let* ((num-raw (number-at-point))
+              (num (abs num-raw))
+              ;; handles negative number edge case (bounds contain the `-')
+              (bounds-raw (bounds-of-thing-at-point 'number))
+              (bounds (if (< num-raw 0)
+                          (cons (1- (car bounds-raw)) (cdr bounds-raw))
+                        bounds-raw))
+              (rectangle (rectangle-for-bounds bounds))
+              (neighbours (apply #'concat
+                                 (apply #'extract-rectangle rectangle))))
+    (if (-any #'aoc23d3-symbol-p (string-to-list neighbours))
+        (cons num rectangle)
+      (cons nil rectangle))))
+
+
+(defun find-machine-parts (input)
+  (with-temp-buffer
+    (insert input)
+    (goto-char (point-min))
+    (save-excursion
+      (replace-string "." " "))
+
+    (cl-loop while (forward-word)
+             for result = (get-machine-part)
+             when (car result) collect (car result))))
+
+
+;; debugging
+
+(defvar aoc23d3-example "467..114..
+...*......
+..35..633.
+......#...
+617*......
+.....+.58.
+..592.....
+......755.
+...$.*....
+.664.598..")
+
+(defvar aoc23d3-example2 "12.......*..
++.........34
+.......-12..
+..78........
+..*....60...
+78..........
+.......23...
+....90*12...
+............
+2.2......12.
+.*.........*
+1.1.......56")
+
+(defvar aoc23d3-example3 "243.
+..*.
+....")
+
+(defun aoc23d3-debug (p)
+  "Interactive debugger for the solution, can be bound to a key in
+an input buffer. Dots should already have been replaced with
+spaces."
+  (interactive "P")
+  (unless p
+    (goto-char aoc23d3-last))
+  (rectangle-mark-mode 1)
+  (forward-word)
+  (setq aoc23d3-last (point))
+  (pcase (get-machine-part)
+    (`(nil ,b ,e) (progn (set-mark b)
+                          (goto-char e)
+                          (set-face-attribute 'region nil :background "#FAA0A0")))
+    (`(,num ,b ,e) (progn (set-mark b)
+                          (goto-char e)
+                          (set-face-attribute 'region nil :background "#d1ffbd")))
+    (other (deactivate-mark))))
+
+(cl-assert (= 4361 (-sum (find-machine-parts aoc23d3-example))) nil
+           "example from website is working")
+
+(cl-assert (= 413 (-sum (find-machine-parts aoc23d3-example2))) nil
+           "example from subreddit is working")
+
+(cl-assert (= 243 (-sum (find-machine-parts aoc23d3-example3))) nil
+           "example from telegram is working")
+
+;; day 1 (incomplete)
+
+(-sum (find-machine-parts (s-trim (f-read "~/Downloads/input.txt"))))
diff --git a/users/tazjin/blog/default.nix b/users/tazjin/blog/default.nix
index 6ac89e4665..60c79f0941 100644
--- a/users/tazjin/blog/default.nix
+++ b/users/tazjin/blog/default.nix
@@ -8,6 +8,7 @@ let
   config = {
     name = "tazjin's blog";
     baseUrl = "https://tazj.in/blog";
+    staticUrl = "https://tazj.in/static/";
 
     footer = ''
       <p class="footer">
@@ -21,9 +22,9 @@ let
 
   inherit (depot.web.blog) post includePost renderPost;
 
-  posts = filter includePost (list post (import ./posts.nix));
+  posts = list post (import ./posts.nix);
 
-  rendered = pkgs.runCommandNoCC "tazjins-blog" {} ''
+  rendered = pkgs.runCommand "tazjins-blog" { } ''
     mkdir -p $out
 
     ${lib.concatStringsSep "\n" (map (post:
@@ -31,13 +32,19 @@ let
     ) posts)}
   '';
 
-in {
-  inherit posts rendered config;
+in
+{
+  inherit rendered config;
+
+  # Filter unlisted posts from the index
+  posts = filter includePost posts;
 
   # Generate embeddable nginx configuration for redirects from old post URLs
-  oldRedirects = lib.concatStringsSep "\n" (map (post: ''
-    location ~* ^(/en)?/${post.oldKey} {
-      return 301 https://tazj.in/blog/${post.key};
-    }
-  '') (filter (hasAttr "oldKey") posts));
+  oldRedirects = lib.concatStringsSep "\n" (map
+    (post: ''
+      location ~* ^(/en)?/${post.oldKey} {
+        return 301 https://tazj.in/blog/${post.key};
+      }
+    '')
+    (filter (hasAttr "oldKey") posts));
 }
diff --git a/users/tazjin/blog/posts.nix b/users/tazjin/blog/posts.nix
index b43598d013..a95a50d766 100644
--- a/users/tazjin/blog/posts.nix
+++ b/users/tazjin/blog/posts.nix
@@ -1,6 +1,19 @@
 # This file defines all the blog posts.
 [
   {
+    key = "reliably-switch-buffers";
+    title = "Π—Π°Ρ‡Π΅ΠΌ reliably-switch-buffers?";
+    content = ./posts/reliably-switch-buffers.md;
+    date = 1692882000;
+  }
+  {
+    key = "tvix-eval-talk-2023";
+    title = "[Π΄ΠΎΠΊΠ»Π°Π΄] tvix-eval, имплСмСнтация языка Nix Π½Π° Rust";
+    date = 1694102400;
+    content = ./posts/tvix-eval-talk-2023.md;
+    tagfilter = false;
+  }
+  {
     key = "emacs-is-underrated";
     title = "Emacs is the most underrated tool";
     date = 1581286656;
@@ -37,7 +50,7 @@
     key = "the-smu-problem";
     title = "The SMU-problem of messaging apps";
     date = 1450354078;
-    content =./posts/the-smu-problem.md;
+    content = ./posts/the-smu-problem.md;
     oldKey = "1450354078";
   }
   {
@@ -46,6 +59,7 @@
     date = 1423995834;
     content = ./posts/sick-in-sweden.md;
     oldKey = "1423995834";
+    listed = false;
   }
   {
     key = "nsa-zettabytes";
@@ -54,4 +68,11 @@
     content = ./posts/nsa-zettabytes.md;
     oldKey = "1375310627";
   }
+  {
+    key = "thoughts";
+    title = "Some thoughts";
+    date = 1665095948;
+    content = ./posts/thoughts.md;
+    listed = false;
+  }
 ]
diff --git a/users/tazjin/blog/posts/best-tools.md b/users/tazjin/blog/posts/best-tools.md
index e4bad8f4cd..afe61767b1 100644
--- a/users/tazjin/blog/posts/best-tools.md
+++ b/users/tazjin/blog/posts/best-tools.md
@@ -42,19 +42,32 @@ list. I own several of them and there will probably be more in the future. They
 last forever and your wrists will thank you in the future, even if you do not
 suffer from RSI yet.
 
+Kinesis have announced a split version of the Advantage. Once that is
+easily available, I will buy one and evaluate it.
+
 [advantage]: https://kinesis-ergo.com/shop/advantage2/
 
 ## Speakers
 
-The speakers that I've hooked up to my audio setup (including both record player
-& Chromecast / TV) are the [Teufel Motiv 2][motiv-2]. I've had these for over a
-decade and they're incredibly good, but unfortunately Teufel no longer makes
-them.
+There are two sets of speakers I use, unfortunately one pair has been in storage
+since I left the UK.
+
+My original favourite speakers are the [Teufel Motiv 2][motiv-2], usually hooked
+up to a Chromecast and a record player. I've had these for over a decade and
+they're incredibly good, but unfortunately Teufel no longer makes them. Mine are
+currently in a warehouse somewhere in London, and I don't know when I will see
+them again ...
 
 It's possible to grab a pair on eBay occasionally, so keep an eye out if you're
 interested!
 
+In my Moscow flat, I have a pair of [Wharfedale Diamond 12][diamond-12]
+connected to a Philips amplifier older than myself. These provide an excellent,
+balanced, "Wharfedale-sound". Some people find it needs some getting used to,
+but don't want to go back after that initial phase.
+
 [motiv-2]: https://www.teufelaudio.com/uk/pc/motiv-2-p167.html
+[diamond-12]: https://www.wharfedaleusa.com/collections/diamond-12
 
 ## Headphones
 
@@ -85,40 +98,38 @@ flat.
 
 ## Phone
 
-The best phone I have used in recent years is the [iPhone SE][se]. It was the
-*last* phone that had a reasonable size (up to 4") *and* a 3.5mm headphone jack.
+My current phone is the [Palm phone][palm-phone]. It's basically the smallest
+smartphone on the market at about the size of a credit card. I picked this phone
+because I am trying to not use a phone anymore, but some things simply require a
+smartphone.
 
-Unfortunately, it runs iOS. Despite owning a whole bunch of SEs, I have finally
-moved on to an Android phone that is only moderately larger (still by an
-annoying amount), but does at least have a headphone jack: The [Samsung Galaxy
-S10e][s10e].
+The Palm has terrible battery life but is otherwise great. I always have it in
+power saving mode and only turn on receiving calls when I'm expecting a call,.
 
-It has pretty good hardware and I can almost reach 70% of the screen, which is
-better than other phones out there right now. Unfortunately it runs Samsung's
-impossible-to-remove bloatware on top of Android, but that is still less
-annoying to use than iOS.
+Previous phones I used and liked were:
 
-QUESTION: This is the only item on this list for which I am actively seeking a
-replacement, so if you have any tips about new phones that might fit these
-criteria that I've missed please let me know!
+* [Unihertz Atom L][atom-l] - small-screen, rugged phone with headphone jack
+* Original [iPhone SE][se] - perfect-sized phone, unfortunately with iOS
 
+[palm-phone]: https://palm.com/pages/product
+[atom-l]: https://www.unihertz.com/products/atom-l
 [se]: https://en.wikipedia.org/wiki/IPhone_SE
-[s10e]: https://www.phonearena.com/phones/Samsung-Galaxy-S10e_id11114
 
 # Other stuff
 
 ## Toothbrush
 
-The [Philips Sonicare][sonicare] (note: link goes to a newer generation than
-mine) is excellent and well worth its money.
+The [Philips Sonicare][sonicare] is excellent and well worth its price.
 
 I've had it for a few years and whereas I occasionally had minor teeth issues
 before, they seem to be mostly gone now. According to my dentist the state of my
 teeth is now usually pretty good and I draw a direct correlation back to this
 thing.
 
-The newer generations come with flashy features like apps and probably more
-LEDs, but I suspect that those can just be ignored.
+It has an app and stuff, but I just ignore that.
+
+I first got one of these in about 2014, and it lasted until 2020, at which point
+I upgraded to whatever the current model was.
 
 [sonicare]: https://www.philips.co.uk/c-m-pe/electric-toothbrushes
 
@@ -126,10 +137,12 @@ LEDs, but I suspect that those can just be ignored.
 
 The [Philipps SensoTouch 3D][sensotouch] is excellent. Super-comfortable close
 face shave in no time and leaves absolutely no mess around, as far as I can
-tell! I've had this for ~5 years and it's not showing any signs of aging yet.
+tell! I've had this for ~7 years and it's not showing any serious signs of aging
+yet.
 
-Another bonus is that its battery time is effectively infinite. I've never had
-to worry when bringing it on a longer trip!
+Another bonus is that its battery time is effectively infinite (in the order of
+months of use per charge). I've never had to worry when bringing it on a longer
+trip!
 
 [sensotouch]: https://www.philips.co.uk/c-p/1250X_40/norelco-sensotouch-3d-wet-and-dry-electric-razor-with-precision-trimmer
 
@@ -146,15 +159,26 @@ The one I settled on is the [Waterfield Muzetto][muzetto] leather bag. It's one
 of those things that comes with a bit of a price tag attached, but it's well
 worth it!
 
+**Unfortunately**, just like my speakers, this bag is now in storage somewhere
+in the UK since I left the country.
+
+After moving to Moscow I quickly ran into the same problem as in London when
+using the metro, but getting another Muzetto was kind of impractical.
+
+I couldn't find any other vertical messenger bags that I liked, and ended up
+going for a more traditional one: The [Brialdi Ostin][ostin].
+
 [muzetto]: https://www.sfbags.com/collections/shoulder-messenger-bags/products/muzetto-leather-bag
+[ostin]: https://www.brialdi.ru/shop/handbags/brialdi_ostin_brown/
 
 ## Wallet
 
-My wallet is the [Bellroy Slim Sleeve][slim-sleeve]. I don't carry cash unless
-I'm attending an event in Germany and this wallet fits that lifestyle perfectly.
+My wallet is the [Bellroy Coin Wallet][coin-wallet]. It's the slimmest wallet I
+could find that can deal with the volume of cards I (have to) carry around, as
+well as with cash.
 
-It's near indestructible, looks great, is very slim and fits a ton of cards,
-business cards, receipts and whatever else you want to be lugging around with
-you!
+I've used Bellroy wallets for a long time, with the [Slim Sleeve][slim-sleeve]
+serving me in the days when I lived in no-cash countries.
 
+[coin-wallet]: https://bellroy.com/products/coin-wallet
 [slim-sleeve]: https://bellroy.com/products/slim-sleeve-wallet/default/charcoal
diff --git a/users/tazjin/blog/posts/nixery-layers.md b/users/tazjin/blog/posts/nixery-layers.md
index 3f25ceadce..26526d11b5 100644
--- a/users/tazjin/blog/posts/nixery-layers.md
+++ b/users/tazjin/blog/posts/nixery-layers.md
@@ -260,13 +260,13 @@ TIP: This is implemented in [popcount][] in Nixery.
 Hopefully this detailed design review was useful to you. You can also watch [my
 NixCon talk][talk] about Nixery for a review of some of this, and some demos.
 
-[Nixery]: https://github.com/google/nixery
+[Nixery]: https://cs.tvl.fyi/depot/-/tree/tools/nixery
 [grhmc]: https://grahamc.com/blog/nix-and-layered-docker-images
 [Nix]: https://nixos.org/nix
 [registry protocols]: https://github.com/opencontainers/distribution-spec/blob/master/spec.md
 [nixery.dev]: https://nixery.dev
 [dominator trees]: https://en.wikipedia.org/wiki/Dominator_(graph_theory)
 [gonum/graph]: https://godoc.org/gonum.org/v1/gonum/graph
-[layers.go]: https://github.com/google/nixery/blob/master/builder/layers.go
-[popcount]: https://github.com/google/nixery/tree/master/popcount
+[layers.go]: https://cs.tvl.fyi/depot/-/blob/tools/nixery/layers/layers.go
+[popcount]: https://cs.tvl.fyi/depot/-/tree/tools/nixery/popcount
 [talk]: https://www.youtube.com/watch?v=pOI9H4oeXqA
diff --git a/users/tazjin/blog/posts/reliably-switch-buffers.md b/users/tazjin/blog/posts/reliably-switch-buffers.md
new file mode 100644
index 0000000000..ec56c4b2d0
--- /dev/null
+++ b/users/tazjin/blog/posts/reliably-switch-buffers.md
@@ -0,0 +1,18 @@
+Π’Ρ‡Π΅Ρ€Π° Π²Π΅Ρ‡Π΅Ρ€ΠΎΠΌ написал Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠ°Ρ‚Ρ‡ΠΈ для ΠΌΠΎΠ΅Π³ΠΎ emacs-ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°. Π˜Ρ… Π½Π° самом Π΄Π΅Π»Π΅ Π΄Π°Π²Π½ΠΎ ΡƒΠΆΠ΅ Ρ…ΠΎΡ‚Π΅Π» Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ, ΠΎΠ½ΠΈ Ρ€Π΅ΡˆΠ°ΡŽΡ‚ малСнькиС ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠ½Π΅ постоянно мСшали. Об ΠΎΠ΄Π½ΠΎΠΉ ΠΈΠ· ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ я Ρ…ΠΎΡ‡Ρƒ Ρ€Π°ΡΡΠΊΠ°Π·Π°Ρ‚ΡŒ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ ΠΎΠ½Π° ΠΏΡ€ΠΈΠ²Π΅Π»Π° ΠΊ Ρ‚ΠΎΠΌΡƒ, Ρ‡Ρ‚ΠΎ "ΠΏΠΎΡ€ΠΎΠ³ раздраТСния" Π±Ρ‹Π» пСрСступлСн.
+
+Emacs Ρƒ мСня основная Ρ‡Π°ΡΡ‚ΡŒ своСй Ρ€Π°Π±ΠΎΡ‡Π΅ΠΉ срСды. Он Ρƒ мСня являСтся, ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ, тСкстовым Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€ΠΎΠΌ, Π½ΠΎ ΠΈ Π΅Ρ‰Π΅ ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ€ΠΎΠΌ ΠΎΠΊΠΎΠ½, мэйл-ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠΌ, Ρ‡Π°Ρ‚-ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠΌ ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ Π΄Ρ€ΡƒΠ³ΠΎΠ³ΠΎ.
+
+Π’Π½ΡƒΡ‚Ρ€ΠΈ emacs Π΅ΡΡ‚ΡŒ концСпция "Π±ΡƒΡ„Π΅Ρ€ΠΎΠ²", ΠΎΠ΄ΠΈΠ½ Π±ΡƒΡ„Π΅Ρ€ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΎΠ΄ΠΈΠ½ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΉ Ρ„Π°ΠΉΠ» Π² тСкстовом Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€Π΅, ΠΎΠ΄ΠΈΠ½ Ρ‡Π°Ρ‚ Π½Π° Π’Π΅Π»Π΅Π³Ρ€Π°ΠΌΠ΅, ΠΈΠ»ΠΈ ΠΎΠ΄Π½ΠΎ дСсктопноС ΠΎΠΊΠ½ΠΎ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π±Ρ€Π°ΡƒΠ·Π΅Ρ€). Навигация ΠΌΠ΅ΠΆΠ΄Ρƒ Π½ΠΈΠΌΠΈ осущСствляСтся с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ `switch-to-buffer` (ΠΈΠ»ΠΈ ΠΊΠΎΠ΅-ΠΊΠ°ΠΊΠΈΡ… Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ², Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ `ivy-switch-buffer`, `helm-switch-buffer` ΠΈ Ρ‚Π°ΠΊ Π΄Π°Π»Π΅Π΅). Π‘ΡƒΡ„Π΅Ρ€ - Π½Π° сторонС emacs-lisp являСтся ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ с Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌΠΈ полями. Одно ΠΈΠ· Π½ΠΈΡ…: `buffer-name`.
+
+Π£ всСх buffer-switch ΠΊΠΎΠΌΠ°Π½Π΄ Π΅ΡΡ‚ΡŒ одинаковая ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: Они Π±Π΅Ρ€ΡƒΡ‚ список Π±ΡƒΡ„Π΅Ρ€ΠΎΠ² ΠΈΠ· emacs, ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‚ *ΠΈΠΌΠ΅Π½Π°* Π±ΡƒΡ„Π΅Ρ€ΠΎΠ² ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŽ, ΠΈ Π² Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π°ΡŽΡ‚ Π²Ρ‹Π±Ρ€Π°Π½Π½ΠΎΠ΅ *имя*. Π—Π°Ρ‚Π΅ΠΌ ΠΎΠ½ΠΈ просят emacs ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ Π±ΡƒΡ„Π΅Ρ€ с этим ΠΈΠΌΠ΅Π½Π΅ΠΌ.
+
+ΠšΡ‚ΠΎ-Ρ‚ΠΎ Π½Π°Π²Π΅Ρ€Π½ΠΎ ΡƒΠΆΠ΅ понял какая Ρ‚ΡƒΡ‚ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°. ИмСни Π±ΡƒΡ„Π΅Ρ€ΠΎΠ² ΠΌΠΎΠ³ΡƒΡ‚ ΠΌΠ΅Π½ΡΡ‚ΡŒΡΡ, ΠΈ Π΄Π°, Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΌΠΎΠ³ΡƒΡ‚, Π½ΠΎ ΠΈ Π΄Π΅Π»Π°ΡŽΡ‚! НапримСр, Π’Π΅Π»Π΅Π³Ρ€Π°ΠΌ-ΠΊΠ»ΠΈΠ΅Π½Ρ‚ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ каличСство Π½Π΅ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½Π½Ρ‹Ρ… сообщСний Π² Π½Π°Π·Π²Π°Π½ΠΈΠΈ, ΠΎΠΊΠ½ΠΎ с ЯндСкс ΠœΡƒΠ·Ρ‹ΠΊΠΎΠΉ мСняСт названия ΠΏΠΎ Ρ‚Ρ€Π΅ΠΊΡƒ, ΠΈ Ρ‚Π°ΠΊ Π΄Π°Π»Π΅Π΅. ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ΡΡ довольно часто такая ситуация, Ρ‡Ρ‚ΠΎ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ мСняСтся ΠΏΡ€ΠΈ Π²Ρ‹Π±ΠΎΡ€Π΅ Π±ΡƒΡ„Π΅Ρ€Π°, ΠΈ `switch-to-buffer` большС Π½Π΅ Π½Π°ΠΉΠ΄Π΅Ρ‚ Π²Ρ‹Π±Ρ€Π°Π½Π½Ρ‹ΠΉ Π±ΡƒΡ„Π΅Ρ€ ΠΈ просто ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅Ρ‚ Π½ΠΎΠ²Ρ‹ΠΉ, пустой Π±ΡƒΡ„Π΅Ρ€ с старым Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ! Когда Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‹Π²Π°Π»ΠΈ эти ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ Π² emacs (Π΄Π°, это ΡΠΎΠ²Π΅Ρ€ΡˆΠ΅Π½Π½ΠΎ Π΄Π°Π²Π½ΠΎ, Π³Π΄Π΅-Ρ‚ΠΎ Π² 70Ρ…/80Ρ…, Π±ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²Π° нас ΠΏΠΎΠΊΠ° Π½Π΅ Π±Ρ‹Π»ΠΎ Ρ‚ΠΎΠ³Π΄Π°!), ΠΎΠ½ΠΈ Π½ΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ ΡΡ‚Π°Π»ΠΊΠΈΠ²Π°Π»ΠΈΡΡŒ с Ρ‚Π°ΠΊΠΈΠΌΠΈ ситуациями, ΠΈ это Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Ρ‚ΠΎΠ³Π΄Π° Ρ…ΠΎΡ€ΠΎΡˆΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Π»ΠΎ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ большС просто Π½Π΅ Π°Π΄Π΅ΠΊΠ²Π°Ρ‚Π½ΠΎ.
+
+Ѐикс Π±Ρ‹Π» Π½Π΅ ΠΎΡ‡Π΅Π½ΡŒ слоТным. ВмСсто списка ΠΈΠΌΠ΅Π½ Π±ΡƒΡ„Π΅Ρ€ΠΎΠ² создаю alist с Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ ΠΈ *с самим ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ*, ΠΈ послС Π²Ρ‹Π±ΠΎΡ€Π° Π±ΡƒΡ„Π΅Ρ€Π° с списка ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽ ΠΈΠΌΠ΅Π½Π½ΠΎ этот ΠΎΠ±ΡŠΠ΅ΠΊΡ‚, Π° Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π΅Π³ΠΎ Π½Π°Π·Π²Π°Π½ΠΈΠ΅, Π² Ρ„ΡƒΠ½ΠΊΡ‚Ρ†ΠΈΡŽ, которая ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅Ρ‚ Π±ΡƒΡ„Π΅Ρ€.
+
+ΠšΠΎΠΌΠΌΠΈΡ‚ с этой Π½ΠΎΠ²ΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ здСсь: cl/9147
+Π‘ΠΎΠ²Π΅Ρ‚ΡƒΡŽ Π΅Ρ‘ особСнно всСм ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌ EXWM!
+
+Для мСня это настоящСС ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΠ΅ ΠΆΠΈΠ·Π½ΠΈ. ΠšΠΎΠ½Π΅Ρ‡Π½ΠΎ, это странно Π·Π²ΡƒΡ‡ΠΈΡ‚, Π½ΠΎ Π΄Π°ΠΆΠ΅ Ссли Π±Ρ‹ Ρƒ мСня Π±Ρ‹Π»Π° такая ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° всСго Ρ€Π°Π· Π² дСнь, это ΠΊΠ°ΠΊΠΈΠΌ-Ρ‚ΠΎ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΏΡ€ΠΈΠ²Π΅Π»ΠΎ Π±Ρ‹ ΠΊ ΡƒΡ…ΡƒΠ΄ΡˆΠ΅Π½ΠΈΡŽ ΠΌΠΎΠ΅Π³ΠΎ настроСния. Как малСнький камСшСк Π² Ρ‚Π²ΠΎΠ΅ΠΌ Π±ΠΎΡ‚ΠΈΠ½ΠΊΠ΅.
+
+Π’Ρ‹Π½ΡŒΡ‚Π΅ ΠΊΠ°ΠΌΠ½ΠΈ ΠΈΠ· своих Π±ΠΎΡ‚ΠΈΠ½ΠΎΠΊ!
diff --git a/users/tazjin/blog/posts/reversing-watchguard-vpn.md b/users/tazjin/blog/posts/reversing-watchguard-vpn.md
index 8968dc8645..e000d7a764 100644
--- a/users/tazjin/blog/posts/reversing-watchguard-vpn.md
+++ b/users/tazjin/blog/posts/reversing-watchguard-vpn.md
@@ -1,5 +1,5 @@
 TIP: WatchGuard has
-[responded](https://www.reddit.com/r/netsec/comments/5tg0f9/reverseengineering_watchguard_mobile_vpn/dds6knx/)
+[responded](https://web.archive.org/web/20230326041952/https://www.reddit.com/r/netsec/comments/5tg0f9/reverseengineering_watchguard_mobile_vpn/dds6knx/)
 to this post on Reddit. If you haven\'t read the post yet I\'d recommend
 doing that first before reading the response to have the proper context.
 
diff --git a/users/tazjin/blog/posts/thoughts.md b/users/tazjin/blog/posts/thoughts.md
new file mode 100644
index 0000000000..7ce23f9c87
--- /dev/null
+++ b/users/tazjin/blog/posts/thoughts.md
@@ -0,0 +1,142 @@
+<!--
+
+  This file contains a bunch of random thoughts I don't want to lose,
+  often resulting from conversation with other people, but that are
+  too far removed from what most people can relate to for me to just
+  publish them. Sometimes it's convenient to be able to share them,
+  though.
+
+  For that reason, if you stumble upon this file without me having
+  linked it to you intentionally, feel free to read it but keep the
+  sharing to a minimum (though do feel free to share the thoughts
+  themselves, of course).
+
+-->
+WARNING: This is not intended for a large audience. If you stumble
+upon this page by chance, please keep the sharing to a minimum.
+
+TIP: It's always work-in-progress. Things come and go. Or change. Who
+knows?
+
+---------
+
+### Three things
+
+*[mid/late 2020]*
+
+All things in the universe take the shape of one of approximately
+three things. If you had Hoogle for the entire universe, you'd
+probably find that one of them is `fmap`.
+
+There might be a few more, or a few less (or some may have been
+deprecated), but you get the idea. I guess [five][] would be a good
+number.
+
+[five]: https://principiadiscordia.com/book/23.php
+
+----------------------
+
+### Free energy principle
+
+*[mid/late 2020]*
+
+Karl Friston wrote:
+
+> The free-energy principle says that any self-organizing system that
+> is at equilibrium with its environment must minimize its free
+> energy.
+
+Or, somewhat paraphrased:
+
+> Any Markov blanket capable of modeling its environment aims to
+> reduce its level of surprise by either adapting its model, or
+> through other action.
+
+Seems reasonable to me.
+
+### More bizarre universe
+
+*[many years ago]*
+
+Douglas Adams wrote:
+
+> There is a theory which states that if ever anyone discovers exactly
+> what the Universe is for and why it is here, it will instantly
+> disappear and be replaced by something even more bizarre and
+> inexplicable. There is another theory which states that this has
+> already happened.
+
+### Alpha decay
+
+*[late 2022]*
+
+Finance people say:
+
+> Alpha Decay is commonly referred to as the loss of prediction power
+> of a trading strategy over time. As a consequence, the profitability
+> of a strategy tends to gradually decrease. Given enough time, the
+> strategy converges to having no superior predictive power and
+> returns when compared to a suitable benchmark.
+
+A market is a big optimiser. Any successful trading strategy adds
+friction in a place that the optimiser wants to remove.
+
+Alpha decay is unavoidable without changing and adapting the strategy.
+
+### Optimising universe
+
+*[late 2022]*
+
+*(thanks edef for helping me think through this one!)*
+
+Assume that the universe acts as a giant optimiser, and consider that
+the three things above are related and specialisations of more generic
+ideas:
+
+1. Every delineable entity in the universe (i.e. every *Markov
+   blanket*) attempts to reduce its level of surprise (the free energy
+   principle).
+
+2. The universe needs replacement (a more bizarre universe) if global
+   surprise drops to a minimum[^heat].
+
+3. Without improvement that outpaces the optimiser of the universe,
+   any strategy leading to (2) will get eroded by alpha decay long
+   before.
+
+4. We don't know if it is possible to outpace the optimiser from
+   within.
+
+On a personal note, it seems to me that achieving (2) is likely
+undesirable. It probably takes god[^god] a lot of resources to create
+an ever more complex universe and this process might be much less
+enjoyable than "running" (for lack of a better word) a universe. Under
+this assumption, a universe that achieves (2) faster than others might
+be a failure, and on a higher level conditions leading to its creation
+might be subject to another optimiser.
+
+Or it could be the other way around, but this seems more likely to me
+personally.
+
+### Superintelligence
+
+*[late 2022]*
+
+Under the previous assumption, achieving superintelligence is likely a
+bad idea for anyone feeling some kind of attachment to *this*
+universe.
+
+Or it might be the exact opposite, but I don't think so.
+
+-------------------------------
+
+[^heat]: Note that this is consistent with the heat death of the
+    universe.
+
+[^god]: I'm using the word "god" as the best English approximation of
+    a concept that different religions and philosophies all attempt to
+    approach. I think that for many cognitive purposes, an
+    anthropomorphised idea (as in the abrahamic religions) is useful,
+    but ideas from some Eastern religions or modern philosophers like
+    Bach or Watts are likely more aligned with the "nature of things"
+    as such.
diff --git a/users/tazjin/blog/posts/tvix-eval-talk-2023.md b/users/tazjin/blog/posts/tvix-eval-talk-2023.md
new file mode 100644
index 0000000000..4a0ec56881
--- /dev/null
+++ b/users/tazjin/blog/posts/tvix-eval-talk-2023.md
@@ -0,0 +1,19 @@
+7 сСнтября я выступил с Π΄ΠΎΠΊΠ»Π°Π΄ΠΎΠΌ ΠΏΡ€ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ языка Nix Π½Π° Rust, Π½Π°
+[Московском Rust-ΠΌΠΈΡ‚Π°ΠΏΠ΅][rustmsk] / [Московском ΠΊΠ»ΡƒΠ±Π΅
+программистов][progmsk].
+
+<iframe width="800" height="500" src="https://www.youtube.com/embed/7zS2_ZhwPfY?start=4013" title="RUST - соврСмСнный язык программирования" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
+
+Π’ΠΎΡ‚ всС связанныС с Π½ΠΈΠΌ ссылки, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ интСрСсны:
+
+* [Tvix](https://tvix.dev), Π³Π»Π°Π²Π½Ρ‹ΠΉ сайт ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°
+* [TVL](https://tvl.fyi), нашС ΠΎΠ½Π»Π°ΠΉΠ½-сообщСство
+* [Tvixbolt](https://bolt.tvix.dev/), наш "godbolt" для tvix
+* [MMTk](https://www.mmtk.io/), Rust-Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° с ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°ΠΌΠΈ для garbage-collection
+* [Π˜Π½Ρ‚Π΅Ρ€Π²ΡŒΡŽ / Π΄ΠΎΠΊΠ»Π°Π΄](https://www.youtube.com/live/0Lhahzs-Wos?si=BlFDVBUPsIpHg0p5), Nix -- Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠ°ΠΊΠ΅Ρ‚Π½Ρ‹ΠΉ ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ€
+* [NixCon 2023](https://2023.nixcon.org/)
+* [Yew](https://yew.rs/), WASM-Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ для Rust
+* [tazlog](https://t.me/tazlog), ΠΌΠΎΠΉ ΠΊΠ°Π½Π°Π» Π½Π° Π’Π΅Π»Π΅Π³Π΅
+
+[rustmsk]: https://t.me/ruRust_msk
+[progmsk]: https://prog.msk.ru/
diff --git a/users/tazjin/chase-geese/default.nix b/users/tazjin/chase-geese/default.nix
new file mode 100644
index 0000000000..3549f75868
--- /dev/null
+++ b/users/tazjin/chase-geese/default.nix
@@ -0,0 +1,13 @@
+# Helpers for mounting GeeseFS into the right place.
+{ depot, pkgs, ... }:
+
+pkgs.writeShellScriptBin "chase-geese" ''
+  set -ueo pipefail
+
+  echo "Fetching credentials ..."
+  eval $(pass show keys/tazjin-geesefs)
+
+  echo "Mounting the cloud ..."
+  mkdir -p ~/cloud
+  ${depot.third_party.geesefs}/bin/geesefs tazjins-files ~/cloud
+''
diff --git a/users/tazjin/default.nix b/users/tazjin/default.nix
new file mode 100644
index 0000000000..1b68b7127a
--- /dev/null
+++ b/users/tazjin/default.nix
@@ -0,0 +1,30 @@
+# //users/tazjin-specific CI configuration.
+{ depot, pkgs, ... }:
+
+let
+  rustfmt = pkgs.writeShellScript "rustfmt-tazjin" ''
+    ${pkgs.fd}/bin/fd -e rs | \
+      ${pkgs.ripgrep}/bin/rg 'users/tazjin' | \
+      xargs ${pkgs.rustfmt}/bin/rustfmt --check --config-path users/tazjin
+  '';
+
+in
+depot.nix.readTree.drvTargets {
+  rustfmt = rustfmt.overrideAttrs (_: {
+    # rustfmt not respecting config atm, disable
+    meta.ci.skip = true;
+
+    meta.ci.extraSteps.rustfmt = {
+      command = rustfmt;
+    };
+  });
+
+  # Use a screen lock command that resets the keyboard layout
+  # before locking, to avoid locking me out when the layout is
+  # in Russian.
+  screenLock = pkgs.writeShellScriptBin "tazjin-screen-lock" ''
+    ${pkgs.xorg.setxkbmap}/bin/setxkbmap us
+    ${pkgs.xorg.setxkbmap}/bin/setxkbmap -option caps:super
+    exec ${pkgs.xsecurelock}/bin/xsecurelock
+  '';
+}
diff --git a/users/tazjin/dns/default.nix b/users/tazjin/dns/default.nix
index da92b88bea..6ff6cc06e2 100644
--- a/users/tazjin/dns/default.nix
+++ b/users/tazjin/dns/default.nix
@@ -2,11 +2,12 @@
 { depot, pkgs, ... }:
 
 let
-  checkZone = zone: file: pkgs.runCommandNoCC "${zone}-check" {} ''
+  checkZone = zone: file: pkgs.runCommand "${zone}-check" { } ''
     ${pkgs.bind}/bin/named-checkzone -i local ${zone} ${file} | tee $out
   '';
 
-in depot.nix.readTree.drvTargets {
-  kontemplate-works = checkZone "kontemplate.works"./kontemplate.works.zone;
+in
+depot.nix.readTree.drvTargets {
+  kontemplate-works = checkZone "kontemplate.works" ./kontemplate.works.zone;
   tazj-in = checkZone "tazj.in" ./tazj.in.zone;
 }
diff --git a/users/tazjin/docs/install-zfs.md b/users/tazjin/docs/install-zfs.md
new file mode 100644
index 0000000000..415af30fd4
--- /dev/null
+++ b/users/tazjin/docs/install-zfs.md
@@ -0,0 +1,116 @@
+Current steps for my NixOS-on-ZFS installs with impermanence.
+
+## Target layout (example from tverskoy):
+
+Partitioning:
+
+```
+nvme0n1     259:0    0 238.5G  0 disk
+β”œβ”€nvme0n1p1 259:1    0   128M  0 part /boot (type: EFI system)
+└─nvme0n1p2 259:2    0 238.3G  0 part       (type: Solaris root)
+```
+
+ZFS layout:
+
+```
+NAME                   USED  AVAIL     REFER  MOUNTPOINT
+zpool                  212G  19.0G      248K  /zpool
+zpool/ephemeral        668M  19.0G      192K  /zpool/ephemeral
+zpool/ephemeral/home   667M  19.0G      667M  legacy
+zpool/local           71.3G  19.0G      192K  /zpool/local
+zpool/local/nix       71.3G  19.0G     71.3G  legacy
+zpool/safe             140G  19.0G      192K  /zpool/safe
+zpool/safe/depot       414M  19.0G      414M  legacy
+zpool/safe/persist     139G  19.0G      139G  legacy
+```
+
+With reset-snapshots:
+
+```
+NAME                                USED  AVAIL     REFER  MOUNTPOINT
+zpool/ephemeral/home@blank          144K      -      192K  -
+zpool/ephemeral/home@tazjin-clean   144K      -      200K  -
+```
+
+Legacy mountpoints are used because the NixOS wiki advises that using
+ZFS own mountpoints might lead to issues with the mount order during
+boot.
+
+## Install steps
+
+1. First, get internet.
+
+2. Use `fdisk` to set up the partition layout above (fwiw, EFI type
+   should be `1`, Solaris root should be `66`).
+
+3. Format the first partition for EFI: `mkfs.fat -F32 -n EFI $part1`
+
+4. Init ZFS stuff:
+
+   ```
+   zpool create \
+     # 2 SSD only settings
+     -o ashift=12 \
+     -o autotrim=on \
+     -R /mnt \
+     -O canmount=off \
+     -O mountpoint=none \
+     -O acltype=posixacl \
+     -O compression=lz4 \
+     -O atime=off \
+     -O xattr=sa \
+     -O encryption=aes-256-gcm \
+     -O keylocation=prompt \
+     -O keyformat=passphrase \
+     zpool $part2
+   ```
+
+   Reserve some space for deletions:
+
+   ```
+   zfs create -o refreservation=1G -o mountpoint=none zpool/reserved
+   ```
+
+   Create the datasets as per the target layout:
+
+   ```
+   # Throwaway datasets
+   zfs create -o canmount=off -o mountpoint=none zpool/ephemeral
+   zfs create -o mountpoint=legacy zpool/ephemeral/root
+   zfs create -o mountpoint=legacy zpool/ephemeral/home
+
+   # Persistent datasets
+   zfs create -o canmount=off -o mountpoint=none zpool/persistent
+   zfs create -o mountpoint=legacy zpool/persistent/nix
+   zfs create -o mountpoint=legacy zpool/persistent/depot
+   zfs create -o mountpoint=legacy zpool/persistent/data
+   ```
+
+   Create completely blank snapshots of the ephemeral datasets:
+
+   ```
+   zfs snapshot zpool/ephemeral/root@blank
+   zfs snapshot zpool/ephemeral/home@blank
+   ```
+
+   The ephemeral home volume needs the user folder already set up with
+   permissions. Mount it and create the folder there:
+
+   ```
+   mount -t zfs zpool/ephemeral/root /mnt
+   mkdir /mnt/home
+   mount -t zfs zpool/ephemeral/home /mnt/home
+   mkdir /mnt/home/tazjin
+   chmod 1000:100 /mnt/home/tazjin
+   zfs snapshot zpool/ephemeral/home@tazjin-clean
+   ```
+
+   Now the persistent Nix store volume can be mounted and installation
+   can begin.
+
+   ```
+   mkdir /mnt/nix
+   mount -t zfs zpool/persistent/nix /mnt/nix
+   ```
+
+4. Configure & install NixOS as usual.
diff --git a/users/tazjin/elisp-deps/deps.el b/users/tazjin/elisp-deps/deps.el
new file mode 100644
index 0000000000..954d71cfba
--- /dev/null
+++ b/users/tazjin/elisp-deps/deps.el
@@ -0,0 +1,83 @@
+;; Visualise the internal structure of an Emacs Lisp file using
+;; Graphviz.
+;;
+;; Entry point is the function `edeps-analyse-file'.
+
+(require 'map)
+
+(defun edeps-read-defs (file-name)
+  "Stupidly read all definitions from an Emacs Lisp file. This only
+considers top-level forms, where the first element of the form is
+a symbol whose name contains the string `def', and where the
+second element is a symbol.
+
+Returns a hashmap of all these symbols with the remaining forms
+in their bodies."
+
+  (with-temp-buffer
+    (insert-file-contents file-name)
+    (goto-char (point-min))
+
+    (let ((symbols (make-hash-table)))
+      (condition-case _err
+          (while t
+            (let ((form (read (current-buffer))))
+              (when (and (listp form)
+                         (symbolp (car form))
+                         (string-match "def" (symbol-name (car form)))
+                         (symbolp (cadr form)))
+                (when (and (map-contains-key symbols (cadr form))
+                           ;; generic methods have multiple definitions
+                           (not (eq (car form) 'cl-defmethod)))
+                  (error "Duplicate symbol: %s" (symbol-name (cadr form))))
+
+                (map-put! symbols (cadr form)
+                          (cons (car form) (cddr form))))))
+        (end-of-file symbols)))))
+
+(defun edeps-analyse-structure (symbols)
+  "Analyse the internal structure of the symbols found by
+edeps-read-defs, and return a hashmap with the results of the
+analysis. The hashmap uses the symbols as keys, "
+  (let ((deps (make-hash-table)))
+    (map-do
+     (lambda (sym val)
+       (dolist (expr (flatten-list (cdr val)))
+         (when (map-contains-key symbols expr)
+           (map-put! deps expr (cons sym (ht-get deps expr))))))
+     symbols)
+    deps))
+
+(defun edeps-graph-deps (symbols deps)
+  (with-temp-buffer
+    (insert "digraph edeps {\n")
+
+    ;; List all symbols first
+    (insert "  subgraph {\n")
+    (map-do
+     (lambda (sym val)
+       (insert "    " (format "\"%s\" [label=\"%s\\n(%s)\"];\n" sym sym (car val))))
+     symbols)
+    (insert "  }\n\n")
+
+    ;; Then drop all the edges in there ..
+    (insert "  subgraph {\n")
+    (map-do
+     (lambda (sym deps)
+       (dolist (dep deps)
+         (insert "    " (format "\"%s\" -> \"%s\";\n" dep sym))))
+     deps)
+    (insert "  }\n")
+
+    (insert "}\n")
+    (buffer-string)))
+
+(defun edeps-analyse-file (infile outfile)
+  "Produces a dot-graph in OUTFILE from an internal structural
+analysis of INFILE. This can be graphed using the graphviz
+package."
+  (let* ((symbols (edeps-read-defs infile))
+         (deps (edeps-analyse-structure symbols)))
+    (with-temp-buffer
+      (insert (edeps-graph-deps symbols deps))
+      (write-file outfile))))
diff --git a/users/tazjin/emacs/config/bindings.el b/users/tazjin/emacs/config/bindings.el
index 916d947756..d8b63e33e4 100644
--- a/users/tazjin/emacs/config/bindings.el
+++ b/users/tazjin/emacs/config/bindings.el
@@ -1,11 +1,11 @@
+;; Switch buffers reliably in the face of spurious renames.
+(global-set-key (kbd "C-x b") #'reliably-switch-buffer)
+
 ;; Font size
 (define-key global-map (kbd "C-=") 'increase-default-text-scale) ;; '=' because there lies '+'
 (define-key global-map (kbd "C--") 'decrease-default-text-scale)
 (define-key global-map (kbd "C-x C-0") 'set-default-text-scale)
 
-;; What does <tab> do? Well, it depends ...
-(define-key prog-mode-map (kbd "<tab>") #'company-indent-or-complete-common)
-
 ;; imenu instead of insert-file
 (global-set-key (kbd "C-x i") 'imenu)
 
@@ -15,7 +15,6 @@
 ;; Start eshell or switch to it if it's active.
 (global-set-key (kbd "C-x m") 'eshell)
 
-(global-set-key (kbd "C-x C-p") 'browse-repositories)
 (global-set-key (kbd "M-g M-g") 'goto-line-with-feedback)
 
 ;; Miscellaneous editing commands
@@ -23,7 +22,7 @@
 (global-set-key (kbd "C-c a") 'align-regexp)
 (global-set-key (kbd "C-c m") 'mc/mark-dwim)
 
-;; Browse URLs (very useful for Gitlab's SSH output!)
+;; Browse URLs (very useful for Gerrit's push output, etc!)
 (global-set-key (kbd "C-c b p") 'browse-url-at-point)
 (global-set-key (kbd "C-c b b") 'browse-url)
 
@@ -34,23 +33,17 @@
 ;; Open a file in project:
 (global-set-key (kbd "C-c f") 'project-find-file)
 
-;; Search in a project
-(global-set-key (kbd "C-c r g") 'rg-in-project)
-
 ;; Open a file via magit:
 (global-set-key (kbd "C-c C-f") #'magit-find-file-worktree)
 
 ;; Insert TODO comments
 (global-set-key (kbd "C-c t") 'insert-todo-comment)
 
-;; Make sharing music easier
-(global-set-key (kbd "s-s w") #'songwhip-lookup-url)
-
 ;; Open the depot
 (global-set-key (kbd "s-s d") #'tvl-depot-status)
 
-;; Open any repo through zoxide
-(global-set-key (kbd "s-s r") #'zoxide-open-magit)
+;; Open any project through zoxide
+(global-set-key (kbd "s-s r") #'zoxide-open-project)
 
 ;; Add subthread collapsing to notmuch-show.
 ;;
@@ -62,4 +55,50 @@
     (interactive)
     (notmuch-show-open-or-close-subthread t))) ;; open
 
+;; Get rid of the annoying `save-some-buffers' shortcut which I
+;; *NEVER* use intentionally.
+(unbind-key (kbd "C-x s") 'global-map)
+
+;; German keyboard layout with Y and Z in the correct place.
+
+(quail-define-package
+ "german-qwerty" "German" "DE@" t
+ "German (Deutsch) input method with QWERTY keys"
+ nil t t t t nil nil nil nil nil t)
+
+;; 1!  2"  3§  4$  5%  6&  7/  8(  9)  0=  ß?  [{  ]}
+;;  qQ  wW  eE  rR  tT  yY  uU  iI  oO  pP  üÜ  +*
+;;   aA  sS  dD  fF  gG  hH  jJ  kK  lL  ΓΆΓ–  ÀÄ  #^
+;;    zZ  xX  cC  vV  bB  nN  mM  ,;  .:  -_
+
+(quail-define-rules
+ ("-" ?ß)
+ ("=" ?\[)
+ ("`" ?\])
+ ("[" ?ΓΌ)
+ ("]" ?+)
+ (";" ?ΓΆ)
+ ("'" ?Γ€)
+ ("\\" ?#)
+ ("/" ?-)
+
+ ("@" ?\")
+ ("#" ?Β§)
+ ("^" ?&)
+ ("&" ?/)
+ ("*" ?\()
+ ("(" ?\))
+ (")" ?=)
+ ("_" ??)
+ ("+" ?{)
+ ("~" ?})
+ ("{" ?Ü)
+ ("}" ?*)
+ (":" ?Γ–)
+ ("\"" ?Γ„)
+ ("|" ?^)
+ ("<" ?\;)
+ (">" ?:)
+ ("?" ?_))
+
 (provide 'bindings)
diff --git a/users/tazjin/emacs/config/custom.el b/users/tazjin/emacs/config/custom.el
index 2bb7ad4896..3e9a9dcd06 100644
--- a/users/tazjin/emacs/config/custom.el
+++ b/users/tazjin/emacs/config/custom.el
@@ -7,9 +7,9 @@
  '(ac-delay 0.2)
  '(avy-background t)
  '(cargo-process--enable-rust-backtrace 1)
- '(company-auto-complete (quote (quote company-explicit-action-p)))
- '(company-idle-delay 0.5)
- '(custom-enabled-themes (quote (gruber-darker)))
+ '(custom-safe-themes
+   (quote
+    ("d61fc0e6409f0c2a22e97162d7d151dee9e192a90fa623f8d6a071dbf49229c6" "3c83b3676d796422704082049fc38b6966bcad960f896669dfc21a7a37a748fa" "89336ca71dae5068c165d932418a368a394848c3b8881b2f96807405d8c6b5b6" default)))
  '(display-time-default-load-average nil)
  '(display-time-interval 30)
  '(elnode-send-file-program "/run/current-system/sw/bin/cat")
diff --git a/users/tazjin/emacs/config/desktop.el b/users/tazjin/emacs/config/desktop.el
index c160ae131f..aa232fec2f 100644
--- a/users/tazjin/emacs/config/desktop.el
+++ b/users/tazjin/emacs/config/desktop.el
@@ -4,14 +4,15 @@
 ;; window-management (EXWM) as well as additional system-wide
 ;; commands.
 
-(require 'dash)
 (require 'exwm)
 (require 'exwm-config)
 (require 'exwm-randr)
 (require 'exwm-systemtray)
 (require 'exwm-xim )
 (require 'f)
+(require 'ring)
 (require 's)
+(require 'seq)
 
 (defcustom tazjin--screen-lock-command "tazjin-screen-lock"
   "Command to execute for locking the screen."
@@ -69,36 +70,17 @@
   human-accessible titles."
 
   (pcase (list (or exwm-class-name "unknown") (or exwm-title "unknown"))
-    ;; In Cider windows, rename the class and keep the workspace/file
-    ;; as the title.
-    (`("Google-chrome" ,(and (pred (lambda (title) (s-ends-with? " - Cider" title))) title))
-     (format "Cider<%s>" (s-chop-suffix " - Cider" title)))
-    (`("Google-chrome" ,(and (pred (lambda (title) (s-ends-with? " - Cider V" title))) title))
-     (format "Cider V<%s>" (s-chop-suffix " - Cider V" title)))
-
-    ;; Attempt to detect IRCCloud windows via their title, which is a
-    ;; combination of the channel name and network.
-    ;;
-    ;; This is what would often be referred to as a "hack". The regexp
-    ;; will not work if a network connection buffer is selected in
-    ;; IRCCloud, but since the title contains no other indication that
-    ;; we're dealing with an IRCCloud window
-    (`("Google-chrome"
-       ,(and (pred (lambda (title)
-                     (s-matches? "^[\*\+]\s#[a-zA-Z0-9/\-]+\s\|\s[a-zA-Z\.]+$" title)))
-             title))
-     (format "IRCCloud<%s>" title))
-
-    ;; For other Chrome windows, make the title shorter.
-    (`("Google-chrome" ,title)
-     (format "Chrome<%s>" (s-truncate 42 (s-chop-suffix " - Google Chrome" title))))
-
-    ;; Gnome-terminal -> Term
-    (`("Gnome-terminal" ,title)
-     ;; fish-shell buffers contain some unnecessary whitespace and
-     ;; such before the current working directory. This can be
-     ;; stripped since most of my terminals are fish shells anyways.
-     (format "Term<%s>" (s-trim-left (s-chop-prefix "fish" title))))
+    ;; Yandex.Music -> `Π―.Music<... stuff ...>'
+    (`("Chromium-browser" ,(and (pred (lambda (title) (s-starts-with? "Yandex.Music - " title))) title))
+     (format "Π―.Music<%s>" (s-chop-prefix "Yandex.Music - " title)))
+
+    ;; For other Chromium windows, make the title shorter.
+    (`("Chromium-browser" ,title)
+     (format "Chromium<%s>" (s-truncate 42 (s-chop-suffix " - Chromium" title))))
+
+    ;; similarly for Firefox
+    (`("firefox" ,title)
+     (format "FF<%s>" title))
 
     ;; Quassel buffers
     ;;
@@ -120,9 +102,6 @@
     (`(,class ,title) (format "%s<%s>" class (s-truncate 12 title)))))
 
 ;; EXWM launch configuration
-;;
-;; This used to use use-package, but when something breaks use-package
-;; it doesn't exactly make debugging any easier.
 
 (let ((titlef (lambda ()
                 (exwm-workspace-rename-buffer (create-window-name)))))
@@ -130,23 +109,57 @@
   (add-hook 'exwm-update-title-hook titlef))
 
 (fringe-mode 3)
+
+;; tab-bar related config
+(setq tab-bar-show 1)
+(setq tab-bar-tab-hints t)
+
+(setq tab-bar-format
+      '(tab-bar-format-history
+        tab-bar-format-tabs tab-bar-separator
+        tab-bar-format-align-right tab-bar-format-global))
+
+(setq tab-bar-new-tab-choice
+      (lambda () (get-buffer-create "*scratch*")))
+
+(tab-bar-mode 1)
+
+(setq x-no-window-manager t) ;; TODO(tazjin): figure out when to remove this
 (exwm-enable)
+(exwm-randr-enable)
 
-;; 's-N': Switch to certain workspace
-(setq exwm-workspace-number 10)
-(dotimes (i 10)
-  (exwm-input-set-key (kbd (format "s-%d" i))
-                      `(lambda ()
-                         (interactive)
-                         (exwm-workspace-switch-create ,i))))
+;; Tab-management shortcuts
+
+(defun tab-bar-select-or-return ()
+  "This function behaves like `tab-bar-select-tab', except it calls
+`tab-recent' if asked to jump to the current tab. This simulates
+the back&forth behaviour of i3."
+  (interactive)
+  (let* ((key (event-basic-type last-command-event))
+         (tab (if (and (characterp key) (>= key ?1) (<= key ?9))
+                  (- key ?0)
+                0))
+         (current (1+ (tab-bar--current-tab-index))))
+    (if (eq tab current)
+        (tab-recent)
+      (tab-bar-select-tab tab))))
+
+(dotimes (i 8)
+  (exwm-input-set-key (kbd (format "s-%d" (+ 1 i))) #'tab-bar-select-or-return))
+
+(exwm-input-set-key (kbd "s-9") #'tab-last)
+(exwm-input-set-key (kbd "s-f") #'tab-next)
+(exwm-input-set-key (kbd "s-b") #'tab-recent)
+(exwm-input-set-key (kbd "s-w") #'tab-close)
+(exwm-input-set-key (kbd "s-n") #'tab-new)
 
 ;; Launch applications / any command with completion (dmenu style!)
-(exwm-input-set-key (kbd "s-d") #'counsel-linux-app)
+(exwm-input-set-key (kbd "s-d") #'run-xdg-app)
 (exwm-input-set-key (kbd "s-x") #'run-external-command)
 (exwm-input-set-key (kbd "s-p") #'password-store-lookup)
 
-;; Add X11 terminal selector to a key
-(exwm-input-set-key (kbd "C-x t") #'ts/switch-to-terminal)
+;; Add vterm selector to a key
+(exwm-input-set-key (kbd "s-v") #'ts/switch-to-terminal)
 
 ;; Toggle between line-mode / char-mode
 (exwm-input-set-key (kbd "C-c C-t C-t") #'exwm-input-toggle-keyboard)
@@ -173,10 +186,6 @@
 (bind-xkb "no" "k n")
 (bind-xkb "ru" "k r")
 (bind-xkb "se" "k s")
-
-;; These are commented out because Emacs no longer starts (??) if
-;; they're set at launch.
-;;
 (bind-xkb "us" "Π» Π³")
 (bind-xkb "de" "Π» Π²")
 (bind-xkb "no" "Π» Ρ‚")
@@ -188,9 +197,8 @@
 (push ?\C-\\ exwm-input-prefix-keys)
 
 ;; Line-editing shortcuts
-(exwm-input-set-simulation-keys
- '(([?\C-d] . delete)
-   ([?\C-w] . ?\C-c)))
+(exwm-input-set-simulation-key (kbd "C-d") (kbd "DEL"))
+(exwm-input-set-simulation-key (kbd "C-w") (kbd "C-c"))
 
 ;; Show time & battery status in the mode line
 (display-time-mode)
@@ -199,66 +207,123 @@
 ;; enable display of X11 system tray within Emacs
 (exwm-systemtray-enable)
 
-;; Configure xrandr (multi-monitor setup).
-
-(defun set-randr-config (screens)
-  (setq exwm-randr-workspace-monitor-plist
-        (-flatten (-map (lambda (screen)
-                          (-map (lambda (screen-id) (list screen-id (car screen))) (cdr screen)))
-                        screens))))
+;; Multi-monitor configuration.
+;;
+;; With tab-bar-mode, each monitor only displays at most one
+;; workspace. Workspaces are only created, never deleted, meaning that
+;; the number of workspaces will be equivalent to the maximum number
+;; of displays that were connected during a session.
+;;
+;; The first workspace is special: It is kept on the primary monitor.
 
-;; Layouts for Tverskoy (X13 AMD laptop)
-(defun randr-tverskoy-layout-single ()
-  "Laptop screen only!"
+(defun exwm-assign-workspaces ()
+  "Assigns workspaces to the currently existing monitors, putting
+the first one on the primary display and allocating the others
+dynamically if needed in no particular order."
+  (interactive)
+  (let* ((randr-monitors (exwm-randr--get-monitors))
+         (primary (car randr-monitors))
+         (all-monitors (seq-map #'car (cadr randr-monitors)))
+         (sorted-primary-first (seq-sort (lambda (a b)
+                                           (or (equal a primary)
+                                               (< a b)))
+                                         all-monitors))
+         ;; assign workspace numbers to each monitor ...
+         (workspace-assignments
+          (flatten-list (seq-map-indexed (lambda (monitor idx)
+                                           (list idx monitor))
+                                         sorted-primary-first))))
+    ;; ensure that the required workspaces exist
+    (exwm-workspace-switch-create (- (seq-length all-monitors) 1))
+
+    ;; update randr config
+    (setq exwm-randr-workspace-monitor-plist workspace-assignments)
+    (exwm-randr-refresh)
+
+    ;; leave focus on primary workspace
+    (exwm-workspace-switch 0)))
+
+(defun list-available-monitors ()
+  "List connected, but unused monitors."
+  (let* ((all-connected
+          (seq-map (lambda (line) (car (s-split " " line)))
+                   (s-lines (s-trim (shell-command-to-string "xrandr | grep connected | grep -v disconnected")))))
+         (all-active (seq-map #'car (cadr (exwm-randr--get-monitors)))))
+    (seq-filter (lambda (s) (not (seq-contains-p all-active s)))
+                all-connected)))
+
+(defun exwm-enable-monitor ()
+  "Interactively construct an EXWM invocation that enable the
+given monitor and assigns a workspace to it."
   (interactive)
-  (set-randr-config '(("eDP" (number-sequence 0 9))))
-  (shell-command "xrandr --output eDP --auto --primary")
-  (shell-command "xrandr --output HDMI-A-0 --off")
-  (exwm-randr-refresh))
 
-(defun randr-tverskoy-split-workspace ()
-  "Split the workspace across two screens, assuming external to the left."
+  (let* ((monitors (list-available-monitors))
+         (primary (car (exwm-randr--get-monitors)))
+         (monitor (pcase (seq-length monitors)
+                    (0 (error "No available monitors."))
+                    (1 (car monitors))
+                    (_
+                     (completing-read "Which monitor? " (list-available-monitors) nil t))))
+
+         (configurations `(("secondary (left)" . ,(format "--left-of %s" primary))
+                           ("secondary (right)" . ,(format "--right-of %s" primary))
+                           ("primary (left)" . ,(format "--left-of %s --primary" primary))
+                           ("primary (right)" . ,(format "--right-of %s --primary" primary))
+                           ("mirror" . ,(format "--same-as %s" primary))))
+
+         (where (completing-read (format "%s should be " monitor)
+                                 (seq-map #'car configurations)
+                                 nil t))
+         (xrandr-pos (cdr (assoc where configurations)))
+         (xrandr-cmd (format "xrandr --output %s --auto %s" monitor xrandr-pos)))
+    (message "Invoking '%s'" xrandr-cmd)
+    (shell-command xrandr-cmd)
+    (exwm-assign-workspaces)))
+
+(defun exwm-disable-monitor ()
+  "Interactively choose a monitor to disable."
   (interactive)
-  (set-randr-config
-   '(("HDMI-A-0" 1 2 3 4 5 6 7)
-     ("eDP" 8 9 0)))
 
-  (shell-command "xrandr --output HDMI-A-0 --left-of eDP --auto")
-  (exwm-randr-refresh))
+  (let* ((all (exwm-randr--get-monitors))
+         (active (seq-map #'car (cadr all)))
+         (monitor (if (> (seq-length active) 1)
+                      (completing-read "Disable which monitor? " active nil t)
+                    (error "Only one monitor is active!")))
 
-;; Layouts for frog (desktop)
+         ;; If this monitor was primary, pick another active one instead.
+         (remaining (seq-filter (lambda (s) (not (equal s monitor))) active))
+         (new-primary
+          (when (equal monitor (car all))
+            (pcase (seq-length remaining)
+              (1 (car remaining))
+              (_ (completing-read "New primary? " remaining nil t))))))
 
-(defun randr-frog-layout-right-only ()
-  "Use only the right screen on frog."
-  (interactive)
-  (set-randr-config `(("DisplayPort-0" ,(number-sequence 0 9))))
-  (shell-command "xrandr --output DisplayPort-0 --off")
-  (shell-command "xrandr --output DisplayPort-1 --auto --primary"))
+    (when new-primary
+      (shell-command (format "xrandr --output %s --primary" new-primary)))
 
-(defun randr-frog-layout-both ()
-  "Use the left and right screen on frog."
-  (interactive)
-  (set-randr-config `(("DisplayPort-0" 1 2 3 4 5)
-                      ("DisplayPort-1" 6 7 8 9 0)))
+    (shell-command (format "xrandr --output %s --off" monitor))
+    (exwm-assign-workspaces)))
 
-  (shell-command "xrandr --output DisplayPort-0 --auto --primary --left-of DisplayPort-1")
-  (shell-command "xrandr --output DisplayPort-1 --auto --right-of DisplayPort-0 --rotate left"))
+(defun exwm-switch-monitor ()
+  "Switch focus to another monitor by name."
+  (interactive)
 
-(pcase (s-trim (shell-command-to-string "hostname"))
-  ("tverskoy"
-   (exwm-input-set-key (kbd "s-m s") #'randr-tverskoy-layout-single)
-   (exwm-input-set-key (kbd "s-m 2") #'randr-tverskoy-split-workspace))
+  ;; TODO: Filter out currently active? How to determine it?
+  (let* ((target (completing-read "Switch to monitor: "
+                                  (seq-map #'car (cadr (exwm-randr--get-monitors)))
+                                  nil t))
+         (target-workspace
+          (cl-loop for (workspace screen) on exwm-randr-workspace-monitor-plist by #'cddr
+                   when (equal screen target) return workspace)))
+    (exwm-workspace-switch target-workspace)))
 
-  ("frog"
-   (exwm-input-set-key (kbd "s-m b") #'randr-frog-layout-both)
-   (exwm-input-set-key (kbd "s-m r") #'randr-frog-layout-right-only)))
+(exwm-input-set-key (kbd "s-m e") #'exwm-enable-monitor)
+(exwm-input-set-key (kbd "s-m d") #'exwm-disable-monitor)
+(exwm-input-set-key (kbd "s-m o") #'exwm-switch-monitor)
 
 ;; Notmuch shortcuts as EXWM globals
 ;; (g m => gmail)
 (exwm-input-set-key (kbd "s-g m") #'notmuch)
-(exwm-input-set-key (kbd "s-g M") #'counsel-notmuch)
-
-(exwm-randr-enable)
 
 ;; Let buffers move seamlessly between workspaces by making them
 ;; accessible in selectors on all frames.
diff --git a/users/tazjin/emacs/config/functions.el b/users/tazjin/emacs/config/functions.el
index ba7301e794..68a384d20f 100644
--- a/users/tazjin/emacs/config/functions.el
+++ b/users/tazjin/emacs/config/functions.el
@@ -2,9 +2,7 @@
 (require 'dash)
 (require 'map)
 
-(defun load-file-if-exists (filename)
-  (if (file-exists-p filename)
-      (load filename)))
+(require 'gio-list-apps) ;; native module!
 
 (defun goto-line-with-feedback ()
   "Show line numbers temporarily, while prompting for the line number input"
@@ -17,24 +15,19 @@
           (goto-line target)))
     (setq-local display-line-numbers nil)))
 
-;; These come from the emacs starter kit
-
 (defun esk-add-watchwords ()
   (font-lock-add-keywords
    nil '(("\\<\\(FIX\\(ME\\)?\\|TODO\\|DEBUG\\|HACK\\|REFACTOR\\|NOCOMMIT\\)"
           1 font-lock-warning-face t))))
 
+(add-hook 'prog-mode-hook 'esk-add-watchwords)
+
 (defun esk-sudo-edit (&optional arg)
   (interactive "p")
   (if (or arg (not buffer-file-name))
       (find-file (concat "/sudo:root@localhost:" (read-file-name "File: ")))
     (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))
 
-;; Open the NixOS man page
-(defun nixos-man ()
-  (interactive)
-  (man "configuration.nix"))
-
 ;; Get the nix store path for a given derivation.
 ;; If the derivation has not been built before, this will trigger a build.
 (defun nix-store-path (derivation)
@@ -114,7 +107,9 @@ the GPG agent correctly."
                    nil ;; predicate
                    t   ;; require-match
                    ))
-         (password (auth-source-pass-get 'secret entry)))
+         (password (or (let ((epa-suppress-error-buffer t))
+                         (auth-source-pass-get 'secret entry))
+                       (error "failed to decrypt '%s', wrong password?" entry))))
     (password-store-clear)
     (kill-new password)
     (setq password-store-kill-ring-pointer kill-ring-yank-pointer)
@@ -124,25 +119,8 @@ the GPG agent correctly."
           (run-at-time (password-store-timeout)
                        nil 'password-store-clear))))
 
-(defun browse-repositories ()
-  "Select a git repository and open its associated magit buffer."
-
-  (interactive)
-  (magit-status
-   (completing-read "Repository: " (magit-list-repos))))
-
-(defun bottom-right-window-p ()
-  "Determines whether the last (i.e. bottom-right) window of the
-  active frame is showing the buffer in which this function is
-  executed."
-  (let* ((frame (selected-frame))
-         (right-windows (window-at-side-list frame 'right))
-         (bottom-windows (window-at-side-list frame 'bottom))
-         (last-window (car (seq-intersection right-windows bottom-windows))))
-    (eq (current-buffer) (window-buffer last-window))))
-
 (defhydra mc/mark-more-hydra (:color pink)
-  ("<up>" mmlte--up "Mark previous like this")
+  ("<up>" mc/mmlte--up "Mark previous like this")
   ("<down>" mc/mmlte--down "Mark next like this")
   ("<left>" mc/mmlte--left (if (eq mc/mark-more-like-this-extended-direction 'up)
                                "Skip past the cursor furthest up"
@@ -168,26 +146,15 @@ the GPG agent correctly."
       (mc/mmlte--down)
       (mc/mark-more-hydra/body))))
 
-(defun memespace-region ()
-  "Make a meme out of it."
+(setq mc/cmds-to-run-for-all '(kill-region paredit-newline))
 
-  (interactive)
-  (let* ((start (region-beginning))
-         (end (region-end))
-         (memed
-          (message
-           (s-trim-right
-            (apply #'string
-                   (-flatten
-                    (nreverse
-                     (-reduce-from (lambda (acc x)
-                                     (cons (cons x (-repeat (+ 1 (length acc)) 32)) acc))
-                                   '()
-                                   (string-to-list (buffer-substring-no-properties start end))))))))))
-
-    (save-excursion (delete-region start end)
-                    (goto-char start)
-                    (insert memed))))
+(setq mc/cmds-to-run-once '(mc/mark-dwim
+                            mc/mark-more-hydra/mc/mmlte--down
+                            mc/mark-more-hydra/mc/mmlte--left
+                            mc/mark-more-hydra/mc/mmlte--right
+                            mc/mark-more-hydra/mc/mmlte--up
+                            mc/mark-more-hydra/mmlte--up
+                            mc/mark-more-hydra/nil))
 
 (defun insert-todo-comment (prefix todo)
   "Insert a comment at point with something for me to do."
@@ -231,11 +198,16 @@ the GPG agent correctly."
   (if prefix (text-scale-adjust 0)
     (set-face-attribute 'default nil :height (or to 120))))
 
-(defun scrot-select ()
+(defun screenshot-select (filename)
   "Take a screenshot based on a mouse-selection and save it to
   ~/screenshots."
-  (interactive)
-  (shell-command "scrot '$a_%Y-%m-%d_%s.png' -s -e 'mv $f ~/screenshots/'"))
+  (interactive "sScreenshot filename: ")
+  (let* ((path (f-join "~/screenshots"
+                       (format "%s-%d.png"
+                               (if (string-empty-p filename) "shot" filename)
+                               (time-convert nil 'integer)))))
+    (shell-command (format "maim --select %s" path))
+    (message "Wrote screenshot to %s" path)))
 
 (defun graph-unread-mails ()
   "Create a bar chart of unread mails based on notmuch tags.
@@ -283,6 +255,17 @@ the GPG agent correctly."
 
 (add-to-list 'project-find-functions #'find-depot-project)
 
+(defun find-cargo-project (dir)
+  "Attempt to find the current project in `project-find-functions'
+by looking for a `Cargo.toml' file."
+  (when dir
+    (unless (equal "/" dir)
+      (if (f-exists-p (f-join dir "Cargo.toml"))
+          (cons 'transient dir)
+        (find-cargo-project (f-parent dir))))))
+
+(add-to-list 'project-find-functions #'find-cargo-project)
+
 (defun magit-find-file-worktree ()
   (interactive)
   "Find a file in the current (ma)git worktree."
@@ -290,44 +273,80 @@ the GPG agent correctly."
                              (magit-read-file-from-rev "HEAD" "Find file")
                              #'pop-to-buffer-same-window))
 
-(defun songwhip--handle-result (status &optional cbargs)
-  ;; TODO(tazjin): Inspect status, which looks different in practice
-  ;; than the manual claims.
-  (if-let* ((response (json-parse-string
-                       (buffer-substring url-http-end-of-headers (point-max))))
-            (sw-path (ht-get* response "data" "path"))
-            (link (format "https://songwhip.com/%s" sw-path))
-            (select-enable-clipboard t))
-      (progn
-        (kill-new link)
-        (message "Copied Songwhip link (%s)" link))
-    (warn "Something went wrong while retrieving Songwhip link!")
-    ;; For debug purposes, the buffer is persisted in this case.
-    (setq songwhip--debug-buffer (current-buffer))))
-
-(defun songwhip-lookup-url (url)
-  "Look up URL on Songwhip and copy the resulting link to the clipboard."
-  (interactive "sEnter source URL: ")
-  (let ((songwhip-url "https://songwhip.com/api/")
-        (url-request-method "POST")
-        (url-request-extra-headers '(("Content-Type" . "application/json")))
-        (url-request-data
-         (json-serialize `((country . "GB")
-                           (url . ,url)))))
-    (url-retrieve "https://songwhip.com/api/" #'songwhip--handle-result nil t t)
-    (message "Requesting Songwhip URL ... please hold the line.")))
-
-(defun rg-in-project (&optional prefix)
-  "Interactively call ripgrep in the current project, or fall
-  back to ripgrep default behaviour if prefix is set."
-  (interactive "P")
-  (counsel-rg nil (unless prefix
-                    (if-let ((pr (project-current)))
-                        (project-root pr)))))
+(defun zoxide-open-project ()
+  "Query Zoxide for paths, and open the result as appropriate (magit or dired)."
+  (interactive)
+  (zoxide-open-with
+   nil
+   (lambda (path)
+     (condition-case err (magit-status-setup-buffer path)
+       (magit-outside-git-repo (dired path))))))
+
+(defun toggle-nix-test-and-exp ()
+  "Switch between the .nix and .exp file in a Tvix/Nix test."
+  (interactive)
+  (let* ((file (buffer-file-name))
+         (other (if (s-suffix? ".nix" file)
+                    (s-replace-regexp ".nix$" ".exp" file)
+                  (if (s-suffix? ".exp" file)
+                      (s-replace-regexp ".exp$" ".nix" file)
+                    (error "Not a .nix/.exp file!")))))
+    (find-file other)))
+
+(defun reliably-switch-buffer ()
+  "Reliably and interactively switch buffers, without ending up in a
+situation where the buffer was renamed during selection and an
+empty new buffer is created.
+
+This is done by, in contrast to most buffer-switching functions,
+retaining a list of the buffer *objects* and their associated
+names, instead of only their names (which might change)."
+
+  (interactive)
+  (let* ((buffers (seq-map (lambda (b) (cons (buffer-name b) b))
+                           (seq-filter (lambda (b) (not (string-prefix-p " " (buffer-name b))))
+                                       (buffer-list))))
+
+         ;; Annotate buffers that display remote files. I frequently
+         ;; want to see it, because I might have identically named
+         ;; files open locally and remotely at the same time, and it
+         ;; helps with differentiating them.
+         (completion-extra-properties
+          '(:annotation-function
+            (lambda (name)
+              (if-let* ((file (buffer-file-name (cdr (assoc name buffers))))
+                        (remote (file-remote-p file)))
+                  (format " [%s]" remote)))))
+
+         (name (completing-read "Switch to buffer: " (seq-map #'car buffers)))
+         (selected (or (cdr (assoc name buffers))
+                       ;; Allow users to manually select invisible buffers ...
+                       (get-buffer name))))
+    (switch-to-buffer (or selected name) nil 't)))
+
+(defun run-xdg-app ()
+  "Use `//users/tazjin/gio-list-apps' to retrieve a list of
+installed (and visible) XDG apps, and let users launch them."
+  (interactive)
+  (let* ((apps (taz-list-xdg-apps))
+
+         ;; Display the command that will be run as an annotation
+         (completion-extra-properties
+          '(:annotation-function (lambda (app) (format " [%s]" (cdr (assoc app apps)))))))
+
+    (run-external-command--handler (cdr (assoc (completing-read "App: " apps nil t) apps)))))
+
+(defun advice-remove-all (sym)
+  "Remove all advices from symbol SYM."
+  (interactive "aFunction symbol: ")
+  (advice-mapc (lambda (advice _props) (advice-remove sym advice)) sym))
 
-(defun zoxide-open-magit ()
-  "Query Zoxide for paths and open magit in the result."
+(defun M-x-always-same-window ()
+  "Run `execute-extended-command', but ensure that whatever it does
+always opens in the same window in which the command was invoked."
   (interactive)
-  (zoxide-open-with nil #'magit-status-setup-buffer))
+  (let ((display-buffer-overriding-action
+         '((display-buffer-same-window) . ((inhibit-same-window . nil)))))
+    (call-interactively #'execute-extended-command)))
 
 (provide 'functions)
diff --git a/users/tazjin/emacs/config/init.el b/users/tazjin/emacs/config/init.el
index 27e6312e4e..ced3bf2ff8 100644
--- a/users/tazjin/emacs/config/init.el
+++ b/users/tazjin/emacs/config/init.el
@@ -10,23 +10,9 @@
 (require 'use-package)
 (require 'seq)
 
-;; TODO(tazjin): Figure out what's up with vc.
-;;
-;; Leaving vc enabled breaks all find-file operations with messages
-;; about .git folders being absent, but in random places.
-(require 'vc)
-(setq vc-handled-backends nil)
-
 (package-initialize)
 
 ;; Initialise all packages installed via Nix.
-;;
-;; TODO: Generate this section in Nix for all packages that do not
-;; require special configuration.
-
-;;
-;; Packages providing generic functionality.
-;;
 
 (use-package ace-window
   :bind (("C-x o" . ace-window))
@@ -43,13 +29,10 @@
 
 (use-package browse-kill-ring)
 
-(use-package company
-  :hook ((prog-mode . company-mode))
-  :config (setq company-tooltip-align-annotations t))
-
-(use-package counsel
-  :after (ivy)
-  :config (counsel-mode 1))
+(use-package consult
+  :bind
+  ("C-c r g" . consult-ripgrep)
+  ("C-s" . consult-line))
 
 (use-package dash)
 (use-package gruber-darker-theme)
@@ -59,39 +42,11 @@
   (eglot-autoshutdown t)
   (eglot-send-changes-idle-time 0.3))
 
-(use-package elfeed
-  :config
-  (setq elfeed-feeds
-        '("https://lobste.rs/rss"
-          "https://www.anti-spiegel.ru/feed/"
-          "https://www.reddit.com/r/lockdownskepticism/.rss"
-          "https://www.reddit.com/r/rust/.rss"
-          "https://news.ycombinator.com/rss"
-          ("https://xkcd.com/atom.xml" media)
-
-          ;; vlogcreations
-          ("https://www.youtube.com/feeds/videos.xml?channel_id=UCR0VLWitB2xM4q7tjkoJUPw" media)
-          )))
-
 (use-package ht)
 
 (use-package hydra)
 (use-package idle-highlight-mode :hook ((prog-mode . idle-highlight-mode)))
 
-(use-package ivy
-  :config
-  (ivy-mode 1)
-  (setq enable-recursive-minibuffers t)
-  (setq ivy-use-virtual-buffers t))
-
-(use-package ivy-prescient
-  :after (ivy prescient)
-  :config
-  (ivy-prescient-mode)
-  ;; Fixes an issue with how regexes are passed to ripgrep from counsel,
-  ;; see raxod502/prescient.el#43
-  (setf (alist-get 'counsel-rg ivy-re-builders-alist) #'ivy--regex-plus))
-
 (use-package multiple-cursors)
 
 (use-package notmuch
@@ -109,33 +64,20 @@
   (pinentry-start))
 
 (use-package prescient
-  :after (ivy counsel)
-  :config (prescient-persist-mode))
-
-(use-package rainbow-delimiters
-  :hook (prog-mode . rainbow-delimiters-mode)
-  :custom-face
-  (rainbow-delimiters-depth-1-face ((t (:foreground "#2aa198"))))
-  (rainbow-delimiters-depth-2-face ((t (:foreground "#b58900"))))
-  (rainbow-delimiters-depth-3-face ((t (:foreground "#268bd2"))))
-  (rainbow-delimiters-depth-4-face ((t (:foreground "#dc322f"))))
-  (rainbow-delimiters-depth-5-face ((t (:foreground "#859900"))))
-  (rainbow-delimiters-depth-6-face ((t (:foreground "#268bd2"))))
-  (rainbow-delimiters-depth-7-face ((t (:foreground "#cb4b16"))))
-  (rainbow-delimiters-depth-8-face ((t (:foreground "#d33682"))))
-  (rainbow-delimiters-depth-9-face ((t (:foreground "#839496")))))
+  :config
+  (prescient-persist-mode)
+  (setq completion-styles '(basic prescient)))
 
+(use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode))
 (use-package rainbow-mode)
 (use-package s)
-(use-package string-edit)
+(use-package string-edit-at-point)
+(use-package term-switcher)
 
-(use-package swiper
-  :after (counsel ivy)
-  :bind (("C-s" . swiper)))
+(use-package undo-tree
+  :config (global-undo-tree-mode)
+  :custom (undo-tree-auto-save-history nil))
 
-(use-package telephone-line) ;; configuration happens outside of use-package
-(use-package term-switcher)
-(use-package undo-tree :config (global-undo-tree-mode))
 (use-package uuidgen)
 (use-package which-key :config (which-key-mode t))
 
@@ -152,20 +94,9 @@
 (use-package restclient)
 
 (use-package vterm
-  :config (progn
-            (setq vterm-shell "fish")
-            (setq vterm-exit-functions
-                  (lambda (&rest _) (kill-buffer (current-buffer))))
-            (setq vterm-kill-buffer-on-exit t))
-  :custom-face
-  (term-color-black ((t (:background "#282828" :foreground "#282828"))))
-  (term-color-blue ((t (:background "#96a6c8" :foreground "#96a6c8"))))
-  (term-color-cyan ((t (:background "#1fad83" :foreground "#1fad83"))))
-  (term-color-green ((t (:background "#73c936" :foreground "#73c936"))))
-  (term-color-magenta ((t (:background "#9e95c7" :foreground "#9e95c7"))))
-  (term-color-red ((t (:background "#f43841" :foreground "#f43841"))))
-  (term-color-white ((t (:background "#f5f5f5" :foreground "#f5f5f5"))))
-  (term-color-yellow ((t (:background "#ffdd33" :foreground "#ffdd33")))))
+  :custom
+  (vterm-shell "fish")
+  (vterm-kill-buffer-on-exit t))
 
 ;; vterm removed the ability to set a custom title generator function
 ;; via the public API, so this overrides its private title generation
@@ -186,7 +117,7 @@
          (cargo-process-mode . visual-line-mode))
   :bind (:map cargo-mode-map ("C-c C-c C-l" . ignore)))
 
-(use-package dockerfile-mode)
+(use-package dockerfile-ts-mode)
 
 (use-package erlang
   :hook ((erlang-mode . (lambda ()
@@ -206,9 +137,7 @@
 
 (use-package ielm
   :hook ((inferior-emacs-lisp-mode . (lambda ()
-                                       (paredit-mode)
-                                       (rainbow-delimiters-mode-enable)
-                                       (company-mode)))))
+                                       (rainbow-delimiters-mode-enable)))))
 
 (use-package jq-mode
   :config (add-to-list 'auto-mode-alist '("\\.jq\\'" . jq-mode)))
@@ -217,8 +146,6 @@
   :hook ((kotlin-mode . (lambda ()
                           (setq indent-line-function #'indent-relative)))))
 
-(use-package lsp-mode)
-
 (use-package markdown-mode
   :config
   (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))
@@ -237,30 +164,55 @@
 (use-package sly
   :hook ((sly-mrepl-mode . (lambda ()
                              (paredit-mode)
-                             (rainbow-delimiters-mode-enable)
-                             (company-mode))))
+                             (rainbow-delimiters-mode-enable))))
   :config
   (setq common-lisp-hyperspec-root "file:///home/tazjin/docs/lisp/"))
 
 (use-package telega
-  :bind (:map global-map ("s-t" . telega))
-  :config
-  (telega-mode-line-mode 1)
-  (add-hook 'telega-msg-ignore-predicates 'telega-msg-from-blocked-sender-p))
+  :bind (:map global-map ("s-c" . (lambda (p) (interactive "P")
+                                    (if p (call-interactively #'telega-chat-with)
+                                      (telega))))
+         :map telega-chat-button-map ("a" . ignore))
+  :config (telega-mode-line-mode 1)
+  :custom
+  (telega-emoji-use-images nil)
+  (telega-completing-read-function #'completing-read))
 
 (use-package terraform-mode)
-(use-package toml-mode)
+(use-package toml-ts-mode)
+
+(use-package treecrumbs
+  :hook ((yaml-ts-mode . treecrumbs-mode)))
 
 (use-package tvl)
 
+(use-package vertico
+  :config
+  (vertico-mode))
+
 (use-package web-mode)
-(use-package yaml-mode)
+(use-package yaml-ts-mode)
 (use-package zoxide)
 
 (use-package passively
   :custom
   (passively-store-state "/persist/tazjin/known-russian-words.el"))
 
+;; Note taking configuration for deft.
+(use-package deft
+  :custom
+  (deft-directory "/persist/tazjin/deft/")
+  (deft-extensions '("md" "org" "txt"))
+  (deft-default-extension "md"))
+
+(use-package zetteldeft
+  :custom
+  ;; Configure for Markdown
+  (zetteldeft-link-indicator "[[")
+  (zetteldeft-link-suffix "]]")
+  (zetteldeft-title-prefix "# ")
+  (zetteldeft-list-prefix "* "))
+
 ;; Initialise midnight.el, which by default automatically cleans up
 ;; unused buffers at midnight.
 (require 'midnight)
@@ -279,7 +231,7 @@
 ;; The way this will work for now is that Emacs will *write*
 ;; configuration to the file tracked in my repository, while not
 ;; actually *reading* it from there (unless Emacs is rebuilt).
-(setq custom-file (expand-file-name "~/depot/tools/emacs/config/custom.el"))
+(setq custom-file (f-join depot-path "users" "tazjin" "emacs" "config" "custom.el"))
 (load-library "custom")
 
 (defvar home-dir (expand-file-name "~"))
@@ -294,10 +246,8 @@
                  look-and-feel
                  functions
                  settings
-                 modes
                  bindings
                  eshell-setup))
-(telephone-line-setup)
 (ace-window-display-mode)
 
 ;; If a local configuration library exists, it should be loaded.
diff --git a/users/tazjin/emacs/config/look-and-feel.el b/users/tazjin/emacs/config/look-and-feel.el
index 8cca6e1bf0..b771b4cd03 100644
--- a/users/tazjin/emacs/config/look-and-feel.el
+++ b/users/tazjin/emacs/config/look-and-feel.el
@@ -11,9 +11,6 @@
 (setq ring-bell-function 'ignore)
 (setq initial-scratch-message "")
 
-;; Remember layout changes
-(winner-mode 1)
-
 ;; Usually emacs will run as a proper GUI application, in which case a few
 ;; extra settings are nice-to-have:
 (when window-system
@@ -22,69 +19,39 @@
   (blink-cursor-mode -1))
 
 ;; Configure Emacs fonts.
-(let ((font (if (equal "frog" (s-trim (shell-command-to-string "hostname")))
-                ;; For unclear reasons, frog refuses to render the
-                ;; regular font weight - everything ends up bold,
-                ;; which makes it hard to distinguish e.g. read/unread
-                ;; emails.
-                ;;
-                ;; Semi-bold looks a little different than on vauxhall
-                ;; and other machines, but it's alright.
-                (format "JetBrains Mono Semi Light-%d" 12)
-              (format "JetBrains Mono-%d" 12))))
+(let ((font (format "JetBrains Mono-%d" 12)))
   (setq default-frame-alist `((font . ,font)))
   (set-frame-font font t t))
 
-;; Configure telephone-line
-(defun telephone-misc-if-last-window ()
-  "Renders the mode-line-misc-info string for display in the
-  mode-line if the currently active window is the last one in the
-  frame.
-
-  The idea is to not display information like the current time,
-  load, battery levels on all buffers."
-
-  (when (bottom-right-window-p)
-    (telephone-line-raw mode-line-misc-info t)))
-
-(defun telephone-line-setup ()
-  (telephone-line-defsegment telephone-line-last-window-segment ()
-    (telephone-misc-if-last-window))
-
-  ;; Display the current EXWM workspace index in the mode-line
-  (telephone-line-defsegment telephone-line-exwm-workspace-index ()
-    (when (bottom-right-window-p)
-      (format "[%s]" exwm-workspace-current-index)))
-
-  ;; Define a highlight font for ~ important ~ information in the last
-  ;; window.
-  (defface special-highlight '((t (:foreground "white" :background "#5f627f"))) "")
-  (add-to-list 'telephone-line-faces
-               '(highlight . (special-highlight . special-highlight)))
-
-  (setq telephone-line-lhs
-        '((nil . (telephone-line-position-segment))
-          (accent . (telephone-line-buffer-segment))))
-
-  (setq telephone-line-rhs
-        '((accent . (telephone-line-major-mode-segment))
-          (nil . (telephone-line-last-window-segment
-                  telephone-line-exwm-workspace-index))
-
-          ;; TODO(tazjin): lets not do this particular thing while I
-          ;; don't actually run notmuch, there are too many things
-          ;; that have a dependency on the modeline drawing correctly
-          ;; (including randr operations!)
-          ;;
-          ;; (highlight . (telephone-line-notmuch-counts))
-          ))
-
-  (setq telephone-line-primary-left-separator 'telephone-line-tan-left
-        telephone-line-primary-right-separator 'telephone-line-tan-right
-        telephone-line-secondary-left-separator 'telephone-line-tan-hollow-left
-        telephone-line-secondary-right-separator 'telephone-line-tan-hollow-right)
-
-  (telephone-line-mode 1))
+;; Configure the modeline
+
+;; Implements a mode-line warning if there are any logged in TTY
+;; sessions apart from the graphical one.
+;;
+;; The status is only updated once every 30 seconds, as it requires
+;; shelling out to some commands (for now).
+(defun list-tty-sessions ()
+  "List all logged in tty sessions, except tty7 (graphical)"
+  (let ((command "who | awk '{print $2}' | grep -v tty7"))
+    (-filter (lambda (s) (not (string-empty-p s)))
+             (s-lines
+              (s-trim (let ((default-directory "/"))
+                        (shell-command-to-string command)))))))
+
+(defvar cached-tty-sessions (cons (time-convert nil 'integer) (list-tty-sessions))
+   "Cached TTY session value to avoid running the command too often.")
+
+;; TODO(tazjin): add this to the modeline
+
+(defun get-cached-tty-sessions ()
+  (let ((time ))
+    (when (< 30
+             (- (time-convert nil 'integer)
+                (car cached-tty-sessions)))
+      (setq cached-tty-sessions
+            (cons (time-convert nil 'integer) (list-tty-sessions)))))
+
+  (cdr cached-tty-sessions))
 
 ;; Auto refresh buffers
 (global-auto-revert-mode 1)
@@ -119,4 +86,13 @@
 ;; Don't wrap around when moving between buffers
 (setq windmove-wrap-around nil)
 
+;; Don't show me all emacs warnings immediately. Unfortunately this is
+;; not very granular, as emacs displays most of its warnings in the
+;; `emacs' "category", but without it every time I
+;; fullscreen/unfullscreen the warning buffer destroys my layout.
+;;
+;; Warnings suppressed by this are still logged to the warnings
+;; buffer.
+(setq warning-suppress-types '((emacs)))
+
 (provide 'look-and-feel)
diff --git a/users/tazjin/emacs/config/mail-setup.el b/users/tazjin/emacs/config/mail-setup.el
index 7fbece1b10..7352c8ba10 100644
--- a/users/tazjin/emacs/config/mail-setup.el
+++ b/users/tazjin/emacs/config/mail-setup.el
@@ -1,8 +1,6 @@
 (require 'notmuch)
-(require 'counsel-notmuch)
 
 ;; (global-set-key (kbd "C-c m") 'notmuch-hello)
-;; (global-set-key (kbd "C-c C-m") 'counsel-notmuch)
 ;; (global-set-key (kbd "C-c C-e n") 'notmuch-mua-new-mail)
 
 (setq notmuch-cache-dir (format "%s/.cache/notmuch" (getenv "HOME")))
@@ -51,7 +49,7 @@
 ;; handle that gracefully.
 (define-key notmuch-message-mode-map (kbd "C-x C-s") #'ignore)
 
-;; Define a telephone-line segment for displaying the count of unread,
+;; Define a mode-line segment for displaying the count of unread,
 ;; important mails in the last window's mode-line:
 (defvar *last-notmuch-count-redraw* 0)
 (defvar *current-notmuch-count* nil)
@@ -76,10 +74,6 @@
              (not (equal *current-notmuch-count* "I: 0; D: 0")))
     *current-notmuch-count*))
 
-(telephone-line-defsegment telephone-line-notmuch-counts ()
-  "This segment displays the count of unread notmuch messages in
-  the last window's mode-line (if unread messages are present)."
-
-  (update-display-notmuch-counts))
+;; TODO(tazjin): re-add this segment to the modeline
 
 (provide 'mail-setup)
diff --git a/users/tazjin/emacs/config/modes.el b/users/tazjin/emacs/config/modes.el
deleted file mode 100644
index 69fb523d0d..0000000000
--- a/users/tazjin/emacs/config/modes.el
+++ /dev/null
@@ -1,37 +0,0 @@
-;; Initializes modes I use.
-
-(add-hook 'prog-mode-hook 'esk-add-watchwords)
-(add-hook 'prog-mode-hook 'hl-line-mode)
-
-;; Use auto-complete as completion at point
-(defun set-auto-complete-as-completion-at-point-function ()
-  (setq completion-at-point-functions '(auto-complete)))
-
-(add-hook 'auto-complete-mode-hook
-          'set-auto-complete-as-completion-at-point-function)
-
-;; Enable rainbow-delimiters for all things programming
-(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
-
-;; Enable Paredit & Company in Emacs Lisp mode
-(add-hook 'emacs-lisp-mode-hook 'company-mode)
-
-;; Always highlight matching brackets
-(show-paren-mode 1)
-
-;; Always auto-close parantheses and other pairs
-(electric-pair-mode)
-
-;; Keep track of recent files
-(recentf-mode)
-
-;; Easily navigate sillycased words
-(global-subword-mode 1)
-
-;; Transparently open compressed files
-(auto-compression-mode t)
-
-;; Configure go-mode for Go2 Alpha
-(add-to-list 'auto-mode-alist '("\\.go2$" . go-mode))
-
-(provide 'modes)
diff --git a/users/tazjin/emacs/config/settings.el b/users/tazjin/emacs/config/settings.el
index 8b15b6cda1..6c66ca608d 100644
--- a/users/tazjin/emacs/config/settings.el
+++ b/users/tazjin/emacs/config/settings.el
@@ -45,4 +45,45 @@
 ;; Show time in 24h format
 (setq display-time-24hr-format t)
 
+;; Use python-mode for Starlark files.
+(add-to-list 'auto-mode-alist '("\\.star\\'" . python-mode))
+
+;; Use cmake-mode for relevant files.
+(add-to-list 'auto-mode-alist '("ya\\.make\\'" . cmake-ts-mode))
+
+;; Use tree-sitter modes for various languages.
+(setq major-mode-remap-alist
+      '((bash-mode . bash-ts-mode)
+        (c++-mode . c++-ts-mode)
+        (c-mode . c-ts-mode)
+        (c-or-c++-mode . c-or-c++-ts-mode)
+        (json-mode . json-ts-mode)
+        (python-mode . python-ts-mode)
+        (rust-mode . rust-ts-mode)
+        (toml-mode . toml-ts-mode)
+        (yaml-mode . yaml-ts-mode)
+        (go-mode . go-ts-mode)
+        (cmake-mode . cmake-ts-mode)))
+
+;; Visually highlight current line in programming buffers
+(add-hook 'prog-mode-hook 'hl-line-mode)
+
+;; Enable rainbow-delimiters for all things programming
+(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
+
+;; Always highlight matching brackets
+(show-paren-mode 1)
+
+;; Always auto-close parantheses and other pairs
+(electric-pair-mode)
+
+;; Keep track of recent files
+(recentf-mode)
+
+;; Easily navigate sillycased words
+(global-subword-mode 1)
+
+;; Transparently open compressed files
+(auto-compression-mode t)
+
 (provide 'settings)
diff --git a/users/tazjin/emacs/default.nix b/users/tazjin/emacs/default.nix
index c7c57ba355..46843432f1 100644
--- a/users/tazjin/emacs/default.nix
+++ b/users/tazjin/emacs/default.nix
@@ -1,167 +1,189 @@
 # This file builds an Emacs pre-configured with the packages I need
 # and my personal Emacs configuration.
-{ lib, pkgs, ... }:
+{ depot, lib, pkgs, ... }:
 
-pkgs.makeOverridable({ emacs ? pkgs.emacsGcc }:
-let
-  emacsWithPackages = (pkgs.emacsPackagesGen emacs).emacsWithPackages;
+pkgs.makeOverridable
+  ({ emacs ? pkgs.emacs29 }:
+  let
+    emacsPackages = (pkgs.emacsPackagesFor emacs);
+    emacsWithPackages = emacsPackages.emacsWithPackages;
 
-  # If switching telega versions, use this variable because it will
-  # keep the version check, binary path and so on in sync.
-  currentTelega = epkgs: epkgs.melpaPackages.telega;
+    # If switching telega versions, use this variable because it will
+    # keep the version check, binary path and so on in sync.
+    currentTelega = epkgs: epkgs.melpaPackages.telega;
 
-  # $PATH for binaries that need to be available to Emacs
-  emacsBinPath = lib.makeBinPath [
-    (currentTelega pkgs.emacsPackages)
-    pkgs.libwebp # for dwebp, required by telega
-  ];
+    # $PATH for binaries that need to be available to Emacs
+    emacsBinPath = lib.makeBinPath [
+      (currentTelega pkgs.emacsPackages)
+      pkgs.libwebp # for dwebp, required by telega
+    ];
 
-  identity = x: x;
+    identity = x: x;
 
-  tazjinsEmacs = pkgfun: (emacsWithPackages(epkgs: pkgfun(with epkgs; [
-    ace-link
-    ace-window
-    avy
-    bazel
-    browse-kill-ring
-    cargo
-    clojure-mode
-    cmake-mode
-    company
-    counsel
-    counsel-notmuch
-    d-mode
-    direnv
-    dockerfile-mode
-    eglot
-    elfeed
-    elixir-mode
-    elm-mode
-    erlang
-    exwm
-    flymake
-    go-mode
-    google-c-style
-    gruber-darker-theme
-    haskell-mode
-    ht
-    hydra
-    idle-highlight-mode
-    ivy
-    ivy-prescient
-    jq-mode
-    kotlin-mode
-    lsp-mode
-    magit
-    markdown-toc
-    meson-mode
-    multi-term
-    multiple-cursors
-    nginx-mode
-    nix-mode
-    notmuch
-    paredit
-    password-store
-    pinentry
-    polymode
-    prescient
-    protobuf-mode
-    rainbow-delimiters
-    rainbow-mode
-    refine
-    request
-    restclient
-    rust-mode
-    sly
-    string-edit
-    swiper
-    telephone-line
-    terraform-mode
-    toml-mode
-    transient
-    undo-tree
-    use-package
-    uuidgen
-    vterm
-    web-mode
-    websocket
-    which-key
-    xelb
-    yaml-mode
-    yasnippet
-    zoxide
+    # tree-sitter grammars for various ts-modes
+    customTreesitGrammars = emacs.pkgs.treesit-grammars.with-grammars (g: with g; [
+      tree-sitter-bash
+      tree-sitter-c
+      tree-sitter-cmake
+      tree-sitter-cpp
+      tree-sitter-css
+      tree-sitter-dockerfile
+      tree-sitter-go
+      tree-sitter-gomod
+      tree-sitter-hcl
+      tree-sitter-html
+      tree-sitter-java
+      tree-sitter-json
+      tree-sitter-latex
+      tree-sitter-make
+      tree-sitter-nix
+      tree-sitter-python
+      tree-sitter-rust
+      tree-sitter-sql
+      tree-sitter-toml
+      tree-sitter-yaml
+    ]);
 
-    # Wonky stuff
-    (currentTelega epkgs)
+    tazjinsEmacs = pkgfun: (emacsWithPackages (epkgs: pkgfun (with epkgs; [
+      ace-link
+      ace-window
+      avy
+      bazel
+      browse-kill-ring
+      cargo
+      clojure-mode
+      consult
+      deft
+      direnv
+      elixir-mode
+      elm-mode
+      erlang
+      depotExwm
+      go-mode
+      google-c-style
+      gruber-darker-theme
+      haskell-mode
+      ht
+      hydra
+      idle-highlight-mode
+      inspector
+      jq-mode
+      kotlin-mode
+      kubernetes
+      magit
+      markdown-toc
+      multiple-cursors
+      nginx-mode
+      nix-mode
+      notmuch
+      paredit
+      password-store
+      pinentry
+      prescient
+      protobuf-mode
+      rainbow-delimiters
+      rainbow-mode
+      request
+      restclient
+      rust-mode
+      sly
+      string-edit-at-point
+      terraform-mode
+      undo-tree
+      uuidgen
+      vertico
+      vterm
+      web-mode
+      websocket
+      which-key
+      xelb
+      yasnippet
+      zetteldeft
+      zoxide
 
-    # Custom depot packages (either ours, or overridden ones)
-    tvlPackages.dottime
-    tvlPackages.nix-util
-    tvlPackages.passively
-    tvlPackages.rcirc
-    tvlPackages.term-switcher
-    tvlPackages.tvl
-  ])));
+      # experimental (not otherwise embedded in config yet)
+      orderless
+      corfu
+      eat
 
-  # Tired of telega.el runtime breakages through tdlib
-  # incompatibility. Target to make that a build failure instead.
-  tdlibCheck =
-    let
-      tgEmacs = emacsWithPackages(epkgs: [ (currentTelega epkgs) ]);
-      verifyTdlibVersion = builtins.toFile "verify-tdlib-version.el" ''
-        (require 'telega)
-        (defvar tdlib-version "${pkgs.tdlib.version}")
-        (when (or (version< tdlib-version
-                            telega-tdlib-min-version)
-                  (and telega-tdlib-max-version
-                        (version< telega-tdlib-max-version
-                                  tdlib-version)))
-           (message "Found TDLib version %s, but require %s to %s"
-                   tdlib-version telega-tdlib-min-version telega-tdlib-max-version)
-          (kill-emacs 1))
-       '';
-    in pkgs.runCommandNoCC "tdlibCheck" {} ''
-      export PATH="${emacsBinPath}:$PATH"
-      ${tgEmacs}/bin/emacs --script ${verifyTdlibVersion} && touch $out
-    '';
-in lib.fix(self: l: f: pkgs.writeShellScriptBin "tazjins-emacs" ''
-  export PATH="${emacsBinPath}:$PATH"
-  exec ${tazjinsEmacs f}/bin/emacs \
-    --debug-init \
-    --no-site-file \
-    --no-site-lisp \
-    --no-init-file \
-    --directory ${./config} ${if l != null then "--directory ${l}" else ""} \
-    --eval "(require 'init)" $@
-  '' // {
-    # Call overrideEmacs with a function (pkgs -> pkgs) to modify the
-    # packages that should be included in this Emacs distribution.
-    overrideEmacs = f': self l f';
+      # Wonky stuff
+      (currentTelega epkgs)
+      customTreesitGrammars # TODO(tazjin): how is this *supposed* to work?!
+
+      # Custom depot packages (either ours, or overridden ones)
+      tvlPackages.dottime
+      tvlPackages.nix-util
+      tvlPackages.passively
+      tvlPackages.rcirc
+      tvlPackages.term-switcher
+      tvlPackages.treecrumbs
+      tvlPackages.tvl
 
-    # Call withLocalConfig with the path to a *folder* containing a
-    # `local.el` which provides local system configuration.
-    withLocalConfig = confDir: self confDir f;
+      # Dynamic/native modules
+      depot.users.tazjin.gio-list-apps
+    ])));
 
-    # Build a derivation that uses the specified local Emacs (i.e.
-    # built outside of Nix) instead
-    withLocalEmacs = emacsBin: pkgs.writeShellScriptBin "tazjins-emacs" ''
+    # Tired of telega.el runtime breakages through tdlib
+    # incompatibility. Target to make that a build failure instead.
+    tdlibCheck =
+      let
+        tgEmacs = emacsWithPackages (epkgs: [ (currentTelega epkgs) ]);
+        verifyTdlibVersion = builtins.toFile "verify-tdlib-version.el" ''
+          (require 'telega)
+          (defvar tdlib-version "${pkgs.tdlib.version}")
+          (when (or (version< tdlib-version
+                              telega-tdlib-min-version)
+                    (and telega-tdlib-max-version
+                          (version< telega-tdlib-max-version
+                                    tdlib-version)))
+             (message "Found TDLib version %s, but require %s to %s"
+                     tdlib-version telega-tdlib-min-version telega-tdlib-max-version)
+            (kill-emacs 1))
+        '';
+      in
+      pkgs.runCommand "tdlibCheck" { } ''
+        export PATH="${emacsBinPath}:$PATH"
+        ${tgEmacs}/bin/emacs --script ${verifyTdlibVersion} && touch $out
+      '';
+  in
+  lib.fix
+    (self: l: f: (pkgs.writeShellScriptBin "tazjins-emacs" ''
       export PATH="${emacsBinPath}:$PATH"
-      export EMACSLOADPATH="${(tazjinsEmacs f).deps}/share/emacs/site-lisp:"
-      exec ${emacsBin} \
+      exec ${tazjinsEmacs f}/bin/emacs \
         --debug-init \
         --no-site-file \
         --no-site-lisp \
         --no-init-file \
-        --directory ${./config} \
-        ${if l != null then "--directory ${l}" else ""} \
+        --directory ${./config} ${if l != null then "--directory ${l}" else ""} \
+        --eval "(add-to-list 'treesit-extra-load-path \"${customTreesitGrammars}/lib\")" \
         --eval "(require 'init)" $@
-    '';
+    '').overrideAttrs
+      (_: {
+        passthru = {
+          # Expose original Emacs used for my configuration.
+          inherit emacs;
+
+          # Expose the pure emacs with all packages.
+          inherit emacsPackages;
+          emacsWithPackages = tazjinsEmacs f;
+
+          # Call overrideEmacs with a function (pkgs -> pkgs) to modify the
+          # packages that should be included in this Emacs distribution.
+          overrideEmacs = f': self l f';
+
+          # Call withLocalConfig with the path to a *folder* containing a
+          # `local.el` which provides local system configuration.
+          withLocalConfig = confDir: self confDir f;
 
-    # Expose telega/tdlib version check as a target that is built in
-    # CI.
-    #
-    # TODO(tazjin): uncomment when telega works again
-    inherit tdlibCheck;
-    # meta.targets = [ "tdlibCheck" ];
-  }) null identity
-) {}
+          # Expose telega/tdlib version check as a target that is built in
+          # CI.
+          #
+          # TODO(tazjin): uncomment when telega works again
+          inherit tdlibCheck;
+          meta.ci.targets = [ "tdlibCheck" ];
+        };
+      }))
+    null
+    identity
+  )
+{ }
diff --git a/users/tazjin/finito/default.nix b/users/tazjin/finito/default.nix
index e50ac32be4..9a39591eab 100644
--- a/users/tazjin/finito/default.nix
+++ b/users/tazjin/finito/default.nix
@@ -2,4 +2,8 @@
 
 depot.third_party.naersk.buildPackage {
   src = ./.;
+
+  # Got broken by a rustc update (?)
+  # https://buildkite.com/tvl/depot/builds/17910#01841493-dc42-44f8-b904-32bf3d835485
+  meta.ci.skip = true;
 }
diff --git a/users/tazjin/finito/finito-core/src/lib.rs b/users/tazjin/finito/finito-core/src/lib.rs
index 517bfad2bc..aaec03a77b 100644
--- a/users/tazjin/finito/finito-core/src/lib.rs
+++ b/users/tazjin/finito/finito-core/src/lib.rs
@@ -38,8 +38,8 @@
 //!
 //!   * an event type representing all possible events in the machine
 //!
-//!   * an action type representing a description of all possible
-//!     side-effects of the machine
+//!   * an action type representing a description of all possible side-effects
+//!     of the machine
 //!
 //! Using the definition above we can now say that a transition in a
 //! state-machine, involving these three types, takes an initial state
@@ -92,14 +92,13 @@
 //!
 //!   * `finito`: Core components and classes of Finito
 //!
-//!   * `finito-in-mem`: In-memory implementation of state machines
-//!     that do not need to live longer than an application using
-//!     standard library concurrency primitives.
+//!   * `finito-in-mem`: In-memory implementation of state machines that do not
+//!     need to live longer than an application using standard library
+//!     concurrency primitives.
 //!
-//!   * `finito-postgres`: Postgres-backed, persistent implementation
-//!     of state machines that, well, do need to live longer. Uses
-//!     Postgres for concurrency synchronisation, so keep that in
-//!     mind.
+//!   * `finito-postgres`: Postgres-backed, persistent implementation of state
+//!     machines that, well, do need to live longer. Uses Postgres for
+//!     concurrency synchronisation, so keep that in mind.
 //!
 //! Which should cover most use-cases. Okay, enough prose, lets dive
 //! in.
@@ -110,8 +109,8 @@
 
 extern crate serde;
 
-use serde::Serialize;
 use serde::de::DeserializeOwned;
+use serde::Serialize;
 use std::fmt::Debug;
 use std::mem;
 
@@ -120,7 +119,10 @@ use std::mem;
 ///
 /// This trait is used to implement transition logic and to "tie the
 /// room together", with the room being our triplet of types.
-pub trait FSM where Self: Sized {
+pub trait FSM
+where
+    Self: Sized,
+{
     /// A human-readable string uniquely describing what this FSM
     /// models. This is used in log messages, database tables and
     /// various other things throughout Finito.
@@ -166,7 +168,7 @@ pub trait FSM where Self: Sized {
 
     /// `act` interprets and executes FSM actions. This is the only
     /// part of an FSM in which side-effects are allowed.
-    fn act(Self::Action, &Self::State) -> Result<Vec<Self::Event>, Self::Error>;
+    fn act(action: Self::Action, state: &Self::State) -> Result<Vec<Self::Event>, Self::Error>;
 }
 
 /// This function is the primary function used to advance a state
@@ -223,11 +225,13 @@ pub trait FSMBackend<S: 'static> {
     /// Insert a new state-machine into the backend's storage and
     /// return its newly allocated key.
     fn insert_machine<F>(&self, initial: F) -> Result<Self::Key, Self::Error>
-    where F: FSM + Serialize + DeserializeOwned;
+    where
+        F: FSM + Serialize + DeserializeOwned;
 
     /// Retrieve the current state of an FSM by its key.
     fn get_machine<F: FSM>(&self, key: Self::Key) -> Result<F, Self::Error>
-    where F: FSM + Serialize + DeserializeOwned;
+    where
+        F: FSM + Serialize + DeserializeOwned;
 
     /// Advance a state machine by applying an event and persisting it
     /// as well as any resulting actions.
@@ -236,8 +240,9 @@ pub trait FSMBackend<S: 'static> {
     /// on the backend used. Please consult the backend's
     /// documentation for details.
     fn advance<'a, F: FSM>(&'a self, key: Self::Key, event: F::Event) -> Result<F, Self::Error>
-    where F: FSM + Serialize + DeserializeOwned,
-          F::State: From<&'a S>,
-          F::Event: Serialize + DeserializeOwned,
-          F::Action: Serialize + DeserializeOwned;
+    where
+        F: FSM + Serialize + DeserializeOwned,
+        F::State: From<&'a S>,
+        F::Event: Serialize + DeserializeOwned,
+        F::Action: Serialize + DeserializeOwned;
 }
diff --git a/users/tazjin/finito/finito-door/src/lib.rs b/users/tazjin/finito/finito-door/src/lib.rs
index 68542c0bc4..441ab0e3d2 100644
--- a/users/tazjin/finito/finito-door/src/lib.rs
+++ b/users/tazjin/finito/finito-door/src/lib.rs
@@ -27,15 +27,15 @@
 //! The door can only be locked if it is closed. Oh, and it has a few
 //! extra features:
 //!
-//! * whenever the door's state changes, an IRC channel receives a
-//!   message about that
+//! * whenever the door's state changes, an IRC channel receives a message about
+//!   that
 //!
-//! * the door calls the police if the code is intered incorrectly more
-//!   than a specified number of times (mhm, lets say, three)
+//! * the door calls the police if the code is intered incorrectly more than a
+//!   specified number of times (mhm, lets say, three)
 //!
-//! * if the police is called the door can not be interacted with
-//!   anymore (and honestly, for the sake of this example, we don't
-//!   care how its functionality is restored)
+//! * if the police is called the door can not be interacted with anymore (and
+//!   honestly, for the sake of this example, we don't care how its
+//!   functionality is restored)
 //!
 //! ## The Door - Visualized
 //!
@@ -71,7 +71,8 @@
 //!
 //! Alright, enough foreplay, lets dive in!
 
-#[macro_use] extern crate serde_derive;
+#[macro_use]
+extern crate serde_derive;
 
 extern crate failure;
 extern crate finito;
@@ -292,11 +293,13 @@ mod tests {
     use finito::advance;
 
     fn test_fsm<S: FSM>(initial: S, events: Vec<S::Event>) -> (S, Vec<S::Action>) {
-        events.into_iter().fold((initial, vec![]), |(state, mut actions), event| {
-            let (new_state, mut new_actions) = advance(state, event);
-            actions.append(&mut new_actions);
-            (new_state, actions)
-        })
+        events
+            .into_iter()
+            .fold((initial, vec![]), |(state, mut actions), event| {
+                let (new_state, mut new_actions) = advance(state, event);
+                actions.append(&mut new_actions);
+                (new_state, actions)
+            })
     }
 
     #[test]
@@ -313,7 +316,10 @@ mod tests {
         ];
         let (final_state, actions) = test_fsm(initial, events);
 
-        assert_eq!(final_state, DoorState::Locked { code: 4567, attempts: 2 });
+        assert_eq!(final_state, DoorState::Locked {
+            code: 4567,
+            attempts: 2
+        });
         assert_eq!(actions, vec![
             DoorAction::NotifyIRC("door was closed".into()),
             DoorAction::NotifyIRC("door was opened".into()),
diff --git a/users/tazjin/finito/finito-postgres/src/error.rs b/users/tazjin/finito/finito-postgres/src/error.rs
index e130d18361..ed33775cd7 100644
--- a/users/tazjin/finito/finito-postgres/src/error.rs
+++ b/users/tazjin/finito/finito-postgres/src/error.rs
@@ -1,10 +1,9 @@
 //! This module defines error types and conversions for issue that can
 //! occur while dealing with persisted state machines.
 
-use std::result;
-use std::fmt;
-use uuid::Uuid;
 use std::error::Error as StdError;
+use std::{fmt, result};
+use uuid::Uuid;
 
 // errors to chain:
 use postgres::Error as PgError;
@@ -41,20 +40,15 @@ impl fmt::Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use ErrorKind::*;
         let msg = match &self.kind {
-            Serialization(err) =>
-                format!("JSON serialization error: {}", err),
+            Serialization(err) => format!("JSON serialization error: {}", err),
 
-            Database(err) =>
-                format!("PostgreSQL error: {}", err),
+            Database(err) => format!("PostgreSQL error: {}", err),
 
-            DBPool(err) =>
-                format!("Database connection pool error: {}", err),
+            DBPool(err) => format!("Database connection pool error: {}", err),
 
-            FSMNotFound(id) =>
-                format!("FSM with ID {} not found", id),
+            FSMNotFound(id) => format!("FSM with ID {} not found", id),
 
-            ActionNotFound(id) =>
-                format!("Action with ID {} not found", id),
+            ActionNotFound(id) => format!("Action with ID {} not found", id),
         };
 
         match &self.context {
@@ -66,7 +60,7 @@ impl fmt::Display for Error {
 
 impl StdError for Error {}
 
-impl <E: Into<ErrorKind>> From<E> for Error {
+impl<E: Into<ErrorKind>> From<E> for Error {
     fn from(err: E) -> Error {
         Error {
             kind: err.into(),
@@ -99,11 +93,11 @@ pub trait ResultExt<T> {
     fn context<C: fmt::Display>(self, ctx: C) -> Result<T>;
 }
 
-impl <T, E: Into<Error>> ResultExt<T> for result::Result<T, E> {
+impl<T, E: Into<Error>> ResultExt<T> for result::Result<T, E> {
     fn context<C: fmt::Display>(self, ctx: C) -> Result<T> {
         self.map_err(|err| Error {
             context: Some(format!("{}", ctx)),
-            .. err.into()
+            ..err.into()
         })
     }
 }
diff --git a/users/tazjin/finito/finito-postgres/src/lib.rs b/users/tazjin/finito/finito-postgres/src/lib.rs
index ae147f751f..ea63cc9dfd 100644
--- a/users/tazjin/finito/finito-postgres/src/lib.rs
+++ b/users/tazjin/finito/finito-postgres/src/lib.rs
@@ -4,8 +4,10 @@
 //!
 //! TODO: events & actions should have `SERIAL` keys
 
-#[macro_use] extern crate postgres;
-#[macro_use] extern crate postgres_derive;
+#[macro_use]
+extern crate postgres;
+#[macro_use]
+extern crate postgres_derive;
 
 extern crate chrono;
 extern crate finito;
@@ -14,23 +16,25 @@ extern crate serde;
 extern crate serde_json;
 extern crate uuid;
 
-#[cfg(test)] mod tests;
-#[cfg(test)] extern crate finito_door;
+#[cfg(test)]
+mod tests;
+#[cfg(test)]
+extern crate finito_door;
 
 mod error;
-pub use error::{Result, Error, ErrorKind};
+pub use error::{Error, ErrorKind, Result};
 
 use chrono::prelude::{DateTime, Utc};
 use error::ResultExt;
-use finito::{FSM, FSMBackend};
+use finito::{FSMBackend, FSM};
 use postgres::transaction::Transaction;
 use postgres::GenericConnection;
-use serde::Serialize;
+use r2d2_postgres::{r2d2, PostgresConnectionManager};
 use serde::de::DeserializeOwned;
+use serde::Serialize;
 use serde_json::Value;
 use std::marker::PhantomData;
 use uuid::Uuid;
-use r2d2_postgres::{r2d2, PostgresConnectionManager};
 
 type DBPool = r2d2::Pool<PostgresConnectionManager>;
 type DBConn = r2d2::PooledConnection<PostgresConnectionManager>;
@@ -112,15 +116,13 @@ pub struct FinitoPostgres<S> {
     db_pool: DBPool,
 }
 
-impl <S> FinitoPostgres<S> {
+impl<S> FinitoPostgres<S> {
     pub fn new(state: S, db_pool: DBPool, _pool_size: usize) -> Self {
-        FinitoPostgres {
-            state, db_pool,
-        }
+        FinitoPostgres { state, db_pool }
     }
 }
 
-impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
+impl<State: 'static> FSMBackend<State> for FinitoPostgres<State> {
     type Key = Uuid;
     type Error = Error;
 
@@ -134,10 +136,11 @@ impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
         let fsm = S::FSM_NAME.to_string();
         let state = serde_json::to_value(initial).context("failed to serialise FSM")?;
 
-        self.conn()?.execute(query, &[&id, &fsm, &state]).context("failed to insert FSM")?;
+        self.conn()?
+            .execute(query, &[&id, &fsm, &state])
+            .context("failed to insert FSM")?;
 
         return Ok(id);
-
     }
 
     fn get_machine<S: FSM + DeserializeOwned>(&self, key: Uuid) -> Result<S> {
@@ -156,10 +159,12 @@ impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
     /// processing is finished as running actions may result in additional
     /// transitions.
     fn advance<'a, S>(&'a self, key: Uuid, event: S::Event) -> Result<S>
-    where S: FSM + Serialize + DeserializeOwned,
-          S::State: From<&'a State>,
-          S::Event: Serialize + DeserializeOwned,
-          S::Action: Serialize + DeserializeOwned {
+    where
+        S: FSM + Serialize + DeserializeOwned,
+        S::State: From<&'a State>,
+        S::Event: Serialize + DeserializeOwned,
+        S::Action: Serialize + DeserializeOwned,
+    {
         let conn = self.conn()?;
         let tx = conn.transaction().context("could not begin transaction")?;
         let state = get_machine_internal(&tx, key, true)?;
@@ -187,16 +192,18 @@ impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
     }
 }
 
-impl <State: 'static> FinitoPostgres<State> {
+impl<State: 'static> FinitoPostgres<State> {
     /// Execute several actions at the same time, each in a separate
     /// thread. Note that actions returning further events, causing
     /// further transitions, returning further actions and so on will
     /// potentially cause multiple threads to get created.
-    fn run_actions<'a, S>(&'a self, fsm_id: Uuid, action_ids: Vec<Uuid>) where
+    fn run_actions<'a, S>(&'a self, fsm_id: Uuid, action_ids: Vec<Uuid>)
+    where
         S: FSM + Serialize + DeserializeOwned,
         S::Event: Serialize + DeserializeOwned,
         S::Action: Serialize + DeserializeOwned,
-        S::State: From<&'a State> {
+        S::State: From<&'a State>,
+    {
         let state: S::State = (&self.state).into();
         let conn = self.conn().expect("TODO");
 
@@ -214,17 +221,19 @@ impl <State: 'static> FinitoPostgres<State> {
 
     /// Retrieve a single connection from the database connection pool.
     fn conn(&self) -> Result<DBConn> {
-        self.db_pool.get().context("failed to retrieve connection from pool")
+        self.db_pool
+            .get()
+            .context("failed to retrieve connection from pool")
     }
 }
 
-
-
 /// Insert a single state-machine into the database and return its
 /// newly allocated, random UUID.
-pub fn insert_machine<C, S>(conn: &C, initial: S) -> Result<Uuid> where
+pub fn insert_machine<C, S>(conn: &C, initial: S) -> Result<Uuid>
+where
     C: GenericConnection,
-    S: FSM + Serialize {
+    S: FSM + Serialize,
+{
     let query = r#"
       INSERT INTO machines (id, fsm, state)
       VALUES ($1, $2, $3)
@@ -240,13 +249,12 @@ pub fn insert_machine<C, S>(conn: &C, initial: S) -> Result<Uuid> where
 }
 
 /// Insert a single event into the database and return its UUID.
-fn insert_event<C, S>(conn: &C,
-                      fsm_id: Uuid,
-                      event: &S::Event) -> Result<Uuid>
+fn insert_event<C, S>(conn: &C, fsm_id: Uuid, event: &S::Event) -> Result<Uuid>
 where
     C: GenericConnection,
     S: FSM,
-    S::Event: Serialize {
+    S::Event: Serialize,
+{
     let query = r#"
       INSERT INTO events (id, fsm, fsm_id, event)
       VALUES ($1, $2, $3, $4)
@@ -254,21 +262,19 @@ where
 
     let id = Uuid::new_v4();
     let fsm = S::FSM_NAME.to_string();
-    let event_value = serde_json::to_value(event)
-        .context("failed to serialize event")?;
+    let event_value = serde_json::to_value(event).context("failed to serialize event")?;
 
     conn.execute(query, &[&id, &fsm, &fsm_id, &event_value])?;
-    return Ok(id)
+    return Ok(id);
 }
 
 /// Insert a single action into the database and return its UUID.
-fn insert_action<C, S>(conn: &C,
-                       fsm_id: Uuid,
-                       event_id: Uuid,
-                       action: &S::Action) -> Result<Uuid> where
+fn insert_action<C, S>(conn: &C, fsm_id: Uuid, event_id: Uuid, action: &S::Action) -> Result<Uuid>
+where
     C: GenericConnection,
     S: FSM,
-    S::Action: Serialize {
+    S::Action: Serialize,
+{
     let query = r#"
       INSERT INTO actions (id, fsm, fsm_id, event_id, content, status)
       VALUES ($1, $2, $3, $4, $5, $6)
@@ -276,23 +282,26 @@ fn insert_action<C, S>(conn: &C,
 
     let id = Uuid::new_v4();
     let fsm = S::FSM_NAME.to_string();
-    let action_value = serde_json::to_value(action)
-        .context("failed to serialize action")?;
+    let action_value = serde_json::to_value(action).context("failed to serialize action")?;
 
-    conn.execute(
-        query,
-        &[&id, &fsm, &fsm_id, &event_id, &action_value, &ActionStatus::Pending]
-    )?;
+    conn.execute(query, &[
+        &id,
+        &fsm,
+        &fsm_id,
+        &event_id,
+        &action_value,
+        &ActionStatus::Pending,
+    ])?;
 
-    return Ok(id)
+    return Ok(id);
 }
 
 /// Update the state of a specified machine.
-fn update_state<C, S>(conn: &C,
-                      fsm_id: Uuid,
-                      state: &S) -> Result<()> where
+fn update_state<C, S>(conn: &C, fsm_id: Uuid, state: &S) -> Result<()>
+where
     C: GenericConnection,
-    S: FSM + Serialize {
+    S: FSM + Serialize,
+{
     let query = r#"
       UPDATE machines SET state = $1 WHERE id = $2
     "#;
@@ -312,23 +321,28 @@ fn update_state<C, S>(conn: &C,
 fn alter_for_update(alter: bool, query: &str) -> String {
     match alter {
         false => query.to_string(),
-        true  => format!("{} FOR UPDATE", query),
+        true => format!("{} FOR UPDATE", query),
     }
 }
 
 /// Retrieve the current state of a state machine from the database,
 /// optionally locking the machine state for the duration of some
 /// enclosing transaction.
-fn get_machine_internal<C, S>(conn: &C,
-                              id: Uuid,
-                              for_update: bool) -> Result<S> where
+fn get_machine_internal<C, S>(conn: &C, id: Uuid, for_update: bool) -> Result<S>
+where
     C: GenericConnection,
-    S: FSM + DeserializeOwned {
-    let query = alter_for_update(for_update, r#"
+    S: FSM + DeserializeOwned,
+{
+    let query = alter_for_update(
+        for_update,
+        r#"
       SELECT state FROM machines WHERE id = $1
-    "#);
+    "#,
+    );
 
-    let rows = conn.query(&query, &[&id]).context("failed to retrieve FSM")?;
+    let rows = conn
+        .query(&query, &[&id])
+        .context("failed to retrieve FSM")?;
 
     if let Some(row) = rows.into_iter().next() {
         Ok(serde_json::from_value(row.get(0)).context("failed to deserialize FSM")?)
@@ -339,20 +353,25 @@ fn get_machine_internal<C, S>(conn: &C,
 
 /// Retrieve an action from the database, optionally locking it for
 /// the duration of some enclosing transaction.
-fn get_action<C, S>(conn: &C, id: Uuid) -> Result<(ActionStatus, S::Action)> where
+fn get_action<C, S>(conn: &C, id: Uuid) -> Result<(ActionStatus, S::Action)>
+where
     C: GenericConnection,
     S: FSM,
-    S::Action: DeserializeOwned {
-    let query = alter_for_update(true, r#"
+    S::Action: DeserializeOwned,
+{
+    let query = alter_for_update(
+        true,
+        r#"
       SELECT status, content FROM actions
       WHERE id = $1 AND fsm = $2
-    "#);
+    "#,
+    );
 
     let rows = conn.query(&query, &[&id, &S::FSM_NAME])?;
 
     if let Some(row) = rows.into_iter().next() {
-        let action = serde_json::from_value(row.get(1))
-            .context("failed to deserialize FSM action")?;
+        let action =
+            serde_json::from_value(row.get(1)).context("failed to deserialize FSM action")?;
         Ok((row.get(0), action))
     } else {
         Err(ErrorKind::ActionNotFound(id).into())
@@ -360,13 +379,17 @@ fn get_action<C, S>(conn: &C, id: Uuid) -> Result<(ActionStatus, S::Action)> whe
 }
 
 /// Update the status of an action after an attempt to run it.
-fn update_action_status<C, S>(conn: &C,
-                              id: Uuid,
-                              status: ActionStatus,
-                              error: Option<String>,
-                              _fsm: PhantomData<S>) -> Result<()> where
+fn update_action_status<C, S>(
+    conn: &C,
+    id: Uuid,
+    status: ActionStatus,
+    error: Option<String>,
+    _fsm: PhantomData<S>,
+) -> Result<()>
+where
     C: GenericConnection,
-    S: FSM {
+    S: FSM,
+{
     let query = r#"
       UPDATE actions SET status = $1, error = $2
       WHERE id = $3 AND fsm = $4
@@ -389,10 +412,16 @@ fn update_action_status<C, S>(conn: &C,
 /// panic), the error will be persisted. Should it fail by panicking
 /// (which developers should never do explicitly in action
 /// interpreters) its status will not be changed.
-fn run_action<S>(tx: Transaction, id: Uuid, state: &S::State, _fsm: PhantomData<S>)
-                 -> Result<Vec<S::Event>> where
+fn run_action<S>(
+    tx: Transaction,
+    id: Uuid,
+    state: &S::State,
+    _fsm: PhantomData<S>,
+) -> Result<Vec<S::Event>>
+where
     S: FSM,
-    S::Action: DeserializeOwned {
+    S::Action: DeserializeOwned,
+{
     let (status, action) = get_action::<Transaction, S>(&tx, id)?;
 
     let result = match status {
@@ -401,29 +430,25 @@ fn run_action<S>(tx: Transaction, id: Uuid, state: &S::State, _fsm: PhantomData<
                 // If the action succeeded, update its status to
                 // completed and return the created events.
                 Ok(events) => {
-                    update_action_status(
-                        &tx, id, ActionStatus::Completed, None, PhantomData::<S>
-                    )?;
+                    update_action_status(&tx, id, ActionStatus::Completed, None, PhantomData::<S>)?;
                     events
-                },
+                }
 
                 // If the action failed, persist the debug message and
                 // return nothing.
                 Err(err) => {
                     let msg = Some(format!("{:?}", err));
-                    update_action_status(
-                        &tx, id, ActionStatus::Failed, msg, PhantomData::<S>
-                    )?;
+                    update_action_status(&tx, id, ActionStatus::Failed, msg, PhantomData::<S>)?;
                     vec![]
-                },
+                }
             }
-        },
+        }
 
         _ => {
             // TODO: Currently only pending actions are run because
             // retryable actions are not yet implemented.
             vec![]
-        },
+        }
     };
 
     tx.commit().context("failed to commit transaction")?;
diff --git a/users/tazjin/finito/finito-postgres/src/tests.rs b/users/tazjin/finito/finito-postgres/src/tests.rs
index b1b5821be3..dd270c3875 100644
--- a/users/tazjin/finito/finito-postgres/src/tests.rs
+++ b/users/tazjin/finito/finito-postgres/src/tests.rs
@@ -16,7 +16,11 @@ fn test_insert_machine() {
     let door = insert_machine(&conn, initial).expect("Failed to insert door");
     let result = get_machine(&conn, &door, false).expect("Failed to fetch door");
 
-    assert_eq!(result, DoorState::Opened, "Inserted door state should match");
+    assert_eq!(
+        result,
+        DoorState::Opened,
+        "Inserted door state should match"
+    );
 }
 
 #[test]
@@ -41,7 +45,10 @@ fn test_advance() {
     }
 
     let result = get_machine(&conn, &door, false).expect("Failed to fetch door");
-    let expected = DoorState::Locked { code: 4567, attempts: 2 };
+    let expected = DoorState::Locked {
+        code: 4567,
+        attempts: 2,
+    };
 
     assert_eq!(result, expected, "Advanced door state should match");
 }
diff --git a/users/tazjin/generator-example/.gitignore b/users/tazjin/generator-example/.gitignore
new file mode 100644
index 0000000000..ea8c4bf7f3
--- /dev/null
+++ b/users/tazjin/generator-example/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/users/tazjin/generator-example/Cargo.lock b/users/tazjin/generator-example/Cargo.lock
new file mode 100644
index 0000000000..a6f25ee394
--- /dev/null
+++ b/users/tazjin/generator-example/Cargo.lock
@@ -0,0 +1,124 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "genawaiter"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0"
+dependencies = [
+ "genawaiter-macro",
+ "genawaiter-proc-macro",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "genawaiter-macro"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc"
+
+[[package]]
+name = "genawaiter-proc-macro"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784f84eebc366e15251c4a8c3acee82a6a6f427949776ecb88377362a9621738"
+dependencies = [
+ "proc-macro-error",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "generator-example"
+version = "0.1.0"
+dependencies = [
+ "genawaiter",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "syn-mid",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.20+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn-mid"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baa8e7560a164edb1621a55d18a0c59abf49d360f47aa7b821061dd7eea7fac9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
diff --git a/users/tazjin/generator-example/Cargo.toml b/users/tazjin/generator-example/Cargo.toml
new file mode 100644
index 0000000000..faf313973f
--- /dev/null
+++ b/users/tazjin/generator-example/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "generator-example"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+genawaiter = "0.99.1"
diff --git a/users/tazjin/generator-example/README.md b/users/tazjin/generator-example/README.md
new file mode 100644
index 0000000000..0bec13ee9a
--- /dev/null
+++ b/users/tazjin/generator-example/README.md
@@ -0,0 +1,11 @@
+generator-example
+=================
+
+This is an experiment with the [`genawaiter`][] crate, to see if it
+could be suitable for dealing with the execution flattening problem in
+Tvix.
+
+It constructs a dummy example that is similar to some of the problems
+we have in Tvix that require generator-like thunk forcing.
+
+[`genawaiter`]: https://docs.rs/genawaiter/latest/genawaiter/index.html
diff --git a/users/tazjin/generator-example/src/main.rs b/users/tazjin/generator-example/src/main.rs
new file mode 100644
index 0000000000..4aa931caf8
--- /dev/null
+++ b/users/tazjin/generator-example/src/main.rs
@@ -0,0 +1,115 @@
+use genawaiter::rc::{Co, Gen};
+use std::cell::RefCell;
+use std::future::Future;
+use std::pin::Pin;
+use std::rc::Rc;
+
+#[derive(Debug)]
+enum ValueRepr {
+    Int(i64),
+    Thunk((i64, i64)),
+}
+
+#[derive(Clone, Debug)]
+struct Value(Rc<RefCell<ValueRepr>>);
+
+impl Value {
+    fn force(&self) {
+        let mut inner = self.0.borrow_mut();
+        match *inner {
+            ValueRepr::Int(_) => return,
+            ValueRepr::Thunk((a, b)) => {
+                *inner = ValueRepr::Int(a + b);
+            }
+        }
+    }
+
+    fn is_forced(&self) -> bool {
+        matches!(*self.0.borrow(), ValueRepr::Int(_))
+    }
+
+    fn int(&self) -> i64 {
+        match *self.0.borrow() {
+            ValueRepr::Int(i) => i,
+            ValueRepr::Thunk(_) => panic!("unforced thunk!"),
+        }
+    }
+}
+
+impl From<i64> for Value {
+    fn from(value: i64) -> Self {
+        Value(Rc::new(RefCell::new(ValueRepr::Int(value))))
+    }
+}
+
+impl From<(i64, i64)> for Value {
+    fn from(value: (i64, i64)) -> Self {
+        Value(Rc::new(RefCell::new(ValueRepr::Thunk(value))))
+    }
+}
+
+async fn list_maker(values: Vec<Value>, co: Co<Value>) -> Vec<i64> {
+    let mut output: Vec<i64> = vec![];
+
+    for value in values {
+        if !value.is_forced() {
+            co.yield_(value.clone()).await;
+        }
+
+        output.push(value.int());
+    }
+
+    output
+}
+
+async fn list_reverser(values: Vec<Value>, co: Co<Value>) -> Vec<i64> {
+    let mut output = list_maker(values, co).await;
+    output.reverse();
+    output
+}
+
+struct Frame {
+    gen: Gen<Value, (), Pin<Box<dyn Future<Output = Vec<i64>>>>>,
+}
+
+fn pin_future(
+    f: impl Future<Output = Vec<i64>> + 'static,
+) -> Pin<Box<dyn Future<Output = Vec<i64>>>> {
+    Box::pin(f)
+}
+
+fn main() {
+    let mut frames: Vec<Frame> = vec![];
+
+    let values: Vec<Value> = vec![
+        42.into(),
+        (12, 54).into(),
+        4.into(),
+        (40, 2).into(),
+        2.into(),
+    ];
+    let second = values.clone();
+
+    frames.push(Frame {
+        gen: Gen::new(|co| pin_future(list_maker(values, co))),
+    });
+
+    frames.push(Frame {
+        gen: Gen::new(|co| pin_future(list_reverser(second, co))),
+    });
+
+    for (idx, mut frame) in frames.into_iter().enumerate() {
+        loop {
+            match frame.gen.resume() {
+                genawaiter::GeneratorState::Yielded(val) => {
+                    println!("yielded {:?} in frame {}", val, idx);
+                    val.force();
+                }
+                genawaiter::GeneratorState::Complete(list) => {
+                    println!("result {}: {:?}", idx, list);
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/users/tazjin/gio-list-apps/.gitignore b/users/tazjin/gio-list-apps/.gitignore
new file mode 100644
index 0000000000..2f7896d1d1
--- /dev/null
+++ b/users/tazjin/gio-list-apps/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/tazjin/gio-list-apps/Cargo.lock b/users/tazjin/gio-list-apps/Cargo.lock
new file mode 100644
index 0000000000..b475b35a6c
--- /dev/null
+++ b/users/tazjin/gio-list-apps/Cargo.lock
@@ -0,0 +1,616 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
+[[package]]
+name = "cfg-expr"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "ctor"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "emacs"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6797a940189d353de79bec32abe717aeeecd79a08236e84404c888354e040665"
+dependencies = [
+ "anyhow",
+ "ctor",
+ "emacs-macros",
+ "emacs_module",
+ "once_cell",
+ "rustc_version",
+ "thiserror",
+]
+
+[[package]]
+name = "emacs-macros"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69656fdfe7c2608b87164964db848b5c3795de7302e3130cce7131552c6be161"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "emacs_module"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3067bc974045ed2c6db333bd4fc30d3bdaafa6421a9a889fa7b2826b6f7f2fa"
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-core",
+ "futures-macro",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gio"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7884cba6b1c5db1607d970cadf44b14a43913d42bc68766eea6a5e2fe0891524"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-util",
+ "gio-sys",
+ "glib",
+ "libc",
+ "once_cell",
+ "pin-project-lite",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gio-list-apps"
+version = "0.1.0"
+dependencies = [
+ "emacs",
+ "gio",
+]
+
+[[package]]
+name = "gio-sys"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+ "winapi",
+]
+
+[[package]]
+name = "glib"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "331156127e8166dd815cf8d2db3a5beb492610c716c03ee6db4f2d07092af0a7"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "futures-util",
+ "gio-sys",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "memchr",
+ "once_cell",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "179643c50bf28d20d2f6eacd2531a88f2f5d9747dd0b86b8af1e8bb5dd0de3c0"
+dependencies = [
+ "heck",
+ "proc-macro-crate",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
+dependencies = [
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "indexmap"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
+dependencies = [
+ "once_cell",
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
+
+[[package]]
+name = "strsim"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
+
+[[package]]
+name = "thiserror"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "toml"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "version-compare"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "winnow"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
+dependencies = [
+ "memchr",
+]
diff --git a/users/tazjin/gio-list-apps/Cargo.toml b/users/tazjin/gio-list-apps/Cargo.toml
new file mode 100644
index 0000000000..eb62d1fcaf
--- /dev/null
+++ b/users/tazjin/gio-list-apps/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "gio-list-apps"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+emacs = "0.18.0"
+gio = "0.18.1"
diff --git a/users/tazjin/gio-list-apps/default.nix b/users/tazjin/gio-list-apps/default.nix
new file mode 100644
index 0000000000..c63f4dd487
--- /dev/null
+++ b/users/tazjin/gio-list-apps/default.nix
@@ -0,0 +1,14 @@
+{ depot, pkgs, lib, ... }:
+
+pkgs.rustPlatform.buildRustPackage {
+  name = "gio-list-apps";
+  src = lib.cleanSource ./.;
+  cargoLock.lockFile = ./Cargo.lock;
+  nativeBuildInputs = [ pkgs.pkg-config ];
+  buildInputs = [ pkgs.gtk3 depot.users.tazjin.emacs.emacs ];
+
+  postInstall = ''
+    mkdir -p $out/share/emacs/site-lisp
+    ln -s $out/lib/libgio_list_apps.so $out/share/emacs/site-lisp/gio-list-apps.so
+  '';
+}
diff --git a/users/tazjin/gio-list-apps/src/lib.rs b/users/tazjin/gio-list-apps/src/lib.rs
new file mode 100644
index 0000000000..55eb8dc0be
--- /dev/null
+++ b/users/tazjin/gio-list-apps/src/lib.rs
@@ -0,0 +1,31 @@
+use emacs::{defun, Env, IntoLisp, Result, Value};
+use gio::traits::AppInfoExt;
+use gio::AppInfo;
+
+emacs::plugin_is_GPL_compatible!();
+
+#[emacs::module(defun_prefix = "taz", mod_in_name = false)]
+fn init(_: &Env) -> Result<()> {
+    Ok(())
+}
+
+/// Returns an alist of the currently available XDG applications (through their
+/// `.desktop' shortcuts), and the command line parameters needed to start them.
+///
+/// Hidden applications or applications without specified command-line
+/// parameters are not included.
+#[defun]
+fn list_xdg_apps(env: &Env) -> Result<Value> {
+    let mut visible_apps: Vec<Value> = vec![];
+
+    for app in AppInfo::all().into_iter().filter(AppInfo::should_show) {
+        if let Some(cmd) = app
+            .commandline()
+            .and_then(|p| Some(p.to_str()?.to_string()))
+        {
+            visible_apps.push(env.cons(app.name().as_str().into_lisp(env)?, cmd.into_lisp(env)?)?);
+        }
+    }
+
+    env.list(&visible_apps)
+}
diff --git a/users/tazjin/home/khamovnik.nix b/users/tazjin/home/khamovnik.nix
new file mode 100644
index 0000000000..6bac67eb1c
--- /dev/null
+++ b/users/tazjin/home/khamovnik.nix
@@ -0,0 +1,10 @@
+# Home manage configuration for zamalek.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+{
+  imports = [
+    depot.users.tazjin.home.shared
+  ];
+}
diff --git a/users/tazjin/home/persistence.nix b/users/tazjin/home/persistence.nix
new file mode 100644
index 0000000000..9ea5ca8eb9
--- /dev/null
+++ b/users/tazjin/home/persistence.nix
@@ -0,0 +1,42 @@
+# Persistence configuration for machines with throw-away setups.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+{
+  imports = [ (depot.third_party.sources.impermanence + "/home-manager.nix") ];
+
+  home.persistence."/persist/tazjin/home" = {
+    allowOther = true;
+
+    directories = [
+      ".cargo"
+      ".config/audacity"
+      ".config/chromium"
+      ".config/google-chrome"
+      ".config/quassel-irc.org"
+      ".config/syncthing"
+      ".config/unity3d"
+      ".electrum"
+      ".gnupg"
+      ".local/share/audacity"
+      ".local/share/direnv"
+      ".local/share/fish"
+      ".local/share/keyrings"
+      ".local/share/zoxide"
+      ".mozilla/firefox"
+      ".password-store"
+      ".rustup"
+      ".ssh"
+      ".steam"
+      ".telega"
+      ".thunderbird"
+      "go"
+      "mail"
+    ];
+
+    files = [
+      ".notmuch-config"
+    ];
+  };
+}
diff --git a/users/tazjin/home/shared.nix b/users/tazjin/home/shared.nix
new file mode 100644
index 0000000000..38d8add4ac
--- /dev/null
+++ b/users/tazjin/home/shared.nix
@@ -0,0 +1,91 @@
+# Shared home configuration for all machines.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+
+let
+  # URL handler to open `tg://` URLs in telega.el
+  telega-launcher = pkgs.writeShellScriptBin "telega-launcher" ''
+    echo "Opening ''${1} in telega.el ..."
+    ${depot.users.tazjin.emacs.emacs}/bin/emacsclient -e "(telega-browse-url \"''${1}\")"
+  '';
+in
+{
+  home.activation.screenshots = lib.hm.dag.entryAnywhere ''
+    $DRY_RUN_CMD mkdir -p $HOME/screenshots
+  '';
+
+  programs.git = {
+    enable = true;
+    userName = "Vincent Ambo";
+    userEmail = "mail@tazj.in";
+    extraConfig = {
+      pull.rebase = true;
+      init.defaultBranch = "canon";
+      safe.directory = [ "/depot" ];
+    };
+  };
+
+  programs.fish = {
+    enable = true;
+    interactiveShellInit = ''
+      # emacs vterm integration
+      source (find '${pkgs.emacsPackages.vterm}' -name 'emacs-vterm.fish')
+
+      # z
+      ${pkgs.zoxide}/bin/zoxide init fish | source
+    '';
+  };
+
+  services.screen-locker = {
+    enable = true;
+    inactiveInterval = 10; # minutes
+    lockCmd = "${depot.users.tazjin.screenLock}/bin/tazjin-screen-lock";
+  };
+
+  home.packages = [ telega-launcher ];
+
+  xdg.desktopEntries.telega-launcher = {
+    name = "Telega Launcher";
+    exec = "${telega-launcher}/bin/telega-launcher";
+    terminal = false;
+    mimeType = [ "x-scheme-handler/tg" ];
+  };
+
+  xdg.mimeApps = {
+    enable = true;
+    defaultApplications = {
+      "x-scheme-handler/tg" = [ "telega-launcher.desktop" ];
+      "text/html" = [ "firefox.desktop" ];
+      "x-scheme-handler/http" = [ "firefox.desktop" ];
+      "x-scheme-handler/https" = [ "firefox.desktop" ];
+      "x-scheme-handler/about" = [ "firefox.desktop" ];
+      "x-scheme-handler/unknown" = [ "firefox.desktop" ];
+    };
+  };
+
+  services.picom = {
+    enable = true;
+    vSync = true;
+    backend = "glx";
+  };
+
+  services.syncthing.enable = true;
+
+  # Enable the dunst notification daemon, but force the
+  # configuration file separately instead of going via the strange
+  # Nix->dunstrc encoding route.
+  services.dunst.enable = true;
+  xdg.configFile."dunst/dunstrc" = {
+    source = depot.users.tazjin.dotfiles.dunstrc;
+    onChange = ''
+      ${pkgs.procps}/bin/pkill -u "$USER" ''${VERBOSE+-e} dunst || true
+    '';
+  };
+
+  systemd.user.startServices = true;
+
+  # Previous default version, see https://github.com/nix-community/home-manager/blob/master/docs/release-notes/rl-2211.adoc
+  home.stateVersion = "18.09";
+}
diff --git a/users/tazjin/home/tverskoy.nix b/users/tazjin/home/tverskoy.nix
new file mode 100644
index 0000000000..6f1116340c
--- /dev/null
+++ b/users/tazjin/home/tverskoy.nix
@@ -0,0 +1,18 @@
+# Home manage configuration for tverskoy.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+{
+  imports = [
+    depot.users.tazjin.home.shared
+    depot.users.tazjin.home.persistence
+  ];
+
+  home.persistence."/persist/tazjin/home" = {
+    directories = [
+      ".config/spotify"
+      ".local/share/Steam"
+    ];
+  };
+}
diff --git a/users/tazjin/home/zamalek.nix b/users/tazjin/home/zamalek.nix
new file mode 100644
index 0000000000..d24de945bb
--- /dev/null
+++ b/users/tazjin/home/zamalek.nix
@@ -0,0 +1,11 @@
+# Home manage configuration for zamalek.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+{
+  imports = [
+    depot.users.tazjin.home.shared
+    depot.users.tazjin.home.persistence
+  ];
+}
diff --git a/users/tazjin/homepage/default.nix b/users/tazjin/homepage/default.nix
index 2ce1cf6322..b46f9d4917 100644
--- a/users/tazjin/homepage/default.nix
+++ b/users/tazjin/homepage/default.nix
@@ -12,16 +12,21 @@ with nix.yants;
 
 let
   inherit (builtins) readFile replaceStrings sort;
-  inherit (pkgs) writeFile runCommandNoCC;
+  inherit (pkgs) writeFile runCommand;
 
   # The different types of entries on the homepage.
-  entryClass = enum "entryClass" [ "blog" "project" "misc" ];
+  entryClass = enum "entryClass" [
+    "blog"
+    "project"
+    "note"
+    "misc"
+  ];
 
   # The definition of a single entry.
   entry = struct "entry" {
     class = entryClass;
-    title = string;
-    url = string;
+    title = option string;
+    url = option string;
     date = int; # epoch
     description = option string;
   };
@@ -33,28 +38,42 @@ let
     title = post.title;
     url = "/blog/${post.key}";
     date = post.date;
+    description = post.description or "Blog post from ${formatDate post.date}";
   });
 
-  formatDate = defun [ int string ] (date: readFile (runCommandNoCC "date" {} ''
-    date --date='@${toString date}' '+%Y-%m-%d' > $out
+  formatDate = defun [ int string ] (date: readFile (runCommand "date" { } ''
+    date --date='@${toString date}' '+%Y-%m-%d' | tr -d '\n' > $out
   ''));
 
-  formatEntryDate = defun [ entry string ] (entry: entryClass.match entry.class {
-    blog = "Blog post from ${formatDate entry.date}";
-    project = "Project from ${formatDate entry.date}";
-    misc = "Posted on ${formatDate entry.date}";
-  });
+  entryUrl = defun [ entry string ] (entry:
+    if entry.class == "note"
+    then "#${toString entry.date}"
+    else entry.url
+  );
+
+  hasDescription = defun [ entry bool ] (entry:
+    ((entry ? description) && (entry.description != null))
+  );
+
+  entryTitle = defun [ entry string ] (entry:
+    let
+      optionalColon = lib.optionalString (hasDescription entry) ":";
+      titleText =
+        if (!(entry ? title) && (entry.class == "note"))
+        then "[${formatDate entry.date}]"
+        else lib.optionalString (entry ? title) ((escape entry.title) + optionalColon);
+    in
+    lib.optionalString (titleText != "")
+      ''<span class="entry-title ${entry.class}">${titleText}</span>''
+  );
 
   entryToDiv = defun [ entry string ] (entry: ''
-    <a href="${entry.url}" class="entry ${entry.class}">
-      <div>
-        <p class="entry-title">${escape entry.title}</p>
-        ${
-          lib.optionalString ((entry ? description) && (entry.description != null))
-          "<p class=\"entry-description\">${escape entry.description}</p>"
-        }
-        <p class="entry-date">${formatEntryDate entry}</p>
-      </div>
+    <a href="${entryUrl entry}" id="${toString entry.date}" class="entry">
+      ${entryTitle entry}
+      ${
+        lib.optionalString (hasDescription entry)
+        "<span class=\"entry-description\">${escape entry.description}</span>"
+      }
     </a>
   '');
 
@@ -67,7 +86,8 @@ let
   pageEntries = import ./entries.nix;
   homepage = index ((map postToEntry users.tazjin.blog.posts) ++ pageEntries);
   atomFeed = import ./feed.nix (args // { inherit entry pageEntries; });
-in runCommandNoCC "website" {} ''
+in
+runCommand "website" { } ''
   mkdir $out
   cp ${homepage} $out/index.html
   cp ${atomFeed} $out/feed.atom
diff --git a/users/tazjin/homepage/entries.nix b/users/tazjin/homepage/entries.nix
index 9e43516e53..0e98c073ef 100644
--- a/users/tazjin/homepage/entries.nix
+++ b/users/tazjin/homepage/entries.nix
@@ -1,12 +1,27 @@
+let
+  note = date: description: {
+    class = "note";
+    inherit description date;
+  };
+in
 [
   {
+    class = "project";
+    title = "VolgaSprint - Nix hacking in Kazan";
+    url = "https://volgasprint.org/";
+    date = 1712307024;
+    description = ''
+      Hacking on Nix projects for a week in Kazan, Russia, in August
+      2024. Come join us!
+    '';
+  }
+  {
     class = "misc";
     title = "@tazlog on Telegram";
     url = "https://t.me/tazlog";
     date = 1643321164;
     description = ''
-      My new channel on Telegram, for occasional updates smaller (and
-      more frequent) than what ends up being posted here.
+      My Telegram channel with occasional random life updates and musings.
     '';
   }
   {
@@ -15,8 +30,7 @@
     url = "https://changelog.com/shipit/37";
     date = 1641819600;
     description = ''
-      Episode #37 of Ship It!, a podcast about systems, featuring me.
-      We talk about TVL, Nix, monorepos and related things.
+      Podcast episode about TVL, Nix, monorepos and all sorts of related things.
     '';
   }
   {
@@ -24,9 +38,7 @@
     title = "Tvix";
     url = "https://tvl.fyi/blog/rewriting-nix";
     date = 1638381387;
-    description = ''
-      TVL is rewriting Nix with funding from NLNet.
-    '';
+    description = "TVL is rewriting Nix with funding from NLNet.";
   }
   {
     class = "misc";
@@ -34,8 +46,7 @@
     url = "https://www.youtube.com/watch?v=P-2P3MSZrBM";
     date = 1594594800;
     description = ''
-      A fascinating, mind-bending interview by Lex Fridman with Joscha
-      Bach about the Nature of the Universe.
+      Mind-bending discussion with philosopher Joscha Bach.
     '';
   }
   {
@@ -43,7 +54,7 @@
     title = "The Virus Lounge";
     url = "https://tvl.fyi";
     date = 1587435629;
-    description = "A community around Nix, monorepos, build tooling and the like!";
+    description = "A community around Nix, monorepos, build tooling and more!";
   }
   {
     class = "project";
@@ -71,7 +82,7 @@
     title = "dottime";
     url = "https://dotti.me/";
     date = 1560898800;
-    description = "A universal convention for conveying time (by edef <3)";
+    description = "A universal convention for conveying time";
   }
   {
     class = "project";
@@ -86,18 +97,63 @@
     url = "https://principiadiscordia.com/book/1.php";
     date = 1495494000;
     description = ''
-      The Principia is a short book I read as a child, and didn't
-      understand until much later. It shaped much of my world view.
+      A short book about everything that everyone should read.
     '';
   }
   {
     class = "misc";
-    title = "This Week in Virology";
-    url = "http://www.microbe.tv/twiv/";
-    date = 1585517557;
-    description = ''
-      Podcast with high-quality information about virology,
-      epidemiology and so on. Highly relevant to COVID19.
-    '';
+    title = "Nix β€” Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠ°ΠΊΠ΅Ρ‚Π½Ρ‹ΠΉ ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ€";
+    date = 1663923600;
+    url = "https://www.youtube.com/watch?v=0Lhahzs-Wos";
+    description = "Двухчасовой (!) Ρ€Π°Π·Π³ΠΎΠ²ΠΎΡ€ с Π²Π²Π΅Π΄Π΅Π½ΠΈΠ΅ΠΌ Π² Nix, NixOS ΠΈ Ρ‚Π°ΠΊ Π΄Π°Π»Π΅Π΅";
+  }
+  {
+    class = "project";
+    title = "yandex-cloud-rs";
+    date = 1650877200;
+    url = "https://docs.rs/yandex-cloud";
+    description = "ΠŸΡ€ΠΎΡΡ‚ΠΎΠΉ SDK Π½Π° Rust для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с API Yandex Cloud.";
+  }
+  {
+    class = "project";
+    title = "nix-1p";
+    date = 1564650000;
+    url = "https://code.tvl.fyi/about/nix/nix-1p";
+    description = "A (more or less) one-page introduction to the Nix language.";
+  }
+  {
+    class = "misc";
+    title = "Π‘Ρ‚Π°Π²ΠΈΠΌ NixOS!";
+    date = 1678784400;
+    url = "https://progmsk.timepad.ru/event/2358560/";
+    description = "ВстрСча Π² undef.space для ΠΏΠΎΠΌΠΎΡ‰ΠΈ Π² Π½Π°Ρ‡Π°Π»Π΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Nix/NixOS";
+  }
+  {
+    class = "misc";
+    title = "Tvix - September '22";
+    date = 1662973200;
+    url = "https://tvl.fyi/blog/tvix-status-september-22";
+    description = "Tvix update blog post over on TVL";
+  }
+  {
+    class = "project";
+    title = "Tvixbolt";
+    date = 1667293200;
+    url = "https://bolt.tvix.dev/";
+    description = "In-browser language evaluator for Nix, based on Tvix";
+  }
+  {
+    class = "project";
+    title = "ООО Π’Π’Π›";
+    date = 1609491600;
+    url = "https://tvl.su/ru/";
+    description = "ΠžΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ сайт ΠΌΠΎΠ΅ΠΉ ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ ΠΏΠΎ IT-консалтингу.";
   }
+
+  # Notes.
+  (note 1676106000 "If you have a Huawei device that sometimes struggles on public Wi-Fi networks, try enabling MAC-address randomisation. Huawei devices often get pushed onto management networks!")
+  (note 1686868637 "I moved some of my pages (including this one) to a machine in my flat in Moscow. If you end up having access trouble because your ISP blocks Russian resources, please let me know.")
+  (note 1686868636 "Protip: Use the Reddit blackout to click the 'Logout' button, and never come back.")
+  (note 1486550941 "↓ I no longer recommend people to use this. Generate your configuration from a language like Nix instead.")
+  (note 1576800001 "↓ No longer just my projects, it's all of TVL! Go check it out.")
 ]
diff --git a/users/tazjin/homepage/feed.nix b/users/tazjin/homepage/feed.nix
index 2a033444e8..8043d7ff30 100644
--- a/users/tazjin/homepage/feed.nix
+++ b/users/tazjin/homepage/feed.nix
@@ -4,7 +4,7 @@
 with depot.nix.yants;
 
 let
-  inherit (builtins) map readFile;
+  inherit (builtins) filter map readFile;
   inherit (lib) max singleton;
   inherit (pkgs) writeText;
   inherit (depot.web) blog atom-feed;
@@ -23,7 +23,7 @@ let
   });
 
   allEntries = (with depot.users.tazjin.blog; map (blog.toFeedEntry config) posts)
-             ++ (map pageEntryToEntry pageEntries);
+    ++ (map pageEntryToEntry (filter (e: e.class != "note") pageEntries));
 
   feed = {
     id = "https://tazj.in/";
@@ -39,4 +39,5 @@ let
 
     entries = allEntries;
   };
-in writeText "feed.atom" (atom-feed.renderFeed feed)
+in
+writeText "feed.atom" (atom-feed.renderFeed feed)
diff --git a/users/tazjin/homepage/header.html b/users/tazjin/homepage/header.html
index 5a22d9eb7b..320b5ded8c 100644
--- a/users/tazjin/homepage/header.html
+++ b/users/tazjin/homepage/header.html
@@ -3,6 +3,7 @@
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <meta name="description" content="tazjin&#39;s blog">
   <link rel="stylesheet" type="text/css" href="static/tvl.css" media="all">
+  <link rel="stylesheet" type="text/css" href="static/tazjin.css" media="all">
   <link rel="icon" type="image/webp" href="/static/favicon.webp">
   <link rel="alternate" type="application/atom+xml" href="/feed.atom">
   <title>tazjin&#39;s interblag</title>
@@ -15,22 +16,14 @@
     <hr>
   </header>
   <div class="introduction">
-    <p>Hi, I'm tazjin.</p>
     <p>
-      I spend a lot of my time hacking on the
-      <a class="dark-link" href="https://tvl.fyi">TVL</a> monorepo and
-      doing other computer-related things. Follow me
-      on <a class="dark-link" href="https://t.me/tazlog">Telegram</a>,
-      via the feed here or (occasionally) catch me in-person
-      at <a href="https://undef.club/#about" class="dark-link">
-      undef.club</a>.
-    </p>
-    <p>
-      Below is a collection of
+      Below are some of
       my <span class="project">projects</span>, <span class="blog">blog
-      posts</span> and some <span class="misc">random things</span> by
-      me or others. If you'd like to get in touch about anything, send
-      me a mail at mail@[this domain] or ping me on IRC.
+      posts</span>, <span class="note">notes</span> and some
+      other <span class="misc">random things</span>. If you'd like to
+      get in touch, email me at mail@[this domain] or ping me
+      on <a class="dark-link" href="https://tvl.fyi">TVL</a> IRC.
     </p>
+    <hr>
   </div>
   <div class="entry-container">
diff --git a/users/tazjin/homepage/static/tazjin.css b/users/tazjin/homepage/static/tazjin.css
new file mode 100644
index 0000000000..f921b562ee
--- /dev/null
+++ b/users/tazjin/homepage/static/tazjin.css
@@ -0,0 +1,57 @@
+/* Homepage styling */
+
+.dark {
+    background-color: #181818;
+    color: #e4e4ef;
+}
+
+.dark-link, .interblag-title {
+    color: #96a6c8;
+}
+
+
+.interblag-title {
+    text-decoration: none;
+}
+
+.entry-container {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: nowrap;
+    justify-content: flex-start;
+}
+
+.entry {
+    margin-top: 5px;
+    margin-bottom: 5px;
+    padding-left: 5px;
+    text-decoration: none;
+}
+
+.entry:nth-child(odd) {
+    background: #282828;
+}
+
+.entry-description {
+    color: #e4e4ef;
+}
+
+.misc {
+    color: #73c936;
+    border-color: #73c936;
+}
+
+.blog {
+    color: #268bd2;
+    border-color: #268bd2;
+}
+
+.project {
+    color: #ff4f58;
+    border-color: #ff4f58;
+}
+
+.note {
+    color: #ffdd33;
+    border-color: #ffdd33;
+}
diff --git a/users/tazjin/keys.nix b/users/tazjin/keys.nix
deleted file mode 100644
index d6941b66d9..0000000000
--- a/users/tazjin/keys.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-# My SSH public keys
-{ ... }:
-
-let withAll = keys: keys // { all = builtins.attrValues keys; };
-in withAll {
-  # frog = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKMZzRdcrHTuCPoaFy36MPr5IW/hnImlse/OBOn6udL/ tazjin@frog";
-  s10e = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDf7CNlYoauHcSYsMNnCZt5h9QSYH/7keYkg8g3hT32+";
-  tverskoy = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBAWvA3RpXpMAqruUbB+eVgvvHCzhs5R9khFRza3YSLeFiIqOxVVgyhzW/BnCSD9t/5JrqRdJIGQLnkQU9m4REhUAAAAEc3NoOg== tazjin@tverskoy";
-}
diff --git a/users/tazjin/keys/default.nix b/users/tazjin/keys/default.nix
new file mode 100644
index 0000000000..16b232b094
--- /dev/null
+++ b/users/tazjin/keys/default.nix
@@ -0,0 +1,12 @@
+# My SSH public keys
+{ ... }:
+
+let withAll = keys: keys // { all = builtins.attrValues keys; };
+in withAll {
+  tverskoy = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBAWvA3RpXpMAqruUbB+eVgvvHCzhs5R9khFRza3YSLeFiIqOxVVgyhzW/BnCSD9t/5JrqRdJIGQLnkQU9m4REhUAAAAEc3NoOg== tazjin@tverskoy";
+  tverskoy_ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM1fGWz/gsq+ZeZXjvUrV+pBlanw1c3zJ9kLTax9FWQy tazjin@tverskoy";
+  zamalek_sk = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOAw3OaPAjnC6hArGYEmBoXhPf7aZdRGlDZcSqm6gbB8AAAABHNzaDo= tazjin@zamalek";
+  zamalek_ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDBRXeb8EuecLHP0bW4zuebXp4KRnXgJTZfeVWXQ1n1R tazjin@zamalek";
+  khamovnik_yk = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPgOyR4rRM8IaVGgN2ZxGlKtd7GLYbxdRTRa3u9EhRNSkHAvRTN9sgw7mm0iPLnHChPy10anKV43vTaIm906Gm8=";
+  khamovnik_agenix = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG4YSl5+DHQR3rOoBJLQfQ840U0CrYkByMKdzu/LDxoT tazjin@khamovnik";
+}
diff --git a/users/tazjin/kinesis/README.md b/users/tazjin/kinesis/README.md
new file mode 100644
index 0000000000..7cd95a5e5f
--- /dev/null
+++ b/users/tazjin/kinesis/README.md
@@ -0,0 +1,10 @@
+Kinesis configuration
+=====================
+
+This folder backs up the configuration for my Kinesis keyboards.
+Configuration is not mutually compatible between the Advantage 2 and
+the Advantage 360, so they are stored in different folders and
+(mostly) programmed on-board.
+
+I keep these around in case I get a new keyboard and want to bootstrap
+it to behave the same way as the previous one.
diff --git a/users/tazjin/kinesis/advantage2/qwerty.txt b/users/tazjin/kinesis/advantage2/qwerty.txt
new file mode 100755
index 0000000000..624e809c22
--- /dev/null
+++ b/users/tazjin/kinesis/advantage2/qwerty.txt
@@ -0,0 +1,6 @@
+[caps]>[rwin]

+[lctrl]>[lalt]

+[delete]>[lctrl]

+[rctrl]>[rwin]

+{pup}>{-rwin}{b}{+rwin}

+{pdown}>{-rwin}{f}{+rwin}

diff --git a/users/tazjin/nix.svg b/users/tazjin/nix.svg
index 4da795a436..d2ef7c81aa 100644
--- a/users/tazjin/nix.svg
+++ b/users/tazjin/nix.svg
@@ -33,7 +33,7 @@
        id="lambda-4"
        href="#lambda-path"
        visibility="visible"
-       fill="#f8f8ff" />
+       fill="#d52b1e" />
     <use
        id="lambda-5"
        transform="rotate(60,407.11155,-715.78724)"
@@ -45,6 +45,6 @@
        id="lambda-6"
        href="#lambda-path"
        visibility="visible"
-       fill="#d52b1e" />
+       fill="#f8f8ff" />
   </g>
 </svg>
diff --git a/users/tazjin/nixos/.gitignore b/users/tazjin/nixos/.gitignore
new file mode 100644
index 0000000000..212d3ad270
--- /dev/null
+++ b/users/tazjin/nixos/.gitignore
@@ -0,0 +1 @@
+local-config.nix
diff --git a/users/tazjin/nixos/camden/default.nix b/users/tazjin/nixos/camden/default.nix
index 4b5e4b4872..130b51dd38 100644
--- a/users/tazjin/nixos/camden/default.nix
+++ b/users/tazjin/nixos/camden/default.nix
@@ -1,7 +1,8 @@
 # This file configures camden.tazj.in, my homeserver.
 { depot, pkgs, lib, ... }:
 
-config: let
+config:
+let
   nginxRedirect = { from, to, acmeHost }: {
     serverName = from;
     useACMEHost = acmeHost;
@@ -9,22 +10,13 @@ config: let
 
     extraConfig = "return 301 https://${to}$request_uri;";
   };
-in lib.fix(self: {
-  # Disable the current ACME module and use the old one from 19.09
-  # instead, until the various regressions have been sorted out.
-  # TODO(tazjin): Remove this once the new ACME module works.
-  disabledModules = [ "security/acme" ];
-  imports =
-    let oldChannel = fetchTarball {
-      # NixOS 19.09 on 2020-10-04
-      url = "https://github.com/NixOS/nixpkgs-channels/archive/75f4ba05c63be3f147bcc2f7bd4ba1f029cedcb1.tar.gz";
-      sha256 = "157c64220lf825ll4c0cxsdwg7cxqdx4z559fdp7kpz0g6p8fhhr";
-    };
-    in [
-      "${depot.path}/ops/modules/quassel.nix"
-      "${depot.path}/ops/modules/smtprelay.nix"
-      "${oldChannel}/nixos/modules/security/acme.nix"
-    ];
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+in
+lib.fix (self: {
+  imports = [
+    (mod "quassel.nix")
+    (mod "smtprelay.nix")
+  ];
 
   # camden is intended to boot unattended, despite having an encrypted
   # root partition.
@@ -37,8 +29,14 @@ in lib.fix(self: {
   boot = {
     initrd = {
       availableKernelModules = [
-        "ahci" "xhci_pci" "usbhid" "usb_storage" "sd_mod" "sdhci_pci"
-        "rtsx_usb_sdmmc" "r8169"
+        "ahci"
+        "xhci_pci"
+        "usbhid"
+        "usb_storage"
+        "sd_mod"
+        "sdhci_pci"
+        "rtsx_usb_sdmmc"
+        "r8169"
       ];
 
       kernelModules = [ "dm-snapshot" ];
@@ -56,7 +54,7 @@ in lib.fix(self: {
       efi.canTouchEfiVariables = true;
     };
 
-    cleanTmpDir = true;
+    tmp.cleanOnBoot = true;
   };
 
   fileSystems = {
@@ -76,16 +74,14 @@ in lib.fix(self: {
     };
   };
 
-  nix = {
-    maxJobs = lib.mkDefault 4;
-
-    trustedUsers = [ "root" "tazjin" ];
-
-    binaryCaches = [
+  nix.settings = {
+    max-jobs = lib.mkDefault 4;
+    trusted-users = [ "root" "tazjin" ];
+    substituters = [
       "https://tazjin.cachix.org"
     ];
 
-    binaryCachePublicKeys = [
+    trusted-public-keys = [
       "tazjin.cachix.org-1:IZkgLeqfOr1kAZjypItHMg1NoBjm4zX9Zzep8oRSh7U="
     ];
   };
@@ -112,7 +108,7 @@ in lib.fix(self: {
   programs.mosh.enable = true;
 
   fonts = {
-    fonts = [ pkgs.jetbrains-mono ];
+    packages = [ pkgs.jetbrains-mono ];
     fontconfig.defaultFonts.monospace = [ "JetBrains Mono" ];
   };
 
@@ -128,7 +124,7 @@ in lib.fix(self: {
       bat
       curl
       direnv
-      emacs27-nox
+      emacs28-nox
       fswebcam
       git
       gnupg
@@ -152,7 +148,7 @@ in lib.fix(self: {
     };
 
     # Set up a user & group for general git shenanigans
-    groups.git = {};
+    groups.git = { };
     users.git = {
       group = "git";
       isSystemUser = true;
@@ -167,7 +163,7 @@ in lib.fix(self: {
   services.tailscale.enable = true;
 
   # Allow sudo-ing via the forwarded SSH agent.
-  security.pam.enableSSHAgentAuth = true;
+  security.pam.sshAgentAuth.enable = true;
 
   # NixOS 20.03 broke nginx and I can't be bothered to debug it
   # anymore, all solution attempts have failed, so here's a
@@ -191,38 +187,36 @@ in lib.fix(self: {
   # Provision a TLS certificate outside of nginx to avoid
   # nixpkgs#38144
   security.acme = {
-    # acceptTerms = true;
+    acceptTerms = true;
 
     certs."tazj.in" = {
       email = "mail@tazj.in";
-      user = "nginx";
       group = "nginx";
       webroot = "/var/lib/acme/acme-challenge";
-      extraDomains = {
-        "cs.tazj.in" = null;
-        "git.tazj.in" = null;
-        "www.tazj.in" = null;
+      postRun = "systemctl reload nginx";
+
+      extraDomainNames = [
+        "cs.tazj.in"
+        "git.tazj.in"
+        "www.tazj.in"
 
         # Local domains (for this machine only)
-        "camden.tazj.in" = null;
-      };
-      postRun = "systemctl reload nginx";
+        "camden.tazj.in"
+      ];
     };
 
     certs."quassel.tazj.in" = {
       email = "mail@tazj.in";
       webroot = "/var/lib/acme/challenge-quassel";
-      user = "nginx"; # required because of a bug in the ACME module
       group = "quassel";
-      allowKeysForGroup = true;
     };
   };
 
   # Forward logs to Google Cloud Platform
   services.journaldriver = {
-    enable                 = true;
-    logStream              = "home";
-    googleCloudProject     = "tazjins-infrastructure";
+    enable = true;
+    logStream = "home";
+    googleCloudProject = "tazjins-infrastructure";
     applicationCredentials = "/etc/gcp/key.json";
   };
 
diff --git a/users/tazjin/nixos/default.nix b/users/tazjin/nixos/default.nix
index 04123a3b5a..8f82c39ea1 100644
--- a/users/tazjin/nixos/default.nix
+++ b/users/tazjin/nixos/default.nix
@@ -1,10 +1,12 @@
 { depot, lib, ... }:
 
 let systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
-in {
+in depot.nix.readTree.drvTargets {
   camdenSystem = systemFor depot.users.tazjin.nixos.camden;
   frogSystem = systemFor depot.users.tazjin.nixos.frog;
   tverskoySystem = systemFor depot.users.tazjin.nixos.tverskoy;
-
-  meta.targets = [ "camdenSystem" "frogSystem" "tverskoySystem" ];
+  zamalekSystem = systemFor depot.users.tazjin.nixos.zamalek;
+  koptevoRaw = depot.ops.nixos.nixosFor depot.users.tazjin.nixos.koptevo;
+  koptevoSystem = systemFor depot.users.tazjin.nixos.koptevo;
+  khamovnikSystem = systemFor depot.users.tazjin.nixos.khamovnik;
 }
diff --git a/users/tazjin/nixos/frog/default.nix b/users/tazjin/nixos/frog/default.nix
index b3c803c871..dfb6b46d5a 100644
--- a/users/tazjin/nixos/frog/default.nix
+++ b/users/tazjin/nixos/frog/default.nix
@@ -1,6 +1,7 @@
 { depot, lib, pkgs, ... }:
 
-config: let
+config:
+let
   inherit (pkgs) lieer;
 
   quasselClient = pkgs.quassel.override {
@@ -8,13 +9,10 @@ config: let
     enableDaemon = false;
     monolithic = false;
   };
-in lib.fix(self: {
-  imports = [
-    "${depot.path}/ops/modules/v4l2loopback.nix"
-  ];
-
+in
+lib.fix (self: {
   boot = {
-    tmpOnTmpfs = true;
+    tmp.useTmpfs = true;
     kernelModules = [ "kvm-amd" ];
 
     loader = {
@@ -59,10 +57,9 @@ in lib.fix(self: {
     };
   };
 
-  nix = {
-    maxJobs = 48;
-    binaryCaches = ["ssh://nix-ssh@whitby.tvl.fyi"];
-    binaryCachePublicKeys = ["cache.tvl.fyi:fd+9d1ceCPvDX/xVhcfv8nAa6njEhAGAEe+oGJDEeoc="];
+  nix.settings = {
+    max-jobs = 48;
+    substituters = [ "ssh://nix-ssh@whitby.tvl.fyi" ];
   };
 
   networking = {
@@ -109,7 +106,7 @@ in lib.fix(self: {
   };
 
   fonts = {
-    fonts = with pkgs; [
+    packages = with pkgs; [
       corefonts
       dejavu_fonts
       jetbrains-mono
@@ -155,8 +152,8 @@ in lib.fix(self: {
 
   services.xserver = {
     enable = true;
-    layout = "us";
-    xkbOptions = "caps:super";
+    xkb.layout = "us";
+    xkb.options = "caps:super";
     exportConfiguration = true;
     videoDrivers = [ "amdgpu" ];
     displayManager = {
@@ -220,13 +217,12 @@ in lib.fix(self: {
       bat
       chromium
       clang-manpages
-      clang-tools_11
-      clang_11
+      clang-tools
+      clang
       curl
       direnv
       dnsutils
-      emacs27 # mostly for emacsclient
-      exa
+      emacs28 # mostly for emacsclient
       fd
       file
       gdb
@@ -242,7 +238,7 @@ in lib.fix(self: {
       jq
       kubectl
       linuxPackages.perf
-      manpages
+      man-pages
       miller
       msmtp
       nix-prefetch-github
@@ -260,7 +256,6 @@ in lib.fix(self: {
       ripgrep
       rustup
       screen
-      scrot
       spotify
       tokei
       transmission
diff --git a/users/tazjin/nixos/khamovnik/default.nix b/users/tazjin/nixos/khamovnik/default.nix
new file mode 100644
index 0000000000..8ea925c90d
--- /dev/null
+++ b/users/tazjin/nixos/khamovnik/default.nix
@@ -0,0 +1,133 @@
+# Yandex work laptop
+#
+# Some of the configuration for this machine is not public.
+{ depot, lib, pkgs, ... }:
+
+config:
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+  usermod = name: depot.path.origSrc + ("/users/tazjin/nixos/modules/" + name);
+  private = /arc/junk/tazjin;
+
+  zdevice = device: {
+    inherit device;
+    fsType = "zfs";
+  };
+in
+{
+  imports = [
+    (usermod "chromium.nix")
+    (usermod "desktop.nix")
+    (usermod "fonts.nix")
+    (usermod "home-config.nix")
+    (usermod "laptop.nix")
+    (usermod "physical.nix")
+    (pkgs.home-manager.src + "/nixos")
+  ] ++ (if (builtins.pathExists private) then [
+    (private + "/nixos/yandex.nix")
+    (private + "/emacs/module.nix")
+  ] else [ ]);
+
+  # from hardware-configuration.nix
+  boot = {
+    initrd.luks.devices."luks-9c3cd590-a648-450d-ae42-ed3859d4c717".device =
+      "/dev/disk/by-uuid/9c3cd590-a648-450d-ae42-ed3859d4c717";
+
+    initrd.availableKernelModules = [
+      "xhci_pci"
+      "thunderbolt"
+      "ahci"
+      "nvme"
+      "usb_storage"
+      "sd_mod"
+      "rtsx_pci_sdmmc"
+    ];
+    kernelModules = [ "kvm-intel" ];
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/disk/by-uuid/1f783029-c4f9-4192-b893-84f4f0c2a493";
+      fsType = "ext4";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/DD01-2B3E";
+      fsType = "vfat";
+    };
+  };
+
+  swapDevices = [{
+    device = "/dev/disk/by-uuid/9b9049c5-5975-441d-9ac6-2f9150775fd6";
+  }];
+
+  tvl.cache.enable = true;
+
+  networking.hostName = "khamovnik";
+
+  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
+  hardware.cpu.intel.updateMicrocode = true;
+  hardware.enableRedistributableFirmware = true;
+  hardware.opengl.extraPackages = with pkgs; [
+    intel-compute-runtime
+    intel-media-driver
+    intel-vaapi-driver
+  ];
+
+  # from generated configuration.nix
+  # Bootloader.
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  # Setup keyfile
+  boot.initrd.secrets = {
+    "/crypto_keyfile.bin" = null;
+  };
+
+  # Enable swap on luks
+  boot.initrd.luks.devices."luks-e9a4b4dc-ade2-45bf-8ed0-0ed5c4c392c9".device = "/dev/disk/by-uuid/e9a4b4dc-ade2-45bf-8ed0-0ed5c4c392c9";
+  boot.initrd.luks.devices."luks-e9a4b4dc-ade2-45bf-8ed0-0ed5c4c392c9".keyFile = "/crypto_keyfile.bin";
+
+  # Select internationalisation properties.
+  i18n.defaultLocale = "en_US.UTF-8";
+  i18n.extraLocaleSettings = {
+    LC_ADDRESS = "ru_RU.UTF-8";
+    LC_IDENTIFICATION = "ru_RU.UTF-8";
+    LC_MEASUREMENT = "ru_RU.UTF-8";
+    LC_MONETARY = "ru_RU.UTF-8";
+    LC_NAME = "ru_RU.UTF-8";
+    LC_NUMERIC = "ru_RU.UTF-8";
+    LC_PAPER = "ru_RU.UTF-8";
+    LC_TELEPHONE = "ru_RU.UTF-8";
+    LC_TIME = "ru_RU.UTF-8";
+  };
+
+  # Enable sound with pipewire.
+  sound.enable = true;
+  hardware.pulseaudio.enable = false;
+  security.rtkit.enable = true;
+  services.pipewire = {
+    enable = true;
+    alsa.enable = true;
+    alsa.support32Bit = true;
+    pulse.enable = true;
+  };
+
+  # Try to work around Intel CPU throttling bugs
+  services.throttled.enable = true;
+
+  virtualisation.docker.enable = true;
+
+  hardware.bluetooth.enable = true;
+  users.users.tazjin.extraGroups = [ "tss" ];
+
+  environment.systemPackages = with pkgs; [
+    tdesktop
+    linuxPackages.perf
+    hotspot
+    protobuf
+  ];
+
+  system.stateVersion = "23.05"; # Did you read the comment?
+}
diff --git a/users/tazjin/nixos/koptevo/default.nix b/users/tazjin/nixos/koptevo/default.nix
new file mode 100644
index 0000000000..ea8dfd4bd8
--- /dev/null
+++ b/users/tazjin/nixos/koptevo/default.nix
@@ -0,0 +1,187 @@
+# NUC in my closet.
+_: # ignore readTree options
+
+{ config, depot, lib, pkgs, ... }:
+
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+  usermod = name: depot.path.origSrc + ("/users/tazjin/nixos/modules/" + name);
+in
+{
+  imports = [
+    (mod "quassel.nix")
+    (mod "www/base.nix")
+    (mod "www/tazj.in.nix")
+    (usermod "airsonic.nix")
+    (usermod "geesefs.nix")
+    (usermod "predlozhnik.nix")
+    (usermod "tgsa.nix")
+    (usermod "miniflux.nix")
+    (depot.third_party.agenix.src + "/modules/age.nix")
+  ];
+
+  boot = {
+    loader.systemd-boot.enable = true;
+    loader.efi.canTouchEfiVariables = true;
+    initrd.availableKernelModules = [ "ahci" "xhci_pci" "usb_storage" "sd_mod" "sdhci_pci" ];
+    kernelModules = [ "kvm-intel" ];
+    kernelParams = [ "nomodeset" ];
+  };
+
+  nix.settings.trusted-users = [ "tazjin" ];
+
+  fileSystems = {
+    "/" = {
+      device = "rpool/root";
+      fsType = "zfs";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/E214-E6B3";
+      fsType = "vfat";
+    };
+
+    "/var" = {
+      device = "rpool/var";
+      fsType = "zfs";
+    };
+
+    "/home" = {
+      device = "rpool/home";
+      fsType = "zfs";
+    };
+  };
+
+  hardware.cpu.intel.updateMicrocode = true;
+  hardware.enableRedistributableFirmware = true;
+  services.fwupd.enable = true;
+
+  networking = {
+    hostName = "koptevo";
+    hostId = "07bbbf4f";
+    domain = "tazj.in";
+    useDHCP = true;
+    firewall.enable = true;
+    firewall.allowedTCPPorts = [ 22 80 443 ];
+
+    wireless.enable = true;
+    wireless.networks."How do I computer fast?" = {
+      psk = "washyourface";
+    };
+  };
+
+  time.timeZone = "UTC";
+
+  security.acme.acceptTerms = true;
+  security.acme.defaults.email = lib.mkForce "acme@tazj.in";
+
+  programs.fish.enable = true;
+
+  users.users.tazjin = {
+    isNormalUser = true;
+    extraGroups = [ "wheel" "docker" "systemd-journal" ];
+    shell = pkgs.fish;
+    openssh.authorizedKeys.keys = depot.users.tazjin.keys.all;
+  };
+
+  age.secrets =
+    let
+      secretFile = name: depot.users.tazjin.secrets."${name}.age";
+    in
+    {
+      tgsa-yandex.file = secretFile "tgsa-yandex";
+    };
+
+  security.sudo.wheelNeedsPassword = false;
+
+  services.openssh.enable = true;
+
+  services.depot.quassel = {
+    enable = true;
+    acmeHost = "koptevo.tazj.in";
+    bindAddresses = [
+      "0.0.0.0"
+    ];
+  };
+
+  services.tailscale = {
+    enable = true;
+    useRoutingFeatures = "server"; # for exit-node usage
+  };
+
+  # Automatically collect garbage from the Nix store.
+  services.depot.automatic-gc = {
+    enable = true;
+    interval = "daily";
+    diskThreshold = 15; # GiB
+    maxFreed = 10; # GiB
+    preserveGenerations = "14d";
+  };
+
+  services.nginx.virtualHosts."koptevo.tazj.in" = {
+    addSSL = true;
+    enableACME = true;
+
+    extraConfig = ''
+      location = / {
+        return 302 https://at.tvl.fyi/?q=%2F%2Fusers%2Ftazjin%2Fnixos%2Fkoptevo%2Fdefault.nix;
+      }
+    '';
+  };
+
+  # I don't use the podcast nor playlist feature,
+  # but I *have to* supply podcasts to gonic ...
+  systemd.tmpfiles.rules = [
+    "d /tmp/fake-podcasts 0555 nobody nobody -"
+    "d /tmp/fake-playlists 0555 nobody nobody -"
+  ];
+
+  services.gonic = {
+    enable = true;
+    settings = {
+      listen-addr = "0.0.0.0:4747";
+      scan-interval = 5;
+      scan-at-start-enabled = true;
+      podcast-path = [ "/tmp/fake-podcasts" ];
+      playlists-path = [ "/tmp/fake-playlists" ];
+      music-path = [ "/var/lib/geesefs/tazjins-files/music" ];
+    };
+  };
+
+  # hack to work around the strict sandboxing of the gonic module
+  # breaking DNS resolution
+  systemd.services.gonic.serviceConfig.BindReadOnlyPaths = [
+    "-/etc/resolv.conf"
+  ];
+
+  # add a hard dependency on the FUSE mount
+  systemd.services.gonic.requires = [ "geesefs.service" ];
+
+  services.nginx.virtualHosts."music.tazj.in" = {
+    addSSL = true;
+    enableACME = true;
+
+    locations."/" = {
+      proxyPass = "http://127.0.0.1:4747";
+    };
+  };
+
+  # List packages installed in system profile. To search, run:
+  # $ nix search wget
+  environment.systemPackages = with pkgs; [
+    curl
+    htop
+    jq
+    nmap
+    bat
+    emacs-nox
+    nano
+    wget
+  ];
+
+  programs.mtr.enable = true;
+  programs.mosh.enable = true;
+  zramSwap.enable = true;
+
+  system.stateVersion = "23.05";
+}
diff --git a/users/tazjin/nixos/modules/airsonic.nix b/users/tazjin/nixos/modules/airsonic.nix
new file mode 100644
index 0000000000..815f183778
--- /dev/null
+++ b/users/tazjin/nixos/modules/airsonic.nix
@@ -0,0 +1,32 @@
+# airsonic is a decent, web-based player UI for subsonic
+{ pkgs, ... }:
+
+let
+  env = builtins.toFile "env.js" ''
+    window.env = {
+      SERVER_URL: "https://music.tazj.in",
+    }
+  '';
+
+  airsonicDist = pkgs.fetchzip {
+    name = "airsonic-refix";
+
+    # from master CI @ f894d5eacebec2f47486f340c8610f446d4f64b3
+    # https://github.com/tamland/airsonic-refix/actions/runs/6150155527
+    url = "https://storage.yandexcloud.net/tazjin-public/airsonic-refix-f894d5ea.zip";
+    sha256 = "02rnh9h7rh22wkghays389yddwbwg7sawmczdxdmjrcnkc7mq2jz";
+
+    stripRoot = false;
+    postFetch = "cp ${env} $out/env.js";
+  };
+in
+{
+  services.nginx.virtualHosts."player.tazj.in" = {
+    enableACME = true;
+    forceSSL = true;
+    root = "${airsonicDist}";
+
+    # deal with SPA routing requirements
+    locations."/".extraConfig = "try_files $uri /index.html;";
+  };
+}
diff --git a/users/tazjin/nixos/modules/chromium.nix b/users/tazjin/nixos/modules/chromium.nix
new file mode 100644
index 0000000000..22f1c8d362
--- /dev/null
+++ b/users/tazjin/nixos/modules/chromium.nix
@@ -0,0 +1,30 @@
+# Configure the Chromium browser with various useful things.
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = [
+    (pkgs.chromium.override {
+      enableWideVine = true; # DRM support (for Кинопоиск)
+    })
+  ];
+
+  programs.chromium = {
+    enable = true;
+    homepageLocation = "about:blank";
+
+    extensions = [
+      "dbepggeogbaibhgnhhndojpepiihcmeb" # Vimium
+      "cjpalhdlnbpafiamejdnhcphjbkeiagm" # uBlock Origin
+      "mohaicophfnifehkkkdbcejkflmgfkof" # nitter redirect
+      "lhdifindchogekmjooeiolmjdlheilae" # Huruf
+    ];
+
+    extraOpts = {
+      SpellcheckEnabled = true;
+      SpellcheckLanguage = [
+        "ru"
+        "en-GB"
+      ];
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/default.nix b/users/tazjin/nixos/modules/default.nix
new file mode 100644
index 0000000000..d747e8e131
--- /dev/null
+++ b/users/tazjin/nixos/modules/default.nix
@@ -0,0 +1,2 @@
+# Make readTree happy at this level.
+_: { }
diff --git a/users/tazjin/nixos/modules/desktop.nix b/users/tazjin/nixos/modules/desktop.nix
new file mode 100644
index 0000000000..12a42b8faa
--- /dev/null
+++ b/users/tazjin/nixos/modules/desktop.nix
@@ -0,0 +1,55 @@
+# EXWM and other desktop configuration.
+{ config, depot, lib, pkgs, ... }:
+
+{
+  services = {
+    pipewire = {
+      enable = true;
+      alsa.enable = true;
+      alsa.support32Bit = true;
+      pulse.enable = true;
+    };
+
+    redshift.enable = true;
+    blueman.enable = true;
+
+    xserver = {
+      enable = true;
+      xkb.layout = "us";
+      xkb.options = "caps:super";
+
+      libinput.enable = true;
+
+      displayManager = {
+        # Give EXWM permission to control the session.
+        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER";
+        lightdm.enable = true;
+        # lightdm.greeters.gtk.clock-format = "%H:%M"; # TODO(tazjin): TZ?
+      };
+
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${config.tazjin.emacs}/bin/tazjins-emacs --internal-border=0 --border-width=0";
+      };
+    };
+  };
+
+  # Set variables to enable EXWM-XIM and other Emacs features.
+  environment.sessionVariables = {
+    XMODIFIERS = "@im=exwm-xim";
+    GTK_IM_MODULE = "xim";
+    QT_IM_MODULE = "xim";
+    CLUTTER_IM_MODULE = "xim";
+    EDITOR = "emacsclient";
+    _JAVA_AWT_WM_NONREPARENTING = "1";
+  };
+
+  # Do not restart the display manager automatically
+  systemd.services.display-manager.restartIfChanged = lib.mkForce false;
+
+  # If something needs more than 10s to stop it should probably be
+  # killed.
+  systemd.extraConfig = ''
+    DefaultTimeoutStopSec=10s
+  '';
+}
diff --git a/users/tazjin/nixos/modules/fonts.nix b/users/tazjin/nixos/modules/fonts.nix
new file mode 100644
index 0000000000..ee1b84e581
--- /dev/null
+++ b/users/tazjin/nixos/modules/fonts.nix
@@ -0,0 +1,24 @@
+# Attempt at configuring reasonable font-rendering.
+
+{ pkgs, ... }:
+
+{
+  fonts = {
+    packages = with pkgs; [
+      corefonts
+      dejavu_fonts
+      jetbrains-mono
+      noto-fonts-cjk
+      noto-fonts-emoji
+    ];
+
+    fontconfig = {
+      hinting.enable = true;
+      subpixel.lcdfilter = "light";
+
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/geesefs.nix b/users/tazjin/nixos/modules/geesefs.nix
new file mode 100644
index 0000000000..c45ee528f6
--- /dev/null
+++ b/users/tazjin/nixos/modules/geesefs.nix
@@ -0,0 +1,38 @@
+{ depot, pkgs, ... }:
+
+{
+  imports = [
+    (depot.third_party.agenix.src + "/modules/age.nix")
+  ];
+
+  age.secrets.geesefs-tazjins-files.file = depot.users.tazjin.secrets."geesefs-tazjins-files.age";
+  programs.fuse.userAllowOther = true;
+
+  systemd.services.geesefs = {
+    description = "geesefs @ tazjins-files";
+    wantedBy = [ "multi-user.target" ];
+    path = [ pkgs.fuse ];
+
+    serviceConfig = {
+      # TODO: can't get fusermount to work for non-root users (e.g. DynamicUser) here, why?
+
+      Restart = "always";
+      LoadCredential = "geesefs-tazjins-files:/run/agenix/geesefs-tazjins-files";
+      StateDirectory = "geesefs";
+      ExecStartPre = "/run/wrappers/bin/umount -a -t fuse.geesefs";
+    };
+
+    script = ''
+      set -u # bail out if systemd is misconfigured ...
+      set -x
+
+      mkdir -p $STATE_DIRECTORY/tazjins-files $STATE_DIRECTORY/cache
+
+      ${depot.third_party.geesefs}/bin/geesefs \
+        -f -o allow_other \
+        --cache $STATE_DIRECTORY/cache \
+        --shared-config $CREDENTIALS_DIRECTORY/geesefs-tazjins-files \
+        tazjins-files $STATE_DIRECTORY/tazjins-files
+    '';
+  };
+}
diff --git a/users/tazjin/nixos/modules/hidpi.nix b/users/tazjin/nixos/modules/hidpi.nix
new file mode 100644
index 0000000000..2ff61d499a
--- /dev/null
+++ b/users/tazjin/nixos/modules/hidpi.nix
@@ -0,0 +1,19 @@
+# Configuration for machines with HiDPI displays, which are a total
+# mess, of course.
+{ ... }:
+
+{
+  # Expose a variable to all programs that might be interested in the
+  # screen settings to do conditional initialisation (mostly for Emacs).
+  environment.variables.HIDPI_SCREEN = "true";
+
+  # TODO(tazjin): this option has been removed and needs to be replaced
+  # by manual configuration: https://github.com/NixOS/nixpkgs/issues/222805
+  # Ensure a larger font size in early boot stage.
+  # hardware.video.hidpi.enable = true;
+
+  # Bump DPI across the board.
+  # TODO(tazjin): This should actually be set per monitor, but I
+  # haven't yet figured out the right interface for doing that.
+  services.xserver.dpi = 161;
+}
diff --git a/users/tazjin/nixos/modules/home-config.nix b/users/tazjin/nixos/modules/home-config.nix
new file mode 100644
index 0000000000..bda8f7a440
--- /dev/null
+++ b/users/tazjin/nixos/modules/home-config.nix
@@ -0,0 +1,19 @@
+# Inject the right home-manager config for the machine.
+
+{ config, depot, pkgs, ... }:
+
+{
+  users.users.tazjin = {
+    isNormalUser = true;
+    createHome = true;
+    extraGroups = [ "wheel" "networkmanager" "video" "adbusers" ];
+    uid = 1000;
+    shell = pkgs.fish;
+    initialHashedPassword = "$2b$05$1eBPdoIgan/C/L8JFqIHBuVscQyTKw1L/4VBlzlLvLBEf6CXS3EW6";
+  };
+
+  nix.settings.trusted-users = [ "tazjin" ];
+
+  home-manager.useGlobalPkgs = true;
+  home-manager.users.tazjin = depot.users.tazjin.home."${config.networking.hostName}";
+}
diff --git a/users/tazjin/nixos/modules/laptop.nix b/users/tazjin/nixos/modules/laptop.nix
new file mode 100644
index 0000000000..e0d67dc259
--- /dev/null
+++ b/users/tazjin/nixos/modules/laptop.nix
@@ -0,0 +1,15 @@
+# Configuration specifically for laptops that move around.
+{ ... }:
+
+{
+  time.timeZone = "Europe/Moscow";
+
+  # Automatically detect location for redshift & so on ...
+  services.geoclue2.enable = true;
+  location.provider = "geoclue2";
+
+  # Enable power-saving features.
+  services.tlp.enable = true;
+
+  programs.light.enable = true;
+}
diff --git a/users/tazjin/nixos/modules/miniflux.nix b/users/tazjin/nixos/modules/miniflux.nix
new file mode 100644
index 0000000000..72089bfb3d
--- /dev/null
+++ b/users/tazjin/nixos/modules/miniflux.nix
@@ -0,0 +1,22 @@
+{ config, depot, lib, pkgs, ... }:
+
+{
+  age.secrets.miniflux.file = depot.users.tazjin.secrets."miniflux.age";
+
+  services.miniflux = {
+    enable = true;
+    adminCredentialsFile = "/run/agenix/miniflux";
+    config.LISTEN_ADDR = "127.0.0.1:6359";
+    config.BASE_URL = "https://feeds.tazj.in";
+  };
+
+  services.nginx.virtualHosts."feeds" = {
+    serverName = "feeds.tazj.in";
+    enableACME = true;
+    forceSSL = true;
+
+    locations."/" = {
+      proxyPass = "http://127.0.0.1:6359";
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/persistence.nix b/users/tazjin/nixos/modules/persistence.nix
new file mode 100644
index 0000000000..b864d13a8d
--- /dev/null
+++ b/users/tazjin/nixos/modules/persistence.nix
@@ -0,0 +1,26 @@
+# Configuration for persistent (non-home) data.
+{ config, depot, pkgs, lib, ... }:
+
+{
+  imports = [
+    (depot.third_party.sources.impermanence + "/nixos.nix")
+  ];
+
+  environment.persistence."/persist" = {
+    directories = [
+      "/etc/NetworkManager/system-connections"
+      "/etc/mullvad-vpn"
+      "/var/cache/mullvad-vpn"
+      "/var/lib/bluetooth"
+      "/var/lib/systemd/coredump"
+      "/var/lib/tailscale"
+      "/var/log"
+    ];
+
+    files = lib.optional (builtins.isNull config.networking.hostId) [
+      "/etc/machine-id"
+    ];
+  };
+
+  programs.fuse.userAllowOther = true;
+}
diff --git a/users/tazjin/nixos/modules/physical.nix b/users/tazjin/nixos/modules/physical.nix
new file mode 100644
index 0000000000..bb85c6fb98
--- /dev/null
+++ b/users/tazjin/nixos/modules/physical.nix
@@ -0,0 +1,105 @@
+# Default configuration settings for physical machines that I use.
+{ lib, pkgs, config, depot, ... }:
+
+let
+  pass-otp = pkgs.pass.withExtensions (e: [ e.pass-otp ]);
+in
+{
+  options = with lib; {
+    tazjin.emacs = mkOption {
+      type = types.package;
+      default = depot.users.tazjin.emacs;
+      description = ''
+        Derivation with my Emacs package, with configuration included.
+      '';
+    };
+  };
+
+  config = {
+    # Install all the default software.
+    environment.systemPackages =
+      # programs from the depot
+      (with depot; [
+        users.tazjin.screenLock
+        users.tazjin.chase-geese
+        config.tazjin.emacs
+        third_party.agenix.cli
+      ]) ++
+
+      # programs from nixpkgs
+      (with pkgs; [
+        (aspellWithDicts (d: [ d.ru ]))
+        amber
+        bat
+        curl
+        ddcutil
+        direnv
+        dnsutils
+        electrum
+        firefox
+        config.tazjin.emacs.emacs # emacsclient
+        expect
+        fd
+        file
+        gdb
+        git
+        gnupg
+        gtk3 # for gtk-launch
+        htop
+        hyperfine
+        iftop
+        imagemagick
+        josh
+        jq
+        lieer
+        maim
+        man-pages
+        moreutils
+        mosh
+        msmtp
+        networkmanagerapplet
+        nix-prefetch-github
+        nmap
+        notmuch
+        openssh
+        openssl
+        pass-otp
+        pavucontrol
+        pinentry
+        pinentry-emacs
+        pulseaudio # for pactl
+        pwgen
+        quasselClient
+        rink
+        ripgrep
+        rustup
+        screen
+        tig
+        tokei
+        tree
+        unzip
+        vlc
+        volumeicon
+        whois
+        xclip
+        xsecurelock
+        zoxide
+      ]);
+
+    # Run services & configure programs for all machines.
+    services.fwupd.enable = true;
+
+    # Disable the broken NetworkManager-wait-online.service
+    systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
+
+    # Disable the thing that prints annoying warnings when trying to
+    # run manually patchelfed binaries
+    environment.stub-ld.enable = false;
+
+    programs = {
+      fish.enable = true;
+      mosh.enable = true;
+      ssh.startAgent = true;
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/predlozhnik.nix b/users/tazjin/nixos/modules/predlozhnik.nix
new file mode 100644
index 0000000000..db20963df1
--- /dev/null
+++ b/users/tazjin/nixos/modules/predlozhnik.nix
@@ -0,0 +1,10 @@
+# Host predlozhnik.ru, serving //users/tazjin/predlozhnik
+{ depot, ... }:
+
+{
+  services.nginx.virtualHosts."predlozhnik.ru" = {
+    root = depot.corp.russian.predlozhnik;
+    enableACME = true;
+    forceSSL = true;
+  };
+}
diff --git a/users/tazjin/nixos/modules/tgsa.nix b/users/tazjin/nixos/modules/tgsa.nix
new file mode 100644
index 0000000000..e162e0d822
--- /dev/null
+++ b/users/tazjin/nixos/modules/tgsa.nix
@@ -0,0 +1,29 @@
+{ config, depot, lib, pkgs, ... }:
+
+{
+  systemd.services.tgsa = {
+    description = "telegram -> SA bbcode thing";
+    wantedBy = [ "multi-user.target" ];
+
+    serviceConfig = {
+      DynamicUser = true;
+      Restart = "always";
+      LoadCredential = "tgsa-yandex.json:/run/agenix/tgsa-yandex";
+    };
+
+    script = ''
+      export YANDEX_KEY_FILE="''${CREDENTIALS_DIRECTORY}/tgsa-yandex.json"
+      ${depot.users.tazjin.tgsa}/bin/tgsa
+    '';
+  };
+
+  services.nginx.virtualHosts."tgsa" = {
+    serverName = "tgsa.tazj.in";
+    enableACME = true;
+    forceSSL = true;
+
+    locations."/" = {
+      proxyPass = "http://127.0.0.1:8472";
+    };
+  };
+}
diff --git a/users/tazjin/nixos/tverskoy/default.nix b/users/tazjin/nixos/tverskoy/default.nix
index ab44c3b98e..733929219a 100644
--- a/users/tazjin/nixos/tverskoy/default.nix
+++ b/users/tazjin/nixos/tverskoy/default.nix
@@ -1,25 +1,30 @@
+# tverskoy is my Thinkpad X13 AMD 1st gen
 { depot, lib, pkgs, ... }:
 
-config: let
+config:
+let
   quasselClient = pkgs.quassel.override {
     client = true;
     enableDaemon = false;
     monolithic = false;
   };
 
-  # Use a screen lock command that resets the keyboard layout
-  # before locking, to avoid locking me out when the layout is
-  # in Russian.
-  screenLock = pkgs.writeShellScriptBin "tazjin-screen-lock" ''
-    ${pkgs.xorg.setxkbmap}/bin/setxkbmap us
-    ${pkgs.xorg.setxkbmap}/bin/setxkbmap -option caps:super
-    exec ${pkgs.xsecurelock}/bin/xsecurelock
-  '';
-in lib.fix(self: {
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+  usermod = name: depot.path.origSrc + ("/users/tazjin/nixos/modules/" + name);
+in
+lib.fix (self: {
   imports = [
-    "${depot.third_party.impermanence}/nixos.nix"
-    "${pkgs.home-manager.src}/nixos"
-  ];
+    (mod "open_eid.nix")
+    (usermod "chromium.nix")
+    (usermod "desktop.nix")
+    (usermod "fonts.nix")
+    (usermod "home-config.nix")
+    (usermod "laptop.nix")
+    (usermod "persistence.nix")
+    (usermod "physical.nix")
+
+    (pkgs.home-manager.src + "/nixos")
+  ] ++ lib.optional (builtins.pathExists ./local-config.nix) ./local-config.nix;
 
   tvl.cache.enable = true;
 
@@ -36,17 +41,18 @@ in lib.fix(self: {
     extraModulePackages = [ kernelPackages.acpi_call ];
 
     kernelModules = [ "kvm-amd" "i2c_dev" ];
-    kernelPackages = pkgs.linuxPackages_latest;
+    kernelPackages = pkgs.zfsUnstable.latestCompatibleLinuxPackages;
     loader.systemd-boot.enable = true;
     loader.efi.canTouchEfiVariables = true;
-    zfs.enableUnstable = true;
   };
 
+  virtualisation.docker.enable = true;
+  users.users.tazjin.extraGroups = [ "docker" "vboxusers" "adbusers" ];
+
   fileSystems = {
-    "/" =  {
-      device = "tmpfs";
-      fsType = "tmpfs";
-      options = [ "defaults" "size=8G" "mode=755" ];
+    "/" = {
+      device = "zpool/ephemeral/root";
+      fsType = "zfs";
     };
 
     "/home" = {
@@ -89,6 +95,8 @@ in lib.fix(self: {
 
     opengl = {
       enable = true;
+      driSupport32Bit = true;
+
       extraPackages = with pkgs; [
         vaapiVdpau
         libvdpau-va-gl
@@ -110,52 +118,10 @@ in lib.fix(self: {
     ];
   };
 
-  fonts = {
-    fonts = with pkgs; [
-      corefonts
-      dejavu_fonts
-      jetbrains-mono
-      noto-fonts-cjk
-      noto-fonts-emoji
-    ];
-
-    fontconfig = {
-      hinting.enable = true;
-      subpixel.lcdfilter = "light";
-
-      defaultFonts = {
-        monospace = [ "JetBrains Mono" ];
-      };
-    };
-  };
-
-  environment.persistence."/persist" = {
-    directories = [
-      "/etc/NetworkManager/system-connections"
-      "/etc/mullvad-vpn"
-      "/var/cache/mullvad-vpn"
-      "/var/lib/bluetooth"
-      "/var/lib/systemd/coredump"
-      "/var/log"
-    ];
-    files = [
-      "/etc/machine-id"
-    ];
-  };
-
   security.rtkit.enable = true;
 
   services = {
-    pipewire = {
-      enable = true;
-      alsa.enable = true;
-      pulse.enable = true;
-    };
-
-    redshift.enable = true;
-    blueman.enable = true;
-    mullvad-vpn.enable = true;
-    fwupd.enable = true;
+    tailscale.enable = true;
     printing.enable = true;
 
     # expose i2c device as /dev/i2c-amdgpu-dm and make it user-accessible
@@ -164,34 +130,7 @@ in lib.fix(self: {
       SUBSYSTEM=="i2c-dev", ACTION=="add", DEVPATH=="/devices/pci0000:00/0000:00:08.1/0000:06:00.0/i2c-5/i2c-dev/i2c-5", SYMLINK+="i2c-amdgpu-dm", TAG+="uaccess"
     '';
 
-    # Configure TLP to keep battery charge between 40-70% while
-    # plugged in to the wall (thanks etu for the recommendation).
-    tlp = {
-      enable = true;
-      settings.START_CHARGE_THRESH_BAT0 = 40;
-      settings.STOP_CHARGE_THRESH_BAT0 = 70;
-    };
-
-    xserver = {
-      enable = true;
-      layout = "us";
-      xkbOptions = "caps:super";
-      videoDrivers = [ "amdgpu" ];
-
-      libinput.enable = true;
-
-      displayManager = {
-        # Give EXWM permission to control the session.
-        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER";
-        lightdm.enable = true;
-        # lightdm.greeters.gtk.clock-format = "%H:%M"; # TODO(tazjin): TZ?
-      };
-
-      windowManager.session = lib.singleton {
-        name = "exwm";
-        start = "${depot.users.tazjin.emacs}/bin/tazjins-emacs";
-      };
-    };
+    xserver.videoDrivers = [ "amdgpu" ];
 
     # Automatically collect garbage from the Nix store.
     depot.automatic-gc = {
@@ -201,231 +140,36 @@ in lib.fix(self: {
       maxFreed = 10; # GiB
       preserveGenerations = "14d";
     };
-
-    # Run keyring daemon, solely required by Fractal.
-    gnome.gnome-keyring.enable = true;
-  };
-
-  # Set variables to enable EXWM-XIM
-  environment.sessionVariables = {
-    XMODIFIERS = "@im=exwm-xim";
-    GTK_IM_MODULE = "xim";
-    QT_IM_MODULE = "xim";
-    CLUTTER_IM_MODULE = "xim";
   };
 
-  # Automatically detect location to use for redshift
-  location.provider = "geoclue2";
-
-  # Do not restart the display manager automatically
-  systemd.services.display-manager.restartIfChanged = lib.mkForce false;
+  systemd.user.services.lieer-tazjin = {
+    description = "Synchronise mail@tazj.in via lieer";
+    script = "${pkgs.lieer}/bin/gmi sync";
 
-  # If something needs more than 10s to stop it should probably be
-  # killed.
-  systemd.extraConfig = ''
-    DefaultTimeoutStopSec=10s
-  '';
-
-  time.timeZone = "Europe/Moscow";
-
-  users.users.tazjin = {
-    isNormalUser = true;
-    createHome = true;
-    extraGroups = [ "wheel" "networkmanager" "video" "adbusers" ];
-    uid = 1000;
-    shell = pkgs.fish;
-    initialHashedPassword = "$6$d3FywUNCuZnJ4l.$ZW2ul59MLYon1v1xhC3lTJZfZ91lWW6Tpi13MpME0cJcYZNrsx7ABdgQRn.K05awruG2Y9ARAzURnmiJ31WTS1";
+    serviceConfig = {
+      WorkingDirectory = "%h/mail/account.tazjin";
+      Type = "oneshot";
+    };
   };
 
-  programs = {
-    adb.enable = true;
-    fish.enable = true;
-    light.enable = true;
-    mosh.enable = true;
-    ssh.startAgent = true;
-    steam.enable = true;
-
-    # Required by impermanence
-    fuse.userAllowOther = true;
-  };
+  systemd.user.timers.lieer-tazjin = {
+    wantedBy = [ "timers.target" ];
 
-  environment.systemPackages =
-    # programs from the depot
-    (with depot; [
-      screenLock
-      tools.nsfv-setup
-      users.tazjin.emacs
-      third_party.agenix.cli
-      third_party.dfmt
-    ]) ++
-
-    # programs from nixpkgs
-    (with pkgs; [
-      amber
-      audacity
-      bat
-      chromium
-      curl
-      ddcutil
-      direnv
-      dmd
-      dnsutils
-      emacs27-nox # emacsclient
-      exa
-      fd
-      file
-      firefox
-      fractal
-      gdb
-      gh
-      git
-      gnupg
-      google-chrome
-      gtk3 # for gtk-launch
-      htop
-      hyperfine
-      iftop
-      imagemagick
-      jq
-      lieer
-      manpages
-      mosh
-      msmtp
-      mullvad-vpn
-      networkmanagerapplet
-      nix-prefetch-github
-      nmap
-      notmuch
-      openssh
-      openssl
-      paperlike-go
-      pass
-      pavucontrol
-      pinentry
-      pinentry-emacs
-      pulseaudioLight # for pactl
-      pwgen
-      quasselClient
-      rink
-      ripgrep
-      rustup
-      screen
-      scrot
-      spotify
-      syncthing
-      tig
-      tokei
-      tree
-      unzip
-      vlc
-      whois
-      xsecurelock
-      zoxide
-    ]);
-
-    systemd.user.services.lieer-tazjin = {
-      description = "Synchronise mail@tazj.in via lieer";
-      script = "${pkgs.lieer}/bin/gmi sync";
-
-      serviceConfig = {
-        WorkingDirectory = "%h/mail/account.tazjin";
-        Type = "oneshot";
-      };
+    timerConfig = {
+      OnActiveSec = "1";
+      OnUnitActiveSec = "180";
     };
+  };
 
-    systemd.user.timers.lieer-tazjin = {
-      wantedBy = [ "timers.target" ];
+  # android stuff for hacking on Awful.apk
+  programs.adb.enable = true;
 
-      timerConfig = {
-        OnActiveSec = "1";
-        OnUnitActiveSec = "180";
-      };
-    };
+  # systemd-oomd seems to have been enabled by default around ~
+  # December 2022, and it's really into killing my X session as soon
+  # as I do anything stressful to the machine
+  systemd.services.systemd-oomd.enable = lib.mkForce false;
 
-    home-manager.useGlobalPkgs = true;
-    home-manager.users.tazjin = { config, lib, ... }: {
-      imports = [ "${depot.third_party.impermanence}/home-manager.nix" ];
-
-      home.persistence."/persist/tazjin/home" = {
-        allowOther = true;
-
-        directories = [
-          ".cargo"
-          ".config/audacity"
-          ".config/google-chrome"
-          ".config/quassel-irc.org"
-          ".config/spotify"
-          ".config/syncthing"
-          ".elfeed"
-          ".gnupg"
-          ".local/share/Steam"
-          ".local/share/audacity"
-          ".local/share/direnv"
-          ".local/share/fish"
-          ".local/share/keyrings"
-          ".local/share/zoxide"
-          ".mozilla/firefox"
-          ".password-store"
-          ".rustup"
-          ".ssh"
-          ".steam"
-          ".telega"
-          "go"
-          "mail"
-        ];
-
-        files = [
-          ".notmuch-config"
-        ];
-      };
-
-      home.activation.screenshots = lib.hm.dag.entryAnywhere ''
-        $DRY_RUN_CMD mkdir -p $HOME/screenshots
-      '';
-
-      programs.git = {
-        enable = true;
-        userName = "Vincent Ambo";
-        userEmail = "mail@tazj.in";
-        extraConfig = {
-          pull.rebase = true;
-          init.defaultBranch = "canon";
-        };
-      };
-
-      programs.fish = {
-        enable = true;
-        interactiveShellInit = ''
-          ${pkgs.zoxide}/bin/zoxide init fish | source
-        '';
-      };
-
-      services.screen-locker = {
-        enable = true;
-        enableDetectSleep = true;
-        inactiveInterval = 10; # minutes
-        lockCmd = "${screenLock}/bin/tazjin-screen-lock";
-      };
-
-      services.picom = {
-        enable = true;
-        vSync = true;
-        backend = "glx";
-      };
-
-      # Enable the dunst notification daemon, but force the
-      # configuration file separately instead of going via the strange
-      # Nix->dunstrc encoding route.
-      services.dunst.enable = true;
-      xdg.configFile."dunst/dunstrc" = {
-        source = depot.users.tazjin.dotfiles.dunstrc;
-        onChange = ''
-          ${pkgs.procps}/bin/pkill -u "$USER" ''${VERBOSE+-e} dunst || true
-        '';
-      };
-
-      systemd.user.startServices = true;
-    };
+  environment.systemPackages = [ pkgs.vulkan-tools ];
 
-    system.stateVersion = "20.09";
+  system.stateVersion = "20.09";
 })
diff --git a/users/tazjin/nixos/zamalek/default.nix b/users/tazjin/nixos/zamalek/default.nix
new file mode 100644
index 0000000000..a340e8a3e8
--- /dev/null
+++ b/users/tazjin/nixos/zamalek/default.nix
@@ -0,0 +1,88 @@
+# zamalek is my Huawei MateBook X (unknown year)
+{ depot, lib, pkgs, ... }:
+
+config:
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+  usermod = name: depot.path.origSrc + ("/users/tazjin/nixos/modules/" + name);
+
+  zdevice = device: {
+    inherit device;
+    fsType = "zfs";
+  };
+in
+{
+  imports = [
+    (usermod "chromium.nix")
+    (usermod "desktop.nix")
+    (usermod "fonts.nix")
+    (usermod "hidpi.nix")
+    (usermod "home-config.nix")
+    (usermod "laptop.nix")
+    (usermod "persistence.nix")
+    (usermod "physical.nix")
+
+    (pkgs.home-manager.src + "/nixos")
+  ] ++ lib.optional (builtins.pathExists ./local-config.nix) ./local-config.nix;
+
+  tvl.cache.enable = true;
+
+  boot = {
+    initrd.availableKernelModules = [ "nvme" "xhci_pci" ];
+    loader.systemd-boot.enable = true;
+    loader.efi.canTouchEfiVariables = true;
+    supportedFilesystems = [ "zfs" ];
+    zfs.devNodes = "/dev/";
+
+    extraModprobeConfig = ''
+      options snd_hda_intel power_save=1
+      options iwlwifi power_save=1
+      options iwldvm force_cam=0
+      options i915 enable_guc=3 enable_fbc=1
+    '';
+  };
+
+  fileSystems = {
+    "/" = zdevice "zpool/ephemeral/root";
+    "/home" = zdevice "zpool/ephemeral/home";
+    "/persist" = zdevice "zpool/persistent/data" // { neededForBoot = true; };
+    "/nix" = zdevice "zpool/persistent/nix";
+    "/depot" = zdevice "zpool/persistent/depot";
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/2487-3908";
+      fsType = "vfat";
+    };
+  };
+
+  networking = {
+    hostName = "zamalek";
+    domain = "tvl.su";
+    hostId = "ee399356";
+    networkmanager.enable = true;
+
+    extraHosts = ''
+      10.101.240.1 wifi.silja.fi
+    '';
+
+    nameservers = [
+      "8.8.8.8"
+      "8.8.4.4"
+    ];
+  };
+
+  hardware = {
+    cpu.intel.updateMicrocode = true;
+    bluetooth.enable = true;
+    enableRedistributableFirmware = true;
+    opengl.enable = true;
+  };
+
+  services.xserver.libinput.touchpad.clickMethod = "clickfinger";
+  services.xserver.libinput.touchpad.tapping = false;
+  services.avahi.enable = true;
+  services.tailscale.enable = true;
+  powerManagement.powertop.enable = true;
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/tazjin/presentations/bootstrapping-2018/default.nix b/users/tazjin/presentations/bootstrapping-2018/default.nix
index 0dff14b2a1..2775d0b3fb 100644
--- a/users/tazjin/presentations/bootstrapping-2018/default.nix
+++ b/users/tazjin/presentations/bootstrapping-2018/default.nix
@@ -4,24 +4,26 @@
 
 with pkgs;
 
-let tex = texlive.combine {
-  inherit (texlive)
-    beamer
-    beamertheme-metropolis
-    etoolbox
-    euenc
-    extsizes
-    fontspec
-    lualibs
-    luaotfload
-    luatex
-    minted
-    ms
-    pgfopts
-    scheme-basic
-    translator;
-};
-in stdenv.mkDerivation {
+let
+  tex = texlive.combine {
+    inherit (texlive)
+      beamer
+      beamertheme-metropolis
+      etoolbox
+      euenc
+      extsizes
+      fontspec
+      lualibs
+      luaotfload
+      luatex
+      minted
+      ms
+      pgfopts
+      scheme-basic
+      translator;
+  };
+in
+stdenv.mkDerivation {
   name = "nuug-bootstrapping-slides";
   src = ./.;
 
diff --git a/users/tazjin/presentations/tvix-eval-2023/README.md b/users/tazjin/presentations/tvix-eval-2023/README.md
new file mode 100644
index 0000000000..b14ba8ff50
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/README.md
@@ -0,0 +1,12 @@
+These are the slides for a talk at the Moscow Rust User Group /
+ProgMSK on 2023-09-07.
+
+After building, the presentation can be launched with `pdfpc`
+(available in `nixpkgs`), like this:
+
+```
+pdfpc --windowed=both result/presentation.pdf -R presentation.pdfpc -d 40
+```
+
+I keep the JSON file formatted using `jq . presentation.pdfpc | sponge
+presentation.pdfpc` for easier diffs.
diff --git a/users/tazjin/presentations/tvix-eval-2023/cppnix-example-lexer.cpp b/users/tazjin/presentations/tvix-eval-2023/cppnix-example-lexer.cpp
new file mode 100644
index 0000000000..7c52bce8b6
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/cppnix-example-lexer.cpp
@@ -0,0 +1,13 @@
+attrpath
+  : attrpath '.' attr {
+    $$ = $1; $1->push_back(AttrName(data->symbols.create($3)));
+  }
+  | attrpath '.' string_attr
+    { $$ = $1;
+      ExprString * str = dynamic_cast<ExprString *>($3);
+      if (str) {
+          $$->push_back(AttrName(data->symbols.create(str->s)));
+          delete str;
+      } else
+          $$->push_back(AttrName($3));
+    }
diff --git a/users/tazjin/presentations/tvix-eval-2023/cppnix-example-smuggling.cpp b/users/tazjin/presentations/tvix-eval-2023/cppnix-example-smuggling.cpp
new file mode 100644
index 0000000000..37b9219b2e
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/cppnix-example-smuggling.cpp
@@ -0,0 +1,12 @@
+struct Env {
+  // ... some struct fields ...
+  Value* values[0];
+};
+
+// ....
+
+if (env->type == Env::HasWithExpr) {
+  // ...
+  evalAttrs(*env->up, (Expr *) env->values[0], *v, noPos, "<borked>");
+  //                  ^^^^^^^^^^^^^^^^^^^^^^^
+}
diff --git a/users/tazjin/presentations/tvix-eval-2023/default.nix b/users/tazjin/presentations/tvix-eval-2023/default.nix
new file mode 100644
index 0000000000..a4d855197c
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/default.nix
@@ -0,0 +1,63 @@
+{ depot, pkgs, ... }:
+
+let
+  inherit (pkgs) fontconfig texlive stdenv imagemagick runCommand qrencode;
+
+  tex = texlive.combine {
+    inherit (texlive)
+      babel
+      babel-russian
+      beamer
+      beamertheme-metropolis
+      etoolbox
+      euenc
+      extsizes
+      fontspec
+      listings
+      xetex
+      minted
+      ms
+      pgfopts
+      scheme-basic
+      translator;
+  };
+
+  linksQrCode = runCommand "qrcode.png" { } ''
+    ${qrencode}/bin/qrencode -o code.png -s 8 \
+      --background=fafafa \
+      --foreground=000000 \
+      'https://tazj.in/blog/tvix-eval-talk-2023'
+
+    # latex has trouble with the PDF produced by qrencode
+    ${imagemagick}/bin/convert code.png $out
+  '';
+in
+stdenv.mkDerivation {
+  name = "progmsk-tvix-eval";
+  src = ./.;
+
+  nativeBuildInputs = [ tex imagemagick fontconfig ];
+
+  FONTCONFIG_FILE = pkgs.makeFontsConf {
+    fontDirectories = with pkgs; [ jetbrains-mono fira fira-code fira-mono ];
+  };
+
+  buildPhase = ''
+    # LaTeX needs a cache folder in /home/ ...
+    mkdir home
+    export HOME=$PWD/home
+
+    cp ${depot.tvix.logo}/logo.png tvix-logo.png
+    cp ${linksQrCode} qrcode.png
+
+    # As usual, TeX needs to be run twice ...
+    ${tex}/bin/xelatex presentation.tex
+    ${tex}/bin/xelatex presentation.tex
+  '';
+
+  installPhase = ''
+    mkdir -p $out
+    cp presentation.pdf $out/
+    cp $src/presentation.pdfpc $out/
+  '';
+}
diff --git a/users/tazjin/presentations/tvix-eval-2023/presentation.pdfpc b/users/tazjin/presentations/tvix-eval-2023/presentation.pdfpc
new file mode 100644
index 0000000000..ab5cba68bf
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/presentation.pdfpc
@@ -0,0 +1,98 @@
+{
+  "pdfpcFormat": 2,
+  "duration": 40,
+  "disableMarkdown": false,
+  "noteFontSize": 20,
+  "pages": [
+    {
+      "idx": 1,
+      "label": "2",
+      "overlay": 0,
+      "note": "ΠŸΡ€ΠΈΠ²Π΅Ρ‚, мСня Π·ΠΎΠ²ΡƒΡ‚ .... Π― ΡƒΠΆΠ΅ ΠΌΠ½ΠΎΠ³ΠΎ Π»Π΅Ρ‚, с 2016Π³. ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ, ΠΏΠΈΡˆΡƒ Π½Π° РастС, ΠΈ хотя Π½Π° Ρ€Π°Π±ΠΎΡ‚Π΅ Ρƒ мСня часто Π±Ρ‹Π²Π°ΡŽΡ‚ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ языки, Раст - Π»ΡŽΠ±ΠΈΠΌΡ‹ΠΉ ΠΌΠΎΠΉ язык.\n\nΠŸΠ°Ρ€Ρƒ Π»Π΅Ρ‚ Π½Π°Π·Π°Π΄, Π²ΠΎ врСмя Кодида, я создал ΠΎΠ½Π»Π°ΠΉΠ½-ΠΊΠΎΠΌΠΌΡŽΠ½ΠΈΡ‚ΠΈ Π’Π’Π›, ΠΈ сСгодня Ρ…ΠΎΡ‡Ρƒ Π²Π°ΠΌ ΠΎΠ± ΠΎΠ΄Π½ΠΎΠΌ ΠΈΠ· Π½Π°ΡˆΠΈΡ… ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ² Ρ€Π°ΡΡΠΊΠ°Π·Π°Ρ‚ΡŒ."
+    },
+    {
+      "idx": 2,
+      "label": "3",
+      "overlay": 0,
+      "note": "ΠΌΠΎΠ½ΠΎΡ€Π΅ΠΏΠΎ: ΠΎΠ±ΡŠΡΡΠ½ΠΈΡ‚ΡŒ. Π’Π΅ΡΡŒ ΠΊΠΎΠ΄ ΠΎΡ€Π³Π° Π² ΠΎΠ΄Π½ΠΎΠΌ мСстС. Π•Π΄ΠΈΠ½Π½Ρ‹ΠΉ Ρ‚ΡƒΠ»ΠΈΠ½Π³. Много ΠΈΠ· нас Ρ€Π°Π½ΡŒΡˆΠ΅ Ρ€Π°Π±ΠΎΡ‚Π°Π»ΠΈ Π² компаниях, Π³Π΄Π΅ Ρ‚Π°ΠΊ Π΄Π΅Π»Π°ΡŽΡ‚ (Π½ΠΏ Π“ΡƒΠ³Π»).\n\nΠœΡ‹ Ρ…ΠΎΡ‚Π΅Π»ΠΈ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Ρ‚Π°ΠΊΠΎΠΉ ΠΆΠ΅ Ρ‚ΡƒΠ»ΠΈΠ½Π³, Π½ΠΎ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΎ. Но Ρƒ нас мСньшС рСсурсов Ρ‡Π΅ΠΌ Ρƒ Π“ΡƒΠ³Π»Π°, Π½Π°ΠΌΠ½ΠΎΠ³ΠΎ мСньшС. ΠŸΡ€ΠΈΡˆΠ»ΠΎΡΡŒ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ эфф. способ.\nЀокусируСм Π½Π° Никс, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ (...). Π•ΡΡ‚ΡŒ Π΄ΠΎΠΊΠ»Π°Π΄.\n\nΠœΡ‹ Π½Π°Ρ‡Π°Π»ΠΈ Π΅Π³ΠΎ Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ (ΠΊΠΎΠ½Ρ„ΠΈΠ³ ΠΈ Ρ‚Π΄.), Ρ…ΠΎΡ‚Π΅Π»ΠΎΡΡŒ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Π²Π΅Π·Π΄Π΅."
+    },
+    {
+      "idx": 3,
+      "label": "4",
+      "overlay": 0,
+      "note": "Π‘ΡƒΠ΄Π΅ΠΌ Ρ„ΠΎΠΊΡƒΡΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π½Π° язык сСйчас. ΠžΡΡ‚Π°Π»ΡŒΠ½Ρ‹Π΅ части интСрСсныС, Π½ΠΎ Π½Π΅ сСгодня.\nΠ―Π·Ρ‹ΠΊ Π»Π΅Π½ΠΈΠ²Ρ‹ΠΉ, Π·Π½Π°Ρ‡ΠΈΡ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ вычисляСм ΠΊΠΎΠ΄, ΠΊΠΎΠ³Π΄Π° Π΅Π³ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π½ΡƒΠΆΠ΅Π½ Π³Π΄Π΅-Ρ‚ΠΎ.\nΠ—Π½Π°Ρ‡ΠΈΡ‚, Π½Π°ΠΌ Π½ΡƒΠΆΠ΅Π½ Ρ€Π°Π½Ρ‚Π°ΠΉΠΌ-прСдставлСниС ΠΎΡ‚Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… вычислСний.\nΠžΡ€Π³Π°Π½ΠΈΡ‡Π½ΠΎ развивался: Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ Ρ„ΠΈΡ‡ΠΈ, ΠΊΠΎΠ³Π΄Π° Π½ΡƒΠΆΠ΄Π°Π»ΠΈΡΡŒ. Много Ρ„ΡƒΠ½ΠΊΡ‚Ρ†ΠΈΠΈ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ случайно, ΠΊΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΠΈ Ρ„ΠΈΡ‡ - часто странно. (ΡˆΡƒΡ‚ΠΊΠ° ΠΏΡ€ΠΎ Π‘++?)\nНо Π΅ΡΡ‚ΡŒ Ρ…ΠΎΡ€ΠΎΡˆΠΈΠΉ Ρ„Π°ΠΊΡ‚ΠΎΡ€: вСсь ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ Π² ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠ΅ Π² ΠΎΠ΄Π½ΠΎΠΌ Ρ€Π΅ΠΏΠΎ. ΠžΠ±ΡŠΡΡΠ½ΡΡ‚ΡŒ nixpkgs."
+    },
+    {
+      "idx": 4,
+      "label": "5",
+      "overlay": 0,
+      "note": "Π’Π΅ΠΊΡƒΡˆΠ°Ρ имплСмСнтация Π½Π° Π‘++. Π’ΠΎΡ‚ ΠΏΡ€ΠΈΠΌΠ΅Ρ€. ΠšΡ‚ΠΎ-Ρ‚ΠΎ здСсь ΠΏΠΎΠ½ΠΈΠΌΠ°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ Π²ΠΈΠ΄Π΅ΠΌ? Π­Ρ‚ΠΎ Ρ‡Π°ΡΡ‚ΡŒ парсСра Π² як, Π½ΠΎ Ρ‚ΡƒΡ‚ ΡΠΎΠ·Π΄Π°ΡŽΡ‚ Ρ€Π°Π½Ρ‚Π°ΠΉΠΌ-значСния Π²ΠΎ врСмя парсинга. ΠžΡ‡Π΅Π½ΡŒ слоТно ΠΏΠΎΠ½ΠΈΠΌΠ°Ρ‚ΡŒ, Ρ‡ΠΈΡ‚Π°Ρ‚ΡŒ, Π΄Π΅Π±Π°ΠΆΠΈΡ‚ΡŒ ΠΈ Ρ‚Π°ΠΊ Π΄Π°Π»Π΅Π΅.\nΠ§ΠΈΡ‚Π°Π»ΠΈ парсСр, ΠΈ Π΄Π°ΠΆΠ΅ нашли Ρ‚Π°ΠΌ нСизвСстныС Ρ„ΠΈΡ‡ΠΈ языка."
+    },
+    {
+      "idx": 5,
+      "label": "6",
+      "overlay": 0,
+      "note": "Π’Ρ‚ΠΎΡ€ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€. Π•ΡΡ‚ΡŒ стракт Env, которая ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π²ΠΎ ΠΌΠ½ΠΎΠ³ΠΈΡ… мСстах Π² ΠΊΠΎΠ΄Π΅. Π’Π°ΠΌ массив Ρ‚ΠΈΠΏΠ° Value.\nΠ’ΠΎΡ‚ использованиС этого массива. Π§Ρ‚ΠΎ ΠΌΡ‹ Π²ΠΈΠ΄Π΅ΠΌ? ΠšΡ‚ΠΎ ΠΏΠΎΠ½ΠΈΠΌΠ°Π΅Ρ‚?\nΠ”Π°, Ρ‚Π°ΠΌ Π½Π° самом Π΄Π΅Π»Π΅ происходит каст Π½Π° Π΄Ρ€ΡƒΠ³ΠΎΠΉ Ρ‚ΠΈΠΏ. Π—Π½Π°Ρ‡ΠΈΡ‚, Π² структурС Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ Π΄Π°Π½Π½Ρ‹Π΅, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½Π΅ ΠΏΠΎΠ΄ΠΎΠΉΠ΄Π΅Ρ‚. ΠžΡ‡Π΅Π½ΡŒ unsafe!\n\nΠ”Π°, Ρ‡Ρ‚ΠΎ ΠΆΠ΅ Π΄Π΅Π»Π°Ρ‚ΡŒ? ΠŸΡ‹Ρ‚Π°Π»ΠΈΡΡŒ ΠΏΠΎΡ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ ΠΊΠΎΠ΄, Π½ΠΎ ΡΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ burnout ΠΎΡ‡Π΅Π½ΡŒ быстро. МСняСшь ΠΎΠ΄Π½Ρƒ ΠΌΠ°Π»Π΅Π½ΡŒΠΊΡƒΡŽ ΡˆΡ‚ΡƒΠΊΡƒ -> segfaults.\nΠŸΠΎΡ‡Π΅ΠΌΡƒ ΠΊΠΎΠ΄ Π²ΠΎΡ‚ Ρ‚Π°ΠΊΠΎΠΉ? -> ΠΎΠ±ΡŠΡΡΠ½ΡΡ‚ΡŒ.\nΠŸΡ€ΠΈΡˆΠ»Π°ΡΡŒ очСвидная идСя."
+    },
+    {
+      "idx": 6,
+      "label": "6",
+      "overlay": 1,
+      "note": "ΠŸΠ΅Ρ€Π΅ΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρ‹ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ, ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ ΠΎΡ‡Π΅Π½ΡŒ слоТно. Но ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΌΠ΅Π½ΡΡ‚ΡŒ Π°Ρ€Ρ…., ΠΈ ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ пСрСписиваниС Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… частСй.\nРаст - Π½Π°ΠΌ ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½Ρ‹ΠΉ Π²Ρ‹Π±ΠΎΡ€ для имплСмСнтация языка. Много нас Π·Π½Π°ΡŽΡ‚ Раст, ΠΈ Π² Ρ†Π΅Π»ΠΎΠΌ, ΠΏΠΎΡ‡Π΅ΠΌΡƒ ΠΈΠΌΠ΅Π½Π½ΠΎ Раст, Π²Ρ‹ ΡƒΠΆΠ΅ сами ΠΏΠΎΠ½ΠΈΠΌΠ°Π΅Ρ‚Π΅.\n\nΠœΡ‹ ΠΎΡ‚ NLNet, организация, ..., ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΈ Π΄Π΅Π½Π³ΠΈ Π·Π° этого ΠΈ Π½Π°Ρ‡Π°Π»ΠΈ с языком. Π­Ρ‚ΠΎΡ‚ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ Π½Π°Π·Ρ‹Π²Π°Π΅ΠΌ tvix-eval.\n\nΠ•ΡΡ‚ΡŒ Π΅Ρ‰Π΅ ΠΎΠ΄Π½Π° ваТная ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π° для Π²Ρ‹Π±ΠΎΡ€Π° Раста."
+    },
+    {
+      "idx": 7,
+      "label": "6",
+      "overlay": 2,
+      "note": "ΠŸΠ°Ρ€Ρƒ Π»Π΅Ρ‚ Π½Π°Π·Π°Π΄, швСдский ΠΏΠ°Ρ€Π΅Π½ΡŒ ΡŽΠ·Π΅Ρ€Π½Π΅ΠΉΠΌΠΎΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ нСльзя ΠΏΡ€ΠΎΠΈΡΠ½ΠΎΡΠΈΡ‚ΡŒ, написал Π½Π° РастС ΠΎΡ‡Π΅Π½ΡŒ быстрый ΠΈ Π² Ρ†Π΅Π»ΠΎΠΌ Ρ…ΠΎΡ€ΠΎΡˆΠΈΠΉ парсСр для Никса.\nΠ­Ρ‚ΠΎΡ‚ парсСр ΡƒΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² Ρ€Π°Π·Π½Ρ‹Ρ… с Никсом связанных ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°Ρ…. Он скорСС всСго Π² ΠΏΡƒΡ‚ΠΈ ΡΡ‚Π°Ρ‚ΡŒ Π΄Π΅Ρ„ΠΎΠ»ΡŒΡ‚Π½ΠΈΠΌ парсСром Никса.\nΠšΠΎΠ½Π΅Ρ‡Π½ΠΎ, Π½Π΅ΠΏΠ»ΠΎΡ…ΠΎ Ссли ΠΌΡ‹ Π΅Π³ΠΎ Ρ‚ΠΎΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ.\nК соТалСнию, Π°Π²Ρ‚ΠΎΡ€ рникса ΡƒΠΌΠ΅Ρ€Π» Π² 2021 Π³ΠΎΠ΄Ρƒ. Мали исвСстно ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ ΡΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ. ΠœΡ‹ Π΅ΠΌΡƒ ΠΎΡ‡Π΅Π½ΡŒ Π±Π»Π°Π³ΠΎΠ΄Π°Ρ€Π½Ρ‹Π΅, ΠΈ я просто Ρ…ΠΎΡ‚Π΅Π» Π΅Π³ΠΎ здСсь ΡƒΠΏΠΎΠΌΡΠ½ΡƒΡ‚ΡŒ."
+    },
+    {
+      "idx": 9,
+      "label": "8",
+      "overlay": 0,
+      "note": "ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ opcode.rs, compiler/mod (compile_binop)\n\nΡ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΠ½ Π½Π΅ Ρ€Π°Π·ΠΆΠΈΡ€Π΅Π» (ΠΏΡ€ΠΎ variant_size_differences)\n"
+    },
+    {
+      "idx": 10,
+      "label": "9",
+      "overlay": 0,
+      "note": "ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ value/mod.rs, ΠΏΠΎΡ‚ΠΎΠΌ value/list.rs\n\nкороткая объяснСниС ситуации с Gc<...> vs. Rc<...>"
+    },
+    {
+      "idx": 11,
+      "label": "10",
+      "overlay": 0,
+      "note": "ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ vm/mod.rs\n\nΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ Π²Ρ‹ΠΏΠΎΠ»ΡŒΠ½ΡΠ΅Ρ‚ инструктции Π² execute_bytecode\n\nсначала Π½Π° Π°Π»Ρ„Π°Π²ΠΈΡ‚Π½Ρ‹ΠΌ порядкС, ΠΏΠΎΡ‚ΠΎΠΌ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΏΡ€ΠΎΡ„Π°ΠΉΠ»Π΅Ρ€Π° мСняли это"
+    },
+    {
+      "idx": 12,
+      "label": "10",
+      "overlay": 1,
+      "note": "ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ Π΄ΠΈΠ°Π³Ρ€Π°ΠΌΠΌΡƒ\n\nΠ³Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΈΠΎΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ\n\nTCO - хвостовый Π²Ρ‹Π·ΠΎΠ²\n\nasync - ΠΎΡ‡Π΅Π½ΡŒ наязчивный (intrusive), Π½Π°Π΄ΠΎ Π±Ρ‹Π»ΠΎ Π΅Π³ΠΎ Π²Π΅Π·Π΄Π΅ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ, Π½Π΅ΡƒΠ΄ΠΎΠ±"
+    },
+    {
+      "idx": 13,
+      "label": "10",
+      "overlay": 2,
+      "note": "Зависимо ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ, ΠΌΠΎΠΆΠ½ΠΎ Π»ΠΈΠ±ΠΎ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΡ€ΠΎ tvixbolt, Π»ΠΈΠ±ΠΎ Ρ‚ΠΎΠΆΠ΅ ΠΏΡ€ΠΎ тСсты ΠΈΠ· cppnix"
+    },
+    {
+      "idx": 14,
+      "label": "11",
+      "overlay": 0,
+      "note": "Π½Π° самом Π΄Π΅Π»Π΅ ΡƒΠ΄ΠΈΠ²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π»Π΅Π³ΠΊΠΎ, Π½ΠΎ ΡΡ‚Π°Π»ΠΊΠΈΠ²Π°Π»ΠΈΡΡŒ с ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΎΠΉ, Ρ‡Ρ‚ΠΎ ΠΎΠ½ ΠΈΠ½ΠΎΠ³Π΄Π° пСрСстал Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ\n\nΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ с SystemTime::now\n\nΠ΅ΡΡ‚ΡŒ ΠΊΠΎΠ΅-ΠΊΠ°ΠΊΠΈΠ΅ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠ± ΠΏΠΎΠΌΠΎΠ³ΡƒΡ‚, Π½ΠΎ ΠΌΡ‹ ΠΈΡ… ΠΏΠΎΠΊΠ° Π½Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠ»ΠΈ\n\nΠ² Ρ†Π΅Π»ΠΎΠΌ, wasm Π½Π° растС довольно ΡƒΠ΄ΠΎΠ±Π½ΠΎ"
+    },
+    {
+      "idx": 15,
+      "label": "12",
+      "overlay": 0,
+      "note": "ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΉ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚, ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅ΠΌ ΠΊΠΎΠΌΠΌΠΈΡ‚Ρ‹ ΠΎΡ‚ всСх\n\nΠ΅ΡΡ‚ΡŒ Π΅Ρ‰Π΅ Π±Π°Π³ΠΈ, TODOs, ΠΈ Ρ‚Π΄ Π² tvix-eval\n\nΠ½ΠΎ Π΅ΡΡ‚ΡŒ Ρ‚ΠΎΠΆΠ΅ ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Π΅ части твикса, Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ найдСтся"
+    },
+    {
+      "idx": 16,
+      "label": "13",
+      "overlay": 0,
+      "note": "спасибо всСм, Π²ΠΎΡ‚ ссылки, Π½Π° QR-ΠΊΠΎΠ΄Π΅ Π΅ΡΡ‚ΡŒ всС Π²ΠΎΡ‚ этот Π²ΠΎΡ‚, ΠΈ Ρ‚Π°ΠΌ Ρ‚ΠΎΠΆΠ΅ ΠΏΠΎΡ‚ΠΎΠΌ добавлю сам Π΄ΠΎΠΊΠ»Π°Π΄\n\nΠ΅Ρ‰Π΅ Π·Π°Π²Ρ‚Ρ€Π° начинаСтся NixCon, Ссли Π²Π°ΠΌ Π²Π΄Ρ€ΡƒΠ³ интСрСсно, ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ½Π»Π°ΠΉΠ½ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ. Π’Π°ΠΌ Π±ΡƒΠ΄Π΅Ρ‚ Π΄ΠΎΠΊΠ»Π°Π΄ ΠΏΡ€ΠΎ tvix Ρ‚ΠΎΠΆΠ΅, Π½ΠΎ ΠΎΠ± ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Ρ… частях."
+    }
+  ]
+}
diff --git a/users/tazjin/presentations/tvix-eval-2023/presentation.tex b/users/tazjin/presentations/tvix-eval-2023/presentation.tex
new file mode 100644
index 0000000000..294dad7942
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/presentation.tex
@@ -0,0 +1,148 @@
+\documentclass[12pt]{beamer}
+
+\usepackage[utf8]{inputenc}
+\usepackage[main=russian,english]{babel}
+\usepackage{fontspec}
+\usepackage{listings}
+
+\setmainfont{JetBrains Mono}
+\setsansfont{JetBrains Mono}
+
+\usetheme{metropolis}
+\newenvironment{code}{\ttfamily}{\par}
+\title{tvix-eval \\ компилятор ΠΈ Ρ€Π°Π½Ρ‚Π°ΠΉΠΌ для Nix, Π½Π° Rust}
+
+\titlegraphic{\vspace{4.8cm}\flushright\includegraphics[width=6cm,keepaspectratio=true]{tvix-logo.png}}
+
+\date{2023-09-07}
+\author{ВинсСнт Амбо}
+\institute{TVL}
+
+\begin{document}
+  %% Slide -1 (before counter):
+  \begin{frame}
+    \begin{center}
+      \titlepage
+    \end{center}
+  \end{frame}
+
+  %% Slide 0 (title):
+  \begin{frame}
+    \begin{center}
+      \titlepage
+    \end{center}
+  \end{frame}
+
+  %% Slide 1:
+  \begin{frame}{\textbf{Π’}he \textbf{V}irus \textbf{L}ounge}
+    \begin{itemize}
+    \item ΠΎΠ½Π»Π°ΠΉΠ½-ΠΊΠΎΠΌΡŒΡŽΠ½ΠΈΡ‚ΠΈ, Π·Π°Π½ΠΈΠΌΠ°ΡŽΡ‰Π΅Π΅ΡΡ Ρ‚ΡƒΠ»ΠΈΠ½Π³ΠΎΠΌ для ΠΌΠΎΠ½ΠΎΡ€Π΅ΠΏΠΎ
+    \item основной фокус Π½Π° Nix
+    \item Nix Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для сборки ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ²
+    \item Π₯ΠΎΡ‚Π΅Π»ΠΎΡΡŒ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Nix Π²Π΅Π·Π΄Π΅
+    \end{itemize}
+  \end{frame}
+
+  %% Slide 2:
+  \begin{frame}{ΠžΡΠΎΠ±Π΅Π½Π½ΠΎΡΡ‚ΠΈ языка Nix}
+    \begin{itemize}
+    \item Π›Π΅Π½ΠΈΠ²Ρ‹ΠΉ язык. Π’Ρ‹Ρ‡ΠΈΡΠ»ΡΡ‚ΡŒ всС сразу нСльзя.
+    \item Π―Π·Ρ‹ΠΊ развивался ΠΎΡ€Π³Π°Π½ΠΈΡ‡Π½ΠΎ.
+    \item Π‘ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²ΠΎ ΠΊΠΎΠ΄Π° Π½Π° Nix --- Π² ΠΎΠ΄Π½ΠΎΠΌ мСстС: \begin{code}nixpkgs\end{code}
+    \end{itemize}
+  \end{frame}
+
+  %% Slide 3:
+  \begin{frame}{ВСкущая имплСмСнтация: C++ Nix}
+    \lstinputlisting[
+      language=c++,
+      basicstyle={\scriptsize}
+    ]{cppnix-example-lexer.cpp}
+  \end{frame}
+
+  %% Slide 4:
+  \begin{frame}{ВСкущая имплСмСнтация: C++ Nix}
+    \lstinputlisting[
+      language=c++,
+      basicstyle={\scriptsize}
+    ]{cppnix-example-smuggling.cpp}
+  \end{frame}
+
+  %% Slide 5:
+  \section{``Let's rewrite it in Rust!''}
+
+  %% Slide 6:
+  \section*{Бпасибо, jD91mZM2!\\\normalsize{Π°Π²Ρ‚ΠΎΡ€ ``rnix-parser''; *2002 - \textdagger 2021}}
+
+  %% Slide 7:
+  \begin{frame}{tvix-eval, - (язык) Nix, Π½Π° Rust}
+    \begin{itemize}
+    \item написано с ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΌ парсСром
+    \item bytecode-ΠΈΠ½Ρ‚Π΅Ρ€ΠΏΡ€Π΅Ρ‚Π°Ρ‚ΠΎΡ€, вмСсто tree-walk
+    \item Π΄ΠΎΠ»ΠΆΠ½Π° Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Ρ… частСй tvix
+    \end{itemize}
+  \end{frame}
+
+  %% Slide 8:
+  \begin{frame}{tvix-eval, основныС части}
+    \begin{enumerate}
+    \item собствСнный Π±Π°ΠΉΡ‚ΠΊΠΎΠ΄ ΠΈ компилятор
+    \end{enumerate}
+  \end{frame}
+
+  %% ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ opcode.rs, быстро ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ compiler/mod.rs
+
+  %% Slide 9:
+  \begin{frame}{tvix-eval, основныС части}
+    \begin{enumerate}
+    \item собствСнный Π±Π°ΠΉΡ‚ΠΊΠΎΠ΄ ΠΈ компилятор
+    \item прСдставлСниС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ языка Π² Ρ€Π°Π½Ρ‚Π°ΠΉΠΌΠ΅
+    \end{enumerate}
+  \end{frame}
+
+  %% ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ Value
+
+  %% Slide 10:
+  \begin{frame}{tvix-eval, основныС части}
+    \begin{enumerate}
+    \item собствСнный Π±Π°ΠΉΡ‚ΠΊΠΎΠ΄ ΠΈ компилятор
+    \item прСдставлСниС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΈ языка Π² Ρ€Π°Π½Ρ‚Π°ΠΉΠΌΠ΅
+    \item ... ΠΈ сам Ρ€Π°Π½Ρ‚Π°ΠΉΠΌ!
+    \end{enumerate}
+  \end{frame}
+
+  %% ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ VM
+
+  \section{``ПодоТди, Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ Ρ€Π°Π½Ρ‚Π°ΠΉΠΌ ΠΆΠ΅ Π½Π΅ Ρ‚Π°ΠΊ просто?''}
+
+  %% ΠΎΠ±ΡŠΡΡΠ½ΠΈΡ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ со стСком ΠΈ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ Π΄ΠΈΠ°Π³Ρ€Π°ΠΌΠΌΡƒ
+
+  \section{``А ΠΎΡ‚ΠΊΡƒΠ΄Π° знаСшь, Ρ‡Ρ‚ΠΎ это всС ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚?''}
+
+  %% ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΊΠ°ΠΊ тСсты Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚
+  %% ΠΎΠ±ΡŠΡΡΠ½ΠΈΡ‚ΡŒ Π΄Π΅Π±Π°Π³ΠΈΠ½Π³, Ввиксболт ΠΈ Ρ‚Π΄
+
+  %% Slide 10:
+  \begin{frame}{tvix-eval, Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅}
+    \begin{itemize}
+    \item ΡƒΠ΄ΠΈΠ²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π»Π΅Π³ΠΊΠΎ Π΄Π΅Π»Π°Ρ‚ΡŒ
+    \item Π½ΠΎ Π΅ΡΡ‚ΡŒ слоТности Π² \begin{code}std::\end{code}
+      % ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΏΡ€ΠΈΠΌΠ΅Ρ€
+    \end{itemize}
+  \end{frame}
+
+  %% Slide 11:
+  \begin{frame}{А Ρ‡Ρ‚ΠΎ дальшС?}
+    Π’ tvix-eval Π΅ΡΡ‚ΡŒ Π΅Ρ‰Π΅ ΠΊΠΎΠ΅-ΠΊΠ°ΠΊΠΈΠ΅ интСрСсныС ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹. ΠœΠΎΠΆΠ΅Ρ‚ Ρ‚Ρ‹ ΠΈΡ…
+    Ρ€Π΅ΡˆΠΈΡˆΡŒ?
+  \end{frame}
+
+  \begin{frame}{Бпасибо!}
+    \begin{center}
+      \includegraphics[width=6cm,keepaspectratio=true]{qrcode.png}
+
+      https://tazj.in/blog/tvix-eval-talk-2023 \\
+      t.me/tazjin | t.me/tazlog
+    \end{center}
+  \end{frame}
+\end{document}
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/.gitignore b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/.gitignore
new file mode 100644
index 0000000000..73b9c106db
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/.gitignore
@@ -0,0 +1,2 @@
+target/
+dist/
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.lock b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.lock
new file mode 100644
index 0000000000..ef879254cb
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.lock
@@ -0,0 +1,899 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "anymap2"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "boolinator"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
+
+[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-io"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
+name = "gloo"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d"
+dependencies = [
+ "gloo-console",
+ "gloo-dialogs",
+ "gloo-events",
+ "gloo-file",
+ "gloo-history",
+ "gloo-net",
+ "gloo-render",
+ "gloo-storage",
+ "gloo-timers",
+ "gloo-utils",
+ "gloo-worker",
+]
+
+[[package]]
+name = "gloo-console"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-dialogs"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-events"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-file"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7"
+dependencies = [
+ "gloo-events",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-history"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f"
+dependencies = [
+ "gloo-events",
+ "gloo-utils",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_urlencoded",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-net"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-sink",
+ "gloo-utils",
+ "http",
+ "js-sys",
+ "pin-project",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-render"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-storage"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-worker"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a"
+dependencies = [
+ "anymap2",
+ "bincode",
+ "gloo-console",
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "implicit-clone"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c6ecbd987bb94f1f3c76c6787879756cf4b6f73bfff48d79308e8c56b46f65f"
+dependencies = [
+ "indexmap",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "js-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "memchr"
+version = "2.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "pin-project"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pinned"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b"
+dependencies = [
+ "futures",
+ "rustversion",
+ "thiserror",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+dependencies = [
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prokio"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488"
+dependencies = [
+ "futures",
+ "gloo",
+ "num_cpus",
+ "once_cell",
+ "pin-project",
+ "pinned",
+ "tokio",
+ "tokio-stream",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.105"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "tokio"
+version = "1.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
+dependencies = [
+ "backtrace",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+
+[[package]]
+name = "wasm-fs-demo"
+version = "0.1.0"
+dependencies = [
+ "yew",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "yew"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc"
+dependencies = [
+ "console_error_panic_hook",
+ "futures",
+ "gloo",
+ "implicit-clone",
+ "indexmap",
+ "js-sys",
+ "prokio",
+ "rustversion",
+ "serde",
+ "slab",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "yew-macro",
+]
+
+[[package]]
+name = "yew-macro"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b64c253c1d401f1ea868ca9988db63958cfa15a69f739101f338d6f05eea8301"
+dependencies = [
+ "boolinator",
+ "once_cell",
+ "prettyplease",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.toml b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.toml
new file mode 100644
index 0000000000..4a445065e4
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "wasm-fs-demo"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+yew = { version = "0.20.0", features = [ "csr" ]}
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/index.html b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/index.html
new file mode 100644
index 0000000000..e024c466cd
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/index.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <title>wasm-fs-demo</title>
+    </head>
+</html>
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/src/main.rs b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/src/main.rs
new file mode 100644
index 0000000000..4ad177ad7a
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/src/main.rs
@@ -0,0 +1,41 @@
+use std::time::{SystemTime, UNIX_EPOCH};
+use yew::prelude::*;
+
+fn time_example() -> Html {
+    let epoch = match SystemTime::now().duration_since(UNIX_EPOCH) {
+        Ok(duration) => duration.as_secs(),
+        Err(err) => {
+            return html! {
+                format!("failed to calculate duration: {}", err)
+            }
+        }
+    };
+
+    html! {
+        <p>
+          {"Seconds since epoch: "}{epoch}
+        </p>
+    }
+}
+
+struct App;
+impl Component for App {
+    type Message = ();
+    type Properties = ();
+
+    fn create(_: &Context<Self>) -> Self {
+        Self
+    }
+
+    fn update(&mut self, _: &Context<Self>, _: Self::Message) -> bool {
+        false
+    }
+
+    fn view(&self, _: &Context<Self>) -> Html {
+        time_example()
+    }
+}
+
+fn main() {
+    yew::Renderer::<App>::new().render();
+}
diff --git a/users/tazjin/rlox/src/bytecode/compiler.rs b/users/tazjin/rlox/src/bytecode/compiler.rs
index 3e8a80653f..89584f19d7 100644
--- a/users/tazjin/rlox/src/bytecode/compiler.rs
+++ b/users/tazjin/rlox/src/bytecode/compiler.rs
@@ -63,9 +63,11 @@ enum Precedence {
     Equality,   // == !=
     Comparison, // < > <= >=
     Term,       // + -
-    Factor,     // * /
-    Unary,      // ! -
-    Call,       // . ()
+    Factor,     //
+    // 
+    // * /
+    Unary, // ! -
+    Call,  // . ()
     Primary,
 }
 
@@ -78,11 +80,7 @@ struct ParseRule<T: Iterator<Item = Token>> {
 }
 
 impl<T: Iterator<Item = Token>> ParseRule<T> {
-    fn new(
-        prefix: Option<ParseFn<T>>,
-        infix: Option<ParseFn<T>>,
-        precedence: Precedence,
-    ) -> Self {
+    fn new(prefix: Option<ParseFn<T>>, infix: Option<ParseFn<T>>, precedence: Precedence) -> Self {
         ParseRule {
             prefix,
             infix,
@@ -105,18 +103,16 @@ impl Precedence {
             Precedence::Factor => Precedence::Unary,
             Precedence::Unary => Precedence::Call,
             Precedence::Call => Precedence::Primary,
-            Precedence::Primary => panic!(
-                "invalid parser state: no higher precedence than Primary"
-            ),
+            Precedence::Primary => {
+                panic!("invalid parser state: no higher precedence than Primary")
+            }
         }
     }
 }
 
 fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
     match token {
-        TokenKind::LeftParen => {
-            ParseRule::new(Some(Compiler::grouping), None, Precedence::None)
-        }
+        TokenKind::LeftParen => ParseRule::new(Some(Compiler::grouping), None, Precedence::None),
 
         TokenKind::Minus => ParseRule::new(
             Some(Compiler::unary),
@@ -124,57 +120,33 @@ fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
             Precedence::Term,
         ),
 
-        TokenKind::Plus => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Term)
-        }
+        TokenKind::Plus => ParseRule::new(None, Some(Compiler::binary), Precedence::Term),
 
-        TokenKind::Slash => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Factor)
-        }
+        TokenKind::Slash => ParseRule::new(None, Some(Compiler::binary), Precedence::Factor),
 
-        TokenKind::Star => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Factor)
-        }
+        TokenKind::Star => ParseRule::new(None, Some(Compiler::binary), Precedence::Factor),
 
-        TokenKind::Number(_) => {
-            ParseRule::new(Some(Compiler::number), None, Precedence::None)
-        }
+        TokenKind::Number(_) => ParseRule::new(Some(Compiler::number), None, Precedence::None),
 
-        TokenKind::True => {
-            ParseRule::new(Some(Compiler::literal), None, Precedence::None)
-        }
+        TokenKind::True => ParseRule::new(Some(Compiler::literal), None, Precedence::None),
 
-        TokenKind::False => {
-            ParseRule::new(Some(Compiler::literal), None, Precedence::None)
-        }
+        TokenKind::False => ParseRule::new(Some(Compiler::literal), None, Precedence::None),
 
-        TokenKind::Nil => {
-            ParseRule::new(Some(Compiler::literal), None, Precedence::None)
-        }
+        TokenKind::Nil => ParseRule::new(Some(Compiler::literal), None, Precedence::None),
 
-        TokenKind::Bang => {
-            ParseRule::new(Some(Compiler::unary), None, Precedence::None)
-        }
+        TokenKind::Bang => ParseRule::new(Some(Compiler::unary), None, Precedence::None),
 
-        TokenKind::BangEqual => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Equality)
-        }
+        TokenKind::BangEqual => ParseRule::new(None, Some(Compiler::binary), Precedence::Equality),
 
-        TokenKind::EqualEqual => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Equality)
-        }
+        TokenKind::EqualEqual => ParseRule::new(None, Some(Compiler::binary), Precedence::Equality),
 
-        TokenKind::Greater => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
-        }
+        TokenKind::Greater => ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison),
 
         TokenKind::GreaterEqual => {
             ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
         }
 
-        TokenKind::Less => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
-        }
+        TokenKind::Less => ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison),
 
         TokenKind::LessEqual => {
             ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
@@ -184,9 +156,7 @@ fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
             ParseRule::new(Some(Compiler::variable), None, Precedence::None)
         }
 
-        TokenKind::String(_) => {
-            ParseRule::new(Some(Compiler::string), None, Precedence::None)
-        }
+        TokenKind::String(_) => ParseRule::new(Some(Compiler::string), None, Precedence::None),
 
         _ => ParseRule::new(None, None, Precedence::None),
     }
@@ -236,9 +206,7 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
 
     fn define_variable(&mut self, var: Option<ConstantIdx>) -> LoxResult<()> {
         if self.locals.scope_depth == 0 {
-            self.emit_op(OpCode::OpDefineGlobal(
-                var.expect("should be global"),
-            ));
+            self.emit_op(OpCode::OpDefineGlobal(var.expect("should be global")));
         } else {
             self.locals
                 .locals
@@ -305,9 +273,7 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
     }
 
     fn block(&mut self) -> LoxResult<()> {
-        while !self.check(&TokenKind::RightBrace)
-            && !self.check(&TokenKind::Eof)
-        {
+        while !self.check(&TokenKind::RightBrace) && !self.check(&TokenKind::Eof) {
             self.declaration()?;
         }
 
@@ -712,9 +678,8 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
 
 pub fn compile(code: &str) -> Result<(Interner, Chunk), Vec<Error>> {
     let chars = code.chars().collect::<Vec<char>>();
-    let tokens = scanner::scan(&chars).map_err(|errors| {
-        errors.into_iter().map(Into::into).collect::<Vec<Error>>()
-    })?;
+    let tokens = scanner::scan(&chars)
+        .map_err(|errors| errors.into_iter().map(Into::into).collect::<Vec<Error>>())?;
 
     let mut compiler = Compiler {
         tokens: tokens.into_iter().peekable(),
diff --git a/users/tazjin/rlox/src/bytecode/mod.rs b/users/tazjin/rlox/src/bytecode/mod.rs
index c6f3a737ae..117f17824a 100644
--- a/users/tazjin/rlox/src/bytecode/mod.rs
+++ b/users/tazjin/rlox/src/bytecode/mod.rs
@@ -23,10 +23,7 @@ impl crate::Lox for Interpreter {
         Interpreter {}
     }
 
-    fn interpret(
-        &mut self,
-        code: String,
-    ) -> Result<Self::Value, Vec<Self::Error>> {
+    fn interpret(&mut self, code: String) -> Result<Self::Value, Vec<Self::Error>> {
         let (strings, chunk) = compiler::compile(&code)?;
         vm::interpret(strings, chunk).map_err(|e| vec![e])
     }
diff --git a/users/tazjin/rlox/src/bytecode/vm.rs b/users/tazjin/rlox/src/bytecode/vm.rs
index d287ec7cb8..30ffebc79c 100644
--- a/users/tazjin/rlox/src/bytecode/vm.rs
+++ b/users/tazjin/rlox/src/bytecode/vm.rs
@@ -118,12 +118,7 @@ impl VM {
 
                 OpCode::OpNegate => {
                     let v = self.pop();
-                    with_type!(
-                        self,
-                        v,
-                        Value::Number(num),
-                        self.push(Value::Number(-num))
-                    );
+                    with_type!(self, v, Value::Number(num), self.push(Value::Number(-num)));
                 }
 
                 OpCode::OpSubtract => binary_op!(self, Number, -),
@@ -141,15 +136,18 @@ impl VM {
                             self.push(Value::String(new_s.into()));
                         }
 
-                        (Value::Number(n_a), Value::Number(n_b)) =>
-                            self.push(Value::Number(n_a + n_b)),
+                        (Value::Number(n_a), Value::Number(n_b)) => {
+                            self.push(Value::Number(n_a + n_b))
+                        }
 
-                        _ => return Err(Error {
-                            line: self.chunk.get_line(self.ip - 1),
-                            kind: ErrorKind::TypeError(
-                                "'+' operator only works on strings and numbers".into()
-                            ),
-                        })
+                        _ => {
+                            return Err(Error {
+                                line: self.chunk.get_line(self.ip - 1),
+                                kind: ErrorKind::TypeError(
+                                    "'+' operator only works on strings and numbers".into(),
+                                ),
+                            })
+                        }
                     }
                 }
 
@@ -205,8 +203,7 @@ impl VM {
                         self.stack.len() > local_idx.0,
                         "stack is not currently large enough for local"
                     );
-                    self.stack[local_idx.0] =
-                        self.stack.last().unwrap().clone();
+                    self.stack[local_idx.0] = self.stack.last().unwrap().clone();
                 }
 
                 OpCode::OpJumpPlaceholder(_) => {
@@ -255,9 +252,7 @@ impl VM {
     fn print_value(&self, val: Value) -> String {
         match val {
             Value::String(LoxString::Heap(s)) => s,
-            Value::String(LoxString::Interned(id)) => {
-                self.strings.lookup(id).into()
-            }
+            Value::String(LoxString::Interned(id)) => self.strings.lookup(id).into(),
             _ => format!("{:?}", val),
         }
     }
diff --git a/users/tazjin/rlox/src/main.rs b/users/tazjin/rlox/src/main.rs
index 2d8cf4f354..ee61ae01a1 100644
--- a/users/tazjin/rlox/src/main.rs
+++ b/users/tazjin/rlox/src/main.rs
@@ -1,8 +1,5 @@
-use std::env;
-use std::fs;
-use std::io;
 use std::io::Write;
-use std::process;
+use std::{env, fs, io, process};
 
 mod bytecode;
 mod scanner;
@@ -15,10 +12,7 @@ pub trait Lox {
     type Error: std::fmt::Display;
 
     fn create() -> Self;
-    fn interpret(
-        &mut self,
-        source: String,
-    ) -> Result<Self::Value, Vec<Self::Error>>;
+    fn interpret(&mut self, source: String) -> Result<Self::Value, Vec<Self::Error>>;
 }
 
 fn main() {
@@ -29,9 +23,7 @@ fn main() {
     }
 
     match env::var("LOX_INTERPRETER").as_ref().map(String::as_str) {
-        Ok("treewalk") => {
-            pick::<treewalk::interpreter::Interpreter>(args.nth(1))
-        }
+        Ok("treewalk") => pick::<treewalk::interpreter::Interpreter>(args.nth(1)),
         _ => pick::<bytecode::Interpreter>(args.nth(1)),
     }
 }
@@ -46,8 +38,7 @@ fn pick<I: Lox>(file_arg: Option<String>) {
 
 // Run Lox code from a file and print results to stdout
 fn run_file<I: Lox>(file: &str) {
-    let contents =
-        fs::read_to_string(file).expect("failed to read the input file");
+    let contents = fs::read_to_string(file).expect("failed to read the input file");
     let mut lox = I::create();
     run(&mut lox, contents);
 }
diff --git a/users/tazjin/rlox/src/scanner.rs b/users/tazjin/rlox/src/scanner.rs
index 4e8f07b61f..314b56d6d3 100644
--- a/users/tazjin/rlox/src/scanner.rs
+++ b/users/tazjin/rlox/src/scanner.rs
@@ -106,15 +106,9 @@ impl<'a> Scanner<'a> {
 
             // possible multi-character tokens
             '!' => self.add_if_next('=', TokenKind::BangEqual, TokenKind::Bang),
-            '=' => {
-                self.add_if_next('=', TokenKind::EqualEqual, TokenKind::Equal)
-            }
+            '=' => self.add_if_next('=', TokenKind::EqualEqual, TokenKind::Equal),
             '<' => self.add_if_next('=', TokenKind::LessEqual, TokenKind::Less),
-            '>' => self.add_if_next(
-                '=',
-                TokenKind::GreaterEqual,
-                TokenKind::Greater,
-            ),
+            '>' => self.add_if_next('=', TokenKind::GreaterEqual, TokenKind::Greater),
 
             '/' => {
                 // support comments until EOL by discarding characters
@@ -234,8 +228,7 @@ impl<'a> Scanner<'a> {
             self.advance();
         }
 
-        let ident: String =
-            self.source[self.start..self.current].iter().collect();
+        let ident: String = self.source[self.start..self.current].iter().collect();
 
         // Determine whether this is an identifier, or a keyword:
         let token_kind = match ident.as_str() {
diff --git a/users/tazjin/rlox/src/treewalk/interpreter.rs b/users/tazjin/rlox/src/treewalk/interpreter.rs
index d9fe336616..3285775bbe 100644
--- a/users/tazjin/rlox/src/treewalk/interpreter.rs
+++ b/users/tazjin/rlox/src/treewalk/interpreter.rs
@@ -34,11 +34,7 @@ impl Callable {
         }
     }
 
-    fn call(
-        &self,
-        lox: &mut Interpreter,
-        args: Vec<Value>,
-    ) -> Result<Value, Error> {
+    fn call(&self, lox: &mut Interpreter, args: Vec<Value>) -> Result<Value, Error> {
         match self {
             Callable::Builtin(builtin) => builtin.call(args),
 
@@ -50,10 +46,8 @@ impl Callable {
                     fn_env.define(param, value)?;
                 }
 
-                let result = lox.interpret_block_with_env(
-                    Some(Rc::new(RwLock::new(fn_env))),
-                    &func.body,
-                );
+                let result =
+                    lox.interpret_block_with_env(Some(Rc::new(RwLock::new(fn_env))), &func.body);
 
                 match result {
                     // extract returned values if applicable
@@ -109,22 +103,13 @@ pub struct Environment {
 }
 
 impl Environment {
-    fn define(
-        &mut self,
-        name: &scanner::Token,
-        value: Value,
-    ) -> Result<(), Error> {
+    fn define(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
         let ident = identifier_str(name)?;
         self.values.insert(ident.into(), value);
         Ok(())
     }
 
-    fn get(
-        &self,
-        ident: &str,
-        line: usize,
-        depth: usize,
-    ) -> Result<Value, Error> {
+    fn get(&self, ident: &str, line: usize, depth: usize) -> Result<Value, Error> {
         if depth > 0 {
             match &self.enclosing {
                 None => {
@@ -137,9 +122,7 @@ impl Environment {
                     })
                 }
                 Some(parent) => {
-                    let env = parent
-                        .read()
-                        .expect("fatal: environment lock poisoned");
+                    let env = parent.read().expect("fatal: environment lock poisoned");
                     return env.get(ident, line, depth - 1);
                 }
             }
@@ -154,11 +137,7 @@ impl Environment {
             })
     }
 
-    fn assign(
-        &mut self,
-        name: &scanner::Token,
-        value: Value,
-    ) -> Result<(), Error> {
+    fn assign(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
         let ident = identifier_str(name)?;
 
         match self.values.get_mut(ident) {
@@ -242,22 +221,14 @@ impl Lox for Interpreter {
 
 impl Interpreter {
     // Environment modification helpers
-    fn define_var(
-        &mut self,
-        name: &scanner::Token,
-        value: Value,
-    ) -> Result<(), Error> {
+    fn define_var(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
         self.env
             .write()
             .expect("environment lock is poisoned")
             .define(name, value)
     }
 
-    fn assign_var(
-        &mut self,
-        name: &scanner::Token,
-        value: Value,
-    ) -> Result<(), Error> {
+    fn assign_var(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
         self.env
             .write()
             .expect("environment lock is poisoned")
@@ -271,11 +242,10 @@ impl Interpreter {
             kind: ErrorKind::UndefinedVariable(ident.into()),
         })?;
 
-        self.env.read().expect("environment lock is poisoned").get(
-            ident,
-            var.name.line,
-            depth,
-        )
+        self.env
+            .read()
+            .expect("environment lock is poisoned")
+            .get(ident, var.name.line, depth)
     }
 
     /// Interpret the block in the supplied environment. If no
@@ -324,16 +294,10 @@ impl Interpreter {
                 Value::Literal(Literal::String(output))
             }
             Statement::Var(var) => return self.interpret_var(var),
-            Statement::Block(block) => {
-                return self.interpret_block_with_env(None, block)
-            }
+            Statement::Block(block) => return self.interpret_block_with_env(None, block),
             Statement::If(if_stmt) => return self.interpret_if(if_stmt),
-            Statement::While(while_stmt) => {
-                return self.interpret_while(while_stmt)
-            }
-            Statement::Function(func) => {
-                return self.interpret_function(func.clone())
-            }
+            Statement::While(while_stmt) => return self.interpret_while(while_stmt),
+            Statement::Function(func) => return self.interpret_function(func.clone()),
             Statement::Return(ret) => {
                 return Err(Error {
                     line: 0,
@@ -348,9 +312,7 @@ impl Interpreter {
     fn interpret_var(&mut self, var: &parser::Var) -> Result<Value, Error> {
         let init = var.initialiser.as_ref().ok_or_else(|| Error {
             line: var.name.line,
-            kind: ErrorKind::InternalError(
-                "missing variable initialiser".into(),
-            ),
+            kind: ErrorKind::InternalError("missing variable initialiser".into()),
         })?;
         let value = self.eval(init)?;
         self.define_var(&var.name, value.clone())?;
@@ -369,10 +331,7 @@ impl Interpreter {
         }
     }
 
-    fn interpret_while(
-        &mut self,
-        stmt: &parser::While,
-    ) -> Result<Value, Error> {
+    fn interpret_while(&mut self, stmt: &parser::While) -> Result<Value, Error> {
         let mut value = Value::Literal(Literal::Nil);
         while eval_truthy(&self.eval(&stmt.condition)?) {
             value = self.interpret_stmt(&stmt.body)?;
@@ -381,10 +340,7 @@ impl Interpreter {
         Ok(value)
     }
 
-    fn interpret_function(
-        &mut self,
-        func: Rc<parser::Function>,
-    ) -> Result<Value, Error> {
+    fn interpret_function(&mut self, func: Rc<parser::Function>) -> Result<Value, Error> {
         let name = func.name.clone();
         let value = Value::Callable(Callable::Function {
             func,
@@ -414,9 +370,7 @@ impl Interpreter {
             (TokenKind::Minus, Value::Literal(Literal::Number(num))) => {
                 Ok(Literal::Number(-num).into())
             }
-            (TokenKind::Bang, right) => {
-                Ok(Literal::Boolean(!eval_truthy(&right)).into())
-            }
+            (TokenKind::Bang, right) => Ok(Literal::Boolean(!eval_truthy(&right)).into()),
 
             (op, right) => Err(Error {
                 line: expr.operator.line,
@@ -478,10 +432,7 @@ impl Interpreter {
         Ok(value)
     }
 
-    fn eval_logical(
-        &mut self,
-        logical: &parser::Logical,
-    ) -> Result<Value, Error> {
+    fn eval_logical(&mut self, logical: &parser::Logical) -> Result<Value, Error> {
         let left = eval_truthy(&self.eval(&logical.left)?);
         let right = eval_truthy(&self.eval(&logical.right)?);
 
@@ -490,10 +441,7 @@ impl Interpreter {
             TokenKind::Or => Ok(Literal::Boolean(left || right).into()),
             kind => Err(Error {
                 line: logical.operator.line,
-                kind: ErrorKind::InternalError(format!(
-                    "Invalid logical operator: {:?}",
-                    kind
-                )),
+                kind: ErrorKind::InternalError(format!("Invalid logical operator: {:?}", kind)),
             }),
         }
     }
@@ -504,10 +452,7 @@ impl Interpreter {
             Value::Literal(v) => {
                 return Err(Error {
                     line: call.paren.line,
-                    kind: ErrorKind::RuntimeError(format!(
-                        "not callable: {:?}",
-                        v
-                    )),
+                    kind: ErrorKind::RuntimeError(format!("not callable: {:?}", v)),
                 })
             }
         };
@@ -546,10 +491,7 @@ fn eval_truthy(lit: &Value) -> bool {
     }
 }
 
-fn set_enclosing_env(
-    this: &RwLock<Environment>,
-    parent: Rc<RwLock<Environment>>,
-) {
+fn set_enclosing_env(this: &RwLock<Environment>, parent: Rc<RwLock<Environment>>) {
     this.write()
         .expect("environment lock is poisoned")
         .enclosing = Some(parent);
diff --git a/users/tazjin/rlox/src/treewalk/parser.rs b/users/tazjin/rlox/src/treewalk/parser.rs
index 003cc34b46..5794b42d15 100644
--- a/users/tazjin/rlox/src/treewalk/parser.rs
+++ b/users/tazjin/rlox/src/treewalk/parser.rs
@@ -124,56 +124,54 @@ pub enum Statement {
 
 // Parser
 
-/*
-program        β†’ declaration* EOF ;
-
-declaration    β†’ funDecl
-               | varDecl
-               | statement ;
-
-funDecl        β†’ "fun" function ;
-function       β†’ IDENTIFIER "(" parameters? ")" block ;
-parameters     β†’ IDENTIFIER ( "," IDENTIFIER )* ;
-
-
-statement      β†’ exprStmt
-               | forStmt
-               | ifStmt
-               | printStmt
-               | returnStmt
-               | whileStmt
-               | block ;
-
-forStmt        β†’ "for" "(" ( varDecl | exprStmt | ";" )
-                 expression? ";"
-                 expression? ")" statement ;
-
-returnStmt     β†’ "return" expression? ";" ;
-
-whileStmt      β†’ "while" "(" expression ")" statement ;
-
-exprStmt       β†’ expression ";" ;
-
-ifStmt         β†’ "if" "(" expression ")" statement
-               ( "else" statement )? ;
-
-printStmt      β†’ "print" expression ";" ;
-
-expression     β†’ assignment ;
-assignment     β†’ IDENTIFIER "=" assignment
-               | logic_or ;
-logic_or       β†’ logic_and ( "or" logic_and )* ;
-logic_and      β†’ equality ( "and" equality )* ;
-equality       β†’ comparison ( ( "!=" | "==" ) comparison )* ;
-comparison     β†’ term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
-term           β†’ factor ( ( "-" | "+" ) factor )* ;
-factor         β†’ unary ( ( "/" | "*" ) unary )* ;
-unary          β†’ ( "!" | "-" ) unary | call ;
-call           β†’ primary ( "(" arguments? ")" )* ;
-arguments      β†’ expression ( "," expression )* ;
-primary        β†’ NUMBER | STRING | "true" | "false" | "nil"
-               | "(" expression ")" ;
-*/
+// program        β†’ declaration* EOF ;
+//
+// declaration    β†’ funDecl
+// | varDecl
+// | statement ;
+//
+// funDecl        β†’ "fun" function ;
+// function       β†’ IDENTIFIER "(" parameters? ")" block ;
+// parameters     β†’ IDENTIFIER ( "," IDENTIFIER )* ;
+//
+//
+// statement      β†’ exprStmt
+// | forStmt
+// | ifStmt
+// | printStmt
+// | returnStmt
+// | whileStmt
+// | block ;
+//
+// forStmt        β†’ "for" "(" ( varDecl | exprStmt | ";" )
+// expression? ";"
+// expression? ")" statement ;
+//
+// returnStmt     β†’ "return" expression? ";" ;
+//
+// whileStmt      β†’ "while" "(" expression ")" statement ;
+//
+// exprStmt       β†’ expression ";" ;
+//
+// ifStmt         β†’ "if" "(" expression ")" statement
+// ( "else" statement )? ;
+//
+// printStmt      β†’ "print" expression ";" ;
+//
+// expression     β†’ assignment ;
+// assignment     β†’ IDENTIFIER "=" assignment
+// | logic_or ;
+// logic_or       β†’ logic_and ( "or" logic_and )* ;
+// logic_and      β†’ equality ( "and" equality )* ;
+// equality       β†’ comparison ( ( "!=" | "==" ) comparison )* ;
+// comparison     β†’ term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
+// term           β†’ factor ( ( "-" | "+" ) factor )* ;
+// factor         β†’ unary ( ( "/" | "*" ) unary )* ;
+// unary          β†’ ( "!" | "-" ) unary | call ;
+// call           β†’ primary ( "(" arguments? ")" )* ;
+// arguments      β†’ expression ( "," expression )* ;
+// primary        β†’ NUMBER | STRING | "true" | "false" | "nil"
+// | "(" expression ")" ;
 
 struct Parser {
     tokens: Vec<Token>,
@@ -213,9 +211,7 @@ impl Parser {
                 if params.len() >= 255 {
                     return Err(Error {
                         line: self.peek().line,
-                        kind: ErrorKind::InternalError(
-                            "255 parameter limit exceeded.".into(),
-                        ),
+                        kind: ErrorKind::InternalError("255 parameter limit exceeded.".into()),
                     });
                 }
 
@@ -429,10 +425,7 @@ impl Parser {
 
             return Err(Error {
                 line: equals.line,
-                kind: ErrorKind::InvalidAssignmentTarget(format!(
-                    "{:?}",
-                    equals
-                )),
+                kind: ErrorKind::InvalidAssignmentTarget(format!("{:?}", equals)),
             });
         }
 
@@ -495,9 +488,7 @@ impl Parser {
     }
 
     fn unary(&mut self) -> ExprResult {
-        if self.match_token(&TokenKind::Bang)
-            || self.match_token(&TokenKind::Minus)
-        {
+        if self.match_token(&TokenKind::Bang) || self.match_token(&TokenKind::Minus) {
             return Ok(Expr::Unary(Unary {
                 operator: self.previous().clone(),
                 right: Box::new(self.unary()?),
@@ -557,10 +548,7 @@ impl Parser {
 
             TokenKind::LeftParen => {
                 let expr = self.expression()?;
-                self.consume(
-                    &TokenKind::RightParen,
-                    ErrorKind::UnmatchedParens,
-                )?;
+                self.consume(&TokenKind::RightParen, ErrorKind::UnmatchedParens)?;
                 return Ok(Expr::Grouping(Grouping(Box::new(expr))));
             }
 
@@ -632,11 +620,7 @@ impl Parser {
         &self.tokens[self.current - 1]
     }
 
-    fn consume(
-        &mut self,
-        kind: &TokenKind,
-        err: ErrorKind,
-    ) -> Result<Token, Error> {
+    fn consume(&mut self, kind: &TokenKind, err: ErrorKind) -> Result<Token, Error> {
         if self.check_token(kind) {
             return Ok(self.advance());
         }
diff --git a/users/tazjin/rlox/src/treewalk/resolver.rs b/users/tazjin/rlox/src/treewalk/resolver.rs
index 8231ce5a9e..3d12973aa0 100644
--- a/users/tazjin/rlox/src/treewalk/resolver.rs
+++ b/users/tazjin/rlox/src/treewalk/resolver.rs
@@ -56,13 +56,14 @@ impl<'a> Resolver<'a> {
                 // The resolver does not clone references, so unless
                 // the interpreter is called before the resolver this
                 // case should never happen.
-                None => return Err(Error {
-                    line: 0,
-                    kind: ErrorKind::InternalError(
-                        "multiple function references before interpretation"
-                            .into(),
-                    ),
-                }),
+                None => {
+                    return Err(Error {
+                        line: 0,
+                        kind: ErrorKind::InternalError(
+                            "multiple function references before interpretation".into(),
+                        ),
+                    })
+                }
             },
         }
     }
@@ -79,10 +80,7 @@ impl<'a> Resolver<'a> {
         Ok(())
     }
 
-    fn resolve_function(
-        &mut self,
-        func: &'a mut parser::Function,
-    ) -> Result<(), Error> {
+    fn resolve_function(&mut self, func: &'a mut parser::Function) -> Result<(), Error> {
         self.declare(&func.name.lexeme);
         self.define(&func.name.lexeme);
 
@@ -123,17 +121,13 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    fn resolve_variable(
-        &mut self,
-        var: &'a mut parser::Variable,
-    ) -> Result<(), Error> {
+    fn resolve_variable(&mut self, var: &'a mut parser::Variable) -> Result<(), Error> {
         if let Some(scope) = self.scopes.last_mut() {
             if let Some(false) = scope.get(var.name.lexeme.as_str()) {
                 return Err(Error {
                     line: var.name.line,
                     kind: ErrorKind::StaticError(
-                        "can't read local variable in its own initialiser"
-                            .into(),
+                        "can't read local variable in its own initialiser".into(),
                     ),
                 });
             }
@@ -143,10 +137,7 @@ impl<'a> Resolver<'a> {
         Ok(())
     }
 
-    fn resolve_assign(
-        &mut self,
-        assign: &'a mut parser::Assign,
-    ) -> Result<(), Error> {
+    fn resolve_assign(&mut self, assign: &'a mut parser::Assign) -> Result<(), Error> {
         self.resolve_expr(&mut assign.value)?;
         assign.depth = self.resolve_local(&assign.name);
         Ok(())
@@ -162,10 +153,7 @@ impl<'a> Resolver<'a> {
         None
     }
 
-    fn resolve_call(
-        &mut self,
-        call: &'a mut parser::Call,
-    ) -> Result<(), Error> {
+    fn resolve_call(&mut self, call: &'a mut parser::Call) -> Result<(), Error> {
         self.resolve_expr(&mut call.callee)?;
 
         for arg in call.args.iter_mut() {
@@ -198,10 +186,7 @@ impl<'a> Resolver<'a> {
     }
 }
 
-pub fn resolve(
-    globals: &[String],
-    block: &mut parser::Block,
-) -> Result<(), Error> {
+pub fn resolve(globals: &[String], block: &mut parser::Block) -> Result<(), Error> {
     let mut resolver: Resolver = Default::default();
 
     // Scope for static globals only starts, never ends.
diff --git a/users/tazjin/rustfmt.toml b/users/tazjin/rustfmt.toml
new file mode 100644
index 0000000000..0c719dcfec
--- /dev/null
+++ b/users/tazjin/rustfmt.toml
@@ -0,0 +1,22 @@
+edition = "2021"
+newline_style = "Unix"
+
+# Default code with is 100 characters, comments should follow
+# suit.
+wrap_comments = true
+
+# The default of this option creates hard-to-read nesting of
+# conditionals, turn it off.
+combine_control_expr = false
+
+# Group imports by module, but no higher. This avoids hard-to-read
+# nested use statements.
+imports_granularity = "Module"
+
+# Avoid vertical visual clutter by unnecessarily exploding
+# block-like arguments.
+overflow_delimited_expr = true
+
+# Miscellaneous
+format_code_in_doc_comments = true
+normalize_comments = true
diff --git a/users/tazjin/secrets/default.nix b/users/tazjin/secrets/default.nix
new file mode 100644
index 0000000000..5550103c5a
--- /dev/null
+++ b/users/tazjin/secrets/default.nix
@@ -0,0 +1,3 @@
+{ depot, ... }:
+
+depot.ops.secrets.mkSecrets ./. (import ./secrets.nix)
diff --git a/users/tazjin/secrets/geesefs-tazjins-files.age b/users/tazjin/secrets/geesefs-tazjins-files.age
new file mode 100644
index 0000000000..9132c7d108
--- /dev/null
+++ b/users/tazjin/secrets/geesefs-tazjins-files.age
@@ -0,0 +1,18 @@
+age-encryption.org/v1
+-> ssh-ed25519 dcsaLw SrmIul/C/aRTYy5+vVBB0H2bS65XayYf2TXrOSTEbGg
+Js016EtAxiFmyJ4gTmXEjsKT9JmIntcMNgAds+qT7Js
+-> ssh-ed25519 zcCuhA NfUQBKL1KgvUosB2y3oI5HwPjA+4kf8kbBbpNf43JAk
+oE4R2rz1sdBitKzQlzMzneyu8Rvc5utHYRyCeGCQR8g
+-> ssh-rsa zXi7VA
+aDDiygAF5benqqJ1387F9qVDyvb48BkBLAwRi7eUYWkG41s9XUdmK5ppjFdxy1c8
+fx5YcPjO3m84pIv0RxiK7rZhkVi1/eiHhT5lId83wIzQdybjKFSc85YjFO3mGv9A
+EeFMEmlfsRkBHYq/j6Npbg4M5kMxSuSwGSyt6qnoHSWT2phS+41WLA/XT9ln4pRR
+dBDO0ZyK/CpgfDuGo/JARLiGeEMwt7SvkyXidcbD8glg9buu1VGxb/8m/ob1yrbn
+y3mjfOFzO2zF8ZHuScWQlZgvaVk412Xne+n+wva12tS52dEX4FRSMtmUOB8Ai4oq
+wvWB6Ikru5jXRxe8NgDoZw
+-> ssh-ed25519 At5Mag yBwWJVhArq9iwngwaIph56iGfje8T55Ig0nW9268Kic
+T9IWxRJF1U0STinVlBJoaGoegERnWRjnGZeW0HHGQ9Y
+-> C"!?nfs%-grease >>.%|I mA Fd7?aw2m 37I
+vRH3yR7+Ow
+--- AKc40DwXghKw6GHzJUYNJYE0JqMr4M+hR41VRA1IvS4
+ςΕΧtڜργ)u;T™•	Œ'ΰφ8φ=ψS7RΝ<Cx†tωz_md0ZH2Κάα^N—j·†ο*CΙ—	늣΄1ΝΉyΖΌa—eΫΉ8c~Ηζi	5/²‹xş7nΕS^±aΗsygή’Π―(CŠΎ™€₯Ν Ρ%N’Έ‘Πθψ|Y!έvάΠAππI
\ No newline at end of file
diff --git a/users/tazjin/secrets/miniflux.age b/users/tazjin/secrets/miniflux.age
new file mode 100644
index 0000000000..753dc6f034
--- /dev/null
+++ b/users/tazjin/secrets/miniflux.age
@@ -0,0 +1,14 @@
+age-encryption.org/v1
+-> ssh-ed25519 dcsaLw SJBK+ym6o6dcB/+HFWzArbXS9RmyDjnglVxcXduJA1g
+pPWIi2A4G4X7I14HoZUWsNd/MOfhW1ZanwB/5OROSrw
+-> ssh-ed25519 zcCuhA oo/8OTqpV85g/9pha0qkmxwlYAlsc7v+nXbbtj67Jmc
+AexsAIgW6e5fYoPNJJZYdP61OvON2bKiL9ZJgLdG/zU
+-> ssh-ed25519 ph9lig 4evTl0M3SfdlmTixm3WnVqfHMPf/TYIyBKPdlfPisC0
+AK4GyhgqXN2wxbcFRGwbNNQJ4/2iFPt3CKGHosNJbmY
+-> ssh-ed25519 At5Mag JJ8r/qD5i+LLAY7jnnHXAgykAuHtzxtGGzdqw7BAogY
+wotjW3yaTq1IdqVUwoCVwzglXsmnzniQIt7SDBrF4jY
+-> sPHo{W-grease , h6 =mEp^w `ccnF
+QQEb+Vh1+Fv++oPQwdTfOB2Cg5JaP4GCOq0o3J+xSqCY1gE0cguwLGXwa6+Tylu2
+Kuh4pMovAxnlHUt44u6f
+--- yWQyncCrxJzVHffFaFT704BEp8hjUn09a+23r4S39N4
+}»£Ο
οl
˜ΨI&έm_{ι½μ¨XΜΏ΄λ?f1M½¨Ι§|„ΖJ’‰ύ½ΧVΖLδ<Hυ5 ŒN›QφγΌζQVμΫ#>Ϋη§s2M°0 ’Iώ
\ No newline at end of file
diff --git a/users/tazjin/secrets/secrets.nix b/users/tazjin/secrets/secrets.nix
new file mode 100644
index 0000000000..12f12f721c
--- /dev/null
+++ b/users/tazjin/secrets/secrets.nix
@@ -0,0 +1,16 @@
+let
+  myKeys = import ../keys { };
+  allKeys = [
+    # local keys
+    myKeys.tverskoy_ed25519
+    myKeys.zamalek_ed25519
+    myKeys.khamovnik_agenix
+    # koptevo
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMw2ZfdNZCXCOtbQNT6hztXCIkTcO9MBrOuDqMlmGOYK root@koptevo"
+  ];
+in
+{
+  "geesefs-tazjins-files.age".publicKeys = allKeys;
+  "miniflux.age".publicKeys = allKeys;
+  "tgsa-yandex.age".publicKeys = allKeys;
+}
diff --git a/users/tazjin/secrets/tgsa-yandex.age b/users/tazjin/secrets/tgsa-yandex.age
new file mode 100644
index 0000000000..b1400d0673
--- /dev/null
+++ b/users/tazjin/secrets/tgsa-yandex.age
Binary files differdiff --git a/users/tazjin/tgsa/.gitignore b/users/tazjin/tgsa/.gitignore
new file mode 100644
index 0000000000..29e65519ba
--- /dev/null
+++ b/users/tazjin/tgsa/.gitignore
@@ -0,0 +1,3 @@
+result
+/target
+**/*.rs.bk
diff --git a/users/tazjin/tgsa/Cargo.lock b/users/tazjin/tgsa/Cargo.lock
new file mode 100644
index 0000000000..6be9c490d4
--- /dev/null
+++ b/users/tazjin/tgsa/Cargo.lock
@@ -0,0 +1,1567 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
+
+[[package]]
+name = "ascii"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
+
+[[package]]
+name = "autocfg"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "buf_redux"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
+dependencies = [
+ "memchr",
+ "safemem",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "cc"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "chunked_transfer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901"
+
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "crimp"
+version = "4087.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ead2c83f7d1f9b8e5a6f7a25985d0d1759ccd2cd72abb1eee2db65d05e12b39"
+dependencies = [
+ "curl",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "cssparser"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a"
+dependencies = [
+ "cssparser-macros",
+ "dtoa-short",
+ "itoa 0.4.8",
+ "matches",
+ "phf 0.8.0",
+ "proc-macro2",
+ "quote",
+ "smallvec",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "cssparser-macros"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
+dependencies = [
+ "quote",
+ "syn 2.0.57",
+]
+
+[[package]]
+name = "curl"
+version = "0.4.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "windows-sys",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.72+curl-8.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "windows-sys",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "dtoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
+
+[[package]]
+name = "dtoa-short"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74"
+dependencies = [
+ "dtoa",
+]
+
+[[package]]
+name = "ego-tree"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591"
+
+[[package]]
+name = "errno"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
+
+[[package]]
+name = "filetime"
+version = "0.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "windows-sys",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
+dependencies = [
+ "mac",
+ "new_debug_unreachable",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "html5ever"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
+dependencies = [
+ "log",
+ "mac",
+ "markup5ever",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "libz-sys"
+version = "1.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+
+[[package]]
+name = "markup5ever"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
+dependencies = [
+ "log",
+ "phf 0.10.1",
+ "phf_codegen 0.10.0",
+ "string_cache",
+ "string_cache_codegen",
+ "tendril",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "multipart"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182"
+dependencies = [
+ "buf_redux",
+ "httparse",
+ "log",
+ "mime",
+ "mime_guess",
+ "quick-error",
+ "rand 0.8.5",
+ "safemem",
+ "tempfile",
+ "twoway",
+]
+
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "nodrop"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "openssl"
+version = "0.10.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
+dependencies = [
+ "bitflags 2.5.0",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.57",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "phf"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+dependencies = [
+ "phf_macros",
+ "phf_shared 0.8.0",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "phf"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
+dependencies = [
+ "phf_shared 0.10.0",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+dependencies = [
+ "phf_generator 0.8.0",
+ "phf_shared 0.8.0",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
+dependencies = [
+ "phf_generator 0.10.0",
+ "phf_shared 0.10.0",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+dependencies = [
+ "phf_shared 0.8.0",
+ "rand 0.7.3",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
+dependencies = [
+ "phf_shared 0.10.0",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
+dependencies = [
+ "phf_generator 0.8.0",
+ "phf_shared 0.8.0",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.20+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc",
+ "rand_pcg",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.12",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "rouille"
+version = "3.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921"
+dependencies = [
+ "base64 0.13.1",
+ "chrono",
+ "filetime",
+ "multipart",
+ "percent-encoding",
+ "rand 0.8.5",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "sha1_smol",
+ "threadpool",
+ "time",
+ "tiny_http",
+ "url",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
+dependencies = [
+ "bitflags 2.5.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "safemem"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
+
+[[package]]
+name = "schannel"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "scraper"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5684396b456f3eb69ceeb34d1b5cb1a2f6acf7ca4452131efa3ba0ee2c2d0a70"
+dependencies = [
+ "cssparser",
+ "ego-tree",
+ "getopts",
+ "html5ever",
+ "matches",
+ "selectors",
+ "smallvec",
+ "tendril",
+]
+
+[[package]]
+name = "selectors"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe"
+dependencies = [
+ "bitflags 1.3.2",
+ "cssparser",
+ "derive_more",
+ "fxhash",
+ "log",
+ "matches",
+ "phf 0.8.0",
+ "phf_codegen 0.8.0",
+ "precomputed-hash",
+ "servo_arc",
+ "smallvec",
+ "thin-slice",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+
+[[package]]
+name = "serde"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.57",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.115"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
+dependencies = [
+ "itoa 1.0.11",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "servo_arc"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432"
+dependencies = [
+ "nodrop",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "socket2"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "string_cache"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
+dependencies = [
+ "new_debug_unreachable",
+ "once_cell",
+ "parking_lot",
+ "phf_shared 0.10.0",
+ "precomputed-hash",
+ "serde",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
+dependencies = [
+ "phf_generator 0.10.0",
+ "phf_shared 0.10.0",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "tendril"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
+dependencies = [
+ "futf",
+ "mac",
+ "utf-8",
+]
+
+[[package]]
+name = "tgsa"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "base64 0.21.7",
+ "crimp",
+ "ego-tree",
+ "lazy_static",
+ "openssl",
+ "ring",
+ "rouille",
+ "scraper",
+ "serde",
+ "serde_json",
+ "url",
+]
+
+[[package]]
+name = "thin-slice"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
+
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "time"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
+dependencies = [
+ "deranged",
+ "libc",
+ "num-conv",
+ "num_threads",
+ "powerfmt",
+ "serde",
+ "time-core",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "tiny_http"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
+dependencies = [
+ "ascii",
+ "chunked_transfer",
+ "httpdate",
+ "log",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "twoway"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "url"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.57",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.57",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.4",
+ "windows_aarch64_msvc 0.52.4",
+ "windows_i686_gnu 0.52.4",
+ "windows_i686_msvc 0.52.4",
+ "windows_x86_64_gnu 0.52.4",
+ "windows_x86_64_gnullvm 0.52.4",
+ "windows_x86_64_msvc 0.52.4",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
diff --git a/users/tazjin/tgsa/Cargo.toml b/users/tazjin/tgsa/Cargo.toml
new file mode 100644
index 0000000000..8764ef6524
--- /dev/null
+++ b/users/tazjin/tgsa/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "tgsa"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0"
+crimp = "4087.0"
+rouille = { version = "3.5", default-features = false }
+url = "2.3"
+scraper = "0.13"
+ego-tree = "0.6" # in tandem with 'scraper'
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+ring = "0.16.20"
+openssl = "0.10.54"
+base64 = "0.21.2"
+lazy_static = "1.4.0"
diff --git a/users/tazjin/tgsa/default.nix b/users/tazjin/tgsa/default.nix
new file mode 100644
index 0000000000..063781047a
--- /dev/null
+++ b/users/tazjin/tgsa/default.nix
@@ -0,0 +1,17 @@
+{ depot, pkgs, ... }:
+
+depot.third_party.naersk.buildPackage {
+  src = depot.nix.sparseTree {
+    root = ./.;
+    paths = [
+      ./Cargo.lock
+      ./Cargo.toml
+      ./src
+    ];
+  };
+
+  buildInputs = with pkgs; [
+    pkg-config
+    openssl
+  ];
+}
diff --git a/users/tazjin/tgsa/src/main.rs b/users/tazjin/tgsa/src/main.rs
new file mode 100644
index 0000000000..d9a5d4abc2
--- /dev/null
+++ b/users/tazjin/tgsa/src/main.rs
@@ -0,0 +1,403 @@
+use anyhow::{anyhow, Context, Result};
+use scraper::{Html, Selector};
+use std::collections::HashMap;
+use std::sync::RwLock;
+use std::time::{Duration, Instant};
+
+mod translate;
+
+#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+struct TgLink {
+    username: String,
+    message_id: usize,
+    translated: bool,
+}
+
+impl TgLink {
+    fn human_friendly_url(&self) -> String {
+        format!("t.me/{}/{}", self.username, self.message_id)
+    }
+
+    fn to_url(&self, embed: bool) -> String {
+        format!(
+            "https://t.me/{}/{}{}",
+            self.username,
+            self.message_id,
+            if embed { "?embed=1" } else { "" }
+        )
+    }
+
+    fn parse(url: &str, translated: bool) -> Option<Self> {
+        let url = url.strip_prefix('/')?;
+        let parsed = url::Url::parse(url).ok()?;
+
+        if parsed.host()? != url::Host::Domain("t.me") {
+            // only t.me links are supported
+            return None;
+        }
+
+        let parts = parsed.path_segments()?.collect::<Vec<&str>>();
+        if parts.len() != 2 {
+            // only message links are supported
+            return None;
+        }
+
+        Some(TgLink {
+            username: parts[0].into(),
+            message_id: parts[1].parse().ok()?,
+            translated,
+        })
+    }
+}
+
+fn fetch_post(link: &TgLink, embed: bool) -> Result<String> {
+    println!("fetching {}#{}", link.username, link.message_id);
+    let response = crimp::Request::get(&link.to_url(embed))
+        .send()
+        .context("failed to fetch embed data")?
+        .as_string()
+        .context("failed to decode embed data")?
+        .error_for_status(|resp| {
+            anyhow!("telegram request failed: {} ({})", resp.body, resp.status)
+        })?;
+
+    Ok(response.body)
+}
+
+// in some cases, posts can not be embedded, but telegram still
+// includes their content in metadata tags for content previews.
+//
+// we skip images in this case, as they are scaled down to thumbnail
+// size and not useful.
+fn fetch_fallback(link: &TgLink) -> Result<Option<String>> {
+    let post = fetch_post(link, false)?;
+    let doc = Html::parse_document(&post);
+    let desc_sel = Selector::parse("meta[property=\"og:description\"]").unwrap();
+    let desc_elem = match doc.select(&desc_sel).next() {
+        None => return Ok(None),
+        Some(elem) => elem,
+    };
+
+    let content = match desc_elem.value().attr("content") {
+        None => return Ok(None),
+        Some(content) => content.to_string(),
+    };
+
+    Ok(Some(content))
+}
+
+#[derive(Debug)]
+struct TgMessage {
+    author: String,
+    message: Option<String>,
+    photos: Vec<String>,
+    videos: Vec<String>,
+    has_audio: bool,
+}
+
+fn extract_photo_url(style: &str) -> Option<&str> {
+    let url_start = style.find("url('")? + 5;
+    let url_end = style.find("')")?;
+
+    Some(&style[url_start..url_end])
+}
+
+fn parse_tgmessage(embed: &str) -> Result<TgMessage> {
+    let doc = Html::parse_document(embed);
+
+    let author_sel = Selector::parse("a.tgme_widget_message_owner_name").unwrap();
+    let author = doc
+        .select(&author_sel)
+        .next()
+        .ok_or_else(|| anyhow!("failed to find message author"))?
+        .text()
+        .collect::<Vec<&str>>()
+        .concat();
+
+    let msg_sel = Selector::parse("div.tgme_widget_message_text.js-message_text").unwrap();
+
+    // The ElementRef::text() iterator does not yield newlines present
+    // in the message, so it is partially reimplemented here.
+    let message = if let Some(msg_elem) = doc.select(&msg_sel).next() {
+        use ego_tree::iter::Edge;
+        use scraper::node::Node;
+
+        let mut out = String::new();
+
+        for edge in &mut msg_elem.traverse() {
+            if let Edge::Open(node) = edge {
+                match node.value() {
+                    Node::Text(ref text) => out.push_str(text),
+                    Node::Element(elem) if elem.name() == "br" => out.push('\n'),
+                    _ => {}
+                }
+            }
+        }
+
+        Some(out)
+    } else {
+        // Not all Telegram messages have a textual message.
+        None
+    };
+
+    let photo_sel = Selector::parse("a.tgme_widget_message_photo_wrap").unwrap();
+    let mut photos = vec![];
+
+    for photo in doc.select(&photo_sel) {
+        if let Some(style) = photo.value().attr("style") {
+            if let Some(url) = extract_photo_url(style) {
+                photos.push(url.to_string())
+            }
+        }
+    }
+
+    let video_sel = Selector::parse("i.tgme_widget_message_video_thumb").unwrap();
+    let mut videos = vec![];
+
+    for video in doc.select(&video_sel) {
+        if let Some(style) = video.value().attr("style") {
+            if let Some(url) = extract_photo_url(style) {
+                videos.push(url.to_string())
+            }
+        }
+    }
+
+    let audio_sel = Selector::parse("audio.tgme_widget_message_voice.js-message_voice").unwrap();
+    let mut has_audio = false;
+    if doc.select(&audio_sel).next().is_some() {
+        has_audio = true;
+    }
+
+    Ok(TgMessage {
+        author,
+        message,
+        photos,
+        videos,
+        has_audio,
+    })
+}
+
+// create a permanent media url that tgsa can redirect if telegram
+// changes its upstream links.
+//
+// assumes that tgsa lives at tgsa.tazj.in (which it does)
+fn media_url(link: &TgLink, idx: usize) -> String {
+    format!(
+        "https://tgsa.tazj.in/img/{}/{}/{}",
+        link.username, link.message_id, idx
+    )
+}
+
+fn to_bbcode(link: &TgLink, msg: &TgMessage) -> String {
+    let mut out = String::new();
+
+    out.push_str(&format!("[quote=\"{}\"]\n", msg.author));
+
+    for video in 0..msg.videos.len() {
+        out.push_str(&format!("[url=\"{}\"]", link.to_url(true)));
+
+        // video thumbnail links are appended to the photos, hence the
+        // addition here
+        out.push_str(&format!(
+            "[img]{}[/img]",
+            media_url(link, video + msg.photos.len())
+        ));
+
+        out.push_str("[/url]\n");
+        out.push_str("[sub](Click thumbnail to open video)[/sub]\n")
+    }
+
+    for photo in 0..msg.photos.len() {
+        out.push_str(&format!("[timg]{}[/timg]\n", media_url(link, photo)));
+    }
+
+    if msg.has_audio {
+        out.push_str(&format!(
+            "[i]This message has audio attached. Go [url=\"{}\"]to Telegram[/url] to listen.[/i]",
+            link.to_url(true),
+        ));
+    }
+
+    if let Some(message) = &msg.message {
+        out.push_str(message);
+    }
+
+    out.push_str("\n[/quote]\n");
+
+    out.push_str(&format!(
+        "[sub](from [url=\"{}\"]{}[/url], via [url=\"https://tgsa.tazj.in\"]tgsa[/url])[/sub]\n",
+        link.to_url(true),
+        link.human_friendly_url(),
+    ));
+
+    out
+}
+
+// cache everything for one hour
+const CACHE_EXPIRY: Duration = Duration::from_secs(60 * 60);
+
+#[derive(Clone)]
+struct TgPost {
+    bbcode: String,
+    at: Instant,
+    media: Vec<String>,
+}
+
+type Cache = RwLock<HashMap<TgLink, TgPost>>;
+
+fn fetch_with_cache(cache: &Cache, link: &TgLink) -> Result<TgPost> {
+    if let Some(entry) = cache.read().unwrap().get(link) {
+        if Instant::now() - entry.at < CACHE_EXPIRY {
+            println!("serving {}#{} from cache", link.username, link.message_id);
+            return Ok(entry.clone());
+        }
+    }
+
+    // limit concurrent fetching
+    // TODO(tazjin): per link?
+    let mut writer = cache.write().unwrap();
+
+    let post = fetch_post(link, true)?;
+    let mut msg = parse_tgmessage(&post)?;
+
+    if msg.message.is_none() {
+        msg.message = fetch_fallback(link)?;
+    }
+
+    if let Some(message) = &msg.message {
+        if link.translated {
+            println!("translating {}#{}", link.username, link.message_id);
+            msg.message = Some(translate::fetch_translation(message)?);
+        }
+    }
+
+    let bbcode = to_bbcode(link, &msg);
+
+    let mut media = vec![];
+    media.append(&mut msg.photos);
+    media.append(&mut msg.videos);
+
+    let post = TgPost {
+        bbcode,
+        media,
+        at: Instant::now(),
+    };
+
+    writer.insert(link.clone(), post.clone());
+
+    Ok(post)
+}
+
+fn handle_img_redirect(cache: &Cache, img_path: &str) -> Result<rouille::Response> {
+    // img_path:
+    //
+    // RWApodcast/113/1
+    // ^          ^   ^
+    // |          |   |
+    // |          |   image (0-indexed)
+    // |          post ID
+    // username
+
+    let img_parts: Vec<&str> = img_path.split('/').collect();
+
+    if img_parts.len() != 3 {
+        println!("invalid image link: {}", img_path);
+        return Err(anyhow!("not a valid image link: {}", img_path));
+    }
+
+    let link = TgLink {
+        username: img_parts[0].into(),
+        message_id: img_parts[1].parse().context("failed to parse message_id")?,
+        translated: false,
+    };
+
+    let img_idx: usize = img_parts[2].parse().context("failed to parse img_idx")?;
+    let post = fetch_with_cache(cache, &link)?;
+
+    if img_idx >= post.media.len() {
+        return Err(anyhow!(
+            "there is no {}. image in {}/{}",
+            img_idx,
+            link.username,
+            link.message_id
+        ));
+    }
+
+    Ok(rouille::Response::redirect_303(post.media[img_idx].clone()))
+}
+
+fn handle_tg_link(cache: &Cache, link: &TgLink) -> Result<rouille::Response> {
+    let post = fetch_with_cache(cache, link)?;
+    Ok(rouille::Response::text(post.bbcode))
+}
+
+fn main() {
+    crimp::init();
+
+    let cache: Cache = RwLock::new(HashMap::new());
+
+    rouille::start_server("0.0.0.0:8472", move |request| {
+        let mut raw_url = request.raw_url();
+        let mut translate = false;
+
+        let response = loop {
+            if raw_url.starts_with("/img/") {
+                break handle_img_redirect(&cache, &raw_url[5..]);
+            }
+
+            if raw_url.starts_with("/translate/") {
+                translate = true;
+                raw_url = &raw_url[10..];
+            }
+
+            break match TgLink::parse(raw_url, translate) {
+                None => Ok(rouille::Response::text(
+                    r#"tgsa
+----
+
+this is a stupid program that lets you turn telegram message links
+into BBcode suitable for pasting on somethingawful dot com
+
+you can use it by putting a valid telegram message link in the url and
+waiting for some bbcode to show up.
+
+for example:
+
+  https://tgsa.tazj.in/https://t.me/RWApodcast/113
+
+yes, that looks stupid, but it works
+
+if you see this message and think you did the above correctly, you
+didn't. try again. idiot.
+
+it can also translate posts from russian, ukrainian or whatever other
+dumb language you speak into english by adding `/translate/`, for
+example:
+
+  https://tgsa.tazj.in/translate/https://t.me/strelkovii/4329
+
+expect this to be slow though. that's the price to pay for translating
+shitty slang.
+
+pm me on the forums if any of this makes you mad or something.
+"#,
+                )),
+                Some(link) => handle_tg_link(&cache, &link),
+            };
+        };
+
+        match response {
+            Ok(resp) => resp,
+            Err(err) => {
+                println!("something failed: {}", err);
+                rouille::Response::text(format!(
+                    r#"ugh, something broke: {}
+
+nobody has been alerted about this and it has probably not been
+logged. pm me on the forums if you think it's important."#,
+                    err
+                ))
+            }
+        }
+    });
+}
diff --git a/users/tazjin/tgsa/src/translate.rs b/users/tazjin/tgsa/src/translate.rs
new file mode 100644
index 0000000000..35d7b35ca8
--- /dev/null
+++ b/users/tazjin/tgsa/src/translate.rs
@@ -0,0 +1,191 @@
+//! integration with yandex cloud translate api, for automatically
+//! translating telegram posts.
+//!
+//! most of this module is concerned with handling the authentication
+//! tokens for yandex cloud, as jwt signing needs to be handled
+//! manually (none of the rust jwt libraries that i tried actually
+//! work).
+
+use anyhow::{anyhow, Context, Result};
+use base64::prelude::BASE64_URL_SAFE_NO_PAD as B64;
+use base64::Engine;
+use lazy_static::lazy_static;
+use ring::signature as sig;
+use serde::Deserialize;
+use serde_json::{json, Value};
+use std::sync::Mutex;
+use std::time::{Duration, SystemTime};
+
+/// token exchange url (exchanging a signed jwt for an iam token
+/// understood by the translation service)
+const TOKEN_URL: &str = "https://iam.api.cloud.yandex.net/iam/v1/tokens";
+
+/// translation endpoint
+const TRANSLATE_URL: &str = "https://translate.api.cloud.yandex.net/translate/v2/translate";
+
+/// describes the private key as downloaded from yandex, pem-encoded.
+#[derive(Deserialize)]
+struct AuthorizedKey {
+    id: String,
+    service_account_id: String,
+    private_key: String,
+}
+
+/// cached iam token for yandex cloud
+struct Token {
+    token: String,
+    expiry: SystemTime,
+}
+
+impl Token {
+    fn is_expired(&self) -> bool {
+        self.expiry < SystemTime::now()
+    }
+}
+
+lazy_static! {
+    static ref KEY_FILE: String =
+        std::env::var("YANDEX_KEY_FILE").expect("`YANDEX_KEY_FILE` variable should be set");
+    static ref CACHED_TOKEN: Mutex<Token> = {
+        let token = refresh_token().expect("fetching initial translation token must not fail");
+        Mutex::new(token)
+    };
+}
+
+/// wrap all the authentication logic below into a single function.
+fn refresh_token() -> Result<Token> {
+    let file = std::fs::File::open(KEY_FILE.as_str())?;
+    let key: AuthorizedKey = serde_json::from_reader(file)?;
+    let jwt = sign_yandex_jwt(&key)?;
+    let token = fetch_iam_token(&jwt)?;
+
+    Ok(Token {
+        token,
+        expiry: SystemTime::now() + Duration::from_secs(3600),
+    })
+}
+
+/// wrapper around the cached token that refreshes if required.
+fn current_token() -> Result<String> {
+    let mut token = CACHED_TOKEN
+        .lock()
+        .expect("thread operating on token should never fail");
+
+    if token.is_expired() {
+        println!("refreshing translation token");
+        *token = refresh_token().context("refreshing translation token")?;
+    }
+
+    Ok(token.token.clone())
+}
+
+/// use openssl to read the pem-encoded key, as ring itself is not
+/// capable of this.
+fn read_pem_key(key: &AuthorizedKey) -> Result<sig::RsaKeyPair> {
+    let rsa = openssl::rsa::Rsa::private_key_from_pem(key.private_key.as_bytes())
+        .context("parsing RSA key")?;
+
+    let der = rsa
+        .private_key_to_der()
+        .context("encoding key as DER for ring")?;
+
+    sig::RsaKeyPair::from_der(&der).map_err(|err| anyhow!("decoding DER key in ring: {}", err))
+}
+
+/// manually construct and sign the jwt required to perform the
+/// iam-token key exchange with yandex.
+fn sign_yandex_jwt(key: &AuthorizedKey) -> Result<String> {
+    let iat = SystemTime::now()
+        .duration_since(SystemTime::UNIX_EPOCH)?
+        .as_secs();
+
+    let header = json!({
+        "typ": "JWT",
+        "alg": "PS256",
+        "kid": key.id,
+    })
+    .to_string();
+
+    let payload = json!({
+        "iss": key.service_account_id,
+        "aud": TOKEN_URL,
+        "iat": iat,
+        "exp": iat + 60,
+    })
+    .to_string();
+
+    let unsigned = format!("{}.{}", B64.encode(header), B64.encode(payload));
+    let key_pair = read_pem_key(key)?;
+
+    let rng = ring::rand::SystemRandom::new();
+    let mut signature = vec![0; key_pair.public_modulus_len()];
+    key_pair
+        .sign(
+            &sig::RSA_PSS_SHA256,
+            &rng,
+            unsigned.as_bytes(),
+            &mut signature,
+        )
+        .map_err(|err| anyhow!("while signing JWT: {}", err))?;
+
+    Ok(format!("{}.{}", unsigned, B64.encode(&signature)))
+}
+
+/// exchange the jwt for an iam token
+fn fetch_iam_token(token: &str) -> Result<String> {
+    #[derive(Deserialize)]
+    #[serde(rename_all = "camelCase")]
+    struct TokenResponse {
+        iam_token: String,
+    }
+
+    let response = crimp::Request::post(TOKEN_URL)
+        .json(&json!({
+            "jwt": token,
+        }))?
+        .send()?
+        .error_for_status(|resp| {
+            anyhow::anyhow!("{} ({})", String::from_utf8_lossy(&resp.body), resp.status)
+        })?
+        .as_json::<TokenResponse>()
+        .context("deserialising IAM token")?;
+
+    Ok(response.body.iam_token)
+}
+
+pub fn fetch_translation(message: &str) -> Result<String> {
+    let request_body = json!({
+        "folderId": "b1g5k8f0tgimg06i6p5h",
+        "texts": [ message ],
+        "targetLanguageCode": "en",
+    });
+
+    let response = crimp::Request::post(TRANSLATE_URL)
+        .bearer_auth(&current_token()?)
+        .context("adding 'Bearer' token")?
+        .json(&request_body)
+        .context("preparing JSON body")?
+        .send()
+        .context("failed to fetch translation from yandex")?
+        .error_for_status(|resp| {
+            anyhow!(
+                "translation request failed: {} ({})",
+                String::from_utf8_lossy(&resp.body),
+                resp.status
+            )
+        })?
+        .as_json::<Value>()?
+        .body;
+
+    let translation = response
+        .get("translations")
+        .ok_or_else(|| anyhow!("missing 'translations' key"))?
+        .get(0)
+        .ok_or_else(|| anyhow!("translations list is empty"))?
+        .get("text")
+        .ok_or_else(|| anyhow!("translation missing 'text' key"))?
+        .as_str()
+        .ok_or_else(|| anyhow!("'text' was not a string"))?;
+
+    Ok(translation.to_string())
+}
diff --git a/users/tazjin/tvix-eval-value.d2 b/users/tazjin/tvix-eval-value.d2
new file mode 100644
index 0000000000..dad2dbcef2
--- /dev/null
+++ b/users/tazjin/tvix-eval-value.d2
@@ -0,0 +1,98 @@
+# D2 diagram of tvix-eval's `Value` type.
+#
+# can be rendered at https://play.d2lang.com/
+#
+# colours have meanings:
+#
+# yellow: recurses
+# orange: heap allocation
+# red: refcount
+#
+# this intentionally does *not* include some internal variants
+
+Value -> Null
+Value -> Bool
+Value -> Integer
+Value -> Float
+
+Box*.style.fill: "lightsalmon"
+Rc*.style.fill: "salmon"
+Vec\<*.style.fill: "salmon"
+
+Value -> String -> NixString -> "Box<str>"
+
+Value -> Path -> "Box<PathBuf>" -> PathBuf
+PathBuf.style.fill: "lightsalmon"
+
+# attribute sets are kinda complicated
+Value -> Attrs -> "Box<NixAttrs>" -> NixAttrs
+NixAttrs -> Empty
+NixAttrs -> KV
+KV.style.fill: "LemonChiffon"
+KV -> Value
+KV -> Value
+NixAttrs -> Map
+Map -> "OrdMap<NixString, Value>" -> "MapEntry<NixString, Value>"
+"OrdMap<NixString, Value>".style.fill: "lightsalmon"
+"MapEntry<NixString, Value>".style.fill: "salmon"
+"MapEntry<NixString, Value>".style.multiple: true
+"MapEntry<NixString, Value>" -> NixString
+"MapEntry<NixString, Value>" -> Value
+"MapEntry<NixString, Value>".style.stroke-width: 15
+"MapEntry<NixString, Value>".style.stroke: "lemonchiffon"
+
+Value -> List -> NixList -> "Rc<imbl::Vector<Value>>"
+"Rc<imbl::Vector<Value>>" -> "VecEntry<Value>" -> Value
+"VecEntry<Value>".style.multiple: true
+"VecEntry<Value>".style.fill: "salmon"
+"VecEntry<Value>".style.stroke-width: 15
+"VecEntry<Value>".style.stroke: "lemonchiffon"
+
+# closures
+
+Value -> Closure -> "Rc<Closure>" -> Closure
+Closure -> "Rc<Lambda>" -> Lambda
+
+Lambda -> Chunk
+Lambda -> SmolStr: sometimes allocates
+SmolStr.style.fill: "lightsalmon"
+Lambda -> usize
+Lambda -> "Option<Formals>" -> Formals
+
+Formals -> "HashMap<NixString, bool>" -> "MapEntry<NixString, bool>"
+"HashMap<NixString, bool>".style.fill: "lightsalmon"
+"MapEntry<NixString, bool>".style.fill: "salmon"
+"MapEntry<NixString, bool>".style.multiple: true
+"MapEntry<NixString, bool>" -> NixString
+
+Closure -> "Rc<Upvalues>" -> Upvalues
+
+Upvalues -> "Vec<Value>"
+"Vec<Value>" -> Value
+"Vec<Value>".style.stroke-width: 15
+"Vec<Value>".style.stroke: "lemonchiffon"
+Upvalues -> "Option<Vec<Value>>"
+"Option<Vec<Value>>" -> Value
+"Option<Vec<Value>>".style.fill: "lightsalmon"
+"Option<Vec<Value>>".style.stroke-width: 15
+"Option<Vec<Value>>".style.stroke: "lemonchiffon"
+
+Value -> Blueprint -> "Rc<Lambda>"
+
+# builtins
+
+Value -> Builtin -> "Box<BuiltinRepr>" -> BuiltinRepr
+BuiltinRepr -> "Rc<dyn BuiltinGen>"
+BuiltinRepr -> "Vec<Value>"
+
+# thunks
+
+Value -> Thunk -> "Rc<RefCell<ThunkRepr>>" -> ThunkRepr
+ThunkRepr -> Suspended
+Suspended -> "Rc<Lambda>"
+Suspended -> "Rc<Upvalues>"
+
+ThunkRepr -> Native -> "Box<dyn Fn() -> Result<Value, ErrorKind>>"
+ThunkRepr -> Blackhole
+ThunkRepr -> Evaluated -> Value
+Evaluated.style.fill: "lemonchiffon"
diff --git a/users/tazjin/yddns/.gitignore b/users/tazjin/yddns/.gitignore
new file mode 100644
index 0000000000..2f7896d1d1
--- /dev/null
+++ b/users/tazjin/yddns/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/tazjin/yddns/Cargo.lock b/users/tazjin/yddns/Cargo.lock
new file mode 100644
index 0000000000..58b37d553b
--- /dev/null
+++ b/users/tazjin/yddns/Cargo.lock
@@ -0,0 +1,1425 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "axum"
+version = "0.6.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bitflags 1.3.2",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "sync_wrapper",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "mime",
+ "rustversion",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cc"
+version = "1.0.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crimp"
+version = "4087.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ead2c83f7d1f9b8e5a6f7a25985d0d1759ccd2cd72abb1eee2db65d05e12b39"
+dependencies = [
+ "curl",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "curl"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2 0.4.10",
+ "winapi",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.68+curl-8.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "windows-sys",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "flate2"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
+
+[[package]]
+name = "futures-task"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
+
+[[package]]
+name = "futures-util"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
+name = "h2"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap 1.9.3",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "home"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "http"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f95b9abcae896730d42b78e09c155ed4ddf82c07b4de772c64aee5b2d8b7c150"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "0.14.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2 0.4.10",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
+dependencies = [
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+ "tokio-io-timeout",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.2",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+
+[[package]]
+name = "libz-sys"
+version = "1.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "multimap"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+
+[[package]]
+name = "object"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "petgraph"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
+dependencies = [
+ "fixedbitset",
+ "indexmap 2.1.0",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "prettyplease"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+dependencies = [
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prost"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
+dependencies = [
+ "bytes",
+ "heck",
+ "itertools",
+ "lazy_static",
+ "log",
+ "multimap",
+ "petgraph",
+ "prettyplease",
+ "prost",
+ "prost-types",
+ "regex",
+ "syn 1.0.109",
+ "tempfile",
+ "which",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
+dependencies = [
+ "prost",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "ring"
+version = "0.17.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
+dependencies = [
+ "cc",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustix"
+version = "0.38.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+dependencies = [
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustls"
+version = "0.21.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-webpki",
+ "sct",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
+dependencies = [
+ "base64",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.101.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "sct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "tempfile"
+version = "3.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio"
+version = "1.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2 0.5.5",
+ "tokio-macros",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-io-timeout"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
+dependencies = [
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tonic"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
+dependencies = [
+ "async-stream",
+ "async-trait",
+ "axum",
+ "base64",
+ "bytes",
+ "flate2",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-timeout",
+ "percent-encoding",
+ "pin-project",
+ "prost",
+ "rustls-native-certs",
+ "rustls-pemfile",
+ "tokio",
+ "tokio-rustls",
+ "tokio-stream",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tonic-build"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07"
+dependencies = [
+ "prettyplease",
+ "proc-macro2",
+ "prost-build",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "indexmap 1.9.3",
+ "pin-project",
+ "pin-project-lite",
+ "rand",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "walkdir"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "yandex-cloud"
+version = "2023.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98f67140264e8e090e26b70925096adf295569c057a8b2ad2cd7e0f10c01cfae"
+dependencies = [
+ "prost",
+ "prost-types",
+ "tonic",
+ "tonic-build",
+ "walkdir",
+]
+
+[[package]]
+name = "yddns"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "crimp",
+ "tokio",
+ "yandex-cloud",
+]
diff --git a/users/tazjin/yddns/Cargo.toml b/users/tazjin/yddns/Cargo.toml
new file mode 100644
index 0000000000..78691f303d
--- /dev/null
+++ b/users/tazjin/yddns/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "yddns"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0"
+crimp = "4087.0"
+tokio = "*" # pulled in by yandex-cloud
+yandex-cloud = "2023.6.13"
diff --git a/users/tazjin/yddns/default.nix b/users/tazjin/yddns/default.nix
new file mode 100644
index 0000000000..40b0d1c24e
--- /dev/null
+++ b/users/tazjin/yddns/default.nix
@@ -0,0 +1,9 @@
+{ depot, pkgs, ... }:
+
+depot.third_party.naersk.buildPackage {
+  src = ./.;
+  buildInputs = with pkgs; [
+    pkg-config
+    openssl
+  ];
+}
diff --git a/users/tazjin/yddns/src/main.rs b/users/tazjin/yddns/src/main.rs
new file mode 100644
index 0000000000..2e2f9fe02f
--- /dev/null
+++ b/users/tazjin/yddns/src/main.rs
@@ -0,0 +1,142 @@
+use anyhow::{anyhow, bail, Context, Result};
+use crimp::Request;
+use std::env;
+use std::net::Ipv4Addr;
+use tokio::runtime;
+use yandex_cloud::tonic_exports::{Channel, Endpoint, InterceptedService};
+use yandex_cloud::yandex::cloud::dns::v1 as dns;
+use yandex_cloud::yandex::cloud::dns::v1::dns_zone_service_client::DnsZoneServiceClient;
+use yandex_cloud::{AuthInterceptor, TokenProvider};
+
+type DnsClient<T> = DnsZoneServiceClient<InterceptedService<Channel, AuthInterceptor<T>>>;
+
+/// Fetch the current IP from the given URL. It should be the URL of a
+/// site that responds only with the IP in plain text, and nothing else.
+fn get_current_ip(source: &str) -> Result<Ipv4Addr> {
+    let response = Request::get(source)
+        .send()
+        .context("failed to fetch current IP")?
+        .error_for_status(|resp| anyhow!("error response ({})", resp.status))
+        .context("received error response for IP")?
+        .as_string()?
+        .body;
+
+    Ok(response.trim().parse().with_context(|| {
+        format!(
+            "failed to parse IP address from response body: {}",
+            response
+        )
+    })?)
+}
+
+/// Fetch the current address of the target record.
+async fn fetch_current_record_addr<T: TokenProvider>(
+    client: &mut DnsClient<T>,
+    zone_id: &str,
+    record_name: &str,
+) -> Result<Ipv4Addr> {
+    let req = dns::GetDnsZoneRecordSetRequest {
+        dns_zone_id: zone_id.into(),
+        name: record_name.into(),
+        r#type: "A".into(),
+    };
+
+    let response = client
+        .get_record_set(req)
+        .await
+        .context("failed to fetch current record set")?
+        .into_inner();
+
+    if response.data.len() != 1 {
+        bail!(
+            "expected exactly one record for 'A {}', but found {}",
+            record_name,
+            response.data.len()
+        );
+    }
+
+    Ok(response.data[0]
+        .parse()
+        .context("failed to parse returned record")?)
+}
+
+/// Update the record with the new address, if required.
+async fn update_record<T: TokenProvider>(
+    client: &mut DnsClient<T>,
+    zone_id: &str,
+    record_name: &str,
+    new_address: Ipv4Addr,
+) -> Result<()> {
+    let request = dns::UpsertRecordSetsRequest {
+        dns_zone_id: zone_id.into(),
+        replacements: vec![dns::RecordSet {
+            name: record_name.into(),
+            r#type: "A".into(),
+            ttl: 3600, // 1 hour
+            data: vec![new_address.to_string()],
+        }],
+        ..Default::default()
+    };
+
+    client
+        .upsert_record_sets(request)
+        .await
+        .context("failed to update record")?;
+
+    Ok(())
+}
+
+/// Compare the record with the expected value, and issue an update if
+/// necessary.
+async fn compare_update_record<T: TokenProvider>(
+    client: &mut DnsClient<T>,
+    zone_id: &str,
+    record_name: &str,
+    new_ip: Ipv4Addr,
+) -> Result<()> {
+    let old_ip = fetch_current_record_addr(client, zone_id, record_name).await?;
+
+    if old_ip == new_ip {
+        println!("IP address unchanged ({})", old_ip);
+        return Ok(());
+    }
+
+    println!(
+        "IP address changed: current record points to {}, but address is {}",
+        old_ip, new_ip
+    );
+
+    update_record(client, zone_id, record_name, new_ip).await?;
+    println!("successfully updated '{}' to 'A {}'", record_name, new_ip);
+
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    let token =
+        env::var("YANDEX_CLOUD_TOKEN").context("Yandex Cloud authentication token unset")?;
+    let target_zone_id =
+        env::var("TARGET_ZONE").unwrap_or_else(|_| "dnsd0tif5mokfu0mg8i5".to_string());
+    let target_record = env::var("TARGET_RECORD").unwrap_or_else(|_| "khtrsk".to_string());
+
+    let current_ip = get_current_ip("http://ifconfig.me")?;
+    println!("current IP address is '{}'", current_ip);
+
+    let rt = runtime::Builder::new_current_thread()
+        .enable_time()
+        .enable_io()
+        .build()?;
+
+    rt.block_on(async move {
+        let channel = Endpoint::from_static("https://dns.api.cloud.yandex.net")
+            .connect()
+            .await?;
+
+        let mut client =
+            DnsZoneServiceClient::with_interceptor(channel, AuthInterceptor::new(token));
+
+        compare_update_record(&mut client, &target_zone_id, &target_record, current_ip).await
+    })?;
+
+    Ok(())
+}
diff --git a/users/wpcarro/.envrc b/users/wpcarro/.envrc
index b23a41fbd7..3e1e1a35f0 100644
--- a/users/wpcarro/.envrc
+++ b/users/wpcarro/.envrc
@@ -1,3 +1,5 @@
 source_up
+
 export PATH="${PWD}/bin:${PATH}"
+export REPO_ROOT=$(git rev-parse --show-toplevel)
 export WPCARRO="${REPO_ROOT}/users/wpcarro"
diff --git a/users/wpcarro/.gitignore b/users/wpcarro/.gitignore
index 2380eb7b66..64703ed129 100644
--- a/users/wpcarro/.gitignore
+++ b/users/wpcarro/.gitignore
@@ -30,3 +30,6 @@ node_modules/
 .gitsecret/keys/random_seed
 !*.secret
 secrets.json
+
+# Nix gcroots symlinks created by .envrc
+/.gcroots/*
diff --git a/users/wpcarro/Makefile b/users/wpcarro/Makefile
deleted file mode 100644
index 4e3361b800..0000000000
--- a/users/wpcarro/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-install-cli-tools:
-	nix-env -f "${BRIEFCASE}" -iA shared.cliTools
-
-install-configs:
-	nix-build -A configs.install && \
-	./result && \
-	rm ./result
-
-uninstall-configs:
-	nix-build -A configs.uninstall && \
-	./result && \
-	rm ./result
-
-list-broken-links:
-	nix-build -A tools.symlinkManager && \
-	./result/bin/symlink-mgr -audit && \
-	rm ./result
diff --git a/users/wpcarro/OWNERS b/users/wpcarro/OWNERS
index 4dbd390b67..ac1283f56d 100644
--- a/users/wpcarro/OWNERS
+++ b/users/wpcarro/OWNERS
@@ -1,3 +1 @@
-inherited: false
-owners:
-  - wpcarro
+wpcarro
diff --git a/users/wpcarro/README.md b/users/wpcarro/README.md
index fdc885be20..be0aacf247 100644
--- a/users/wpcarro/README.md
+++ b/users/wpcarro/README.md
@@ -36,11 +36,12 @@ Below I have outlined a few projects that you might find interesting.
 
 ### NixOS Machine
 
-- Ensure Nix is installed.
-- Ensure direnv is installed.
-- Ensure `~/.password-store` exists.
-- Transfer GPG stuffs:
-  - old computer: `configs/.gnupg/export.sh $outdir`
-  - new computer: `configs/.gnupg/import.sh <path-to-zip>`
-  from the new machine.
-- Consult `Makefile`.
+```shell
+$ nix-shell -p nixos.{git,direnv}
+$ git clone https://code.tvl.fyi/depot.git /depot
+$ cd /depot
+$ eval "$(direnv hook bash)"
+$ HOSTNAME=base rebuild-system
+$ sudo tailscale up
+$ git clone 'user@host:~/.passage' ~/.passage
+```
diff --git a/users/wpcarro/assessments/brilliant/default.nix b/users/wpcarro/assessments/brilliant/default.nix
index 536e54d365..0628679c01 100644
--- a/users/wpcarro/assessments/brilliant/default.nix
+++ b/users/wpcarro/assessments/brilliant/default.nix
@@ -12,5 +12,5 @@ depot.users.wpcarro.buildHaskell.program {
     split
     rio
   ];
-  ghcExtensions = [];
+  ghcExtensions = [ ];
 }
diff --git a/users/wpcarro/bin/__dispatch.sh b/users/wpcarro/bin/__dispatch.sh
deleted file mode 100755
index 17556ad2e0..0000000000
--- a/users/wpcarro/bin/__dispatch.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-# This script dispatches invocations transparently to programs instantiated from
-# Nix.
-#
-# To add a new tool, insert it into the case statement below by setting `attr`
-# to the key in nixpkgs which represents the program you want to run.
-set -ueo pipefail
-
-TARGET_TOOL=$(basename "$0")
-
-case "${TARGET_TOOL}" in
-  deploy-diogenes)
-    attr="users.wpcarro.nixos.deploy-diogenes"
-    ;;
-  import-gpg)
-    attr="users.wpcarro.configs.import-gpg"
-    ;;
-  export-gpg)
-    attr="users.wpcarro.configs.export-gpg"
-    ;;
-  *)
-    echo "The tool '${TARGET_TOOL}' is currently not installed in this repository."
-    exit 1
-    ;;
-esac
-
-result=$(nix-build --no-out-link --attr "${attr}" "${REPO_ROOT}")
-PATH="${result}/bin:$PATH"
-
-exec "${TARGET_TOOL}" "${@}"
diff --git a/users/wpcarro/bin/deploy-diogenes b/users/wpcarro/bin/deploy-diogenes
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/users/wpcarro/bin/deploy-diogenes
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/users/wpcarro/bin/export-gpg b/users/wpcarro/bin/export-gpg
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/users/wpcarro/bin/export-gpg
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/users/wpcarro/bin/import-gpg b/users/wpcarro/bin/import-gpg
deleted file mode 120000
index 8390ec9c96..0000000000
--- a/users/wpcarro/bin/import-gpg
+++ /dev/null
@@ -1 +0,0 @@
-__dispatch.sh
\ No newline at end of file
diff --git a/users/wpcarro/buildHaskell/default.nix b/users/wpcarro/buildHaskell/default.nix
index 5958b1ea26..2f0fd9e1c2 100644
--- a/users/wpcarro/buildHaskell/default.nix
+++ b/users/wpcarro/buildHaskell/default.nix
@@ -2,11 +2,13 @@
 
 {
   # Create a nix-shell for Haskell development.
-  shell = { deps }: let
-    ghc = pkgs.haskellPackages.ghcWithPackages (hpkgs: deps hpkgs);
-  in pkgs.mkShell {
-    buildInputs = [ghc];
-  };
+  shell = { deps }:
+    let
+      ghc = pkgs.haskellPackages.ghcWithPackages (hpkgs: deps hpkgs);
+    in
+    pkgs.mkShell {
+      buildInputs = [ ghc ];
+    };
 
   # Build a Haskell executable. This assumes a project directory with a
   # top-level Main.hs.
@@ -15,17 +17,19 @@
   # - `deps`: A function that accepts `hpkgs` and returns a list of Haskell
   # - `ghcExtensions`: A list of strings representing the language extensions to
   #   use.
-  program = { name, srcs, deps, ghcExtensions }: let
-    ghc = pkgs.haskellPackages.ghcWithPackages (hpkgs: deps hpkgs);
-  in pkgs.stdenv.mkDerivation {
-    name = name;
-    buildInputs = [];
-    srcs = srcs;
-    buildPhase = ''
-      ${ghc}/bin/ghc -Wall Main.hs ${pkgs.lib.concatMapStrings (x: "-X${x} ") ghcExtensions}
-    '';
-    installPhase = ''
-      mkdir -p $out && mv Main $out/${name}
-    '';
-  };
+  program = { name, srcs, deps, ghcExtensions }:
+    let
+      ghc = pkgs.haskellPackages.ghcWithPackages (hpkgs: deps hpkgs);
+    in
+    pkgs.stdenv.mkDerivation {
+      name = name;
+      buildInputs = [ ];
+      srcs = srcs;
+      buildPhase = ''
+        ${ghc}/bin/ghc -Wall Main.hs ${pkgs.lib.concatMapStrings (x: "-X${x} ") ghcExtensions}
+      '';
+      installPhase = ''
+        mkdir -p $out && mv Main $out/${name}
+      '';
+    };
 }
diff --git a/users/wpcarro/ci/pipelines/post-receive.nix b/users/wpcarro/ci/pipelines/post-receive.nix
index b69ee28af3..09b8990e13 100644
--- a/users/wpcarro/ci/pipelines/post-receive.nix
+++ b/users/wpcarro/ci/pipelines/post-receive.nix
@@ -2,7 +2,6 @@
 
 let
   inherit (builtins) path toJSON;
-  inherit (depot.users.wpcarro.emacs) initEl runScript;
 
   pipeline.steps = [
     {
@@ -10,19 +9,6 @@ let
       command = "${pkgs.git-secrets}/bin/git-secrets --scan-history";
       label = ":broom: lint secrets";
     }
-    {
-      key = "init-emacs";
-      command = let
-        scriptEl = path {
-          path = ./script.el;
-          name = "script.el";
-        };
-        runScriptEl = runScript {
-          script = scriptEl;
-        };
-      in "${runScriptEl} ${initEl}";
-      label = ":gnu: initialize Emacs";
-      depends_on = "lint-secrets";
-    }
   ];
-in pkgs.writeText "pipeline.yaml" (toJSON pipeline)
+in
+pkgs.writeText "pipeline.yaml" (toJSON pipeline)
diff --git a/users/wpcarro/ci/pipelines/script.el b/users/wpcarro/ci/pipelines/script.el
deleted file mode 100644
index da079b64ba..0000000000
--- a/users/wpcarro/ci/pipelines/script.el
+++ /dev/null
@@ -1,44 +0,0 @@
-;; This script initializes Emacs and exits with either a zero or non-zero status
-;; depending on whether or not Emacs initialized without logging warnings or
-;; encountering errors.
-;;
-;; This script reads the location of init.el as the last argument in `argv'.
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'prelude)
-(require 'f)
-(require 'dash)
-(require 'buffer)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Script
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defvar init-el-path (-last-item argv)
-  "Path to the init.el file that this script attempts to load.")
-
-(prelude-assert (f-exists? init-el-path))
-
-(condition-case err
-    (load init-el-path)
-  (error
-   (message "Encountered an error while attempting to load init.el: %s" err)
-   (kill-emacs 1)))
-
-(when (buffer-exists? "*Errors*")
-  (progn
-    (with-current-buffer "*Errors*"
-      (message "Encountered errors in *Errors* buffer: %s" (buffer-string)))
-    (kill-emacs 1)))
-
-(when (buffer-exists? "*Warnings*")
-  (progn
-    (with-current-buffer "*Warnings*"
-      (message "Encountered warnings in *Warnings* buffer: %s" (buffer-string)))
-    (kill-emacs 1)))
-
-(message "Successfully init'd Emacs without encountering errors or warnings!")
-(kill-emacs 0)
diff --git a/users/wpcarro/clients/monsterpoker/default.nix b/users/wpcarro/clients/monsterpoker/default.nix
deleted file mode 100644
index 0e079261fb..0000000000
--- a/users/wpcarro/clients/monsterpoker/default.nix
+++ /dev/null
@@ -1,6 +0,0 @@
-{ depot, pkgs, ... }:
-
-pkgs.runCommandNoCC "monsterpoker.app" {} ''
-  mkdir -p $out
-  cp ${./index.html} $out/index.html
-''
diff --git a/users/wpcarro/clients/monsterpoker/index.html b/users/wpcarro/clients/monsterpoker/index.html
deleted file mode 100644
index 100108c3f2..0000000000
--- a/users/wpcarro/clients/monsterpoker/index.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <title>Buy Monster Poker</title>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <script src="https://cdn.tailwindcss.com"></script>
-    <script>
-      tailwind.config = {
-        theme: {
-          extend: {},
-        },
-      };
-    </script>
-  </head>
-  <body class="bg-cyan-900 container mx-auto">
-    <h1 class="text-cyan-300 text-6xl text-center pt-10">
-      <a class="hover:underline"
-         href="https://monsterpoker.myshopify.com/products/monster-poker-republic">
-        Buy Monster Poker <b>today</b>!
-      </a>
-    </h1>
-  </body>
-</html>
diff --git a/users/wpcarro/common.nix b/users/wpcarro/common.nix
index 829c3394d1..582b63fc4c 100644
--- a/users/wpcarro/common.nix
+++ b/users/wpcarro/common.nix
@@ -2,10 +2,13 @@
 
 let
   inherit (depot.users) wpcarro;
-in {
+in
+{
   programs = {
     fish.enable = true;
 
+    gnupg.agent.enable = true;
+
     ssh = {
       startAgent = true;
       extraConfig = ''
@@ -23,7 +26,11 @@ in {
   };
 
   services = {
-    locate.enable = true;
+    # Remodel the system clipboard as a list instead of a point.
+    clipmenu.enable = true;
+
+    # TODO(wpcarro): broken in nixpkgs as of 2023-10-04
+    locate.enable = false;
 
     depot.automatic-gc = {
       enable = true;
@@ -37,19 +44,22 @@ in {
   # Command-line tools I commonly used and want available on most (or all) of my
   # machines.
   shell-utils = with pkgs; [
+    age
     bat
-    dig
+    coreutils
     direnv
     diskus
     emacs
-    exa
     fd
     fzf
     git
+    gnupg
+    htop
     jq
-    mkpasswd
     nmap
+    passage
     python3
+    rink
     ripgrep
     tldr
     tokei
@@ -59,5 +69,15 @@ in {
     # TODO(wpcarro): Debug this failing build.
     # wpcarro.tools.simple_vim
     xclip
-  ];
+    zip
+  ] ++
+  (if pkgs.stdenv.isLinux then [
+    mkpasswd
+    sysz
+    # This depends on compiler-rt-libc-10.0.1, which is marked as broken on
+    # aarch64-darwin, but depot sets `allowBroken = true`, which means any
+    # build that depends on dig will fail on OSX (e.g. emacs).
+    # https://cs.tvl.fyi/github.com/NixOS/nixpkgs@e9b195248c6cd7961a453b10294aea9ab58e01b4/-/blob/pkgs/development/compilers/llvm/10/compiler-rt/default.nix?L122
+    dig
+  ] else [ ]);
 }
diff --git a/users/wpcarro/configs/.gitconfig b/users/wpcarro/configs/.gitconfig
deleted file mode 100644
index a036b13081..0000000000
--- a/users/wpcarro/configs/.gitconfig
+++ /dev/null
@@ -1,3 +0,0 @@
-[user]
-	name = "William Carroll"
-	email = "wpcarro@gmail.com"
diff --git a/users/wpcarro/configs/.gnupg/export.sh b/users/wpcarro/configs/.gnupg/export.sh
deleted file mode 100755
index 31def2beb1..0000000000
--- a/users/wpcarro/configs/.gnupg/export.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env bash
-
-set -euo pipefail
-
-# Run this script to export all the information required to transport your GPG
-# information.
-# Usage: ./export.sh
-# TODO: run this periodically as a job.
-
-output="$(pwd)/export.zip"
-destination="$(mktemp -d)"
-
-function cleanup() {
-  rm -rf "${destination}"
-}
-trap cleanup EXIT
-
-gpg --armor --export >"${destination}/public.asc"
-gpg --armor --export-secret-keys >"${destination}/secret.asc"
-gpg --armor --export-ownertrust >"${destination}/ownertrust.txt"
-
-# Strangely enough this appears to be the only way to create a zip of a
-# directory that doesn't contain the (noisy) full paths of each item from the
-# source filesystem. (i.e. -j doesn't cooperate with -r)
-pushd "${destination}"
-zip -r "${output}" ./*
-popd
-
-echo "$(realpath ${output})"
diff --git a/users/wpcarro/configs/.gnupg/import.sh b/users/wpcarro/configs/.gnupg/import.sh
deleted file mode 100755
index bb449267ce..0000000000
--- a/users/wpcarro/configs/.gnupg/import.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-
-set -euo pipefail
-
-# Run this script to import all of the information exported by `export.sh`.
-# Usage: ./import.sh path/to/export.zip
-
-if [ -z "${1+x}" ]; then
-  echo "You must specify the path to export.zip. Exiting..."
-  exit 1
-fi
-
-destination="$(mktemp -d)"
-
-function cleanup() {
-  rm -rf "${destination}"
-}
-trap cleanup EXIT
-
-unzip "${1}" -d "${destination}" >/dev/null
-
-gpg --import "${destination}/public.asc"
-gpg --import "${destination}/secret.asc"
-gpg --import-ownertrust "${destination}/ownertrust.txt"
-
-# Run this at the end to output some verification
-gpg --list-keys
-gpg --list-secret-keys
diff --git a/users/wpcarro/configs/default.nix b/users/wpcarro/configs/default.nix
index 81ba5b4d48..681f976052 100644
--- a/users/wpcarro/configs/default.nix
+++ b/users/wpcarro/configs/default.nix
@@ -3,7 +3,8 @@
 let
   inherit (pkgs) writeShellScript;
   inherit (pkgs.lib.strings) makeBinPath;
-in {
+in
+{
   install = writeShellScript "install-configs" ''
     cd "$WPCARRO/configs" && ${pkgs.stow}/bin/stow --target="$HOME" .
   '';
diff --git a/users/wpcarro/dotfiles/config.fish b/users/wpcarro/dotfiles/config.fish
index 2e2006e9ca..3be8fefd9c 100644
--- a/users/wpcarro/dotfiles/config.fish
+++ b/users/wpcarro/dotfiles/config.fish
@@ -7,17 +7,27 @@ alias tpr 'tput reset'
 alias ls 'exa --sort=type'
 alias ll 'exa --long --sort=type'
 alias la 'exa --long --all --sort=type'
+alias gcan 'git commit --amend --no-edit'
+alias gco 'git checkout'
 alias gd 'git diff'
+alias gds 'git diff --staged'
 alias glp 'git log --pretty --oneline --graph'
-alias gpf 'git push --force-with-lease'
+alias gpf 'git push --force'
 alias gsh 'git show HEAD'
 alias gst 'git status'
+alias gprom 'git pull --rebase origin main'
+alias gfom 'git fetch origin main'
+alias grh 'git reset --hard'
+alias gproc 'git pull --rebase origin canon'
 alias edit 'emacsclient -n'
-# fs navigation
+alias h 'cd /hadrian'
 alias d 'cd /depot'
-alias w 'cd /depot/users/wpcarro'
-# This allows me to call rebuild-system from any directory.
-alias rebuild-system 'sudo /depot/bin/rebuild-system'
+alias hw 'cd /hadrian/users/wpcarro'
+alias dw 'cd /depot/users/wpcarro'
+alias sc 'systemctl'
+alias ef 'edit ~/.config/fish/config.fish'
+alias sf 'source ~/.config/fish/config.fish'
+alias tf 'terraform'
 
 # environment variables
 set -gx EDITOR "emacsclient"
@@ -25,7 +35,7 @@ set -gx ALTERNATE_EDITOR "emacs -q -nw"
 set -gx VISUAL "emacsclient"
 
 # Use my custom fish prompt
-source ~/.config/fish/prompt.fish
+source /depot/users/wpcarro/dotfiles/prompt.fish
 
 # Configure fuzzy history, file, directory searching
 source (fzf-share)/key-bindings.fish && fzf_key_bindings
diff --git a/users/wpcarro/dotfiles/gitconfig b/users/wpcarro/dotfiles/gitconfig
new file mode 100644
index 0000000000..f81c0c40f7
--- /dev/null
+++ b/users/wpcarro/dotfiles/gitconfig
@@ -0,0 +1,9 @@
+[user]
+	name = "William Carroll"
+	email = "wpcarro@gmail.com"
+[diff]
+  external = difft
+[push]
+	default = current
+[rebase]
+	autoStash = true
diff --git a/users/wpcarro/configs/.config/fish/prompt.fish b/users/wpcarro/dotfiles/prompt.fish
index 58d22dab5e..58d22dab5e 100644
--- a/users/wpcarro/configs/.config/fish/prompt.fish
+++ b/users/wpcarro/dotfiles/prompt.fish
diff --git a/users/wpcarro/emacs/.emacs.d/init.el b/users/wpcarro/emacs/.emacs.d/init.el
index a87be333c7..5db74d36c7 100644
--- a/users/wpcarro/emacs/.emacs.d/init.el
+++ b/users/wpcarro/emacs/.emacs.d/init.el
@@ -4,7 +4,6 @@
 (require 'wpc-misc)
 (require 'ssh)
 (require 'keyboard)
-(require 'irc)
 (require 'email)
 (require 'keybindings)
 (require 'window-manager)
@@ -13,10 +12,4 @@
 (require 'wpc-org)
 (require 'wpc-company)
 (require 'wpc-shell)
-(require 'wpc-lisp)
-(require 'wpc-haskell)
-(require 'wpc-elixir)
-(require 'wpc-nix)
-(require 'wpc-rust)
-(require 'wpc-clojure)
-(require 'wpc-prolog)
+(require 'wpc-language-support)
diff --git a/users/wpcarro/emacs/.emacs.d/vendor/dired+.el b/users/wpcarro/emacs/.emacs.d/vendor/dired+.el
deleted file mode 100644
index 2403b0af9c..0000000000
--- a/users/wpcarro/emacs/.emacs.d/vendor/dired+.el
+++ /dev/null
@@ -1,13696 +0,0 @@
-;;; dired+.el --- Extensions to Dired.
-;;
-;; Filename: dired+.el
-;; Description: Extensions to Dired.
-;; Author: Drew Adams
-;; Maintainer: Drew Adams (concat "drew.adams" "@" "oracle" ".com")
-;; Copyright (C) 1999-2019, Drew Adams, all rights reserved.
-;; Created: Fri Mar 19 15:58:58 1999
-;; Version: 2019.04.21
-;; Package-Requires: ()
-;; Last-Updated: Sun Jul 21 09:47:33 2019 (-0700)
-;;           By: dradams
-;;     Update #: 11727
-;; URL: https://www.emacswiki.org/emacs/download/dired%2b.el
-;; Doc URL: https://www.emacswiki.org/emacs/DiredPlus
-;; Keywords: unix, mouse, directories, diredp, dired
-;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x, 24.x, 25.x, 26.x
-;;
-;; Features that might be required by this library:
-;;
-;;   `apropos', `apropos+', `autofit-frame', `avoid', `backquote',
-;;   `bookmark', `bookmark+', `bookmark+-1', `bookmark+-bmu',
-;;   `bookmark+-key', `bookmark+-lit', `button', `bytecomp', `cconv',
-;;   `cl', `cl-lib', `cmds-menu', `col-highlight', `crosshairs',
-;;   `dired', `dired+', `dired-aux', `dired-loaddefs', `dired-x',
-;;   `easymenu', `fit-frame', `font-lock', `font-lock+',
-;;   `format-spec', `frame-fns', `gv', `help+', `help-fns',
-;;   `help-fns+', `help-macro', `help-macro+', `help-mode',
-;;   `highlight', `hl-line', `hl-line+', `image', `image-dired',
-;;   `image-file', `image-mode', `info', `info+', `kmacro',
-;;   `macroexp', `menu-bar', `menu-bar+', `misc-cmds', `misc-fns',
-;;   `naked', `pp', `pp+', `radix-tree', `replace', `second-sel',
-;;   `strings', `syntax', `text-mode', `thingatpt', `thingatpt+',
-;;   `vline', `w32-browser', `w32browser-dlgopen', `wid-edit',
-;;   `wid-edit+', `widget'.
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;;; Commentary:
-;;
-;;    Extensions to Dired.
-;;
-;;  This file extends functionalities provided by standard GNU Emacs
-;;  files `dired.el', `dired-aux.el', and `dired-x.el'.
-;;
-;;  Key bindings changed.  Menus redefined.  `diredp-mouse-3-menu'
-;;  popup menu added.  New commands.  Some commands enhanced.
-;;
-;;  All of the new functions, variables, and faces defined here have
-;;  the prefix `diredp-' (for Dired Plus) in their names.
-;;
-;;
-;;  Wraparound Navigation
-;;  ---------------------
-;;
-;;  In vanilla Dired, `dired-next-marked-file' (`M-}' or `* C-n') and
-;;  `dired-previous-marked-file' (`M-{' or `* C-p') wrap around when
-;;  you get to the end or the beginning of the Dired buffer.  Handy.
-;;
-;;  But the other navigation commands do not wrap around.  In `Dired+'
-;;  they do, provided option `diredp-wrap-around-flag' is non-nil,
-;;  which it is by default.  This means the following commands:
-;;
-;;    `diredp-next-line'     - `n', `C-n', `down', `SPC'
-;;    `diredp-previous-line' - `p', `C-p', `up'
-;;    `diredp-next-dirline'  - `>'
-;;    `diredp-prev-dirline'  - `<'
-;;    `diredp-next-subdir'   - `C-M-n'
-;;    `diredp-prev-subdir'   - `C-M-p'
-;;
-;;
-;;  Quick Viewing While Navigating
-;;  ------------------------------
-;;
-;;  You can use key `C-down' or `C-up' to navigate to the next or
-;;  previous file line, respectively, and at the same time show its
-;;  file in another window.  The focus remains on the Dired buffer.
-;;  A numeric prefix arg means move that many lines first.
-;;
-;;  Names of files and directories that match either of the options
-;;  `diredp-visit-ignore-extensions' or `diredp-visit-ignore-regexps'
-;;  are skipped.
-;;
-;;  You can use `e' to show the file of the current line.  If it is
-;;  already shown in the same frame, and if Dired is the only other
-;;  window there, then the file is hidden (its window is deleted).
-;;
-;;
-;;  Font-Lock Highlighting
-;;  ----------------------
-;;
-;;  If you want a maximum or minimum fontification for Dired mode,
-;;  then customize option `font-lock-maximum-decoration'.  If you want
-;;  a different fontification level for Dired than for other modes,
-;;  you can do this too by customizing
-;;  `font-lock-maximize-decoration'.
-;;
-;;  A few of the user options defined here have an effect on
-;;  font-locking, and this effect is established only when Dired+ is
-;;  loaded, which defines the font-lock keywords for Dired.  These
-;;  options include `diredp-compressed-extensions',
-;;  `diredp-ignore-compressed-flag', `dired-omit-extensions', and
-;;  `diredp-omit-files-regexp'.  This means that if you change the
-;;  value of such an option then you will see the change only in a new
-;;  Emacs session.
-;;
-;;  (You can see the effect in the same session if you use `C-M-x' on
-;;  the `defvar' sexp for `diredp-font-lock-keywords-1', and then you
-;;  toggle font-lock off and back on.)
-;;
-;;
-;;  Act on All Files
-;;  ----------------
-;;
-;;  Most of the commands (such as `C' and `M-g') that operate on the
-;;  marked files have the added feature here that multiple `C-u' use
-;;  not the files that are marked or the next or previous N files, but
-;;  *all* of the files in the Dired buffer.  Just what "all" files
-;;  means changes with the number of `C-u', as follows:
-;;
-;;    `C-u C-u'         - Use all files present, but no directories.
-;;    `C-u C-u C-u'     - Use all files and dirs except `.' and `..'.
-;;    `C-u C-u C-u C-u' - use all files and dirs, `.' and `..'.
-;;
-;;    (More than four `C-u' act the same as two.)
-;;
-;;  This feature can be particularly useful when you have a Dired
-;;  buffer with files chosen from multiple directories.
-;;
-;;  Note that in most cases this behavior is described only in the doc
-;;  string of function `dired-get-marked-files'.  It is generally
-;;  *not* described in the doc strings of the various commands,
-;;  because that would require redefining each command separately
-;;  here.  Instead, we redefine macro `dired-map-over-marks' and
-;;  function `dired-get-filename' in order to achieve this effect.
-;;
-;;  Commands such as `dired-do-load' for which it does not make sense
-;;  to act on directories generally treat more than two `C-u' the same
-;;  as two `C-u'.
-;;
-;;  Exceptions to the general behavior described here are called out
-;;  in the doc strings.  In particular, the behavior of a prefix arg
-;;  for `dired-do-query-replace-regexp' is different, so that you can
-;;  use it also to specify word-delimited replacement.
-;;
-;;
-;;  Act on Marked (or All) Files Here and Below
-;;  -------------------------------------------
-;;
-;;  The prefix argument behavior just described does not apply to the
-;;  `diredp-*-recursive' commands.  These commands act on the marked
-;;  files in the current Dired buffer or on all files in the directory
-;;  if none are marked.
-;;
-;;  But these commands also handle marked subdirectories recursively,
-;;  in the same way.  That is, they act also on the marked files in
-;;  any marked subdirectories, found recursively.  If such a
-;;  descendant directory is listed in a Dired buffer then its marked
-;;  files and subdirs are handled the same way.  If there is no Dired
-;;  buffer that lists a given marked subdirectory then all of its
-;;  files and subdirs are acted on.
-;;
-;;  For most such here-and-below commands, a prefix argument means
-;;  ignore all marks.  The commands then act on all files in the
-;;  current Dired buffer and all of its subdirectories, recursively.
-;;
-;;  But here-and-below commands that unmark or change marks act
-;;  differently for different kinds of prefix argument:
-;;
-;;  * A non-positive prefix arg means ignore subdir markings and act
-;;    instead on ALL subdirs.
-;;
-;;  * A non-negative prefix arg means do not change marks on subdirs
-;;    themselves.
-;;
-;;  For example, `M-+ U' removes all marks, including from marked
-;;  subdirs, recursively.  `C-- M-+ U' removes them from all files in
-;;  all subdirs (marked or not), recursively.  `C-9 M-+ U' removes all
-;;  marks, recursively, except the marks on subdirs themselves.  `C-0
-;;  M-+ U' acts like those two combined: it descends everywhere,
-;;  ignoring which subdirs are marked, but it does not remove marks
-;;  from subdirs themselves.
-;;
-;;  All of the `diredp-*-recursive' commands are on prefix key `M-+',
-;;  and most are available on submenu `Marked Here and Below' of the
-;;  `Multiple' menu-bar menu.  The commands that unmark and change
-;;  marks are also in submenu `Here and Below' of menu-bar menu
-;;  `Marks'.
-;;
-;;  If you use library `Icicles' then you have the following
-;;  additional commands/keys that act recursively on marked files.
-;;  They are in the `Icicles' submenu of menu `Multiple' > `Marked
-;;  Here and Below'.
-;;
-;;  * `M-+ M-s M-s' or `M-s M-s m' - Use Icicles search (and its
-;;                  on-demand replace) on the marked files.
-;;
-;;  * Save the names of the marked files:
-;;
-;;    `M-+ C-M->' - Save as a completion set, for use during
-;;                  completion (e.g. with `C-x C-f').
-;;
-;;    `M-+ C->'   - Add marked names to the names in the current saved
-;;                  completion set.
-;;
-;;    `M-+ C-}'   - Save persistently to an Icicles cache file, for
-;;                  use during completion in another session.
-;;
-;;    `icicle-dired-save-marked-to-fileset-recursive' - Like `M-+
-;;                  C-}', but save persistently to an Emacs fileset.
-;;
-;;    `M-+ C-M-}' - Save to a Lisp variable.
-;;
-;;
-;;  In the other direction, if you have a saved set of file names then
-;;  you can use `C-M-<' (`icicle-dired-chosen-files-other-window') in
-;;  Dired to open a Dired buffer for just those files.  So you can
-;;  mark some files and subdirs in a hierarchy of Dired buffers, use
-;;  `M-+ C-}' to save their names persistently, then later use `C-{'
-;;  to retrieve them, and `C-M-<' (in Dired) to open Dired on them.
-;;
-;;
-;;  Image Files
-;;  -----------
-;;
-;;  `Dired+' provides several enhancements regarding image files.
-;;  Most of these require standard library `image-dired.el'.  One of
-;;  them, command `diredp-do-display-images', which displays all of
-;;  the marked image files, requires standard library `image-file.el'.
-;;
-;;  `Dired+' loads these libraries automatically, if available, which
-;;  means an Emacs version that supports image display (Emacs 22 or
-;;  later).  (You must of course have installed whatever else your
-;;  Emacs version needs to display images.)
-;;
-;;  Besides command `diredp-do-display-images', see the commands whose
-;;  names have prefix `diredp-image-'.  And see options
-;;  `diredp-image-preview-in-tooltip' and
-;;  `diredp-auto-focus-frame-for-thumbnail-tooltip-flag'.
-;;    
-;;
-;;  Inserted Subdirs, Multiple Dired Buffers, Files from Anywhere,...
-;;  -----------------------------------------------------------------
-;;
-;;  These three standard Dired features are worth pointing out.  The
-;;  third in particular is little known because (a) it is limited in
-;;  vanilla Dired and (b) you cannot use it interactively.
-;;
-;;   * You can pass a glob pattern with wildcards to `dired'
-;;     interactively, as the file name.
-;;
-;;   * You can insert multiple subdirectory listings into a single
-;;     Dired buffer using `i' on each subdir line.  Use `C-u i' to
-;;     specify `ls' switches.  Specifying switch `R' inserts the
-;;     inserted subdirectory's subdirs also, recursively.  You can
-;;     also use `i' to bounce between a subdirectory line and its
-;;     inserted-listing header line.  You can delete a subdir listing
-;;     using `C-u k' on its header line.  You can hide/show an
-;;     inserted subdir using `$'.  You can use `C-_' to undo any of
-;;     these operations.
-;;
-;;   * You can open a Dired buffer for an arbitrary set of files from
-;;     different directories.  You do this by invoking `dired'
-;;     non-interactively, passing it a cons of a Dired buffer name and
-;;     the file names.  Relative file names are interpreted relative
-;;     to the value of `default-directory'.  Use absolute file names
-;;     when appropriate.
-;;
-;;  `Dired+' makes these features more useful.
-;;
-;;  `$' is improved: It is a simple toggle - it does not move the
-;;  cursor forward.  `M-$' advances the cursor, in addition to
-;;  toggling like `$'.  `C-u $' does hide/show all (what `M-$' does in
-;;  vanilla Dired).
-;;
-;;  `i' is improved in these ways:
-;;
-;;   * Once a subdir has been inserted, `i' bounces between the subdir
-;;     listing and the subdir line in the parent listing.  If the
-;;     parent dir is hidden, then `i' from a subdir opens the parent
-;;     listing so it can move to the subdir line there (Emacs 24+).
-;;
-;;   * Vanilla Dired lets you create a Dired listing with files and
-;;     directories from arbitrary locations, but you cannot insert
-;;     (`i') such a directory if it is not in the same directory tree
-;;     as the `default-directory' used to create the Dired buffer.
-;;     `Dired+' removes this limitation; you can insert any non-root
-;;     directories (that is, not `/', `c:/', etc.).
-;;
-;;  `Dired+' lets you create Dired buffers that contain arbitrary
-;;  files and directories interactively, not just using Lisp.  Just
-;;  use a non-positive prefix arg (e.g., `C--') when invoking `dired'.
-;;
-;;  You are then prompted for the Dired buffer name (anything you
-;;  like, not necessarily a directory name) and the individual files
-;;  and directories that you want listed.
-;;
-;;  A non-negative prefix arg still prompts you for the `ls' switches
-;;  to use.  (So `C-0' does both: prompts for `ls' switches and for
-;;  the Dired buffer name and the files to list.)
-;;
-;;  `Dired+' adds commands for combining and augmenting Dired
-;;  listings:
-;;
-;;   * `diredp-add-to-dired-buffer', bound globally to `C-x D A', lets
-;;     you add arbitrary file and directory names to an existing Dired
-;;     buffer.
-;;
-;;   * `diredp-dired-union', bound globally to `C-x D U', lets you
-;;     take the union of multiple Dired listings, or convert an
-;;     ordinary Dired listing to an explicit list of absolute file
-;;     names.  With a non-positive prefix arg, you can add extra file
-;;     and directory names, just as for `diredp-add-to-dired-buffer'.
-;;
-;;  You can optionally add a header line to a Dired buffer using
-;;  toggle command `diredp-breadcrumbs-in-header-line-mode'.  (A
-;;  header line remains at the top of the window - no need to scroll
-;;  to see it.)  If you want to show the header line automatically in
-;;  all Dired buffers, you can do this:
-;;
-;;    (add-hook 'dired-before-readin-hook
-;;              'diredp-breadcrumbs-in-header-line-mode)
-;;
-;;  Some other libraries, such as `Bookmark+' and `Icicles', make it
-;;  easy to create or re-create Dired buffers that list specific files
-;;  and have a particular set of markings.  `Bookmark+' records Dired
-;;  buffers persistently, remembering `ls' switches, markings, subdir
-;;  insertions, and hidden subdirs.  If you use `Icicles' then `dired'
-;;  is a multi-command: you can open multiple Dired buffers with one
-;;  `dired' invocation.
-;;
-;;  Dired can help you manage projects.  You might have multiple Dired
-;;  buffers with quite specific contents.  You might have some
-;;  subdirectories inserted in the same Dired buffer, and you might
-;;  have separate Dired buffers for some subdirectories.  Sometimes it
-;;  is useful to have both for the same subdirectory.  And sometimes
-;;  it is useful to move from one presentation to the other.
-;;
-;;  This is one motivation for the `Dired+' `diredp-*-recursive'
-;;  commands, which act on the marked files in marked subdirectories,
-;;  recursively.  In one sense, these commands are an alternative to
-;;  using a single Dired buffer with inserted subdirectories.  They
-;;  let you use the same operations on the files in a set of Dired
-;;  directories, without inserting those directories into an ancestor
-;;  Dired buffer.
-;;
-;;  You can use command `diredp-dired-inserted-subdirs' to open a
-;;  separate Dired buffer for each of the subdirs that is inserted in
-;;  the current Dired buffer.  Markings and Dired switches are
-;;  preserved.
-;;
-;;  In the opposite direction, if you use `Icicles' then you can use
-;;  multi-command `icicle-dired-insert-as-subdir', which lets you
-;;  insert any number of directories you choose interactively into a
-;;  Dired ancestor directory listing.  If a directory you choose to
-;;  insert already has its own Dired buffer, then its markings and
-;;  switches are preserved for the new, subdirectory listing in the
-;;  ancestor Dired buffer.
-;;
-;;
-;;  Hide/Show Details
-;;  -----------------
-;;
-;;  Starting with Emacs 24.4, listing details are hidden by default.
-;;  Note that this is different from the vanilla Emacs behavior, which
-;;  is to show details by default.
-;;
-;;  Use `(' anytime to toggle this hiding.  You can use option
-;;  `diredp-hide-details-initially-flag' to change the default/initial
-;;  state.  See also option `diredp-hide-details-propagate-flag'.
-;;
-;;  NOTE: If you do not want to hide details initially then you must
-;;        either (1) change `diredp-hide-details-initially-flag' using
-;;        Customize (recommended) or (2) set it to `nil' (e.g., using
-;;        `setq') *BEFORE* loading `dired+.el'.
-;;
-;;  If you have an Emacs version older than 24.4, you can use library
-;;  `dired-details+.el' (plus `dired-details.el') to get similar
-;;  behavior.
-;;
-;;
-;;  Mode-Line
-;;  ---------
-;;
-;;  The number of files and dirs that are marked with `*', and the
-;;  number that are flagged for deletion (marked `D') are indicated in
-;;  the mode-line.  When the cursor is on such a line the indication
-;;  tells you how many more there are.  For example, if the cursor is
-;;  on the line of the third file that is marked `*', and there are
-;;  seven of them total, then the mode-line shows `3/7*'.
-;;
-;;  The mode-line also indicates, for the current listing (which could
-;;  be a subdir listing), how many files and dirs are listed.  If the
-;;  cursor is on the 27th file in a listing of 78 files then the
-;;  mode-line shows 27/78.
-;;
-;;  For counting files and dirs in a listing, option
-;;  `diredp-count-.-and-..-flag' controls whether to count the lines
-;;  for `.' and `..'.  By default it is nil, meaning they are not
-;;  counted.
-;;
-;;
-;;  If You Use Dired+ in Terminal Mode
-;;  ----------------------------------
-;;
-;;  By default, Dired+ binds some keys that can be problematic in some
-;;  terminals when you use Emacs in terminal mode (i.e., `emacs -nw').
-;;  This is controlled by option
-;;  `diredp-bind-problematic-terminal-keys'.
-;;
-;;  In particular, keys that use modifiers Meta and Shift together can
-;;  be problematic.  If you use Dired+ in text-only terminal, and you
-;;  find that your terminal does not support such keys, then you might
-;;  want to customize the option to set the value to `nil', and then
-;;  bind the commands to some other keys, which your terminal
-;;  supports.
-;;  
-;;  The problematic keys used by Dired+ include these:
-;;
-;;    `M-M'   (aka `M-S-m')   - `diredp-chmod-this-file'
-;;    `M-O'   (aka `M-S-o')   - `diredp-chown-this-file'
-;;    `M-T'   (aka `M-S-t')   - `diredp-touch-this-file'
-;;    `C-M-B' (aka `C-M-S-b') - `diredp-do-bookmark-in-bookmark-file'
-;;    `C-M-G' (aka `C-M-S-g') - `diredp-chgrp-this-file'
-;;    `C-M-R' (aka `C-M-S-r') - `diredp-toggle-find-file-reuse-dir'
-;;    `C-M-T' (aka `C-M-S-t') - `dired-do-touch'
-;;    `M-+ M-B'   (aka `M-+ M-S-b') -
-;;        `diredp-do-bookmark-dirs-recursive'
-;;    `M-+ C-M-B' (aka `M-+ C-M-S-b') -
-;;        `diredp-do-bookmark-in-bookmark-file-recursive'
-;;    `M-+ C-M-T' (aka `M-+ C-M-S-t') - `diredp-do-touch-recursive'
-;;
-;;  (See also `(info "(org) TTY keys")' for more information about
-;;  keys that can be problematic in a text-only terminal.)
-;;
-;;
-;;  Faces defined here:
-;;
-;;    `diredp-autofile-name', `diredp-compressed-file-suffix',
-;;    `diredp-date-time', `diredp-deletion',
-;;    `diredp-deletion-file-name', `diredp-dir-heading',
-;;    `diredp-dir-priv', `diredp-exec-priv', `diredp-executable-tag',
-;;    `diredp-file-name', `diredp-file-suffix', `diredp-flag-mark',
-;;    `diredp-flag-mark-line', `diredp-get-file-or-dir-name',
-;;    `diredp-ignored-file-name', `diredp-link-priv',
-;;    `diredp-mode-line-flagged', `diredp-mode-line-marked'
-;;    `diredp-omit-file-name', `diredp-no-priv', `diredp-number',
-;;    `diredp-other-priv', `diredp-rare-priv', `diredp-read-priv',
-;;    `diredp-symlink', `diredp-tagged-autofile-name',
-;;    `diredp-write-priv'.
-;;
-;;  Commands defined here:
-;;
-;;    `diredp-add-to-dired-buffer', `diredp-add-to-this-dired-buffer',
-;;    `diredp-do-apply-function',
-;;    `diredp-do-apply-function-recursive',
-;;    `diredp-async-shell-command-this-file',
-;;    `diredp-bookmark-this-file',
-;;    `diredp-breadcrumbs-in-header-line-mode' (Emacs 22+),
-;;    `diredp-byte-compile-this-file', `diredp-capitalize',
-;;    `diredp-capitalize-recursive', `diredp-capitalize-this-file',
-;;    `diredp-change-marks-recursive' (Emacs 22+),
-;;    `diredp-chgrp-this-file', `diredp-chmod-this-file',
-;;    `diredp-chown-this-file',
-;;    `diredp-compilation-files-other-window' (Emacs 24+),
-;;    `diredp-compress-this-file',
-;;    `diredp-copy-abs-filenames-as-kill',
-;;    `diredp-copy-abs-filenames-as-kill-recursive',
-;;    `diredp-copy-filename-as-kill-recursive',
-;;    `diredp-copy-tags-this-file', `diredp-copy-this-file',
-;;    `diredp-decrypt-this-file', `diredp-delete-this-file',
-;;    `diredp-describe-autofile', `diredp-describe-file',
-;;    `diredp-describe-marked-autofiles', `diredp-describe-mode',
-;;    `diredp-dired-for-files', `diredp-dired-for-files-other-window',
-;;    `diredp-dired-inserted-subdirs', `diredp-dired-plus-help',
-;;    `diredp-dired-recent-dirs',
-;;    `diredp-dired-recent-dirs-other-window',
-;;    `diredp-dired-this-subdir', `diredp-dired-union',
-;;    `diredp-do-async-shell-command-recursive', `diredp-do-bookmark',
-;;    `diredp-do-bookmark-dirs-recursive',
-;;    `diredp-do-bookmark-in-bookmark-file',
-;;    `diredp-do-bookmark-in-bookmark-file-recursive',
-;;    `diredp-do-bookmark-recursive', `diredp-do-chmod-recursive',
-;;    `diredp-do-chgrp-recursive', `diredp-do-chown-recursive',
-;;    `diredp-do-copy-recursive', `diredp-do-decrypt-recursive',
-;;    `diredp-do-delete-recursive', `diredp-do-display-images' (Emacs
-;;    22+), `diredp-do-emacs-command', `diredp-do-encrypt-recursive',
-;;    `diredp-do-find-marked-files-recursive', `diredp-do-grep',
-;;    `diredp-do-grep-recursive', `diredp-do-hardlink-recursive',
-;;    `diredp-do-isearch-recursive',
-;;    `diredp-do-isearch-regexp-recursive', `diredp-do-lisp-sexp'
-;;    (Emacs 22+), `diredp-do-move-recursive',
-;;    `diredp-do-paste-add-tags', `diredp-do-paste-replace-tags',
-;;    `diredp-do-print-recursive',
-;;    `diredp-do-query-replace-regexp-recursive',
-;;    `diredp-do-redisplay-recursive',
-;;    `diredp-do-relsymlink-recursive', `diredp-do-remove-all-tags',
-;;    `diredp-do-search-recursive', `diredp-do-set-tag-value',
-;;    `diredp-do-shell-command-recursive', `diredp-do-sign-recursive',
-;;    `diredp-do-symlink-recursive', `diredp-do-tag',
-;;    `diredp-do-touch-recursive', `diredp-do-untag',
-;;    `diredp-do-verify-recursive', `diredp-downcase-recursive',
-;;    `diredp-downcase-this-file', `diredp-ediff',
-;;    `diredp-encrypt-this-file', `diredp-fileset',
-;;    `diredp-fileset-other-window', `diredp-find-a-file',
-;;    `diredp-find-a-file-other-frame',
-;;    `diredp-find-a-file-other-window',
-;;    `diredp-find-file-other-frame',
-;;    `diredp-find-file-reuse-dir-buffer',
-;;    `diredp-find-line-file-other-window',
-;;    `diredp-flag-auto-save-files-recursive',
-;;    `diredp-flag-region-files-for-deletion',
-;;    `diredp-grepped-files-other-window', `diredp-grep-this-file',
-;;    `diredp-hardlink-this-file', `diredp-highlight-autofiles-mode',
-;;    `diredp-image-dired-comment-file',
-;;    `diredp-image-dired-comment-files-recursive',
-;;    `diredp-image-dired-copy-with-exif-name',
-;;    `diredp-image-dired-create-thumb',
-;;    `diredp-image-dired-delete-tag',
-;;    `diredp-image-dired-delete-tag-recursive',
-;;    `diredp-image-dired-display-thumb',
-;;    `diredp-image-dired-display-thumbs-recursive',
-;;    `diredp-image-dired-edit-comment-and-tags',
-;;    `diredp-image-dired-tag-file',
-;;    `diredp-image-dired-tag-files-recursive',
-;;    `diredp-image-show-this-file', `diredp-insert-as-subdir',
-;;    `diredp-insert-subdirs', `diredp-insert-subdirs-recursive',
-;;    `diredp-kill-this-tree', `diredp-list-marked-recursive',
-;;    `diredp-load-this-file', `diredp-mark-autofiles',
-;;    `diredp-marked', `diredp-marked-other-window',
-;;    `diredp-marked-recursive',
-;;    `diredp-marked-recursive-other-window',
-;;    `diredp-mark-extension-recursive',
-;;    `diredp-mark-files-containing-regexp-recursive',
-;;    `diredp-mark-files-regexp-recursive',
-;;    `diredp-mark-files-tagged-all', `diredp-mark-files-tagged-none',
-;;    `diredp-mark-files-tagged-not-all',
-;;    `diredp-mark-files-tagged-some',
-;;    `diredp-mark-files-tagged-regexp', `diredp-mark-region-files',
-;;    `diredp-mark-sexp-recursive' (Emacs 22+),
-;;    `diredp-mark/unmark-autofiles', `diredp-mark/unmark-extension',
-;;    `diredp-mouse-3-menu', `diredp-mouse-backup-diff',
-;;    `diredp-mouse-copy-tags', `diredp-mouse-describe-autofile',
-;;    `diredp-mouse-describe-file', `diredp-mouse-diff',
-;;    `diredp-mouse-do-bookmark', `diredp-mouse-do-byte-compile',
-;;    `diredp-mouse-do-chgrp', `diredp-mouse-do-chmod',
-;;    `diredp-mouse-do-chown', `diredp-mouse-do-compress',
-;;    `diredp-mouse-do-copy', `diredp-mouse-do-delete',
-;;    `diredp-mouse-do-grep', `diredp-mouse-do-hardlink',
-;;    `diredp-mouse-do-load', `diredp-mouse-do-print',
-;;    `diredp-mouse-do-remove-all-tags', `diredp-mouse-do-rename',
-;;    `diredp-mouse-do-set-tag-value',
-;;    `diredp-mouse-do-shell-command', `diredp-mouse-do-symlink',
-;;    `diredp-mouse-do-tag', `diredp-mouse-do-untag',
-;;    `diredp-mouse-downcase', `diredp-mouse-ediff',
-;;    `diredp-mouse-find-line-file-other-window',
-;;    `diredp-mouse-find-file-other-frame',
-;;    `diredp-mouse-find-file-reuse-dir-buffer',
-;;    `diredp-mouse-flag-file-deletion', `diredp-mouse-mark',
-;;    `diredp-mouse-mark-region-files', `diredp-mouse-mark/unmark',
-;;    `diredp-mouse-unmark', `diredp-mouse-upcase',
-;;    `diredp-mouse-view-file', `diredp-move-file' (Emacs 24+),
-;;    `diredp-multiple-w32-browser-recursive',
-;;    `diredp-nb-marked-in-mode-name', `diredp-next-dirline',
-;;    `diredp-next-line', `diredp-next-subdir', `diredp-omit-marked',
-;;    `diredp-omit-unmarked', `diredp-paste-add-tags-this-file',
-;;    `diredp-paste-files', `diredp-paste-replace-tags-this-file',
-;;    `diredp-prev-dirline', `diredp-previous-line',
-;;    `diredp-prev-subdir', `diredp-print-this-file',
-;;    `diredp-relsymlink-this-file',
-;;    `diredp-remove-all-tags-this-file', `diredp-rename-this-file',
-;;    `diredp-send-bug-report',
-;;    `diredp-set-bookmark-file-bookmark-for-marked',
-;;    `diredp-set-bookmark-file-bookmark-for-marked-recursive',
-;;    `diredp-set-tag-value-this-file',
-;;    `diredp-shell-command-this-file', `diredp-show-metadata',
-;;    `diredp-show-metadata-for-marked', `diredp-sign-this-file',
-;;    `diredp-symlink-this-file', `diredp-tag-this-file',
-;;    `diredp-toggle-find-file-reuse-dir',
-;;    `diredp-toggle-marks-in-region', `diredp-touch-this-file',
-;;    `diredp-unmark-all-files-recursive' (Emacs 22+),
-;;    `diredp-unmark-all-marks-recursive' (Emacs 22+),
-;;    `diredp-unmark-autofiles', `diredp-unmark-files-tagged-all',
-;;    `diredp-unmark-files-tagged-none',
-;;    `diredp-unmark-files-tagged-not-all',
-;;    `diredp-unmark-files-tagged-some', `diredp-unmark-region-files',
-;;    `diredp-untag-this-file', `diredp-upcase-recursive',
-;;    `diredp-up-directory', `diredp-up-directory-reuse-dir-buffer',
-;;    `diredp-upcase-this-file', `diredp-verify-this-file',
-;;    `diredp-visit-next-file', `diredp-visit-previous-file',
-;;    `diredp-visit-this-file', `diredp-w32-drives',
-;;    `diredp-w32-drives-mode', `diredp-yank-files',
-;;    `global-dired-hide-details-mode' (Emacs 24.4+),
-;;    `toggle-diredp-find-file-reuse-dir'.
-;;
-;;  User options defined here:
-;;
-;;    `diredp-auto-focus-frame-for-thumbnail-tooltip-flag',
-;;    `diredp-bind-problematic-terminal-keys',
-;;    `diredp-compressed-extensions', `diredp-count-.-and-..-flag'
-;;    (Emacs 22+), `diredp-do-report-echo-limit',
-;;    `diredp-dwim-any-frame-flag' (Emacs 22+),
-;;    `diredp-image-preview-in-tooltip', `diff-switches',
-;;    `diredp-hide-details-initially-flag' (Emacs 24.4+),
-;;    `diredp-highlight-autofiles-mode',
-;;    `diredp-hide-details-propagate-flag' (Emacs 24.4+),
-;;    `diredp-ignore-compressed-flag',
-;;    `diredp-image-show-this-file-use-frame-flag' (Emacs 22+),
-;;    `diredp-list-file-attributes', `diredp-max-frames',
-;;    `diredp-move-file-dirs' (Emacs 24+), `diredp-omit-files-regexp'
-;;    `diredp-prompt-for-bookmark-prefix-flag',
-;;    `diredp-visit-ignore-extensions', `diredp-visit-ignore-regexps',
-;;    `diredp-w32-local-drives', `diredp-wrap-around-flag'.
-;;
-;;  Non-interactive functions defined here:
-;;
-;;    `derived-mode-p' (Emacs < 22), `diredp-all-files',
-;;    `diredp-ancestor-dirs', `diredp-apply-function-to-file-name',
-;;    `diredp-bookmark',
-;;    `diredp-create-files-non-directory-recursive',
-;;    `diredp-delete-dups', `diredp-delete-if',
-;;    `diredp-delete-if-not', `diredp-directories-within',
-;;    `diredp-dired-plus-description',
-;;    `diredp-dired-plus-description+links',
-;;    `diredp-dired-plus-help-link', `diredp-dired-union-1',
-;;    `diredp-dired-union-interactive-spec', `diredp-display-image'
-;;    (Emacs 22+), `diredp-do-chxxx-recursive',
-;;    `diredp-do-create-files-recursive', `diredp-do-grep-1',
-;;    `diredp-ensure-bookmark+', `diredp-ensure-mode',
-;;    `diredp-eval-lisp-sexp' (Emacs 22+),
-;;    `diredp-existing-dired-buffer-p', `diredp-fewer-than-2-files-p',
-;;    `diredp-fewer-than-echo-limit-files-p',
-;;    `diredp-fewer-than-N-files-p', `diredp-fileset-1',
-;;    `diredp-find-a-file-read-args',
-;;    `diredp-file-for-compilation-hit-at-point' (Emacs 24+),
-;;    `diredp-files-within', `diredp-files-within-1',
-;;    `diredp-fit-frame-unless-buffer-narrowed' (Emacs 24.4+),
-;;    `diredp-get-confirmation-recursive', `diredp-get-files',
-;;    `diredp-get-files-for-dir', `diredp-get-subdirs',
-;;    `diredp-hide-details-if-dired' (Emacs 24.4+),
-;;    `diredp-hide/show-details' (Emacs 24.4+),
-;;    `diredp-highlight-autofiles', `diredp-image-dired-required-msg',
-;;    `diredp-get-image-filename', `diredp-internal-do-deletions',
-;;    `diredp-invoke-emacs-command', `diredp-invoke-function-no-args',
-;;    `diredp-list-file', `diredp-list-files', `diredp-looking-at-p',
-;;    `diredp-make-find-file-keys-reuse-dirs',
-;;    `diredp-make-find-file-keys-not-reuse-dirs', `diredp-maplist',
-;;    `diredp-map-over-marks-and-report', `diredp-marked-here',
-;;    `diredp-mark-files-tagged-all/none',
-;;    `diredp-mark-files-tagged-some/not-all',
-;;    `diredp-nonempty-region-p', `diredp-parent-dir',
-;;    `diredp-paste-add-tags', `diredp-paste-replace-tags',
-;;    `diredp-read-bookmark-file-args', `diredp-read-command',
-;;    `diredp-read-expression' (Emacs 22+),
-;;    `diredp-read-include/exclude', `diredp-read-regexp',
-;;    `diredp-recent-dirs', `diredp-refontify-buffer',
-;;    `diredp-remove-if', `diredp-remove-if-not',
-;;    `diredp-report-file-result', `diredp--reuse-dir-buffer-helper',
-;;    `diredp-root-directory-p', `diredp-set-header-line-breadcrumbs'
-;;    (Emacs 22+), `diredp-set-tag-value', `diredp-set-union',
-;;    `diredp--set-up-font-locking', `diredp-string-match-p',
-;;    `diredp-tag', `diredp-this-file-marked-p',
-;;    `diredp-this-file-unmarked-p', `diredp-this-subdir',
-;;    `diredp-untag', `diredp-visit-ignore-regexp',
-;;    `diredp-y-or-n-files-p'.
-;;
-;;  Variables defined here:
-;;
-;;    `diredp-bookmark-menu', `diredp-file-line-overlay',
-;;    `diredp-files-within-dirs-done', `diredp-font-lock-keywords-1',
-;;    `diredp-hide-details-last-state' (Emacs 24.4+),
-;;    `diredp-hide-details-toggled' (Emacs 24.4+),
-;;    `diredp-hide/show-menu', `diredp-images-recursive-menu',
-;;    `diredp-last-copied-filenames', `diredp-list-files-map',
-;;    `diredp-loaded-p', `diredp-marks-recursive-menu',
-;;    `diredp-menu-bar-dir-menu', `diredp-menu-bar-marks-menu',
-;;    `diredp-menu-bar-multiple-menu', `diredp-menu-bar-regexp-menu',
-;;    `diredp-menu-bar-single-menu', `diredp-multiple-bookmarks-menu',
-;;    `diredp-multiple-delete-menu', `diredp-multiple-dired-menu',
-;;    `diredp-multiple-images-menu',
-;;    `diredp-multiple-encryption-menu',
-;;    `diredp-multiple-move-copy-link-menu',
-;;    `diredp-multiple-omit-menu', `diredp-multiple-recursive-menu',
-;;    `diredp-multiple-rename-menu', `diredp-multiple-search-menu',
-;;    `diredp-navigate-menu', `diredp-regexp-recursive-menu',
-;;    `diredp-re-no-dot', `diredp-single-bookmarks-menu',
-;;    `diredp-single-encryption-menu', `diredp-single-image-menu',
-;;    `diredp-single-move-copy-link-menu', `diredp-single-open-menu',
-;;    `diredp-single-rename-menu', `diredp-w32-drives-mode-map'.
-;;
-;;  Macros defined here:
-;;
-;;    `diredp-mark-if', `diredp-user-error',
-;;    `diredp-with-help-window'.
-;;
-;;
-;;  ***** NOTE: The following macros defined in `dired.el' have
-;;              been REDEFINED HERE:
-;;
-;;  `dired-map-over-marks'    - Treat multiple `C-u' specially.
-;;
-;;
-;;  ***** NOTE: The following functions defined in `dired.el' have
-;;              been REDEFINED or ADVISED HERE:
-;;
-;;  `dired'                   - Handle non-positive prefix arg.
-;;  `dired-do-delete'         - Display message to warn that marked,
-;;                              not flagged, files will be deleted.
-;;  `dired-do-flagged-delete' - Display message to warn that flagged,
-;;                              not marked, files will be deleted.
-;;  `dired-dwim-target-directory' - Uses `diredp-dwim-any-frame-flag'.
-;;  `dired-find-file'         - Allow `.' and `..' (Emacs 20 only).
-;;  `dired-get-filename'      - Test `./' and `../' (like `.', `..').
-;;  `dired-get-marked-files'  - Can include `.' and `..'.
-;;                              Allow FILTER + DISTINGUISH-ONE-MARKED.
-;;  `dired-goto-file'         - Fix Emacs bug #7126.
-;;                              Remove `/' from dir before compare.
-;;                              (Emacs < 24 only.)
-;;  `dired-hide-details-mode' - Respect new user options:
-;;                              * `diredp-hide-details-initially-flag'
-;;                              * `diredp-hide-details-propagate-flag'
-;;                              (Emacs 24.4+)
-;;  `dired-insert-directory'  - Compute WILDCARD arg for
-;;                              `insert-directory' for individual file
-;;                              (don't just use nil). (Emacs 23+, and
-;;                              only for MS Windows)
-;;  `dired-insert-set-properties' - `mouse-face' on whole line.
-;;  `dired-flag-auto-save-files', `dired-mark-directories',
-;;  `dired-mark-executables', `dired-mark-files-containing-regexp',
-;;  `dired-mark-files-regexp', `dired-mark-symlinks'
-;;                            - Use `diredp-mark-if', not `dired-mark-if'.
-;;  `dired-mark-files-regexp' - Add regexp to `regexp-search-ring'.
-;;                              More matching possibilities.
-;;                              Added optional arg LOCALP.
-;;  `dired-mark-pop-up'       - Delete the window or frame popped up,
-;;                              afterward, and bury its buffer. Do not
-;;                              show a menu bar for pop-up frame.
-;;  `dired-other-frame'       - Handle non-positive prefix arg.
-;;  `dired-other-window'      - Handle non-positive prefix arg.
-;;  `dired-pop-to-buffer'     - Put window point at bob (bug #12281).
-;;                              (Emacs 22-24.1)
-;;  `dired-read-dir-and-switches' - Non-positive prefix arg behavior.
-;;
-;;; NOT YET:
-;;; ;;  `dired-readin-insert'     - Use t as WILDCARD arg to
-;;; ;;                              `dired-insert-directory'.  (Emacs 23+,
-;;; ;;                              and only for MS Windows)
-;;
-;;  `dired-revert'            - Reset `mode-line-process' to nil.
-;;  `dired-switches-escape-p' - Made compatible with Emacs 20, 21.
-;;
-;;
-;;  ***** NOTE: The following functions are included here with little
-;;              or no change to their definitions.  They are here to
-;;              take advantage of the new definition of macro
-;;              `dired-map-over-marks':
-;;
-;;  `dired-do-redisplay', `dired-map-over-marks-check',
-;;  `image-dired-dired-insert-marked-thumbs',
-;;  `image-dired-dired-toggle-marked-thumbs'.
-;;
-;;
-;;  ***** NOTE: The following functions defined in `dired-aux.el' have
-;;              been REDEFINED HERE:
-;;
-;;  `dired-do-byte-compile', `dired-do-compress', `dired-do-load' -
-;;     Redisplay only if at most one file is being treated.
-;;  `dired-do-find-regexp', `dired-do-find-regexp-and-replace' -
-;;     Prefix arg lets you act on files other than those marked.
-;;  `dired-do-isearch', `dired-do-isearch-regexp',
-;;     `dired-do-query-replace-regexp', `dired-do-search' -
-;;        Use new `dired-get-marked-files'.
-;;  `dired-insert-subdir-newpos' - If not a descendant, put at eob.
-;;  `dired-insert-subdir-validate' - Do nothing: no restrictions.
-;;  `dired-maybe-insert-subdir' - Go back to subdir line if in listing.
-;;  `dired-handle-overwrite' - Added optional arg FROM, for listing.
-;;  `dired-copy-file(-recursive)', `dired-hardlink', `dired-query',
-;;     `dired-rename-file' - You can list (`l') the files involved.
-;;
-;;
-;;  ***** NOTE: The following functions defined in `dired-x.el' have
-;;              been REDEFINED HERE:
-;;
-;;  `dired-copy-filename-as-kill' -
-;;     Put file names also in var `diredp-last-copied-filenames'.
-;;  `dired-do-find-marked-files' -
-;;     Call `dired-get-marked-files' with original ARG.
-;;     Added optional arg INTERACTIVEP - no error if nil and no files.
-;;  `dired-do-run-mail' - Require confirmation.
-;;  `dired-mark-sexp' - 1. Variable `s' -> `blks'.
-;;                      2. Fixes to `uid' and `gid'.
-;;  `dired-mark-unmarked-files' (Emacs < 24 only) - Emacs 24+ version.
-;;  `dired-simultaneous-find-file' -
-;;     Use separate frames instead of windows if `pop-up-frames' is
-;;     non-nil, or if prefix arg < 0.
-;;
-;;
-;;  ***** NOTE: (Emacs 20 only) The following variable defined in
-;;        `dired.el' has been REDEFINED HERE:
-;;
-;;  `dired-move-to-filename-regexp' - Recognize file size in k etc.
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;;; Change Log:
-;;
-;; 2019/07/03 dadams
-;;     dired-mark-unmarked-files: Apply fix for Emacs bug #27465.
-;;     diredp-mark-if, diredp-mark-sexp(-recursive), dired-mark-unmarked-files:
-;;       Use char-after, not diredp-looking-at-p.
-;; 2019/07/19 dadams
-;;     diredp-change-marks-recursive, diredp-unmark-all-files-recursive,
-;;       diredp-mark-files(-containing)-regexp-recursive, diredp-mark-sexp-recursive, diredp-mark-recursive-1:
-;;         Added missing PREDICATE arg in calls to diredp-get-subdirs.
-;; 2019/06/25 dadams
-;;     diredp-mark-if, diredp-this-file-(un)marked-p: Use regexp-quote for marker char.
-;; 2019/06/03 dadams
-;;     Removed autoload cookie for diredp-omit-files-regexp - it evaluates dired-omit-files, from dired-x.el.
-;;     Hard-require dired-x.el.  (No reason not to.)  Removed fboundp guards for it.
-;; 2019/04/22 dadams
-;;     Added diredp-move-files-named-in-kill-ring.  Bound to C-w.
-;; 2019/04/21 dadams
-;;     Added redefinitions of dired-do-find-regexp, dired-do-find-regexp-and-replace.
-;;     diredp-multiple-search-menu: Added "Using TAGS Table" for dired-do-(query-replace|search).
-;; 2019/04/20 dadams
-;;     Added:
-;;       diredp-map-over-marks-and-report, diredp-do-emacs-command, diredp-invoke-emacs-command,
-;;       diredp-read-command, diredp-do-lisp-sexp, diredp-eval-lisp-sexp, diredp-report-file-result,
-;;       diredp-do-report-echo-limit, diredp-fewer-than-N-files-p, diredp-fewer-than-echo-limit-files-p,
-;;       diredp-apply-function-to-file-name, diredp-invoke-function-no-args, diredp-list-file-attributes.
-;;     diredp-do-apply-function: Redefine to use diredp-map-over-marks-and-report.
-;;     diredp-dired-plus-description, diredp-menu-bar-multiple-menu:
-;;       Added diredp-do-emacs-command, diredp-do-lisp-sexp.
-;;     diredp-menu-bar-multiple-menu: Reordered items.
-;;     diredp-list-marked, diredp-*-recursive, diredp-describe-marked-autofiles:
-;;       Use diredp-list-file-attributes for DETAILS arg interactively.
-;;     diredp-yank-files, dired-query: Use diredp-list-file-attributes, not harcoded list (5 8).
-;;     diredp-set-bookmark-file-bookmark-for-marked-recursive: Corrected interactive spec.
-;; 2019/04/16 dadams
-;;     Added: diredp-delete-if.
-;;     dired-map-over-marks-check: Added &rest argument FUN-ARGS, so FUN can accept arguments.
-;; 2019/04/12 dadams
-;;     dired-get-marked-files: Do not add t to RESULT.  Thx to Jeff Spencer for bug report.
-;;                             If all marked is (t) for some reason reset it to nil, per vanilla Emacs 24+.
-;;     diredp-compressed-extensions: Added .rar, .rev.
-;; 2019/04/10 dadams
-;;     Added diredp-read-expression (forgot it when added diredp-mark-sexp-recursive).
-;;     diredp-mark-sexp-recursive is thus only for Emacs 22+.
-;; 2019/03/20 dadams
-;;     Added option diredp-omit-files-regexp.
-;;     Face diredp-omit-file-name: Added strike-through.
-;;     diredp-font-lock-keywords-1, for face diredp-omit-file-name:
-;;       Move to file name.  Use diredp-omit-files-regexp.  Append * for executable flag.  Highlight whole line.
-;; 2019/03/17 dadams
-;;     diredp-font-lock-keywords-1:
-;;       Use just dired-omit-files as regexp - its components already have ^...$.
-;;       Removed superfluous execute *'s in regexps and superfluous concat for compressed extensions.
-;;     Face diredp-omit-file-name: Removed :strike-through for default value.
-;; 2019/03/16 dadms
-;;     Added face diredp-omit-file-name.
-;;     diredp-font-lock-keywords-1: Use face diredp-omit-file-name for dired-omit-files matches.
-;; 2019/03/15 dadams
-;;     diredp-font-lock-keywords-1: Treat dired-omit-files like dired-omit-extensions.
-;; 2019/01/27 dadams
-;;     Added: diredp-mark-files-containing-regexp-recursive.
-;;              Bound to M-+ % g.  Added to diredp-marks-recursive-menu, diredp-regexp-recursive-menu.
-;; 2019/01/17 dadams
-;;     Added: diredp-mark-sexp-recursive.  Bound to M-+ M-(, M-+ * (.  Added to diredp-marks-recursive-menu.
-;;     dired-query: Use dired-query-alist only when available.
-;;     diredp-move-file: Fix format string in error call.
-;;     diredp-mark-symlinks-recursive: Added missing DETAILS arg for diredp-mark-recursive-1.
-;; 2019/01/01 dadams
-;;     Added: diredp-list-file.
-;;     Added redefinitions of dired-query, dired-handle-overwrite, dired-copy-file(-recursive), dired-rename-file,
-;;           dired-hardlink.
-;;     Added optional arg DETAILS to these functions: diredp-get-(subdirs|files), diredp-y-or-n-files-p,
-;;       diredp-list-(marked|files), diredp-yank-files, diredp-describe-marked-autofiles, plus all functions with
-;;       "recursive" in their name except diredp-get-confirmation-recursive.
-;;       Added optional arg DETAILS.
-;;     diredp-get-(subdirs|files), diredp-y-or-n-files-p, diredp-list-(marked|files), diredp-yank-files, 
-;;       diredp-describe-marked-autofiles:
-;;         Added optional arg DETAILS.
-;;     diredp-list-files: Use dired-list-file, to optionally show details.
-;;     diredp-yank-files: Non-positive prefix arg shows details now.
-;; 2018/12/02 dadams
-;;     dired-mark-pop-up: Work around Emacs 22 bug in dired-pop-to-buffer which can exit in Dired buffer.
-;; 2018/10/17 dadams
-;;     dired-read-dir-and-switches: Removed mention of icicle-file-sort-first-time-p (no longer used in Icicles).
-;; 2018/09/21 dadams
-;;     diredp-image-dired-edit-comment-and-tags, diredp-w32-drives:
-;;       Use pop-to-buffer-same-window, not switch-to-buffer.
-;; 2018/09/14 dadams
-;;     Added: diredp-move-file-dirs, diredp-move-file.
-;; 2018/06/30 dadams
-;;     Added: diredp-delete-if-not.
-;; 2018/06/16 dadams
-;;     Added: diredp-visit-ignore-extensions, diredp-visit-ignore-regexps, diredp-visit-next-file,
-;;            diredp-visit-previous-file, diredp-visit-this-file, diredp-visit-ignore-regexp.
-;;     Bind the commands to C-down, C-up, e.
-;; 2018/03/25 dadams
-;;     Added: diredp-user-error.
-;;     Updated for Emacs 27-pretest-2 change in dired-get-marked-files signature.
-;;       dired-get-marked-files: Added optional arg ERROR-IF-NONE-P.
-;;       diredp-list-marked, diredp-insert-subdirs, dired-do-(i)search(-regexp), dired-do-query-replace-regexp,
-;;         dired-do-find-marked-files, diredp-describe-marked-autofiles:
-;;           Added optional arg INTERACTIVEP.
-;;           Pass non-nil ERROR-IF-NONE-P to dired-get-marked-files when INTERACTIVEP.  (See Emacs bug #30938.)
-;; 2018/03/23 dadams
-;;     Added diredp-mark-if.  Removed: redefinition of dired-mark-if.
-;;       Differences: msg and return value include both number of matches and number of changes.
-;;     Added redefinitions (use diredp-mark-if) of dired-flag-auto-save-files,
-;;           dired-mark-(files-containing-regexp|symlinks|directories|executables).
-;;     Everywhere: Use diredp-mark-if, not dired-mark-if.
-;; 2018/03/03 dadams
-;;     diredp-delete-dups: defalias the symbol, not its symbol-function (dunno why I did the latter).
-;; 2018/02/28 dadams
-;;     Added: diredp-last-copied-filenames, diredp-copy-abs-filenames-as-kill-recursive,
-;;            and redefinition of vanilla diredp-last-copied-filenames.
-;;     diredp-copy-abs-filenames-as-kill: Use diredp-ensure-mode in interactive spec.
-;;     diredp-copy-filename-as-kill-recursive: Update diredp-last-copied-filenames with filenames string.
-;;     diredp-yank-files: Require confirmation for pasting, using diredp-y-or-n-files-p.
-;;                        Get file names from variable diredp-last-copied-filenames, not kill-ring.
-;;                        Added NO-CONFIRM-P arg.
-;;     diredp-ensure-mode: Added doc string.
-;;     diredp-do-grep, diredp-do-grep-recursive: Changed bindings to C-M-G and M-+ C-M-G, due to M-g conflict.
-;; 2018/02/27 dadams
-;;     Added: diredp-copy-abs-filenames-as-kill, diredp-yank-files (aka diredp-paste-files) (bound to C-y).
-;;     diredp-menu-bar-multiple-menu: Added diredp-copy-abs-filenames-as-kill.
-;;     diredp-menu-bar-dir-menu: Added diredp-yank-files.
-;; 2018/01/11 dadams
-;;     diredp-get-files:
-;;       Set IGNORE-MARKS-P to non-nil if nothing marked here.  (It was not getting all if nothing marked.)
-;;     diredp-marked-recursive(-other-window):
-;;       Corrected interactive spec, which was missing nil DIRNAME arg.  Corrected body: use DIRNAME.
-;;     diredp-get-files-for-dir, diredp-do-bookmark-dirs-recursive, diredp-change-marks-recursive,
-;;       diredp-unmark-all-files-recursive, diredp-mark-files-regexp-recursive, diredp-mark-recursive-1,
-;;       diredp-do-delete-recursive:
-;;         Factor out (dired-buffers-for-dir (expand-file-name directory)).
-;; 2018/01/03 dadams
-;;     dired-mark-files-regexp: Corrected doc string wrt prefix args.  Thx to John Mastro.
-;;     diredp-do-grep-recursive: Removed unused optional arg IGNORE-MARKS-P.
-;;     diredp-marked-recursive(-other-window): Moved handling of optional arg from interactive spec to body.
-;; 2018/01/02 dadams
-;;     Added: diredp-flag-auto-save-files-recursive.  Bound to M-+ #.
-;;     diredp-get-file-or-dir-name, diredp-marked-here: Doubled backslashes to escape dots.
-;;     diredp-marked-here: Fixed regexp to match only double-dot, not single-dot.
-;;     diredp-flag-auto-save-files-recursive: Updated to include more M-+ keys.
-;;     diredp-marks-recursive-menu: Added diredp-flag-auto-save-files-recursive.
-;; 2017/12/31 dadams
-;;     diredp-get-files-for-dir: Pass non-nil NO-DOT-DOT-P arg to diredp-marked-here.
-;;     dired-get-marked-files: Allow use of FILTER and DISTINGUISH-ONE-MARKED together.
-;;     diredp-marked-here: Added optional arg NO-DOT-DOT-P.
-;;     diredp-change-marks-recursive, diredp-unmark-all-files-recursive: Removed unused vars include-dirs, files.
-;; 2017/12/30 dadams
-;;     Added: diredp-change-marks-recursive, diredp-unmark-all-files-recursive, diredp-unmark-all-marks-recursive.
-;;            Bound to M-+ * c, M-+ M-DEL, M-+ U, respectively.
-;;     diredp-menu-bar-marks-menu: Rename item Change Marks to Change Mark.
-;;     diredp-marks-recursive-menu, diredp-multiple-recursive-menu:
-;;       Added diredp-change-marks-recursive, diredp-unmark-all-(files|marks)-recursive.
-;; 2017/12/21 dadams
-;;     Added: diredp-mark-recursive-1.  Forgot to add it last June.
-;; 2017/12/17 dadams
-;;     Removed: diredp-display-graphic-p.
-;;     Do not use diredp-display-graphic-p to allow binding diredp-bind-problematic-terminal-keys by default.
-;; 2017/11/25 dadams
-;;     diredp-nb-marked-in-mode-name: Wrap last :eval sexp in save-excursion.
-;;                                    Protect Call dired-current-directory only when dired-subdir-alist.
-;; 2017/10/23 dadams
-;;     Added: diredp-count-.-and-..-flag, diredp--reuse-dir-buffer-helper.
-;;     Removed: diredp-mouse-find-file.
-;;     diredp-find-file-reuse-dir-buffer, diredp-mouse-find-file-reuse-dir-buffer,
-;;       diredp-up-directory-reuse-dir-buffer:
-;;         Use diredp--reuse-dir-buffer-helper.
-;;     diredp-find-file-reuse-dir-buffer: Changed logic: do find-alternate-file only if target is a dir not in
-;;                                        Dired and current Dired buffer is in only this window.
-;;     diredp-mouse-find-file-reuse-dir-buffer: Added optional args FIND-FILE-FUNC and  FIND-DIR-FUNC.
-;;     diredp-up-directory, diredp-up-directory-reuse-dir-buffer: Pass OTHER-WINDOW arg to diredp-w32-drives.
-;;     diredp-nb-marked-in-mode-name: Show also number of lines in current listing, and listing-relative lineno,
-;;                                    respecting diredp-count-.-and-..-flag.
-;;     diredp-find-a-file*: Added autoload cookies.
-;; 2017/08/18 dadams
-;;     Fixed emacswiki URLs everywhere.  They changed the locations and changed http to https.
-;; 2017/06/30 dadams
-;;     Added: diredp-bind-problematic-terminal-keys, diredp-display-graphic-p.
-;;     Guard bindings of problematic keys with diredp-display-graphic-p & diredp-bind-problematic-terminal-keys.
-;;     Documented problematic keys for terminal mode in commentary.
-;; 2017/06/23 dadams
-;;     Added: diredp-read-regexp (removed alias to read-regexp), diredp-marks-recursive-menu,
-;;       diredp-mark-executables-recursive (bound to M-+ * *),
-;;       diredp-mark-directories-recursive (bound to M-+ * /),
-;;       diredp-mark-extension-recursive (bound to M-+ * .),
-;;       diredp-mark-autofiles-recursive (bound to M-+ * B),
-;;       diredp-mark-executables-recursive (bound to M-+ * *),
-;;       diredp-mark-directories-recursive (bound to M-+ * /),
-;;       diredp-mark-symlinks-recursive (bound to M-+ * @),
-;;     Bind diredp-mark-autofiles to * B.
-;;     diredp-marked-here: Bind dired-marker-char to ?*.
-;;     diredp-mark-files-regexp-recursive: Better msgs - show total count.
-;;     Everywhere: Use diredp-looking-at, not looking-at.  Use diredp-read-regexp, not dired-read-regexp.
-;; 2017/05/30 dadams
-;;     Fixed typo: direp--set-up-font-locking -> diredp--set-up-font-locking.
-;; 2017/05/22 dadams
-;;     Added: direp--set-up-font-locking.
-;;     Use direp--set-up-font-locking instead of lambda in dired-mode-hook.
-;; 2017/04/09 dadams
-;;     Version 2017.04.09.
-;;     Added: diredp-multiple-move-copy-link-menu, diredp-multiple-rename-menu, diredp-multiple-dired-menu,
-;;            diredp-multiple-omit-menu, diredp-multiple-delete-menu, diredp-single-bookmarks-menu,
-;;            diredp-single-encryption-menu, diredp-single-image-menu, diredp-single-open-menu,
-;;            diredp-single-move-copy-link-menu, diredp-single-rename-menu.
-;;            Moved single menu items there.
-;;     Renamed: diredp-menu-bar-encryption-menu        to diredp-multiple-encryption-menu,
-;;              diredp-menu-bar-mark-menu              to diredp-menu-bar-marks-menu,
-;;              diredp-menu-bar-operate-menu           to diredp-menu-bar-multiple-menu,
-;;              diredp-menu-bar-operate-bookmarks-menu to diredp-multiple-bookmarks-menu,
-;;              diredp-menu-bar-operate-recursive-menu to diredp-multiple-recursive-menu,
-;;              diredp-menu-bar-operate-search-menu    to diredp-multiple-search-menu,
-;;              diredp-menu-bar-images-menu            to diredp-multiple-images-menu,
-;;              diredp-menu-bar-images-recursive-menu  to diredp-images-recursive-menu,
-;;              diredp-menu-bar-immediate-menu         to diredp-menu-bar-single-menu,
-;;              diredp-menu-bar-regexp-recursive-menu  to diredp-regexp-recursive-menu,
-;;              diredp-menu-bar-subdir-menu            to diredp-menu-bar-dir-menu.
-;;     Added dired-do-rename to diredp-multiple-rename-menu.
-;;     diredp-nonempty-region-p: Ensure (mark) also.
-;; 2017/03/30 dadams
-;;     Moved key bindings to end of file.  Moved defgroup before defcustoms.
-;;     Bind dired-multiple-w32-browser to C-M-RET, diredp-multiple-w32-browser-recursive to M-+ C-M-RET.
-;; 2017/03/29 dadams
-;;     Added: diredp-dired-union-other-window, diredp-add-to-dired-buffer-other-window.
-;;     diredp-dired-union-1: Added optional arg OTHERWIN.
-;;     diredp-dired-plus-description: Updated doc string.
-;;     diredp-menu-bar-subdir-menu: Added diredp-dired-for-files.
-;;     Bind diredp-w32-drives to :/, diredp-dired-inserted-subdirs to C-M-i.
-;;     Bind diredp-add-to-dired-buffer to C-x D A (not C-x E), diredp-dired-union to C-x D U (not C-x D),
-;;          diredp-fileset to C-x D S (not C-M-f), diredp-dired-recent-dirs to C-x D R (not C-x R),
-;;          diredp-dired-for-files to C-x D F, plus other-window versions.
-;; 2017/03/24 dadams
-;;     Added defalias for dired-read-regexp.
-;;     diredp-mouse-3-menu: Removed second arg to mouse-save-then-kill.
-;; 2017/02/20 dadams
-;;     diredp-(next|previous)-line, diredp-(next|prev)-dirline, diredp-(next|prev)-subdir:
-;;       Update interactive spec to use (in effect) ^p for prefix arg (for shift-select-mode).
-;; 2017/01/12 dadams
-;;     dired-mark-files-regexp: Swapped prefix-arg behavior for relative and absolute name matching.
-;; 2017/01/01 dadams
-;;     dired-mark-files-regexp: Fix to prompt for no prefix arg.
-;; 2016/12/28 dadams
-;;     dired-mark-files-regexp: Corrected prompt string for Mark/UNmark.  Thx to Tino Calancha.
-;; 2016/11/20 dadams
-;;     diredp-menu-bar-operate-search-menu: Added dired-do-find-regexp and dired-do-find-regexp-and-replace.
-;;     Bind dired-do-search to M-a and dired-do-query(-replace)-regexp to M-q.
-;;     diredp-dired-plus-description: Added dired-do-find-regexp and dired-do-find-regexp-and-replace.
-;; 2016/10/12 dadams
-;;     diredp-compressed-extensions: Added extensions .xz and .lzma.  Thx to xuhdev (https://www.topbug.net/).
-;; 2016/09/20 dadams
-;;     Emacs 25.1: Bind M-z to dired-do-compress-to (replaces c).  (Emacs bug #24484.)
-;;                 diredp-menu-bar-operate-menu: Added item: Compress to (dired-do-compress-to).
-;; 2016/09/15 dadams
-;;     Added: diredp-max-frames.
-;;     dired-do-find-marked-files: Pass non-nil ARG to dired-get-marked-files only if it is a cons.
-;;                                 Clarified doc string wrt prefix arg.
-;;     dired-simultaneous-find-file: Require confirmation if more files than diredp-max-frames.
-;;     diredp-do-find-marked-files-recursive: Clarified doc string wrt prefix arg.
-;;     Thx to Tino Calancha.
-;; 2016/09/14 dadams
-;;     diredp-dired-plus-description: Added entry for dired-hide-details-mode - ( key.
-;; 2016/08/26 dadams
-;;     diredp-y-or-n-files-p: pop-to-buffer only when the buffer was created.
-;;                            Update wrt vanilla (scroll actions).
-;;     diredp-do-query-replace-regexp-recursive:
-;;       Call diredp-get-confirmation-recursive.
-;;       Use only diredp-get-files, not dired-get-marked-files.
-;;       Non-positive prefix arg means DELIMITED.
-;; 2016/08/08 dadams
-;;     diredp-menu-bar-mark-menu:
-;;       Added: dired-mark-files-containing-regexp, dired-mark-sexp, image-dired-mark-tagged-files, 
-;; 2016/05/28 dadams
-;;     diredp-mark-files-regexp-recursive: Use nil for dired-get-filename LOCALP arg.
-;;     dired-mark-files-regexp: Corrected doc string: absolute filename matching by default.
-;; 2016/05/24 dadams
-;;     dired-mark-files-regexp: Added optional arg LOCALP, so can mark/unmark matching different file-name forms.
-;; 2016/05/15 dadams
-;;     Added: diredp-bookmark-menu, diredp-hide/show-menu, diredp-navigate-menu.
-;;     Move insert after revert and rename it to Insert/Move-To This Subdir.  Move create-directory before revert.
-;; 2016/04/29 dadams
-;;     diredp-next-line: Respect goal-column.
-;; 2016/01/24 dadams
-;;     Added: diredp-ensure-bookmark+, diredp-mark-autofiles, diredp-unmark-autofiles,
-;;            diredp-mark/unmark-autofiles, diredp-describe-autofile, diredp-show-metadata,
-;;            diredp-mouse-describe-autofile, diredp-describe-marked-autofiles, diredp-show-metadata-for-marked
-;;     Soft-require help-fns+.el (Emacs 22+) or help+20.el (Emacs 20-21).
-;;     Add to menu-bar menus:
-;;       diredp-(un)mark-autofiles, diredp-describe-autofile, diredp-describe-marked-autofiles.
-;;     diredp-menu-bar-immediate-menu: Add diredp-describe-file only if defined.
-;;     Bind diredp-describe-file to keys only if defined.
-;;     Use diredp-ensure-bookmark+ everywhere, instead of its definition.
-;;     diredp(-mouse)-describe-file: Define only if describe-file is defined.  Removed raising error if not.
-;;     diredp-mouse-3-menu: Use  diredp-describe-autofile if diredp-describe-file is not defined.
-;;     diredp-dired-plus-description: Add diredp-mouse-describe-autofile, when bound.
-;;     dired-mark-if: Do not count non-changes.
-;; 2015/12/15 dadams
-;;     diredp-font-lock-keywords-1: Follow # with optional [/ ], for face diredp-number.  Thx to Tino Calancha.
-;; 2015/11/10 dadams
-;;     diredp-fileset(-other-window): Separate error msgs for unloaded filesets.el and empty filesets-data.
-;; 2015/10/02 dadams
-;;     dired-mark-sexp: Like vanilla, skip extended attributes marker before setting NLINK.  Thx to Tino Calancha.
-;; 2015/09/29 dadams
-;;     diredp-delete-this-file: Redefined to use delete-file instead of dired-do-delete.
-;; 2015/09/07 dadams
-;;     diredp-font-lock-keywords-1: Do not test diredp-ignore-compressed-flag when highlighting file names.
-;;                                  Use separate entries for compressed and non-compressed file names.
-;;                                  Added missing \\| before ignored compressed extensions.
-;; 2015/09/06 dadams
-;;     diredp-compressed-extensions: Added .tgz.  Removed duplicate .gz.
-;;     diredp-font-lock-keywords-1: Use regexp-opt where possible, instead of mapcar regexp-quote.
-;; 2015/09/05 dadams
-;;     Added: diredp-compressed-extensions, diredp-ignore-compressed-flag, diredp-compressed-file-name,
-;;            diredp-dir-name.
-;;     diredp-font-lock-keywords-1:
-;;       Allow spaces in symlink names.  Highlight compressed-file names, if diredp-ignore-compressed-flag.
-;;       Use diredp-compressed-extensions instead of hardcoding extensions.
-;;     Highlight d with diredp-dir-priv (fix).
-;;     Treat l in third column the same as - and d there.
-;;     Highlight whole line for D-flagged files, with face diredp-deletion-file-name.
-;;     Thx to Nick Helm.
-;; 2015/08/30 dadams
-;;     dired-mark-sexp: Updated per Emacs 25 code.
-;; 2015/07/30 dadams
-;;     diredp-fileset(-other-window): Changed key binding from C-x F to C-x C-M-f (conflicted with find-function).
-;; 2015/06/24 dadams
-;;     Added: diredp-parent-dir, diredp-breadcrumbs-in-header-line-mode, diredp-set-header-line-breadcrumbs.
-;; 2015/06/06 dadams
-;;     Added dired-other-(frame|window).
-;;     diredp-font-lock-keywords-1:
-;;       Use dired-re-maybe-mark and dired-re-inode-size for permission matchings and directory names.
-;;     dired(-other-(frame|window)) advice:
-;;       Add interactive spec, to handle arg <= 0 (broken by change to dired-read-dir-and-switches 2015/02/02).
-;;     diredp-dired-for-files: Typo: pass empy string.
-;; 2015/06/05 dadams
-;;     Added: diredp-grepped-files-other-window as alias for diredp-compilation-files-other-window.
-;;     diredp-compilation-files-other-window: Added SWITCHES optional arg (prefix arg).
-;; 2015/06/04 dadams
-;;     diredp-dired-for-files(-other-window):
-;;       Updated to fit change to dired-read-dir-and-switches made 2015/02/02: addition of READ-EXTRA-FILES-P.
-;;       Use prefix arg to prompt for switches.
-;; 2015/05/31 dadams
-;;     Added: diredp-image-show-this-file,diredp-image-show-this-file-use-frame-flag, diredp-get-image-filename.
-;;     image-dired-dired-toggle-marked-thumbs, diredp-menu-bar-immediate-menu [image]:
-;;       Use diredp-get-image-filename.
-;;     Bound diredp-image-show-this-file to C-t I.
-;;     diredp-menu-bar-immediate-image-menu: Added diredp-image-show-this-file and dired-find-file.
-;;     Added autoload cookies for image commands.
-;; 2015/04/16 dadams
-;;     Added: diredp-do-apply-function, diredp-do-apply-function-recursive.  Added to menus.  Bind to @, M-+ @.
-;;     dired-do-query-replace-regexp: Handle nil ARG properly.
-;; 2015/03/26 dadams
-;;     Added: redefinitions of dired-do-isearch, dired-do-isearch-regexp, dired-do-query-replace-regexp,
-;;            dired-do-search, to handle multi-C-u.
-;;     Added: dired-nondirectory-p (Emacs 20), diredp-refontify-buffer.
-;;     dired-do-byte-compile, dired-do-load, : Corrected interactive spec, to treat more than two C-u as two.
-;;     dired-after-readin-hook: Add diredp-refontify-buffer.  In particular, this ensures that reverting Dired
-;;       for a listing of explicit file names gets refontified.  (Just turn-on-font-lock does not refontify.)
-;; 2015/03/24 dadams
-;;     Added: diredp-compilation-files-other-window, diredp-file-for-compilation-hit-at-point.
-;; 2015/03/06 dadams
-;;     Renamed: diredp-menu-bar-recursive-marked-menu to diredp-menu-bar-operate-recursive-menu.
-;;     Added: diredp-do-delete-recursive: M-+ D.  Added to diredp-menu-bar-operate-recursive-menu.
-;;     Added: diredp-mark-files-regexp-recursive: M-+ % m.  Added to diredp-menu-bar-regexp-recursive-menu.
-;; 2015/03/04 dadams
-;;     Added: diredp-dwim-any-frame-flag, (redefinition of) dired-dwim-target-directory.
-;; 2015/02/22 dadams
-;;     diredp-bookmark: Corrected for use without Bookmark+ - bookmark-store signature.
-;;                      Pass correct value to bmkp-autofile-set for its MSG-P arg.
-;;     diredp-mouse-do-bookmark: Do not pass non-nil NO-MSG-P arg to diredp-bookmark.
-;; 2015/02/03 dadams
-;;     Added: diredp-add-to-this-dired-buffer.
-;;     Removed: diredp-add-to-dired-buffer-other-window, diredp-dired-union-other-window.
-;;     diredp-dired-union-1: Removed optional arg OTHER-WINDOW-P.
-;;     diredp-menu-bar-subdir-menu: Added diredp-add-to-this-dired-buffer.
-;;     dired-read-dir-and-switches, diredp-dired-union-interactive-spec:
-;;       Added optional arg DIRED-BUFFER.  If nil, use current buffer name as default when reading buffer name.
-;; 2015/02/02 dadams
-;;     Added: diredp-add-to-dired-buffer, diredp-add-to-dired-buffer-other-window, diredp-set-union,
-;;            diredp-existing-dired-buffer-p.
-;;     Bind diredp-add-to-dired-buffer(-other-window) globally to C-x E, C-x 4 E.
-;;     diredp-dired-union(-other-window):
-;;       Added args DIRNAME and EXTRA.  Pass them to diredp-dired-union-1.  Moved "UNION" to *-interactive-spec.
-;;       Pass values for new args NO-DIRED-BUFS and READ-EXTRA-FILES-P to diredp-dired-union-interactive-spec.
-;;     diredp-dired-union-interactive-spec:
-;;       Added args NO-DIRED-BUFS and READ-EXTRA-FILES-P.  Use (updated) dired-read-dir-and-switches.
-;;       Delete dead buffers from dired-buffers.  Remove DIRNAME buffer as candidate.
-;;       Apply expand-file-name to default-directory.  Return list of DIRNAME BUFS SWITCHES EXTRA-FILES.
-;;     diredp-dired-union-1:
-;;       Added args DIRED-NAME and EXTRA.
-;;       For existing Dired buffer whose dired-directory is a cons:
-;;         Include its current listing.  Replace buffer with new one of same name, after deleting its window.
-;;     dired-read-dir-and-switches:
-;;       Added arg READ-EXTRA-FILES-P.
-;;       If chosen Dired buffer exists and is an ordinary listing then start out with its directory-files.
-;;     diredp-dired-union, diredp-fileset, diredp-dired-recent-dirs: Bind globally, not just in Dired mode.
-;; 2015/01/30 dadams
-;;     dired-read-dir-and-switches: Remove any killed buffers from dired-buffers, before using for completion.
-;; 2014/10/25 dadams
-;;     diredp-dired-union-interactive-spec: Typo: quote buffer-name-history.  Pass other-window STRING.
-;;     diredp-dired-union-other-window: Pass other-window STRING.
-;;     dired-read-dir-and-switches: Include STRING for reading buffer name also.
-;;     dired (defadvice): Corrected doc string for prefix arg >= and <= 0.
-;; 2014/10/15 dadams
-;;     diredp-hide-details-initially-flag:
-;;       Added :set, to ensure that diredp-hide-details-last-state is kept up-to-date.
-;; 2014/09/28 dadams
-;;     Added: diredp-recent-dirs, diredp-read-include/exclude, diredp-root-directory-p, diredp-remove-if.
-;;     diredp-dired-recent-dirs(-other-window): Added optional ARG.  Use diredp-recent-dirs.  Pass SWITCHES.
-;;     dired-read-dir-and-switches: Use diredp-root-directory-p.
-;;     Bound diredp-dired-recent-dirs(-other-window) to C-x R and C-x 4 R.
-;;     Added diredp-dired-recent-dirs to Dir menu.
-;; 2014/09/27 dadams
-;;     Added: diredp-dired-recent-dirs, diredp-dired-recent-dirs-other-window, diredp-delete-dups.
-;; 2014/09/26 dadams
-;;     diredp-mouseover-help: dired-get-filename etc. has to be inside the save-excursion.
-;;     diredp-image-dired-create-thumb: Added FILE arg.  Use numeric prefix arg as the new thumbnail size.
-;; 2014/09/22 dadams
-;;     diredp-mouse-3-menu: Do not place overlay unless on a file/dir name (i.e., dired-get-filename).
-;; 2014/09/15 dadams
-;;     dired-read-dir-and-switches: Made it (thus dired too) an Icicles multi-command.
-;;     dired (defadvice): Added doc about using it with Icicles.
-;; 2014/09/14 dadams
-;;     Added: diredp-kill-this-tree.
-;;     Removed: diredp-dired-files(-other-window), diredp-dired-files-interactive-spec.
-;;     dired-read-dir-and-switches:
-;;       Based on diredp-dired-files-interactive-spec implementation now, but:
-;;        Moved unwind-protect outside call to list.  completing-read, not read-string, for DIRBUF.
-;;        Do not allow inclusion of root directories.  Protected icicle-sort-comparer with boundp.
-;;     dired-insert-subdir-validate: Make it a no-op.
-;;     dired advice (doc string): Mention wildcards, Icicles.
-;;     diredp-dired-for-files(-other-window):
-;;       Use dired-read-dir-and-switches and dired, not diredp-dired-files-interactive-spec and
-;;       diredp-dired-files.
-;;     diredp-menu-bar-immediate-menu, diredp-mouse-3-menu:
-;;       Added item for diredp-kill-this-tree.
-;;       Corrected visible condition: expand-file-name, so ~/ compares with its expansion.
-;;     diredp-font-lock-keywords-1: Include period (.) for diredp(-compressed)-file-suffix.
-;; 2014/09/09 dadams
-;;     Added: dired-read-dir-and-switches.
-;;     Advise dired, for doc string.
-;;     dired-get-filename: Hack for Emacs 20-22, to expand ~/...
-;; 2014/09/07 dadams
-;;     Added: redefinitions of dired-insert-subdir-newpos, dired-insert-subdir-validate.
-;; 2014/07/26 dadams
-;;     diredp-do-find-marked-files-recursive:
-;;       Only ARG >= 0 ignores marks now.  And ARG <= 0 means find but do not display.
-;; 2014/07/13 dadams
-;;     diredp-mouseover-help: Wrap (goto-char pos) in save-excursion (Emacs bug #18011).
-;; 2014/07/12 dadams
-;;     Faces diredp(-tagged)-autofile-name: Made paler/darker (less saturated).
-;;     Moved diredp-highlight-autofiles before diredp-highlight-autofiles-mode, so will be
-;;      defined for first revert.
-;;     diredp-mouse-3-menu: Renamed items Tag, Untag to Add Tags, Remove Tags.
-;;     diredp-dired-plus-description: Updated.
-;; 2014/07/11 dadams
-;;     Added: diredp-highlight-autofiles-mode, diredp-highlight-autofiles,
-;;            diredp-autofile-name, diredp-tagged-autofile-name.
-;;     Soft-require bookmark+.el.  Soft-require highlight.el if bookmark+.el is provided.
-;;     diredp-menu-bar-subdir-menu: Added item Toggle Autofile Highlighting.
-;;     Removed unused face: diredp-display-msg.
-;; 2014/06/29 dadams
-;;     dired-get-marked-files, diredp-internal-do-deletions:
-;;       Remove nils from dired-map-over-marks result.
-;; 2014/05/28 dadams
-;;     diredp-mode-line-marked: Use DarkViolet for both light and dark background modes.
-;; 2014/05/23 dadams
-;;     Added: diredp-with-help-window.
-;;     diredp-list-files, diredp-dired-plus-help:
-;;       Use diredp-with-help-window, not with-output-to-temp-buffer.  See Emacs bug #17109.
-;; 2014/05/06 dadams
-;;     Added: diredp-image-dired-required-msg, diredp-list-files-map,
-;;            diredp-find-line-file-other-window, diredp-mouse-find-line-file-other-window,
-;;            image-dired-dired-toggle-marked-thumbs, diredp-list-marked.
-;;     Soft-require image-dired.el and image-file.el.
-;;     diredp-image-dired-create-thumb: Define unconditionally.
-;;     image-dired-dired-insert-marked-thumbs, diredp-image-dired-comment-file, 
-;;       diredp-image-dired-tag-file, diredp-image-dired-delete-tag,
-;;       diredp-image-dired-display-thumb, diredp-image-dired-copy-with-exif-name,
-;;       diredp-image-dired-edit-comment-and-tags, diredp-do-display-images:
-;;         Define unconditionally and raise error if no image-(dired|file).el.
-;;     diredp-menu-bar-immediate-image-menu, diredp-menu-bar-images-menu,
-;;       diredp-menu-bar-images-recursive-menu, image-dired-mark-tagged-files:
-;;         Define unconditionally and use :enable.
-;;     diredp-menu-bar-images-menu, diredp-menu-bar-images-recursive-menu:
-;;       Add defalias so can use menu-item with :enable.
-;;     diredp-list-files: Add properties mouse-face, keymap, and help-echo.
-;;     diredp-mouseover-help: Make it work also for diredp-list-files listings.
-;;     image-dired-dired-insert-marked-thumbs: Add autoload cookie.
-;;     dired-get-marked-files: Pass non-nil 2nd arg to dired-get-filename, to include . and .. .
-;;     Bind diredp-list-marked to C-M-l and diredp-list-marked-recursive to M+ C-M-l.
-;;     diredp-insert-subdirs: Exclude . and .., as dired-get-marked-files can now include them.
-;;     diredp-menu-bar-operate-menu: Add diredp-menu-bar-operate-menu to menu.
-;;     diredp-dired-plus-description: Mention diredp-list-marked*.
-;; 2014/05/03 dadams
-;;     dired-switches-escape-p: Use dired-switches-check if available, based on bug #17218 fix.
-;; 2014/04/25 dadams
-;;     diredp-image-dired-create-thumb:
-;;       Do not call diredp-image-dired-create-thumb twice: reuse THUMB-NAME.
-;; 2014/04/24 dadams
-;;     Added: diredp-mouseover-help, diredp-auto-focus-frame-for-thumbnail-tooltip-flag,
-;;            diredp-image-preview-in-tooltip.
-;;     dired-insert-set-properties: Show image-file preview in tooltip.
-;;     diredp-image-dired-create-thumb: Return thumbnail file name or nil.
-;; 2014/04/23 dadams
-;;     Added: diredp-looking-at-p.
-;;     dired-insert-set-properties: Applied fix for bug #17228.
-;; 2014/04/05 dadams
-;;     Added: diredp-do-bookmark-dirs-recursive.
-;;            Renamed from bmkp-create-dired-bookmarks-recursive in bookmark+-1.el (removed).
-;;       Bound to M-B (aka M-S-b).
-;;       Added to menus *-subdir-menu, *-operate-bookmarks-menu, *-bookmarks-menu.
-;;     diredp-get-confirmation-recursive: Added optional TYPE arg.
-;;     diredp-insert-subdirs-recursive: Call diredp-get-confirmation-recursive with TYPE arg.
-;; 2014/02/16 dadams
-;;     dired-pop-to-buffer: Do not redefine for Emacs > 24.1.
-;;     dired-mark-pop-up: Updated doc string.
-;; 2014/02/13 dadams
-;;     Added: diredp-fileset-other-window, diredp-fileset-1.
-;;     diredp-fileset: Use diredp-fileset-1.
-;;     Bind diredp-dired-union(-other-window) to C-x D, C-x 4 D,
-;;          diredp-fileset(-other-window)     to C-x F, C-x 4 F.
-;;     Use diredp-fileset-other-window, not diredp-fileset, in menu.
-;; 2014/02/03 dadams
-;;     Added: diredp-hide-subdir-nomove.
-;;     Added: dired-goto-file for Emacs 24+ - open hidden parent dir, so can goto destination.
-;;     Replace bindings for dired-hide-subdir with diredp-hide-subdir-nomove.
-;;     Bind dired-hide-subdir to M-$ (not $).
-;; 2014/02/02 dadams
-;;     dired-goto-file: Redefine only for Emacs < 24. 
-;; 2014/01/15 dadams
-;;     Bind diredp-toggle-find-file-reuse-dir to C-M-R (aka C-M-S-r).
-;; 2014/01/05 dadams
-;;     Bind dired-omit-mode (aka dired-omit-toggle) to C-x M-o.
-;; 2013/12/05 dadams
-;;     diredp-do-grep-1: Call grep-default-command with arg, if grep+.el is loaded.
-;; 2013/11/05 dadams
-;;     Added: diredp-get-subdirs.
-;;     diredp-get-files, diredp-get-files-for-dir, diredp-marked-here: Added optional arg NIL-IF-NONE-P.
-;;     diredp-get-files: Pass INCLUDE-DIRS-P to diredp-files-within.
-;; 2013/11/04 dadams
-;;     Renamed Bookmarks submenus to Bookmark.
-;;     Added Bookmark Dired Buffer to Dir menu.
-;;     Alias dired-toggle-marks to dired-do-toggle for Emacs 20, instead of backwards for others.
-;;     Use dired-toggle-marks everywhere instead of dired-do-toggle.
-;; 2013/11/03 dadams
-;;     Created submenus of Multiple menu: Bookmarks, Search.
-;;     Created submenus of Multiple > Marked Here and Below menu:
-;;       Images, Encryption, Search, Bookmarks.
-;;     Reordered menus.
-;; 2013/09/26 dadams
-;;     diredp-next-line: Use let*, so line-move sees let bindings.
-;; 2013/08/11 dadams
-;;     diredp-dired-files-interactive-spec:
-;;       Protect icicle-file-sort with boundp.  Thx to Vladimir Lomov.
-;; 2013/08/06 dadams
-;;     diredp-display-image,diredp-menu-bar-immediate-image-menu (:enable's):
-;;       Protect diredp-string-match-p from nil argument.
-;; 2013/07/24 dadams
-;;     Added: diredp-nonempty-region-p.  Use everywhere, in place of its definition.
-;; 2013/07/21 dadams
-;;     Added: diredp-image-dired-(comment-file|copy-with-exif-name|(create|display)-thumb|
-;;                                delete-tag|edit-comment-and-tags|tag-file),
-;;            diredp-string-match-p, diredp-menu-bar-immediate-image-menu.
-;;     Put this-file image commands on new menu diredp-menu-bar-immediate-image-menu.
-;;     diredp-menu-bar-images-menu: Added diredp-do-display-images.
-;;     Use diredp-string-match-p instead of string-match where appropriate.
-;;     diredp-find-a-file-read-args: Removed #' from lambda.
-;; 2013/07/19 dadams
-;;     Added redefinition of dired-hide-details-mode.
-;;     Added: diredp-hide-details-propagate-flag, diredp-hide-details-initially-flag,
-;;            diredp-hide-details-last-state, diredp-hide-details-toggled,
-;;            diredp-hide-details-if-dired, global-dired-hide-details-mode,
-;;            diredp-fit-frame-unless-buffer-narrowed, diredp-hide/show-details,
-;;            diredp-do-display-images, diredp-display-image.
-;;     On dired-after-readin-hook: diredp-hide/show-details.
-;;     On dired-hide-details-mode-hook: diredp-fit-frame-unless-buffer-narrowed.
-;;     diredp-maplist: Use diredp-maplist, not maplist, in recursive call.
-;;     diredp-next-line: Added bobp test for negative ARG.
-;;                       Emacs 20 line-move returns nil, so use (progn ... t).
-;;     Soft-require autofit-frame.el.
-;; 2013/07/18 dadams
-;;     diredp-next-line: Protect visible-p with fboundp for Emacs 20.
-;; 2013/07/17 dadams
-;;     Added: diredp-menu-bar-encryption-menu, diredp-menu-bar-images-menu,
-;;            diredp-menu-bar-immediate-encryption-menu,
-;;            diredp-(decrypt|verify|sign|encrypt)-this-file.
-;;     Added diredp-(decrypt|verify|sign|encrypt)-this-file to *-immediate-encryption-menu.
-;;     Moved encryption and image-dired items to the new Multiple submenus from Multiple menu.
-;; 2013/07/15 dadams
-;;     Added: diredp-async-shell-command-this-file, diredp-do-async-shell-command-recursive.
-;;            Added them to menus.  Bind diredp-do-async-shell-command-recursive to M-+ &.
-;;     diredp-menu-bar-mark-menu, diredp-dired-plus-description: Added dired-mark-omitted.
-;;     diredp-menu-bar-subdir-menu: Added dired-omit-mode, dired-hide-details-mode.
-;;     diredp-menu-bar-regexp-menu: Added image-dired-mark-tagged-files.
-;;     diredp-menu-bar-subdir-menu: Added dired-hide-details-mode.
-;;     diredp-shell-command-this-file: Corrected: provide file list to dired-do-shell-command.
-;; 2013/07/13 dadams
-;;     diredp-font-lock-keywords-1:
-;;       Ensure diredp-dir-priv is not used for directory header of d:/... (Windows drive name).
-;;     dired-insert-directory:
-;;       Update wrt Emacs 24.4: Do dired-insert-set-properties last; use saved CONTENT-POINT.
-;;     dired-insert-set-properties: Updated for Emacs 24.4, for dired-hide-details-mode.
-;;     Add frame-fitting to dired-hide-details-mode-hook.
-;;     dired-mouse-find-file(-other-window): Error msg if click off a file name.
-;; 2013/07/12 dadams
-;;     Added: diredp-wrap-around-flag, diredp-(next|previous)-(subdir|(dir)line). 
-;;     Renamed dired-up-directory to diredp-up-directory.
-;;     Replaced vanilla commands by these new commands everywhere.
-;; 2013/07/11 dadams
-;;     Added: diredp-up-directory-reuse-dir-buffer.
-;;     diredp-make-find-file-keys(-not)-reuse-dirs: Added diredp-up-directory-reuse-dir-buffer.
-;; 2013/02/06 dadams
-;;     dired-mark-pop-up: goto point-min, so show start of file list.  Thx to Michael Heerdegen.
-;; 2013/01/28 dadams
-;;     Added redefinition of dired-do-run-mail.  Fixes Emacs bug #13561.
-;; 2012/12/18 dadams
-;;     diredp-ediff: Better default for FILE2.  Thx to Michael Heerdegen.
-;;     Require subr-21.el for Emacs 20.
-;; 2012/11/17 dadams
-;;     Added: derived-mode-p (for Emacs < 22), diredp-ensure-mode.
-;;     Use diredp-ensure-mode everywhere for mode, so compatible with Sunrise Commander etc.
-;; 2012/11/01 dadams
-;;     Do not require ediff.el.  It is required in diredp-ediff itself.
-;; 2012/10/06 dadams
-;;     Added: minibuffer-with-setup-hook for code byte-compiled using Emacs < 22.
-;; 2012/09/28 dadams
-;;     Moved dired-*w32* bindings after normal mouse bindings, so they override them.
-;; 2012/09/05 dadams
-;;     diredp-(rename|copy|(rel)symlink|hardlink)-this-file: Bind use-file-dialog to nil.
-;; 2012/08/26 dadams
-;;     Set font-lock-defaults to a 3-element list, so it works with font-menus(-da).el.
-;; 2012/08/25 dadams
-;;     Added: redefinition of dired-pop-to-buffer (fix for bug #12281).
-;;     dired-mark-pop-up: If buffer is shown in a separate frame, do not show menu bar.
-;; 2012/07/10 dadams
-;;     Removed unneeded substitute-key-definition for (next|previous)-line.
-;; 2012/07/09 dadams
-;;     Added redefinition of dired-mark-files-regexp: Push REGEXP onto regexp-search-ring.
-;; 2012/06/21 dadams
-;;     diredp-nb-marked-in-mode-name:
-;;       Add marker numbers regardless of name match.
-;;       Use text property dired+-mode-name to tell whether mode-name was already changed.
-;; 2012/06/20 dadams
-;;     Added: diredp-nb-marked-in-mode-name, diredp-mode-line-(flagged|marked).  Added to hooks.
-;;     Thx to Michael Heerdegen.
-;; 2012/06/14 dadams
-;;     dired-mark-pop-up: Wrap save-excursion around window/frame deletion.
-;;     dired-do-redisplay: Updated wrt Emacs 23: bind, (then run) dired-after-readin-hook.
-;;     diredp-y-or-n-files-p: Corrected construction of prompt wrt final SPC.
-;; 2012/06/13 dadams
-;;     dired-buffers-for-dir: Updated wrt Emacs 23:
-;;       If dired-directory is a list then expand FILE in DIR & check whether in cdr of list.
-;;     diredp-get-files-for-dir, diredp-files-within-1, diredp-insert-as-subdir:
-;;       Expand dir name before passing it to dired-buffers-for-dir.
-;; 2012/06/05 dadams
-;;     MS Windows: Just do not define commands that are inappropriate for Windows (instead of
-;;       defining them to raise an error or making them invisible in menus).
-;; 2012/06/02 dadams
-;;     Added: diredp-do-(print|encrypt|decrypt|sign|verify)-recursive.  Menus.  Keys.
-;;     diredp-do-move-recursive: Corrected to use dired-rename-file, not dired-copy-file.
-;; 2012/05/30 dadams
-;;     diredp-insert-as-subdir: Added optional arg IN-DIRED-NOW-P.  Pick up markings & switches
-;;                              from sole Dired buffer for CHILD if not in Dired now.
-;; 2012/05/29 dadams
-;;     Added: diredp-do-(chxxx|chgrp|chown|touch)-recursive, diredp-touch-this-file,
-;;       diredp-menu-bar-(immediate|operate)-bookmarks-menu.  Added to menus.  Bound to keys.
-;;     Factored bookmark stuff into Bookmark(s) submenus.
-;;     diredp-menu-bar-immediate-menu: Added dired-kill-subdir, [goto-subdir].
-;;     diredp-dired-this-subdir, dired-maybe-insert-subdir: Corrected :visible/enable.
-;;     diredp-dired-inserted-subdirs: Do dired-(remember-marks|mark-remembered) in this-buff.
-;;     diredp-mouse-3-menu:
-;;       Do not use save-excursion, because some commands move point on purpose.  Just return to
-;;         original point unless command intends to MOVEP.
-;;       Added to menu dired-maybe-insert-subdir (two entries), dired-kill-subdir.
-;;       Use *-this-file*, not *-do-*: copy|symlink|shell-command|grep|load (don't use :keys).
-;; 2012/05/26 dadams
-;;     diredp-dired-inserted-subdirs, diredp-insert-as-subdir:
-;;       Preserve markings and switches in target buffer.
-;;     dired-mark-pop-up: Use unwind-protect.  Bury buffer too.
-;;     diredp-do-chmod-recursive: Use only 5 args if < Emacs 23.
-;; 2012/05/25 dadams
-;;     Added: diredp-insert-as-subdir, diredp-ancestor-dirs, diredp-maplist,
-;;            diredp-do-redisplay-recursive, diredp-do-chmod-recursive.
-;;            Bound diredp-do-chmod-recursive. to M-+ M and added to menu.
-;;     diredp-get-files: Added optional arg DONT-ASKP.
-;;     diredp-y-or-n-files-p: Kill list buffer if it was never shown.
-;;     dired-mark-pop-up: ignore error when delete frame/window.
-;; 2012/05/22 dadams
-;;     diredp-get-files(-for-dir): Added optional arg INCLUDE-DIRS-P.
-;;     Added: diredp-insert-subdirs(-recursive), diredp(-this)-dired-inserted-subdir(s).
-;;            Added to menus.  Bound diredp-insert-subdirs* to (M-+) M-i.
-;;     Bound diredp-capitalize(-recursive) to (M-+) %c.
-;;     Added diredp-dired-union-other-window to Dirs menu.
-;;     Updated diredp-dired-plus-description.
-;; 2012/05/19 dadams
-;;     Added: diredp-image-dired-*-recursive, diredp-*link-recursive,
-;;            diredp-do-isearch(-regexp)-recursive, diredp-do-query-replace-regexp-recursive,
-;;            diredp-do-search-recursive, diredp-(capitalize|(up|down)case)-recursive,
-;;            diredp-create-files-non-directory-recursive.
-;;              Bound on M-+ prefix key.  Added to menus.
-;;     diredp-get-files, diredp-y-or-n-files-p, diredp-list-files, diredp-list-marked-recursive:
-;;       Added optional arg PREDICATE.
-;;     diredp-do-create-files-recursive: Removed MARKER-CHAR arg.  Hard-code to keep markings.
-;;     diredp-do-(copy|move)-recursive: Use arg IGNORE-MARKS-P (forgot to use it).
-;;                                      Removed MARKER-CHAR arg in call to d-d-c-f-r.
-;;     Added missing autoload cookies.
-;; 2012/05/06 dadsms
-;;     diredp-y-or-n-files-p: Do not kill buffer *Files* - just bury it.
-;; 2012/05/05 dadams
-;;     Added: diredp-do-bookmark-recursive, diredp-do-bookmark-in-bookmark-file-recursive,
-;;            diredp-set-bookmark-file-bookmark-for-marked-recursive.
-;;              Bound to M-+ M-b, M-+ C-M-B (aka C-M-S-b), M-+ C-M-b, respectively.  Added to menus.
-;;     diredp-bookmark: Added optional arg FILE.
-;;     diredp-do-bookmark-in-bookmark-file: Added optional arg FILES.
-;;     diredp-dired-plus-description: Updated.
-;;     diredp-get-confirmation-recursive: Raise error if not in Dired.
-;;     diredp-do-bookmark-recursive, diredp-marked-recursive(-other-window),
-;;       diredp-multiple-w32-browser-recursive:
-;;         Use diredp-get-confirmation-recursive.
-;; 2012/05/04 dadams
-;;     Added: dired-mark-unmarked-files for Emacs < 24.
-;;     diredp-do-create-files-recursive: Corrected for Emacs < 24.
-;;     diredp-do-create-files-recursive, diredp-(un)mark-files-tagged-regexp,
-;;       diredp(-mouse)-do-(un)tag, diredp(-mouse)-do-remove-all-tags,
-;;       diredp(-mouse)-do-paste-(add|replace)-tags, diredp(-mouse)-do-set-tag-value,
-;;       diredp(-mouse)-do-bookmark(-in-bookmark-file), diredp-find-a-file-read-args,
-;;       diredp-mouse-do-shell-command:
-;;         Use lexical-let(*), to get closures for free vars in lambdas.
-;; 2012/04/28 dadams
-;;     Added:
-;;       diredp-copy-filename-as-kill-recursive, diredp-do-copy-recursive,
-;;       diredp-do-find-marked-files-recursive, diredp-do-grep-recursive,
-;;       diredp-do-move-recursive, diredp-do-shell-command-recursive,
-;;       diredp-list-marked-recursive, diredp-marked-recursive(-other-window),
-;;       diredp-multiple-w32-browser-recursive, diredp-do-create-files-recursive,
-;;       diredp-get-confirmation-recursive, diredp-list-files, diredp-y-or-n-files-p,
-;;       diredp-menu-bar-recursive-marked-menu.
-;;     diredp-get-files: Use diredp-y-or-n-files-p, not y-or-n-p.
-;;     Commented out dired-readin-insert - see comment.
-;;     Moved bookmark menu items to submenu Bookmarks.
-;;     Added keys (with M-+ prefix) and menu items for new (*-recursive) commands.
-;;     Reordered w32-browser stuff in menus.
-;;     diredp-do-grep: Combined defs for diff Emacs versions - do version test at runtime.
-;; 2012/04/25 dadams
-;;     dired-insert-directory: Updated per Emacs 24.
-;; 2012/04/23 dadams
-;;     Added (moved here from Icicles, and renamed prefix):
-;;       diredp-re-no-dot, diredp-get-files, diredp-get-files-for-dir, diredp-files-within,
-;;       diredp-files-within-dirs-done.
-;; 2012/04/05 dadams
-;;     Added redefinition of dired-mark-pop-up, to fix Emacs bug #7533.  If they ever fix it
-;;     then remove this hack.
-;; 2012/03/13 dadams
-;;     diredp-dired(-for)-files(-other-window):
-;;       Bind: icicle-sort-comparer, icicle-all-candidates-list-alt-action-fn.
-;;       Use icicle-(un)bind-file-candidate-keys.
-;;     diredp-dired-files-interactive-spec: Updated doc strings accordingly.
-;; 2012/03/07 dadams
-;;     Added: dired-switches-escape-p.
-;;     dired-get-filename: Updated wrt Emacs 24:
-;;       whitespace quoting for bug #10469, filename quoting per Emacs 23.3+,
-;;         MS Windows conversion of \ to / per Emacs 23.3+.
-;;     dired-goto-file: Escape whitespace, per Emacs 24 (for bug #10469).
-;; 2012/03/02 dadams
-;;     Require cl.el at compile time even for Emacs 22+, for case macro.
-;; 2012/02/28 dadams
-;;     Do not bother to soft-require mkhtml.el anymore.
-;; 2012/02/18 dadams
-;;     Swapped keys for dired-w32(-browser|explore), so the former is M-RET, as in Bookmark+.
-;; 2012/01/10 dadams
-;;     diredp-font-lock-keywords-1: Corrected for date/time when locale is used, not iso.
-;; 2011/12/19 dadams
-;;     dired-insert-set-properties, dired-mark-sexp, diredp-(un)mark-region-files,
-;;       diredp-flag-region-files-for-deletion, diredp-mouse-3-menu:
-;;         Use line-(beginning|end)-position.
-;; 2011/12/16 dadams
-;;     diredp-menu-bar-mark-menu: Removed Revert item.
-;;     diredp-menu-bar-subdir-menu: Add image-dired-dired-toggle-marked-thumbs.
-;;     diredp-mouse-3-menu:
-;;       Use commands bound to keys, so the keys show up in the menu.  Prefer *-this-file.
-;;       Correct the mark/unmark/flag menu-item visibility.  Added Capitalize.
-;; 2011/12/09 dadams
-;;     diredp-w32-drives: Use dolist, not mapcar.
-;;     diredp-mouse-3-menu: Use easymenu to build the menu.  Conditionalize some items.
-;;     Bind down-mouse-3, not mouse-3, to diredp-mouse-3-menu.  (bind mouse-3 to ignore).
-;;     Added eval-when-compile for easymenu.el.
-;; 2011/12//02 dadams
-;;     Added diredp-internal-do-deletions.
-;;     dired(-mouse)-do(-flagged)-delete, : Use diredp-internal-do-deletions, for trash.
-;; 2011/11/29 dadams
-;;     diredp-read-bookmark-file-args: Corrected use of list of default file names: > Emacs 23.1.
-;; 2011/10/31 dadams
-;;     dired-mode-hook: Call font-lock-refresh-defaults - see Emacs 24 bugs #6662 and #9919.
-;; 2011/10/24 dadams
-;;     Protect dired-show-file-type with fboundp.
-;; 2011/09/03 dadams
-;;     diredp-do-grep-1: Map shell-quote-argument over file names.  Thx to Joe Bloggs.
-;; 2011/08/07 dadams
-;;     diredp-bookmark (need to keep in sync with bmkp-make-record-for-target-file):
-;;       Instead of image-bookmark-make-record, use explicit function that includes file, type.
-;; 2011/07/25 dadams
-;;     Changed featurep to eval-after-load, for bookmark+-1.el and w32-browser.el.
-;; 2011/07/01 dadams
-;;     Fixed typo: dired-toggle-find-file-reuse-dir -> ...diredp....  Thx to pasja on Emacs Wiki.
-;; 2011/06/18 dadams
-;;     Added: diredp-describe-mode, diredp-dired-plus-help(-link), diredp-help-button,
-;;            diredp-dired-plus-description(+links), diredp-send-bug-report.
-;;     Bound diredp-describe-mode to whatever describe-mode is bound to.
-;;     All menus, :enable with mark-active: Added transient-mark-mode and mark != point.
-;;     toggle-diredp-find-file-reuse-dir: Swapped which one is the alias.
-;;     diredp-w32-list-mapped-drives: Display *Shell Command Output* at end.
-;;     diredp-mouse-(describe-file|3-menu|mark/unmark|(find|view)-file(-other-window)):
-;;       save-excursion set-buffer -> with-current-buffer.
-;; 2011/06/08 dadams
-;;     Added: diredp-dired-for-files(-other-window).
-;; 2011/06/07 dadams
-;;     Bound dired-show-file-type to _, since y is diredp-relsymlink-this-file.
-;; 2011/04/25 dadams
-;;     Added (from files+.el): dired(-mouse)-describe-file. Bound to C-h (C-)RET, added to menus.
-;; 2011/04/23 dadams
-;;     Added, bound (T c, T M-w, T 0, T v, T p, T C-y, T q), and added to menus:
-;;       diredp-copy-tags-this-file, diredp-mouse-copy-tags,
-;;       diredp(-mouse)(-do)-remove-all-tags(-this-file),
-;;       diredp(-mouse)(-do)-set-tag-value(-this-file),
-;;       diredp(-mouse)(-do)-paste-(add|replace)-tags(-this-file).
-;;     diredp-mark-files-tagged-(all/none|some/not-all): Bound free var presentp.
-;;     dired-map-over-marks: Corrected: Bind NEWARG and use that, not ARG.
-;;     dired-get-marked-files: let* -> let.
-;;     dired-do-redisplay, diredp-mouse-diff: when/if -> and.
-;;     dired-readin-insert, dired-get-filename: if -> unless/when.
-;;     diredp-mouse-find-file-reuse-dir-buffer: with-current-buffer, not save...
-;;     diredp-mouse-mark/unmark: Removed unused bol/eol vars.
-;; 2011/04/19 dadams
-;;     Added: diredp-(un)mark-files-tagged-((not-)all|none|some|regexp|all/none|some/not-all),
-;;            dired-mark-if.  Added Tagged submenu for Mark menu.
-;;     Put tags commands on prefix key T, as in Bookmark+.  Removed C-(M-)+/- tags-cmd bindings.
-;;     diredp-untag-this-file: Added prefix-arg behavior.
-;; 2011/04/18 dadams
-;;     Added: diredp-prompt-for-bookmark-prefix-flag.
-;;            Use it in diredp(-mouse)-do-(un)tag, diredp-read-bookmark-file-args,
-;;                      diredp(-mouse)-do-bookmark, diredp-(bookmark|(un)tag)-this-file.
-;;     diredp-(bookmark|(un)tag)-this-file, diredp(-do)-bookmark, diredp-(un)tag,
-;;       diredp-do-bookmark-in-bookmark-file, diredp-set-bookmark-file-bookmark-for-marked:
-;;         Made PREFIX arg optional.
-;; 2011/04/17 dadams
-;;     Added: diredp-(bookmark|(un)tag)-this-file, diredp(-mouse)(-do)-(un)tag.
-;;     diredp-mouse-3-menu: Added: diredp-mouse-do-(un)tag.
-;;     diredp-menu-bar-immediate-menu: Added diredp-(un)tag-this-file, diredp-bookmark-this-file.
-;;     diredp-menu-bar-operate-menu: Added diredp-do-(un)tag.
-;;     Bound diredp-do-tag to C-+, diredp-tag-this-file to C-M-+, diredp-do-untag to C--,
-;;           diredp-untag-this-file to C-M--, diredp-bookmark-this-file to C-B.
-;;     diredp-bookmark: Use bmkp-autofile-set, not bmkp-file-target-set, so get autofile.
-;;     diredp-read-bookmark-file-args, diredp(-mouse)-do-bookmark:
-;;       Default for prefix is now an empty string, not the directory.
-;;     diredp-mouse-do-bookmark: Removed optional second arg.
-;;     Corrected typo: direp-read-bookmark-file-args -> diredp-read-bookmark-file-args.
-;; 2011/03/25 dadams
-;;     diredp-bookmark: Fixed typo: bmkp-file-indirect-set -> bmkp-file-target-set.
-;; 2011/02/11 dadams
-;;     diredp-deletion, diredp-deletion-file-name, diredp-executable-tag:
-;;       Made default the same for dark background as for light.
-;;     diredp-ignored-file-name: Made default a bit darker for dark background.
-;; 2011/02/03 dadams
-;;     All deffaces: Provided default values for dark-background screens too.
-;; 2011/01/12 dadams
-;;     dired-do-flagged-delete: Removed sit-for added on 1/02.
-;; 2011/01/04 dadams
-;;     defsubst -> defun everywhere.
-;;     Removed autoload cookies from non def* sexps, defvar, and non-interactive functions.
-;;     Added some missing autoload cookies for defcustom and commands.
-;; 2011/01/02 dadams
-;;     Added: diredp-this-file-(un)marked-p, diredp-toggle-marks-in-region.
-;;     diredp-(un)mark-region-files, diredp-flag-region-files-for-deletion:
-;;       Act only on marked/unmarked files (opposite).  Fix 2nd arg to dired-mark-if.
-;;     diredp-mouse-3-menu:
-;;       If region is active and mouse3.el was loaded, then use its popup.
-;;       Fix Toggle Marked/Unmarked:
-;;         Use diredp-toggle-marks-in-region, so widen, show details and use bol/eol.
-;;     dired-do-flagged-delete: Added sit-for.
-;; 2010/11/28 dadams
-;;     diredp-mouse-3-menu: Added Toggle Marked/Unmarked for region menu.
-;; 2010/10/20 dadams
-;;     Moved Emacs 20 tweak to recognize k in file sizes to var dired-move-to-filename-regexp.
-;;     Added diredp-loaded-p.
-;; 2010/10/19 dadams
-;;     diredp-font-lock-keywords-1:
-;;       Handle decimal pt in file size.  Thx to Michael Heerdegen.
-;;       Enable Emacs 20/21 to handle -h option (decimal point size).
-;;     Renamed: face diredp-inode+size to diredp-number.
-;; 2010/10/01 dadams
-;;     dired-goto-file: Avoid infloop from looking for dir line.  Thx to not-use.dilines.net.
-;; 2010/09/29 dadams
-;;     Added: diredp-dired-union(-1|-other-window|-interactive-spec).
-;;     dired-goto-file: fix for Emacs bug #7126.
-;; 2010/09/27 dadams
-;;     Renamed diredp-dired-interactive-spec to diredp-dired-files-interactive-spec.
-;;     diredp-dired-files-interactive-spec: Respect file-list arg: kill existing Dired buffer.
-;;                                          Fix use of prefix arg for switches.
-;; 2010/09/26 dadams
-;;     Added: dired-insert-directory: Compute WILDCARD arg for individual files.
-;;     Added: dired-readin-insert: Use t as WILDCARD arg to dired-insert-directory.
-;;     Added: diredp-dired-files(-other-window), diredp-dired-interactive-spec.
-;; 2010/08/27 dadams
-;;     Use diredp-font-lock-keywords-1 properly as a second level of fontification.
-;;     Added: diredp-w32-drives(-mode(-map)), dired-up-directory.
-;; 2010/08/07 dadams
-;;     dired-map-over-marks: Removed loop that used dired-between-files.
-;;     diredp-get-file-or-dir-name: test against subdir/..? also.
-;;     dired-do-find-marked-files: Pass original ARG to dired-get-marked-files.
-;; 2010/08/05 dadams
-;;     diredp-bookmark:
-;;       Handle image files (and sound files, if Bookmark+ is used).
-;;       Use bmkp-file-indirect-set if available.
-;;       Use error-message-string to get failure msg.
-;; 2010/07/11 dadams
-;;     Added: diredp-set-bookmark-file-bookmark-for-marked (C-M-b), diredp-mouse-do-bookmark,
-;;            diredp-do-bookmark-in-bookmark-file (C-M-B, aka C-M-S-b), diredp-read-bookmark-file-args.
-;;     Added them to the operate menu.  Added diredp-do-bookmark to mouse-3 menu.
-;; 2010/07/07 dadams
-;;     dired-do-*: Updated doc strings for prefix arg treatment from dired-map-over-marks-check.
-;;     Added missing autoload cookies.
-;; 2010/05/29 dadams
-;;     diredp-bookmark: Use relative file name in bookmark name.
-;;     Removed defvar of directory-listing-before-filename-regexp.
-;; 2010/05/28 dadams
-;;     Changed menu item for dired-create-directory to New Directory.  Moved it before Up Dir.
-;; 2010/03/19 dadams
-;;     diredp-font-lock-keywords-1: Handle date+time wrt regexp changes for Emacs 23.2.
-;; 2010/01/31 dadams
-;;     diredp-bookmark:
-;;       Don't use bookmark-set or find-file-noselect - inline the needed bookmark-store code.
-;;       Call bookmark-maybe-load-default-file.  Use rudimentary bookmark-make-record-function.
-;; 2010/01/21 dadams
-;;     Renamed:
-;;       diredp-subst-find-alternate-for-find to diredp-make-find-file-keys-reuse-dirs
-;;       diredp-subst-find-for-find-alternate to diredp-make-find-file-keys-not-reuse-dirs.
-;;     diredp-make-find-file-keys(-not)-reuse-dirs: Handle also dired(-mouse)-w32-browser.
-;; 2010/01/10 dadams
-;;     Added: face diredp-inode+size.  Use in diredp-font-lock-keywords-1.
-;;     diredp-font-lock-keywords-1: Allow decimal point in file size.  Thx to Regis.
-;; 2010/01/05 dadams
-;;     dired-insert-set-properties:
-;;       Add text property dired-filename to the file name (for Emacs 23).
-;; 2009/10/23 dadams
-;;     diredp-font-lock-keywords-1: Override `l' and `t' matches in headings with default face.
-;; 2009/10/13 dadams
-;;     Added: diredp(-do)-bookmark.  Added to Multiple menu, and bound to M-b.
-;; 2009/10/11 dadams
-;;     diredp-menu-bar-immediate-menu:
-;;       Added items: image display items, dired-maybe-insert-subdir.
-;;       Test dired-do-relsymlink, not diredp-relsymlink-this-file.
-;;     diredp-menu-bar-operate-menu:
-;;       Added items: epa encryption items, image items, isearch items.
-;;     diredp-menu-bar-subdir-menu:
-;;       Added items: revert, isearch file names, dired-compare-directories.
-;;     Removed macro menu-item-any-version - use menu-item everywhere (works for Emacs 20+).
-;;     Added wdired-change-to-wdired-mode to subdir menu even for Emacs 20, if defined.
-;; 2009/07/09 dadams
-;;     dired-goto-file: Make sure we have a string before calling directory-file-name.
-;; 2009/05/08 dadams
-;;     dired-find-file (Emacs 20): Raise error if dired-get-filename returns nil.
-;; 2009/04/26 dadams
-;;     dired-insert-set-properties, diredp-(un)mark-region-files,
-;;       diredp-flag-region-files-for-deletion, diredp-mouse-3-menu, diredp-mouse-mark/unmark:
-;;         Bind inhibit-field-text-motion to t, to ensure real eol.
-;; 2008/12/17 dadams
-;;     diredp-font-lock-keywords-1: Don't do diredp-deletion, diredp-flag-mark for empty lines.
-;; 2008/09/22 dadams
-;;     Added: diredp-fileset, diredp-get-file-or-dir-name, and redefinitions of
-;;            dired-map-over-marks, dired-find-file, and dired-mouse-find-file-other-window.
-;;     Added vanilla code to pick up macro dired-map-over-marks:
-;;       dired-get-marked-files, dired-do-delete, dired-map-over-marks-check,
-;;       dired-do-redisplay, image-dired-dired-insert-marked-thumbs.
-;;     diredp-find-file-other-frame, diredp-mouse-(find|view)-file:
-;;       Added nil t args to dired-get-filename calls.
-;;     diredp-do-grep(-1): Use new dired-get-marked-files instead of ad-hoc treatment of C-u.
-;; 2008/09/21 dadams
-;;     diredp-marked(-other-window): Don't treat zero prefix arg as numerical (no empty Dired).
-;;     Added dired-find-file redefinition for Emacs 20.
-;; 2008/09/11 dadams
-;;     diredp-do-grep: Plain C-u means grep all files in Dired buffer.
-;;     diredp-do-grep-1: Treat 'all value of FILES arg.
-;;     Added: diredp-all-files.
-;; 2008/09/09 dadams
-;;     Added: diredp-marked(-other-window).  Added to menus.  Bound *-other-window to C-M-*.
-;; 2008/09/07 dadams
-;;     Added: diredp(-mouse)-do-grep(-1), diredp-grep-this-file.
-;;     Bound diredp-do-grep to M-g.  Added grep commands to menus.
-;; 2008/07/18 dadams
-;;     Soft-require w32-browser.el.  Bind its commands in Dired map and menus.
-;; 2008/03/08 dadams
-;;     dired-maybe-insert-subdir: Fit one-window frame after inserting subdir.
-;; 2008/03/07 dadams
-;;     Added: redefinitions of dired-maybe-insert-subdir, dired-goto-file, dired-get-filename.
-;;     Added: diredp-this-subdir.
-;; 2007/11/27 dadams
-;;     diredp-mouse(-backup)-diff: If available, use icicle-read-string-completing.
-;; 2007/09/23 dadams
-;;     Removed second arg to undefine-killer-commands.
-;; 2007/07/27 dadams
-;;     diredp-font-lock-keywords-1: Allow also for bz2 compressed files - Thx to Andreas Eder.
-;; 2006/09/03 dadams
-;;     diredp-font-lock-keywords-1: Corrected file size and inode number.  Thx to Peter Barabas.
-;; 2006/08/20 dadams
-;;     Added: diredp-find-a-file*.
-;; 2006/06/18 dadams
-;;     diredp-font-lock-keywords-1: Highlight file name (also) of flagged files.
-;;                                  Use dired-del-marker instead of literal D.
-;;     Added: diredp-deletion-file-name.
-;; 2006/03/31 dadams
-;;     No longer use display-in-minibuffer.
-;; 2006/01/07 dadams
-;;     Added: link for sending bug report.
-;; 2006/01/06 dadams
-;;     Added defgroup Dired-Plus and used it. Added :link.
-;; 2006/01/04 dadams
-;;     Added defvar of directory-listing-before-filename-regexp, for Emacs 22 compatibility.
-;; 2005/12/29 dadams
-;;     Added: diredp-mouse-mark/unmark-mark-region-files.
-;; 2005/12/26 dadams
-;;     Updated groups.
-;; 2005/12/05 dadams
-;;     diredp-ignored-file-name: Made it slightly darker.
-;; 2005/11/05 dadams
-;;     Renamed all stuff defined here to have diredp- prefix.
-;;     diredp-relsymlink-this-file: Protected with fboundp.
-;;     Changed to soft require: dired-x.el.
-;;     Removed comment to require this inside eval-after-load.
-;; 2005/11/03 dadams
-;;     Added: dired-display-msg.  Replace blue-foreground-face with it.
-;;     Alias dired-do-toggle to dired-toggle-marks, if defined.
-;; 2005/11/02 dadams
-;;     Added: dired-get-file-for-visit, dired(-mouse)-find-alternate-file*,
-;;            togglep-dired-find-file-reuse-dir, dired+-subst-find-*.
-;;     Use defface for all faces.  Renamed without "-face".  No longer require def-face-const.
-;;     dired-simultaneous-find-file: Minor bug fix (typo).
-;; 2005/07/10 dadams
-;;     dired-unmark-all-files-no-query -> dired-unmark-all-marks
-;;       (thanks to Sivaram Neelakantan for bug report).
-;; 2005/05/25 dadams
-;;     string-to-int -> string-to-number everywhere.
-;; 2005/05/17 dadams
-;;     Updated to work with Emacs 22.x.
-;; 2005/02/16 dadams
-;;     Added dired-mark/unmark-extension. Replaced dired-mark-extension with it everywhere.
-;; 2005/01/08 dadams
-;;     Bind [S-mouse-1], instead of [S-down-mouse-1], to dired-mouse-mark-region-files.
-;; 2004/11/20 dadams
-;;     dired-mark-sexp: Search for literal month names only for versions before Emacs 20.
-;;     Refined to deal with Emacs 21 < 21.3.50 (soon to be 22.x)
-;; 2004/11/14 dadams
-;;     Bound dired-no-confirm to non-nil for dired-mouse-*.
-;;     Updated for Emacs 21 and improved highlighting:
-;;       Spaces OK in file and directory names. Highlight date/time and size.
-;; 2004/10/17 dadams
-;;     Require cl only for Emacs 20, and only when compile.
-;; 2004/10/01 dadams
-;;     Updated to work with Emacs 21 also.
-;; 2004/04/02 dadams
-;;     dired-font-lock-keywords-1: Prefer using dired-omit-extensions
-;;     to completion-ignored-extensions, if available.
-;; 2004/03/22 dadams
-;;     Added dired-mouse-mark-region-files and dired-mouse-mark/unmark.
-;; 2000/09/27 dadams
-;;     1. dired-font-lock-keywords-1: fixed for spaces in dir names.
-;;     2. Added: dired-buffers-for-dir.
-;; 1999/09/06 dadams
-;;     Added S-*-mouse-2 bindings (same as C-*-mouse-2).
-;; 1999/08/26 dadams
-;;     1. Added *-face vars and dired-font-lock-keywords-1.
-;;     2. Added possibility to use dired-font-lock-keywords-1 via hook.
-;; 1999/08/26 dadams
-;;     Changed key binding of dired-mouse-find-file from down-mouse-2 to mouse-2.
-;; 1999/08/25 dadams
-;;     Changed (C-)(M-)mouse-2 bindings.
-;; 1999/08/25 dadams
-;;     1. Added cmds & menu bar and key bindings: (dired-)find-file-other-frame.
-;;     2. Changed binding for dired-display-file.
-;; 1999/03/26 dadams
-;;     1. Get rid of Edit menu-bar menu.
-;;     2. dired-mouse-3-menu: Changed popup titles and item names.
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program; see the file COPYING.  If not, write to
-;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
-;; Floor, Boston, MA 02110-1301, USA.
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;;; Code:
-
-(eval-when-compile (require 'cl)) ;; case (plus, for Emacs 20: dolist, pop, push)
-(eval-when-compile (require 'easymenu)) ;; easy-menu-create-menu
-
-(require 'dired) ;; dired-revert
-(require 'dired-aux) ;; dired-bunch-files, dired-do-chxxx, dired-do-create-files,
-                     ;; dired-mark-read-string, dired-read-shell-command,
-                     ;; dired-run-shell-command, dired-shell-stuff-it
-(require 'dired-x) ;; dired-do-relsymlink
-(require 'autofit-frame nil t) ;; (no error if not found) fit-frame-if-one-window
-(require 'bookmark+ nil t) ;; (no error if not found)
- ;; bmkp-autofile-add-tags, bmkp-autofile-remove-tags, bmkp-autofile-set, bmkp-copied-tags,
- ;; bmkp-current-bookmark-file, bmkp-describe-bookmark, bmkp-empty-file, bmkp-get-autofile-bookmark,
- ;; bmkp-get-bookmark-in-alist, bmkp-get-tags, bmkp-read-tag-completing,
- ;; bmkp-read-tags-completing, bmkp-refresh/rebuild-menu-list, bmkp-remove-all-tags,
- ;; bmkp-same-file-p, bmkp-set-bookmark-file-bookmark, bmkp-set-sequence-bookmark,
- ;; bmkp-set-tag-value, bmkp-some, bmkp-switch-bookmark-file, bmkp-tag-name
-
-;; For now at least, `highlight.el' is needed only if you use `bookmark+.el'.
-(when (featurep 'bookmark+) (require 'highlight nil t)) ;; (no error if not found):
- ;; hlt-highlight-region
-
-(if (> emacs-major-version 21) (require 'help-fns+ nil t) (require 'help+20 nil t))  ;; (no error if not found):
- ;; describe-file
-
-(require 'misc-fns nil t) ;; (no error if not found): undefine-killer-commands
-(require 'image-file nil t) ;; (no error if not found): image-file-name-regexp
-(require 'image-dired nil t) ;; (no error if not found):
- ;; image-dired-create-thumb, image-dired-create-thumbnail-buffer,
- ;; image-dired-dired-after-readin-hook, image-dired-delete-tag, image-dired-dired-comment-files,
- ;; image-dired-dired-display-external, image-dired-dired-display-image,
- ;; image-dired-display-thumbs, image-dired-get-comment, image-dired-get-exif-file-name,
- ;; image-dired-get-thumbnail-image, image-dired-insert-thumbnail, image-dired-line-up,
- ;; image-dired-line-up-dynamic, image-dired-line-up-interactive, image-dired-line-up-method,
- ;; image-dired-list-tags, image-dired-main-image-directory, image-dired-mark-tagged-files,
- ;; image-dired-read-comment, image-dired-remove-tag, image-dired-save-information-from-widgets,
- ;; image-dired-tag-files, image-dired-thumb-height, image-dired-thumbnail-buffer,
- ;; image-dired-thumb-name, image-dired-thumb-size, image-dired-thumb-width,
- ;; image-dired-widget-list, image-dired-write-comments, image-dired-write-tags
-(when (memq system-type '(windows-nt ms-dos))
-  ;; (no error if not found):
-  (require 'w32-browser nil t));; dired-w32explore, dired-w32-browser, dired-mouse-w32-browser,
-                               ;; dired-multiple-w32-browser
-(when (< emacs-major-version 21) (require 'subr-21)) ;; replace-regexp-in-string
-
-;; Provide macro for code byte-compiled using Emacs < 22.
-(eval-when-compile
- (when (< emacs-major-version 22)
-   (defmacro minibuffer-with-setup-hook (fun &rest body)
-     "Temporarily add FUN to `minibuffer-setup-hook' while executing BODY.
-BODY should use the minibuffer at most once.
-Recursive uses of the minibuffer are unaffected (FUN is not
-called additional times).
-
-This macro actually adds an auxiliary function that calls FUN,
-rather than FUN itself, to `minibuffer-setup-hook'."
-     ;; (declare (indent 1) (debug t))
-     (let ((hook  (make-symbol "setup-hook")))
-       `(let (,hook)
-         (setq ,hook  (lambda ()
-                        ;; Clear out this hook so it does not interfere
-                        ;; with any recursive minibuffer usage.
-                        (remove-hook 'minibuffer-setup-hook ,hook)
-                        (funcall ,fun)))
-         (unwind-protect
-              (progn (add-hook 'minibuffer-setup-hook ,hook) ,@body)
-           (remove-hook 'minibuffer-setup-hook ,hook)))))))
-
-(defmacro diredp-user-error (&rest args)
-  `(if (fboundp 'user-error) (user-error ,@args) (error ,@args)))
-
-;; Define these for Emacs 20 and 21.
-(unless (fboundp 'dired-get-file-for-visit) ; Emacs 22+
-  (defun dired-get-file-for-visit ()    ; Not bound
-    "Get the current line's file name, with an error if file does not exist."
-    (interactive)
-    (let ((raw  (dired-get-filename nil t)) ; Pass t for second arg so no error for `.' and `..'.
-          file-name)
-      (unless raw (error "No file on this line"))
-      (setq file-name  (file-name-sans-versions raw t))
-      (if (file-exists-p file-name)
-          file-name
-        (if (file-symlink-p file-name)
-            (error "File is a symlink to a nonexistent target")
-          (error "File no longer exists; type `g' to update Dired buffer")))))
-
-  (defun dired-find-alternate-file ()   ; Not bound
-    "In Dired, visit this file or directory instead of the Dired buffer."
-    (interactive)
-    (set-buffer-modified-p nil)
-    (find-alternate-file (dired-get-file-for-visit))))
-
-;;;;;;;;;;;;;;;;;;;;;;;
-
-
-(provide 'dired+)
-(require 'dired+)                       ; Ensure loaded before compile this.
-
-;; Quiet the byte-compiler.
-(defvar bmkp-copied-tags)                         ; In `bookmark+-1.el'
-(defvar bmkp-current-bookmark-file)               ; In `bookmark+-1.el'
-(defvar bookmark-default-file)                    ; In `bookmark.el'
-(defvar compilation-current-error)                ; In `compile.el'
-(defvar delete-by-moving-to-trash)                ; Built-in, Emacs 23+
-(defvar dired-always-read-filesystem)             ; In `dired.el', Emacs 26+
-(defvar dired-auto-revert-buffer)                 ; In `dired.el', Emacs 23+
-(defvar dired-create-files-failures)              ; In `dired-aux.el', Emacs 22+
-(defvar dired-details-state)                      ; In `dired-details+.el'
-(defvar dired-keep-marker-hardlink)               ; In `dired-x.el'
-(defvar dired-overwrite-confirmed)                ; In `dired-aux.el'
-(defvar dired-query-alist)                        ; In `dired-aux.el', Emacs < 24
-(defvar dired-recursive-copies)                   ; In `dired-aux.el', Emacs 22+
-(defvar dired-recursive-deletes)                  ; In `dired.el', Emacs 22+
-(defvar dired-shrink-to-fit)                      ; In `dired.el'
-(defvar dired-switches-alist)                     ; In `dired.el'
-(defvar dired-subdir-switches)                    ; In `dired.el'
-(defvar dired-touch-program)                      ; Emacs 22+
-(defvar dired-use-ls-dired)                       ; Emacs 22+
-(defvar diredp-count-.-and-..-flag)               ; Here, Emacs 22+
-(defvar diredp-hide-details-initially-flag)       ; Here, Emacs 24.4+
-(defvar diredp-hide-details-last-state)           ; Here, Emacs 24.4+
-(defvar diredp-hide-details-propagate-flag)       ; Here, Emacs 24.4+
-(defvar diredp-hide-details-toggled)              ; Here, Emacs 24.4+
-(defvar diredp-highlight-autofiles-mode)          ; Here, Emacs 22+
-(defvar diredp-menu-bar-encryption-menu)          ; Here, Emacs 23+
-(defvar diredp-menu-bar-images-recursive-menu)    ; Here (old name)
-(defvar diredp-menu-bar-regexp-recursive-menu)    ; Here (old name)
-(defvar diredp-menu-bar-subdir-menu)              ; Here (old name)
-(defvar diredp-move-file-dirs)                    ; Here, Emacs 24+
-(defvar diredp-single-bookmarks-menu)             ; Here, if Bookmark+ is available
-(defvar filesets-data)                            ; In `filesets.el'
-(defvar grep-use-null-device)                     ; In `grep.el'
-(defvar header-line-format)                       ; Emacs 22+
-(defvar icicle-file-sort)                         ; In `icicles-opt.el'
-;; $$$$ (defvar icicle-file-sort-first-time-p)            ; In `icicles-var.el'
-(defvar icicle-files-ido-like-flag)               ; In `icicles-opt.el'
-(defvar icicle-ignored-directories)               ; In `icicles-opt.el'
-(defvar icicle-sort-comparer)                     ; In `icicles-opt.el'
-(defvar image-dired-display-image-buffer)         ; In `image-dired.el'
-(defvar image-dired-line-up-method)               ; In `image-dired.el'
-(defvar image-dired-main-image-directory)         ; In `image-dired.el'
-(defvar image-dired-thumbnail-buffer)             ; In `image-dired.el'
-(defvar image-dired-thumb-height)                 ; In `image-dired.el'
-(defvar image-dired-thumb-width)                  ; In `image-dired.el'
-(defvar image-dired-widget-list)                  ; In `image-dired.el'
-(defvar ls-lisp-use-insert-directory-program)     ; In `ls-lisp.el'
-(defvar minibuffer-default-add-function)          ; In `simple.el', Emacs 23+
-(defvar mouse3-dired-function)                    ; In `mouse3.el'
-(defvar read-file-name-completion-ignore-case)    ; In `minibuffer.el', Emacs 23+.  In C code, Emacs 22.
-(defvar recentf-list)                             ; In `recentf.el'
-(defvar switch-to-buffer-preserve-window-point)   ; In `window.el', Emacs 24+
-(defvar tooltip-mode)                             ; In `tooltip.el'
-(defvar vc-directory-exclusion-list)              ; In `vc'
-(defvar w32-browser-wait-time)                    ; In `w32-browser.el'
-
-;;;;;;;;;;;;;;;;;;;;;;;
-
-(defgroup Dired-Plus nil
-  "Various enhancements to Dired."
-  :prefix "diredp-" :group 'dired
-  :link `(url-link :tag "Send Bug Report"
-          ,(concat "mailto:" "drew.adams" "@" "oracle" ".com?subject=\
-dired+.el bug: \
-&body=Describe bug here, starting with `emacs -q'.  \
-Don't forget to mention your Emacs and library versions."))
-  :link '(url-link :tag "Other Libraries by Drew"
-          "https://www.emacswiki.org/emacs/DrewsElispLibraries")
-  :link '(url-link :tag "Download"
-          "https://www.emacswiki.org/emacs/download/dired%2b.el")
-  :link '(url-link :tag "Description"
-          "https://www.emacswiki.org/emacs/DiredPlus")
-  :link '(emacs-commentary-link :tag "Commentary" "dired+"))
- 
-;;; Variables
-
-;; `dired-do-toggle' was renamed to `dired-toggle-marks' after Emacs 20.
-(unless (fboundp 'dired-toggle-marks) (defalias 'dired-toggle-marks 'dired-do-toggle))
-
-;;; This is duplicated in `diff.el' and `vc.el'.
-;;;###autoload
-(defcustom diff-switches "-c"
-  "*A string or list of strings specifying switches to be passed to diff."
-  :type '(choice string (repeat string))
-  :group 'dired :group 'diff)
-
-;;;###autoload
-(defcustom diredp-auto-focus-frame-for-thumbnail-tooltip-flag nil
-  "*Non-nil means automatically focus the frame for a thumbnail tooltip.
-If nil then you will not see a thumbnail image tooltip when you
-mouseover an image-file name in Dired, unless you first give the frame
-the input focus (e.g., by clicking its title bar).
-
-This option has no effect if `diredp-image-preview-in-tooltip' is nil.
-It also has no effect for Emacs versions prior to Emacs 22."
-  :type 'boolean :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-bind-problematic-terminal-keys t
-  "*Non-nil means bind some keys that might not work in a text-only terminal.
-This applies to keys that use modifiers Meta and Shift together.
-If you use Emacs in text-only terminal and your terminal does not
-support the use of such keys then customize this option to nil."
-  :type 'boolean :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-compressed-extensions '(".tar" ".taz" ".tgz" ".arj" ".lzh"
-                                          ".lzma" ".xz" ".zip" ".z" ".Z" ".gz" ".bz2" ".rar" ".rev")
-  "*List of compressed-file extensions, for highlighting.
-
-Note: If you change the value of this option then you need to restart
-Emacs to see the effect of the new value on font-locking."
-  :type '(repeat string) :group 'Dired-Plus)
-
-(when (> emacs-major-version 21)        ; Emacs 22+
-  (defcustom diredp-count-.-and-..-flag nil
-    "Non-nil means count `.' and `..' when counting files for mode-line."
-    :type 'boolean :group 'Dired-Plus))
-
-;;;###autoload
-(defcustom diredp-do-report-echo-limit 5
-  "Echo result for each file, for fewer than this many files.
-If more than this many files are acted on then there is no echoing.
-
-Used by some do-and-report commands such as `diredp-do-emacs-command'.
-Results that are not echoed are anyway reported by `dired-log', so you
-can show them with `?' in the Dired buffer."
-  :type '(restricted-sexp :match-alternatives (wholenump)) :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-dwim-any-frame-flag pop-up-frames
-  "*Non-nil means the target directory can be in a window in another frame.
-Only visible frames are considered.
-This is used by ``dired-dwim-target-directory'.
-This option has no effect for Emacs versions before Emacs 22."
-  :type 'boolean :group 'Dired-Plus)
-
-(when (fboundp 'dired-hide-details-mode) ; Emacs 24.4+
-  (defcustom diredp-hide-details-initially-flag t
-    "*Non-nil means hide details in Dired from the outset."
-    :type 'boolean :group 'Dired-Plus
-    :set (lambda (sym defs)
-           (custom-set-default sym defs)
-           (setq diredp-hide-details-last-state  diredp-hide-details-initially-flag)))
-
-  (defcustom diredp-hide-details-propagate-flag t
-    "*Non-nil means display the next Dired buffer the same way as the last.
-The last `dired-hide-details-mode' value set is used by the next Dired
-buffer created."
-    :type 'boolean :group 'Dired-Plus))
-
-;; Emacs 20 only.
-;;;###autoload
-(unless (fboundp 'define-minor-mode)
-  (defcustom diredp-highlight-autofiles-mode t
-    "*Non-nil means highlight names of files that are autofile bookmarks.
-Autofiles that have tags are highlighted using face
-`diredp-tagged-autofile-name'.  Those with no tags are highlighted
-using face `diredp-autofile-name'.
-
-Setting this option directly does not take effect; use either
-\\[customize] or command `diredp-highlight-autofiles-mode'.
-
-NOTE: When `dired+.el' is loaded (for the first time per Emacs
-session), the highlighting is turned ON, regardless of the option
-value.  To prevent this and have the highlighting OFF by default, you
-must do one of the following:
-
- * Put (diredp-highlight-autofiles-mode -1) in your init file, AFTER
-   it loads `dired+.el'.
-
- * Customize the option to `nil', AND ensure that your `custom-file'
-   (or the `custom-saved-variables' part of your init file) is
-   evaluated before `dired+.el' is loaded.
-
-This option has no effect unless you use libraries `Bookmark and
-`highlight.el'."
-    :set        (lambda (symbol value) (diredp-highlight-autofiles-mode (if value 1 -1)))
-    :initialize 'custom-initialize-default
-    :type 'boolean :group 'Dired-Plus :require 'dired+))
-
-;;;###autoload
-(defcustom diredp-ignore-compressed-flag t
-  "*Non-nil means to font-lock names of compressed files as ignored files.
-This applies to filenames whose extensions are in
-`diredp-compressed-extensions'.  If nil they are highlighted using
-face `diredp-compressed-file-name'.
-
-Note: If you change the value of this option then you need to restart
-Emacs to see the effect of the new value on font-locking."
-  :type 'boolean :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-image-preview-in-tooltip (or (and (boundp 'image-dired-thumb-size)  image-dired-thumb-size)
-                                               100)
-  "*Whether and what kind of image preview to show in a tooltip.
-The possible values are:
-
- `nil'       : do not show a tooltip preview
- integer N>0 : show a thumbnail preview of that size
- `full'      : show a full-size preview of the image
-
-To enable tooltip image preview you must turn on `tooltip-mode' and
-load library `image-dired.el'.  See also option
-`diredp-auto-focus-frame-for-thumbnail-tooltip-flag'.
-
-This option has no effect for Emacs versions prior to Emacs 22."
-  :type '(choice
-          (restricted-sexp :tag "Show a thumnail image of size"
-           :match-alternatives ((lambda (x) (and (wholenump x)  (not (zerop x))))))
-          (const :tag "Show a full-size image preview"      full)
-          (const :tag "OFF: Do not show an image preview"   nil))
-  :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-image-show-this-file-use-frame-flag t
-  "Non-nil means `diredp-image-show-this-file' uses another frame.
-If nil then it uses another window.  Using another frame means you
-have more control over the image size when you use a prefix arg.
-
-If it uses another window then the prefix arg controls only the
-minimum window height, not necessarily the image scale (height).
-
-\(If the buffer displaying the image is already considered a
-special-display buffer by your Emacs setup, then a nil value of this
-option has no effect.)"
-  :type 'boolean :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-list-file-attributes (list '(5 8) 'auto)
-  "Which file attributes `diredp-list-file' uses, and when."
-  :group 'Dired-Plus :type '(list (repeat integer)
-                                  (choice
-                                   (const :tag "Show automatically, immediately" 'auto)
-                                   (const :tag "Show on demand via `l'" 'on-demand))))
-
-;;;###autoload
-(defcustom diredp-max-frames 200
-  "*Max number of frames, for commands that find files in separate frames.
-These commands are `dired-do-find-marked-files' and
-`diredp-do-find-marked-files-recursive'.  See their descriptions for
-the circumstances in which they show the files in separate frames."
-  :type '(restricted-sexp :match-alternatives ((lambda (x) (and (wholenump x)  (not (zerop x))))))
-  :group 'Dired-Plus)
-
-(when (fboundp 'file-equal-p)           ; Emacs 24+
-  (defcustom diredp-move-file-dirs ()
-    "Alist of names of files and preferred directories to move them to.
-File names should be relative (no directory component).
-Target directory names should be absolute."
-    :group 'files :type '(alist :key-type file :value-type directory)))
-
-;; (Not used - just use the body directly in the option default value.
-;; (defun diredp-omit-files-regexp ()
-;;   "Return regexp to use for font-locking, using `dired-omit-files' as base."
-;;   (let* ((strg  dired-omit-files)
-;;          (strg  (if (eq ?^ (aref strg 0)) (substring strg 1) strg)) ; Remove initial ^
-;;          (strg  (replace-regexp-in-string "\\(\\\\[|]\\)\\^" "\\1" strg 'FIXEDCASE nil)) ; Remove other ^'s
-;;          (strg  (replace-regexp-in-string "\\([$]\\)" "" strg 'FIXEDCASE nil))) ; Remove $'s
-;;     strg))
-
-(defcustom diredp-omit-files-regexp (let* ((strg  dired-omit-files)
-                                           (strg  (if (eq ?^ (aref strg 0)) ; Remove initial ^
-                                                      (substring strg 1)
-                                                    strg))
-                                           (strg  (replace-regexp-in-string "\\(\\\\[|]\\)\\^" ; Remove other ^'s
-                                                                            "\\1"
-                                                                            strg
-                                                                            'FIXEDCASE
-                                                                            nil))
-                                           (strg  (replace-regexp-in-string "\\([$]\\)" ; Remove $'s
-                                                                            ""
-                                                                            strg
-                                                                            'FIXEDCASE
-                                                                            nil)))
-                                      strg)
-  "Regexp for font-locking file names to be omitted by `dired-omit-mode'.
-The regexp is matched only against the file name, but the entire line
-is highlighted (with face `diredp-omit-file-name').
-
-The default value of this option differs from that of
-`dired-omit-files' by removing \"^\" from the beginning, and \"$\"
-from the end, of each regexp choice.  (The default value of
-`dired-omit-files', at least prior to Emacs 27, uses \"^\" and \"$\",
-but it should not.)
-
-If you want to control the beginning and end of choice matches then
-use \"\\`\" and \"\\'\" instead of \"^\" and \"$\".
-
-Note: If you change the value of this option then you need to restart
-Emacs to see the effect of the new value on font-locking."
-  :group 'Dired-Plus :type 'regexp)
-
-;;;###autoload
-(defcustom diredp-prompt-for-bookmark-prefix-flag nil
-  "*Non-nil means prompt for a prefix string for bookmark names."
-  :type 'boolean :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-visit-ignore-regexps ()
-  "Regexps matching file names for `diredp-visit-(next|previous)' to skip.
-A file or directory name matching one of these regexps is skipped,
-along with those with an extension in `diredp-visit-ignore-extensions'."
-  :type '(repeat regexp) :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-visit-ignore-extensions '("elc")
-  "Extensions of file names for `diredp-visit-(next|previous)' to skip.
-A file name with one of these extensions is skipped, along with those
-matching a regexp in `diredp-visit-ignore-regexps'."
-  :type '(repeat string) :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-w32-local-drives '(("C:" "Local disk"))
-  "*Local MS Windows drives that you want to use for `diredp-w32-drives'.
-Each entry is a list (DRIVE DESCRIPTION), where DRIVE is the drive
-name and DESCRIPTION describes DRIVE."
-  :type '(alist
-          :key-type   (string        :tag "Drive name")
-          :value-type (group (string :tag "Drive description")))
-  :group 'Dired-Plus)
-
-;;;###autoload
-(defcustom diredp-wrap-around-flag t
-  "*Non-nil means Dired \"next\" commands wrap around to buffer beginning."
-  :type 'boolean :group 'Dired-Plus)
-
-(when (fboundp 'dired-hide-details-mode) ; Emacs 24.4+
-  (defvar diredp-hide-details-last-state diredp-hide-details-initially-flag
-    "Last `dired-hide-details-mode' value.
-Initialized to the value of option `diredp-hide-details-initially-flag'.")
-
-  (defvar diredp-hide-details-toggled nil
-    "Non-nil means you have already toggled hiding details in this buffer.")
-  (make-variable-buffer-local 'diredp-hide-details-toggled))
-
-;; Same value as the default value of `icicle-re-no-dot'.
-(defvar diredp-re-no-dot "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*"
-  "Regexp that matches anything except `.' and `..'.")
-
-(defvar diredp-w32-drives-mode-map (let ((map  (make-sparse-keymap)))
-                                     (define-key map "q"       'bury-buffer)
-                                     (define-key map "\r"      'widget-button-press)
-                                     (define-key map [mouse-2] 'widget-button-click)
-                                     map)
-  "Keymap for `diredp-w32-drives-mode'.")
-
-;;; $$$$$$ Starting with Emacs 22, *-move-to* is defvaraliased to *-listing-before*.
-;;; But `files+.el' defines *-listing-before*, so we define it here too.
-;;; (unless (> emacs-major-version 21)
-;;;   (defvar directory-listing-before-filename-regexp dired-move-to-filename-regexp
-;;;     "Regular expression to match up to the file name in a directory listing.
-;;; The default value is designed to recognize dates and times
-;;; regardless of the language."))
- 
-;;; Macros
-
-
-;; Unlike `dired-mark-if':
-;;
-;; 1. Value returned and message indicate both the number matched and the number changed.
-;; 2. Added optional arg PLURAL, for irregular plurals (e.g. "directories").
-;;
-(defmacro diredp-mark-if (predicate singular &optional plural)
-  "Mark files for PREDICATE, according to `dired-marker-char'.
-PREDICATE is evaluated on each line, with point at beginning of line.
-SINGULAR is a singular noun phrase for the type of files being marked.
-Optional arg PLURAL is a plural noun phrase for the type of files
- being marked.
-If PLURAL is nil then SINGULAR should end with a noun that can be
-pluralized by adding `s'.
-
-Return nil if no files matched PREDICATE.
-Otherwise return a cons (CHANGED . MATCHED), where:
- CHANGED is the number of markings that were changed by the operation.
- MATCHED is the number of files that matched PREDICATE."
-  `(let ((inhibit-read-only  t)
-         changed matched)
-    (save-excursion
-      (setq matched  0
-            changed  0)
-      (when ,singular (message "%s %s%s..."
-                               (cond ((eq dired-marker-char ?\040)            "Unmarking")
-                                     ((eq dired-del-marker dired-marker-char) "Flagging")
-                                     (t                                       "Marking"))
-                               (or ,plural  (concat ,singular "s"))
-                               (if (eq dired-del-marker dired-marker-char) " for deletion" "")))
-      (goto-char (point-min))
-      (while (not (eobp))
-        (when ,predicate
-          (setq matched  (1+ matched))
-          (unless (eq dired-marker-char (char-after))
-            (delete-char 1) (insert dired-marker-char) (setq changed  (1+ changed))))
-        (forward-line 1))
-      (when ,singular (message "%s %s%s%s newly %s%s"
-                               matched
-                               (if (= matched 1) ,singular (or ,plural  (concat ,singular "s")))
-                               (if (not (= matched changed)) " matched, " "")
-                               (if (not (= matched changed)) changed "")
-                               (if (eq dired-marker-char ?\040) "un" "")
-                               (if (eq dired-marker-char dired-del-marker) "flagged" "marked"))))
-    (and (> matched 0)  (cons changed matched))))
-
-
-;; Just a helper function for `dired-map-over-marks'.
-(defun diredp-get-file-or-dir-name (arg)
-  "Return name of next file or directory or nil if none.
-Argument ARG:
- `all-files-no-dirs' or nil means skip directories.
- `all-files-no-dots' means skip `.' and `..'."
-  (let ((fname  nil))
-    (while (and (not fname)  (not (eobp)))
-      (setq fname  (dired-get-filename t t))
-      (when (and fname  (or (not arg)  (eq arg 'all-files-no-dirs))  (file-directory-p fname))
-        (setq fname  nil))
-      (when (and fname  (eq arg 'all-files-no-dots)  (or (member fname '("." ".."))
-                                                         (diredp-string-match-p "/\\.\\.?$" fname)))
-        (setq fname  nil))
-      (forward-line 1))
-    (forward-line -1)
-    fname))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; Treat multiple `C-u' specially.
-;;
-(defmacro dired-map-over-marks (body arg &optional show-progress
-                                distinguish-one-marked)
-  "Eval BODY with point on each marked line.  Return a list of BODY's results.
-If no marked file could be found, execute BODY on the current line.
-ARG, if non-nil, specifies the files to use instead of the marked files.
- If ARG is an integer, use the next ARG files (previous -ARG, if < 0).
-   In that case, point is dragged along.  This is so that commands on
-   the next ARG (instead of the marked) files can be easily chained.
- If ARG is a cons with element 16, 64, or 256, corresponding to
-   `C-u C-u', `C-u C-u C-u', or `C-u C-u C-u C-u', then use all files
-   in the Dired buffer, where:
-     16 includes NO directories (including `.' and `..')
-     64 includes directories EXCEPT `.' and `..'
-    256 includes ALL directories (including `.' and `..')
- If ARG is otherwise non-nil, use the current file.
-If optional third arg SHOW-PROGRESS evaluates to non-nil,
- redisplay the Dired buffer after each file is processed.
-
-No guarantee is made about the position on the marked line.  BODY must
-ensure this itself, if it depends on this.
-
-Search starts at the beginning of the buffer, thus the car of the list
-corresponds to the line nearest the end of the buffer.  This is also
-true for (positive and negative) integer values of ARG.
-
-BODY should not be too long, because it is expanded four times.
-
-If DISTINGUISH-ONE-MARKED is non-nil, then return (t FILENAME) instead
- of (FILENAME), if only one file is marked."
-  ;; WARNING: BODY must not add new lines before point - this may cause an
-  ;; endless loop.  This warning should not apply any longer, sk  2-Sep-1991 14:10.
-  `(prog1
-    (let ((inhibit-read-only  t)
-          (newarg             ,arg)
-          multi-C-u case-fold-search found results)
-      (when (and (consp newarg)  (> (prefix-numeric-value newarg) 4))
-        (setq newarg     (case (prefix-numeric-value newarg)
-                           (16   'all-files-no-dirs) ; `C-u C-u'
-                           (64   'all-files-no-dots) ; `C-u C-u C-u'
-                           (256  'all-files) ; `C-u C-u C-u C-u'
-                           (t    'all-files-no-dirs))
-              multi-C-u  t))
-      (if (and newarg  (not multi-C-u))
-          (if (integerp newarg)
-              (progn                    ; No `save-excursion', want to move point.
-                (dired-repeat-over-lines newarg #'(lambda ()
-                                                    (when ,show-progress (sit-for 0))
-                                                    (setq results  (cons ,body results))))
-                (if (< newarg 0) (nreverse results) results))
-            ;; Non-nil, non-integer ARG means use current file:
-            (list ,body))
-        (let ((regexp  (dired-marker-regexp))
-              next-position)
-          (save-excursion
-            (goto-char (point-min))
-            ;; Remember position of next marked file before BODY can insert lines before the
-            ;; just found file, confusing us by finding the same marked file again and again...
-            (setq next-position  (and (if multi-C-u
-                                          (diredp-get-file-or-dir-name newarg)
-                                        (re-search-forward regexp nil t))
-                                      (point-marker))
-                  found          (not (null next-position)))
-            (while next-position
-              (goto-char next-position)
-              (when ,show-progress (sit-for 0))
-              (setq results  (cons ,body results))
-              ;; move after last match
-              (goto-char next-position)
-              (forward-line 1)
-              (set-marker next-position nil)
-              (setq next-position  (and (if multi-C-u
-                                            (diredp-get-file-or-dir-name newarg)
-                                          (re-search-forward regexp nil t))
-                                        (point-marker)))))
-          (when (and ,distinguish-one-marked  (= (length results) 1))
-            (setq results  (cons t results)))
-          (if found results (list ,body)))))
-    ;; `save-excursion' loses, again
-    (dired-move-to-filename)))
-
-;; Same as `icicle-with-help-window' in `icicles-mac.el'
-;; and `bmkp-with-help-window' in `bookmark+-mac.el'.
-(defmacro diredp-with-help-window (buffer &rest body)
-  "`with-help-window', if available; else `with-output-to-temp-buffer'."
-  (if (fboundp 'with-help-window)
-      `(with-help-window ,buffer ,@body)
-    `(with-output-to-temp-buffer ,buffer ,@body)))
-
-(put 'diredp-with-help-window 'common-lisp-indent-function '(4 &body))
- 
-;;; Utility functions
-
-;; Same as `imenup-delete-if-not'.
-;;
-(defun diredp-delete-if-not (predicate xs)
-  "Remove all elements of list XS that do not satisfy PREDICATE.
-This operation is destructive, reusing conses of XS whenever possible."
-  (while (and xs  (not (funcall predicate (car xs))))
-    (setq xs  (cdr xs)))
-  (let ((cl-p  xs))
-    (while (cdr cl-p)
-      (if (not (funcall predicate (cadr cl-p))) (setcdr cl-p (cddr cl-p)) (setq cl-p  (cdr cl-p)))))
-  xs)
-
-;; Same as `imenup-delete-if'.
-;;
-(defun diredp-delete-if (predicate xs)
-  "Remove all elements of list XS that satisfy PREDICATE.
-This operation is destructive, reusing conses of XS whenever possible."
-  (while (and xs  (funcall predicate (car xs)))
-    (setq xs  (cdr xs)))
-  (let ((cl-p  xs))
-    (while (cdr cl-p)
-      (if (funcall predicate (cadr cl-p))
-          (setcdr cl-p (cddr cl-p))
-        (setq cl-p  (cdr cl-p)))))
-  xs)
-
-;; Same as `tap-string-match-p' in `thingatpt+.el'.
-(if (fboundp 'string-match-p)
-    (defalias 'diredp-string-match-p 'string-match-p) ; Emacs 23+
-  (defun diredp-string-match-p (regexp string &optional start)
-    "Like `string-match', but this saves and restores the match data."
-    (save-match-data (string-match regexp string start))))
-
-(if (fboundp 'looking-at-p)
-    (defalias 'diredp-looking-at-p 'looking-at-p) ; Emacs 23+
-  (defun diredp-looking-at-p (regexp)
-    "Like `looking-at', but this saves and restores the match data."
-    (save-match-data (looking-at regexp))))
-
-;; `dired-read-regexp' does not accept DEFAULT and HISTORY for older Emacsen, so use this.
-(defun diredp-read-regexp (prompt &optional default history)
-  "Read a regexp.
-HISTORY defaults to `dired-regexp-history'."
-  (setq history  (or history 'dired-regexp-history))
-  (if (fboundp 'read-regexp)
-      (read-regexp prompt default history)
-    (read-from-minibuffer prompt nil nil nil history default)))
-
-(if (fboundp 'delete-dups)
-    (defalias 'diredp-delete-dups 'delete-dups)
-  (defun diredp-delete-dups (list)
-    "Destructively remove `equal' duplicates from LIST.
-Store the result in LIST and return it.  LIST must be a proper list.
-Of several `equal' occurrences of an element in LIST, the first
-one is kept."
-    (let ((tail list))
-      (while tail
-        (setcdr tail (delete (car tail) (cdr tail)))
-        (setq tail (cdr tail))))
-    list))
-
-(defun diredp-nonempty-region-p ()
-  "Return non-nil if region is active and non-empty."
-  (and transient-mark-mode  mark-active  (mark)  (> (region-end) (region-beginning))))
-
-(defun diredp-get-image-filename (&optional localp no-error-if-not-filep)
-  "Return the image-file name on this line, or nil if no image file.
-If not in Dired (or a mode derived from Dired), then test the entire
-text of the current line as the file name.
-
-The optional args are the same as for `dired-get-filename'.  They are
-ignored if not in a Dired mode.
-
-\(Prior to Emacs 22, this function just returns nil.)"
-  (let ((file  (if (derived-mode-p 'dired-mode)
-                   (dired-get-filename localp no-error-if-not-filep)
-                 ;; Make it work also for `diredp-list-files' listings.
-                 (buffer-substring-no-properties (line-beginning-position) (line-end-position)))))
-    (and file
-         (fboundp 'image-file-name-regexp) ; Emacs 22+, `image-file.el'.
-         (diredp-string-match-p (image-file-name-regexp) file)
-         file)))
-
-(defun diredp-root-directory-p (file)
-  "Return non-nil if FILE is a root directory."
-  (if (fboundp 'ange-ftp-root-dir-p)
-      (ange-ftp-root-dir-p (file-name-as-directory file))
-    ;; This is essentially `ange-ftp-root-dir-p' applied to `file-name-as-directory'.
-    ;; If `ange-ftp-root-dir-p' changes, update this code.
-    (or (and (eq system-type 'windows-nt)  (diredp-string-match-p "\\`[a-zA-Z]:[/\\]\\'"
-                                                                  (file-name-as-directory file)))
-        (string= "/" file))))
-
-(defun diredp-parent-dir (file &optional relativep)
-  "Return the parent directory of FILE, or nil if none.
-Optional arg RELATIVEP non-nil means return a relative name, that is,
-just the parent component."
-  (let ((parent  (file-name-directory (directory-file-name (expand-file-name file))))
-        relparent)
-    (when relativep (setq relparent  (file-name-nondirectory (directory-file-name parent))))
-    (and (not (equal parent file))  (or relparent  parent))))
-
-(unless (fboundp 'derived-mode-p)       ; Emacs 20, 21.
-  (defun derived-mode-p (&rest modes)
-    "Non-nil if the current major mode is derived from one of MODES.
-Uses the `derived-mode-parent' property of the symbol to trace backwards."
-    (let ((parent  major-mode))
-      (while (and (not (memq parent modes))  (setq parent  (get parent 'derived-mode-parent))))
-      parent)))
-
-(defun diredp-ensure-mode ()
-  "Raise an error if not in Dired or a mode derived from it."
-  (unless (derived-mode-p 'dired-mode)
-    (error "You must be in Dired or a mode derived from it to use this command")))
-
-(defun diredp-ensure-bookmark+ ()
-  (unless (require 'bookmark+ nil t) (error "This command requires library `bookmark+.el'")))
- 
-
-(unless (fboundp 'dired-nondirectory-p) ; Emacs 20, 21.
-  (defun dired-nondirectory-p (file)
-    "Return non-nil if FILE is not a directory."
-    (not (file-directory-p file))))
-
-
-;;; Some of the redefinitions that follow are essentially unaltered vanilla Emacs code to be
-;;; reloaded, to use the new definition of `dired-map-over-marks' here.
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; 1. Pass non-nil second arg to `dired-get-filename' so we can include `.' and `..'.
-;; 2. Doc string is updated to reflect the new ARG behavior.
-;; 3. Allow, unlike vanilla Emacs, use of FILTER and DISTINGUISH-ONE-MARKED together.
-;;
-(defun dired-get-marked-files (&optional localp arg filter distinguish-one-marked error-if-none-p)
-  "Return names of the marked files and directories as a list of strings.
-The list is in the same order as the buffer, that is, the car is the
-  first marked file.
-Values returned are normally absolute file names.
-Optional arg LOCALP as in `dired-get-filename'.
-Optional second argument ARG specifies files to use instead of marked.
- Usually ARG comes from the command's prefix arg.
- If ARG is an integer, use the next ARG files (previous -ARG, if < 0).
- If ARG is a cons with element 16, 64, or 256, corresponding to
-  `C-u C-u', `C-u C-u C-u', or `C-u C-u C-u C-u', then use all files
-  in the Dired buffer, where:
-    16 includes NO directories (including `.' and `..')
-    64 includes directories EXCEPT `.' and `..'
-   256 includes ALL directories (including `.' and `..')
- If ARG is otherwise non-nil, use the current file.
-Optional third argument FILTER, if non-nil, is a function to select
- some of the files: those for which (funcall FILTER FILENAME) is
- non-nil.
-If DISTINGUISH-ONE-MARKED is non-nil, then return (t FILENAME) instead
- of (FILENAME) if only one file is marked (after any filtering by
- FILTER).
-If ERROR-IF-NONE-P is non-nil, signal an error if the list of files is
- empty.  If ERROR-IF-NONE-P is a string then it is the error message.
-
-Note that the Dired+ version of this function differs from the vanilla
-version in these respects:
-
-* There are more possibilities for argument ARG (prefix argument).
-* Directories `.' and `..' can be included as marked.
-* You can use arguments FILTER and DISTINGUISH-ONE-MARKED together."
-  (let ((all  (delq nil (save-excursion (dired-map-over-marks (dired-get-filename localp 'NO-ERROR-IF-NOT-FILEP)
-                                                              arg
-                                                              nil
-                                                              distinguish-one-marked))))
-        result)
-    (when (equal all '(t)) (setq all  nil)) ; Added by vanilla Emacs 24+.
-    (if (and distinguish-one-marked  (eq (car all) t))
-        (if (not filter)
-            all
-          (and (funcall filter (cadr all))  (list t (cadr all))))
-      (dolist (file  all)
-        (when (or (not filter)  (funcall filter file)) (push file result)))
-      (when (and (null result)  error-if-none-p)
-        (diredp-user-error (if (stringp error-if-none-p) error-if-none-p "No files specified")))
-      result)))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'.
-;;
-;; 1. Define here to make use of my `dired-map-over-marks'.
-;; 2. Added &rest arg FUN-ARGS.
-;; 3. Added doc string.
-;;
-(defun dired-map-over-marks-check (fun mark-arg op-symbol &optional show-progress &rest fun-args)
-  "Map FUN over marked lines and display failures.
-FUN returns non-nil (the offending object, e.g. the short form of the
-filename) for a failure and probably logs a detailed error explanation
-using function `dired-log'.
-
-MARK-ARG is as the second argument of `dired-map-over-marks'.
-
-OP-SYMBOL is a symbol describing the operation performed (e.g.
-`compress').  It is used with `dired-mark-pop-up' to prompt the user
-\(e.g. with `Compress * [2 files]? ') and to display errors (e.g.
-`Failed to compress 1 of 2 files - type ? for details (\"foo\")')
-
-SHOW-PROGRESS if non-nil means redisplay Dired after each file.
-
-FUN-ARGS is the list of any remaining args to
-`dired-map-over-marks-check'.  Function FUN is applied to these
-arguments."
-  (and (dired-mark-confirm op-symbol mark-arg)
-       (let* ((results     (dired-map-over-marks (apply fun fun-args) mark-arg show-progress)) ; FUN return vals.
-              (nb-results  (length results))
-              (failures    (delq nil results))
-              (nb-fail     (length failures))
-              (op-strg     (if (eq op-symbol 'compress) "Compress or uncompress" (capitalize
-                                                                                  (symbol-name op-symbol)))))
-         (if (null failures)
-             (message "%s: %d file%s." op-strg nb-results (dired-plural-s nb-results))
-           (dired-log-summary (format "Failed to %s %d of %d file%s"
-                                      (downcase op-strg) nb-fail nb-results (dired-plural-s nb-results))
-                              failures)))))
-
-;; Like `dired-map-over-marks-check', but `dired-log-summary' is always called, and first arg passed is different.
-;;
-(defun diredp-map-over-marks-and-report (fun mark-arg op-symbol &optional show-progress &rest fun-args)
-  "Map FUN over marked lines and report the results.
-FUN returns non-nil (the offending object, e.g. the short form of the
-filename) for a failure and probably logs a detailed error explanation
-using function `dired-log'.
-
-MARK-ARG is as the second argument of `dired-map-over-marks'.
-
-OP-SYMBOL is a symbol describing the operation performed (e.g.
-`compress').  It is used with `dired-mark-pop-up' to prompt the user
-\(e.g. with `Compress * [2 files]? ') and to display errors (e.g.
-`Failed to compress 1 of 2 files - type ? to see why (\"foo\")')
-
-SHOW-PROGRESS if non-nil means redisplay Dired after each file.
-
-FUN-ARGS is the list of any remaining args to
-`diredp-map-over-marks-and-report'.  Function FUN is applied to these
-arguments."
-  (and (dired-mark-confirm op-symbol mark-arg)
-       (let* ((results     (dired-map-over-marks (apply fun fun-args) mark-arg show-progress)) ; FUN return vals.
-              (nb-results  (length results))
-              (failures    (delq nil results))
-              (nb-fail     (length failures))
-              (op-strg     (capitalize (symbol-name op-symbol))))
-         (dired-log-summary (format "%s for %d file%s%s"
-                                    op-strg nb-results (dired-plural-s nb-results)
-                                    (if failures (format ": %d failures" nb-fail) ""))
-                            failures))))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'.
-;;
-(when (boundp 'dired-subdir-switches)   ; Emacs 22+
-  (defun dired-do-redisplay (&optional arg test-for-subdir) ; Bound to `l'
-    "Redisplay all marked (or next ARG) files.
-If on a subdir line, redisplay that subdirectory.  In that case,
-a prefix arg lets you edit the `ls' switches used for the new listing.
-
-Dired remembers switches specified with a prefix arg, so that reverting
-the buffer will not reset them.  However, using `dired-undo' to re-insert
-or delete subdirectories can bypass this machinery.  Hence, you sometimes
-may have to reset some subdirectory switches after a `dired-undo'.
-You can reset all subdirectory switches to the default using
-\\<dired-mode-map>\\[dired-reset-subdir-switches].
-See Info node `(emacs)Subdir switches' for more details."
-    ;; Moves point if the next ARG files are redisplayed.
-    (interactive "P\np")
-    (if (and test-for-subdir  (dired-get-subdir))
-        (let* ((dir       (dired-get-subdir))
-               (switches  (cdr (assoc-string dir dired-switches-alist))))
-          (dired-insert-subdir dir (and arg  (read-string "Switches for listing: "
-                                                          (or switches
-                                                              dired-subdir-switches
-                                                              dired-actual-switches)))))
-      (message "Redisplaying...")
-      ;; `message' is much faster than making `dired-map-over-marks' show progress
-      (dired-uncache (if (consp dired-directory) (car dired-directory) dired-directory))
-      (dired-map-over-marks (let ((fname                    (dired-get-filename))
-                                  ;; Postpone readin hook map over all marked files (Bug#6810).
-                                  (dired-after-readin-hook  nil))
-                              (message "Redisplaying... `%s'" fname)
-                              (dired-update-file-line fname))
-                            arg)
-      (run-hooks 'dired-after-readin-hook)
-      (dired-move-to-filename)
-      (message "Redisplaying...done"))))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'.
-;;
-(unless (boundp 'dired-subdir-switches) ; Emacs 20, 21
-  (defun dired-do-redisplay (&optional arg test-for-subdir) ; Bound to `l'
-    "Redisplay all marked (or next ARG) files.
-If on a subdir line, redisplay that subdirectory.  In that case,
-a prefix arg lets you edit the `ls' switches used for the new listing."
-    ;; Moves point if the next ARG files are redisplayed.
-    (interactive "P\np")
-    (if (and test-for-subdir  (dired-get-subdir))
-        (dired-insert-subdir (dired-get-subdir)
-                             (and arg  (read-string "Switches for listing: " dired-actual-switches)))
-      (message "Redisplaying...")
-      ;; `message' is much faster than making dired-map-over-marks show progress
-      (dired-uncache (if (consp dired-directory) (car dired-directory) dired-directory))
-      (dired-map-over-marks (let ((fname  (dired-get-filename)))
-                              (message "Redisplaying... `%s'" fname)
-                              (dired-update-file-line fname))
-                            arg)
-      (dired-move-to-filename)
-      (message "Redisplaying...done"))))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-(when (fboundp 'get-window-with-predicate) ; Emacs 22+
-  (defun dired-dwim-target-directory ()
-    "Guess a target directory to use for Dired.
-If there is a Dired buffer displayed in another window, use its
-current subdir, else use current subdir of this Dired buffer."
-    (let ((this-dir  (and (eq major-mode 'dired-mode)  (dired-current-directory))))
-      ;; Non-dired buffer may want to profit from this function, e.g. `vm-uudecode'.
-      (if dired-dwim-target
-          (let* ((other-win  (get-window-with-predicate (lambda (window)
-                                                          (with-current-buffer (window-buffer window)
-                                                            (eq major-mode 'dired-mode)))
-                                                        nil
-                                                        (and diredp-dwim-any-frame-flag  'visible)))
-                 (other-dir  (and other-win  (with-current-buffer (window-buffer other-win)
-                                               (and (eq major-mode 'dired-mode)  (dired-current-directory))))))
-            (or other-dir  this-dir))
-        this-dir))))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; 1. Added behavior for non-positive prefix arg:
-;;    * Construct a cons DIRNAME arg.
-;;    * Read a Dired buffer name (not a directory) for its car.
-;;    * If READ-EXTRA-FILES-P is non-nil then read any number of file and dir names, to be included as its cdr. 
-;;    * If chosen Dired buffer exists and is an ordinary listing then start out with its `directory-files'.
-;;
-;; 2. If you use Icicles then this is a multi-command - see doc for `dired' defadvice.
-;;
-(defun dired-read-dir-and-switches (string &optional read-extra-files-p dired-buffer)
-  "Read arguments for `dired' commands.
-STRING is added to the prompt after \"Dired \".  If not \"\", it should
-end with a space.
-
-With a non-negative prefix arg, read the `ls' switches.
-With a non-negative prefix arg or none, read the directory to Dired.
-
-With a non-positive prefix arg:
-* If DIRED-BUFFER is non-nil, it is the name of the Dired buffer to
-  use.  Otherwise, read it (it is not necessarily a directory name).
-  If in Dired now, the current buffer name is the default.
-* If READ-EXTRA-FILES-P is non-nil then read any number of directory
-  or file names, to make up the Dired arbitrary-files listing.  You
-  can use file-name wildcards (i.e., `*' for globbing), to include the
-  matching files and directories.  Use `C-g' when done entering the
-  files and directories to list.
-
-Return a list of arguments for `dired': (DIRNAME SWITCHES).  DIRNAME
-here has the same form as `dired-directory'.  When a non-positive
-prefix arg is used, DIRNAME is a cons of the buffer name and the list
-of file names.
-
-If you use Icicles then reading uses Icicles completion, with
-additional multi-command keys.  See `dired' (defadvice doc)."
-  (let* ((switchs                                     (and current-prefix-arg
-                                                           (natnump (prefix-numeric-value current-prefix-arg))
-                                                           (read-string "Dired listing switches: "
-                                                                        dired-listing-switches)))
-         (icicle-candidate-action-fn
-          (lambda (cand)
-            (dired-other-window cand (and current-prefix-arg  (read-string "Dired listing switches: "
-                                                                           dired-listing-switches)))
-            (select-window (minibuffer-window))
-            (select-frame-set-input-focus (selected-frame))))
-;;; $$$$$$ Alternative: Could choose no-op for non-dir candidate.
-;;;          (icicle-candidate-action-fn
-;;;           (lambda (cand)
-;;;             (cond ((file-directory-p cand)
-;;;                    (dired-other-window cand (and current-prefix-arg  (read-string "Dired listing switches: "
-;;;                                                                                   dired-listing-switches)))
-;;;                    (select-window (minibuffer-window))
-;;;                    (select-frame-set-input-focus (selected-frame)))
-;;;                   (t
-;;;                    (message "Not a directory: `%s'" cand) (sit-for 2)))))
-         (icicle-all-candidates-list-alt-action-fn ; M-|'
-          (lambda (files)
-            (let ((enable-recursive-minibuffers  t))
-              (dired-other-window (cons (read-string (format "Dired %s(buffer name): " string)) files)))))
-         (icicle-sort-comparer                        (or (and (boundp 'icicle-file-sort) ; If not reading files
-                                                               icicle-file-sort) ; then dirs first.
-                                                          (and (> (prefix-numeric-value current-prefix-arg) 0)
-                                                               'icicle-dirs-first-p)
-                                                          (and (boundp 'icicle-sort-comparer)
-                                                               icicle-sort-comparer)))
-
-         ;; The rest of the bindings are from `icicle-file-bindings', in `icicles-mac.el'.
-         (completion-ignore-case
-          (or (and (boundp 'read-file-name-completion-ignore-case)  read-file-name-completion-ignore-case)
-              completion-ignore-case))
-         (icicle-show-Completions-initially-flag      (and (boundp 'icicle-show-Completions-initially-flag)
-                                                           (or icicle-show-Completions-initially-flag
-                                                               icicle-files-ido-like-flag)))
-         (icicle-top-level-when-sole-completion-flag  (and (boundp 'icicle-top-level-when-sole-completion-flag)
-                                                           (or icicle-top-level-when-sole-completion-flag
-                                                               icicle-files-ido-like-flag)))
-         (icicle-default-value                        (and (boundp 'icicle-default-value)
-                                                           (if (and icicle-files-ido-like-flag
-                                                                    icicle-default-value)
-                                                               icicle-files-ido-like-flag
-                                                             ;;  Get default via `M-n', but do not insert it.
-                                                             (and (memq icicle-default-value '(t nil))
-                                                                  icicle-default-value))))
-         (icicle-must-match-regexp                    (and (boundp 'icicle-file-match-regexp)
-                                                           icicle-file-match-regexp))
-         (icicle-must-not-match-regexp                (and (boundp 'icicle-file-no-match-regexp)
-                                                           icicle-file-no-match-regexp))
-         (icicle-must-pass-after-match-predicate      (and (boundp 'icicle-file-predicate)
-                                                           icicle-file-predicate))
-         (icicle-require-match-flag                   (and (boundp 'icicle-file-require-match-flag)
-                                                           icicle-file-require-match-flag))
-         (icicle-file-completing-p                    t)
-         (icicle-extra-candidates                     (and (boundp 'icicle-file-extras)  icicle-file-extras))
-         (icicle-transform-function                   'icicle-remove-dups-if-extras)
-         ;; Put `icicle-file-sort' first.  If already in the list, move it, else add it, to beginning.
-         (icicle--temp-orders                         (and (boundp 'icicle-sort-orders-alist)
-                                                           (copy-sequence icicle-sort-orders-alist)))
-         (icicle-candidate-help-fn                    (lambda (cand)
-                                                        (icicle-describe-file cand current-prefix-arg t)))
-         (icicle-candidate-alt-action-fn              (and (boundp 'icicle-candidate-alt-action-fn)
-                                                           (or icicle-candidate-alt-action-fn
-                                                               (icicle-alt-act-fn-for-type "file"))))
-         (icicle-delete-candidate-object              'icicle-delete-file-or-directory)
-         (icicle-sort-orders-alist
-          (and (boundp 'icicle-sort-orders-alist)
-               (progn (when t ; $$$$ (and icicle-file-sort-first-time-p  icicle-file-sort)
-                        (setq icicle-sort-comparer  icicle-file-sort))
-                        ; $$$$ (setq icicle-file-sort-first-time-p  nil))
-                      (if icicle-file-sort
-                          (let ((already-there  (rassq icicle-file-sort icicle--temp-orders)))
-                            (if already-there
-                                (cons already-there (setq icicle--temp-orders  (delete already-there
-                                                                                       icicle--temp-orders)))
-                              (cons `("by `icicle-file-sort'" ,@icicle-file-sort) icicle--temp-orders)))
-                        icicle--temp-orders)))))
-    (when (fboundp 'icicle-bind-file-candidate-keys) (icicle-bind-file-candidate-keys))
-    (unwind-protect
-         (list
-          (if (> (prefix-numeric-value current-prefix-arg) 0)
-              ;; If a dialog box is about to be used, call `read-directory-name' so the dialog
-              ;; code knows we want directories.  Some dialog boxes can only select directories
-              ;; or files when popped up, not both. If no dialog box is used, call `read-file-name'
-              ;; because the user may want completion of file names for use in a wildcard pattern.
-              (funcall (if (and (fboundp 'read-directory-name)  (next-read-file-uses-dialog-p))
-                           #'read-directory-name
-                         #'read-file-name)
-                       (format "Dired %s(directory): " string) nil default-directory nil)
-            (dolist (db  dired-buffers) ; Remove any killed buffers from `dired-buffers' (even if DIRED-BUFFER).
-              (unless (buffer-name (cdr db)) (setq dired-buffers  (delq db dired-buffers))))
-            (let* ((dbufs   (and (not dired-buffer)
-                                 (mapcar (lambda (db) (list (buffer-name (cdr db)))) dired-buffers)))
-                   (dirbuf  (or dired-buffer
-                                (completing-read (format "Dired %s(buffer name): " string) dbufs nil nil nil nil
-                                                 (and (derived-mode-p 'dired-mode)  (buffer-name)))))
-                   (files   (and (diredp-existing-dired-buffer-p dirbuf)
-                                 (with-current-buffer (get-buffer dirbuf)
-                                   (and (not (consp dired-directory))
-                                        (directory-files dired-directory 'FULL diredp-re-no-dot)))))
-                   file)
-              (when read-extra-files-p
-                (while (condition-case nil ; Use lax completion, to allow wildcards.
-                           (setq file  (read-file-name "File or dir (C-g when done): "))
-                         (quit nil))
-                  ;; Do not allow root dir (`/' or a Windows drive letter, e.g. `d:/').
-                  (if (diredp-root-directory-p file)
-                      (progn (message "Cannot choose root directory") (sit-for 1))
-                    (push file files))))
-              (cons dirbuf files)))
-          switchs)
-      (when (fboundp 'icicle-unbind-file-candidate-keys) (icicle-unbind-file-candidate-keys)))))
-
-
-;;; $$$$$$$$ An alternative implementation - different behavior.
-;;;
-;;; ;; REPLACE ORIGINAL in `dired.el'.
-;;; ;;
-;;; ;; Non-positive prefix arg means construct cons DIRNAME arg: Read Dired name and files/dirs.
-;;; ;;
-;;; (defun dired-read-dir-and-switches (string)
-;;;   "Read arguments for `dired'.
-;;; With a non-negative prefix arg, prompt first for `ls' switches.
-;;; With a non-positive prefix arg, read the Dired buffer name and then
-;;;  read any number of dir or file names, to make up the Dired listing.
-
-;;; STRING is appended to the prompt, unless prefix arg is non-positive.
-;;; If non-empty, STRING should begin with a SPC."
-;;;   (let ((switches    (and current-prefix-arg
-;;;                           (>= (prefix-numeric-value current-prefix-arg) 0)
-;;;                           (read-string "Dired listing switches: " dired-listing-switches)))
-;;;         (formt       (format "Dired %s(directory): " string))
-;;;         (entries     ())
-;;;         (curr-entry  ""))
-;;;     (when (and current-prefix-arg  (<= (prefix-numeric-value current-prefix-arg) 0))
-;;;       (push (completing-read "Dired buffer name: " dired-buffers) entries)
-;;;       (setq curr-entry  (read-file-name (format "Dir or file: ") nil "" 'MUST-MATCH))
-;;;       (while (not (equal "" curr-entry))
-;;;         (push curr-entry entries)
-;;;         (setq curr-entry  (read-file-name (format "Dir or file: ") nil "" 'MUST-MATCH)))
-;;;       (unless (cadr entries) (push default-directory entries)))
-;;;     (list (or (nreverse entries)  (if (and (fboundp 'next-read-file-uses-dialog-p)
-;;;                                            (next-read-file-uses-dialog-p))
-;;;                                       (read-directory-name formt nil default-directory nil)
-;;;                                     (read-file-name formt nil default-directory nil)))
-;;;           switches)))
-
-
-;; ADVISE ORIGINAL in `dired.el'.
-;;
-;; Add to doc string, to document non-positive prefix arg.
-;;
-(defadvice dired (before diredp-doc-cons-arg activate)
-  "Interactively, a prefix argument changes the behavior as follows:
-
-* If >= 0, you are first prompted for the `ls' switches to use.
-
-* If <= 0, you are prompted first for the name of the Dired  buffer.
-  Then you are prompted repeatedly for the names of the directories
-  or files to list in the buffer.  You can use file-name wildcards
-  (i.e., `*' for globbing), to include the matching files and
-  directories.  Use `C-g' to end.
-
-  In other words, instead of listing a single directory, the Dired
-  buffer can list any number of directories and file names, which can
-  even belong to different directory trees.
-
-The rest of this description applies only if you use Icicles.
-
-In Icicle mode this is a multi-command: You can cycle among file-name
-completion candidates and act individually on those that name
-directories.  The action is to open Dired for the directory.  While
-cycling, these keys are active:
-
-\\<minibuffer-local-completion-map>\
-`C-mouse-2', `C-return' - Act on current completion candidate only
-`C-down', `C-wheel-down' - Move to next completion candidate and act
-`C-up', `C-wheel-up' - Move to previous completion candidate and act
-`C-next'  - Move to next apropos-completion candidate and act
-`C-prior' - Move to previous apropos-completion candidate and act
-`C-end'   - Move to next prefix-completion candidate and act
-`C-home'  - Move to previous prefix-completion candidate and act
-`\\[icicle-all-candidates-action]'     - Act on *all* candidates, successively (careful!)
-`\\[icicle-all-candidates-list-alt-action]'     - Open Dired on all candidates
-
-When candidate action and cycling are combined (e.g. `C-next'), user
-option `icicle-act-before-cycle-flag' determines which occurs first.
-
-With prefix `C-M-' instead of `C-', the same keys (`C-M-mouse-2',
-`C-M-RET', `C-M-down', and so on) provide help about candidates.
-
-Use `mouse-2', `RET', or `S-RET' to finally choose a candidate, or
-`C-g' to quit.
-
-These keys are also bound in the minibuffer during completion (`*'
-means the key requires library `Bookmark+'):
-
-   S-delete    - Delete candidate file or (empty) dir
-   C-c +       - Create a new directory
-   C-backspace - Go up one directory level
- * C-x C-t *   - Narrow to files with all of the tags you specify
- * C-x C-t +   - Narrow to files with some of the tags you specify
- * C-x C-t % * - Narrow to files with all tags matching a regexp
- * C-x C-t % + - Narrow to files with some tags  matching a regexp
- * C-x a +     - Add tags to the current-candidate file
- * C-x a -     - Remove tags from the current-candidate file
- * C-x m       - Access file bookmarks (not just autofiles)"
-  (interactive (dired-read-dir-and-switches "" 'READ-EXTRA-FILES-P)))
-
-
-;; ADVISE ORIGINAL in `dired.el'.
-;;
-;; Add to doc string, to document non-positive prefix arg.
-;;
-(defadvice dired-other-window (before diredp-doc-cons-arg activate)
-  "Interactively, a prefix argument changes the behavior.
-A non-positive prefix arg lets you choose an explicit set of files and
-directories to list.  See the advice for `dired' for more information."
-  (interactive (dired-read-dir-and-switches "" 'READ-EXTRA-FILES-P)))
-
-
-;; ADVISE ORIGINAL in `dired.el'.
-;;
-;; Add to doc string, to document non-positive prefix arg.
-;;
-(defadvice dired-other-frame (before diredp-doc-cons-arg activate)
-  "Interactively, a prefix argument changes the behavior.
-A non-positive prefix arg lets you choose an explicit set of files and
-directories to list.  See the advice for `dired' for more information."
-  (interactive (dired-read-dir-and-switches "" 'READ-EXTRA-FILES-P)))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; Made compatible with Emacs 20, 21, which do not have [:alnum].
-;; Also, this is defined here because it is used elsewhere in the file.
-;;
-(defun dired-switches-escape-p (switches)
-  "Return non-nil if the string SWITCHES contains `-b' or `--escape'."
-  (if (fboundp 'dired-switches-check)   ; Emacs 24.4+ - see Emacs bug #17218.
-      (dired-switches-check switches "escape" "b")
-    ;; Do not match things like "--block-size" that happen to contain "b".
-    (if (> emacs-major-version 21)      ; SWITCHES must be a string here, not nil.
-        (diredp-string-match-p "\\(\\`\\| \\)-[[:alnum:]]*b\\|--escape\\>" switches)
-      (diredp-string-match-p "\\(\\`\\| \\)-\\(\w\\|[0-9]\\)*b\\|--escape\\>" switches))))
-
-
-;; From `dired.el'
-
-(when (and (> emacs-major-version 22)  (featurep 'ls-lisp+))
-
-;;; 2012/04/26: Commented this out.
-;;;             Might need it again when update `ls-lisp+.el' to fix other things.
-;;;
-;;;   ;; Use t as WILDCARD arg to `dired-insert-directory'.
-;;;   ;;
-;;;   (defun dired-readin-insert ()
-;;;     ;; Insert listing for the specified dir (and maybe file  list)
-;;;     ;; already in dired-directory, assuming a clean buffer.
-;;;     (let (dir  file-list)
-;;;       (if (consp dired-directory)
-;;;           (setq dir        (car dired-directory)
-;;;                 file-list  (cdr dired-directory))
-;;;         (setq dir        dired-directory
-;;;               file-list  ()))
-;;;       (setq dir  (expand-file-name dir))
-;;;       (if (and (equal "" (file-name-nondirectory dir))  (not file-list))
-;;;           ;; If we are reading a whole single directory...
-;;;           (dired-insert-directory dir dired-actual-switches nil nil t)
-;;;         (unless (file-readable-p (directory-file-name (file-name-directory dir)))
-;;;           (error "Directory `%s' inaccessible or nonexistent" dir))
-;;;         ;; Else treat it as a wildcard spec.
-;;;         (dired-insert-directory dir dired-actual-switches file-list t t))))
-
-
-  ;; REPLACE ORIGINAL in `dired.el'.
-  ;;
-  ;; Compute WILDCARD arg for `insert-directory' for individual file (don't just use nil).
-  ;;
-  (defun dired-insert-directory (dir switches &optional file-list wildcard hdr)
-    "Insert a directory listing of DIR, Dired style.
-Use SWITCHES to make the listings.
-If FILE-LIST is non-nil, list only those files.
-Otherwise, if WILDCARD is non-nil, expand wildcards;
- in that case, DIR should be a file name that uses wildcards.
-In other cases, DIR should be a directory name or a directory filename.
-If HDR is non-nil, insert a header line with the directory name."
-    (let ((opoint               (point))
-          (process-environment  (copy-sequence process-environment))
-          end)
-      (when (and
-             ;; Do not try to invoke `ls' if on DOS/Windows, where `ls-lisp' is used, unless
-             ;; the user really wants to use `ls', as indicated by
-             ;; `ls-lisp-use-insert-directory-program'.
-             (or (not (featurep 'ls-lisp))  ls-lisp-use-insert-directory-program)
-             (or (if (eq dired-use-ls-dired 'unspecified)
-                     ;; Check if "ls --dired" gives exit code 0.  Put it in `dired-use-ls-dired'.
-                     (or (setq dired-use-ls-dired  (eq 0 (call-process insert-directory-program
-                                                                       nil nil nil "--dired")))
-                         (progn (message "Command `ls' does not support switch `--dired' - see \
-`dired-use-ls-dired'.")
-                                nil))
-                   dired-use-ls-dired)
-                 (file-remote-p dir)))
-        (setq switches  (concat "--dired " switches)))
-      ;; We used to specify the C locale here, to force English month names.  This should not be
-      ;; necessary any more with the new value of `directory-listing-before-filename-regexp'.
-      (if file-list
-          (dolist (f  file-list)
-            (let ((beg  (point)))
-              ;; Compute wildcard arg for this file.
-              (insert-directory f switches (diredp-string-match-p "[[?*]" f) nil)
-              ;; Re-align fields, if necessary.
-              (dired-align-file beg (point))))
-        (insert-directory dir switches wildcard (not wildcard)))
-      ;; Quote certain characters, unless `ls' quoted them for us.
-      (unless (dired-switches-escape-p dired-actual-switches)
-        (save-excursion
-          (setq end  (point-marker))
-          (goto-char opoint)
-          (while (search-forward "\\" end t)
-            (replace-match (apply #'propertize "\\\\" (text-properties-at (match-beginning 0)))
-                           nil t))
-          (goto-char opoint)
-          (while (search-forward "\^m" end t)
-            (replace-match (apply #'propertize "\\015" (text-properties-at (match-beginning 0)))
-                           nil t))
-          (set-marker end nil))
-        ;; Comment in original, from some Emacs Dev developer:
-        ;;
-        ;; Replace any newlines in DIR with literal "\n" for the sake of the header line.  To
-        ;; disambiguate a literal "\n" in the actual dirname, we also replace "\" with "\\".
-        ;; I think this should always be done, irrespective of the value of
-        ;; dired-actual-switches, because:
-        ;;   i) Dired does not work with an unescaped newline in the directory name used in the
-        ;;      header (bug=10469#28), and
-        ;;  ii) "\" is always replaced with "\\" in the listing, so doing it in the header as
-        ;;      well makes things consistent.
-        ;; But at present it is done only if "-b" is in ls-switches, because newlines in dirnames
-        ;; are uncommon, and people may have gotten used to seeing unescaped "\" in the headers.
-        ;; Note: adjust `dired-build-subdir-alist' if you change this.
-        (setq dir  (replace-regexp-in-string "\\\\" "\\\\" dir nil t)
-              dir  (replace-regexp-in-string "\n" "\\n" dir nil t)))
-      ;; If we used `--dired' and it worked, the lines are already indented.  Else indent them.
-      (unless (save-excursion (goto-char opoint) (diredp-looking-at-p "  "))
-        (let ((indent-tabs-mode  nil)) (indent-rigidly opoint (point) 2)))
-      ;; Insert text at the beginning to standardize things.
-      (let ((content-point opoint))
-        (save-excursion
-          (goto-char opoint)
-          (when (and (or hdr  wildcard)  (not (and (looking-at "^  \\(.*\\):$")
-                                                   (file-name-absolute-p (match-string 1)))))
-            ;; `dired-build-subdir-alist' will replace the name by its expansion, so it does not
-            ;; matter whether what we insert here is fully expanded, but it should be absolute.
-            (insert "  " (directory-file-name (file-name-directory dir)) ":\n")
-            (setq content-point (point)))
-          (when wildcard
-            ;; Insert "wildcard" line where "total" line would be for a full dir.
-            (insert "  wildcard " (file-name-nondirectory dir) "\n")))
-        (dired-insert-set-properties content-point (point))))))
-
-
-;;; Image stuff.
-
-(defun diredp-image-dired-required-msg ()
-  "Raise an error if `image-dired.el' is not loaded."
-  (unless (require 'image-dired nil t) (error "This command requires library `image-dired.el'")))
-
-;; See `image-dired-create-thumb'.
-;; Define this even if `image-dired.el' is not loaded.
-;; Do NOT raise an error if not loaded, because this is used in `diredp-mouseover-help'.
-;;;###autoload
-(defun diredp-image-dired-create-thumb (file &optional arg)
-  "Create thumbnail image file for FILE (default: file on current line).
-With a prefix arg, replace any existing thumbnail for FILE.
-With a numeric prefix arg (not a cons), use it as the thumbnail size.
-Return the name of the thumbnail image file, or nil if none."
-  (interactive (list (if (derived-mode-p 'dired-mode)
-                         (dired-get-filename nil 'NO-ERROR)
-                       ;; Make it work also for `diredp-list-files' listings.
-                       (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
-                     current-prefix-arg))
-  (and (fboundp 'image-dired-thumb-name) ; No-op (return nil) if `image-dired.el' not loaded.
-       (let ((thumb-name  (image-dired-thumb-name file)))
-         (when arg (clear-image-cache))
-         (when (or arg  (not (file-exists-p thumb-name)))
-           (let ((image-dired-thumb-width   (or (and arg  (atom arg)  arg)  image-dired-thumb-width))
-                 (image-dired-thumb-height  (or (and arg  (atom arg)  arg)  image-dired-thumb-height)))
-             (unless (zerop (image-dired-create-thumb file thumb-name))
-               (error "Thumbnail image file could not be created"))))
-         (and (file-exists-p thumb-name)  thumb-name))))
-
-
-;; REPLACE ORIGINAL in `image-dired.el' (Emacs 22-23).
-;;
-;; 1. Raise an error if `image-dired.el' is not available.
-;; 2. Repro it here so it picks up `Dired+' version of `dired-map-over-marks'.
-;;
-;;;###autoload
-(defun image-dired-dired-insert-marked-thumbs () ; Bound to `C-t C-t' (Emacs 22-23)
-  "Insert thumbnails before file names of marked files in the Dired buffer."
-  (interactive (progn (diredp-image-dired-required-msg) ()))
-  (dired-map-over-marks
-   (let* ((image-pos   (dired-move-to-filename))
-          (image-file  (dired-get-filename))
-          (thumb-file  (image-dired-get-thumbnail-image image-file))
-          overlay)
-     ;; If image is not already added, then add it.
-     (unless (delq nil (mapcar (lambda (o) (overlay-get o 'put-image))
-                               ;; Can't use (overlays-at (point)), BUG?
-                               (overlays-in (point) (1+ (point)))))
-       (put-image thumb-file image-pos)
-       (setq overlay  (car (delq nil (mapcar (lambda (ov) (and (overlay-get ov 'put-image)  ov))
-                                             (overlays-in (point) (1+ (point)))))))
-       (overlay-put overlay 'image-file image-file)
-       (overlay-put overlay 'thumb-file thumb-file)))
-   nil)
-  (add-hook 'dired-after-readin-hook 'image-dired-dired-after-readin-hook nil t))
-
-
-;; REPLACE ORIGINAL in `image-dired.el' (Emacs 24+).
-;;
-;; 1. Raise an error if `image-dired.el' is not available.
-;; 2. Repro it here so it picks up `Dired+' version of `dired-map-over-marks'.
-;;
-;;;###autoload
-(defun image-dired-dired-toggle-marked-thumbs (&optional arg) ; Bound to `C-t C-t' (Emacs 24+)
-  "Toggle thumbnails in front of file names in Dired.
-If no files are marked, insert or hide thumbnails on the current line.
-With a numeric prefix arg N, ignore marked files and act on the next N
-files (previous -N files, if N < 0)."
-  (interactive (progn (diredp-image-dired-required-msg) (list current-prefix-arg)))
-  (dired-map-over-marks
-   (let* ((image-pos   (dired-move-to-filename))
-          (image-file  (diredp-get-image-filename nil 'NO-ERROR))
-          thumb-file  overlay)
-     (when image-file
-       (setq thumb-file  (image-dired-get-thumbnail-image image-file))
-       ;; If image is not already added, then add it.
-       (let* ((cur-ovs   (overlays-in (point) (1+ (point))))
-              (thumb-ov  (car (diredp-remove-if-not (lambda (ov) (overlay-get ov 'thumb-file))
-                                                    cur-ovs))))
-         (if thumb-ov
-             (delete-overlay thumb-ov)
-           (put-image thumb-file image-pos)
-           (setq overlay (car (delq nil (mapcar (lambda (ov) (and (overlay-get ov 'put-image)  ov))
-                                                (overlays-in (point) (1+ (point)))))))
-           (overlay-put overlay 'image-file image-file)
-           (overlay-put overlay 'thumb-file thumb-file)))))
-   arg
-   'SHOW-PROGRESS)
-  (add-hook 'dired-after-readin-hook 'image-dired-dired-after-readin-hook nil t))
-
-;; Corresponds to `image-dired-dired-comment-files'.
-;;;###autoload
-(defun diredp-image-dired-comment-file ()
-  "Add comment to this image file."
-  (interactive (progn (diredp-image-dired-required-msg) ()))
-  (image-dired-write-comments (cons (dired-get-filename) (image-dired-read-comment))))
-
-;; Corresponds to `image-dired-tag-files'.
-;;;###autoload
-(defun diredp-image-dired-tag-file ()
-  "Tag this image file with an `image-dired' tag."
-  (interactive (progn (diredp-image-dired-required-msg) ()))
-  (image-dired-write-tags (cons (dired-get-filename)
-                                (read-string "Tags to add (use `;' to separate): "))))
-
-;; Corresponds to `image-dired-delete-tag'.
-;;;###autoload
-(defun diredp-image-dired-delete-tag ()
-  "Remove an `image-dired' tag from  this image file."
-  (interactive (progn (diredp-image-dired-required-msg) ()))
-  (image-dired-remove-tag (list (dired-get-filename)) (read-string "Tag to remove: ")))
-
-;; Corresponds to `image-dired-display-thumbs'.
-;;;###autoload
-(defun diredp-image-dired-display-thumb (&optional append)
-  "Pop to thumbnail of this image file, in `image-dired-thumbnail-buffer'.
-If a thumbnail image does not yet exist for this file, create it.
-With a prefix arg, append the thumbnail to the thumbnails buffer,
-instead of clearing the buffer first."
-  (interactive (progn (diredp-image-dired-required-msg) (list current-prefix-arg)))
-  (let* ((dired-buf   (current-buffer))
-         (curr-file   (dired-get-filename))
-         (thumb-name  (image-dired-thumb-name curr-file)))
-    (with-current-buffer (image-dired-create-thumbnail-buffer)
-      (let ((inhibit-read-only  t))
-        (if (not append) (erase-buffer) (goto-char (point-max)))
-        (if (and (not (file-exists-p thumb-name))
-                 (not (zerop (image-dired-create-thumb curr-file thumb-name))))
-            (message "Cannot create thumbnail image for file `%s'" curr-file)
-          (image-dired-insert-thumbnail thumb-name curr-file dired-buf)))
-      (cond ((eq 'dynamic image-dired-line-up-method)     (image-dired-line-up-dynamic))
-            ((eq 'fixed image-dired-line-up-method)       (image-dired-line-up))
-            ((eq 'interactive image-dired-line-up-method) (image-dired-line-up-interactive))
-            ((eq 'none image-dired-line-up-method)        nil)
-            (t                                            (image-dired-line-up-dynamic))))
-    (pop-to-buffer image-dired-thumbnail-buffer)))
-
-;; Corresponds to `image-dired-copy-with-exif-file-name'.
-;;;###autoload
-(defun diredp-image-dired-copy-with-exif-name ()
-  "Copy this image file to your main image directory.
-Uses `image-dired-get-exif-file-name' to name the new file."
-  (interactive (progn (diredp-image-dired-required-msg) ()))
-  (let* ((curr-file  (dired-get-filename))
-         (new-name   (format "%s/%s" (file-name-as-directory
-                                      (expand-file-name image-dired-main-image-directory))
-                             (image-dired-get-exif-file-name curr-file))))
-    (message "Copying `%s' to `%s'..." curr-file new-name)
-    (copy-file curr-file new-name)
-    (message "Copying `%s' to `%s'...done" curr-file new-name)))
-
-;; Corresponds to `image-dired-dired-edit-comment-and-tags'.
-;;;###autoload
-(defun diredp-image-dired-edit-comment-and-tags ()
-  "Edit comment and tags for this image file."
-  (interactive (progn (diredp-image-dired-required-msg) ()))
-  (setq image-dired-widget-list  ())
-  (let ((file  (dired-get-filename)))
-    (if (fboundp 'pop-to-buffer-same-window)
-        (pop-to-buffer-same-window "*Image-Dired Edit Meta Data*")
-      (switch-to-buffer "*Image-Dired Edit Meta Data*"))
-    (kill-all-local-variables)
-    (make-local-variable 'widget-example-repeat)
-    (let ((inhibit-read-only  t))
-      (erase-buffer)
-      (remove-overlays)
-      (widget-insert
-       "\nEdit comment and tags for the image.  Separate multiple tags
-with a comma (`,').  Move forward among fields using `TAB' or `RET'.
-Move backward using `S-TAB'.  Click `Save' to save your edits or
-`Cancel' to abandon them.\n\n")
-      (let* ((thumb-file  (image-dired-thumb-name file))
-             (img         (create-image thumb-file))
-             comment-widget  tag-widget)
-        (insert-image img)
-        (widget-insert "\n\nComment: ")
-        (setq comment-widget  (widget-create 'editable-field :size 60 :format "%v "
-                                             :value (or (image-dired-get-comment file)  "")))
-        (widget-insert "\nTags:    ")
-        (setq tag-widget  (widget-create 'editable-field :size 60 :format "%v "
-                                         :value (or (mapconcat #'identity (image-dired-list-tags file) ",")  "")))
-        ;; Save info in widgets to use when the user saves the form.
-        (setq image-dired-widget-list  (append image-dired-widget-list
-                                               (list (list file comment-widget tag-widget))))
-        (widget-insert "\n\n")))
-    (widget-insert "\n")
-    (widget-create 'push-button :notify (lambda (&rest _ignore)
-                                          (image-dired-save-information-from-widgets)
-                                          (bury-buffer)
-                                          (message "Done"))
-                   "Save")
-    (widget-insert " ")
-    (widget-create 'push-button :notify (lambda (&rest _ignore)
-                                          (bury-buffer)
-                                          (message "Operation canceled"))
-                   "Cancel")
-    (widget-insert "\n")
-    (use-local-map widget-keymap)
-    (widget-setup)
-    (widget-forward 1)))                ; Jump to the first widget.
-
-;;;###autoload
-(defun diredp-do-display-images (&optional arg)
-  "Display the marked image files.
-A prefix argument ARG specifies files to use instead of those marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any files are marked).
- More than one `C-u' means use all files in the Dired buffer, as if
- they were all marked."
-  (interactive (progn (unless (require 'image-file nil t)
-                        (error "This command requires library `image-file.el'"))
-                      (diredp-ensure-mode)
-                      (list current-prefix-arg)))
-  (dired-map-over-marks-check #'diredp-display-image arg 'display\ image
-                              (diredp-fewer-than-2-files-p arg)))
-
-(defun diredp-display-image ()
-  "Display image file at point.  Log an error using `dired-log'."
-  (let ((file     (dired-get-filename 'LOCAL 'NO-ERROR))
-        (failure  nil))
-    (save-excursion
-      (if (let ((inhibit-changing-match-data  t))
-            (and file  (diredp-string-match-p (image-file-name-regexp) file)))
-          (condition-case err
-              (let ((find-file-run-dired  nil)) (find-file-other-window file))
-            (error (setq failure  (error-message-string err))))
-        (dired-log (format "Not an image file: `%s'" file))
-        (setq failure  t)))
-    (and failure                    ; Return nil for success.
-         (prog1 file                ; Return file name for failure.
-           (unless (eq t failure) (dired-log "Cannot display image file `%s':\n%s\n" file failure)  t)))))
-
-;;;###autoload
-(defun diredp-image-show-this-file (&optional arg)
-  "Show the image file named on this line in another frame or window.
-Option `diredp-image-show-this-file-use-frame-flag' which is used.
-
-With a prefix arg, shrink the image to fit a frame that many lines
-high or a window at least that many lines high.
-Otherwise, show the image full size.
-Note:
- * To show the image full size, you can also use `\\<dired-mode-map>\\[dired-find-file]'.
- * To show the image in another window, at whatever scale fits there,
-   you can use `\\[image-dired-dired-display-image]'."
-  (interactive (progn (diredp-image-dired-required-msg) (list current-prefix-arg)))
-  (image-dired-create-display-image-buffer)
-  (let ((fit-frame-inhibit-fitting-flag  t) ; In `fit-frame.el'.
-        (img-file                        (diredp-get-image-filename)))
-    (if img-file
-        (with-current-buffer image-dired-display-image-buffer
-          (let* ((window-min-height  (if arg
-                                         (prefix-numeric-value arg)
-                                       (ceiling (cdr (image-size (create-image img-file))))))
-                 (special-display-frame-alist   (if diredp-image-show-this-file-use-frame-flag
-                                                    (cons `(height . ,window-min-height)
-                                                          special-display-frame-alist)
-                                                  special-display-frame-alist))
-                 (special-display-buffer-names  (if diredp-image-show-this-file-use-frame-flag
-                                                    (cons image-dired-display-image-buffer
-                                                          special-display-buffer-names)
-                                                  special-display-buffer-names)))
-            (display-buffer image-dired-display-image-buffer)
-            (image-dired-display-image img-file (not arg))))
-      (message "No image file here")))) ; An error is handled by `diredp-get-image-filename'.
-
-(defun diredp-report-file-result (file result failure echop)
-  (cond (failure
-         (when echop (message "Error for %s:\n%s\n" file failure) (sit-for 1))
-         (dired-log "Error for %s:\n%s\n" file failure)
-         (dired-make-relative file))   ; Return file name for failure.
-        (t
-         (when echop (message "Result for %s:\n%s\n" file result) (sit-for 1))
-         (dired-log "Result for %s:\n%s\n" file result)
-         nil)))                      ; Return nil for success.
-
-;;;###autoload
-(defun diredp-do-emacs-command (command &optional arg)
-  "Invoke an Emacs COMMAND in each marked file.
-Visit each marked file at its beginning, then invoke COMMAND.
-You are prompted for the COMMAND.
-
-The result returned for each file is logged by `dired-log'.  Use `?'
-to see all such results and any error messages.  If there are fewer
-marked files than `diredp-do-report-echo-limit' then each result is
-also echoed momentarily.
-
-A prefix argument behaves according to the ARG argument of
-`dired-get-marked-files'.  In particular, `C-u C-u' operates on all
-files in the Dired buffer."
-  (interactive (progn (diredp-ensure-mode)
-                      (list (diredp-read-command) current-prefix-arg)))
-  (save-selected-window
-    (diredp-map-over-marks-and-report
-     #'diredp-invoke-emacs-command arg 'invoke\ emacs\ command (diredp-fewer-than-2-files-p arg)
-     command (diredp-fewer-than-echo-limit-files-p arg))))
-
-(defun diredp-invoke-emacs-command (command &optional echop)
-  "Visit file of this line at its beginning, then invoke COMMAND.
-Log the result returned or any error.
-Non-nil optional arg ECHOP means also echo the result."
-  (let* ((file     (dired-get-filename))
-         (failure  (not (file-exists-p file)))
-         result)
-    (unless failure
-      (condition-case err
-          (with-current-buffer (find-file-noselect file)
-            (save-excursion
-              (goto-char (point-min))
-              (setq result  (call-interactively command))))
-        (error (setq failure  err))))
-    (diredp-report-file-result file result failure echop)))
-
-(defun diredp-read-command (&optional prompt default)
-  "Read the name of a command and return a symbol with that name.
-\(A command is anything that satisfies predicate `commandp'.)
-Prompt with PROMPT, which defaults to \"Command: \".
-By default, return the command named DEFAULT (or, with Emacs 23+, its
-first element if DEFAULT is a list).  (If DEFAULT does not name a
-command then it is ignored.)"
-  (setq prompt  (or prompt  "Command: "))
-  (let ((name  (completing-read prompt obarray #'commandp t nil
-                                'extended-command-history default)))
-    (while (string= "" name)
-      (setq name  (completing-read prompt obarray #'commandp t nil
-                                   'extended-command-history default)))
-    (intern name)))
-
-(when (fboundp 'diredp-read-expression) ; Emacs 22+
-
-  (defun diredp-do-lisp-sexp (sexp &optional arg)
-    "Evaluate an Emacs-Lisp SEXP in each marked file.
-Visit each marked file at its beginning, then evaluate SEXP.
-You are prompted for the SEXP.
-
-The result returned for each file is logged by `dired-log'.  Use `?'
-to see all such results and any error messages.  If there are fewer
-marked files than `diredp-do-report-echo-limit' then each result is
-also echoed momentarily.
-
-A prefix argument behaves according to the ARG argument of
-`dired-get-marked-files'.  In particular, `C-u C-u' operates on all
-files in the Dired buffer."
-    (interactive (progn (diredp-ensure-mode)
-                        (list (diredp-read-expression "Sexp: ") current-prefix-arg)))
-    (save-selected-window
-      (diredp-map-over-marks-and-report
-       #'diredp-eval-lisp-sexp arg 'eval\ elisp\ sexp (diredp-fewer-than-2-files-p arg)
-       sexp (diredp-fewer-than-echo-limit-files-p arg))))
-
-  (defun diredp-eval-lisp-sexp (sexp &optional echop)
-    "Visit file of this line at its beginning, then evaluate SEXP.
-Log the result returned or any error.
-Non-nil optional arg ECHOP means also echo the result."
-    (let* ((file     (dired-get-filename))
-           (failure  (not (file-exists-p file)))
-           result)
-      (unless failure
-        (condition-case err
-            (with-current-buffer (find-file-noselect file)
-              (save-excursion
-                (goto-char (point-min))
-                (setq result  (eval-expression sexp))))
-          (error (setq failure  err))))
-      (diredp-report-file-result file result failure echop)))
-
-  )
- 
-;;; Face Definitions
-
-(defface diredp-autofile-name
-    '((((background dark)) (:background "#111313F03181")) ; Very dark blue
-      (t                   (:background "#EEECEC0FCE7E"))) ; Very pale goldenrod
-  "*Face used in Dired for names of files that are autofile bookmarks."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-autofile-name 'diredp-autofile-name)
-
-(defface diredp-compressed-file-name
-    '((((background dark)) (:foreground "Blue"))
-      (t                   (:foreground "Brown")))
-  "*Face used for compressed file names."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-compressed-file-name 'diredp-compressed-file-name)
-
-(defface diredp-compressed-file-suffix
-    '((((background dark)) (:foreground "Blue"))
-      (t                   (:foreground "Yellow")))
-  "*Face used for compressed file suffixes in Dired buffers.
-This means the `.' plus the file extension.  Example: `.zip'."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-compressed-file-suffix 'diredp-compressed-file-suffix)
-
-(defface diredp-date-time
-    '((((background dark)) (:foreground "#74749A9AF7F7")) ; ~ med blue
-      (t                   (:foreground "DarkGoldenrod4")))
-  "*Face used for date and time in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-date-time 'diredp-date-time)
-
-(defface diredp-deletion
-    '((t (:foreground "Yellow" :background "Red")))
-  "*Face used for deletion flags (D) in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-deletion 'diredp-deletion)
-
-(defface diredp-deletion-file-name
-    '((t (:foreground "Red")))
-  "*Face used for names of deleted files in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-deletion-file-name 'diredp-deletion-file-name)
-
-(defface diredp-dir-heading
-    '((((background dark)) (:foreground "Yellow" :background "#00003F3F3434")) ; ~ dark green
-      (t                   (:foreground "Blue" :background "Pink")))
-  "*Face used for directory headings in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-dir-heading 'diredp-dir-heading)
-
-(defface diredp-dir-name
-    '((((background dark))
-       (:foreground "#7474FFFFFFFF" :background "#2C2C2C2C2C2C")) ; ~ cyan, dark gray
-      (t (:foreground "DarkRed" :background "LightGray")))
-  "*Face used for directory names."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-dir-name 'diredp-dir-name)
-
-(defface diredp-dir-priv
-    '((((background dark))
-       (:foreground "#7474FFFFFFFF" :background "#2C2C2C2C2C2C")) ; ~ cyan, dark gray
-      (t (:foreground "DarkRed" :background "LightGray")))
-  "*Face used for directory privilege indicator (d) in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-dir-priv 'diredp-dir-priv)
-
-(defface diredp-exec-priv
-    '((((background dark)) (:background "#4F4F3B3B2121")) ; ~ dark brown
-      (t                   (:background "LightSteelBlue")))
-  "*Face used for execute privilege indicator (x) in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-exec-priv 'diredp-exec-priv)
-
-;; For this to show up, you need `F' among the options in `dired-listing-switches'.
-;; For example, I use "-alF" for `dired-listing-switches'.
-(defface diredp-executable-tag
-    '((t (:foreground "Red")))
-  "*Face used for executable tag (*) on file names in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-executable-tag 'diredp-executable-tag)
-
-(defface diredp-file-name
-    '((((background dark)) (:foreground "Yellow"))
-      (t                   (:foreground "Blue")))
-  "*Face used for file names (without suffixes) in Dired buffers.
-This means the base name.  It does not include the `.'."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-file-name 'diredp-file-name)
-
-(defface diredp-file-suffix
-    '((((background dark)) (:foreground "#7474FFFF7474")) ; ~ light green
-      (t                   (:foreground "DarkMagenta")))
-  "*Face used for file suffixes in Dired buffers.
-This means the `.' plus the file extension.  Example: `.elc'."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-file-suffix 'diredp-file-suffix)
-
-(defface diredp-flag-mark
-    '((((background dark)) (:foreground "Blue" :background "#7575D4D41D1D")) ; ~ olive green
-      (t                   (:foreground "Yellow" :background "Blueviolet")))
-  "*Face used for flags and marks (except D) in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-flag-mark 'diredp-flag-mark)
-
-(defface diredp-flag-mark-line
-    '((((background dark)) (:background "#787831311414")) ; ~ dark red brown
-      (t                   (:background "Skyblue")))
-  "*Face used for flagged and marked lines in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-flag-mark-line 'diredp-flag-mark-line)
-
-(defface diredp-ignored-file-name
-  '((((background dark)) (:foreground "#C29D6F156F15"))    ; ~ salmon
-    (t                   (:foreground "#00006DE06DE0"))) ; ~ dark cyan
-  "*Face used for files whose names are omitted based on the extension.
-See also face `diredp-omit-file-name'."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-ignored-file-name 'diredp-ignored-file-name)
-
-(defface diredp-link-priv
-    '((((background dark)) (:foreground "#00007373FFFF")) ; ~ blue
-      (t                   (:foreground "DarkOrange")))
-  "*Face used for link privilege indicator (l) in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-link-priv 'diredp-link-priv)
-
-(when (> emacs-major-version 21)
-  (defface diredp-mode-line-marked
-      '((t (:foreground "DarkViolet")))
-    "*Face for marked number in mode-line `mode-name' for Dired buffers."
-    :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-
-  (defface diredp-mode-line-flagged
-      '((t (:foreground "Red")))
-    "*Face for flagged number in mode-line `mode-name' for Dired buffers."
-    :group 'Dired-Plus :group 'font-lock-highlighting-faces))
-
-(defface diredp-no-priv
-    '((((background dark)) (:background "#2C2C2C2C2C2C")) ; ~ dark gray
-      (t                   (:background "LightGray")))
-  "*Face used for no privilege indicator (-) in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-no-priv 'diredp-no-priv)
-
-(defface diredp-number
-    '((((background dark)) (:foreground "#FFFFFFFF7474")) ; ~ light yellow
-      (t                   (:foreground "DarkBlue")))
-  "*Face used for numerical fields in Dired buffers.
-In particular, inode number, number of hard links, and file size."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-number 'diredp-number)
-
-(defface diredp-omit-file-name
-  (if (assq :inherit custom-face-attributes) ; Emacs 22+
-      '((((background dark)) (:inherit diredp-ignored-file-name :strike-through "#555555555555")) ; ~ dark gray
-        (t                   (:inherit diredp-ignored-file-name :strike-through "#AAAAAAAAAAAA"))) ; ~ light gray
-    '((((background dark)) (:foreground "#C29D6F156F15")) ; ~ salmon
-      (t                   (:foreground "#00006DE06DE0")))) ; ~ dark cyan
-  "*Face used for files whose names will be omitted in `dired-omit-mode'.
-This means file names that match regexp `diredp-omit-files-regexp'.
-\(File names matching `dired-omit-extensions' are highlighted with face
-`diredp-ignored-file-name' instead.)"
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-omit-file-name 'diredp-omit-file-name)
-
-(defface diredp-other-priv
-    '((((background dark)) (:background "#111117175555")) ; ~ dark blue
-      (t                   (:background "PaleGoldenrod")))
-  "*Face used for l,s,S,t,T privilege indicators in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-other-priv 'diredp-other-priv)
-
-(defface diredp-rare-priv
-    '((((background dark)) (:foreground "Green" :background "#FFFF00008080")) ; ~ hot pink
-      (t                   (:foreground "Magenta" :background "SpringGreen")))
-  "*Face used for rare privilege indicators (b,c,s,m,p,S) in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-rare-priv 'diredp-rare-priv)
-
-(defface diredp-read-priv
-    '((((background dark)) (:background "#999932325555")) ; ~ burgundy / dark magenta
-      (t                   (:background "MediumAquamarine")))
-  "*Face used for read privilege indicator (w) in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-read-priv 'diredp-read-priv)
-
-(defface diredp-symlink
-    '((((background dark)) (:foreground "#00007373FFFF")) ; ~ blue
-      (t                   (:foreground "DarkOrange")))
-  "*Face used for symbolic links in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-symlink 'diredp-symlink)
-
-(defface diredp-tagged-autofile-name
-    '((((background dark)) (:background "#328C0411328C")) ; Very dark magenta
-      (t                   (:background "#CD73FBEECD73"))) ; Very pale green
-  "*Face used in Dired for names of files that are autofile bookmarks."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-tagged-autofile-name 'diredp-tagged-autofile-name)
-
-(defface diredp-write-priv
-    '((((background dark)) (:background "#25258F8F2929")) ; ~ dark green
-      (t                   (:background "Orchid")))
-  "*Face used for write privilege indicator (w) in Dired buffers."
-  :group 'Dired-Plus :group 'font-lock-highlighting-faces)
-(defvar diredp-write-priv 'diredp-write-priv)
-
-;; Fix Emacs 20 recognition of fields up through file name when size is expressed using `k' etc.
-(when (and (< emacs-major-version 21)  (not (boundp 'diredp-loaded-p))
-           dired-move-to-filename-regexp ; These last two checks are just in case.
-           (eq (aref dired-move-to-filename-regexp 7) ?\  ))
-  (setq dired-move-to-filename-regexp  (concat "[0-9][BkKMGTPEZY]?"
-                                               (substring dired-move-to-filename-regexp 7))))
-
-;;; Define second level of fontifying.
-(defvar diredp-font-lock-keywords-1
-  (list
-   '("^  \\(.+:\\)$" 1 diredp-dir-heading) ; Directory headers
-   '("^  wildcard.*$" 0 'default)   ; Override others, e.g. `l' for `diredp-other-priv'.
-   '("^  (No match).*$" 0 'default) ; Override others, e.g. `t' for `diredp-other-priv'.
-   '("[^ .]\\(\\.[^. /]+\\)$" 1 diredp-file-suffix) ; Suffix, including `.'.
-   '("\\([^ ]+\\) -> .+$" 1 diredp-symlink)         ; Symbolic links
-
-   ;; 1) Date/time and 2) filename w/o suffix.
-   ;;    This is a bear, and it is fragile - Emacs can change `dired-move-to-filename-regexp'.
-   (if (or (not (fboundp 'version<))  (version< emacs-version "23.2"))
-       (list dired-move-to-filename-regexp
-             (list 1 'diredp-date-time t t) ; Date/time
-             (list (concat "\\(.+\\)\\(" (concat (funcall #'regexp-opt diredp-compressed-extensions)
-                                                 "\\)[*]?$")) ; Compressed-file name
-                   nil nil (list 0 diredp-compressed-file-name 'keep t)))
-     `(,dired-move-to-filename-regexp
-       (7 diredp-date-time t t) ; Date/time, locale (western or eastern)
-       (2 diredp-date-time t t) ; Date/time, ISO
-       (,(concat "\\(.+\\)\\(" (concat (funcall #'regexp-opt diredp-compressed-extensions)
-                                       "\\)[*]?$"))
-        nil nil (0 diredp-compressed-file-name keep t)))) ; Compressed-file suffix
-   (if (or (not (fboundp 'version<))  (version< emacs-version "23.2"))
-       (list dired-move-to-filename-regexp
-             (list 1 'diredp-date-time t t) ; Date/time
-             (list "\\(.+\\)$" nil nil (list 0 diredp-file-name 'keep t))) ; Filename
-     `(,dired-move-to-filename-regexp
-       (7 diredp-date-time t t) ; Date/time, locale (western or eastern)
-       (2 diredp-date-time t t) ; Date/time, ISO
-       ("\\(.+\\)$" nil nil (0 diredp-file-name keep t)))) ; Filename (not a compressed file)
-
-   ;; Files to ignore.
-   ;;   Use face `diredp-ignored-file-name' for omission by file-name extension.
-   ;;   Use face `diredp-omit-file-name' for omission by entire file name.
-   (let* ((omit-exts   (or (and (boundp 'dired-omit-extensions)  dired-omit-extensions)
-                           completion-ignored-extensions))
-          (omit-exts   (and omit-exts  (mapconcat #'regexp-quote omit-exts "\\|")))
-          (compr-exts  (and diredp-ignore-compressed-flag
-                            (concat "\\|" (mapconcat #'regexp-quote diredp-compressed-extensions "\\|")))))
-     (list (concat "^  \\(.*\\(" omit-exts compr-exts "\\)[*]?\\)$") ; [*]? allows for executable flag (*).
-           1 diredp-ignored-file-name t))
-   `(,(concat "^.*" dired-move-to-filename-regexp
-              "\\(" diredp-omit-files-regexp "\\).*[*]?$") ; [*]? allows for executable flag (*).
-     (0 diredp-omit-file-name t))
-
-   ;; Compressed-file (suffix)
-   (list (concat "\\(" (funcall #'regexp-opt diredp-compressed-extensions) "\\)[*]?$")
-         1 diredp-compressed-file-suffix t)
-   '("\\([*]\\)$" 1 diredp-executable-tag t) ; Executable (*)
-
-   ;; Inode, hard-links, & file size (. and , are for the decimal point, depending on locale)
-   ;; See comment for `directory-listing-before-filename-regexp' in `files.el' or `files+.el'.
-   '("\\(\\([0-9]+\\([.,][0-9]+\\)?\\)[BkKMGTPEZY]?[ /]?\\)" 1 diredp-number)
-
-   ;; Directory names - exclude d:/..., Windows drive letter in a dir heading.
-   (list (concat dired-re-maybe-mark dired-re-inode-size "\\(d\\)[^:]")
-         '(1 diredp-dir-priv t) '(".+" (dired-move-to-filename) nil (0 diredp-dir-name t)))
-
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]........\\(x\\)") ; o x
- 	 '(1 diredp-exec-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]........\\([lsStT]\\)") ; o misc
- 	 '(1 diredp-other-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].......\\(w\\).") ; o w
- 	 '(1 diredp-write-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]......\\(r\\)..") ; o r
- 	 '(1 diredp-read-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].....\\(x\\)...") ; g x
- 	 '(1 diredp-exec-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].....\\([lsStT]\\)...") ; g misc
- 	 '(1 diredp-other-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]....\\(w\\)....") ; g w
- 	 '(1 diredp-write-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]...\\(r\\).....") ; g r
- 	 '(1 diredp-read-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]..\\(x\\)...") ; u x
- 	 '(1 diredp-exec-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]..\\([lsStT]\\)...") ; u misc
- 	 '(1 diredp-other-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].\\(w\\)....") ; u w
- 	 '(1 diredp-write-priv))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]\\(r\\).....") ; u r
- 	 '(1 diredp-read-priv))
-
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]........\\([-rwxlsStT]\\)") ; o -
- 	 '(1 diredp-no-priv keep))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].......\\([-rwxlsStT]\\).") ; g -
- 	 '(1 diredp-no-priv keep))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]......\\([-rwxlsStT]\\)..") ; u -
- 	 '(1 diredp-no-priv keep))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].....\\([-rwxlsStT]\\)...") ; o -
- 	 '(1 diredp-no-priv keep))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]....\\([-rwxlsStT]\\)....") ; g -
- 	 '(1 diredp-no-priv keep))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]...\\([-rwxlsStT]\\).....") ; u -
- 	 '(1 diredp-no-priv keep))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]..\\([-rwxlsStT]\\)......") ; o -
- 	 '(1 diredp-no-priv keep))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].\\([-rwxlsStT]\\).......") ; g -
- 	 '(1 diredp-no-priv keep))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]\\([-rwxlsStT]\\)........") ; u -
- 	 '(1 diredp-no-priv keep))
-
-   (list (concat dired-re-maybe-mark dired-re-inode-size "\\([bcsmpS]\\)") ; (rare)
-         '(1 diredp-rare-priv keep))
-   (list (concat dired-re-maybe-mark dired-re-inode-size "\\(l\\)[-rwxlsStT]") ; l
-         '(1 diredp-rare-priv keep))
-
-   (list (concat "^\\([^\n " (char-to-string dired-del-marker) "].*$\\)")
-         1 diredp-flag-mark-line t)     ; Flag/mark lines
-   (list (concat "^\\([^\n " (char-to-string dired-del-marker) "]\\)") ; Flags, marks (except D)
-         1 diredp-flag-mark t)
-
-   (list (concat "^\\([" (char-to-string dired-del-marker) "].*$\\)") ; Deletion-flagged lines
-         1 diredp-deletion-file-name t)
-   (list (concat "^\\([" (char-to-string dired-del-marker) "]\\)") ; Deletion flags (D)
-         1 diredp-deletion t)
-
-   ) "2nd level of Dired highlighting.  See `font-lock-maximum-decoration'.")
-
-
-(defun diredp--set-up-font-locking ()
-  "Add this to `dired-mode-hook' to provide for second-level fontifying."
-  (set (make-local-variable 'font-lock-defaults)
-       ;; Two levels.  Use 3-element list, since it is standard to have one more
-       ;; than the number of levels.  This is necessary for it to work with
-       ;; `font(-lock)-menus.el'.
-       '((dired-font-lock-keywords
-          dired-font-lock-keywords
-          diredp-font-lock-keywords-1)
-         t nil nil beginning-of-line))
-  ;; Refresh `font-lock-keywords' from `font-lock-defaults'
-  (when (fboundp 'font-lock-refresh-defaults) (font-lock-refresh-defaults)))
-
-;;; Provide for the second level of fontifying.
-(add-hook 'dired-mode-hook 'diredp--set-up-font-locking)
-
-;; Ensure that Dired buffers are refontified when you use `g' or otherwise read in the file list.
-(defun diredp-refontify-buffer ()
-  "Turn `font-lock-mode' off, then on."
-  (setq font-lock-mode  nil)
-  (font-lock-mode))
-(add-hook 'dired-after-readin-hook 'diredp-refontify-buffer)
- 
-;;; Function Definitions
-
-;;; $$$$$$$$
-;;; (defun diredp-dired-files (arg &optional switches) ; Not bound
-;;;   "Like `dired', but non-positive prefix arg prompts for files to list.
-;;; This is like `dired' unless you use a non-positive prefix arg.
-;;; In that case, you are prompted for names of files and directories to
-;;; list, and then you are prompted for the name of the Dired buffer that
-;;; lists them.  Use `C-g' when you are done entering file names to list.
-
-;;; In all cases, when inputting a file or directory name you can use
-;;; shell wildcards.
-
-;;; If you use Icicles, then in Icicle mode the following keys are bound
-;;; in the minibuffer during completion (`*' means the key requires
-;;; library `Bookmark+'):
-
-;;;   M-|       - Open Dired on the file names matching your input
-;;;   C-c +     - Create a new directory
-;;;  *C-x a +   - Add tags to the current-candidate file
-;;;  *C-x a -   - Remove tags from the current-candidate file
-;;;  *C-x m     - Access file bookmarks (not just autofiles)"
-;;;   (interactive (diredp-dired-files-interactive-spec ""))
-;;;   (when (consp arg)
-;;;     (let ((buf  (dired-find-buffer-nocreate (car arg)))) ; Respect file list.
-;;;       (when buf (kill-buffer buf))))
-;;;   (if (fboundp 'pop-to-buffer-same-window)
-;;;       (pop-to-buffer-same-window (dired-noselect arg switches))
-;;;     (switch-to-buffer (dired-noselect arg switches))))
-
-;;; (defun diredp-dired-files-other-window (arg &optional switches) ; Not bound
-;;;   "Same as `diredp-dired-files' except uses another window."
-;;;   (interactive (diredp-dired-files-interactive-spec "in other window "))
-;;;   (when (consp arg)
-;;;     (let ((buf  (dired-find-buffer-nocreate (car arg)))) ; Respect file list.
-;;;       (when buf (kill-buffer buf))))
-;;;   (dired-other-window arg switches))
-
-;;;###autoload
-(defun diredp-dired-for-files (arg &optional switches) ; Bound to `C-x D F'
-  "Dired file names that you enter, in a Dired buffer that you name.
-You are prompted for the name of the Dired buffer to use.
-You are then prompted for names of files and directories to list,
- which can be located anywhere.
-Use `C-g' when you are done.
-
-With a prefix arg you are first prompted for the `ls' switches to use.
-
-See also `dired' (including the advice)."
-  (interactive (let ((current-prefix-arg  (if current-prefix-arg 0 -1)))
-                 (dired-read-dir-and-switches "" 'READ-EXTRA-FILES-P)))
-  (dired arg switches))
-
-;;;###autoload
-(defun diredp-dired-for-files-other-window (arg &optional switches) ; Bound to `C-x 4 D F'
-  "Same as `diredp-dired-for-files', except uses another window."
-  (interactive (let ((current-prefix-arg  (if current-prefix-arg 0 -1)))
-                 (dired-read-dir-and-switches "in other window " 'READ-EXTRA-FILES-P)))
-  (dired-other-window arg switches))
-
-;;;###autoload
-(defun diredp-dired-recent-dirs (buffer &optional arg) ; Bound to `C-x D R'
-  "Open Dired in BUFFER, showing recently used directories.
-You are prompted for BUFFER.
-
-No prefix arg or a plain prefix arg (`C-u', `C-u C-u', etc.) means
-list all of the recently used directories.
-
-With a prefix arg:
-* If 0, `-', or plain (`C-u') then you are prompted for the `ls'
-  switches to use.
-* If not plain (`C-u') then:
-  * If >= 0 then the directories to include are read, one by one.
-  * If  < 0 then the directories to exclude are read, one by one.
-
-When entering directories to include or exclude, use `C-g' to end."
-  (interactive (list (completing-read "Dired buffer name: " dired-buffers) current-prefix-arg))
-  (unless (require 'recentf nil t) (error "This command requires library `recentf.el'"))
-  (let ((switches  (and (or (zerop (prefix-numeric-value arg))  (consp arg))
-                        (read-string "Dired listing switches: " dired-listing-switches))))
-    (dired (cons (generate-new-buffer-name buffer) (diredp-recent-dirs arg)) switches)))
-
-;;;###autoload
-(defun diredp-dired-recent-dirs-other-window (buffer &optional arg) ; Bound to `C-x 4 D R'
-  "Same as `diredp-dired-recent-dirs', but use other window."
-  (interactive (list (completing-read "Dired buffer name: " dired-buffers) current-prefix-arg))
-  (unless (require 'recentf nil t) (error "This command requires library `recentf.el'"))
-  (let ((switches  (and (or (zerop (prefix-numeric-value arg))  (consp arg)  (eq '- arg))
-                        (read-string "Dired listing switches: " dired-listing-switches))))
-    (dired-other-window (cons (generate-new-buffer-name buffer) (diredp-recent-dirs arg)) switches)))
-
-(defun diredp-recent-dirs (arg)
-  "Return a list of recently used directories.
-ARG is as for `diredp-dired-recent-dirs'."
-  (let ((recent-dirs  (diredp-remove-if #'diredp-root-directory-p
-                                        (diredp-delete-dups
-                                         (mapcar (lambda (f/d)
-                                                   (if (file-directory-p f/d) f/d (file-name-directory f/d)))
-                                                 recentf-list)))))
-    (if (and arg  (atom arg))
-        (diredp-read-include/exclude 'Dir recent-dirs (not (natnump (prefix-numeric-value arg))))
-      recent-dirs)))
-         
-(defun diredp-read-include/exclude (thing things &optional exclude)
-  "Read which THINGs to include (or to EXCLUDE, if non-nil) from list THINGS.
-The things are read one by one.  `C-g' stops reading.
-
-THING is a string or symbol naming the type of thing to read, e.g.,
-`File' or `Directory'.  It is used only in the prompt, which is THING
-followed by \" to exclude\" or \" to include\" and a reminder about `C-g'.
-
-A new list is returned - list THINGS is not modified."
-  (let* ((thgs                    (if exclude (copy-sequence things) ()))
-         (prompt                  (format "%s to %s (C-g when done): " thing (if exclude 'EXCLUDE 'INCLUDE)))
-         (completion-ignore-case  (or (and (boundp 'read-file-name-completion-ignore-case)
-                                           (memq thing '(Dir Directory File "Dir" "Directory" "File")) ; Hack
-                                           read-file-name-completion-ignore-case)
-                                      completion-ignore-case))
-         thing)
-    (while (condition-case nil
-               (setq thing  (completing-read prompt (mapcar #'list things) nil t))
-             (quit nil))
-      (if exclude (delete thing thgs)
-        (push thing thgs)))
-    thgs))
-
-;;; $$$$$$$$
-;;; (defun diredp-dired-files-interactive-spec (str)
-;;;   "`interactive' spec for `diredp-dired-files' commands.
-;;; STR is a string appended to the prompt.
-;;; With non-negative prefix arg, read switches.
-;;; With non-positive prefix arg, read files and dirs to list and then the
-;;;  Dired buffer name.  User uses `C-g' when done reading files and dirs.
-
-;;; If you use Icicles, then in Icicle mode the following keys are bound
-;;; in the minibuffer during completion (`*' means the key requires
-;;; library `Bookmark+'):
-
-;;;   M-|       - Open Dired on the file names matching your input
-;;;   C-c +     - Create a new directory
-;;;  *C-x a +   - Add tags to the current-candidate file
-;;;  *C-x a -   - Remove tags from the current-candidate file
-;;;  *C-x m     - Access file bookmarks (not just autofiles)"
-;;;   (list
-;;;    (unwind-protect
-;;;         (let ((icicle-sort-comparer  (or (and (boundp 'icicle-file-sort) ;; If not reading files
-;;;                                               icicle-file-sort)          ;; then dirs first.
-;;;                                          (and (> (prefix-numeric-value current-prefix-arg) 0)
-;;;                                               'icicle-dirs-first-p)
-;;;                                          icicle-sort-comparer))
-;;;               (icicle-all-candidates-list-alt-action-fn ; M-|'
-;;;                (lambda (files)
-;;;                  (let ((enable-recursive-minibuffers  t))
-;;;                    (dired-other-window (cons (read-string "Dired buffer name: ") files))))))
-;;;           (when (fboundp 'icicle-bind-file-candidate-keys) (icicle-bind-file-candidate-keys))
-;;;           (if (> (prefix-numeric-value current-prefix-arg) 0)
-;;;               ;; If a dialog box is about to be used, call `read-directory-name' so the dialog
-;;;               ;; code knows we want directories.  Some dialog boxes can only select directories
-;;;               ;; or files when popped up, not both.
-;;;               (if (and (fboundp 'read-directory-name)  (next-read-file-uses-dialog-p))
-;;;                   (read-directory-name (format "Dired %s(directory): " str) nil
-;;;                                        default-directory nil)
-;;;                 (read-file-name (format "Dired %s(directory): " str) nil default-directory nil))
-;;;             (let ((insert-default-directory  nil)
-;;;                   (files                     ())
-;;;                   file)
-;;;               (while (condition-case nil ; Use lax completion, to allow wildcards.
-;;;                          (setq file  (read-file-name "File or dir (C-g when done): "))
-;;;                        (quit nil))
-;;;                 (push file files))
-;;;               (cons (read-string "Dired buffer name: " nil nil default-directory) files))))
-;;;      (when (fboundp 'icicle-unbind-file-candidate-keys)
-;;;        (icicle-unbind-file-candidate-keys)))
-;;;    (and current-prefix-arg  (natnump (prefix-numeric-value current-prefix-arg))
-;;;         (read-string "Dired listing switches: " dired-listing-switches))))
-
-;;;###autoload
-(defun diredp-dired-union (dired-name dirbufs &optional switches extra) ; Bound to `C-x D U'
-  "Create a Dired buffer that is the union of some existing Dired buffers.
-With a non-negative prefix arg, you are prompted for `ls' switches.
-With a non-positive prefix arg, you are prompted for file and dir
-names to add to the listing - see below.
-
-You are prompted for the name of the Dired union buffer.  Completion
-against names of existing Dired buffers is available, but you can
-enter any other name to create a new Dired buffer of that name.
-
-If the union buffer name you choose names an existing Dired buffer,
-then what happens depends on whether that buffer is an ordinary Dired
-directory listing or a list of arbitrary file names.  That is, it
-depends on whether `dired-directory' is a directory name or a cons of
-a Dired buffer name plus file names.
-
-* If the buffer is an ordinary Dired listing, then it is converted to
-  an explicit list of absolute file names, just as if these had been
-  chosen individually.  The existing buffer and window are replaced by
-  new ones that show the explicit listing.  (This replacement is
-  necessary because the list of files contained in an ordinary Dired
-  listing cannot be modified.)
-
-* If the buffer lists arbitrary file names explicitly, then it is
-  updated to include also the files from any Dired buffers and any
-  additional files that you specify.
-
-If the union buffer name you choose does not name an existing Dired
-buffer, then its `default-directory' is the same as the
-`default-directory' before invoking the command.
-
-If you use a non-positive prefix arg, then you can next choose
-additional file and directory names to add to the listing.  Use `C-g'
-when done choosing them.
-
-Any directory names you choose this way are included as single entries
-in the listing - the directory contents are not included (these
-directories are not unioned).  To instead include the contents of a
-directory chosen this way, use a glob pattern: `/*' after the
-directory name.
-
-You are then prompted for the Dired buffers to union.  Use `C-g' when
-done choosing them.  These Dired listings to union are included in the
-order that you chose them, and each entry is listed only once in the
-new Dired buffer.
-
-The new Dired listing respects the markings, subdirectory insertions,
-and hidden subdirectories of the selected Dired listings.  However, in
-case of conflict between marked or unmarked status for the same entry,
-the entry is marked.  Similarly, in case of conflict over an included
-subdirectory between it being hidden or shown, it is hidden, but its
-contained files are also listed.
-
-See also command `diredp-add-to-dired-buffer'.
-
-From Lisp:
- DIRED-NAME is the name of the resulting Dired union buffer.
- DIRBUFS is a list of the names of Dired buffers to union.
- SWITCHES is a string of `ls' switches.
- EXTRA is a list of files & directories to be included in the listing."
-  (interactive (diredp-dired-union-interactive-spec "UNION "
-                                                    nil
-                                                    (and current-prefix-arg
-                                                         (<= (prefix-numeric-value current-prefix-arg) 0))))
-  (diredp-dired-union-1 dired-name dirbufs switches extra))
-
-;;;###autoload
-(defun diredp-dired-union-other-window (dired-name dirbufs &optional switches extra) ; Bound to `C-x 4 D U'
-  "Same as `diredp-dired-union', except use other window."
-  (interactive (diredp-dired-union-interactive-spec "UNION "
-                                                    nil
-                                                    (and current-prefix-arg
-                                                         (<= (prefix-numeric-value current-prefix-arg) 0))))
-  (diredp-dired-union-1 dired-name dirbufs switches extra 'OTHERWIN))
-
-;;;###autoload
-(defun diredp-add-to-dired-buffer (dired-name to-add &optional switches) ; Bound to `C-x D A'
-  "Add individual file and directory names to a Dired buffer.
-You are prompted for the buffer name.
-With a prefix arg, you are also prompted for the `ls' switches.
-
-The buffer must either not exist yet or must list arbitrary file and
-directory names.  That is, it cannot be an ordinary Dired directory
-listing - those cannot be modified.
-
-Any directory names you choose this way are included as single entries
-in the listing - the directory contents are not included (these
-directories are not unioned).  To instead include the contents of a
-directory chosen this way, use a glob pattern: `/*' after the
-directory name.
-
-See also command `diredp-dired-union'.
-
-From Lisp:
- DIRED-NAME is the name of the Dired buffer to modify.
- TO-ADD is the list of files and dirs to add to it.
- SWITCHES is the string of `ls' switches."
-  ;; Bind `current-prefix-arg' to force reading file/dir names.
-  ;; Read `ls' switches too, if user used prefix arg.
-  (interactive
-   (let* ((current-prefix-arg  (if current-prefix-arg 0 -1))
-          (all                 (diredp-dired-union-interactive-spec "add files/dirs "
-                                                                    'NO-DIRED-BUFS
-                                                                    'READ-EXTRA-FILES-P)))
-     (list (nth 0 all) (nth 3 all) (nth 2 all))))
-  (diredp-dired-union-1 dired-name () switches to-add))
-
-;;;###autoload
-(defun diredp-add-to-dired-buffer-other-window (dired-name to-add &optional switches) ; Bound to `C-x 4 D A'
-  "Same as `diredp-add-to-dired-buffer', except use other window."
-  ;; Bind `current-prefix-arg' to force reading file/dir names.
-  ;; Read `ls' switches too, if user used prefix arg.
-  (interactive
-   (let* ((current-prefix-arg  (if current-prefix-arg 0 -1))
-          (all                 (diredp-dired-union-interactive-spec "add files/dirs "
-                                                                    'NO-DIRED-BUFS
-                                                                    'READ-EXTRA-FILES-P)))
-     (list (nth 0 all) (nth 3 all) (nth 2 all))))
-  (diredp-dired-union-1 dired-name () switches to-add 'OTHERWIN))
-
-;;;###autoload
-(defun diredp-add-to-this-dired-buffer (dired-name to-add &optional switches) ; Not bound by default
-  "Same as `diredp-add-to-dired-buffer' for this Dired buffer."
-  ;; Bind `current-prefix-arg' to force reading file/dir names.
-  ;; Read `ls' switches too, if user used prefix arg.
-  (interactive
-   (progn (unless (derived-mode-p 'dired-mode) (error "Not in a Dired buffer"))
-          (let* ((current-prefix-arg  (if current-prefix-arg 0 -1))
-                 (all                 (diredp-dired-union-interactive-spec "add files/dirs here "
-                                                                           'NO-DIRED-BUFS
-                                                                           'READ-EXTRA-FILES-P
-                                                                           (buffer-name))))
-            (list (nth 0 all) (nth 3 all) (nth 2 all)))))
-  (diredp-dired-union-1 dired-name () switches to-add))
-
-;; $$$$$ Maybe I should set `dired-sort-inhibit' to t for now (?),
-;; since there is an Emacs bug (at least on Windows) that prevents
-;; sorting from working for a Dired buffer with an explicit file list.
-(defun diredp-dired-union-1 (dired-name dirbufs switches extra &optional otherwin)
-  "Helper for `diredp-dired-union' and `diredp-add-to-dired-buffer'.
-Non-nil optional OTHERWIN means use other window for the Dired buffer.
-See `diredp-dired-union' for the other argument descriptions."
-  (let ((dbuf         (get-buffer dired-name))
-        (files        extra)
-        (marked       ())
-        (subdirs      ())
-        (hidden-dirs  ())
-        hid-here files-here)
-    (dolist (buf  (reverse dirbufs))
-      (with-current-buffer buf
-        (unwind-protect
-             (progn (setq hid-here    (save-excursion (dired-remember-hidden))
-                          files-here  (if (consp dired-directory)
-                                          (reverse (cdr dired-directory)) ; Reverse bc will push.
-                                        ()))
-                    (unless files-here
-                      (save-excursion   ; This bit is more or less from `dired-toggle-marks'.
-                        (goto-char (point-min))
-                        (while (not (eobp))
-                          (or (diredp-looking-at-p dired-re-dot)
-                              (push (dired-get-filename nil 'NO-ERROR-P) files-here))
-                          (forward-line 1)))
-                      (setq files-here  (delq nil files-here)))
-                    (dolist (hid-here  hid-here) (push hid-here hidden-dirs))
-                    (dolist (sub  (cdr (reverse dired-subdir-alist)))
-                      (push (list (car sub)) subdirs))
-                    (dolist (mkd  (dired-remember-marks (point-min) (point-max))) ; This unhides.
-                      (push (car mkd) marked))
-                    (dolist (file  files-here)
-                      (when (or (not (file-name-absolute-p file))  (not (member file files)))
-                        (push file files))))
-          (save-excursion               ; Hide subdirs that were hidden.
-            (dolist (dir  hid-here)  (when (dired-goto-subdir dir) (dired-hide-subdir 1)))))))
-    ;; For an existing Dired buffer having this name whose `dired-directory' is a cons:
-    ;; 1. Include the files and dirs already listed there.
-    ;; 2. Kill the current buffer and delete its window.  A new buffer of the same name is created and shown.
-    (when dbuf
-      (with-current-buffer dbuf
-        (when (consp dired-directory) (setq files  (diredp-set-union (cdr dired-directory) files)))
-        (let ((win  (get-buffer-window dbuf 0))) (when win (delete-window win)))
-        (kill-buffer dbuf)))
-    (setq dbuf  (dired-other-window (cons dired-name files) switches))
-    (with-current-buffer dbuf
-      (let ((inhibit-read-only  t))
-        (dired-insert-old-subdirs subdirs)
-        (dired-mark-remembered          ; Don't really need `expand-file-name' - already abs.
-         (mapcar (lambda (mf) (cons (expand-file-name mf dired-directory) 42)) marked))
-        (save-excursion
-          (dolist (hdir  hidden-dirs)  (when (dired-goto-subdir hdir) (dired-hide-subdir 1))))))))
-
-(defun diredp-dired-union-interactive-spec (string &optional no-dired-bufs read-extra-files-p dired-buffer)
-  "Read arguments for `diredp-dired-union' and `diredp-add-to-dired-buffer'.
-STRING is appended to the prompt for the listing buffer name.
-Non-nil NO-DIRED-BUFS means do not read Dired buffers to union.
-Non-nil READ-EXTRA-FILES-P is passed to `dired-read-dir-and-switches',
- and means read extra files to add to the listing.
-Non-nil DIRED-BUFFER is passed to `dired-read-dir-and-switches'.
-  It is the name of the Dired union buffer."
-  (let* ((current-prefix-arg  -1)
-         (dir+switches        (dired-read-dir-and-switches string read-extra-files-p dired-buffer))
-         (dirname             (car  dir+switches))
-         (switches            (cadr dir+switches))
-         (dirbufs             ())
-         (bufs                ())
-         (extra-files         ())
-         buf)
-    (when (consp dirname) (setq extra-files  (cdr dirname)
-                                dirname      (car dirname)))
-    (unless no-dired-bufs
-      ;; Remove any killed buffers from `dired-buffers'.  Then use all but the target buffer as candidates.
-      (dolist (db  dired-buffers)
-        (if (buffer-live-p (cdr db))
-            (unless (equal dirname (buffer-name (cdr db)))
-              (push (cons (buffer-name (cdr db)) (car db)) dirbufs))
-          (setq dired-buffers  (delq db dired-buffers))))
-      (while (and dirbufs  (condition-case nil
-                               (setq buf  (completing-read "Existing Dired buffer to include (C-g when done): "
-                                                           dirbufs nil t nil 'buffer-name-history
-                                                           (and dirbufs  (car (assoc (buffer-name) dirbufs)))))
-                             (quit nil)))
-        (push buf bufs)
-        (setq dirbufs  (delete (cons buf (with-current-buffer buf (expand-file-name default-directory)))
-                               dirbufs)))
-      (setq bufs  (nreverse bufs)))
-    (list dirname bufs switches extra-files)))
-
-(when (> emacs-major-version 23)        ; `compilation--loc->file-struct'
-
-  (defalias 'diredp-grepped-files-other-window 'diredp-compilation-files-other-window)
-  (defun diredp-compilation-files-other-window (&optional switches)
-    "Open Dired on the files indicated by compilation (e.g., `grep') hits.
-Applies to any `compilation-mode'-derived buffer, such as `*grep*'.
-You are prompted for the name of the new Dired buffer.
-With a prefix arg you are first prompted for the `ls' switches.
-
-\(However, Emacs bug #20739 means that the switches are ignored.)"
-    (interactive (list (and current-prefix-arg  (read-string "Dired listing switches: " dired-listing-switches))))
-    (unless (compilation-buffer-p (current-buffer)) (error "Not in a buffer derived from `compilation-mode'"))
-    (let ((files  ()))
-      (save-excursion (goto-char (point-min))
-                      (while (condition-case nil (compilation-next-file 1) (error nil))
-                        (setq compilation-current-error  (point))
-                        (push (diredp-file-for-compilation-hit-at-point) files)))
-      (setq files  (nreverse files))
-      (dired-other-window
-       (cons (read-string "Dired buffer name: " nil nil (generate-new-buffer-name default-directory)) files)
-       switches)))
-
-  (defun diredp-file-for-compilation-hit-at-point ()
-    "Return the name of the file for the compilation hit at point.
-The name is expanded in the directory for the last directory change."
-    (let* ((msg         (compilation-next-error 0))
-           (loc         (compilation--message->loc msg))
-           (filestruct  (compilation--loc->file-struct loc))
-           (file        (caar filestruct))
-           (dir         (cadr (car filestruct))))
-      (when dir (setq file  (expand-file-name file dir)))
-      file))
-  )
-
-;;;###autoload
-(defun diredp-fileset (flset-name)      ; Bound to `C-x D S'
-  "Open Dired on the files in fileset FLSET-NAME."
-  (interactive
-   (progn (unless (require 'filesets nil t) (error "Feature `filesets' not provided"))
-          (unless filesets-data (error "`filesets-data' is empty"))
-          (list (completing-read "Open Dired on fileset: " filesets-data))))
-  (diredp-fileset-1 flset-name))
-
-;;;###autoload
-(defun diredp-fileset-other-window (flset-name) ; Bound to `C-x 4 D S'
-  "Open Dired in another window on the files in fileset FLSET-NAME."
-  (interactive
-   (progn (unless (require 'filesets nil t) (error "Feature `filesets' not provided"))
-          (unless filesets-data (error "`filesets-data' is empty"))
-          (list (completing-read "Open Dired on fileset, in other window: " filesets-data))))
-  (diredp-fileset-1 flset-name 'OTHER-WINDOW))
-
-(defun diredp-fileset-1 (flset-name &optional other-window-p)
-  "Helper for `diredp-fileset(-other-window)'."
-  (let ((flset   (filesets-get-fileset-from-name flset-name))
-        (files   ())
-        (mode    nil)
-        (dirfun  (if other-window-p #'dired-other-window #'dired)))
-    (unless (or (setq mode  (filesets-entry-mode flset)) ; ("my-fs" (:files "a" "b"))
-                (setq flset  (cons "dummy" flset) ; (:files "a" "b")
-                      mode   (filesets-entry-mode flset)))
-      (error "Bad fileset: %S" flset-name))
-    (message "Gathering file names...")
-    (dolist (file  (filesets-get-filelist flset mode)) (push file files))
-    (funcall dirfun (cons (generate-new-buffer-name flset-name)
-                          (nreverse (mapcar (lambda (file)
-                                              (if (file-name-absolute-p file)
-                                                  (expand-file-name file)
-                                                file))
-                                            files))))))
-
-;;;###autoload
-(defun diredp-dired-this-subdir (&optional tear-off-p msgp)
-  "Open Dired for the subdir at or above point.
-If point is not on a subdir line, but is in an inserted subdir
-listing, then use that subdir.
-
-With a prefix arg:
- If the subdir is inserted and point is in the inserted listing then
- remove that listing and move to the ordinary subdir line.  In other
- words, when in an inserted listing, a prefix arg tears off the
- inserted subdir to its own Dired buffer."
-  (interactive "P\np")
-  (diredp-ensure-mode)
-  (let* ((this-dir       default-directory)
-         (this-subdir    (diredp-this-subdir))
-         (on-dir-line-p  (atom this-subdir)))
-    (unless on-dir-line-p               ; Subdir header line or non-directory file.
-      (setq this-subdir  (car this-subdir)))
-    (unless (string= this-subdir this-dir)
-      (when tear-off-p
-        (unless on-dir-line-p
-          (dired-kill-subdir)           ; Tear it off.
-          (dired-goto-file this-subdir))) ; Move to normal subdir line.
-      (dired-other-window this-subdir))))
-
-;;;###autoload
-(defun diredp-dired-inserted-subdirs (&optional no-show-p msgp) ; Bound to `C-M-i'
-  "Open Dired for each of the subdirs inserted in this Dired buffer.
-A separate Dired buffer is used for each of them.
-With a prefix arg, create the Dired buffers but do not display them.
-Markings and current Dired switches are preserved."
-  (interactive "P\np")
-  (diredp-ensure-mode)
-  (let ((this-dir    default-directory)
-        (this-buff   (current-buffer))
-        (this-frame  (selected-frame))
-        marked)
-    (unwind-protect
-         (save-selected-window
-           (dolist (entry  dired-subdir-alist)
-             (unless (string= (car entry) this-dir)
-               (setq marked  (with-current-buffer this-buff
-                               (dired-remember-marks (dired-get-subdir-min entry) (dired-get-subdir-max entry))))
-               (if (not no-show-p)
-                   (dired-other-window (car entry) dired-actual-switches)
-                 (dired-noselect (car entry) dired-actual-switches)
-                 (when msgp (message "Dired buffers created but not shown")))
-               (set-buffer this-buff)
-               (let ((inhibit-read-only  t))
-                 (dired-mark-remembered marked))
-               (set-buffer-modified-p nil))))
-      (select-frame-set-input-focus this-frame))))
-
-
-;;; Actions on marked files and subdirs, recursively.
-
-(defun diredp-get-subdirs (&optional ignore-marks-p predicate details)
-  "Return subdirs from this Dired buffer and from marked subdirs, recursively.
-If optional arg IGNORE-MARKS-P is non-nil then include all
-subdirectories.  Otherwise, include only those that are marked.
-
-Non-nil optional arg PREDICATE means include only subdirectory names
-for which the PREDICATE returns non-nil.  PREDICATE must accept a file
-name as its only required argument.
-
-Optional arg DETAILS is passed to `diredp-get-files'."
-  (diredp-get-files ignore-marks-p (if predicate
-                                       `(lambda (name) (and (file-directory-p name)  (funcall ,predicate name)))
-                                     #'file-directory-p)
-                    'INCLUDE-DIRS-P 'DONT-ASKP 'ONLY-MARKED-P details))
-
-(defun diredp-get-files (&optional ignore-marks-p predicate include-dirs-p dont-askp only-marked-p details)
-  "Return file names from this Dired buffer and subdirectories, recursively.
-The names are those that are marked in the current Dired buffer, or
-all files in the directory if none are marked.  Marked subdirectories
-are handled recursively in the same way.
-
-If there is some included subdirectory that has a Dired buffer with
-marked files, then (unless DONT-ASKP is non-nil) this asks you whether
-to use the marked files in Dired buffers, as opposed to using all of
-the files in included directories.  To this y-or-n question you can
-hit `l' to see the list of files that will be included (using
-`diredp-list-files').  In that `l' listing you can mouseover to see
-image-file previews or use `RET' or `mouse-2' to visit files.
-
-\(Directories in `icicle-ignored-directories' are skipped, if you use
-Icicles.  Otherwise, directories in `vc-directory-exclusion-list' are
-skipped.)
-
-Non-nil IGNORE-MARKS-P means ignore all Dired markings: just get all
-of the files in the current directory (and all of the subdirectories,
-if INCLUDE-DIRS-P is non-nil).
-
-Non-nil PREDICATE means include only file names for which the
-PREDICATE returns non-nil.  PREDICATE must accept a file name as its
-only required argument.
-
-Non-nil INCLUDE-DIRS-P means include marked subdirectory names (but
-also handle those subdirs recursively, picking up their marked files
-and subdirs).
-
-Non-nil DONT-ASKP means do not ask the user whether to use marked
-instead of all.  Act as if the user was asked and replied `y'.
-
-Non-nil optional arg ONLY-MARKED-P means collect only marked files,
-instead of collecting all files if none are marked.  This argument is
-ignored if IGNORE-MARKS-P is non-nil.
-
-Optional arg DETAILS is passed to `diredp-y-or-n-files-p'."
-  (let ((askp  (list nil)))             ; The cons's car will be set to `t' if need to ask user.
-    (if ignore-marks-p
-        (diredp-files-within (directory-files default-directory 'FULL diredp-re-no-dot)
-                             () nil include-dirs-p predicate)
-      ;; Pass FILES and ASKP to `diredp-get-files-for-dir', so we don't have to use them as
-      ;; free vars there.  But that means that they each need to be a cons cell that we can
-      ;; modify, so we can get back the updated info.
-      (let ((files  (list 'DUMMY)))     ; The files picked up will be added to this list.
-        (diredp-get-files-for-dir default-directory files askp include-dirs-p only-marked-p)
-        (setq files  (cdr files))       ; Remove `DUMMY' from the modifed list.
-        (if (or dont-askp
-                (not (car askp))
-                (diredp-y-or-n-files-p "Use marked (instead of all) in subdir Dired buffers? "
-                                       files predicate details))
-            (if predicate (diredp-remove-if-not predicate files) files)
-          (setq files  ())
-          (dolist (file  (diredp-marked-here))
-            (if (not (file-directory-p file))
-                (when (or (not predicate)  (funcall predicate file))
-                  (add-to-list 'files file))
-              (when include-dirs-p (setq files  (nconc files (list file))))
-              (setq files  (nconc files (diredp-files-within (directory-files file 'FULL diredp-re-no-dot)
-                                                             () nil include-dirs-p predicate)))))
-          (nreverse files))))))
-
-(defun diredp-get-files-for-dir (directory accum askp &optional include-dirs-p only-marked-p)
-  "Return marked file names for DIRECTORY and subdirectories, recursively.
-Pick up names of all marked files in DIRECTORY if it has a Dired
-buffer, or all files in DIRECTORY if not.  Handle subdirs recursively
-\(only marked subdirs, if Dired).
-
-ACCUM is an accumulator list: the files picked up in this call are
-nconc'd to it.
-
-ASKP is a one-element list, the element indicating whether to ask the
-user about respecting Dired markings.  It is set here to `t' if there
-is a Dired buffer for DIRECTORY.
-
-Non-nil optional arg INCLUDE-DIRS-P means include marked subdirectory
-names (but also handle those subdirs recursively).
-
-Non-nil optional arg ONLY-MARKED-P means collect only marked files,
-instead of collecting all files if none are marked.
-
-If there is more than one Dired buffer for DIRECTORY then raise an
-error."
-  (let ((dbufs  (dired-buffers-for-dir (expand-file-name directory))))
-    (dolist (file  (if (not dbufs)
-                       (and (not only-marked-p)  (directory-files directory 'FULL diredp-re-no-dot))
-                     (when (cadr dbufs) (error "More than one Dired buffer for `%s'" directory))
-                     (unless (equal directory default-directory) (setcar askp  t))
-                     (with-current-buffer (car dbufs) (diredp-marked-here only-marked-p 'NO-DOT-DOT))))
-      (if (not (file-directory-p file))
-          (setcdr (last accum) (list file))
-        (when include-dirs-p (setcdr (last accum) (list file)))
-        (diredp-get-files-for-dir file accum askp include-dirs-p only-marked-p)))))
-
-(defun diredp-marked-here (&optional only-marked-p no-dot-dot-p)
-  "Marked files and subdirs in this Dired buffer, or all if none are marked.
-Non-nil optional arg ONLY-MARKED-P means return nil if none are
-marked.
-Non-nil optional arg NO-DOT-DOT-P means do not include marked `..'."
-  ;; If no file is marked, exclude `(FILENAME)': the unmarked file at cursor.
-  ;; If there are no marked files as a result, return all files and subdirs in the dir.
-  (let* ((dired-marker-char  ?*)
-         (ff                 (condition-case nil ; Ignore error if on `.' or `..' and no file is marked.
-                                 (dired-get-marked-files
-                                  nil nil (and no-dot-dot-p
-                                               (lambda (mf) (not (diredp-string-match-p "/\\.\\.$" mf))))
-                                  'DISTINGUISH-ONE-MARKED)
-                               (error nil))))
-    (cond ((eq t (car ff))  (cdr ff))   ; Single marked
-          ((cadr ff)        ff)         ; Multiple marked
-          (t                (and (not only-marked-p) ; None marked
-                                 (directory-files default-directory 'FULL diredp-re-no-dot 'NOSORT))))))
-
-(defun diredp-y-or-n-files-p (prompt files &optional predicate details)
-  "PROMPT user with a \"y or n\" question about a list of FILES.
-Return t if answer is \"y\".  Otherwise, return nil.
-
-Like `y-or-n-p', but you can also hit `l' to display the list of files
-that the confirmation is for, in buffer `*Files'.  In that `'l'
-listing you can mouseover to see image-file previews or use `RET' or
-`mouse-2' to visit files.
-
-When finished, buffer `*Files*' is killed if it was never shown, or is
-hidden and buried otherwise.  Thus, if it was shown then it is still
-available to revisit afterward (even if you quit using `C-g').
-
-PREDICATE is passed to `diredp-list-files', to list only file names
-for which it returns non-nil.
-
-DETAILS is passed to `diredp-list-files', to show details about FILES."
-  (let ((answer  'recenter))
-    (cond (noninteractive
-           (setq prompt  (concat prompt
-                                 (and (not (eq ?\   (aref prompt (1- (length prompt)))))  " ")
-                                 "(y or n; l to show file list) "))
-           (let ((temp-prompt  prompt))
-             (while (not (memq answer '(act skip)))
-               (let ((str  (read-string temp-prompt)))
-                 (cond ((member str '("y" "Y")) (setq answer  'act))
-                       ((member str '("n" "N")) (setq answer  'skip))
-                       (t (setq temp-prompt  (concat "Please answer y or n.  " prompt))))))))
-          ((if (not (fboundp 'display-popup-menus-p))
-               (and window-system  (listp last-nonmenu-event)  use-dialog-box)
-             (and (display-popup-menus-p)  (listp last-nonmenu-event)  use-dialog-box))
-           (setq answer  (x-popup-dialog t `(,prompt ("Yes" . act) ("No" . skip)))))
-          (t
-           (let ((list-buf        (generate-new-buffer-name "*Files*"))
-                 (list-was-shown  nil))
-             (unwind-protect
-                 (progn
-                   (define-key query-replace-map "l" 'show)
-                   (setq prompt  (concat prompt
-                                         (and (eq ?\   (aref prompt (1- (length prompt))))
-                                              "" " ")
-                                         "(y or n; l to show file list) "))
-                   (while (let* ((reprompt-actions  '(recenter scroll-up scroll-down
-                                                               scroll-other-window scroll-other-window-down))
-                                 (key               (let ((cursor-in-echo-area  t))
-                                                      (when minibuffer-auto-raise
-                                                        (raise-frame (window-frame (minibuffer-window))))
-                                                      (if (fboundp 'read-key)
-                                                          (read-key (propertize
-                                                                     (if (memq answer reprompt-actions)
-                                                                         prompt
-                                                                       (concat "Please answer y or n.  " prompt))
-                                                                     'face 'minibuffer-prompt))
-                                                        (read-char-exclusive
-                                                         (if (memq answer reprompt-actions)
-                                                             prompt
-                                                           (concat "Please answer y or n.  " prompt)))))))
-                            (setq answer  (lookup-key query-replace-map (vector key) t))
-                            (case answer
-                              ((skip  act)              nil)
-                              (recenter                 (recenter) t)
-                              (show                     (diredp-list-files files nil list-buf predicate details)
-                                                        (setq list-was-shown  t)) ; Record showing it.
-                              (help                     (message "Use `l' to show file list") (sit-for 1))
-                              (scroll-up                (condition-case nil (scroll-up-command) (error nil)) t)
-                              (scroll-down              (condition-case nil (scroll-down-command) (error nil)) t)
-                              (scroll-other-window      (condition-case nil (scroll-other-window) (error nil)) t)
-                              (scroll-other-window-down (condition-case nil (scroll-other-window-down nil)
-                                                          (error nil)) t)
-                              ((exit-prefix  quit)      (signal 'quit nil) t)
-                              (t (or (not (eq key ?\e))  (progn (signal 'quit nil) t)))))
-                     (ding)
-                     (discard-input)))
-               (when (get-buffer list-buf)
-                 (save-window-excursion (pop-to-buffer list-buf)
-                                        (condition-case nil ; Ignore error if user already deleted.
-                                            (if (one-window-p) (delete-frame) (delete-window))
-                                          (error nil))
-                                        (if list-was-shown (bury-buffer list-buf) (kill-buffer list-buf))))
-               (define-key query-replace-map "l" nil)))))
-    (let ((ret  (eq answer 'act)))
-      (unless noninteractive (message "%s %s" prompt (if ret "y" "n")))
-      ret)))
-
-(defvar diredp-list-files-map
-  (let ((map  (make-sparse-keymap)))
-    (define-key map "q"       'quit-window)
-    (define-key map "\r"      'diredp-find-line-file-other-window)
-    (define-key map [mouse-2] 'diredp-mouse-find-line-file-other-window)
-    map)
-  "Keymap for `diredp-list-files' output.")
-(fset 'diredp-list-files-map diredp-list-files-map)
-
-;;;###autoload
-(defun diredp-find-line-file-other-window ()
-  "Visit file named by current line, in another window.
-The full text of the line is used as the file name."
-  (interactive)
-  (let ((file  (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
-    (when file (find-file-other-window file))))
-
-;;;###autoload
-(defun diredp-mouse-find-line-file-other-window (e)
-  "Visit file named by clicked line, in another window.
-The full text of the line is used as the file name."
-  (interactive "e")
-  (save-excursion (mouse-set-point e) (diredp-find-line-file-other-window)))
-
-;;;###autoload
-(defun diredp-list-marked (&optional arg predicate interactivep details) ; Bound to `C-M-l'
-  "List the marked files in this Dired buffer.
-A prefix arg specifies files to use instead of the marked files:
-
- * Numeric prefix arg N: The next N files (previous -N, if < 0).
- * C-u C-u: All files, but no directories.
- * C-u C-u C-u: All files and directories, except `.' and `..'
- * C-u C-u C-u C-u: All files and directories, including `.' and `..'
- * Any other prefix arg: The current line's file only.
-
-You can use `RET' or `mouse-2' to visit any of the files.
-If `tooltip-mode' is on then moving the mouse over image-file names
-shows image previews.
-
-When called from Lisp:
- Non-nil optional arg PREDICATE is a file-name predicate.  List only
-  the files for which it returns non-nil.
- Non-nil optional arg DETAILS is passed to `diredp-list-files'."
-  (interactive (progn (diredp-ensure-mode) (list current-prefix-arg nil t diredp-list-file-attributes)))
-  (let ((files  (dired-get-marked-files nil arg predicate 'DISTINGUISH-ONE interactivep)))
-    (diredp-list-files files nil nil nil details)))
-
-(defun diredp-list-files (files &optional dir bufname predicate details)
-  "Display FILES, a list of file names.  Wildcard patterns are expanded.
-The files are shown in a new buffer, `*Files*' by default.
-
-Optional arg DIR serves as the default directory for expanding file
- names that are not absolute.  It defaults to `default-directory'.
-
-Optional arg BUFNAME is the name of the buffer for the display.
- It defaults to `*Files*' (or `*Files*<N>' if `*Files*' exists).
-
-Optional arg PREDICATE is a predicate used to filter FILES: only files
- satisfying PREDICATE are listed.
-
-Non-nil arg DETAILS means show details about each file, in addition to
-the file name.  It is passed to `diredp-list-file' (which see).
-
-File names listed are absolute.  Mouseover gives help or an image-file
-preview, and you can use `RET' or `mouse-2' to visit files."
-  (unless bufname (setq bufname  (generate-new-buffer-name "*Files*")))
-  (diredp-with-help-window
-   bufname
-   (princ "Files\n-----\n\n")
-   (let ((all-files-no-wildcards  ())
-         file-alist file-dir)
-     (dolist (file  files)
-       (unless (or (string= file "")    ; Ignore empty file names.
-                   (and predicate  (not (funcall predicate file))))
-         (if (not (diredp-string-match-p "[[?*]" file))
-             (add-to-list 'all-files-no-wildcards (diredp-list-file file details))
-           (setq file-dir    (or (file-name-directory file)  default-directory)
-                 file-alist  (directory-files-and-attributes file-dir 'FULL "[[?*]" 'NOSORT))
-           (dolist (ff  file-alist)
-             (add-to-list 'all-files-no-wildcards (diredp-list-file file details))))))
-     (save-excursion (dolist (fff  (nreverse all-files-no-wildcards))
-                       (princ fff) (terpri)))))
-  (with-current-buffer bufname
-    (let ((buffer-read-only  nil))
-      (save-excursion
-        (goto-char (point-min))
-        (forward-line 3)
-        (while (not (eobp))
-          (add-text-properties (line-beginning-position) (line-end-position)
-                               '(mouse-face highlight help-echo diredp-mouseover-help dired-filename t
-                                            ;; `keymap' does not work for Emacs 20.  Could use `local-map'
-                                            ;; but that still leaves `RET' bound to `help-follow'.
-                                            keymap diredp-list-files-map))
-          (forward-line 1))))
-    (set-buffer-modified-p nil)
-    (setq buffer-read-only  t)
-    (buffer-enable-undo)))
-
-(defun diredp-list-file (file &optional details)
-  "Return FILE name, expanded.
-Non-nil optional arg DETAILS means append details about FILE to the
-returned string.
-
-If DETAILS is a list of file attribute numbers then include only the
-values of those attributes.  Otherwise, include all attribute values."
-  (let ((file-dir  (and details  (or (file-name-directory file)  default-directory)))
-        attrs)
-    (setq file  (expand-file-name file file-dir))
-    (when (and details  (atom details)) (setq details  '(0 1 2 3 4 5 6 7 8 9 10 11)))
-    (concat
-     file
-     (and details
-          (setq attrs  (file-attributes file))
-          (concat
-           "\n"
-           (and (memq 0 details)
-                (format " File Type:                  %s\n"
-                        (cond ((eq t (nth 0 attrs))  "Directory")
-                              ((stringp (nth 0 attrs))  (format "Symbolic link to `%s'" (nth 0 attrs)))
-                              (t  "Normal file"))))
-           (and (memq 8 details)
-                (format " Permissions:                %s\n" (nth 8 attrs)))
-           (and (memq 7 details)  (not (eq t (nth 0 attrs)))
-                (format " Size in bytes:              %g\n" (nth 7 attrs)))
-           (and (memq 4 details)
-                (format-time-string " Time of last access:        %a %b %e %T %Y (%Z)\n" (nth 4 attrs)))
-           (and (memq 5 details)
-                (format-time-string " Time of last modification:  %a %b %e %T %Y (%Z)\n" (nth 5 attrs)))
-           (and (memq 6 details)
-                (format-time-string " Time of last status change: %a %b %e %T %Y (%Z)\n" (nth 6 attrs)))
-           (and (memq 1 details)
-                (format " Number of links:            %d\n" (nth 1 attrs)))
-           (and (memq 2 details)
-                (format " User ID (UID):              %s\n" (nth 2 attrs)))
-           (and (memq 3 details)
-                (format " Group ID (GID):             %s\n" (nth 3 attrs)))
-           (and (memq 10 details)
-                (format " Inode:                      %S\n" (nth 10 attrs)))
-           (and (memq 11 details)
-                (format " Device number:              %s\n" (nth 11 attrs))))))))
-
-(defvar diredp-files-within-dirs-done ()
-  "Directories already processed by `diredp-files-within'.")
-
-
-;; Not used in the `Dired+' code yet.
-(defun diredp-directories-within (&optional directory no-symlinks-p predicate)
-  "List of accessible directories within DIRECTORY.
-Directories in `icicle-ignored-directories' are skipped, if you use
-Icicles.  Otherwise, directories in `vc-directory-exclusion-list' are
-skipped.
-
-Optional arg DIRECTORY defaults to the value of `default-directory'.
-Non-nil optional arg NO-SYMLINKS-P means do not follow symbolic links.
-Non-nil optional arg PREDICATE must be a function that accepts a
- file-name argument.  Only directories that satisfy PREDICATE are
- included in the result."
-  (unless directory (setq directory  default-directory))
-  (let ((dirs  (diredp-files-within (directory-files directory 'FULL diredp-re-no-dot)
-                                    () no-symlinks-p 'INCLUDE-DIRS-P
-                                    #'file-directory-p)))
-    (if predicate (diredp-remove-if-not predicate dirs) dirs)))
-
-;; Args INCLUDE-DIRS-P and PREDICATE are not used in the `Dired+' code yet
-;; (except in `diredp-directories-within', which also is not used yet).
-;;
-(defun diredp-files-within (file-list accum &optional no-symlinks-p include-dirs-p predicate)
-  "List of readable files in FILE-LIST, handling directories recursively.
-FILE-LIST is a list of file names or a function that returns such.
-If a function then invoke it with no args to get the list of files.
-
-Accessible directories in the list of files are processed recursively
-to include their files and the files in their subdirectories.  The
-directories themselves are not included, unless optional arg
-INCLUDE-DIRS-P is non-nil.  (Directories in
-`icicle-ignored-directories' are skipped, if you use Icicles.
-Otherwise, directories in `vc-directory-exclusion-list' are skipped.)
-
-But if there is a Dired buffer for such a directory, and if FILE-LIST
-is a function, then it is invoked in that Dired buffer to return the
-list of files to use.  E.g., if FILE-LIST is `dired-get-marked-files'
-then only the marked files and subdirectories are included.  If you
-have more than one Dired buffer for a directory that is processed
-here, then only the first one in `dired-buffers' is used.
-
-The list of files is accumulated in ACCUM, which is used for recursive
-calls.
-
-Non-nil optional arg NO-SYMLINKS-P means do not follow symbolic links.
-
-Non-nil optional arg INCLUDE-DIRS-P means include directory names
-along with the names of non-directories.
-
-Non-nil optional arg PREDICATE must be a function that accepts a
-file-name argument.  Only files (and possibly directories) that
-satisfy PREDICATE are included in the result."
-  ;; Bind `diredp-files-within-dirs-done' for use as a free var in `diredp-files-within-1'.
-  (let ((diredp-files-within-dirs-done  ()))
-    (nreverse (diredp-files-within-1 file-list accum no-symlinks-p include-dirs-p predicate))))
-
-;; `diredp-files-within-dirs-done' is free here, bound in `diredp-files-within'.
-(defun diredp-files-within-1 (file-list accum no-symlinks-p include-dirs-p predicate)
-  "Helper for `diredp-files-within'."
-  (let ((files  (if (functionp file-list) (funcall file-list) file-list))
-        (res    accum)
-        file)
-    (when (and files  predicate) (setq files  (diredp-remove-if-not predicate files)))
-    (while files
-      (setq file  (car files))
-      (unless (and no-symlinks-p  (file-symlink-p file))
-        (if (file-directory-p file)
-            ;; Skip directory if ignored, already treated, or inaccessible.
-            (when (and (not (member (file-name-nondirectory file)
-                                    (if (boundp 'icicle-ignored-directories)
-                                        icicle-ignored-directories
-                                      (and (boundp 'vc-directory-exclusion-list)
-                                           vc-directory-exclusion-list))))
-                       (not (member (file-truename file) diredp-files-within-dirs-done))
-                       (file-accessible-directory-p file))
-              (setq res  (diredp-files-within-1 (or (and (functionp file-list)
-                                                         (dired-buffers-for-dir
-                                                          (expand-file-name file)) ; Removes killed buffers.
-                                                         (with-current-buffer
-                                                             (cdr (assoc (file-name-as-directory file)
-                                                                         dired-buffers))
-                                                           (funcall file-list)))
-                                                    (directory-files file 'FULL diredp-re-no-dot))
-                                                res no-symlinks-p include-dirs-p predicate))
-              (when include-dirs-p (push file res))
-              (push (file-truename file) diredp-files-within-dirs-done))
-          (when (file-readable-p file) (push file res))))
-      (pop files))
-    res))
-
-(defun diredp-remove-if (pred xs)
-  "A copy of list XS with no elements that satisfy predicate PRED."
-  (let ((result  ()))
-    (dolist (x xs) (unless (funcall pred x) (push x result)))
-    (nreverse result)))
-
-(defun diredp-remove-if-not (pred xs)
-  "A copy of list XS with only elements that satisfy predicate PRED."
-  (let ((result  ()))
-    (dolist (x xs) (when (funcall pred x) (push x result)))
-    (nreverse result)))
-
-(when (> emacs-major-version 21)        ; Emacs 20 has no PREDICATE arg to `read-file-name'.
-  (defun diredp-insert-as-subdir (child ancestor &optional in-dired-now-p)
-    "Insert the current Dired dir into a Dired listing of an ancestor dir.
-Ancestor means parent, grandparent, etc. at any level.
-You are prompted for the ancestor directory.
-The ancestor Dired buffer is selected.
-
-Markings and switches in the current Dired buffer are preserved for
-the subdir listing in the ancestor Dired buffer.
-
-Note: If you use Icicles, then you can use
-`icicle-dired-insert-as-subdir' instead: it is a multi-command.  It
-does the same thing, but it lets you insert any number of descendant
-directories into a given ancestor-directory Dired buffer.
-
-Non-interactively:
- Insert CHILD dir into Dired listing for ANCESTOR dir.
-
- Non-nil optional arg IN-DIRED-NOW-P means to use the current buffer
- as the Dired buffer from which to pick up markings and switches.
- Otherwise, pick them up from a Dired buffer for CHILD, if there is
- exactly one such buffer."
-    (interactive (progn (diredp-ensure-mode)
-                        (list default-directory
-                              (completing-read
-                               "Insert this dir into ancestor dir: "
-                               (mapcar #'list (diredp-ancestor-dirs default-directory)))
-                              t)))
-    (let ((child-dired-buf  (if in-dired-now-p
-                                (current-buffer)
-                              (dired-buffers-for-dir (expand-file-name child))))
-          (switches         ())
-          (marked           ()))
-      (when (consp child-dired-buf)
-        (setq child-dired-buf  (and (= 1 (length child-dired-buf))  (car child-dired-buf))))
-      (when child-dired-buf
-        (with-current-buffer child-dired-buf
-          (setq switches  dired-actual-switches
-                marked    (dired-remember-marks (point-min) (point-max)))))
-      (dired-other-window ancestor)
-      (dired-insert-subdir child switches)
-      (when marked (let ((inhibit-read-only  t)) (dired-mark-remembered marked)))
-      (set-buffer-modified-p nil))))
-
-(defun diredp-ancestor-dirs (dir)
-  "Return a list of the ancestor directories of directory DIR."
-  (mapcar #'file-name-as-directory
-          (diredp-maplist (lambda (dd) (mapconcat #'identity (reverse dd) "/"))
-                          (cdr (nreverse (split-string dir "/" t))))))
-
-(defun diredp-maplist (function list)
-  "Map FUNCTION over LIST and its cdrs.
-A simple, recursive version of the classic `maplist'."
-  (and list  (cons (funcall function list) (diredp-maplist function (cdr list)))))
-
-(defun diredp-existing-dired-buffer-p (buffer-name)
-  "Return non-nil if BUFFER-NAME names a live, existing Dired buffer."
-  (let ((dbuf  (get-buffer buffer-name)))
-    (and dbuf  (buffer-live-p dbuf)  (rassq dbuf dired-buffers))))
-
-;; From `cl-seq.el', function `union', without keyword treatment.
-;; (Same as `icicle-set-union' in `icicles-fn.el'.)
-(defun diredp-set-union (list1 list2)
-  "Combine LIST1 and LIST2 using a set-union operation.
-The result list contains all items that appear in either LIST1 or
-LIST2.  Comparison is done using `equal'.  This is a non-destructive
-function; it copies the data if necessary."
-  (cond ((null list1)         list2)
-        ((null list2)         list1)
-        ((equal list1 list2)  list1)
-        (t
-         (unless (>= (length list1) (length list2))
-           (setq list1  (prog1 list2 (setq list2  list1)))) ; Swap them.
-         (while list2
-           (unless (member (car list2) list1)  (setq list1  (cons (car list2) list1)))
-           (setq list2  (cdr list2)))
-         list1)))
-
-(when (fboundp 'file-equal-p)           ; Emacs 24+
-  (defun diredp-move-file (file &optional prompt-anyway)
-    "Move FILE to associated directory in `diredp-move-file-dirs'.
-If no association, or if you use a prefix arg, prompt for directory."
-    (interactive (list (dired-get-filename) current-prefix-arg))
-    (unless file (error "No file specified"))
-    (let* ((file-sans  (file-name-nondirectory file))
-           (dir        (file-name-as-directory
-                        (or (and (not prompt-anyway)
-                                 (cdr (assoc file-sans diredp-move-file-dirs)))
-                            (read-directory-name "Move to: ")))))
-      (when (file-equal-p dir (file-name-directory file))
-        (error "Cannot move to same directory: %s" dir))
-      (dired-rename-file file dir nil)
-      (dired-add-file (expand-file-name file-sans dir))
-      (message "Moved `%s' to `%s'" file-sans dir))))
-
-(defvar diredp-last-copied-filenames ()
-  "String list of file names last copied to the `kill-ring'.
-Copying is done by `dired-copy-filename-as-kill' and related commands.")
-
-
-;; REPLACE ORIGINAL in `dired-x.el'.
-;;
-;; Put text copied to kill ring in variable `diredp-last-copied-filenames'.
-;;
-(defun dired-copy-filename-as-kill (&optional arg)
-  "Copy names of marked (or next ARG) files into the kill ring.
-The names are separated by a space.
-With a zero prefix arg, use the absolute file name of each marked file.
-With \\[universal-argument], use the file name relative to the Dired buffer's
-`default-directory'.  (This still may contain slashes if in a subdirectory.)
-
-If on a subdir headerline, use absolute subdirname instead;
-prefix arg and marked files are ignored in this case.
-
-You can then feed the file name(s) to other commands with \\[yank].
-
-The value of global variable `diredp-last-copied-filenames' is updated
-to the string list of file name(s), so you can obtain it even after
-the kill ring is modified."
-  (interactive "P")
-  (let* ((num-arg  (prefix-numeric-value arg))
-         (string  (or (dired-get-subdir)
-                      (mapconcat #'identity
-                                 (cond ((not arg)       (dired-get-marked-files 'no-dir))
-                                       ((zerop num-arg) (dired-get-marked-files))
-                                       ((consp arg)     (dired-get-marked-files t))
-                                       (t               (dired-get-marked-files 'no-dir num-arg)))
-                                 " "))))
-    (unless (string= "" string)
-      (if (eq last-command 'kill-region) (kill-append string nil) (kill-new string))
-      (setq diredp-last-copied-filenames  (car kill-ring-yank-pointer))
-      (message "%s" string))))
-
-(defun diredp-copy-abs-filenames-as-kill () ; Not bound.
-  "Copy absolute names of marked files in Dired to the kill ring.
-Also set variable `diredp-last-copied-filenames' to the string that
-lists the file names.
-
-This is the same as using a zero prefix arg with command
-`dired-copy-filename-as-kill', that is, \\<dired-mode-map>`M-0 \\[dired-copy-filename-as-kill]'."
-  (interactive (diredp-ensure-mode))
-  (dired-copy-filename-as-kill 0))
-
-;;;###autoload
-(defalias 'diredp-paste-files 'diredp-yank-files) ; Bound to `C-y'.
-;;;###autoload
-(defun diredp-yank-files (&optional dir no-confirm-p details)
-  "Paste files, whose absolute names you copied, to the current directory.
-With a non-negative prefix arg you are instead prompted for the target
- directory.
-With a non-positive prefix arg you can see details about the files if
- you hit `l' when prompted to confirm pasting.  Otherwise you see only
- the file names.  The details you see are defined by option
- `diredp-list-file-attributes'.
-
-You should have copied the list of file names as a string to the kill
-ring using \\<dired-mode-map>`M-0 \\[dired-copy-filename-as-kill]' or \
-\\[diredp-copy-abs-filenames-as-kill].
-Those commands also set variable `diredp-last-copied-filenames' to the
-same string.  `diredp-yank-files' uses the value of that variable, not
-whatever is currently at the head of the kill ring.
-
-When called from Lisp:
-
-Optional arg NO-CONFIRM-P means do not ask for confirmation to copy.
-Optional arg DETAILS is passed to `diredp-y-or-n-files-p'."
-  (interactive (list (and current-prefix-arg  (natnump (prefix-numeric-value current-prefix-arg))
-                          (expand-file-name (read-directory-name "Yank files to directory: ")))
-                     nil
-                     (and current-prefix-arg
-                          (<= (prefix-numeric-value current-prefix-arg) 0)
-                          diredp-list-file-attributes)))
-  (setq dir  (or dir  (and (derived-mode-p 'dired-mode)  (dired-current-directory))))
-  (unless (file-directory-p dir) (error "Not a directory: `%s'" dir))
-  (let ((files  diredp-last-copied-filenames))
-    (unless (stringp files)  (error "No copied file names"))
-    (setq files  (diredp-delete-if-not (lambda (file) (file-name-absolute-p file)) (split-string files)))
-    (unless files  (error "No copied *absolute* file names (Did you use `M-0 w'?)"))
-    (if (and (not no-confirm-p)
-             (diredp-y-or-n-files-p "Paste files whose names you copied? " files nil details))
-        (dired-create-files #'dired-copy-file "Copy" files
-                            (lambda (from) (expand-file-name (file-name-nondirectory from) dir)))
-      (message "OK, file-pasting canceled"))))
-
-;;;###autoload
-(defun diredp-move-files-named-in-kill-ring (&optional dir no-confirm-p details) ; Bound to `C-w'
-  "Move files, whose absolute names you copied, to the current directory.
-With a non-negative prefix arg you are instead prompted for the target
- directory.
-With a non-positive prefix arg you can see details about the files if
- you hit `l' when prompted to confirm pasting.  Otherwise you see only
- the file names.  The details you see are defined by option
- `diredp-list-file-attributes'.
-
-You should have copied the list of file names as a string to the kill
-ring using \\<dired-mode-map>`M-0 \\[dired-copy-filename-as-kill]' or \
-\\[diredp-copy-abs-filenames-as-kill].
-Those commands also set variable `diredp-last-copied-filenames' to the
-same string.  `diredp-move-files-named-in-kill-ring' uses the value of
-that variable, not whatever is currently at the head of the kill ring.
-
-When called from Lisp:
-
-Optional arg NO-CONFIRM-P means do not ask for confirmation to move.
-Optional arg DETAILS is passed to `diredp-y-or-n-files-p'."
-  (interactive (list (and current-prefix-arg  (natnump (prefix-numeric-value current-prefix-arg))
-                          (expand-file-name (read-directory-name "Move files to directory: ")))
-                     nil
-                     (and current-prefix-arg
-                          (<= (prefix-numeric-value current-prefix-arg) 0)
-                          diredp-list-file-attributes)))
-  (setq dir  (or dir  (and (derived-mode-p 'dired-mode)  (dired-current-directory))))
-  (unless (file-directory-p dir) (error "Not a directory: `%s'" dir))
-  (let ((files  diredp-last-copied-filenames))
-    (unless (stringp files)  (error "No copied file names"))
-    (setq files  (diredp-delete-if-not (lambda (file) (file-name-absolute-p file)) (split-string files)))
-    (unless files  (error "No copied (absolute* file names (Did you use `M-0 w'?)"))
-    (if (and (not no-confirm-p)
-             (diredp-y-or-n-files-p "MOVE files whose names you copied? " files nil details))
-        (dired-create-files #'dired-rename-file "Move" files
-                            (lambda (from) (expand-file-name (file-name-nondirectory from) dir)))
-      (message "OK, file-moves canceled"))))
-
-
-;;; Commands operating on marked at all levels below (recursively)
-
-(defun diredp-get-confirmation-recursive (&optional type)
-  "Get confirmation from user to act on all TYPE here and below.
-If TYPE is nil use \"files\" in the confirmation prompt, else use TYPE.
-Raise an error if not confirmed.
-Raise an error first if not in Dired mode."
-  (diredp-ensure-mode)
-  (unless (y-or-n-p (format "Act on ALL %s (or all marked if any) in and UNDER this dir? "
-                            (or type 'files)))
-    (error "OK, canceled")))
-
-;;;###autoload
-(when (> emacs-major-version 21)        ; Emacs 22+ has KILL-ROOT parameter.
-  (defun diredp-kill-this-tree ()
-    "Remove this subdir listing and lower listings."
-    (interactive)
-    (dired-kill-tree (dired-current-directory) nil 'KILL-ROOT)))
-
-;;;###autoload
-(defun diredp-insert-subdirs (&optional switches interactivep) ; Bound to `M-i'
-  "Insert the marked subdirectories.
-Like using \\<dired-mode-map>`\\[dired-maybe-insert-subdir]' at each marked directory line."
-  (interactive (list (and current-prefix-arg
-                          (read-string "Switches for listing: "
-                                       (or (and (boundp 'dired-subdir-switches)  dired-subdir-switches)
-                                           dired-actual-switches)))
-                     t))
-  (dolist (subdir  (dired-get-marked-files nil
-                                           nil
-                                           (lambda (fl) (and (file-directory-p fl) ; Exclude `.' and `..'
-                                                             (not (diredp-string-match-p "/[.][.]?\\'" fl))))
-                                           nil
-                                           interactivep))
-    (dired-maybe-insert-subdir subdir switches)))
-
-;;;###autoload
-(defun diredp-insert-subdirs-recursive (&optional ignore-marks-p details) ; Bound to `M-+ M-i'
-  "Insert the marked subdirs, including those in marked subdirs.
-Like `diredp-insert-subdirs', but act recursively on subdirs.
-The subdirs inserted are those that are marked in the current Dired
-buffer, or ALL subdirs in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way (their marked
-subdirs are inserted...).
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive 'subdirs)
-                      (list current-prefix-arg diredp-list-file-attributes)))
-  (dolist (subdir  (diredp-get-files ignore-marks-p #'file-directory-p 'INCLUDE-SUBDIRS-P nil nil details))
-    (dired-maybe-insert-subdir subdir)))
-
-;;;###autoload
-(defun diredp-do-shell-command-recursive (command &optional ignore-marks-p details) ; Bound to `M-+ !'
-  "Run shell COMMAND on the marked files, including those in marked subdirs.
-Like `dired-do-shell-command', but act recursively on subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive
-   (progn (diredp-get-confirmation-recursive)
-          (let* ((prompt  "! on *: ")
-                 (cmd     (minibuffer-with-setup-hook
-                           (lambda ()
-                             (set (make-local-variable 'minibuffer-default-add-function)
-                                  'minibuffer-default-add-dired-shell-commands))
-                           (let ((dired-no-confirm  t))
-                             (if (functionp 'dired-guess-shell-command)
-                                 ;; Guess cmd based only on files marked in current (top) dir.
-                                 (dired-guess-shell-command prompt (dired-get-marked-files t))
-                               (read-shell-command prompt nil nil))))))
-            (list cmd current-prefix-arg diredp-list-file-attributes))))
-  (dired-do-shell-command command nil (diredp-get-files ignore-marks-p nil nil nil nil details)))
-
-(when (fboundp 'dired-do-async-shell-command) ; Emacs 23+
-
-  (defun diredp-do-async-shell-command-recursive (command &optional ignore-marks-p details)
-                                        ; Bound to `M-+ &'
-    "Run async shell COMMAND on marked files, including in marked subdirs.
-Like `dired-do-async-shell-command', but act recursively on subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-    (interactive
-     (progn (diredp-get-confirmation-recursive)
-            (let* ((prompt  "! on *: ")
-                   (cmd     (minibuffer-with-setup-hook
-                             (lambda ()
-                               (set (make-local-variable 'minibuffer-default-add-function)
-                                    'minibuffer-default-add-dired-shell-commands))
-                             (let ((dired-no-confirm  t))
-                               (if (functionp 'dired-guess-shell-command)
-                                   ;; Guess cmd based only on files marked in current (top) dir.
-                                   (dired-guess-shell-command prompt (dired-get-marked-files t))
-                                 (read-shell-command prompt nil nil))))))
-              (list cmd current-prefix-arg diredp-list-file-attributes))))
-    (dired-do-async-shell-command command nil (diredp-get-files ignore-marks-p nil nil nil nil details))))
-
-;;;###autoload
-(defun diredp-do-symlink-recursive (&optional ignore-marks-p details) ; Bound to `M-+ S'
-  "Make symbolic links to marked files, including those in marked subdirs.
-Like `dired-do-symlink', but act recursively on subdirs to pick up the
-files to link.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-do-create-files-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (diredp-do-create-files-recursive #'make-symbolic-link "Symlink" ignore-marks-p details))
-
-(defun diredp-do-relsymlink-recursive (&optional ignore-marks-p details) ; Bound to `M-+ Y'
-  "Relative symlink all marked files, including those in marked subdirs into a dir.
-Like `dired-do-relsymlink', but act recursively on subdirs to pick up the
-files to link.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-For absolute symlinks, use \\[diredp-do-symlink-recursive].
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-do-create-files-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive)
-                      (list current-prefix-arg diredp-list-file-attributes)))
-  (diredp-do-create-files-recursive #'dired-make-relative-symlink "RelSymLink" ignore-marks-p details))
-
-;;;###autoload
-(defun diredp-do-hardlink-recursive (&optional ignore-marks-p details) ; Bound to `M-+ H'
-  "Add hard links for marked files, including those in marked subdirs.
-Like `dired-do-hardlink', but act recursively on subdirs to pick up the
-files to link.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-do-create-files-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (diredp-do-create-files-recursive #'dired-hardlink "Hardlink" ignore-marks-p details))
-
-;;;###autoload
-(defun diredp-do-print-recursive (&optional ignore-marks-p details) ; Bound to `M-+ P'
-  "Print the marked files, including those in marked subdirs.
-Like `dired-do-print', but act recursively on subdirs to pick up the
-files to print.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (let* ((file-list  (diredp-get-files ignore-marks-p nil nil nil nil details))
-         (command    (dired-mark-read-string
-                      "Print %s with: "
-                      (mapconcat #'identity
-                                 (cons lpr-command (if (stringp lpr-switches) (list lpr-switches) lpr-switches))
-                                 " ")
-                      'print nil file-list)))
-    (dired-run-shell-command (dired-shell-stuff-it command file-list nil))))
-
-;;;###autoload
-(defun diredp-image-dired-display-thumbs-recursive (&optional ignore-marks-p append do-not-pop details)
-                                        ; Bound to `M-+ C-t d'
-  "Display thumbnails of marked files, including those in marked subdirs.
-Like `image-dired-display-thumbs', but act recursively on subdirs.
-Optional arguments APPEND and DO-NOT-POP are as for
-`image-dired-display-thumbs'.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-image-dired-required-msg)
-                      (diredp-get-confirmation-recursive)
-                      (list current-prefix-arg nil nil diredp-list-file-attributes)))
-  (let ((buf  (image-dired-create-thumbnail-buffer))
-        thumb-name files dired-buf)
-    (setq files      (diredp-get-files ignore-marks-p nil nil nil nil details)
-          dired-buf  (current-buffer))
-    (with-current-buffer buf
-      (let ((inhibit-read-only  t))
-        (if append (goto-char (point-max)) (erase-buffer))
-        (mapc (lambda (curr-file)
-                (setq thumb-name  (image-dired-thumb-name curr-file))
-                (if (and (not (file-exists-p thumb-name))
-                         (not (= 0 (image-dired-create-thumb curr-file thumb-name))))
-                    (message "Thumb could not be created for file %s" curr-file)
-                  (image-dired-insert-thumbnail thumb-name curr-file dired-buf)))
-              files))
-      (case image-dired-line-up-method
-        (dynamic      (image-dired-line-up-dynamic))
-        (fixed        (image-dired-line-up))
-        (interactive  (image-dired-line-up-interactive))
-        (none         nil)
-        (t            (image-dired-line-up-dynamic))))
-    (if do-not-pop
-        (display-buffer image-dired-thumbnail-buffer)
-      (pop-to-buffer image-dired-thumbnail-buffer))))
-
-;;;###autoload
-(defun diredp-image-dired-tag-files-recursive (&optional ignore-marks-p details) ; Bound to `M-+ C-t t'
-  "Tag marked files with an `image-dired' tag, including in marked subdirs.
-Like `image-dired-tag-files', but act recursively on subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-image-dired-required-msg)
-                      (diredp-get-confirmation-recursive)
-                      (list current-prefix-arg diredp-list-file-attributes)))
-  (let ((tag  (read-string "Tags to add (separate tags with a semicolon): ")))
-    (image-dired-write-tags (mapcar (lambda (x) (cons x tag))
-                                    (diredp-get-files ignore-marks-p nil nil nil nil details)))))
-
-;;;###autoload
-(defun diredp-image-dired-delete-tag-recursive (&optional ignore-marks-p details) ; Bound to `M-+ C-t r'
-  "Remove `image-dired' tag for marked files, including in marked subdirs.
-Like `image-dired-delete-tag', but act recursively on subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-image-dired-required-msg)
-                      (diredp-get-confirmation-recursive)
-                      (list current-prefix-arg diredp-list-file-attributes)))
-  (image-dired-remove-tag (diredp-get-files ignore-marks-p nil nil nil nil details)
-                          (read-string "Tag to remove: ")))
-
-;;;###autoload
-(defun diredp-image-dired-comment-files-recursive (&optional ignore-marks-p details)
-                                        ; Bound to `M-+ C-t c'
-  "Add comment to marked files in dired, including those in marked subdirs.
-Like `image-dired-dired-comment-files' but act recursively on subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-image-dired-required-msg)
-                      (diredp-get-confirmation-recursive)
-                      (list current-prefix-arg diredp-list-file-attributes)))
-  (let ((comment  (image-dired-read-comment)))
-    (image-dired-write-comments (mapcar (lambda (curr-file) (cons curr-file comment))
-                                        (diredp-get-files ignore-marks-p nil nil nil nil details)))))
-
-(when (> emacs-major-version 22)
-
-  (defun diredp-do-decrypt-recursive (&optional ignore-marks-p details) ; Bound to `M-+ : d'
-    "Decrypt marked files, including those in marked subdirs.
-Like `epa-dired-do-decrypt', but act recursively on subdirs to pick up
-the files to decrypt.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-    (interactive (progn (diredp-get-confirmation-recursive)
-                        (list current-prefix-arg diredp-list-file-attributes)))
-    (dolist (file  (diredp-get-files ignore-marks-p nil nil nil nil details))
-      (epa-decrypt-file (expand-file-name file)))
-    (revert-buffer))
-
-  (defun diredp-do-verify-recursive (&optional ignore-marks-p details) ; Bound to `M-+ : v'
-    "Verify marked files, including those in marked subdirs.
-Like `epa-dired-do-verify', but act recursively on subdirs to pick up
-the files to verify.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-    (interactive (progn (diredp-get-confirmation-recursive)
-                        (list current-prefix-arg diredp-list-file-attributes)))
-    (dolist (file  (diredp-get-files ignore-marks-p nil nil nil nil details))
-      (epa-verify-file (expand-file-name file)))
-    (revert-buffer))
-
-  (defun diredp-do-sign-recursive (&optional ignore-marks-p details) ; Bound to `M-+ : s'
-    "Sign marked files, including those in marked subdirs.
-Like `epa-dired-do-sign', but act recursively on subdirs to pick up
-the files to sign.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-    (interactive (progn (diredp-get-confirmation-recursive)
-                        (list current-prefix-arg diredp-list-file-attributes)))
-    (dolist (file  (diredp-get-files ignore-marks-p nil nil nil nil details))
-      (epa-sign-file (expand-file-name file)
-                     (epa-select-keys (epg-make-context) "Select keys for signing.
-If none are selected, the default secret key is used.  ")
-                     (y-or-n-p "Make a detached signature? ")))
-    (revert-buffer))
-
-  (defun diredp-do-encrypt-recursive (&optional ignore-marks-p details) ; Bound to `M-+ : e'
-    "Encrypt marked files, including those in marked subdirs.
-Like `epa-dired-do-encrypt', but act recursively on subdirs to pick up
-the files to encrypt.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-    (interactive (progn (diredp-get-confirmation-recursive)
-                        (list current-prefix-arg diredp-list-file-attributes)))
-    (dolist (file  (diredp-get-files ignore-marks-p nil nil nil nil details))
-      (epa-encrypt-file (expand-file-name file)
-                        (epa-select-keys (epg-make-context) "Select recipients for encryption.
-If none are selected, symmetric encryption is performed.  ")))
-    (revert-buffer)))
-
-;;;###autoload
-(defun diredp-do-bookmark-recursive (&optional ignore-marks-p prefix details) ; Bound to `M-+ M-b'
-  "Bookmark the marked files, including those in marked subdirs.
-Like `diredp-do-bookmark', but act recursively on subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive)
-                      (list current-prefix-arg
-                            (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            diredp-list-file-attributes)))
-  (dolist (file  (diredp-get-files ignore-marks-p nil nil nil nil details))
-    (diredp-bookmark prefix file 'NO-MSG-P)))
-
-;;;###autoload
-(defun diredp-do-bookmark-dirs-recursive (ignore-marks-p &optional details msgp)
-  "Bookmark this Dired buffer and marked subdirectory Dired buffers, recursively.
-Create a Dired bookmark for this directory and for each of its marked
-subdirectories.  Handle each of the marked subdirectory similarly:
-bookmark it and its marked subdirectories, and so on, recursively.
-Name each of these Dired bookmarks with the Dired buffer name.
-
-After creating the Dired bookmarks, create a sequence bookmark, named
-`DIRBUF and subdirs', where DIRBUF is the name of the original buffer.
-This bookmark represents the whole Dired tree rooted in the directory
-where you invoked the command.  Jumping to this sequence bookmark
-restores all of the Dired buffers making up the tree, by jumping to
-each of their bookmarks.
-
-With a prefix arg, bookmark the marked and unmarked subdirectory Dired
-buffers, recursively, that is, ignore markings.
-
-Note:
-
-* If there is more than one Dired buffer for a given subdirectory then
-  only the first such is used.
-
-* This command creates new bookmarks.  It never updates or overwrites
-  an existing bookmark.
-
-You need library `Bookmark+' for this command.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-subdirs'."
-  (interactive (progn (unless (featurep 'bookmark+)
-                        (error "You need library `Bookmark+' for this command"))
-                      (diredp-get-confirmation-recursive 'subdirs)
-                      (list current-prefix-arg diredp-list-file-attributes t)))
-  (diredp-ensure-mode)
-  (let ((sdirs   (diredp-get-subdirs ignore-marks-p nil details))
-        (snames  ())
-        dbufs)
-    (when (and msgp  sdirs) (message "Checking descendant directories..."))
-    (dolist (dir  (cons default-directory sdirs))
-      (when (setq dbufs  (dired-buffers-for-dir (expand-file-name dir))) ; Dirs with Dired buffers only.
-        (with-current-buffer (car dbufs)
-          (let ((bname  (bookmark-buffer-name))
-                (count  2))
-            (while (and (bmkp-get-bookmark-in-alist bname 'NOERROR)  (setq bname  (format "%s[%d]" bname count))))
-            (bookmark-set bname nil nil 'NO-UPDATE-P) ; Inhibit updating displayed list.
-            (push bname snames)))))
-    (let ((bname  (format "%s and subdirs" (bookmark-buffer-name)))
-          (count  2))
-      (while (and (bmkp-get-bookmark-in-alist bname 'NOERROR)  (setq bname  (format "%s[%d]" bname count))))
-      (bmkp-set-sequence-bookmark bname (nreverse snames) -1 'MSGP))
-    (bmkp-refresh/rebuild-menu-list nil)))
-
-;;;###autoload
-(defun diredp-do-bookmark-in-bookmark-file-recursive (bookmark-file ; Bound to `M-+ C-M-B', aka `M-+ C-M-S-b')
-                                                      &optional prefix ignore-marks-p bfile-bookmarkp details)
-  "Bookmark files here and below in BOOKMARK-FILE and save BOOKMARK-FILE.
-Like `diredp-do-bookmark-in-bookmark-file', but act recursively on
-subdirs.  The files included are those that are marked in the current
-Dired buffer, or all files in the directory if none are marked.
-Marked subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp:
- * Optional arg BFILE-BOOKMARKP non-nil means create a bookmark-file
-   bookmark for BOOKMARK-FILE.
- * Optional arg DETAILS is passed to `diredp-get-files'."
-  (interactive
-   (progn (diredp-get-confirmation-recursive)
-          (let ((d-r-b-f-args  (diredp-read-bookmark-file-args)))
-            (list (car d-r-b-f-args)
-                  (cadr d-r-b-f-args)
-                  (car (cddr d-r-b-f-args))
-                  nil
-                  diredp-list-file-attributes))))
-  (diredp-do-bookmark-in-bookmark-file bookmark-file prefix nil bfile-bookmarkp
-                                       (diredp-get-files ignore-marks-p nil nil nil nil details)))
-
-;;;###autoload
-(defun diredp-set-bookmark-file-bookmark-for-marked-recursive (bookmark-file
-                                                               &optional prefix ignore-marks-p details)
-                                        ; Bound to `M-+ C-M-b'
-  "Bookmark the marked files and create a bookmark-file bookmark for them.
-Like `diredp-set-bookmark-file-bookmark-for-marked', but act
-recursively on subdirs.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-do-bookmark-in-bookmark-file-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive)
-                      (let ((d-r-b-f-args  (diredp-read-bookmark-file-args)))
-                        (list (car d-r-b-f-args)
-                              (cadr d-r-b-f-args)
-                              (car (cddr d-r-b-f-args))
-                              diredp-list-file-attributes))))
-  (diredp-ensure-bookmark+)
-  (diredp-do-bookmark-in-bookmark-file-recursive
-   bookmark-file prefix ignore-marks-p 'CREATE-BOOKMARK-FILE-BOOKMARK details))
-
-;;;###autoload
-(defun diredp-do-find-marked-files-recursive (&optional arg details) ; Bound to `M-+ F'
-  "Find marked files simultaneously, including those in marked subdirs.
-Like `dired-do-find-marked-files', but act recursively on subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With (explicit) numeric prefix ARG >= 0, find the files but do not
-display them.
-
-With numeric prefix ARG <= 0, ignore all marks - include all files in
-this Dired buffer and all subdirs, recursively.
-
-Note that prefix-argument behavior is different for this command than
-for `dired-do-find-marked-files'.  In particular, a negative numeric
-prefix arg does not cause the files to be shown in separate frames.
-Only non-nil `pop-up-frames' (or equivalent configuration) causes
-the files to be shown in separate frames.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive)
-                      (list current-prefix-arg diredp-list-file-attributes)))
-  (let ((narg  (prefix-numeric-value arg)))
-    (dired-simultaneous-find-file (diredp-get-files (<= narg 0) nil nil nil nil details)
-                                  (and arg  (>= narg 0)  narg))))
-
-(when (fboundp 'dired-do-isearch-regexp) ; Emacs 23+
-
-  (defun diredp-do-isearch-recursive (&optional ignore-marks-p details) ; Bound to `M-+ M-s a C-s'
-    "Isearch the marked files, including those in marked subdirs.
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-    (interactive (progn (diredp-get-confirmation-recursive)
-                        (list current-prefix-arg diredp-list-file-attributes)))
-    (multi-isearch-files (diredp-get-files ignore-marks-p nil nil nil nil details)))
-
-  (defun diredp-do-isearch-regexp-recursive (&optional ignore-marks-p details) ; `M-+ M-s a C-M-s'
-    "Regexp-Isearch the marked files, including those in marked subdirs.
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-    (interactive (progn (diredp-get-confirmation-recursive)
-                        (list current-prefix-arg diredp-list-file-attributes)))
-    (multi-isearch-files-regexp (diredp-get-files ignore-marks-p nil nil nil nil details))))
-
-(defun diredp-do-search-recursive (regexp &optional ignore-marks-p details) ; Bound to `M-+ A'
-  "Regexp-search the marked files, including those in marked subdirs.
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-Stops when a match is found.
-To continue searching for the next match, use `\\[tags-loop-continue]'.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive)
-                      (list (read-string "Search marked files (regexp): ")
-                            current-prefix-arg
-                            diredp-list-file-attributes)))
-  (tags-search regexp '(diredp-get-files ignore-marks-p nil nil nil nil details)))
-
-;;;###autoload
-(defun diredp-do-query-replace-regexp-recursive (from to &optional arg details)
-                                        ; Bound to `M-+ Q'
-  "Do `query-replace-regexp' on marked files, including in marked subdirs.
-Query-replace FROM with TO.
-
-Like `dired-do-query-replace', but act recursively on subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With an (explicit) numeric prefix argument:
-
-* >= 0 means ignore all marks - include ALL files in this Dired buffer
-  and all subdirs, recursively.
-
-* <= 0 means replace only word-delimited matches.
-
-If you exit (`\\[keyboard-quit]', `RET' or `q'), you can resume the query replacement
-using `\\[tags-loop-continue]'.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive)
-                      (let ((common  (query-replace-read-args "Query replace regexp in marked files" t t)))
-                        (list (nth 0 common)
-                              (nth 1 common)
-                              current-prefix-arg
-                              diredp-list-file-attributes))))
-  (let* ((narg                  (and arg  (prefix-numeric-value arg)))
-         (delimited             (and narg  (<= narg 0)))
-         (ignore-marks-p        (and narg  (>= narg 0)))
-         (files                 (diredp-get-files ignore-marks-p nil nil nil nil details))
-         (fit-frame-min-width   30)
-         (fit-frame-min-height  15))
-    (dolist (file  files)
-      (let ((buffer  (get-file-buffer file)))
-        (when (and buffer  (with-current-buffer buffer buffer-read-only))
-          (error "File `%s' is visited read-only" file))))
-    (tags-query-replace from to delimited `',files)))
-
-;;;###autoload
-(defun diredp-do-grep-recursive (command-args &optional details) ; Bound to `M+ C-M-G'
-  "Run `grep' on marked files, including those in marked subdirs.
-Like `diredp-do-grep', but act recursively on subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive)
-                      (unless (if (< emacs-major-version 22)
-                                  grep-command
-                                (and grep-command  (or (not grep-use-null-device)  (eq grep-use-null-device t))))
-                        (grep-compute-defaults))
-                      (list (diredp-do-grep-1
-                             (diredp-get-files current-prefix-arg nil nil nil nil diredp-list-file-attributes)))))
-  (grep command-args))
-
-;;;###autoload
-(defun diredp-marked-recursive (dirname &optional ignore-marks-p details) ; Not bound to a key
-  "Open Dired on marked files, including those in marked subdirs.
-Like `diredp-marked', but act recursively on subdirs.
-
-See `diredp-do-find-marked-files-recursive' for a description of the
-files included.  In particular, if no files are marked here or in a
-marked subdir, then all files in the directory are included.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, DIRNAME here must be a string, not a cons.  It
-is used as the name of the new Dired buffer.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive)
-                      (list nil current-prefix-arg diredp-list-file-attributes)))
-  (dired (cons (or dirname  (generate-new-buffer-name (buffer-name)))
-               (diredp-get-files ignore-marks-p nil nil nil nil details))))
-
-;;;###autoload
-(defun diredp-marked-recursive-other-window (dirname &optional ignore-marks-p details) ; Bound to `M-+ C-M-*'
-  "Same as `diredp-marked-recursive', but uses a different window.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive)
-                      (list nil current-prefix-arg diredp-list-file-attributes)))
-  (dired-other-window
-   (cons (or dirname  (generate-new-buffer-name (buffer-name)))
-         (diredp-get-files ignore-marks-p nil nil nil nil details))))
-
-;;;###autoload
-(defun diredp-list-marked-recursive (&optional ignore-marks-p predicate details) ; Bound to `M-+ C-M-l'
-  "List the files marked here and in marked subdirs, recursively.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, all marks are ignored: all files in this Dired
-buffer and all descendant directories are included.
-
-You can use `RET' or `mouse-2' to visit any of the files.
-If `tooltip-mode' is on then moving the mouse over image-file names
-shows image previews.
-
-When called from Lisp:
- Non-nil optional arg IGNORE-MARKS-P means ignore marks.
- Non-nil optional arg PREDICATE is a file-name predicate.  List only
-  the files for which it returns non-nil.
- Non-nil optional arg DETAILS is passed to `diredp-list-files'."
-  (interactive ; No need for `diredp-get-confirmation-recursive' here.
-   (progn (diredp-ensure-mode) (list current-prefix-arg nil diredp-list-file-attributes)))
-  (let ((files  (diredp-get-files ignore-marks-p predicate))) (diredp-list-files files nil nil nil details)))
-
-;;;###autoload
-(defun diredp-flag-auto-save-files-recursive (&optional arg details) ; `M-+ #'
-  "Flag all auto-save files for deletion, including in marked subdirs.
-A non-negative prefix arg means to unmark (unflag) them instead.
-
-A non-positive prefix arg means to ignore subdir markings and act
-instead on ALL subdirs.  That is, flag all in this directory and all
-descendant directories.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-mark-recursive-1'."
-  (interactive (list current-prefix-arg diredp-list-file-attributes))
-  (let ((dired-marker-char  dired-del-marker))
-    (diredp-mark-recursive-1 arg "auto-save files" "auto-save file" '(diredp-looking-at-p "^.* #.+#$") details)))
-
-(when (fboundp 'char-displayable-p)     ; Emacs 22+
-
-  (defun diredp-change-marks-recursive (old new &optional arg predicate details) ; `M-+ * c'
-    "Change all OLD marks to NEW marks, including those in marked subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-* A non-positive prefix arg means ignore subdir markings and act
-  instead on ALL subdirs.
-
-* A non-negative prefix arg means do not change marks on subdirs
-  themselves.
-
-Note: If there is more than one Dired buffer for a given subdirectory
-then only the first such is used.
-
-When called from Lisp:
- Non-nil arg PREDICATE is a file-name predicate.  Act on only the
-  files for which it returns non-nil.
- DETAILS is passed to `diredp-get-subdirs'."
-    (interactive
-     (progn (diredp-get-confirmation-recursive)
-            (let* ((cursor-in-echo-area  t)
-                   (old                  (progn (message "Change (old mark): ") (read-char)))
-                   (new                  (progn (message "Change `%c' marks to (new mark): " old) (read-char))))
-              (list old new current-prefix-arg nil diredp-list-file-attributes))))
-    (let* ((numarg             (and arg  (prefix-numeric-value arg)))
-           (nosubs             (natnump numarg))
-           (ignore-marks       (and numarg  (<= numarg 0)))
-           (dired-marker-char  new)
-           (sdirs              (diredp-get-subdirs ignore-marks predicate details))
-           (old-strg           (format "\n%c" old))
-           (count              0)
-           dbufs)
-      (unless (char-displayable-p old) (error "Not a displayable character: `%c'" old))
-      (unless (char-displayable-p new) (error "Not a displayable character: `%c'" new))
-      (message "Changing mark `%c' to `%c'..." old new)
-      (dolist (dir  (cons default-directory sdirs))
-        (when (setq dbufs  (dired-buffers-for-dir (expand-file-name dir))) ; Dirs with Dired buffers only.
-          (with-current-buffer (car dbufs)
-            (let ((inhibit-read-only  t)
-                  (file               nil))
-              (save-excursion
-                (goto-char (point-min))
-                (while (search-forward old-strg nil t)
-                  (save-match-data (setq file  (dired-get-filename 'no-dir t)))
-                  ;; Do nothing if changing from UNmarked and not on a file or dir name.
-                  (unless (and (= old ?   )  (not file))
-                    ;; Do nothing if marked subdir and not changing subdir marks.
-                    (unless (and nosubs  file  (file-directory-p file))
-                      (subst-char-in-region (match-beginning 0) (match-end 0) old new)
-                      (setq count  (1+ count))))))))))
-      (message "%d mark%s changed from `%c' to `%c'" count (dired-plural-s count) old new)))
-
-  (defun diredp-unmark-all-marks-recursive (&optional arg details) ; `M-+ U'
-    "Remove ALL marks everywhere, including in marked subdirs.
-A prefix arg is as for `diredp-unmark-all-files-recursive'.
-Note that a negative prefix arg (e.g. `C--') removes all marks from
-this Dired buffer and then does the same recursively for each of its
-subdirs.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-unmark-all-files-recursive'."
-    (interactive (progn (diredp-get-confirmation-recursive)
-                        (list current-prefix-arg diredp-list-file-attributes)))
-    (diredp-unmark-all-files-recursive ?\r arg details))
-
-  (defun diredp-unmark-all-files-recursive (mark &optional arg predicate details) ; `M-+ M-DEL'
-    "Remove a given mark (or ALL) everywhere, including in marked subdirs.
-You are prompted for the mark character to remove.  If you hit `RET'
-instead then ALL mark characters are removed.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-* A non-positive prefix arg means ignore subdir markings and act
-  instead on ALL subdirs.
-
-* A non-negative prefix arg means do not change marks on subdirs
-  themselves.
-
-Note: If there is more than one Dired buffer for a given subdirectory
-then only the first such is used.
-
-When called from Lisp:
- Non-nil arg PREDICATE is a file-name predicate.  Act on only the
-  files for which it returns non-nil.
- DETAILS is passed to `diredp-get-subdirs'."
-    (interactive
-     (progn (diredp-get-confirmation-recursive)
-            (let* ((cursor-in-echo-area  t)
-                   (mrk                  (progn (message "Remove marks (RET means all): ") (read-char))))
-              (list mrk current-prefix-arg nil diredp-list-file-attributes))))
-    (let* ((numarg             (and arg  (prefix-numeric-value arg)))
-           (nosubs             (natnump numarg))
-           (ignore-marks       (and numarg  (<= numarg 0)))
-           (dired-marker-char  ?\  )    ; Unmark
-           (sdirs              (diredp-get-subdirs ignore-marks predicate details))
-           (mrk-strg           (format "\n%c" mark))
-           (count              0)
-           dbufs)
-      (unless (char-displayable-p mark) (error "Not a displayable character: `%c'" mark))
-      (if (eq mark ?\r)
-          (message "Unmarking ALL marks here and below...")
-        (message "Unmarking mark `%c' here and below..." mark))
-      (dolist (dir  (cons default-directory sdirs))
-        (when (setq dbufs  (dired-buffers-for-dir (expand-file-name dir))) ; Dirs with Dired buffers only.
-          (with-current-buffer (car dbufs)
-            (let ((inhibit-read-only  t)
-                  (file               nil))
-              (save-excursion
-                (goto-char (point-min))
-                (while (if (eq mark ?\r)
-                           (re-search-forward dired-re-mark nil t)
-                         (search-forward mrk-strg nil t))
-                  (save-match-data (setq file  (dired-get-filename 'no-dir t)))
-                  ;; Do nothing if marked subdir and not changing subdir marks.
-                  (unless (and nosubs  file  (file-directory-p file))
-                    (subst-char-in-region (match-beginning 0) (match-end 0) (preceding-char) ?\   ))
-                  (setq count  (1+ count))))))))
-      (message "%d mark%s UNmarked" count (dired-plural-s count))))
-
-  )
-
-(when (and (memq system-type '(windows-nt ms-dos))  (fboundp 'w32-browser))
-
-  (defun diredp-multiple-w32-browser-recursive (&optional ignore-marks-p details)
-    "Run Windows apps for with marked files, including those in marked subdirs.
-Like `dired-multiple-w32-browser', but act recursively on subdirs.
-
-See `diredp-do-find-marked-files-recursive' for a description of the
-files included.  In particular, if no files are marked here or in a
-marked subdir, then all files in the directory are included.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-    (interactive (progn (diredp-get-confirmation-recursive)
-                        (list current-prefix-arg diredp-list-file-attributes)))
-    (let ((files  (diredp-get-files ignore-marks-p nil nil nil nil details)))
-      (while files
-        (w32-browser (car files))
-        (sleep-for w32-browser-wait-time)
-        (setq files  (cdr files)))))
-
-  )
-
-;;;###autoload
-(defun diredp-copy-filename-as-kill-recursive (&optional arg details) ; Bound to `M-+ M-w'
-  "Copy names of marked files here and in marked subdirs, to `kill-ring'.
-The names are separated by a space.
-
-Like `dired-copy-filename-as-kill', but act recursively on subdirs.
-\(Do not copy subdir names themselves.)
-
-With no prefix arg, use relative file names.
-With a zero prefix arg, use absolute file names.
-With a plain prefix arg (`C-u'), use names relative to the current
-Dired directory.  (This might contain slashes if in a subdirectory.)
-
-If on a subdir headerline, use absolute subdir name instead - prefix
-arg and marked files are ignored in this case.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-The names are copied to the kill ring and to variable
-`diredp-last-copied-filenames'.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive                          ; No need for `diredp-get-confirmation-recursive' here.
-   (progn (diredp-ensure-mode) (list current-prefix-arg diredp-list-file-attributes)))
-  (let* ((files   (mapcar (cond ((zerop (prefix-numeric-value arg)) #'identity)
-                                ((consp arg) (lambda (fn) (concat (dired-current-directory t)
-                                                                  (file-name-nondirectory fn))))
-                                (t (lambda (fn) (file-name-nondirectory fn))))
-                          (diredp-get-files nil nil nil nil nil details)))
-         (string  (mapconcat #'identity files " ")))
-    (unless (string= "" string)
-      (if (eq last-command 'kill-region) (kill-append string nil) (kill-new string))
-      (setq diredp-last-copied-filenames  (car kill-ring-yank-pointer)))
-    (message "%s" string)))
-
-;;;###autoload
-(defun diredp-copy-abs-filenames-as-kill-recursive (&optional ignore-marks-p details) ; Not bound.
-  "Copy absolute names of files marked here and in marked subdirs, recursively.
-The names are copied to the kill ring and to variable
-`dired-copy-filename-as-kill'.
-
-The files whose names are copied are those that are marked in the
-current Dired buffer, or all files in the directory if none are
-marked.  Marked subdirectories are handled recursively in the same
-way.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-copy-filename-as-kill-recursive'."
-  (interactive                          ; No need for `diredp-get-confirmation-recursive' here.
-   (progn (diredp-ensure-mode) (list current-prefix-arg diredp-list-file-attributes)))
-  (diredp-copy-filename-as-kill-recursive 0 details)
-  (setq diredp-last-copied-filenames  (car kill-ring-yank-pointer)))
-
-;;;###autoload
-(defun diredp-mark-files-regexp-recursive (regexp
-                                           &optional marker-char ignore-marks-p details) ; Bound to `M-+ % m'
-  "Mark all files matching REGEXP, including those in marked subdirs.
-Like `dired-mark-files-regexp' but act recursively on marked subdirs.
-
-The file names to be matched by this command are always absolute -
-they include the full directory.  Note that this does NOT correspond
-to the default behavior for `dired-mark-files-regexp'.  The other
-matching possibilities offered by `dired-mark-files-regexp' are not
-available for this command.
-
-Directories `.' and `..' are never marked.
-
-A non-negative prefix arg means to UNmark the files instead.
-
-A non-positive prefix arg means to ignore subdir markings and act
-instead on ALL subdirs.  That is, mark all matching files in this
-directory and all descendant directories.
-
-REGEXP is an Emacs regexp, not a shell wildcard.  Thus, use `\\.o$' for
-object files--just `.o' will mark more than you might think.
-
-REGEXP is added to `regexp-search-ring', for regexp search.
-
-Note: If there is more than one Dired buffer for a given subdirectory
-then only the first such is used.
-
-When called from Lisp, DETAILS is passed to `diredp-get-subdirs'."
-  (interactive (let* ((numarg   (and current-prefix-arg  (prefix-numeric-value current-prefix-arg)))
-                      (unmark   (and numarg  (>= numarg 0)))
-                      (ignorep  (and numarg  (<= numarg 0))))
-                 (list (diredp-read-regexp (concat (if unmark "UNmark" "Mark") " files (regexp): "))
-                       (and unmark  ?\040)
-                       ignorep
-                       diredp-list-file-attributes)))
-  (add-to-list 'regexp-search-ring regexp) ; Add REGEXP to `regexp-search-ring'.
-  (let ((dired-marker-char  (or marker-char  dired-marker-char))
-        (sdirs              (diredp-get-subdirs ignore-marks-p nil details))
-        (matched            0)
-        (changed            0)
-        dbufs chg.mtch)
-    (message "%s files..." (if (eq ?\040 dired-marker-char) "UNmarking" "Marking"))
-    (dolist (dir  (cons default-directory sdirs))
-      (when (setq dbufs  (dired-buffers-for-dir (expand-file-name dir))) ; Dirs with Dired buffers only.
-        (with-current-buffer (car dbufs)
-          (setq chg.mtch  (diredp-mark-if (and (not (diredp-looking-at-p dired-re-dot))
-                                               (not (eolp)) ; Empty line
-                                               (let ((fn  (dired-get-filename nil 'NO-ERROR)))
-                                                 (and fn  (diredp-string-match-p regexp fn))))
-                                          "file")
-                changed   (+ changed (or (car chg.mtch)  0))
-                matched   (+ matched (or (cdr chg.mtch)  0))))))
-    (message "%s file%s%s%s newly %s"
-             matched
-             (dired-plural-s matched)
-             (if (not (= matched changed)) " matched, " "")
-             (if (not (= matched changed)) changed "")
-             (if (eq ?\040 dired-marker-char) "unmarked" "marked"))))
-
-;;;###autoload
-(defun diredp-mark-files-containing-regexp-recursive (regexp
-                                                      &optional marker-char ignore-marks-p details) ; `M-+ % g'
-  "Mark files with contents containing a REGEXP match, including in marked subdirs.
-Like `dired-mark-files-containing-regexp' but act recursively on
-marked subdirs.
-
-A non-negative prefix arg means to UNmark the files instead.
-
-A non-positive prefix arg means to ignore subdir markings and act
-instead on ALL subdirs.  That is, mark all matching files in this
-directory and all descendant directories.
-
-REGEXP is added to `regexp-search-ring', for regexp search.
-
-Note: If there is more than one Dired buffer for a given subdirectory
-then only the first such is used.
-
-If a file is visited in a buffer and `dired-always-read-filesystem' is
-nil, this looks in the buffer without revisiting the file, so the
-results might be inconsistent with the file on disk if its contents
-have changed since it was last visited.
-
-When called from Lisp, DETAILS is passed to `diredp-get-subdirs'."
-
-  (interactive (let* ((numarg   (and current-prefix-arg  (prefix-numeric-value current-prefix-arg)))
-                      (unmark   (and numarg  (>= numarg 0)))
-                      (ignorep  (and numarg  (<= numarg 0))))
-                 (list (diredp-read-regexp (concat (if unmark "UNmark" "Mark") " files containing (regexp): "))
-                       (and unmark  ?\040)
-                       ignorep
-                       diredp-list-file-attributes)))
-  (add-to-list 'regexp-search-ring regexp) ; Add REGEXP to `regexp-search-ring'.
-  (let ((dired-marker-char  (or marker-char  dired-marker-char))
-        (sdirs              (diredp-get-subdirs ignore-marks-p nil details))
-        (matched            0)
-        (changed            0)
-        dbufs chg.mtch)
-    (message "%s files..." (if (eq ?\040 dired-marker-char) "UNmarking" "Marking"))
-    (dolist (dir  (cons default-directory sdirs))
-      (when (setq dbufs  (dired-buffers-for-dir (expand-file-name dir))) ; Dirs with Dired buffers only.
-        (with-current-buffer (car dbufs)
-          (setq chg.mtch
-                (diredp-mark-if
-                 (and (not (diredp-looking-at-p dired-re-dot))
-                      (not (eolp))
-                      (let ((fname  (dired-get-filename nil t)))
-                                  
-                        (and fname
-                             (file-readable-p fname)
-                             (not (file-directory-p fname))
-                             (let ((prebuf  (get-file-buffer fname)))
-                               (message "Checking %s" fname)
-                               ;; For now, do it inside Emacs.  Grep might be better if there are lots of files.
-                               (if (and prebuf  (or (not (boundp 'dired-always-read-filesystem))
-                                                    (not dired-always-read-filesystem))) ; Emacs 26+
-                                   (with-current-buffer prebuf
-                                     (save-excursion (goto-char (point-min)) (re-search-forward regexp nil t)))
-                                 (with-temp-buffer
-                                   (insert-file-contents fname)
-                                   (goto-char (point-min))
-                                   (re-search-forward regexp nil t)))))))
-                 "file")
-                changed   (+ changed (or (car chg.mtch)  0))
-                matched   (+ matched (or (cdr chg.mtch)  0))))))
-    (message "%s file%s%s%s newly %s"
-             matched
-             (dired-plural-s matched)
-             (if (not (= matched changed)) " matched, " "")
-             (if (not (= matched changed)) changed "")
-             (if (eq ?\040 dired-marker-char) "unmarked" "marked"))))
-
-(defun diredp-mark-extension-recursive (extension &optional arg details) ; Bound to `M-+ * .'
-  "Mark all files with a certain EXTENSION, including in marked subdirs.
-A `.' is not automatically prepended to the string entered.
-
-This is like `diredp-mark/unmark-extension', but this acts recursively
-on marked subdirs, and a non-positive prefix arg acts differently.
-
-A non-negative prefix arg means to unmark them instead.
-
-A non-positive prefix arg means to ignore subdir markings and act
-instead on ALL subdirs.  That is, mark all in this directory and all
-descendant directories.
-
-Non-interactively, EXTENSION is the extension (a string).  It can also
-be a list of extension strings.
-Optional argument ARG is the prefix arg.
-
-When called from Lisp, DETAILS is passed to `diredp-mark-files-regexp-recursive'."
-  (interactive (let* ((numarg  (and current-prefix-arg  (prefix-numeric-value current-prefix-arg)))
-                      (unmark  (and numarg  (>= numarg 0))))
-                 (list (diredp-read-regexp (concat (if unmark "UNmark" "Mark") " extension: "))
-                       current-prefix-arg
-                       diredp-list-file-attributes)))
-  (let* ((numarg   (and arg  (prefix-numeric-value arg)))
-         (unmark   (and numarg  (>= numarg 0)))
-         (ignorep  (and numarg  (<= numarg 0))))
-    (or (listp extension)  (setq extension  (list extension)))
-    (diredp-mark-files-regexp-recursive (concat ".+[.]\\("
-                                                (mapconcat #'regexp-quote extension "\\|")
-                                                "\\)$")
-                                        (if unmark ?\040 dired-marker-char)
-                                        ignorep
-                                        details)))
-
-;; FIXME: Factor out code that is common with `dired-mark-sexp'.
-;;
-(when (fboundp 'minibuffer-with-setup-hook) ; Emacs 22+
-
-  (defun diredp-mark-sexp-recursive (predicate &optional arg details) ; Bound to `M-+ M-(', `M-+ * ('
-    "Mark files here and below for which PREDICATE returns non-nil.
-Like `diredp-mark-sexp', but act recursively on subdirs.
-
-A non-negative prefix arg means to unmark those files instead.
-
-A non-positive prefix arg means to ignore subdir markings and act
-instead on ALL subdirs.  That is, mark all in this directory and all
-descendant directories.
-
-PREDICATE is a lisp sexp that can refer to the following symbols as
-variables:
-
-    `mode'   [string]  file permission bits, e.g. \"-rw-r--r--\"
-    `nlink'  [integer] number of links to file
-    `size'   [integer] file size in bytes
-    `uid'    [string]  owner
-    `gid'    [string]  group (If the gid is not displayed by `ls',
-                       this will still be set (to the same as uid))
-    `time'   [string]  the time that `ls' displays, e.g. \"Feb 12 14:17\"
-    `name'   [string]  the name of the file
-    `sym'    [string]  if file is a symbolic link, the linked-to name,
-                       else \"\"
-    `inode'  [integer] the inode of the file (only for `ls -i' output)
-    `blks'   [integer] the size of the file for `ls -s' output
-                       (ususally in blocks or, with `-k', in Kbytes)
-Examples:
-  Mark zero-length files: `(equal 0 size)'
-  Mark files last modified on Feb 2: `(string-match \"Feb  2\" time)'
-  Mark uncompiled Emacs Lisp files (`.el' file without a `.elc' file):
-     First, Dired just the source files: `dired *.el'.
-     Then, use \\[diredp-mark-sexp-recursive] with this sexp:
-          (not (file-exists-p (concat name \"c\")))
-
-There's an ambiguity when a single integer not followed by a unit
-prefix precedes the file mode: It is then parsed as inode number
-and not as block size (this always works for GNU coreutils ls).
-
-Another limitation is that the uid field is needed for the
-function to work correctly.  In particular, the field is not
-present for some values of `ls-lisp-emulation'.
-
-This function operates only on the Dired buffer content.  It does not
-refer at all to the underlying file system.  Contrast this with
-`find-dired', which might be preferable for the task at hand.
-
-When called from Lisp, DETAILS is passed to `diredp-get-subdirs'."
-    ;; Using `sym' = "", instead of nil, for non-linked files avoids the trap of
-    ;; (string-match "foo" sym) into which a user would soon fall.
-    ;; Use `equal' instead of `=' in the example, as it works on integers and strings.
-    ;; (interactive "xMark if (vars: inode,blks,mode,nlink,uid,gid,size,time,name,sym): \nP")
-
-    (interactive
-     (let* ((numarg  (and current-prefix-arg  (prefix-numeric-value current-prefix-arg)))
-            (unmark  (and numarg  (>= numarg 0))))
-       (diredp-get-confirmation-recursive)
-       (list (diredp-read-expression (format "%s if (Lisp expr): " (if current-prefix-arg "UNmark" "Mark")))
-             current-prefix-arg
-             diredp-list-file-attributes)))
-    (message "%s" predicate)
-    (let* ((numarg             (and arg  (prefix-numeric-value arg)))
-           (unmark             (and numarg  (>= numarg 0)))
-           (ignorep            (and numarg  (<= numarg 0)))
-           (dired-marker-char  (if unmark ?\040 dired-marker-char))
-           (inode              nil)
-           (blks               ())
-           (matched            0)
-           (changed            0)
-           dbufs chg.mtch mode nlink uid gid size time name sym)
-      (dolist (dir  (cons default-directory (diredp-get-subdirs ignorep nil details)))
-        (when (setq dbufs  (dired-buffers-for-dir (expand-file-name dir))) ; Dirs with Dired buffers only.
-          (with-current-buffer (car dbufs)
-            (setq chg.mtch
-                  (diredp-mark-if
-                   (save-excursion
-                     (and
-                      ;; Sets vars INODE BLKS MODE NLINK UID GID SIZE TIME NAME and SYM
-                      ;; according to current file line.  Returns `t' for success, nil if
-                      ;; there is no file line.  Upon success, these vars are set, to either
-                      ;; nil or the appropriate value, so they need not be initialized.
-                      ;; Moves point within the current line.
-                      (dired-move-to-filename)
-                      (let ((mode-len             10) ; Length of `mode' string.
-                            ;; As in `dired.el', but with subexpressions \1=inode, \2=blks:
-                            ;; GNU `ls -hs' suffixes the block count with a unit and prints it as a float
-                            ;; FreeBSD does neither.
-                            ;; $$$$$$ (dired-re-inode-size  "\\s *\\([0-9]*\\)\\s *\\([0-9]*\\) ?")
-                            (dired-re-inode-size  (if (> emacs-major-version 24)
-                                                      "\\=\\s *\\([0-9]+\\s +\\)?\
-\\(?:\\([0-9]+\\(?:\\.[0-9]*\\)?[BkKMGTPEZY]?\\)? ?\\)"
-                                                    "\\s *\\([0-9]*\\)\\s *\\([0-9]*\\) ?"))
-                            pos)
-                        (beginning-of-line)
-                        (forward-char 2)
-                        (search-forward-regexp dired-re-inode-size nil t)
-                        ;; `INODE', `BLKS', `MODE'
-                        ;; XXX Might be a size not followed by a unit prefix.
-                        ;; Could set `blks' to `inode' if it were otherwise nil, with similar reasoning
-                        ;; as for setting `gid' to `uid', but it would be even more whimsical.
-                        (setq inode  (and (match-string 1)  (string-to-number (match-string 1)))
-                              blks   (and (match-string 2)  (if (fboundp 'dired-x--string-to-number) ; Emacs 25+
-                                                                (dired-x--string-to-number (match-string 2))
-                                                              (string-to-number (match-string 2))))
-                              mode   (buffer-substring (point) (+ mode-len (point))))
-                        (forward-char mode-len)
-                        ;; Skip any extended attributes marker ("." or "+").
-                        (unless (eq (char-after) ?\   ) (forward-char 1))
-                        (setq nlink  (read (current-buffer))) ; `NLINK'
-
-                        ;; `UID'
-                        ;; Another issue is that GNU `ls -n' right-justifies numerical UIDs and GIDs,
-                        ;; while FreeBSD left-justifies them, so do not rely on a specific whitespace
-                        ;; layout.  Both of them right-justify all other numbers, though.
-                        ;; XXX Return a number if the `uid' or `gid' seems to be numerical?
-                        ;; $$$$$$ (setq uid  (buffer-substring (+ (point) 1) (progn (forward-word 1) (point))))
-                        (setq uid  (buffer-substring (progn (skip-chars-forward " \t")  (point))
-                                                     (progn (skip-chars-forward "^ \t") (point))))
-                        (cond ((> emacs-major-version 24)
-                               (dired-move-to-filename)
-                               (save-excursion
-                                 (setq time ; `TIME'
-                                       ;; The regexp below tries to match from the last digit of the size
-                                       ;; field through a space after the date.  Also, dates may have
-                                       ;; different formats depending on file age, so the date column need
-                                       ;; not be aligned to the right.
-                                       (buffer-substring
-                                        (save-excursion (skip-chars-backward " \t") (point))
-                                        (progn (re-search-backward directory-listing-before-filename-regexp)
-                                               (skip-chars-forward "^ \t")
-                                               (1+ (point))))
-
-                                       size ; `SIZE'
-                                       (dired-x--string-to-number
-                                        ;; We know that there's some kind of number before point because
-                                        ;; the regexp search above succeeded.  Not worth doing an extra
-                                        ;; check for leading garbage.
-                                        (buffer-substring (point) (progn (skip-chars-backward "^ \t") (point))))
-                                       ;; If no `gid' is displayed, `gid' will be set to `uid' but user
-                                       ;; will then not reference it anyway in PREDICATE.
-
-                                       gid ; `GID'
-                                       (buffer-substring (progn (skip-chars-backward " \t") (point))
-                                                         (progn (skip-chars-backward "^ \t") (point)))))
-                               ;; `NAME', `SYM'
-                               (setq name  (buffer-substring (point)
-                                                             (or (dired-move-to-end-of-filename t)  (point)))
-                                     sym   (if (diredp-looking-at-p " -> ")
-                                               (buffer-substring (progn (forward-char 4) (point))
-                                                                 (line-end-position))
-                                             "")))
-                              (t
-                               (re-search-forward
-                                (if (< emacs-major-version 20)
-                                    "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct\\|Nov\\|Dec\\)"
-                                  dired-move-to-filename-regexp))
-                               (goto-char (match-beginning 1))
-                               (forward-char -1)
-                               (setq size ; `SIZE'
-                                     (string-to-number (buffer-substring (save-excursion (backward-word 1)
-                                                                                         (setq pos  (point)))
-                                                                         (point))))
-                               (goto-char pos)
-                               (backward-word 1)
-                               ;; `GID', `TIME', `NAME', `SYM'
-                               ;; if no `gid' is displayed, `gid' will be set to `uid' but user will then
-                               ;; not reference it anyway in PREDICATE.
-                               (setq gid   (buffer-substring (save-excursion (forward-word 1) (point)) (point))
-                                     time  (buffer-substring (match-beginning 1) (1- (dired-move-to-filename)))
-                                     name  (buffer-substring (point) (or (dired-move-to-end-of-filename t)
-                                                                         (point)))
-                                     sym   (if (diredp-looking-at-p " -> ")
-                                               (buffer-substring (progn (forward-char 4) (point))
-                                                                 (line-end-position))
-                                             "")))))
-                      ;; Vanilla Emacs uses `lexical-binding' = t, and it passes bindings to `eval'
-                      ;; as a second arg.  We use `lexical-binding' = nil, and anyway there should
-                      ;; be no need to pass the bindings.
-                      (eval predicate)))
-                   (format "'%s file" predicate)))
-            (setq changed   (+ changed (or (car chg.mtch)  0))
-                  matched   (+ matched (or (cdr chg.mtch)  0))))))
-      (message "%s file%s%s%s newly %s" matched (dired-plural-s matched)
-               (if (not (= matched changed)) " matched, " "")
-               (if (not (= matched changed)) changed "")
-               (if (eq ?\040 dired-marker-char) "unmarked" "marked"))))
-
-  (if (fboundp 'read--expression)       ; Emacs 24.4+
-      (defalias 'diredp-read-expression 'read--expression)
-    (defun diredp-read-expression (prompt &optional initial-contents)
-      (let ((minibuffer-completing-symbol  t))
-        (minibuffer-with-setup-hook
-            (lambda ()       ; Vanilla Emacs FIXME: call `emacs-lisp-mode'?
-              (add-function :before-until (local 'eldoc-documentation-function)
-                            #'elisp-eldoc-documentation-function)
-              (eldoc-mode 1)
-              (add-hook 'completion-at-point-functions #'elisp-completion-at-point nil t)
-              (run-hooks 'eval-expression-minibuffer-setup-hook))
-          (read-from-minibuffer
-           prompt initial-contents (if (boundp 'pp-read-expression-map)
-                                       pp-read-expression-map
-                                     read-expression-map)
-           t 'read-expression-history)))))
-
-  )
-
-;;;###autoload
-(defun diredp-mark-autofiles-recursive (&optional arg details) ; Bound to `M-+ * B'
-  "Mark all autofiles, including in marked subdirs.
-Autofiles are files that have an autofile bookmark.
-A non-negative prefix arg means to unmark them instead.
-
-A non-positive prefix arg means to ignore subdir markings and act
-instead on ALL subdirs.  That is, mark all in this directory and all
-descendant directories.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-mark-recursive-1'."
-  (interactive (list current-prefix-arg diredp-list-file-attributes))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-recursive-1 arg "autofiles" "autofile"
-                           '(and (not (diredp-looking-at-p dired-re-dot))  (not (eolp))
-                             (let ((fname  (dired-get-filename nil t)))
-                               (and fname  (bmkp-get-autofile-bookmark fname))))
-                           details))
-
-;;;###autoload
-(defun diredp-mark-executables-recursive (&optional arg details) ; Bound to `M-+ * *'
-  "Mark all executable files, including in marked subdirs.
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-A non-negative prefix arg means to unmark them instead.
-
-A non-positive prefix arg means to ignore subdir markings and act
-instead on ALL subdirs.  That is, mark all in this directory and all
-descendant directories.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-mark-recursive-1'."
-  (interactive (list current-prefix-arg diredp-list-file-attributes))
-  (diredp-mark-recursive-1 arg "executable files" "executable file" '(diredp-looking-at-p dired-re-exe) details))
-
-;;;###autoload
-(defun diredp-mark-directories-recursive (&optional arg details) ; Bound to `M-+ * /'
-  "Mark all directories except `.' and `..', including in marked subdirs.
-The directories included are those that are marked in the current
-Dired buffer, or all subdirs in the directory if none are marked.
-Marked subdirectories are handled recursively in the same way.
-
-A non-negative prefix arg means to unmark them instead.
-
-A non-positive prefix arg means to ignore subdir markings and act
-instead on ALL subdirs.  That is, mark all in this directory and all
-descendant directories.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-mark-recursive-1'."
-  (interactive (list current-prefix-arg diredp-list-file-attributes))
-  (diredp-mark-recursive-1 arg "directories" "directory" '(and (diredp-looking-at-p dired-re-dir)
-                                                           (not (diredp-looking-at-p dired-re-dot)))
-                           details))
-;;;###autoload
-(defun diredp-mark-symlinks-recursive (&optional arg details) ; Bound to `M-+ * @'
-  "Mark all symbolic links, including in marked subdirs.
-The symlinks included are those that are marked in the current Dired
-buffer, or all symlinks in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-A non-negative prefix arg means to unmark them instead.
-
-A non-positive prefix arg means to ignore subdir markings and act
-instead on ALL subdirs.  That is, mark all in this directory and all
-descendant directories.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-subdirs'."
-  (interactive (list current-prefix-arg diredp-list-file-attributes))
-  (diredp-mark-recursive-1 arg "symlinks" "symbolic link" '(diredp-looking-at-p dired-re-sym) details))
-
-(defun diredp-mark-recursive-1 (arg plural singular predicate-sexp details)
-  "Helper for `diredp-mark-*-recursive' commands."
-  (let* ((numarg             (and arg  (prefix-numeric-value arg)))
-         (unmark             (and numarg  (>= numarg 0)))
-         (ignorep            (and numarg  (<= numarg 0)))
-         (dired-marker-char  (if unmark ?\040 dired-marker-char))
-         (sdirs              (diredp-get-subdirs ignorep nil details))
-         (changed            0)
-         (matched            0)
-         dbufs chg.mtch)
-    (message "%s %s..." (if (eq ?\040 dired-marker-char) "UNmarking" "Marking") plural)
-    (dolist (dir  (cons default-directory sdirs))
-      (when (setq dbufs  (dired-buffers-for-dir (expand-file-name dir))) ; Dirs with Dired buffers only.
-        (with-current-buffer (car dbufs)
-          (setq chg.mtch  (diredp-mark-if (eval predicate-sexp) singular)
-                changed   (+ changed (or (car chg.mtch)  0))
-                matched   (+ matched (or (cdr chg.mtch)  0))))))
-    (message "%s %s%s%s newly %s"
-             matched
-             (if (= 1 matched) singular plural)
-             (if (not (= matched changed)) " matched, " "")
-             (if (not (= matched changed)) changed "")
-             (if (eq ?\040 dired-marker-char) "unmarked" "marked"))))
-
-;;;###autoload
-(defun diredp-capitalize-recursive (&optional ignore-marks-p details) ; Bound to `M-+ % c'
-  "Rename marked files, including in marked subdirs, by capitalizing them.
-Like `diredp-capitalize', but act recursively on subdirs.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-create-files-non-directory-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (diredp-create-files-non-directory-recursive
-   #'dired-rename-file #'capitalize "Rename by capitalizing:" ignore-marks-p details))
-
-;;;###autoload
-(defun diredp-upcase-recursive (&optional ignore-marks-p details) ; Bound to `M-+ % u'
-  "Rename marked files, including in marked subdirs, making them uppercase.
-Like `dired-upcase', but act recursively on subdirs.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-create-files-non-directory-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (diredp-create-files-non-directory-recursive
-   #'dired-rename-file #'upcase "Rename to uppercase:" ignore-marks-p details))
-
-;;;###autoload
-(defun diredp-downcase-recursive (&optional ignore-marks-p details) ; Bound to `M-+ % l'
-  "Rename marked files, including in marked subdirs, making them lowercase.
-Like `dired-downcase', but act recursively on subdirs.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-create-files-non-directory-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (diredp-create-files-non-directory-recursive
-   #'dired-rename-file #'downcase "Rename to lowercase:" ignore-marks-p details))
-
-;;;###autoload
-(defun diredp-do-apply-function-recursive (function &optional arg details) ; Bound to `M-+ @'
-  "Apply FUNCTION to the marked files.
-Like `diredp-do-apply-function' but act recursively on subdirs and do
-no result or error logging or echoing.
-
-The files acted on are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-With a plain prefix ARG (`C-u'), visit each file and invoke FUNCTION
- with no arguments.
-Otherwise, apply FUNCTION to each file name.
-
-Any other prefix arg behaves according to the ARG argument of
-`dired-get-marked-files'.  In particular, `C-u C-u' operates on all
-files in the Dired buffer.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-get-confirmation-recursive) 
-                      (list (read (completing-read "Function: " obarray 'functionp nil nil
-                                                   (and (boundp 'function-name-history)  'function-name-history)))
-                            current-prefix-arg
-                            diredp-list-file-attributes)))
-  (if (and (consp arg)  (< (car arg) 16))
-      (dolist (file  (diredp-get-files)) (with-current-buffer (find-file-noselect file) (funcall function)))
-    (dolist (file  (diredp-get-files arg nil nil nil nil details)) (funcall function file))))
-
-;;;###autoload
-(defun diredp-do-delete-recursive (arg &optional details) ; Bound to `M-+ D'
-  "Delete marked (not flagged) files, including in marked subdirs.
-Like `dired-do-delete' but act recursively on subdirs.
-
-The files to be deleted are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files' and `diredp-get-subdirs'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (unless arg
-    (ding)
-    (message "NOTE: Deletion of files marked `%c' (not those flagged `%c')."
-             dired-marker-char dired-del-marker))
-  (let* ((files     (diredp-get-files nil nil nil nil 'ONLY-MARKED-P details))
-         (count     (length files))
-         (trashing  (and (boundp 'delete-by-moving-to-trash)  delete-by-moving-to-trash))
-         (succ      0))
-    (if (dired-mark-pop-up
-         " *Deletions*" 'delete files dired-deletion-confirmer
-         (format "%s %s " (if trashing "Trash" "Delete") (dired-mark-prompt arg files)))
-        (let ((progress-reporter  (and (fboundp 'make-progress-reporter)
-                                       (make-progress-reporter (if trashing "Trashing..." "Deleting...")
-                                                               succ
-                                                               count)))
-              (failures           ()))
-          (unless progress-reporter (message "Deleting..."))
-          (dolist (file  files)
-            (condition-case err
-                (progn (if (fboundp 'dired-delete-file) ; Emacs 22+
-                           (dired-delete-file file dired-recursive-deletes trashing)
-                         ;; This test is equivalent to (and (file-directory-p file)  (not (file-symlink-p file)))
-                         ;; but more efficient.
-                         (if (eq t (car (file-attributes file))) (delete-directory file) (delete-file file)))
-                       (setq succ  (1+ succ))
-                       (when (fboundp 'progress-reporter-update)
-                         (progress-reporter-update progress-reporter succ)))
-              (error (dired-log "%s\n" err) ; Catch errors from failed deletions.
-                     (setq failures  (cons file failures))))
-            (dired-clean-up-after-deletion file))
-          (if failures
-              (dired-log-summary (format "%d of %d deletion%s failed"
-                                         (length failures) count (dired-plural-s count))
-                                 failures)
-            (if (fboundp 'progress-reporter-done)
-                (progress-reporter-done progress-reporter)
-              (message "Deleting...done")))
-          (let ((sdirs  (diredp-get-subdirs nil nil details))
-                dbufs)
-            (dolist (dir  (cons default-directory sdirs))
-              (when (setq dbufs  (dired-buffers-for-dir (expand-file-name dir))) ; Dirs with Dired buffers only.
-                (with-current-buffer (car dbufs) (dired-revert))))))
-      (message "OK. NO deletions performed"))))
-
-;;;###autoload
-(defun diredp-do-move-recursive (&optional ignore-marks-p details) ; Bound to `M-+ R'
-  "Move marked files, including in marked subdirs, to a given directory.
-Like `dired-do-rename', but act recursively on subdirs to pick up the
-files to move.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-This means move the marked files of marked subdirs and their marked
-subdirs, etc.  It does not mean move or rename the subdirs themselves
-recursively.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-Renames any buffers that are visiting the files.
-
-The default suggested for the target directory depends on the value of
-`dired-dwim-target', which see."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (diredp-do-create-files-recursive #'dired-rename-file "Move" ignore-marks-p details))
-
-;;;###autoload
-(defun diredp-do-copy-recursive (&optional ignore-marks-p details) ; Bound to `M-+ C'
-  "Copy marked files, including in marked subdirs, to a given directory.
-Like `dired-do-copy', but act recursively on subdirs to pick up the
-files to copy.
-
-The files included are those that are marked in the current Dired
-buffer, or all files in the directory if none are marked.  Marked
-subdirectories are handled recursively in the same way.
-
-This means copy the marked files of marked subdirs and their marked
-subdirs, etc.  It does not mean copy the subdirs themselves
-recursively.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-Preserves the last-modified date when copying, unless
-`dired-copy-preserve-time' is nil.
-
-The default suggested for the target directory depends on the value of
-`dired-dwim-target', which see.
-
-This command copies symbolic links by creating new ones, like UNIX
-command `cp -d'.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-do-create-files-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (let ((dired-recursive-copies  nil))  ; Doesn't have to be nil, but let's not go overboard now.
-    (diredp-do-create-files-recursive #'dired-copy-file "Copy" ignore-marks-p details)))
-
-(defun diredp-do-create-files-recursive (file-creator operation ignore-marks-p &optional details)
-  "Create a new file for each marked file, including those in marked subdirs.
-Like `dired-do-create-files', but act recursively on subdirs, and
-always keep markings.
-Prompts for the target directory, in which to create the files.
-FILE-CREATOR and OPERATION are as in `dired-create-files'.
-Non-nil IGNORE-MARKS-P means ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (lexical-let* ((fn-list     (diredp-get-files ignore-marks-p nil nil nil nil details))
-                 (target-dir  (dired-dwim-target-directory))
-                 (defaults    (and (fboundp 'dired-dwim-target-defaults) ; Emacs 23+
-                                   (dired-dwim-target-defaults fn-list target-dir)))
-                 (target      (expand-file-name
-                               (if (fboundp 'minibuffer-with-setup-hook) ; Emacs 22+
-                                   (minibuffer-with-setup-hook
-                                    (lambda ()
-                                      (set (make-local-variable 'minibuffer-default-add-function)
-                                           nil)
-                                      (setq minibuffer-default  defaults))
-                                    (funcall (if (fboundp 'read-directory-name)
-                                                 #'read-directory-name
-                                               #'read-file-name)
-                                             (concat operation " files to: ")
-                                             default-directory default-directory))
-                                 (funcall (if (fboundp 'read-directory-name)
-                                              #'read-directory-name
-                                            #'read-file-name)
-                                          (concat operation "files to: ")
-                                          default-directory default-directory)))))
-    (unless (file-directory-p target) (error "Target is not a directory: `%s'" target))
-    (dired-create-files
-     file-creator operation fn-list
-     #'(lambda (from) (expand-file-name (file-name-nondirectory from) target))
-     ;; Hard-code `*' marker, or else it will be removed in lower dirs because the code uses
-     ;; `dired-file-marker', which only works in the current Dired directory.
-     ?*)))
-
-(defun diredp-create-files-non-directory-recursive (file-creator basename-constructor operation
-                                                    &optional ignore-marks-p details)
-  "Apply FILE-CREATOR + BASENAME-CONSTRUCTOR to non-dir part of marked names.
-Like `dired-create-files-non-directory', but act recursively on subdirs.
-
-The files acted on are those marked in the current Dired buffer, or
-all files in the directory if none are marked.  Marked subdirectories
-are handled recursively in the same way.
-
-With non-nil IGNORE-MARKS-P, ignore all marks - include all files in
-this Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (let (rename-non-directory-query)
-    (dired-create-files
-     file-creator
-     operation
-     (diredp-get-files ignore-marks-p nil nil nil nil details)
-     #'(lambda (from)
-         (let ((to  (concat (file-name-directory from)
-                            (funcall basename-constructor (file-name-nondirectory from)))))
-           (and (let ((help-form  (format "\
-Type SPC or `y' to %s one file, DEL or `n' to skip to next,
-`!' to %s all remaining matches with no more questions."
-                                          (downcase operation)
-                                          (downcase operation))))
-                  (dired-query 'rename-non-directory-query (concat operation " `%s' to `%s'")
-                               (dired-make-relative from) (dired-make-relative to)))
-                to)))
-     ;; Hard-code `*' marker, or else it will be removed in lower dirs because the code uses
-     ;; `dired-file-marker', which only works in the current Dired directory.
-     ?*)))
-
-(defun diredp-do-chxxx-recursive (attribute-name program op-symbol &optional ignore-marks-p default details)
-  "Change attributes of the marked files, including those in marked subdirs.
-Refresh their file lines.
-
-Like `dired-do-chxxx', but act recursively on subdirs.  The subdirs
-acted on are those that are marked in the current Dired buffer, or all
-subdirs in the directory if none are marked.  Marked subdirectories
-are handled recursively in the same way.
-
-ATTRIBUTE-NAME is a string describing the attribute to the user.
-PROGRAM is the program used to change the attribute.
-OP-SYMBOL is the type of operation (for use in `dired-mark-pop-up').
-Non-nil IGNORE-MARKS-P means ignore all marks - include all files in this
- Dired buffer and all subdirs, recursively.
-DEFAULT is the default value for reading the mark string.
-DETAILS is passed to `diredp-get-files' and
- `diredp-do-redisplay-recursive'."
-  (let* ((this-buff      (current-buffer))
-         (files          (diredp-get-files ignore-marks-p nil nil nil nil details))
-         (prompt         (concat "Change " attribute-name " of %s to: "))
-         (new-attribute  (if (> emacs-major-version 22)
-                             (dired-mark-read-string prompt nil op-symbol ignore-marks-p files default)
-                           (dired-mark-read-string prompt nil op-symbol ignore-marks-p files)))
-         (operation      (concat program " " new-attribute))
-         failures)
-    (setq failures  (dired-bunch-files 10000 (function dired-check-process)
-                                       (append (list operation program)
-                                               (unless (string-equal new-attribute "")
-                                                 (if (equal attribute-name "Timestamp")
-                                                     (list "-t" new-attribute)
-                                                   (list new-attribute)))
-                                               (and (diredp-string-match-p "gnu" system-configuration)
-                                                    '("--"))) ; --------------------------------
-                                       files))
-    (with-current-buffer this-buff (diredp-do-redisplay-recursive details 'MSGP))
-    (when failures (dired-log-summary (format "%s: error" operation) nil))))
-
-;;;###autoload
-(defun diredp-do-chmod-recursive (&optional ignore-marks-p details) ; Bound to `M-+ M'
-  "Change the mode of the marked files, including those in marked subdirs.
-Symbolic modes like `g+w' are allowed.
-
-Note that marked subdirs are not changed.  Their markings are used only
-to indicate that some of their files are to be changed.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files' and `diredp-do-redisplay-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (let* ((files    (diredp-get-files ignore-marks-p nil nil nil nil details))
-         (modestr  (and (stringp (car files))  (nth 8 (file-attributes (car files)))))
-         (default  (and (stringp modestr)
-                        (string-match "^.\\(...\\)\\(...\\)\\(...\\)$" modestr)
-                        (replace-regexp-in-string "-" "" (format "u=%s,g=%s,o=%s"
-                                                                 (match-string 1 modestr)
-                                                                 (match-string 2 modestr)
-                                                                 (match-string 3 modestr)))))
-         (modes    (if (> emacs-major-version 22)
-                       (dired-mark-read-string
-                        "Change mode of marked files here and below to: " nil 'chmod
-                        nil files default)
-                     (dired-mark-read-string
-                      "Change mode of marked files here and below to: " nil 'chmod
-                      nil files))))
-    (when (equal modes "") (error "No file mode specified"))
-    (dolist (file  files)
-      (set-file-modes file (or (and (diredp-string-match-p "^[0-7]+" modes)  (string-to-number modes 8))
-                               (file-modes-symbolic-to-number modes (file-modes file)))))
-    (diredp-do-redisplay-recursive details 'MSGP)))
-
-(unless (memq system-type '(windows-nt ms-dos))
-  (defun diredp-do-chgrp-recursive (&optional ignore-marks-p details)
-    "Change the group of the marked (or next ARG) files.
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-do-chxxx-recursive'."
-    (interactive (list current-prefix-arg diredp-list-file-attributes))
-    (diredp-do-chxxx-recursive "Group" "chgrp" 'chgrp ignore-marks-p nil details)))
-
-(unless (memq system-type '(windows-nt ms-dos))
-  (defun diredp-do-chown-recursive (&optional ignore-marks-p details)
-    "Change the owner of the marked (or next ARG) files.
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-do-chxxx-recursive'."
-    (interactive (list current-prefix-arg diredp-list-file-attributes))
-    (diredp-do-chxxx-recursive "Owner" dired-chown-program 'chown ignore-marks-p nil details)))
-
-;;;###autoload
-(defun diredp-do-touch-recursive (&optional ignore-marks-p details)
-  "Change the timestamp of marked files, including those in marked subdirs.
-This calls `touch'.  Like `dired-do-touch', but act recursively on
-subdirs.  The subdirs inserted are those that are marked in the
-current Dired buffer, or all subdirs in the directory if none are
-marked.  Marked subdirectories are handled recursively in the same
-way.
-
-With a prefix argument, ignore all marks - include all files in this
-Dired buffer and all subdirs, recursively.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-do-chxxx-recursive'."
-  (interactive (progn (diredp-get-confirmation-recursive) (list current-prefix-arg diredp-list-file-attributes)))
-  (diredp-do-chxxx-recursive "Timestamp" (if (boundp 'dired-touch-program)
-                                             dired-touch-program ; Emacs 22+
-                                           "touch")
-                             'touch
-                             ignore-marks-p
-                             (format-time-string "%Y%m%d%H%M.%S" (current-time))
-                             details))
-
-;;;###autoload
-(defun diredp-do-redisplay-recursive (&optional details msgp)
-  "Redisplay marked file lines, including those in marked subdirs.
-Non-nil MSGP means show status messages.
-Like `dired-do-redisplay' with no args, but act recursively on
-subdirs.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (progn (diredp-ensure-mode)
-                      (unless (y-or-n-p "Act on all marked file lines in and UNDER this dir? ")
-                        (error "OK, canceled"))
-                      (list diredp-list-file-attributes t)))
-  (when msgp (message "Redisplaying..."))
-  (dolist (dir  (cons default-directory
-                      (diredp-get-files nil #'file-directory-p 'INCLUDE-SUBDIRS 'DONT-ASK nil details)))
-    (with-current-buffer (dired-noselect dir)
-      ;; `message' is much faster than making `dired-map-over-marks' show progress
-      (dired-uncache (if (consp dired-directory) (car dired-directory) dired-directory))
-      (dired-map-over-marks
-       (let ((fname                    (dired-get-filename))
-             ;; Postpone readin hook till we map over all marked files (Bug#6810).
-             (dired-after-readin-hook  nil))
-         (message "Redisplaying... %s" fname)
-         (dired-update-file-line fname))
-       nil)
-      (run-hooks 'dired-after-readin-hook)
-      (dired-move-to-filename)))
-  (when msgp (message "Redisplaying...done")))
-
-
-;;; `diredp-marked(-other-window)' tries to treat SWITCHES, but SWITCHES seems to be ignored
-;;; by `dired' when the DIRNAME arg is a cons, at least on MS Windows.  I filed Emacs bug #952
-;;; on 2008-09-10, but this doesn't work in Emacs 20, 21, 22, or 23, so I don't know if it will
-;;; ever be fixed.  If it is declared a non-bug and it doesn't work on any platforms, then I'll
-;;; remove SWITCHES here, alas.
-
-;;;###autoload
-(defun diredp-marked (dirname &optional n switches) ; Not bound
-  "Open Dired on only the marked files or the next N files.
-With a non-zero numeric prefix arg N, use the next abs(N) files.
-A plain (`C-u'), zero, or negative prefix arg prompts for listing
-switches as in command `dired'.
-
-Note that the marked files can include files in inserted
-subdirectories, so the Dired buffer that is opened can contain files
-from multiple directories in the same tree."
-  (interactive (progn (diredp-ensure-mode)
-                      (let ((num  (and current-prefix-arg
-                                       (atom current-prefix-arg)
-                                       (not (zerop (prefix-numeric-value current-prefix-arg)))
-                                       (abs (prefix-numeric-value current-prefix-arg)))))
-                        (list (cons (generate-new-buffer-name (buffer-name)) (dired-get-marked-files t num))
-                              num
-                              (and current-prefix-arg ; Switches
-                                   (or (consp current-prefix-arg)
-                                       (< (prefix-numeric-value current-prefix-arg) 0))
-                                   (read-string "Dired listing switches: " dired-listing-switches))))))
-  (unless (or n  (save-excursion (goto-char (point-min))
-                                 (and (re-search-forward (dired-marker-regexp) nil t)
-                                      (re-search-forward (dired-marker-regexp) nil t))))
-    (error "No marked files"))
-  (dired dirname switches))
-
-;;;###autoload
-(defun diredp-marked-other-window (dirname &optional n switches) ; Bound to `C-M-*'
-  "Same as `diredp-marked', but uses a different window."
-  (interactive (progn (diredp-ensure-mode)
-                      (let ((num  (and current-prefix-arg
-                                       (atom current-prefix-arg)
-                                       (not (zerop (prefix-numeric-value current-prefix-arg)))
-                                       (abs (prefix-numeric-value current-prefix-arg)))))
-                        (list (cons (generate-new-buffer-name (buffer-name)) (dired-get-marked-files t num))
-                              num
-                              (and current-prefix-arg ; Switches
-                                   (or (consp current-prefix-arg)
-                                       (< (prefix-numeric-value current-prefix-arg) 0))
-                                   (read-string "Dired listing switches: " dired-listing-switches))))))
-  (unless (or n  (save-excursion (goto-char (point-min))
-                                 (and (re-search-forward (dired-marker-regexp) nil t)
-                                      (re-search-forward (dired-marker-regexp) nil t))))
-    (error "No marked files"))
-  (dired-other-window dirname switches))
-
-
-;; Similar to `dired-mark-extension' in `dired-x.el'.
-;; The difference is that this uses prefix arg to unmark, not to determine the mark character.
-;;;###autoload
-(defun diredp-mark/unmark-extension (extension &optional unmark-p) ; Bound to `* .'
-  "Mark all files with a certain EXTENSION for use in later commands.
-A `.' is not automatically prepended to the string entered.
-Non-nil prefix argument UNMARK-P means unmark instead of mark.
-
-Non-interactively, EXTENSION is the extension (a string).  It can also
-  be a list of extension strings.
-Optional argument UNMARK-P is the prefix arg."
-  (interactive (list (diredp-read-regexp (concat (if current-prefix-arg "UNmark" "Mark") "ing extension: "))
-                     current-prefix-arg))
-  (or (listp extension)  (setq extension  (list extension)))
-  (dired-mark-files-regexp (concat ".";; Do not match names with nothing but an extension
-                                   "\\("
-                                   (mapconcat #'regexp-quote extension "\\|")
-                                   "\\)$")
-                           (and current-prefix-arg  ?\040)))
-
-(defun diredp-mark-files-tagged-all/none (tags &optional none-p unmarkp prefix)
-  "Mark or unmark files tagged with all or none of TAGS.
-TAGS is a list of strings, the tag names.
-NONEP non-nil means mark/unmark files that have none of the TAGS.
-UNMARKP non-nil means unmark; nil means mark.
-PREFIX non-nil is the prefix of the autofile bookmarks to check.
-
-As a special case, if TAGS is empty, then mark or unmark the files
-that have any tags at all, or if NONEP is non-nil then mark or unmark
-those that have no tags at all."
-  (let ((dired-marker-char  (if unmarkp ?\040 dired-marker-char)))
-    (diredp-mark-if (and (not (diredp-looking-at-p dired-re-dot))  (not (eolp))
-                         (let* ((fname     (dired-get-filename nil t))
-                                (bmk       (and fname  (bmkp-get-autofile-bookmark fname nil prefix)))
-                                (btgs      (and bmk  (bmkp-get-tags bmk)))
-                                (presentp  nil)
-                                (allp      (and btgs  (catch 'diredp-m-f-t-an
-                                                        (dolist (tag  tags)
-                                                          (setq presentp  (assoc-default tag btgs nil t))
-                                                          (unless (if none-p (not presentp) presentp)
-                                                            (throw 'diredp-m-f-t-an nil)))
-                                                        t))))
-                           (if (null tags)
-                               (if none-p (not btgs) btgs)
-                             allp)))
-                    (if none-p "no-tags-matching file" "all-tags-matching file"))))
-
-(defun diredp-mark-files-tagged-some/not-all (tags &optional notallp unmarkp prefix)
-  "Mark or unmark files tagged with any or not all of TAGS.
-TAGS is a list of strings, the tag names.
-NOTALLP non-nil means mark/unmark files that do not have all TAGS.
-UNMARKP non-nil means unmark; nil means mark.
-PREFIX non-nil is the prefix of the autofile bookmarks to check.
-
-As a special case, if TAGS is empty, then mark or unmark the files
-that have any tags at all, or if NOTALLP is non-nil then mark or
-unmark those that have no tags at all."
-  (let ((dired-marker-char  (if unmarkp ?\040 dired-marker-char)))
-    (diredp-mark-if (and (not (diredp-looking-at-p dired-re-dot))  (not (eolp))
-                         (let* ((fname     (dired-get-filename nil t))
-                                (bmk       (and fname
-                                                (bmkp-get-autofile-bookmark fname nil prefix)))
-                                (btgs      (and bmk  (bmkp-get-tags bmk)))
-                                (presentp  nil)
-                                (allp      (and btgs  (catch 'diredp-m-f-t-sna
-                                                        (dolist (tag  tags)
-                                                          (setq presentp  (assoc-default tag btgs nil t))
-                                                          (when (if notallp (not presentp) presentp)
-                                                            (throw 'diredp-m-f-t-sna t)))
-                                                        nil))))
-                           (if (null tags) (if notallp (not btgs) btgs) allp)))
-                    (if notallp "some-tags-not-matching file" "some-tags-matching file"))))
-
-;;;###autoload
-(defun diredp-mark-files-tagged-all (tags &optional none-p prefix) ; `T m *'
-  "Mark all files that are tagged with *each* tag in TAGS.
-As a special case, if TAGS is empty, then mark the files that have
- any tags at all (i.e., at least one tag).
-With a prefix arg, mark all that are *not* tagged with *any* TAGS.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (and (fboundp 'bmkp-read-tags-completing)  (bmkp-read-tags-completing))
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-files-tagged-all/none tags none-p nil prefix))
-
-;;;###autoload
-(defun diredp-mark-files-tagged-none (tags &optional allp prefix) ; `T m ~ +'
-  "Mark all files that are not tagged with *any* tag in TAGS.
-As a special case, if TAGS is empty, then mark the files that have
- no tags at all.
-With a prefix arg, mark all that are tagged with *each* tag in TAGS.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (and (fboundp 'bmkp-read-tags-completing)  (bmkp-read-tags-completing))
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-files-tagged-all/none tags (not allp) nil prefix))
-
-;;;###autoload
-(defun diredp-mark-files-tagged-some (tags &optional somenotp prefix) ; `T m +'
-  "Mark all files that are tagged with *some* tag in TAGS.
-As a special case, if TAGS is empty, then mark the files that have
- any tags at all (i.e., at least one tag).
-With a prefix arg, mark all that are *not* tagged with *all* TAGS.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (and (fboundp 'bmkp-read-tags-completing)  (bmkp-read-tags-completing))
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-files-tagged-some/not-all tags somenotp nil prefix))
-
-;;;###autoload
-(defun diredp-mark-files-tagged-not-all (tags &optional somep prefix) ; `T m ~ *'
-  "Mark all files that are not tagged with *all* TAGS.
-As a special case, if TAGS is empty, then mark the files that have
- no tags at all.
-With a prefix arg, mark all that are tagged with *some* TAGS.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (and (fboundp 'bmkp-read-tags-completing)  (bmkp-read-tags-completing))
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-files-tagged-some/not-all tags (not somep) nil prefix))
-
-;;;###autoload
-(defun diredp-mark-files-tagged-regexp (regexp &optional notp prefix) ; `T m %'
-  "Mark files that have at least one tag that matches REGEXP.
-With a prefix arg, mark all that are tagged but have no matching tags.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (read-string "Regexp: ")
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-if (and (not (diredp-looking-at-p dired-re-dot))  (not (eolp))
-                       (lexical-let* ((fname  (dired-get-filename nil t))
-                                      (bmk    (and fname
-                                                   (bmkp-get-autofile-bookmark fname nil prefix)))
-                                      (btgs   (and bmk  (bmkp-get-tags bmk)))
-                                      (anyp   (and btgs  (bmkp-some #'(lambda (tag)
-                                                                        (diredp-string-match-p
-                                                                         regexp
-                                                                         (bmkp-tag-name tag)))
-                                                                    btgs))))
-                         (and btgs  (if notp (not anyp) anyp))))
-                  "some-tag-matching-regexp file"))
-
-;;;###autoload
-(defun diredp-unmark-files-tagged-regexp (regexp &optional notp prefix) ; `T u %'
-  "Unmark files that have at least one tag that matches REGEXP.
-With a prefix arg, unmark all that are tagged but have no matching tags.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (read-string "Regexp: ")
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (let ((dired-marker-char  ?\040))
-    (diredp-mark-if (and (not (diredp-looking-at-p dired-re-dot))  (not (eolp))
-                         (lexical-let* ((fname  (dired-get-filename nil t))
-                                        (bmk    (and fname  (bmkp-get-autofile-bookmark fname nil prefix)))
-                                        (btgs   (and bmk  (bmkp-get-tags bmk)))
-                                        (anyp   (and btgs (bmkp-some #'(lambda (tag)
-                                                                         (diredp-string-match-p
-                                                                          regexp
-                                                                          (bmkp-tag-name tag)))
-                                                                     btgs))))
-                           (and btgs  (if notp (not anyp) anyp))))
-                    "some-tag-matching-regexp file")))
-
-;;;###autoload
-(defun diredp-unmark-files-tagged-all (tags &optional none-p prefix) ; `T u *'
-  "Unmark all files that are tagged with *each* tag in TAGS.
-As a special case, if TAGS is empty, then unmark the files that have
- any tags at all (i.e., at least one tag).
-With a prefix arg, unmark all that are *not* tagged with *any* TAGS.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (and (fboundp 'bmkp-read-tags-completing)  (bmkp-read-tags-completing))
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-files-tagged-all/none tags none-p 'UNMARK prefix))
-
-;;;###autoload
-(defun diredp-unmark-files-tagged-none (tags &optional allp prefix) ; `T u ~ +'
-  "Unmark all files that are *not* tagged with *any* tag in TAGS.
-As a special case, if TAGS is empty, then unmark the files that have
- no tags at all.
-With a prefix arg, unmark all that are tagged with *each* tag in TAGS.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (and (fboundp 'bmkp-read-tags-completing)  (bmkp-read-tags-completing))
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-files-tagged-all/none tags (not allp) 'UNMARK prefix))
-
-;;;###autoload
-(defun diredp-unmark-files-tagged-some (tags &optional somenotp prefix) ; `T u +'
-  "Unmark all files that are tagged with *some* tag in TAGS.
-As a special case, if TAGS is empty, then unmark the files that have
- any tags at all.
-With a prefix arg, unmark all that are *not* tagged with *all* TAGS.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (and (fboundp 'bmkp-read-tags-completing)  (bmkp-read-tags-completing))
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-files-tagged-some/not-all tags somenotp 'UNMARK prefix))
-
-;;;###autoload
-(defun diredp-unmark-files-tagged-not-all (tags &optional somep prefix) ; `T u ~ *'
-  "Unmark all files that are *not* tagged with *all* TAGS.
-As a special case, if TAGS is empty, then unmark the files that have
- no tags at all.
-With a prefix arg, unmark all that are tagged with *some* TAGS.
-You need library `bookmark+.el' to use this command."
-  (interactive (list (and (fboundp 'bmkp-read-tags-completing)  (bmkp-read-tags-completing))
-                     current-prefix-arg
-                     (and diredp-prompt-for-bookmark-prefix-flag
-                          (read-string "Prefix for autofile bookmark names: "))))
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark-files-tagged-some/not-all tags (not somep) 'UNMARK prefix))
-
-;;;###autoload
-(defun diredp-do-tag (tags &optional prefix arg) ; `T > +'
-  "Tag the marked (or the next prefix argument) files.
-You need library `bookmark+.el' to use this command.
-
-Hit `RET' to enter each tag, then hit `RET' again after the last tag.
-You can use completion to enter each tag.  Completion is lax: you are
-not limited to existing tags.
-
-TAGS is a list of strings.  PREFIX is as for `diredp-do-bookmark'.
-
-A prefix argument ARG specifies files to use instead of those marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (bmkp-read-tags-completing)
-                            (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for autofile bookmark name: "))
-                            current-prefix-arg)))
-  (dired-map-over-marks-check (lexical-let ((pref  prefix)) #'(lambda () (diredp-tag tags pref)))
-                              arg 'tag (diredp-fewer-than-2-files-p arg)))
-
-(defun diredp-tag (tags &optional prefix)
-  "Add tags to the file or directory named on the current line.
-You need library `bookmark+.el' to use this function.
-The bookmark name is the non-directory portion of the file name,
- prefixed by PREFIX if it is non-nil.
-Return nil for success, file name otherwise."
-  (bookmark-maybe-load-default-file)
-  (let ((file  (dired-get-file-for-visit))
-        failure)
-    (condition-case err
-        (bmkp-autofile-add-tags file tags nil prefix)
-      (error (setq failure  (error-message-string err))))
-    (if (not failure)
-        nil                             ; Return nil for success.
-      (dired-log failure)
-      (dired-make-relative file))))     ; Return file name for failure.
-
-;;;###autoload
-(defun diredp-mouse-do-tag (event)      ; Not bound
-  "In Dired, add some tags to this file.
-You need library `bookmark+.el' to use this command."
-  (interactive "e")
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (lexical-let ((mouse-pos         (event-start event))
-                (dired-no-confirm  t)
-                (prefix            (and diredp-prompt-for-bookmark-prefix-flag
-                                        (read-string "Prefix for bookmark name: "))))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-map-over-marks-check #'(lambda () (diredp-tag (bmkp-read-tags-completing) prefix))
-                                1 'tag t))
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-do-untag (tags &optional prefix arg) ; `T > -'
-  "Remove some tags from the marked (or the next prefix arg) files.
-You need library `bookmark+.el' to use this command.
-
-Hit `RET' to enter each tag, then hit `RET' again after the last tag.
-You can use completion to enter each tag.  Completion is lax: you are
-not limited to existing tags.
-
-TAGS is a list of strings.  PREFIX is as for `diredp-do-bookmark'.
-
-A prefix argument ARG specifies files to use instead of those marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (bmkp-read-tags-completing)
-                            (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            current-prefix-arg)))
-  (dired-map-over-marks-check (lexical-let ((pref  prefix))
-                                #'(lambda () (diredp-untag tags pref)))
-                              arg 'untag (diredp-fewer-than-2-files-p arg)))
-
-(defun diredp-untag (tags &optional prefix)
-  "Remove some tags from the file or directory named on the current line.
-You need library `bookmark+.el' to use this function.
-The bookmark name is the non-directory portion of the file name,
- prefixed by PREFIX if it is non-nil.
-Return nil for success, file name otherwise."
-  (bookmark-maybe-load-default-file)
-  (let ((file  (dired-get-file-for-visit))
-        failure)
-    (condition-case err
-        (bmkp-autofile-remove-tags file tags nil prefix)
-      (error (setq failure  (error-message-string err))))
-    (if (not failure)
-        nil                             ; Return nil for success.
-      (dired-log failure)
-      (dired-make-relative file))))     ; Return file name for failure.
-
-;;;###autoload
-(defun diredp-mouse-do-untag (event)    ; Not bound
-  "In Dired, remove some tags from this file.
-You need library `bookmark+.el' to use this command."
-  (interactive "e")
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (lexical-let ((mouse-pos         (event-start event))
-                (dired-no-confirm  t)
-                (prefix            (and diredp-prompt-for-bookmark-prefix-flag
-                                        (read-string "Prefix for bookmark name: "))))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (lexical-let* ((bmk   (bmkp-get-autofile-bookmark  (dired-get-filename) nil prefix))
-                   (btgs  (and bmk  (bmkp-get-tags bmk))))
-      (unless btgs (error "File has no tags to remove"))
-      (dired-map-over-marks-check
-       #'(lambda () (diredp-untag (bmkp-read-tags-completing btgs) prefix)) 1 'untag t)))
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-do-remove-all-tags (&optional prefix arg) ; `T > 0'
-  "Remove all tags from the marked (or the next prefix arg) files.
-You need library `bookmark+.el' to use this command.
-
-PREFIX is as for `diredp-do-bookmark'.
-
-A prefix argument ARG specifies files to use instead of those marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            current-prefix-arg)))
-  (lexical-let ((pref  prefix))
-    (dired-map-over-marks-check #'(lambda () (diredp-remove-all-tags pref)) arg 'remove-all-tags
-                                (diredp-fewer-than-2-files-p arg))))
-
-(defun diredp-remove-all-tags (&optional prefix)
-  "Remove all tags from the file or directory named on the current line.
-You need library `bookmark+.el' to use this function.
-The bookmark name is the non-directory portion of the file name,
- prefixed by PREFIX if it is non-nil.
-Return nil for success, file name otherwise."
-  (bookmark-maybe-load-default-file)
-  (let ((file  (dired-get-file-for-visit))
-        failure)
-    (condition-case err
-        (bmkp-remove-all-tags (bmkp-autofile-set file nil prefix))
-      (error (setq failure  (error-message-string err))))
-    (if (not failure)
-        nil                             ; Return nil for success.
-      (dired-log failure)
-      (dired-make-relative file))))     ; Return file name for failure.
-
-;;;###autoload
-(defun diredp-mouse-do-remove-all-tags (event) ; Not bound
-  "In Dired, remove all tags from the marked (or next prefix arg) files.
-You need library `bookmark+.el' to use this command."
-  (interactive "e")
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (lexical-let ((mouse-pos         (event-start event))
-                (dired-no-confirm  t)
-                (prefix            (and diredp-prompt-for-bookmark-prefix-flag
-                                        (read-string "Prefix for bookmark name: "))))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-map-over-marks-check #'(lambda () (diredp-remove-all-tags prefix))
-                                1 'remove-all-tags t))
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-do-paste-add-tags (&optional prefix arg) ; `T > p', `T > C-y'
-  "Add previously copied tags to the marked (or next prefix arg) files.
-The tags were previously copied from a file to `bmkp-copied-tags'.
-You need library `bookmark+.el' to use this command.
-
-A prefix argument ARG specifies files to use instead of those marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for autofile bookmark name: "))
-                            current-prefix-arg)))
-  (dired-map-over-marks-check (lexical-let ((pref  prefix))
-                                #'(lambda () (diredp-paste-add-tags pref)))
-                              arg 'paste-add-tags
-                              (diredp-fewer-than-2-files-p arg)))
-
-(defun diredp-paste-add-tags (&optional prefix)
-  "Add previously copied tags to the file or directory on the current line.
-The tags were previously copied from a file to `bmkp-copied-tags'.
-You need library `bookmark+.el' to use this function.
-The bookmark name is the non-directory portion of the file name,
- prefixed by PREFIX if it is non-nil.
-Return nil for success, file name otherwise."
-  (bookmark-maybe-load-default-file)
-  (let ((file  (dired-get-file-for-visit))
-        failure)
-    (condition-case err
-        (bmkp-autofile-add-tags file bmkp-copied-tags nil prefix)
-      (error (setq failure  (error-message-string err))))
-    (if (not failure)
-        nil                             ; Return nil for success.
-      (dired-log failure)
-      (dired-make-relative file))))     ; Return file name for failure.
-
-;;;###autoload
-(defun diredp-mouse-do-paste-add-tags (event) ; Not bound
-  "In Dired, add previously copied tags to this file.
-The tags were previously copied from a file to `bmkp-copied-tags'.
-You need library `bookmark+.el' to use this command."
-  (interactive "e")
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (lexical-let ((mouse-pos         (event-start event))
-                (dired-no-confirm  t)
-                (prefix            (and diredp-prompt-for-bookmark-prefix-flag
-                                        (read-string "Prefix for bookmark name: "))))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-map-over-marks-check #'(lambda () (diredp-paste-add-tags prefix))
-                                1 'paste-add-tags t))
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-do-paste-replace-tags (&optional prefix arg) ; `T > q'
-  "Replace tags for marked (or next prefix arg) files with copied tags.
-The tags were previously copied from a file to `bmkp-copied-tags'.
-You need library `bookmark+.el' to use this command.
-
-A prefix argument ARG specifies files to use instead of those marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for autofile bookmark name: "))
-                            current-prefix-arg)))
-  (dired-map-over-marks-check (lexical-let ((pref  prefix))
-                                #'(lambda () (diredp-paste-replace-tags pref)))
-                              arg 'paste-replace-tags (diredp-fewer-than-2-files-p arg)))
-
-(defun diredp-paste-replace-tags (&optional prefix)
-  "Replace tags for this file or dir with tags copied previously.
-The tags were previously copied from a file to `bmkp-copied-tags'.
-You need library `bookmark+.el' to use this function.
-The bookmark name is the non-directory portion of the file name,
- prefixed by PREFIX if it is non-nil.
-Return nil for success, file name otherwise."
-  (bookmark-maybe-load-default-file)
-  (let ((file  (dired-get-file-for-visit))
-        failure)
-    (condition-case err
-        (progn (bmkp-remove-all-tags (bmkp-autofile-set file nil prefix))
-               (bmkp-autofile-add-tags file bmkp-copied-tags nil prefix))
-      (error (setq failure  (error-message-string err))))
-    (if (not failure)
-        nil                             ; Return nil for success.
-      (dired-log failure)
-      (dired-make-relative file))))
-
-;;;###autoload
-(defun diredp-mouse-do-paste-replace-tags (event) ; Not bound
-  "In Dired, replace tags for this file with tags copied previously.
-The tags were previously copied from a file to `bmkp-copied-tags'.
-You need library `bookmark+.el' to use this command."
-  (interactive "e")
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (lexical-let ((mouse-pos         (event-start event))
-                (dired-no-confirm  t)
-                (prefix            (and diredp-prompt-for-bookmark-prefix-flag
-                                        (read-string "Prefix for bookmark name: "))))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-map-over-marks-check #'(lambda () (diredp-paste-replace-tags prefix))
-                                1 'paste-replace-tags t))
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-do-set-tag-value (tag value &optional prefix arg) ; `T > v'
-  "Set TAG value to VALUE, for the marked (or next prefix arg) files.
-This does not change the TAG name.
-You need library `bookmark+.el' to use this command.
-
-PREFIX is as for `diredp-do-bookmark'.
-
-A prefix argument ARG specifies files to use instead of those marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (bmkp-read-tag-completing)
-                            (read (read-string "Value: "))
-                            (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            current-prefix-arg)))
-  (dired-map-over-marks-check (lexical-let ((tg    tag)
-                                            (val   value)
-                                            (pref  prefix))
-                                #'(lambda () (diredp-set-tag-value tg val pref)))
-                              arg 'set-tag-value (diredp-fewer-than-2-files-p arg)))
-
-(defun diredp-set-tag-value (tag value &optional prefix)
-  "Set TAG value to VALUE for this file or directory.
-This does not change the TAG name.
-You need library `bookmark+.el' to use this function.
-The bookmark name is the non-directory portion of the file name,
- prefixed by PREFIX if it is non-nil.
-Return nil for success, file name otherwise."
-  (bookmark-maybe-load-default-file)
-  (let ((file  (dired-get-file-for-visit))
-        failure)
-    (condition-case err
-        (bmkp-set-tag-value (bmkp-autofile-set file nil prefix) tag value)
-      (error (setq failure  (error-message-string err))))
-    (if (not failure)
-        nil                             ; Return nil for success.
-      (dired-log failure)
-      (dired-make-relative file))))     ; Return file name for failure.
-
-;;;###autoload
-(defun diredp-mouse-do-set-tag-value (event) ; Not bound
-  "In Dired, set the value of a tag for this file.
-This does not change the tag name.
-You need library `bookmark+.el' to use this command."
-  (interactive "e")
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (lexical-let ((mouse-pos         (event-start event))
-                (dired-no-confirm  t)
-                (prefix            (and diredp-prompt-for-bookmark-prefix-flag
-                                        (read-string "Prefix for bookmark name: "))))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-map-over-marks-check #'(lambda () (diredp-set-tag-value (bmkp-read-tag-completing)
-                                                                   (read (read-string "Value: "))
-                                                                   prefix))
-                                1 'set-tag-value t))
-  (diredp-previous-line 1))
-
-
-;; Define these even if `Bookmark+' is not loaded.
-;;;###autoload
-(defun diredp-mark-autofiles ()         ; Bound to `* B'
-  "Mark all autofiles, that is, files that have an autofile bookmark."
-  (interactive)
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark/unmark-autofiles))
-
-;;;###autoload
-(defun diredp-unmark-autofiles ()
-  "Unmark all autofiles, that is, files that have an autofile bookmark."
-  (interactive)
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (diredp-mark/unmark-autofiles t))
-
-;;;###autoload
-(defun diredp-mark/unmark-autofiles (&optional unmarkp)
-  "Mark all autofiles, or unmark if UNMARKP is non-nil."
-  (let ((dired-marker-char  (if unmarkp ?\040 dired-marker-char)))
-    (diredp-mark-if (and (not (diredp-looking-at-p dired-re-dot))  (not (eolp))
-                         (let ((fname  (dired-get-filename nil t)))
-                           (and fname  (bmkp-get-autofile-bookmark fname))))
-                    "autofile")))
-
-(when (and (fboundp 'bmkp-get-autofile-bookmark) ; Defined in `bookmark+-1.el'.
-           (fboundp 'hlt-highlight-region)) ; Defined in `highlight.el'.
-
-  (defun diredp-highlight-autofiles ()
-    "Highlight files that are autofile bookmarks.
-Highlighting uses face `diredp-autofile-name'."
-    (save-excursion
-      (goto-char (point-min))
-      (while (re-search-forward dired-move-to-filename-regexp nil t)
-        ;; If Dired details are hidden the match data gets changed.
-        (let* ((bmk    (save-match-data
-                         (bmkp-get-autofile-bookmark (buffer-substring (match-end 0) (line-end-position)))))
-               (tags  (and bmk  (bmkp-get-tags bmk))))
-          (when bmk
-            (hlt-highlight-region (match-end 0) (line-end-position)
-                                  (if tags
-                                      'diredp-tagged-autofile-name
-                                    'diredp-autofile-name)))))))
-
-  (cond ((fboundp 'define-minor-mode)
-         ;; Emacs 21+.  Use `eval' so that even if the library is byte-compiled with Emacs 20,
-         ;; loading it into Emacs 21+ will define variable `diredp-highlight-autofiles-mode'.
-         (eval '(define-minor-mode diredp-highlight-autofiles-mode
-                 "Toggle automatic highlighting of autofile bookmarks.
-When you turn this on, it ensures that your bookmark file is loaded.
-
-NOTE: This mode is ON BY DEFAULT.  More precisely, when `dired+.el' is
-loaded (for the first time per Emacs session), the mode is turned ON.
-To prevent this and have the mode OFF by default, you must do one of
-the following:
-
- * Put (diredp-highlight-autofiles-mode -1) in your init file, AFTER
-   it loads `dired+.el'.
-
- * Customize option `diredp-highlight-autofiles-mode' to `nil', AND
-   ensure that your `custom-file' (or the `custom-saved-variables'
-   part of your init file) is evaluated before `dired+.el' is loaded.
-
-You need libraries `Bookmark and `highlight.el' for this command."
-                 :init-value t :global t :group 'Dired-Plus :require 'dired+
-                 (if (not diredp-highlight-autofiles-mode)
-                     (remove-hook 'dired-after-readin-hook #'diredp-highlight-autofiles)
-                   (add-hook 'dired-after-readin-hook #'diredp-highlight-autofiles)
-                   (bookmark-maybe-load-default-file))
-                 (when (derived-mode-p 'dired-mode) (dired-revert nil nil))
-                 (when (interactive-p)
-                   (message "Dired highlighting of autofile bookmarks is now %s"
-                            (if diredp-highlight-autofiles-mode "ON" "OFF"))))))
-        (t;; Emacs 20.
-         (defun diredp-highlight-autofiles-mode (&optional arg)
-           "Toggle automatic highlighting of autofile bookmarks.
-When you turn this on, it ensures that your bookmark file is loaded.
-
-NOTE: This mode is ON BY DEFAULT.  More precisely, when `dired+.el' is
-loaded (for the first time per Emacs session), the mode is turned ON.
-To prevent this and have the mode OFF by default, you must do one of
-the following:
-
- * Put (diredp-highlight-autofiles-mode -1) in your init file, AFTER
-   it loads `dired+.el'.
-
- * Customize option `diredp-highlight-autofiles-mode' to `nil', AND
-   ensure that your `custom-file' (or the `custom-saved-variables'
-   part of your init file) is evaluated before `dired+.el' is loaded.
-
-You need libraries `Bookmark and `highlight.el' for this command."
-           (interactive (list (or current-prefix-arg  'toggle)))
-           (setq diredp-highlight-autofiles-mode  (if (eq arg 'toggle)
-                                                      (not diredp-highlight-autofiles-mode)
-                                                    (> (prefix-numeric-value arg) 0)))
-           (if (not diredp-highlight-autofiles-mode)
-               (remove-hook 'dired-after-readin-hook #'diredp-highlight-autofiles)
-             (add-hook 'dired-after-readin-hook #'diredp-highlight-autofiles)
-             (bookmark-maybe-load-default-file))
-           (when (derived-mode-p 'dired-mode) (dired-revert nil nil))
-           (when (interactive-p) (message "Dired highlighting of autofile bookmarks is now %s"
-                                          (if diredp-highlight-autofiles-mode "ON" "OFF"))))))
-
-  ;; Turn it ON BY DEFAULT.
-  (unless (or (boundp 'diredp-loaded-p)  (get 'diredp-highlight-autofiles-mode 'saved-value))
-    (diredp-highlight-autofiles-mode 1))
-  )
-
-;;;###autoload
-(defun diredp-do-bookmark (&optional prefix arg) ; Bound to `M-b'
-  "Bookmark the marked (or the next prefix argument) files.
-Each bookmark name is the non-directory portion of the file name,
- prefixed by PREFIX if it is non-nil.
-Interactively, you are prompted for the PREFIX if
- `diredp-prompt-for-bookmark-prefix-flag' is non-nil.
-The bookmarked position is the beginning of the file.
-If you use library `bookmark+.el' then the bookmark is an autofile.
-
-A prefix argument ARG specifies files to use instead of those marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories."
-  (interactive (progn (diredp-ensure-mode)
-                      (list (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            current-prefix-arg)))
-  (dired-map-over-marks-check (lexical-let ((pref  prefix))
-                                #'(lambda () (diredp-bookmark pref nil 'NO-MSG-P)))
-                              arg 'bookmark (diredp-fewer-than-2-files-p arg)))
-
-;;;###autoload
-(defun diredp-mouse-do-bookmark (event) ; Not bound
-  "In Dired, bookmark this file.  See `diredp-do-bookmark'."
-  (interactive "e")
-  (lexical-let ((mouse-pos         (event-start event))
-                (dired-no-confirm  t)
-                (prefix            (and diredp-prompt-for-bookmark-prefix-flag
-                                        (read-string "Prefix for bookmark name: "))))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-map-over-marks-check #'(lambda () (diredp-bookmark prefix nil)) nil 'bookmark t))
-  (diredp-previous-line 1))
-
-(defun diredp-bookmark (&optional prefix file no-msg-p)
-  "Bookmark the file or directory FILE.
-If you use library `bookmark+.el' then the bookmark is an autofile.
-Return nil for success or the file name otherwise.
-
-The bookmark name is the (non-directory) file name, prefixed by
- optional arg PREFIX (a string) if non-nil.
-
-FILE defaults to the file name on the current Dired line.
-
-Non-nil optional arg NO-MSG-P means do not show progress messages."
-  (bookmark-maybe-load-default-file)
-  (let ((fil      (or file  (dired-get-file-for-visit)))
-        (failure  nil))
-    (condition-case err
-        (if (fboundp 'bmkp-autofile-set) ; Bookmark+ - just set an autofile bookmark.
-            (bmkp-autofile-set fil nil prefix nil (not no-msg-p))
-          ;; Vanilla `bookmark.el' (or very old Bookmark+ version).
-          (let ((bookmark-make-record-function
-                 (cond ((and (require 'image nil t)  (require 'image-mode nil t)
-                             (condition-case nil (image-type fil) (error nil)))
-                        ;; Last two lines of function are from `image-bookmark-make-record'.
-                        ;; But don't use that directly, because it uses
-                        ;; `bookmark-make-record-default', which gets nil for `filename'.
-                        (lambda ()
-                          `((filename   . ,fil)
-                            (position   . 0)
-                            ;; NEED to keep this part of code sync'd with `bmkp-make-record-for-target-file'.
-                            (image-type . ,(image-type fil))
-                            (handler    . image-bookmark-jump)))) ; In `image-mode.el'.
-                       (t
-                        (lambda ()
-                          `((filename . ,fil)
-                            (position . 0)))))))
-            (bookmark-store (concat prefix (file-name-nondirectory fil)) (cdr (bookmark-make-record)) nil)))
-      (error (setq failure  (error-message-string err))))
-    (if (not failure)
-        nil                             ; Return nil for success.
-      (if (fboundp 'bmkp-autofile-set)
-          (dired-log failure)
-        (dired-log "Failed to create bookmark for `%s':\n%s\n" fil failure))
-      (dired-make-relative fil))))      ; Return file name for failure.
-
-;;;###autoload
-(defun diredp-set-bookmark-file-bookmark-for-marked (bookmark-file ; Bound to `C-M-b'
-                                                     &optional prefix arg)
-  "Bookmark the marked files and create a bookmark-file bookmark for them.
-The bookmarked position is the beginning of the file.
-Jumping to the bookmark-file bookmark loads the set of file bookmarks.
-You need library `bookmark+.el' to use this command.
-
-Each bookmark name is the non-directory portion of the file name,
- prefixed by PREFIX if it is non-nil.
-Interactively, you are prompted for PREFIX if
- `diredp-prompt-for-bookmark-prefix-flag' is non-nil.
-
-A prefix argument ARG specifies files to use instead of those marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories.
-
-You are also prompted for the bookmark file, BOOKMARK-FILE.  The
-default is `.emacs.bmk' in the current directory, but you can enter
-any file name, anywhere.
-
-The marked-file bookmarks are added to file BOOKMARK-FILE, but this
-command does not make BOOKMARK-FILE the current bookmark file.  To
-make it current, just jump to the bookmark-file bookmark created by
-this command.  That bookmark (which bookmarks BOOKMARK-FILE) is
-defined in that current bookmark file.
-
-Example:
-
- Bookmark file `~/.emacs.bmk' is current before invoking this command.
- The current (Dired) directory is `/foo/bar'.
- The marked files are bookmarked in the (possibly new) bookmark file
-   `/foo/bar/.emacs.bmk'.
- The bookmarks for the marked files have names prefixed by `FOOBAR '.
- The name of the bookmark-file bookmark is `Foobar Files'.
- Bookmark `Foobar Files' is itself in bookmark file `~/.emacs.bmk'.
- Bookmark file `~/.emacs.bmk' is current after invoking this command.
-
-You are prompted for the name of the bookmark-file bookmark, the
-BOOKMARK-FILE for the marked-file bookmarks, and a PREFIX string for
-each of the marked-file bookmarks.
-
-See also command `diredp-do-bookmark-in-bookmark-file'."
-  (interactive (diredp-read-bookmark-file-args))
-  (diredp-ensure-bookmark+)
-  (diredp-do-bookmark-in-bookmark-file bookmark-file prefix arg 'CREATE-BOOKMARK-FILE-BOOKMARK))
-
-;;;###autoload
-(defun diredp-do-bookmark-in-bookmark-file (bookmark-file ; Bound to `C-M-B' (aka `C-M-S-b')
-                                            &optional prefix arg bfile-bookmarkp files)
-  "Bookmark marked files in BOOKMARK-FILE and save BOOKMARK-FILE.
-The files bookmarked are the marked files, by default.
-The bookmarked position is the beginning of the file.
-You are prompted for BOOKMARK-FILE.  The default is `.emacs.bmk' in
-the current directory, but you can enter any file name, anywhere.
-You need library `bookmark+.el' to use this command.
-
-The marked files are bookmarked in file BOOKMARK-FILE, but this
-command does not make BOOKMARK-FILE the current bookmark file.  To
-make it current, use `\\[bmkp-switch-bookmark-file]' (`bmkp-switch-bookmark-file').
-
-Each bookmark name is the non-directory portion of the file name,
- prefixed by PREFIX if it is non-nil.
-Interactively, you are prompted for PREFIX if
- `diredp-prompt-for-bookmark-prefix-flag' is non-nil.
-
-Interactively, a prefix argument ARG specifies the files to use
-instead of those marked.
-
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories.
-
-See also command `diredp-set-bookmark-file-bookmark-for-marked'.
-
-Non-interactively:
-
- * Non-nil BFILE-BOOKMARKP means create a bookmark-file bookmark for
-   BOOKMARK-FILE.
- * Non-nil FILES is the list of files to bookmark."
-  (interactive (diredp-read-bookmark-file-args))
-  (diredp-ensure-bookmark+)
-  (let ((bfile-exists-p  (file-readable-p bookmark-file)))
-    (unless bfile-exists-p (bmkp-empty-file bookmark-file))
-    (unless bmkp-current-bookmark-file (setq bmkp-current-bookmark-file  bookmark-default-file))
-    (let ((old-bmkp-current-bookmark-file  bmkp-current-bookmark-file))
-      (unwind-protect
-           (progn (bmkp-switch-bookmark-file bookmark-file) ; Changes `*-current-bookmark-file'.
-                  (if files
-                      (dolist (file  files)  (diredp-bookmark prefix file 'NO-MSG-P))
-                    (dired-map-over-marks-check
-                     (lexical-let ((pref  prefix)) #'(lambda () (diredp-bookmark pref nil 'NO-MSG-P)))
-                     arg 'bookmark (diredp-fewer-than-2-files-p arg)))
-                  (bookmark-save)
-                  (unless bfile-exists-p (revert-buffer)))
-        (unless (bmkp-same-file-p old-bmkp-current-bookmark-file  bmkp-current-bookmark-file)
-          (bmkp-switch-bookmark-file old-bmkp-current-bookmark-file 'NO-MSG))))
-    (when bfile-bookmarkp (bmkp-set-bookmark-file-bookmark bookmark-file))))
-
-(defun diredp-read-bookmark-file-args ()
-  "Read args for `diredp-do-bookmark-in-bookmark-file' and similar."
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (list (let* ((insert-default-directory  t)
-               (bmk-file                  (expand-file-name
-                                           (read-file-name
-                                            "Use bookmark file (default is in CURRENT dir): " nil
-                                            (if (or (> emacs-major-version 23)
-                                                    (and (= emacs-major-version 23)  (> emacs-minor-version 1)))
-                                                (list ".emacs.bmk" bookmark-default-file)
-                                              ".emacs.bmk")))))
-          bmk-file)
-        (and diredp-prompt-for-bookmark-prefix-flag  (read-string "Prefix for autofile bookmark names: "))
-        current-prefix-arg))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; Allows for consp `dired-directory' too.
-;;
-(defun dired-buffers-for-dir (dir &optional file)
-  "Return a list of buffers that Dired DIR (top level or in-situ subdir).
-If FILE is non-nil, include only those whose wildcard pattern (if any)
-matches FILE.
-The list is in reverse order of buffer creation, most recent last.
-As a side effect, killed Dired buffers for DIR are removed from
-`dired-buffers'."
-  (setq dir  (file-name-as-directory dir))
-  (let (result buf)
-    (dolist (elt  dired-buffers)
-      (setq buf  (cdr elt))
-      (cond ((null (buffer-name buf))   ; Buffer is killed - clean up.
-             (setq dired-buffers  (delq elt dired-buffers)))
-            ((dired-in-this-tree dir (car elt))
-             (with-current-buffer buf
-               (and (assoc dir dired-subdir-alist)
-                    (or (null file)
-                        (if (stringp dired-directory)
-                            ;; Allow for consp `dired-directory' too.
-                            (let ((wildcards  (file-name-nondirectory (if (consp dired-directory)
-                                                                          (car dired-directory)
-                                                                        dired-directory))))
-                              (or (zerop (length wildcards))
-                                  (diredp-string-match-p (dired-glob-regexp wildcards) file)))
-                          (member (expand-file-name file dir) (cdr dired-directory))))
-                    (setq result  (cons buf result)))))))
-    result))
-
-
-;; If you use library `files+.el', you need not use these commands
-;; explicitly, because that library redefines `find-file-read-args' to
-;; do the same thing, in Dired mode.  These are provided here in case
-;; you want to bind them directly - for example, in case your code
-;; does not use `find-file-read-args'.
-;;
-;;;###autoload
-(defun diredp-find-a-file (filename &optional wildcards) ; Not bound
-  "`find-file', but use file on current line as default (`M-n')."
-  (interactive (diredp-find-a-file-read-args "Find file: " nil))
-  (find-file filename wildcards))
-
-;;;###autoload
-(defun diredp-find-a-file-other-frame (filename &optional wildcards) ; Not bound
-  "`find-file-other-frame', but use file under cursor as default (`M-n')."
-  (interactive (diredp-find-a-file-read-args "Find file: " nil))
-  (find-file-other-frame filename wildcards))
-
-;;;###autoload
-(defun diredp-find-a-file-other-window (filename &optional wildcards) ; Not bound
-  "`find-file-other-window', but use file under cursor as default (`M-n')."
-  (interactive (diredp-find-a-file-read-args "Find file: " nil))
-  (find-file-other-window filename wildcards))
-
-;;;###autoload
-(defun diredp-find-a-file-read-args (prompt mustmatch) ; Not bound
-  (list (lexical-let ((find-file-default  (abbreviate-file-name (dired-get-file-for-visit))))
-          (minibuffer-with-setup-hook (lambda ()
-                                        (setq minibuffer-default  find-file-default))
-                                      (read-file-name prompt nil default-directory mustmatch)))
-        t))
-
-;;;###autoload
-(defun diredp-find-file-reuse-dir-buffer () ; Not bound
-  "Like `dired-find-file', but reuse Dired buffers.
-Unlike `dired-find-alternate-file' this does not use
-`find-alternate-file' unless (1) the target is a directory that is not
-yet visited as a Dired buffer, and (2) the current (Dired) buffer is
-not visited also in some other window (possibly in an iconified
-frame)."
-  (interactive)
-  (set-buffer-modified-p nil)
-  (let ((file  (dired-get-file-for-visit)))
-    (diredp--reuse-dir-buffer-helper file)))
-
-;;;###autoload
-(defun diredp-mouse-find-file-reuse-dir-buffer (event &optional find-file-func find-dir-func) ; Not bound
-  "Like `dired-mouse-find-file', but reuse Dired buffers.
-Unlike `dired-find-alternate-file' this does not use
-`find-alternate-file' unless (1) the target is a directory that is not
-yet visited as a Dired buffer, and (2) the current (Dired) buffer is
-not visited also in some other window (possibly in an iconified
-frame).
-
-Non-nil optional args FIND-FILE-FUNC and FIND-DIR-FUNC specify
-functions to visit the file and directory, respectively.
-Defaults: `find-file' and `dired', respectively."
-  (interactive "e")
-  (let (window pos file)
-    (save-excursion
-      (setq window  (posn-window (event-end event))
-	    pos     (posn-point (event-end event)))
-      (unless (windowp window) (error "No file chosen"))
-      (set-buffer (window-buffer window))
-      (goto-char pos)
-      (setq file  (dired-get-file-for-visit)))
-    (select-window window)
-    (diredp--reuse-dir-buffer-helper file find-file-func find-dir-func)))
-
-(defun diredp--reuse-dir-buffer-helper (file &optional find-file-func find-dir-func other-window)
-  "Helper for commands `diredp-*-reuse-dir-buffer' commands.
-Non-nil optional args FIND-FILE-FUNC and FIND-DIR-FUNC specify
-functions to visit the file and directory, respectively.
-Defaults: `find-file' and `dired', respectively.
-
-Unlike `dired-find-alternate-file' this does not use
-`find-alternate-file' unless (1) the target is a directory that is not
-yet visited as a Dired buffer, and (2) the current (Dired) buffer is
-not visited also in some other window (possibly in an iconified
-frame)."
-  (setq find-file-func  (or find-file-func (if other-window #'find-file-other-window #'find-file))
-        find-dir-func   (or find-dir-func  (if other-window #'dired-other-window #'dired)))
-  (let (;; This binding prevents problems with preserving point in windows displaying Dired buffers, because
-        ;; reverting a Dired buffer empties it, which changes the places where the markers used by
-        ;; `switch-to-buffer-preserve-window-point' point.
-        (switch-to-buffer-preserve-window-point  (and (boundp 'switch-to-buffer-preserve-window-point) ; Emacs 24+
-                                                      (or (not (boundp 'dired-auto-revert-buffer))
-                                                          (not dired-auto-revert-buffer))
-                                                      switch-to-buffer-preserve-window-point))
-        (find-file-run-dired                     t)
-        (wins                                    ())
-        (alt-find-file-func                      (if other-window
-                                                     #'find-alternate-file-other-window
-                                                   #'find-alternate-file))
-        dir-bufs)
-    (if (or (not (file-directory-p file)) ; New is a not a directory
-            (dired-buffers-for-dir file) ; or there is a Dired buffer for it, even as a subdir.
-            (and (setq dir-bufs  (dired-buffers-for-dir default-directory)) ; Dired bufs for current (old).
-                 (progn
-                   (dolist (buf  dir-bufs)
-                     (setq wins  (append wins (get-buffer-window-list buf 'NOMINI 0))))
-                   (setq wins  (delq nil wins))
-                   (cdr wins))))        ; More than one window showing current Dired buffer.
-        (if (file-directory-p file)
-	    (or (and (cdr dired-subdir-alist)  (dired-goto-subdir file)) ; New is a subdir inserted in current
-	        (funcall find-dir-func file))
-          (funcall find-file-func (file-name-sans-versions file t)))
-      (funcall alt-find-file-func (file-name-sans-versions file t)))))
-
-;;;###autoload
-(defalias 'toggle-diredp-find-file-reuse-dir 'diredp-toggle-find-file-reuse-dir)
-;;;###autoload
-(defun diredp-toggle-find-file-reuse-dir (force-p) ; Bound to `C-M-R' (aka `C-M-S-r')
-  "Toggle whether Dired `find-file' commands reuse directories.
-This applies also to `dired-w32-browser' commands and
-`diredp-up-directory'.
-
-A prefix arg specifies directly whether or not to reuse.
- If its numeric value is non-negative then reuse; else do not reuse.
-
-To set the behavior as a preference (default behavior), put this in
-your ~/.emacs, where VALUE is 1 to reuse or -1 to not reuse:
-
- (diredp-toggle-find-file-reuse-dir VALUE)
-
-Note: This affects only these commands:
-
-  `dired-find-file'
-  `dired-mouse-find-file'
-
-It does not affect the corresponding `-other-window' commands.  Note
-too that, by default, mouse clicks to open files or directories open
-in another window: command `diredp-mouse-find-file-other-window', not
-`dired-mouse-find-file'.  If you want a mouse click to reuse a
-directory then bind `mouse-2' to `dired-mouse-find-file' instead."
-  (interactive "P")
-  (if force-p                           ; Force.
-      (if (natnump (prefix-numeric-value force-p))
-          (diredp-make-find-file-keys-reuse-dirs)
-        (diredp-make-find-file-keys-not-reuse-dirs))
-    (if (where-is-internal 'dired-find-file dired-mode-map 'ascii)
-        (diredp-make-find-file-keys-reuse-dirs)
-      (diredp-make-find-file-keys-not-reuse-dirs))))
-
-(defun diredp-make-find-file-keys-reuse-dirs ()
-  "Make find-file keys reuse Dired buffers."
-  (substitute-key-definition 'diredp-up-directory 'diredp-up-directory-reuse-dir-buffer dired-mode-map)
-  (substitute-key-definition 'dired-find-file 'diredp-find-file-reuse-dir-buffer dired-mode-map)
-  (substitute-key-definition 'dired-mouse-find-file 'diredp-mouse-find-file-reuse-dir-buffer dired-mode-map)
-  ;; These commands are defined in `w32-browser.el' (for use with MS Windows).
-  (substitute-key-definition 'dired-w32-browser 'dired-w32-browser-reuse-dir-buffer dired-mode-map)
-  (substitute-key-definition 'dired-mouse-w32-browser 'dired-mouse-w32-browser-reuse-dir-buffer dired-mode-map)
-  (message "Reusing Dired buffers is now ON"))
-
-(defun diredp-make-find-file-keys-not-reuse-dirs ()
-  "Make find-file keys not reuse Dired buffers (i.e. act normally)."
-  (substitute-key-definition 'diredp-up-directory-reuse-dir-buffer 'diredp-up-directory dired-mode-map)
-  (substitute-key-definition 'diredp-find-file-reuse-dir-buffer 'dired-find-file dired-mode-map)
-  (substitute-key-definition 'diredp-mouse-find-file-reuse-dir-buffer 'dired-mouse-find-file dired-mode-map)
-  ;; These commands are defined in `w32-browser.el' (for use with MS Windows).
-  (substitute-key-definition 'dired-w32-browser-reuse-dir-buffer 'dired-w32-browser dired-mode-map)
-  (substitute-key-definition 'dired-mouse-w32-browser-reuse-dir-buffer 'dired-mouse-w32-browser dired-mode-map)
-  (message "Reusing Dired buffers is now OFF"))
-
-;;;###autoload
-(defun diredp-omit-marked ()            ; Not bound
-  "Omit lines of marked files.  Return the number of lines omitted."
-  (interactive)
-  (let ((old-modified-p  (buffer-modified-p))
-        count)
-    (when (interactive-p) (message "Omitting marked lines..."))
-    (setq count  (dired-do-kill-lines nil "Omitted %d line%s."))
-    (set-buffer-modified-p old-modified-p) ; So no `%*' appear in mode-line.
-    count))
-
-;;;###autoload
-(defun diredp-omit-unmarked ()          ; Not bound
-  "Omit lines of unmarked files.  Return the number of lines omitted."
-  (interactive)
-  (let ((old-modified-p  (buffer-modified-p))
-        count)
-    (dired-toggle-marks)
-    (message "Omitting unmarked lines...")
-    (setq count  (diredp-omit-marked))
-    (dired-toggle-marks)                ; Marks all except `.', `..'
-    (set-buffer-modified-p old-modified-p) ; So no `%*' appear in mode-line.
-    count))
-
-;;;###autoload
-(defun diredp-ediff (file2)             ; Bound to `='
-  "Compare file at cursor with file FILE2 using `ediff'.
-FILE2 defaults to the file at the cursor as well.  If you enter just a
-directory name for FILE2, then the file at the cursor is compared with
-a file of the same name in that directory.  FILE2 is the second file
-given to `ediff'; the file at the cursor is the first.
-
-Try to guess a useful default value for FILE2, as follows:
-
-* If the mark is active, use the file at mark.
-* Else if the file at cursor is a autosave file or a backup file, use
-  the corresponding base file.
-* Else if there is any backup file for the file at point, use the
-  newest backup file for it.
-* Else use the file at point."
-  (interactive (progn (require 'ediff)
-                      (list (ediff-read-file-name ; In `ediff-util.el'.
-                             (format "Compare %s with" (dired-get-filename t))
-                             (dired-current-directory)
-                             (let* ((file           (dired-get-filename))
-                                    (file-sans-dir  (file-name-nondirectory file))
-                                    (file-dir       (file-name-directory file))
-                                    (file-at-mark   (and transient-mark-mode
-                                                         mark-active
-                                                         (save-excursion (goto-char (mark t))
-                                                                         (dired-get-filename t t))))
-                                    (last-backup    (file-newest-backup file)))
-                               (cond
-                                 (file-at-mark)
-                                 ((auto-save-file-name-p file-sans-dir)
-                                  (expand-file-name (substring file-sans-dir 1 -1) file-dir))
-                                 ((backup-file-name-p file-sans-dir)
-                                  (expand-file-name (file-name-sans-versions file-sans-dir) file-dir))
-                                 (last-backup)
-                                 (t file)))))))
-  (ediff-files (dired-get-filename) file2)) ; In `ediff.el'.
-
-(defun diredp-fewer-than-N-files-p (arg n)
-  "Return non-nil iff fewer than N files are to be treated by dired.
-More precisely, return non-nil iff ARG is nil and fewer than N
-files are marked, or the absolute value of ARG is less than N."
-  (if arg
-      (and (integerp arg)  (< (abs arg) n)) ; Next or previous file (or none).
-    (not (save-excursion                ; Fewer than two marked files.
-           (goto-char (point-min))
-           (re-search-forward (dired-marker-regexp) nil t n)))))
-
-(defun diredp-fewer-than-2-files-p (arg)
-  "Return non-nil iff fewer than two files are to be treated by dired.
-More precisely, return non-nil iff ARG is nil and fewer than two
-files are marked, or ARG is -1, 0 or 1."
-  (diredp-fewer-than-N-files-p arg 2))
-
-(defun diredp-fewer-than-echo-limit-files-p (arg)
-  "Return non-nil iff < `diredp-do-report-echo-limit' files marked.
-More precisely, return non-nil iff ARG is nil and fewer than two
-files are marked, or ARG is -1, 0 or 1."
-  (diredp-fewer-than-N-files-p arg diredp-do-report-echo-limit))
-
-;;;###autoload
-(defun diredp-do-apply-function (function &optional arg) ; Bound to `@'
-  "Apply FUNCTION to the marked files.
-You are prompted for the FUNCTION.
-
-With a plain prefix ARG (`C-u'), visit each file and invoke FUNCTION
- with no arguments.
-Otherwise, apply FUNCTION to each file name.
-
-Any prefix arg other than single `C-u' behaves according to the ARG
-argument of `dired-get-marked-files'.  In particular, `C-u C-u'
-operates on all files in the Dired buffer.
-
-The result returned for each file is logged by `dired-log'.  Use `?'
-to see all such results and any error messages.  If there are fewer
-marked files than `diredp-do-report-echo-limit' then each result is
-also echoed momentarily."
-  (interactive (progn (diredp-ensure-mode)
-                      (list (read (completing-read "Function: " obarray 'functionp nil nil
-                                                   (and (boundp 'function-name-history)
-                                                        'function-name-history)))
-                            current-prefix-arg)))
-  (let ((use-no-args-p  (and (consp arg)  (< (car arg) 16))))
-    (when use-no-args-p (setq arg  ()))
-    (save-selected-window
-      (diredp-map-over-marks-and-report
-       (if use-no-args-p #'diredp-invoke-function-no-args #'diredp-apply-function-to-file-name)
-       arg
-       'apply\ function (diredp-fewer-than-2-files-p arg)
-       function
-       (diredp-fewer-than-echo-limit-files-p arg)))))
-
-(defun diredp-invoke-function-no-args (fun &optional echop)
-  "Visit file of this line at its beginning, then invoke function FUN.
-No arguments are passed to FUN.
-Log the result returned or any error.
-Non-nil optional arg ECHOP means also echo the result."
-  (let* ((file     (dired-get-filename))
-         (failure  (not (file-exists-p file)))
-         result)
-    (unless failure
-      (condition-case err
-          (with-current-buffer (find-file-noselect file)
-            (save-excursion
-              (goto-char (point-min))
-              (setq result  (funcall fun))))
-        (error (setq failure  err))))
-    (diredp-report-file-result file result failure echop)))
-
-(defun diredp-apply-function-to-file-name (fun &optional echop)
-  "Apply function FUN to (absolute) file name on this line.
-Log the result returned or any error.
-Non-nil optional arg ECHOP means also echo the result."
-  (let ((file     (dired-get-filename))
-        (failure  nil)
-        result)
-    (condition-case err
-        (setq result  (funcall fun file))
-      (error (setq failure  err)))
-    (diredp-report-file-result file result failure echop)))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'.
-;;
-;; 1. Redisplay only if at most one file is being treated.
-;; 2. Doc string reflects `Dired+'s version of `dired-map-over-marks-check'.
-;;
-;;;###autoload
-(defun dired-do-compress (&optional arg) ; Bound to `Z'
-  "Compress or uncompress marked (or next prefix argument) files.
-A prefix argument ARG specifies files to use instead of marked.
- An integer means use the next ARG files (previous -ARG, if < 0).
- `C-u': Use the current file (whether or not any are marked).
- `C-u C-u': Use all files in Dired, except directories.
- `C-u C-u C-u': Use all files and directories, except `.' and `..'.
- `C-u C-u C-u C-u': Use all files and all directories."
-  (interactive "P")
-  (dired-map-over-marks-check #'dired-compress arg 'compress (diredp-fewer-than-2-files-p arg)))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'.
-;;
-;; 1. Redisplay only if at most one file is being treated.
-;; 2. Doc string reflects `Dired+'s version of `dired-map-over-marks-check'.
-;;
-;;;###autoload
-(defun dired-do-byte-compile (&optional arg) ; Bound to `B'
-  "Byte compile marked Emacs Lisp files.
-A prefix argument ARG specifies files to use instead of those marked.
- * An integer means use the next ARG files (previous -ARG, if < 0).
- * Two or more `C-u' (e.g. `C-u C-u') means ignore any marks and use
-   all files in the Dired buffer.
- * Any other prefix arg means use the current file."
-  (interactive (let* ((arg  current-prefix-arg)
-                      (C-u  (and (consp arg)  arg)))
-                 (when (and C-u  (> (prefix-numeric-value arg) 16)) (setq arg  '(16)))
-                 (list arg)))
-  (dired-map-over-marks-check #'dired-byte-compile arg 'byte-compile
-                              (diredp-fewer-than-2-files-p arg)))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'.
-;;
-;; 1. Redisplay only if at most one file is being treated.
-;; 2. Doc string reflects `Dired+' version of `dired-map-over-marks-check'.
-;;
-;;;###autoload
-(defun dired-do-load (&optional arg)    ; Bound to `L'
-  "Load the marked Emacs Lisp files.
-A prefix argument ARG specifies files to use instead of those marked.
- * An integer means use the next ARG files (previous -ARG, if < 0).
- * Two or more `C-u' (e.g. `C-u C-u') means ignore any marks and use
-   all files in the Dired buffer.
- * Any other prefix arg means use the current file."
-  (interactive (let* ((arg  current-prefix-arg)
-                      (C-u  (and (consp arg)  arg)))
-                 (when (and C-u  (> (prefix-numeric-value arg) 16)) (setq arg  '(16)))
-                 (list arg)))
-  (dired-map-over-marks-check #'dired-load arg 'load (diredp-fewer-than-2-files-p arg)))
-
-
-(when (fboundp 'multi-isearch-files)
-
-  ;; REPLACE ORIGINAL in `dired.el':
-  ;;
-  ;; 1. Added optional arg ARG, so you can act on next ARG files or on all files.
-  ;; 2. Added optional arg INTERACTIVEP.
-  ;; 3. Do not raise error if no files when not INTERACTIVEP.
-  ;;
-  (defun dired-do-isearch (&optional arg interactivep)
-    "Search for a string through all marked files using Isearch.
-A prefix argument ARG specifies files to use instead of those marked.
- * An integer means use the next ARG files (previous -ARG, if < 0).
- * Two or more `C-u' (e.g. `C-u C-u') means ignore any marks and use
-   all files in the Dired buffer.
- * Any other prefix arg means use the current file.
-When invoked interactively, raise an error if no files are marked."
-    (interactive (let* ((arg  current-prefix-arg)
-                        (C-u  (and (consp arg)  arg)))
-                   (when (and C-u  (> (prefix-numeric-value arg) 16)) (setq arg  '(16)))
-                   (list arg t)))
-    (multi-isearch-files (dired-get-marked-files nil arg 'dired-nondirectory-p nil interactivep)))
-
-
-  ;; REPLACE ORIGINAL in `dired.el':
-  ;;
-  ;; 1. Added optional arg ARG, so you can act on next ARG files or on all files.
-  ;; 2. Added optional arg INTERACTIVEP.
-  ;; 3. Do not raise error if no files when not INTERACTIVEP.
-  ;;
-  (defun dired-do-isearch-regexp (&optional arg interactivep)
-    "Search for a regexp through all marked files using Isearch.
-A prefix arg behaves as follows:
- * An integer means use the next ARG files (previous -ARG, if < 0).
- * Two or more `C-u' (e.g. `C-u C-u') means ignore any marks and use
-   all files in the Dired buffer.
- * Any other prefix arg means use the current file.
-When invoked interactively, raise an error if no files are marked."
-    (interactive (let* ((arg  current-prefix-arg)
-                        (C-u  (and (consp arg)  arg)))
-                   (when (and C-u  (> (prefix-numeric-value arg) 16)) (setq arg  '(16)))
-                   (list arg t)))
-    (multi-isearch-files-regexp (dired-get-marked-files nil arg 'dired-nondirectory-p nil interactivep)))
-
-  )
-
-
-;; REPLACE ORIGINAL in `dired-aux.el':
-;;
-;; 1. Added optional arg ARG, so you can act on next ARG files or on all files.
-;; 2. Added optional arg INTERACTIVEP.
-;; 3. Do not raise error if no files when not INTERACTIVEP.
-;;
-;;;###autoload
-(defun dired-do-search (regexp &optional arg interactivep)
-  "Search through all marked files for a match for REGEXP.
-Stops when a match is found.
-To continue searching for next match, use command \\[tags-loop-continue].
-
-A prefix arg behaves as follows:
- * An integer means use the next ARG files (previous -ARG, if < 0).
- * Two or more `C-u' (e.g. `C-u C-u') means ignore any marks and use
-   all files in the Dired buffer.
- * Any other prefix arg means use the current file.
-
-When invoked interactively, raise an error if no files are marked."
-  (interactive (let* ((arg  current-prefix-arg)
-                      (C-u  (and (consp arg)  arg)))
-                 (when (and C-u  (> (prefix-numeric-value arg) 16)) (setq arg  '(16)))
-                 (list (diredp-read-regexp "Search marked files (regexp): ")
-                       arg
-                       t)))
-  (tags-search regexp `(dired-get-marked-files nil ',arg 'dired-nondirectory-p nil ,interactivep)))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el':
-;;
-;; 1. Added optional arg ARG, so you can act on next ARG files or on all files.
-;; 2. Added optional arg INTERACTIVEP.
-;; 3. Do not raise error if no files when not INTERACTIVEP.
-;;
-;;;###autoload
-(defun dired-do-query-replace-regexp (from to &optional arg interactivep)
-  "Do `query-replace-regexp' of FROM with TO, on all marked files.
-NOTE: A prefix arg for this command acts differently than for other
-commands, so that you can use it to request word-delimited matches.
-
-With a prefix argument:
- * An odd number of plain `C-u': act on the marked files, but replace
-   only word-delimited matches.
- * More than one plain `C-u': act on all files, ignoring whether any
-   are marked.
- * Any other prefix arg: Act on the next numeric-prefix files.
-
-So for example:
- * `C-u C-u C-u': act on all files, replacing word-delimited matches.
- * `C-u 4': act on the next 4 files.  `C-4' means the same thing.
- * `C-u': act on the marked files, replacing word-delimited matches.
-
-When invoked interactively, raise an error if no files are marked.
-
-If you exit (\\[keyboard-quit], RET or q), you can resume the query replace
-with the command \\[tags-loop-continue]."
-  (interactive (let ((common  (query-replace-read-args "Query replace regexp in marked files" t t)))
-                 (list (nth 0 common)
-                       (nth 1 common)
-                       current-prefix-arg
-                       t)))
-  (let* ((argnum     (and (consp arg)  (prefix-numeric-value arg)))
-         (delimited  (and argnum  (eq (logand (truncate (log argnum 4)) 1) 1))) ; Odd number of plain `C-u'.
-         (all        (and argnum  (> argnum 4))) ; At least 3 plain `C-u'.
-         (dgmf-arg   (dired-get-marked-files nil
-                                             (if (and arg  (atom arg)) (abs arg) (and all  '(16)))
-                                             'dired-nondirectory-p
-                                             nil
-                                             interactivep)))
-    (dolist (file  dgmf-arg)
-      (let ((buffer  (get-file-buffer file)))
-        (when (and buffer  (with-current-buffer buffer buffer-read-only))
-          (error "File `%s' is visited read-only" file))))
-    (tags-query-replace from to delimited `',dgmf-arg)))
-
-
-(when (fboundp 'xref-collect-matches)   ; Emacs 25+
-
-
-  ;; REPLACE ORIGINAL in `dired-aux.el':
-  ;;
-  ;; 1. Added optional arg ARG, so you can act on next ARG files or on all files.
-  ;; 2. Added optional arg INTERACTIVEP.
-  ;; 3. Do not raise error if no files when not INTERACTIVEP.
-  ;;
-  (defun dired-do-find-regexp (regexp &optional arg interactivep)
-    "Find all matches for REGEXP in all marked files.
-For any marked directory, all of its files are searched recursively.
-However, files matching `grep-find-ignored-files' and subdirectories
-matching `grep-find-ignored-directories' are skipped in the marked
-directories.
-
-A prefix arg behaves as follows:
- * An integer means use the next ARG files (previous -ARG, if < 0).
- * Two or more `C-u' (e.g. `C-u C-u') means ignore any marks and use
-   all files in the Dired buffer.
- * Any other prefix arg means use the current file.
-
-When invoked interactively, raise an error if no files are marked.
-
-REGEXP should use constructs supported by your local `grep' command."
-    (interactive (let* ((arg  current-prefix-arg)
-                        (C-u  (and (consp arg)  arg)))
-                   (when (and C-u  (> (prefix-numeric-value arg) 16)) (setq arg  '(16)))
-                   (list (diredp-read-regexp "Search marked files (regexp): ")
-                         arg
-                         t)))
-    (require 'grep)
-    (defvar grep-find-ignored-files)
-    (defvar grep-find-ignored-directories)
-    (let* ((files    (dired-get-marked-files nil arg nil nil interactivep))
-           (ignores  (nconc (mapcar (lambda (s) (concat s "/")) grep-find-ignored-directories)
-                            grep-find-ignored-files))
-           (xrefs    (mapcan (lambda (file)
-                               (xref-collect-matches
-                                regexp "*" file (and (file-directory-p file)  ignores)))
-                             files)))
-      (if xrefs
-          (xref--show-xrefs xrefs nil t)
-        (when interactivep (diredp-user-error "No matches for: %s" regexp)))))
-
-
-  ;; REPLACE ORIGINAL in `dired-aux.el':
-  ;;
-  ;; 1. Added optional arg ARG, so you can act on next ARG files or on all files.
-  ;; 2. Added optional arg INTERACTIVEP.
-  ;; 3. Do not raise error if no files when not INTERACTIVEP.
-  ;;
-;;;###autoload
-  (defun dired-do-find-regexp-and-replace (from to &optional arg interactivep)
-    "Replace matches of FROM with TO, in all marked files.
-For any marked directory, matches in all of its files are replaced,
-recursively.  However, files matching `grep-find-ignored-files'
-and subdirectories matching `grep-find-ignored-directories' are skipped
-in the marked directories.
-
-A prefix arg behaves as follows:
- * An integer means use the next ARG files (previous -ARG, if < 0).
- * Two or more `C-u' (e.g. `C-u C-u') means ignore any marks and use
-   all files in the Dired buffer.
- * Any other prefix arg means use the current file.
-
-When invoked interactively, raise an error if no files are marked.
-
-REGEXP should use constructs supported by your local `grep' command."
-    (interactive (let ((common  (query-replace-read-args "Query replace regexp in marked files" t t))
-                       (arg     current-prefix-arg)
-                       (C-u     (and (consp arg)  arg)))
-                   (when (and C-u  (> (prefix-numeric-value arg) 16)) (setq arg  '(16)))
-                   (list (nth 0 common)
-                         (nth 1 common)
-                         arg
-                         t)))
-    (with-current-buffer (dired-do-find-regexp from arg interactivep)
-      (xref-query-replace-in-results from to)))
-
-  )
-
-;;;###autoload
-(defun diredp-do-grep (command-args)    ; Bound to `C-M-G'
-  "Run `grep' on marked (or next prefix arg) files.
-A prefix argument behaves according to the ARG argument of
-`dired-get-marked-files'.  In particular, `C-u C-u' operates on all
-files in the Dired buffer."
-  (interactive (progn (unless (if (< emacs-major-version 22)
-                                  grep-command
-                                (and grep-command  (or (not grep-use-null-device)  (eq grep-use-null-device t))))
-                        (grep-compute-defaults))
-                      (list (diredp-do-grep-1))))
-  (grep command-args))
-
-;; Optional arg FILES is no longer used.  It was used in `diredp-do-grep' before the
-;; new `dired-get-marked-files'.
-(defun diredp-do-grep-1 (&optional files)
-  "Helper function for `diredp-do-grep'.
-Non-nil optional arg FILES are the files to grep, overriding the files
-choice described for `diredp-do-grep'."
-  (let ((default  (and (fboundp 'grep-default-command)
-                       (if (fboundp 'grepp-default-regexp-fn) ; In `grep+.el'.
-                           (grep-default-command (funcall (grepp-default-regexp-fn)))
-                         (grep-default-command)))))
-    (read-from-minibuffer
-     "grep <pattern> <files> :  "
-     (let ((up-to-files  (concat grep-command "   ")))
-       (cons (concat up-to-files
-                     (mapconcat #'identity
-                                (or files  (mapcar 'shell-quote-argument
-                                                   (dired-get-marked-files nil current-prefix-arg)))
-                                " "))
-             (- (length up-to-files) 2)))
-     nil nil 'grep-history default)))
-
-(when (memq system-type '(windows-nt ms-dos))
-  (define-derived-mode diredp-w32-drives-mode fundamental-mode "Drives"
-    "Mode for Dired buffer listing MS Windows drives (local or remote)."
-    (setq buffer-read-only  t)))
-
-;; The next two commands were originally taken from Emacs Wiki, page WThirtyTwoBrowseNetDrives:
-;; https://www.emacswiki.org/emacs/WThirtyTwoBrowseNetDrives.  They are referred to there as
-;; commands `show-net-connections' and `netdir'.  I am hoping that the contributor (anonymous)
-;; does not mind my adapting them and including them in `Dired+'.
-
-(when (memq system-type '(windows-nt ms-dos))
-  (defun diredp-w32-list-mapped-drives () ; Not bound
-    "List network connection information for shared MS Windows resources.
-This just invokes the Windows `NET USE' command."
-    (interactive)
-    (shell-command "net use")
-    (display-buffer "*Shell Command Output*")))
-
-(when (memq system-type '(windows-nt ms-dos))
-  (defun diredp-w32-drives (&optional other-window-p) ; Bound to `:/'
-    "Visit a list of MS Windows drives for use by Dired.
-With a prefix argument use another window for the list.
-In the list, use `mouse-2' or `RET' to open Dired for a given drive.
-
-The drives listed are the remote drives currently available, as
-determined by the Windows command `NET USE', plus the local drives
-specified by option `diredp-w32-local-drives', which you can
-customize.
-
-Note: When you are in Dired at the root of a drive (e.g. directory
-      `C:/'), command `diredp-up-directory' invokes this command.
-      So you can use `\\[diredp-up-directory]' to go up to the list of drives."
-    (interactive "P")
-    (require 'widget)
-    (let ((drive              (copy-sequence diredp-w32-local-drives))
-          (inhibit-read-only  t))
-      (with-temp-buffer
-        (insert (shell-command-to-string "net use"))
-        (goto-char (point-min))
-        (while (re-search-forward "[A-Z]: +\\\\\\\\[^ ]+" nil t nil)
-          (setq drive  (cons (split-string (match-string 0)) drive))))
-      (if other-window-p
-          (pop-to-buffer "*Windows Drives*")
-        (if (fboundp 'pop-to-buffer-same-window)
-            (pop-to-buffer-same-window "*Windows Drives*")
-          (switch-to-buffer "*Windows Drives*")))
-      (erase-buffer)
-      (widget-minor-mode 1)
-      (dolist (drv  (sort drive (lambda (a b) (string-lessp (car a) (car b)))))
-        (lexical-let ((drv  drv))
-          (widget-create 'push-button
-                         :notify (lambda (widget &rest ignore) (dired (car drv)))
-                         (concat (car drv) "  " (cadr drv))))
-        (widget-insert "\n"))
-      (goto-char (point-min))
-      (diredp-w32-drives-mode))))
-
-;; $$$$$$ NO LONGER USED.  Was used in `diredp-do-grep(-1)' before new `dired-get-marked-files'.
-(defun diredp-all-files ()
-  "List of all files shown in current Dired buffer.
-Directories are not included."
-  (let ((pos    (make-marker))
-        (files  ())
-        file)
-    (save-excursion
-      (goto-char (point-min)) (beginning-of-line)
-      (while (not (eobp))
-        (beginning-of-line)
-        (while (and (not (eobp))  (dired-between-files))  (forward-line 1))
-        (save-excursion (forward-line 1) (move-marker pos (1+ (point))))
-        (setq file  (dired-get-filename nil t)) ; Non-nil second arg means "also . and ..".
-        (when file                      ; Remove directory portion if in same directory.
-          (setq file  (dired-get-filename (dired-in-this-tree file default-directory) t)))
-        (unless (or (not file)  (file-directory-p file))  (push file files))
-        (goto-char pos))
-      (move-marker pos nil))
-    (setq files  (sort files (if (and (featurep 'ls-lisp)
-                                      (not (symbol-value 'ls-lisp-use-insert-directory-program)))
-                                 'ls-lisp-string-lessp
-                               (if case-fold-search
-                                   (lambda (s1 s2) (string-lessp (upcase s1) (upcase s2)))
-                                 'string-lessp))))))
-
-(when (fboundp 'read-char-choice)       ; Emacs 24+
-
-
-  ;; REPLACE ORIGINAL in `dired-aux.el'
-  ;;
-  ;; `l' lists the files involved and prompts again.
-  ;;
-  (defun dired-query (sym prompt &rest args)
-    "Format PROMPT with ARGS, query user, and store the result in SYM.
-The return value is either nil or t.
-
-The user can type:
- `y' or `SPC' to accept once
- `n' or `DEL' to skip once
- `!' to accept this and subsequent queries
- `l' list the files, showing details per `diredp-list-file-attributes'
- `q' or `ESC' to decline this and subsequent queries
-
-If SYM is already bound to a non-nil value, this function may return
-automatically without querying the user.  If SYM is `!', return t; if
-SYM is `q' or ESC, return nil."
-    (let* ((char            (symbol-value sym))
-           (char-choices    '(?y ?\   ?n ?\177 ?! ?l ?q ?\e)) ; Use ?\  , not ?\s, for Emacs 20 byte-compiler.
-           (list-buf        (generate-new-buffer-name "*Files*"))
-           (list-was-shown  nil))
-      (unwind-protect
-           (cond ((eq char ?!) t)       ; Accept, and don't ask again.
-                 ((memq char '(?q ?\e)) nil) ; Skip, and don't ask again.
-                 (t                     ; No previous answer - ask now
-                  (setq prompt  (concat (apply (if (fboundp 'format-message) #'format-message #'format)
-                                               prompt
-                                               args)
-                                        (if help-form
-                                            (format " [Type ynlq! or %s] " (key-description (vector help-char)))
-                                          " [Type y, n, l, q or !] ")))
-                  (set sym (setq char  (read-char-choice prompt char-choices)))
-                  (when (eq char ?l)    ; List files and prompt again.
-                    (diredp-list-files args nil nil nil diredp-list-file-attributes)
-                    (set sym (setq char  (read-char-choice prompt char-choices))))
-                  (and (memq char '(?y ?\   ?!))  t))) ; Use ?\  , not ?\s, for Emacs 20.
-        (when (get-buffer list-buf)
-          (save-window-excursion
-            (pop-to-buffer list-buf)
-            (condition-case nil         ; Ignore error if user already deleted.
-                (if (one-window-p) (delete-frame) (delete-window))
-              (error nil))
-            (if list-was-shown (bury-buffer list-buf) (kill-buffer list-buf)))))))
-
-  )
-
-(unless (fboundp 'read-char-choice)     ; Emacs 20-23 (modified the Emacs 23 version).  Needs `dired-query-alist'.
-
-
-  ;; REPLACE ORIGINAL in `dired-aux.el'
-  ;;
-  ;; 1. `l' lists the files involved and prompts again.
-  ;; 2. Compatible with older Emacs versions (before Emacs 24): can use `dired-query-alist'.
-  ;;
-  (defun dired-query (qs-var qs-prompt &rest qs-args)
-    "Query user and return nil or t.
-The user can type:
- `y' or `SPC' to accept once
- `n' or `DEL' to skip once
- `!' to accept this and subsequent queries
- `l' list the files, showing details per `diredp-list-file-attributes'
- `q' or `ESC' to decline this and subsequent queries
-
-Store answer in symbol VAR (which must initially be bound to nil).
-Format PROMPT with ARGS.
-Binding variable `help-form' will help the user who types the help key."
-    (let* ((char               (symbol-value qs-var))
-           (dired-query-alist  (cons '(?l . l) dired-query-alist))
-           (action             (cdr (assoc char dired-query-alist))))
-      (cond ((eq 'yes action) t)        ; Accept, and don't ask again.
-            ((eq 'no action) nil)       ; Skip, and don't ask again.
-            (t                          ; No lasting effects from last time we asked - ask now.
-             (let ((cursor-in-echo-area  t)
-                   (executing-kbd-macro  executing-kbd-macro)
-                   (qprompt              (concat qs-prompt
-                                                 (if help-form
-                                                     (format " [Type ynl!q or %s] "
-                                                             (key-description (char-to-string help-char)))
-                                                   " [Type y, n, l, q or !] ")))
-                   done result elt)
-               (while (not done)
-                 (apply #'message qprompt qs-args)
-                 (setq char  (set qs-var (read-event)))
-                 (when (eq char ?l)     ; List files and prompt again.
-                   (diredp-list-files qs-args nil nil nil diredp-list-file-attributes)
-                   (apply #'message qprompt qs-args)
-                   (setq char  (set qs-var (read-event))))
-                 (if (numberp char)
-                     (cond ((and executing-kbd-macro (= char -1))
-                            ;; `read-event' returns -1 if we are in a keyboard macro and there are no more
-                            ;; events in the macro.  Try to get an event interactively.
-                            (setq executing-kbd-macro  nil))
-                           ((eq (key-binding (vector char)) 'keyboard-quit) (keyboard-quit))
-                           (t (setq done  (setq elt  (assoc char dired-query-alist)))))))
-               ;; Display the question with the answer.
-               (message "%s" (concat (apply #'format qprompt qs-args) (char-to-string char)))
-               (memq (cdr elt) '(t y yes)))))))
-
-  )
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'.
-;;
-;; 1. Use `diredp-this-subdir' instead of `dired-get-filename'.
-;; 2. If on a subdir listing header line or a non-dir file in a subdir listing, go to
-;;    the line for the subdirectory in the parent directory listing.
-;; 3. Fit one-window frame after inserting subdir.
-;;
-;;;###autoload
-(defun dired-maybe-insert-subdir (dirname &optional switches no-error-if-not-dir-p)
-                                        ; Bound to `i'
-  "Move to Dired subdirectory line or subdirectory listing.
-This bounces you back and forth between a subdirectory line and its
-inserted listing header line.  Using it on a non-directory line in a
-subdirectory listing acts the same as using it on the subdirectory
-header line.
-
-* If on a subdirectory line, then go to the subdirectory's listing,
-  creating it if not yet present.
-
-* If on a subdirectory listing header line or a non-directory file in
-  a subdirectory listing, then go to the line for the subdirectory in
-  the parent directory listing.
-
-* If on a non-directory file in the top Dired directory listing, do
-  nothing.
-
-Subdirectories are listed in the same position as for `ls -lR' output.
-
-With a prefix arg, you can edit the `ls' switches used for this
-listing.  Add `R' to the switches to expand the directory tree under a
-subdirectory.
-
-Dired remembers the switches you specify with a prefix arg, so
-reverting the buffer does not reset them.  However, you might
-sometimes need to reset some subdirectory switches after a
-`dired-undo'.  You can reset all subdirectory switches to the
-default value using \\<dired-mode-map>\\[dired-reset-subdir-switches].  See \
-Info node
-`(emacs)Subdir switches' for more details."
-  (interactive (list (diredp-this-subdir)
-                     (and current-prefix-arg
-                          (read-string "Switches for listing: "
-                                       (or (and (boundp 'dired-subdir-switches)  dired-subdir-switches)
-                                           dired-actual-switches)))))
-  (let ((opoint    (point))
-        (filename  dirname))
-    (cond ((consp filename)             ; Subdir header line or non-directory file.
-           (setq filename  (car filename))
-           (if (assoc filename dired-subdir-alist)
-               (dired-goto-file filename) ;  Subdir header line.
-             (dired-insert-subdir (substring (file-name-directory filename) 0 -1))))
-          (t
-           ;; We don't need a marker for opoint as the subdir is always
-           ;; inserted *after* opoint.
-           (setq dirname  (file-name-as-directory dirname))
-           (or (and (not switches)  (dired-goto-subdir dirname))
-               (dired-insert-subdir dirname switches no-error-if-not-dir-p))
-           ;; Push mark so that it's easy to go back.  Do this after the
-           ;; insertion message so that the user sees the `Mark set' message.
-           (push-mark opoint)
-           (when (and (get-buffer-window (current-buffer)) ; Fit one-window frame.
-                      (fboundp 'fit-frame-if-one-window)) ; In `autofit-frame.el'.
-             (fit-frame-if-one-window))))))
-
-(defun diredp-this-subdir ()
-  "This line's filename, if directory, or `dired-current-directory' list.
-If on a directory line, then return the directory name.
-Else return a singleton list of a directory name, which is as follows:
-  If on a subdirectory header line (either of the two lines), then use
-  that subdirectory name.  Else use the parent directory name."
-  (or (let ((file  (dired-get-filename nil t)))
-        (and file
-             (file-directory-p file)
-             (not (member (file-relative-name file (file-name-directory (directory-file-name file)))
-                          '("." ".." "./" "../")))
-             file))
-      (list (dired-current-directory))))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'
-;;
-;; 1. Added optional arg FROM, which is also listed by `l' when prompted.
-;; 2. Added missing doc string.
-;;
-(defun dired-handle-overwrite (to &optional from)
-  "Save old version of file TO that is to be overwritten.
-`dired-overwrite-confirmed' and `overwrite-backup-query' are fluid vars
-from `dired-create-files'.
-
-Optional arg FROM is a file being copied or renamed to TO.  It is used
-only when a user hits `l' to list files when asked whether to
-overwrite."
-  (let (backup)
-    (when (and dired-backup-overwrite
-               dired-overwrite-confirmed
-               (setq backup (car (find-backup-file-name to)))
-               (or (eq 'always dired-backup-overwrite)
-                   (dired-query 'overwrite-backup-query "Make backup for existing file `%s'? " to from)))
-      (rename-file to backup 0)         ; Confirm overwrite of old backup.
-      (dired-relist-entry backup))))
-
-
-(when (fboundp 'dired-copy-file-recursive) ; Emacs 22+
-
-
-  ;; REPLACE ORIGINAL in `dired-aux.el'
-  ;;
-  ;; 1. Pass also FROM to `dired-handle-overwrite', so `l' lists it too.
-  ;; 2. Added missing doc string.
-  ;;
-  (defun dired-copy-file (from to ok-if-already-exists)
-    "Copy file FROM to location TO.
-Non-nil arg OK-IF-ALREADY-EXISTS is passed to `copy-file' or
- `make-symbolic-link'.
-Preserves the last-modified date when copying, unless
-`dired-copy-preserve-time' is nil."
-    (dired-handle-overwrite to from)
-    (dired-copy-file-recursive from to ok-if-already-exists dired-copy-preserve-time t dired-recursive-copies))
-
-
-  ;; REPLACE ORIGINAL in `dired-aux.el'
-  ;;
-  ;; 1. Pass also FROM to `dired-handle-overwrite', so `l' lists it too.
-  ;; 2. Added missing doc string.
-  ;;
-  (defun dired-copy-file-recursive (from to ok-if-already-exists &optional keep-time top recursive)
-    "Copy file FROM to location TO, handling directories in FROM recursively.
-Non-nil arg OK-IF-ALREADY-EXISTS is passed to `copy-file' or
- `make-symbolic-link'.
-Non-nil optional arg KEEP-TIME is passed to `copy-file' or
- `copy-directory'.
-Non-nil optional arg TOP means do not bother with `dired-handle-overwrite'.
-Non-nil optional arg RECURSIVE means recurse on any directories in
- FROM, after confirmation if RECURSIVE is not `always'."
-    (when (and (eq t (car (file-attributes from)))  (file-in-directory-p to from))
-      (error "Cannot copy `%s' into its subdirectory `%s'" from to))
-    (let ((attrs  (file-attributes from)))
-      (if (and recursive
-               (eq t (car attrs))
-               (or (eq recursive 'always)  (yes-or-no-p (format "Recursive copies of %s? " from))))
-          (copy-directory from to keep-time)
-        (or top  (dired-handle-overwrite to from))
-        (condition-case err
-            (if (stringp (car attrs))   ; It is a symlink
-                (make-symbolic-link (car attrs) to ok-if-already-exists)
-              (copy-file from to ok-if-already-exists keep-time))
-          (file-date-error
-           (push (dired-make-relative from) dired-create-files-failures)
-           (dired-log "Can't set date on %s:\n%s\n" from err))))))
-
-  )
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'
-;;
-;; 1. Pass also FILE to `dired-handle-overwrite', so `l' lists it too.
-;; 2. Added missing doc string.
-;;
-(defun dired-rename-file (file newname ok-if-already-exists)
-  "Rename FILE to NEWNAME.
-Non-nil arg OK-IF-ALREADY-EXISTS is passed to `rename-file'."
-  (dired-handle-overwrite newname file)
-  (rename-file file newname ok-if-already-exists) ; Error is caught in `-create-files'.
-  ;; Silently rename the visited file of any buffer visiting this file.
-  (and (get-file-buffer file)  (with-current-buffer (get-file-buffer file) (set-visited-file-name newname nil t)))
-  (dired-remove-file file)
-  ;; See if it's an inserted subdir, and rename that, too.
-  (dired-rename-subdir file newname))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'
-;;
-;; Pass also FILE to `dired-handle-overwrite', so `l' lists it too.
-;;
-(defun dired-hardlink (file newname &optional ok-if-already-exists)
-  "Give FILE additional name NEWNAME.
-Non-nil arg OK-IF-ALREADY-EXISTS is passed to `add-name-to-file'."
-  (dired-handle-overwrite newname file)
-  (add-name-to-file file newname ok-if-already-exists) ; Error is caught in -create-files'.
-  (dired-relist-file file))             ; Update the link count.
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; No-op: does nothing now.
-;;
-(defun dired-insert-subdir-validate (dirname &optional switches))
-
-
-;;; $$$$$$$$
-;;; ;; REPLACE ORIGINAL in `dired-aux.el'.
-;;; ;;
-;;; ;; 1. Do not require that DIRNAME be in the current directory tree (no error if not).
-;;; ;; 2. Use `dolist' instead of `mapcar'.
-;;; ;;
-;;; (defun dired-insert-subdir-validate (dirname &optional switches)
-;;;   "Raise an error if it is invalid to insert DIRNAME with SWITCHES."
-;;; ;;; (or (dired-in-this-tree dirname (expand-file-name default-directory)) ; REMOVED
-;;; ;;;     (error  "%s: not in this directory tree" dirname))
-;;;   (let ((real-switches  (or switches  (and (boundp 'dired-subdir-switches) ; Emacs 22+
-;;;                                            dired-subdir-switches))))
-;;;     (when real-switches
-;;;       (let (case-fold-search)
-;;;         (dolist (switchs  '("F" "b"))   ; Switches that matter for `dired-get-filename'.
-;;;           (unless (eq (null (diredp-string-match-p switchs real-switches))
-;;;                       (null (diredp-string-match-p switchs dired-actual-switches)))
-;;;             (error "Can't have dirs with and without `-%s' switches together" switchs)))))))
-
-
-;; REPLACE ORIGINAL in `dired-aux.el'.
-;;
-;; If NEW-DIR is not a descendant of a directory in the buffer, put it at eob.
-;;
-(defun dired-insert-subdir-newpos (new-dir)
-  "Move to the proper position for inserting NEW-DIR, and return it.
-Respect the order within each directory tree.  But if NEW-DIR is not a
-descendant of any directory in the buffer, then put it at the end."
-  (let ((alist  dired-subdir-alist)
-        elt dir new-pos)
-    (while alist
-      (setq elt    (car alist)
-            alist  (cdr alist)
-            dir    (car elt))
-      (if (dired-tree-lessp dir new-dir)
-          (setq new-pos  (dired-get-subdir-max elt) ; Position NEW-DIR after DIR.
-                alist    ())
-        (setq new-pos  (point-max))))
-    (goto-char new-pos))
-  (unless (eobp) (forward-line -1))
-  (insert "\n")
-  (point))
-
-
-;; This is like original `dired-hide-subdir' in `dired-aux.el', except:
-;;
-;; 1. Plain prefix arg means invoke `dired-hide-all'.  Added optional arg NEXT.
-;; 2. Do not move to the next subdir.
-;; 3. Modified to work with also with older Emacs versions.
-;;
-(defun diredp-hide-subdir-nomove (arg &optional next)
-  "Hide or unhide the current directory.
-Unlike `dired-hide-subdir', this does not advance the cursor to the
-next directory header line.
-
-With a plain prefix arg (`C-u'), invoke `dired-hide-all' to hide or
- show everything.
-With a numeric prefix arg N, hide this subdirectory and the next N-1
- subdirectories."
-  (interactive "P")
-  (dired-hide-check)
-  (if (consp arg)
-      (dired-hide-all 'IGNORED)         ; Arg needed for older Emacs versions.
-    (setq arg  (prefix-numeric-value arg))
-    (let ((modflag  (buffer-modified-p)))
-      (while (>=  (setq arg  (1- arg)) 0)
-        (let* ((cur-dir   (dired-current-directory))
-               (hidden-p  (dired-subdir-hidden-p cur-dir))
-               (elt       (assoc cur-dir dired-subdir-alist))
-               (end-pos   (1- (dired-get-subdir-max elt)))
-               buffer-read-only)
-          (goto-char (dired-get-subdir-min elt)) ; Keep header line visible, hide rest
-          (skip-chars-forward "^\n\r")
-          (if hidden-p
-              (subst-char-in-region (point) end-pos ?\r ?\n)
-            (subst-char-in-region (point) end-pos ?\n ?\r)))
-        (when next (dired-next-subdir 1 t)))
-      (if (fboundp 'restore-buffer-modified-p)
-          (restore-buffer-modified-p modflag)
-        (set-buffer-modified-p modflag)))))
-
-;;; ----------------------
-;;; If we instead renamed `diredp-hide-subdir-nomove' to `dired-hide-subdir' as a replacement,
-;;; then we would define things this way:
-;;;
-;;;
-;;; ;; REPLACE ORIGINAL in `dired-aux.el'.
-;;; ;;
-;;; ;; 1. Plain prefix arg means invoke `dired-hide-all'.  Added optional arg NEXT.
-;;; ;;
-;;; ;; 2. Do not move to the next subdir.
-;;; ;;
-;;; ;; 3. Modified to work with also with older Emacs versions.
-;;; ;;
-;;; (defun dired-hide-subdir (arg &optional next)
-;;;   "Hide or unhide the current directory.
-;;; Unlike `diredp-hide-subdir-goto-next', this does not advance the
-;;; cursor to the next directory header line.
-;;;
-;;; With a plain prefix arg (`C-u'), invoke `dired-hide-all' to hide or
-;;;  show everything.
-;;; With a numeric prefix arg N, hide this subdirectory and the next N-1
-;;;  subdirectories."
-;;;   (interactive "P")
-;;;   (dired-hide-check)
-;;;   (if (consp arg)
-;;;       (dired-hide-all 'IGNORED)         ; Arg needed for older Emacs versions.
-;;;     (setq arg  (prefix-numeric-value arg))
-;;;     (let ((modflag  (buffer-modified-p)))
-;;;       (while (>=  (setq arg  (1- arg)) 0)
-;;;         (let* ((cur-dir   (dired-current-directory))
-;;;                (hidden-p  (dired-subdir-hidden-p cur-dir))
-;;;                (elt       (assoc cur-dir dired-subdir-alist))
-;;;                (end-pos   (1- (dired-get-subdir-max elt)))
-;;;                buffer-read-only)
-;;;           (goto-char (dired-get-subdir-min elt)) ; Keep header line visible, hide rest
-;;;           (skip-chars-forward "^\n\r")
-;;;           (if hidden-p
-;;;               (subst-char-in-region (point) end-pos ?\r ?\n)
-;;;             (subst-char-in-region (point) end-pos ?\n ?\r)))
-;;;         (when next (dired-next-subdir 1 t)))
-;;;       (if (fboundp 'restore-buffer-modified-p)
-;;;           (restore-buffer-modified-p modflag)
-;;;         (set-buffer-modified-p modflag)))))
-;;;
-;;; (defun diredp-hide-subdir-goto-next (arg)
-;;;   "Hide or unhide current directory and move to next directory header line."
-;;;   (interactive "P")
-;;;   (dired-hide-subdir arg 'NEXT))
-;;; ----------------------
-
-
-;; REPLACE ORIGINAL in `dired-x.el'.
-;;
-;; Fix the `interactive' spec.  This is the Emacs 24+ version, provided for earlier versions.
-;;
-(unless (> emacs-major-version 23)
-  (defun dired-mark-unmarked-files (regexp msg &optional unflag-p localp)
-    "Mark unmarked files matching REGEXP, displaying MSG.
-REGEXP is matched against the entire file name.  When called
-interactively, prompt for REGEXP.
-With prefix argument, unflag all those files.
-
-Non-interactively:
- Returns t if any work was done, nil otherwise.
- Optional fourth argument LOCALP is as in `dired-get-filename'."
-    (interactive (list (diredp-read-regexp "Mark unmarked files matching regexp (default all): ")
-                       nil
-                       current-prefix-arg
-                       nil))
-    (let ((dired-marker-char  (if unflag-p ?\   dired-marker-char))
-          (unmarkedp          (eq (char-after) ?\   )))
-      (diredp-mark-if (and (if unflag-p (not unmarkedp) unmarkedp) ; Fixes Emacs bug #27465.
-                           (let ((fn  (dired-get-filename localp 'NO-ERROR))) ; Uninteresting
-                             (and fn  (diredp-string-match-p regexp fn))))
-                      msg))))
-
-
-;; REPLACE ORIGINAL in `dired-x.el'.
-;;
-;; 1. Call `dired-get-marked-files' with original ARG, to get its multi-`C-u' behavior.
-;; 2. Doc string updated to reflect change to `dired-simultaneous-find-file'.
-;; 3. Added optional arg INTERACTIVEP.
-;; 4. Do not raise error if no files when not INTERACTIVEP.
-;;
-;;;###autoload
-(defun dired-do-find-marked-files (&optional arg interactivep) ; Bound to `F'
-  "Find marked files, displaying all of them simultaneously.
-With no prefix argument:
-
-* If `pop-up-frames' is nil then split the current window across all
-  marked files, as evenly as possible.  Remaining lines go to the
-  bottom-most window.  The number of files that can be displayed this
-  way is restricted by the height of the current window and
-  `window-min-height'.
-
-* If `pop-up-frames' is non-nil then show each marked file in a
-  separate frame (not window).
-
-With a prefix argument:
-
-* One or more plain `C-u' behaves as for `dired-get-marked-files'.
-  In particular, `C-u C-u' means ignore any markings and operate on
-  ALL files and directories (except `.' and `..') in the Dired buffer.
-
-* A numeric prefix arg >= 0 means just find (visit) the marked files -
-  do not show them.
-
-* A numeric prefix arg < 0 means show each marked file in a separate
-  frame (not window).  (This is the same behavior as no prefix arg
-  with non-nil `pop-up-frames'.)
-
-Note that a numeric prefix argument acts differently with this command
-than it does with other `dired-do-*' commands: it does NOT act on the
-next or previous (abs ARG) files, ignoring markings.
-
-To keep the Dired buffer displayed, split the window (e.g., `C-x 2')
-first.  To show only the marked files, type `\\[delete-other-windows]' first.
-
-When invoked interactively, raise an error if no files are marked."
-  (interactive "P\np")
-  (dired-simultaneous-find-file
-   (dired-get-marked-files nil (and (consp arg)  arg) nil nil interactivep)
-   (and arg  (prefix-numeric-value arg))))
-
-
-;; REPLACE ORIGINAL in `dired-x.el'.
-;;
-;; Use separate frames instead of windows if `pop-up-frames' is non-nil,
-;; or if prefix arg is negative.
-;;
-(defun dired-simultaneous-find-file (file-list option)
-  "Visit all files in list FILE-LIST and display them simultaneously.
-With non-nil OPTION >= 0, the files are found (visited) but not shown.
-
-If `pop-up-frames' is non-nil or if OPTION < 0, use a separate frame
-for each file.  (See also option `diredp-max-frames'.)
-
-Otherwise, the current window is split across all files in FILE-LIST,
-as evenly as possible.  Remaining lines go to the bottom-most window.
-The number of files that can be displayed this way is restricted by
-the height of the current window and the value of variable
-`window-min-height'."
-  ;; This is not interactive because it is usually too clumsy to specify FILE-LIST interactively unless via dired.
-  (let (size)
-    (cond ((and option  (natnump option))
-           (while file-list (find-file-noselect (car file-list)) (pop file-list)))
-          ((or pop-up-frames  option)
-           (let ((nb-files  (length file-list)))
-             (when (and (> nb-files diredp-max-frames)
-                        (not (y-or-n-p (format "Really show %d files in separate frames? " nb-files))))
-               (error "OK, canceled"))
-             (while file-list (find-file-other-frame (car file-list)) (pop file-list))))
-          (t
-           (setq size  (/ (window-height) (length file-list)))
-           (when (> window-min-height size) (error "Too many files to show simultaneously"))
-           (find-file (car file-list))
-           (pop file-list)
-           (while file-list
-             ;; Vertically split off a window of desired size. Upper window will have SIZE lines.
-             ;; Select lower (larger) window.  We split it again.
-             (select-window (split-window nil size))
-             (find-file (car file-list))
-             (pop file-list))))))
-
-
-;;;;;; REPLACE ORIGINAL in both `dired.el' and `dired-x.el':
-;;;;;;
-;;;;;; 1. This incorporates the `dired-x.el' change to the `dired.el'
-;;;;;;    definition.  This version works with or without using dired-x.
-;;;;;;    The `dired-x.el' version respects the var `dired-find-subdir'.
-;;;;;;    When `dired-find-subdir' is non-nil, this version is the same
-;;;;;;    as the `dired-x.el' version, except that a bug is corrected:
-;;;;;;    Whenever the argument to `dired-find-buffer-nocreate' is a cons,
-;;;;;;    the call to `dired-buffers-for-dir' gave a wrong type error.
-;;;;;;    This has been avoided by not respecting `dired-find-subdir'
-;;;;;;    whenever `dired-find-buffer-nocreate' is a cons.
-;;;;;;    For the case when `dired-find-subdir' is nil, see #2, below.
-;;;;;;
-;;;;;; 2. Unless `dired-find-subdir' is bound and non-nil:
-;;;;;;    If both DIRNAME and `dired-directory' are conses, then only
-;;;;;;    compare their cars (directories), not their explicit file lists
-;;;;;;    too.  If equal, then update `dired-directory's file list to that
-;;;;;;    of DIRNAME.
-;;;;;;
-;;;;;;    This prevents `dired-internal-noselect' (which is currently
-;;;;;;    `dired-find-buffer-nocreate's only caller) from creating a new
-;;;;;;    buffer in this case whenever a different set of files is present
-;;;;;;    in the cdr of DIRNAME and DIRNAME represents the same buffer as
-;;;;;;    `dired-directory'.
-;;;;;;
-;;;;;;    If only one of DIRNAME and `dired-directory' is a cons, then
-;;;;;;    this returns nil.
-;;;;;;;###autoload
-;;;;(defun dired-find-buffer-nocreate (dirname &optional mode)
-;;;;  (let ((atomic-dirname-p  (atom dirname)))
-;;;;    (if (and (boundp 'dired-find-subdir) dired-find-subdir atomic-dirname-p)
-;;;;        ;; This is the `dired-x.el' change:
-;;;;        (let* ((cur-buf (current-buffer))
-;;;;               (buffers (nreverse (dired-buffers-for-dir dirname)))
-;;;;               (cur-buf-matches (and (memq cur-buf buffers)
-;;;;                                     ;; Files list (wildcards) must match, too:
-;;;;                                     (equal dired-directory dirname))))
-;;;;          (setq buffers  (delq cur-buf buffers)) ; Avoid using same buffer---
-;;;;          (or (car (sort buffers (function dired-buffer-more-recently-used-p)))
-;;;;              (and cur-buf-matches cur-buf))) ; ---unless no other possibility.
-;;;;      ;; Comment from `dired.el':
-;;;;      ;;  This differs from `dired-buffers-for-dir' in that it doesn't consider
-;;;;      ;;  subdirs of `default-directory' and searches for the first match only.
-;;;;      (let ((blist dired-buffers)       ; was (buffer-list)
-;;;;            found)
-;;;;        (or mode (setq mode  'dired-mode))
-;;;;        (while blist
-;;;;          (if (null (buffer-name (cdr (car blist))))
-;;;;              (setq blist  (cdr blist))
-;;;;            (save-excursion
-;;;;              (set-buffer (cdr (car blist)))
-;;;;              (if (not (and (eq major-mode mode)
-;;;;                            ;; DIRNAME and `dired-directory' have the same dir,
-;;;;                            ;; and if either of them has an explicit file list,
-;;;;                            ;; then both of them do.  In that case, update
-;;;;                            ;; `dired-directory's file list from DIRNAME.
-;;;;                            (if atomic-dirname-p
-;;;;                                (and (atom dired-directory) ; Both are atoms.
-;;;;                                     (string= (file-truename dirname)
-;;;;                                              (file-truename dired-directory)))
-;;;;                              (and (consp dired-directory) ; Both are conses.
-;;;;                                   (string=
-;;;;                                    (file-truename (car dirname))
-;;;;                                    (file-truename (car dired-directory)))
-;;;;                                   ;; Update `dired-directory's file list.
-;;;;                                   (setq dired-directory  dirname)))))
-;;;;                  (setq blist  (cdr blist))
-;;;;                (setq found  (cdr (car blist)))
-;;;;                (setq blist  nil)))))
-;;;;        found))))
-
-
-;; REPLACE ORIGINAL in `dired-x.el'.
-;;
-;; Require confirmation.  Fixes Emacs bug #13561.
-;;
-(defun dired-do-run-mail ()
-  "If `dired-bind-vm' is non-nil, call `dired-vm', else call `dired-rmail'."
-  (interactive)
-  (unless (y-or-n-p "Read all marked mail folders? ") (error "OK, canceled"))
-  (if dired-bind-vm
-      ;; Read mail folder using vm.
-      (dired-vm)
-    ;; Read mail folder using rmail.
-    (dired-rmail)))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; 1. Put `mouse-face' on whole line, not just file name.
-;; 2. Add text property `dired-filename' to only the file name.
-;; 3. Show image-file preview on mouseover, if `tooltip-mode'
-;;    and if `diredp-image-preview-in-tooltip'.
-;;
-(defun dired-insert-set-properties (beg end)
-  "Add various text properties to the lines in the region.
-Highlight entire line upon mouseover.
-Add text property `dired-filename' to the file name.
-Handle `dired-hide-details-mode' invisibility spec (Emacs 24.4+)."
-  (let ((inhibit-field-text-motion  t)) ; Just in case.
-    (save-excursion
-      (goto-char beg)
-      (while (< (point) end)
-        (condition-case nil
-            (cond ((dired-move-to-filename)
-                   (add-text-properties (line-beginning-position) (line-end-position)
-                                        '(mouse-face highlight help-echo diredp-mouseover-help))
-                   (put-text-property
-                    (point) (save-excursion (dired-move-to-end-of-filename) (point))
-                    'dired-filename t)
-                   (when (fboundp 'dired-hide-details-mode) ; Emacs 24.4+
-                     (put-text-property (+ (line-beginning-position) 1) (1- (point))
-                                        'invisible 'dired-hide-details-detail)
-                     (dired-move-to-end-of-filename)
-                     (when (< (+ (point) 4) (line-end-position))
-                       (put-text-property (+ (point) 4) (line-end-position)
-                                          'invisible 'dired-hide-details-link))))
-                  ((fboundp 'dired-hide-details-mode) ; Emacs 24.4+
-                   (unless (or (diredp-looking-at-p "^$")  (diredp-looking-at-p dired-subdir-regexp))
-                     (put-text-property (line-beginning-position) (1+ (line-end-position))
-                                        'invisible 'dired-hide-details-information))))
-          (error nil))
-        (forward-line 1)))))
-
-(defun diredp-mouseover-help (window buffer pos)
-  "Show `help-echo' help for a file name, in Dired.
-If `tooltip-mode' is on and `diredp-image-preview-in-tooltip' says to
-show an image preview, then do so.  Otherwise, show text help."
-  (let ((image-dired-thumb-width   (or (and (wholenump diredp-image-preview-in-tooltip)
-                                            diredp-image-preview-in-tooltip)
-                                       image-dired-thumb-width))
-        (image-dired-thumb-height  (or (and (wholenump diredp-image-preview-in-tooltip)
-                                            diredp-image-preview-in-tooltip)
-                                       image-dired-thumb-height))
-        file)
-    (or (and (boundp 'tooltip-mode)  tooltip-mode
-             (fboundp 'image-file-name-regexp) ; Emacs 22+, `image-file.el'.
-             diredp-image-preview-in-tooltip
-             (condition-case nil
-                 (and (with-current-buffer buffer
-                        (save-excursion (goto-char pos)
-                                        (diredp-string-match-p
-                                         (image-file-name-regexp)
-                                         (setq file  (if (derived-mode-p 'dired-mode)
-                                                         (dired-get-filename nil 'NO-ERROR)
-                                                       ;; Make it work also for `diredp-list-files' listings.
-                                                       (buffer-substring-no-properties (line-beginning-position)
-                                                                                       (line-end-position)))))))
-                      (or (not diredp-auto-focus-frame-for-thumbnail-tooltip-flag)
-                          (progn (select-frame-set-input-focus (window-frame window)) t))
-                      (let ((img-file  (if (eq 'full diredp-image-preview-in-tooltip)
-                                           file
-                                         (diredp-image-dired-create-thumb file))))
-                        (propertize " " 'display (create-image img-file))))
-               (error nil)))
-        (if (fboundp 'describe-file)    ; Library `help-fns+.el'
-            "mouse-2: visit in another window, C-h RET: describe"
-          "mouse-2: visit this file/dir in another window"))))
-
-;; `dired-hide-details-mode' enhancements.
-(when (fboundp 'dired-hide-details-mode) ; Emacs 24.4+
-
-  (defun diredp-hide-details-if-dired ()
-    "In Dired mode hide details.  Outside Dired, do nothing."
-    (when (derived-mode-p 'dired-mode) (dired-hide-details-mode 1)))
-
-  ;; Use `eval' of list so file byte-compiled in Emacs 20 will be OK in later versions.
-  (eval '(define-globalized-minor-mode global-dired-hide-details-mode
-          dired-hide-details-mode diredp-hide-details-if-dired))
-
-  (eval '(define-minor-mode dired-hide-details-mode
-          "Hide details in Dired mode."
-          (and diredp-hide-details-propagate-flag  diredp-hide-details-last-state)
-          :group 'dired
-          (unless (derived-mode-p 'dired-mode) (error "Not a Dired buffer"))
-          (dired-hide-details-update-invisibility-spec)
-          (setq diredp-hide-details-toggled  t)
-          (when diredp-hide-details-propagate-flag
-            (setq diredp-hide-details-last-state  dired-hide-details-mode))
-          (if dired-hide-details-mode
-              (add-hook 'wdired-mode-hook 'dired-hide-details-update-invisibility-spec nil t)
-            (remove-hook 'wdired-mode-hook 'dired-hide-details-update-invisibility-spec t))))
-
-  (defun diredp-hide/show-details ()
-    "Hide/show details according to user options.
-If `diredp-hide-details-propagate-flag' is non-nil and details have
-never been hidden in the buffer, then hide/show according to your last
-hide/show choice in any other Dired buffer or, if no last choice,
-according to option `diredp-hide-details-initially-flag'."
-    (unless (or diredp-hide-details-toggled ; No op if hide/show already set.
-                (buffer-narrowed-p))    ; No-op when showing just newly copied file etc.
-      (cond (diredp-hide-details-propagate-flag
-             (dired-hide-details-mode (if diredp-hide-details-last-state 1 -1)))
-            (diredp-hide-details-initially-flag
-             (dired-hide-details-mode 1)))))
-
-  (add-hook 'dired-after-readin-hook #'diredp-hide/show-details)
-
-  (defun diredp-fit-frame-unless-buffer-narrowed ()
-    "Fit frame unless Dired buffer is narrowed.
-Requires library `autofit-frame.el'."
-    (when (and (get-buffer-window (current-buffer))  (not (buffer-narrowed-p)))
-      (fit-frame-if-one-window)))
-
-  ;; Fit frame only if not narrowed.  Put it on this hook because `dired-hide-details-mode' is
-  ;; invoked from `dired-after-readin-hook' via `diredp-hide/show-details', even for an update
-  ;; such as copying a file, where buffer is narrowed when invoked.
-  (when (fboundp 'fit-frame-if-one-window) ; In `autofit-frame.el'.
-    (add-hook 'dired-hide-details-mode-hook #'diredp-fit-frame-unless-buffer-narrowed)))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; Reset `mode-line-process' to nil.
-;;
-(when (< emacs-major-version 21)
-  (or (fboundp 'old-dired-revert)  (fset 'old-dired-revert (symbol-function 'dired-revert)))
-  (defun dired-revert (&optional arg noconfirm)
-    (setq mode-line-process  nil)        ; Set by, e.g., `find-dired'.
-    (old-dired-revert arg noconfirm)))
-
-;; Like `dired-up-directory', but go up to MS Windows drive if in top-level directory.
-;;
-;;;###autoload
-(defun diredp-up-directory (&optional other-window) ; Bound to `^'
-  "Run Dired on parent directory of current directory.
-Find the parent directory either in this buffer or another buffer.
-Creates a buffer if necessary.
-
-With a prefix arg, Dired the parent directory in another window.
-
-On MS Windows, if you are already at the root directory, invoke
-`diredp-w32-drives' to visit a navigable list of Windows drives."
-  (interactive "P")
-  (let* ((dir  (dired-current-directory))
-         (up   (file-name-directory (directory-file-name dir))))
-    (or (dired-goto-file (directory-file-name dir))
-        ;; Only try `dired-goto-subdir' if buffer has more than one dir.
-        (and (cdr dired-subdir-alist)  (dired-goto-subdir up))
-        (progn (if other-window (dired-other-window up) (dired up))
-               (dired-goto-file dir))
-        (and (memq system-type '(windows-nt ms-dos))  (diredp-w32-drives other-window)))))
-
-;;;###autoload
-(defun diredp-up-directory-reuse-dir-buffer (&optional other-window) ; Not bound
-  "Like `diredp-up-directory', but reuse Dired buffers.
-With a prefix arg, Dired the parent directory in another window.
-
-On MS Windows, moving up from a root Dired buffer does not kill that
-buffer (the Windows drives buffer is not really a Dired buffer)."
-  (interactive "P")
-  (let* ((dir      (dired-current-directory))
-         (dirfile  (directory-file-name dir))
-         (up       (file-name-directory dirfile)))
-    (or (dired-goto-file dirfile)
-        ;; Only try `dired-goto-subdir' if buffer has more than one dir.
-        (and (cdr dired-subdir-alist)  (dired-goto-subdir up)) ; It is a subdir inserted in current Dired.
-        (progn (diredp--reuse-dir-buffer-helper up nil nil other-window)
-               (dired-goto-file dir))
-        (and (memq system-type '(windows-nt ms-dos))  (diredp-w32-drives other-window)))))
-
-;; Differs from `dired-next-line' in both wraparound and respect of `goal-column'.
-;;
-;;;###autoload
-(defun diredp-next-line (arg)           ; Bound to `SPC', `n', `C-n', `down'
-  "Move down lines then position cursor at filename.
-If `goal-column' is non-nil then put the cursor at that column.
-Optional prefix ARG says how many lines to move; default is one line.
-
-If `diredp-wrap-around-flag' is non-nil then wrap around if none is
-found before the buffer end (buffer beginning, if ARG is negative).
-Otherwise, just move to the buffer limit."
-  (interactive (let ((narg  (prefix-numeric-value current-prefix-arg)))
-                 (when (and (boundp 'shift-select-mode)  shift-select-mode) (handle-shift-selection)) ; Emacs 23+
-                 (list narg)))          ; Equivalent to "^p"
-  (let* ((line-move-visual  nil)
-         ;; (goal-column       nil)
-
-         ;; Use `condition-case' and `(progn... t)' because Emacs < 22 `line-move' has no
-         ;; NO-ERROR arg and it always returns nil.
-         (no-more           (or (not (condition-case nil (progn (line-move arg) t) (error nil)))
-                                (if (< arg 0) (bobp) (eobp)))))
-    (when (and diredp-wrap-around-flag  no-more)
-      (let ((diredp-wrap-around-flag  nil))
-        (goto-char (if (< arg 0) (point-max) (point-min)))
-        (diredp-next-line arg)))
-    ;; We never want to move point into an invisible line.
-    (while (and (fboundp 'invisible-p)  ; Emacs 22+
-                (invisible-p (point))
-                (not (if (and arg  (< arg 0)) (bobp) (eobp))))
-      (forward-char (if (and arg  (< arg 0)) -1 1)))
-    (unless goal-column (dired-move-to-filename))))
-
-;; In Emacs < 22, `C-p' does not wrap around, because it never moves to the first header line.
-;;;###autoload
-(defun diredp-previous-line (arg)       ; Bound to `p', `C-p', `up'
-  "Move up lines then position cursor at filename.
-If `goal-column' is non-nil then put the cursor at that column.
-Optional prefix ARG says how many lines to move; default is one line.
-
-If `diredp-wrap-around-flag' is non-nil then wrap around if none is
-found before the buffer beginning (buffer end, if ARG is negative).
-Otherwise, just move to the buffer limit."
-  (interactive (let ((narg  (prefix-numeric-value current-prefix-arg)))
-                 (when (and (boundp 'shift-select-mode)  shift-select-mode) (handle-shift-selection)) ; Emacs 23+
-                 (list narg)))          ; Equivalent to "^p"
-  (diredp-next-line (- (or arg  1))))
-  
-;;;###autoload
-(defun diredp-next-dirline (arg &optional opoint) ; Bound to `>'
-  "Goto ARGth next directory file line.
-If `diredp-wrap-around-flag' is non-nil then wrap around if none is
-found before the buffer beginning (buffer end, if ARG is negative).
-Otherwise, raise an error or, if NO-ERROR-IF-NOT-FOUND is nil, return
-nil."
-  (interactive (let ((narg  (prefix-numeric-value current-prefix-arg)))
-                 (when (and (boundp 'shift-select-mode)  shift-select-mode) (handle-shift-selection)) ; Emacs 23+
-                 (list narg)))          ; Equivalent to "^p"
-  (or opoint  (setq opoint  (point)))
-  (if (if (> arg 0)
-          (re-search-forward dired-re-dir nil t arg)
-        (beginning-of-line)
-        (re-search-backward dired-re-dir nil t (- arg)))
-      (dired-move-to-filename)          ; user may type `i' or `f'
-    (if diredp-wrap-around-flag
-        (let ((diredp-wrap-around-flag  nil))
-          (goto-char (if (< arg 0) (point-max) (point-min)))
-          (diredp-next-dirline arg opoint))
-      (goto-char opoint)
-      (error "No more subdirectories"))))
-
-;;;###autoload
-(defun diredp-prev-dirline (arg)        ; Bound to `<'
-  "Goto ARGth previous directory file line."
-  (interactive (let ((narg  (prefix-numeric-value current-prefix-arg)))
-                 (when (and (boundp 'shift-select-mode)  shift-select-mode) (handle-shift-selection)) ; Emacs 23+
-                 (list narg)))          ; Equivalent to "^p"
-  (diredp-next-dirline (- arg)))
-
-;;;###autoload
-(defun diredp-next-subdir (arg &optional no-error-if-not-found no-skip) ; Bound to `C-M-n'
-  "Go to the next subdirectory, regardless of level.
-If ARG = 0 then go to this directory's header line.
-
-If `diredp-wrap-around-flag' is non-nil then wrap around if none is
-found before the buffer end (buffer beginning, if ARG is negative).
-Otherwise, raise an error or, if NO-ERROR-IF-NOT-FOUND is nil, return
-nil.
-
-Non-nil NO-SKIP means do not move to end of header line, and return
-the position moved to so far."
-  (interactive (let ((narg  (prefix-numeric-value current-prefix-arg)))
-                 (when (and (boundp 'shift-select-mode)  shift-select-mode) (handle-shift-selection)) ; Emacs 23+
-                 (list narg)))          ; Equivalent to "^p"
-  (let ((this-dir  (dired-current-directory))
-        pos index)
-    ;; `nth' with negative arg does not return nil but the first element
-    (setq index  (if diredp-wrap-around-flag
-                     (mod (- (dired-subdir-index this-dir) arg) (length dired-subdir-alist))
-                   (- (dired-subdir-index this-dir) arg))
-          pos    (and (>= index 0)  (dired-get-subdir-min (nth index dired-subdir-alist))))
-    (if pos
-        (progn (goto-char pos)
-               (or no-skip  (skip-chars-forward "^\n\r"))
-               (point))
-      (if no-error-if-not-found
-          nil                           ; Return nil if not found
-        (error "%s directory" (if (> arg 0) "Last" "First"))))))
-
-;;;###autoload
-(defun diredp-prev-subdir (arg &optional no-error-if-not-found no-skip) ; Bound to `C-M-p'
-  "Go to the previous subdirectory, regardless of level.
-When called interactively and not on a subdir line, go to this subdir's line.
-Otherwise, this is a mirror image of `diredp-next-subdir'."
-  ;;(interactive "^p")
-  (interactive
-   (list (if current-prefix-arg
-             (let ((narg  (prefix-numeric-value current-prefix-arg)))
-               (when (and (boundp 'shift-select-mode)  shift-select-mode) (handle-shift-selection)) ; Emacs 23+
-               narg)                    ; Equivalent to "^p"
-           ;; If on subdir start already then do not stay there.
-           (if (dired-get-subdir) 1 0))))
-  (diredp-next-subdir (- arg) no-error-if-not-found no-skip))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; 1. Test also ./ and ../, in addition to . and .., for error "Cannot operate on `.' or `..'".
-;; 2. Hack for Emacs 20-22, to expand `~/...'.
-;;
-(defun dired-get-filename (&optional localp no-error-if-not-filep)
-  "In Dired, return name of file mentioned on this line.
-Value returned normally includes the directory name.
-
-Optional arg LOCALP:
- `no-dir' means do not include directory name in result.
- `verbatim' means  return the name exactly as it occurs in the buffer.
- Any other non-nil value means construct the name relative to
-  `default-directory', which still might contain slashes if point is
-  in a subdirectory.
-
-Non-nil optional arg NO-ERROR-IF-NOT-FILEP means treat `.' and `..' as
-regular filenames and return nil if there is no filename on this line.
-Otherwise, an error occurs in these cases."
-  (let ((case-fold-search  nil)
-        (already-absolute  nil)
-        file p1 p2)
-    (save-excursion (when (setq p1  (dired-move-to-filename (not no-error-if-not-filep)))
-                      (setq p2  (dired-move-to-end-of-filename no-error-if-not-filep))))
-    ;; nil if no file on this line but `no-error-if-not-filep' is t:
-    (when (setq file  (and p1  p2  (buffer-substring p1 p2)))
-      ;; Get rid of the mouse-face property that file names have.
-      (set-text-properties 0 (length file) nil file)
-
-      ;; Unquote names quoted by `ls' or by `dired-insert-directory'.
-      ;; Prior to Emacs 23.3, this code was written using `read' (see commented code below),
-      ;; because that is faster than substituting \007 (4 chars) -> ^G (1 char) etc. in a loop.
-      ;; Unfortunately, that implementation required hacks such as dealing with filenames
-      ;; with quotation marks in their names.
-      (while (string-match (if (> emacs-major-version 21)
-                               "\\(?:[^\\]\\|\\`\\)\\(\"\\)" ; Shy group: Emacs 22+.
-                             "\\([^\\]\\|\\`\\)\\(\"\\)")
-                           file)
-        (setq file  (replace-match "\\\"" nil t file 1)))
-
-      ;; $$$ This was the code for that unquoting prior to Emacs 23.3:
-      ;; (setq file  (read (concat "\"" ; Some `ls -b' do not escape quotes.  But GNU `ls' is OK.
-      ;;                           (or (dired-string-replace-match
-      ;;                                "\\([^\\]\\|\\`\\)\"" file "\\1\\\\\"" nil t)
-      ;;                               file)
-      ;;                           "\"")))
-
-      ;; This sexp was added by Emacs 24, to fix bug #10469:
-      ;; Unescape any spaces escaped by `ls -b'.
-      ;; Other `-b' quotes, such as \t and \n, work transparently.
-      (when (dired-switches-escape-p dired-actual-switches)
-        (let ((start  0)
-              (rep    "")
-              (shift  -1))
-          (when (eq localp 'verbatim)  (setq rep    "\\\\"
-                                             shift  +1))
-          (while (string-match "\\(\\\\\\) " file start)
-            (setq file   (replace-match rep nil t file 1)
-                  start  (+ shift (match-end 0))))))
-
-      ;; $$$ This sexp was added by Emacs 23.3.
-      (when (memq system-type '(windows-nt ms-dos))
-        (save-match-data
-          (let ((start  0))
-            (while (string-match "\\\\" file start)
-              (aset file (match-beginning 0) ?/)
-              (setq start  (match-end 0))))))
-
-      ;; $$$ This sexp was added by Emacs 23.3.
-      ;; Hence we don't need to worry about converting `\\' back to `\'.
-      (setq file  (read (concat "\"" file "\"")))
-
-      ;; Above `read' returns a unibyte string if FILE contains eight-bit-control/graphic chars.
-      (when (and (fboundp 'string-to-multibyte) ; Emacs 22
-                 enable-multibyte-characters
-                 (not (multibyte-string-p file)))
-        (setq file  (string-to-multibyte file))))
-    (and file
-         (file-name-absolute-p file)
-         ;; A relative file name can start with ~.  Do not treat it as absolute in this context.
-         (not (eq (aref file 0) ?~))
-         (setq already-absolute  t))
-    (cond ((null file) nil)
-          ((eq localp 'verbatim) file)
-          ;; This is the essential `Dired+' change: Added ./ and ../, not just . and ..
-          ((and (not no-error-if-not-filep)  (member file '("." ".." "./" "../")))
-           (error "Cannot operate on `.' or `..'"))
-          ((and (eq localp 'no-dir)  already-absolute)
-           (file-name-nondirectory file))
-          (already-absolute
-           (let ((handler  (find-file-name-handler file nil)))
-             ;; check for safe-magic property so that we won't
-             ;; put /: for names that don't really need them.
-             ;; For instance, .gz files when auto-compression-mode is on.
-             (if (and handler  (not (get handler 'safe-magic)))
-                 (concat "/:" file)
-               file)))
-          ((eq localp 'no-dir) file)
-          ((equal (dired-current-directory) "/")
-           (setq file  (concat (dired-current-directory localp) file))
-           (let ((handler  (find-file-name-handler file nil)))
-             ;; check for safe-magic property so that we won't
-             ;; put /: for names that don't really need them.
-             ;; For instance, .gz files when auto-compression-mode is on.
-             (if (and handler  (not (get handler 'safe-magic)))
-                 (concat "/:" file)
-               file)))
-          ;; Ugly hack for Emacs < 23, for which `ls-lisp-insert-directory' can insert a subdir
-          ;; using `~/...'.  Expand `~/' for return value.
-          ((and (< emacs-major-version 23)  file  (file-name-absolute-p file)
-                (eq (aref file 0) ?~))
-           (expand-file-name file))
-          (t
-           (concat (dired-current-directory localp) file)))))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; 1. Fixes Emacs bug #7126: Did not work with arbitrary file list (cons arg to `dired').
-;; 2. Remove `/' from directory name before comparing with BASE.
-;;
-(when (< emacs-major-version 24)
-  (defun dired-goto-file (file)         ; Bound to `j'
-    "Go to line describing file FILE in this Dired buffer.
-FILE must be an absolute file name.
-Return buffer position on success, else nil."
-    ;; Loses if FILE contains control chars like "\007" for which `ls' inserts "?" or "\\007"
-    ;; into the buffer, so we won't find it in the buffer.
-    (interactive (prog1                 ; Let push-mark display its message
-                     (list (expand-file-name (read-file-name "Goto file: " (dired-current-directory))))
-                   (push-mark)))
-    (unless (file-name-absolute-p file) (error "File name `%s' is not absolute" file))
-    (setq file  (directory-file-name file)) ; does no harm if no directory
-    (let* ((case-fold-search  nil)
-           (dir               (file-name-directory file))
-           (found             nil))
-      ;; `Dired+': Added this sexp.
-      (save-excursion
-        (goto-char (point-min))
-        (let ((search-string  (replace-regexp-in-string "\^m" "\\^m" file nil t))
-              (here           nil))
-          (setq search-string  (replace-regexp-in-string "\\\\" "\\\\" search-string nil t))
-
-          ;; Escape whitespace.  Added per Emacs 24 addition in `unless' code below:
-          (when (and (dired-switches-escape-p dired-actual-switches)
-                     (diredp-string-match-p "[ \t\n]" search-string))
-            ;; FIXME: fix this for all possible file names (embedded control chars etc).
-            ;;        Need to escape everything that `ls -b' escapes.
-            (setq search-string  (replace-regexp-in-string " " "\\ "  search-string nil t)
-                  search-string  (replace-regexp-in-string "\t" "\\t" search-string nil t)
-                  search-string  (replace-regexp-in-string "\n" "\\n" search-string nil t)))
-
-          ;; Use HERE to ensure we do not keep searching for a directory entry.
-          (while (and (not (eobp))  (not found)  (not (equal here (point))))
-            (setq here  (point))
-            (if (search-forward (concat " " search-string) nil 'NO-ERROR)
-                ;; Must move to filename since an (actually correct) match could have been
-                ;; elsewhere on the line (e.g. "-" would match somewhere in permission bits).
-                (setq found  (dired-move-to-filename))
-              ;; If this isn't the right line, move forward to avoid trying this line again.
-              (forward-line 1)))))
-
-      (unless found
-        (save-excursion
-          ;; The difficulty here is to get the result of `dired-goto-subdir' without really
-          ;; calling it, if we don't have any subdirs.
-          (when (if (string= dir (expand-file-name default-directory))
-                    (goto-char (point-min))
-                  (and (cdr dired-subdir-alist)  (dired-goto-subdir dir)))
-            (let ((base      (file-name-nondirectory file))
-                  (boundary  (dired-subdir-max))
-                  search-string)
-              (setq search-string  (replace-regexp-in-string "\^m" "\\^m" base nil t)
-                    search-string  (replace-regexp-in-string "\\\\" "\\\\" search-string nil t))
-            
-              ;; Escape whitespace.  Sexp added by Emacs 24:
-              (when (and (dired-switches-escape-p dired-actual-switches)
-                         (diredp-string-match-p "[ \t\n]" search-string))
-                ;; FIXME: fix this for all possible file names (embedded control chars etc).
-                ;;        Need to escape everything that `ls -b' escapes.
-                (setq search-string  (replace-regexp-in-string " " "\\ " search-string nil t)
-                      search-string  (replace-regexp-in-string "\t" "\\t" search-string nil t)
-                      search-string  (replace-regexp-in-string "\n" "\\n" search-string nil t)))
-              (while (and (not found)
-                          ;; Filenames are preceded by SPC.  This makes the search faster
-                          ;; (e.g. for the filename "-"!).
-                          (search-forward (concat " " search-string) boundary 'move))
-                ;; `Dired+': Remove `/' from filename, then compare with BASE.
-                ;; Match could have BASE just as initial substring or
-                ;; or in permission bits or date or not be a proper filename at all.
-                (if (and (dired-get-filename 'no-dir t)
-                         (equal base (directory-file-name (dired-get-filename 'no-dir t))))
-                    ;; Must move to filename since an (actually correct) match could have been
-                    ;; elsewhere on the line (e.g. "-" would match somewhere in permission bits).
-                    (setq found  (dired-move-to-filename))
-                  ;; If this is not the right line, move forward to avoid trying this line again.
-                  (forward-line 1)))))))
-      (and found  (goto-char found))))) ; Return buffer position, or nil if not found.
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; If destination is in a hidden dir listing, open that listing and move to destination in it.
-;;
-(unless (< emacs-major-version 24)
-  (defun dired-goto-file (file)
-    "Go to line describing file FILE in this Dired buffer.
-FILE must be an absolute file name.
-Return buffer position on success, else nil."
-    ;; Loses if FILE contains control chars like "\007" for which `ls' inserts "?" or "\\007"
-    ;; into the buffer, so we won't find it in the buffer.
-    (interactive (prog1 (list (expand-file-name (read-file-name "Goto file: " (dired-current-directory))))
-                   (push-mark)))        ; Let push-mark display its message.
-    (unless (file-name-absolute-p file) (error "File name `%s' is not absolute" file))
-    (setq file  (directory-file-name file)) ; Does no harm if not a directory
-    (let* ((case-fold-search  nil)
-           (dir               (file-name-directory file))
-           (found
-            (or
-             ;; First, look for a listing under the absolute name.
-             (save-excursion (goto-char (point-min)) (dired-goto-file-1 file file (point-max)))
-             ;; Else look for it as a relative name.  The difficulty is to get the result
-             ;; of `dired-goto-subdir' without calling it, if we don't have any subdirs.
-             (save-excursion
-               (when (if (string= dir (expand-file-name default-directory))
-                         (goto-char (point-min))
-                       (and (cdr dired-subdir-alist)  (dired-goto-subdir dir)))
-                 (when (dired-subdir-hidden-p (dired-current-directory))
-                   (diredp-hide-subdir-nomove 1)) ; Open hidden parent directory.
-                 (dired-goto-file-1 (file-name-nondirectory file) file (dired-subdir-max)))))))
-      (and found  (goto-char found))))) ; Return buffer position, or nil if not found.
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; 1. Display a message to warn that flagged, not marked, files will be deleted.
-;; 2. Use `diredp-internal-do-deletions', so it works with all Emacs versions.
-;;
-;;;###autoload
-(defun dired-do-flagged-delete (&optional no-msg) ; Bound to `x'
-  "In Dired, delete the files flagged for deletion.
-NOTE: This deletes flagged, not marked, files.
-If arg NO-MSG is non-nil, no message is displayed.
-
-User option `dired-recursive-deletes' controls whether deletion of
-non-empty directories is allowed."
-  (interactive)
-  (unless no-msg
-    (ding)
-    (message "NOTE: Deletion of files flagged `%c' (not those marked `%c')"
-             dired-del-marker dired-marker-char)
-    ;; Too slow/annoying, but without it the message is never seen: (sit-for 2)
-    )
-  (let* ((dired-marker-char  dired-del-marker)
-         (regexp             (dired-marker-regexp))
-         (case-fold-search   nil))
-    (if (save-excursion (goto-char (point-min)) (re-search-forward regexp nil t))
-        (diredp-internal-do-deletions
-         ;; This cannot move point since last arg is nil.
-         (dired-map-over-marks (cons (dired-get-filename) (point)) nil)
-         nil
-         'USE-TRASH-CAN)                ; This arg is for Emacs 24+ only.
-      (unless no-msg (message "(No deletions requested.)")))))
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; 1. Display a message to warn that marked, not flagged, files will be deleted.
-;; 2. Use `diredp-internal-do-deletions', so it works with all Emacs versions.
-;;
-;;;###autoload
-(defun dired-do-delete (&optional arg)  ; Bound to `D'
-  "Delete all marked (or next ARG) files.
-NOTE: This deletes marked, not flagged, files.
-`dired-recursive-deletes' controls whether deletion of
-non-empty directories is allowed."
-  (interactive "P")
-  ;; This is more consistent with the file-marking feature than
-  ;; `dired-do-flagged-delete'.  But it can be confusing to the user,
-  ;; especially since this is usually bound to `D', which is also the
-  ;; `dired-del-marker'.  So offer this warning message:
-  (unless arg
-    (ding)
-    (message "NOTE: Deletion of files marked `%c' (not those flagged `%c')."
-             dired-marker-char dired-del-marker))
-  (diredp-internal-do-deletions
-   ;; This can move point if ARG is an integer.
-   (dired-map-over-marks (cons (dired-get-filename) (point)) arg)
-   arg
-   'USE-TRASH-CAN))                     ; This arg is for Emacs 24+ only.
-
-(defun diredp-internal-do-deletions (file-alist arg &optional trash)
-  "`dired-internal-do-deletions', but for any Emacs version.
-FILE-ALIST is an alist of files to delete, with their buffer positions.
-ARG is the prefix arg.  Filenames are absolute.
-Non-nil TRASH means use the trash can."
-  ;; \(car FILE-ALIST) *must* be the *last* (bottommost) file in the dired
-  ;; buffer.  That way as changes are made in the buffer they do not shift
-  ;; the lines still to be changed, so the (point) values in FILE-ALIST
-  ;; stay valid.  Also, for subdirs in natural order, a subdir's files are
-  ;; deleted before the subdir itself - the other way around would not work."
-  (setq file-alist  (delq nil file-alist)) ; nils could come from `dired-map-over-marks'.
-  (if (> emacs-major-version 23)
-      (dired-internal-do-deletions file-alist arg trash)
-    (dired-internal-do-deletions file-alist arg)))
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; Put window point at bob.  Fixes bug #12281.
-;;
-(when (and (> emacs-major-version 22)  (or (< emacs-major-version 24)
-                                           (and (= emacs-major-version 24)  (= emacs-minor-version 1))))
-  (defun dired-pop-to-buffer (buf)
-    "Pop up buffer BUF in a way suitable for Dired."
-    (let ((split-window-preferred-function
-           (lambda (window)
-             (or (and (let ((split-height-threshold  0)) (window-splittable-p (selected-window)))
-                      ;; Try to split the selected window vertically if that's possible.  (Bug#1806)
-                      (if (fboundp 'split-window-below) (split-window-below) (split-window-vertically)))
-                 (split-window-sensibly window))))
-          pop-up-frames)
-      (pop-to-buffer (get-buffer-create buf)))
-    (set-window-start (selected-window) (point-min))
-    (when dired-shrink-to-fit
-      ;; Try to not delete window when we want to display less than `window-min-height' lines.
-      (fit-window-to-buffer (get-buffer-window buf) nil 1))))
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; 1. Delete the window or frame popped up, afterward, and bury its buffer.
-;;    Fixes Emacs bug #7533.
-;;
-;; 2, If buffer is shown in a separate frame, do not show a menu bar for that frame.
-;;
-(defun dired-mark-pop-up (buffer-or-name op-symbol files function &rest args)
-  "Return FUNCTION's result on ARGS after showing which files are marked.
-Displays the file names in a buffer named BUFFER-OR-NAME, the default
-name being \" *Marked Files*\".  The buffer is not shown if there is
-just one file, `dired-no-confirm' is t, or OP-SYMBOL is a member of
-the list in `dired-no-confirm'.  Uses function `dired-pop-to-buffer'
-to show the buffer.
-
-The window is not shown if there is just one file, `dired-no-confirm'
-is `t', or OP-SYMBOL is a member of `dired-no-confirm'.
-
-FILES is the list of marked files.  It can also be (t FILENAME)
-in the case of one marked file, to distinguish that from using
-just the current file.
-
-FUNCTION should not manipulate the files.  It should just read input
-\(an argument or confirmation)."
-  (unless buffer-or-name (setq buffer-or-name  " *Marked Files*"))
-  (let (result)
-    (if (or (eq dired-no-confirm t)
-            (memq op-symbol dired-no-confirm)
-            ;; If FILES defaulted to the current line's file.
-            (= (length files) 1))
-        (setq result  (apply function args))
-      (with-current-buffer (get-buffer-create buffer-or-name)
-        (erase-buffer)
-        ;; Handle (t FILE) just like (FILE), here.  That value is used (only in some cases),
-        ;; to mean just one file that was marked, rather than the current-line file.
-        (dired-format-columns-of-files (if (eq (car files) t) (cdr files) files))
-        (remove-text-properties (point-min) (point-max)
-                                '(mouse-face nil help-echo nil)))
-      (unwind-protect
-           (save-window-excursion
-             ;; Do not show menu bar, if buffer is popped up in a separate frame.
-             (let ((special-display-frame-alist  (cons '(menu-bar-lines . 0)
-                                                       special-display-frame-alist))
-                   (default-frame-alist          (cons '(menu-bar-lines . 0)
-                                                       default-frame-alist)))
-               (dired-pop-to-buffer buffer-or-name)
-               ;; Work around Emacs 22 bug in `dired-pop-to-buffer', which can exit with Dired buffer current.
-               (set-buffer buffer-or-name)
-               (goto-char (point-min)))
-             (setq result  (apply function args)))
-        (save-excursion
-          (condition-case nil           ; Ignore error if user already deleted window.
-              (progn (select-window (get-buffer-window buffer-or-name 0))
-                     (if (one-window-p) (delete-frame) (delete-window)))
-            (error nil)))
-        (bury-buffer buffer-or-name)))
-    result))
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; 1. Prefix arg has more possibilities.
-;; 2, Added optional arg LOCALP, so you can mark/unmark matching different file-name forms.
-;; 3. Push REGEXP onto `regexp-search-ring'.
-;;
-;;;###autoload
-(defun dired-mark-files-regexp (regexp &optional marker-char localp)
-  "Mark all file names matching REGEXP for use in later commands.
-`.' and `..' are never marked or unmarked by this command.
-
-Whether to mark or unmark, and what form of file name to match, are
-governed by the prefix argument.  For this, a plain (`C-u') or a
-double-plain (`C-u C-u') prefix arg is considered only as such - it is
-not considered numerically.
-
-Whether to mark or unmark:
-
- - No prefix arg, a positive arg, or a negative arg means mark.
-
- - Plain (`C-u'), double-plain (`C-u C-u'), or zero (e.g. `M-0' means
-   unmark.
-
-The form of a file name used for matching:
-
- - No prefix arg (to mark) or a plain prefix arg (`C-u', to unmark)
-   means use the relative file name (no directory part).
-
- - A negative arg (e.g. `M--', to mark) or a zero arg (e.g. `M-0', to
-   unmark) means use the absolute file name, that is, including all
-   directory components.
-
- - A positive arg (e.g. `M-+', to mark) or a double plain arg (`C-u
-   C-u', to unmark) means construct the name relative to
-   `default-directory'.  For an entry in an inserted subdir listing,
-   this means prefix the relative file name (no directory part) with
-   the subdir name relative to `default-directory'.
-
-Note that the default matching behavior of this command is different
-for Dired+ than it is for vanilla Emacs.  Using a positive prefix arg
-or a double plain prefix arg (`C-u C-u') gives you the same behavior
-as vanilla Emacs (marking or unmarking, respectively): matching
-against names that are relative to the `default-directory'.
-
-What Dired+ offers in addition is the possibility to match against
-names that are relative (have no directory part - no prefix arg or
-`C-u' to mark and unmark, respectively) or absolute (`M--' or `M-0',
-respectively).  The default behavior uses relative names because this
-is likely to be the more common use case.  But matching against
-absolute names gives you more flexibility.
-
-REGEXP is an Emacs regexp, not a shell wildcard.  Thus, use `\\.o$'
-for object files--just `.o' might mark more than you might expect.
-
-REGEXP is added to `regexp-search-ring', for regexp search.
-
-Non-interactively:
- MARKER-CHAR is the marker character - used for `dired-marker-char'.
- LOCALP is passed to `dired-get-filename'.  It determines the form of
-   filename that is matched against REGEXP."
-  (interactive (let* ((raw      current-prefix-arg)
-                      (C-u      (and (consp raw)  (= 4 (car raw))))
-                      (C-u-C-u  (and (consp raw)  (= 16 (car raw))))
-                      (num      (and raw  (prefix-numeric-value raw))))
-                 (list (diredp-read-regexp (concat (if (or (consp raw)  (and num  (zerop num)))
-                                                       "UNmark"
-                                                     "Mark")
-                                                   " files (regexp): "))
-                       (and raw  (or C-u  C-u-C-u  (zerop num))  ?\040)
-                       (cond ((or (not raw)  C-u)  t) ; none, `C-u' 
-                             ((> num 0)            nil) ; `M-+', `C-u C-u'
-                             (t                    'no-dir))))) ; `M--', `M-0'
-  (add-to-list 'regexp-search-ring regexp) ; Add REGEXP to `regexp-search-ring'.
-  (let ((dired-marker-char  (or marker-char  dired-marker-char)))
-    (diredp-mark-if (and (not (diredp-looking-at-p dired-re-dot))
-                         (not (eolp))   ; Empty line
-                         (let ((fn  (dired-get-filename localp t)))
-                           (and fn  (diredp-string-match-p regexp fn))))
-                    "file")))
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; Use `diredp-mark-if', not `dired-mark-if'.
-;;
-;;;###autoload
-(defun dired-mark-files-containing-regexp (regexp &optional marker-char)
-  "Mark files with contents containing a REGEXP match.
-A prefix argument means unmark them instead.
-`.' and `..' are never marked.
-
-If a file is visited in a buffer and `dired-always-read-filesystem' is
-nil, this looks in the buffer without revisiting the file, so the
-results might be inconsistent with the file on disk if its contents
-have changed since it was last visited."
-  (interactive
-   (list (diredp-read-regexp (concat (if current-prefix-arg "Unmark" "Mark") " files containing (regexp): ")
-                             nil 'dired-regexp-history)
-	 (and current-prefix-arg  ?\040)))
-  (let ((dired-marker-char  (or marker-char  dired-marker-char)))
-    (diredp-mark-if (and (not (diredp-looking-at-p dired-re-dot))
-                         (not (eolp))
-                         (let ((fname  (dired-get-filename nil t)))
-                           (when (and fname  (file-readable-p fname)  (not (file-directory-p fname)))
-                             (let ((prebuf  (get-file-buffer fname)))
-                               (message "Checking %s" fname)
-                               ;; For now, do it inside Emacs.  Grep might be better if there are lots of files.
-                               (if (and prebuf  (or (not (boundp 'dired-always-read-filesystem))
-                                                    (not dired-always-read-filesystem))) ; Emacs 26+
-                                   (with-current-buffer prebuf
-                                     (save-excursion (goto-char (point-min)) (re-search-forward regexp nil t)))
-                                 (with-temp-buffer
-                                   (insert-file-contents fname)
-                                   (goto-char (point-min))
-                                   (re-search-forward regexp nil t)))))))
-                    "file")))
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; Use `diredp-mark-if', not `dired-mark-if'.
-;;
-;;;###autoload
-(defun dired-mark-symlinks (unflag-p)
-  "Mark all symbolic links.
-With prefix argument, unmark or unflag all those files."
-  (interactive "P")
-  (let ((dired-marker-char  (if unflag-p ?\040 dired-marker-char)))
-    (diredp-mark-if (diredp-looking-at-p dired-re-sym) "symbolic link")))
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; Use `diredp-mark-if', not `dired-mark-if'.
-;;
-;;;###autoload
-(defun dired-mark-directories (unflag-p)
-  "Mark all directory file lines except `.' and `..'.
-With prefix argument, unmark or unflag the files instead."
-  (interactive "P")
-  (let ((dired-marker-char  (if unflag-p ?\040 dired-marker-char)))
-    (diredp-mark-if (and (diredp-looking-at-p dired-re-dir)  (not (diredp-looking-at-p dired-re-dot)))
-                    "directory" "directories")))
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; Use `diredp-mark-if', not `dired-mark-if'.
-;;
-;;;###autoload
-(defun dired-mark-executables (unflag-p)
-  "Mark all executable files.
-With prefix argument, unmark or unflag the files instead."
-  (interactive "P")
-  (let ((dired-marker-char  (if unflag-p ?\040 dired-marker-char)))
-    (diredp-mark-if (diredp-looking-at-p dired-re-exe) "executable file")))
-
-
-;; REPLACE ORIGINAL in `dired.el':
-;;
-;; Use `diredp-mark-if', not `dired-mark-if'.
-;;
-;;;###autoload
-(defun dired-flag-auto-save-files (&optional unflag-p)
-  "Flag for deletion files whose names suggest they are auto save files.
-A prefix argument says to unmark or unflag the files instead."
-  (interactive "P")
-  (let ((dired-marker-char  (if unflag-p ?\040 dired-del-marker)))
-    (diredp-mark-if
-     ;; It is less than general to check for # here, but it's the only way this runs fast enough.
-     (and (save-excursion (end-of-line)
-                          (or (eq (preceding-char) ?#)
-                              ;; Handle executables in case of -F option.  Need not worry about the other kinds
-                              ;; of markings that -F makes, since they won't appear on real auto-save files.
-                              (and (eq (preceding-char) ?*)
-                                   (progn (forward-char -1) (eq (preceding-char) ?#)))))
-	  (not (diredp-looking-at-p dired-re-dir))
-	  (let ((fname  (dired-get-filename t t)))
-	    (and fname  (auto-save-file-name-p (file-name-nondirectory fname)))))
-     "auto-save file")))
-
-;;;###autoload
-(defun diredp-capitalize (&optional arg) ; Bound to `% c'
-  "Rename all marked (or next ARG) files by capitalizing them.
-Makes the first char of the name uppercase and the others lowercase."
-  (interactive "P")
-  (dired-rename-non-directory #'capitalize "Rename by capitalizing:" arg))
-
-;; This is more useful than a single-file version of `dired-do-delete'.
-;;;###autoload
-(defun diredp-delete-this-file (&optional use-trash-can) ; Bound to `C-k', `delete'
-  "In Dired, delete the file on the cursor line, upon confirmation.
-This uses `delete-file'.
-If the file is a symlink, remove the symlink.  If the file has
-multiple names, it continues to exist with the other names.
-
-For Emacs 24 and later, a prefix arg means that if
-`delete-by-moving-to-trash' is non-nil then trash the file instead of
-deleting it."
-  (interactive "P")
-  (let ((file  (dired-get-filename)))
-    (if (not (yes-or-no-p (format "%s file `%s'? " (if (and use-trash-can  delete-by-moving-to-trash)
-                                                       "Trash"
-                                                     "Permanently delete")
-                                  file)))
-        (message "OK - canceled")
-      (if (> emacs-major-version 23) (delete-file file use-trash-can) (delete-file file))
-      (revert-buffer))))
-
-;;; Versions of `dired-do-*' commands for just this line's file.
-;;;###autoload
-(defun diredp-capitalize-this-file ()   ; Bound to `M-c'
-  "In Dired, rename the file on the cursor line by capitalizing it.
-Makes the first char of the name uppercase and the others lowercase."
-  (interactive) (diredp-capitalize 1))
-
-;;;###autoload
-(defun diredp-downcase-this-file ()     ; Bound to `M-l'
-  "In Dired, rename the file on the cursor line to lower case."
-  (interactive) (dired-downcase 1))
-
-;;;###autoload
-(defun diredp-upcase-this-file ()       ; Bound to `M-u'
-  "In Dired, rename the file on the cursor line to upper case."
-  (interactive) (dired-upcase 1))
-
-;;;###autoload
-(defun diredp-rename-this-file ()       ; Bound to `r'
-  "In Dired, rename the file on the cursor line."
-  (interactive)
-  (let ((use-file-dialog  nil)) (dired-do-rename 1)))
-
-(when (fboundp 'epa-dired-do-encrypt)   ; Emacs 23+
-  (defun diredp-decrypt-this-file ()
-    "In Dired, decrypt the file on the cursor line."
-    (interactive)
-    (let ((use-file-dialog  nil)) (epa-dired-do-decrypt 1)))
-
-  (defun diredp-encrypt-this-file ()
-    "In Dired, encrypt the file on the cursor line."
-    (interactive)
-    (let ((use-file-dialog  nil)) (epa-dired-do-encrypt 1)))
-
-  (defun diredp-verify-this-file ()
-    "In Dired, verify the file on the cursor line."
-    (interactive)
-    (let ((use-file-dialog  nil)) (epa-dired-do-verify 1)))
-
-  (defun diredp-sign-this-file ()
-    "In Dired, sign the file on the cursor line."
-    (interactive)
-    (let ((use-file-dialog  nil)) (epa-dired-do-sign 1))))
-
-;;;###autoload
-(defun diredp-copy-this-file ()         ; Not bound
-  "In Dired, copy the file on the cursor line."
-  (interactive)
-  (let ((use-file-dialog  nil)) (dired-do-copy 1)))
-
-;;;###autoload
-(defun diredp-relsymlink-this-file ()   ; Bound to `y'
-  "In Dired, make a relative symbolic link to file on cursor line."
-  (interactive)
-  (let ((use-file-dialog  nil)) (dired-do-relsymlink 1)))
-
-;;;###autoload
-(defun diredp-symlink-this-file ()      ; Not bound
-  "In Dired, make a symbolic link to the file on the cursor line."
-  (interactive)
-  (let ((use-file-dialog  nil)) (dired-do-symlink 1)))
-
-;;;###autoload
-(defun diredp-hardlink-this-file ()     ; Not bound
-  "In Dired, add a name (hard link) to the file on the cursor line."
-  (interactive)
-  (let ((use-file-dialog  nil)) (dired-do-hardlink 1)))
-
-;;;###autoload
-(defun diredp-print-this-file ()        ; Bound to `M-p'
-  "In Dired, print the file on the cursor line."
-  (interactive) (dired-do-print 1))
-
-;;;###autoload
-(defun diredp-grep-this-file ()         ; Not bound
-  "In Dired, grep the file on the cursor line."
-  (interactive)
-  (unless (and grep-command  (or (< emacs-major-version 22)
-                                 (not grep-use-null-device)
-                                 (eq grep-use-null-device t)))
-    (grep-compute-defaults))
-  (grep (diredp-do-grep-1 (list (dired-get-filename t)))))
-
-;;;###autoload
-(defun diredp-compress-this-file ()     ; Bound to `z'
-  "In Dired, compress or uncompress the file on the cursor line."
-  (interactive) (dired-do-compress 1))
-
-;;;###autoload
-(defun diredp-async-shell-command-this-file (command filelist) ; Not bound
-  "Run a shell COMMAND asynchronously on the file on the Dired cursor line.
-Like `diredp-shell-command-this-file', but adds `&' at the end of
-COMMAND to execute it asynchronously.  The command output appears in
-buffer `*Async Shell Command*'."
-  (interactive (list (dired-read-shell-command (concat "& on " "%s: ") 1 (list (dired-get-filename t)))
-                     (list (dired-get-filename t))))
-  (unless (diredp-string-match-p "&[ \t]*\\'" command) (setq command  (concat command " &")))
-  (dired-do-shell-command command 1 filelist))
-
-;;;###autoload
-(defun diredp-shell-command-this-file (command filelist) ; Not bound
-  "In Dired, run a shell COMMAND on the file on the cursor line."
-  (interactive (list (dired-read-shell-command (concat "! on " "%s: ") 1 (list (dired-get-filename t)))
-                     (list (dired-get-filename t))))
-  (dired-do-shell-command command 1 filelist))
-
-;;;###autoload
-(defun diredp-bookmark-this-file (&optional prefix) ; Bound to `C-B' (`C-S-b')
-  "In Dired, bookmark the file on the cursor line.
-See `diredp-do-bookmark'."
-  (interactive (progn (diredp-ensure-mode)
-                      (list (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: ")))))
-  (diredp-do-bookmark prefix 1))
-
-;;;###autoload
-(defun diredp-tag-this-file (tags &optional prefix) ; Bound to `T +'
-  "In Dired, add some tags to the file on the cursor line.
-You need library `bookmark+.el' to use this command."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (bmkp-read-tags-completing)
-                            (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: ")))))
-  (diredp-do-tag tags prefix 1))
-
-;;;###autoload
-(defun diredp-untag-this-file (tags &optional prefix arg) ; Bound to `T -'
-  "In Dired, remove some tags from the file on the cursor line.
-With a prefix arg, remove all tags from the file.
-You need library `bookmark+.el' to use this command."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (let* ((pref  (and diredp-prompt-for-bookmark-prefix-flag
-                                         (read-string "Prefix for bookmark name: ")))
-                             (bmk   (bmkp-get-autofile-bookmark (dired-get-filename) nil pref))
-                             (btgs  (and bmk  (bmkp-get-tags bmk))))
-                        (unless btgs (error "File has no tags to remove"))
-                        (list (if current-prefix-arg btgs (bmkp-read-tags-completing btgs))
-                              pref
-                              current-prefix-arg))))
-  (diredp-do-untag tags prefix 1))
-
-;;;###autoload
-(defun diredp-remove-all-tags-this-file (&optional prefix msgp) ; Bound to `T 0'
-  "In Dired, remove all tags from this file.
-You need library `bookmark+.el' to use this command."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            'MSG)))
-  (bookmark-maybe-load-default-file)
-  (diredp-do-remove-all-tags prefix 1))
-
-;;;###autoload
-(defun diredp-paste-add-tags-this-file (&optional prefix msgp) ; Bound to `T p', `T C-y'
-  "In Dired, add previously copied tags to this file.
-See `diredp-paste-add-tags'.
-You need library `bookmark+.el' to use this command."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            'MSG)))
-  (bookmark-maybe-load-default-file)
-  (diredp-do-paste-add-tags prefix 1))
-
-;;;###autoload
-(defun diredp-paste-replace-tags-this-file (&optional prefix msgp) ; Bound to `T q'
-  "In Dired, replace tags for this file with previously copied tags.
-See `diredp-paste-replace-tags'.
-You need library `bookmark+.el' to use this command."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            'MSG)))
-  (bookmark-maybe-load-default-file)
-  (diredp-do-paste-add-tags prefix 1))
-
-;;;###autoload
-(defun diredp-set-tag-value-this-file (tag value &optional prefix msgp) ; Bound to `T v'
-  "In Dired, Set value of TAG to VALUE for this file.
-See `diredp-set-tag-value'.
-You need library `bookmark+.el' to use this command."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (bmkp-read-tag-completing)
-                            (read (read-string "Value: "))
-                            (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            'MSG)))
-  (bookmark-maybe-load-default-file)
-  (diredp-do-set-tag-value tag value prefix 1))
-
-;;;###autoload
-(defun diredp-copy-tags-this-file (&optional prefix msgp) ; Bound to `T c', `T M-w'
-  "In Dired, copy the tags from this file, so you can paste them to another.
-See `diredp-copy-tags'.
-You need library `bookmark+.el' to use this command."
-  (interactive (progn (diredp-ensure-bookmark+)
-                      (diredp-ensure-mode)
-                      (list (and diredp-prompt-for-bookmark-prefix-flag
-                                 (read-string "Prefix for bookmark name: "))
-                            'MSG)))
-  (bookmark-maybe-load-default-file)
-  (let ((bmk  (bmkp-get-autofile-bookmark  (dired-get-filename) nil prefix)))
-    (and bmk  (bmkp-copy-tags bmk msgp))))
-
-;;;###autoload
-(defun diredp-mouse-copy-tags (event)   ; Not bound
-  "In Dired, copy the tags from this file, so you can paste them to another.
-You need library `bookmark+.el' to use this command."
-  (interactive "e")
-  (let ((mouse-pos         (event-start event))
-        (dired-no-confirm  t)
-        (prefix            (and diredp-prompt-for-bookmark-prefix-flag
-                                (read-string "Prefix for bookmark name: "))))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (diredp-copy-tags-this-file prefix 'MSG))
-  (diredp-previous-line 1))
-
-(when (fboundp 'describe-file)          ; In `help-fns+.el' or `help+20.el'.
-  (defun diredp-describe-file (&optional internal-form-p) ; Bound to `C-h RET', `C-h C-RET'
-    "In Dired, describe this file or directory.
-You need library `help-fns+.el' to use this command.
-If the file has an autofile bookmark and you use library `Bookmark+',
-then show also the bookmark information (tags etc.).  In this case, a
-prefix arg shows the internal form of the bookmark."
-    (interactive "P")
-    (describe-file (dired-get-filename nil t) internal-form-p))
-
-  (defun diredp-mouse-describe-file (event &optional internal-form-p) ; Not bound
-    "Describe the clicked file.
-You need library `help-fns+.el' to use this command.
-If the file has an autofile bookmark and you use library `Bookmark+',
-then show also the bookmark information (tags etc.).  In this case, a
-prefix arg shows the internal form of the bookmark."
-    (interactive "e\nP")
-    (let (file)
-      (with-current-buffer (window-buffer (posn-window (event-end event)))
-        (save-excursion (goto-char (posn-point (event-end event)))
-                        (setq file  (dired-get-filename nil t))))
-      (describe-file file internal-form-p))))
-
-;; Define these even if `Bookmark+' is not loaded.
-;;;###autoload
-(defalias 'diredp-show-metadata 'diredp-describe-autofile)
-;;;###autoload
-(defun diredp-describe-autofile (&optional internal-form-p)
-  "Show the metadata for the file of the current line.
-The file must name an autofile bookmark.  The metadata is the bookmark
-information.
-
-With a prefix argument, show the internal definition of the bookmark.
-
-You need library `bookmark+.el' for this command."
-  (interactive "P")
-  (diredp-ensure-bookmark+)
-  (diredp-ensure-mode)
-  (let ((bmk  (save-match-data
-                (bmkp-get-autofile-bookmark (dired-get-filename nil t)))))
-    (unless bmk (error "Not on an autofile bookmark"))
-    (save-selected-window (if internal-form-p
-                              (bmkp-describe-bookmark-internals bmk)
-                            (bmkp-describe-bookmark bmk)))))
-
-(defun diredp-mouse-describe-autofile (event &optional internal-form-p) ; Not bound
-  "Show the metadata for the file whose name you click.
-The file must name an autofile bookmark.  The metadata is the bookmark
-information.
-
-With a prefix argument, show the internal definition of the bookmark.
-
-You need library `bookmark+.el' for this command."
-  (interactive "e\nP")
-  (diredp-ensure-bookmark+)
-  (let (file)
-    (with-current-buffer (window-buffer (posn-window (event-end event)))
-      (diredp-ensure-mode)
-      (save-excursion (goto-char (posn-point (event-end event)))
-                      (setq file  (dired-get-filename nil t))))
-    (let ((bmk  (save-match-data (bmkp-get-autofile-bookmark file))))
-      (unless bmk (error "Not an autofile bookmark"))
-      (save-selected-window (if internal-form-p
-                                (bmkp-describe-bookmark-internals bmk)
-                              (bmkp-describe-bookmark bmk))))))
-
-;;;###autoload
-(defalias 'diredp-show-metadata-for-marked 'diredp-describe-marked-autofiles)
-;;;###autoload
-(defun diredp-describe-marked-autofiles (&optional internal-form-p interactivep details)
-  "Show metadata for the marked files.
-If no file is marked, describe ALL autofiles in this directory.
-With a prefix argument, show the internal (Lisp) form of the metadata.
-When invoked interactively, raise an error if no files are marked.
-You need library `bookmark+.el' for this command.
-
-When called from Lisp, optional arg DETAILS is passed to
-`diredp-get-files'."
-  (interactive (list current-prefix-arg t diredp-list-file-attributes))
-  (diredp-ensure-bookmark+)
-  (let ((help-xref-following  nil))
-    (help-setup-xref (list `(lambda (_buf)
-                             (with-current-buffer ,(current-buffer) (diredp-describe-marked-autofiles)))
-                           internal-form-p)
-                     (if (or (> emacs-major-version 23)
-                             (and (= emacs-major-version 23)  (> emacs-minor-version 1)))
-                         (called-interactively-p 'interactive)
-                       (interactive-p))))
-  (diredp-with-help-window "*Help*"
-    (let ((marked  (dired-get-marked-files nil nil nil 'DISTINGUISH-ONE-MARKED interactivep)))
-      (unless (cdr marked)
-        (message "Describing ALL autofiles here (none are marked)...")
-        (setq marked  (diredp-get-files 'IGNORE-MARKS-P nil nil nil nil details)))
-      (if (eq t (car marked))
-          (diredp-describe-autofile internal-form-p)
-        (dolist (bmk  (delq nil (mapcar #'bmkp-get-autofile-bookmark marked)))
-          (if internal-form-p
-              (let* ((bname      (bmkp-bookmark-name-from-record bmk))
-                     (help-text  (format "%s\n%s\n\n%s"
-                                         bname (make-string (length bname) ?-) (pp-to-string bmk))))
-                (princ help-text) (terpri))
-            (princ (bmkp-bookmark-description bmk)) (terpri)))))))
-
-;;;###autoload
-(defun diredp-byte-compile-this-file () ; Bound to `b'
-  "In Dired, byte compile the (Lisp source) file on the cursor line."
-  (interactive) (dired-do-byte-compile 1))
-
-;;;###autoload
-(defun diredp-load-this-file ()         ; Not bound
-  "In Dired, load the file on the cursor line."
-  (interactive) (dired-do-load 1))
-
-;;;###autoload
-(defun diredp-chmod-this-file ()        ; Bound to `M-m'
-  "In Dired, change the mode of the file on the cursor line."
-  (interactive) (dired-do-chmod 1))
-
-(unless (memq system-type '(windows-nt ms-dos))
-  (defun diredp-chgrp-this-file ()      ; Not bound
-    "In Dired, change the group of the file on the cursor line."
-    (interactive) (dired-do-chgrp 1)))
-
-(unless (memq system-type '(windows-nt ms-dos))
-  (defun diredp-chown-this-file ()      ; Not bound
-    "In Dired, change the owner of the file on the cursor line."
-    (interactive) (dired-do-chown 1)))
-
-(when (fboundp 'dired-do-touch)
-  (defun diredp-touch-this-file ()        ; Not bound
-    "In Dired, `touch' (change the timestamp of) the file on the cursor line."
-    (interactive) (dired-do-touch 1)))
-
-
-;; REPLACE ORIGINAL in `dired-x.el'.
-;;
-;; 1. Variable (symbol) `s' -> `blks'.
-;; 2. Fixes to remove leading space from `uid' and allow `.' in `gid'.
-;; 3. Cleaned up doc string and code a bit.
-;;
-;;;###autoload
-(defun dired-mark-sexp (predicate &optional unmark-p) ; Bound to `M-(', `* ('
-  "Mark files for which PREDICATE returns non-nil.
-With a prefix arg, unmark or unflag those files instead.
-
-PREDICATE is a lisp sexp that can refer to the following symbols as
-variables:
-
-    `mode'   [string]  file permission bits, e.g. \"-rw-r--r--\"
-    `nlink'  [integer] number of links to file
-    `size'   [integer] file size in bytes
-    `uid'    [string]  owner
-    `gid'    [string]  group (If the gid is not displayed by `ls',
-                       this will still be set (to the same as uid))
-    `time'   [string]  the time that `ls' displays, e.g. \"Feb 12 14:17\"
-    `name'   [string]  the name of the file
-    `sym'    [string]  if file is a symbolic link, the linked-to name,
-                       else \"\"
-    `inode'  [integer] the inode of the file (only for `ls -i' output)
-    `blks'   [integer] the size of the file for `ls -s' output
-                       (ususally in blocks or, with `-k', in Kbytes)
-Examples:
-  Mark zero-length files: `(equal 0 size)'
-  Mark files last modified on Feb 2: `(string-match \"Feb  2\" time)'
-  Mark uncompiled Emacs Lisp files (`.el' file without a `.elc' file):
-     First, Dired just the source files: `dired *.el'.
-     Then, use \\[dired-mark-sexp] with this sexp:
-          (not (file-exists-p (concat name \"c\")))
-
-There's an ambiguity when a single integer not followed by a unit
-prefix precedes the file mode: It is then parsed as inode number
-and not as block size (this always works for GNU coreutils ls).
-
-Another limitation is that the uid field is needed for the
-function to work correctly.  In particular, the field is not
-present for some values of `ls-lisp-emulation'.
-
-This function operates only on the Dired buffer content.  It does not
-refer at all to the underlying file system.  Contrast this with
-`find-dired', which might be preferable for the task at hand."
-  ;; Using `sym' = "", instead of nil, for non-linked files avoids the trap of
-  ;; (string-match "foo" sym) into which a user would soon fall.
-  ;; Use `equal' instead of `=' in the example, as it works on integers and strings.
-  (interactive "xMark if (vars: inode,blks,mode,nlink,uid,gid,size,time,name,sym): \nP")
-  (message "%s" predicate)
-  (let ((dired-marker-char  (if unmark-p ?\040 dired-marker-char))
-        (inode              nil)
-        (blks               ())
-        mode nlink uid gid size time name sym)
-    (diredp-mark-if
-     (save-excursion
-       (and
-        ;; Sets vars INODE BLKS MODE NLINK UID GID SIZE TIME NAME and SYM
-        ;; according to current file line.  Returns `t' for success, nil if
-        ;; there is no file line.  Upon success, these vars are set, to either
-        ;; nil or the appropriate value, so they need not be initialized.
-        ;; Moves point within the current line.
-        (dired-move-to-filename)
-        (let ((mode-len             10) ; Length of mode string.
-              ;; As in `dired.el', but with subexpressions \1=inode, \2=blks:
-              ;; GNU `ls -hs' suffixes the block count with a unit and prints it as a float; FreeBSD does neither.
-              ;; $$$$$$ (dired-re-inode-size  "\\s *\\([0-9]*\\)\\s *\\([0-9]*\\) ?")
-              (dired-re-inode-size (if (> emacs-major-version 24)
-                                       "\\=\\s *\\([0-9]+\\s +\\)?\
-\\(?:\\([0-9]+\\(?:\\.[0-9]*\\)?[BkKMGTPEZY]?\\)? ?\\)"
-                                     "\\s *\\([0-9]*\\)\\s *\\([0-9]*\\) ?"))
-              pos)
-          (beginning-of-line)
-          (forward-char 2)
-          (search-forward-regexp dired-re-inode-size nil t)
-          ;; XXX Might be a size not followed by a unit prefix.  Could set `blks' to `inode' if it were otherwise
-          ;; nil, with similar reasoning as for setting `gid' to `uid', but it would be even more whimsical.
-          (setq inode  (and (match-string 1)  (string-to-number (match-string 1)))
-                blks   (and (match-string 2)  (if (fboundp 'dired-x--string-to-number)
-                                                  (dired-x--string-to-number (match-string 2)) ; Emacs 25+
-                                                (string-to-number (match-string 2))))
-                mode   (buffer-substring (point) (+ mode-len (point))))
-          (forward-char mode-len)
-          (unless (eq (char-after) ?\   ) (forward-char 1)) ; Skip any extended attributes marker ("." or "+").
-          (setq nlink  (read (current-buffer)))
-          ;; Karsten Wenger <kw@cis.uni-muenchen.de> fixed uid.
-
-          ;; Another issue is that GNU `ls -n' right-justifies numerical UIDs and GIDs, while FreeBSD
-          ;; left-justifies them, so do not rely on a specific whitespace layout.  Both of them right-justify all
-          ;; other numbers, though.
-          ;; XXX Return a number if the `uid' or `gid' seems to be numerical?
-          ;; $$$$$$ (setq uid  (buffer-substring (+ (point) 1) (progn (forward-word 1) (point))))
-          (setq uid  (buffer-substring (progn (skip-chars-forward " \t")  (point))
-                                       (progn (skip-chars-forward "^ \t") (point))))
-          (cond ((> emacs-major-version 24)
-                 (dired-move-to-filename)
-                 (save-excursion
-                   (setq time
-                         ;; The regexp below tries to match from the last digit of the size field through a
-                         ;; space after the date.  Also, dates may have different formats depending on file age,
-                         ;; so the date column need not be aligned to the right.
-                         (buffer-substring (save-excursion (skip-chars-backward " \t") (point))
-                                           (progn (re-search-backward directory-listing-before-filename-regexp)
-                                                  (skip-chars-forward "^ \t")
-                                                  (1+ (point))))
-                         size
-                         (dired-x--string-to-number
-                          ;; We know that there's some kind of number before point because the regexp search
-                          ;; above succeeded.  Not worth doing an extra check for leading garbage.
-                          (buffer-substring (point) (progn (skip-chars-backward "^ \t") (point))))
-                         ;; If no `gid' is displayed, `gid' will be set to `uid' but user will then not reference
-                         ;; it anyway in PREDICATE.
-                         gid
-                         (buffer-substring (progn (skip-chars-backward " \t") (point))
-                                           (progn (skip-chars-backward "^ \t") (point)))))
-                 (setq name  (buffer-substring (point) (or (dired-move-to-end-of-filename t)  (point)))
-                       sym   (if (diredp-looking-at-p " -> ")
-                                 (buffer-substring (progn (forward-char 4) (point)) (line-end-position))
-                               "")))
-                (t
-                 (re-search-forward
-                  (if (< emacs-major-version 20)
-                      "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct\\|Nov\\|Dec\\)"
-                    dired-move-to-filename-regexp))
-                 (goto-char (match-beginning 1))
-                 (forward-char -1)
-                 (setq size  (string-to-number (buffer-substring (save-excursion (backward-word 1)
-                                                                                 (setq pos  (point)))
-                                                                 (point))))
-                 (goto-char pos)
-                 (backward-word 1)
-                 ;; if no `gid' is displayed, `gid' will be set to `uid' but user will then not reference
-                 ;; it anyway in PREDICATE.
-                 (setq gid   (buffer-substring (save-excursion (forward-word 1) (point)) (point))
-                       time  (buffer-substring (match-beginning 1) (1- (dired-move-to-filename)))
-                       name  (buffer-substring (point) (or (dired-move-to-end-of-filename t)  (point)))
-                       sym   (if (diredp-looking-at-p " -> ")
-                                 (buffer-substring (progn (forward-char 4) (point)) (line-end-position))
-                               "")))))
-        ;; Vanilla Emacs uses `lexical-binding' = t, and it passes bindings to `eval' as a second arg.
-        ;; We use `lexical-binding' = nil, and anyway there should be no need to pass the bindings.
-        (eval predicate)))
-     (format "'%s file" predicate))))
-
-(defun diredp-this-file-marked-p (&optional mark-char)
-  "Return non-nil if the file on this line is marked.
-Optional arg MARK-CHAR is the type of mark to check.
- If nil, then if the file has any mark, including `D', it is marked."
-  (and (dired-get-filename t t)  (save-excursion
-                                   (beginning-of-line)
-                                   (if mark-char
-                                       (diredp-looking-at-p
-                                        (concat "^" (regexp-quote (char-to-string mark-char))))
-                                     (not (diredp-looking-at-p "^ "))))))
-
-(defun diredp-this-file-unmarked-p (&optional mark-char)
-  "Return non-nil if the file on this line is unmarked.
-Optional arg MARK-CHAR is the type of mark to check.
- If nil, then if the file has no mark, including `D', it is unmarked.
- If non-nil, then it is unmarked for MARK-CHAR if it has no mark or
- it has any mark except MARK-CHAR."
-  (and (dired-get-filename t t)  (save-excursion
-                                   (beginning-of-line)
-                                   (if mark-char
-                                       (not (diredp-looking-at-p
-                                             (concat "^" (regexp-quote (char-to-string mark-char)))))
-                                     (diredp-looking-at-p "^ ")))))
-
-;;;###autoload
-(defun diredp-mark-region-files (&optional unmark-p) ; Not bound
-  "Mark all of the files in the current region (if it is active).
-With non-nil prefix arg, unmark them instead."
-  (interactive "P")
-  (let ((beg                        (min (point) (mark)))
-        (end                        (max (point) (mark)))
-        (inhibit-field-text-motion  t)) ; Just in case.
-    (setq beg  (save-excursion (goto-char beg) (line-beginning-position))
-          end  (save-excursion (goto-char end) (line-end-position)))
-    (let ((dired-marker-char  (if unmark-p ?\040 dired-marker-char)))
-      (diredp-mark-if (and (<= (point) end)  (>= (point) beg)  (diredp-this-file-unmarked-p)) "region file"))))
-
-;;;###autoload
-(defun diredp-unmark-region-files (&optional mark-p) ; Not bound
-  "Unmark all of the files in the current region (if it is active).
-With non-nil prefix arg, mark them instead."
-  (interactive "P")
-  (let ((beg                        (min (point) (mark)))
-        (end                        (max (point) (mark)))
-        (inhibit-field-text-motion  t)) ; Just in case.
-    (setq beg  (save-excursion (goto-char beg) (line-beginning-position))
-          end  (save-excursion (goto-char end) (line-end-position)))
-    (let ((dired-marker-char  (if mark-p dired-marker-char ?\040)))
-      (diredp-mark-if (and (<= (point) end)  (>= (point) beg)  (diredp-this-file-marked-p)) "region file"))))
-
-;;;###autoload
-(defun diredp-flag-region-files-for-deletion () ; Not bound
-  "Flag all of the files in the current region (if it is active) for deletion."
-  (interactive)
-  (let ((beg                        (min (point) (mark)))
-        (end                        (max (point) (mark)))
-        (inhibit-field-text-motion  t)) ; Just in case.
-    (setq beg  (save-excursion (goto-char beg) (line-beginning-position))
-          end  (save-excursion (goto-char end) (line-end-position)))
-    (let ((dired-marker-char  dired-del-marker))
-      (diredp-mark-if (and (<= (point) end)  (>= (point) beg)  (diredp-this-file-unmarked-p ?\D))
-                      "region file"))))
-
-;;;###autoload
-(defun diredp-toggle-marks-in-region (start end) ; Not bound
-  "Toggle marks in the region."
-  (interactive "r")
-  (save-excursion
-    (save-restriction
-      (if (not (fboundp 'dired-toggle-marks))
-          ;; Pre-Emacs 22.  Use bol, eol.  If details hidden, show first.
-          (let ((details-hidden-p  (and (boundp 'dired-details-state)  (eq 'hidden dired-details-state))))
-            (widen)
-            (when details-hidden-p (dired-details-show))
-            (goto-char start)
-            (setq start  (line-beginning-position))
-            (goto-char end)
-            (setq end    (line-end-position))
-            (narrow-to-region start end)
-            (dired-toggle-marks)
-            (when details-hidden-p (dired-details-hide)))
-        (narrow-to-region start end)
-        (dired-toggle-marks))))
-  (when (and (get-buffer-window (current-buffer))  (fboundp 'fit-frame-if-one-window))
-    (fit-frame-if-one-window)))
-
-
-;;; Mouse 3 menu.
-;;;;;;;;;;;;;;;;;
-
-(defvar diredp-file-line-overlay nil)
-
-;;;###autoload
-(defun diredp-mouse-3-menu (event)      ; Bound to `mouse-3'
-  "Dired pop-up `mouse-3' menu, for files in selection or current line."
-  (interactive "e")
-  (if (not (and (fboundp 'mouse3-dired-use-menu)  (diredp-nonempty-region-p)))
-      ;; No `mouse3.el' or no region.
-      (if (diredp-nonempty-region-p)
-          ;; Region
-          (let ((reg-choice  (x-popup-menu
-                              event
-                              (list "Files in Region"
-                                    (list ""
-                                          '("Mark" . diredp-mark-region-files)
-                                          '("Unmark" . diredp-unmark-region-files)
-                                          '("Toggle Marked/Unmarked" .
-                                            diredp-toggle-marks-in-region)
-                                          '("Flag for Deletion" .
-                                            diredp-flag-region-files-for-deletion))))))
-            (when reg-choice (call-interactively reg-choice)))
-        ;; Single file/dir (no region).
-        (let ((mouse-pos                  (event-start event))
-              ;; Do not use `save-excursion', because some commands will move point on purpose.
-              ;; Just save original point and return to it unless MOVEP is set to non-nil.
-              (opoint                     (point))
-              (movep                      nil)
-              (inhibit-field-text-motion  t) ; Just in case.
-              choice bol  eol  file/dir-name)
-          (with-current-buffer (window-buffer (posn-window mouse-pos))
-            (goto-char (posn-point mouse-pos))
-            (setq bol  (line-beginning-position)
-                  eol  (line-end-position))
-            (unwind-protect
-                 (when (setq file/dir-name  (and (not (eobp))  (dired-get-filename nil t)))
-                   (if diredp-file-line-overlay ; Don't re-create if exists.
-                       (move-overlay diredp-file-line-overlay bol eol (current-buffer))
-                     (setq diredp-file-line-overlay  (make-overlay bol eol))
-                     (overlay-put diredp-file-line-overlay 'face 'region))
-                   (sit-for 0)
-                   (let ((map
-                          (easy-menu-create-menu
-                           "This File"
-                           `(
-                             ("Bookmark" :visible (featurep 'bookmark+)
-                              ["Bookmark..." diredp-bookmark-this-file]
-                              ["Add Tags..." diredp-tag-this-file
-                               :visible (featurep 'bookmark+)]
-                              ["Remove Tags..." diredp-untag-this-file
-                               :visible (featurep 'bookmark+)]
-                              ["Remove All Tags" diredp-remove-all-tags-this-file
-                               :visible (featurep 'bookmark+)]
-                              ["Copy Tags" diredp-copy-tags-this-file
-                               :visible (featurep 'bookmark+)]
-                              ["Paste Tags (Add)" diredp-paste-add-tags-this-file
-                               :visible (featurep 'bookmark+)]
-                              ["Paste Tags (Replace)" diredp-paste-replace-tags-this-file
-                               :visible (featurep 'bookmark+)]
-                              ["Set Tag Value..." diredp-set-tag-value-this-file
-                               :visible (featurep 'bookmark+)]
-                              )
-                             ["Describe" ',(if (if (> emacs-major-version 21)
-                                                   (require 'help-fns+ nil t)
-                                                 (require 'help+20 nil t))
-                                               'diredp-describe-file
-                                               'diredp-describe-autofile)] ; Requires `bookmark+.el'
-                             ;; Stuff from `Marks' menu.
-                             ["Mark"  dired-mark
-                              :visible (not (eql (dired-file-marker file/dir-name)
-                                             dired-marker-char))]
-                             ["Unmark" dired-unmark
-                              :visible (dired-file-marker file/dir-name)]
-                             ["Flag for Deletion" dired-flag-file-deletion
-                              :visible (not (eql (dired-file-marker file/dir-name)
-                                             dired-del-marker))]
-                             ["Delete..." diredp-delete-this-file]
-                             "--"       ; ------------------------------------------------------
-                             ;; Stuff from `Single' / `Multiple' menus.
-                             ["Open" dired-find-file]
-                             ["Open in Other Window" dired-find-file-other-window]
-                             ["Open in Other Frame" diredp-find-file-other-frame]
-                             ["Open Associated Windows App" dired-w32-browser
-                              :visible (featurep 'w32-browser)]
-                             ["Open in Windows Explorer" dired-w32explore
-                              :visible (featurep 'w32-browser)]
-                             ["View (Read Only)" dired-view-file]
-                             ["--" 'ignore ; -------------------------------------------------
-                              :visible (or (atom (diredp-this-subdir)) ; Subdir line.
-                                        (not (equal (expand-file-name (dired-current-directory))
-                                              (expand-file-name default-directory))))] ; Not top.
-                             ["Insert This Subdir"
-                              (lambda () (interactive)
-                                      (call-interactively #'dired-maybe-insert-subdir)
-                                      (setq movep  t))
-                              :visible (and (atom (diredp-this-subdir))
-                                        (not (assoc (file-name-as-directory (diredp-this-subdir))
-                                              dired-subdir-alist)))
-                              :enable (atom (diredp-this-subdir))]
-                             ["Go To Inserted Subdir"
-                              (lambda () (interactive)
-                                      (call-interactively #'dired-maybe-insert-subdir)
-                                      (setq movep  t))
-                              :visible (and (atom (diredp-this-subdir))
-                                        (assoc (file-name-as-directory (diredp-this-subdir))
-                                         dired-subdir-alist))
-                              :enable (atom (diredp-this-subdir))
-                              :keys "i"]
-                             ["Remove This Inserted Subdir" dired-kill-subdir
-                              :visible (not (equal
-                                             (expand-file-name (dired-current-directory))
-                                             (expand-file-name default-directory)))] ; In subdir, not top.
-                             ["Remove This Inserted Subdir and Lower" diredp-kill-this-tree
-                              :visible (and (fboundp 'diredp-kill-this-tree)
-                                        (not (equal
-                                              (expand-file-name (dired-current-directory))
-                                              (expand-file-name default-directory))))] ; In subdir, not top.
-                             ["Dired This Inserted Subdir (Tear Off)"
-                              (lambda () (interactive) (diredp-dired-this-subdir t))
-                              :visible (not (equal (expand-file-name (dired-current-directory))
-                                             (expand-file-name default-directory)))] ; In subdir, not top.
-                             "--"       ; ------------------------------------------------------
-                             ["Compare..." diredp-ediff]
-                             ["Diff..." dired-diff]
-                             ["Diff with Backup" dired-backup-diff]
-
-                             ["Bookmark..." diredp-bookmark-this-file
-                              :visible (not (featurep 'bookmark+))]
-                             "--"       ; ------------------------------------------------------
-                             ["Rename to..." diredp-rename-this-file]
-                             ["Capitalize" diredp-capitalize-this-file]
-                             ["Upcase" diredp-upcase-this-file]
-                             ["Downcase" diredp-downcase-this-file]
-                             "--"       ; ------------------------------------------------------
-                             ["Copy to..." diredp-copy-this-file]
-                             ["Symlink to (Relative)..." diredp-relsymlink-this-file]
-                             ["Symlink to..." diredp-symlink-this-file]
-                             ["Hardlink to..." diredp-hardlink-this-file]
-                             "--"       ; ------------------------------------------------------
-                             ["Shell Command..." diredp-shell-command-this-file]
-                             ["Asynchronous Shell Command..."
-                              diredp-async-shell-command-this-file]
-                             ["Print..." diredp-print-this-file]
-                             ["Grep" diredp-grep-this-file]
-                             ["Compress/Uncompress" diredp-compress-this-file]
-                             ["Byte-Compile" diredp-byte-compile-this-file]
-                             ["Load" diredp-load-this-file]
-                             "--"       ; ------------------------------------------------------
-                             ["Change Timestamp..." diredp-touch-this-file]
-                             ["Change Mode..." diredp-chmod-this-file]
-                             ["Change Group..." diredp-chgrp-this-file
-                              :visible (fboundp 'diredp-chgrp-this-file)]
-                             ["Change Owner..." diredp-chown-this-file
-                              :visible (fboundp 'diredp-chown-this-file)]))))
-                     (when diredp-file-line-overlay
-                       (delete-overlay diredp-file-line-overlay))
-                     (setq choice  (x-popup-menu event map))
-                     (when choice (call-interactively (lookup-key map (apply 'vector choice))))))
-              (unless movep (goto-char opoint))))))
-    ;; `mouse3.el' and active region.
-    (unless (eq mouse3-dired-function 'mouse3-dired-use-menu)
-      (funcall #'mouse3-dired-use-menu)
-      (revert-buffer))
-    (let ((last-command  'mouse-save-then-kill)) (mouse-save-then-kill event))))
-
-
-;; REPLACE ORIGINAL in `dired.el' for Emacs 20.
-;;
-;; Allow `.' and `..', by using non-nil second arg to `dired-get-filename'.
-;;
-(when (< emacs-major-version 21)
-  (defun dired-find-file ()             ; Bound to `RET'
-    "In Dired, visit the file or directory named on this line."
-    (interactive)
-    (let* ((dgf-result  (or (dired-get-filename nil t)  (error "No file on this line")))
-           (file-name   (file-name-sans-versions dgf-result t)))
-      (if (file-exists-p file-name)
-          (find-file file-name)
-        (if (file-symlink-p file-name)
-            (error "File is a symlink to a nonexistent target")
-          (error "File no longer exists; type `g' to update Dired buffer"))))))
-
-;;;###autoload
-(defun diredp-find-file-other-frame ()  ; Bound to `C-o'
-  "In Dired, visit this file or directory in another frame."
-  (interactive)
-  (find-file-other-frame (file-name-sans-versions (dired-get-filename nil t) t)))
-
-;;;###autoload
-(defun diredp-mouse-find-file-other-frame (event) ; Bound to `M-mouse-2'
-  "In Dired, visit file or directory clicked on in another frame."
-  (interactive "e")
-  (let ((pop-up-frames  t)) (dired-mouse-find-file-other-window event)))
-
-
-;; REPLACE ORIGINAL in `dired.el'.
-;;
-;; Allow `.' and `..', by using non-nil second arg to `dired-get-filename'.
-;;
-;;;###autoload
-(defun dired-mouse-find-file-other-window (event) ; Bound to `mouse-2'
-  "In Dired, visit the file or directory name you click on."
-  (interactive "e")
-  (let (file)
-    (with-current-buffer (window-buffer (posn-window (event-end event)))
-      (save-excursion (goto-char (posn-point (event-end event)))
-                      (setq file  (dired-get-filename nil t))))
-    (unless (stringp file) (error "No file here"))
-    (select-window (posn-window (event-end event)))
-    (find-file-other-window (file-name-sans-versions file t))))
-
-;;;###autoload
-(defun diredp-mouse-view-file (event)   ; Not bound
-  "Examine this file in view mode, returning to Dired when done.
-When file is a directory, show it in this buffer if it is inserted;
-otherwise, display it in another buffer."
-  (interactive "e")
-  (let (file)
-    (with-current-buffer (window-buffer (posn-window (event-end event)))
-      (save-excursion (goto-char (posn-point (event-end event)))
-                      (setq file  (dired-get-filename nil t))))
-    (select-window (posn-window (event-end event)))
-    (if (file-directory-p file)
-        (or (and (cdr dired-subdir-alist)  (dired-goto-subdir file))  (dired file))
-      (view-file file))))               ; In `view.el'.
-
-;;;###autoload
-(defun diredp-mouse-ediff (event)       ; Not bound
-  "Compare this file (pointed by mouse) with file FILE2 using `ediff'.
-FILE2 defaults to this file as well.  If you enter just a directory
-name for FILE2, then this file is compared with a file of the same
-name in that directory.  FILE2 is the second file given to `ediff';
-this file is the first given to it."
-  (interactive "e")
-  (require 'ediff)
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (call-interactively 'diredp-ediff)))
-
-;;;###autoload
-(defun diredp-mouse-diff (event &optional switches) ; Not bound
-  "Compare this file (pointed by mouse) with file FILE2 using `diff'.
-FILE2 defaults to the file at the mark.  This file is the first file
-given to `diff'.  With prefix arg, prompt for second arg SWITCHES,
-which are options for `diff'."
-  (interactive "e")
-  (let ((default    (and (mark t)  (save-excursion (goto-char (mark t))
-                                                   (dired-get-filename t t))))
-        (mouse-pos  (event-start event)))
-    (require 'diff)
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (let ((file2  (read-file-name (format "Diff %s with: %s"
-                                          (dired-get-filename t)
-                                          (if default (concat "(default " default ") ") ""))
-                                  (dired-current-directory) default t)))
-      (setq switches  (and current-prefix-arg
-                           (if (fboundp 'icicle-read-string-completing) ; In `icicles-fn.el'
-                               (icicle-read-string-completing "Options for diff: "
-                                                              (if (stringp diff-switches)
-                                                                  diff-switches
-                                                                (mapconcat #'identity diff-switches " "))
-                                                              (lambda (c)
-                                                                (diredp-string-match-p "switches"
-                                                                                       (symbol-name c))))
-                             (read-string "Options for diff: " (if (stringp diff-switches)
-                                                                   diff-switches
-                                                                 (mapconcat #'identity diff-switches " "))))))
-      (diff file2 (dired-get-filename t) switches))))
-
-;;;###autoload
-(defun diredp-mouse-backup-diff (event) ; Not bound
-  "Diff this file with its backup file or vice versa.
-Use the latest backup, if there are several numerical backups.
-If this file is a backup, diff it with its original.
-The backup file is the first file given to `diff'.
-With prefix arg, prompt for SWITCHES which are the options for `diff'."
-  (interactive "e")
-  (let ((switches   (and current-prefix-arg
-                         (if (fboundp 'icicle-read-string-completing) ; In `icicles-fn.el'
-                             (icicle-read-string-completing "Options for diff: "
-                                                            (if (stringp diff-switches)
-                                                                diff-switches
-                                                              (mapconcat #'identity diff-switches " "))
-                                                            (lambda (c)
-                                                              (diredp-string-match-p "switches"
-                                                                                     (symbol-name c))))
-                           (read-string "Options for diff: " (if (stringp diff-switches)
-                                                                 diff-switches
-                                                               (mapconcat #'identity diff-switches " "))))))
-        (mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (diff-backup (dired-get-filename) switches)))
-
-;;;###autoload
-(defun diredp-mouse-mark (event)        ; Not bound
-  "In Dired, mark this file.
-If on a subdir headerline, mark all its files except `.' and `..'.
-
-Use \\[dired-unmark-all-files] to remove all marks,
-and \\[dired-unmark] on a subdir to remove the marks in this subdir."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (if (and (cdr dired-subdir-alist)  (dired-get-subdir))
-      (save-excursion (dired-mark-subdir-files))
-    (let ((buffer-read-only  nil))
-      (dired-repeat-over-lines 1 #'(lambda () (delete-char 1) (insert dired-marker-char)))
-      (diredp-previous-line 1))))
-
-;;;###autoload
-(defun diredp-mouse-unmark (event)      ; Not bound
-  "In Dired, unmark this file.
-If looking at a subdir, unmark all its files except `.' and `..'."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (let ((dired-marker-char  ?\040)) (dired-mark nil))
-  (diredp-previous-line 1))
-
-;;; This can be bound to [C-down-mouse-1] to give behavior similar to Windows Explorer.
-;;; However, Emacs generally uses [C-down-mouse-1] for `mouse-buffer-menu'.
-;;;###autoload
-(defun diredp-mouse-mark/unmark (event) ; Not bound
-  "Mark/unmark file or directory at mouse EVENT."
-  (interactive "e")
-  (let* ((mouse-pos                  (event-start event))
-         (inhibit-field-text-motion  t) ; Just in case.
-         (file/dir-name              (with-current-buffer (window-buffer (posn-window mouse-pos))
-                                       (save-excursion
-                                         (goto-char (posn-point mouse-pos))
-                                         (and (not (eobp))  (dired-get-filename nil t))))))
-    ;; Return nil iff not on a file or directory name.
-    (and file/dir-name  (cond ((dired-file-marker file/dir-name)
-                               (diredp-mouse-unmark event)
-                               (message "Unmarked: %s" file/dir-name))
-                              (t
-                               (diredp-mouse-mark event)
-                               (message "Marked: %s" file/dir-name))))))
-
-;; This can be bound to [S-mouse-1] to give behavior similar to Windows Explorer.
-;; If you do that, consider binding `diredp-mouse-mark/unmark' to `C-mouse-1'.
-;; Alternatively, just bind `diredp-mouse-mark/unmark-mark-region-files' to [S-mouse-1].
-;;;###autoload
-(defun diredp-mouse-mark-region-files (event) ; Bound to `S-mouse-1'
-  "Mark files between point and the mouse."
-  (interactive "e")
-  (call-interactively 'mouse-save-then-kill)
-  (diredp-mark-region-files))
-
-;; This can be bound to [S-mouse-1] to give behavior similar to Windows Explorer.
-;; If you don't bind `diredp-mouse-mark/unmark' to, for instance, `C-mouse-1', then
-;; Consider binding this to [S-mouse-1].
-;;;###autoload
-(defun diredp-mouse-mark/unmark-mark-region-files (event) ; Not bound
-  "Mark/unmark file or mark files in region.
-If the file the cursor is on is marked, then mark all files between it
- and the line clicked (included).
-Otherwise (cursor's file is unmarked):
- If the file clicked is marked, then unmark it.
- If it is unmarked, then mark it."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    ;; If same click same line as cursor, or cursor's line is marked,
-    ;; Then toggle the clicked line's mark.
-    ;; Else mark all files in region between point and clicked line (included).
-    (if (or (eq (count-lines (point-min) (posn-point mouse-pos))
-                (count-lines (point-min) (point)))
-            (equal dired-marker-char (dired-file-marker (dired-get-filename nil t))))
-        (diredp-mouse-mark/unmark event)
-      (call-interactively 'mouse-save-then-kill)
-      (diredp-mark-region-files))))
-
-;;;###autoload
-(defun diredp-mouse-flag-file-deletion (event) ; Not bound
-  "In Dired, flag this file for deletion.
-If on a subdir headerline, mark all its files except `.' and `..'."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (let ((dired-marker-char  dired-del-marker)) (dired-mark 1))
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-mouse-do-copy (event)     ; Not bound
-  "In Dired, copy this file.
-This normally preserves the last-modified date when copying."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (dired-do-create-files 'copy #'dired-copy-file (if dired-copy-preserve-time "Copy [-p]" "Copy")
-                         1 dired-keep-marker-copy))
-
-;;;###autoload
-(defun diredp-mouse-do-rename (event)   ; Not bound
-  "In Dired, rename this file."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (dired-do-create-files 'move #'dired-rename-file "Move" 1 dired-keep-marker-rename "Rename"))
-
-;;;###autoload
-(defun diredp-mouse-upcase (event)      ; Not bound
-  "In Dired, rename this file to upper case."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (dired-rename-non-directory #'upcase "Rename to uppercase:" nil))
-
-;;;###autoload
-(defun diredp-mouse-downcase (event)    ; Not bound
-  "In Dired, rename this file to lower case."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (dired-rename-non-directory #'downcase "Rename to lowercase:" nil))
-
-;;;###autoload
-(defun diredp-mouse-do-delete (event)   ; Not bound
-  "In Dired, delete this file, upon confirmation."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (diredp-internal-do-deletions (dired-map-over-marks (cons (dired-get-filename) (point)) 1)
-                                1
-                                'USE-TRASH-CAN) ; This arg is for Emacs 24+ only.
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-mouse-do-shell-command (event) ; Not bound
-  "Run a shell COMMAND on this file.
-If there is output, it goes to a separate buffer.
-
-No automatic redisplay of Dired buffers is attempted, as there's no
-telling what files the command may have changed.  Type
-\\[dired-do-redisplay] to redisplay.
-
-The shell command has the top level directory as working directory, so
-output files usually are created there instead of in a subdir."
-  ;;Functions dired-run-shell-command and dired-shell-stuff-it do the
-  ;;actual work and can be redefined for customization.
-  (interactive "e")
-  (lexical-let ((mouse-pos  (event-start event))
-                (command    (dired-read-shell-command "! on %s: " nil (dired-get-marked-files t nil))))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-bunch-files (- 10000 (length command))
-                       (lambda (&rest files) (dired-run-shell-command (dired-shell-stuff-it command files t 1)))
-                       nil
-                       (dired-get-marked-files t 1))))
-
-;;;###autoload
-(defun diredp-mouse-do-symlink (event)  ; Not bound
-  "Make symbolic link to this file."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (dired-do-create-files 'symlink #'make-symbolic-link "Symlink" 1 dired-keep-marker-symlink))
-
-;;;###autoload
-(defun diredp-mouse-do-hardlink (event) ; Not bound
-  "Make hard link (alias) to this file."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (dired-do-create-files 'hardlink #'add-name-to-file "Hardlink" 1 dired-keep-marker-hardlink))
-
-;;;###autoload
-(defun diredp-mouse-do-print (event)    ; Not bound
-  "Print this file.
-Uses the shell command coming from variables `lpr-command' and
-`lpr-switches' as default."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (let* ((file     (dired-get-filename))
-         (command  (dired-mark-read-string "Print %s with: "
-                                           (apply 'concat lpr-command " " lpr-switches)
-                                           'print 1 (list file))))
-    (dired-run-shell-command (dired-shell-stuff-it command (list file) nil))))
-
-;;;###autoload
-(defun diredp-mouse-do-grep (event)     ; Not bound
-  "Run grep against this file."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (unless grep-command (grep-compute-defaults))
-  (grep (diredp-do-grep-1 (list (dired-get-filename t)))))
-
-;;;###autoload
-(defun diredp-mouse-do-compress (event) ; Not bound
-  "Compress or uncompress this file."
-  (interactive "e")
-  (let ((mouse-pos         (event-start event))
-        (dired-no-confirm  t))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-map-over-marks-check #'dired-compress 1 'compress t))
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-mouse-do-byte-compile (event) ; Not bound
-  "Byte compile this file."
-  (interactive "e")
-  (let ((mouse-pos         (event-start event))
-        (dired-no-confirm  t))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-map-over-marks-check #'dired-byte-compile 1 'byte-compile t))
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-mouse-do-load (event)     ; Not bound
-  "Load this Emacs Lisp file."
-  (interactive "e")
-  (let ((mouse-pos         (event-start event))
-        (dired-no-confirm  t))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos))
-    (dired-map-over-marks-check #'dired-load 1 'load t))
-  (diredp-previous-line 1))
-
-;;;###autoload
-(defun diredp-mouse-do-chmod (event)    ; Not bound
-  "Change the mode of this file.
-This calls chmod, so symbolic modes like `g+w' are allowed."
-  (interactive "e")
-  (let ((mouse-pos  (event-start event)))
-    (select-window (posn-window mouse-pos))
-    (goto-char (posn-point mouse-pos)))
-  (dired-do-chxxx "Mode" "chmod" 'chmod 1)
-  (diredp-previous-line 1))
-
-(unless (memq system-type '(windows-nt ms-dos))
-  (defun diredp-mouse-do-chgrp (event)  ; Not bound
-    "Change the group of this file."
-    (interactive "e")
-    (let ((mouse-pos  (event-start event)))
-      (select-window (posn-window mouse-pos))
-      (goto-char (posn-point mouse-pos)))
-    (dired-do-chxxx "Group" "chgrp" 'chgrp 1)
-    (diredp-previous-line 1)))
-
-(unless (memq system-type '(windows-nt ms-dos))
-  (defun diredp-mouse-do-chown (event)  ; Not bound
-    "Change the owner of this file."
-    (interactive "e")
-    (let ((mouse-pos  (event-start event)))
-      (select-window (posn-window mouse-pos))
-      (goto-char (posn-point mouse-pos)))
-    (dired-do-chxxx "Owner" dired-chown-program 'chown 1)
-    (diredp-previous-line 1)))
-
-
-;;; Breadcrumbs
-
-(when (fboundp 'define-minor-mode)
-
-  ;; Macro `define-minor-mode' is not defined in Emacs 20, so in order to be able to byte-compile
-  ;; this file in Emacs 20, prohibit byte-compiling of the `define-minor-mode' call.
-  ;;
-  (eval '(define-minor-mode diredp-breadcrumbs-in-header-line-mode
-          "Toggle the use of breadcrumbs in Dired header line.
-With arg, show breadcrumbs iff arg is positive."
-          :init-value nil :group 'header-line :group 'Dired-Plus
-          (unless (derived-mode-p 'dired-mode)
-            (error "You must be in Dired or a mode derived from it to use this command"))
-          (if diredp-breadcrumbs-in-header-line-mode
-              (diredp-set-header-line-breadcrumbs)
-            (setq header-line-format  (default-value 'header-line-format)))))
-
-  (defun diredp-set-header-line-breadcrumbs ()
-    "Show a header line with breadcrumbs to parent directories."
-    (let ((parent  (diredp-parent-dir default-directory))
-          (dirs    ())
-          (text    ""))
-      (while parent
-        (push parent dirs)
-        (setq parent  (diredp-parent-dir parent)))
-      (dolist (dir  dirs)
-        (let* ((crumbs-map    (make-sparse-keymap))
-               (menu-map      (make-sparse-keymap "Breadcrumbs in Header Line"))
-               ;; The next three are for showing the root as absolute and the rest as relative.
-               (rootp         (diredp-root-directory-p dir))
-               (parent-rootp  (and (not rootp)  (diredp-root-directory-p (diredp-parent-dir dir))))
-               (rdir          dir))
-          ;; (define-key crumbs-map [header-line mouse-3] menu-map)
-          (unless rootp (setq rdir  (file-name-nondirectory (directory-file-name dir))))
-          (when dir
-            (setq rdir  (propertize rdir
-                                    'local-map (progn (define-key crumbs-map [header-line mouse-1]
-                                                        `(lambda () (interactive)
-                                                          (dired ,dir dired-actual-switches)))
-                                                      (define-key crumbs-map [header-line mouse-2]
-                                                        `(lambda () (interactive)
-                                                          (dired-other-window ,dir dired-actual-switches)))
-                                                      crumbs-map)
-                                    'mouse-face 'mode-line-highlight
-                                    ;;'help-echo "mouse-1: Dired; mouse-2: Dired in other window; mouse-3: Menu"))
-                                    'help-echo "mouse-1: Dired; mouse-2: Dired in other window"))
-            (setq text  (concat text (if (or rootp  parent-rootp) " "  " / ") rdir)))))
-      (make-local-variable 'header-line-format)
-      (setq header-line-format  text)))
-
-  ;; Users can do this.
-  ;;
-  ;; (add-hook 'dired-before-readin-hook 'diredp-breadcrumbs-in-header-line-mode)
-
-  )
-
-
-;;; `Dired+' Help
-
-;;;###autoload
-(defun diredp-describe-mode (&optional buffer)
-  "Describe Dired mode, including Dired+ features.
-This is `describe-mode' plus a description of Dired+ features.
-For just the latter, use \\<dired-mode-map>`\\[diredp-dired-plus-help]'."
-  (interactive "@")
-  (unless (derived-mode-p 'dired-mode)
-    (error "Use `diredp-dired-plus-help' if you want information about Dired+"))
-  (with-current-buffer (or buffer  (current-buffer)) (describe-mode))
-  (with-current-buffer (get-buffer-create "*Help*")
-    (save-excursion
-      (goto-char (point-min))
-      (diredp-dired-plus-help-link)
-      (let ((buffer-read-only  nil)) (insert "\n"))
-      (when (re-search-forward "Keybindings:\nkey\\s-+binding\n---\\s-+-------" nil t)
-        (goto-char (match-beginning 0))
-        (let ((buffer-read-only  nil))
-          (insert "\f\n")
-          (diredp-dired-plus-description+links)
-          (insert "\f\n"))))))
-
-;;;###autoload
-(defun diredp-dired-plus-help ()
-  "Describe Dired+."
-  (interactive "@")
-  (diredp-with-help-window "*Help*" (diredp-dired-plus-description+links)))
-
-(defun diredp-dired-plus-description+links ()
-  "Insert Dired+ help text in `*Help*'."
-  (with-current-buffer (get-buffer-create "*Help*")
-    (let ((buffer-read-only  nil))
-      (save-restriction
-        (narrow-to-region (point) (point))
-        (diredp-dired-plus-help-link)
-        (insert (diredp-dired-plus-description))
-        (goto-char (point-max))
-        (insert "\n")
-        (diredp-dired-plus-help-link)))))
-
-(when (and (> emacs-major-version 21)
-           (require 'help-mode nil t)
-           (get 'help-xref 'button-category-symbol)) ; `button.el'
-  (define-button-type 'diredp-help-button
-      :supertype 'help-xref
-      'help-function #'(lambda () (browse-url "https://www.emacswiki.org/emacs/DiredPlus"))
-      'help-echo
-      (purecopy "mouse-2, RET: Dired+ documentation on the Emacs Wiki (requires \
-Internet access)")))
-
-(defun diredp-dired-plus-help-link ()
-  "Add Web link for Dired+ help, and reminder about sending bug report."
-  ;; Don't bother to do this for Emacs 21.3.  Its `help-insert-xref-button' is different.
-  (when (and (> emacs-major-version 21)
-             (require 'help-mode nil t)
-             (fboundp 'help-insert-xref-button)) ; `help-mode.el'.
-    (let ((buffer-read-only  nil))
-      (help-insert-xref-button "[Dired+ Help on the Web]" 'diredp-help-button)
-      (insert (substitute-command-keys
-               "\t\tSend a Dired+ bug report:\n\t\t\t\t\t`\\[diredp-send-bug-report]'\n")))))
-
-(defun diredp-dired-plus-description ()
-  "Dired+ description."
-  (substitute-command-keys
-   (concat
-    "\\<dired-mode-map>\
-              Dired+ Features
-              ---------------
-
-To see or customize the  Dired+ options or faces, use
-`M-x customize-option diredp TAB' or `M-x customize-face diredp TAB'.
-
-Most keys listed here are in addition to those for vanilla Dired.
-
-Menus
------
-
-Many Dired+ actions are available from the menu-bar menus and the
-`mouse-3' context menu.  This may include commands shown here as not
-being bound to keys (i.e., listed as `M-x ...').
-
-General Here
-------------
-
-"
-    (and (fboundp 'diredp-w32-drives)
-         "  \\[diredp-w32-drives]\t\t- Go up to a list of MS Windows drives
-")
-    (and (fboundp 'dired-hide-details-mode)
-         "  \\[dired-hide-details-mode]\t\t- Hide/show details
-")
-
-    "  \\[revert-buffer]\t\t- Refresh (sync and show all)
-  \\[diredp-toggle-find-file-reuse-dir]\t- Toggle reusing directories
-"
-    "  \\[diredp-marked-other-window]\t\t- Open Dired on marked files here
-  \\[diredp-dired-inserted-subdirs]\t\t- Dired separately each subdir inserted here
-"    
-    (and (featurep 'bookmark+)
-         "  \\[diredp-highlight-autofiles-mode]\t- Toggle autofile highlighting
-
-")
-
-    "General Globally
-----------------
-
-\\<global-map>\
-  \\[diredp-add-to-dired-buffer]\t- Add files to a Dired buffer
-  \\[diredp-fileset]\t- Open Dired on files in a fileset
-  \\[diredp-dired-recent-dirs]\t- Open Dired on recently used dirs
-  \\[diredp-dired-union]\t- Create union of some Dired buffers
-  \\[diredp-dired-for-files]\t- Open Dired on files located anywhere
-\\<dired-mode-map>\
-
-Mouse
------
-
-  \\[diredp-mouse-3-menu]\t- Context-sensitive menu
-"
-
-    (and (where-is-internal 'diredp-mouse-describe-file dired-mode-map)
-         "  \\[diredp-mouse-describe-file]\t- Describe file
-")
-
-    (and (where-is-internal 'diredp-mouse-describe-autofile dired-mode-map)
-         "  \\[diredp-mouse-describe-autofile]\t- Describe autofile
-")
-
-    "  \\[diredp-mouse-mark-region-files]\t\t- Mark all in region
-"
-
-    (and (fboundp 'dired-mouse-w32-browser) ; In `w32-browser.el'.
-         (where-is-internal 'dired-mouse-w32-browser dired-mode-map)
-         "  \\[dired-mouse-w32-browser]\t\t- MS Windows `Open' action
-")
-    (and (fboundp 'dired-mouse-w32-browser-reuse-dir-buffer) ; In `w32-browser.el'.
-         (where-is-internal 'dired-mouse-w32-browser-reuse-dir-buffer dired-mode-map)
-         "  \\[dired-mouse-w32-browser-reuse-dir-buffer]\t- MS Windows `Open' action
-")
-
-    (and (where-is-internal 'dired-mouse-find-file dired-mode-map)
-         "  \\[dired-mouse-find-file]\t- Open in this window
-")
-    (and (where-is-internal 'diredp-mouse-find-file-reuse-dir-buffer dired-mode-map)
-         "  \\[diredp-mouse-find-file-reuse-dir-buffer]\t- Open in this window
-")
-
-    (and (where-is-internal 'dired-mouse-find-file-other-window dired-mode-map)
-         "  \\[dired-mouse-find-file-other-window]\t\t- Open in another window
-")
-
-    "  \\[diredp-mouse-find-file-other-frame]\t\t- Open in another frame
-"
-
-    "
-Marking
--------
-
-  \\[dired-mark]\t\t- Mark this file/dir
-  \\[dired-unmark]\t\t- Unmark this file/dir
-  \\[dired-toggle-marks]\t\t- Toggle marked/unmarked
-  \\[dired-mark-sexp]\t\t- Mark all satisfying a predicate
-  \\[dired-unmark-all-marks]\t\t- Unmark all
-  \\[diredp-mark/unmark-extension]\t\t- Mark/unmark all that have a given extension
-"
-
-    (and (fboundp 'dired-mark-omitted)  ; In `dired-x.el' Emacs 22+.
-         "  \\[dired-mark-omitted]\t\t- Mark omitted
-")
-
-    "  \\[diredp-mark-files-tagged-regexp]\t\t- Mark those with a tag that matches a regexp
-  \\[diredp-unmark-files-tagged-regexp]\t\t- Unmark those with a tag that matches a regexp
-  \\[diredp-mark-files-tagged-all]\t\t- Mark those with all of the given tags
-  \\[diredp-unmark-files-tagged-all]\t\t- Unmark those with all of the given tags
-  \\[diredp-mark-files-tagged-some]\t\t- Mark those with some of the given tags
-  \\[diredp-unmark-files-tagged-some]\t\t- Unmark those with some of the given tags
-  \\[diredp-mark-files-tagged-not-all]\t- Mark those without some of the given tags
-  \\[diredp-unmark-files-tagged-not-all]\t- Unmark those without some of the given tags
-  \\[diredp-mark-files-tagged-none]\t- Mark those with none of the given tags
-  \\[diredp-unmark-files-tagged-none]\t- Unmark those with none of the given tags
-"
-
-    "
-Current file/subdir (current line)
-----------------------------------
-
-  \\[diredp-describe-file]\t- Describe
-  \\[dired-find-file]\t\t- Open
-"
-    (and (fboundp 'dired-mouse-w32-browser) ; In `w32-browser.el'.
-         (where-is-internal 'dired-mouse-w32-browser dired-mode-map)
-         "  \\[dired-mouse-w32-browser]\t- MS Windows `Open' action
-  \\[dired-w32explore]\t- MS Windows Explorer
-")
-
-    "  \\[diredp-byte-compile-this-file]\t\t- Byte-compile
-  \\[diredp-compress-this-file]\t\t- Compress/uncompress
-  \\[diredp-print-this-file]\t\t- Print
-  \\[diredp-relsymlink-this-file]\t\t- Create relative symlink
-  \\[diredp-delete-this-file]\t\t- Delete (with confirmation)
-  \\[diredp-rename-this-file]\t\t- Rename
-  \\[diredp-capitalize-this-file]\t\t- Capitalize (rename)
-  \\[diredp-upcase-this-file]\t\t- Rename to uppercase
-  \\[diredp-downcase-this-file]\t\t- Rename to lowercase
-  \\[diredp-ediff]\t\t- Ediff
-  \\[diredp-bookmark-this-file]\t\t- Bookmark
-"
-    (and (featurep 'bookmark+)
-         "  \\[diredp-tag-this-file]\t\t- Add some tags to this file/dir
-  \\[diredp-untag-this-file]\t\t- Remove some tags from this file/dir
-  \\[diredp-remove-all-tags-this-file]\t\t- Remove all tags from this file/dir
-  \\[diredp-copy-tags-this-file]\t\t- Copy the tags from this file/dir
-  \\[diredp-paste-add-tags-this-file]\t\t- Paste (add) copied tags to this file/dir
-  \\[diredp-paste-replace-tags-this-file]\t\t- Paste (replace) tags for this file/dir
-  \\[diredp-set-tag-value-this-file]\t\t- Set a tag value for this file/dir
-")
-
-    (and (fboundp 'dired-mouse-w32-browser-reuse-dir-buffer) ; In `w32-browser.el'.
-         (where-is-internal 'dired-mouse-w32-browser-reuse-dir-buffer dired-mode-map)
-         "  \\[dired-mouse-w32-browser-reuse-dir-buffer]\t- MS Windows `Open' action
-  \\[dired-w32explore]\t- MS Windows Explorer
-")
-
-    "
-Marked (or next prefix arg) files & subdirs here
-------------------------------------------------
-"
-    (and (fboundp 'dired-multiple-w32-browser) ; In `w32-browser.el'.
-         "
-  \\[dired-multiple-w32-browser]\t- MS Windows `Open' action
-")
-
-
-    "  \\[diredp-list-marked]\t\t- List marked files and directories
-  \\[diredp-insert-subdirs]\t\t- Insert marked subdirectories
-  \\[dired-copy-filename-as-kill]\t\t- Copy names for pasting
-  M-o \\[dired-copy-filename-as-kill]\t\t- Copy absolute names for pasting
-  \\[diredp-yank-files]\t\t- Paste files whose absolute names you copied
-  \\[dired-do-find-marked-files]\t\t- Visit
-  \\[dired-do-copy]\t\t- Copy
-  \\[dired-do-rename]\t\t- Rename/move
-  \\[diredp-do-grep]\t\t- Run `grep'
-  \\[dired-do-search]\t\t- Search
-"
-    (and (fboundp 'dired-do-find-regexp) ; Emacs 25+
-         "  \\[dired-do-find-regexp]\t\t- Search using `find'
-")
-
-    (if (fboundp 'dired-do-query-replace-regexp) ; Emacs 22+
-        "  \\[dired-do-query-replace-regexp]\t\t- Query-replace
-"
-      "  \\[dired-do-query-replace]\t\t- Query-replace
-")
-
-    (and (fboundp 'dired-do-find-regexp-and-replace)
-         "  \\[dired-do-find-regexp-and-replace]\t\t- Query-replace using `find'
-")
-
-    (and (fboundp 'dired-do-isearch)
-         "  \\[dired-do-isearch]\t- Isearch
-  \\[dired-do-isearch-regexp]\t- Regexp isearch
-")
-
-    (and (fboundp 'dired-do-async-shell-command)
-         "  \\[dired-do-async-shell-command]\t\t- Run shell command asynchronously
-")
-
-    "  \\[dired-do-shell-command]\t\t- Run shell command
-  \\[diredp-marked-other-window]\t\t- Dired
-  \\[dired-do-compress]\t\t- Compress
-  \\[dired-do-byte-compile]\t\t- Byte-compile
-  \\[dired-do-load]\t\t- Load (Emacs Lisp)
-  \\[diredp-do-apply-function]\t\t- Apply Lisp function
-  \\[diredp-do-emacs-command]\t\t- Invoke Emacs command
-"
-    (and (fboundp 'diredp-read-expression) ; Emacs 22+
-         "  \\[diredp-do-lisp-sexp]\t\t- Evaluate Lisp sexp
-")
-
-    "  \\[diredp-omit-marked]\t- Omit
-  \\[diredp-omit-unmarked]\t- Omit unmarked
-"
-
-    (and (featurep 'bookmark+)
-         "
-  \\[diredp-do-tag]\t\t- Add some tags to marked
-  \\[diredp-do-untag]\t\t- Remove some tags from marked
-  \\[diredp-do-remove-all-tags]\t\t- Remove all tags from marked
-  \\[diredp-do-paste-add-tags]\t- Paste (add) copied tags to marked
-  \\[diredp-do-paste-replace-tags]\t\t- Paste (replace) tags for marked
-  \\[diredp-do-set-tag-value]\t\t- Set a tag value for marked
-  \\[diredp-mark-files-tagged-regexp]\t\t- Mark those with a tag that matches a regexp
-  \\[diredp-mark-files-tagged-all]\t\t- Mark those with all of the given tags
-  \\[diredp-mark-files-tagged-some]\t\t- Mark those with some of the given tags
-  \\[diredp-mark-files-tagged-not-all]\t- Mark those without some of the given tags
-  \\[diredp-mark-files-tagged-none]\t- Mark those with none of the given tags
-  \\[diredp-unmark-files-tagged-regexp]\t\t- Unmark those with a tag that matches a regexp
-  \\[diredp-unmark-files-tagged-all]\t\t- Unmark those with all of the given tags
-  \\[diredp-unmark-files-tagged-some]\t\t- Unmark those with some of the given tags
-  \\[diredp-unmark-files-tagged-not-all]\t- Unmark those without some of the given tags
-  \\[diredp-unmark-files-tagged-none]\t- Unmark those with none of the given tags")
-
-    "
-
-  \\[diredp-do-bookmark]\t\t- Bookmark
-"
-
-    (and (featurep 'bookmark+)
-         "  \\[diredp-set-bookmark-file-bookmark-for-marked]\t\t- \
-Bookmark and create bookmark-file bookmark
-  \\[diredp-do-bookmark-in-bookmark-file]\t- Bookmark in specific bookmark file
-")
-
-    "
-Here and below (in marked subdirs)
-----------------------------------
-"
-    (and (fboundp 'dired-multiple-w32-browser) ; In `w32-browser.el'.
-         "
-  \\[diredp-multiple-w32-browser-recursive]\t- MS Windows `Open' action
-")
-
-    "  \\[diredp-list-marked-recursive]\t\t- List marked files and directories
-  \\[diredp-insert-subdirs-recursive]\t\t- Insert marked subdirectories
-  \\[diredp-copy-filename-as-kill-recursive]\t\t- Copy names for pasting
-  \\[diredp-do-find-marked-files-recursive]\t\t\t- Visit
-  \\[diredp-do-print-recursive]\t\t\t- Print
-  \\[diredp-do-copy-recursive]\t\t\t- Copy
-  \\[diredp-do-move-recursive]\t\t\t- Move
-  \\[diredp-do-touch-recursive]\t\t- Touch (update timestamp)
-  \\[diredp-do-chmod-recursive]\t\t\t- Change mode
-
-  \\[diredp-do-symlink-recursive]\t\t\t- Add symbolic links
-  \\[diredp-do-relsymlink-recursive]\t\t\t- Add relative symbolic links
-  \\[diredp-do-hardlink-recursive]\t\t\t- Add hard links
-
-  \\[diredp-capitalize-recursive]\t\t- Capitalize
-  \\[diredp-downcase-recursive]\t\t- Downcase
-  \\[diredp-upcase-recursive]\t\t- Upcase
-"
-  (and (fboundp 'epa-dired-do-encrypt)   ; Emacs 23+
-       "
-  \\[diredp-do-encrypt-recursive]\t\t- Encrypt
-  \\[diredp-do-decrypt-recursive]\t\t- Decrypt
-  \\[diredp-do-sign-recursive]\t\t- Sign
-  \\[diredp-do-verify-recursive]\t\t- Verify
-")
-
-  "
-  \\[diredp-do-grep-recursive]\t\t- `grep'
-  \\[diredp-do-search-recursive]\t\t\t- Search
-  \\[diredp-do-query-replace-regexp-recursive]\t\t\t- Query-replace
-  \\[diredp-do-isearch-recursive]\t\t- Isearch
-  \\[diredp-do-isearch-regexp-recursive]\t- Regexp isearch
-"
-    (and (fboundp 'diredp-do-async-shell-command-recursive) ; Emacs 23+
-         "
-  \\[diredp-do-async-shell-command-recursive]\t\t\t- Run shell command asynchronously
-")
-
-    "  \\[diredp-do-shell-command-recursive]\t\t\t- Run shell command
-  \\[diredp-do-apply-function-recursive]\t\t\t- Apply Lisp function
-
-  \\[diredp-marked-recursive-other-window]\t\t- Dired
-  \\[diredp-list-marked-recursive]\t\t- List
-
-  \\[diredp-image-dired-comment-files-recursive]\t\t- Add image comment
-  \\[diredp-image-dired-display-thumbs-recursive]\t\t- Show thumbnail images
-  \\[diredp-image-dired-tag-files-recursive]\t\t- Tag images
-  \\[diredp-image-dired-delete-tag-recursive]\t\t- Delete image tags
-
-  \\[diredp-do-bookmark-recursive]\t\t- Bookmark
-"
-    (and (featurep 'bookmark+)
-         "  \\[diredp-do-bookmark-in-bookmark-file-recursive]\t\t- Bookmark in bookmark file
-  \\[diredp-set-bookmark-file-bookmark-for-marked-recursive]\t\t- Create bookmark-file bookmark
-")
-
-    "
-  \\[diredp-mark-directories-recursive]\t\t- Mark directories
-  \\[diredp-mark-executables-recursive]\t\t- Mark executables
-  \\[diredp-mark-symlinks-recursive]\t\t- Mark symbolic links
-  \\[diredp-mark-files-containing-regexp-recursive]\t\t- Mark content regexp matches
-  \\[diredp-mark-files-regexp-recursive]\t\t- Mark filename regexp matches
-"
-    (and (featurep 'bookmark+)
-         "  \\[diredp-mark-autofiles-recursive]\t\t- Mark autofiles
-")
-    "  \\[diredp-flag-auto-save-files-recursive]\t\t\t- Flag auto-save
-  \\[diredp-do-delete-recursive]\t\t\t- Delete marked (not flagged)
-  \\[diredp-change-marks-recursive]\t\t- Change marks
-  \\[diredp-unmark-all-files-recursive]\t\t- Remove a given mark
-  \\[diredp-unmark-all-marks-recursive]\t\t\t- Remove all marks
-"
-    (and (featurep 'bookmark+)
-"
-
-Tagging
--------
-
-  \\[diredp-tag-this-file]\t\t- Add some tags to this file/dir
-  \\[diredp-untag-this-file]\t\t- Remove some tags from this file/dir
-  \\[diredp-remove-all-tags-this-file]\t\t- Remove all tags from this file/dir
-  \\[diredp-copy-tags-this-file]\t\t- Copy the tags from this file/dir
-  \\[diredp-paste-add-tags-this-file]\t\t- Paste (add) copied tags to this file/dir
-  \\[diredp-paste-replace-tags-this-file]\t\t- Paste (replace) tags for this file/dir
-  \\[diredp-set-tag-value-this-file]\t\t- Set a tag value for this file/dir
-  \\[diredp-do-tag]\t\t- Add some tags to marked
-  \\[diredp-do-untag]\t\t- Remove some tags from marked
-  \\[diredp-do-remove-all-tags]\t\t- Remove all tags from marked
-  \\[diredp-do-paste-add-tags]\t- Paste (add) copied tags to marked
-  \\[diredp-do-paste-replace-tags]\t\t- Paste (replace) tags for marked
-  \\[diredp-do-set-tag-value]\t\t- Set a tag value for marked
-  \\[diredp-mark-files-tagged-regexp]\t\t- Mark those with a tag that matches a regexp
-  \\[diredp-mark-files-tagged-all]\t\t- Mark those with all of the given tags
-  \\[diredp-mark-files-tagged-some]\t\t- Mark those with some of the given tags
-  \\[diredp-mark-files-tagged-not-all]\t- Mark those without some of the given tags
-  \\[diredp-mark-files-tagged-none]\t- Mark those with none of the given tags
-  \\[diredp-unmark-files-tagged-regexp]\t\t- Unmark those with a tag that matches a regexp
-  \\[diredp-unmark-files-tagged-all]\t\t- Unmark those with all of the given tags
-  \\[diredp-unmark-files-tagged-some]\t\t- Unmark those with some of the given tags
-  \\[diredp-unmark-files-tagged-not-all]\t- Unmark those without some of the given tags
-  \\[diredp-unmark-files-tagged-none]\t- Unmark those with none of the given tags
-")
-
-    "
-Bookmarking
------------
-
-  \\[diredp-bookmark-this-file]\t\t- Bookmark this file/dir
-  \\[diredp-do-bookmark]\t\t- Bookmark marked"
-
-    (and (featurep 'bookmark+)
-         "
-  \\[diredp-set-bookmark-file-bookmark-for-marked]\t\t- \
-Bookmark marked and create bookmark-file bookmark
-  \\[diredp-do-bookmark-in-bookmark-file]\t- Bookmark marked, in specific bookmark file
-")
-
-    "  \\[diredp-do-bookmark-recursive]\t- Bookmark marked, here and below
-"
-    (and (featurep 'bookmark+)
-         "  \\[diredp-do-bookmark-in-bookmark-file-recursive]\t- \
-Bookmark marked, here and below, in specific file
-  \\[diredp-set-bookmark-file-bookmark-for-marked-recursive]\t- \
-Set bookmark-file bookmark for marked here and below
-")
-
-    )))
-
-(when (> emacs-major-version 21)
-  (defun diredp-nb-marked-in-mode-name ()
-    "Show number of marked, flagged, and current-list lines in mode-line.
-\(Flagged means flagged for deletion.)
-If the current line is marked/flagged and there are others
-marked/flagged after it then show `N/M', where `N' is the number
-marked/flagged through the current line and `M' is the total number
-marked/flagged.
-
-If the current line is for a file then show `L/T', where `L' is the
-line number in the current listing and `T' is the number of files in
-that listing.  If option `diredp-count-.-and-..-flag' is non-nil then
-count also `.' and `..'.
-
-Also abbreviate `mode-name', using \"Dired/\" instead of \"Dired by\"."
-    (let ((mname  (format-mode-line mode-name)))
-      ;; Property `dired+-mode-name' indicates whether `mode-name' has been changed.
-      (unless (get-text-property 0 'dired+-mode-name mname)
-        (save-match-data
-          (setq mode-name
-                `(,(propertize (if (string-match "^[dD]ired \\(by \\)?\\(.*\\)" mname)
-                                   (format "Dired/%s" (match-string 2 mname))
-                                 mname)
-                               'dired+-mode-name t)
-                  (:eval (let* ((dired-marker-char  (if (eq ?D dired-marker-char)
-                                                        ?* ; `dired-do-flagged-delete' binds it.
-                                                      dired-marker-char))
-                                (marked-regexp      (dired-marker-regexp))
-                                (nb-marked          (count-matches marked-regexp
-                                                                   (point-min) (point-max))))
-                           (if (not (> nb-marked 0))
-                               ""
-                             (propertize
-                              (format " %s%d%c"
-                                      (save-excursion
-                                        (forward-line 0)
-                                        (if (diredp-looking-at-p (concat marked-regexp ".*"))
-                                            (format "%d/" (1+ (count-matches
-                                                               marked-regexp
-                                                               (point-min) (point))))
-                                          ""))
-                                      nb-marked dired-marker-char)
-                              'face 'diredp-mode-line-marked 'dired+-mode-name t))))
-                  (:eval (let* ((flagged-regexp  (let ((dired-marker-char  dired-del-marker))
-                                                   (dired-marker-regexp)))
-                                (nb-flagged      (count-matches flagged-regexp
-                                                                (point-min) (point-max))))
-                           (if (not (> nb-flagged 0))
-                               ""
-                             (propertize
-                              (format " %s%dD"
-                                      (save-excursion
-                                        (forward-line 0)
-                                        (if (diredp-looking-at-p (concat flagged-regexp ".*"))
-                                            (format "%d/" (1+ (count-matches
-                                                               flagged-regexp
-                                                               (point-min) (point))))
-                                          ""))
-                                      nb-flagged)
-                              'face 'diredp-mode-line-flagged))))
-                  (:eval (save-excursion
-                           (let ((this   0)
-                                 (total  0)
-                                 (o-pt   (line-beginning-position))
-                                 (e-pt   (or (condition-case nil
-                                                 (let ((diredp-wrap-around-flag  nil))
-                                                   (save-excursion
-                                                     (diredp-next-subdir 1)
-                                                     (line-beginning-position)))
-                                               (error nil))
-                                             (save-excursion (goto-char (point-max)) (line-beginning-position)))))
-                             (when dired-subdir-alist (dired-goto-subdir (dired-current-directory)))
-                             (while (and (<= (point) e-pt)
-                                         (< (point) (point-max))) ; Hack to work around Emacs display-engine bug.
-                               (when (condition-case nil
-                                         (dired-get-filename nil diredp-count-.-and-..-flag)
-                                       (error nil))
-                                 (when (<= (line-beginning-position) o-pt) (setq this  (1+ this)))
-                                 (setq total  (1+ total)))
-                               (forward-line 1))
-                             (if (not (> this 0)) (format " %d" total) (format " %d/%d" this total)))))))))))
-
-  (add-hook 'dired-after-readin-hook 'diredp-nb-marked-in-mode-name)
-  ;; This one is needed for `find-dired', because it does not call `dired-readin'.
-  (add-hook 'dired-mode-hook         'diredp-nb-marked-in-mode-name))
-
-;;;###autoload
-(defun diredp-send-bug-report ()
-  "Send a bug report about a Dired+ problem."
-  (interactive)
-  (browse-url (format (concat "mailto:" "drew.adams" "@" "oracle" ".com?subject=\
-Dired+ bug: \
-&body=Describe bug below, using a precise recipe that starts with `emacs -Q' or `emacs -q'.  \
-File `dired+.el' has a header `Update #' that you can use to identify it.\
-%%0A%%0AEmacs version: %s.")
-                      (emacs-version))))
-
-(defun diredp-visit-ignore-regexp ()    ; Taken from `image-file-name-regexp'.
-  "Return a regular expression matching file names to skip.
-This is used by `dired-visit-(next|previous)'."
-  (let ((exts-regexp  (and diredp-visit-ignore-extensions
-                           (concat "\\." (regexp-opt (nconc (mapcar #'upcase diredp-visit-ignore-extensions)
-                                                            diredp-visit-ignore-extensions)
-                                                     t)
-                                   "\\'"))))
-    (if diredp-visit-ignore-regexps
-	(mapconcat #'identity (if exts-regexp
-                                  (cons exts-regexp diredp-visit-ignore-regexps)
-                                diredp-visit-ignore-regexps)
-		   "\\|")
-      exts-regexp)))
-
-;;;###autoload
-(defun diredp-visit-next-file (&optional arg) ; Bound to `C-down'
-  "Move down a line and visit its file in another window.
-With numeric prefix arg N, move down N-1 lines first.
-
-After moving N lines, skip any lines with file names that match either
-`diredp-visit-ignore-extensions' or `diredp-visit-ignore-regexps'. 
-
-Kill the last buffer visited by a `dired-visit-*' command."
-  (interactive "p")
-  (dired-next-line arg)
-  (while (diredp-string-match-p (diredp-visit-ignore-regexp) (dired-get-file-for-visit))
-    (dired-next-line 1))
-  (diredp-visit-this-file))
-
-;;;###autoload
-(defun diredp-visit-previous-file (&optional arg) ; Bound to `C-up'
-  "Move up a line and visit its file in another window.
-With numeric prefix arg N, move up N-1 lines first.
-
-After moving N lines, skip any lines with file names that match either
-`diredp-visit-ignore-extensions' or `diredp-visit-ignore-regexps'. 
-
-Kill the last buffer visited by a `dired-visit-*' command."
-  (interactive "p")
-  (dired-previous-line arg)
-  (while (diredp-string-match-p (diredp-visit-ignore-regexp) (dired-get-file-for-visit))
-    (dired-previous-line 1))
-  (diredp-visit-this-file))
-
-;;;###autoload
-(defun diredp-visit-this-file ()        ; Bound to `e' (replaces `dired-find-file' binding)
-  "View the file on this line in another window in the same frame.
-If it was not already shown there then kill the previous buffer
-visited by a `dired-visit-*' command.
-
-If it was already shown there, and if it and Dired are the only
-windows there, then delete its window (toggle : show/hide the file)."
-  (interactive)
-  (let ((file   (dired-get-file-for-visit))
-        (obuf   (current-buffer))
-        (shown  nil)
-        fwin)
-    (unless (or (and (fboundp 'window-parent)  (window-parent))
-                (not (one-window-p 'NOMINI)))
-      (split-window))
-    (save-selected-window
-      (other-window 1)
-      (setq fwin  (selected-window))
-      (unless (or (setq shown  (or (equal (current-buffer) (get-file-buffer file))
-                                   (memq (current-buffer) (dired-buffers-for-dir file))))
-                  (equal obuf (current-buffer)))
-        (kill-buffer (current-buffer))))
-    (if shown
-        (when (= 2 (count-windows 'NOMINI)) (delete-window fwin))
-      (set-window-buffer fwin (find-file-noselect file)))))
- 
-;;; Key Bindings.
-
-
-;; Menu Bar.
-;; New order is (left -> right):
-;;
-;;     Dir  Regexp  Mark  Multiple  Single
-
-;; Get rid of menu bar predefined in `dired.el'.
-(define-key dired-mode-map [menu-bar] nil)
-;; Get rid of Edit menu bar menu to save space.
-(define-key dired-mode-map [menu-bar edit] 'undefined)
-
-
-;; `Single' menu.
-;;
-;; REPLACE ORIGINAL `Immediate' menu in `dired.el'.
-;;
-(defvar diredp-menu-bar-single-menu (make-sparse-keymap "Single"))
-(define-key dired-mode-map [menu-bar immediate] (cons "Single" diredp-menu-bar-single-menu))
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-immediate-menu 'diredp-menu-bar-single-menu))
-(make-obsolete-variable 'diredp-menu-bar-immediate-menu 'diredp-menu-bar-single-menu) ; 2017-04-09
-
-(if (fboundp 'diredp-describe-file)
-    (define-key diredp-menu-bar-single-menu [diredp-describe-file]
-      '(menu-item "Describe" diredp-describe-file
-        :help "Describe the file or directory at cursor"))
-  (define-key diredp-menu-bar-single-menu [diredp-describe-autofile]
-    '(menu-item "Describe" diredp-describe-autofile
-      :help "Describe the autofile at cursor"
-      :enable (featurep 'bookmark+))))
-(define-key diredp-menu-bar-single-menu [separator-describe] '("--")) ; ---------------------
-
-(when (fboundp 'diredp-chown-this-file)
-  (define-key diredp-menu-bar-single-menu [chown]
-    '(menu-item "Change Owner..." diredp-chown-this-file
-      :help "Change the owner of file at cursor")))
-(when (fboundp 'diredp-chgrp-this-file)
-  (define-key diredp-menu-bar-single-menu [chgrp]
-    '(menu-item "Change Group..." diredp-chgrp-this-file
-      :help "Change the group of file at cursor")))
-(define-key diredp-menu-bar-single-menu [chmod]
-  '(menu-item "Change Mode..." diredp-chmod-this-file
-    :help "Change mode (attributes) of file at cursor"))
-(when (fboundp 'dired-do-touch)         ; Emacs 22+
-  (define-key diredp-menu-bar-single-menu [touch]
-    '(menu-item "Change Timestamp (`touch')..." diredp-touch-this-file
-      :help "Change the timestamp of file at cursor, using `touch'")))
-(define-key diredp-menu-bar-single-menu [separator-change] '("--")) ; -----------------------
-
-(define-key diredp-menu-bar-single-menu [print]
-  '(menu-item "Print..." diredp-print-this-file
-    :help "Print file at cursor, supplying print command"))
-(define-key diredp-menu-bar-single-menu [grep]
-  '(menu-item "Grep..." diredp-grep-this-file :help "Grep file at cursor"))
-(define-key diredp-menu-bar-single-menu [compress]
-  '(menu-item "Compress/Uncompress" diredp-compress-this-file
-    :help "Compress/uncompress file at cursor"))
-(define-key diredp-menu-bar-single-menu [command]
-  '(menu-item "Shell Command..." diredp-shell-command-this-file
-    :help "Run a shell command on file at cursor"))
-(define-key diredp-menu-bar-single-menu [diredp-async-shell-command-this-file]
-  '(menu-item "Asynchronous Shell Command..." diredp-async-shell-command-this-file
-    :help "Run a shell command asynchronously on file at cursor"))
-(define-key diredp-menu-bar-single-menu [compile]
-  '(menu-item "Byte Compile" diredp-byte-compile-this-file
-    :help "Byte-compile this Emacs Lisp file"))
-(define-key diredp-menu-bar-single-menu [load]
-  '(menu-item "Load" diredp-load-this-file
-    :help "Load this Emacs Lisp file"))
-
-(when (fboundp 'mkhtml-dired-files)     ; In `mkhtml.el'.
-  (define-key diredp-menu-bar-single-menu [mkhtml-dired-files]
-    '(menu-item "Create HTML" mkhtml-dired-files
-      :help "Create an HTML file corresponding to file at cursor")))
-(define-key diredp-menu-bar-single-menu [separator-misc] '("--")) ; -------------------------
-
-(define-key diredp-menu-bar-single-menu [delete]
-  '(menu-item "Delete" diredp-delete-this-file :help "Delete file at cursor"))
-(define-key diredp-menu-bar-single-menu [separator-delete] '("--")) ; -----------------------
-
-(define-key diredp-menu-bar-single-menu [backup-diff]
-  '(menu-item "Diff with Backup" dired-backup-diff
-    :help "Diff file at cursor with its latest backup"))
-(define-key diredp-menu-bar-single-menu [diff]
-  '(menu-item "Diff..." dired-diff
-    :help "Compare file at cursor with another file using `diff'"))
-(define-key diredp-menu-bar-single-menu [ediff]
-  '(menu-item "Compare..." diredp-ediff :help "Compare file at cursor with another file"))
-(define-key diredp-menu-bar-single-menu [separator-diff] '("--")) ; -------------------------
-
-(define-key diredp-menu-bar-single-menu [diredp-kill-this-tree]
-  '(menu-item "Remove This Inserted Subdir and Lower" diredp-kill-this-tree
-    :visible (and (fboundp 'diredp-kill-this-tree)
-              (not (equal
-                    (expand-file-name (dired-current-directory))
-                    (expand-file-name default-directory)))))) ; In subdir, not top.
-(define-key diredp-menu-bar-single-menu [dired-kill-subdir]
-  '(menu-item "Remove This Inserted Subdir" dired-kill-subdir
-    :visible (not (equal (expand-file-name (dired-current-directory))
-                         (expand-file-name default-directory))))) ; In subdir, not top.
-(define-key diredp-menu-bar-single-menu [diredp-dired-this-subdir]
-  '(menu-item "Dired This Inserted Subdir (Tear Off)"
-    (lambda () (interactive) (diredp-dired-this-subdir t))
-    :visible (and (cdr dired-subdir-alist) ; First is current dir.  Must have at least one more.
-              (not (equal (expand-file-name (dired-current-directory))
-                          (expand-file-name default-directory)))) ; Must be sub, not top.
-    :help "Open Dired for subdir at or above point, tearing it off if inserted"))
-(define-key diredp-menu-bar-single-menu [insert-subdir]
-  '(menu-item "Insert This Subdir" dired-maybe-insert-subdir
-    :visible (and (atom (diredp-this-subdir))
-              (not (assoc (file-name-as-directory (diredp-this-subdir)) dired-subdir-alist)))
-    :enable (atom (diredp-this-subdir))
-    :help "Insert a listing of this subdirectory"))
-(define-key diredp-menu-bar-single-menu [goto-subdir]
-  '(menu-item "Go To Inserted Subdir" dired-maybe-insert-subdir
-    :visible (and (atom (diredp-this-subdir))
-              (assoc (file-name-as-directory (diredp-this-subdir)) dired-subdir-alist))
-    :enable (atom (diredp-this-subdir))
-    :help "Go to the inserted listing of this subdirectory"))
-(define-key diredp-menu-bar-single-menu [separator-subdir] '("--" ; ------------------------
-            :visible (or (atom (diredp-this-subdir)) ; Subdir line.
-                         (not (equal (expand-file-name (dired-current-directory))
-                                     (expand-file-name default-directory)))))) ; Not top.
-
-(define-key diredp-menu-bar-single-menu [view]
-  '(menu-item "View (Read Only)" dired-view-file
-    :help "Examine file at cursor in read-only mode"))
-(define-key diredp-menu-bar-single-menu [display]
-  '(menu-item "Display in Other Window" dired-display-file
-    :help "Display file at cursor in a different window"))
-
-
-;; `Single' > `Open' menu.
-;;
-(defvar diredp-single-open-menu (make-sparse-keymap "Rename")
-  "`Open' submenu for Dired menu-bar `Single' menu.")
-(define-key diredp-menu-bar-single-menu [multiple-open] (cons "Open" diredp-single-open-menu))
-
-;; On Windows, bind more.
-(eval-after-load "w32-browser"
-  '(progn
-    (define-key diredp-single-open-menu [dired-w32-browser]
-      '(menu-item "Open Associated Windows App" dired-w32-browser
-        :help "Open file using the Windows app associated with its file type"))
-    (define-key diredp-single-open-menu [dired-w32explore]
-      '(menu-item "Open in Windows Explorer" dired-w32explore
-        :help "Open file in Windows Explorer"))))
-(define-key diredp-single-open-menu [find-file-other-frame]
-  '(menu-item "Open in Other Frame" diredp-find-file-other-frame
-    :help "Edit file at cursor in a different frame"))
-(define-key diredp-single-open-menu [find-file-other-window]
-  '(menu-item "Open in Other Window" dired-find-file-other-window
-    :help "Edit file at cursor in a different window"))
-(define-key diredp-single-open-menu [find-file]
-  '(menu-item "Open" dired-find-file :help "Edit file at cursor"))
-
-
-;; `Single' > `Rename' menu.
-;;
-(defvar diredp-single-rename-menu (make-sparse-keymap "Rename")
-  "`Rename' submenu for Dired menu-bar `Single' menu.")
-(define-key diredp-menu-bar-single-menu [multiple-case] (cons "Rename" diredp-single-rename-menu))
-
-(define-key diredp-single-rename-menu [single-rename-capitalize]
-  '(menu-item "Capitalize" diredp-capitalize-this-file
-    :help "Capitalize (initial caps) name of file at cursor"))
-(define-key diredp-single-rename-menu [single-rename-downcase]
-  '(menu-item "Downcase" diredp-downcase-this-file
-    ;; When running on plain MS-DOS, there is only one letter-case for file names.
-    :enable (or (not (fboundp 'msdos-long-file-names))  (msdos-long-file-names))
-    :help "Rename file at cursor to a lower-case name"))
-(define-key diredp-single-rename-menu [single-rename-upcase]
-  '(menu-item "Upcase" diredp-upcase-this-file
-    :enable (or (not (fboundp 'msdos-long-file-names))  (msdos-long-file-names))
-    :help "Rename file at cursor to an upper-case name"))
-
-
-;; `Single' > `Move / Copy / Link' menu.
-;;
-(defvar diredp-single-move-copy-link-menu (make-sparse-keymap "Move / Copy / Link")
-  "`Move / Copy / Link' submenu for Dired menu-bar `Single' menu.")
-(define-key diredp-menu-bar-single-menu [multiple-move-copy-link]
-  (cons "Move / Copy / Link" diredp-single-move-copy-link-menu))
-
-(define-key diredp-single-move-copy-link-menu [single-hardlink]
-  '(menu-item "Hardlink to..." diredp-hardlink-this-file
-    :help "Make hard links for current or marked files"))
-(define-key diredp-single-move-copy-link-menu [single-symlink]
-  '(menu-item "Symlink to (Absolute)..." diredp-symlink-this-file
-              :help "Make absolute symbolic link for file at cursor"))
-(define-key diredp-single-move-copy-link-menu [single-relsymlink]
-  '(menu-item "Symlink to (Relative)..." diredp-relsymlink-this-file
-              :help "Make relative symbolic link for file at cursor"))
-(define-key diredp-single-move-copy-link-menu [single-copy]
-  '(menu-item "Copy to..." diredp-copy-this-file :help "Copy file at cursor"))
-(define-key diredp-single-move-copy-link-menu [single-rename]
-  '(menu-item "Move to..." diredp-rename-this-file
-    :help "Rename file at cursor, or move it to a different directory"))
-
-
-;; `Single' > `Image' menu.
-;;
-(defvar diredp-single-image-menu (make-sparse-keymap "Image"))
-(defalias 'diredp-single-image-menu diredp-single-image-menu)
-(define-key diredp-menu-bar-single-menu [image]
-  '(menu-item "Image" diredp-single-image-menu
-    :enable (let ((img-file  (diredp-get-image-filename 'LOCALP 'NO-ERROR)))
-              (and (fboundp 'image-dired-dired-display-image)  img-file))))
-
-(define-key diredp-single-image-menu [diredp-image-dired-display-thumb]
-  '(menu-item "Go To Thumbnail" diredp-image-dired-display-thumb
-    :help "Pop to buffer showing the thumbnail of this image file"))
-(define-key diredp-single-image-menu [diredp-image-dired-create-thumb]
-  '(menu-item "Create Thumbnail" diredp-image-dired-create-thumb
-    :help "Create a thumbnail image for this image file"))
-(define-key diredp-single-image-menu [diredp-image-dired-edit-comment-and-tags]
-  '(menu-item "Edit Comment and Tags..." diredp-image-dired-edit-comment-and-tags
-    :help "Edit comment and tags for this image file"))
-(define-key diredp-single-image-menu [diredp-image-dired-delete-tag]
-  '(menu-item "Delete Image Tag..." diredp-image-dired-delete-tag
-    :help "Remove an `image-dired' tag from this image file"))
-(define-key diredp-single-image-menu [diredp-image-dired-tag-file]
-  '(menu-item "Add Tags..." diredp-image-dired-tag-file
-    :help "Add tags to this image file"))
-(define-key diredp-single-image-menu [diredp-image-dired-comment-file]
-  '(menu-item "Add Comment..." diredp-image-dired-comment-file
-    :help "Add a comment to this image file"))
-(define-key diredp-single-image-menu [diredp-image-dired-copy-with-exif-name]
-  '(menu-item "Copy with EXIF Name" diredp-image-dired-copy-with-exif-name
-    :help "Copy this image file to main image dir using EXIF name"))
-(define-key diredp-single-image-menu [image-dired-dired-display-external]
-  '(menu-item "Display Externally" image-dired-dired-display-external
-    :help "Display image using external viewer"))
-(define-key diredp-single-image-menu [image-dired-dired-display-image]
-  '(menu-item "Display to Fit Other Window" image-dired-dired-display-image
-    :help "Display scaled image to fit a separate window"))
-(define-key diredp-single-image-menu [diredp-image-show-this-file]
-  '(menu-item "Display Full Size Or Smaller" diredp-image-show-this-file
-    :help "Display image full size or at least prefix-arg lines high"))
-(define-key diredp-single-image-menu [dired-find-file]
-  '(menu-item "Display Full Size" dired-find-file
-    :help "Display image full size"))
-
-
-;; `Single' > `Encryption' menu.
-;;
-(when (fboundp 'epa-dired-do-encrypt)   ; Emacs 23+
-  (defvar diredp-single-encryption-menu (make-sparse-keymap "Encryption"))
-  (define-key diredp-menu-bar-single-menu [encryption]
-    (cons "Encryption" diredp-single-encryption-menu))
-
-  (define-key diredp-single-encryption-menu [diredp-decrypt-this-file]
-    '(menu-item "Decrypt..." (lambda ()
-                               (interactive)
-                               (epa-decrypt-file (expand-file-name (dired-get-filename
-                                                                    nil 'NO-ERROR-P))))
-      :help "Decrypt this file"))
-  (define-key diredp-single-encryption-menu [diredp-verify-this-file]
-    '(menu-item "Verify..." (lambda ()
-                              (interactive)
-                              (epa-verify-file (expand-file-name (dired-get-filename
-                                                                  nil 'NO-ERROR-P))))
-      :help "Verify this file"))
-  (define-key diredp-single-encryption-menu [diredp-sign-this-file]
-    '(menu-item "Sign..." (lambda ()
-                            (interactive)
-                            (epa-sign-file (expand-file-name (dired-get-filename
-                                                              nil 'NO-ERROR-P))
-                                           (epa-select-keys (epg-make-context)
-                                                            "Select keys for signing.
-If no one is selected, default secret key is used.  "
-                                                            nil t)))
-      :help "Encrypt this file"))
-  (define-key diredp-single-encryption-menu [diredp-encrypt-this-file]
-    '(menu-item "Encrypt..." (lambda ()
-                               (interactive)
-                               (epa-encrypt-file (expand-file-name (dired-get-filename
-                                                                    nil 'NO-ERROR-P))
-                                                 (epa-select-keys
-                                                  (epg-make-context)
-                                                  "Select recipients for encryption.
-If no one is selected, symmetric encryption will be performed.  "
-                                                  nil t)))
-      :help "Sign this file")))
-
-
-;; `Single' > `Bookmark' menu.
-;;
-(when (require 'bookmark+ nil t)
-  (defvar diredp-single-bookmarks-menu (make-sparse-keymap "Bookmark"))
-  (define-key diredp-menu-bar-single-menu [bookmark]
-    (cons "Bookmark" diredp-single-bookmarks-menu))
-
-  (define-key diredp-single-bookmarks-menu [diredp-set-tag-value-this-file]
-    '(menu-item "Set Tag Value..." diredp-set-tag-value-this-file
-      :help "Set the value (not the name) of a given tag for this file"))
-  (define-key diredp-single-bookmarks-menu [diredp-paste-replace-tags-this-file]
-    '(menu-item "Paste Tags (Replace)" diredp-paste-replace-tags-this-file
-      :help "Replace tags for this file with previously copied tags"))
-  (define-key diredp-single-bookmarks-menu [diredp-paste-add-tags-this-file]
-    '(menu-item "Paste Tags (Add)" diredp-paste-add-tags-this-file
-      :help "Add previously copied tags to this file"))
-  (define-key diredp-single-bookmarks-menu [diredp-copy-tags-this-file]
-    '(menu-item "Copy Tags" diredp-copy-tags-this-file
-      :help "Copy the tags from this file, so you can paste them to another"))
-  (define-key diredp-single-bookmarks-menu [diredp-remove-all-tags-this-file]
-    '(menu-item "Remove All Tags" diredp-remove-all-tags-this-file
-      :help "Remove all tags from the file at cursor"))
-  (define-key diredp-single-bookmarks-menu [diredp-untag-this-file]
-    '(menu-item "Remove Tags..." diredp-untag-this-file
-      :help "Remove some tags from the file at cursor (`C-u': remove all tags)"))
-  (define-key diredp-single-bookmarks-menu [diredp-tag-this-file]
-    '(menu-item "Add Tags..." diredp-tag-this-file :help "Add some tags to the file at cursor"))
-  (define-key diredp-single-bookmarks-menu [diredp-bookmark-this-file]
-    '(menu-item "Bookmark..." diredp-bookmark-this-file
-      :help "Bookmark the file at cursor (create/set autofile)")))
-
-
-;; `Multiple' menu.
-;;
-;; REPLACE ORIGINAL "Operate" menu in `dired.el'.
-;;
-(defvar diredp-menu-bar-multiple-menu (make-sparse-keymap "Multiple"))
-(define-key dired-mode-map [menu-bar operate] (cons "Multiple" diredp-menu-bar-multiple-menu))
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-operate-menu 'diredp-menu-bar-multiple-menu))
-(make-obsolete-variable 'diredp-menu-bar-operate-menu 'diredp-menu-bar-multiple-menu) ; 2017-04-09
-
-(define-key diredp-menu-bar-multiple-menu [diredp-describe-marked-autofiles]
-  '(menu-item "Describe Marked Autofiles" diredp-describe-marked-autofiles
-    :help "Show the metadata for the marked files that are autofiles"
-    :enable (featurep 'bookmark+)))
-(define-key diredp-menu-bar-multiple-menu [separator-describe] '("--")) ; -----------------------
-
-(unless (memq system-type '(windows-nt ms-dos))
-  (define-key diredp-menu-bar-multiple-menu [chown]
-    '(menu-item "Change Owner..." dired-do-chown
-      :help "Change the owner of marked files")))
-(unless (memq system-type '(windows-nt ms-dos))
-  (define-key diredp-menu-bar-multiple-menu [chgrp]
-    '(menu-item "Change Group..." dired-do-chgrp
-      :help "Change the owner of marked files")))
-(define-key diredp-menu-bar-multiple-menu [chmod]
-  '(menu-item "Change Mode..." dired-do-chmod
-    :help "Change mode (attributes) of marked files"))
-(when (fboundp 'dired-do-touch)         ; Emacs 22+
-  (define-key diredp-menu-bar-multiple-menu [touch]
-    '(menu-item "Change Timestamp (`touch')..." dired-do-touch
-      :help "Change the timestamp of the marked files, using `touch'")))
-(define-key diredp-menu-bar-multiple-menu [separator-change] '("--")) ; -------------------------
-
-(when (fboundp 'diredp-read-expression) ; Emacs 22+
-  (define-key diredp-menu-bar-multiple-menu [diredp-do-lisp-sexp]
-    '(menu-item "Eval Sexp..." diredp-do-lisp-sexp
-                :help "Evaluate an Emacs-Lisp sexp in each marked file")))
-(define-key diredp-menu-bar-multiple-menu [diredp-do-emacs-command]
-    '(menu-item "Invoke Emacs Command..." diredp-do-emacs-command
-      :help "Invoke an Emacs command in each marked file"))
-(define-key diredp-menu-bar-multiple-menu [diredp-do-apply-function]
-    '(menu-item "Apply Function..." diredp-do-apply-function
-      :help "Apply a Lisp function to each marked file name (`C-u': file contents, not name)"))
-(define-key diredp-menu-bar-multiple-menu [print]
-  '(menu-item "Print..." dired-do-print :help "Print marked files, supplying print command"))
-(define-key diredp-menu-bar-multiple-menu [compress]
-  '(menu-item "Compress/Uncompress" dired-do-compress :help "Compress/uncompress marked files"))
-(when (fboundp 'dired-do-compress-to)
-  (define-key diredp-menu-bar-multiple-menu [compress-to]
-    '(menu-item "Compress to..." dired-do-compress-to
-      :help "Compress marked files and dirs together, in the same archive")))
-(define-key diredp-menu-bar-multiple-menu [command]
-  '(menu-item "Shell Command..." dired-do-shell-command
-    :help "Run a shell command on each marked file"))
-(when (fboundp 'dired-do-async-shell-command) ; Emacs 23+
-  (define-key diredp-menu-bar-multiple-menu [async-command]
-    '(menu-item "Asynchronous Shell Command..." dired-do-async-shell-command
-      :help "Run a shell command asynchronously on each marked file")))
-(define-key diredp-menu-bar-multiple-menu [compile]
-  '(menu-item "Byte Compile" dired-do-byte-compile :help "Byte-compile marked Emacs Lisp files"))
-(define-key diredp-menu-bar-multiple-menu [load]
-  '(menu-item "Load" dired-do-load :help "Load marked Emacs Lisp files"))
-
-(unless (require 'bookmark+ nil t)
-  (define-key diredp-menu-bar-multiple-menu [diredp-bookmark-this-file]
-    '(menu-item "Bookmark..." diredp-bookmark-this-file :help "Bookmark the file at cursor")))
-(when (fboundp 'mkhtml-dired-files)     ; In `mkhtml.el'.
-  (define-key diredp-menu-bar-multiple-menu [mkhtml-dired-files]
-    '(menu-item "Create HTML" mkhtml-dired-files
-      :help "Create HTML files corresponding to marked files")))
-(define-key diredp-menu-bar-multiple-menu [separator-misc] '("--")) ; ---------------------------
-
-(define-key diredp-menu-bar-multiple-menu [diredp-copy-abs-filenames-as-kill]
-  '(menu-item "Copy Marked Names as Absolute" diredp-copy-abs-filenames-as-kill
-    :help "Copy absolute names of marked files to the kill ring"
-    :keys "M-0 w"))
-(define-key diredp-menu-bar-multiple-menu [kill-ring]
-  '(menu-item "Copy Marked Names" dired-copy-filename-as-kill
-    :help "Copy names of marked files to the kill ring, for pasting"))
-(define-key diredp-menu-bar-multiple-menu [diredp-list-marked]
-    '(menu-item "List Marked Files" diredp-list-marked
-      :help "List the files marked here (C-u C-u: all, C-u C-u C-u: all + dirs)"))
-(define-key diredp-menu-bar-multiple-menu [diredp-insert-subdirs]
-  '(menu-item "Insert Subdirs" diredp-insert-subdirs
-    :help "Insert the marked subdirectories - like using `i' at each marked dir"))
-;; On Windows, bind more.
-(eval-after-load "w32-browser"
-  '(define-key diredp-menu-bar-multiple-menu [dired-multiple-w32-browser]
-      '(menu-item "Open Associated Windows Apps" dired-multiple-w32-browser
-        :help "Open files using the Windows apps associated with their file types")))
-(when (fboundp 'dired-do-find-marked-files)
-  (define-key diredp-menu-bar-multiple-menu [find-files]
-    '(menu-item "Open" dired-do-find-marked-files ; In `dired-x.el'.
-      :help "Open each marked file for editing")))
-
-
-;; `Multiple' > `Dired' menu.
-;;
-(defvar diredp-multiple-dired-menu (make-sparse-keymap "Dired")
-  "`Dired' submenu for Dired menu-bar `Multiple' menu.")
-(define-key diredp-menu-bar-multiple-menu [multiple-dired]
-  `(menu-item "Dired" ,diredp-multiple-dired-menu
-    :enable (save-excursion (goto-char (point-min))
-                            (and (re-search-forward (dired-marker-regexp) nil t)
-                                 (re-search-forward (dired-marker-regexp) nil t)))
-    :help "Open Dired on marked files and dirs only"))
-
-(define-key diredp-multiple-dired-menu [diredp-marked-other-window]
-  '(menu-item "Dired Marked in Other Window" diredp-marked-other-window
-    :enable (save-excursion (goto-char (point-min))
-                            (and (re-search-forward (dired-marker-regexp) nil t)
-                                 (re-search-forward (dired-marker-regexp) nil t)))
-    :help "Open Dired on marked files and dirs only, in other window"))
-(define-key diredp-multiple-dired-menu [diredp-marked]
-  '(menu-item "Dired Marked" diredp-marked
-    :enable (save-excursion (goto-char (point-min))
-                            (and (re-search-forward (dired-marker-regexp) nil t)
-                                 (re-search-forward (dired-marker-regexp) nil t)))
-    :help "Open Dired on marked files and dirs only"))
-
-
-;; `Multiple' > `Omit' menu.
-;;
-(defvar diredp-multiple-omit-menu (make-sparse-keymap "Omit")
-  "`Omit' submenu for Dired menu-bar `Multiple' menu.")
-(define-key diredp-menu-bar-multiple-menu [multiple-omit] (cons "Omit" diredp-multiple-omit-menu))
-
-(define-key diredp-multiple-omit-menu [omit-unmarked]
-  '(menu-item "Omit Unmarked" diredp-omit-unmarked :help "Hide lines of unmarked files"))
-(define-key diredp-multiple-omit-menu [omit-marked]
-  '(menu-item "Omit Marked" diredp-omit-marked :help "Hide lines of marked files"))
-
-
-;; `Multiple' > `Delete' menu.
-;;
-(defvar diredp-multiple-delete-menu (make-sparse-keymap "Delete")
-  "`Delete' submenu for Dired menu-bar `Multiple' menu.")
-(define-key diredp-menu-bar-multiple-menu [multiple-delete] (cons "Delete" diredp-multiple-delete-menu))
-
-(define-key diredp-multiple-delete-menu [delete-flagged]
-  '(menu-item "Delete Flagged" dired-do-flagged-delete
-    :help "Delete all files flagged for deletion (D)"))
-(define-key diredp-multiple-delete-menu [delete]
-  '(menu-item "Delete Marked (not Flagged)" dired-do-delete
-    :help "Delete current file or all marked files (not flagged files)"))
-
-
-;; `Multiple' > `Rename' menu.
-;;
-(defvar diredp-multiple-rename-menu (make-sparse-keymap "Rename")
-  "`Rename' submenu for Dired menu-bar `Multiple' menu.")
-(define-key diredp-menu-bar-multiple-menu [multiple-case] (cons "Rename" diredp-multiple-rename-menu))
-
-(define-key diredp-multiple-rename-menu [multiple-rename-rename]
-  '(menu-item "Move to Dir... / Rename This..." dired-do-rename
-    :help "Move marked (or next N) files, or rename current file"))
-
-(define-key diredp-multiple-rename-menu [multiple-rename-capitalize]
-  '(menu-item "Capitalize" diredp-capitalize
-    :help "Capitalize (initial caps) the names of all marked files"))
-(define-key diredp-multiple-rename-menu [multiple-rename-downcase]
-  '(menu-item "Downcase" dired-downcase
-    :enable (or (not (fboundp 'msdos-long-file-names))  (msdos-long-file-names))
-    :help "Rename marked files to lowercase names"))
-(define-key diredp-multiple-rename-menu [multiple-rename-upcase]
-  '(menu-item "Upcase" dired-upcase
-    :enable (or (not (fboundp 'msdos-long-file-names))  (msdos-long-file-names))
-    :help "Rename marked files to uppercase names"))
-
-
-;; `Multiple' > `Move / Copy / Link' menu.
-;;
-(defvar diredp-multiple-move-copy-link-menu (make-sparse-keymap "Move / Copy / Link")
-  "`Move / Copy / Link' submenu for Dired menu-bar `Multiple' menu.")
-(define-key diredp-menu-bar-multiple-menu [multiple-move-copy-link]
-  (cons "Move / Copy / Link" diredp-multiple-move-copy-link-menu))
-
-(define-key diredp-multiple-move-copy-link-menu [multiple-move-copy-link-hardlink]
-  '(menu-item "Hardlink to..." dired-do-hardlink
-    :help "Make hard links for current or marked files"))
-(define-key diredp-multiple-move-copy-link-menu [multiple-move-copy-link-symlink]
-  '(menu-item "Symlink to (Absolute)..." dired-do-symlink ; In `dired-aux.el'.
-              :help "Make absolute symbolic links for current or marked files"))
-(define-key diredp-multiple-move-copy-link-menu [multiple-move-copy-link-relsymlink]
-  '(menu-item "Symlink to (Relative)..." dired-do-relsymlink ; In `dired-x.el'.
-              :help "Make relative symbolic links for current or marked files"))
-(define-key diredp-multiple-move-copy-link-menu [multiple-move-copy-link-copy]
-  '(menu-item "Copy to..." dired-do-copy :help "Copy current file or all marked files"))
-(define-key diredp-multiple-move-copy-link-menu [multiple-move-copy-link-rename]
-  '(menu-item "Move to..." dired-do-rename :help "Rename current file or move marked files"))
-
-
-;; `Multiple' > `Images' menu.
-;;
-(defvar diredp-multiple-images-menu (make-sparse-keymap "Images"))
-(defalias 'diredp-multiple-images-menu diredp-multiple-images-menu)
-(define-key diredp-menu-bar-multiple-menu [images]
-  '(menu-item "Images" diredp-multiple-images-menu
-    :enable (fboundp 'image-dired-display-thumbs)))
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-images-menu 'diredp-multiple-images-menu))
-(make-obsolete-variable 'diredp-menu-bar-images-menu 'diredp-multiple-images-menu) ; 2017-04-09
-
-;; Remove the items from `Multiple' menu.
-(define-key diredp-menu-bar-multiple-menu [image-dired-delete-tag] nil)
-(define-key diredp-menu-bar-multiple-menu [image-dired-tag-files] nil)
-(define-key diredp-menu-bar-multiple-menu [image-dired-dired-comment-files] nil)
-(define-key diredp-menu-bar-multiple-menu [image-dired-display-thumbs] nil)
-
-;; Add them to `Multiple' > `Images' menu.
-(define-key diredp-multiple-images-menu [image-dired-delete-tag]
-  '(menu-item "Delete Tag..." image-dired-delete-tag
-    :help "Delete tag from marked image files"))
-(define-key diredp-multiple-images-menu [image-dired-tag-files]
-  '(menu-item "Add Tags..." image-dired-tag-files
-    :help "Add tags to marked image files"))
-(define-key diredp-multiple-images-menu [image-dired-dired-comment-files]
-  '(menu-item "Add Comment..." image-dired-dired-comment-files
-    :help "Add comment to marked image files"))
-(define-key diredp-multiple-images-menu [image-dired-display-thumbs]
-  '(menu-item "Display Thumbnails" image-dired-display-thumbs
-    :help "Display thumbnails for marked image files"))
-(define-key diredp-multiple-images-menu [diredp-do-display-images]
-  '(menu-item "Display" diredp-do-display-images
-    :help "Display the marked image files"))
-
-
-;; `Multiple' > `Encryption' menu.
-;;
-(when (fboundp 'epa-dired-do-encrypt)   ; Emacs 23+
-  (defvar diredp-multiple-encryption-menu (make-sparse-keymap "Encryption"))
-  (define-key diredp-menu-bar-multiple-menu [encryption]
-    (cons "Encryption" diredp-multiple-encryption-menu))
-
-  ;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-  ;; works for newer Emacs too.
-  (when (fboundp 'defvaralias)          ; Emacs 22+
-    (defvaralias 'diredp-menu-bar-encryption-menu 'diredp-multiple-encryption-menu))
-  (make-obsolete-variable 'diredp-menu-bar-encryption-menu 'diredp-multiple-encryption-menu) ; 2017-04-09
-
-  (when (boundp 'diredp-menu-bar-encryption-menu)
-    (defalias 'diredp-menu-bar-encryption-menu diredp-menu-bar-encryption-menu))
-  (make-obsolete 'diredp-menu-bar-encryption-menu 'diredp-multiple-encryption-menu) ; 2017-04-09
-
-  ;; Remove the items from `Multiple' menu.
-  (define-key diredp-menu-bar-multiple-menu [epa-dired-do-decrypt] nil)
-  (define-key diredp-menu-bar-multiple-menu [epa-dired-do-verify] nil)
-  (define-key diredp-menu-bar-multiple-menu [epa-dired-do-sign] nil)
-  (define-key diredp-menu-bar-multiple-menu [epa-dired-do-encrypt] nil)
-
-  ;; Add them to `Multiple' > `Encryption' menu.
-  (define-key diredp-multiple-encryption-menu [epa-dired-do-decrypt]
-    '(menu-item "Decrypt..." epa-dired-do-decrypt :help "Decrypt the marked files"))
-  (define-key diredp-multiple-encryption-menu [epa-dired-do-verify]
-    '(menu-item "Verify..." epa-dired-do-verify :help "Verify the marked files"))
-  (define-key diredp-multiple-encryption-menu [epa-dired-do-sign]
-    '(menu-item "Sign..." epa-dired-do-sign :help "Sign the marked files"))
-  (define-key diredp-multiple-encryption-menu [epa-dired-do-encrypt]
-    '(menu-item "Encrypt..." epa-dired-do-encrypt :help "Encrypt the marked files")))
-
-
-;; `Multiple' > `Search' menu.
-;;
-(defvar diredp-multiple-search-menu (make-sparse-keymap "Search"))
-(define-key diredp-menu-bar-multiple-menu [search]
-  (cons "Search" diredp-multiple-search-menu))
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-operate-search-menu 'diredp-multiple-search-menu))
-(make-obsolete-variable 'diredp-menu-bar-operate-search-menu 'diredp-multiple-search-menu) ; 2017-04-09
-
-(when (fboundp 'dired-do-isearch-regexp) ; Emacs 23+
-  (define-key diredp-multiple-search-menu [isearch-regexp]
-    '(menu-item "Isearch Regexp Files..." dired-do-isearch-regexp
-      :help "Incrementally search marked files for regexp"))
-  (define-key diredp-multiple-search-menu [isearch]
-    '(menu-item "Isearch Files..." dired-do-isearch
-      :help "Incrementally search marked files for string")))
-(when (fboundp 'dired-do-find-regexp-and-replace)
-  (define-key diredp-multiple-search-menu [find-query-replace]
-    '(menu-item "Query Replace Using `find'..." dired-do-find-regexp-and-replace
-      :help "Replace regexp in marked files using `find'")))
-(define-key diredp-multiple-search-menu [query-replace]
-  (if (< emacs-major-version 21)
-      '(menu-item "Query Replace Using TAGS Table..." dired-do-query-replace)
-    '(menu-item "Query Replace Using TAGS Table..." dired-do-query-replace-regexp
-      :help "Replace regexp in marked files using tags in a TAGS table")))
-(when (fboundp 'dired-do-find-regexp)
-  (define-key diredp-multiple-search-menu [find-regexp]
-    '(menu-item "Search Files Using `find'..." dired-do-find-regexp
-      :help "Search marked files for regexp using `find'")))
-(define-key diredp-multiple-search-menu [search]
-  '(menu-item "Search Files Using TAGS Table..." dired-do-search
-              :help "Search marked files for regexp using tags in a TAGS table"))
-(define-key diredp-multiple-search-menu [grep]
-  '(menu-item "Grep..." diredp-do-grep :help "Grep marked, next N, or all files shown"))
-
-
-;; `Multiple' > `Bookmark' menu.
-;;
-(defvar diredp-multiple-bookmarks-menu (make-sparse-keymap "Bookmark"))
-(define-key diredp-menu-bar-multiple-menu [bookmark]
-  (cons "Bookmark" diredp-multiple-bookmarks-menu))
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-operate-bookmarks-menu 'diredp-multiple-bookmarks-menu))
-(make-obsolete-variable 'diredp-menu-bar-operate-bookmarks-menu 'diredp-multiple-bookmarks-menu) ; 2017-04-09
-
-(when (require 'bookmark+ nil t)
-  (define-key diredp-multiple-bookmarks-menu [diredp-do-set-tag-value]
-    '(menu-item "Set Tag Value..." diredp-do-set-tag-value
-      :help "Set the value of a given tag for the marked or next N files"))
-  (define-key diredp-multiple-bookmarks-menu [diredp-do-paste-replace-tags]
-    '(menu-item "Paste Tags (Replace)" diredp-do-paste-replace-tags
-      :help "Replace tags for the marked or next N files with copied tags"))
-  (define-key diredp-multiple-bookmarks-menu [diredp-do-paste-add-tags]
-    '(menu-item "Paste Tags (Add)" diredp-do-paste-add-tags
-      :help "Add previously copied tags to the marked or next N files"))
-  (define-key diredp-multiple-bookmarks-menu [diredp-do-remove-all-tags]
-    '(menu-item "Remove All Tags" diredp-do-remove-all-tags
-      :help "Remove all tags from the marked or next N files"))
-  (define-key diredp-multiple-bookmarks-menu [diredp-do-untag]
-    '(menu-item "Remove Tags..." diredp-do-untag
-      :help "Remove some tags from the marked or next N files"))
-  (define-key diredp-multiple-bookmarks-menu [diredp-do-tag]
-    '(menu-item "Add Tags..." diredp-do-tag
-      :help "Add some tags to the marked or next N files"))
-  (define-key diredp-multiple-bookmarks-menu [separator-book-2] '("--"))) ; ------------
-
-(define-key diredp-multiple-bookmarks-menu
-    [diredp-do-bookmark-in-bookmark-file-recursive]
-  '(menu-item "Bookmark in Bookmark File (Here and Below)..."
-    diredp-do-bookmark-in-bookmark-file-recursive
-    :help "Bookmark marked files (including in marked subdirs) in bookmark file and save it"))
-(define-key diredp-multiple-bookmarks-menu
-    [diredp-set-bookmark-file-bookmark-for-marked-recursive]
-  '(menu-item "Create Bookmark-File Bookmark (Here and Below)..."
-    diredp-set-bookmark-file-bookmark-for-marked-recursive
-    :help "Create a bookmark-file bookmark for marked files, including in marked subdirs"))
-(define-key diredp-multiple-bookmarks-menu [diredp-do-bookmark-dirs-recursive]
-    '(menu-item "Bookmark Dirs (Here and Below)..." diredp-do-bookmark-dirs-recursive
-      :help "Bookmark this Dired buffer and marked subdirectory Dired buffers, recursively."))
-(define-key diredp-multiple-bookmarks-menu [diredp-do-bookmark-recursive]
-  '(menu-item "Bookmark (Here and Below)..." diredp-do-bookmark-recursive
-    :help "Bookmark the marked files, including those in marked subdirs"))
-(define-key diredp-multiple-bookmarks-menu [separator-book-1] '("--")) ; ---------------
-
-(define-key diredp-multiple-bookmarks-menu [diredp-do-bookmark-in-bookmark-file]
-  '(menu-item "Bookmark in Bookmark File..." diredp-do-bookmark-in-bookmark-file
-    :help "Bookmark the marked files in BOOKMARK-FILE and save BOOKMARK-FILE"))
-(define-key diredp-multiple-bookmarks-menu [diredp-set-bookmark-file-bookmark-for-marked]
-  '(menu-item "Create Bookmark-File Bookmark..." diredp-set-bookmark-file-bookmark-for-marked
-    :help "Create a bookmark-file bookmark, and bookmark the marked files in it"))
-(define-key diredp-multiple-bookmarks-menu [diredp-do-bookmark]
-  '(menu-item "Bookmark..." diredp-do-bookmark :help "Bookmark the marked or next N files"))
-
-
-;; `Multiple' > `Marked Here and Below' menu.
-;;
-(defvar diredp-multiple-recursive-menu (make-sparse-keymap "Marked Here and Below"))
-(define-key diredp-menu-bar-multiple-menu [operate-recursive]
-  (cons "Marked Here and Below" diredp-multiple-recursive-menu))
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-operate-recursive-menu 'diredp-multiple-recursive-menu))
-(make-obsolete-variable 'diredp-menu-bar-operate-recursive-menu 'diredp-multiple-recursive-menu) ; 2017-04-09
-
-(when (fboundp 'diredp-do-chown-recursive)
-  (define-key diredp-multiple-recursive-menu [chown]
-    '(menu-item "Change Owner..." diredp-do-chown-recursive
-      :help "Change the owner of marked files, including those in marked subdirs")))
-(when (fboundp 'diredp-do-chgrp-recursive)
-  (define-key diredp-multiple-recursive-menu [chgrp]
-    '(menu-item "Change Group..." diredp-do-chgrp-recursive
-      :help "Change the owner of marked files, including those in marked subdirs")))
-(define-key diredp-multiple-recursive-menu [chmod]
-  '(menu-item "Change Mode..." diredp-do-chmod-recursive
-    :help "Change mode (attributes) of marked files, including those in marked subdirs"))
-(when (fboundp 'dired-do-touch)         ; Emacs 22+
-  (define-key diredp-multiple-recursive-menu [touch]
-    '(menu-item "Change Timestamp (`touch')..." diredp-do-touch-recursive
-      :help "Change timestamp of marked files, including those in marked subdirs")))
-(define-key diredp-multiple-recursive-menu [separator-change] '("--")) ; ----------------
-
-(define-key diredp-multiple-recursive-menu [diredp-do-apply-function-recursive]
-    '(menu-item "Apply Lisp Function..." diredp-do-apply-function-recursive
-      :help "Apply a Lisp function to the marked files, including those in marked subdirs"))
-(define-key diredp-multiple-recursive-menu [diredp-do-print-recursive]
-    '(menu-item "Print..." diredp-do-print-recursive
-      :help "Print the marked files, including those in marked subdirs"))
-(define-key diredp-multiple-recursive-menu [diredp-do-shell-command-recursive]
-    '(menu-item "Shell Command..." diredp-do-shell-command-recursive
-      :help "Run shell command on the marked files, including those in marked subdirs"))
-(when (fboundp 'dired-do-async-shell-command) ; Emacs 23+
-  (define-key diredp-multiple-recursive-menu [diredp-do-async-shell-command-recursive]
-    '(menu-item "Asynchronous Shell Command..." diredp-do-async-shell-command-recursive
-      :help "Run shell command asynchronously on marked files, including in marked subdirs")))
-
-(when (fboundp 'diredp-unmark-all-marks-recursive) ; Emacs 22+
-  (define-key diredp-multiple-recursive-menu [separator-1] '("--")) ; ------------
-  (define-key diredp-multiple-recursive-menu [diredp-change-marks-recursive]
-    '(menu-item "Change Mark..." diredp-change-marks-recursive
-      :help "Change all OLD marks to NEW marks, including those in marked subdirs"))
-  (define-key diredp-multiple-recursive-menu [diredp-unmark-all-files-recursive]
-    '(menu-item "Unmark Marked-With..." diredp-unmark-all-files-recursive
-      :help "Remove a given mark everywhere, including in marked subdirs"))
-  (define-key diredp-multiple-recursive-menu [diredp-unmark-all-marks-recursive]
-    '(menu-item "Unmark All..." diredp-unmark-all-marks-recursive
-      :help "Remove ALL marks everywhere, including in marked subdirs")))
-
-(define-key diredp-multiple-recursive-menu [separator-misc] '("--")) ; ------------------
-
-(define-key diredp-multiple-recursive-menu [diredp-do-delete-recursive]
-    '(menu-item "Delete Marked (not Flagged)" diredp-do-delete-recursive
-      :help "Delete marked (not flagged) files, including in marked subdirs"))
-(define-key diredp-multiple-recursive-menu [separator-delete] '("--")) ; ----------------
-
-(define-key diredp-multiple-recursive-menu [diredp-do-hardlink-recursive]
-  '(menu-item "Hardlink to..." diredp-do-hardlink-recursive
-    :help "Make hard links for marked files, including those in marked subdirs"))
-(define-key diredp-multiple-recursive-menu [diredp-do-symlink-recursive]
-  '(menu-item "Symlink to (Absolute)..." diredp-do-symlink-recursive
-              :help "Make absolute symbolic links for marked files, including those in marked subdirs"))
-(define-key diredp-multiple-recursive-menu [diredp-do-relsymlink-recursive]
-  '(menu-item "Symlink to (Relative)..." diredp-do-relsymlink-recursive
-              :help "Make relative symbolic links for marked files, including those in marked subdirs"))
-(define-key diredp-multiple-recursive-menu [diredp-do-copy-recursive]
-    '(menu-item "Copy to..." diredp-do-copy-recursive
-      :help "Copy marked files, including in marked subdirs, to a given directory"))
-(define-key diredp-multiple-recursive-menu [diredp-do-move-recursive]
-    '(menu-item "Move to..." diredp-do-move-recursive
-      :help "Move marked files, including in marked subdirs, to a given directory"))
-(define-key diredp-multiple-recursive-menu [separator-copy-move] '("--")) ; -------------
-
-(define-key diredp-multiple-recursive-menu [diredp-capitalize-recursive]
-  '(menu-item "Capitalize" diredp-capitalize-recursive
-    :enable (or (not (fboundp 'msdos-long-file-names))  (msdos-long-file-names))
-    :help "Capitalize the names of all marked files, including in marked subdirs"))
-(define-key diredp-multiple-recursive-menu [diredp-downcase-recursive]
-  '(menu-item "Downcase" diredp-downcase-recursive
-    :enable (or (not (fboundp 'msdos-long-file-names))  (msdos-long-file-names))
-    :help "Rename marked files, including in marked subdirs, to lowercase names"))
-(define-key diredp-multiple-recursive-menu [diredp-upcase-recursive]
-  '(menu-item "Upcase" diredp-upcase-recursive
-    :enable (or (not (fboundp 'msdos-long-file-names))  (msdos-long-file-names))
-    :help "Rename marked files, including in marked subdirs, to uppercase names"))
-(define-key diredp-multiple-recursive-menu [separator-lettercase] '("--")) ; ------------
-
-(define-key diredp-multiple-recursive-menu [diredp-list-marked-recursive]
-    '(menu-item "List Marked Files" diredp-list-marked-recursive
-      :help "List the files marked here and in marked subdirs, recursively"))
-(define-key diredp-multiple-recursive-menu [diredp-copy-filename-as-kill-recursive]
-    '(menu-item "Copy File Names (to Paste)" diredp-copy-filename-as-kill-recursive
-      :help "Copy names of files marked here and in marked subdirs, to `kill-ring'"))
-(define-key diredp-multiple-recursive-menu [diredp-insert-subdirs-recursive]
-  '(menu-item "Insert Subdirs" diredp-insert-subdirs-recursive
-    :help "Insert the marked subdirectories, gathered recursively"))
-(define-key diredp-multiple-recursive-menu [separator-dirs] '("--")) ; ------------------
-
-(define-key diredp-multiple-recursive-menu [diredp-marked-recursive-other-window]
-    '(menu-item "Dired (Marked) in Other Window" diredp-marked-recursive-other-window
-      :help "Open Dired (in other window) on marked files, including those in marked subdirs"))
-(define-key diredp-multiple-recursive-menu [diredp-marked-recursive]
-    '(menu-item "Dired (Marked)" diredp-marked-recursive
-      :help "Open Dired on marked files, including those in marked subdirs"))
-;; On Windows, bind more.
-(eval-after-load "w32-browser"
-  '(define-key diredp-multiple-recursive-menu [diredp-multiple-w32-browser-recursive]
-    '(menu-item "Open Associated Windows Apps" diredp-multiple-w32-browser-recursive
-      :help "Run Windows apps for with marked files, including those in marked subdirs")))
-(define-key diredp-multiple-recursive-menu [diredp-do-find-marked-files-recursive]
-    '(menu-item "Open" diredp-do-find-marked-files-recursive
-      :help "Find marked files simultaneously, including those in marked subdirs"))
-
-
-;; `Multiple' > `Marked Here and Below' > `Images' menu.
-;;
-(defvar diredp-images-recursive-menu (make-sparse-keymap "Images"))
-(defalias 'diredp-images-recursive-menu diredp-images-recursive-menu)
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-images-recursive-menu 'diredp-images-recursive-menu))
-(make-obsolete-variable 'diredp-menu-bar-images-recursive-menu 'diredp-images-recursive-menu) ; 2017-04-09
-
-(when (boundp 'diredp-menu-bar-images-recursive-menu)
-  (defalias 'diredp-menu-bar-images-recursive-menu diredp-menu-bar-images-recursive-menu))
-(make-obsolete 'diredp-menu-bar-images-recursive-menu 'diredp-images-recursive-menu) ; 2017-04-09
-
-(define-key diredp-multiple-recursive-menu [images]
-  '(menu-item "Images" diredp-images-recursive-menu
-    :enable (fboundp 'image-dired-delete-tag)))
-(define-key diredp-images-recursive-menu [diredp-image-dired-delete-tag-recursive]
-  '(menu-item "Delete Image Tag..." diredp-image-dired-delete-tag-recursive
-    :help "Remove an `image-dired' tag from marked files, including those in marked subdirs"))
-(define-key diredp-images-recursive-menu [diredp-image-dired-tag-files-recursive]
-  '(menu-item "Add Image Tags..." diredp-image-dired-tag-files-recursive
-    :help "Add `image-dired' tags to marked files, including those in marked subdirs"))
-(define-key diredp-images-recursive-menu [diredp-image-dired-comment-files-recursive]
-  '(menu-item "Add Image Comment..." diredp-image-dired-comment-files-recursive
-    :help "Add image comment to marked files, including those in marked subdirs"))
-(define-key diredp-images-recursive-menu [diredp-image-dired-display-thumbs-recursive]
-  '(menu-item "Display Image Thumbnails" diredp-image-dired-display-thumbs-recursive
-    :help "Show thumbnails for marked image files, including those in marked subdirs"))
-
-
-;; `Multiple' > `Marked Here and Below' > `Encryption' menu.
-;;
-(when (fboundp 'epa-dired-do-encrypt)   ; Emacs 23+
-  (defvar diredp-menu-bar-encryption-recursive-menu (make-sparse-keymap "Encryption"))
-  (define-key diredp-multiple-recursive-menu [encryption]
-    (cons "Encryption" diredp-menu-bar-encryption-recursive-menu))
-  (define-key diredp-menu-bar-encryption-recursive-menu [diredp-do-decrypt-recursive]
-    '(menu-item "Decrypt..." diredp-do-decrypt-recursive
-      :help "Decrypt marked files, including those in marked subdirs"))
-  (define-key diredp-menu-bar-encryption-recursive-menu [diredp-do-verify-recursive]
-    '(menu-item "Verify..." diredp-do-verify-recursive
-      :help "Verify marked files, including those in marked subdirs"))
-  (define-key diredp-menu-bar-encryption-recursive-menu [diredp-do-sign-recursive]
-    '(menu-item "Sign..." diredp-do-sign-recursive
-      :help "Sign marked files, including those in marked subdirs"))
-  (define-key diredp-menu-bar-encryption-recursive-menu [diredp-do-encrypt-recursive]
-    '(menu-item "Encrypt..." diredp-do-encrypt-recursive
-      :help "Encrypt marked files, including those in marked subdirs")))
-
-
-;; `Multiple' > `Marked Here and Below' > `Search' menu.
-;;
-(defvar diredp-menu-bar-search-recursive-menu (make-sparse-keymap "Search"))
-(define-key diredp-multiple-recursive-menu [search]
-  (cons "Search" diredp-menu-bar-search-recursive-menu))
-(when (fboundp 'dired-do-isearch-regexp) ; Emacs 23+
-  (define-key diredp-menu-bar-search-recursive-menu [diredp-do-isearch-regexp-recursive]
-    '(menu-item "Isearch Regexp Files..." diredp-do-isearch-regexp-recursive
-      :help "Incrementally regexp search marked files, including those in marked subdirs"))
-  (define-key diredp-menu-bar-search-recursive-menu [diredp-do-isearch-recursive]
-    '(menu-item "Isearch Files..." diredp-do-isearch-recursive
-      :help "Incrementally search marked files, including those in marked subdirs")))
-(define-key diredp-menu-bar-search-recursive-menu [diredp-do-query-replace-regexp-recursive]
-  '(menu-item "Query Replace..." diredp-do-query-replace-regexp-recursive
-    :help "Replace regexp in marked files, including those in marked subdirs"))
-(define-key diredp-menu-bar-search-recursive-menu [diredp-do-search-recursive]
-  '(menu-item "Search Files..." diredp-do-search-recursive
-    :help "Regexp search marked files, including those in marked subdirs"))
-(define-key diredp-menu-bar-search-recursive-menu [diredp-do-grep-recursive]
-  '(menu-item "Grep..." diredp-do-grep-recursive
-    :help "Run `grep' on the marked files, including those in marked subdirs"))
-
-
-;; `Multiple' > `Marked Here and Below' > `Bookmark' menu.
-;;
-(defvar diredp-menu-bar-bookmarks-recursive-menu (make-sparse-keymap "Bookmark"))
-(define-key diredp-multiple-recursive-menu [bookmarks]
-  (cons "Bookmark" diredp-menu-bar-bookmarks-recursive-menu))
-(define-key diredp-menu-bar-bookmarks-recursive-menu
-    [diredp-do-bookmark-in-bookmark-file-recursive]
-    '(menu-item "Bookmark in Bookmark File..." diredp-do-bookmark-in-bookmark-file-recursive
-      :help "Bookmark marked files, including those in marked subdirs, in a bookmark file"))
-(define-key diredp-menu-bar-bookmarks-recursive-menu
-    [diredp-set-bookmark-file-bookmark-for-marked-recursive]
-  '(menu-item "Create Bookmark-File Bookmark..."
-    diredp-set-bookmark-file-bookmark-for-marked-recursive
-    :help "Create a bookmark-file bookmark for marked files, including in marked subdirs"))
-(define-key diredp-menu-bar-bookmarks-recursive-menu [diredp-do-bookmark-dirs-recursive]
-    '(menu-item "Bookmark Dirs..." diredp-do-bookmark-dirs-recursive
-      :help "Bookmark this Dired buffer and marked subdirectory Dired buffers, recursively."))
-(define-key diredp-menu-bar-bookmarks-recursive-menu [diredp-do-bookmark-recursive]
-    '(menu-item "Bookmark..." diredp-do-bookmark-recursive
-      :help "Bookmark the marked files, including those in marked subdirs"))
-
-
-
-;; `Regexp' menu.
-;;
-;; REPLACE ORIGINAL `Regexp' menu in `dired.el'.
-;;
-(defvar diredp-menu-bar-regexp-menu (make-sparse-keymap "Regexp"))
-(define-key dired-mode-map [menu-bar regexp] (cons "Regexp" diredp-menu-bar-regexp-menu))
-
-(define-key diredp-menu-bar-regexp-menu [hardlink]
-  '(menu-item "Hardlink to..." dired-do-hardlink-regexp ; In `dired-aux.el'.
-    :help "Make hard links for files matching regexp"))
-(define-key diredp-menu-bar-regexp-menu [symlink]
-  '(menu-item "Symlink to (Absolute)..." dired-do-symlink-regexp ; In `dired-aux.el'.
-              :help "Make absolute symbolic links for files matching regexp"))
-(define-key diredp-menu-bar-regexp-menu [relsymlink]
-  '(menu-item "Symlink to (Relative)..." dired-do-relsymlink-regexp ; In `dired-x.el'.
-              :help "Make relative symbolic links for files matching regexp"))
-(define-key diredp-menu-bar-regexp-menu [copy]
-  '(menu-item "Copy to..." dired-do-copy-regexp ; In `dired-aux.el'.
-              :help "Copy marked files matching regexp"))
-(define-key diredp-menu-bar-regexp-menu [rename]
-  '(menu-item "Move to..." dired-do-rename-regexp ; In `dired-aux.el'.
-              :help "Move marked files matching regexp"))
-(define-key diredp-menu-bar-regexp-menu [flag]
-  '(menu-item "Flag..." dired-flag-files-regexp :help "Flag files matching regexp for deletion"))
-(define-key diredp-menu-bar-regexp-menu [image-dired-mark-tagged-files]
-  '(menu-item "Mark Image Files Tagged..." image-dired-mark-tagged-files
-    :enable (fboundp 'image-dired-mark-tagged-files)
-    :help "Mark image files whose image tags match regexp"))
-(define-key diredp-menu-bar-regexp-menu [mark-cont]
-  '(menu-item "Mark Containing..." dired-mark-files-containing-regexp
-    :help "Mark files whose contents matches regexp"))
-(define-key diredp-menu-bar-regexp-menu [mark]
-  '(menu-item "Mark..." dired-mark-files-regexp
-    :help "Mark files matching regexp"))
-
-
-;; `Regexp' > `Here and Below' menu.
-;;
-(defvar diredp-regexp-recursive-menu (make-sparse-keymap "Here and Below"))
-(define-key diredp-menu-bar-regexp-menu [mark-recursive]
-  (cons "Here and Below" diredp-regexp-recursive-menu))
-(define-key diredp-regexp-recursive-menu [diredp-mark-files-regexp-recursive]
-    '(menu-item "Mark Named..." diredp-mark-files-regexp-recursive
-      :help "Mark all file names matching a regexp, including those in marked subdirs"))
-(define-key diredp-regexp-recursive-menu [diredp-mark-files-containing-regexp-recursive]
-    '(menu-item "Mark Containing..." diredp-mark-files-containing-regexp-recursive
-      :help "Mark all files with content matching a regexp, including in marked subdirs"))
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-regexp-recursive-menu 'diredp-regexp-recursive-menu))
-(make-obsolete-variable 'diredp-menu-bar-regexp-recursive-menu 'diredp-regexp-recursive-menu) ; 2017-04-09
-
-(when (boundp 'diredp-menu-bar-regexp-recursive-menu)
-  (defalias 'diredp-menu-bar-regexp-recursive-menu diredp-menu-bar-regexp-recursive-menu))
-(make-obsolete 'diredp-menu-bar-regexp-recursive-menu 'diredp-regexp-recursive-menu) ; 2017-04-09
-
-
-;; "Marks" menu.
-;;
-;; REPLACE ORIGINAL `Marks' menu in `dired.el'.
-;;
-(defvar diredp-menu-bar-marks-menu (make-sparse-keymap "Marks"))
-(define-key dired-mode-map [menu-bar mark] (cons "Marks" diredp-menu-bar-marks-menu))
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-mark-menu 'diredp-menu-bar-marks-menu))
-(make-obsolete-variable 'diredp-menu-bar-mark-menu 'diredp-menu-bar-marks-menu) ; 2017-04-09
-
-(define-key diredp-menu-bar-marks-menu [prev]
-  '(menu-item "Previous Marked" dired-prev-marked-file :help "Move to previous marked file"))
-(define-key diredp-menu-bar-marks-menu [next]
-  '(menu-item "Next Marked" dired-next-marked-file :help "Move to next marked file"))
-(define-key diredp-menu-bar-marks-menu [marks]
-  '(menu-item "Change Mark..." dired-change-marks
-    :help "Replace a given mark character with another"))
-(define-key diredp-menu-bar-marks-menu [toggle-marks]
-  (if (> emacs-major-version 21)
-      '(menu-item "Toggle Marked/Unmarked" dired-toggle-marks
-        :help "Mark unmarked files, unmark marked ones")
-    '(menu-item "Toggle Marked/Unmarked" dired-toggle-marks
-      :help "Mark unmarked files, unmark marked ones")))
-
-
-;; `Marks' > `Tagged' menu.
-;;
-(when (require 'bookmark+ nil t)
-  (defvar diredp-marks-tags-menu (make-sparse-keymap "Tagged (Autofiles)")
-    "`Tags' submenu for Dired menu-bar `Marks' menu.")
-  (define-key diredp-menu-bar-marks-menu [mark-tags] (cons "Tagged" diredp-marks-tags-menu))
-
-  (define-key diredp-marks-tags-menu [diredp-unmark-files-tagged-none]
-    '(menu-item "Unmark Not Tagged with Any..." diredp-unmark-files-tagged-none
-      :help "Unmark files that are not tagged with *any* of the tags you enter"))
-  (define-key diredp-marks-tags-menu [diredp-unmark-files-tagged-not-all]
-    '(menu-item "Unmark Not Tagged with All..." diredp-unmark-files-tagged-not-all
-      :help "Unmark files that are not tagged with *all* tags"))
-  (define-key diredp-marks-tags-menu [diredp-unmark-files-tagged-some]
-    '(menu-item "Unmark Tagged with Some..." diredp-unmark-files-tagged-some
-      :help "Unmark files that are tagged with at least one of the tags you enter"))
-  (define-key diredp-marks-tags-menu [diredp-unmark-files-tagged-all]
-    '(menu-item "Unmark Tagged with All..." diredp-unmark-files-tagged-all
-      :help "Unmark files that are tagged with *each* tag you enter"))
-  (define-key diredp-marks-tags-menu [diredp-unmark-files-tagged-regexp]
-    '(menu-item "Unmark Tagged Matching Regexp..." diredp-unmark-files-tagged-regexp
-      :help "Unmark files that have at least one tag that matches a regexp"))
-  (define-key diredp-marks-tags-menu [separator-marks-tags] '("--")) ; -------------------------
-
-  (define-key diredp-marks-tags-menu [diredp-mark-files-tagged-none]
-    '(menu-item "Mark Not Tagged with Any..." diredp-mark-files-tagged-none
-      :help "Mark files that are not tagged with *any* of the tags you enter"))
-  (define-key diredp-marks-tags-menu [diredp-mark-files-tagged-not-all]
-    '(menu-item "Mark Not Tagged with All..." diredp-mark-files-tagged-not-all
-      :help "Mark files that are not tagged with *all* tags"))
-  (define-key diredp-marks-tags-menu [diredp-mark-files-tagged-some]
-    '(menu-item "Mark Tagged with Some..." diredp-mark-files-tagged-some
-      :help "Mark files that are tagged with at least one of the tags you enter"))
-  (define-key diredp-marks-tags-menu [diredp-mark-files-tagged-all]
-    '(menu-item "Mark Tagged with All..." diredp-mark-files-tagged-all
-      :help "Mark files that are tagged with *each* tag you enter"))
-  (define-key diredp-marks-tags-menu [diredp-mark-files-tagged-regexp]
-    '(menu-item "Mark Tagged Matching Regexp..." diredp-mark-files-tagged-regexp
-      :help "Mark files that have at least one tag that matches a regexp")))
-
-
-;; `Marks' > `Omit' menu.
-;;
-(defvar diredp-marks-omit-menu (make-sparse-keymap "Omit")
-  "`Omit' submenu for Dired menu-bar `Marks' menu.")
-(define-key diredp-menu-bar-marks-menu [marks-omit] (cons "Omit" diredp-marks-omit-menu))
-
-(define-key diredp-marks-omit-menu [marks-omit-unmarked]
-  '(menu-item "Omit Unmarked" diredp-omit-unmarked :help "Hide lines of unmarked files"))
-(define-key diredp-marks-omit-menu [marks-omit-marked]
-  '(menu-item "Omit Marked" diredp-omit-marked :help "Hide lines of marked files"))
-
-
-;; `Marks' > `Flag' menu.
-;;
-(defvar diredp-marks-flag-menu (make-sparse-keymap "Flag")
-  "`Flag' submenu for Dired menu-bar `Marks' menu.")
-(define-key diredp-menu-bar-marks-menu [mark-flag] (cons "Flag" diredp-marks-flag-menu))
-
-(define-key diredp-marks-flag-menu [marks-flag-extension]
-  '(menu-item "Flag Extension..." dired-flag-extension ; In `dired-x.el'
-              :help "Flag all files that have a certain extension, for deletion"))
-(define-key diredp-marks-flag-menu [marks-flag-garbage-files]
-  '(menu-item "Flag Garbage Files" dired-flag-garbage-files
-    :help "Flag unneeded files for deletion"))
-(define-key diredp-marks-flag-menu [marks-flag-backup-files]
-  '(menu-item "Flag Backup Files" dired-flag-backup-files
-    :help "Flag all backup files for deletion"))
-(define-key diredp-marks-flag-menu [marks-flag-auto-save-files]
-  '(menu-item "Flag Auto-save Files" dired-flag-auto-save-files
-    :help "Flag auto-save files for deletion"))
-(define-key diredp-marks-flag-menu [marks-flag-region]
-  '(menu-item "Flag Region" diredp-flag-region-files-for-deletion
-    :visible (diredp-nonempty-region-p)
-    :help "Flag all files in the region (selection) for deletion"))
-(when (< emacs-major-version 21)
-  (put 'diredp-flag-region-files-for-deletion 'menu-enable '(diredp-nonempty-region-p)))
-(define-key diredp-marks-flag-menu [marks-flag-deletion]
-  '(menu-item "Flag This" dired-flag-file-deletion
-    :visible (not (diredp-nonempty-region-p))
-    :help "Flag current line's file for deletion"))
-
-
-;; `Marks' > `Unmark' menu.
-;;
-(defvar diredp-marks-unmark-menu (make-sparse-keymap "Unmark")
-  "`Unmark' submenu for Dired menu-bar `Marks' menu.")
-(define-key diredp-menu-bar-marks-menu [mark-mark] (cons "Unmark" diredp-marks-unmark-menu))
-
-(define-key diredp-marks-unmark-menu [unmark-autofiles]
-  '(menu-item "Unmark Autofiles" diredp-unmark-autofiles
-    :help "Unmark all autofiles (bookmarks with same name as file)"
-    :enable (featurep 'bookmark+)))
-(define-key diredp-marks-unmark-menu [unmark-all]
-  '(menu-item "Unmark All" dired-unmark-all-marks :help "Remove all marks from all files"))
-(define-key diredp-marks-unmark-menu [unmark-with]
-  '(menu-item "Unmark Marked-With..." dired-unmark-all-files
-    :help "Remove a specific mark (or all marks) from every file"))
-(define-key diredp-marks-unmark-menu [unmark-region]
-  '(menu-item "Unmark Region" diredp-unmark-region-files
-    :visible (diredp-nonempty-region-p)
-    :help "Unmark all files in the region (selection)"))
-(when (< emacs-major-version 21)
-  (put 'diredp-unmark-region-files 'menu-enable '(diredp-nonempty-region-p)))
-(define-key diredp-marks-unmark-menu [unmark-this]
-  '(menu-item "Unmark This" dired-unmark
-    :visible (not (diredp-nonempty-region-p))
-    :help "Unmark or unflag current line's file"))
-
-
-;; `Marks' > `Mark' menu.
-;;
-(defvar diredp-marks-mark-menu (make-sparse-keymap "Mark")
-  "`Mark' submenu for Dired menu-bar `Marks' menu.")
-(define-key diredp-menu-bar-marks-menu [marks-mark] (cons "Mark" diredp-marks-mark-menu))
-
-(define-key diredp-marks-mark-menu [marks-mark-sexp]
-  '(menu-item "Mark If..." dired-mark-sexp ; In `dired-x.el'.
-              :help "Mark files that satisfy specified condition"))
-(define-key diredp-marks-mark-menu [marks-image-dired-mark-tagged-files]
-  '(menu-item "Mark Image Files Tagged..." image-dired-mark-tagged-files
-    :enable (fboundp 'image-dired-mark-tagged-files) ; In `image-dired.el'.
-    :help "Mark image files whose image tags match regexp"))
-(define-key diredp-marks-mark-menu [marks-mark-cont]
-  '(menu-item "Mark Content Matching Regexp..." dired-mark-files-containing-regexp
-    :help "Mark files whose contents matches regexp"))
-(define-key diredp-marks-mark-menu [marks-mark...]
-  '(menu-item "Mark Name Matching Regexp..." dired-mark-files-regexp
-    :help "Mark file names matching regexp"))
-(when (fboundp 'dired-mark-omitted)     ; In `dired-x.el', Emacs 22+.
-  (define-key diredp-marks-mark-menu [marks-mark-omitted]
-    '(menu-item "Mark Omitted..." dired-mark-omitted
-      :help "Mark all omitted files and subdirectories")))
-(define-key diredp-marks-mark-menu [marks-mark-extension]
-  '(menu-item "Mark Extension..." diredp-mark/unmark-extension
-    :help "Mark all files with specified extension"))
-(define-key diredp-marks-mark-menu [marks-mark-autofiles]
-  '(menu-item "Mark Autofiles" diredp-mark-autofiles
-    :help "Mark all autofiles (bookmarks with same name as file)"
-    :enable (featurep 'bookmark+)))
-(define-key diredp-marks-mark-menu [marks-mark-symlinks]
-  '(menu-item "Mark Symlinks" dired-mark-symlinks
-    :visible (fboundp 'make-symbolic-link) :help "Mark all symbolic links"))
-(define-key diredp-marks-mark-menu [marks-mark-directories]
-  '(menu-item "Mark Directories" dired-mark-directories
-    :help "Mark all directories except `.' and `..'"))
-(define-key diredp-marks-mark-menu [marks-mark-directory]
-  '(menu-item "Mark Old Backups" dired-clean-directory
-    :help "Flag old numbered backups for deletion"))
-(define-key diredp-marks-mark-menu [marks-mark-executables]
-  '(menu-item "Mark Executables" dired-mark-executables :help "Mark all executable files"))
-(define-key diredp-marks-mark-menu [marks-mark-region]
-  '(menu-item "Mark Region" diredp-mark-region-files
-    :visible (diredp-nonempty-region-p)
-    :help "Mark all of the files in the region (selection)"))
-(when (< emacs-major-version 21)
-  (put 'diredp-mark-region-files 'menu-enable '(diredp-nonempty-region-p)))
-(define-key diredp-marks-mark-menu [marks-mark-this]
-  '(menu-item "Mark This" dired-mark
-    :visible (not (diredp-nonempty-region-p))
-    :help "Mark current line's file for future operations"))
-
-
-;; `Marks' > `Here and Below' menu.
-;;
-(defvar diredp-marks-recursive-menu (make-sparse-keymap "Here and Below"))
-(define-key diredp-menu-bar-marks-menu [mark-recursive]
-  (cons "Here and Below" diredp-marks-recursive-menu))
-
-(define-key diredp-marks-recursive-menu [diredp-flag-auto-save-files-recursive]
-    '(menu-item "Flag Auto-Save Files..." diredp-flag-auto-save-files-recursive
-      :help "Flag all auto-save files for deletion, including those in marked subdirs"))
-(when (fboundp 'diredp-unmark-all-marks-recursive) ; Emacs 22+
-  (define-key diredp-marks-recursive-menu [diredp-change-marks-recursive]
-    '(menu-item "Change Mark..." diredp-change-marks-recursive
-      :help "Change all OLD marks to NEW marks, including those in marked subdirs"))
-  (define-key diredp-marks-recursive-menu [diredp-unmark-all-files-recursive]
-    '(menu-item "Unmark Marked-With..." diredp-unmark-all-files-recursive
-      :help "Remove a given mark everywhere, including in marked subdirs"))
-  (define-key diredp-marks-recursive-menu [diredp-unmark-all-marks-recursive]
-    '(menu-item "Unmark All..." diredp-unmark-all-marks-recursive
-      :help "Remove ALL marks everywhere, including in marked subdirs"))
-  (define-key diredp-marks-recursive-menu [separator-1] '("--"))) ; ------------
-(define-key diredp-marks-recursive-menu [diredp-mark-sexp-recursive]
-  '(menu-item "If..." diredp-mark-sexp-recursive
-    :help "Mark files satisfying specified condition, including those in marked subdirs"))
-(define-key diredp-marks-recursive-menu [diredp-mark-files-containing-regexp-recursive]
-  '(menu-item "Containing Regexp..." diredp-mark-files-containing-regexp-recursive
-    :help "Mark all files with content matching a regexp, including in marked subdirs"))
-(define-key diredp-marks-recursive-menu [diredp-mark-files-regexp-recursive]
-  '(menu-item "Named Regexp..." diredp-mark-files-regexp-recursive
-    :help "Mark all file names matching a regexp, including those in marked subdirs"))
-(define-key diredp-marks-recursive-menu [diredp-mark-extension-recursive]
-  '(menu-item "Extension..." diredp-mark-extension-recursive
-    :help "Mark all files with a given extension, including those in marked subdirs"))
-(define-key diredp-marks-recursive-menu [diredp-mark-autofiles-recursive]
-  '(menu-item "Autofiles" diredp-mark-autofiles-recursive
-    :help "Mark all files with a given extension, including those in marked subdirs"
-    :enable (featurep 'bookmark+)))
-(define-key diredp-marks-recursive-menu [diredp-mark-symlinks-recursive]
-  '(menu-item "Symbolic Links" diredp-mark-symlinks-recursive
-    :help "Mark all symbolic links, including those in marked subdirs"))
-(define-key diredp-marks-recursive-menu [diredp-mark-directories-recursive]
-  '(menu-item "Directories" diredp-mark-directories-recursive
-    :help "Mark all directories, including those in marked subdirs"))
-(define-key diredp-marks-recursive-menu [diredp-mark-executables-recursive]
-  '(menu-item "Executables" diredp-mark-executables-recursive
-    :help "Mark all executable files, including those in marked subdirs"))
-
-
-;; "Dir" menu.
-;;
-;; REPLACE ORIGINAL `Subdir' menu in `dired.el'.
-;;
-(defvar diredp-menu-bar-dir-menu (make-sparse-keymap "Dir"))
-(define-key dired-mode-map [menu-bar subdir] (cons "Dir" diredp-menu-bar-dir-menu))
-
-;; We don't use `define-obsolete-variable-alias' so that byte-compilation in older Emacs
-;; works for newer Emacs too.
-(when (fboundp 'defvaralias)            ; Emacs 22+
-  (defvaralias 'diredp-menu-bar-subdir-menu 'diredp-dir-menu))
-(make-obsolete-variable 'diredp-menu-bar-subdir-menu 'diredp-dir-menu) ; 2017-04-09
-
-(when (boundp 'diredp-menu-bar-subdir-menu)
-  (defalias 'diredp-menu-bar-subdir-menu diredp-menu-bar-subdir-menu))
-(make-obsolete 'diredp-menu-bar-subdir-menu 'diredp-dir-menu) ; 2017-04-09
-
-
-;; `Dir' > `Hide/Show' menu.
-;;
-(defvar diredp-hide/show-menu (make-sparse-keymap "Hide/Show")
-  "`Hide/Show' submenu for Dired menu-bar `Dir' menu.")
-(define-key diredp-menu-bar-dir-menu [hide-show] (cons "Hide/Show" diredp-hide/show-menu))
-
-(when (fboundp 'dired-omit-mode)
-  (define-key diredp-hide/show-menu [dired-omit-mode]
-    '(menu-item "Hide/Show Uninteresting (Omit Mode)" dired-omit-mode
-      :help "Toggle omission of uninteresting files (Omit mode)")))
-(when (fboundp 'dired-hide-details-mode) ; Emacs 24.4+
-  (define-key diredp-hide/show-menu [hide-details]
-    '(menu-item "Hide/Show Details" dired-hide-details-mode
-      :help "Hide or show less important fields of directory listing")))
-(define-key diredp-hide/show-menu [hide-all]
-  '(menu-item "Hide/Show All Subdirs" dired-hide-all
-    :help "Hide all subdirectories, leave only header lines"))
-(define-key diredp-hide/show-menu [hide-subdir]
-  '(menu-item "Hide/Show Subdir" diredp-hide-subdir-nomove
-    :help "Hide or unhide current directory listing"))
-
-
-;; `Dir' > `Bookmark' menu.
-;;
-(defvar diredp-bookmark-menu (make-sparse-keymap "Bookmark")
-  "`Bookmark' submenu for Dired menu-bar `Dir' menu.")
-(define-key diredp-menu-bar-dir-menu [bookmark] (cons "Bookmark" diredp-bookmark-menu))
-
-(define-key diredp-bookmark-menu [diredp-highlight-autofiles-mode]
-  '(menu-item "Toggle Autofile Highlighting" diredp-highlight-autofiles-mode
-    :help "Toggle whether to highlight autofile bookmarks"
-    :visible (and (featurep 'bookmark+)  (featurep 'highlight))))
-(define-key diredp-bookmark-menu [diredp-do-bookmark-dirs-recursive]
-    '(menu-item "Bookmark Dirs Here and Below..." diredp-do-bookmark-dirs-recursive
-      :help "Bookmark this Dired buffer and marked subdirectory Dired buffers, recursively."))
-(define-key diredp-bookmark-menu [bookmark-dired]
-  '(menu-item "Bookmark Dired Buffer..." bookmark-set :help "Bookmark this Dired buffer"))
-
-
-;; `Dir' > `Navigate' menu.
-;;
-(defvar diredp-navigate-menu (make-sparse-keymap "Navigate")
-  "`Navigate' submenu for Dired menu-bar `Dir' menu.")
-(define-key diredp-menu-bar-dir-menu [navigate] (cons "Navigate" diredp-navigate-menu))
-
-(define-key diredp-navigate-menu [insert]
-  '(menu-item "Move To This Subdir" dired-maybe-insert-subdir
-    :help "Move to subdirectory line or listing"))
-(define-key diredp-navigate-menu [tree-down]
-  '(menu-item "Tree Down" dired-tree-down :help "Go to first subdirectory header down the tree"))
-(define-key diredp-navigate-menu [tree-up]
-  '(menu-item "Tree Up" dired-tree-up :help "Go to first subdirectory header up the tree"))
-(define-key diredp-navigate-menu [up]
-  '(menu-item "Up Directory" diredp-up-directory :help "Dired the parent directory"))
-(define-key diredp-navigate-menu [prev-subdir]
-  '(menu-item "Prev Subdir" diredp-prev-subdir :help "Go to previous subdirectory header line"))
-(define-key diredp-navigate-menu [next-subdir]
-  '(menu-item "Next Subdir" diredp-next-subdir :help "Go to next subdirectory header line"))
-(define-key diredp-navigate-menu [prev-dirline]
-  '(menu-item "Prev Dirline" diredp-prev-dirline :help "Move to previous directory-file line"))
-(define-key diredp-navigate-menu [next-dirline]
-  '(menu-item "Next Dirline" diredp-next-dirline :help "Move to next directory-file line"))
-
-(define-key diredp-menu-bar-dir-menu [separator-subdir] '("--")) ; --------------------------
-
-(define-key diredp-menu-bar-dir-menu [image-dired-dired-toggle-marked-thumbs]
-  '(menu-item "Toggle Image Thumbnails" image-dired-dired-toggle-marked-thumbs
-    :enable (fboundp 'image-dired-dired-toggle-marked-thumbs)
-    :help "Add or remove image thumbnails in front of marked file names"))
-(when (fboundp 'dired-isearch-filenames) ; Emacs 23+
-  (define-key diredp-menu-bar-dir-menu [isearch-filenames-regexp]
-    '(menu-item "Isearch Regexp in File Names..." dired-isearch-filenames-regexp
-      :help "Incrementally search for regexp in file names only"))
-  (define-key diredp-menu-bar-dir-menu [isearch-filenames]
-    '(menu-item "Isearch in File Names..." dired-isearch-filenames
-      :help "Incrementally search for literal text in file names only.")))
-(when (or (> emacs-major-version 21)  (fboundp 'wdired-change-to-wdired-mode))
-  (define-key diredp-menu-bar-dir-menu [wdired-mode]
-    '(menu-item "Edit File Names (WDired)" wdired-change-to-wdired-mode
-      :help "Put a Dired buffer in a mode in which filenames are editable"
-      :keys "C-x C-q" :filter (lambda (x) (and (derived-mode-p 'dired-mode)  x)))))
-(define-key diredp-menu-bar-dir-menu [diredp-yank-files]
-  '(menu-item "Paste Files from Copied Absolute Names" diredp-yank-files
-    :help "Paste files here whose absolute names you copied"
-    :enable (catch 'dir-menu--yank-files
-              (let ((files  (car kill-ring-yank-pointer)))
-                (and (stringp files)
-                     (dolist (file  (split-string files))
-                       (unless (file-name-absolute-p file) (throw 'dir-menu--yank-files nil)))))
-              t)))
-(when (fboundp 'dired-compare-directories) ; Emacs 22+
-  (define-key diredp-menu-bar-dir-menu [compare-directories]
-    '(menu-item "Compare Directories..." dired-compare-directories
-      :help "Mark files with different attributes in two Dired buffers")))
-
-(define-key diredp-menu-bar-dir-menu [separator-dired-on-set] '("--")) ; --------------------
-
-(define-key diredp-menu-bar-dir-menu [diredp-dired-recent-dirs]
-  '(menu-item "Dired Recent Directories..." diredp-dired-recent-dirs
-    :visible (boundp 'recentf-list) :enable  (and (boundp 'recentf-list)  (consp recentf-list))
-    :help "Open a Dired buffer for recently used directories"))
-(define-key diredp-menu-bar-dir-menu [diredp-dired-inserted-subdirs]
-  '(menu-item "Dired Each Inserted Subdir..." diredp-dired-inserted-subdirs
-    :enable (cdr dired-subdir-alist)    ; First elt is current dir.  Must have at least one more.
-    :help "Open Dired separately for each of the inserted subdirectories"))
-(define-key diredp-menu-bar-dir-menu [diredp-add-to-this-dired-buffer]
-  '(menu-item "Add Entries Here..." diredp-add-to-this-dired-buffer
-    :help "Add individual file and directory names to the listing"
-    :keys "C-x E"))
-(define-key diredp-menu-bar-dir-menu [diredp-dired-union]
-  '(menu-item "Dired Union..." diredp-dired-union
-    :help "Open Dired for the union of some existing Dired buffers"))
-(define-key diredp-menu-bar-dir-menu [diredp-fileset-other-window]
-  '(menu-item "Dired Fileset..." diredp-fileset-other-window
-    :enable (> emacs-major-version 21) :help "Open Dired on an Emacs fileset"))
-(define-key diredp-menu-bar-dir-menu [diredp-dired-for-files]
-  '(menu-item "Dired Files Located Anywhere" diredp-dired-for-files
-    :help "Open Dired on specific files whose names you provide"))
-(define-key diredp-menu-bar-dir-menu [diredp-marked-other-window]
-  '(menu-item "Dired Marked Files in Other Window" diredp-marked-other-window
-    :enable (save-excursion (goto-char (point-min))
-                            (and (re-search-forward (dired-marker-regexp) nil t)
-                                 (re-search-forward (dired-marker-regexp) nil t)))
-    :help "Open Dired on marked files only, in other window"))
-(define-key diredp-menu-bar-dir-menu [diredp-marked]
-  '(menu-item "Dired Marked Files" diredp-marked
-    :enable (save-excursion (goto-char (point-min))
-                            (and (re-search-forward (dired-marker-regexp) nil t)
-                                 (re-search-forward (dired-marker-regexp) nil t)))
-    :help "Open Dired on marked files only"))
-(define-key diredp-menu-bar-dir-menu [dired]
-  '(menu-item "Dired (Filter via Wildcards)..." dired
-    :help "Explore a directory (you can provide wildcards)"))
-
-(define-key diredp-menu-bar-dir-menu [separator-dired] '("--")) ; ---------------------
-
-(define-key diredp-menu-bar-dir-menu [insert]
-  '(menu-item "Insert/Move-To This Subdir" dired-maybe-insert-subdir
-    :help "Move to subdirectory line or listing"))
-(define-key diredp-menu-bar-dir-menu [revert]
-  '(menu-item "Refresh (Sync \& Show All)" revert-buffer :help "Update directory contents"))
-(define-key diredp-menu-bar-dir-menu [create-directory] ; Moved from "Immediate".
-  '(menu-item "New Directory..." dired-create-directory :help "Create a directory"))
-
-
-;;; Mouse-3 menu binding.
-(define-key dired-mode-map [down-mouse-3] 'diredp-mouse-3-menu)
-(define-key dired-mode-map [mouse-3]      'ignore)
-
-
-;;; Non-menu Dired bindings.
-
-;; Move `dired-omit-mode' to `C-x M-o', so prefix key `M-o' is free for face/font-lock stuff.
-(define-key dired-mode-map "\C-x\M-o" (if (fboundp 'dired-omit-mode) 'dired-omit-mode 'dired-omit-toggle))
-(when (memq (lookup-key dired-mode-map "\M-o") '(dired-omit-mode dired-omit-toggle))
-  (define-key dired-mode-map "\M-o" nil))
-
-;; These are global, not just Dired mode.  They are on prefix key `C-x D'.
-(unless (lookup-key ctl-x-map "D")
-  (define-key ctl-x-map   "D" nil)      ; For Emacs 20
-  (define-key ctl-x-map   "DA" 'diredp-add-to-dired-buffer)                ; `C-x D A'
-  (define-key ctl-x-map   "DF" 'diredp-dired-for-files)                    ; `C-x D F'
-  (define-key ctl-x-map   "DR" 'diredp-dired-recent-dirs)                  ; `C-x D R'
-  (define-key ctl-x-map   "DS" 'diredp-fileset)                            ; `C-x D S'
-  (define-key ctl-x-map   "DU" 'diredp-dired-union))                       ; `C-x D U'
-
-(unless (lookup-key ctl-x-4-map "D")
-  (define-key ctl-x-4-map "D" nil)      ; For Emacs 20
-  (define-key ctl-x-4-map "DA" 'diredp-add-to-dired-buffer-other-window)   ; `C-x 4 D A'
-  (define-key ctl-x-4-map "DF" 'diredp-dired-for-files-other-window)       ; `C-x 4 D F'
-  (define-key ctl-x-4-map "DR" 'diredp-dired-recent-dirs-other-window)     ; `C-x 4 D R'
-  (define-key ctl-x-4-map "DS" 'diredp-fileset-other-window)               ; `C-x 4 D S'
-  (define-key ctl-x-4-map "DU" 'diredp-dired-union-other-window))          ; `C-x 4 D U'
-
-;; Navigation
-(substitute-key-definition 'dired-up-directory 'diredp-up-directory dired-mode-map)
-(substitute-key-definition 'dired-next-line 'diredp-next-line dired-mode-map)
-(substitute-key-definition 'dired-previous-line 'diredp-previous-line dired-mode-map)
-(substitute-key-definition 'dired-next-dirline 'diredp-next-dirline dired-mode-map)
-(substitute-key-definition 'dired-prev-dirline 'diredp-prev-dirline dired-mode-map)
-(substitute-key-definition 'dired-next-subdir 'diredp-next-subdir dired-mode-map)
-(substitute-key-definition 'dired-prev-subdir 'diredp-prev-subdir dired-mode-map)
-
-
-(define-key dired-mode-map [S-down-mouse-1] 'ignore) ; (normally `mouse-set-font')
-;; `diredp-mouse-mark-region-files' provides Windows-Explorer behavior
-;; for selecting (marking) files.
-(define-key dired-mode-map [S-mouse-1] 'diredp-mouse-mark-region-files)     ; `S-mouse-1'
-(define-key dired-mode-map [mouse-2] 'dired-mouse-find-file-other-window)   ; `mouse-2'
-;; But be aware that `dired-sort-menu.el' binds `S-mouse-2' to `dired-sort-menu-popup'.
-(define-key dired-mode-map [S-down-mouse-2] 'dired-mouse-find-file)         ; `S-mouse-2'
-(define-key dired-mode-map [S-mouse-2] 'ignore)
-(define-key dired-mode-map [M-mouse-2] 'diredp-mouse-find-file-other-frame) ; `M-mouse-2'
-
-;; On Windows, bind more.
-(eval-after-load "w32-browser"
-  '(progn
-    (define-key dired-mode-map [(control return)] 'dired-w32explore)                ; `C-RET'
-    (define-key dired-mode-map [(meta return)] 'dired-w32-browser)                  ; `M-RET'
-    (define-key dired-mode-map [mouse-2] 'dired-mouse-w32-browser)                  ; `mouse-2'
-    (define-key dired-mode-map (kbd "<C-M-return>") 'dired-multiple-w32-browser)))  ; `C-M-RET'
-
-(when (fboundp 'diredp-w32-drives)
-  (when (< emacs-major-version 21) (define-key dired-mode-map ":"    nil)) ; For Emacs 20
-  (define-key dired-mode-map ":/"    'diredp-w32-drives))                            ; `:/'
-
-;; Other keyboard keys
-(define-key dired-mode-map "@"       'diredp-do-apply-function)                     ; `@'
-(define-key dired-mode-map "\$"      'diredp-hide-subdir-nomove)                    ; `$'
-(define-key dired-mode-map "\M-$"    'dired-hide-subdir)                            ; `M-$'
-(define-key dired-mode-map "="       'diredp-ediff)                                 ; `='
-;; This replaces the `dired-x.el' binding of `dired-mark-extension'.
-(define-key dired-mode-map "*."      'diredp-mark/unmark-extension)                 ; `* .'
-(define-key dired-mode-map "*B"      'diredp-mark-autofiles)                        ; `* B'
-(define-key dired-mode-map [(control meta ?*)] 'diredp-marked-other-window)         ; `C-M-*'
-(define-key dired-mode-map "\M-a"    'dired-do-search)                              ; `M-a'
-(define-key dired-mode-map "\M-b"    'diredp-do-bookmark)                           ; `M-b'
-(define-key dired-mode-map "\C-\M-b" 'diredp-set-bookmark-file-bookmark-for-marked) ; `C-M-b'
-(when diredp-bind-problematic-terminal-keys
-  (define-key dired-mode-map [(control meta shift ?b)]                              ; `C-M-B' (aka `C-M-S-b')
-    'diredp-do-bookmark-in-bookmark-file))
-(define-key dired-mode-map "e"       'diredp-visit-this-file)                       ; `e' (was `dired-find-file')
-(define-key dired-mode-map [C-down]  'diredp-visit-next-file)                 ; `C-down' (was `forward-paragraph')
-(define-key dired-mode-map [C-up]    'diredp-visit-previous-file)             ; `C-up' (was `backward-paragraph')
-(define-key dired-mode-map "\C-\M-G" 'diredp-do-grep)                               ; `C-M-G'
-(when (fboundp 'mkhtml-dired-files)     ; In `mkhtml.el'.
-  (define-key dired-mode-map "\M-h"  'mkhtml-dired-files))                          ; `M-h'
-(define-key dired-mode-map "\C-\M-i" 'diredp-dired-inserted-subdirs)                ; `C-M-i'
-(define-key dired-mode-map "\M-q"    (if (< emacs-major-version 21)
-                                         'dired-do-query-replace
-                                       'dired-do-query-replace-regexp))             ; `M-q'
-(when diredp-bind-problematic-terminal-keys
-  (define-key dired-mode-map [(control meta shift ?r)]                              ; `C-M-R' (aka `C-M-S-r')
-    'diredp-toggle-find-file-reuse-dir))
-(define-key dired-mode-map "U"       'dired-unmark-all-marks)                       ; `U'
-(substitute-key-definition 'describe-mode 'diredp-describe-mode                     ; `h', `C-h m'
-                           dired-mode-map (current-global-map))
-
-;; Tags - same keys as in `*Bookmark List*'.
-;;
-;; NOTE: If this changes then need to update `dired-sort-menu+.el' to reflect the changes.
-;;
-(define-key dired-mode-map "T"       nil) ; For Emacs 20
-(define-key dired-mode-map "T+"      'diredp-tag-this-file)                ; `T +'
-(define-key dired-mode-map "T-"      'diredp-untag-this-file)              ; `T -'
-(define-key dired-mode-map "T0"      'diredp-remove-all-tags-this-file)    ; `T 0'
-(define-key dired-mode-map "Tc"      'diredp-copy-tags-this-file)          ; `T c'
-(define-key dired-mode-map "Tp"      'diredp-paste-add-tags-this-file)     ; `T p'
-(define-key dired-mode-map "Tq"      'diredp-paste-replace-tags-this-file) ; `T q'
-(define-key dired-mode-map "Tv"      'diredp-set-tag-value-this-file)      ; `T v'
-(define-key dired-mode-map "T\M-w"   'diredp-copy-tags-this-file)          ; `T M-w'
-(define-key dired-mode-map "T\C-y"   'diredp-paste-add-tags-this-file)     ; `T C-y'
-(define-key dired-mode-map "T>+"     'diredp-do-tag)                       ; `T > +'
-(define-key dired-mode-map "T>-"     'diredp-do-untag)                     ; `T > -'
-(define-key dired-mode-map "T>0"     'diredp-do-remove-all-tags)           ; `T > 0'
-(define-key dired-mode-map "T>p"     'diredp-do-paste-add-tags)            ; `T > p'
-(define-key dired-mode-map "T>q"     'diredp-do-paste-replace-tags)        ; `T > q'
-(define-key dired-mode-map "T>v"     'diredp-do-set-tag-value)             ; `T > v'
-(define-key dired-mode-map "T>\C-y"  'diredp-do-paste-add-tags)            ; `T > C-y'
-(define-key dired-mode-map "Tm%"     'diredp-mark-files-tagged-regexp)     ; `T m %'
-(define-key dired-mode-map "Tm*"     'diredp-mark-files-tagged-all)        ; `T m *'
-(define-key dired-mode-map "Tm+"     'diredp-mark-files-tagged-some)       ; `T m +'
-(define-key dired-mode-map "Tm~*"    'diredp-mark-files-tagged-not-all)    ; `T m ~ *'
-(define-key dired-mode-map "Tm~+"    'diredp-mark-files-tagged-none)       ; `T m ~ +'
-(define-key dired-mode-map "Tu%"     'diredp-unmark-files-tagged-regexp)   ; `T u %'
-(define-key dired-mode-map "Tu*"     'diredp-unmark-files-tagged-all)      ; `T u *'
-(define-key dired-mode-map "Tu+"     'diredp-unmark-files-tagged-some)     ; `T u +'
-(define-key dired-mode-map "Tu~*"    'diredp-unmark-files-tagged-not-all)  ; `T u ~ *'
-(define-key dired-mode-map "Tu~+"    'diredp-unmark-files-tagged-none)     ; `T u ~ +'
-;; $$$$$$ (define-key dired-mode-map [(control ?+)] 'diredp-do-tag)
-;; $$$$$$ (define-key dired-mode-map [(control ?-)] 'diredp-do-untag)
-
-
-;; Vanilla Emacs binds `c' to `dired-do-compress-to'.  Use `M-z' instead'.
-;; (`dired-sort-menu.el' binds `c' to `dired-sort-menu-toggle-ignore-case'.)
-;;
-(when (fboundp 'dired-do-compress-to) ; Emacs 25+
-  (define-key dired-mode-map (kbd "M-z") 'dired-do-compress-to))
-
-
-;; Commands for operating on the current line's file.  When possible,
-;; these are lower-case versions of the upper-case commands for operating on
-;; the marked files.  (Most of the other corresponding lower-case letters are already
-;; defined and cannot be used here.)
-
-;; $$$$$$ (define-key dired-mode-map [(control meta ?+)] 'diredp-tag-this-file)
-;; $$$$$$ (define-key dired-mode-map [(control meta ?-)] 'diredp-untag-this-file)
-(define-key dired-mode-map "\r"      'dired-find-file)                      ; `RET'
-(when (fboundp 'diredp-describe-file)
-  (define-key dired-mode-map (kbd "C-h RET")        'diredp-describe-file)  ; `C-h RET'
-  (define-key dired-mode-map (kbd "C-h C-<return>") 'diredp-describe-file)) ; `C-h C-RET'
-(define-key dired-mode-map "%c"      'diredp-capitalize)                    ; `% c'
-(define-key dired-mode-map "b"       'diredp-byte-compile-this-file)        ; `b'
-(define-key dired-mode-map [(control shift ?b)] 'diredp-bookmark-this-file) ; `C-B'
-(define-key dired-mode-map "\M-c"    'diredp-capitalize-this-file)          ; `M-c'
-(when (and (fboundp 'diredp-chgrp-this-file)  diredp-bind-problematic-terminal-keys)
-  (define-key dired-mode-map [(control meta shift ?g)] 'diredp-chgrp-this-file)) ; `C-M-G' (aka `C-M-S-g')
-(define-key dired-mode-map "\M-i"    'diredp-insert-subdirs)                ; `M-i'
-(define-key dired-mode-map "\M-l"    'diredp-downcase-this-file)            ; `M-l'
-(define-key dired-mode-map "\C-\M-l" 'diredp-list-marked)                   ; `C-M-l'
-(when diredp-bind-problematic-terminal-keys
-  (define-key dired-mode-map [(meta shift ?m)] 'diredp-chmod-this-file))    ; `M-M' (aka `M-S-m')
-(define-key dired-mode-map "\C-o"    'diredp-find-file-other-frame)         ; `C-o'
-(when (and (fboundp 'diredp-chown-this-file)  diredp-bind-problematic-terminal-keys)
-  (define-key dired-mode-map [(meta shift ?o)] 'diredp-chown-this-file))    ; `M-O' (aka `M-S-o')
-(define-key dired-mode-map "\C-\M-o" 'dired-display-file)                   ; `C-M-o' (not `C-o')
-(define-key dired-mode-map "\M-p"    'diredp-print-this-file)               ; `M-p'
-(define-key dired-mode-map "r"       'diredp-rename-this-file)              ; `r'
-(when (fboundp 'image-dired-dired-display-image)
-  (define-key dired-mode-map "\C-tI"   'diredp-image-show-this-file))       ; `C-t I'
-(when diredp-bind-problematic-terminal-keys
-  (define-key dired-mode-map [(meta shift ?t)] 'diredp-touch-this-file)     ; `M-T' (aka `M-S-t')
-  (define-key dired-mode-map [(control meta shift ?t)] 'dired-do-touch))    ; `C-M-T' (aka `C-M-S-t')
-(define-key dired-mode-map "\M-u"    'diredp-upcase-this-file)              ; `M-u'
-(define-key dired-mode-map "y"       'diredp-relsymlink-this-file)          ; `y'
-(define-key dired-mode-map "\C-w"    'diredp-move-files-named-in-kill-ring) ; `C-w'
-(define-key dired-mode-map "\C-y"    'diredp-yank-files)                    ; `C-y'
-(define-key dired-mode-map "z"       'diredp-compress-this-file)            ; `z'
-(when (fboundp 'dired-show-file-type)
-  (define-key dired-mode-map "_"      'dired-show-file-type))               ; `_' (underscore)
-(substitute-key-definition 'kill-line 'diredp-delete-this-file              ; `C-k', `delete', `deleteline'
-                           dired-mode-map (current-global-map))
-
-
-;; Commands that handle marked below, recursively.
-;; Use `M-+' as a prefix key for all such commands.
-
-(define-prefix-command 'diredp-recursive-map)
-(define-key dired-mode-map "\M-+"  diredp-recursive-map) ; `M-+'
-
-(when (fboundp 'char-displayable-p)     ; Emacs 22+
-  (define-key diredp-recursive-map "\M-\C-?"   'diredp-unmark-all-files-recursive))     ; `M-DEL'
-(define-key diredp-recursive-map "@"           'diredp-do-apply-function-recursive)     ; `@'
-(define-key diredp-recursive-map "#"           'diredp-flag-auto-save-files-recursive)  ; `#'
-(define-key diredp-recursive-map "*@"          'diredp-mark-symlinks-recursive)         ; `* @'
-(define-key diredp-recursive-map "**"          'diredp-mark-executables-recursive)      ; `* *'
-(define-key diredp-recursive-map "*/"          'diredp-mark-directories-recursive)      ; `* /'
-(define-key diredp-recursive-map "*."          'diredp-mark-extension-recursive)        ; `* .'
-(define-key diredp-recursive-map "*("          'diredp-mark-sexp-recursive)             ; `* ('
-(define-key diredp-recursive-map "*B"          'diredp-mark-autofiles-recursive)        ; `* B'
-(when (fboundp 'char-displayable-p)     ; Emacs 22+
-  (define-key diredp-recursive-map "*c"        'diredp-change-marks-recursive))         ; `* c'
-(define-key diredp-recursive-map "*%"          'diredp-mark-files-regexp-recursive)     ; `* %'
-(when (> emacs-major-version 22)
-  (define-key diredp-recursive-map ":d"        'diredp-do-decrypt-recursive)            ; `: d'
-  (define-key diredp-recursive-map ":e"        'diredp-do-encrypt-recursive)            ; `: e'
-  (define-key diredp-recursive-map ":s"        'diredp-do-sign-recursive)               ; `: s'
-  (define-key diredp-recursive-map ":v"        'diredp-do-verify-recursive))            ; `: v'
-(define-key diredp-recursive-map "%c"          'diredp-capitalize-recursive)            ; `% c'
-(define-key diredp-recursive-map "%g"          'diredp-mark-files-containing-regexp-recursive) ; `% g'
-(define-key diredp-recursive-map "%l"          'diredp-downcase-recursive)              ; `% l'
-(define-key diredp-recursive-map "%m"          'diredp-mark-files-regexp-recursive)     ; `% m'
-(define-key diredp-recursive-map "%u"          'diredp-upcase-recursive)                ; `% u'
-(when (fboundp 'dired-do-async-shell-command) ; Emacs 23+
-  (define-key diredp-recursive-map "&"         'diredp-do-async-shell-command-recursive)) ; `&'
-(define-key diredp-recursive-map "!"           'diredp-do-shell-command-recursive)      ; `!'
-(define-key diredp-recursive-map (kbd "C-M-*") 'diredp-marked-recursive-other-window)   ; `C-M-*'
-(define-key diredp-recursive-map "A"           'diredp-do-search-recursive)             ; `A'
-(define-key diredp-recursive-map "\M-b"        'diredp-do-bookmark-recursive)           ; `M-b'
-(when diredp-bind-problematic-terminal-keys
-  (define-key diredp-recursive-map [(meta shift ?b)]                                    ; `M-B' (aka `M-S-b')
-    'diredp-do-bookmark-dirs-recursive))
-(define-key diredp-recursive-map (kbd "C-M-b")                                          ; `C-M-b'
-  'diredp-set-bookmark-file-bookmark-for-marked-recursive)
-(when diredp-bind-problematic-terminal-keys
-  (define-key diredp-recursive-map [(control meta shift ?b)]                            ; `C-M-B' (aka `C-M-S-b')
-    'diredp-do-bookmark-in-bookmark-file-recursive))
-(define-key diredp-recursive-map "C"           'diredp-do-copy-recursive)               ; `C'
-(define-key diredp-recursive-map "D"           'diredp-do-delete-recursive)             ; `D'
-(define-key diredp-recursive-map "F"           'diredp-do-find-marked-files-recursive)  ; `F'
-(when (fboundp 'diredp-do-chgrp-recursive)
-  (define-key diredp-recursive-map "G"         'diredp-do-chgrp-recursive))             ; `G'
-(define-key diredp-recursive-map "\C-\M-G"     'diredp-do-grep-recursive)               ; `C-M-G'
-(define-key diredp-recursive-map "H"           'diredp-do-hardlink-recursive)           ; `H'
-(define-key diredp-recursive-map "\M-i"        'diredp-insert-subdirs-recursive)        ; `M-i'
-(define-key diredp-recursive-map "\C-\M-l"     'diredp-list-marked-recursive)           ; `C-M-l'
-(define-key diredp-recursive-map "M"           'diredp-do-chmod-recursive)              ; `M'
-(when (fboundp 'diredp-do-chown-recursive)
-  (define-key diredp-recursive-map "O"         'diredp-do-chown-recursive))             ; `O'
-(define-key diredp-recursive-map "P"           'diredp-do-print-recursive)              ; `P'
-(define-key diredp-recursive-map "Q"         'diredp-do-query-replace-regexp-recursive) ; `Q'
-(define-key diredp-recursive-map "R"           'diredp-do-move-recursive)               ; `R'
-(define-key diredp-recursive-map "S"           'diredp-do-symlink-recursive)            ; `S'
-(define-key diredp-recursive-map (kbd "M-s a C-s")                                      ; `M-s a C-s'
-  'diredp-do-isearch-recursive)
-(define-key diredp-recursive-map (kbd "M-s a C-M-s")                                    ; `M-s a C-M-s'
-  'diredp-do-isearch-regexp-recursive)
-(when diredp-bind-problematic-terminal-keys
-  (define-key diredp-recursive-map [(control meta shift ?t)]
-    'diredp-do-touch-recursive))                                                        ; `C-M-T' (aka `C-M-S-t')
-(define-key diredp-recursive-map "\C-tc"   'diredp-image-dired-comment-files-recursive) ; `C-t c'
-(define-key diredp-recursive-map "\C-td"  'diredp-image-dired-display-thumbs-recursive) ; `C-t d'
-(define-key diredp-recursive-map "\C-tr"      'diredp-image-dired-delete-tag-recursive) ; `C-t r'
-(define-key diredp-recursive-map "\C-tt"       'diredp-image-dired-tag-files-recursive) ; `C-t t'
-(when (fboundp 'char-displayable-p)     ; Emacs 22+
-  (define-key diredp-recursive-map "U"         'diredp-unmark-all-marks-recursive))     ; `U'
-(define-key diredp-recursive-map "\M-("        'diredp-mark-sexp-recursive)             ; `M-('
-(define-key diredp-recursive-map "\M-w"        'diredp-copy-filename-as-kill-recursive) ; `M-w'
-(define-key diredp-recursive-map "Y"           'diredp-do-relsymlink-recursive)         ; `Y'
-
-(eval-after-load "w32-browser"
-  '(define-key diredp-recursive-map (kbd "<C-M-return>") 'diredp-multiple-w32-browser-recursive)) ; `C-M-RET'
-
-;; Undefine some bindings that would try to modify a Dired buffer.  Their key sequences will
-;; then appear to the user as available for local (Dired) definition.
-(when (fboundp 'undefine-killer-commands) (undefine-killer-commands dired-mode-map))
-
-;;;;;;;;;;;;
-
-(setq diredp-loaded-p  t)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; dired+.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/vendor/reason-indent.el b/users/wpcarro/emacs/.emacs.d/vendor/reason-indent.el
deleted file mode 100644
index 8fd3c94258..0000000000
--- a/users/wpcarro/emacs/.emacs.d/vendor/reason-indent.el
+++ /dev/null
@@ -1,304 +0,0 @@
-;;; reason-indent.el --- Indentation functions for ReasonML -*-lexical-binding: t-*-
-
-;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
-
-;;; Commentary:
-
-;; Indentation functions for Reason.
-
-;;; Code:
-
-(defconst reason-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
-
-(defcustom reason-indent-offset 2
-  "Indent Reason code by this number of spaces."
-  :type 'integer
-  :group 'reason-mode
-  :safe #'integerp)
-
-(defun reason-looking-back-str (str)
-  "Like `looking-back' but for fixed strings rather than regexps.
-Works around some regexp slowness.
-Argument STR string to search for."
-  (let ((len (length str)))
-    (and (> (point) len)
-         (equal str (buffer-substring-no-properties (- (point) len) (point))))))
-
-(defun reason-paren-level ()
-  "Get the level of nesting inside parentheses."
-  (nth 0 (syntax-ppss)))
-
-(defun reason-in-str-or-cmnt ()
-  "Return whether point is currently inside a string or a comment."
-  (nth 8 (syntax-ppss)))
-
-(defun reason-rewind-past-str-cmnt ()
-  "Rewind past string or comment."
-  (goto-char (nth 8 (syntax-ppss))))
-
-(defun reason-rewind-irrelevant ()
-  "Rewind past irrelevant characters (whitespace of inside comments)."
-  (interactive)
-  (let ((starting (point)))
-    (skip-chars-backward "[:space:]\n")
-    (if (reason-looking-back-str "*/") (backward-char))
-    (if (reason-in-str-or-cmnt)
-        (reason-rewind-past-str-cmnt))
-    (if (/= starting (point))
-        (reason-rewind-irrelevant))))
-
-(defun reason-align-to-expr-after-brace ()
-  "Align the expression at point to the expression after the previous brace."
-  (save-excursion
-    (forward-char)
-    ;; We don't want to indent out to the open bracket if the
-    ;; open bracket ends the line
-    (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
-      (when (looking-at "[[:space:]]")
-        (forward-word 1)
-        (backward-word 1))
-      (current-column))))
-
-(defun reason-align-to-prev-expr ()
-  "Align the expression at point to the previous expression."
-  (let ((alignment (save-excursion
-                     (forward-char)
-                     ;; We don't want to indent out to the open bracket if the
-                     ;; open bracket ends the line
-                     (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
-                       (if (looking-at "[[:space:]]")
-                           (progn
-                             (forward-word 1)
-                             (backward-word 1))
-                         (backward-char))
-                       (current-column)))))
-    (if (not alignment)
-        (save-excursion
-          (forward-char)
-          (forward-line)
-          (back-to-indentation)
-          (current-column))
-      alignment)))
-
-;;; Start of a reason binding
-(defvar reason-binding
-  (regexp-opt '("let" "type" "module" "fun")))
-
-(defun reason-beginning-of-defun (&optional arg)
-  "Move backward to the beginning of the current defun.
-
-With ARG, move backward multiple defuns.  Negative ARG means
-move forward.
-
-This is written mainly to be used as `beginning-of-defun-function'.
-Don't move to the beginning of the line.  `beginning-of-defun',
-which calls this, does that afterwards."
-  (interactive "p")
-  (re-search-backward (concat "^\\(" reason-binding "\\)\\_>")
-                      nil 'move (or arg 1)))
-
-(defun reason-end-of-defun ()
-  "Move forward to the next end of defun.
-
-With argument, do it that many times.
-Negative argument -N means move back to Nth preceding end of defun.
-
-Assume that this is called after β€˜beginning-of-defun’.  So point is
-at the beginning of the defun body.
-
-This is written mainly to be used as `end-of-defun-function' for Reason."
-  (interactive)
-  ;; Find the opening brace
-  (if (re-search-forward "[{]" nil t)
-      (progn
-        (goto-char (match-beginning 0))
-        ;; Go to the closing brace
-        (condition-case nil
-            (forward-sexp)
-          (scan-error
-           ;; The parentheses are unbalanced; instead of being unable to fontify, just jump to the end of the buffer
-           (goto-char (point-max)))))
-    ;; There is no opening brace, so consider the whole buffer to be one "defun"
-    (goto-char (point-max))))
-
-(defun reason-rewind-to-beginning-of-current-level-expr ()
-  "Rewind to the beginning of the expression on the current level of nesting."
-  (interactive)
-  (let ((current-level (reason-paren-level)))
-    (back-to-indentation)
-    (when (looking-at "=>")
-      (reason-rewind-irrelevant)
-      (back-to-indentation))
-    (while (> (reason-paren-level) current-level)
-      (backward-up-list)
-      (back-to-indentation))))
-
-(defun reason-mode-indent-line ()
-  "Indent current line."
-  (interactive)
-  (let ((indent
-         (save-excursion
-           (back-to-indentation)
-           ;; Point is now at beginning of current line
-           (let* ((level (reason-paren-level))
-                  (baseline
-                   ;; Our "baseline" is one level out from the indentation of the expression
-                   ;; containing the innermost enclosing opening bracket. That
-                   ;; way if we are within a block that has a different
-                   ;; indentation than this mode would give it, we still indent
-                   ;; the inside of it correctly relative to the outside.
-                   (if (= 0 level)
-                       0
-                     (save-excursion
-                       (reason-rewind-irrelevant)
-                       (if (save-excursion
-                             (reason-rewind-to-beginning-of-current-level-expr)
-                             (looking-at "<"))
-                           (progn
-                             (reason-rewind-to-beginning-of-current-level-expr)
-                             (current-column))
-                           (progn
-                             (backward-up-list)
-                             (reason-rewind-to-beginning-of-current-level-expr)
-
-                             (cond
-                              ((looking-at "switch")
-                               (current-column))
-
-                              ((looking-at "|")
-                               (+ (current-column) (* reason-indent-offset 2)))
-
-                              (t
-                               (let ((current-level (reason-paren-level)))
-                                 (save-excursion
-                                   (while (and (= current-level (reason-paren-level))
-                                               (not (looking-at reason-binding)))
-                                     (reason-rewind-irrelevant)
-                                     (reason-rewind-to-beginning-of-current-level-expr))
-                                   (+ (current-column) reason-indent-offset)))))))))))
-             (cond
-              ;; A function return type is indented to the corresponding function arguments
-              ((looking-at "=>")
-               (+ baseline reason-indent-offset))
-
-              ((reason-in-str-or-cmnt)
-               (cond
-                ;; In the end of the block -- align with star
-                ((looking-at "*/") (+ baseline 1))
-                ;; Indent to the following shape:
-                ;; /* abcd
-                ;;  * asdf
-                ;;  */
-                ;;
-                ((looking-at "*") (+ baseline 1))
-                ;; Indent to the following shape:
-                ;; /* abcd
-                ;;    asdf
-                ;;  */
-                ;;
-                (t (+ baseline (+ reason-indent-offset 1)))))
-
-              ((looking-at "</") (- baseline reason-indent-offset))
-
-              ;; A closing brace is 1 level unindented
-              ((looking-at "}\\|)\\|\\]")
-               (save-excursion
-                 (reason-rewind-irrelevant)
-                 (let ((jsx? (reason-looking-back-str ">")))
-                   (backward-up-list)
-                   (reason-rewind-to-beginning-of-current-level-expr)
-                   (cond
-                    ((looking-at "switch") baseline)
-
-                    (jsx? (current-column))
-
-                    (t (- baseline reason-indent-offset))))))
-
-              ;; Doc comments in /** style with leading * indent to line up the *s
-              ((and (nth 4 (syntax-ppss)) (looking-at "*"))
-               (+ 1 baseline))
-
-              ;; If we're in any other token-tree / sexp, then:
-              (t
-               (or
-                ;; If we are inside a pair of braces, with something after the
-                ;; open brace on the same line and ending with a comma, treat
-                ;; it as fields and align them.
-                (when (> level 0)
-                  (save-excursion
-                    (reason-rewind-irrelevant)
-                    (backward-up-list)
-                    ;; Point is now at the beginning of the containing set of braces
-                    (reason-align-to-expr-after-brace)))
-
-                (progn
-                  (back-to-indentation)
-                  (cond ((looking-at (regexp-opt '("and" "type")))
-                         baseline)
-                        ((save-excursion
-                           (reason-rewind-irrelevant)
-                           (= (point) 1))
-                         baseline)
-                        ((save-excursion
-                           (while (looking-at "|")
-                             (reason-rewind-irrelevant)
-                             (back-to-indentation))
-                           (looking-at (regexp-opt '("type"))))
-                         (+ baseline reason-indent-offset))
-                        ((looking-at "|\\|/[/*]")
-                         baseline)
-                        ((and (> level 0)
-                              (save-excursion
-                                (reason-rewind-irrelevant)
-                                (backward-up-list)
-                                (reason-rewind-to-beginning-of-current-level-expr)
-                                (looking-at "switch")))
-                         (+ baseline reason-indent-offset))
-                        ((save-excursion
-                           (reason-rewind-irrelevant)
-                           (looking-back "[{;,\\[(]" (- (point) 2)))
-                         baseline)
-                        ((and
-                          (save-excursion
-                            (reason-rewind-irrelevant)
-                            (reason-rewind-to-beginning-of-current-level-expr)
-                            (and (looking-at reason-binding)
-                                 (not (progn
-                                        (forward-sexp)
-                                        (forward-sexp)
-                                        (skip-chars-forward "[:space:]\n")
-                                        (looking-at "=")))))
-                          (not (save-excursion
-                                 (skip-chars-backward "[:space:]\n")
-                                 (reason-looking-back-str "=>"))))
-                         (save-excursion
-                           (reason-rewind-irrelevant)
-                           (backward-sexp)
-                           (reason-align-to-prev-expr)))
-                        ((save-excursion
-                           (reason-rewind-irrelevant)
-                           (looking-back "<\/.*?>" (- (point) 30)))
-                         baseline)
-                        (t
-                         (save-excursion
-                           (reason-rewind-irrelevant)
-                           (reason-rewind-to-beginning-of-current-level-expr)
-
-                           (if (looking-at "|")
-                               baseline
-                             (+ baseline reason-indent-offset)))))
-                  ;; Point is now at the beginning of the current line
-                  ))))))))
-
-    (when indent
-      ;; If we're at the beginning of the line (before or at the current
-      ;; indentation), jump with the indentation change.  Otherwise, save the
-      ;; excursion so that adding the indentations will leave us at the
-      ;; equivalent position within the line to where we were before.
-      (if (<= (current-column) (current-indentation))
-          (indent-line-to indent)
-        (save-excursion (indent-line-to indent))))))
-
-(provide 'reason-indent)
-
-;;; reason-indent.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/vendor/reason-interaction.el b/users/wpcarro/emacs/.emacs.d/vendor/reason-interaction.el
deleted file mode 100644
index 6ceaed1e93..0000000000
--- a/users/wpcarro/emacs/.emacs.d/vendor/reason-interaction.el
+++ /dev/null
@@ -1,216 +0,0 @@
-;;; reason-interaction.el --- Phrase navitagion for rtop -*-lexical-binding: t-*-
-
-;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
-
-;;; Commentary:
-
-;; Phrase navigation for utop and maybe other REPLs.
-
-;; The utop compatibility layer for Reason was mainly taken from:
-;; https://github.com/ocaml/tuareg/blob/master/tuareg-light.el (big thanks!)
-
-;;; Code:
-
-(defun reason-backward-char (&optional step)
-  "Go back one char.
-Similar to `backward-char` but it does not signal errors
-`beginning-of-buffer` and `end-of-buffer`.  It optionally takes a
-STEP parameter for jumping back more than one character."
-  (when step (goto-char (- (point) step))
-        (goto-char (1- (point)))))
-
-(defun reason-forward-char (&optional step)
-  "Go forward one char.
-Similar to `forward-char` but it does not signal errors
-`beginning-of-buffer` and `end-of-buffer`.  It optionally takes a
-STEP parameter for jumping back more than one character."
-  (when step (goto-char (+ (point) step))
-    (goto-char (1+ (point)))))
-
-(defun reason-in-literal-p ()
-  "Return non-nil if point is inside an Reason literal."
-  (nth 3 (syntax-ppss)))
-
-(defconst reason-comment-delimiter-regexp "\\*/\\|/\\*"
-  "Regex for identify either open or close comment delimiters.")
-
-(defun reason-in-between-comment-chars-p ()
-  "Return non-nil iff point is in between the comment delimiter chars.
-It returns non-nil if point is between the chars only (*|/ or /|*
-where | is point)."
-  (and (not (bobp)) (not (eobp))
-       (or (and (char-equal ?/ (char-before)) (char-equal ?* (char-after)))
-           (and (char-equal ?* (char-before)) (char-equal ?/ (char-after))))))
-
-(defun reason-looking-at-comment-delimiters-p ()
-  "Return non-nil iff point in between comment delimiters."
-  (looking-at-p reason-comment-delimiter-regexp))
-
-(defun reason-in-between-comment-delimiters-p ()
-  "Return non-nil if inside /* and */."
-  (nth 4 (syntax-ppss)))
-
-(defun reason-in-comment-p ()
-  "Return non-nil iff point is inside or right before a comment."
-  (or (reason-in-between-comment-delimiters-p)
-      (reason-in-between-comment-chars-p)
-      (reason-looking-at-comment-delimiters-p)))
-
-(defun reason-beginning-of-literal-or-comment ()
-  "Skip to the beginning of the current literal or comment (or buffer)."
-  (interactive)
-  (goto-char (or (nth 8 (syntax-ppss)) (point))))
-
-(defun reason-inside-block-scope-p ()
-  "Skip to the beginning of the current literal or comment (or buffer)."
-  (and (> (nth 0 (syntax-ppss)) 0)
-       (let ((delim-start (nth 1 (syntax-ppss))))
-         (save-excursion
-           (goto-char delim-start)
-           (char-equal ?{ (following-char))))))
-
-(defun reason-at-phrase-break-p ()
-  "Is the underlying `;' a phrase break?"
-  ;; Difference from OCaml, the phrase separator is a single semi-colon
-  (and (not (eobp))
-       (char-equal ?\; (following-char))))
-
-(defun reason-skip-to-close-delimiter (&optional limit)
-  "Skip to the end of a Reason block.
-It basically calls `re-search-forward` in order to go to any
-closing delimiter, not concerning itself with balancing of any
-sort.  Client code needs to check that.
-LIMIT is passed to `re-search-forward` directly."
-  (re-search-forward "\\s)" limit 'move))
-
-(defun reason-skip-back-to-open-delimiter (&optional limit)
-  "Skip to the beginning of a Reason block backwards.
-It basically calls `re-search-backward` in order to go to any
-opening delimiter, not concerning itself with balancing of any
-sort.  Client code needs to check that.
-LIMIT is passed to `re-search-backward` directly."
-  (re-search-backward "\\s(" limit 'move))
-
-(defun reason-find-phrase-end ()
-  "Skip to the end of a phrase."
-  (while (and (not (eobp))
-              (not (reason-at-phrase-break-p)))
-    (if (re-search-forward ";" nil 'move)
-        (progn (when (reason-inside-block-scope-p)
-                 (reason-skip-to-close-delimiter))
-               (goto-char (1- (point))))
-      ;; avoid infinite loop at the end of the buffer
-      (re-search-forward "[[:space:]\\|\n]+" nil 'move)))
-  (min (goto-char (1+ (point))) (point-max)))
-
-(defun reason-skip-blank-and-comments ()
-  "Skip blank spaces and comments."
-  (cond
-   ((eobp) (point))
-   ((or (reason-in-between-comment-chars-p)
-        (reason-looking-at-comment-delimiters-p)) (progn
-                                                    (reason-forward-char 1)
-                                                    (reason-skip-blank-and-comments)))
-   ((reason-in-between-comment-delimiters-p) (progn
-                                               (search-forward "*/" nil t)
-                                               (reason-skip-blank-and-comments)))
-   ((eolp) (progn
-             (reason-forward-char 1)
-             (reason-skip-blank-and-comments)))
-   (t (progn (skip-syntax-forward " ")
-             (point)))))
-
-(defun reason-skip-back-blank-and-comments ()
-  "Skip blank spaces and comments backwards."
-  (cond
-   ((bobp) (point))
-   ((looking-back reason-comment-delimiter-regexp) (progn
-                                                     (reason-backward-char 1)
-                                                     (reason-skip-back-blank-and-comments)))
-   ((reason-in-between-comment-delimiters-p) (progn
-                                               (search-backward "/*" nil t)
-                                               (reason-backward-char 1)
-                                               (reason-skip-back-blank-and-comments)))
-   ((or (reason-in-between-comment-chars-p)
-        (reason-looking-at-comment-delimiters-p)) (progn
-                                                    (reason-backward-char 1)
-                                                    (reason-skip-back-blank-and-comments)))
-   ((bolp) (progn
-             (reason-backward-char 1)
-             (reason-skip-back-blank-and-comments)))
-   (t (progn (skip-syntax-backward " ")
-             (point)))))
-
-(defun reason-ro (&rest words)
-  "Build a regex matching iff at least a word in WORDS is present."
-  (concat "\\<" (regexp-opt words t) "\\>"))
-
-(defconst reason-find-phrase-beginning-regexp
-  (concat (reason-ro "end" "type" "module" "sig" "struct" "class"
-                     "exception" "open" "let")
-          "\\|^#[ \t]*[a-z][_a-z]*\\>\\|;"))
-
-(defun reason-at-phrase-start-p ()
-  "Return t if is looking at the beginning of a phrase.
-A phrase starts when a toplevel keyword is at the beginning of a line."
-  (or (looking-at "#")
-      (looking-at reason-find-phrase-beginning-regexp)))
-
-(defun reason-find-phrase-beginning-backward ()
-  "Find the beginning of a phrase and return point.
-It scans code backwards, therefore the caller can assume that the
-beginning of the phrase (if found) is always before the starting
-point.  No error is signalled and (point-min) is returned when a
-phrease cannot be found."
-  (beginning-of-line)
-  (while (and (not (bobp)) (not (reason-at-phrase-start-p)))
-    (if (reason-inside-block-scope-p)
-        (reason-skip-back-to-open-delimiter)
-      (re-search-backward reason-find-phrase-beginning-regexp nil 'move)))
-  (point))
-
-(defun reason-discover-phrase ()
-  "Discover a Reason phrase in the buffer."
-  ;; TODO reason-with-internal-syntax ;; tuareg2 modifies the syntax table (removed for now)
-  ;; TODO stop-at-and feature for phrase detection (do we need it?)
-  ;; TODO tuareg2 has some custom logic for module and class (do we need it?)
-  (save-excursion
-    (let ((case-fold-search nil))
-      (reason-skip-blank-and-comments)
-      (list (reason-find-phrase-beginning-backward) ;; beginning
-            (reason-find-phrase-end)                ;; end
-            (save-excursion                         ;; end-with-comment
-              (reason-skip-blank-and-comments)
-              (point))))))
-
-(defun reason-discover-phrase-debug ()
-  "Discover a Reason phrase in the buffer (debug mode)."
-  (let ((triple (reason-discover-phrase)))
-    (message (concat "Evaluating: \"" (reason-fetch-phrase triple) "\""))
-    triple))
-
-(defun reason-fetch-phrase (triple)
-  "Fetch the phrase text given a TRIPLE."
-  (let* ((start (nth 0 triple))
-         (end (nth 1 triple))) ;; we don't need end-with-comment
-    (buffer-substring-no-properties start end)))
-
-(defun reason-next-phrase ()
-  "Skip to the beginning of the next phrase."
-  (cond
-   ((reason-at-phrase-start-p) (point))
-   ((eolp) (progn
-             (forward-char 1)
-             (reason-skip-blank-and-comments)
-             (reason-next-phrase)))
-   ((reason-inside-block-scope-p) (progn (reason-skip-to-close-delimiter)
-                                         (reason-next-phrase)))
-   ((looking-at ";") (progn
-                       (forward-char 1)
-                       (reason-next-phrase)))
-   (t (progn (end-of-line)
-             (reason-next-phrase)))))
-
-(provide 'reason-interaction)
-
-;;; reason-interaction.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/vendor/reason-mode.el b/users/wpcarro/emacs/.emacs.d/vendor/reason-mode.el
deleted file mode 100644
index 789735955d..0000000000
--- a/users/wpcarro/emacs/.emacs.d/vendor/reason-mode.el
+++ /dev/null
@@ -1,242 +0,0 @@
-;;; reason-mode.el --- A major mode for editing ReasonML -*-lexical-binding: t-*-
-;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
-
-;; Version: 0.4.0
-;; Author: Mozilla
-;; Url: https://github.com/reasonml-editor/reason-mode
-;; Keywords: languages, ocaml
-;; Package-Requires: ((emacs "24.3"))
-
-;; This file is NOT part of GNU Emacs.
-
-;; This file is distributed under the terms of both the MIT license and the
-;; Apache License (version 2.0).
-
-;;; Commentary:
-;; This project provides useful functions and helpers for developing code
-;; using the Reason programming language (https://facebook.github.io/reason).
-;;
-;; Reason is an umbrella project that provides a curated layer for OCaml.
-;;
-;; It offers:
-;;  - A new, familiar syntax for the battle-tested language that is OCaml.
-;;  - A workflow for compiling to JavaScript and native code.
-;;  - A set of friendly documentations, libraries and utilities.
-;;
-;; See the README.md for more details.
-
-;;; Code:
-
-(require 'reason-indent)
-(require 'refmt)
-(require 'reason-interaction)
-
-(eval-when-compile (require 'rx)
-                   (require 'compile)
-                   (require 'url-vars))
-
-;; Syntax definitions and helpers
-(defvar reason-mode-syntax-table
-  (let ((table (make-syntax-table)))
-
-    ;; Operators
-    (dolist (i '(?+ ?- ?* ?/ ?& ?| ?^ ?! ?< ?> ?~ ?@))
-      (modify-syntax-entry i "." table))
-
-    ;; Strings
-    (modify-syntax-entry ?\" "\"" table)
-    (modify-syntax-entry ?\\ "\\" table)
-    (modify-syntax-entry ?\' "_"  table)
-
-    ;; Comments
-    (modify-syntax-entry ?/  ". 124b" table)
-    (modify-syntax-entry ?*  ". 23n"  table)
-    (modify-syntax-entry ?\n "> b"    table)
-    (modify-syntax-entry ?\^m "> b"   table)
-
-    table))
-
-(defgroup reason nil
-  "Support for Reason code."
-  :link '(url-link "http://facebook.github.io/reason/")
-  :group 'languages)
-
-(defcustom reason-mode-hook nil
-  "Hook called by `reason-mode'."
-  :type 'hook
-  :group 'reason)
-
-;; Font-locking definitions and helpers
-(defconst reason-mode-keywords
-  '("and" "as"
-    "else" "external"
-    "fun" "for"
-    "if" "impl" "in" "include"
-    "let"
-    "module" "match" "mod" "move" "mutable"
-    "open"
-    "priv" "pub"
-    "rec" "ref" "return"
-    "self" "static" "switch" "struct" "super"
-    "trait" "type"
-    "use"
-    "virtual"
-    "where" "when" "while"))
-
-(defconst reason-mode-consts
-  '("true" "false"))
-
-(defconst reason-special-types
-  '("int" "float" "string" "char"
-    "bool" "unit" "list" "array" "exn"
-    "option" "ref"))
-
-(defconst reason-camel-case
-  (rx symbol-start
-      (group upper (0+ (any word nonascii digit "_")))
-      symbol-end))
-
-(eval-and-compile
-  (defconst reason--char-literal-rx
-    (rx (seq (group "'")
-             (or (seq "\\" anything)
-                 (not (any "'\\")))
-             (group "'")))))
-
-(defun reason-re-word (inner)
-  "Build a word regexp given INNER."
-  (concat "\\<" inner "\\>"))
-
-(defun reason-re-grab (inner)
-  "Build a grab regexp given INNER."
-  (concat "\\(" inner "\\)"))
-
-(defun reason-regexp-opt-symbols (words)
-  "Like `(regexp-opt words 'symbols)`, but will work on Emacs 23.
-See rust-mode PR #42.
-Argument WORDS argument to pass to `regexp-opt`."
-  (concat "\\_<" (regexp-opt words t) "\\_>"))
-
-;;; Syntax highlighting for Reason
-(defvar reason-font-lock-keywords
-  `((,(reason-regexp-opt-symbols reason-mode-keywords) . font-lock-keyword-face)
-    (,(reason-regexp-opt-symbols reason-special-types) . font-lock-builtin-face)
-    (,(reason-regexp-opt-symbols reason-mode-consts) . font-lock-constant-face)
-
-    (,reason-camel-case 1 font-lock-type-face)
-
-    ;; Field names like `foo:`, highlight excluding the :
-    (,(concat (reason-re-grab reason-re-ident) ":[^:]") 1 font-lock-variable-name-face)
-    ;; Module names like `foo::`, highlight including the ::
-    (,(reason-re-grab (concat reason-re-ident "::")) 1 font-lock-type-face)
-    ;; Name punned labeled args like ::foo
-    (,(concat "[[:space:]]+" (reason-re-grab (concat "::" reason-re-ident))) 1 font-lock-type-face)
-
-    ;; TODO jsx attribs?
-    (,
-     (concat "<[/]?" (reason-re-grab reason-re-ident) "[^>]*" ">")
-     1 font-lock-type-face)))
-
-(defun reason-mode-try-find-alternate-file (mod-name extension)
-  "Switch to the file given by MOD-NAME and EXTENSION."
-  (let* ((filename (concat mod-name extension))
-         (buffer (get-file-buffer filename)))
-    (if buffer (switch-to-buffer buffer)
-      (find-file filename))))
-
-(defun reason-mode-find-alternate-file ()
-  "Switch to implementation/interface file."
-  (interactive)
-  (let ((name buffer-file-name))
-    (when (string-match "\\`\\(.*\\)\\.re\\([il]\\)?\\'" name)
-      (let ((mod-name (match-string 1 name))
-            (e (match-string 2 name)))
-        (cond
-         ((string= e "i")
-          (reason-mode-try-find-alternate-file mod-name ".re"))
-         (t
-          (reason-mode-try-find-alternate-file mod-name ".rei")))))))
-
-(defun reason--syntax-propertize-multiline-string (end)
-  "Propertize Reason multiline string.
-Argument END marks the end of the string."
-  (let ((ppss (syntax-ppss)))
-    (when (eq t (nth 3 ppss))
-      (let ((key (save-excursion
-                   (goto-char (nth 8 ppss))
-                   (and (looking-at "{\\([a-z]*\\)|")
-                        (match-string 1)))))
-        (when (search-forward (format "|%s}" key) end 'move)
-          (put-text-property (1- (match-end 0)) (match-end 0)
-                             'syntax-table (string-to-syntax "|")))))))
-
-(defun reason-syntax-propertize-function (start end)
-  "Propertize Reason function.
-Argument START marks the beginning of the function.
-Argument END marks the end of the function."
-  (goto-char start)
-  (reason--syntax-propertize-multiline-string end)
-  (funcall
-   (syntax-propertize-rules
-    (reason--char-literal-rx (1 "\"") (2 "\""))
-    ;; multi line strings
-    ("\\({\\)[a-z]*|"
-     (1 (prog1 "|"
-          (goto-char (match-end 0))
-          (reason--syntax-propertize-multiline-string end)))))
-   (point) end))
-
-(defvar reason-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-c\C-a" #'reason-mode-find-alternate-file)
-    (define-key map "\C-c\C-r" #'refmt-region-ocaml-to-reason)
-    (define-key map "\C-c\C-o" #'refmt-region-reason-to-ocaml)
-    map))
-
-;;;###autoload
-(define-derived-mode reason-mode prog-mode "Reason"
-  "Major mode for Reason code.
-
-\\{reason-mode-map}"
-  :group 'reason
-  :syntax-table reason-mode-syntax-table
-  :keymap reason-mode-map
-
-  ;; Syntax
-  (setq-local syntax-propertize-function #'reason-syntax-propertize-function)
-  ;; Indentation
-  (setq-local indent-line-function 'reason-mode-indent-line)
-  ;; Fonts
-  (setq-local font-lock-defaults '(reason-font-lock-keywords))
-  ;; Misc
-  (setq-local comment-start "/*")
-  (setq-local comment-end   "*/")
-  (setq-local indent-tabs-mode nil)
-  ;; Allow paragraph fills for comments
-  (setq-local comment-start-skip "/\\*+[ \t]*")
-  (setq-local paragraph-start
-              (concat "^[ \t]*$\\|\\*)$\\|" page-delimiter))
-  (setq-local paragraph-separate paragraph-start)
-  (setq-local require-final-newline t)
-  (setq-local normal-auto-fill-function nil)
-  (setq-local comment-multi-line t)
-
-  (setq-local beginning-of-defun-function 'reason-beginning-of-defun)
-  (setq-local end-of-defun-function 'reason-end-of-defun)
-  (setq-local parse-sexp-lookup-properties t))
-
-;;;###autoload
-(add-to-list 'auto-mode-alist '("\\.rei?\\'" . reason-mode))
-
-(defun reason-mode-reload ()
-  "Reload Reason mode."
-  (interactive)
-  (unload-feature 'reason-mode)
-  (unload-feature 'reason-indent)
-  (unload-feature 'reason-interaction)
-  (require 'reason-mode)
-  (reason-mode))
-
-(provide 'reason-mode)
-
-;;; reason-mode.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/vendor/refmt.el b/users/wpcarro/emacs/.emacs.d/vendor/refmt.el
deleted file mode 100644
index b9ea2b43f0..0000000000
--- a/users/wpcarro/emacs/.emacs.d/vendor/refmt.el
+++ /dev/null
@@ -1,231 +0,0 @@
-;;; refmt.el --- utility functions to format reason code
-
-;; Copyright (c) 2014 The go-mode Authors. All rights reserved.
-;; Portions Copyright (c) 2015-present, Facebook, 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:
-
-;; * 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 the copyright holder 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.)
-
-;;; Commentary:
-;;
-
-;;; Code:
-
-(require 'cl-lib)
-
-(defcustom refmt-command "refmt"
-  "The 'refmt' command."
-  :type 'string
-  :group 're-fmt)
-
-(defcustom refmt-show-errors 'buffer
-    "Where to display refmt error output.
-It can either be displayed in its own buffer, in the echo area, or not at all.
-Please note that Emacs outputs to the echo area when writing
-files and will overwrite refmt's echo output if used from inside
-a `before-save-hook'."
-    :type '(choice
-            (const :tag "Own buffer" buffer)
-            (const :tag "Echo area" echo)
-            (const :tag "None" nil))
-      :group 're-fmt)
-
-(defcustom refmt-width-mode nil
-  "Specify width when formatting buffer contents."
-  :type '(choice
-          (const :tag "Window width" window)
-          (const :tag "Fill column" fill)
-          (const :tag "None" nil))
-  :group 're-fmt)
-
-;;;###autoload
-(defun refmt-before-save ()
-  "Add this to .emacs to run refmt on the current buffer when saving:
- (add-hook 'before-save-hook 'refmt-before-save)."
-    (interactive)
-      (when (eq major-mode 'reason-mode) (refmt)))
-
-(defun reason--goto-line (line)
-  (goto-char (point-min))
-    (forward-line (1- line)))
-
-(defun reason--delete-whole-line (&optional arg)
-    "Delete the current line without putting it in the `kill-ring'.
-Derived from function `kill-whole-line'.  ARG is defined as for that
-function."
-    (setq arg (or arg 1))
-    (if (and (> arg 0)
-             (eobp)
-             (save-excursion (forward-visible-line 0) (eobp)))
-        (signal 'end-of-buffer nil))
-    (if (and (< arg 0)
-             (bobp)
-             (save-excursion (end-of-visible-line) (bobp)))
-        (signal 'beginning-of-buffer nil))
-    (cond ((zerop arg)
-           (delete-region (progn (forward-visible-line 0) (point))
-                          (progn (end-of-visible-line) (point))))
-          ((< arg 0)
-           (delete-region (progn (end-of-visible-line) (point))
-                          (progn (forward-visible-line (1+ arg))
-                                 (unless (bobp)
-                                   (backward-char))
-                                 (point))))
-          (t
-           (delete-region (progn (forward-visible-line 0) (point))
-                                                  (progn (forward-visible-line arg) (point))))))
-
-(defun reason--apply-rcs-patch (patch-buffer &optional start-pos)
-  "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer."
-  (setq start-pos (or start-pos (point-min)))
-  (let ((first-line (line-number-at-pos start-pos))
-        (target-buffer (current-buffer))
-        ;; Relative offset between buffer line numbers and line numbers
-        ;; in patch.
-        ;;
-        ;; Line numbers in the patch are based on the source file, so
-        ;; we have to keep an offset when making changes to the
-        ;; buffer.
-        ;;
-        ;; Appending lines decrements the offset (possibly making it
-        ;; negative), deleting lines increments it. This order
-        ;; simplifies the forward-line invocations.
-        (line-offset 0))
-    (save-excursion
-      (with-current-buffer patch-buffer
-        (goto-char (point-min))
-        (while (not (eobp))
-          (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
-            (error "invalid rcs patch or internal error in reason--apply-rcs-patch"))
-          (forward-line)
-          (let ((action (match-string 1))
-                (from (string-to-number (match-string 2)))
-                (len  (string-to-number (match-string 3))))
-            (cond
-             ((equal action "a")
-              (let ((start (point)))
-                (forward-line len)
-                (let ((text (buffer-substring start (point))))
-                  (with-current-buffer target-buffer
-                    (cl-decf line-offset len)
-                    (goto-char start-pos)
-                    (forward-line (- from len line-offset))
-                    (insert text)))))
-             ((equal action "d")
-              (with-current-buffer target-buffer
-                (reason--goto-line (- (1- (+ first-line from)) line-offset))
-                (cl-incf line-offset len)
-                (reason--delete-whole-line len)))
-             (t
-              (error "invalid rcs patch or internal error in reason--apply-rcs-patch")))))))))
-
-(defun refmt--process-errors (filename tmpfile errorfile errbuf)
-  (with-current-buffer errbuf
-    (if (eq refmt-show-errors 'echo)
-        (progn
-          (message "%s" (buffer-string))
-          (refmt--kill-error-buffer errbuf))
-      (insert-file-contents errorfile nil nil nil)
-      ;; Convert the refmt stderr to something understood by the compilation mode.
-      (goto-char (point-min))
-      (insert "refmt errors:\n")
-      (while (search-forward-regexp (regexp-quote tmpfile) nil t)
-        (replace-match (file-name-nondirectory filename)))
-      (compilation-mode)
-      (display-buffer errbuf))))
-
-(defun refmt--kill-error-buffer (errbuf)
-  (let ((win (get-buffer-window errbuf)))
-    (if win
-        (quit-window t win)
-      (with-current-buffer errbuf
-        (erase-buffer))
-      (kill-buffer errbuf))))
-
-(defun apply-refmt (&optional start end from to)
-  (setq start (or start (point-min))
-        end (or end (point-max))
-        from (or from "re")
-        to (or to "re"))
-   (let* ((ext (file-name-extension buffer-file-name t))
-          (bufferfile (make-temp-file "refmt" nil ext))
-          (outputfile (make-temp-file "refmt" nil ext))
-          (errorfile (make-temp-file "refmt" nil ext))
-          (errbuf (if refmt-show-errors (get-buffer-create "*Refmt Errors*")))
-          (patchbuf (get-buffer-create "*Refmt patch*"))
-          (coding-system-for-read 'utf-8)
-          (coding-system-for-write 'utf-8)
-          (width-args
-           (cond
-            ((equal refmt-width-mode 'window)
-             (list "--print-width" (number-to-string (window-body-width))))
-            ((equal refmt-width-mode 'fill)
-             (list "--print-width" (number-to-string fill-column)))
-            (t
-             '()))))
-     (unwind-protect
-         (save-restriction
-           (widen)
-           (write-region start end bufferfile)
-           (if errbuf
-               (with-current-buffer errbuf
-                 (setq buffer-read-only nil)
-                 (erase-buffer)))
-           (with-current-buffer patchbuf
-             (erase-buffer))
-           (if (zerop (apply 'call-process
-                             refmt-command nil (list (list :file outputfile) errorfile)
-                             nil (append width-args (list "--parse" from "--print" to bufferfile))))
-               (progn
-                 (call-process-region start end "diff" nil patchbuf nil "-n" "-"
-                                      outputfile)
-                 (reason--apply-rcs-patch patchbuf start)
-                 (message "Applied refmt")
-                 (if errbuf (refmt--kill-error-buffer errbuf)))
-             (message "Could not apply refmt")
-             (if errbuf
-                 (refmt--process-errors (buffer-file-name) bufferfile errorfile errbuf)))))
-     (kill-buffer patchbuf)
-     (delete-file errorfile)
-     (delete-file bufferfile)
-     (delete-file outputfile)))
-
-(defun refmt ()
-  "Format the current buffer according to the refmt tool."
-  (interactive)
-  (apply-refmt))
-
-(defun refmt-region-ocaml-to-reason (start end)
-  (interactive "r")
-  (apply-refmt start end "ml"))
-
-(defun refmt-region-reason-to-ocaml (start end)
-  (interactive "r")
-  (apply-refmt start end "re" "ml"))
-
-(provide 'refmt)
-
-;;; refmt.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/bag.el b/users/wpcarro/emacs/.emacs.d/wpc/bag.el
deleted file mode 100644
index 467e25fceb..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/bag.el
+++ /dev/null
@@ -1,70 +0,0 @@
-;;; bag.el --- Working with bags (aka multi-sets) -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.3"))
-
-;;; Commentary:
-;; What is a bag?  A bag should be thought of as a frequency table.  It's a way
-;; to convert a list of something into a set that allows duplicates.  Isn't
-;; allowing duplicates the whole thing with Sets?  Kind of.  But the interface
-;; of Sets is something that bags resemble, so multi-set isn't as bag of a name
-;; as it may first seem.
-;;
-;; If you've used Python's collections.Counter, the concept of a bag should be
-;; familiar already.
-;;
-;; Interface:
-;; - add        :: x -> Bag(x) -> Bag(x)
-;; - remove     :: x -> Bag(x) -> Bag(x)
-;; - union      :: Bag(x) -> Bag(x) -> Bag(x)
-;; - difference :: Bag(x) -> Bag(x) -> Bag(x)
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'al)
-(require 'number)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(cl-defstruct bag xs)
-
-(defun bag-update (f xs)
-  "Call F on alist in XS."
-  (let ((ys (bag-xs xs)))
-    (setf (bag-xs xs) (funcall f ys))))
-
-(defun bag-new ()
-  "Create an empty bag."
-  (make-bag :xs (al-new)))
-
-(defun bag-contains? (x xs)
-  "Return t if XS has X."
-  (al-has-key? x (bag-xs xs)))
-
-;; TODO: Tabling this for now since working with structs seems to be
-;; disappointingly difficult.  Where is `struct-update'?
-;; (defun bag-add (x xs)
-;;   "Add X to XS.")
-
-;; TODO: What do we name delete vs. remove?
-;; (defun bag-remove (x xs)
-;;   "Remove X from XS.
-;; This is a no-op is X doesn't exist in XS.")
-
-(defun bag-from-list (xs)
-  "Map a list of `XS' into a bag."
-  (->> xs
-       (list-reduce
-        (bag-new)
-        (lambda (x acc)
-          (bag-add x 1 #'number-inc acc)))))
-
-(provide 'bag)
-;;; bag.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/bookmark.el b/users/wpcarro/emacs/.emacs.d/wpc/bookmark.el
deleted file mode 100644
index c5e3bf3ac9..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/bookmark.el
+++ /dev/null
@@ -1,89 +0,0 @@
-;;; bookmark.el --- Saved files and directories on my filesystem -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.3"))
-
-;;; Commentary:
-;; After enjoying and relying on Emacs's builtin `jump-to-register' command, I'd
-;; like to recreate this functionality with a few extensions.
-;;
-;; Everything herein will mimmick my previous KBDs for `jump-to-register', which
-;; were <leader>-j-<register-kbd>.  If the `bookmark-path' is a file, Emacs will
-;; open a buffer with that file.  If the `bookmark-path' is a directory, Emacs
-;; will open an ivy window searching that directory.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'f)
-(require 'buffer)
-(require 'dash)
-(require 'string)
-(require 'set)
-(require 'constants)
-(require 'general)
-(require 'tvl)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Constants
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(cl-defstruct bookmark label path kbd)
-
-(defun bookmark-handle-directory-dwim (path)
-  "Open PATH as either a project directory or a regular directory."
-  (with-temp-buffer
-    (cd path)
-    (call-interactively #'project-find-file)))
-
-(defconst bookmark-handle-directory #'bookmark-handle-directory-dwim
-  "Function to call when a bookmark points to a directory.")
-
-(defconst bookmark-handle-file #'counsel-find-file-action
-  "Function to call when a bookmark points to a file.")
-
-(defconst bookmark-whitelist
-  (list
-   (make-bookmark :label "depot"
-                  :path tvl-depot-path
-                  :kbd "d"))
-  "List of registered bookmarks.")
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; API
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun bookmark-open (b)
-  "Open bookmark, B, in a new buffer or an ivy minibuffer."
-  (let ((path (bookmark-path b)))
-    (cond
-     ((f-directory? path)
-      (funcall bookmark-handle-directory path))
-     ((f-file? path)
-      (funcall bookmark-handle-file path)))))
-
-(defun bookmark-install-kbd (b)
-  "Define two functions to explore B and assign them to keybindings."
-  (eval `(defun ,(intern (format "bookmark-visit-%s" (bookmark-label b))) ()
-           (interactive)
-           (find-file ,(bookmark-path b))))
-  (eval `(defun ,(intern (format "bookmark-browse-%s" (bookmark-label b))) ()
-           (interactive)
-           (bookmark-open ,b)))
-  (general-define-key
-   :prefix "<SPC>"
-   :states '(motion)
-   (format "J%s" (bookmark-kbd b)) `,(intern (format "bookmark-visit-%s" (bookmark-label b)))
-   (format "j%s" (bookmark-kbd b)) `,(intern (format "bookmark-browse-%s" (bookmark-label b)))))
-
-(defun bookmark-install-kbds ()
-  "Install the keybindings defined herein."
-  (->> bookmark-whitelist
-       (-map #'bookmark-install-kbd)))
-
-(provide 'bookmark)
-;;; bookmark.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/buffer.el b/users/wpcarro/emacs/.emacs.d/wpc/buffer.el
index fa98393df8..0f86f7f811 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/buffer.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/buffer.el
@@ -33,6 +33,7 @@
 (require 'struct)
 (require 'ts)
 (require 'general)
+(require 'list)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Library
@@ -142,12 +143,12 @@ Return a reference to that buffer."
 (defun buffer-cycle-next ()
   "Cycle forward through the `buffer-source-code-buffers'."
   (interactive)
-  (buffer-cycle #'cycle-next))
+  (buffer-cycle #'cycle-next!))
 
 (defun buffer-cycle-prev ()
   "Cycle backward through the `buffer-source-code-buffers'."
   (interactive)
-  (buffer-cycle #'cycle-prev))
+  (buffer-cycle #'cycle-prev!))
 
 (defun buffer-ivy-source-code ()
   "Use `ivy-read' to choose among all open source code buffers."
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/bytes.el b/users/wpcarro/emacs/.emacs.d/wpc/bytes.el
deleted file mode 100644
index b76921d3c7..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/bytes.el
+++ /dev/null
@@ -1,112 +0,0 @@
-;;; bytes.el --- Working with byte values -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.3"))
-
-;;; Commentary:
-;; Functions to help with human-readable representations of byte values.
-;;
-;; Usage:
-;; See the test cases for example usage.  Or better yet, I should use a type of
-;; structured documentation that would allow me to expose a view into the test
-;; suite here.  Is this currently possible in Elisp?
-;;
-;; API:
-;; - serialize :: Integer -> String
-;;
-;; Wish list:
-;; - Rounding: e.g. (bytes (* 1024 1.7)) => "2KB"
-
-;;; Code:
-
-;; TODO: Support -ibabyte variants like Gibibyte (GiB).
-
-;; Ranges:
-;;  B: [   0,  1e3)
-;; KB: [ 1e3,  1e6)
-;; MB: [ 1e6,  1e6)
-;; GB: [ 1e9, 1e12)
-;; TB: [1e12, 1e15)
-;; PB: [1e15, 1e18)
-;;
-;; Note: I'm currently not support exabytes because that causes the integer to
-;;  overflow.  I imagine a larger integer type may exist, but for now, I'll
-;;  treat this as a YAGNI.
-
-(require 'prelude)
-(require 'tuple)
-(require 'math)
-(require 'number)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Constants
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defconst bytes-kb (math-exp 2 10)
-  "Number of bytes in a kilobyte.")
-
-(defconst bytes-mb (math-exp 2 20)
-  "Number of bytes in a megabytes.")
-
-(defconst bytes-gb (math-exp 2 30)
-  "Number of bytes in a gigabyte.")
-
-(defconst bytes-tb (math-exp 2 40)
-  "Number of bytes in a terabyte.")
-
-(defconst bytes-pb (math-exp 2 50)
-  "Number of bytes in a petabyte.")
-
-(defconst bytes-eb (math-exp 2 60)
-  "Number of bytes in an exabyte.")
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Functions
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun bytes-classify (x)
-  "Return unit that closest fits byte count, X."
-  (prelude-assert (number-whole? x))
-  (cond
-   ((and (>= x 0)        (< x bytes-kb))     'byte)
-   ((and (>= x bytes-kb) (< x bytes-mb)) 'kilobyte)
-   ((and (>= x bytes-mb) (< x bytes-gb)) 'megabyte)
-   ((and (>= x bytes-gb) (< x bytes-tb)) 'gigabyte)
-   ((and (>= x bytes-tb) (< x bytes-pb)) 'terabyte)
-   ((and (>= x bytes-pb) (< x bytes-eb)) 'petabyte)))
-
-(defun bytes-to-string (x)
-  "Convert integer X into a human-readable string."
-  (let ((base-and-unit
-         (pcase (bytes-classify x)
-           ('byte     (tuple/from        1 "B"))
-           ('kilobyte (tuple/from bytes-kb "KB"))
-           ('megabyte (tuple/from bytes-mb "MB"))
-           ('gigabyte (tuple/from bytes-gb "GB"))
-           ('terabyte (tuple/from bytes-tb "TB"))
-           ('petabyte (tuple/from bytes-pb "PB")))))
-    (string-format "%d%s"
-                   (round x (tuple/first base-and-unit))
-                   (tuple/second base-and-unit))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(progn
-  (prelude-assert
-   (equal "1000B" (bytes-to-string 1000)))
-  (prelude-assert
-   (equal "2KB" (bytes-to-string (* 2 bytes-kb))))
-  (prelude-assert
-   (equal "17MB" (bytes-to-string (* 17 bytes-mb))))
-  (prelude-assert
-   (equal "419GB" (bytes-to-string (* 419 bytes-gb))))
-  (prelude-assert
-   (equal "999TB" (bytes-to-string (* 999 bytes-tb))))
-  (prelude-assert
-   (equal "2PB" (bytes-to-string (* 2 bytes-pb)))))
-
-(provide 'bytes)
-;;; bytes.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/cache.el b/users/wpcarro/emacs/.emacs.d/wpc/cache.el
deleted file mode 100644
index 70ebdb71ef..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/cache.el
+++ /dev/null
@@ -1,88 +0,0 @@
-;;; cache.el --- Caching things -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.3"))
-
-;;; Commentary:
-;; An immutable cache data structure.
-;;
-;; This is like a sideways stack, that you can pull values out from and re-push
-;; to the top.  It'd be like a stack supporting push, pop, pull.
-;;
-;; This isn't a key-value data-structure like you might expect from a
-;; traditional cache.  The name is subject to change, but the underlying idea of
-;; a cache remains the same.
-;;
-;; Think about prescient.el, which uses essentially an LRU cache integrated into
-;; counsel to help create a "clairovoyant", self-organizing list.
-;;
-;; Use-cases:
-;; - Keeps an cache of workspaces sorted as MRU with an LRU eviction strategy.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'prelude)
-(require 'struct)
-(require '>)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(cl-defstruct cache xs)
-
-;; TODO: Prefer another KBD for yasnippet form completion than company-mode's
-;; current KBD.
-
-(defun cache-from-list (xs)
-  "Turn list, XS, into a cache."
-  (make-cache :xs xs))
-
-(defun cache-contains? (x xs)
-  "Return t if X in XS."
-  (->> xs
-       cache-xs
-       (list-contains? x)))
-
-(defun cache-touch (x xs)
-  "Ensure value X in cache, XS, is front of the list.
-If X isn't in XS (using `equal'), insert it at the front."
-  (struct-update
-   cache
-   xs
-   (>-> (list-reject (lambda (y) (equal x y)))
-       (list-cons x))
-   xs))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(progn
-  (let ((cache (cache-from-list '("chicken" "nugget"))))
-    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-    ;; contains?/2
-    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-    (prelude-refute
-     (cache-contains? "turkey" cache))
-    (prelude-assert
-     (cache-contains? "chicken" cache))
-    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-    ;; touch/2
-    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-    (prelude-assert
-     (equal
-      (cache-touch "nugget" cache)
-      (cache-from-list '("nugget" "chicken"))))
-    (prelude-assert
-     (equal
-      (cache-touch "spicy" cache)
-      (cache-from-list '("spicy" "chicken" "nugget"))))))
-
-(provide 'cache)
-;;; cache.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/clipboard.el b/users/wpcarro/emacs/.emacs.d/wpc/clipboard.el
index 1c14eebae1..ec2a46f540 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/clipboard.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/clipboard.el
@@ -9,9 +9,6 @@
 ;;
 ;; Integrate with bburns/clipmon so that System Clipboard can integrate with
 ;; Emacs's kill-ring.
-;;
-;; Wish list:
-;; - Create an Emacs integration with github.com/cdown/clipmenud.
 
 ;;; Code:
 
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/colorscheme.el b/users/wpcarro/emacs/.emacs.d/wpc/colorscheme.el
deleted file mode 100644
index cc2afd6c57..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/colorscheme.el
+++ /dev/null
@@ -1,85 +0,0 @@
-;;; colorscheme.el --- Syntax highlight and friends -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.3"))
-
-;;; Commentary:
-;;
-;; TODO: Clarify this.
-;; Since I have my own definition of "theme", which couples wallpaper, font,
-;; with Emacs's traditional notion of the word "theme", I'm choosing to use
-;; "colorscheme" to refer to *just* the notion of syntax highlight etc.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'cycle)
-(require '>)
-(require 'cl-lib)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defcustom colorscheme-whitelist
-  (cycle-from-list
-   (->> (custom-available-themes)
-        (list-map #'symbol-name)
-        (list-filter (>-> (s-starts-with? "doom-")))
-        (list-map #'intern)))
-  "The whitelist of colorschemes through which to cycle.")
-
-(defun colorscheme-current ()
-  "Return the currently enabled colorscheme."
-  (cycle-current colorscheme-whitelist))
-
-(defun colorscheme-disable-all ()
-  "Disable all currently enabled colorschemes."
-  (interactive)
-  (->> custom-enabled-themes
-       (list-map #'disable-theme)))
-
-(defun colorscheme-set (theme)
-    "Call `load-theme' with `THEME', ensuring that the line numbers are bright.
-There is no hook that I'm aware of to handle this more elegantly."
-    (load-theme theme t)
-    (prelude-set-line-number-color "#da5468"))
-
-(defun colorscheme-whitelist-set (colorscheme)
-  "Focus the COLORSCHEME in the `colorscheme-whitelist' cycle."
-  (cycle-focus (lambda (x) (equal x colorscheme)) colorscheme-whitelist)
-  (colorscheme-set (colorscheme-current)))
-
-(defun colorscheme-ivy-select ()
-  "Load a colorscheme using ivy."
-  (interactive)
-  (let ((theme (ivy-read "Theme: " (cycle-to-list colorscheme-whitelist))))
-    (colorscheme-disable-all)
-    (colorscheme-set (intern theme))))
-
-(cl-defun colorscheme-cycle (&key forward?)
-  "Cycle next if `FORWARD?' is non-nil.
-Cycle prev otherwise."
-  (disable-theme (cycle-current colorscheme-whitelist))
-  (let ((theme (if forward?
-                   (cycle-next colorscheme-whitelist)
-                 (cycle-prev colorscheme-whitelist))))
-    (colorscheme-set theme)
-    (message (s-concat "Active theme: " (symbol-to-string theme)))))
-
-(defun colorscheme-next ()
-  "Disable the currently active theme and load the next theme."
-  (interactive)
-  (colorscheme-cycle :forward? t))
-
-(defun colorscheme-prev ()
-  "Disable the currently active theme and load the previous theme."
-  (interactive)
-  (colorscheme-cycle :forward? nil))
-
-(provide 'colorscheme)
-;;; colorscheme.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/constants.el b/users/wpcarro/emacs/.emacs.d/wpc/constants.el
index 69003f5955..48bcd9042f 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/constants.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/constants.el
@@ -20,7 +20,10 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (defconst constants-ci? (maybe-some? (getenv "CI"))
-  "Encoded as t when Emacs is running in CI.")
+  "Defined as t when Emacs is running in CI.")
+
+(defconst constants-osx? (eq system-type 'darwin)
+  "Defined as t when OSX is running.")
 
 (provide 'constants)
 ;;; constants.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/device.el b/users/wpcarro/emacs/.emacs.d/wpc/device.el
deleted file mode 100644
index 09819ad748..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/device.el
+++ /dev/null
@@ -1,62 +0,0 @@
-;;; device.el --- Physical device information -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; Functions for querying device information.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'dash)
-(require 'al)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; TODO(wpcarro): Consider integrating this with Nix and depot instead of
-;; denormalizing the state.
-(defconst device-hostname->device
-  '(("zeno.lon.corp.google.com" . work-desktop-lon)
-    ("wpcarro.svl.corp.google.com" . work-desktop-svl)
-    ("seneca" . work-laptop)
-    ("marcus" . personal-laptop)
-    ("diogenes" . personal-vm))
-  "Mapping hostname to a device symbol.")
-
-;; TODO: Should I generate these predicates?
-
-(defun device-classify ()
-  "Return the device symbol for the current host or nil if not supported."
-  (al-get system-name device-hostname->device))
-
-(defun device-work-laptop? ()
-  "Return t if current device is work laptop."
-  (equal 'work-laptop
-         (device-classify)))
-
-(defun device-work-desktop? ()
-  "Return t if current device is work desktop."
-  (-contains? '(work-desktop-lon
-                work-desktop-svl)
-              (device-classify)))
-
-(defun device-corporate? ()
-  "Return t if the current device is owned by my company."
-  (-contains? '(work-desktop-lon
-                work-desktop-svl
-                work-laptop)
-              (device-classify)))
-
-(defun device-laptop? ()
-  "Return t if the current device is a laptop."
-  (-contains? '(work-laptop personal-laptop) (device-classify)))
-
-(provide 'device)
-;;; device.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/dotted.el b/users/wpcarro/emacs/.emacs.d/wpc/dotted.el
deleted file mode 100644
index b824ddbda7..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/dotted.el
+++ /dev/null
@@ -1,57 +0,0 @@
-;;; dotted.el --- Working with dotted pairs in Elisp -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.3"))
-
-;;; Commentary:
-;; Part of my primitives library extensions in Elisp.  Contrast my primitives
-;; with the wrapper extensions that I provide, which expose immutable variants
-;; of data structures like an list, alist, tuple, as well as quasi-typeclasses
-;; like sequence, etc.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'prelude)
-(require 'macros)
-(require 'cl-lib)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(cl-defun dotted-new (&optional a b)
-  "Create a new dotted pair of A and B."
-  (cons a b))
-
-(defun dotted-instance? (x)
-  "Return t if X is a dotted pair."
-  (let ((b (cdr x)))
-    (and b (atom b))))
-
-(defun dotted-first (x)
-  "Return the first element of X."
-  (car x))
-
-(defun dotted-second (x)
-  "Return the second element of X."
-  (cdr x))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(progn
-  (prelude-assert
-   (equal '(fname . "Bob") (dotted-new 'fname "Bob")))
-  (prelude-assert
-   (dotted-instance? '(one . two)))
-  (prelude-refute
-   (dotted-instance? '(1 2 3))))
-
-(provide 'dotted)
-;;; dotted.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/fonts.el b/users/wpcarro/emacs/.emacs.d/wpc/fonts.el
index 196b882862..0f70f69c2b 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/fonts.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/fonts.el
@@ -9,15 +9,10 @@
 
 ;;; Code:
 
-;; TODO: `defcustom' font-size.
-;; TODO: `defcustom' fonts.
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Dependencies
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(require 'prelude)
-(require 'cycle)
 (require 'maybe)
 (require 'cl-lib)
 
@@ -25,93 +20,30 @@
 ;; Constants
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-;; TODO: Troubleshoot why "8" appears so large on my desktop.
-
-;; TODO: Consider having a different font size when I'm using my 4K monitor.
-
-(defconst fonts-size "10"
-  "My preferred default font-size.")
-
-(defconst fonts-size-step 10
-  "The amount (%) by which to increase or decrease a font.")
-
-(defconst fonts-hacker-news-recommendations
-  '("APL385 Unicode"
-    "Go Mono"
-    "Sudo"
-    "Monoid"
-    "Input Mono Medium" ;; NOTE: Also "Input Mono Thin" is nice.
-    )
-  "List of fonts optimized for programming I found in a HN article.")
-
-(defconst fonts-whitelist
-  (cycle-from-list
-   (list-concat
-    fonts-hacker-news-recommendations
-    '("JetBrainsMono"
-      "Mononoki Medium"
-      "Monospace"
-      "Operator Mono Light"
-      "Courier"
-      "Andale Mono"
-      "Source Code Pro"
-      "Terminus")))
-  "This is a list of my preferred fonts.")
+(defgroup fonts nil
+  "Customize group for fonts configuration.")
+
+(defcustom fonts-size "10"
+  "My preferred default font-size."
+  :group 'fonts)
+
+(defcustom fonts-size-step 10
+  "The amount (%) by which to increase or decrease a font."
+  :group 'fonts)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Functions
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-;; TODO: fonts and fonts-whitelist make it difficult to name functions like
-;; fonts-set as a generic Emacs function vs choosing a font from the whitelist.
-
-(cl-defun fonts-cycle (&key forward?)
-  "Cycle forwards when `FORWARD?' non-nil."
-  (let ((font (if forward?
-                  (cycle-next fonts-whitelist)
-                (cycle-prev fonts-whitelist))))
-    (message (s-concat "Active font: " font))
-    (fonts-set font)))
-
-(defun fonts-next ()
-  "Quickly cycle through preferred fonts."
-  (interactive)
-  (fonts-cycle :forward? t))
-
-(defun fonts-prev ()
-  "Quickly cycle through preferred fonts."
-  (interactive)
-  (fonts-cycle :forward? nil))
-
 (defun fonts-set (font &optional size)
   "Change the font to `FONT' with option integer, SIZE, in pixels."
   (if (maybe-some? size)
       (set-frame-font (string-format "%s %s" font size) nil t)
     (set-frame-font font nil t)))
 
-(defun fonts-whitelist-set (font)
-  "Focuses the FONT in the `fonts-whitelist' cycle.
-The size of the font is determined by `fonts-size'."
-  (prelude-assert (cycle-contains? font fonts-whitelist))
-  (cycle-focus (lambda (x) (equal x font)) fonts-whitelist)
-  (fonts-set (fonts-current) fonts-size))
-
-(defun fonts-ivy-select ()
-  "Select a font from an ivy prompt."
-  (interactive)
-  (fonts-whitelist-set
-   (ivy-read "Font: " (cycle-to-list fonts-whitelist))))
-
-(defun fonts-print-current ()
-  "Message the currently enabled font."
-  (interactive)
-  (message
-   (string-format "[fonts] Current font: \"%s\""
-                  (fonts-current))))
-
 (defun fonts-current ()
   "Return the currently enabled font."
-  (cycle-current fonts-whitelist))
+  (symbol-name (font-get (face-attribute 'default :font) :family)))
 
 (defun fonts-increase-size ()
   "Increase font size."
@@ -130,7 +62,7 @@ The size of the font is determined by `fonts-size'."
 (defun fonts-reset-size ()
   "Restore font size to its default value."
   (interactive)
-  (fonts-whitelist-set (fonts-current)))
+  (fonts-set (fonts-current) fonts-size))
 
 (defun fonts-enable-ligatures ()
   "Call this function to enable ligatures."
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/fs.el b/users/wpcarro/emacs/.emacs.d/wpc/fs.el
deleted file mode 100644
index c303b23539..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/fs.el
+++ /dev/null
@@ -1,69 +0,0 @@
-;;; fs.el --- Make working with the filesystem easier -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.1"))
-
-;;; Commentary:
-;; Ergonomic alternatives for working with the filesystem.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'dash)
-(require 'f)
-(require 's)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun fs-ensure-file (path)
-  "Ensure that a file and its directories in `PATH' exist.
-Will error for inputs with a trailing slash."
-  (when (s-ends-with? "/" path)
-    (error (format "Input path has trailing slash: %s" path)))
-  (->> path
-       f-dirname
-       fs-ensure-dir)
-  (f-touch path))
-
-(f-dirname "/tmp/a/b/file.txt")
-
-(defun fs-ensure-dir (path)
-  "Ensure that a directory and its ancestor directories in `PATH' exist."
-  (->> path
-       f-split
-       (apply #'f-mkdir)))
-
-(defun fs-ls (dir &optional full-path?)
-  "List the files in `DIR' one-level deep.
-Should behave similarly in spirit to the Unix command, ls.
-If `FULL-PATH?' is set, return the full-path of the files."
-  (-drop 2 (directory-files dir full-path?)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(ert-deftest fs-test-ensure-file ()
-  (let ((file "/tmp/file/a/b/c/file.txt"))
-    ;; Ensure this file doesn't exist first to prevent false-positives.
-    (f-delete file t)
-    (fs-ensure-file file)
-    (should (and (f-exists? file)
-                 (f-file? file)))))
-
-(ert-deftest fs-test-ensure-dir ()
-  (let ((dir "/tmp/dir/a/b/c"))
-    ;; Ensure the directory doesn't exist.
-    (f-delete dir t)
-    (fs-ensure-dir dir)
-    (should (and (f-exists? dir)
-                 (f-dir? dir)))))
-
-(provide 'fs)
-;;; fs.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/functions.el b/users/wpcarro/emacs/.emacs.d/wpc/functions.el
deleted file mode 100644
index 936e25eb2d..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/functions.el
+++ /dev/null
@@ -1,46 +0,0 @@
-;;; functions.el --- Helper functions -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24"))
-
-;;; Commentary:
-;; This file hopefully contains friendly APIs that making ELisp development more
-;; enjoyable.
-
-;; TODO: Break these out into separate modules.
-
-;;; Code:
-(defun functions-evil-window-vsplit-right ()
-  "Split the window vertically and focus the right half."
-  (interactive)
-  (evil-window-vsplit)
-  (windmove-right))
-
-(defun functions-evil-window-split-down ()
-  "Split the window horizontal and focus the bottom half."
-  (interactive)
-  (evil-window-split)
-  (windmove-down))
-
-(defun functions-create-snippet ()
-  "Create a window split and then opens the Yasnippet editor."
-  (interactive)
-  (evil-window-vsplit)
-  (call-interactively #'yas-new-snippet))
-
-(defun functions-evil-replace-under-point ()
-  "Faster than typing %s//thing/g."
-  (interactive)
-  (let ((term (s-replace "/" "\\/" (symbol-to-string (symbol-at-point)))))
-    (save-excursion
-      (evil-ex (concat "%s/\\b" term "\\b/")))))
-
-(defun functions-buffer-dirname ()
-  "Return the directory name of the current buffer as a string."
-  (->> buffer-file-name
-       f-dirname
-       f-filename))
-
-(provide 'functions)
-;;; functions.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/graph.el b/users/wpcarro/emacs/.emacs.d/wpc/graph.el
deleted file mode 100644
index 1d2f67a4dd..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/graph.el
+++ /dev/null
@@ -1,94 +0,0 @@
-;;; graph.el --- Working with in-memory graphs -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.3"))
-
-;;; Commentary:
-;;
-;; Remember that there are optimal three ways to model a graph:
-;; 1. Edge List
-;; 2. Vertex Table (a.k.a. Neighbors Table)
-;; 3. Adjacency Matrix
-;;
-;; I may call these "Edges", "Neighbors", "Adjacencies" to avoid verbose naming.
-;; For now, I'm avoiding dealing with Adjacency Matrices as I don't have an
-;; immediate use-case for them.  This is subject to change.
-;;
-;; There are also hybrid representations of graphs that combine the three
-;; aforementioned models.  I believe Erlang's digraph module models graphs in
-;; Erlang Term Storage (i.e. ETS) this way.
-;; TODO: Verify this claim.
-;;
-;; Graphs can be weighted or unweighted.  They can also be directed or
-;; undirected.
-;; TODO: Create a table explaining all graph variants.
-;;
-;; TODO: Figure out the relationship of this module and tree.el, which should in
-;; principle overlap.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'prelude)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; For now, I'll support storing *either* neighbors or edges in the graph struct
-;; as long as both aren't set, since that introduces consistency issues.  I may
-;; want to handle that use-case in the future, but not now.
-(cl-defstruct graph neighbors edges)
-
-;; TODO: How do you find the starting point for a topo sort?
-(defun graph-sort (xs)
-  "Return a topological sort of XS.")
-
-(defun graph-from-edges (xs)
-  "Create a graph struct from the Edge List, XS.
-The user must pass in a valid Edge List since asserting on the shape of XS might
-  be expensive."
-  (make-graph :edges xs))
-
-(defun graph-from-neighbors (xs)
-  "Create a graph struct from a Neighbors Table, XS.
-The user must pass in a valid Neighbors Table since asserting on the shape of
-  XS might be expensive."
-  (make-graph :neighbors xs))
-
-(defun graph-instance? (xs)
-  "Return t if XS is a graph struct."
-  (graph-p xs))
-
-;; TODO: Model each of the mapping functions into an isomorphism.
-(defun graph-edges->neighbors (xs)
-  "Map Edge List, XS, into a Neighbors Table."
-  (prelude-assert (graph-instance? xs)))
-
-(defun graph-neighbors->edges (xs)
-  "Map Neighbors Table, XS, into an Edge List."
-  (prelude-assert (graph-instance? xs)))
-
-;; Below are three different models of the same unweighted, directed graph.
-
-(defvar graph-edges
-  '((a . b) (a . c) (a . e)
-    (b . c) (b . d)
-    (c . e)
-    (d . f)
-    (e . d) (e . f)))
-
-(defvar graph-neighbors
-  ((a b c e)
-   (b c d)
-   (c e)
-   (d f)
-   (e d g)
-   (f)))
-
-(provide 'graph)
-;;; graph.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/irc.el b/users/wpcarro/emacs/.emacs.d/wpc/irc.el
deleted file mode 100644
index 9103bd38fe..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/irc.el
+++ /dev/null
@@ -1,170 +0,0 @@
-;;; irc.el --- Configuration for IRC chat -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; Need to decide which client I will use for IRC.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'erc)
-(require 'cycle)
-(require 'string)
-(require 'prelude)
-(require 'al)
-(require 'set)
-(require 'maybe)
-(require 'macros)
-(require '>)
-(require 'password-store)
-(require 'general)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Configuration
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defcustom irc-install-kbds? t
-  "When t, install the keybindings defined herein.")
-
-(setq erc-rename-buffers t)
-
-;; Setting `erc-join-buffer' to 'bury prevents erc from stealing focus of the
-;; current buffer when it connects to IRC servers.
-(setq erc-join-buffer 'bury)
-
-;; TODO: Find a way to avoid putting "freenode" and "#freenode" as channels
-;; here.  I'm doing it because when erc first connects, it's `(buffer-name)' is
-;; "freenode", so when `irc-next-channel' is called, it 404s on the
-;; `cycle-contains?' call in `irc-channel->cycle" unless "freenode" is there. To
-;; make matters even uglier, when `erc-join-channel' is called with "freenode"
-;; as the value, it connects to the "#freenode" channel, so unless "#freenode"
-;; exists in this cycle also, `irc-next-channel' breaks again.
-(defconst irc-server->channels
-  `(("irc.freenode.net"    . ,(cycle-new "freenode" "#freenode" "#nixos" "#emacs" "#pass"))
-    ("irc.corp.google.com" . ,(cycle-new "#drive-prod")))
-  "Mapping of IRC servers to a cycle of my preferred channels.")
-
-;; TODO: Here is another horrible hack that should be revisted.
-(setq erc-autojoin-channels-alist
-      (->> irc-server->channels
-           (al-map-values #'cycle-to-list)
-           (al-map-keys (>-> (s-chop-prefix "irc.")
-                             (s-chop-suffix ".net")))))
-
-;; TODO: Assert that no two servers have a channel with the same name. We need
-;; this because that's the assumption that underpins the `irc-channel->server'
-;; function. This will probably be an O(n^2) operation.
-(prelude-assert
- (set-distinct? (set-from-list
-                 (cycle-to-list
-                  (al-get "irc.freenode.net"
-                          irc-server->channels)))
-                (set-from-list
-                 (cycle-to-list
-                  (al-get "irc.corp.google.com"
-                          irc-server->channels)))))
-
-(defun irc-channel->server (server->channels channel)
-  "Using SERVER->CHANNELS, resolve an IRC server from a given CHANNEL."
-  (let ((result (al-find (lambda (k v) (cycle-contains? channel v))
-                         server->channels)))
-    (prelude-assert (maybe-some? result))
-    result))
-
-(defun irc-channel->cycle (server->channels channel)
-  "Using SERVER->CHANNELS, resolve an IRC's channels cycle from CHANNEL."
-  (al-get (irc-channel->server server->channels channel)
-          server->channels))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun irc-message (x)
-  "Print message X in a structured way."
-  (message (string-format "[irc.el] %s" x)))
-
-;; TODO: Integrate Google setup with Freenode setup.
-
-;; TODO: Support function or KBD for switching to an ERC buffer.
-
-(defun irc-kill-all-erc-processes ()
-  "Kill all ERC buffers and processes."
-  (interactive)
-  (->> (erc-buffer-list)
-       (-map #'kill-buffer)))
-
-(defun irc-switch-to-erc-buffer ()
-  "Switch to an ERC buffer."
-  (interactive)
-  (let ((buffers (erc-buffer-list)))
-    (if (list-empty? buffers)
-        (error "[irc.el] No ERC buffers available")
-      (switch-to-buffer (list-head (erc-buffer-list))))))
-
-(defun irc-connect-to-freenode ()
-  "Connect to Freenode IRC."
-  (interactive)
-  (erc-ssl :server "irc.freenode.net"
-           :port 6697
-           :nick "wpcarro"
-           :password (password-store-get "programming/irc/freenode")
-           :full-name "William Carroll"))
-
-;; TODO: Handle failed connections.
-(defun irc-connect-to-google ()
-  "Connect to Google's Corp IRC using ERC."
-  (interactive)
-  (erc-ssl :server "irc.corp.google.com"
-           :port 6697
-           :nick "wpcarro"
-           :full-name "William Carroll"))
-
-;; TODO: Prefer defining these with a less homespun solution. There is a
-;; function call `erc-buffer-filter' that would be more appropriate for the
-;; implementation of `irc-next-channel' and `irc-prev-channel'.
-(defun irc-next-channel ()
-  "Join the next channel for the active server."
-  (interactive)
-  (with-current-buffer (current-buffer)
-    (let ((cycle (irc-channel->cycle irc-server->channels (buffer-name))))
-      (erc-join-channel
-       (cycle-next cycle))
-      (irc-message
-       (string-format "Current IRC channel: %s" (cycle-current cycle))))))
-
-(defun irc-prev-channel ()
-  "Join the previous channel for the active server."
-  (interactive)
-  (with-current-buffer (current-buffer)
-    (let ((cycle (irc-channel->cycle irc-server->channels (buffer-name))))
-      (erc-join-channel
-       (cycle-prev cycle))
-      (irc-message
-       (string-format "Current IRC channel: %s" (cycle-current cycle))))))
-
-(add-hook 'erc-mode-hook (macros-disable auto-fill-mode))
-(add-hook 'erc-mode-hook (macros-disable company-mode))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Keybindings
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(when irc-install-kbds?
-  (general-define-key
-   :keymaps 'erc-mode-map
-   "<C-tab>" #'irc-next-channel
-   "<C-S-iso-lefttab>" #'irc-prev-channel))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(provide 'irc)
-;;; irc.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/ivy-clipmenu.el b/users/wpcarro/emacs/.emacs.d/wpc/ivy-clipmenu.el
deleted file mode 100644
index 70e68d257c..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/ivy-clipmenu.el
+++ /dev/null
@@ -1,137 +0,0 @@
-;;; ivy-clipmenu.el --- Emacs client for clipmenu -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; Ivy integration with the clipboard manager, clipmenu.  Essentially, clipmenu
-;; turns your system clipboard into a list.
-;;
-;; To use this module, you must first install clipmenu and ensure that the
-;; clipmenud daemon is running.  Refer to the installation instructions at
-;; github.com/cdown/clipmenu for those details.
-;;
-;; This module intentionally does not define any keybindings since I'd prefer
-;; not to presume my users' preferences.  Personally, I use EXWM as my window
-;; manager, so I call `exwm-input-set-key' and map it to `ivy-clipmenu-copy'.
-;;
-;; Usually clipmenu integrates with rofi or dmenu.  This Emacs module integrates
-;; with ivy.  Launch this when you want to select a clip.
-;;
-;; Clipmenu itself supports a variety of environment variables that allow you to
-;; customize its behavior.  These variables are respected herein.  If you'd
-;; prefer to customize clipmenu's behavior from within Emacs, refer to the
-;; variables defined in this module.
-;;
-;; For more information:
-;; - See `clipmenu --help`.
-;; - Visit github.com/cdown/clipmenu.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'f)
-(require 's)
-(require 'dash)
-(require 'ivy)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Variables
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defgroup ivy-clipmenu nil
-  "Ivy integration for clipmenu."
-  :group 'ivy)
-
-(defcustom ivy-clipmenu-directory
-  (or (getenv "XDG_RUNTIME_DIR")
-      (getenv "TMPDIR")
-      "/tmp")
-  "Base directory for clipmenu's data."
-  :type 'string
-  :group 'ivy-clipmenu)
-
-(defconst ivy-clipmenu-executable-version 5
-   "The major version number for the clipmenu executable.")
-
-(defconst ivy-clipmenu-cache-directory
-  (f-join ivy-clipmenu-directory
-          (format "clipmenu.%s.%s"
-                  ivy-clipmenu-executable-version
-                  (getenv "USER")))
-  "Directory where the clips are stored.")
-
-(defconst ivy-clipmenu-cache-file-pattern
-  (f-join ivy-clipmenu-cache-directory "line_cache_*")
-  "Glob pattern matching the locations on disk for clipmenu's labels.")
-
-(defcustom ivy-clipmenu-history-length
-  (or (getenv "CM_HISTLENGTH") 25)
-  "Limit the number of clips in the history.
-This value defaults to 25.")
-
-(defvar ivy-clipmenu-history nil
-  "History for `ivy-clipmenu-copy'.")
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Functions
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun ivy-clipmenu-parse-content (x)
-  "Parse the label from the entry, X, in clipmenu's line-cache."
-  (->> (s-split " " x)
-       (-drop 1)
-       (s-join " ")))
-
-(defun ivy-clipmenu-list-clips ()
-  "Return a list of the content of all of the clips."
-  (->> ivy-clipmenu-cache-file-pattern
-       f-glob
-       (-map (lambda (path)
-               (s-split "\n" (f-read path) t)))
-       -flatten
-       (-reject #'s-blank?)
-       (-sort #'string>)
-       (-map #'ivy-clipmenu-parse-content)
-       delete-dups
-       (-take ivy-clipmenu-history-length)))
-
-(defun ivy-clipmenu-checksum (content)
-  "Return the CRC checksum of CONTENT."
-  (s-trim-right
-   (with-temp-buffer
-     (call-process "/bin/bash" nil (current-buffer) nil "-c"
-                   (format "cksum <<<'%s'" content))
-     (buffer-string))))
-
-(defun ivy-clipmenu-line-to-content (line)
-  "Map the chosen LINE from the line cache its content from disk."
-  (->> line
-       ivy-clipmenu-checksum
-       (f-join ivy-clipmenu-cache-directory)
-       f-read))
-
-(defun ivy-clipmenu-do-copy (x)
-  "Copy string, X, to the system clipboard."
-  (kill-new x)
-  (message "[ivy-clipmenu.el] Copied!"))
-
-(defun ivy-clipmenu-copy ()
-  "Use `ivy-read' to select and copy a clip.
-It's recommended to bind this function to a globally available keymap."
-  (interactive)
-  (let ((ivy-sort-functions-alist nil))
-    (ivy-read "Clipmenu: "
-              (ivy-clipmenu-list-clips)
-              :history 'ivy-clipmenu-history
-              :action (lambda (line)
-                        (->> line
-                             ivy-clipmenu-line-to-content
-                             ivy-clipmenu-do-copy)))))
-
-(provide 'ivy-clipmenu)
-;;; ivy-clipmenu.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/keybindings.el b/users/wpcarro/emacs/.emacs.d/wpc/keybindings.el
index c6082ec704..a55bf27330 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/keybindings.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/keybindings.el
@@ -21,23 +21,20 @@
 ;; Dependencies
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(require 'functions)
-(require 'clipboard)
 (require 'screen-brightness)
 (require 'pulse-audio)
 (require 'scrot)
+(require 'ivy)
 (require 'ivy-clipmenu)
 (require 'ivy-helpers)
 (require 'general)
 (require 'exwm)
 (require 'vterm-mgt)
 (require 'buffer)
-(require 'device)
 (require 'fonts)
 (require 'bookmark)
 (require 'tvl)
 (require 'window-manager)
-(require 'wpc-misc)
 
 ;; Note: The following lines must be sorted this way.
 (setq evil-want-integration t)
@@ -49,6 +46,43 @@
 (require 'evil-surround)
 (require 'key-chord)
 (require 'edebug)
+(require 'avy)
+(require 'passage)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helper Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun keybindings--window-vsplit-right ()
+  "Split the window vertically and focus the right half."
+  (interactive)
+  (evil-window-vsplit)
+  (windmove-right))
+
+(defun keybindings--window-split-down ()
+  "Split the window horizontal and focus the bottom half."
+  (interactive)
+  (evil-window-split)
+  (windmove-down))
+
+(defun keybindings--create-snippet ()
+  "Create a window split and then opens the Yasnippet editor."
+  (interactive)
+  (evil-window-vsplit)
+  (call-interactively #'yas-new-snippet))
+
+(defun keybindings--replace-under-point ()
+  "Faster than typing %s//thing/g."
+  (interactive)
+  (let ((term (s-replace "/" "\\/" (symbol-to-string (symbol-at-point)))))
+    (save-excursion
+      (evil-ex (concat "%s/\\b" term "\\b/")))))
+
+(defun keybindings--evil-ex-define-cmd-local (cmd f)
+  "Define CMD to F locally to a buffer."
+  (unless (local-variable-p 'evil-ex-commands)
+    (setq-local evil-ex-commands (copy-alist evil-ex-commands)))
+  (evil-ex-define-cmd cmd f))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; General Keybindings
@@ -77,10 +111,10 @@
   "L"   #'evil-end-of-line
   "_"   #'ranger
   "-"   #'dired-jump
-  "sl"  #'functions-evil-window-vsplit-right
+  "sl"  #'keybindings--window-vsplit-right
   "sh"  #'evil-window-vsplit
   "sk"  #'evil-window-split
-  "sj"  #'functions-evil-window-split-down)
+  "sj"  #'keybindings--window-split-down)
 
 (general-nmap
   :keymaps 'override
@@ -121,6 +155,20 @@
         (after advice-for-evil-search-previous activate)
       (evil-scroll-line-to-center (line-number-at-pos)))))
 
+(general-define-key
+ :keymaps '(isearch-mode-map)
+ "C-p" #'isearch-ring-retreat
+ "C-n" #'isearch-ring-advance
+ "<up>" #'isearch-ring-retreat
+ "<down>" #'isearch-ring-advance)
+
+(general-define-key
+ :keymaps '(minibuffer-local-isearch-map)
+ "C-p" #'previous-line-or-history-element
+ "C-n" #'next-line-or-history-element
+ "<up>" #'previous-line-or-history-element
+ "<down>" #'next-line-or-history-element)
+
 (key-chord-mode 1)
 (key-chord-define evil-insert-state-map "jk" 'evil-normal-state)
 
@@ -174,8 +222,14 @@
 (keybindings-exwm "<M-tab>" #'window-manager-next-workspace)
 (keybindings-exwm "<M-S-iso-lefttab>" #'window-manager-prev-workspace)
 (keybindings-exwm "C-S-f" #'window-manager-toggle-previous)
-(keybindings-exwm "C-M-\\" #'ivy-pass)
-(keybindings-exwm "s-e" #'wpc-misc-copy-emoji)
+(keybindings-exwm "C-M-\\" #'passage-select)
+
+(defun keybindings-copy-emoji ()
+  "Select an emoji from the completing-read menu."
+  (interactive)
+  (clipboard-copy (emojify-completing-read "Copy: ")))
+
+(keybindings-exwm "s-e" #'keybindings-copy-emoji)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Workspaces
@@ -227,11 +281,11 @@
  "gss" #'magit-status
  "gsd" #'tvl-depot-status
  "E" #'refine
- "es" #'functions-create-snippet
+ "es" #'keybindings--create-snippet
  "l" #'linum-mode
  "B" #'magit-blame
  "w" #'save-buffer
- "r" #'functions-evil-replace-under-point
+ "r" #'keybindings--replace-under-point
  "R" #'deadgrep)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -260,7 +314,9 @@
  "<s-backspace>" #'vterm-mgt-rename-buffer
  ;; Without this, typing "+" is effectively no-op. Try for yourself:
  ;; (vterm-send-key "<kp-add>")
- "<kp-add>" "+")
+ "<kp-add>" "+"
+ "M--" #'evil-window-split
+ "M-\\" #'evil-window-vsplit)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; notmuch
@@ -277,19 +333,6 @@
  "<C-tab>" #'notmuch-show-next-thread-show
  "e" #'notmuch-show-archive-message-then-next-or-next-thread)
 
-;; TODO(wpcarro): Consider moving this to a separate module
-(defun keybindings--evil-ex-define-cmd-local (cmd f)
-  "Define CMD to F locally to a buffer."
-  (unless (local-variable-p 'evil-ex-commands)
-    (setq-local evil-ex-commands (copy-alist evil-ex-commands)))
-  (evil-ex-define-cmd cmd f))
-
-;; TODO(wpcarro): Support a macro that can easily define evil-ex commands for a
-;; particular mode.
-;; Consumption:
-;; (evil-ex-for-mode 'notmuch-message-mode
-;;                   "x" #'notmuch-mua-send-and-exit)
-
 (add-hook 'notmuch-message-mode-hook
           (lambda ()
             (keybindings--evil-ex-define-cmd-local "x" #'notmuch-mua-send-and-exit)))
@@ -396,8 +439,57 @@
 ;; bookmarks
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-;; Install KBDs like <SPC>jb to search through my monorepo.
-(bookmark-install-kbds)
+(bookmark-install-kbd
+ (make-bookmark :label "wpcarro"
+                :path (f-join tvl-depot-path "users/wpcarro")
+                :kbd "w"))
+
+(bookmark-install-kbd
+ (make-bookmark :label "depot"
+                :path tvl-depot-path
+                :kbd "d"))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; refine
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(general-define-key
+ :keymaps '(refine-mode-map)
+ :states '(normal)
+ "K" #'refine-delete
+ "q" #'kill-this-buffer)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; avy
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(global-set-key (kbd "C-;") #'avy-goto-char)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; ivy
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; restore the ability to paste in ivy
+(general-define-key
+ :keymaps '(ivy-minibuffer-map)
+ "C-k" #'kill-line
+ "C-u" (lambda () (interactive) (kill-line 0))
+ "C-v" #'clipboard-yank
+ "C-S-v" #'clipboard-yank)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Rust
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(general-define-key
+ :keymaps '(rust-mode-map)
+ :states '(normal)
+ "gd" #'lsp-find-definition
+ "gr" #'lsp-find-references)
+
+(general-define-key
+ :keymaps '(rust-mode-map)
+ "TAB" #'company-indent-or-complete-common)
 
 (provide 'keybindings)
 ;;; keybindings.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/keyboard.el b/users/wpcarro/emacs/.emacs.d/wpc/keyboard.el
index 03fb9e3f35..0ee00e1b84 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/keyboard.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/keyboard.el
@@ -15,9 +15,8 @@
 ;; Dependencies
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(require 'string)
-(require 'number)
 (require 'cl-lib)
+(require 'prelude)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Constants
@@ -46,7 +45,7 @@
 
 (defun keyboard-message (x)
   "Message X in a structured way."
-  (message (string-format "[keyboard.el] %s" x)))
+  (message (format "[keyboard.el] %s" x)))
 
 (cl-defun keyboard-set-key-repeat (&key
                                    (rate keyboard-repeat-rate)
@@ -54,7 +53,7 @@
   "Use xset to set the key-repeat RATE and DELAY."
   (prelude-start-process
    :name "keyboard-set-key-repeat"
-   :command (string-format "xset r rate %s %s" delay rate)))
+   :command (format "xset r rate %s %s" delay rate)))
 
 ;; NOTE: Settings like this are machine-dependent. For instance I only need to
 ;; do this on my laptop and other devices where I don't have access to my split
@@ -76,42 +75,42 @@
 (defun keyboard-inc-repeat-rate ()
   "Increment `keyboard-repeat-rate'."
   (interactive)
-  (setq keyboard-repeat-rate (number-inc keyboard-repeat-rate))
+  (setq keyboard-repeat-rate (1+ keyboard-repeat-rate))
   (keyboard-set-key-repeat :rate keyboard-repeat-rate)
   (keyboard-message
-   (string-format "Rate: %s" keyboard-repeat-rate)))
+   (format "Rate: %s" keyboard-repeat-rate)))
 
 (defun keyboard-dec-repeat-rate ()
   "Decrement `keyboard-repeat-rate'."
   (interactive)
-  (setq keyboard-repeat-rate (number-dec keyboard-repeat-rate))
+  (setq keyboard-repeat-rate (1- keyboard-repeat-rate))
   (keyboard-set-key-repeat :rate keyboard-repeat-rate)
   (keyboard-message
-   (string-format "Rate: %s" keyboard-repeat-rate)))
+   (format "Rate: %s" keyboard-repeat-rate)))
 
 (defun keyboard-inc-repeat-delay ()
   "Increment `keyboard-repeat-delay'."
   (interactive)
-  (setq keyboard-repeat-delay (number-inc keyboard-repeat-delay))
+  (setq keyboard-repeat-delay (1+ keyboard-repeat-delay))
   (keyboard-set-key-repeat :delay keyboard-repeat-delay)
   (keyboard-message
-   (string-format "Delay: %s" keyboard-repeat-delay)))
+   (format "Delay: %s" keyboard-repeat-delay)))
 
 (defun keyboard-dec-repeat-delay ()
   "Decrement `keyboard-repeat-delay'."
   (interactive)
-  (setq keyboard-repeat-delay (number-dec keyboard-repeat-delay))
+  (setq keyboard-repeat-delay (1- keyboard-repeat-delay))
   (keyboard-set-key-repeat :delay keyboard-repeat-delay)
   (keyboard-message
-   (string-format "Delay: %s" keyboard-repeat-delay)))
+   (format "Delay: %s" keyboard-repeat-delay)))
 
 (defun keyboard-print-key-repeat ()
   "Print the currently set values for key repeat."
   (interactive)
   (keyboard-message
-   (string-format "Rate: %s. Delay: %s"
-                  keyboard-repeat-rate
-                  keyboard-repeat-delay)))
+   (format "Rate: %s. Delay: %s"
+           keyboard-repeat-rate
+           keyboard-repeat-delay)))
 
 (defun keyboard-set-preferences ()
   "Reset the keyboard preferences to their default values.
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/laptop-battery.el b/users/wpcarro/emacs/.emacs.d/wpc/laptop-battery.el
deleted file mode 100644
index 80dc96ebeb..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/laptop-battery.el
+++ /dev/null
@@ -1,63 +0,0 @@
-;;; laptop-battery.el --- Display laptop battery information -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; Some wrappers to obtain battery information.
-;;
-;; To troubleshoot battery consumpton look into the CLI `powertop`.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Roadmap
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; TODO: Support functions that work with reporting battery stats.
-;; TODO: low-battery-reporting-threshold
-;; TODO: charged-battery-reporting-threshold
-;; TODO: Format modeline battery information.
-;; TODO: Provide better time information in the modeline.
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'battery)
-(require 'al)
-(require 'maybe)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun laptop-battery-available? ()
-  "Return t if battery information is available."
-  (maybe-some? battery-status-function))
-
-(defun laptop-battery-percentage ()
-  "Return the current percentage of the battery."
-  (->> battery-status-function
-       funcall
-       (al-get 112)))
-
-(defun laptop-battery-print-percentage ()
-  "Return the current percentage of the battery."
-  (interactive)
-  (->> (laptop-battery-percentage)
-       message))
-
-(defun laptop-battery-display ()
-  "Display laptop battery percentage in the modeline."
-  (interactive)
-  (display-battery-mode 1))
-
-(defun laptop-battery-hide ()
-  "Hide laptop battery percentage in the modeline."
-  (interactive)
-  (display-battery-mode -1))
-
-(provide 'laptop-battery)
-;;; laptop-battery.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/list.el b/users/wpcarro/emacs/.emacs.d/wpc/list.el
deleted file mode 100644
index 2f1509eeb4..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/list.el
+++ /dev/null
@@ -1,221 +0,0 @@
-;;; list.el --- Functions for working with lists -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24"))
-
-;;; Commentary:
-;; Since I prefer having the `list-' namespace, I wrote this module to wrap many
-;; of the functions that are defined in the the global namespace in ELisp.  I
-;; sometimes forget the names of these functions, so it's nice for them to be
-;; organized like this.
-;;
-;; Motivation:
-;; Here are some examples of function names that I cannot tolerate:
-;; - `car': Return the first element (i.e. "head") of a linked list
-;; - `cdr': Return the tail of a linked list
-
-;; As are most APIs for standard libraries that I write, this is heavily
-;; influenced by Elixir's standard library.
-;;
-;; Elixir's List library:
-;; - ++/2
-;; - --/2
-;; - hd/1
-;; - tl/1
-;; - in/2
-;; - length/1
-;;
-;; Similar libraries:
-;; - dash.el: Functional library that mimmicks Clojure.  It is consumed herein.
-;; - list-utils.el: Utility library that covers things that dash.el may not
-;;   cover.
-;;   stream.el: Elisp implementation of streams, "implemented as delayed
-;;   evaluation of cons cells."
-
-;; TODO: Consider naming this file linked-list.el.
-
-;; TODO: Support module-like macro that auto-namespaces functions.
-
-;; TODO: Consider wrapping most data structures like linked-lists,
-;; associative-lists, etc in a `cl-defstruct', so that the dispatching by type
-;; can be nominal instead of duck-typing.  I'm not sure if this is a good idea
-;; or not.  If I do this, I should provide isomorphisms to map between idiomatic
-;; ways of working with Elisp data structures and my wrapped variants.
-
-;; TODO: Are function aliases/synonyms even a good idea?  Or do they just
-;; bloat the API unnecessarily?
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; TODO: Move `prelude-assert' elsewhere so that I can require it without
-;; introducing the circular dependency of list.el -> prelude.el -> list.el.
-;;(require 'prelude)
-(require 'dash)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Constants
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defconst list-tests? t
-  "When t, run the test suite.")
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun list-new ()
-  "Return a new, empty list."
-  '())
-
-(defun list-concat (&rest lists)
-  "Joins `LISTS' into on list."
-  (apply #'-concat lists))
-
-(defun list-join (joint xs)
-  "Join a list of strings, XS, with JOINT."
-  (if (list-empty? xs)
-      ""
-    (list-reduce (list-first xs)
-                 (lambda (x acc)
-                   (string-concat acc joint x))
-                 (list-tail xs))))
-
-(defun list-length (xs)
-  "Return the number of elements in `XS'."
-  (length xs))
-
-(defun list-get (i xs)
-  "Return the value in `XS' at `I', or nil."
-  (nth i xs))
-
-(defun list-head (xs)
-  "Return the head of `XS'."
-  (car xs))
-
-;; TODO: Learn how to write proper function aliases.
-(defun list-first (xs)
-  "Alias for `list-head' for `XS'."
-  (list-head xs))
-
-(defun list-tail (xs)
-  "Return the tail of `XS'."
-  (cdr xs))
-
-(defun list-reverse (xs)
-  "Reverses `XS'."
-  (reverse xs))
-
-(defun list-cons (x xs)
-  "Add `X' to the head of `XS'."
-  (cons x xs))
-
-;; map, filter, reduce
-
-;; TODO: Create function adapters like swap.
-;; (defun adapter/swap (f)
-;;   "Return a new function that wraps `F' and swaps the arguments."
-;;   (lambda (a b)
-;;     (funcall f b a)))
-
-;; TODO: Make this function work.
-(defun list-reduce (acc f xs)
-  "Return over `XS' calling `F' on an element in `XS'and `ACC'."
-  (-reduce-from (lambda (acc x) (funcall f x acc)) acc xs))
-
-(defun list-map (f xs)
-  "Call `F' on each element of `XS'."
-  (-map f xs))
-
-(defun list-map-indexed (f xs)
-  "Call `F' on each element of `XS' along with its index."
-  (-map-indexed (lambda (i x) (funcall f x i)) xs))
-
-(defun list-filter (p xs)
-  "Return a subset of XS where predicate P returned t."
-  (list-reverse
-   (list-reduce
-    '()
-    (lambda (x acc)
-      (if (funcall p x)
-          (list-cons x acc)
-        acc))
-    xs)))
-
-(defun list-reject (p xs)
-  "Return a subset of XS where predicate of P return nil."
-  (list-filter (lambda (x) (not (funcall p x))) xs))
-
-(defun list-find (p xs)
-  "Return the first x in XS that passes P or nil."
-  (-find p xs))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Predicates
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun list-instance? (xs)
-  "Return t if `XS' is a list.
-Be leery of using this with things like alists.  Many data structures in Elisp
-  are implemented using linked lists."
-  (listp xs))
-
-(defun list-empty? (xs)
-  "Return t if XS are empty."
-  (= 0 (list-length xs)))
-
-(defun list-all? (p xs)
-  "Return t if all `XS' pass the predicate, `P'."
-  (-all? p xs))
-
-(defun list-any? (p xs)
-  "Return t if any `XS' pass the predicate, `P'."
-  (-any? p xs))
-
-(defun list-contains? (x xs)
-  "Return t if X is in XS using `equal'."
-  (-contains? xs x))
-
-(defun list-xs-distinct-by? (f xs)
-  "Return t if all elements in XS are distinct after applying F to each."
-  (= (length xs)
-     (->> xs (-map f) set-from-list set-count)))
-
-;; TODO: Support dedupe.
-;; TODO: Should we call this unique? Or distinct?
-
-;; TODO: Add tests.
-(defun list-dedupe-adjacent (xs)
-  "Return XS without adjacent duplicates."
-  (prelude-assert (not (list-empty? xs)))
-  (list-reduce (list (list-first xs))
-    (lambda (x acc)
-      (if (equal x (list-first acc))
-          acc
-        (list-cons x acc)))
-    xs))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; (when list-tests?
-;;   (prelude-assert
-;;    (= 0
-;;       (list-length '())))
-;;   (prelude-assert
-;;    (= 5
-;;       (list-length '(1 2 3 4 5))))
-;;   (prelude-assert
-;;    (= 16
-;;       (list-reduce 1 (lambda (x acc) (+ x acc)) '(1 2 3 4 5))))
-;;   (prelude-assert
-;;    (equal '(2 4 6 8 10)
-;;           (list-map (lambda (x) (* x 2)) '(1 2 3 4 5)))))
-
-(provide 'list)
-;;; list.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/macros.el b/users/wpcarro/emacs/.emacs.d/wpc/macros.el
deleted file mode 100644
index 32c9b59dcd..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/macros.el
+++ /dev/null
@@ -1,63 +0,0 @@
-;;; macros.el --- Helpful variables for making my ELisp life more enjoyable -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24"))
-
-;;; Commentary:
-;; This file contains helpful variables that I use in my ELisp development.
-
-;; TODO: Consider a macro solution for mimmicking OCaml's auto resolution of
-;; dependencies using `load-path' and friends.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'f)
-(require 'string)
-(require 'symbol)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defmacro macros-enable (mode)
-  "Helper for enabling `MODE'.
-Useful in `add-hook' calls.  Some modes, like `linum-mode' need to be called as
-`(linum-mode 1)', so `(add-hook mode #'linum-mode)' won't work."
-  `#'(lambda nil (,mode 1)))
-
-(defmacro macros-disable (mode)
-  "Helper for disabling `MODE'.
-Useful in `add-hook' calls."
-  `#'(lambda nil (,mode -1)))
-
-(defmacro macros-add-hook-before-save (mode f)
-  "Register a hook, `F', for a mode, `MODE' more conveniently.
-Usage: (macros-add-hook-before-save 'reason-mode-hook #'refmt-before-save)"
-  `(add-hook ,mode
-             (lambda ()
-               (add-hook 'before-save-hook ,f))))
-
-;; TODO: Privatize?
-(defun macros--namespace ()
-  "Return the namespace for a function based on the filename."
-  (->> (buffer-file-name)
-       f-filename
-       f-base))
-
-(defmacro macros-comment (&rest _)
-  "Empty comment s-expresion where `BODY' is ignored."
-  `nil)
-
-(defmacro macros-support-file-extension (ext mode)
-  "Register MODE to automatically load with files ending with EXT extension.
-Usage: (macros-support-file-extension \"pb\" protobuf-mode)"
-  (let ((extension (string-format "\\.%s\\'" ext)))
-    `(add-to-list 'auto-mode-alist '(,extension . ,mode))))
-
-(provide 'macros)
-;;; macros.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/maybe.el b/users/wpcarro/emacs/.emacs.d/wpc/maybe.el
deleted file mode 100644
index ef92e5a4c1..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/maybe.el
+++ /dev/null
@@ -1,78 +0,0 @@
-;;; maybe.el --- Library for dealing with nil values -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24"))
-
-;;; Commentary:
-;; Inspired by Elm's Maybe library.
-;;
-;; For now, a Nothing value will be defined exclusively as a nil value.  I'm
-;; uninterested in supported falsiness in this module even at risk of going
-;; against the LISP grain.
-;;
-;; I'm avoiding introducing a struct to handle the creation of Just and Nothing
-;; variants of Maybe.  Perhaps this is a mistake in which case this file would
-;; be more aptly named nil.el.  I may change that.  Because of this limitation,
-;; functions in Elm's Maybe library like andThen, which is the monadic bind for
-;; the Maybe type, doesn't have a home here since we cannot compose multiple
-;; Nothing or Just values without a struct or some other construct.
-;;
-;; Possible names for the variants of a Maybe.
-;; None    | Some
-;; Nothing | Something
-;; None    | Just
-;; Nil     | Set
-;;
-;; NOTE: In Elisp, values like '() (i.e. the empty list) are aliases for nil.
-;; What else in Elisp is an alias in this way?
-;; Examples:
-;; TODO: Provide examples of other nil types in Elisp.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'list)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Constants
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defvar maybe--run-tests? t
-  "When t, run the test suite defined herein.")
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun maybe-nil? (x)
-  "Return t if X is nil."
-  (eq nil x))
-
-(defun maybe-some? (x)
-  "Return t when X is non-nil."
-  (not (maybe-nil? x)))
-
-(defun maybe-nils? (&rest xs)
-  "Return t if all XS are nil."
-  (list-all? #'maybe-nil? xs))
-
-(defun maybe-somes? (&rest xs)
-  "Return t if all XS are non-nil."
-  (list-all? #'maybe-some? xs))
-
-(defun maybe-default (default x)
-  "Return DEFAULT when X is nil."
-  (if (maybe-nil? x) default x))
-
-(defun maybe-map (f x)
-  "Apply F to X if X is not nil."
-  (if (maybe-some? x)
-      (funcall f x)
-    x))
-
-(provide 'maybe)
-;;; maybe.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/number.el b/users/wpcarro/emacs/.emacs.d/wpc/number.el
deleted file mode 100644
index c8ed665b30..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/number.el
+++ /dev/null
@@ -1,142 +0,0 @@
-;;; number.el --- Functions for working with numbers -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24"))
-;; Homepage: https://user.git.corp.google.com/wpcarro/briefcase
-
-;;; Commentary:
-;;
-;; Classifications of numbers:
-;; - Natural: (a.k.a positive integers, counting numbers); {1, 2, 3, ... }
-;;
-;; - Whole: Natural Numbers, plus zero; {0, 1, 2, 3, ...}
-;;
-;; - Integers: Whole numbers plus all the negatives of the natural numbers;
-;;   {... , -2, -1, 0, 1, 2, ...}
-;;
-;; - Rational numbers: (a.k.a. fractions) where the top and bottom numbers are
-;;   integers; e.g., 1/2, 3/4, 7/2, ⁻4/3, 4/1.  Note: The denominator cannot be
-;;   0, but the numerator can be.
-;;
-;; - Real numbers: All numbers that can be written as a decimal.  This includes
-;;   fractions written in decimal form e.g., 0.5, 0.75 2.35, ⁻0.073, 0.3333, or
-;;   2.142857. It also includes all the irrational numbers such as Ο€, √2 etc.
-;;   Every real number corresponds to a point on the number line.
-;;
-;; The functions defined herein attempt to capture the mathematical definitions
-;; of numbers and their classifications as defined above.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'prelude)
-(require 'dash)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defconst number-test? t
-  "When t, run the test suite defined herein.")
-
-;; TODO: What about int.el?
-
-;; TODO: How do we handle a number typeclass?
-
-(defun number-positive? (x)
-  "Return t if `X' is a positive number."
-  (> x 0))
-
-(defun number-negative? (x)
-  "Return t if `X' is a positive number."
-  (< x 0))
-
-;; TODO: Don't rely on this. Need to have 10.0 and 10 behave similarly.
-(defun number-float? (x)
-  "Return t if `X' is a floating point number."
-  (floatp x))
-
-(defun number-natural? (x)
-  "Return t if `X' is a natural number."
-  (and (number-positive? x)
-       (not (number-float? x))))
-
-(defun number-whole? (x)
-  "Return t if `X' is a whole number."
-  (or (= 0 x)
-      (number-natural? x)))
-
-(defun number-integer? (x)
-  "Return t if `X' is an integer."
-  (or (number-whole? x)
-      (number-natural? (- x))))
-
-;; TODO: How defensive should these guards be?  Should we assert that the inputs
-;; are integers before checking evenness or oddness?
-
-;; TODO: Look up Runar (from Unison) definition of handling zero as even or odd.
-
-;; TODO: How should rational numbers be handled? Lisp is supposedly famous for
-;; its handling of rational numbers.
-;; TODO: `calc-mode' supports rational numbers as "1:2" meaning "1/2"
-;; (defun number-rational? (x))
-
-;; TODO: Can or should I support real numbers?
-;; (defun number-real? (x))
-
-(defun number-even? (x)
-  "Return t if `X' is an even number."
-  (or (= 0 x)
-      (= 0 (mod x 2))))
-
-(defun number-odd? (x)
-  "Return t if `X' is an odd number."
-  (not (number-even? x)))
-
-(defun number-dec (x)
-  "Subtract one from `X'.
-While this function is undeniably trivial, I have unintentionally done (- 1 x)
-  when in fact I meant to do (- x 1) that I figure it's better for this function
-  to exist, and for me to train myself to reach for it and its inc counterpart."
-  (- x 1))
-
-(defun number-inc (x)
-  "Add one to `X'."
-  (+ x 1))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(when number-test?
-  (prelude-assert
-   (number-positive? 10))
-  (prelude-assert
-   (number-natural? 10))
-  (prelude-assert
-   (number-whole? 10))
-  (prelude-assert
-   (number-whole? 0))
-  (prelude-assert
-   (number-integer? 10))
-  ;; (prelude-assert
-  ;;  (= 120 (number-factorial 5)))
-  (prelude-assert
-   (number-even? 6))
-  (prelude-refute
-   (number-odd? 6))
-  (prelude-refute
-   (number-positive? -10))
-  (prelude-refute
-   (number-natural? 10.0))
-  (prelude-refute
-   (number-natural? -10))
-  (prelude-refute
-   (number-natural? -10.0)))
-
-(provide 'number)
-;;; number.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/random.el b/users/wpcarro/emacs/.emacs.d/wpc/random.el
deleted file mode 100644
index dfe10b6d47..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/random.el
+++ /dev/null
@@ -1,80 +0,0 @@
-;;; random.el --- Functions for working with randomness -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24"))
-
-;;; Commentary:
-;; Functions for working with randomness.  Some of this code is not as
-;; functional as I'd like from.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'prelude)
-(require 'number)
-(require 'math)
-(require 'series)
-(require 'list)
-(require 'set)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun random-int (x)
-  "Return a random integer from 0 to `X'."
-  (random x))
-
-;; TODO: Make this work with sequences instead of lists.
-(defun random-choice (xs)
-  "Return a random element of `XS'."
-  (let ((ct (list-length xs)))
-    (list-get
-     (random-int ct)
-     xs)))
-
-(defun random-boolean? ()
-  "Randonly return t or nil."
-  (random-choice (list t nil)))
-
-;; TODO: This may not work if any of these generate numbers like 0, 1, etc.
-(defun random-uuid ()
-  "Return a generated UUID string."
-  (let ((eight  (number-dec (math-triangle-of-power :base 16 :power 8)))
-        (four   (number-dec (math-triangle-of-power :base 16 :power 4)))
-        (twelve (number-dec (math-triangle-of-power :base 16 :power 12))))
-    (format "%x-%x-%x-%x-%x"
-            (random-int eight)
-            (random-int four)
-            (random-int four)
-            (random-int four)
-            (random-int twelve))))
-
-(defun random-token (length)
-  "Return a randomly generated hexadecimal string of LENGTH."
-  (->> (series/range 0 (number-dec length))
-       (list-map (lambda (_) (format "%x" (random-int 15))))
-       (list-join "")))
-
-;; TODO: Support random-sample
-;; (defun random-sample (n xs)
-;;   "Return a randomly sample of list XS of size N."
-;;   (prelude-assert (and (>= n 0) (< n (list-length xs))))
-;;   (cl-labels ((do-sample
-;;                (n xs y ys)
-;;                (if (= n (set-count ys))
-;;                    (->> ys
-;;                         set-to-list
-;;                         (list-map (lambda (i)
-;;                                     (list-get i xs))))
-;;                  (if (set-contains? y ys)
-;;                      (do-sample n xs (random-int (list-length xs)) ys)
-;;                    (do-sample n xs y (set-add y ys))))))
-;;     (do-sample n xs (random-int (list-length xs)) (set-new))))
-
-(provide 'random)
-;;; random.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/scope.el b/users/wpcarro/emacs/.emacs.d/wpc/scope.el
deleted file mode 100644
index 99cdbd2b5e..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/scope.el
+++ /dev/null
@@ -1,106 +0,0 @@
-;;; scope.el --- Work with a scope data structure -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; Exposing an API for working with a scope data structure in a non-mutative
-;; way.
-;;
-;; What's a scope?  Think of a scope as a stack of key-value bindings.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'al)
-(require 'stack)
-(require 'struct)
-(require '>)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Create
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(cl-defstruct scope scopes)
-
-(defun scope-new ()
-  "Return an empty scope."
-  (make-scope :scopes (->> (stack-new)
-                           (stack-push (al-new)))))
-
-(defun scope-flatten (xs)
-  "Return a flattened representation of the scope, XS.
-The newest bindings eclipse the oldest."
-  (->> xs
-       scope-scopes
-       stack-to-list
-       (list-reduce (al-new)
-                    (lambda (scope acc)
-                      (al-merge acc scope)))))
-
-(defun scope-push-new (xs)
-  "Push a new, empty scope onto XS."
-  (struct-update scope
-                 scopes
-                 (>-> (stack-push (al-new)))
-                 xs))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Read
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun scope-get (k xs)
-  "Return K from XS if it's in scope."
-  (->> xs
-       scope-flatten
-       (al-get k)))
-
-(defun scope-current (xs)
-  "Return the newest scope from XS."
-  (let ((xs-copy (copy-scope xs)))
-    (->> xs-copy
-         scope-scopes
-         stack-peek)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Update
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun scope-set (k v xs)
-  "Set value, V, at key, K, in XS for the current scope."
-  (struct-update scope
-                 scopes
-                 (>-> (stack-map-top (>-> (al-set k v))))
-                 xs))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Delete
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun scope-pop (xs)
-  "Return a new scope without the top element from XS."
-  (->> xs
-       scope-scopes
-       stack-pop))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Predicates
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun scope-defined? (k xs)
-  "Return t if K is in scope of XS."
-  (->> xs
-       scope-flatten
-       (al-has-key? k)))
-
-;; TODO: Find a faster way to write aliases like this.
-(defun scope-instance? (xs)
-  "Return t if XS is a scope struct."
-  (scope-p xs))
-
-(provide 'scope)
-;;; scope.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/sequence.el b/users/wpcarro/emacs/.emacs.d/wpc/sequence.el
deleted file mode 100644
index 204a72c5b0..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/sequence.el
+++ /dev/null
@@ -1,108 +0,0 @@
-;;; sequence.el --- Working with the "sequence" types -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; Elisp supports a typeclass none as "sequence" which covers the following
-;; types:
-;; - list: '(1 2 3 4 5)
-;; - vector: ["John" 27 :blue]
-;; - string: "To be or not to be..."
-
-;; TODO: Document the difference between a "reduce" and a "fold".  I.e. - reduce
-;; has an initial value whereas fold uses the first element in the sequence as
-;; the initial value.
-;;
-;; Note: This should be an approximation of Elixir's Enum protocol albeit
-;; without streams.
-;;
-;; Elisp has done a lot of this work already and these are mostly wrapper
-;; functions.
-;; See the following list for reference:
-;; - sequencep
-;; - elt
-;; - copy-sequence
-;; - reverse
-;; - nreverse
-;; - sort
-;; - seq-elt
-;; - seq-length
-;; - seqp
-;; - seq-drop
-;; - seq-take
-;; - seq-take-while
-;; - seq-drop-while
-;; - seq-do
-;; - seq-map
-;; - seq-mapn
-;; - seq-filter
-;; - seq-remove
-;; - seq-reduce
-;; - seq-some
-;; - seq-find
-;; - seq-every-p
-;; - seq-empty-p
-;; - seq-count
-;; - seq-sort
-;; - seq-contains
-;; - seq-position
-;; - seq-uniq
-;; - seq-subseq
-;; - seq-concatenate
-;; - seq-mapcat
-;; - seq-partition
-;; - seq-intersection
-;; - seq-difference
-;; - seq-group-by
-;; - seq-into
-;; - seq-min
-;; - seq-max
-;; - seq-doseq
-;; - seq-let
-
-;;; Code:
-
-;; Perhaps we can provide default implementations for `filter' and `map' derived
-;; from the `reduce' implementation.
-;; (defprotocol sequence
-;;   :functions (reduce))
-;; (definstance sequence list
-;;   :reduce #'list-reduce
-;;   :filter #'list-filter
-;;   :map    #'list-map)
-;; (definstance sequence vector
-;;   :reduce #'vector/reduce)
-;; (definstance sequence string
-;;   :reduce #'string)
-
-(defun sequence-classify (xs)
-  "Return the type of `XS'."
-  (cond
-   ((listp xs) 'list)
-   ((vectorp xs) 'vector)
-   ((stringp xs) 'string)))
-
-(defun sequence-reduce (acc f xs)
-  "Reduce of `XS' calling `F' on x and `ACC'."
-  (seq-reduce
-   (lambda (acc x)
-     (funcall f x acc))
-   xs
-   acc))
-
-;; Elixir also turned everything into a list for efficiecy reasons.
-
-(defun sequence-filter (p xs)
-  "Filter `XS' with predicate, `P'.
-Returns a list regardless of the type of `XS'."
-  (seq-filter p xs))
-
-(defun sequence-map (f xs)
-  "Maps `XS' calling `F' on each element.
-Returns a list regardless of the type of `XS'."
-  (seq-map f xs))
-
-(provide 'sequence)
-;;; sequence.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/series.el b/users/wpcarro/emacs/.emacs.d/wpc/series.el
deleted file mode 100644
index d890038839..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/series.el
+++ /dev/null
@@ -1,92 +0,0 @@
-;;; series.el --- Hosting common series of numbers -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24"))
-
-;;; Commentary:
-;; Encoding number series as I learn about them.
-;;
-;; These are the following series I'm interested in supporting:
-;; - Fibonacci
-;; - Catalan numbers
-;; - Figurate number series
-;;   - Triangular
-;;   - Square
-;;   - Pentagonal
-;;   - Hexagonal
-;;   - Lazy-caterer
-;; - Magic square
-;; - Look-and-say
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'number)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun series-range (beg end)
-  "Create a list of numbers from `BEG' to `END'.
-This is an inclusive number range."
-  (if (< end beg)
-      (list-reverse
-       (number-sequence end beg))
-    (number-sequence beg end)))
-
-(defun series-fibonacci-number (i)
-  "Return the number in the fibonacci series at `I'."
-  (cond
-   ((= 0 i) 0)
-   ((= 1 i) 1)
-   (t (+ (series-fibonacci-number (- i 1))
-         (series-fibonacci-number (- i 2))))))
-
-(defun series-fibonacci (n)
-  "Return the first `N' numbers of the fibonaccci series starting at zero."
-  (if (= 0 n)
-      '()
-    (list-reverse
-     (list-cons (series-fibonacci-number (number-dec n))
-                (list-reverse
-                 (series-fibonacci (number-dec n)))))))
-
-;; TODO: Consider memoization.
-(defun series-triangular-number (i)
-  "Return the number in the triangular series at `I'."
-  (if (= 0 i)
-      0
-    (+ i (series-triangular-number (number-dec i)))))
-
-;; TODO: Improve performance.
-;; TODO: Consider creating a stream protocol with `stream/next' and implement
-;; this using that.
-(defun series-triangular (n)
-  "Return the first `N' numbers of a triangular series starting at 0."
-  (if (= 0 n)
-      '()
-    (list-reverse
-     (list-cons (series-triangular-number (number-dec n))
-                (list-reverse
-                 (series-triangular (number-dec n)))))))
-
-(defun series-catalan-number (i)
-  "Return the catalan number in the series at `I'."
-  (if (= 0 i)
-      1
-    (/ (number-factorial (* 2 i))
-       (* (number-factorial (number-inc i))
-          (number-factorial i)))))
-
-(defun series-catalan (n)
-  "Return the first `N' numbers in a catalan series."
-  (->> (series-range 0 (number-dec n))
-       (list-map #'series-catalan-number)))
-
-(provide 'series)
-;;; series.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/set.el b/users/wpcarro/emacs/.emacs.d/wpc/set.el
deleted file mode 100644
index 778b089e15..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/set.el
+++ /dev/null
@@ -1,174 +0,0 @@
-;;; set.el --- Working with mathematical sets -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.3"))
-
-;;; Commentary:
-;; The set data structure is a collection that deduplicates its elements.
-
-;;; Code:
-
-(require 'ht) ;; friendlier API for hash-tables
-(require 'dotted)
-(require 'struct)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Wish List
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; - TODO: Support enum protocol for set.
-;; - TODO: Prefer a different hash-table library that doesn't rely on mutative
-;;   code.
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(cl-defstruct set xs)
-
-(defconst set-enable-testing? t
-  "Run tests when t.")
-
-(defun set-from-list (xs)
-  "Create a new set from the list XS."
-  (make-set :xs (->> xs
-                     (list-map #'dotted-new)
-                     ht-from-alist)))
-
-(defun set-new (&rest args)
-  "Create a new set from ARGS."
-  (set-from-list args))
-
-(defun set-to-list (xs)
-  "Map set XS into a list."
-  (->> xs
-       set-xs
-       ht-keys))
-
-(defun set-add (x xs)
-  "Add X to set XS."
-  (struct-update set
-                 xs
-                 (lambda (table)
-                   (let ((table-copy (ht-copy table)))
-                     (ht-set table-copy x nil)
-                     table-copy))
-                 xs))
-
-;; TODO: Ensure all `*/reduce' functions share the same API.
-(defun set-reduce (acc f xs)
-  "Return a new set by calling F on each element of XS and ACC."
-  (->> xs
-       set-to-list
-       (list-reduce acc f)))
-
-(defun set-intersection (a b)
-  "Return the set intersection between A and B."
-  (set-reduce (set-new)
-              (lambda (x acc)
-                (if (set-contains? x b)
-                    (set-add x acc)
-                  acc))
-              a))
-
-(defun set-count (xs)
-  "Return the number of elements in XS."
-  (->> xs
-       set-xs
-       ht-size))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Predicates
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun set-empty? (xs)
-  "Return t if XS has no elements in it."
-  (= 0 (set-count xs)))
-
-(defun set-contains? (x xs)
-  "Return t if set XS has X."
-  (ht-contains? (set-xs xs) x))
-
-;; TODO: Prefer using `ht.el' functions for this.
-(defun set-equal? (a b)
-  "Return t if A and B share the name members."
-  (ht-equal? (set-xs a)
-             (set-xs b)))
-
-(defun set-distinct? (a b)
-  "Return t if A and B have no shared members."
-  (set-empty? (set-intersection a b)))
-
-(defun set-superset? (a b)
-  "Return t if A has all of the members of B."
-  (->> b
-       set-to-list
-       (list-all? (lambda (x) (set-contains? x a)))))
-
-(defun set-subset? (a b)
-  "Return t if each member of set A is present in set B."
-  (set-superset? b a))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(when set-enable-testing?
-  ;; set-distinct?
-  (prelude-assert
-   (set-distinct? (set-new 'one 'two 'three)
-                  (set-new 'a 'b 'c)))
-  (prelude-refute
-   (set-distinct? (set-new 1 2 3)
-                  (set-new 3 4 5)))
-  (prelude-refute
-   (set-distinct? (set-new 1 2 3)
-                  (set-new 1 2 3)))
-  ;; set-equal?
-  (prelude-refute
-   (set-equal? (set-new 'a 'b 'c)
-               (set-new 'x 'y 'z)))
-  (prelude-refute
-   (set-equal? (set-new 'a 'b 'c)
-               (set-new 'a 'b)))
-  (prelude-assert
-   (set-equal? (set-new 'a 'b 'c)
-               (set-new 'a 'b 'c)))
-  ;; set-intersection
-  (prelude-assert
-   (set-equal? (set-new 2 3)
-               (set-intersection (set-new 1 2 3)
-                                 (set-new 2 3 4))))
-  ;; set-{from,to}-list
-  (prelude-assert (equal '(1 2 3)
-                         (->> '(1 1 2 2 3 3)
-                              set-from-list
-                              set-to-list)))
-  (let ((primary-colors (set-new "red" "green" "blue")))
-    ;; set-subset?
-    (prelude-refute
-     (set-subset? (set-new "black" "grey")
-                  primary-colors))
-    (prelude-assert
-     (set-subset? (set-new "red")
-                  primary-colors))
-    ;; set-superset?
-    (prelude-refute
-     (set-superset? primary-colors
-                    (set-new "black" "grey")))
-    (prelude-assert
-     (set-superset? primary-colors
-                    (set-new "red" "green" "blue")))
-    (prelude-assert
-     (set-superset? primary-colors
-                    (set-new "red" "blue"))))
-  ;; set-empty?
-  (prelude-assert (set-empty? (set-new)))
-  (prelude-refute (set-empty? (set-new 1 2 3)))
-  ;; set-count
-  (prelude-assert (= 0 (set-count (set-new))))
-  (prelude-assert (= 2 (set-count (set-new 1 1 2 2)))))
-
-(provide 'set)
-;;; set.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/ssh.el b/users/wpcarro/emacs/.emacs.d/wpc/ssh.el
index eb7debc492..1179e90363 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/ssh.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/ssh.el
@@ -41,7 +41,9 @@
 ;; enables ControlMaster.
 (setq tramp-use-ssh-controlmaster-options nil)
 
-(defcustom ssh-hosts '("wpcarro@wpcarro.dev")
+(defcustom ssh-hosts '("wpcarro@wpcarro.dev"
+                       "foundation"
+                       "edge")
   "List of hosts to which I commonly connect.")
 
 (defun ssh-sudo-buffer ()
@@ -49,7 +51,10 @@
   (interactive)
   (with-current-buffer (current-buffer)
     (if (s-starts-with? "/ssh:" buffer-file-name)
-        (message "[ssh.el] calling ssh-sudo-buffer for remote files isn't currently supported")
+        (pcase (s-split ":" buffer-file-name)
+          (`(,one ,two ,three) (find-file (format "/ssh:%s|sudo:%s:%s" two two three))))
+        (find-file
+         (s-join ":" (-insert-at 2 "|sudo" (s-split ":" buffer-file-name))))
       (find-file (format "/sudo::%s" buffer-file-name)))))
 
 (defun ssh-cd-home ()
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/stack.el b/users/wpcarro/emacs/.emacs.d/wpc/stack.el
deleted file mode 100644
index 3d1e3e4a16..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/stack.el
+++ /dev/null
@@ -1,101 +0,0 @@
-;;; stack.el --- Working with stacks in Elisp -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; A stack is a LIFO queue.
-;; The design goal here is to expose an intuitive API for working with stacks in
-;; non-mutative way.
-;;
-;; TODO: Consider naming a Functor instance "Mappable."
-;; TODO: Consider naming a Foldable instance "Reduceable."
-;;
-;; TODO: Consider implementing an instance for Mappable.
-;; TODO: Consider implementing an instance for Reduceable.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'list)
-(require '>)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Create
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(cl-defstruct stack xs)
-
-(defun stack-new ()
-  "Create an empty stack."
-  (make-stack :xs '()))
-
-(defun stack-from-list (xs)
-  "Create a new stack from the list, `XS'."
-  (list-reduce (stack-new) #'stack-push xs))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Read
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun stack-peek (xs)
-  "Look at the top element of `XS' without popping it off."
-  (->> xs
-       stack-xs
-       list-head))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Update
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun stack-push (x xs)
-  "Push `X' on `XS'."
-  (struct-update stack
-                 xs
-                 (>-> (list-cons x))
-                 xs))
-
-;; TODO: How to return something like {(list-head xs), (list-tail xs)} in Elixir
-;; TODO: How to handle popping from empty stacks?
-(defun stack-pop (xs)
-  "Return the stack, `XS', without the top element.
-Since I cannot figure out a nice way of return tuples in Elisp, if you want to
-look at the first element, use `stack-peek' before running `stack-pop'."
-  (struct-update stack
-                 xs
-                 (>-> list-tail)
-                 xs))
-
-(defun stack-map-top (f xs)
-  "Apply F to the top element of XS."
-  (->> xs
-       stack-pop
-       (stack-push (funcall f (stack-peek xs)))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Miscellaneous
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun stack-to-list (xs)
-  "Return XS as a list.
-The round-trip property of `stack-from-list' and `stack-to-list' should hold."
-  (->> xs
-       stack-xs
-       list-reverse))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Predicates
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; TODO: Create a macro that wraps `cl-defstruct' that automatically creates
-;; things like `new', `instance?'.
-(defun stack-instance? (xs)
-  "Return t if XS is a stack."
-  (stack-p xs))
-
-(provide 'stack)
-;;; stack.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/struct.el b/users/wpcarro/emacs/.emacs.d/wpc/struct.el
deleted file mode 100644
index eeea04bf26..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/struct.el
+++ /dev/null
@@ -1,85 +0,0 @@
-;;; struct.el --- Helpers for working with structs -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24.3"))
-
-;;; Commentary:
-;; Provides new macros for working with structs.  Also provides adapter
-;; interfaces to existing struct macros, that should have more intuitive
-;; interfaces.
-;;
-;; Sometimes `setf' just isn't enough.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'string)
-(require 'dash)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defvar struct--enable-tests? t
-  "When t, run the test suite defined herein.")
-
-(defmacro struct-update (type field f xs)
-  "Apply F to FIELD in XS, which is a struct of TYPE.
-This is immutable."
-  (let ((copier (->> type
-                     symbol-name
-                     (string-prepend "copy-")
-                     intern))
-        (accessor (->> field
-                       symbol-name
-                       (string-prepend (string-concat (symbol-name type) "-"))
-                       intern)))
-    `(let ((copy (,copier ,xs)))
-       (setf (,accessor copy) (funcall ,f (,accessor copy)))
-       copy)))
-
-(defmacro struct-set (type field x xs)
-  "Immutably set FIELD in XS (struct TYPE) to X."
-  (let ((copier (->> type
-                     symbol-name
-                     (string-prepend "copy-")
-                     intern))
-        (accessor (->> field
-                       symbol-name
-                       (string-prepend (string-concat (symbol-name type) "-"))
-                       intern)))
-    `(let ((copy (,copier ,xs)))
-       (setf (,accessor copy) ,x)
-       copy)))
-
-(defmacro struct-set! (type field x xs)
-  "Set FIELD in XS (struct TYPE) to X mutably.
-This is an adapter interface to `setf'."
-  (let ((accessor (->> field
-                       symbol-name
-                       (string-prepend (string-concat (symbol-name type) "-"))
-                       intern)))
-    `(progn
-       (setf (,accessor ,xs) ,x)
-       ,xs)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(when struct--enable-tests?
-  (cl-defstruct dummy name age)
-  (defvar struct--test-dummy (make-dummy :name "Roofus" :age 19))
-  (struct-set! dummy name "Doofus" struct--test-dummy)
-  (prelude-assert (string= "Doofus" (dummy-name struct--test-dummy)))
-  (let ((result (struct-set dummy name "Shoofus" struct--test-dummy)))
-    ;; Test the immutability of `struct-set'
-    (prelude-assert (string= "Doofus" (dummy-name struct--test-dummy)))
-    (prelude-assert (string= "Shoofus" (dummy-name result)))))
-
-(provide 'struct)
-;;; struct.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/symbol.el b/users/wpcarro/emacs/.emacs.d/wpc/symbol.el
deleted file mode 100644
index 79d665fa20..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/symbol.el
+++ /dev/null
@@ -1,48 +0,0 @@
-;;; symbol.el --- Library for working with symbols -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "24"))
-
-;;; Commentary:
-;; Library for working with symbols.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'string)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; Symbols
-(defun symbol-as-string (callback x)
-  "Treat the symbol, X, as a string while applying CALLBACK to it.
-Coerce back to a symbol on the way out."
-  (->> x
-       #'symbol-name
-       callback
-       #'intern))
-
-(defun symbol-to-string (x)
-  "Map `X' into a string."
-  (string-<-symbol x))
-
-(defun symbol-hookify (x)
-  "Append \"-hook\" to X when X is a symbol."
-  (symbol-as-string #'string-hookify x))
-
-(defun symbol-ensure-hookified (x)
-  "Ensure that X has \"-hook\" appended to it when X is a symbol."
-  (symbol-as-string #'string-ensure-hookified x))
-
-(defun symbol-instance? (x)
-  "Return t if X is a symbol."
-  (symbolp x))
-
-(provide 'symbol)
-;;; symbol.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/timestring.el b/users/wpcarro/emacs/.emacs.d/wpc/timestring.el
deleted file mode 100644
index 245ace49e7..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/timestring.el
+++ /dev/null
@@ -1,77 +0,0 @@
-;;; timestring.el --- Quickly access timestamps in different formats -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-
-;; I was making some API calls where a URL needed a `since` parameter that of an
-;; RFC 3339 encoded string.
-;;
-;; Because I didn't know what a RFC 3339 encoded
-;; string was at the time, and because I didn't know what its format was
-;; according to strftime, and because I'm most likely to forget both of these
-;; things by the next time that I need something similar, I decided to write
-;; this package so that I can accumulate a list of common time encodings.
-;;
-;; Thank you, Emacs.
-;;
-;; p.s. - I may turn this into a proper module and publish it.  But not today.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'ts)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defgroup timestring nil
-  "Customize group for timestring configuration.")
-
-(defcustom timestring-supported-encodings
-  '(("RFC 3339" . "%Y-%m-%dT%H:%M:%SZ")
-    ;; Does anyone recognize this format?
-    ("IDK" . "%Y-%m-%d %H:%M:%S %z"))
-  "Mapping of encoding names to their format strings."
-  :group 'timestring)
-
-(defcustom timestring-supported-times
-  '(("yesterday" . timestring--yesterday)
-    ("now" . ts-now)
-    ("tomorrow" . timestring--tomorrow))
-  "Mapping of a labels to the functions that create those time objects."
-  :group 'timestring)
-
-(defun timestring--yesterday ()
-  "Return a time object for yesterday."
-  (ts-adjust 'day -1 (ts-now)))
-
-(defun timestring--tomorrow ()
-  "Return a time object for yesterday."
-  (ts-adjust 'day +1 (ts-now)))
-
-(defun timestring--completing-read (label xs)
-  "Call `completing-read' with LABEL over the collection XS."
-  (alist-get (completing-read label xs) xs nil nil #'equal))
-
-(defun timestring-copy-encoded-time ()
-  "Select a common time and an encoding.
-
-The selected time will be encoded using the selected encoding and copied onto
-your clipboard."
-  (interactive)
-  (let ((time (funcall (timestring--completing-read
-                        "Time: " timestring-supported-times)))
-        (fmt (timestring--completing-read
-              "Encoding: " timestring-supported-encodings)))
-    (kill-new (ts-format fmt time))
-    (message "Copied!")))
-
-(provide 'timestring)
-;;; timestring.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/tree.el b/users/wpcarro/emacs/.emacs.d/wpc/tree.el
deleted file mode 100644
index 332e6c8d25..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/tree.el
+++ /dev/null
@@ -1,199 +0,0 @@
-;;; tree.el --- Working with Trees -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; Some friendly functions that hopefully will make working with trees cheaper
-;; and therefore more appealing!
-;;
-;; Tree terminology:
-;; - leaf: node with zero children.
-;; - root: node with zero parents.
-;; - depth: measures a node's distance from the root node.  This implies the
-;;   root node has a depth of zero.
-;; - height: measures the longest traversal from a node to a leaf.  This implies
-;;   that a leaf node has a height of zero.
-;; - balanced?
-;;
-;; Tree variants:
-;; - binary: the maximum number of children is two.
-;; - binary search: the maximum number of children is two and left sub-trees are
-;;   lower in value than right sub-trees.
-;; - rose: the number of children is variable.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'prelude)
-(require 'list)
-(require 'set)
-(require 'tuple)
-(require 'series)
-(require 'random)
-(require 'maybe)
-(require 'cl-lib)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(cl-defstruct tree xs)
-
-(cl-defstruct node value children)
-
-(cl-defun tree-node (value &optional children)
-  "Create a node struct of VALUE with CHILDREN."
-  (make-node :value value
-             :children children))
-
-(defun tree-reduce-breadth (acc f xs)
-  "Reduce over XS breadth-first applying F to each x and ACC (in that order).
-Breadth-first traversals guarantee to find the shortest path in a graph.
-  They're typically more difficult to implement than DFTs and may also incur
-  higher memory costs on average than their depth-first counterparts.")
-
-;; TODO: Support :order as 'pre | 'in | 'post.
-;; TODO: Troubleshoot why I need defensive (nil? node) check.
-(defun tree-reduce-depth (acc f node)
-  "Reduce over NODE depth-first applying F to each NODE and ACC.
-F is called with each NODE, ACC, and the current depth.
-Depth-first traversals have the advantage of typically consuming less memory
-  than their breadth-first equivalents would have.  They're also typically
-  easier to implement using recursion.  This comes at the cost of not
-  guaranteeing to be able to find the shortest path in a graph."
-  (cl-labels ((do-reduce-depth
-               (acc f node depth)
-               (let ((acc-new (funcall f node acc depth)))
-                 (if (or (maybe-nil? node)
-                         (tree-leaf? node))
-                     acc-new
-                   (list-reduce
-                    acc-new
-                    (lambda (node acc)
-                      (tree-do-reduce-depth
-                       acc
-                       f
-                       node
-                       (number-inc depth)))
-                    (node-children node))))))
-    (do-reduce-depth acc f node 0)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Helpers
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun tree-height (xs)
-  "Return the height of tree XS.")
-
-;; TODO: Troubleshoot why need for (nil? node).  Similar misgiving
-;; above.
-(defun tree-leaf-depths (xs)
-  "Return a list of all of the depths of the leaf nodes in XS."
-  (list-reverse
-   (tree-reduce-depth
-    '()
-    (lambda (node acc depth)
-      (if (or (maybe-nil? node)
-              (tree-leaf? node))
-          (list-cons depth acc)
-        acc))
-    xs)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Generators
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; TODO: Consider parameterizing height, forced min-max branching, random
-;; distributions, etc.
-
-;; TODO: Bail out before stack overflowing by consider branching, current-depth.
-
-(cl-defun tree-random (&optional (value-fn (lambda (_) nil))
-                                 (branching-factor 2))
-  "Randomly generate a tree with BRANCHING-FACTOR.
-
-This uses VALUE-FN to compute the node values.  VALUE-FN is called with the
-current-depth of the node.  Useful for generating test data.  Warning this
-function can overflow the stack."
-  (cl-labels ((do-random
-               (d vf bf)
-               (make-node
-                :value (funcall vf d)
-                :children (->> (series/range 0 (number-dec bf))
-                               (list-map
-                                (lambda (_)
-                                  (when (random-boolean?)
-                                    (do-random d vf bf))))))))
-    (do-random 0 value-fn branching-factor)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Predicates
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun tree-instance? (tree)
-  "Return t if TREE is a tree struct."
-  (node-p tree))
-
-(defun tree-leaf? (node)
-  "Return t if NODE has no children."
-  (maybe-nil? (node-children node)))
-
-(defun tree-balanced? (n xs)
-  "Return t if the tree, XS, is balanced.
-A tree is balanced if none of the differences between any two depths of two leaf
-  nodes in XS is greater than N."
-  (> n (->> xs
-            tree-leaf-depths
-            set-from-list
-            set-count
-            number-dec)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defconst tree-enable-testing? t
-  "When t, test suite runs.")
-
-;; TODO: Create set of macros for a proper test suite including:
-;; - describe (arbitrarily nestable)
-;; - it (arbitrarily nestable)
-;; - line numbers for errors
-;; - accumulated output for synopsis
-;; - do we want describe *and* it? Why not a generic label that works for both?
-(when tree-enable-testing?
-  (let ((tree-a (tree-node 1
-                           (list (tree-node 2
-                                            (list (tree-node 5)
-                                                  (tree-node 6)))
-                                 (tree-node 3
-                                            (list (tree-node 7)
-                                                  (tree-node 8)))
-                                 (tree-node 4
-                                            (list (tree-node 9)
-                                                  (tree-node 10))))))
-        (tree-b (tree-node 1
-                           (list (tree-node 2
-                                            (list (tree-node 5)
-                                                  (tree-node 6)))
-                                 (tree-node 3)
-                                 (tree-node 4
-                                            (list (tree-node 9)
-                                                  (tree-node 10)))))))
-    ;; instance?
-    (prelude-assert (tree-instance? tree-a))
-    (prelude-assert (tree-instance? tree-b))
-    (prelude-refute (tree-instance? '(1 2 3)))
-    (prelude-refute (tree-instance? "oak"))
-    ;; balanced?
-    (prelude-assert (tree-balanced? 1 tree-a))
-    (prelude-refute (tree-balanced? 1 tree-b))
-    (message "Tests pass!")))
-
-(provide 'tree)
-;;; tree.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/vector.el b/users/wpcarro/emacs/.emacs.d/wpc/vector.el
deleted file mode 100644
index 6b89708cef..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/vector.el
+++ /dev/null
@@ -1,84 +0,0 @@
-;;; vector.el --- Working with Elisp's Vector data type -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; It might be best to think of Elisp vectors as tuples in languages like
-;; Haskell or Erlang.
-;;
-;; Not surprisingly, this API is modelled after Elixir's Tuple API.
-;;
-;; Some Elisp trivia:
-;; - "Array": Usually means vector or string.
-;; - "Sequence": Usually means list or "array" (see above).
-;;
-;; It might be a good idea to think of Array and Sequence as typeclasses in
-;; Elisp.  This is perhaps more similar to Elixir's notion of the Enum protocol.
-;;
-;; Intentionally not supporting a to-list function, because tuples can contain
-;; heterogenous types whereas lists should contain homogenous types.
-
-;;; Code:
-
-;; TODO: Consider supporting an alias named tuple for vector.
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defconst vector-enable-tests? t
-  "When t, run the tests defined herein.")
-
-;; TODO: Consider labelling variadic functions like `vector-concat*'
-;; vs. `vector-concat'.
-(defun vector-concat (&rest args)
-  "Return a new vector composed of all vectors in `ARGS'."
-  (apply #'vconcat args))
-
-;; TODO: Here's a sketch of a protocol macro being consumed.
-;; (definstance monoid vector
-;;   :empty (lambda () []))
-
-(defun vector-prepend (x xs)
-  "Add `X' to the beginning of `XS'."
-  (vector-concat `[,x] xs))
-
-(defun vector-append (x xs)
-  "Add `X' to the end of `XS'."
-  (vector-concat xs `[,x]))
-
-(defun vector-get (i xs)
-  "Return the value in `XS' at index, `I'."
-  (aref xs i))
-
-(defun vector-set (i v xs)
-  "Set index `I' to value `V' in `XS'.
-Returns a copy of `XS' with the updates."
-  (let ((copy (vconcat [] xs)))
-    (aset copy i v)
-    copy))
-
-(defun vector-set! (i v xs)
-  "Set index `I' to value `V' in `XS'.
-This function mutates XS."
-  (aset xs i v))
-
-(when vector-enable-tests?
-  (let ((xs [1 2 3])
-        (ys [1 2 3]))
-    (prelude-assert (= 1 (vector-get 0 ys)))
-    (vector-set 0 4 ys)
-    (prelude-assert (= 1 (vector-get 0 ys)))
-    (prelude-assert (= 1 (vector-get 0 xs)))
-    (vector-set! 0 4 xs)
-    (prelude-assert (= 4 (vector-get 0 xs)))))
-
-;; TODO: Decide between "remove" and "delete" as the appropriate verbs.
-;; TODO: Implement this.
-;; (defun vector/delete (i xs)
-;;   "Remove the element at `I' in `XS'.")
-
-(provide 'vector)
-;;; vector.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/window-manager.el b/users/wpcarro/emacs/.emacs.d/wpc/window-manager.el
index 6e00979261..94fb99d427 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/window-manager.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/window-manager.el
@@ -97,12 +97,12 @@
 (defun window-manager-next-workspace ()
   "Cycle forwards to the next workspace."
   (interactive)
-  (window-manager--change-workspace (cycle-next window-manager--workspaces)))
+  (window-manager--change-workspace (cycle-next! window-manager--workspaces)))
 
 (defun window-manager-prev-workspace ()
   "Cycle backwards to the previous workspace."
   (interactive)
-  (window-manager--change-workspace (cycle-prev window-manager--workspaces)))
+  (window-manager--change-workspace (cycle-prev! window-manager--workspaces)))
 
 ;; Here is the code required to toggle EXWM's modes.
 (defun window-manager--line-mode ()
@@ -120,7 +120,7 @@
   (interactive)
   (with-current-buffer (window-buffer)
     (when (eq major-mode 'exwm-mode)
-      (funcall (cycle-next window-manager--modes)))))
+      (funcall (cycle-next! window-manager--modes)))))
 
 (defun window-manager--label->index (label workspaces)
   "Return the index of the workspace in WORKSPACES named LABEL."
@@ -152,10 +152,10 @@ Currently using super- as the prefix for switching workspaces."
 
 (defun window-manager--switch (label)
   "Switch to a named workspaces using LABEL."
-  (cycle-focus (lambda (x)
-                 (equal label
-                        (window-manager-named-workspace-label x)))
-               window-manager--workspaces)
+  (cycle-focus! (lambda (x)
+                  (equal label
+                         (window-manager-named-workspace-label x)))
+                window-manager--workspaces)
   (window-manager--change-workspace (cycle-current window-manager--workspaces)))
 
 (defun window-manager-toggle-previous ()
@@ -196,21 +196,33 @@ predicate."
        cycle-current
        window-manager-named-workspace-label))
 
-(defun window-manager-swap-workspaces ()
-  "Prompt the user to switch the current workspace with another."
+(defun window-manager-workspace-move ()
+  "Prompt the user to move the current workspace to another."
   (interactive)
-  (let* ((selection (->> window-manager-named-workspaces
-                         (-map #'window-manager-named-workspace-label)
-                         (-reject
-                          (lambda (x)
-                            (s-equals? x (window-manager-current-workspace))))
-                         (completing-read
-                          (format "Swap current workspace (i.e. \"%s\") with: "
-                                  (window-manager-current-workspace)))))
-         (i (-find-index (lambda (x)
-                           (s-equals? selection (window-manager-named-workspace-label x)))
-                                 window-manager-named-workspaces)))
-    (exwm-workspace-swap exwm-workspace--current (elt exwm-workspace--list i))))
+  (exwm-workspace-move
+   exwm-workspace--current
+   (window-manager--label->index
+    (completing-read "Move current workspace to: "
+                     (->> window-manager-named-workspaces
+                          (-map #'window-manager-named-workspace-label))
+                     nil
+                     t)
+    window-manager-named-workspaces)))
+
+(defun window-manager-move-window ()
+  "Prompt the user to move the current window to another workspace."
+  (interactive)
+  (let ((window (get-buffer-window))
+        (dest (completing-read "Move current window to: "
+                               (->> window-manager-named-workspaces
+                                    (-map #'window-manager-named-workspace-label))
+                               nil
+                               t)))
+    (exwm-workspace-move-window
+     (exwm-workspace--workspace-from-frame-or-index
+      (window-manager--label->index dest window-manager-named-workspaces))
+     (exwm--buffer->id window))
+    (window-manager--switch dest)))
 
 (provide 'window-manager)
 ;;; window-manager.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/window.el b/users/wpcarro/emacs/.emacs.d/wpc/window.el
deleted file mode 100644
index aec3c7012f..0000000000
--- a/users/wpcarro/emacs/.emacs.d/wpc/window.el
+++ /dev/null
@@ -1,40 +0,0 @@
-;;; window.el --- Working with windows -*- lexical-binding: t -*-
-
-;; Author: William Carroll <wpcarro@gmail.com>
-;; Version: 0.0.1
-;; Package-Requires: ((emacs "25.1"))
-
-;;; Commentary:
-;; Utilities to make CRUDing windows in Emacs easier.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Dependencies
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(require 'prelude)
-(require 'macros)
-(require 'maybe)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Library
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defun window-find (name)
-  "Find a window by the NAME of the buffer it's hosting."
-  (let ((buffer (get-buffer name)))
-    (if (maybe-some? buffer)
-        (get-buffer-window buffer)
-      nil)))
-
-;; TODO: Find a way to incorporate these into function documentation.
-(macros-comment
- (window-find "*scratch*"))
-
-(defun window-delete (window)
-  "Delete the WINDOW reference."
-  (delete-window window))
-
-(provide 'window)
-;;; window.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-dired.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-dired.el
index 6df7ba4106..7c22a4657f 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/wpc-dired.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-dired.el
@@ -24,8 +24,7 @@
 (progn
   (require 'dired)
   (setq dired-recursive-copies 'always
-        dired-recursive-deletes 'top
-        dired-dwim-target t)
+        dired-recursive-deletes 'top)
   (setq dired-listing-switches "-la --group-directories-first")
   (general-define-key
    :keymaps 'dired-mode-map
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-dotnet.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-dotnet.el
new file mode 100644
index 0000000000..03fc430e48
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-dotnet.el
@@ -0,0 +1,16 @@
+;;; wpc-dotnet.el --- C# and company -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Windows things v0v.
+
+;;; Code:
+
+(require 'macros)
+
+(use-package csharp-mode)
+(macros-support-file-extension "csproj" xml-mode)
+
+(provide 'wpc-dotnet)
+;;; wpc-dotnet.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-javascript.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-javascript.el
index 7c1816c561..9e137ad880 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/wpc-javascript.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-javascript.el
@@ -42,11 +42,6 @@
       js-indent-level 2
       css-indent-offset 2)
 
-;; Flow for Javascript
-(use-package add-node-modules-path
-  :config
-  (general-add-hook wpc-javascript--js-hooks #'add-node-modules-path))
-
 (use-package web-mode
   :mode "\\.html\\'"
   :config
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-language-support.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-language-support.el
new file mode 100644
index 0000000000..8363e3c08e
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-language-support.el
@@ -0,0 +1,36 @@
+;;; wpc-language-support.el --- Support for miscellaneous programming languages -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; I defined this module to declutter my init.el.
+;;
+;; When a particular programming-language's configuration gets too complicated,
+;; I break it out into a dedicated module. Everything else gets dumped in
+;; "Miscellaneous Configuration".
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dedicated Modules
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'wpc-lisp)
+(require 'wpc-haskell)
+(require 'wpc-elixir)
+(require 'wpc-nix)
+(require 'wpc-rust)
+(require 'wpc-clojure)
+(require 'wpc-prolog)
+(require 'wpc-dotnet)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Miscellaneous Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package terraform-mode)
+
+(provide 'wpc-language-support)
+;;; wpc-language-support.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-lisp.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-lisp.el
index 4984e5dccd..599d426204 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/wpc-lisp.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-lisp.el
@@ -100,10 +100,18 @@
   :config
   (general-add-hook 'emacs-lisp-mode #'ielm-mode))
 
+(defun wpc-lisp-copy-elisp-eval-output ()
+  "Copy the output of the elisp evaluation"
+  (interactive)
+  (call-interactively 'eval-last-sexp)
+  (clipboard-copy (current-message)
+                  :message (format "%s - copied!" (current-message))))
+
 (general-define-key
  :keymaps 'emacs-lisp-mode-map
  :prefix "<SPC>"
  :states 'normal
+ "c" #'wpc-lisp-copy-elisp-eval-output
  "x" #'eval-defun
  "X" #'eval-buffer
  "d" (lambda ()
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-misc.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-misc.el
index e2d01e2d23..36fbf8b12c 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/wpc-misc.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-misc.el
@@ -19,6 +19,7 @@
 (require 'tvl)
 (require 'region)
 (require 'general)
+(require 'constants)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Configuration
@@ -67,9 +68,6 @@
 (setq sh-basic-offset 2)
 (setq sh-indentation 2)
 
-;; Emacs library that interfaces with my Linux password manager.
-(use-package password-store)
-
 (use-package vterm
   :config
   (general-define-key
@@ -124,7 +122,7 @@
               (company-mode -1)
               (flyspell-mode 1)))
   (setq magit-display-buffer-function
-        #'magit-display-buffer-fullframe-status-v1))
+        #'magit-display-buffer-same-window-except-diff-v1))
 
 (use-package magit-popup)
 
@@ -179,12 +177,6 @@
 ;; configure ibuffer
 (setq ibuffer-default-sorting-mode 'major-mode)
 
-;; config Emacs to use $PATH values
-(use-package exec-path-from-shell
-  :if (memq window-system '(mac ns))
-  :config
-  (exec-path-from-shell-initialize))
-
 ;; Emacs autosave, backup, interlocking files
 (setq auto-save-default nil
       make-backup-files nil
@@ -225,8 +217,9 @@
 
 (use-package yasnippet
   :config
-  (setq yas-snippet-dirs (list (f-join user-emacs-directory "snippets")))
-  (yas-global-mode 1))
+  (unless constants-ci?
+    (setq yas-snippet-dirs (list (f-join user-emacs-directory "snippets")))
+    (yas-global-mode 1)))
 
 (use-package projectile
   :config
@@ -324,11 +317,10 @@
   ;; Disable the default styles of:
   ;; - ascii  :P (When this is enabled, the vim command, :x, renders as 😢)
   ;; - github :smile:
-  (setq emojify-emoji-styles '(unicode))
-  (defun wpc-misc-copy-emoji ()
-    "Select an emoji from the completing-read menu."
-    (interactive)
-    (clipboard-copy (emojify-completing-read "Copy: "))))
+  (setq emojify-emoji-styles '(unicode)))
+
+;; Always auto-close parantheses and other pairs
+(electric-pair-mode)
 
 ;; Start the Emacs server
 (when (not (server-running-p))
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-rust.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-rust.el
index dfee2fc86b..b609efb431 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/wpc-rust.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-rust.el
@@ -6,11 +6,6 @@
 
 ;;; Commentary:
 ;; Supports my Rust work.
-;;
-;; Dependencies:
-;; - `rustup`
-;; - `rustup component add rust-src`
-;; - `rustup toolchain add nightly && cargo +nightly install racer`
 
 ;;; Code:
 
@@ -18,30 +13,18 @@
 ;; Dependencies
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(require 'macros)
+(require 'lsp)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Configuration
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(use-package racer
-  :config
-  (setq rust-sysroot (->> "~/.cargo/bin/rustc --print sysroot"
-                          shell-command-to-string
-                          s-trim-right))
-  (setq racer-rust-src-path (f-join rust-sysroot "lib/rustlib/src/rust/src"))
-  (add-hook 'racer-mode-hook #'eldoc-mode))
-
 (use-package rust-mode
   :config
-  (add-hook 'rust-mode-hook #'racer-mode)
-  (macros-add-hook-before-save 'rust-mode-hook #'rust-format-buffer)
-  (define-key rust-mode-map
-    (kbd "TAB")
-    #'company-indent-or-complete-common)
-  (define-key rust-mode-map
-    (kbd "M-d")
-    #'racer-describe))
+  (setq lsp-rust-server #'rust-analyzer)
+  (setq rust-format-show-buffer nil)
+  (setq rust-format-on-save t)
+  (add-hook 'rust-mode-hook #'lsp))
 
 (provide 'wpc-rust)
 ;;; wpc-rust.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-ui.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-ui.el
index f3fb8324c2..a2f533cec0 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/wpc-ui.el
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-ui.el
@@ -13,16 +13,15 @@
 ;; Dependencies
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(require 'constants)
-(require 'tvl)
-(require 'prelude)
+(require '>)
 (require 'al)
+(require 'constants)
+(require 'dash)
 (require 'fonts)
-(require 'colorscheme)
-(require 'device)
-(require 'laptop-battery)
-(require 'modeline)
 (require 'general)
+(require 'modeline)
+(require 'prelude)
+(require 'theme)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Configuration
@@ -70,9 +69,6 @@
 ;; disable toolbar
 (tool-bar-mode -1)
 
-;; set default buffer for Emacs
-(setq initial-buffer-choice tvl-depot-path)
-
 ;; premium Emacs themes
 (use-package doom-themes
   :config
@@ -110,16 +106,16 @@
 (use-package ivy-prescient
   :config
   (ivy-prescient-mode 1)
-  (prescient-persist-mode 1))
-
-(use-package ivy-pass)
+  (unless constants-ci?
+    (prescient-persist-mode 1)))
 
 ;; all-the-icons
 (use-package all-the-icons
   :config
-  (when (not constants-ci?)
-    (unless (f-exists? "~/.local/share/fonts/all-the-icons.ttf")
-      (all-the-icons-install-fonts t))))
+  (unless (or constants-ci?
+              (f-exists? "~/.local/share/fonts/all-the-icons.ttf")
+              (f-exists? "~/Library/Fonts/all-the-icons.ttf"))
+    (all-the-icons-install-fonts t)))
 
 ;; icons for Ivy
 (use-package all-the-icons-ivy
@@ -140,8 +136,8 @@
   ;; example, Google Java projects prefer 100 character width instead of 80
   ;; character width.
   (setq whitespace-line-column 80)
-  (setq whitespace-style '(face lines-tail))
-  (add-hook 'prog-mode-hook #'whitespace-mode))
+  (setq whitespace-style '(face lines-tail tabs))
+  (global-whitespace-mode t))
 
 ;; dirname/filename instead of filename<dirname>
 (setq uniquify-buffer-name-style 'forward)
@@ -160,15 +156,24 @@
   :config
   (setq alert-default-style 'notifier))
 
-;; TODO: Should `device-laptop?' be a function or a constant that gets set
-;; during initialization?
-(when (device-laptop?) (laptop-battery-display))
+(display-battery-mode 1)
 
-(colorscheme-whitelist-set 'doom-peacock)
+(setq theme-whitelist
+      (->> (custom-available-themes)
+           (list-map #'symbol-name)
+           (list-filter (>-> (s-starts-with? "doom-")))
+           (list-map #'intern)
+           cycle-from-list))
+(setq theme-linum-color-override "da5478")
+(add-hook 'theme-after-change
+          (lambda () (prelude-set-line-number-color "#da5478")))
+(theme-whitelist-set 'doom-flatwhite)
 
 (when window-system
-  (let ((font "Monospace"))
-    (fonts-whitelist-set font)
+  ;; On OSX, JetBrainsMono is installed as "JetBrains Mono", and I'm
+  ;; not sure how to change that.
+  (let ((font (if constants-osx? "JetBrains Mono" "JetBrainsMono")))
+    (fonts-set font)
     ;; Some themes (e.g. doom-acario-*) change the font for comments. This
     ;; should prevent that.
     (set-face-attribute font-lock-comment-face nil
diff --git a/users/wpcarro/emacs/AppIcon.icns b/users/wpcarro/emacs/AppIcon.icns
new file mode 100644
index 0000000000..b3be251ccf
--- /dev/null
+++ b/users/wpcarro/emacs/AppIcon.icns
Binary files differdiff --git a/users/wpcarro/emacs/README.md b/users/wpcarro/emacs/README.md
index 1ed6634e06..16f4fc31f9 100644
--- a/users/wpcarro/emacs/README.md
+++ b/users/wpcarro/emacs/README.md
@@ -11,3 +11,5 @@ the top-level `depot` directory:
 ```shell
 $ nix-env -iA users.wpcarro.emacs.nixos
 ```
+
+Test edit (from depot).
diff --git a/users/wpcarro/emacs/ci.el b/users/wpcarro/emacs/ci.el
new file mode 100644
index 0000000000..9dfaf3056f
--- /dev/null
+++ b/users/wpcarro/emacs/ci.el
@@ -0,0 +1,44 @@
+;; This script initializes Emacs and exits with either a zero or non-zero status
+;; depending on whether or not Emacs initialized without logging warnings or
+;; encountering errors.
+;;
+;; This script reads the location of init.el as the last argument in `argv'.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'f)
+(require 'dash)
+(require 'buffer)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Script
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar init-el-path (-last-item argv)
+  "Path to the init.el file that this script attempts to load.")
+
+(prelude-assert (f-exists? init-el-path))
+
+(condition-case err
+    (load init-el-path)
+  (error
+   (message "Encountered an error while attempting to load init.el: %s" err)
+   (kill-emacs 1)))
+
+(when (buffer-exists? "*Errors*")
+  (progn
+    (with-current-buffer "*Errors*"
+      (message "Encountered errors in *Errors* buffer: %s" (buffer-string)))
+    (kill-emacs 1)))
+
+(when (buffer-exists? "*Warnings*")
+  (progn
+    (with-current-buffer "*Warnings*"
+      (message "Encountered warnings in *Warnings* buffer: %s" (buffer-string)))
+    (kill-emacs 1)))
+
+(message "Successfully initialized Emacs without errors or warnings!")
+(kill-emacs 0)
diff --git a/users/wpcarro/emacs/default.nix b/users/wpcarro/emacs/default.nix
index 20ef805dba..0b3c5a6e73 100644
--- a/users/wpcarro/emacs/default.nix
+++ b/users/wpcarro/emacs/default.nix
@@ -1,29 +1,64 @@
+# My Emacs distribution, which is supporting the following platforms:
+# - Linux
+# - Darwin
+#
+# USAGE:
+#   $ nix-build -A users.wpcarro.emacs.osx -o /Applications/BillsEmacs.app
 { depot, pkgs, lib, ... }:
 
 # TODO(wpcarro): See if it's possible to expose emacsclient on PATH, so that I
 # don't need to depend on wpcarros-emacs and emacs in my NixOS configurations.
 let
-  inherit (builtins) path;
-  inherit (depot.third_party.nixpkgs) emacsPackagesGen emacs27;
+  inherit (depot.third_party.nixpkgs) emacsPackagesFor emacs28;
   inherit (depot.users) wpcarro;
-  inherit (pkgs) writeShellScript writeShellScriptBin;
   inherit (lib) mapAttrsToList;
   inherit (lib.strings) concatStringsSep makeBinPath;
+  inherit (pkgs) runCommand writeShellScriptBin;
 
   emacsBinPath = makeBinPath (
     wpcarro.common.shell-utils ++
+    # Rust dependencies
+    (with pkgs; [
+      cargo
+      rust-analyzer
+      rustc
+      rustfmt
+    ]) ++
+    # Misc dependencies
     (with pkgs; [
-      clipmenu
       ispell
       nix
-      pass
-      scrot
+      rust-analyzer
+      rustc
+      rustfmt
       xorg.xset
-    ]));
+    ] ++
+    (if pkgs.stdenv.isLinux then [
+      scrot
+    ] else [ ]))
+  );
 
-  emacsWithPackages = (emacsPackagesGen emacs27).emacsWithPackages;
+  emacsWithPackages = (emacsPackagesFor emacs28).emacsWithPackages;
 
   wpcarrosEmacs = emacsWithPackages (epkgs:
+    (with wpcarro.emacs.pkgs; [
+      al
+      bookmark
+      cycle
+      list
+      macros
+      maybe
+      passage
+      set
+      string
+      struct
+      symbol
+      theme
+      tuple
+      vterm-mgt
+      zle
+    ]) ++
+
     (with epkgs.tvlPackages; [
       tvl
     ]) ++
@@ -33,116 +68,91 @@ let
     ]) ++
 
     (with epkgs.melpaPackages; [
-      org-bullets
-      sly
-      notmuch
-      elm-mode
-      ts
-      vterm
+      alert
+      all-the-icons
+      all-the-icons-ivy
+      avy
       base16-theme
-      password-store
-      # TODO(wpcarro): Prefer an Emacs client for clipmenud.
-      clipmon
+      cider
+      clojure-mode
+      company
+      counsel
+      counsel-projectile
+      csharp-mode
+      dap-mode
+      dash
+      deadgrep
+      deferred
+      diminish
+      direnv
+      dockerfile-mode
+      # TODO(wpcarro): broken since channel bump cl/10204
+      # doom-themes
+      elisp-slime-nav
+      elixir-mode
+      elm-mode
+      emojify
+      engine-mode
       evil
       evil-collection
       evil-commentary
       evil-surround
-      key-chord
-      # TODO(wpcarro): Assess whether or not I need this with Nix.
-      add-node-modules-path
-      web-mode
-      rjsx-mode
-      tide
-      prettier-js
+      f
+      fish-mode
       flycheck
-      diminish
-      doom-themes
-      telephone-line
-      which-key
-      all-the-icons
-      all-the-icons-ivy
+      flymake-shellcheck
+      general
+      go-mode
+      haskell-mode
+      helpful
       ivy
-      ivy-pass
+      ivy-clipmenu
       ivy-prescient
-      restclient
-      package-lint
-      parsec
+      key-chord
+      lispyville
+      lsp-ui
+      magit
       magit-popup
-      direnv
-      alert
+      markdown-mode
       nix-mode
-      racer
-      rust-mode
-      rainbow-delimiters
-      racket-mode
-      lispyville
-      elisp-slime-nav
+      notmuch
+      org-bullets
+      package-lint
+      paradox
+      parsec
+      pcre2el
+      prettier-js
+      projectile
       py-yapf
+      racket-mode
+      rainbow-delimiters
       reason-mode
-      elixir-mode
-      go-mode
-      company
-      markdown-mode
       refine
-      deferred
-      magit
       request
-      pcre2el
-      helpful
-      # TODO(wpcarro): Determine if Nix solves this problem.
-      exec-path-from-shell
-      yasnippet
-      projectile
-      deadgrep
-      counsel
-      counsel-projectile
-      # TODO(wpcarro): Learn what this is.
-      engine-mode
-      eglot
-      dap-mode
-      lsp-ui
+      restclient
+      rjsx-mode
+      rust-mode
+      sly
       suggest
-      paradox
-      flymake-shellcheck
-      fish-mode
+      telephone-line
+      terraform-mode
+      tide
+      ts
       tuareg
-      haskell-mode
       use-package
-      general
-      clojure-mode
-      cider
-      f
-      dash
-      company
-      counsel
-      flycheck
-      emojify
-    ]));
-
-  vendorDir = path {
-    path = ./.emacs.d/vendor;
-    name = "emacs-vendor";
-  };
-
-  # TODO(wpcarro): byte-compile these by packaging each as an Elisp library.
-  wpcDir = path {
-    path = ./.emacs.d/wpc;
-    name = "emacs-libs";
-  };
-
-  wpcPackageEl = path {
-    path = ./.emacs.d/wpc/wpc-package.el;
-    name = "wpc-package.el";
-  };
+      vterm
+      web-mode
+      which-key
+      yaml-mode
+      yasnippet
+    ]) ++
 
-  initEl = path {
-    path = ./.emacs.d/init.el;
-    name = "init.el";
-  };
+    [
+      epkgs.eglot # from elpa devel
+    ]);
 
   loadPath = concatStringsSep ":" [
-    wpcDir
-    vendorDir
+    ./.emacs.d/wpc
     # TODO(wpcarro): Explain why the trailing ":" is needed.
     "${wpcarrosEmacs.deps}/share/emacs/site-lisp:"
   ];
@@ -151,7 +161,7 @@ let
   makeEnvVars = env: concatStringsSep "\n"
     (mapAttrsToList (k: v: "export ${k}=\"${v}\"") env);
 
-  withEmacsPath = { emacsBin, env ? {} }:
+  withEmacsPath = { emacsBin, env ? { }, load ? [ ] }:
     writeShellScriptBin "wpcarros-emacs" ''
       export XMODIFIERS=emacs
       export PATH="${emacsBinPath}:$PATH"
@@ -162,28 +172,96 @@ let
         --no-init-file \
         --no-site-file \
         --no-site-lisp \
-        --load ${initEl} \
+        --load ${./.emacs.d/init.el} \
+        ${concatStringsSep "\n  " (map (el: "--load ${el} \\") load)}
         "$@"
     '';
-in {
-  inherit initEl withEmacsPath;
 
-  # I need to start my Emacs from CI without the call to `--load ${initEl}`.
-  runScript = { script }:
-    writeShellScript "run-emacs-script" ''
-      export PATH="${emacsBinPath}:$PATH"
-      export EMACSLOADPATH="${wpcDir}:${vendorDir}:${wpcarrosEmacs.deps}/share/emacs/site-lisp"
+  # I can't figure out how to augment LSEnvironment.PATH such that it inherits
+  # the default $PATH and adds the things that I need as well, so let's
+  # hardcode the desired outcome in the meantime.
+  osxDefaultPath = builtins.concatStringsSep ":" [
+    "/Users/bill/.nix-profile/bin"
+    "/nix/var/nix/profiles/default/bin"
+    "/opt/homebrew/bin"
+    "/opt/homebrew/sbin"
+    "/usr/local/bin"
+    "/usr/bin"
+    "/bin"
+    "/usr/sbin"
+    "/sbin"
+    "/opt/X11/bin"
+  ];
+
+  infoPlist = pkgs.writeText "Info.plist" (pkgs.lib.generators.toPlist { } {
+    LSEnvironment = {
+      PATH = "${emacsBinPath}:${osxDefaultPath}";
+    };
+    CFBundleExecutable = "BillsEmacs";
+    CFBundleDisplayName = "BillsEmacs";
+    CFBundleIconFile = "AppIcon";
+    CFBundleIconName = "AppIcon";
+  });
+
+  versionPlist = pkgs.writeText "version.plist" (pkgs.lib.generators.toPlist { } {
+    ProjectName = "OSXPlatformSupport";
+  });
+in
+{
+  # TODO(wpcarro): Support this with base.overrideAttrs or something similar.
+  nixos = { load ? [ ] }: withEmacsPath {
+    inherit load;
+    emacsBin = "${wpcarrosEmacs}/bin/emacs";
+  };
+
+  # To install GUI:
+  # $ nix-build -A users.wpcarro.emacs.osx -o /Applications/BillsEmacs.app
+  osx = pkgs.stdenv.mkDerivation {
+    pname = "bills-emacs";
+    version = "0.0.1";
+    src = ./.;
+    dontFixup = true;
+    installPhase = ''
+      runHook preInstall
+      APP="$out"
+      mkdir -p "$APP/Contents/MacOS"
+      mkdir -p "$APP/Contents/Resources"
+      cp ${infoPlist}      "$APP/Contents/Info.plist"
+      cp ${versionPlist}   "$APP/Contents/version.plist"
+      cp ${./AppIcon.icns} "$APP/Contents/Resources/AppIcon.icns"
+      echo "APPL????"  > "$APP/Contents/PkgInfo"
+      cat << EOF > "$APP/Contents/MacOS/BillsEmacs"
+      #!${pkgs.stdenvNoCC.shell}
+      export EMACSLOADPATH="${loadPath}"
       exec ${wpcarrosEmacs}/bin/emacs \
+        --debug-init \
+        --no-init-file \
         --no-site-file \
         --no-site-lisp \
-        --no-init-file \
-        --script ${script} \
-        "$@"
+        --load ${./.emacs.d/init.el}
+      EOF
+      chmod +x "$APP/Contents/MacOS/BillsEmacs"
+      runHook postInstall
     '';
-
-  nixos = withEmacsPath {
-    emacsBin = "${wpcarrosEmacs}/bin/emacs";
+    meta.platforms = [ "aarch64-darwin" ];
   };
 
-  meta.targets = [ "nixos" ];
+  # Script that asserts my Emacs can initialize without warnings or errors.
+  check = runCommand "check-emacs" { } ''
+    # Even though Buildkite defines this, I'd still like still be able to test
+    # this locally without depending on my ability to remember to set CI=true.
+    export CI=true
+    export PATH="${emacsBinPath}:$PATH"
+    export EMACSLOADPATH="${loadPath}"
+    ${wpcarrosEmacs}/bin/emacs \
+      --no-site-file \
+      --no-site-lisp \
+      --no-init-file \
+      --script ${./ci.el} \
+      ${./.emacs.d/init.el} && \
+    touch $out
+  '';
+
+  # TODO(wpcarro): commented out because of doom-themes breakage; cl/10204
+  # meta.ci.targets = [ "check" ];
 }
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/al.el b/users/wpcarro/emacs/pkgs/al/al.el
index 3cf98fee29..4c37526c64 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/al.el
+++ b/users/wpcarro/emacs/pkgs/al/al.el
@@ -65,10 +65,9 @@
 ;; Dependencies:
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(require 'macros)
 (require 'dash)
-(require 'tuple)
-(require 'maybe)
+(require 'list)
+(require 'map)
 
 ;; TODO: Support function aliases for:
 ;; - create/set
@@ -85,13 +84,6 @@
 ;; TODO: Consider wrapping all of this with `(cl-defstruct alist xs)'.
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Constants
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(defconst al-enable-tests? t
-  "When t, run the test suite.")
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Library
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -119,10 +111,12 @@ Note that this doesn't append to the alist in the way that most alists handle
   (map-put! xs k v))
 
 ;; Read
-(defun al-get (k xs)
-  "Return the value at K in XS; otherwise, return nil.
+(defun al-get (k xs &optional default)
+  "Return the value at K in XS; otherwise, return nil or DEFAULT (if set).
 Returns the first occurrence of K in XS since alists support multiple entries."
-  (cdr (assoc k xs)))
+  (if (not (al-has-key? k xs))
+      default
+    (cdr (assoc k xs))))
 
 (defun al-get-entry (k xs)
   "Return the first key-value pair at K in XS."
@@ -183,11 +177,11 @@ Mutative variant of `al-delete'."
 
 (defun al-has-key? (k xs)
   "Return t if XS has a key `equal' to K."
-  (maybe-some? (assoc k xs)))
+  (not (eq nil (assoc k xs))))
 
 (defun al-has-value? (v xs)
   "Return t if XS has a value of V."
-  (maybe-some? (rassoc v xs)))
+  (not (eq nil (rassoc v xs))))
 
 (defun al-count (xs)
   "Return the number of entries in XS."
@@ -229,26 +223,5 @@ F should return a tuple.  See tuple.el for more information."
 In this case, the last writer wins, which is B."
   (al-reduce a #'al-set b))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(when al-enable-tests?
-  (prelude-assert
-   (equal '((2 . one)
-            (3 . two))
-          (al-map-keys #'1+
-                          '((1 . one)
-                            (2 . two)))))
-  (prelude-assert
-   (equal '((one . 2)
-            (two . 3))
-          (al-map-values #'1+
-                            '((one . 1)
-                              (two . 2))))))
-
-
-;; TODO: Support test cases for the entire API.
-
 (provide 'al)
 ;;; al.el ends here
diff --git a/users/wpcarro/emacs/pkgs/al/default.nix b/users/wpcarro/emacs/pkgs/al/default.nix
new file mode 100644
index 0000000000..d88e0757a8
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/al/default.nix
@@ -0,0 +1,28 @@
+{ pkgs, depot, ... }:
+
+let
+  al = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "al";
+        version = "1.0.0";
+        src = ./al.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+          ]) ++
+          (with depot.users.wpcarro.emacs.pkgs; [
+            list
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ al ]);
+in
+al.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/al/tests.el b/users/wpcarro/emacs/pkgs/al/tests.el
new file mode 100644
index 0000000000..04fe4dcbb5
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/al/tests.el
@@ -0,0 +1,53 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'al)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest al-has-key? ()
+  (should (al-has-key? 'fname '((fname . "William"))))
+  (should (not (al-has-key? 'lname '((fname . "William"))))))
+
+(ert-deftest al-get ()
+  (let ((xs (->> (al-new)
+                 (al-set 'fname "John")
+                 (al-set 'employed? nil))))
+    (should (string= "John" (al-get 'fname xs)))
+    (should (string= "Cleese" (al-get 'lname xs "Cleese")))
+    ;; Test that the value of nil is returned even when a default is defined,
+    ;; which could be a subtle bug in the typical Elisp pattern of supporting
+    ;; defaults with: (or foo default).
+    (should (eq nil (al-get 'employed? xs)))
+    (should (eq nil (al-get 'employed? xs "default")))))
+
+(ert-deftest al-has-value? ()
+  (should (al-has-value? "William" '((fname . "William"))))
+  (should (not (al-has-key? "John" '((fname . "William"))))))
+
+(ert-deftest al-map-keys ()
+  (should
+   (equal '((2 . one)
+            (3 . two))
+          (al-map-keys #'1+
+                       '((1 . one)
+                         (2 . two))))))
+
+(ert-deftest al-map-values ()
+  (should (equal '((one . 2)
+                   (two . 3))
+                 (al-map-values #'1+
+                                '((one . 1)
+                                  (two . 2))))))
+
+(ert-deftest al-delete ()
+  (let ((person (->> (al-new)
+                     (al-set "fname" "John")
+                     (al-set "lname" "Cleese")
+                     (al-set "age" 82))))
+    (should (al-has-key? "age" person))
+    (should (not (al-has-key? "age" (al-delete "age" person))))))
diff --git a/users/wpcarro/emacs/pkgs/bag/bag.el b/users/wpcarro/emacs/pkgs/bag/bag.el
new file mode 100644
index 0000000000..502f567253
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bag/bag.el
@@ -0,0 +1,78 @@
+;;; bag.el --- Working with bags (aka multi-sets) -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; What is a bag?  A bag should be thought of as a frequency table.  It's a way
+;; to convert a list of something into a set that allows duplicates.  Isn't
+;; allowing duplicates the whole thing with Sets?  Kind of.  But the interface
+;; of Sets is something that bags resemble, so multi-set isn't as bad of a name
+;; as it may first seem.
+;;
+;; If you've used Python's collections.Counter, the concept of a bag should be
+;; familiar already.
+;;
+;; Interface:
+;; - add        :: x -> Bag(x) -> Bag(x)
+;; - remove     :: x -> Bag(x) -> Bag(x)
+;; - union      :: Bag(x) -> Bag(x) -> Bag(x)
+;; - difference :: Bag(x) -> Bag(x) -> Bag(x)
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'al)
+(require 'list)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct bag xs)
+
+(defun bag-new ()
+  "Create an empty bag."
+  (make-bag :xs (al-new)))
+
+(defun bag-from-list (xs)
+  "Map a list of `XS' into a bag."
+  (->> xs
+       (list-reduce (bag-new) #'bag-add)))
+
+(defun bag-add (x xs)
+  "Add X to XS."
+  (if (bag-contains? x xs)
+      (struct-update
+       bag xs (lambda (xs) (al-update x (lambda (x) (+ 1 x)) xs)) xs)
+    (struct-update bag xs (lambda (xs) (al-set x 1 xs)) xs)))
+
+(defun bag-remove (x xs)
+  "Remove X from XS.
+This is a no-op is X doesn't exist in XS."
+  (when (bag-contains? x xs)
+    (struct-update bag xs (lambda (xs) (al-delete x xs)) xs)))
+
+(defun bag-count (x xs)
+  "Return the number of occurrences of X in XS."
+  (al-get x (bag-xs xs) 0))
+
+(defun bag-total (xs)
+  "Return the total number of elements in XS."
+  (->> (bag-xs xs)
+       (al-reduce 0 (lambda (_key v acc) (+ acc v)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun bag-contains? (x xs)
+  "Return t if XS has X."
+  (al-has-key? x (bag-xs xs)))
+
+(provide 'bag)
+;;; bag.el ends here
diff --git a/users/wpcarro/emacs/pkgs/bag/default.nix b/users/wpcarro/emacs/pkgs/bag/default.nix
new file mode 100644
index 0000000000..3dedc27286
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bag/default.nix
@@ -0,0 +1,26 @@
+{ pkgs, depot, ... }:
+
+let
+  bag = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "bag";
+        version = "1.0.0";
+        src = ./bag.el;
+        packageRequires =
+          (with depot.users.wpcarro.emacs.pkgs; [
+            al
+            list
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ bag ]);
+in
+bag.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/bag/tests.el b/users/wpcarro/emacs/pkgs/bag/tests.el
new file mode 100644
index 0000000000..4970f70815
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bag/tests.el
@@ -0,0 +1,32 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'bag)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq fixture (bag-from-list '(1 1 1 2 2 3)))
+
+(ert-deftest bag-add ()
+  (should (not (bag-contains? 4 fixture)))
+  (should (bag-contains? 4 (bag-add 4 fixture))))
+
+(ert-deftest bag-remove ()
+  (should (bag-contains? 1 fixture))
+  (should (not (bag-contains? 3 (bag-remove 3 fixture)))))
+
+(ert-deftest bag-count ()
+  (should (= 3 (bag-count 1 fixture)))
+  (should (= 2 (bag-count 2 fixture)))
+  (should (= 1 (bag-count 3 fixture))))
+
+(ert-deftest bag-total ()
+  (should (= 6 (bag-total fixture))))
+
+(ert-deftest bag-contains? ()
+  (should (bag-contains? 1 fixture))
+  (should (not (bag-contains? 4 fixture))))
diff --git a/users/wpcarro/emacs/pkgs/bookmark/bookmark.el b/users/wpcarro/emacs/pkgs/bookmark/bookmark.el
new file mode 100644
index 0000000000..ab9169a078
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bookmark/bookmark.el
@@ -0,0 +1,50 @@
+;;; bookmark.el --- Saved files and directories on my filesystem -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; A more opinionated version of Emacs's builtin `jump-to-register'.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'project)
+(require 'general)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct bookmark label path kbd)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; API
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun bookmark-open (b)
+  "Open bookmark, B, as either a project directory or a regular directory."
+  (with-temp-buffer
+    (cd (bookmark-path b))
+    (call-interactively #'project-find-file)))
+
+(defun bookmark-install-kbd (b)
+  "Define two functions to explore B and assign them to keybindings."
+  (eval `(defun ,(intern (format "bookmark-visit-%s" (bookmark-label b))) ()
+           (interactive)
+           (find-file ,(bookmark-path b))))
+  (eval `(defun ,(intern (format "bookmark-browse-%s" (bookmark-label b))) ()
+           (interactive)
+           (bookmark-open ,b)))
+  (general-define-key
+   :prefix "<SPC>"
+   :states '(motion)
+   (format "J%s" (bookmark-kbd b)) `,(intern (format "bookmark-visit-%s" (bookmark-label b)))
+   (format "j%s" (bookmark-kbd b)) `,(intern (format "bookmark-browse-%s" (bookmark-label b)))))
+
+(provide 'bookmark)
+;;; bookmark.el ends here
diff --git a/users/wpcarro/emacs/pkgs/bookmark/default.nix b/users/wpcarro/emacs/pkgs/bookmark/default.nix
new file mode 100644
index 0000000000..882481701f
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bookmark/default.nix
@@ -0,0 +1,13 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "bookmark";
+    version = "1.0.0";
+    src = ./bookmark.el;
+    packageRequires = (with pkgs.emacsPackages; [
+      general
+    ]);
+  })
+{ }
diff --git a/users/wpcarro/emacs/pkgs/bytes/bytes.el b/users/wpcarro/emacs/pkgs/bytes/bytes.el
new file mode 100644
index 0000000000..b0d64795a0
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bytes/bytes.el
@@ -0,0 +1,94 @@
+;;; bytes.el --- Working with byte values -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Functions to help with human-readable representations of byte values.
+;;
+;; Usage:
+;; See the test cases for example usage.  Or better yet, I should use a type of
+;; structured documentation that would allow me to expose a view into the test
+;; suite here.  Is this currently possible in Elisp?
+;;
+;; API:
+;; - serialize :: Integer -> String
+;;
+;; Wish list:
+;; - Rounding: e.g. (bytes (* 1024 1.7)) => "2KB"
+
+;;; Code:
+
+;; TODO: Support -ibabyte variants like Gibibyte (GiB).
+
+;; Ranges:
+;;  B: [   0,  1e3)
+;; KB: [ 1e3,  1e6)
+;; MB: [ 1e6,  1e6)
+;; GB: [ 1e9, 1e12)
+;; TB: [1e12, 1e15)
+;; PB: [1e15, 1e18)
+;;
+;; Note: I'm currently not support exabytes because that causes the integer to
+;;  overflow.  I imagine a larger integer type may exist, but for now, I'll
+;;  treat this as a YAGNI.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'tuple)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst bytes-kb (expt 2 10)
+  "Number of bytes in a kilobyte.")
+
+(defconst bytes-mb (expt 2 20)
+  "Number of bytes in a megabytes.")
+
+(defconst bytes-gb (expt 2 30)
+  "Number of bytes in a gigabyte.")
+
+(defconst bytes-tb (expt 2 40)
+  "Number of bytes in a terabyte.")
+
+(defconst bytes-pb (expt 2 50)
+  "Number of bytes in a petabyte.")
+
+(defconst bytes-eb (expt 2 60)
+  "Number of bytes in an exabyte.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun bytes-classify (x)
+  "Return unit that closest fits byte count, X."
+  (cond
+   ((and (>= x 0)        (< x bytes-kb))     'byte)
+   ((and (>= x bytes-kb) (< x bytes-mb)) 'kilobyte)
+   ((and (>= x bytes-mb) (< x bytes-gb)) 'megabyte)
+   ((and (>= x bytes-gb) (< x bytes-tb)) 'gigabyte)
+   ((and (>= x bytes-tb) (< x bytes-pb)) 'terabyte)
+   ((and (>= x bytes-pb) (< x bytes-eb)) 'petabyte)))
+
+(defun bytes-to-string (x)
+  "Convert integer X into a human-readable string."
+  (let ((base-and-unit
+         (pcase (bytes-classify x)
+           ('byte     (tuple-from        1 "B"))
+           ('kilobyte (tuple-from bytes-kb "KB"))
+           ('megabyte (tuple-from bytes-mb "MB"))
+           ('gigabyte (tuple-from bytes-gb "GB"))
+           ('terabyte (tuple-from bytes-tb "TB"))
+           ('petabyte (tuple-from bytes-pb "PB")))))
+    (format "%d%s"
+            (round x (tuple-first base-and-unit))
+            (tuple-second base-and-unit))))
+
+(provide 'bytes)
+;;; bytes.el ends here
diff --git a/users/wpcarro/emacs/pkgs/bytes/default.nix b/users/wpcarro/emacs/pkgs/bytes/default.nix
new file mode 100644
index 0000000000..4e9f52d9b9
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bytes/default.nix
@@ -0,0 +1,25 @@
+{ pkgs, depot, ... }:
+
+let
+  bytes = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "bytes";
+        version = "1.0.0";
+        src = ./bytes.el;
+        packageRequires =
+          (with depot.users.wpcarro.emacs.pkgs; [
+            tuple
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ bytes ]);
+in
+bytes.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/bytes/tests.el b/users/wpcarro/emacs/pkgs/bytes/tests.el
new file mode 100644
index 0000000000..9b71a466c7
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bytes/tests.el
@@ -0,0 +1,18 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'bytes)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest bytes-to-string ()
+  (should (equal "1000B" (bytes-to-string 1000)))
+  (should (equal "2KB" (bytes-to-string (* 2 bytes-kb))))
+  (should (equal "17MB" (bytes-to-string (* 17 bytes-mb))))
+  (should (equal "419GB" (bytes-to-string (* 419 bytes-gb))))
+  (should (equal "999TB" (bytes-to-string (* 999 bytes-tb))))
+  (should (equal "2PB" (bytes-to-string (* 2 bytes-pb)))))
diff --git a/users/wpcarro/emacs/pkgs/cycle/README.md b/users/wpcarro/emacs/pkgs/cycle/README.md
new file mode 100644
index 0000000000..416900d253
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/cycle/README.md
@@ -0,0 +1,7 @@
+# cycle.el
+
+[![Build status](https://badge.buildkite.com/016bff4b8ae2704a3bbbb0a250784e6692007c582983b6dea7.svg?branch=refs/heads/canon)](https://buildkite.com/tvl/depot)
+
+Cycle data structure exposing mutable and (coming soon!) immutable APIs.
+
+[![asciicast](https://asciinema.org/a/FvFujpRpYjV9qCSGvk3uobOpV.svg)](https://asciinema.org/a/FvFujpRpYjV9qCSGvk3uobOpV)
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/cycle.el b/users/wpcarro/emacs/pkgs/cycle/cycle.el
index a1853ece14..2f5b252a0d 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/cycle.el
+++ b/users/wpcarro/emacs/pkgs/cycle/cycle.el
@@ -14,9 +14,7 @@
 ;; Dependencies
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(require 'prelude)
-(require 'math)
-(require 'maybe)
+(require 'dash)
 (require 'struct)
 (require 'cl-lib)
 
@@ -26,7 +24,6 @@
 
 ;; - TODO: Provide immutable variant.
 ;; - TODO: Replace mutable consumption with immutable variant.
-;; - TODO: Replace indexing with (math-mod current cycle).
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Library
@@ -36,9 +33,6 @@
 ;; `xs' is the original list
 (cl-defstruct cycle current-index previous-index xs)
 
-(defconst cycle-enable-tests? t
-  "When t, run the tests defined herein.")
-
 (defun cycle-from-list (xs)
   "Create a cycle from a list of `XS'."
   (if (= 0 (length xs))
@@ -57,45 +51,21 @@
   "Return the list representation of a cycle, XS."
   (cycle-xs xs))
 
-(defun cycle--next-index<- (lo hi x)
-  "Return the next index in a cycle when moving downwards.
-- `LO' is the lower bound.
-- `HI' is the upper bound.
-- `X' is the current index."
-  (if (< (- x 1) lo)
-      (- hi 1)
-    (- x 1)))
-
-(defun cycle--next-index-> (lo hi x)
-  "Return the next index in a cycle when moving upwards.
-- `LO' is the lower bound.
-- `HI' is the upper bound.
-- `X' is the current index."
-  (if (>= (+ 1 x) hi)
-      lo
-    (+ 1 x)))
-
 (defun cycle-previous-focus (cycle)
   "Return the previously focused entry in CYCLE."
   (let ((i (cycle-previous-index cycle)))
-    (if (maybe-some? i)
-        (nth i (cycle-xs cycle))
-      nil)))
+    (when i (nth i (cycle-xs cycle)))))
 
-;; TODO: Consider adding "!" to the function name herein since many of them
-;; mutate the collection, and the APIs are beginning to confuse me.
 (defun cycle-focus-previous! (xs)
   "Jump to the item in XS that was most recently focused; return the cycle.
 This will error when previous-index is nil.  This function mutates the
 underlying struct."
   (let ((i (cycle-previous-index xs)))
-    (if (maybe-some? i)
-        (progn
-          (cycle-jump i xs)
-          (cycle-current xs))
+    (if i
+        (progn (cycle-jump! i xs) (cycle-current xs))
       (error "Cannot focus the previous element since cycle-previous-index is nil"))))
 
-(defun cycle-next (xs)
+(defun cycle-next! (xs)
   "Return the next value in `XS' and update `current-index'."
   (let* ((current-index (cycle-current-index xs))
          (next-index (cycle--next-index-> 0 (cycle-count xs) current-index)))
@@ -103,7 +73,7 @@ underlying struct."
     (struct-set! cycle current-index next-index xs)
     (nth next-index (cycle-xs xs))))
 
-(defun cycle-prev (xs)
+(defun cycle-prev! (xs)
   "Return the previous value in `XS' and update `current-index'."
   (let* ((current-index (cycle-current-index xs))
          (next-index (cycle--next-index<- 0 (cycle-count xs) current-index)))
@@ -119,43 +89,29 @@ underlying struct."
   "Return the length of `xs' in `CYCLE'."
   (length (cycle-xs cycle)))
 
-(defun cycle-jump (i xs)
+(defun cycle-jump! (i xs)
   "Jump to the I index of XS."
   (let ((current-index (cycle-current-index xs))
-        (next-index (math-mod i (cycle-count xs))))
+        (next-index (mod i (cycle-count xs))))
     (struct-set! cycle previous-index current-index xs)
     (struct-set! cycle current-index next-index xs))
   xs)
 
-(defun cycle-focus (p cycle)
+(defun cycle-focus! (p cycle)
   "Focus the element in CYCLE for which predicate, P, is t."
   (let ((i (->> cycle
                 cycle-xs
                 (-find-index p))))
     (if i
-        (cycle-jump i cycle)
+        (cycle-jump! i cycle)
       (error "No element in cycle matches predicate"))))
 
-(defun cycle-focus-item (x xs)
+(defun cycle-focus-item! (x xs)
   "Focus item, X, in cycle XS.
 ITEM is the first item in XS that t for `equal'."
-  (cycle-focus (lambda (y) (equal x y)) xs))
-
-(defun cycle-contains? (x xs)
-  "Return t if cycle, XS, has member X."
-  (->> xs
-       cycle-xs
-       (list-contains? x)))
-
-(defun cycle-empty? (xs)
-  "Return t if cycle XS has no elements."
-  (= 0 (length (cycle-xs xs))))
-
-(defun cycle-focused? (xs)
-  "Return t if cycle XS has a non-nil value for current-index."
-  (maybe-some? (cycle-current-index xs)))
+  (cycle-focus! (lambda (y) (equal x y)) xs))
 
-(defun cycle-append (x xs)
+(defun cycle-append! (x xs)
   "Add X to the left of the focused element in XS.
 If there is no currently focused item, add X to the beginning of XS."
   (if (cycle-empty? xs)
@@ -176,7 +132,7 @@ If there is no currently focused item, add X to the beginning of XS."
           (when prev-i (struct-set! cycle previous-index (1+ prev-i) xs))))
       xs)))
 
-(defun cycle-remove (x xs)
+(defun cycle-remove! (x xs)
   "Attempt to remove X from XS.
 
 X is found using `equal'.
@@ -197,28 +153,42 @@ If X is the currently focused value, after it's deleted, current-index will be
     xs))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tests
+;; Predicates
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(when cycle-enable-tests?
-  (let ((xs (cycle-new 1 2 3)))
-    (prelude-assert (maybe-nil? (cycle-previous-focus xs)))
-    (prelude-assert (= 1 (cycle-current xs)))
-    (prelude-assert (= 2 (cycle-next xs)))
-    (prelude-assert (= 1 (cycle-previous-focus xs)))
-    (prelude-assert (= 1 (->> xs (cycle-jump 0) cycle-current)))
-    (prelude-assert (= 2 (->> xs (cycle-jump 1) cycle-current)))
-    (prelude-assert (= 3 (->> xs (cycle-jump 2) cycle-current)))
-    (prelude-assert (= 2 (cycle-previous-focus xs)))
-    (prelude-assert (= 2 (cycle-focus-previous! xs)))
-    (prelude-assert (equal '(1 4 2 3) (cycle-xs (cycle-append 4 xs))))
-    (prelude-assert (equal '(1 2 3) (cycle-xs (cycle-remove 4 xs))))
-    (progn
-      (cycle-focus-item 3 xs)
-      (cycle-focus-item 2 xs)
-      (cycle-remove 1 xs)
-      (prelude-assert (= 2 (cycle-current xs)))
-      (prelude-assert (= 3 (cycle-previous-focus xs))))))
+(defun cycle-contains? (x xs)
+  "Return t if cycle, XS, has member X."
+  (not (null (-contains? (cycle-xs xs) x))))
+
+(defun cycle-empty? (xs)
+  "Return t if cycle XS has no elements."
+  (= 0 (length (cycle-xs xs))))
+
+(defun cycle-focused? (xs)
+  "Return t if cycle XS has a non-nil value for current-index."
+  (not (null (cycle-current-index xs))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helper Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun cycle--next-index<- (lo hi x)
+  "Return the next index in a cycle when moving downwards.
+- `LO' is the lower bound.
+- `HI' is the upper bound.
+- `X' is the current index."
+  (if (< (- x 1) lo)
+      (- hi 1)
+    (- x 1)))
+
+(defun cycle--next-index-> (lo hi x)
+  "Return the next index in a cycle when moving upwards.
+- `LO' is the lower bound.
+- `HI' is the upper bound.
+- `X' is the current index."
+  (if (>= (+ 1 x) hi)
+      lo
+    (+ 1 x)))
 
 (provide 'cycle)
 ;;; cycle.el ends here
diff --git a/users/wpcarro/emacs/pkgs/cycle/default.nix b/users/wpcarro/emacs/pkgs/cycle/default.nix
new file mode 100644
index 0000000000..7ef3b431ad
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/cycle/default.nix
@@ -0,0 +1,36 @@
+{ pkgs, depot, ... }:
+
+let
+  cycle = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "cycle";
+        version = "1.0.0";
+        src = ./cycle.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+          ]) ++
+          (with depot.users.wpcarro.emacs.pkgs; [
+            struct
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    epkgs.dash
+    cycle
+  ]);
+in
+cycle.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+  passthru.meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+    filter = ":/users/wpcarro/emacs/pkgs/cycle";
+    remote = "git@github.com:wpcarro/cycle.el.git";
+    ref = "refs/heads/canon";
+  };
+})
diff --git a/users/wpcarro/emacs/pkgs/cycle/tests.el b/users/wpcarro/emacs/pkgs/cycle/tests.el
new file mode 100644
index 0000000000..29c0e2a0d5
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/cycle/tests.el
@@ -0,0 +1,79 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'cycle)
+(require 'dash)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq xs (cycle-new 1 2 3))
+
+(ert-deftest cycle-initializes-properly ()
+  (should (= 3 (cycle-count xs)))
+  (should (null (cycle-previous-focus xs)))
+  (should (cycle-contains? 1 xs))
+  (should (cycle-contains? 2 xs))
+  (should (cycle-contains? 3 xs)))
+
+(ert-deftest cycle-contains? ()
+  ;; Returns t or nil
+  (should (eq t (cycle-contains? 1 xs)))
+  (should (eq t (cycle-contains? 2 xs)))
+  (should (eq t (cycle-contains? 3 xs)))
+  (should (eq nil (cycle-contains? 4 xs))))
+
+(ert-deftest cycle-empty? ()
+  (should (eq t (cycle-empty? (cycle-new))))
+  (should (eq nil (cycle-empty? xs))))
+
+(ert-deftest cycle-current ()
+  (should (= 1 (cycle-current xs))))
+
+(ert-deftest cycle-next! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (should (= 2 (cycle-next! xs)))))
+
+(ert-deftest cycle-prev! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (cycle-next! xs)
+    (should (= 1 (cycle-prev! xs)))))
+
+(ert-deftest cycle-previous-focus ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (cycle-focus-item! 2 xs)
+    (cycle-next! xs)
+    (should (= 2 (cycle-previous-focus xs)))))
+
+(ert-deftest cycle-jump! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (should (= 1 (->> xs (cycle-jump! 0) cycle-current)))
+    (should (= 2 (->> xs (cycle-jump! 1) cycle-current)))
+    (should (= 3 (->> xs (cycle-jump! 2) cycle-current)))))
+
+(ert-deftest cycle-focus-previous! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (cycle-focus-item! 2 xs)
+    (cycle-next! xs)
+    (should (= 2 (cycle-previous-focus xs)))
+    (should (= 2 (cycle-focus-previous! xs)))))
+
+(ert-deftest cycle-append! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (cycle-focus-item! 2 xs)
+    (cycle-append! 4 xs)
+    (should (equal '(1 4 2 3) (cycle-xs xs)))))
+
+(ert-deftest cycle-remove! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (should (equal '(1 2) (cycle-xs (cycle-remove! 3 xs))))))
+
+(ert-deftest cycle-misc ()
+  (cycle-focus-item! 3 xs)
+  (cycle-focus-item! 2 xs)
+  (cycle-remove! 1 xs)
+  (should (= 2 (cycle-current xs)))
+  (should (= 3 (cycle-previous-focus xs))))
diff --git a/users/wpcarro/emacs/pkgs/fs/default.nix b/users/wpcarro/emacs/pkgs/fs/default.nix
new file mode 100644
index 0000000000..e6afd107e9
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/fs/default.nix
@@ -0,0 +1,29 @@
+{ pkgs, depot, ... }:
+
+let
+  fs = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "fs";
+        version = "1.0.0";
+        src = ./fs.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+            f
+            s
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    fs
+  ]);
+in
+fs.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/fs/fs.el b/users/wpcarro/emacs/pkgs/fs/fs.el
new file mode 100644
index 0000000000..125c1f1007
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/fs/fs.el
@@ -0,0 +1,47 @@
+;;; fs.el --- Make working with the filesystem easier -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.1"))
+
+;;; Commentary:
+;; Ergonomic alternatives for working with the filesystem.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'dash)
+(require 'f)
+(require 's)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun fs-ensure-file (path)
+  "Ensure that a file and its directories in `PATH' exist.
+Will error for inputs with a trailing slash."
+  (when (s-ends-with? "/" path)
+    (error (format "Input path has trailing slash: %s" path)))
+  (->> path
+       f-dirname
+       fs-ensure-dir)
+  (f-touch path))
+
+(defun fs-ensure-dir (path)
+  "Ensure that a directory and its ancestor directories in `PATH' exist."
+  (->> path
+       f-split
+       (apply #'f-mkdir)))
+
+(defun fs-ls (dir &optional full-path?)
+  "List the files in `DIR' one-level deep.
+Should behave similarly in spirit to the Unix command, ls.
+If `FULL-PATH?' is set, return the full-path of the files."
+  (-drop 2 (directory-files dir full-path?)))
+
+(provide 'fs)
+;;; fs.el ends here
diff --git a/users/wpcarro/emacs/pkgs/fs/tests.el b/users/wpcarro/emacs/pkgs/fs/tests.el
new file mode 100644
index 0000000000..adef11a607
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/fs/tests.el
@@ -0,0 +1,26 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'fs)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest fs-test-ensure-file ()
+  (let ((file "/tmp/file/a/b/c/file.txt"))
+    ;; Ensure this file doesn't exist first to prevent false-positives.
+    (f-delete file t)
+    (fs-ensure-file file)
+    (should (and (f-exists? file)
+                 (f-file? file)))))
+
+(ert-deftest fs-test-ensure-dir ()
+  (let ((dir "/tmp/dir/a/b/c"))
+    ;; Ensure the directory doesn't exist.
+    (f-delete dir t)
+    (fs-ensure-dir dir)
+    (should (and (f-exists? dir)
+                 (f-dir? dir)))))
diff --git a/users/wpcarro/emacs/pkgs/list/README.md b/users/wpcarro/emacs/pkgs/list/README.md
new file mode 100644
index 0000000000..7afa8494fb
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/list/README.md
@@ -0,0 +1,19 @@
+# list.el
+
+Functions for working with lists in Elisp.
+
+## Wish List
+
+Here are some additional functions that I'd like to support.
+
+-  **TODO**: delete_at/2
+-  **TODO**: flatten/1
+-  **TODO**: flatten/2
+-  **TODO**: foldl/3
+-  **TODO**: foldr/3
+-  **TODO**: insert_at/3
+-  **TODO**: pop_at/3
+-  **TODO**: replace_at/3
+-  **TODO**: starts_with?/2
+-  **TODO**: update_at/3
+-  **TODO**: zip/1
diff --git a/users/wpcarro/emacs/pkgs/list/default.nix b/users/wpcarro/emacs/pkgs/list/default.nix
new file mode 100644
index 0000000000..1be0b901eb
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/list/default.nix
@@ -0,0 +1,26 @@
+{ pkgs, depot, ... }:
+
+let
+  list = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "list";
+        version = "1.0.0";
+        src = ./list.el;
+        packageRequires =
+          (with depot.users.wpcarro.emacs.pkgs; [
+            maybe
+            set
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ list ]);
+in
+list.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/list/list.el b/users/wpcarro/emacs/pkgs/list/list.el
new file mode 100644
index 0000000000..18be5f0a71
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/list/list.el
@@ -0,0 +1,219 @@
+;;; list.el --- Functions for working with lists -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Since I prefer having the `list-' namespace, I wrote this module to wrap many
+;; of the functions that are defined in the the global namespace in Elisp.  I
+;; sometimes forget the names of these functions, so it's nice for them to be
+;; organized like this.
+;;
+;; Motivation:
+;; Here are some examples of function names where I prefer more modern
+;; alternatives:
+;; - `car': Return the first element (i.e. "head") of a linked list
+;; - `cdr': Return the tail of a linked list
+
+;; As are most APIs for standard libraries that I write, this is influenced by
+;; Elixir's standard library.
+;;
+;; Similar libraries:
+;; - dash.el: Excellent and widely adopted library for working with lists.
+;; - list-utils.el: Utility library that covers things that dash.el may not
+;;   cover.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'maybe)
+(require 'set)
+(require 'set)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun list-new ()
+  "Return a new, empty list."
+  '())
+
+(defun list-concat (&rest lists)
+  "Joins `LISTS' into on list."
+  (apply #'append lists))
+
+(defun list-duplicate (n x)
+  "Duplicates the given element, X, N times in a list."
+  (list-map (lambda (_) x) (number-sequence 1 n)))
+
+(defun list-join (joint xs)
+  "Join a list of strings, XS, with JOINT."
+  (if (list-empty? xs)
+      ""
+    (list-reduce (list-first xs)
+                 (lambda (x acc)
+                   (format "%s%s%s" acc joint x))
+                 (list-tail xs))))
+
+(defun list-length (xs)
+  "Return the number of elements in `XS'."
+  (length xs))
+
+(defun list-get (i xs)
+  "Return the value in `XS' at `I', or nil."
+  (nth i xs))
+
+(defun list-first (xs &optional default)
+  "Alias for `list-head' for `XS'."
+  (if (list-empty? xs)
+      default
+    (car xs)))
+
+(defun list-last (xs &optional default)
+  "Returns the last element in XS or DEFAULT if empty."
+  (if (list-empty? xs)
+      default
+    (nth (- (length xs) 1) xs)))
+
+(defun list-tail (xs)
+  "Return the tail of `XS'."
+  (cdr xs))
+
+(defun list-reverse (xs)
+  "Reverses `XS'."
+  (reverse xs))
+
+(defun list-cons (x xs)
+  "Add `X' to the head of `XS'."
+  (cons x xs))
+
+(defun list-delete (x xs)
+  "Deletes the given element, X, from XS.
+Returns a new list without X. If X occurs more than once, only the first
+  occurrence is removed."
+  (let ((deleted? nil))
+    (list-reject (lambda (y)
+                   (if deleted? nil
+                     (when (equal x y)
+                       (setq deleted? t) t)))
+                 xs)))
+
+(defun list-filter (p xs)
+  "Return a subset of XS where predicate P returned t."
+  (list--assert-instance xs)
+  (seq-filter p xs))
+
+(defun list-map (f xs)
+  "Call `F' on each element of `XS'."
+  (list--assert-instance xs)
+  (seq-map f xs))
+
+(defun list-reduce (acc f xs)
+  "Return over `XS' calling `F' on an element in `XS'and `ACC'."
+  (list--assert-instance xs)
+  (seq-reduce (lambda (acc x) (funcall f x acc)) xs acc))
+
+(defun list-map-indexed (f xs)
+  "Call `F' on each element of `XS' along with its index."
+  (list-reverse
+   (cdr
+    (list-reduce '(0 . nil)
+                 (lambda (x acc)
+                   (let ((i (car acc))
+                         (result (cdr acc)))
+                     `(,(+ 1 i) . ,(cons (funcall f x i) result))))
+                 xs))))
+
+(defun list-reject (p xs)
+  "Return a subset of XS where predicate of P return nil."
+  (list-filter (lambda (x) (not (funcall p x))) xs))
+
+(defun list-find (p xs)
+  "Return the first x in XS that passes P or nil."
+  (list--assert-instance xs)
+  (seq-find p xs))
+
+(defun list-dedupe-adjacent (xs)
+  "Return XS without adjacent duplicates."
+  (list-reverse
+   (list-reduce (list (list-first xs))
+                (lambda (x acc)
+                  (if (equal x (list-first acc))
+                      acc
+                    (list-cons x acc)))
+                xs)))
+
+(defun list-chunk (n xs)
+  "Chunk XS into lists of size N."
+  (if (> n (length xs))
+      (list xs)
+    (let* ((xs (list-reduce '(:curr () :result ())
+                            (lambda (x acc)
+                              (let ((curr (plist-get acc :curr))
+                                    (result (plist-get acc :result)))
+                                (if (= (- n 1) (length curr))
+                                    `(:curr () :result ,(list-cons (list-reverse (list-cons x curr)) result))
+                                  `(:curr ,(list-cons x curr) :result
+                                          ,result)))) xs))
+           (curr (plist-get xs :curr))
+           (result (plist-get xs :result)))
+      (list-reverse (if curr (list-cons curr result) result)))))
+
+(defun list-wrap (xs)
+  "Wraps XS in a list if it is not a list already."
+  (if (list-instance? xs)
+      xs
+    (list xs)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun list-instance? (xs)
+  "Return t if `XS' is a list.
+Be leery of using this with things like alists.  Many data structures in Elisp
+  are implemented using linked lists."
+  (listp xs))
+
+(defun list-empty? (xs)
+  "Return t if XS are empty."
+  (= 0 (list-length xs)))
+
+(defun list-all? (p xs)
+  "Return t if all `XS' pass the predicate, `P'."
+  (if (list-empty? xs)
+      t
+    (and (maybe-some? (funcall p (car xs)))
+         (list-all? p (cdr xs)))))
+
+(defun list-any? (p xs)
+  "Return t if any `XS' pass the predicate, `P'."
+  (if (list-empty? xs)
+      nil
+    (or (maybe-some? (funcall p (car xs)))
+        (list-any? p (cdr xs)))))
+
+(defun list-contains? (x xs)
+  "Return t if X is in XS using `equal'."
+  (list--assert-instance xs)
+  (maybe-some? (seq-contains-p xs x)))
+
+(defun list-xs-distinct-by? (f xs)
+  "Return t if all elements in XS are distinct after applying F to each."
+  (= (length xs)
+     (set-count (set-from-list (list-map f xs)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helpers
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun list--assert-instance (xs)
+  (unless (list-instance? xs)
+    (error (format "Assertion failed: argument is not a list: %s" xs))))
+
+(provide 'list)
+;;; list.el ends here
diff --git a/users/wpcarro/emacs/pkgs/list/tests.el b/users/wpcarro/emacs/pkgs/list/tests.el
new file mode 100644
index 0000000000..4b45796883
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/list/tests.el
@@ -0,0 +1,107 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'list)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq xs '(1 2 3 4 5))
+
+(ert-deftest list-length ()
+  (should (= 0 (list-length '())))
+  (should (= 5 (list-length xs))))
+
+(ert-deftest list-reduce ()
+  (should (= 16 (list-reduce 1 (lambda (x acc) (+ x acc)) xs))))
+
+(ert-deftest list-map ()
+  (should
+   (equal '(2 4 6 8 10)
+          (list-map (lambda (x) (* x 2)) xs))))
+
+(ert-deftest list-xs-distinct-by? ()
+  (should
+   (equal t (list-xs-distinct-by?
+             (lambda (x) (plist-get x :kbd))
+             '((:kbd "C-a" :name "foo")
+               (:kbd "C-b" :name "foo"))))))
+
+(ert-deftest list-dedupe-adjacent ()
+  (should (equal '(1 2 3 4 3 5)
+                 (list-dedupe-adjacent '(1 1 1 2 2 3 4 4 3 5 5)))))
+
+(ert-deftest list-contains? ()
+  ;; Assert returns t or nil
+  (should (equal t (list-contains? 1 xs)))
+  (should (equal nil (list-contains? 100 xs))))
+
+(ert-deftest list-join ()
+  (should (equal "foo-bar-baz"
+                 (list-join "-" '("foo" "bar" "baz")))))
+
+(ert-deftest list-chunk ()
+  (should (equal '((1 2 3 4 5 6))
+                 (list-chunk 7 '(1 2 3 4 5 6))))
+  (should (equal '((1) (2) (3) (4) (5) (6))
+                 (list-chunk 1 '(1 2 3 4 5 6))))
+  (should (equal '((1 2 3) (4 5 6))
+                 (list-chunk 3 '(1 2 3 4 5 6))))
+  (should (equal '((1 2) (3 4) (5 6))
+                 (list-chunk 2 '(1 2 3 4 5 6)))))
+
+(ert-deftest list-find ()
+  (should (equal 2 (list-find (lambda (x) (= 2 x)) '(1 2 3 4)))))
+
+(ert-deftest list-all? ()
+  (should (equal t (list-all? (lambda (x) (= 2 x)) nil)))
+  (should (null (list-all? (lambda (x) (= 2 x)) '(1 2 3))))
+  (should (equal t (list-all? (lambda (x) (= 2 x)) '(2 2 2 2)))))
+
+(ert-deftest list-any? ()
+  (should (null (list-any? (lambda (x) (= 2 x)) nil)))
+  (should (equal t (list-any? (lambda (x) (= 2 x)) '(1 2 3))))
+  (should (null (list-any? (lambda (x) (= 4 x)) '(1 2 3)))))
+
+(ert-deftest list-duplicate ()
+  (should (equal '() (list-duplicate 0 "hello")))
+  (should (equal '("hi") (list-duplicate 1 "hi")))
+  (should (equal '("bye" "bye") (list-duplicate 2 "bye")))
+  (should (equal '((1 2) (1 2) (1 2)) (list-duplicate 3 '(1 2)))))
+
+(ert-deftest list-first ()
+  (should (null (list-first '())))
+  (should (equal 1 (list-first '() 1)))
+  (should (equal 1 (list-first '(1))))
+  (should (equal 1 (list-first '(1) 2)))
+  (should (equal 1 (list-first '(1 2 3)))))
+
+(ert-deftest list-last ()
+  (should (null (list-last '())))
+  (should (equal 1 (list-last '() 1)))
+  (should (equal 1 (list-last '(1))))
+  (should (equal 1 (list-last '(1) 2)))
+  (should (equal 3 (list-last '(1 2 3)))))
+
+(ert-deftest list-wrap ()
+  (should (equal '("hello") (list-wrap "hello")))
+  (should (equal '(1 2 3) (list-wrap '(1 2 3))))
+  (should (equal '() (list-wrap nil))))
+
+(ert-deftest list-delete ()
+  (should (equal '(b c) (list-delete 'a '(a b c))))
+  (should (equal '(a b c) (list-delete 'd '(a b c))))
+  (should (equal '(a b c) (list-delete 'b '(a b b c))))
+  (should (equal '() (list-delete 'b '()))))
+
+(ert-deftest list-concat ()
+  (should (equal '(1 2 3 4 5) (list-concat '(1) '(2 3) '(4 5))))
+  (should (equal '(1 2 3) (list-concat '() '(1 2 3)))))
+
+;; TODO(wpcarro): Supoprt this.
+;; (ert-deftest list-zip ()
+;;   (should (equal '((1 3 5) (2 4 6)) (list-zip '(1 2) '(3 4) '(5 6))))
+;;   (should (equal '((1 3 5)) (list-zip '(1 2) '(3) '(5 6)))))
diff --git a/users/wpcarro/emacs/pkgs/macros/default.nix b/users/wpcarro/emacs/pkgs/macros/default.nix
new file mode 100644
index 0000000000..d2811ed39f
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/macros/default.nix
@@ -0,0 +1,10 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "macros";
+    version = "1.0.0";
+    src = ./macros.el;
+  })
+{ }
diff --git a/users/wpcarro/emacs/pkgs/macros/macros.el b/users/wpcarro/emacs/pkgs/macros/macros.el
new file mode 100644
index 0000000000..3642686eeb
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/macros/macros.el
@@ -0,0 +1,45 @@
+;;; macros.el --- Helpful variables for making my ELisp life more enjoyable -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; This file contains helpful variables that I use in my ELisp development.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro macros-enable (mode)
+  "Helper for enabling `MODE'.
+Useful in `add-hook' calls.  Some modes, like `linum-mode' need to be called as
+`(linum-mode 1)', so `(add-hook mode #'linum-mode)' won't work."
+  `#'(lambda nil (,mode 1)))
+
+(defmacro macros-disable (mode)
+  "Helper for disabling `MODE'.
+Useful in `add-hook' calls."
+  `#'(lambda nil (,mode -1)))
+
+(defmacro macros-add-hook-before-save (mode f)
+  "Register a hook, `F', for a mode, `MODE' more conveniently.
+Usage: (macros-add-hook-before-save 'reason-mode-hook #'refmt-before-save)"
+  `(add-hook ,mode
+             (lambda ()
+               (add-hook 'before-save-hook ,f))))
+
+(defmacro macros-comment (&rest _)
+  "Empty comment s-expresion where `BODY' is ignored."
+  `nil)
+
+(defmacro macros-support-file-extension (ext mode)
+  "Register MODE to automatically load with files ending with EXT extension.
+Usage: (macros-support-file-extension \"pb\" protobuf-mode)"
+  (let ((extension (format "\\.%s\\'" ext)))
+    `(add-to-list 'auto-mode-alist '(,extension . ,mode))))
+
+(provide 'macros)
+;;; macros.el ends here
diff --git a/users/wpcarro/emacs/pkgs/math/default.nix b/users/wpcarro/emacs/pkgs/math/default.nix
new file mode 100644
index 0000000000..9167d61d4e
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/math/default.nix
@@ -0,0 +1,30 @@
+{ pkgs, depot, ... }:
+
+let
+  math = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "math";
+        version = "1.0.0";
+        src = ./math.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+          ]) ++
+          (with depot.users.wpcarro.emacs.pkgs; [
+            maybe
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    math
+  ]);
+in
+math.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/math.el b/users/wpcarro/emacs/pkgs/math/math.el
index 4013ce3be2..dbc527928a 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/math.el
+++ b/users/wpcarro/emacs/pkgs/math/math.el
@@ -13,6 +13,7 @@
 ;; Dependencies
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+(require 'dash)
 (require 'maybe)
 (require 'cl-lib)
 
@@ -31,13 +32,13 @@
 ;; Int -> Int -> Int -> Boolean
 (cl-defun math-triangle-of-power (&key base power result)
   (cond
-   ((maybe-somes? base power result)
+   ((-all? #'maybe-some? (list base power result))
     (error "All three arguments should not be set"))
-   ((maybe-somes? power result)
+   ((-all? #'maybe-some? (list power result))
     (message "power and result"))
-   ((maybe-somes? base result)
+   ((-all? #'maybe-some? (list base result))
     (log result base))
-   ((maybe-somes? base power)
+   ((-all? #'maybe-some? (list base power))
     (expt base power))
    (t
     (error "Two of the three arguments must be set"))))
diff --git a/users/wpcarro/emacs/pkgs/math/tests.el b/users/wpcarro/emacs/pkgs/math/tests.el
new file mode 100644
index 0000000000..ef3430c913
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/math/tests.el
@@ -0,0 +1,25 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'math)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest math-mod ()
+  (should (= 0 (math-mod 9 3)))
+  (should (= 4 (math-mod 9 5))))
+
+(ert-deftest math-exp ()
+  (should (= 9 (math-exp 3 2)))
+  (should (= 8 (math-exp 2 3))))
+
+(ert-deftest math-round ()
+  (should (= 10 (math-round 9.5)))
+  (should (= 9 (math-round 9.45))))
+
+(ert-deftest math-floor ()
+  (should (= 9 (math-floor 9.5))))
diff --git a/users/wpcarro/emacs/pkgs/maybe/default.nix b/users/wpcarro/emacs/pkgs/maybe/default.nix
new file mode 100644
index 0000000000..68e058b42b
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/maybe/default.nix
@@ -0,0 +1,24 @@
+{ pkgs, depot, ... }:
+
+let
+  maybe = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "maybe";
+        version = "1.0.0";
+        src = ./maybe.el;
+        packageRequires = [ ];
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    maybe
+  ]);
+in
+maybe.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/maybe/maybe.el b/users/wpcarro/emacs/pkgs/maybe/maybe.el
new file mode 100644
index 0000000000..581568d8cc
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/maybe/maybe.el
@@ -0,0 +1,54 @@
+;;; maybe.el --- Library for dealing with nil values -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Inspired by Elm's Maybe library.
+;;
+;; For now, a Nothing value will be defined exclusively as a nil value.  I'm
+;; uninterested in supported falsiness in this module even at risk of going
+;; against the LISP grain.
+;;
+;; I'm avoiding introducing a struct to handle the creation of Just and Nothing
+;; variants of Maybe.  Perhaps this is a mistake in which case this file would
+;; be more aptly named nil.el.  I may change that.  Because of this limitation,
+;; functions in Elm's Maybe library like andThen, which is the monadic bind for
+;; the Maybe type, doesn't have a home here since we cannot compose multiple
+;; Nothing or Just values without a struct or some other construct.
+;;
+;; Possible names for the variants of a Maybe.
+;; None    | Some
+;; Nothing | Something
+;; None    | Just
+;; Nil     | Set
+;;
+;; NOTE: In Elisp, values like '() (i.e. the empty list) are aliases for nil.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun maybe-nil? (x)
+  "Return t if X is nil."
+  (null x))
+
+(defun maybe-some? (x)
+  "Return t when X is non-nil."
+  (not (maybe-nil? x)))
+
+(defun maybe-default (default x)
+  "Return DEFAULT when X is nil."
+  (if (maybe-nil? x) default x))
+
+(defun maybe-map (f x)
+  "Apply F to X if X is not nil."
+  (if (maybe-some? x)
+      (funcall f x)
+    x))
+
+(provide 'maybe)
+;;; maybe.el ends here
diff --git a/users/wpcarro/emacs/pkgs/maybe/tests.el b/users/wpcarro/emacs/pkgs/maybe/tests.el
new file mode 100644
index 0000000000..c0463cc65a
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/maybe/tests.el
@@ -0,0 +1,25 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'maybe)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest maybe-nil? ()
+  (should (maybe-nil? nil))
+  (should (not (maybe-nil? t))))
+
+(ert-deftest maybe-some? ()
+  (should (maybe-some? '(1 2 3)))
+  (should (not (maybe-some? nil))))
+
+(ert-deftest maybe-default ()
+  (should (string= "some" (maybe-default "some" nil)))
+  (should (= 10 (maybe-default 1 10))))
+
+(ert-deftest maybe-map ()
+  (should (eq nil (maybe-map (lambda (x) (* x 2)) nil)))
+  (should (= 4 (maybe-map (lambda (x) (* x 2)) 2))))
diff --git a/users/wpcarro/emacs/pkgs/passage/README.md b/users/wpcarro/emacs/pkgs/passage/README.md
new file mode 100644
index 0000000000..51f7bd6efd
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/passage/README.md
@@ -0,0 +1,12 @@
+# passage.el
+
+Emacs support for `passage`.
+
+## Alternative Packages
+
+If you're looking for more feature-complete, configurable alternatives,
+check-out the following packages:
+
+- `ivy-pass.el`
+- `password-store.el`
+- `pass.el`
diff --git a/users/wpcarro/emacs/pkgs/passage/default.nix b/users/wpcarro/emacs/pkgs/passage/default.nix
new file mode 100644
index 0000000000..ac87f193b4
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/passage/default.nix
@@ -0,0 +1,12 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "passage";
+    version = "1.0.0";
+    src = ./passage.el;
+    packageRequires = (with emacsPackages; [ dash f s ]);
+  }
+  )
+{ }
diff --git a/users/wpcarro/emacs/pkgs/passage/passage.el b/users/wpcarro/emacs/pkgs/passage/passage.el
new file mode 100644
index 0000000000..4a43920e0b
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/passage/passage.el
@@ -0,0 +1,65 @@
+;;; passage.el --- Emacs passage support -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022-2023 William Carroll <wpcarro@gmail.com>
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 1.0.0
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package provides functions for working with passage.
+
+;;; Code:
+
+(require 'dash)
+(require 'f)
+(require 's)
+
+(defgroup passage nil
+  "Customization options for `passage'."
+  :prefix "passage-"
+  :group 'vterm)
+
+(defcustom passage-store
+  "~/.passage/store"
+  "Path to the passage store directory."
+  :type 'string
+  :group 'passage)
+
+(defcustom passage-executable
+  (or (executable-find "passage")
+      "/nix/store/jgffkfdiiwiqa4zqpxn3691mx9xc6axa-passage-unstable-2022-05-01/bin/passage")
+  "Path to passage executable."
+  :type 'string
+  :group 'passage)
+
+(defun passage-select ()
+  "Select an entry and copy its password to the kill ring."
+  (interactive)
+  (let ((key (completing-read "Copy password of entry: "
+                              (-map (lambda (x)
+                                      (f-no-ext (f-relative x passage-store)))
+                                    (f-files passage-store nil t)))))
+    (kill-new
+     (s-trim-right
+      (shell-command-to-string
+       (format "%s show %s | head -1" passage-executable key))))
+    (message "[passage.el] Copied \"%s\"!" key)))
+
+(provide 'passage)
+;;; passage.el ends here
diff --git a/users/wpcarro/emacs/pkgs/set/default.nix b/users/wpcarro/emacs/pkgs/set/default.nix
new file mode 100644
index 0000000000..319ba92744
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/set/default.nix
@@ -0,0 +1,32 @@
+{ pkgs, depot, ... }:
+
+let
+  set = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "set";
+        version = "1.0.0";
+        src = ./set.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+            ht
+          ]) ++
+          (with depot.users.wpcarro.emacs.pkgs; [
+            struct
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    epkgs.dash
+    set
+  ]);
+in
+set.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/set/set.el b/users/wpcarro/emacs/pkgs/set/set.el
new file mode 100644
index 0000000000..2d6e14917a
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/set/set.el
@@ -0,0 +1,116 @@
+;;; set.el --- Working with mathematical sets -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; The set data structure is a collection that deduplicates its elements.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'cl-lib)
+(require 'dash)
+(require 'ht) ;; friendlier API for hash-tables
+(require 'struct)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Wish List
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; - TODO: Support enum protocol for set.
+;; - TODO: Prefer a different hash-table library that doesn't rely on mutative
+;;   code.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct set xs)
+
+(defun set-from-list (xs)
+  "Create a new set from the list XS."
+  (make-set :xs (->> xs
+                     (-map (lambda (x) (cons x nil)))
+                     ht-from-alist)))
+
+(defun set-new (&rest args)
+  "Create a new set from ARGS."
+  (set-from-list args))
+
+(defun set-to-list (xs)
+  "Map set XS into a list."
+  (->> xs
+       set-xs
+       ht-keys))
+
+(defun set-add (x xs)
+  "Add X to set XS."
+  (struct-update set
+                 xs
+                 (lambda (table)
+                   (let ((table-copy (ht-copy table)))
+                     (ht-set table-copy x nil)
+                     table-copy))
+                 xs))
+
+;; TODO: Ensure all `*/reduce' functions share the same API.
+(defun set-reduce (acc f xs)
+  "Return a new set by calling F on each element of XS and ACC."
+  (->> xs
+       set-to-list
+       (-reduce-from (lambda (acc x) (funcall f x acc)) acc)))
+
+(defun set-intersection (a b)
+  "Return the set intersection between A and B."
+  (set-reduce (set-new)
+              (lambda (x acc)
+                (if (set-contains? x b)
+                    (set-add x acc)
+                  acc))
+              a))
+
+(defun set-count (xs)
+  "Return the number of elements in XS."
+  (->> xs
+       set-xs
+       ht-size))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun set-empty? (xs)
+  "Return t if XS has no elements in it."
+  (= 0 (set-count xs)))
+
+(defun set-contains? (x xs)
+  "Return t if set XS has X."
+  (ht-contains? (set-xs xs) x))
+
+;; TODO: Prefer using `ht.el' functions for this.
+(defun set-equal? (a b)
+  "Return t if A and B share the name members."
+  (ht-equal? (set-xs a)
+             (set-xs b)))
+
+(defun set-distinct? (a b)
+  "Return t if A and B have no shared members."
+  (set-empty? (set-intersection a b)))
+
+(defun set-superset? (a b)
+  "Return t if A has all of the members of B."
+  (->> b
+       set-to-list
+       (-all? (lambda (x) (set-contains? x a)))))
+
+(defun set-subset? (a b)
+  "Return t if each member of set A is present in set B."
+  (set-superset? b a))
+
+(provide 'set)
+;;; set.el ends here
diff --git a/users/wpcarro/emacs/pkgs/set/tests.el b/users/wpcarro/emacs/pkgs/set/tests.el
new file mode 100644
index 0000000000..7f5c2ae3ff
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/set/tests.el
@@ -0,0 +1,69 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'dash)
+(require 'set)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest set-from-list ()
+  (should (equal '(1 2 3)
+                 (->> '(1 2 3 1 2 3)
+                      set-from-list
+                      set-to-list))))
+
+(ert-deftest set-distinct? ()
+  (should (set-distinct? (set-new 'one 'two 'three)
+                         (set-new 'a 'b 'c)))
+  (should (not
+           (set-distinct? (set-new 1 2 3)
+                          (set-new 3 4 5))))
+  (should (not
+           (set-distinct? (set-new 1 2 3)
+                          (set-new 1 2 3)))))
+
+(ert-deftest set-equal? ()
+  (should (not (set-equal? (set-new 'a 'b 'c)
+                           (set-new 'x 'y 'z))))
+  (should (not (set-equal? (set-new 'a 'b 'c)
+                           (set-new 'a 'b))))
+  (should (set-equal? (set-new 'a 'b 'c)
+                      (set-new 'a 'b 'c))))
+
+(ert-deftest set-intersection ()
+  (should (set-equal? (set-new 2 3)
+                      (set-intersection (set-new 1 2 3)
+                                        (set-new 2 3 4)))))
+
+(ert-deftest set-to/from-list ()
+  (should (equal '(1 2 3)
+                 (->> '(1 1 2 2 3 3)
+                      set-from-list
+                      set-to-list))))
+
+(ert-deftest set-subset? ()
+  (should (not (set-subset? (set-new "black" "grey")
+                            (set-new "red" "green" "blue"))))
+  (should (set-subset? (set-new "red")
+                       (set-new "red" "green" "blue"))))
+
+(ert-deftest set-superset? ()
+  (let ((primary-colors (set-new "red" "green" "blue")))
+    (should (not (set-superset? primary-colors
+                                (set-new "black" "grey"))))
+    (should (set-superset? primary-colors
+                           (set-new "red" "green" "blue")))
+    (should (set-superset? primary-colors
+                           (set-new "red" "blue")))))
+
+(ert-deftest set-empty? ()
+  (should (set-empty? (set-new)))
+  (should (not (set-empty? (set-new 1 2 3)))))
+
+(ert-deftest set-count ()
+  (should (= 0 (set-count (set-new))))
+  (should (= 2 (set-count (set-new 1 1 2 2)))))
diff --git a/users/wpcarro/emacs/pkgs/string/default.nix b/users/wpcarro/emacs/pkgs/string/default.nix
new file mode 100644
index 0000000000..406cccdfcb
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/string/default.nix
@@ -0,0 +1,27 @@
+{ pkgs, depot, ... }:
+
+let
+  string = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "string";
+        version = "1.0.0";
+        src = ./string.el;
+        packageRequires = [
+          emacsPackages.dash
+          emacsPackages.s
+        ];
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    string
+  ]);
+in
+string.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/string.el b/users/wpcarro/emacs/pkgs/string/string.el
index 7e3f10c75a..30da1805e8 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/string.el
+++ b/users/wpcarro/emacs/pkgs/string/string.el
@@ -5,7 +5,7 @@
 ;; Package-Requires: ((emacs "24"))
 
 ;;; Commentary:
-;; Library for working with string.
+;; Library for working with strings.
 
 ;;; Code:
 
@@ -15,31 +15,15 @@
 
 (require 's)
 (require 'dash)
-;; TODO: Resolve the circular dependency that this introduces.
-;; (require 'prelude)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Library
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(defun string-contains? (c x)
-  "Return t if X is in C."
-  (s-contains? c x))
-
-(defun string-hookify (x)
-  "Append \"-hook\" to X."
-  (s-append "-hook" x))
-
 (defun string-split (y x)
   "Map string X into a list of strings that were separated by Y."
   (s-split y x))
 
-(defun string-ensure-hookified (x)
-  "Ensure that X has \"-hook\" appended to it."
-  (if (s-ends-with? "-hook" x)
-      x
-    (string-hookify x)))
-
 (defun string-format (x &rest args)
   "Format template string X with ARGS."
   (apply #'format (cons x args)))
@@ -48,11 +32,11 @@
   "Joins `STRINGS' into onto string."
   (apply #'s-concat strings))
 
-(defun string-->symbol (string)
+(defun string-to-symbol (string)
   "Maps `STRING' to a symbol."
   (intern string))
 
-(defun string-<-symbol (symbol)
+(defun string-from-symbol (symbol)
   "Maps `SYMBOL' into a string."
   (symbol-name symbol))
 
@@ -106,5 +90,9 @@
   "Return t if X is a string."
   (stringp x))
 
+(defun string-contains? (c x)
+  "Return t if X is in C."
+  (s-contains? c x))
+
 (provide 'string)
 ;;; string.el ends here
diff --git a/users/wpcarro/emacs/pkgs/string/tests.el b/users/wpcarro/emacs/pkgs/string/tests.el
new file mode 100644
index 0000000000..351e305466
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/string/tests.el
@@ -0,0 +1,22 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest string-caps->kebab ()
+  (should (string= "foo-bar-baz" (string-caps->kebab "FOO_BAR_BAZ"))))
+
+(ert-deftest string-kebab->caps ()
+  (should (string= "FOO_BAR_BAZ" (string-kebab->caps "foo-bar-baz"))))
+
+(ert-deftest string-lower->caps ()
+  (should (string= "FOO_BAR_BAZ" (string-lower->caps "foo bar baz"))))
+
+(ert-deftest string-lower->kebab ()
+  (should (string= "foo-bar-baz" (string-lower->kebab "foo bar baz"))))
diff --git a/users/wpcarro/emacs/pkgs/struct/README.md b/users/wpcarro/emacs/pkgs/struct/README.md
new file mode 100644
index 0000000000..34dac6614c
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/struct/README.md
@@ -0,0 +1,6 @@
+# struct.el
+
+[![Build status](https://badge.buildkite.com/016bff4b8ae2704a3bbbb0a250784e6692007c582983b6dea7.svg?branch=refs/heads/canon)](https://buildkite.com/tvl/depot)
+
+Provides new macros exposing immutable and mutable interfaces for working with
+structs in Elisp.
diff --git a/users/wpcarro/emacs/pkgs/struct/default.nix b/users/wpcarro/emacs/pkgs/struct/default.nix
new file mode 100644
index 0000000000..558ebd0a3d
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/struct/default.nix
@@ -0,0 +1,29 @@
+{ pkgs, depot, ... }:
+
+let
+  struct = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "struct";
+        version = "1.0.0";
+        src = ./struct.el;
+        packageRequires = [ ];
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    struct
+  ]);
+in
+struct.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+  passthru.meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+    filter = ":/users/wpcarro/emacs/pkgs/struct";
+    remote = "git@github.com:wpcarro/struct.el.git";
+    ref = "refs/heads/canon";
+  };
+})
diff --git a/users/wpcarro/emacs/pkgs/struct/struct.el b/users/wpcarro/emacs/pkgs/struct/struct.el
new file mode 100644
index 0000000000..5d6572bf6d
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/struct/struct.el
@@ -0,0 +1,65 @@
+;;; struct.el --- Helpers for working with structs -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 1.0.0
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Provides new macros for working with structs.  Also provides adapter
+;; interfaces to existing struct macros, that should have more intuitive
+;; interfaces.
+;;
+;; Sometimes `setf' just isn't enough.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro struct-update (type field f xs)
+  "Apply F to FIELD in XS, which is a struct of TYPE.
+This is immutable."
+  (let ((copier (struct--copier-for type))
+        (accessor (struct--accessor-for type field)))
+    `(let ((copy (,copier ,xs)))
+       (setf (,accessor copy) (funcall ,f (,accessor copy)))
+       copy)))
+
+(defmacro struct-update! (type field f xs)
+  "Mutably apply F to FIELD in XS."
+  (let ((accessor (struct--accessor-for type field)))
+    `(progn
+       (setf (,accessor ,xs) (funcall ,f (,accessor ,xs)))
+       ,xs)))
+
+(defmacro struct-set (type field x xs)
+  "Immutably set FIELD in XS (struct TYPE) to X."
+  (let ((copier (struct--copier-for type))
+        (accessor (struct--accessor-for type field)))
+    `(let ((copy (,copier ,xs)))
+       (setf (,accessor copy) ,x)
+       copy)))
+
+(defmacro struct-set! (type field x xs)
+  "Set FIELD in XS (struct TYPE) to X mutably.
+This is an adapter interface to `setf'."
+  (let ((accessor (struct--accessor-for type field)))
+    `(progn
+       (setf (,accessor ,xs) ,x)
+       ,xs)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helper Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun struct--copier-for (type)
+  (intern (format "copy-%s" (symbol-name type))))
+
+(defun struct--accessor-for (type field)
+  (intern (format "%s-%s"
+                  (symbol-name type)
+                  (symbol-name field))))
+
+(provide 'struct)
+;;; struct.el ends here
diff --git a/users/wpcarro/emacs/pkgs/struct/tests.el b/users/wpcarro/emacs/pkgs/struct/tests.el
new file mode 100644
index 0000000000..a7ddb52c46
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/struct/tests.el
@@ -0,0 +1,44 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'struct)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct dummy name age)
+
+(ert-deftest struct-update ()
+  (let* ((test (make-dummy :name "Roofus" :age 19))
+         (result (struct-update dummy name #'upcase test)))
+    ;; test
+    (should (string= "Roofus" (dummy-name test)))
+    (should (= 19 (dummy-age test)))
+    ;; result
+    (should (string= "ROOFUS" (dummy-name result)))
+    (should (= 19 (dummy-age result)))))
+
+(ert-deftest struct-update! ()
+  (let ((test (make-dummy :name "Roofus" :age 19)))
+    (struct-update! dummy name #'upcase test)
+    (should (string= "ROOFUS" (dummy-name test)))
+    (should (= 19 (dummy-age test)))))
+
+(ert-deftest struct-set ()
+  (let* ((test (make-dummy :name "Roofus" :age 19))
+         (result (struct-set dummy name "Shoofus" test)))
+    ;; test
+    (should (string= "Roofus" (dummy-name test)))
+    (should (= 19 (dummy-age test)))
+    ;; result
+    (should (string= "Shoofus" (dummy-name result)))
+    (should (= 19 (dummy-age result)))))
+
+(ert-deftest struct-set! ()
+  (let ((test (make-dummy :name "Roofus" :age 19)))
+    (struct-set! dummy name "Doofus" test)
+    (should (string= "Doofus" (dummy-name test)))
+    (should (= 19 (dummy-age test)))))
diff --git a/users/wpcarro/emacs/pkgs/symbol/default.nix b/users/wpcarro/emacs/pkgs/symbol/default.nix
new file mode 100644
index 0000000000..9334697e32
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/symbol/default.nix
@@ -0,0 +1,24 @@
+{ pkgs, depot, ... }:
+
+let
+  symbol = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "symbol";
+        version = "1.0.0";
+        src = ./symbol.el;
+        packageRequires = [ ];
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    symbol
+  ]);
+in
+symbol.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/symbol/symbol.el b/users/wpcarro/emacs/pkgs/symbol/symbol.el
new file mode 100644
index 0000000000..4b16351831
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/symbol/symbol.el
@@ -0,0 +1,38 @@
+;;; symbol.el --- Library for working with symbols -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Library for working with symbols.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun symbol-to-string (symbol)
+  "Map `SYMBOL' into a string."
+  (symbol-name symbol))
+
+(defun symbol-from-string (string)
+  "Map `STRING' into a symbol."
+  (intern string))
+
+(defun symbol-as-string (f x)
+  "Treat the symbol, X, as a string while applying F to it.
+Coerce back to a symbol on the way out."
+  (symbol-from-string (funcall f (symbol-to-string x))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun symbol-instance? (x)
+  "Return t if X is a symbol."
+  (symbolp x))
+
+(provide 'symbol)
+;;; symbol.el ends here
diff --git a/users/wpcarro/emacs/pkgs/symbol/tests.el b/users/wpcarro/emacs/pkgs/symbol/tests.el
new file mode 100644
index 0000000000..b10362b162
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/symbol/tests.el
@@ -0,0 +1,22 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'symbol)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest symbol-to-string ()
+  (should (string= "foo" (symbol-to-string 'foo))))
+
+(ert-deftest symbol-from-string ()
+  (should (eq 'foo (symbol-from-string "foo"))))
+
+(ert-deftest symbol-as-string ()
+  (should (eq 'foo-hook
+              (symbol-as-string
+               (lambda (x) (format "%s-hook" x))
+               'foo))))
diff --git a/users/wpcarro/emacs/pkgs/theme/default.nix b/users/wpcarro/emacs/pkgs/theme/default.nix
new file mode 100644
index 0000000000..aea6394369
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/theme/default.nix
@@ -0,0 +1,14 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "theme";
+    version = "1.0.0";
+    src = ./theme.el;
+    packageRequires =
+      (with depot.users.wpcarro.emacs.pkgs; [
+        cycle
+      ]);
+  })
+{ }
diff --git a/users/wpcarro/emacs/pkgs/theme/theme.el b/users/wpcarro/emacs/pkgs/theme/theme.el
new file mode 100644
index 0000000000..32f2c89a4d
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/theme/theme.el
@@ -0,0 +1,78 @@
+;;; theme.el --- Colors and stuff -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;;
+;; Cycle through a whitelist of themes.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'cycle)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defgroup theme nil
+  "Customization options for `theme'."
+  :group 'theme)
+
+(defcustom theme-whitelist
+  (cycle-from-list (custom-available-themes))
+  "The whitelist of themes through which to cycle."
+  :type '(cycle symbol)
+  :group 'theme)
+
+(defcustom theme-after-change
+  nil
+  "Hook invoked after a new theme is loaded"
+  :type 'hook
+  :group 'theme)
+
+(defun theme-whitelist-set (theme)
+  "Focus the THEME in the `theme-whitelist' cycle."
+  (cycle-focus! (lambda (x) (equal x theme)) theme-whitelist)
+  (theme--set (cycle-current theme-whitelist)))
+
+(defun theme-select ()
+  "Load a theme using `completing-read'."
+  (interactive)
+  (let ((theme (completing-read "Theme: " (cycle-to-list theme-whitelist))))
+    (theme--disable-all)
+    (theme--set (intern theme))))
+
+(defun theme-next ()
+  "Disable the currently active theme and load the next theme."
+  (interactive)
+  (disable-theme (cycle-current theme-whitelist))
+  (theme--set (cycle-next! theme-whitelist))
+  (message (format "Active theme: %s" (cycle-current theme-whitelist))))
+
+(defun theme-prev ()
+  "Disable the currently active theme and load the previous theme."
+  (interactive)
+  (disable-theme (cycle-current theme-whitelist))
+  (theme--set (cycle-prev! theme-whitelist))
+  (message (format "Active theme: %s" (cycle-current theme-whitelist))))
+
+(defun theme--disable-all ()
+  "Disable all currently enabled themes."
+  (interactive)
+  (dolist (x custom-enabled-themes)
+    (disable-theme x)))
+
+(defun theme--set (theme)
+    "Call `load-theme' with `THEME', ensuring that the line numbers are bright.
+There is no hook that I'm aware of to handle this more elegantly."
+    (load-theme theme t)
+    (run-hooks 'theme-after-change))
+
+(provide 'theme)
+;;; theme.el ends here
diff --git a/users/wpcarro/emacs/pkgs/tuple/default.nix b/users/wpcarro/emacs/pkgs/tuple/default.nix
new file mode 100644
index 0000000000..0626370e47
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/tuple/default.nix
@@ -0,0 +1,10 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "tuple";
+    version = "1.0.0";
+    src = ./tuple.el;
+  })
+{ }
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/tuple.el b/users/wpcarro/emacs/pkgs/tuple/tuple.el
index 848c6fa48b..848c6fa48b 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/tuple.el
+++ b/users/wpcarro/emacs/pkgs/tuple/tuple.el
diff --git a/users/wpcarro/emacs/pkgs/vector/default.nix b/users/wpcarro/emacs/pkgs/vector/default.nix
new file mode 100644
index 0000000000..c0a475aaaa
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vector/default.nix
@@ -0,0 +1,21 @@
+{ pkgs, depot, ... }:
+
+let
+  vector = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "vector";
+        version = "1.0.0";
+        src = ./vector.el;
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ vector ]);
+in
+vector.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/vector/tests.el b/users/wpcarro/emacs/pkgs/vector/tests.el
new file mode 100644
index 0000000000..ffa9831882
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vector/tests.el
@@ -0,0 +1,20 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'vector)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest vector-misc-tests ()
+  (let ((xs [1 2 3])
+        (ys [1 2 3]))
+    (should (= 1 (vector-get 0 ys)))
+    (vector-set 0 4 ys)
+    (should (= 1 (vector-get 0 ys)))
+    (should (= 1 (vector-get 0 xs)))
+    (vector-set! 0 4 xs)
+    (should (= 4 (vector-get 0 xs)))))
diff --git a/users/wpcarro/emacs/pkgs/vector/vector.el b/users/wpcarro/emacs/pkgs/vector/vector.el
new file mode 100644
index 0000000000..87f38d7d93
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vector/vector.el
@@ -0,0 +1,58 @@
+;;; vector.el --- Working with Elisp's Vector data type -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; It might be best to think of Elisp vectors as tuples in languages like
+;; Haskell or Erlang.
+;;
+;; Not surprisingly, this API is modelled after Elixir's Tuple API.
+;;
+;; Some Elisp trivia:
+;; - "Array": Usually means vector or string.
+;; - "Sequence": Usually means list or "array" (see above).
+;;
+;; It might be a good idea to think of Array and Sequence as typeclasses in
+;; Elisp.  This is perhaps more similar to Elixir's notion of the Enum protocol.
+;;
+;; Intentionally not supporting a to-list function, because tuples can contain
+;; heterogenous types whereas lists should contain homogenous types.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun vector-concat (&rest args)
+  "Return a new vector composed of all vectors in `ARGS'."
+  (apply #'vconcat args))
+
+(defun vector-prepend (x xs)
+  "Add `X' to the beginning of `XS'."
+  (vector-concat `[,x] xs))
+
+(defun vector-append (x xs)
+  "Add `X' to the end of `XS'."
+  (vector-concat xs `[,x]))
+
+(defun vector-get (i xs)
+  "Return the value in `XS' at index, `I'."
+  (aref xs i))
+
+(defun vector-set (i v xs)
+  "Set index `I' to value `V' in `XS'.
+Returns a copy of `XS' with the updates."
+  (let ((copy (vconcat [] xs)))
+    (aset copy i v)
+    copy))
+
+(defun vector-set! (i v xs)
+  "Set index `I' to value `V' in `XS'.
+This function mutates XS."
+  (aset xs i v))
+
+(provide 'vector)
+;;; vector.el ends here
diff --git a/users/wpcarro/emacs/pkgs/vterm-mgt/README.md b/users/wpcarro/emacs/pkgs/vterm-mgt/README.md
new file mode 100644
index 0000000000..b855826929
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vterm-mgt/README.md
@@ -0,0 +1,17 @@
+# vterm-mgt.el
+
+[![Build status](https://badge.buildkite.com/016bff4b8ae2704a3bbbb0a250784e6692007c582983b6dea7.svg?branch=refs/heads/canon)](https://buildkite.com/tvl/depot)
+
+[emacs-libvterm](https://github.com/akermu/emacs-libvterm) is a feature-complete
+terminal emulator inside Emacs based on libvterm.
+
+`vterm-mgt.el`, adds functionality on top of `vterm` to allow you to:
+
+* find-or-create `vterm` instances
+* fuzzily switch between existing `vterm` buffers
+* cycle through existing `vterm` instances
+* easily rename `vterm` buffers
+
+## Alternatives to vterm-mgt.el
+
+* [multi-vterm](https://github.com/suonlight/multi-vterm)
diff --git a/users/wpcarro/emacs/pkgs/vterm-mgt/default.nix b/users/wpcarro/emacs/pkgs/vterm-mgt/default.nix
new file mode 100644
index 0000000000..88eb550204
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vterm-mgt/default.nix
@@ -0,0 +1,19 @@
+{ pkgs, depot, ... }:
+
+pkgs.emacsPackages.trivialBuild {
+  pname = "vterm-mgt";
+  version = "1.0.0";
+  src = ./vterm-mgt.el;
+  packageRequires =
+    (with pkgs.emacsPackages; [
+      vterm
+    ]) ++
+    (with depot.users.wpcarro.emacs.pkgs; [
+      cycle
+    ]);
+  passthru.meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+    filter = ":/users/wpcarro/emacs/pkgs/vterm-mgt";
+    remote = "git@github.com:wpcarro/vterm-mgt.el.git";
+    ref = "refs/heads/canon";
+  };
+}
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/vterm-mgt.el b/users/wpcarro/emacs/pkgs/vterm-mgt/vterm-mgt.el
index ec9a04d1c8..c082e54a59 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/vterm-mgt.el
+++ b/users/wpcarro/emacs/pkgs/vterm-mgt/vterm-mgt.el
@@ -22,16 +22,17 @@
 ;; Dependencies
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(require 'dash)
 (require 'cycle)
 (require 'vterm)
+(require 'seq)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Configuration
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (defgroup vterm-mgt nil
-  "Customization options for `vterm-mgt'.")
+  "Customization options for `vterm-mgt'."
+  :group 'vterm)
 
 (defcustom vterm-mgt-scroll-on-focus nil
   "When t, call `end-of-buffer' after focusing a vterm instance."
@@ -45,9 +46,10 @@
   "Return t if the buffer B is a vterm instance."
   (equal 'vterm-mode (buffer-local-value 'major-mode b)))
 
-(defmacro vterm-mgt--assert-vterm-buffer ()
+(defun vterm-mgt--assert-vterm-buffer ()
   "Error when the `current-buffer' is not a vterm buffer."
-  '(prelude-assert (vterm-mgt--instance? (current-buffer))))
+  (unless (vterm-mgt--instance? (current-buffer))
+    (error "Current buffer is not a vterm buffer")))
 
 (defun vterm-mgt-next ()
   "Replace the current buffer with the next item in `vterm-mgt--instances'.
@@ -55,8 +57,8 @@ This function should be called from a buffer running vterm."
   (interactive)
   (vterm-mgt--assert-vterm-buffer)
   (vterm-mgt-reconcile-state)
-  (cycle-focus-item (current-buffer) vterm-mgt--instances)
-  (switch-to-buffer (cycle-next vterm-mgt--instances))
+  (cycle-focus-item! (current-buffer) vterm-mgt--instances)
+  (switch-to-buffer (cycle-next! vterm-mgt--instances))
   (when vterm-mgt-scroll-on-focus (end-of-buffer)))
 
 (defun vterm-mgt-prev ()
@@ -65,8 +67,8 @@ This function should be called from a buffer running vterm."
   (interactive)
   (vterm-mgt--assert-vterm-buffer)
   (vterm-mgt-reconcile-state)
-  (cycle-focus-item (current-buffer) vterm-mgt--instances)
-  (switch-to-buffer (cycle-prev vterm-mgt--instances))
+  (cycle-focus-item! (current-buffer) vterm-mgt--instances)
+  (switch-to-buffer (cycle-prev! vterm-mgt--instances))
   (when vterm-mgt-scroll-on-focus (end-of-buffer)))
 
 (defun vterm-mgt-instantiate ()
@@ -81,8 +83,8 @@ If however you must call `vterm', if you'd like to cycle through vterm
   (interactive)
   (vterm-mgt-reconcile-state)
   (let ((buffer (vterm t)))
-    (cycle-append buffer vterm-mgt--instances)
-    (cycle-focus-item buffer vterm-mgt--instances)))
+    (cycle-append! buffer vterm-mgt--instances)
+    (cycle-focus-item! buffer vterm-mgt--instances)))
 
 (defun vterm-mgt-kill ()
   "Kill the current buffer and remove it from `vterm-mgt--instances'.
@@ -106,15 +108,15 @@ instance."
     (if (cycle-focused? vterm-mgt--instances)
         (switch-to-buffer (cycle-current vterm-mgt--instances))
       (progn
-        (cycle-jump 0 vterm-mgt--instances)
+        (cycle-jump! 0 vterm-mgt--instances)
         (switch-to-buffer (cycle-current vterm-mgt--instances))))))
 
 (defun vterm-mgt-rename-buffer (name)
-  "Rename the current buffer ensuring that its NAME is wrapped in *vterm*<...>.
+  "Rename the current buffer ensuring that its NAME is wrapped in *vterm<...>*.
 This function should be called from a buffer running vterm."
   (interactive "SRename vterm buffer: ")
   (vterm-mgt--assert-vterm-buffer)
-  (rename-buffer (format "*vterm*<%s>" name)))
+  (rename-buffer (format "*vterm<%s>*" name)))
 
 (defun vterm-mgt-reconcile-state ()
   "Fill `vterm-mgt--instances' with the existing vterm buffers.
@@ -124,9 +126,7 @@ If for whatever reason, the state of `vterm-mgt--instances' is corrupted and
   restore the state."
   (interactive)
   (setq vterm-mgt--instances
-        (->> (buffer-list)
-             (-filter #'vterm-mgt--instance?)
-             cycle-from-list)))
+        (cycle-from-list (seq-filter #'vterm-mgt--instance? (buffer-list)))))
 
 (defun vterm-mgt-select ()
   "Select a vterm instance by name from the list in `vterm-mgt--instances'."
@@ -134,9 +134,7 @@ If for whatever reason, the state of `vterm-mgt--instances' is corrupted and
   (vterm-mgt-reconcile-state)
   (switch-to-buffer
    (completing-read "Switch to vterm: "
-                    (->> vterm-mgt--instances
-                         cycle-to-list
-                         (-map #'buffer-name)))))
+                    (seq-map #'buffer-name (cycle-to-list vterm-mgt--instances)))))
 
 (provide 'vterm-mgt)
 ;;; vterm-mgt.el ends here
diff --git a/users/wpcarro/emacs/pkgs/zle/default.nix b/users/wpcarro/emacs/pkgs/zle/default.nix
new file mode 100644
index 0000000000..9d4820a944
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/zle/default.nix
@@ -0,0 +1,10 @@
+{ pkgs, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "zle";
+    version = "1.0.0";
+    src = ./zle.el;
+  })
+{ }
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/zle.el b/users/wpcarro/emacs/pkgs/zle/zle.el
index d4aa88258f..21a6e35f13 100644
--- a/users/wpcarro/emacs/.emacs.d/wpc/zle.el
+++ b/users/wpcarro/emacs/pkgs/zle/zle.el
@@ -72,12 +72,11 @@
 
 (defvar zle-kbds
   (let ((map (make-sparse-keymap)))
-    (bind-keys :map map
-               ("C-j"   . zle-subshell)
-               ("C-v"   . zle-variable)
-               ("C-M--" . zle-dash-dash)
-               ("M-'"   . zle-single-quote)
-               ("M-\""  . zle-double-quote))
+    (define-key map (kbd "C-j") #'zle-subshell)
+    (define-key map (kbd "C-v") #'zle-variable)
+    (define-key map (kbd "C-M--") #'zle-dash-dash)
+    (define-key map (kbd "M-'") #'zle-single-quote)
+    (define-key map (kbd "M-\"") #'zle-double-quote)
     map)
   "Keybindings shaving milliseconds off of typing.")
 
diff --git a/users/wpcarro/emacs/workspace.josh b/users/wpcarro/emacs/workspace.josh
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/users/wpcarro/emacs/workspace.josh
diff --git a/users/wpcarro/haskell-file/shell.nix b/users/wpcarro/haskell-file/shell.nix
index 7682e8246c..0c6a298bf2 100644
--- a/users/wpcarro/haskell-file/shell.nix
+++ b/users/wpcarro/haskell-file/shell.nix
@@ -1,5 +1,5 @@
 { depot, ... }:
 
 depot.users.wpcarro.buildHaskell.shell {
-  deps = hpkgs: [];
+  deps = hpkgs: [ ];
 }
diff --git a/users/wpcarro/keys.nix b/users/wpcarro/keys.nix
index 42a349573b..531d110f71 100644
--- a/users/wpcarro/keys.nix
+++ b/users/wpcarro/keys.nix
@@ -2,9 +2,19 @@
 { ... }:
 
 rec {
-  diogenes = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILFDRfpNXDxQuTJAqVg8+Mm/hOfE5VAJP+Lpw9kA5cDG wpcarro@gmail.com";
+  ava = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB/5Fuo7wi8rNXVXgNaCK2X6ePCh9LQs/9h7Tj6UeXrl wpcarro@ava";
+  iphone = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEU1tsRQ/cMxi9Hd7Xo+YpiWB5i6qx24EJLCEFBK4q4W wpcarro@iphone";
+  kyoko = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBFILKdkNqfTP5WeoQAV6K3MdTzsDW65ToXGc6KlQ9yl wpcarro@kyoko";
   marcus = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJkNQJBXekuSzZJ8+gxT+V1+eXTm3hYsfigllr/ARXkf wpcarro@gmail.com";
-  seneca = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDSVqb0ehIxp33j8wJgPoCcd45NPnZ3CisDlcO1f5isTvOTSLVf5WAdqgDk9fuTTcOMdaiPQcun7psZYEZ7EnoiDYiNM/iYoqN5ga0MdQVCU0pvkJuExuK8MqdwAnjwrLx1bqw8ivXxbmrKMyN/fUsG8I7IzC+D61ycbMwxM5qSz2mgQjzHIhpS1HgCPJXchD3jo1kw9FgSVMAJJMGpWU6BcJsQ2cNTb3W8Kg3kdeahcIssysC6TbD2MFCI6ucPOdBvP/nMHQ/zwK3CgR75M57lyzqDPqu29OpiFacefN8Jxjgwlg4h/TP3tCkb+fSV/5vkBl8aagoPb+nepC5AWF9ADsagJ6y7HYRqkXnI6FaYRbHg+NjcEu1ljYQqAIl8lRLcVqFEHfqll1V12f1UeciNoSrOBXpb0pQrUs4YlaZU1rbq0t9dQob5x+mm5BrhNhKagEvx5nV+X5bxPywCLpdrotjKpW1oS+EssRq75cv9Aw2vqdNmk2pLhgKkOJu5RrOuitHL9Ts7ax6Co5S086BT57g3BCjaiCZDoWUSRTPc6K+rDPriCGXJqfGncdUJh20QsZPYIrWQSSJuRDW59WxnNbKvIH5aFvHM2S+HyjhZC+d5pjm9mhfHuluL9+Hwis7kxlqNoX3i/i5ufGEODaLeRu5xWp0hc5fYype8BL+NNw== wpcarro@gmail.com";
+  nathan = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP2NjuP722VUgpSu5bVUPTfdVNPO8fSW0Jlas8L4up13 bill@nathan";
+  tarasco = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh+wG4f7tI0IwGyF2sLi5mPlh3JKE7KqV2ab0tlcL36 wpcarro@tarasco";
 
-  all = [ diogenes marcus seneca ];
+  all = [
+    ava
+    iphone
+    kyoko
+    marcus
+    nathan
+    tarasco
+  ];
 }
diff --git a/users/wpcarro/lib/default.nix b/users/wpcarro/lib/default.nix
new file mode 100644
index 0000000000..6354877dd4
--- /dev/null
+++ b/users/wpcarro/lib/default.nix
@@ -0,0 +1,5 @@
+{ depot, ... }:
+
+{
+  usermod = name: depot.path.origSrc + ("/users/wpcarro/nixos/modules/${name}");
+}
diff --git a/users/wpcarro/nixos/ava/ava.el b/users/wpcarro/nixos/ava/ava.el
new file mode 100644
index 0000000000..b0b13746b0
--- /dev/null
+++ b/users/wpcarro/nixos/ava/ava.el
@@ -0,0 +1,61 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'bookmark)
+(require 'display)
+(require 'window-manager)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(bookmark-install-kbd
+ (make-bookmark :label "hadrian"
+                :path "/hadrian"
+                :kbd "h"))
+
+(setq initial-buffer-choice "/hadrian")
+
+(add-to-list 'ssh-hosts "wpcarro@tarasco")
+
+(display-register primary
+                  :output "HDMI-1"
+                  :primary t
+                  :coords (0 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-register secondary
+                  :output "HDMI-2"
+                  :primary nil
+                  :coords (2561 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-arrangement main :displays (primary secondary))
+
+(setq window-manager-named-workspaces
+      (list (make-window-manager-named-workspace
+             :label "Web Browsing"
+             :kbd "c"
+             :display display-secondary)
+            (make-window-manager-named-workspace
+             :label "Coding I"
+             :kbd "1"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Coding II"
+             :kbd "2"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Chatting"
+             :kbd "h"
+             :display display-secondary)))
+
+;; I *think* this needs to be the last statement in this file.
+(window-manager-init :init-hook #'display-arrange-main)
diff --git a/users/wpcarro/nixos/ava/default.nix b/users/wpcarro/nixos/ava/default.nix
new file mode 100644
index 0000000000..25c43c003f
--- /dev/null
+++ b/users/wpcarro/nixos/ava/default.nix
@@ -0,0 +1,150 @@
+{ depot, pkgs, lib, ... }:
+{ ... }:
+
+let
+  inherit (depot.users) wpcarro;
+  inherit (depot.users.wpcarro.lib) usermod;
+
+  wpcarrosEmacs = wpcarro.emacs.nixos {
+    load = [ ./ava.el ];
+  };
+
+  quasselClient = pkgs.quassel.override {
+    client = true;
+    enableDaemon = false;
+    monolithic = false;
+  };
+in
+{
+  imports = [
+    (usermod "hardware/nopn.nix")
+  ];
+
+  # Use the TVL binary cache
+  tvl.cache.enable = true;
+
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  # Support IP forwarding to use this device as a Tailscale exit node.
+  boot.kernel.sysctl."net.ipv4.ip_forward" = true;
+  boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
+  # Additionall exit node settings that Tailscale recommends.
+  networking.firewall.checkReversePath = "loose";
+
+  time.timeZone = "America/Los_Angeles";
+
+  networking = {
+    # The global useDHCP flag is deprecated, therefore explicitly set to false
+    # here.  Per-interface useDHCP will be mandatory in the future, so this
+    # generated config replicates the default behaviour.
+    useDHCP = false;
+    hostName = "ava";
+    networkmanager.enable = true;
+    interfaces.enp1s0.useDHCP = true;
+    interfaces.enp3s0.useDHCP = true;
+    interfaces.wlp2s0.useDHCP = true;
+  };
+
+  services = wpcarro.common.services // {
+    # Check the amount of available memory and free swap a few times per second
+    # and kill the largest process if both are below 10%.
+    earlyoom.enable = true;
+
+    tailscale.enable = true;
+
+    openssh.enable = true;
+
+    printing = {
+      enable = true;
+      drivers = with pkgs; [ gutenprint ];
+    };
+
+    xserver = {
+      enable = true;
+      xkb.layout = "us";
+      xkb.options = "caps:escape";
+      displayManager = {
+        # Give EXWM permission to control the session (from tazjin's setup).
+        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localhost:$USER";
+        lightdm.enable = true;
+      };
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${wpcarrosEmacs}/bin/wpcarros-emacs";
+      };
+    };
+  };
+
+  # Enable sound.
+  sound.enable = true;
+  hardware.pulseaudio.enable = true;
+
+  users.mutableUsers = true;
+  users.users.root.openssh.authorizedKeys.keys = with wpcarro.keys; [
+    iphone
+    nathan
+    tarasco
+  ];
+  users.users.wpcarro = {
+    initialPassword = "password";
+    isNormalUser = true;
+    extraGroups = [
+      "networkmanager"
+      "wheel"
+      "docker"
+    ];
+    shell = pkgs.fish;
+    openssh.authorizedKeys.keys = with wpcarro.keys; [
+      iphone
+      nathan
+      tarasco
+    ];
+  };
+  users.extraGroups.vboxusers.members = [ "wpcarro" ];
+
+  security.sudo.wheelNeedsPassword = false;
+
+  fonts = {
+    packages = with pkgs; [
+      jetbrains-mono
+    ];
+
+    fontconfig = {
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+
+  programs = wpcarro.common.programs // {
+    mosh.enable = true;
+  };
+
+  virtualisation.docker.enable = true;
+  virtualisation.virtualbox.host.enable = true;
+
+  environment.variables = {
+    EDITOR = "emacsclient";
+    ALTERNATE_EDITOR = "emacs -q -nw";
+    VISUAL = "emacsclient";
+  };
+
+  environment.systemPackages =
+    wpcarro.common.shell-utils ++
+    (with pkgs; [
+      alacritty
+      ec2-api-tools
+      firefox
+      google-chrome
+      httpie
+      pavucontrol
+      quasselClient
+      remmina
+      tdesktop
+      wpcarrosEmacs
+      xsecurelock
+    ]);
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/wpcarro/nixos/default.nix b/users/wpcarro/nixos/default.nix
index e7ae5490b9..9c8a7e5a79 100644
--- a/users/wpcarro/nixos/default.nix
+++ b/users/wpcarro/nixos/default.nix
@@ -1,22 +1,24 @@
 { depot, pkgs, ... }:
 
-let systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
-in {
-  marcusSystem = systemFor depot.users.wpcarro.nixos.marcus;
-  deploy-diogenes = pkgs.writeShellScriptBin "deploy-diogenes" ''
-    set -euo pipefail
-    readonly TF_STATE_DIR=/depot/users/wpcarro/terraform
-    rm -f $TF_STATE_DIR/*.json
-    readonly STORE_PATH="$(nix-build /depot -A users.wpcarro.nixos.diogenes)"
-    cp $STORE_PATH $TF_STATE_DIR
+let
+  inherit (depot.users.wpcarro.nixos)
+    ava
+    kyoko
+    marcus
+    tarasco;
 
-    function cleanup() {
-      rm -f "$TF_STATE_DIR/$(basename $STORE_PATH)"
-    }
+  systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
+in
+{
+  avaSystem = systemFor ava;
+  kyokoSystem = systemFor kyoko;
+  marcusSystem = systemFor marcus;
+  tarascoSystem = systemFor tarasco;
 
-    trap cleanup EXIT
-    ${pkgs.terraform}/bin/terraform -chdir="$TF_STATE_DIR" apply
-  '';
-
-  meta.targets = [ "marcusSystem" ];
+  meta.ci.targets = [
+    "avaSystem"
+    "kyokoSystem"
+    "marcusSystem"
+    "tarascoSystem"
+  ];
 }
diff --git a/users/wpcarro/nixos/diogenes/README.md b/users/wpcarro/nixos/diogenes/README.md
deleted file mode 100644
index 3cda88c9c7..0000000000
--- a/users/wpcarro/nixos/diogenes/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# diogenes
-
-diogenes is a NixOS machine deployed on a Google VM. It hosts
-https://wpcarro.dev.
-
-## Deployment
-
-I manage diogenes's deployment with Terraform. My current workflow looks like
-this:
-
-```shell
-deploy-diogenes
-```
diff --git a/users/wpcarro/nixos/diogenes/default.nix b/users/wpcarro/nixos/diogenes/default.nix
deleted file mode 100644
index d1246de4f3..0000000000
--- a/users/wpcarro/nixos/diogenes/default.nix
+++ /dev/null
@@ -1,178 +0,0 @@
-{ depot, pkgs, ... }:
-
-let
-  inherit (depot.users) wpcarro;
-  name = "diogenes";
-  domainName = "billandhiscomputer.com";
-in wpcarro.terraform.googleCloudVM {
-  project = "wpcarros-infrastructure";
-  name = "diogenes";
-  region = "us-central1";
-  zone = "us-central1-a";
-
-  # DNS configuration
-  extraConfig = {
-    # billandhiscomputer.com
-    resource.google_dns_managed_zone."${name}" = {
-      inherit name;
-      dns_name = "${domainName}.";
-    };
-
-    resource.google_dns_record_set."${name}" = {
-      name = "${domainName}.";
-      type = "A";
-      ttl = 300; # 5m
-      managed_zone = "\${google_dns_managed_zone.${name}.name}";
-      rrdatas = ["\${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip}"];
-    };
-
-    resource.google_compute_instance."${name}" = {
-      network_interface.access_config = {
-        public_ptr_domain_name = "${domainName}.";
-      };
-    };
-
-    # monsterpoker.app
-    resource.google_dns_managed_zone."monsterpoker" = {
-      name = "monsterpoker";
-      dns_name = "monsterpoker.app.";
-    };
-
-    resource.google_dns_record_set."monsterpoker" = {
-      name = "monsterpoker.app.";
-      type = "A";
-      ttl = 300; # 5m
-      managed_zone = "\${google_dns_managed_zone.monsterpoker.name}";
-      rrdatas = ["\${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip}"];
-    };
-  };
-
-  configuration = {
-    imports = [
-      "${depot.path}/ops/modules/quassel.nix"
-    ];
-
-    networking = {
-      firewall.allowedTCPPorts = [
-        22   # ssh
-        80   # http
-        443  # https
-        6698 # quassel
-      ];
-      firewall.allowedUDPPortRanges = [
-        { from = 60000; to = 61000; } # mosh
-      ];
-    };
-
-    # Use the TVL binary cache
-    tvl.cache.enable = true;
-
-    users = {
-      mutableUsers = true;
-      users = {
-        root = {
-          openssh.authorizedKeys.keys = wpcarro.keys.all;
-        };
-        wpcarro = {
-          isNormalUser = true;
-          extraGroups = [ "wheel" "quassel" ];
-          openssh.authorizedKeys.keys = wpcarro.keys.all;
-          shell = pkgs.fish;
-        };
-        # This is required so that quasselcore can read the ACME cert in
-        # /var/lib/acme, which is only available to user=acme or group=nginx.
-        quassel.extraGroups = [ "nginx" ];
-      };
-    };
-
-    security = {
-      acme = {
-        acceptTerms = true;
-        defaults.email = "wpcarro@gmail.com";
-      };
-
-      sudo.wheelNeedsPassword = false;
-    };
-
-    programs = wpcarro.common.programs // {
-      mosh.enable = true;
-    };
-
-    # I won't have an Emacs server running on diogenes, and I'll likely be in an
-    # SSH session from within vterm. As such, Vim is one of the few editors that
-    # I tolerably navigate this way.
-    environment.variables = {
-      EDITOR = "vim";
-    };
-
-    environment.systemPackages = wpcarro.common.shell-utils;
-
-    services = wpcarro.common.services // {
-      # TODO(wpcarro): Re-enable this when rebuild-system better supports
-      # terraform deployments.
-      # depot.auto-deploy = {
-      #   enable = true;
-      #   interval = "1h";
-      # };
-
-      # TODO(wpcarro): Re-enable this after debugging ACME and NXDOMAIN.
-      depot.quassel = {
-        enable = true;
-        acmeHost = domainName;
-        bindAddresses = [
-          "0.0.0.0"
-        ];
-      };
-
-      journaldriver = {
-        enable = true;
-        logStream = "home";
-        googleCloudProject = "wpcarros-infrastructure";
-        applicationCredentials = "/etc/gcp/key.json";
-      };
-
-      nginx = {
-        enable = true;
-        enableReload = true;
-
-        recommendedTlsSettings = true;
-        recommendedGzipSettings = true;
-        recommendedProxySettings = true;
-
-        # for journaldriver
-        commonHttpConfig = ''
-          log_format json_combined escape=json
-          '{'
-              '"remote_addr":"$remote_addr",'
-              '"method":"$request_method",'
-              '"host":"$host",'
-              '"uri":"$request_uri",'
-              '"status":$status,'
-              '"request_size":$request_length,'
-              '"response_size":$body_bytes_sent,'
-              '"response_time":$request_time,'
-              '"referrer":"$http_referer",'
-              '"user_agent":"$http_user_agent"'
-          '}';
-
-          access_log syslog:server=unix:/dev/log,nohostname json_combined;
-        '';
-
-        virtualHosts = {
-          "${domainName}" = {
-            addSSL = true;
-            enableACME = true;
-            root = wpcarro.website.root;
-          };
-          "monsterpoker.app" = {
-            addSSL = true;
-            enableACME = true;
-            root = wpcarro.clients.monsterpoker;
-          };
-        };
-      };
-    };
-
-    system.stateVersion = "21.11";
-  };
-}
diff --git a/users/wpcarro/nixos/kyoko/default.nix b/users/wpcarro/nixos/kyoko/default.nix
new file mode 100644
index 0000000000..0d8907edd2
--- /dev/null
+++ b/users/wpcarro/nixos/kyoko/default.nix
@@ -0,0 +1,153 @@
+{ depot, pkgs, lib, ... }:
+_:
+
+let
+  inherit (depot.users) wpcarro;
+  inherit (depot.users.wpcarro.lib) usermod;
+
+  wpcarrosEmacs = wpcarro.emacs.nixos {
+    load = [ ./kyoko.el ];
+  };
+
+  quasselClient = pkgs.quassel.override {
+    client = true;
+    enableDaemon = false;
+    monolithic = false;
+  };
+in
+{
+  imports = [
+    (usermod "hardware/dell-emc-egw-5200.nix")
+    (usermod "hadrian-cache.nix")
+  ];
+
+  # TVL's Nix binary cache
+  tvl.cache.enable = true;
+
+  # Hadrian's Nix binary cache.
+  hadrian.cache.enable = true;
+
+  nix.settings.trusted-users = [ "@wheel" ];
+
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  # Additionall exit node settings that Tailscale recommends.
+  networking.firewall.checkReversePath = "loose";
+
+  time.timeZone = "America/Los_Angeles";
+
+  networking = {
+    # The global useDHCP flag is deprecated, therefore explicitly set to false
+    # here.  Per-interface useDHCP will be mandatory in the future, so this
+    # generated config replicates the default behaviour.
+    useDHCP = false;
+    hostName = "kyoko";
+    networkmanager.enable = true;
+    interfaces.enp1s0.useDHCP = true;
+    interfaces.enp3s0.useDHCP = true;
+    interfaces.wlp2s0.useDHCP = true;
+  };
+
+  services = wpcarro.common.services // {
+    # Check the amount of available memory and free swap a few times per second
+    # and kill the largest process if both are below 10%.
+    earlyoom.enable = true;
+
+    tailscale.enable = true;
+
+    openssh.enable = true;
+
+    printing = {
+      enable = true;
+      drivers = with pkgs; [ gutenprint ];
+    };
+
+    xserver = {
+      enable = true;
+      xkb.layout = "us";
+      xkb.options = "caps:escape";
+      displayManager = {
+        # Give EXWM permission to control the session (from tazjin's setup).
+        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localhost:$USER";
+        lightdm.enable = true;
+      };
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${wpcarrosEmacs}/bin/wpcarros-emacs";
+      };
+    };
+  };
+
+  # Enable sound.
+  sound.enable = true;
+  hardware.pulseaudio.enable = true;
+
+  users.mutableUsers = true;
+  users.users.root.openssh.authorizedKeys.keys = with wpcarro.keys; [
+    iphone
+    nathan
+    tarasco
+  ];
+  users.users.wpcarro = {
+    initialPassword = "password";
+    isNormalUser = true;
+    extraGroups = [
+      "networkmanager"
+      "wheel"
+      "docker"
+    ];
+    shell = pkgs.fish;
+    openssh.authorizedKeys.keys = with wpcarro.keys; [
+      iphone
+      nathan
+      tarasco
+    ];
+  };
+  users.extraGroups.vboxusers.members = [ "wpcarro" ];
+
+  security.sudo.wheelNeedsPassword = false;
+
+  fonts = {
+    packages = with pkgs; [
+      jetbrains-mono
+    ];
+
+    fontconfig = {
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+
+  programs = wpcarro.common.programs // {
+    mosh.enable = true;
+  };
+
+  virtualisation.docker.enable = true;
+  virtualisation.virtualbox.host.enable = true;
+
+  environment.variables = {
+    EDITOR = "emacsclient";
+    ALTERNATE_EDITOR = "emacs -q -nw";
+    VISUAL = "emacsclient";
+  };
+
+  environment.systemPackages =
+    wpcarro.common.shell-utils ++
+    (with pkgs; [
+      alacritty
+      ec2-api-tools
+      firefox
+      google-chrome
+      httpie
+      pavucontrol
+      quasselClient
+      remmina
+      tdesktop
+      wpcarrosEmacs
+      xsecurelock
+    ]);
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/wpcarro/nixos/kyoko/kyoko.el b/users/wpcarro/nixos/kyoko/kyoko.el
new file mode 100644
index 0000000000..310323688a
--- /dev/null
+++ b/users/wpcarro/nixos/kyoko/kyoko.el
@@ -0,0 +1,61 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'bookmark)
+(require 'display)
+(require 'window-manager)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(bookmark-install-kbd
+ (make-bookmark :label "hadrian"
+                :path "/hadrian"
+                :kbd "h"))
+
+(setq initial-buffer-choice "/hadrian")
+
+(add-to-list 'ssh-hosts "wpcarro@tarasco")
+
+(display-register primary
+                  :output "DP-2"
+                  :primary t
+                  :coords (0 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-register secondary
+                  :output "DP-1"
+                  :primary nil
+                  :coords (2561 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-arrangement main :displays (primary secondary))
+
+(setq window-manager-named-workspaces
+      (list (make-window-manager-named-workspace
+             :label "Web Browsing"
+             :kbd "c"
+             :display display-secondary)
+            (make-window-manager-named-workspace
+             :label "Coding I"
+             :kbd "1"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Coding II"
+             :kbd "2"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Chatting"
+             :kbd "h"
+             :display display-secondary)))
+
+;; I *think* this needs to be the last statement in this file.
+(window-manager-init :init-hook #'display-arrange-main)
diff --git a/users/wpcarro/nixos/marcus/default.nix b/users/wpcarro/nixos/marcus/default.nix
index fb4442c6cc..a97d6d264d 100644
--- a/users/wpcarro/nixos/marcus/default.nix
+++ b/users/wpcarro/nixos/marcus/default.nix
@@ -3,17 +3,23 @@
 
 let
   inherit (depot.users) wpcarro;
-  wpcarrosEmacs = wpcarro.emacs.nixos;
+  inherit (depot.users.wpcarro.lib) usermod;
+
+  wpcarrosEmacs = wpcarro.emacs.nixos {
+    load = [ ./marcus.el ];
+  };
 
   quasselClient = pkgs.quassel.override {
     client = true;
     enableDaemon = false;
     monolithic = false;
   };
-in {
+in
+{
   imports = [
-    (depot.path + "/users/wpcarro/nixos/marcus/hardware.nix")
-    "${pkgs.home-manager.src}/nixos"
+    (depot.path.origSrc + "/users/wpcarro/nixos/marcus/hardware.nix")
+    (pkgs.home-manager.src + "/nixos")
+    (usermod "laptop.nix")
   ];
 
   # Use the TVL binary cache
@@ -34,8 +40,16 @@ in {
   };
 
   services = wpcarro.common.services // {
+    # Enable the Tailscale daemon to connect to work and personal Tailnet.
+    tailscale.enable = true;
+
     tzupdate.enable = true;
 
+    depot.auto-deploy = {
+      enable = true;
+      interval = "1d";
+    };
+
     xserver = {
       enable = true;
       libinput = {
@@ -43,8 +57,8 @@ in {
         touchpad.naturalScrolling = false;
         touchpad.tapping = false;
       };
-      layout = "us";
-      xkbOptions = "caps:escape";
+      xkb.layout = "us";
+      xkb.options = "caps:escape";
       displayManager = {
         # Give EXWM permission to control the session (from tazjin's setup).
         sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localhost:$USER";
@@ -82,7 +96,7 @@ in {
   security.sudo.wheelNeedsPassword = false;
 
   fonts = {
-    fonts = with pkgs; [
+    packages = with pkgs; [
       jetbrains-mono
     ];
 
@@ -93,9 +107,7 @@ in {
     };
   };
 
-  programs = wpcarro.common.programs // {
-    light.enable = true;
-  };
+  programs = wpcarro.common.programs;
 
   environment.variables = {
     EDITOR = "emacsclient";
@@ -120,6 +132,12 @@ in {
       backend = "glx";
     };
 
+    services.redshift = {
+      enable = true;
+      latitude = 37.4223931;
+      longitude = -122.0864016;
+    };
+
     services.dunst.enable = true;
     xdg.configFile."dunst/dunstrc" = {
       source = wpcarro.dotfiles.dunstrc;
@@ -129,6 +147,9 @@ in {
     };
 
     systemd.user.startServices = true;
+
+    # Previous default version, see https://github.com/nix-community/home-manager/blob/master/docs/release-notes/rl-2211.adoc
+    home.stateVersion = "18.09";
   };
 
   environment.systemPackages =
diff --git a/users/wpcarro/nixos/marcus/hardware.nix b/users/wpcarro/nixos/marcus/hardware.nix
index a5735bc8e2..8a2672206b 100644
--- a/users/wpcarro/nixos/marcus/hardware.nix
+++ b/users/wpcarro/nixos/marcus/hardware.nix
@@ -6,23 +6,23 @@
   ];
 
   boot.initrd.availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" "sd_mod" ];
-  boot.initrd.kernelModules = [];
+  boot.initrd.kernelModules = [ ];
   boot.kernelModules = [ "kvm-intel" ];
-  boot.extraModulePackages = [];
+  boot.extraModulePackages = [ ];
 
   fileSystems."/" = {
-    device = "/dev/disk/by-uuid/b8b911ee-e9b9-40ea-89d6-551f11350e7b";
+    device = "/dev/disk/by-label/nixos";
     fsType = "ext4";
   };
 
   fileSystems."/boot" = {
-    device = "/dev/disk/by-uuid/A7EA-369C";
+    device = "/dev/disk/by-label/boot";
     fsType = "vfat";
   };
 
-  swapDevices = [
-    { device = "/dev/disk/by-uuid/b87e2b8f-c835-4179-a428-fe466a846df0"; }
-  ];
+  swapDevices = lib.singleton {
+    device = "/dev/disk/by-label/swap";
+  };
 
   powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
   hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
diff --git a/users/wpcarro/nixos/marcus/marcus.el b/users/wpcarro/nixos/marcus/marcus.el
new file mode 100644
index 0000000000..90c04f7ff3
--- /dev/null
+++ b/users/wpcarro/nixos/marcus/marcus.el
@@ -0,0 +1,40 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'tvl)
+(require 'display)
+(require 'window-manager)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Monitor Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(display-register laptop
+                  :output "eDP-1"
+                  :primary t
+                  :coords (0 0)
+                  :size (1920 1080)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-arrangement primary :displays (laptop))
+
+(setq initial-buffer-choice tvl-depot-path)
+
+(setq window-manager-named-workspaces
+      (list (make-window-manager-named-workspace
+             :label "Web Browsing"
+             :kbd "c"
+             :display display-laptop)
+            (make-window-manager-named-workspace
+             :label "Coding"
+             :kbd "d"
+             :display display-laptop)
+            (make-window-manager-named-workspace
+             :label "Chatting"
+             :kbd "h"
+             :display display-laptop)))
+
+(window-manager-init :init-hook #'display-arrange-primary)
diff --git a/users/wpcarro/nixos/modules/.skip-subtree b/users/wpcarro/nixos/modules/.skip-subtree
new file mode 100644
index 0000000000..09520f8c83
--- /dev/null
+++ b/users/wpcarro/nixos/modules/.skip-subtree
@@ -0,0 +1 @@
+NixOS modules are not readTree compatible.
diff --git a/users/wpcarro/nixos/modules/hadrian-cache.nix b/users/wpcarro/nixos/modules/hadrian-cache.nix
new file mode 100644
index 0000000000..033c03c825
--- /dev/null
+++ b/users/wpcarro/nixos/modules/hadrian-cache.nix
@@ -0,0 +1,17 @@
+# If enabled, use Hadrian's Nix cache.
+{ config, lib, pkgs, ... }:
+
+{
+  options = {
+    hadrian.cache.enable = lib.mkEnableOption "Hadrian's binary cache";
+  };
+
+  config = lib.mkIf config.hadrian.cache.enable {
+    nix.settings.trusted-public-keys = [
+      "cache.hadrian.internal:XWdYSn5ZASj6IqZd4nnDBXJmahQEolBrtq9DvSe0UT0="
+    ];
+    nix.settings.substituters = [
+      "http://cache.hadrian.internal"
+    ];
+  };
+}
diff --git a/users/wpcarro/nixos/modules/hardware/dell-emc-egw-5200.nix b/users/wpcarro/nixos/modules/hardware/dell-emc-egw-5200.nix
new file mode 100644
index 0000000000..df46405629
--- /dev/null
+++ b/users/wpcarro/nixos/modules/hardware/dell-emc-egw-5200.nix
@@ -0,0 +1,47 @@
+# In a nutshell, this configuration defines the configuration required to run
+# NixOS on the Dell EMC EGW 5200 (often the config that NixOS put in
+# hardware.nix by default).
+{ config, lib, modulesPath, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+  ];
+
+  boot.initrd.availableKernelModules = [
+    "xhci_pci"
+    "ahci"
+    "usb_storage"
+    "usbhid"
+    "sd_mod"
+  ];
+  boot.initrd.kernelModules = [ ];
+  boot.kernelModules = [ "kvm-intel" ];
+  boot.extraModulePackages = [ ];
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  fileSystems."/" = {
+    device = "/dev/disk/by-label/NIXROOT";
+    fsType = "ext4";
+  };
+
+  fileSystems."/boot" = {
+    device = "/dev/disk/by-label/NIXBOOT";
+    fsType = "vfat";
+  };
+
+  swapDevices = [ ];
+
+  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
+  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+
+  # Needed for Tailscale subnet routing
+  boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
+  networking.useDHCP = false;
+  networking.interfaces.eno1.useDHCP = true;
+  networking.interfaces.enp3s0.useDHCP = true;
+  networking.interfaces.enp4s0.useDHCP = true;
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/wpcarro/nixos/modules/hardware/nopn.nix b/users/wpcarro/nixos/modules/hardware/nopn.nix
new file mode 100644
index 0000000000..a356954212
--- /dev/null
+++ b/users/wpcarro/nixos/modules/hardware/nopn.nix
@@ -0,0 +1,53 @@
+# I tried looking up the manufacturer, product name, and version, but
+# `dmidecode -t system` reported "To be filled by O.E.M." for each of these
+# fields.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+  ];
+
+  fileSystems."/" = {
+    device = "/dev/disk/by-label/NIXROOT";
+    fsType = "ext4";
+  };
+
+  fileSystems."/boot" = {
+    device = "/dev/disk/by-label/NIXBOOT";
+    fsType = "vfat";
+  };
+
+  boot = {
+    initrd.availableKernelModules = [
+      "xhci_pci"
+      "ehci_pci"
+      "ahci"
+      "usb_storage"
+      "usbhid"
+      "sd_mod"
+    ];
+    initrd.kernelModules = [ ];
+    kernelModules = [ "kvm-intel" ];
+    extraModulePackages = [ ];
+
+    # Can verify these settings with:
+    # $ lsmod
+    # ...or:
+    # $ cat /etc/modprobe.d/nixos.conf
+    blacklistedKernelModules = [
+      # Disabling this buggy network driver (and preferring ethernet) to prevent
+      # my machine from becoming unresponsive.
+      # TODO(wpcarro): Consider replacing this module with this fork (if NixOS
+      # isn't already): https://github.com/tomaspinho/rtl8821ce
+      "rtw88_8821ce"
+    ];
+  };
+
+  swapDevices = [ ];
+
+  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+  # TODO(wpcarro): https://github.com/NixOS/nixpkgs/issues/222805
+  # high-resolution display
+  # hardware.video.hidpi.enable = lib.mkDefault true;
+}
diff --git a/users/wpcarro/nixos/modules/laptop.nix b/users/wpcarro/nixos/modules/laptop.nix
new file mode 100644
index 0000000000..03dd0f39bb
--- /dev/null
+++ b/users/wpcarro/nixos/modules/laptop.nix
@@ -0,0 +1,15 @@
+# Laptop-specific NixOS configuration.
+_:
+
+{
+  # Automatically detect location for redshift.
+  services.geoclue2.enable = true;
+  location.provider = "geoclue2";
+
+  # Enable power-saving features.
+  powerManagement.powertop.enable = true;
+
+  # Backlight control command.
+  programs.light.enable = true;
+}
+
diff --git a/users/wpcarro/nixos/modules/nginx.nix b/users/wpcarro/nixos/modules/nginx.nix
new file mode 100644
index 0000000000..e6cc6b0feb
--- /dev/null
+++ b/users/wpcarro/nixos/modules/nginx.nix
@@ -0,0 +1,45 @@
+# Common configuration for Nginx.
+{ pkgs, ... }:
+
+{
+  config = {
+    security.acme = {
+      acceptTerms = true;
+      defaults.email = "wpcarro@gmail.com";
+    };
+
+    services.nginx = {
+      enable = true;
+      enableReload = true;
+
+      recommendedTlsSettings = true;
+      recommendedGzipSettings = true;
+
+      # Log errors to journald (i.e. /dev/log) with debug verbosity.
+      logError = "syslog:server=unix:/dev/log debug";
+
+      # for journaldriver
+      commonHttpConfig = ''
+        log_format json_combined escape=json
+        '{'
+            '"remote_addr":"$remote_addr",'
+            '"method":"$request_method",'
+            '"host":"$host",'
+            '"uri":"$request_uri",'
+            '"status":$status,'
+            '"request_size":$request_length,'
+            '"response_size":$body_bytes_sent,'
+            '"response_time":$request_time,'
+            '"referrer":"$http_referer",'
+            '"user_agent":"$http_user_agent"'
+        '}';
+
+        access_log syslog:server=unix:/dev/log,nohostname json_combined;
+      '';
+
+      appendHttpConfig = ''
+        add_header Permissions-Policy "interest-cohort=()";
+      '';
+    };
+  };
+}
diff --git a/users/wpcarro/nixos/tarasco/default.nix b/users/wpcarro/nixos/tarasco/default.nix
new file mode 100644
index 0000000000..7033caa11a
--- /dev/null
+++ b/users/wpcarro/nixos/tarasco/default.nix
@@ -0,0 +1,144 @@
+{ depot, pkgs, lib, ... }:
+{ ... }:
+
+let
+  inherit (depot.users) wpcarro;
+  inherit (depot.users.wpcarro.lib) usermod;
+
+  wpcarrosEmacs = wpcarro.emacs.nixos {
+    load = [ ./tarasco.el ];
+  };
+
+  quasselClient = pkgs.quassel.override {
+    client = true;
+    enableDaemon = false;
+    monolithic = false;
+  };
+in
+{
+  imports = [
+    (usermod "hardware/nopn.nix")
+  ];
+
+  # Use the TVL binary cache
+  tvl.cache.enable = true;
+
+  boot = {
+    loader.systemd-boot.enable = true;
+    loader.efi.canTouchEfiVariables = true;
+
+    # Support IP forwarding to use this device as a Tailscale exit node.
+    kernel.sysctl."net.ipv4.ip_forward" = true;
+    kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
+  };
+
+
+  time.timeZone = "America/Los_Angeles";
+
+  networking = {
+    useDHCP = false;
+    hostName = "tarasco";
+    networkmanager.enable = true;
+    interfaces.enp1s0.useDHCP = true;
+    interfaces.enp3s0.useDHCP = true;
+    firewall.checkReversePath = "loose";
+    # Disabling wifi because the Realtek network card drivers crash. For more
+    # context, see the boot.blacklistedKernelModules configuration.
+    # interfaces.wlp2s0.useDHCP = true;
+  };
+
+  services = wpcarro.common.services // {
+    # Check the amount of available memory and free swap a few times per second
+    # and kill the largest process if both are below 10%.
+    earlyoom.enable = true;
+
+    tailscale.enable = true;
+
+    openssh.enable = true;
+
+    xserver = {
+      enable = true;
+      xkb.layout = "us";
+      xkb.options = "caps:escape";
+      displayManager = {
+        # Give EXWM permission to control the session (from tazjin's setup).
+        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localhost:$USER";
+        lightdm.enable = true;
+      };
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${wpcarrosEmacs}/bin/wpcarros-emacs";
+      };
+    };
+  };
+
+  # Enable sound.
+  sound.enable = true;
+  hardware.pulseaudio.enable = true;
+
+  users.mutableUsers = true;
+  users.users.root.openssh.authorizedKeys.keys = with wpcarro.keys; [
+    ava
+    iphone
+    nathan
+  ];
+  users.users.wpcarro = {
+    isNormalUser = true;
+    extraGroups = [
+      "networkmanager"
+      "wheel"
+      "docker"
+    ];
+    shell = pkgs.fish;
+    openssh.authorizedKeys.keys = with wpcarro.keys; [
+      ava
+      iphone
+      nathan
+    ];
+  };
+  users.extraGroups.vboxusers.members = [ "wpcarro" ];
+
+  security.sudo.wheelNeedsPassword = false;
+
+  fonts = {
+    packages = with pkgs; [
+      jetbrains-mono
+    ];
+
+    fontconfig = {
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+
+  programs = wpcarro.common.programs // {
+    mosh.enable = true;
+  };
+
+  virtualisation.docker.enable = true;
+  virtualisation.virtualbox.host.enable = true;
+
+  environment.variables = {
+    EDITOR = "emacsclient";
+    ALTERNATE_EDITOR = "emacs -q -nw";
+    VISUAL = "emacsclient";
+  };
+
+  environment.systemPackages =
+    wpcarro.common.shell-utils ++
+    (with pkgs; [
+      alacritty
+      firefox
+      google-chrome
+      httpie
+      pavucontrol
+      quasselClient
+      remmina
+      tdesktop
+      wpcarrosEmacs
+      xsecurelock
+    ]);
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/wpcarro/nixos/tarasco/tarasco.el b/users/wpcarro/nixos/tarasco/tarasco.el
new file mode 100644
index 0000000000..c840493f24
--- /dev/null
+++ b/users/wpcarro/nixos/tarasco/tarasco.el
@@ -0,0 +1,61 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'bookmark)
+(require 'display)
+(require 'window-manager)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(bookmark-install-kbd
+ (make-bookmark :label "hadrian"
+                :path "/hadrian"
+                :kbd "h"))
+
+(setq initial-buffer-choice "/hadrian")
+
+(add-to-list 'ssh-hosts "wpcarro@ava")
+
+(display-register primary
+                  :output "HDMI-1"
+                  :primary t
+                  :coords (0 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-register secondary
+                  :output "HDMI-2"
+                  :primary nil
+                  :coords (2561 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-arrangement main :displays (primary secondary))
+
+(setq window-manager-named-workspaces
+      (list (make-window-manager-named-workspace
+             :label "Web Browsing"
+             :kbd "c"
+             :display display-secondary)
+            (make-window-manager-named-workspace
+             :label "Coding I"
+             :kbd "1"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Coding II"
+             :kbd "2"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Chatting"
+             :kbd "h"
+             :display display-secondary)))
+
+;; I *think* this needs to be the last statement in this file.
+(window-manager-init :init-hook #'display-arrange-main)
diff --git a/users/wpcarro/playbooks/first-of-the-month.org b/users/wpcarro/playbooks/first-of-the-month.org
index 98d6d591f2..7bce39ca76 100644
--- a/users/wpcarro/playbooks/first-of-the-month.org
+++ b/users/wpcarro/playbooks/first-of-the-month.org
@@ -4,10 +4,9 @@
 # should be thought of instead as two-minutes worth of work per day that is all
 # being completed upfront.
 * Tasks
-** TODO [20m] Create habit template in journal.
-*** Spend time choosing a habit that you can accomplish giving known traveling constraints.
-** TODO [45m] Assess previous month's performance.
-** TODO [10m] Book massage for the month.
-** TODO [05m] Register for HotPodYoga classes.
-** TODO [10m] Plan one museum date in London.
-** TODO [20m] Plan each weekend for month.
+** TODO [10m] create habit template in journal
+** TODO [30m] assess previous month's performance
+** TODO [10m] book massage for the month
+** TODO [10m] create go/hallpass entries (BJJ, VHP)
+** TODO [10m] expense home internet
+** TODO [10m] buy TSLA through tdameritrade.com
diff --git a/users/wpcarro/playbooks/habits.org b/users/wpcarro/playbooks/habits.org
index 3b6f6f680e..aac63735d9 100644
--- a/users/wpcarro/playbooks/habits.org
+++ b/users/wpcarro/playbooks/habits.org
@@ -1,12 +1,7 @@
 * First of the year
 ** [1hr] Write a post mortem for the previous year
 * First of the month
-** [20m] Create habit template in journal.
-** [45m] Assess previous month's performance.
-** [10m] Book massage for the month.
-** [05m] Register for HotPodYoga classes.
-** [10m] Plan one museum date in London.
-** [20m] Plan each weekend for month.
+** see ./first-of-the-month.org
 * Payday
 ** [10m] Audit Monzo expenses
 ** [05m] Review "finances_2020" spreadsheet
diff --git a/users/wpcarro/playbooks/nix_gcr/cloud_run.nix b/users/wpcarro/playbooks/nix_gcr/cloud_run.nix
index 3d98161181..1f473b5f59 100644
--- a/users/wpcarro/playbooks/nix_gcr/cloud_run.nix
+++ b/users/wpcarro/playbooks/nix_gcr/cloud_run.nix
@@ -4,7 +4,7 @@ pkgs.dockerTools.buildLayeredImage {
   name = "gemma";
   tag = "latest";
   config.ExposedPorts = {
-    "4242" = {};
+    "4242" = { };
   };
   config.Env = [
     "GEMMA_CONFIG=${./config.lisp}"
diff --git a/users/wpcarro/scratch/blockchain/default.nix b/users/wpcarro/scratch/blockchain/default.nix
index 745e7a5ab4..c02f9a9c81 100644
--- a/users/wpcarro/scratch/blockchain/default.nix
+++ b/users/wpcarro/scratch/blockchain/default.nix
@@ -2,7 +2,8 @@
 
 let
   pypkgs = pkgs.python3Packages;
-in pkgs.python3Packages.buildPythonApplication {
+in
+pkgs.python3Packages.buildPythonApplication {
   pname = "main";
   src = ./.;
   version = "0.0.1";
diff --git a/users/wpcarro/scratch/compiler/.envrc b/users/wpcarro/scratch/compiler/.envrc
new file mode 100644
index 0000000000..ff7eea1f7a
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/.envrc
@@ -0,0 +1,3 @@
+source_up
+
+use_nix
diff --git a/users/wpcarro/scratch/compiler/.gitignore b/users/wpcarro/scratch/compiler/.gitignore
new file mode 100644
index 0000000000..96261d3fc7
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/.gitignore
@@ -0,0 +1,5 @@
+a.out
+*.cmi
+*.cmo
+*.cmx
+*.o
\ No newline at end of file
diff --git a/users/wpcarro/scratch/compiler/debug.ml b/users/wpcarro/scratch/compiler/debug.ml
new file mode 100644
index 0000000000..e39ff13742
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/debug.ml
@@ -0,0 +1,66 @@
+open Types
+
+(* Print x prefixed with tag and return x unchanged. *)
+let print (f : 'a -> string) (tag : string) (x : 'a) : 'a =
+  Printf.printf "%s: %s\n" tag (f x);
+  x
+
+let rec ast (tree : Types.value) : string =
+  match tree with
+  | ValueLiteral (LiteralBool x) ->
+     Printf.sprintf "ValueLiteral (LiteralBool %s)" (string_of_bool x)
+  | ValueLiteral (LiteralInt x) ->
+     Printf.sprintf "ValueLiteral (LiteralInt %s)" (string_of_int x)
+  | ValueVariable x ->
+     Printf.sprintf "ValueVariable %s" x
+  | ValueFunction (x, body) ->
+     Printf.sprintf "ValueFunction (%s, %s)" x (ast body)
+  | ValueApplication (f, x) ->
+     Printf.sprintf "ValueApplication (%s, %s)" (ast f) (ast x)
+  | ValueVarApplication (f, x) ->
+     Printf.sprintf "ValueVarApplication (%s, %s)" f (ast x)
+  | ValueBinder (k, v, x) ->
+      Printf.sprintf "ValueBinder (%s, %s, %s)" k (ast v) (ast x)
+
+let rec value (x : value) : string =
+  match x with
+  | ValueLiteral (LiteralInt x) ->
+     Printf.sprintf "Int %d" x
+  | ValueLiteral (LiteralBool x) ->
+     Printf.sprintf "Bool %b" x
+  | ValueVariable x ->
+     Printf.sprintf "Var %s" x
+  | ValueFunction (name, x) ->
+     Printf.sprintf "Fn %s %s" name (value x)
+  | ValueApplication (f, x) ->
+     Printf.sprintf "App %s %s" (value f) (value x)
+  | ValueVarApplication (name, x) ->
+     Printf.sprintf "App %s %s" name (value x)
+  | ValueBinder (name, x, body) ->
+     Printf.sprintf "Bind %s %s %s" name (value x) (value body)
+
+let rec type' (t : _type) : string =
+  match t with
+  | TypeInt -> "Integer"
+  | TypeBool -> "Boolean"
+  | TypeVariable k -> Printf.sprintf "%s" k
+  | TypeArrow (a, b) -> Printf.sprintf "%s -> %s" (type' a) (type' b)
+
+let quantified_type (q : quantified_type) : string =
+  let QuantifiedType (vars, t) = q in
+  if List.length vars == 0 then
+    Printf.sprintf "%s" (type' t)
+  else
+    Printf.sprintf "forall %s. %s" (String.concat "," vars) (type' t)
+
+let substitution (s : substitution) : string =
+  FromString.fold (fun k v acc -> Printf.sprintf "%s\"%s\" |-> %s;" acc k (type' v)) s ""
+  |> Printf.sprintf "{ %s }"
+
+let env (s : env) : string =
+  FromString.fold (fun k v acc -> Printf.sprintf "%s\"%s\" |-> %s;" acc k (quantified_type v)) s ""
+  |> Printf.sprintf "{ %s }"
+
+let inference (Inference (s, t)) =
+  Printf.sprintf "type: %s; sub: %s" (type' t) (substitution s)
+
diff --git a/users/wpcarro/scratch/compiler/expr_parser.ml b/users/wpcarro/scratch/compiler/expr_parser.ml
new file mode 100644
index 0000000000..797592931a
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/expr_parser.ml
@@ -0,0 +1,187 @@
+(*******************************************************************************
+ * CLI REPL for an s-expression Lambda Calculus.
+ *
+ * Lambda Calculus Expression Language:
+ *
+ *   Helpers:
+ *     symbol     -> [-a-z]+
+ *     string     -> '"' [^"]* '"'
+ *     boolean    -> 'true' | 'false'
+ *     integer    -> [1-9][0-9]*
+ *
+ *   Core:
+ *     expression -> funcdef
+ *     binding    -> '(' 'let' symbol expr expr ')'
+ *     funcdef    -> '(' 'fn' symbol expr ')'
+ *     funccall   -> '(' ( symbol | funcdef) expr ')'
+ *     literal    -> string | boolean | integer
+ *     variable   -> symbol
+ *
+ * Example Usage:
+ *   $ ocamlopt types.ml str.cmxa inference.ml parser.ml expr_parser.ml && ./a.out
+ *   repl> true
+ *   tokens: [ "true" ]
+ *   ast: ValueLiteral (LiteralBool true)
+ *   Boolean
+ *   repl>
+ *
+ ******************************************************************************)
+
+open Parser
+open Inference
+open Debug
+open Prettify
+open Vec
+
+type literal = LiteralBool of bool | LiteralInt of int
+
+let ( let* ) = Option.bind
+let map = Option.map
+
+let tokenize (x : string) : token vec =
+  let xs = Vec.create () in
+  let i = ref 0 in
+  while !i < String.length x do
+    match x.[!i] with
+    | ' ' -> i := !i + 1
+    (* strings *)
+    | '"' ->
+      let curr = ref "\"" in 
+      i := !i + 1;
+      while x.[!i] != '"' do
+        curr := !curr ^ "?";
+        i := !i + 1
+      done;
+      curr := !curr ^ "\"";
+      Vec.append !curr xs;
+      i := !i + 1
+    | '(' ->
+        Vec.append "(" xs;
+        i := !i + 1
+    | ')' ->
+        Vec.append ")" xs;
+        i := !i + 1
+    | _ ->
+        let token = ref "" in
+        while !i < String.length x && not (String.contains "() " x.[!i]) do
+          token := !token ^ String.make 1 x.[!i];
+          i := !i + 1
+        done;
+        Vec.append !token xs
+  done;
+  xs
+
+let parse_symbol (p : parser) : string option =
+  let* x = p#curr in
+  if Str.string_match (Str.regexp "[-a-z][0-9]*") x 0 then
+    begin
+      p#advance;
+      Some x
+    end
+  else
+    None
+
+let parse_variable (p : parser) : Types.value option =
+  let* x = parse_symbol p in
+  Some (Types.ValueVariable x)
+
+let parse_literal (p : parser) : Types.value option =
+  match p#curr with
+  | Some "true" ->
+     p#advance;
+     Some (ValueLiteral (LiteralBool true))
+  | Some "false" ->
+     p#advance;
+     Some (ValueLiteral (LiteralBool false))
+  | Some x ->
+     (match int_of_string_opt x with
+      | Some n ->
+         p#advance;
+         Some (ValueLiteral (LiteralInt n))
+      | _ -> 
+        if String.starts_with ~prefix:"\"" x then
+          begin
+            p#advance;
+            Some (ValueLiteral (LiteralString x))
+          end
+        else
+          parse_variable p)
+  | _ -> None
+
+let rec parse_expression (p : parser) : Types.value option =
+  parse_binding p
+
+and parse_funccall (p : parser) : Types.value option =
+  match (p#curr, p#next) with
+  | (Some "(", Some "(") ->
+     p#advance;
+     let* f = parse_funcdef p in
+     let* x = parse_expression p in
+     p#expect ")";
+     Some (Types.ValueApplication (f, x))
+  | (Some "(", _) ->
+     p#advance;
+     let* f = parse_symbol p in
+     let* x = parse_expression p in
+     p#expect ")";
+     Some (Types.ValueVarApplication (f, x))
+  | _ -> parse_literal p
+
+and parse_funcdef (p : parser) : Types.value option =
+  match (p#curr, p#next) with
+  | (Some "(", Some "fn") ->
+     p#advance;
+     p#advance;
+     let* name = parse_symbol p in
+     let* body = parse_expression p in
+     p#expect ")";
+     Some (Types.ValueFunction (name, body))
+  | _ -> parse_funccall p
+
+and parse_binding (p : parser) : Types.value option =
+  match (p#curr, p#next) with
+  | (Some "(", Some "let") ->
+     p#advance;
+     p#advance;
+     let* name = parse_symbol p in
+     let* value = parse_expression p in
+     let* body = parse_expression p in
+     Some (Types.ValueBinder (name, value, body))
+  | _ -> parse_funcdef p
+
+let print_tokens (xs : string vec) : unit =
+  xs 
+  |> Vec.map (Printf.sprintf "\"%s\"")
+  |> Vec.join ", "
+  |> Printf.sprintf "tokens: [ %s ]"
+  |> print_string 
+  |> print_newline
+
+let parse_language (x : string) : Types.value option =
+  let tokens = tokenize x in
+  print_tokens tokens;
+  parse_expression (new parser tokens)
+
+let main =
+  while true do
+    begin
+      print_string "repl> ";
+      let x = read_line () in
+      match parse_language x with
+      | Some ast ->
+         (match ast |> Debug.print Debug.ast "ast" |> do_infer with
+          | None ->
+             "Type-check failed"
+             |> print_string
+             |> print_newline
+          | Some x ->
+             x
+             |> Prettify.type'
+             |> print_string
+             |> print_newline)
+      | None ->
+         "Could not parse"
+         |> print_string
+         |> print_newline
+    end
+  done
diff --git a/users/wpcarro/scratch/compiler/inference.ml b/users/wpcarro/scratch/compiler/inference.ml
new file mode 100644
index 0000000000..e00904a09e
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/inference.ml
@@ -0,0 +1,183 @@
+(*******************************************************************************
+ * WIP implementation of the Hindley-Milner type system primarily for learning
+ * purposes.
+ *
+ * Wish List:
+ * - TODO Debug this inference (let f (fn x x) f)
+ ******************************************************************************)
+
+open Types
+open Debug
+
+(*******************************************************************************
+ * Library
+ ******************************************************************************)
+
+let ( let* ) = Option.bind
+
+let set_from_list (xs : string list) : set =
+  xs |> List.fold_left (fun acc x -> FromString.add x true acc) FromString.empty
+
+(* Map union that favors the rhs values (i.e. "last writer wins"). *)
+let lww (xs : 'a FromString.t) (ys : 'a FromString.t) : 'a FromString.t =
+  FromString.union (fun k x y -> Some y) xs ys
+
+let emptyEnv : env = FromString.empty
+
+let rec free_type_vars (t : _type) : set =
+  match t with
+  | TypeVariable k -> FromString.singleton k true
+  | TypeInt -> FromString.empty
+  | TypeBool -> FromString.empty
+  | TypeString -> FromString.empty
+  | TypeArrow (a, b) -> lww (free_type_vars a) (free_type_vars b)
+
+let i : int ref = ref 0
+
+let make_type_var () : _type =
+  let res = Printf.sprintf "a%d" !i in
+  i := !i + 1;
+  TypeVariable res
+
+exception OccursCheck
+
+let bind_var (k : string) (t : _type) : substitution =
+  if t == TypeVariable k then FromString.empty
+  else if FromString.exists (fun name _ -> name == k) (free_type_vars t) then
+    raise OccursCheck
+  else FromString.singleton k t
+
+let rec instantiate (q : quantified_type) : _type =
+  let (QuantifiedType (names, t)) = q in
+  match t with
+  | TypeInt -> TypeInt
+  | TypeBool -> TypeBool
+  | TypeString -> TypeString
+  | TypeVariable k ->
+      if List.exists (( == ) k) names then make_type_var () else TypeVariable k
+  | TypeArrow (a, b) ->
+      TypeArrow
+        (instantiate (QuantifiedType (names, a)), instantiate (QuantifiedType (names, b)))
+
+let quantified_type_ftvs (q : quantified_type) : set =
+  let (QuantifiedType (names, t)) = q in
+  lww (free_type_vars t) (names |> set_from_list)
+
+let generalize (env : env) (t : _type) : quantified_type =
+  let envftv =
+    env |> FromString.bindings
+    |> List.map (fun (_, v) -> quantified_type_ftvs v)
+    |> List.fold_left lww FromString.empty
+  in
+  let names =
+    lww (free_type_vars t) envftv
+    |> FromString.bindings
+    |> List.map (fun (k, _) -> k)
+  in
+  QuantifiedType (names, t)
+
+let rec substitute_type (s : substitution) (t : _type) : _type =
+  match t with
+  | TypeVariable k as tvar ->
+     (match FromString.find_opt k s with
+      | Some v -> substitute_type s v
+      | None -> tvar)
+  | TypeArrow (a, b) -> TypeArrow (substitute_type s a, substitute_type s b)
+  | TypeInt -> TypeInt
+  | TypeBool -> TypeBool
+  | TypeString -> TypeString
+
+let substitute_quantified_type (s : substitution) (q : quantified_type) : quantified_type =
+  let (QuantifiedType (names, t)) = q in
+  let s1 =
+    FromString.filter (fun k v -> List.exists (fun x -> k != x) names) s
+  in
+  QuantifiedType (names, substitute_type s1 t)
+
+let substitute_env (s : substitution) (env : env) : env =
+  FromString.map (fun q -> substitute_quantified_type s q) env
+
+let compose_substitutions (xs : substitution list) : substitution =
+  let do_compose_substitutions s1 s2 = lww s2 (FromString.map (substitute_type s2) s1) in
+  List.fold_left do_compose_substitutions FromString.empty xs
+
+let rec unify (a : _type) (b : _type) : substitution option =
+  match (a, b) with
+  | TypeInt, TypeInt -> Some FromString.empty
+  | TypeBool, TypeBool -> Some FromString.empty
+  | TypeString, TypeString -> Some FromString.empty
+  | TypeVariable k, _ -> Some (bind_var k b)
+  | _, TypeVariable k -> Some (bind_var k a)
+  | TypeArrow (a, b), TypeArrow (c, d) ->
+      let* s1 = unify a c in
+      let* s2 = unify (substitute_type s1 b) (substitute_type s1 d) in
+      let s3 = compose_substitutions [s1; s2] in
+      s1 |> Debug.substitution |> Printf.sprintf "s1: %s\n" |> print_string;
+      s2 |> Debug.substitution |> Printf.sprintf "s2: %s\n" |> print_string;
+      s3 |> Debug.substitution |> Printf.sprintf "s3: %s\n" |> print_string;
+      Some s3
+  | _ -> None
+
+let print_env (env : env) =
+  Printf.sprintf "env: %s\n" (Debug.env env)
+  |> print_string
+
+let print_val (x : value) =
+  Printf.sprintf "val: %s\n" (Debug.value x)
+  |> print_string
+
+let print_inference (x : inference option) =
+  match x with
+  | None -> "no inference\n" |> print_string
+  | Some x ->
+     Printf.sprintf "inf: %s\n" (Debug.inference x)
+     |> print_string
+
+let rec infer (env : env) (x : value) : inference option =
+  print_env env;
+  print_val x;
+  let res = match x with
+  | ValueLiteral lit -> (
+      match lit with
+      | LiteralInt _ -> Some (Inference (FromString.empty, TypeInt))
+      | LiteralBool _ -> Some (Inference (FromString.empty, TypeBool))
+      | LiteralString _ -> Some (Inference (FromString.empty, TypeString)))
+  | ValueVariable k ->
+      let* v = FromString.find_opt k env in
+      Some (Inference (FromString.empty, instantiate v))
+  | ValueFunction (param, body) ->
+      let typevar = make_type_var () in
+      let env1 = FromString.remove param env in
+      let env2 = lww (FromString.singleton param (QuantifiedType ([], typevar))) env1 in
+      let* (Inference (s1, t1)) = infer env2 body in
+      Some (Inference (s1, TypeArrow (substitute_type s1 typevar, t1)))
+  | ValueApplication (f, x) ->
+      let result = make_type_var () in
+      let* (Inference (s1, t1)) = infer env f in
+      let* (Inference (s2, t2)) = infer (substitute_env s1 env) x in
+      let* s3 = unify (substitute_type s2 t1) (TypeArrow (t2, result)) in
+      Some (Inference
+              ( compose_substitutions [s3; s2; s1],
+                substitute_type s3 result ))
+  | ValueVarApplication (name, x) ->
+      let* v = FromString.find_opt name env in
+      let t1 = instantiate v in
+      let typevar = make_type_var () in
+      let* (Inference (s2, t2)) = infer env x in
+      let* s3 = unify (substitute_type s2 t1) (TypeArrow (t2, typevar)) in
+      Some (Inference
+              ( compose_substitutions [s2; s3],
+                substitute_type s3 typevar ))
+  | ValueBinder (k, v, body) ->
+      let* (Inference (s1, t1)) = infer env v in
+      let env1 = FromString.remove k env in
+      let tg = generalize (substitute_env s1 env) t1 in
+      let env2 = FromString.add k tg env1 in
+      let* (Inference (s2, t2)) = infer (substitute_env s1 env2) body in
+      Some (Inference (compose_substitutions [s1; s2], t2)) in
+  print_inference res;
+  res
+
+let do_infer (x : value) : _type option =
+  let* Inference (_, t) = infer FromString.empty x in
+  Some t
diff --git a/users/wpcarro/scratch/compiler/parser.ml b/users/wpcarro/scratch/compiler/parser.ml
new file mode 100644
index 0000000000..dc66f2506e
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/parser.ml
@@ -0,0 +1,47 @@
+(****************************************************************************** 
+ * Defines a generic parser class.
+ ******************************************************************************)
+
+open Vec
+
+exception ParseError of string
+
+type token = string
+type state = { i : int; tokens : token vec }
+
+class parser (tokens : token vec) =
+  object (self)
+    val mutable tokens = tokens
+    val mutable i = ref 0
+
+    method advance = i := !i + 1
+    method prev : token option = Vec.get (!i - 1) tokens
+    method curr : token option = Vec.get !i tokens
+    method next : token option = Vec.get (!i + 1) tokens
+
+    method consume : token option =
+      match self#curr with
+      | None -> None
+      | Some x as res ->
+          self#advance;
+          res
+
+    method expect (x : token) =
+      match self#curr with
+      | Some y when x = y -> self#advance
+      | _ -> raise (ParseError (Printf.sprintf "Expected %s" x))
+
+    method matches (x : token) : bool =
+      match self#curr with
+      | None -> false
+      | Some y ->
+          if x = y then
+            begin
+              self#advance;
+              true
+            end
+          else false
+
+    method exhausted : bool = !i >= Vec.length tokens
+    method state : state = { i = !i; tokens }
+  end
diff --git a/users/wpcarro/scratch/compiler/prettify.ml b/users/wpcarro/scratch/compiler/prettify.ml
new file mode 100644
index 0000000000..7903ad3694
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/prettify.ml
@@ -0,0 +1,9 @@
+open Types
+
+(* Pretty-print the type, t. *)
+let rec type' (t : _type) : string =
+  match t with
+  | TypeInt -> "Integer"
+  | TypeBool -> "Boolean"
+  | TypeVariable k -> Printf.sprintf "%s" k
+  | TypeArrow (a, b) -> Printf.sprintf "%s -> %s" (type' a) (type' b)
diff --git a/users/wpcarro/scratch/compiler/register_vm.ml b/users/wpcarro/scratch/compiler/register_vm.ml
new file mode 100644
index 0000000000..0a573048e7
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/register_vm.ml
@@ -0,0 +1,129 @@
+(*
+  Rewriting the Python implementation of the register VM in OCaml to see how
+  how much imperative/mutative programming OCaml allows.
+
+  Note: Some of this code is intentionally not written in a functional style
+  because one of the goals was to see how similar this OCaml implementation
+  could be to the Python implementation.
+
+  Conclusion: It's pretty easy to switch between the two languages.
+
+  Usage: Recommended compilation settings I hastily found online:
+  $ ocamlopt -w +A-42-48 -warn-error +A-3-44 ./register_vm.ml && ./a.out
+
+  Formatting:
+  $ ocamlformat --inplace --enable-outside-detected-project ./register_vm.ml
+ *)
+
+open Vec
+
+type reg = X | Y | Res
+type binop = int -> int -> int
+
+type ast =
+  | Const of int
+  | Add of ast * ast
+  | Sub of ast * ast
+  | Mul of ast * ast
+  | Div of ast * ast
+
+type opcode0 =
+  | Op0AssignRegLit of reg * int
+  | Op0AssignRegReg of reg * reg
+  | Op0BinOp of binop * reg * reg * reg
+  | Op0PushReg of reg
+  | Op0PopAndSet of reg
+  | Op0Null
+
+type opcode1 =
+  | Op1AssignRegLit of int * int
+  | Op1AssignRegReg of int * int
+  | Op1BinOp of (int -> int -> int) * int * int * int
+  | Op1PushReg of int
+  | Op1PopAndSet of int
+  | Op1Null
+
+type opcodes0 = opcode0 vec
+type opcodes1 = opcode1 vec
+
+let registers : int vec = Vec.make 8 0
+let stack : int Stack.t = Stack.create ()
+let reg_idx (r : reg) : int = match r with X -> 0 | Y -> 1 | Res -> 2
+
+let reg_name (r : reg) : string =
+  match r with X -> "x" | Y -> "y" | Res -> "res"
+
+let print_opcodes0 (xs : opcodes0) : opcodes0 =
+  let print_opcode x =
+    match x with
+    | Op0AssignRegLit (r, x) -> Printf.printf "%s <- %d\n" (reg_name r) x
+    | Op0AssignRegReg (dst, src) ->
+        Printf.printf "%s <- $%s\n" (reg_name dst) (reg_name src)
+    | Op0PushReg src -> Printf.printf "push $%s\n" (reg_name src)
+    | Op0PopAndSet dst -> Printf.printf "%s <- pop\n" (reg_name dst)
+    | Op0BinOp (_, lhs, rhs, dst) ->
+        Printf.printf "%s <- $%s ? $%s\n" (reg_name dst) (reg_name lhs)
+          (reg_name rhs)
+    | Op0Null -> ()
+  in
+  Vec.iter print_opcode xs;
+  xs
+
+let rec compile (ast : ast) : opcodes0 =
+  let result : opcodes0 = Vec.create () in
+  (match ast with
+   | Const x -> Vec.append (Op0AssignRegLit (Res, x)) result;
+   | Add (lhs, rhs) -> compile_bin_op ( + ) lhs rhs result
+   | Sub (lhs, rhs) -> compile_bin_op ( - ) lhs rhs result
+   | Mul (lhs, rhs) -> compile_bin_op ( * ) lhs rhs result
+   | Div (lhs, rhs) -> compile_bin_op ( / ) lhs rhs result);
+  result
+
+and compile_bin_op (f : binop) (lhs : ast) (rhs : ast) (result : opcodes0) =
+  lhs |> compile |> Vec.append_to result;
+  Vec.append (Op0PushReg Res) result;
+  rhs |> compile |> Vec.append_to result;
+  Vec.append (Op0PopAndSet X) result;
+  Vec.append (Op0AssignRegReg (Y, Res)) result;
+  Vec.append (Op0BinOp (f, X, Y, Res)) result
+
+let compile_registers (xs : opcodes0) : opcodes1 =
+  let do_compile x =
+    match x with
+    | Op0AssignRegLit (dst, x) -> Op1AssignRegLit (reg_idx dst, x)
+    | Op0AssignRegReg (dst, src) -> Op1AssignRegReg (reg_idx dst, reg_idx src)
+    | Op0PushReg src -> Op1PushReg (reg_idx src)
+    | Op0PopAndSet dst -> Op1PopAndSet (reg_idx dst)
+    | Op0BinOp (f, lhs, rhs, dst) -> Op1BinOp (f, reg_idx lhs, reg_idx rhs, reg_idx dst)
+    | Op0Null -> Op1Null
+  in
+  Vec.map do_compile xs
+
+let eval (xs : opcodes1) : int =
+  let ip = ref 0 in
+  while !ip < Vec.length xs do
+    match Vec.get_unsafe !ip xs with
+    | Op1AssignRegLit (dst, x) ->
+        Vec.set dst x registers;
+        ip := !ip + 1
+    | Op1AssignRegReg (dst, src) ->
+        Vec.set dst (Vec.get_unsafe src registers) registers;
+        ip := !ip + 1
+    | Op1PushReg src ->
+        Stack.push (Vec.get_unsafe src registers) stack;
+        ip := !ip + 1
+    | Op1PopAndSet dst ->
+        Vec.set dst (Stack.pop stack) registers;
+        ip := !ip + 1
+    | Op1BinOp (f, lhs, rhs, dst) ->
+        let lhs = Vec.get_unsafe lhs registers in
+        let rhs = Vec.get_unsafe rhs registers in
+        Vec.set dst (f lhs rhs) registers;
+        ip := !ip + 1
+    | Op1Null -> ip := !ip + 1
+  done;
+  Vec.get_unsafe (reg_idx Res) registers
+;;
+
+Add (Mul (Const 2, Div (Const 100, Const 2)), Const 5)
+|> compile |> print_opcodes0 |> compile_registers |> eval |> print_int
diff --git a/users/wpcarro/scratch/compiler/register_vm.py b/users/wpcarro/scratch/compiler/register_vm.py
new file mode 100644
index 0000000000..302bce5a0e
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/register_vm.py
@@ -0,0 +1,161 @@
+# Silly proof-of-concept register VM.
+
+def compile_binary_op(op, ast):
+    result = []
+    for x in compile(ast[1]):
+        result.append(x)
+    result.append(PUSH_REG)
+    result.append(RES)
+    for x in compile(ast[2]):
+        result.append(x)
+    result.append(ASSIGN_REG_REG)
+    result.append(Y)
+    result.append(RES)
+    result.append(POP)
+    result.append(X)
+    result.append(op)
+    return result
+
+def compile(ast):
+    result = []
+
+    if ast[0] == 'CONST':
+        result.append(ASSIGN_REG_LIT)
+        result.append(RES)
+        result.append(ast[1])
+    elif ast[0] == 'ADD':
+        result += compile_binary_op(ADD, ast)
+    elif ast[0] == 'SUB':
+        result += compile_binary_op(SUB, ast)
+    elif ast[0] == 'MUL':
+        result += compile_binary_op(MUL, ast)
+    elif ast[0] == 'DIV':
+        result += compile_binary_op(DIV, ast)
+    elif ast[0] == 'RETURN':
+        result.append(RETURN)
+    else:
+        raise Exception('Cannot compile unknown AST node: {}'.format(ast[0]))
+
+    return result
+
+# opcodes
+ASSIGN_REG_LIT = 0x0
+ASSIGN_REG_REG = 0x1
+ADD = 0x2
+SUB = 0x3
+MUL = 0x4
+DIV = 0x5
+SWAP = 0x6
+RETURN = 0x7
+PUSH_REG = 0x8
+POP = 0x9
+
+# register indices
+X = 0x0
+Y = 0x1
+RES = 0x2
+
+registers = [0x0] * 8
+stack = []
+
+def reg_name(i):
+    if i == X: return 'x'
+    if i == Y: return 'x'
+    if i == RES: return 'res'
+
+def print_instructions(xs):
+    i = 0
+
+    while i < len(xs):
+        if xs[i] == ASSIGN_REG_LIT:
+            # print('ASSIGN_REG_LIT {} {}'.format(reg_name(xs[i + 1]), xs[i + 2]))
+            print('{} <- {}'.format(reg_name(xs[i + 1]), xs[i + 2]))
+            i += 3
+        elif xs[i] == ASSIGN_REG_REG:
+            # print('ASSIGN_REG_REG {} {}'.format(reg_name(xs[i + 1]), reg_name(xs[i + 2])))
+            print('{} <- ${}'.format(reg_name(xs[i + 1]), reg_name(xs[i + 2])))
+            i += 3
+        elif xs[i] == ADD:
+            print('add')
+            i += 1
+        elif xs[i] == SUB:
+            print('sub')
+            i += 1
+        elif xs[i] == MUL:
+            print('mul')
+            i += 1
+        elif xs[i] == DIV:
+            print('div')
+            i += 1
+        elif xs[i] == PUSH_REG:
+            print('push ${}'.format(reg_name(xs[i + 1])))
+            i += 2
+        elif xs[i] == POP:
+            print('{} <- pop'.format(reg_name(xs[i + 1])))
+            i += 2
+        else:
+            raise Exception('Cannot print instruction: {}'.format(xs[i]))
+
+def eval(instructions):
+    print_instructions(instructions)
+    ip = 0
+    cont = True
+    while ip < len(instructions):
+        if instructions[ip] == ASSIGN_REG_LIT:
+            r = instructions[ip + 1]
+            x = instructions[ip + 2]
+            registers[r] = x
+            ip += 3
+        elif instructions[ip] == ASSIGN_REG_REG:
+            r_dst = instructions[ip + 1]
+            r_src = instructions[ip + 2]
+            registers[r_dst] = registers[r_src]
+            ip += 3
+        elif instructions[ip] == ADD:
+            registers[RES] = registers[X] + registers[Y]
+            ip += 1
+        elif instructions[ip] == MUL:
+            registers[RES] = registers[X] * registers[Y]
+            ip += 1
+        elif instructions[ip] == SUB:
+            registers[RES] = registers[X] - registers[Y]
+            ip += 1
+        elif instructions[ip] == MUL:
+            registers[RES] = registers[X] * registers[Y]
+            ip += 1
+        elif instructions[ip] == DIV:
+            registers[RES] = registers[X] / registers[Y]
+            ip += 1
+        elif instructions[ip] == SWAP:
+            r1 = instructions[ip + 1]
+            r2 = instructions[ip + 2]
+            registers[r1], registers[r2] = registers[r2], registers[r1]
+            ip += 3
+        elif instructions[ip] == RETURN:
+            ip += 1
+            cont = False
+            return registers[RES]
+        elif instructions[ip] == PUSH_REG:
+            src = instructions[ip + 1]
+            stack.append(registers[src])
+            ip += 2
+        elif instructions[ip] == POP:
+            dst = instructions[ip + 1]
+            registers[dst] = stack.pop()
+            ip += 2
+        else:
+            raise Exception('Cannot eval instruction: {}'.format(instructions[ip]))
+    return registers[RES]
+
+def main():
+    ast = ['ADD',
+           ['MUL',
+            ['MUL', ['CONST', 2], ['CONST', 3]],
+            ['DIV', ['CONST', 5], ['CONST', 5]]],
+           ['ADD',
+            ['SUB', ['CONST', 10], ['CONST', 1]],
+            ['MUL', ['CONST', 2], ['CONST', 2]]]]
+
+    print('result: {}'.format(eval(compile(ast))))
+
+main()
diff --git a/users/wpcarro/scratch/compiler/shell.nix b/users/wpcarro/scratch/compiler/shell.nix
new file mode 100644
index 0000000000..ec339eb91d
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/shell.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    ocaml
+    ocamlPackages.utop
+    ocamlformat
+  ];
+}
diff --git a/users/wpcarro/scratch/compiler/tests.ml b/users/wpcarro/scratch/compiler/tests.ml
new file mode 100644
index 0000000000..828cbd16f0
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/tests.ml
@@ -0,0 +1,43 @@
+open Expr_parser
+open Type_parser
+open Inference
+
+type test = { input : string; expect : string; }
+(* type sub_test = { s1 : string; s2 : string; s3 : string } *)
+
+let ( let* ) = Option.bind
+
+let tests = [
+    { input = "((fn x x) 10)"; expect = "Integer"; };
+    { input = "(let f (fn x x) f)"; expect = "a -> a"; };
+]
+
+(* let sub_tests = [ *)
+(*     { *)
+(*       s1 = "{b |-> b -> Int}"; *)
+(*       s2 = "{a: Bool, b: Int, c: Bool}"; *)
+(*       s3 = "{a: Bool, b: Int -> Int, c: Bool}"; *)
+(*     } *)
+(* ] *)
+
+exception FailedAssertion
+exception TestError
+
+let main =
+  tests
+  |> List.iter (fun { input; expect } ->
+         Printf.sprintf ":t %s == %s\n" input expect |> print_string;
+         match (parse_language input, parse_input expect) with
+         | Some ast, Some expected ->
+            (match do_infer ast with
+             | Some actual ->
+                if actual != expected then
+                  begin
+                    print_type actual;
+                    raise FailedAssertion
+                  end
+                else
+                  print_string "Test passed.\n"
+             | _ -> raise TestError)
+         | _ -> raise TestError);
+  print_string "All tests pass!"
diff --git a/users/wpcarro/scratch/compiler/type_parser.ml b/users/wpcarro/scratch/compiler/type_parser.ml
new file mode 100644
index 0000000000..99cc8bbc4f
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/type_parser.ml
@@ -0,0 +1,104 @@
+(******************************************************************************
+ * Type Expression Language:
+ *
+ * Helpers:
+ *   symbol   -> [a-z]
+ *
+ * Core:
+ *   type     -> function
+ *   function -> ( variable | literal ) '->' type
+ *   literal  -> 'Integer' | 'Boolean'
+ *   variable -> symbol
+ ******************************************************************************)
+
+open Types
+open Prettify
+open Parser
+open Inference
+open Vec
+
+type side = LHS | RHS
+
+let ( let* ) = Option.bind
+
+let printsub (s : substitution) =
+  s |> Debug.substitution |> print_string |> print_newline
+
+let tokenize (x : string) : token vec =
+  let xs = Vec.create () in
+  let i = ref 0 in
+  while !i < String.length x do
+    match x.[!i] with
+    | ' ' -> i := !i + 1
+    | _ ->
+       let beg = !i in
+       while (!i < String.length x) && (x.[!i] != ' ') do
+         i := !i + 1
+       done;
+       Vec.append (String.sub x beg (!i - beg)) xs
+  done;
+  xs
+
+let rec parse_type (p : parser) : _type option =
+  parse_function p
+and parse_function (p : parser) : _type option =
+  match p#next with
+  | Some "->" ->
+     let* a = parse_literal p in
+     p#advance;
+     let* b = parse_type p in
+     Some (TypeArrow (a, b))
+  | _ -> parse_literal p
+and parse_literal (p : parser) : _type option =
+  match p#curr with
+  | Some "Integer" | Some "Int" -> p#advance; Some TypeInt
+  | Some "Boolean" | Some "Bool" -> p#advance; Some TypeBool
+  | Some _ -> parse_variable p
+  | None -> None
+and parse_variable (p : parser) : _type option =
+  match p#curr with
+  | Some x when String.length x = 1 -> p#advance; Some (TypeVariable x)
+  | _ -> None
+
+let print_tokens (xs : string vec) =
+  xs
+  |> Vec.map (Printf.sprintf "\"%s\"")
+  |> Vec.join ", "
+  |> Printf.sprintf "tokens: [ %s ]"
+  |> print_string 
+  |> print_newline
+
+let print_type (t : _type) =
+  t |> Debug.type' |> Printf.sprintf "type: %s" |> print_string |> print_newline
+
+let parse_input (x : string) : _type option =
+  let tokens = tokenize x in
+  print_tokens tokens;
+  parse_type (new parser tokens)
+
+(* Continually prompt until user provides a parseable type expression *)
+let rec read_type (arg : side) : _type =
+  let prompt = match arg with
+    | LHS -> "lhs> "
+    | RHS -> "rhs> " in
+  print_string prompt;
+  let x = read_line () in
+  match parse_input x with
+  | None ->
+     print_string "Failed to parse input.\n";
+     read_type arg
+  | Some ast ->
+     print_type ast;
+     ast
+
+let main =
+  while true do
+    begin
+      let lhs = read_type LHS in
+      let rhs = read_type RHS in
+      match unify lhs rhs with
+      | None ->
+         Printf.printf "Cannot unify \"%s\" with \"%s\"\n" (Debug.type' lhs) (Debug.type' rhs)
+      | Some x -> printsub x
+    end
+  done
diff --git a/users/wpcarro/scratch/compiler/types.ml b/users/wpcarro/scratch/compiler/types.ml
new file mode 100644
index 0000000000..0acd05737c
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/types.ml
@@ -0,0 +1,31 @@
+type literal 
+  = LiteralInt of int 
+  | LiteralBool of bool
+  | LiteralString of string
+
+(* Lambda Calculus definition *)
+type value =
+  | ValueLiteral of literal
+  | ValueVariable of string
+  | ValueFunction of string * value
+  | ValueApplication of value * value
+  | ValueVarApplication of string * value
+  | ValueBinder of string * value * value
+
+module FromString = Map.Make (String)
+
+type _type =
+  | TypeInt
+  | TypeBool
+  | TypeString
+  | TypeVariable of string
+  | TypeArrow of _type * _type
+
+type quantified_type = QuantifiedType of string list * _type
+
+type set = bool FromString.t
+type substitution = _type FromString.t
+
+type env = quantified_type FromString.t
+
+type inference = Inference of substitution * _type
diff --git a/users/wpcarro/scratch/compiler/vec.ml b/users/wpcarro/scratch/compiler/vec.ml
new file mode 100644
index 0000000000..549078c5d8
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/vec.ml
@@ -0,0 +1,127 @@
+(****************************************************************************** 
+ * Similar to Python's list
+ *
+ * - mutable
+ * - dynamically resized
+ * - O(1) read
+ * - O(1) write
+ * - O(1) append (average case)
+ *
+ ******************************************************************************)
+
+type 'a vec = {
+  mutable length: int;
+  mutable capacity: int;
+  mutable xs: 'a array;
+}
+
+(****************************************************************************** 
+ * Constructors
+ ******************************************************************************)
+
+let make (size : int) (seed : 'a) : 'a vec = { 
+  length = size;
+  capacity = size;
+  xs = Array.make size seed;
+}
+
+let create () = {
+  length = 0;
+  capacity = 0;
+  xs = [||];
+}
+
+let from_array (xs : 'a array) : 'a vec = {
+  length = Array.length xs;
+  capacity = Array.length xs;
+  xs = xs;
+}
+
+let from_list (xs : 'a list) : 'a vec = 
+  match xs with
+  | [] -> create ()
+  | y::ys -> 
+    let result = {
+      length = List.length xs;
+      capacity = List.length xs;
+      xs = Array.make (List.length xs) y;
+    } in
+    List.iteri (fun i x -> Array.set result.xs i x) xs;
+    result
+
+(****************************************************************************** 
+ * Miscellaneous
+ ******************************************************************************)
+
+let append (x : 'a) (v : 'a vec) =
+  if v.capacity = 0 then
+    begin
+      v.length <- 1;
+      v.capacity <- 1;
+      v.xs <- [|x|];
+    end
+  else if v.length = v.capacity then
+    begin
+      (* According to Wikipedia, Python uses 1.25 as the growth factor *)
+      let new_cap = v.capacity |> float_of_int |> Float.mul 1.25 |> ceil |> int_of_float in
+      let new_xs = Array.make new_cap x in
+      Array.iteri (fun i x -> Array.set new_xs i x) v.xs;
+      v.capacity <- new_cap;
+      v.xs <- new_xs;
+      Array.set v.xs v.length x;
+      v.length <- v.length + 1;
+    end
+  else
+    begin
+      Array.set v.xs v.length x;
+      v.length <- v.length + 1;
+    end
+
+let get (i : int) (v : 'a vec) : 'a option =
+  if i >= v.length then
+    None
+  else
+    Some v.xs.(i)
+
+let get_unsafe (i : int) (v : 'a vec) : 'a =
+  v.xs.(i)
+
+let set (i : int) (x : 'a) (v : 'a vec) : unit =
+  if i < v.length then
+    Array.set v.xs i x
+
+let length (v : 'a vec) : int = 
+  v.length
+
+let update (i : int) (f : 'a -> 'a) (v : 'a vec) : unit =
+  match get i v with
+  | None -> ()
+  | Some x -> set i (f x) v
+
+let iter (f : 'a -> unit) (v : 'a vec) : unit =
+  let n = ref 0 in
+  while !n < v.length do
+    f v.xs.(!n);
+    n := !n + 1;
+  done
+
+let join (sep : string) (v : string vec) : string =
+  if length v = 0 then
+    ""
+  else
+    let i = ref 1 in
+    let result = ref v.xs.(0) in
+    while !i < v.length do
+      result := !result ^ sep ^ v.xs.(!i);
+      i := !i + 1;
+    done;
+    !result
+
+let map (f : 'a -> 'b) (v : 'a vec) : 'b vec =
+  let result = create () in
+  iter (fun x -> append (f x) result) v;
+  result
+
+let append_to (dst : 'a vec) (xs : 'a vec) : unit =
+  iter (fun x -> append x dst) xs
+
diff --git a/users/wpcarro/scratch/groceries/shell.nix b/users/wpcarro/scratch/groceries/shell.nix
index 7682e8246c..0c6a298bf2 100644
--- a/users/wpcarro/scratch/groceries/shell.nix
+++ b/users/wpcarro/scratch/groceries/shell.nix
@@ -1,5 +1,5 @@
 { depot, ... }:
 
 depot.users.wpcarro.buildHaskell.shell {
-  deps = hpkgs: [];
+  deps = hpkgs: [ ];
 }
diff --git a/users/wpcarro/scratch/picoctf/challenge_166/shell.nix b/users/wpcarro/scratch/picoctf/challenge_166/shell.nix
index 07a3a2e281..85d3865a51 100644
--- a/users/wpcarro/scratch/picoctf/challenge_166/shell.nix
+++ b/users/wpcarro/scratch/picoctf/challenge_166/shell.nix
@@ -1,7 +1,8 @@
 { pkgs, ... }:
 
 let
-  python =pkgs.python3.withPackages (pypkgs: with pypkgs; [
+  python = pkgs.python3.withPackages (pypkgs: with pypkgs; [
     cryptography
   ]);
-in python.env
+in
+python.env
diff --git a/users/wpcarro/scratch/rust/.gitignore b/users/wpcarro/scratch/rust/.gitignore
new file mode 100644
index 0000000000..9f970225ad
--- /dev/null
+++ b/users/wpcarro/scratch/rust/.gitignore
@@ -0,0 +1 @@
+target/
\ No newline at end of file
diff --git a/users/wpcarro/scratch/rust/Cargo.lock b/users/wpcarro/scratch/rust/Cargo.lock
new file mode 100644
index 0000000000..28aa1250ce
--- /dev/null
+++ b/users/wpcarro/scratch/rust/Cargo.lock
@@ -0,0 +1,89 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "itoa"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rust"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
+
+[[package]]
+name = "serde"
+version = "1.0.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.99"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
diff --git a/users/wpcarro/scratch/rust/Cargo.toml b/users/wpcarro/scratch/rust/Cargo.toml
new file mode 100644
index 0000000000..76235d11d3
--- /dev/null
+++ b/users/wpcarro/scratch/rust/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "rust"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+serde_json = "1.0.81"
+serde = { version = "1.0.137", features = ["derive"] }
diff --git a/users/wpcarro/scratch/rust/README.md b/users/wpcarro/scratch/rust/README.md
new file mode 100644
index 0000000000..9ff7dd97ea
--- /dev/null
+++ b/users/wpcarro/scratch/rust/README.md
@@ -0,0 +1,11 @@
+# Rust
+
+Watch me fumble around as I learn Rust.
+
+## Usage
+
+```shell
+$ nix-shell /depot -A users.wpcarro.scratch.rust
+$ cargo new json && cd ./json
+$ cargo run json
+```
diff --git a/users/wpcarro/scratch/rust/shell.nix b/users/wpcarro/scratch/rust/shell.nix
new file mode 100644
index 0000000000..98e2dbf4b2
--- /dev/null
+++ b/users/wpcarro/scratch/rust/shell.nix
@@ -0,0 +1,7 @@
+{ pkgs ? import <nixpkgs> { }, ... }:
+
+pkgs.mkShell {
+  buildInputs = [
+    pkgs.cargo
+  ];
+}
diff --git a/users/wpcarro/scratch/rust/src/display/mod.rs b/users/wpcarro/scratch/rust/src/display/mod.rs
new file mode 100644
index 0000000000..8384631091
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/display/mod.rs
@@ -0,0 +1,13 @@
+use std::fmt;
+
+pub struct Person {
+    pub fname: String,
+    pub lname: String,
+    pub age: i8,
+}
+
+impl fmt::Display for Person {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}, {} ({} years old)", self.lname, self.fname, self.age)
+    }
+}
diff --git a/users/wpcarro/scratch/rust/src/json/mod.rs b/users/wpcarro/scratch/rust/src/json/mod.rs
new file mode 100644
index 0000000000..d3307b394e
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/json/mod.rs
@@ -0,0 +1,81 @@
+use serde::{Deserialize, Serialize};
+use serde_json::{json, Value};
+
+// From the serde_json docs:
+//
+// > There are three common ways that you might find yourself needing to work
+// > with JSON data in Rust.
+// >
+// > 1. As text data. An unprocessed string of JSON data that you receive on an
+// >    HTTP endpoint, read from a file, or prepare to send to a remote server.
+// > 2. As an untyped or loosely typed representation. Maybe you want to check
+// >    that some JSON data is valid before passing it on, but without knowing
+// >    the structure of what it contains. Or you want to do very basic
+// >    manipulations like insert a key in a particular spot.
+// > 3. As a strongly typed Rust data structure. When you expect all or most of
+// >    your data to conform to a particular structure and want to get real work
+// >    done without JSON’s loosey-goosey nature tripping you up.
+//
+// So let's take a look at all three...
+
+////////////////////////////////////////////////////////////////////////////////
+// Types
+////////////////////////////////////////////////////////////////////////////////
+
+#[derive(Serialize, Deserialize, Debug)]
+struct Person {
+    fname: String,
+    lname: String,
+    age: u8,
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Functions
+////////////////////////////////////////////////////////////////////////////////
+
+// 1) Reading/writing from/to plain text.
+//    TL;DR:
+//    - read:  serde_json::from_str(data)
+//    - write: x.to_string()
+pub fn one() {
+    let data = json!({
+        "fname": "William",
+        "lname": "Carroll",
+        "age": 30,
+    })
+    .to_string();
+
+    println!("result: {:?}", data);
+}
+
+// 2) Parse into a loosely typed representation; mutate it; serialize it back.
+//    TL;DR:
+//    - read:  serde_json::from_str(data)
+//    - write: x.to_string()
+pub fn two() {
+    let data = r#"{"fname":"William","lname":"Carroll","age":30}"#;
+
+    let mut parsed: Value = serde_json::from_str(data).unwrap();
+    parsed["fname"] = json!("Norm");
+    parsed["lname"] = json!("Macdonald");
+    parsed["age"] = json!(61);
+
+    let result = parsed.to_string();
+    println!("result: {:?}", result);
+}
+
+// 3) Parse into a strongly typed structure.
+//    TL;DR:
+//    - read:  serde_json::from_str(data)
+//    - write: serde_json::to_string(x).unwrap()
+pub fn three() {
+    let data = r#"{"fname":"William","lname":"Carroll","age":30}"#;
+
+    let mut read: Person = serde_json::from_str(data).unwrap();
+    read.fname = "Norm".to_string();
+    read.lname = "Macdonald".to_string();
+    read.age = 61;
+
+    let write = serde_json::to_string(&read).unwrap();
+    println!("result: {:?}", write);
+}
diff --git a/users/wpcarro/scratch/rust/src/main.rs b/users/wpcarro/scratch/rust/src/main.rs
new file mode 100644
index 0000000000..671b330930
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/main.rs
@@ -0,0 +1,15 @@
+use serde::{Deserialize, Serialize};
+use serde_json::{json, Value};
+
+mod display;
+mod json;
+mod rc;
+mod stdin;
+
+////////////////////////////////////////////////////////////////////////////////
+// Main
+////////////////////////////////////////////////////////////////////////////////
+
+fn main() {
+    rc::example();
+}
diff --git a/users/wpcarro/scratch/rust/src/rc/mod.rs b/users/wpcarro/scratch/rust/src/rc/mod.rs
new file mode 100644
index 0000000000..67251ca6aa
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/rc/mod.rs
@@ -0,0 +1,12 @@
+// Playing around with Rust's "smart pointers". Starting off with a wrapper type
+// that allows multiple readers (owners?) of some data.
+
+use std::rc::Rc;
+
+pub fn example() {
+    let five = Rc::new(5);
+    let x = Rc::clone(&five);
+    let y = Rc::clone(&five);
+    let z = Rc::clone(&five);
+    println!("result: {}", *x + *y + *z)
+}
diff --git a/users/wpcarro/scratch/rust/src/stdin/mod.rs b/users/wpcarro/scratch/rust/src/stdin/mod.rs
new file mode 100644
index 0000000000..4be95afa45
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/stdin/mod.rs
@@ -0,0 +1,22 @@
+use std::io::Write;
+use std::process::{Command, Stdio};
+
+// Example of piping-in a string defined in Rust to a shell command.
+pub fn example() {
+    let input = "Hello, world!";
+
+    let mut cat = Command::new("cat")
+        .stdin(Stdio::piped())
+        .spawn()
+        .ok()
+        .unwrap();
+
+    cat.stdin
+        .take()
+        .unwrap()
+        .write_all(&input.as_bytes())
+        .unwrap();
+
+    let output = cat.wait_with_output().unwrap();
+    println!("{}", String::from_utf8_lossy(&output.stdout));
+}
diff --git a/users/wpcarro/scratch/simple-select/README.md b/users/wpcarro/scratch/simple-select/README.md
new file mode 100644
index 0000000000..69e5707302
--- /dev/null
+++ b/users/wpcarro/scratch/simple-select/README.md
@@ -0,0 +1,71 @@
+# Simple Select
+
+- Simple Select is a less expressive but more ergonomic query language for
+  tabular data than SQL.
+- `slx` is a command-line tool for querying CSVs using the Simple Select query
+  language.
+
+Simple Select queries look like this: `director:"Tarantino" OR director:"Scorsese"`.
+
+## Example
+
+Say we have the following data in a CSV:
+
+```csv
+title,year,rating,director
+"Spirited Away",2001,8.5,"Hayao Miyazaki"
+Andhadhun,2018,8.1,"Sriram Raghavan"
+Dangal,2016,8.3,"Sriram Raghavan"
+"Avengers: Infinity War",2019,8.4,"Anthony Russo"
+Alien,1979,8.4,"Ridley Scott"
+...
+```
+
+We can invoke `slx` like so...
+
+```
+$ slx -f /tmp/movies.csv
+```
+
+...and then query using the REPL:
+
+```
+> director:/S.*m/ OR director:"Hayao"
+Andhadhun       2018    8.1     1       Sriram Raghavan 0       1
+Dangal  2016    8.3     1       Sriram Raghavan 0       1
+Howls Moving Castle     2004    8.2     0       Hayao Miyazaki  1       1
+Judgment at Nuremberg   1961    8.1     0       Stanley Kramer  0       0
+Laputa: Castle in the Sky       1986    8.0     0       Hayao Miyazaki  1       1
+Nausicaa of the Valley of the Wind      1984    8.0     0       Hayao Miyazaki  1       1
+Network 1976    8.1     0       Sidney Lumet    0       0
+```
+
+## Warning
+
+Simple Select is **not intended for production use**. I wrote this as a toy
+project for my own consumption. There are quite a few bugs of which I'm aware
+and quite a few other features that I'd like to support but haven't had time to
+support just yet.
+
+Why publish it then? Maybe this project will inspire drive-by contributions or
+other, better-implemented spin-offs.
+
+## Wish List
+
+Speaking of drive-by contributions, here are some things that I'd like to
+support:
+
+- Implicit `AND` conjunctions (`director:/Tarantino/ year:"2000"` instead of
+  `director:/Tarantino/ AND year:"2000"`)
+- Support for types like numbers, dates (`year:2000` instead of `year:"2000"`)
+- `slx` should support CSV *and* (at the very least) sqlite3 file formats (open
+  to other formats as well)
+- Regexes should be the default query primitive (`director:Tarantino` instead of
+  `director:/Tarantino/`)
+- Improve parsing errors (including surfacing errors to the user)
+- Support for reading from `STDIN` and issuing queries from the command-line
+- Unit-testing
+- Configurable delimiters for output data (right now it's just `\t`)
+- (Maybe) rewrite in a faster, more-type-safe languages (e.g. Rust)
+
+I'm likely missing other FRs, bugs, so please file issues!
diff --git a/users/wpcarro/scratch/simple-select/main.py b/users/wpcarro/scratch/simple-select/main.py
new file mode 100644
index 0000000000..3ae6c5d60e
--- /dev/null
+++ b/users/wpcarro/scratch/simple-select/main.py
@@ -0,0 +1,262 @@
+from argparse import ArgumentParser
+
+import csv
+from parser import Parser
+import sqlite3
+import string
+from scanner import Scanner
+import re
+import readline
+
+################################################################################
+# Predicates
+################################################################################
+
+def is_alpha(c):
+  return c in string.ascii_letters
+
+def is_digit(c):
+  return c in "0123456789"
+
+def is_alphanumeric(c):
+  return is_alpha(c) or is_digit(c)
+
+def is_whitespace(c):
+  return c in " \r\t\n"
+
+################################################################################
+# Tokenizer
+################################################################################
+
+AND    = ("CONJUNCTION", "AND")
+OR     = ("CONJUNCTION", "OR")
+NOT    = ("PUNCTUATION", "NOT")
+COLON  = ("PUNCTUATION", "COLON")
+LPAREN = ("PUNCTUATION", "LPAREN")
+RPAREN = ("PUNCTUATION", "RPAREN")
+
+def tokenize(x):
+  s = Scanner(x)
+  tokens = scan_tokens(s)
+  return tokens
+
+def scan_tokens(s):
+  result = []
+  while not s.exhausted():
+    if is_whitespace(s.peek()):
+      s.advance()
+    else:
+      result.append(scan_token(s))
+  return result
+
+def scan_token(s):
+  punctuation = {
+      "-": NOT,
+      ":": COLON,
+      "(": LPAREN,
+      ")": RPAREN,
+  }
+  c = s.peek()
+  if c in punctuation:
+    s.advance()
+    return punctuation[c]
+  if c == "\"":
+    return tokenize_string(s)
+  if c == "/":
+    return tokenize_regex(s)
+  if is_alpha(c):
+    return tokenize_identifier(s)
+
+def tokenize_string(s):
+  s.advance() # ignore opening 2x-quote
+  current = ""
+  while s.peek() != "\"" and not s.exhausted():
+    current += s.advance()
+  if s.exhausted():
+    raise Exception("Unterminated string")
+  s.advance() # ignore closing 2x-quote
+  return ("STRING", current)
+
+def tokenize_regex(s):
+  s.advance() # ignore opening forward-slash
+  current = ""
+  while s.peek() != "/" and not s.exhausted():
+    current += s.advance()
+  if s.exhausted():
+    raise Exception("Unterminated regex")
+  s.advance() # ignore closing forward-slash
+  return ("REGEX", current)
+
+def tokenize_identifier(s):
+  conjunctions = {
+      "AND",
+      "OR",
+  }
+  current = s.advance()
+  while is_alphanumeric(s.peek()):
+    current += s.advance()
+  if current.upper() in conjunctions:
+    return ("CONJUNCTION", current.upper())
+  else:
+    return ("IDENTIFIER", current)
+
+################################################################################
+# Parser
+################################################################################
+
+# EBNF
+# Note: we order expression types by ascending levels of precedence.
+#
+# expression  -> conjunction ;
+# conjunction -> selection ( ( "AND" | "OR" )? selection )* ;
+# selection   -> "-"? IDENTIFIER ":" ( REGEX | STRING ) | grouping ;
+# grouping    -> REGEX | STRING | "(" expression ")" ;
+
+def parse(x):
+  tokens = tokenize(x)
+  p = Parser(tokens)
+  return expression(p)
+
+def expression(p):
+  return conjunction(p)
+
+def conjunction(p):
+  lhs = selection(p)
+
+  # TODO(wpcarro): Support default AND conjuctions when they're undefined.
+  while not p.exhausted() and p.match({AND, OR}):
+    conj = p.peek(n=-1)
+    rhs = selection(p)
+    lhs = ("CONJUNCTION", conj[1], lhs, rhs)
+
+  return lhs
+
+def selection(p):
+  negate = False
+  if p.peek() == NOT:
+    negate = True
+    p.advance()
+
+  if p.peek()[0] != "IDENTIFIER":
+    return grouping(p)
+
+  ident = p.expect(lambda x: x[0] == "IDENTIFIER")
+  colon = p.expect(lambda x: x[1] == "COLON")
+  value = p.expect(lambda x: x[0] in {"REGEX", "STRING"})
+  return ("SELECTION", negate, ident[1], value)
+
+def grouping(p):
+  if p.peek()[0] == "REGEX":
+    return p.advance()
+
+  if p.peek()[0] == "STRING":
+    return p.advance()
+
+  if p.peek() == LPAREN:
+    p.advance()
+    expr = expression(p)
+    p.expect(lambda x: x == RPAREN)
+    return ("GROUPING", expr)
+
+################################################################################
+# Compiler
+################################################################################
+
+def compile(source, table, columns):
+  ast = parse(source)
+  return "SELECT * FROM {} WHERE {};".format(table, do_compile(ast, columns))
+
+def do_compile(ast, columns):
+  if ast[0] == "REGEX":
+    cols = "({})".format(" || ".join(columns))
+    return "{} REGEXP '.*{}.*'".format(cols, ast[1])
+
+  if ast[0] == "STRING":
+    cols = "({})".format(" || ".join(columns))
+    return "{} LIKE '%{}%'".format(cols, ast[1])
+
+  if ast[0] == "SELECTION":
+    return compile_selection(ast)
+
+  if ast[0] == "CONJUNCTION":
+    _, conj, lhs, rhs = ast
+    lhs = do_compile(lhs, columns)
+    rhs = do_compile(rhs, columns)
+    return "{} {} {}".format(lhs, conj, rhs)
+
+  if ast[0] == "GROUPING":
+    return "({})".format(do_compile(ast[1], columns))
+
+  raise Exception("Unexpected AST: \"{}\"".format(ast))
+
+def compile_selection(ast):
+  _, negate, column, query = ast
+  match = compile_query(negate, query)
+  return "{} {}".format(column, match)
+
+def compile_query(negate, query):
+  query_type, query_string = query
+  if query_type == "REGEX":
+    if negate:
+      return "NOT REGEXP '.*{}.*'".format(query_string)
+    return "REGEXP '.*{}.*'".format(query_string)
+
+  if query_type == "STRING":
+    if negate:
+      return "NOT LIKE '%{}%'".format(query_string)
+    return "LIKE '%{}%'".format(query_string)
+
+################################################################################
+# Helper Functions
+################################################################################
+
+def regexp(expr, x):
+  reg = re.compile(expr)
+  return reg.search(x) is not None
+
+################################################################################
+# Main
+################################################################################
+
+def main(csv_path=None, debug=False):
+  # Import CSV to SQLite
+  table = "main"
+  con = sqlite3.connect(":memory:")
+
+  con.create_function("REGEXP", 2, regexp)
+
+  cur = con.cursor()
+  with open(csv_path, "r") as f:
+    r = csv.DictReader(f)
+    columns = next(r).keys()
+
+    # TODO(wpcarro): Use safer interpolation variant of "?" here and throughout.
+    cur.execute("CREATE TABLE {} ({});".format(table, ",".join(columns)))
+    rows = [tuple(row[col] for col in columns) for row in r]
+    cur.executemany("INSERT INTO {} ({}) VALUES ({});".format(table, ",".join(columns), ",".join("?" for _ in columns)), rows)
+    con.commit()
+
+  while True:
+    x = input("> ")
+
+    if debug:
+      print("tokens:\t{}".format(tokenize(x)))
+      print("AST:\t{}".format(parse(x)))
+      print("query:\t\"{}\"".format(compile(x, table, columns)))
+
+    try:
+      compile(x, table, columns)
+      for row in cur.execute(compile(x, table, columns)):
+        print("\t".join(str(cell) for cell in row))
+    except:
+      print("Compilation error.")
+
+  # TODO(wpcarro): Trap exits and ensure cleanup always runs.
+  con.close()
+
+if __name__ == "__main__":
+  parser = ArgumentParser()
+  parser.add_argument("-f", "--file", dest="file", help="Path to the CSV from which to read", metavar="PATH")
+  parser.add_argument("-d", "--debug", dest="debug", default=False, action="store_true", help="Enable debugging")
+  args = parser.parse_args()
+  main(csv_path=args.file, debug=args.debug)
diff --git a/users/wpcarro/scratch/simple-select/parser.py b/users/wpcarro/scratch/simple-select/parser.py
new file mode 100644
index 0000000000..d26f970e57
--- /dev/null
+++ b/users/wpcarro/scratch/simple-select/parser.py
@@ -0,0 +1,31 @@
+class Parser(object):
+    def __init__(self, tokens):
+        self.tokens = tokens
+        self.i = 0
+
+    def exhausted(self):
+        return self.i >= len(self.tokens)
+
+    def peek(self, n=0):
+        return self.tokens[self.i + n]
+
+    def advance(self):
+        if not self.exhausted():
+            self.i += 1
+        return self.peek(n=-1)
+
+    def match(self, xs):
+        if self.peek() in xs:
+            self.advance()
+            return True
+        return False
+
+    def test(self, predicate):
+        return predicate(self.tokens, self.i)
+
+    def expect(self, predicate):
+        if self.exhausted():
+            raise Exception("Unexpected EOL")
+        if predicate(self.peek()):
+            return self.advance()
+        raise Exception("Unexpected token: \"{}\"".format(self.peek()))
diff --git a/users/wpcarro/scratch/simple-select/scanner.py b/users/wpcarro/scratch/simple-select/scanner.py
new file mode 100644
index 0000000000..5dae68aee5
--- /dev/null
+++ b/users/wpcarro/scratch/simple-select/scanner.py
@@ -0,0 +1,27 @@
+# According to Crafting Interpreters, the only two primitives that a
+# scanner/lexer needs are peek and advance; other functions (e.g. match) are
+# nice-to-haves.
+class Scanner(object):
+  def __init__(self, chars):
+    self.i = 0
+    self.chars = chars
+
+  def exhausted(self):
+    return self.i >= len(self.chars)
+
+  def peek(self, n=0):
+    return self.chars[self.i + n] if self.i in range(0, len(self.chars)) else '\0'
+
+  def advance(self):
+    result = self.peek()
+    self.i += 1
+    return result
+
+  def match(self, x):
+    if self.exhausted():
+      return False
+    if self.peek() == x:
+      self.advance()
+      return True
+    else:
+      return False
diff --git a/users/wpcarro/slx.js/.gitignore b/users/wpcarro/slx.js/.gitignore
new file mode 100644
index 0000000000..d60e5798c1
--- /dev/null
+++ b/users/wpcarro/slx.js/.gitignore
@@ -0,0 +1,3 @@
+/.parcel-cache
+/dist
+/node_modules
\ No newline at end of file
diff --git a/users/wpcarro/slx.js/README.md b/users/wpcarro/slx.js/README.md
new file mode 100644
index 0000000000..3fbebc4706
--- /dev/null
+++ b/users/wpcarro/slx.js/README.md
@@ -0,0 +1,55 @@
+# slx.js
+
+Filter tabular data in the browser using an ergonomic query language.
+
+## Status
+
+This project is usable today (I use it in my projects), but it's currently alpha
+status. See the wish list for remaining features.
+
+## Installation
+
+`slx.js` is available via CDN:
+
+```shell
+<script src="https://cdn.jsdelivr.net/gh/wpcarro/slx.js/index.js" async></script>
+```
+
+## Usage
+
+`slx.js` hasn't been properly benchmarked, but in my personal projects, it works
+fine with `O(1,000)s` of records.
+
+```javascript
+const cast = [
+  { first: "Graham", last: "Chapman" },
+  { first: "John", last: "Cleese" },
+  { first: "Terry", last: "Gilliam" },
+  { first: "Eric", last: "Idle" },
+  { first: "Terry", last: "Jones" },
+  { first: "Michael", last: "Palin" },
+];
+
+const config = {
+    // Match values case sensitively when filtering.
+    caseSensitive: false,
+    // Coerce values into regular expressions (instead of strings) when they're defined as atoms.
+    preferRegex: true,
+    // The key in the JS object that hosts the Date type against which we filter.
+    dateKey: 'Date',
+};
+
+console.log(select('last:^C.+$', cast, config));
+// [{ first: "Graham", last: "Chapman" }, { first: "John", last: "Cleese" }]
+```
+
+## Wish List
+
+- Support explicit grouping with parentheses (e.g. `title:once (director:Tarantino OR director:Coen)`).
+- Proper benchmarking (see "Usage" section).
+- Something something documentation.
+- Something something testing.
+
+## See also:
+
+- [`slx`](https://github.com/wpcarro/slx)
diff --git a/users/wpcarro/slx.js/default.nix b/users/wpcarro/slx.js/default.nix
new file mode 100644
index 0000000000..bf903e77aa
--- /dev/null
+++ b/users/wpcarro/slx.js/default.nix
@@ -0,0 +1,11 @@
+{ pkgs, depot, ... }:
+
+(pkgs.writeText "source.txt" ''
+  ${depot.third_party.gitignoreSource ./.}
+'').overrideAttrs (_: {
+  meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+    filter = ":/users/wpcarro/slx.js";
+    remote = "git@github.com:wpcarro/slx.js.git";
+    ref = "refs/heads/canon";
+  };
+})
diff --git a/users/wpcarro/slx.js/index.html b/users/wpcarro/slx.js/index.html
new file mode 100644
index 0000000000..966705a642
--- /dev/null
+++ b/users/wpcarro/slx.js/index.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>Tests</title>
+    <link rel="stylesheet" href="https://unpkg.com/terminal.css@0.7.2/dist/terminal.min.css" />
+  </head>
+  <body>
+    <div id="mount"></div>
+    <script src="./index.js"></script>
+    <script src="./tests.js" type="module"></script>
+  </body>
+</html>
diff --git a/users/wpcarro/slx.js/index.js b/users/wpcarro/slx.js/index.js
new file mode 100644
index 0000000000..3729978c75
--- /dev/null
+++ b/users/wpcarro/slx.js/index.js
@@ -0,0 +1,455 @@
+function select(query, xs, config) {
+    // naive optimizations
+    if (query === '' || xs === []) {
+        return xs;
+    }
+
+    const predicate = compile(parse(query, config), config);
+    return xs.filter(predicate);
+}
+
+function compile(ast, config) {
+    if (ast.type === 'CONJUNCTION') {
+        const lhs = compile(ast.lhs, compile);
+        const rhs = compile(ast.rhs, compile);
+
+        if (ast.joint === 'AND') {
+            return function(x) {
+                return lhs(x) && rhs(x);
+            };
+        }
+        if (ast.joint === 'OR') {
+            return function(x) {
+                return lhs(x) || rhs(x);
+            };
+        }
+    }
+    if (ast.type === 'DATE_SELECTION') {
+        if (ast.key === 'before') {
+            return function(row) {
+                let t = new Date();
+                if (ast.val === 'yesterday') {
+                    t.setDate(t.getDate() - 1);
+                    console.log(t);
+                }
+                // MM/DD/YYYY
+                else {
+                    t = new Date(ast.val);
+                }
+                return row[config.dateKey] < t;
+            };
+        }
+        if (ast.key === 'after') {
+            return function(row) {
+                let t = new Date();
+                if (ast.val === 'yesterday') {
+                    t.setDate(t.getDate() - 1);
+                    console.log(t);
+                }
+                // MM/DD/YYYY
+                else {
+                    t = new Date(ast.val);
+                }
+                return row[config.dateKey] > t;
+            };
+        }
+    }
+    if (ast.type === 'COMPARE_SELECTION') {
+        const f = compile(ast.val, config);
+
+        let compare = null;
+        if (ast.operator === 'EQ') { compare = (x, y) => x === y; }
+        if (ast.operator === 'LT') { compare = (x, y) => x < y; }
+        if (ast.operator === 'GT') { compare = (x, y) => x > y; }
+        if (ast.operator === 'LTE') { compare = (x, y) => x <= y; }
+        if (ast.operator === 'GTE') { compare = (x, y) => x >= y; }
+
+        return function(row) {
+            return ast.negate ? !compare(row[ast.key], ast.val) : compare(row[ast.key], ast.val);
+        };
+    }
+    if (ast.type === 'SELECTION') {
+        const f = compile(ast.val, config);
+        return function(row) {
+            return ast.negate ? !f(row[ast.key]) : f(row[ast.key]);
+        };
+    }
+    if (ast.type === 'MATCH_ALL') {
+        if (ast.matchType === 'STRING') {
+            return function(row) {
+                return Object.values(row).some(x => {
+                    if (config.caseSensitive) {
+                        return x === ast.val;
+                    } else {
+                        return x.toLowerCase() === ast.val.toLowerCase();
+                    }
+                })
+            };
+        }
+        if (ast.matchType === 'REGEX') {
+            return function(row) {
+                return Object.values(row).some(x => ast.val.test(x));
+            };
+        }
+    }
+    if (ast.type === 'GROUPING') {
+        return compile(ast.content);
+    }
+    if (ast.type === 'STRING') {
+        return function(x) {
+            if (config.caseSensitive) {
+                return x === ast.val;
+            } else {
+                return x.toLowerCase() === ast.val.toLowerCase();
+            }
+        };
+    }
+    if (ast.type === 'REGEX') {
+        return function(x) {
+            return ast.val.test(x);
+        };
+    }
+}
+
+// A "selection" without a "$column:" prefix should fuzzy-search all columns.
+//
+// conjunction -> selection ( ( "AND" | "OR" )? selection )* ;
+// selection   -> "-"? COLUMN ":" ( regex | string ) | regex ;
+// regex       -> [_-a-zA-Z0-9] | "/" [ _-a-zA-Z0-9] "/" | string ;
+// string      -> "\"" [ _-a-zA-Z0-9] "\"" ;
+
+// Whatever characters are valid for a JS regex.
+const ATOM_REGEX = /[-_.\[\]a-zA-Z0-9*+^$]/;
+
+function tokenize(x) {
+    const result = [];
+    let i = 0;
+    while (i < x.length) {
+        if (x[i] === ' ') {
+            i += 1;
+            while (i < x.length && x[i] === ' ') {
+                i += 1;
+            }
+            result.push(['WHITESPACE', null]);
+            continue;
+        }
+        if (x[i] === '-') {
+            result.push(['NEGATE', null]);
+            i += 1;
+            continue;
+        }
+        // Tokenize numbers (i.e. integers, floats).
+        if (/[0-9]/.test(x[i])) {
+            let curr = x[i];
+            i += 1;
+            while (i < x.length && /[0-9]/.test(x[i])) {
+                curr += x[i];
+                i += 1;
+            }
+            result.push(['NUMBER', parseFloat(curr)]);
+            continue;
+        }
+        if (ATOM_REGEX.test(x[i])) {
+            let curr = x[i];
+            i += 1;
+            while (i < x.length && ATOM_REGEX.test(x[i])) {
+                curr += x[i];
+                i += 1;
+            }
+            result.push(['ATOM', curr]);
+            continue;
+        }
+        if (x[i] === '=') {
+            result.push(['COMPARE', 'EQ']);
+            i += 1;
+            continue;
+        }
+        if (x[i] === '<' && i + 1 < x.length && x[i + 1] === '=') {
+            result.push(['COMPARE', 'LTE']);
+            i += 2;
+            continue;
+        }
+        if (x[i] === '<') {
+            result.push(['COMPARE', 'LT']);
+            i += 1;
+            continue;
+        }
+        if (x[i] === '>' && i + i < x.length && x[i + 1] === '=') {
+            result.push(['COMPARE', 'GTE']);
+            i += 2;
+            continue;
+        }
+        if (x[i] === '>') {
+            result.push(['COMPARE', 'GT']);
+            i += 1;
+            continue;
+        }
+        if (x[i] === ':') {
+            result.push(['COLON', null]);
+            i += 1;
+            continue;
+        }
+        if (x[i] === '(') {
+            result.push(['LPAREN', null]);
+            i += 1;
+            continue;
+        }
+        if (x[i] === ')') {
+            result.push(['RPAREN', null]);
+            i += 1;
+            continue;
+        }
+        if (x[i] === '/') {
+            let start = i;
+            let curr = '';
+            i += 1;
+            while (i < x.length && x[i] !== '/') {
+                curr += x[i];
+                i += 1;
+            }
+            // error
+            if (i >= x.length) {
+                throw `Tokenize Error: EOL while attempting to tokenize the regex beginning at column: ${start}`;
+            }
+            if (x[i] === '/') {
+                result.push(['REGEX', curr]);
+                i += 1;
+            }
+            continue;
+        }
+        if (x[i] === '"') {
+            let start = i;
+            let curr = '';
+            i += 1;
+            while (i < x.length && x[i] !== '"') {
+                // continue on \"
+                if (x[i] === '\\' && x[i + 1] === '"') {
+                    curr += '\"';
+                    i += 2;
+                } else {
+                    curr += x[i];
+                    i += 1;
+                }
+            }
+            if (i >= x.length) {
+                throw `Tokenize Error: EOL while attempting to tokenize the string starting at column: ${start}`;
+            }
+            if (x[i] === '"') {
+                result.push(['STRING', curr]);
+                i += 1;
+            }
+            continue;
+        }
+        else {
+            i += 1;
+        }
+    }
+    return result;
+}
+
+function expect(f, expectation, p) {
+    const [type, val] = p.tokens[p.i];
+    if (f(type, val)) {
+        p.i += 1;
+    } else {
+        throw `Parse Error: expected ${expectation}, but got ${p.tokens[p.i]}; ${JSON.stringify(p)}`
+    }
+}
+
+function matches(f, p) {
+    const [type, val] = p.tokens[p.i];
+    if (f(type, val)) {
+        return true;
+    }
+    return false;
+}
+
+function match(f, expectation, p) {
+    const [type, val] = p.tokens[p.i];
+    if (f(type, val)) {
+        p.i += 1;
+        return val;
+    }
+    throw `Parse Error: expected ${expectation}, but got: ${p.tokens[p.i]}; ${JSON.stringify(p)}`;
+}
+
+function skipWhitespace(p) {
+    while (p.i < p.tokens.length && matches((type, _) => type === 'WHITESPACE', p)) {
+        p.i += 1;
+    }
+}
+
+function peekType(n, p) {
+    if (p.i + n < p.tokens.length) {
+        return p.tokens[p.i + n][0];
+    }
+    return null;
+}
+
+function parser(tokens) {
+    return { i: 0, tokens };
+}
+
+function parse(x, config) {
+    const tokens = tokenize(x);
+    const p = parser(tokens);
+    return conjunction(p, config);
+}
+
+function conjunction(p, config) {
+    skipWhitespace(p);
+
+    const lhs = selection(p, config);
+    skipWhitespace(p);
+
+    // TODO(wpcarro): Consider re-architecting the parser to avoid smells like
+    // this.
+    if (peekType(0, p) === 'RPAREN') {
+        return lhs;
+    }
+
+    if (p.i >= p.tokens.length) {
+        return lhs;
+    }
+
+    let joint = 'AND';
+    if (matches((type, val) => type === 'ATOM' && val === 'AND', p)) {
+        joint = 'AND';
+        p.i += 1;
+    } else if (matches((type, val) => type === 'ATOM' && val === 'OR', p)) {
+        joint = 'OR';
+        p.i += 1;
+    }
+    skipWhitespace(p);
+    const rhs = conjunction(p, config);
+
+    return {
+        type: 'CONJUNCTION',
+        joint,
+        lhs,
+        rhs,
+    };
+}
+
+function selection(p, config) {
+    // column:value OR -column:value
+    if ((peekType(0, p) === 'ATOM' && peekType(1, p) === 'COLON') ||
+        (peekType(0, p) === 'NEGATE' && peekType(1, p) === 'ATOM' && peekType(2, p) === 'COLON')) {
+
+        let negate = false;
+        if (p.tokens[p.i][0] === 'NEGATE') {
+            negate = true;
+            p.i += 1;
+        }
+
+        const key = match((type, _) => type === 'ATOM', 'a column label', p);
+        expect((type, val) => type === 'COLON', 'a colon', p);
+
+        if (key === 'before' || key === 'after') {
+            const val = date(p);
+            return {
+                type: 'DATE_SELECTION',
+                key,
+                val,
+            };
+        } else {
+            const val = value(p, config);
+            return {
+                type: 'SELECTION',
+                negate,
+                key,
+                val,
+            };
+        }
+    }
+    // column<value OR -column<value
+    else if ((peekType(0, p) === 'ATOM' && peekType(1, p) === 'COMPARE') ||
+             (peekType(0, p) === 'NEGATE' && peekType(1, p) === 'ATOM' && peekType(2, p) === 'COMPARE')) {
+        let negate = false;
+        if (p.tokens[p.i][0] === 'NEGATE') {
+            negate = true;
+            p.i += 1;
+        }
+
+        const key = match((type, _) => type === 'ATOM', 'a column label', p);
+        const operator = match((type, _) => type === 'COMPARE', 'a comparison operator (i.e. "<", ">", "<=", ">=")', p);
+        const val = match((type, _) => type === 'NUMBER', 'a number', p);
+
+        return {
+            type: 'COMPARE_SELECTION',
+            operator,
+            negate,
+            key,
+            val,
+        };
+    }
+    else {
+        return matchAll(p, config);
+    }
+}
+
+function matchAll(p, config) {
+    const [type, val] = p.tokens[p.i];
+
+    // Cast atoms into strings or regexes depending on the current config.
+    if (type === 'ATOM') {
+        p.i += 1;
+        if (config.preferRegex) {
+            const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i");
+            return { type: 'MATCH_ALL', matchType: 'REGEX', val: regex };
+        } else {
+            return { type: 'MATCH_ALL', matchType: 'STRING', val }
+        }
+    }
+    if (type === 'STRING') {
+        p.i += 1;
+        return { type: 'MATCH_ALL', matchType: 'STRING', val };
+    }
+    if (type === 'REGEX') {
+        p.i += 1;
+        const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i");
+        return { type: 'MATCH_ALL', matchType: 'REGEX', val: regex };
+    }
+    if (type === 'LPAREN') {
+        p.i += 1;
+        const content = conjunction(p, config);
+        expect((type, _) => type === 'RPAREN', 'a closing parenthesis', p);
+        return {
+            type: 'GROUPING',
+            content,
+        };
+    }
+    throw `Parse Error: Expected a regular expression or a string, but got: ${p.tokens[p.i]}; ${JSON.stringify(p)}`;
+}
+
+function value(p, config) {
+    const [type, val] = p.tokens[p.i];
+
+    // Cast atoms into strings or regexes depending on the current config.
+    if (type === 'ATOM') {
+        p.i += 1;
+        if (config.preferRegex) {
+            const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i");
+            return { type: 'REGEX', val: regex };
+        } else {
+            return { type: 'STRING', val }
+        }
+    }
+    if (type === 'STRING') {
+        p.i += 1;
+        return { type, val };
+    }
+    if (type === 'REGEX') {
+        p.i += 1;
+        const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i");
+        return { type, val: regex };
+    }
+    throw `Parse Error: Expected a regular expression or a string, but got: ${p.tokens[p.i]}; ${JSON.stringify(p)}`;
+}
+
+function date(p) {
+    const [type, val] = p.tokens[p.i];
+    p.i += 1;
+
+    return val;
+}
diff --git a/users/wpcarro/slx.js/package.json b/users/wpcarro/slx.js/package.json
new file mode 100644
index 0000000000..d8f2e678fa
--- /dev/null
+++ b/users/wpcarro/slx.js/package.json
@@ -0,0 +1,14 @@
+{
+  "name": "slx.js",
+  "version": "1.0.0",
+  "main": "index.js",
+  "license": "MIT",
+  "dependencies": {
+    "parcel": "^2.8.3",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"
+  },
+  "devDependencies": {
+    "process": "^0.11.10"
+  }
+}
diff --git a/users/wpcarro/slx.js/tests.js b/users/wpcarro/slx.js/tests.js
new file mode 100644
index 0000000000..9ed68a588c
--- /dev/null
+++ b/users/wpcarro/slx.js/tests.js
@@ -0,0 +1,68 @@
+import { createRoot } from "react-dom/client";
+import React from "react";
+
+
+const john = { first: 'John', last: 'Cleese', age: 83, birthday: new Date("10/27/1939") };
+const graham = { first: 'Graham', last: 'Chapman', age: 48, birthday: new Date("01/08/1941") };
+
+const xs = [
+    john,
+    graham,
+];
+const cfg = {
+    caseSensitive: false,
+    preferRegex: true,
+    dateKey: 'birthday',
+};
+const tests = [
+    ['support EQ', 'age=83', xs, cfg, [john]],
+    ['supports LT', 'age<83', xs, cfg, [graham]],
+    ['supports LTE', 'age<=83', xs, cfg, [john, graham]],
+    ['supports GT', 'age>48', xs, cfg, [john]],
+    ['supports GTE', 'age>=48', xs, cfg, [john, graham]],
+    ['supports grouping (1)', 'last:/^C/ (age=83 OR age=48)', xs, cfg, [john, graham]],
+    ['supports grouping (2)', '(age=83)', xs, cfg, [john]],
+    ['supports grouping (3)', '(age=83 OR age=48)', xs, cfg, [john, graham]],
+];
+
+function equal(xs, ys) {
+    return xs.length === ys.length && xs.every((x, i) => x === ys[i]);
+}
+
+class App extends React.Component {
+    constructor(props) {
+        super(props);
+    }
+    render() {
+        return (
+            <table>
+              <thead>
+                <th>pass/fail</th>
+                <th>Label</th>
+                <th>code</th>
+                <th>actual</th>
+                <th>expected</th>
+              </thead>
+              <tbody>
+                {this.props.tests.map(test => {
+                    const [label, query, xs, cfg, expected] = test;
+                    const actual = select(query, xs, cfg);
+                    return (
+                        <tr style={{backgroundColor: equal(actual, expected) ? null : 'red'}}>
+                          <td>{equal(actual, expected) ? "pass" : "fail"}</td>
+                          <td>{label}</td>
+                          <td>select("{query}", {JSON.stringify(xs)}, {JSON.stringify(cfg)})</td>
+                          <td>{JSON.stringify(actual)}</td>
+                          <td>{JSON.stringify(expected)}</td>
+                        </tr>
+                    );
+                })}
+              </tbody>
+            </table>
+        );
+    }
+}
+
+const container = document.getElementById("mount");
+const root = createRoot(container);
+root.render(<App tests={tests} />);
diff --git a/users/wpcarro/slx.js/yarn.lock b/users/wpcarro/slx.js/yarn.lock
new file mode 100644
index 0000000000..4d0ec7633f
--- /dev/null
+++ b/users/wpcarro/slx.js/yarn.lock
@@ -0,0 +1,1495 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
+  integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
+  dependencies:
+    "@babel/highlight" "^7.18.6"
+
+"@babel/helper-validator-identifier@^7.18.6":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
+  integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
+
+"@babel/highlight@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+  integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.18.6"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
+"@jridgewell/gen-mapping@^0.3.0":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
+  integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/resolve-uri@3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+  integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/set-array@^1.0.1":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+  integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
+"@jridgewell/source-map@^0.3.2":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+  integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
+  version "1.4.14"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+  integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@^0.3.9":
+  version "0.3.17"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
+  integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
+  dependencies:
+    "@jridgewell/resolve-uri" "3.1.0"
+    "@jridgewell/sourcemap-codec" "1.4.14"
+
+"@lezer/common@^0.15.0", "@lezer/common@^0.15.7":
+  version "0.15.12"
+  resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.12.tgz#2f21aec551dd5fd7d24eb069f90f54d5bc6ee5e9"
+  integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==
+
+"@lezer/lr@^0.15.4":
+  version "0.15.8"
+  resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.8.tgz#1564a911e62b0a0f75ca63794a6aa8c5dc63db21"
+  integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==
+  dependencies:
+    "@lezer/common" "^0.15.0"
+
+"@lmdb/lmdb-darwin-arm64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz#bc66fa43286b5c082e8fee0eacc17995806b6fbe"
+  integrity sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A==
+
+"@lmdb/lmdb-darwin-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz#89d8390041bce6bab24a82a20392be22faf54ffc"
+  integrity sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA==
+
+"@lmdb/lmdb-linux-arm64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz#14fe4c96c2bb1285f93797f45915fa35ee047268"
+  integrity sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ==
+
+"@lmdb/lmdb-linux-arm@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz#05bde4573ab10cf21827339fe687148f2590cfa1"
+  integrity sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw==
+
+"@lmdb/lmdb-linux-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz#d2f85afd857d2c33d2caa5b057944574edafcfee"
+  integrity sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q==
+
+"@lmdb/lmdb-win32-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz#28f643fbc0bec30b07fbe95b137879b6b4d1c9c5"
+  integrity sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA==
+
+"@mischnic/json-sourcemap@^0.1.0":
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz#38af657be4108140a548638267d02a2ea3336507"
+  integrity sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==
+  dependencies:
+    "@lezer/common" "^0.15.7"
+    "@lezer/lr" "^0.15.4"
+    json5 "^2.2.1"
+
+"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.2.0.tgz#901c5937e1441572ea23e631fe6deca68482fe76"
+  integrity sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==
+
+"@msgpackr-extract/msgpackr-extract-darwin-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.2.0.tgz#fb877fe6bae3c4d3cea29786737840e2ae689066"
+  integrity sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.2.0.tgz#986179c38b10ac41fbdaf7d036c825cbc72855d9"
+  integrity sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.2.0.tgz#15f2c6fe9e0adc06c21af7e95f484ff4880d79ce"
+  integrity sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg==
+
+"@msgpackr-extract/msgpackr-extract-linux-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.2.0.tgz#30cae5c9a202f3e1fa1deb3191b18ffcb2f239a2"
+  integrity sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw==
+
+"@msgpackr-extract/msgpackr-extract-win32-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.2.0.tgz#016d855b6bc459fd908095811f6826e45dd4ba64"
+  integrity sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA==
+
+"@parcel/bundler-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/bundler-default/-/bundler-default-2.8.3.tgz#d64739dbc2dbd59d6629861bf77a8083aced5229"
+  integrity sha512-yJvRsNWWu5fVydsWk3O2L4yIy3UZiKWO2cPDukGOIWMgp/Vbpp+2Ct5IygVRtE22bnseW/E/oe0PV3d2IkEJGg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/graph" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/cache@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/cache/-/cache-2.8.3.tgz#169e130cf59913c0ed9fadce1a450e68f710e16f"
+  integrity sha512-k7xv5vSQrJLdXuglo+Hv3yF4BCSs1tQ/8Vbd6CHTkOhf7LcGg6CPtLw053R/KdMpd/4GPn0QrAsOLdATm1ELtQ==
+  dependencies:
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    lmdb "2.5.2"
+
+"@parcel/codeframe@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.8.3.tgz#84fb529ef70def7f5bc64f6c59b18d24826f5fcc"
+  integrity sha512-FE7sY53D6n/+2Pgg6M9iuEC6F5fvmyBkRE4d9VdnOoxhTXtkEqpqYgX7RJ12FAQwNlxKq4suBJQMgQHMF2Kjeg==
+  dependencies:
+    chalk "^4.1.0"
+
+"@parcel/compressor-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/compressor-raw/-/compressor-raw-2.8.3.tgz#301753df8c6de967553149639e8a4179b88f0c95"
+  integrity sha512-bVDsqleBUxRdKMakWSlWC9ZjOcqDKE60BE+Gh3JSN6WJrycJ02P5wxjTVF4CStNP/G7X17U+nkENxSlMG77ySg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/config-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/config-default/-/config-default-2.8.3.tgz#9a43486e7c702e96c68052c37b79098d7240e35b"
+  integrity sha512-o/A/mbrO6X/BfGS65Sib8d6SSG45NYrNooNBkH/o7zbOBSRQxwyTlysleK1/3Wa35YpvFyLOwgfakqCtbGy4fw==
+  dependencies:
+    "@parcel/bundler-default" "2.8.3"
+    "@parcel/compressor-raw" "2.8.3"
+    "@parcel/namer-default" "2.8.3"
+    "@parcel/optimizer-css" "2.8.3"
+    "@parcel/optimizer-htmlnano" "2.8.3"
+    "@parcel/optimizer-image" "2.8.3"
+    "@parcel/optimizer-svgo" "2.8.3"
+    "@parcel/optimizer-terser" "2.8.3"
+    "@parcel/packager-css" "2.8.3"
+    "@parcel/packager-html" "2.8.3"
+    "@parcel/packager-js" "2.8.3"
+    "@parcel/packager-raw" "2.8.3"
+    "@parcel/packager-svg" "2.8.3"
+    "@parcel/reporter-dev-server" "2.8.3"
+    "@parcel/resolver-default" "2.8.3"
+    "@parcel/runtime-browser-hmr" "2.8.3"
+    "@parcel/runtime-js" "2.8.3"
+    "@parcel/runtime-react-refresh" "2.8.3"
+    "@parcel/runtime-service-worker" "2.8.3"
+    "@parcel/transformer-babel" "2.8.3"
+    "@parcel/transformer-css" "2.8.3"
+    "@parcel/transformer-html" "2.8.3"
+    "@parcel/transformer-image" "2.8.3"
+    "@parcel/transformer-js" "2.8.3"
+    "@parcel/transformer-json" "2.8.3"
+    "@parcel/transformer-postcss" "2.8.3"
+    "@parcel/transformer-posthtml" "2.8.3"
+    "@parcel/transformer-raw" "2.8.3"
+    "@parcel/transformer-react-refresh-wrap" "2.8.3"
+    "@parcel/transformer-svg" "2.8.3"
+
+"@parcel/core@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/core/-/core-2.8.3.tgz#22a69f36095d53736ab10bf42697d9aa5f4e382b"
+  integrity sha512-Euf/un4ZAiClnlUXqPB9phQlKbveU+2CotZv7m7i+qkgvFn5nAGnrV4h1OzQU42j9dpgOxWi7AttUDMrvkbhCQ==
+  dependencies:
+    "@mischnic/json-sourcemap" "^0.1.0"
+    "@parcel/cache" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/graph" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    abortcontroller-polyfill "^1.1.9"
+    base-x "^3.0.8"
+    browserslist "^4.6.6"
+    clone "^2.1.1"
+    dotenv "^7.0.0"
+    dotenv-expand "^5.1.0"
+    json5 "^2.2.0"
+    msgpackr "^1.5.4"
+    nullthrows "^1.1.1"
+    semver "^5.7.1"
+
+"@parcel/diagnostic@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/diagnostic/-/diagnostic-2.8.3.tgz#d560276d5d2804b48beafa1feaf3fc6b2ac5e39d"
+  integrity sha512-u7wSzuMhLGWZjVNYJZq/SOViS3uFG0xwIcqXw12w54Uozd6BH8JlhVtVyAsq9kqnn7YFkw6pXHqAo5Tzh4FqsQ==
+  dependencies:
+    "@mischnic/json-sourcemap" "^0.1.0"
+    nullthrows "^1.1.1"
+
+"@parcel/events@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.8.3.tgz#205f8d874e6ecc2cbdb941bf8d54bae669e571af"
+  integrity sha512-hoIS4tAxWp8FJk3628bsgKxEvR7bq2scCVYHSqZ4fTi/s0+VymEATrRCUqf+12e5H47uw1/ZjoqrGtBI02pz4w==
+
+"@parcel/fs-search@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.8.3.tgz#1c7d812c110b808758f44c56e61dfffdb09e9451"
+  integrity sha512-DJBT2N8knfN7Na6PP2mett3spQLTqxFrvl0gv+TJRp61T8Ljc4VuUTb0hqBj+belaASIp3Q+e8+SgaFQu7wLiQ==
+  dependencies:
+    detect-libc "^1.0.3"
+
+"@parcel/fs@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.8.3.tgz#80536afe877fc8a2bd26be5576b9ba27bb4c5754"
+  integrity sha512-y+i+oXbT7lP0e0pJZi/YSm1vg0LDsbycFuHZIL80pNwdEppUAtibfJZCp606B7HOjMAlNZOBo48e3hPG3d8jgQ==
+  dependencies:
+    "@parcel/fs-search" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/watcher" "^2.0.7"
+    "@parcel/workers" "2.8.3"
+
+"@parcel/graph@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/graph/-/graph-2.8.3.tgz#00ffe8ec032e74fee57199e54529f1da7322571d"
+  integrity sha512-26GL8fYZPdsRhSXCZ0ZWliloK6DHlMJPWh6Z+3VVZ5mnDSbYg/rRKWmrkhnr99ZWmL9rJsv4G74ZwvDEXTMPBg==
+  dependencies:
+    nullthrows "^1.1.1"
+
+"@parcel/hash@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/hash/-/hash-2.8.3.tgz#bc2499a27395169616cad2a99e19e69b9098f6e9"
+  integrity sha512-FVItqzjWmnyP4ZsVgX+G00+6U2IzOvqDtdwQIWisCcVoXJFCqZJDy6oa2qDDFz96xCCCynjRjPdQx2jYBCpfYw==
+  dependencies:
+    detect-libc "^1.0.3"
+    xxhash-wasm "^0.4.2"
+
+"@parcel/logger@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.8.3.tgz#e14e4debafb3ca9e87c07c06780f9afc38b2712c"
+  integrity sha512-Kpxd3O/Vs7nYJIzkdmB6Bvp3l/85ydIxaZaPfGSGTYOfaffSOTkhcW9l6WemsxUrlts4za6CaEWcc4DOvaMOPA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+
+"@parcel/markdown-ansi@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.8.3.tgz#1337d421bb1133ad178f386a8e1b746631bba4a1"
+  integrity sha512-4v+pjyoh9f5zuU/gJlNvNFGEAb6J90sOBwpKJYJhdWXLZMNFCVzSigxrYO+vCsi8G4rl6/B2c0LcwIMjGPHmFQ==
+  dependencies:
+    chalk "^4.1.0"
+
+"@parcel/namer-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/namer-default/-/namer-default-2.8.3.tgz#5304bee74beb4b9c1880781bdbe35be0656372f4"
+  integrity sha512-tJ7JehZviS5QwnxbARd8Uh63rkikZdZs1QOyivUhEvhN+DddSAVEdQLHGPzkl3YRk0tjFhbqo+Jci7TpezuAMw==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/node-resolver-core@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/node-resolver-core/-/node-resolver-core-2.8.3.tgz#581df074a27646400b3fed9da95297b616a7db8f"
+  integrity sha512-12YryWcA5Iw2WNoEVr/t2HDjYR1iEzbjEcxfh1vaVDdZ020PiGw67g5hyIE/tsnG7SRJ0xdRx1fQ2hDgED+0Ww==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    semver "^5.7.1"
+
+"@parcel/optimizer-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-css/-/optimizer-css-2.8.3.tgz#420a333f4b78f7ff15e69217dfed34421b1143ee"
+  integrity sha512-JotGAWo8JhuXsQDK0UkzeQB0UR5hDAKvAviXrjqB4KM9wZNLhLleeEAW4Hk8R9smCeQFP6Xg/N/NkLDpqMwT3g==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    lightningcss "^1.16.1"
+    nullthrows "^1.1.1"
+
+"@parcel/optimizer-htmlnano@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.8.3.tgz#a71ab6f0f24160ef9f573266064438eff65e96d0"
+  integrity sha512-L8/fHbEy8Id2a2E0fwR5eKGlv9VYDjrH9PwdJE9Za9v1O/vEsfl/0T/79/x129l5O0yB6EFQkFa20MiK3b+vOg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    htmlnano "^2.0.0"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    svgo "^2.4.0"
+
+"@parcel/optimizer-image@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-image/-/optimizer-image-2.8.3.tgz#ea49b4245b4f7d60b38c7585c6311fb21d341baa"
+  integrity sha512-SD71sSH27SkCDNUNx9A3jizqB/WIJr3dsfp+JZGZC42tpD/Siim6Rqy9M4To/BpMMQIIiEXa5ofwS+DgTEiEHQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    detect-libc "^1.0.3"
+
+"@parcel/optimizer-svgo@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-svgo/-/optimizer-svgo-2.8.3.tgz#04da4efec6b623679539a84961bff6998034ba8a"
+  integrity sha512-9KQed99NZnQw3/W4qBYVQ7212rzA9EqrQG019TIWJzkA9tjGBMIm2c/nXpK1tc3hQ3e7KkXkFCQ3C+ibVUnHNA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    svgo "^2.4.0"
+
+"@parcel/optimizer-terser@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-terser/-/optimizer-terser-2.8.3.tgz#3a06d98d09386a1a0ae1be85376a8739bfba9618"
+  integrity sha512-9EeQlN6zIeUWwzrzu6Q2pQSaYsYGah8MtiQ/hog9KEPlYTP60hBv/+utDyYEHSQhL7y5ym08tPX5GzBvwAD/dA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    terser "^5.2.0"
+
+"@parcel/package-manager@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/package-manager/-/package-manager-2.8.3.tgz#ddd0d62feae3cf0fb6cc0537791b3a16296ad458"
+  integrity sha512-tIpY5pD2lH53p9hpi++GsODy6V3khSTX4pLEGuMpeSYbHthnOViobqIlFLsjni+QA1pfc8NNNIQwSNdGjYflVA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    semver "^5.7.1"
+
+"@parcel/packager-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-css/-/packager-css-2.8.3.tgz#0eff34268cb4f5dfb53c1bbca85f5567aeb1835a"
+  integrity sha512-WyvkMmsurlHG8d8oUVm7S+D+cC/T3qGeqogb7sTI52gB6uiywU7lRCizLNqGFyFGIxcVTVHWnSHqItBcLN76lA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/packager-html@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-html/-/packager-html-2.8.3.tgz#f9263b891aa4dd46c6e2fa2b07025a482132fff1"
+  integrity sha512-OhPu1Hx1RRKJodpiu86ZqL8el2Aa4uhBHF6RAL1Pcrh2EhRRlPf70Sk0tC22zUpYL7es+iNKZ/n0Rl+OWSHWEw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+
+"@parcel/packager-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-js/-/packager-js-2.8.3.tgz#3ed11565915d73d12192b6901c75a6b820e4a83a"
+  integrity sha512-0pGKC3Ax5vFuxuZCRB+nBucRfFRz4ioie19BbDxYnvBxrd4M3FIu45njf6zbBYsI9eXqaDnL1b3DcZJfYqtIzw==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    globals "^13.2.0"
+    nullthrows "^1.1.1"
+
+"@parcel/packager-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-raw/-/packager-raw-2.8.3.tgz#bdec826df991e186cb58691cc45d12ad5c06676e"
+  integrity sha512-BA6enNQo1RCnco9MhkxGrjOk59O71IZ9DPKu3lCtqqYEVd823tXff2clDKHK25i6cChmeHu6oB1Rb73hlPqhUA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/packager-svg@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-svg/-/packager-svg-2.8.3.tgz#7233315296001c531cb55ca96b5f2ef672343630"
+  integrity sha512-mvIoHpmv5yzl36OjrklTDFShLUfPFTwrmp1eIwiszGdEBuQaX7JVI3Oo2jbVQgcN4W7J6SENzGQ3Q5hPTW3pMw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    posthtml "^0.16.4"
+
+"@parcel/plugin@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/plugin/-/plugin-2.8.3.tgz#7bb30a5775eaa6473c27f002a0a3ee7308d6d669"
+  integrity sha512-jZ6mnsS4D9X9GaNnvrixDQwlUQJCohDX2hGyM0U0bY2NWU8Km97SjtoCpWjq+XBCx/gpC4g58+fk9VQeZq2vlw==
+  dependencies:
+    "@parcel/types" "2.8.3"
+
+"@parcel/reporter-cli@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/reporter-cli/-/reporter-cli-2.8.3.tgz#12a4743b51b8fe6837f53c20e01bbf1f7336e8e4"
+  integrity sha512-3sJkS6tFFzgIOz3u3IpD/RsmRxvOKKiQHOTkiiqRt1l44mMDGKS7zANRnJYsQzdCsgwc9SOP30XFgJwtoVlMbw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chalk "^4.1.0"
+    term-size "^2.2.1"
+
+"@parcel/reporter-dev-server@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/reporter-dev-server/-/reporter-dev-server-2.8.3.tgz#a0daa5cc015642684cea561f4e0e7116bbffdc1c"
+  integrity sha512-Y8C8hzgzTd13IoWTj+COYXEyCkXfmVJs3//GDBsH22pbtSFMuzAZd+8J9qsCo0EWpiDow7V9f1LischvEh3FbQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/resolver-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/resolver-default/-/resolver-default-2.8.3.tgz#5ae41e537ae4a793c1abb47f094482b9e2ac3535"
+  integrity sha512-k0B5M/PJ+3rFbNj4xZSBr6d6HVIe6DH/P3dClLcgBYSXAvElNDfXgtIimbjCyItFkW9/BfcgOVKEEIZOeySH/A==
+  dependencies:
+    "@parcel/node-resolver-core" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/runtime-browser-hmr@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.8.3.tgz#1fa74e1fbd1030b0a920c58afa3a9eb7dc4bcd1e"
+  integrity sha512-2O1PYi2j/Q0lTyGNV3JdBYwg4rKo6TEVFlYGdd5wCYU9ZIN9RRuoCnWWH2qCPj3pjIVtBeppYxzfVjPEHINWVg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/runtime-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-js/-/runtime-js-2.8.3.tgz#0baa4c8fbf77eabce05d01ccc186614968ffc0cd"
+  integrity sha512-IRja0vNKwvMtPgIqkBQh0QtRn0XcxNC8HU1jrgWGRckzu10qJWO+5ULgtOeR4pv9krffmMPqywGXw6l/gvJKYQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/runtime-react-refresh@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.8.3.tgz#381a942fb81e8f5ac6c7e0ee1b91dbf34763c3f8"
+  integrity sha512-2v/qFKp00MfG0234OdOgQNAo6TLENpFYZMbVbAsPMY9ITiqG73MrEsrGXVoGbYiGTMB/Toer/lSWlJxtacOCuA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    react-error-overlay "6.0.9"
+    react-refresh "^0.9.0"
+
+"@parcel/runtime-service-worker@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-service-worker/-/runtime-service-worker-2.8.3.tgz#54d92da9ff1dfbd27db0e84164a22fa59e99b348"
+  integrity sha512-/Skkw+EeRiwzOJso5fQtK8c9b452uWLNhQH1ISTodbmlcyB4YalAiSsyHCtMYD0c3/t5Sx4ZS7vxBAtQd0RvOw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/source-map@^2.1.1":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@parcel/source-map/-/source-map-2.1.1.tgz#fb193b82dba6dd62cc7a76b326f57bb35000a782"
+  integrity sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==
+  dependencies:
+    detect-libc "^1.0.3"
+
+"@parcel/transformer-babel@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-babel/-/transformer-babel-2.8.3.tgz#286bc6cb9afe4c0259f0b28e0f2f47322a24b130"
+  integrity sha512-L6lExfpvvC7T/g3pxf3CIJRouQl+sgrSzuWQ0fD4PemUDHvHchSP4SNUVnd6gOytF3Y1KpnEZIunQGi5xVqQCQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    json5 "^2.2.0"
+    nullthrows "^1.1.1"
+    semver "^5.7.0"
+
+"@parcel/transformer-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-css/-/transformer-css-2.8.3.tgz#d6c44100204e73841ad8e0f90472172ea8b9120c"
+  integrity sha512-xTqFwlSXtnaYen9ivAgz+xPW7yRl/u4QxtnDyDpz5dr8gSeOpQYRcjkd4RsYzKsWzZcGtB5EofEk8ayUbWKEUg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    lightningcss "^1.16.1"
+    nullthrows "^1.1.1"
+
+"@parcel/transformer-html@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-html/-/transformer-html-2.8.3.tgz#5c68b28ee6b8c7a13b8aee87f7957ad3227bd83f"
+  integrity sha512-kIZO3qsMYTbSnSpl9cnZog+SwL517ffWH54JeB410OSAYF1ouf4n5v9qBnALZbuCCmPwJRGs4jUtE452hxwN4g==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+    srcset "4"
+
+"@parcel/transformer-image@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-image/-/transformer-image-2.8.3.tgz#73805b2bfc3c8919d7737544e5f8be39e3f303fe"
+  integrity sha512-cO4uptcCGTi5H6bvTrAWEFUsTNhA4kCo8BSvRSCHA2sf/4C5tGQPHt3JhdO0GQLPwZRCh/R41EkJs5HZ8A8DAg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/transformer-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-js/-/transformer-js-2.8.3.tgz#fe400df428394d1e7fe5afb6dea5c7c858e44f03"
+  integrity sha512-9Qd6bib+sWRcpovvzvxwy/PdFrLUXGfmSW9XcVVG8pvgXsZPFaNjnNT8stzGQj1pQiougCoxMY4aTM5p1lGHEQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    "@swc/helpers" "^0.4.12"
+    browserslist "^4.6.6"
+    detect-libc "^1.0.3"
+    nullthrows "^1.1.1"
+    regenerator-runtime "^0.13.7"
+    semver "^5.7.1"
+
+"@parcel/transformer-json@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-json/-/transformer-json-2.8.3.tgz#25deb3a5138cc70a83269fc5d39d564609354d36"
+  integrity sha512-B7LmVq5Q7bZO4ERb6NHtRuUKWGysEeaj9H4zelnyBv+wLgpo4f5FCxSE1/rTNmP9u1qHvQ3scGdK6EdSSokGPg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    json5 "^2.2.0"
+
+"@parcel/transformer-postcss@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-postcss/-/transformer-postcss-2.8.3.tgz#df4fdc1c90893823445f2a8eb8e2bdd0349ccc58"
+  integrity sha512-e8luB/poIlz6jBsD1Izms+6ElbyzuoFVa4lFVLZnTAChI3UxPdt9p/uTsIO46HyBps/Bk8ocvt3J4YF84jzmvg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    clone "^2.1.1"
+    nullthrows "^1.1.1"
+    postcss-value-parser "^4.2.0"
+    semver "^5.7.1"
+
+"@parcel/transformer-posthtml@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-posthtml/-/transformer-posthtml-2.8.3.tgz#7c3912a5a631cb26485f6464e0d6eeabb6f1e718"
+  integrity sha512-pkzf9Smyeaw4uaRLsT41RGrPLT5Aip8ZPcntawAfIo+KivBQUV0erY1IvHYjyfFzq1ld/Fo2Ith9He6mxpPifA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+
+"@parcel/transformer-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-raw/-/transformer-raw-2.8.3.tgz#3a22213fe18a5f83fd78889cb49f06e059cfead7"
+  integrity sha512-G+5cXnd2/1O3nV/pgRxVKZY/HcGSseuhAe71gQdSQftb8uJEURyUHoQ9Eh0JUD3MgWh9V+nIKoyFEZdf9T0sUQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/transformer-react-refresh-wrap@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.8.3.tgz#8b0392638405dd470a886002229f7889d5464822"
+  integrity sha512-q8AAoEvBnCf/nPvgOwFwKZfEl/thwq7c2duxXkhl+tTLDRN2vGmyz4355IxCkavSX+pLWSQ5MexklSEeMkgthg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    react-refresh "^0.9.0"
+
+"@parcel/transformer-svg@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-svg/-/transformer-svg-2.8.3.tgz#4df959cba4ebf45d7aaddd540f752e6e84df38b2"
+  integrity sha512-3Zr/gBzxi1ZH1fftH/+KsZU7w5GqkmxlB0ZM8ovS5E/Pl1lq1t0xvGJue9m2VuQqP8Mxfpl5qLFmsKlhaZdMIQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+
+"@parcel/types@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/types/-/types-2.8.3.tgz#3306bc5391b6913bd619914894b8cd84a24b30fa"
+  integrity sha512-FECA1FB7+0UpITKU0D6TgGBpGxYpVSMNEENZbSJxFSajNy3wrko+zwBKQmFOLOiPcEtnGikxNs+jkFWbPlUAtw==
+  dependencies:
+    "@parcel/cache" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/workers" "2.8.3"
+    utility-types "^3.10.0"
+
+"@parcel/utils@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.8.3.tgz#0d56c9e8e22c119590a5e044a0e01031965da40e"
+  integrity sha512-IhVrmNiJ+LOKHcCivG5dnuLGjhPYxQ/IzbnF2DKNQXWBTsYlHkJZpmz7THoeLtLliGmSOZ3ZCsbR8/tJJKmxjA==
+  dependencies:
+    "@parcel/codeframe" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/markdown-ansi" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    chalk "^4.1.0"
+
+"@parcel/watcher@^2.0.7":
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545"
+  integrity sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==
+  dependencies:
+    is-glob "^4.0.3"
+    micromatch "^4.0.5"
+    node-addon-api "^3.2.1"
+    node-gyp-build "^4.3.0"
+
+"@parcel/workers@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-2.8.3.tgz#255450ccf4db234082407e4ddda5fd575f08c235"
+  integrity sha512-+AxBnKgjqVpUHBcHLWIHcjYgKIvHIpZjN33mG5LG9XXvrZiqdWvouEzqEXlVLq5VzzVbKIQQcmsvRy138YErkg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chrome-trace-event "^1.0.2"
+    nullthrows "^1.1.1"
+
+"@swc/helpers@^0.4.12":
+  version "0.4.14"
+  resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
+  integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==
+  dependencies:
+    tslib "^2.4.0"
+
+"@trysound/sax@0.2.0":
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
+  integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
+
+"@types/parse-json@^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
+  integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+
+abortcontroller-polyfill@^1.1.9:
+  version "1.7.5"
+  resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed"
+  integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==
+
+acorn@^8.5.0:
+  version "8.8.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
+  integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
+
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+base-x@^3.0.8:
+  version "3.0.9"
+  resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320"
+  integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==
+  dependencies:
+    safe-buffer "^5.0.1"
+
+boolbase@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
+braces@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+  dependencies:
+    fill-range "^7.0.1"
+
+browserslist@^4.6.6:
+  version "4.21.4"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
+  integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
+  dependencies:
+    caniuse-lite "^1.0.30001400"
+    electron-to-chromium "^1.4.251"
+    node-releases "^2.0.6"
+    update-browserslist-db "^1.0.9"
+
+buffer-from@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+  integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+caniuse-lite@^1.0.30001400:
+  version "1.0.30001446"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz#6d4ba828ab19f49f9bcd14a8430d30feebf1e0c5"
+  integrity sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==
+
+chalk@^2.0.0:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^4.1.0:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chrome-trace-event@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
+  integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
+
+clone@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
+  integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
+
+color-convert@^1.9.0:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@^7.0.0, commander@^7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+  integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
+cosmiconfig@^7.0.1:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
+  integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
+  dependencies:
+    "@types/parse-json" "^4.0.0"
+    import-fresh "^3.2.1"
+    parse-json "^5.0.0"
+    path-type "^4.0.0"
+    yaml "^1.10.0"
+
+css-select@^4.1.3:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
+  integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^6.0.1"
+    domhandler "^4.3.1"
+    domutils "^2.8.0"
+    nth-check "^2.0.1"
+
+css-tree@^1.1.2, css-tree@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
+  integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
+  dependencies:
+    mdn-data "2.0.14"
+    source-map "^0.6.1"
+
+css-what@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+  integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
+csso@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
+  integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
+  dependencies:
+    css-tree "^1.1.2"
+
+detect-libc@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+  integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
+
+dom-serializer@^1.0.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
+  integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.2.0"
+    entities "^2.0.0"
+
+domelementtype@^2.0.1, domelementtype@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+  integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^4.2.0, domhandler@^4.2.2, domhandler@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
+  integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
+  dependencies:
+    domelementtype "^2.2.0"
+
+domutils@^2.8.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
+  integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
+  dependencies:
+    dom-serializer "^1.0.1"
+    domelementtype "^2.2.0"
+    domhandler "^4.2.0"
+
+dotenv-expand@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
+  integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
+
+dotenv@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c"
+  integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==
+
+electron-to-chromium@^1.4.251:
+  version "1.4.284"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592"
+  integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==
+
+entities@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
+  integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+
+entities@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
+  integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
+
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+escalade@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+get-port@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119"
+  integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==
+
+globals@^13.2.0:
+  version "13.19.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8"
+  integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==
+  dependencies:
+    type-fest "^0.20.2"
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+htmlnano@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-2.0.3.tgz#50ee639ed63357d4a6c01309f52a35892e4edc2e"
+  integrity sha512-S4PGGj9RbdgW8LhbILNK7W9JhmYP8zmDY7KDV/8eCiJBQJlbmltp5I0gv8c5ntLljfdxxfmJ+UJVSqyH4mb41A==
+  dependencies:
+    cosmiconfig "^7.0.1"
+    posthtml "^0.16.5"
+    timsort "^0.3.0"
+
+htmlparser2@^7.1.1:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5"
+  integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.2.2"
+    domutils "^2.8.0"
+    entities "^3.0.1"
+
+import-fresh@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-glob@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-json@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-json/-/is-json-2.0.1.tgz#6be166d144828a131d686891b983df62c39491ff"
+  integrity sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+json-parse-even-better-errors@^2.3.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+  integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json5@^2.2.0, json5@^2.2.1:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+lightningcss-darwin-arm64@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.18.0.tgz#bcd7d494d99c69947abd71136a42e80dfa80c682"
+  integrity sha512-OqjydwtiNPgdH1ByIjA1YzqvDG/OMR6L3LPN6wRl1729LB0y4Mik7L06kmZaTb+pvUHr+NmDd2KCwnlrQ4zO3w==
+
+lightningcss-darwin-x64@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.18.0.tgz#952abea2405fe2bb8dd0bb57a9d5590f8d1d6414"
+  integrity sha512-mNiuPHj89/JHZmJMp+5H8EZSt6EL5DZRWJ31O6k3DrLLnRIQjXuXdDdN8kP7LoIkeWI5xvyD60CsReJm+YWYAw==
+
+lightningcss-linux-arm-gnueabihf@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.18.0.tgz#23ca85e05dc4def9b4975aef307554ef292b56cd"
+  integrity sha512-S+25JjI6601HiAVoTDXW6SqH+E94a+FHA7WQqseyNHunOgVWKcAkNEc2LJvVxgwTq6z41sDIb9/M3Z9wa9lk4A==
+
+lightningcss-linux-arm64-gnu@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.18.0.tgz#6c8e0a6e2c8b44cf180f3a0f0740402e8f656155"
+  integrity sha512-JSqh4+21dCgBecIQUet35dtE4PhhSEMyqe3y0ZNQrAJQ5kyUPSQHiw81WXnPJcOSTTpG0TyMLiC8K//+BsFGQA==
+
+lightningcss-linux-arm64-musl@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.18.0.tgz#88393c101cf236ea0cdc97fddd66b82db964d835"
+  integrity sha512-2FWHa8iUhShnZnqhn2wfIcK5adJat9hAAaX7etNsoXJymlliDIOFuBQEsba2KBAZSM4QqfQtvRdR7m8i0I7ybQ==
+
+lightningcss-linux-x64-gnu@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.18.0.tgz#ad068d24836568337bfe545650565e13f813c8ee"
+  integrity sha512-plCPGQJtDZHcLVKVRLnQVF2XRsIC32WvuJhQ7fJ7F6BV98b/VZX0OlX05qUaOESD9dCDHjYSfxsgcvOKgCWh7A==
+
+lightningcss-linux-x64-musl@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.18.0.tgz#4d84de26b8185aa42450e0f4c83bbfb5a36ae750"
+  integrity sha512-na+BGtVU6fpZvOHKhnlA0XHeibkT3/46nj6vLluG3kzdJYoBKU6dIl7DSOk++8jv4ybZyFJ0aOFMMSc8g2h58A==
+
+lightningcss-win32-x64-msvc@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.18.0.tgz#f83952d16b83dfce65f4615f87c867769220d117"
+  integrity sha512-5qeAH4RMNy2yMNEl7e5TI6upt/7xD2ZpHWH4RkT8iJ7/6POS5mjHbXWUO9Q1hhDhqkdzGa76uAdMzEouIeCyNw==
+
+lightningcss@^1.16.1:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.18.0.tgz#ca3327a1a7571a83bbb9733ed4e4cded775bdadf"
+  integrity sha512-uk10tNxi5fhZqU93vtYiQgx/8a9f0Kvtj5AXIm+VlOXY+t/DWDmCZWJEkZJmmALgvbS6aAW8or+Kq85eJ6TDTw==
+  dependencies:
+    detect-libc "^1.0.3"
+  optionalDependencies:
+    lightningcss-darwin-arm64 "1.18.0"
+    lightningcss-darwin-x64 "1.18.0"
+    lightningcss-linux-arm-gnueabihf "1.18.0"
+    lightningcss-linux-arm64-gnu "1.18.0"
+    lightningcss-linux-arm64-musl "1.18.0"
+    lightningcss-linux-x64-gnu "1.18.0"
+    lightningcss-linux-x64-musl "1.18.0"
+    lightningcss-win32-x64-msvc "1.18.0"
+
+lines-and-columns@^1.1.6:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+  integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+lmdb@2.5.2:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.5.2.tgz#37e28a9fb43405f4dc48c44cec0e13a14c4a6ff1"
+  integrity sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA==
+  dependencies:
+    msgpackr "^1.5.4"
+    node-addon-api "^4.3.0"
+    node-gyp-build-optional-packages "5.0.3"
+    ordered-binary "^1.2.4"
+    weak-lru-cache "^1.2.2"
+  optionalDependencies:
+    "@lmdb/lmdb-darwin-arm64" "2.5.2"
+    "@lmdb/lmdb-darwin-x64" "2.5.2"
+    "@lmdb/lmdb-linux-arm" "2.5.2"
+    "@lmdb/lmdb-linux-arm64" "2.5.2"
+    "@lmdb/lmdb-linux-x64" "2.5.2"
+    "@lmdb/lmdb-win32-x64" "2.5.2"
+
+loose-envify@^1.1.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+mdn-data@2.0.14:
+  version "2.0.14"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
+  integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
+
+micromatch@^4.0.5:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+  dependencies:
+    braces "^3.0.2"
+    picomatch "^2.3.1"
+
+msgpackr-extract@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.2.0.tgz#4bb749b58d9764cfdc0d91c7977a007b08e8f262"
+  integrity sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==
+  dependencies:
+    node-gyp-build-optional-packages "5.0.3"
+  optionalDependencies:
+    "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-arm" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-x64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-win32-x64" "2.2.0"
+
+msgpackr@^1.5.4:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.1.tgz#2298aed8a14f83e99df77d344cbda3e436f29b5b"
+  integrity sha512-05fT4J8ZqjYlR4QcRDIhLCYKUOHXk7C/xa62GzMKj74l3up9k2QZ3LgFc6qWdsPHl91QA2WLWqWc8b8t7GLNNw==
+  optionalDependencies:
+    msgpackr-extract "^2.2.0"
+
+node-addon-api@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
+  integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
+
+node-addon-api@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
+  integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
+
+node-gyp-build-optional-packages@5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17"
+  integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==
+
+node-gyp-build@^4.3.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055"
+  integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==
+
+node-releases@^2.0.6:
+  version "2.0.8"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae"
+  integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==
+
+nth-check@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+  integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+  dependencies:
+    boolbase "^1.0.0"
+
+nullthrows@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
+  integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==
+
+ordered-binary@^1.2.4:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.0.tgz#6bb53d44925f3b8afc33d1eed0fa15693b211389"
+  integrity sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ==
+
+parcel@^2.8.3:
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.8.3.tgz#1ff71d7317274fd367379bc7310a52c6b75d30c2"
+  integrity sha512-5rMBpbNE72g6jZvkdR5gS2nyhwIXaJy8i65osOqs/+5b7zgf3eMKgjSsDrv6bhz3gzifsba6MBJiZdBckl+vnA==
+  dependencies:
+    "@parcel/config-default" "2.8.3"
+    "@parcel/core" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/reporter-cli" "2.8.3"
+    "@parcel/reporter-dev-server" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chalk "^4.1.0"
+    commander "^7.0.0"
+    get-port "^4.2.0"
+    v8-compile-cache "^2.0.0"
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+parse-json@^5.0.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+  integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    error-ex "^1.3.1"
+    json-parse-even-better-errors "^2.3.0"
+    lines-and-columns "^1.1.6"
+
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picocolors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+postcss-value-parser@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+  integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+posthtml-parser@^0.10.1:
+  version "0.10.2"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.10.2.tgz#df364d7b179f2a6bf0466b56be7b98fd4e97c573"
+  integrity sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==
+  dependencies:
+    htmlparser2 "^7.1.1"
+
+posthtml-parser@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.11.0.tgz#25d1c7bf811ea83559bc4c21c189a29747a24b7a"
+  integrity sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==
+  dependencies:
+    htmlparser2 "^7.1.1"
+
+posthtml-render@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-3.0.0.tgz#97be44931496f495b4f07b99e903cc70ad6a3205"
+  integrity sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==
+  dependencies:
+    is-json "^2.0.1"
+
+posthtml@^0.16.4, posthtml@^0.16.5:
+  version "0.16.6"
+  resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.16.6.tgz#e2fc407f67a64d2fa3567afe770409ffdadafe59"
+  integrity sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==
+  dependencies:
+    posthtml-parser "^0.11.0"
+    posthtml-render "^3.0.0"
+
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
+
+react-dom@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+  integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
+  dependencies:
+    loose-envify "^1.1.0"
+    scheduler "^0.23.0"
+
+react-error-overlay@6.0.9:
+  version "6.0.9"
+  resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
+  integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
+
+react-refresh@^0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf"
+  integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==
+
+react@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+  integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
+  dependencies:
+    loose-envify "^1.1.0"
+
+regenerator-runtime@^0.13.7:
+  version "0.13.11"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
+  integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+safe-buffer@^5.0.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+scheduler@^0.23.0:
+  version "0.23.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+  integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
+  dependencies:
+    loose-envify "^1.1.0"
+
+semver@^5.7.0, semver@^5.7.1:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+source-map-support@~0.5.20:
+  version "0.5.21"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+  integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map@^0.6.0, source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+srcset@4:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4"
+  integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==
+
+stable@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+svgo@^2.4.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
+  integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
+  dependencies:
+    "@trysound/sax" "0.2.0"
+    commander "^7.2.0"
+    css-select "^4.1.3"
+    css-tree "^1.1.3"
+    csso "^4.2.0"
+    picocolors "^1.0.0"
+    stable "^0.1.8"
+
+term-size@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
+  integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
+
+terser@^5.2.0:
+  version "5.16.1"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.1.tgz#5af3bc3d0f24241c7fb2024199d5c461a1075880"
+  integrity sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==
+  dependencies:
+    "@jridgewell/source-map" "^0.3.2"
+    acorn "^8.5.0"
+    commander "^2.20.0"
+    source-map-support "~0.5.20"
+
+timsort@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
+  integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+tslib@^2.4.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
+  integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
+
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+update-browserslist-db@^1.0.9:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
+  integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==
+  dependencies:
+    escalade "^3.1.1"
+    picocolors "^1.0.0"
+
+utility-types@^3.10.0:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b"
+  integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==
+
+v8-compile-cache@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+  integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
+weak-lru-cache@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19"
+  integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==
+
+xxhash-wasm@^0.4.2:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz#752398c131a4dd407b5132ba62ad372029be6f79"
+  integrity sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==
+
+yaml@^1.10.0:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
+  integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
diff --git a/users/wpcarro/terraform/default.nix b/users/wpcarro/terraform/default.nix
index be35785a54..b8625c3b8d 100644
--- a/users/wpcarro/terraform/default.nix
+++ b/users/wpcarro/terraform/default.nix
@@ -5,181 +5,188 @@ let
   inherit (depot.users) wpcarro;
   inherit (pkgs) writeText;
 
-  images = import "${pkgs.path}/nixos/modules/virtualisation/gce-images.nix";
+  images = import (pkgs.path + "/nixos/modules/virtualisation/gce-images.nix");
   nixosImage = images."20.09";
-in {
-  googleCloudVM = {
-    project,
-    name,
-    region,
-    zone,
-    configuration,
-    extraConfig ? {},
-  }: let
-    inherit (configuration.users.users) root;
-    inherit (configuration.networking) firewall;
-
-    # Convert NixOS-style port numbers to Terraform-style.
-    asStrings = xs: map toString xs;
-    asRanges = xs: map (x: "${toString x.from}-${toString x.to}") xs;
-
-    sshKeys = concatStringsSep "\n"
-      (map (key: "root:${key}") root.openssh.authorizedKeys.keys);
-
-    os = depot.ops.nixos.nixosFor (_: {
-      imports = [
-        "${pkgs.path}/nixos/modules/virtualisation/google-compute-image.nix"
-        configuration
-      ];
-
-      networking.hostName = name;
-
-      fileSystems."/nix" = {
-        device = "/dev/disk/by-label/google-${name}-disk";
-        fsType = "ext4";
-      };
-    });
-
-    osRoot = os.config.system.build.toplevel;
-    osPath = unsafeDiscardStringContext (toString osRoot.outPath);
-    drvPath = unsafeDiscardStringContext (toString osRoot.drvPath);
-  in writeText "terraform.tf.json" (toJSON (lib.recursiveUpdate extraConfig {
-    provider.google = {
-      inherit project region zone;
-    };
-
-    resource.google_compute_instance."${name}" = {
-      inherit name zone;
-      machine_type = "e2-standard-2";
-  
-      tags = [
-        "http-server"
-        "https-server"
-        "${name}-firewall"
-      ];
-  
-      boot_disk = {
-        device_name = "boot";
-        initialize_params = {
-          size = 10;
-          image = "projects/nixos-cloud/global/images/${nixosImage.name}";
+in
+{
+  googleCloudVM =
+    { project
+    , name
+    , region
+    , zone
+    , configuration
+    , extraConfig ? { }
+    ,
+    }:
+    let
+      inherit (configuration.users.users) root;
+      inherit (configuration.networking) firewall;
+
+      # Convert NixOS-style port numbers to Terraform-style.
+      asStrings = xs: map toString xs;
+      asRanges = xs: map (x: "${toString x.from}-${toString x.to}") xs;
+
+      sshKeys = concatStringsSep "\n"
+        (map (key: "root:${key}") root.openssh.authorizedKeys.keys);
+
+      os = depot.ops.nixos.nixosFor (_: {
+        imports = [
+          (pkgs.path + "/nixos/modules/virtualisation/google-compute-image.nix")
+          configuration
+        ];
+
+        networking.hostName = name;
+
+        fileSystems."/nix" = {
+          device = "/dev/disk/by-label/google-${name}-disk";
+          fsType = "ext4";
+        };
+      });
+
+      osRoot = os.config.system.build.toplevel;
+      osPath = unsafeDiscardStringContext (toString osRoot.outPath);
+      drvPath = unsafeDiscardStringContext (toString osRoot.drvPath);
+    in
+    {
+      inherit drvPath osPath;
+      json = writeText "terraform.tf.json" (toJSON (lib.recursiveUpdate extraConfig {
+        provider.google = {
+          inherit project region zone;
         };
-      };
-  
-      attached_disk = {
-        source = "\${google_compute_disk.${name}.id}";
-        device_name = "${name}-disk";
-      };
-  
-      network_interface = {
-        network = "default";
-        subnetwork = "default";
-        access_config = {};
-      };
-  
-      # Copy root's SSH keys from the NixOS configuration and expose them to the
-      # metadata server.
-      metadata = {
-        inherit sshKeys;
-        ssh-keys = sshKeys;
-
-        # NixOS's fetch-instance-ssh-keys.bash relies on these fields being
-        # available on the metadata server.
-        ssh_host_ed25519_key = "\${tls_private_key.${name}.private_key_pem}";
-        ssh_host_ed25519_key_pub = "\${tls_private_key.${name}.public_key_pem}";
-
-        # Even though we have SSH access, having oslogin can still be useful for
-        # troubleshooting in the browser if for some reason SSH isn't working as
-        # expected.
-        enable-oslogin = "TRUE";
-      };
-  
-      service_account.scopes = ["cloud-platform"];
-    };
 
-    resource.tls_private_key."${name}" = {
-      algorithm = "ECDSA";
-      ecdsa_curve = "P384";
-    };
+        resource.google_compute_instance."${name}" = {
+          inherit name zone;
+          machine_type = "e2-standard-2";
 
-    resource.google_compute_firewall."${name}" = {
-      name = "${name}-firewall";
-      network = "default";
-
-      # Read the firewall configuration from the NixOS configuration.
-      allow = [
-        {
-          protocol = "tcp";
-          ports = concatLists [
-            (asStrings (firewall.allowedTCPPorts or []))
-            (asRanges (firewall.allowedTCPPortRanges or []))
+          tags = [
+            "http-server"
+            "https-server"
+            "${name}-firewall"
           ];
-        }
-        {
-          protocol = "udp";
-          ports = concatLists [
-            (asStrings (firewall.allowedUDPPorts or []))
-            (asRanges (firewall.allowedUDPPortRanges or []))
+
+          boot_disk = {
+            device_name = "boot";
+            initialize_params = {
+              size = 10;
+              image = "projects/nixos-cloud/global/images/${nixosImage.name}";
+            };
+          };
+
+          attached_disk = {
+            source = "\${google_compute_disk.${name}.id}";
+            device_name = "${name}-disk";
+          };
+
+          network_interface = {
+            network = "default";
+            subnetwork = "default";
+            access_config = { };
+          };
+
+          # Copy root's SSH keys from the NixOS configuration and expose them to the
+          # metadata server.
+          metadata = {
+            inherit sshKeys;
+            ssh-keys = sshKeys;
+
+            # NixOS's fetch-instance-ssh-keys.bash relies on these fields being
+            # available on the metadata server.
+            ssh_host_ed25519_key = "\${tls_private_key.${name}.private_key_pem}";
+            ssh_host_ed25519_key_pub = "\${tls_private_key.${name}.public_key_pem}";
+
+            # Even though we have SSH access, having oslogin can still be useful for
+            # troubleshooting in the browser if for some reason SSH isn't working as
+            # expected.
+            enable-oslogin = "TRUE";
+          };
+
+          service_account.scopes = [ "cloud-platform" ];
+        };
+
+        resource.tls_private_key."${name}" = {
+          algorithm = "ECDSA";
+          ecdsa_curve = "P384";
+        };
+
+        resource.google_compute_firewall."${name}" = {
+          name = "${name}-firewall";
+          network = "default";
+
+          # Read the firewall configuration from the NixOS configuration.
+          allow = [
+            {
+              protocol = "tcp";
+              ports = concatLists [
+                (asStrings (firewall.allowedTCPPorts or [ ]))
+                (asRanges (firewall.allowedTCPPortRanges or [ ]))
+              ];
+            }
+            {
+              protocol = "udp";
+              ports = concatLists [
+                (asStrings (firewall.allowedUDPPorts or [ ]))
+                (asRanges (firewall.allowedUDPPortRanges or [ ]))
+              ];
+            }
           ];
-        }
-      ];
-      source_ranges = ["0.0.0.0/0"];
-    };
-  
-    resource.google_compute_disk."${name}" = {
-      inherit zone;
-      name = "${name}-disk";
-      size = 100;
-    };
+          source_ranges = [ "0.0.0.0/0" ];
+        };
 
-    resource.null_resource.deploy_nixos = {
-      triggers = {
-        # Redeploy when the NixOS configuration changes.
-        os = "${osPath}";
-        # Redeploy when a new machine is provisioned.
-        machine_id = "\${google_compute_instance.${name}.id}";
-      };
-
-      connection = {
-        host = "\${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip}";
-      };
-
-      provisioner = [
-        { remote-exec.inline = ["true"]; }
-        {
-          local-exec.command = ''
-            export PATH="${pkgs.openssh}/bin:$PATH"
-
-            scratch="$(mktemp -d)"
-            function cleanup() {
-              rm -rf $scratch
+        resource.google_compute_disk."${name}" = {
+          inherit zone;
+          name = "${name}-disk";
+          size = 100;
+        };
+
+        resource.null_resource.deploy_nixos = {
+          triggers = {
+            # Redeploy when the NixOS configuration changes.
+            os = "${osPath}";
+            # Redeploy when a new machine is provisioned.
+            machine_id = "\${google_compute_instance.${name}.id}";
+          };
+
+          connection = {
+            host = "\${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip}";
+          };
+
+          provisioner = [
+            { remote-exec.inline = [ "true" ]; }
+            {
+              local-exec.command = ''
+                export PATH="${pkgs.openssh}/bin:$PATH"
+
+                scratch="$(mktemp -d)"
+                function cleanup() {
+                  rm -rf $scratch
+                }
+                trap cleanup EXIT
+
+                # write out ssh key
+                echo -n "''${tls_private_key.${name}.private_key_pem}" > $scratch/id_rsa.pem
+                chmod 0600 $scratch/id_rsa.pem
+
+                export NIX_SSHOPTS="\
+                  -o StrictHostKeyChecking=no\
+                  -o UserKnownHostsFile=/dev/null\
+                  -o GlobalKnownHostsFile=/dev/null\
+                  -o IdentityFile=$scratch/id_rsa.pem
+                "
+
+                nix-build ${drvPath}
+                nix-copy-closure --to \
+                  root@''${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip} \
+                  ${osPath} --gzip --use-substitutes
+              '';
+            }
+            {
+              remote-exec.inline = [
+                "nix-env --profile /nix/var/nix/profiles/system --set ${osPath}"
+                "${osPath}/bin/switch-to-configuration switch"
+              ];
             }
-            trap cleanup EXIT
-
-            # write out ssh key
-            echo -n "''${tls_private_key.${name}.private_key_pem}" > $scratch/id_rsa.pem
-            chmod 0600 $scratch/id_rsa.pem
-
-            export NIX_SSHOPTS="\
-              -o StrictHostKeyChecking=no\
-              -o UserKnownHostsFile=/dev/null\
-              -o GlobalKnownHostsFile=/dev/null\
-              -o IdentityFile=$scratch/id_rsa.pem
-            "
-
-            nix-build ${drvPath}
-            nix-copy-closure --to \
-              root@''${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip} \
-              ${osPath} --gzip --use-substitutes
-          '';
-        }
-        {
-          remote-exec.inline = [
-            "nix-env --profile /nix/var/nix/profiles/system --set ${osPath}"
-            "${osPath}/bin/switch-to-configuration switch"
           ];
-        }
-      ];
+        };
+      }));
     };
-  }));
 }
diff --git a/users/wpcarro/todo-lists/cta-curriculum.csv b/users/wpcarro/todo-lists/cta-curriculum.csv
new file mode 100644
index 0000000000..7ebc82d645
--- /dev/null
+++ b/users/wpcarro/todo-lists/cta-curriculum.csv
@@ -0,0 +1,108 @@
+name,position,goal

+collar choke,back,submission

+bow & arrow choke,back,submission

+rear naked choke,back,submission

+armlock,back,submission

+collar choke escape,back,escape

+framing defense,back,defense

+pulling the arm to the other side (I),back,defense

+pulling the arm to the other side (II),back,defense

+back escape to 1x-leg,back,escape

+changing sides,back,defense

+opening closed guard (sleeve),closed guard,escape

+opening closed guard (hips),closed guard,escape

+opening closed guard (hips -> sleeve),closed guard,escape

+catucada (I),closed guard,sweep

+catucada (II),closed guard,sweep

+sit-up sweep,closed guard,sweep

+scissor sweep,closed guard,sweep

+2x ankle sweep,closed guard,sweep

+sit-up sweep -> kimura (I),closed guard,submission

+sit-up sweep -> kimura (variations),closed guard,submission

+omoplata,closed guard,submission

+omoplata escape -> side control,closed guard,escape

+omoplata escape -> standing,closed guard,escape

+overhook triangle (I),closed guard,submission

+overhook triangle (II),closed guard,submission

+armlock,closed guard,submission

+flower sweep,closed guard,sweep

+kimura,closed guard,submission

+triangle defense,closed guard,defense

+triangle escape,closed guard,escape

+armlock escape,closed guard,escape

+half guard -> closed guard (I),half guard,transition

+half guard -> closed guard (II),half guard,transition

+upa,half guard,sweep

+half guard -> back,half guard,transition

+underhook sweep,half guard,sweep

+knee slide pass (backstep),half guard,pass

+knee slide pass (hip-switch, knee-cut),half guard,pass

+knee slide pass (push the knee),half guard,pass

+knee slide pass (2x-hook magic),half guard,pass

+tripod pass (backstep),half guard,pass

+tripod pass (hip-switch, knee-cut),half guard,pass

+tripod pass (push the knee),half guard,pass

+tripod pass (2x-hook magic),half guard,pass

+keylock,mount,submission

+keylock -> armlock (I),mount,submission

+keylock -> armlock (II),mount,submission

+upa,mount,sweep

+cross-choke defense,mount,defense

+keylock escape,mount,escape

+hip press escape (straight back),mount,escape

+hip press escape (sideways),mount,escape

+elbow escape,mount,escape

+ezekiel choke,mount,submission

+retaining low mount,mount,retention

+retaining high mount,mount,retention

+armlock,mount,submission

+armlock escape,mount,escape

+armlock (breaking the grips),mount,submission

+cross-choke (I),mount,submission

+cross-choke (II),mount,submission

+bull pass,open guard,pass

+2x-under,open guard,pass

+1x-under,open guard,pass

+1x-under -> half guard,open guard,pass

+straight ankle lock,open guard,submission

+straight ankle lock defense,open guard,defense

+straight ankle lock defense -> mount,open guard,escape

+side control -> mount,side control,transition

+armlock (same side),side control,submission

+armlock escape (hitchhiker),side control,escape

+kimura,side control,submission

+kimura -> armlock,side control,submission

+kimura (breaking the grips),side control,submission

+escape (doorstop),side control,escape

+modern hip escape,side control,escape

+escape,side control,escape

+kesagatame escape,side control,escape

+kesagatame escape (from punches),side control,escape

+retention,knee on belly,retention

+armlock,knee on belly,submission

+knee on belly escape,knee on belly,escape

+knee on belly -> mount,knee on belly,transition

+pulling closed guard,standing,transition

+pulling to armbar,standing,submission

+pendulum sweep,standing,sweep

+2x-ankle sweep,standing,sweep

+collar drag to 1x-leg,standing,sweep

+collar drag sweep,standing,sweep

+collar drag (seated),standing,transition

+hip throw from neck control,standing,escape

+hip throw to armbar,standing,submission

+osoto gari from neck control,standing,escape

+osoto gari to armbar,standing,submission

+basic osoto gari,standing,takedown

+1x-leg,standing,takedown

+guillotine (arm out),standing,submission

+guillotine (arm out) escape,standing,escape

+headlock escape,standing,escape

+headlock escape (from punches),standing,escape

+guillotine (arm in),standing,submission

+guillotine (arm in) escape,standing,escape

+outside trip -> 2x-leg,standing,takedown

+bear hug escape,standing,escape

+body lock escape,standing,escape

+2x-leg,standing,takedown

+2x-leg sprawl defense to back,standing,defense
\ No newline at end of file
diff --git a/users/wpcarro/tools/monzo_ynab/.envrc b/users/wpcarro/tools/monzo_ynab/.envrc
index 2e3b53cd61..6560926eae 100644
--- a/users/wpcarro/tools/monzo_ynab/.envrc
+++ b/users/wpcarro/tools/monzo_ynab/.envrc
@@ -1,5 +1,5 @@
 source_up
-use_nix
+
 # TODO(wpcarro): Prefer age-nix solution if possible.
 export monzo_client_id="$(jq -j '.monzo | .clientId' < $WPCARRO/secrets.json)"
 export monzo_client_secret="$(jq -j '.monzo | .clientSecret' < $WPCARRO/secrets.json)"
diff --git a/users/wpcarro/tools/monzo_ynab/.skip-subtree b/users/wpcarro/tools/monzo_ynab/.skip-subtree
deleted file mode 100644
index 8db1f814f6..0000000000
--- a/users/wpcarro/tools/monzo_ynab/.skip-subtree
+++ /dev/null
@@ -1,2 +0,0 @@
-Subdirectories of this folder should not be imported since they are
-internal to buildGo.nix and incompatible with readTree.
diff --git a/users/wpcarro/tools/monzo_ynab/job.nix b/users/wpcarro/tools/monzo_ynab/job.nix
deleted file mode 100644
index c2c8baab3b..0000000000
--- a/users/wpcarro/tools/monzo_ynab/job.nix
+++ /dev/null
@@ -1,14 +0,0 @@
-{ depot, ... }:
-
-let
-  inherit (depot.users.wpcarro) gopkgs;
-in depot.nix.buildGo.program {
-  name = "job";
-  srcs = [
-    ./main.go
-  ];
-  deps = with gopkgs; [
-    kv
-    utils
-  ];
-}
diff --git a/users/wpcarro/tools/monzo_ynab/main.go b/users/wpcarro/tools/monzo_ynab/main.go
index bf37071381..900deac0cb 100644
--- a/users/wpcarro/tools/monzo_ynab/main.go
+++ b/users/wpcarro/tools/monzo_ynab/main.go
@@ -10,8 +10,11 @@
 package main
 
 import (
+	"monzoClient"
 	"monzoSerde"
 	"os"
+	"ynabClient"
+	"ynabSerde"
 )
 
 var (
@@ -34,11 +37,12 @@ func toYnab(tx monzoSerde.Transaction) ynabSerde.Transaction {
 }
 
 func main() {
+	monzo := monzoClient.Create()
 	txs := monzo.TransactionsLast24Hours()
 	var ynabTxs []ynabSerde.Transaction
-	for tx := range txs {
-		append(ynabTxs, toYnab(tx))
+	for _, tx := range txs {
+		ynabTxs = append(ynabTxs, toYnab(tx))
 	}
-	ynab.PostTransactions(ynabTxs)
+	ynabClient.PostTransactions(ynabTxs)
 	os.Exit(0)
 }
diff --git a/users/wpcarro/tools/monzo_ynab/monzo/client.go b/users/wpcarro/tools/monzo_ynab/monzo/client.go
index 8c6c41e29f..9621ffc5ad 100644
--- a/users/wpcarro/tools/monzo_ynab/monzo/client.go
+++ b/users/wpcarro/tools/monzo_ynab/monzo/client.go
@@ -27,8 +27,8 @@ func Create() *Client {
 }
 
 // Returns a slice of transactions from the last 24 hours.
-func (c *Client) Transactions24Hours() []monzoSerde.Transaction {
-	token := tokens.AccessToken()
+func (c *Client) TransactionsLast24Hours() []monzoSerde.Transaction {
+	token := tokens.GetState().AccessToken
 	form := url.Values{"account_id": {accountID}}
 	client := http.Client{}
 	req, _ := http.NewRequest("POST", "https://api.monzo.com/transactions",
diff --git a/users/wpcarro/tools/monzo_ynab/monzo/serde.go b/users/wpcarro/tools/monzo_ynab/monzo/serde.go
index a38585eca6..e2f55dad45 100644
--- a/users/wpcarro/tools/monzo_ynab/monzo/serde.go
+++ b/users/wpcarro/tools/monzo_ynab/monzo/serde.go
@@ -1,11 +1,9 @@
 // This package hosts the serialization and deserialization logic for all of the
 // data types with which our application interacts from the Monzo API.
-package main
+package monzoSerde
 
 import (
 	"encoding/json"
-	"fmt"
-	"io/ioutil"
 	"time"
 )
 
@@ -72,11 +70,3 @@ func deserializeTx(x string) (*Transaction, error) {
 	err := json.Unmarshal([]byte(x), target)
 	return target, err
 }
-
-func main() {
-	b, _ := ioutil.ReadFile("./fixture.json")
-	tx := string(b)
-	target, _ := deserializeTx(tx)
-	out, _ := serializeTx(target)
-	fmt.Println(out)
-}
diff --git a/users/wpcarro/tools/monzo_ynab/shell.nix b/users/wpcarro/tools/monzo_ynab/shell.nix
deleted file mode 100644
index f777c13fef..0000000000
--- a/users/wpcarro/tools/monzo_ynab/shell.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-{ pkgs, ... }:
-
-pkgs.mkShell {
-  buildInputs = with pkgs; [
-    go
-    goimports
-    godef
-  ];
-}
diff --git a/users/wpcarro/tools/monzo_ynab/tokens.go b/users/wpcarro/tools/monzo_ynab/tokens.go
index 4be967ccb8..01b57d3daa 100644
--- a/users/wpcarro/tools/monzo_ynab/tokens.go
+++ b/users/wpcarro/tools/monzo_ynab/tokens.go
@@ -1,7 +1,7 @@
 // Creating a Tokens server to manage my access and refresh tokens. Keeping this
 // as a separate server allows me to develop and use the access tokens without
 // going through client authorization.
-package main
+package tokens
 
 ////////////////////////////////////////////////////////////////////////////////
 // Dependencies
@@ -46,7 +46,7 @@ type setTokensRequest struct {
 
 // This is our application state.
 type state struct {
-	accessToken  string `json:"access_token"`
+	AccessToken  string `json:"access_token"`
 	refreshToken string `json:"refresh_token"`
 }
 
@@ -90,7 +90,7 @@ func logTokens(access string, refresh string) {
 }
 
 func (state *state) String() string {
-	return fmt.Sprintf("state{\n\taccessToken: \"%s\",\n\trefreshToken: \"%s\"\n}\n", state.accessToken, state.refreshToken)
+	return fmt.Sprintf("state{\n\tAccessToken: \"%s\",\n\trefreshToken: \"%s\"\n}\n", state.AccessToken, state.refreshToken)
 }
 
 // Schedule a token refresh for `expiresIn` seconds using the provided
@@ -104,10 +104,10 @@ func scheduleTokenRefresh(expiresIn int, refreshToken string) {
 	log.Printf("Scheduling token refresh for %v\n", timestamp)
 	time.Sleep(duration)
 	log.Println("Refreshing tokens now...")
-	accessToken, refreshToken := refreshTokens(refreshToken)
+	AccessToken, refreshToken := refreshTokens(refreshToken)
 	log.Println("Successfully refreshed tokens.")
-	logTokens(accessToken, refreshToken)
-	setState(accessToken, refreshToken)
+	logTokens(AccessToken, refreshToken)
+	setState(AccessToken, refreshToken)
 }
 
 // Exchange existing credentials for a new access token and `refreshToken`. Also
@@ -169,8 +169,8 @@ func handleInterrupts() {
 	go func() {
 		sig := <-sigs
 		log.Printf("Received signal to shutdown. %v\n", sig)
-		state := getState()
-		persistTokens(state.accessToken, state.refreshToken)
+		state := GetState()
+		persistTokens(state.AccessToken, state.refreshToken)
 		done <- true
 	}()
 
@@ -179,25 +179,21 @@ func handleInterrupts() {
 	os.Exit(0)
 }
 
-// Set `accessToken` and `refreshToken` on application state.
-func setState(accessToken string, refreshToken string) {
-	msg := writeMsg{state{accessToken, refreshToken}, make(chan bool)}
+// Set `AccessToken` and `refreshToken` on application state.
+func setState(AccessToken string, refreshToken string) {
+	msg := writeMsg{state{AccessToken, refreshToken}, make(chan bool)}
 	chans.writes <- msg
 	<-msg.sender
 }
 
 // Return our application state.
-func getState() state {
+func GetState() state {
 	msg := readMsg{make(chan state)}
 	chans.reads <- msg
 	return <-msg.sender
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Main
-////////////////////////////////////////////////////////////////////////////////
-
-func main() {
+func StartServer() {
 	// Manage application state.
 	go func() {
 		state := &state{}
@@ -215,7 +211,7 @@ func main() {
 				// As an attempt to maintain consistency between application
 				// state and persisted state, everytime we write to the
 				// application state, we will write to the store.
-				persistTokens(state.accessToken, state.refreshToken)
+				persistTokens(state.AccessToken, state.refreshToken)
 				msg.sender <- true
 			}
 		}
@@ -251,7 +247,7 @@ func main() {
 	log.Fatal(http.ListenAndServe(":4242",
 		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 			if req.URL.Path == "/refresh-tokens" && req.Method == "POST" {
-				state := getState()
+				state := GetState()
 				go scheduleTokenRefresh(0, state.refreshToken)
 				fmt.Fprintf(w, "Done.")
 			} else if req.URL.Path == "/set-tokens" && req.Method == "POST" {
@@ -273,7 +269,7 @@ func main() {
 			} else if req.URL.Path == "/state" && req.Method == "GET" {
 				// TODO(wpcarro): Ensure that this returns serialized state.
 				w.Header().Set("Content-type", "application/json")
-				state := getState()
+				state := GetState()
 				payload, _ := json.Marshal(state)
 				io.WriteString(w, string(payload))
 			} else {
diff --git a/users/wpcarro/tools/monzo_ynab/tokens.nix b/users/wpcarro/tools/monzo_ynab/tokens.nix
deleted file mode 100644
index b58c272bde..0000000000
--- a/users/wpcarro/tools/monzo_ynab/tokens.nix
+++ /dev/null
@@ -1,25 +0,0 @@
-{ depot, ... }:
-
-let
-  inherit (depot.users.wpcarro) gopkgs;
-
-  auth = depot.nix.buildGo.package {
-    name = "auth";
-    srcs = [
-      ./auth.go
-    ];
-    deps = with gopkgs; [
-      utils
-    ];
-  };
-in depot.nix.buildGo.program {
-  name = "token-server";
-  srcs = [
-    ./tokens.go
-  ];
-  deps = with gopkgs; [
-    kv
-    utils
-    auth
-  ];
-}
diff --git a/users/wpcarro/tools/monzo_ynab/ynab/client.go b/users/wpcarro/tools/monzo_ynab/ynab/client.go
index b3e9930f62..e63010b281 100644
--- a/users/wpcarro/tools/monzo_ynab/ynab/client.go
+++ b/users/wpcarro/tools/monzo_ynab/ynab/client.go
@@ -1,24 +1,9 @@
-package client
+package ynabClient
 
 import (
-	"serde"
+	"ynabSerde"
 )
 
-// // See requests.txt for more details.
-// func PostTransactions(accountID string, txs []serde.Transaction{}) error {
-// 	return map[string]string{
-// 		"transactions": [
-// 			{
-// 				"account_id": accountID,
-// 					"date": "2019-12-30",
-// 					"amount": 10000,
-// 					"payee_name": "Richard Stallman",
-// 					"memo": "Not so free software after all...",
-// 					"cleared": "cleared",
-// 					"approved": true,
-// 					"flag_color": "red",
-// 					"import_id": "xyz-123"
-// 			}
-// 		]
-// 	}
-// }
+// See requests.txt for more details.
+func PostTransactions(txs []ynabSerde.Transaction) {
+}
diff --git a/users/wpcarro/tools/monzo_ynab/ynab/serde.go b/users/wpcarro/tools/monzo_ynab/ynab/serde.go
index 53dd33e836..45dd921b24 100644
--- a/users/wpcarro/tools/monzo_ynab/ynab/serde.go
+++ b/users/wpcarro/tools/monzo_ynab/ynab/serde.go
@@ -1,10 +1,9 @@
 // This package hosts the serialization and deserialization logic for all of the
 // data types with which our application interacts from the YNAB API.
-package main
+package ynabSerde
 
 import (
 	"encoding/json"
-	"fmt"
 	"time"
 )
 
@@ -43,10 +42,3 @@ func deserializeTx(x string) (*Transaction, error) {
 	err := json.Unmarshal([]byte(x), target)
 	return target, err
 }
-
-func main() {
-	target, _ := deserializeTx(tx)
-	out, _ := serializeTx(target)
-	fmt.Println(out)
-	fmt.Println(ynabOut)
-}
diff --git a/users/wpcarro/tools/rfcToKindle/default.nix b/users/wpcarro/tools/rfcToKindle/default.nix
index 4ea2719439..ca87abdee0 100644
--- a/users/wpcarro/tools/rfcToKindle/default.nix
+++ b/users/wpcarro/tools/rfcToKindle/default.nix
@@ -7,5 +7,5 @@ depot.nix.buildGo.program {
   srcs = [
     ./main.go
   ];
-  deps = [];
+  deps = [ ];
 }
diff --git a/users/wpcarro/tools/symlinkManager/default.nix b/users/wpcarro/tools/symlinkManager/default.nix
index 4e261d7309..7d022828ee 100644
--- a/users/wpcarro/tools/symlinkManager/default.nix
+++ b/users/wpcarro/tools/symlinkManager/default.nix
@@ -2,7 +2,8 @@
 
 let
   inherit (depot.users.wpcarro) gopkgs;
-in depot.nix.buildGo.program {
+in
+depot.nix.buildGo.program {
   name = "symlink-mgr";
   srcs = [
     ./main.go
diff --git a/users/wpcarro/tools/systemd-shell/default.nix b/users/wpcarro/tools/systemd-shell/default.nix
new file mode 100644
index 0000000000..eace76b708
--- /dev/null
+++ b/users/wpcarro/tools/systemd-shell/default.nix
@@ -0,0 +1,8 @@
+{ pkgs, ... }:
+
+pkgs.python310Packages.buildPythonApplication {
+  pname = "systemd-shell";
+  version = "0.0.1";
+  src = ./.;
+  doCheck = false;
+}
diff --git a/users/wpcarro/tools/systemd-shell/setup.py b/users/wpcarro/tools/systemd-shell/setup.py
new file mode 100644
index 0000000000..f45e058e67
--- /dev/null
+++ b/users/wpcarro/tools/systemd-shell/setup.py
@@ -0,0 +1,9 @@
+from setuptools import setup
+
+setup(
+    name="systemd-shell",
+    version="0.0.1",
+    author="William Carroll",
+    author_email="wpcarro@gmail.com",
+    scripts=["systemd-shell"],
+)
diff --git a/users/wpcarro/tools/systemd-shell/systemd-shell b/users/wpcarro/tools/systemd-shell/systemd-shell
new file mode 100644
index 0000000000..646d59143a
--- /dev/null
+++ b/users/wpcarro/tools/systemd-shell/systemd-shell
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+
+# Drop into a new shell environment with the same variables defined in a systemd
+# unit file (for debugging purposes).
+#
+# USAGE:
+#   $ systemd-shell -u buildkite-agent-foundation-1.service
+
+import argparse
+import os
+
+def parse_env(entry):
+    x = entry[12:].split("=", 1)
+    return x[0].removeprefix("\"").removesuffix("\""), x[1].removeprefix("\"").removesuffix("\"")
+
+def run(unit):
+  envfile = []
+  print("--- Environment ---")
+  for line in open(f"/etc/systemd/system/{unit}").readlines():
+      if line.startswith("Environment="):
+          key, val = parse_env(line[:-1])
+          print(f"export {key}={val}")
+          envfile.append(f"{key}={val}")
+      else:
+          continue
+  print()
+
+  env = " ".join(envfile)
+  print("--- Command ---")
+  os.system(f"/usr/bin/env {env} /bin/sh")
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-u", "--unit", type=str, required=True)
+    args = parser.parse_args()
+    run(args.unit)
diff --git a/users/wpcarro/tools/url-blocker/default.nix b/users/wpcarro/tools/url-blocker/default.nix
index 943644e5f5..ae24aa41b7 100644
--- a/users/wpcarro/tools/url-blocker/default.nix
+++ b/users/wpcarro/tools/url-blocker/default.nix
@@ -12,11 +12,11 @@ let
     name = "url-blocker";
     src = builtins.path { path = ./.; name = "url-blocker"; };
     buildPhase = ''
-    ${ghc}/bin/ghc Main.hs
-  '';
+      ${ghc}/bin/ghc Main.hs
+    '';
     installPhase = ''
-    mv ./Main $out
-  '';
+      mv ./Main $out
+    '';
   };
 
   # This is the systemd timer unit.
@@ -26,8 +26,9 @@ let
     systemd = {
       timers.simple-timer = {
         wantedBy = [ "timers.target" ];
-        partOf = [];
+        partOf = [ ];
       };
     };
   };
-in null
+in
+null
diff --git a/users/wpcarro/tools/wpcarro-deps.nix b/users/wpcarro/tools/wpcarro-deps.nix
new file mode 100644
index 0000000000..5eafd0bfda
--- /dev/null
+++ b/users/wpcarro/tools/wpcarro-deps.nix
@@ -0,0 +1,8 @@
+# Shell derivation to invoke //nix/lazy-deps with the dependencies that should
+# be lazily made available in wpcarro's users dir in depot.
+{ pkgs, depot, ... }:
+
+depot.nix.lazy-deps {
+  import-gpg.attr = "users.wpcarro.configs.import-gpg";
+  export-gpg.attr = "users.wpcarro.configs.export-gpg";
+}
diff --git a/users/wpcarro/utils/builder.nix b/users/wpcarro/utils/builder.nix
index 45e783cf0b..2bc061d366 100644
--- a/users/wpcarro/utils/builder.nix
+++ b/users/wpcarro/utils/builder.nix
@@ -2,7 +2,8 @@
 
 let
   inherit (pkgs) writeShellScriptBin;
-in {
+in
+{
   # Create a derivation that creates an executable shell script named `as` that
   # calls the program located at `path`, forwarding all of the arguments.
   wrapNonNixProgram = { path, as }: writeShellScriptBin as ''
diff --git a/users/wpcarro/utils/default.nix b/users/wpcarro/utils/default.nix
index 59aa322076..46d30acfa2 100644
--- a/users/wpcarro/utils/default.nix
+++ b/users/wpcarro/utils/default.nix
@@ -8,7 +8,8 @@ args@{ pkgs, ... }:
 
 let
   builder = import ./builder.nix args;
-  fs      = import ./fs.nix args;
-in {
+  fs = import ./fs.nix args;
+in
+{
   inherit builder fs;
 }
diff --git a/users/wpcarro/utils/fs.nix b/users/wpcarro/utils/fs.nix
index 6305e705b1..d7d5e34e99 100644
--- a/users/wpcarro/utils/fs.nix
+++ b/users/wpcarro/utils/fs.nix
@@ -5,7 +5,8 @@
 let
   inherit (builtins) attrNames hasAttr map readDir;
   inherit (pkgs.lib) filterAttrs;
-in {
+in
+{
   # Returns a list of all of the regular files in `dir`.
   files = dir:
     map (name: dir + "/${name}")
@@ -31,11 +32,11 @@ in {
     if hasAttr name (readDir dir) then
       dir + "/${name}"
     else
-      # This prevents the function from infinitely recursing and eventually
-      # stack overflowing.
+    # This prevents the function from infinitely recursing and eventually
+    # stack overflowing.
       if (dirOf dir) == dir then
         null
       else
         resolve name (dirOf dir);
-  };
+};
 }
diff --git a/users/wpcarro/website/blog/default.nix b/users/wpcarro/website/blog/default.nix
index 4ccf200223..27541b0f39 100644
--- a/users/wpcarro/website/blog/default.nix
+++ b/users/wpcarro/website/blog/default.nix
@@ -11,13 +11,14 @@ let
   config = {
     name = "bill and his blog";
     baseUrl = "https://${domain}/blog";
+    staticUrl = "https://static.tvl.fyi/latest";
     footer = "";
   };
 
   posts = sort (x: y: x.date > y.date)
     (filter includePost (list post (import ./posts.nix)));
 
-  rendered = pkgs.runCommandNoCC "blog-posts" {} ''
+  rendered = pkgs.runCommand "blog-posts" { } ''
     mkdir -p $out
 
     ${lib.concatStringsSep "\n" (map (post:
@@ -25,7 +26,7 @@ let
     ) posts)}
   '';
 
-  formatDate = date: readFile (pkgs.runCommandNoCC "date" {} ''
+  formatDate = date: readFile (pkgs.runCommand "date" { } ''
     date --date='@${toString date}' '+%B %e, %Y' > $out
   '');
 
@@ -38,7 +39,8 @@ let
     postTitle = post.title;
     postDate = formatDate post.date;
   });
-in pkgs.runCommandNoCC "blog" {} ''
+in
+pkgs.runCommand "blog" { } ''
   mkdir -p $out
   cp ${withBrand (readFile postsHtml)} $out/index.html
   cp -r ${rendered} $out/posts
diff --git a/users/wpcarro/website/blog/fragments/post.html b/users/wpcarro/website/blog/fragments/post.html
index 44593094ec..2741292aa9 100644
--- a/users/wpcarro/website/blog/fragments/post.html
+++ b/users/wpcarro/website/blog/fragments/post.html
@@ -1,5 +1,5 @@
-<li class="pb-6 md:pb-10">
-  <h2 class="text-bold text-xl">
+<li class="pb-6 md:pb-10 text-md md:text-xl">
+  <h2 class="text-bold">
     <a class="font-bold text-blue-600 hover:underline" href="@postUrl@">
       @postTitle@
     </a>
diff --git a/users/wpcarro/website/blog/posts.nix b/users/wpcarro/website/blog/posts.nix
index 3ed1671982..31fb0c83d8 100644
--- a/users/wpcarro/website/blog/posts.nix
+++ b/users/wpcarro/website/blog/posts.nix
@@ -22,4 +22,95 @@
     content = ./posts/send-mail-as-2fa.md;
     draft = false;
   }
+  {
+    key = "auto-reboot-nixos";
+    title = "Automatically Reboot NixOS";
+    date = 1643666914;
+    content = ./posts/auto-reboot-nixos.md;
+    draft = false;
+  }
+  {
+    key = "csharp-unused-variables";
+    title = "Unused Variables Broke Prod";
+    date = 1655840877;
+    content = ./posts/csharp-unused-variables.md;
+    draft = false;
+  }
+  {
+    key = "restic-field-guide";
+    title = "Beginner's Field Guide to restic";
+    date = 1656645093;
+    content = ./posts/restic.md;
+    draft = false;
+  }
+  {
+    key = "tee-time";
+    title = "tee time";
+    date = 1657597870;
+    content = ./posts/tee-time.md;
+    draft = false;
+  }
+  {
+    key = "ssh-oddities";
+    title = "SSH Oddities";
+    date = 1657647994;
+    content = ./posts/ssh-oddities.md;
+    draft = false;
+  }
+  {
+    key = "nix-shell";
+    title = "nix-shell (note to self)";
+    date = 1664902186;
+    content = ./posts/nix-shell-note.md;
+    draft = false;
+  }
+  {
+    key = "git-filter-repo-note";
+    title = "git-filter-repo (note to self)";
+    date = 1665163559;
+    content = ./posts/git-filter-repo-note.md;
+    draft = false;
+  }
+  {
+    key = "nixos-disk-full-note";
+    title = "disk full (note to self)";
+    date = 1666801882;
+    content = ./posts/nixos-disk-full-note.md;
+    draft = false;
+  }
+  {
+    key = "git-rev-refs";
+    title = "git revision numbers as refs (note to self)";
+    date = 1666823030;
+    content = ./posts/git-rev-refs.md;
+    draft = false;
+  }
+  {
+    key = "import-subtree-checklist";
+    title = "Checklist for Importing Subtrees";
+    date = 1666903846;
+    content = ./posts/importing-subtrees.md;
+    draft = false;
+  }
+  {
+    key = "nix-env-note";
+    title = "nix-env (note to self)";
+    date = 1667343279;
+    content = ./posts/nix-env-note.md;
+    draft = false;
+  }
+  {
+    key = "nginx-virtual-host-note";
+    title = "Nginx Virtual Host (note to self)";
+    date = 1668448541;
+    content = ./posts/nginx-curl-note.md;
+    draft = false;
+  }
+  {
+    key = "tcp-tunneling-note";
+    title = "TCP Tunneling (note to self)";
+    date = 1668709613;
+    content = ./posts/tcp-tunneling-note.md;
+    draft = false;
+  }
 ]
diff --git a/users/wpcarro/website/blog/posts/auto-reboot-nixos.md b/users/wpcarro/website/blog/posts/auto-reboot-nixos.md
new file mode 100644
index 0000000000..24474e6dfe
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/auto-reboot-nixos.md
@@ -0,0 +1,40 @@
+## Show me the codes
+
+Regularly rebooting machines can be a useful, hygienic practice, but quite
+frankly I cannot be relied on to remember to regularly reboot my machine.
+
+Let's free-up some wetware-RAM by automating this with Nix. The following
+addition to your `configuration.nix` will schedule daily reboots at `03:00`:
+
+```nix
+systemd.timers.auto-reboot = {
+  wantedBy = [ "timers.target" ];
+  timerConfig = {
+    OnCalendar = "*-*-* 03:00:00";
+    Unit = "reboot.target";
+  };
+};
+```
+
+If you want to fiddle with the date format, `systemd-analyze` is your friend:
+
+```shell
+Ξ» systemd-analyze calendar '*-*-* 03:00:00'
+Normalized form: *-*-* 03:00:00
+    Next elapse: Tue 2022-02-01 03:00:00 PST
+       (in UTC): Tue 2022-02-01 11:00:00 UTC
+       From now: 12h left
+```
+
+After calling `nixos-rebuild switch`, you can verify that `systemd` started the
+timer with:
+
+```shell
+Ξ» systemctl list-timers auto-reboot
+#  output omitted because I'm writing this from a different machine
+```
+
+## That's all, folks!
+
+I wanted to keep this post short-and-sweet, to build the habit of posting more
+regularly. Hopefully someone out there found this useful.
diff --git a/users/wpcarro/website/blog/posts/cell-phone-experiment.md b/users/wpcarro/website/blog/posts/cell-phone-experiment.md
index c289954a58..f781a60873 100644
--- a/users/wpcarro/website/blog/posts/cell-phone-experiment.md
+++ b/users/wpcarro/website/blog/posts/cell-phone-experiment.md
@@ -5,16 +5,16 @@ on it.
 
 ### Explore/Exploit
 
-Ever since I read Charles Duhigg's book, [The Power of Habit](poh), I try to
+Ever since I read Charles Duhigg's book, [The Power of Habit][poh], I try to
 habituate as many aspects of my life as I can.
 
 Making my bed every morning is an example of a habit -- so too is flossing at
 night before bed.
 
-The *exploit* axis of the [explore/exploit tradeoff](exp-exp) endows habits with
+The *exploit* axis of the [explore/exploit tradeoff][exp-exp] endows habits with
 their power. Brian Christian and Tom Griffiths explain this concept more clearly
 than I can in Chapter 2 of their exceptional book, [Algorithms to Live
-By](algos).
+By][algos].
 
 Habits are powerful, but if I overly exploit an activity, I may settle on a
 local optimum in lieu of settling on a global optimum; these are the opportunity
diff --git a/users/wpcarro/website/blog/posts/csharp-unused-variables.md b/users/wpcarro/website/blog/posts/csharp-unused-variables.md
new file mode 100644
index 0000000000..a5b62647bc
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/csharp-unused-variables.md
@@ -0,0 +1,38 @@
+**Problem**: This morning we broke production because (believe it or not) an
+unused variable went undetected.
+
+**Solution**: Consume the variable in the relevant codepath.
+
+**Further AI**: Treat unused variables as errors (which will block CI).
+
+## Warning/Disclaimer
+
+I am not a C# programmer. I know close to nothing about C#. But at `$WORK`, one
+of our codebases is written in C#, so occasionally I interface with it.
+
+## Treating Unused Variables as Errors
+
+C# uses `.csproj` files to configure projects. The following changes to our
+`.csproj` file WAI'd:
+
+```diff
++    <!-- IDE0059: Remove unnecessary value assignment -->
++    <WarningsAsErrors>IDE0059</WarningsAsErrors>
++    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
+```
+
+However, supporting this turned out to be a ~1h adventure... Why was this
+unexpectedly difficult? As it turns out, there are the 3x promising compiler
+warnings that I had to discover/try:
+
+- `CS0219`: doesn't WAI (see "Note" here: https://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0219)
+- `CA1804`: silently unsupported (replaced by `IDE0059`)
+- `IDE0059`: WAIs
+
+Legend:
+- `CS`: stands for C#
+- `CA`: stands for Code Analysis (I *think* a Visual Studio concept)
+- `IDE`: stands for IDE (I think *also* a Visual Studio concept)
+
+For `CA` and `IDE` prefixed warnings, `EnforceCodeStyleInBuild` must also be
+enabled.
diff --git a/users/wpcarro/website/blog/posts/git-filter-repo-note.md b/users/wpcarro/website/blog/posts/git-filter-repo-note.md
new file mode 100644
index 0000000000..e5fbb05f5c
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/git-filter-repo-note.md
@@ -0,0 +1,59 @@
+## Background
+
+- I recently used `git-filter-repo` to scrub cleartext secrets from a
+  repository.
+- We pin some services' deployments to commit SHAs.
+- These commit SHAs are no longer reachable from `origin/main`.
+
+## Problem
+
+If `git` garbage-collects any of the commits to which services are pinned, and
+that service attempts to redeploy, the deployment will fail.
+
+`git for-each-ref --contains $SHA` will report all of the refs that can reach
+some commit, `$SHA`. This may report things like:
+- `refs/replace` (i.e. `git-filter-repo` artifacts)
+- `refs/stash`
+- some local branches
+- some remote branches
+
+One solution might involve creating references to avoid garbage-collection. But
+if any of our pinned commits contains sensitive cleartext we *want* to ensure
+that `git` purges these.
+
+Instead let's find the SHAs of the new, rewritten commits and replace the pinned
+versions with those.
+
+## Solution
+
+Essentially we want to find a commit with the same *tree* state as the currently
+pinned commit. Here are two ways to get that info...
+
+This way is indirect, but provides more context about the change:
+
+```shell
+Ξ» git cat-file -p $SHA
+tree d011a1dd4a3c5c4c6455ab3592fa2bf71d551d22 # <-- copy this tree info
+parent ba88bbf8de61be932184631244d2ec0ec8205cb8
+author William Carroll <wpcarro@gmail.com> 1664993052 -0700
+committer William Carroll <wpcarro@gmail.com> 1665116042 -0700
+
+feat(florp): Florp can now flarp
+
+You're welcome :)
+```
+
+This way is more direct (read: code-golf-friendly):
+
+```shell
+Ξ» git log -1 --format=%T $SHA
+```
+
+Now that we have the SHA of the desired tree state, let's use it to query `git`
+for commits with the same tree SHA.
+
+```shell
+Ξ» git log --format='%H %T' | grep $(git log --format=%T -1 $SHA) | awk '{ print $1 }'
+```
+
+Hopefully this helps!
diff --git a/users/wpcarro/website/blog/posts/git-rev-refs.md b/users/wpcarro/website/blog/posts/git-rev-refs.md
new file mode 100644
index 0000000000..fdc0aaf5cc
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/git-rev-refs.md
@@ -0,0 +1,85 @@
+## Credit
+
+Credit goes to `tazjin@` for this idea :)
+
+## Background
+
+Using `git` revisions to pin versions is nice, but git SHAs aren't very
+human-friendly:
+
+- They're difficult to type.
+- They're difficult to say in conversation.
+- They're difficult to compare. e.g. Which is newer? `2911fcd` or `db6ac90`?
+
+## Solution
+
+Let's assign monotonically increasing natural numbers to each of
+our repo's mainline commits and create `git` refs so we can use references like
+`r/123` instead of `2911fcd`.
+
+- They're easy to type: `r/123`
+- They're easy to say in conversion: "Check-out rev one-twenty-three."
+- They're easy to compare: `r/123` is an earlier version than `r/147`.
+
+## Backfilling
+
+Let's start-off by assigning "revision numbers" as refs for each of the mainline
+commits:
+
+```shell
+for commit in $(git rev-list --first-parent HEAD); do
+  git update-ref "refs/r/$(git rev-list --count --first-parent $commit)" $commit
+done
+```
+
+We can verify with:
+
+```shell
+Ξ» git log --first-parent --oneline
+```
+
+If everything looks good, we can publish the refs to the remote:
+
+```shell
+Ξ» git push origin 'refs/r/*:refs/r/*'
+```
+
+## Staying Current
+
+In order to make sure that any newly merged commits have an associated revision
+number as a ref, add something like the following to your CI system to run on
+the builds of your repo's mainline branch:
+
+```shell
+Ξ» git push origin "HEAD:refs/r/$(git rev-list --count --first-parent HEAD)"
+```
+
+## Summary
+
+To verify that the remote now has the expected refs, we can use:
+
+```shell
+Ξ» git ls-remote origin | less # grep for refs/r
+```
+
+If that looks good, you should now be able to *manually* fetch the refs with:
+
+```shell
+Ξ» git fetch origin 'refs/r/*:refs/r/*'
+```
+
+Or you can use `git config` to automate this:
+
+```shell
+Ξ» git config --add remote.origin.fetch '+refs/r/*:refs/r/*'
+Ξ» git fetch origin
+```
+
+Now you can run fun commands like:
+
+```shell
+Ξ» git show r/1234
+Ξ» git diff r/123{4,8} # see changes from 1234 -> 1238
+```
+
+Thanks for reading!
diff --git a/users/wpcarro/website/blog/posts/importing-subtrees.md b/users/wpcarro/website/blog/posts/importing-subtrees.md
new file mode 100644
index 0000000000..e1070fc3b9
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/importing-subtrees.md
@@ -0,0 +1,147 @@
+## Background
+
+Sometimes you need to merge one Git repo into another. This is a common task
+when administrating a monorepo.
+
+Here's a checklist that I follow:
+
+1. Detect leaked secrets.
+1. Rotate leaked secrets.
+1. Purge leaked secrets from repo history.
+1. Create mainline references to branches (for deployments).
+1. Subtree-merge into the target repo.
+1. Format the code.
+1. Celebrate!
+
+## Secrets
+
+**Note:** If you notice any leaked secrets, first and foremost rotate them
+before moving on...
+
+`gitleaks` supports `gitleaks protect`, but that doesn't seem to work for `WRN`
+level leaks, which in my experience often contain sensitive cleartext. We can
+use `git-filter-repo` to purge the cleartext from our repo history.
+
+Let's make a `secrets.txt` file that we can feed `git-filter-repo`:
+
+```shell
+Ξ» gitleaks detect -r /tmp/secrets.json
+Ξ» jq -r 'map_values(.Secret) | .[]' /tmp/secrets.txt
+```
+
+Now for the redacting...
+
+```shell
+Ξ» git-filter-repo --force --replace-text /tmp/secrets.txt
+```
+
+Verify that the secrets were removed.
+
+```shell
+Ξ» rg --hidden '\*\*\*REMOVED\*\*\*'
+Ξ» gitleaks detect -v
+```
+
+Looks good! Let's move on to support the adopted repo's deploy strategy.
+
+## Supporting Deploys
+
+While deploying services when someone pushes to a given branch is a common
+deployment strategy, branch-based deployment don't make a whole lot of sense in
+a monorepo.
+
+When adopting another repo, you'll typically encounter a Github Action
+configuration that contains a section like this:
+
+```yaml
+on:
+  push:
+    - staging
+    - production
+```
+
+In our monorepo, `staging` and `production` don't exist. And I don't think we
+want to support them either. `staging` and `production` are ambiguous in a
+monorepo that hosts multiple services each of which likely having its own notion
+of `staging` and `production`.
+
+Doing "pinned releases" where a service is deployed from a `git` revision from
+the mainline branch works well in these scenarios. In order to support this we
+need to make sure the adopted repo has references to
+
+`git subtree add` asks us to define which branch it should use when grafting the
+repository onto our monorepo. We'll use `main` (or whatever the mainline branch
+is).
+
+In order to support the *current* deployments while migrating to a pinned
+release strategy, we have to ensure that `main` has a commit containing the same
+tree state as `staging` *and* another commit containing the same tree state as
+`production`. Let's do that!
+
+```shell
+Ξ» git checkout main # ensure you're on the main branch
+Ξ» git diff main staging >/tmp/main-to-staging.patch
+Ξ» git diff main production >/tmp/main-to-production.patch
+```
+
+### staging
+
+```shell
+Ξ» git apply /tmp/main-to-staging.patch
+Ξ» git add . && git commit # chore: main -> staging
+Ξ» git revert HEAD
+Ξ» git commit --amend # revert: staging -> main
+```
+
+### production
+
+```shell
+Ξ» git apply /tmp/main-to-production.patch
+Ξ» git add . && git commit # chore: main -> production
+Ξ» git revert HEAD
+Ξ» git commit --amend # revert: production -> main
+```
+
+Now let's check our work:
+
+```shell
+Ξ» git log --oneline
+38f4422 revert: production -> main
+f071a9f chore: main -> production
+02ea731 revert: staging -> main
+308ed90 chore: main -> staging
+```
+
+When we go to support pinned releases we can do something like so:
+
+```json
+{
+  "staging": "308ed90",
+  "production": "f071a9f"
+}
+```
+
+## Subtree Merge
+
+Now the repo is ready to be merged.
+
+```shell
+Ξ» git subtree add --prefix=foo/bar/baz path/to/baz main
+Ξ» git commit --amend # subtree: Dock baz into monorepo!
+```
+
+## Formatting
+
+Some CI enforces code formatting standards, so you may need to run that:
+
+```shell
+Ξ» repofmt
+Ξ» git add . && git commit # chore(fmt): Format the codes
+```
+
+Lastly, if you need the latest monorepo code from `origin/main` before opening a
+pull request, the following should work:
+
+```shell
+Ξ» git fetch origin main && git rebase origin/main --rebase-merges --strategy=subtree
+```
diff --git a/users/wpcarro/website/blog/posts/nginx-curl-note.md b/users/wpcarro/website/blog/posts/nginx-curl-note.md
new file mode 100644
index 0000000000..e2f4341f54
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/nginx-curl-note.md
@@ -0,0 +1,5 @@
+Use the following to make requests to Nginx virtual hosts from the host itself:
+
+```shell
+$ curl -H 'Host: trace.website.internal' localhost:8000
+```
diff --git a/users/wpcarro/website/blog/posts/nix-env-note.md b/users/wpcarro/website/blog/posts/nix-env-note.md
new file mode 100644
index 0000000000..8683c52e8f
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/nix-env-note.md
@@ -0,0 +1,33 @@
+## Background
+
+Much in the same vain as my [nix-shell (note to self)][nix-shell-note], I'm
+going to leave a note to my future self on how to install packages using
+`nix-env`, which is something I do once in a blue moon.
+
+## Solution
+
+```shell
+Ξ» nix-env -iA tvix.eval -f /depot
+```
+
+Looks like I was forgetting the `-f /depot` option all this time:
+
+> --file / -f path
+>     Specifies the Nix expression (designated below as the active Nix
+>     expression) used by the --install, --upgrade, and --query --available
+>     operations to obtain derivations. The default is ~/.nix-defexpr.
+> - `man nix-env`
+
+## Failed Attempts (don't try these at home)
+
+This section is brought to you by my shell's `Ctrl-r`!
+
+```shell
+Ξ» nix-env -I depot=/depot -iA depot.tvix.eval
+Ξ» NIX_PATH=depot=/depot nix-env -iA depot.tvix.eval
+Ξ» nix-env -iE '(import /depot {}).tvix.eval'
+```
+
+Thanks for reading!
+
+[nix-shell-note]: https://billandhiscomputer.com/blog/posts/nix-shell.html
diff --git a/users/wpcarro/website/blog/posts/nix-shell-note.md b/users/wpcarro/website/blog/posts/nix-shell-note.md
new file mode 100644
index 0000000000..da33c846ce
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/nix-shell-note.md
@@ -0,0 +1,50 @@
+## Background
+
+I rarely use `nix-shell` for its originally intended purpose of "reproducing the
+environment of a derivation for development". Instead, I often use it to put
+some executable on my `PATH` for some ad hoc task.
+
+What's `nix-shell`'s "intended purpose"? Let's ask The Man (`man nix-shell`):
+
+> The command nix-shell will build the dependencies of the specified derivation,
+> but not the derivation itself. It will then start an interactive shell in
+> which all environment variables defined by the derivation path have been set
+> to their corresponding values, and the script $stdenv/setup has been
+> sourced. This is useful for reproducing the environment of a derivation for
+> development.
+
+Because I'm abusing `nix-shell` in this way, I'm liable to forget that
+`nix-shell` puts `buildInputs` on `PATH` and *not* the derivation itself. But I
+often only want the derivation!
+
+## Solution
+
+Pass the Nix expression to `nix-shell -p`:
+
+```shell
+Ξ» nix-shell -p '(import /depot {}).tvix.eval'
+```
+
+## Explanation
+
+This works because Nix forwards the arguments passed to `-p` (i.e. `--packages`)
+and interpolates them into this expression here: [source][nix-src]
+
+```nix
+{ ... }@args:
+
+with import <nixpkgs> args;
+
+(pkgs.runCommandCC or pkgs.runCommand) "shell" {
+  buildInputs = [
+    # --packages go here
+  ];
+}
+```
+
+So really you can pass-in *any* valid Nix expression that produces a derivation
+and `nix-shell` will put its outputs on your `PATH`.
+
+Enjoy!
+
+[nix-src]: https://sourcegraph.com/github.com/NixOS/nix@3ae9467d57188f9db41f85b0e5c41c0c9d141955/-/blob/src/nix-build/nix-build.cc?L266
diff --git a/users/wpcarro/website/blog/posts/nixos-disk-full-note.md b/users/wpcarro/website/blog/posts/nixos-disk-full-note.md
new file mode 100644
index 0000000000..4bbd3f58e2
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/nixos-disk-full-note.md
@@ -0,0 +1,113 @@
+## Background
+
+Every now and then NixOS hosts runs out of disk space. This happened to my IRC
+server recently...
+
+> No problem. Let's free-up some space with Nix's garbage-collection:
+> - me
+
+```shell
+Ξ» nix-collect-garbage -d # failed due lack of disk space
+```
+
+Ironically Nix needs to do an SQLite transaction before deleting stuff and
+SQLite can't do that if there's no space. This is especially funny because the
+SQLite is probably a `DELETE`.
+
+## Solution
+
+First let's verify that our disk is indeed at capacity:
+
+```shell
+Ξ» df -h
+Filesystem                Size  Used Avail Use% Mounted on
+devtmpfs                  399M     0  399M   0% /dev
+tmpfs                     3.9G     0  3.9G   0% /dev/shm
+tmpfs                     2.0G  3.7M  2.0G   1% /run
+tmpfs                     3.9G  408K  3.9G   1% /run/wrappers
+/dev/disk/by-label/nixos  9.9G  9.9G    0G 100% /
+tmpfs                     4.0M     0  4.0M   0% /sys/fs/cgroup
+tmpfs                     797M     0  797M   0% /run/user/0
+```
+
+Looks like `/dev/disk/by-label/nixos` is at `100%`. Now let's find some easy
+targets to free-up space so that we can run `nix-collect-garbage -d`...
+
+```shell
+Ξ» du -hs /* 2>/dev/null
+8.0K    /bin
+12M     /boot
+0       /dev
+200K    /etc
+68K     /home
+16K     /lost+found
+9.0G    /nix
+0       /proc
+1.2M    /root
+2.9M    /run
+4.0K    /srv
+0       /sys
+44K     /tmp
+12K     /usr
+1.2G    /var
+```
+
+Okay: `/var` looks like an easy candidate. Let's recurse into that directory:
+
+```shell
+Ξ» du -hs /var/*
+40K     /var/cache
+12K     /var/db
+4.0K    /var/empty
+4.0K    /var/google-users.d
+211M    /var/lib
+0       /var/lock
+918M    /var/log
+0       /var/run
+4.0K    /var/spool
+44K     /var/tmp
+Ξ» du -hs /var/log/* # /var/log looks promising
+60M     /var/log/btmp
+82M     /var/log/btmp.1
+776M    /var/log/journal # ah-ha! journald. Let's clean-up some logs
+8.0K    /var/log/lastlog
+1.1M    /var/log/nginx
+4.0K    /var/log/private
+12K     /var/log/wtmp
+```
+
+To retain at most 1w's worth of logs:
+
+```shell
+Ξ» journalctl --vacuum-time=1w
+```
+
+...or if you'd prefer to retain only 100M's worth of logs:
+
+```shell
+Ξ» journalctl --vacuum-size=100M
+```
+
+Now Nix should be able to garbage-collect!
+
+```shell
+Ξ» nix-collect-garbage -d
+```
+
+And lastly verify that it WAI'd:
+
+```
+Ξ» df -h
+Filesystem                Size  Used Avail Use% Mounted on
+devtmpfs                  399M     0  399M   0% /dev
+tmpfs                     3.9G     0  3.9G   0% /dev/shm
+tmpfs                     2.0G  3.7M  2.0G   1% /run
+tmpfs                     3.9G  408K  3.9G   1% /run/wrappers
+/dev/disk/by-label/nixos  9.9G  5.1G  4.3G  55% /
+tmpfs                     4.0M     0  4.0M   0% /sys/fs/cgroup
+tmpfs                     797M     0  797M   0% /run/user/0
+```
+
+## Closing Thoughts
+
+Why doesn't Nix just reserve enough space to be able to GC itself? Not sure...
diff --git a/users/wpcarro/website/blog/posts/restic.md b/users/wpcarro/website/blog/posts/restic.md
new file mode 100644
index 0000000000..4af1fab368
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/restic.md
@@ -0,0 +1,91 @@
+Continuing along the trend that [Profpatsch][2] recently inspired in me: writing
+short notes to myself instead of fully fledged blog posts aimed at some
+unknowable audience. Today we're looking at how I burned myself by only
+*partially* RTFD.
+
+## Background
+
+I recently started using [restic][4] and NixOS thanks to the help of [TVL's
+`restic.nix` module][1]. I setup `1x/h` backups to [MinIO][3] (S3-compatible
+storage) for just a handful of `/var/lib` directories (`~9GiB` total), but after
+a few days MinIO reported that my bucket size was `O(100GiB)`!
+
+> What's going on?
+> -- me
+
+```shell
+$ restic stats
+repository 763bfe37 opened successfully, password is correct
+scanning...
+Stats in restore-size mode:
+Snapshots processed:   175
+   Total File Count:   31369384
+         Total Size:   21.027 GiB
+```
+
+> Wait: 20GiB... wat?
+> -- me (moments later)
+
+Maybe we're snapshotting our MinIO buckets, and that's contributing to our
+bucket size. Checking the logs proved that `restic` was backing-up `1.5GiB/h`,
+which supported MinIO's reports.
+
+> Ah maybe `restic stats` isn't reporting what I *think* it's reporting...
+> -- me (again)
+
+Let's consult Le Docs:
+
+```shell
+$ restic stats -h
+
+The "stats" command walks one or multiple snapshots in a repository
+and accumulates statistics about the data stored therein. It reports
+on the number of unique files and their sizes, according to one of
+the counting modes as given by the --mode flag.
+
+It operates on all snapshots matching the selection criteria or all
+snapshots if nothing is specified. The special snapshot ID "latest"
+is also supported. Some modes make more sense over
+just a single snapshot, while others are useful across all snapshots,
+depending on what you are trying to calculate.
+
+[to be continued]
+```
+
+This is where I stopped reading (the first time). But then I returned a second
+time as I was running low on theories...
+
+```shell
+[continued]
+
+The modes are:
+
+* restore-size: (default) Counts the size of the restored files.
+* files-by-contents: Counts total size of files, where a file is
+   considered unique if it has unique contents.
+* raw-data: Counts the size of blobs in the repository, regardless of
+  how many files reference them.
+* blobs-per-file: A combination of files-by-contents and raw-data.
+```
+
+Bingo: `--mode=raw-data` **not** `--mode=restore-size`.
+
+## Solution
+
+```shell
+$ restic stats --mode=raw-data
+repository 763bfe37 opened successfully, password is correct
+scanning...
+Stats in raw-data mode:
+Snapshots processed:   175
+   Total Blob Count:   710988
+         Total Size:   303.216 GiB
+```
+
+> Ah... the world agrees again.
+> -- me
+
+[1]: https://cs.tvl.fyi/depot@2ec0d3611960b163a7052e8554ba065f3c89a8cc/-/blob/ops/modules/restic.nix?L9
+[2]: https://github.com/profpatsch
+[3]: https://min.io/
+[4]: https://restic.net/
diff --git a/users/wpcarro/website/blog/posts/ssh-oddities.md b/users/wpcarro/website/blog/posts/ssh-oddities.md
new file mode 100644
index 0000000000..ae0bd5c9f5
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/ssh-oddities.md
@@ -0,0 +1,59 @@
+## Background
+
+I was trying to debug a service over `ssh` that offered password-only
+authentication, but I couldn't seem to get the `ssh` client to prompt me for the
+password.
+
+It looked something like this (skip ahead to the conclusion if you're pressed
+for time):
+
+## Troubleshooting
+
+```shell
+Ξ» ssh root@[redacted]
+Unable to negotiate with [redacted] port 22: no matching host key type found. Their offer: ssh-rsa
+```
+
+But the same command was working just fine for my coworker.
+
+I took a closer look with `ssh -v root@[redacted]`, but nothing jumped-out at
+me. Maybe it's something with *my* `ssh` configuration; let's remove that
+variable:
+
+```shell
+Ξ» ssh -F /dev/null root@[redacted]
+Unable to negotiate with [redacted] port 22: no matching host key type found. Their offer: ssh-rsa
+```
+
+> Ah it looks like there's a way to set my preferred authentication method...
+> -- me
+
+```shell
+Ξ» ssh -F /dev/null -o PreferredAuthentications=password root@[redacted]
+Unable to negotiate with [redacted] port 22: no matching host key type found. Their offer: ssh-rsa
+```
+
+## Conclusion
+
+Well it turns-out that newer SSH clients disable the `ssh-rsa` public key
+signature algorithm because it depends on SHA-1, which is considered insecure.
+
+```shell
+Ξ» ssh -V
+OpenSSH_9.0p1, OpenSSL 1.1.1p  21 Jun 2022
+```
+
+...and according to the `ssh -v` output, the server is running a pre-COVID(!!!)
+version of `ssh`:
+
+```
+debug1: Remote protocol version 2.0, remote software version dropbear_2018.76
+```
+
+So if you don't have time to upgrade the SSH server, and you just want to
+connect, the following should work because we're *opting-into* the less secure
+option:
+
+```shell
+Ξ» ssh -o HostKeyAlgorithms=+ssh-rsa root@[redacted]
+```
diff --git a/users/wpcarro/website/blog/posts/tcp-tunneling-note.md b/users/wpcarro/website/blog/posts/tcp-tunneling-note.md
new file mode 100644
index 0000000000..06f6469aff
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/tcp-tunneling-note.md
@@ -0,0 +1,63 @@
+## Background
+
+Let's say we'd like to debug a remote machine but use some of the debugging
+tools we have on our local machine like wireshark.
+
+You *can* run `tcpdump` on the remote and then `scp` the file to your local
+machine to analyze the traffic, but after doing that a few times you may want a
+workflow with a tighter feedback loop. For this we'll forward traffic from a
+remote machine to our local machine.
+
+**Note:** There's also `termshark`, which is a `wireshark` TUI that you can run
+on the remote. It's quite cool!
+
+## Local
+
+Run the following on your local machine to forward your remote's traffic:
+
+```shell
+Ξ» ssh -R 4317:127.0.0.1:4317 -N -f user@remote
+```
+
+Here is an abridged explanation of the flags we're passing from `man ssh`:
+
+```
+-N     Do  not  execute  a remote command.  This is useful for just forwarding ports.
+-f     Requests ssh to go to background just before command execution.
+```
+
+**Note:** I couldn't find a good explanation for the `-R` option, so I tried
+removing it and re-running the command, but that results in a resolution error:
+
+```
+ssh: Could not resolve hostname 4317:127.0.0.1:4317: Name or service not known
+```
+
+The remote should now be forwarding traffic from port `4317` to our
+machine.
+
+## Testing
+
+Let's generate some traffic on the remote:
+
+```shell
+Ξ» telnet localhost 4317
+Trying ::1...
+Connected to localhost.
+Escape character is '^]'.
+hello
+world
+```
+
+Locally you should see:
+
+```shell
+Ξ» nc -l 4317 -k # run this *before* running the above command
+hello
+world
+```
+
+You should now be able to `tcpdump -i lo port 4317` or just use `wireshark`
+locally.
+
+Happy debugging!
diff --git a/users/wpcarro/website/blog/posts/tee-time.md b/users/wpcarro/website/blog/posts/tee-time.md
new file mode 100644
index 0000000000..c8107fcded
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/tee-time.md
@@ -0,0 +1,16 @@
+I encountered this fun TIL while troubleshooting Linux write permissions
+issues...
+
+## TL;DR
+
+Don't do this (unless you want misleading test results):
+
+```shell
+Ξ» sudo -u node-exporter echo 'Hello, world' >/var/lib/textfile-exporter/test.prom
+```
+
+Do this:
+
+```shell
+Ξ» echo 'Hello, world' | sudo -u node-exporter tee /var/lib/textfile-exporter/test.prom
+```
diff --git a/users/wpcarro/website/default.nix b/users/wpcarro/website/default.nix
index 9694aad17d..56f5b02cc8 100644
--- a/users/wpcarro/website/default.nix
+++ b/users/wpcarro/website/default.nix
@@ -8,11 +8,11 @@ let
 
   globalVars = {
     inherit domain;
-    homepage  = "https://${domain}/";
-    blog      = "https://${domain}/blog";
-    habits    = "https://${domain}/habits";
-    github    = "https://github.com/wpcarro";
-    linkedin  = "https://linkedin.com/in/williampatrickcarroll";
+    homepage = "https://${domain}/";
+    blog = "https://${domain}/blog";
+    habits = "https://${domain}/habits";
+    github = "https://github.com/wpcarro";
+    linkedin = "https://linkedin.com/in/williampatrickcarroll";
     depotWork = "https://cs.tvl.fyi/depot/-/blob/users/wpcarro";
   };
 
@@ -23,10 +23,28 @@ let
   withBrand = contentHtml: renderTemplate ./fragments/template.html {
     inherit contentHtml;
   };
-in {
+
+  # Create a simple static file server using nginx to serve `content`.
+  nginxCfgFor = content: pkgs.writeText "nginx.conf" ''
+    user nobody nobody;
+    daemon off;
+    error_log /dev/stdout info;
+    pid /dev/null;
+    events {}
+    http {
+      server {
+        listen 8080;
+        location / {
+          root ${content};
+        }
+      }
+    }
+  '';
+in
+rec {
   inherit domain renderTemplate withBrand;
 
-  root = pkgs.runCommandNoCC "wpcarro.dev" {} ''
+  content = pkgs.runCommand "wpcarro.dev" { } ''
     mkdir -p $out
 
     # /
@@ -39,4 +57,21 @@ in {
     # /blog
     cp -r ${wpcarro.website.blog} $out/blog
   '';
+
+  # Create a docker image suitable for Google Cloud Run (to save costs).
+  image = pkgs.dockerTools.buildLayeredImage {
+    name = "website";
+    tag = "latest";
+    contents = [ pkgs.fakeNss ];
+    extraCommands = ''
+      mkdir -p tmp/nginx_client_body
+      mkdir -p var/log/nginx
+    '';
+    config = {
+      Cmd = [ "${pkgs.nginx}/bin/nginx" "-c" (nginxCfgFor content) ];
+      ExposedPorts = { "8080/tcp" = { }; };
+    };
+  };
+
+  meta.ci.targets = [ "root" "image" ];
 }
diff --git a/users/wpcarro/website/fragments/homepage.html b/users/wpcarro/website/fragments/homepage.html
index f515d97400..79ccd0ded4 100644
--- a/users/wpcarro/website/fragments/homepage.html
+++ b/users/wpcarro/website/fragments/homepage.html
@@ -1,5 +1,5 @@
-<section class="leading-7">
-  <p class="relative text-3xl text-center font-bold pt-6 md:pt-14 pb-10">
+<section class="leading-6 md:leading-7 text-xs md:text-base">
+  <p class="relative text-xl md:text-3xl text-center font-bold pt-6 md:pt-14 pb-10">
     Hey! I'm Bill.<span class="pl-10 relative"><span class="block absolute right-0 top-0 transition-transform hover:rotate-90">πŸ‘‹</span></span>
   </p>
   <p class="pb-4">
diff --git a/users/wpcarro/website/fragments/template.html b/users/wpcarro/website/fragments/template.html
index 241e11a12f..0da4e46d65 100644
--- a/users/wpcarro/website/fragments/template.html
+++ b/users/wpcarro/website/fragments/template.html
@@ -19,9 +19,9 @@
     </script>
   </head>
   <body class="font-mono bg-gray-100">
-    <header class="sticky z-10 transition duration-300 bg-gray-100 top-0 py-6">
+    <header class="sticky z-10 transition duration-300 bg-gray-100 top-0 py-3 md:py-6">
       <div class="flex max-w-sm md:max-w-3xl mx-auto">
-        <div class="flex-1 text-center md:text-left text-xl md:text-base">
+        <div class="flex-1 text-center md:text-left text-sm md:text-base">
           <a href="@depotWork@/website">
             <h1 class="font-bold">
               <span class="text-black">(</span><a class="text-purple-600 hover:underline" href="@depotWork@/website">def</a>&nbsp;<a class="text-green-600 hover:underline text-bold" href="@homepage@">"@domain@"</a><span class="text-black">)</span>
@@ -79,11 +79,6 @@
               LinkedIn <span class="text-blue-300">-></span>
             </a>
           </li>
-          <li class="pb-6">
-            <a class="text-blue-600 font-bold" href="https://www.buymeacoffee.com/billandhisjoe">
-              Caffeinate Bill? <span class="text-blue-300">-></span>
-            </a>
-          </li>
         </ul>
       </footer>
     </div>
diff --git a/users/wpcarro/website/habit-screens/default.nix b/users/wpcarro/website/habit-screens/default.nix
index 345e6f010d..3036ba1821 100644
--- a/users/wpcarro/website/habit-screens/default.nix
+++ b/users/wpcarro/website/habit-screens/default.nix
@@ -8,7 +8,7 @@ let
     , src
     , name
     , srcdir ? "./src"
-    , targets ? []
+    , targets ? [ ]
     , registryDat ? ./registry.dat
     , outputJavaScript ? false
     }:
@@ -24,33 +24,36 @@ let
         inherit registryDat;
       };
 
-      installPhase = let
-        elmfile = module: "${srcdir}/${builtins.replaceStrings ["."] ["/"] module}.elm";
-        extension = if outputJavaScript then "js" else "html";
-      in ''
-        mkdir -p $out/share/doc
-        ${lib.concatStrings (map (module: ''
-          echo "compiling ${elmfile module}"
-          elm make ${elmfile module} --output $out/${module}.${extension} --docs $out/share/doc/${module}.json
-          ${lib.optionalString outputJavaScript ''
-            echo "minifying ${elmfile module}"
-            uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \
-                | uglifyjs --mangle --output $out/${module}.min.${extension}
-          ''}
-        '') targets)}
-      '';
+      installPhase =
+        let
+          elmfile = module: "${srcdir}/${builtins.replaceStrings ["."] ["/"] module}.elm";
+          extension = if outputJavaScript then "js" else "html";
+        in
+        ''
+          mkdir -p $out/share/doc
+          ${lib.concatStrings (map (module: ''
+            echo "compiling ${elmfile module}"
+            elm make ${elmfile module} --output $out/${module}.${extension} --docs $out/share/doc/${module}.json
+            ${lib.optionalString outputJavaScript ''
+              echo "minifying ${elmfile module}"
+              uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \
+                  | uglifyjs --mangle --output $out/${module}.min.${extension}
+            ''}
+          '') targets)}
+        '';
     };
   mainDotElm = mkDerivation {
     name = "elm-app-0.1.0";
     srcs = ./elm-srcs.nix;
     src = ./.;
-    targets = ["Main"];
+    targets = [ "Main" ];
     srcdir = "./src";
     outputJavaScript = true;
   };
-in stdenv.mkDerivation {
+in
+stdenv.mkDerivation {
   name = "habit-screens";
-  buildInputs = [];
+  buildInputs = [ ];
   src = builtins.path { path = ./.; name = "habit-screens"; };
   buildPhase = ''
     mkdir -p $out
diff --git a/users/wpcarro/website/habit-screens/elm-srcs.nix b/users/wpcarro/website/habit-screens/elm-srcs.nix
index 167708e072..7f6f77741a 100644
--- a/users/wpcarro/website/habit-screens/elm-srcs.nix
+++ b/users/wpcarro/website/habit-screens/elm-srcs.nix
@@ -1,77 +1,77 @@
 {
 
-      "elm-community/maybe-extra" = {
-        sha256 = "0qslmgswa625d218djd3p62pnqcrz38f5p558mbjl6kc1ss0kzv3";
-        version = "5.2.0";
-      };
+  "elm-community/maybe-extra" = {
+    sha256 = "0qslmgswa625d218djd3p62pnqcrz38f5p558mbjl6kc1ss0kzv3";
+    version = "5.2.0";
+  };
 
-      "elm/html" = {
-        sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
-        version = "1.0.0";
-      };
+  "elm/html" = {
+    sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
+    version = "1.0.0";
+  };
 
-      "elm-community/random-extra" = {
-        sha256 = "1dg2nz77w2cvp16xazbdsxkkw0xc9ycqpkd032faqdyky6gmz9g6";
-        version = "3.1.0";
-      };
+  "elm-community/random-extra" = {
+    sha256 = "1dg2nz77w2cvp16xazbdsxkkw0xc9ycqpkd032faqdyky6gmz9g6";
+    version = "3.1.0";
+  };
 
-      "elm/svg" = {
-        sha256 = "1cwcj73p61q45wqwgqvrvz3aypjyy3fw732xyxdyj6s256hwkn0k";
-        version = "1.0.1";
-      };
+  "elm/svg" = {
+    sha256 = "1cwcj73p61q45wqwgqvrvz3aypjyy3fw732xyxdyj6s256hwkn0k";
+    version = "1.0.1";
+  };
 
-      "justinmimbs/date" = {
-        sha256 = "1f0wcl8yhlvp3x4rj53rdy4r4ga7lkl6n8fdfh6b96scz2rnxmd4";
-        version = "3.2.1";
-      };
+  "justinmimbs/date" = {
+    sha256 = "1f0wcl8yhlvp3x4rj53rdy4r4ga7lkl6n8fdfh6b96scz2rnxmd4";
+    version = "3.2.1";
+  };
 
-      "elm/browser" = {
-        sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
-        version = "1.0.2";
-      };
+  "elm/browser" = {
+    sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
+    version = "1.0.2";
+  };
 
-      "elm/core" = {
-        sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
-        version = "1.0.5";
-      };
+  "elm/core" = {
+    sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
+    version = "1.0.5";
+  };
 
-      "elm-community/list-extra" = {
-        sha256 = "1ayv3148drynqnxdfwpjxal8vwzgsjqanjg7yxp6lhdcbkxgd3vd";
-        version = "8.2.3";
-      };
+  "elm-community/list-extra" = {
+    sha256 = "1ayv3148drynqnxdfwpjxal8vwzgsjqanjg7yxp6lhdcbkxgd3vd";
+    version = "8.2.3";
+  };
 
-      "elm/random" = {
-        sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
-        version = "1.0.0";
-      };
+  "elm/random" = {
+    sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
+    version = "1.0.0";
+  };
 
-      "elm/time" = {
-        sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
-        version = "1.0.0";
-      };
+  "elm/time" = {
+    sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
+    version = "1.0.0";
+  };
 
-      "elm/json" = {
-        sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
-        version = "1.1.3";
-      };
+  "elm/json" = {
+    sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
+    version = "1.1.3";
+  };
 
-      "elm/parser" = {
-        sha256 = "0a3cxrvbm7mwg9ykynhp7vjid58zsw03r63qxipxp3z09qks7512";
-        version = "1.1.0";
-      };
+  "elm/parser" = {
+    sha256 = "0a3cxrvbm7mwg9ykynhp7vjid58zsw03r63qxipxp3z09qks7512";
+    version = "1.1.0";
+  };
 
-      "owanturist/elm-union-find" = {
-        sha256 = "13gm7msnp0gr1lqia5m7m4lhy3m6kvjg37d304whb3psn88wqhj5";
-        version = "1.0.0";
-      };
+  "owanturist/elm-union-find" = {
+    sha256 = "13gm7msnp0gr1lqia5m7m4lhy3m6kvjg37d304whb3psn88wqhj5";
+    version = "1.0.0";
+  };
 
-      "elm/url" = {
-        sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
-        version = "1.0.0";
-      };
+  "elm/url" = {
+    sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
+    version = "1.0.0";
+  };
 
-      "elm/virtual-dom" = {
-        sha256 = "0q1v5gi4g336bzz1lgwpn5b1639lrn63d8y6k6pimcyismp2i1yg";
-        version = "1.0.2";
-      };
+  "elm/virtual-dom" = {
+    sha256 = "0q1v5gi4g336bzz1lgwpn5b1639lrn63d8y6k6pimcyismp2i1yg";
+    version = "1.0.2";
+  };
 }
diff --git a/users/wpcarro/website/sandbox/learnpianochords/default.nix b/users/wpcarro/website/sandbox/learnpianochords/default.nix
index 934fbd70ac..7cfdf7c451 100644
--- a/users/wpcarro/website/sandbox/learnpianochords/default.nix
+++ b/users/wpcarro/website/sandbox/learnpianochords/default.nix
@@ -8,7 +8,7 @@ let
     , src
     , name
     , srcdir ? "./src"
-    , targets ? []
+    , targets ? [ ]
     , registryDat ? ./registry.dat
     , outputJavaScript ? false
     }:
@@ -24,33 +24,36 @@ let
         inherit registryDat;
       };
 
-      installPhase = let
-        elmfile = module: "${srcdir}/${builtins.replaceStrings ["."] ["/"] module}.elm";
-        extension = if outputJavaScript then "js" else "html";
-      in ''
-        mkdir -p $out/share/doc
-        ${lib.concatStrings (map (module: ''
-          echo "compiling ${elmfile module}"
-          elm make ${elmfile module} --output $out/${module}.${extension} --docs $out/share/doc/${module}.json
-          ${lib.optionalString outputJavaScript ''
-            echo "minifying ${elmfile module}"
-            uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \
-                | uglifyjs --mangle --output $out/${module}.min.${extension}
-          ''}
-        '') targets)}
-      '';
+      installPhase =
+        let
+          elmfile = module: "${srcdir}/${builtins.replaceStrings ["."] ["/"] module}.elm";
+          extension = if outputJavaScript then "js" else "html";
+        in
+        ''
+          mkdir -p $out/share/doc
+          ${lib.concatStrings (map (module: ''
+            echo "compiling ${elmfile module}"
+            elm make ${elmfile module} --output $out/${module}.${extension} --docs $out/share/doc/${module}.json
+            ${lib.optionalString outputJavaScript ''
+              echo "minifying ${elmfile module}"
+              uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \
+                  | uglifyjs --mangle --output $out/${module}.min.${extension}
+            ''}
+          '') targets)}
+        '';
     };
   mainDotElm = mkDerivation {
     name = "elm-app-0.1.0";
     srcs = ./elm-srcs.nix;
     src = builtins.path { path = ./.; name = "learnpianochords"; };
-    targets = ["Main"];
+    targets = [ "Main" ];
     srcdir = "./src";
     outputJavaScript = true;
   };
-in stdenv.mkDerivation {
+in
+stdenv.mkDerivation {
   name = "learn-piano-chords";
-  buildInputs = [];
+  buildInputs = [ ];
   src = builtins.path { path = ./.; name = "learnpianochords"; };
   buildPhase = ''
     mkdir -p $out
diff --git a/users/wpcarro/website/sandbox/learnpianochords/elm-srcs.nix b/users/wpcarro/website/sandbox/learnpianochords/elm-srcs.nix
index 2823b430f8..c62262e683 100644
--- a/users/wpcarro/website/sandbox/learnpianochords/elm-srcs.nix
+++ b/users/wpcarro/website/sandbox/learnpianochords/elm-srcs.nix
@@ -1,67 +1,67 @@
 {
 
-      "elm-community/maybe-extra" = {
-        sha256 = "0qslmgswa625d218djd3p62pnqcrz38f5p558mbjl6kc1ss0kzv3";
-        version = "5.2.0";
-      };
+  "elm-community/maybe-extra" = {
+    sha256 = "0qslmgswa625d218djd3p62pnqcrz38f5p558mbjl6kc1ss0kzv3";
+    version = "5.2.0";
+  };
 
-      "elm/html" = {
-        sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
-        version = "1.0.0";
-      };
+  "elm/html" = {
+    sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
+    version = "1.0.0";
+  };
 
-      "elm-community/random-extra" = {
-        sha256 = "1dg2nz77w2cvp16xazbdsxkkw0xc9ycqpkd032faqdyky6gmz9g6";
-        version = "3.1.0";
-      };
+  "elm-community/random-extra" = {
+    sha256 = "1dg2nz77w2cvp16xazbdsxkkw0xc9ycqpkd032faqdyky6gmz9g6";
+    version = "3.1.0";
+  };
 
-      "elm/svg" = {
-        sha256 = "1cwcj73p61q45wqwgqvrvz3aypjyy3fw732xyxdyj6s256hwkn0k";
-        version = "1.0.1";
-      };
+  "elm/svg" = {
+    sha256 = "1cwcj73p61q45wqwgqvrvz3aypjyy3fw732xyxdyj6s256hwkn0k";
+    version = "1.0.1";
+  };
 
-      "elm/browser" = {
-        sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
-        version = "1.0.2";
-      };
+  "elm/browser" = {
+    sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
+    version = "1.0.2";
+  };
 
-      "elm/core" = {
-        sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
-        version = "1.0.5";
-      };
+  "elm/core" = {
+    sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
+    version = "1.0.5";
+  };
 
-      "elm-community/list-extra" = {
-        sha256 = "1ayv3148drynqnxdfwpjxal8vwzgsjqanjg7yxp6lhdcbkxgd3vd";
-        version = "8.2.3";
-      };
+  "elm-community/list-extra" = {
+    sha256 = "1ayv3148drynqnxdfwpjxal8vwzgsjqanjg7yxp6lhdcbkxgd3vd";
+    version = "8.2.3";
+  };
 
-      "elm/random" = {
-        sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
-        version = "1.0.0";
-      };
+  "elm/random" = {
+    sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
+    version = "1.0.0";
+  };
 
-      "elm/time" = {
-        sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
-        version = "1.0.0";
-      };
+  "elm/time" = {
+    sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
+    version = "1.0.0";
+  };
 
-      "elm/json" = {
-        sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
-        version = "1.1.3";
-      };
+  "elm/json" = {
+    sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
+    version = "1.1.3";
+  };
 
-      "owanturist/elm-union-find" = {
-        sha256 = "13gm7msnp0gr1lqia5m7m4lhy3m6kvjg37d304whb3psn88wqhj5";
-        version = "1.0.0";
-      };
+  "owanturist/elm-union-find" = {
+    sha256 = "13gm7msnp0gr1lqia5m7m4lhy3m6kvjg37d304whb3psn88wqhj5";
+    version = "1.0.0";
+  };
 
-      "elm/url" = {
-        sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
-        version = "1.0.0";
-      };
+  "elm/url" = {
+    sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
+    version = "1.0.0";
+  };
 
-      "elm/virtual-dom" = {
-        sha256 = "0q1v5gi4g336bzz1lgwpn5b1639lrn63d8y6k6pimcyismp2i1yg";
-        version = "1.0.2";
-      };
+  "elm/virtual-dom" = {
+    sha256 = "0q1v5gi4g336bzz1lgwpn5b1639lrn63d8y6k6pimcyismp2i1yg";
+    version = "1.0.2";
+  };
 }
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/App.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/App.hs
index e23757b015..b7a31457b7 100644
--- a/users/wpcarro/website/sandbox/learnpianochords/src/server/App.hs
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/App.hs
@@ -43,7 +43,7 @@ run = do
   ctx@T.Context{..} <- ask
   ctx
     |> server
-    |> serve (Proxy @ API)
+    |> serve (Proxy @API)
     |> cors (const $ Just corsPolicy)
     |> Warp.run contextServerPort
     |> liftIO
diff --git a/users/wpcarro/ynabsql/dataviz/.gitignore b/users/wpcarro/ynabsql/dataviz/.gitignore
new file mode 100644
index 0000000000..efb13a1549
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/.gitignore
@@ -0,0 +1,5 @@
+/dist
+/node_modules
+/.parcel-cache
+/cdn
+/data.js
\ No newline at end of file
diff --git a/users/wpcarro/ynabsql/dataviz/.parcelrc b/users/wpcarro/ynabsql/dataviz/.parcelrc
new file mode 100644
index 0000000000..5dacc3dd88
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/.parcelrc
@@ -0,0 +1,3 @@
+{
+    "extends": "@parcel/config-default"
+}
\ No newline at end of file
diff --git a/users/wpcarro/ynabsql/dataviz/cdn b/users/wpcarro/ynabsql/dataviz/cdn
new file mode 120000
index 0000000000..9c83dcee43
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/cdn
@@ -0,0 +1 @@
+/tmp/cdn
\ No newline at end of file
diff --git a/users/wpcarro/ynabsql/dataviz/components.jsx b/users/wpcarro/ynabsql/dataviz/components.jsx
new file mode 100644
index 0000000000..0dbfc9fd80
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/components.jsx
@@ -0,0 +1,1256 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import Chart from 'chart.js/auto';
+import 'chartjs-adapter-date-fns';
+
+const colors = {
+    red: 'rgb(255, 45, 70)',
+    green: 'rgb(75, 192, 35)',
+    white: 'rgb(249, 246, 238)',
+    blue: 'rgb(137, 207, 240)',
+    fadedBlue: 'rgb(137, 207, 240, 0.25)',
+    purple: 'rgb(203, 195, 227)',
+    brown: 'rgb(205, 127, 50)',
+    black: 'rgb(53, 57, 53)',
+};
+
+const months = [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December',
+];
+
+function getWeek(x) {
+    const dowOffset = 0;
+    const newYear = new Dte(x.getFullYear(), 0, 1);
+    let day = newYear.getDay() - dowOffset; //the day of week the year begins on
+    day = (day >= 0 ? day : day + 7);
+    const daynum = Math.floor((x.getTime() - newYear.getTime() -
+        (x.getTimezoneOffset() - newYear.getTimezoneOffset()) * 60000) / 86400000) + 1;
+    let weeknum;
+
+    //if the year starts before the middle of a week
+    if (day < 4) {
+        weeknum = Math.floor((daynum + day - 1) / 7) + 1;
+        if (weeknum > 52) {
+            const nYear = new Date(x.getFullYear() + 1, 0, 1);
+            let nday = nYear.getDay() - dowOffset;
+            nday = nday >= 0 ? nday : nday + 7;
+            /*if the next year starts before the middle of
+              the week, it is week #1 of that year*/
+            weeknum = nday < 4 ? 1 : 53;
+        }
+    }
+    else {
+        weeknum = Math.floor((daynum + day - 1) / 7);
+    }
+    return weeknum;
+}
+
+// Convert a sorting expressions (e.g. "Outflow DESC; Date ASC; Category ASC")
+// into a function that can be passed to Array.prototype.sort.
+function compileSort(expr) {
+    return expr.split(/\s*;\s*/).reverse().reduce((acc, x) => {
+        const [k, dir] = x.split(/\s+/);
+        if (dir === 'ASC') {
+            return function(x, y) {
+                if (x[k] > y[k]) { return 1; }
+                if (x[k] < y[k]) { return -1; }
+                else { return acc(x, y); }
+            };
+        }
+        if (dir === 'DESC') {
+            return function(x, y) {
+                if (x[k] > y[k]) { return -1; }
+                if (x[k] < y[k]) { return 1; }
+                else { return acc(x, y); }
+            };
+        }
+        else {
+            throw new Error(`Sort direction not supported, ${dir}, must be either "ASC" or "DESC"`);
+        }
+    }, function(x, y) { return 0; })
+}
+
+function dollars(n, sensitive) {
+    if (sensitive) {
+        const order = magnitude(n);
+        // Shortcut to avoid writing comma-insertion logic v0v.
+        if (n === 0) {
+            return '$X.XX';
+        }
+        if (order <= 0) {
+            return '$X.XX';
+        }
+        if (order === 1) {
+            return '$XX.XX';
+        }
+        if (order === 2) {
+            return '$XXX.XX';
+        }
+        if (order === 3) {
+            return '$X,XXX.XX';
+        }
+        if (order === 4) {
+            return '$XX,XXX.XX';
+        }
+        if (order === 4) {
+            return '$XX,XXX.XX';
+        }
+        if (order === 5) {
+            return '$XXX,XXX.XX';
+        }
+        // Coming soon! :P
+        if (order === 6) {
+            return '$X,XXX,XXX.XX';
+        }
+        if (order === 7) {
+            return '$XX,XXX,XXX.XX';
+        }
+        if (order === 8) {
+            return '$XXX,XXX,XXX.XX';
+        }
+        // Unsupported
+        else {
+            return '$???.??';
+        }
+    }
+    return usd.format(n);
+}
+
+const usd = new Intl.NumberFormat('en-US', {
+    style: 'currency',
+    currency: 'USD',
+});
+
+const queries = {
+    housing: 'Category:/(rent|electric)/',
+    food: 'Category:/(eating|alcohol|grocer)/',
+    commute: 'Category:/(vacation|gasoline|parking|car maintenance)/',
+};
+
+/**
+ * Return the Order of Magnitude of some value, x.
+ */
+function magnitude(x) {
+    return Math.floor(Math.log(x) / Math.LN10 + 0.000000001);
+}
+
+function getSum(transactions) {
+    return transactions.reduce((acc, x) => acc + x.Outflow, 0);
+}
+
+function transactionKey(x) {
+    const keys = [
+        'Account',
+        'Flag',
+        'Date',
+        'Payee',
+        'Category',
+        'Memo',
+        'Outflow',
+        'Inflow',
+        'Cleared',
+    ];
+    return keys.map(k => x[k]).join('|');
+}
+
+function parseCSV(csv) {
+  let lines=csv.split("\n");
+  let result = [];
+
+  // Strip the surrounding 2x-quotes from the header.
+  //
+  // NOTE: If your columns contain commas in their values, you'll need
+  // to deal with those before doing the next step
+  let headers = lines[0].split(",").map(x => x.slice(1, -1));
+
+  for(let i = 1; i < lines.length; i += 1) {
+      let obj = {};
+      let currentline=lines[i].split(",");
+
+      for(let j = 0; j < headers.length; j += 1) {
+        obj[headers[j]] = currentline[j].slice(1, -1);
+      }
+
+      result.push(obj);
+  }
+
+  return result.map(x => ({
+    ...x,
+    Date: new Date(x.Date),
+    Inflow: parseFloat(x.Inflow),
+    Outflow: parseFloat(x.Outflow),
+  }));
+}
+
+
+class UploadJSON extends React.Component {
+    handleUpload(e) {
+        let files = e.target.files;
+        if (!files.length) {
+            alert('No file selected!');
+            return;
+        }
+        let file = files[0];
+        let reader = new FileReader();
+        reader.onload = (event) => {
+            this.props.onUpload(parseCSV(event.target.result));
+        };
+        reader.readAsText(file);
+    }
+    render() {
+        return <input onChange={e => this.handleUpload(e)} id="json-upload" type="file" accept="application/csv" />;
+    }
+}
+
+class ScatterChart extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+    componentDidUpdate(prevProps) {
+        if (this.props.transactions !== prevProps.transactions) {
+            this.chart.data.datasets[0].data = this.props.transactions.filter(x => x.Inflow > 0).map(x => ({
+                x: x.Date,
+                y: x.Inflow,
+                metadata: x,
+            }));
+            this.chart.data.datasets[1].data = this.props.transactions.filter(x => x.Outflow > 0).map(x => ({
+                x: x.Date,
+                y: x.Outflow,
+                metadata: x,
+            }));
+            this.chart.update();
+        }
+    }
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        this.chart = new Chart(mount, {
+            type: 'scatter',
+            data: {
+                datasets: [
+                    {
+                        label: 'Revenue',
+                        data: this.props.transactions.filter(x => x.Inflow > 0).map(x => ({
+                            x: x.Date,
+                            y: x.Inflow,
+                            metadata: x,
+                        })),
+                        backgroundColor: colors.green,
+                    },
+                    {
+                        label: 'Expenses',
+                        data: this.props.transactions.filter(x => x.Outflow).map(x => ({
+                            x: x.Date,
+                            y: x.Outflow,
+                            metadata: x,
+                        })),
+                        backgroundColor: colors.red,
+                    },
+                ],
+            },
+            options: {
+                scales: {
+                    x: {
+                        type: 'time',
+                        title: {
+                            display: true,
+                            text: 'Date',
+                        },
+                    },
+                    y: {
+                        title: {
+                            display: true,
+                            text: 'Amount ($USD)'
+                        },
+                    },
+                },
+                plugins: {
+                    tooltip: {
+                        callbacks: {
+                            title: function (x) {
+                                return `$${x[0].raw.y} (${x[0].raw.metadata.Date.toLocaleDateString()})`;
+                            },
+                            label: function (x) {
+                                const { Category, Payee, Memo } = x.raw.metadata;
+                                return `${Payee} - ${Category} (${Memo})`;
+                            },
+                        },
+                    },
+                },
+            },
+        });
+    }
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+/**
+ * Generic line chart parameterized by:
+ * - datasets: forwarded to chart.js library
+ * - x: string label for x-axis
+ * - y: string label for y-axis
+ */
+class GenLineChart extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+
+    componentDidUpdate(prevProps, prevState) {
+        if (this.props.datasets != prevProps.datasets) {
+            this.chart.data.datasets = this.props.datasets;
+            this.chart.update();
+        }
+    }
+
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        this.chart = new Chart(mount, {
+            type: 'line',
+            data: {
+                datasets: this.props.datasets,
+            },
+            options: {
+                scales: {
+                    x: {
+                        type: 'time',
+                        title: {
+                            display: true,
+                            text: this.props.x,
+                        },
+                    },
+                    y: {
+                        title: {
+                            display: true,
+                            text: this.props.y
+                        },
+                    },
+                },
+            },
+        });
+    }
+
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+class DonutChart extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+
+    componentDidUpdate(prevProps, prevState) {
+        if (this.props.datasets != prevProps.datasets) {
+            this.chart.data.datasets = this.props.datasets;
+            this.chart.update();
+        }
+    }
+
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        this.chart = new Chart(mount, {
+            type: 'doughnut',
+            data: {
+                labels: this.props.labels,
+                datasets: this.props.datasets,
+            },
+            options: {
+                resonsive: true,
+            },
+        });
+    }
+
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+class StackedHistogram extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+
+    componentDidUpdate(prevProps, prevState) {
+        if (this.props.datasets != prevProps.datasets) {
+            this.chart.data.datasets = this.props.datasets;
+            this.chart.update();
+        }
+    }
+
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        this.chart = new Chart(mount, {
+            type: 'bar',
+            data: {
+                labels: this.props.labels,
+                datasets: this.props.datasets,
+            },
+            options: {
+                scales: {
+                    x: {
+                        stacked: true,
+                    },
+                    y: {
+                        stacked: true,
+                    },
+                },
+            },
+        });
+    }
+
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+/**
+ * Display the "Actual Savings Rate" (bucketed by month) as a line chart with
+ * the "Expected Savings Rate" overlay.
+ */
+class SavingsRateLineChart extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+
+    static getRevenue(transactions) {
+        // Bucket revenues into months.
+        return transactions.reduce((acc, x) => {
+            const month = x.Date.getMonth();
+            acc[month] += x.Inflow;
+            return acc;
+        }, new Array(12).fill(0));
+    }
+
+    static getExpenses(transactions) {
+        // Bucket revenues into months.
+        return transactions.reduce((acc, x) => {
+            const month = x.Date.getMonth();
+            acc[month] += x.Outflow;
+            return acc;
+        }, new Array(12).fill(0));
+    }
+
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        const revenue = SavingsRateLineChart.getRevenue(this.props.transactions);
+        const expenses = SavingsRateLineChart.getExpenses(this.props.transactions);
+
+        this.chart = new Chart(mount, {
+            type: 'line',
+            data: {
+                datasets: [
+                    {
+                        label: 'actual savings (by month)',
+                        data: new Array(12).fill(null).map((_, i) => ({
+                            x: i,
+                            y: (revenue[i] - expenses[i]) / revenue[i],
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.fadedBlue,
+                        backgroundColor: colors.fadedBlue,
+                    },
+                    {
+                        label: 'actual savings (overall)',
+                        data: new Array(12).fill(null).map((_, i) => ({
+                            x: i,
+                            y: this.props.rate,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.blue,
+                        backgroundColor: colors.blue,
+                    },
+                    // 0% marker (out of debt)
+                    {
+                        label: 'beginner (0%)',
+                        data: new Array(12).fill(null).map((x, i) => ({
+                            x: i,
+                            y: 0.00,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.white,
+                        backgroundColor: colors.white,
+                    },
+                    // 25% marker (quarter "Washington" club)
+                    {
+                        label: 'healthy (25%)',
+                        data: new Array(12).fill(null).map((x, i) => ({
+                            x: i,
+                            y: 0.25,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.purple,
+                        backgroundColor: colors.purple,
+                    },
+                    // 50% marker (1/2-dollar "Kennedy" club)
+                    {
+                        label: 'rich (50%)',
+                        data: new Array(12).fill(null).map((x, i) => ({
+                            x: i,
+                            y: 0.50,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.brown,
+                        backgroundColor: colors.brown,
+                    },
+                    // 75% marker
+                    {
+                        label: 'wealthy (75%)',
+                        data: new Array(12).fill(null).map((x, i) => ({
+                            x: i,
+                            y: 0.75,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.black,
+                        backgroundColor: colors.black,
+                    },
+                ],
+                labels: months,
+            },
+            options: {
+                scales: {
+                    y: {
+                        max: 1.0,
+                        min: -1.0,
+                        title: {
+                            display: true,
+                            text: 'Savings Rate (%)'
+                        },
+                    },
+                },
+            },
+        });
+    }
+
+    componentDidUpdate(prevProps, prevState) {
+        // Bucket revenues into months.
+        const revenue = SavingsRateLineChart.getRevenue(this.props.transactions);
+        const expenses = SavingsRateLineChart.getExpenses(this.props.transactions);
+
+        this.chart.data.datasets[0].data = new Array(12).fill(null).map((_, i) => ({
+            x: i,
+            y: (revenue[i] - expenses[i]) / revenue[i],
+        }));
+        this.chart.update();
+    }
+
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+class App extends React.Component {
+    constructor(props) {
+        super(props);
+        const query = 'Account:/checking/';
+        const allTransactions = [];
+        const savingsView = 'after:"01/01/2022"';
+        const inflowQuery = 'Account:/checking/';
+        const outflowQuery = 'Account:/checking/ -Category:/(stocks|crypto)/';
+
+        // slx configuration
+        const slxCaseSensitive = false;
+        const slxPreferRegex = true;
+        const slxDateKey = 'Date';
+
+        this.state = {
+            query,
+            transactionsView: 'table',
+            sensitive: false,
+            allTransactions,
+            slxCaseSensitive,
+            slxPreferRegex,
+            slxDateKey,
+            filteredTransactions: select(query, allTransactions, {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }),
+            saved: {},
+            focus: {
+                sum: false,
+                1000: false,
+                100: false,
+                10: false,
+                1: false,
+                0.1: false,
+            },
+            budget: [
+                {
+                    label: 'Flexible',
+                    children: [
+                        { label: 'groceries - $200/mo', savings: false, monthly: 400.00 },
+                        { label: 'eating out - $150/mo', savings: false, monthly: 200.00 },
+                        { label: 'alcohol - $200/mo', savings: false, monthly: 200.00 },
+                        { label: 'household items - $50/mo', savings: false, monthly: 50.00 },
+                        { label: 'toiletries - $200/yr', savings: false, monthly: 200.00 / 12 },
+                        { label: 'haircuts - $400/yr', savings: false, monthly: 400.00 / 12 },
+                        { label: 'gasoline - $100/mo', savings: false, monthly: 100.00 },
+                        { label: 'parking - $10/mo', savings: false, monthly: 10.00 },
+                        { label: 'ride services - $25/mo', savings: false, monthly: 50.00 },
+                        { label: 'LMNT - $45/mo', savings: false, monthly: 45.00 },
+                        { label: 'books - $25/mo', savings: false, monthly: 25.00 },
+                        { label: 'vacation - $4,000/yr', savings: false, monthly: 4000.00 / 12 },
+                        { label: 'reimbursements - $5,000 balance', savings: false, monthly: 0.00 },
+                    ],
+                },
+                {
+                    label: 'Fixed',
+                    children: [
+                        { label: 'rent - $3,100/mo', savings: false, monthly: 3100.00 },
+                        { label: 'electric - $50/mo', savings: false, monthly: 50.00 },
+                        { label: 'SoCalGas - $30/mo', savings: false, monthly: 30.00 },
+                        { label: 'YNAB', savings: false, monthly: 100.00 / 12 },
+                        { label: 'Robinhood Gold', savings: false, monthly: 5.00 },
+                        { label: 'Spotify', savings: false, monthly: 10.00 },
+                        { label: 'Surfline', savings: false, monthly: 100.00 / 12 },
+                        { label: 'HBO Max', savings: false, monthly: 170.00 },
+                        { label: 'Clear', savings: false, monthly: 179.00 },
+                        { label: 'car insurance', savings: false, monthly: 100.00 },
+                        { label: 'Making Sense', savings: false, monthly: 50.00 / 12 },
+                        { label: 'internet', savings: false, monthly: 100.00 },
+                        { label: 'tax return', savings: false, monthly: 200.00 / 12 },
+                    ],
+                },
+                {
+                    label: 'Rainy Day (dont touch)',
+                    children: [
+                        { label: 'emergency fund', savings: false, monthly: 0.00 },
+                        { label: 'check-ups', savings: false, monthly: 7.50 },
+                        { label: 'car maintenance', savings: false, monthly: 98.33 },
+                    ],
+                },
+                {
+                    label: 'Savings (dont touch)',
+                    children: [
+                        { label: 'stocks', savings: true, monthly: 4000.00 },
+                        { label: 'crypto', savings: true, monthly: 736.00 },
+                    ],
+                },
+                {
+                    label: 'Gifts (dont touch)',
+                    children: [
+                        { label: 'birthdays', savings: false, monthly: 250.00 / 12 },
+                        { label: 'Valentines Day', savings: false, monthly: 100.00 / 12 },
+                        { label: 'Mothers Day', savings: false, monthly: 25.00 / 12 },
+                        { label: 'Fathers Day', savings: false, monthly: 25.00 / 12 },
+                        { label: 'Christmas', savings: false, monthly: 500.00 / 12 },
+                    ],
+                },
+                {
+                    label: 'Error Budget',
+                    children: [
+                        { label: 'stuff I forgot to budget for - $2,254/mo', savings: false, monthly: 0.00 },
+                    ],
+                },
+            ],
+            paycheck: 6000.00,
+            view: 'query',
+            savingsView,
+            inflowQuery,
+            outflowQuery,
+            inflowTransactions: select(inflowQuery, select(savingsView, allTransactions, {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }), {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }),
+            outflowTransactions: select(outflowQuery, select(savingsView, allTransactions, {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }), {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }),
+            sortExpr: 'Date DESC; Outflow DESC',
+        };
+    }
+
+    render() {
+        const sum = this.state.filteredTransactions.reduce((acc, { Outflow }) => acc + Outflow, 0);
+        const savedSum = Object.values(this.state.saved).reduce((acc, sum) => acc + sum, 0);
+        const categories = this.state.allTransactions.reduce((acc, x) => {
+            if (!(x.Category in acc)) {
+                acc[x.Category] = [];
+            }
+            acc[x.Category].push(x);
+            return acc;
+        }, {});
+
+        let view = null;
+        if (this.state.view === 'query') {
+            view = (
+                <QueryView
+                    transactionsView={this.state.transactionsView}
+                    sortExpr={this.state.sortExpr}
+                    onSortExprChange={sortExpr => this.setState({ sortExpr })}
+                    sensitive={this.state.sensitive}
+                    query={this.state.query}
+                    focus={this.state.focus}
+                    allTransactions={this.state.allTransactions}
+                    transactions={this.state.filteredTransactions}
+                    saved={this.state.saved}
+                    setState={this.setState.bind(this)}
+                    slxConfig={{
+                        caseSensitive: this.state.slxCaseSensitive,
+                        preferRegex: this.state.slxPreferRegex,
+                        dateKey: this.state.slxDateKey,
+                    }}
+                />
+            );
+        } else if (this.state.view === 'savings') {
+            view = (
+                <SavingsView
+                    categories={categories}
+                    sensitive={this.state.sensitive}
+                    savingsView={this.state.savingsView}
+                    inflowQuery={this.state.inflowQuery}
+                    outflowQuery={this.state.outflowQuery}
+                    inflowTransactions={this.state.inflowTransactions}
+                    outflowTransactions={this.state.outflowTransactions}
+                    onFilterInflow={() => this.setState({
+                        inflowTransactions: select(this.state.inflowQuery, select(this.state.savingsView, this.state.allTransactions, {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }), {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }),
+                    })}
+                    onFilterOutflow={() => this.setState({
+                        outflowTransactions: select(this.state.outflowQuery, select(this.state.savingsView, this.state.allTransactions, {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }), {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }),
+                    })}
+                    onFilterSavingsView={() => this.setState({
+                        inflowTransactions: select(this.state.inflowQuery, select(this.state.savingsView, this.state.allTransactions, {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }), {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }),
+                        outflowTransactions: select(this.state.outflowQuery, select(this.state.savingsView, this.state.allTransactions, {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }), {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }),
+                    })}
+                    onSavingsViewChange={x => this.setState({ savingsView: x })}
+                    onInflowQueryChange={x => this.setState({ inflowQuery: x })}
+                    onOutflowQueryChange={x => this.setState({ outflowQuery: x })}
+                />
+            );
+        } else if (this.state.view === 'budget') {
+            // Planned expenses:
+            // - minus planned assignment to emergency fund (not an expense)
+            // - minus planned spend to investments (e.g. stocks, crypto)
+            const budgetedSpend = this.state.budget.reduce((acc, x) => acc + x.children.filter(x => x.savings).reduce((acc, x) => acc + x.monthly, 0), 0);
+
+            view = (
+                <div>
+                    <fieldset>
+                        <legend>Details</legend>
+                        <div className="form-group">
+                            <label htmlFor="paycheck">Paycheck</label>
+                            <input name="paycheck" type="text" />
+                        </div>
+                        <div className="form-group">
+                            <label htmlFor="savings-rate">Savings Rate</label>
+                            <input name="savings-rate" type="text" />
+                        </div>
+                    </fieldset>
+                    <ul>
+                        <li>Available Spend: {dollars(this.state.paycheck * 2, this.state.sensitive)}</li>
+                        <li>Target Spend: {dollars(this.state.paycheck, this.state.sensitive)}</li>
+                        <li>Budgeted Spend (minus savings): {dollars(budgetedSpend, this.state.sensitive)}</li>
+                        <li>Emergency Fund Size (recommended): {dollars(budgetedSpend * 3, this.state.sensitive)}</li>
+                    </ul>
+                    <div style={{ width: '30em' }}>
+                        <DonutChart labels={this.state.budget.map(x => x.label)} datasets={[
+                            {
+                                label: 'Categories',
+                                data: this.state.budget.map(x => x.children.reduce((acc, y) => acc + y.monthly, 0)),
+                            }
+                        ]} />
+                    </div>
+                    <ul>
+                        {this.state.budget.map(x => (
+                            <li>
+                                <div>{x.label} - {dollars(x.children.reduce((acc, x) => acc + x.monthly, 0), this.state.sensitive)}</div>
+                                <table>
+                                    <thead>
+                                        <tr>
+                                            <th>category</th>
+                                            <th>assigned (target)</th>
+                                            <th>last year (actual)</th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+                                        {x.children.map(y => (
+                                            <tr>
+                                                <td>{y.label}</td>
+                                                <td>{dollars(y.monthly, this.state.sensitive)}</td>
+                                                <td>{dollars((categories[y.label] || []).reduce((acc, x) => acc + x.Outflow, 0) / 12, this.state.sensitive)}</td>
+                                            </tr>
+                                        ))}
+                                    </tbody>
+                                </table>
+                            </li>
+                        ))}
+                    </ul>
+                </div>
+            );
+        }
+
+        return (
+            <div className="container">
+                <nav className="terminal-menu">
+                    <ul>
+                        <li><a href="#" onClick={() => this.setState({ view: 'query' })}>query</a></li>
+                        <li><a href="#" onClick={() => this.setState({ view: 'savings' })}>savings</a></li>
+                        <li><a href="#" onClick={() => this.setState({ view: 'budget' })}>budget</a></li>
+                        <li><a href="#" onClick={() => this.setState({ sensitive: !this.state.sensitive })}>sensitive</a></li>
+                    </ul>
+                </nav>
+                <UploadJSON onUpload={xs => this.setState({
+                    allTransactions: xs,
+                    filteredTransactions: select(this.state.query, xs, {
+                        caseSensitive: this.state.slxCaseSensitive,
+                        preferRegex: this.state.slxPreferRegex,
+                        slxDateKey: this.state.slxDateKey,
+                    }).sort(compileSort(this.state.sortExpr))
+                })} />
+                {view}
+            </div>
+        );
+    }
+}
+
+const QueryView = ({ sensitive, query, focus, allTransactions, transactions, saved, setState, slxConfig, sortExpr, transactionsView }) => (
+    <div>
+        <Query
+            query={query}
+            onChange={query => setState({
+                query,
+            })}
+            onFilter={() => setState({
+                filteredTransactions: select(query, allTransactions, slxConfig),
+            })}
+        />
+        <fieldset>
+            <legend>Sort</legend>
+            <div className="form-group">
+                <input type="text" value={sortExpr} onChange={e => setState({ sortExpr: e.target.value, })} />
+            </div>
+            <div className="form-group">
+                <button className="btn btn-default" onClick={() => setState({
+                    filteredTransactions: transactions.slice().sort(compileSort(sortExpr)),
+                })}>Sort</button>
+            </div>
+        </fieldset>
+        <hr />
+        <ScatterChart transactions={transactions} />
+        <hr />
+        <Transactions
+            transactionsView={transactionsView}
+            sensitive={sensitive}
+            transactions={transactions}
+            onClick={x => setState({
+                saved: { ...saved, [transactionKey(x)]: x.Outflow }
+            })}
+            onViewChange={transactionsView => setState({ transactionsView })}
+        />
+    </div>
+);
+
+function classifyRate(x) {
+    if (x < 0.25) {
+        return 'needs improvement';
+    }
+    if (x < 0.50) {
+        return 'healthy';
+    }
+    if (x < 0.75) {
+        return 'rich';
+    }
+    if (x < 1.00) {
+        return 'wealthy';
+    }
+}
+
+const SavingsView = ({
+    sensitive,
+    categories,
+    savingsView,
+    inflowQuery,
+    outflowQuery,
+    inflowTransactions,
+    outflowTransactions,
+    onSavingsViewChange,
+    onInflowQueryChange,
+    onOutflowQueryChange,
+    onFilterInflow,
+    onFilterOutflow,
+    onFilterSavingsView,
+}) => {
+    const revenue = inflowTransactions.reduce((acc, x) => {
+        acc[x.Date.getMonth()] += x.Inflow;
+        return acc;
+    }, new Array(12).fill(0));
+
+    const inflow = inflowTransactions.reduce((acc, x) => acc + x.Inflow, 0);
+    const outflow = outflowTransactions.reduce((acc, x) => acc + x.Outflow, 0);
+
+    const delta25Sum = new Array(12).fill(0);
+    for (let i = 1; i < 12; i += 1) {
+        delta25Sum[i] = delta25Sum[i - 1] + revenue[i] * 0.25;
+    }
+
+    const delta50Sum = new Array(12).fill(0);
+    for (let i = 1; i < 12; i += 1) {
+        delta50Sum[i] = delta50Sum[i - 1] + revenue[i] * 0.5;
+    }
+
+    const delta75Sum = new Array(12).fill(0);
+    for (let i = 1; i < 12; i += 1) {
+        delta75Sum[i] = delta75Sum[i - 1] + revenue[i] * 0.75;
+    }
+
+    return (
+        <section>
+            <fieldset>
+                <legend>Filtering</legend>
+                <div className="form-group">
+                    <label htmlFor="savings-view">Savings View</label>
+                    <input name="savings-view" type="text" placeholder="Savings View..." value={savingsView}
+                        onChange={e => onSavingsViewChange(e.target.value)} />
+                    <button className="btn btn-default" onClick={() => onFilterSavingsView()}>Apply</button>
+                </div>
+                <div className="form-group">
+                    <label htmlFor="inflow-query">Inflow Query</label>
+                    <input name="inflow-query" type="text" placeholder="Inflow query..." value={inflowQuery}
+                        onChange={e => onInflowQueryChange(e.target.value)} />
+                    <button className="btn btn-default" onClick={() => onFilterInflow()}>Filter</button>
+                </div>
+                <div className="form-group">
+                    <label htmlFor="outflow-query">Outflow Query</label>
+                    <input name="outflow-query" type="text" placeholder="Outflow query..." value={outflowQuery}
+                        onChange={e => onOutflowQueryChange(e.target.value)} />
+                    <button className="btn btn-default" onClick={() => onFilterOutflow()}>Filter</button>
+                </div>
+            </fieldset>
+            <ul>
+                <li>inflow: {dollars(inflow, sensitive)}</li>
+                <li>outflow: {dollars(outflow)}</li>
+                <li>savings: {dollars(inflow - outflow)}</li>
+                <li>rate: {parseFloat((inflow - outflow) / inflow * 100).toFixed(2) + "%"} ({classifyRate((inflow - outflow) / inflow)})</li>
+            </ul>
+            <SavingsRateLineChart rate={(inflow - outflow) / inflow} transactions={outflowTransactions} />
+            {/* O($1,000) */}
+            <StackedHistogram labels={months} datasets={Object.keys(categories).map(k => ({
+                label: k,
+                data: categories[k].reduce((acc, x) => {
+                    acc[x.Date.getMonth()] += x.Outflow;
+                    return acc;
+                }, new Array(12).fill(0)).map((x, i) => ({
+                    x: i,
+                    y: x,
+                }))
+            }))} />
+            {/* O($100) */}
+            <StackedHistogram labels={months} datasets={Object.keys(categories).map(k => ({
+                label: k,
+                data: categories[k].reduce((acc, x) => {
+                    acc[x.Date.getMonth()] += x.Outflow;
+                    return acc;
+                }, new Array(12).fill(0)).map((x, i) => ({
+                    x: i,
+                    y: x,
+                }))
+            }))} />
+            {/* O($10) */}
+            <StackedHistogram labels={months} datasets={Object.keys(categories).map(k => ({
+                label: k,
+                data: categories[k].reduce((acc, x) => {
+                    acc[x.Date.getMonth()] += x.Outflow;
+                    return acc;
+                }, new Array(12).fill(0)).map((x, i) => ({
+                    x: i,
+                    y: x,
+                }))
+            }))} />
+            {/* <GenLineChart x="X" y="Y" datasets={[
+                {
+                    label: '25%',
+                    data: delta25Sum.map((x, i) => ({
+                        x: i,
+                        y: x,
+                    })),
+                },
+                {
+                    label: '50%',
+                    data: delta50Sum.map((x, i) => ({
+                        x: i,
+                        y: x,
+                    })),
+                },
+                {
+                    label: '75%',
+                    data: delta75Sum.map((x, i) => ({
+                        x: i,
+                        y: x,
+                    })),
+                },
+            ]} /> */}
+            <hr />
+            <table>
+                <thead>
+                    <tr>
+                        <th>Month</th>
+                        <th>Delta (25%)</th>
+                        <th>Delta (50%)</th>
+                        <th>Delta (75%)</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    {months.map((x, i) => (
+                        <tr key={i}>
+                            <td>{x}</td>
+                            <td>{dollars(delta25Sum[i], sensitive)}</td>
+                            <td>{dollars(delta50Sum[i], sensitive)}</td>
+                            <td>{dollars(delta75Sum[i], sensitive)}</td>
+                        </tr>
+                    ))}
+                </tbody>
+            </table>
+        </section>
+    );
+};
+
+const Calculator = ({ sensitive, saved, onClear }) => (
+    <div>
+        <ul>
+            {Object.keys(saved).map(k => (
+                <li key={k}>
+                    {dollars(saved[k], sensitive)} {k}
+                </li>
+            ))}
+        </ul>
+        <p>{dollars(savedSum, sensitive)}</p>
+        <button className="btn btn-default" onClick={() => onClear()}>clear</button>
+    </div>
+);
+
+/**
+ * Table rendering information about transactions bucketed by its order of
+ * magnitude.
+ */
+const Magnitable = ({ sensitive, label, transactions }) => {
+    const categories = transactions.reduce((acc, x) => {
+        if (x.Category === '') {
+            return acc;
+        }
+        if (!(x.Category in acc)) {
+            acc[x.Category] = 0;
+        }
+        acc[x.Category] += x.Outflow;
+        return acc;
+    }, {});
+
+    // Sort category keys by sum decreasing.
+    const keys = [...Object.keys(categories)].sort((x, y) => {
+        if (categories[x] < categories[y]) {
+            return 1;
+        } else if (categories[x] > categories[y]) {
+            return -1;
+        } else {
+            return 0;
+        }
+    });
+
+    return (
+        <React.Fragment>
+            {keys.map(k => (
+                <tr style={{ backgroundColor: '#F0F8FF' }}>
+                    <td>{k}</td><td>{dollars(categories[k], sensitive)}</td>
+                </tr>
+            ))}
+        </React.Fragment>
+    );
+};
+
+/**
+ * Calculates and renders various aggregates over an input list of transactions.
+ */
+const AggregateTable = ({ sensitive, focus, onFocus, transactions }) => {
+    const net = transactions.reduce((acc, x) => acc + x.Inflow - x.Outflow, 0);
+    const sum = transactions.reduce((acc, x) => acc + x.Outflow, 0);
+    const buckets = transactions.reduce((acc, x) => {
+        const order = magnitude(x.Outflow);
+        const bucket = Math.pow(10, order);
+        acc[bucket].push(x);
+        return acc;
+    }, { 0.1: [], 0: [], 1: [], 10: [], 100: [], 1000: [] });
+
+    return (
+        <div>
+            <table>
+                <caption>Aggregations</caption>
+                <thead>
+                    <tr>
+                        <th>function</th>
+                        <th>value</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr><td>net</td><td>{dollars(net, sensitive)}</td></tr>
+                    <tr onClick={() => onFocus('sum')}><td>sum</td><td>{dollars(sum, sensitive)}</td></tr>
+                    {focus.sum && <Magnitable sensitive={sensitive} label="sum" transactions={transactions} />}
+                    <tr><td>per day</td><td>{dollars(sum / 365, sensitive)}</td></tr>
+                    <tr><td>per week</td><td>{dollars(sum / 52, sensitive)}</td></tr>
+                    <tr><td>per month</td><td>{dollars(sum / 12, sensitive)}</td></tr>
+                    <tr onClick={() => onFocus(1000)}><td>Σ Θ($1,000)</td><td>{dollars(buckets[1000].reduce((acc, x) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[1000]) && <Magnitable sensitive={sensitive} label="$1,000" transactions={buckets[1000]} />}
+                    <tr onClick={() => onFocus(100)}><td>Σ Θ($100)</td><td>{dollars(buckets[100].reduce((acc, x, sensitive) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[100]) && <Magnitable sensitive={sensitive} label="$100" transactions={buckets[100]} />}
+                    <tr onClick={() => onFocus(10)}><td>Σ Θ($10)</td><td>{dollars(buckets[10].reduce((acc, x) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[10]) && <Magnitable sensitive={sensitive} label="$10" transactions={buckets[10]} />}
+                    <tr onClick={() => onFocus(1)}><td>Σ Θ($1)</td><td>{dollars(buckets[1].reduce((acc, x) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[1]) && <Magnitable sensitive={sensitive} label="$1.00" transactions={buckets[1]} />}
+                    <tr onClick={() => onFocus(0.1)}><td>Σ Θ($0.10)</td><td>{dollars(buckets[0.1].reduce((acc, x) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[0.1]) && <Magnitable sensitive={sensitive} label="$0.10" transactions={buckets[0.1]} />}
+                    <tr><td>average</td><td>{dollars(sum / transactions.length, sensitive)}</td></tr>
+                    <tr><td>count</td><td>{transactions.length}</td></tr>
+                </tbody>
+            </table>
+        </div>
+    );
+};
+
+const Query = ({ query, onChange, onFilter }) => (
+    <fieldset>
+        <legend>Query</legend>
+        <div className="form-group">
+            <input name="query" type="text" value={query} onChange={e => onChange(e.target.value)} />
+        </div>
+        <div className="form-group">
+            <button className="btn btn-default" onClick={() => onFilter()}>Filter</button>
+        </div>
+    </fieldset>
+);
+
+const tableHeaders = [
+    'Account',
+    'Category',
+    'Date',
+    'Inflow',
+    'Outflow',
+    'Payee',
+    'Memo',
+];
+
+const Transactions = ({ sensitive, transactions, onSort, onClick, onViewChange, transactionsView }) => {
+    let view = null;
+    if (transactionsView === 'table') {
+        view = (
+            <table>
+                <thead>
+                    <tr>
+                        {tableHeaders.map(x => <th>{x}</th>)}
+                    </tr>
+                </thead>
+                <tbody>
+                    {transactions.map(x => (
+                        <tr onClick={() => onClick(x)}>
+                            <td>{x.Account}</td>
+                            <td>{x.Category}</td>
+                            <td>{x.Date.toLocaleDateString()}</td>
+                            <td>{dollars(x.Inflow, sensitive)}</td>
+                            <td>{dollars(x.Outflow, sensitive)}</td>
+                            <td>{x.Payee}</td>
+                            <td>{x.Memo}</td>
+                        </tr>
+                    ))}
+                </tbody>
+            </table>
+        );
+    }
+    else if (transactionsView === 'csv') {
+        view = (
+            <code>{tableHeaders.join(',') + '\n' + transactions.map(x => tableHeaders.map(k => x[k]).join(',')).join("\n")}</code>
+        );
+    }
+    else if (transactionsView === 'json') {
+        view = (
+            <code>{JSON.stringify(transactions)}</code>
+        );
+    }
+
+    return (
+        <div>
+            <legend>Transactions</legend>
+            <div className="btn-group">
+                <button onClick={() => onViewChange('table')} className="btn btn-default btn-ghost">Table</button>
+                <button onClick={() => onViewChange('csv')} className="btn btn-default btn-ghost">CSV</button>
+                <button onClick={() => onViewChange('json')} className="btn btn-default btn-ghost">JSON</button>
+            </div>
+            {view}
+        </div>
+    );
+}
+
+const domContainer = document.querySelector('#mount');
+const root = ReactDOM.createRoot(domContainer);
+
+root.render(<App />);
diff --git a/users/wpcarro/ynabsql/dataviz/index.html b/users/wpcarro/ynabsql/dataviz/index.html
new file mode 100644
index 0000000000..e30c8682b2
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/index.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <!-- TODO(wpcarro): Cache these locally -->
+    <link rel="stylesheet" href="./cdn/terminal.min.css" />
+    <style>
+      :root {
+        --page-width: 100em;
+      }
+    </style>
+  </head>
+  <body class="container">
+    <div id="mount"></div>
+    <!-- depot JS -->
+    <script src="./cdn/slx.js"></script>
+    <script src="./components.jsx" type="module"></script>
+  </body>
+</html>
diff --git a/users/wpcarro/ynabsql/dataviz/package.json b/users/wpcarro/ynabsql/dataviz/package.json
new file mode 100644
index 0000000000..03f795c0bf
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/package.json
@@ -0,0 +1,15 @@
+{
+  "devDependencies": {
+    "@parcel/validator-typescript": "^2.8.3",
+    "parcel": "^2.8.3",
+    "process": "^0.11.10",
+    "typescript": ">=3.0.0"
+  },
+  "dependencies": {
+    "chart.js": "^4.2.0",
+    "chartjs-adapter-date-fns": "^3.0.0",
+    "date-fns": "^2.29.3",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"
+  }
+}
diff --git a/users/wpcarro/ynabsql/dataviz/yarn.lock b/users/wpcarro/ynabsql/dataviz/yarn.lock
new file mode 100644
index 0000000000..70c52a3a9f
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/yarn.lock
@@ -0,0 +1,1540 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
+  integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
+  dependencies:
+    "@babel/highlight" "^7.18.6"
+
+"@babel/helper-validator-identifier@^7.18.6":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
+  integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
+
+"@babel/highlight@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+  integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.18.6"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
+"@jridgewell/gen-mapping@^0.3.0":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
+  integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/resolve-uri@3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+  integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/set-array@^1.0.1":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+  integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
+"@jridgewell/source-map@^0.3.2":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+  integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
+  version "1.4.14"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+  integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@^0.3.9":
+  version "0.3.17"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
+  integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
+  dependencies:
+    "@jridgewell/resolve-uri" "3.1.0"
+    "@jridgewell/sourcemap-codec" "1.4.14"
+
+"@kurkle/color@^0.3.0":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f"
+  integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==
+
+"@lezer/common@^0.15.0", "@lezer/common@^0.15.7":
+  version "0.15.12"
+  resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.12.tgz#2f21aec551dd5fd7d24eb069f90f54d5bc6ee5e9"
+  integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==
+
+"@lezer/lr@^0.15.4":
+  version "0.15.8"
+  resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.8.tgz#1564a911e62b0a0f75ca63794a6aa8c5dc63db21"
+  integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==
+  dependencies:
+    "@lezer/common" "^0.15.0"
+
+"@lmdb/lmdb-darwin-arm64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz#bc66fa43286b5c082e8fee0eacc17995806b6fbe"
+  integrity sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A==
+
+"@lmdb/lmdb-darwin-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz#89d8390041bce6bab24a82a20392be22faf54ffc"
+  integrity sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA==
+
+"@lmdb/lmdb-linux-arm64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz#14fe4c96c2bb1285f93797f45915fa35ee047268"
+  integrity sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ==
+
+"@lmdb/lmdb-linux-arm@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz#05bde4573ab10cf21827339fe687148f2590cfa1"
+  integrity sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw==
+
+"@lmdb/lmdb-linux-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz#d2f85afd857d2c33d2caa5b057944574edafcfee"
+  integrity sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q==
+
+"@lmdb/lmdb-win32-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz#28f643fbc0bec30b07fbe95b137879b6b4d1c9c5"
+  integrity sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA==
+
+"@mischnic/json-sourcemap@^0.1.0":
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz#38af657be4108140a548638267d02a2ea3336507"
+  integrity sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==
+  dependencies:
+    "@lezer/common" "^0.15.7"
+    "@lezer/lr" "^0.15.4"
+    json5 "^2.2.1"
+
+"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.2.0.tgz#901c5937e1441572ea23e631fe6deca68482fe76"
+  integrity sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==
+
+"@msgpackr-extract/msgpackr-extract-darwin-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.2.0.tgz#fb877fe6bae3c4d3cea29786737840e2ae689066"
+  integrity sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.2.0.tgz#986179c38b10ac41fbdaf7d036c825cbc72855d9"
+  integrity sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.2.0.tgz#15f2c6fe9e0adc06c21af7e95f484ff4880d79ce"
+  integrity sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg==
+
+"@msgpackr-extract/msgpackr-extract-linux-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.2.0.tgz#30cae5c9a202f3e1fa1deb3191b18ffcb2f239a2"
+  integrity sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw==
+
+"@msgpackr-extract/msgpackr-extract-win32-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.2.0.tgz#016d855b6bc459fd908095811f6826e45dd4ba64"
+  integrity sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA==
+
+"@parcel/bundler-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/bundler-default/-/bundler-default-2.8.3.tgz#d64739dbc2dbd59d6629861bf77a8083aced5229"
+  integrity sha512-yJvRsNWWu5fVydsWk3O2L4yIy3UZiKWO2cPDukGOIWMgp/Vbpp+2Ct5IygVRtE22bnseW/E/oe0PV3d2IkEJGg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/graph" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/cache@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/cache/-/cache-2.8.3.tgz#169e130cf59913c0ed9fadce1a450e68f710e16f"
+  integrity sha512-k7xv5vSQrJLdXuglo+Hv3yF4BCSs1tQ/8Vbd6CHTkOhf7LcGg6CPtLw053R/KdMpd/4GPn0QrAsOLdATm1ELtQ==
+  dependencies:
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    lmdb "2.5.2"
+
+"@parcel/codeframe@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.8.3.tgz#84fb529ef70def7f5bc64f6c59b18d24826f5fcc"
+  integrity sha512-FE7sY53D6n/+2Pgg6M9iuEC6F5fvmyBkRE4d9VdnOoxhTXtkEqpqYgX7RJ12FAQwNlxKq4suBJQMgQHMF2Kjeg==
+  dependencies:
+    chalk "^4.1.0"
+
+"@parcel/compressor-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/compressor-raw/-/compressor-raw-2.8.3.tgz#301753df8c6de967553149639e8a4179b88f0c95"
+  integrity sha512-bVDsqleBUxRdKMakWSlWC9ZjOcqDKE60BE+Gh3JSN6WJrycJ02P5wxjTVF4CStNP/G7X17U+nkENxSlMG77ySg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/config-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/config-default/-/config-default-2.8.3.tgz#9a43486e7c702e96c68052c37b79098d7240e35b"
+  integrity sha512-o/A/mbrO6X/BfGS65Sib8d6SSG45NYrNooNBkH/o7zbOBSRQxwyTlysleK1/3Wa35YpvFyLOwgfakqCtbGy4fw==
+  dependencies:
+    "@parcel/bundler-default" "2.8.3"
+    "@parcel/compressor-raw" "2.8.3"
+    "@parcel/namer-default" "2.8.3"
+    "@parcel/optimizer-css" "2.8.3"
+    "@parcel/optimizer-htmlnano" "2.8.3"
+    "@parcel/optimizer-image" "2.8.3"
+    "@parcel/optimizer-svgo" "2.8.3"
+    "@parcel/optimizer-terser" "2.8.3"
+    "@parcel/packager-css" "2.8.3"
+    "@parcel/packager-html" "2.8.3"
+    "@parcel/packager-js" "2.8.3"
+    "@parcel/packager-raw" "2.8.3"
+    "@parcel/packager-svg" "2.8.3"
+    "@parcel/reporter-dev-server" "2.8.3"
+    "@parcel/resolver-default" "2.8.3"
+    "@parcel/runtime-browser-hmr" "2.8.3"
+    "@parcel/runtime-js" "2.8.3"
+    "@parcel/runtime-react-refresh" "2.8.3"
+    "@parcel/runtime-service-worker" "2.8.3"
+    "@parcel/transformer-babel" "2.8.3"
+    "@parcel/transformer-css" "2.8.3"
+    "@parcel/transformer-html" "2.8.3"
+    "@parcel/transformer-image" "2.8.3"
+    "@parcel/transformer-js" "2.8.3"
+    "@parcel/transformer-json" "2.8.3"
+    "@parcel/transformer-postcss" "2.8.3"
+    "@parcel/transformer-posthtml" "2.8.3"
+    "@parcel/transformer-raw" "2.8.3"
+    "@parcel/transformer-react-refresh-wrap" "2.8.3"
+    "@parcel/transformer-svg" "2.8.3"
+
+"@parcel/core@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/core/-/core-2.8.3.tgz#22a69f36095d53736ab10bf42697d9aa5f4e382b"
+  integrity sha512-Euf/un4ZAiClnlUXqPB9phQlKbveU+2CotZv7m7i+qkgvFn5nAGnrV4h1OzQU42j9dpgOxWi7AttUDMrvkbhCQ==
+  dependencies:
+    "@mischnic/json-sourcemap" "^0.1.0"
+    "@parcel/cache" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/graph" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    abortcontroller-polyfill "^1.1.9"
+    base-x "^3.0.8"
+    browserslist "^4.6.6"
+    clone "^2.1.1"
+    dotenv "^7.0.0"
+    dotenv-expand "^5.1.0"
+    json5 "^2.2.0"
+    msgpackr "^1.5.4"
+    nullthrows "^1.1.1"
+    semver "^5.7.1"
+
+"@parcel/diagnostic@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/diagnostic/-/diagnostic-2.8.3.tgz#d560276d5d2804b48beafa1feaf3fc6b2ac5e39d"
+  integrity sha512-u7wSzuMhLGWZjVNYJZq/SOViS3uFG0xwIcqXw12w54Uozd6BH8JlhVtVyAsq9kqnn7YFkw6pXHqAo5Tzh4FqsQ==
+  dependencies:
+    "@mischnic/json-sourcemap" "^0.1.0"
+    nullthrows "^1.1.1"
+
+"@parcel/events@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.8.3.tgz#205f8d874e6ecc2cbdb941bf8d54bae669e571af"
+  integrity sha512-hoIS4tAxWp8FJk3628bsgKxEvR7bq2scCVYHSqZ4fTi/s0+VymEATrRCUqf+12e5H47uw1/ZjoqrGtBI02pz4w==
+
+"@parcel/fs-search@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.8.3.tgz#1c7d812c110b808758f44c56e61dfffdb09e9451"
+  integrity sha512-DJBT2N8knfN7Na6PP2mett3spQLTqxFrvl0gv+TJRp61T8Ljc4VuUTb0hqBj+belaASIp3Q+e8+SgaFQu7wLiQ==
+  dependencies:
+    detect-libc "^1.0.3"
+
+"@parcel/fs@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.8.3.tgz#80536afe877fc8a2bd26be5576b9ba27bb4c5754"
+  integrity sha512-y+i+oXbT7lP0e0pJZi/YSm1vg0LDsbycFuHZIL80pNwdEppUAtibfJZCp606B7HOjMAlNZOBo48e3hPG3d8jgQ==
+  dependencies:
+    "@parcel/fs-search" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/watcher" "^2.0.7"
+    "@parcel/workers" "2.8.3"
+
+"@parcel/graph@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/graph/-/graph-2.8.3.tgz#00ffe8ec032e74fee57199e54529f1da7322571d"
+  integrity sha512-26GL8fYZPdsRhSXCZ0ZWliloK6DHlMJPWh6Z+3VVZ5mnDSbYg/rRKWmrkhnr99ZWmL9rJsv4G74ZwvDEXTMPBg==
+  dependencies:
+    nullthrows "^1.1.1"
+
+"@parcel/hash@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/hash/-/hash-2.8.3.tgz#bc2499a27395169616cad2a99e19e69b9098f6e9"
+  integrity sha512-FVItqzjWmnyP4ZsVgX+G00+6U2IzOvqDtdwQIWisCcVoXJFCqZJDy6oa2qDDFz96xCCCynjRjPdQx2jYBCpfYw==
+  dependencies:
+    detect-libc "^1.0.3"
+    xxhash-wasm "^0.4.2"
+
+"@parcel/logger@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.8.3.tgz#e14e4debafb3ca9e87c07c06780f9afc38b2712c"
+  integrity sha512-Kpxd3O/Vs7nYJIzkdmB6Bvp3l/85ydIxaZaPfGSGTYOfaffSOTkhcW9l6WemsxUrlts4za6CaEWcc4DOvaMOPA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+
+"@parcel/markdown-ansi@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.8.3.tgz#1337d421bb1133ad178f386a8e1b746631bba4a1"
+  integrity sha512-4v+pjyoh9f5zuU/gJlNvNFGEAb6J90sOBwpKJYJhdWXLZMNFCVzSigxrYO+vCsi8G4rl6/B2c0LcwIMjGPHmFQ==
+  dependencies:
+    chalk "^4.1.0"
+
+"@parcel/namer-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/namer-default/-/namer-default-2.8.3.tgz#5304bee74beb4b9c1880781bdbe35be0656372f4"
+  integrity sha512-tJ7JehZviS5QwnxbARd8Uh63rkikZdZs1QOyivUhEvhN+DddSAVEdQLHGPzkl3YRk0tjFhbqo+Jci7TpezuAMw==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/node-resolver-core@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/node-resolver-core/-/node-resolver-core-2.8.3.tgz#581df074a27646400b3fed9da95297b616a7db8f"
+  integrity sha512-12YryWcA5Iw2WNoEVr/t2HDjYR1iEzbjEcxfh1vaVDdZ020PiGw67g5hyIE/tsnG7SRJ0xdRx1fQ2hDgED+0Ww==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    semver "^5.7.1"
+
+"@parcel/optimizer-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-css/-/optimizer-css-2.8.3.tgz#420a333f4b78f7ff15e69217dfed34421b1143ee"
+  integrity sha512-JotGAWo8JhuXsQDK0UkzeQB0UR5hDAKvAviXrjqB4KM9wZNLhLleeEAW4Hk8R9smCeQFP6Xg/N/NkLDpqMwT3g==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    lightningcss "^1.16.1"
+    nullthrows "^1.1.1"
+
+"@parcel/optimizer-htmlnano@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.8.3.tgz#a71ab6f0f24160ef9f573266064438eff65e96d0"
+  integrity sha512-L8/fHbEy8Id2a2E0fwR5eKGlv9VYDjrH9PwdJE9Za9v1O/vEsfl/0T/79/x129l5O0yB6EFQkFa20MiK3b+vOg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    htmlnano "^2.0.0"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    svgo "^2.4.0"
+
+"@parcel/optimizer-image@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-image/-/optimizer-image-2.8.3.tgz#ea49b4245b4f7d60b38c7585c6311fb21d341baa"
+  integrity sha512-SD71sSH27SkCDNUNx9A3jizqB/WIJr3dsfp+JZGZC42tpD/Siim6Rqy9M4To/BpMMQIIiEXa5ofwS+DgTEiEHQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    detect-libc "^1.0.3"
+
+"@parcel/optimizer-svgo@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-svgo/-/optimizer-svgo-2.8.3.tgz#04da4efec6b623679539a84961bff6998034ba8a"
+  integrity sha512-9KQed99NZnQw3/W4qBYVQ7212rzA9EqrQG019TIWJzkA9tjGBMIm2c/nXpK1tc3hQ3e7KkXkFCQ3C+ibVUnHNA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    svgo "^2.4.0"
+
+"@parcel/optimizer-terser@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-terser/-/optimizer-terser-2.8.3.tgz#3a06d98d09386a1a0ae1be85376a8739bfba9618"
+  integrity sha512-9EeQlN6zIeUWwzrzu6Q2pQSaYsYGah8MtiQ/hog9KEPlYTP60hBv/+utDyYEHSQhL7y5ym08tPX5GzBvwAD/dA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    terser "^5.2.0"
+
+"@parcel/package-manager@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/package-manager/-/package-manager-2.8.3.tgz#ddd0d62feae3cf0fb6cc0537791b3a16296ad458"
+  integrity sha512-tIpY5pD2lH53p9hpi++GsODy6V3khSTX4pLEGuMpeSYbHthnOViobqIlFLsjni+QA1pfc8NNNIQwSNdGjYflVA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    semver "^5.7.1"
+
+"@parcel/packager-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-css/-/packager-css-2.8.3.tgz#0eff34268cb4f5dfb53c1bbca85f5567aeb1835a"
+  integrity sha512-WyvkMmsurlHG8d8oUVm7S+D+cC/T3qGeqogb7sTI52gB6uiywU7lRCizLNqGFyFGIxcVTVHWnSHqItBcLN76lA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/packager-html@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-html/-/packager-html-2.8.3.tgz#f9263b891aa4dd46c6e2fa2b07025a482132fff1"
+  integrity sha512-OhPu1Hx1RRKJodpiu86ZqL8el2Aa4uhBHF6RAL1Pcrh2EhRRlPf70Sk0tC22zUpYL7es+iNKZ/n0Rl+OWSHWEw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+
+"@parcel/packager-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-js/-/packager-js-2.8.3.tgz#3ed11565915d73d12192b6901c75a6b820e4a83a"
+  integrity sha512-0pGKC3Ax5vFuxuZCRB+nBucRfFRz4ioie19BbDxYnvBxrd4M3FIu45njf6zbBYsI9eXqaDnL1b3DcZJfYqtIzw==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    globals "^13.2.0"
+    nullthrows "^1.1.1"
+
+"@parcel/packager-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-raw/-/packager-raw-2.8.3.tgz#bdec826df991e186cb58691cc45d12ad5c06676e"
+  integrity sha512-BA6enNQo1RCnco9MhkxGrjOk59O71IZ9DPKu3lCtqqYEVd823tXff2clDKHK25i6cChmeHu6oB1Rb73hlPqhUA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/packager-svg@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-svg/-/packager-svg-2.8.3.tgz#7233315296001c531cb55ca96b5f2ef672343630"
+  integrity sha512-mvIoHpmv5yzl36OjrklTDFShLUfPFTwrmp1eIwiszGdEBuQaX7JVI3Oo2jbVQgcN4W7J6SENzGQ3Q5hPTW3pMw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    posthtml "^0.16.4"
+
+"@parcel/plugin@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/plugin/-/plugin-2.8.3.tgz#7bb30a5775eaa6473c27f002a0a3ee7308d6d669"
+  integrity sha512-jZ6mnsS4D9X9GaNnvrixDQwlUQJCohDX2hGyM0U0bY2NWU8Km97SjtoCpWjq+XBCx/gpC4g58+fk9VQeZq2vlw==
+  dependencies:
+    "@parcel/types" "2.8.3"
+
+"@parcel/reporter-cli@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/reporter-cli/-/reporter-cli-2.8.3.tgz#12a4743b51b8fe6837f53c20e01bbf1f7336e8e4"
+  integrity sha512-3sJkS6tFFzgIOz3u3IpD/RsmRxvOKKiQHOTkiiqRt1l44mMDGKS7zANRnJYsQzdCsgwc9SOP30XFgJwtoVlMbw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chalk "^4.1.0"
+    term-size "^2.2.1"
+
+"@parcel/reporter-dev-server@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/reporter-dev-server/-/reporter-dev-server-2.8.3.tgz#a0daa5cc015642684cea561f4e0e7116bbffdc1c"
+  integrity sha512-Y8C8hzgzTd13IoWTj+COYXEyCkXfmVJs3//GDBsH22pbtSFMuzAZd+8J9qsCo0EWpiDow7V9f1LischvEh3FbQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/resolver-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/resolver-default/-/resolver-default-2.8.3.tgz#5ae41e537ae4a793c1abb47f094482b9e2ac3535"
+  integrity sha512-k0B5M/PJ+3rFbNj4xZSBr6d6HVIe6DH/P3dClLcgBYSXAvElNDfXgtIimbjCyItFkW9/BfcgOVKEEIZOeySH/A==
+  dependencies:
+    "@parcel/node-resolver-core" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/runtime-browser-hmr@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.8.3.tgz#1fa74e1fbd1030b0a920c58afa3a9eb7dc4bcd1e"
+  integrity sha512-2O1PYi2j/Q0lTyGNV3JdBYwg4rKo6TEVFlYGdd5wCYU9ZIN9RRuoCnWWH2qCPj3pjIVtBeppYxzfVjPEHINWVg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/runtime-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-js/-/runtime-js-2.8.3.tgz#0baa4c8fbf77eabce05d01ccc186614968ffc0cd"
+  integrity sha512-IRja0vNKwvMtPgIqkBQh0QtRn0XcxNC8HU1jrgWGRckzu10qJWO+5ULgtOeR4pv9krffmMPqywGXw6l/gvJKYQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/runtime-react-refresh@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.8.3.tgz#381a942fb81e8f5ac6c7e0ee1b91dbf34763c3f8"
+  integrity sha512-2v/qFKp00MfG0234OdOgQNAo6TLENpFYZMbVbAsPMY9ITiqG73MrEsrGXVoGbYiGTMB/Toer/lSWlJxtacOCuA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    react-error-overlay "6.0.9"
+    react-refresh "^0.9.0"
+
+"@parcel/runtime-service-worker@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-service-worker/-/runtime-service-worker-2.8.3.tgz#54d92da9ff1dfbd27db0e84164a22fa59e99b348"
+  integrity sha512-/Skkw+EeRiwzOJso5fQtK8c9b452uWLNhQH1ISTodbmlcyB4YalAiSsyHCtMYD0c3/t5Sx4ZS7vxBAtQd0RvOw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/source-map@^2.1.1":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@parcel/source-map/-/source-map-2.1.1.tgz#fb193b82dba6dd62cc7a76b326f57bb35000a782"
+  integrity sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==
+  dependencies:
+    detect-libc "^1.0.3"
+
+"@parcel/transformer-babel@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-babel/-/transformer-babel-2.8.3.tgz#286bc6cb9afe4c0259f0b28e0f2f47322a24b130"
+  integrity sha512-L6lExfpvvC7T/g3pxf3CIJRouQl+sgrSzuWQ0fD4PemUDHvHchSP4SNUVnd6gOytF3Y1KpnEZIunQGi5xVqQCQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    json5 "^2.2.0"
+    nullthrows "^1.1.1"
+    semver "^5.7.0"
+
+"@parcel/transformer-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-css/-/transformer-css-2.8.3.tgz#d6c44100204e73841ad8e0f90472172ea8b9120c"
+  integrity sha512-xTqFwlSXtnaYen9ivAgz+xPW7yRl/u4QxtnDyDpz5dr8gSeOpQYRcjkd4RsYzKsWzZcGtB5EofEk8ayUbWKEUg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    lightningcss "^1.16.1"
+    nullthrows "^1.1.1"
+
+"@parcel/transformer-html@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-html/-/transformer-html-2.8.3.tgz#5c68b28ee6b8c7a13b8aee87f7957ad3227bd83f"
+  integrity sha512-kIZO3qsMYTbSnSpl9cnZog+SwL517ffWH54JeB410OSAYF1ouf4n5v9qBnALZbuCCmPwJRGs4jUtE452hxwN4g==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+    srcset "4"
+
+"@parcel/transformer-image@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-image/-/transformer-image-2.8.3.tgz#73805b2bfc3c8919d7737544e5f8be39e3f303fe"
+  integrity sha512-cO4uptcCGTi5H6bvTrAWEFUsTNhA4kCo8BSvRSCHA2sf/4C5tGQPHt3JhdO0GQLPwZRCh/R41EkJs5HZ8A8DAg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/transformer-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-js/-/transformer-js-2.8.3.tgz#fe400df428394d1e7fe5afb6dea5c7c858e44f03"
+  integrity sha512-9Qd6bib+sWRcpovvzvxwy/PdFrLUXGfmSW9XcVVG8pvgXsZPFaNjnNT8stzGQj1pQiougCoxMY4aTM5p1lGHEQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    "@swc/helpers" "^0.4.12"
+    browserslist "^4.6.6"
+    detect-libc "^1.0.3"
+    nullthrows "^1.1.1"
+    regenerator-runtime "^0.13.7"
+    semver "^5.7.1"
+
+"@parcel/transformer-json@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-json/-/transformer-json-2.8.3.tgz#25deb3a5138cc70a83269fc5d39d564609354d36"
+  integrity sha512-B7LmVq5Q7bZO4ERb6NHtRuUKWGysEeaj9H4zelnyBv+wLgpo4f5FCxSE1/rTNmP9u1qHvQ3scGdK6EdSSokGPg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    json5 "^2.2.0"
+
+"@parcel/transformer-postcss@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-postcss/-/transformer-postcss-2.8.3.tgz#df4fdc1c90893823445f2a8eb8e2bdd0349ccc58"
+  integrity sha512-e8luB/poIlz6jBsD1Izms+6ElbyzuoFVa4lFVLZnTAChI3UxPdt9p/uTsIO46HyBps/Bk8ocvt3J4YF84jzmvg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    clone "^2.1.1"
+    nullthrows "^1.1.1"
+    postcss-value-parser "^4.2.0"
+    semver "^5.7.1"
+
+"@parcel/transformer-posthtml@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-posthtml/-/transformer-posthtml-2.8.3.tgz#7c3912a5a631cb26485f6464e0d6eeabb6f1e718"
+  integrity sha512-pkzf9Smyeaw4uaRLsT41RGrPLT5Aip8ZPcntawAfIo+KivBQUV0erY1IvHYjyfFzq1ld/Fo2Ith9He6mxpPifA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+
+"@parcel/transformer-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-raw/-/transformer-raw-2.8.3.tgz#3a22213fe18a5f83fd78889cb49f06e059cfead7"
+  integrity sha512-G+5cXnd2/1O3nV/pgRxVKZY/HcGSseuhAe71gQdSQftb8uJEURyUHoQ9Eh0JUD3MgWh9V+nIKoyFEZdf9T0sUQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/transformer-react-refresh-wrap@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.8.3.tgz#8b0392638405dd470a886002229f7889d5464822"
+  integrity sha512-q8AAoEvBnCf/nPvgOwFwKZfEl/thwq7c2duxXkhl+tTLDRN2vGmyz4355IxCkavSX+pLWSQ5MexklSEeMkgthg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    react-refresh "^0.9.0"
+
+"@parcel/transformer-svg@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-svg/-/transformer-svg-2.8.3.tgz#4df959cba4ebf45d7aaddd540f752e6e84df38b2"
+  integrity sha512-3Zr/gBzxi1ZH1fftH/+KsZU7w5GqkmxlB0ZM8ovS5E/Pl1lq1t0xvGJue9m2VuQqP8Mxfpl5qLFmsKlhaZdMIQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+
+"@parcel/ts-utils@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/ts-utils/-/ts-utils-2.8.3.tgz#f3590ca033c061779dc35ff3d14af2860ed106ac"
+  integrity sha512-4HMt9B9LF2pDFvSKGImho48tlCvCUl7ly1ZMXvQdmEq2i0yoS81tDsmxX3yly/RVUVeUCGAj1JRuuy1lw5zw1A==
+  dependencies:
+    nullthrows "^1.1.1"
+
+"@parcel/types@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/types/-/types-2.8.3.tgz#3306bc5391b6913bd619914894b8cd84a24b30fa"
+  integrity sha512-FECA1FB7+0UpITKU0D6TgGBpGxYpVSMNEENZbSJxFSajNy3wrko+zwBKQmFOLOiPcEtnGikxNs+jkFWbPlUAtw==
+  dependencies:
+    "@parcel/cache" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/workers" "2.8.3"
+    utility-types "^3.10.0"
+
+"@parcel/utils@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.8.3.tgz#0d56c9e8e22c119590a5e044a0e01031965da40e"
+  integrity sha512-IhVrmNiJ+LOKHcCivG5dnuLGjhPYxQ/IzbnF2DKNQXWBTsYlHkJZpmz7THoeLtLliGmSOZ3ZCsbR8/tJJKmxjA==
+  dependencies:
+    "@parcel/codeframe" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/markdown-ansi" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    chalk "^4.1.0"
+
+"@parcel/validator-typescript@^2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/validator-typescript/-/validator-typescript-2.8.3.tgz#6f9cb5b48df302b1d65e9b17dc1a20870e746976"
+  integrity sha512-2UYGCAwrxh7HIGcrXl8Vu9Sisd8vAu/6Jp/oJV5n9ZQuT5O9pQAlK2lZGSocYRucBtmb4WajII2S2GTzUZeEuQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/ts-utils" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/watcher@^2.0.7":
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545"
+  integrity sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==
+  dependencies:
+    is-glob "^4.0.3"
+    micromatch "^4.0.5"
+    node-addon-api "^3.2.1"
+    node-gyp-build "^4.3.0"
+
+"@parcel/workers@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-2.8.3.tgz#255450ccf4db234082407e4ddda5fd575f08c235"
+  integrity sha512-+AxBnKgjqVpUHBcHLWIHcjYgKIvHIpZjN33mG5LG9XXvrZiqdWvouEzqEXlVLq5VzzVbKIQQcmsvRy138YErkg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chrome-trace-event "^1.0.2"
+    nullthrows "^1.1.1"
+
+"@swc/helpers@^0.4.12":
+  version "0.4.14"
+  resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
+  integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==
+  dependencies:
+    tslib "^2.4.0"
+
+"@trysound/sax@0.2.0":
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
+  integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
+
+"@types/parse-json@^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
+  integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+
+abortcontroller-polyfill@^1.1.9:
+  version "1.7.5"
+  resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed"
+  integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==
+
+acorn@^8.5.0:
+  version "8.8.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
+  integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
+
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+base-x@^3.0.8:
+  version "3.0.9"
+  resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320"
+  integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==
+  dependencies:
+    safe-buffer "^5.0.1"
+
+boolbase@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
+braces@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+  dependencies:
+    fill-range "^7.0.1"
+
+browserslist@^4.6.6:
+  version "4.21.4"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
+  integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
+  dependencies:
+    caniuse-lite "^1.0.30001400"
+    electron-to-chromium "^1.4.251"
+    node-releases "^2.0.6"
+    update-browserslist-db "^1.0.9"
+
+buffer-from@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+  integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+caniuse-lite@^1.0.30001400:
+  version "1.0.30001446"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz#6d4ba828ab19f49f9bcd14a8430d30feebf1e0c5"
+  integrity sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==
+
+chalk@^2.0.0:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^4.1.0:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chart.js@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.2.0.tgz#dd281b2ce890bff32f3e249cf2972a1e74bc032c"
+  integrity sha512-wbtcV+QKeH0F7gQZaCJEIpsNriFheacouJQTVIjITi3eQA8bTlIBoknz0+dgV79aeKLNMAX+nDslIVE/nJ3rzA==
+  dependencies:
+    "@kurkle/color" "^0.3.0"
+
+chartjs-adapter-date-fns@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz#c25f63c7f317c1f96f9a7c44bd45eeedb8a478e5"
+  integrity sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==
+
+chrome-trace-event@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
+  integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
+
+clone@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
+  integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
+
+color-convert@^1.9.0:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@^7.0.0, commander@^7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+  integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
+cosmiconfig@^7.0.1:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
+  integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
+  dependencies:
+    "@types/parse-json" "^4.0.0"
+    import-fresh "^3.2.1"
+    parse-json "^5.0.0"
+    path-type "^4.0.0"
+    yaml "^1.10.0"
+
+css-select@^4.1.3:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
+  integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^6.0.1"
+    domhandler "^4.3.1"
+    domutils "^2.8.0"
+    nth-check "^2.0.1"
+
+css-tree@^1.1.2, css-tree@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
+  integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
+  dependencies:
+    mdn-data "2.0.14"
+    source-map "^0.6.1"
+
+css-what@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+  integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
+csso@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
+  integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
+  dependencies:
+    css-tree "^1.1.2"
+
+date-fns@^2.29.3:
+  version "2.29.3"
+  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
+  integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
+
+detect-libc@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+  integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
+
+dom-serializer@^1.0.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
+  integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.2.0"
+    entities "^2.0.0"
+
+domelementtype@^2.0.1, domelementtype@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+  integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^4.2.0, domhandler@^4.2.2, domhandler@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
+  integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
+  dependencies:
+    domelementtype "^2.2.0"
+
+domutils@^2.8.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
+  integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
+  dependencies:
+    dom-serializer "^1.0.1"
+    domelementtype "^2.2.0"
+    domhandler "^4.2.0"
+
+dotenv-expand@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
+  integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
+
+dotenv@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c"
+  integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==
+
+electron-to-chromium@^1.4.251:
+  version "1.4.284"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592"
+  integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==
+
+entities@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
+  integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+
+entities@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
+  integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
+
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+escalade@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+get-port@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119"
+  integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==
+
+globals@^13.2.0:
+  version "13.19.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8"
+  integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==
+  dependencies:
+    type-fest "^0.20.2"
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+htmlnano@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-2.0.3.tgz#50ee639ed63357d4a6c01309f52a35892e4edc2e"
+  integrity sha512-S4PGGj9RbdgW8LhbILNK7W9JhmYP8zmDY7KDV/8eCiJBQJlbmltp5I0gv8c5ntLljfdxxfmJ+UJVSqyH4mb41A==
+  dependencies:
+    cosmiconfig "^7.0.1"
+    posthtml "^0.16.5"
+    timsort "^0.3.0"
+
+htmlparser2@^7.1.1:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5"
+  integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.2.2"
+    domutils "^2.8.0"
+    entities "^3.0.1"
+
+import-fresh@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-glob@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-json@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-json/-/is-json-2.0.1.tgz#6be166d144828a131d686891b983df62c39491ff"
+  integrity sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+json-parse-even-better-errors@^2.3.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+  integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json5@^2.2.0, json5@^2.2.1:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+lightningcss-darwin-arm64@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.18.0.tgz#bcd7d494d99c69947abd71136a42e80dfa80c682"
+  integrity sha512-OqjydwtiNPgdH1ByIjA1YzqvDG/OMR6L3LPN6wRl1729LB0y4Mik7L06kmZaTb+pvUHr+NmDd2KCwnlrQ4zO3w==
+
+lightningcss-darwin-x64@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.18.0.tgz#952abea2405fe2bb8dd0bb57a9d5590f8d1d6414"
+  integrity sha512-mNiuPHj89/JHZmJMp+5H8EZSt6EL5DZRWJ31O6k3DrLLnRIQjXuXdDdN8kP7LoIkeWI5xvyD60CsReJm+YWYAw==
+
+lightningcss-linux-arm-gnueabihf@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.18.0.tgz#23ca85e05dc4def9b4975aef307554ef292b56cd"
+  integrity sha512-S+25JjI6601HiAVoTDXW6SqH+E94a+FHA7WQqseyNHunOgVWKcAkNEc2LJvVxgwTq6z41sDIb9/M3Z9wa9lk4A==
+
+lightningcss-linux-arm64-gnu@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.18.0.tgz#6c8e0a6e2c8b44cf180f3a0f0740402e8f656155"
+  integrity sha512-JSqh4+21dCgBecIQUet35dtE4PhhSEMyqe3y0ZNQrAJQ5kyUPSQHiw81WXnPJcOSTTpG0TyMLiC8K//+BsFGQA==
+
+lightningcss-linux-arm64-musl@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.18.0.tgz#88393c101cf236ea0cdc97fddd66b82db964d835"
+  integrity sha512-2FWHa8iUhShnZnqhn2wfIcK5adJat9hAAaX7etNsoXJymlliDIOFuBQEsba2KBAZSM4QqfQtvRdR7m8i0I7ybQ==
+
+lightningcss-linux-x64-gnu@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.18.0.tgz#ad068d24836568337bfe545650565e13f813c8ee"
+  integrity sha512-plCPGQJtDZHcLVKVRLnQVF2XRsIC32WvuJhQ7fJ7F6BV98b/VZX0OlX05qUaOESD9dCDHjYSfxsgcvOKgCWh7A==
+
+lightningcss-linux-x64-musl@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.18.0.tgz#4d84de26b8185aa42450e0f4c83bbfb5a36ae750"
+  integrity sha512-na+BGtVU6fpZvOHKhnlA0XHeibkT3/46nj6vLluG3kzdJYoBKU6dIl7DSOk++8jv4ybZyFJ0aOFMMSc8g2h58A==
+
+lightningcss-win32-x64-msvc@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.18.0.tgz#f83952d16b83dfce65f4615f87c867769220d117"
+  integrity sha512-5qeAH4RMNy2yMNEl7e5TI6upt/7xD2ZpHWH4RkT8iJ7/6POS5mjHbXWUO9Q1hhDhqkdzGa76uAdMzEouIeCyNw==
+
+lightningcss@^1.16.1:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.18.0.tgz#ca3327a1a7571a83bbb9733ed4e4cded775bdadf"
+  integrity sha512-uk10tNxi5fhZqU93vtYiQgx/8a9f0Kvtj5AXIm+VlOXY+t/DWDmCZWJEkZJmmALgvbS6aAW8or+Kq85eJ6TDTw==
+  dependencies:
+    detect-libc "^1.0.3"
+  optionalDependencies:
+    lightningcss-darwin-arm64 "1.18.0"
+    lightningcss-darwin-x64 "1.18.0"
+    lightningcss-linux-arm-gnueabihf "1.18.0"
+    lightningcss-linux-arm64-gnu "1.18.0"
+    lightningcss-linux-arm64-musl "1.18.0"
+    lightningcss-linux-x64-gnu "1.18.0"
+    lightningcss-linux-x64-musl "1.18.0"
+    lightningcss-win32-x64-msvc "1.18.0"
+
+lines-and-columns@^1.1.6:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+  integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+lmdb@2.5.2:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.5.2.tgz#37e28a9fb43405f4dc48c44cec0e13a14c4a6ff1"
+  integrity sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA==
+  dependencies:
+    msgpackr "^1.5.4"
+    node-addon-api "^4.3.0"
+    node-gyp-build-optional-packages "5.0.3"
+    ordered-binary "^1.2.4"
+    weak-lru-cache "^1.2.2"
+  optionalDependencies:
+    "@lmdb/lmdb-darwin-arm64" "2.5.2"
+    "@lmdb/lmdb-darwin-x64" "2.5.2"
+    "@lmdb/lmdb-linux-arm" "2.5.2"
+    "@lmdb/lmdb-linux-arm64" "2.5.2"
+    "@lmdb/lmdb-linux-x64" "2.5.2"
+    "@lmdb/lmdb-win32-x64" "2.5.2"
+
+loose-envify@^1.1.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+mdn-data@2.0.14:
+  version "2.0.14"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
+  integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
+
+micromatch@^4.0.5:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+  dependencies:
+    braces "^3.0.2"
+    picomatch "^2.3.1"
+
+msgpackr-extract@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.2.0.tgz#4bb749b58d9764cfdc0d91c7977a007b08e8f262"
+  integrity sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==
+  dependencies:
+    node-gyp-build-optional-packages "5.0.3"
+  optionalDependencies:
+    "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-arm" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-x64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-win32-x64" "2.2.0"
+
+msgpackr@^1.5.4:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.1.tgz#2298aed8a14f83e99df77d344cbda3e436f29b5b"
+  integrity sha512-05fT4J8ZqjYlR4QcRDIhLCYKUOHXk7C/xa62GzMKj74l3up9k2QZ3LgFc6qWdsPHl91QA2WLWqWc8b8t7GLNNw==
+  optionalDependencies:
+    msgpackr-extract "^2.2.0"
+
+node-addon-api@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
+  integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
+
+node-addon-api@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
+  integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
+
+node-gyp-build-optional-packages@5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17"
+  integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==
+
+node-gyp-build@^4.3.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055"
+  integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==
+
+node-releases@^2.0.6:
+  version "2.0.8"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae"
+  integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==
+
+nth-check@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+  integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+  dependencies:
+    boolbase "^1.0.0"
+
+nullthrows@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
+  integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==
+
+ordered-binary@^1.2.4:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.0.tgz#6bb53d44925f3b8afc33d1eed0fa15693b211389"
+  integrity sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ==
+
+parcel@^2.8.3:
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.8.3.tgz#1ff71d7317274fd367379bc7310a52c6b75d30c2"
+  integrity sha512-5rMBpbNE72g6jZvkdR5gS2nyhwIXaJy8i65osOqs/+5b7zgf3eMKgjSsDrv6bhz3gzifsba6MBJiZdBckl+vnA==
+  dependencies:
+    "@parcel/config-default" "2.8.3"
+    "@parcel/core" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/reporter-cli" "2.8.3"
+    "@parcel/reporter-dev-server" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chalk "^4.1.0"
+    commander "^7.0.0"
+    get-port "^4.2.0"
+    v8-compile-cache "^2.0.0"
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+parse-json@^5.0.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+  integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    error-ex "^1.3.1"
+    json-parse-even-better-errors "^2.3.0"
+    lines-and-columns "^1.1.6"
+
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picocolors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+postcss-value-parser@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+  integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+posthtml-parser@^0.10.1:
+  version "0.10.2"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.10.2.tgz#df364d7b179f2a6bf0466b56be7b98fd4e97c573"
+  integrity sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==
+  dependencies:
+    htmlparser2 "^7.1.1"
+
+posthtml-parser@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.11.0.tgz#25d1c7bf811ea83559bc4c21c189a29747a24b7a"
+  integrity sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==
+  dependencies:
+    htmlparser2 "^7.1.1"
+
+posthtml-render@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-3.0.0.tgz#97be44931496f495b4f07b99e903cc70ad6a3205"
+  integrity sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==
+  dependencies:
+    is-json "^2.0.1"
+
+posthtml@^0.16.4, posthtml@^0.16.5:
+  version "0.16.6"
+  resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.16.6.tgz#e2fc407f67a64d2fa3567afe770409ffdadafe59"
+  integrity sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==
+  dependencies:
+    posthtml-parser "^0.11.0"
+    posthtml-render "^3.0.0"
+
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
+
+react-dom@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+  integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
+  dependencies:
+    loose-envify "^1.1.0"
+    scheduler "^0.23.0"
+
+react-error-overlay@6.0.9:
+  version "6.0.9"
+  resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
+  integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
+
+react-refresh@^0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf"
+  integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==
+
+react@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+  integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
+  dependencies:
+    loose-envify "^1.1.0"
+
+regenerator-runtime@^0.13.7:
+  version "0.13.11"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
+  integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+safe-buffer@^5.0.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+scheduler@^0.23.0:
+  version "0.23.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+  integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
+  dependencies:
+    loose-envify "^1.1.0"
+
+semver@^5.7.0, semver@^5.7.1:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+source-map-support@~0.5.20:
+  version "0.5.21"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+  integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map@^0.6.0, source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+srcset@4:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4"
+  integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==
+
+stable@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+svgo@^2.4.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
+  integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
+  dependencies:
+    "@trysound/sax" "0.2.0"
+    commander "^7.2.0"
+    css-select "^4.1.3"
+    css-tree "^1.1.3"
+    csso "^4.2.0"
+    picocolors "^1.0.0"
+    stable "^0.1.8"
+
+term-size@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
+  integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
+
+terser@^5.2.0:
+  version "5.16.1"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.1.tgz#5af3bc3d0f24241c7fb2024199d5c461a1075880"
+  integrity sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==
+  dependencies:
+    "@jridgewell/source-map" "^0.3.2"
+    acorn "^8.5.0"
+    commander "^2.20.0"
+    source-map-support "~0.5.20"
+
+timsort@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
+  integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+tslib@^2.4.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
+  integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
+
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+typescript@>=3.0.0:
+  version "4.9.4"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78"
+  integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
+
+update-browserslist-db@^1.0.9:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
+  integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==
+  dependencies:
+    escalade "^3.1.1"
+    picocolors "^1.0.0"
+
+utility-types@^3.10.0:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b"
+  integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==
+
+v8-compile-cache@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+  integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
+weak-lru-cache@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19"
+  integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==
+
+xxhash-wasm@^0.4.2:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz#752398c131a4dd407b5132ba62ad372029be6f79"
+  integrity sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==
+
+yaml@^1.10.0:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
+  integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
diff --git a/users/wpcarro/zoo/Main.hs b/users/wpcarro/zoo/Main.hs
index 407d29e61e..c18edbed96 100644
--- a/users/wpcarro/zoo/Main.hs
+++ b/users/wpcarro/zoo/Main.hs
@@ -157,4 +157,4 @@ parseInput x =
     _ -> Nothing
 
 main :: IO ()
-main = Warp.run 8000 $ serve (Proxy @ Api) server
+main = Warp.run 8000 $ serve (Proxy @Api) server
diff --git a/users/zseri/OWNERS b/users/zseri/OWNERS
deleted file mode 100644
index 4f56571214..0000000000
--- a/users/zseri/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherited: false
-owners:
-  - zseri
diff --git a/users/zseri/store-ref-scanner/Cargo.toml b/users/zseri/store-ref-scanner/Cargo.toml
deleted file mode 100644
index ad565f09af..0000000000
--- a/users/zseri/store-ref-scanner/Cargo.toml
+++ /dev/null
@@ -1,6 +0,0 @@
-[package]
-name = "store-ref-scanner"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
diff --git a/users/zseri/store-ref-scanner/tests.nix b/users/zseri/store-ref-scanner/tests.nix
deleted file mode 100644
index a4c82fe3a9..0000000000
--- a/users/zseri/store-ref-scanner/tests.nix
+++ /dev/null
@@ -1,32 +0,0 @@
-{ depot, lib, pkgs, ... }:
-
-let
-  parent = depot.users.zseri.store-ref-scanner;
-in
-pkgs.buildRustCrate {
-  pname = "store-ref-scanner-tests";
-  inherit (parent) crateName src version edition;
-  buildTests = true;
-  postInstall = ''
-    set -ex
-    export RUST_BACKTRACE=1
-    # recreate a file hierarchy as when running tests with cargo
-    # the source for test data
-    # build outputs
-    testRoot=target/debug
-    mkdir -p $testRoot
-    chmod +w -R .
-    # test harness executables are suffixed with a hash,
-    # like cargo does this allows to prevent name collision
-    # with the main executables of the crate
-    hash=$(basename $out)
-    ls -lasR $out
-    for file in $out/tests/*; do
-      f=$testRoot/$(basename $file)-$hash
-      cp $file $f
-      $f 2>&1 | tee -a $out/tests.log
-    done
-    rm -rf $out/tests
-    set +ex
-  '';
-}
diff --git a/views/README.md b/views/README.md
index 83464d5ee2..a6ebd93a77 100644
--- a/views/README.md
+++ b/views/README.md
@@ -3,4 +3,29 @@ depot views
 
 This folder contains external views of depot content, defined using
 josh workspaces. See the individual views for a description of their
-content and usage information.
+individual content and usage information.
+
+Testing changes locally
+-----------------------
+
+Generally, when iterating on these files, it's best to locally invoke `josh-
+filter` (from `//third_party//josh`) locally to inspect how the workspace would
+look like:
+
+  - Commit your changes. This is required, as `josh-filter` operates on your
+    `HEAD`, not working directory state.
+  - Invoke `josh-filter` with the filter expression,
+    for example `josh-filter ':workspace=views/tvix'`.
+  - Peek at the synthesized git history by looking at `FILTERED_HEAD`.
+
+Testing changes in Gerrit
+-------------------------
+
+It's also possible to clone resulting workspaces for CLs that were already
+pushed to Gerrit, but didn't land in master yet.
+
+For CL1234 at revision 2, the URL passed to `git clone` would look like this:
+
+```
+https://code.tvl.fyi/depot.git@refs/changes/32/1234/2:workspace=views/kit.git
+````
diff --git a/views/default.nix b/views/default.nix
new file mode 100644
index 0000000000..83c2782719
--- /dev/null
+++ b/views/default.nix
@@ -0,0 +1,26 @@
+# Export configuration for the views.
+{ depot, pkgs, ... }:
+
+let
+  export-tvix = depot.tools.releases.filteredGitPush {
+    filter = ":workspace=views/tvix";
+    remote = "git@github.com:tvlfyi/tvix.git";
+    ref = "refs/heads/canon";
+  };
+
+  export-kit = depot.tools.releases.filteredGitPush {
+    filter = ":unsign:workspace=views/kit";
+    remote = "git@github.com:tvlfyi/kit.git";
+    ref = "refs/heads/canon";
+  };
+in
+(pkgs.runCommandLocal "export-views" { }
+  ''
+    echo "no-op carrier target for repo export steps" | tee $out
+  '').overrideAttrs
+  (_: {
+    meta.ci.extraSteps = {
+      inherit export-tvix export-kit;
+    };
+  })
+
diff --git a/views/kit/README.md b/views/kit/README.md
index 491a97c0c4..95a38e7382 100644
--- a/views/kit/README.md
+++ b/views/kit/README.md
@@ -12,10 +12,12 @@ tooling, currently comprising of:
 * `readTree`: Nix library to dynamically compute attribute trees
   corresponding to the physical layout of a repository.
 * `besadii`: Configurable Gerrit/Buildkite integration hook.
+* `magrathea`: Command-line tool for working with TVL-style monorepos
+* `checks`: Collection of useful CI checks for Buildkite
 
 It can be accessed via git by cloning it as such:
 
-    git clone https://code.tvl.fyi/depot.git:workspace=views/kit.git tvl-kit
+    git clone https://code.tvl.fyi/depot.git:unsign:workspace=views/kit.git tvl-kit
 
 If you are looking at this within the TVL depot, you can see the
 [josh][] configuration in `workspace.josh`. You will find the projects
diff --git a/views/kit/buildkite.yml b/views/kit/buildkite.yml
index cf6795f211..0bba63726d 100644
--- a/views/kit/buildkite.yml
+++ b/views/kit/buildkite.yml
@@ -6,3 +6,11 @@
 steps:
   - command: "nix-build --no-out-link -A besadii"
     label: ":nix: besadii"
+
+  - command: "nix-build --no-out-link -A magrathea"
+    label: ":nix: magrathea"
+
+  - label: ":nix: lazy-deps"
+    command: |
+      nix-build -E 'with import ./. {}; lazy-deps { mg.attr = "magrathea"; }'
+      result/bin/mg
diff --git a/views/kit/default.nix b/views/kit/default.nix
index 759b36997a..bb4b37e36c 100644
--- a/views/kit/default.nix
+++ b/views/kit/default.nix
@@ -6,14 +6,39 @@
 # it simple. Adding it may be useful if we set up test scaffolding
 # around the exported workspace.
 
-{ pkgs ? import ./nixpkgs { depotOverlays = false; }, ... }:
-
-pkgs.lib.fix(self: {
-  buildGo = import ./buildGo { inherit pkgs; };
-  buildkite = import ./buildkite { inherit pkgs; };
-  readTree = import ./readTree { };
+{ pkgs ? (import ./nixpkgs {
+    depotOverlays = false;
+    depot.third_party.sources = import ./sources { };
+    externalArgs = args;
+  })
+, ...
+}@args:
 
+pkgs.lib.fix (self: {
   besadii = import ./besadii {
     depot.nix.buildGo = self.buildGo;
   };
+
+  buildGo = import ./buildGo { inherit pkgs; };
+
+  buildkite = import ./buildkite {
+    inherit pkgs;
+    depot.nix = {
+      inherit (self) readTree dependency-analyzer;
+    };
+  };
+
+  checks = import ./checks { inherit pkgs; };
+  dependency-analyzer = import ./dependency-analyzer {
+    inherit pkgs;
+    inherit (pkgs) lib;
+    depot.nix.stateMonad = self.stateMonad;
+  };
+  lazy-deps = import ./lazy-deps {
+    inherit pkgs;
+    lib = pkgs.lib;
+  };
+  magrathea = import ./magrathea { inherit pkgs; };
+  readTree = import ./readTree { };
+  stateMonad = import ./stateMonad { };
 })
diff --git a/views/kit/workspace.josh b/views/kit/workspace.josh
index 33ca3e22e6..63b3cd49b4 100644
--- a/views/kit/workspace.josh
+++ b/views/kit/workspace.josh
@@ -3,6 +3,14 @@ besadii = :/ops/besadii
 :/nix:[
     ::buildGo/
     ::buildkite/
+    ::dependency-analyzer/
+    ::lazy-deps/
     ::readTree/
+    ::stateMonad/
 ]
-nixpkgs = :/third_party/nixpkgs
+:/third_party:[
+    ::nixpkgs/
+    ::sources/
+]
+magrathea = :/tools/magrathea
+checks = :/tools/checks
diff --git a/views/tvix/workspace.josh b/views/tvix/workspace.josh
new file mode 100644
index 0000000000..57d5f8a8c4
--- /dev/null
+++ b/views/tvix/workspace.josh
@@ -0,0 +1,9 @@
+:/third_party:[
+    ::nixpkgs/,
+    ::sources/
+]
+::tvix:exclude[
+    ::tvix/default.nix
+    ::tvix/**/default.nix:exclude[::tvix/eval/src/tests/tvix_tests/directory/default.nix]
+]:/tvix
+::third_party/overlays/patches/cbtemulator-uds.patch:/third_party/overlays/patches:prefix=nixpkgs
diff --git a/web/atom-feed/default.nix b/web/atom-feed/default.nix
index 1fbcde9bd4..27c90a7b91 100644
--- a/web/atom-feed/default.nix
+++ b/web/atom-feed/default.nix
@@ -7,7 +7,7 @@ with depot.nix.yants;
 let
   inherit (builtins) foldl' map readFile replaceStrings sort;
   inherit (lib) concatStrings concatStringsSep max removeSuffix;
-  inherit (pkgs) runCommandNoCC;
+  inherit (pkgs) runCommand;
 
   # 'link' describes a related link to a feed, or feed element.
   #
@@ -90,7 +90,7 @@ let
 
   # Feed generation functions:
 
-  renderEpoch = epoch: removeSuffix "\n" (readFile (runCommandNoCC "date-${toString epoch}" {} ''
+  renderEpoch = epoch: removeSuffix "\n" (readFile (runCommand "date-${toString epoch}" { } ''
     date --date='@${toString epoch}' --utc --iso-8601='seconds' > $out
   ''));
 
@@ -147,6 +147,7 @@ let
       ${concatStrings (map renderEntry (sortEntries f.entries))}
     </feed>
   '');
-in {
+in
+{
   inherit entry feed renderFeed renderEpoch;
 }
diff --git a/web/atward/Cargo.lock b/web/atward/Cargo.lock
index 0eb7ae9508..8b0ef04f5e 100644
--- a/web/atward/Cargo.lock
+++ b/web/atward/Cargo.lock
@@ -1,5 +1,7 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
 [[package]]
 name = "adler32"
 version = "1.2.0"
@@ -8,33 +10,48 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.18"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "alloc-no-stdlib"
-version = "2.0.3"
+version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
 
 [[package]]
 name = "alloc-stdlib"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
 dependencies = [
  "alloc-no-stdlib",
 ]
 
 [[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "ascii"
-version = "1.0.0"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109"
+checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
 
 [[package]]
 name = "atward"
@@ -46,15 +63,15 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "base64"
-version = "0.13.0"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "bitflags"
@@ -63,10 +80,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
 name = "brotli"
-version = "3.3.2"
+version = "3.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71cb90ade945043d3d53597b2fc359bb063db8ade2bcffe7997351d0756e9d50"
+checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f"
 dependencies = [
  "alloc-no-stdlib",
  "alloc-stdlib",
@@ -75,9 +98,9 @@ dependencies = [
 
 [[package]]
 name = "brotli-decompressor"
-version = "2.3.2"
+version = "2.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80"
+checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
 dependencies = [
  "alloc-no-stdlib",
  "alloc-stdlib",
@@ -94,6 +117,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "cc"
+version = "1.0.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "cfg-if"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -101,68 +139,98 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.19"
+version = "0.4.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
 dependencies = [
- "libc",
- "num-integer",
+ "android-tzdata",
+ "iana-time-zone",
  "num-traits",
- "winapi",
+ "windows-targets",
 ]
 
 [[package]]
 name = "chunked_transfer"
-version = "1.4.0"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
+checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
 
 [[package]]
 name = "crc32fast"
-version = "1.3.0"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "deflate"
-version = "0.9.1"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f95bf05dffba6e6cce8dfbb30def788154949ccd9aed761b472119c21e01c70"
+checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f"
 dependencies = [
  "adler32",
  "gzip-header",
 ]
 
 [[package]]
+name = "deranged"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+
+[[package]]
 name = "filetime"
-version = "0.2.15"
+version = "0.2.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
+checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall",
- "winapi",
+ "redox_syscall 0.3.5",
+ "windows-sys",
 ]
 
 [[package]]
 name = "form_urlencoded"
-version = "1.0.1"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
 dependencies = [
- "matches",
  "percent-encoding",
 ]
 
 [[package]]
 name = "getrandom"
-version = "0.2.3"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
 dependencies = [
  "cfg-if",
  "libc",
@@ -171,83 +239,114 @@ dependencies = [
 
 [[package]]
 name = "gzip-header"
-version = "0.3.0"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0131feb3d3bb2a5a238d8a4d09f6353b7ebfdc52e77bccbf4ea6eaa751dde639"
+checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2"
 dependencies = [
  "crc32fast",
 ]
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
 dependencies = [
- "libc",
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
 ]
 
 [[package]]
-name = "httparse"
-version = "1.5.1"
+name = "iana-time-zone-haiku"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
 
 [[package]]
 name = "idna"
-version = "0.2.3"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
+checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
 dependencies = [
- "matches",
  "unicode-bidi",
  "unicode-normalization",
 ]
 
 [[package]]
 name = "itoa"
-version = "1.0.1"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "js-sys"
+version = "0.3.65"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
+checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+dependencies = [
+ "wasm-bindgen",
+]
 
 [[package]]
 name = "libc"
-version = "0.2.112"
+version = "0.2.150"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
 
 [[package]]
-name = "log"
-version = "0.4.14"
+name = "linux-raw-sys"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
-dependencies = [
- "cfg-if",
-]
+checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
 
 [[package]]
-name = "matches"
-version = "0.1.9"
+name = "log"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
 [[package]]
 name = "memchr"
-version = "2.4.1"
+version = "2.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
 
 [[package]]
 name = "mime"
-version = "0.3.16"
+version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
 [[package]]
 name = "mime_guess"
-version = "2.0.3"
+version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
 dependencies = [
  "mime",
  "unicase",
@@ -272,53 +371,64 @@ dependencies = [
 ]
 
 [[package]]
-name = "num-integer"
-version = "0.1.44"
+name = "num-traits"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
 dependencies = [
  "autocfg",
- "num-traits",
 ]
 
 [[package]]
-name = "num-traits"
-version = "0.2.14"
+name = "num_cpus"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "autocfg",
+ "hermit-abi",
+ "libc",
 ]
 
 [[package]]
-name = "num_cpus"
-version = "1.13.1"
+name = "num_threads"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
 dependencies = [
- "hermit-abi",
  "libc",
 ]
 
 [[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
 name = "percent-encoding"
-version = "2.1.0"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.16"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.36"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
 dependencies = [
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -329,23 +439,22 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 
 [[package]]
 name = "quote"
-version = "1.0.14"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "rand"
-version = "0.8.4"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
  "rand_chacha",
  "rand_core",
- "rand_hc",
 ]
 
 [[package]]
@@ -360,62 +469,65 @@ dependencies = [
 
 [[package]]
 name = "rand_core"
-version = "0.6.3"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
  "getrandom",
 ]
 
 [[package]]
-name = "rand_hc"
-version = "0.3.1"
+name = "redox_syscall"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
 dependencies = [
- "rand_core",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.10"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.4"
+version = "1.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
 dependencies = [
  "aho-corasick",
  "memchr",
+ "regex-automata",
  "regex-syntax",
 ]
 
 [[package]]
-name = "regex-syntax"
-version = "0.6.25"
+name = "regex-automata"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
 
 [[package]]
-name = "remove_dir_all"
-version = "0.5.3"
+name = "regex-syntax"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
 
 [[package]]
 name = "rouille"
-version = "3.5.0"
+version = "3.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18b2380c42510ef4a28b5f228a174c801e0dec590103e215e60812e2e2f34d05"
+checksum = "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921"
 dependencies = [
  "base64",
  "brotli",
@@ -423,13 +535,12 @@ dependencies = [
  "deflate",
  "filetime",
  "multipart",
- "num_cpus",
  "percent-encoding",
  "rand",
  "serde",
  "serde_derive",
  "serde_json",
- "sha1",
+ "sha1_smol",
  "threadpool",
  "time",
  "tiny_http",
@@ -437,10 +548,23 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustix"
+version = "0.38.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+dependencies = [
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
 name = "ryu"
-version = "1.0.9"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
 
 [[package]]
 name = "safemem"
@@ -450,15 +574,18 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
 
 [[package]]
 name = "serde"
-version = "1.0.132"
+version = "1.0.192"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
+checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+dependencies = [
+ "serde_derive",
+]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.132"
+version = "1.0.192"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
+checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -467,9 +594,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.73"
+version = "1.0.108"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
 dependencies = [
  "itoa",
  "ryu",
@@ -477,34 +604,33 @@ dependencies = [
 ]
 
 [[package]]
-name = "sha1"
-version = "0.6.0"
+name = "sha1_smol"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
 
 [[package]]
 name = "syn"
-version = "1.0.84"
+version = "2.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
 dependencies = [
  "proc-macro2",
  "quote",
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
 name = "tempfile"
-version = "3.2.0"
+version = "3.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
 dependencies = [
  "cfg-if",
- "libc",
- "rand",
- "redox_syscall",
- "remove_dir_all",
- "winapi",
+ "fastrand",
+ "redox_syscall 0.4.1",
+ "rustix",
+ "windows-sys",
 ]
 
 [[package]]
@@ -518,40 +644,50 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.5"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad"
+checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
 dependencies = [
+ "deranged",
  "libc",
+ "num_threads",
+ "powerfmt",
+ "serde",
+ "time-core",
 ]
 
 [[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
 name = "tiny_http"
-version = "0.8.2"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce51b50006056f590c9b7c3808c3bd70f0d1101666629713866c227d6e58d39"
+checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
 dependencies = [
  "ascii",
- "chrono",
  "chunked_transfer",
+ "httpdate",
  "log",
- "url",
 ]
 
 [[package]]
 name = "tinyvec"
-version = "1.5.1"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
 dependencies = [
  "tinyvec_macros",
 ]
 
 [[package]]
 name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "twoway"
@@ -564,43 +700,42 @@ dependencies = [
 
 [[package]]
 name = "unicase"
-version = "2.6.0"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
 dependencies = [
  "version_check",
 ]
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.7"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.19"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
 dependencies = [
  "tinyvec",
 ]
 
 [[package]]
-name = "unicode-xid"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
-[[package]]
 name = "url"
-version = "2.2.2"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
+checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
 dependencies = [
  "form_urlencoded",
  "idna",
- "matches",
  "percent-encoding",
 ]
 
@@ -612,28 +747,135 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
-name = "winapi"
-version = "0.3.9"
+name = "wasm-bindgen"
+version = "0.2.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
 dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "cfg-if",
+ "wasm-bindgen-macro",
 ]
 
 [[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
+name = "wasm-bindgen-backend"
+version = "0.2.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
 
 [[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "wasm-bindgen-macro"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
+
+[[package]]
+name = "windows-core"
+version = "0.51.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/web/atward/build.rs b/web/atward/build.rs
index 5dadba3bf3..90a2f35cd1 100644
--- a/web/atward/build.rs
+++ b/web/atward/build.rs
@@ -28,14 +28,19 @@ fn main() {
 
     // Otherwise ask Nix to build it and inject the result.
     let output = Command::new("nix-build")
-        .arg("-A").arg("web.atward.indexHtml")
+        .arg("-A")
+        .arg("web.atward.indexHtml")
         // ... assuming atward is at //web/atward ...
         .arg("../..")
         .output()
         .expect(ERROR_MESSAGE);
 
     if !output.status.success() {
-        eprintln!("{}\nNix output: {}", ERROR_MESSAGE, String::from_utf8_lossy(&output.stderr));
+        eprintln!(
+            "{}\nNix output: {}",
+            ERROR_MESSAGE,
+            String::from_utf8_lossy(&output.stderr)
+        );
         return;
     }
 
diff --git a/web/atward/src/main.rs b/web/atward/src/main.rs
index 26d79cde1a..eb2603a226 100644
--- a/web/atward/src/main.rs
+++ b/web/atward/src/main.rs
@@ -8,6 +8,9 @@ use regex::Regex;
 use rouille::input::cookies;
 use rouille::{Request, Response};
 
+#[cfg(test)]
+mod tests;
+
 /// A query handler supported by atward. It consists of a pattern on
 /// which to match and trigger the query, and a function to execute
 /// that returns the target URL.
@@ -195,194 +198,3 @@ fn main() {
         })
     });
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn bug_query() {
-        assert_eq!(
-            dispatch(&handlers(), &"b/42".into()),
-            Some("https://b.tvl.fyi/42".to_string())
-        );
-
-        assert_eq!(
-            dispatch(&handlers(), &"something only mentioning b/42".into()),
-            None,
-        );
-        assert_eq!(dispatch(&handlers(), &"b/invalid".into()), None,);
-    }
-
-    #[test]
-    fn cl_query() {
-        assert_eq!(
-            dispatch(&handlers(), &"cl/42".into()),
-            Some("https://cl.tvl.fyi/42".to_string())
-        );
-
-        assert_eq!(
-            dispatch(&handlers(), &"something only mentioning cl/42".into()),
-            None,
-        );
-        assert_eq!(dispatch(&handlers(), &"cl/invalid".into()), None,);
-    }
-
-    #[test]
-    fn depot_path_cgit_query() {
-        assert_eq!(
-            dispatch(&handlers(), &"//web/atward/default.nix".into()),
-            Some("https://code.tvl.fyi/tree/web/atward/default.nix".to_string()),
-        );
-
-        assert_eq!(
-            dispatch(&handlers(), &"//nix/readTree/README.md".into()),
-            Some("https://code.tvl.fyi/about/nix/readTree/README.md".to_string()),
-        );
-
-        assert_eq!(dispatch(&handlers(), &"/not/a/depot/path".into()), None);
-    }
-
-    #[test]
-    fn depot_path_sourcegraph_query() {
-        assert_eq!(
-            dispatch(
-                &handlers(),
-                &Query {
-                    query: "//web/atward/default.nix".to_string(),
-                    cs: true,
-                }
-            ),
-            Some("https://cs.tvl.fyi/depot/-/tree/web/atward/default.nix".to_string()),
-        );
-
-        assert_eq!(
-            dispatch(
-                &handlers(),
-                &Query {
-                    query: "/not/a/depot/path".to_string(),
-                    cs: true,
-                }
-            ),
-            None
-        );
-    }
-
-    #[test]
-    fn depot_root_cgit_query() {
-        assert_eq!(
-            dispatch(
-                &handlers(),
-                &Query {
-                    query: "//".to_string(),
-                    cs: false,
-                }
-            ),
-            Some("https://code.tvl.fyi/tree/".to_string()),
-        );
-    }
-
-    #[test]
-    fn plain_host_queries() {
-        assert_eq!(
-            dispatch(&handlers(), &"cs".into()),
-            Some("https://cs.tvl.fyi/".to_string()),
-        );
-
-        assert_eq!(
-            dispatch(&handlers(), &"cl".into()),
-            Some("https://cl.tvl.fyi/".to_string()),
-        );
-
-        assert_eq!(
-            dispatch(&handlers(), &"b".into()),
-            Some("https://b.tvl.fyi/".to_string()),
-        );
-
-        assert_eq!(
-            dispatch(&handlers(), &"todo".into()),
-            Some("https://todo.tvl.fyi/".to_string()),
-        );
-    }
-
-    #[test]
-    fn request_to_query() {
-        assert_eq!(
-            Query::from_request(&Request::fake_http("GET", "/?q=b%2F42", vec![], vec![]))
-                .expect("request should parse to a query"),
-            Query {
-                query: "b/42".to_string(),
-                cs: false,
-            },
-        );
-
-        assert_eq!(
-            Query::from_request(&Request::fake_http("GET", "/", vec![], vec![])),
-            None
-        );
-    }
-
-    #[test]
-    fn settings_from_cookie() {
-        assert_eq!(
-            Query::from_request(&Request::fake_http(
-                "GET",
-                "/?q=b%2F42",
-                vec![("Cookie".to_string(), "cs=true;".to_string())],
-                vec![]
-            ))
-            .expect("request should parse to a query"),
-            Query {
-                query: "b/42".to_string(),
-                cs: true,
-            },
-        );
-    }
-
-    #[test]
-    fn settings_from_query_parameter() {
-        assert_eq!(
-            Query::from_request(&Request::fake_http(
-                "GET",
-                "/?q=b%2F42&cs=true",
-                vec![],
-                vec![]
-            ))
-            .expect("request should parse to a query"),
-            Query {
-                query: "b/42".to_string(),
-                cs: true,
-            },
-        );
-
-        // Query parameter should override cookie
-        assert_eq!(
-            Query::from_request(&Request::fake_http(
-                "GET",
-                "/?q=b%2F42&cs=false",
-                vec![("Cookie".to_string(), "cs=true;".to_string())],
-                vec![]
-            ))
-            .expect("request should parse to a query"),
-            Query {
-                query: "b/42".to_string(),
-                cs: false,
-            },
-        );
-    }
-
-    #[test]
-    fn depot_revision_query() {
-        assert_eq!(
-            dispatch(&handlers(), &"r/3002".into()),
-            Some("https://code.tvl.fyi/commit/?id=refs/r/3002".to_string())
-        );
-
-        assert_eq!(
-            dispatch(&handlers(), &"something only mentioning r/3002".into()),
-            None,
-        );
-
-        assert_eq!(dispatch(&handlers(), &"r/invalid".into()), None,);
-    }
-}
diff --git a/web/atward/src/tests.rs b/web/atward/src/tests.rs
new file mode 100644
index 0000000000..a23f96ee9a
--- /dev/null
+++ b/web/atward/src/tests.rs
@@ -0,0 +1,187 @@
+use super::*;
+
+#[test]
+fn bug_query() {
+    assert_eq!(
+        dispatch(&handlers(), &"b/42".into()),
+        Some("https://b.tvl.fyi/42".to_string())
+    );
+
+    assert_eq!(
+        dispatch(&handlers(), &"something only mentioning b/42".into()),
+        None,
+    );
+    assert_eq!(dispatch(&handlers(), &"b/invalid".into()), None,);
+}
+
+#[test]
+fn cl_query() {
+    assert_eq!(
+        dispatch(&handlers(), &"cl/42".into()),
+        Some("https://cl.tvl.fyi/42".to_string())
+    );
+
+    assert_eq!(
+        dispatch(&handlers(), &"something only mentioning cl/42".into()),
+        None,
+    );
+    assert_eq!(dispatch(&handlers(), &"cl/invalid".into()), None,);
+}
+
+#[test]
+fn depot_path_cgit_query() {
+    assert_eq!(
+        dispatch(&handlers(), &"//web/atward/default.nix".into()),
+        Some("https://code.tvl.fyi/tree/web/atward/default.nix".to_string()),
+    );
+
+    assert_eq!(
+        dispatch(&handlers(), &"//nix/readTree/README.md".into()),
+        Some("https://code.tvl.fyi/about/nix/readTree/README.md".to_string()),
+    );
+
+    assert_eq!(dispatch(&handlers(), &"/not/a/depot/path".into()), None);
+}
+
+#[test]
+fn depot_path_sourcegraph_query() {
+    assert_eq!(
+        dispatch(
+            &handlers(),
+            &Query {
+                query: "//web/atward/default.nix".to_string(),
+                cs: true,
+            }
+        ),
+        Some("https://cs.tvl.fyi/depot/-/tree/web/atward/default.nix".to_string()),
+    );
+
+    assert_eq!(
+        dispatch(
+            &handlers(),
+            &Query {
+                query: "/not/a/depot/path".to_string(),
+                cs: true,
+            }
+        ),
+        None
+    );
+}
+
+#[test]
+fn depot_root_cgit_query() {
+    assert_eq!(
+        dispatch(
+            &handlers(),
+            &Query {
+                query: "//".to_string(),
+                cs: false,
+            }
+        ),
+        Some("https://code.tvl.fyi/tree/".to_string()),
+    );
+}
+
+#[test]
+fn plain_host_queries() {
+    assert_eq!(
+        dispatch(&handlers(), &"cs".into()),
+        Some("https://cs.tvl.fyi/".to_string()),
+    );
+
+    assert_eq!(
+        dispatch(&handlers(), &"cl".into()),
+        Some("https://cl.tvl.fyi/".to_string()),
+    );
+
+    assert_eq!(
+        dispatch(&handlers(), &"b".into()),
+        Some("https://b.tvl.fyi/".to_string()),
+    );
+
+    assert_eq!(
+        dispatch(&handlers(), &"todo".into()),
+        Some("https://todo.tvl.fyi/".to_string()),
+    );
+}
+
+#[test]
+fn request_to_query() {
+    assert_eq!(
+        Query::from_request(&Request::fake_http("GET", "/?q=b%2F42", vec![], vec![]))
+            .expect("request should parse to a query"),
+        Query {
+            query: "b/42".to_string(),
+            cs: false,
+        },
+    );
+
+    assert_eq!(
+        Query::from_request(&Request::fake_http("GET", "/", vec![], vec![])),
+        None
+    );
+}
+
+#[test]
+fn settings_from_cookie() {
+    assert_eq!(
+        Query::from_request(&Request::fake_http(
+            "GET",
+            "/?q=b%2F42",
+            vec![("Cookie".to_string(), "cs=true;".to_string())],
+            vec![]
+        ))
+        .expect("request should parse to a query"),
+        Query {
+            query: "b/42".to_string(),
+            cs: true,
+        },
+    );
+}
+
+#[test]
+fn settings_from_query_parameter() {
+    assert_eq!(
+        Query::from_request(&Request::fake_http(
+            "GET",
+            "/?q=b%2F42&cs=true",
+            vec![],
+            vec![]
+        ))
+        .expect("request should parse to a query"),
+        Query {
+            query: "b/42".to_string(),
+            cs: true,
+        },
+    );
+
+    // Query parameter should override cookie
+    assert_eq!(
+        Query::from_request(&Request::fake_http(
+            "GET",
+            "/?q=b%2F42&cs=false",
+            vec![("Cookie".to_string(), "cs=true;".to_string())],
+            vec![]
+        ))
+        .expect("request should parse to a query"),
+        Query {
+            query: "b/42".to_string(),
+            cs: false,
+        },
+    );
+}
+
+#[test]
+fn depot_revision_query() {
+    assert_eq!(
+        dispatch(&handlers(), &"r/3002".into()),
+        Some("https://code.tvl.fyi/commit/?id=refs/r/3002".to_string())
+    );
+
+    assert_eq!(
+        dispatch(&handlers(), &"something only mentioning r/3002".into()),
+        None,
+    );
+
+    assert_eq!(dispatch(&handlers(), &"r/invalid".into()), None,);
+}
diff --git a/web/blog/default.nix b/web/blog/default.nix
index 2cabc09b55..696a9529c3 100644
--- a/web/blog/default.nix
+++ b/web/blog/default.nix
@@ -24,6 +24,13 @@ let
     # Path to the Markdown file containing the post content.
     content = path;
 
+    # Whether dangerous HTML tags should be filtered in this post. Can
+    # be disabled to, for example, embed videos in a post.
+    tagfilter = option bool;
+
+    # Optional name of the author to display.
+    author = option string;
+
     # Should this post be included in the index? (defaults to true)
     listed = option bool;
 
@@ -40,7 +47,7 @@ let
   fragments = import ./fragments.nix args;
 
   # Functions for generating feeds for these blogs using //web/atom-feed.
-  toFeedEntry = { baseUrl, ...}: defun [ post atom-feed.entry ] (post: rec {
+  toFeedEntry = { baseUrl, ... }: defun [ post atom-feed.entry ] (post: rec {
     id = "${baseUrl}/${post.key}";
     title = post.title;
     content = readFile (renderMarkdown post.content);
@@ -52,7 +59,8 @@ let
       href = id;
     };
   });
-in {
+in
+{
   inherit post toFeedEntry;
   inherit (fragments) renderPost;
 
diff --git a/web/blog/fragments.nix b/web/blog/fragments.nix
index 63fc1ab4c5..20fb879d9e 100644
--- a/web/blog/fragments.nix
+++ b/web/blog/fragments.nix
@@ -8,41 +8,39 @@
 
 let
   inherit (builtins) filter map hasAttr replaceStrings;
-  inherit (pkgs) runCommandNoCC writeText;
+  inherit (pkgs) runCommand writeText;
   inherit (depot.nix) renderMarkdown;
 
-  staticUrl = "https://static.tvl.fyi/${depot.web.static.drvHash}";
-
   # Generate a post list for all listed, non-draft posts.
   isDraft = post: (hasAttr "draft" post) && post.draft;
   isUnlisted = post: (hasAttr "listed" post) && !post.listed;
 
   escape = replaceStrings [ "<" ">" "&" "'" ] [ "&lt;" "&gt;" "&amp;" "&#39;" ];
 
-  header = name: title: ''
-  <!DOCTYPE html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <meta name="description" content="${escape name}">
-    <link rel="stylesheet" type="text/css" href="${staticUrl}/tvl.css" media="all">
-    <link rel="icon" type="image/webp" href="/static/favicon.webp">
-    <link rel="alternate" type="application/atom+xml" title="Atom Feed" href="https://tvl.fyi/feed.atom">
-    <title>${escape name}: ${escape title}</title>
-  </head>
-  <body class="light">
-    <header>
-      <h1><a class="blog-title" href="/">${escape name}</a> </h1>
-      <hr>
-    </header>
+  header = name: title: staticUrl: ''
+    <!DOCTYPE html>
+    <head>
+      <meta charset="utf-8">
+      <meta name="viewport" content="width=device-width, initial-scale=1">
+      <meta name="description" content="${escape name}">
+      <link rel="stylesheet" type="text/css" href="${staticUrl}/tvl.css" media="all">
+      <link rel="icon" type="image/webp" href="/static/favicon.webp">
+      <link rel="alternate" type="application/atom+xml" title="Atom Feed" href="https://tvl.fyi/feed.atom">
+      <title>${escape name}: ${escape title}</title>
+    </head>
+    <body class="light">
+      <header>
+        <h1><a class="blog-title" href="/">${escape name}</a> </h1>
+        <hr>
+      </header>
   '';
 
   fullFooter = content: ''
-    <hr>
-    <footer>
-      ${content}
-    </footer>
-  </body>
+      <hr>
+      <footer>
+        ${content}
+      </footer>
+    </body>
   '';
 
   draftWarning = writeText "draft.html" ''
@@ -61,8 +59,8 @@ let
     <hr>
   '';
 
-  renderPost = { name, footer, ... }: post: runCommandNoCC "${post.key}.html" {} ''
-    cat ${writeText "header.html" (header name post.title)} > $out
+  renderPost = { name, footer, staticUrl ? "https://static.tvl.fyi/${depot.web.static.drvHash}", ... }: post: runCommand "${post.key}.html" { } ''
+    cat ${writeText "header.html" (header name post.title staticUrl)} > $out
 
     # Write the post title & date
     echo '<article><h2 class="inline">${escape post.title}</h2>' >> $out
@@ -73,6 +71,7 @@ let
       then ''date --date="@${toString post.updated}" '+ (updated %Y-%m-%d)' >> $out''
       else ""
     }
+    ${if post ? author then "echo ' by ${post.author}' >> $out" else ""}
     echo '</aside>' >> $out
 
     ${
@@ -85,11 +84,12 @@ let
     }
 
     # Write the actual post through cheddar's about-filter mechanism
-    cat ${renderMarkdown post.content} >> $out
+    cat ${renderMarkdown { path = post.content; tagfilter = post.tagfilter or true; }} >> $out
     echo '</article>' >> $out
 
     cat ${writeText "footer.html" (fullFooter footer)} >> $out
   '';
-in {
+in
+{
   inherit isDraft isUnlisted renderPost;
 }
diff --git a/web/bubblegum/OWNERS b/web/bubblegum/OWNERS
index f16dd105d7..2e95807063 100644
--- a/web/bubblegum/OWNERS
+++ b/web/bubblegum/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - sterni
+sterni
diff --git a/web/bubblegum/default.nix b/web/bubblegum/default.nix
index 1fbe544bce..ed9ab61680 100644
--- a/web/bubblegum/default.nix
+++ b/web/bubblegum/default.nix
@@ -10,23 +10,28 @@ let
     nint
     ;
 
-  minimalDepot = sparseTree depot.path.origSrc [
-    # general depot things
-    "default.nix"
-    "nix/readTree"
-    # nixpkgs for lib and packages
-    "third_party/nixpkgs"
-    "third_party/overlays"
-    # bubblegum and its dependencies
-    "web/bubblegum"
-    "nix/runExecline"
-    "nix/utils"
-    "nix/sparseTree"
-    # tvix docs for svg demo
-    "tvix/docs"
-    # for blog.nix
-    "users/sterni/nix"
-  ];
+  minimalDepot = sparseTree {
+    root = depot.path.origSrc;
+    name = "minimal-depot";
+
+    paths = [
+      # general depot things
+      "default.nix"
+      "nix/readTree"
+      # nixpkgs for lib and packages
+      "third_party/nixpkgs"
+      "third_party/overlays"
+      # bubblegum and its dependencies
+      "web/bubblegum"
+      "nix/runExecline"
+      "nix/utils"
+      "nix/sparseTree"
+      # tvix docs for svg demo
+      "tvix/docs"
+      # for blog.nix
+      "users/sterni/nix"
+    ];
+  };
 
   statusCodes = {
     # 1xx
@@ -134,20 +139,23 @@ let
           code = statusCodes."${statusArg}" or null;
           line = statusArg;
         } else {
-          code = null; line = null;
+          code = null;
+          line = null;
         };
       renderedHeaders = lib.concatStrings
         (lib.mapAttrsToList (n: v: "${n}: ${toString v}\r\n") headers);
-      internalError = msg: respond 500 {
-        Content-type = "text/plain";
-      } "bubblegum error: ${msg}";
+      internalError = msg: respond 500
+        {
+          Content-type = "text/plain";
+        } "bubblegum error: ${msg}";
       body = builtins.tryEval bodyArg;
     in
-      if status.code == null || status.line == null
-      then internalError "Invalid status ${lib.generators.toPretty {} statusArg}."
-      else if !body.success
-      then internalError "Unknown evaluation error in user code"
-      else lib.concatStrings [
+    if status.code == null || status.line == null
+    then internalError "Invalid status ${lib.generators.toPretty {} statusArg}."
+    else if !body.success
+    then internalError "Unknown evaluation error in user code"
+    else
+      lib.concatStrings [
         "Status: ${toString status.code} ${status.line}\r\n"
         renderedHeaders
         "\r\n"
@@ -169,9 +177,9 @@ let
     let
       p = builtins.getEnv "PATH_INFO";
     in
-      if builtins.stringLength p == 0
-      then "/"
-      else p;
+    if builtins.stringLength p == 0
+    then "/"
+    else p;
 
   /* Helper function which converts a path from the
      root of the CGI script (i. e. something which
@@ -187,12 +195,13 @@ let
     else "${scriptName}/${path}";
 
   bins = getBins pkgs.coreutils [ "env" "tee" "cat" "printf" "chmod" ]
-      // getBins nint [ "nint" ];
+    // getBins nint [ "nint" ];
 
   /* Type: args -> either path derivation string -> derivation
   */
   writeCGI =
-    { # if given sets the `PATH` to search for `nix-instantiate`
+    {
+      # if given sets the `PATH` to search for `nix-instantiate`
       # Useful when using for example thttpd which unsets `PATH`
       # in the CGI environment.
       binPath ? ""
@@ -202,7 +211,8 @@ let
     , name ? null
     , ...
     }@args:
-    input: let
+    input:
+    let
       drvName =
         if builtins.isString input || args ? name
         then args.name
@@ -227,20 +237,31 @@ let
         # always pass depot so scripts can use this library
         "--arg depot '(import ${minimalDepot} {})'"
       ]);
-    in runExecline.local drvName {} [
-      "importas" "out" "out"
-      "pipeline" [
-        "foreground" [
-          "if" [ bins.printf "%s\n" shebang ]
+    in
+    runExecline.local drvName { } [
+      "importas"
+      "out"
+      "out"
+      "pipeline"
+      [
+        "foreground"
+        [
+          "if"
+          [ bins.printf "%s\n" shebang ]
         ]
-        "if" [ bins.cat script ]
+        "if"
+        [ bins.cat script ]
       ]
-      "if" [ bins.tee "$out" ]
-      "if" [ bins.chmod "+x" "$out" ]
-      "exit" "0"
+      "if"
+      [ bins.tee "$out" ]
+      "if"
+      [ bins.chmod "+x" "$out" ]
+      "exit"
+      "0"
     ];
 
-in {
+in
+{
   inherit
     respond
     pathInfo
diff --git a/web/bubblegum/examples/blog.nix b/web/bubblegum/examples/blog.nix
index 9359d38fa2..76b91168b8 100644
--- a/web/bubblegum/examples/blog.nix
+++ b/web/bubblegum/examples/blog.nix
@@ -26,17 +26,17 @@ let
     let
       matched = builtins.match "/?([0-9]+)-([0-9]+)-([0-9]+)-.+" post;
     in
-      if matched == null
-      then [ 0 0 0 ]
-      else builtins.map builtins.fromJSON matched;
+    if matched == null
+    then [ 0 0 0 ]
+    else builtins.map builtins.fromJSON matched;
 
   parseTitle = post:
     let
       matched = builtins.match "/?[0-9]+-[0-9]+-[0-9]+-(.+).html" post;
     in
-      if matched == null
-      then "no title"
-      else builtins.head matched;
+    if matched == null
+    then "no title"
+    else builtins.head matched;
 
   dateAtLeast = a: b:
     builtins.all fun.id
@@ -68,11 +68,13 @@ let
     <main>
       <h1>blog posts</h1>
       <ul>
-  '' + lib.concatMapStrings (post: ''
+  '' + lib.concatMapStrings
+    (post: ''
       <li>
         <a href="${absolutePath (url.encode {} post)}">${parseTitle post}</a>
       </li>
-  '') posts + ''
+    '')
+    posts + ''
       </ul>
     </main>
   '';
@@ -80,10 +82,14 @@ let
   formatDate =
     let
       # Assume we never deal with years < 1000
-      formatDigit = d: string.fit {
-        char = "0"; width = 2;
-      } (toString d);
-    in lib.concatMapStringsSep "-" formatDigit;
+      formatDigit = d: string.fit
+        {
+          char = "0";
+          width = 2;
+        }
+        (toString d);
+    in
+    lib.concatMapStringsSep "-" formatDigit;
 
   post = title: post: ''
     <main>
@@ -101,8 +107,9 @@ let
   validatePathInfo = pathInfo:
     let
       chars = string.toChars pathInfo;
-    in builtins.length chars > 1
-      && !(builtins.elem "/" (builtins.tail chars));
+    in
+    builtins.length chars > 1
+    && !(builtins.elem "/" (builtins.tail chars));
 
   response =
     if pathInfo == "/"
@@ -129,6 +136,8 @@ let
       inner = "<h1>404 β€” not found</h1>";
     };
 in
-  respond response.status {
-    "Content-type" = "text/html";
-  } (generic response)
+respond response.status
+{
+  "Content-type" = "text/html";
+}
+  (generic response)
diff --git a/web/bubblegum/examples/default.nix b/web/bubblegum/examples/default.nix
index 3f0f51db63..20f897dbbf 100644
--- a/web/bubblegum/examples/default.nix
+++ b/web/bubblegum/examples/default.nix
@@ -4,7 +4,6 @@ let
 
   scripts = [
     ./hello.nix
-    ./derivation-svg.nix
     (substituteAll {
       src = ./blog.nix;
       # by making this a plain string this
@@ -29,33 +28,54 @@ let
     ;
 
   bins = (getBins pkgs.thttpd [ "thttpd" ])
-      // (getBins pkgs.coreutils [ "printf" "cp" "mkdir" ]);
+    // (getBins pkgs.coreutils [ "printf" "cp" "mkdir" ]);
 
   webRoot =
     let
       copyScripts = lib.concatMap
-        (path: let
-          cgi = writeCGI {
-            # assume we are on NixOS since thttpd doesn't set PATH.
-            # using third_party.nix is tricky because not everyone
-            # has a tvix daemon running.
-            binPath = "/run/current-system/sw/bin";
-          } path;
-        in [
-          "if" [ bins.cp cgi "\${out}/${cgi.name}" ]
-        ]) scripts;
-    in runExecline.local "webroot" {} ([
-      "importas" "out" "out"
-      "if" [ bins.mkdir "-p" "$out" ]
+        (path:
+          let
+            cgi = writeCGI
+              {
+                # assume we are on NixOS since thttpd doesn't set PATH.
+                # using third_party.nix is tricky because not everyone
+                # has a tvix daemon running.
+                binPath = "/run/current-system/sw/bin";
+              }
+              path;
+          in
+          [
+            "if"
+            [ bins.cp cgi "\${out}/${cgi.name}" ]
+          ])
+        scripts;
+    in
+    runExecline.local "webroot" { } ([
+      "importas"
+      "out"
+      "out"
+      "if"
+      [ bins.mkdir "-p" "$out" ]
     ] ++ copyScripts);
 
   port = 9000;
 
 in
-  writeExecline "serve-examples" {} [
-    "foreground" [
-      bins.printf "%s\n" "Running on http://localhost:${toString port}"
-    ]
-    "${bins.thttpd}" "-D" "-p" (toString port) "-l" "/dev/stderr"
-                     "-c" "*.nix" "-d" webRoot
+writeExecline "serve-examples" { } [
+  "foreground"
+  [
+    bins.printf
+    "%s\n"
+    "Running on http://localhost:${toString port}"
   ]
+  "${bins.thttpd}"
+  "-D"
+  "-p"
+  (toString port)
+  "-l"
+  "/dev/stderr"
+  "-c"
+  "*.nix"
+  "-d"
+  webRoot
+]
diff --git a/web/bubblegum/examples/derivation-svg.nix b/web/bubblegum/examples/derivation-svg.nix
deleted file mode 100644
index a5f30a2bd1..0000000000
--- a/web/bubblegum/examples/derivation-svg.nix
+++ /dev/null
@@ -1,11 +0,0 @@
-# Warning: this is *very* slow on the first request
-{ depot, ... }:
-
-let
-  inherit (depot.web.bubblegum)
-    respond
-    ;
-in
-  respond "OK" {
-    Content-type = "image/svg+xml";
-  } (builtins.readFile "${depot.tvix.docs.svg}/component-flow.svg")
diff --git a/web/bubblegum/examples/hello.nix b/web/bubblegum/examples/hello.nix
index db04d40823..bd4891f7d6 100644
--- a/web/bubblegum/examples/hello.nix
+++ b/web/bubblegum/examples/hello.nix
@@ -87,6 +87,8 @@ let
   response = routes."${pathInfo}" or notFound;
 
 in
-  respond response.status {
-    "Content-type" = "text/html";
-  } (template response)
+respond response.status
+{
+  "Content-type" = "text/html";
+}
+  (template response)
diff --git a/web/cgit-taz/default.nix b/web/cgit-taz/default.nix
deleted file mode 100644
index a89b96fd92..0000000000
--- a/web/cgit-taz/default.nix
+++ /dev/null
@@ -1,74 +0,0 @@
-# This derivation configures a 'cgit' instance to serve repositories
-# from a different source.
-{ depot, pkgs, ... }:
-
-let
-  inherit (pkgs)
-    mime-types
-    thttpd
-    writeShellScriptBin
-    writeText
-    ;
-
-  sourceFilter = writeShellScriptBin "cheddar-about" ''
-    exec ${depot.tools.cheddar}/bin/cheddar --about-filter $@
-  '';
-  cgitConfig = writeText "cgitrc" ''
-    # Global configuration
-    virtual-root=/
-    enable-http-clone=0
-    readme=:README.md
-    about-filter=${sourceFilter}/bin/cheddar-about
-    source-filter=${depot.tools.cheddar}/bin/cheddar
-    enable-log-filecount=1
-    enable-log-linecount=1
-    enable-follow-links=1
-    enable-blame=1
-    mimetype-file=${mime-types}/etc/mime.types
-    logo=https://static.tvl.fyi/${depot.web.static.drvHash}/logo-animated.svg
-
-    # Repository configuration
-    repo.url=depot
-    repo.path=/var/lib/gerrit/git/depot.git/
-    repo.desc=monorepo for the virus lounge
-    repo.owner=The Virus Lounge
-    repo.clone-url=https://code.tvl.fyi/depot.git
-  '';
-
-  thttpdConfig = writeText "thttpd.conf" ''
-    port=2448
-    dir=${depot.third_party.cgit}/cgit
-    nochroot
-    novhost
-    cgipat=**.cgi
-  '';
-
-  # Patched version of thttpd that serves cgit.cgi as the index and
-  # sets the environment variable for pointing cgit at the correct
-  # configuration.
-  #
-  # Things are done this way because recompilation of thttpd is much
-  # faster than cgit and I don't want to wait long when iterating on
-  # config.
-  thttpdConfigPatch = writeText "thttpd_cgit_conf.patch" ''
-    diff --git a/libhttpd.c b/libhttpd.c
-    index c6b1622..eef4b73 100644
-    --- a/libhttpd.c
-    +++ b/libhttpd.c
-    @@ -3055,4 +3055,6 @@ make_envp( httpd_conn* hc )
-
-         envn = 0;
-    +    // force cgit to load the correct configuration
-    +    envp[envn++] = "CGIT_CONFIG=${cgitConfig}";
-         envp[envn++] = build_env( "PATH=%s", CGI_PATH );
-     #ifdef CGI_LD_LIBRARY_PATH
-  '';
-  thttpdCgit = thttpd.overrideAttrs(old: {
-    patches = [
-      ./thttpd_cgi_idx.patch
-      thttpdConfigPatch
-    ];
-  });
-in writeShellScriptBin "cgit-launch" ''
-  exec ${thttpdCgit}/bin/thttpd -D -C ${thttpdConfig}
-''
diff --git a/web/cgit-tvl/default.nix b/web/cgit-tvl/default.nix
new file mode 100644
index 0000000000..d26de9a5eb
--- /dev/null
+++ b/web/cgit-tvl/default.nix
@@ -0,0 +1,73 @@
+# Wrapper for running cgit through thttpd with TVL-specific
+# configuration.
+#
+# In practice this is only used for //ops/modules/cgit, but exposing
+# it here makes it easy to experiment with cgit locally.
+{ depot, lib, pkgs, ... }:
+
+let
+  cgitConfig = repo: pkgs.writeText "cgitrc" ''
+    # Global configuration
+    virtual-root=/
+    enable-http-clone=0
+    readme=:README.md
+    about-filter=${depot.tools.cheddar.about-filter}/bin/cheddar-about
+    source-filter=${depot.tools.cheddar}/bin/cheddar
+    enable-log-filecount=1
+    enable-log-linecount=1
+    enable-follow-links=1
+    enable-blame=1
+    mimetype-file=${pkgs.mime-types}/etc/mime.types
+    logo=https://static.tvl.fyi/${depot.web.static.drvHash}/logo-animated.svg
+
+    # Repository configuration
+    repo.url=depot
+    repo.path=${repo}
+    repo.desc=monorepo for the virus lounge
+    repo.owner=The Virus Lounge
+    repo.clone-url=https://code.tvl.fyi/depot.git
+  '';
+
+  thttpdConfig = port: pkgs.writeText "thttpd.conf" ''
+    port=${toString port}
+    dir=${depot.third_party.cgit}/cgit
+    nochroot
+    novhost
+    cgipat=**.cgi
+  '';
+
+  # Patched version of thttpd that serves cgit.cgi as the index and
+  # sets the environment variable for pointing cgit at the correct
+  # configuration.
+  #
+  # Things are done this way because recompilation of thttpd is much
+  # faster than cgit.
+  thttpdConfigPatch = repo: pkgs.writeText "thttpd_cgit_conf.patch" ''
+    diff --git a/libhttpd.c b/libhttpd.c
+    index c6b1622..eef4b73 100644
+    --- a/libhttpd.c
+    +++ b/libhttpd.c
+    @@ -3055,4 +3055,6 @@ make_envp( httpd_conn* hc )
+
+         envn = 0;
+    +    // force cgit to load the correct configuration
+    +    envp[envn++] = "CGIT_CONFIG=${cgitConfig repo}";
+         envp[envn++] = build_env( "PATH=%s", CGI_PATH );
+     #ifdef CGI_LD_LIBRARY_PATH
+  '';
+
+  thttpdCgit = repo: pkgs.thttpd.overrideAttrs (old: {
+    patches = [
+      ./thttpd_cgi_idx.patch
+      (thttpdConfigPatch repo)
+    ];
+  });
+
+in
+lib.makeOverridable
+  ({ port ? 2448
+   , repo ? "/var/lib/gerrit/git/depot.git/"
+   }: pkgs.writeShellScript "cgit-launch" ''
+    exec ${thttpdCgit repo}/bin/thttpd -D -C ${thttpdConfig port}
+  '')
+{ }
diff --git a/web/cgit-taz/thttpd_cgi_idx.patch b/web/cgit-tvl/thttpd_cgi_idx.patch
index 67dbc0c7ab..67dbc0c7ab 100644
--- a/web/cgit-taz/thttpd_cgi_idx.patch
+++ b/web/cgit-tvl/thttpd_cgi_idx.patch
diff --git a/web/converse/Cargo.lock b/web/converse/Cargo.lock
index b022bdd55e..651656b902 100644
--- a/web/converse/Cargo.lock
+++ b/web/converse/Cargo.lock
@@ -1,5 +1,7 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
 [[package]]
 name = "actix"
 version = "0.7.9"
@@ -7,14 +9,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6c616db5fa4b0c40702fb75201c2af7f8aa8f3a2e2c1dda3b0655772aa949666"
 dependencies = [
  "actix_derive",
- "bitflags 1.2.1",
+ "bitflags 1.3.2",
  "bytes",
  "crossbeam-channel",
  "failure",
  "fnv",
  "futures",
  "libc",
- "log 0.4.14",
+ "log 0.4.17",
  "parking_lot 0.7.1",
  "smallvec 0.6.14",
  "tokio",
@@ -39,11 +41,11 @@ dependencies = [
  "actix",
  "bytes",
  "futures",
- "log 0.4.14",
+ "log 0.4.17",
  "mio",
  "net2",
  "num_cpus",
- "slab 0.4.2",
+ "slab 0.4.7",
  "tokio",
  "tokio-codec",
  "tokio-current-thread",
@@ -64,7 +66,7 @@ dependencies = [
  "actix",
  "actix-net",
  "base64 0.10.1",
- "bitflags 1.2.1",
+ "bitflags 1.3.2",
  "brotli2",
  "byteorder",
  "bytes",
@@ -80,9 +82,9 @@ dependencies = [
  "language-tags",
  "lazy_static",
  "lazycell",
- "log 0.4.14",
- "mime 0.3.16",
- "mime_guess 2.0.3",
+ "log 0.4.17",
+ "mime",
+ "mime_guess",
  "mio",
  "net2",
  "num_cpus",
@@ -93,17 +95,17 @@ dependencies = [
  "serde",
  "serde_json",
  "serde_urlencoded",
- "sha1",
- "slab 0.4.2",
+ "sha1 0.6.1",
+ "slab 0.4.7",
  "smallvec 0.6.14",
- "time",
+ "time 0.1.45",
  "tokio",
  "tokio-current-thread",
  "tokio-io",
  "tokio-reactor",
  "tokio-tcp",
  "tokio-timer",
- "url",
+ "url 1.7.2",
  "v_htmlescape",
  "version_check 0.1.5",
 ]
@@ -121,9 +123,9 @@ dependencies = [
 
 [[package]]
 name = "addr2line"
-version = "0.14.1"
+version = "0.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7"
+checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
 dependencies = [
  "gimli",
 ]
@@ -196,39 +198,51 @@ dependencies = [
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.15"
+version = "0.7.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
 dependencies = [
- "memchr 2.3.4",
+ "memchr 2.5.0",
 ]
 
 [[package]]
-name = "ansi_term"
-version = "0.11.0"
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
 dependencies = [
- "winapi 0.3.9",
+ "alloc-no-stdlib",
 ]
 
 [[package]]
-name = "arrayref"
-version = "0.3.6"
+name = "android_system_properties"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
 
 [[package]]
-name = "arrayvec"
-version = "0.5.2"
+name = "ansi_term"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi 0.3.9",
+]
 
 [[package]]
 name = "ascii"
-version = "0.8.7"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97be891acc47ca214468e09425d02cef3af2c94d0d82081cd02061f996802f14"
+checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
 
 [[package]]
 name = "askama"
@@ -274,26 +288,30 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "0.1.7"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
+checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
+dependencies = [
+ "autocfg 1.1.0",
+]
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "backtrace"
-version = "0.3.56"
+version = "0.3.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc"
+checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
 dependencies = [
  "addr2line",
+ "cc",
  "cfg-if 1.0.0",
  "libc",
- "miniz_oxide",
+ "miniz_oxide 0.5.4",
  "object",
  "rustc-demangle",
 ]
@@ -319,9 +337,9 @@ dependencies = [
 
 [[package]]
 name = "base64"
-version = "0.13.0"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "bitflags"
@@ -331,31 +349,50 @@ checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
 
 [[package]]
 name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
-name = "blake2b_simd"
-version = "0.5.11"
+name = "block-buffer"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
 dependencies = [
- "arrayref",
- "arrayvec",
- "constant_time_eq",
+ "generic-array",
 ]
 
 [[package]]
 name = "block-buffer"
-version = "0.9.0"
+version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
 dependencies = [
  "generic-array",
 ]
 
 [[package]]
+name = "brotli"
+version = "3.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
+
+[[package]]
+name = "brotli-decompressor"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+]
+
+[[package]]
 name = "brotli-sys"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -381,11 +418,17 @@ version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
 dependencies = [
- "memchr 2.3.4",
+ "memchr 2.5.0",
  "safemem",
 ]
 
 [[package]]
+name = "bumpalo"
+version = "3.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+
+[[package]]
 name = "byteorder"
 version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -403,9 +446,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.67"
+version = "1.0.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
+checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
 
 [[package]]
 name = "cfg-if"
@@ -421,23 +464,25 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.19"
+version = "0.4.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
 dependencies = [
- "libc",
+ "iana-time-zone",
+ "js-sys",
  "num-integer",
  "num-traits",
  "serde",
- "time",
+ "time 0.1.45",
+ "wasm-bindgen",
  "winapi 0.3.9",
 ]
 
 [[package]]
 name = "chunked_transfer"
-version = "0.3.1"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87"
+checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
 
 [[package]]
 name = "cipher"
@@ -450,13 +495,13 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "2.33.3"
+version = "2.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
 dependencies = [
  "ansi_term",
  "atty",
- "bitflags 1.2.1",
+ "bitflags 1.3.2",
  "strsim",
  "textwrap",
  "unicode-width",
@@ -469,7 +514,17 @@ version = "0.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
 dependencies = [
- "bitflags 1.2.1",
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
 ]
 
 [[package]]
@@ -490,12 +545,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "constant_time_eq"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
-
-[[package]]
 name = "converse"
 version = "0.1.0"
 dependencies = [
@@ -511,9 +560,9 @@ dependencies = [
  "failure",
  "futures",
  "hyper",
- "log 0.4.14",
+ "log 0.4.17",
  "md5",
- "mime_guess 2.0.3",
+ "mime_guess",
  "pq-sys",
  "pulldown-cmark",
  "r2d2",
@@ -524,31 +573,40 @@ dependencies = [
  "serde_json",
  "tokio",
  "tokio-timer",
- "url",
+ "url 1.7.2",
  "url_serde",
 ]
 
 [[package]]
 name = "cookie"
-version = "0.11.4"
+version = "0.11.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80f6044740a4a516b8aac14c140cdf35c1a640b1bd6b98b6224e49143b2f1566"
+checksum = "be2018768ed1d848cc4d347d551546474025ba820e5db70e4c9aaa349f678bd7"
 dependencies = [
  "aes-gcm",
- "base64 0.13.0",
+ "base64 0.13.1",
  "hkdf",
  "hmac",
- "percent-encoding 2.1.0",
- "rand 0.8.3",
+ "percent-encoding 2.2.0",
+ "rand 0.8.5",
  "sha2",
- "time",
+ "time 0.1.45",
 ]
 
 [[package]]
-name = "cpuid-bool"
-version = "0.1.2"
+name = "core-foundation-sys"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "cpuid-bool"
@@ -558,9 +616,9 @@ checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"
 
 [[package]]
 name = "crc32fast"
-version = "1.2.1"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
 dependencies = [
  "cfg-if 1.0.0",
 ]
@@ -587,9 +645,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-deque"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
+checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed"
 dependencies = [
  "crossbeam-epoch",
  "crossbeam-utils 0.7.2",
@@ -602,7 +660,7 @@ version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg 1.1.0",
  "cfg-if 0.1.10",
  "crossbeam-utils 0.7.2",
  "lazy_static",
@@ -638,27 +696,26 @@ version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg 1.1.0",
  "cfg-if 0.1.10",
  "lazy_static",
 ]
 
 [[package]]
-name = "crossbeam-utils"
-version = "0.8.3"
+name = "crypto-common"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
 dependencies = [
- "autocfg 1.0.1",
- "cfg-if 1.0.0",
- "lazy_static",
+ "generic-array",
+ "typenum",
 ]
 
 [[package]]
 name = "crypto-mac"
-version = "0.10.0"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6"
+checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
 dependencies = [
  "generic-array",
  "subtle",
@@ -675,24 +732,24 @@ dependencies = [
 
 [[package]]
 name = "curl"
-version = "0.4.35"
+version = "0.4.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a872858e9cb9e3b96c80dd78774ad9e32e44d3b05dc31e142b858d14aebc82c"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
 dependencies = [
  "curl-sys",
  "libc",
  "openssl-probe",
  "openssl-sys",
  "schannel",
- "socket2",
+ "socket2 0.4.7",
  "winapi 0.3.9",
 ]
 
 [[package]]
 name = "curl-sys"
-version = "0.4.41+curl-7.75.0"
+version = "0.4.59+curl-7.86.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ec466abd277c7cab2905948f3e94d10bc4963f1f5d47921c1cc4ffd2028fe65"
+checksum = "6cfce34829f448b08f55b7db6d0009e23e2e86a34e8c2b366269bf5799b4a407"
 dependencies = [
  "cc",
  "libc",
@@ -704,23 +761,66 @@ dependencies = [
 ]
 
 [[package]]
+name = "cxx"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "scratch",
+ "syn 1.0.105",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470"
+dependencies = [
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.105",
+]
+
+[[package]]
 name = "deflate"
-version = "0.7.20"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4"
+checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f"
 dependencies = [
  "adler32",
- "byteorder",
  "gzip-header",
 ]
 
 [[package]]
 name = "diesel"
-version = "1.4.6"
+version = "1.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "047bfc4d5c3bd2ef6ca6f981941046113524b9a9f9a7cbdfdd7ff40f58e6f542"
+checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d"
 dependencies = [
- "bitflags 1.2.1",
+ "bitflags 1.3.2",
  "byteorder",
  "chrono",
  "diesel_derives",
@@ -734,9 +834,9 @@ version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
 dependencies = [
- "proc-macro2 1.0.26",
- "quote 1.0.9",
- "syn 1.0.68",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.105",
 ]
 
 [[package]]
@@ -749,14 +849,13 @@ dependencies = [
 ]
 
 [[package]]
-name = "dirs"
-version = "1.0.5"
+name = "digest"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
+checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
 dependencies = [
- "libc",
- "redox_users",
- "winapi 0.3.9",
+ "block-buffer 0.10.3",
+ "crypto-common",
 ]
 
 [[package]]
@@ -843,7 +942,7 @@ checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38"
 dependencies = [
  "atty",
  "humantime",
- "log 0.4.14",
+ "log 0.4.17",
  "regex",
  "termcolor",
 ]
@@ -882,35 +981,41 @@ version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
 dependencies = [
- "proc-macro2 1.0.26",
- "quote 1.0.9",
- "syn 1.0.68",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.105",
  "synstructure",
 ]
 
 [[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
 name = "filetime"
-version = "0.2.14"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
+checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "redox_syscall 0.2.5",
- "winapi 0.3.9",
+ "redox_syscall 0.2.16",
+ "windows-sys 0.42.0",
 ]
 
 [[package]]
 name = "flate2"
-version = "1.0.20"
+version = "1.0.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
+checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
 dependencies = [
- "cfg-if 1.0.0",
  "crc32fast",
- "libc",
- "miniz-sys",
- "miniz_oxide",
+ "miniz_oxide 0.6.2",
 ]
 
 [[package]]
@@ -920,6 +1025,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
+name = "form_urlencoded"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+dependencies = [
+ "percent-encoding 2.2.0",
+]
+
+[[package]]
 name = "fuchsia-cprng"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -931,7 +1045,7 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 dependencies = [
- "bitflags 1.2.1",
+ "bitflags 1.3.2",
  "fuchsia-zircon-sys",
 ]
 
@@ -959,12 +1073,12 @@ dependencies = [
 
 [[package]]
 name = "generic-array"
-version = "0.14.4"
+version = "0.14.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
 dependencies = [
  "typenum",
- "version_check 0.9.3",
+ "version_check 0.9.4",
 ]
 
 [[package]]
@@ -978,24 +1092,13 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.2"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
+ "wasi 0.11.0+wasi-snapshot-preview1",
 ]
 
 [[package]]
@@ -1010,15 +1113,15 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.23.0"
+version = "0.26.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
+checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
 
 [[package]]
 name = "gzip-header"
-version = "0.3.0"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0131feb3d3bb2a5a238d8a4d09f6353b7ebfdc52e77bccbf4ea6eaa751dde639"
+checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2"
 dependencies = [
  "crc32fast",
 ]
@@ -1035,23 +1138,23 @@ dependencies = [
  "futures",
  "http",
  "indexmap",
- "log 0.4.14",
- "slab 0.4.2",
+ "log 0.4.17",
+ "slab 0.4.7",
  "string",
  "tokio-io",
 ]
 
 [[package]]
 name = "hashbrown"
-version = "0.9.1"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.18"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
 dependencies = [
  "libc",
 ]
@@ -1062,7 +1165,7 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
 dependencies = [
- "digest",
+ "digest 0.9.0",
  "hmac",
 ]
 
@@ -1073,7 +1176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
 dependencies = [
  "crypto-mac",
- "digest",
+ "digest 0.9.0",
 ]
 
 [[package]]
@@ -1095,14 +1198,20 @@ checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0"
 dependencies = [
  "bytes",
  "fnv",
- "itoa",
+ "itoa 0.4.8",
 ]
 
 [[package]]
 name = "httparse"
-version = "1.3.5"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
 
 [[package]]
 name = "humantime"
@@ -1126,21 +1235,45 @@ dependencies = [
  "httparse",
  "iovec",
  "language-tags",
- "log 0.4.14",
- "mime 0.3.16",
+ "log 0.4.17",
+ "mime",
  "net2",
  "percent-encoding 1.0.1",
  "relay",
- "time",
+ "time 0.1.45",
  "tokio-core",
  "tokio-io",
  "tokio-proto",
  "tokio-service",
- "unicase 2.6.0",
+ "unicase",
  "want",
 ]
 
 [[package]]
+name = "iana-time-zone"
+version = "0.1.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
+[[package]]
 name = "idna"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1152,20 +1285,30 @@ dependencies = [
 ]
 
 [[package]]
+name = "idna"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
 name = "indexmap"
-version = "1.6.2"
+version = "1.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg 1.1.0",
  "hashbrown",
 ]
 
 [[package]]
 name = "instant"
-version = "0.1.9"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
 dependencies = [
  "cfg-if 1.0.0",
 ]
@@ -1186,7 +1329,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "08f7eadeaf4b52700de180d147c4805f199854600b36faa963d91114827b2ffc"
 dependencies = [
  "error-chain 0.8.1",
- "socket2",
+ "socket2 0.3.19",
  "widestring",
  "winapi 0.3.9",
  "winreg",
@@ -1194,9 +1337,24 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "0.4.7"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+
+[[package]]
+name = "itoa"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
 
 [[package]]
 name = "kernel32-sys"
@@ -1228,15 +1386,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 
 [[package]]
 name = "libc"
-version = "0.2.92"
+version = "0.2.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
+checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
 
 [[package]]
 name = "libz-sys"
-version = "1.1.2"
+version = "1.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655"
+checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
 dependencies = [
  "cc",
  "libc",
@@ -1245,10 +1403,19 @@ dependencies = [
 ]
 
 [[package]]
+name = "link-cplusplus"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
+dependencies = [
+ "cc",
+]
+
+[[package]]
 name = "linked-hash-map"
-version = "0.5.4"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
 
 [[package]]
 name = "lock_api"
@@ -1271,10 +1438,11 @@ dependencies = [
 
 [[package]]
 name = "lock_api"
-version = "0.4.3"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
 dependencies = [
+ "autocfg 1.1.0",
  "scopeguard 1.1.0",
 ]
 
@@ -1284,14 +1452,14 @@ version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
 dependencies = [
- "log 0.4.14",
+ "log 0.4.17",
 ]
 
 [[package]]
 name = "log"
-version = "0.4.14"
+version = "0.4.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
 dependencies = [
  "cfg-if 1.0.0",
 ]
@@ -1313,9 +1481,9 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
 
 [[package]]
 name = "matches"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
+checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
 
 [[package]]
 name = "maybe-uninit"
@@ -1340,9 +1508,9 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.3.4"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
 name = "memoffset"
@@ -1350,16 +1518,7 @@ version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
 dependencies = [
- "autocfg 1.0.1",
-]
-
-[[package]]
-name = "mime"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
-dependencies = [
- "log 0.3.9",
+ "autocfg 1.1.0",
 ]
 
 [[package]]
@@ -1370,44 +1529,30 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
 
 [[package]]
 name = "mime_guess"
-version = "1.8.8"
+version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "216929a5ee4dd316b1702eedf5e74548c123d370f47841ceaac38ca154690ca3"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
 dependencies = [
- "mime 0.2.6",
- "phf",
- "phf_codegen",
- "unicase 1.4.2",
+ "mime",
+ "unicase",
 ]
 
 [[package]]
-name = "mime_guess"
-version = "2.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
-dependencies = [
- "mime 0.3.16",
- "unicase 2.6.0",
-]
-
-[[package]]
-name = "miniz-sys"
-version = "0.1.12"
+name = "miniz_oxide"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202"
+checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
 dependencies = [
- "cc",
- "libc",
+ "adler",
 ]
 
 [[package]]
 name = "miniz_oxide"
-version = "0.4.4"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
 dependencies = [
  "adler",
- "autocfg 1.0.1",
 ]
 
 [[package]]
@@ -1422,10 +1567,10 @@ dependencies = [
  "iovec",
  "kernel32-sys",
  "libc",
- "log 0.4.14",
+ "log 0.4.17",
  "miow",
  "net2",
- "slab 0.4.2",
+ "slab 0.4.7",
  "winapi 0.2.8",
 ]
 
@@ -1454,27 +1599,27 @@ dependencies = [
 
 [[package]]
 name = "multipart"
-version = "0.15.4"
+version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adba94490a79baf2d6a23eac897157047008272fa3eecb3373ae6377b91eca28"
+checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182"
 dependencies = [
  "buf_redux",
  "httparse",
- "log 0.4.14",
- "mime 0.2.6",
- "mime_guess 1.8.8",
+ "log 0.4.17",
+ "mime",
+ "mime_guess",
  "quick-error",
- "rand 0.4.6",
+ "rand 0.8.5",
  "safemem",
- "tempdir",
+ "tempfile",
  "twoway",
 ]
 
 [[package]]
 name = "net2"
-version = "0.2.37"
+version = "0.2.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
+checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
@@ -1496,44 +1641,62 @@ version = "4.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
 dependencies = [
- "memchr 2.3.4",
+ "memchr 2.5.0",
  "version_check 0.1.5",
 ]
 
 [[package]]
 name = "num-integer"
-version = "0.1.44"
+version = "0.1.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg 1.1.0",
  "num-traits",
 ]
 
 [[package]]
 name = "num-traits"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg 1.1.0",
 ]
 
 [[package]]
 name = "num_cpus"
-version = "1.13.0"
+version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
 dependencies = [
  "hermit-abi",
  "libc",
 ]
 
 [[package]]
+name = "num_threads"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "object"
-version = "0.23.0"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
+dependencies = [
+ "memchr 2.5.0",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
 
 [[package]]
 name = "opaque-debug"
@@ -1543,17 +1706,17 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
 [[package]]
 name = "openssl-probe"
-version = "0.1.2"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.61"
+version = "0.9.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
+checksum = "07d5c8cb6e57b3a3612064d7b18b117912b4ce70955c2504d4b741c9e244b132"
 dependencies = [
- "autocfg 1.0.1",
+ "autocfg 1.1.0",
  "cc",
  "libc",
  "pkg-config",
@@ -1592,13 +1755,12 @@ dependencies = [
 
 [[package]]
 name = "parking_lot"
-version = "0.11.1"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
 dependencies = [
- "instant",
- "lock_api 0.4.3",
- "parking_lot_core 0.8.3",
+ "lock_api 0.4.9",
+ "parking_lot_core 0.9.5",
 ]
 
 [[package]]
@@ -1631,16 +1793,15 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.8.3"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
+checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
 dependencies = [
  "cfg-if 1.0.0",
- "instant",
  "libc",
- "redox_syscall 0.2.5",
- "smallvec 1.6.1",
- "winapi 0.3.9",
+ "redox_syscall 0.2.16",
+ "smallvec 1.10.0",
+ "windows-sys 0.42.0",
 ]
 
 [[package]]
@@ -1651,9 +1812,9 @@ checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
 
 [[package]]
 name = "percent-encoding"
-version = "2.1.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
 
 [[package]]
 name = "pest"
@@ -1673,49 +1834,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "phf"
-version = "0.7.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18"
-dependencies = [
- "phf_shared",
-]
-
-[[package]]
-name = "phf_codegen"
-version = "0.7.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e"
-dependencies = [
- "phf_generator",
- "phf_shared",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.7.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
-dependencies = [
- "phf_shared",
- "rand 0.6.5",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.7.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
-dependencies = [
- "siphasher",
- "unicase 1.4.2",
-]
-
-[[package]]
 name = "pkg-config"
-version = "0.3.19"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
 
 [[package]]
 name = "polyval"
@@ -1723,16 +1845,16 @@ version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd"
 dependencies = [
- "cpuid-bool 0.2.0",
+ "cpuid-bool",
  "opaque-debug",
  "universal-hash",
 ]
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.10"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "pq-sys"
@@ -1763,11 +1885,11 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.26"
+version = "1.0.47"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
 dependencies = [
- "unicode-xid 0.2.1",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -1812,21 +1934,21 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.9"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
 dependencies = [
- "proc-macro2 1.0.26",
+ "proc-macro2 1.0.47",
 ]
 
 [[package]]
 name = "r2d2"
-version = "0.8.9"
+version = "0.8.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
+checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
 dependencies = [
- "log 0.4.14",
- "parking_lot 0.11.1",
+ "log 0.4.17",
+ "parking_lot 0.12.1",
  "scheduled-thread-pool",
 ]
 
@@ -1872,11 +1994,11 @@ version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
 dependencies = [
- "autocfg 0.1.7",
+ "autocfg 0.1.8",
  "libc",
  "rand_chacha 0.1.1",
  "rand_core 0.4.2",
- "rand_hc 0.1.0",
+ "rand_hc",
  "rand_isaac",
  "rand_jitter",
  "rand_os",
@@ -1887,14 +2009,13 @@ dependencies = [
 
 [[package]]
 name = "rand"
-version = "0.8.3"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
- "rand_chacha 0.3.0",
- "rand_core 0.6.2",
- "rand_hc 0.3.0",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
 ]
 
 [[package]]
@@ -1903,18 +2024,18 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
 dependencies = [
- "autocfg 0.1.7",
+ "autocfg 0.1.8",
  "rand_core 0.3.1",
 ]
 
 [[package]]
 name = "rand_chacha"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
- "rand_core 0.6.2",
+ "rand_core 0.6.4",
 ]
 
 [[package]]
@@ -1934,11 +2055,11 @@ checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
 
 [[package]]
 name = "rand_core"
-version = "0.6.2"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
- "getrandom 0.2.2",
+ "getrandom",
 ]
 
 [[package]]
@@ -1951,15 +2072,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "rand_hc"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
-dependencies = [
- "rand_core 0.6.2",
-]
-
-[[package]]
 name = "rand_isaac"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1999,7 +2111,7 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
 dependencies = [
- "autocfg 0.1.7",
+ "autocfg 0.1.8",
  "rand_core 0.4.2",
 ]
 
@@ -2029,40 +2141,29 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
-dependencies = [
- "bitflags 1.2.1",
-]
-
-[[package]]
-name = "redox_users"
-version = "0.3.5"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
 dependencies = [
- "getrandom 0.1.16",
- "redox_syscall 0.1.57",
- "rust-argon2",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "regex"
-version = "1.4.5"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
+checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
 dependencies = [
  "aho-corasick",
- "memchr 2.3.4",
+ "memchr 2.5.0",
  "regex-syntax",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.23"
+version = "0.6.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
 
 [[package]]
 name = "relay"
@@ -2094,46 +2195,34 @@ dependencies = [
 
 [[package]]
 name = "rouille"
-version = "3.0.0"
+version = "3.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "112568052ec17fa26c6c11c40acbb30d3ad244bf3d6da0be181f5e7e42e5004f"
+checksum = "4f86e4c51a773f953f02bbab5fd049f004bfd384341d62da2a079aff812ab176"
 dependencies = [
- "base64 0.9.3",
- "brotli2",
+ "base64 0.13.1",
+ "brotli",
  "chrono",
  "deflate",
  "filetime",
  "multipart",
  "num_cpus",
- "rand 0.5.6",
+ "percent-encoding 2.2.0",
+ "rand 0.8.5",
  "serde",
  "serde_derive",
  "serde_json",
- "sha1",
- "term",
+ "sha1 0.10.5",
  "threadpool",
- "time",
+ "time 0.3.17",
  "tiny_http",
- "url",
-]
-
-[[package]]
-name = "rust-argon2"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
-dependencies = [
- "base64 0.13.0",
- "blake2b_simd",
- "constant_time_eq",
- "crossbeam-utils 0.8.3",
+ "url 2.3.1",
 ]
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.18"
+version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
 
 [[package]]
 name = "rustc_version"
@@ -2146,9 +2235,9 @@ dependencies = [
 
 [[package]]
 name = "ryu"
-version = "1.0.5"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
 
 [[package]]
 name = "safemem"
@@ -2158,21 +2247,21 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
 
 [[package]]
 name = "schannel"
-version = "0.1.19"
+version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
+checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
 dependencies = [
  "lazy_static",
- "winapi 0.3.9",
+ "windows-sys 0.36.1",
 ]
 
 [[package]]
 name = "scheduled-thread-pool"
-version = "0.2.5"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
+checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf"
 dependencies = [
- "parking_lot 0.11.1",
+ "parking_lot 0.12.1",
 ]
 
 [[package]]
@@ -2194,6 +2283,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
+name = "scratch"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
+
+[[package]]
 name = "semver"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2210,28 +2305,28 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 
 [[package]]
 name = "serde"
-version = "1.0.125"
+version = "1.0.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
+checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
 
 [[package]]
 name = "serde_derive"
-version = "1.0.125"
+version = "1.0.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
+checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
 dependencies = [
- "proc-macro2 1.0.26",
- "quote 1.0.9",
- "syn 1.0.68",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.105",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.64"
+version = "1.0.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
+checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
 dependencies = [
- "itoa",
+ "itoa 1.0.4",
  "ryu",
  "serde",
 ]
@@ -2243,46 +2338,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a"
 dependencies = [
  "dtoa",
- "itoa",
+ "itoa 0.4.8",
  "serde",
- "url",
+ "url 1.7.2",
 ]
 
 [[package]]
 name = "sha1"
-version = "0.6.0"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
+checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
+dependencies = [
+ "sha1_smol",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+dependencies = [
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "digest 0.10.6",
+]
+
+[[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
 
 [[package]]
 name = "sha2"
-version = "0.9.3"
+version = "0.9.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
 dependencies = [
- "block-buffer",
+ "block-buffer 0.9.0",
  "cfg-if 1.0.0",
- "cpuid-bool 0.1.2",
- "digest",
+ "cpufeatures",
+ "digest 0.9.0",
  "opaque-debug",
 ]
 
 [[package]]
 name = "signal-hook-registry"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
 dependencies = [
  "libc",
 ]
 
 [[package]]
-name = "siphasher"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
-
-[[package]]
 name = "slab"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2290,9 +2399,12 @@ checksum = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
 
 [[package]]
 name = "slab"
-version = "0.4.2"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg 1.1.0",
+]
 
 [[package]]
 name = "smallvec"
@@ -2311,9 +2423,9 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.6.1"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
 
 [[package]]
 name = "socket2"
@@ -2327,6 +2439,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "socket2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+dependencies = [
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
 name = "stable_deref_trait"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2349,9 +2471,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 
 [[package]]
 name = "subtle"
-version = "2.4.0"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
 
 [[package]]
 name = "syn"
@@ -2388,13 +2510,13 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.68"
+version = "1.0.105"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
+checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
 dependencies = [
- "proc-macro2 1.0.26",
- "quote 1.0.9",
- "unicode-xid 0.2.1",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -2408,14 +2530,14 @@ dependencies = [
 
 [[package]]
 name = "synstructure"
-version = "0.12.4"
+version = "0.12.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
+checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
 dependencies = [
- "proc-macro2 1.0.26",
- "quote 1.0.9",
- "syn 1.0.68",
- "unicode-xid 0.2.1",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.105",
+ "unicode-xid 0.2.4",
 ]
 
 [[package]]
@@ -2425,31 +2547,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
 
 [[package]]
-name = "tempdir"
-version = "0.3.7"
+name = "tempfile"
+version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
 dependencies = [
- "rand 0.4.6",
+ "cfg-if 1.0.0",
+ "fastrand",
+ "libc",
+ "redox_syscall 0.2.16",
  "remove_dir_all",
-]
-
-[[package]]
-name = "term"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
-dependencies = [
- "byteorder",
- "dirs",
  "winapi 0.3.9",
 ]
 
 [[package]]
 name = "termcolor"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
 dependencies = [
  "winapi-util",
 ]
@@ -2474,9 +2589,9 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.1.44"
+version = "0.1.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
 dependencies = [
  "libc",
  "wasi 0.10.0+wasi-snapshot-preview1",
@@ -2484,23 +2599,40 @@ dependencies = [
 ]
 
 [[package]]
+name = "time"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
+dependencies = [
+ "libc",
+ "num_threads",
+ "serde",
+ "time-core",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
+
+[[package]]
 name = "tiny_http"
-version = "0.6.2"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1661fa0a44c95d01604bd05c66732a446c657efb62b5164a7a083a3b552b4951"
+checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
 dependencies = [
  "ascii",
- "chrono",
  "chunked_transfer",
- "log 0.4.14",
- "url",
+ "httpdate",
+ "log 0.4.17",
 ]
 
 [[package]]
 name = "tinyvec"
-version = "1.2.0"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
 dependencies = [
  "tinyvec_macros",
 ]
@@ -2555,7 +2687,7 @@ dependencies = [
  "bytes",
  "futures",
  "iovec",
- "log 0.4.14",
+ "log 0.4.17",
  "mio",
  "scoped-tls",
  "tokio",
@@ -2604,7 +2736,7 @@ checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
 dependencies = [
  "bytes",
  "futures",
- "log 0.4.14",
+ "log 0.4.17",
 ]
 
 [[package]]
@@ -2634,11 +2766,11 @@ dependencies = [
  "crossbeam-utils 0.7.2",
  "futures",
  "lazy_static",
- "log 0.4.14",
+ "log 0.4.17",
  "mio",
  "num_cpus",
  "parking_lot 0.9.0",
- "slab 0.4.2",
+ "slab 0.4.7",
  "tokio-executor",
  "tokio-io",
  "tokio-sync",
@@ -2705,9 +2837,9 @@ dependencies = [
  "crossbeam-utils 0.7.2",
  "futures",
  "lazy_static",
- "log 0.4.14",
+ "log 0.4.17",
  "num_cpus",
- "slab 0.4.2",
+ "slab 0.4.7",
  "tokio-executor",
 ]
 
@@ -2719,7 +2851,7 @@ checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
 dependencies = [
  "crossbeam-utils 0.7.2",
  "futures",
- "slab 0.4.2",
+ "slab 0.4.7",
  "tokio-executor",
 ]
 
@@ -2731,7 +2863,7 @@ checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82"
 dependencies = [
  "bytes",
  "futures",
- "log 0.4.14",
+ "log 0.4.17",
  "mio",
  "tokio-codec",
  "tokio-io",
@@ -2748,7 +2880,7 @@ dependencies = [
  "futures",
  "iovec",
  "libc",
- "log 0.4.14",
+ "log 0.4.17",
  "mio",
  "mio-uds",
  "tokio-codec",
@@ -2774,19 +2906,19 @@ dependencies = [
  "byteorder",
  "failure",
  "futures",
- "idna",
+ "idna 0.1.5",
  "lazy_static",
- "log 0.4.14",
+ "log 0.4.17",
  "rand 0.5.6",
  "smallvec 0.6.14",
- "socket2",
+ "socket2 0.3.19",
  "tokio-executor",
  "tokio-io",
  "tokio-reactor",
  "tokio-tcp",
  "tokio-timer",
  "tokio-udp",
- "url",
+ "url 1.7.2",
 ]
 
 [[package]]
@@ -2798,19 +2930,19 @@ dependencies = [
  "byteorder",
  "failure",
  "futures",
- "idna",
+ "idna 0.1.5",
  "lazy_static",
- "log 0.4.14",
+ "log 0.4.17",
  "rand 0.5.6",
  "smallvec 0.6.14",
- "socket2",
+ "socket2 0.3.19",
  "tokio-executor",
  "tokio-io",
  "tokio-reactor",
  "tokio-tcp",
  "tokio-timer",
  "tokio-udp",
- "url",
+ "url 1.7.2",
 ]
 
 [[package]]
@@ -2824,7 +2956,7 @@ dependencies = [
  "futures",
  "ipconfig",
  "lazy_static",
- "log 0.4.14",
+ "log 0.4.17",
  "lru-cache",
  "resolv-conf",
  "smallvec 0.6.14",
@@ -2844,7 +2976,7 @@ version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
 dependencies = [
- "memchr 2.3.4",
+ "memchr 2.5.0",
 ]
 
 [[package]]
@@ -2855,18 +2987,9 @@ checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
 
 [[package]]
 name = "typenum"
-version = "1.13.0"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
-
-[[package]]
-name = "unicase"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
-dependencies = [
- "version_check 0.1.5",
-]
+checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
 
 [[package]]
 name = "unicase"
@@ -2874,32 +2997,35 @@ version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
 dependencies = [
- "version_check 0.9.3",
+ "version_check 0.9.4",
 ]
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.4"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
-dependencies = [
- "matches",
-]
+checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.17"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
 dependencies = [
  "tinyvec",
 ]
 
 [[package]]
 name = "unicode-width"
-version = "0.1.8"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
 
 [[package]]
 name = "unicode-xid"
@@ -2915,9 +3041,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
 
 [[package]]
 name = "unicode-xid"
-version = "0.2.1"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
 
 [[package]]
 name = "unicode_categories"
@@ -2927,9 +3053,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
 
 [[package]]
 name = "universal-hash"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402"
+checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
 dependencies = [
  "generic-array",
  "subtle",
@@ -2942,19 +3068,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
 dependencies = [
  "encoding",
- "idna",
+ "idna 0.1.5",
  "matches",
  "percent-encoding 1.0.1",
 ]
 
 [[package]]
+name = "url"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
+dependencies = [
+ "form_urlencoded",
+ "idna 0.3.0",
+ "percent-encoding 2.2.0",
+]
+
+[[package]]
 name = "url_serde"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "74e7d099f1ee52f823d4bdd60c93c3602043c728f5db3b97bdb548467f7bddea"
 dependencies = [
  "serde",
- "url",
+ "url 1.7.2",
 ]
 
 [[package]]
@@ -2982,9 +3119,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c2ca2a14bc3fc5b64d188b087a7d3a927df87b152e941ccfbc66672e20c467ae"
 dependencies = [
  "nom 4.2.3",
- "proc-macro2 1.0.26",
- "quote 1.0.9",
- "syn 1.0.68",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.105",
 ]
 
 [[package]]
@@ -2999,9 +3136,9 @@ dependencies = [
 
 [[package]]
 name = "vcpkg"
-version = "0.2.11"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
 [[package]]
 name = "vec_map"
@@ -3017,9 +3154,9 @@ checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
 
 [[package]]
 name = "version_check"
-version = "0.9.3"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "want"
@@ -3028,21 +3165,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1"
 dependencies = [
  "futures",
- "log 0.4.14",
+ "log 0.4.17",
  "try-lock",
 ]
 
 [[package]]
 name = "wasi"
-version = "0.9.0+wasi-snapshot-preview1"
+version = "0.10.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
 
 [[package]]
 name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if 1.0.0",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log 0.4.17",
+ "once_cell",
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.105",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote 1.0.21",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.105",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
 
 [[package]]
 name = "widestring"
@@ -3094,6 +3285,106 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc 0.36.1",
+ "windows_i686_gnu 0.36.1",
+ "windows_i686_msvc 0.36.1",
+ "windows_x86_64_gnu 0.36.1",
+ "windows_x86_64_msvc 0.36.1",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.42.0",
+ "windows_i686_gnu 0.42.0",
+ "windows_i686_msvc 0.42.0",
+ "windows_x86_64_gnu 0.42.0",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.42.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+
+[[package]]
 name = "winreg"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/web/converse/default.nix b/web/converse/default.nix
index cc2dab7f88..7a6583d31e 100644
--- a/web/converse/default.nix
+++ b/web/converse/default.nix
@@ -3,5 +3,5 @@
 depot.third_party.naersk.buildPackage {
   src = ./.;
   buildInputs = with pkgs; [ openssl postgresql.lib ];
-  nativeBuildInputs = [ pkgs.pkgconfig ];
+  nativeBuildInputs = [ pkgs.pkg-config ];
 }
diff --git a/web/converse/src/db.rs b/web/converse/src/db.rs
index ae186bdf4e..a0d8915504 100644
--- a/web/converse/src/db.rs
+++ b/web/converse/src/db.rs
@@ -19,13 +19,13 @@
 //! This module implements the database executor, which holds the
 //! database connection and performs queries on it.
 
+use crate::errors::{ConverseError, Result};
+use crate::models::*;
 use actix::prelude::*;
-use diesel::{self, sql_query};
-use diesel::sql_types::Text;
 use diesel::prelude::*;
-use diesel::r2d2::{Pool, ConnectionManager};
-use crate::models::*;
-use crate::errors::{ConverseError, Result};
+use diesel::r2d2::{ConnectionManager, Pool};
+use diesel::sql_types::Text;
+use diesel::{self, sql_query};
 
 /// Raw PostgreSQL query used to perform full-text search on posts
 /// with a supplied phrase. For now, the query language is hardcoded
@@ -50,14 +50,12 @@ pub struct DbExecutor(pub Pool<ConnectionManager<PgConnection>>);
 
 impl DbExecutor {
     /// Request a list of threads.
-    //
     // TODO(tazjin): This should support pagination.
     pub fn list_threads(&self) -> Result<Vec<ThreadIndex>> {
         use crate::schema::thread_index::dsl::*;
 
         let conn = self.0.get()?;
-        let results = thread_index
-            .load::<ThreadIndex>(&conn)?;
+        let results = thread_index.load::<ThreadIndex>(&conn)?;
         Ok(results)
     }
 
@@ -69,9 +67,7 @@ impl DbExecutor {
 
         let conn = self.0.get()?;
 
-        let opt_user = users
-            .filter(email.eq(email))
-            .first(&conn).optional()?;
+        let opt_user = users.filter(email.eq(email)).first(&conn).optional()?;
 
         if let Some(user) = opt_user {
             Ok(user)
@@ -93,12 +89,11 @@ impl DbExecutor {
 
     /// Fetch a specific thread and return it with its posts.
     pub fn get_thread(&self, thread_id: i32) -> Result<(Thread, Vec<SimplePost>)> {
-        use crate::schema::threads::dsl::*;
         use crate::schema::simple_posts::dsl::id;
+        use crate::schema::threads::dsl::*;
 
         let conn = self.0.get()?;
-        let thread_result: Thread = threads
-            .find(thread_id).first(&conn)?;
+        let thread_result: Thread = threads.find(thread_id).first(&conn)?;
 
         let post_list = SimplePost::belonging_to(&thread_result)
             .order_by(id.asc())
@@ -127,8 +122,7 @@ impl DbExecutor {
 
     /// Create a new thread.
     pub fn create_thread(&self, new_thread: NewThread, post_text: String) -> Result<Thread> {
-                use crate::schema::threads;
-        use crate::schema::posts;
+        use crate::schema::{posts, threads};
 
         let conn = self.0.get()?;
 
@@ -161,20 +155,21 @@ impl DbExecutor {
 
         let closed: bool = {
             use crate::schema::threads::dsl::*;
-            threads.select(closed)
+            threads
+                .select(closed)
                 .find(new_post.thread_id)
                 .first(&conn)?
         };
 
         if closed {
             return Err(ConverseError::ThreadClosed {
-                id: new_post.thread_id
-            })
+                id: new_post.thread_id,
+            });
         }
 
         Ok(diesel::insert_into(posts::table)
-           .values(&new_post)
-           .get_result(&conn)?)
+            .values(&new_post)
+            .get_result(&conn)?)
     }
 
     /// Search for posts.
@@ -197,7 +192,6 @@ impl DbExecutor {
     }
 }
 
-
 // Old actor implementation:
 
 impl Actor for DbExecutor {
@@ -216,9 +210,7 @@ message!(LookupOrCreateUser, Result<User>);
 impl Handler<LookupOrCreateUser> for DbExecutor {
     type Result = <LookupOrCreateUser as Message>::Result;
 
-    fn handle(&mut self,
-              _: LookupOrCreateUser,
-              _: &mut Self::Context) -> Self::Result {
+    fn handle(&mut self, _: LookupOrCreateUser, _: &mut Self::Context) -> Self::Result {
         unimplemented!()
     }
 }
@@ -238,7 +230,9 @@ impl Handler<GetThread> for DbExecutor {
 
 /// Message used to fetch a specific post.
 #[derive(Deserialize, Debug)]
-pub struct GetPost { pub id: i32 }
+pub struct GetPost {
+    pub id: i32,
+}
 
 message!(GetPost, Result<SimplePost>);
 
@@ -296,7 +290,9 @@ impl Handler<CreatePost> for DbExecutor {
 
 /// Message used to search for posts
 #[derive(Deserialize)]
-pub struct SearchPosts { pub query: String }
+pub struct SearchPosts {
+    pub query: String,
+}
 message!(SearchPosts, Result<Vec<SearchResult>>);
 
 impl Handler<SearchPosts> for DbExecutor {
diff --git a/web/converse/src/errors.rs b/web/converse/src/errors.rs
index 32507c51b0..a4bd69023b 100644
--- a/web/converse/src/errors.rs
+++ b/web/converse/src/errors.rs
@@ -21,17 +21,12 @@
 //! are established in a similar way as was tradition in
 //! `error_chain`, albeit manually.
 
-use std::result;
-use actix_web::{ResponseError, HttpResponse};
 use actix_web::http::StatusCode;
+use actix_web::{HttpResponse, ResponseError};
+use std::result;
 
 // Modules with foreign errors:
-use actix;
-use actix_web;
-use askama;
-use diesel;
-use r2d2;
-use tokio_timer;
+use {actix, actix_web, askama, diesel, r2d2, tokio_timer};
 
 pub type Result<T> = result::Result<T, ConverseError>;
 pub type ConverseResult<T> = result::Result<T, ConverseError>;
@@ -96,7 +91,9 @@ impl From<askama::Error> for ConverseError {
 
 impl From<actix::MailboxError> for ConverseError {
     fn from(error: actix::MailboxError) -> ConverseError {
-        ConverseError::Actix { error: Box::new(error) }
+        ConverseError::Actix {
+            error: Box::new(error),
+        }
     }
 }
 
@@ -136,7 +133,7 @@ impl ResponseError for ConverseError {
                 .header("Location", format!("/thread/{}#post-reply", id))
                 .finish(),
             _ => HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR)
-                .body(format!("An error occured: {}", self))
+                .body(format!("An error occured: {}", self)),
         }
     }
 }
diff --git a/web/converse/src/handlers.rs b/web/converse/src/handlers.rs
index 0759cec5c1..49f9dcf974 100644
--- a/web/converse/src/handlers.rs
+++ b/web/converse/src/handlers.rs
@@ -23,22 +23,22 @@
 //! the tera templates stored in the `/templates` directory in the
 //! project root.
 
-use actix::prelude::*;
-use actix_web::*;
-use actix_web::http::Method;
-use actix_web::middleware::identity::RequestIdentity;
-use actix_web::middleware::{Started, Middleware};
-use actix_web;
 use crate::db::*;
-use crate::errors::{ConverseResult, ConverseError};
-use futures::Future;
+use crate::errors::{ConverseError, ConverseResult};
 use crate::models::*;
 use crate::oidc::*;
 use crate::render::*;
+use actix::prelude::*;
+use actix_web;
+use actix_web::http::Method;
+use actix_web::middleware::identity::RequestIdentity;
+use actix_web::middleware::{Middleware, Started};
+use actix_web::*;
+use futures::Future;
 
 use rouille::{Request, Response};
 
-type ConverseResponse = Box<dyn Future<Item=HttpResponse, Error=ConverseError>>;
+type ConverseResponse = Box<dyn Future<Item = HttpResponse, Error = ConverseError>>;
 
 const HTML: &'static str = "text/html";
 const ANONYMOUS: i32 = 1;
@@ -84,23 +84,31 @@ pub fn get_user_id_rouille(_req: &Request) -> i32 {
     ANONYMOUS
 }
 
-pub fn forum_thread_rouille(req: &Request, db: &DbExecutor, thread_id: i32)
-                            -> ConverseResult<Response> {
+pub fn forum_thread_rouille(
+    req: &Request,
+    db: &DbExecutor,
+    thread_id: i32,
+) -> ConverseResult<Response> {
     let user = get_user_id_rouille(&req);
     let thread = db.get_thread(thread_id)?;
     Ok(Response::html(thread_page(user, thread.0, thread.1)?))
 }
 
 /// This handler retrieves and displays a single forum thread.
-pub fn forum_thread(_: State<AppState>,
-                    _: HttpRequest<AppState>,
-                    _: Path<i32>) -> ConverseResponse {
+pub fn forum_thread(
+    _: State<AppState>,
+    _: HttpRequest<AppState>,
+    _: Path<i32>,
+) -> ConverseResponse {
     unimplemented!()
 }
 
 /// This handler presents the user with the "New Thread" form.
 pub fn new_thread(state: State<AppState>) -> ConverseResponse {
-    state.renderer.send(NewThreadPage::default()).flatten()
+    state
+        .renderer
+        .send(NewThreadPage::default())
+        .flatten()
         .map(|res| HttpResponse::Ok().content_type(HTML).body(res))
         .responder()
 }
@@ -113,9 +121,9 @@ pub struct NewThreadForm {
 
 /// This handler receives a "New thread"-form and redirects the user
 /// to the new thread after creation.
-pub fn submit_thread((state, input, req): (State<AppState>,
-                                           Form<NewThreadForm>,
-                                           HttpRequest<AppState>)) -> ConverseResponse {
+pub fn submit_thread(
+    (state, input, req): (State<AppState>, Form<NewThreadForm>, HttpRequest<AppState>),
+) -> ConverseResponse {
     // Trim whitespace out of inputs:
     let input = NewThreadForm {
         title: input.title.trim().into(),
@@ -124,7 +132,8 @@ pub fn submit_thread((state, input, req): (State<AppState>,
 
     // Perform simple validation and abort here if it fails:
     if input.title.is_empty() || input.post.is_empty() {
-        return state.renderer
+        return state
+            .renderer
             .send(NewThreadPage {
                 alerts: vec![NEW_THREAD_LENGTH_ERR],
                 title: Some(input.title),
@@ -147,14 +156,19 @@ pub fn submit_thread((state, input, req): (State<AppState>,
         post: input.post,
     };
 
-    state.db.send(msg)
+    state
+        .db
+        .send(msg)
         .from_err()
         .and_then(move |res| {
             let thread = res?;
-            info!("Created new thread \"{}\" with ID {}", thread.title, thread.id);
+            info!(
+                "Created new thread \"{}\" with ID {}",
+                thread.title, thread.id
+            );
             Ok(HttpResponse::SeeOther()
-               .header("Location", format!("/thread/{}", thread.id))
-               .finish())
+                .header("Location", format!("/thread/{}", thread.id))
+                .finish())
         })
         .responder()
 }
@@ -167,9 +181,11 @@ pub struct NewPostForm {
 
 /// This handler receives a "Reply"-form and redirects the user to the
 /// new post after creation.
-pub fn reply_thread(state: State<AppState>,
-                    input: Form<NewPostForm>,
-                    req: HttpRequest<AppState>) -> ConverseResponse {
+pub fn reply_thread(
+    state: State<AppState>,
+    input: Form<NewPostForm>,
+    req: HttpRequest<AppState>,
+) -> ConverseResponse {
     let user_id = get_user_id(&req);
 
     let new_post = NewPost {
@@ -178,14 +194,19 @@ pub fn reply_thread(state: State<AppState>,
         body: input.post.trim().into(),
     };
 
-    state.db.send(CreatePost(new_post))
+    state
+        .db
+        .send(CreatePost(new_post))
         .flatten()
         .from_err()
         .and_then(move |post| {
             info!("Posted reply {} to thread {}", post.id, post.thread_id);
             Ok(HttpResponse::SeeOther()
-               .header("Location", format!("/thread/{}#post-{}", post.thread_id, post.id))
-               .finish())
+                .header(
+                    "Location",
+                    format!("/thread/{}#post-{}", post.thread_id, post.id),
+                )
+                .finish())
         })
         .responder()
 }
@@ -194,12 +215,16 @@ pub fn reply_thread(state: State<AppState>,
 /// the user attempts to edit a post that they do not have access to,
 /// they are currently ungracefully redirected back to the post
 /// itself.
-pub fn edit_form(state: State<AppState>,
-                 req: HttpRequest<AppState>,
-                 query: Path<GetPost>) -> ConverseResponse {
+pub fn edit_form(
+    state: State<AppState>,
+    req: HttpRequest<AppState>,
+    query: Path<GetPost>,
+) -> ConverseResponse {
     let user_id = get_user_id(&req);
 
-    state.db.send(query.into_inner())
+    state
+        .db
+        .send(query.into_inner())
         .flatten()
         .from_err()
         .and_then(move |post| {
@@ -227,17 +252,21 @@ pub fn edit_form(state: State<AppState>,
 
 /// This handler "executes" an edit to a post if the current user owns
 /// the edited post.
-pub fn edit_post(state: State<AppState>,
-                 req: HttpRequest<AppState>,
-                 update: Form<UpdatePost>) -> ConverseResponse {
+pub fn edit_post(
+    state: State<AppState>,
+    req: HttpRequest<AppState>,
+    update: Form<UpdatePost>,
+) -> ConverseResponse {
     let user_id = get_user_id(&req);
 
-    state.db.send(GetPost { id: update.post_id })
+    state
+        .db
+        .send(GetPost { id: update.post_id })
         .flatten()
         .from_err()
         .and_then(move |post| {
             if user_id != 1 && post.user_id == user_id {
-                 Ok(())
+                Ok(())
             } else {
                 Err(ConverseError::PostEditForbidden {
                     user: user_id,
@@ -247,24 +276,34 @@ pub fn edit_post(state: State<AppState>,
         })
         .and_then(move |_| state.db.send(update.0).from_err())
         .flatten()
-        .map(|updated| HttpResponse::SeeOther()
-             .header("Location", format!("/thread/{}#post-{}",
-                                         updated.thread_id, updated.id))
-             .finish())
+        .map(|updated| {
+            HttpResponse::SeeOther()
+                .header(
+                    "Location",
+                    format!("/thread/{}#post-{}", updated.thread_id, updated.id),
+                )
+                .finish()
+        })
         .responder()
 }
 
 /// This handler executes a full-text search on the forum database and
 /// displays the results to the user.
-pub fn search_forum(state: State<AppState>,
-                    query: Query<SearchPosts>) -> ConverseResponse {
+pub fn search_forum(state: State<AppState>, query: Query<SearchPosts>) -> ConverseResponse {
     let query_string = query.query.clone();
-    state.db.send(query.into_inner())
+    state
+        .db
+        .send(query.into_inner())
         .flatten()
-        .and_then(move |results| state.renderer.send(SearchResultPage {
-            results,
-            query: query_string,
-        }).from_err())
+        .and_then(move |results| {
+            state
+                .renderer
+                .send(SearchResultPage {
+                    results,
+                    query: query_string,
+                })
+                .from_err()
+        })
         .flatten()
         .map(|res| HttpResponse::Ok().content_type(HTML).body(res))
         .responder()
@@ -272,11 +311,15 @@ pub fn search_forum(state: State<AppState>,
 
 /// This handler initiates an OIDC login.
 pub fn login(state: State<AppState>) -> ConverseResponse {
-    state.oidc.send(GetLoginUrl)
+    state
+        .oidc
+        .send(GetLoginUrl)
         .from_err()
-        .and_then(|url| Ok(HttpResponse::TemporaryRedirect()
-                           .header("Location", url)
-                           .finish()))
+        .and_then(|url| {
+            Ok(HttpResponse::TemporaryRedirect()
+                .header("Location", url)
+                .finish())
+        })
         .responder()
 }
 
@@ -286,21 +329,26 @@ pub fn login(state: State<AppState>) -> ConverseResponse {
 /// provider and a user lookup is performed. If a user with a matching
 /// email-address is found in the database, it is logged in -
 /// otherwise a new user is created.
-pub fn callback(state: State<AppState>,
-                data: Form<CodeResponse>,
-                req: HttpRequest<AppState>) -> ConverseResponse {
-    state.oidc.send(RetrieveToken(data.0)).flatten()
+pub fn callback(
+    state: State<AppState>,
+    data: Form<CodeResponse>,
+    req: HttpRequest<AppState>,
+) -> ConverseResponse {
+    state
+        .oidc
+        .send(RetrieveToken(data.0))
+        .flatten()
         .map(|author| LookupOrCreateUser {
             email: author.email,
             name: author.name,
         })
-        .and_then(move |msg| state.db.send(msg).from_err()).flatten()
+        .and_then(move |msg| state.db.send(msg).from_err())
+        .flatten()
         .and_then(move |user| {
             info!("Completed login for user {} ({})", user.email, user.id);
             req.remember(user.id.to_string());
-            Ok(HttpResponse::SeeOther()
-               .header("Location", "/")
-               .finish())})
+            Ok(HttpResponse::SeeOther().header("Location", "/").finish())
+        })
         .responder()
 }
 
@@ -317,9 +365,7 @@ impl EmbeddedFile for App<AppState> {
     fn static_file(self, path: &'static str, content: &'static [u8]) -> Self {
         self.route(path, Method::GET, move |_: HttpRequest<_>| {
             let mime = format!("{}", mime_guess::from_path(path).first_or_octet_stream());
-            HttpResponse::Ok()
-                .content_type(mime.as_str())
-                .body(content)
+            HttpResponse::Ok().content_type(mime.as_str()).body(content)
         })
     }
 }
@@ -327,7 +373,7 @@ impl EmbeddedFile for App<AppState> {
 /// Middleware used to enforce logins unceremoniously.
 pub struct RequireLogin;
 
-impl <S> Middleware<S> for RequireLogin {
+impl<S> Middleware<S> for RequireLogin {
     fn start(&self, req: &HttpRequest<S>) -> actix_web::Result<Started> {
         let logged_in = req.identity().is_some();
         let is_oidc_req = req.path().starts_with("/oidc");
@@ -336,7 +382,7 @@ impl <S> Middleware<S> for RequireLogin {
             Ok(Started::Response(
                 HttpResponse::SeeOther()
                     .header("Location", "/oidc/login")
-                    .finish()
+                    .finish(),
             ))
         } else {
             Ok(Started::Done)
diff --git a/web/converse/src/main.rs b/web/converse/src/main.rs
index 6d6e9ac710..78d0241600 100644
--- a/web/converse/src/main.rs
+++ b/web/converse/src/main.rs
@@ -30,7 +30,6 @@ extern crate log;
 #[macro_use]
 extern crate serde_derive;
 
-extern crate rouille;
 extern crate actix;
 extern crate actix_web;
 extern crate chrono;
@@ -44,6 +43,7 @@ extern crate md5;
 extern crate mime_guess;
 extern crate r2d2;
 extern crate rand;
+extern crate rouille;
 extern crate serde;
 extern crate serde_json;
 extern crate tokio;
@@ -58,7 +58,7 @@ macro_rules! message {
         impl Message for $t {
             type Result = $r;
         }
-    }
+    };
 }
 
 pub mod db;
@@ -69,18 +69,18 @@ pub mod oidc;
 pub mod render;
 pub mod schema;
 
+use crate::db::*;
+use crate::handlers::*;
+use crate::oidc::OidcExecutor;
+use crate::render::Renderer;
 use actix::prelude::*;
-use actix_web::*;
 use actix_web::http::Method;
+use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
 use actix_web::middleware::Logger;
-use actix_web::middleware::identity::{IdentityService, CookieIdentityPolicy};
-use crate::db::*;
+use actix_web::*;
 use diesel::pg::PgConnection;
 use diesel::r2d2::{ConnectionManager, Pool};
-use crate::handlers::*;
-use crate::oidc::OidcExecutor;
 use rand::{OsRng, Rng};
-use crate::render::Renderer;
 use std::env;
 
 fn config(name: &str) -> String {
@@ -96,16 +96,18 @@ fn start_db_executor() -> Addr<DbExecutor> {
     let db_url = config("DATABASE_URL");
 
     let manager = ConnectionManager::<PgConnection>::new(db_url);
-    let pool = Pool::builder().build(manager).expect("Failed to initialise DB pool");
+    let pool = Pool::builder()
+        .build(manager)
+        .expect("Failed to initialise DB pool");
 
     SyncArbiter::start(2, move || DbExecutor(pool.clone()))
 }
 
 fn schedule_search_refresh(db: Addr<DbExecutor>) {
+    use std::thread;
+    use std::time::{Duration, Instant};
     use tokio::prelude::*;
     use tokio::timer::Interval;
-    use std::time::{Duration, Instant};
-    use std::thread;
 
     let task = Interval::new(Instant::now(), Duration::from_secs(60))
         .from_err()
@@ -118,8 +120,8 @@ fn schedule_search_refresh(db: Addr<DbExecutor>) {
 fn start_oidc_executor(base_url: &str) -> Addr<OidcExecutor> {
     info!("Initialising OIDC integration ...");
     let oidc_url = config("OIDC_DISCOVERY_URL");
-    let oidc_config = oidc::load_oidc(&oidc_url)
-        .expect("Failed to retrieve OIDC discovery document");
+    let oidc_config =
+        oidc::load_oidc(&oidc_url).expect("Failed to retrieve OIDC discovery document");
 
     let oidc = oidc::OidcExecutor {
         oidc_config,
@@ -132,7 +134,7 @@ fn start_oidc_executor(base_url: &str) -> Addr<OidcExecutor> {
 }
 
 fn start_renderer() -> Addr<Renderer> {
-    let comrak = comrak::ComrakOptions{
+    let comrak = comrak::ComrakOptions {
         github_pre_lang: true,
         ext_strikethrough: true,
         ext_table: true,
@@ -143,22 +145,23 @@ fn start_renderer() -> Addr<Renderer> {
         ..Default::default()
     };
 
-    Renderer{ comrak }.start()
+    Renderer { comrak }.start()
 }
 
 fn gen_session_key() -> [u8; 64] {
     let mut key_bytes = [0; 64];
-    let mut rng = OsRng::new()
-        .expect("Failed to retrieve RNG for key generation");
+    let mut rng = OsRng::new().expect("Failed to retrieve RNG for key generation");
     rng.fill_bytes(&mut key_bytes);
 
     key_bytes
 }
 
-fn start_http_server(base_url: String,
-                     db_addr: Addr<DbExecutor>,
-                     oidc_addr: Addr<OidcExecutor>,
-                     renderer_addr: Addr<Renderer>) {
+fn start_http_server(
+    base_url: String,
+    db_addr: Addr<DbExecutor>,
+    oidc_addr: Addr<OidcExecutor>,
+    renderer_addr: Addr<Renderer>,
+) {
     info!("Initialising HTTP server ...");
     let bind_host = config_default("CONVERSE_BIND_HOST", "127.0.0.1:4567");
     let key = gen_session_key();
@@ -175,7 +178,7 @@ fn start_http_server(base_url: String,
             CookieIdentityPolicy::new(&key)
                 .name("converse_auth")
                 .path("/")
-                .secure(base_url.starts_with("https"))
+                .secure(base_url.starts_with("https")),
         );
 
         let app = App::with_state(state)
@@ -183,25 +186,37 @@ fn start_http_server(base_url: String,
             .middleware(identity)
             .resource("/", |r| r.method(Method::GET).with(forum_index))
             .resource("/thread/new", |r| r.method(Method::GET).with(new_thread))
-            .resource("/thread/submit", |r| r.method(Method::POST).with(submit_thread))
-            .resource("/thread/reply", |r| r.method(Method::POST).with(reply_thread))
+            .resource("/thread/submit", |r| {
+                r.method(Method::POST).with(submit_thread)
+            })
+            .resource("/thread/reply", |r| {
+                r.method(Method::POST).with(reply_thread)
+            })
             .resource("/thread/{id}", |r| r.method(Method::GET).with(forum_thread))
             .resource("/post/{id}/edit", |r| r.method(Method::GET).with(edit_form))
             .resource("/post/edit", |r| r.method(Method::POST).with(edit_post))
             .resource("/search", |r| r.method(Method::GET).with(search_forum))
             .resource("/oidc/login", |r| r.method(Method::GET).with(login))
             .resource("/oidc/callback", |r| r.method(Method::POST).with(callback))
-            .static_file("/static/highlight.css", include_bytes!("../static/highlight.css"))
-            .static_file("/static/highlight.js", include_bytes!("../static/highlight.js"))
+            .static_file(
+                "/static/highlight.css",
+                include_bytes!("../static/highlight.css"),
+            )
+            .static_file(
+                "/static/highlight.js",
+                include_bytes!("../static/highlight.js"),
+            )
             .static_file("/static/styles.css", include_bytes!("../static/styles.css"));
 
         if require_login {
             app.middleware(RequireLogin)
         } else {
             app
-        }})
-        .bind(&bind_host).expect(&format!("Could not bind on '{}'", bind_host))
-        .start();
+        }
+    })
+    .bind(&bind_host)
+    .expect(&format!("Could not bind on '{}'", bind_host))
+    .start();
 }
 
 fn main() {
diff --git a/web/converse/src/models.rs b/web/converse/src/models.rs
index da628f78b5..63b15fbed0 100644
--- a/web/converse/src/models.rs
+++ b/web/converse/src/models.rs
@@ -16,9 +16,9 @@
 // along with this program. If not, see
 // <https://www.gnu.org/licenses/>.
 
+use crate::schema::{posts, simple_posts, threads, users};
 use chrono::prelude::{DateTime, Utc};
-use crate::schema::{users, threads, posts, simple_posts};
-use diesel::sql_types::{Text, Integer};
+use diesel::sql_types::{Integer, Text};
 
 /// Represents a single user in the Converse database. Converse does
 /// not handle logins itself, but rather looks them up based on the
@@ -85,21 +85,21 @@ pub struct ThreadIndex {
 }
 
 #[derive(Deserialize, Insertable)]
-#[table_name="threads"]
+#[table_name = "threads"]
 pub struct NewThread {
     pub title: String,
     pub user_id: i32,
 }
 
 #[derive(Deserialize, Insertable)]
-#[table_name="users"]
+#[table_name = "users"]
 pub struct NewUser {
     pub email: String,
     pub name: String,
 }
 
 #[derive(Deserialize, Insertable)]
-#[table_name="posts"]
+#[table_name = "posts"]
 pub struct NewPost {
     pub thread_id: i32,
     pub body: String,
diff --git a/web/converse/src/oidc.rs b/web/converse/src/oidc.rs
index 9f566c04a7..75e3eabc88 100644
--- a/web/converse/src/oidc.rs
+++ b/web/converse/src/oidc.rs
@@ -22,12 +22,12 @@
 //! Currently Converse only supports a single OIDC provider. Note that
 //! this has so far only been tested with Office365.
 
-use actix::prelude::*;
 use crate::errors::*;
+use actix::prelude::*;
 use crimp::Request;
+use curl::easy::Form;
 use url::Url;
 use url_serde;
-use curl::easy::Form;
 
 /// This structure represents the contents of an OIDC discovery
 /// document.
@@ -114,20 +114,30 @@ impl Handler<RetrieveToken> for OidcExecutor {
         debug!("Received OAuth2 code, requesting access_token");
 
         let mut form = Form::new();
-        form.part("client_id").contents(&self.client_id.as_bytes())
-            .add().expect("critical error: invalid form data");
-
-        form.part("client_secret").contents(&self.client_secret.as_bytes())
-            .add().expect("critical error: invalid form data");
-
-        form.part("grant_type").contents("authorization_code".as_bytes())
-            .add().expect("critical error: invalid form data");
-
-        form.part("code").contents(&msg.0.code.as_bytes())
-            .add().expect("critical error: invalid form data");
-
-        form.part("redirect_uri").contents(&self.redirect_uri.as_bytes())
-            .add().expect("critical error: invalid form data");
+        form.part("client_id")
+            .contents(&self.client_id.as_bytes())
+            .add()
+            .expect("critical error: invalid form data");
+
+        form.part("client_secret")
+            .contents(&self.client_secret.as_bytes())
+            .add()
+            .expect("critical error: invalid form data");
+
+        form.part("grant_type")
+            .contents("authorization_code".as_bytes())
+            .add()
+            .expect("critical error: invalid form data");
+
+        form.part("code")
+            .contents(&msg.0.code.as_bytes())
+            .add()
+            .expect("critical error: invalid form data");
+
+        form.part("redirect_uri")
+            .contents(&self.redirect_uri.as_bytes())
+            .add()
+            .expect("critical error: invalid form data");
 
         let response = Request::post(&self.oidc_config.token_endpoint)
             .user_agent(concat!("converse-", env!("CARGO_PKG_VERSION")))?
@@ -142,7 +152,8 @@ impl Handler<RetrieveToken> for OidcExecutor {
             .user_agent(concat!("converse-", env!("CARGO_PKG_VERSION")))?
             .header("Authorization", &bearer)?
             .send()?
-            .as_json()?.body;
+            .as_json()?
+            .body;
 
         Ok(Author {
             name: user.name,
diff --git a/web/converse/src/render.rs b/web/converse/src/render.rs
index 749e77ef50..d06af12bd9 100644
--- a/web/converse/src/render.rs
+++ b/web/converse/src/render.rs
@@ -20,14 +20,14 @@
 //! data into whatever format is needed by the templates and rendering
 //! them.
 
-use actix::prelude::*;
-use askama::Template;
 use crate::errors::*;
-use std::fmt;
-use md5;
 use crate::models::*;
+use actix::prelude::*;
+use askama::Template;
 use chrono::prelude::{DateTime, Utc};
-use comrak::{ComrakOptions, markdown_to_html};
+use comrak::{markdown_to_html, ComrakOptions};
+use md5;
+use std::fmt;
 
 pub struct Renderer {
     pub comrak: ComrakOptions,
@@ -101,7 +101,9 @@ pub enum EditingMode {
 }
 
 impl Default for EditingMode {
-    fn default() -> EditingMode { EditingMode::NewThread }
+    fn default() -> EditingMode {
+        EditingMode::NewThread
+    }
 }
 
 /// This is the template used for rendering the new thread, edit post
@@ -215,19 +217,22 @@ pub fn index_page(threads: Vec<ThreadIndex>) -> Result<String> {
 
 // Render the page of a given thread.
 pub fn thread_page(user: i32, thread: Thread, posts: Vec<SimplePost>) -> Result<String> {
-    let posts = posts.into_iter().map(|post| {
-        let editable = user != 1 && post.user_id == user;
-
-        let comrak = ComrakOptions::default(); // TODO(tazjin): cheddar
-        RenderablePost {
-            id: post.id,
-            body: markdown_to_html(&post.body, &comrak),
-            posted: FormattedDate(post.posted),
-            author_name: post.author_name.clone(),
-            author_gravatar: md5_hex(post.author_email.as_bytes()),
-            editable,
-        }
-    }).collect();
+    let posts = posts
+        .into_iter()
+        .map(|post| {
+            let editable = user != 1 && post.user_id == user;
+
+            let comrak = ComrakOptions::default(); // TODO(tazjin): cheddar
+            RenderablePost {
+                id: post.id,
+                body: markdown_to_html(&post.body, &comrak),
+                posted: FormattedDate(post.posted),
+                author_name: post.author_name.clone(),
+                author_gravatar: md5_hex(post.author_email.as_bytes()),
+                editable,
+            }
+        })
+        .collect();
 
     let renderable = RenderableThreadPage {
         posts,
diff --git a/web/converse/src/schema.rs b/web/converse/src/schema.rs
index 7de6d13668..520af43422 100644
--- a/web/converse/src/schema.rs
+++ b/web/converse/src/schema.rs
@@ -80,9 +80,4 @@ joinable!(posts -> users (user_id));
 joinable!(threads -> users (user_id));
 joinable!(simple_posts -> threads (thread_id));
 
-allow_tables_to_appear_in_same_query!(
-    posts,
-    threads,
-    users,
-    simple_posts,
-);
+allow_tables_to_appear_in_same_query!(posts, threads, users, simple_posts,);
diff --git a/web/inbox.nix b/web/inbox.nix
new file mode 100644
index 0000000000..4bbf56300d
--- /dev/null
+++ b/web/inbox.nix
@@ -0,0 +1,81 @@
+# landing page for inbox.tvl.su
+
+{ depot, ... }:
+
+depot.web.tvl.template {
+  title = "TVL's public inbox";
+
+  # not hosted on whitby, so we need /latest
+  staticUrl = "https://static.tvl.su/latest";
+
+  extraHead = ''
+    <link rel="alternate" type="application/atom+xml" href="https://inbox.tvl.su/depot/new.atom" />
+  '';
+
+  content = ''
+    TVL's public inbox
+    ==================
+
+    This is the [public-inbox][] for [The Virus Lounge][TVL]. It is
+    essentially like a pull-based mailing list, where we discuss
+    anything related to our software or organisation, as well as
+    receive patches from external users.
+
+    ## Posting to the inbox
+
+    Anyone can send messages to the inbox by emailing
+    **depot@tvl.su**.
+
+    ## Accessing the inbox
+
+    There are several ways to access the inbox, depending on what is
+    most convenient for your personal email workflow.
+
+    ### Web browser
+
+    Go to [`/depot/`][inbox-html] to read the inbox in your web
+    browser. This is the easiest way to access messages, and with an
+    email client supporting `mailto:` links you can respond to
+    messages from there, too.
+
+    ### IMAP
+
+    The inbox is available via IMAP:
+
+    **Server:** `inbox.tvl.su`
+
+    **Port:** `993` (TLS enabled)
+
+    **Inbox:** `su.tvl.depot.0` (auto-discoverable)
+
+    You can use *any* credentials to log in, for example the username
+    `anonymous` with the password `kittens`. The server will just
+    ignore it.
+
+    TIP: There is a wrapper script in `//tools/fetch-depot-inbox` in
+    the TVL depot which you can use to synchronise the maildir to your
+    computer, which works for email clients like `notmuch`.
+
+    ### Atom feed
+
+    An Atom feed [is available][feed] and should work with your
+    favourite feed reader.
+
+    ### NNTP
+
+    News readers can access the inbox via NNTP:
+
+    **Server:** `inbox.tvl.su`
+
+    **Port:** `563` (TLS enabled)
+
+    **Group:** `su.tvl.depot.0` (auto-discoverable)
+
+    No credentials are required to access the server.
+
+    [public-inbox]: https://public-inbox.org/README.html
+    [TVL]: https://tvl.fyi
+    [inbox-html]: https://inbox.tvl.su/depot/
+    [feed]: https://inbox.tvl.su/depot/new.atom
+  '';
+}
diff --git a/web/panettone/OWNERS b/web/panettone/OWNERS
index b2b0acc303..5ad475b1c7 100644
--- a/web/panettone/OWNERS
+++ b/web/panettone/OWNERS
@@ -1,5 +1,3 @@
-inherited: true
-owners:
-  - grfn
-  - tazjin
-  - sterni
+aspen
+tazjin
+sterni
diff --git a/web/panettone/default.nix b/web/panettone/default.nix
index a01e0d81c8..60fca99e75 100644
--- a/web/panettone/default.nix
+++ b/web/panettone/default.nix
@@ -1,4 +1,4 @@
-{ depot, ... }:
+{ depot, pkgs, ... }:
 
 depot.nix.buildLisp.program {
   name = "panettone";
@@ -9,6 +9,7 @@ depot.nix.buildLisp.program {
     cl-ppcre
     cl-smtp
     cl-who
+    str
     defclass-std
     drakma
     easy-routes
@@ -16,7 +17,6 @@ depot.nix.buildLisp.program {
     lass
     local-time
     postmodern
-    trivial-ldap
 
     depot.lisp.klatre
   ];
@@ -24,6 +24,15 @@ depot.nix.buildLisp.program {
   srcs = [
     ./panettone.asd
     ./src/packages.lisp
+    (pkgs.writeText "build.lisp" ''
+      (defpackage build
+        (:use :cl :alexandria)
+        (:export :*migrations-dir* :*static-dir*))
+      (in-package :build)
+      (declaim (optimize (safety 3)))
+      (defvar *migrations-dir* "${./src/migrations}")
+      (defvar *static-dir* "${./src/static}")
+    '')
     ./src/util.lisp
     ./src/css.lisp
     ./src/email.lisp
@@ -43,6 +52,7 @@ depot.nix.buildLisp.program {
       ./test/package.lisp
       ./test/model_test.lisp
       ./test/inline-markdown_test.lisp
+      ./test/util_test.lisp
     ];
 
     expression = "(fiveam:run!)";
diff --git a/web/panettone/docker-compose.yml b/web/panettone/docker-compose.yml
index 84723667e6..18e3498306 100644
--- a/web/panettone/docker-compose.yml
+++ b/web/panettone/docker-compose.yml
@@ -1,7 +1,7 @@
-version: '3.4'
+version: "3.4"
 services:
   postgres:
-    image: postgres:11
+    image: postgres:16
     restart: always
     environment:
       POSTGRES_USER: panettone
diff --git a/web/panettone/shell.nix b/web/panettone/shell.nix
index aeafc7afcd..483481ca9a 100644
--- a/web/panettone/shell.nix
+++ b/web/panettone/shell.nix
@@ -1,6 +1,6 @@
-{ depot ? import ../.. {} }:
+{ depot ? import ../.. { } }:
 
-with depot.third_party;
+with depot.third_party.nixpkgs;
 
 mkShell {
   buildInputs = [
diff --git a/web/panettone/src/authentication.lisp b/web/panettone/src/authentication.lisp
index c335345020..496a0e0bd7 100644
--- a/web/panettone/src/authentication.lisp
+++ b/web/panettone/src/authentication.lisp
@@ -3,113 +3,121 @@
 (defvar *user* nil
   "The currently logged-in user")
 
-(defvar *ldap* nil
-  "The ldap connection")
-
-(defvar *ldap-host* "localhost"
-  "The host for the ldap connection")
-
-(defvar *ldap-port* 389
-  "The port for the ldap connection")
-
 (defclass/std user ()
   ((cn dn mail displayname :type string)))
 
-(defun connect-ldap (&key
-                       (host "localhost")
-                       (port 389))
-  (setq *ldap-host* host
-        *ldap-port* port
-        *ldap* (ldap:new-ldap :host host :port port)))
-
-(defun reconnect-ldap ()
-  (setq *ldap* (ldap:new-ldap
-                :host *ldap-host*
-                :port *ldap-port*)))
-
-(defmacro with-ldap ((&key (max-tries 1)) &body body)
-  "Execute BODY in a context where ldap connection errors trigger a reconnect
-and a retry"
-  (with-gensyms (n try retry e)
-    `(flet
-         ((,try
-              (,n)
-            (flet ((,retry (,e)
-                     (if (>= ,n ,max-tries)
-                         (error ,e)
-                         (progn
-                           (reconnect-ldap)
-                           (,try (1+ ,n))))))
-              (handler-case
-                  (progn
-                    ,@body)
-                (end-of-file (,e) (,retry ,e))
-                (trivial-ldap:ldap-connection-error (,e) (,retry ,e))))))
-       (,try 0))))
-
-(defun ldap-entry->user (entry)
-  (apply
-   #'make-instance
-   'user
-   :dn (ldap:dn entry)
-   (alexandria:mappend
-    (lambda (field)
-      (list field (car (ldap:attr-value entry field))))
-    (list :mail
-          :cn
-          :displayname))))
-
-(defun find-user/ldap (username)
-  (check-type username (simple-array character (*)))
-  (with-ldap ()
-    (ldap:search
-     *ldap*
-     `(and (= objectClass organizationalPerson)
-           (or
-            (= cn ,username)
-            (= dn ,username)))
-     ;; TODO(grfn): make this configurable
-     :base "ou=users,dc=tvl,dc=fyi")
-    (ldap:next-search-result *ldap*)))
-
-(defun find-user (username)
-  (check-type username (simple-array character (*)))
-  (when-let ((ldap-entry (find-user/ldap username)))
-    (ldap-entry->user ldap-entry)))
+;; Migrating user authentication to OAuth2 necessitates some temporary
+;; workarounds while other parts of the panettone code are being
+;; amended appropriately.
+
+(defun fake-dn (username)
+  "Users are no longer read directly from LDAP, but everything in
+panettone is keyed on the DNs. This function constructs matching
+'fake' DNs."
+  (format nil "cn=~A,ou=users,dc=tvl,dc=fyi" username))
 
 (defun find-user-by-dn (dn)
-  "Look up the user with the given DN in the LDAP database, returning an
-instance of `user'"
-  (with-ldap ()
-    (let ((have-results
-            (handler-case
-              (ldap:search *ldap* `(= objectClass organizationalPerson)
-                           :base dn
-                           :scope 'ldap:base)
-              ; catch ldap-errors generated by trivial-ldap:parse-ldap-message
-              ; since this is thrown on conditions which we don't want this
-              ; function to fail like when there are no search results
-              (trivial-ldap:ldap-error (e) nil))))
-      (when have-results
-        (when-let ((ldap-entry (ldap:next-search-result *ldap*)))
-          (ldap-entry->user ldap-entry))))))
+  "Previously this function looked up users in LDAP based on their DN,
+however panettone now does not have direct access to a user database.
+
+For most cases only the username is needed, which can be parsed out of
+the user, however email addresses are temporarily not available."
+  (let ((username
+          (car (uiop:split-string (subseq dn 3) :separator '(#\,)))))
+    (make-instance
+     'user
+     :dn dn
+     :cn username
+     :displayname username
+     :mail nil)))
+
+;; Implementation of standard OAuth2 authorisation flow.
+
+(defvar *oauth2-auth-endpoint* nil)
+(defvar *oauth2-token-endpoint* nil)
+(defvar *oauth2-client-id* nil)
+(defvar *oauth2-client-secret* nil)
+
+(defvar *oauth2-redirect-uri*
+  (or (uiop:getenv "OAUTH2_REDIRECT_URI")
+      "https://b.tvl.fyi/auth"))
 
 (comment
- (find-user-by-dn "cn=grfn,ou=users,dc=tvl,dc=fyi")
+ (setq *oauth2-redirect-uri* "http://localhost:6161/auth")
  )
 
-(defun authenticate-user (user-or-username password)
-  "Checks the given USER-OR-USERNAME has the given PASSWORD, by making a bind
-request against the ldap server at *ldap*. Returns the user if authentication is
-successful, `nil' otherwise"
-  (when-let ((user (if (typep user-or-username 'user) user-or-username
-                       (find-user user-or-username))))
-    (let ((dn (dn user)))
-      (let ((code-sym
-              (nth-value 1 (ldap:bind
-                            (ldap:new-ldap :host (ldap:host *ldap*)
-                                           :port (ldap:port *ldap*)
-                                           :user dn
-                                           :pass password)))))
-        (when (equalp code-sym 'trivial-ldap:success)
-          user)))))
+(defun initialise-oauth2 ()
+  "Initialise all settings needed for OAuth2"
+
+  (setq *oauth2-auth-endpoint*
+        (or *oauth2-auth-endpoint*
+            (uiop:getenv "OAUTH2_AUTH_ENDPOINT")
+            "https://auth.tvl.fyi/auth/realms/TVL/protocol/openid-connect/auth"))
+
+  (setq *oauth2-token-endpoint*
+        (or *oauth2-token-endpoint*
+            (uiop:getenv "OAUTH2_TOKEN_ENDPOINT")
+            "https://auth.tvl.fyi/auth/realms/TVL/protocol/openid-connect/token"))
+
+  (setq *oauth2-client-id*
+        (or *oauth2-client-id*
+            (uiop:getenv "OAUTH2_CLIENT_ID")
+            "panettone"))
+
+  (setq *oauth2-client-secret*
+        (or *oauth2-client-secret*
+            (uiop:getenv "OAUTH2_CLIENT_SECRET")
+            (error "OAUTH2_CLIENT_SECRET must be set!"))))
+
+(defun auth-url ()
+  (format nil "~A?response_type=code&client_id=~A&redirect_uri=~A"
+          *oauth2-auth-endpoint*
+          (drakma:url-encode *oauth2-client-id* :utf-8)
+          (drakma:url-encode *oauth2-redirect-uri* :utf-8)))
+
+(defun claims-to-user (claims)
+  (let ((username (cdr (assoc :preferred--username claims)))
+        (email (cdr (assoc :email claims))))
+    (make-instance
+     'user
+     :dn (fake-dn username)
+     :cn username
+     :mail email
+     ;; TODO(tazjin): Figure out actual displayName mapping in tokens.
+     :displayname username)))
+
+(defun fetch-token (code)
+  "Fetches the access token on completion of user authentication through
+the OAuth2 endpoint and returns the resulting user object."
+
+  (multiple-value-bind (body status)
+      (drakma:http-request *oauth2-token-endpoint*
+                           :method :post
+                           :parameters `(("grant_type" . "authorization_code")
+                                         ("client_id" . ,*oauth2-client-id*)
+                                         ("client_secret" . ,*oauth2-client-secret*)
+                                         ("redirect_uri" . ,*oauth2-redirect-uri*)
+                                         ("code" . ,code))
+                           :external-format-out :utf-8
+                           :want-stream t)
+    (if (/= status 200)
+        (error "Authentication failed: ~A (~A)~%"
+               (alexandria:read-stream-content-into-string body)
+               status)
+
+        ;; Returned JWT contains username and email, we can populate
+        ;; all fields from that.
+        (progn
+          (setf (flexi-streams:flexi-stream-external-format body) :utf-8)
+          (let* ((response (cl-json:decode-json body))
+                 (access-token (cdr (assoc :access--token response)))
+                 (payload (cadr (uiop:split-string access-token :separator '(#\.))))
+                 (claims (cl-json:decode-json-from-string
+                          (base64:base64-string-to-string
+                           ;; The JWT spec specifies that base64 strings
+                           ;; embedded in jwts are *not* padded, but the common
+                           ;; lisp base64 library doesn't know how to deal with
+                           ;; that - we need to add those extra padding
+                           ;; characters here.
+                           (panettone.util:add-missing-base64-padding payload)))))
+            (claims-to-user claims))))))
diff --git a/web/panettone/src/css.lisp b/web/panettone/src/css.lisp
index aa753cb50f..3bba2bb591 100644
--- a/web/panettone/src/css.lisp
+++ b/web/panettone/src/css.lisp
@@ -49,7 +49,24 @@
        :color "var(--primary)")))
 
     (.comment-count
-     :color "var(--gray)")))
+     :color "var(--gray)")
+
+    (.issue-links
+     :display "flex"
+     :flex-direction "row"
+     :align-items "center"
+     :justify-content "space-between"
+     :flex-wrap "wrap")
+
+    (.issue-search
+     ((:and input (:= type "search"))
+      :padding "0.5rem"
+      :background-image "url('static/search.png')"
+      :background-position "10px 10px"
+      :background-repeat "no-repeat"
+      :background-size "1rem"
+      :padding-left "2rem"
+      :border "1px" "solid" "var(--gray)"))))
 
 (defparameter issue-history-styles
   `((.issue-history
@@ -220,4 +237,15 @@
      :margin "0 auto")
 
     (.created-by-at
-     :color "var(--gray)")))
+     :color "var(--gray)")
+
+    ;; screen-reader-only content
+    (.sr-only
+     :border 0
+     :clip "rect(0 0 0 0)"
+     :height "1px"
+     :margin "-1px"
+     :overflow "hidden"
+     :padding 0
+     :position "absolute"
+     :width "1px")))
diff --git a/web/panettone/src/email.lisp b/web/panettone/src/email.lisp
index cb01c488a2..66ea299858 100644
--- a/web/panettone/src/email.lisp
+++ b/web/panettone/src/email.lisp
@@ -41,8 +41,9 @@ values of `*smtp-server*', `*smtp-server-port*' and `*email-notification-from*'"
   "Sends an email notification to the user with DN with the given SUBJECT and
   MESSAGE, iff that user has not disabled email notifications"
   (when (user-has-email-notifications-enabled-p dn)
-    (when-let ((user (find-user-by-dn dn)))
+    (when-let* ((user (find-user-by-dn dn))
+                (user-mail (mail user)))
       (send-email-notification
-       :to (mail user)
+       :to user-mail
        :subject subject
        :message message))))
diff --git a/web/panettone/src/inline-markdown.lisp b/web/panettone/src/inline-markdown.lisp
index fc5f77584f..e49293519b 100644
--- a/web/panettone/src/inline-markdown.lisp
+++ b/web/panettone/src/inline-markdown.lisp
@@ -124,7 +124,4 @@
          ; only eliminates the slight use case for nesting :em inside :del, but
          ; shouldn't be too bad. As a side effect this is the precise behavior
          ; we want for :code.
-         ;
-         ; TODO(sterni): maybe bring back the restart-based system which allowed
-         ;               to skip nested tokens if desired.
          (t (write-string (who:escape-string tok-str) target)))))
diff --git a/web/panettone/src/migrations/1-init-schema.lisp b/web/panettone/src/migrations/1-init-schema.lisp
new file mode 100644
index 0000000000..3be6c4fcc0
--- /dev/null
+++ b/web/panettone/src/migrations/1-init-schema.lisp
@@ -0,0 +1,23 @@
+"Initialize the database schema from before migrations were added"
+
+(defun ddl/create-issue-status ()
+  "Issue DDL to create the `issue-status' type, if it doesn't exist"
+  (unless (query (:select (:exists (:select 1
+                                    :from 'pg_type
+                                    :where (:= 'typname "issue_status"))))
+                 :single)
+    (query (sql-compile
+            `(:create-enum issue-status ,panettone.model:+issue-statuses+)))))
+
+(defun ddl/create-tables ()
+  "Issue DDL to create all tables, if they don't already exist."
+  (dolist (table '(panettone.model:issue
+                   panettone.model:issue-comment
+                   panettone.model:issue-event
+                   panettone.model:user-settings))
+    (unless (table-exists-p (dao-table-name table))
+      (create-table table))))
+
+(defun up ()
+  (ddl/create-issue-status)
+  (ddl/create-tables))
diff --git a/web/panettone/src/migrations/3920286378-add-issue-tsv.lisp b/web/panettone/src/migrations/3920286378-add-issue-tsv.lisp
new file mode 100644
index 0000000000..2a965a7bba
--- /dev/null
+++ b/web/panettone/src/migrations/3920286378-add-issue-tsv.lisp
@@ -0,0 +1,5 @@
+"Add tsvector for full-text search of issues"
+
+(defun up ()
+  (query "ALTER TABLE issues ADD COLUMN tsv tsvector GENERATED ALWAYS AS (to_tsvector('english', subject || ' ' || body)) STORED")
+  (query "CREATE INDEX issues_tsv_index ON issues USING GIN (tsv);"))
diff --git a/web/panettone/src/migrations/3921488651-create-users-table.lisp b/web/panettone/src/migrations/3921488651-create-users-table.lisp
new file mode 100644
index 0000000000..2598ab101e
--- /dev/null
+++ b/web/panettone/src/migrations/3921488651-create-users-table.lisp
@@ -0,0 +1,6 @@
+"Add a table to store information about users, load the initial set of users
+from the authentication provider, and change fks for other tables"
+
+(defun up ()
+  (panettone.model:create-table-if-not-exists
+   'panettone.model:user))
diff --git a/web/panettone/src/model.lisp b/web/panettone/src/model.lisp
index a3b75380c8..a106e9479b 100644
--- a/web/panettone/src/model.lisp
+++ b/web/panettone/src/model.lisp
@@ -1,33 +1,60 @@
 (in-package :panettone.model)
 (declaim (optimize (safety 3)))
 
-(defun connect-postgres (&key
-                           (host (or (uiop:getenvp "PGHOST") "localhost"))
-                           (user (or (uiop:getenvp "PGUSER") "panettone"))
-                           (password (or (uiop:getenvp "PGPASSWORD") "password"))
-                           (database (or (uiop:getenvp "PGDATABASE") "panettone"))
-                           (port (or (integer-env "PGPORT") 5432)))
-  "Initialize the global postgresql connection for Panettone"
-  (postmodern:connect-toplevel database user password host :port port))
-
-(defun make-thread
-    (function &rest args)
-  "Make a new thread as per `BORDEAUX-THREADS:MAKE-THREAD' but with its own, new
-database connection."
-  (let ((spec `(,(or (uiop:getenvp "PGDATABASE") "panettone")
-                ,(or (uiop:getenvp "PGUSER") "panettone")
-                ,(or (uiop:getenvp "PGPASSWORD") "password")
-                ,(or (uiop:getenvp "PGHOST") "localhost")
-                :port ,(or (integer-env "PGPORT") 5432))))
-    (apply #'bt:make-thread
-           (lambda ()
-             (postmodern:call-with-connection spec function))
-           args)))
+(setq pomo:*ignore-unknown-columns* t)
+
+(defvar *pg-spec* nil
+  "Connection spec for use with the with-connection macro. Needs to be
+initialised at launch time.")
+
+(defun make-pg-spec ()
+  "Construct the Postgres connection spec from the environment."
+  (list (or (uiop:getenvp "PGDATABASE") "panettone")
+        (or (uiop:getenvp "PGUSER") "panettone")
+        (or (uiop:getenvp "PGPASSWORD") "password")
+        (or (uiop:getenvp "PGHOST") "localhost")
+
+        :port (or (integer-env "PGPORT") 5432)
+        :application-name "panettone"
+        :pooled-p t))
+
+(defun prepare-db-connections ()
+  "Initialises the connection spec used for all Postgres connections."
+  (setq *pg-spec* (make-pg-spec)))
+
+(defun connect-to-db ()
+  "Connect using *PG-SPEC* at the top-level, for use during development"
+  (apply #'connect-toplevel
+         (loop for v in *pg-spec*
+               until (eq v :pooled-p)
+               collect v)))
+
+(defun pg-spec->url (&optional (spec *pg-spec*))
+  (destructuring-bind (db user password host &key port &allow-other-keys) spec
+    (format nil
+            "postgres://~A:~A@~A:~A/~A"
+            user password host port db)))
 
 ;;;
 ;;; Schema
 ;;;
 
+(defclass user ()
+  ((sub :col-type uuid :initarg :sub :accessor sub
+        :documentation
+        "ID for the user in the authentication provider. Taken from the `:SUB'
+        field in the JWT when the user first logged in")
+   (username :col-type string :initarg :username :accessor username)
+   (email :col-type string :initarg :email :accessor email))
+  (:metaclass dao-class)
+  (:keys sub)
+  (:table-name users)
+  (:documentation
+   "Panettone users. Uses an external authentication provider."))
+
+(deftable (user "users")
+  (!dao-def))
+
 (defclass user-settings ()
   ((user-dn :col-type string :initarg :user-dn :accessor user-dn)
    (enable-email-notifications
@@ -81,15 +108,6 @@ database connection."
   "Type specifier for the status of an `issue'"
   (cons 'member +issue-statuses+))
 
-(defun ddl/create-issue-status ()
-  "Issue DDL to create the `issue-status' type, if it doesn't exist"
-  (unless (query (:select (:exists (:select 1
-                                    :from 'pg_type
-                                    :where (:= 'typname "issue_status"))))
-                 :single)
-    (query (sql-compile
-            `(:create-enum issue-status ,+issue-statuses+)))))
-
 (defclass has-created-at ()
   ((created-at :col-type timestamp
                :col-default (local-time:now)
@@ -196,23 +214,171 @@ its new value will be formatted using ~A into NEW-VALUE"))
   (!dao-def)
   (!foreign 'issues 'issue-id 'id :on-delete :cascade :on-update :cascade))
 
-(define-constant +all-tables+
-    '(issue
-      issue-comment
-      issue-event
-      user-settings)
-  :test #'equal)
+(defclass migration ()
+  ((version
+    :col-type bigint
+    :primary-key t
+    :initarg :version
+    :accessor version)
+   (name :col-type string :initarg :name :accessor name)
+   (docstring :col-type string :initarg :docstring :accessor docstring)
+   (path :col-type string
+         :type pathname
+         :initarg :path
+         :accessor path
+         :col-export namestring
+         :col-import parse-namestring)
+   (package :type keyword :initarg :package :accessor migration-package))
+  (:metaclass dao-class)
+  (:keys version)
+  (:table-name migrations)
+  (:documentation "Migration scripts that have been run on the database"))
+(deftable migration (!dao-def))
 
-(defun ddl/create-tables ()
-  "Issue DDL to create all tables, if they don't already exist."
-  (dolist (table +all-tables+)
-    (unless (table-exists-p (dao-table-name table))
-      (create-table table))))
+;;;
+;;; Utils
+;;;
 
-(defun ddl/init ()
-  "Idempotently initialize the full database schema for Panettone"
-  (ddl/create-issue-status)
-  (ddl/create-tables))
+(defun create-table-if-not-exists (name)
+  " Takes the name of a dao-class and creates the table identified by symbol by
+executing all forms in its definition as found in the *tables* list, if it does
+not already exist."
+  (unless (table-exists-p (dao-table-name name))
+    (create-table name)))
+
+;;;
+;;; Migrations
+;;;
+
+(defun ensure-migrations-table ()
+  "Ensure the migrations table exists"
+  (unless (table-exists-p (dao-table-name 'migration))
+    (create-table 'migration)))
+
+(define-build-time-var *migrations-dir* "migrations/"
+    "The directory where migrations are stored")
+
+(defun load-migration-docstring (migration-path)
+  "If the first form in the file pointed to by `migration-pathname` is
+  a string, return it, otherwise return NIL."
+
+  (handler-case
+      (with-open-file (s migration-path)
+        (when-let ((form (read s)))
+          (when (stringp form) form)))
+    (t () nil)))
+
+(defun load-migration (path)
+  (let* ((parts (str:split #\- (pathname-name path) :limit 2))
+         (version (parse-integer (car parts)))
+         (name (cadr parts))
+         (docstring (load-migration-docstring path))
+         (package (intern (format nil "MIGRATION-~A" version)
+                          :keyword))
+         (migration (make-instance 'migration
+                                   :version version
+                                   :name name
+                                   :docstring docstring
+                                   :path path
+                                   :package package)))
+    (uiop/package:ensure-package package
+                                 :use '(#:common-lisp
+                                        #:postmodern
+                                        #:panettone.model))
+    (let ((*package* (find-package package)))
+      (load path))
+
+    migration))
+
+(defun run-migration (migration)
+  (declare (type migration migration))
+  (with-transaction ()
+    (format t "Running migration ~A (version ~A)"
+            (name migration)
+            (version migration))
+    (query
+     (sql-compile
+      `(:delete-from migrations
+        :where (= version ,(version migration)))))
+    (uiop:symbol-call (migration-package migration) :up)
+    (insert-dao migration)))
+
+(defun list-migration-files ()
+  (remove-if-not
+   (lambda (pn) (string= "lisp" (pathname-type pn)))
+   (uiop:directory-files (util:->dir *migrations-dir*))))
+
+(defun load-migrations ()
+  (mapcar #'load-migration (list-migration-files)))
+
+(defun generate-migration (name &key documentation)
+  "Generate a new database migration with the given NAME, optionally
+prepopulated with the given DOCUMENTATION.
+
+Returns the file that the migration is located at, as a `pathname'. Write Lisp
+code in this migration file to define a function called `up', which will be run
+in the context of a database transaction and should perform the migration."
+  (let* ((version (get-universal-time))
+         (filename (format nil "~A-~A.lisp"
+                           version
+                           name))
+         (pathname
+           (merge-pathnames filename *migrations-dir*)))
+    (with-open-file (stream pathname
+                            :direction :output
+                            :if-does-not-exist :create)
+      (when documentation
+        (format stream "~S~%~%" documentation))
+
+      (format stream "(defun up ()~%)"))
+    pathname))
+
+(defun migrations-already-run ()
+  "Query the database for a list of migrations that have already been run"
+  (query-dao 'migration (sql-compile '(:select * :from migrations))))
+
+(define-condition migration-name-mismatch ()
+  ((version :type integer :initarg :version)
+   (name-in-database :type string :initarg :name-in-database)
+   (name-in-code :type string :initarg :name-in-code))
+  (:report
+   (lambda (cond stream)
+     (format stream "Migration mismatch: Migration version ~A has name ~S in the database, but we have name ~S"
+             (slot-value cond 'version)
+             (slot-value cond 'name-in-database)
+             (slot-value cond 'name-in-code)))))
+
+(defun migrate ()
+  "Migrate the database, running all migrations that have not yet been run"
+  (ensure-migrations-table)
+  (format t "Running migrations from ~A...~%" *migrations-dir*)
+  (let* ((all-migrations (load-migrations))
+         (already-run (migrations-already-run))
+         (num-migrations-run 0))
+    (iter (for migration in all-migrations)
+      (if-let ((existing (find-if (lambda (existing)
+                                    (= (version existing)
+                                       (version migration)))
+                                  already-run)))
+        (progn
+          (unless (string= (name migration)
+                           (name existing))
+            (restart-case
+                (error 'migration-name-mismatch
+                       :version (version existing)
+                       :name-in-database (name existing)
+                       :name-in-code (name migration))
+              (skip ()
+                :report "Skip this migration"
+                (next-iteration))
+              (run-and-overwrite ()
+                :report "Run this migration anyway, overwriting the previous migration"
+                (run-migration migration))))
+          (next-iteration))
+        ;; otherwise, run the migration
+        (run-migration migration))
+      (incf num-migrations-run))
+    (format t "Ran ~A migration~:P~%" num-migrations-run)))
 
 ;;;
 ;;; Querying
@@ -247,28 +413,35 @@ type `ISSUE-NOT-FOUND'."
                       :where (:= 'id id))))
    :single))
 
-(defun list-issues (&key status (with '(:num-comments)))
+(defun list-issues (&key status search (with '(:num-comments)))
   "Return a list of all issues with the given STATUS (or all if nil), ordered by
   ID descending. If WITH contains `:NUM-COMMENTS' (the default) each issue will
   have the `num-comments' slot filled with the number of comments on that issue
   (to avoid N+1 queries)."
-  (let* ((condition (unless (null status)
-                      `(:where (:= status $1))))
+  (let* ((conditions
+           (and-where*
+            (unless (null status)
+              `(:= status $1))
+            (when (str:non-blank-string-p search)
+              `(:@@ tsv (:websearch-to-tsquery ,search)))))
          (select (if (find :num-comments with)
                      `(:select issues.* (:as (:count issue-comments.id)
                                              num-comments)
-                               :from issues
-                               :left-join issue-comments
-                               :on (:= issues.id issue-comments.issue-id)
-                               ,@condition
-                               :group-by issues.id)
-                     `(:select * :from issues ,@condition)))
+                       :from issues
+                       :left-join issue-comments
+                       :on (:= issues.id issue-comments.issue-id)
+                       :where ,conditions
+                       :group-by issues.id)
+                     `(:select * :from issues :where ,conditions)))
+         (order (if (str:non-blank-string-p search)
+                    `(:desc (:ts-rank-cd tsv (:websearch-to-tsquery ,search)))
+                    `(:desc id)))
          (query (sql-compile
-                 `(:order-by ,select (:desc id)))))
+                 `(:order-by ,select ,order))))
     (with-column-writers ('num_comments 'num-comments)
       (query-dao 'issue query status))))
 
-(defmethod num-comments ((issue-id integer))
+(defmethod count-comments ((issue-id integer))
   "Return the number of comments for the given ISSUE-ID."
   (query
    (:select (:count '*)
@@ -306,7 +479,6 @@ NOTE: This makes a database query, so be wary of N+1 queries"
      :where (:= 'issue-id issue-id))
     (:asc 'created-at))))
 
-
 ;;;
 ;;; Writing
 ;;;
@@ -414,13 +586,23 @@ explicitly subscribing to / unsubscribing from individual issues."
 
 
 (comment
- (connect-postgres)
- (ddl/init)
+
  (make-instance 'issue :subject "test")
- (create-issue :subject "test"
-               :author-dn "cn=grfn,ou=users,dc=tvl,dc=fyi")
+
+ (with-connection *pg-spec*
+   (create-issue :subject "test"
+                 :author-dn "cn=aspen,ou=users,dc=tvl,dc=fyi"))
 
  (issue-commenter-dns 1)
  (issue-subscribers 1)
 
+ ;; Creating new migrations
+ (setq *migrations-dir* (merge-pathnames "migrations/"))
+ (generate-migration "create-users-table"
+                     :documentation "Add a table to store information about users")
+ (load-migrations)
+
+ ;; Running migrations
+ (with-connection *pg-spec*
+   (migrate))
  )
diff --git a/web/panettone/src/packages.lisp b/web/panettone/src/packages.lisp
index b0833e4541..8e77c0ff2b 100644
--- a/web/panettone/src/packages.lisp
+++ b/web/panettone/src/packages.lisp
@@ -1,7 +1,10 @@
 (defpackage panettone.util
+  (:nicknames :util)
   (:use :cl :klatre)
   (:import-from :alexandria :when-let)
-  (:export :integer-env))
+  (:export
+   :integer-env :add-missing-base64-padding :and-where :and-where*
+   :define-build-time-var :->dir))
 
 (defpackage panettone.css
   (:use :cl :lass)
@@ -23,24 +26,34 @@
   (:import-from :defclass-std :defclass/std)
   (:import-from :alexandria :when-let :with-gensyms)
   (:export
-   :*user* :*ldap*
+   :*user*
+   :auth-url
+   :fetch-token
    :user :cn :dn :mail :displayname
-   :connect-ldap :find-user :find-user-by-dn :authenticate-user))
+   :find-user-by-dn
+   :initialise-oauth2))
 
 (defpackage panettone.model
   (:nicknames :model)
   (:use :cl :panettone.util :klatre :postmodern :iterate)
   (:import-from :alexandria :if-let :when-let :define-constant)
   (:export
-   :connect-postgres :ddl/init :make-thread
+   :prepare-db-connections
+   :migrate
+   :*pg-spec*
+
+   :create-table-if-not-exists
+
+   :user
+   :sub :username :email
 
    :user-settings
    :user-dn :enable-email-notifications-p :settings-for-user
    :update-user-settings :enable-email-notifications
 
-   :issue :issue-comment :issue-event
+   :issue :issue-comment :issue-event :migration
    :id :subject :body :author-dn :issue-id :status :created-at :acting-user-dn
-   :field :previous-value :new-value
+   :field :previous-value :new-value :+issue-statuses+
 
    :get-issue :issue-exists-p :list-issues :create-issue :set-issue-status
    :update-issue :delete-issue :issue-not-found :not-found-id
@@ -53,7 +66,7 @@
 (defpackage panettone.email
   (:nicknames :email)
   (:use :cl)
-  (:import-from :alexandria :when-let)
+  (:import-from :alexandria :when-let :when-let*)
   (:import-from :panettone.model
    :settings-for-user :enable-email-notifications-p)
   (:import-from :panettone.authentication
@@ -76,7 +89,7 @@
    :panettone.model
    :id :subject :body :author-dn :issue-id :status :created-at
    :field :previous-value :new-value :acting-user-dn
-   :issue-comments :num-comments :issue-events)
+   :*pg-spec*)
   (:import-from :panettone.irc :send-irc-notification)
   (:shadow :next)
-  (:export :start-pannetone :config :main))
+  (:export :start-panettone :config :main))
diff --git a/web/panettone/src/panettone.lisp b/web/panettone/src/panettone.lisp
index 4c9c7dafee..37d194d0f9 100644
--- a/web/panettone/src/panettone.lisp
+++ b/web/panettone/src/panettone.lisp
@@ -78,7 +78,7 @@
          (who:htm
           (:a :href
               (format nil
-                      "/login?original-uri=~A"
+                      "/auth?original-uri=~A"
                       (drakma:url-encode (hunchentoot:request-uri*)
                                          :utf-8))
               "Log In"))))))
@@ -135,36 +135,6 @@
     (when message
       (who:htm (:div :class "alert" (who:esc message))))))
 
-(defun render/login (&key message (original-uri "/"))
-  (render (:footer nil :header nil)
-    (:div
-     :class "login-form"
-     (:header
-      (:h1 "Login"))
-     (:main
-      :class "login-form"
-      (render/alert message)
-      (:form
-       :method :post :action "/login"
-       (:input :type "hidden" :name "original-uri"
-               :value (who:escape-string original-uri))
-       (:div
-        (:label :for "username"
-                "Username")
-        (:input :type "text"
-                :name "username"
-                :id "username"
-                :placeholder "username"))
-       (:div
-        (:label :for "password"
-                "Password")
-        (:input :type "password"
-                :name "password"
-                :id "password"
-                :placeholder "password"))
-       (:input :type "submit"
-               :value "Submit"))))))
-
 (defun render/settings ()
   (let ((settings (model:settings-for-user (dn *user*))))
     (render ()
@@ -215,7 +185,7 @@
                       (who:esc (format nil "#~A" issue-id)))
                " - "
                (created-by-at issue)
-               (let ((num-comments (length (issue-comments issue))))
+               (let ((num-comments (length (model:issue-comments issue))))
                  (unless (zerop num-comments)
                    (who:htm
                     (:span :class "comment-count"
@@ -223,7 +193,21 @@
                            (who:esc
                             (format nil "~A comment~:p" num-comments))))))))))))))
 
-(defun render/index (&key issues)
+(defun render/issue-search (&key search)
+  (who:with-html-output (*standard-output*)
+    (:form
+     :method "get"
+     :class "issue-search"
+     (:input :type "search"
+             :name "search"
+             :title "Issue search query"
+             :value search)
+     (:input
+      :type "submit"
+      :value "Search Issues"
+      :class "sr-only"))))
+
+(defun render/index (&key issues search)
   (render ()
     (:header
      (:h1 "Issues")
@@ -235,17 +219,19 @@
     (:main
      (:div
       :class "issue-links"
-      (:a :href "/issues/closed" "View closed issues"))
+      (:a :href "/issues/closed" "View closed issues")
+      (render/issue-search :search search))
      (render/issue-list :issues issues))))
 
-(defun render/closed-issues (&key issues)
+(defun render/closed-issues (&key issues search)
   (render ()
     (:header
      (:h1 "Closed issues"))
     (:main
      (:div
       :class "issue-links"
-      (:a :href "/" "View open isues"))
+      (:a :href "/" "View open isues")
+      (render/issue-search :search search))
      (render/issue-list :issues issues))))
 
 (defun render/issue-form (&optional issue message)
@@ -383,21 +369,22 @@
                      (:open "Close")
                      (:closed "Reopen"))))))
        (:p (who:str (render-markdown (body issue))))
-       (let* ((comments (issue-comments issue))
-              (events (issue-events issue))
+       (let* ((comments (model:issue-comments issue))
+              (events (model:issue-events issue))
               (history (merge 'list
                               comments
                               events
                               #'local-time:timestamp<
                               :key #'created-at)))
          (markdownify-comment-bodies comments)
-         (who:htm
-          (:ol
-           :class "issue-history"
-           (dolist (item history)
-             (render/issue-history-item item))
-           (when *user*
-             (render/new-comment (id issue))))))))))
+         (when (or history *user*)
+           (who:htm
+            (:ol
+             :class "issue-history"
+             (dolist (item history)
+               (render/issue-history-item item))
+             (when *user*
+               (render/new-comment (id issue)))))))))))
 
 (defun render/not-found (entity-type)
   (render ()
@@ -412,14 +399,15 @@
   "Send an email notification to all subscribers to the given issue with the
 given subject an body (in a thread, to avoid blocking)"
   (let ((current-user *user*))
-    (model:make-thread
+    (bordeaux-threads:make-thread
      (lambda ()
-       (dolist (user-dn (model:issue-subscribers issue-id))
-         (when (not (equal (dn current-user) user-dn))
-           (email:notify-user
-            user-dn
-            :subject subject
-            :message message)))))))
+       (pomo:with-connection *pg-spec*
+         (dolist (user-dn (model:issue-subscribers issue-id))
+           (when (not (equal (dn current-user) user-dn))
+             (email:notify-user
+              user-dn
+              :subject subject
+              :message message))))))))
 
 (defun link-to-issue (issue-id)
   (format nil "https://b.tvl.fyi/issues/~A" issue-id))
@@ -432,20 +420,22 @@ given subject an body (in a thread, to avoid blocking)"
   (if-let ((*user* (hunchentoot:session-value 'user)))
     (funcall next)
     (hunchentoot:redirect
-     (format nil "/login?original-uri=~A"
+     (format nil "/auth?original-uri=~A"
              (drakma:url-encode
               (hunchentoot:request-uri*)
               :utf-8)))))
 
-(defun @txn (next)
-  (pomo:with-transaction ()
-    (catch
-        ;; 'hunchentoot:handler-done is unexported, but is used by functions
-        ;; like hunchentoot:redirect to nonlocally abort the request handler -
-        ;; this doesn't mean an error occurred, so we need to catch it here to
-        ;; make the transaction still get committed
-        (intern "HANDLER-DONE" "HUNCHENTOOT")
-      (funcall next))))
+(defun @db (next)
+  "Decorator for handlers that use the database, wrapped in a transaction."
+  (pomo:with-connection *pg-spec*
+    (pomo:with-transaction ()
+      (catch
+          ;; 'hunchentoot:handler-done is unexported, but is used by functions
+          ;; like hunchentoot:redirect to nonlocally abort the request handler -
+          ;; this doesn't mean an error occurred, so we need to catch it here to
+          ;; make the transaction still get committed
+          (intern "HANDLER-DONE" "HUNCHENTOOT")
+        (funcall next)))))
 
 (defun @handle-issue-not-found (next)
   (handler-case (funcall next)
@@ -453,33 +443,31 @@ given subject an body (in a thread, to avoid blocking)"
       (render/not-found
        (format nil "Issue #~A" (model:not-found-id err))))))
 
-(defroute login-form ("/login" :method :get)
-    (original-uri)
-  (if (hunchentoot:session-value 'user)
-      (hunchentoot:redirect (or original-uri "/"))
-      (render/login :original-uri original-uri)))
+(defroute auth-handler ("/auth" :method :get :decorators (@auth-optional)) ()
+  (if-let ((code (hunchentoot:get-parameter "code")))
+    (let ((user (fetch-token code)))
+      (setf (hunchentoot:session-value 'user) user)
+      (hunchentoot:redirect (or (hunchentoot:session-value 'original-uri) "/")))
 
-(defroute submit-login ("/login" :method :post)
-    (&post original-uri username password)
-  (if-let ((user (authenticate-user username password)))
     (progn
-      (setf (hunchentoot:session-value 'user) user)
-      (hunchentoot:redirect (or original-uri "/")))
-    (render/login :message "Invalid credentials"
-                  :original-uri original-uri)))
+      (when-let ((original-uri (hunchentoot:get-parameter "original-uri")))
+        (setf (hunchentoot:session-value 'original-uri) original-uri))
+      (hunchentoot:redirect (authn:auth-url)))))
 
 (defroute logout ("/logout" :method :post) ()
   (hunchentoot:delete-session-value 'user)
   (hunchentoot:redirect "/"))
 
-(defroute index ("/" :decorators (@auth-optional)) ()
-  (let ((issues (model:list-issues :status :open)))
-    (render/index :issues issues)))
+(defroute index ("/" :decorators (@auth-optional @db)) (&get search)
+  (let ((issues (model:list-issues :status :open
+                                   :search search)))
+    (render/index :issues issues
+                  :search search)))
 
-(defroute settings ("/settings" :method :get :decorators (@auth)) ()
+(defroute settings ("/settings" :method :get :decorators (@auth @db)) ()
   (render/settings))
 
-(defroute save-settings ("/settings" :method :post :decorators (@auth))
+(defroute save-settings ("/settings" :method :post :decorators (@auth @db))
     (&post enable-email-notifications)
   (let ((settings (model:settings-for-user (dn *user*))))
     (model:update-user-settings
@@ -488,15 +476,18 @@ given subject an body (in a thread, to avoid blocking)"
     (render/settings)))
 
 (defroute handle-closed-issues
-    ("/issues/closed" :decorators (@auth-optional)) ()
-  (let ((issues (model:list-issues :status :closed)))
-    (render/closed-issues :issues issues)))
+    ("/issues/closed" :decorators (@auth-optional @db))
+    (&get search)
+  (let ((issues (model:list-issues :status :closed
+                                   :search search)))
+    (render/closed-issues :issues issues
+                          :search search)))
 
 (defroute new-issue ("/issues/new" :decorators (@auth)) ()
   (render/issue-form))
 
 (defroute handle-create-issue
-    ("/issues" :method :post :decorators (@auth @txn))
+    ("/issues" :method :post :decorators (@auth @db))
     (&post subject body)
   (if (string= subject "")
       (render/issue-form
@@ -515,10 +506,11 @@ given subject an body (in a thread, to avoid blocking)"
                  (id issue))
          :channel (or (uiop:getenvp "ISSUECHANNEL")
                       "#tvl"))
-        (hunchentoot:redirect "/"))))
+        (hunchentoot:redirect
+         (format nil "/issues/~A" (id issue))))))
 
 (defroute show-issue
-    ("/issues/:id" :decorators (@auth-optional @handle-issue-not-found))
+    ("/issues/:id" :decorators (@auth-optional @handle-issue-not-found @db))
     (&path (id 'integer))
   (let* ((issue (model:get-issue id))
          (*title* (format nil "~A | Panettone"
@@ -526,14 +518,14 @@ given subject an body (in a thread, to avoid blocking)"
     (render/issue issue)))
 
 (defroute edit-issue
-    ("/issues/:id/edit" :decorators (@auth @handle-issue-not-found))
+    ("/issues/:id/edit" :decorators (@auth @handle-issue-not-found @db))
     (&path (id 'integer))
   (let* ((issue (model:get-issue id))
          (*title* "Edit Issue | Panettone"))
     (render/issue-form issue)))
 
 (defroute update-issue
-    ("/issues/:id" :decorators (@auth @handle-issue-not-found @txn)
+    ("/issues/:id" :decorators (@auth @handle-issue-not-found @db)
                    ;; NOTE: this should be a put, but we're all HTML forms
                    ;; right now and those don't support PUT
                    :method :post)
@@ -551,7 +543,7 @@ given subject an body (in a thread, to avoid blocking)"
 
 (defroute handle-create-comment
     ("/issues/:id/comments"
-     :decorators (@auth @handle-issue-not-found @txn)
+     :decorators (@auth @handle-issue-not-found @db)
      :method :post)
     (&path (id 'integer) &post body)
   (flet ((redirect-to-issue ()
@@ -578,7 +570,7 @@ given subject an body (in a thread, to avoid blocking)"
        (redirect-to-issue)))))
 
 (defroute close-issue
-    ("/issues/:id/close" :decorators (@auth @handle-issue-not-found @txn)
+    ("/issues/:id/close" :decorators (@auth @handle-issue-not-found @db)
                          :method :post)
     (&path (id 'integer))
   (model:set-issue-status id :closed)
@@ -602,7 +594,7 @@ given subject an body (in a thread, to avoid blocking)"
   (hunchentoot:redirect (format nil "/issues/~A" id)))
 
 (defroute open-issue
-    ("/issues/:id/open" :decorators (@auth)
+    ("/issues/:id/open" :decorators (@auth @db)
                         :method :post)
     (&path (id 'integer))
   (model:set-issue-status id :open)
@@ -634,17 +626,15 @@ given subject an body (in a thread, to avoid blocking)"
 
 (defun migrate-db ()
   "Migrate the database to the latest version of the schema"
-  (model:ddl/init))
+  (pomo:with-connection *pg-spec*
+    (model:migrate)))
 
-(defun start-panettone (&key port
-                          (ldap-host "localhost")
-                          (ldap-port 389)
-                          postgres-params
-                          session-secret)
-  (connect-ldap :host ldap-host
-                :port ldap-port)
+(define-build-time-var *static-dir* "static/"
+    "Directory to serve static files from")
 
-  (apply #'model:connect-postgres postgres-params)
+(defun start-panettone (&key port session-secret)
+  (authn:initialise-oauth2)
+  (model:prepare-db-connections)
   (migrate-db)
 
   (when session-secret
@@ -653,12 +643,18 @@ given subject an body (in a thread, to avoid blocking)"
   (setq hunchentoot:*session-max-time* (* 60 60 24 90))
 
   (setq *acceptor*
-        (make-instance 'easy-routes:routes-acceptor :port port))
+        (make-instance 'easy-routes:easy-routes-acceptor :port port))
+
+  (push
+   (hunchentoot:create-folder-dispatcher-and-handler
+    "/static/"
+    (util:->dir *static-dir*))
+   hunchentoot:*dispatch-table*)
+
   (hunchentoot:start *acceptor*))
 
 (defun main ()
   (let ((port (integer-env "PANETTONE_PORT" :default 6161))
-        (ldap-port (integer-env "LDAP_PORT" :default 389))
         (cheddar-url (uiop:getenvp "CHEDDAR_URL"))
         (session-secret (uiop:getenvp "SESSION_SECRET")))
     (when cheddar-url (setq *cheddar-url* cheddar-url))
@@ -666,9 +662,10 @@ given subject an body (in a thread, to avoid blocking)"
     (setq hunchentoot:*log-lisp-backtraces-p* nil)
 
     (start-panettone :port port
-                     :ldap-port ldap-port
                      :session-secret session-secret)
 
+    (format t "launched panettone on port ~A~%" port)
+
     (sb-thread:join-thread
      (find-if (lambda (th)
                 (string= (sb-thread:thread-name th)
@@ -677,9 +674,8 @@ given subject an body (in a thread, to avoid blocking)"
 
 (comment
  (setq hunchentoot:*catch-errors-p* nil)
- ;; to setup an ssh tunnel to ldap+cheddar+irccat for development:
- ;; ssh -NL 3899:localhost:389 -L 4238:localhost:4238 -L 4722:localhost:4722 whitby.tvl.fyi
+ ;; to setup an ssh tunnel to cheddar+irccat for development:
+ ;; ssh -N -L 4238:localhost:4238 -L 4722:localhost:4722 whitby.tvl.fyi
  (start-panettone :port 6161
-                  :ldap-port 3899
                   :session-secret "session-secret")
  )
diff --git a/web/panettone/src/static/search.png b/web/panettone/src/static/search.png
new file mode 100644
index 0000000000..0fd78c6651
--- /dev/null
+++ b/web/panettone/src/static/search.png
Binary files differdiff --git a/web/panettone/src/util.lisp b/web/panettone/src/util.lisp
index 9fd9ceaa79..4c3c4f1aa6 100644
--- a/web/panettone/src/util.lisp
+++ b/web/panettone/src/util.lisp
@@ -5,3 +5,35 @@
    (when-let ((str (uiop:getenvp var)))
      (try-parse-integer str))
    default))
+
+(defun add-missing-base64-padding (s)
+  "Add any missing padding characters to the (un-padded) base64 string `S', such
+that it can be successfully decoded by the `BASE64' package"
+  ;; I apologize
+  (let* ((needed-padding (mod (length s) 4))
+         (pad-chars (if (zerop needed-padding) 0 (- 4 needed-padding))))
+    (format nil "~A~v@{~A~:*~}" s pad-chars "=")))
+
+(defun and-where (clauses)
+  "Combine all non-nil clauses in CLAUSES into a single S-SQL WHERE form"
+  (let ((clauses (remove nil clauses)))
+    (if (null clauses) t
+        (reduce (lambda (x y) `(:and ,x ,y)) clauses))))
+
+(defun and-where* (&rest clauses)
+  "Combine all non-nil clauses in CLAUSES into a single S-SQL WHERE form"
+  (and-where clauses))
+
+(defmacro define-build-time-var
+    (name value-if-not-in-build &optional (doc nil))
+  `(defvar ,name
+     (or (when-let ((package (find-package :build)))
+           (let ((sym (find-symbol ,(symbol-name name) package)))
+             (when (boundp sym) (symbol-value sym))))
+         ,value-if-not-in-build)
+     ,doc))
+
+(defun ->dir (dir)
+  (if (char-equal (uiop:last-char dir) #\/)
+      dir
+      (concatenate 'string dir "/")))
diff --git a/web/panettone/test/util_test.lisp b/web/panettone/test/util_test.lisp
new file mode 100644
index 0000000000..ff52d916cb
--- /dev/null
+++ b/web/panettone/test/util_test.lisp
@@ -0,0 +1,9 @@
+(in-package :panettone.tests)
+(declaim (optimize (safety 3)))
+
+(test add-missing-base64-padding-test
+  (is (string=
+       "abcdef"
+       (base64:base64-string-to-string
+        (panettone.util:add-missing-base64-padding
+         "YWJjZGVm")))))
diff --git a/web/pwcrypt/.gitignore b/web/pwcrypt/.gitignore
new file mode 100644
index 0000000000..73b9c106db
--- /dev/null
+++ b/web/pwcrypt/.gitignore
@@ -0,0 +1,2 @@
+target/
+dist/
diff --git a/web/pwcrypt/Cargo.lock b/web/pwcrypt/Cargo.lock
new file mode 100644
index 0000000000..7d33ab4bf1
--- /dev/null
+++ b/web/pwcrypt/Cargo.lock
@@ -0,0 +1,999 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anymap2"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
+
+[[package]]
+name = "argon2"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95c2fcf79ad1932ac6269a738109997a83c227c09b75842ae564dc8ede6a861c"
+dependencies = [
+ "base64ct",
+ "blake2",
+ "password-hash",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "blake2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "boolinator"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
+
+[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-io"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a4bef6b277b3ab073253d4bca60761240cf8d6998f4bd142211957b69a61b20"
+dependencies = [
+ "gloo-console",
+ "gloo-dialogs",
+ "gloo-events",
+ "gloo-file",
+ "gloo-history",
+ "gloo-net",
+ "gloo-render",
+ "gloo-storage",
+ "gloo-timers",
+ "gloo-utils",
+ "gloo-worker",
+]
+
+[[package]]
+name = "gloo-console"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-dialogs"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-events"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-file"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7"
+dependencies = [
+ "gloo-events",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-history"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd451019e0b7a2b8a7a7b23e74916601abf1135c54664e57ff71dcc26dfcdeb7"
+dependencies = [
+ "gloo-events",
+ "gloo-utils",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_urlencoded",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-net"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-sink",
+ "gloo-utils",
+ "js-sys",
+ "pin-project",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-render"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-storage"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-worker"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a"
+dependencies = [
+ "anymap2",
+ "bincode",
+ "gloo-console",
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "implicit-clone"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7"
+dependencies = [
+ "indexmap",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
+name = "js-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.146"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
+
+[[package]]
+name = "log"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "num_cpus"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "password-hash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
+dependencies = [
+ "base64ct",
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "pin-project"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pinned"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b"
+dependencies = [
+ "futures",
+ "rustversion",
+ "thiserror",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+dependencies = [
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prokio"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488"
+dependencies = [
+ "futures",
+ "gloo",
+ "num_cpus",
+ "once_cell",
+ "pin-project",
+ "pinned",
+ "tokio",
+ "tokio-stream",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "pwcrypt"
+version = "0.1.0"
+dependencies = [
+ "argon2",
+ "getrandom",
+ "gloo",
+ "rand_core",
+ "wasm-bindgen",
+ "web-sys",
+ "yew",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
+
+[[package]]
+name = "ryu"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "serde"
+version = "1.0.164"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.164"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "tokio"
+version = "1.28.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2"
+dependencies = [
+ "autocfg",
+ "pin-project-lite",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "typenum"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
+
+[[package]]
+name = "web-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "yew"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc"
+dependencies = [
+ "console_error_panic_hook",
+ "futures",
+ "gloo",
+ "implicit-clone",
+ "indexmap",
+ "js-sys",
+ "prokio",
+ "rustversion",
+ "serde",
+ "slab",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "yew-macro",
+]
+
+[[package]]
+name = "yew-macro"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b64c253c1d401f1ea868ca9988db63958cfa15a69f739101f338d6f05eea8301"
+dependencies = [
+ "boolinator",
+ "once_cell",
+ "prettyplease",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
diff --git a/web/pwcrypt/Cargo.toml b/web/pwcrypt/Cargo.toml
new file mode 100644
index 0000000000..6c0a6e5b6d
--- /dev/null
+++ b/web/pwcrypt/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "pwcrypt"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+argon2 = "0.5.0"
+getrandom = { version = "0.2.10", features = ["js"] }
+gloo = "0.8.0"
+rand_core = { version = "0.6.4", features = ["getrandom"] }
+wasm-bindgen = "= 0.2.91"
+web-sys = "0.3"
+yew = { version = "0.20.0", features = [ "csr" ]}
diff --git a/web/pwcrypt/default.nix b/web/pwcrypt/default.nix
new file mode 100644
index 0000000000..c0b2974f9a
--- /dev/null
+++ b/web/pwcrypt/default.nix
@@ -0,0 +1,51 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  wasmRust = pkgs.rust-bin.stable.latest.default.override {
+    targets = [ "wasm32-unknown-unknown" ];
+  };
+
+  cargoToml = with builtins; fromTOML (readFile ./Cargo.toml);
+
+  wasmBindgenMatch =
+    cargoToml.dependencies.wasm-bindgen == "= ${pkgs.wasm-bindgen-cli.version}";
+
+  assertWasmBindgen = assert (lib.assertMsg wasmBindgenMatch ''
+    Due to instability in the Rust WASM ecosystem, the trunk build
+    tool enforces that the Cargo-dependency version of `wasm-bindgen`
+    MUST match the version of the CLI supplied in the environment.
+
+    This can get out of sync when nixpkgs is updated. To resolve it,
+    wasm-bindgen must be bumped in the Cargo.toml file and cargo needs
+    to be run to resolve the dependencies.
+
+    Versions of `wasm-bindgen` in Cargo.toml:
+
+      Expected: '= ${pkgs.wasm-bindgen-cli.version}'
+      Actual:   '${cargoToml.dependencies.wasm-bindgen}'
+  ''); pkgs.wasm-bindgen-cli;
+
+  deps = [
+    pkgs.binaryen
+    pkgs.sass
+    pkgs.trunk
+
+    wasmRust
+    assertWasmBindgen
+  ];
+in
+pkgs.rustPlatform.buildRustPackage rec {
+  pname = "pwcrypt";
+  version = "canon";
+  src = lib.cleanSource ./.;
+  cargoLock.lockFile = ./Cargo.lock;
+
+  buildPhase = ''
+    export PATH=${lib.makeBinPath deps}:$PATH
+    mkdir home
+    export HOME=$PWD/home
+    trunk build --release -d $out
+  '';
+
+  dontInstall = true;
+}
diff --git a/web/pwcrypt/index.html b/web/pwcrypt/index.html
new file mode 100644
index 0000000000..cacf984d88
--- /dev/null
+++ b/web/pwcrypt/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet"
+          href="https://static.tvl.su/latest/terminal.min.css" />
+    <title>//web/pwcrypt</title>
+  </head>
+
+  <body>
+    <noscript>
+      <h1>//web/pwcrypt</h1>
+      <p>
+        This application hashes passwords in your browser, and
+        requires JavaScript (or more specifically, WebAssembly) to
+        run.
+      </p>
+      <p>
+        All of the involved code is
+        available <a href="https://at.tvl.fyi/?q=%2F%2Fweb%2Fpwcrypt">in
+        the depot</a> and is licensed under free software licenses.
+      </p>
+    </noscript>
+  </body>
+</html>
diff --git a/web/pwcrypt/src/main.html b/web/pwcrypt/src/main.html
new file mode 100644
index 0000000000..b19cbd379b
--- /dev/null
+++ b/web/pwcrypt/src/main.html
@@ -0,0 +1,48 @@
+html!{
+<div class="container">
+  <h1>{"//web/pwcrypt"}</h1>
+  <p>{"You can use this page to create your hashed credentials for a TVL account. Enter your desired username and password below, and send us the output you receive in order for us to create your account."}</p>
+  <p>
+    {"Detailed documentation about the registration process is "}
+    <a href="https://code.tvl.fyi/about/docs/REVIEWS.md#registration">
+      {"available here"}
+    </a>
+    {"."}
+  </p>
+  <p>{"All of this happens in your browser: Your password does not leave this site!"}</p>
+
+  <form>
+    <fieldset>
+      <legend>{"Credentials:"}</legend>
+
+      <div class="form-group">
+        <label for="username">{"Username:"}</label>
+        <input id="username" name="username" type="text"
+               oninput={link.callback(|event| input_to_message(event, Msg::SetUsername))} />
+      </div>
+
+      <div class="form-group">
+        <label for="email">{"Email:"}</label>
+        <input id="email" name="email" type="email"
+               oninput={link.callback(|event| input_to_message(event, Msg::SetEmail))} />
+      </div>
+
+      <div class="form-group">
+        <label for="password">{"Password:"}</label>
+        <input id="password" name="password" type="password"
+               oninput={link.callback(|event| input_to_message(event, Msg::SetPassword))} />
+      </div>
+
+      if let Some(missing) = self.whats_missing() {
+        <p>{"Please fill in "}{missing}{"."}</p>
+      } else {
+        <div class="form-group">
+          <button class="btn btn-default" type="button"
+                  onclick={link.callback(|_| Msg::UpdateCredentials)}>{"Prepare credentials"}</button>
+        </div>
+      }
+    </fieldset>
+  </form>
+  {self.display_credentials()}
+</div>
+}
diff --git a/web/pwcrypt/src/main.rs b/web/pwcrypt/src/main.rs
new file mode 100644
index 0000000000..2b9b82abb0
--- /dev/null
+++ b/web/pwcrypt/src/main.rs
@@ -0,0 +1,160 @@
+use argon2::password_hash::{PasswordHasher, SaltString};
+use argon2::Argon2;
+use gloo::console::log;
+use rand_core::OsRng;
+use web_sys::HtmlInputElement;
+use yew::prelude::*;
+
+fn hash_password(pw: &str) -> String {
+    let salt = SaltString::generate(&mut OsRng);
+    let argon2 = Argon2::default();
+    argon2
+        .hash_password(pw.as_bytes(), &salt)
+        .expect("failed to hash pw")
+        .to_string()
+}
+
+enum Msg {
+    NoOp,
+    SetEmail(String),
+    SetPassword(String),
+    SetUsername(String),
+    UpdateCredentials,
+}
+
+#[derive(Default)]
+struct App {
+    email: Option<String>,
+    password: Option<String>,
+    username: Option<String>,
+    hashed: Option<String>,
+}
+
+impl App {
+    fn whats_missing(&self) -> Option<String> {
+        let mut missing = vec![];
+
+        if self.username.is_none() {
+            missing.push("username");
+        }
+
+        if self.email.is_none() {
+            missing.push("email");
+        }
+
+        if self.password.is_none() {
+            missing.push("password");
+        }
+
+        match missing.len() {
+            0 => None,
+            1 => Some(missing[0].to_string()),
+            2 => Some(format!("{} and {}", missing[0], missing[1])),
+            3 => Some(format!("{}, {} and {}", missing[0], missing[1], missing[2])),
+            _ => unreachable!(),
+        }
+    }
+
+    fn update_credentials(&mut self) {
+        if self.password.is_none() {
+            log!("error: password unset, but credentials requested");
+            return;
+        }
+
+        let pw = self.password.as_ref().unwrap();
+        let hashed = hash_password(pw);
+        log!("hashed password to", &hashed);
+        self.hashed = Some(hashed);
+    }
+
+    fn display_credentials(&self) -> Html {
+        if let (Some(username), Some(email), Some(hash)) =
+            (&self.username, &self.email, &self.hashed)
+        {
+            html! {
+              <>
+                <hr />
+                <p>{"Your credentials are as follows: "}</p>
+                <pre>
+                  {"  {\n"}
+                  {"    username = \""}{username}{"\";\n"}
+                  {"    email = \""}{email}{"\";\n"}
+                  {"    password = \"{ARGON2}"}{hash}{"\";\n"}
+                  {"  }"}
+                </pre>
+                <p>
+                  {"Please propose a CL to "}
+                  <a href="https://at.tvl.fyi/?q=//ops/users/default.nix">
+                    <code>{"//ops/users/default.nix"}</code>
+                  </a>
+                  {", or submit your patch via email to "}
+                  <a href="mailto:depot@tvl.su">{"depot@tvl.su"}</a>
+                  {"."}
+                </p>
+              </>
+            }
+        } else {
+            html! {}
+        }
+    }
+}
+
+fn input_to_message(event: InputEvent, msg: fn(String) -> Msg) -> Msg {
+    let input = event.target_unchecked_into::<HtmlInputElement>();
+    if input.check_validity() {
+        msg(input.value())
+    } else {
+        Msg::NoOp
+    }
+}
+
+fn set_if_present(s: String, target: &mut Option<String>) {
+    if s.is_empty() {
+        *target = None;
+    } else {
+        *target = Some(s);
+    }
+}
+
+impl Component for App {
+    type Message = Msg;
+    type Properties = ();
+
+    fn create(_: &Context<Self>) -> Self {
+        Self::default()
+    }
+
+    fn update(&mut self, _: &Context<Self>, msg: Self::Message) -> bool {
+        log!(
+            "handling message ",
+            match msg {
+                Msg::NoOp => "NoOp",
+                Msg::SetEmail(_) => "SetEmail",
+                Msg::SetUsername(_) => "SetUsername",
+                Msg::SetPassword(_) => "SetPassword",
+                Msg::UpdateCredentials => "UpdateCredentials",
+            }
+        );
+
+        match msg {
+            Msg::NoOp => return false,
+            Msg::SetEmail(email) => set_if_present(email, &mut self.email),
+            Msg::SetUsername(username) => set_if_present(username, &mut self.username),
+            Msg::SetPassword(password) => set_if_present(password, &mut self.password),
+            Msg::UpdateCredentials => {
+                self.update_credentials();
+            }
+        }
+
+        true
+    }
+
+    fn view(&self, ctx: &Context<Self>) -> Html {
+        let link = ctx.link();
+        include!("main.html")
+    }
+}
+
+fn main() {
+    yew::Renderer::<App>::new().render();
+}
diff --git a/web/static/default.nix b/web/static/default.nix
index 2120e649f0..9eaeb0ec14 100644
--- a/web/static/default.nix
+++ b/web/static/default.nix
@@ -5,7 +5,9 @@
 let
   storeDirLength = with builtins; (stringLength storeDir) + 1;
   logo = depot.web.tvl.logo;
-in lib.fix(self: pkgs.runCommand "tvl-static" {
+in
+lib.fix (self: pkgs.runCommand "tvl-static"
+{
   passthru = {
     # Preserving the string context here makes little sense: While we are
     # referencing this derivation, we are not doing so via the nix store,
diff --git a/web/static/terminal.min.css b/web/static/terminal.min.css
new file mode 100644
index 0000000000..8289785823
--- /dev/null
+++ b/web/static/terminal.min.css
@@ -0,0 +1 @@
+/* SPDX-License-Identifier: MIT; https://terminalcss.xyz/ */ :root{--global-font-size:15px;--global-line-height:1.4em;--global-space:10px;--font-stack:Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;--mono-font-stack:Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;--background-color:#fff;--page-width:60em;--font-color:#151515;--invert-font-color:#fff;--primary-color:#1a95e0;--secondary-color:#727578;--error-color:#d20962;--progress-bar-background:#727578;--progress-bar-fill:#151515;--code-bg-color:#e8eff2;--input-style:solid;--display-h1-decoration:none}*{box-sizing:border-box;text-rendering:geometricPrecision}::-moz-selection{background:var(--primary-color);color:var(--invert-font-color)}::selection{background:var(--primary-color);color:var(--invert-font-color)}body{font-size:var(--global-font-size);color:var(--font-color);line-height:var(--global-line-height);margin:0;font-family:var(--font-stack);word-wrap:break-word;background-color:var(--background-color)}.logo,h1,h2,h3,h4,h5,h6{line-height:var(--global-line-height)}a{cursor:pointer;color:var(--primary-color);text-decoration:none}a:hover{background-color:var(--primary-color);color:var(--invert-font-color)}em{font-size:var(--global-font-size);font-style:italic;font-family:var(--font-stack);color:var(--font-color)}blockquote,code,em,strong{line-height:var(--global-line-height)}.logo,blockquote,code,footer,h1,h2,h3,h4,h5,h6,header,li,ol,p,section,ul{float:none;margin:0;padding:0}.logo,blockquote,h1,ol,p,ul{margin-top:calc(var(--global-space) * 2);margin-bottom:calc(var(--global-space) * 2)}.logo,h1{position:relative;display:inline-block;display:table-cell;padding:calc(var(--global-space) * 2) 0 calc(var(--global-space) * 2);margin:0;overflow:hidden;font-weight:600}h1::after{content:"====================================================================================================";position:absolute;bottom:5px;left:0;display:var(--display-h1-decoration)}.logo+*,h1+*{margin-top:0}h2,h3,h4,h5,h6{position:relative;margin-bottom:var(--global-line-height);font-weight:600}blockquote{position:relative;padding-left:calc(var(--global-space) * 2);padding-left:2ch;overflow:hidden}blockquote::after{content:">\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>";white-space:pre;position:absolute;top:0;left:0;line-height:var(--global-line-height);color:#9ca2ab}code{font-weight:inherit;background-color:var(--code-bg-color);font-family:var(--mono-font-stack)}code::after,code::before{content:"`";display:inline}pre code::after,pre code::before{content:""}pre{display:block;word-break:break-all;word-wrap:break-word;color:var(--secondary-color);background-color:var(--background-color);border:1px solid var(--secondary-color);padding:var(--global-space);white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap}pre code{overflow-x:scroll;padding:0;margin:0;display:inline-block;min-width:100%;font-family:var(--mono-font-stack)}.terminal .logo,.terminal blockquote,.terminal code,.terminal h1,.terminal h2,.terminal h3,.terminal h4,.terminal h5,.terminal h6,.terminal strong{font-size:var(--global-font-size);font-style:normal;font-family:var(--font-stack);color:var(--font-color)}.terminal-prompt{position:relative;white-space:nowrap}.terminal-prompt::before{content:"> "}.terminal-prompt::after{content:"";-webkit-animation:cursor .8s infinite;animation:cursor .8s infinite;background:var(--primary-color);border-radius:0;display:inline-block;height:1em;margin-left:.2em;width:3px;bottom:-2px;position:relative}@-webkit-keyframes cursor{0%{opacity:0}50%{opacity:1}to{opacity:0}}@keyframes cursor{0%{opacity:0}50%{opacity:1}to{opacity:0}}li,li>ul>li{position:relative;display:block;padding-left:calc(var(--global-space) * 2)}nav>ul>li{padding-left:0}li::after{position:absolute;top:0;left:0}ul>li::after{content:"-"}nav ul>li::after{content:""}ol li::before{content:counters(item, ".") ". ";counter-increment:item}ol ol li::before{content:counters(item, ".") " ";counter-increment:item}.terminal-menu li::after,.terminal-menu li::before{display:none}ol{counter-reset:item}ol li:nth-child(n+10)::after{left:-7px}ol ol{margin-top:0;margin-bottom:0}.terminal-menu{width:100%}.terminal-nav{display:flex;flex-direction:column;align-items:flex-start}ul ul{margin-top:0;margin-bottom:0}.terminal-menu ul{list-style-type:none;padding:0!important;display:flex;flex-direction:column;width:100%;flex-grow:1;font-size:var(--global-font-size);margin-top:0}.terminal-menu li{display:flex;margin:0 0 .5em 0;padding:0}ol.terminal-toc li{border-bottom:1px dotted var(--secondary-color);padding:0;margin-bottom:15px}.terminal-menu li:last-child{margin-bottom:0}ol.terminal-toc li a{margin:4px 4px 4px 0;background:var(--background-color);position:relative;top:6px;text-align:left;padding-right:4px}.terminal-menu li a:not(.btn){text-decoration:none;display:block;width:100%;border:none;color:var(--secondary-color)}.terminal-menu li a.active{color:var(--font-color)}.terminal-menu li a:hover{background:0 0;color:inherit}ol.terminal-toc li::before{content:counters(item, ".") ". ";counter-increment:item;position:absolute;right:0;background:var(--background-color);padding:4px 0 4px 4px;bottom:-8px}ol.terminal-toc li a:hover{background:var(--primary-color);color:var(--invert-font-color)}hr{position:relative;overflow:hidden;margin:calc(var(--global-space) * 4) 0;border:0;border-bottom:1px dashed var(--secondary-color)}p{margin:0 0 var(--global-line-height);color:var(--global-font-color)}.container{max-width:var(--page-width)}.container,.container-fluid{margin:0 auto;padding:0 calc(var(--global-space) * 2)}img{width:100%}.progress-bar{height:8px;background-color:var(--progress-bar-background);margin:12px 0}.progress-bar.progress-bar-show-percent{margin-top:38px}.progress-bar-filled{background-color:var(--progress-bar-fill);height:100%;transition:width .3s ease;position:relative;width:0}.progress-bar-filled::before{content:"";border:6px solid transparent;border-top-color:var(--progress-bar-fill);position:absolute;top:-12px;right:-6px}.progress-bar-filled::after{color:var(--progress-bar-fill);content:attr(data-filled);display:block;font-size:12px;white-space:nowrap;position:absolute;border:6px solid transparent;top:-38px;right:0;transform:translateX(50%)}.progress-bar-no-arrow>.progress-bar-filled::after,.progress-bar-no-arrow>.progress-bar-filled::before{content:"";display:none;visibility:hidden;opacity:0}table{width:100%;border-collapse:collapse;margin:var(--global-line-height) 0;color:var(--font-color);font-size:var(--global-font-size)}table td,table th{vertical-align:top;border:1px solid var(--font-color);line-height:var(--global-line-height);padding:calc(var(--global-space)/ 2);font-size:1em}table thead th{font-size:1em}table tfoot tr th{font-weight:500}table caption{font-size:1em;margin:0 0 1em 0}table tbody td:first-child{font-weight:700;color:var(--secondary-color)}.form{width:100%}fieldset{border:1px solid var(--font-color);padding:1em}label{font-size:1em;color:var(--font-color)}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text]{border:1px var(--input-style) var(--font-color);width:100%;padding:.7em .5em;font-size:1em;font-family:var(--font-stack);-webkit-appearance:none;border-radius:0}input[type=email]:active,input[type=email]:focus,input[type=number]:active,input[type=number]:focus,input[type=password]:active,input[type=password]:focus,input[type=search]:active,input[type=search]:focus,input[type=text]:active,input[type=text]:focus{outline:0;-webkit-appearance:none;border:1px solid var(--font-color)}input[type=email]:not(:placeholder-shown):invalid,input[type=number]:not(:placeholder-shown):invalid,input[type=password]:not(:placeholder-shown):invalid,input[type=search]:not(:placeholder-shown):invalid,input[type=text]:not(:placeholder-shown):invalid{border-color:var(--error-color)}input,textarea{color:var(--font-color);background-color:var(--background-color)}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::-moz-placeholder,textarea::-moz-placeholder{color:var(--secondary-color)!important;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::placeholder,textarea::placeholder{color:var(--secondary-color)!important;opacity:1}textarea{height:auto;width:100%;resize:none;border:1px var(--input-style) var(--font-color);padding:.5em;font-size:1em;font-family:var(--font-stack);-webkit-appearance:none;border-radius:0}textarea:focus{outline:0;-webkit-appearance:none;border:1px solid var(--font-color)}textarea:not(:placeholder-shown):invalid{border-color:var(--error-color)}input:-webkit-autofill,input:-webkit-autofill:focus textarea:-webkit-autofill,input:-webkit-autofill:hover,select:-webkit-autofill,select:-webkit-autofill:focus,select:-webkit-autofill:hover,textarea:-webkit-autofill:hover textarea:-webkit-autofill:focus{border:1px solid var(--font-color);-webkit-text-fill-color:var(--font-color);box-shadow:0 0 0 1000px var(--invert-font-color) inset;-webkit-box-shadow:0 0 0 1000px var(--invert-font-color) inset;transition:background-color 5000s ease-in-out 0s}.form-group{margin-bottom:var(--global-line-height);overflow:auto}.btn{border-style:solid;border-width:1px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;outline:0;padding:.65em 2em;font-size:1em;font-family:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative;z-index:1}.btn:active{box-shadow:none}.btn.btn-ghost{border-color:var(--font-color);color:var(--font-color);background-color:transparent}.btn.btn-ghost:focus,.btn.btn-ghost:hover{border-color:var(--tertiary-color);color:var(--tertiary-color);z-index:2}.btn.btn-ghost:hover{background-color:transparent}.btn-block{width:100%;display:flex}.btn-default{background-color:var(--font-color);border-color:var(--invert-font-color);color:var(--invert-font-color)}.btn-default:focus:not(.btn-ghost),.btn-default:hover{background-color:var(--secondary-color);color:var(--invert-font-color)}.btn-default.btn-ghost:focus,.btn-default.btn-ghost:hover{border-color:var(--secondary-color);color:var(--secondary-color);z-index:2}.btn-error{color:var(--invert-font-color);background-color:var(--error-color);border:1px solid var(--error-color)}.btn-error:focus:not(.btn-ghost),.btn-error:hover{background-color:var(--error-color);border-color:var(--error-color)}.btn-error.btn-ghost{border-color:var(--error-color);color:var(--error-color)}.btn-error.btn-ghost:focus,.btn-error.btn-ghost:hover{border-color:var(--error-color);color:var(--error-color);z-index:2}.btn-primary{color:var(--invert-font-color);background-color:var(--primary-color);border:1px solid var(--primary-color)}.btn-primary:focus:not(.btn-ghost),.btn-primary:hover{background-color:var(--primary-color);border-color:var(--primary-color)}.btn-primary.btn-ghost{border-color:var(--primary-color);color:var(--primary-color)}.btn-primary.btn-ghost:focus,.btn-primary.btn-ghost:hover{border-color:var(--primary-color);color:var(--primary-color);z-index:2}.btn-small{padding:.5em 1.3em!important;font-size:.9em!important}.btn-group{overflow:auto}.btn-group .btn{float:left}.btn-group .btn-ghost:not(:first-child){margin-left:-1px}.terminal-card{border:1px solid var(--secondary-color)}.terminal-card>header{color:var(--invert-font-color);text-align:center;background-color:var(--secondary-color);padding:.5em 0}.terminal-card>div:first-of-type{padding:var(--global-space)}.terminal-timeline{position:relative;padding-left:70px}.terminal-timeline::before{content:' ';background:var(--secondary-color);display:inline-block;position:absolute;left:35px;width:2px;height:100%;z-index:400}.terminal-timeline .terminal-card{margin-bottom:25px}.terminal-timeline .terminal-card::before{content:' ';background:var(--invert-font-color);border:2px solid var(--secondary-color);display:inline-block;position:absolute;margin-top:25px;left:26px;width:15px;height:15px;z-index:400}.terminal-alert{color:var(--font-color);padding:1em;border:1px solid var(--font-color);margin-bottom:var(--global-space)}.terminal-alert-error{color:var(--error-color);border-color:var(--error-color)}.terminal-alert-primary{color:var(--primary-color);border-color:var(--primary-color)}@media screen and (max-width:960px){label{display:block;width:100%}pre::-webkit-scrollbar{height:3px}}@media screen and (max-width:480px){form{width:100%}}@media only screen and (min-width:30em){.terminal-nav{flex-direction:row;align-items:center}.terminal-menu ul{flex-direction:row;justify-items:flex-end;align-items:center;justify-content:flex-end;margin-top:calc(var(--global-space) * 2)}.terminal-menu li{margin:0;margin-right:2em}.terminal-menu li:last-child{margin-right:0}}.terminal-media:not(:last-child){margin-bottom:1.25rem}.terminal-media-left{padding-right:var(--global-space)}.terminal-media-left,.terminal-media-right{display:table-cell;vertical-align:top}.terminal-media-right{padding-left:var(--global-space)}.terminal-media-body{display:table-cell;vertical-align:top}.terminal-media-heading{font-size:1em;font-weight:700}.terminal-media-content{margin-top:.3rem}.terminal-placeholder{background-color:var(--secondary-color);text-align:center;color:var(--font-color);font-size:1rem;border:1px solid var(--secondary-color)}figure>img{padding:0}.terminal-avatarholder{width:calc(var(--global-space) * 5);height:calc(var(--global-space) * 5)}.terminal-avatarholder img{padding:0}figure{margin:0}figure>figcaption{color:var(--secondary-color);text-align:center}.hljs{display:block;overflow-x:auto;padding:.5em;background:var(--block-background-color);color:var(--font-color)}.hljs-comment,.hljs-quote{color:var(--secondary-color)}.hljs-variable{color:var(--font-color)}.hljs-built_in,.hljs-keyword,.hljs-name,.hljs-selector-tag,.hljs-tag{color:var(--primary-color)}.hljs-addition,.hljs-attribute,.hljs-literal,.hljs-section,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type{color:var(--secondary-color)}.hljs-string{color:var(--secondary-color)}.hljs-deletion,.hljs-meta,.hljs-selector-attr,.hljs-selector-pseudo{color:var(--primary-color)}.hljs-doctag{color:var(--secondary-color)}.hljs-attr{color:var(--primary-color)}.hljs-bullet,.hljs-link,.hljs-symbol{color:var(--primary-color)}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
diff --git a/web/static/tvl.css b/web/static/tvl.css
index aea4d426ea..b28736bce1 100644
--- a/web/static/tvl.css
+++ b/web/static/tvl.css
@@ -35,70 +35,19 @@ body {
     font-family: jetbrains-mono, monospace;
 }
 
-p, a :not(.uncoloured-link) {
-    color: inherit;
-}
-
 h1, h2, h3 {
     line-height: 1.2
 }
 
-/* Homepage styling */
-
-.dark {
-    background-color: #181818;
-    color: #e4e4ef;
-}
-
-.dark-link, .interblag-title {
-    color: #96a6c8;
-}
-
-.entry-container {
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    justify-content: flex-start;
-}
-
-.interblag-title {
-    text-decoration: none;
-}
-
-.entry {
-    width: 42%;
-    margin: 5px;
-    padding-left: 7px;
-    padding-right: 5px;
-    border: 2px solid;
-    border-radius: 5px;
-    flex-grow: 1;
-    text-decoration: none;
-}
-
-.misc {
-    color: #73c936;
-    border-color: #73c936;
-}
+/* Blog Posts */
 
-.blog {
-    color: #268bd2;
-    border-color: #268bd2;
+article {
+    line-height: 1.5em;
 }
 
-.project {
-    color: #ff4f58;
-    border-color: #ff4f58;
-}
-
-.entry-title {
-    color: inherit !important;
-    font-weight: bold;
-    text-decoration: none;
-}
-
-.entry-date {
-    font-style: italic;
+/* spacing between the paragraphs in blog posts */
+article p {
+    margin: 1.4em auto;
 }
 
 /* Blog styling */
@@ -139,6 +88,10 @@ pre {
     overflow: auto;
 }
 
+code {
+    background: aliceblue;
+}
+
 img {
     max-width: 100%;
 }
diff --git a/web/todolist/default.nix b/web/todolist/default.nix
index a2fcb501bd..4712ad21ba 100644
--- a/web/todolist/default.nix
+++ b/web/todolist/default.nix
@@ -8,7 +8,7 @@ let
   inherit (pkgs)
     jq
     ripgrep
-    runCommandNoCC
+    runCommand
     writeTextFile
     ;
 
@@ -39,7 +39,7 @@ let
     user = string;
   };
 
-  allTodos = fromJSON (readFile (runCommandNoCC "depot-todos.json" {} ''
+  allTodos = fromJSON (readFile (runCommand "depot-todos.json" { } ''
     ${ripgrep}/bin/rg --json 'TODO\(\w+\):.*$' ${depot.path} | \
       ${jq}/bin/jq -s -f ${./extract-todos.jq} > $out
   ''));
@@ -58,22 +58,23 @@ let
   '');
 
   userParagraph = todos:
-  let user = (head todos).user;
-  in ''
-    <p>
-      <h3>
-        <a style="color:inherit; text-decoration: none;"
-           name="${user}"
-           href="#${user}">${user}</a>
-      </h3>
-      ${concatStringsSep "\n" (map todoElement todos)}
-    </p>
-    <hr>
-  '';
+    let user = (head todos).user;
+    in ''
+      <p>
+        <h3>
+          <a style="color:inherit; text-decoration: none;"
+             name="${user}"
+             href="#${user}">${user}</a>
+        </h3>
+        ${concatStringsSep "\n" (map todoElement todos)}
+      </p>
+      <hr>
+    '';
 
   staticUrl = "https://static.tvl.fyi/${depot.web.static.drvHash}";
 
-in writeTextFile {
+in
+writeTextFile {
   name = "tvl-todos";
   destination = "/index.html";
   text = ''
diff --git a/web/tvixbolt/.gitignore b/web/tvixbolt/.gitignore
new file mode 100644
index 0000000000..77fdd3e5cf
--- /dev/null
+++ b/web/tvixbolt/.gitignore
@@ -0,0 +1,2 @@
+/target
+dist
diff --git a/web/tvixbolt/Cargo.lock b/web/tvixbolt/Cargo.lock
new file mode 100644
index 0000000000..d3c5faf10c
--- /dev/null
+++ b/web/tvixbolt/Cargo.lock
@@ -0,0 +1,1199 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "bitmaps"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "boolinator"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
+
+[[package]]
+name = "bstr"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
+dependencies = [
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "codemap"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24"
+
+[[package]]
+name = "codemap-diagnostic"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc20770be05b566a963bf91505e60412c4a2d016d1ef95c5512823bb085a8122"
+dependencies = [
+ "codemap",
+ "termcolor",
+]
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "countme"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dirs"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "genawaiter"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0"
+dependencies = [
+ "genawaiter-macro",
+]
+
+[[package]]
+name = "genawaiter-macro"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc"
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gloo"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23947965eee55e3e97a5cd142dd4c10631cc349b48cecca0ed230fd296f568cd"
+dependencies = [
+ "gloo-console",
+ "gloo-dialogs",
+ "gloo-events",
+ "gloo-file",
+ "gloo-render",
+ "gloo-storage",
+ "gloo-timers",
+ "gloo-utils",
+]
+
+[[package]]
+name = "gloo-console"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-dialogs"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-events"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-file"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7"
+dependencies = [
+ "futures-channel",
+ "gloo-events",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-render"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-storage"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "imbl"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978d142c8028edf52095703af2fad11d6f611af1246685725d6b850634647085"
+dependencies = [
+ "bitmaps",
+ "imbl-sized-chunks",
+ "rand_core",
+ "rand_xoshiro",
+ "serde",
+ "version_check",
+]
+
+[[package]]
+name = "imbl-sized-chunks"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6957ea0b2541c5ca561d3ef4538044af79f8a05a1eb3a3b148936aaceaa1076"
+dependencies = [
+ "bitmaps",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
+[[package]]
+name = "js-sys"
+version = "0.3.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lexical-core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46"
+dependencies = [
+ "lexical-parse-float",
+ "lexical-parse-integer",
+ "lexical-util",
+ "lexical-write-float",
+ "lexical-write-integer",
+]
+
+[[package]]
+name = "lexical-parse-float"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f"
+dependencies = [
+ "lexical-parse-integer",
+ "lexical-util",
+ "static_assertions",
+]
+
+[[package]]
+name = "lexical-parse-integer"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9"
+dependencies = [
+ "lexical-util",
+ "static_assertions",
+]
+
+[[package]]
+name = "lexical-util"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc"
+dependencies = [
+ "static_assertions",
+]
+
+[[package]]
+name = "lexical-write-float"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862"
+dependencies = [
+ "lexical-util",
+ "lexical-write-integer",
+ "static_assertions",
+]
+
+[[package]]
+name = "lexical-write-integer"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446"
+dependencies = [
+ "lexical-util",
+ "static_assertions",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
+
+[[package]]
+name = "libredox"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
+dependencies = [
+ "bitflags 2.4.1",
+ "libc",
+ "redox_syscall",
+]
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "md-5"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
+dependencies = [
+ "cfg-if",
+ "digest",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "nom8"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "path-clean"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+
+[[package]]
+name = "rand_xoshiro"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "rnix"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb35cedbeb70e0ccabef2a31bcff0aebd114f19566086300b8f42c725fc2cb5f"
+dependencies = [
+ "rowan",
+]
+
+[[package]]
+name = "route-recognizer"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
+
+[[package]]
+name = "rowan"
+version = "0.15.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
+dependencies = [
+ "countme",
+ "hashbrown 0.14.3",
+ "memoffset",
+ "rustc-hash",
+ "text-size",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "ryu"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
+
+[[package]]
+name = "scoped-tls-hkt"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ddc765d3410d9f6c6ca071bf0b67f6b01e3ec4595dc3892f02677e75819dddc"
+
+[[package]]
+name = "serde"
+version = "1.0.195"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "618365e8e586c22123d692b72a7d791d5ee697817b65a218cdf12a98870af0f7"
+dependencies = [
+ "fnv",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.195"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.111"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smol_str"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tabwriter"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a327282c4f64f6dc37e3bba4c2b6842cc3a992f204fa58d917696a89f691e5f6"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "text-size"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
+
+[[package]]
+name = "thiserror"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "toml"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b"
+dependencies = [
+ "indexmap",
+ "nom8",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+]
+
+[[package]]
+name = "tvix-eval"
+version = "0.1.0"
+dependencies = [
+ "bstr",
+ "bytes",
+ "codemap",
+ "codemap-diagnostic",
+ "data-encoding",
+ "dirs",
+ "genawaiter",
+ "imbl",
+ "itertools",
+ "lazy_static",
+ "lexical-core",
+ "md-5",
+ "os_str_bytes",
+ "path-clean",
+ "regex",
+ "rnix",
+ "rowan",
+ "serde",
+ "serde_json",
+ "sha1",
+ "sha2",
+ "smol_str",
+ "tabwriter",
+ "toml",
+ "tvix-eval-builtin-macros",
+ "xml-rs",
+]
+
+[[package]]
+name = "tvix-eval-builtin-macros"
+version = "0.0.1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "tvixbolt"
+version = "0.1.0"
+dependencies = [
+ "codemap",
+ "rnix",
+ "serde",
+ "serde_urlencoded",
+ "tvix-eval",
+ "wasm-bindgen",
+ "web-sys",
+ "yew",
+ "yew-router",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
+
+[[package]]
+name = "web-sys"
+version = "0.3.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
+
+[[package]]
+name = "yew"
+version = "0.19.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a1ccb53e57d3f7d847338cf5758befa811cabe207df07f543c06f502f9998cd"
+dependencies = [
+ "console_error_panic_hook",
+ "gloo",
+ "gloo-utils",
+ "indexmap",
+ "js-sys",
+ "scoped-tls-hkt",
+ "slab",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "yew-macro",
+]
+
+[[package]]
+name = "yew-macro"
+version = "0.19.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fab79082b556d768d6e21811869c761893f0450e1d550a67892b9bce303b7bb"
+dependencies = [
+ "boolinator",
+ "lazy_static",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "yew-router"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "155804f6f3aa309f596d5c3fa14486a94e7756f1edd7634569949e401d5099f2"
+dependencies = [
+ "gloo",
+ "gloo-utils",
+ "js-sys",
+ "route-recognizer",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_urlencoded",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "yew",
+ "yew-router-macro",
+]
+
+[[package]]
+name = "yew-router-macro"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39049d193b52eaad4ffc80916bf08806d142c90b5edcebd527644de438a7e19a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
diff --git a/web/tvixbolt/Cargo.toml b/web/tvixbolt/Cargo.toml
new file mode 100644
index 0000000000..ce5ffb90e3
--- /dev/null
+++ b/web/tvixbolt/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "tvixbolt"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+yew = "0.19.3"
+yew-router = "0.16"
+codemap = "0.1.3"
+serde_urlencoded = "*" # pinned by yew
+rnix = "0.11.0"
+
+# needs to be in sync with nixpkgs
+wasm-bindgen = "= 0.2.91"
+
+[dependencies.tvix-eval]
+path = "../../tvix/eval"
+default-features = false
+
+[dependencies.serde]
+version = "*" # pinned by yew
+features = [ "derive" ]
+
+[dependencies.web-sys]
+version = "*" # pinned by yew
+features = [ "HtmlDetailsElement" ]
diff --git a/web/tvixbolt/LICENSE b/web/tvixbolt/LICENSE
new file mode 100644
index 0000000000..be3f7b28e5
--- /dev/null
+++ b/web/tvixbolt/LICENSE
@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<https://www.gnu.org/licenses/>.
diff --git a/web/tvixbolt/default.nix b/web/tvixbolt/default.nix
new file mode 100644
index 0000000000..9b6baa582f
--- /dev/null
+++ b/web/tvixbolt/default.nix
@@ -0,0 +1,74 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  wasmRust = pkgs.rust-bin.stable.latest.default.override {
+    targets = [ "wasm32-unknown-unknown" ];
+  };
+
+  cargoToml = with builtins; fromTOML (readFile ./Cargo.toml);
+
+  wasmBindgenMatch =
+    cargoToml.dependencies.wasm-bindgen == "= ${pkgs.wasm-bindgen-cli.version}";
+
+  assertWasmBindgen = assert (lib.assertMsg wasmBindgenMatch ''
+    Due to instability in the Rust WASM ecosystem, the trunk build
+    tool enforces that the Cargo-dependency version of `wasm-bindgen`
+    MUST match the version of the CLI supplied in the environment.
+
+    This can get out of sync when nixpkgs is updated. To resolve it,
+    wasm-bindgen must be bumped in the Cargo.toml file and cargo needs
+    to be run to resolve the dependencies.
+
+    Versions of `wasm-bindgen` in Cargo.toml:
+
+      Expected: '= ${pkgs.wasm-bindgen-cli.version}'
+      Actual:   '${cargoToml.dependencies.wasm-bindgen}'
+  ''); pkgs.wasm-bindgen-cli;
+
+  deps = [
+    pkgs.binaryen
+    pkgs.sass
+    pkgs.trunk
+
+    wasmRust
+    assertWasmBindgen
+  ];
+
+  # Cargo.toml needs to be patched with the /nix/store source path of
+  # tvix-eval.
+  cargoTomlPatch = pkgs.writeText "tvix-eval-src.patch" ''
+    diff --git a/Cargo.toml b/Cargo.toml
+    index 75006bec18..6ca244bbb2 100644
+    --- a/Cargo.toml
+    +++ b/Cargo.toml
+    @@ -16,7 +16,7 @@ rnix = "0.11.0"
+     wasm-bindgen = "= 0.2.83"
+
+     [dependencies.tvix-eval]
+    -path = "../../tvix/eval"
+    +path = "${depot.tvix.crates.workspaceMembers.tvix-eval.build.src}"
+     default-features = false
+
+     [dependencies.serde]
+  '';
+in
+pkgs.rustPlatform.buildRustPackage rec {
+  pname = "tvixbolt";
+  version = "canon";
+  src = lib.cleanSource ./.;
+
+  cargoLock.lockFile = ./Cargo.lock;
+
+  patches = [
+    cargoTomlPatch
+  ];
+
+  buildPhase = ''
+    export PATH=${lib.makeBinPath deps}:$PATH
+    mkdir home
+    export HOME=$PWD/home
+    trunk build --release -d $out
+  '';
+
+  dontInstall = true;
+}
diff --git a/web/tvixbolt/index.css b/web/tvixbolt/index.css
new file mode 100644
index 0000000000..95bd7d0983
--- /dev/null
+++ b/web/tvixbolt/index.css
@@ -0,0 +1,7 @@
+.footer {
+    text-align: right;
+}
+
+.lod {
+    text-align: center;
+}
diff --git a/web/tvixbolt/index.html b/web/tvixbolt/index.html
new file mode 100644
index 0000000000..410eb4eae2
--- /dev/null
+++ b/web/tvixbolt/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet"
+          href="https://static.tvl.su/latest/terminal.min.css" />
+    <link data-trunk rel="inline" href="index.css">
+    <title>Tvixbolt</title>
+  </head>
+</html>
diff --git a/web/tvixbolt/src/main.rs b/web/tvixbolt/src/main.rs
new file mode 100644
index 0000000000..2e68e03fb0
--- /dev/null
+++ b/web/tvixbolt/src/main.rs
@@ -0,0 +1,315 @@
+// tvixbolt - an online tool for exploring Tvix language evaluation
+//
+// Copyright (C) The TVL Community
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+use std::fmt::Write;
+
+use serde::{Deserialize, Serialize};
+use tvix_eval::observer::{DisassemblingObserver, TracingObserver};
+use web_sys::HtmlDetailsElement;
+use web_sys::HtmlTextAreaElement;
+use yew::prelude::*;
+use yew::TargetCast;
+use yew_router::{prelude::*, AnyRoute};
+
+#[derive(Clone)]
+enum Msg {
+    CodeChange(String),
+    ToggleTrace(bool),
+    ToggleDisplayAst(bool),
+
+    // Required because browsers are stupid and it's easy to get into
+    // infinite loops with `ontoggle` events.
+    NoOp,
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+struct Model {
+    code: String,
+
+    // #[serde(skip_serializing)]
+    trace: bool,
+
+    // #[serde(skip_serializing)]
+    display_ast: bool,
+}
+
+fn tvixbolt_overview() -> Html {
+    html! {
+        <>
+          <p>
+            {"This page lets you explore the bytecode generated by the "}
+            <a href="https://tvix.dev">{"Tvix"}</a>
+            {" compiler for the Nix language."}
+          </p>
+          <p>
+            {"Tvix is still "}<i>{"work-in-progress"}</i>{" and we would appreciate "}
+            {"if you told us about bugs you find."}
+          </p>
+          <p>
+            {"Tvixbolt is a project by "}
+            <a href="https://tvl.fyi">
+              {"TVL"}
+            </a>
+            {"."}
+          </p>
+        </>
+    }
+}
+
+fn footer_link(location: &'static str, name: &str) -> Html {
+    html! {
+        <>
+            <a class="uncoloured-link" href={location}>{name}</a>{" | "}
+        </>
+    }
+}
+
+fn footer() -> Html {
+    html! {
+        <>
+        <hr/>
+        <footer>
+          <p class="footer">
+            {footer_link("https://tvl.fyi", "home")}
+            {footer_link("https://cs.tvl.fyi", "code")}
+            {footer_link("https://tvl.fyi/builds", "ci")}
+            {footer_link("https://b.tvl.fyi", "bugs")}
+            {"Β© TVL"}
+          </p>
+          <p class="lod">{"ΰ² _ΰ² "}</p>
+        </footer>
+        </>
+    }
+}
+
+impl Component for Model {
+    type Message = Msg;
+    type Properties = ();
+
+    fn create(_: &Context<Self>) -> Self {
+        BrowserHistory::new()
+            .location()
+            .query::<Self>()
+            .unwrap_or_else(|_| Self {
+                code: String::new(),
+                trace: false,
+                display_ast: false,
+            })
+    }
+
+    fn update(&mut self, _: &Context<Self>, msg: Self::Message) -> bool {
+        match msg {
+            Msg::ToggleTrace(trace) => {
+                self.trace = trace;
+            }
+
+            Msg::ToggleDisplayAst(display_ast) => {
+                self.display_ast = display_ast;
+            }
+
+            Msg::CodeChange(new_code) => {
+                self.code = new_code;
+            }
+
+            Msg::NoOp => {}
+        }
+
+        let _ = BrowserHistory::new().replace_with_query(AnyRoute::new("/"), self.clone());
+
+        true
+    }
+
+    fn view(&self, ctx: &Context<Self>) -> Html {
+        // This gives us a component's "`Scope`" which allows us to send messages, etc to the component.
+        let link = ctx.link();
+        html! {
+            <>
+            <div class="container">
+                <h1>{"tvixbolt"}</h1>
+                {tvixbolt_overview()}
+                <form>
+                  <fieldset>
+                    <legend>{"Input"}</legend>
+
+                    <div class="form-group">
+                        <label for="code">{"Nix code:"}</label>
+                        <textarea
+                         oninput={link.callback(|e: InputEvent| {
+                             let ta = e.target_unchecked_into::<HtmlTextAreaElement>().value();
+                             Msg::CodeChange(ta)
+
+                         })}
+                         id="code" cols="30" rows="10" value={self.code.clone()}>
+                         </textarea>
+                    </div>
+                  </fieldset>
+                </form>
+                <hr />
+                {self.run(ctx)}
+                {footer()}
+            </div>
+            </>
+        }
+    }
+}
+
+impl Model {
+    fn run(&self, ctx: &Context<Self>) -> Html {
+        if self.code.is_empty() {
+            return html! {
+                <p>
+                  {"Enter some Nix code above to get started. Don't know Nix yet? "}
+                  {"Check out "}
+                  <a href="https://code.tvl.fyi/about/nix/nix-1p/README.md">{"nix-1p"}</a>
+                  {"!"}
+                </p>
+            };
+        }
+
+        html! {
+            <>
+              <h2>{"Result:"}</h2>
+            {eval(self).display(ctx, self)}
+            </>
+        }
+    }
+}
+
+#[derive(Default)]
+struct Output {
+    errors: String,
+    warnings: String,
+    output: String,
+    bytecode: Vec<u8>,
+    trace: Vec<u8>,
+    ast: String,
+}
+
+fn maybe_show(title: &str, s: &str) -> Html {
+    if s.is_empty() {
+        html! {}
+    } else {
+        html! {
+            <>
+              <h3>{title}</h3>
+              <pre>{s}</pre>
+            </>
+        }
+    }
+}
+
+fn maybe_details(
+    ctx: &Context<Model>,
+    title: &str,
+    s: &str,
+    display: bool,
+    toggle: fn(bool) -> Msg,
+) -> Html {
+    let link = ctx.link();
+    if display {
+        let msg = toggle(false);
+        html! {
+            <details open=true
+                     ontoggle={link.callback(move |e: Event| {
+                         let details = e.target_unchecked_into::<HtmlDetailsElement>();
+                         if !details.open() {
+                             msg.clone()
+                         } else {
+                             Msg::NoOp
+                         }
+                     })}>
+
+              <summary><h3 style="display: inline;">{title}</h3></summary>
+              <pre>{s}</pre>
+            </details>
+        }
+    } else {
+        let msg = toggle(true);
+        html! {
+            <details ontoggle={link.callback(move |e: Event| {
+                         let details = e.target_unchecked_into::<HtmlDetailsElement>();
+                         if details.open() {
+                             msg.clone()
+                         } else {
+                             Msg::NoOp
+                         }
+                     })}>
+              <summary><h3 style="display: inline;">{title}</h3></summary>
+            </details>
+        }
+    }
+}
+
+impl Output {
+    fn display(self, ctx: &Context<Model>, model: &Model) -> Html {
+        html! {
+            <>
+            {maybe_show("Errors:", &self.errors)}
+            {maybe_show("Warnings:", &self.warnings)}
+            {maybe_show("Output:", &self.output)}
+            {maybe_show("Bytecode:", &String::from_utf8_lossy(&self.bytecode))}
+            {maybe_details(ctx, "Runtime trace:", &String::from_utf8_lossy(&self.trace), model.trace, Msg::ToggleTrace)}
+            {maybe_details(ctx, "Parsed AST:", &self.ast, model.display_ast, Msg::ToggleDisplayAst)}
+            </>
+        }
+    }
+}
+
+fn eval(model: &Model) -> Output {
+    let mut out = Output::default();
+
+    if model.code.is_empty() {
+        return out;
+    }
+
+    let mut eval = tvix_eval::Evaluation::new_pure();
+    let source = eval.source_map();
+
+    let result = {
+        let mut compiler_observer = DisassemblingObserver::new(source.clone(), &mut out.bytecode);
+        eval.compiler_observer = Some(&mut compiler_observer);
+
+        let mut runtime_observer = TracingObserver::new(&mut out.trace);
+        if model.trace {
+            eval.runtime_observer = Some(&mut runtime_observer);
+        }
+
+        eval.evaluate(&model.code, Some("/nixbolt".into()))
+    };
+
+    if model.display_ast {
+        if let Some(ref expr) = result.expr {
+            out.ast = tvix_eval::pretty_print_expr(expr);
+        }
+    }
+
+    out.output = match result.value {
+        Some(val) => val.to_string(),
+        None => "".to_string(),
+    };
+
+    for warning in result.warnings {
+        writeln!(
+            &mut out.warnings,
+            "{}\n",
+            warning.fancy_format_str(&source).trim(),
+        )
+        .unwrap();
+    }
+
+    if !result.errors.is_empty() {
+        for error in &result.errors {
+            writeln!(&mut out.errors, "{}\n", error.fancy_format_str().trim(),).unwrap();
+        }
+
+        return out;
+    }
+
+    out
+}
+
+fn main() {
+    yew::start_app::<Model>();
+}
diff --git a/web/tvl/blog/2024-02-tvix-update.md b/web/tvl/blog/2024-02-tvix-update.md
new file mode 100644
index 0000000000..ce9bbf547f
--- /dev/null
+++ b/web/tvl/blog/2024-02-tvix-update.md
@@ -0,0 +1,333 @@
+We've now been working on our rewrite of Nix, [Tvix][], for a little more than
+two years.
+
+Our last written update was in September 2023, and although we did publish a
+couple of things in the meantime (flokli's talk on Tvix at [NixCon
+2023][nixcon2023], our interview at the [Nix Developer
+Dialogues][nix-dev-dialogues-tvix], or tazjin's [talk on
+tvix-eval][tvix-eval-ru] (in Russian)), we never found the time to write
+something down.
+
+In the meantime a lot of stuff has happened though, so it's time to change that
+:-)
+
+Note: This blog post is intended for a technical audience that is already
+intimately familiar with Nix, and knows what things like derivations or store
+paths are. If you're new to Nix, this will not make a lot of sense to you!
+
+## Evaluation regression testing
+
+Most of the evaluator work has been driven by evaluating `nixpkgs`, and ensuring
+that we produce the same derivations, and that their build results end up in the
+same store paths.
+
+Builds are not hooked up all the way to the evaluator yet, but for Nix code
+without IFD (such as `nixpkgs`!) we can verify this property without building.
+An evaluated Nix derivation's `outPath` (and `drvPath`) can be compared with
+what C++ Nix produces for the same code, to determine whether we evaluated the
+package (and all of its dependencies!) correctly [^1].
+
+We added integration tests in CI that ensure that the paths we calculate match
+C++ Nix, and are successfully evaluating fairly complicated expressions in them.
+For example, we test against the Firefox derivation, which exercises some of the
+more hairy bits in `nixpkgs` (like WASM cross-compilation infrastructure). Yay!
+
+Although we're avoiding fine-grained optimization until we're sure Tvix
+evaluates all of `nixpkgs` correctly, we still want to have an idea about
+evaluation performance and how our work affects it over time.
+
+For this we extended our benchmark suite and integrated it with
+[Windtunnel][windtunnel], which now regularly runs benchmarks and provides a
+view into how the timings change from commit to commit.
+
+In the future, we plan to run this as a part of code review, before changes are
+applied to our canonical branch, to provide this as an additional signal to
+authors and reviewers without having to run the benchmarks manually.
+
+## ATerms, output path calculation, and `builtins.derivation`
+
+We've implemented all of these features, which comprise the components needed to
+construct derivations in the Nix language, and to allow us to perform the path
+comparisons we mentioned before.
+
+As an interesting side note, in C++ Nix `builtins.derivation` is not actually a
+builtin! It is a piece of [bundled Nix code][nixcpp-builtins-derivation], that
+massages some parameters and then calls the *actual* builtin:
+`derivationStrict`. We've decided to keep this setup, and implemented support in
+Tvix to have builtins defined in `.nix` source code.
+
+These builtins return attribute sets with the previously mentioned `outPath` and
+`drvPath` fields. Implementing them correctly meant that we needed to implement
+output path calculation *exactly* the same way as Nix does (bit-by-bit).
+
+Very little of how this output path calculation works is documented anywhere in
+C++ Nix. It uses a subset of [ATerm][aterm] internally, produces "fingerprints"
+containing hashes of these ATerms, which are then hashed again. The intermediate
+hashes are not printed out anywhere (except if you [patch
+Nix][nixcpp-patch-hashes] to do so).
+
+We already did parts of this correctly while starting this work on
+[go-nix][go-nix-outpath] some while ago, but found some more edge cases and
+ultimately came up with a nicer interface for Tvix.
+
+All the Derivation internal data model, ATerm serialization and output path
+calculation have been sliced out into a more general-purpose
+[nix-compat][nix-compat-derivation] crate, alongside with more documentation
+unit tests and a Derivation ATerm parser, so hopefully this will now be more
+accessible for everyone now.
+
+Note our builtin does *not* yet persist the Derivation anywhere "on
+disk" (though we have a debug CL that does write it to a temporary directory,
+in case we want to track down differences).
+
+## `tvix-[ca]store`
+Tvix now has a store implementation!
+
+### The Nix model
+Inside Nix, store path contents are normally hashed and communicated in NAR
+format, which is very coarse and often wasteful - a single bit of change in one
+file in a large store path causes a new NAR file to be uploaded to the binary
+cache, which then needs to be downloaded.
+
+Additionally, identifying everything by the SHA256 digest of its NAR
+representation makes Nix store paths very incompatible with other
+content-addressed systems, as it's a very Nix-specific format.
+
+### The more granular Tvix model
+After experimenting with some concepts and ideas in Golang, mostly around how to
+improve binary cache performance[^3], both on-disk as well as over the network,
+we settled on a more granular, content-addressed and general-purpose format.
+
+Internally, it behaves very similar to how git handles tree objects, except
+blobs are identified by their raw BLAKE3 digests rather than some custom
+encoding, and similarly, tree/directory objects use the BLAKE3 digest of its
+canonical protobuf serialization as identifiers.
+
+This provides some immediate benefits:
+ - We only need to keep the same data once, even if it's used across different
+   store paths.
+ - Transfers can be more granular and only need to fetch the data that's
+   needed. Due to everything being content-addressed, it can be fetched from
+   anything supporting BLAKE3 digests, immediately making it compatible with
+   other P2P systems (IPFS blake3 blobs, …), or general-purpose
+   content-addressed caches ([bazel-remote]).
+
+There's a lot more details about the data model, certain decisions etc. in
+[the docs][castore-docs].
+
+### Compatibility
+We however still want to stay compatible with Nix, as in calculating
+"NAR-addressed" store paths the same, support substituting from regular Nix
+binary caches, as well as storing all the other additional metadata about store
+paths.
+
+We accomplished this by splitting the two different concerns into two separate
+`tvix-store` and `tvix-castore` crates, with the former one holding all
+Nix-specific metadata and functionality, and the latter being a general-purpose
+content-addressed blob and filesystem tree storage system, which is usable in a
+lot of contexts outside of Tvix too. For example, if you want to use
+tvix-castore to write your own git alternative, or provide granular and
+authenticated access into large scientific datasets, you could!
+
+### Backends
+In addition to a gRPC API and client bindings, there's support for local
+filesystem-based backends, as well as for sled, an embedded K/V database.
+
+We're also currently working on a backend supporting most common object
+storages, as well as on more granular seeking and content-defined chunking for
+blobs.
+
+### FUSE/virtiofs
+A tvix-store can be mounted via FUSE, or exposed through virtiofs[^4].
+While doing the obvious thing - allowing mounting and browsing the contents
+of the store, this will allow lazy substitution of builds on remote builders, be
+in containerized or virtualized workloads.
+
+We have an [example][tvix-boot-readme] in the repository seeding gnu hello into
+a throwaway store, then booting a MicroVM and executing it.
+
+### nar-bridge, bridging binary caches
+`nar-bridge` and the `NixHTTPPathInfoService` bridge `tvix-[ca]store` with
+existing Nix binary caches and Nix.
+
+The former exposes a `tvix-[ca]store` over the common Nix HTTP Binary Cache
+interface (both read and write).
+
+The latter allows Tvix to substitute from regular Nix HTTP Binary caches,
+unpacking NARs and ingesting them on-the-fly into the castore model.
+The necessary parsers for NARInfo, signatures etc are also available in the
+[nix-compat crate][nix-compat-narinfo].
+
+## EvalIO / builtins interacting with the store more closely
+tvix-eval itself is designed to be quite pure when it comes to IO - it doesn't
+do any IO directly on its own, but for the very little IO functionality it
+does as part of "basic interaction with paths" (like importing other
+`.nix` files), it goes through an `EvalIO` interface, which is provided to the
+Evaluator struct on instantiation.
+
+This allows us to be a bit more flexible with how IO looks like in practice,
+which becomes interesting for specific store implementations that might not
+expose a POSIX filesystem directly, or targets where we don't have a filesystem
+at all (like WASM).
+
+Using the `EvalIO` trait also lets `tvix-eval` avoid becoming too strongly
+coupled to a specific store implementation, hashing scheme etc[^2]. As we can
+extend the set of builtins available to the evaluator with "foreign builtins",
+these can live in other crates.
+
+Following this pattern, we started implementing some of the "basic" builtins
+that deal with path access in `tvix-eval`, like:
+
+ - `builtins.pathExists`
+ - `builtins.readFile`
+
+We also recently started working on more complicated builtins like
+`builtins.filterSource` and `builtins.path`, which are also used in `nixpkgs`.
+
+Both import a path into the store, and allow passing a Nix expression that's
+used as a filter function for each path. `builtins.path` can also ensuring the
+imported contents match a certain hash.
+
+This required the builtin to interact with the store and evaluator in a very
+tight fashion, as the filter function (written in Nix) needs to be repeatedly
+executed for each path, and its return value is able to cause the store to skip
+over certain paths (which it previously couldn't).
+
+Getting the abstractions right there required some back-and-forth, but the
+remaining changes should land quite soon.
+
+## Catchables / tryEval
+
+Nix has a limited exception system for dealing with user-generated errors:
+`builtins.tryEval` can be used to detect if an expression fails (if
+`builtins.throw` or `assert` are used to generate it). This feature requires
+extra support in any Nix implementation, as errors may not necessarily cause the
+Nix program to abort.
+
+The C++ Nix implementation reuses the C++ language-provided Exception system for
+`builtins.tryEval` which Tvix can't (even if Rust had an equivalent system):
+
+In C++ Nix the runtime representation of the program in execution corresponds
+to the Nix expression tree of the relevant source files. This means that an
+exception raised in C++ code will automatically bubble up correctly since the
+C++ and Nix call stacks are equivalent to each other.
+
+Tvix compiles the Nix expressions to a byte code program which may be mutated by
+extra optimization rules (for example, we hope to eliminate as many thunks as
+possible in the future). This means that such a correspondence between the state
+of the runtime and the original Nix code is not guaranteed.
+
+Previously, `builtins.tryEval` (which is implemented in Rust and can access VM
+internals) just allowed the VM to recover from certain kinds of errors. This
+proved to be insufficient as it [blew up as soon as a `builtins.tryEval`-ed
+thunk is forced again][tryeval-infrec] – extra bookkeeping was needed. As a
+solution, we now store recoverable errors as a separate runtime value type.
+
+As you can imagine, storing evaluation failures as "normal" values quickly leads
+to all sorts of bugs because most VM/builtins code is written with only ordinary
+values like attribute sets, strings etc. in mind.
+
+While ironing those out, we made sure to supplement those fixes with as many
+test cases for `builtins.tryEval` as possible. This will hopefully prevent any
+regressions if or rather when we touch this system again. We already have some
+ideas for replacing the `Catchable` value type with a cleaner representation,
+but first we want to pin down all the unspoken behaviour.
+
+## String contexts
+
+For a long time, we had the [working theory][refscan-string-contexts] that we
+could get away with not implementing string contexts, and instead do reference
+scanning on a set of "known paths" (and not implement
+`builtins.unsafeDiscardStringContext`).
+
+Unfortunately, we discovered that while this is *conceptually* true, due to a
+[bug in Nix][string-contexts-nix-bug] that's worked around in the
+`stdenv.mkDerivation` implementation, we can't currently do this and calculate
+the same hashes.
+
+Because hash compatibility is important for us at this point, we bit the bullet
+and added support for string contexts into our `NixString` implementation,
+implemented the context-related builtins, and added more unit tests that verify
+string context behaviour of various builtins.
+
+## Strings as byte strings
+
+C++ Nix uses C-style zero-terminated strings internally - however, until
+recently, Tvix has used standard Rust strings for string values. Since those are
+required to be valid UTF-8, we haven't been able to properly represent all the
+string values that Nix supports.
+
+We recently converted our internal representation to byte strings, which allows
+us to treat a `Vec<u8>` as a "string-like" value.
+
+## JSON/TOML/XML
+
+We added support for the `toJSON`, `toXML`, `fromJSON` and `fromTOML` builtins.
+
+`toXML` is particularly exciting, as it's the only format that allows expressing
+(partially applied) functions. It's also used in some of Nix' own test suite, so
+we can now include these in our unit test suite (and pass, yay!).
+
+## Builder protocol, drv->builder
+
+We've been working on the builder protocol, and Tvix's internal build
+representation.
+
+Nix uses derivations (encoded in ATerm) as nodes in its build graph, but it
+refers to other store paths used in that build by these store paths *only*. As
+mentioned before, store paths only address the inputs - and not the content.
+
+This poses a big problem in Nix as soon as builds are scheduled on remote
+builders: There is no guarantee that files at the same store path on the remote
+builder actually have the same contents as on the machine orchestrating the
+build. If a package is not binary reproducible, this can lead to so-called
+[frankenbuilds][frankenbuild].
+
+This also introduces a dependency on the state that's present on the remote
+builder machine: Whatever is in its store and matches the paths will be used,
+even if it was maliciously placed there.
+
+To eliminate this hermiticity problem and increase the integrity of builds,
+we've decided to use content-addressing in the builder protocol.
+
+We're currently hacking on this at [Thaigersprint](https://thaigersprint.org/)
+and might have some more news to share soon!
+
+--------------
+
+That's it for now, try out Tvix and hit us up on IRC or on our mailing list if
+you run into any snags, or have any questions.
+
+ΰΉ€ΰΈˆΰΈ­ΰΈΰΈ±ΰΈ™ΰΈ™ΰΈ° :)
+
+[^1]: We know that we calculated all dependencies correctly because of how their
+      hashes are included in the hashes of their dependents, and so on. More on
+      path calculation and input-addressed paths in the next section!
+[^2]: That's the same reason why `builtins.derivation[Strict]` also lives in
+      `tvix-glue`, not in `tvix-eval`.
+[^3]: See [nix-casync](https://discourse.nixos.org/t/nix-casync-a-more-efficient-way-to-store-and-substitute-nix-store-paths/16539)
+      for one example - investing content-defined chunking (while still keeping
+      the NAR format)
+[^4]: Strictly speaking, not limited to tvix-store - literally anything
+      providing a listing into tvix-castore nodes.
+
+[Tvix]:                       https://tvix.dev
+[aterm]:                      http://program-transformation.org/Tools/ATermFormat.html
+[bazel-remote]:               https://github.com/buchgr/bazel-remote/pull/715
+[castore-docs]:               https://cs.tvl.fyi/depot/-/blob/tvix/castore/docs
+[frankenbuild]:               https://blog.layus.be/posts/2021-06-25-frankenbuilds.html
+[go-nix-outpath]:             https://github.com/nix-community/go-nix/blob/93cb24a868562714f1691840e94d54ef57bc0a5a/pkg/derivation/hashes.go#L52
+[nix-compat-derivation]:      https://docs.tvix.dev/rust/nix_compat/derivation/struct.Derivation.html
+[nix-compat-narinfo]:         https://docs.tvix.dev/rust/nix_compat/narinfo/index.html
+[nix-dev-dialogues-tvix]:     https://www.youtube.com/watch?v=ZYG3T4l8RU8
+[nixcon2023]:                 https://www.youtube.com/watch?v=j67prAPYScY
+[tvix-eval-ru]:               https://tazj.in/blog/tvix-eval-talk-2023
+[nixcpp-builtins-derivation]: https://github.com/NixOS/nix/blob/49cf090cb2f51d6935756a6cf94d568cab063f81/src/libexpr/primops/derivation.nix#L4
+[nixcpp-patch-hashes]:        https://github.com/adisbladis/nix/tree/hash-tracing
+[refscan-string-contexts]:    https://inbox.tvl.su/depot/20230316120039.j4fkp3puzrtbjcpi@tp/T/#t
+[store-docs]:                 https://cs.tvl.fyi/depot/-/blob/tvix/store/docs/api.md
+[string-contexts-nix-bug]:    https://github.com/NixOS/nix/issues/4629
+[tryeval-infrec]:             https://b.tvl.fyi/issues/281
+[tvix-boot-readme]:           https://cs.tvl.fyi/depot/-/blob/tvix/boot/README.md
+[why-string-contexts-now]:    https://cl.tvl.fyi/c/depot/+/10446/7/tvix/eval/docs/build-references.md
+[windtunnel]:                 https://staging.windtunnel.ci/tvl/tvix
diff --git a/web/tvl/blog/default.nix b/web/tvl/blog/default.nix
index fe8d1c42d6..2a1dfe85cc 100644
--- a/web/tvl/blog/default.nix
+++ b/web/tvl/blog/default.nix
@@ -3,16 +3,33 @@
 {
   config = {
     name = "TVL's blog";
-    footer = depot.web.tvl.footer {};
+    footer = depot.web.tvl.footer { };
     baseUrl = "https://tvl.fyi/blog";
   };
 
-  posts = [
+  posts = builtins.sort (a: b: a.date > b.date) [
     {
       key = "rewriting-nix";
       title = "Tvix: We are rewriting Nix";
       date = 1638381387;
       content = ./rewriting-nix.md;
+      author = "tazjin";
+    }
+
+    {
+      key = "tvix-status-september-22";
+      title = "Tvix Status - September '22";
+      date = 1662995534;
+      content = ./tvix-status-202209.md;
+      author = "tazjin";
+    }
+
+    {
+      key = "tvix-update-february-24";
+      title = "Tvix Status - February '24";
+      date = 1707472132;
+      content = ./2024-02-tvix-update.md;
+      author = "flokli";
     }
   ];
 }
diff --git a/web/tvl/blog/tvix-status-202209.md b/web/tvl/blog/tvix-status-202209.md
new file mode 100644
index 0000000000..dae1dae194
--- /dev/null
+++ b/web/tvl/blog/tvix-status-202209.md
@@ -0,0 +1,165 @@
+We've now been working on our rewrite of Nix, [Tvix][], for over a
+year.
+
+As you can imagine, this past year has been turbulent, to say the
+least, given the regions where many of us live. As a result we haven't
+had as much time to work on fun things (like open-source software
+projects!) as we'd like.
+
+We've all been fortunate enough to continue making progress, but we
+just haven't had the bandwidth to communicate with you and keep you up
+to speed on what's going on. That's what this blog post is for.
+
+## Nix language evaluator
+
+The most significant progress in the past six months has been on our
+Nix language evaluator. To answer the most important question: yes,
+you can play with it right now – in [Tvixbolt][]!
+
+We got the evaluator into its current state by first listing all the
+problems we were likely to encounter, then solving them independently,
+and finally assembling all those small-scale solutions into a coherent
+whole. As a result, we briefly had an impractically large private
+source tree, which we have since [integrated][] into our monorepo.
+
+This process was much slower than we would have liked, due to code
+review bandwidth... which is to say, we're all volunteers. People have
+lives, bottlenecks happen.
+
+Most of this code was either written or reviewed by [grfn][],
+[sterni][] and [tazjin][] (that's me!).
+
+### How much of eval is working?
+
+*Most of it*! You can enter most (but not *all*, sorry! Not yet,
+anyway.) Nix language expressions in [Tvixbolt][] and observe how they
+are evaluated.
+
+There's a lot of interesting stuff going on under the hood, such as:
+
+* The Tvix compiler can emit warnings and errors without failing
+  early, and retains as much source information as possible. This will
+  enable you to use Tvix as the basis for developer tooling, such as
+  language servers.
+
+* The Tvix compiler performs in-depth scope analysis, so it can both
+  generate efficient bytecode for accessing identifiers, and alert you
+  about problems in your code before runtime.
+
+* The runtime supports tail-call optimisation in many (but – again –
+  not yet all) cases, so you can evaluate recursive expressions in
+  constant stack space.
+
+* The runtime can give you different backing representations for the
+  same Nix type. For example, an attribute set is represented
+  differently depending on whether you've constructed an empty one, a
+  `name/value` pair, or a larger set. This lets us optimise frequent,
+  well-known use-cases without impacting the general case much.
+
+We've run some initial benchmarks against C++ Nix (using the features
+that are ready), and in most cases Tvix evaluation is an order of
+magnitude faster. To be fair, though, these benchmarks are in no way
+indicative of real-life performance for things like `nixpkgs`. More
+information is coming... eventually.
+
+### How does it all work?
+
+Tvix's evaluator uses a custom abstract machine with a Nix-specific
+instruction set, and a compiler that traverses a parsed Nix AST to
+emit this bytecode and perform a set of optimisations and other
+analysis. The most important benefit of this is that we can plan and
+lay out the execution of a program in a way that is better suited to
+an efficient runtime than directly traversing the AST.
+
+TIP: You can see the generated bytecode in [Tvixbolt][]!
+
+This is all written in about 4000 lines of Rust (naturally), some of
+which – especially around scope-handling – are deceptively simple.
+
+As part of our CI suite, we run the evaluator against some tests we
+wrote ourselves, as well as against the upstream Nix test suite (which
+we don't *quite* pass yet. We're working on it!).
+
+### What's next for tvix-eval?
+
+Despite all our progress, there are still some unfinished feature
+areas, and some of them are pretty important:
+
+1. The majority of Nix's builtins – including fundamental ones like
+   `import` and `derivation` – aren't implemented yet.
+
+2. Neither are recursive attribute sets (`rec`). This isn't because of
+   a problem with the recursion itself, but because of the handling of
+   nested keys (such as `a.b`). We have a lackluster solution already,
+   but are designing a more efficient one.
+
+In both cases, we've mostly figured out what to do; now it's just a
+matter of finding the time to do it. Our progress is steady, and can
+be tracked [in the source][src] (viewer without Javascript
+[here][src-noscript]).
+
+Apart from that, the next steps are:
+
+* Comprehensive benchmarking. We're standing up an infrastructure for
+  continuous benchmarking to measure the impact of changes. It'll also
+  let us identify and optimise hotspots
+
+* Implementing known optimisations. There are some areas of the code
+  that have the potential for significant speed gains, but we're
+  holding off implementing those until the evaluator is feature
+  complete and passes the Nix test suite.
+
+* Finishing our language specification. Based on what we've learned,
+  we're writing a specification of the Nix language that captures its
+  various behaviours in all their tricky subtlety and subtle trickery.
+
+Once we can evaluate `nixpkgs`, we're likely to shift our focus
+towards the other areas of Tvix.
+
+## The Other Areas of Tvix
+
+Speaking of these other areas (most importantly, the builder and store
+implementation), we've made some nice progress there also.
+
+While we've yet to start assembling the actual pieces, [flokli][] and
+[adisbladis][] have been hard at work on [go-nix][], which aims to
+implement many of the low-level primitives required for the Nix store
+and builder (hashing and encoding schemes, archive formats, reference
+scanning ...).
+
+We're looking forward to telling you more in the next Tvix status
+update!
+
+## Outro ...
+
+We'd be delighted to onboard new contributors to Tvix! Please take a
+look at the main [TVL page](https://tvl.fyi) to find out how to get in
+touch with us if you'd like to join!
+
+Thanks also, of course, to [NLNet](https://nlnet.nl/) for sponsoring
+some of this work!
+
+And finally, we would like to thank and pay our respects to jD91mZM2 –
+the original author of
+[rnix-parser](https://github.com/nix-community/rnix-parser) – who has
+sadly passed away. Please, tell people how important they are to you.
+
+We use `rnix-parser` in our compiler, and its well-designed internals
+(also thanks to its new maintainers!) have saved us a lot of time.
+
+That's it for this update. Go play with [Tvixbolt][], have fun
+figuring out weird ways to break it – and if you do, let us know.
+
+We'll see you around!
+
+[Tvix]: https://tvl.fyi/blog/rewriting-nix
+[Tvixbolt]: https://bolt.tvix.dev
+[integrated]: https://cl.tvl.fyi/q/status:merged+%2522tvix/eval%2522+mergedbefore:2022-09-09
+[src]: https://cs.tvl.fyi/depot/-/tree/tvix/eval
+[src-noscript]: https://code.tvl.fyi/tree/tvix/eval
+[tazjin]: https://tazj.in
+[grfn]: https://gws.fyi/
+[sterni]: https://github.com/sternenseemann
+[go-nix]: https://github.com/nix-community/go-nix
+[flokli]: https://flokli.de/
+[adisbladis]: https://github.com/adisbladis
diff --git a/web/tvl/default.nix b/web/tvl/default.nix
index 1025b1a7b2..8bbc7d566a 100644
--- a/web/tvl/default.nix
+++ b/web/tvl/default.nix
@@ -4,7 +4,7 @@ with depot.nix.yants;
 
 let
   inherit (builtins) filter;
-  inherit (pkgs) graphviz runCommandNoCC writeText;
+  inherit (pkgs) graphviz runCommand writeText;
   inherit (depot.web) atom-feed blog tvl;
 
   listPosts = defun [ (list blog.post) string ] (posts:
@@ -16,9 +16,10 @@ let
       (map (p: "cp ${blog.renderPost tvl.blog.config p} $out/blog/${p.key}.html") posts)
   );
 
-  tvlGraph = runCommandNoCC "tvl.svg" {
-    nativeBuildInputs = with pkgs; [ fontconfig freetype cairo jetbrains-mono ];
-  } ''
+  tvlGraph = runCommand "tvl.svg"
+    {
+      nativeBuildInputs = with pkgs; [ fontconfig freetype cairo jetbrains-mono ];
+    } ''
     ${graphviz}/bin/neato -Tsvg ${./tvl.dot} > $out
   '';
 
@@ -62,6 +63,20 @@ let
       Feel free to explore the tech we have built so far, all our
       systems are linked in the footer.
 
+      ----------------
+
+      ## Blog
+
+      Here are the most recent TVL blog posts.
+
+      ${listPosts publishedPosts}
+
+      You can also follow our [atom feed](https://tvl.fyi/feed.atom).
+
+      ----------------
+
+      ## Getting in touch
+
       We mostly hang out on IRC. You can find us in [`#tvl`][tvl-irc]
       on [hackint][], which is also reachable [via XMPP][hackint-xmpp]
       at [`#tvl@irc.hackint.org`][tvl-xmpp] (sic!) and [via
@@ -77,15 +92,11 @@ let
       [tvl-matrix]: https://matrix.to/#/#tvl:hackint.org
       [tvl-webchat]: https://webirc.hackint.org/#ircs://irc.hackint.org/#tvl
 
-      ----------------
-
-      ## Blog
-
-      Here are the most recent TVL blog posts.
+      Discussions of our software, patches, and anything else really
+      can also be sent to us via email to **depot@tvl.su**. You can
+      see the mails submitted to that list in our [public inbox][].
 
-      ${listPosts publishedPosts}
-
-      You can also follow our [atom feed](https://tvl.fyi/feed.atom).
+      [public inbox]: https://inbox.tvl.su
 
       ----------------
 
@@ -121,7 +132,8 @@ let
       </style>
     '';
   };
-in runCommandNoCC "website" {} ''
+in
+runCommand "website" { } ''
   mkdir -p $out/blog
   cp ${homepage} $out/index.html
   ${postRenderingCommands tvl.blog.posts}
diff --git a/web/tvl/footer/default.nix b/web/tvl/footer/default.nix
index 7412d019ee..dc2c963f90 100644
--- a/web/tvl/footer/default.nix
+++ b/web/tvl/footer/default.nix
@@ -15,7 +15,7 @@ args: ''
     <a class="uncoloured-link" href="https://todo.tvl.fyi/">todos</a>
     |
     <a class="uncoloured-link" href="https://atward.tvl.fyi/">search</a>
-    '' + lib.optionalString (args ? extraFooter) args.extraFooter + ''
+'' + lib.optionalString (args ? extraFooter) args.extraFooter + ''
   </p>
   <p class="lod">ΰ² _ΰ² </p>
 ''
diff --git a/web/tvl/logo/default.nix b/web/tvl/logo/default.nix
index 940f67199b..8084135492 100644
--- a/web/tvl/logo/default.nix
+++ b/web/tvl/logo/default.nix
@@ -21,20 +21,22 @@ let
 
   # Create an animated CSS that equally spreads out the colours over
   # the animation duration (1min).
-  animatedCss = colours: let
-    # Calculate at which percentage offset each colour should appear.
-    stepSize = 100 / ((builtins.length colours) - 1);
-    frames = lib.imap0 (idx: colour: { inherit colour; at = idx * stepSize; }) colours;
-    frameCss = frame: "${toString frame.at}% { fill: ${frame.colour}; }";
-  in ''
-    #armchair-background {
-      animation: 30s infinite alternate armchairPalette;
-    }
+  animatedCss = colours:
+    let
+      # Calculate at which percentage offset each colour should appear.
+      stepSize = 100 / ((builtins.length colours) - 1);
+      frames = lib.imap0 (idx: colour: { inherit colour; at = idx * stepSize; }) colours;
+      frameCss = frame: "${toString frame.at}% { fill: ${frame.colour}; }";
+    in
+    ''
+      #armchair-background {
+        animation: 30s infinite alternate armchairPalette;
+      }
 
-    @keyframes armchairPalette {
-    ${lib.concatStringsSep "\n" (map frameCss frames)}
-    }
-  '';
+      @keyframes armchairPalette {
+      ${lib.concatStringsSep "\n" (map frameCss frames)}
+      }
+    '';
 
   # Dark version of the logo, suitable for light backgrounds.
   darkCss = armchairCss: ''
@@ -67,7 +69,8 @@ let
     </svg>
   '';
 
-in depot.nix.readTree.drvTargets(lib.fix (self: {
+in
+depot.nix.readTree.drvTargets (lib.fix (self: {
   # Expose the logo construction functions.
   inherit palette darkCss lightCss animatedCss staticCss;
 
@@ -75,7 +78,7 @@ in depot.nix.readTree.drvTargets(lib.fix (self: {
   logoSvg = style: pkgs.writeText "logo.svg" (logoSvg style);
 
   # Create a PNG of the TVL logo with the specified style and DPI.
-  logoPng = style: dpi: pkgs.runCommandNoCC "logo.png" {} ''
+  logoPng = style: dpi: pkgs.runCommand "logo.png" { } ''
     ${pkgs.inkscape}/bin/inkscape \
       --export-area-drawing \
       --export-background-opacity 0 \
@@ -87,7 +90,8 @@ in depot.nix.readTree.drvTargets(lib.fix (self: {
   pastelRainbow = self.logoSvg (darkCss (animatedCss (lib.attrValues palette)));
 }
 
-# Add individual outputs for static dark logos of each colour.
-// (lib.mapAttrs'
-    (k: v: lib.nameValuePair "${k}Png"
-     (self.logoPng (darkCss (staticCss v)) 96)) palette)))
+  # Add individual outputs for static dark logos of each colour.
+  // (lib.mapAttrs'
+  (k: v: lib.nameValuePair "${k}Png"
+    (self.logoPng (darkCss (staticCss v)) 96))
+  palette)))
diff --git a/web/tvl/template/default.nix b/web/tvl/template/default.nix
index 6ccc10de62..50ddc31e73 100644
--- a/web/tvl/template/default.nix
+++ b/web/tvl/template/default.nix
@@ -1,6 +1,7 @@
 { depot, pkgs, lib, ... }:
 
-{ # content of the <title> tag
+{
+  # content of the <title> tag
   title
   # main part of the page, usually wrapped with <main>
 , content
@@ -13,11 +14,12 @@
 }@args:
 
 let
-  inherit (pkgs) runCommandNoCC lib;
+  inherit (pkgs) runCommand lib;
   inherit (depot.tools) cheddar;
 in
 
-runCommandNoCC "${lib.strings.sanitizeDerivationName title}-index.html" {
+runCommand "${lib.strings.sanitizeDerivationName title}-index.html"
+{
   headerPart = ''
     <!DOCTYPE html>
     <head>
@@ -36,11 +38,11 @@ runCommandNoCC "${lib.strings.sanitizeDerivationName title}-index.html" {
   inherit content;
 
   footerPart = ''
-    <hr>
-    <footer>
-      ${depot.web.tvl.footer args}
-    </footer>
-  </body>
+      <hr>
+      <footer>
+        ${depot.web.tvl.footer args}
+      </footer>
+    </body>
   '';
 
   passAsFile = [ "headerPart" "content" "footerPart" ];
diff --git a/web/tvl/tvl.dot b/web/tvl/tvl.dot
index b28c529e0c..a4ced3d473 100644
--- a/web/tvl/tvl.dot
+++ b/web/tvl/tvl.dot
@@ -9,9 +9,11 @@ digraph tvl {
   // people
   subgraph {
     Irenes [href="https://www.pluralpride.com/"];
+    K900 [href="https://0upti.me/"];
+    Profpatsch [href="http://profpatsch.de/"];
     adisbladis [href="http://nixos.expert/"];
+    amjoseph;
     andi [label="andi-" href="https://andreas.rammhold.de/"];
-    anon1 [color="grey" fontcolor="grey"];
     aurora [href="https://nonegenderleftfox.aventine.se/"];
     benjojo [href="https://benjojo.co.uk/"];
     cynthia [href="https://cynthia.re/"];
@@ -19,32 +21,30 @@ digraph tvl {
     ericvolp [href="https://ericv.me"];
     espes;
     eta [href="https://theta.eu.org/"];
+    etu [href="https://elis.nu/"];
+    ezemtsov [href="https://github.com/ezemtsov"];
     firefly [href="http://firefly.nu/"];
     flokli [href="https://flokli.de/"];
     fzakaria [href="https://fzakaria.com/"];
     ghuntley [href="https://ghuntley.com/"];
     grfn [href="http://gws.fyi"];
-    htbf [href="https://htbf.dev/"];
     implr [href="https://twitter.com/implring"];
     isomer [href="https://www.lorier.net/"];
+    j4m3s [href="https://github.com/j4m3s-s"];
     jusrin [href="https://jusrin.dev/"];
     kn;
     lassulus;
     leah2 [href="https://leahneukirchen.org/"];
     lukegb [href="https://lukegb.com/"];
     marcusr [href="http://marcus.nordaaker.com/"];
-    mdjnsn;
     ncl;
     nikky [href="http://nikky.moe/"];
     nyanotech [href="https://twitter.com/nyanotech"];
-    poigon;
-    Profpatsch [href="http://profpatsch.de/"];
-    qyliss [href="https://alyssa.is"];
     seven [href="https://open.spotify.com/user/so7"];
-    spacekookie [href="https://spacekookie.de/"];
     sterni [href="https://sterni.lv/"];
     tazjin [href="https://tazj.in/"];
     wpcarro [href="https://wpcarro.dev/"];
+    raitobezarius [href="https://ryan.lahfa.xyz/"];
     yuuko;
   }
 
@@ -60,8 +60,8 @@ digraph tvl {
     node [color="#db4437" fontcolor="#db4437"];
     eve [href="https://www.eveonline.com/"];
     nix [href="https://nixos.org/nix/"];
+    tvix [href="https://code.tvl.fyi/tree/tvix"];
     ircv3 [href="https://ircv3.net/"];
-    lgbtslack [label="lgbt.tech" href="https://lgbtq.technology/"];
     muccc [label="Β΅ccc" href="https://muc.ccc.de/"];
     afra [label="AfRA" href="https://afra-berlin.de/"];
   }
@@ -75,12 +75,15 @@ digraph tvl {
   // primary edges (how did they end up in TVL?)
   subgraph {
     // Direct edges
-    nix -> tazjin;
+    nix -> TVL;
+    tvix -> TVL;
+
     spotify -> tazjin;
     google -> tazjin;
     eve -> tazjin;
     unspecific -> tazjin;
     edef -> tazjin;
+    ezemtsov -> tazjin;
 
     // via nix
     adisbladis -> nix;
@@ -90,6 +93,12 @@ digraph tvl {
     andi -> nix;
     Profpatsch -> nix;
     lassulus -> nix;
+    etu -> nix;
+
+    // via tvix
+    j4m3s -> tvix;
+    amjoseph -> tvix;
+    K900 -> tvix;
 
     // via edef
     benjojo -> edef;
@@ -97,50 +106,41 @@ digraph tvl {
     firefly -> edef;
     leah2 -> aurora;
     ncl -> edef;
-    qyliss -> edef;
 
     // via spotify
     seven -> spotify;
 
     // via google
-    htbf -> google;
     Irenes -> google;
     isomer -> google;
     lukegb -> google;
     wpcarro -> google;
     fzakaria -> google;
-    mdjnsn -> google;
 
     // random primary
     grfn -> wpcarro;
-    anon1 -> google;
     aurora -> eve;
     cynthia -> benjojo;
-    eta -> anon1;
+    eta -> unspecific;
     ericvolp -> lukegb;
     marcusr -> unspecific;
-    poigon -> eve;
     implr -> lukegb;
     afra -> unspecific;
     nikky -> afra;
-    spacekookie -> qyliss;
     kn -> flokli;
     sterni -> Profpatsch;
     yuuko -> ncl;
+    raitobezarius -> flokli;
   }
 
   // secondary edges (how are they connected otherwise?)
   subgraph {
     edge [weight=0 style="dotted" color="grey" arrowhead="none"];
 
-    // lgbt slack
-    aurora -> lgbtslack;
-    leah2 -> lgbtslack;
-    edef -> lgbtslack;
-
     // ircv3
     eta -> ircv3;
     firefly -> ircv3;
+    raitobezarius -> ircv3;
 
     // Β΅ccc
     leah2 -> muccc;
@@ -156,11 +156,10 @@ digraph tvl {
     lukegb -> benjojo;
     espes -> benjojo;
     espes -> aurora;
-    qyliss -> nix;
     grfn -> nix;
     edef -> nix;
-    spacekookie -> afra;
-    qyliss -> afra;
+    ezemtsov -> nix;
+    raitobezarius -> nix;
   }
 
   // baby
diff --git a/web/volgasprint/README.md b/web/volgasprint/README.md
new file mode 100644
index 0000000000..d593d17b20
--- /dev/null
+++ b/web/volgasprint/README.md
@@ -0,0 +1,3 @@
+# VolgaSprint.org
+
+Website for the Nix sprint in Kazan.
diff --git a/web/volgasprint/default.nix b/web/volgasprint/default.nix
new file mode 100644
index 0000000000..5f49d88e26
--- /dev/null
+++ b/web/volgasprint/default.nix
@@ -0,0 +1,23 @@
+{ pkgs, ... }:
+
+let
+  pythonEnv = pkgs.python3.withPackages (ps: with ps; [
+    mkdocs
+    mkdocs-material
+    pillow
+    cairosvg
+  ]);
+in
+pkgs.runCommand "website"
+{
+  buildInputs = [
+    pythonEnv
+  ];
+}
+  ''
+    cp -r ${./.} ./source
+    chmod -R +w ./source
+    cd ./source
+    mkdocs build
+    mv site $out
+  ''
diff --git a/web/volgasprint/docs/assets/baumana.webp b/web/volgasprint/docs/assets/baumana.webp
new file mode 100644
index 0000000000..920e762849
--- /dev/null
+++ b/web/volgasprint/docs/assets/baumana.webp
Binary files differdiff --git a/web/volgasprint/docs/assets/kazan_overview.webp b/web/volgasprint/docs/assets/kazan_overview.webp
new file mode 100644
index 0000000000..39c1ebb428
--- /dev/null
+++ b/web/volgasprint/docs/assets/kazan_overview.webp
Binary files differdiff --git a/web/volgasprint/docs/assets/kazan_tree.webp b/web/volgasprint/docs/assets/kazan_tree.webp
new file mode 100644
index 0000000000..31d1ecaf5b
--- /dev/null
+++ b/web/volgasprint/docs/assets/kazan_tree.webp
Binary files differdiff --git a/web/volgasprint/docs/assets/logos/nixos.svg b/web/volgasprint/docs/assets/logos/nixos.svg
new file mode 100644
index 0000000000..d69da69546
--- /dev/null
+++ b/web/volgasprint/docs/assets/logos/nixos.svg
@@ -0,0 +1,459 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="435.58978mm"
+   height="136.68491mm"
+   viewBox="0 0 1543.4284 484.31659"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.92.0 r15299"
+   sodipodi:docname="nixos-hex.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5562">
+      <stop
+         style="stop-color:#699ad7;stop-opacity:1"
+         offset="0"
+         id="stop5564" />
+      <stop
+         id="stop5566"
+         offset="0.24345198"
+         style="stop-color:#7eb1dd;stop-opacity:1" />
+      <stop
+         style="stop-color:#7ebae4;stop-opacity:1"
+         offset="1"
+         id="stop5568" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5053">
+      <stop
+         style="stop-color:#415e9a;stop-opacity:1"
+         offset="0"
+         id="stop5055" />
+      <stop
+         id="stop5057"
+         offset="0.23168644"
+         style="stop-color:#4a6baf;stop-opacity:1" />
+      <stop
+         style="stop-color:#5277c3;stop-opacity:1"
+         offset="1"
+         id="stop5059" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5960"
+       inkscape:collect="always">
+      <stop
+         id="stop5962"
+         offset="0"
+         style="stop-color:#637ddf;stop-opacity:1" />
+      <stop
+         style="stop-color:#649afa;stop-opacity:1"
+         offset="0.23168644"
+         id="stop5964" />
+      <stop
+         id="stop5966"
+         offset="1"
+         style="stop-color:#719efa;stop-opacity:1" />
+    </linearGradient>
+    <linearGradient
+       y2="515.97058"
+       x2="282.26105"
+       y1="338.62445"
+       x1="213.95642"
+       gradientTransform="translate(983.36076,601.38885)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5855"
+       xlink:href="#linearGradient5960"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5562"
+       id="linearGradient5384"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(70.650339,-1055.1511)"
+       x1="200.59668"
+       y1="351.41116"
+       x2="290.08701"
+       y2="506.18814" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5053"
+       id="linearGradient5386"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(864.69589,-1491.3405)"
+       x1="-584.19934"
+       y1="782.33563"
+       x2="-496.29703"
+       y2="937.71399" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.34760742"
+     inkscape:cx="803.54996"
+     inkscape:cy="186.45699"
+     inkscape:document-units="px"
+     inkscape:current-layer="g5329"
+     showgrid="false"
+     inkscape:window-width="1366"
+     inkscape:window-height="706"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:snap-global="true"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer7"
+     inkscape:label="bg"
+     style="display:none">
+    <rect
+       transform="translate(-132.5822,958.04022)"
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       id="rect5389"
+       width="1543.4283"
+       height="483.7439"
+       x="132.5822"
+       y="-957.77832" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="guide"
+     style="display:none;opacity:0.51599995"
+     transform="translate(-132.5822,958.04022)">
+    <rect
+       y="-957.77832"
+       x="132.5822"
+       height="483.7439"
+       width="1543.4283"
+       id="rect5350"
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#d4d4d4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+    <rect
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#9b9b9b;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       id="rect5346"
+       width="1496.443"
+       height="435.68069"
+       x="155.77646"
+       y="-933.38721"
+       inkscape:export-xdpi="17.971878"
+       inkscape:export-ydpi="17.971878" />
+    <rect
+       y="-851.65918"
+       x="159.02695"
+       height="272.58423"
+       width="1492.5731"
+       id="rect5348"
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#848484;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer6"
+     inkscape:label="logo-guide"
+     style="display:none"
+     transform="translate(-132.5822,958.04022)">
+    <rect
+       y="-958.02759"
+       x="132.65129"
+       height="484.30399"
+       width="550.41602"
+       id="rect5379"
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5c201e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       inkscape:export-filename="/home/tim/dev/nix/homepage/logo/nix-wiki.png"
+       inkscape:export-xdpi="22.07"
+       inkscape:export-ydpi="22.07" />
+    <rect
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c24a46;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       id="rect5372"
+       width="501.94415"
+       height="434.30405"
+       x="156.12303"
+       y="-933.02759"
+       inkscape:export-filename="/home/tim/dev/nix/homepage/logo/nixos-logo-only-hires-print.png"
+       inkscape:export-xdpi="212.2"
+       inkscape:export-ydpi="212.2" />
+    <rect
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#d98d8a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       id="rect5381"
+       width="24.939611"
+       height="24.939611"
+       x="658.02826"
+       y="-958.04022" />
+  </g>
+  <g
+     inkscape:label="print-logo"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline"
+     sodipodi:insensitive="true"
+     transform="translate(-132.5822,958.04022)">
+    <path
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 309.40365,-710.2521 122.19683,211.6751 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4902 -33.22946,-57.8256 z"
+       id="path4861"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#7ebae4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 353.50926,-797.4433 -122.21756,211.6631 -28.53477,-48.37 32.93839,-56.6875 -65.41521,-0.1719 -13.9414,-24.1698 14.23637,-24.721 93.11177,0.2939 33.46371,-57.6903 z"
+       id="use4863"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#7ebae4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 362.88537,-628.243 244.41439,0.012 -27.62229,48.8968 -65.56199,-0.1817 32.55876,56.7371 -13.96098,24.1585 -28.52722,0.032 -46.3013,-80.7841 -66.69317,-0.1353 z"
+       id="use4865"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#7ebae4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 505.14318,-720.9886 -122.19683,-211.6751 56.15706,-0.5268 32.6236,56.8692 32.85645,-56.5653 27.90237,0.011 14.29086,24.6896 -46.81047,80.4902 33.22946,57.8256 z"
+       id="use4867"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:connector-curvature="0"
+       id="path4873"
+       d="m 309.40365,-710.2521 122.19683,211.6751 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4902 -33.22946,-57.8256 z"
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+    <path
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:connector-curvature="0"
+       id="use4875"
+       d="m 451.3364,-803.53264 -244.4144,-0.012 27.62229,-48.89685 65.56199,0.18175 -32.55875,-56.73717 13.96097,-24.15851 28.52722,-0.0315 46.3013,80.78414 66.69317,0.13524 z"
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+    <path
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:connector-curvature="0"
+       id="use4877"
+       d="m 460.87178,-633.8425 122.21757,-211.66304 28.53477,48.37003 -32.93839,56.68751 65.4152,0.1718 13.9414,24.1698 -14.23636,24.7211 -93.11177,-0.294 -33.46371,57.6904 z"
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+    <g
+       id="layer2"
+       inkscape:label="guides"
+       style="display:none"
+       transform="translate(72.039038,-1799.4476)">
+      <path
+         d="M 460.60629,594.72881 209.74183,594.7288 84.309616,377.4738 209.74185,160.21882 l 250.86446,1e-5 125.43222,217.255 z"
+         inkscape:randomized="0"
+         inkscape:rounded="0"
+         inkscape:flatsided="true"
+         sodipodi:arg2="1.5707963"
+         sodipodi:arg1="1.0471976"
+         sodipodi:r2="217.25499"
+         sodipodi:r1="250.86446"
+         sodipodi:cy="377.47382"
+         sodipodi:cx="335.17407"
+         sodipodi:sides="6"
+         id="path6032"
+         style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.23600003;fill:#4e4d52;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+         sodipodi:type="star" />
+      <path
+         transform="translate(0,-308.26772)"
+         sodipodi:type="star"
+         style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#4e4d52;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+         id="path5875"
+         sodipodi:sides="6"
+         sodipodi:cx="335.17407"
+         sodipodi:cy="685.74158"
+         sodipodi:r1="100.83495"
+         sodipodi:r2="87.32563"
+         sodipodi:arg1="1.0471976"
+         sodipodi:arg2="1.5707963"
+         inkscape:flatsided="true"
+         inkscape:rounded="0"
+         inkscape:randomized="0"
+         d="m 385.59154,773.06721 -100.83495,0 -50.41747,-87.32564 50.41748,-87.32563 100.83495,10e-6 50.41748,87.32563 z" />
+      <path
+         transform="translate(0,-308.26772)"
+         sodipodi:nodetypes="ccccccccc"
+         inkscape:connector-curvature="0"
+         id="path5851"
+         d="m 1216.5591,938.53395 123.0545,228.14035 -42.6807,-1.2616 -43.4823,-79.7725 -39.6506,80.3267 -32.6875,-19.7984 53.4737,-100.2848 -37.1157,-73.88955 z"
+         style="fill:url(#linearGradient5855);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <rect
+         style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.41499999;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c53a3a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         id="rect5884"
+         width="48.834862"
+         height="226.22897"
+         x="-34.74221"
+         y="446.17056"
+         transform="matrix(0.8660254,-0.5,0.5,0.8660254,0,0)" />
+      <path
+         transform="translate(0,-308.26772)"
+         sodipodi:type="star"
+         style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.50899999;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         id="path3428"
+         sodipodi:sides="6"
+         sodipodi:cx="223.93674"
+         sodipodi:cy="878.63831"
+         sodipodi:r1="28.048939"
+         sodipodi:r2="24.291094"
+         sodipodi:arg1="0"
+         sodipodi:arg2="0.52359878"
+         inkscape:flatsided="true"
+         inkscape:rounded="0"
+         inkscape:randomized="0"
+         d="m 251.98568,878.63831 -14.02447,24.29109 h -28.04894 l -14.02447,-24.29109 14.02447,-24.2911 h 28.04894 z" />
+      <use
+         x="0"
+         y="0"
+         xlink:href="#rect5884"
+         id="use4252"
+         transform="matrix(0.5,0.8660254,-0.8660254,0.5,558.02636,12.372992)"
+         width="100%"
+         height="100%" />
+      <rect
+         style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.6507937;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         id="rect4254"
+         width="5.3947482"
+         height="115.12564"
+         x="545.71014"
+         y="467.07007"
+         transform="matrix(0.8660254,0.5,-0.5,0.8660254,0,-308.26772)" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="gradient-logo"
+     style="display:inline;opacity:1"
+     sodipodi:insensitive="true"
+     transform="translate(-132.5822,958.04022)">
+    <path
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3336-6"
+       d="m 309.54892,-710.38827 122.19683,211.67512 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4901 -33.22946,-57.8257 z"
+       style="opacity:1;fill:url(#linearGradient5384);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    <use
+       height="100%"
+       width="100%"
+       transform="rotate(60,407.11155,-715.78724)"
+       id="use3439-6"
+       inkscape:transform-center-y="151.59082"
+       inkscape:transform-center-x="124.43045"
+       xlink:href="#path3336-6"
+       y="0"
+       x="0" />
+    <use
+       height="100%"
+       width="100%"
+       transform="rotate(-60,407.31177,-715.70016)"
+       id="use3445-0"
+       inkscape:transform-center-y="75.573958"
+       inkscape:transform-center-x="-168.20651"
+       xlink:href="#path3336-6"
+       y="0"
+       x="0" />
+    <use
+       height="100%"
+       width="100%"
+       transform="rotate(180,407.41868,-715.7565)"
+       id="use3449-5"
+       inkscape:transform-center-y="-139.94592"
+       inkscape:transform-center-x="59.669705"
+       xlink:href="#path3336-6"
+       y="0"
+       x="0" />
+    <path
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient5386);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 309.54892,-710.38827 122.19683,211.67512 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4901 -33.22946,-57.8256 z"
+       id="path4260-0"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <use
+       height="100%"
+       width="100%"
+       transform="rotate(120,407.33916,-716.08356)"
+       id="use4354-5"
+       xlink:href="#path4260-0"
+       y="0"
+       x="0"
+       style="display:inline" />
+    <use
+       height="100%"
+       width="100%"
+       transform="rotate(-120,407.28823,-715.86995)"
+       id="use4362-2"
+       xlink:href="#path4260-0"
+       y="0"
+       x="0"
+       style="display:inline" />
+  </g>
+  <g
+     style="display:inline"
+     inkscape:label="text-vegur"
+     id="g5329"
+     inkscape:groupmode="layer"
+     transform="translate(-132.5822,958.04022)">
+    <g
+       aria-label="Nix"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:395.09683228px;line-height:125%;font-family:Carlito;-inkscape-font-specification:Carlito;letter-spacing:0px;word-spacing:0px;display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text5407">
+      <path
+         d="m 969.15319,-847.11833 h -30.81755 v 139.86428 c 0,19.75484 0.79019,50.96749 1.97548,85.73601 h -1.18529 c -15.40877,-28.84207 -32.79303,-56.49884 -45.04104,-75.46349 l -96.79872,-150.1368 h -42.27536 v 267.87565 h 30.81755 v -139.86427 c 0,-19.75485 -0.79019,-56.89395 -1.97548,-91.26737 h 1.18529 c 22.91561,39.90478 36.34891,62.0302 48.99201,80.99485 l 96.79872,150.13679 h 38.32439 z"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Vegur;-inkscape-font-specification:Vegur"
+         id="path4683" />
+      <path
+         d="m 1027.8251,-579.24268 h 33.1881 v -191.22686 h -33.1881 z m 16.594,-219.27874 c 11.4578,0 20.5451,-9.08722 20.5451,-20.54503 0,-11.45781 -9.0873,-20.54504 -20.5451,-20.54504 -11.4578,0 -20.545,9.08723 -20.545,20.54504 0,11.45781 9.0872,20.54503 20.545,20.54503 z"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Vegur;-inkscape-font-specification:Vegur"
+         id="path4685" />
+      <path
+         d="m 1267.7785,-770.46954 h -37.9293 l -46.6214,70.32723 h -1.1853 l -45.0411,-70.32723 h -41.09 l 68.3517,93.24285 v 1.18529 l -70.7223,96.79872 h 37.9293 l 49.7822,-75.85859 h 1.1853 l 49.7822,75.85859 h 41.09 l -72.3027,-98.37911 v -1.18529 z"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Vegur;-inkscape-font-specification:Vegur"
+         id="path4687" />
+    </g>
+    <g
+       aria-label="O"
+       transform="scale(0.95067318,1.0518862)"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:367.48727417px;line-height:125%;font-family:Carlito;-inkscape-font-specification:Carlito;letter-spacing:0px;word-spacing:0px;display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text5356">
+      <path
+         d="m 1468.5915,-800.79725 c -66.1477,0 -120.5358,48.14083 -120.5358,128.25306 0,80.11223 54.3881,128.25306 120.5358,128.25306 66.1477,0 120.5359,-48.14083 120.5359,-128.25306 0,-80.11223 -54.3882,-128.25306 -120.5359,-128.25306 z m 0,24.98914 c 49.2433,0 86.727,36.74872 86.727,103.26392 0,66.5152 -37.4837,103.26392 -86.727,103.26392 -49.2433,0 -86.727,-36.74872 -86.727,-103.26392 0,-66.5152 37.4837,-103.26392 86.727,-103.26392 z"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Vegur;-inkscape-font-specification:Vegur"
+         id="path4680" />
+    </g>
+    <g
+       aria-label="S"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:386.55480957px;line-height:125%;font-family:Carlito;-inkscape-font-specification:Carlito;letter-spacing:0px;word-spacing:0px;display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text5364">
+      <path
+         d="m 1523.761,-773.88643 c 0,37.10927 19.3277,57.21012 64.1681,75.37819 34.4034,13.91598 48.3193,26.28573 48.3193,51.79835 0,30.92438 -25.126,46.38657 -58.3697,46.38657 -17.395,0 -37.1093,-2.70588 -58.7564,-10.05042 l -3.479,26.67228 c 18.9412,6.95799 39.8152,9.66387 60.6891,9.66387 51.7984,0 95.0925,-26.28573 95.0925,-79.24374 0,-36.7227 -22.4202,-54.50422 -67.6471,-72.6723 -30.1512,-11.9832 -44.8403,-24.73951 -44.8403,-51.41179 0,-25.89917 22.4202,-40.2017 50.6387,-40.2017 16.6218,0 34.7899,4.2521 47.5462,9.27732 l 3.479,-26.28573 c -14.6891,-6.18488 -32.8572,-9.27732 -52.958,-9.27732 -47.5463,0 -83.8824,27.4454 -83.8824,69.96642 z"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Vegur;-inkscape-font-specification:Vegur"
+         id="path4677" />
+    </g>
+  </g>
+</svg>
diff --git a/web/volgasprint/docs/assets/logos/volgasprint_nix.svg b/web/volgasprint/docs/assets/logos/volgasprint_nix.svg
new file mode 100644
index 0000000000..adc7c0da65
--- /dev/null
+++ b/web/volgasprint/docs/assets/logos/volgasprint_nix.svg
@@ -0,0 +1,28 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="2353 4900 5790 5018">
+  <defs>
+    <style>
+      .white {
+        fill: #fefefe
+      }
+
+      .dark {
+        fill: #5277c3
+      }
+
+      .light {
+        fill: #7EBAE4
+      }
+    </style>
+  </defs>
+  <g id="volgasprint">
+    <!-- Do not change the order: It affects the overlapping! -->
+    <path id="northeast" class="light" d="m6671.55 6762.87-357.77-619.7 546.73-946.96-167.66-290.41h-335.34l-379.06 656.57-379.07-656.57h-670.66l1407.5 2437.88z" />
+    <path id="northwest" class="dark" d="M5395.29 5852.77h-707.14l-546.72-946.97h-335.34l-167.66 290.4 379.07 656.57h-758.14l-335.33 580.81h2797.01z" />
+    <path id="southeast" class="light" d="m6860.51 9615.39-379.07-656.57 758.14.01 335.33-580.81H4746.48l335.33 580.81h728.98l546.73 946.97h335.33z" />
+    <path id="west" class="light" d="M4659 6484.08h-670.66l-364.49 631.32H2530.38l-167.65 290.4 167.65 290.4h758.15l-379.07 656.57 335.32 580.81z" />
+    <path id="east" class="dark" d="M6875.09 7696.2h1093.47l167.65-290.4-167.65-290.4h-758.15l379.07-656.57-335.32-580.81-1414.22 2449.5h670.66z" />
+    <path id="tower" d="m5240.93 5261.4-81.71 591.38h-38.58v217.01h-.01l-75.42 479.8h-93.65l-16.13 39.17h16.13v286.59H4856.9v.02l-26.14 64.39h26.14v359.29h-185.08v525.28h-155.33v671.56h-258.68V9905.8h1983.32V8495.89h-258.68v-671.56h-155.33v-525.28h-185.08v-359.29h26.14l-26.14-64.39v-.02H5547.38v-286.59h16.13l-16.13-39.17h-93.65l-75.42-479.8h-.01v-217.01h-38.58l-81.71-591.38c13.65-3.74 23.67-16.24 23.67-31.06 0-17.8-14.42-32.22-32.21-32.22-12.56 0-22.75-10.18-22.75-22.75 0-9.94 6.37-18.38 15.24-21.49v-27.38c-59.45-7.67-102.21-61.44-95.9-121.39 6.08-57.95 55.85-100.74 113.14-99.11-25.87 14.46-44.53 40.85-47.87 72.55-5.45 51.85 32.16 98.3 84 103.75 21.09 2.21 41.28-2.69 58.18-12.78-18.86 34.71-55.53 57.66-96.53 57.9v26.46c8.87 3.11 15.24 11.55 15.24 21.49 0 12.57-10.19 22.75-22.75 22.75-17.79 0-32.21 14.42-32.21 32.22 0 14.82 10.02 27.32 23.67 31.06z" style="fill:#5b5b5b" />
+    <path id="windows" class="white" d="M5060.89 6652.77h-.02c-31.64 0-57.51 25.87-57.51 57.51v101.05h115.04v-101.05c0-31.64-25.87-57.51-57.51-57.51zm538.58 3253.03h-700v-641.91c0-192.01 157.09-349.1 349.11-349.1h1.78c192.02 0 349.11 157.09 349.11 349.1v641.91zm-329.78-3970.44h-40.44v93.49h40.44v-93.49zm81.67 0h-40.45v93.49h40.45v-93.49zm-162.19 0h-40.45v93.49h40.45v-93.49zm-58.78 2293.71h-136.88v202.23h136.88v-202.23zm176.6-1176.25h-115.04v158.56h115.04v-158.56zm-252.71 563.22h-107.34v158.57h107.34v-158.57zm498.13 0h-107.33v158.57h107.33v-158.57zm-46.9 613.03h-136.89v202.23h136.89v-202.23zm-256.03-1576.3h-.02c-31.64 0-57.51 25.87-57.51 57.51v101.05h115.04v-101.05c0-31.64-25.87-57.51-57.51-57.51zm188.59 0h-.02c-31.64 0-57.51 25.87-57.51 57.51v101.05h115.04v-101.05c0-31.64-25.87-57.51-57.51-57.51z" />
+    <path id="southwest" class="dark" d="m4520.49 9249.23 379.07 656.57h670.66L4156 7456.31l-335.32 580.8 364.48 631.32-546.73 946.97 167.66 290.4h335.34z" />
+  </g>
+</svg>
diff --git a/web/volgasprint/docs/assets/logos/volgasprint_ru.svg b/web/volgasprint/docs/assets/logos/volgasprint_ru.svg
new file mode 100644
index 0000000000..73e3d2fff7
--- /dev/null
+++ b/web/volgasprint/docs/assets/logos/volgasprint_ru.svg
@@ -0,0 +1,28 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="2353 4900 5790 5018">
+  <defs>
+    <style>
+      .white {
+        fill: #fefefe
+      }
+
+      .red {
+        fill: #e31e24
+      }
+
+      .blue {
+        fill: #264796
+      }
+    </style>
+  </defs>
+  <g id="volgasprint">
+    <!-- Do not change the order: It affects the overlapping! -->
+    <path id="northeast" class="white" d="m6671.55 6762.87-357.77-619.7 546.73-946.96-167.66-290.41h-335.34l-379.06 656.57-379.07-656.57h-670.66l1407.5 2437.88z" />
+    <path id="northwest" class="white" d="M5395.29 5852.77h-707.14l-546.72-946.97h-335.34l-167.66 290.4 379.07 656.57h-758.14l-335.33 580.81h2797.01z" />
+    <path id="southeast" class="red" d="m6860.51 9615.39-379.07-656.57 758.14.01 335.33-580.81H4746.48l335.33 580.81h728.98l546.73 946.97h335.33z" />
+    <path id="west" class="blue" d="M4659 6484.08h-670.66l-364.49 631.32H2530.38l-167.65 290.4 167.65 290.4h758.15l-379.07 656.57 335.32 580.81z" />
+    <path id="east" class="blue" d="M6875.09 7696.2h1093.47l167.65-290.4-167.65-290.4h-758.15l379.07-656.57-335.32-580.81-1414.22 2449.5h670.66z" />
+    <path id="tower" d="m5240.93 5261.4-81.71 591.38h-38.58v217.01h-.01l-75.42 479.8h-93.65l-16.13 39.17h16.13v286.59H4856.9v.02l-26.14 64.39h26.14v359.29h-185.08v525.28h-155.33v671.56h-258.68V9905.8h1983.32V8495.89h-258.68v-671.56h-155.33v-525.28h-185.08v-359.29h26.14l-26.14-64.39v-.02H5547.38v-286.59h16.13l-16.13-39.17h-93.65l-75.42-479.8h-.01v-217.01h-38.58l-81.71-591.38c13.65-3.74 23.67-16.24 23.67-31.06 0-17.8-14.42-32.22-32.21-32.22-12.56 0-22.75-10.18-22.75-22.75 0-9.94 6.37-18.38 15.24-21.49v-27.38c-59.45-7.67-102.21-61.44-95.9-121.39 6.08-57.95 55.85-100.74 113.14-99.11-25.87 14.46-44.53 40.85-47.87 72.55-5.45 51.85 32.16 98.3 84 103.75 21.09 2.21 41.28-2.69 58.18-12.78-18.86 34.71-55.53 57.66-96.53 57.9v26.46c8.87 3.11 15.24 11.55 15.24 21.49 0 12.57-10.19 22.75-22.75 22.75-17.79 0-32.21 14.42-32.21 32.22 0 14.82 10.02 27.32 23.67 31.06z" style="fill:#5b5b5b" />
+    <path id="windows" class="white" d="M5060.89 6652.77h-.02c-31.64 0-57.51 25.87-57.51 57.51v101.05h115.04v-101.05c0-31.64-25.87-57.51-57.51-57.51zm538.58 3253.03h-700v-641.91c0-192.01 157.09-349.1 349.11-349.1h1.78c192.02 0 349.11 157.09 349.11 349.1v641.91zm-329.78-3970.44h-40.44v93.49h40.44v-93.49zm81.67 0h-40.45v93.49h40.45v-93.49zm-162.19 0h-40.45v93.49h40.45v-93.49zm-58.78 2293.71h-136.88v202.23h136.88v-202.23zm176.6-1176.25h-115.04v158.56h115.04v-158.56zm-252.71 563.22h-107.34v158.57h107.34v-158.57zm498.13 0h-107.33v158.57h107.33v-158.57zm-46.9 613.03h-136.89v202.23h136.89v-202.23zm-256.03-1576.3h-.02c-31.64 0-57.51 25.87-57.51 57.51v101.05h115.04v-101.05c0-31.64-25.87-57.51-57.51-57.51zm188.59 0h-.02c-31.64 0-57.51 25.87-57.51 57.51v101.05h115.04v-101.05c0-31.64-25.87-57.51-57.51-57.51z" />
+    <path id="southwest" class="red" d="m4520.49 9249.23 379.07 656.57h670.66L4156 7456.31l-335.32 580.8 364.48 631.32-546.73 946.97 167.66 290.4h335.34z" />
+  </g>
+</svg>
diff --git a/web/volgasprint/docs/index.md b/web/volgasprint/docs/index.md
new file mode 100644
index 0000000000..a3e945e2a1
--- /dev/null
+++ b/web/volgasprint/docs/index.md
@@ -0,0 +1,121 @@
+# NixOS Volga Sprint 2024
+
+<img src="assets/logos/volgasprint_ru.svg"
+     alt="Nix logo with the tower SΓΆyembikΓ€, a famous Kazan landmark"
+     width="300"
+     style="filter: drop-shadow(1px 1px 6px grey);">
+
+*What*
+
+:    A week of intense hacking [on Nix](https://nixos.org/nix) in Kazan, the capital of Tatarstan in Russia.
+
+*When*
+:    22/08/2024 - 29/08/2024 (Thu - Thu)
+
+*Where*
+:    [Kazan, Tatarstan, Russia](https://yandex.com/maps/-/CDFaeW~J), on the bank of the [Volga](https://en.wikipedia.org/wiki/Volga)
+
+*Who*
+:    ~20 developers.
+
+Volga Sprint is similar to other Nix events like [Ocean Sprint](https://oceansprint.org/) and [Thaiger Sprint](https://thaigersprint.org/).
+
+## πŸ“ Location
+
+[Kazan, Tatarstan, Russia](https://yandex.com/maps/-/CDFaeW~J)
+
+Kazan is the historial capital of the Republic of Tatarstan. The city is over 1000 years old and is considered one of the main tourist destinations in Russia.
+
+Kazan is located on the bank of the Volga, the longest river in Europe, and surrounded by numerous lakes and beautiful nature.
+
+![Birds eye view of the Kazan Kremlin on the embankment](assets/kazan_overview.webp)
+
+As one of Russia's seven Muslim-majority republics, it offers a beautiful blend of Islamic architecture with traditional Russian styles.
+
+![Farmer's Palace in Kazan; enormous, beautiful building with a large tree growing in the central portcullis](assets/kazan_tree.webp)
+
+![Bauman Street; one of the central pedestrian areas of Kazan](assets/baumana.webp)
+
+For the sprint we will rent a large private house close to the centre of Kazan.
+
+## 🏘️ Accommodation
+
+Once we've determined who will attend the sprint, we'll get everyone to a shared chat room to help organise accomodation (e.g. shared apartment rentals). We'll also have some rooms available in the house that we're hosting the sprint in, but not enough for everyone.
+
+We will choose a house that is not too far from decent hotels and rentable apartments.
+
+Note that the sprint budget will *not* cover accommodation.
+
+## 🍲 Food
+
+During the sprint we'll take care of lunch in the house. For other meals, a large amount of good restaurants are available in Kazan (many of which work 24/7, which might help those who arrive with a jet-lag!).
+
+We'll try to have a recommended dinner location on most evenings, but everyone is of course free to do their own thing!
+
+## πŸ”₯ Registration
+
+[Fill out the form to apply](https://forms.yandex.ru/cloud/65e640fc73cee71ec679db66/).
+
+We're aiming to process applications as soon as possible and notify attendees in May.
+
+## 🧡 Topics
+
+The topics will depend on attendees' interest, in general anything related to Nix is fine! You should have a general idea of what you're interested in working on, but ultimate it is of course up to you.
+
+## πŸ›¬ How to get here
+
+Kazan has an international airport [with many connections](https://www.flightconnections.com/flights-from-kazan-kzn), including to hub airports like Istanbul (for connections to Europe), Dubai (for connections to ... everywhere) and directly flights to many Asian and Arab countries.
+
+Depending on where you're coming from a flight via Moscow or a more eastern location like Vladivostok might be preferable. Internal Russian flights are fairly cheap, and train rides are also a comfortable alternative (although they can be up to a week long - it's a big country!).
+
+### πŸ›‚ Visas
+
+Tatarstan is subject to standard Russian visa regulations. For citizens of most European[^1] (and many other) countries, a simple e-visa can be [applied for online](https://evisa.kdmid.ru/) and is valid for up to 16 days.
+
+Citizens of many non-Western nations[^2] can travel to Russia visa-free. Please make sure to check your particular situation.
+
+For everyone else (notably, Americans and Brits) a visa application at your local Russian embassy or visa centre is necessary. If you're approved for the sprint we're happy to help out with this!
+
+[^1]: **e-visas** available for citizens of Andorra, Austria, Bahrain, Belgium, Bulgaria, Cambodia, China, Croatia, Cyprus, Czech Republic, Denmark, Estonia, Finland, France, Germany, Greece, Hungary, Iceland, India, Indonesia, Iran, Ireland, Italy, Japan, North Korea, Kuwait, Latvia, Liechtenstein, Lithuania, Luxembourg, Malaysia, Malta, Mexico, Monaco, Myanmar, Netherlands, North Macedonia, Norway, Oman, Philippines, Poland, Portugal, Romania, San Marino, Saudi Arabia, Serbia, Singapore, Slovakia, Slovenia, Spain, Sweden, Switzerland, Taiwan, China, Turkey, Vatican, Viet Nam
+
+[^2]: **visa-free** travel for citizens of Abkhazia, Argentina, Armenia, Azerbaijan, Belarus, Bolivia, Bosnia and Herzegovina, Brazil, Brunei Darussalam, Chile, Colombia, Costa Rica, Cuba, Dominican Republic, Ecuador, Fiji, Guatemala, Guyana, Honduras, Israel, Kazakhstan, South Korea, Kyrgyzstan, Moldova, Mongolia, Montenegro, Nicaragua, North Macedonia, Peru, Serbia, South Africa, Tajikistan, Thailand, Turkey, Ukraine, United Arab Emirates, Uruguay, Uzbekistan, Venezuela
+
+## πŸ₯³ Stuff to do in Kazan
+
+We'll organise a handful of group activities during the sprint and you can choose to attend the ones that are interesting to you! What exactly we're doing is not yet decided, but some options are things like:
+
+* Excursion to the Kazan Kremlin, including a visit to the Kul Sharif Mosque, one of the largest mosques in Europe
+* Evening sunset dinner on the bank of the Volga
+- Excursion to one of the crystal clear, blue lakes near Kazan
+* Evening visit to a traditional Russian *banya* (steam bath house)
+* Traditional Tatar cooking class (including how to make Chak-Chak)
+
+## πŸ’™ Sponsors
+
+We're looking for sponsors to cover expenses such as venue, catering, T-shirt printing and some of the activities.
+
+Please help us make this a productive event by chipping in, so we can focus on coding instead of grocery shopping and cooking.
+
+Our target budget is 500 000 RUB (~ 5000 EUR). Reach out to [sponsors@volgasprint.org](mailto:sponsors@volgasprint.org) or directly to tazjin.
+
+| Level      | Contribution (RUB) | Perks                                                                                     |
+|------------|--------------------|-------------------------------------------------------------------------------------------|
+| Gold       | 200 000            | Silver + Large logo on the T-shirt, 2 total reserved seats, shoutout during dinner, SWAG. |
+| Silver     | 150 000            | Individual + Logo on the T-shirt + 1 reserved seat.                                       |
+| Individual | 50 000             | Logo on the website.                                                                      |
+
+<!--
+### πŸ† Gold
+
+### 🏒 Silver
+
+### πŸ’» Individual
+
+-->
+
+<!-- ## πŸ§‘ Participants -->
+
+## πŸŽ–οΈ Organizers
+
+* [Vincent Ambo (tazjin)](https://tazj.in), long-time Nix user, initiator of [TVL](https://tvl.fyi) & [Tvix](https://tvix.dev)
+* [Mark Shevchenko](https://markshevchenko.pro/), organiser of [ProgMSK](https://prog.msk.ru/), programming polyglot and Nix user
diff --git a/web/volgasprint/mkdocs.yml b/web/volgasprint/mkdocs.yml
new file mode 100644
index 0000000000..3407b6a176
--- /dev/null
+++ b/web/volgasprint/mkdocs.yml
@@ -0,0 +1,41 @@
+site_name: Volga Sprint
+site_description: 'A week of Nix hacking near the Volga'
+site_url: 'https://volgasprint.org'
+repo_name: '//web/volgasprint'
+repo_url: 'https://code.tvl.fyi/tree/web/volgasprint'
+theme:
+  name: material
+  logo: assets/logos/volgasprint_nix.svg
+  favicon: assets/logos/volgasprint_nix.svg
+  palette:
+    primary: black
+    accent: '#FBFBFB'
+  features:
+    - tabs
+    - instant
+    - navigation.tabs
+    - navigation.expand
+    - navigation.instant
+    - navigation.tracking
+    - content.action.edit
+    - content.code.annotate
+    - content.tabs.link
+    - content.footnote.tooltips
+    - toc.integrate
+nav:
+  - Home: index.md
+copyright: Copyright &copy; 2024 <a href="https://tvl.fyi">The TVL Community</a>
+markdown_extensions:
+  - tables
+  - admonition
+  - def_list
+  - attr_list
+  - footnotes
+  - pymdownx.highlight:
+      anchor_linenums: true
+  - pymdownx.inlinehilite
+  - pymdownx.snippets
+  - pymdownx.superfences
+  - pymdownx.tabbed:
+      alternate_style: true
+  - pymdownx.tasklist
diff --git a/web/volgasprint/requirements.txt b/web/volgasprint/requirements.txt
new file mode 100644
index 0000000000..83b695ed8a
--- /dev/null
+++ b/web/volgasprint/requirements.txt
@@ -0,0 +1,4 @@
+mkdocs-material==9.4.6
+mkdocs-rss-plugin==1.8.0
+pillow
+cairosvg